code wiki / (root) / nx_csg_test.nx

nx_csg_test.nx source

↩ module page · 123 lines · 5824 B

1// nx_csg_test.nx -- KAT gate for SDF + marching-tetrahedra CSG booleans. 2// Proves: (a) the difference actually REMOVES material (fewer inside samples than 3// the box alone), (b) the result is a valid mesh with the box's extent, (c) it 4// slices WATERTIGHT and a mid cut shows >=2 closed loops (outer wall + the hole = 5// the topological signature of a through-hole), (d) the CSG solid prints (real 6// G-code). This is the CSG-booleans rung moving from BEHIND toward parity. 7// expect_exit: 0. 8 9import "nx_syscalls.nx" 10import "nx_mesh.nx" 11import "nx_mesh_edit.nx" 12import "nx_sdf.nx" 13import "nx_csg.nx" 14import "nx_bvh.nx" 15import "nx_slice_plane.nx" 16import "nx_slice_contour.nx" 17import "nx_stl_write.nx" 18import "nx_stl.nx" 19import "nx_machine_graph.nx" 20import "nx_material_profile.nx" 21import "nx_gcode_emit.nx" 22import "nx_slice_pipeline.nx" 23 24func w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 25func wn(v: i64) -> i64 { 26 let b: *u8 = sys_mmap(28); var m: i64 = v 27 if m < 0 { m = 0 - m; sys_write(1, "-" as *u8, 1) } 28 let t: *u8 = sys_mmap(28); var k: i64 = 0 29 if m == 0 { t[0] = 48 as u8; k = 1 } 30 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 31 var i: i64 = 0; while i < k { b[i] = t[k - 1 - i]; i = i + 1 } 32 sys_write(1, b, k); return 0 33} 34func smoke_count(buf: *u8, len: i64, needle: *u8) -> i64 { 35 var nlen: i64 = 0; while needle[nlen] != 0 { nlen = nlen + 1 } 36 if len < nlen { return 0 } 37 var count: i64 = 0; var i: i64 = 0; let last: i64 = len - nlen 38 while i <= last { 39 var j: i64 = 0; var matched: i64 = 1 40 while j < nlen { if buf[i + j] != needle[j] { matched = 0; j = nlen } j = j + 1 } 41 if matched == 1 { count = count + 1 } 42 i = i + 1 43 } 44 return count 45} 46func chk(cond: i64, pass: *i64, fail: *i64, label: *u8) -> i64 { 47 if cond == 1 { pass[0] = pass[0] + 1; w(" ok " as *u8); w(label); w("\n" as *u8) } 48 else { fail[0] = fail[0] + 1; w(" XX " as *u8); w(label); w("\n" as *u8) } 49 return 0 50} 51func eqi(a: i64, b: i64) -> i64 { if a == b { return 1 } return 0 } 52 53func main() -> i64 { 54 let pass: *i64 = (sys_mmap(8)) as *i64 55 let fail: *i64 = (sys_mmap(8)) as *i64 56 pass[0] = 0 57 fail[0] = 0 58 let bb: *i64 = (sys_mmap(48)) as *i64 59 let H: i64 = 163840 // 10mm box half-extent (20mm cube) 60 let R: i64 = 81920 // 5mm hole radius 61 w("=== nx_csg KAT (SDF + marching-tetrahedra booleans) ===\n" as *u8) 62 63 // --- (a) the difference REMOVES material: sample-count box vs box-minus-cyl --- 64 let boxp: *NxSdfPrim = nx_sdf_make(NX_SDF_BOX, 0, 0, 0, H, H, H) 65 let cylp: *NxSdfPrim = nx_sdf_make(NX_SDF_CYL, 0, 0, 0, R, H + 16384, 0) 66 var nbox: i64 = 0 67 var ndiff: i64 = 0 68 var gz: i64 = 0 - H 69 while gz <= H { 70 var gy: i64 = 0 - H 71 while gy <= H { 72 var gx: i64 = 0 - H 73 while gx <= H { 74 if nx_sdf_eval(boxp, gx, gy, gz) < 0 { nbox = nbox + 1 } 75 if nx_csg_field(boxp, cylp, NX_CSG_DIFF, gx, gy, gz) < 0 { ndiff = ndiff + 1 } 76 gx = gx + 16384 77 } 78 gy = gy + 16384 79 } 80 gz = gz + 16384 81 } 82 w(" inside samples: box=" as *u8); wn(nbox); w(" box-cyl=" as *u8); wn(ndiff); w("\n" as *u8) 83 chk(ndiff < nbox, pass, fail, "difference REMOVED material (box-cyl < box)" as *u8) 84 chk(ndiff > 0, pass, fail, "solid is non-empty after subtraction" as *u8) 85 86 // --- (b) build the CSG mesh --- 87 let m: *NxMesh = nx_csg_box_minus_cyl(H, H, H, R, 24) 88 w(" CSG mesh tris=" as *u8); wn(m.n_tris); w(" verts=" as *u8); wn(m.n_verts); w("\n" as *u8) 89 chk(m.n_tris > 100, pass, fail, "CSG produced a substantial mesh" as *u8) 90 chk(eqi(nx_mesh_validate(m), NX_MESH_OK), pass, fail, "CSG mesh valid (indices in range)" as *u8) 91 nx_mesh_aabb(m, bb) 92 w(" AABB x[" as *u8); wn(bb[0]); w("," as *u8); wn(bb[3]); w("]\n" as *u8) 93 chk(bb[3] >= 160000, pass, fail, "CSG mesh keeps box extent (max_x ~ +10mm)" as *u8) 94 chk(bb[3] <= 167000, pass, fail, "CSG mesh not over-grown past box face" as *u8) 95 96 // --- (c) watertight + the hole is real (mid slice has >=2 closed loops) --- 97 nx_mesh_translate(m, 0, 0, H) // sit on bed (z 0..2H) 98 let bvh: *NxBvh = nx_bvh_build(m) 99 let soup: *NxSliceSoup = nx_slice_plane(m, bvh, H + 7000) // mid height, off the grid planes 100 let c: *NxSliceContours = nx_slice_contour_build(soup) 101 w(" mid slice: polys=" as *u8); wn(c.n_polys); w(" open=" as *u8); wn(c.n_open); w("\n" as *u8) 102 chk(eqi(c.n_open, 0), pass, fail, "CSG solid slices WATERTIGHT (n_open=0)" as *u8) 103 chk(c.n_polys >= 2, pass, fail, "through-hole present: mid slice has >=2 closed loops" as *u8) 104 105 // --- (d) the CSG solid PRINTS (real G-code) --- 106 let stl: *u8 = sys_mmap(1048576) 107 let n: i64 = nx_stl_write_mesh(stl, m) 108 let r: *NxStlResult = nx_stl_load_binary(stl, n) 109 chk(eqi(r.verdict, NX_STL_OK), pass, fail, "CSG STL round-trips through reader" as *u8) 110 let qidi: *NxMachineGraph = nx_machine_graph_qidi_xmax3() 111 let pla: *NxMaterialProfile = nx_material_profile_generic_pla() 112 let e: *NxGcodeEmitter = nx_gemit_new(qidi, pla, 3277, 6554, 4194304) 113 nx_gemit_preamble(e) 114 let nl: i64 = nx_slice_pipe_run_v2(e, r.mesh, 20, 3277) 115 let g1: i64 = smoke_count(e.buf, e.len, "G1 X" as *u8) 116 w(" CSG->print: layers=" as *u8); wn(nl); w(" G1=" as *u8); wn(g1); w(" bytes=" as *u8); wn(e.len); w("\n" as *u8) 117 chk(nl >= 50, pass, fail, "CSG solid sliced >= 50 layers" as *u8) 118 chk(g1 >= 1000, pass, fail, "CSG solid: many real extrusion moves" as *u8) 119 120 w("=== VERDICT pass=" as *u8); wn(pass[0]); w(" fail=" as *u8); wn(fail[0]); w(" ===\n" as *u8) 121 if fail[0] == 0 { return 0 } 122 return 1 123}