computeSdf = (triangle, pos = { x: 0, y: 0 }) => {
const mx = d3.mean(triangle, (d) => d.x),
my = d3.mean(triangle, (d) => d.y);
let x,
y,
cx,
cy,
x0,
y0,
vx,
vy,
r2,
dot,
sdf = () => {
let distance, nearestPnt;
dot = x * vx + y * vy;
if ((x * vy - y * vx) * (cx * vy - cy * vx) > 0) {
let r = dot / r2;
distance = dist(x - r * vx, y - r * vy);
nearestPnt = { x: x0 + r * vx, y: y0 + r * vy };
return { distance, nearestPnt, outside: false };
}
if (dot < 0) {
nearestPnt = { x: x0, y: y0 };
distance = Math.sqrt(dist(x, y));
} else if (dot > r2) {
nearestPnt = { x: x0 + vx, y: y0 + vy };
distance = dist(vx - x, vy - y);
} else {
let r = dot / r2;
nearestPnt = { x: x0 + r * vx, y: y0 + r * vy };
distance = dist(x - r * vx, y - r * vy);
}
return { distance, nearestPnt, outside: true };
};
let sdfs = [];
for (let i = 0; i < triangle.length; ++i) {
x0 = triangle[(i + 1) % triangle.length].x;
y0 = triangle[(i + 1) % triangle.length].y;
vx = triangle[i].x - x0;
vy = triangle[i].y - y0;
x = pos.x - x0;
y = pos.y - y0;
cx = mx - x0;
cy = my - y0;
r2 = vx * vx + vy * vy;
sdfs.push(sdf());
}
let outsides = sdfs
.filter((d) => d.outside)
.sort((a, b) => a.distance - b.distance),
insides = sdfs
.filter((d) => !d.outsides)
.sort((a, b) => a.distance - b.distance);
if (outsides.length > 0) {
return Object.assign(pos, {
npx: outsides[0].nearestPnt.x,
npy: outsides[0].nearestPnt.y,
distance: outsides[0].distance
});
}
return Object.assign(pos, {
npx: insides[0].nearestPnt.x,
npy: insides[0].nearestPnt.y,
distance: -insides[0].distance
});
}