Skip to content

fix(canvas): compensate print border in tool coordinate mapping#614

Merged
marcinz606 merged 2 commits into
mainfrom
fix/border-offset
Jul 23, 2026
Merged

fix(canvas): compensate print border in tool coordinate mapping#614
marcinz606 merged 2 commits into
mainfrom
fix/border-offset

Conversation

@marcinz606

Copy link
Copy Markdown
Owner

Problem

With finish.border_size > 0 (or paper aspect ≠ ORIGINAL) the preview canvas is padded with the print/mat border. The overlay stored the delivered content_rect but nothing used it: every tool click was normalized over the full padded frame while the pipeline expects content coords. Manual heal spots, dodge&burn lassos, mask-vertex edits and WB picks all landed offset by the border size (asymmetric with border_bottom_weight > 1). The reverse map had the same omission, so markers drew at the click point while the healed pixel landed elsewhere.

Cleared suspects: filed carrier and vignette are value-only (no geometry); rotation/fine-rot/flip/distortion/crop are already handled by uv_grid; stored config coords are raw-source-normalized, so no existing edits are affected.

Bonus bug found along the way: CPU mode padded the border-less crop/analysis preview too (main_window), misaligning the crop rect. GPU skips the layout pass there.

Fix

Single contract change in CanvasOverlay: every normalized coordinate crossing the overlay boundary is content-normalized, via one helper _content_view_rect() (short-circuits to _view_rect when there is no border).

  • forward click map, _raw_to_screen, brush radius, drag clamps, mat-click gating, IR/wash raster draw targets
  • get_pixel_rgb samples content-relative
  • densitometer gets content_rect=None (was the only compensated path — would double-compensate)
  • CPU crop/analysis preview no longer padded
  • controller tool handlers and crop/analysis mapping untouched

Verification

  • TDD: 9 new tests in tests/test_canvas_border_mapping.py; 7 failed pre-fix for the predicted reasons
  • make all green: 2437 passed, lint + type clean
  • Headless E2E (real GUI, offscreen, both engines): border 1.5 cm + bottom weight 1.6, real mouse click on a synthetic dust blob — stored stroke coords (0.3499, 0.4494) vs target (0.35, 0.45); healed-region centroid 0.5–0.7 px from the blob; the pre-fix mapping would have healed 23–25 px away. GPU and CPU: all checks pass.

Heal spots, dodge/burn lassos, mask edits and WB picks landed offset
by the border size when the finish border/paper layout padded the
preview: the overlay normalized clicks over the full padded frame and
ignored content_rect (only the densitometer compensated).

- overlay: _content_view_rect(); all coords crossing the overlay
  boundary are now content-normalized (forward map, _raw_to_screen,
  brush radius, drag clamps, IR/wash raster targets); mat clicks drop
- widget: get_pixel_rgb samples content-relative
- controller: densitometer no longer double-compensates
- main_window: CPU path no longer pads the border-less crop/analysis
  preview (parity with GPU skipping the layout pass there)
@marcinz606
marcinz606 merged commit 07dd965 into main Jul 23, 2026
1 check passed
@marcinz606
marcinz606 deleted the fix/border-offset branch July 23, 2026 17:40
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.

1 participant