|
(function() { |
|
// noprotect; |
|
/* BEGIN |
|
*/ |
|
|
|
var CX, CY, R, canvas_left, canvas_right, cx_tool, cy_tool, df, dist, dist_tool, edit, hcl_linear_rainbow, height, pixel_x, pixel_y, r_tool, redraw, side, svg, tool, width; |
|
|
|
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 |
|
*/ |
|
|
|
|
|
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; |
|
|
|
/* 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; |
|
|
|
d3.select('body').classed('wait', true); |
|
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 === '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() { |
|
redraw(); |
|
return d3.select('body').classed('wait', false); |
|
}); |
|
}; |
|
|
|
redraw = function() { |
|
/* Draw the distance function |
|
*/ |
|
|
|
var BLUR, Fxy, MAX_D, a, b, color, colorize_inner, colorize_outer, ctx, g, image, pixel_i, r, value, _i, _j, _k, _l, _ref, _ref1; |
|
|
|
ctx = canvas_left.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]; |
|
Fxy = df[pixel_x][pixel_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 |
|
*/ |
|
|
|
ctx = canvas_right.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; |
|
_ref1 = [pixel_i + 0, pixel_i + 1, pixel_i + 2, pixel_i + 3], r = _ref1[0], g = _ref1[1], b = _ref1[2], a = _ref1[3]; |
|
Fxy = df[pixel_x][pixel_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; |
|
} |
|
} |
|
return ctx.putImageData(image, (width - side) / 2, (height - side) / 2); |
|
}; |
|
|
|
redraw(); |
|
|
|
}).call(this); |