Skip to content

feat: elbow (orthogonal) arrows — Straight | Elbow arrow type - #111

Merged
Amark19 merged 7 commits into
feat/arrow-shape-bindingfrom
feat/elbow-arrows
Aug 23, 2026
Merged

Amark19 merged 7 commits into
feat/arrow-shape-bindingfrom
feat/elbow-arrows

Conversation

@Amark19

@Amark19 Amark19 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

The eraser.io look you referenced: right-angle connectors. Stacked on #110 (binding) so bound elbow arrows compose.

What changed

  • New Arrow-type toggle in the properties panel: Straight | Elbow (default straight). Draw with it, or select an arrow and switch — it rebuilds preserving endpoints, label and bindings.
  • The connector generalises from a straight line to an orthogonally-routed polyline:
    • elbowRoute — a clean mid-bend Z (routes along the dominant axis first, right angles at each bend; collapses to straight when aligned).
    • layoutElbowPolyline — pins the polyline's left/top to its pathOffset so points render at exact group-local coords (the fabric.Polyline positioning gotcha, de-risked in-browser first).
    • applyEndpointsLocal / localEndpoints / sceneEndpoints are now connector-agnostic; the head aims along the last route segment.
  • Detection: isArrow/getArrowParts accept a line or polyline; new isElbowArrow.
  • rebuildArrow centralises every arrow rebuild (panel head/type changes + resize handler), preserving heads / straight-vs-elbow / label / bindings and handling both connector kinds.
  • Composes with binding: a bound elbow arrow re-routes its right-angle path edge-to-edge as shapes move (uses the ellipse-accurate border anchoring from feat: arrow ↔ shape binding (arrows follow boxes when moved) #110).

Test plan

  • Unit (79 pass): elbowRoute geometry (H-then-V-then-H, mid-y bend, straight collapse, all segments axis-aligned); isElbowArrow true/false; existing arrow/endpoint/binding suites unchanged.
  • Manual (in-browser): drew an elbow arrow between two boxes → 4-point polyline, orthogonal Z, bound both ends, edge-anchored; moved a box → re-routed orthogonally, still bound; straight arrows unaffected.

Follow-ups (deferred)

  • Perpendicular-to-edge exits and obstacle-aware routing (v1 uses a mid-bend Z between the anchor points).
  • Live elbow preview while drawing (v1 previews a straight line, snaps to elbow on drop).
  • Rounded elbow corners.

Stack

master → #104 → #106 → #108 → #105 (binding) → this.

🤖 Generated with Claude Code

Adds right-angle connectors like eraser.io. The arrow's connector generalises
from a straight line to an orthogonally-routed polyline, chosen via a new
Arrow-type toggle (Straight | Elbow, default straight); it composes with
binding — a bound elbow arrow re-routes its right-angle path edge-to-edge as
boxes move.

- arrowEndpoints: elbowRoute (mid-bend Z), layoutElbowPolyline (pin left/top to
  pathOffset so points render at exact group-local coords), polyline-aware
  localEndpoints + applyEndpointsLocal (head aims along the last segment),
  exported sceneEndpoints.
- shapeLabel: isArrow/getArrowParts accept a line OR polyline connector;
  new isElbowArrow.
- arrow.js: buildArrowGroup builds the polyline connector for elbow and re-lays
  it in group-local space; new rebuildArrow centralises rebuilds (panel +
  resize) preserving heads/type/label/bindings and handling both connectors.
- toolStyle: arrowType default + ARROW_TYPE_OPTIONS; PropertiesPanel: Arrow-type
  control + setArrowType (rebuild selected arrow); canvas.currentArrowType.
- binding: endpoint helper delegates to sceneEndpoints (elbow-safe).
- arrowResizeHandler routes through rebuildArrow.
- Tests: elbowRoute geometry + isElbowArrow (79 total).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 17, 2026 •

Copy link
Copy Markdown

Deploying whiteboard with  Cloudflare Pages  Cloudflare Pages

Latest commit: bc13abc
Status: ✅  Deploy successful!
Preview URL: https://b7f1c42f.whiteboard-988.pages.dev
Branch Preview URL: https://feat-elbow-arrows.whiteboard-988.pages.dev

View logs

Amark19 and others added 6 commits August 18, 2026 15:17
- The highlight is now an additional border layer ON the target's exact outline
  (no padding), shape-aware: an ellipse target gets an ellipse outline, a
  rounded rect matches its corner radius — instead of a padded rounded box.
- Trigger distance BIND_MARGIN 24 -> 8, so the shape highlights/binds when the
  endpoint is on the border, not while it's still ~24px away.
- (Draw already highlights BOTH source and destination; verified with the exact
  overlay.) Icons remain limited by their own transparent bbox padding.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…udged off

Two binding glitches:
- The arrowhead's centre was placed at the endpoint, so its tip vertex
  overshot by half the head length and poked inside a bound shape. Back the
  centre off along the incoming segment (headCenterFor) so the visible tip
  lands exactly on the endpoint — for straight + elbow, live preview + final.
- Moving/nudging a bound arrow dragged its bound end off into empty space
  (it "went to a different position"). Reroute the arrow on object:modified so
  bound ends snap back onto their shapes; the free end keeps the drag.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The re-glue-on-move handler also fired for endpoint drags (object:modified
carries action "arrowEndpoint"), and it ran BEFORE arrow:endpoint:up — so it
snapped a just-dragged-off endpoint back onto the shape, and the up handler
then re-detected the shape and rebound it. Net effect: a bound end could never
be unbound. Skip the reroute for endpoint-drag modifications; the endpoint drag
already owns its own (un)binding via arrow:endpoint:up. Whole-arrow moves
(action "drag") still re-glue as before.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ect move

Three binding/arrow glitches from the video:

1. Line poked past the arrowhead tip. The connector ran all the way to the tip
   vertex, where the triangle narrows to a point, so the line's width showed
   through. The connector now stops at the head CENTRE (hidden under the head);
   the head's tip vertex becomes the logical endpoint (localEndpoints reads it),
   so controls/binding still use the true tip. Applies to straight + elbow and
   the live draw preview.

2. Moving a bound arrow snapped it back to the shape. A whole-arrow move now
   re-evaluates each bound end where it lands: still over its shape -> stay
   glued (a tiny nudge won't float); dragged off -> unbind and stay put; dropped
   on another shape -> rebind. So an arrow can be moved away instead of springing
   back.

3. Arrows didn't follow a multi-select drag. onShapeChange ignored
   activeSelection; now it fans out to every selected shape. And a shape inside
   an activeSelection reports selection-RELATIVE coords, so sceneBBox transforms
   the bbox by the group matrix to recover absolute scene coords — otherwise the
   re-routed arrow jumped to the origin.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…k gap

Dragging a bound arrow's body let the arrowhead follow the cursor into the
shape, then snapped it back to the border on release — leaving a gap between the
cursor and the arrowhead. Now the bound end is re-glued to its shape's border
LIVE on every object:moving frame, so the head stays on the shape as you drag
(the free end follows) and there's no jump on drop.

To keep the live re-glue from fighting fabric's own translate of the dragged
group, setArrowEndpoints/rerouteArrow take a refit flag: mid-drag we reposition
only the children (refit=false); bounds are re-fitted once on drop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
borderPoint clipped the centre->target ray to the shape's bounding box, which is
exact for rectangles but for a diamond/hexagon lands in the corner gap between
the bbox and the slanted edge — so the arrowhead floated outside the shape.
Polygons now use a ray-polygon intersection against their actual edges (group-
aware, so it also works mid multi-select drag), falling back to the bbox clip.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Amark19
Amark19 merged commit fca0f06 into master Aug 23, 2026
1 check passed
@Amark19
Amark19 deleted the feat/elbow-arrows branch August 23, 2026 17:14
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