Floating Studio panel for slicing a VoxelGrid — axis-selector, in-axis position slider, colormap picker, value- range + opacity controls, slice stats, and a live colourbar legend. Owns the lifetime of the slice SceneModel it builds; each parameter change rebakes the slice.

Per-grid singleton: opening the panel a second time for the same grid reveals the existing instance rather than constructing a new one. Use the static getFor / openFor factories for that behaviour.

Hierarchy (View Summary)

Constructors

Properties

_classPrefix: string
_closeBtn: HTMLButtonElement

Close button (clicked → hide(), surfaces the pill).

_container: HTMLElement
_destroyed: boolean = false
_header: HTMLElement

Drag handle inside the panel — usually the title row.

_minHeight: number
_minWidth: number
_modal: boolean
_panel: HTMLElement

Root panel element. Populated by subclass in _buildDom().

_pill: HTMLElement

Floating "reopen" pill shown while the panel is hidden.

_resizable: boolean
_storageKey: string
_tier: "default" | "view"
grid: VoxelGrid

The scalar field currently driving slice / iso techniques. Public — but mutate via setGrids so the panel can resync its derived state (value-range, slider bounds, etc).

onLayoutChanged: EventEmitter<FloatingPanelBase, void> = ...

Fires while the user drags the panel (one notification per pointermove during a drag) and once when the drag ends.

Drag changes the panel's CSS left / top but never its size, so a ResizeObserver on any descendant element never sees it. Subclasses or hosts that need to track the panel's viewport position — for example a View embedded in the body whose shared WebGL canvas must follow the panel — should subscribe to this and re-read the panel's bounding rect.

onVisibilityChanged: EventEmitter<FloatingPanelBase, boolean> = ...

Fires when show() / hide() toggles the panel's visibility.

scene: Scene
sliceModel: SceneModel = null

Heatmap SceneModel the panel currently shows, or null.

vectorGrid: VectorGrid

Optional vector field — null when streamlines aren't available.

Accessors

Methods

  • Inject eight invisible drag handles (4 edges + 4 corners) into _panel and wire pointer events. Each handle pins the panel to absolute left / top on pointerdown (so subsequent size + position writes take effect over any CSS that uses right / bottom), then writes new width / height — and, for north / west edges, new left / top — on pointermove. onLayoutChanged fires per move and at drag-end; the saved layout is updated on drag-end.

    min-width / min-height enforced from _minWidth / _minHeight; max-width / max-height are released by setting them to "none" inline so user-driven size is not clipped by CSS bounds like max-height: calc(100vh - 32px).

    Returns void

  • Engage the header drag from an external pointerdown / move event so the panel begins following ev immediately. Used by the drag-to-undock gesture on docked View cells — the cell's own pointer listener detects motion-past-threshold, replaces the cell with this freshly-created floating panel, and hands the live pointer over via this method.

    The panel is reflowed so the pointer sits inside its header at (panelOffsetX, panelOffsetY) from the panel's top-left. Then setPointerCapture routes all subsequent pointermove / pointerup events for this pointerId to the panel's own header, where _bindChrome's existing listeners drive the drag — no parallel drag loop required.

    Parameters

    • ev: PointerEvent
    • OptionalpanelOffsetX: number
    • OptionalpanelOffsetY: number

    Returns void

  • Swap the panel's data source. The first scalar array becomes the new grid; the first vector array (if any) becomes the new vectorGrid. The panel re-derives default value-range, slice-position bounds, isovalue range, etc., from the new field and re-bakes the active technique.

    The WeakMap singleton key is updated to the new grid so a subsequent panels.open("volumeOverlayPanel", {grid: ...}) call with the new grid reveals this panel rather than constructing a second one.

    Parameters

    Returns void