ixfx
Version:
A framework for programming interactivity
1,766 lines (1,763 loc) • 131 kB
JavaScript
import {
clamp,
clamper
} from "./chunk-QAEJS6HO.js";
import {
pairwise
} from "./chunk-BGQOJZFW.js";
import {
defaultRandom
} from "./chunk-5VWJ6TUI.js";
import {
numberInclusiveRangeTest,
throwFromResult,
throwNumberTest
} from "./chunk-UC4AQMTL.js";
import {
__export
} from "./chunk-L5EJU35C.js";
// src/visual/colour/index.ts
var colour_exports = {};
__export(colour_exports, {
cssLinearGradient: () => cssLinearGradient,
getCssVariable: () => getCssVariable,
goldenAngleColour: () => goldenAngleColour,
hslFromAbsoluteValues: () => hslFromAbsoluteValues,
hslFromRelativeValues: () => hslFromRelativeValues,
hslToAbsolute: () => hslToAbsolute,
hslToColorJs: () => hslToColorJs,
hslToRelative: () => hslToRelative,
hslToString: () => hslToString,
interpolator: () => interpolator,
isHsl: () => isHsl,
isOklch: () => isOklch,
isRgb: () => isRgb,
multiplyOpacity: () => multiplyOpacity,
multiplySaturation: () => multiplySaturation,
oklchToColorJs: () => oklchToColorJs,
parseRgbObject: () => parseRgbObject,
randomHue: () => randomHue,
resolveCss: () => resolveCss,
rgbToColorJs: () => rgbToColorJs,
scale: () => scale3,
structuredToColorJs: () => structuredToColorJs,
structuredToColorJsConstructor: () => structuredToColorJsConstructor,
toHex: () => toHex,
toHsl: () => toHsl,
toRgb: () => toRgb,
toRgb8bit: () => toRgb8bit,
toRgbRelative: () => toRgbRelative,
toString: () => toString,
toStringFirst: () => toStringFirst
});
// src/visual/colour/Generate.ts
var goldenAngleColour = (index, saturation = 0.5, lightness = 0.75, alpha = 1) => {
throwNumberTest(index, `positive`, `index`);
throwNumberTest(saturation, `percentage`, `saturation`);
throwNumberTest(lightness, `percentage`, `lightness`);
throwNumberTest(alpha, `percentage`, `alpha`);
const hue = index * 137.508;
return alpha === 1 ? `hsl(${hue},${saturation * 100}%,${lightness * 100}%)` : `hsl(${hue},${saturation * 100}%,${lightness * 100}%,${alpha * 100}%)`;
};
var randomHue = (rand = defaultRandom) => {
const r = rand();
return r * 360;
};
// node_modules/colorjs.io/dist/color.js
function multiplyMatrices(A, B) {
let m3 = A.length;
if (!Array.isArray(A[0])) {
A = [A];
}
if (!Array.isArray(B[0])) {
B = B.map((x) => [x]);
}
let p2 = B[0].length;
let B_cols = B[0].map((_, i) => B.map((x) => x[i]));
let product = A.map((row) => B_cols.map((col) => {
let ret = 0;
if (!Array.isArray(row)) {
for (let c4 of col) {
ret += row * c4;
}
return ret;
}
for (let i = 0; i < row.length; i++) {
ret += row[i] * (col[i] || 0);
}
return ret;
}));
if (m3 === 1) {
product = product[0];
}
if (p2 === 1) {
return product.map((x) => x[0]);
}
return product;
}
function isString(str) {
return type(str) === "string";
}
function type(o) {
let str = Object.prototype.toString.call(o);
return (str.match(/^\[object\s+(.*?)\]$/)[1] || "").toLowerCase();
}
function serializeNumber(n2, { precision, unit }) {
if (isNone(n2)) {
return "none";
}
return toPrecision(n2, precision) + (unit ?? "");
}
function isNone(n2) {
return Number.isNaN(n2) || n2 instanceof Number && n2?.none;
}
function skipNone(n2) {
return isNone(n2) ? 0 : n2;
}
function toPrecision(n2, precision) {
if (n2 === 0) {
return 0;
}
let integer = ~~n2;
let digits = 0;
if (integer && precision) {
digits = ~~Math.log10(Math.abs(integer)) + 1;
}
const multiplier = 10 ** (precision - digits);
return Math.floor(n2 * multiplier + 0.5) / multiplier;
}
var angleFactor = {
deg: 1,
grad: 0.9,
rad: 180 / Math.PI,
turn: 360
};
function parseFunction(str) {
if (!str) {
return;
}
str = str.trim();
const isFunctionRegex = /^([a-z]+)\((.+?)\)$/i;
const isNumberRegex = /^-?[\d.]+$/;
const unitValueRegex = /%|deg|g?rad|turn$/;
const singleArgument = /\/?\s*(none|[-\w.]+(?:%|deg|g?rad|turn)?)/g;
let parts = str.match(isFunctionRegex);
if (parts) {
let args = [];
parts[2].replace(singleArgument, ($0, rawArg) => {
let match = rawArg.match(unitValueRegex);
let arg = rawArg;
if (match) {
let unit = match[0];
let unitlessArg = arg.slice(0, -unit.length);
if (unit === "%") {
arg = new Number(unitlessArg / 100);
arg.type = "<percentage>";
} else {
arg = new Number(unitlessArg * angleFactor[unit]);
arg.type = "<angle>";
arg.unit = unit;
}
} else if (isNumberRegex.test(arg)) {
arg = new Number(arg);
arg.type = "<number>";
} else if (arg === "none") {
arg = new Number(NaN);
arg.none = true;
}
if ($0.startsWith("/")) {
arg = arg instanceof Number ? arg : new Number(arg);
arg.alpha = true;
}
if (typeof arg === "object" && arg instanceof Number) {
arg.raw = rawArg;
}
args.push(arg);
});
return {
name: parts[1].toLowerCase(),
rawName: parts[1],
rawArgs: parts[2],
// An argument could be (as of css-color-4):
// a number, percentage, degrees (hue), ident (in color())
args
};
}
}
function last(arr) {
return arr[arr.length - 1];
}
function interpolate(start, end, p2) {
if (isNaN(start)) {
return end;
}
if (isNaN(end)) {
return start;
}
return start + (end - start) * p2;
}
function interpolateInv(start, end, value) {
return (value - start) / (end - start);
}
function mapRange(from, to2, value) {
return interpolate(to2[0], to2[1], interpolateInv(from[0], from[1], value));
}
function parseCoordGrammar(coordGrammars) {
return coordGrammars.map((coordGrammar2) => {
return coordGrammar2.split("|").map((type2) => {
type2 = type2.trim();
let range2 = type2.match(/^(<[a-z]+>)\[(-?[.\d]+),\s*(-?[.\d]+)\]?$/);
if (range2) {
let ret = new String(range2[1]);
ret.range = [+range2[2], +range2[3]];
return ret;
}
return type2;
});
});
}
function clamp2(min, val, max2) {
return Math.max(Math.min(max2, val), min);
}
function copySign(to2, from) {
return Math.sign(to2) === Math.sign(from) ? to2 : -to2;
}
function spow(base, exp) {
return copySign(Math.abs(base) ** exp, base);
}
function zdiv(n2, d2) {
return d2 === 0 ? 0 : n2 / d2;
}
function bisectLeft(arr, value, lo = 0, hi = arr.length) {
while (lo < hi) {
const mid = lo + hi >> 1;
if (arr[mid] < value) {
lo = mid + 1;
} else {
hi = mid;
}
}
return lo;
}
var util = /* @__PURE__ */ Object.freeze({
__proto__: null,
bisectLeft,
clamp: clamp2,
copySign,
interpolate,
interpolateInv,
isNone,
isString,
last,
mapRange,
multiplyMatrices,
parseCoordGrammar,
parseFunction,
serializeNumber,
skipNone,
spow,
toPrecision,
type,
zdiv
});
var Hooks = class {
add(name, callback, first) {
if (typeof arguments[0] != "string") {
for (var name in arguments[0]) {
this.add(name, arguments[0][name], arguments[1]);
}
return;
}
(Array.isArray(name) ? name : [name]).forEach(function(name2) {
this[name2] = this[name2] || [];
if (callback) {
this[name2][first ? "unshift" : "push"](callback);
}
}, this);
}
run(name, env) {
this[name] = this[name] || [];
this[name].forEach(function(callback) {
callback.call(env && env.context ? env.context : env, env);
});
}
};
var hooks = new Hooks();
var defaults = {
gamut_mapping: "css",
precision: 5,
deltaE: "76",
// Default deltaE method
verbose: globalThis?.process?.env?.NODE_ENV?.toLowerCase() !== "test",
warn: function warn(msg) {
if (this.verbose) {
globalThis?.console?.warn?.(msg);
}
}
};
var WHITES = {
// for compatibility, the four-digit chromaticity-derived ones everyone else uses
D50: [0.3457 / 0.3585, 1, (1 - 0.3457 - 0.3585) / 0.3585],
D65: [0.3127 / 0.329, 1, (1 - 0.3127 - 0.329) / 0.329]
};
function getWhite(name) {
if (Array.isArray(name)) {
return name;
}
return WHITES[name];
}
function adapt$2(W1, W2, XYZ, options = {}) {
W1 = getWhite(W1);
W2 = getWhite(W2);
if (!W1 || !W2) {
throw new TypeError(`Missing white point to convert ${!W1 ? "from" : ""}${!W1 && !W2 ? "/" : ""}${!W2 ? "to" : ""}`);
}
if (W1 === W2) {
return XYZ;
}
let env = { W1, W2, XYZ, options };
hooks.run("chromatic-adaptation-start", env);
if (!env.M) {
if (env.W1 === WHITES.D65 && env.W2 === WHITES.D50) {
env.M = [
[1.0479297925449969, 0.022946870601609652, -0.05019226628920524],
[0.02962780877005599, 0.9904344267538799, -0.017073799063418826],
[-0.009243040646204504, 0.015055191490298152, 0.7518742814281371]
];
} else if (env.W1 === WHITES.D50 && env.W2 === WHITES.D65) {
env.M = [
[0.955473421488075, -0.02309845494876471, 0.06325924320057072],
[-0.0283697093338637, 1.0099953980813041, 0.021041441191917323],
[0.012314014864481998, -0.020507649298898964, 1.330365926242124]
];
}
}
hooks.run("chromatic-adaptation-end", env);
if (env.M) {
return multiplyMatrices(env.M, env.XYZ);
} else {
throw new TypeError("Only Bradford CAT with white points D50 and D65 supported for now.");
}
}
var noneTypes = /* @__PURE__ */ new Set(["<number>", "<percentage>", "<angle>"]);
function coerceCoords(space, format, name, coords) {
let types = Object.entries(space.coords).map(([id, coordMeta], i) => {
let coordGrammar2 = format.coordGrammar[i];
let arg = coords[i];
let providedType = arg?.type;
let type2;
if (arg.none) {
type2 = coordGrammar2.find((c4) => noneTypes.has(c4));
} else {
type2 = coordGrammar2.find((c4) => c4 == providedType);
}
if (!type2) {
let coordName = coordMeta.name || id;
throw new TypeError(`${providedType ?? arg.raw} not allowed for ${coordName} in ${name}()`);
}
let fromRange = type2.range;
if (providedType === "<percentage>") {
fromRange ||= [0, 1];
}
let toRange = coordMeta.range || coordMeta.refRange;
if (fromRange && toRange) {
coords[i] = mapRange(fromRange, toRange, coords[i]);
}
return type2;
});
return types;
}
function parse(str, { meta } = {}) {
let env = { "str": String(str)?.trim() };
hooks.run("parse-start", env);
if (env.color) {
return env.color;
}
env.parsed = parseFunction(env.str);
if (env.parsed) {
let name = env.parsed.name;
if (name === "color") {
let id = env.parsed.args.shift();
let alternateId = id.startsWith("--") ? id.substring(2) : `--${id}`;
let ids = [id, alternateId];
let alpha = env.parsed.rawArgs.indexOf("/") > 0 ? env.parsed.args.pop() : 1;
for (let space of ColorSpace.all) {
let colorSpec = space.getFormat("color");
if (colorSpec) {
if (ids.includes(colorSpec.id) || colorSpec.ids?.filter((specId) => ids.includes(specId)).length) {
const coords = Object.keys(space.coords).map((_, i) => env.parsed.args[i] || 0);
let types;
if (colorSpec.coordGrammar) {
types = coerceCoords(space, colorSpec, "color", coords);
}
if (meta) {
Object.assign(meta, { formatId: "color", types });
}
if (colorSpec.id.startsWith("--") && !id.startsWith("--")) {
defaults.warn(`${space.name} is a non-standard space and not currently supported in the CSS spec. Use prefixed color(${colorSpec.id}) instead of color(${id}).`);
}
if (id.startsWith("--") && !colorSpec.id.startsWith("--")) {
defaults.warn(`${space.name} is a standard space and supported in the CSS spec. Use color(${colorSpec.id}) instead of prefixed color(${id}).`);
}
return { spaceId: space.id, coords, alpha };
}
}
}
let didYouMean = "";
let registryId = id in ColorSpace.registry ? id : alternateId;
if (registryId in ColorSpace.registry) {
let cssId = ColorSpace.registry[registryId].formats?.color?.id;
if (cssId) {
didYouMean = `Did you mean color(${cssId})?`;
}
}
throw new TypeError(`Cannot parse color(${id}). ` + (didYouMean || "Missing a plugin?"));
} else {
for (let space of ColorSpace.all) {
let format = space.getFormat(name);
if (format && format.type === "function") {
let alpha = 1;
if (format.lastAlpha || last(env.parsed.args).alpha) {
alpha = env.parsed.args.pop();
}
let coords = env.parsed.args;
let types;
if (format.coordGrammar) {
types = coerceCoords(space, format, name, coords);
}
if (meta) {
Object.assign(meta, { formatId: format.name, types });
}
return {
spaceId: space.id,
coords,
alpha
};
}
}
}
} else {
for (let space of ColorSpace.all) {
for (let formatId in space.formats) {
let format = space.formats[formatId];
if (format.type !== "custom") {
continue;
}
if (format.test && !format.test(env.str)) {
continue;
}
let color = format.parse(env.str);
if (color) {
color.alpha ??= 1;
if (meta) {
meta.formatId = formatId;
}
return color;
}
}
}
}
throw new TypeError(`Could not parse ${str} as a color. Missing a plugin?`);
}
function getColor(color) {
if (Array.isArray(color)) {
return color.map(getColor);
}
if (!color) {
throw new TypeError("Empty color reference");
}
if (isString(color)) {
color = parse(color);
}
let space = color.space || color.spaceId;
if (!(space instanceof ColorSpace)) {
color.space = ColorSpace.get(space);
}
if (color.alpha === void 0) {
color.alpha = 1;
}
return color;
}
var \u03B5$7 = 75e-6;
var ColorSpace = class _ColorSpace {
constructor(options) {
this.id = options.id;
this.name = options.name;
this.base = options.base ? _ColorSpace.get(options.base) : null;
this.aliases = options.aliases;
if (this.base) {
this.fromBase = options.fromBase;
this.toBase = options.toBase;
}
let coords = options.coords ?? this.base.coords;
for (let name in coords) {
if (!("name" in coords[name])) {
coords[name].name = name;
}
}
this.coords = coords;
let white2 = options.white ?? this.base.white ?? "D65";
this.white = getWhite(white2);
this.formats = options.formats ?? {};
for (let name in this.formats) {
let format = this.formats[name];
format.type ||= "function";
format.name ||= name;
}
if (!this.formats.color?.id) {
this.formats.color = {
...this.formats.color ?? {},
id: options.cssId || this.id
};
}
if (options.gamutSpace) {
this.gamutSpace = options.gamutSpace === "self" ? this : _ColorSpace.get(options.gamutSpace);
} else {
if (this.isPolar) {
this.gamutSpace = this.base;
} else {
this.gamutSpace = this;
}
}
if (this.gamutSpace.isUnbounded) {
this.inGamut = (coords2, options2) => {
return true;
};
}
this.referred = options.referred;
Object.defineProperty(this, "path", {
value: getPath(this).reverse(),
writable: false,
enumerable: true,
configurable: true
});
hooks.run("colorspace-init-end", this);
}
inGamut(coords, { epsilon = \u03B5$7 } = {}) {
if (!this.equals(this.gamutSpace)) {
coords = this.to(this.gamutSpace, coords);
return this.gamutSpace.inGamut(coords, { epsilon });
}
let coordMeta = Object.values(this.coords);
return coords.every((c4, i) => {
let meta = coordMeta[i];
if (meta.type !== "angle" && meta.range) {
if (Number.isNaN(c4)) {
return true;
}
let [min, max2] = meta.range;
return (min === void 0 || c4 >= min - epsilon) && (max2 === void 0 || c4 <= max2 + epsilon);
}
return true;
});
}
get isUnbounded() {
return Object.values(this.coords).every((coord) => !("range" in coord));
}
get cssId() {
return this.formats?.color?.id || this.id;
}
get isPolar() {
for (let id in this.coords) {
if (this.coords[id].type === "angle") {
return true;
}
}
return false;
}
getFormat(format) {
if (typeof format === "object") {
format = processFormat(format, this);
return format;
}
let ret;
if (format === "default") {
ret = Object.values(this.formats)[0];
} else {
ret = this.formats[format];
}
if (ret) {
ret = processFormat(ret, this);
return ret;
}
return null;
}
/**
* Check if this color space is the same as another color space reference.
* Allows proxying color space objects and comparing color spaces with ids.
* @param {string | ColorSpace} space ColorSpace object or id to compare to
* @returns {boolean}
*/
equals(space) {
if (!space) {
return false;
}
return this === space || this.id === space || this.id === space.id;
}
to(space, coords) {
if (arguments.length === 1) {
const color = getColor(space);
[space, coords] = [color.space, color.coords];
}
space = _ColorSpace.get(space);
if (this.equals(space)) {
return coords;
}
coords = coords.map((c4) => Number.isNaN(c4) ? 0 : c4);
let myPath = this.path;
let otherPath = space.path;
let connectionSpace, connectionSpaceIndex;
for (let i = 0; i < myPath.length; i++) {
if (myPath[i].equals(otherPath[i])) {
connectionSpace = myPath[i];
connectionSpaceIndex = i;
} else {
break;
}
}
if (!connectionSpace) {
throw new Error(`Cannot convert between color spaces ${this} and ${space}: no connection space was found`);
}
for (let i = myPath.length - 1; i > connectionSpaceIndex; i--) {
coords = myPath[i].toBase(coords);
}
for (let i = connectionSpaceIndex + 1; i < otherPath.length; i++) {
coords = otherPath[i].fromBase(coords);
}
return coords;
}
from(space, coords) {
if (arguments.length === 1) {
const color = getColor(space);
[space, coords] = [color.space, color.coords];
}
space = _ColorSpace.get(space);
return space.to(this, coords);
}
toString() {
return `${this.name} (${this.id})`;
}
getMinCoords() {
let ret = [];
for (let id in this.coords) {
let meta = this.coords[id];
let range2 = meta.range || meta.refRange;
ret.push(range2?.min ?? 0);
}
return ret;
}
static registry = {};
// Returns array of unique color spaces
static get all() {
return [...new Set(Object.values(_ColorSpace.registry))];
}
static register(id, space) {
if (arguments.length === 1) {
space = arguments[0];
id = space.id;
}
space = this.get(space);
if (this.registry[id] && this.registry[id] !== space) {
throw new Error(`Duplicate color space registration: '${id}'`);
}
this.registry[id] = space;
if (arguments.length === 1 && space.aliases) {
for (let alias of space.aliases) {
this.register(alias, space);
}
}
return space;
}
/**
* Lookup ColorSpace object by name
* @param {ColorSpace | string} name
*/
static get(space, ...alternatives) {
if (!space || space instanceof _ColorSpace) {
return space;
}
let argType = type(space);
if (argType === "string") {
let ret = _ColorSpace.registry[space.toLowerCase()];
if (!ret) {
throw new TypeError(`No color space found with id = "${space}"`);
}
return ret;
}
if (alternatives.length) {
return _ColorSpace.get(...alternatives);
}
throw new TypeError(`${space} is not a valid color space`);
}
/**
* Get metadata about a coordinate of a color space
*
* @static
* @param {Array | string} ref
* @param {ColorSpace | string} [workingSpace]
* @return {Object}
*/
static resolveCoord(ref, workingSpace) {
let coordType = type(ref);
let space, coord;
if (coordType === "string") {
if (ref.includes(".")) {
[space, coord] = ref.split(".");
} else {
[space, coord] = [, ref];
}
} else if (Array.isArray(ref)) {
[space, coord] = ref;
} else {
space = ref.space;
coord = ref.coordId;
}
space = _ColorSpace.get(space);
if (!space) {
space = workingSpace;
}
if (!space) {
throw new TypeError(`Cannot resolve coordinate reference ${ref}: No color space specified and relative references are not allowed here`);
}
coordType = type(coord);
if (coordType === "number" || coordType === "string" && coord >= 0) {
let meta = Object.entries(space.coords)[coord];
if (meta) {
return { space, id: meta[0], index: coord, ...meta[1] };
}
}
space = _ColorSpace.get(space);
let normalizedCoord = coord.toLowerCase();
let i = 0;
for (let id in space.coords) {
let meta = space.coords[id];
if (id.toLowerCase() === normalizedCoord || meta.name?.toLowerCase() === normalizedCoord) {
return { space, id, index: i, ...meta };
}
i++;
}
throw new TypeError(`No "${coord}" coordinate found in ${space.name}. Its coordinates are: ${Object.keys(space.coords).join(", ")}`);
}
static DEFAULT_FORMAT = {
type: "functions",
name: "color"
};
};
function getPath(space) {
let ret = [space];
for (let s = space; s = s.base; ) {
ret.push(s);
}
return ret;
}
function processFormat(format, { coords } = {}) {
if (format.coords && !format.coordGrammar) {
format.type ||= "function";
format.name ||= "color";
format.coordGrammar = parseCoordGrammar(format.coords);
let coordFormats = Object.entries(coords).map(([id, coordMeta], i) => {
let outputType = format.coordGrammar[i][0];
let fromRange = coordMeta.range || coordMeta.refRange;
let toRange = outputType.range, suffix = "";
if (outputType == "<percentage>") {
toRange = [0, 100];
suffix = "%";
} else if (outputType == "<angle>") {
suffix = "deg";
}
return { fromRange, toRange, suffix };
});
format.serializeCoords = (coords2, precision) => {
return coords2.map((c4, i) => {
let { fromRange, toRange, suffix } = coordFormats[i];
if (fromRange && toRange) {
c4 = mapRange(fromRange, toRange, c4);
}
c4 = serializeNumber(c4, { precision, unit: suffix });
return c4;
});
};
}
return format;
}
var xyz_d65 = new ColorSpace({
id: "xyz-d65",
name: "XYZ D65",
coords: {
x: { name: "X" },
y: { name: "Y" },
z: { name: "Z" }
},
white: "D65",
formats: {
color: {
ids: ["xyz-d65", "xyz"]
}
},
aliases: ["xyz"]
});
var RGBColorSpace = class extends ColorSpace {
/**
* Creates a new RGB ColorSpace.
* If coords are not specified, they will use the default RGB coords.
* Instead of `fromBase()` and `toBase()` functions,
* you can specify to/from XYZ matrices and have `toBase()` and `fromBase()` automatically generated.
* @param {*} options - Same options as {@link ColorSpace} plus:
* @param {number[][]} options.toXYZ_M - Matrix to convert to XYZ
* @param {number[][]} options.fromXYZ_M - Matrix to convert from XYZ
*/
constructor(options) {
if (!options.coords) {
options.coords = {
r: {
range: [0, 1],
name: "Red"
},
g: {
range: [0, 1],
name: "Green"
},
b: {
range: [0, 1],
name: "Blue"
}
};
}
if (!options.base) {
options.base = xyz_d65;
}
if (options.toXYZ_M && options.fromXYZ_M) {
options.toBase ??= (rgb) => {
let xyz = multiplyMatrices(options.toXYZ_M, rgb);
if (this.white !== this.base.white) {
xyz = adapt$2(this.white, this.base.white, xyz);
}
return xyz;
};
options.fromBase ??= (xyz) => {
xyz = adapt$2(this.base.white, this.white, xyz);
return multiplyMatrices(options.fromXYZ_M, xyz);
};
}
options.referred ??= "display";
super(options);
}
};
function getAll(color, space) {
color = getColor(color);
if (!space || color.space.equals(space)) {
return color.coords.slice();
}
space = ColorSpace.get(space);
return space.from(color);
}
function get(color, prop) {
color = getColor(color);
let { space, index } = ColorSpace.resolveCoord(prop, color.space);
let coords = getAll(color, space);
return coords[index];
}
function setAll(color, space, coords) {
color = getColor(color);
space = ColorSpace.get(space);
color.coords = space.to(color.space, coords);
return color;
}
setAll.returns = "color";
function set(color, prop, value) {
color = getColor(color);
if (arguments.length === 2 && type(arguments[1]) === "object") {
let object = arguments[1];
for (let p2 in object) {
set(color, p2, object[p2]);
}
} else {
if (typeof value === "function") {
value = value(get(color, prop));
}
let { space, index } = ColorSpace.resolveCoord(prop, color.space);
let coords = getAll(color, space);
coords[index] = value;
setAll(color, space, coords);
}
return color;
}
set.returns = "color";
var XYZ_D50 = new ColorSpace({
id: "xyz-d50",
name: "XYZ D50",
white: "D50",
base: xyz_d65,
fromBase: (coords) => adapt$2(xyz_d65.white, "D50", coords),
toBase: (coords) => adapt$2("D50", xyz_d65.white, coords)
});
var \u03B5$6 = 216 / 24389;
var \u03B53$1 = 24 / 116;
var \u03BA$4 = 24389 / 27;
var white$4 = WHITES.D50;
var lab = new ColorSpace({
id: "lab",
name: "Lab",
coords: {
l: {
refRange: [0, 100],
name: "Lightness"
},
a: {
refRange: [-125, 125]
},
b: {
refRange: [-125, 125]
}
},
// Assuming XYZ is relative to D50, convert to CIE Lab
// from CIE standard, which now defines these as a rational fraction
white: white$4,
base: XYZ_D50,
// Convert D50-adapted XYX to Lab
// CIE 15.3:2004 section 8.2.1.1
fromBase(XYZ) {
let xyz = XYZ.map((value, i) => value / white$4[i]);
let f = xyz.map((value) => value > \u03B5$6 ? Math.cbrt(value) : (\u03BA$4 * value + 16) / 116);
return [
116 * f[1] - 16,
// L
500 * (f[0] - f[1]),
// a
200 * (f[1] - f[2])
// b
];
},
// Convert Lab to D50-adapted XYZ
// Same result as CIE 15.3:2004 Appendix D although the derivation is different
// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
toBase(Lab) {
let f = [];
f[1] = (Lab[0] + 16) / 116;
f[0] = Lab[1] / 500 + f[1];
f[2] = f[1] - Lab[2] / 200;
let xyz = [
f[0] > \u03B53$1 ? Math.pow(f[0], 3) : (116 * f[0] - 16) / \u03BA$4,
Lab[0] > 8 ? Math.pow((Lab[0] + 16) / 116, 3) : Lab[0] / \u03BA$4,
f[2] > \u03B53$1 ? Math.pow(f[2], 3) : (116 * f[2] - 16) / \u03BA$4
];
return xyz.map((value, i) => value * white$4[i]);
},
formats: {
"lab": {
coords: ["<number> | <percentage>", "<number> | <percentage>[-1,1]", "<number> | <percentage>[-1,1]"]
}
}
});
function constrain(angle) {
return (angle % 360 + 360) % 360;
}
function adjust(arc, angles) {
if (arc === "raw") {
return angles;
}
let [a1, a2] = angles.map(constrain);
let angleDiff = a2 - a1;
if (arc === "increasing") {
if (angleDiff < 0) {
a2 += 360;
}
} else if (arc === "decreasing") {
if (angleDiff > 0) {
a1 += 360;
}
} else if (arc === "longer") {
if (-180 < angleDiff && angleDiff < 180) {
if (angleDiff > 0) {
a1 += 360;
} else {
a2 += 360;
}
}
} else if (arc === "shorter") {
if (angleDiff > 180) {
a1 += 360;
} else if (angleDiff < -180) {
a2 += 360;
}
}
return [a1, a2];
}
var lch = new ColorSpace({
id: "lch",
name: "LCH",
coords: {
l: {
refRange: [0, 100],
name: "Lightness"
},
c: {
refRange: [0, 150],
name: "Chroma"
},
h: {
refRange: [0, 360],
type: "angle",
name: "Hue"
}
},
base: lab,
fromBase(Lab) {
let [L, a2, b2] = Lab;
let hue;
const \u03B52 = 0.02;
if (Math.abs(a2) < \u03B52 && Math.abs(b2) < \u03B52) {
hue = NaN;
} else {
hue = Math.atan2(b2, a2) * 180 / Math.PI;
}
return [
L,
// L is still L
Math.sqrt(a2 ** 2 + b2 ** 2),
// Chroma
constrain(hue)
// Hue, in degrees [0 to 360)
];
},
toBase(LCH) {
let [Lightness, Chroma, Hue] = LCH;
if (Chroma < 0) {
Chroma = 0;
}
if (isNaN(Hue)) {
Hue = 0;
}
return [
Lightness,
// L is still L
Chroma * Math.cos(Hue * Math.PI / 180),
// a
Chroma * Math.sin(Hue * Math.PI / 180)
// b
];
},
formats: {
"lch": {
coords: ["<number> | <percentage>", "<number> | <percentage>", "<number> | <angle>"]
}
}
});
var Gfactor = 25 ** 7;
var \u03C0$1 = Math.PI;
var r2d = 180 / \u03C0$1;
var d2r$1 = \u03C0$1 / 180;
function pow7(x) {
const x2 = x * x;
const x7 = x2 * x2 * x2 * x;
return x7;
}
function deltaE2000(color, sample, { kL = 1, kC = 1, kH = 1 } = {}) {
[color, sample] = getColor([color, sample]);
let [L1, a1, b1] = lab.from(color);
let C1 = lch.from(lab, [L1, a1, b1])[1];
let [L2, a2, b2] = lab.from(sample);
let C2 = lch.from(lab, [L2, a2, b2])[1];
if (C1 < 0) {
C1 = 0;
}
if (C2 < 0) {
C2 = 0;
}
let Cbar = (C1 + C2) / 2;
let C7 = pow7(Cbar);
let G = 0.5 * (1 - Math.sqrt(C7 / (C7 + Gfactor)));
let adash1 = (1 + G) * a1;
let adash2 = (1 + G) * a2;
let Cdash1 = Math.sqrt(adash1 ** 2 + b1 ** 2);
let Cdash2 = Math.sqrt(adash2 ** 2 + b2 ** 2);
let h1 = adash1 === 0 && b1 === 0 ? 0 : Math.atan2(b1, adash1);
let h2 = adash2 === 0 && b2 === 0 ? 0 : Math.atan2(b2, adash2);
if (h1 < 0) {
h1 += 2 * \u03C0$1;
}
if (h2 < 0) {
h2 += 2 * \u03C0$1;
}
h1 *= r2d;
h2 *= r2d;
let \u0394L = L2 - L1;
let \u0394C = Cdash2 - Cdash1;
let hdiff = h2 - h1;
let hsum = h1 + h2;
let habs = Math.abs(hdiff);
let \u0394h;
if (Cdash1 * Cdash2 === 0) {
\u0394h = 0;
} else if (habs <= 180) {
\u0394h = hdiff;
} else if (hdiff > 180) {
\u0394h = hdiff - 360;
} else if (hdiff < -180) {
\u0394h = hdiff + 360;
} else {
defaults.warn("the unthinkable has happened");
}
let \u0394H = 2 * Math.sqrt(Cdash2 * Cdash1) * Math.sin(\u0394h * d2r$1 / 2);
let Ldash = (L1 + L2) / 2;
let Cdash = (Cdash1 + Cdash2) / 2;
let Cdash7 = pow7(Cdash);
let hdash;
if (Cdash1 * Cdash2 === 0) {
hdash = hsum;
} else if (habs <= 180) {
hdash = hsum / 2;
} else if (hsum < 360) {
hdash = (hsum + 360) / 2;
} else {
hdash = (hsum - 360) / 2;
}
let lsq = (Ldash - 50) ** 2;
let SL = 1 + 0.015 * lsq / Math.sqrt(20 + lsq);
let SC = 1 + 0.045 * Cdash;
let T = 1;
T -= 0.17 * Math.cos((hdash - 30) * d2r$1);
T += 0.24 * Math.cos(2 * hdash * d2r$1);
T += 0.32 * Math.cos((3 * hdash + 6) * d2r$1);
T -= 0.2 * Math.cos((4 * hdash - 63) * d2r$1);
let SH = 1 + 0.015 * Cdash * T;
let \u0394\u03B8 = 30 * Math.exp(-1 * ((hdash - 275) / 25) ** 2);
let RC = 2 * Math.sqrt(Cdash7 / (Cdash7 + Gfactor));
let RT = -1 * Math.sin(2 * \u0394\u03B8 * d2r$1) * RC;
let dE = (\u0394L / (kL * SL)) ** 2;
dE += (\u0394C / (kC * SC)) ** 2;
dE += (\u0394H / (kH * SH)) ** 2;
dE += RT * (\u0394C / (kC * SC)) * (\u0394H / (kH * SH));
return Math.sqrt(dE);
}
var XYZtoLMS_M$1 = [
[0.819022437996703, 0.3619062600528904, -0.1288737815209879],
[0.0329836539323885, 0.9292868615863434, 0.0361446663506424],
[0.0481771893596242, 0.2642395317527308, 0.6335478284694309]
];
var LMStoXYZ_M$1 = [
[1.2268798758459243, -0.5578149944602171, 0.2813910456659647],
[-0.0405757452148008, 1.112286803280317, -0.0717110580655164],
[-0.0763729366746601, -0.4214933324022432, 1.5869240198367816]
];
var LMStoLab_M = [
[0.210454268309314, 0.7936177747023054, -0.0040720430116193],
[1.9779985324311684, -2.42859224204858, 0.450593709617411],
[0.0259040424655478, 0.7827717124575296, -0.8086757549230774]
];
var LabtoLMS_M = [
[1, 0.3963377773761749, 0.2158037573099136],
[1, -0.1055613458156586, -0.0638541728258133],
[1, -0.0894841775298119, -1.2914855480194092]
];
var OKLab = new ColorSpace({
id: "oklab",
name: "Oklab",
coords: {
l: {
refRange: [0, 1],
name: "Lightness"
},
a: {
refRange: [-0.4, 0.4]
},
b: {
refRange: [-0.4, 0.4]
}
},
// Note that XYZ is relative to D65
white: "D65",
base: xyz_d65,
fromBase(XYZ) {
let LMS = multiplyMatrices(XYZtoLMS_M$1, XYZ);
let LMSg = LMS.map((val) => Math.cbrt(val));
return multiplyMatrices(LMStoLab_M, LMSg);
},
toBase(OKLab2) {
let LMSg = multiplyMatrices(LabtoLMS_M, OKLab2);
let LMS = LMSg.map((val) => val ** 3);
return multiplyMatrices(LMStoXYZ_M$1, LMS);
},
formats: {
"oklab": {
coords: ["<percentage> | <number>", "<number> | <percentage>[-1,1]", "<number> | <percentage>[-1,1]"]
}
}
});
function deltaEOK(color, sample) {
[color, sample] = getColor([color, sample]);
let [L1, a1, b1] = OKLab.from(color);
let [L2, a2, b2] = OKLab.from(sample);
let \u0394L = L1 - L2;
let \u0394a = a1 - a2;
let \u0394b = b1 - b2;
return Math.sqrt(\u0394L ** 2 + \u0394a ** 2 + \u0394b ** 2);
}
var \u03B5$5 = 75e-6;
function inGamut(color, space, { epsilon = \u03B5$5 } = {}) {
color = getColor(color);
if (!space) {
space = color.space;
}
space = ColorSpace.get(space);
let coords = color.coords;
if (space !== color.space) {
coords = space.from(color);
}
return space.inGamut(coords, { epsilon });
}
function clone(color) {
return {
space: color.space,
coords: color.coords.slice(),
alpha: color.alpha
};
}
function distance(color1, color2, space = "lab") {
space = ColorSpace.get(space);
let coords1 = space.from(color1);
let coords2 = space.from(color2);
return Math.sqrt(coords1.reduce((acc, c12, i) => {
let c22 = coords2[i];
if (isNaN(c12) || isNaN(c22)) {
return acc;
}
return acc + (c22 - c12) ** 2;
}, 0));
}
function deltaE76(color, sample) {
return distance(color, sample, "lab");
}
var \u03C0 = Math.PI;
var d2r = \u03C0 / 180;
function deltaECMC(color, sample, { l = 2, c: c4 = 1 } = {}) {
[color, sample] = getColor([color, sample]);
let [L1, a1, b1] = lab.from(color);
let [, C1, H1] = lch.from(lab, [L1, a1, b1]);
let [L2, a2, b2] = lab.from(sample);
let C2 = lch.from(lab, [L2, a2, b2])[1];
if (C1 < 0) {
C1 = 0;
}
if (C2 < 0) {
C2 = 0;
}
let \u0394L = L1 - L2;
let \u0394C = C1 - C2;
let \u0394a = a1 - a2;
let \u0394b = b1 - b2;
let H2 = \u0394a ** 2 + \u0394b ** 2 - \u0394C ** 2;
let SL = 0.511;
if (L1 >= 16) {
SL = 0.040975 * L1 / (1 + 0.01765 * L1);
}
let SC = 0.0638 * C1 / (1 + 0.0131 * C1) + 0.638;
let T;
if (Number.isNaN(H1)) {
H1 = 0;
}
if (H1 >= 164 && H1 <= 345) {
T = 0.56 + Math.abs(0.2 * Math.cos((H1 + 168) * d2r));
} else {
T = 0.36 + Math.abs(0.4 * Math.cos((H1 + 35) * d2r));
}
let C4 = Math.pow(C1, 4);
let F = Math.sqrt(C4 / (C4 + 1900));
let SH = SC * (F * T + 1 - F);
let dE = (\u0394L / (l * SL)) ** 2;
dE += (\u0394C / (c4 * SC)) ** 2;
dE += H2 / SH ** 2;
return Math.sqrt(dE);
}
var Yw$1 = 203;
var XYZ_Abs_D65 = new ColorSpace({
// Absolute CIE XYZ, with a D65 whitepoint,
// as used in most HDR colorspaces as a starting point.
// SDR spaces are converted per BT.2048
// so that diffuse, media white is 203 cd/m²
id: "xyz-abs-d65",
cssId: "--xyz-abs-d65",
name: "Absolute XYZ D65",
coords: {
x: {
refRange: [0, 9504.7],
name: "Xa"
},
y: {
refRange: [0, 1e4],
name: "Ya"
},
z: {
refRange: [0, 10888.3],
name: "Za"
}
},
base: xyz_d65,
fromBase(XYZ) {
return XYZ.map((v) => Math.max(v * Yw$1, 0));
},
toBase(AbsXYZ) {
return AbsXYZ.map((v) => Math.max(v / Yw$1, 0));
}
});
var b$1 = 1.15;
var g = 0.66;
var n$1 = 2610 / 2 ** 14;
var ninv$1 = 2 ** 14 / 2610;
var c1$2 = 3424 / 2 ** 12;
var c2$2 = 2413 / 2 ** 7;
var c3$2 = 2392 / 2 ** 7;
var p = 1.7 * 2523 / 2 ** 5;
var pinv = 2 ** 5 / (1.7 * 2523);
var d = -0.56;
var d0 = 16295499532821565e-27;
var XYZtoCone_M = [
[0.41478972, 0.579999, 0.014648],
[-0.20151, 1.120649, 0.0531008],
[-0.0166008, 0.2648, 0.6684799]
];
var ConetoXYZ_M = [
[1.9242264357876067, -1.0047923125953657, 0.037651404030618],
[0.35031676209499907, 0.7264811939316552, -0.06538442294808501],
[-0.09098281098284752, -0.3127282905230739, 1.5227665613052603]
];
var ConetoIab_M = [
[0.5, 0.5, 0],
[3.524, -4.066708, 0.542708],
[0.199076, 1.096799, -1.295875]
];
var IabtoCone_M = [
[1, 0.1386050432715393, 0.05804731615611886],
[0.9999999999999999, -0.1386050432715393, -0.05804731615611886],
[0.9999999999999998, -0.09601924202631895, -0.8118918960560388]
];
var Jzazbz = new ColorSpace({
id: "jzazbz",
name: "Jzazbz",
coords: {
jz: {
refRange: [0, 1],
name: "Jz"
},
az: {
refRange: [-0.5, 0.5]
},
bz: {
refRange: [-0.5, 0.5]
}
},
base: XYZ_Abs_D65,
fromBase(XYZ) {
let [Xa, Ya, Za] = XYZ;
let Xm = b$1 * Xa - (b$1 - 1) * Za;
let Ym = g * Ya - (g - 1) * Xa;
let LMS = multiplyMatrices(XYZtoCone_M, [Xm, Ym, Za]);
let PQLMS = LMS.map(function(val) {
let num = c1$2 + c2$2 * (val / 1e4) ** n$1;
let denom = 1 + c3$2 * (val / 1e4) ** n$1;
return (num / denom) ** p;
});
let [Iz, az, bz] = multiplyMatrices(ConetoIab_M, PQLMS);
let Jz = (1 + d) * Iz / (1 + d * Iz) - d0;
return [Jz, az, bz];
},
toBase(Jzazbz2) {
let [Jz, az, bz] = Jzazbz2;
let Iz = (Jz + d0) / (1 + d - d * (Jz + d0));
let PQLMS = multiplyMatrices(IabtoCone_M, [Iz, az, bz]);
let LMS = PQLMS.map(function(val) {
let num = c1$2 - val ** pinv;
let denom = c3$2 * val ** pinv - c2$2;
let x = 1e4 * (num / denom) ** ninv$1;
return x;
});
let [Xm, Ym, Za] = multiplyMatrices(ConetoXYZ_M, LMS);
let Xa = (Xm + (b$1 - 1) * Za) / b$1;
let Ya = (Ym + (g - 1) * Xa) / g;
return [Xa, Ya, Za];
},
formats: {
// https://drafts.csswg.org/css-color-hdr/#Jzazbz
"color": {
coords: ["<number> | <percentage>", "<number> | <percentage>[-1,1]", "<number> | <percentage>[-1,1]"]
}
}
});
var jzczhz = new ColorSpace({
id: "jzczhz",
name: "JzCzHz",
coords: {
jz: {
refRange: [0, 1],
name: "Jz"
},
cz: {
refRange: [0, 1],
name: "Chroma"
},
hz: {
refRange: [0, 360],
type: "angle",
name: "Hue"
}
},
base: Jzazbz,
fromBase(jzazbz) {
let [Jz, az, bz] = jzazbz;
let hue;
const \u03B52 = 2e-4;
if (Math.abs(az) < \u03B52 && Math.abs(bz) < \u03B52) {
hue = NaN;
} else {
hue = Math.atan2(bz, az) * 180 / Math.PI;
}
return [
Jz,
// Jz is still Jz
Math.sqrt(az ** 2 + bz ** 2),
// Chroma
constrain(hue)
// Hue, in degrees [0 to 360)
];
},
toBase(jzczhz2) {
return [
jzczhz2[0],
// Jz is still Jz
jzczhz2[1] * Math.cos(jzczhz2[2] * Math.PI / 180),
// az
jzczhz2[1] * Math.sin(jzczhz2[2] * Math.PI / 180)
// bz
];
}
});
function deltaEJz(color, sample) {
[color, sample] = getColor([color, sample]);
let [Jz1, Cz1, Hz1] = jzczhz.from(color);
let [Jz2, Cz2, Hz2] = jzczhz.from(sample);
let \u0394J = Jz1 - Jz2;
let \u0394C = Cz1 - Cz2;
if (Number.isNaN(Hz1) && Number.isNaN(Hz2)) {
Hz1 = 0;
Hz2 = 0;
} else if (Number.isNaN(Hz1)) {
Hz1 = Hz2;
} else if (Number.isNaN(Hz2)) {
Hz2 = Hz1;
}
let \u0394h = Hz1 - Hz2;
let \u0394H = 2 * Math.sqrt(Cz1 * Cz2) * Math.sin(\u0394h / 2 * (Math.PI / 180));
return Math.sqrt(\u0394J ** 2 + \u0394C ** 2 + \u0394H ** 2);
}
var c1$1 = 3424 / 4096;
var c2$1 = 2413 / 128;
var c3$1 = 2392 / 128;
var m1$1 = 2610 / 16384;
var m2 = 2523 / 32;
var im1 = 16384 / 2610;
var im2 = 32 / 2523;
var XYZtoLMS_M = [
[0.3592832590121217, 0.6976051147779502, -0.035891593232029],
[-0.1920808463704993, 1.100476797037432, 0.0753748658519118],
[0.0070797844607479, 0.0748396662186362, 0.8433265453898765]
];
var LMStoIPT_M = [
[2048 / 4096, 2048 / 4096, 0],
[6610 / 4096, -13613 / 4096, 7003 / 4096],
[17933 / 4096, -17390 / 4096, -543 / 4096]
];
var IPTtoLMS_M = [
[0.9999999999999998, 0.0086090370379328, 0.111029625003026],
[0.9999999999999998, -0.0086090370379328, -0.1110296250030259],
[0.9999999999999998, 0.5600313357106791, -0.3206271749873188]
];
var LMStoXYZ_M = [
[2.0701522183894223, -1.3263473389671563, 0.2066510476294053],
[0.3647385209748072, 0.6805660249472273, -0.0453045459220347],
[-0.0497472075358123, -0.0492609666966131, 1.1880659249923042]
];
var ictcp = new ColorSpace({
id: "ictcp",
name: "ICTCP",
// From BT.2100-2 page 7:
// During production, signal values are expected to exceed the
// range E′ = [0.0 : 1.0]. This provides processing headroom and avoids
// signal degradation during cascaded processing. Such values of E′,
// below 0.0 or exceeding 1.0, should not be clipped during production
// and exchange.
// Values below 0.0 should not be clipped in reference displays (even
// though they represent “negative” light) to allow the black level of
// the signal (LB) to be properly set using test signals known as “PLUGE”
coords: {
i: {
refRange: [0, 1],
// Constant luminance,
name: "I"
},
ct: {
refRange: [-0.5, 0.5],
// Full BT.2020 gamut in range [-0.5, 0.5]
name: "CT"
},
cp: {
refRange: [-0.5, 0.5],
name: "CP"
}
},
base: XYZ_Abs_D65,
fromBase(XYZ) {
let LMS = multiplyMatrices(XYZtoLMS_M, XYZ);
return LMStoICtCp(LMS);
},
toBase(ICtCp) {
let LMS = ICtCptoLMS(ICtCp);
return multiplyMatrices(LMStoXYZ_M, LMS);
}
});
function LMStoICtCp(LMS) {
let PQLMS = LMS.map(function(val) {
let num = c1$1 + c2$1 * (val / 1e4) ** m1$1;
let denom = 1 + c3$1 * (val / 1e4) ** m1$1;
return (num / denom) ** m2;
});
return multiplyMatrices(LMStoIPT_M, PQLMS);
}
function ICtCptoLMS(ICtCp) {
let PQLMS = multiplyMatrices(IPTtoLMS_M, ICtCp);
let LMS = PQLMS.map(function(val) {
let num = Math.max(val ** im2 - c1$1, 0);
let denom = c2$1 - c3$1 * val ** im2;
return 1e4 * (num / denom) ** im1;
});
return LMS;
}
function deltaEITP(color, sample) {
[color, sample] = getColor([color, sample]);
let [I1, T1, P1] = ictcp.from(color);
let [I2, T2, P2] = ictcp.from(sample);
return 720 * Math.sqrt((I1 - I2) ** 2 + 0.25 * (T1 - T2) ** 2 + (P1 - P2) ** 2);
}
var white$3 = WHITES.D65;
var adaptedCoef = 0.42;
var adaptedCoefInv = 1 / adaptedCoef;
var tau = 2 * Math.PI;
var cat16 = [
[0.401288, 0.650173, -0.051461],
[-0.250268, 1.204414, 0.045854],
[-2079e-6, 0.048952, 0.953127]
];
var cat16Inv = [
[1.8620678550872327, -1.0112546305316843, 0.14918677544445175],
[0.38752654323613717, 0.6214474419314753, -0.008973985167612518],
[-0.015841498849333856, -0.03412293802851557, 1.0499644368778496]
];
var m1 = [
[460, 451, 288],
[460, -891, -261],
[460, -220, -6300]
];
var surroundMap = {
dark: [0.8, 0.525, 0.8],
dim: [0.9, 0.59, 0.9],
average: [1, 0.69, 1]
};
var hueQuadMap = {
// Red, Yellow, Green, Blue, Red
h: [20.14, 90, 164.25, 237.53, 380.14],
e: [0.8, 0.7, 1, 1.2, 0.8],
H: [0, 100, 200, 300, 400]
};
var rad2deg = 180 / Math.PI;
var deg2rad$1 = Math.PI / 180;
function adapt$1(coords, fl) {
const temp = coords.map((c4) => {
const x = spow(fl * Math.abs(c4) * 0.01, adaptedCoef);
return 400 * copySign(x, c4) / (x + 27.13);
});
return temp;
}
function unadapt(adapted, fl) {
const constant = 100 / fl * 27.13 ** adaptedCoefInv;
return adapted.map((c4) => {
const cabs = Math.abs(c4);
return copySign(constant * spow(cabs / (400 - cabs), adaptedCoefInv), c4);
});
}
function hueQuadrature(h) {
let hp = constrain(h);
if (hp <= hueQuadMap.h[0]) {
hp += 360;
}
const i = bisectLeft(hueQuadMap.h, hp) - 1;
const [hi, hii] = hueQuadMap.h.slice(i, i + 2);
const [ei, eii] = hueQuadMap.e.slice(i, i + 2);
const Hi = hueQuadMap.H[i];
const t = (hp - hi) / ei;
return Hi + 100 * t / (t + (hii - hp) / eii);
}
function invHueQuadrature(H) {
let Hp = (H % 400 + 400) % 400;
const i = Math.floor(0.01 * Hp);
Hp = Hp % 100;
const [hi, hii] = hueQuadMap.h.slice(i, i + 2);
const [ei, eii] = hueQuadMap.e.slice(i, i + 2);
return constrain(
(Hp * (eii * hi - ei * hii) - 100 * hi * eii) / (Hp * (eii - ei) - 100 * eii)
);
}
function environment(refWhite, adaptingLuminance, backgroundLuminance, surround, discounting) {
const env = {};
env.discounting = discounting;
env.refWhite = refWhite;
env.surround = surround;
const xyzW = refWhite.map((c4) => {
return c4 * 100;
});
env.la = adaptingLuminance;
env.yb = backgroundLuminance;
const yw = xyzW[1];
const rgbW = multiplyMatrices(cat16, xyzW);
surround = surroundMap[env.surround];
const f = surround[0];
env.c = surround[1];
env.nc = surround[2];
const k = 1 / (5 * env.la + 1);
const k4 = k ** 4;
env.fl = k4 * env.la + 0.1 * (1 - k4) * (1 - k4) * Math.cbrt(5 * env.la);
env.flRoot = env.fl ** 0.25;
env.n = env.yb / yw;
env.z = 1.48 + Math.sqrt(env.n);
env.nbb = 0.725 * env.n ** -0.2;
env.ncb = env.nbb;
const d2 = discounting ? 1 : Math.max(
Math.min(f * (1 - 1 / 3.6 * Math.exp((-env.la - 42) / 92)), 1),
0
);
env.dRgb = rgbW.map((c4) => {
return interpolate(1, yw / c4, d2);
});
env.dRgbInv = env.dRgb.map((c4) => {
return 1 / c4;
});
const rgbCW = rgbW.map((c4, i) => {
return c4 * env.dRgb[i];
});
const rgbAW = adapt$1(rgbCW, env.fl);
env.aW = env.nbb * (2 * rgbAW[0] + rgbAW[1] + 0.05 * rgbAW[2]);
return env;
}
var viewingConditions$1 = environment(
white$3,
64 / Math.PI * 0.2,
20,
"average",
false
);
function fromCam16(cam162, env) {
if (!(cam162.J !== void 0 ^ cam162.Q !== void 0)) {
throw new Error("Conversion requires one and only one: 'J' or 'Q'");
}
if (!(cam162.C !== void 0 ^ cam162.M !== void 0 ^ cam162.s !== void 0)) {
throw new Error("Conversion requires one and only one: 'C', 'M' or 's'");
}
if (!(cam162.h !== void 0 ^ cam162.H !== void 0)) {
throw new Error("Conversion requires one and only one: 'h' or 'H'");
}
if (cam162.J === 0 || cam162.Q === 0) {
return [0, 0, 0];
}
let hRad = 0;
if (cam162.h !== void 0) {
hRad = constrain(cam162.h) * deg2rad$1;
} else {
hRad = invHueQuadrature(cam162.H) * deg2rad$1;
}
const cosh = Math.cos(hRad);
const sinh = Math.sin(hRad);
let Jroot = 0;
if (cam162.J !== void 0) {
Jroot = spow(cam162.J, 1 / 2) * 0.1;
} else if (cam162.Q !== void 0) {
Jroot = 0.25 * env.c * cam162.Q / ((env.aW + 4) * env.flRoot);
}
let alpha = 0;
if (cam162.C !== void 0) {
alpha = cam162.C / Jroot;
} else if (cam162.M !== void 0) {
alpha = cam162.M / env.flRoot / Jroot;
} else if (cam162.s !== void 0) {
alpha = 4e-4 * cam162.s ** 2 * (env.aW + 4) / env.c;
}
const t = spow(
alpha * Math.pow(1.64 - Math.pow(0.29, env.n), -0.73),
10 / 9
);
const et = 0.25 * (Math.cos(hRad + 2) + 3.8);
const A = env.aW * spow(Jroot, 2 / env.c / env.z);
const p1 = 5e4 / 13 * env.nc * env.ncb * et;
const p2 = A / env.nbb;
const r = 23 * (p2 + 0.305) * zdiv(t, 23 * p1 + t * (11 * cosh + 108 * sinh));
const a2 = r * cosh;
const b2 = r * sinh;
const rgb_c = unadapt(
multiplyMatrices(m1, [p2, a2, b2]).map((c4) => {
return c4 * 1 / 1403;
}),
env.fl
);
return multiplyMatrices(
cat16Inv,
rgb_c.map((c4, i) => {
return c4 * env.dRgbInv[i];
})
).map((c4) => {
return c4 / 100;
});
}
function toCam16(xyzd65, env) {
const xyz100 = xyzd65.map((c4) => {
return c4 * 100;
});
const rgbA = adapt$1(
multiplyMatrices(cat16, xyz100).map((c4, i) => {
return c4 * env.dRgb[i];
}),
env.fl
);
const a2 = rgbA[0] + (-12 * rgbA[1] + rgbA[2]) / 11;
const b2 = (rgbA[0] + rgbA[1] - 2 * rgbA[2]) / 9;
const hRad = (Math.atan2(b2, a2) % tau + tau) % tau;
const et = 0.25 * (Math.cos(hRad + 2) + 3.8);
const t = 5e4 / 13 * env.nc * env.ncb * zdiv(
et * Math.sqrt(a2 ** 2 + b2 ** 2),
rgbA[0] + rgbA[1] + 1.05 * rgbA[2] + 0.305
);
const alpha = spow(t, 0.9) * Math.pow(1.64 - Math.pow(0.29, env.n), 0.73);
const A = env.nbb * (2 * rgbA[0] + rgbA[1] + 0.05 * rgbA[2]);
const Jroot = spow(A / env.aW, 0.5 * env.c * env.z);
const J = 100 * spow(Jroot, 2);
const Q = 4 / env.c * Jroot * (env.aW + 4) * env.flRoot;
const C = alpha * Jroot;
const M = C * env.flRoot;
const h = constrain(hRad * rad2deg);
const H = hueQuadrature(h);
const s = 50 * spow(env.c * alpha / (env.aW + 4), 1 / 2);
return { J, C, h, s, Q, M, H };
}
var cam16 = new ColorSpace({
id: "cam16-jmh",
cssId: "--cam16-jmh",
name: "CAM16-JMh",
coords: {
j: {
refRange: [0, 100],
name: "J"
},
m: {
refRange: [0, 105],
name: "Colorfulness"
},
h: {
refRange: [0, 360],
type: "angle",
name: