(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); } }); })();