2026 04 03_layout_variant_follow_on_plan - mark-ik/graphshell GitHub Wiki
Status: Active follow-on plan. Updated 2026-04-19 — the Layout<N> trait
home moved from graph::layouts/ to graph_canvas::layout; Radial,
Phyllotaxis, and rapier2d have landed; Timeline and Kanban are the active
next-wave targets. Penrose and L-system move to a Step-5 design pass (see
2026-02-24_physics_engine_extensibility_plan.md).
Scope: Extracts the built-in layout-variant lane from 2026-02-24_physics_engine_extensibility_plan.md into an execution plan focused on new native layout families beyond FR/Barnes-Hut.
Related:
2026-02-24_physics_engine_extensibility_plan.md2026-04-03_layout_backend_state_ownership_plan.md2026-04-03_wasm_layout_runtime_plan.md2026-04-03_layout_transition_and_history_plan.mdlayout_algorithm_portfolio_spec.mdlayout_behaviors_and_physics_spec.mdmulti_view_pane_spec.mdview_dimension_spec.md
The physics extensibility seam is landed far enough that Graphshell no longer needs to keep all future layout work inside one umbrella note.
Current reality:
- post-physics helpers are Graphshell-owned and useful for force-directed refinement
-
ActiveLayout/ActiveLayoutStatealready provide a built-in layout dispatcher - Barnes-Hut proves Graphshell can ship new layout variants without taking full ownership of the
upstream
Layout<S>/LayoutStatetrait contract
This lane is specifically about new native built-in layout families. It depends on the shared
state-carrier decisions in 2026-04-03_layout_backend_state_ownership_plan.md, and it should stay
distinct from the runtime-loaded guest layout work tracked in
2026-04-03_wasm_layout_runtime_plan.md.
Layout work in this lane separates four distinct concerns. Conflating them is how unrelated capabilities get bundled into one umbrella variant, and how "force-directed vs Rapier" gets miscast as a single-axis comparison.
| Tier | Question answered | Examples |
|---|---|---|
| Layout algorithm | How are node positions computed or updated? | FR, Barnes-Hut, radial BFS, phyllotaxis spiral, Kanban column packing, timeline axis projection |
| Scene representation | What entities and constraints exist before layout runs? | plain node-edge, axial (timeline / Kanban / UDC-depth), rigid-body + joint + collider (Rapier), containment / domain hierarchy, workbench frame-affinity |
| Simulation backend | What advances the world over time, if anything? | none (analytic one-shot), Graphshell post-physics helpers, rapier2d world step, future persistent simulation |
| Render profile | How is the resulting scene displayed? | main graph canvas, Navigator swatch instance, overview/atlas card, debug overlay |
Force-directed is the default member of the algorithm tier with a trivial
scene representation and no persistent simulation backend. Rapier is richer
not because it computes positions differently, but because it brings a
physical scene representation (bodies, joints, colliders, masses) and a
persistent simulation backend (world stepping, momentum across frames). The
admission bar in Feature Target 4 evaluates each tier independently: a
candidate that only adds a new algorithm should not inherit a
scene-representation budget, and a candidate that needs a new scene
representation should be designed against the shared state carrier in
2026-04-03_layout_backend_state_ownership_plan.md rather than each variant
inventing its own.
The render-profile tier matters for Navigator swatches: the same algorithm + scene + backend triple may render at full fidelity on the main canvas and at a compact, virtualized fidelity inside a Navigator swatch without becoming a separate "layout variant."
- replacing the upstream
egui_graphstrait contract in this lane - turning built-in native variants into a proxy for the WASM runtime lane
- rapier scene-physics as a prerequisite for the first analytic variants
- quietly expanding this plan into layout transition/history ownership
ActiveLayoutState is currently too FR-shaped for analytic or topology-driven layouts. This plan
should consume the widened carrier defined in 2026-04-03_layout_backend_state_ownership_plan.md
rather than inventing a second persistence model.
- Replace the FR-shaped
physics-only assumption with per-variant payload support inside the shared Graphshell-owned state carrier. - Keep the outer adapter compatible with the current upstream
Layout<S>/LayoutStateseam. - Preserve per-
GraphViewIdpersistence and deterministic restore behavior. - Ensure the render layer continues to see one concrete
ActiveLayoutStatetype.
- ForceDirected and BarnesHut roundtrip through the widened carrier without state loss.
- Snapshot restore does not relayout unexpectedly when restoring the same variant and state.
- Unknown or unavailable variants degrade through the documented fallback path.
The first new variants should maximize conceptual difference from FR while minimizing dependency surface.
-
graph_layout:radial— landed asgraph_canvas::layout::RadialwithRadialConfig(focus, center, ring_spacing) and BFS-based ring assignment. -
graph_layout:phyllotaxis— landed asgraph_canvas::layout::Phyllotaxiswith inward/outwardSpiralOrientation. -
graph_layout:timeline— pending (needs metadata slot onLayoutExtras; see Target 2.1 below).
-
Add one module per variant underVariants live ingraph/layouts/.crates/graph-canvas/src/layout/static_layouts.rsunder the portableLayout<N>trait; they implement delta-to-target semantics with aStaticLayoutState.dampingfor instant-or-eased application. - Define deterministic input ordering rules for each variant.
- Add stable variant IDs and compatibility declarations consistent with
layout_algorithm_portfolio_spec.md. - Route those variants through the
LayoutAlgorithmregistry atapp::graph_layout, which is what graphshell's host already uses for one-shot apply (GridandTreealready live there; Radial, Timeline, Phyllotaxis need registry entries that delegate to the graph-canvas impls). - Define how transitions between variants use
2026-04-03_layout_transition_and_history_plan.mdinstead of ad hoc one-off animation behavior.
Timeline needs a per-node time coordinate that isn't in the current
CanvasSceneInput / LayoutExtras. Design question:
-
Option A: add
time_by_node: HashMap<N, f64>toLayoutExtras. Narrow, explicit, matches Kanban's analog. -
Option B: generalize to
axis_value_by_node: HashMap<N, AxisValue>whereAxisValueisenum { Numeric(f64), Categorical(String) }. Shared slot for Timeline's x-axis time + Kanban's column bucket tag + future axial layouts (UDC-depth, topic-frequency, etc.).
Option B is marginally more work but aligns the Timeline/Kanban/future analog into one slot and one mental model. Recommended.
- Switching among ForceDirected, BarnesHut, and first-wave variants preserves graph truth.
- Variant selection/restoration is deterministic by stable external ID.
- Layout switches can opt into the shared transition/history path without changing graph truth.
New variants only pay off if they are visible to selection policy, fallback logic, and quality diagnostics rather than living as hidden one-off modules.
- Ensure the portfolio registry can discover each built-in variant by canonical ID.
- Define compatibility/fallback rules for graphs or views that cannot satisfy a requested analytic layout.
- Expose requested vs resolved variant IDs through diagnostics.
- Ensure recommendation logic distinguishes analytic layouts from dynamic physics layouts.
- The portfolio registry can discover each new built-in variant by canonical ID.
- Unknown or incompatible requests fall back deterministically without mutating graph truth.
- Diagnostics identify both the requested and resolved variant IDs.
Once the first wave lands, Graphshell can admit more specialized variants without turning the portfolio into an uncurated idea dump.
-
Kanban / column projection — pending as Target 4.1 below. Reads
per-node categorical tags, buckets into columns. Blocks on the same
LayoutExtrasslot decision as Timeline (see Target 2.1). Small implementation (~80 LOC) once the slot exists. -
rapier-backed scene layout — landed as
graph_canvas::layout::RapierLayout(feature-gated behindsimulate). Current revision rebuilds the world per step; a persistent variant that carries momentum across frames is tracked in ../../../archive_docs/checkpoint_2026-04-20/graphshell_docs/implementation_strategy/graph/2026-04-19_persistent_rapier_adapter_plan.md (archived 2026-04-20; momentum-preserving drag-release landed). - Penrose / aperiodic tiling — pending design pass as part of Step 5 in 2026-02-24_physics_engine_extensibility_plan.md. Open questions: P2 kite-dart vs P3 rhombus, handling of node counts that don't fit a subdivision level cleanly.
- L-system path layouts — pending design pass as part of Step 5. Open questions: built-in grammars only (Hilbert / Koch / dragon) or a runtime grammar registry; if registry, grammar syntax and sandboxing.
Column bucket by categorical node value. Depends on the LayoutExtras
metadata slot decision in Target 2.1.
pub struct KanbanConfig {
pub origin: Point2D<f32>,
pub column_gap: f32,
pub row_gap: f32,
/// Canonical order of columns left-to-right. Nodes whose tag is absent
/// go in an "other" column at the end.
pub column_order: Vec<String>,
}Within each column, nodes are stacked top-down in stable index order. Columns with no members are omitted.
- Admit a new built-in variant only if it needs native compile-time ownership and does not fit better as a runtime-loaded WASM guest.
- Require a stable external ID, deterministic input ordering, and a fallback story before adding any new variant.
- Keep scene-backed and projection-backed variants aligned with their own authorities instead of collapsing everything into one layout bucket.
- Candidate variants can be rejected or deferred without ambiguity.
- The admission bar cleanly separates built-in native variants from runtime-loaded guest layouts.
- Variant growth does not force immediate replacement of the upstream trait seam.
This plan is complete when Graphshell can ship at least one non-FR analytic layout family beyond Barnes-Hut through the same built-in dispatcher, shared persisted state carrier, and diagnostics path used by the existing force-directed variants.