Skip to content

Instantly share code, notes, and snippets.

@nitaku
Last active August 29, 2015 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nitaku/3f02ab54b366477d8f30 to your computer and use it in GitHub Desktop.
Save nitaku/3f02ab54b366477d8f30 to your computer and use it in GitHub Desktop.
HCL decomposition: cubehelix
### Cubehelix plugin, from http://bl.ocks.org/mbostock/11413789 ###
`
(function() {
var radians = Math.PI / 180;
d3.scale.cubehelix = function() {
return d3.scale.linear()
.range([d3.hsl(300, .5, 0), d3.hsl(-240, .5, 1)])
.interpolate(d3.interpolateCubehelix);
};
d3.interpolateCubehelix = d3_interpolateCubehelix(1);
d3.interpolateCubehelix.gamma = d3_interpolateCubehelix;
function d3_interpolateCubehelix(gamma) {
return function(a, b) {
a = d3.hsl(a);
b = d3.hsl(b);
var ah = (a.h + 120) * radians,
bh = (b.h + 120) * radians - ah,
as = a.s,
bs = b.s - as,
al = a.l,
bl = b.l - al;
if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as;
if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah;
return function(t) {
var h = ah + bh * t,
l = Math.pow(al + bl * t, gamma),
a = (as + bs * t) * l * (1 - l);
return "#"
+ hex(l + a * (-0.14861 * Math.cos(h) + 1.78277 * Math.sin(h)))
+ hex(l + a * (-0.29227 * Math.cos(h) - 0.90649 * Math.sin(h)))
+ hex(l + a * (+1.97294 * Math.cos(h)));
};
};
}
function hex(v) {
var s = (v = v <= 0 ? 0 : v >= 1 ? 255 : v * 255 | 0).toString(16);
return v < 0x10 ? "0" + s : s;
}
})();
`
colorify = d3.scale.cubehelix()
.domain([0, 255])
palette = d3.range(256).map (i) -> colorify(i)
svg = d3.select('svg')
svg.selectAll('.colored_band')
.data(palette)
.enter().append('rect')
.attr('class', 'colored_band')
.attr('x', (d,i)->32+3.5*i)
.attr('y', 21)
.attr('width', 3.5)
.attr('height', 120)
.attr('fill', (d)->d )
svg.selectAll('.hue_band')
.data(palette)
.enter().append('rect')
.attr('class', 'hue_band')
.attr('x', (d,i)->32+3.5*i)
.attr('y', 166)
.attr('width', 3.5)
.attr('height', 60)
.attr('fill', (d) ->
hcl = d3.hcl(d)
hcl.c = 60
hcl.l = 60
return hcl
)
svg.selectAll('.chroma_band')
.data(palette)
.enter().append('rect')
.attr('class', 'chroma_band')
.attr('x', (d,i)->32+3.5*i)
.attr('y', 237)
.attr('width', 3.5)
.attr('height', 116)
.attr('fill', (d) ->
hcl = d3.hcl(d)
hcl.l = hcl.c/1.4
hcl.h = 80
hcl.c = 25
return hcl
)
svg.selectAll('.luminance_band')
.data(palette)
.enter().append('rect')
.attr('class', 'luminance_band')
.attr('x', (d,i)->32+3.5*i)
.attr('y', 364)
.attr('width', 3.5)
.attr('height', 116)
.attr('fill', (d) ->
hcl = d3.hcl(d)
hcl.c = 0
return hcl
)
x = d3.scale.linear()
.domain([0,255])
.range([32, 928])
h_y = d3.scale.linear()
.domain([0,360])
.range([166+60, 166])
c_y = d3.scale.linear()
.domain([0,150])
.range([237+116, 237])
l_y = d3.scale.linear()
.domain([0,100])
.range([364+116, 364])
h_line = d3.svg.line()
.x((_, i) -> x(i))
.y((d, i) ->
# work around NaN error
if i is 0
return h_y(0)
h = d3.hcl(d).h
return h_y( if h > 0 then h else h+360 )
)
c_line = d3.svg.line()
.x((_, i) -> x(i))
.y((d, i) ->
# work around NaN error
if i is 0
return c_y(0)
c_y( d3.hcl(d).c )
)
l_line = d3.svg.line()
.x((_, i) -> x(i))
.y((d, i) -> l_y( d3.hcl(d).l ) )
svg.append('path')
.datum(palette)
.attr('class', 'hue')
.attr('d', h_line)
svg.append('path')
.datum(palette)
.attr('class', 'chroma')
.attr('d', c_line)
svg.append('path')
.datum(palette)
.attr('class', 'luminance')
.attr('d', l_line)
h_axis = d3.svg.axis()
.scale(h_y)
.tickValues([0,360])
.orient('right')
.tickSize(3,5)
c_axis = d3.svg.axis()
.scale(c_y)
.ticks(4)
.orient('right')
.tickSize(3,5)
l_axis = d3.svg.axis()
.scale(l_y)
.ticks(2)
.orient('right')
.tickSize(3,5)
svg.append('g')
.attr('class', 'axis')
.attr('transform', 'translate(932,0)')
.call(h_axis)
svg.append('g')
.attr('class', 'axis')
.attr('transform', 'translate(932,0)')
.call(c_axis)
svg.append('g')
.attr('class', 'axis')
.attr('transform', 'translate(932,0)')
.call(l_axis)
body {
background: #272822;
margin: 0;
padding: 0;
}
svg {
background: white;
}
.label {
text-anchor: middle;
font-size: 24px;
}
rect {
shape-rendering: crispEdges;
}
path {
fill: none;
}
path.hue {
stroke: white;
stroke-width: 1;
}
path.chroma {
stroke: #FF3D00;
stroke-width: 1;
}
path.luminance {
stroke: black;
stroke-width: 0.6;
}
.axis .domain, .axis .tick line {
stroke: black;
shape-rendering: crispEdges;
}
.axis {
font-size: 8px;
font-family: sans-serif;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="HCL decomposition: cubehelix" />
<title>HCL decomposition: cubehelix</title>
<link rel="stylesheet" href="index.css">
<script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<svg height="500" width="960">
<text class="label" x="16" y="198" dy="0.35em">H</text>
<text class="label" x="16" y="295" dy="0.35em">C</text>
<text class="label" x="16" y="420" dy="0.35em">L</text>
</svg>
<script src="index.js"></script>
</body>
</html>
/* Cubehelix plugin, from http://bl.ocks.org/mbostock/11413789
*/
(function() {
(function() {
var radians = Math.PI / 180;
d3.scale.cubehelix = function() {
return d3.scale.linear()
.range([d3.hsl(300, .5, 0), d3.hsl(-240, .5, 1)])
.interpolate(d3.interpolateCubehelix);
};
d3.interpolateCubehelix = d3_interpolateCubehelix(1);
d3.interpolateCubehelix.gamma = d3_interpolateCubehelix;
function d3_interpolateCubehelix(gamma) {
return function(a, b) {
a = d3.hsl(a);
b = d3.hsl(b);
var ah = (a.h + 120) * radians,
bh = (b.h + 120) * radians - ah,
as = a.s,
bs = b.s - as,
al = a.l,
bl = b.l - al;
if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as;
if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah;
return function(t) {
var h = ah + bh * t,
l = Math.pow(al + bl * t, gamma),
a = (as + bs * t) * l * (1 - l);
return "#"
+ hex(l + a * (-0.14861 * Math.cos(h) + 1.78277 * Math.sin(h)))
+ hex(l + a * (-0.29227 * Math.cos(h) - 0.90649 * Math.sin(h)))
+ hex(l + a * (+1.97294 * Math.cos(h)));
};
};
}
function hex(v) {
var s = (v = v <= 0 ? 0 : v >= 1 ? 255 : v * 255 | 0).toString(16);
return v < 0x10 ? "0" + s : s;
}
})();
;
var c_axis, c_line, c_y, colorify, h_axis, h_line, h_y, l_axis, l_line, l_y, palette, svg, x;
colorify = d3.scale.cubehelix().domain([0, 255]);
palette = d3.range(256).map(function(i) {
return colorify(i);
});
svg = d3.select('svg');
svg.selectAll('.colored_band').data(palette).enter().append('rect').attr('class', 'colored_band').attr('x', function(d, i) {
return 32 + 3.5 * i;
}).attr('y', 21).attr('width', 3.5).attr('height', 120).attr('fill', function(d) {
return d;
});
svg.selectAll('.hue_band').data(palette).enter().append('rect').attr('class', 'hue_band').attr('x', function(d, i) {
return 32 + 3.5 * i;
}).attr('y', 166).attr('width', 3.5).attr('height', 60).attr('fill', function(d) {
var hcl;
hcl = d3.hcl(d);
hcl.c = 60;
hcl.l = 60;
return hcl;
});
svg.selectAll('.chroma_band').data(palette).enter().append('rect').attr('class', 'chroma_band').attr('x', function(d, i) {
return 32 + 3.5 * i;
}).attr('y', 237).attr('width', 3.5).attr('height', 116).attr('fill', function(d) {
var hcl;
hcl = d3.hcl(d);
hcl.l = hcl.c / 1.4;
hcl.h = 80;
hcl.c = 25;
return hcl;
});
svg.selectAll('.luminance_band').data(palette).enter().append('rect').attr('class', 'luminance_band').attr('x', function(d, i) {
return 32 + 3.5 * i;
}).attr('y', 364).attr('width', 3.5).attr('height', 116).attr('fill', function(d) {
var hcl;
hcl = d3.hcl(d);
hcl.c = 0;
return hcl;
});
x = d3.scale.linear().domain([0, 255]).range([32, 928]);
h_y = d3.scale.linear().domain([0, 360]).range([166 + 60, 166]);
c_y = d3.scale.linear().domain([0, 150]).range([237 + 116, 237]);
l_y = d3.scale.linear().domain([0, 100]).range([364 + 116, 364]);
h_line = d3.svg.line().x(function(_, i) {
return x(i);
}).y(function(d, i) {
var h;
if (i === 0) {
return h_y(0);
}
h = d3.hcl(d).h;
return h_y(h > 0 ? h : h + 360);
});
c_line = d3.svg.line().x(function(_, i) {
return x(i);
}).y(function(d, i) {
if (i === 0) {
return c_y(0);
}
return c_y(d3.hcl(d).c);
});
l_line = d3.svg.line().x(function(_, i) {
return x(i);
}).y(function(d, i) {
return l_y(d3.hcl(d).l);
});
svg.append('path').datum(palette).attr('class', 'hue').attr('d', h_line);
svg.append('path').datum(palette).attr('class', 'chroma').attr('d', c_line);
svg.append('path').datum(palette).attr('class', 'luminance').attr('d', l_line);
h_axis = d3.svg.axis().scale(h_y).tickValues([0, 360]).orient('right').tickSize(3, 5);
c_axis = d3.svg.axis().scale(c_y).ticks(4).orient('right').tickSize(3, 5);
l_axis = d3.svg.axis().scale(l_y).ticks(2).orient('right').tickSize(3, 5);
svg.append('g').attr('class', 'axis').attr('transform', 'translate(932,0)').call(h_axis);
svg.append('g').attr('class', 'axis').attr('transform', 'translate(932,0)').call(c_axis);
svg.append('g').attr('class', 'axis').attr('transform', 'translate(932,0)').call(l_axis);
}).call(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment