├── .gitignore
├── .npmignore
├── CHANGELOG.md
├── .eslintrc
├── test
├── cmc-test.js
├── cie94-test.js
├── din99o-test.js
├── cie76-test.js
├── euclidean-test.js
└── ciede2000-test.js
├── src
├── withOpacity.js
├── cie94.js
├── euclidean.js
├── din99o.js
├── cmc.js
└── ciede2000.js
├── rollup.config.js
├── index.js
├── package.json
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | build
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | *.sublime-*
2 | build/*.zip
3 | test/
4 | yarn*
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ### 0.1.3
2 |
3 | Addeed the DIN99o Delta-E color difference formula.
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | parserOptions:
2 | sourceType: module
3 |
4 | env:
5 | node: true
6 |
7 | extends:
8 | "eslint:recommended"
9 |
10 | rules:
11 | no-cond-assign: 0
12 | no-floating-decimal: 2
13 |
--------------------------------------------------------------------------------
/test/cmc-test.js:
--------------------------------------------------------------------------------
1 | var tape = require('tape');
2 | var diff = require('../');
3 |
4 | tape('', function(test) {
5 |
6 | test.equal(diff.differenceCmc('red', 'green'), 7.054710682003843);
7 |
8 | test.end();
9 | });
10 |
--------------------------------------------------------------------------------
/src/withOpacity.js:
--------------------------------------------------------------------------------
1 | function differenceWithOpacity(metric, std, smp) {
2 | let dist = metric(std, smp);
3 | return Math.sqrt(Math.pow(dist, 2) + Math.pow(std.opacity - smp.opacity, 2));
4 | }
5 |
6 | export default differenceWithOpacity;
--------------------------------------------------------------------------------
/test/cie94-test.js:
--------------------------------------------------------------------------------
1 | var tape = require('tape');
2 | var diff = require('../');
3 |
4 | tape('', function(test) {
5 |
6 | test.equal(diff.differenceCie94('red', 'green'), 48.848456144752525);
7 |
8 | test.end();
9 | });
10 |
--------------------------------------------------------------------------------
/test/din99o-test.js:
--------------------------------------------------------------------------------
1 | var tape = require('tape');
2 | var diff = require('../');
3 |
4 | tape('', function(test) {
5 |
6 | test.equal(diff.differenceDin99o('red', 'green'), 68.61703579396708);
7 |
8 | test.end();
9 | });
10 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | const pkg = require("./package.json");
2 | const deps = Object.keys(pkg.dependencies || {});
3 |
4 | export default {
5 | input: "index",
6 | external: deps,
7 | output: {
8 | extend: true,
9 | file: `build/${pkg.name}.js`,
10 | format: "umd",
11 | globals: deps.reduce((p, v) => (p[v] = "d3", p), {}),
12 | name: "d3"
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/test/cie76-test.js:
--------------------------------------------------------------------------------
1 | var tape = require('tape');
2 | var diff = require('../');
3 |
4 | tape('differenceCie76 is an alias of differenceEuclideanLab', function(test) {
5 |
6 | test.equal(diff.differenceCie76('red', 'green'), diff.differenceEuclideanLab('red', 'green'));
7 | test.equal(diff.differenceCie76('blue', 'white'), diff.differenceEuclideanLab('blue', 'white'));
8 | test.equal(diff.differenceCie76('fuchsia', 'crimson'), diff.differenceEuclideanLab('fuchsia', 'crimson'));
9 |
10 | test.end();
11 | });
12 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | export {
2 | differenceEuclideanRGB,
3 | differenceEuclideanLab,
4 | differenceEuclideanHcl,
5 | differenceEuclideanHsl,
6 | differenceEuclideanCubehelix,
7 | differenceEuclideanLab as differenceCie76
8 | } from './src/euclidean';
9 | export { default as differenceCie94, differenceCie94Weighted } from './src/cie94';
10 | export { default as differenceCiede2000, differenceCiede2000Weighted } from './src/ciede2000';
11 | export { default as differenceCmc, differenceCmcWeighted } from './src/cmc';
12 | export { default as differenceDin99o, differenceDin99oWeighted } from './src/din99o.js';
--------------------------------------------------------------------------------
/src/cie94.js:
--------------------------------------------------------------------------------
1 | import { lab } from 'd3-color';
2 |
3 | function differenceCie94(kL, K1, K2) {
4 |
5 | kL = kL !== undefined ? kL : 1;
6 | K1 = K1 !== undefined ? K1 : 0.045;
7 | K2 = K2 !== undefined ? K2 : 0.015;
8 |
9 | return function(std, smp) {
10 | var LabStd = lab(std);
11 | var LabSmp = lab(smp);
12 |
13 | // Extract Lab values, and compute Chroma
14 | var lStd = LabStd.l;
15 | var aStd = LabStd.a;
16 | var bStd = LabStd.b;
17 | var cStd = Math.sqrt(aStd * aStd + bStd * bStd);
18 |
19 | var lSmp = LabSmp.l;
20 | var aSmp = LabSmp.a;
21 | var bSmp = LabSmp.b;
22 | var cSmp = Math.sqrt(aSmp * aSmp + bSmp * bSmp);
23 |
24 | var dL2 = Math.pow(lStd - lSmp, 2);
25 | var dC2 = Math.pow(cStd - cSmp, 2);
26 | var dH2 = Math.pow(aStd - aSmp, 2) + Math.pow(bStd - bSmp, 2) - dC2;
27 |
28 | return Math.sqrt(
29 | dL2 / Math.pow(kL, 2) +
30 | dC2 / Math.pow(1 + K1 * cStd, 2) +
31 | dH2 / Math.pow(1 + K2 * cStd, 2)
32 | );
33 | }
34 | }
35 |
36 | var differenceCie94Default = differenceCie94();
37 |
38 | export {
39 | differenceCie94Default as default,
40 | differenceCie94 as differenceCie94Weighted
41 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "d3-color-difference",
3 | "version": "0.1.3",
4 | "main": "build/d3-color-difference.js",
5 | "module": "index",
6 | "jsnext:main": "index",
7 | "repository": "git@github.com:danburzo/d3-color-difference.git",
8 | "license": "BSD-3-Clause",
9 | "homepage": "https://github.com/danburzo/d3-color-difference",
10 | "author": {
11 | "name": "Dan Burzo",
12 | "url": "http://danburzo.ro"
13 | },
14 | "keywords": [
15 | "d3",
16 | "d3-color",
17 | "color",
18 | "color-difference",
19 | "cie76",
20 | "cie94",
21 | "ciede2000",
22 | "cmc"
23 | ],
24 | "devDependencies": {
25 | "eslint": "^4.19.1",
26 | "package-preamble": "^0.1.0",
27 | "rollup": "^0.58.0",
28 | "tape": "^4.9.0",
29 | "uglify-js": "^3.3.21"
30 | },
31 | "dependencies": {
32 | "d3-color": "^1.1.0"
33 | },
34 | "scripts": {
35 | "pretest": "rm -rf build && mkdir build && rollup -c --banner \"$(preamble)\"",
36 | "test": "tape 'test/**/*-test.js' && eslint index.js src test",
37 | "prepublish": "npm run test && uglifyjs --preamble \"$(preamble)\" build/d3-color-difference.js -c -m -o build/d3-color-difference.min.js"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/euclidean.js:
--------------------------------------------------------------------------------
1 | import { lab, rgb, hsl, hcl, cubehelix } from 'd3-color';
2 |
3 | function euclidean(ca1, ca2, ca3, cb1, cb2, cb3) {
4 | return Math.sqrt(Math.pow(ca1 - cb1, 2) + Math.pow(ca2 - cb2, 2) + Math.pow(ca3 - cb3, 2));
5 | }
6 |
7 | function differenceEuclideanRGB(std, smp) {
8 | std = rgb(std); smp = rgb(smp);
9 | return euclidean(std.r, std.g, std.b, smp.r, smp.g, smp.b);
10 | }
11 |
12 | function differenceEuclideanLab(std, smp) {
13 | std = lab(std); smp = lab(smp);
14 | return euclidean(std.l, std.a, std.b, smp.l, smp.a, smp.b);
15 | }
16 |
17 | function differenceEuclideanHcl(std, smp) {
18 | std = hcl(std); smp = hcl(smp);
19 | return euclidean(std.h, std.c, std.l, smp.h, smp.c, smp.l);
20 | }
21 |
22 | function differenceEuclideanHsl(std, smp) {
23 | std = hsl(std); smp = hsl(smp);
24 | return euclidean(std.h, std.s, std.l, smp.h, smp.s, smp.l);
25 | }
26 |
27 | function differenceEuclideanCubehelix(std, smp) {
28 | std = cubehelix(std); smp = cubehelix(smp);
29 | return euclidean(std.h, std.s, std.l, smp.h, smp.s, smp.l);
30 | }
31 |
32 | export {
33 | euclidean,
34 | differenceEuclideanRGB,
35 | differenceEuclideanLab,
36 | differenceEuclideanHcl,
37 | differenceEuclideanHsl,
38 | differenceEuclideanCubehelix
39 | };
--------------------------------------------------------------------------------
/src/din99o.js:
--------------------------------------------------------------------------------
1 | import { lab } from 'd3-color';
2 | import { euclidean } from './euclidean';
3 |
4 | var θ = 26 / 180 * Math.PI;
5 | var cosθ = Math.cos(θ);
6 | var sinθ = Math.sin(θ);
7 | var factor = 100/Math.log(139/100); // ~ 303.67
8 |
9 | function din99o(color, kCH, kE) {
10 | var l = factor / kE * Math.log(1 + 0.0039 * color.l);
11 | if (color.a === 0 && color.b === 0) {
12 | return lab(l, 0, 0); // achromatic colors
13 | }
14 | var e = color.a * cosθ + color.b * sinθ;
15 | var f = 0.83 * (color.b * cosθ - color.a * sinθ);
16 | var G = Math.sqrt(e * e + f * f);
17 | var c = Math.log(1 + 0.075 * G) / (0.0435 * kCH * kE);
18 | var h = (Math.atan2(f, e) + θ) / Math.PI * 180;
19 | return lab(l, c * Math.cos(h / 180 * Math.PI), c * Math.sin(h / 180 * Math.PI));
20 | }
21 |
22 | function differenceDin99o(kCH, kE) {
23 |
24 | kCH = kCH !== undefined ? kCH : 1;
25 | kE = kE !== undefined ? kE : 1;
26 |
27 | return function(std, smp) {
28 | std = din99o(lab(std), kCH, kE);
29 | smp = din99o(lab(smp), kCH, kE);
30 | return euclidean(std.l, std.a, std.b, smp.l, smp.a, smp.b);
31 | }
32 | }
33 |
34 | var differenceDin99oDefault = differenceDin99o();
35 |
36 | export {
37 | differenceDin99oDefault as default,
38 | differenceDin99o as differenceDin99oWeighted
39 | }
--------------------------------------------------------------------------------
/src/cmc.js:
--------------------------------------------------------------------------------
1 | import { lab } from 'd3-color';
2 |
3 | function differenceCmc(l, c) {
4 |
5 | l = l !== undefined ? l : 1;
6 | c = c !== undefined ? c : 1;
7 |
8 | return function(std, smp) {
9 |
10 | var LabStd = lab(std);
11 | var LabSmp = lab(smp);
12 |
13 | var lStd = LabStd.l;
14 | var aStd = LabStd.a;
15 | var bStd = LabStd.b;
16 | var cStd = Math.sqrt(aStd * aStd + bStd * bStd);
17 | var hStd = Math.atan2(bStd, aStd);
18 | hStd = hStd + 2 * Math.PI * (hStd < 0);
19 |
20 | var lSmp = LabSmp.l;
21 | var aSmp = LabSmp.a;
22 | var bSmp = LabSmp.b;
23 | var cSmp = Math.sqrt(aSmp * aSmp + bSmp * bSmp);
24 |
25 | var dL2 = Math.pow(lStd - lSmp, 2);
26 | var dC2 = Math.pow(cStd - cSmp, 2);
27 | var dH2 = Math.pow(aStd - aSmp, 2) + Math.pow(bStd - bSmp, 2) - dC2;
28 |
29 | var F = Math.sqrt(Math.pow(cStd, 4) / (Math.pow(cStd, 4) + 1900));
30 | var T = hStd >= (164 / 180 * Math.PI) &&
31 | hStd <= (345 / 180 * Math.PI) ?
32 | 0.56 + Math.abs(0.2 * Math.cos(hStd + 168 / 180 * Math.PI))
33 | : 0.36 + Math.abs(0.4 * Math.cos(hStd + 35 / 180 * Math.PI));
34 |
35 | var Sl = lStd < 16 ? 0.511 : ((0.040975 * lStd) / (1 + 0.01765 * lStd));
36 | var Sc = 0.0638 * cStd / (1 + 0.0131 * cStd) + 0.638;
37 | var Sh = Sc * (F * T + 1 - F);
38 |
39 | return Math.sqrt(
40 | dL2 / Math.pow(l * Sl, 2),
41 | dC2 / Math.pow(c * Sc, 2),
42 | dH2 / Math.pow(Sh, 2)
43 | );
44 | };
45 | }
46 |
47 | var differenceCmcDefault = differenceCmc();
48 |
49 | export {
50 | differenceCmcDefault as default,
51 | differenceCmc as differenceCmcWeighted
52 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2018, Dan Burzo
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | * Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/test/euclidean-test.js:
--------------------------------------------------------------------------------
1 | var tape = require('tape');
2 | var diff = require('../');
3 | var { lab, hcl, cubehelix, hsl } = require('d3-color');
4 |
5 | tape('differenceEuclideanRGB', function(test) {
6 |
7 | test.equal(diff.differenceEuclideanRGB('#ff0000', '#00ff00'), 360.62445840513925);
8 | test.equal(diff.differenceEuclideanRGB('#ff0000', '#0000ff'), 360.62445840513925);
9 | test.equal(diff.differenceEuclideanRGB('#00ff00', '#0000ff'), 360.62445840513925);
10 |
11 | test.end();
12 | });
13 |
14 | tape('differenceEuclideanLab', function(test) {
15 |
16 | test.equal(diff.differenceEuclideanLab(lab(100,0,0), lab(0,0,100)), 141.4213562373095);
17 | test.equal(diff.differenceEuclideanLab(lab(100,0,0), lab(0,100,0)), 141.4213562373095);
18 | test.equal(diff.differenceEuclideanLab(lab(0,100,0), lab(100,0,0)), 141.4213562373095);
19 |
20 | test.end();
21 | });
22 |
23 | tape('differenceEuclideanHcl', function(test) {
24 |
25 | test.equal(diff.differenceEuclideanHcl(hcl(100,0,0), hcl(0,0,100)), 141.4213562373095);
26 | test.equal(diff.differenceEuclideanHcl(hcl(100,0,0), hcl(0,100,0)), 141.4213562373095);
27 | test.equal(diff.differenceEuclideanHcl(hcl(0,100,0), hcl(100,0,0)), 141.4213562373095);
28 |
29 | test.end();
30 | });
31 |
32 | tape('differenceEuclideanHsl', function(test) {
33 |
34 | test.equal(diff.differenceEuclideanHsl(hsl(100,0,0), hsl(0,0,100)), 141.4213562373095);
35 | test.equal(diff.differenceEuclideanHsl(hsl(100,0,0), hsl(0,100,0)), 141.4213562373095);
36 | test.equal(diff.differenceEuclideanHsl(hsl(0,100,0), hsl(100,0,0)), 141.4213562373095);
37 |
38 | test.end();
39 | });
40 |
41 | tape('differenceEuclideanCubehelix', function(test) {
42 |
43 | test.equal(diff.differenceEuclideanCubehelix(cubehelix(100,0,0), cubehelix(0,0,100)), 141.4213562373095);
44 | test.equal(diff.differenceEuclideanCubehelix(cubehelix(100,0,0), cubehelix(0,100,0)), 141.4213562373095);
45 | test.equal(diff.differenceEuclideanCubehelix(cubehelix(0,100,0), cubehelix(100,0,0)), 141.4213562373095);
46 |
47 | test.end();
48 | });
--------------------------------------------------------------------------------
/test/ciede2000-test.js:
--------------------------------------------------------------------------------
1 | var tape = require('tape');
2 | var diff = require('../');
3 | var { lab } = require('d3-color');
4 |
5 | // Test data from: http://www2.ece.rochester.edu/~gsharma/ciede2000/
6 | let testdata = `50.0000 2.6772 -79.7751 50.0000 0.0000 -82.7485 2.0425
7 | 50.0000 3.1571 -77.2803 50.0000 0.0000 -82.7485 2.8615
8 | 50.0000 2.8361 -74.0200 50.0000 0.0000 -82.7485 3.4412
9 | 50.0000 -1.3802 -84.2814 50.0000 0.0000 -82.7485 1.0000
10 | 50.0000 -1.1848 -84.8006 50.0000 0.0000 -82.7485 1.0000
11 | 50.0000 -0.9009 -85.5211 50.0000 0.0000 -82.7485 1.0000
12 | 50.0000 0.0000 0.0000 50.0000 -1.0000 2.0000 2.3669
13 | 50.0000 -1.0000 2.0000 50.0000 0.0000 0.0000 2.3669
14 | 50.0000 2.4900 -0.0010 50.0000 -2.4900 0.0009 7.1792
15 | 50.0000 2.4900 -0.0010 50.0000 -2.4900 0.0010 7.1792
16 | 50.0000 2.4900 -0.0010 50.0000 -2.4900 0.0011 7.2195
17 | 50.0000 2.4900 -0.0010 50.0000 -2.4900 0.0012 7.2195
18 | 50.0000 -0.0010 2.4900 50.0000 0.0009 -2.4900 4.8045
19 | 50.0000 -0.0010 2.4900 50.0000 0.0010 -2.4900 4.8045
20 | 50.0000 -0.0010 2.4900 50.0000 0.0011 -2.4900 4.7461
21 | 50.0000 2.5000 0.0000 50.0000 0.0000 -2.5000 4.3065
22 | 50.0000 2.5000 0.0000 73.0000 25.0000 -18.0000 27.1492
23 | 50.0000 2.5000 0.0000 61.0000 -5.0000 29.0000 22.8977
24 | 50.0000 2.5000 0.0000 56.0000 -27.0000 -3.0000 31.9030
25 | 50.0000 2.5000 0.0000 58.0000 24.0000 15.0000 19.4535
26 | 50.0000 2.5000 0.0000 50.0000 3.1736 0.5854 1.0000
27 | 50.0000 2.5000 0.0000 50.0000 3.2972 0.0000 1.0000
28 | 50.0000 2.5000 0.0000 50.0000 1.8634 0.5757 1.0000
29 | 50.0000 2.5000 0.0000 50.0000 3.2592 0.3350 1.0000
30 | 60.2574 -34.0099 36.2677 60.4626 -34.1751 39.4387 1.2644
31 | 63.0109 -31.0961 -5.8663 62.8187 -29.7946 -4.0864 1.2630
32 | 61.2901 3.7196 -5.3901 61.4292 2.2480 -4.9620 1.8731
33 | 35.0831 -44.1164 3.7933 35.0232 -40.0716 1.5901 1.8645
34 | 22.7233 20.0904 -46.6940 23.0331 14.9730 -42.5619 2.0373
35 | 36.4612 47.8580 18.3852 36.2715 50.5065 21.2231 1.4146
36 | 90.8027 -2.0831 1.4410 91.1528 -1.6435 0.0447 1.4441
37 | 90.9257 -0.5406 -0.9208 88.6381 -0.8985 -0.7239 1.5381
38 | 6.7747 -0.2908 -2.4247 5.8714 -0.0985 -2.2286 0.6377
39 | 2.0776 0.0795 -1.1350 0.9033 -0.0636 -0.5514 0.9082`
40 | .split('\n').map(line => line.trim().split(/\s+/));
41 |
42 | function round(value, precision) {
43 | return Math.round(value * (precision = Math.pow(10, precision))) / precision;
44 | }
45 |
46 | tape('Computes correctly the Sharma test data', function(test) {
47 |
48 | for (var i = 0; i < testdata.length; i++) {
49 | let line = testdata[i];
50 | test.equal(
51 | round(diff.differenceCiede2000(
52 | lab(line[0], line[1], line[2]),
53 | lab(line[3], line[4], line[5])
54 | ), 4),
55 | +line[6]
56 | );
57 |
58 | // test symmetry
59 | test.equal(
60 | round(diff.differenceCiede2000(
61 | lab(line[3], line[4], line[5]),
62 | lab(line[0], line[1], line[2])
63 | ), 4),
64 | +line[6]
65 | );
66 | }
67 |
68 | test.end();
69 |
70 | });
71 |
--------------------------------------------------------------------------------
/src/ciede2000.js:
--------------------------------------------------------------------------------
1 | import { lab } from 'd3-color';
2 |
3 | /*
4 | CIEDE2000 color difference, original Matlab implementation by Gaurav Sharma
5 | Based on "The CIEDE2000 Color-Difference Formula: Implementation Notes, Supplementary Test Data, and Mathematical Observations"
6 | by Gaurav Sharma, Wencheng Wu, Edul N. Dalal in Color Research and Application, vol. 30. No. 1, pp. 21-30, February 2005.
7 | http://www2.ece.rochester.edu/~gsharma/ciede2000/
8 | */
9 |
10 | function differenceCiede2000(kL, kC, kH) {
11 |
12 | kL = kL !== undefined ? kL : 1;
13 | kC = kC !== undefined ? kC : 1;
14 | kH = kH !== undefined ? kH : 1;
15 |
16 | return function(std, smp) {
17 | var LabStd = lab(std);
18 | var LabSmp = lab(smp);
19 |
20 | var lStd = LabStd.l;
21 | var aStd = LabStd.a;
22 | var bStd = LabStd.b;
23 | var cStd = Math.sqrt(aStd * aStd + bStd * bStd);
24 |
25 | var lSmp = LabSmp.l;
26 | var aSmp = LabSmp.a;
27 | var bSmp = LabSmp.b;
28 | var cSmp = Math.sqrt(aSmp * aSmp + bSmp * bSmp);
29 |
30 | var cAvg = (cStd + cSmp) / 2;
31 |
32 | var G = 0.5 * (1 - Math.sqrt(Math.pow(cAvg, 7) / (Math.pow(cAvg, 7) + Math.pow(25, 7))));
33 |
34 | var apStd = aStd * (1 + G);
35 | var apSmp = aSmp * (1 + G);
36 |
37 | var cpStd = Math.sqrt(apStd * apStd + bStd * bStd);
38 | var cpSmp = Math.sqrt(apSmp * apSmp + bSmp * bSmp);
39 |
40 | var hpStd = Math.abs(apStd) + Math.abs(bStd) === 0 ? 0 : Math.atan2(bStd, apStd);
41 | hpStd += (hpStd < 0) * 2 * Math.PI;
42 |
43 | var hpSmp = Math.abs(apSmp) + Math.abs(bSmp) === 0 ? 0 : Math.atan2(bSmp, apSmp);
44 | hpSmp += (hpSmp < 0) * 2 * Math.PI;
45 |
46 | var dL = lSmp - lStd;
47 | var dC = cpSmp - cpStd;
48 |
49 | var dhp = cpStd * cpSmp === 0 ? 0 : hpSmp - hpStd;
50 | dhp -= (dhp > Math.PI) * 2 * Math.PI;
51 | dhp += (dhp < -Math.PI) * 2 * Math.PI;
52 |
53 | var dH = 2 * Math.sqrt(cpStd * cpSmp) * Math.sin(dhp / 2);
54 |
55 | var Lp = (lStd + lSmp) / 2;
56 | var Cp = (cpStd + cpSmp) / 2;
57 |
58 | var hp;
59 | if (cpStd * cpSmp === 0) {
60 | hp = hpStd + hpSmp;
61 | } else {
62 | hp = (hpStd + hpSmp) / 2;
63 | hp -= (Math.abs(hpStd - hpSmp) > Math.PI) * Math.PI;
64 | hp += (hp < 0) * 2 * Math.PI;
65 | }
66 |
67 | var Lpm50 = Math.pow(Lp - 50, 2);
68 | var T = 1 -
69 | 0.17 * Math.cos(hp - Math.PI / 6) +
70 | 0.24 * Math.cos(2 * hp) +
71 | 0.32 * Math.cos(3 * hp + Math.PI / 30) -
72 | 0.20 * Math.cos(4 * hp - 63 * Math.PI / 180);
73 |
74 | var Sl = 1 + (0.015 * Lpm50) / Math.sqrt(20 + Lpm50);
75 | var Sc = 1 + 0.045 * Cp;
76 | var Sh = 1 + 0.015 * Cp * T;
77 |
78 | var deltaTheta = 30 * Math.PI / 180 * Math.exp(-1 * Math.pow((180 / Math.PI * hp - 275)/25, 2));
79 | var Rc = 2 * Math.sqrt(
80 | Math.pow(Cp, 7) / (Math.pow(Cp, 7) + Math.pow(25, 7))
81 | );
82 |
83 | var Rt = -1 * Math.sin(2 * deltaTheta) * Rc;
84 |
85 | return Math.sqrt(
86 | Math.pow(dL / (kL * Sl), 2) +
87 | Math.pow(dC / (kC * Sc), 2) +
88 | Math.pow(dH / (kH * Sh), 2) +
89 | Rt * dC / (kC * Sc) * dH / (kH * Sh)
90 | );
91 | };
92 | }
93 |
94 | var differenceCiede2000Default = differenceCiede2000();
95 |
96 | export {
97 | differenceCiede2000Default as default,
98 | differenceCiede2000 as differenceCiede2000Weighted
99 | };
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # d3-color-difference
2 |
3 | See this [Observable notebook](https://beta.observablehq.com/@danburzo/color-difference-formulas-with-d3-color-difference) for a demonstration.
4 |
5 | ## Installing
6 |
7 | ```bash
8 | $ npm install d3-color-difference
9 | ```
10 |
11 | ## API Reference
12 |
13 | ### Euclidean Distances
14 |
15 | # d3.__differenceEuclideanRGB__(_a_, _b_) [<>](https://github.com/danburzo/d3-color-difference/blob/master/src/euclidean.js "Source")
16 |
17 | Computes the [Euclidean distance][euclidean] between the colors _a_ and _b_ in the [RGB][RGB] color space.
18 |
19 | # d3.__differenceEuclideanLab__(_a_, _b_) [<>](https://github.com/danburzo/d3-color-difference/blob/master/src/euclidean.js "Source")
20 |
21 | Computes the [Euclidean distance][euclidean] between the colors _a_ and _b_ in the [Lab][Lab] color space.
22 |
23 | # d3.__differenceEuclideanHcl__(_a_, _b_) [<>](https://github.com/danburzo/d3-color-difference/blob/master/src/euclidean.js "Source")
24 |
25 | Computes the [Euclidean distance][euclidean] between the colors _a_ and _b_ in the [HCL][HCL] color space.
26 |
27 | # d3.__differenceEuclideanHsl__(_a_, _b_) [<>](https://github.com/danburzo/d3-color-difference/blob/master/src/euclidean.js "Source")
28 |
29 | Computes the [Euclidean distance][euclidean] between the colors _a_ and _b_ in the [HSL][HSL] color space.
30 |
31 | # d3.__differenceEuclideanCubehelix__(_a_, _b_) [<>](https://github.com/danburzo/d3-color-difference/blob/master/src/euclidean.js "Source")
32 |
33 | Computes the [Euclidean distance][euclidean] between the colors _a_ and _b_ in the [Cubehelix][Cubehelix] color space.
34 |
35 | ### CIE Delta-E
36 |
37 | # d3.__differenceCie76__(_a_, _b_) [<>](https://github.com/danburzo/d3-color-difference/blob/master/src/euclidean.js "Source")
38 |
39 | Computes the [CIE76][CIE76] ΔE\*ab color difference between the colors _a_ and _b_. The computation is done in the [Lab][Lab] color space and it is analogous to [differenceEuclideanLab](#differenceEuclideanLab).
40 |
41 | # d3.__differenceCie94__(_a_, _b_) [<>](https://github.com/danburzo/d3-color-difference/blob/master/src/cie94.js "Source")
42 |
43 | Computes the [CIE94][CIE94] ΔE\*94 color difference between the colors _a_ and _b_. The computation is done in the [Lab][Lab] color space, with the default weights _kL = 1_, _K1 = 0.045_, and _K2 = 0.015_.
44 |
45 | # d3.__differenceCie94Weighted__(_kL_, _K1_, _K2_) [<>](https://github.com/danburzo/d3-color-difference/blob/master/src/cie94.js "Source")
46 |
47 | Returns a [CIE94][CIE94] difference function with custom weighting parameters.
48 |
49 | # d3.__differenceCiede2000__(_a_, _b_) [<>](https://github.com/danburzo/d3-color-difference/blob/master/src/ciede2000.js "Source")
50 |
51 | Computes the [CIEDE2000][CIEDE2000] ΔE\*00 color difference between the colors _a_ and _b_ as implemented by [G. Sharma](http://www2.ece.rochester.edu/~gsharma/ciede2000/). The computation is done in the [Lab][Lab] color space, with the default weights _kL = kC = kH = 1_.
52 |
53 | # d3.__differenceCiede2000Weighted__(_kL_, _kC_, _kH_) [<>](https://github.com/danburzo/d3-color-difference/blob/master/src/ciede2000.js "Source")
54 |
55 | Returns a [CIEDE2000][CIEDE2000] difference function with custom weighting parameters.
56 |
57 | # d3.__differenceCmc__(_a_, _b_) [<>](https://github.com/danburzo/d3-color-difference/blob/master/src/cmc.js "Source")
58 |
59 | Computes the [CMC l:c (1984)][CMC] ΔE\*CMC color difference between the colors _a_ and _b_. The computation is done in the [Lab][Lab] color space with the default weights _l = c = 1_.
60 |
61 | _Note:_ ΔE\*CMC is not considered a metric since it's not symmetrical, i.e. the distance from _a_ to _b_ is not always equal to the distance from _b_ to _a_.
62 |
63 | # d3.__differenceCmcWeighted__(_l_, _c_) [<>](https://github.com/danburzo/d3-color-difference/blob/master/src/cmc.js "Source")
64 |
65 | Returns a [CMC l:c (1984)][CMC] difference function with custom weighting parameters.
66 |
67 | # d3.__differenceDin99o__(_a_, _b_) [<>](https://github.com/danburzo/d3-color-difference/blob/master/src/din99o.js "Source")
68 |
69 | Computes the [DIN99o][DIN99oDE] ΔE\*99o color difference between the colors _a_ and _b_. The computation is done in the [DIN99o][DIN99o] color space with the default weights _kCH = kE = 1_.
70 |
71 | # d3.__differenceDin99oWeighted__(_kCH_, _kE_) [<>](https://github.com/danburzo/d3-color-difference/blob/master/src/din99o.js "Source")
72 |
73 | Returns a [DIN99o][DIN99oDE] difference function with custom weighting parameters.
74 |
75 | ### Opacity
76 |
77 | # d3.__differenceWithOpacity__(_differenceFunction_, _a_, _b_) [<>](https://github.com/danburzo/d3-color-difference/blob/master/src/withOpacity.js "Source")
78 |
79 | The difference functions don't take the colors' alpha channel into account when computing distances. This method allows you to factor the colors' opacities into the distance.
80 |
81 | [color-diff]: https://en.wikipedia.org/wiki/Color_difference
82 | [euclidean]: https://en.wikipedia.org/wiki/Color_difference#Euclidean
83 | [CIE76]: https://en.wikipedia.org/wiki/Color_difference#CIE76
84 | [CIE94]: https://en.wikipedia.org/wiki/Color_difference#CIE94
85 | [CIEDE2000]: https://en.wikipedia.org/wiki/Color_difference#CIEDE2000
86 | [CMC]: https://en.wikipedia.org/wiki/Color_difference#CMC_l:c_(1984)
87 | [DIN99o]: https://de.wikipedia.org/wiki/DIN99-Farbraum
88 | [DIN99oDE]: https://de.wikipedia.org/wiki/DIN99-Farbraum#Farbabstandsformel
89 | [RGB]: https://github.com/d3/d3-color#rgb
90 | [HSL]: https://github.com/d3/d3-color#hsl
91 | [Lab]: https://github.com/d3/d3-color#lab
92 | [HCL]: https://github.com/d3/d3-color#hcl
93 | [Cubehelix]: https://github.com/d3/d3-color#cubehelix
--------------------------------------------------------------------------------