diff --git a/src/gcode/parser.test.ts b/src/gcode/parser.test.ts index 5712359..4d53eba 100644 --- a/src/gcode/parser.test.ts +++ b/src/gcode/parser.test.ts @@ -47,4 +47,23 @@ describe('parseGcode', () => { expect(segments).toHaveLength(1); expect(warnings).toHaveLength(0); }); + + it('does not emit segments for pure-Z plunge moves but keeps drawing XY after', () => { + const { segments } = parseGcode(`G21 G90\nG1 X5 Y0\nG1 Z-3\nG1 X10 Y0`); + expect(segments).toHaveLength(2); + expect(segments[1]!.points).toEqual([[5, 0], [10, 0]]); + }); + + it('does not emit a segment or warning for a bare motion word', () => { + const { segments, warnings } = parseGcode(`G21 G90\nG2\nG1 X5 Y0`); + expect(segments).toHaveLength(1); + expect(warnings).toHaveLength(0); + }); + + it('still flattens a full-circle arc where start == end', () => { + const { segments } = parseGcode(`G21 G90\nG0 X10 Y0\nG2 X10 Y0 I-10 J0`); + const arc = segments[1]!; + expect(arc.kind).toBe('cut'); + expect(arc.points.length).toBeGreaterThan(4); + }); }); diff --git a/src/gcode/parser.ts b/src/gcode/parser.ts index 4f98385..3eeee77 100644 --- a/src/gcode/parser.ts +++ b/src/gcode/parser.ts @@ -39,13 +39,11 @@ export function parseGcode(text: string): ParseResult { } const axis: Record = {}; - let motionThisLine: number | null = null; for (const [letter, value] of words) { switch (letter) { case 'G': if (value === 0 || value === 1 || value === 2 || value === 3) { st.motion = value; - motionThisLine = value; } else if (value === 20) st.scale = 25.4; else if (value === 21) st.scale = 1; else if (value === 90) st.absolute = true; @@ -61,8 +59,12 @@ export function parseGcode(text: string): ParseResult { } } - const hasCoord = 'X' in axis || 'Y' in axis || 'Z' in axis; - if (motionThisLine === null && !hasCoord) continue; // no movement on this line + if ('Z' in axis) st.z = axis['Z']!; // track depth even on pure-Z moves + const hasXY = 'X' in axis || 'Y' in axis; + const hasArc = 'I' in axis || 'J' in axis || 'R' in axis; + // Only XY motion (or an arc, including full circles) yields a drawable segment. + // This skips bare motion words (e.g. `G1`), pure-Z plunges, and modal/comment lines. + if (!hasXY && !hasArc) continue; const start: Vec2 = [st.x, st.y]; const target = resolveTarget(st, axis); @@ -88,7 +90,6 @@ export function parseGcode(text: string): ParseResult { } st.x = target[0]; st.y = target[1]; - if ('Z' in axis) st.z = axis['Z']!; } return { segments, warnings }; diff --git a/src/geometry/arc.test.ts b/src/geometry/arc.test.ts index 260151e..1c8bfe9 100644 --- a/src/geometry/arc.test.ts +++ b/src/geometry/arc.test.ts @@ -17,7 +17,7 @@ describe('flattenArc', () => { it('CW arc sweeps the other way (midpoint has negative y)', () => { // centre (0,0), radius 10, from (10,0) CW to (0,10) → goes the long way through y<0 const pts = flattenArc([10, 0], [0, 10], [0, 0], true, 0.1); - const mid = pts[Math.floor(pts.length / 2)]; + const mid = pts[Math.floor(pts.length / 2)]!; expect(mid[1]).toBeLessThan(0); });