1// Isolated ribbon repro: synthetic straight + arc paths, marble dropped on2// the ribbon ONLY (no boxes, no cups). Where does collision break?3import { channelProxies } from "/Users/dami/Developer/marbles/src/gen/endless/proxies.ts";4import {5 PhysicsEngine,6 type PhysicsModule,7} from "/Users/dami/Developer/marbles/src/client/physics/engine.ts";8import type { LoopPath } from "/Users/dami/Developer/marbles/src/shared/types.ts";910const factory =11 (await import("/Users/dami/Developer/marbles/static/dist/box3d.mjs"))12 .default as () => Promise<PhysicsModule>;1314const straight: LoopPath = {15 segments: [{16 placementIndex: -1,17 curve: [{ kind: "line", a: [0, 0.5, 0], b: [12, 0.5, 0] }],18 length: 12,19 }],20 totalLength: 12,21};22const arc: LoopPath = {23 segments: [{24 placementIndex: -1,25 curve: [{26 kind: "arc",27 center: [0, 0.5, 3],28 radius: 3,29 a0: -Math.PI / 2,30 a1: 0,31 y0: 0.5,32 y1: 0.5,33 }],34 length: 4.7,35 }],36 totalLength: 4.7,37};3839for (40 const [name, path, spawn, vel] of [41 ["straight", straight, [1, 1.2, 0], [2, 0, 0]],42 ["arc", arc, [0.3, 1.2, 0.1], [0, 0, 2]],43 ] as const44) {45 const m = await factory();46 const engine = new PhysicsEngine(m);47 const proxies = channelProxies(path, true);48 const mesh = proxies.find((p) => p.kind === "mesh");49 console.log(50 `${name}: proxies=${proxies.length} meshVerts=${51 mesh && mesh.kind === "mesh" ? mesh.vertices.length / 3 : 052 } tris=${mesh && mesh.kind === "mesh" ? mesh.indices.length / 3 : 0}`,53 );54 engine.addSegment(0, proxies);55 engine.spawnMarble(56 0,57 spawn as [number, number, number],58 vel as [number, number, number],59 );60 for (let i = 0; i < 300; i++) engine.step(1 / 60);61 const st = engine.state();62 console.log(63 ` after 5s: pos=(${st.pos.map((v) => v.toFixed(2))}) — ${64 st.pos[1] > 0 ? "RESTING/ROLLING ✓" : "FELL THROUGH ✗"65 }`,66 );67}6869// Diagnose the arc: first triangle normal + ring layout.70{71 const proxies = channelProxies(arc, true);72 const mesh = proxies.find((p) => p.kind === "mesh");73 if (mesh && mesh.kind === "mesh") {74 const v = mesh.vertices;75 const t = mesh.indices;76 const p = (i: number) => [v[i * 3], v[i * 3 + 1], v[i * 3 + 2]];77 console.log("ring0 first3:", p(0), p(1), p(2));78 console.log("ring1 first3:", p(10), p(11), p(12));79 const [i0, i1, i2] = [t[0], t[1], t[2]];80 const A = p(i0), B = p(i1), C = p(i2);81 const e1 = [B[0] - A[0], B[1] - A[1], B[2] - A[2]];82 const e2 = [C[0] - A[0], C[1] - A[1], C[2] - A[2]];83 const n = [84 e1[1] * e2[2] - e1[2] * e2[1],85 e1[2] * e2[0] - e1[0] * e2[2],86 e1[0] * e2[1] - e1[1] * e2[0],87 ];88 console.log(`tri0 idx=(${i0},${i1},${i2}) normalY=${n[1].toFixed(4)}`);89 }90}