Skip to content

Instantly share code, notes, and snippets.

@nitaku
Last active August 29, 2015 14:03
Show Gist options
  • Save nitaku/ec0171990afec2c38a4e to your computer and use it in GitHub Desktop.
Save nitaku/ec0171990afec2c38a4e to your computer and use it in GitHub Desktop.
Distance fields (edit tool - faster)

TBC

The main bottleneck was the color scale invocation.

`// noprotect`
canvas_left = d3.select('#left')
canvas_right = d3.select('#right')
width = canvas_left.node().getBoundingClientRect().width
height = canvas_left.node().getBoundingClientRect().height
side = Math.min(width, height) - 20
ctx_left = canvas_left.node().getContext('2d')
ctx_right = canvas_right.node().getContext('2d')
### define the distance function for the original circle ###
CX = 0.5
CY = 0.5
R = 0.25
dist = (x, y) -> R - ( Math.pow(x-CX, 2) + Math.pow(y-CY, 2) )/R
### compute the field ###
df = (( dist(pixel_x/side, pixel_y/side) for pixel_y in [0...side]) for pixel_x in [0...side])
### edit tool ###
svg = d3.select('svg')
cx_tool = 0
cy_tool = 0
r_tool = 0.125
dist_tool = (x, y) -> r_tool - ( Math.pow(x-cx_tool, 2) + Math.pow(y-cy_tool, 2) )/r_tool
tool = svg.append('circle')
.attr
id: 'tool'
r: r_tool*side
svg.on 'mousemove', () ->
[x,y] = d3.mouse(this)
[cx_tool, cy_tool] = [x/side, y/side]
tool
.attr
cx: x+0.5
cy: y+0.5
svg.on 'click', () ->
edit('union')
svg.on 'contextmenu', () ->
edit('sub')
# suppress context menu
d3.event.preventDefault()
# change tool size
svg.on 'mousewheel', () ->
if d3.event.wheelDelta > 0
r_tool *= 1.2
else
r_tool /= 1.2
tool
.attr
r: r_tool*side
edit = (op) ->
for pixel_x in [0...side]
for pixel_y in [0...side]
if op is 'union'
df[pixel_x][pixel_y] = Math.max( df[pixel_x][pixel_y], dist_tool(pixel_x/side, pixel_y/side) )
else if op is 'intersection'
df[pixel_x][pixel_y] = Math.min( df[pixel_x][pixel_y], dist_tool(pixel_x/side, pixel_y/side) )
else if op is 'sub'
df[pixel_x][pixel_y] = Math.min( df[pixel_x][pixel_y], -dist_tool(pixel_x/side, pixel_y/side) )
window.requestAnimationFrame () ->
redraw()
MAX_D = Math.sqrt(2)/4
BLUR = 3
redraw = () ->
### Draw the distance field... ###
image_left = ctx_left.createImageData(side, side)
### ...and the reconstructed shape ###
image_right = ctx_right.createImageData(side, side)
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]
Fxy = df[pixel_x][pixel_y]
image_left.data[r] = -Fxy/MAX_D*255
image_left.data[g] = 0
image_left.data[b] = Fxy/MAX_D*255
image_left.data[a] = 255
value = Math.min(1+Fxy/(BLUR/side), 1)
image_right.data[r] = 255
image_right.data[g] = 255
image_right.data[b] = 255
image_right.data[a] = value*255
ctx_left.putImageData(image_left,(width-side)/2,(height-side)/2)
ctx_right.putImageData(image_right,(width-side)/2,(height-side)/2)
redraw()
html, body {
padding: 0;
margin: 0;
}
body.wait {
cursor: wait;
}
canvas {
background: #222;
position: absolute;
}
#left {
top: 0;
left: 0;
}
#right {
top: 0;
left: 480px;
}
svg {
position: absolute;
top: 20px;
left: 490px;
}
#tool {
fill: none;
stroke: magenta;
stroke-width: 1.5;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="Distance fields (edit tool - faster)" />
<title>Distance fields (edit tool - faster)</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>
<svg width="460" height="460"></svg>
<script src="index.js"></script>
</body>
</html>
(function() {
// noprotect;
var BLUR, CX, CY, MAX_D, R, canvas_left, canvas_right, ctx_left, ctx_right, cx_tool, cy_tool, df, dist, dist_tool, edit, height, pixel_x, pixel_y, r_tool, redraw, side, svg, tool, width;
canvas_left = d3.select('#left');
canvas_right = d3.select('#right');
width = canvas_left.node().getBoundingClientRect().width;
height = canvas_left.node().getBoundingClientRect().height;
side = Math.min(width, height) - 20;
ctx_left = canvas_left.node().getContext('2d');
ctx_right = canvas_right.node().getContext('2d');
/* define the distance function for the original circle
*/
CX = 0.5;
CY = 0.5;
R = 0.25;
dist = function(x, y) {
return R - (Math.pow(x - CX, 2) + Math.pow(y - CY, 2)) / R;
};
/* compute the field
*/
df = (function() {
var _i, _results;
_results = [];
for (pixel_x = _i = 0; 0 <= side ? _i < side : _i > side; pixel_x = 0 <= side ? ++_i : --_i) {
_results.push((function() {
var _j, _results1;
_results1 = [];
for (pixel_y = _j = 0; 0 <= side ? _j < side : _j > side; pixel_y = 0 <= side ? ++_j : --_j) {
_results1.push(dist(pixel_x / side, pixel_y / side));
}
return _results1;
})());
}
return _results;
})();
/* edit tool
*/
svg = d3.select('svg');
cx_tool = 0;
cy_tool = 0;
r_tool = 0.125;
dist_tool = function(x, y) {
return r_tool - (Math.pow(x - cx_tool, 2) + Math.pow(y - cy_tool, 2)) / r_tool;
};
tool = svg.append('circle').attr({
id: 'tool',
r: r_tool * side
});
svg.on('mousemove', function() {
var x, y, _ref, _ref1;
_ref = d3.mouse(this), x = _ref[0], y = _ref[1];
_ref1 = [x / side, y / side], cx_tool = _ref1[0], cy_tool = _ref1[1];
return tool.attr({
cx: x + 0.5,
cy: y + 0.5
});
});
svg.on('click', function() {
return edit('union');
});
svg.on('contextmenu', function() {
edit('sub');
return d3.event.preventDefault();
});
svg.on('mousewheel', function() {
if (d3.event.wheelDelta > 0) {
r_tool *= 1.2;
} else {
r_tool /= 1.2;
}
return tool.attr({
r: r_tool * side
});
});
edit = function(op) {
var _i, _j;
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) {
if (op === 'union') {
df[pixel_x][pixel_y] = Math.max(df[pixel_x][pixel_y], dist_tool(pixel_x / side, pixel_y / side));
} else if (op === 'intersection') {
df[pixel_x][pixel_y] = Math.min(df[pixel_x][pixel_y], dist_tool(pixel_x / side, pixel_y / side));
} else if (op === 'sub') {
df[pixel_x][pixel_y] = Math.min(df[pixel_x][pixel_y], -dist_tool(pixel_x / side, pixel_y / side));
}
}
}
return window.requestAnimationFrame(function() {
return redraw();
});
};
MAX_D = Math.sqrt(2) / 4;
BLUR = 3;
redraw = function() {
/* Draw the distance field...
*/
var Fxy, a, b, g, image_left, image_right, pixel_i, r, value, _i, _j, _ref;
image_left = ctx_left.createImageData(side, side);
/* ...and the reconstructed shape
*/
image_right = ctx_right.createImageData(side, side);
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];
Fxy = df[pixel_x][pixel_y];
image_left.data[r] = -Fxy / MAX_D * 255;
image_left.data[g] = 0;
image_left.data[b] = Fxy / MAX_D * 255;
image_left.data[a] = 255;
value = Math.min(1 + Fxy / (BLUR / side), 1);
image_right.data[r] = 255;
image_right.data[g] = 255;
image_right.data[b] = 255;
image_right.data[a] = value * 255;
}
}
ctx_left.putImageData(image_left, (width - side) / 2, (height - side) / 2);
return ctx_right.putImageData(image_right, (width - side) / 2, (height - side) / 2);
};
redraw();
}).call(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment