block by nitaku 533974b74c7c40ad67b9

Distance fields (boolean subtraction)

Full Screen

-

index.js

(function() {
  // noprotect;
  /* BEGIN
  */

  var BLUR, CX1, CX2, CY1, CY2, Fxy, MAX_D, R1, R2, a, b, canvas, color, colorize_inner, colorize_outer, ctx, dist1, dist2, g, hcl_linear_rainbow, height, image, pixel_i, pixel_x, pixel_y, r, side, value, width, x, y, _i, _j, _k, _l, _ref, _ref1, _ref2, _ref3;

  hcl_linear_rainbow = function() {
    var chroma_range, domain, hue_range, luminance_range, scale;

    domain = [0, 1];
    hue_range = [340, 340 - 480];
    chroma_range = [0, 40];
    luminance_range = [0, 100];
    scale = function(x) {
      var c, ext, h, l, xn;

      ext = domain[1] - domain[0];
      xn = (x - domain[0]) / ext;
      h = hue_range[0] + (hue_range[1] - hue_range[0]) * xn;
      c = chroma_range[0] + (chroma_range[1] - chroma_range[0]) * (1 - Math.pow(1 - 2 * xn, 2));
      l = luminance_range[0] + (luminance_range[1] - luminance_range[0]) * xn;
      h = Math.max(Math.min(h, d3.max(hue_range)), d3.min(hue_range));
      c = Math.max(Math.min(c, d3.max(chroma_range)), d3.min(chroma_range));
      l = Math.max(Math.min(l, d3.max(luminance_range)), d3.min(luminance_range));
      return d3.hcl(h, c, l);
    };
    scale.domain = function(x) {
      if (!arguments.length) {
        return domain;
      }
      domain = x;
      return scale;
    };
    scale.hue_range = function(x) {
      if (!arguments.length) {
        return hue_range;
      }
      hue_range = x;
      return scale;
    };
    scale.chroma_range = function(x) {
      if (!arguments.length) {
        return chroma_range;
      }
      chroma_range = x;
      return scale;
    };
    scale.luminance_range = function(x) {
      if (!arguments.length) {
        return luminance_range;
      }
      luminance_range = x;
      return scale;
    };
    return scale;
  };

  /* END
  */


  /* define the distance function for two circles
  */


  CX1 = 0.5;

  CY1 = 0.5;

  R1 = 0.25;

  dist1 = function(x, y) {
    return R1 - (Math.pow(x - CX1, 2) + Math.pow(y - CY1, 2)) / R1;
  };

  CX2 = 0.7;

  CY2 = 0.7;

  R2 = 0.2;

  dist2 = function(x, y) {
    return R2 - (Math.pow(x - CX2, 2) + Math.pow(y - CY2, 2)) / R2;
  };

  /* Draw the distance function
  */


  canvas = d3.select('#left');

  width = canvas.node().getBoundingClientRect().width;

  height = canvas.node().getBoundingClientRect().height;

  side = Math.min(width, height) - 20;

  ctx = canvas.node().getContext('2d');

  image = ctx.createImageData(side, side);

  /* define a default cubehelix-style hcl linear rainbow scale
  */


  MAX_D = Math.sqrt(2) / 4;

  colorize_inner = hcl_linear_rainbow().domain([MAX_D, 0]).hue_range([200, 200 + 90]);

  colorize_outer = hcl_linear_rainbow().domain([-MAX_D, 0]).hue_range([200 - 180, 200 - 180 + 90]);

  console.debug('Coloring...');

  for (pixel_x = _i = 0; 0 <= side ? _i < side : _i > side; pixel_x = 0 <= side ? ++_i : --_i) {
    for (pixel_y = _j = 0; 0 <= side ? _j < side : _j > side; pixel_y = 0 <= side ? ++_j : --_j) {
      pixel_i = (pixel_y * side + pixel_x) * 4;
      _ref = [pixel_i + 0, pixel_i + 1, pixel_i + 2, pixel_i + 3], r = _ref[0], g = _ref[1], b = _ref[2], a = _ref[3];
      _ref1 = [pixel_x / side, pixel_y / side], x = _ref1[0], y = _ref1[1];
      Fxy = Math.min(dist1(x, y), -dist2(x, y));
      if (Fxy > 0) {
        color = d3.rgb(colorize_inner(Fxy));
      } else {
        color = d3.rgb(colorize_outer(Fxy));
      }
      image.data[r] = color.r;
      image.data[g] = color.g;
      image.data[b] = color.b;
      image.data[a] = 255;
    }
  }

  ctx.putImageData(image, (width - side) / 2, (height - side) / 2);

  /* Draw the reconstructed shape
  */


  canvas = d3.select('#right');

  width = canvas.node().getBoundingClientRect().width;

  height = canvas.node().getBoundingClientRect().height;

  side = Math.min(width, height) - 20;

  ctx = canvas.node().getContext('2d');

  image = ctx.createImageData(side, side);

  BLUR = 3;

  for (pixel_x = _k = 0; 0 <= side ? _k < side : _k > side; pixel_x = 0 <= side ? ++_k : --_k) {
    for (pixel_y = _l = 0; 0 <= side ? _l < side : _l > side; pixel_y = 0 <= side ? ++_l : --_l) {
      pixel_i = (pixel_y * side + pixel_x) * 4;
      _ref2 = [pixel_i + 0, pixel_i + 1, pixel_i + 2, pixel_i + 3], r = _ref2[0], g = _ref2[1], b = _ref2[2], a = _ref2[3];
      _ref3 = [pixel_x / side, pixel_y / side], x = _ref3[0], y = _ref3[1];
      Fxy = Math.min(dist1(x, y), -dist2(x, y));
      value = Math.min(1 + Fxy / (BLUR / side), 1);
      image.data[r] = 255;
      image.data[g] = 255;
      image.data[b] = 255;
      image.data[a] = value * 255;
    }
  }

  ctx.putImageData(image, (width - side) / 2, (height - side) / 2);

}).call(this);

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="description" content="Distance functions (boolean subtraction)" />
    <title>Distance functions (boolean subtraction)</title>
    <link rel="stylesheet" href="index.css">
    <script src="//d3js.org/d3.v3.min.js"></script>
