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