diff --git a/chroma-light.js b/chroma-light.js index 38f132e0..8647540f 100644 --- a/chroma-light.js +++ b/chroma-light.js @@ -199,7 +199,7 @@ }; chroma.Color = Color_1; - chroma.version = '2.1.0'; + chroma.version = '2.1.1'; var chroma_1 = chroma; diff --git a/chroma-light.min.js b/chroma-light.min.js index b6a2ff37..875d39ca 100644 --- a/chroma-light.min.js +++ b/chroma-light.min.js @@ -55,4 +55,4 @@ * @preserve */ -!function(t,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):t.chroma=r()}(this,function(){"use strict";for(var n=function(t,r,n){return void 0===r&&(r=0),void 0===n&&(n=1),t>16,r>>8&255,255&r,1]}if(t.match(K)){5!==t.length&&9!==t.length||(t=t.substr(1)),4===t.length&&(t=(t=t.split(""))[0]+t[0]+t[1]+t[1]+t[2]+t[2]+t[3]+t[3]);var n=parseInt(t,16);return[n>>24&255,n>>16&255,n>>8&255,Math.round((255&n)/255*100)/100]}throw new Error("unknown hex color: "+t)},T=u;d.prototype.hex=function(t){return H(this._rgb,t)},b.hex=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];return new(Function.prototype.bind.apply(d,[null].concat(t,["hex"])))},h.format.hex=Q,h.autodetect.push({p:4,test:function(t){for(var r=[],n=arguments.length-1;0>16,r>>8&255,255&r,1]}if(t.match(H)){4===(t=5===t.length||9===t.length?t.substr(1):t).length&&(t=(t=t.split(""))[0]+t[0]+t[1]+t[1]+t[2]+t[2]+t[3]+t[3]);r=parseInt(t,16);return[r>>24&255,r>>16&255,r>>8&255,Math.round((255&r)/255*100)/100]}throw new Error("unknown hex color: "+t)},J=u;d.prototype.hex=function(t){return z(this._rgb,t)},i.hex=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];return new(Function.prototype.bind.apply(d,[null].concat(t,["hex"])))},h.format.hex=l,h.autodetect.push({p:4,test:function(t){for(var r=[],n=arguments.length-1;0= 360) { h1 -= 360; } - var t = (h1 >= 164.0) && (h1 <= 345.0) ? (0.56 + abs$1(0.2 * cos$4((PI$2 * (h1 + 168.0)) / 180.0))) : (0.36 + abs$1(0.4 * cos$4((PI$2 * (h1 + 35.0)) / 180.0))); - var c4 = c1 * c1 * c1 * c1; - var f = sqrt$4(c4 / (c4 + 1900.0)); - var sh = sc * (((f * t) + 1.0) - f); - var delL = L1 - L2; - var delC = c1 - c2; - var delA = a1 - a2; - var delB = b1 - b2; - var dH2 = ((delA * delA) + (delB * delB)) - (delC * delC); - var v1 = delL / (L * sl); - var v2 = delC / (C * sc); - var v3 = sh; - return sqrt$4((v1 * v1) + (v2 * v2) + (dH2 / (v3 * v3))); + var avgL = (L1 + L2)/2; + var C1 = sqrt$4(pow$8(a1, 2) + pow$8(b1, 2)); + var C2 = sqrt$4(pow$8(a2, 2) + pow$8(b2, 2)); + var avgC = (C1 + C2)/2; + var G = 0.5*(1-sqrt$4(pow$8(avgC, 7)/(pow$8(avgC, 7) + pow$8(25, 7)))); + var a1p = a1*(1+G); + var a2p = a2*(1+G); + var C1p = sqrt$4(pow$8(a1p, 2) + pow$8(b1, 2)); + var C2p = sqrt$4(pow$8(a2p, 2) + pow$8(b2, 2)); + var avgCp = (C1p + C2p)/2; + var arctan1 = rad2deg(atan2$2(b1, a1p)); + var arctan2 = rad2deg(atan2$2(b2, a2p)); + var h1p = arctan1 >= 0 ? arctan1 : arctan1 + 360; + var h2p = arctan2 >= 0 ? arctan2 : arctan2 + 360; + var avgHp = abs$1(h1p - h2p) > 180 ? (h1p + h2p + 360)/2 : (h1p + h2p)/2; + var T = 1 - 0.17*cos$4(deg2rad(avgHp - 30)) + 0.24*cos$4(deg2rad(2*avgHp)) + 0.32*cos$4(deg2rad(3*avgHp + 6)) - 0.2*cos$4(deg2rad(4*avgHp - 63)); + var deltaHp = h2p - h1p; + deltaHp = abs$1(deltaHp) <= 180 ? deltaHp : h2p <= h1p ? deltaHp + 360 : deltaHp - 360; + deltaHp = 2*sqrt$4(C1p*C2p)*sin$3(deg2rad(deltaHp)/2); + var deltaL = L2 - L1; + var deltaCp = C2p - C1p; + var sl = 1 + (0.015*pow$8(avgL - 50, 2))/sqrt$4(20 + pow$8(avgL - 50, 2)); + var sc = 1 + 0.045*avgCp; + var sh = 1 + 0.015*avgCp*T; + var deltaTheta = 30*exp(-pow$8((avgHp - 275)/25, 2)); + var Rc = 2*sqrt$4(pow$8(avgCp, 7)/(pow$8(avgCp, 7) + pow$8(25, 7))); + var Rt = -Rc*sin$3(2*deg2rad(deltaTheta)); + var result = sqrt$4(pow$8(deltaL/(Kl*sl), 2) + pow$8(deltaCp/(Kc*sc), 2) + pow$8(deltaHp/(Kh*sh), 2) + Rt*(deltaCp/(Kc*sc))*(deltaHp/(Kh*sh))); + return max$2(0, min$2(100, result)); }; // simple Euclidean distance diff --git a/chroma.min.js b/chroma.min.js index 93cac504..1bf567b8 100644 --- a/chroma.min.js +++ b/chroma.min.js @@ -55,4 +55,4 @@ * @preserve */ -!function(r,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):r.chroma=e()}(this,function(){"use strict";for(var n=function(r,e,n){return void 0===e&&(e=0),void 0===n&&(n=1),r>16,e>>8&255,255&e,1]}if(r.match(fr)){5!==r.length&&9!==r.length||(r=r.substr(1)),4===r.length&&(r=(r=r.split(""))[0]+r[0]+r[1]+r[1]+r[2]+r[2]+r[3]+r[3]);var n=parseInt(r,16);return[n>>24&255,n>>16&255,n>>8&255,Math.round((255&n)/255*100)/100]}throw new Error("unknown hex color: "+r)},ur=o.type;A.prototype.hex=function(r){return tr(this._rgb,r)},_.hex=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];return new(Function.prototype.bind.apply(A,[null].concat(r,["hex"])))},b.format.hex=or,b.autodetect.push({p:4,test:function(r){for(var e=[],n=arguments.length-1;0>16,r>>8&255,255&r,1];throw new Error("unknown num color: "+r)},xe=o.type;A.prototype.num=function(){return Me(this._rgb)},_.num=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];return new(Function.prototype.bind.apply(A,[null].concat(r,["num"])))},b.format.num=_e,b.autodetect.push({p:5,test:function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];if(1===r.length&&"number"===xe(r[0])&&0<=r[0]&&r[0]<=16777215)return"num"}});var Ae=o.unpack,Ee=o.type,Pe=Math.round;A.prototype.rgb=function(r){return void 0===r&&(r=!0),!1===r?this._rgb.slice(0,3):this._rgb.slice(0,3).map(Pe)},A.prototype.rgba=function(n){return void 0===n&&(n=!0),this._rgb.slice(0,4).map(function(r,e){return e<3?!1===n?r:Pe(r):r})},_.rgb=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];return new(Function.prototype.bind.apply(A,[null].concat(r,["rgb"])))},b.format.rgb=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];var n=Ae(r,"rgba");return void 0===n[3]&&(n[3]=1),n},b.autodetect.push({p:3,test:function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];if(r=Ae(r,"rgba"),"array"===Ee(r)&&(3===r.length||4===r.length&&"number"==Ee(r[3])&&0<=r[3]&&r[3]<=1))return"rgb"}});var Fe=Math.log,Oe=function(r){var e,n,t,a=r/100;return t=a<66?(e=255,n=-155.25485562709179-.44596950469579133*(n=a-2)+104.49216199393888*Fe(n),a<20?0:.8274096064007395*(t=a-10)-254.76935184120902+115.67994401066147*Fe(t)):(e=351.97690566805693+.114206453784165*(e=a-55)-40.25366309332127*Fe(e),n=325.4494125711974+.07943456536662342*(n=a-50)-28.0852963507957*Fe(n),255),[e,n,t,1]},je=o.unpack,Ge=Math.round,qe=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];for(var n,t=je(r,"rgb"),a=t[0],f=t[2],o=1e3,u=4e4;.4=f/a?u=n:o=n}return Ge(n)};A.prototype.temp=A.prototype.kelvin=A.prototype.temperature=function(){return qe(this._rgb)},_.temp=_.kelvin=_.temperature=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];return new(Function.prototype.bind.apply(A,[null].concat(r,["temp"])))},b.format.temp=b.format.kelvin=b.format.temperature=Oe;var Le=o.type;A.prototype.alpha=function(r,e){return void 0===e&&(e=!1),void 0!==r&&"number"===Le(r)?e?(this._rgb[3]=r,this):new A([this._rgb[0],this._rgb[1],this._rgb[2],r],"rgb"):this._rgb[3]},A.prototype.clipped=function(){return this._rgb._clipped||!1},A.prototype.darken=function(r){void 0===r&&(r=1);var e=this.lab();return e[0]-=qr*r,new A(e,"lab").alpha(this.alpha(),!0)},A.prototype.brighten=function(r){return void 0===r&&(r=1),this.darken(-r)},A.prototype.darker=A.prototype.darken,A.prototype.brighter=A.prototype.brighten,A.prototype.get=function(r){var e=r.split("."),n=e[0],t=e[1],a=this[n]();if(t){var f=n.indexOf(t);if(-1=s[n];)n++;return n-1}(r)/(s.length-2):g!==p?(r-p)/(g-p):1;t=k(t),e||(t=w(t)),1!==y&&(t=nn(t,y)),t=d[0]+t*(1-d[0]-d[1]),t=Math.min(1,Math.max(0,t));var a=Math.floor(1e4*t);if(m&&v[a])n=v[a];else{if("array"===en(b))for(var f=0;f=u[e+1];)e++;var n=(r-u[e])/(u[e+1]-u[e]);return o[e]+n*(o[e+1]-o[e])})}}return l=[p,g],N},N.mode=function(r){return arguments.length?(u=r,f(),N):u},N.range=function(r,e){return a(r),N},N.out=function(r){return n=r,N},N.spread=function(r){return arguments.length?(e=r,N):e},N.correctLightness=function(r){return null==r&&(r=!0),t=r,f(),w=t?function(r){for(var e=M(0,!0).lab()[0],n=M(1,!0).lab()[0],t=nn.max&&(n.max=r),n.count+=1)}),n.domain=[n.min,n.max],n.limits=function(r,e){return Nn(n,r,e)},n},Nn=function(r,e,n){void 0===e&&(e="equal"),void 0===n&&(n=7),"array"==Y(r)&&(r=Mn(r));var t=r.min,a=r.max,f=r.values.sort(function(r,e){return r-e});if(1===n)return[t,a];var o=[];if("c"===e.substr(0,1)&&(o.push(t),o.push(a)),"e"===e.substr(0,1)){o.push(t);for(var u=1;u 0");var c=Math.LOG10E*mn(t),i=Math.LOG10E*mn(a);o.push(t);for(var l=1;l>16,e>>8&255,255&e,1]}if(r.match(tr)){4===(r=5===r.length||9===r.length?r.substr(1):r).length&&(r=(r=r.split(""))[0]+r[0]+r[1]+r[1]+r[2]+r[2]+r[3]+r[3]);e=parseInt(r,16);return[e>>24&255,e>>16&255,e>>8&255,Math.round((255&e)/255*100)/100]}throw new Error("unknown hex color: "+r)},fr=o.type;p.prototype.hex=function(r){return er(this._rgb,r)},N.hex=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];return new(Function.prototype.bind.apply(p,[null].concat(r,["hex"])))},h.format.hex=ar,h.autodetect.push({p:4,test:function(r){for(var e=[],n=arguments.length-1;0>16,r>>8&255,255&r,1];throw new Error("unknown num color: "+r)},ye=o.type;p.prototype.num=function(){return ve(this._rgb)},N.num=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];return new(Function.prototype.bind.apply(p,[null].concat(r,["num"])))},h.format.num=m,h.autodetect.push({p:5,test:function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];if(1===r.length&&"number"===ye(r[0])&&0<=r[0]&&r[0]<=16777215)return"num"}});var we=o.unpack,ke=o.type,Me=Math.round;p.prototype.rgb=function(r){return!1===(r=void 0===r?!0:r)?this._rgb.slice(0,3):this._rgb.slice(0,3).map(Me)},p.prototype.rgba=function(n){return void 0===n&&(n=!0),this._rgb.slice(0,4).map(function(r,e){return!(e<3)||!1===n?r:Me(r)})},N.rgb=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];return new(Function.prototype.bind.apply(p,[null].concat(r,["rgb"])))},h.format.rgb=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];var n=we(r,"rgba");return void 0===n[3]&&(n[3]=1),n},h.autodetect.push({p:3,test:function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];if(r=we(r,"rgba"),"array"===ke(r)&&(3===r.length||4===r.length&&"number"==ke(r[3])&&0<=r[3]&&r[3]<=1))return"rgb"}});var Ne=Math.log,_e=function(r){var e,n,r=r/100,t=r<66?(e=255,n=-155.25485562709179-.44596950469579133*(n=r-2)+104.49216199393888*Ne(n),r<20?0:.8274096064007395*(t=r-10)-254.76935184120902+115.67994401066147*Ne(t)):(e=351.97690566805693+.114206453784165*(e=r-55)-40.25366309332127*Ne(e),n=325.4494125711974+.07943456536662342*(n=r-50)-28.0852963507957*Ne(n),255);return[e,n,t,1]},xe=o.unpack,Ae=Math.round,Ee=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];for(var n=xe(r,"rgb"),t=n[0],a=n[2],f=1e3,o=4e4;.4=a/t?o=u:f=u}return Ae(u)};p.prototype.temp=p.prototype.kelvin=p.prototype.temperature=function(){return Ee(this._rgb)},N.temp=N.kelvin=N.temperature=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];return new(Function.prototype.bind.apply(p,[null].concat(r,["temp"])))},h.format.temp=h.format.kelvin=h.format.temperature=_e;var Pe=o.type;p.prototype.alpha=function(r,e){return void 0===e&&(e=!1),void 0!==r&&"number"===Pe(r)?e?(this._rgb[3]=r,this):new p([this._rgb[0],this._rgb[1],this._rgb[2],r],"rgb"):this._rgb[3]},p.prototype.clipped=function(){return this._rgb._clipped||!1},p.prototype.darken=function(r){void 0===r&&(r=1);var e=this.lab();return e[0]-=jr*r,new p(e,"lab").alpha(this.alpha(),!0)},p.prototype.brighten=function(r){return this.darken(-(r=void 0===r?1:r))},p.prototype.darker=p.prototype.darken,p.prototype.brighter=p.prototype.brighten,p.prototype.get=function(r){var e=r.split("."),n=e[0],t=e[1],r=this[n]();if(t){e=n.indexOf(t);if(-1=s[n];)n++;return n-1}(r)/(s.length-2):g!==p?(r-p)/(g-p):1,t=k(t),e||(t=w(t)),1!==y&&(t=Je(t,y)),t=d[0]+t*(1-d[0]-d[1]),t=Math.min(1,Math.max(0,t)),e=Math.floor(1e4*t),m&&v[e])n=v[e];else{if("array"===He(b))for(var a=0;a=o[e+1];)e++;var n=(r-o[e])/(o[e+1]-o[e]);return f[e]+n*(f[e+1]-f[e])}))}return h=[p,g],M},M.mode=function(r){return arguments.length?(o=r,f(),M):o},M.range=function(r,e){return n(r),M},M.out=function(r){return a=r,M},M.spread=function(r){return arguments.length?(t=r,M):t},M.correctLightness=function(r){return e=r=null==r?!0:r,f(),w=e?function(r){for(var e=l(0,!0).lab()[0],n=l(1,!0).lab()[0],t=nn.max&&(n.max=r),n.count+=1)}),n.domain=[n.min,n.max],n.limits=function(r,e){return sn(n,r,e)},n},sn=function(r,e,n){void 0===e&&(e="equal"),void 0===n&&(n=7);var t=(r="array"==$(r)?dn(r):r).min,a=r.max,f=r.values.sort(function(r,e){return r-e});if(1===n)return[t,a];var o=[];if("c"===e.substr(0,1)&&(o.push(t),o.push(a)),"e"===e.substr(0,1)){o.push(t);for(var u=1;u 0");var c=Math.LOG10E*un(t),i=Math.LOG10E*un(a);o.push(t);for(var l=1;l(color1, color2, mode='lab') chroma.distance('#fff', '#f0f');

chroma.deltaE

-

(reference, sample, L=1, C=1)

-

Computes color difference as developed by the Colour Measurement Committee of the Society of Dyers and Colourists (CMC) in 1984. The implementation is adapted from Bruce Lindbloom. The parameters L and C are weighting factors for lightness and chromaticity.

-
chroma.deltaE('#ededee', '#edeeed');
+

(color1, color2, Kl=1, Kc=1, Kh=1)

+

Computes color difference as developed by the International Commission on Illumination (CIE) in 2000. The implementation is based on the formula from Bruce Lindbloom. Resulting values range from 0 (no difference) to 100 (maximum difference), and are a metric for how the human eye percieves color difference. The optional parameters Kl, Kc, and Kh may be used to adjust weightings of lightness, chroma, and hue.

+
chroma.deltaE('#ededee', '#ededee');
+chroma.deltaE('#ededee', '#edeeed');
 chroma.deltaE('#ececee', '#eceeec');
 chroma.deltaE('#e9e9ee', '#e9eee9');
 chroma.deltaE('#e4e4ee', '#e4eee4');
 chroma.deltaE('#e0e0ee', '#e0eee0');
+chroma.deltaE('#000000', '#ffffff');
 
 

chroma.brewer

diff --git a/docs/libs/chroma-light.js b/docs/libs/chroma-light.js index 38f132e0..8647540f 100644 --- a/docs/libs/chroma-light.js +++ b/docs/libs/chroma-light.js @@ -199,7 +199,7 @@ }; chroma.Color = Color_1; - chroma.version = '2.1.0'; + chroma.version = '2.1.1'; var chroma_1 = chroma; diff --git a/docs/libs/chroma-light.min.js b/docs/libs/chroma-light.min.js index b6a2ff37..875d39ca 100644 --- a/docs/libs/chroma-light.min.js +++ b/docs/libs/chroma-light.min.js @@ -55,4 +55,4 @@ * @preserve */ -!function(t,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):t.chroma=r()}(this,function(){"use strict";for(var n=function(t,r,n){return void 0===r&&(r=0),void 0===n&&(n=1),t>16,r>>8&255,255&r,1]}if(t.match(K)){5!==t.length&&9!==t.length||(t=t.substr(1)),4===t.length&&(t=(t=t.split(""))[0]+t[0]+t[1]+t[1]+t[2]+t[2]+t[3]+t[3]);var n=parseInt(t,16);return[n>>24&255,n>>16&255,n>>8&255,Math.round((255&n)/255*100)/100]}throw new Error("unknown hex color: "+t)},T=u;d.prototype.hex=function(t){return H(this._rgb,t)},b.hex=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];return new(Function.prototype.bind.apply(d,[null].concat(t,["hex"])))},h.format.hex=Q,h.autodetect.push({p:4,test:function(t){for(var r=[],n=arguments.length-1;0>16,r>>8&255,255&r,1]}if(t.match(H)){4===(t=5===t.length||9===t.length?t.substr(1):t).length&&(t=(t=t.split(""))[0]+t[0]+t[1]+t[1]+t[2]+t[2]+t[3]+t[3]);r=parseInt(t,16);return[r>>24&255,r>>16&255,r>>8&255,Math.round((255&r)/255*100)/100]}throw new Error("unknown hex color: "+t)},J=u;d.prototype.hex=function(t){return z(this._rgb,t)},i.hex=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];return new(Function.prototype.bind.apply(d,[null].concat(t,["hex"])))},h.format.hex=l,h.autodetect.push({p:4,test:function(t){for(var r=[],n=arguments.length-1;0= 360) { h1 -= 360; } - var t = (h1 >= 164.0) && (h1 <= 345.0) ? (0.56 + abs$1(0.2 * cos$4((PI$2 * (h1 + 168.0)) / 180.0))) : (0.36 + abs$1(0.4 * cos$4((PI$2 * (h1 + 35.0)) / 180.0))); - var c4 = c1 * c1 * c1 * c1; - var f = sqrt$4(c4 / (c4 + 1900.0)); - var sh = sc * (((f * t) + 1.0) - f); - var delL = L1 - L2; - var delC = c1 - c2; - var delA = a1 - a2; - var delB = b1 - b2; - var dH2 = ((delA * delA) + (delB * delB)) - (delC * delC); - var v1 = delL / (L * sl); - var v2 = delC / (C * sc); - var v3 = sh; - return sqrt$4((v1 * v1) + (v2 * v2) + (dH2 / (v3 * v3))); + var avgL = (L1 + L2)/2; + var C1 = sqrt$4(pow$8(a1, 2) + pow$8(b1, 2)); + var C2 = sqrt$4(pow$8(a2, 2) + pow$8(b2, 2)); + var avgC = (C1 + C2)/2; + var G = 0.5*(1-sqrt$4(pow$8(avgC, 7)/(pow$8(avgC, 7) + pow$8(25, 7)))); + var a1p = a1*(1+G); + var a2p = a2*(1+G); + var C1p = sqrt$4(pow$8(a1p, 2) + pow$8(b1, 2)); + var C2p = sqrt$4(pow$8(a2p, 2) + pow$8(b2, 2)); + var avgCp = (C1p + C2p)/2; + var arctan1 = rad2deg(atan2$2(b1, a1p)); + var arctan2 = rad2deg(atan2$2(b2, a2p)); + var h1p = arctan1 >= 0 ? arctan1 : arctan1 + 360; + var h2p = arctan2 >= 0 ? arctan2 : arctan2 + 360; + var avgHp = abs$1(h1p - h2p) > 180 ? (h1p + h2p + 360)/2 : (h1p + h2p)/2; + var T = 1 - 0.17*cos$4(deg2rad(avgHp - 30)) + 0.24*cos$4(deg2rad(2*avgHp)) + 0.32*cos$4(deg2rad(3*avgHp + 6)) - 0.2*cos$4(deg2rad(4*avgHp - 63)); + var deltaHp = h2p - h1p; + deltaHp = abs$1(deltaHp) <= 180 ? deltaHp : h2p <= h1p ? deltaHp + 360 : deltaHp - 360; + deltaHp = 2*sqrt$4(C1p*C2p)*sin$3(deg2rad(deltaHp)/2); + var deltaL = L2 - L1; + var deltaCp = C2p - C1p; + var sl = 1 + (0.015*pow$8(avgL - 50, 2))/sqrt$4(20 + pow$8(avgL - 50, 2)); + var sc = 1 + 0.045*avgCp; + var sh = 1 + 0.015*avgCp*T; + var deltaTheta = 30*exp(-pow$8((avgHp - 275)/25, 2)); + var Rc = 2*sqrt$4(pow$8(avgCp, 7)/(pow$8(avgCp, 7) + pow$8(25, 7))); + var Rt = -Rc*sin$3(2*deg2rad(deltaTheta)); + var result = sqrt$4(pow$8(deltaL/(Kl*sl), 2) + pow$8(deltaCp/(Kc*sc), 2) + pow$8(deltaHp/(Kh*sh), 2) + Rt*(deltaCp/(Kc*sc))*(deltaHp/(Kh*sh))); + return max$2(0, min$2(100, result)); }; // simple Euclidean distance diff --git a/docs/libs/chroma.min.js b/docs/libs/chroma.min.js index 93cac504..1bf567b8 100644 --- a/docs/libs/chroma.min.js +++ b/docs/libs/chroma.min.js @@ -55,4 +55,4 @@ * @preserve */ -!function(r,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):r.chroma=e()}(this,function(){"use strict";for(var n=function(r,e,n){return void 0===e&&(e=0),void 0===n&&(n=1),r>16,e>>8&255,255&e,1]}if(r.match(fr)){5!==r.length&&9!==r.length||(r=r.substr(1)),4===r.length&&(r=(r=r.split(""))[0]+r[0]+r[1]+r[1]+r[2]+r[2]+r[3]+r[3]);var n=parseInt(r,16);return[n>>24&255,n>>16&255,n>>8&255,Math.round((255&n)/255*100)/100]}throw new Error("unknown hex color: "+r)},ur=o.type;A.prototype.hex=function(r){return tr(this._rgb,r)},_.hex=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];return new(Function.prototype.bind.apply(A,[null].concat(r,["hex"])))},b.format.hex=or,b.autodetect.push({p:4,test:function(r){for(var e=[],n=arguments.length-1;0>16,r>>8&255,255&r,1];throw new Error("unknown num color: "+r)},xe=o.type;A.prototype.num=function(){return Me(this._rgb)},_.num=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];return new(Function.prototype.bind.apply(A,[null].concat(r,["num"])))},b.format.num=_e,b.autodetect.push({p:5,test:function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];if(1===r.length&&"number"===xe(r[0])&&0<=r[0]&&r[0]<=16777215)return"num"}});var Ae=o.unpack,Ee=o.type,Pe=Math.round;A.prototype.rgb=function(r){return void 0===r&&(r=!0),!1===r?this._rgb.slice(0,3):this._rgb.slice(0,3).map(Pe)},A.prototype.rgba=function(n){return void 0===n&&(n=!0),this._rgb.slice(0,4).map(function(r,e){return e<3?!1===n?r:Pe(r):r})},_.rgb=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];return new(Function.prototype.bind.apply(A,[null].concat(r,["rgb"])))},b.format.rgb=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];var n=Ae(r,"rgba");return void 0===n[3]&&(n[3]=1),n},b.autodetect.push({p:3,test:function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];if(r=Ae(r,"rgba"),"array"===Ee(r)&&(3===r.length||4===r.length&&"number"==Ee(r[3])&&0<=r[3]&&r[3]<=1))return"rgb"}});var Fe=Math.log,Oe=function(r){var e,n,t,a=r/100;return t=a<66?(e=255,n=-155.25485562709179-.44596950469579133*(n=a-2)+104.49216199393888*Fe(n),a<20?0:.8274096064007395*(t=a-10)-254.76935184120902+115.67994401066147*Fe(t)):(e=351.97690566805693+.114206453784165*(e=a-55)-40.25366309332127*Fe(e),n=325.4494125711974+.07943456536662342*(n=a-50)-28.0852963507957*Fe(n),255),[e,n,t,1]},je=o.unpack,Ge=Math.round,qe=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];for(var n,t=je(r,"rgb"),a=t[0],f=t[2],o=1e3,u=4e4;.4=f/a?u=n:o=n}return Ge(n)};A.prototype.temp=A.prototype.kelvin=A.prototype.temperature=function(){return qe(this._rgb)},_.temp=_.kelvin=_.temperature=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];return new(Function.prototype.bind.apply(A,[null].concat(r,["temp"])))},b.format.temp=b.format.kelvin=b.format.temperature=Oe;var Le=o.type;A.prototype.alpha=function(r,e){return void 0===e&&(e=!1),void 0!==r&&"number"===Le(r)?e?(this._rgb[3]=r,this):new A([this._rgb[0],this._rgb[1],this._rgb[2],r],"rgb"):this._rgb[3]},A.prototype.clipped=function(){return this._rgb._clipped||!1},A.prototype.darken=function(r){void 0===r&&(r=1);var e=this.lab();return e[0]-=qr*r,new A(e,"lab").alpha(this.alpha(),!0)},A.prototype.brighten=function(r){return void 0===r&&(r=1),this.darken(-r)},A.prototype.darker=A.prototype.darken,A.prototype.brighter=A.prototype.brighten,A.prototype.get=function(r){var e=r.split("."),n=e[0],t=e[1],a=this[n]();if(t){var f=n.indexOf(t);if(-1=s[n];)n++;return n-1}(r)/(s.length-2):g!==p?(r-p)/(g-p):1;t=k(t),e||(t=w(t)),1!==y&&(t=nn(t,y)),t=d[0]+t*(1-d[0]-d[1]),t=Math.min(1,Math.max(0,t));var a=Math.floor(1e4*t);if(m&&v[a])n=v[a];else{if("array"===en(b))for(var f=0;f=u[e+1];)e++;var n=(r-u[e])/(u[e+1]-u[e]);return o[e]+n*(o[e+1]-o[e])})}}return l=[p,g],N},N.mode=function(r){return arguments.length?(u=r,f(),N):u},N.range=function(r,e){return a(r),N},N.out=function(r){return n=r,N},N.spread=function(r){return arguments.length?(e=r,N):e},N.correctLightness=function(r){return null==r&&(r=!0),t=r,f(),w=t?function(r){for(var e=M(0,!0).lab()[0],n=M(1,!0).lab()[0],t=nn.max&&(n.max=r),n.count+=1)}),n.domain=[n.min,n.max],n.limits=function(r,e){return Nn(n,r,e)},n},Nn=function(r,e,n){void 0===e&&(e="equal"),void 0===n&&(n=7),"array"==Y(r)&&(r=Mn(r));var t=r.min,a=r.max,f=r.values.sort(function(r,e){return r-e});if(1===n)return[t,a];var o=[];if("c"===e.substr(0,1)&&(o.push(t),o.push(a)),"e"===e.substr(0,1)){o.push(t);for(var u=1;u 0");var c=Math.LOG10E*mn(t),i=Math.LOG10E*mn(a);o.push(t);for(var l=1;l>16,e>>8&255,255&e,1]}if(r.match(tr)){4===(r=5===r.length||9===r.length?r.substr(1):r).length&&(r=(r=r.split(""))[0]+r[0]+r[1]+r[1]+r[2]+r[2]+r[3]+r[3]);e=parseInt(r,16);return[e>>24&255,e>>16&255,e>>8&255,Math.round((255&e)/255*100)/100]}throw new Error("unknown hex color: "+r)},fr=o.type;p.prototype.hex=function(r){return er(this._rgb,r)},N.hex=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];return new(Function.prototype.bind.apply(p,[null].concat(r,["hex"])))},h.format.hex=ar,h.autodetect.push({p:4,test:function(r){for(var e=[],n=arguments.length-1;0>16,r>>8&255,255&r,1];throw new Error("unknown num color: "+r)},ye=o.type;p.prototype.num=function(){return ve(this._rgb)},N.num=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];return new(Function.prototype.bind.apply(p,[null].concat(r,["num"])))},h.format.num=m,h.autodetect.push({p:5,test:function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];if(1===r.length&&"number"===ye(r[0])&&0<=r[0]&&r[0]<=16777215)return"num"}});var we=o.unpack,ke=o.type,Me=Math.round;p.prototype.rgb=function(r){return!1===(r=void 0===r?!0:r)?this._rgb.slice(0,3):this._rgb.slice(0,3).map(Me)},p.prototype.rgba=function(n){return void 0===n&&(n=!0),this._rgb.slice(0,4).map(function(r,e){return!(e<3)||!1===n?r:Me(r)})},N.rgb=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];return new(Function.prototype.bind.apply(p,[null].concat(r,["rgb"])))},h.format.rgb=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];var n=we(r,"rgba");return void 0===n[3]&&(n[3]=1),n},h.autodetect.push({p:3,test:function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];if(r=we(r,"rgba"),"array"===ke(r)&&(3===r.length||4===r.length&&"number"==ke(r[3])&&0<=r[3]&&r[3]<=1))return"rgb"}});var Ne=Math.log,_e=function(r){var e,n,r=r/100,t=r<66?(e=255,n=-155.25485562709179-.44596950469579133*(n=r-2)+104.49216199393888*Ne(n),r<20?0:.8274096064007395*(t=r-10)-254.76935184120902+115.67994401066147*Ne(t)):(e=351.97690566805693+.114206453784165*(e=r-55)-40.25366309332127*Ne(e),n=325.4494125711974+.07943456536662342*(n=r-50)-28.0852963507957*Ne(n),255);return[e,n,t,1]},xe=o.unpack,Ae=Math.round,Ee=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];for(var n=xe(r,"rgb"),t=n[0],a=n[2],f=1e3,o=4e4;.4=a/t?o=u:f=u}return Ae(u)};p.prototype.temp=p.prototype.kelvin=p.prototype.temperature=function(){return Ee(this._rgb)},N.temp=N.kelvin=N.temperature=function(){for(var r=[],e=arguments.length;e--;)r[e]=arguments[e];return new(Function.prototype.bind.apply(p,[null].concat(r,["temp"])))},h.format.temp=h.format.kelvin=h.format.temperature=_e;var Pe=o.type;p.prototype.alpha=function(r,e){return void 0===e&&(e=!1),void 0!==r&&"number"===Pe(r)?e?(this._rgb[3]=r,this):new p([this._rgb[0],this._rgb[1],this._rgb[2],r],"rgb"):this._rgb[3]},p.prototype.clipped=function(){return this._rgb._clipped||!1},p.prototype.darken=function(r){void 0===r&&(r=1);var e=this.lab();return e[0]-=jr*r,new p(e,"lab").alpha(this.alpha(),!0)},p.prototype.brighten=function(r){return this.darken(-(r=void 0===r?1:r))},p.prototype.darker=p.prototype.darken,p.prototype.brighter=p.prototype.brighten,p.prototype.get=function(r){var e=r.split("."),n=e[0],t=e[1],r=this[n]();if(t){e=n.indexOf(t);if(-1=s[n];)n++;return n-1}(r)/(s.length-2):g!==p?(r-p)/(g-p):1,t=k(t),e||(t=w(t)),1!==y&&(t=Je(t,y)),t=d[0]+t*(1-d[0]-d[1]),t=Math.min(1,Math.max(0,t)),e=Math.floor(1e4*t),m&&v[e])n=v[e];else{if("array"===He(b))for(var a=0;a=o[e+1];)e++;var n=(r-o[e])/(o[e+1]-o[e]);return f[e]+n*(f[e+1]-f[e])}))}return h=[p,g],M},M.mode=function(r){return arguments.length?(o=r,f(),M):o},M.range=function(r,e){return n(r),M},M.out=function(r){return a=r,M},M.spread=function(r){return arguments.length?(t=r,M):t},M.correctLightness=function(r){return e=r=null==r?!0:r,f(),w=e?function(r){for(var e=l(0,!0).lab()[0],n=l(1,!0).lab()[0],t=nn.max&&(n.max=r),n.count+=1)}),n.domain=[n.min,n.max],n.limits=function(r,e){return sn(n,r,e)},n},sn=function(r,e,n){void 0===e&&(e="equal"),void 0===n&&(n=7);var t=(r="array"==$(r)?dn(r):r).min,a=r.max,f=r.values.sort(function(r,e){return r-e});if(1===n)return[t,a];var o=[];if("c"===e.substr(0,1)&&(o.push(t),o.push(a)),"e"===e.substr(0,1)){o.push(t);for(var u=1;u 0");var c=Math.LOG10E*un(t),i=Math.LOG10E*un(a);o.push(t);for(var l=1;l= 360) { h1 -= 360; } - const t = (h1 >= 164.0) && (h1 <= 345.0) ? (0.56 + abs(0.2 * cos((PI * (h1 + 168.0)) / 180.0))) : (0.36 + abs(0.4 * cos((PI * (h1 + 35.0)) / 180.0))); - const c4 = c1 * c1 * c1 * c1; - const f = sqrt(c4 / (c4 + 1900.0)); - const sh = sc * (((f * t) + 1.0) - f); - const delL = L1 - L2; - const delC = c1 - c2; - const delA = a1 - a2; - const delB = b1 - b2; - const dH2 = ((delA * delA) + (delB * delB)) - (delC * delC); - const v1 = delL / (L * sl); - const v2 = delC / (C * sc); - const v3 = sh; - return sqrt((v1 * v1) + (v2 * v2) + (dH2 / (v3 * v3))); + const avgL = (L1 + L2)/2; + const C1 = sqrt(pow(a1, 2) + pow(b1, 2)); + const C2 = sqrt(pow(a2, 2) + pow(b2, 2)); + const avgC = (C1 + C2)/2; + const G = 0.5*(1-sqrt(pow(avgC, 7)/(pow(avgC, 7) + pow(25, 7)))); + const a1p = a1*(1+G); + const a2p = a2*(1+G); + const C1p = sqrt(pow(a1p, 2) + pow(b1, 2)); + const C2p = sqrt(pow(a2p, 2) + pow(b2, 2)); + const avgCp = (C1p + C2p)/2; + const arctan1 = rad2deg(atan2(b1, a1p)); + const arctan2 = rad2deg(atan2(b2, a2p)); + const h1p = arctan1 >= 0 ? arctan1 : arctan1 + 360; + const h2p = arctan2 >= 0 ? arctan2 : arctan2 + 360; + const avgHp = abs(h1p - h2p) > 180 ? (h1p + h2p + 360)/2 : (h1p + h2p)/2; + const T = 1 - 0.17*cos(deg2rad(avgHp - 30)) + 0.24*cos(deg2rad(2*avgHp)) + 0.32*cos(deg2rad(3*avgHp + 6)) - 0.2*cos(deg2rad(4*avgHp - 63)); + let deltaHp = h2p - h1p; + deltaHp = abs(deltaHp) <= 180 ? deltaHp : h2p <= h1p ? deltaHp + 360 : deltaHp - 360; + deltaHp = 2*sqrt(C1p*C2p)*sin(deg2rad(deltaHp)/2); + const deltaL = L2 - L1; + const deltaCp = C2p - C1p; + const sl = 1 + (0.015*pow(avgL - 50, 2))/sqrt(20 + pow(avgL - 50, 2)); + const sc = 1 + 0.045*avgCp; + const sh = 1 + 0.015*avgCp*T; + const deltaTheta = 30*exp(-pow((avgHp - 275)/25, 2)); + const Rc = 2*sqrt(pow(avgCp, 7)/(pow(avgCp, 7) + pow(25, 7))); + const Rt = -Rc*sin(2*deg2rad(deltaTheta)); + const result = sqrt(pow(deltaL/(Kl*sl), 2) + pow(deltaCp/(Kc*sc), 2) + pow(deltaHp/(Kh*sh), 2) + Rt*(deltaCp/(Kc*sc))*(deltaHp/(Kh*sh))); + return max(0, min(100, result)); }; - diff --git a/test/delta-e.test.js b/test/delta-e.test.js new file mode 100644 index 00000000..45be2deb --- /dev/null +++ b/test/delta-e.test.js @@ -0,0 +1,44 @@ +const vows = require('vows') +const assert = require('assert'); +require('es6-shim'); + +const deltaE = require('../src/utils/delta-e'); + +// due to floating-point arithmetic on different devices, differences in decimals may be found. +// Running http://www.brucelindbloom.com/index.html?ColorDifferenceCalc.html JS code locally +// on the same device as the delta-e code in this library will provide the exact same results. +const tests = { + nodifference: {in: [0x000000, 0x000000], out: 0}, + maxdifference: {in: [0xFFFFFF, 0x000000], out: 100}, + redgreen: {in: [0xff0000, 0x00ff00], out: 86.6082374535373}, + greenred: {in: [0x00ff00, 0xff0000], out: 86.6082374535373}, + beef: {in: [0x00beef, 0xbeef00], out: 56.75641476716213}, + similar: {in: [0xededee, 0xedeeed], out: 1.3211081906645834}, + similarish: {in: [0xececee, 0xeceeec], out: 2.601879624602976}, + lesssimilar: {in: [0xe9e9ee, 0xe9eee9], out: 6.220878841368716}, + lesssimilarish: {in: [0xe4e4ee, 0xe4eee4], out: 11.598175546813964}, + notverysimilar: {in: [0xe0e0ee, 0xe0eee0], out: 15.391371803506503}, +}; + +const batch = {}; + +Object.keys(tests).forEach(key => { + batch[`delta-e ${key}`] = { + topic: tests[key], + num(topic) { + // checks if result is within 1% of "out" value. + // This is done because results may be slightly + // off depending on device setup due to floating + // point arithmetic. If "out" is 0, and result was 0, + // avoid divide by zero and set to true. + let result = deltaE(topic.in[0], topic.in[1]); + let percent = Math.abs(result-topic.out)/topic.out; + assert.deepEqual((topic.out == 0 && result == 0) || percent < 0.01, true); + } + } +}) + +vows + .describe('Testing delta-e color delta (dE00)') + .addBatch(batch) + .export(module);