1// Package charmtone contains an API for the CharmTone color palette.
2package charmtone
3
4import (
5 "fmt"
6 "image/color"
7 "slices"
8
9 "github.com/lucasb-eyer/go-colorful"
10)
11
12var _ color.Color = Key(0)
13
14// Key is a type for color keys.
15type Key int
16
17// Available colors.
18const (
19 Cumin Key = iota
20 Tang
21 Yam
22 Paprika
23 Bengal
24 Uni
25 Sriracha
26 Coral
27 Salmon
28 Chili
29 Cherry
30 Tuna
31 Macaron
32 Pony
33 Cheeky
34 Flamingo
35 Dolly
36 Blush
37 Urchin
38 Mochi
39 Lilac
40 Prince
41 Violet
42 Mauve
43 Grape
44 Plum
45 Orchid
46 Jelly
47 Charple
48 Hazy
49 Ox
50 Sapphire
51 Guppy
52 Oceania
53 Thunder
54 Anchovy
55 Damson
56 Malibu
57 Sardine
58 Zinc
59 Turtle
60 Lichen
61 Guac
62 Julep
63 Bok
64 Mustard
65 Citron
66 Zest
67 Pepper
68 Barbeque
69 Charcoal
70 Iron
71 Oyster
72 Squid
73 Smoke
74 Ash
75 Salt
76 Butter
77)
78
79// RGBA returns the red, green, blue, and alpha values of the color. It
80// satisfies the color.Color interface.
81func (k Key) RGBA() (r, g, b, a uint32) {
82 c, err := colorful.Hex(k.Hex())
83 if err != nil {
84 panic(fmt.Sprintf("invalid color key %d: %s: %v", k, k.String(), err))
85 }
86 return c.RGBA()
87}
88
89// String returns the official CharmTone name of the color. It satisfies the
90// fmt.Stringer interface.
91func (k Key) String() string {
92 return map[Key]string{
93 Cumin: "Cumin",
94 Tang: "Tang",
95 Yam: "Yam",
96 Paprika: "Paprika",
97 Bengal: "Bengal",
98 Uni: "Uni",
99 Sriracha: "Sriracha",
100 Coral: "Coral",
101 Salmon: "Salmon",
102 Chili: "Chili",
103 Cherry: "Cherry",
104 Tuna: "Tuna",
105 Macaron: "Macaron",
106 Pony: "Pony",
107 Cheeky: "Cheeky",
108 Flamingo: "Flamingo",
109 Dolly: "Dolly",
110 Blush: "Blush",
111 Urchin: "Urchin",
112 Mochi: "Crystal",
113 Lilac: "Lilac",
114 Prince: "Prince",
115 Violet: "Violet",
116 Mauve: "Mauve",
117 Grape: "Grape",
118 Plum: "Plum",
119 Orchid: "Orchid",
120 Jelly: "Jelly",
121 Charple: "Charple",
122 Hazy: "Hazy",
123 Ox: "Ox",
124 Sapphire: "Sapphire",
125 Guppy: "Guppy",
126 Oceania: "Oceania",
127 Thunder: "Thunder",
128 Anchovy: "Anchovy",
129 Damson: "Damson",
130 Malibu: "Malibu",
131 Sardine: "Sardine",
132 Zinc: "Zinc",
133 Turtle: "Turtle",
134 Lichen: "Lichen",
135 Guac: "Guac",
136 Julep: "Julep",
137 Bok: "Bok",
138 Mustard: "Mustard",
139 Citron: "Citron",
140 Zest: "Zest",
141 Pepper: "Pepper",
142 Barbeque: "Barbeque",
143 Charcoal: "Charcoal",
144 Iron: "Iron",
145 Oyster: "Oyster",
146 Squid: "Squid",
147 Smoke: "Smoke",
148 Salt: "Salt",
149 Ash: "Ash",
150 Butter: "Butter",
151 }[k]
152}
153
154// Hex returns the hex value of the color.
155func (k Key) Hex() string {
156 return map[Key]string{
157 Cumin: "#BF976F",
158 Tang: "#FF985A",
159 Yam: "#FFB587",
160 Paprika: "#D36C64",
161 Bengal: "#FF6E63",
162 Uni: "#FF937D",
163 Sriracha: "#EB4268",
164 Coral: "#FF577D",
165 Salmon: "#FF7F90",
166 Chili: "#E23080",
167 Cherry: "#FF388B",
168 Tuna: "#FF6DAA",
169 Macaron: "#E940B0",
170 Pony: "#FF4FBF",
171 Cheeky: "#FF79D0",
172 Flamingo: "#F947E3",
173 Dolly: "#FF60FF",
174 Blush: "#FF84FF",
175 Urchin: "#C337E0",
176 Mochi: "#EB5DFF",
177 Lilac: "#F379FF",
178 Prince: "#9C35E1",
179 Violet: "#C259FF",
180 Mauve: "#D46EFF",
181 Grape: "#7134DD",
182 Plum: "#9953FF",
183 Orchid: "#AD6EFF",
184 Jelly: "#4A30D9",
185 Charple: "#6B50FF",
186 Hazy: "#8B75FF",
187 Ox: "#3331B2",
188 Sapphire: "#4949FF",
189 Guppy: "#7272FF",
190 Oceania: "#2B55B3",
191 Thunder: "#4776FF",
192 Anchovy: "#719AFC",
193 Damson: "#007AB8",
194 Malibu: "#00A4FF",
195 Sardine: "#4FBEFE",
196 Zinc: "#0e9996",
197 Turtle: "#0ADCD9",
198 Lichen: "#5CDFEA",
199 Guac: "#00b875",
200 Julep: "#00FFB2",
201 Bok: "#68FFD6",
202 Mustard: "#F5EF34",
203 Citron: "#E8FF27",
204 Zest: "#E8FE96",
205 Pepper: "#201F26",
206 Barbeque: "#2d2c35",
207 Charcoal: "#3A3943",
208 Iron: "#4D4C57",
209 Oyster: "#605F6B",
210 Squid: "#858392",
211 Smoke: "#BFBCC8",
212 Ash: "#DFDBDD",
213 Salt: "#F1EFEF",
214 Butter: "#FFFAF1",
215 }[k]
216}
217
218// Keys returns a slice of all CharmTone color keys.
219func Keys() []Key {
220 return []Key{
221 Cumin,
222 Tang,
223 Yam,
224 Paprika,
225 Bengal,
226 Uni,
227 Sriracha,
228 Coral,
229 Salmon,
230 Chili,
231 Cherry,
232 Tuna,
233 Macaron,
234 Pony,
235 Cheeky,
236 Flamingo,
237 Dolly,
238 Blush,
239 Urchin,
240 Mochi,
241 Lilac,
242 Prince,
243 Violet,
244 Mauve,
245 Grape,
246 Plum,
247 Orchid,
248 Jelly,
249 Charple,
250 Hazy,
251 Ox,
252 Sapphire,
253 Guppy,
254 Oceania,
255 Thunder,
256 Anchovy,
257 Damson,
258 Malibu,
259 Sardine,
260 Zinc,
261 Turtle,
262 Lichen,
263 Guac,
264 Julep,
265 Bok,
266 Mustard,
267 Citron,
268 Zest,
269 Pepper,
270 Barbeque,
271 Charcoal,
272 Iron,
273 Oyster,
274 Squid,
275 Smoke,
276 Ash,
277 Salt,
278 Butter,
279 }
280}
281
282// IsPrimary indicates which colors are part of the core palette.
283func (k Key) IsPrimary() bool {
284 return slices.Contains([]Key{
285 Charple,
286 Dolly,
287 Julep,
288 Zest,
289 Butter,
290 }, k)
291}
292
293// IsSecondary indicates which colors are part of the secondary palette.
294func (k Key) IsSecondary() bool {
295 return slices.Contains([]Key{
296 Hazy,
297 Blush,
298 Bok,
299 }, k)
300}
301
302// IsTertiary indicates which colors are part of the tertiary palette.
303func (k Key) IsTertiary() bool {
304 return slices.Contains([]Key{
305 Turtle,
306 Malibu,
307 Violet,
308 Tuna,
309 Coral,
310 Uni,
311 }, k)
312}
313
314// BlendColors returns a slice of colors blended between the given keys.
315// Blending is done as Hcl to stay in gamut.
316func BlendColors(size int, keys ...Key) []color.Color {
317 if len(keys) < 2 {
318 return nil
319 }
320
321 stops := make([]colorful.Color, len(keys))
322 for i, k := range keys {
323 stops[i], _ = colorful.Hex(k.Hex())
324 }
325
326 numSegments := len(stops) - 1
327 if numSegments == 0 {
328 return nil
329 }
330 blended := make([]color.Color, 0, size)
331
332 // Calculate how many colors each segment should have.
333 segmentSizes := make([]int, numSegments)
334 baseSize := size / numSegments
335 remainder := size % numSegments
336
337 // Distribute the remainder across segments.
338 for i := range numSegments {
339 segmentSizes[i] = baseSize
340 if i < remainder {
341 segmentSizes[i]++
342 }
343 }
344
345 // Generate colors for each segment.
346 for i := range numSegments {
347 c1 := stops[i]
348 c2 := stops[i+1]
349 segmentSize := segmentSizes[i]
350
351 for j := range segmentSize {
352 if segmentSize == 0 {
353 continue
354 }
355 t := float64(j) / float64(segmentSize)
356 c := c1.BlendHcl(c2, t)
357 blended = append(blended, c)
358 }
359 }
360
361 return blended
362}