1 package colorful
2
3 import "math"
4
5
6
7
8
9
10
11
12
13
14
15 var hSLuvD65 = [3]float64{0.95045592705167, 1.0, 1.089057750759878}
16
17 func LuvLChToHSLuv(l, c, h float64) (float64, float64, float64) {
18
19 c *= 100.0
20 l *= 100.0
21
22 var s, max float64
23 if l > 99.9999999 || l < 0.00000001 {
24 s = 0.0
25 } else {
26 max = maxChromaForLH(l, h)
27 s = c / max * 100.0
28 }
29 return h, clamp01(s / 100.0), clamp01(l / 100.0)
30 }
31
32 func HSLuvToLuvLCh(h, s, l float64) (float64, float64, float64) {
33 l *= 100.0
34 s *= 100.0
35
36 var c, max float64
37 if l > 99.9999999 || l < 0.00000001 {
38 c = 0.0
39 } else {
40 max = maxChromaForLH(l, h)
41 c = max / 100.0 * s
42 }
43
44
45 return clamp01(l / 100.0), c / 100.0, h
46 }
47
48 func LuvLChToHPLuv(l, c, h float64) (float64, float64, float64) {
49
50 c *= 100.0
51 l *= 100.0
52
53 var s, max float64
54 if l > 99.9999999 || l < 0.00000001 {
55 s = 0.0
56 } else {
57 max = maxSafeChromaForL(l)
58 s = c / max * 100.0
59 }
60 return h, s / 100.0, l / 100.0
61 }
62
63 func HPLuvToLuvLCh(h, s, l float64) (float64, float64, float64) {
64
65 l *= 100.0
66 s *= 100.0
67
68 var c, max float64
69 if l > 99.9999999 || l < 0.00000001 {
70 c = 0.0
71 } else {
72 max = maxSafeChromaForL(l)
73 c = max / 100.0 * s
74 }
75 return l / 100.0, c / 100.0, h
76 }
77
78
79
80
81
82
83 func HSLuv(h, s, l float64) Color {
84
85 l, u, v := LuvLChToLuv(HSLuvToLuvLCh(h, s, l))
86 return LinearRgb(XyzToLinearRgb(LuvToXyzWhiteRef(l, u, v, hSLuvD65))).Clamped()
87 }
88
89
90
91
92
93
94 func HPLuv(h, s, l float64) Color {
95
96 l, u, v := LuvLChToLuv(HPLuvToLuvLCh(h, s, l))
97 return LinearRgb(XyzToLinearRgb(LuvToXyzWhiteRef(l, u, v, hSLuvD65))).Clamped()
98 }
99
100
101
102
103 func (col Color) HSLuv() (h, s, l float64) {
104
105 return LuvLChToHSLuv(col.LuvLChWhiteRef(hSLuvD65))
106 }
107
108
109
110
111
112
113
114 func (col Color) HPLuv() (h, s, l float64) {
115 return LuvLChToHPLuv(col.LuvLChWhiteRef(hSLuvD65))
116 }
117
118
119
120
121
122
123 func (c1 Color) DistanceHSLuv(c2 Color) float64 {
124 h1, s1, l1 := c1.HSLuv()
125 h2, s2, l2 := c2.HSLuv()
126 return math.Sqrt(sq((h1-h2)/100.0) + sq(s1-s2) + sq(l1-l2))
127 }
128
129
130
131
132
133
134 func (c1 Color) DistanceHPLuv(c2 Color) float64 {
135 h1, s1, l1 := c1.HPLuv()
136 h2, s2, l2 := c2.HPLuv()
137 return math.Sqrt(sq((h1-h2)/100.0) + sq(s1-s2) + sq(l1-l2))
138 }
139
140 var m = [3][3]float64{
141 {3.2409699419045214, -1.5373831775700935, -0.49861076029300328},
142 {-0.96924363628087983, 1.8759675015077207, 0.041555057407175613},
143 {0.055630079696993609, -0.20397695888897657, 1.0569715142428786},
144 }
145
146 const kappa = 903.2962962962963
147 const epsilon = 0.0088564516790356308
148
149 func maxChromaForLH(l, h float64) float64 {
150 hRad := h / 360.0 * math.Pi * 2.0
151 minLength := math.MaxFloat64
152 for _, line := range getBounds(l) {
153 length := lengthOfRayUntilIntersect(hRad, line[0], line[1])
154 if length > 0.0 && length < minLength {
155 minLength = length
156 }
157 }
158 return minLength
159 }
160
161 func getBounds(l float64) [6][2]float64 {
162 var sub2 float64
163 var ret [6][2]float64
164 sub1 := math.Pow(l+16.0, 3.0) / 1560896.0
165 if sub1 > epsilon {
166 sub2 = sub1
167 } else {
168 sub2 = l / kappa
169 }
170 for i := range m {
171 for k := 0; k < 2; k++ {
172 top1 := (284517.0*m[i][0] - 94839.0*m[i][2]) * sub2
173 top2 := (838422.0*m[i][2]+769860.0*m[i][1]+731718.0*m[i][0])*l*sub2 - 769860.0*float64(k)*l
174 bottom := (632260.0*m[i][2]-126452.0*m[i][1])*sub2 + 126452.0*float64(k)
175 ret[i*2+k][0] = top1 / bottom
176 ret[i*2+k][1] = top2 / bottom
177 }
178 }
179 return ret
180 }
181
182 func lengthOfRayUntilIntersect(theta, x, y float64) (length float64) {
183 length = y / (math.Sin(theta) - x*math.Cos(theta))
184 return
185 }
186
187 func maxSafeChromaForL(l float64) float64 {
188 minLength := math.MaxFloat64
189 for _, line := range getBounds(l) {
190 m1 := line[0]
191 b1 := line[1]
192 x := intersectLineLine(m1, b1, -1.0/m1, 0.0)
193 dist := distanceFromPole(x, b1+x*m1)
194 if dist < minLength {
195 minLength = dist
196 }
197 }
198 return minLength
199 }
200
201 func intersectLineLine(x1, y1, x2, y2 float64) float64 {
202 return (y1 - y2) / (x2 - x1)
203 }
204
205 func distanceFromPole(x, y float64) float64 {
206 return math.Sqrt(math.Pow(x, 2.0) + math.Pow(y, 2.0))
207 }
208
View as plain text