Skip to content

Commit 828f856

Browse files
safarelisharkdp
authored andcommitted
add hsv support (#27)
* added `hsv`, `hsva`, `toHSVA`
1 parent 4ce675a commit 828f856

File tree

2 files changed

+76
-22
lines changed

2 files changed

+76
-22
lines changed

src/Color.purs

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,16 @@ module Color
2323
, rgb'
2424
, hsla
2525
, hsl
26+
, hsva
27+
, hsv
2628
, xyz
2729
, lab
2830
, lch
2931
, fromHexString
3032
, fromInt
3133
-- Convert
3234
, toHSLA
35+
, toHSVA
3336
, toRGBA
3437
, toRGBA'
3538
, toXYZ
@@ -160,22 +163,40 @@ rgba' r g b a = rgba (round $ r * 255.0)
160163
rgb' :: Number -> Number -> Number -> Color
161164
rgb' r g b = rgba' r g b 1.0
162165

163-
-- | Create a `Color` from hue, saturation, lightness and alpha values. The
164-
-- | hue is given in degrees, as a `Number` between 0.0 and 360.0. Saturation,
165-
-- | lightness and alpha are numbers between 0.0 and 1.0.
166+
-- | Create a `Color` from Hue, Saturation, Lightness and Alpha values. The
167+
-- | Hue is given in degrees, as a `Number` between 0.0 and 360.0. Saturation,
168+
-- | Lightness and Alpha are numbers between 0.0 and 1.0.
166169
hsla :: Number -> Number -> Number -> Number -> Color
167170
hsla h s l a = HSLA h' s' l' a'
168171
where h' = if h == 360.0 then h else h `modPos` 360.0
169172
s' = clamp 0.0 1.0 s
170173
l' = clamp 0.0 1.0 l
171174
a' = clamp 0.0 1.0 a
172175

173-
-- | Create a `Color` from hue, saturation and lightness values. The hue is
174-
-- | given in degrees, as a `Number` between 0.0 and 360.0. Both saturation and
175-
-- | lightness are numbers between 0.0 and 1.0.
176+
-- | Create a `Color` from Hue, Saturation and Lightness values. The Hue is
177+
-- | given in degrees, as a `Number` between 0.0 and 360.0. Both Saturation and
178+
-- | Lightness are numbers between 0.0 and 1.0.
176179
hsl :: Number -> Number -> Number -> Color
177180
hsl h s l = hsla h s l 1.0
178181

182+
-- | Create a `Color` from Hue, Saturation, Value and Alpha values. The
183+
-- | Hue is given in degrees, as a `Number` between 0.0 and 360.0. Saturation,
184+
-- | Value and Alpha are numbers between 0.0 and 1.0.
185+
hsva :: Number Number Number Number Color
186+
hsva h s 0.0 a = HSLA h (s / (2.0 - s)) 0.0 a
187+
hsva h 0.0 1.0 a = HSLA h 0.0 1.0 a
188+
hsva h s' v' a = HSLA h s l a
189+
where
190+
tmp = (2.0 - s') * v'
191+
s = s' * v' / (if tmp < 1.0 then tmp else 2.0 - tmp)
192+
l = tmp / 2.0
193+
194+
-- | Create a `Color` from Hue, Saturation and Value values. The Hue is
195+
-- | given in degrees, as a `Number` between 0.0 and 360.0. Both Saturation and
196+
-- | Value are numbers between 0.0 and 1.0.
197+
hsv :: Number Number Number Color
198+
hsv h s v = hsva h s v 1.0
199+
179200
-- | Create a `Color` from XYZ coordinates in the CIE 1931 color space. Note
180201
-- | that a `Color` always represents a color in the sRGB gamut (colors that
181202
-- | can be represented on a typical computer screen) while the XYZ color space
@@ -273,11 +294,22 @@ fromInt m = rgb r g b
273294
r = (n `shr` 16) .&. 0xff
274295
n = clamp 0 0xffffff m
275296

276-
-- | Convert a `Color` to its hue, saturation, lightness and alpha values. See
297+
-- | Convert a `Color` to its Hue, Saturation, Lightness and Alpha values. See
277298
-- | `hsla` for the ranges of each channel.
278299
toHSLA :: Color -> { h :: Number, s :: Number, l :: Number, a :: Number }
279300
toHSLA (HSLA h s l a) = { h, s, l, a }
280301

302+
-- | Convert a `Color` to its Hue, Saturation, Value and Alpha values. See
303+
-- | `hsva` for the ranges of each channel.
304+
toHSVA :: Color -> { h :: Number, s :: Number, v :: Number, a :: Number }
305+
toHSVA (HSLA h s 0.0 a) = { h, s: 2.0 * s / (1.0 + s), v: 0.0, a }
306+
toHSVA (HSLA h 0.0 1.0 a) = { h, s: 0.0, v: 1.0, a }
307+
toHSVA (HSLA h s' l' a) = { h, s, v, a }
308+
where
309+
tmp = s' * (if l' < 0.5 then l' else 1.0 - l')
310+
s = 2.0 * tmp / (l' + tmp)
311+
v = l' + tmp
312+
281313
-- | Convert a `Color` to its red, green, blue and alpha values. The RGB values
282314
-- | are integers in the range from 0 to 255. The alpha channel is a number
283315
-- | between 0.0 and 1.0.

test/Main.purs

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
module Test.Main where
22

33
import Prelude
4-
import Color (Color, ColorSpace(..), rgb', toHexString, toRGBA, toHSLA, saturate, lighten, white, black, graytone, mix, rgb, distance, textColor, contrast, luminance, brightness, fromInt, toGray, desaturate, darken, complementary, rotateHue, rgba, cssStringRGBA, hsla, cssStringHSLA, hsl, fromHexString, lch, toLCh, lab, toLab, xyz, toXYZ)
4+
5+
import Color (Color, ColorSpace(..), rgb', toHexString, toRGBA, toHSLA, toHSVA, saturate, lighten, white, black, graytone, mix, rgb, distance, textColor, contrast, luminance, brightness, fromInt, toGray, desaturate, darken, complementary, rotateHue, rgba, cssStringRGBA, hsla, cssStringHSLA, hsl, hsva, fromHexString, lch, toLCh, lab, toLab, xyz, toXYZ)
56
import Color.Blending (BlendMode(..), blend)
67
import Color.Scale (grayscale, sample, colors, uniformScale, colorStop, colorScale)
78
import Color.Scheme.X11 (orangered, seagreen, yellow, red, blue, magenta, hotpink, purple, pink, darkslateblue, aquamarine, cyan, green, lime)
@@ -24,20 +25,20 @@ import Test.Unit.Main (runTest)
2425
almostEqual :: forall e. Color -> Color -> Aff e Unit
2526
almostEqual expected actual =
2627
if almostEqual' expected actual then success
27-
else failure $ "\n expected: " <> show expected <>
28-
"\n got: " <> show actual
29-
30-
where
31-
abs n = if n < 0 then 0 - n else n
32-
aE n1 n2 = abs (n1 - n2) <= 1
33-
almostEqual' col1 col2 =
34-
aE c1.r c2.r &&
35-
aE c1.g c2.g &&
36-
aE c1.b c2.b
37-
38-
where
39-
c1 = toRGBA col1
40-
c2 = toRGBA col2
28+
else failure $ "\n expected: " <> show' expected <>
29+
"\n got: " <> show' actual
30+
where
31+
show' c = case toHSLA c of
32+
{h, s, l, a} -> cssStringRGBA c <> " " <> cssStringHSLA c
33+
abs n = if n < 0 then 0 - n else n
34+
aE n1 n2 = abs (n1 - n2) <= 1
35+
almostEqual' col1 col2 =
36+
aE c1.r c2.r &&
37+
aE c1.g c2.g &&
38+
aE c1.b c2.b
39+
where
40+
c1 = toRGBA col1
41+
c2 = toRGBA col2
4142

4243
main :: forall e. Eff (console :: CONSOLE, testOutput :: TESTOUTPUT, avar :: AVAR | e) Unit
4344
main = runTest do
@@ -123,6 +124,27 @@ main = runTest do
123124
hue <- 0 .. 360
124125
pure $ xyzRoundtrip (toNumber hue) 0.2 0.8
125126

127+
test "hsv / toHSV (HSV -> HSL -> HSV, HSL -> HSV -> HSL)" do
128+
let
129+
hsvRoundtrip h' s' l_v' a' = do
130+
almostEqual colorIn1 colorOut1
131+
almostEqual colorIn2 colorOut2
132+
where
133+
colorIn1 = hsla h' s' l_v' a'
134+
colorIn2 = hsva h' s' l_v' a'
135+
colorOut1 = case toHSVA colorIn1 of { h, s, v, a } -> hsva h s v a
136+
colorOut2 = case toHSVA colorIn2 of { h, s, v, a } -> hsva h s v a
137+
sequence_ do
138+
hue <- 0 .. 3
139+
saturation <- 0 .. 5
140+
lightness <- 0 .. 5
141+
[ hsvRoundtrip 90.0 (toNumber saturation / 5.0) 1.0 1.0
142+
, hsvRoundtrip 90.0 1.0 (toNumber lightness / 5.0) 1.0
143+
, hsvRoundtrip 90.0 (toNumber saturation / 5.0) 0.0 1.0
144+
, hsvRoundtrip 90.0 0.0 (toNumber lightness / 5.0) 1.0
145+
, hsvRoundtrip (toNumber (hue * 90)) (toNumber saturation / 5.0) (toNumber lightness / 5.0) 1.0
146+
]
147+
126148
test "lab / toLab (Lab -> HSL -> Lab)" do
127149
let labRoundtrip h' s' l' =
128150
case toLab col of

0 commit comments

Comments
 (0)