Add barycentric demo
This commit is contained in:
parent
c695f27186
commit
f5a53bdcc1
132
cg/barycentric/barycentric.js
Normal file
132
cg/barycentric/barycentric.js
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
43
cg/barycentric/index.html
Normal file
43
cg/barycentric/index.html
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>barycentric</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
font-family: Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.main {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.canvas {
|
||||||
|
flex: 1;
|
||||||
|
width: 100vw;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
font-size: 1.5em;
|
||||||
|
width: 30rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="main">
|
||||||
|
<div class="canvas"></div>
|
||||||
|
<div class="text">
|
||||||
|
<p>λ₁ = <span id="lambda1">...</span></p>
|
||||||
|
<p>λ₂ = <span id="lambda2">...</span></p>
|
||||||
|
<p>λ₃ = <span id="lambda3">...</span></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="./barycentric.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
x
Reference in New Issue
Block a user