Skip to content

fix(canvas): prevent coordinate drift and redundant saves on navigation#11

Merged
lstachowicz merged 1 commit into
masterfrom
fix/annotation-coordinate-drift-v2
May 7, 2026
Merged

fix(canvas): prevent coordinate drift and redundant saves on navigation#11
lstachowicz merged 1 commit into
masterfrom
fix/annotation-coordinate-drift-v2

Conversation

@lstachowicz

Copy link
Copy Markdown
Owner

Summary

  • Coordinate drift bug: polygon points were shifting by 1px per save/load cycle when navigating between images. Root cause: serializer used 3 decimal places (insufficient precision) combined with truncation (static_cast<int>) instead of rounding in the deserializer. At 4x zoom this appeared as a 4px jump per navigation cycle, stabilizing after 2-3 passes.
  • Redundant saves: AutoSaveCurrentImage was called unconditionally in LoadImageAtIndex, but the PolygonsChanged signal already triggers a synchronous save before navigation occurs — making the explicit call dead code.
  • Spurious PolygonsChanged emissions: Undo/Redo emitted the signal even when there was nothing to undo/redo; Paste emitted it even when the clipboard was empty.

Changes

  • Increase serializer precision from 3 to 6 decimal places (covers images up to ~1M px width losslessly)
  • Replace static_cast<int> truncation with std::round in deserializer
  • Guard Undo/Redo signal emission behind CanUndo/CanRedo checks
  • Guard Paste signal emission behind successful paste result check
  • Remove AutoSaveCurrentImage call from LoadImageAtIndex (dead code)

Test plan

  • Fix KeyPressCtrlZCallsUndo test (was incorrectly passing with CanUndo=false)
  • Add UndoSkippedWhenCanUndoFalse
  • Add RedoSkippedWhenCanRedoFalse
  • Add PasteEmitsPolygonsChangedOnSuccess
  • Add PasteDoesNotEmitPolygonsChangedWhenClipboardEmpty
  • Add RoundTripNonDivisibleCoordinates — verifies exact round-trip for coords that drift with 3dec+truncation (e.g. 105/640)
  • Add RoundTripStableAcrossMultipleCycles — simulates 5 navigation cycles, verifies zero drift

Polygon points were drifting by 1px per save/load cycle when navigating
between images. Two bugs combined to cause this: serializer used only 3
decimal places (insufficient for sub-pixel accuracy at common resolutions),
and the deserializer used truncation instead of rounding. At 4x zoom the
drift was visible as a 4px jump per navigation cycle, stabilizing after
2-3 passes.

Also removes redundant AutoSaveCurrentImage call from LoadImageAtIndex.
The PolygonsChanged signal already triggers a synchronous save before
navigation can occur, so the explicit call was dead code. Undo and Redo
now check CanUndo/CanRedo before emitting PolygonsChanged, and Paste no
longer emits the signal when the clipboard is empty.

- Increase serializer precision from 3 to 6 decimal places
- Replace static_cast<int> truncation with std::round in deserializer
- Guard Undo/Redo emission behind CanUndo/CanRedo checks
- Guard Paste emission behind successful paste check
- Remove AutoSaveCurrentImage from LoadImageAtIndex (dead code)
- Add RoundTripNonDivisibleCoordinates and RoundTripStableAcrossMultipleCycles tests
- Fix KeyPressCtrlZCallsUndo test and add missing Undo/Redo/Paste signal tests
@lstachowicz lstachowicz merged commit a4fc9dc into master May 7, 2026
4 checks passed
@lstachowicz lstachowicz deleted the fix/annotation-coordinate-drift-v2 branch May 7, 2026 01:42
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