chromonym 2.0.0 → 3.0.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 +32 -0
- package/README.md +138 -72
- package/dist/identify.d.ts +36 -11
- package/dist/identify.d.ts.map +1 -1
- package/dist/identify.js +34 -19
- package/dist/identify.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/indexing.d.ts +18 -0
- package/dist/indexing.d.ts.map +1 -1
- package/dist/indexing.js +62 -0
- package/dist/indexing.js.map +1 -1
- package/dist/math/editDistance.d.ts +17 -0
- package/dist/math/editDistance.d.ts.map +1 -0
- package/dist/math/editDistance.js +48 -0
- package/dist/math/editDistance.js.map +1 -0
- package/dist/palettes/crayola.d.ts +200 -0
- package/dist/palettes/crayola.d.ts.map +1 -0
- package/dist/palettes/crayola.js +121 -0
- package/dist/palettes/crayola.js.map +1 -0
- package/dist/palettes/index.d.ts +1 -0
- package/dist/palettes/index.d.ts.map +1 -1
- package/dist/palettes/index.js +1 -0
- package/dist/palettes/index.js.map +1 -1
- package/dist/resolve.d.ts +39 -6
- package/dist/resolve.d.ts.map +1 -1
- package/dist/resolve.js +39 -12
- package/dist/resolve.js.map +1 -1
- package/package.json +8 -2
package/dist/indexing.d.ts
CHANGED
|
@@ -47,4 +47,22 @@ export declare function nearestByRgb(target: Rgba, space: AnyPalette): string;
|
|
|
47
47
|
* hide the distance-metric choice from the hot path.
|
|
48
48
|
*/
|
|
49
49
|
export declare function nearest(target: Rgba, space: AnyPalette, metric: DistanceMetric): string;
|
|
50
|
+
/**
|
|
51
|
+
* Rank every palette entry by distance to `target`. Returns an array of
|
|
52
|
+
* `{ name, distance }` sorted ascending (nearest first). Optional `k`
|
|
53
|
+
* truncates to the top-k matches.
|
|
54
|
+
*
|
|
55
|
+
* Distances are **real** metric values (not squared) — ΔE76 / ΔE94 /
|
|
56
|
+
* ΔE2000 / ΔE-OK are in standard ΔE units (≈1 = just-noticeable for
|
|
57
|
+
* most of the gamut on ΔE76/2000); `euclidean-srgb` / `euclidean-linear`
|
|
58
|
+
* are in their respective channel-unit Euclidean distances.
|
|
59
|
+
*
|
|
60
|
+
* Cost: O(n log n) for the sort vs O(n) for `nearest` — acceptable for
|
|
61
|
+
* top-K queries. For strict "what's the single nearest?" stay on
|
|
62
|
+
* `nearest` / `identify`.
|
|
63
|
+
*/
|
|
64
|
+
export declare function nearestAll(target: Rgba, space: AnyPalette, metric: DistanceMetric, k?: number): Array<{
|
|
65
|
+
name: string;
|
|
66
|
+
distance: number;
|
|
67
|
+
}>;
|
|
50
68
|
//# sourceMappingURL=indexing.d.ts.map
|
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,EAAY,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvF;;;;;;GAMG;AAKH,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC9E,YAAY,EAAE,WAAW,EAAE,CAAC;AAE5B,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;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAS9E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,CAYpE;AAsED;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAavF","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, HexColor, NormalizeFn, 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\n// Re-export normalizers from their own module for backward compat with any\n// caller that expected them here. The functions live in ./palettes/normalize\n// so palette modules can import them without pulling this file's math graph.\nexport { pantoneNormalize, standardNormalize } from './palettes/normalize.js';\nexport type { NormalizeFn };\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 */\nexport function getRgbaIndex(space: AnyPalette): Array<readonly [string, Rgba]> {\n let idx = rgbaIndexCache.get(space);\n if (idx === undefined) {\n idx = Object.entries(space.colors).map(\n ([name, hex]) => [name, hexToRgba(hex as HexColor)] as const,\n );\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 */\nexport function 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"]}
|
|
1
|
+
{"version":3,"file":"indexing.d.ts","sourceRoot":"","sources":["../src/indexing.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAY,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvF;;;;;;GAMG;AAKH,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC9E,YAAY,EAAE,WAAW,EAAE,CAAC;AAE5B,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;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAS9E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,CAYpE;AAsED;;;;;;;;;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, HexColor, NormalizeFn, 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\n// Re-export normalizers from their own module for backward compat with any\n// caller that expected them here. The functions live in ./palettes/normalize\n// so palette modules can import them without pulling this file's math graph.\nexport { pantoneNormalize, standardNormalize } from './palettes/normalize.js';\nexport type { NormalizeFn };\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 */\nexport function getRgbaIndex(space: AnyPalette): Array<readonly [string, Rgba]> {\n let idx = rgbaIndexCache.get(space);\n if (idx === undefined) {\n idx = Object.entries(space.colors).map(\n ([name, hex]) => [name, hexToRgba(hex as HexColor)] as const,\n );\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 */\nexport function 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"]}
|
package/dist/indexing.js
CHANGED
|
@@ -164,4 +164,66 @@ export function nearest(target, space, metric) {
|
|
|
164
164
|
return argminBy(labIndex, labTarget, deltaE94);
|
|
165
165
|
return argminBy(labIndex, labTarget, deltaE2000);
|
|
166
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* Rank every palette entry by distance to `target`. Returns an array of
|
|
169
|
+
* `{ name, distance }` sorted ascending (nearest first). Optional `k`
|
|
170
|
+
* truncates to the top-k matches.
|
|
171
|
+
*
|
|
172
|
+
* Distances are **real** metric values (not squared) — ΔE76 / ΔE94 /
|
|
173
|
+
* ΔE2000 / ΔE-OK are in standard ΔE units (≈1 = just-noticeable for
|
|
174
|
+
* most of the gamut on ΔE76/2000); `euclidean-srgb` / `euclidean-linear`
|
|
175
|
+
* are in their respective channel-unit Euclidean distances.
|
|
176
|
+
*
|
|
177
|
+
* Cost: O(n log n) for the sort vs O(n) for `nearest` — acceptable for
|
|
178
|
+
* top-K queries. For strict "what's the single nearest?" stay on
|
|
179
|
+
* `nearest` / `identify`.
|
|
180
|
+
*/
|
|
181
|
+
export function nearestAll(target, space, metric, k) {
|
|
182
|
+
const ranked = [];
|
|
183
|
+
if (metric === 'euclidean-srgb') {
|
|
184
|
+
for (const [name, candidate] of getRgbaIndex(space)) {
|
|
185
|
+
ranked.push([name, Math.sqrt(squaredDistanceRgb(target, candidate))]);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
else if (metric === 'euclidean-linear') {
|
|
189
|
+
const tgt = rgbaToLinearRgb(target);
|
|
190
|
+
for (const [name, candidate] of getLinearIndex(space)) {
|
|
191
|
+
ranked.push([name, Math.sqrt(squaredDistanceTriple(tgt, candidate))]);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
else if (metric === 'deltaEok') {
|
|
195
|
+
const tgt = rgbaToOklab(target);
|
|
196
|
+
for (const [name, candidate] of getOklabIndex(space)) {
|
|
197
|
+
ranked.push([name, Math.sqrt(deltaEokSquared(tgt, candidate))]);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
const tgt = rgbaToLab(target);
|
|
202
|
+
const labIndex = getLabIndex(space);
|
|
203
|
+
if (metric === 'deltaE76') {
|
|
204
|
+
for (const [name, candidate] of labIndex) {
|
|
205
|
+
ranked.push([name, Math.sqrt(deltaE76Squared(tgt, candidate))]);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
else if (metric === 'deltaE94') {
|
|
209
|
+
for (const [name, candidate] of labIndex) {
|
|
210
|
+
ranked.push([name, deltaE94(tgt, candidate)]);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
for (const [name, candidate] of labIndex) {
|
|
215
|
+
ranked.push([name, deltaE2000(tgt, candidate)]);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
ranked.sort((a, b) => a[1] - b[1]);
|
|
220
|
+
const limit = k === undefined ? ranked.length : Math.min(Math.max(k, 0), ranked.length);
|
|
221
|
+
const result = new Array(limit);
|
|
222
|
+
for (let i = 0; i < limit; i++) {
|
|
223
|
+
const entry = ranked[i];
|
|
224
|
+
if (entry !== undefined)
|
|
225
|
+
result[i] = { name: entry[0], distance: entry[1] };
|
|
226
|
+
}
|
|
227
|
+
return result;
|
|
228
|
+
}
|
|
167
229
|
//# 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":"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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, HexColor, NormalizeFn, 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\n// Re-export normalizers from their own module for backward compat with any\n// caller that expected them here. The functions live in ./palettes/normalize\n// so palette modules can import them without pulling this file's math graph.\nexport { pantoneNormalize, standardNormalize } from './palettes/normalize.js';\nexport type { NormalizeFn };\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 */\nexport function getRgbaIndex(space: AnyPalette): Array<readonly [string, Rgba]> {\n let idx = rgbaIndexCache.get(space);\n if (idx === undefined) {\n idx = Object.entries(space.colors).map(\n ([name, hex]) => [name, hexToRgba(hex as HexColor)] as const,\n );\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 */\nexport function 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"]}
|
|
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, HexColor, NormalizeFn, 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\n// Re-export normalizers from their own module for backward compat with any\n// caller that expected them here. The functions live in ./palettes/normalize\n// so palette modules can import them without pulling this file's math graph.\nexport { pantoneNormalize, standardNormalize } from './palettes/normalize.js';\nexport type { NormalizeFn };\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 */\nexport function getRgbaIndex(space: AnyPalette): Array<readonly [string, Rgba]> {\n let idx = rgbaIndexCache.get(space);\n if (idx === undefined) {\n idx = Object.entries(space.colors).map(\n ([name, hex]) => [name, hexToRgba(hex as HexColor)] as const,\n );\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 */\nexport function 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"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Levenshtein edit distance between two strings. Classic dynamic-programming
|
|
3
|
+
* implementation; returns the minimum number of single-character insertions,
|
|
4
|
+
* deletions, or substitutions required to transform `a` into `b`.
|
|
5
|
+
*
|
|
6
|
+
* Used by `resolve(_, { k })` for fuzzy-name matching — operating on the
|
|
7
|
+
* palette's already-normalized keys (lowercase, alphanumeric) so typos,
|
|
8
|
+
* casing, and punctuation all reduce to the same canonical forms before
|
|
9
|
+
* the edit distance is measured.
|
|
10
|
+
*
|
|
11
|
+
* Space-optimized: uses two rolling rows instead of a full n×m matrix
|
|
12
|
+
* (O(min(n,m)) space, O(n·m) time). For palette keys (< 20 chars each)
|
|
13
|
+
* this runs sub-microsecond per pair, so a full scan over 907 Pantone
|
|
14
|
+
* entries fits well inside a millisecond.
|
|
15
|
+
*/
|
|
16
|
+
export declare function levenshtein(a: string, b: string): number;
|
|
17
|
+
//# sourceMappingURL=editDistance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editDistance.d.ts","sourceRoot":"","sources":["../../src/math/editDistance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CA8BxD","sourcesContent":["/**\n * Levenshtein edit distance between two strings. Classic dynamic-programming\n * implementation; returns the minimum number of single-character insertions,\n * deletions, or substitutions required to transform `a` into `b`.\n *\n * Used by `resolve(_, { k })` for fuzzy-name matching — operating on the\n * palette's already-normalized keys (lowercase, alphanumeric) so typos,\n * casing, and punctuation all reduce to the same canonical forms before\n * the edit distance is measured.\n *\n * Space-optimized: uses two rolling rows instead of a full n×m matrix\n * (O(min(n,m)) space, O(n·m) time). For palette keys (< 20 chars each)\n * this runs sub-microsecond per pair, so a full scan over 907 Pantone\n * entries fits well inside a millisecond.\n */\nexport function levenshtein(a: string, b: string): number {\n if (a === b) return 0;\n if (a.length === 0) return b.length;\n if (b.length === 0) return a.length;\n\n // Ensure `a` is the shorter string so the rolling row is minimal.\n if (a.length > b.length) [a, b] = [b, a];\n\n const aLen = a.length;\n const bLen = b.length;\n\n // `prev[i]` = edit distance between a[0..i] and b[0..j-1] (previous row).\n // `curr[i]` = edit distance between a[0..i] and b[0..j] (current row).\n let prev = new Array<number>(aLen + 1);\n let curr = new Array<number>(aLen + 1);\n for (let i = 0; i <= aLen; i++) prev[i] = i;\n\n for (let j = 1; j <= bLen; j++) {\n curr[0] = j;\n const bj = b.charCodeAt(j - 1);\n for (let i = 1; i <= aLen; i++) {\n const cost = a.charCodeAt(i - 1) === bj ? 0 : 1;\n const deletion = (prev[i] ?? 0) + 1;\n const insertion = (curr[i - 1] ?? 0) + 1;\n const substitution = (prev[i - 1] ?? 0) + cost;\n curr[i] = Math.min(deletion, insertion, substitution);\n }\n [prev, curr] = [curr, prev];\n }\n return prev[aLen] ?? 0;\n}\n"]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Levenshtein edit distance between two strings. Classic dynamic-programming
|
|
3
|
+
* implementation; returns the minimum number of single-character insertions,
|
|
4
|
+
* deletions, or substitutions required to transform `a` into `b`.
|
|
5
|
+
*
|
|
6
|
+
* Used by `resolve(_, { k })` for fuzzy-name matching — operating on the
|
|
7
|
+
* palette's already-normalized keys (lowercase, alphanumeric) so typos,
|
|
8
|
+
* casing, and punctuation all reduce to the same canonical forms before
|
|
9
|
+
* the edit distance is measured.
|
|
10
|
+
*
|
|
11
|
+
* Space-optimized: uses two rolling rows instead of a full n×m matrix
|
|
12
|
+
* (O(min(n,m)) space, O(n·m) time). For palette keys (< 20 chars each)
|
|
13
|
+
* this runs sub-microsecond per pair, so a full scan over 907 Pantone
|
|
14
|
+
* entries fits well inside a millisecond.
|
|
15
|
+
*/
|
|
16
|
+
export function levenshtein(a, b) {
|
|
17
|
+
if (a === b)
|
|
18
|
+
return 0;
|
|
19
|
+
if (a.length === 0)
|
|
20
|
+
return b.length;
|
|
21
|
+
if (b.length === 0)
|
|
22
|
+
return a.length;
|
|
23
|
+
// Ensure `a` is the shorter string so the rolling row is minimal.
|
|
24
|
+
if (a.length > b.length)
|
|
25
|
+
[a, b] = [b, a];
|
|
26
|
+
const aLen = a.length;
|
|
27
|
+
const bLen = b.length;
|
|
28
|
+
// `prev[i]` = edit distance between a[0..i] and b[0..j-1] (previous row).
|
|
29
|
+
// `curr[i]` = edit distance between a[0..i] and b[0..j] (current row).
|
|
30
|
+
let prev = new Array(aLen + 1);
|
|
31
|
+
let curr = new Array(aLen + 1);
|
|
32
|
+
for (let i = 0; i <= aLen; i++)
|
|
33
|
+
prev[i] = i;
|
|
34
|
+
for (let j = 1; j <= bLen; j++) {
|
|
35
|
+
curr[0] = j;
|
|
36
|
+
const bj = b.charCodeAt(j - 1);
|
|
37
|
+
for (let i = 1; i <= aLen; i++) {
|
|
38
|
+
const cost = a.charCodeAt(i - 1) === bj ? 0 : 1;
|
|
39
|
+
const deletion = (prev[i] ?? 0) + 1;
|
|
40
|
+
const insertion = (curr[i - 1] ?? 0) + 1;
|
|
41
|
+
const substitution = (prev[i - 1] ?? 0) + cost;
|
|
42
|
+
curr[i] = Math.min(deletion, insertion, substitution);
|
|
43
|
+
}
|
|
44
|
+
[prev, curr] = [curr, prev];
|
|
45
|
+
}
|
|
46
|
+
return prev[aLen] ?? 0;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=editDistance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editDistance.js","sourceRoot":"","sources":["../../src/math/editDistance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,WAAW,CAAC,CAAS,EAAE,CAAS;IAC9C,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACtB,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC,MAAM,CAAC;IACpC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC,MAAM,CAAC;IAEpC,kEAAkE;IAClE,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM;QAAE,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAEzC,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC;IACtB,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC;IAEtB,0EAA0E;IAC1E,yEAAyE;IACzE,IAAI,IAAI,GAAG,IAAI,KAAK,CAAS,IAAI,GAAG,CAAC,CAAC,CAAC;IACvC,IAAI,IAAI,GAAG,IAAI,KAAK,CAAS,IAAI,GAAG,CAAC,CAAC,CAAC;IACvC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE;QAAE,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAE5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACZ,MAAM,EAAE,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/B,MAAM,IAAI,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAChD,MAAM,QAAQ,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;YACpC,MAAM,SAAS,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;YACzC,MAAM,YAAY,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;YAC/C,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;QACxD,CAAC;QACD,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACzB,CAAC","sourcesContent":["/**\n * Levenshtein edit distance between two strings. Classic dynamic-programming\n * implementation; returns the minimum number of single-character insertions,\n * deletions, or substitutions required to transform `a` into `b`.\n *\n * Used by `resolve(_, { k })` for fuzzy-name matching — operating on the\n * palette's already-normalized keys (lowercase, alphanumeric) so typos,\n * casing, and punctuation all reduce to the same canonical forms before\n * the edit distance is measured.\n *\n * Space-optimized: uses two rolling rows instead of a full n×m matrix\n * (O(min(n,m)) space, O(n·m) time). For palette keys (< 20 chars each)\n * this runs sub-microsecond per pair, so a full scan over 907 Pantone\n * entries fits well inside a millisecond.\n */\nexport function levenshtein(a: string, b: string): number {\n if (a === b) return 0;\n if (a.length === 0) return b.length;\n if (b.length === 0) return a.length;\n\n // Ensure `a` is the shorter string so the rolling row is minimal.\n if (a.length > b.length) [a, b] = [b, a];\n\n const aLen = a.length;\n const bLen = b.length;\n\n // `prev[i]` = edit distance between a[0..i] and b[0..j-1] (previous row).\n // `curr[i]` = edit distance between a[0..i] and b[0..j] (current row).\n let prev = new Array<number>(aLen + 1);\n let curr = new Array<number>(aLen + 1);\n for (let i = 0; i <= aLen; i++) prev[i] = i;\n\n for (let j = 1; j <= bLen; j++) {\n curr[0] = j;\n const bj = b.charCodeAt(j - 1);\n for (let i = 1; i <= aLen; i++) {\n const cost = a.charCodeAt(i - 1) === bj ? 0 : 1;\n const deletion = (prev[i] ?? 0) + 1;\n const insertion = (curr[i - 1] ?? 0) + 1;\n const substitution = (prev[i - 1] ?? 0) + cost;\n curr[i] = Math.min(deletion, insertion, substitution);\n }\n [prev, curr] = [curr, prev];\n }\n return prev[aLen] ?? 0;\n}\n"]}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crayola crayon colors — canonical subset. ~85 entries covering the
|
|
3
|
+
* current 64-standard box, iconic fluorescents, and widely-recognized
|
|
4
|
+
* modern additions.
|
|
5
|
+
*
|
|
6
|
+
* Keys are Title Case with spaces (`'Granny Smith Apple'`,
|
|
7
|
+
* `'Razzmatazz'`) — how the names are printed on the crayon wrapper.
|
|
8
|
+
* `standardNormalize` strips spaces + case + punctuation on input, so
|
|
9
|
+
* `resolve('granny-smith-apple')` and `resolve('Granny Smith Apple')`
|
|
10
|
+
* both hit the same entry.
|
|
11
|
+
*
|
|
12
|
+
* sRGB values are community-sourced approximations (Wikipedia's
|
|
13
|
+
* well-cited list of Crayola crayon colors). They do not represent
|
|
14
|
+
* a licensed Crayola reference. See NOTICE.md for trademark note.
|
|
15
|
+
*
|
|
16
|
+
* Default metric: `deltaEok`. The Crayola set spans a wide gamut with
|
|
17
|
+
* many saturated brights (fluorescents especially) where CIELAB-based
|
|
18
|
+
* metrics are less perceptually uniform than OKLAB. Same cost profile
|
|
19
|
+
* as deltaE76, better neighbor selection in saturated regions.
|
|
20
|
+
*/
|
|
21
|
+
declare const crayolaColors: {
|
|
22
|
+
readonly Red: '#ee204d';
|
|
23
|
+
readonly 'Red Orange': '#ff5349';
|
|
24
|
+
readonly Orange: '#ff7538';
|
|
25
|
+
readonly 'Yellow Orange': '#ffae42';
|
|
26
|
+
readonly Yellow: '#fce883';
|
|
27
|
+
readonly 'Yellow Green': '#c5e384';
|
|
28
|
+
readonly Green: '#1cac78';
|
|
29
|
+
readonly 'Blue Green': '#0d98ba';
|
|
30
|
+
readonly Blue: '#1f75fe';
|
|
31
|
+
readonly 'Blue Violet': '#7366bd';
|
|
32
|
+
readonly Violet: '#926eae';
|
|
33
|
+
readonly 'Red Violet': '#c0448f';
|
|
34
|
+
readonly Black: '#232323';
|
|
35
|
+
readonly Brown: '#b4674d';
|
|
36
|
+
readonly White: '#ededed';
|
|
37
|
+
readonly Gray: '#95918c';
|
|
38
|
+
readonly 'Carnation Pink': '#ffaacc';
|
|
39
|
+
readonly 'Sky Blue': '#80daeb';
|
|
40
|
+
readonly Tan: '#faa76c';
|
|
41
|
+
readonly Apricot: '#fdd9b5';
|
|
42
|
+
readonly Peach: '#ffcfab';
|
|
43
|
+
readonly 'Cadet Blue': '#b0b7c6';
|
|
44
|
+
readonly Salmon: '#ff9baa';
|
|
45
|
+
readonly 'Forest Green': '#6dae81';
|
|
46
|
+
readonly 'Lemon Yellow': '#fff44f';
|
|
47
|
+
readonly Mahogany: '#cd4a4c';
|
|
48
|
+
readonly Maroon: '#c8385a';
|
|
49
|
+
readonly Bittersweet: '#fd7c6e';
|
|
50
|
+
readonly Sepia: '#a5694f';
|
|
51
|
+
readonly 'Burnt Sienna': '#ea7e5d';
|
|
52
|
+
readonly 'Burnt Orange': '#ff7f49';
|
|
53
|
+
readonly 'Raw Sienna': '#d68a59';
|
|
54
|
+
readonly Silver: '#cdc5c2';
|
|
55
|
+
readonly Gold: '#e7c697';
|
|
56
|
+
readonly Copper: '#dd9475';
|
|
57
|
+
readonly 'Green Yellow': '#f0e891';
|
|
58
|
+
readonly 'Pine Green': '#158078';
|
|
59
|
+
readonly 'Olive Green': '#bab86c';
|
|
60
|
+
readonly Indigo: '#5d76cb';
|
|
61
|
+
readonly Periwinkle: '#c5d0e6';
|
|
62
|
+
readonly Cerulean: '#1dacd6';
|
|
63
|
+
readonly 'Sea Green': '#9fe2bf';
|
|
64
|
+
readonly 'Turquoise Blue': '#77dde7';
|
|
65
|
+
readonly 'Atomic Tangerine': '#ff9966';
|
|
66
|
+
readonly 'Blizzard Blue': '#ace5ee';
|
|
67
|
+
readonly 'Hot Magenta': '#ff1dce';
|
|
68
|
+
readonly 'Laser Lemon': '#fdfc74';
|
|
69
|
+
readonly 'Neon Carrot': '#ffa343';
|
|
70
|
+
readonly 'Purple Pizzazz': '#fe4eda';
|
|
71
|
+
readonly 'Razzle Dazzle Rose': '#ff48d0';
|
|
72
|
+
readonly Razzmatazz: '#e3256b';
|
|
73
|
+
readonly "Screamin' Green": '#76ff7a';
|
|
74
|
+
readonly 'Shocking Pink': '#fb7efd';
|
|
75
|
+
readonly 'Tickle Me Pink': '#fc89ac';
|
|
76
|
+
readonly 'Unmellow Yellow': '#ffff66';
|
|
77
|
+
readonly 'Wild Watermelon': '#fc6c85';
|
|
78
|
+
readonly 'Outrageous Orange': '#ff6e4a';
|
|
79
|
+
readonly 'Electric Lime': '#ceff1d';
|
|
80
|
+
readonly 'Granny Smith Apple': '#a8e4a0';
|
|
81
|
+
readonly 'Macaroni and Cheese': '#ffbd88';
|
|
82
|
+
readonly 'Banana Mania': '#fae7b5';
|
|
83
|
+
readonly 'Caribbean Green': '#1cd3a2';
|
|
84
|
+
readonly Cerise: '#dd4492';
|
|
85
|
+
readonly 'Cotton Candy': '#ffbcd9';
|
|
86
|
+
readonly 'Fuzzy Wuzzy': '#cc6666';
|
|
87
|
+
readonly Inchworm: '#b2ec5d';
|
|
88
|
+
readonly 'Jazzberry Jam': '#ca3767';
|
|
89
|
+
readonly Manatee: '#979aaa';
|
|
90
|
+
readonly 'Mango Tango': '#ff8243';
|
|
91
|
+
readonly Mauvelous: '#ef98aa';
|
|
92
|
+
readonly 'Midnight Blue': '#1a4876';
|
|
93
|
+
readonly 'Navy Blue': '#1974d2';
|
|
94
|
+
readonly 'Pacific Blue': '#1ca9c9';
|
|
95
|
+
readonly 'Piggy Pink': '#fddde6';
|
|
96
|
+
readonly Plum: '#8e4585';
|
|
97
|
+
readonly 'Purple Heart': '#7442c8';
|
|
98
|
+
readonly "Robin's Egg Blue": '#1fcecb';
|
|
99
|
+
readonly 'Royal Purple': '#7851a9';
|
|
100
|
+
readonly Scarlet: '#fc2847';
|
|
101
|
+
readonly Timberwolf: '#dbd7d2';
|
|
102
|
+
readonly Tumbleweed: '#deaa88';
|
|
103
|
+
readonly 'Violet Red': '#f75394';
|
|
104
|
+
readonly 'Vivid Violet': '#8f509d';
|
|
105
|
+
readonly Wisteria: '#cda4de';
|
|
106
|
+
};
|
|
107
|
+
export type CrayolaColorName = keyof typeof crayolaColors;
|
|
108
|
+
export declare const crayola: {
|
|
109
|
+
readonly name: 'crayola';
|
|
110
|
+
readonly colors: {
|
|
111
|
+
readonly Red: '#ee204d';
|
|
112
|
+
readonly 'Red Orange': '#ff5349';
|
|
113
|
+
readonly Orange: '#ff7538';
|
|
114
|
+
readonly 'Yellow Orange': '#ffae42';
|
|
115
|
+
readonly Yellow: '#fce883';
|
|
116
|
+
readonly 'Yellow Green': '#c5e384';
|
|
117
|
+
readonly Green: '#1cac78';
|
|
118
|
+
readonly 'Blue Green': '#0d98ba';
|
|
119
|
+
readonly Blue: '#1f75fe';
|
|
120
|
+
readonly 'Blue Violet': '#7366bd';
|
|
121
|
+
readonly Violet: '#926eae';
|
|
122
|
+
readonly 'Red Violet': '#c0448f';
|
|
123
|
+
readonly Black: '#232323';
|
|
124
|
+
readonly Brown: '#b4674d';
|
|
125
|
+
readonly White: '#ededed';
|
|
126
|
+
readonly Gray: '#95918c';
|
|
127
|
+
readonly 'Carnation Pink': '#ffaacc';
|
|
128
|
+
readonly 'Sky Blue': '#80daeb';
|
|
129
|
+
readonly Tan: '#faa76c';
|
|
130
|
+
readonly Apricot: '#fdd9b5';
|
|
131
|
+
readonly Peach: '#ffcfab';
|
|
132
|
+
readonly 'Cadet Blue': '#b0b7c6';
|
|
133
|
+
readonly Salmon: '#ff9baa';
|
|
134
|
+
readonly 'Forest Green': '#6dae81';
|
|
135
|
+
readonly 'Lemon Yellow': '#fff44f';
|
|
136
|
+
readonly Mahogany: '#cd4a4c';
|
|
137
|
+
readonly Maroon: '#c8385a';
|
|
138
|
+
readonly Bittersweet: '#fd7c6e';
|
|
139
|
+
readonly Sepia: '#a5694f';
|
|
140
|
+
readonly 'Burnt Sienna': '#ea7e5d';
|
|
141
|
+
readonly 'Burnt Orange': '#ff7f49';
|
|
142
|
+
readonly 'Raw Sienna': '#d68a59';
|
|
143
|
+
readonly Silver: '#cdc5c2';
|
|
144
|
+
readonly Gold: '#e7c697';
|
|
145
|
+
readonly Copper: '#dd9475';
|
|
146
|
+
readonly 'Green Yellow': '#f0e891';
|
|
147
|
+
readonly 'Pine Green': '#158078';
|
|
148
|
+
readonly 'Olive Green': '#bab86c';
|
|
149
|
+
readonly Indigo: '#5d76cb';
|
|
150
|
+
readonly Periwinkle: '#c5d0e6';
|
|
151
|
+
readonly Cerulean: '#1dacd6';
|
|
152
|
+
readonly 'Sea Green': '#9fe2bf';
|
|
153
|
+
readonly 'Turquoise Blue': '#77dde7';
|
|
154
|
+
readonly 'Atomic Tangerine': '#ff9966';
|
|
155
|
+
readonly 'Blizzard Blue': '#ace5ee';
|
|
156
|
+
readonly 'Hot Magenta': '#ff1dce';
|
|
157
|
+
readonly 'Laser Lemon': '#fdfc74';
|
|
158
|
+
readonly 'Neon Carrot': '#ffa343';
|
|
159
|
+
readonly 'Purple Pizzazz': '#fe4eda';
|
|
160
|
+
readonly 'Razzle Dazzle Rose': '#ff48d0';
|
|
161
|
+
readonly Razzmatazz: '#e3256b';
|
|
162
|
+
readonly "Screamin' Green": '#76ff7a';
|
|
163
|
+
readonly 'Shocking Pink': '#fb7efd';
|
|
164
|
+
readonly 'Tickle Me Pink': '#fc89ac';
|
|
165
|
+
readonly 'Unmellow Yellow': '#ffff66';
|
|
166
|
+
readonly 'Wild Watermelon': '#fc6c85';
|
|
167
|
+
readonly 'Outrageous Orange': '#ff6e4a';
|
|
168
|
+
readonly 'Electric Lime': '#ceff1d';
|
|
169
|
+
readonly 'Granny Smith Apple': '#a8e4a0';
|
|
170
|
+
readonly 'Macaroni and Cheese': '#ffbd88';
|
|
171
|
+
readonly 'Banana Mania': '#fae7b5';
|
|
172
|
+
readonly 'Caribbean Green': '#1cd3a2';
|
|
173
|
+
readonly Cerise: '#dd4492';
|
|
174
|
+
readonly 'Cotton Candy': '#ffbcd9';
|
|
175
|
+
readonly 'Fuzzy Wuzzy': '#cc6666';
|
|
176
|
+
readonly Inchworm: '#b2ec5d';
|
|
177
|
+
readonly 'Jazzberry Jam': '#ca3767';
|
|
178
|
+
readonly Manatee: '#979aaa';
|
|
179
|
+
readonly 'Mango Tango': '#ff8243';
|
|
180
|
+
readonly Mauvelous: '#ef98aa';
|
|
181
|
+
readonly 'Midnight Blue': '#1a4876';
|
|
182
|
+
readonly 'Navy Blue': '#1974d2';
|
|
183
|
+
readonly 'Pacific Blue': '#1ca9c9';
|
|
184
|
+
readonly 'Piggy Pink': '#fddde6';
|
|
185
|
+
readonly Plum: '#8e4585';
|
|
186
|
+
readonly 'Purple Heart': '#7442c8';
|
|
187
|
+
readonly "Robin's Egg Blue": '#1fcecb';
|
|
188
|
+
readonly 'Royal Purple': '#7851a9';
|
|
189
|
+
readonly Scarlet: '#fc2847';
|
|
190
|
+
readonly Timberwolf: '#dbd7d2';
|
|
191
|
+
readonly Tumbleweed: '#deaa88';
|
|
192
|
+
readonly 'Violet Red': '#f75394';
|
|
193
|
+
readonly 'Vivid Violet': '#8f509d';
|
|
194
|
+
readonly Wisteria: '#cda4de';
|
|
195
|
+
};
|
|
196
|
+
readonly normalize: import("../types.js").NormalizeFn;
|
|
197
|
+
readonly defaultMetric: 'deltaEok';
|
|
198
|
+
};
|
|
199
|
+
export {};
|
|
200
|
+
//# sourceMappingURL=crayola.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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\nexport const crayola = {\n name: 'crayola',\n colors: crayolaColors,\n normalize: standardNormalize,\n defaultMetric: 'deltaEok',\n} as const satisfies Palette<CrayolaColorName>;\n"]}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { standardNormalize } from './normalize.js';
|
|
2
|
+
/**
|
|
3
|
+
* Crayola crayon colors — canonical subset. ~85 entries covering the
|
|
4
|
+
* current 64-standard box, iconic fluorescents, and widely-recognized
|
|
5
|
+
* modern additions.
|
|
6
|
+
*
|
|
7
|
+
* Keys are Title Case with spaces (`'Granny Smith Apple'`,
|
|
8
|
+
* `'Razzmatazz'`) — how the names are printed on the crayon wrapper.
|
|
9
|
+
* `standardNormalize` strips spaces + case + punctuation on input, so
|
|
10
|
+
* `resolve('granny-smith-apple')` and `resolve('Granny Smith Apple')`
|
|
11
|
+
* both hit the same entry.
|
|
12
|
+
*
|
|
13
|
+
* sRGB values are community-sourced approximations (Wikipedia's
|
|
14
|
+
* well-cited list of Crayola crayon colors). They do not represent
|
|
15
|
+
* a licensed Crayola reference. See NOTICE.md for trademark note.
|
|
16
|
+
*
|
|
17
|
+
* Default metric: `deltaEok`. The Crayola set spans a wide gamut with
|
|
18
|
+
* many saturated brights (fluorescents especially) where CIELAB-based
|
|
19
|
+
* metrics are less perceptually uniform than OKLAB. Same cost profile
|
|
20
|
+
* as deltaE76, better neighbor selection in saturated regions.
|
|
21
|
+
*/
|
|
22
|
+
const crayolaColors = {
|
|
23
|
+
// --- Current standard box: the 12 primary/secondary hue wheel ---
|
|
24
|
+
Red: '#ee204d',
|
|
25
|
+
'Red Orange': '#ff5349',
|
|
26
|
+
Orange: '#ff7538',
|
|
27
|
+
'Yellow Orange': '#ffae42',
|
|
28
|
+
Yellow: '#fce883',
|
|
29
|
+
'Yellow Green': '#c5e384',
|
|
30
|
+
Green: '#1cac78',
|
|
31
|
+
'Blue Green': '#0d98ba',
|
|
32
|
+
Blue: '#1f75fe',
|
|
33
|
+
'Blue Violet': '#7366bd',
|
|
34
|
+
Violet: '#926eae',
|
|
35
|
+
'Red Violet': '#c0448f',
|
|
36
|
+
// --- Achromatics ---
|
|
37
|
+
Black: '#232323',
|
|
38
|
+
Brown: '#b4674d',
|
|
39
|
+
White: '#ededed',
|
|
40
|
+
Gray: '#95918c',
|
|
41
|
+
// --- 64-box additions (pre-1990 core set) ---
|
|
42
|
+
'Carnation Pink': '#ffaacc',
|
|
43
|
+
'Sky Blue': '#80daeb',
|
|
44
|
+
Tan: '#faa76c',
|
|
45
|
+
Apricot: '#fdd9b5',
|
|
46
|
+
Peach: '#ffcfab',
|
|
47
|
+
'Cadet Blue': '#b0b7c6',
|
|
48
|
+
Salmon: '#ff9baa',
|
|
49
|
+
'Forest Green': '#6dae81',
|
|
50
|
+
'Lemon Yellow': '#fff44f',
|
|
51
|
+
Mahogany: '#cd4a4c',
|
|
52
|
+
Maroon: '#c8385a',
|
|
53
|
+
Bittersweet: '#fd7c6e',
|
|
54
|
+
Sepia: '#a5694f',
|
|
55
|
+
'Burnt Sienna': '#ea7e5d',
|
|
56
|
+
'Burnt Orange': '#ff7f49',
|
|
57
|
+
'Raw Sienna': '#d68a59',
|
|
58
|
+
// --- Metallics ---
|
|
59
|
+
Silver: '#cdc5c2',
|
|
60
|
+
Gold: '#e7c697',
|
|
61
|
+
Copper: '#dd9475',
|
|
62
|
+
// --- Earth / nature tones ---
|
|
63
|
+
'Green Yellow': '#f0e891',
|
|
64
|
+
'Pine Green': '#158078',
|
|
65
|
+
'Olive Green': '#bab86c',
|
|
66
|
+
Indigo: '#5d76cb',
|
|
67
|
+
Periwinkle: '#c5d0e6',
|
|
68
|
+
Cerulean: '#1dacd6',
|
|
69
|
+
'Sea Green': '#9fe2bf',
|
|
70
|
+
'Turquoise Blue': '#77dde7',
|
|
71
|
+
// --- Fluorescents (the iconic "Neon 24" era) ---
|
|
72
|
+
'Atomic Tangerine': '#ff9966',
|
|
73
|
+
'Blizzard Blue': '#ace5ee',
|
|
74
|
+
'Hot Magenta': '#ff1dce',
|
|
75
|
+
'Laser Lemon': '#fdfc74',
|
|
76
|
+
'Neon Carrot': '#ffa343',
|
|
77
|
+
'Purple Pizzazz': '#fe4eda',
|
|
78
|
+
'Razzle Dazzle Rose': '#ff48d0',
|
|
79
|
+
Razzmatazz: '#e3256b',
|
|
80
|
+
"Screamin' Green": '#76ff7a',
|
|
81
|
+
'Shocking Pink': '#fb7efd',
|
|
82
|
+
'Tickle Me Pink': '#fc89ac',
|
|
83
|
+
'Unmellow Yellow': '#ffff66',
|
|
84
|
+
'Wild Watermelon': '#fc6c85',
|
|
85
|
+
'Outrageous Orange': '#ff6e4a',
|
|
86
|
+
'Electric Lime': '#ceff1d',
|
|
87
|
+
// --- Modern / whimsical additions ---
|
|
88
|
+
'Granny Smith Apple': '#a8e4a0',
|
|
89
|
+
'Macaroni and Cheese': '#ffbd88',
|
|
90
|
+
'Banana Mania': '#fae7b5',
|
|
91
|
+
'Caribbean Green': '#1cd3a2',
|
|
92
|
+
Cerise: '#dd4492',
|
|
93
|
+
'Cotton Candy': '#ffbcd9',
|
|
94
|
+
'Fuzzy Wuzzy': '#cc6666',
|
|
95
|
+
Inchworm: '#b2ec5d',
|
|
96
|
+
'Jazzberry Jam': '#ca3767',
|
|
97
|
+
Manatee: '#979aaa',
|
|
98
|
+
'Mango Tango': '#ff8243',
|
|
99
|
+
Mauvelous: '#ef98aa',
|
|
100
|
+
'Midnight Blue': '#1a4876',
|
|
101
|
+
'Navy Blue': '#1974d2',
|
|
102
|
+
'Pacific Blue': '#1ca9c9',
|
|
103
|
+
'Piggy Pink': '#fddde6',
|
|
104
|
+
Plum: '#8e4585',
|
|
105
|
+
'Purple Heart': '#7442c8',
|
|
106
|
+
"Robin's Egg Blue": '#1fcecb',
|
|
107
|
+
'Royal Purple': '#7851a9',
|
|
108
|
+
Scarlet: '#fc2847',
|
|
109
|
+
Timberwolf: '#dbd7d2',
|
|
110
|
+
Tumbleweed: '#deaa88',
|
|
111
|
+
'Violet Red': '#f75394',
|
|
112
|
+
'Vivid Violet': '#8f509d',
|
|
113
|
+
Wisteria: '#cda4de',
|
|
114
|
+
};
|
|
115
|
+
export const crayola = {
|
|
116
|
+
name: 'crayola',
|
|
117
|
+
colors: crayolaColors,
|
|
118
|
+
normalize: standardNormalize,
|
|
119
|
+
defaultMetric: 'deltaEok',
|
|
120
|
+
};
|
|
121
|
+
//# sourceMappingURL=crayola.js.map
|