block by nitaku 3f02ab54b366477d8f30

HCL decomposition: cubehelix

Full Screen

Cubehelix

index.js

/* 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);

index.html

<!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="//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>

index.coffee

### 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)

index.css

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;
}