Skip to content

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

Description

@cuttlefisch

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

  1. 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.
  2. 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.
  3. 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.
  4. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions