code wiki / (root) / nx_csg_fillet_test.nx

nx_csg_fillet_test.nx source

↩ module page · 126 lines · 5647 B

1// nx_csg_fillet_test.nx -- KAT gate for SMOOTH (filleted) SDF booleans. Proves: 2// (a) the smooth union ADDS material at the junction vs the sharp union (the defining 3// property of a fillet), (b) more fillet radius -> more added material (monotone), 4// (c) the filleted solid is watertight + prints. Fillets are the axis where SDF CSG 5// is genuinely AHEAD of B-rep (robust, single-parameter, never fails). expect_exit: 0. 6 7import "nx_syscalls.nx" 8import "nx_mesh.nx" 9import "nx_mesh_edit.nx" 10import "nx_sdf.nx" 11import "nx_csg.nx" 12import "nx_bvh.nx" 13import "nx_slice_plane.nx" 14import "nx_slice_contour.nx" 15import "nx_stl_write.nx" 16import "nx_stl.nx" 17import "nx_machine_graph.nx" 18import "nx_material_profile.nx" 19import "nx_gcode_emit.nx" 20import "nx_slice_pipeline.nx" 21 22func w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 23func wn(v: i64) -> i64 { 24 let b: *u8 = sys_mmap(28); var m: i64 = v 25 if m < 0 { m = 0 - m; sys_write(1, "-" as *u8, 1) } 26 let t: *u8 = sys_mmap(28); var k: i64 = 0 27 if m == 0 { t[0] = 48 as u8; k = 1 } 28 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 29 var i: i64 = 0; while i < k { b[i] = t[k - 1 - i]; i = i + 1 } 30 sys_write(1, b, k); return 0 31} 32func smoke_count(buf: *u8, len: i64, needle: *u8) -> i64 { 33 var nlen: i64 = 0; while needle[nlen] != 0 { nlen = nlen + 1 } 34 if len < nlen { return 0 } 35 var count: i64 = 0; var i: i64 = 0; let last: i64 = len - nlen 36 while i <= last { 37 var j: i64 = 0; var matched: i64 = 1 38 while j < nlen { if buf[i + j] != needle[j] { matched = 0; j = nlen } j = j + 1 } 39 if matched == 1 { count = count + 1 } 40 i = i + 1 41 } 42 return count 43} 44func chk(cond: i64, pass: *i64, fail: *i64, label: *u8) -> i64 { 45 if cond == 1 { pass[0] = pass[0] + 1; w(" ok " as *u8); w(label); w("\n" as *u8) } 46 else { fail[0] = fail[0] + 1; w(" XX " as *u8); w(label); w("\n" as *u8) } 47 return 0 48} 49func eqi(a: i64, b: i64) -> i64 { if a == b { return 1 } return 0 } 50 51// count inside samples of a smooth union over a coarse grid (step 1mm) 52func count_union(sa: *NxSdfPrim, sb: *NxSdfPrim, k: i64, ext: i64) -> i64 { 53 var c: i64 = 0 54 var z: i64 = 0 - ext 55 while z <= ext { 56 var y: i64 = 0 - ext 57 while y <= ext { 58 var x: i64 = 0 - ext 59 while x <= ext { 60 if nx_csg_field_k(sa, sb, NX_CSG_UNION, k, x, y, z) < 0 { c = c + 1 } 61 x = x + 16384 62 } 63 y = y + 16384 64 } 65 z = z + 16384 66 } 67 return c 68} 69 70func main() -> i64 { 71 let pass: *i64 = (sys_mmap(8)) as *i64 72 let fail: *i64 = (sys_mmap(8)) as *i64 73 pass[0] = 0 74 fail[0] = 0 75 let bb: *i64 = (sys_mmap(48)) as *i64 76 let R: i64 = 98304 // 6mm sphere radius 77 let SEP: i64 = 81920 // centres at x = +/- 5mm 78 let K: i64 = 49152 // 3mm fillet 79 w("=== nx_csg fillet KAT (smooth SDF booleans) ===\n" as *u8) 80 81 let sa: *NxSdfPrim = nx_sdf_make(NX_SDF_SPHERE, 0 - SEP, 0, 0, R, 0, 0) 82 let sb: *NxSdfPrim = nx_sdf_make(NX_SDF_SPHERE, SEP, 0, 0, R, 0, 0) 83 let ext: i64 = SEP + R + K + 16384 84 85 // (a) + (b) fillet ADDS material, monotone in k 86 let sharp: i64 = count_union(sa, sb, 0, ext) 87 let mid: i64 = count_union(sa, sb, 32768, ext) 88 let big: i64 = count_union(sa, sb, K, ext) 89 w(" inside samples: sharp=" as *u8); wn(sharp); w(" k2mm=" as *u8); wn(mid); w(" k3mm=" as *u8); wn(big); w("\n" as *u8) 90 chk(mid > sharp, pass, fail, "fillet ADDS material at the junction (smooth > sharp)" as *u8) 91 chk(big >= mid, pass, fail, "more fillet radius -> more material (monotone)" as *u8) 92 93 // (c) the filleted solid is a valid, watertight, printable mesh 94 let m: *NxMesh = nx_csg_two_sphere_fillet(R, SEP, K, 28) 95 w(" fillet mesh tris=" as *u8); wn(m.n_tris); w("\n" as *u8) 96 chk(m.n_tris > 100, pass, fail, "filleted union meshed" as *u8) 97 chk(eqi(nx_mesh_validate(m), NX_MESH_OK), pass, fail, "fillet mesh valid" as *u8) 98 nx_mesh_aabb(m, bb) 99 nx_mesh_translate(m, 0 - bb[0], 0 - bb[1], 0 - bb[2]) // sit on bed 100 let bvh: *NxBvh = nx_bvh_build(m) 101 nx_mesh_aabb(m, bb) 102 let zmid: i64 = bb[2] + (bb[5] - bb[2]) / 2 + 3000 // mid height, off-grid 103 let soup: *NxSliceSoup = nx_slice_plane(m, bvh, zmid) 104 let c: *NxSliceContours = nx_slice_contour_build(soup) 105 w(" mid slice: polys=" as *u8); wn(c.n_polys); w(" open=" as *u8); wn(c.n_open); w("\n" as *u8) 106 chk(eqi(c.n_open, 0), pass, fail, "filleted solid slices WATERTIGHT" as *u8) 107 chk(c.n_polys >= 1, pass, fail, "filleted solid is a single connected blend (>=1 loop)" as *u8) 108 109 let stl: *u8 = sys_mmap(1048576) 110 let n: i64 = nx_stl_write_mesh(stl, m) 111 let r: *NxStlResult = nx_stl_load_binary(stl, n) 112 chk(eqi(r.verdict, NX_STL_OK), pass, fail, "fillet STL round-trips" as *u8) 113 let qidi: *NxMachineGraph = nx_machine_graph_qidi_xmax3() 114 let pla: *NxMaterialProfile = nx_material_profile_generic_pla() 115 let e: *NxGcodeEmitter = nx_gemit_new(qidi, pla, 3277, 6554, 4194304) 116 nx_gemit_preamble(e) 117 let nl: i64 = nx_slice_pipe_run_v2(e, r.mesh, 20, 3277) 118 let g1: i64 = smoke_count(e.buf, e.len, "G1 X" as *u8) 119 w(" fillet->print: layers=" as *u8); wn(nl); w(" G1=" as *u8); wn(g1); w("\n" as *u8) 120 chk(nl >= 30, pass, fail, "filleted solid sliced" as *u8) 121 chk(g1 >= 500, pass, fail, "filleted solid: real extrusion moves" as *u8) 122 123 w("=== VERDICT pass=" as *u8); wn(pass[0]); w(" fail=" as *u8); wn(fail[0]); w(" ===\n" as *u8) 124 if fail[0] == 0 { return 0 } 125 return 1 126}