Skip to content

Add chord-diagram (circular) graph-layout mode, make it the default - #371

Merged
cuttlefisch merged 6 commits into
mainfrom
feat/chord-diagram-graph-layout
Jul 22, 2026
Merged

Add chord-diagram (circular) graph-layout mode, make it the default#371
cuttlefisch merged 6 commits into
mainfrom
feat/chord-diagram-graph-layout

Conversation

@cuttlefisch

Copy link
Copy Markdown
Owner

Summary

Closes #367. Adds a second layout algorithm for MAE's native KB graph view alongside the existing
force-directed (Fruchterman-Reingold) layout: a chord diagram — nodes evenly spaced around a
circle's circumference, relationships drawn as curved chords through the interior (Circos/D3-chord
style). Per explicit request, chord is now the default layout mode.

  • New GraphLayoutAlgorithm enum (Force/Chord) — orthogonal to the existing GraphLayoutMode
    (a scheduling concept, one-shot vs. animated tick, for whichever algorithm runs).
  • New kb_graph_layout_algorithm option, following the exact 3-part OptionRegistry pattern
    daemon_mode already established.
  • build_kb_graph_chord_positions shares all edge-building logic with the existing
    build_kb_graph_positions_only via a new positions_to_scene helper — only the position
    computation differs.
  • populate_graph_buffer skips queuing a background force-refinement pass entirely for Chord mode
    (the layout is immediately final).
  • Curved-edge rendering already existed; only the control-point formula needed a Chord-specific
    branch (pulled toward the viewport-transformed scene origin instead of Force's perpendicular
    midpoint offset).

Switch back to the old behavior any time with :set kb_graph_layout_algorithm force.

Test plan

  • cargo build --workspace + cd daemon && cargo build — clean
  • cargo test --workspace + daemon — all green (2745 mae-core tests, full canvas/daemon suites)
  • cargo clippy --workspace --all-targets -- -D warnings (both workspaces) — clean
  • cargo fmt --check — clean
  • make code-map-check — up to date
  • New tests: chord node placement (equidistant from origin, evenly angularly spaced, radius
    scales with node count, edge-building parity with the sunflower layout), populate_graph_buffer
    never queues a background pass for Chord (and still does for Force — regression guard),
    option roundtrip/reject, and edge-curve control-point direction (Chord pulls toward center,
    including under a panned viewport; Force keeps its existing perpendicular-offset behavior
    byte-for-byte unchanged)

🤖 Generated with Claude Code

cuttlefisch and others added 6 commits July 22, 2026 22:52
… the default (#367)

Adds a second algorithm for MAE's native KB graph view alongside the
existing force-directed (Fruchterman-Reingold) layout: nodes evenly
spaced around a circle's circumference, relationships drawn as curved
chords through the interior (Circos/D3-chord style). Chord is now the
default.

- New GraphLayoutAlgorithm enum (Force/Chord), orthogonal to the existing
  GraphLayoutMode (which is a scheduling concept -- one-shot vs animated
  tick -- for whichever algorithm runs, not an algorithm choice itself).
- New kb_graph_layout_algorithm option, following the exact 3-part
  OptionRegistry pattern daemon_mode already established (a real Rust
  enum with parse/as_str, validated in the setter).
- build_kb_graph_chord_positions (crates/canvas/src/kb_graph.rs) places
  nodes on a ring whose circumference grows linearly with node count
  (unlike the sunflower disk's deliberately sub-linear growth -- correct
  for a 1-D ring, not merely acceptable). Shares all edge-building logic
  with build_kb_graph_positions_only via a new positions_to_scene helper
  (only the position-computation differs between the two).
- populate_graph_buffer branches on the algorithm: Chord's layout is
  immediately final, so it never queues a background force-refinement
  pass (also forces GraphView.animating false regardless of
  kb_graph_animate, since nothing will ever arrive to settle it).
- Curved-edge rendering already existed (kb_graph_edge_curvature ->
  VisualElement::Curve -> a real Skia quadratic Bezier); only the
  control-point formula needed a Chord-specific branch: pulled toward the
  viewport-transformed scene origin instead of Force's perpendicular
  offset from the edge midpoint.
…branch

main's #369 changeset added KbNodeInfo::is_seed after this branch's chord
test fixtures were authored; backfill the field on the 3 affected literals.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ce mode's scale

Constant-arc-spacing (radius growing linearly with node count) blew the
chord ring up to tens of thousands of scene units on a real ~1300-node
subgraph -- far past what the shared [0.1, 10.0] zoom range can zoom out
to fit, leaving the diagram permanently too large to view. Reuse the
sunflower disk's sqrt(n) growth rate instead, extracted to a shared
sqrt_area_radius() helper, so both algorithms stay visually comparable.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…fault color, radial labels, quieter boundary badges

Smoke-testing the chord-diagram layout (#367) surfaced four related issues:

- No opacity control on edges, so dense chords near the circle's center
  blended into a solid gray mass. New kb_graph_edge_alpha option (default
  0.5, per chord-diagram design research); the boundary self-loop stub
  stays fully opaque regardless (sparse correctness signal, not part of
  the density problem).
- default.toml's ui.graph.edge was a stray literal hex instead of
  aliasing "comment" like every other shipped theme already does --
  one-line fix, no Rust changes needed (theme_hex_fg already resolves it).
- Node labels were always drawn flat-horizontal, so nodes near the top/
  bottom of the ring collided. Added radial label rotation with the
  standard D3/Circos 180-flip-and-right-align technique for the far half
  of the circle (chord_label_placement), gated on layout_algorithm ==
  Chord with zero effect on Force mode. label_bbox (used by the existing
  label-declutter pass) now shares the same placement so declutter and
  the real draw can never disagree.
- The boundary stub's "... (+N)" count badge is now hidden by default,
  shown only when its source node is hovered/selected (progressive
  disclosure) -- new kb_graph_boundary_stub_label_always_shown option
  restores the original always-on behavior.

Grounded in chord/Circos-diagram design research (D3, Circos docs, the
CHORDination VINCI-2024 study, Tufte chartjunk/data-ink-ratio framing).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@cuttlefisch
cuttlefisch merged commit 42776a9 into main Jul 22, 2026
27 of 28 checks passed
@cuttlefisch
cuttlefisch deleted the feat/chord-diagram-graph-layout branch July 22, 2026 23:01
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.

Add a chord-diagram (circular) graph-layout mode for the native KB graph view, make it default

1 participant