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}