feat: prototype perimeter patrol, 3D view, and swarm algorithms
CI & Release / Verify simulator (push) Skipped
CI & Release / Trivy scan (push) Skipped
CI & Release / Semantic Release (push) Skipped
CI & Release / Verify simulator (pull_request) Failing after 5s
CI & Release / Trivy scan (pull_request) Skipped
CI & Release / Semantic Release (pull_request) Skipped

Add optional Three.js scene, base-pad lifecycle, mycelial swarms,
UFO transit steering, and algorithm selector (boids, APF, ACO, hypha).
This commit is contained in:
2026-07-12 12:51:25 +01:00
parent ed531f9fb6
commit 1e5854ee0a
10 changed files with 4084 additions and 192 deletions
+22 -10
View File
@@ -79,16 +79,27 @@ function linkKey(a: number, b: number): string {
/** Convert live poses into the World shape the renderer already speaks. */
export function toLiveWorld(poses: LivePose[], prev: World | null, dtS: number): World {
const k = fitScale(poses);
const drones = poses.map((p, i) => ({
id: i,
name: p.id,
pos: { x: p.x * k, y: p.y * k },
vel: { x: 0, y: 0 },
heading: (p.yawDeg * Math.PI) / 180,
waypoint: 0,
battery: p.battery ?? 100,
channel: 0,
}));
const drones = poses.map((p, i) => {
const t = (prev?.t ?? 0) + dtS;
const band = 16 + (i % 6) * 4.5;
const alt =
band +
Math.sin(t * 0.62 + i * 0.91) * 4.5 +
Math.sin(p.x * 0.011 + t * 0.45) * 2;
const altVel = Math.cos(t * 0.62 + i * 0.91) * 2.8;
return {
id: i,
name: p.id,
pos: { x: p.x * k, y: p.y * k },
vel: { x: 0, y: 0 },
heading: (p.yawDeg * Math.PI) / 180,
waypoint: 0,
battery: p.battery ?? 100,
channel: 0,
alt,
altVel,
};
});
// Every in-range pair exchanges pose broadcasts; accumulate totals so the
// panel keeps its meaning between polls.
@@ -99,6 +110,7 @@ export function toLiveWorld(poses: LivePose[], prev: World | null, dtS: number):
const dist = Math.hypot(
drones[i].pos.x - drones[j].pos.x,
drones[i].pos.y - drones[j].pos.y,
(drones[i].alt ?? 0) - (drones[j].alt ?? 0),
);
if (dist > LINK_RANGE) continue;
const key = linkKey(i, j);