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 BBQ
69 Charcoal
70 Iron
71 Oyster
72 Squid
73 Smoke
74 Ash
75 Salt
76 Butter
77
78 // Diffs: additions. The brightest color in this set is Julep, defined
79 // above.
80 Pickle
81 Gator
82 Spinach
83
84 // Diffs: deletions. The brightest color in this set is Cherry, defined
85 // above.
86 Pom
87 Steak
88 Toast
89
90 // Provisional.
91 NeueGuac
92 NeueZinc
93)
94
95// RGBA returns the red, green, blue, and alpha values of the color. It
96// satisfies the color.Color interface.
97func (k Key) RGBA() (r, g, b, a uint32) {
98 c, err := colorful.Hex(k.Hex())
99 if err != nil {
100 panic(fmt.Sprintf("invalid color key %d: %s: %v", k, k.String(), err))
101 }
102 return c.RGBA()
103}
104
105// String returns the official CharmTone name of the color. It satisfies the
106// fmt.Stringer interface.
107func (k Key) String() string {
108 return map[Key]string{
109 Cumin: "Cumin",
110 Tang: "Tang",
111 Yam: "Yam",
112 Paprika: "Paprika",
113 Bengal: "Bengal",
114 Uni: "Uni",
115 Sriracha: "Sriracha",
116 Coral: "Coral",
117 Salmon: "Salmon",
118 Chili: "Chili",
119 Cherry: "Cherry",
120 Tuna: "Tuna",
121 Macaron: "Macaron",
122 Pony: "Pony",
123 Cheeky: "Cheeky",
124 Flamingo: "Flamingo",
125 Dolly: "Dolly",
126 Blush: "Blush",
127 Urchin: "Urchin",
128 Mochi: "Crystal",
129 Lilac: "Lilac",
130 Prince: "Prince",
131 Violet: "Violet",
132 Mauve: "Mauve",
133 Grape: "Grape",
134 Plum: "Plum",
135 Orchid: "Orchid",
136 Jelly: "Jelly",
137 Charple: "Charple",
138 Hazy: "Hazy",
139 Ox: "Ox",
140 Sapphire: "Sapphire",
141 Guppy: "Guppy",
142 Oceania: "Oceania",
143 Thunder: "Thunder",
144 Anchovy: "Anchovy",
145 Damson: "Damson",
146 Malibu: "Malibu",
147 Sardine: "Sardine",
148 Zinc: "Zinc",
149 Turtle: "Turtle",
150 Lichen: "Lichen",
151 Guac: "Guac",
152 Julep: "Julep",
153 Bok: "Bok",
154 Mustard: "Mustard",
155 Citron: "Citron",
156 Zest: "Zest",
157 Pepper: "Pepper",
158 BBQ: "BBQ",
159 Charcoal: "Charcoal",
160 Iron: "Iron",
161 Oyster: "Oyster",
162 Squid: "Squid",
163 Smoke: "Smoke",
164 Salt: "Salt",
165 Ash: "Ash",
166 Butter: "Butter",
167
168 // Diffs: additions.
169 Pickle: "Pickle",
170 Gator: "Gator",
171 Spinach: "Spinach",
172
173 // Diffs: deletions.
174 Pom: "Pom",
175 Steak: "Steak",
176 Toast: "Toast",
177
178 // Provisional.
179 NeueGuac: "Neue Guac",
180 NeueZinc: "Neue Zinc",
181 }[k]
182}
183
184// Hex returns the hex value of the color.
185func (k Key) Hex() string {
186 return map[Key]string{
187 Cumin: "#BF976F",
188 Tang: "#FF985A",
189 Yam: "#FFB587",
190 Paprika: "#D36C64",
191 Bengal: "#FF6E63",
192 Uni: "#FF937D",
193 Sriracha: "#EB4268",
194 Coral: "#FF577D",
195 Salmon: "#FF7F90",
196 Chili: "#E23080",
197 Cherry: "#FF388B",
198 Tuna: "#FF6DAA",
199 Macaron: "#E940B0",
200 Pony: "#FF4FBF",
201 Cheeky: "#FF79D0",
202 Flamingo: "#F947E3",
203 Dolly: "#FF60FF",
204 Blush: "#FF84FF",
205 Urchin: "#C337E0",
206 Mochi: "#EB5DFF",
207 Lilac: "#F379FF",
208 Prince: "#9C35E1",
209 Violet: "#C259FF",
210 Mauve: "#D46EFF",
211 Grape: "#7134DD",
212 Plum: "#9953FF",
213 Orchid: "#AD6EFF",
214 Jelly: "#4A30D9",
215 Charple: "#6B50FF",
216 Hazy: "#8B75FF",
217 Ox: "#3331B2",
218 Sapphire: "#4949FF",
219 Guppy: "#7272FF",
220 Oceania: "#2B55B3",
221 Thunder: "#4776FF",
222 Anchovy: "#719AFC",
223 Damson: "#007AB8",
224 Malibu: "#00A4FF",
225 Sardine: "#4FBEFE",
226 Zinc: "#10B1AE",
227 Turtle: "#0ADCD9",
228 Lichen: "#5CDFEA",
229 Guac: "#12C78F",
230 Julep: "#00FFB2",
231 Bok: "#68FFD6",
232 Mustard: "#F5EF34",
233 Citron: "#E8FF27",
234 Zest: "#E8FE96",
235 Pepper: "#201F26",
236 BBQ: "#2d2c35",
237 Charcoal: "#3A3943",
238 Iron: "#4D4C57",
239 Oyster: "#605F6B",
240 Squid: "#858392",
241 Smoke: "#BFBCC8",
242 Ash: "#DFDBDD",
243 Salt: "#F1EFEF",
244 Butter: "#FFFAF1",
245
246 // Diffs: additions.
247 Pickle: "#00A475",
248 Gator: "#18463D",
249 Spinach: "#1C3634",
250
251 // Diffs: deletions.
252 Pom: "#AB2454",
253 Steak: "#582238",
254 Toast: "#412130",
255
256 // Provisional.
257 NeueGuac: "#00b875",
258 NeueZinc: "#0e9996",
259 }[k]
260}
261
262// Keys returns a slice of all CharmTone color keys.
263func Keys() []Key {
264 return []Key{
265 Cumin,
266 Tang,
267 Yam,
268 Paprika,
269 Bengal,
270 Uni,
271 Sriracha,
272 Coral,
273 Salmon,
274 Chili,
275 Cherry,
276 Tuna,
277 Macaron,
278 Pony,
279 Cheeky,
280 Flamingo,
281 Dolly,
282 Blush,
283 Urchin,
284 Mochi,
285 Lilac,
286 Prince,
287 Violet,
288 Mauve,
289 Grape,
290 Plum,
291 Orchid,
292 Jelly,
293 Charple,
294 Hazy,
295 Ox,
296 Sapphire,
297 Guppy,
298 Oceania,
299 Thunder,
300 Anchovy,
301 Damson,
302 Malibu,
303 Sardine,
304 Zinc,
305 Turtle,
306 Lichen,
307 Guac,
308 Julep,
309 Bok,
310 Mustard,
311 Citron,
312 Zest,
313 Pepper,
314 BBQ,
315 Charcoal,
316 Iron,
317 Oyster,
318 Squid,
319 Smoke,
320 Ash,
321 Salt,
322 Butter,
323
324 // XXX: additions and deletions are not included, yet.
325 }
326}
327
328// IsPrimary indicates which colors are part of the core palette.
329func (k Key) IsPrimary() bool {
330 return slices.Contains([]Key{
331 Charple,
332 Dolly,
333 Julep,
334 Zest,
335 Butter,
336 }, k)
337}
338
339// IsSecondary indicates which colors are part of the secondary palette.
340func (k Key) IsSecondary() bool {
341 return slices.Contains([]Key{
342 Hazy,
343 Blush,
344 Bok,
345 }, k)
346}
347
348// IsTertiary indicates which colors are part of the tertiary palette.
349func (k Key) IsTertiary() bool {
350 return slices.Contains([]Key{
351 Turtle,
352 Malibu,
353 Violet,
354 Tuna,
355 Coral,
356 Uni,
357 }, k)
358}
359
360// BlendColors returns a slice of colors blended between the given keys.
361// Blending is done as Hcl to stay in gamut.
362func BlendColors(size int, keys ...Key) []color.Color {
363 if len(keys) < 2 {
364 return nil
365 }
366
367 stops := make([]colorful.Color, len(keys))
368 for i, k := range keys {
369 stops[i], _ = colorful.Hex(k.Hex())
370 }
371
372 numSegments := len(stops) - 1
373 if numSegments == 0 {
374 return nil
375 }
376 blended := make([]color.Color, 0, size)
377
378 // Calculate how many colors each segment should have.
379 segmentSizes := make([]int, numSegments)
380 baseSize := size / numSegments
381 remainder := size % numSegments
382
383 // Distribute the remainder across segments.
384 for i := range numSegments {
385 segmentSizes[i] = baseSize
386 if i < remainder {
387 segmentSizes[i]++
388 }
389 }
390
391 // Generate colors for each segment.
392 for i := range numSegments {
393 c1 := stops[i]
394 c2 := stops[i+1]
395 segmentSize := segmentSizes[i]
396
397 for j := range segmentSize {
398 if segmentSize == 0 {
399 continue
400 }
401 t := float64(j) / float64(segmentSize)
402 c := c1.BlendHcl(c2, t)
403 blended = append(blended, c)
404 }
405 }
406
407 return blended
408}