Summary
Add a two-click measurement mode: place point A, place point B, and display the direct distance with an optional right-angle triangle showing Δx and Δy decomposition.
Use case
Measuring distances between elements that are not aligned or not adjacent — e.g. "how far is this icon from that label?" Handles interrupted or diagonal measurements that the crosshair mode cannot.
Interaction
- User selects point-to-point mode (keyboard shortcut, e.g.
6).
- Click to place point A (auto-snapped via existing
snapPointToNearestEdge).
- While moving to point B, a live preview line from A to cursor is drawn, with a live distance readout.
- Click to place point B (also auto-snapped).
- The measurement annotation is displayed.
Display
- A line segment from A to B.
- Distance label on the line: e.g.
143.4 px.
- Right-angle triangle (if the line is not near-axis-aligned):
- Drawn only when
min(|Δx|, |Δy|) > threshold (e.g. 8 px), otherwise it's nearly a straight horizontal/vertical line and the triangle adds clutter.
- Shows the horizontal leg (Δx), vertical leg (Δy), and the hypotenuse (total distance).
- Each leg labeled with its length.
- Triangle drawn with dashed or lighter lines to distinguish from the main measurement line.
Implementation notes
- Two-click state machine in QML:
idle → waitingSecondPoint → placed.
- Reuse
snapPointToNearestEdge for both clicks.
- Distance calculation:
sqrt(Δx² + Δy²) in logical pixels. Use backend DPR for coordinate mapping.
- Add a new
@pyqtSlot or compute purely on the QML/JS side since it's just Pythagoras.
- Visual components:
Canvas for the line + triangle, MeasurementLabel for the text.
Quick mode vs Session mode
Summary
Add a two-click measurement mode: place point A, place point B, and display the direct distance with an optional right-angle triangle showing Δx and Δy decomposition.
Use case
Measuring distances between elements that are not aligned or not adjacent — e.g. "how far is this icon from that label?" Handles interrupted or diagonal measurements that the crosshair mode cannot.
Interaction
6).snapPointToNearestEdge).Display
143.4 px.min(|Δx|, |Δy|) > threshold(e.g. 8 px), otherwise it's nearly a straight horizontal/vertical line and the triangle adds clutter.Implementation notes
idle→waitingSecondPoint→placed.snapPointToNearestEdgefor both clicks.sqrt(Δx² + Δy²)in logical pixels. Use backend DPR for coordinate mapping.@pyqtSlotor compute purely on the QML/JS side since it's just Pythagoras.Canvasfor the line + triangle,MeasurementLabelfor the text.Quick mode vs Session mode
A(x1, y1) → B(x2, y2) — 143.4 px (Δx=140, Δy=25).