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
19 type smoothDiverging struct {
20
21 start, end msh
22
23
24
25 convergeM float64
26
27
28
29 alpha float64
30
31
32
33 min, max float64
34
35
36
37 convergePoint float64
38 }
39
40
41
42
43
44
45
46
47
48
49
50 func NewSmoothDiverging(start, end color.Color, convergeM float64) palette.DivergingColorMap {
51 return newSmoothDiverging(colorToMSH(start), colorToMSH(end), convergeM)
52 }
53
54
55
56
57
58
59 func newSmoothDiverging(start, end msh, convergeM float64) palette.DivergingColorMap {
60 return &smoothDiverging{
61 start: start,
62 end: end,
63 convergeM: convergeM,
64 convergePoint: math.NaN(),
65 alpha: 1,
66 }
67 }
68
69
70 func (p *smoothDiverging) At(v float64) (color.Color, error) {
71 if err := checkRange(p.min, p.max, v); err != nil {
72 return nil, err
73 }
74 convergePoint := (p.convergePoint - p.min) / (p.max - p.min)
75 scalar := (v - p.min) / (p.max - p.min)
76 o := p.interpolateMSHDiverging(scalar, convergePoint).cieLAB().cieXYZ().rgb().sRGBA(p.alpha)
77 if !inUnitRange(o.R) || !inUnitRange(o.G) || !inUnitRange(o.B) || !inUnitRange(o.A) {
78 return nil, fmt.Errorf("moreland: invalid color r:%g, g:%g, b:%g, a:%g", o.R, o.G, o.B, o.A)
79 }
80 return o, nil
81 }
82
83 func inUnitRange(v float64) bool { return 0 <= v && v <= 1 }
84
85
86 func (p *smoothDiverging) SetMax(v float64) {
87 p.max = v
88 p.convergePoint = (p.min + p.max) / 2
89 }
90
91
92 func (p *smoothDiverging) SetMin(v float64) {
93 p.min = v
94 p.convergePoint = (p.min + p.max) / 2
95 }
96
97
98 func (p *smoothDiverging) Max() float64 {
99 return p.max
100 }
101
102
103 func (p *smoothDiverging) Min() float64 {
104 return p.min
105 }
106
107
108
109
110 func (p *smoothDiverging) SetAlpha(alpha float64) {
111 if !inUnitRange(alpha) {
112 panic(fmt.Errorf("invalid alpha: %g", alpha))
113 }
114 p.alpha = alpha
115 }
116
117
118 func (p *smoothDiverging) Alpha() float64 {
119 return p.alpha
120 }
121
122
123
124 func (p *smoothDiverging) SetConvergePoint(val float64) {
125 if val > p.Max() || val < p.Min() {
126 panic(fmt.Errorf("moreland: convergence point (%g) must be between min (%g) and max (%g)",
127 val, p.Min(), p.Max()))
128 }
129 p.convergePoint = val
130 }
131
132
133 func (p *smoothDiverging) ConvergePoint() float64 {
134 return p.convergePoint
135 }
136
137
138
139
140
141 func (p *smoothDiverging) interpolateMSHDiverging(scalar, convergePoint float64) msh {
142 startHTwist := hueTwist(p.start, p.convergeM)
143 endHTwist := hueTwist(p.end, p.convergeM)
144 if scalar < convergePoint {
145
146 interp := scalar / convergePoint
147 return msh{
148 M: (p.convergeM-p.start.M)*interp + p.start.M,
149 S: p.start.S * (1 - interp),
150 H: p.start.H + startHTwist*interp,
151 }
152 }
153
154 interp1 := (scalar - 1) / (convergePoint - 1)
155 interp2 := (scalar/convergePoint - 1)
156 var H float64
157 if scalar > convergePoint {
158 H = p.end.H + endHTwist*interp1
159 }
160 return msh{
161 M: (p.convergeM-p.end.M)*interp1 + p.end.M,
162 S: p.end.S * interp2,
163 H: H,
164 }
165 }
166
167
168 func (p smoothDiverging) Palette(n int) palette.Palette {
169 if p.Max() == 0 && p.Min() == 0 {
170 p.SetMin(0)
171 p.SetMax(1)
172 }
173 delta := (p.max - p.min) / float64(n-1)
174 c := make([]color.Color, n)
175 for i := range c {
176 v := p.min + delta*float64(i)
177 var err error
178 c[i], err = p.At(v)
179 if err != nil {
180 panic(err)
181 }
182 }
183 return plte(c)
184 }
185
186
187 func SmoothBlueRed() palette.DivergingColorMap {
188 start := msh{
189 M: 80,
190 S: 1.08,
191 H: -1.1,
192 }
193 end := msh{
194 M: 80,
195 S: 1.08,
196 H: 0.5,
197 }
198 return newSmoothDiverging(start, end, 88)
199 }
200
201
202 func SmoothPurpleOrange() palette.DivergingColorMap {
203 start := msh{
204 M: 64.97539711,
205 S: 0.899434815,
206 H: -0.899431964,
207 }
208 end := msh{
209 M: 85.00850996,
210 S: 0.949730284,
211 H: 0.950636521,
212 }
213 return newSmoothDiverging(start, end, 88)
214 }
215
216
217 func SmoothGreenPurple() palette.DivergingColorMap {
218 start := msh{
219 M: 78.04105346,
220 S: 0.885011982,
221 H: 2.499491379,
222 }
223 end := msh{
224 M: 64.97539711,
225 S: 0.899434815,
226 H: -0.899431964,
227 }
228 return newSmoothDiverging(start, end, 88)
229 }
230
231
232 func SmoothBlueTan() palette.DivergingColorMap {
233 start := msh{
234 M: 79.94788321,
235 S: 0.798754784,
236 H: -1.401313221,
237 }
238 end := msh{
239 M: 80.07193125,
240 S: 0.799798811,
241 H: 1.401089787,
242 }
243 return newSmoothDiverging(start, end, 88)
244 }
245
246
247 func SmoothGreenRed() palette.DivergingColorMap {
248 start := msh{
249 M: 78.04105346,
250 S: 0.885011982,
251 H: 2.499491379,
252 }
253 end := msh{
254 M: 76.96722122,
255 S: 0.949483656,
256 H: 0.499492043,
257 }
258 return newSmoothDiverging(start, end, 88)
259 }
260
View as plain text