@thi.ng/color
Version:
Array-based color types, CSS parsing, conversions, transformations, declarative theme generation, gradients, presets
119 lines (118 loc) • 4.05 kB
JavaScript
import { atan2Abs, cossin } from "@thi.ng/math/angle";
import { DEG2RAD, PI, SIXTH_PI, TAU, THIRD_PI } from "@thi.ng/math/api";
import { ONE3 } from "@thi.ng/vectors/api";
import { dist3, dist4 } from "@thi.ng/vectors/dist";
import { labD50 } from "./lab/lab50.js";
import { labD65 } from "./lab/lab65.js";
import { luminanceRgb, luminanceSrgb } from "./luminance-rgb.js";
const { abs, cos, hypot, sin, sqrt } = Math;
const distChannel = (id) => (a, b) => abs(a[id] - b[id]);
const distHsv = (a, b) => {
const aa = cossin(a[0] * TAU, a[1]);
const bb = cossin(b[0] * TAU, b[1]);
return hypot(aa[0] - bb[0], aa[1] - bb[1], a[2] - b[2]);
};
const distLch = ({ 0: la, 1: ca, 2: ha }, { 0: lb, 1: cb, 2: hb }) => Math.sqrt(
ca * ca + cb * cb - 2 * ca * cb * Math.cos((ha - hb) * TAU) + (la - lb) ** 2
);
const distHsvSat = distChannel(1);
const distHsvLuma = distChannel(2);
const distEucledian3 = dist3;
const distEucledian4 = dist4;
const distRgbLuma = (a, b) => abs(luminanceRgb(a) - luminanceRgb(b));
const distSrgbLuma = (a, b) => abs(luminanceSrgb(a) - luminanceSrgb(b));
const distRgbRed = distChannel(0);
const distRgbGreen = distChannel(1);
const distRgbBlue = distChannel(1);
const H6 = 6 * DEG2RAD;
const H25 = 25 * DEG2RAD;
const H63 = 63 * DEG2RAD;
const H275 = 275 * DEG2RAD;
const distCIEDE2000 = (weights = ONE3) => (a, b) => {
let { 0: l1, 1: a1, 2: b1 } = labD50(a);
let { 0: l2, 1: a2, 2: b2 } = labD50(b);
l1 *= 100;
a1 *= 100;
b1 *= 100;
l2 *= 100;
a2 *= 100;
b2 *= 100;
const c1ab = hypot(a1, b1);
const c2ab = hypot(a2, b2);
const cab = (c1ab + c2ab) * 0.5;
const g = 1 + 0.5 * (1 - __c7Coeff(cab));
a1 *= g;
a2 *= g;
const c1 = hypot(a1, b1);
const c2 = hypot(a2, b2);
const cmean = (c1 + c2) * 0.5;
const { deltaH, H } = __computeDeltaH(a1, b1, a2, b2, c1, c2);
const T = 1 - 0.17 * cos(H - SIXTH_PI) + 0.24 * cos(2 * H) + 0.32 * cos(3 * H + H6) - 0.2 * cos(4 * H - H63);
const Rt = -2 * __c7Coeff(cmean) * sin(THIRD_PI * Math.exp(-(((H - H275) / H25) ** 2)));
const L50 = ((l1 + l2) * 0.5 - 50) ** 2;
const Sl = 1 + 0.015 * L50 / sqrt(20 + L50);
const Sc = 1 + 0.045 * cmean;
const Sh = 1 + 0.015 * cmean * T;
const termL = (l2 - l1) / (weights[0] * Sl);
const termC = (c2 - c1) / (weights[1] * Sc);
const termH = deltaH / (weights[2] * Sh);
return sqrt(termL ** 2 + termC ** 2 + termH ** 2 + Rt * termC * termH);
};
const __c7Coeff = (c) => {
c = c ** 7;
return sqrt(c / (c + 25 ** 7));
};
const __computeDeltaH = (a1, b1, a2, b2, c1, c2, eps = 1e-3) => {
const h1 = atan2Abs(b1, a1);
const h2 = atan2Abs(b2, a2);
if (c1 <= eps || c2 <= eps) return { deltaH: 0, H: h1 + h2 };
let dh = h2 - h1;
const sumH = h1 + h2;
const absH = abs(dh);
dh = absH <= PI ? dh : h2 <= h1 ? dh + TAU : dh - TAU;
const deltaH = 2 * sqrt(c1 * c2) * sin(dh / 2);
const H = 0.5 * (absH <= PI ? sumH : sumH < TAU ? sumH + TAU : sumH - TAU);
return { deltaH, H };
};
const H35 = 35 * DEG2RAD;
const H164 = 164 * DEG2RAD;
const H168 = 168 * DEG2RAD;
const H345 = 345 * DEG2RAD;
const distCMC = (kl = 1, kc = 1) => (a, b) => {
let { 0: l1, 1: a1, 2: b1 } = labD65(a);
let { 0: l2, 1: a2, 2: b2 } = labD65(b);
l1 *= 100;
a1 *= 100;
b1 *= 100;
l2 *= 100;
a2 *= 100;
b2 *= 100;
const c1 = hypot(a1, b1);
const c2 = hypot(a2, b2);
const dC = c1 - c2;
const dH = sqrt((a2 - a1) ** 2 + (b2 - b1) ** 2 - dC ** 2);
const h1 = atan2Abs(b1, a1);
const t = h1 >= H164 && h1 <= H345 ? 0.56 + abs(0.2 * cos(h1 + H168)) : 0.36 + abs(0.4 * cos(h1 + H35));
const c14 = c1 ** 4;
const f = sqrt(c14 / (c14 + 1900));
const Sl = l1 >= 16 ? 0.040975 * l1 / (1 + 0.01765 * l1) : 0.511;
const Sc = 0.0638 * c1 / (1 + 0.0131 * c1) + 0.638;
const Sh = Sc * (f * t + 1 - f);
return hypot((l1 - l2) / (kl * Sl), dC / (kc * Sc), dH / Sh);
};
export {
distCIEDE2000,
distCMC,
distChannel,
distEucledian3,
distEucledian4,
distHsv,
distHsvLuma,
distHsvSat,
distLch,
distRgbBlue,
distRgbGreen,
distRgbLuma,
distRgbRed,
distSrgbLuma
};