diff --git a/cg/barycentric/barycentric.js b/cg/barycentric/barycentric.js new file mode 100644 index 0000000..fba0f18 --- /dev/null +++ b/cg/barycentric/barycentric.js @@ -0,0 +1,132 @@ +(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); + } + }); +})(); diff --git a/cg/barycentric/index.html b/cg/barycentric/index.html new file mode 100644 index 0000000..7d72d41 --- /dev/null +++ b/cg/barycentric/index.html @@ -0,0 +1,43 @@ + + + + + barycentric + + + +
+
+
+

λ₁ = ...

+

λ₂ = ...

+

λ₃ = ...

+
+
+ + +