Summary
Add a second graph-layout algorithm to MAE's native KB graph view (BufferKind::Graph): a
chord diagram (a.k.a. circular/radial layout) — nodes evenly spaced around the circumference of
a circle, relationships between them drawn as curved chords through the interior (the classic
Circos-plot / D3 chord-diagram style). Currently the only layout is a force-directed physics
simulation (Fruchterman-Reingold). Longer-term goal (separate decision, not part of this issue's
initial scope): make the chord layout the default for KB graph views.
Architecture grounding (investigated, not guessed — file:line references)
GraphLayoutMode (crates/core/src/graph_view.rs:618-621) is a scheduling mode
(OneShot { iterations } vs Tick { temperature }) for the one existing algorithm (ForceLayout,
crates/canvas/src/layout.rs) — it is NOT an algorithm-choice enum. A chord/circular layout needs
a genuinely new sibling axis ("which algorithm"), not reuse of this enum. Worth flagging explicitly
so implementation doesn't conflate the two.
build_kb_graph_positions_only (crates/canvas/src/kb_graph.rs:76-144) is the reusable,
non-iterative seam: it already computes initial node positions with zero physics iteration
(currently a Vogel/Fibonacci-spiral "sunflower" disk placement, used today only as ForceLayout's
seed state before the physics pass runs). A circular layout is an even better fit for this exact
code path than the sunflower placement is — minimal new surface, e.g. one new function placing
SceneNode.x/y evenly around a circle's circumference. No new fields needed on SceneNode itself
for node placement.
- Curved-edge rendering already exists — this meaningfully shrinks the scope. The
kb_graph_edge_curvature option (crates/core/src/options.rs:576-581) drives
crates/core/src/graph_view.rs:1278-1305, which emits VisualElement::Curve { x1,y1, ctrl_x,ctrl_y, x2,y2, .. } (crates/core/src/visual_buffer.rs), rendered via a real Skia quadratic-Bezier
quad_to path (crates/gui/src/lib.rs:1569+) — not a fake/segmented line. Today's control point
is a perpendicular offset from the straight-line midpoint (used to bow parallel edges apart), not
the classic chord-diagram control point (pulled toward the circle's center). The gap is "wrong
control-point formula for this mode," not "no curve support at all" — a much smaller item than
building Bezier rendering from scratch.
- Background-threading (
crates/mae/src/graph_layout_bridge.rs, spawn_layout_computation) exists
because ForceLayout::run/step is O(n²)-per-iteration CPU-bound work. A circular layout is O(n)
direct computation with no iteration — not structurally required to go through the background
bridge; could run synchronously inline, same as the sunflower seed placement already does today.
- OptionRegistry pattern for a "which layout algorithm" (and eventually "default to circular")
setting: the same 3-part pattern every other kb_graph_* option already uses — opt!()
registration in options.rs (~lines 501-646), a matching Editor struct field (editor/mod.rs),
and a get/set arm in option_ops.rs (mirroring kb_graph_edge_curvature's own registration
exactly: options.rs:576-581 + option_ops.rs:252,1138).
Suggested phased scope
- New position-computation function (e.g.
build_kb_graph_circular_positions in
crates/canvas/src/kb_graph.rs, or a CircularLayout struct in layout.rs) reusing the
build_kb_graph_positions_only seam — places nodes evenly around a circle.
- New
kb_graph_layout_algorithm option (OptionKind::String/enum: "force" | "chord") following
the established 3-part OptionRegistry pattern. Ship initially NOT defaulted to chord — behind the
option first.
- Adjust the chord-mode edge control-point formula to pull toward the circle's center, rather than
reusing the parallel-edge-bow perpendicular-offset formula used today.
- Once validated, flip the default per the original ask (a separate, deliberate decision — not
bundled into step 1-3's initial ship).
Open implementation detail (not a blocker): whether background-threading is worth keeping for
consistency/animatable-transition purposes even though it's not strictly required for an O(n)
layout — a call for whoever picks this up, not decided here.
Related
Filed as a standalone tracking issue per user request; not blocking any currently-open PR.
Summary
Add a second graph-layout algorithm to MAE's native KB graph view (
BufferKind::Graph): achord diagram (a.k.a. circular/radial layout) — nodes evenly spaced around the circumference of
a circle, relationships between them drawn as curved chords through the interior (the classic
Circos-plot / D3 chord-diagram style). Currently the only layout is a force-directed physics
simulation (Fruchterman-Reingold). Longer-term goal (separate decision, not part of this issue's
initial scope): make the chord layout the default for KB graph views.
Architecture grounding (investigated, not guessed — file:line references)
GraphLayoutMode(crates/core/src/graph_view.rs:618-621) is a scheduling mode(
OneShot { iterations }vsTick { temperature }) for the one existing algorithm (ForceLayout,crates/canvas/src/layout.rs) — it is NOT an algorithm-choice enum. A chord/circular layout needsa genuinely new sibling axis ("which algorithm"), not reuse of this enum. Worth flagging explicitly
so implementation doesn't conflate the two.
build_kb_graph_positions_only(crates/canvas/src/kb_graph.rs:76-144) is the reusable,non-iterative seam: it already computes initial node positions with zero physics iteration
(currently a Vogel/Fibonacci-spiral "sunflower" disk placement, used today only as
ForceLayout'sseed state before the physics pass runs). A circular layout is an even better fit for this exact
code path than the sunflower placement is — minimal new surface, e.g. one new function placing
SceneNode.x/yevenly around a circle's circumference. No new fields needed onSceneNodeitselffor node placement.
kb_graph_edge_curvatureoption (crates/core/src/options.rs:576-581) drivescrates/core/src/graph_view.rs:1278-1305, which emitsVisualElement::Curve { x1,y1, ctrl_x,ctrl_y, x2,y2, .. }(crates/core/src/visual_buffer.rs), rendered via a real Skia quadratic-Bezierquad_topath (crates/gui/src/lib.rs:1569+) — not a fake/segmented line. Today's control pointis a perpendicular offset from the straight-line midpoint (used to bow parallel edges apart), not
the classic chord-diagram control point (pulled toward the circle's center). The gap is "wrong
control-point formula for this mode," not "no curve support at all" — a much smaller item than
building Bezier rendering from scratch.
crates/mae/src/graph_layout_bridge.rs,spawn_layout_computation) existsbecause
ForceLayout::run/stepis O(n²)-per-iteration CPU-bound work. A circular layout is O(n)direct computation with no iteration — not structurally required to go through the background
bridge; could run synchronously inline, same as the sunflower seed placement already does today.
setting: the same 3-part pattern every other
kb_graph_*option already uses —opt!()registration in
options.rs(~lines 501-646), a matchingEditorstruct field (editor/mod.rs),and a get/set arm in
option_ops.rs(mirroringkb_graph_edge_curvature's own registrationexactly:
options.rs:576-581+option_ops.rs:252,1138).Suggested phased scope
build_kb_graph_circular_positionsincrates/canvas/src/kb_graph.rs, or aCircularLayoutstruct inlayout.rs) reusing thebuild_kb_graph_positions_onlyseam — places nodes evenly around a circle.kb_graph_layout_algorithmoption (OptionKind::String/enum:"force" | "chord") followingthe established 3-part OptionRegistry pattern. Ship initially NOT defaulted to chord — behind the
option first.
reusing the parallel-edge-bow perpendicular-offset formula used today.
bundled into step 1-3's initial ship).
Open implementation detail (not a blocker): whether background-threading is worth keeping for
consistency/animatable-transition purposes even though it's not strictly required for an O(n)
layout — a call for whoever picks this up, not decided here.
Related
Filed as a standalone tracking issue per user request; not blocking any currently-open PR.