UNPKG

chromaticity-color-utilities

Version:
556 lines (555 loc) 25.4 kB
"use strict"; // chromaticity-color-utilities // Copyright (C) 2022 Emma Litwa-Vulcu // // This program 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. // // This program 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, see <https://www.gnu.org/licenses/>. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); var Modify_1 = __importDefault(require("./Modify")); var Blend_1 = __importDefault(require("./Blend")); var Colors = __importStar(require("./Colors")); var Harmony = /** @class */ (function () { function Harmony() { } /** * Return 180deg complement of color * * @param {Colors.hsv} hsv * @return {Colors.hsv[]} */ Harmony.complement = function (hsv) { var hueComplement = Modify_1.default.hueShift(hsv.getH(), 180); return [hsv, new Colors.hsv(hueComplement, hsv.getS(), hsv.getV())]; }; /** * Return an analogous color scheme based on input color and angle * * @param {Colors.hsv} hsv * @param {number} [angle=30] degrees * @return {Array<Colors.hsv>} */ Harmony.analogous = function (hsv, angle) { if (angle === void 0) { angle = 30; } var aHue1 = Modify_1.default.hueShift(hsv.getH(), angle); var aHue2 = Modify_1.default.hueShift(hsv.getH(), angle * -1); return [ hsv, new Colors.hsv(aHue1, hsv.getS(), hsv.getV()), new Colors.hsv(aHue2, hsv.getS(), hsv.getV()), ]; }; /** * Return a triadic color scheme based on input color * Alias of analogous() with 120deg angle * * @param {Colors.hsv} hsv * @param {number} [angle=120] * @return {Array<Colors.hsv>} */ Harmony.triadic = function (hsv, angle) { if (angle === void 0) { angle = 120; } return this.analogous(hsv, angle); }; /** * Return a split complement color scheme based on input color and angle * Alias of analogous() but with different default angle * * @param {Colors.hsv} hsv * @param {number} [angle=150] degrees * @return {Array<Colors.hsv>} */ Harmony.splitComplement = function (hsv, angle) { if (angle === void 0) { angle = 150; } return this.analogous(hsv, angle); }; /** * Return a tetradic color scheme based on input color and angle * * @param {Colors.hsv} hsv * @param {number} [angle=45] degrees * @return {Array<Colors.hsv>} */ Harmony.tetradic = function (hsv, angle) { if (angle === void 0) { angle = 45; } var hue2 = Modify_1.default.hueShift(hsv.getH(), angle); var hue3 = Modify_1.default.hueShift(hsv.getH(), angle + 180); var hue4 = Modify_1.default.hueShift(hsv.getH(), 180); return [ hsv, new Colors.hsv(hue2, hsv.getS(), hsv.getV()), new Colors.hsv(hue3, hsv.getS(), hsv.getV()), new Colors.hsv(hue4, hsv.getS(), hsv.getV()), ]; }; /** * Return a square color scheme based on input color * Alias of tetradic() with 90deg angle * * @param {Colors.hsv} hsv * @return {Array<Colors.hsv>} */ Harmony.square = function (hsv) { return this.tetradic(hsv, 90); }; /** * Returns an array of colors of a darker shade * * @param {T extends colorType} color * @param {string} [method='hsl'] * @param {number} colors * @param {number} [distanceToBlack=1] 0-1, where 1 is all the way to black * @param {boolesn} [round=true] * @returns {T[]} */ Harmony.shade = function (color, method, colors, distanceToBlack) { if (method === void 0) { method = 'hsl'; } if (distanceToBlack === void 0) { distanceToBlack = 1; } var scheme = []; if ([ 'hsv', 'hsva', 'hsi', 'hsia', 'hsp', 'hspa', 'hsl', 'hsla', 'rgb', 'rgb2', 'cmyk', 'lab', 'luv', ].includes(method)) { var type = method.replace(/[0-9]/, '').replace(/a$/, ''); var start = color.to(type, { round: false }); for (var n = 0; n < colors; n++) { scheme.push(start.modify('darken', { method: method, amount: (n / (colors - 1)) * distanceToBlack, round: false, })); } } else { throw new Error('Invalid shade scheme method: ' + method); } return scheme; }; /** * Returns an array of colors of a lighter tint * * @param {T extends colorType} color * @param {string} [method='hsl'] * @param {number} colors * @param {number} [distanceToWhite=1] 0-1, where 1 is all the way to white * @param {boolesn} [round=true] * @returns {T[]} */ Harmony.tint = function (color, method, colors, distanceToWhite) { if (method === void 0) { method = 'hsl'; } if (distanceToWhite === void 0) { distanceToWhite = 1; } var scheme = []; if ([ 'hsv', 'hsva', 'hsi', 'hsia', 'hsp', 'hspa', 'hsl', 'hsla', 'rgb', 'rgb2', 'cmyk', 'lab', 'luv', ].includes(method)) { var type = method.replace(/[0-9]/, '').replace(/a$/, ''); var start = color.to(type, { round: false }); for (var n = 0; n < colors; n++) { scheme.push(start.modify('lighten', { method: method, amount: (n / (colors - 1)) * distanceToWhite, round: false, })); } } else { throw new Error('Invalid tint scheme method: ' + method); } return scheme; }; /** * Returns an array of colors of darker shades and lighter tints * * @param {T extends colorType} color * @param {string} method * @param {number} colors * @param {boolean} [round=true] * @param {number} [distance=1] 0-1, where 1 is all the way to closest bound OR white, if distanceShade given * @param {number} [distanceShade=1] 0-1, where 1 is all the way to black * @returns */ Harmony.shadetint = function (color, method, colors, round, distance, distanceShade) { if (round === void 0) { round = true; } if (distance === void 0) { distance = 1; } var scheme = []; switch (method) { case 'hsl': case 'hsla': var hsl = color.to('hsl', { round: false }); if (typeof distanceShade === 'undefined') { if (100 - hsl.getL() < hsl.getL()) { distanceShade = ((100 - hsl.getL()) / 50) * distance; } else { distanceShade = distance; distance = (hsl.getL() / 50) * distanceShade; } } for (var i = 0; i < colors; i++) { scheme.push(Modify_1.default.hslDarken(hsl, ((colors - i) / colors) * distanceShade).to(color.getType(), { round: round })); } scheme.push(hsl.to(color.getType(), { round: round })); for (var i = 1; i <= colors; i++) { scheme.push(Modify_1.default.hslLighten(hsl, (i / colors) * distance).to(color.getType(), { round: round })); } break; case 'hsv': case 'hsva': var hsv = color.to('hsv', { round: false }); if (typeof distanceShade === 'undefined') { if (100 - hsv.getV() < hsv.getV()) { distanceShade = ((100 - hsv.getV()) / 50) * distance; } else { distanceShade = distance; distance = (hsv.getV() / 50) * distanceShade; } } for (var i = 0; i < colors; i++) { scheme.push(Modify_1.default.hsvDarken(hsv, ((colors - i) / colors) * distanceShade).to(color.getType(), { round: round })); } scheme.push(hsv.to(color.getType(), { round: round })); for (var i = 1; i <= colors; i++) { scheme.push(Modify_1.default.hsvLighten(hsv, (i / colors) * distance).to(color.getType(), { round: round })); } break; case 'hsi': case 'hsia': var hsi = color.to('hsi', { round: false }); if (typeof distanceShade === 'undefined') { if (100 - hsi.getI() < hsi.getI()) { distanceShade = ((100 - hsi.getI()) / 50) * distance; } else { distanceShade = distance; distance = (hsi.getI() / 50) * distanceShade; } } for (var i = 0; i < colors; i++) { scheme.push(Modify_1.default.hsiDarken(hsi, ((colors - i) / colors) * distanceShade).to(color.getType(), { round: round })); } scheme.push(hsi.to(color.getType(), { round: round })); for (var i = 1; i <= colors; i++) { scheme.push(Modify_1.default.hsiLighten(hsi, (i / colors) * distance).to(color.getType(), { round: round })); } break; case 'hsp': case 'hspa': var hsp = color.to('hsp', { round: false }); if (typeof distanceShade === 'undefined') { if (100 - hsp.getP() < hsp.getP()) { distanceShade = ((100 - hsp.getP()) / 50) * distance; } else { distanceShade = distance; distance = (hsp.getP() / 50) * distanceShade; } } for (var i = 0; i < colors; i++) { scheme.push(Modify_1.default.hspDarken(hsp, ((colors - i) / colors) * distanceShade).to(color.getType(), { round: round })); } scheme.push(hsp.to(color.getType(), { round: round })); for (var i = 1; i <= colors; i++) { scheme.push(Modify_1.default.hspLighten(hsp, (i / colors) * distance).to(color.getType(), { round: round })); } break; case 'rgb2': case 'rgba2': var rgb2 = color.to('rgb', { round: false }); if (typeof distanceShade === 'undefined') { var avg = (rgb2.getR() + rgb2.getG() + rgb2.getB()) / 3; if (rgb2.getMax() - avg < avg) { distanceShade = ((rgb2.getMax() - Math.min(rgb2.getR(), rgb2.getG(), rgb2.getB())) / rgb2.getMax() / 2) * distance; } else { distanceShade = distance; distance = (Math.max(rgb2.getR(), rgb2.getG(), rgb2.getB()) / rgb2.getMax() / 2) * distanceShade; } } for (var i = 0; i < colors; i++) { scheme.push(Modify_1.default.rgb2Darken(rgb2, ((colors - i) / colors) * distanceShade).to(color.getType(), { round: round })); } scheme.push(rgb2.to(color.getType(), { round: round })); for (var i = 1; i <= colors; i++) { scheme.push(Modify_1.default.rgb2Lighten(rgb2, (i / colors) * distance).to(color.getType(), { round: round })); } break; case 'rgb': case 'rgba': var rgb = color.to('rgb', { round: false }); if (typeof distanceShade === 'undefined') { var avg = (rgb.getR() + rgb.getG() + rgb.getB()) / 3; if (rgb.getMax() - avg < avg) { distanceShade = ((rgb.getMax() - Math.min(rgb.getR(), rgb.getG(), rgb.getB())) / rgb.getMax() / 2) * distance; } else { distanceShade = distance; distance = (Math.max(rgb.getR(), rgb.getG(), rgb.getB()) / rgb.getMax() / 2) * distanceShade; } } for (var i = 0; i < colors; i++) { scheme.push(Modify_1.default.rgbDarken(rgb, ((colors - i) / colors) * distanceShade).to(color.getType(), { round: round })); } scheme.push(rgb.to(color.getType(), { round: round })); for (var i = 1; i <= colors; i++) { scheme.push(Modify_1.default.rgbLighten(rgb, (i / colors) * distance).to(color.getType(), { round: round })); } break; case 'cmyk': var cmyk = color.to('cmyk', { round: false }); if (typeof distanceShade === 'undefined') { if (cmyk.getK() < 50) { distanceShade = (cmyk.getK() / 50) * distance; } else { distanceShade = distance; distance = ((100 - cmyk.getK()) / 50) * distanceShade; } } for (var i = 0; i < colors; i++) { scheme.push(Modify_1.default.cmykDarken(cmyk, ((colors - i) / colors) * distanceShade).to(color.getType(), { round: round })); } scheme.push(cmyk.to(color.getType(), { round: round })); for (var i = 1; i <= colors; i++) { scheme.push(Modify_1.default.cmykLighten(cmyk, (i / colors) * distance).to(color.getType(), { round: round })); } break; case 'lab': var lab = color.to('lab', { round: false }); if (typeof distanceShade === 'undefined') { if (lab.getL() < 50) { distanceShade = (lab.getL() / 50) * distance; } else { distanceShade = distance; distance = ((100 - lab.getL()) / 50) * distanceShade; } } for (var i = 0; i < colors; i++) { scheme.push(Modify_1.default.labDarken(lab, ((colors - i) / colors) * distanceShade).to(color.getType(), { round: round })); } scheme.push(lab.to(color.getType(), { round: round })); for (var i = 1; i <= colors; i++) { scheme.push(Modify_1.default.labLighten(lab, (i / colors) * distance).to(color.getType(), { round: round })); } break; case 'luv': var luv = color.to('luv', { round: false }); if (typeof distanceShade === 'undefined') { if (luv.getL() < 50) { distanceShade = (luv.getL() / 50) * distance; } else { distanceShade = distance; distance = ((100 - luv.getL()) / 50) * distanceShade; } } for (var i = 0; i < colors; i++) { scheme.push(Modify_1.default.luvDarken(luv, ((colors - i) / colors) * distanceShade).to(color.getType(), { round: round })); } scheme.push(luv.to(color.getType(), { round: round })); for (var i = 1; i <= colors; i++) { scheme.push(Modify_1.default.luvLighten(luv, (i / colors) * distance).to(color.getType(), { round: round })); } break; default: throw new Error('Invalid method for generating color scheme'); } return scheme; }; /** * Return an array of colors blended from color1 to color2 * * @param {T extends colorType} color1 * @param {T extends colorType} color2 * @param {number} colors number of colors in scheme (including color1 and color2) * @returns {T[]} */ Harmony.gradient = function (type, color1, color2, colors, round) { if (round === void 0) { round = true; } if (colors < 2) { throw new Error('Unable to generate gradient with less than two colors'); } type = type.toLowerCase(); var reachesColor2 = ![ 'multiply', 'screen', 'overlay', 'softlight', 'colorburn', 'colordodge', 'vividlight', 'linearburn', 'lineardodge', 'linearlight', 'divide', 'addition', 'subtraction', 'difference', 'hue', 'value', 'lightness', 'intensity', 'perceivedbrightness', 'perceived', ].includes(type); var inBetweenColors = reachesColor2 ? colors - 2 : colors - 1; var gradient = []; gradient.push(color1); for (var i = 0; i < inBetweenColors; i++) { var amount = (i + 1) / (inBetweenColors + 1); if ([ 'multiply', 'screen', 'overlay', 'softlight', 'colordodge', 'colorburn', 'vividlight', 'lineardodge', 'linearburn', 'linearlight', 'divide', 'addition', 'subtraction', 'difference', ].includes(type)) { gradient.push(Blend_1.default.rgbBlendMode(color1.to('rgb', { round: false }), color2.to('rgb', { round: false }), amount, type).to(color1.getType(), { round: round })); } else { switch (type) { case 'rgb': case 'rgba': gradient.push(Blend_1.default.rgbBlend(color1.to('rgb', { round: false }), color2.to('rgb', { round: false }), amount).to(color1.getType(), { round: round })); break; case 'hue': gradient.push(Blend_1.default.hueBlend(color1.to('hsv', { round: false }), color2.to('hsv', { round: false }), amount).to(color1.getType(), { round: round })); break; case 'hsv': case 'hsva': gradient.push(Blend_1.default.hsvBlend(color1.to('hsv', { round: false }), color2.to('hsv', { round: false }), amount).to(color1.getType(), { round: round })); break; case 'value': gradient.push(Blend_1.default.valueBlend(color1.to('hsv', { round: false }), color2.to('hsv', { round: false }), amount).to(color1.getType(), { round: round })); break; case 'hsl': case 'hsla': gradient.push(Blend_1.default.hslBlend(color1.to('hsl', { round: false }), color2.to('hsl', { round: false }), amount).to(color1.getType(), { round: round })); break; case 'lightness': gradient.push(Blend_1.default.lightnessBlend(color1.to('hsl', { round: false }), color2.to('hsl', { round: false }), amount).to(color1.getType(), { round: round })); break; case 'hsi': case 'hsia': gradient.push(Blend_1.default.hsiBlend(color1.to('hsi', { round: false }), color2.to('hsi', { round: false }), amount).to(color1.getType(), { round: round })); break; case 'intensity': gradient.push(Blend_1.default.intensityBlend(color1.to('hsi', { round: false }), color2.to('hsi', { round: false }), amount).to(color1.getType(), { round: round })); break; case 'hsp': case 'hspa': gradient.push(Blend_1.default.hspBlend(color1.to('hsp', { round: false }), color2.to('hsp', { round: false }), amount).to(color1.getType(), { round: round })); break; case 'perceivedbrightness': case 'perceived': gradient.push(Blend_1.default.perceivedBrightnessBlend(color1.to('hsp', { round: false }), color2.to('hsp', { round: false }), amount).to(color1.getType(), { round: round })); break; case 'cmyk': gradient.push(Blend_1.default.cmykBlend(color1.to('cmyk', { round: false }), color2.to('cmyk', { round: false }), amount).to(color1.getType(), { round: round })); break; case 'yiq': gradient.push(Blend_1.default.yiqBlend(color1.to('yiq', { round: false }), color2.to('yiq', { round: false }), amount).to(color1.getType(), { round: round })); break; case 'lab': gradient.push(Blend_1.default.labBlend(color1.to('lab', { round: false }), color2.to('lab', { round: false }), amount).to(color1.getType(), { round: round })); break; case 'luv': gradient.push(Blend_1.default.luvBlend(color1.to('luv', { round: false }), color2.to('luv', { round: false }), amount).to(color1.getType(), { round: round })); break; default: throw new Error('Unrecognized gradient method'); } } } if (reachesColor2) gradient.push(color2); return gradient; }; return Harmony; }()); exports.default = Harmony;