</head>
<body>
  <canvas id="left" width="480" height="500"></canvas>
  <canvas id="right" width="480" height="500"></canvas>
  
  <script src="index.js"></script>
</body>
</html>

index.coffee

`// noprotect`

### BEGIN ###
hcl_linear_rainbow = () ->
  # H and L are linear, C is quadratic
  domain = [0,1]
  hue_range = [340, 340-480]
  chroma_range = [0, 40]
  luminance_range = [0, 100]
  
  scale = (x) ->
    ext = domain[1]-domain[0]
    xn = (x-domain[0]) / ext
    h = hue_range[0] + (hue_range[1]-hue_range[0])*xn
    c = chroma_range[0] + (chroma_range[1]-chroma_range[0]) * (1 - Math.pow(1 - 2*xn, 2) )
    l = luminance_range[0] + (luminance_range[1]-luminance_range[0]) *xn
    
    # clamp
    h = Math.max( Math.min(h, d3.max(hue_range)), d3.min(hue_range) )
    c = Math.max( Math.min(c, d3.max(chroma_range)), d3.min(chroma_range) )
    l = Math.max( Math.min(l, d3.max(luminance_range)), d3.min(luminance_range) )
    
    return d3.hcl(h,c,l)
    
  scale.domain = (x) ->
    return domain if(!arguments.length)
    domain = x
    return scale
  
  scale.hue_range = (x) ->
    return hue_range if(!arguments.length)
    hue_range = x
    return scale
  
  scale.chroma_range = (x) ->
    return chroma_range if(!arguments.length)
    chroma_range = x
    return scale
  
  scale.luminance_range = (x) ->
    return luminance_range if(!arguments.length)
    luminance_range = x
    return scale
         
  return scale
### END ###

### define the distance function for two circles ###
CX1 = 0.5
CY1 = 0.5
R1 = 0.25
dist1 = (x, y) -> R1 - ( Math.pow(x-CX1, 2) + Math.pow(y-CY1, 2) )/R1

CX2 = 0.7
CY2 = 0.7
R2 = 0.2
dist2 = (x, y) -> R2 - ( Math.pow(x-CX2, 2) + Math.pow(y-CY2, 2) )/R2

### Draw the distance function ###

canvas = d3.select('#left')
width = canvas.node().getBoundingClientRect().width
height = canvas.node().getBoundingClientRect().height
side = Math.min(width, height) - 20

ctx = canvas.node().getContext('2d')
image = ctx.createImageData(side, side)

### define a default cubehelix-style hcl linear rainbow scale ###
MAX_D = Math.sqrt(2)/4
colorize_inner = hcl_linear_rainbow()
  .domain([MAX_D,0])
  .hue_range([200, 200+90])
  
colorize_outer = hcl_linear_rainbow()
  .domain([-MAX_D,0])
  .hue_range([200-180, 200-180+90])

console.debug 'Coloring...'

for pixel_x in [0...side]
  for pixel_y in [0...side]
    pixel_i = (pixel_y*side + pixel_x)*4
    [r,g,b,a] = [pixel_i+0, pixel_i+1, pixel_i+2, pixel_i+3]
    
    [x,y] = [pixel_x/side, pixel_y/side]
    Fxy = Math.min( dist1(x, y), -dist2(x, y) )
    
    if Fxy > 0
      color = d3.rgb colorize_inner(Fxy)
    else
      color = d3.rgb colorize_outer(Fxy)
    
    image.data[r] = color.r
    image.data[g] = color.g
    image.data[b] = color.b
    image.data[a] = 255
  
ctx.putImageData(image,(width-side)/2,(height-side)/2)


### Draw the reconstructed shape ###

canvas = d3.select('#right')
width = canvas.node().getBoundingClientRect().width
height = canvas.node().getBoundingClientRect().height
side = Math.min(width, height) - 20

ctx = canvas.node().getContext('2d')
image = ctx.createImageData(side, side)

BLUR = 3

for pixel_x in [0...side]
  for pixel_y in [0...side]
    pixel_i = (pixel_y*side + pixel_x)*4
    [r,g,b,a] = [pixel_i+0, pixel_i+1, pixel_i+2, pixel_i+3]
    
    [x,y] = [pixel_x/side, pixel_y/side]
    Fxy = Math.min( dist1(x, y), -dist2(x, y) )
    
    value = Math.min(1+Fxy/(BLUR/side), 1)
    
    image.data[r] = 255
    image.data[g] = 255
    image.data[b] = 255
    image.data[a] = value*255
  
ctx.putImageData(image,(width-side)/2,(height-side)/2)

index.css

html, body {
  padding: 0;
  margin: 0;
}

canvas {
  background: #222;
  position: absolute;
}
#left {
  top: 0;
  left: 0;
}
#right {
  top: 0;
  left: 480px;
}