Skip to content

Instantly share code, notes, and snippets.

@yinshanyang
Last active August 29, 2015 14:23
Show Gist options
  • Save yinshanyang/e90f8830c48f07a2085c to your computer and use it in GitHub Desktop.
Save yinshanyang/e90f8830c48f07a2085c to your computer and use it in GitHub Desktop.
D3 HUSL Interpolator
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){(function(){var L_to_Y,Y_to_L,conv,distanceFromPole,dotProduct,epsilon,fromLinear,getBounds,intersectLineLine,kappa,lengthOfRayUntilIntersect,m,m_inv,maxChromaForLH,maxSafeChromaForL,refU,refV,refX,refY,refZ,rgbPrepare,root,round,toLinear;m={R:[3.240969941904521,-1.537383177570093,-.498610760293],G:[-.96924363628087,1.87596750150772,.041555057407175],B:[.055630079696993,-.20397695888897,1.056971514242878]};m_inv={X:[.41239079926595,.35758433938387,.18048078840183],Y:[.21263900587151,.71516867876775,.072192315360733],Z:[.019330818715591,.11919477979462,.95053215224966]};refX=.95045592705167;refY=1;refZ=1.089057750759878;refU=.19783000664283;refV=.46831999493879;kappa=903.2962962;epsilon=.0088564516;getBounds=function(L){var bottom,channel,j,k,len1,len2,m1,m2,m3,ref,ref1,ref2,ret,sub1,sub2,t,top1,top2;sub1=Math.pow(L+16,3)/1560896;sub2=sub1>epsilon?sub1:L/kappa;ret=[];ref=["R","G","B"];for(j=0,len1=ref.length;j<len1;j++){channel=ref[j];ref1=m[channel],m1=ref1[0],m2=ref1[1],m3=ref1[2];ref2=[0,1];for(k=0,len2=ref2.length;k<len2;k++){t=ref2[k];top1=(284517*m1-94839*m3)*sub2;top2=(838422*m3+769860*m2+731718*m1)*L*sub2-769860*t*L;bottom=(632260*m3-126452*m2)*sub2+126452*t;ret.push([top1/bottom,top2/bottom])}}return ret};intersectLineLine=function(line1,line2){return(line1[1]-line2[1])/(line2[0]-line1[0])};distanceFromPole=function(point){return Math.sqrt(Math.pow(point[0],2)+Math.pow(point[1],2))};lengthOfRayUntilIntersect=function(theta,line){var b1,len,m1;m1=line[0],b1=line[1];len=b1/(Math.sin(theta)-m1*Math.cos(theta));if(len<0){return null}return len};maxSafeChromaForL=function(L){var b1,j,len1,lengths,m1,ref,ref1,x;lengths=[];ref=getBounds(L);for(j=0,len1=ref.length;j<len1;j++){ref1=ref[j],m1=ref1[0],b1=ref1[1];x=intersectLineLine([m1,b1],[-1/m1,0]);lengths.push(distanceFromPole([x,b1+x*m1]))}return Math.min.apply(Math,lengths)};maxChromaForLH=function(L,H){var hrad,j,l,len1,lengths,line,ref;hrad=H/360*Math.PI*2;lengths=[];ref=getBounds(L);for(j=0,len1=ref.length;j<len1;j++){line=ref[j];l=lengthOfRayUntilIntersect(hrad,line);if(l!==null){lengths.push(l)}}return Math.min.apply(Math,lengths)};dotProduct=function(a,b){var i,j,ref,ret;ret=0;for(i=j=0,ref=a.length-1;0<=ref?j<=ref:j>=ref;i=0<=ref?++j:--j){ret+=a[i]*b[i]}return ret};round=function(num,places){var n;n=Math.pow(10,places);return Math.round(num*n)/n};fromLinear=function(c){if(c<=.0031308){return 12.92*c}else{return 1.055*Math.pow(c,1/2.4)-.055}};toLinear=function(c){var a;a=.055;if(c>.04045){return Math.pow((c+a)/(1+a),2.4)}else{return c/12.92}};rgbPrepare=function(tuple){var ch,j,k,len1,len2,n,results;tuple=function(){var j,len1,results;results=[];for(j=0,len1=tuple.length;j<len1;j++){n=tuple[j];results.push(round(n,3))}return results}();for(j=0,len1=tuple.length;j<len1;j++){ch=tuple[j];if(ch<-1e-4||ch>1.0001){throw new Error("Illegal rgb value: "+ch)}if(ch<0){ch=0}if(ch>1){ch=1}}results=[];for(k=0,len2=tuple.length;k<len2;k++){ch=tuple[k];results.push(Math.round(ch*255))}return results};conv={xyz:{},luv:{},lch:{},husl:{},huslp:{},rgb:{},hex:{}};conv.xyz.rgb=function(tuple){var B,G,R;R=fromLinear(dotProduct(m.R,tuple));G=fromLinear(dotProduct(m.G,tuple));B=fromLinear(dotProduct(m.B,tuple));return[R,G,B]};conv.rgb.xyz=function(tuple){var B,G,R,X,Y,Z,rgbl;R=tuple[0],G=tuple[1],B=tuple[2];rgbl=[toLinear(R),toLinear(G),toLinear(B)];X=dotProduct(m_inv.X,rgbl);Y=dotProduct(m_inv.Y,rgbl);Z=dotProduct(m_inv.Z,rgbl);return[X,Y,Z]};Y_to_L=function(Y){if(Y<=epsilon){return Y/refY*kappa}else{return 116*Math.pow(Y/refY,1/3)-16}};L_to_Y=function(L){if(L<=8){return refY*L/kappa}else{return refY*Math.pow((L+16)/116,3)}};conv.xyz.luv=function(tuple){var L,U,V,X,Y,Z,varU,varV;X=tuple[0],Y=tuple[1],Z=tuple[2];varU=4*X/(X+15*Y+3*Z);varV=9*Y/(X+15*Y+3*Z);L=Y_to_L(Y);if(L===0){return[0,0,0]}U=13*L*(varU-refU);V=13*L*(varV-refV);return[L,U,V]};conv.luv.xyz=function(tuple){var L,U,V,X,Y,Z,varU,varV;L=tuple[0],U=tuple[1],V=tuple[2];if(L===0){return[0,0,0]}varU=U/(13*L)+refU;varV=V/(13*L)+refV;Y=L_to_Y(L);X=0-9*Y*varU/((varU-4)*varV-varU*varV);Z=(9*Y-15*varV*Y-varV*X)/(3*varV);return[X,Y,Z]};conv.luv.lch=function(tuple){var C,H,Hrad,L,U,V;L=tuple[0],U=tuple[1],V=tuple[2];C=Math.pow(Math.pow(U,2)+Math.pow(V,2),1/2);Hrad=Math.atan2(V,U);H=Hrad*360/2/Math.PI;if(H<0){H=360+H}return[L,C,H]};conv.lch.luv=function(tuple){var C,H,Hrad,L,U,V;L=tuple[0],C=tuple[1],H=tuple[2];Hrad=H/360*2*Math.PI;U=Math.cos(Hrad)*C;V=Math.sin(Hrad)*C;return[L,U,V]};conv.husl.lch=function(tuple){var C,H,L,S,max;H=tuple[0],S=tuple[1],L=tuple[2];if(L>99.9999999){return[100,0,H]}if(L<1e-8){return[0,0,H]}max=maxChromaForLH(L,H);C=max/100*S;return[L,C,H]};conv.lch.husl=function(tuple){var C,H,L,S,max;L=tuple[0],C=tuple[1],H=tuple[2];if(L>99.9999999){return[H,0,100]}if(L<1e-8){return[H,0,0]}max=maxChromaForLH(L,H);S=C/max*100;return[H,S,L]};conv.huslp.lch=function(tuple){var C,H,L,S,max;H=tuple[0],S=tuple[1],L=tuple[2];if(L>99.9999999){return[100,0,H]}if(L<1e-8){return[0,0,H]}max=maxSafeChromaForL(L);C=max/100*S;return[L,C,H]};conv.lch.huslp=function(tuple){var C,H,L,S,max;L=tuple[0],C=tuple[1],H=tuple[2];if(L>99.9999999){return[H,0,100]}if(L<1e-8){return[H,0,0]}max=maxSafeChromaForL(L);S=C/max*100;return[H,S,L]};conv.rgb.hex=function(tuple){var ch,hex,j,len1;hex="#";tuple=rgbPrepare(tuple);for(j=0,len1=tuple.length;j<len1;j++){ch=tuple[j];ch=ch.toString(16);if(ch.length===1){ch="0"+ch}hex+=ch}return hex};conv.hex.rgb=function(hex){var b,g,j,len1,n,r,ref,results;if(hex.charAt(0)==="#"){hex=hex.substring(1,7)}r=hex.substring(0,2);g=hex.substring(2,4);b=hex.substring(4,6);ref=[r,g,b];results=[];for(j=0,len1=ref.length;j<len1;j++){n=ref[j];results.push(parseInt(n,16)/255)}return results};conv.lch.rgb=function(tuple){return conv.xyz.rgb(conv.luv.xyz(conv.lch.luv(tuple)))};conv.rgb.lch=function(tuple){return conv.luv.lch(conv.xyz.luv(conv.rgb.xyz(tuple)))};conv.husl.rgb=function(tuple){return conv.lch.rgb(conv.husl.lch(tuple))};conv.rgb.husl=function(tuple){return conv.lch.husl(conv.rgb.lch(tuple))};conv.huslp.rgb=function(tuple){return conv.lch.rgb(conv.huslp.lch(tuple))};conv.rgb.huslp=function(tuple){return conv.lch.huslp(conv.rgb.lch(tuple))};root={};root.fromRGB=function(R,G,B){return conv.rgb.husl([R,G,B])};root.fromHex=function(hex){return conv.rgb.husl(conv.hex.rgb(hex))};root.toRGB=function(H,S,L){return conv.husl.rgb([H,S,L])};root.toHex=function(H,S,L){return conv.rgb.hex(conv.husl.rgb([H,S,L]))};root.p={};root.p.toRGB=function(H,S,L){return conv.xyz.rgb(conv.luv.xyz(conv.lch.luv(conv.huslp.lch([H,S,L]))))};root.p.toHex=function(H,S,L){return conv.rgb.hex(conv.xyz.rgb(conv.luv.xyz(conv.lch.luv(conv.huslp.lch([H,S,L])))))};root.p.fromRGB=function(R,G,B){return conv.lch.huslp(conv.luv.lch(conv.xyz.luv(conv.rgb.xyz([R,G,B]))))};root.p.fromHex=function(hex){return conv.lch.huslp(conv.luv.lch(conv.xyz.luv(conv.rgb.xyz(conv.hex.rgb(hex)))))};root._conv=conv;root._round=round;root._rgbPrepare=rgbPrepare;root._getBounds=getBounds;root._maxChromaForLH=maxChromaForLH;root._maxSafeChromaForL=maxSafeChromaForL;if(!(typeof module!=="undefined"&&module!==null||typeof jQuery!=="undefined"&&jQuery!==null||typeof requirejs!=="undefined"&&requirejs!==null)){this.HUSL=root}if(typeof module!=="undefined"&&module!==null){module.exports=root}if(typeof jQuery!=="undefined"&&jQuery!==null){jQuery.husl=root}if(typeof requirejs!=="undefined"&&requirejs!==null&&(typeof define!=="undefined"&&define!==null)){define(root)}}).call(this)},{}],2:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports["default"]=interpolateHusl;function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{"default":obj}}var _husl=require("husl");var _husl2=_interopRequireDefault(_husl);function interpolateHusl(a,b){a=toHusl(a);b=toHusl(b);var ah=a[0],as=a[1],al=a[2],bh=b[0]-ah,bs=b[1]-as,bl=b[2]-al;if(isNaN(bs))bs=0,as=isNaN(as)?b.s:as;if(isNaN(bh))bh=0,ah=isNaN(ah)?b.h:ah;else if(bh>180)bh-=360;else if(bh<-180)bh+=360;return function(t){return _husl2["default"].toHex(ah+bh*t,as+bs*t,al+bl*t)}}function toHusl(format){var r=0,g=0,b=0,color,m1,m2;m1=/([a-z]+)\((.*)\)/i.exec(format);if(m1){m2=m1[2].split(",");if(m1[1]==="hsl"){return[parseFloat(m2[0]),parseFloat(m2[1]),parseFloat(m2[2])]}}if(format!=null&&format.charAt(0)==="#"&&!isNaN(color=parseInt(format.slice(1),16))){if(format.length===4){r=(color&3840)>>4;r=r>>4|r;g=color&240;g=g>>4|g;b=color&15;b=b<<4|b}else if(format.length===7){r=(color&16711680)>>16;g=(color&65280)>>8;b=color&255}}return _husl2["default"].fromRGB(r/255,g/255,b/255)}module.exports=exports["default"]},{husl:1}],3:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports["default"]=interpolateHuslp;function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{"default":obj}}var _husl=require("husl");var _husl2=_interopRequireDefault(_husl);function interpolateHuslp(a,b){a=toHusl(a);b=toHusl(b);var ah=a[0],as=a[1],al=a[2],bh=b[0]-ah,bs=b[1]-as,bl=b[2]-al;if(isNaN(bs))bs=0,as=isNaN(as)?b.s:as;if(isNaN(bh))bh=0,ah=isNaN(ah)?b.h:ah;else if(bh>180)bh-=360;else if(bh<-180)bh+=360;return function(t){return _husl2["default"].p.toHex(ah+bh*t,as+bs*t,al+bl*t)}}function toHusl(format){var r=0,g=0,b=0,color,m1,m2;m1=/([a-z]+)\((.*)\)/i.exec(format);if(m1){m2=m1[2].split(",");if(m1[1]==="hsl"){return[parseFloat(m2[0]),parseFloat(m2[1]),parseFloat(m2[2])]}}if(format!=null&&format.charAt(0)==="#"&&!isNaN(color=parseInt(format.slice(1),16))){if(format.length===4){r=(color&3840)>>4;r=r>>4|r;g=color&240;g=g>>4|g;b=color&15;b=b<<4|b}else if(format.length===7){r=(color&16711680)>>16;g=(color&65280)>>8;b=color&255}}return _husl2["default"].fromRGB(r/255,g/255,b/255)}module.exports=exports["default"]},{husl:1}],4:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{"default":obj}}var _husl=require("./husl");var _husl2=_interopRequireDefault(_husl);var _huslp=require("./huslp");var _huslp2=_interopRequireDefault(_huslp);if(typeof window!=="undefined"){if(window.d3!==undefined){window.d3.interpolateHusl=_husl2["default"];window.d3.interpolateHuslp=_huslp2["default"]}}exports.interpolateHusl=_husl2["default"];exports.interpolateHuslp=_huslp2["default"]},{"./husl":2,"./huslp":3}]},{},[4]);
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.space {
position: absolute;
}
.space div {
position: absolute;
top: 0;
left: 20px;
}
</style>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="d3-interpolate-husl.min.js"></script>
<script>
var spaces = [
{name: "HSL", interpolate: d3.interpolateHsl},
{name: "HCL", interpolate: d3.interpolateHcl},
{name: "Lab", interpolate: d3.interpolateLab},
{name: "RGB", interpolate: d3.interpolateRgb},
{name: "HUSL", interpolate: d3.interpolateHusl},
{name: "HUSLp", interpolate: d3.interpolateHuslp}
];
var y = d3.scale.ordinal()
.domain(spaces.map(function(d) { return d.name; }))
.rangeRoundBands([0, 500], .09);
var margin = y.range()[0],
width = 960 - margin - margin,
height = y.rangeBand();
var color = d3.scale.linear()
.domain([0, width])
.range(["hsl(20,100%,60%)", "hsl(125, 100%,60%)"]);
var space = d3.select("body").selectAll(".space")
.data(spaces)
.enter().append("div")
.attr("class", "space")
.style("width", width + "px")
.style("height", height + "px")
.style("left", margin + "px")
.style("top", function(d, i) { return y(d.name) + "px"; });
space.append("canvas")
.attr("width", width)
.attr("height", 1)
.style("width", width + "px")
.style("height", height + "px")
.each(render);
space.append("div")
.style("line-height", height + "px")
.text(function(d) { return d.name; });
function render(d) {
var context = this.getContext("2d"),
image = context.createImageData(width, 1);
color.interpolate(d.interpolate);
for (var i = 0, j = -1, c; i < width; ++i) {
c = d3.rgb(color(i));
image.data[++j] = c.r;
image.data[++j] = c.g;
image.data[++j] = c.b;
image.data[++j] = 255;
}
context.putImageData(image, 0, 0);
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment