mandala/palette.go

240 lines
6.7 KiB
Go

package main
import (
"image/color"
"math/rand/v2"
)
type Palette []color.RGBA
var textColors = []color.RGBA{
// basic bright colors
{255, 100, 100, 255}, // red
{100, 255, 100, 255}, // green
{100, 100, 255, 255}, // blue
{255, 255, 100, 255}, // yellow
{255, 100, 255, 255}, // magenta
{100, 255, 255, 255}, // cyan
{255, 255, 255, 255}, // white
{255, 150, 50, 255}, // orange
{150, 255, 50, 255}, // lime
{255, 50, 150, 255}, // pink
{50, 150, 255, 255}, // sky blue
{200, 200, 255, 255}, // light blue
// extended primary variations
{255, 50, 50, 255}, // bright red
{255, 150, 150, 255}, // light red
{50, 255, 50, 255}, // bright green
{150, 255, 150, 255}, // light green
{50, 50, 255, 255}, // bright blue
{150, 150, 255, 255}, // light blue variant
// warm colors
{255, 200, 50, 255}, // golden yellow
{255, 175, 0, 255}, // amber
{255, 69, 0, 255}, // red orange
{255, 20, 147, 255}, // deep pink
{255, 105, 180, 255}, // hot pink
{255, 182, 193, 255}, // light pink
{255, 160, 122, 255}, // light salmon
{255, 99, 71, 255}, // tomato
{255, 127, 80, 255}, // coral
// cool colors
{0, 255, 127, 255}, // spring green
{0, 250, 154, 255}, // medium spring green
{64, 224, 208, 255}, // turquoise
{72, 209, 204, 255}, // medium turquoise
{135, 206, 250, 255}, // light sky blue
{70, 130, 180, 255}, // steel blue
{100, 149, 237, 255}, // cornflower blue
{123, 104, 238, 255}, // medium slate blue
{138, 43, 226, 255}, // blue violet
// purple spectrum
{147, 112, 219, 255}, // medium purple
{186, 85, 211, 255}, // medium orchid
{218, 112, 214, 255}, // orchid
{221, 160, 221, 255}, // plum
{238, 130, 238, 255}, // violet
{255, 0, 255, 255}, // pure magenta
{199, 21, 133, 255}, // medium violet red
{219, 112, 147, 255}, // pale violet red
// neon/electric colors
{57, 255, 20, 255}, // electric green
{255, 20, 147, 255}, // electric pink
{0, 255, 255, 255}, // electric cyan
{255, 255, 0, 255}, // electric yellow
{255, 0, 127, 255}, // electric rose
{127, 255, 0, 255}, // electric lime
{255, 127, 0, 255}, // electric orange
{0, 127, 255, 255}, // electric blue
// pastel variants
{255, 218, 185, 255}, // peach puff
{255, 228, 196, 255}, // bisque
{255, 239, 213, 255}, // papaya whip
{240, 248, 255, 255}, // alice blue
{230, 230, 250, 255}, // lavender
{255, 240, 245, 255}, // lavender blush
{255, 228, 225, 255}, // misty rose
{245, 255, 250, 255}, // mint cream
// earth tones (brighter versions)
{210, 180, 140, 255}, // tan
{222, 184, 135, 255}, // burlywood
{238, 203, 173, 255}, // navajo white
{255, 218, 185, 255}, // peach
{255, 165, 79, 255}, // sandy brown
{205, 133, 63, 255}, // peru
{160, 82, 45, 255}, // saddle brown (lighter)
{210, 105, 30, 255}, // chocolate (lighter)
// jewel tones
{50, 205, 50, 255}, // lime green
{220, 20, 60, 255}, // crimson
{75, 0, 130, 255}, // indigo (brightened)
{128, 0, 128, 255}, // purple (brightened)
{165, 42, 42, 255}, // brown (brightened)
{128, 128, 0, 255}, // olive (brightened)
{0, 128, 128, 255}, // teal (brightened)
{128, 0, 0, 255}, // maroon (brightened)
// unique/exotic colors
{255, 215, 0, 255}, // gold
{192, 192, 192, 255}, // silver
{255, 20, 147, 255}, // deep pink
{0, 191, 255, 255}, // deep sky blue
{30, 144, 255, 255}, // dodger blue
{255, 69, 0, 255}, // orange red
{154, 205, 50, 255}, // yellow green
{32, 178, 170, 255}, // light sea green
{60, 179, 113, 255}, // medium sea green
// vibrant spectrum
{127, 255, 212, 255}, // aquamarine
{240, 230, 140, 255}, // khaki
{238, 232, 170, 255}, // pale goldenrod
{250, 240, 230, 255}, // linen
{253, 245, 230, 255}, // old lace
{255, 250, 240, 255}, // floral white
{248, 248, 255, 255}, // ghost white
{245, 245, 220, 255}, // beige
}
var themePalettes = map[string]Palette{
"vibrant": {
{255, 0, 150, 255}, // hot pink
{255, 100, 0, 255}, // orange
{255, 255, 0, 255}, // yellow
{0, 255, 100, 255}, // lime
{0, 150, 255, 255}, // sky blue
{150, 0, 255, 255}, // purple
},
"sunset": {
{255, 94, 77, 255}, // coral
{255, 154, 0, 255}, // orange
{255, 206, 84, 255}, // yellow
{255, 118, 117, 255}, // light coral
{240, 147, 43, 255}, // dark orange
{235, 77, 75, 255}, // red
},
"ocean": {
{0, 123, 255, 255}, // blue
{0, 200, 255, 255}, // light blue
{0, 255, 200, 255}, // cyan
{100, 200, 255, 255}, // sky blue
{0, 150, 200, 255}, // dark blue
{50, 255, 150, 255}, // aqua
},
"neon": {
{255, 0, 255, 255}, // magenta
{0, 255, 255, 255}, // cyan
{255, 255, 0, 255}, // yellow
{255, 0, 100, 255}, // hot pink
{100, 255, 0, 255}, // lime
{150, 0, 255, 255}, // purple
},
"forest": {
{34, 139, 34, 255}, // forest green
{107, 142, 35, 255}, // olive
{154, 205, 50, 255}, // yellow green
{50, 205, 50, 255}, // lime green
{0, 128, 0, 255}, // green
{85, 107, 47, 255}, // dark olive
},
"cosmic": {
{138, 43, 226, 255}, // blue violet
{75, 0, 130, 255}, // indigo
{148, 0, 211, 255}, // dark violet
{255, 20, 147, 255}, // deep pink
{255, 0, 255, 255}, // magenta
{72, 61, 139, 255}, // dark slate blue
},
}
func getRandomTextColors() (color.RGBA, color.RGBA) {
textColor := textColors[rand.IntN(len(textColors))]
// calculate perceived brightness using luminance formula
// std. weights for human eye sensitivity: 0.299*R + 0.587*G + 0.114*B
brightness := 0.299*float64(textColor.R) + 0.587*float64(textColor.G) + 0.114*float64(textColor.B)
var outlineColor color.RGBA
if brightness < 128 { // Dark text - use white outline
outlineColor = color.RGBA{255, 255, 255, 255}
} else { // Bright text - use black outline
outlineColor = color.RGBA{0, 0, 0, 255}
}
return textColor, outlineColor
}
func getThemePalette(name string) Palette {
if palette, exists := themePalettes[name]; exists {
return palette
}
return themePalettes["vibrant"] // default
}
func getRandomThemePalette() (Palette, string) {
name := config.ColorThemes[rand.IntN(len(config.ColorThemes))]
return getThemePalette(name), name
}
func (p Palette) interpolate(t float64) color.RGBA {
if len(p) == 0 {
return color.RGBA{128, 128, 128, 255}
}
// clamp to to [0, 1]
if t < 0 {
t = 0
}
if t > 1 {
t = 1
}
// calculate which colors to interpolate between
scaledT := t * float64(len(p)-1)
index := int(scaledT)
localT := scaledT - float64(index)
if index >= len(p)-1 {
return p[len(p)-1]
}
c1 := p[index]
c2 := p[index+1]
// linear interpolation between the two colors
r := uint8(float64(c1.R)*(1-localT) + float64(c2.R)*localT)
g := uint8(float64(c1.G)*(1-localT) + float64(c2.G)*localT)
b := uint8(float64(c1.B)*(1-localT) + float64(c2.B)*localT)
a := uint8(float64(c1.A)*(1-localT) + float64(c2.A)*localT)
return color.RGBA{r, g, b, a}
}