SOURCE / PINNED RELEASE
Made of little things.
Napplet Machines V8
- Release
- a48ffab2ae1e…
- Author-recorded commit
- 2a817e4d389e…
- License
- LICENSE
- Author’s source reference
- nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-b5572362d4a
Archive hash verified: ecd5161370161eeb…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
import type { Body } from './physics.js';
export type Vector3 = [number, number, number];
const dot = (a: Vector3, b: Vector3) => a.reduce((sum, v, i) => sum + v * b[i], 0);
export function cameraAxes(azimuth: number, elevation: number) {
const right: Vector3 = [Math.cos(azimuth), Math.sin(azimuth), 0];
const up: Vector3 = [-Math.sin(azimuth) * Math.sin(elevation), Math.cos(azimuth) * Math.sin(elevation), Math.cos(elevation)];
return { right, up };
}
// Fit screen-space extents, including elevated cars and the road ahead of each
// driver. Opposing headings get independent front padding rather than cancelling.
export function sharedCamera(bodies: Body[], ratio: number, right: Vector3, up: Vector3) {
const points: Vector3[] = [];
for (const b of bodies) {
const speed = Math.hypot(b.vx, b.vy), look = Math.min(20, 7 + speed * 0.65);
const dx = speed > 2 ? b.vx / speed : Math.cos(b.angle);
const dy = speed > 2 ? b.vy / speed : Math.sin(b.angle);
points.push([b.x, b.y, b.z], [b.x, b.y, b.z + 3], [b.x + dx * look, b.y + dy * look, b.z]);
}
const xs = points.map(p => dot(p, right)), ys = points.map(p => dot(p, up));
const x = (Math.min(...xs) + Math.max(...xs)) / 2;
const y = (Math.min(...ys) + Math.max(...ys)) / 2;
const anchor: Vector3 = [0, 0, 0];
for (const b of bodies) {
anchor[0] += b.x / bodies.length; anchor[1] += b.y / bodies.length; anchor[2] += b.z / bodies.length;
}
const sx = x - dot(anchor, right), sy = y - dot(anchor, up);
const target = anchor.map((v, i) => v + right[i] * sx + up[i] * sy) as Vector3;
const requiredSpan = (center: Vector3) => Math.max(
48 / Math.min(Math.max(ratio, 1), 1.8),
...points.flatMap(p => [
(Math.abs(dot(p, right) - dot(center, right)) + 6) * 2 / (ratio * 0.82),
(Math.abs(dot(p, up) - dot(center, up)) + 6) * 2 / 0.55,
]),
);
return { target, requiredSpan };
}
