Skip to content

fix: support Excellon G02/G03 arc routing with I/J center offsets#298

Merged
spe-ciellt merged 1 commit intogerbv:developfrom
SourceParts:fix/excellon-g02-g03-arc-routing
Mar 2, 2026
Merged

fix: support Excellon G02/G03 arc routing with I/J center offsets#298
spe-ciellt merged 1 commit intogerbv:developfrom
SourceParts:fix/excellon-g02-g03-arc-routing

Conversation

@rampageservices
Copy link
Copy Markdown
Contributor

Summary

Add circular arc routing support to the Excellon drill parser:

  • G02 (clockwise) and G03 (counter-clockwise) arc commands with I/J center offsets are now parsed and rendered
  • Reuses the existing multi-quadrant arc algorithm (calc_cirseg_mq from gerber.c) for consistent arc geometry
  • Adds I/J offset parsing to the Excellon coordinate handler

Why

Modern CAD tools (KiCad, Altium, etc.) emit G02/G03 arc routing commands for arc-routed slots, rounded board edges, and curved milling paths. Previously these commands were silently ignored, causing incomplete rendering of routed features.

How

  • New drill_add_arc_segment() function in src/drill.c that constructs a gerbv_cirseg_t from the start point, end point, and I/J center offsets, computing arc angles via the same quadrant logic as Gerber circular interpolation
  • Extended the Excellon coordinate parser to recognize I and J offset tokens and store them in drill_state_t
  • G02/G03 commands (with tool down in route mode) now dispatch to drill_add_arc_segment() instead of being ignored

Test

New test file test/inputs/test-drill-arc-routing.exc covering:

  • CW 90° arc (G02)
  • CCW 180° arc (G03)
  • Full 360° circle (G02)

Golden reference image at test/golden/test-drill-arc-routing.png. Test registered in test/tests.list. All pre-existing tests continue to pass (95/105 pass; 10 pre-existing failures unrelated to this change).

Add circular arc routing to the Excellon drill parser. G02 (clockwise)
and G03 (counter-clockwise) arc commands with I/J center offsets are
now parsed and rendered, using the same multi-quadrant arc algorithm
as the Gerber circular interpolation code.
@rampageservices
Copy link
Copy Markdown
Contributor Author

Addresses the highest-priority item in #297 (G02/G03 arc routing).

@rampageservices
Copy link
Copy Markdown
Contributor Author

Upstream dependencies:

@spe-ciellt spe-ciellt added the enhancement New feature or request label Mar 1, 2026
@spe-ciellt spe-ciellt self-assigned this Mar 1, 2026
@spe-ciellt
Copy link
Copy Markdown
Contributor

Yes, I will probably need information in what order you have thought all your PRs should be merged? I will check all your PRs in the coming days and drop comments.

@spe-ciellt
Copy link
Copy Markdown
Contributor

Comments for future updates

1. Code duplication — calc_cirseg_mq and calc_cirseg_bbox re-implemented verbatim

Both are static in gerber.c so they can't be called from drill.c. The PR duplicates:

  • Full body of calc_cirseg_mq() (~30 lines of math + comments)
  • Most of calc_cirseg_bbox() (~25 lines) reimplemented inline in the bbox block

If a bug is ever found in calc_cirseg_mq, it needs fixing in two places. The correct structural fix is to move both functions to a shared location (e.g., gerb_image.c) and export them. That's a larger refactor — acceptable to defer, but worth a TODO comment or a follow-up issue.

2. Bounding box uses floor() — differs from calc_cirseg_bbox but is actually more correct

The PR's middle-point loop:

for (step_pi_2 = (floor(ang1 / M_PI_2) + 1) * M_PI_2; ...)

calc_cirseg_bbox in gerber.c uses:

for (step_pi_2 = (ang1/M_PI_2 + 1)*M_PI_2; ...)

For non-axis-aligned ang1 (e.g. 45°), gerber.c gives start = 1.5×(π/2) = 135° — which skips the 90° axis crossing. The PR's floor(0.5)=0 gives start = 1×(π/2) = 90° — which is correct (first axis crossing after 45°). The PR accidentally fixes a latent imprecision in the original bbox algorithm. No action needed here, but it's worth documenting.

3. delta_cp_x/y not reset on T-code tool change (drill.c:887)

case 'T':
    drill_parse_T_code(fd, state, image, file_line);
    state->route_mode = DRILL_G_DRILL;
    state->tool_down = FALSE;
    /* delta_cp_x/y NOT reset here */
    break;

In practice this is harmless — both fields are cleared after every arc segment and on tool-up, so by the time a T-code is seen they're already 0. But for defensive consistency with the route_mode/tool_down resets, adding state->delta_cp_x = state->delta_cp_y = 0; here would be better hygiene.

Great patch, will merge it and drop an issue for future reference.

@spe-ciellt spe-ciellt merged commit 94f9ae5 into gerbv:develop Mar 2, 2026
1 check passed
rampageservices added a commit to SourceParts/gerbv that referenced this pull request Mar 3, 2026
PR gerbv#298 added G02/G03 support but only for I/J center-offset arcs.
The A radius format (e.g. G02X194478Y14182A2542) is used by some CAM
tools and was completely unhandled — the parser broke out of the
coordinate loop on encountering A, ignoring the arc radius.

Parse the A parameter in drill_parse_coordinate(), store it in new
arc_radius/found_arc_radius fields on drill_state_t, and convert
radius to center offsets in drill_add_arc_segment() using chord
geometry before the existing cirseg calculation.

Fixes the rendering bug reported in gerbv#297.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants