code wiki / (root) / nx_gcode_arc_test.nx

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}