133 lines
3.6 KiB
JavaScript
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);
|
|
}
|
|
});
|
|
})();
|