1
2
3
4 package colorful
5
6 import (
7 "fmt"
8 "math"
9 "math/rand"
10 )
11
12
13
14 type lab_t struct {
15 L, A, B float64
16 }
17
18 type SoftPaletteSettings struct {
19
20 CheckColor func(l, a, b float64) bool
21
22
23 Iterations int
24
25
26
27 ManySamples bool
28 }
29
30
31
32
33
34
35 func SoftPaletteEx(colorsCount int, settings SoftPaletteSettings) ([]Color, error) {
36
37
38 check := func(col lab_t) bool {
39 c := Lab(col.L, col.A, col.B)
40 return c.IsValid() && (settings.CheckColor == nil || settings.CheckColor(col.L, col.A, col.B))
41 }
42
43
44 dl := 0.05
45 dab := 0.1
46 if settings.ManySamples {
47 dl = 0.01
48 dab = 0.05
49 }
50
51 samples := make([]lab_t, 0, int(1.0/dl*2.0/dab*2.0/dab))
52 for l := 0.0; l <= 1.0; l += dl {
53 for a := -1.0; a <= 1.0; a += dab {
54 for b := -1.0; b <= 1.0; b += dab {
55 if check(lab_t{l, a, b}) {
56 samples = append(samples, lab_t{l, a, b})
57 }
58 }
59 }
60 }
61
62
63 if len(samples) < colorsCount {
64 return nil, fmt.Errorf("palettegen: more colors requested (%v) than samples available (%v). Your requested color count may be wrong, you might want to use many samples or your constraint function makes the valid color space too small", colorsCount, len(samples))
65 } else if len(samples) == colorsCount {
66 return labs2cols(samples), nil
67 }
68
69
70
71 means := make([]lab_t, colorsCount)
72 for i := 0; i < colorsCount; i++ {
73 for means[i] = samples[rand.Intn(len(samples))]; in(means, i, means[i]); means[i] = samples[rand.Intn(len(samples))] {
74 }
75 }
76
77 clusters := make([]int, len(samples))
78 samples_used := make([]bool, len(samples))
79
80
81 for i := 0; i < settings.Iterations; i++ {
82
83
84 for isample, sample := range samples {
85 samples_used[isample] = false
86 mindist := math.Inf(+1)
87 for imean, mean := range means {
88 dist := lab_dist(sample, mean)
89 if dist < mindist {
90 mindist = dist
91 clusters[isample] = imean
92 }
93
94
95 if lab_eq(sample, mean) {
96 samples_used[isample] = true
97 }
98 }
99 }
100
101
102 for imean := range means {
103
104 nsamples := 0
105 newmean := lab_t{0.0, 0.0, 0.0}
106 for isample, sample := range samples {
107 if clusters[isample] == imean {
108 nsamples++
109 newmean.L += sample.L
110 newmean.A += sample.A
111 newmean.B += sample.B
112 }
113 }
114 if nsamples > 0 {
115 newmean.L /= float64(nsamples)
116 newmean.A /= float64(nsamples)
117 newmean.B /= float64(nsamples)
118 } else {
119
120 var inewmean int
121 for inewmean = rand.Intn(len(samples_used)); samples_used[inewmean]; inewmean = rand.Intn(len(samples_used)) {
122 }
123 newmean = samples[inewmean]
124 samples_used[inewmean] = true
125 }
126
127
128 if nsamples > 0 && check(newmean) {
129
130 means[imean] = newmean
131 } else {
132
133
134
135 mindist := math.Inf(+1)
136 for isample, sample := range samples {
137 if !samples_used[isample] {
138 dist := lab_dist(sample, newmean)
139 if dist < mindist {
140 mindist = dist
141 newmean = sample
142 }
143 }
144 }
145 }
146 }
147 }
148 return labs2cols(means), nil
149 }
150
151
152 func SoftPalette(colorsCount int) ([]Color, error) {
153 return SoftPaletteEx(colorsCount, SoftPaletteSettings{nil, 50, false})
154 }
155
156 func in(haystack []lab_t, upto int, needle lab_t) bool {
157 for i := 0; i < upto && i < len(haystack); i++ {
158 if haystack[i] == needle {
159 return true
160 }
161 }
162 return false
163 }
164
165 const LAB_DELTA = 1e-6
166
167 func lab_eq(lab1, lab2 lab_t) bool {
168 return math.Abs(lab1.L-lab2.L) < LAB_DELTA &&
169 math.Abs(lab1.A-lab2.A) < LAB_DELTA &&
170 math.Abs(lab1.B-lab2.B) < LAB_DELTA
171 }
172
173
174
175 func lab_dist(lab1, lab2 lab_t) float64 {
176 return math.Sqrt(sq(lab1.L-lab2.L) + sq(lab1.A-lab2.A) + sq(lab1.B-lab2.B))
177 }
178
179 func labs2cols(labs []lab_t) (cols []Color) {
180 cols = make([]Color, len(labs))
181 for k, v := range labs {
182 cols[k] = Lab(v.L, v.A, v.B)
183 }
184 return cols
185 }
186
View as plain text