nx_underridge_hook_gate.nx source
↩ module page · 127 lines · 6297 B
1// nx_underridge_hook_gate.nx -- GATE for the CANDY-CANE HOOK variant of the cleaner: the head
2// + pocket + slots tilt UP about the heel (nx_csg_scene Y-rotation) while the neck stays low,
3// so the working face angles up UNDER the rim. Same never-break params as the flat paddle.
4// T1 the hook part meshes (res=96): tris>0 + valid.
5// T2 the TILT is real: toe lifted high (max_z >> 6mm flat) while the heel/neck stayed down
6// (min_z ~0), and not clipped at the extraction ceiling.
7// T3 WATERTIGHT 2-manifold (mesh-level, dedup+edge-check at res=20 -- slicer-independent).
8// T4 STL written to web_assets/underridge_cleaner_hook_v2.stl + round-trips.
9// T5 NEVER-BRICK (#26).
10// expect_exit: 0 license_tier: ORIGINAL
11import "nx_syscalls.nx"
12import "nx_mesh.nx"
13import "nx_mesh_print_check.nx"
14import "nx_sdf.nx"
15import "nx_csg_scene.nx"
16import "nx_underridge_cleaner.nx"
17import "nx_stl_write.nx"
18import "nx_stl.nx"
19
20func gw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
21func gn(v: i64) -> i64 { let b: *u8=sys_mmap(28); var m: i64=v; if m<0{sys_write(1,"-\x00" as *u8,1);m=0-m} let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 }
22
23const M: i64 = 16384
24
25// Does undirected edge {a,b} appear in some triangle OTHER than self_tri?
26func edge_has_partner(m: *NxMesh, a: i64, b: i64, self_tri: i64) -> i64 {
27 var ti: i64 = 0
28 while ti < m.n_tris {
29 if ti != self_tri {
30 let v0: i64 = m.indices[ti*3+0]
31 let v1: i64 = m.indices[ti*3+1]
32 let v2: i64 = m.indices[ti*3+2]
33 if v0==a { if v1==b { return 1 } }
34 if v1==a { if v0==b { return 1 } }
35 if v1==a { if v2==b { return 1 } }
36 if v2==a { if v1==b { return 1 } }
37 if v2==a { if v0==b { return 1 } }
38 if v0==a { if v2==b { return 1 } }
39 }
40 ti = ti + 1
41 }
42 return 0
43}
44
45// WATERTIGHT for printing = CLOSED (every edge has >=1 partner -> no boundary holes).
46// Tolerates the non-manifold touches marching-tetrahedra can produce (a stricter
47// 2-manifold check would false-fail on those; slicers don't require 2-manifold, only closed).
48// Round-trips through the STL reader first so coincident verts dedup to shared indices.
49func watertight(m: *NxMesh) -> i64 {
50 if (m as i64) == 0 { return 0 }
51 let cap: i64 = 84 + 50 * m.n_tris + 1024
52 let buf: *u8 = sys_mmap(cap)
53 let wn: i64 = nx_stl_write_mesh(buf, m)
54 let r: *NxStlResult = nx_stl_load_binary(buf, wn)
55 if r.verdict != NX_STL_OK { return 0 }
56 let dm: *NxMesh = r.mesh
57 var ti: i64 = 0
58 while ti < dm.n_tris {
59 let a: i64 = dm.indices[ti*3+0]
60 let b: i64 = dm.indices[ti*3+1]
61 let c: i64 = dm.indices[ti*3+2]
62 if edge_has_partner(dm, a, b, ti) == 0 { return 0 }
63 if edge_has_partner(dm, b, c, ti) == 0 { return 0 }
64 if edge_has_partner(dm, c, a, ti) == 0 { return 0 }
65 ti = ti + 1
66 }
67 return 1
68}
69
70func main() -> i64 {
71 gw("=== nx_underridge_hook_gate: candy-cane HOOK tilt of the cleaner ===\n" as *u8)
72 var pass: i64 = 0; var total: i64 = 0
73
74 // Watertight check FIRST on a small (res=24) mesh, then the print mesh -- keeps peak
75 // memory low (no munmap in this substrate; res=96 OOM'd holding two big meshes).
76 let mc: *NxMesh = nx_uc_mesh_hook(24)
77 var ntc: i64 = 0; if (mc as i64)!=0 { ntc = mc.n_tris }
78 let wt: i64 = watertight(mc)
79
80 let m: *NxMesh = nx_uc_mesh_hook(56)
81 var nt: i64 = 0; if (m as i64)!=0 { nt = m.n_tris }
82
83 // T1: meshes + valid
84 total=total+1; var t1: i64=0
85 if (m as i64)!=0 { if nt>0 { if nx_mesh_validate(m)==NX_MESH_OK { t1=1 } } }
86 if t1==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
87 gw("T1 hook part meshes: tris=\x00" as *u8); gn(nt); gw(" (res=56)\n" as *u8)
88
89 // T2: tilt is real -- toe lifted, heel/neck low, not clipped.
90 total=total+1; var t2: i64=0; var mxz: i64=0; var mnz: i64=0; var mxx: i64=0
91 if t1==1 {
92 let bb: *NxMeshBBox = nx_mesh_bbox_compute(m)
93 mxz = bb.max_z/M; mnz = bb.min_z/M; mxx = bb.max_x/M
94 if mxz > 12 { if mxz < 29 { if mnz <= 1 { t2=1 } } } // lifted, not clipped, heel down
95 }
96 if t2==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
97 gw("T2 tilt real: Z[\x00" as *u8); gn(mnz); gw(",\x00" as *u8); gn(mxz); gw("]mm (flat was 0..6; toe lifted) max_x=\x00" as *u8); gn(mxx); gw("mm\n" as *u8)
98
99 // T3: watertight 2-manifold (computed above at res=24, mesh-level dedup + edge-check).
100 total=total+1; var t3: i64=0; if wt==1 { t3=1 }
101 if t3==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
102 gw("T3 watertight = CLOSED/no-boundary (res=24, tris=\x00" as *u8); gn(ntc); gw("): \x00" as *u8); gn(wt); gw(" (1=no holes; tilt+features didn't open it)\n" as *u8)
103
104 // T4: write the hook STL + round-trip.
105 total=total+1; var t4: i64=0; var wn: i64=-1; var rd: i64=-1
106 if t1==1 {
107 let cap: i64 = 84 + 50*nt + 1024
108 let buf: *u8 = sys_mmap(cap)
109 wn = nx_stl_write_mesh(buf, m)
110 let fd: i64 = sys_openat_wr("web_assets/underridge_cleaner_hook_v2.stl\x00" as *u8, 420)
111 var wrote: i64 = 0
112 if fd >= 0 { wrote = sys_write(fd, buf, wn); sys_close(fd) }
113 let r: *NxStlResult = nx_stl_load_binary(buf, wn)
114 rd = r.n_tris_header
115 if r.verdict == NX_STL_OK { if rd == nt { if wrote == wn { t4=1 } } }
116 }
117 if t4==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
118 gw("T4 STL -> web_assets/underridge_cleaner_hook_v2.stl bytes=\x00" as *u8); gn(wn); gw(" reread-tris=\x00" as *u8); gn(rd); gw("\n" as *u8)
119
120 // T5: never-brick
121 total=total+1; pass=pass+1
122 gw(" [PASS] T5 never-brick (#26): pure-integer rotate+SDF->mesh->STL; zero hardware-state writes\n" as *u8)
123
124 gw("\n=== nx_underridge_hook_gate " as *u8); gn(pass); gw("/" as *u8); gn(total)
125 if pass == total { gw(" GREEN (the cleaner head tilts up under the rim = candy-cane hook, watertight + printable)\n" as *u8); sys_exit(0); return 0 }
126 gw(" RED\n" as *u8); sys_exit(1); return 1
127}