1
2
3
4
5 package moreland
6
7 import (
8 "fmt"
9 "image/color"
10 "math"
11
12 "gonum.org/v1/plot/palette"
13 )
14
15
16
17
18 type luminance struct {
19
20
21 colors []cieLAB
22
23
24
25
26
27
28
29 scalars []float64
30
31
32
33 alpha float64
34
35
36
37 min, max float64
38 }
39
40
41
42
43
44
45
46 func NewLuminance(controls []color.Color) (palette.ColorMap, error) {
47 l := luminance{
48 colors: make([]cieLAB, len(controls)),
49 scalars: make([]float64, len(controls)),
50 alpha: 1,
51 }
52 max := math.Inf(-1)
53 min := math.Inf(1)
54 for i, c := range controls {
55 lab := colorTosRGBA(c).cieLAB()
56 l.colors[i] = lab
57 max = math.Max(max, lab.L)
58 min = math.Min(min, lab.L)
59 if i > 0 && lab.L <= l.colors[i-1].L {
60 return nil, fmt.Errorf("moreland: luminance of color %d (%g) is not "+
61 "greater than that of color %d (%g)", i, lab.L, i-1, l.colors[i-1].L)
62 }
63 }
64
65 rnge := max - min
66 for i, c := range l.colors {
67 l.scalars[i] = (c.L - min) / rnge
68 }
69
70
71
72
73
74
75
76 l.scalars[0] = 0
77 l.scalars[len(l.scalars)-1] = 1
78 return &l, nil
79 }
80
81
82 func (l *luminance) At(v float64) (color.Color, error) {
83 if err := checkRange(l.min, l.max, v); err != nil {
84 return nil, err
85 }
86 scalar := (v - l.min) / (l.max - l.min)
87 if !inUnitRange(scalar) {
88 return nil, fmt.Errorf("moreland: interpolation value (%g) out of range [%g,%g]", scalar, l.min, l.max)
89 }
90 i := searchFloat64s(l.scalars, scalar)
91 if i == 0 {
92 return l.colors[i].cieXYZ().rgb().sRGBA(l.alpha), nil
93 }
94 c1 := l.colors[i-1]
95 c2 := l.colors[i]
96 frac := (scalar - l.scalars[i-1]) / (l.scalars[i] - l.scalars[i-1])
97 o := cieLAB{
98 L: frac*(c2.L-c1.L) + c1.L,
99 A: frac*(c2.A-c1.A) + c1.A,
100 B: frac*(c2.B-c1.B) + c1.B,
101 }.cieXYZ().rgb().sRGBA(l.alpha)
102 o.clamp()
103 return o, nil
104 }
105
106 func checkRange(min, max, val float64) error {
107 if max == min {
108 return fmt.Errorf("moreland: color map max == min == %g", max)
109 }
110 if min > max {
111 return fmt.Errorf("moreland: color map max (%g) < min (%g)", max, min)
112 }
113 if val < min {
114 return palette.ErrUnderflow
115 }
116 if val > max {
117 return palette.ErrOverflow
118 }
119 if math.IsNaN(val) {
120 return palette.ErrNaN
121 }
122 return nil
123 }
124
125
126
127 func searchFloat64s(vals []float64, val float64) int {
128 for j, v := range vals {
129 if val <= v {
130 return j
131 }
132 }
133 return len(vals)
134 }
135
136
137 func (l *luminance) SetMax(v float64) {
138 l.max = v
139 }
140
141
142 func (l *luminance) SetMin(v float64) {
143 l.min = v
144 }
145
146
147 func (l *luminance) Max() float64 {
148 return l.max
149 }
150
151
152 func (l *luminance) Min() float64 {
153 return l.min
154 }
155
156
157
158
159 func (l *luminance) SetAlpha(alpha float64) {
160 if !inUnitRange(alpha) {
161 panic(fmt.Errorf("moreland: invalid alpha: %g", alpha))
162 }
163 l.alpha = alpha
164 }
165
166
167 func (l *luminance) Alpha() float64 {
168 return l.alpha
169 }
170
171
172
173 func (l luminance) Palette(n int) palette.Palette {
174 if l.Max() == 0 && l.Min() == 0 {
175 l.SetMin(0)
176 l.SetMax(1)
177 }
178 delta := (l.max - l.min) / float64(n-1)
179 var v float64
180 c := make([]color.Color, n)
181 for i := 0; i < n; i++ {
182 v = l.min + delta*float64(i)
183 var err error
184 c[i], err = l.At(v)
185 if err != nil {
186 panic(err)
187 }
188 }
189 return plte(c)
190 }
191
192
193 type plte []color.Color
194
195
196 func (p plte) Colors() []color.Color {
197 return p
198 }
199
200
201
202
203
204
205
206 func BlackBody() palette.ColorMap {
207 return &luminance{
208 colors: []cieLAB{
209 {L: 0, A: 0, B: 0},
210 {L: 39.112572747719774, A: 55.92470934659227, B: 37.65159714510402},
211 {L: 58.45705480680232, A: 43.34389690857626, B: 65.95409116544081},
212 {L: 84.13253643355525, A: -6.459770854468639, B: 82.41994470228775},
213 {L: 100, A: 0, B: 0}},
214 scalars: []float64{0, 0.39112572747719776, 0.5845705480680232, 0.8413253643355525, 1},
215 alpha: 1,
216 }
217 }
218
219
220
221
222
223
224
225 func ExtendedBlackBody() palette.ColorMap {
226 return &luminance{
227 colors: []cieLAB{
228 {L: 0, A: 0, B: 0},
229 {L: 21.873483862751876, A: 50.19882295659109, B: -74.66982659778306},
230 {L: 34.506542513775905, A: 75.41302687474061, B: -88.73807072507786},
231 {L: 47.02980511087303, A: 70.93217189227919, B: 33.59880053746508},
232 {L: 65.17482203230537, A: 49.14591409658836, B: 56.86480950937553},
233 {L: 84.13253643355525, A: -6.459770854468639, B: 82.41994470228775},
234 {L: 100, A: 0, B: 0},
235 },
236 scalars: []float64{0, 0.21873483862751875, 0.34506542513775906, 0.4702980511087303,
237 0.6517482203230537, 0.8413253643355525, 1},
238 alpha: 1,
239 }
240 }
241
242
243
244
245
246
247
248
249
250
251
252
253 func Kindlmann() palette.ColorMap {
254 return &luminance{
255 colors: []cieLAB{
256 {L: 0, A: 0, B: 0},
257 {L: 10.479520542426698, A: 34.05557958902206, B: -34.21934877170809},
258 {L: 21.03011379005111, A: 52.30473571100955, B: -61.852601228346536},
259 {L: 31.03098927978494, A: 23.814976212074402, B: -57.73419358300511},
260 {L: 40.21480513626115, A: -24.858012706049536, B: -7.322176588219942},
261 {L: 52.73108089333358, A: -19.064976357731634, B: -25.558178073848147},
262 {L: 60.007326812392634, A: -61.75624590074585, B: 56.43522875191319},
263 {L: 69.81578343076002, A: -58.33353084882392, B: 68.37457857626646},
264 {L: 79.55703752324776, A: -22.50477758899383, B: 78.57946686200843},
265 {L: 89.818961593653, A: 7.586705160677109, B: 15.375961528833981},
266 {L: 100, A: 0, B: 0},
267 },
268 scalars: []float64{0, 0.10479520542426699, 0.2103011379005111, 0.3103098927978494,
269 0.4021480513626115, 0.5273108089333358, 0.6000732681239264, 0.6981578343076003,
270 0.7955703752324775, 0.89818961593653, 1},
271 alpha: 1,
272 }
273 }
274
275
276
277
278
279
280 func ExtendedKindlmann() palette.ColorMap {
281 return &luminance{
282 colors: []cieLAB{
283 {L: 0, A: 0, B: 0},
284 {L: 13.371291966477482, A: 40.39368469479174, B: -47.73239449160565},
285 {L: 25.072421338587574, A: -18.01441053740843, B: -5.313556572210176},
286 {L: 37.411516363056116, A: -43.058336774976055, B: 39.30203907343062},
287 {L: 49.75026355291354, A: -15.774050138318895, B: 53.507917567416094},
288 {L: 61.643756252245225, A: 52.67703578954919, B: 43.82595336046358},
289 {L: 74.93187540089825, A: 50.92061741619164, B: -30.235411697966242},
290 {L: 87.64732748562544, A: 14.355163639545697, B: -17.471161313826332},
291 {L: 100, A: 0, B: 0},
292 },
293 scalars: []float64{0, 0.13371291966477483, 0.25072421338587575, 0.37411516363056113,
294 0.4975026355291354, 0.6164375625224523, 0.7493187540089825, 0.8764732748562544, 1},
295 alpha: 1,
296 }
297 }
298
View as plain text