Skip to content

Instantly share code, notes, and snippets.

@nitaku
Last active August 29, 2015 14:03
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/ca0496ccc76849ea8339 to your computer and use it in GitHub Desktop.
Save nitaku/ca0496ccc76849ea8339 to your computer and use it in GitHub Desktop.
Distance fields (corner case)

TBC

A circle is subtracted from an identical one. An outline is still visible because of the tolerance introduced by the BLUR parameter. If BLUR is set to zero, the shape is not rendered.

`// 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.5
CY2 = 0.5
R2 = 0.25
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)
html, body {
padding: 0;
margin: 0;
}
canvas {
background: #222;
position: absolute;
}
#left {
top: 0;
left: 0;
}
#right {
top: 0;
left: 480px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="Distance fields (corner case)" />
<title>Distance fields (corner case)</title>
<link rel="stylesheet" href="index.css">
<script src="http://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>
(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.5;
CY2 = 0.5;
R2 = 0.25;
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);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment