Skip to content

CellierController

The top-level entry point for building and driving a cellier scene.

cellier.CellierController

The main class for constructing and controlling a cellier visualization.

Wraps a ViewerModel (model layer) and a RenderManager (render layer) and performs the synchronization between both.

incoming_events property

incoming_events: EventBus

Incoming event bus for GUI-driven model mutations.

Emit AppearanceUpdateEvent, DimsUpdateEvent, AABBUpdateEvent or BackgroundUpdateEvent onto this bus to request model changes. The controller dispatches each event to the corresponding update_* method, preserving source_id end-to-end.

canvas_ids property

canvas_ids: tuple[UUID, ...]

IDs of every canvas registered with this controller, across scenes.

Scene-scoped callers want :meth:get_canvas_ids; this is for code that must find canvases without knowing which scene they belong to -- the Qt window composite in :func:~cellier.convenience.screenshot_window walks this to discover which canvases live inside a given window.

camera_reslice_enabled property writable

camera_reslice_enabled: bool

Whether camera movement triggers automatic reslicing.

While False the camera trackers still run and still emit CameraInteractionEvent; only the reslices at a motion's end and at a programmatic jump are skipped.

render_config property

render_config: RenderManagerConfig

Live rendering configuration.

Mutating a field here changes the model but not the GPU state, and notifies no widget. :meth:update_render_config_field and the dedicated properties (ambient_occlusion_power and friends) do all three in one step, and are what a GUI should drive.

render_manager property

render_manager: RenderManager

The render manager owning the canvases and the GPU-side state.

outline_enabled property writable

outline_enabled: bool

Whether the screen-space outline pass is active.

outline_boundaries_enabled property writable

outline_boundaries_enabled: bool

Whether the boundaries layer (every outlined region) draws.

outline_selection_enabled property writable

outline_selection_enabled: bool

Whether the selection layer (regions with a palette slot) draws.

ambient_occlusion_enabled property writable

ambient_occlusion_enabled: bool

Whether the screen-space ambient occlusion pass is active.

Ambient occlusion darkens creases by sampling the depth buffer, and is the cheapest shape cue available for cellier's default unlit isosurfaces. It runs in 3D only.

ambient_occlusion_radius property writable

ambient_occlusion_radius: float | None

Occlusion hemisphere radius in scene units, or None for auto.

None derives the radius from the scene bounding box diagonal (:attr:ambient_occlusion_auto_radius_fraction, 2 percent by default), which is the only default that means anything across cellier's coordinate systems. :attr:ambient_occlusion_effective_radius reports what that came to.

ambient_occlusion_auto_radius_fraction property writable

ambient_occlusion_auto_radius_fraction: float

Fraction of the scene bounding box diagonal used when radius is auto.

ambient_occlusion_effective_radius property

ambient_occlusion_effective_radius: float | None

The occlusion radius actually in use, in scene units.

The explicit :attr:ambient_occlusion_radius when one is set, otherwise the auto-derived value. Read-only, and the number worth showing next to the radius control: a radius means nothing until it can be compared with the scale of the thing being rendered. None when there is no canvas to ask.

ambient_occlusion_strength property writable

ambient_occlusion_strength: float

How far the occlusion is applied, 0 (off) to 1 (full).

ambient_occlusion_power property writable

ambient_occlusion_power: float

Contrast exponent applied to the occlusion before the multiply.

ambient_occlusion_bias property writable

ambient_occlusion_bias: float

Depth-comparison bias, as a fraction of the effective radius.

Dimensionless on purpose: an absolute bias tuned for one coordinate system self-occludes a flat plane in another.

ambient_occlusion_n_samples property writable

ambient_occlusion_n_samples: int

Hemisphere samples per pixel. Changing this recompiles the shader.

ambient_occlusion_blur_radius property writable

ambient_occlusion_blur_radius: int

Occlusion box-blur half-width in internal pixels. Recompiles.

temporal_enabled property writable

temporal_enabled: bool

Whether the temporal accumulation pass is active.

The pass averages successive jittered frames, which is what lets the volume raymarcher and the occlusion kernel use few samples per frame and still settle to a clean image when the camera stops. It is off in 2D whatever this says.

temporal_blend_weight property writable

temporal_blend_weight: float

Minimum EMA blend weight for the current frame, in (0, 1].

Lower values give a smoother settled image and take longer to get there after a camera move.

camera_settle_threshold_s property writable

camera_settle_threshold_s: float

Stillness, in seconds, after which a camera motion ends.

CameraConfig.settle_threshold_s: the camera tracker's stillness time. A motion driven by the camera controller normally ends sooner, when the controller stops driving the camera.

set_widget_parent

set_widget_parent(parent: object) -> None

Set the Qt parent for subsequently created canvas widgets.

Only meaningful when self._gui == "qt"; the anywidget gui ignores the parent (notebook canvases are not laid out by a Qt parent).

from_model classmethod

from_model(model: ViewerModel, widget_parent: QWidget | None = None, render_config: RenderManagerConfig | None = None) -> CellierController

Construct a controller from a serialized ViewerModel.

Iteratively adds all data stores, scenes, visuals, and canvases through the public API. The order is:

  1. Data stores — registered before visuals reference them.
  2. Scenes — registered with render_modes and lighting from model.
  3. Visuals — added per scene; data stores must already be present.
  4. Canvases — restored with camera state from model.

Parameters:

Name Type Description Default
model ViewerModel

A ViewerModel loaded from disk or constructed programmatically.

required
widget_parent QWidget or None

Qt parent for canvas widgets. Defaults to None.

None
render_config RenderManagerConfig or None

Render pipeline configuration. Defaults to None (uses defaults).

None

Returns:

Type Description
CellierController

from_file classmethod

from_file(path: str | Path, widget_parent: QWidget | None = None, render_config: RenderManagerConfig | None = None) -> CellierController

Deserialize a ViewerModel from disk and construct a controller.

Parameters:

Name Type Description Default
path str or Path

Path to a JSON file previously written by to_file.

required
widget_parent QWidget or None

Qt parent for canvas widgets.

None
render_config RenderManagerConfig or None

Render pipeline configuration.

None

Returns:

Type Description
CellierController

to_file

to_file(path: str | Path) -> None

Serialize the current model state to a JSON file.

to_model

to_model() -> ViewerModel

Return a copy of the current model state.

add_scene_model

add_scene_model(scene: Scene) -> Scene

Register a pre-built Scene model with the controller.

Used by from_model to restore scenes from a serialized ViewerModel, and called internally by add_scene.

Parameters:

Name Type Description Default
scene Scene

Pre-built scene model.

required

Returns:

Type Description
Scene

The same object passed in.

add_scene

add_scene(*, name: str = 'scene', dim: Literal['2d', '3d'] = '3d', coordinate_system: WorldAxesLike | None = None, render_modes: set[Literal['2d', '3d']] | None = None, lighting: Literal['none', 'default'] = 'none', background: BackgroundAppearance | None = None) -> Scene

Create a Scene from keyword arguments and register it.

Parameters:

Name Type Description Default
name str

Human-readable scene name.

'scene'
dim '2d' or '3d'

Initial display dimensionality. "3d" sets displayed_axes to the last three axes of the coordinate system; "2d" sets it to the last two.

'3d'
coordinate_system WorldAxesLike or None

The scene's world axes: a WorldCoordinateSystem, or a sequence of Axis objects and/or (name, axis_type) pairs. Axis types are stated, never inferred -- spatial_axes("z", "y", "x") is the shorthand for an all-spatial world. Defaults to a 3-axis spatial ("z", "y", "x") world when None.

None
render_modes set or None

Which rendering modes visuals should support. Defaults to {"2d", "3d"}.

None
lighting 'none' or 'default'

Pass "default" to add ambient/directional lights (required for MeshPhongAppearance).

'none'
background BackgroundAppearance or None

Background appearance for the scene. None uses the model defaults (the cellier vertical gray gradient).

None

Returns:

Type Description
Scene

The newly created and registered Scene.

add_data_store

add_data_store(data_store: BaseDataStore) -> BaseDataStore

Register a data store and return it.

Parameters:

Name Type Description Default
data_store BaseDataStore

The store to register.

required

Returns:

Type Description
BaseDataStore

The same object passed in.

add_visual

add_visual(scene_id: UUID, visual_model: VisualType, data_store: BaseDataStore | None = None) -> VisualType

Register a pre-built visual model with a scene.

This is the canonical construction path used by from_model. All typed convenience methods (add_image, add_mesh, etc.) delegate to this method internally.

Parameters:

Name Type Description Default
scene_id UUID

ID of an existing scene.

required
visual_model VisualType

Pre-built visual model. Its data_store_id must already be registered via add_data_store, or data_store must be passed explicitly.

required
data_store BaseDataStore or None

If provided, register the store first (no-op if already present), then use it. If None, the store is looked up by visual_model.data_store_id; a KeyError is raised if not found.

None

Returns:

Type Description
VisualType

The same visual_model passed in.

Raises:

Type Description
KeyError

If data_store is None and visual_model.data_store_id is not registered.

TypeError

If the visual type is not recognized.

add_image

add_image(data: ImageMemoryStore, scene_id: UUID, appearance: InMemoryImageAppearance | None = None, name: str = 'image', *, single: InMemoryImageSingleAppearance | None = None, channel_axis: int | None = None, composite: bool = False, channels: dict[int, InMemoryImageChannelAppearance] | None = None, max_channels: int = 4, transform: AffineTransform | None = None, outline: VisualOutline | None = None, ambient_occlusion: bool | None = None, pick_write: bool = True, clipping_planes: Sequence[ClippingPlane] = (), render_planes: Sequence[RenderPlane] = ()) -> ImageVisual

Add an in-memory image visual to a scene.

One visual draws the image single-channel or composited (unified image design 3.1): composite picks the mode, single is the appearance single mode draws with, and channels the per-channel appearances composite mode draws with.

Parameters:

Name Type Description Default
data ImageMemoryStore

The backing data store.

required
scene_id UUID

ID of an existing scene.

required
appearance InMemoryImageAppearance or None

Shared by both modes. None uses the defaults.

None
name str

Human-readable label. Default "image".

'image'
single InMemoryImageSingleAppearance or None

Single mode's appearance. None uses the defaults.

None
channel_axis int or None

The data axis a composite draws channels along. It must map to a world axis. None (default) gives an image with no channels.

None
composite bool

Start in composite mode. Requires channel_axis.

False
channels dict[int, InMemoryImageChannelAppearance] or None

Composite mode's per-channel appearances. None is none.

None
max_channels int

The most channels the visual may hold. Default 4.

4
transform AffineTransform or None

The data -> world transform. None (default) is the identity between the store's level-0 system and the world.

None
outline VisualOutline or None

Screen-space outline assignment. None (default) leaves the visual unoutlined.

None
ambient_occlusion bool or None

Whether this visual receives ambient occlusion. None (default) is automatic.

None
pick_write bool

Whether the visual writes to the pick buffer. Default True.

True
clipping_planes Sequence[ClippingPlane]

Clipping planes, in the store's level-0 data coordinates: the visual is drawn only on the kept side of every enabled plane. Build them from data.data_coordinate_systems[0]. They can be changed later by assigning visual.clipping_planes. Default none.

()
render_planes Sequence[RenderPlane]

The planes the visual draws its data on in a 3D view while its render mode is "plane", in the scene's world space; at most four. Build them from the scene's world coordinate system (RenderPlane.from_point_normal). They can be changed later with :meth:set_render_planes. Default none.

()

Returns:

Type Description
ImageVisual

Raises:

Type Description
ValueError

If composite is set without channel_axis, if channel_axis maps to no world axis, if the composited axis is displayed, or if channels has more than max_channels entries.

add_labels

add_labels(data: LabelMemoryStore, scene_id: UUID, appearance: BaseLabelsAppearance | None = None, name: str = 'labels', transform: AffineTransform | None = None, outline: VisualOutline | None = None, ambient_occlusion: bool | None = None, pick_write: bool = True, outline_selected_labels: dict[int, int] | None = None, outline_mode: OutlineMode = 'per_label', clipping_planes: Sequence[ClippingPlane] = (), render_planes: Sequence[RenderPlane] = ()) -> LabelMemoryVisual

Add an in-memory label visual to a scene.

Parameters:

Name Type Description Default
data LabelMemoryStore

Backing int32 label store.

required
scene_id UUID

ID of an existing scene.

required
appearance BaseLabelsAppearance or None

Appearance parameters. Defaults to InMemoryLabelsAppearance().

None
name str

Human-readable label. Default "labels".

'labels'
transform AffineTransform or None

Data-to-world transform. Defaults to identity when None.

None
outline VisualOutline or None

Screen-space outline assignment. None (default) leaves the visual unoutlined. Requires the outline pass to be enabled; see :attr:outline_enabled.

None
ambient_occlusion bool or None

Whether this visual receives ambient occlusion. None (default) is automatic: excluded while it renders in a MIP-family mode, included otherwise.

None
pick_write bool

Whether the visual writes to the pick buffer. True (default) makes it pickable. False keeps it out -- say, a volume drawn over outlined visuals, whose pick ids would otherwise erase their outlines. Outlines and ambient-occlusion exclusions are both derived from the pick buffer, so turning it off stops them on this visual; asking for an outline as well turns it back on, with a warning.

True
outline_selected_labels dict[int, int] or None

Maps a label value to the palette slot the selection layer draws it in. None (default) selects no label, so an outlined labels visual shows boundaries only.

None
outline_mode ('per_label', 'whole_object', 'all_boundaries')

How the labels are outlined. "per_label" (default) outlines the label values in outline_selected_labels, each in its own slot's colour. "whole_object" outlines the volume as one silhouette and "all_boundaries" every label's boundary, both in the colour of the outline slot.

"per_label"
clipping_planes Sequence[ClippingPlane]

Clipping planes, in the store's level-0 data coordinates: the visual is drawn only on the kept side of every enabled plane. Build them from data.data_coordinate_systems[0]. They can be changed later by assigning visual.clipping_planes. Default none.

()
render_planes Sequence[RenderPlane]

The planes the visual draws its data on in a 3D view while its render mode is "plane", in the scene's world space; at most four. Build them from the scene's world coordinate system (RenderPlane.from_point_normal). They can be changed later with :meth:set_render_planes. Default none.

()

Returns:

Type Description
LabelMemoryVisual

add_mesh

add_mesh(data: MeshMemoryStore, scene_id: UUID, appearance: MeshAppearance, name: str = 'mesh', transform: AffineTransform | None = None, outline: VisualOutline | None = None, ambient_occlusion: bool | None = None, pick_write: bool = True, section: MeshSectionConfig | None = None, clipping_planes: Sequence[ClippingPlane] = ()) -> MeshVisual

Add a mesh visual to a scene.

Parameters:

Name Type Description Default
data MeshMemoryStore

In-memory mesh. Normals are auto-computed if not supplied; indices are coerced to int32.

required
scene_id UUID

ID of an existing scene.

required
appearance MeshFlatAppearance | MeshPhongAppearance

Appearance. Use MeshPhongAppearance with lighting="default" on the scene for shaded rendering.

required
name str

Human-readable label. Default "mesh".

'mesh'
transform AffineTransform or None

Data-to-world transform for this visual. Defaults to identity when None.

None
outline VisualOutline or None

Screen-space outline assignment. None (default) leaves the visual unoutlined. Requires the outline pass to be enabled; see :attr:outline_enabled.

None
ambient_occlusion bool or None

Whether this visual receives ambient occlusion. None (default) is automatic: excluded while it renders in a MIP-family mode, included otherwise.

None
pick_write bool

Whether the visual writes to the pick buffer. True (default) makes it pickable. False keeps it out -- say, a volume drawn over outlined visuals, whose pick ids would otherwise erase their outlines. Outlines and ambient-occlusion exclusions are both derived from the pick buffer, so turning it off stops them on this visual; asking for an outline as well turns it back on, with a warning.

True
section MeshSectionConfig or None

How the mesh is drawn in a 2D view: the outline and fill of its cross-section, and whether the cut is the slice plane (mode="cut") or the scene's slab (mode="slab"). None (default) is an outline and a fill of the cut.

None
clipping_planes Sequence[ClippingPlane]

Clipping planes, in the store's level-0 data coordinates: the visual is drawn only on the kept side of every enabled plane. Build them from data.data_coordinate_systems[0]. They can be changed later by assigning visual.clipping_planes. Default none.

()

Returns:

Type Description
MeshVisual

add_multiscale_mesh

add_multiscale_mesh(data: MultiscaleMeshStore, scene_id: UUID, appearance: MeshAppearance, name: str = 'mesh', transform: AffineTransform | None = None, outline: VisualOutline | None = None, ambient_occlusion: bool | None = None, pick_write: bool = True, section: MeshSectionConfig | None = None, lod: GeometryLodConfig | None = None, clipping_planes: Sequence[ClippingPlane] = ()) -> MultiscaleMeshVisual

Add a mesh with levels of detail to a scene.

Two levels are kept loaded: the finest, and one coarse level. When the position changes the coarse level is read first, so the mesh is back on screen sooner, and the finest replaces it when it has loaded. A pick reports which level was drawn (MeshPickInfo.level, 0 the finest). The bounding box and a camera fit use the finest level's extent.

Parameters:

Name Type Description Default
data MultiscaleMeshStore

The mesh's levels, finest first. A store of one level loads as a plain mesh.

required
scene_id UUID

ID of an existing scene.

required
appearance MeshFlatAppearance | MeshPhongAppearance

Appearance, shared by both levels. Use MeshPhongAppearance with lighting="default" on the scene for shaded rendering.

required
name str

Human-readable label. Default "mesh".

'mesh'
transform AffineTransform or None

Data-to-world transform for this visual. Defaults to identity when None.

None
outline VisualOutline or None

Screen-space outline assignment; see :meth:add_mesh.

None
ambient_occlusion bool or None

Whether this visual receives ambient occlusion; see :meth:add_mesh.

None
pick_write bool

Whether the visual writes to the pick buffer; see :meth:add_mesh.

True
section MeshSectionConfig or None

How the mesh is drawn in a 2D view; see :meth:add_mesh.

None
lod GeometryLodConfig or None

Which coarse level is kept (coarse_level, 1-based; the coarsest by default), and when it is loaded and drawn.

None
clipping_planes Sequence[ClippingPlane]

Clipping planes, in the store's level-0 data coordinates: the visual is drawn only on the kept side of every enabled plane. Build them from data.data_coordinate_systems[0]. They can be changed later by assigning visual.clipping_planes. Default none.

()

Returns:

Type Description
MultiscaleMeshVisual

Raises:

Type Description
ValueError

If lod.coarse_level names a level the store does not have.

add_points

add_points(data: PointsMemoryStore, scene_id: UUID, appearance: PointsMarkerAppearance | None = None, name: str = 'points', transform: AffineTransform | None = None, outline: VisualOutline | None = None, ambient_occlusion: bool | None = None, pick_write: bool = True, clipping_planes: Sequence[ClippingPlane] = ()) -> PointsVisual

Add a points visual backed by a PointsMemoryStore.

Parameters:

Name Type Description Default
data PointsMemoryStore

The backing data store.

required
scene_id UUID

ID of the target scene.

required
appearance PointsMarkerAppearance or None

Appearance model. Defaults to PointsMarkerAppearance() if None.

None
name str

Human-readable label for the visual.

'points'
transform AffineTransform or None

Data-to-world transform for this visual. Defaults to identity when None.

None
outline VisualOutline or None

Screen-space outline assignment. None (default) leaves the visual unoutlined. Requires the outline pass to be enabled; see :attr:outline_enabled.

None
ambient_occlusion bool or None

Whether this visual receives ambient occlusion. None (default) is automatic: excluded while it renders in a MIP-family mode, included otherwise.

None
pick_write bool

Whether the visual writes to the pick buffer. True (default) makes it pickable. False keeps it out -- say, a volume drawn over outlined visuals, whose pick ids would otherwise erase their outlines. Outlines and ambient-occlusion exclusions are both derived from the pick buffer, so turning it off stops them on this visual; asking for an outline as well turns it back on, with a warning.

True
clipping_planes Sequence[ClippingPlane]

Clipping planes, in the store's level-0 data coordinates: the visual is drawn only on the kept side of every enabled plane. Build them from data.data_coordinate_systems[0]. They can be changed later by assigning visual.clipping_planes. Default none.

()

Returns:

Type Description
PointsVisual

add_lines

add_lines(data: LinesMemoryStore, scene_id: UUID, appearance: LinesMemoryAppearance | None = None, name: str = 'lines', transform: AffineTransform | None = None, outline: VisualOutline | None = None, ambient_occlusion: bool | None = None, pick_write: bool = True, clipping_planes: Sequence[ClippingPlane] = ()) -> LinesVisual

Add a lines visual backed by a LinesMemoryStore.

Parameters:

Name Type Description Default
data LinesMemoryStore

The backing data store.

required
scene_id UUID

ID of the target scene.

required
appearance LinesMemoryAppearance or None

Appearance model. Defaults to LinesMemoryAppearance() if None.

None
name str

Human-readable label for the visual.

'lines'
transform AffineTransform or None

Data-to-world transform for this visual. Defaults to identity when None.

None
outline VisualOutline or None

Screen-space outline assignment. None (default) leaves the visual unoutlined. Requires the outline pass to be enabled; see :attr:outline_enabled.

None
ambient_occlusion bool or None

Whether this visual receives ambient occlusion. None (default) is automatic: excluded while it renders in a MIP-family mode, included otherwise.

None
pick_write bool

Whether the visual writes to the pick buffer. True (default) makes it pickable. False keeps it out -- say, a volume drawn over outlined visuals, whose pick ids would otherwise erase their outlines. Outlines and ambient-occlusion exclusions are both derived from the pick buffer, so turning it off stops them on this visual; asking for an outline as well turns it back on, with a warning.

True
clipping_planes Sequence[ClippingPlane]

Clipping planes, in the store's level-0 data coordinates: the visual is drawn only on the kept side of every enabled plane. Build them from data.data_coordinate_systems[0]. They can be changed later by assigning visual.clipping_planes. Default none.

()

Returns:

Type Description
LinesVisual

add_graph

add_graph(data: GraphMemoryStore, scene_id: UUID, appearance: GraphAppearance | None = None, name: str = 'graph', transform: AffineTransform | None = None, trail: dict[int, TrailConfig] | None = None, outline: VisualOutline | None = None, ambient_occlusion: bool | None = None, pick_write: bool = True, clipping_planes: Sequence[ClippingPlane] = ()) -> GraphVisual

Add a spatial-graph visual backed by a GraphMemoryStore.

Parameters:

Name Type Description Default
data GraphMemoryStore

The backing data store.

required
scene_id UUID

ID of the target scene.

required
appearance GraphAppearance or None

Appearance model. Defaults to GraphAppearance() if None.

None
name str

Human-readable label for the visual.

'graph'
transform AffineTransform or None

Data-to-world transform for this visual. When None, the store's own transform is used if it has one -- a geff file's per-axis scale / offset (D23) -- and identity otherwise. An explicit argument always wins, as it does for every other visual: D23 constrains construction, not composition.

None
trail dict[int, TrailConfig] or None

Axis index -> window configuration. Keys are validated against the store's ndim; an out-of-range axis raises ValueError (D21).

None
outline VisualOutline or None

Screen-space outline assignment. None (default) leaves the visual unoutlined. Requires the outline pass to be enabled; see :attr:outline_enabled.

None
ambient_occlusion bool or None

Whether this visual receives ambient occlusion. None (default) is automatic: excluded while it renders in a MIP-family mode, included otherwise.

None
pick_write bool

Whether the visual writes to the pick buffer. True (default) makes it pickable. False keeps it out -- say, a volume drawn over outlined visuals, whose pick ids would otherwise erase their outlines. Outlines and ambient-occlusion exclusions are both derived from the pick buffer, so turning it off stops them on this visual; asking for an outline as well turns it back on, with a warning.

True
clipping_planes Sequence[ClippingPlane]

Clipping planes, in the store's level-0 data coordinates: the visual is drawn only on the kept side of every enabled plane. Build them from data.data_coordinate_systems[0]. They can be changed later by assigning visual.clipping_planes. Default none.

()

Returns:

Type Description
GraphVisual

Raises:

Type Description
ValueError

If any trail key is not a valid axis index for data.

add_image_multiscale

add_image_multiscale(data: BaseDataStore, scene_id: UUID, appearance: MultiscaleImageAppearance | None = None, name: str = 'image', render_config: MultiscaleImageRenderConfig | None = None, transform: AffineTransform | None = None, *, single: MultiscaleImageSingleAppearance | None = None, channel_axis: int | None = None, composite: bool = False, channels: dict[int, MultiscaleImageChannelAppearance] | None = None, max_channels: int = 4, outline: VisualOutline | None = None, ambient_occlusion: bool | None = None, pick_write: bool = True, clipping_planes: Sequence[ClippingPlane] = (), render_planes: Sequence[RenderPlane] = ()) -> MultiscaleImageVisual

Add a multiscale image visual to a scene.

The multiscale twin of :meth:add_image; see it for the two modes.

Parameters:

Name Type Description Default
data BaseDataStore

The backing multiscale data store.

required
scene_id UUID

ID of an existing scene.

required
appearance MultiscaleImageAppearance or None

Shared by both modes, including the LOD settings. None uses the defaults.

None
name str

Human-readable label. Default "image".

'image'
render_config MultiscaleImageRenderConfig or None

GPU cache configuration. The budget is split evenly between the visual's slots: one without a channel axis, max_channels with.

None
transform AffineTransform or None

Data-to-world transform. Defaults to identity when None.

None
single MultiscaleImageSingleAppearance or None

Single mode's appearance.

None
channel_axis int or None

The data axis a composite draws channels along.

None
composite bool

Start in composite mode. Requires channel_axis.

False
channels dict[int, MultiscaleImageChannelAppearance] or None

Composite mode's per-channel appearances.

None
max_channels int

The most channels the visual may hold. Default 4.

4
outline VisualOutline or None

Screen-space outline assignment.

None
ambient_occlusion bool or None

Whether this visual receives ambient occlusion.

None
pick_write bool

Whether the visual writes to the pick buffer. Default True.

True
clipping_planes Sequence[ClippingPlane]

Clipping planes, in the store's level-0 data coordinates: the visual is drawn only on the kept side of every enabled plane. Build them from data.data_coordinate_systems[0]. They can be changed later by assigning visual.clipping_planes. Default none.

()
render_planes Sequence[RenderPlane]

The planes the visual draws its data on in a 3D view while its render mode is "plane", in the scene's world space; at most four. Build them from the scene's world coordinate system (RenderPlane.from_point_normal). They can be changed later with :meth:set_render_planes. Default none.

()

Returns:

Type Description
MultiscaleImageVisual

Raises:

Type Description
ValueError

As :meth:add_image.

add_labels_multiscale

add_labels_multiscale(data: BaseDataStore, scene_id: UUID, appearance: MultiscaleLabelsAppearance, name: str = 'labels', render_config: MultiscaleLabelRenderConfig | None = None, transform: AffineTransform | None = None, outline: VisualOutline | None = None, ambient_occlusion: bool | None = None, pick_write: bool = True, outline_selected_labels: dict[int, int] | None = None, outline_mode: OutlineMode = 'per_label', clipping_planes: Sequence[ClippingPlane] = (), render_planes: Sequence[RenderPlane] = ()) -> MultiscaleLabelVisual

Add a multiscale label visual to a scene.

Parameters:

Name Type Description Default
data BaseDataStore

The backing label data store (e.g. OMEZarrLabelDataStore).

required
scene_id UUID

ID of an existing scene.

required
appearance MultiscaleLabelsAppearance

Visual appearance parameters.

required
name str

Human-readable label. Default "labels".

'labels'
render_config MultiscaleLabelRenderConfig or None

Render-layer configuration. Defaults to MultiscaleLabelRenderConfig() with all default values if None.

None
transform AffineTransform or None

Data-to-world transform. Defaults to identity when None.

None
outline VisualOutline or None

Screen-space outline assignment. None (default) leaves the visual unoutlined. Requires the outline pass to be enabled; see :attr:outline_enabled.

None
ambient_occlusion bool or None

Whether this visual receives ambient occlusion. None (default) is automatic: excluded while it renders in a MIP-family mode, included otherwise.

None
pick_write bool

Whether the visual writes to the pick buffer. True (default) makes it pickable. False keeps it out -- say, a volume drawn over outlined visuals, whose pick ids would otherwise erase their outlines. Outlines and ambient-occlusion exclusions are both derived from the pick buffer, so turning it off stops them on this visual; asking for an outline as well turns it back on, with a warning.

True
outline_selected_labels dict[int, int] or None

Maps a label value to the palette slot the selection layer draws it in. None (default) selects no label, so an outlined labels visual shows boundaries only.

None
outline_mode ('per_label', 'whole_object', 'all_boundaries')

How the labels are outlined. "per_label" (default) outlines the label values in outline_selected_labels, each in its own slot's colour. "whole_object" outlines the volume as one silhouette and "all_boundaries" every label's boundary, both in the colour of the outline slot.

"per_label"
clipping_planes Sequence[ClippingPlane]

Clipping planes, in the store's level-0 data coordinates: the visual is drawn only on the kept side of every enabled plane. Build them from data.data_coordinate_systems[0]. They can be changed later by assigning visual.clipping_planes. Default none.

()
render_planes Sequence[RenderPlane]

The planes the visual draws its data on in a 3D view while its render mode is "plane", in the scene's world space; at most four. Build them from the scene's world coordinate system (RenderPlane.from_point_normal). They can be changed later with :meth:set_render_planes. Default none.

()

Returns:

Type Description
MultiscaleLabelVisual

add_channel

add_channel(visual_id: UUID, channel_index: int, appearance: InMemoryImageChannelAppearance | MultiscaleImageChannelAppearance) -> None

Add a channel to an image visual's composite channels.

The channels bridge reslices the visual, so a composite draws the new channel straight away.

Parameters:

Name Type Description Default
visual_id UUID

ID of an ImageVisual or MultiscaleImageVisual.

required
channel_index int

Index along the visual's channel_axis. Must not already be present.

required
appearance channel appearance

The channel's appearance, of the visual's own family.

required

Raises:

Type Description
ValueError

If the visual has no channel_axis, channel_index is already present, or the visual already holds max_channels channels.

remove_channel

remove_channel(visual_id: UUID, channel_index: int) -> None

Remove a channel from an image visual's composite channels.

Any channel may be removed, including the last (D35); an empty composite draws nothing. The channels bridge reslices.

Parameters:

Name Type Description Default
visual_id UUID

ID of an ImageVisual or MultiscaleImageVisual.

required
channel_index int

Index of the channel to remove.

required

Raises:

Type Description
KeyError

If channel_index is not in visual.channels.

add_canvas_overlay

add_canvas_overlay(canvas_id: UUID, overlay: CanvasOverlay) -> CanvasOverlay

Attach a screen-space overlay to a specific canvas.

The overlay is rendered as a post-pass on top of the main scene each frame. It does not participate in reslicing, has no world-space transform, and is not added to scene.visuals. It is stored in the Canvas.overlays list of canvas_id, making it part of the serializable model, and its fields are live: assign to them directly or through :meth:update_overlay_field.

Parameters:

Name Type Description Default
canvas_id UUID

ID of the canvas that should display the overlay. Use :meth:get_canvas_ids to look up canvas IDs for a scene.

required
overlay CanvasOverlay

Model-layer overlay description, e.g. a :class:~cellier.visuals.CenteredAxes2D.

required

Returns:

Type Description
CanvasOverlay

The same overlay object passed in (for ID access or chaining).

Raises:

Type Description
KeyError

If canvas_id is not registered.

ValueError

If an overlay with the same id is already registered.

add_scene_overlay

add_scene_overlay(scene_id: UUID, overlay: SceneOverlay) -> SceneOverlay

Attach a world-space overlay to a scene.

The overlay is drawn in the scene's world by the scene camera, in the main pass, on every canvas showing the scene. Its geometry follows the scene: it is rebuilt when visuals are added or removed, a transform is replaced, the displayed axes change, or a store changes (signalled by :meth:reslice_visual). It is stored in Scene.overlays, and its fields are live.

Parameters:

Name Type Description Default
scene_id UUID

ID of the scene that should hold the overlay.

required
overlay SceneOverlay

Model-layer overlay description, e.g. a :class:~cellier.visuals.SceneBoundingBox.

required

Returns:

Type Description
SceneOverlay

The same overlay object passed in.

Raises:

Type Description
KeyError

If scene_id is not registered.

ValueError

If an overlay with the same id is already registered.

NotImplementedError

If the scene's selection is not axis aligned.

get_overlay

get_overlay(overlay_id: UUID) -> CanvasOverlay | SceneOverlay

Return the overlay model registered under overlay_id.

Raises:

Type Description
KeyError

If no overlay with that id is registered.

remove_overlay

remove_overlay(overlay_id: UUID) -> None

Remove an overlay of either category.

Disconnects its bridge, detaches it from the render layer, and removes it from Canvas.overlays / Scene.overlays.

Parameters:

Name Type Description Default
overlay_id UUID

ID of the overlay to remove.

required

Raises:

Type Description
KeyError

If no overlay with that id is registered.

update_overlay_field

update_overlay_field(overlay_id: UUID, field: str, value: Any, *, source_id: UUID | None = None) -> None

Set one field on an overlay model.

Tags the emitted OverlayChangedEvent with source_id. GUI widgets should pass source_id=self._id so their own subscription can ignore the echo.

Parameters:

Name Type Description Default
overlay_id UUID

Target overlay, of either category.

required
field str

Dotted path on the overlay model: "visible", or "appearance.color" for an appearance field.

required
value Any

New value for the field.

required
source_id UUID or None

UUID to stamp on the emitted event. Defaults to the controller's own ID.

None

Raises:

Type Description
KeyError

If no overlay with that id is registered.

set_overlay_visible

set_overlay_visible(overlay_id: UUID, visible: bool, *, source_id: UUID | None = None) -> None

Show or hide an overlay of either category.

Equivalent to update_overlay_field(overlay_id, "visible", visible).

Parameters:

Name Type Description Default
overlay_id UUID

ID of the overlay.

required
visible bool

True to show the overlay, False to hide it.

required
source_id UUID or None

UUID to stamp on the emitted OverlayChangedEvent.

None

Raises:

Type Description
KeyError

If no overlay with overlay_id is registered.

add_canvas

add_canvas(scene_id: UUID, render_modes: set[str] | None = None, initial_dim: str | None = None, fov: float = 70.0, depth_range_3d: tuple[float, float] = (1.0, 8000.0), depth_range_2d: tuple[float, float] = (-500.0, 500.0), canvas_size: tuple[int, int] | None = None) -> QWidget

Create a canvas attached to a scene and return its embeddable widget.

Parameters:

Name Type Description Default
scene_id UUID

ID of an existing scene.

required
render_modes set[str] or None

Which camera modes to prepare on the canvas. Each entry must be "2d" or "3d". When None, defaults to the scene's own render_modes. Pass {"2d", "3d"} for a canvas that can switch between views.

None
initial_dim str or None

Which mode is active when the canvas first appears. Must be a member of render_modes. When None, inferred from the scene's current displayed_axes length (3 axes -> "3d", otherwise "2d").

None
fov float

Vertical field of view in degrees for the 3D perspective camera. Ignored when "3d" is not in render_modes. Default 70.0.

70.0
depth_range_3d tuple[float, float]

(near, far) clip distances for the 3D perspective camera. Default (1.0, 8000.0).

(1.0, 8000.0)
depth_range_2d tuple[float, float]

(near, far) clip distances for the 2D orthographic camera. Default (-500.0, 500.0).

(-500.0, 500.0)
canvas_size tuple[int, int] or None

Initial CSS pixel size for the anywidget canvas. Ignored for the Qt gui (which is sized by its parent layout). Defaults to (600, 600) when None for the anywidget gui.

None

Returns:

Type Description
QWidget

The render widget. Embed with layout.addWidget(widget).

Raises:

Type Description
ValueError

If initial_dim is supplied but is not a member of render_modes.

add_canvas_model

add_canvas_model(scene_id: UUID, canvas_model: Canvas, initial_dim: str | None = None, canvas_size: tuple[int, int] | None = None) -> QWidget

Register a pre-built Canvas model with a scene.

Used by from_model to restore canvases from a serialized ViewerModel, and called internally by add_canvas. Camera state (position, rotation, fov, depth range) is read from the camera models stored in canvas_model.cameras.

Parameters:

Name Type Description Default
scene_id UUID

ID of an existing scene.

required
canvas_model Canvas

Pre-built canvas model. Must have at least one entry in canvas_model.cameras.

required
initial_dim str or None

Which dim to activate first. When None, the first key in canvas_model.cameras is used (insertion order is preserved by Python dicts, so this is deterministic for serialized models).

None
canvas_size tuple[int, int] or None

Initial CSS pixel size for the anywidget canvas. Ignored for the Qt gui. Defaults to (600, 600) for the anywidget gui.

None

Returns:

Type Description
QWidget

The render widget.

Raises:

Type Description
ValueError

If canvas_model.cameras is empty, or if initial_dim is not a key in canvas_model.cameras.

get_scene

get_scene(scene_id: UUID) -> Scene

Return the live Scene model for scene_id.

get_visual_scene_id

get_visual_scene_id(visual_id: UUID) -> UUID

Return the id of the scene visual_id belongs to.

Parameters:

Name Type Description Default
visual_id UUID

ID of a visual added to one of the controller's scenes.

required

Returns:

Type Description
UUID

Raises:

Type Description
KeyError

If no visual with visual_id has been added.

get_data_store

get_data_store(store_id: UUID) -> BaseDataStore

Return the registered data store for store_id.

Parameters:

Name Type Description Default
store_id UUID

ID of a previously registered data store.

required

Returns:

Type Description
BaseDataStore

Raises:

Type Description
KeyError

If no store with store_id has been registered.

fit_camera

fit_camera(scene_id: UUID, canvas_id: UUID | None = None, *, interactive: bool = False) -> None

Fit the camera to the current scene bounding box.

Safe to call immediately after add_image / add_image_multiscale and transform assignment — the node matrix is set at construction time so no chunk data needs to be loaded first.

The move is a jump: camera-sensitive visuals (multiscale image and labels) reslice at once for the fitted view, and a camera motion in progress on the canvas ends. A fit that leaves the camera where it is does nothing. See :meth:set_camera_state for interactive.

Parameters:

Name Type Description Default
scene_id UUID

ID of the scene whose camera should be fitted.

required
canvas_id UUID or None

If provided, fit only that canvas. When None (default), all canvases attached to scene_id are fitted.

None
interactive bool

Whether the move is a tick of a camera motion instead of a jump.

False

get_scene_by_name

get_scene_by_name(name: str) -> Scene

Return the live Scene model for the given name.

Raises KeyError if no scene with that name exists.

get_canvas_ids

get_canvas_ids(scene_id: UUID) -> list[UUID]

Return the IDs of all canvases registered for scene_id.

Parameters:

Name Type Description Default
scene_id UUID

ID of an existing scene.

required

Returns:

Type Description
list[UUID]

Canvas IDs in registration order. Empty if no canvases have been added yet.

get_canvas_view

get_canvas_view(canvas_id: UUID) -> CanvasView

Return the render-layer CanvasView for canvas_id.

Provides access to the rendering backend (camera, widget, overlays) for a canvas registered via :meth:add_canvas or :meth:add_canvas_model.

Parameters:

Name Type Description Default
canvas_id UUID

ID of a registered canvas.

required

Returns:

Type Description
CanvasView

Raises:

Type Description
KeyError

If canvas_id is not registered.

get_camera_state

get_camera_state(canvas_id: UUID) -> CameraState

Return a snapshot of the current camera state for canvas_id.

Useful for seeding downstream widgets (e.g. orientation overlays) with the post-fit camera state without constructing a synthetic CameraChangedEvent.

Parameters:

Name Type Description Default
canvas_id UUID

ID of a registered canvas.

required

Returns:

Type Description
CameraState

Raises:

Type Description
KeyError

If canvas_id is not registered.

screenshot

screenshot(canvas_id: UUID, size: tuple[int, int] | None = None, scale: float = 1.0, *, frames: int | Literal['converged'] = 1, **capture_kwargs) -> ndarray

Capture a reproducible screenshot as an RGBA uint8 array.

The frame is rendered on a dedicated offscreen canvas built on the same scene, not read back from the canvas on screen. Two captures of the same viewer state therefore produce byte-identical arrays (on the same machine and GPU driver), at exactly the size asked for, whether or not anything is on screen and whichever GUI toolkit is in use.

canvas_id selects a viewpoint, not a surface: the capture copies that canvas's camera, dimensionality and depth range, then renders its own frame. Use :meth:screenshot_scene to capture a scene that has no canvas at all.

What the capture shows is the data currently resident on the GPU. It does not reslice, so a multiscale scene is captured at the level of detail already loaded; a higher scale enlarges that level rather than fetching a finer one. Call :meth:on_scene_ready (or use the convenience launchers' on_ready) before capturing if a load may still be in flight.

Parameters:

Name Type Description Default
canvas_id UUID

ID of a registered canvas, whose viewpoint the capture copies.

required
size tuple[int, int] or None

Target (width, height) in pixels before scale. Defaults to the canvas's physical size, so an unqualified call reproduces the on-screen framing.

None
scale float

Multiplier applied to size. scale=2 doubles the output resolution.

1.0
frames int or 'converged'

1 (default) disables temporal accumulation and draws a single frame. "converged" enables it and draws the number of frames the accumulator needs to settle (44 at the default blend weight) -- what you want whenever ambient occlusion is enabled, since a single-sample AO frame is visibly noisy. N draws exactly N accumulated frames.

1
**capture_kwargs

Forwarded to the capture helper (max_frames, residual).

{}

Returns:

Type Description
ndarray

RGBA uint8 array of shape (height, width, 4).

Raises:

Type Description
KeyError

If canvas_id is not registered.

RuntimeError

If frames="converged" would need more than max_frames frames to settle.

screenshot_scene

screenshot_scene(scene_id: UUID, size: tuple[int, int] | None = None, scale: float = 1.0, *, frames: int | Literal['converged'] = 1, dim: str | None = None, **capture_kwargs) -> ndarray

Capture scene_id without needing a canvas to exist.

The same offscreen capture as :meth:screenshot, but with the camera fitted to the scene rather than copied from a canvas -- so a scene can be captured with no window, no widget and no event loop.

A scene with no canvas has no data. Slice requests are planned per canvas, from its camera, size and frustum, so reslice_all on a scene with no canvas requests nothing and this returns a correct picture of an empty scene. Add a canvas (add_canvas) and let the reslice complete before capturing; cellier.convenience.capture does exactly that. This method's own fit is for the case where a canvas exists but its viewpoint is not the one you want.

Parameters:

Name Type Description Default
scene_id UUID

ID of the scene to render.

required
size tuple[int, int] or None

Target (width, height) before scale. Defaults to (600, 600).

None
scale float

Multiplier applied to size.

1.0
frames int or 'converged'

See :meth:screenshot.

1
dim str or None

"2d" or "3d". Inferred from the scene's displayed axes when None.

None
**capture_kwargs

Forwarded to the capture helper.

{}

Returns:

Type Description
ndarray

RGBA uint8 array of shape (height, width, 4).

get_visual_model

get_visual_model(visual_id: UUID) -> MultiscaleImageVisual

Return the live visual model for visual_id.

Searches all scenes. Raises KeyError if not found.

coordinate_system

coordinate_system(system_id: UUID) -> CoordinateSystemType

Return the coordinate system with system_id.

Three transform methods -- map_bounding_box, then and validate_against -- take coordinate system objects while a transform stores only their ids, so composing anything needs this lookup. It is a plain dict: no edges, no path finding and no automatic composition (D15).

Parameters:

Name Type Description Default
system_id UUID

The system's id.

required

Returns:

Type Description
CoordinateSystemType

The registered system.

Raises:

Type Description
KeyError

If no system with that id is registered. A stored transform naming an unregistered system usually means it outlived the scene or store that owned its endpoint.

render_spaces

render_spaces(visual_id: UUID) -> RenderSpaces | None

The systems a visual's render-layer counterpart places geometry with.

None when the visual is not placeable yet -- its store has no coordinate systems, or the scene has no canvas and so no rendered system.

visuals_outlined_beyond

visuals_outlined_beyond(n_slots: int) -> list[tuple[str, int]]

Return (name, slot) for visuals outlined past n_slots.

The slots a palette of n_slots entries cannot colour. Useful to a GUI before it shrinks the palette, and to the palette route after.

slot_usage

slot_usage() -> dict[int, int]

Return {slot: how many visuals use it}, for slots 1 and up.

What lets a palette editor show that slot 2 is three visuals rather than leaving the user to hold it in their head.

update_visual_render_field

update_visual_render_field(visual_id: UUID, field: str, value: Any, *, source_id: UUID | None = None) -> None

Set one screen-space render field on a visual.

The seam a GUI drives, and the twin of :meth:update_render_config_field for the per-visual half. Writes the model; the psygnal bridge does the rest.

Parameters:

Name Type Description Default
visual_id UUID

Target visual.

required
field str

"outline.slot", "outline.placement", "ambient_occlusion" or "outline_selected_labels".

required
value Any

New value for the field.

required
source_id UUID | None

UUID to stamp on the emitted VisualRenderChangedEvent. GUI widgets should pass source_id=self._id so their own subscription can ignore the echo.

None

Raises:

Type Description
ValueError

If field is not a settable per-visual render field.

set_loading_config

set_loading_config(visual_id: UUID, *, source_id: UUID | None = None, **fields: Any) -> ProgressiveLoadingConfig

Change how a multiscale visual loads, while it is shown.

Merges fields into the visual's current render_config.loading and applies the result at once: the visual replans with the new settings and keeps what it has loaded. Emits LoadingConfigChangedEvent. Nothing happens when the merged config equals the current one.

Parameters:

Name Type Description Default
visual_id UUID

A multiscale image or labels visual.

required
source_id UUID | None

UUID to stamp on the emitted LoadingConfigChangedEvent. GUI widgets pass source_id=self._id so their own subscription can ignore the echo.

None
**fields Any

ProgressiveLoadingConfig fields: backstop_level, backstop_extent, backstop_max_slot_fraction.

{}

Returns:

Type Description
ProgressiveLoadingConfig

The visual's config after the call.

Raises:

Type Description
TypeError

If the visual is not a multiscale image or labels visual.

ValueError

If a field name is unknown, or the merged config is invalid (e.g. backstop_level=0). The visual is left unchanged; nothing is corrected.

update_visual_trail

update_visual_trail(visual_id: UUID, axis: int, config: TrailConfig | None, *, source_id: UUID | None = None) -> None

Set or clear the trail window on one axis of a graph visual.

The seam a GUI drives. An axis that already has a window is edited in place, one field event per field that differs, so nudging one spin box reslices once rather than rebuilding the whole trail. Adding or removing an axis replaces visual.trail, which is what rewires the per-config handlers (see :meth:_wire_trail).

Parameters:

Name Type Description Default
visual_id UUID

Target graph visual.

required
axis int

The data-axis index the window is keyed by.

required
config TrailConfig | None

The complete window for axis, or None to remove it. When the axis has no window yet the object itself is adopted, so pass one no other visual holds.

required
source_id UUID | None

UUID to stamp on the emitted TrailChangedEvent. GUI widgets should pass source_id=self._id so their own subscription can ignore the echo.

None

Raises:

Type Description
TypeError

If the visual is not a graph visual.

ValueError

If axis is out of range for the graph's store.

reslice_all

reslice_all() -> None

Trigger a data load for all visuals across all scenes.

reslice_scene

reslice_scene(scene_id: UUID, *, on_ready: Callable[[], None] | None = None, owner_id: UUID | None = None) -> None

Trigger a data load for all visuals in one scene.

Parameters:

Name Type Description Default
scene_id UUID

ID of the scene to reslice.

required
on_ready Callable[[], None] or None

If provided, a zero-argument callback fired exactly once after all visuals loaded by this reslice have committed to the GPU (across every canvas attached to the scene). Visuals with no data in the current view (culled, empty, or hidden) do not delay it. Works uniformly for in-memory, multiscale, multichannel, and geometry visuals. See :meth:on_scene_ready.

The callback tracks this reslice generation. If a superseding reslice (e.g. a camera-motion reload) cancels these in-flight reads before they commit, the cancelled visual never reports completion and the callback may not fire. Callers that need a guaranteed startup signal should suppress camera-driven reslicing during the load (the convenience launchers do this automatically).

None
owner_id UUID or None

Owner under which the temporary on_ready subscriptions are registered (for teardown). Defaults to the controller's own id.

None

reslice_visual

reslice_visual(visual_id: UUID) -> None

Trigger a data load for one visual.

Not needed after changing a store: stores announce their own changes (reassigning a data field, or store.notify_changed) and the controller reslices every visual reading them.

suppress_reslice

suppress_reslice() -> Generator[None, None, None]

Context manager that blocks reslice_scene inside transform handlers.

Use this when updating a visual's transform without needing to reload its underlying data — for example, repositioning a static-geometry mesh by translation only.

.. warning:: _suppress_reslice is a flat boolean. Nested calls or concurrent async tasks that mutate transforms inside overlapping suppress_reslice blocks will interfere. Replace with a depth counter if that becomes necessary.

data_to_world

data_to_world(scene_id: UUID, data_store: Any, scale: Sequence[float] | None = None, translation: Sequence[float] | None = None) -> AffineTransform

Build a data -> world transform from a per-axis scale and offset.

A transform names the two coordinate systems it maps between, and only the viewer knows both: the store's level-0 system and the scene's world. This is the short way to say "this dataset is 4 um in z and sits 10 um along it" without assembling an axis map by hand.

Before Phase 8 the same thing was said with a bare v1 AffineTransform, which stated the numbers and named nothing; add_* accepted one and attached the endpoints itself. That went with v1, and this replaces it.

Parameters:

Name Type Description Default
scene_id UUID

The scene whose world the transform maps into.

required
data_store Any

The store whose voxel space it maps from. Given its coordinate systems if it does not have them.

required
scale Sequence[float] or None

Per-data-axis scale. None is all ones.

None
translation Sequence[float] or None

Per-data-axis offset, in world units. None is all zeros.

None

Returns:

Type Description
AffineTransform

Ready to hand to any add_* or to :meth:set_visual_transform.

Raises:

Type Description
ValueError

If the store and the world disagree about how many axes they have: the correspondence here is positional, so there is nothing to infer. Use AffineTransform.from_axis_map to state it.

set_visual_transform

set_visual_transform(visual_id: UUID, transform: AffineTransform, *, reslice: bool = True) -> None

Update the data-to-world transform of a visual.

Assigns transform to the live visual model, which fires its psygnal field event and propagates the change to the render layer via the event bus.

Parameters:

Name Type Description Default
visual_id UUID

ID of the visual to update.

required
transform AffineTransform

New data-to-world transform.

required
reslice bool

If True (default), a full reslice is triggered after the transform is applied — required for image visuals where the transform changes which data falls in the current slab. Pass False for static-geometry visuals (mesh, points, lines) where the transform only repositions the node and the underlying data is unchanged.

True

begin_dims_interaction

begin_dims_interaction(scene_id: UUID, *, source_id: UUID) -> None

Open a dims interaction scope on a scene.

While any scope is open, every slice-position change on the scene is a scrub tick, whatever its interactive flag: visuals that opted in plan coarse, and plan in full when the scrub ends. Opening a scope starts nothing by itself; the first tick does. Holding still for SchedulerConfig.dims_settle_s still ends a scrub, and the next tick starts a new one.

A slider calls this when it is pressed. Scripts normally use :meth:dims_interaction.

Parameters:

Name Type Description Default
scene_id UUID

The scene whose dims are about to be scrubbed.

required
source_id UUID

Who holds the scope. One scope is counted per source.

required

Raises:

Type Description
KeyError

If scene_id is not registered.

end_dims_interaction

end_dims_interaction(scene_id: UUID, *, source_id: UUID) -> None

Close a dims interaction scope opened by source_id.

Closing the last open scope ends a scrub at once ("release"): the visuals that planned coarse plan in full without waiting for the stillness time. Closing a scope that is not open does nothing.

Parameters:

Name Type Description Default
scene_id UUID

The scene the scope was opened on.

required
source_id UUID

The source that opened it.

required

dims_interaction

dims_interaction(scene_id: UUID) -> Generator[None, None, None]

Scrub a scene's dims for the length of a with block.

Every :meth:update_slice_indices inside the block is a scrub tick, so a player or a scripted sweep loads coarse while it runs and in full when the block exits::

with controller.dims_interaction(scene_id):
    for t in frames:
        controller.update_slice_indices(scene_id, {0: t})

Parameters:

Name Type Description Default
scene_id UUID

The scene to scrub.

required

dims_interaction_state

dims_interaction_state(scene_id: UUID) -> Literal['idle', 'active']

Whether scene_id's dims are being scrubbed.

Parameters:

Name Type Description Default
scene_id UUID

The scene to query.

required

Returns:

Type Description
{'idle', 'active'}

update_slice_indices

update_slice_indices(scene_id: UUID, slice_indices: Mapping[int, float], *, source_id: UUID | None = None, interactive: bool = False) -> None

Move the slice position of one or more world axes on a scene.

Merges into the scene's positions: axes absent from slice_indices keep theirs. Every world axis has a position whether or not it is displayed (D36), so this never adds or removes one.

Tags the emitted bus event with source_id. GUI widgets should pass source_id=self._id so their own DimsChangedEvent subscription can ignore the echo.

By default the move is a jump: every visual plans in full at once, and a scrub in progress on the scene ends. With interactive=True, or inside an open scope (:meth:dims_interaction), it is a tick of a scrub: visuals that opted in (coarsest_while_moving_3d or _2d) plan coarse, and plan in full when the scrub ends, on release or after SchedulerConfig.dims_settle_s of stillness. A call that moves no sliced axis is neither.

Parameters:

Name Type Description Default
scene_id UUID

Target scene.

required
slice_indices Mapping[int, float]

Mapping of world axis index -> world slice position.

required
source_id UUID | None

UUID to stamp on the emitted DimsChangedEvent. Defaults to the controller's own ID.

None
interactive bool

Whether the move is a tick of a scrub. Sliders pass True.

False

Raises:

Type Description
ValueError

If a key is not an axis of the scene's world. Nothing is changed.

update_thickness

update_thickness(scene_id: UUID, thickness: Mapping[int, float], *, source_id: UUID | None = None) -> None

Replace a scene's per-axis half-thicknesses.

Parameters:

Name Type Description Default
scene_id UUID

Target scene.

required
thickness Mapping[int, float]

World axis index -> half-thickness in world units. An axis absent from the mapping slices a plane.

required
source_id UUID | None

UUID to stamp on the emitted DimsChangedEvent.

None

Raises:

Type Description
ValueError

If a key is not an axis of the scene's world.

set_slider_override

set_slider_override(scene_id: UUID, axis: int, value: bool | None, *, source_id: UUID | None = None) -> None

Force a world axis's slider shown or hidden, or return it to automatic.

Parameters:

Name Type Description Default
scene_id UUID

Target scene.

required
axis int

World axis index.

required
value bool | None

True force-shows the slider, False force-hides it, and None removes the override so the visuals decide.

required
source_id UUID | None

UUID to stamp on the SliderAxesChangedEvent this may emit.

None

Raises:

Type Description
ValueError

If axis is not an axis of the scene's world.

update_appearance_field

update_appearance_field(visual_id: UUID, field: str, value: Any, *, source_id: UUID | None = None) -> None

Set one field on a visual's appearance model.

Tags the emitted bus event with source_id. GUI widgets should pass source_id=self._id so their own AppearanceChangedEvent subscription can ignore the echo.

Parameters:

Name Type Description Default
visual_id UUID

Target visual.

required
field str

Attribute name on the appearance model, e.g. "clim".

required
value Any

New value for the field.

required
source_id UUID | None

UUID to stamp on the emitted AppearanceChangedEvent. Defaults to the controller's own ID.

None

update_background_field

update_background_field(scene_id: UUID, field: str, value: Any, *, source_id: UUID | None = None) -> None

Set one field on a scene's background appearance model.

Tags the emitted bus event with source_id. GUI widgets should pass source_id=self._id so their own BackgroundChangedEvent subscription can ignore the echo.

Parameters:

Name Type Description Default
scene_id UUID

Target scene.

required
field str

Attribute name on the background model, e.g. "top_color".

required
value Any

New value for the field.

required
source_id UUID | None

UUID to stamp on the emitted BackgroundChangedEvent. Defaults to the controller's own ID.

None

set_background

set_background(scene_id: UUID, background: BackgroundAppearance, *, source_id: UUID | None = None) -> None

Replace a scene's background appearance model wholesale.

Emits a single BackgroundChangedEvent with field_name=None. Assigning scene.background directly does the same thing; this method exists to stamp a source_id on the resulting event.

Parameters:

Name Type Description Default
scene_id UUID

Target scene.

required
background BackgroundAppearance

The background appearance to apply.

required
source_id UUID | None

UUID to stamp on the emitted BackgroundChangedEvent.

None

update_appearance_group_field

update_appearance_group_field(visual_ids: list[UUID], field: str, value: Any, *, source_id: UUID | None = None) -> None

Set one appearance field across a group of visuals in lock-step.

Fan-out over :meth:update_appearance_field so every visual in the group -- the per-panel visuals of an OrthoViewer, say -- receives the same change. This is the programmatic write-side companion to the widget subscribe-to-all read side, matching :meth:update_channel_group_field.

Parameters:

Name Type Description Default
visual_ids list[UUID]

Target visuals, kept equal.

required
field str

Attribute name on each appearance model, e.g. "clim".

required
value Any

New value for the field.

required
source_id UUID | None

UUID to stamp on each emitted event. Defaults to the controller's own ID.

None

update_aabb_group_field

update_aabb_group_field(visual_ids: list[UUID], field: str, value: Any, *, source_id: UUID | None = None) -> None

Set one AABB field across a group of visuals in lock-step.

The AABB is not an appearance field: it lives on visual.aabb and travels on AABBChangedEvent, so it needs its own group helper rather than riding on :meth:update_appearance_group_field.

Parameters:

Name Type Description Default
visual_ids list[UUID]

Target visuals, kept equal.

required
field str

Attribute name on each AABB model, e.g. "enabled".

required
value Any

New value for the field.

required
source_id UUID | None

UUID to stamp on each emitted AABBChangedEvent. Defaults to the controller's own ID.

None

update_channel_appearance_field

update_channel_appearance_field(visual_id: UUID, channel_index: int, field: str, value: Any, *, source_id: UUID | None = None) -> None

Set one field on one channel of an image visual.

Tags the emitted bus event with source_id. GUI widgets should pass source_id=self._id so their own ChannelAppearanceChangedEvent subscription can ignore the echo.

This mutates a single visual only. When a channel is shared in lock-step across several panels (e.g. an OrthoViewer), calling this on one panel's visual leaves the sibling panels unequal until they are written too; use update_channel_group_field to keep the group in lock-step.

A pydantic.ValidationError from a malformed value is allowed to propagate (matching update_appearance_field).

render_mode is the exception to "one channel": every channel of an image has the same render mode, so setting it here sets it on all of them, as :meth:set_image_render_mode does. source_id is stamped on this channel's event only; the other channels' events carry the controller's id, so the caller's echo filter lets them through.

Parameters:

Name Type Description Default
visual_id UUID

Target visual.

required
channel_index int

Index into visual.channels selecting the channel appearance.

required
field str

Attribute name on the channel appearance model, e.g. "clim".

required
value Any

New value for the field.

required
source_id UUID | None

UUID to stamp on the emitted ChannelAppearanceChangedEvent. Defaults to the controller's own ID.

None

update_channel_group_field

update_channel_group_field(visual_ids: list[UUID], channel_index: int, field: str, value: Any, *, source_id: UUID | None = None) -> None

Set one channel field across a group of visuals in lock-step.

Fan-out over update_channel_appearance_field so every visual in the group (e.g. the per-panel visuals of an OrthoViewer) receives the same channel change. This is the programmatic write-side companion to the widget subscribe-to-all read side.

Parameters:

Name Type Description Default
visual_ids list[UUID]

Target visuals sharing the channel set.

required
channel_index int

Index into each visual's channels mapping.

required
field str

Attribute name on the channel appearance model.

required
value Any

New value for the field.

required
source_id UUID | None

UUID to stamp on each emitted ChannelAppearanceChangedEvent. Defaults to the controller's own ID.

None

update_single_appearance_field

update_single_appearance_field(visual_id: UUID, field: str, value: Any, *, source_id: UUID | None = None) -> None

Set one field on an image visual's single-mode appearance.

A pydantic.ValidationError from a malformed value propagates.

Parameters:

Name Type Description Default
visual_id UUID

Target image visual.

required
field str

Attribute name on visual.single, e.g. "clim".

required
value Any

New value for the field.

required
source_id UUID | None

UUID to stamp on the emitted SingleAppearanceChangedEvent.

None

update_single_group_field

update_single_group_field(visual_ids: list[UUID], field: str, value: Any, *, source_id: UUID | None = None) -> None

Set one single-mode field across a group of image visuals in lock-step.

Parameters:

Name Type Description Default
visual_ids list[UUID]

Target visuals, kept equal -- an OrthoViewer's panel siblings.

required
field str

Attribute name on each single model.

required
value Any

New value for the field.

required
source_id UUID | None

UUID to stamp on each emitted event.

None

set_image_render_mode

set_image_render_mode(visual_id: UUID, render_mode: str, *, source_id: UUID | None = None) -> None

Set the render mode of every channel of an image visual.

Every entry of visual.channels has the same render_mode, so the mode is set here, on all of them at once. Assigning it to one channel of several is refused. single.render_mode, which single mode draws with, is separate and is not touched.

One ChannelAppearanceChangedEvent is emitted per channel whose mode changed. Entering or leaving "plane" mode reslices the visual once.

Parameters:

Name Type Description Default
visual_id UUID

Target visual: an image visual.

required
render_mode str

The new mode, e.g. "mip" or "plane".

required
source_id UUID | None

UUID to stamp on the emitted events. Defaults to the controller's own ID.

None

Raises:

Type Description
TypeError

If the visual is not an image visual.

ValidationError

If render_mode is not a mode of the visual's channels. Nothing is changed.

set_image_composite

set_image_composite(visual_id: UUID, composite: bool, *, source_id: UUID | None = None) -> None

Switch an image visual between single and composite mode.

Validates, then assigns. The reslice, ImageCompositeChangedEvent and any SliderAxesChangedEvent come from the composite model bridge, so a direct visual.composite = ... gets them too -- but a direct assignment skips the checks below (design 3.4).

Parameters:

Name Type Description Default
visual_id UUID

Target image visual.

required
composite bool

True for composite mode.

required
source_id UUID | None

UUID to stamp on the emitted ImageCompositeChangedEvent.

None

Raises:

Type Description
ValueError

If composite is True and the visual has no channel_axis, or its channel axis is displayed. The model is unchanged.

set_image_composite_group

set_image_composite_group(visual_ids: list[UUID], composite: bool, *, source_id: UUID | None = None) -> None

Switch a group of image visuals' mode in lock-step.

Every visual is validated before any is changed, so the group is never left split between the two modes.

Parameters:

Name Type Description Default
visual_ids list[UUID]

Target visuals -- an OrthoViewer's panel siblings.

required
composite bool

True for composite mode.

required
source_id UUID | None

UUID to stamp on each emitted event.

None

Raises:

Type Description
ValueError

As :meth:set_image_composite, for any visual in the group.

update_section_field

update_section_field(visual_id: UUID, field: str, value: Any, *, source_id: UUID | None = None) -> None

Set one field of a mesh visual's 2D section config.

mode, outline and fill change what the mesh reads, so the mesh loads again and is not drawn until it has; outline_width applies at once.

Parameters:

Name Type Description Default
visual_id UUID

Target mesh visual.

required
field str

Attribute name on MeshSectionConfig: "mode", "outline", "fill" or "outline_width".

required
value Any

New value for the field.

required
source_id UUID | None

UUID to stamp on the emitted MeshSectionChangedEvent, so a GUI widget can ignore its own echo. Defaults to the controller's own ID.

None

Raises:

Type Description
TypeError

If the visual is not a mesh.

set_lod_config

set_lod_config(visual_id: UUID, *, source_id: UUID | None = None, **fields: Any) -> GeometryLodConfig

Change the level-of-detail settings of a multiscale mesh.

dims_drag_draw and camera_motion choose among the levels already loaded: they apply in the next frame, with nothing read. dims_drag decides what the next dims scrub loads. Emits LodConfigChangedEvent. Nothing happens when the merged config equals the current one.

Parameters:

Name Type Description Default
visual_id UUID

Target visual.

required
source_id UUID | None

UUID to stamp on the emitted LodConfigChangedEvent. GUI widgets pass source_id=self._id so their own subscription can ignore the echo.

None
**fields Any

GeometryLodConfig fields to replace: dims_drag, dims_drag_draw, camera_motion.

{}

Returns:

Type Description
GeometryLodConfig

The visual's new config.

Raises:

Type Description
TypeError

If the visual has no levels of detail.

ValueError

If a field is unknown or its value invalid, or if coarse_level is changed: which coarse level is kept is fixed when the visual is added.

set_clipping_planes

set_clipping_planes(visual_id: UUID, clipping_planes: Sequence[ClippingPlane], *, source_id: UUID | None = None) -> tuple[ClippingPlane, ...]

Replace a visual's clipping planes.

The same as assigning visual.clipping_planes, with the check made before anything changes and a source_id for echo filtering. Emits ClippingPlanesChangedEvent; nothing happens when the tuple equals the current one.

Parameters:

Name Type Description Default
visual_id UUID

Target visual.

required
clipping_planes Sequence[ClippingPlane]

The complete new sequence of ClippingPlane, in the visual's level-0 data coordinates. Empty removes them all.

required
source_id UUID | None

UUID to stamp on the emitted ClippingPlanesChangedEvent. GUI widgets pass source_id=self._id so their own subscription can ignore the echo.

None

Returns:

Type Description
tuple[ClippingPlane, ...]

The visual's new planes.

Raises:

Type Description
ValueError

If a plane is not in the visual's data coordinate system or has the wrong number of components.

get_clipping_plane

get_clipping_plane(visual_id: UUID, plane_id: UUID) -> ClippingPlane

Return one clipping plane of a visual, by the plane's id.

Raises:

Type Description
KeyError

If the visual has no plane with that id.

set_clipping_plane

set_clipping_plane(visual_id: UUID, plane_id: UUID, plane: Plane, *, source_id: UUID | None = None) -> ClippingPlane

Move one clipping plane of a visual; the others stay.

The plane keeps its id, its place in the tuple and its enabled flag. Goes through :meth:set_clipping_planes, so it emits ClippingPlanesChangedEvent and does nothing for an equal plane.

Parameters:

Name Type Description Default
visual_id UUID

Target visual.

required
plane_id UUID

The id of the ClippingPlane to move.

required
plane Plane

Its new plane, in the visual's level-0 data coordinates.

required
source_id UUID | None

UUID to stamp on the emitted ClippingPlanesChangedEvent.

None

Returns:

Type Description
ClippingPlane

The moved plane.

Raises:

Type Description
KeyError

If the visual has no plane with that id.

ValueError

If plane is not in the visual's data coordinate system or has the wrong number of components.

set_render_planes

set_render_planes(visual_id: UUID, render_planes: Sequence[RenderPlane], *, source_id: UUID | None = None) -> tuple[RenderPlane, ...]

Replace an image or labels visual's render planes.

The same as assigning visual.render_planes, with the check made before anything changes and a source_id for echo filtering. Emits RenderPlanesChangedEvent; nothing happens when the tuple equals the current one.

The planes are drawn only in a 3D view, and only while the visual's render mode is "plane"; otherwise the tuple is stored and announced and nothing else happens. Inside :meth:plane_interaction the change is a tick of a drag.

Parameters:

Name Type Description Default
visual_id UUID

Target visual: an image or labels visual.

required
render_planes Sequence[RenderPlane]

The complete new sequence of RenderPlane, in the scene's world space. At most four. Empty removes them all.

required
source_id UUID | None

UUID to stamp on the emitted RenderPlanesChangedEvent. GUI widgets pass source_id=self._id so their own subscription can ignore the echo.

None

Returns:

Type Description
tuple[RenderPlane, ...]

The visual's new planes.

Raises:

Type Description
TypeError

If the visual is not an image or labels visual.

ValueError

If there are more than four planes, two share an id, a plane is not on three axes of the scene's world coordinate system, or the visual's transform does not map a plane's axes by a scale and a translation per axis.

get_render_plane

get_render_plane(visual_id: UUID, plane_id: UUID) -> RenderPlane

Return one render plane of a visual, by the plane's id.

Raises:

Type Description
KeyError

If the visual has no render plane with that id.

visual_world_extent

visual_world_extent(visual_id: UUID) -> list[tuple[float, float]]

The box a visual's data occupies, per world axis of its scene.

Parameters:

Name Type Description Default
visual_id UUID

The visual.

required

Returns:

Type Description
list[tuple[float, float]]

(low, high) per world axis, in the world system's order. (nan, nan) on an axis the visual has no extent on (one it broadcasts over) and for a store with no data yet.

Raises:

Type Description
KeyError

If visual_id is not registered.

default_render_plane

default_render_plane(visual_id: UUID) -> RenderPlane

A new render plane for a visual: centred, unbounded.

The plane a control adds: through the centre of the visual's data box, on the three world axes the scene displays, facing the first of them, unbounded on every side.

Parameters:

Name Type Description Default
visual_id UUID

The visual the plane is for. It is not given the plane.

required

Returns:

Type Description
RenderPlane

Raises:

Type Description
KeyError

If visual_id is not registered.

ValueError

If the visual's scene does not display three axes.

render_planes_blocked

render_planes_blocked(visual_id: UUID) -> str

Why a visual's render planes are not drawn now, or "" if they are.

A sentence a control can show: the visual is not in the "plane" render mode, or its scene is shown in 2D (where a visual in plane mode shows its normal slice).

Parameters:

Name Type Description Default
visual_id UUID

The visual.

required

Returns:

Type Description
str

set_render_plane

set_render_plane(visual_id: UUID, plane_id: UUID, plane: RenderPlane, *, source_id: UUID | None = None) -> RenderPlane

Replace one render plane of a visual; the others stay.

The plane keeps its id and its place in the tuple; its pose, extents and enabled flag are taken from plane. Goes through :meth:set_render_planes, so it emits RenderPlanesChangedEvent and does nothing for an equal plane.

Parameters:

Name Type Description Default
visual_id UUID

Target visual.

required
plane_id UUID

The id of the RenderPlane to replace.

required
plane RenderPlane

Its new state. Its own id is ignored.

required
source_id UUID | None

UUID to stamp on the emitted RenderPlanesChangedEvent.

None

Returns:

Type Description
RenderPlane

The plane now in the tuple.

Raises:

Type Description
KeyError

If the visual has no render plane with that id.

ValueError

As :meth:set_render_planes.

begin_plane_interaction

begin_plane_interaction(visual_id: UUID, *, source_id: UUID) -> None

Open a plane interaction scope on a visual.

While any scope is open, every change of the visual's clipping_planes, or of its render_planes while a 3D view draws them, is a tick of one drag, announced by a PlaneInteractionEvent at its start and its end. Opening a scope starts nothing by itself; the first change does. Holding still for SchedulerConfig.dims_settle_s ends a drag, and the next change starts a new one.

A multiscale image or labels visual in a 3D view with appearance.coarsest_while_moving_3d on (the default) plans nothing during a drag: it keeps drawing the bricks it has, and the backstop where the planes reveal more. Its target is planned when the drag ends, once its scene's dims and cameras are still too. With the setting off, and for every other visual, a plane change inside a drag plans as it does outside one.

A plane gizmo calls this when a handle is grabbed. Scripts normally use :meth:plane_interaction.

Parameters:

Name Type Description Default
visual_id UUID

The visual whose planes are about to be dragged.

required
source_id UUID

Who holds the scope. One scope is counted per source.

required

Raises:

Type Description
KeyError

If visual_id is not registered.

end_plane_interaction

end_plane_interaction(visual_id: UUID, *, source_id: UUID) -> None

Close a plane interaction scope opened by source_id.

Closing the last open scope ends a drag at once ("release"). Closing a scope that is not open does nothing.

Parameters:

Name Type Description Default
visual_id UUID

The visual the scope was opened on.

required
source_id UUID

The source that opened it.

required

plane_interaction

plane_interaction(visual_id: UUID) -> Generator[None, None, None]

Drag a visual's planes for the length of a with block.

Every change of clipping_planes or render_planes inside the block belongs to one drag::

with controller.plane_interaction(visual.id):
    for offset in offsets:
        controller.set_clipping_plane(visual.id, plane_id, ...)

Parameters:

Name Type Description Default
visual_id UUID

The visual whose planes are dragged.

required

plane_interaction_state

plane_interaction_state(visual_id: UUID) -> Literal['idle', 'active']

Whether visual_id's clipping or render planes are being dragged.

Parameters:

Name Type Description Default
visual_id UUID

The visual to query.

required

Returns:

Type Description
{'idle', 'active'}

add_clipping_plane_gizmo

add_clipping_plane_gizmo(visual_id: UUID, canvas_id: UUID, plane_id: UUID, *, source_id: UUID | None = None, screen_size: float = 100.0) -> ClippingPlaneGizmoController

Put a gizmo on one clipping plane of a visual, in a 3D canvas.

Dragging the gizmo moves and tilts the plane; a plane changed from anywhere else moves the gizmo. A canvas has one gizmo at a time: the one it had is closed. The gizmo closes itself when its plane or its visual is removed, when the canvas leaves 3D, and when the plane gains a component on an axis the view does not show.

The canvas's one gizmo serves both kinds of plane: a gizmo on a render plane (:meth:add_render_plane_gizmo) is closed too.

Emits PlaneGizmoChangedEvent with kind="clipping".

Parameters:

Name Type Description Default
visual_id UUID

The visual the plane belongs to.

required
canvas_id UUID

A 3D canvas of the visual's scene.

required
plane_id UUID

The id of the ClippingPlane to edit. The plane may be disabled.

required
source_id UUID or None

Stamped on the emitted PlaneGizmoChangedEvent.

None
screen_size float

The gizmo's size on screen, in logical pixels.

100.0

Returns:

Type Description
ClippingPlaneGizmoController

The session. close() ends it.

Raises:

Type Description
KeyError

If the canvas or the visual is not registered, or the visual has no plane with that id.

ValueError

If the canvas is in 2D, the visual is not in the canvas's scene, or the plane has a component on an axis the view does not show. The canvas keeps the gizmo it had.

add_render_plane_gizmo

add_render_plane_gizmo(visual_id: UUID, canvas_id: UUID, plane_id: UUID, *, source_id: UUID | None = None, screen_size: float = 100.0) -> RenderPlaneGizmoController

Put a gizmo on one render plane of a visual, in a 3D canvas.

The gizmo carries the plane's whole pose. Dragging a translate handle moves the plane's origin, a rotation ring turns its in-plane axes, and a scale handle on an in-plane axis multiplies that axis's extent about the origin (an axis with an unbounded side has no scale handle). A plane changed from anywhere else moves the gizmo. Each drag is one plane interaction on the visual.

A canvas has one gizmo at a time, whatever the kind of plane: the one it had is closed. The gizmo closes itself when its plane or its visual is removed, when the canvas leaves 3D, when the visual leaves the "plane" render mode, and when the plane's axes stop being the displayed ones.

Emits PlaneGizmoChangedEvent with kind="render".

Parameters:

Name Type Description Default
visual_id UUID

The visual the plane belongs to. It must be in the "plane" render mode.

required
canvas_id UUID

A 3D canvas of the visual's scene.

required
plane_id UUID

The id of the RenderPlane to edit. The plane may be disabled.

required
source_id UUID or None

Stamped on the emitted PlaneGizmoChangedEvent.

None
screen_size float

The gizmo's size on screen, in logical pixels.

100.0

Returns:

Type Description
RenderPlaneGizmoController

The session. close() ends it.

Raises:

Type Description
KeyError

If the canvas or the visual is not registered, or the visual has no render plane with that id.

ValueError

If the canvas is in 2D, the visual is not in the canvas's scene or not in the "plane" render mode, or the plane's axes are not the ones the view displays. The canvas keeps the gizmo it had.

clipping_plane_gizmo_blocked

clipping_plane_gizmo_blocked(visual_id: UUID, canvas_id: UUID, plane_id: UUID) -> str

Why a plane cannot have a gizmo in a canvas now, or "" if it can.

What :meth:add_clipping_plane_gizmo would refuse, as a sentence a control can show: the canvas is in 2D, or the plane has a component on an axis the 3D view does not show.

Parameters:

Name Type Description Default
visual_id UUID

The visual the plane belongs to.

required
canvas_id UUID

The canvas the gizmo would be drawn in.

required
plane_id UUID

The id of the ClippingPlane.

required

Returns:

Type Description
str

render_plane_gizmo_blocked

render_plane_gizmo_blocked(visual_id: UUID, canvas_id: UUID, plane_id: UUID) -> str

Why a render plane cannot have a gizmo in a canvas now, or "".

What :meth:add_render_plane_gizmo would refuse, as a sentence a control can show: the canvas is in 2D, the visual is not in the "plane" render mode, or the plane's axes are not the displayed ones.

Parameters:

Name Type Description Default
visual_id UUID

The visual the plane belongs to.

required
canvas_id UUID

The canvas the gizmo would be drawn in.

required
plane_id UUID

The id of the RenderPlane.

required

Returns:

Type Description
str

get_plane_gizmo

get_plane_gizmo(canvas_id: UUID) -> PlaneGizmoController | None

The plane gizmo of a canvas, or None if it has none.

Its kind is "clipping" or "render".

remove_plane_gizmo

remove_plane_gizmo(canvas_id: UUID, *, source_id: UUID | None = None) -> None

Close a canvas's plane gizmo, of either kind; nothing if it has none.

Emits PlaneGizmoChangedEvent stamped with source_id.

update_aabb_field

update_aabb_field(visual_id: UUID, field: str, value: Any, *, source_id: UUID | None = None) -> None

Set one field on a visual's AABB params model.

Tags the emitted bus event with source_id. GUI widgets should pass source_id=self._id so their own AABBChangedEvent subscription can ignore the echo.

Parameters:

Name Type Description Default
visual_id UUID

Target visual.

required
field str

Attribute name on the AABB model, e.g. "enabled".

required
value Any

New value for the field.

required
source_id UUID | None

UUID to stamp on the emitted AABBChangedEvent. Defaults to the controller's own ID.

None

update_displayed_axes

update_displayed_axes(scene_id: UUID, displayed_axes: tuple[int, ...], *, source_id: UUID | None = None) -> None

Set displayed_axes on a scene's dims.

Tags the emitted bus event with source_id. GUI widgets should pass source_id=self._id so their own DimsChangedEvent subscription can ignore the echo.

Parameters:

Name Type Description Default
scene_id UUID

Target scene.

required
displayed_axes tuple[int, ...]

Tuple of axis indices to display; length 2 for 2D, 3 for 3D.

required
source_id UUID | None

UUID to stamp on the emitted DimsChangedEvent. Defaults to the controller's own ID.

None

set_displayed_axes

set_displayed_axes(scene_id: UUID, displayed_axes: tuple[int, ...], *, source_id: UUID | None = None) -> None

Set displayed axes on a scene's dims (preferred public API).

Equivalent to :meth:update_displayed_axes. The controller's psygnal bridge fires _rebuild_visuals_geometry and _switch_canvas_cameras automatically when the model field changes.

Parameters:

Name Type Description Default
scene_id UUID

Target scene.

required
displayed_axes tuple[int, ...]

Tuple of axis indices to display; length 2 for 2D, 3 for 3D.

required
source_id UUID | None

UUID stamped on the emitted DimsChangedEvent.

None

update_render_config_field

update_render_config_field(section: str, field: str, value: Any, *, source_id: UUID | None = None) -> None

Set one field of the render configuration and apply it.

The single seam every render-config write goes through: it updates the model, pushes the change to the GPU by whichever route that field needs, and emits a RenderConfigChangedEvent so subscribed widgets follow along. Which fields recompile a shader and which are plain uniforms is a property of the field, recorded once in :data:_RENDER_CONFIG_ROUTES, so no caller has to know.

Parameters:

Name Type Description Default
section str

"outline", "ambient_occlusion" or "temporal".

required
field str

Dotted attribute path within the section, e.g. "power" or "selection.inward_thickness".

required
value Any

New value for the field.

required
source_id UUID | None

UUID to stamp on the emitted RenderConfigChangedEvent. GUI widgets should pass source_id=self._id so their own subscription can ignore the echo. Defaults to the controller's own ID.

None

Raises:

Type Description
ValueError

If section or field is not a settable render-config field.

reset_temporal_accumulation

reset_temporal_accumulation() -> None

Discard the accumulated history on every canvas.

The next frame is shown raw and accumulation restarts from it. Cellier already does this on every camera and content change; this is for a caller who has changed something cellier cannot see.

apply_ambient_occlusion_config

apply_ambient_occlusion_config() -> None

Push render_config.ambient_occlusion onto every canvas's occlusion pass.

Needed after mutating the config model in place. n_samples and blur_radius are shader template vars, so changing them recompiles; the rest are uniforms and do not.

apply_outline_config

apply_outline_config() -> None

Push render_config.outline onto every canvas's outline pass.

Call this after mutating thicknesses or colours in place. Changing a thickness recompiles the outline shader; enables, colours and the palette do not.

begin_camera_interaction

begin_camera_interaction(canvas_id: UUID, *, source_id: UUID) -> None

Open a camera interaction scope on a canvas.

While any scope is open, every camera move on the canvas is a tick of a motion, programmatic ones included: nothing is resliced until the motion ends. Opening a scope starts nothing by itself; the first move does. Holding still for CameraConfig.settle_threshold_s still ends a motion, and the next move starts a new one.

The canvas's own camera controller holds a scope while it drives the camera. Scripts normally use :meth:camera_interaction.

Parameters:

Name Type Description Default
canvas_id UUID

The canvas whose camera is about to move.

required
source_id UUID

Who holds the scope. One scope is counted per source.

required

Raises:

Type Description
KeyError

If canvas_id is not registered.

end_camera_interaction

end_camera_interaction(canvas_id: UUID, *, source_id: UUID) -> None

Close a camera interaction scope opened by source_id.

Closing the last open scope ends a motion at once ("release"): the scene's camera-sensitive visuals reslice without waiting for the stillness time. Closing a scope that is not open does nothing.

Parameters:

Name Type Description Default
canvas_id UUID

The canvas the scope was opened on.

required
source_id UUID

The source that opened it.

required

camera_interaction

camera_interaction(canvas_id: UUID) -> Generator[None, None, None]

Move a canvas's camera as one motion for the length of a with block.

Every programmatic move inside the block is a tick of a motion, so a fly-through reslices once, when the block exits, instead of at every pose::

with controller.camera_interaction(canvas_id):
    for pose in path:
        controller.set_camera_state(canvas_id, pose)

Parameters:

Name Type Description Default
canvas_id UUID

The canvas whose camera moves.

required

camera_interaction_state

camera_interaction_state(canvas_id: UUID) -> Literal['idle', 'active']

Whether canvas_id's camera is in motion.

Parameters:

Name Type Description Default
canvas_id UUID

The canvas to query.

required

Returns:

Type Description
{'idle', 'active'}

set_camera_state

set_camera_state(canvas_id: UUID, state: CameraState, *, interactive: bool = False) -> None

Move a canvas's camera to state.

By default the move is a jump: the scene's camera-sensitive visuals (multiscale image and labels) reslice at once, and a motion in progress on the canvas ends. With interactive=True, or inside :meth:camera_interaction, it is a tick of a motion: nothing is resliced until the motion ends, on release or after CameraConfig.settle_threshold_s of stillness. A state equal to the camera's current one does nothing.

Parameters:

Name Type Description Default
canvas_id UUID

ID of a registered canvas.

required
state CameraState

The state to apply; :meth:get_camera_state returns one. Its camera_type must match the canvas's active camera.

required
interactive bool

Whether the move is a tick of a motion.

False

Raises:

Type Description
KeyError

If canvas_id is not registered.

ValueError

If state is for the other kind of camera.

look_at_visual

look_at_visual(visual_id: UUID, canvas_id: UUID, view_direction: tuple[float, float, float] = (-1, -1, -1), up: tuple[float, float, float] = (0, 0, 1), *, interactive: bool = False) -> None

Fit the camera to a visual's bounding box.

A jump, like :meth:fit_camera: camera-sensitive visuals reslice at once for the new view.

Parameters:

Name Type Description Default
visual_id UUID

ID of the target visual.

required
canvas_id UUID

ID of the canvas whose camera should be fitted.

required
view_direction tuple[float, float, float]

Camera look direction vector (need not be normalized).

(-1, -1, -1)
up tuple[float, float, float]

Camera up vector.

(0, 0, 1)
interactive bool

Whether the move is a tick of a camera motion instead of a jump; see :meth:set_camera_state.

False

set_camera_depth_range

set_camera_depth_range(canvas_id: UUID, depth_range: tuple[float, float], *, interactive: bool = False) -> None

Set the near/far clip distances for a canvas camera.

A jump, like :meth:fit_camera.

Parameters:

Name Type Description Default
canvas_id UUID

ID of the target canvas.

required
depth_range tuple[float, float]

(near, far) clip distances in world units.

required
interactive bool

Whether the move is a tick of a camera motion instead of a jump; see :meth:set_camera_state.

False

add_paint_controller

add_paint_controller(visual_id: UUID, canvas_id: UUID, brush_value: int = 1, brush_radius_voxels: float = 2.0, history_depth: int = 100, autosave_interval_s: float | None = None)

Create and wire a paint controller for a labels visual.

Only labels can be painted. The controller is chosen by the visual's type -- a LabelMemoryVisual gets a SyncPaintController and a MultiscaleLabelVisual a MultiscalePaintController -- rather than by its store, because a multiscale labels visual may be backed by a generic MultiscaleZarrDataStore. Image visuals raise.

Parameters:

Name Type Description Default
visual_id UUID

Visual to paint on.

required
canvas_id UUID

Canvas to bind to. Its camera controller is disabled for the session. Pass controller.get_canvas_ids(scene_id)[0] for the common single-canvas case.

required
brush_value int

Integer label ID written to every painted voxel.

1
brush_radius_voxels float

Brush radius in level-0 voxel units.

2.0
history_depth int

Maximum undoable strokes.

100
autosave_interval_s float | None

Seconds between automatic flushes for MultiscalePaintController. Each autosave rebuilds the pyramid and resets GPU paint textures. None disables autosave. Ignored for SyncPaintController.

None

Returns:

Type Description
AbstractPaintController

Fully wired; caller owns the object.

Raises:

Type Description
TypeError

If the visual type has no registered paint controller.

remove_scene

remove_scene(scene_id: UUID) -> None

Remove a scene and all its visuals and canvases.

Teardown order mirrors remove_visual for each child visual, then cleans up the scene-level maps and render layer.

Parameters:

Name Type Description Default
scene_id UUID

ID of the scene to remove.

required

Raises:

Type Description
KeyError

If scene_id is not registered.

remove_canvas

remove_canvas(canvas_id: UUID) -> None

Remove a canvas from its scene, disconnecting all wiring.

Teardown order mirrors remove_scene for the canvas-level steps: 1. Drop the canvas's camera motion state. 2. Remove bus subscriptions owned by this canvas. 3. Update controller lookup maps. 4. Remove from the model layer. 5. Render-layer teardown (drops widget and GPU references).

Parameters:

Name Type Description Default
canvas_id UUID

ID of the canvas to remove.

required

Raises:

Type Description
KeyError

If canvas_id is not registered.

remove_visual

remove_visual(visual_id: UUID) -> None

Remove a visual from its scene, disconnecting all wiring.

Teardown order: 1. Psygnal bridge handlers disconnected first — prevents the bridge closures from firing during any subsequent model access. 2. Bus subscriptions removed — prevents dangling GFX-layer handlers from receiving events after the node is gone from the scene graph. 3. Render-layer removal — drops scene-graph node and GPU references. 4. VisualRemovedEvent emitted for external observers.

Parameters:

Name Type Description Default
visual_id UUID

ID of the visual to remove.

required

Raises:

Type Description
KeyError

If visual_id is not registered.

remove_data_store

remove_data_store(data_store_id: UUID) -> None

Remove a data store from the model.

Raises ValueError if any live visual still references the store. Call remove_visual for each referencing visual first.

Parameters:

Name Type Description Default
data_store_id UUID

ID of the data store to remove.

required

Raises:

Type Description
ValueError

If one or more visuals still reference the store. The error message names each visual so the caller can identify them.

KeyError

If data_store_id is not registered.

on_dims_changed

on_dims_changed(scene_id: UUID, callback: Callable[[DimsChangedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired whenever the dims for scene_id change.

The callback receives the full DimsChangedEvent, which includes source_id for echo-filtering and dims_state for the new state.

Parameters:

Name Type Description Default
scene_id UUID

The scene to watch.

required
callback Callable[[DimsChangedEvent], None]

Called with the DimsChangedEvent on each dims change.

required
owner_id UUID

UUID under which this subscription is registered. Pass the caller's own UUID so unsubscribe_owner(owner_id) removes it during teardown.

required
weak bool

If True, hold only a weak reference to callback. Use for transient widgets that may be destroyed outside the controller's teardown path. Cannot be used with lambdas.

False

Returns:

Type Description
SubscriptionHandle

Pass to EventBus.unsubscribe() for individual removal.

on_dims_interaction

on_dims_interaction(scene_id: UUID, callback: Callable[[DimsInteractionEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when a dims scrub starts or ends.

The callback receives a DimsInteractionEvent. The start is emitted before the scrub's first tick changes the dims model.

Parameters:

Name Type Description Default
scene_id UUID

The scene to watch.

required
callback Callable[[DimsInteractionEvent], None]

Called with the DimsInteractionEvent on each start and end.

required
owner_id UUID

UUID under which this subscription is registered. Pass the caller's own UUID so unsubscribe_owner(owner_id) removes it during teardown.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_camera_changed

on_camera_changed(scene_id: UUID, callback: Callable[[CameraChangedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired whenever the camera for scene_id changes.

The callback receives a CameraChangedEvent carrying the latest CameraState, including extent (width, height) for OrthographicCamera scenes.

Parameters:

Name Type Description Default
scene_id UUID

The scene to watch.

required
callback Callable[[CameraChangedEvent], None]

Called with the CameraChangedEvent on each camera change.

required
owner_id UUID

UUID under which this subscription is registered. Pass the caller's own UUID so unsubscribe_owner(owner_id) removes it during teardown.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_camera_interaction

on_camera_interaction(scene_id: UUID, callback: Callable[[CameraInteractionEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when a camera motion starts or ends.

The callback receives a CameraInteractionEvent for every canvas of scene_id; its canvas_id says which.

Parameters:

Name Type Description Default
scene_id UUID

The scene to watch.

required
callback Callable[[CameraInteractionEvent], None]

Called with the CameraInteractionEvent on each start and end.

required
owner_id UUID

UUID under which this subscription is registered. Pass the caller's own UUID so unsubscribe_owner(owner_id) removes it during teardown.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

unsubscribe_mouse

unsubscribe_mouse(handle: SubscriptionHandle) -> None

Remove a subscription created by an on_mouse_* method.

Mouse subscriptions do not gate pick details; this is EventBus.unsubscribe, kept so a handle from on_pick passed here by mistake still updates the pick counters.

Parameters:

Name Type Description Default
handle SubscriptionHandle

A handle returned by one of the on_mouse_* methods.

required

on_pick

on_pick(canvas_id: UUID, event_type: type, callback: Callable[[Any], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when a visual of one kind is picked.

Mouse events say that something was hit; pick events say what (unified image design 3.7). A pick event follows the mouse event for the same pointer event, carries the same gesture_id, action, button, buttons and modifiers, and is emitted only for hits. Image and labels events carry the values under the pointer; for multiscale visuals those are read asynchronously, so match events by gesture_id and action rather than by arrival order.

Pick details are extracted while the canvas has any pick subscriber, and image and labels values are read only while that event type has one.

Parameters:

Name Type Description Default
canvas_id UUID

The canvas to watch.

required
event_type type

One of :data:cellier.events.PICK_EVENT_TYPES: ImagePickEvent, LabelsPickEvent, PointsPickEvent, LinesPickEvent, MeshPickEvent or GraphPickEvent.

required
callback Callable

Called with each pick event.

required
owner_id UUID

UUID under which this subscription is registered for bulk removal via unsubscribe_all(owner_id).

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

Pass it to :meth:unsubscribe_pick.

Raises:

Type Description
TypeError

If event_type is not a pick event type.

unsubscribe_pick

unsubscribe_pick(handle: SubscriptionHandle) -> None

Remove a subscription created by :meth:on_pick.

Keeps the per-canvas and per-type subscriber counts accurate: the last subscriber for a type stops its value reads, and the last for a canvas disables pick-detail extraction there.

Parameters:

Name Type Description Default
handle SubscriptionHandle

A handle returned by :meth:on_pick.

required

on_mouse_press_2d

on_mouse_press_2d(canvas_id: UUID, callback: Callable[[CanvasMousePress2DEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired on every pointer-down event on a 2D canvas.

Parameters:

Name Type Description Default
canvas_id UUID

The canvas to watch.

required
callback Callable[[CanvasMousePress2DEvent], None]

Called with the CanvasMousePress2DEvent on each press.

required
owner_id UUID

UUID under which this subscription is registered for bulk removal via unsubscribe_all(owner_id).

required
weak bool

If True, hold only a weak reference to callback.

False

on_mouse_move_2d

on_mouse_move_2d(canvas_id: UUID, callback: Callable[[CanvasMouseMove2DEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired on every pointer-move event on a 2D canvas.

on_mouse_release_2d

on_mouse_release_2d(canvas_id: UUID, callback: Callable[[CanvasMouseRelease2DEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired on every pointer-up event on a 2D canvas.

on_mouse_press_3d

on_mouse_press_3d(canvas_id: UUID, callback: Callable[[CanvasMousePress3DEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired on every pointer-down event on a 3D canvas.

Parameters:

Name Type Description Default
canvas_id UUID

The canvas to watch.

required
callback Callable[[CanvasMousePress3DEvent], None]

Called with the CanvasMousePress3DEvent on each press.

required
owner_id UUID

UUID under which this subscription is registered for bulk removal via unsubscribe_all(owner_id).

required
weak bool

If True, hold only a weak reference to callback.

False

on_mouse_move_3d

on_mouse_move_3d(canvas_id: UUID, callback: Callable[[CanvasMouseMove3DEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired on every pointer-move event on a 3D canvas.

on_mouse_release_3d

on_mouse_release_3d(canvas_id: UUID, callback: Callable[[CanvasMouseRelease3DEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired on every pointer-up event on a 3D canvas.

set_camera_controller_enabled

set_camera_controller_enabled(canvas_id: UUID, enabled: bool) -> None

Enable or disable the camera controller for one canvas.

Parameters:

Name Type Description Default
canvas_id UUID

The canvas whose controller state should change.

required
enabled bool

False disables the controller (paint session active). True restores normal camera interaction (session ended).

required

on_appearance_changed

on_appearance_changed(visual_id: UUID, callback: Callable[[AppearanceChangedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired whenever the appearance of visual_id changes.

The callback receives the full AppearanceChangedEvent, which includes source_id for echo-filtering, field_name, and new_value.

Parameters:

Name Type Description Default
visual_id UUID

The visual to watch.

required
callback Callable[[AppearanceChangedEvent], None]

Called with the AppearanceChangedEvent on each appearance change.

required
owner_id UUID

UUID under which this subscription is registered. Pass the caller's own UUID so unsubscribe_owner(owner_id) removes it during teardown.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_visual_changed

on_visual_changed(visual_id: UUID, callback: Callable[[AppearanceChangedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Deprecated. Use on_appearance_changed instead.

on_visibility_changed

on_visibility_changed(visual_id: UUID, callback: Callable[[VisualVisibilityChangedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired whenever the visibility of visual_id changes.

Parameters:

Name Type Description Default
visual_id UUID

The visual to watch.

required
callback Callable[[VisualVisibilityChangedEvent], None]

Called with the VisualVisibilityChangedEvent.

required
owner_id UUID

UUID under which this subscription is registered.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

set_visual_visible

set_visual_visible(visual_id: UUID, visible: bool) -> None

Show or hide a visual.

Parameters:

Name Type Description Default
visual_id UUID

Target visual.

required
visible bool

True to show, False to hide.

required

set_visual_outline

set_visual_outline(visual_id: UUID, slot: int = 1, placement: str | None = None) -> None

Outline a visual with a screen-space contour.

Requires render_config.outline.enabled (or controller.render_manager.outline_enabled = True); the outline pass is off by default.

Parameters:

Name Type Description Default
visual_id UUID

Target visual.

required
slot int

0 removes the outline. 1..15 selects palette entry slot - 1 from OutlineConfig.palette for the selection layer; any nonzero slot also makes the visual visible to the boundaries layer.

1
placement str | None

"inward" puts the band inside the region's own footprint, so the region never appears to grow but one thinner than twice the thickness is consumed entirely. "outward" puts it outside, reading as a halo and leaving the region intact. Defaults by visual type: "outward" for lines and points, whose screen-space defaults (2 px thickness, 5 px marker size) are too thin to survive an inward band at any zoom level; "inward" for everything else.

None

Raises:

Type Description
ValueError

If slot or placement is out of range.

Notes

A nonzero slot sets pick_write = True on the visual, since outlines are derived from the pick buffer. This also enables mouse picking for that visual, which is a side effect worth knowing about if you had deliberately turned picking off.

On a labels visual what slot means depends on outline_mode, and the default mode is "per_label": there the visual is outlined per label rather than as one silhouette, and the colour comes from the label, not from slot -- a nonzero slot only makes the visual eligible for the boundaries layer, and :meth:set_label_selection is what puts a palette colour on individual labels. In "whole_object" and "all_boundaries" mode slot is the colour, exactly as on every other visual. Per label keys need the canvas to have been built with outlines enabled; without that the visual falls back to a silhouette.

set_visual_ambient_occlusion

set_visual_ambient_occlusion(visual_id: UUID, enabled: bool | None = None) -> None

Choose whether one visual receives ambient occlusion.

Requires render_config.ambient_occlusion.enabled (or controller.ambient_occlusion_enabled = True); the occlusion pass is off by default, and off in 2D always.

Parameters:

Name Type Description Default
visual_id UUID

Target visual.

required
enabled bool | None

None (the default) restores the automatic rule: excluded while the visual renders in a MIP-family mode (mip, attenuated_mip, minip), included otherwise, re-derived whenever the render mode changes. True and False are explicit and survive a render-mode change.

None
Notes

A MIP-family mode writes the depth of the brightest sample along the ray rather than of a surface, and that depth jumps between neighbouring pixels, so occlusion computed from it shimmers. That is what the automatic rule exists for; an explicit True is available for anyone who wants it anyway.

Excluding a visual sets pick_write = True on it, because the occlusion pass identifies pixels through the pick buffer -- the same side effect :meth:set_visual_outline has, and worth knowing if you had deliberately turned picking off. The automatic rule needs picking too: a MIP visual with pick_write = False cannot be identified per pixel, so it receives occlusion despite the default.

This controls whether the visual receives occlusion, not whether it casts it: the occlusion loop reads raw depth, so an excluded visual's depth still darkens its neighbours.

get_visual_ambient_occlusion

get_visual_ambient_occlusion(visual_id: UUID) -> bool | None

Return the explicit occlusion setting for visual_id.

Parameters:

Name Type Description Default
visual_id UUID

Target visual.

required

Returns:

Type Description
bool or None

None when the visual is on the automatic rule, which is the default.

get_visual_outline

get_visual_outline(visual_id: UUID) -> tuple[int, str] | None

Return (slot, placement) for visual_id, or None.

Parameters:

Name Type Description Default
visual_id UUID

Target visual.

required

Returns:

Type Description
tuple[int, str] or None

None when the visual is not outlined.

set_label_selection

set_label_selection(visual_id: UUID, selection: dict[int, int]) -> None

Choose which label values the selection layer outlines.

Parameters:

Name Type Description Default
visual_id UUID

A labels visual, already given an outline with :meth:set_visual_outline.

required
selection dict[int, int]

{label value: palette slot}. Slots are clamped into 1..15 and index OutlineConfig.palette as slot - 1. An empty dict clears the selection, leaving the boundaries layer to draw every label boundary.

required
Notes

Selection is exact: the label key is range-partitioned, with 1..15 reserved for selected labels and everything above for unselected ones, so an unselected label can never be mistaken for a selected one.

Requires a canvas built with outlines enabled -- the per-label key lives in a render target that is only allocated then. Without it the visual still gets a whole-object silhouette.

on_scene_added

on_scene_added(scene_id: UUID, callback: Callable[[SceneAddedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when scene_id is added.

Parameters:

Name Type Description Default
scene_id UUID

The scene to watch.

required
callback Callable[[SceneAddedEvent], None]

Called with the SceneAddedEvent.

required
owner_id UUID

UUID under which this subscription is registered.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_scene_removed

on_scene_removed(scene_id: UUID, callback: Callable[[SceneRemovedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when scene_id is removed.

Parameters:

Name Type Description Default
scene_id UUID

The scene to watch.

required
callback Callable[[SceneRemovedEvent], None]

Called with the SceneRemovedEvent.

required
owner_id UUID

UUID under which this subscription is registered.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_visual_added

on_visual_added(visual_id: UUID, callback: Callable[[VisualAddedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when visual_id is added.

The bus routes this event by scene, so the subscription filters on the event's visual_id field instead.

Parameters:

Name Type Description Default
visual_id UUID

The visual to watch.

required
callback Callable[[VisualAddedEvent], None]

Called with the VisualAddedEvent.

required
owner_id UUID

UUID under which this subscription is registered.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_visual_removed

on_visual_removed(visual_id: UUID, callback: Callable[[VisualRemovedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when visual_id is removed.

The bus routes this event by scene, so the subscription filters on the event's visual_id field instead.

Parameters:

Name Type Description Default
visual_id UUID

The visual to watch.

required
callback Callable[[VisualRemovedEvent], None]

Called with the VisualRemovedEvent.

required
owner_id UUID

UUID under which this subscription is registered.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

cancel_pending_slices

cancel_pending_slices(scene_id: UUID) -> None

Cancel all in-flight slice requests for scene_id.

Parameters:

Name Type Description Default
scene_id UUID

ID of the scene whose pending slices to cancel.

required

close

close() -> None

Cancel in-flight slices and close every canvas this viewer owns.

Releases the render surfaces and GPU resources held by the controller. Closing is explicit because the canvases are owned by the GUI backend, not by Python refcounting, so dropping the controller alone leaks them (see :meth:CanvasView.close).

Cancels the interaction timers, the queued camera reslices and every in-flight slice task -- including the ones the slice coordinator no longer tracks -- so a closed controller holds no live asyncio.Task.

Also disconnects the psygnal bridges from the model and clears the event buses. Those hold the controller's own handlers, and psygnal keeps them strongly, so a closed-but-connected controller stays reachable from the models it was watching -- and keeps reacting to them. remove_visual and remove_scene already do this for what they remove; this does it for whatever is left.

Safe to call more than once; the controller must not be used afterwards.

on_section_changed

on_section_changed(visual_id: UUID, callback: Callable[[MeshSectionChangedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when a mesh's section config changes.

Parameters:

Name Type Description Default
visual_id UUID

The mesh visual to watch.

required
callback Callable[[MeshSectionChangedEvent], None]

Called with the MeshSectionChangedEvent: source_id for echo filtering, field_name and new_value.

required
owner_id UUID

UUID under which this subscription is registered.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_plane_gizmo_changed

on_plane_gizmo_changed(canvas_id: UUID, callback: Callable[[PlaneGizmoChangedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when a canvas's plane gizmo changes.

Parameters:

Name Type Description Default
canvas_id UUID

The canvas to watch.

required
callback Callable[[PlaneGizmoChangedEvent], None]

Called with the PlaneGizmoChangedEvent: the visual, the kind ("clipping" or "render") and the plane the canvas's gizmo is now on, or None for all three when it has none.

required
owner_id UUID

UUID under which this subscription is registered.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_plane_interaction

on_plane_interaction(visual_id: UUID, callback: Callable[[PlaneInteractionEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when a clipping plane drag starts or ends.

Parameters:

Name Type Description Default
visual_id UUID

The visual to watch.

required
callback Callable[[PlaneInteractionEvent], None]

Called with the PlaneInteractionEvent on each start and end.

required
owner_id UUID

UUID under which this subscription is registered.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_clipping_planes_changed

on_clipping_planes_changed(visual_id: UUID, callback: Callable[[ClippingPlanesChangedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when a visual's clipping planes change.

Parameters:

Name Type Description Default
visual_id UUID

The visual to watch.

required
callback Callable[[ClippingPlanesChangedEvent], None]

Called with the ClippingPlanesChangedEvent: source_id for echo filtering, and clipping_planes, the complete new tuple.

required
owner_id UUID

UUID under which this subscription is registered.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_render_planes_changed

on_render_planes_changed(visual_id: UUID, callback: Callable[[RenderPlanesChangedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when a visual's render planes change.

Parameters:

Name Type Description Default
visual_id UUID

The visual to watch.

required
callback Callable[[RenderPlanesChangedEvent], None]

Called with the RenderPlanesChangedEvent: source_id for echo filtering, and render_planes, the complete new tuple.

required
owner_id UUID

UUID under which this subscription is registered.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_lod_config_changed

on_lod_config_changed(visual_id: UUID, callback: Callable[[LodConfigChangedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when a multiscale mesh's lod config changes.

Parameters:

Name Type Description Default
visual_id UUID

The multiscale mesh visual to watch.

required
callback Callable[[LodConfigChangedEvent], None]

Called with the LodConfigChangedEvent: source_id for echo filtering, and lod, the complete new config.

required
owner_id UUID

UUID under which this subscription is registered.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_aabb_changed

on_aabb_changed(visual_id: UUID, callback: Callable[[AABBChangedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired whenever the AABB params of visual_id change.

The callback receives the full AABBChangedEvent, which includes source_id for echo-filtering, field_name, and new_value.

Parameters:

Name Type Description Default
visual_id UUID

The visual to watch.

required
callback Callable[[AABBChangedEvent], None]

Called with the AABBChangedEvent on each AABB change.

required
owner_id UUID

UUID under which this subscription is registered. Pass the caller's own UUID so unsubscribe_owner(owner_id) removes it during teardown.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_reslice_started

on_reslice_started(scene_id: UUID, callback: Callable[[ResliceStartedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when a reslice cycle begins for scene_id.

Useful for showing a loading indicator. Fired once per reslice submission, before any async data fetching starts.

Parameters:

Name Type Description Default
scene_id UUID

The scene to watch.

required
callback Callable[[ResliceStartedEvent], None]

Called with the ResliceStartedEvent.

required
owner_id UUID

UUID under which this subscription is registered. Pass the caller's own UUID so unsubscribe_owner(owner_id) removes it during teardown.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_reslice_completed

on_reslice_completed(visual_id: UUID, callback: Callable[[ResliceCompletedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when a reslice cycle completes for visual_id.

Useful for hiding a loading indicator. Fired once per visual per reslice cycle, after all bricks/tiles in the batch are committed.

Parameters:

Name Type Description Default
visual_id UUID

The visual to watch.

required
callback Callable[[ResliceCompletedEvent], None]

Called with the ResliceCompletedEvent.

required
owner_id UUID

UUID under which this subscription is registered. Pass the caller's own UUID so unsubscribe_owner(owner_id) removes it during teardown.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_reslice_progress

on_reslice_progress(visual_id: UUID, callback: Callable[[ResliceProgressEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired as a multiscale visual's data loads.

Fired at most once per event-loop iteration: after each plan, each frame that commits the visual's data, a read given up, or an invalidation. event.progress is a :class:~cellier.events.LoadingProgress. Only multiscale visuals load progressively; others never fire it.

Parameters:

Name Type Description Default
visual_id UUID

The visual to watch.

required
callback Callable[[ResliceProgressEvent], None]

Called with the ResliceProgressEvent.

required
owner_id UUID

UUID under which this subscription is registered, for unsubscribe_owner(owner_id).

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_backstop_complete

on_backstop_complete(visual_id: UUID, callback: Callable[[BackstopCompleteEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when a multiscale visual's backstop is loaded.

From then on the view shows the current slice everywhere, possibly blurry, while finer data keeps loading. Fired once per plan.

Parameters:

Name Type Description Default
visual_id UUID

The visual to watch.

required
callback Callable[[BackstopCompleteEvent], None]

Called with the BackstopCompleteEvent.

required
owner_id UUID

UUID under which this subscription is registered, for unsubscribe_owner(owner_id).

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

loading_progress

loading_progress(visual_id: UUID) -> LoadingProgress | None

A multiscale visual's current loading progress.

The same counts the latest ResliceProgressEvent carried, read now. None for a visual that is not multiscale, or that has not been planned yet (or draws nothing).

Parameters:

Name Type Description Default
visual_id UUID

The visual.

required

Returns:

Type Description
LoadingProgress or None

on_scene_ready

on_scene_ready(scene_id: UUID, callback: Callable[[], None], *, owner_id: UUID | None = None) -> None

Reslice scene_id and fire callback once all its data is on the GPU.

This triggers a reslice of every visual in the scene and invokes callback exactly once, after all visuals loaded by that reslice have committed to the GPU across every attached canvas. Visuals with no data in the current view (frustum-culled, empty slab, or hidden) do not delay the callback.

Unlike :meth:on_reslice_completed (which fires per-visual on every cycle), this is a scene-level, one-shot readiness signal — the right hook for fitting the camera or hiding a startup spinner once a mixed scene of multiscale images, in-memory images, and geometry has fully loaded.

Parameters:

Name Type Description Default
scene_id UUID

ID of the scene to reslice and watch.

required
callback Callable[[], None]

Zero-argument callback fired once the scene's data is resident.

required
owner_id UUID or None

Owner under which the temporary subscriptions are registered. Defaults to the controller's own id.

None

on_canvas_connected

on_canvas_connected(canvas_id: UUID, callback: Callable[[], None], *, owner_id: UUID | None = None) -> None

Fire callback once canvas_id's front end is live and able to draw.

The weaker, earlier sibling of :meth:on_canvas_first_frame: it says the canvas can render, not that it has. On Qt the two nearly coincide; on the anywidget backend the canvas exists in Python long before the browser mounts it, and a frame may never arrive at all -- so work that only needs a usable canvas should wait on this instead.

Fires immediately if the canvas has already connected.

Parameters:

Name Type Description Default
canvas_id UUID

ID of the canvas to watch.

required
callback Callable[[], None]

Zero-argument callback, fired once.

required
owner_id UUID or None

Owner for the temporary subscription. Defaults to the controller's own id.

None

on_canvas_first_frame

on_canvas_first_frame(canvas_id: UUID, callback: Callable[[], None], *, owner_id: UUID | None = None) -> None

Fire callback once canvas_id has rendered its first frame.

The first rendered frame guarantees the canvas has reached its final logical size and its camera matrix has been applied — the precondition for fitting the camera and computing view-dependent (multiscale) slice requests at the correct level of detail. This is a timer-free replacement for deferring startup work with QTimer.singleShot(0).

A draw is requested immediately so the frame is guaranteed to arrive whether or not the event loop is already running.

Parameters:

Name Type Description Default
canvas_id UUID

ID of the canvas to watch.

required
callback Callable[[], None]

Zero-argument callback fired once, on the first frame.

required
owner_id UUID or None

Owner under which the temporary subscription is registered. Defaults to the controller's own id.

None

unsubscribe_owner

unsubscribe_owner(owner_id: UUID) -> None

Remove all event subscriptions registered under owner_id.

GUI widgets should call this from their Qt closeEvent or destroyed signal handler to deterministically clean up their bus subscriptions.

Parameters:

Name Type Description Default
owner_id UUID

The UUID used as owner_id when the subscriptions were registered (typically the widget's own self._id).

required

connect_widget

connect_widget(widget: WidgetView, *, subscription_specs: list[SubscriptionSpec] | None = None) -> None

Wire a widget's psygnal signals to the bus and register subscriptions.

Widgets declare their intent through two psygnal signals and an optional list of SubscriptionSpec objects, so they never import or hold a reference to CellierController.

The caller is responsible for constructing the widget and passing the specs — typically obtained from widget.subscription_specs().

Parameters:

Name Type Description Default
widget WidgetView

Any object exposing:

  • widget._id — a UUID identifying the widget.
  • widget.changed — a psygnal Signal that emits a CellierUpdateEventTypes instance when the user changes a value. Connected to incoming_events.emit.
  • widget.closed — a psygnal Signal (no arguments) emitted when the widget is closed. Triggers unsubscribe_owner(widget._id).
required
subscription_specs list[SubscriptionSpec] | None

Optional list of SubscriptionSpec entries describing which outgoing bus events the widget wants to receive. Pass None (or omit the argument) for pure-output widgets that do not need model-driven updates.

None