UNPKG

color-math

Version:
412 lines (338 loc) 10.2 kB
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } function _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } var Chroma = require('chroma-js'); import ColorScale from './ColorScale'; import BlendMode from './BlendMode'; import ValueType from './ValueType'; var getColorBlender = function () { var bs = {}; bs[BlendMode.None] = function (a) { return a; }; bs[BlendMode.Replace] = function (a, b) { return b; }; bs[BlendMode.Add] = function (a, b) { return Math.min(a + b, 255); }; bs[BlendMode.ColorBurn] = function (a, b) { return b <= 0 ? 0 : Math.max(255 - (255 - a) * 255 / b, 0); }; bs[BlendMode.ColorDodge] = function (a, b) { return b >= 255 ? 255 : Math.min(a * 255 / (255 - b), 255); }; bs[BlendMode.Darken] = function (a, b) { return Math.min(a, b); }; bs[BlendMode.Difference] = function (a, b) { return Math.abs(a - b); }; bs[BlendMode.Divide] = function (a, b) { return Math.min(a / 255 / (b / 255) * 255, 255); }; bs[BlendMode.Exclusion] = function (a, b) { return 255 - ((255 - a) * (255 - b) / 255 + a * b / 255); }; bs[BlendMode.HardLight] = function (a, b) { return b < 128 ? 2 * a * b / 255 : 255 - 2 * (255 - a) * (255 - b) / 255; }; bs[BlendMode.Lighten] = function (a, b) { return Math.max(a, b); }; bs[BlendMode.LinearBurn] = function (a, b) { return Math.max(0, a + b - 255); }; bs[BlendMode.LinearDodge] = function (a, b) { return Math.min(a + b, 255); }; bs[BlendMode.Multiply] = function (a, b) { return a * b / 255; }; bs[BlendMode.Negate] = function (a, b) { return 255 - Math.abs(255 - a - b); }; bs[BlendMode.Overlay] = function (a, b) { return a < 128 ? 2 * a * b / 255 : 255 - 2 * (255 - a) * (255 - b) / 255; }; bs[BlendMode.Screen] = function (a, b) { return 255 - (255 - a) * (255 - b) / 255; }; bs[BlendMode.SoftLight] = function (a, b) { return a < 128 ? ((b >> 1) + 64) * a * (2 / 255) : 255 - (191 - (b >> 1)) * (255 - a) * (2 / 255); }; bs[BlendMode.Subtract] = function (a, b) { return Math.max(a - b, 0); }; return function (mode) { return bs[mode]; }; }(); export function blendColors(bg, fg, mode) { var blender = getColorBlender(mode); var bgComps = bg.rgba(); var fgComps = fg.rgba(); var resComps = [void 0, void 0, void 0, fgComps[3] + bgComps[3] * (1 - fgComps[3])]; for (var i = 0; i < 3; i++) { var c1 = bgComps[i] / 255; var c2 = fgComps[i] / 255; var c = blender(bgComps[i], fgComps[i]) / 255; if (resComps[3]) { c = (fgComps[3] * c2 + bgComps[3] * (c1 - fgComps[3] * (c1 + c2 - c))) / resComps[3]; } resComps[i] = c * 255; } var color = Chroma(resComps); return color; } export function cmyToCmykArray(valuesOrC, m, y) { var k = 1; var c; if (!Array.isArray(valuesOrC)) { c = valuesOrC; } else { var _valuesOrC = _slicedToArray(valuesOrC, 3); c = _valuesOrC[0]; m = _valuesOrC[1]; y = _valuesOrC[2]; } if (c < k) { k = c; } if (m < k) { k = m; } if (y < k) { k = y; } if (k === 1) { c = 0; m = 0; y = 0; } else { c = (c - k) / (1 - k); m = (m - k) / (1 - k); y = (y - k) / (1 - k); } return [c, m, y, k]; } export function cmyToCmyk(c, m, y) { var values = cmyToCmykArray(c, m, y); var color = Chroma(values, 'cmyk'); return color; } export function inverseColor(color) { var color2 = cloneValue(color); color2 = color2.set('rgb.r', 255 - color.get('rgb.r')); color2 = color2.set('rgb.g', 255 - color.get('rgb.g')); color2 = color2.set('rgb.b', 255 - color.get('rgb.b')); return color2; } export function colorArithmeticOp(value1, value2, op) { var color; var n; if (isColor(value1)) { color = value1; n = value2; } else { n = value1; color = value2; } color = cloneValue(color); color = color.set('rgb.r', op + n); color = color.set('rgb.g', op + n); color = color.set('rgb.b', op + n); return color; } export function roundColorComps(color) { var space = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'rgb'; var ranges = getColorSpaceParamsValidRanges(space); var comps = color.get(space); for (var i = 0; i < ranges.length; i++) { if (ranges[i][1] - ranges[i][0] > 2) { comps[i] = Math.round(comps[i]); } } var res = Chroma(comps, space); res = res.alpha(color.alpha()); return res; } export function getColorSpaceParamsValidRanges(space) { switch (space) { case 'rgb': return [[0, 255], [0, 255], [0, 255]]; case 'cmy': return [[0, 1], [0, 1], [0, 1]]; case 'cmyk': return [[0, 1], [0, 1], [0, 1], [0, 1]]; case 'hsl': return [[0, 360], [0, 1], [0, 1]]; case 'hsv': return [[0, 360], [0, 1], [0, 1]]; case 'hsi': return [[0, 360], [0, 1], [0, 1]]; case 'lab': return [[0, 100], [-128, 127], [-128, 127]]; case 'lch': return [[0, 100], [0, 140], [0, 360]]; case 'hcl': return [[0, 360], [0, 140], [0, 100]]; default: throw new SyntaxError("unknown namespace: ".concat(space.toUpperCase(), ".")); } } export function isColor(value) { return value && Array.isArray(value._rgb) && value._rgb.length === 4; } export function formatColor(color) { var appendName = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; color = roundColorComps(color); var hex = color.hex(); var hex8 = color.hex('rgba'); var alpha = color.alpha(); var name = color.name(); var s = alpha === 1 || isNaN(alpha) ? hex : hex8; if (appendName && name !== hex) { s += " (".concat(name, ")"); } return s; } export function colorFromWavelength(wl) { var r = 0; var g = 0; var b = 0; var a = 1; if (wl >= 380 && wl < 440) { r = -1 * (wl - 440) / (440 - 380); g = 0; b = 1; } else if (wl >= 440 && wl < 490) { r = 0; g = (wl - 440) / (490 - 440); b = 1; } else if (wl >= 490 && wl < 510) { r = 0; g = 1; b = -1 * (wl - 510) / (510 - 490); } else if (wl >= 510 && wl < 580) { r = (wl - 510) / (580 - 510); g = 1; b = 0; } else if (wl >= 580 && wl < 645) { r = 1; g = -1 * (wl - 645) / (645 - 580); b = 0.0; } else if (wl >= 645 && wl <= 780) { r = 1; g = 0; b = 0; } if (wl > 780 || wl < 380) { a = 0; } else if (wl > 700) { a = (780 - wl) / (780 - 700); } else if (wl < 420) { a = (wl - 380) / (420 - 380); } var color = Chroma([r, g, b, a], 'gl'); return color; } export function throwError(error, loc) { var locStr = loc ? " (".concat(loc, ")") : ''; throw "Error".concat(locStr, ": ").concat(error, "."); } export function getType(value) { if (typeof value === 'number') { return ValueType.Number; } else if (isColor(value)) { return ValueType.Color; } else if (Array.isArray(value)) { if (value.length) { if (value.every(function (v) { return getType(v) === ValueType.Number; })) { return ValueType.NumberArray; } else if (value.every(function (v) { return getType(v) === ValueType.Color; })) { return ValueType.ColorArray; } } return ValueType.Array; } else if (value instanceof ColorScale) { return ValueType.ColorScale; } } export function forceType(value, type, loc) { var valueType = getType(value); var types = Array.isArray(type) ? type : [type]; if (types.every(function (t) { return (t & valueType) !== t; })) { var strs = types.map(function (t) { switch (t) { case ValueType.Number: return 'a number'; case ValueType.Color: return 'a color'; case ValueType.ColorScale: return 'a color scale'; case ValueType.Array: return 'an array'; case ValueType.NumberArray: return 'a number array'; case ValueType.ColorArray: return 'a color array'; default: throw new Error('invalid value type'); } }); var msg = strs[0]; if (strs.length > 1) { msg = strs.slice(0, -1).join(', ') + ' or ' + strs.slice(-1); } throwError("value is not ".concat(msg), loc); } return value; } export function forceRange(value, loc) { if (getType(value) !== ValueType.NumberArray || value.length !== 2) { throwError('operand is not valid numeric range', loc); } return value; } export function cloneValue(value) { var type = getType(value); switch (type) { case ValueType.Color: return Chroma(value.rgba()); case ValueType.ColorScale: return value.clone(); case ValueType.Array: case ValueType.NumberArray: case ValueType.ColorArray: return value.map(function (v) { return cloneValue(v); }); default: return value; } } export function forceNumInRange(value, minOrRange, maxOrLoc, loc) { var min = Array.isArray(minOrRange) ? minOrRange[0] : minOrRange; var max = Array.isArray(minOrRange) ? minOrRange[1] : maxOrLoc; loc = Array.isArray(minOrRange) ? maxOrLoc : loc; var n = forceType(value, ValueType.Number, loc); if (n < min || n > max) { throwError("number in a range [".concat(min, "..").concat(max, "] is expected, you provided: ").concat(n), loc); } return n; } export function getObjKey(obj, value) { for (var key in obj) { if ((!obj.hasOwnProperty || key in obj) && obj[key] === value) { return key; } } }