diff --git a/Conversions/RgbHslConversion.js b/Conversions/RgbHslConversion.js new file mode 100644 index 0000000000..7e014f1318 --- /dev/null +++ b/Conversions/RgbHslConversion.js @@ -0,0 +1,85 @@ +/** + * Given a color in RGB format, convert it to HSL format. + * + * For more info: https://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl/ + * + * @param {number[]} colorRgb - One dimensional array of integers (RGB color format). + * @returns {number[]} - One dimensional array of integers (HSL color format). + * + * @example + * const colorRgb = [24, 98, 118] + * + * const result = rgbToHsl(colorRgb) + * + * // The function returns the corresponding color in HSL format: + * // result = [193, 66, 28] + */ + +const checkRgbFormat = (colorRgb) => colorRgb.every((c) => c >= 0 && c <= 255) + +const rgbToHsl = (colorRgb) => { + if (!checkRgbFormat(colorRgb)) { + throw new Error('Input is not a valid RGB color.') + } + + let colorHsl = colorRgb + + let red = Math.round(colorRgb[0]) + let green = Math.round(colorRgb[1]) + let blue = Math.round(colorRgb[2]) + + const limit = 255 + + colorHsl[0] = red / limit + colorHsl[1] = green / limit + colorHsl[2] = blue / limit + + let minValue = Math.min(...colorHsl) + let maxValue = Math.max(...colorHsl) + + let channel = 0 + + if (maxValue === colorHsl[1]) { + channel = 1 + } else if (maxValue === colorHsl[2]) { + channel = 2 + } + + let luminance = (minValue + maxValue) / 2 + + let saturation = 0 + + if (minValue !== maxValue) { + if (luminance <= 0.5) { + saturation = (maxValue - minValue) / (maxValue + minValue) + } else { + saturation = (maxValue - minValue) / (2 - maxValue - minValue) + } + } + + let hue = 0 + + if (saturation !== 0) { + if (channel === 0) { + hue = (colorHsl[1] - colorHsl[2]) / (maxValue - minValue) + } else if (channel === 1) { + hue = 2 + (colorHsl[2] - colorHsl[0]) / (maxValue - minValue) + } else { + hue = 4 + (colorHsl[0] - colorHsl[1]) / (maxValue - minValue) + } + } + + hue *= 60 + + if (hue < 0) { + hue += 360 + } + + colorHsl[0] = Math.round(hue) + colorHsl[1] = Math.round(saturation * 100) + colorHsl[2] = Math.round(luminance * 100) + + return colorHsl +} + +export { rgbToHsl } diff --git a/Conversions/test/RgbHslConversion.test.js b/Conversions/test/RgbHslConversion.test.js new file mode 100644 index 0000000000..5dec4835cd --- /dev/null +++ b/Conversions/test/RgbHslConversion.test.js @@ -0,0 +1,43 @@ +import { rgbToHsl } from '../RgbHslConversion' +describe('RgbHslConversion', () => { + test.each([ + [ + [215, 19, 180], + [311, 84, 46] + ], + [ + [21, 190, 18], + [119, 83, 41] + ], + [ + [80, 100, 160], + [225, 33, 47] + ], + [ + [80, 1, 16], + [349, 98, 16] + ], + [ + [8, 20, 0], + [96, 100, 4] + ], + [ + [0, 0, 0], + [0, 0, 0] + ], + [ + [255, 255, 255], + [0, 0, 100] + ] + ])('Should return the color in HSL format.', (colorRgb, expected) => { + expect(rgbToHsl(colorRgb)).toEqual(expected) + }) + + test.each([ + [[256, 180, 9], 'Input is not a valid RGB color.'], + [[-90, 46, 8], 'Input is not a valid RGB color.'], + [[1, 39, 900], 'Input is not a valid RGB color.'] + ])('Should return the error message.', (colorRgb, expected) => { + expect(() => rgbToHsl(colorRgb)).toThrowError(expected) + }) +})