2024-08-07 11:48:07 +02:00

198 lines
5.1 KiB
JavaScript

(function () {
function fillCanvas (element, draw) {
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
var windowToCanvas;
function resize () {
var vw = element.clientWidth;
var vh = element.clientHeight;
canvas.width = vw;
canvas.height = vh;
var vpMin = Math.min(vw, vh);
// cartesion coordinates [0, 100] on both x and y axes
ctx.setTransform(
vpMin / 100, 0,
0, -vpMin / 100,
(vw - vpMin) / 2, (vh - vpMin) / 2 + vh
);
windowToCanvas = ctx.getTransform().inverse();
}
new ResizeObserver(resize).observe(element);
resize();
function render () {
draw(ctx, canvas.width, canvas.height, windowToCanvas);
requestAnimationFrame(render);
}
render();
element.appendChild(canvas);
}
const Vec2 = {
add: (v1, v2) => [v1[0] + v2[0], v1[1] + v2[1]],
sub: (v1, v2) => [v1[0] - v2[0], v1[1] - v2[1]],
scale: (s, v) => [s * v[0], s * v[1]],
magnitude: (v) => Math.sqrt(v[0] * v[0] + v[1] * v[1]),
distance: (v1, v2) => Vec2.magnitude(Vec2.sub(v1, v2)),
normalize: (v) => Vec2.scale(1 / Vec2.magnitude(v), v),
transform: (m, v) => {
return [
m.a * v[0] + m.c * v[1] + m.e,
m.b * v[1] + m.d * v[1] + m.f
];
},
dot: (v1, v2) => [v1[0] * v2[0] + v1[1] * v2[1]],
triangleArea: (p1, p2, p3) => {
// Cramers rule for signed area (necessary to get negative lambdas)
var [x1, y1] = p1;
var [x2, y2] = p2;
var [x3, y3] = p3;
return (x1 * y2 - x1 * y3 - x2 * y1 + x3 * y1 + x2 * y3 - x3 * y2) / 2;
}
};
var ptr = null;
window.addEventListener("mousemove", function (e) {
ptr = [e.clientX, e.clientY];
});
function drawPath (ctx, path) {
for (var i = 1; i < path.length; i++) {
ctx.beginPath();
ctx.moveTo(path[i - 1][0], path[i - 1][1]);
ctx.lineTo(path[i][0], path[i][1]);
ctx.stroke();
}
for (var i = 0; i < path.length; i++) {
ctx.beginPath();
ctx.arc(path[i][0], path[i][1], 1, 0, Math.PI * 2);
ctx.fill();
}
}
function drawCurve (ctx, curve, u, depth) {
var pointsOnCurve = drawPolygons(curve, u, depth);
pointsOnCurve.splice(0, 0, curve[0]);
pointsOnCurve.push(curve[curve.length - 1]);
ctx.fillStyle = "blue";
ctx.strokeStyle = "blue";
ctx.lineWidth = 0.5;
drawPath(ctx, pointsOnCurve, true);
function drawPolygons (curve, u, depth) {
if (depth <= 0) {
return [];
}
ctx.fillStyle = "#555";
ctx.strokeStyle = "#555";
ctx.lineWidth = 0.25;
drawPath(ctx, curve, true);
var b = [curve];
for (var i = 1; i < curve.length; i++) {
var midpoints = [];
for (var k = 0; k < curve.length - i; k++) {
midpoints.push(Vec2.add(
Vec2.scale(u, b[i - 1][k]),
Vec2.scale(1 - u, b[i - 1][k + 1])
));
}
drawPath(ctx, midpoints);
b.push(midpoints);
}
var pointsOnCurve = [];
var c0 = b.map(function (bi) {
return bi[0];
});
pointsOnCurve = pointsOnCurve.concat(drawPolygons(c0, u, depth - 1));
pointsOnCurve.push(b[b.length - 1][0]);
var c1 = b.map(function (bi) {
return bi[bi.length - 1];
});
pointsOnCurve = pointsOnCurve.concat(drawPolygons(c1, 1 - u, depth - 1).toReversed());
return pointsOnCurve;
}
}
var selected = 0;
var curve = [
[10, 10],
[10, 90],
[90, 10],
[90, 90]
];
var depth = 5;
window.addEventListener("keydown", function (e) {
switch (e.key) {
case "j":
selected = (selected + 1) % curve.length;
break;
case "k":
selected = (curve.length + selected - 1) % curve.length;
break;
case "ArrowLeft":
curve[selected][0]--;
break;
case "ArrowRight":
curve[selected][0]++;
break;
case "ArrowUp":
curve[selected][1]++;
break;
case "ArrowDown":
curve[selected][1]--;
break;
case "h":
depth = Math.max(0, depth - 1);
break;
case "l":
depth++;
break;
case "?":
document.querySelector(".legend").classList.toggle("--hidden");
break;
}
});
fillCanvas(document.querySelector(".canvas"), function (ctx, vw, vh, w2c) {
var origin = Vec2.transform(w2c, [0, 0]);
var omega = Vec2.transform(w2c, [vw, vh]);
ctx.fillStyle = "black";
ctx.fillRect(origin[0], origin[1], omega[0] - origin[0], omega[1] - origin[1])
drawCurve(ctx, curve, 0.5 + 0.45 * Math.cos(Math.PI * 2 * performance.now() / 5000), depth);
/*
ctx.beginPath();
ctx.moveTo(curve[0][0], curve[0][1]);
ctx.bezierCurveTo(
curve[1][0], curve[1][1],
curve[2][0], curve[2][1],
curve[3][0], curve[3][1]
);
ctx.strokeStyle = "red";
ctx.lineWidth = 0.25;
ctx.stroke();
*/
ctx.beginPath();
ctx.arc(curve[selected][0], curve[selected][1], 2, 0, Math.PI * 2);
ctx.strokeStyle = "green";
ctx.lineWidth = 0.5;
ctx.stroke();
});
})();