Skip to content

Pull edges towards horizontal, not just out of their own way - #363

Merged
matt-edmondson merged 1 commit into
mainfrom
claude/overlap-regression
Sep 8, 2026
Merged

Pull edges towards horizontal, not just out of their own way#363
matt-edmondson merged 1 commit into
mainfrom
claude/overlap-regression

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Problem

The clearance splay added in #360 only fires once a curve is at risk of hiding behind the bodies at its own ends. That caps an edge at about 75.5 degrees off horizontal and does nothing at all below that — legal, but never flat. A graph settles with edges at whatever angle the springs and gravity happen to leave them, which in the node editor reads as a tangle.

Fix

CalculateLinkFlatteningForces now also levels an edge continuously, with a force proportional to the vertical offset between its two ends. The two mechanisms do different jobs:

fires job
levelling (new) always, on forward edges makes a link lie flat
clearance splay (#360) only below the bezier bound guarantees a link stays visible

A backward edge is exempt from levelling. It is still being reordered, and pulling it level would fight the vertical slide that #362's reorder needs to get its endpoints past one another.

Measured

On a six-body graph with four edges, shaped like the editor screenshot that prompted this:

mean angle off horizontal worst overlap
before 27.4° 0px
after 13.1° 0px

Per-edge after: 36.6°, 13.5°, 1.3°, 0.9° — two of the four land within about a degree of flat.

Neither mechanism can make every edge horizontal at once: a node with several inputs cannot have all of them level with it, so the edges that share a busy node keep some angle. This is a preference the overlap pass and springs push back on, not a constraint. LinkFlatteningStrength now scales both it and the clearance splay; 0 still disables both.

On the overlapping nodes in the same screenshot

I tried to reproduce those and could not. The no-overlap invariant holds at rest both for a two-body pair placed overlapping and for a crowded six-body graph with four backward edges — worst overlap 0px in every case, before and after this change. The most likely explanations are a frame caught mid-settle, or physics not enabled in that editor. Worth another look if it persists with physics running; I did not want to claim a fix I could not demonstrate.

Testing

  • LinkFlattening_PullsAForwardEdgeTowardsHorizontal — a steep forward edge closes to under a quarter of its starting vertical offset.
  • LinkFlattening_ZeroStrength_LeavesAnEdgeAsSteepAsItStarted — with the preference off, the spring alone leaves the edge steeper than it is wide.

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

Not verified visually — this container is headless and the demo UI suite cannot start here (the repo's PNGs are Git LFS pointers and git-lfs is not installed). The angles are measured, but whether LinkFlatteningStrength = 0.5 is the right amount of flattening is a judgement worth making in the real editor; the demo slider covers 0–2.

🤖 Generated with Claude Code

https://claude.ai/code/session_01D2KNUKr1xJPTEdDmeF2tQN


Generated by Claude Code

The clearance splay only fires once a curve is at risk of hiding behind
the bodies at its ends, so it caps an edge at about 75.5 degrees off
horizontal and does nothing at all below that. Legal, but not flat: a
graph settles with edges at whatever angle the springs and gravity leave
them, which in the node editor reads as a tangle.

CalculateLinkFlatteningForces now also levels an edge continuously, with a
force proportional to the vertical offset between its two ends. On a
six-body graph with four edges that takes the mean angle off horizontal
from 27.4 to 13.1 degrees, two of the four edges landing within about a
degree of flat, and introduces no overlap.

A backward edge is exempt. It is still being reordered, and levelling it
would fight the vertical slide that the reorder needs to get its endpoints
past one another.

Neither mechanism can make every edge horizontal at once - a node with
several inputs cannot have all of them level with it - so this is a
preference that the overlap pass and the springs push back on, not a
constraint. LinkFlatteningStrength now scales both it and the clearance
splay; 0 still disables both.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D2KNUKr1xJPTEdDmeF2tQN
@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