UNPKG

ixfx

Version:

Bundle of ixfx libraries

1,637 lines (1,633 loc) 202 kB
import { n as __exportAll } from "./chunk-CaR5F9JI.js"; import { H as continuously, Wt as cloneFromFields } from "./src-BUqDa_u7.js"; import { C as numberTest, M as resultThrow, O as resultErrorToString, S as numberInclusiveRangeTest, p as arrayTest, w as percentTest } from "./src-C_hvyftg.js"; import { v as pairwise } from "./src-CxEyGbiK.js"; import { f as MapOfSimpleMutable, i as StackImmutable, u as QueueImmutable } from "./src-DOorb7Rs.js"; import { n as SimpleEventEmitter } from "./src-CRR1VQls.js"; import { A as round$1, P as interpolate, V as clamp$2, b as quantiseEvery, g as scaler, p as scaleClamped, rt as clamp$1, y as scalerTwoWay } from "./src-Cebc3sfq.js"; import { C as ElementSizer, G as resolveElementTry, W as resolveEl } from "./src-BoQVyMUt.js"; import { At as subtractSize, B as toCartesian, D as isQuadraticBezier, E as isCubicBezier, Ft as EmptyPositioned, H as angleConvert, I as fromNumbers, It as PlaceholderPositioned, Kt as applyFields, Mt as isLine, Nt as isEqual, Pt as Empty, U as angleParse, Ut as corners$1, Wt as center, Xt as guard$5, Yt as isRectPositioned, _ as cells, g as rows, h as offset, i as corners, j as PointsTracker, jt as multiplyScalar, m as indexFromCell, o as scaler$1, qt as guard$6, z as fromLine, zt as subtract } from "./src-DyTd46TV.js"; import { G as delayLoop } from "./src-C3g81yvt.js"; //#region ../node_modules/.pnpm/colorizr@4.0.1/node_modules/colorizr/dist/index.mjs var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __export = (target, all) => { for (var name2 in all) __defProp(target, name2, { get: all[name2], enumerable: true }); }; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); var COLOR_KEYS = { hsl: [ "h", "s", "l" ], oklab: [ "l", "a", "b" ], oklch: [ "l", "c", "h" ], rgb: [ "r", "g", "b" ] }; var COLOR_MODELS = [ "hsl", "oklab", "oklch", "rgb" ]; var DEG2RAD = Math.PI / 180; var LAB_TO_LMS = { l: [.3963377773761749, .2158037573099136], m: [-.1055613458156586, -.0638541728258133], s: [-.0894841775298119, -1.2914855480194092] }; var LRGB_TO_LMS = { l: [ .4122214708, .5363325363, .0514459929 ], m: [ .2119034982, .6806995451, .1073969566 ], s: [ .0883024619, .2817188376, .6299787005 ] }; var LSM_TO_LAB = { l: [ .2104542553, .793617785, .0040720468 ], a: [ 1.9779984951, 2.428592205, .4505937099 ], b: [ .0259040371, .7827717662, .808675766 ] }; var LSM_TO_RGB = { r: [ 4.076741636075958, -3.307711539258063, .2309699031821043 ], g: [ -1.2684379732850315, 2.609757349287688, -.341319376002657 ], b: [ -.0041960761386756, -.7034186179359362, 1.7076146940746117 ] }; var PRECISION = 5; var RAD2DEG = 180 / Math.PI; var MESSAGES = { alpha: "alpha must be a number between 0 and 1", alphaAdjustment: "alpha must be a number between -1 and 1", amount: "amount must be a number between 0 and 100", colorRequired: "color is required", degreesRange: "degrees must be a number between -360 and 360", hueArgs: "point, chroma and h are required", hueRange: "hue must be a number between 0 and 360", input: "input is required", inputHex: "input is required and must be a hex", inputNumber: "input is required and must be a number", inputString: "input is required and must be a string", invalid: "invalid input", invalidColor: "invalid color", invalidCSS: "invalid CSS string", invalidHex: "invalid hex", invalidKey: "invalid key", invalidModel: "invalid model", invalidRange: "color value out of range", left: "left is required and must be a string", lightnessRange: "lightness must be a number between 0 and 1", options: "invalid options", paletteSize: "palette size must be at least 2", ratioRange: "ratio must be a number between 0 and 1", right: "right is required and must be a string", threshold: "threshold must be a number between 0 and 255", thresholdNormalized: "threshold must be a number between 0 and 1" }; function invariant(condition, message) { if (condition) return; if (message === void 0) throw new Error("invariant requires an error message argument"); const error = !message ? /* @__PURE__ */ new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.") : new Error(message); error.name = "colorizr"; throw error; } var cssColors = { aliceblue: "#f0f8ff", antiquewhite: "#faebd7", aqua: "#00ffff", aquamarine: "#7fffd4", azure: "#f0ffff", beige: "#f5f5dc", bisque: "#ffe4c4", black: "#000000", blanchedalmond: "#ffebcd", blue: "#0000ff", blueviolet: "#8a2be2", brown: "#a52a2a", burlywood: "#deb887", cadetblue: "#5f9ea0", chartreuse: "#7fff00", chocolate: "#d2691e", coral: "#ff7f50", cornflowerblue: "#6495ed", cornsilk: "#fff8dc", crimson: "#dc143c", cyan: "#00ffff", darkblue: "#00008b", darkcyan: "#008b8b", darkgoldenrod: "#b8860b", darkgray: "#a9a9a9", darkgreen: "#006400", darkkhaki: "#bdb76b", darkmagenta: "#8b008b", darkolivegreen: "#556b2f", darkorange: "#ff8c00", darkorchid: "#9932cc", darkred: "#8b0000", darksalmon: "#e9967a", darkseagreen: "#8fbc8f", darkslateblue: "#483d8b", darkslategray: "#2f4f4f", darkslategrey: "#2f4f4f", darkturquoise: "#00ced1", darkviolet: "#9400d3", deeppink: "#ff1493", deepskyblue: "#00bfff", dimgray: "#696969", dimgrey: "#696969", dodgerblue: "#1e90ff", firebrick: "#b22222", floralwhite: "#fffaf0", forestgreen: "#228b22", fuchsia: "#ff00ff", gainsboro: "#dcdcdc", ghostwhite: "#f8f8ff", gold: "#ffd700", goldenrod: "#daa520", gray: "#808080", grey: "#808080", green: "#008000", greenyellow: "#adff2f", honeydew: "#f0fff0", hotpink: "#ff69b4", indianred: "#cd5c5c", indigo: "#4b0082", ivory: "#fffff0", khaki: "#f0e68c", lavender: "#e6e6fa", lavenderblush: "#fff0f5", lawngreen: "#7cfc00", lemonchiffon: "#fffacd", lightblue: "#add8e6", lightcoral: "#f08080", lightcyan: "#e0ffff", lightgoldenrodyellow: "#fafad2", lightgray: "#d3d3d3", lightgreen: "#90ee90", lightgrey: "#d3d3d3", lightpink: "#ffb6c1", lightsalmon: "#ffa07a", lightseagreen: "#20b2aa", lightskyblue: "#87cefa", lightslategray: "#778899", lightslategrey: "#778899", lightsteelblue: "#b0c4de", lightyellow: "#ffffe0", lime: "#00ff00", limegreen: "#32cd32", linen: "#faf0e6", magenta: "#ff00ff", maroon: "#800000", mediumaquamarine: "#66cdaa", mediumblue: "#0000cd", mediumorchid: "#ba55d3", mediumpurple: "#9370db", mediumseagreen: "#3cb371", mediumslateblue: "#7b68ee", mediumspringgreen: "#00fa9a", mediumturquoise: "#48d1cc", mediumvioletred: "#c71585", midnightblue: "#191970", mintcream: "#f5fffa", mistyrose: "#ffe4e1", moccasin: "#ffe4b5", navajowhite: "#ffdead", navy: "#000080", oldlace: "#fdf5e6", olive: "#808000", olivedrab: "#6b8e23", orange: "#ffa500", orangered: "#ff4500", orchid: "#da70d6", palegoldenrod: "#eee8aa", palegreen: "#98fb98", paleturquoise: "#afeeee", palevioletred: "#db7093", papayawhip: "#ffefd5", peachpuff: "#ffdab9", peru: "#cd853f", pink: "#ffc0cb", plum: "#dda0dd", powderblue: "#b0e0e6", purple: "#800080", rebeccapurple: "#663399", red: "#ff0000", rosybrown: "#bc8f8f", royalblue: "#4169e1", saddlebrown: "#8b4513", salmon: "#fa8072", sandybrown: "#f4a460", seagreen: "#2e8b57", seashell: "#fff5ee", sienna: "#a0522d", silver: "#c0c0c0", skyblue: "#87ceeb", slateblue: "#6a5acd", slategray: "#708090", slategrey: "#708090", snow: "#fffafa", springgreen: "#00ff7f", steelblue: "#4682b4", tan: "#d2b48c", teal: "#008080", thistle: "#d8bfd8", tomato: "#ff6347", turquoise: "#40e0d0", violet: "#ee82ee", wheat: "#f5deb3", white: "#ffffff", whitesmoke: "#f5f5f5", yellow: "#ffff00", yellowgreen: "#9acd32" }; var hexRegex = /^#(?:[\da-f]{3,4}|[\da-f]{6,8})$/i; function hasValidMatches(input) { return Array.isArray(input) && input.length === 6; } function isHex(input) { if (!isString(input)) return false; return hexRegex.test(input); } function isHSL(input) { if (!isPlainObject(input)) return false; const entries = Object.entries(input); return !!entries.length && entries.every(([key, value]) => { if (key === "h") return value >= 0 && value <= 360; if (key === "alpha") return value >= 0 && value <= 1; return COLOR_KEYS.hsl.includes(key) && value >= 0 && value <= 100; }); } function isLAB(input) { if (!isPlainObject(input)) return false; const entries = Object.entries(input); return !!entries.length && entries.every(([key, value]) => { if (key === "l") return value >= 0 && value <= 100; if (key === "alpha") return value >= 0 && value <= 1; return COLOR_KEYS.oklab.includes(key) && value >= -1 && value <= 1; }); } function isLCH(input) { if (!isPlainObject(input)) return false; const entries = Object.entries(input); return !!entries.length && entries.every(([key, value]) => { if (key === "l") return value >= 0 && value <= 100; if (key === "alpha") return value >= 0 && value <= 1; return COLOR_KEYS.oklch.includes(key) && value >= 0 && value <= (key === "h" ? 360 : 1); }); } function isNamedColor(input) { return isString(input) && Object.keys(cssColors).includes(input.toLowerCase()); } function isNumber(input) { return typeof input === "number" && !Number.isNaN(input); } function isNumberInRange(input, min, max) { return isNumber(input) && input >= min && input <= max; } function isPlainObject(input) { if (!input) return false; const { toString } = Object.prototype; const prototype = Object.getPrototypeOf(input); return toString.call(input) === "[object Object]" && (prototype === null || prototype === Object.getPrototypeOf({})); } function isRGB(input) { if (!isPlainObject(input)) return false; const entries = Object.entries(input); return !!entries.length && entries.every(([key, value]) => { if (key === "alpha") return value >= 0 && value <= 1; return COLOR_KEYS.rgb.includes(key) && value >= 0 && value <= 255; }); } function isString(input, validate = true) { const isValid = typeof input === "string"; if (validate) return isValid && !!input.trim().length; return isValid; } function isValidColorModel(input) { return isHSL(input) || isRGB(input) || isLAB(input) || isLCH(input); } function formatHex(input) { invariant(isHex(input), MESSAGES.inputHex); let color = input.replace("#", ""); if (color.length === 3 || color.length === 4) { const values = [...color]; color = ""; values.forEach((d) => { color += `${d}${d}`; }); } const hex = `#${color}`; invariant(isHex(hex), MESSAGES.invalidHex); return hex; } function addAlpha(input, alpha) { invariant(isValidColorModel(input), MESSAGES.invalid); let value = alpha; if (value === void 0) return input; if (value > 1) value /= 100; if (value === 1) return input; return { ...input, alpha: value }; } function clamp(value, min = 0, max = 100) { return Math.min(Math.max(value, min), max); } function constrainDegrees(input, amount) { invariant(isNumber(input), MESSAGES.inputNumber); let value = input + amount; if (value > 360) value %= 360; if (value < 0) value += 360; return Math.abs(value); } function extractAlpha(input) { if (Array.isArray(input)) return; return input.alpha; } function limit(input, model, key) { invariant(isNumber(input), MESSAGES.inputNumber); invariant(COLOR_MODELS.includes(model), `${MESSAGES.invalidModel}${model ? `: ${model}` : ""}`); invariant(COLOR_KEYS[model].includes(key), `${MESSAGES.invalidKey}${key ? `: ${key}` : ""}`); switch (model) { case "hsl": invariant(COLOR_KEYS.hsl.includes(key), MESSAGES.invalidKey); if (["l", "s"].includes(key)) return clamp(input); return clamp(input, 0, 360); case "rgb": invariant(COLOR_KEYS.rgb.includes(key), MESSAGES.invalidKey); return clamp(input, 0, 255); default: throw new Error("Invalid inputs"); } } function parseInput(input, model) { const keys = COLOR_KEYS[model]; const validator = { hsl: isHSL, oklab: isLAB, oklch: isLCH, rgb: isRGB }; invariant(isPlainObject(input) || Array.isArray(input), MESSAGES.invalid); const value = Array.isArray(input) ? { [keys[0]]: input[0], [keys[1]]: input[1], [keys[2]]: input[2] } : input; invariant(validator[model](value), `${MESSAGES.invalidColor}: ${model}`); return value; } function restrictValues(input, precision = PRECISION, forcePrecision = true) { const output = new Map(Object.entries(input)); for (const [key, value] of output.entries()) output.set(key, round(value, precision, forcePrecision)); return Object.fromEntries(output); } function round(input, precision = 2, forcePrecision = true) { if (!isNumber(input) || input === 0) return 0; if (forcePrecision) { const factor2 = 10 ** precision; return Math.round(input * factor2) / factor2; } const absInput = Math.abs(input); let digits = Math.abs(Math.ceil(Math.log(absInput) / Math.LN10)); if (digits === 0) digits = 2; else if (digits > precision) digits = precision; let exponent = precision - (digits < 0 ? 0 : digits); if (exponent <= 1 && precision > 1) exponent = 2; else if (exponent > precision || exponent === 0) exponent = precision; const factor = 10 ** exponent; return Math.round(input * factor) / factor; } function addAlphaToHex(input, alpha) { invariant(isHex(input), MESSAGES.inputHex); invariant(isNumberInRange(alpha, 0, 1), MESSAGES.alpha); if (alpha >= 1) return removeAlphaFromHex(input); return `${removeAlphaFromHex(input)}${convertAlphaToHex(alpha)}`; } function convertAlphaToHex(input) { invariant(isNumber(input), MESSAGES.inputNumber); let alpha = input; if (input > 1) alpha /= 100; return Math.round(alpha * 255).toString(16).padStart(2, "0"); } function extractAlphaFromHex(input) { invariant(isHex(input), MESSAGES.inputString); const alpha = input.substring(7, 9); if (!alpha) return 1; return round(parseInt(alpha, 16) / 255); } function removeAlphaFromHex(input) { invariant(isHex(input), MESSAGES.inputHex); if (input.length === 5) return input.substring(0, 4); return input.substring(0, 7); } function hex2rgb(input) { invariant(isHex(input), MESSAGES.inputHex); const hex = formatHex(input).slice(1); const alpha = extractAlphaFromHex(input); return addAlpha({ r: parseInt(hex.charAt(0) + hex.charAt(1), 16), g: parseInt(hex.charAt(2) + hex.charAt(3), 16), b: parseInt(hex.charAt(4) + hex.charAt(5), 16) }, alpha); } var converters_exports = {}; __export(converters_exports, { hex2hsl: () => hex2hsl, hex2oklab: () => hex2oklab, hex2oklch: () => hex2oklch, hex2rgb: () => hex2rgb, hsl2hex: () => hsl2hex, hsl2oklab: () => hsl2oklab, hsl2oklch: () => hsl2oklch, hsl2rgb: () => hsl2rgb, oklab2hex: () => oklab2hex, oklab2hsl: () => oklab2hsl, oklab2oklch: () => oklab2oklch, oklab2rgb: () => oklab2rgb, oklch2hex: () => oklch2hex, oklch2hsl: () => oklch2hsl, oklch2oklab: () => oklch2oklab, oklch2rgb: () => oklch2rgb, rgb2hex: () => rgb2hex, rgb2hsl: () => rgb2hsl, rgb2oklab: () => rgb2oklab, rgb2oklch: () => rgb2oklch }); function rgb2hsl(input) { const value = parseInput(input, "rgb"); const alpha = extractAlpha(input); const rLimit = limit(value.r, "rgb", "r") / 255; const gLimit = limit(value.g, "rgb", "g") / 255; const bLimit = limit(value.b, "rgb", "b") / 255; const min = Math.min(rLimit, gLimit, bLimit); const max = Math.max(rLimit, gLimit, bLimit); const delta = max - min; let h = 0; let s; const l = (max + min) / 2; let rate; switch (max) { case rLimit: rate = !delta ? 0 : (gLimit - bLimit) / delta; h = 60 * rate; break; case gLimit: rate = (bLimit - rLimit) / delta; h = 60 * rate + 120; break; case bLimit: rate = (rLimit - gLimit) / delta; h = 60 * rate + 240; break; default: break; } if (h < 0) h = 360 + h; if (min === max) s = 0; else s = l < .5 ? delta / (2 * l) : delta / (2 - 2 * l); return addAlpha({ h: Math.abs(+(h % 360).toFixed(2)), s: +(s * 100).toFixed(2), l: +(l * 100).toFixed(2) }, alpha); } function hex2hsl(input) { invariant(isHex(input), MESSAGES.inputHex); const alpha = extractAlphaFromHex(input); return addAlpha(rgb2hsl(hex2rgb(input)), alpha); } var { cbrt, sign } = Math; function rgb2lrgb(input) { const abs2 = Math.abs(input); if (abs2 < .04045) return input / 12.92; return (sign(input) || 1) * ((abs2 + .055) / 1.055) ** 2.4; } function rgb2oklab(input, precision = PRECISION) { const value = parseInput(input, "rgb"); const alpha = extractAlpha(input); const [lr, lg, lb] = [ rgb2lrgb(value.r / 255), rgb2lrgb(value.g / 255), rgb2lrgb(value.b / 255) ]; const l = cbrt(LRGB_TO_LMS.l[0] * lr + LRGB_TO_LMS.l[1] * lg + LRGB_TO_LMS.l[2] * lb); const m = cbrt(LRGB_TO_LMS.m[0] * lr + LRGB_TO_LMS.m[1] * lg + LRGB_TO_LMS.m[2] * lb); const s = cbrt(LRGB_TO_LMS.s[0] * lr + LRGB_TO_LMS.s[1] * lg + LRGB_TO_LMS.s[2] * lb); return addAlpha(restrictValues({ l: LSM_TO_LAB.l[0] * l + LSM_TO_LAB.l[1] * m - LSM_TO_LAB.l[2] * s, a: LSM_TO_LAB.a[0] * l - LSM_TO_LAB.a[1] * m + LSM_TO_LAB.a[2] * s, b: LSM_TO_LAB.b[0] * l + LSM_TO_LAB.b[1] * m - LSM_TO_LAB.b[2] * s }, precision), alpha); } function hex2oklab(input, precision) { invariant(isHex(input), MESSAGES.inputHex); const alpha = extractAlphaFromHex(input); return addAlpha(rgb2oklab(hex2rgb(input), precision), alpha); } var { atan2, sqrt } = Math; function oklab2oklch(input, precision) { const { l, a, b } = restrictValues(parseInput(input, "oklab")); const alpha = extractAlpha(input); const c = sqrt(a ** 2 + b ** 2); let h = (atan2(b, a) * RAD2DEG + 360) % 360; if (round(c * 1e4) === 0) h = 0; return addAlpha(restrictValues({ l, c, h }, precision), alpha); } function rgb2oklch(input, precision) { const value = parseInput(input, "rgb"); const alpha = extractAlpha(input); return addAlpha(oklab2oklch(rgb2oklab(value, precision), precision), alpha); } function hex2oklch(input, precision) { invariant(isHex(input), MESSAGES.inputHex); const alpha = extractAlphaFromHex(input); return addAlpha(rgb2oklch(hex2rgb(input), precision), alpha); } function hue2rgb(point, chroma2, h) { invariant(isNumber(point) && isNumber(chroma2) && isNumber(h), MESSAGES.hueArgs); let hue = h; if (hue < 0) hue += 1; if (hue > 1) hue -= 1; if (hue < 1 / 6) return round(point + (chroma2 - point) * 6 * hue, 4); if (hue < 1 / 2) return round(chroma2, 4); if (hue < 2 / 3) return round(point + (chroma2 - point) * (2 / 3 - hue) * 6, 4); return round(point, 4); } function hsl2rgb(input) { const value = parseInput(input, "hsl"); const alpha = extractAlpha(input); const h = round(value.h) / 360; const s = round(value.s) / 100; const l = round(value.l) / 100; let r; let g; let b; let point; let chroma2; if (s === 0) { r = l; g = l; b = l; } else { chroma2 = l < .5 ? l * (1 + s) : l + s - l * s; point = 2 * l - chroma2; r = hue2rgb(point, chroma2, h + 1 / 3); g = hue2rgb(point, chroma2, h); b = hue2rgb(point, chroma2, h - 1 / 3); } return addAlpha({ r: Math.round(r * 255), g: Math.round(g * 255), b: Math.round(b * 255) }, alpha); } function rgb2hex(input) { const rgb = parseInput(input, "rgb"); const alpha = extractAlpha(input); const hex = `#${[ rgb.r, rgb.g, rgb.b ].map((d) => `0${Math.floor(d).toString(16)}`.slice(-2)).join("")}`; return alpha !== void 0 && alpha < 1 ? addAlphaToHex(hex, alpha) : hex; } function hsl2hex(input) { const value = parseInput(input, "hsl"); const alpha = extractAlpha(input); const hex = rgb2hex(hsl2rgb(value)); return alpha !== void 0 && alpha < 1 ? addAlphaToHex(hex, alpha) : hex; } function hsl2oklab(input, precision) { const value = parseInput(input, "hsl"); const alpha = extractAlpha(input); return addAlpha(rgb2oklab(hsl2rgb(value), precision), alpha); } function hsl2oklch(input, precision) { const value = parseInput(input, "hsl"); const alpha = extractAlpha(input); return addAlpha(rgb2oklch(hsl2rgb(value), precision), alpha); } var { abs } = Math; function lrgb2rgb(input) { const absoluteNumber = abs(input); const sign2 = input < 0 ? -1 : 1; if (absoluteNumber > .0031308) return sign2 * (absoluteNumber ** (1 / 2.4) * 1.055 - .055); return input * 12.92; } function oklab2rgb(input, precision = 0) { const { l: L, a: A, b: B } = parseInput(input, "oklab"); const alpha = extractAlpha(input); const l = (L + LAB_TO_LMS.l[0] * A + LAB_TO_LMS.l[1] * B) ** 3; const m = (L + LAB_TO_LMS.m[0] * A + LAB_TO_LMS.m[1] * B) ** 3; const s = (L + LAB_TO_LMS.s[0] * A + LAB_TO_LMS.s[1] * B) ** 3; const r = 255 * lrgb2rgb(LSM_TO_RGB.r[0] * l + LSM_TO_RGB.r[1] * m + LSM_TO_RGB.r[2] * s); const g = 255 * lrgb2rgb(LSM_TO_RGB.g[0] * l + LSM_TO_RGB.g[1] * m + LSM_TO_RGB.g[2] * s); const b = 255 * lrgb2rgb(LSM_TO_RGB.b[0] * l + LSM_TO_RGB.b[1] * m + LSM_TO_RGB.b[2] * s); return addAlpha({ r: clamp(round(r, precision), 0, 255), g: clamp(round(g, precision), 0, 255), b: clamp(round(b, precision), 0, 255) }, alpha); } function oklab2hex(input) { const value = parseInput(input, "oklab"); const alpha = extractAlpha(input); const hex = rgb2hex(oklab2rgb(value)); return alpha !== void 0 && alpha < 1 ? addAlphaToHex(hex, alpha) : hex; } function oklab2hsl(input) { const value = parseInput(input, "oklab"); const alpha = extractAlpha(input); return addAlpha(rgb2hsl(oklab2rgb(value)), alpha); } var { sin, cos } = Math; function oklch2oklab(input, precision) { let { l, c, h } = parseInput(input, "oklch"); const alpha = extractAlpha(input); if (Number.isNaN(h) || h < 0) h = 0; return addAlpha(restrictValues({ l, a: c * cos(h * DEG2RAD), b: c * sin(h * DEG2RAD) }, precision), alpha); } function oklch2rgb(input, precision = 0) { const value = parseInput(input, "oklch"); const alpha = extractAlpha(input); return addAlpha(oklab2rgb(oklch2oklab(value), precision), alpha); } function oklch2hex(input) { const value = parseInput(input, "oklch"); const alpha = extractAlpha(input); const hex = rgb2hex(oklch2rgb(value)); return alpha !== void 0 && alpha < 1 ? addAlphaToHex(hex, alpha) : hex; } function oklch2hsl(input) { const value = parseInput(input, "oklch"); const alpha = extractAlpha(input); return addAlpha(rgb2hsl(oklch2rgb(value)), alpha); } var MODEL = "(rgb|hsl|oklab|oklch)a?"; var SEP = "(?:\\s*[,/]\\s*|\\s+)"; var VALUE = "(none|[\\d%.-]+(?:deg|grad|rad|turn)?)"; var colorRegex = new RegExp(`${MODEL}\\s*\\(\\s*${VALUE}${SEP}${VALUE}${SEP}${VALUE}(?:${SEP}${VALUE})?\\s*\\)`, "i"); function parseAngle(value) { const number_ = parseFloat(value); let result; if (value.endsWith("grad")) result = number_ * .9; else if (value.endsWith("rad")) result = number_ * (180 / Math.PI); else if (value.endsWith("turn")) result = number_ * 360; else result = number_; return Math.round(result * 1e5) / 1e5; } function extractColorParts(input) { invariant(isString(input), MESSAGES.inputString); if (isHex(input)) { const keys2 = COLOR_KEYS.rgb; const { r, g, b } = hex2rgb(input); const alpha2 = extractAlphaFromHex(input); return { model: "rgb", [keys2[0]]: r, [keys2[1]]: g, [keys2[2]]: b, alpha: alpha2 < 1 ? alpha2 : void 0 }; } const matches = colorRegex.exec(input); invariant(hasValidMatches(matches), MESSAGES.invalidCSS); const model = matches[1]; const keys = COLOR_KEYS[model]; let alpha = matches[5] ? parseFloat(matches[5]) : 1; if (alpha > 1) alpha /= 100; const parseValue = (value, index) => { if (value === "none") return 0; if (model === "hsl" && index === 0 || model === "oklch" && index === 2) return parseAngle(value); const parsedValue = parseFloat(value); if (!value.includes("%")) return parsedValue; if (model === "oklch") { if (index === 1) return parsedValue * .4 / 100; } else if (model === "oklab") { if (index === 1 || index === 2) return parsedValue * .4 / 100; } return parsedValue; }; const values = [ parseValue(matches[2], 0), parseValue(matches[3], 1), parseValue(matches[4], 2) ]; if (model === "oklab") { invariant(values[1] >= -.4 && values[1] <= .4, MESSAGES.invalidRange); invariant(values[2] >= -.4 && values[2] <= .4, MESSAGES.invalidRange); } else if (model === "oklch") invariant(values[1] >= 0 && values[1] <= .4, MESSAGES.invalidRange); return { model, [keys[0]]: values[0], [keys[1]]: values[1], [keys[2]]: values[2], alpha: alpha < 1 ? alpha : void 0 }; } var toHslConverters = { oklab: oklab2hsl, oklch: oklch2hsl, rgb: rgb2hsl }; var toOklabConverters = { hsl: hsl2oklab, oklch: oklch2oklab, rgb: rgb2oklab }; var toOklchConverters = { hsl: hsl2oklch, oklab: oklab2oklch, rgb: rgb2oklch }; var toRgbConverters = { hsl: hsl2rgb, oklab: oklab2rgb, oklch: oklch2rgb }; var toHexConverters = { hsl: hsl2hex, oklab: oklab2hex, oklch: oklch2hex, rgb: rgb2hex }; var fromHexConverters = { hsl: hex2hsl, oklab: hex2oklab, oklch: hex2oklch, rgb: hex2rgb }; var converterTables = { hsl: toHslConverters, oklab: toOklabConverters, oklch: toOklchConverters, rgb: toRgbConverters }; function convertFromCSS(value, output) { const { alpha, model, ...color } = extractColorParts(value); if (["oklab", "oklch"].includes(model) && color.l > 1) color.l = round(color.l / 100, PRECISION); const colorTuple = Object.values(color); if (output === "hex") { const alphaPrefix = alpha ? convertAlphaToHex(alpha) : ""; return `${toHexConverters[model](colorTuple)}${alphaPrefix}`; } const converter = converterTables[output]?.[model]; return addAlpha(converter ? converter(colorTuple) : color, alpha); } function convertFromHex(value, output) { const alpha = extractAlphaFromHex(value); if (output === "hex") return `${removeAlphaFromHex(value)}${alpha !== 1 ? convertAlphaToHex(alpha) : ""}`; return addAlpha(fromHexConverters[output](value), alpha); } function parseCSS(input, format) { invariant(isString(input), MESSAGES.inputString); const value = isNamedColor(input) ? cssColors[input.toLowerCase()] : input; const output = format ?? (isHex(value) ? "hex" : extractColorParts(value).model); if (isHex(value)) return convertFromHex(value, output); return convertFromCSS(value, output); } var mainTRC = 2.4; var normBG = .56; var normTXT = .57; var revBG = .65; var revTXT = .62; var sRco = .2126729; var sGco = .7151522; var sBco = .072175; var blkThreshold = .022; var blkClamp = 1.414; var scaleBoW = 1.14; var scaleWoB = 1.14; var loBoWOffset = .027; var loWoBOffset = .027; var loClip = .1; var deltaYmin = 5e-4; function softClamp(Y) { return Y > blkThreshold ? Y : Y + (blkThreshold - Y) ** blkClamp; } function sRGBtoY(r, g, b) { return sRco * (r / 255) ** mainTRC + sGco * (g / 255) ** mainTRC + sBco * (b / 255) ** mainTRC; } function apcaContrast(background, foreground) { invariant(isString(background), MESSAGES.inputString); invariant(isString(foreground), MESSAGES.inputString); const bg = hex2rgb(parseCSS(background, "hex")); const fg = hex2rgb(parseCSS(foreground, "hex")); const txtY = softClamp(sRGBtoY(fg.r, fg.g, fg.b)); const bgY = softClamp(sRGBtoY(bg.r, bg.g, bg.b)); if (Math.abs(bgY - txtY) < deltaYmin) return 0; const SAPC = bgY > txtY ? (bgY ** normBG - txtY ** normTXT) * scaleBoW : (bgY ** revBG - txtY ** revTXT) * scaleWoB; if (Math.abs(SAPC) < loClip) return 0; return round(SAPC > 0 ? (SAPC - loBoWOffset) * 100 : (SAPC + loWoBOffset) * 100, 5); } function brightnessDifference(left, right, precision = PRECISION) { invariant(isString(left), MESSAGES.left); invariant(isString(right), MESSAGES.right); const RGBLeft = parseCSS(left, "rgb"); const RGBRight = parseCSS(right, "rgb"); const brightnessLeft = (RGBLeft.r * 299 + RGBLeft.g * 587 + RGBLeft.b * 114) / 1e3; const brightnessRight = (RGBRight.r * 299 + RGBRight.g * 587 + RGBRight.b * 114) / 1e3; return round(Math.abs(brightnessRight - brightnessLeft), precision); } function chroma(input) { invariant(isString(input), MESSAGES.inputString); const { r, g, b } = parseCSS(input, "rgb"); return round((Math.max(r, g, b) - Math.min(r, g, b)) / 255, 4); } function colorDifference(left, right) { invariant(isString(left), MESSAGES.left); invariant(isString(right), MESSAGES.right); const RGBLeft = parseCSS(left, "rgb"); const RGBRight = parseCSS(right, "rgb"); return Math.max(RGBLeft.r, RGBRight.r) - Math.min(RGBLeft.r, RGBRight.r) + (Math.max(RGBLeft.g, RGBRight.g) - Math.min(RGBLeft.g, RGBRight.g)) + (Math.max(RGBLeft.b, RGBRight.b) - Math.min(RGBLeft.b, RGBRight.b)); } function luminance(input) { invariant(isString(input), MESSAGES.inputString); const { r, g, b } = parseCSS(input, "rgb"); const rgb = [ r / 255, g / 255, b / 255 ]; for (let index = 0; index < rgb.length; index++) if (rgb[index] <= .04045) rgb[index] /= 12.92; else rgb[index] = ((rgb[index] + .055) / 1.055) ** 2.4; return round(.2126 * rgb[0] + .7152 * rgb[1] + .0722 * rgb[2], 4); } function contrast(left, right) { invariant(isString(left), MESSAGES.left); invariant(isString(right), MESSAGES.right); const LuminanceLeft = luminance(left); const LuminanceRight = luminance(right); return round(LuminanceLeft >= LuminanceRight ? (LuminanceLeft + .05) / (LuminanceRight + .05) : (LuminanceRight + .05) / (LuminanceLeft + .05)); } function compare(left, right) { invariant(isString(left), MESSAGES.left); invariant(isString(right), MESSAGES.right); const colorThreshold = 500; const brightnessThreshold = 125; const colorDifference2 = colorDifference(left, right); const contrast2 = contrast(left, right); const brightnessDifference2 = brightnessDifference(left, right); const isBright = brightnessDifference2 >= brightnessThreshold; const hasEnoughDifference = colorDifference2 >= colorThreshold; let compliant = 0; if (isBright && hasEnoughDifference) compliant = 2; else if (isBright || hasEnoughDifference) compliant = 1; return { brightnessDifference: brightnessDifference2, colorDifference: colorDifference2, compliant, contrast: contrast2, largeAA: contrast2 >= 3, largeAAA: contrast2 >= 4.5, normalAA: contrast2 >= 4.5, normalAAA: contrast2 >= 7 }; } function getColorModel(input) { if (isHex(input) || isNamedColor(input)) return "hex"; if (isHSL(input)) return "hsl"; if (isLAB(input)) return "oklab"; if (isLCH(input)) return "oklch"; if (isRGB(input)) return "rgb"; throw new Error(MESSAGES.invalid); } function getColorValue(input, output) { const value = isNamedColor(input) ? cssColors[input.toLowerCase()] : input; const from = getColorModel(value); if (from === output) return value; const converter = converters_exports[`${from}2${output}`]; if (!converter) throw new Error(`Converter not found for ${from} to ${output}`); return converter(value); } function formatCSS(input, options = {}) { invariant(isHex(input) || isValidColorModel(input), MESSAGES.invalid); const { alpha, format = "hex", precision = PRECISION, separator: baseSeparator = " " } = options; const opacity2 = alpha && alpha !== 1 ? `${round(alpha * 100)}%` : null; let params = []; let separator = baseSeparator; switch (format) { case "hsl": { const { h, s, l } = getColorValue(input, "hsl"); params = [ h, `${s}%`, `${l}%` ]; break; } case "oklab": { separator = " "; const { l, a, b } = restrictValues(getColorValue(input, "oklab"), precision); params = [ `${round(l * 100, precision)}%`, a, b ]; break; } case "oklch": { separator = " "; const { l, c, h } = restrictValues(getColorValue(input, "oklch"), precision); params = [ `${round(l * 100, precision)}%`, c, h ]; break; } case "rgb": { const { r, g, b } = getColorValue(input, "rgb"); params = [ r, g, b ]; break; } default: { const hex = removeAlphaFromHex(getColorValue(input, "hex")); if (alpha && alpha !== 1) return `${hex}${convertAlphaToHex(alpha)}`; return hex; } } return `${format}(${params.join(separator)}${opacity2 ? ` / ${opacity2}` : ""})`; } function updater(key, operator, format) { return (input, amount) => { invariant(isString(input), MESSAGES.inputString); invariant(isNumberInRange(amount, 0, 100), MESSAGES.amount); const color = parseCSS(input, "hsl"); const output = isHex(input) || isNamedColor(input) ? "hex" : extractColorParts(input).model; return formatCSS({ ...color, [key]: clamp(color[key] + (operator === "+" ? amount : -amount), 0, 100) }, { format: format ?? output }); }; } function darken(input, amount, format) { return updater("l", "-", format)(input, amount); } function desaturate(input, amount, format) { return updater("s", "-", format)(input, amount); } function grayscale(input, format) { invariant(isString(input), MESSAGES.inputString); const output = isHex(input) || isNamedColor(input) ? "hex" : extractColorParts(input).model; const lch = parseCSS(input, "oklch"); return formatCSS({ ...lch, c: 0 }, { format: format ?? output, alpha: lch.alpha }); } function rotate(input, degrees, format) { invariant(isString(input), MESSAGES.inputString); invariant(isNumberInRange(degrees, -360, 360), MESSAGES.degreesRange); const color = parseCSS(input, "hsl"); const output = isHex(input) || isNamedColor(input) ? "hex" : extractColorParts(input).model; return formatCSS({ ...color, h: constrainDegrees(color.h, degrees) }, { format: format ?? output }); } function invert(input) { invariant(isString(input), MESSAGES.inputString); const format = isHex(input) || isNamedColor(input) ? "hex" : extractColorParts(input).model; return formatCSS(rotate(parseCSS(input, "hex"), 180), { format }); } function lighten(input, amount, format) { return updater("l", "+", format)(input, amount); } function interpolateHue(h1, h2, c1, c2, ratio) { if (c1 < 1e-4) return h2; if (c2 < 1e-4) return h1; let diff = h2 - h1; if (diff > 180) diff -= 360; else if (diff < -180) diff += 360; let result = h1 + diff * ratio; if (result < 0) result += 360; else if (result >= 360) result -= 360; return result; } function mix(color1, color2, ratio = .5, format) { invariant(isString(color1), MESSAGES.inputString); invariant(isString(color2), MESSAGES.inputString); invariant(isNumberInRange(ratio, 0, 1), MESSAGES.ratioRange); const output = isHex(color1) || isNamedColor(color1) ? "hex" : extractColorParts(color1).model; const lch1 = parseCSS(color1, "oklch"); const lch2 = parseCSS(color2, "oklch"); const l = lch1.l + (lch2.l - lch1.l) * ratio; const c = lch1.c + (lch2.c - lch1.c) * ratio; const h = interpolateHue(lch1.h, lch2.h, lch1.c, lch2.c, ratio); const alpha1 = lch1.alpha ?? 1; const alpha = alpha1 + ((lch2.alpha ?? 1) - alpha1) * ratio; return formatCSS({ l, c, h }, { format: format ?? output, alpha: alpha < 1 ? alpha : void 0 }); } function parseColor(color) { invariant(!!color, MESSAGES.input); const output = {}; if (isString(color)) { const { alpha = 1 } = extractColorParts(color); const type = isHex(color) ? "hex" : extractColorParts(color).model; output.hex = addAlphaToHex(parseCSS(color, "hex"), alpha); output.hsl = addAlpha(parseCSS(color, "hsl"), alpha); output.oklab = addAlpha(parseCSS(color, "oklab"), alpha); output.oklch = addAlpha(parseCSS(color, "oklch"), alpha); output.rgb = addAlpha(parseCSS(color, "rgb"), alpha); output.alpha = alpha; output.type = type; } else if (isPlainObject(color)) { const { alpha = 1 } = color; if (isHSL(color)) { output.hsl = { h: limit(color.h, "hsl", "h"), s: limit(color.s, "hsl", "s"), l: limit(color.l, "hsl", "l") }; output.rgb = hsl2rgb(output.hsl); output.oklab = hsl2oklab(output.hsl); output.oklch = hsl2oklch(output.hsl); output.type = "hsl"; } else if (isLAB(color)) { output.hsl = oklab2hsl(color); output.oklab = color; output.oklch = oklab2oklch(color); output.rgb = oklab2rgb(color); output.type = "oklab"; } else if (isLCH(color)) { output.hsl = oklch2hsl(color); output.oklab = oklch2oklab(color); output.oklch = color; output.rgb = oklch2rgb(color); output.type = "oklch"; } else if (isRGB(color)) { output.rgb = { r: limit(color.r, "rgb", "r"), g: limit(color.g, "rgb", "g"), b: limit(color.b, "rgb", "b") }; output.hsl = rgb2hsl(output.rgb); output.oklab = rgb2oklab(output.rgb); output.oklch = rgb2oklch(output.rgb); output.type = "rgb"; } else throw new Error("invalid color"); output.hex = addAlphaToHex(hsl2hex(output.hsl), alpha); output.hsl = addAlpha(output.hsl, alpha); output.oklab = addAlpha(output.oklab, alpha); output.oklch = addAlpha(output.oklch, alpha); output.rgb = addAlpha(output.rgb, alpha); output.alpha = alpha; } else throw new Error(MESSAGES.input); return output; } function opacify(input, alpha, format) { invariant(isString(input), MESSAGES.inputString); invariant(isNumberInRange(alpha, 0, 1), MESSAGES.alpha); const type = isHex(input) || isNamedColor(input) ? "hex" : extractColorParts(input).model; return formatCSS(parseCSS(input, "rgb"), { format: format ?? type, alpha }); } function opacity(input) { invariant(isString(input), MESSAGES.inputString); if (isHex(input)) return extractAlphaFromHex(input); else if (Object.keys(cssColors).includes(input)) return 1; const { alpha } = extractColorParts(input); if (!alpha) return 1; return alpha; } function pickByContrast(darkContrast, lightContrast, darkColor, lightColor) { return darkContrast >= lightContrast ? darkColor : lightColor; } function readableColor(backgroundColor, options = {}) { const { darkColor = "#000000", lightColor = "#ffffff", method = "yiq", threshold } = options; invariant(isString(backgroundColor), MESSAGES.inputString); switch (method) { case "yiq": { const yiqThreshold = threshold ?? 128; invariant(yiqThreshold >= 0 && yiqThreshold <= 255, MESSAGES.threshold); const { r, g, b } = hex2rgb(parseCSS(backgroundColor, "hex")); return (r * 299 + g * 587 + b * 114) / 1e3 >= yiqThreshold ? darkColor : lightColor; } case "wcag": { const wcagThreshold = threshold ?? .5; invariant(wcagThreshold >= 0 && wcagThreshold <= 1, MESSAGES.thresholdNormalized); return luminance(backgroundColor) >= wcagThreshold ? darkColor : lightColor; } case "contrast": return pickByContrast(contrast(darkColor, backgroundColor), contrast(lightColor, backgroundColor), darkColor, lightColor); case "oklab": { const oklabThreshold = threshold ?? .5; invariant(oklabThreshold >= 0 && oklabThreshold <= 1, MESSAGES.thresholdNormalized); const { l } = hex2oklab(parseCSS(backgroundColor, "hex")); return l >= oklabThreshold ? darkColor : lightColor; } case "apca": return pickByContrast(Math.abs(apcaContrast(backgroundColor, darkColor)), Math.abs(apcaContrast(backgroundColor, lightColor)), darkColor, lightColor); default: throw new Error(`Unknown method: ${method}`); } } function saturate(input, amount, format) { return updater("s", "+", format)(input, amount); } function transparentize(input, alpha, format) { invariant(isString(input), MESSAGES.inputString); invariant(isNumberInRange(alpha, -1, 1), MESSAGES.alphaAdjustment); return formatCSS(parseCSS(input, "oklab"), { format, alpha: round(clamp(opacity(input) - alpha, 0, 1)) }); } var Colorizr = class { constructor(color, options = {}) { /** The alpha/opacity value (0-1). */ __publicField(this, "alpha"); __publicField(this, "hex"); __publicField(this, "hsl"); __publicField(this, "oklab"); __publicField(this, "oklch"); __publicField(this, "rgb"); __publicField(this, "type"); invariant(!!color, MESSAGES.colorRequired); const { alpha, hex, hsl, oklab, oklch, rgb, type } = parseColor(color); this.hex = hex; this.hsl = hsl; this.oklab = oklab; this.oklch = oklch; this.rgb = rgb; this.alpha = alpha; this.type = options.format ?? type; } /** * Get CSS string */ get css() { return this.currentColor; } /** * Get the red value */ get red() { return this.rgb.r; } /** * Get the green value */ get green() { return this.rgb.g; } /** * Get the blue value */ get blue() { return this.rgb.b; } /** * Get the hue value */ get hue() { return this.hsl.h; } /** * Get the saturation value */ get saturation() { return this.hsl.s; } /** * Get the lightness value */ get lightness() { return this.hsl.l; } /** * Get the luminance value */ get luminance() { return luminance(this.currentColor); } /** * Get the chroma value */ get chroma() { return chroma(this.currentColor); } get opacity() { return opacity(this.currentColor); } /** * Get the most readable color (light or dark) for this color as a background. */ get readableColor() { return readableColor(this.currentColor); } get currentColor() { return formatCSS(this[this.type], { format: this.type, alpha: this.alpha }); } /** * Get the brightness difference between this color and another. * * @param input - The color to compare against. * @returns The brightness difference value. */ brightnessDifference(input) { return brightnessDifference(this.currentColor, input); } /** * Get the color difference between this color and another. * * @param input - The color to compare against. * @returns The color difference value. */ colorDifference(input) { return colorDifference(this.currentColor, input); } /** * Test 2 colors for WCAG compliance. * * @param input - The color to compare against. * @returns Analysis object with compliance information. */ compare(input) { return compare(this.currentColor, input); } /** * Get the contrast ratio between this color and another. * * @param input - The color to compare against. * @returns The contrast ratio. */ contrast(input) { return contrast(this.currentColor, input); } /** * Format the color to a specific type. * * @param type - The color format to convert to. * @param precision - The decimal precision for the output. * @returns The formatted color string. */ format(type, precision) { return formatCSS(this.rgb, { alpha: this.alpha, format: type, precision }); } /** * Increase lightness. * * @param amount - A number between 0 and 100. * @returns The lightened color string. */ lighten(amount) { return lighten(this.currentColor, amount); } /** * Decrease lightness. * * @param amount - A number between 0 and 100. * @returns The darkened color string. */ darken(amount) { return darken(this.currentColor, amount); } /** * Increase saturation. * * @param amount - A number between 0 and 100. * @returns The saturated color string. */ saturate(amount) { return saturate(this.currentColor, amount); } /** * Decrease saturation. * * @param amount - A number between 0 and 100. * @returns The desaturated color string. */ desaturate(amount) { return desaturate(this.currentColor, amount); } /** * Convert to grayscale. * * @returns The grayscale color string. */ grayscale() { return grayscale(this.currentColor); } /** * Invert color. * * @returns The inverted color string. */ invert() { return invert(this.currentColor); } /** * Mix with another color. * * @param color - The color to mix with. * @param ratio - A number between 0 and 1 (0 = this color, 1 = input color). * @returns The mixed color string. */ mix(color, ratio) { return mix(this.currentColor, color, ratio); } /** * Add opacity to the color. * * @param alpha - A number between 0 and 1. * @returns The opacified color string. */ opacify(alpha = .9) { return opacify(this.currentColor, alpha, this.type); } /** * Rotate color hue. * * @param degrees - A number between -360 and 360. * @returns The rotated color string. */ rotate(degrees) { return rotate(this.currentColor, degrees); } /** * Make the color more transparent. * * @param alpha - A number between -1 and 1. * @returns The transparentized color string. */ transparentize(alpha = .1) { return transparentize(this.currentColor, alpha, this.type); } }; function convert$1(input, format) { return formatCSS(parseCSS(input, format), { format }); } /* v8 ignore next 3 -- @preserve */ //#endregion //#region ../packages/visual/src/colour/guards.ts function isHsl(v) { if (typeof v !== `object`) return false; if (!(`h` in v)) return false; if (!(`s` in v)) return false; if (!(`l` in v)) return false; if (!(`unit` in v)) return false; if (!(`space` in v)) return false; if (v.space !== `hsl`) return false; return true; } function isRgb(v) { if (typeof v !== `object`) return false; if (!(`r` in v)) return false; if (!(`g` in v)) return false; if (!(`b` in v)) return false; if (!(`space` in v)) return false; if (!(`unit` in v)) return false; if (v.space === `srgb`) return true; return false; } /** * If the input object has r,g&b properties, it will return a fully- * formed Rgb type with `unit` and `space` properties. * * If it lacks these basic three properties or they are out of range, * _undefined_ is returned. * * If RGB values are less than 1 assumes unit:scalar. Otherwise unit:8bit. * If RGB values exceed 255, _undefined_ returned. * @param v * @returns */ function tryParseObjectToRgb(v) { if (typeof v !== `object`) throw new TypeError(`Param 'v' is expected to be an object, got: ${typeof v}`); if (!(`r` in v && `g` in v && `b` in v)) return; if (!(`unit` in v)) if (v.r <= 1 && v.g <= 1 && v.b <= 1) v.unit = `scalar`; else if (v.r > 255 && v.g <= 255 && v.b <= 255) return; else v.unit = `8bit`; if (!(`space` in v)) v.space = `srgb`; return v; } function tryParseObjectToHsl(v) { if (!(`h` in v && `s` in v && `l` in v)) return; if (!(`unit` in v)) if (v.s <= 1 && v.l <= 1) v.unit = `scalar`; else if (v.s > 100 || v.l > 100) return; else v.unit = `absolute`; if (!(`space` in v)) v.space = `hsl`; return v; } function isOkLch(v) { if (typeof v !== `object`) return false; if (!(`l` in v)) return false; if (!(`c` in v)) return false; if (!(`h` in v)) return false; if (!(`unit` in v)) return false; if (!(`space` in v)) return false; if (v.space === `lch`) return true; if (v.space === `oklch`) return true; return false; } function isColourish(v) { if (typeof v === `string`) return true; if (typeof v !== `object`) return false; if (isHsl(v)) return true; if (isOkLch(v)) return true; if (isRgb(v)) return true; return false; } //#endregion //#region ../packages/visual/src/colour/utility.ts function calculateHueDistance(a, b, limit = 1) { let long = -1; let short = -1; if (b < a) { long = b - a; short = limit - (a - b); } else { long = b - a; short = long - limit; } const forward = short > 0 ? short : long; const backward = short > 0 ? long : short; if (Math.abs(long) < Math.abs(short)) { const t = short; short = long; long = t; } return { long, short, forward, backward }; } function libraryRgbToHexString(rgb) { const componentToHex = (c) => { const hex = Math.floor(c).toString(16); return hex.length === 1 ? `0${hex}` : hex; }; let part = `#${componentToHex(rgb.r)}${componentToHex(rgb.g)}${componentToHex(rgb.b)}`; if (typeof rgb.alpha !== `undefined` && rgb.alpha !== 255) part += componentToHex(rgb.alpha); return part; } function wrapScalarHue(value) { value = value % 1; if (value < 0) return (1 - Math.abs(value)) % 1; return value; } //#endregion //#region ../packages/visual/src/colour/hsl.ts var hsl_exports = /* @__PURE__ */ __exportAll({ absolute: () => absolute$1, changeLightness: () => changeLightness$1, fromCss: () => fromCss$3, fromHexString: () => fromHexString$3, generateScalar: () => generateScalar$1, guard: () => guard$4, interpolator: () => interpolator$3, parseCssHslFunction: () => parseCssHslFunction, scalar: () => scalar$3, toAbsolute: () => toAbsolute$1, toCssString: () => toCssString$2, toHexString: () => toHexString$3, toLibraryRgb: () => toLibraryRgb, toScalar: () => toScalar$3, withOpacity: () => withOpacity$3 }); /** * Scales the opacity value of an input HSL value * ```js * withOpacity() * ``` * @param value Colour * @param fn Function that calcules opacity based on input scalar value */ function withOpacity$3(value, fn) { switch (value.unit) { case `absolute`: return { ...value, opacity: fn((value.opacity ?? 100) / 100, value) * 100 }; case `scalar`: return { ...value, opacity: fn(value.opacity ?? 1, value) }; } } /** * Increases or decreases lightness by this percentage, returning new colour * * Amount to change: * 'fixed': a fixed amount * 'delta': increase/decrease by this amount * 'pdelta': proportion of current value to change by ('percentage delta') * * ``` * const colour = { h: 0.5, s: 0.5, l: 0.5, space: `hsl`, unit: `scalar` }; * changeLightness(colour, { pdelta: 0.1 }); // l: 0.55 * changeLightness(colour, { delta: 0.1 }); // l: 0.6 * changeLightness(colour, { fixed: 0.5 }); // l: 0.5 * ``` * * Keep in mind the numerical value will depend on the unit of `value`. If it's scalar, * lightness is 0..1 scale, otherwise 0..100 scale. * * Use negative values to decrease (does not apply to 'fixed') * @param value Hsl colour * @param amount Amount to change */ function changeLightness$1(value, amount) { let newL = 0; if (typeof amount.pdelta !== `undefined`) newL = value.l + value.l * amount.pdelta; else if (typeof amount.delta !== `undefined`) newL = amount.delta + value.l; else if (typeof amount.fixed !== `undefined`) { if (amount.fixed < 0) throw new TypeError(`Cannot use negative value with 'fixed'`); newL = amount.fixed; } else throw new TypeError(`Parameter 'amount' is missing 'delta/pdelta/fixed' properties`); return { ...value, l: scaleProperty(value, newL, `l`) }; } function scaleProperty(hsl, value, property) { if (hsl.unit === `scalar`) { if (value > 1) value = 1; else if (value < 0) value = 0; } else if (value