nx_slice_pipeline_test.nx source
↩ module page · 112 lines · 4074 B
1// nx_slice_pipeline_test.nx -- INTEGRATION TEST of every P0-P3.2
2// primitive. Tetrahedron mesh -> bvh -> multi-layer slice ->
3// contour -> perimeter + infill -> full Klipper G-code.
4//
5// Tetrahedron at unit-mm scale (Q14_ONE = 16384), layer_height
6// 0.2mm = 3277 Q14. Z range 0..16384, so ~5 layers fit
7// (3277, 6554, 9831, 13108, 16385/clip).
8//
9// Each layer's slice is a smaller right triangle (top of tetrahedron
10// shrinks toward apex). The pipeline produces:
11// - preamble (M104/M140/etc)
12// - per-layer: ;LAYER:N + M106 + G0/G1 travel + G1 extrude perimeter
13// + G0/G1 infill segments
14// - postamble (M104 S0 + M84)
15//
16// Closed-form invariants:
17// (a) Pipeline returns positive layer count (>= 4 layers).
18// (b) Final emitter buffer > 1000 bytes (enough for preamble +
19// layers + postamble).
20// (c) Output contains preamble markers (M104 S215, G28, M109).
21// (d) Output contains per-layer markers (";LAYER:0", ";LAYER:1").
22// (e) Output contains perimeter print moves (G1 X with E value).
23// (f) Output contains postamble markers (M104 S0, M84).
24// (g) E_acc grew during printing (filament actually extruded).
25//
26// expect_exit: 0
27// license_tier: ORIGINAL
28
29import "nx_syscalls.nx"
30import "nx_mesh.nx"
31import "nx_mesh_print_check.nx"
32import "nx_bvh.nx"
33import "nx_machine_graph.nx"
34import "nx_material_profile.nx"
35import "nx_gcode_emit.nx"
36import "nx_slice_pipeline.nx"
37
38const Q14: i64 = 16384
39
40func smoke_contains(buf: *u8, len: i64, needle: *u8) -> i64 {
41 var nlen: i64 = 0
42 while needle[nlen] != 0 { nlen = nlen + 1 }
43 if nlen == 0 { return 1 }
44 if len < nlen { return 0 }
45 var i: i64 = 0
46 while i <= len - nlen {
47 var j: i64 = 0
48 var matched: i64 = 1
49 while j < nlen {
50 if buf[i + j] != needle[j] { matched = 0; j = nlen }
51 j = j + 1
52 }
53 if matched == 1 { return 1 }
54 i = i + 1
55 }
56 return 0
57}
58
59func main() -> i64 {
60 let mesh: *NxMesh = nx_mesh_make_tetrahedron(Q14, 0)
61 if (mesh as i64) == 0 { return 5 }
62
63 let machine: *NxMachineGraph = nx_machine_graph_qidi_xmax3()
64 let material: *NxMaterialProfile = nx_material_profile_generic_pla()
65
66 let layer_h_q14: i64 = 3277 // 0.2 mm
67 let line_w_q14: i64 = 6554 // 0.4 mm
68
69 let emitter: *NxGcodeEmitter = nx_gemit_new(machine, material,
70 layer_h_q14, line_w_q14,
71 65536)
72 if (emitter as i64) == 0 { return 10 }
73
74 // Preamble is the caller's responsibility (per primitive contract).
75 nx_gemit_preamble(emitter)
76 if emitter.verdict != NX_GEMIT_OK { return 11 }
77
78 let E_before: i64 = emitter.E_acc_q14
79
80 // Run the full slicer pipeline at 20% infill density.
81 let n_layers: i64 = nx_slice_pipe_run(emitter, mesh, 20)
82
83 // --- (a) Layer count ---
84 if n_layers < 4 { return 20 }
85
86 // --- (b) Buffer size ---
87 // Per layer for tetrahedron: ~200 bytes (3-vert perimeter + 1
88 // infill segment at this density). 4 layers + preamble + postamble
89 // = ~900-1000 bytes total. Threshold 500 is a generous floor.
90 if emitter.len < 500 { return 30 }
91
92 // --- (c) Preamble markers ---
93 if smoke_contains(emitter.buf, emitter.len, "M104 S215") != 1 { return 40 }
94 if smoke_contains(emitter.buf, emitter.len, "G28") != 1 { return 41 }
95 if smoke_contains(emitter.buf, emitter.len, "M109 S215") != 1 { return 42 }
96
97 // --- (d) Per-layer markers ---
98 if smoke_contains(emitter.buf, emitter.len, ";LAYER:0") != 1 { return 50 }
99 if smoke_contains(emitter.buf, emitter.len, ";LAYER:1") != 1 { return 51 }
100
101 // --- (e) Print moves ---
102 if smoke_contains(emitter.buf, emitter.len, "G1 X") != 1 { return 60 }
103
104 // --- (f) Postamble ---
105 if smoke_contains(emitter.buf, emitter.len, "M104 S0") != 1 { return 70 }
106 if smoke_contains(emitter.buf, emitter.len, "M84") != 1 { return 71 }
107
108 // --- (g) E accumulated ---
109 if emitter.E_acc_q14 <= E_before { return 80 }
110
111 return 0
112}