Skip to content

fix: keep dimension labels out from under the rotate handles - #99

Open
henmedia wants to merge 1 commit into
Formsmith746:mainfrom
henmedia:rotate-handle-dimension-label-clearance
Open

fix: keep dimension labels out from under the rotate handles#99
henmedia wants to merge 1 commit into
Formsmith746:mainfrom
henmedia:rotate-handle-dimension-label-clearance

Conversation

@henmedia

@henmedia henmedia commented Sep 11, 2026

Copy link
Copy Markdown

Summary

  • Keep dimension marks a minimum screen distance from the edge they measure, so a width or depth label is never hidden under a rotate handle.

Clicking a dimension label often started a rotation instead of opening the input.

Before

Screen-0

After

Screen-1

What was happening

Measured in the editor at a 1280x800 viewport with a 20mm box:

Rotate handle hit area 52x52px around a 26px glyph
Dimension label 51x26px, projected 37.6px outside the edge
Bottom rotate handle 26px outside the silhouette, so inside the label ring
Gap between their centres 11.6px, overlapping by 730 square px
Probe points across the visible label 4 of 5 hit the rotate handle

Only the label's left edge was clickable. Both sat at z-index: 3, and the rotate handles render after the labels, so the handle won the pointer. The scale handles in the same overlay were never affected, because .transform-handle already sits at z-index: 5, above the rotate handles.

The overlap itself is a mismatch of units. Dimension marks are placed at a fixed world offset outside the edge they measure, so their screen distance shrinks towards nothing as you zoom out, while rotate handles sit at a fixed pixel offset. The two cross over at everyday zoom levels. Raising the world offset does not fix that, it only moves the zoom level where they collide.

What changed

  • Dimension marks keep a minimum screen distance from their edge: 28px of rotate handle offset, plus 20px of hit area, plus half a label height. Rail, extension lines and label move together, so the text never tears away from its own dimension line and the extension lines simply grow, as they would on a drawing. The push is exactly zero once the projection already clears the minimum, so zoomed-in views are unchanged and nothing jumps as it crosses over.
  • Dimension labels join the scale handles at z-index: 5, so the control you can see is the control you hit.
  • The rotate handle hit area shrinks from 52px to 40px around its 26px glyph. Three quarters of the old grab area was invisible and reached well past anything the user could see.

Rotate handle positions are untouched. An earlier attempt pushed the handles outward instead, which left the two upper ones floating above the object at every zoom level; that approach is not in this branch.

The placement rule is a pure function in transformOverlayTypes, so it can be tested without a renderer.

Testing

  • npm run typecheck
  • npm run test — 271 tests in 47 files pass, 7 of them new
  • Manually tested the affected workflow

The new tests cover the projected offset from 0.5px to 320px across six outward directions: labels below the minimum are lifted to exactly the minimum, labels above it are not moved at all, the outward direction from the projection is preserved, the transition across the threshold is continuous, and degenerate input yields no push. Neutering the function makes two of the seven fail, so they do discriminate.

Manual check covered a box in the editor: dimension text now sits clear of the rotate glyphs, the dimension line and its extension lines follow the text, and the labels open the input on click.

Notes

  • No changes to storage, import, export, grouping or undo/redo.
  • The minimum applies to every dimension mark, including height and elevation on the side, not only the footprint ones. It is a single constant, DIMENSION_LABEL_MIN_SCREEN_OFFSET, if you would prefer a different distance or want it limited to the footprint marks.
  • The hit area change is the one item here with a trade-off: 40px is a smaller touch target than 52px. Happy to put it back to 52px or meet at 44px, the other two changes stand on their own.

🤖 Generated with Claude Code

Clicking a width or depth label often started a rotation instead of
opening the dimension input.

Measured in the editor at 1280x800 with a 20mm box: the bottom rotate
handle's hit area covered 730 square px of the 51x26px label, with only
11.6px between their centres. Both sat at z-index 3, and the rotate
handles render after the labels, so the handle took the pointer. Probing
five points across the visible label, four landed on the rotate handle;
only its left edge reached the label. The scale handles in the same
overlay were unaffected because .transform-handle already sits at
z-index 5, above the rotate handles.

The overlap itself comes from a mismatch of units. Dimension marks are
placed at a fixed *world* offset outside the edge they measure, so their
screen distance shrinks towards nothing as you zoom out, while rotate
handles sit at a fixed *pixel* offset. The two cross over at everyday
zoom levels. Raising the world offset cannot fix that, it only moves the
zoom level where they collide.

- Dimension marks now keep a minimum screen distance from the edge they
  measure: 28px of rotate handle offset, plus 20px of its hit area, plus
  half a label. Rail, extension lines and label move together so the
  text never tears away from its own dimension line, and the extension
  lines simply grow. Zoomed-in views are untouched, the push is exactly
  zero once the projection already clears the minimum.
- Dimension labels join the scale handles at z-index 5, so the control
  you can see is the control you hit.
- The rotate handle hit area shrinks from 52px to 40px around its 26px
  glyph. Three quarters of the old grab area was invisible and reached
  well past anything the user could see.

Rotate handle positions are untouched. The placement rule is a pure
function in transformOverlayTypes with unit tests across the zoom range;
neutering it fails two of the seven.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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