pbrinkmeier.de/cg/barycentric/barycentric.js

133 lines
3.6 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 p1 = [30, 20];
var p2 = [10, 80];
var p3 = [80, 50];
var ptr = null;
window.addEventListener("mousemove", function (e) {
ptr = [e.clientX, e.clientY];
});
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])
ctx.lineCap = "round";
ctx.lineJoin = "round";
var inside = false;
if (ptr !== null) {
var p = Vec2.transform(w2c, ptr);
var a = Vec2.triangleArea(p1, p2, p3);
var lambda1 = Vec2.triangleArea(p, p2, p3) / a;
var lambda2 = Vec2.triangleArea(p1, p, p3) / a;
var lambda3 = Vec2.triangleArea(p1, p2, p) / a;
document.querySelector("#lambda1").innerHTML = formatLambda(lambda1);
document.querySelector("#lambda2").innerHTML = formatLambda(lambda2);
document.querySelector("#lambda3").innerHTML = formatLambda(lambda3);
if (lambda1 > 0 && lambda2 > 0 && lambda3 > 0) {
inside = true;
}
}
ctx.beginPath();
ctx.moveTo(p1[0], p1[1]);
ctx.lineTo(p2[0], p2[1]);
ctx.lineTo(p3[0], p3[1]);
ctx.closePath();
ctx.strokeStyle = inside ? "white" : "red";
ctx.stroke();
if (ptr !== null) {
ctx.beginPath();
ctx.arc(p[0], p[1], 10, 0, Math.PI * 2);
ctx.strokeStyle = "green";
ctx.stroke();
ctx.beginPath();
ctx.moveTo(p1[0], p1[1]);
ctx.lineTo(p[0], p[1]);
ctx.strokeStyle = "white";
ctx.stroke();
ctx.beginPath();
ctx.moveTo(p2[0], p2[1]);
ctx.lineTo(p[0], p[1]);
ctx.strokeStyle = "white";
ctx.stroke();
ctx.beginPath();
ctx.moveTo(p3[0], p3[1]);
ctx.lineTo(p[0], p[1]);
ctx.strokeStyle = "white";
ctx.stroke();
}
function formatLambda (lambda) {
return String(Math.floor(lambda * 100) / 100);
}
});
})();