chromonym 3.1.1 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE.md +243 -1
- package/README.md +51 -43
- package/dist/conversions/rgb.d.ts +7 -1
- package/dist/conversions/rgb.d.ts.map +1 -1
- package/dist/conversions/rgb.js +11 -2
- package/dist/conversions/rgb.js.map +1 -1
- package/dist/convert.d.ts +73 -21
- package/dist/convert.d.ts.map +1 -1
- package/dist/convert.js +13 -2
- package/dist/convert.js.map +1 -1
- package/dist/defineColorPalette.d.ts +53 -0
- package/dist/defineColorPalette.d.ts.map +1 -0
- package/dist/defineColorPalette.js +103 -0
- package/dist/defineColorPalette.js.map +1 -0
- package/dist/detectFormat.d.ts +26 -3
- package/dist/detectFormat.d.ts.map +1 -1
- package/dist/detectFormat.js +20 -0
- package/dist/detectFormat.js.map +1 -1
- package/dist/identify.d.ts +63 -17
- package/dist/identify.d.ts.map +1 -1
- package/dist/identify.js.map +1 -1
- package/dist/index.d.ts +13 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -3
- package/dist/index.js.map +1 -1
- package/dist/indexing.d.ts +13 -3
- package/dist/indexing.d.ts.map +1 -1
- package/dist/indexing.js +67 -19
- package/dist/indexing.js.map +1 -1
- package/dist/palettes/crayola.d.ts +16 -0
- package/dist/palettes/crayola.d.ts.map +1 -1
- package/dist/palettes/crayola.js +16 -0
- package/dist/palettes/crayola.js.map +1 -1
- package/dist/palettes/fs595b.d.ts +467 -0
- package/dist/palettes/fs595b.d.ts.map +1 -0
- package/dist/palettes/fs595b.js +256 -0
- package/dist/palettes/fs595b.js.map +1 -0
- package/dist/palettes/fs595c.d.ts +1224 -0
- package/dist/palettes/fs595c.d.ts.map +1 -0
- package/dist/palettes/fs595c.js +633 -0
- package/dist/palettes/fs595c.js.map +1 -0
- package/dist/palettes/isccNbs.d.ts +562 -0
- package/dist/palettes/isccNbs.d.ts.map +1 -0
- package/dist/palettes/isccNbs.js +300 -0
- package/dist/palettes/isccNbs.js.map +1 -0
- package/dist/palettes/nbs.d.ts +578 -0
- package/dist/palettes/nbs.d.ts.map +1 -0
- package/dist/palettes/nbs.js +309 -0
- package/dist/palettes/nbs.js.map +1 -0
- package/dist/palettes/ncs.d.ts +3949 -0
- package/dist/palettes/ncs.d.ts.map +1 -0
- package/dist/palettes/ncs.js +2011 -0
- package/dist/palettes/ncs.js.map +1 -0
- package/dist/palettes/ntc.d.ts +3170 -0
- package/dist/palettes/ntc.d.ts.map +1 -0
- package/dist/palettes/ntc.js +1602 -0
- package/dist/palettes/ntc.js.map +1 -0
- package/dist/palettes/pantone.d.ts +18 -1
- package/dist/palettes/pantone.d.ts.map +1 -1
- package/dist/palettes/pantone.js +18 -1
- package/dist/palettes/pantone.js.map +1 -1
- package/dist/palettes/resene.d.ts +2802 -0
- package/dist/palettes/resene.d.ts.map +1 -0
- package/dist/palettes/resene.js +1422 -0
- package/dist/palettes/resene.js.map +1 -0
- package/dist/palettes/web.d.ts +10 -0
- package/dist/palettes/web.d.ts.map +1 -1
- package/dist/palettes/web.js +10 -0
- package/dist/palettes/web.js.map +1 -1
- package/dist/palettes/x11.d.ts +15 -10
- package/dist/palettes/x11.d.ts.map +1 -1
- package/dist/palettes/x11.js +15 -10
- package/dist/palettes/x11.js.map +1 -1
- package/dist/palettes/xkcd.d.ts +1880 -0
- package/dist/palettes/xkcd.d.ts.map +1 -0
- package/dist/palettes/xkcd.js +955 -0
- package/dist/palettes/xkcd.js.map +1 -0
- package/dist/resolve.d.ts +58 -14
- package/dist/resolve.d.ts.map +1 -1
- package/dist/resolve.js +32 -16
- package/dist/resolve.js.map +1 -1
- package/dist/types.d.ts +47 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +44 -5
package/dist/identify.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identify.js","sourceRoot":"","sources":["../src/identify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"identify.js","sourceRoot":"","sources":["../src/identify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAkExC;;;GAGG;AACH,SAAS,UAAU,CAAC,KAA0B,EAAE,MAAgB;IAC9D,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAmB,CAAC,CAAC;IACnD,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,KAAmB,EAAE,QAAQ,CAAC,CAAC;IACzE,IAAI,MAAM,KAAK,SAAS,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACtD,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QACpE,IAAI,SAAS,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACzC,MAAM,GAAG,GAAI,MAAM,CAAC,MAAmC,CAAC,SAAS,CAAC,CAAC;QACnE,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACnC,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAuDD,kBAAkB;AAClB,MAAM,UAAU,QAAQ,CACtB,KAA0B,EAC1B,IAAI,GAAqC,EAAE;IAE3C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,GAAG,CAAC;IACpC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,aAAa,CAAC;IACpD,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAE5C,IAAI,IAAI,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;QACzB,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QACtD,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACnB,MAAM,KAAK,GAAI,OAAO,CAAC,MAAmC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACnE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,KAAiB,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC1E,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC5C,OAAO,IAAI,IAAI,IAAI,CAAC;AACtB,CAAC","sourcesContent":["import { hexToRgba } from './conversions/hex.js';\nimport { toRgba } from './convert.js';\nimport { detectFormat } from './detectFormat.js';\nimport { getNameIndex, nearest, nearestAll } from './indexing.js';\nimport { web } from './palettes/web.js';\nimport type { ColorInput, DistanceMetric, HexColor, Palette, PaletteKey, Rgba } from './types.js';\n\n/**\n * One ranked nearest-match entry when `identify` is called with `k`.\n * `value` is the palette's stored hex for the match (useful for rendering\n * swatches). `distance` is in the selected metric's natural units: ΔE for\n * `deltaE*` metrics, channel-unit Euclidean for the others.\n */\nexport type IdentifyMatch<Name extends string> = {\n readonly name: Name;\n readonly value: HexColor;\n readonly distance: number;\n};\n\n/**\n * Options for {@link identify}. Generic over the target palette so the\n * return type narrows to its literal-key union.\n *\n * @example\n * identify('#ff0000', { palette: pantone, metric: 'deltaE2000' });\n * identify('rebeccapurple', { palette: x11, source: web });\n */\nexport type IdentifyOptions<P extends Palette = typeof web> = {\n /**\n * Target palette to search in. Defaults to `web` (CSS named colors).\n */\n readonly palette?: P;\n /**\n * Source palette for cross-palette name input: when `input` is a name\n * from a different palette (e.g. `'rebeccapurple'` when `palette` is\n * `pantone`), set `source` to the palette that owns the name so it can\n * be resolved to an Rgba before the nearest-match lookup.\n *\n * @remarks\n * The `input` argument isn't validated against `source`'s keys at\n * compile time; unknown names resolve to `null` at runtime.\n */\n readonly source?: Palette;\n /**\n * Override the distance metric. Defaults to `palette.defaultMetric`\n * (web/x11: `'deltaE76'`, pantone: `'deltaE2000'`, crayola: `'deltaEok'`).\n */\n readonly metric?: DistanceMetric;\n};\n\n/**\n * Options for the ranked top-k variant of {@link identify}. Returns an\n * array of {@link IdentifyMatch} entries instead of a single name.\n *\n * @example\n * identify('#ff0080', { palette: pantone, k: 3 });\n * // [\n * // { name: '219 C', value: '#da1884', distance: 2.1 },\n * // { name: '226 C', value: '#d0006f', distance: 3.9 },\n * // { name: 'Red 032 C',value: '#ef3340', distance: 5.4 },\n * // ]\n */\nexport type IdentifyRankedOptions<P extends Palette = typeof web> = IdentifyOptions<P> & {\n /**\n * Number of ranked matches to return. When present, `identify`\n * returns `IdentifyMatch[]` instead of a single key.\n */\n readonly k: number;\n};\n\n/**\n * Parse a string input that may be a color literal or a palette name.\n * Returns the canonical Rgba, or `null` if nothing matches.\n */\nfunction parseInput(input: ColorInput | string, source?: Palette): Rgba | null {\n const detected = detectFormat(input as ColorInput);\n if (detected !== 'UNKNOWN') return toRgba(input as ColorInput, detected);\n if (source !== undefined && typeof input === 'string') {\n const canonical = getNameIndex(source).get(source.normalize(input));\n if (canonical === undefined) return null;\n const hex = (source.colors as Record<string, HexColor>)[canonical];\n if (hex === undefined) return null;\n return hexToRgba(hex);\n }\n return null;\n}\n\n/**\n * Identify the nearest-named color for any color input (or palette name\n * from another palette via `source`).\n *\n * **Simple case — color in, name out:**\n * identify('#ff0000') → 'red'\n * identify('#ff0000', { palette: pantone }) → '172 C'\n *\n * **Cross-palette — name from one palette, nearest in another:**\n * identify('rebeccapurple', { palette: pantone, source: web }) → '267 C'\n * identify('Razzmatazz', { palette: pantone, source: crayola }) → '213 C'\n *\n * **Top-k ranked matches with ΔE distances:**\n * identify('#ff0000', { palette: pantone, k: 3 })\n * // [{ name: '172 C', value: '#fa4616', distance: 3.2 }, ...]\n *\n * The distance metric defaults to the target palette's `defaultMetric`\n * (web/x11: `deltaE76`, pantone: `deltaE2000`, crayola: `deltaEok`).\n * Override with `metric`.\n *\n * Tie-breaking: on exact-distance ties, the first-declared entry in\n * the palette's `colors` object wins. Deterministic across metrics\n * and across the single-match / top-k paths.\n *\n * `k` is forgiving: fractional values round, negatives and zero\n * return `[]`, and `NaN` / `±Infinity` return `[]` too. No throws on\n * invalid `k` — callers can safely pipe a slider value in without\n * pre-validating.\n *\n * Returns `null` (or `[]` when `k` is set) if the input can't be parsed.\n */\n\n// Input accepts `ColorInput | string`. The `string` widening is load-bearing\n// for two flows: (1) cross-palette name lookup (`identify(name, { source })`),\n// and (2) interactive callers passing untrusted user text; the library\n// returns `null` on unparseable input rather than throwing, so consumers can\n// feed it directly from a text field. Tightening to `ColorInput`-only would\n// break both patterns and force every interactive consumer to cast or\n// validate at every call site. Runtime safety is maintained by `parseInput`\n// returning `null` for anything it can't interpret.\n\n// Overload 1: no `k` — single name (or null). Unchanged legacy shape.\nexport function identify<P extends Palette = typeof web>(\n input: ColorInput | string,\n opts?: IdentifyOptions<P>,\n): PaletteKey<P> | null;\n\n// Overload 2: `k` present — ranked Match[] with distances.\nexport function identify<P extends Palette = typeof web>(\n input: ColorInput | string,\n opts: IdentifyRankedOptions<P>,\n): Array<IdentifyMatch<PaletteKey<P>>>;\n\n// Implementation.\nexport function identify(\n input: ColorInput | string,\n opts: IdentifyOptions & { k?: number } = {},\n): string | null | Array<IdentifyMatch<string>> {\n const palette = opts.palette ?? web;\n const metric = opts.metric ?? palette.defaultMetric;\n const rgba = parseInput(input, opts.source);\n\n if (opts.k !== undefined) {\n if (rgba === null) return [];\n const raw = nearestAll(rgba, palette, metric, opts.k);\n return raw.map((m) => {\n const value = (palette.colors as Record<string, HexColor>)[m.name];\n return { name: m.name, value: value as HexColor, distance: m.distance };\n });\n }\n\n if (rgba === null) return null;\n const name = nearest(rgba, palette, metric);\n return name || null;\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,14 +3,23 @@ export { hslToRgba, rgbaToHsl } from './conversions/hsl.js';
|
|
|
3
3
|
export { hsvToRgba, rgbaToHsv } from './conversions/hsv.js';
|
|
4
4
|
export { pantoneToRgba, rgbaToPantone } from './conversions/pantone.js';
|
|
5
5
|
export { rgbaToRgb, rgbToRgba } from './conversions/rgb.js';
|
|
6
|
-
export { convert, fromRgba, toRgba } from './convert.js';
|
|
6
|
+
export { type ConvertNameOptions, type ConvertPaletteOptions, type ConvertStructuralOptions, convert, fromRgba, toRgba, } from './convert.js';
|
|
7
|
+
export { type DefineColorPaletteInput, defineColorPalette, } from './defineColorPalette.js';
|
|
7
8
|
export { type DetectedFormat, detectFormat, isColor } from './detectFormat.js';
|
|
8
|
-
export { type IdentifyMatch, identify } from './identify.js';
|
|
9
|
+
export { type IdentifyMatch, type IdentifyOptions, type IdentifyRankedOptions, identify, } from './identify.js';
|
|
9
10
|
export { type CrayolaColorName, crayola } from './palettes/crayola.js';
|
|
11
|
+
export { type Fs595bColorName, fs595b } from './palettes/fs595b.js';
|
|
12
|
+
export { type Fs595cColorName, fs595c } from './palettes/fs595c.js';
|
|
13
|
+
export { type IsccNbsColorName, isccNbs } from './palettes/isccNbs.js';
|
|
14
|
+
export { type NbsColorName, nbs } from './palettes/nbs.js';
|
|
15
|
+
export { type NcsColorName, ncs } from './palettes/ncs.js';
|
|
16
|
+
export { type NtcColorName, ntc } from './palettes/ntc.js';
|
|
10
17
|
export { type PantoneColorName, pantone } from './palettes/pantone.js';
|
|
18
|
+
export { type ReseneColorName, resene } from './palettes/resene.js';
|
|
11
19
|
export { type WebColorName, web } from './palettes/web.js';
|
|
12
20
|
export { type X11ColorName, x11 } from './palettes/x11.js';
|
|
13
|
-
export { type
|
|
14
|
-
export
|
|
21
|
+
export { type XkcdColorName, xkcd } from './palettes/xkcd.js';
|
|
22
|
+
export { type ResolveFuzzyOptions, type ResolveMatch, type ResolveOptions, resolve, } from './resolve.js';
|
|
23
|
+
export type { ColorFormat, ColorInput, ColorValue, DistanceMetric, HexColor, HslInput, HslObject, HslString, HsvInput, HsvObject, HsvString, NormalizeFn, Palette, PaletteKey, Rgba, RgbaInput, RgbaString, RgbaTuple, RgbInput, RgbObject, RgbString, RgbTuple, } from './types.js';
|
|
15
24
|
export { COLOR_FORMATS } from './types.js';
|
|
16
25
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,EAC7B,OAAO,EACP,QAAQ,EACR,MAAM,GACP,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,KAAK,uBAAuB,EAC5B,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,KAAK,cAAc,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,QAAQ,GACT,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,KAAK,gBAAgB,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,KAAK,gBAAgB,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,KAAK,YAAY,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,KAAK,YAAY,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,KAAK,YAAY,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,KAAK,gBAAgB,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,KAAK,YAAY,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,KAAK,YAAY,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,KAAK,aAAa,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,OAAO,GACR,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,WAAW,EACX,UAAU,EACV,UAAU,EACV,cAAc,EACd,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EACT,WAAW,EACX,OAAO,EACP,UAAU,EACV,IAAI,EACJ,SAAS,EACT,UAAU,EACV,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EACT,QAAQ,GACT,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC","sourcesContent":["// Public API barrel — explicit named re-exports only (tree-shake friendly).\n// No default export; no side effects at module scope.\n\nexport { hexToRgba, rgbaToHex } from './conversions/hex.js';\nexport { hslToRgba, rgbaToHsl } from './conversions/hsl.js';\nexport { hsvToRgba, rgbaToHsv } from './conversions/hsv.js';\nexport { pantoneToRgba, rgbaToPantone } from './conversions/pantone.js';\nexport { rgbaToRgb, rgbToRgba } from './conversions/rgb.js';\nexport {\n type ConvertNameOptions,\n type ConvertPaletteOptions,\n type ConvertStructuralOptions,\n convert,\n fromRgba,\n toRgba,\n} from './convert.js';\nexport {\n type DefineColorPaletteInput,\n defineColorPalette,\n} from './defineColorPalette.js';\nexport { type DetectedFormat, detectFormat, isColor } from './detectFormat.js';\nexport {\n type IdentifyMatch,\n type IdentifyOptions,\n type IdentifyRankedOptions,\n identify,\n} from './identify.js';\nexport { type CrayolaColorName, crayola } from './palettes/crayola.js';\nexport { type Fs595bColorName, fs595b } from './palettes/fs595b.js';\nexport { type Fs595cColorName, fs595c } from './palettes/fs595c.js';\nexport { type IsccNbsColorName, isccNbs } from './palettes/isccNbs.js';\nexport { type NbsColorName, nbs } from './palettes/nbs.js';\nexport { type NcsColorName, ncs } from './palettes/ncs.js';\nexport { type NtcColorName, ntc } from './palettes/ntc.js';\nexport { type PantoneColorName, pantone } from './palettes/pantone.js';\nexport { type ReseneColorName, resene } from './palettes/resene.js';\nexport { type WebColorName, web } from './palettes/web.js';\nexport { type X11ColorName, x11 } from './palettes/x11.js';\nexport { type XkcdColorName, xkcd } from './palettes/xkcd.js';\nexport {\n type ResolveFuzzyOptions,\n type ResolveMatch,\n type ResolveOptions,\n resolve,\n} from './resolve.js';\nexport type {\n ColorFormat,\n ColorInput,\n ColorValue,\n DistanceMetric,\n HexColor,\n HslInput,\n HslObject,\n HslString,\n HsvInput,\n HsvObject,\n HsvString,\n NormalizeFn,\n Palette,\n PaletteKey,\n Rgba,\n RgbaInput,\n RgbaString,\n RgbaTuple,\n RgbInput,\n RgbObject,\n RgbString,\n RgbTuple,\n} from './types.js';\nexport { COLOR_FORMATS } from './types.js';\n"]}
|
package/dist/index.js
CHANGED
|
@@ -5,13 +5,22 @@ export { hslToRgba, rgbaToHsl } from './conversions/hsl.js';
|
|
|
5
5
|
export { hsvToRgba, rgbaToHsv } from './conversions/hsv.js';
|
|
6
6
|
export { pantoneToRgba, rgbaToPantone } from './conversions/pantone.js';
|
|
7
7
|
export { rgbaToRgb, rgbToRgba } from './conversions/rgb.js';
|
|
8
|
-
export { convert, fromRgba, toRgba } from './convert.js';
|
|
8
|
+
export { convert, fromRgba, toRgba, } from './convert.js';
|
|
9
|
+
export { defineColorPalette, } from './defineColorPalette.js';
|
|
9
10
|
export { detectFormat, isColor } from './detectFormat.js';
|
|
10
|
-
export { identify } from './identify.js';
|
|
11
|
+
export { identify, } from './identify.js';
|
|
11
12
|
export { crayola } from './palettes/crayola.js';
|
|
13
|
+
export { fs595b } from './palettes/fs595b.js';
|
|
14
|
+
export { fs595c } from './palettes/fs595c.js';
|
|
15
|
+
export { isccNbs } from './palettes/isccNbs.js';
|
|
16
|
+
export { nbs } from './palettes/nbs.js';
|
|
17
|
+
export { ncs } from './palettes/ncs.js';
|
|
18
|
+
export { ntc } from './palettes/ntc.js';
|
|
12
19
|
export { pantone } from './palettes/pantone.js';
|
|
20
|
+
export { resene } from './palettes/resene.js';
|
|
13
21
|
export { web } from './palettes/web.js';
|
|
14
22
|
export { x11 } from './palettes/x11.js';
|
|
15
|
-
export {
|
|
23
|
+
export { xkcd } from './palettes/xkcd.js';
|
|
24
|
+
export { resolve, } from './resolve.js';
|
|
16
25
|
export { COLOR_FORMATS } from './types.js';
|
|
17
26
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,sDAAsD;AAEtD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,sDAAsD;AAEtD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAIL,OAAO,EACP,QAAQ,EACR,MAAM,GACP,MAAM,cAAc,CAAC;AACtB,OAAO,EAEL,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAuB,YAAY,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAIL,QAAQ,GACT,MAAM,eAAe,CAAC;AACvB,OAAO,EAAyB,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAwB,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAwB,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAyB,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAqB,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAqB,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAqB,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAyB,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAwB,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAqB,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAqB,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAsB,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAIL,OAAO,GACR,MAAM,cAAc,CAAC;AAyBtB,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC","sourcesContent":["// Public API barrel — explicit named re-exports only (tree-shake friendly).\n// No default export; no side effects at module scope.\n\nexport { hexToRgba, rgbaToHex } from './conversions/hex.js';\nexport { hslToRgba, rgbaToHsl } from './conversions/hsl.js';\nexport { hsvToRgba, rgbaToHsv } from './conversions/hsv.js';\nexport { pantoneToRgba, rgbaToPantone } from './conversions/pantone.js';\nexport { rgbaToRgb, rgbToRgba } from './conversions/rgb.js';\nexport {\n type ConvertNameOptions,\n type ConvertPaletteOptions,\n type ConvertStructuralOptions,\n convert,\n fromRgba,\n toRgba,\n} from './convert.js';\nexport {\n type DefineColorPaletteInput,\n defineColorPalette,\n} from './defineColorPalette.js';\nexport { type DetectedFormat, detectFormat, isColor } from './detectFormat.js';\nexport {\n type IdentifyMatch,\n type IdentifyOptions,\n type IdentifyRankedOptions,\n identify,\n} from './identify.js';\nexport { type CrayolaColorName, crayola } from './palettes/crayola.js';\nexport { type Fs595bColorName, fs595b } from './palettes/fs595b.js';\nexport { type Fs595cColorName, fs595c } from './palettes/fs595c.js';\nexport { type IsccNbsColorName, isccNbs } from './palettes/isccNbs.js';\nexport { type NbsColorName, nbs } from './palettes/nbs.js';\nexport { type NcsColorName, ncs } from './palettes/ncs.js';\nexport { type NtcColorName, ntc } from './palettes/ntc.js';\nexport { type PantoneColorName, pantone } from './palettes/pantone.js';\nexport { type ReseneColorName, resene } from './palettes/resene.js';\nexport { type WebColorName, web } from './palettes/web.js';\nexport { type X11ColorName, x11 } from './palettes/x11.js';\nexport { type XkcdColorName, xkcd } from './palettes/xkcd.js';\nexport {\n type ResolveFuzzyOptions,\n type ResolveMatch,\n type ResolveOptions,\n resolve,\n} from './resolve.js';\nexport type {\n ColorFormat,\n ColorInput,\n ColorValue,\n DistanceMetric,\n HexColor,\n HslInput,\n HslObject,\n HslString,\n HsvInput,\n HsvObject,\n HsvString,\n NormalizeFn,\n Palette,\n PaletteKey,\n Rgba,\n RgbaInput,\n RgbaString,\n RgbaTuple,\n RgbInput,\n RgbObject,\n RgbString,\n RgbTuple,\n} from './types.js';\nexport { COLOR_FORMATS } from './types.js';\n"]}
|
package/dist/indexing.d.ts
CHANGED
|
@@ -44,9 +44,19 @@ export declare function nearest(target: Rgba, space: AnyPalette, metric: Distanc
|
|
|
44
44
|
* most of the gamut on ΔE76/2000); `euclidean-srgb` / `euclidean-linear`
|
|
45
45
|
* are in their respective channel-unit Euclidean distances.
|
|
46
46
|
*
|
|
47
|
-
* Cost:
|
|
48
|
-
*
|
|
49
|
-
*
|
|
47
|
+
* Cost:
|
|
48
|
+
* - `k < n`: O(n · k) via bounded insertion-sort; much faster than a
|
|
49
|
+
* full sort for small k on cheap metrics where allocation dominates.
|
|
50
|
+
* - `k >= n` or `k === undefined`: O(n log n) via full push-and-sort,
|
|
51
|
+
* returning every entry ranked.
|
|
52
|
+
*
|
|
53
|
+
* Tie-breaking: on exact-distance ties, the first-declared entry in
|
|
54
|
+
* the palette's `colors` object wins. Matches the single-match
|
|
55
|
+
* `nearest` contract.
|
|
56
|
+
*
|
|
57
|
+
* `k` normalization is forgiving: fractional values are rounded,
|
|
58
|
+
* negatives clamp to 0, and `NaN` / `±Infinity` collapse to 0
|
|
59
|
+
* (empty result). No throws.
|
|
50
60
|
*/
|
|
51
61
|
export declare function nearestAll(target: Rgba, space: AnyPalette, metric: DistanceMetric, k?: number): Array<{
|
|
52
62
|
name: string;
|
package/dist/indexing.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexing.d.ts","sourceRoot":"","sources":["../src/indexing.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEhE;;;;;;GAMG;AAEH,KAAK,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAKlC;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAWnE;AAID;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAU1E;AAsGD;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAavF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,IAAI,EACZ,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,cAAc,EACtB,CAAC,CAAC,EAAE,MAAM,GACT,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA2C3C","sourcesContent":["import { hexToRgba } from './conversions/hex.js';\nimport { rgbaToLab, rgbaToLinearRgb, rgbaToOklab } from './math/colorSpace.js';\nimport { deltaE76Squared, deltaE94, deltaE2000, deltaEokSquared } from './math/deltaE.js';\nimport { squaredDistanceRgb } from './math/euclideanDistance.js';\nimport type { DistanceMetric, Palette, Rgba } from './types.js';\n\n/**\n * Per-palette lookup indexes for name resolution and nearest-match.\n *\n * Caches are keyed by the Palette wrapper (WeakMap), so each palette's\n * indexes are built once per process and reused by every caller —\n * including BYO palettes the user supplies.\n */\n\ntype AnyPalette = Palette<string>;\n\nconst nameIndexCache = new WeakMap<AnyPalette, Map<string, string>>();\nconst rgbaIndexCache = new WeakMap<AnyPalette, Array<readonly [string, Rgba]>>();\n\n/**\n * Normalized-name → canonical-key map for a given palette. Built once\n * per palette using its own `normalize` function.\n */\nexport function getNameIndex(space: AnyPalette): Map<string, string> {\n let idx = nameIndexCache.get(space);\n if (idx === undefined) {\n idx = new Map();\n const { normalize } = space;\n for (const key of Object.keys(space.colors)) {\n idx.set(normalize(key), key);\n }\n nameIndexCache.set(space, idx);\n }\n return idx;\n}\n\nconst reverseIndexCache = new WeakMap<AnyPalette, Map<string, string>>();\n\n/**\n * Hex → canonical-key map (reverse of `palette.colors`). Built once per\n * palette. Used by `convert(color, { palette, format: 'NAME' })` for\n * O(1) exact reverse lookup. Returns `undefined` on miss — the caller\n * decides whether that's an error (convert throws; use `identify` for\n * nearest-match semantics).\n *\n * Hex values are lowercased for the key so callers can normalize their\n * input with `rgbaToHex(...).toLowerCase()` before lookup.\n */\nexport function getReverseNameIndex(space: AnyPalette): Map<string, string> {\n let idx = reverseIndexCache.get(space);\n if (idx === undefined) {\n idx = new Map();\n for (const [name, hex] of Object.entries(space.colors)) {\n idx.set((hex as string).toLowerCase(), name);\n }\n reverseIndexCache.set(space, idx);\n }\n return idx;\n}\n\n/**\n * Canonical-key → Rgba entries for a given palette, built by parsing\n * each hex value once. Used by nearest-match lookups (identify, rgbaToPantone).\n */\nfunction getRgbaIndex(space: AnyPalette): Array<readonly [string, Rgba]> {\n let idx = rgbaIndexCache.get(space);\n if (idx === undefined) {\n idx = Object.entries(space.colors).map(([name, hex]) => [name, hexToRgba(hex)] as const);\n rgbaIndexCache.set(space, idx);\n }\n return idx;\n}\n\n/**\n * Find the nearest-named entry in a palette to the given Rgba, using\n * squared Euclidean distance in sRGB (alpha ignored). Used by `identify`\n * and `rgbaToPantone` when metric === 'euclidean-srgb'.\n */\nfunction nearestByRgb(target: Rgba, space: AnyPalette): string {\n const entries = getRgbaIndex(space);\n let bestName = entries[0]?.[0] ?? '';\n let bestDistance = Number.POSITIVE_INFINITY;\n for (const [name, candidate] of entries) {\n const d = squaredDistanceRgb(target, candidate);\n if (d < bestDistance) {\n bestDistance = d;\n bestName = name;\n }\n }\n return bestName;\n}\n\n// Precomputed Lab entries for delta-E metrics. Same shape as rgba index.\ntype LabTriple = readonly [number, number, number];\nconst labIndexCache = new WeakMap<AnyPalette, Array<readonly [string, LabTriple]>>();\n\nfunction getLabIndex(space: AnyPalette): Array<readonly [string, LabTriple]> {\n let idx = labIndexCache.get(space);\n if (idx === undefined) {\n idx = getRgbaIndex(space).map(([name, rgba]) => [name, rgbaToLab(rgba)] as const);\n labIndexCache.set(space, idx);\n }\n return idx;\n}\n\n// Precomputed linear-RGB entries for euclidean-linear metric.\nconst linearIndexCache = new WeakMap<AnyPalette, Array<readonly [string, LabTriple]>>();\n\nfunction getLinearIndex(space: AnyPalette): Array<readonly [string, LabTriple]> {\n let idx = linearIndexCache.get(space);\n if (idx === undefined) {\n idx = getRgbaIndex(space).map(([name, rgba]) => [name, rgbaToLinearRgb(rgba)] as const);\n linearIndexCache.set(space, idx);\n }\n return idx;\n}\n\n// Precomputed OKLAB entries for deltaEok metric.\nconst oklabIndexCache = new WeakMap<AnyPalette, Array<readonly [string, LabTriple]>>();\n\nfunction getOklabIndex(space: AnyPalette): Array<readonly [string, LabTriple]> {\n let idx = oklabIndexCache.get(space);\n if (idx === undefined) {\n idx = getRgbaIndex(space).map(([name, rgba]) => [name, rgbaToOklab(rgba)] as const);\n oklabIndexCache.set(space, idx);\n }\n return idx;\n}\n\n/**\n * Generic argmin over an `[name, value]` entries array with a custom\n * distance function. Factored so the three metric branches in `nearest`\n * share one loop body instead of duplicating it three times.\n */\nfunction argminBy<T>(\n entries: Array<readonly [string, T]>,\n target: T,\n dist: (a: T, b: T) => number,\n): string {\n let bestName = entries[0]?.[0] ?? '';\n let bestDistance = Number.POSITIVE_INFINITY;\n for (const [name, candidate] of entries) {\n const d = dist(target, candidate);\n if (d < bestDistance) {\n bestDistance = d;\n bestName = name;\n }\n }\n return bestName;\n}\n\n// Squared Euclidean on a [number, number, number] triple. Used for the\n// linear-RGB metric (sharing shape with Lab but different meaning).\nfunction squaredDistanceTriple(a: LabTriple, b: LabTriple): number {\n const d0 = a[0] - b[0];\n const d1 = a[1] - b[1];\n const d2 = a[2] - b[2];\n return d0 * d0 + d1 * d1 + d2 * d2;\n}\n\n/**\n * Find the nearest-named entry in a palette under the specified distance\n * metric. Dispatches to the right index (rgba / linear-rgb / Lab) and\n * the right distance function. See `DistanceMetric` in types.ts for how\n * to pick a metric.\n *\n * `metric` is required — callers should pass `space.defaultMetric` if\n * they want the palette's declared preference. A silent default would\n * hide the distance-metric choice from the hot path.\n */\nexport function nearest(target: Rgba, space: AnyPalette, metric: DistanceMetric): string {\n if (metric === 'euclidean-srgb') return nearestByRgb(target, space);\n if (metric === 'euclidean-linear') {\n return argminBy(getLinearIndex(space), rgbaToLinearRgb(target), squaredDistanceTriple);\n }\n if (metric === 'deltaEok') {\n return argminBy(getOklabIndex(space), rgbaToOklab(target), deltaEokSquared);\n }\n const labTarget = rgbaToLab(target);\n const labIndex = getLabIndex(space);\n if (metric === 'deltaE76') return argminBy(labIndex, labTarget, deltaE76Squared);\n if (metric === 'deltaE94') return argminBy(labIndex, labTarget, deltaE94);\n return argminBy(labIndex, labTarget, deltaE2000);\n}\n\n/**\n * Rank every palette entry by distance to `target`. Returns an array of\n * `{ name, distance }` sorted ascending (nearest first). Optional `k`\n * truncates to the top-k matches.\n *\n * Distances are **real** metric values (not squared) — ΔE76 / ΔE94 /\n * ΔE2000 / ΔE-OK are in standard ΔE units (≈1 = just-noticeable for\n * most of the gamut on ΔE76/2000); `euclidean-srgb` / `euclidean-linear`\n * are in their respective channel-unit Euclidean distances.\n *\n * Cost: O(n log n) for the sort vs O(n) for `nearest` — acceptable for\n * top-K queries. For strict \"what's the single nearest?\" stay on\n * `nearest` / `identify`.\n */\nexport function nearestAll(\n target: Rgba,\n space: AnyPalette,\n metric: DistanceMetric,\n k?: number,\n): Array<{ name: string; distance: number }> {\n const ranked: Array<[string, number]> = [];\n\n if (metric === 'euclidean-srgb') {\n for (const [name, candidate] of getRgbaIndex(space)) {\n ranked.push([name, Math.sqrt(squaredDistanceRgb(target, candidate))]);\n }\n } else if (metric === 'euclidean-linear') {\n const tgt = rgbaToLinearRgb(target);\n for (const [name, candidate] of getLinearIndex(space)) {\n ranked.push([name, Math.sqrt(squaredDistanceTriple(tgt, candidate))]);\n }\n } else if (metric === 'deltaEok') {\n const tgt = rgbaToOklab(target);\n for (const [name, candidate] of getOklabIndex(space)) {\n ranked.push([name, Math.sqrt(deltaEokSquared(tgt, candidate))]);\n }\n } else {\n const tgt = rgbaToLab(target);\n const labIndex = getLabIndex(space);\n if (metric === 'deltaE76') {\n for (const [name, candidate] of labIndex) {\n ranked.push([name, Math.sqrt(deltaE76Squared(tgt, candidate))]);\n }\n } else if (metric === 'deltaE94') {\n for (const [name, candidate] of labIndex) {\n ranked.push([name, deltaE94(tgt, candidate)]);\n }\n } else {\n for (const [name, candidate] of labIndex) {\n ranked.push([name, deltaE2000(tgt, candidate)]);\n }\n }\n }\n\n ranked.sort((a, b) => a[1] - b[1]);\n const limit = k === undefined ? ranked.length : Math.min(Math.max(k, 0), ranked.length);\n const result: Array<{ name: string; distance: number }> = new Array(limit);\n for (let i = 0; i < limit; i++) {\n const entry = ranked[i];\n if (entry !== undefined) result[i] = { name: entry[0], distance: entry[1] };\n }\n return result;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"indexing.d.ts","sourceRoot":"","sources":["../src/indexing.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEhE;;;;;;GAMG;AAEH,KAAK,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAKlC;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAWnE;AAID;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAU1E;AAsGD;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAavF;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,IAAI,EACZ,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,cAAc,EACtB,CAAC,CAAC,EAAE,MAAM,GACT,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAkF3C","sourcesContent":["import { hexToRgba } from './conversions/hex.js';\nimport { rgbaToLab, rgbaToLinearRgb, rgbaToOklab } from './math/colorSpace.js';\nimport { deltaE76Squared, deltaE94, deltaE2000, deltaEokSquared } from './math/deltaE.js';\nimport { squaredDistanceRgb } from './math/euclideanDistance.js';\nimport type { DistanceMetric, Palette, Rgba } from './types.js';\n\n/**\n * Per-palette lookup indexes for name resolution and nearest-match.\n *\n * Caches are keyed by the Palette wrapper (WeakMap), so each palette's\n * indexes are built once per process and reused by every caller —\n * including BYO palettes the user supplies.\n */\n\ntype AnyPalette = Palette<string>;\n\nconst nameIndexCache = new WeakMap<AnyPalette, Map<string, string>>();\nconst rgbaIndexCache = new WeakMap<AnyPalette, Array<readonly [string, Rgba]>>();\n\n/**\n * Normalized-name → canonical-key map for a given palette. Built once\n * per palette using its own `normalize` function.\n */\nexport function getNameIndex(space: AnyPalette): Map<string, string> {\n let idx = nameIndexCache.get(space);\n if (idx === undefined) {\n idx = new Map();\n const { normalize } = space;\n for (const key of Object.keys(space.colors)) {\n idx.set(normalize(key), key);\n }\n nameIndexCache.set(space, idx);\n }\n return idx;\n}\n\nconst reverseIndexCache = new WeakMap<AnyPalette, Map<string, string>>();\n\n/**\n * Hex → canonical-key map (reverse of `palette.colors`). Built once per\n * palette. Used by `convert(color, { palette, format: 'NAME' })` for\n * O(1) exact reverse lookup. Returns `undefined` on miss — the caller\n * decides whether that's an error (convert throws; use `identify` for\n * nearest-match semantics).\n *\n * Hex values are lowercased for the key so callers can normalize their\n * input with `rgbaToHex(...).toLowerCase()` before lookup.\n */\nexport function getReverseNameIndex(space: AnyPalette): Map<string, string> {\n let idx = reverseIndexCache.get(space);\n if (idx === undefined) {\n idx = new Map();\n for (const [name, hex] of Object.entries(space.colors)) {\n idx.set((hex as string).toLowerCase(), name);\n }\n reverseIndexCache.set(space, idx);\n }\n return idx;\n}\n\n/**\n * Canonical-key → Rgba entries for a given palette, built by parsing\n * each hex value once. Used by nearest-match lookups (identify, rgbaToPantone).\n */\nfunction getRgbaIndex(space: AnyPalette): Array<readonly [string, Rgba]> {\n let idx = rgbaIndexCache.get(space);\n if (idx === undefined) {\n idx = Object.entries(space.colors).map(([name, hex]) => [name, hexToRgba(hex)] as const);\n rgbaIndexCache.set(space, idx);\n }\n return idx;\n}\n\n/**\n * Find the nearest-named entry in a palette to the given Rgba, using\n * squared Euclidean distance in sRGB (alpha ignored). Used by `identify`\n * and `rgbaToPantone` when metric === 'euclidean-srgb'.\n */\nfunction nearestByRgb(target: Rgba, space: AnyPalette): string {\n const entries = getRgbaIndex(space);\n let bestName = entries[0]?.[0] ?? '';\n let bestDistance = Number.POSITIVE_INFINITY;\n for (const [name, candidate] of entries) {\n const d = squaredDistanceRgb(target, candidate);\n if (d < bestDistance) {\n bestDistance = d;\n bestName = name;\n }\n }\n return bestName;\n}\n\n// Precomputed Lab entries for delta-E metrics. Same shape as rgba index.\ntype LabTriple = readonly [number, number, number];\nconst labIndexCache = new WeakMap<AnyPalette, Array<readonly [string, LabTriple]>>();\n\nfunction getLabIndex(space: AnyPalette): Array<readonly [string, LabTriple]> {\n let idx = labIndexCache.get(space);\n if (idx === undefined) {\n idx = getRgbaIndex(space).map(([name, rgba]) => [name, rgbaToLab(rgba)] as const);\n labIndexCache.set(space, idx);\n }\n return idx;\n}\n\n// Precomputed linear-RGB entries for euclidean-linear metric.\nconst linearIndexCache = new WeakMap<AnyPalette, Array<readonly [string, LabTriple]>>();\n\nfunction getLinearIndex(space: AnyPalette): Array<readonly [string, LabTriple]> {\n let idx = linearIndexCache.get(space);\n if (idx === undefined) {\n idx = getRgbaIndex(space).map(([name, rgba]) => [name, rgbaToLinearRgb(rgba)] as const);\n linearIndexCache.set(space, idx);\n }\n return idx;\n}\n\n// Precomputed OKLAB entries for deltaEok metric.\nconst oklabIndexCache = new WeakMap<AnyPalette, Array<readonly [string, LabTriple]>>();\n\nfunction getOklabIndex(space: AnyPalette): Array<readonly [string, LabTriple]> {\n let idx = oklabIndexCache.get(space);\n if (idx === undefined) {\n idx = getRgbaIndex(space).map(([name, rgba]) => [name, rgbaToOklab(rgba)] as const);\n oklabIndexCache.set(space, idx);\n }\n return idx;\n}\n\n/**\n * Generic argmin over an `[name, value]` entries array with a custom\n * distance function. Factored so the three metric branches in `nearest`\n * share one loop body instead of duplicating it three times.\n */\nfunction argminBy<T>(\n entries: Array<readonly [string, T]>,\n target: T,\n dist: (a: T, b: T) => number,\n): string {\n let bestName = entries[0]?.[0] ?? '';\n let bestDistance = Number.POSITIVE_INFINITY;\n for (const [name, candidate] of entries) {\n const d = dist(target, candidate);\n if (d < bestDistance) {\n bestDistance = d;\n bestName = name;\n }\n }\n return bestName;\n}\n\n// Squared Euclidean on a [number, number, number] triple. Used for the\n// linear-RGB metric (sharing shape with Lab but different meaning).\nfunction squaredDistanceTriple(a: LabTriple, b: LabTriple): number {\n const d0 = a[0] - b[0];\n const d1 = a[1] - b[1];\n const d2 = a[2] - b[2];\n return d0 * d0 + d1 * d1 + d2 * d2;\n}\n\n/**\n * Find the nearest-named entry in a palette under the specified distance\n * metric. Dispatches to the right index (rgba / linear-rgb / Lab) and\n * the right distance function. See `DistanceMetric` in types.ts for how\n * to pick a metric.\n *\n * `metric` is required — callers should pass `space.defaultMetric` if\n * they want the palette's declared preference. A silent default would\n * hide the distance-metric choice from the hot path.\n */\nexport function nearest(target: Rgba, space: AnyPalette, metric: DistanceMetric): string {\n if (metric === 'euclidean-srgb') return nearestByRgb(target, space);\n if (metric === 'euclidean-linear') {\n return argminBy(getLinearIndex(space), rgbaToLinearRgb(target), squaredDistanceTriple);\n }\n if (metric === 'deltaEok') {\n return argminBy(getOklabIndex(space), rgbaToOklab(target), deltaEokSquared);\n }\n const labTarget = rgbaToLab(target);\n const labIndex = getLabIndex(space);\n if (metric === 'deltaE76') return argminBy(labIndex, labTarget, deltaE76Squared);\n if (metric === 'deltaE94') return argminBy(labIndex, labTarget, deltaE94);\n return argminBy(labIndex, labTarget, deltaE2000);\n}\n\n/**\n * Rank every palette entry by distance to `target`. Returns an array of\n * `{ name, distance }` sorted ascending (nearest first). Optional `k`\n * truncates to the top-k matches.\n *\n * Distances are **real** metric values (not squared) — ΔE76 / ΔE94 /\n * ΔE2000 / ΔE-OK are in standard ΔE units (≈1 = just-noticeable for\n * most of the gamut on ΔE76/2000); `euclidean-srgb` / `euclidean-linear`\n * are in their respective channel-unit Euclidean distances.\n *\n * Cost:\n * - `k < n`: O(n · k) via bounded insertion-sort; much faster than a\n * full sort for small k on cheap metrics where allocation dominates.\n * - `k >= n` or `k === undefined`: O(n log n) via full push-and-sort,\n * returning every entry ranked.\n *\n * Tie-breaking: on exact-distance ties, the first-declared entry in\n * the palette's `colors` object wins. Matches the single-match\n * `nearest` contract.\n *\n * `k` normalization is forgiving: fractional values are rounded,\n * negatives clamp to 0, and `NaN` / `±Infinity` collapse to 0\n * (empty result). No throws.\n */\nexport function nearestAll(\n target: Rgba,\n space: AnyPalette,\n metric: DistanceMetric,\n k?: number,\n): Array<{ name: string; distance: number }> {\n const rgbaIndex = getRgbaIndex(space);\n const n = rgbaIndex.length;\n\n // k normalization. Forgiving by design: fractional values are rounded,\n // negatives clamp to 0, NaN / ±Infinity collapse to 0 (empty result).\n // A previous version crashed with `RangeError: Invalid array length`\n // on NaN / fractional k because `new Array(NaN)` throws; the\n // `Number.isFinite` guard eliminates that class of bug.\n const safeK = k === undefined ? n : Number.isFinite(k) ? Math.max(0, Math.round(k)) : 0;\n if (safeK === 0) return [];\n\n // When the caller wants at least every entry ranked, the bounded\n // insertion-sort gives no benefit — it degenerates to O(n^2) on a\n // full result set. Use the plain push-and-sort path instead.\n // When safeK < n, the bounded insertion-sort keeps only the top k\n // entries seen so far, cutting work from O(n log n) to O(n · k)\n // with a much smaller constant; the win is dramatic for cheap\n // metrics (deltaEok at k=5 / 1950-entry ncs measured ~50x) where\n // the full sort's allocation dominates the distance math.\n const useBounded = safeK < n;\n\n const best: Array<{ name: string; distance: number }> = [];\n\n // Single per-candidate branch, chosen once up front. `offer` either\n // pushes unconditionally (full-sort path) or inserts into a bounded\n // sorted window (bounded path). Splitting the switch and the merge\n // strategy into a closure avoids a 6-way metric switch × 2 modes\n // duplication.\n const offer = useBounded\n ? (name: string, dist: number): void => {\n if (best.length < safeK) {\n let j = best.length;\n while (j > 0 && (best[j - 1] as { distance: number }).distance > dist) j--;\n best.splice(j, 0, { name, distance: dist });\n } else if (dist < (best[safeK - 1] as { distance: number }).distance) {\n let j = safeK - 1;\n while (j > 0 && (best[j - 1] as { distance: number }).distance > dist) j--;\n best.splice(j, 0, { name, distance: dist });\n best.pop();\n }\n }\n : (name: string, dist: number): void => {\n best.push({ name, distance: dist });\n };\n\n if (metric === 'euclidean-srgb') {\n for (const [name, candidate] of rgbaIndex) {\n offer(name, Math.sqrt(squaredDistanceRgb(target, candidate)));\n }\n } else if (metric === 'euclidean-linear') {\n const tgt = rgbaToLinearRgb(target);\n for (const [name, candidate] of getLinearIndex(space)) {\n offer(name, Math.sqrt(squaredDistanceTriple(tgt, candidate)));\n }\n } else if (metric === 'deltaEok') {\n const tgt = rgbaToOklab(target);\n for (const [name, candidate] of getOklabIndex(space)) {\n offer(name, Math.sqrt(deltaEokSquared(tgt, candidate)));\n }\n } else {\n const tgt = rgbaToLab(target);\n const labIndex = getLabIndex(space);\n if (metric === 'deltaE76') {\n for (const [name, candidate] of labIndex) {\n offer(name, Math.sqrt(deltaE76Squared(tgt, candidate)));\n }\n } else if (metric === 'deltaE94') {\n for (const [name, candidate] of labIndex) {\n offer(name, deltaE94(tgt, candidate));\n }\n } else {\n for (const [name, candidate] of labIndex) {\n offer(name, deltaE2000(tgt, candidate));\n }\n }\n }\n\n if (!useBounded) {\n best.sort((a, b) => a.distance - b.distance);\n }\n return best;\n}\n"]}
|
package/dist/indexing.js
CHANGED
|
@@ -163,27 +163,80 @@ export function nearest(target, space, metric) {
|
|
|
163
163
|
* most of the gamut on ΔE76/2000); `euclidean-srgb` / `euclidean-linear`
|
|
164
164
|
* are in their respective channel-unit Euclidean distances.
|
|
165
165
|
*
|
|
166
|
-
* Cost:
|
|
167
|
-
*
|
|
168
|
-
*
|
|
166
|
+
* Cost:
|
|
167
|
+
* - `k < n`: O(n · k) via bounded insertion-sort; much faster than a
|
|
168
|
+
* full sort for small k on cheap metrics where allocation dominates.
|
|
169
|
+
* - `k >= n` or `k === undefined`: O(n log n) via full push-and-sort,
|
|
170
|
+
* returning every entry ranked.
|
|
171
|
+
*
|
|
172
|
+
* Tie-breaking: on exact-distance ties, the first-declared entry in
|
|
173
|
+
* the palette's `colors` object wins. Matches the single-match
|
|
174
|
+
* `nearest` contract.
|
|
175
|
+
*
|
|
176
|
+
* `k` normalization is forgiving: fractional values are rounded,
|
|
177
|
+
* negatives clamp to 0, and `NaN` / `±Infinity` collapse to 0
|
|
178
|
+
* (empty result). No throws.
|
|
169
179
|
*/
|
|
170
180
|
export function nearestAll(target, space, metric, k) {
|
|
171
|
-
const
|
|
181
|
+
const rgbaIndex = getRgbaIndex(space);
|
|
182
|
+
const n = rgbaIndex.length;
|
|
183
|
+
// k normalization. Forgiving by design: fractional values are rounded,
|
|
184
|
+
// negatives clamp to 0, NaN / ±Infinity collapse to 0 (empty result).
|
|
185
|
+
// A previous version crashed with `RangeError: Invalid array length`
|
|
186
|
+
// on NaN / fractional k because `new Array(NaN)` throws; the
|
|
187
|
+
// `Number.isFinite` guard eliminates that class of bug.
|
|
188
|
+
const safeK = k === undefined ? n : Number.isFinite(k) ? Math.max(0, Math.round(k)) : 0;
|
|
189
|
+
if (safeK === 0)
|
|
190
|
+
return [];
|
|
191
|
+
// When the caller wants at least every entry ranked, the bounded
|
|
192
|
+
// insertion-sort gives no benefit — it degenerates to O(n^2) on a
|
|
193
|
+
// full result set. Use the plain push-and-sort path instead.
|
|
194
|
+
// When safeK < n, the bounded insertion-sort keeps only the top k
|
|
195
|
+
// entries seen so far, cutting work from O(n log n) to O(n · k)
|
|
196
|
+
// with a much smaller constant; the win is dramatic for cheap
|
|
197
|
+
// metrics (deltaEok at k=5 / 1950-entry ncs measured ~50x) where
|
|
198
|
+
// the full sort's allocation dominates the distance math.
|
|
199
|
+
const useBounded = safeK < n;
|
|
200
|
+
const best = [];
|
|
201
|
+
// Single per-candidate branch, chosen once up front. `offer` either
|
|
202
|
+
// pushes unconditionally (full-sort path) or inserts into a bounded
|
|
203
|
+
// sorted window (bounded path). Splitting the switch and the merge
|
|
204
|
+
// strategy into a closure avoids a 6-way metric switch × 2 modes
|
|
205
|
+
// duplication.
|
|
206
|
+
const offer = useBounded
|
|
207
|
+
? (name, dist) => {
|
|
208
|
+
if (best.length < safeK) {
|
|
209
|
+
let j = best.length;
|
|
210
|
+
while (j > 0 && best[j - 1].distance > dist)
|
|
211
|
+
j--;
|
|
212
|
+
best.splice(j, 0, { name, distance: dist });
|
|
213
|
+
}
|
|
214
|
+
else if (dist < best[safeK - 1].distance) {
|
|
215
|
+
let j = safeK - 1;
|
|
216
|
+
while (j > 0 && best[j - 1].distance > dist)
|
|
217
|
+
j--;
|
|
218
|
+
best.splice(j, 0, { name, distance: dist });
|
|
219
|
+
best.pop();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
: (name, dist) => {
|
|
223
|
+
best.push({ name, distance: dist });
|
|
224
|
+
};
|
|
172
225
|
if (metric === 'euclidean-srgb') {
|
|
173
|
-
for (const [name, candidate] of
|
|
174
|
-
|
|
226
|
+
for (const [name, candidate] of rgbaIndex) {
|
|
227
|
+
offer(name, Math.sqrt(squaredDistanceRgb(target, candidate)));
|
|
175
228
|
}
|
|
176
229
|
}
|
|
177
230
|
else if (metric === 'euclidean-linear') {
|
|
178
231
|
const tgt = rgbaToLinearRgb(target);
|
|
179
232
|
for (const [name, candidate] of getLinearIndex(space)) {
|
|
180
|
-
|
|
233
|
+
offer(name, Math.sqrt(squaredDistanceTriple(tgt, candidate)));
|
|
181
234
|
}
|
|
182
235
|
}
|
|
183
236
|
else if (metric === 'deltaEok') {
|
|
184
237
|
const tgt = rgbaToOklab(target);
|
|
185
238
|
for (const [name, candidate] of getOklabIndex(space)) {
|
|
186
|
-
|
|
239
|
+
offer(name, Math.sqrt(deltaEokSquared(tgt, candidate)));
|
|
187
240
|
}
|
|
188
241
|
}
|
|
189
242
|
else {
|
|
@@ -191,28 +244,23 @@ export function nearestAll(target, space, metric, k) {
|
|
|
191
244
|
const labIndex = getLabIndex(space);
|
|
192
245
|
if (metric === 'deltaE76') {
|
|
193
246
|
for (const [name, candidate] of labIndex) {
|
|
194
|
-
|
|
247
|
+
offer(name, Math.sqrt(deltaE76Squared(tgt, candidate)));
|
|
195
248
|
}
|
|
196
249
|
}
|
|
197
250
|
else if (metric === 'deltaE94') {
|
|
198
251
|
for (const [name, candidate] of labIndex) {
|
|
199
|
-
|
|
252
|
+
offer(name, deltaE94(tgt, candidate));
|
|
200
253
|
}
|
|
201
254
|
}
|
|
202
255
|
else {
|
|
203
256
|
for (const [name, candidate] of labIndex) {
|
|
204
|
-
|
|
257
|
+
offer(name, deltaE2000(tgt, candidate));
|
|
205
258
|
}
|
|
206
259
|
}
|
|
207
260
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const result = new Array(limit);
|
|
211
|
-
for (let i = 0; i < limit; i++) {
|
|
212
|
-
const entry = ranked[i];
|
|
213
|
-
if (entry !== undefined)
|
|
214
|
-
result[i] = { name: entry[0], distance: entry[1] };
|
|
261
|
+
if (!useBounded) {
|
|
262
|
+
best.sort((a, b) => a.distance - b.distance);
|
|
215
263
|
}
|
|
216
|
-
return
|
|
264
|
+
return best;
|
|
217
265
|
}
|
|
218
266
|
//# sourceMappingURL=indexing.js.map
|
package/dist/indexing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexing.js","sourceRoot":"","sources":["../src/indexing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAC1F,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAajE,MAAM,cAAc,GAAG,IAAI,OAAO,EAAmC,CAAC;AACtE,MAAM,cAAc,GAAG,IAAI,OAAO,EAA8C,CAAC;AAEjF;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,KAAiB;IAC5C,IAAI,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACpC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;QAChB,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;QAC5B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5C,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;QAC/B,CAAC;QACD,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,iBAAiB,GAAG,IAAI,OAAO,EAAmC,CAAC;AAEzE;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAiB;IACnD,IAAI,GAAG,GAAG,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;QAChB,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YACvD,GAAG,CAAC,GAAG,CAAE,GAAc,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,CAAC;QAC/C,CAAC;QACD,iBAAiB,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,SAAS,YAAY,CAAC,KAAiB;IACrC,IAAI,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACpC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,GAAG,CAAC,CAAU,CAAC,CAAC;QACzF,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,SAAS,YAAY,CAAC,MAAY,EAAE,KAAiB;IACnD,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACpC,IAAI,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACrC,IAAI,YAAY,GAAG,MAAM,CAAC,iBAAiB,CAAC;IAC5C,KAAK,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,OAAO,EAAE,CAAC;QACxC,MAAM,CAAC,GAAG,kBAAkB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAChD,IAAI,CAAC,GAAG,YAAY,EAAE,CAAC;YACrB,YAAY,GAAG,CAAC,CAAC;YACjB,QAAQ,GAAG,IAAI,CAAC;QAClB,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAID,MAAM,aAAa,GAAG,IAAI,OAAO,EAAmD,CAAC;AAErF,SAAS,WAAW,CAAC,KAAiB;IACpC,IAAI,GAAG,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,GAAG,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAU,CAAC,CAAC;QAClF,aAAa,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,8DAA8D;AAC9D,MAAM,gBAAgB,GAAG,IAAI,OAAO,EAAmD,CAAC;AAExF,SAAS,cAAc,CAAC,KAAiB;IACvC,IAAI,GAAG,GAAG,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACtC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,GAAG,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,eAAe,CAAC,IAAI,CAAC,CAAU,CAAC,CAAC;QACxF,gBAAgB,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,iDAAiD;AACjD,MAAM,eAAe,GAAG,IAAI,OAAO,EAAmD,CAAC;AAEvF,SAAS,aAAa,CAAC,KAAiB;IACtC,IAAI,GAAG,GAAG,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,GAAG,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC,CAAU,CAAC,CAAC;QACpF,eAAe,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,SAAS,QAAQ,CACf,OAAoC,EACpC,MAAS,EACT,IAA4B;IAE5B,IAAI,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACrC,IAAI,YAAY,GAAG,MAAM,CAAC,iBAAiB,CAAC;IAC5C,KAAK,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,OAAO,EAAE,CAAC;QACxC,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAClC,IAAI,CAAC,GAAG,YAAY,EAAE,CAAC;YACrB,YAAY,GAAG,CAAC,CAAC;YACjB,QAAQ,GAAG,IAAI,CAAC;QAClB,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,uEAAuE;AACvE,oEAAoE;AACpE,SAAS,qBAAqB,CAAC,CAAY,EAAE,CAAY;IACvD,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACvB,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACvB,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACvB,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AACrC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,OAAO,CAAC,MAAY,EAAE,KAAiB,EAAE,MAAsB;IAC7E,IAAI,MAAM,KAAK,gBAAgB;QAAE,OAAO,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACpE,IAAI,MAAM,KAAK,kBAAkB,EAAE,CAAC;QAClC,OAAO,QAAQ,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE,qBAAqB,CAAC,CAAC;IACzF,CAAC;IACD,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;QAC1B,OAAO,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC,CAAC;IAC9E,CAAC;IACD,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IACpC,IAAI,MAAM,KAAK,UAAU;QAAE,OAAO,QAAQ,CAAC,QAAQ,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;IACjF,IAAI,MAAM,KAAK,UAAU;QAAE,OAAO,QAAQ,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC1E,OAAO,QAAQ,CAAC,QAAQ,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;AACnD,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,UAAU,CACxB,MAAY,EACZ,KAAiB,EACjB,MAAsB,EACtB,CAAU;IAEV,MAAM,MAAM,GAA4B,EAAE,CAAC;IAE3C,IAAI,MAAM,KAAK,gBAAgB,EAAE,CAAC;QAChC,KAAK,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;YACpD,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;QACxE,CAAC;IACH,CAAC;SAAM,IAAI,MAAM,KAAK,kBAAkB,EAAE,CAAC;QACzC,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;QACpC,KAAK,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;YACtD,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,qBAAqB,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;QACxE,CAAC;IACH,CAAC;SAAM,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;QACjC,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QAChC,KAAK,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;YACrD,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;QAClE,CAAC;IACH,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;QAC9B,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;YAC1B,KAAK,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,QAAQ,EAAE,CAAC;gBACzC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;YAClE,CAAC;QACH,CAAC;aAAM,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;YACjC,KAAK,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,QAAQ,EAAE,CAAC;gBACzC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;YAChD,CAAC;QACH,CAAC;aAAM,CAAC;YACN,KAAK,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,QAAQ,EAAE,CAAC;gBACzC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,KAAK,GAAG,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IACxF,MAAM,MAAM,GAA8C,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;IAC3E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9E,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import { hexToRgba } from './conversions/hex.js';\nimport { rgbaToLab, rgbaToLinearRgb, rgbaToOklab } from './math/colorSpace.js';\nimport { deltaE76Squared, deltaE94, deltaE2000, deltaEokSquared } from './math/deltaE.js';\nimport { squaredDistanceRgb } from './math/euclideanDistance.js';\nimport type { DistanceMetric, Palette, Rgba } from './types.js';\n\n/**\n * Per-palette lookup indexes for name resolution and nearest-match.\n *\n * Caches are keyed by the Palette wrapper (WeakMap), so each palette's\n * indexes are built once per process and reused by every caller —\n * including BYO palettes the user supplies.\n */\n\ntype AnyPalette = Palette<string>;\n\nconst nameIndexCache = new WeakMap<AnyPalette, Map<string, string>>();\nconst rgbaIndexCache = new WeakMap<AnyPalette, Array<readonly [string, Rgba]>>();\n\n/**\n * Normalized-name → canonical-key map for a given palette. Built once\n * per palette using its own `normalize` function.\n */\nexport function getNameIndex(space: AnyPalette): Map<string, string> {\n let idx = nameIndexCache.get(space);\n if (idx === undefined) {\n idx = new Map();\n const { normalize } = space;\n for (const key of Object.keys(space.colors)) {\n idx.set(normalize(key), key);\n }\n nameIndexCache.set(space, idx);\n }\n return idx;\n}\n\nconst reverseIndexCache = new WeakMap<AnyPalette, Map<string, string>>();\n\n/**\n * Hex → canonical-key map (reverse of `palette.colors`). Built once per\n * palette. Used by `convert(color, { palette, format: 'NAME' })` for\n * O(1) exact reverse lookup. Returns `undefined` on miss — the caller\n * decides whether that's an error (convert throws; use `identify` for\n * nearest-match semantics).\n *\n * Hex values are lowercased for the key so callers can normalize their\n * input with `rgbaToHex(...).toLowerCase()` before lookup.\n */\nexport function getReverseNameIndex(space: AnyPalette): Map<string, string> {\n let idx = reverseIndexCache.get(space);\n if (idx === undefined) {\n idx = new Map();\n for (const [name, hex] of Object.entries(space.colors)) {\n idx.set((hex as string).toLowerCase(), name);\n }\n reverseIndexCache.set(space, idx);\n }\n return idx;\n}\n\n/**\n * Canonical-key → Rgba entries for a given palette, built by parsing\n * each hex value once. Used by nearest-match lookups (identify, rgbaToPantone).\n */\nfunction getRgbaIndex(space: AnyPalette): Array<readonly [string, Rgba]> {\n let idx = rgbaIndexCache.get(space);\n if (idx === undefined) {\n idx = Object.entries(space.colors).map(([name, hex]) => [name, hexToRgba(hex)] as const);\n rgbaIndexCache.set(space, idx);\n }\n return idx;\n}\n\n/**\n * Find the nearest-named entry in a palette to the given Rgba, using\n * squared Euclidean distance in sRGB (alpha ignored). Used by `identify`\n * and `rgbaToPantone` when metric === 'euclidean-srgb'.\n */\nfunction nearestByRgb(target: Rgba, space: AnyPalette): string {\n const entries = getRgbaIndex(space);\n let bestName = entries[0]?.[0] ?? '';\n let bestDistance = Number.POSITIVE_INFINITY;\n for (const [name, candidate] of entries) {\n const d = squaredDistanceRgb(target, candidate);\n if (d < bestDistance) {\n bestDistance = d;\n bestName = name;\n }\n }\n return bestName;\n}\n\n// Precomputed Lab entries for delta-E metrics. Same shape as rgba index.\ntype LabTriple = readonly [number, number, number];\nconst labIndexCache = new WeakMap<AnyPalette, Array<readonly [string, LabTriple]>>();\n\nfunction getLabIndex(space: AnyPalette): Array<readonly [string, LabTriple]> {\n let idx = labIndexCache.get(space);\n if (idx === undefined) {\n idx = getRgbaIndex(space).map(([name, rgba]) => [name, rgbaToLab(rgba)] as const);\n labIndexCache.set(space, idx);\n }\n return idx;\n}\n\n// Precomputed linear-RGB entries for euclidean-linear metric.\nconst linearIndexCache = new WeakMap<AnyPalette, Array<readonly [string, LabTriple]>>();\n\nfunction getLinearIndex(space: AnyPalette): Array<readonly [string, LabTriple]> {\n let idx = linearIndexCache.get(space);\n if (idx === undefined) {\n idx = getRgbaIndex(space).map(([name, rgba]) => [name, rgbaToLinearRgb(rgba)] as const);\n linearIndexCache.set(space, idx);\n }\n return idx;\n}\n\n// Precomputed OKLAB entries for deltaEok metric.\nconst oklabIndexCache = new WeakMap<AnyPalette, Array<readonly [string, LabTriple]>>();\n\nfunction getOklabIndex(space: AnyPalette): Array<readonly [string, LabTriple]> {\n let idx = oklabIndexCache.get(space);\n if (idx === undefined) {\n idx = getRgbaIndex(space).map(([name, rgba]) => [name, rgbaToOklab(rgba)] as const);\n oklabIndexCache.set(space, idx);\n }\n return idx;\n}\n\n/**\n * Generic argmin over an `[name, value]` entries array with a custom\n * distance function. Factored so the three metric branches in `nearest`\n * share one loop body instead of duplicating it three times.\n */\nfunction argminBy<T>(\n entries: Array<readonly [string, T]>,\n target: T,\n dist: (a: T, b: T) => number,\n): string {\n let bestName = entries[0]?.[0] ?? '';\n let bestDistance = Number.POSITIVE_INFINITY;\n for (const [name, candidate] of entries) {\n const d = dist(target, candidate);\n if (d < bestDistance) {\n bestDistance = d;\n bestName = name;\n }\n }\n return bestName;\n}\n\n// Squared Euclidean on a [number, number, number] triple. Used for the\n// linear-RGB metric (sharing shape with Lab but different meaning).\nfunction squaredDistanceTriple(a: LabTriple, b: LabTriple): number {\n const d0 = a[0] - b[0];\n const d1 = a[1] - b[1];\n const d2 = a[2] - b[2];\n return d0 * d0 + d1 * d1 + d2 * d2;\n}\n\n/**\n * Find the nearest-named entry in a palette under the specified distance\n * metric. Dispatches to the right index (rgba / linear-rgb / Lab) and\n * the right distance function. See `DistanceMetric` in types.ts for how\n * to pick a metric.\n *\n * `metric` is required — callers should pass `space.defaultMetric` if\n * they want the palette's declared preference. A silent default would\n * hide the distance-metric choice from the hot path.\n */\nexport function nearest(target: Rgba, space: AnyPalette, metric: DistanceMetric): string {\n if (metric === 'euclidean-srgb') return nearestByRgb(target, space);\n if (metric === 'euclidean-linear') {\n return argminBy(getLinearIndex(space), rgbaToLinearRgb(target), squaredDistanceTriple);\n }\n if (metric === 'deltaEok') {\n return argminBy(getOklabIndex(space), rgbaToOklab(target), deltaEokSquared);\n }\n const labTarget = rgbaToLab(target);\n const labIndex = getLabIndex(space);\n if (metric === 'deltaE76') return argminBy(labIndex, labTarget, deltaE76Squared);\n if (metric === 'deltaE94') return argminBy(labIndex, labTarget, deltaE94);\n return argminBy(labIndex, labTarget, deltaE2000);\n}\n\n/**\n * Rank every palette entry by distance to `target`. Returns an array of\n * `{ name, distance }` sorted ascending (nearest first). Optional `k`\n * truncates to the top-k matches.\n *\n * Distances are **real** metric values (not squared) — ΔE76 / ΔE94 /\n * ΔE2000 / ΔE-OK are in standard ΔE units (≈1 = just-noticeable for\n * most of the gamut on ΔE76/2000); `euclidean-srgb` / `euclidean-linear`\n * are in their respective channel-unit Euclidean distances.\n *\n * Cost: O(n log n) for the sort vs O(n) for `nearest` — acceptable for\n * top-K queries. For strict \"what's the single nearest?\" stay on\n * `nearest` / `identify`.\n */\nexport function nearestAll(\n target: Rgba,\n space: AnyPalette,\n metric: DistanceMetric,\n k?: number,\n): Array<{ name: string; distance: number }> {\n const ranked: Array<[string, number]> = [];\n\n if (metric === 'euclidean-srgb') {\n for (const [name, candidate] of getRgbaIndex(space)) {\n ranked.push([name, Math.sqrt(squaredDistanceRgb(target, candidate))]);\n }\n } else if (metric === 'euclidean-linear') {\n const tgt = rgbaToLinearRgb(target);\n for (const [name, candidate] of getLinearIndex(space)) {\n ranked.push([name, Math.sqrt(squaredDistanceTriple(tgt, candidate))]);\n }\n } else if (metric === 'deltaEok') {\n const tgt = rgbaToOklab(target);\n for (const [name, candidate] of getOklabIndex(space)) {\n ranked.push([name, Math.sqrt(deltaEokSquared(tgt, candidate))]);\n }\n } else {\n const tgt = rgbaToLab(target);\n const labIndex = getLabIndex(space);\n if (metric === 'deltaE76') {\n for (const [name, candidate] of labIndex) {\n ranked.push([name, Math.sqrt(deltaE76Squared(tgt, candidate))]);\n }\n } else if (metric === 'deltaE94') {\n for (const [name, candidate] of labIndex) {\n ranked.push([name, deltaE94(tgt, candidate)]);\n }\n } else {\n for (const [name, candidate] of labIndex) {\n ranked.push([name, deltaE2000(tgt, candidate)]);\n }\n }\n }\n\n ranked.sort((a, b) => a[1] - b[1]);\n const limit = k === undefined ? ranked.length : Math.min(Math.max(k, 0), ranked.length);\n const result: Array<{ name: string; distance: number }> = new Array(limit);\n for (let i = 0; i < limit; i++) {\n const entry = ranked[i];\n if (entry !== undefined) result[i] = { name: entry[0], distance: entry[1] };\n }\n return result;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"indexing.js","sourceRoot":"","sources":["../src/indexing.ts"],"names":[],"mappings":"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","sourcesContent":["import { hexToRgba } from './conversions/hex.js';\nimport { rgbaToLab, rgbaToLinearRgb, rgbaToOklab } from './math/colorSpace.js';\nimport { deltaE76Squared, deltaE94, deltaE2000, deltaEokSquared } from './math/deltaE.js';\nimport { squaredDistanceRgb } from './math/euclideanDistance.js';\nimport type { DistanceMetric, Palette, Rgba } from './types.js';\n\n/**\n * Per-palette lookup indexes for name resolution and nearest-match.\n *\n * Caches are keyed by the Palette wrapper (WeakMap), so each palette's\n * indexes are built once per process and reused by every caller —\n * including BYO palettes the user supplies.\n */\n\ntype AnyPalette = Palette<string>;\n\nconst nameIndexCache = new WeakMap<AnyPalette, Map<string, string>>();\nconst rgbaIndexCache = new WeakMap<AnyPalette, Array<readonly [string, Rgba]>>();\n\n/**\n * Normalized-name → canonical-key map for a given palette. Built once\n * per palette using its own `normalize` function.\n */\nexport function getNameIndex(space: AnyPalette): Map<string, string> {\n let idx = nameIndexCache.get(space);\n if (idx === undefined) {\n idx = new Map();\n const { normalize } = space;\n for (const key of Object.keys(space.colors)) {\n idx.set(normalize(key), key);\n }\n nameIndexCache.set(space, idx);\n }\n return idx;\n}\n\nconst reverseIndexCache = new WeakMap<AnyPalette, Map<string, string>>();\n\n/**\n * Hex → canonical-key map (reverse of `palette.colors`). Built once per\n * palette. Used by `convert(color, { palette, format: 'NAME' })` for\n * O(1) exact reverse lookup. Returns `undefined` on miss — the caller\n * decides whether that's an error (convert throws; use `identify` for\n * nearest-match semantics).\n *\n * Hex values are lowercased for the key so callers can normalize their\n * input with `rgbaToHex(...).toLowerCase()` before lookup.\n */\nexport function getReverseNameIndex(space: AnyPalette): Map<string, string> {\n let idx = reverseIndexCache.get(space);\n if (idx === undefined) {\n idx = new Map();\n for (const [name, hex] of Object.entries(space.colors)) {\n idx.set((hex as string).toLowerCase(), name);\n }\n reverseIndexCache.set(space, idx);\n }\n return idx;\n}\n\n/**\n * Canonical-key → Rgba entries for a given palette, built by parsing\n * each hex value once. Used by nearest-match lookups (identify, rgbaToPantone).\n */\nfunction getRgbaIndex(space: AnyPalette): Array<readonly [string, Rgba]> {\n let idx = rgbaIndexCache.get(space);\n if (idx === undefined) {\n idx = Object.entries(space.colors).map(([name, hex]) => [name, hexToRgba(hex)] as const);\n rgbaIndexCache.set(space, idx);\n }\n return idx;\n}\n\n/**\n * Find the nearest-named entry in a palette to the given Rgba, using\n * squared Euclidean distance in sRGB (alpha ignored). Used by `identify`\n * and `rgbaToPantone` when metric === 'euclidean-srgb'.\n */\nfunction nearestByRgb(target: Rgba, space: AnyPalette): string {\n const entries = getRgbaIndex(space);\n let bestName = entries[0]?.[0] ?? '';\n let bestDistance = Number.POSITIVE_INFINITY;\n for (const [name, candidate] of entries) {\n const d = squaredDistanceRgb(target, candidate);\n if (d < bestDistance) {\n bestDistance = d;\n bestName = name;\n }\n }\n return bestName;\n}\n\n// Precomputed Lab entries for delta-E metrics. Same shape as rgba index.\ntype LabTriple = readonly [number, number, number];\nconst labIndexCache = new WeakMap<AnyPalette, Array<readonly [string, LabTriple]>>();\n\nfunction getLabIndex(space: AnyPalette): Array<readonly [string, LabTriple]> {\n let idx = labIndexCache.get(space);\n if (idx === undefined) {\n idx = getRgbaIndex(space).map(([name, rgba]) => [name, rgbaToLab(rgba)] as const);\n labIndexCache.set(space, idx);\n }\n return idx;\n}\n\n// Precomputed linear-RGB entries for euclidean-linear metric.\nconst linearIndexCache = new WeakMap<AnyPalette, Array<readonly [string, LabTriple]>>();\n\nfunction getLinearIndex(space: AnyPalette): Array<readonly [string, LabTriple]> {\n let idx = linearIndexCache.get(space);\n if (idx === undefined) {\n idx = getRgbaIndex(space).map(([name, rgba]) => [name, rgbaToLinearRgb(rgba)] as const);\n linearIndexCache.set(space, idx);\n }\n return idx;\n}\n\n// Precomputed OKLAB entries for deltaEok metric.\nconst oklabIndexCache = new WeakMap<AnyPalette, Array<readonly [string, LabTriple]>>();\n\nfunction getOklabIndex(space: AnyPalette): Array<readonly [string, LabTriple]> {\n let idx = oklabIndexCache.get(space);\n if (idx === undefined) {\n idx = getRgbaIndex(space).map(([name, rgba]) => [name, rgbaToOklab(rgba)] as const);\n oklabIndexCache.set(space, idx);\n }\n return idx;\n}\n\n/**\n * Generic argmin over an `[name, value]` entries array with a custom\n * distance function. Factored so the three metric branches in `nearest`\n * share one loop body instead of duplicating it three times.\n */\nfunction argminBy<T>(\n entries: Array<readonly [string, T]>,\n target: T,\n dist: (a: T, b: T) => number,\n): string {\n let bestName = entries[0]?.[0] ?? '';\n let bestDistance = Number.POSITIVE_INFINITY;\n for (const [name, candidate] of entries) {\n const d = dist(target, candidate);\n if (d < bestDistance) {\n bestDistance = d;\n bestName = name;\n }\n }\n return bestName;\n}\n\n// Squared Euclidean on a [number, number, number] triple. Used for the\n// linear-RGB metric (sharing shape with Lab but different meaning).\nfunction squaredDistanceTriple(a: LabTriple, b: LabTriple): number {\n const d0 = a[0] - b[0];\n const d1 = a[1] - b[1];\n const d2 = a[2] - b[2];\n return d0 * d0 + d1 * d1 + d2 * d2;\n}\n\n/**\n * Find the nearest-named entry in a palette under the specified distance\n * metric. Dispatches to the right index (rgba / linear-rgb / Lab) and\n * the right distance function. See `DistanceMetric` in types.ts for how\n * to pick a metric.\n *\n * `metric` is required — callers should pass `space.defaultMetric` if\n * they want the palette's declared preference. A silent default would\n * hide the distance-metric choice from the hot path.\n */\nexport function nearest(target: Rgba, space: AnyPalette, metric: DistanceMetric): string {\n if (metric === 'euclidean-srgb') return nearestByRgb(target, space);\n if (metric === 'euclidean-linear') {\n return argminBy(getLinearIndex(space), rgbaToLinearRgb(target), squaredDistanceTriple);\n }\n if (metric === 'deltaEok') {\n return argminBy(getOklabIndex(space), rgbaToOklab(target), deltaEokSquared);\n }\n const labTarget = rgbaToLab(target);\n const labIndex = getLabIndex(space);\n if (metric === 'deltaE76') return argminBy(labIndex, labTarget, deltaE76Squared);\n if (metric === 'deltaE94') return argminBy(labIndex, labTarget, deltaE94);\n return argminBy(labIndex, labTarget, deltaE2000);\n}\n\n/**\n * Rank every palette entry by distance to `target`. Returns an array of\n * `{ name, distance }` sorted ascending (nearest first). Optional `k`\n * truncates to the top-k matches.\n *\n * Distances are **real** metric values (not squared) — ΔE76 / ΔE94 /\n * ΔE2000 / ΔE-OK are in standard ΔE units (≈1 = just-noticeable for\n * most of the gamut on ΔE76/2000); `euclidean-srgb` / `euclidean-linear`\n * are in their respective channel-unit Euclidean distances.\n *\n * Cost:\n * - `k < n`: O(n · k) via bounded insertion-sort; much faster than a\n * full sort for small k on cheap metrics where allocation dominates.\n * - `k >= n` or `k === undefined`: O(n log n) via full push-and-sort,\n * returning every entry ranked.\n *\n * Tie-breaking: on exact-distance ties, the first-declared entry in\n * the palette's `colors` object wins. Matches the single-match\n * `nearest` contract.\n *\n * `k` normalization is forgiving: fractional values are rounded,\n * negatives clamp to 0, and `NaN` / `±Infinity` collapse to 0\n * (empty result). No throws.\n */\nexport function nearestAll(\n target: Rgba,\n space: AnyPalette,\n metric: DistanceMetric,\n k?: number,\n): Array<{ name: string; distance: number }> {\n const rgbaIndex = getRgbaIndex(space);\n const n = rgbaIndex.length;\n\n // k normalization. Forgiving by design: fractional values are rounded,\n // negatives clamp to 0, NaN / ±Infinity collapse to 0 (empty result).\n // A previous version crashed with `RangeError: Invalid array length`\n // on NaN / fractional k because `new Array(NaN)` throws; the\n // `Number.isFinite` guard eliminates that class of bug.\n const safeK = k === undefined ? n : Number.isFinite(k) ? Math.max(0, Math.round(k)) : 0;\n if (safeK === 0) return [];\n\n // When the caller wants at least every entry ranked, the bounded\n // insertion-sort gives no benefit — it degenerates to O(n^2) on a\n // full result set. Use the plain push-and-sort path instead.\n // When safeK < n, the bounded insertion-sort keeps only the top k\n // entries seen so far, cutting work from O(n log n) to O(n · k)\n // with a much smaller constant; the win is dramatic for cheap\n // metrics (deltaEok at k=5 / 1950-entry ncs measured ~50x) where\n // the full sort's allocation dominates the distance math.\n const useBounded = safeK < n;\n\n const best: Array<{ name: string; distance: number }> = [];\n\n // Single per-candidate branch, chosen once up front. `offer` either\n // pushes unconditionally (full-sort path) or inserts into a bounded\n // sorted window (bounded path). Splitting the switch and the merge\n // strategy into a closure avoids a 6-way metric switch × 2 modes\n // duplication.\n const offer = useBounded\n ? (name: string, dist: number): void => {\n if (best.length < safeK) {\n let j = best.length;\n while (j > 0 && (best[j - 1] as { distance: number }).distance > dist) j--;\n best.splice(j, 0, { name, distance: dist });\n } else if (dist < (best[safeK - 1] as { distance: number }).distance) {\n let j = safeK - 1;\n while (j > 0 && (best[j - 1] as { distance: number }).distance > dist) j--;\n best.splice(j, 0, { name, distance: dist });\n best.pop();\n }\n }\n : (name: string, dist: number): void => {\n best.push({ name, distance: dist });\n };\n\n if (metric === 'euclidean-srgb') {\n for (const [name, candidate] of rgbaIndex) {\n offer(name, Math.sqrt(squaredDistanceRgb(target, candidate)));\n }\n } else if (metric === 'euclidean-linear') {\n const tgt = rgbaToLinearRgb(target);\n for (const [name, candidate] of getLinearIndex(space)) {\n offer(name, Math.sqrt(squaredDistanceTriple(tgt, candidate)));\n }\n } else if (metric === 'deltaEok') {\n const tgt = rgbaToOklab(target);\n for (const [name, candidate] of getOklabIndex(space)) {\n offer(name, Math.sqrt(deltaEokSquared(tgt, candidate)));\n }\n } else {\n const tgt = rgbaToLab(target);\n const labIndex = getLabIndex(space);\n if (metric === 'deltaE76') {\n for (const [name, candidate] of labIndex) {\n offer(name, Math.sqrt(deltaE76Squared(tgt, candidate)));\n }\n } else if (metric === 'deltaE94') {\n for (const [name, candidate] of labIndex) {\n offer(name, deltaE94(tgt, candidate));\n }\n } else {\n for (const [name, candidate] of labIndex) {\n offer(name, deltaE2000(tgt, candidate));\n }\n }\n }\n\n if (!useBounded) {\n best.sort((a, b) => a.distance - b.distance);\n }\n return best;\n}\n"]}
|
|
@@ -105,6 +105,22 @@ declare const crayolaColors: {
|
|
|
105
105
|
readonly Wisteria: '#cda4de';
|
|
106
106
|
};
|
|
107
107
|
export type CrayolaColorName = keyof typeof crayolaColors;
|
|
108
|
+
/**
|
|
109
|
+
* Crayola crayon-color palette (84 entries), community-curated. Not
|
|
110
|
+
* Crayola-licensed or certified; see `NOTICE.md` for trademark details
|
|
111
|
+
* and the set's scope (classic 64 box + fluorescent neons + selected
|
|
112
|
+
* modern additions).
|
|
113
|
+
*
|
|
114
|
+
* Keys use the Title Case form printed on the crayon wrapper
|
|
115
|
+
* (`'Razzmatazz'`, `'Granny Smith Apple'`). Default metric is `'deltaEok'`:
|
|
116
|
+
* OKLAB's perceptual uniformity better separates the palette's
|
|
117
|
+
* saturated-heavy distribution than ΔE76.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* identify('#e20074', { palette: crayola }); // 'Razzmatazz'
|
|
121
|
+
* resolve('Granny Smith Apple', { palette: crayola }); // '#a8e4a0'
|
|
122
|
+
* crayola.colors.Razzmatazz; // '#e3256b'
|
|
123
|
+
*/
|
|
108
124
|
export declare const crayola: {
|
|
109
125
|
readonly name: 'crayola';
|
|
110
126
|
readonly colors: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crayola.d.ts","sourceRoot":"","sources":["../../src/palettes/crayola.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,QAAA,MAAM,aAAa;aAEjB,GAAG,EAAE,SAAS;aACd,YAAY,EAAE,SAAS;aACvB,MAAM,EAAE,SAAS;aACjB,eAAe,EAAE,SAAS;aAC1B,MAAM,EAAE,SAAS;aACjB,cAAc,EAAE,SAAS;aACzB,KAAK,EAAE,SAAS;aAChB,YAAY,EAAE,SAAS;aACvB,IAAI,EAAE,SAAS;aACf,aAAa,EAAE,SAAS;aACxB,MAAM,EAAE,SAAS;aACjB,YAAY,EAAE,SAAS;aAGvB,KAAK,EAAE,SAAS;aAChB,KAAK,EAAE,SAAS;aAChB,KAAK,EAAE,SAAS;aAChB,IAAI,EAAE,SAAS;aAGf,gBAAgB,EAAE,SAAS;aAC3B,UAAU,EAAE,SAAS;aACrB,GAAG,EAAE,SAAS;aACd,OAAO,EAAE,SAAS;aAClB,KAAK,EAAE,SAAS;aAChB,YAAY,EAAE,SAAS;aACvB,MAAM,EAAE,SAAS;aACjB,cAAc,EAAE,SAAS;aACzB,cAAc,EAAE,SAAS;aACzB,QAAQ,EAAE,SAAS;aACnB,MAAM,EAAE,SAAS;aACjB,WAAW,EAAE,SAAS;aACtB,KAAK,EAAE,SAAS;aAChB,cAAc,EAAE,SAAS;aACzB,cAAc,EAAE,SAAS;aACzB,YAAY,EAAE,SAAS;aAGvB,MAAM,EAAE,SAAS;aACjB,IAAI,EAAE,SAAS;aACf,MAAM,EAAE,SAAS;aAGjB,cAAc,EAAE,SAAS;aACzB,YAAY,EAAE,SAAS;aACvB,aAAa,EAAE,SAAS;aACxB,MAAM,EAAE,SAAS;aACjB,UAAU,EAAE,SAAS;aACrB,QAAQ,EAAE,SAAS;aACnB,WAAW,EAAE,SAAS;aACtB,gBAAgB,EAAE,SAAS;aAG3B,kBAAkB,EAAE,SAAS;aAC7B,eAAe,EAAE,SAAS;aAC1B,aAAa,EAAE,SAAS;aACxB,aAAa,EAAE,SAAS;aACxB,aAAa,EAAE,SAAS;aACxB,gBAAgB,EAAE,SAAS;aAC3B,oBAAoB,EAAE,SAAS;aAC/B,UAAU,EAAE,SAAS;aACrB,iBAAiB,EAAE,SAAS;aAC5B,eAAe,EAAE,SAAS;aAC1B,gBAAgB,EAAE,SAAS;aAC3B,iBAAiB,EAAE,SAAS;aAC5B,iBAAiB,EAAE,SAAS;aAC5B,mBAAmB,EAAE,SAAS;aAC9B,eAAe,EAAE,SAAS;aAG1B,oBAAoB,EAAE,SAAS;aAC/B,qBAAqB,EAAE,SAAS;aAChC,cAAc,EAAE,SAAS;aACzB,iBAAiB,EAAE,SAAS;aAC5B,MAAM,EAAE,SAAS;aACjB,cAAc,EAAE,SAAS;aACzB,aAAa,EAAE,SAAS;aACxB,QAAQ,EAAE,SAAS;aACnB,eAAe,EAAE,SAAS;aAC1B,OAAO,EAAE,SAAS;aAClB,aAAa,EAAE,SAAS;aACxB,SAAS,EAAE,SAAS;aACpB,eAAe,EAAE,SAAS;aAC1B,WAAW,EAAE,SAAS;aACtB,cAAc,EAAE,SAAS;aACzB,YAAY,EAAE,SAAS;aACvB,IAAI,EAAE,SAAS;aACf,cAAc,EAAE,SAAS;aACzB,kBAAkB,EAAE,SAAS;aAC7B,cAAc,EAAE,SAAS;aACzB,OAAO,EAAE,SAAS;aAClB,UAAU,EAAE,SAAS;aACrB,UAAU,EAAE,SAAS;aACrB,YAAY,EAAE,SAAS;aACvB,cAAc,EAAE,SAAS;aACzB,QAAQ,EAAE,SAAS;CACX,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,aAAa,CAAC;AAE1D,eAAO,MAAM,OAAO;mBACZ,SAAS;;
|
|
1
|
+
{"version":3,"file":"crayola.d.ts","sourceRoot":"","sources":["../../src/palettes/crayola.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Palette } from '../types.js';\nimport { standardNormalize } from './normalize.js';\n\n/**\n * Crayola crayon colors — canonical subset. ~85 entries covering the\n * current 64-standard box, iconic fluorescents, and widely-recognized\n * modern additions.\n *\n * Keys are Title Case with spaces (`'Granny Smith Apple'`,\n * `'Razzmatazz'`) — how the names are printed on the crayon wrapper.\n * `standardNormalize` strips spaces + case + punctuation on input, so\n * `resolve('granny-smith-apple')` and `resolve('Granny Smith Apple')`\n * both hit the same entry.\n *\n * sRGB values are community-sourced approximations (Wikipedia's\n * well-cited list of Crayola crayon colors). They do not represent\n * a licensed Crayola reference. See NOTICE.md for trademark note.\n *\n * Default metric: `deltaEok`. The Crayola set spans a wide gamut with\n * many saturated brights (fluorescents especially) where CIELAB-based\n * metrics are less perceptually uniform than OKLAB. Same cost profile\n * as deltaE76, better neighbor selection in saturated regions.\n */\nconst crayolaColors = {\n // --- Current standard box: the 12 primary/secondary hue wheel ---\n Red: '#ee204d',\n 'Red Orange': '#ff5349',\n Orange: '#ff7538',\n 'Yellow Orange': '#ffae42',\n Yellow: '#fce883',\n 'Yellow Green': '#c5e384',\n Green: '#1cac78',\n 'Blue Green': '#0d98ba',\n Blue: '#1f75fe',\n 'Blue Violet': '#7366bd',\n Violet: '#926eae',\n 'Red Violet': '#c0448f',\n\n // --- Achromatics ---\n Black: '#232323',\n Brown: '#b4674d',\n White: '#ededed',\n Gray: '#95918c',\n\n // --- 64-box additions (pre-1990 core set) ---\n 'Carnation Pink': '#ffaacc',\n 'Sky Blue': '#80daeb',\n Tan: '#faa76c',\n Apricot: '#fdd9b5',\n Peach: '#ffcfab',\n 'Cadet Blue': '#b0b7c6',\n Salmon: '#ff9baa',\n 'Forest Green': '#6dae81',\n 'Lemon Yellow': '#fff44f',\n Mahogany: '#cd4a4c',\n Maroon: '#c8385a',\n Bittersweet: '#fd7c6e',\n Sepia: '#a5694f',\n 'Burnt Sienna': '#ea7e5d',\n 'Burnt Orange': '#ff7f49',\n 'Raw Sienna': '#d68a59',\n\n // --- Metallics ---\n Silver: '#cdc5c2',\n Gold: '#e7c697',\n Copper: '#dd9475',\n\n // --- Earth / nature tones ---\n 'Green Yellow': '#f0e891',\n 'Pine Green': '#158078',\n 'Olive Green': '#bab86c',\n Indigo: '#5d76cb',\n Periwinkle: '#c5d0e6',\n Cerulean: '#1dacd6',\n 'Sea Green': '#9fe2bf',\n 'Turquoise Blue': '#77dde7',\n\n // --- Fluorescents (the iconic \"Neon 24\" era) ---\n 'Atomic Tangerine': '#ff9966',\n 'Blizzard Blue': '#ace5ee',\n 'Hot Magenta': '#ff1dce',\n 'Laser Lemon': '#fdfc74',\n 'Neon Carrot': '#ffa343',\n 'Purple Pizzazz': '#fe4eda',\n 'Razzle Dazzle Rose': '#ff48d0',\n Razzmatazz: '#e3256b',\n \"Screamin' Green\": '#76ff7a',\n 'Shocking Pink': '#fb7efd',\n 'Tickle Me Pink': '#fc89ac',\n 'Unmellow Yellow': '#ffff66',\n 'Wild Watermelon': '#fc6c85',\n 'Outrageous Orange': '#ff6e4a',\n 'Electric Lime': '#ceff1d',\n\n // --- Modern / whimsical additions ---\n 'Granny Smith Apple': '#a8e4a0',\n 'Macaroni and Cheese': '#ffbd88',\n 'Banana Mania': '#fae7b5',\n 'Caribbean Green': '#1cd3a2',\n Cerise: '#dd4492',\n 'Cotton Candy': '#ffbcd9',\n 'Fuzzy Wuzzy': '#cc6666',\n Inchworm: '#b2ec5d',\n 'Jazzberry Jam': '#ca3767',\n Manatee: '#979aaa',\n 'Mango Tango': '#ff8243',\n Mauvelous: '#ef98aa',\n 'Midnight Blue': '#1a4876',\n 'Navy Blue': '#1974d2',\n 'Pacific Blue': '#1ca9c9',\n 'Piggy Pink': '#fddde6',\n Plum: '#8e4585',\n 'Purple Heart': '#7442c8',\n \"Robin's Egg Blue\": '#1fcecb',\n 'Royal Purple': '#7851a9',\n Scarlet: '#fc2847',\n Timberwolf: '#dbd7d2',\n Tumbleweed: '#deaa88',\n 'Violet Red': '#f75394',\n 'Vivid Violet': '#8f509d',\n Wisteria: '#cda4de',\n} as const;\n\nexport type CrayolaColorName = keyof typeof crayolaColors;\n\n/**\n * Crayola crayon-color palette (84 entries), community-curated. Not\n * Crayola-licensed or certified; see `NOTICE.md` for trademark details\n * and the set's scope (classic 64 box + fluorescent neons + selected\n * modern additions).\n *\n * Keys use the Title Case form printed on the crayon wrapper\n * (`'Razzmatazz'`, `'Granny Smith Apple'`). Default metric is `'deltaEok'`:\n * OKLAB's perceptual uniformity better separates the palette's\n * saturated-heavy distribution than ΔE76.\n *\n * @example\n * identify('#e20074', { palette: crayola }); // 'Razzmatazz'\n * resolve('Granny Smith Apple', { palette: crayola }); // '#a8e4a0'\n * crayola.colors.Razzmatazz; // '#e3256b'\n */\nexport const crayola = {\n name: 'crayola',\n colors: crayolaColors,\n normalize: standardNormalize,\n defaultMetric: 'deltaEok',\n} as const satisfies Palette<CrayolaColorName>;\n"]}
|
package/dist/palettes/crayola.js
CHANGED
|
@@ -112,6 +112,22 @@ const crayolaColors = {
|
|
|
112
112
|
'Vivid Violet': '#8f509d',
|
|
113
113
|
Wisteria: '#cda4de',
|
|
114
114
|
};
|
|
115
|
+
/**
|
|
116
|
+
* Crayola crayon-color palette (84 entries), community-curated. Not
|
|
117
|
+
* Crayola-licensed or certified; see `NOTICE.md` for trademark details
|
|
118
|
+
* and the set's scope (classic 64 box + fluorescent neons + selected
|
|
119
|
+
* modern additions).
|
|
120
|
+
*
|
|
121
|
+
* Keys use the Title Case form printed on the crayon wrapper
|
|
122
|
+
* (`'Razzmatazz'`, `'Granny Smith Apple'`). Default metric is `'deltaEok'`:
|
|
123
|
+
* OKLAB's perceptual uniformity better separates the palette's
|
|
124
|
+
* saturated-heavy distribution than ΔE76.
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* identify('#e20074', { palette: crayola }); // 'Razzmatazz'
|
|
128
|
+
* resolve('Granny Smith Apple', { palette: crayola }); // '#a8e4a0'
|
|
129
|
+
* crayola.colors.Razzmatazz; // '#e3256b'
|
|
130
|
+
*/
|
|
115
131
|
export const crayola = {
|
|
116
132
|
name: 'crayola',
|
|
117
133
|
colors: crayolaColors,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crayola.js","sourceRoot":"","sources":["../../src/palettes/crayola.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Palette } from '../types.js';\nimport { standardNormalize } from './normalize.js';\n\n/**\n * Crayola crayon colors — canonical subset. ~85 entries covering the\n * current 64-standard box, iconic fluorescents, and widely-recognized\n * modern additions.\n *\n * Keys are Title Case with spaces (`'Granny Smith Apple'`,\n * `'Razzmatazz'`) — how the names are printed on the crayon wrapper.\n * `standardNormalize` strips spaces + case + punctuation on input, so\n * `resolve('granny-smith-apple')` and `resolve('Granny Smith Apple')`\n * both hit the same entry.\n *\n * sRGB values are community-sourced approximations (Wikipedia's\n * well-cited list of Crayola crayon colors). They do not represent\n * a licensed Crayola reference. See NOTICE.md for trademark note.\n *\n * Default metric: `deltaEok`. The Crayola set spans a wide gamut with\n * many saturated brights (fluorescents especially) where CIELAB-based\n * metrics are less perceptually uniform than OKLAB. Same cost profile\n * as deltaE76, better neighbor selection in saturated regions.\n */\nconst crayolaColors = {\n // --- Current standard box: the 12 primary/secondary hue wheel ---\n Red: '#ee204d',\n 'Red Orange': '#ff5349',\n Orange: '#ff7538',\n 'Yellow Orange': '#ffae42',\n Yellow: '#fce883',\n 'Yellow Green': '#c5e384',\n Green: '#1cac78',\n 'Blue Green': '#0d98ba',\n Blue: '#1f75fe',\n 'Blue Violet': '#7366bd',\n Violet: '#926eae',\n 'Red Violet': '#c0448f',\n\n // --- Achromatics ---\n Black: '#232323',\n Brown: '#b4674d',\n White: '#ededed',\n Gray: '#95918c',\n\n // --- 64-box additions (pre-1990 core set) ---\n 'Carnation Pink': '#ffaacc',\n 'Sky Blue': '#80daeb',\n Tan: '#faa76c',\n Apricot: '#fdd9b5',\n Peach: '#ffcfab',\n 'Cadet Blue': '#b0b7c6',\n Salmon: '#ff9baa',\n 'Forest Green': '#6dae81',\n 'Lemon Yellow': '#fff44f',\n Mahogany: '#cd4a4c',\n Maroon: '#c8385a',\n Bittersweet: '#fd7c6e',\n Sepia: '#a5694f',\n 'Burnt Sienna': '#ea7e5d',\n 'Burnt Orange': '#ff7f49',\n 'Raw Sienna': '#d68a59',\n\n // --- Metallics ---\n Silver: '#cdc5c2',\n Gold: '#e7c697',\n Copper: '#dd9475',\n\n // --- Earth / nature tones ---\n 'Green Yellow': '#f0e891',\n 'Pine Green': '#158078',\n 'Olive Green': '#bab86c',\n Indigo: '#5d76cb',\n Periwinkle: '#c5d0e6',\n Cerulean: '#1dacd6',\n 'Sea Green': '#9fe2bf',\n 'Turquoise Blue': '#77dde7',\n\n // --- Fluorescents (the iconic \"Neon 24\" era) ---\n 'Atomic Tangerine': '#ff9966',\n 'Blizzard Blue': '#ace5ee',\n 'Hot Magenta': '#ff1dce',\n 'Laser Lemon': '#fdfc74',\n 'Neon Carrot': '#ffa343',\n 'Purple Pizzazz': '#fe4eda',\n 'Razzle Dazzle Rose': '#ff48d0',\n Razzmatazz: '#e3256b',\n \"Screamin' Green\": '#76ff7a',\n 'Shocking Pink': '#fb7efd',\n 'Tickle Me Pink': '#fc89ac',\n 'Unmellow Yellow': '#ffff66',\n 'Wild Watermelon': '#fc6c85',\n 'Outrageous Orange': '#ff6e4a',\n 'Electric Lime': '#ceff1d',\n\n // --- Modern / whimsical additions ---\n 'Granny Smith Apple': '#a8e4a0',\n 'Macaroni and Cheese': '#ffbd88',\n 'Banana Mania': '#fae7b5',\n 'Caribbean Green': '#1cd3a2',\n Cerise: '#dd4492',\n 'Cotton Candy': '#ffbcd9',\n 'Fuzzy Wuzzy': '#cc6666',\n Inchworm: '#b2ec5d',\n 'Jazzberry Jam': '#ca3767',\n Manatee: '#979aaa',\n 'Mango Tango': '#ff8243',\n Mauvelous: '#ef98aa',\n 'Midnight Blue': '#1a4876',\n 'Navy Blue': '#1974d2',\n 'Pacific Blue': '#1ca9c9',\n 'Piggy Pink': '#fddde6',\n Plum: '#8e4585',\n 'Purple Heart': '#7442c8',\n \"Robin's Egg Blue\": '#1fcecb',\n 'Royal Purple': '#7851a9',\n Scarlet: '#fc2847',\n Timberwolf: '#dbd7d2',\n Tumbleweed: '#deaa88',\n 'Violet Red': '#f75394',\n 'Vivid Violet': '#8f509d',\n Wisteria: '#cda4de',\n} as const;\n\nexport type CrayolaColorName = keyof typeof crayolaColors;\n\nexport const crayola = {\n name: 'crayola',\n colors: crayolaColors,\n normalize: standardNormalize,\n defaultMetric: 'deltaEok',\n} as const satisfies Palette<CrayolaColorName>;\n"]}
|
|
1
|
+
{"version":3,"file":"crayola.js","sourceRoot":"","sources":["../../src/palettes/crayola.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Palette } from '../types.js';\nimport { standardNormalize } from './normalize.js';\n\n/**\n * Crayola crayon colors — canonical subset. ~85 entries covering the\n * current 64-standard box, iconic fluorescents, and widely-recognized\n * modern additions.\n *\n * Keys are Title Case with spaces (`'Granny Smith Apple'`,\n * `'Razzmatazz'`) — how the names are printed on the crayon wrapper.\n * `standardNormalize` strips spaces + case + punctuation on input, so\n * `resolve('granny-smith-apple')` and `resolve('Granny Smith Apple')`\n * both hit the same entry.\n *\n * sRGB values are community-sourced approximations (Wikipedia's\n * well-cited list of Crayola crayon colors). They do not represent\n * a licensed Crayola reference. See NOTICE.md for trademark note.\n *\n * Default metric: `deltaEok`. The Crayola set spans a wide gamut with\n * many saturated brights (fluorescents especially) where CIELAB-based\n * metrics are less perceptually uniform than OKLAB. Same cost profile\n * as deltaE76, better neighbor selection in saturated regions.\n */\nconst crayolaColors = {\n // --- Current standard box: the 12 primary/secondary hue wheel ---\n Red: '#ee204d',\n 'Red Orange': '#ff5349',\n Orange: '#ff7538',\n 'Yellow Orange': '#ffae42',\n Yellow: '#fce883',\n 'Yellow Green': '#c5e384',\n Green: '#1cac78',\n 'Blue Green': '#0d98ba',\n Blue: '#1f75fe',\n 'Blue Violet': '#7366bd',\n Violet: '#926eae',\n 'Red Violet': '#c0448f',\n\n // --- Achromatics ---\n Black: '#232323',\n Brown: '#b4674d',\n White: '#ededed',\n Gray: '#95918c',\n\n // --- 64-box additions (pre-1990 core set) ---\n 'Carnation Pink': '#ffaacc',\n 'Sky Blue': '#80daeb',\n Tan: '#faa76c',\n Apricot: '#fdd9b5',\n Peach: '#ffcfab',\n 'Cadet Blue': '#b0b7c6',\n Salmon: '#ff9baa',\n 'Forest Green': '#6dae81',\n 'Lemon Yellow': '#fff44f',\n Mahogany: '#cd4a4c',\n Maroon: '#c8385a',\n Bittersweet: '#fd7c6e',\n Sepia: '#a5694f',\n 'Burnt Sienna': '#ea7e5d',\n 'Burnt Orange': '#ff7f49',\n 'Raw Sienna': '#d68a59',\n\n // --- Metallics ---\n Silver: '#cdc5c2',\n Gold: '#e7c697',\n Copper: '#dd9475',\n\n // --- Earth / nature tones ---\n 'Green Yellow': '#f0e891',\n 'Pine Green': '#158078',\n 'Olive Green': '#bab86c',\n Indigo: '#5d76cb',\n Periwinkle: '#c5d0e6',\n Cerulean: '#1dacd6',\n 'Sea Green': '#9fe2bf',\n 'Turquoise Blue': '#77dde7',\n\n // --- Fluorescents (the iconic \"Neon 24\" era) ---\n 'Atomic Tangerine': '#ff9966',\n 'Blizzard Blue': '#ace5ee',\n 'Hot Magenta': '#ff1dce',\n 'Laser Lemon': '#fdfc74',\n 'Neon Carrot': '#ffa343',\n 'Purple Pizzazz': '#fe4eda',\n 'Razzle Dazzle Rose': '#ff48d0',\n Razzmatazz: '#e3256b',\n \"Screamin' Green\": '#76ff7a',\n 'Shocking Pink': '#fb7efd',\n 'Tickle Me Pink': '#fc89ac',\n 'Unmellow Yellow': '#ffff66',\n 'Wild Watermelon': '#fc6c85',\n 'Outrageous Orange': '#ff6e4a',\n 'Electric Lime': '#ceff1d',\n\n // --- Modern / whimsical additions ---\n 'Granny Smith Apple': '#a8e4a0',\n 'Macaroni and Cheese': '#ffbd88',\n 'Banana Mania': '#fae7b5',\n 'Caribbean Green': '#1cd3a2',\n Cerise: '#dd4492',\n 'Cotton Candy': '#ffbcd9',\n 'Fuzzy Wuzzy': '#cc6666',\n Inchworm: '#b2ec5d',\n 'Jazzberry Jam': '#ca3767',\n Manatee: '#979aaa',\n 'Mango Tango': '#ff8243',\n Mauvelous: '#ef98aa',\n 'Midnight Blue': '#1a4876',\n 'Navy Blue': '#1974d2',\n 'Pacific Blue': '#1ca9c9',\n 'Piggy Pink': '#fddde6',\n Plum: '#8e4585',\n 'Purple Heart': '#7442c8',\n \"Robin's Egg Blue\": '#1fcecb',\n 'Royal Purple': '#7851a9',\n Scarlet: '#fc2847',\n Timberwolf: '#dbd7d2',\n Tumbleweed: '#deaa88',\n 'Violet Red': '#f75394',\n 'Vivid Violet': '#8f509d',\n Wisteria: '#cda4de',\n} as const;\n\nexport type CrayolaColorName = keyof typeof crayolaColors;\n\n/**\n * Crayola crayon-color palette (84 entries), community-curated. Not\n * Crayola-licensed or certified; see `NOTICE.md` for trademark details\n * and the set's scope (classic 64 box + fluorescent neons + selected\n * modern additions).\n *\n * Keys use the Title Case form printed on the crayon wrapper\n * (`'Razzmatazz'`, `'Granny Smith Apple'`). Default metric is `'deltaEok'`:\n * OKLAB's perceptual uniformity better separates the palette's\n * saturated-heavy distribution than ΔE76.\n *\n * @example\n * identify('#e20074', { palette: crayola }); // 'Razzmatazz'\n * resolve('Granny Smith Apple', { palette: crayola }); // '#a8e4a0'\n * crayola.colors.Razzmatazz; // '#e3256b'\n */\nexport const crayola = {\n name: 'crayola',\n colors: crayolaColors,\n normalize: standardNormalize,\n defaultMetric: 'deltaEok',\n} as const satisfies Palette<CrayolaColorName>;\n"]}
|