Rotating 3-D funnel view (viz::funnel3d)
Face: the Funnel tab's 🌐 3D toggle paints the selected plan's DAG as a spinning, depth-shaded node cloud that cycles three layouts — sphere → helix → force — instead of the flat egui-snarl 2-D graph.
Map (where it lives)
| Part | Location |
| Render + layout math | src/viz/funnel3d.rs (Funnel3D, Layout3D) |
| Toggle + layout-cycle UI | src/viz/funnel_tab.rs — controls bar (🌐 3D, ⟳ <layout>, spin) |
| Central-panel routing | src/viz/funnel_tab.rs::draw (3-D branch vs the snarl branch) |
| Robot state | state_json()["funnel"]["view3d"] + ["funnel"]["graph3d"] |
| Test hooks | FunnelTabState::{set_3d_for_test, cycle_3d_layout_for_test} |
| Unit tests | src/viz/funnel3d.rs #[cfg(test)] (3 tests) |
| Injection test | tests/funnel_3d.rs (demo → 3-D → assert graph + cycle) |
Territory (what it does)
-
Builds one 3-D node per
PlanViewnode (coloured by lifecycle viaNodeStat::color_themed, palette-aware), one edge per dependency. -
Three layouts, cycled by the
⟳button /cycle_3d_layout_for_test():sphere— Fibonacci sphere (even shell spread),helix— helix along Y,force— deterministic 3-D Fruchterman–Reingold (the call-graph look).
-
Spins on its own (Y-rotation); dragging the cloud grabs the yaw and pauses the spin. Depth → node size + brightness; edges fade by mean depth.
-
Rebuilds only when the selected plan changes; spins every frame.
Spine (why it's here / the facett relationship)
The user wanted "a rotating 3 graph of funnel tasks … make it in facett if not". The component already exists in facett as facett-graph3d::Graph3D (../facett/facett-graph3d) — same projection, same three Layout3Ds, same state_json() shape. We could not depend on it directly: nornir is pinned to egui 0.33 (the highest egui-snarl 0.9 supports) while facett is on egui 0.34, so pulling facett-graph3d would fork the egui tree.
src/viz/funnel3d.rs is therefore the egui-0.33-native sibling of facett-graph3d: the layout + projection math is intentionally identical (so the state_json shapes match for the robot matrix). When nornir adopts egui 0.34 (an egui-snarl bump), cut this over to the shared facett-graph3d component and delete the local copy — the data contract is already aligned.
CLI / parity note
The 3-D view is a viz render mode (it visualises the same FunnelView the CLI nornir funnel show already dumps as data); there is no new CLI surface to add. The robot-UI driver toggles it headlessly via the test hooks and observes the result in state_json["funnel"]["graph3d"] (LAW: components expose what they render as readable data).