Skip to content

fix: apply rotation when computing aperture macro bounding boxes#341

Merged
spe-ciellt merged 1 commit intogerbv:developfrom
SourceParts:fix/macro-bbox-rotation
Mar 5, 2026
Merged

fix: apply rotation when computing aperture macro bounding boxes#341
spe-ciellt merged 1 commit intogerbv:developfrom
SourceParts:fix/macro-bbox-rotation

Conversation

@rampageservices
Copy link
Copy Markdown
Contributor

Summary

The bounding box calculation for aperture macro primitives ignored rotation on most primitive types, producing bounding boxes that were too small for rotated shapes. The cairo renderer's viewport culling optimization (draw.c:1159) then skipped rendering these elements at zoom levels where they should have been visible.

GDK "fast" mode was unaffected because it does not use bounding box culling. SVG/PDF export was also affected since it uses the same cairo rendering path.

Changes

Apply gerbv_rotate_coord() to primitive coordinates before bounding box calculation, matching what the cairo draw code already does when rendering:

  • Outline (type 4): rotate each vertex before min/max
  • LINE20 (type 20): rotate start/end points
  • LINE21 (type 21): rotate center offset
  • LINE22 (type 22): rotate computed center offset
  • Polygon (type 5): rotate center offset
  • Moire (type 6): rotate center offset
  • Thermal (type 7): rotate center offset

Circle was already correct (rotation applied in original code).

Test plan

  • Clean build, zero warnings
  • Test suite: 95 passed, 10 failed (all 10 pre-existing, identical to unpatched develop)
  • Manual verification with error_test_v2.gbx from Display not all data in all zoom stages #208 — rotated aperture macros should now be visible at all zoom levels in cairo normal mode

Fixes #208.

The bounding box calculation for aperture macro primitives ignored
rotation on most primitive types, producing bounding boxes that were
too small for rotated shapes. The cairo renderer's viewport culling
optimization then skipped rendering these elements at zoom levels
where they should have been visible. GDK "fast" mode was unaffected
because it does not use bounding box culling.

Apply rotation via gerbv_rotate_coord() for:
- Outline (type 4): rotate each vertex before min/max calculation
- LINE20 (type 20): rotate start/end points
- LINE21 (type 21): rotate center offset
- LINE22 (type 22): rotate computed center offset
- Polygon (type 5): rotate center offset
- Moire (type 6): rotate center offset
- Thermal (type 7): rotate center offset

Circle was already correct (rotation applied since original code).

Fixes gerbv#208.
@spe-ciellt
Copy link
Copy Markdown
Contributor

Issues Found

1. POLYGON, MOIRE, and THERMAL: gerbv_rotate_coord on centre is incorrect (Maintenance)

For POLYGON, MOIRE, and THERMAL, draw.c places the centre with cairo_translate before applying cairo_rotate. This means the rotation parameter spins the primitive about its own centre but does not move the centre in image space.

POLYGON (draw.c lines 491–497):

cairo_translate (cairoTarget,
        ls->parameter[POLYGON_CENTER_X],
        ls->parameter[POLYGON_CENTER_Y]);
gerbv_draw_polygon (cairoTarget,
        ls->parameter[POLYGON_DIAMETER],
        ls->parameter[POLYGON_NUMBER_OF_POINTS],
        ls->parameter[POLYGON_ROTATION]);

There is no cairo_rotate preceding the cairo_translate. POLYGON_ROTATION is handled internally by gerbv_draw_polygon and only rotates the vertex angles; the centre is always at (POLYGON_CENTER_X, POLYGON_CENTER_Y) in image space regardless of POLYGON_ROTATION.

MOIRE (draw.c lines 523–527):

cairo_translate (cairoTarget,
        ls->parameter[MOIRE_CENTER_X],
        ls->parameter[MOIRE_CENTER_Y]);
cairo_rotate (cairoTarget,
        DEG2RAD(ls->parameter[MOIRE_ROTATION]));

Translate before rotate: the centre is placed at (MOIRE_CENTER_X, MOIRE_CENTER_Y) first; MOIRE_ROTATION then rotates the crosshairs and rings about that fixed point. MOIRE_ROTATION does not move the centre in image space.

THERMAL (draw.c lines 587–591):

cairo_translate (cairoTarget,
        ls->parameter[THERMAL_CENTER_X],
        ls->parameter[THERMAL_CENTER_Y]);
cairo_rotate (cairoTarget,
        DEG2RAD(ls->parameter[THERMAL_ROTATION]));

Same translate-before-rotate pattern: THERMAL_ROTATION does not move the centre.

The PR applies gerbv_rotate_coord to the centre coordinates using the rotation parameter for each of these three types:

gerbv_rotate_coord(&offsetx, &offsety,
    DEG2RAD(ls->parameter[POLYGON_ROTATION]));

This produces an incorrect centre offset when POLYGON_CENTER_X/Y, MOIRE_CENTER_X/Y, or THERMAL_CENTER_X/Y are non-zero. For example, a polygon with centre at (1, 0) and 90° rotation would have its bounding box centred at (0, 1) instead of the correct (1, 0).

Practical impact is negligible: in real Gerber files these centre offsets are almost always (0, 0), since macro designers place apertures relative to the flash origin. Rotating (0, 0) is always (0, 0). The incorrect bounding box would only appear for the rare case of a non-zero centre, and only affects viewport culling (the rendered image is always correct).

This inconsistency is the mirror-image of the PR #310 finding: that review established that gerbv_image_copy_all_nets() correctly does not call gerbv_rotate_coord for POLYGON/MOIRE/THERMAL centres, precisely because draw.c places those centres before rotating. The same logic applies here: the bounding box computation should not rotate those centres either. The three gerbv_rotate_coord calls for POLYGON, MOIRE, and THERMAL should be removed.

No direct practical effect, but by experience some CAD vendor will always start generating Gerbers to break gerbv just for the fun of it. So I save it for future reference.

@spe-ciellt spe-ciellt merged commit 06820b3 into gerbv:develop Mar 5, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Display not all data in all zoom stages

2 participants