UNPKG

iwanthue

Version:

Colors for data scientists. Generate and refine palettes of optimally distinct colors.

200 lines (150 loc) 4.05 kB
/** * Iwanthue Library Helpers * ========================= * * Collection of color-related helpers used throughout the library. */ var LAB_CONSTANTS = { // Corresponds roughly to RGB brighter/darker Kn: 18, // D65 standard referent Xn: 0.95047, Yn: 1, Zn: 1.08883, t0: 0.137931034, // 4 / 29 t1: 0.206896552, // 6 / 29 t2: 0.12841855, // 3 * t1 * t1 t3: 0.008856452 // t1 * t1 * t1 }; function xyzToRgb(r) { return Math.round( 255 * (r <= 0.00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - 0.055) ); } function rgbToXyzHelper(r) { if ((r /= 255) <= 0.04045) return r / 12.92; return Math.pow((r + 0.055) / 1.055, 2.4); } function xyzToLab(t) { if (t > LAB_CONSTANTS.t3) return Math.pow(t, 1 / 3); return t / LAB_CONSTANTS.t2 + LAB_CONSTANTS.t0; } function rgbToXyz(rgb) { var r = rgb[0], g = rgb[1], b = rgb[2]; r = rgbToXyzHelper(r); g = rgbToXyzHelper(g); b = rgbToXyzHelper(b); var x = xyzToLab((0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / LAB_CONSTANTS.Xn), y = xyzToLab((0.2126729 * r + 0.7151522 * g + 0.0721750 * b) / LAB_CONSTANTS.Yn), z = xyzToLab((0.0193339 * r + 0.1191920 * g + 0.9503041 * b) / LAB_CONSTANTS.Zn); return [x, y, z]; } function labToXyz(t) { return t > LAB_CONSTANTS.t1 ? t * t * t : LAB_CONSTANTS.t2 * (t - LAB_CONSTANTS.t0); } function labToRgb(lab) { var l = lab[0]; var a = lab[1]; var b = lab[2]; var y = (l + 16) / 116; var x = isNaN(a) ? y : y + a / 500; var z = isNaN(b) ? y : y - b / 200; y = LAB_CONSTANTS.Yn * labToXyz(y); x = LAB_CONSTANTS.Xn * labToXyz(x); z = LAB_CONSTANTS.Zn * labToXyz(z); var r = xyzToRgb(3.2404542 * x - 1.5371385 * y - 0.4985314 * z); // D65 -> sRGB var g = xyzToRgb(-0.969266 * x + 1.8760108 * y + 0.041556 * z); b = xyzToRgb(0.0556434 * x - 0.2040259 * y + 1.0572252 * z); // r, g or b can be -0, beware... return [r, g, b]; } function rgbToLab(rgb) { var xyz = rgbToXyz(rgb); var x = xyz[0], y = xyz[1], z = xyz[2]; var l = 116 * y - 16; return [l < 0 ? 0 : l, 500 * (x - y), 200 * (y - z)]; } function validateRgb(rgb) { var r = rgb[0]; var g = rgb[1]; var b = rgb[2]; return r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255; } function hexPad(x) { return ('0' + x.toString(16)).slice(-2); } function labToRgbHex(lab) { var rgb = labToRgb(lab); return ( '#' + hexPad(rgb[0]) + hexPad(rgb[1]) + hexPad(rgb[2]) ); } var RAD_TO_DEG = 180 / Math.PI; function labToHcl(lab) { var l = lab[0]; var a = lab[1]; var b = lab[2]; var c = Math.sqrt(a * a + b * b); var h = (Math.atan2(b, a) * RAD_TO_DEG + 360) % 360; if (Math.round(c * 10000) === 0) h = NaN; return [h, c, l]; } function diffSort(distance, colors) { colors = colors.slice(); var diffColors = [colors.shift()]; var index, maxDistance, candidateIndex; var A, B, d, i; while (colors.length > 0) { index = -1; maxDistance = -Infinity; for (candidateIndex = 0; candidateIndex < colors.length; candidateIndex++) { A = colors[candidateIndex]; for (i = 0; i < diffColors.length; i++) { B = diffColors[i]; d = distance(A, B); if (d > maxDistance) { maxDistance = d; index = candidateIndex; } } } diffColors.push(colors[index]); colors.splice(index, 1); } return diffColors; } function computeQualityMetrics(distance, colors) { var i, j, l; var min = Infinity, d; var S = 0, t = 0; for (i = 0, l = colors.length; i < l; i++) { for (j = i + 1; j < l; j++) { d = distance(colors[i], colors[j]); if (d < min) min = d; S += d; t++; } } return {min: min, mean: S / t}; } exports.validateRgb = validateRgb; exports.labToRgb = labToRgb; exports.labToRgbHex = labToRgbHex; exports.rgbToLab = rgbToLab; exports.labToHcl = labToHcl; exports.diffSort = diffSort; exports.computeQualityMetrics = computeQualityMetrics;