code wiki / (root) / nx_phys3d_toi_gate.nx

nx_phys3d_toi_gate.nx source

↩ module page · 134 lines · 7586 B

1// nx_phys3d_toi_gate.nx -- PG26: CAN A FAST MOVER EVER END A TICK INSIDE A WALL? (the 2D sweep tooth lifted to 3D) 2// 3// SUBJECT: nx_phys3d.ph_toi through ph_step (in-process). The rung's done-rule in its own words: a 400-per-tick 4// mover stops AT a 4-unit wall, lifted to 3D, and a fast mover can never end a tick inside. Every tooth reads the 5// engine's own positions; the overlap oracle is ph_max_overlap. The neg-control switches CCD OFF and must TUNNEL -- 6// it proves the fixture reaches the condition and that the advancement, not the wall, is doing the stopping. 7import "nx_syscalls.nx" 8import "nx_gate_verdict.nx" 9import "nx_phys3d.nx" 10const TG_WALL_X: i64 = 20000 // fx256 (78 m) 11const TG_WALL_HX: i64 = 512 // 4-unit wall: half-thickness 2 units 12const TG_WALL_HY: i64 = 4096 13const TG_WALL_HZ: i64 = 4096 14const TG_FLOOR_Y: i64 = 0 - 512 // top face at -256, so a unit sphere at y=0 rests on it 15const TG_FLOOR_HX: i64 = 60000 16const TG_FLOOR_HY: i64 = 256 17const TG_FLOOR_HZ: i64 = 8192 18const TG_R: i64 = 256 // the mover: a unit sphere 19const TG_SPEED: i64 = 400 // pos units per tick (400 > r: a FAST mover) 20const TG_START_GAP: i64 = 200 // it starts 200 units short of the wall face: one discrete step tunnels 21const TG_TICKS: i64 = 12 22const TG_SPH_X: i64 = 30000 23const TG_SPH_R: i64 = 512 24const TG_OBL_VX: i64 = 300 25const TG_OBL_VZ: i64 = 200 26const TG_INVM: i64 = 256 27const TG_REST: i64 = 0 28const TG_FRIC: i64 = 64 29 30func tg_world(add_sphere: i64) -> i64 { 31 let base: i64 = sys_mmap(ph_bytes()) as i64 32 ph_init(base) 33 ph_add(base, 1, TG_WALL_X, 0, 0, TG_WALL_HX, TG_WALL_HY, TG_WALL_HZ, 0, 128, TG_FRIC) 34 ph_add(base, 1, 0, TG_FLOOR_Y, 0, TG_FLOOR_HX, TG_FLOOR_HY, TG_FLOOR_HZ, 0, 128, TG_FRIC) 35 if add_sphere == 1 { ph_add(base, 0, TG_SPH_X, 0, 0, TG_SPH_R, TG_SPH_R, TG_SPH_R, 0, 128, TG_FRIC) } 36 return base 37} 38func tg_face() -> i64 { return TG_WALL_X - TG_WALL_HX } 39 40func main() -> i64 { 41 let ctr: *i64 = gv_ctr() 42 gv_head("=== NX-PHYS3D-TOI gate (PG26): a fast mover advances to its time of impact and never ends a tick inside ===" as *u8) 43 // 1. the 2D tooth in 3D: 400 per tick at a 4-unit wall, starting 200 short of the face 44 let base: i64 = tg_world(0) 45 let x0: i64 = tg_face() - TG_R - TG_START_GAP 46 let m: i64 = ph_add(base, 0, x0, 0, 0, TG_R, TG_R, TG_R, TG_INVM, TG_REST, TG_FRIC) 47 ph_set_vel(base, m, TG_SPEED * 256, 0, 0) 48 gv_check("fixture-reaches-the-condition: displacement per tick exceeds the radius AND the gap (400 > 256, 400 > 200)" as *u8, ((TG_SPEED > TG_R) as i64) * ((TG_SPEED > TG_START_GAP) as i64), ctr) 49 let toi: i64 = ph_toi(base, m, TG_SPEED, 0, 0) 50 gv_check("toi-is-a-fraction-of-the-tick (0 < toi < 256) when the wall is closer than one step" as *u8, ((toi > 0) as i64) * ((toi < PH_TOI_Q) as i64), ctr) 51 ph_step(base) 52 let b: *i64 = ph_body(base, m) 53 let x1: i64 = b[1] 54 let ov1: i64 = ph_max_overlap(base, m) 55 gv_check("stops-AT-the-wall: after one tick the sphere touches the face (overlap within slop, not beyond it)" as *u8, ((ov1 <= PH_SLOP + PH_TOI_EPS) as i64) * ((x1 + TG_R >= tg_face() - PH_SLOP - PH_TOI_EPS * 2) as i64), ctr) 56 gv_check("not-beyond-the-wall: the centre stays on the near side of the face" as *u8, (x1 + TG_R <= tg_face() + PH_SLOP) as i64, ctr) 57 // 2. never inside over TG_TICKS ticks (gravity, floor, the contact solver all in play) 58 var worst: i64 = 0 - 1000000 59 var beyond: i64 = 0 60 var k: i64 = 0 61 while k < TG_TICKS { 62 ph_step(base) 63 let ov: i64 = ph_max_overlap(base, m) 64 if ov > worst { worst = ov } 65 if b[1] > TG_WALL_X { beyond = 1 } 66 k = k + 1 67 } 68 gv_check("never-inside-over-the-run: worst overlap across every tick stays within slop" as *u8, (worst <= PH_SLOP + PH_TOI_EPS) as i64, ctr) 69 gv_check("never-tunnels: the centre never crosses the wall's centre plane" as *u8, (beyond == 0) as i64, ctr) 70 // 3. neg-control: CCD off, the same fixture TUNNELS (the discrete step lands inside or beyond) 71 let nb: i64 = tg_world(0) 72 let nm: i64 = ph_add(nb, 0, x0, 0, 0, TG_R, TG_R, TG_R, TG_INVM, TG_REST, TG_FRIC) 73 ph_set_vel(nb, nm, TG_SPEED * 256, 0, 0) 74 ph_ccd_off(1) 75 ph_step(nb) 76 ph_ccd_off(0) 77 let nbody: *i64 = ph_body(nb, nm) 78 let ovn: i64 = ph_max_overlap(nb, nm) 79 gv_check("neg-control-discrete-step-EMBEDS: with CCD off the same tick ends deep inside the wall (overlap far beyond slop)" as *u8, (ovn > PH_SLOP * 8) as i64, ctr) 80 // 4. a slow mover is untouched by the sweep: its position is the plain discrete integrate 81 let sb: i64 = tg_world(0) 82 let sm: i64 = ph_add(sb, 0, 0, 0, 0, TG_R, TG_R, TG_R, TG_INVM, TG_REST, TG_FRIC) 83 let slow: i64 = TG_R / 2 84 ph_set_vel(sb, sm, slow * 256, 0, 0) 85 ph_step(sb) 86 let sbody: *i64 = ph_body(sb, sm) 87 gv_check_eq("slow-mover-takes-the-discrete-path (x advances by exactly v/256)" as *u8, sbody[1], slow, ctr) 88 // 5. sphere target: the mover stops at a static sphere's surface 89 let cb: i64 = tg_world(1) 90 let cx0: i64 = TG_SPH_X - TG_SPH_R - TG_R - TG_START_GAP 91 let cm: i64 = ph_add(cb, 0, cx0, 0, 0, TG_R, TG_R, TG_R, TG_INVM, TG_REST, TG_FRIC) 92 ph_set_vel(cb, cm, TG_SPEED * 256, 0, 0) 93 ph_step(cb) 94 let ovc: i64 = ph_max_overlap(cb, cm) 95 let cbody: *i64 = ph_body(cb, cm) 96 gv_check("sphere-target: stops at the static sphere's surface (touching, not inside)" as *u8, ((ovc <= PH_SLOP + PH_TOI_EPS) as i64) * ((cbody[1] + TG_R >= TG_SPH_X - TG_SPH_R - PH_SLOP - PH_TOI_EPS * 2) as i64), ctr) 97 // 6. oblique 3D approach: velocity with x and z components, several ticks, never inside 98 let ob: i64 = tg_world(0) 99 let om: i64 = ph_add(ob, 0, tg_face() - TG_R - TG_OBL_VX - 100, 0, 0, TG_R, TG_R, TG_R, TG_INVM, TG_REST, TG_FRIC) 100 ph_set_vel(ob, om, TG_OBL_VX * 256, 0, TG_OBL_VZ * 256) 101 var oworst: i64 = 0 - 1000000 102 k = 0 103 while k < 4 { 104 ph_step(ob) 105 let ov2: i64 = ph_max_overlap(ob, om) 106 if ov2 > oworst { oworst = ov2 } 107 k = k + 1 108 } 109 gv_check("oblique-approach-never-inside (x and z velocity, four ticks)" as *u8, (oworst <= PH_SLOP + PH_TOI_EPS) as i64, ctr) 110 // 7. determinism: the whole run twice, byte-equal position 111 let d1: i64 = tg_world(0) 112 let dm1: i64 = ph_add(d1, 0, x0, 0, 0, TG_R, TG_R, TG_R, TG_INVM, TG_REST, TG_FRIC) 113 ph_set_vel(d1, dm1, TG_SPEED * 256, 0, 0) 114 let d2: i64 = tg_world(0) 115 let dm2: i64 = ph_add(d2, 0, x0, 0, 0, TG_R, TG_R, TG_R, TG_INVM, TG_REST, TG_FRIC) 116 ph_set_vel(d2, dm2, TG_SPEED * 256, 0, 0) 117 k = 0 118 while k < TG_TICKS { ph_step(d1); ph_step(d2); k = k + 1 } 119 let e1: *i64 = ph_body(d1, dm1) 120 let e2: *i64 = ph_body(d2, dm2) 121 gv_check("determinism: two identical runs end byte-equal (x, y, z, vx)" as *u8, ((e1[1] == e2[1]) as i64) * ((e1[2] == e2[2]) as i64) * ((e1[3] == e2[3]) as i64) * ((e1[4] == e2[4]) as i64), ctr) 122 gv_values_head() 123 gv_kv("toi_q8_first_tick" as *u8, toi) 124 gv_kv("x_after_first_tick" as *u8, x1) 125 gv_kv("wall_face_x" as *u8, tg_face()) 126 gv_kv("overlap_after_first_tick" as *u8, ov1) 127 gv_kv("worst_overlap_over_run" as *u8, worst) 128 gv_kv("negcontrol_overlap_ccd_off" as *u8, ovn) 129 gv_kv("negcontrol_x_ccd_off" as *u8, nbody[1]) 130 gv_kv("sphere_target_overlap" as *u8, ovc) 131 gv_kv("oblique_worst_overlap" as *u8, oworst) 132 gv_kv("slow_mover_x" as *u8, sbody[1]) 133 return gv_verdict("NX-PHYS3D-TOI" as *u8, ctr, "a fast mover is advanced to its time of impact; the tick can end touching, never inside" as *u8) 134}