nx_cad_test.nx source
↩ module page · 121 lines · 5963 B
1// nx_cad_test.nx -- KAT gate for parametric sketch->extrude solid modeling.
2// Closed-form invariants (vert/tri counts, validity, AABB) for box / triangular
3// prism / faceted cylinder, watertight slicing of the solids, and a full
4// CAD->print proof: a parametric cylinder -> STL -> existing QIDI slicer -> real
5// G-code. Ties the AutoCAD-class tools into the same proven fabrication pipeline
6// the DCC tools use. expect_exit: 0.
7
8import "nx_syscalls.nx"
9import "nx_mesh.nx"
10import "nx_mesh_edit.nx"
11import "nx_cad.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
51func slice_open(model: *NxMesh, plane_z: i64) -> i64 {
52 let bvh: *NxBvh = nx_bvh_build(model)
53 let soup: *NxSliceSoup = nx_slice_plane(model, bvh, plane_z)
54 let c: *NxSliceContours = nx_slice_contour_build(soup)
55 return c.n_open
56}
57
58func main() -> i64 {
59 let pass: *i64 = (sys_mmap(8)) as *i64
60 let fail: *i64 = (sys_mmap(8)) as *i64
61 pass[0] = 0
62 fail[0] = 0
63 let bb: *i64 = (sys_mmap(48)) as *i64
64 w("=== nx_cad KAT (parametric sketch->extrude solid modeling) ===\n" as *u8)
65
66 // --- BOX 20x20x20mm: rectangle sketch extruded -> 8 verts, 12 tris ---
67 let box: *NxMesh = nx_cad_make_box(327680, 327680, 327680, 0)
68 chk(eqi(box.n_verts, 8), pass, fail, "box: 2*4 = 8 verts" as *u8)
69 chk(eqi(box.n_tris, 12), pass, fail, "box: 4*4-4 = 12 tris" as *u8)
70 chk(eqi(nx_mesh_validate(box), NX_MESH_OK), pass, fail, "box valid" as *u8)
71 nx_mesh_aabb(box, bb)
72 chk(eqi(bb[2], 0), pass, fail, "box base on z=0" as *u8)
73 chk(eqi(bb[5], 327680), pass, fail, "box height = 20mm" as *u8)
74 chk(eqi(bb[3], 163840), pass, fail, "box half-width = 10mm" as *u8)
75
76 // --- TRIANGULAR PRISM: arbitrary 3-vert convex sketch -> 6 verts, 8 tris ---
77 let tx: *i64 = (sys_mmap(3 * 8)) as *i64
78 let ty: *i64 = (sys_mmap(3 * 8)) as *i64
79 tx[0] = 0; ty[0] = 0
80 tx[1] = 327680; ty[1] = 0
81 tx[2] = 0; ty[2] = 327680
82 let prism: *NxMesh = nx_cad_extrude(tx, ty, 3, 163840, 0)
83 chk(eqi(prism.n_verts, 6), pass, fail, "tri prism: 2*3 = 6 verts" as *u8)
84 chk(eqi(prism.n_tris, 8), pass, fail, "tri prism: 4*3-4 = 8 tris" as *u8)
85 chk(eqi(nx_mesh_validate(prism), NX_MESH_OK), pass, fail, "tri prism valid" as *u8)
86
87 // --- CYLINDER r=10mm h=20mm, 16 facets -> 32 verts, 60 tris ---
88 let cyl: *NxMesh = nx_cad_make_cylinder(163840, 327680, 16, 0)
89 chk(eqi(cyl.n_verts, 32), pass, fail, "cylinder(16): 2*16 = 32 verts" as *u8)
90 chk(eqi(cyl.n_tris, 60), pass, fail, "cylinder(16): 4*16-4 = 60 tris" as *u8)
91 chk(eqi(nx_mesh_validate(cyl), NX_MESH_OK), pass, fail, "cylinder valid" as *u8)
92 nx_mesh_aabb(cyl, bb)
93 w(" cylinder AABB x[" as *u8); wn(bb[0]); w("," as *u8); wn(bb[3]); w("] z[" as *u8); wn(bb[2]); w("," as *u8); wn(bb[5]); w("]\n" as *u8)
94 chk(bb[3] <= 163840, pass, fail, "cylinder radius <= 10mm (verts on/inside circle)" as *u8)
95 chk(bb[3] >= 150000, pass, fail, "cylinder radius ~10mm (facets close to circle)" as *u8)
96
97 // --- WATERTIGHT slicing of the solids ---
98 chk(eqi(slice_open(box, 163840), 0), pass, fail, "box slices CLOSED" as *u8)
99 chk(eqi(slice_open(cyl, 163840), 0), pass, fail, "cylinder slices CLOSED" as *u8)
100
101 // --- CAD -> PRINT: parametric cylinder -> STL -> QIDI slicer -> G-code ---
102 let model: *NxMesh = nx_cad_make_cylinder(163840, 327680, 32, 0) // smoother 32-facet
103 let stl: *u8 = sys_mmap(32768)
104 let n: i64 = nx_stl_write_mesh(stl, model)
105 let r: *NxStlResult = nx_stl_load_binary(stl, n)
106 chk(eqi(r.verdict, NX_STL_OK), pass, fail, "cylinder STL read OK" as *u8)
107 let qidi: *NxMachineGraph = nx_machine_graph_qidi_xmax3()
108 let pla: *NxMaterialProfile = nx_material_profile_generic_pla()
109 let e: *NxGcodeEmitter = nx_gemit_new(qidi, pla, 3277, 6554, 4194304)
110 nx_gemit_preamble(e)
111 let nl: i64 = nx_slice_pipe_run_v2(e, r.mesh, 20, 3277)
112 let g1: i64 = smoke_count(e.buf, e.len, "G1 X" as *u8)
113 w(" CAD->print cylinder: layers=" as *u8); wn(nl); w(" G1=" as *u8); wn(g1); w(" bytes=" as *u8); wn(e.len); w("\n" as *u8)
114 chk(nl >= 50, pass, fail, "cylinder sliced >= 50 layers" as *u8)
115 chk(g1 >= 1000, pass, fail, "cylinder: many real extrusion moves" as *u8)
116 chk(smoke_count(e.buf, e.len, "M84" as *u8) >= 1, pass, fail, "cylinder G-code has motors-off (complete)" as *u8)
117
118 w("=== VERDICT pass=" as *u8); wn(pass[0]); w(" fail=" as *u8); wn(fail[0]); w(" ===\n" as *u8)
119 if fail[0] == 0 { return 0 }
120 return 1
121}