layouts
New in this cycle: Layout system — arc packing, radial destinations, growth relayout
What changed
1. Rename ArcLayout → RadialLayout
The old ArcLayout with fullCircle: true was actually radial distribution (concentric rings), not arc packing. Renamed to RadialLayout for clarity. Both exported from @qpoint-io/qflow. Stage.js now uses RadialLayout for endpoints.
2. New ArcLayout — true bounded-arc packing
Positions items along an arc segment between configurable startAngle and endAngle. Each item's angular footprint is 2 * arcsin(radius / arcRadius), so larger items consume more arc space and smaller items fill gaps naturally. Overflows to outer rings when the arc fills.
3. Growth-based relayout
Nodes grow via updateNodeSize() as traffic arrives, but layout previously only refreshed on new node discovery — causing overlaps over time.
The problem: When a node's radius increases from 5px to 15px, it now occupies 3x the space but its neighbors haven't moved. With dozens of nodes growing simultaneously, the layout becomes a mess of overlapping circles.
How it works:
- Every call to
updateNodeSize()accumulates|newRadius - oldRadius|into a runninggrowthDeltacounter - When
growthDelta >= growthThreshold, a relayout is triggered and the counter resets to 0 - A
growthCooldowntimer prevents back-to-back relayouts during traffic bursts
Two types of relayout, different triggers:
- Discovery relayout — fires immediately when a new node appears (needs positioning)
- Growth relayout — throttled by both threshold and cooldown (cosmetic fix for overlap)
Controls:
- Growth Threshold (default 30px) — cumulative radius delta before relayout fires. Lower = more frequent relayouts (tighter spacing, more motion). Higher = tolerates more overlap before correcting.
- Cooldown (default 500ms) — minimum time between growth relayouts. Prevents thrashing when many nodes resize in a burst.
- Cumulative Delta — live readout of current accumulated radius change. Shows how close you are to the next growth relayout.
4. Layout interface refactor — computePositions
All layouts now expose computePositions(items) returning {positions: {key: {x,y}}, width, height} — pure data, no container side effects. A separate applyPositions(playerMap, positions, animated) utility handles tweening existing nodes and snapping new ones. This separation enables layout composition (ZonedLayout) and makes testing easier.
5. ZonedLayout stub
Documented interface for a future composite layout:
- Ingress traffic in an arc on the left
- Internal services in a packed cluster in the center
- Egress traffic in an arc on the right
Design questions documented in source: item classification, zone sizing, pulse routing, reclassification animation.
6. Experiment controls
Added to toolbar:
- Dest layout: Radial (renamed), Arc (new), Grid
- Arc controls (visible when dest=arc): start angle, end angle, radius, ring gap sliders
- Relayout section: growth threshold slider, cooldown slider, live cumulative delta display
- New "Arc Demo" preset showcasing the bounded-arc layout
- All existing c6 controls preserved
Visual record
9 captures — heroes, page slices, and full-page renders. Click to view.








