SPEC-13 — Sunburst view: polar projection of the world (GPU, morph, same scan as the treemap)#32
Merged
Merged
Conversation
…ame scan as the treemap) A second map mode, dotMemory-style: depth = concentric rings (geometric decay — infinite depth fits the disc, zoom reveals it), size = angular extent, the zoom root = the hole (name + total). Same ScanController, same tree, same navigation state: switching modes never re-scans. - SunburstWorld: parent-relative angular fractions, ε-stable local revalidation, LOD on projected arc length with hysteresis, subtree reach test (descendants extend outward), sub-pixel tail closed by an aggregate remainder arc (the polar underlay), polar file LOD. - SunburstMetalRenderer: instanced bounding quads carved by a polar SDF in the fragment shader (AA on curved edges, cushion, border, dim), triple buffering + prev-buffer morph, blending on, no depth. - SunburstNSView: isotropic letterboxed camera (circles stay circles, no aspect re-bake), explicit re-root morph on dive/pull-back, free pan/zoom inspection, sector spotlight + hole label in the CG overlay, full interaction parity (hover pill, reveal, context menu, gestures). - MapChrome: hover/menu/unavailable/a11y chrome shared by both views; MapModePicker toggle persisted via @AppStorage("mapMode"). - Tests: 8 SunburstWorld property tests (partition, camera independence, ε order-keeping, re-root composition, rings bounded, LOD hysteresis, tail closure, seam-safe polar helpers) — 116 green.
…), views land on zoomRoot Switching treemap ⇄ sunburst recreated the NSView, which presented frames while detached: drawables went to an uncomposited CAMetalLayer, the 3-deep pool drained, and nextDrawable() timed out (~1 s each) — the treemap came back black for ~10 s (until a data tick re-presented), the sunburst stayed black for good (only the CG overlay drew: hole, hover outlines). - Both NSViews: never present while window == nil; re-present on attach (viewDidMoveToWindow → setScale); renderers' draw() now returns whether a frame was actually presented, and a dropped frame schedules a one-shot retry so a map can never stay blank waiting for the next tick. - TreemapNSView: a view (re)created while zoomed (mode switch) fits the camera on zoomRoot instead of the whole world — both projections agree on where you are; SPEC-13 §5 updated with the lifecycle post-mortem. Verified live: toggle both ways now paints in < 0.4 s with full colours.
…el background - The hole becomes the wheel's readout (dotMemory's retained-size centre): when a subtree is selected it shows its relative path + on-disk size, and falls back to the display root otherwise. - Clicking the emptiness around the disc resets the selection, exactly like clicking the centre. - The wheel now floats on the app's panel colour (the treemap keeps its dedicated darker canvas — its tiles cover it entirely); the hole digs below it on the window colour, in both themes.
The hole (disc + name + size) was gated on morphT >= 1, so every data tick of a live scan — FSEvents reconciliation morphs at up to a few per second on a busy disk — hid the centre for the 0.32 s of the morph and brought it back: a permanent blink. The hole is static world geometry (arcs never enter it, its radius never animates): draw it whatever the morph clock says.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A second map mode, dotMemory-style: depth becomes concentric rings, size becomes angular extent, and the current zoom root becomes the hole in the middle — its relative path + on-disk size as the wheel's readout (the selection takes over the centre when one is active).
Both projections read the same
ScanControllerobservables (tree,version,zoomRoot, selection, search, type highlight): switching modes never re-scans and carries the whole navigation state over. The toggle lives in the map pane's breadcrumb bar, persisted via@AppStorage("mapMode").Full spec with design rationale and post-mortems:
docs/specs/SPEC-13-sunburst.md.How
SunburstWorld— the polar sibling ofTreemapWorld(SPEC-10): per-node children spans stored parent-relative as fractions, ε-stable local revalidation (sibling order holds while share drift ≤ 2 %), LOD on projected arc length with hysteresis. Rings decay geometrically (k = 0.8): an arbitrarily deep tree fits the finite disc, zooming in is what reveals outer generations. Sub-pixel sibling tails close their ring with an aggregate remainder arc (the polar underlay); the visibility walk tests the subtree sector through to the rim, since descendants extend radially outward.SunburstMetalRenderer— same discipline as SPEC-09/10 (one instanced draw, triple buffering, prev-buffer + morph uniform, camera-only frames write nothing): each arc is its bounding quad, the annular sector is carved per-pixel by a polar SDF in the fragment shader — which also anti-aliases the curved edges (no MSAA), draws the border, the radial cushion and the highlight/search dim. Blending on, no depth. During a morph the quad covers the union of both sectors, params lerp in the shader — re-roots sweep, live ticks breathe.SunburstNSView— isotropic letterboxed camera (circles stay circles; resize never re-bakes), explicit re-root on dive/pull-back (no camera-derived focus), free pan/zoom inspection on top. Interaction parity with the treemap: hover pill, click reveals (outside click resets, like the centre), double-click dives, right-click menu, sector spotlight, search/type dimming, live-scan teleport + post-scan morphs.MapChrome— hover pill, GPU-unavailable state, context menus and the VoiceOver summary extracted and shared by both views; identical colour semantics (hue = dominant type, brightness = relative weight) so the wheel matches the File-types legend.Fixes found while shipping it
window == nil, re-present on attach, and retry once on a dropped frame (draw()reports it). Post-mortem in SPEC-13 §5.zoomRootinstead of the whole world — both projections always agree on where you are.Tests & verification
SunburstWorldproperty tests: ring partition (contiguous, largest-first, share-exact), camera independence, ε order-keeping vs re-decide, re-root +worldSpancomposition, ring monotonicity/boundedness, LOD hysteresis, sub-pixel tail closure, seam-safe polar containment and bbox tightness.