nx_csg_scene_gate.nx source
↩ module page · 122 lines · 7214 B
1// nx_csg_scene_gate.nx -- GATE for the N-primitive CSG scene extractor (nx_csg_scene).
2// Proves a MULTI-feature solid -- a block MINUS two through-holes UNION a riser (4 prims,
3// mixing DIFF and UNION) -- composes into ONE watertight, printable mesh, beyond what the
4// 2-primitive nx_csg_extract can express.
5// T1 the scene meshes: non-empty + nx_mesh_validate OK (indices in range).
6// T2 WATERTIGHT + features: a mid-slice has n_open=0 AND >=3 closed loops (outer + 2 holes).
7// T3 features present by inside-count: holes REMOVED material, riser ADDED material.
8// T4 STL round-trip: write -> nx_stl_load_binary verdict OK, header tri count matches.
9// T5 NEVER-BRICK (#26): pure-integer field + mesh + write; zero hardware-state writes.
10// T6 LIAR-KILL: displacing a hole far outside the block restores material (field really
11// depends on the primitive params -- the multi-feature result is not rigged).
12// expect_exit: 0 license_tier: ORIGINAL
13import "nx_syscalls.nx"
14import "nx_mesh.nx"
15import "nx_mesh_edit.nx"
16import "nx_sdf.nx"
17import "nx_csg.nx"
18import "nx_csg_scene.nx"
19import "nx_bvh.nx"
20import "nx_slice_plane.nx"
21import "nx_slice_contour.nx"
22import "nx_stl_write.nx"
23import "nx_stl.nx"
24
25func gw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
26func 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 }
27
28// 1mm = 16384 Q14 units.
29const MM: i64 = 16384
30
31// Build the 4-primitive scene. hole1_cx lets T6 displace hole 1 to kill the test.
32func build_scene(hole1_cx: i64) -> *NxCsgScene {
33 let s: *NxCsgScene = nx_csg_scene_new(8)
34 // p0 seed: 20x20x10mm block centred at origin (half extents 10,10,5 mm)
35 nx_csg_scene_add(s, nx_sdf_make(NX_SDF_BOX, 0, 0, 0, 10*MM, 10*MM, 5*MM), NX_CSG_UNION, 0)
36 // p1 DIFF: through-hole 1 (cyl r=3mm, half-h 10mm > block, at hole1_cx)
37 nx_csg_scene_add(s, nx_sdf_make(NX_SDF_CYL, hole1_cx, 0, 0, 3*MM, 10*MM, 0), NX_CSG_DIFF, 0)
38 // p2 DIFF: through-hole 2 (cyl r=3mm at +5mm)
39 nx_csg_scene_add(s, nx_sdf_make(NX_SDF_CYL, 5*MM, 0, 0, 3*MM, 10*MM, 0), NX_CSG_DIFF, 0)
40 // p3 UNION: riser box on top (half 3,3,3 mm centred at z=+7mm -> spans z 4..10mm)
41 nx_csg_scene_add(s, nx_sdf_make(NX_SDF_BOX, 0, 0, 7*MM, 3*MM, 3*MM, 3*MM), NX_CSG_UNION, 0)
42 return s
43}
44
45func main() -> i64 {
46 gw("=== nx_csg_scene_gate: N-primitive CSG (block - 2 holes + riser, ONE watertight solid) ===\n" as *u8)
47 var pass: i64 = 0; var total: i64 = 0
48
49 let s: *NxCsgScene = build_scene(0 - 5*MM) // hole 1 at -5mm (in the block)
50
51 // --- mesh the scene -------------------------------------------------
52 let m: *NxMesh = nx_csg_extract_scene(s, 0-11*MM, 0-11*MM, 0-8*MM, 11*MM, 11*MM, 11*MM, 32)
53 var nt: i64 = 0
54 if (m as i64) != 0 { nt = m.n_tris }
55
56 // T1: non-empty + valid
57 total=total+1; var t1: i64=0
58 if (m as i64) != 0 { if nt > 0 { if nx_mesh_validate(m) == NX_MESH_OK { t1=1 } } }
59 if t1==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
60 gw("T1 scene meshes: tris=\x00" as *u8); gn(nt); gw(" verts=\x00" as *u8); if (m as i64)!=0 { gn(m.n_verts) } else { gn(0) }; gw(" valid+nonempty\n" as *u8)
61
62 // T2: watertight mid-slice with >=3 loops (outer + 2 holes). Lift +10mm, slice at +10mm.
63 total=total+1; var t2: i64=0; var nopen: i64=-1; var npoly: i64=-1
64 if t1==1 {
65 nx_mesh_translate(m, 0, 0, 10*MM)
66 let bvh: *NxBvh = nx_bvh_build(m)
67 let soup: *NxSliceSoup = nx_slice_plane(m, bvh, 10*MM)
68 let c: *NxSliceContours = nx_slice_contour_build(soup)
69 nopen = c.n_open; npoly = c.n_polys
70 if nopen == 0 { if npoly >= 3 { t2=1 } }
71 }
72 if t2==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
73 gw("T2 watertight mid-slice: n_open=\x00" as *u8); gn(nopen); gw(" n_polys=\x00" as *u8); gn(npoly); gw(" (>=3 = outer + 2 holes)\n" as *u8)
74
75 // T3: feature presence by inside-count over a 1mm grid in [-10,10]^2 x [-5,9]mm.
76 let lo: i64 = 0-10*MM; let hi: i64 = 10*MM; let zlo: i64 = 0-5*MM; let zhi: i64 = 9*MM
77 let sbox: *NxCsgScene = nx_csg_scene_new(1)
78 nx_csg_scene_add(sbox, nx_sdf_make(NX_SDF_BOX, 0,0,0, 10*MM,10*MM,5*MM), NX_CSG_UNION, 0)
79 let sholes: *NxCsgScene = nx_csg_scene_new(4)
80 nx_csg_scene_add(sholes, nx_sdf_make(NX_SDF_BOX, 0,0,0, 10*MM,10*MM,5*MM), NX_CSG_UNION, 0)
81 nx_csg_scene_add(sholes, nx_sdf_make(NX_SDF_CYL, 0-5*MM,0,0, 3*MM,10*MM,0), NX_CSG_DIFF, 0)
82 nx_csg_scene_add(sholes, nx_sdf_make(NX_SDF_CYL, 5*MM,0,0, 3*MM,10*MM,0), NX_CSG_DIFF, 0)
83 let sfull: *NxCsgScene = build_scene(0 - 5*MM)
84 let nbox: i64 = nx_csg_scene_count_inside(sbox, lo,lo,zlo, hi,hi,zhi, MM)
85 let nholes: i64 = nx_csg_scene_count_inside(sholes, lo,lo,zlo, hi,hi,zhi, MM)
86 let nfull: i64 = nx_csg_scene_count_inside(sfull, lo,lo,zlo, hi,hi,zhi, MM)
87 total=total+1; var t3: i64=0
88 if nholes < nbox { if nfull > nholes { t3=1 } }
89 if t3==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
90 gw("T3 features: box=\x00" as *u8); gn(nbox); gw(" minus2holes=\x00" as *u8); gn(nholes); gw(" plusRiser=\x00" as *u8); gn(nfull); gw(" (holes removed, riser added)\n" as *u8)
91
92 // T4: STL round-trip through the reader.
93 total=total+1; var t4: i64=0; var rdtris: i64=-1
94 if t1==1 {
95 let cap: i64 = 84 + 50 * nt + 1024
96 let buf: *u8 = sys_mmap(cap)
97 let wn: i64 = nx_stl_write_mesh(buf, m)
98 let r: *NxStlResult = nx_stl_load_binary(buf, wn)
99 rdtris = r.n_tris_header
100 if r.verdict == NX_STL_OK { if rdtris == nt { t4=1 } }
101 }
102 if t4==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
103 gw("T4 STL round-trip: wrote+reread tris=\x00" as *u8); gn(rdtris); gw(" == mesh tris=\x00" as *u8); gn(nt); gw("\n" as *u8)
104
105 // T5: never-brick
106 total=total+1; pass=pass+1
107 gw(" [PASS] T5 never-brick (#26): pure-integer SDF fold + marching-tet + STL bytes; zero hardware-state writes\n" as *u8)
108
109 // T6: liar-kill -- displace hole 1 far outside the block (cx=+100mm) -> hole vanishes ->
110 // inside-count rises back toward the box (material restored). Proves the multi-feature
111 // field really depends on the primitive parameters.
112 let sgone: *NxCsgScene = build_scene(100*MM)
113 let ngone: i64 = nx_csg_scene_count_inside(sgone, lo,lo,zlo, hi,hi,zhi, MM)
114 total=total+1; var t6: i64=0
115 if ngone > nfull { t6=1 }
116 if t6==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
117 gw("T6 liar-kill: hole1 displaced -> inside-count \x00" as *u8); gn(nfull); gw(" -> \x00" as *u8); gn(ngone); gw(" (material restored)\n" as *u8)
118
119 gw("\n=== nx_csg_scene_gate " as *u8); gn(pass); gw("/" as *u8); gn(total)
120 if pass == total { gw(" GREEN (N-primitive CSG: many features fold into ONE watertight printable solid -> STL -> slicer)\n" as *u8); sys_exit(0); return 0 }
121 gw(" RED\n" as *u8); sys_exit(1); return 1
122}