Skip to content

fix(arrows): clamp boundary anchors to avoid degenerate intersections#8130

Merged
AniKrisn merged 2 commits intomainfrom
ani/arrow-bug-clean
Mar 9, 2026
Merged

fix(arrows): clamp boundary anchors to avoid degenerate intersections#8130
AniKrisn merged 2 commits intomainfrom
ani/arrow-bug-clean

Conversation

@AniKrisn
Copy link
Copy Markdown
Contributor

@AniKrisn AniKrisn commented Mar 2, 2026

Closes #8125

dune-bug-fixed.mp4

Arrow endpoints flicker when normalizedAnchor coordinates are exactly 0 or 1 (shape edges/corners). At these exact values, the intersection math becomes numerically unstable — small floating-point errors cause intersections to toggle between found and not-found across frames.

Solution: if arrow is precise or forceImprecise, clamp each anchor coordinate from [0, 1] to [0.001, 0.999]. This clamping happens inside getArrowTerminalInArrowSpace, which runs every time the arrow info is recomputed (i.e. whenever the arrow or its bound shapes change). The binding record in the store is never modified — the nudge only exists ephemerally during computation, so the intersection math never sees an exact boundary value.

Change type

  • bugfix

Test plan

  1. Create a shape and programmatically bind a self-referential arrow with anchors at exact boundary positions (e.g. {x: 0, y: 0}, {x: 0.5, y: 0})
  2. Verify arrows render without flickering
  3. Resize and move the shape — arrows should remain stable
  • Unit tests

Release notes

  • Fix arrow endpoint flickering when anchors are at exact shape boundaries

Note

Medium Risk
Touches core arrow terminal computation; clamping precise anchors near 0/1 can subtly shift endpoints (up to ~0.1% of shape size) and may affect arrow rendering/attachment in edge cases.

Overview
Prevents arrow endpoint flicker when a bound terminal’s normalizedAnchor lands exactly on a shape edge/corner by clamping precise anchors away from 0/1 during getArrowTerminalInArrowSpace computations.

Adds a small epsilon-based clampNormalizedAnchor helper and refactors the terminal point calculation to use the clamped anchor without mutating stored binding data.

Written by Cursor Bugbot for commit f4ecde2. This will update automatically on new commits. Configure here.

Arrow endpoints flicker when normalizedAnchor coordinates are exactly 0 or 1
(shape edges/corners). At these values, the downstream intersection math hits
degeneracies: coincident rays along edges, shared polygon vertices between
adjacent edges, and tangent arc circles. The intersection toggles between
found/not-found across frames, causing visible oscillation.

Fix by clamping normalizedAnchor from [0, 1] to [0.001, 0.999] during terminal
computation in getArrowTerminalInArrowSpace. The stored binding is never
modified — the nudge only exists ephemerally so the intersection math never
sees an exact boundary value. The 0.001 offset is 0.2px on a 200px shape,
visually imperceptible.
@huppy-bot huppy-bot bot added the bugfix Bug fix label Mar 2, 2026
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 2, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
examples Ready Ready Preview Mar 3, 2026 4:32pm
5 Skipped Deployments
Project Deployment Actions Updated (UTC)
analytics Ignored Ignored Preview Mar 3, 2026 4:32pm
chat-template Ignored Ignored Preview Mar 3, 2026 4:32pm
tldraw-docs Ignored Ignored Preview Mar 3, 2026 4:32pm
tldraw-shader Ignored Ignored Preview Mar 3, 2026 4:32pm
workflow-template Ignored Ignored Preview Mar 3, 2026 4:32pm

Request Review

@AniKrisn AniKrisn requested review from kaneel and steveruizok March 2, 2026 16:08
Copy link
Copy Markdown
Contributor

@kaneel kaneel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't even pretend I understand the math but looking at it, and also trying to paste one of my "buggy mermaid flowcharts" within the example preview that contains your fix, it seems to be working for me!

// If the parent is the bound shape, then it's always treated as precise.
const shouldUsePreciseAnchor = binding.props.isPrecise || forceImprecise
const normalizedAnchor = shouldUsePreciseAnchor
? // Keep exact bindings untouched; only sanitize imprecise/precise anchors to avoid
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the normalized difference here is negligible... unless I'm missing something, I'd normalize everything

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep sounds good. made the change

@mimecuvalo
Copy link
Copy Markdown
Member

mimecuvalo commented Mar 3, 2026

best PR title i've seen in a while

@steveruizok
Copy link
Copy Markdown
Collaborator

send it

@AniKrisn AniKrisn added this pull request to the merge queue Mar 9, 2026
Merged via the queue into main with commit 6846df5 Mar 9, 2026
21 checks passed
@AniKrisn AniKrisn deleted the ani/arrow-bug-clean branch March 9, 2026 14:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Arrows disappear when terminals land exactly on shape edges or corners

4 participants