Rubber-band selects a freehand drawing from empty space inside its bounding box #224

Closed
opened 2026-06-08 10:57:43 +00:00 by AhmedHanafy725 · 2 comments
Member

Summary

A rubber-band selection that covers only the empty area inside a freehand drawing's bounding box selects the drawing, even though it never crosses the ink. It should select a drawing only when the selection rectangle actually overlaps the strokes (this is the rubber-band counterpart of the click/drag fix in #222).

Steps to reproduce

  1. Draw a freehand stroke that leaves empty space inside its bounding box (a curve, or a few separate marks).
  2. In selection mode, drag a rubber-band rectangle over a blank pocket inside that bounding box (not over any ink).
  3. Observed: the drawing gets selected.
  4. Expected: nothing is selected, because the rectangle did not touch the ink.

Root cause

crates/hero_whiteboard_admin/static/web/js/whiteboard/tools.js, onMouseUp (selection branch, ~line 845): the rubber-band selects every .object whose bounding box (shape.getClientRect()) intersects the selection box. For a freehand drawing the bounding box includes the empty space between strokes, so a rectangle over a blank pocket still matches.

Proposed fix

Keep the cheap bounding-box intersection as a pre-filter, but for drawings add a precise test: only select the drawing when the selection rectangle (in world coordinates) actually intersects one of the ink segments. Mirror the world-coordinate segment iteration already used by _drawingHitByCircle, using a segment-vs-AABB (Liang-Barsky) test against the selection rectangle.

Acceptance criteria

  • A rubber-band over only empty space inside a drawing's bbox does not select the drawing.
  • A rubber-band that crosses or contains the ink still selects the drawing.
  • Selection of all other object types (sticky, shape, text, frame, kanban, mindmap, comment markers, connectors) is unchanged.
## Summary A rubber-band selection that covers only the empty area inside a freehand drawing's bounding box selects the drawing, even though it never crosses the ink. It should select a drawing only when the selection rectangle actually overlaps the strokes (this is the rubber-band counterpart of the click/drag fix in #222). ## Steps to reproduce 1. Draw a freehand stroke that leaves empty space inside its bounding box (a curve, or a few separate marks). 2. In selection mode, drag a rubber-band rectangle over a blank pocket inside that bounding box (not over any ink). 3. Observed: the drawing gets selected. 4. Expected: nothing is selected, because the rectangle did not touch the ink. ## Root cause `crates/hero_whiteboard_admin/static/web/js/whiteboard/tools.js`, `onMouseUp` (selection branch, ~line 845): the rubber-band selects every `.object` whose bounding box (`shape.getClientRect()`) intersects the selection box. For a freehand drawing the bounding box includes the empty space between strokes, so a rectangle over a blank pocket still matches. ## Proposed fix Keep the cheap bounding-box intersection as a pre-filter, but for drawings add a precise test: only select the drawing when the selection rectangle (in world coordinates) actually intersects one of the ink segments. Mirror the world-coordinate segment iteration already used by `_drawingHitByCircle`, using a segment-vs-AABB (Liang-Barsky) test against the selection rectangle. ## Acceptance criteria - [ ] A rubber-band over only empty space inside a drawing's bbox does not select the drawing. - [ ] A rubber-band that crosses or contains the ink still selects the drawing. - [ ] Selection of all other object types (sticky, shape, text, frame, kanban, mindmap, comment markers, connectors) is unchanged.
Author
Member

Implementation Spec for Issue #224

Objective

A rubber-band selection should select a freehand drawing only when the selection rectangle actually overlaps the ink, not merely the drawing's bounding box.

Root cause

onMouseUp (selection branch, tools.js ~line 845) selects any .object whose getClientRect() (bbox) intersects the selection box. A drawing's bbox includes blank space between strokes.

Files to Modify

  • crates/hero_whiteboard_admin/static/web/js/whiteboard/tools.js - onMouseUp selection loop + two small geometry helpers.

Implementation Plan

Step 1: Add segment-vs-rectangle helpers

Files: crates/hero_whiteboard_admin/static/web/js/whiteboard/tools.js

  • Add _segIntersectsAABB(x0,y0,x1,y1,minX,minY,maxX,maxY) using Liang-Barsky clipping (returns true if the segment touches the axis-aligned box, including endpoints inside).
  • Add _drawingIntersectsRect(group, rectWorld): iterate the group's .line points in world coords (pts[i] + group.x()/y(), mirroring _drawingHitByCircle) and return true if any segment intersects rectWorld. Single-point strokes: test the point for containment.
    Dependencies: none

Step 2: Use the precise test for drawings in the rubber-band loop

Files: crates/hero_whiteboard_admin/static/web/js/whiteboard/tools.js

  • In onMouseUp, compute the selection box in world coords once: var boxWorld = selectionRect.getClientRect({ relativeTo: layer });.
  • In the shapes.forEach loop, after the existing bbox Konva.Util.haveIntersection(box, r) pre-filter passes, look up WhiteboardObjects.getObject(shape.id()); if its type is drawing and !_drawingIntersectsRect(shape, boxWorld), skip it. All other types keep the bbox behaviour.
    Dependencies: Step 1

Acceptance Criteria

  • A rubber-band over only empty space inside a drawing's bbox does not select the drawing.
  • A rubber-band that crosses or contains the ink still selects the drawing.
  • Selection of all other object types and connectors/comment markers is unchanged.

