Skip to content

Instantly share code, notes, and snippets.

@christianp
Created April 19, 2016 11:57
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 christianp/df5bc189e04cf33abbf84fc243af3a15 to your computer and use it in GitHub Desktop.
Save christianp/df5bc189e04cf33abbf84fc243af3a15 to your computer and use it in GitHub Desktop.
function canvasApp() {
if (!canvasSupport()) {
return
}
;var canvasOne = document.getElementById("canvasOne");
var context = canvasOne.getContext("2d");
setup();
var vec1;
var vec2;
var angle;
var edge_length;
var a;
var b;
var graphics_scale;
var v_translate;
var nodes;
var neighbours;
var axis;
var turn;
var do_redraw;
var x;
var y;
var ox;
var oy;
var tick_interval;
function setup() {
vec1 = [0, 0];
vec2 = [0, 0];
angle = 0;
graphics_scale = 150;
edge_length = 0.1;
a = [edge_length, 0];
b = [edge_length * 0.5, edge_length * 0.5 * Math.sqrt(3)];
v_translate = [0, 0];
nodes = [];
neighbours = [];
setup_coords();
axis = [0., 0.];
turn = 0.;
frame();
canvasOne.addEventListener("mousedown", start_drawing, false);
}
function distance(a, b) {
var dx = a[0] - b[0];
var dy = a[1] - b[1];
return Math.sqrt(dx * dx + dy * dy);
}
function setup_coords() {
for (var i = -30; i < 30; ++i) {
for (var j = -30; j < 30; ++j) {
var vec = [a[0] * i + b[0] * j, a[1] * i + b[1] * j];
if ((i - j) % 3 != 0) {
nodes.push(vec)
}
;
}
}
;for (var i = 0; i < nodes.length; ++i) {
for (var j = i + 1; j < nodes.length; ++j) {
if (distance(nodes[i], nodes[j]) < edge_length * 1.01) {
neighbours.push([i, j])
}
}
}
;
}
function start_drawing(evt) {
var rect = canvasOne.getBoundingClientRect();
x = 2 * (evt.clientX - rect.left) / rect.width - 1;
y = 2 * (evt.clientY - rect.top) / rect.height - 1;
ox = x;
oy = y;
do_redraw = true;
vec1 = [x, y];
if (do_redraw) {
window.addEventListener("mousemove", get_coords, false);
tick_interval = setInterval(tick, 1000 / 60);
}
;canvasOne.removeEventListener("mousedown", start_drawing, false);
window.addEventListener("mouseup", stop_drawing, false);
if (evt.preventDefault) {
evt.preventDefault()
} else {
if (evt.returnValue) {
evt.returnValue = false
}
}
;return false;
}
function tick() {
if (!do_redraw) {
clearInterval(tick_interval)
}
;frame();
}
function stop_drawing(evt) {
canvasOne.addEventListener("mousedown", start_drawing, false);
window.removeEventListener("mouseup", stop_drawing, false);
if (do_redraw) {
do_redraw = false;
window.removeEventListener("mousemove", get_coords, false);
}
;
}
function length(a) {
return Math.sqrt(a[0] * a[0] + a[1] * a[1])
}
function angle_between(a, b) {
var theta = Math.asin((a[0] * b[1] - a[1] * b[0]) / length(a) / length(b));
if (a[0] * b[0] + a[1] * b[1] < 0) {
if (theta > 0) {
theta = Math.PI - theta
} else {
theta = -Math.PI - theta
}
}
;return theta;
}
function get_coords(evt) {
var rect = canvasOne.getBoundingClientRect();
x = 2 * (evt.clientX - rect.left) / rect.width - 1;
y = 2 * (evt.clientY - rect.top) / rect.height - 1;
vec1 = [ox, oy];
vec2 = [x, y];
v_translate[0] = vec2[0] - vec1[0];
v_translate[1] = vec2[1] - vec1[1];
var d;
if (length(v_translate) < 1e-6) {
d = length(vec1)
} else {
d = length(vec1) - Math.abs(v_translate[0] * vec1[0] + v_translate[1] * vec1[1]) / length(v_translate)
}
;if (d < 0.) {
d = 0.
}
;if (d > 1.) {
d = 1.
}
;angle = angle_between(vec2, vec1) * d;
v_translate[0] *= (1. - d);
v_translate[1] *= (1. - d);
for (var i = 0; i < nodes.length; ++i) {
nodes[i] = transform(nodes[i], -angle, v_translate)
}
;axis = transform(axis, -angle, v_translate);
turn -= angle;
while (length(axis) > 3. * edge_length) {
var min = 2e19;
var min_i = -1;
for (var i = 0; i < 6; ++i) {
var dotprod = axis[0] * Math.cos(turn + Math.PI / 3. * i) + axis[1] * Math.sin(turn + Math.PI / 3. * i);
if (dotprod < min) {
min = dotprod;
min_i = i;
}
;
}
;corr = [3. * edge_length * Math.cos(turn + Math.PI / 3. * min_i), 3. * edge_length * Math.sin(turn + Math.PI / 3. * min_i)];
axis = transform(axis, 0., corr);
for (var i = 0; i < nodes.length; ++i) {
nodes[i] = transform(nodes[i], 0., corr)
}
;
}
;ox = x;
oy = y;
}
function transform(vec, a, translate) {
var v_rotated = [Math.cos(a) * vec[0] - Math.sin(a) * vec[1], Math.sin(a) * vec[0] + Math.cos(a) * vec[1]];
var v_out = [v_rotated[0] + translate[0], v_rotated[1] + translate[1]];
return v_out;
}
function draw() {
var scan = new ConvexHullGrahamScan();
var projected_nodes = [];
var visible = [];
for (var i = 0; i < nodes.length; ++i) {
var vec = nodes[i].slice();
var distance_from_origin = length(vec);
visible.push(distance_from_origin > Math.PI * 0.5);
if (distance_from_origin != 0) {
vec[0] *= Math.sin(distance_from_origin) / distance_from_origin;
vec[1] *= Math.sin(distance_from_origin) / distance_from_origin;
}
;projected_nodes.push(vec);
if (!visible[i]) {
scan.addPoint(vec[0], vec[1])
}
;
}
;context.beginPath();
for (var i = 0; i < neighbours.length; ++i) {
if (visible[neighbours[i][0]] || visible[neighbours[i][1]]) {
continue
}
;var start = projected_nodes[neighbours[i][0]];
var end = projected_nodes[neighbours[i][1]];
context.moveTo(start[0] * graphics_scale + graphics_scale, start[1] * graphics_scale + graphics_scale);
context.lineTo(end[0] * graphics_scale + graphics_scale, end[1] * graphics_scale + graphics_scale);
}
;var hull = scan.getHull();
var hull_end = hull[hull.length - 1];
context.moveTo(hull_end.x * graphics_scale + graphics_scale, hull_end.y * graphics_scale + graphics_scale);
for (i = 0; i < hull.length; ++i) {
context.lineTo(hull[i].x * graphics_scale + graphics_scale, hull[i].y * graphics_scale + graphics_scale);
context.moveTo(hull[i].x * graphics_scale + graphics_scale, hull[i].y * graphics_scale + graphics_scale);
}
;context.strokeStyle = "#000066";
context.stroke();
}
function frame() {
context.fillStyle = "white";
context.fillRect(0, 0, canvasOne.width, canvasOne.height);
draw();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment