code wiki / (root) / nx_csg_scene_gate.nx

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}