/*
* This file is part of AUX.
*
* AUX is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* AUX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
/**
* @module utils/colors
*
* @description Colors provides functions for easy-to-use color calculations
* and conversions. Functions requiring RGB or HSL color definitions as
* arguments (all `rgb2x` and `hsl2x`) can be called with different types of arguments
* to make using them more convenient. Examples:
* <ul>
* <li><code>RGBToHSL(240, 128, 128)</code></li>
* <li><code>RGBToHSL({'r':240,'g':128,'b':128}</code></li>
* <li><code>RGBToHSL([240, 128, 128])</code></li>
* </ul>
*
* The universal functions `color2x` take even more flexible arguments.
* The following examples all define the same color:
* <ul>
* <li><code>"lightcoral"</code></li>
* <li><code>"#F08080"</code></li>
* <li><code>[0,0.31,0.28]</code></li>
* <li><code>240,128,128</code></li>
* <li><code>{"r":240,"g":128,"b":128}</code></li>
* </ul>
*
*/
const color_names = {
lightcoral: 'f08080',
salmon: 'fa8072',
darksalmon: 'e9967a',
lightsalmon: 'ffa07a',
crimson: 'dc143c',
red: 'ff0000',
firebrick: 'b22222',
darkred: '8b0000',
pink: 'ffc0cb',
lightpink: 'ffb6c1',
hotpink: 'ff69b4',
deeppink: 'ff1493',
mediumvioletred: 'c71585',
palevioletred: 'db7093',
coral: 'ff7f50',
tomato: 'ff6347',
orangered: 'ff4500',
darkorange: 'ff8c00',
orange: 'ffa500',
gold: 'ffd700',
yellow: 'ffff00',
lightyellow: 'ffffe0',
lemonchiffon: 'fffacd',
lightgoldenrodyellow: 'fafad2',
papayawhip: 'ffefd5',
moccasin: 'ffe4b5',
peachpuff: 'ffdab9',
palegoldenrod: 'eee8aa',
khaki: 'f0e68c',
darkkhaki: 'bdb76b',
lavender: 'e6e6fa',
thistle: 'd8bfd8',
plum: 'dda0dd',
violet: 'ee82ee',
orchid: 'da70d6',
fuchsia: 'ff00ff',
magenta: 'ff00ff',
mediumorchid: 'ba55d3',
mediumpurple: '9370db',
amethyst: '9966cc',
blueviolet: '8a2be2',
darkviolet: '9400d3',
darkorchid: '9932cc',
darkmagenta: '8b008b',
purple: '800080',
indigo: '4b0082',
slateblue: '6a5acd',
darkslateblue: '483d8b',
mediumslateblue: '7b68ee',
greenyellow: 'adff2f',
chartreuse: '7fff00',
lawngreen: '7cfc00',
lime: '00ff00',
limegreen: '32cd32',
palegreen: '98fb98',
lightgreen: '90ee90',
mediumspringgreen: '00fa9a',
springgreen: '00ff7f',
mediumseagreen: '3cb371',
seagreen: '2e8b57',
forestgreen: '228b22',
green: '008000',
darkgreen: '006400',
yellowgreen: '9acd32',
olivedrab: '6b8e23',
olive: '808000',
darkolivegreen: '556b2f',
mediumaquamarine: '66cdaa',
darkseagreen: '8fbc8f',
lightseagreen: '20b2aa',
darkcyan: '008b8b',
teal: '008080',
aqua: '00ffff',
cyan: '00ffff',
lightcyan: 'e0ffff',
paleturquoise: 'afeeee',
aquamarine: '7fffd4',
turquoise: '40e0d0',
mediumturquoise: '48d1cc',
darkturquoise: '00ced1',
cadetblue: '5f9ea0',
steelblue: '4682b4',
lightsteelblue: 'b0c4de',
powderblue: 'b0e0e6',
lightblue: 'add8e6',
skyblue: '87ceeb',
lightskyblue: '87cefa',
deepskyblue: '00bfff',
dodgerblue: '1e90ff',
cornflowerblue: '6495ed',
royalblue: '4169e1',
blue: '0000ff',
mediumblue: '0000cd',
darkblue: '00008b',
navy: '000080',
midnightblue: '191970',
cornsilk: 'fff8dc',
blanchedalmond: 'ffebcd',
bisque: 'ffe4c4',
navajowhite: 'ffdead',
wheat: 'f5deb3',
burlywood: 'deb887',
tan: 'd2b48c',
rosybrown: 'bc8f8f',
sandybrown: 'f4a460',
goldenrod: 'daa520',
darkgoldenrod: 'b8860b',
peru: 'cd853f',
chocolate: 'd2691e',
saddlebrown: '8b4513',
sienna: 'a0522d',
brown: 'a52a2a',
maroon: '800000',
white: 'ffffff',
snow: 'fffafa',
honeydew: 'f0fff0',
mintcream: 'f5fffa',
azure: 'f0ffff',
aliceblue: 'f0f8ff',
ghostwhite: 'f8f8ff',
whitesmoke: 'f5f5f5',
seashell: 'fff5ee',
beige: 'f5f5dc',
oldlace: 'fdf5e6',
floralwhite: 'fffaf0',
ivory: 'fffff0',
antiquewhite: 'faebd7',
linen: 'faf0e6',
lavenderblush: 'fff0f5',
mistyrose: 'ffe4e1',
gainsboro: 'dcdcdc',
lightgrey: 'd3d3d3',
silver: 'c0c0c0',
darkgray: 'a9a9a9',
gray: '808080',
dimgray: '696969',
lightslategray: '778899',
slategray: '708090',
darkslategray: '2f4f4f',
black: '000000',
};
/* helpers */
function pad(n, width, z) {
z = z || '0';
n = n + '';
return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
}
function decodeArgs() {
/*
* Decode random arguments. Expects an arguments array
* from another function call as first argument and a
* series of member names the arguments should be decoded
* to. E.g. decodeArgs(arguments, "r", "g", "b")
* Arguments array can consist of:
* A single array: member names are mapped to the array
* ([[50,100,150]],"r","g","b") => {"r":50,"g":100,"b":150}
*
* An object already containing the members: object is returned
* ([{"r":50,"g":100,"b":150}, ...) => {"r":50,"g":100,"b":150}
*
* Multiple values: values are mapped to member names
* ([50,100,150],"r","g","b") => {"r":50,"g":100,"b":150}
*/
let out = {};
if (arguments[0][0] instanceof Array) {
for (let i = 0; i < arguments.length - 1; i++)
out[arguments[i + 1]] = arguments[0][0][i];
} else if (typeof arguments[0][0] === 'object') {
out = arguments[0][0];
} else {
for (let i = 0; i < arguments.length - 1; i++)
out[arguments[i + 1]] = arguments[0][i];
}
return out;
}
function decodeColor(args) {
/* detects type of input and disassembles it to a useful object.
* Only argument is an arguments array from another function.
* (["lightcoral"]) => {"type":"string","hex":"#F08080","string":"lightcoral","r":240,"g":128,"b":128}
* (["#F08080"]) => {"type":"hex","hex":"#F08080","r":240,"g":128,"b":128}
* ([[0,0.31,0.28]] => {"type":"hsl","h":0,"s":0.31,"l":0.28}
* ([240,128,128] => {"type":"rgb","r":240,"g":128,"b":128}
* ([{"r":240,"g":128,"b":128}] => {"type":"rgb","r":240,"g":128,"b":128}
*/
if (typeof args[0] === 'string' && args[0][0] === '#') {
// HEX string
const res = hexToRGB(args[0]);
res.type = 'hex';
res.hex = args[0];
return res;
}
if (typeof args[0] === 'string' && color_names[args[0]]) {
// color string
const res = hexToRGB('#' + color_names[args[0]]);
res.type = 'string';
res.string = args[0];
res.hex = color_names[args[0]];
return res;
}
const S = decodeArgs(arguments, 'a', 'b', 'c');
if ((S.a > 0 && S.a < 1) || (S.b > 0 && S.b < 1) || (S.c > 0 && S.c < 1)) {
// HSL
return { h: S.a, s: S.b, l: S.c, type: 'hsl' };
}
// RGB
return { r: S.a, g: S.b, b: S.c, type: 'rgb' };
}
/* RGB */
/**
* Returns a hex color string
* from a RGB color.
*
* @function RGBToHex
*
* @param {number|array|object|string} 1st_value - red (0..255) or object with members `r, g, b` or array of RGB or color name or hex string.
* @param {number} [2nd_value] - green (0..255) or saturation (0..1)
* @param {number} [3rd_value] - blue (0..255) or lightnes (0..1)
*
* @returns {string} Hex color string.
*/
export function RGBToHex() {
const col = decodeArgs(arguments, 'r', 'g', 'b');
return (
pad(parseInt(col.r).toString(16), 2) +
pad(parseInt(col.g).toString(16), 2) +
pad(parseInt(col.b).toString(16), 2)
);
}
/**
* Returns an object containing hue ('h'), saturation ('s') and lightness ('l')
* from a RGB color.
*
* @function RGBToHSL
*
* @param {number|array|object|string} 1st_value - red (0..255) or object with members `r, g, b` or array of RGB or color name or hex string.
* @param {number} [2nd_value] - green (0..255) or saturation (0..1)
* @param {number} [3rd_value] - blue (0..255) or lightnes (0..1)
*
* @returns {object} Object with members h, s and l as numbers (0..1)
*/
export function RGBToHSL() {
const col = decodeArgs(arguments, 'r', 'g', 'b');
let r = col.r,
g = col.g,
b = col.b;
r /= 255;
g /= 255;
b /= 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
const l = (max + min) / 2;
let h, s;
if (max === min) {
h = s = 0; // achromatic
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return {
h: Math.min(1, Math.max(0, h)),
s: Math.min(1, Math.max(0, s)),
l: Math.min(1, Math.max(0, l)),
};
}
/**
* Returns a hex color string either black or white at highest contrast compared to the argument
* from a RGB color.
*
* @function RGBToBW
*
* @param {number|array|object|string} 1st_value - red (0..255) or object with members `r, g, b` or array of RGB or color name or hex string.
* @param {number} [2nd_value] - green (0..255) or saturation (0..1)
* @param {number} [3rd_value] - blue (0..255) or lightnes (0..1)
*
* @returns {string} Hex color string.
*/
export function RGBToBW() {
return RGBToGray.apply(null, arguments) >= 0.5 ? '#000000' : '#ffffff';
}
/**
* Returns a hex color string either black or white at lowest contrast compared to the argument
* from a RGB color.
*
* @function RGBToWB
*
* @param {number|array|object|string} 1st_value - red (0..255) or object with members `r, g, b` or array of RGB or color name or hex string.
* @param {number} [2nd_value] - green (0..255) or saturation (0..1)
* @param {number} [3rd_value] - blue (0..255) or lightnes (0..1)
*
* @returns {string} Hex color string.
*/
export function RGBToWB() {
return RGBToGray.apply(null, arguments) < 0.5 ? '#000000' : '#ffffff';
}
/**
* Returns a hex color string of the grayscaled argument
* from a RGB color.
*
* @function RGBToGray
*
* @param {number|array|object|string} 1st_value - red (0..255) or object with members `r, g, b` or array of RGB or color name or hex string.
* @param {number} [2nd_value] - green (0..255) or saturation (0..1)
* @param {number} [3rd_value] - blue (0..255) or lightnes (0..1)
*
* @returns {string} Hex color string.
*/
export function RGBToGray() {
const col = decodeArgs(arguments, 'r', 'g', 'b');
return (col.r * 0.2126 + col.g * 0.7152 + col.b * 0.0722) / 255;
}
/* HSL */
export function hueToRGB(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return Math.min(1, Math.max(0, p));
}
/**
* Returns an object containing red ('r'), green ('g') and blue ('b')
* from a HSL color.
*
* @function HSLToRGB
*
* @param {number|array|object} 1st_value - hue (0..1) or object with members `h, s, l` or array of HSL.
* @param {number} [2nd_value] - saturation (0..1)
* @param {number} [3rd_value] - lightness (0..1)
*
* @returns {object} Object with members r, g and b as numbers (0..255)
*/
export function HSLToRGB() {
const col = decodeArgs(arguments, 'h', 's', 'l');
const h = col.h,
s = col.s,
l = col.l;
let r, g, b;
if (s === 0) {
r = g = b = l; // achromatic
} else {
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hueToRGB(p, q, h + 1 / 3);
g = hueToRGB(p, q, h);
b = hueToRGB(p, q, h - 1 / 3);
}
return { r: r * 255, g: g * 255, b: b * 255 };
}
/**
* Returns a hex color string
* from a HSL color.
*
* @function HSLToHex
*
* @param {number|array|object} 1st_value - hue (0..1) or object with members `h, s, l` or array of HSL.
* @param {number} [2nd_value] - saturation (0..1)
* @param {number} [3rd_value] - lightness (0..1)
*
* @returns {string} Hex color string.
*/
export function HSLToHex() {
return RGBToHex(HSLToRGB.apply(null, arguments));
}
/**
* Returns a hex color string either black or white at highest contrast compared to the argument
* from a HSL color.
*
* @function HSLToBW
*
* @param {number|array|object} 1st_value - hue (0..1) or object with members `h, s, l` or array of HSL.
* @param {number} [2nd_value] - saturation (0..1)
* @param {number} [3rd_value] - lightness (0..1)
*
* @returns {string} Hex color string.
*/
export function HSLToBW() {
return RGBToBW(HSLToRGB.apply(null, arguments));
}
/**
* Returns a hex color string either black or white at lowest contrast compared to the argument
* from a HSL color.
*
* @function HSLToWB
*
* @param {number|array|object} 1st_value - hue (0..1) or object with members `h, s, l` or array of HSL.
* @param {number} [2nd_value] - saturation (0..1)
* @param {number} [3rd_value] - lightness (0..1)
*
* @returns {string} Hex color string.
*/
export function HSLToWB() {
return RGBToWB(HSLToRGB.apply(null, arguments));
}
/**
* Returns a hex color string of the grayscaled argument
* from a HSL color.
*
* @function HSLToGray
*
* @param {number|array|object} 1st_value - hue (0..1) or object with members `h, s, l` or array of HSL.
* @param {number} [2nd_value] - saturation (0..1)
* @param {number} [3rd_value] - lightness (0..1)
*
* @returns {string} Hex color string.
*/
export function HSLToGray() {
return RGBToGray(HSLToRGB.apply(null, arguments));
}
/* HEX */
/**
* Returns an object containing red ('r'), green ('g') and blue ('b')
* from a hex color string.
*
* @function hexToRGB
*
* @param {string} hex - Hex color string.
*
* @returns {object} Object with members r, g and b as numbers (0..255)
*/
export function hexToRGB(hex) {
hex = hex || '000000';
if (hex[0] === '#') hex = hex.substring(1);
if (hex.length === 3)
return {
r: parseInt('0x' + hex[0] + hex[0]),
g: parseInt('0x' + hex[1] + hex[1]),
b: parseInt('0x' + hex[2] + hex[2]),
};
return {
r: parseInt('0x' + hex.substring(0, 2)),
g: parseInt('0x' + hex.substring(2, 4)),
b: parseInt('0x' + hex.substring(4)),
};
}
/**
* Returns an object containing hue ('h'), saturation ('s') and lightness ('l')
* from a hex color string.
*
* @function hexToHSL
*
* @param {string} hex - Hex color string.
*
* @returns {object} Object with members h, s and l as numbers (0..1)
*/
export function hexToHSL(hex) {
return RGBToHSL(hexToRGB(hex));
}
/**
* Returns a hex color string either black or white at highest contrast compared to the argument
* from a hex color string.
*
* @function hexToBW
*
* @param {string} hex - Hex color string.
*
* @returns {string} Hex color string.
*/
export function hexToBW(hex) {
return RGBToBW(hexToRGB(hex));
}
/**
* Returns a hex color string either black or white at lowest contrast compared to the argument
* from a hex color string.
*
* @function hexToWB
*
* @param {string} hex - Hex color string.
*
* @returns {string} Hex color string.
*/
export function hexToWB(hex) {
return RGBToWB(hexToRGB(hex));
}
/**
* Returns a hex color string of the grayscaled argument
* from a hex color string.
*
* @function hexToGray
*
* @param {string} hex - Hex color string.
*
* @returns {string} Hex color string.
*/
export function hexToGray(hex) {
return RGBToGray(hexToRGB(hex));
}
/* STRING */
/**
* Returns a hex color string
* from a color name.
*
* @function nameToHex
*
* @param {string} color - Color name.
*
* @returns {string} Hex color string.
*/
export function nameToHex(name) {
return color_names[name.toLowerCase];
}
/**
* Returns an object containing red ('r'), green ('g') and blue ('b')
* from a color name.
*
* @function nameToRGB
*
* @param {string} color - Color name.
*
* @returns {object} Object with members r, g and b as numbers (0..255)
*/
export function nameToRGB(name) {
return hexToRGB(color_names[name.toLowerCase]);
}
/**
* Returns an object containing hue ('h'), saturation ('s') and lightness ('l')
* from a color name.
*
* @function nameToHSL
*
* @param {string} color - Color name.
*
* @returns {object} Object with members h, s and l as numbers (0..1)
*/
export function nameToHSL(name) {
return hexToHSL(color_names[name.toLowerCase]);
}
/**
* Returns a hex color string either black or white at highest contrast compared to the argument
* from a color name.
*
* @function nameToBW
*
* @param {string} color - Color name.
*
* @returns {string} Hex color string.
*/
export function nameToBW(name) {
return hexToBW(color_names[name.toLowerCase]);
}
/**
* Returns a hex color string either black or white at lowest contrast compared to the argument
* from a color name.
*
* @function nameToWB
*
* @param {string} color - Color name.
*
* @returns {string} Hex color string.
*/
export function nameToWB(name) {
return hexToWB(color_names[name.toLowerCase]);
}
/**
* Returns a hex color string of the grayscaled argument
* from a color name.
*
* @function nameToGray
*
* @param {string} color - Color name.
*
* @returns {string} Hex color string.
*/
export function nameToGray(name) {
return hexToGray(color_names[name.toLowerCase]);
}
/* COLOR */
/**
* Returns an object containing red ('r'), green ('g') and blue ('b')
* from any type of valid color.
*
* @function colorToRGB
*
* @param {number|array|object|string} 1st_value - red (0..255) or hue (0..1) or object with members `r, g, b` or `h, s, l` or array of RGB or HSL or color name or hex string.
* @param {number} [2nd_value] - green (0..255) or saturation (0..1)
* @param {number} [3rd_value] - blue (0..255) or lightnes (0..1)
*
* @returns {object} Object with members r, g and b as numbers (0..255)
*/
export function colorToRGB() {
const C = decodeColor(arguments);
switch (C.type) {
case 'rgb':
return C;
case 'hex':
return C;
case 'hsl':
return RGBToHSL(C);
case 'string':
return C;
}
}
/**
* Returns an object containing hue ('h'), saturation ('s') and lightness ('l')
* from any type of valid color.
*
* @function colorToHSL
*
* @param {number|array|object|string} 1st_value - red (0..255) or hue (0..1) or object with members `r, g, b` or `h, s, l` or array of RGB or HSL or color name or hex string.
* @param {number} [2nd_value] - green (0..255) or saturation (0..1)
* @param {number} [3rd_value] - blue (0..255) or lightnes (0..1)
*
* @returns {object} Object with members h, s and l as numbers (0..1)
*/
export function colorToHSL() {
const C = decodeColor(arguments);
switch (C.type) {
case 'rgb':
return RGBToHSL(C);
case 'hex':
return RGBToHSL(C);
case 'hsl':
return C;
case 'string':
return RGBToHSL(C);
}
}
/**
* Returns a hex color string
* from any type of valid color.
*
* @function colorToHex
*
* @param {number|array|object|string} 1st_value - red (0..255) or hue (0..1) or object with members `r, g, b` or `h, s, l` or array of RGB or HSL or color name or hex string.
* @param {number} [2nd_value] - green (0..255) or saturation (0..1)
* @param {number} [3rd_value] - blue (0..255) or lightnes (0..1)
*
* @returns {string} Hex color string.
*/
export function colorToHex() {
const C = decodeColor(arguments);
switch (C.type) {
case 'rgb':
return RGBToHex(C);
case 'hex':
return C.hex;
case 'hsl':
return HSLToHex(C);
case 'string':
return RGBToHex(C);
}
}
/**
* Returns a hex color string either black or white at highest contrast compared to the argument
* from any type of valid color.
*
* @function colorToBW
*
* @param {number|array|object|string} 1st_value - red (0..255) or hue (0..1) or object with members `r, g, b` or `h, s, l` or array of RGB or HSL or color name or hex string.
* @param {number} [2nd_value] - green (0..255) or saturation (0..1)
* @param {number} [3rd_value] - blue (0..255) or lightnes (0..1)
*
* @returns {string} Hex color string.
*/
export function colorToBW() {
const C = decodeColor(arguments);
switch (C.type) {
case 'rgb':
return RGBToBW(C);
case 'hex':
return RGBToBW(C);
case 'hsl':
return HSLToBW(C);
case 'string':
return RGBToBW(C);
}
}
/**
* Returns a hex color string either black or white at lowest contrast compared to the argument
* from any type of valid color.
*
* @function colorToWB
*
* @param {number|array|object|string} 1st_value - red (0..255) or hue (0..1) or object with members `r, g, b` or `h, s, l` or array of RGB or HSL or color name or hex string.
* @param {number} [2nd_value] - green (0..255) or saturation (0..1)
* @param {number} [3rd_value] - blue (0..255) or lightnes (0..1)
*
* @returns {string} Hex color string.
*/
export function colorToWB() {
const C = decodeColor(arguments);
switch (C.type) {
case 'rgb':
return RGBToWB(C);
case 'hex':
return RGBToWB(C);
case 'hsl':
return HSLToWB(C);
case 'string':
return RGBToWB(C);
}
}
/**
* Returns a hex color string of the grayscaled argument
* from any type of valid color.
*
* @function colorToGray
*
* @param {number|array|object|string} 1st_value - red (0..255) or hue (0..1) or object with members `r, g, b` or `h, s, l` or array of RGB or HSL or color name or hex string.
* @param {number} [2nd_value] - green (0..255) or saturation (0..1)
* @param {number} [3rd_value] - blue (0..255) or lightnes (0..1)
*
* @returns {string} Hex color string.
*/
export function colorToGray() {
const C = decodeColor(arguments);
switch (C.type) {
case 'rgb':
return RGBToBW(C);
case 'hex':
return RGBToBW(C);
case 'hsl':
return HSLToBW(C);
case 'string':
return RGBToBW(C);
}
}