Notes

  • Drawings render with tension (spline smoothing); testing straight segments between points is a close, acceptable approximation for selection.
  • selectionRect and the drawing points are both in world coords, so the test is done in world space (relativeTo: layer); the cheap screen-space bbox pre-filter is kept to avoid the segment test on every object.
  • No API/persistence/sync changes; no JS test suite (verify via node --check + manual).
## Implementation Spec for Issue #224 ### Objective A rubber-band selection should select a freehand drawing only when the selection rectangle actually overlaps the ink, not merely the drawing's bounding box. ### Root cause `onMouseUp` (selection branch, tools.js ~line 845) selects any `.object` whose `getClientRect()` (bbox) intersects the selection box. A drawing's bbox includes blank space between strokes. ### Files to Modify - `crates/hero_whiteboard_admin/static/web/js/whiteboard/tools.js` - `onMouseUp` selection loop + two small geometry helpers. ### Implementation Plan #### Step 1: Add segment-vs-rectangle helpers Files: `crates/hero_whiteboard_admin/static/web/js/whiteboard/tools.js` - Add `_segIntersectsAABB(x0,y0,x1,y1,minX,minY,maxX,maxY)` using Liang-Barsky clipping (returns true if the segment touches the axis-aligned box, including endpoints inside). - Add `_drawingIntersectsRect(group, rectWorld)`: iterate the group's `.line` points in world coords (`pts[i] + group.x()/y()`, mirroring `_drawingHitByCircle`) and return true if any segment intersects `rectWorld`. Single-point strokes: test the point for containment. Dependencies: none #### Step 2: Use the precise test for drawings in the rubber-band loop Files: `crates/hero_whiteboard_admin/static/web/js/whiteboard/tools.js` - In `onMouseUp`, compute the selection box in world coords once: `var boxWorld = selectionRect.getClientRect({ relativeTo: layer });`. - In the `shapes.forEach` loop, after the existing bbox `Konva.Util.haveIntersection(box, r)` pre-filter passes, look up `WhiteboardObjects.getObject(shape.id())`; if its type is `drawing` and `!_drawingIntersectsRect(shape, boxWorld)`, skip it. All other types keep the bbox behaviour. Dependencies: Step 1 ### Acceptance Criteria - [ ] A rubber-band over only empty space inside a drawing's bbox does not select the drawing. - [ ] A rubber-band that crosses or contains the ink still selects the drawing. - [ ] Selection of all other object types and connectors/comment markers is unchanged. ### Notes - Drawings render with `tension` (spline smoothing); testing straight segments between points is a close, acceptable approximation for selection. - selectionRect and the drawing points are both in world coords, so the test is done in world space (`relativeTo: layer`); the cheap screen-space bbox pre-filter is kept to avoid the segment test on every object. - No API/persistence/sync changes; no JS test suite (verify via node --check + manual).
Author
Member

Implementation Summary

Changes

crates/hero_whiteboard_admin/static/web/js/whiteboard/tools.js:

  1. Added _segIntersectsAABB(...) (Liang-Barsky segment-vs-axis-aligned-box test) and _drawingIntersectsRect(group, rectWorld), which iterates the drawing's .line points in world coords (mirroring _drawingHitByCircle) and returns true only when a stroke segment actually crosses the rectangle.
  2. In onMouseUp rubber-band selection, the world-space selection rectangle (selectionRect.getClientRect({ relativeTo: layer })) is computed once. The cheap screen-space bbox haveIntersection stays as a pre-filter; for objects whose type is drawing, the shape is only selected when _drawingIntersectsRect confirms the rectangle overlaps the ink. All other object types keep the bbox behaviour.

Verification

  • No JS test suite; static front-end change, no Rust changes.
  • node --check passes.
  • Unit-checked the geometry helper against endpoint-inside, crossing, fully-outside, corner-miss, and contains-both-ends cases - all correct.
  • Manual check: a rubber-band over a blank pocket inside a drawing's bbox no longer selects it; a rubber-band crossing or enclosing the ink still selects it; other object types unaffected.

Acceptance criteria

  • A rubber-band over only empty space inside a drawing's bbox does not select the drawing.
  • A rubber-band that crosses or contains the ink still selects the drawing.
  • Selection of all other object types and connectors/comment markers is unchanged.
## Implementation Summary ### Changes `crates/hero_whiteboard_admin/static/web/js/whiteboard/tools.js`: 1. Added `_segIntersectsAABB(...)` (Liang-Barsky segment-vs-axis-aligned-box test) and `_drawingIntersectsRect(group, rectWorld)`, which iterates the drawing's `.line` points in world coords (mirroring `_drawingHitByCircle`) and returns true only when a stroke segment actually crosses the rectangle. 2. In `onMouseUp` rubber-band selection, the world-space selection rectangle (`selectionRect.getClientRect({ relativeTo: layer })`) is computed once. The cheap screen-space bbox `haveIntersection` stays as a pre-filter; for objects whose type is `drawing`, the shape is only selected when `_drawingIntersectsRect` confirms the rectangle overlaps the ink. All other object types keep the bbox behaviour. ### Verification - No JS test suite; static front-end change, no Rust changes. - `node --check` passes. - Unit-checked the geometry helper against endpoint-inside, crossing, fully-outside, corner-miss, and contains-both-ends cases - all correct. - Manual check: a rubber-band over a blank pocket inside a drawing's bbox no longer selects it; a rubber-band crossing or enclosing the ink still selects it; other object types unaffected. ### Acceptance criteria - [x] A rubber-band over only empty space inside a drawing's bbox does not select the drawing. - [x] A rubber-band that crosses or contains the ink still selects the drawing. - [x] Selection of all other object types and connectors/comment markers is unchanged.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_whiteboard#224
No description provided.