EmboDi / src /shared /cast-layout.mjs
xlt99's picture
Upload 180 files
970fc3e verified
Raw History Blame Contribute Delete
1.75 kB
const CAST_OFFSETS = Object.freeze([[0, 0, 0], [-0.2, 0, -1.1], [0.55, 0, -0.2], [-0.5, 0, -3.1], [0.1, 0, -1.8]].map(Object.freeze));
const rotate = (p, a) => [Math.cos(a) * p[0] + Math.sin(a) * p[2], p[1], -Math.sin(a) * p[0] + Math.cos(a) * p[2]];
const local = (p, o) => rotate(p.map((v, i) => v - o.position[i]), -o.rotation);
function floorAt(scene, p) {
return scene.objects.filter((o) => (o.role === "floor" || o.id === "ground") && o.shape === "box").filter((o) => {
const q = local(p, o);
return Math.abs(q[0]) <= o.size[0] / 2 + 1e-3 && Math.abs(q[2]) <= o.size[2] / 2 + 1e-3;
}).reduce((n, o) => Math.max(n, o.position[1] + o.size[1] / 2), -Infinity);
}
function fits(scene, p) {
return Number.isFinite(floorAt(scene, p)) && !scene.objects.some((o) => !["floor", "ceiling", "window"].includes(o.role) && o.id !== "ground" && o.shape === "box" && o.position[1] + o.size[1] / 2 > p[1] + 0.15 && o.position[1] - o.size[1] / 2 < p[1] + 1.7 && (() => {
const q = local(p, o);
return Math.abs(q[0]) < o.size[0] / 2 + 0.23 && Math.abs(q[2]) < o.size[2] / 2 + 0.23;
})());
}
function formationPositions(anchor, heading) {
return CAST_OFFSETS.map((offset) => rotate(offset, heading).map((v, i) => v + anchor[i]));
}
function facingViewer(position, viewer) {
if (![position, viewer].every((p) => Array.isArray(p) && p.length === 3 && p.every((n) => Number.isFinite(n) && Math.abs(n) <= 100))) throw new Error("Point at a valid floor");
const x = viewer[0] - position[0], z = viewer[2] - position[2];
if (Math.hypot(x, z) < 0.1) throw new Error("That point is too close. Aim at the floor ahead of you.");
return Math.atan2(x, z);
}
export {
CAST_OFFSETS,
facingViewer,
fits,
floorAt,
formationPositions
};