Skip to content

Raise MaxVelocity so a graph settles in seconds, not minutes - #365

Merged
matt-edmondson merged 1 commit into
mainfrom
claude/layered-spacing
Sep 8, 2026
Merged

Raise MaxVelocity so a graph settles in seconds, not minutes#365
matt-edmondson merged 1 commit into
mainfrom
claude/layered-spacing

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

What this actually was

A graph that opens as a tall narrow column with near-vertical links is not laid out wrongly — it has not finished. Measured on a twenty-node, twenty-edge graph the size of a small class, started clustered:

steps size aspect mean angle off horizontal
600 740 x 1063 0.70 64.8°
1800 1050 x 728 1.44 40.5°
6000 1448 x 833 1.74 28.8°
20000 1447 x 833 1.74 28.8°

600 steps is ten seconds at sixty frames a second, which is about as long as anyone watches a graph unfold. The shape at that point is what looks broken; the settled one is fine. Starting position makes no difference — scattered, clustered and coincident all converge to the same shape — so this was not a local minimum.

I had guessed the horizontal spacing was pinned at the ordering bias's minimum gap (~150) and proposed making it target the rest length. The measurement says otherwise: horizontal step settles at 196–208 against a rest length of 225. That premise was wrong and no layering change is needed.

The bound is MaxVelocity

A body has to travel several hundred units to reach its place, and a cap of 50/second means seconds per body. At 600 steps:

MaxVelocity aspect mean angle
50 (old) 0.70 64.8°
150 1.17 39.0°
250 (new) 1.44 38.8°
400 1.75 33.9°
600 1.34 34.3°

Past ~400 it stops helping — 600 comes back down — so there is a real overshoot limit and the new default sits inside it. 250 reaches its settled shape roughly five times sooner while settling to much the same place: 1.49 at 28.5° against the old default's 1.74 at 28.8°. 400 is faster still but shifts the settled shape more (aspect 2.10), and 600's non-monotonicity says that is near the edge.

Is repulsion still necessary?

Yes, emphatically. Settled, on the same graph:

size aspect mean angle
with repulsion 1313 x 881 1.49 28.5°
without 594 x 840 0.71 72.0°

The overlap pass only keeps bodies off one another; it creates no room beyond that. Without repulsion the graph collapses into exactly the tall column the levelling force then cannot recover from. Removing it would reintroduce the reported symptom permanently.

Testing

Two tests cover what nothing did — settling speed was never measured, which is why a correct layout could look broken:

  • ASmallGraph_IsReadableWithinTenSeconds — at 600 steps the graph must be wider than tall with mean angle under 45°.
  • Repulsion_IsWhatSpreadsAGraphOut — with repulsion the settled graph must be both wider and at least 15° flatter.

Step_WithOverlapMarginZero_LeavesTheBodiesOverlapping needed correcting: it was asserting a transient. Two 400-wide bodies belong 420 apart once the ordering bias has its way (halfWidths + 20), and the pair only stayed overlapping for 120 frames because the old cap made them too slow to get there. It now disables the forces that separate a pair, so it measures the overlap pass rather than how far anything travelled.

ForceDirectedLayout.Tests 39/39, ImGui.NodeEditor.Tests 71/71, ForceDirectedLayout.Native and ImGuiAppDemo build clean in Release.

Not verified visually — this container is headless. The numbers are measured, but whether ten seconds is still too long to wait is a judgement worth making in the editor; MaxVelocity is a setting and the demo exposes it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01D2KNUKr1xJPTEdDmeF2tQN


Generated by Claude Code

A graph that opened as a tall narrow column with near-vertical links was
not laid out wrongly - it had not finished. Measured on a twenty node,
twenty edge graph the size of a small class, starting clustered:

    600 steps    740 x 1063   aspect 0.70   mean angle 64.8 deg
   1800 steps   1050 x  728   aspect 1.44   mean angle 40.5 deg
   6000 steps   1448 x  833   aspect 1.74   mean angle 28.8 deg
  20000 steps   1447 x  833   aspect 1.74   mean angle 28.8 deg

600 steps is ten seconds at sixty frames a second, which is about as long
as anyone watches. The shape at that point is what looked broken; the
settled one is fine. Starting position makes no difference - scattered,
clustered and coincident all converge to the same shape - so this was not
a local minimum.

MaxVelocity was the bound. A body has to travel several hundred units to
reach its place, and a cap of 50 a second means seconds per body. At 600
steps: v50 gives aspect 0.70 at 64.8 deg, v150 1.17 at 39.0, v250 1.44 at
38.8, v400 1.75 at 33.9. Past that it stops helping - v600 comes back to
1.34 at 34.3 - so there is an overshoot limit and the default now sits
inside it at 250, which reaches its settled shape roughly five times
sooner while settling to much the same place (1.49 at 28.5 deg against
1.74 at 28.8).

Repulsion is still doing its job, which is what prompted looking. Settled,
with it the graph is 1313 x 881 at 28.5 deg; without it 594 x 840 at 72.0
deg. The overlap pass only keeps bodies off one another, it creates no
room beyond that, so without repulsion the graph collapses into the column
the levelling force cannot then recover from.

Two tests cover what nothing did: ASmallGraph_IsReadableWithinTenSeconds
pins the settling speed, and Repulsion_IsWhatSpreadsAGraphOut pins why
repulsion stays.

Step_WithOverlapMarginZero_LeavesTheBodiesOverlapping was asserting a
transient. Two 400 wide bodies belong 420 apart once the ordering bias has
its way, and the pair only stayed overlapping for 120 frames because the
old cap made them too slow to get there. It now turns off the forces that
separate a pair, so it measures the overlap pass rather than how far
anything travelled.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D2KNUKr1xJPTEdDmeF2tQN
@matt-edmondson
matt-edmondson merged commit cdf97a9 into main Sep 8, 2026
12 checks passed
@matt-edmondson
matt-edmondson deleted the claude/layered-spacing branch September 8, 2026 11:09
@sonarqubecloud

sonarqubecloud Bot commented Sep 8, 2026

Copy link
Copy Markdown

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.

2 participants