nx_gcode_arc_test.nx source
↩ module page · 103 lines · 4090 B
1// nx_gcode_arc_test.nx -- verify arc-fit detection + G2/G3 emission.
2//
3// Test polygon: 4 vertices on a unit circle at 0°/90°/180°/270°:
4// (1, 0), (0, 1), (-1, 0), (0, -1) [in Q14: 16384, 0, etc]
5// All on circle centered at (0, 0) radius 1.
6//
7// Expected with arc_fit_enabled:
8// - emitter walks polygon
9// - prev_x=16384, prev_y=0
10// - tries arc through (16384,0), (0,16384), (-16384,0)
11// - circle center (0,0) ok
12// - extends with (0,-16384) -- on circle, fits
13// - emits single G3 (or G2) from (16384,0) to (0,-16384)
14// - closes loop back to (16384,0) with one G1
15//
16// So output should contain "G3 X" (or "G2 X") string.
17//
18// With arc_fit_enabled=0 (default), output contains only "G1" moves
19// (no "G2" or "G3").
20//
21// Closed-form invariants:
22// (a) Default disabled: emit produces NO G2/G3
23// (b) Setter enables -> setter returns 0
24// (c) Setter rejects out-of-range -> returns -1
25// (d) Enabled emit on circle-polygon -> output contains "G3 X"
26// (e) Enabled emit on square (4 collinear-ish points NOT on circle)
27// -> output still works; may or may not emit G3 (square IS a
28// degenerate 4-pt-on-circle inscribed in a circle, so could
29// arc-fit -- skip strict check for v1)
30//
31// expect_exit: 0
32// license_tier: ORIGINAL
33
34import "nx_syscalls.nx"
35import "nx_polygon.nx"
36import "nx_slice_contour.nx"
37import "nx_machine_graph.nx"
38import "nx_material_profile.nx"
39import "nx_gcode_emit.nx"
40
41const Q14: i64 = 16384
42
43func smoke_contains(buf: *u8, len: i64, needle: *u8) -> i64 {
44 var nlen: i64 = 0
45 while needle[nlen] != 0 { nlen = nlen + 1 }
46 if len < nlen { return 0 }
47 var i: i64 = 0
48 while i <= len - nlen {
49 var j: i64 = 0
50 var matched: i64 = 1
51 while j < nlen {
52 if buf[i + j] != needle[j] { matched = 0; j = nlen }
53 j = j + 1
54 }
55 if matched == 1 { return 1 }
56 i = i + 1
57 }
58 return 0
59}
60
61func main() -> i64 {
62 let qidi: *NxMachineGraph = nx_machine_graph_qidi_xmax3()
63 let pla: *NxMaterialProfile = nx_material_profile_generic_pla()
64 let lh: i64 = 3277
65 let lw: i64 = 6554
66
67 // 4-vertex polygon on unit circle
68 let circle_poly: *NxPolygon = nx_polygon_alloc(4)
69 nx_polygon_add_vert(circle_poly, Q14, 0) // (1, 0)
70 nx_polygon_add_vert(circle_poly, 0, Q14) // (0, 1)
71 nx_polygon_add_vert(circle_poly, 0 - Q14, 0) // (-1, 0)
72 nx_polygon_add_vert(circle_poly, 0, 0 - Q14) // (0, -1)
73
74 // --- (a) Default disabled: no G2/G3 in output ---
75 let e_off: *NxGcodeEmitter = nx_gemit_new(qidi, pla, lh, lw, 8192)
76 if e_off.arc_fit_enabled != 0 { return 10 }
77 nx_gemit_polygon(e_off, circle_poly, lh, 100, 200)
78 if smoke_contains(e_off.buf, e_off.len, "G3 X") == 1 { return 11 }
79 if smoke_contains(e_off.buf, e_off.len, "G2 X") == 1 { return 12 }
80 // G1 moves should be present
81 if smoke_contains(e_off.buf, e_off.len, "G1 X") != 1 { return 13 }
82
83 // --- (b)(c) Setter validation ---
84 if nx_gemit_set_arc_fitting(e_off, 1, 410) != 0 { return 20 }
85 if e_off.arc_fit_enabled != 1 { return 21 }
86 if nx_gemit_set_arc_fitting(e_off, 99, 410) != -1 { return 22 } // bad enabled
87 if nx_gemit_set_arc_fitting(e_off, 1, -1) != -1 { return 23 } // bad tol
88
89 // --- (d) Enabled emit: circle polygon produces G3 (CCW winding) ---
90 let e_on: *NxGcodeEmitter = nx_gemit_new(qidi, pla, lh, lw, 8192)
91 nx_gemit_set_arc_fitting(e_on, 1, 410)
92 nx_gemit_polygon(e_on, circle_poly, lh, 100, 200)
93 // CCW winding from (1,0) to (0,1) around (0,0) -> G3
94 if smoke_contains(e_on.buf, e_on.len, "G3 X") != 1 { return 30 }
95 // Should also contain I and J offsets
96 if smoke_contains(e_on.buf, e_on.len, " I") != 1 { return 31 }
97 if smoke_contains(e_on.buf, e_on.len, " J") != 1 { return 32 }
98 // Arc emit consolidates 3 G1 perimeter moves into 1 G3 + close G1.
99 // So enabled buffer should be SHORTER than disabled buffer for this case.
100 if e_on.len >= e_off.len { return 33 }
101
102 return 0
103}