Skip to content

Commit ce86248

Browse files
authored
feat: add RGB to HSL color format conversion algorithm (#1475)
* feat: add RGB to HSL color format conversion algorithm * test: add self-tests for rgb to hsl conversion algorithm * fix: change function code to concise format * fix: use throw and segregate the test cases * chore: clean up the test format * chore: use correct styling
1 parent ca761d8 commit ce86248

File tree

2 files changed

+128
-0
lines changed

2 files changed

+128
-0
lines changed

Conversions/RgbHslConversion.js

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/**
2+
* Given a color in RGB format, convert it to HSL format.
3+
*
4+
* For more info: https://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl/
5+
*
6+
* @param {number[]} colorRgb - One dimensional array of integers (RGB color format).
7+
* @returns {number[]} - One dimensional array of integers (HSL color format).
8+
*
9+
* @example
10+
* const colorRgb = [24, 98, 118]
11+
*
12+
* const result = rgbToHsl(colorRgb)
13+
*
14+
* // The function returns the corresponding color in HSL format:
15+
* // result = [193, 66, 28]
16+
*/
17+
18+
const checkRgbFormat = (colorRgb) => colorRgb.every((c) => c >= 0 && c <= 255)
19+
20+
const rgbToHsl = (colorRgb) => {
21+
if (!checkRgbFormat(colorRgb)) {
22+
throw new Error('Input is not a valid RGB color.')
23+
}
24+
25+
let colorHsl = colorRgb
26+
27+
let red = Math.round(colorRgb[0])
28+
let green = Math.round(colorRgb[1])
29+
let blue = Math.round(colorRgb[2])
30+
31+
const limit = 255
32+
33+
colorHsl[0] = red / limit
34+
colorHsl[1] = green / limit
35+
colorHsl[2] = blue / limit
36+
37+
let minValue = Math.min(...colorHsl)
38+
let maxValue = Math.max(...colorHsl)
39+
40+
let channel = 0
41+
42+
if (maxValue === colorHsl[1]) {
43+
channel = 1
44+
} else if (maxValue === colorHsl[2]) {
45+
channel = 2
46+
}
47+
48+
let luminance = (minValue + maxValue) / 2
49+
50+
let saturation = 0
51+
52+
if (minValue !== maxValue) {
53+
if (luminance <= 0.5) {
54+
saturation = (maxValue - minValue) / (maxValue + minValue)
55+
} else {
56+
saturation = (maxValue - minValue) / (2 - maxValue - minValue)
57+
}
58+
}
59+
60+
let hue = 0
61+
62+
if (saturation !== 0) {
63+
if (channel === 0) {
64+
hue = (colorHsl[1] - colorHsl[2]) / (maxValue - minValue)
65+
} else if (channel === 1) {
66+
hue = 2 + (colorHsl[2] - colorHsl[0]) / (maxValue - minValue)
67+
} else {
68+
hue = 4 + (colorHsl[0] - colorHsl[1]) / (maxValue - minValue)
69+
}
70+
}
71+
72+
hue *= 60
73+
74+
if (hue < 0) {
75+
hue += 360
76+
}
77+
78+
colorHsl[0] = Math.round(hue)
79+
colorHsl[1] = Math.round(saturation * 100)
80+
colorHsl[2] = Math.round(luminance * 100)
81+
82+
return colorHsl
83+
}
84+
85+
export { rgbToHsl }
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { rgbToHsl } from '../RgbHslConversion'
2+
describe('RgbHslConversion', () => {
3+
test.each([
4+
[
5+
[215, 19, 180],
6+
[311, 84, 46]
7+
],
8+
[
9+
[21, 190, 18],
10+
[119, 83, 41]
11+
],
12+
[
13+
[80, 100, 160],
14+
[225, 33, 47]
15+
],
16+
[
17+
[80, 1, 16],
18+
[349, 98, 16]
19+
],
20+
[
21+
[8, 20, 0],
22+
[96, 100, 4]
23+
],
24+
[
25+
[0, 0, 0],
26+
[0, 0, 0]
27+
],
28+
[
29+
[255, 255, 255],
30+
[0, 0, 100]
31+
]
32+
])('Should return the color in HSL format.', (colorRgb, expected) => {
33+
expect(rgbToHsl(colorRgb)).toEqual(expected)
34+
})
35+
36+
test.each([
37+
[[256, 180, 9], 'Input is not a valid RGB color.'],
38+
[[-90, 46, 8], 'Input is not a valid RGB color.'],
39+
[[1, 39, 900], 'Input is not a valid RGB color.']
40+
])('Should return the error message.', (colorRgb, expected) => {
41+
expect(() => rgbToHsl(colorRgb)).toThrowError(expected)
42+
})
43+
})

0 commit comments

Comments
 (0)