├── .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 --------------------------------------------------------------------------------