Paint¶
Paint controllers apply label edits to data stores, with undo/redo history and write buffering for multiscale data.
Controllers¶
cellier.paint.AbstractPaintController ¶
Bases: ABC
Minimal shared base for paint controllers.
Owns: EventBus subscriptions, :class:CommandHistory,
:class:ActiveStroke accumulation, and camera-controller locking on
the bound canvas.
Concrete subclasses override the _read_old_values,
_write_values, _on_stroke_completed, commit, and
abort hooks to plug in storage-specific behaviour.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cellier_controller
|
CellierController
|
|
required |
visual_id
|
UUID
|
|
required |
scene_id
|
UUID
|
|
required |
canvas_id
|
UUID
|
Mouse subscriptions are scoped to this canvas; its camera controller is disabled for the session duration. |
required |
data_store_id
|
UUID
|
Recorded on every :class: |
required |
brush_value
|
int
|
|
1
|
brush_radius_voxels
|
float
|
|
2.0
|
history_depth
|
int
|
|
100
|
cellier.paint.SyncPaintController ¶
Bases: AbstractPaintController
Paint controller for :class:ImageMemoryStore and :class:LabelMemoryStore.
Directly and synchronously mutates the backing numpy array on every
brush application, then calls
:meth:CellierController.reslice_scene so the display updates
immediately. Suitable for testing and in-memory annotation
workflows.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cellier_controller
|
CellierController
|
|
required |
visual_id
|
UUID
|
|
required |
scene_id
|
UUID
|
|
required |
canvas_id
|
UUID
|
|
required |
data_store
|
ImageMemoryStore | LabelMemoryStore
|
The store whose |
required |
displayed_axes
|
tuple[int, int]
|
The two data-array axes currently displayed in 2D for the bound
canvas, in |
required |
brush_value
|
int
|
|
1
|
brush_radius_voxels
|
float
|
|
2.0
|
history_depth
|
int
|
|
100
|
cellier.paint.MultiscalePaintController ¶
Bases: AbstractPaintController
Paint controller for OME-Zarr / multiscale-zarr data stores.
Stages all writes in a tensorstore transaction (RAM-only) until the user commits or aborts the session. Visible feedback is provided by writing directly into the GPU paint cache; the shader composites paint over the base sample so painted voxels are visible immediately without a slicer round-trip.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cellier_controller
|
CellierController
|
|
required |
visual_id
|
UUID
|
|
required |
scene_id
|
UUID
|
|
required |
canvas_id
|
UUID
|
|
required |
data_store
|
Any
|
Either |
required |
visual_block_size
|
int
|
Tile / brick side length used by the visual's render config.
Must match the |
required |
displayed_axes
|
tuple[int, ...]
|
The two data-array axes currently displayed in 2D for the bound
canvas, in |
required |
brush_value
|
int
|
|
1
|
brush_radius_voxels
|
float
|
|
2.0
|
history_depth
|
int
|
|
100
|
autosave_interval_s
|
float | None
|
If set, a |
None
|
History¶
cellier.paint.ActiveStroke ¶
Accumulator that builds a :class:PaintStrokeCommand during a drag.
record is called once per brush application. Successive calls
concatenate their voxel arrays. finalise constructs the
immutable :class:PaintStrokeCommand. After finalise, no further
record calls are permitted.
record ¶
Append the voxels painted in one brush application.
finalise ¶
finalise() -> PaintStrokeCommand
Return the completed command. Do not call record after this.
Successive record calls during a drag often overlap. For each
unique voxel we keep the first old_values seen — that is
the true pre-stroke value. Without this dedup, an undo replays
stale "old" values captured after earlier in-stroke writes,
leaving residue. new_values are also taken from the first
occurrence (within one stroke they are constant anyway).
cellier.paint.CommandHistory ¶
Bounded deque of :class:PaintStrokeCommand with undo/redo pointers.
Only the undo stack is bounded by max_depth; the redo stack is
unbounded because it is cleared by push before it can grow
indefinitely.
cellier.paint.PaintStrokeCommand
dataclass
¶
Immutable record of one completed brush stroke.
Attributes:
| Name | Type | Description |
|---|---|---|
visual_id |
UUID
|
|
data_store_id |
UUID
|
|
voxel_indices |
ndarray
|
Shape |
old_values |
ndarray
|
Shape |
new_values |
ndarray
|
Shape |
Write buffers & layers¶
cellier.paint.WriteBuffer ¶
Bases: Protocol
Protocol for staged voxel writes against a level-0 store.
Concrete implementations stage writes in RAM, allow read-your-writes visibility, and flush or discard atomically.
Implementations must be safe to call from the Qt main thread; the paint controller never invokes them from a background thread.
stage ¶
Stage a vector of point writes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
voxel_indices
|
ndarray
|
Shape |
required |
values
|
ndarray
|
Shape |
required |
read_staged ¶
Return the current values at the given voxel indices.
With read-your-writes semantics, this returns staged values for
voxels that have been written through stage, falling back to
the underlying store for unwritten voxels.
Returns shape (N,) in the underlying store's native dtype.
cellier.paint.TensorStoreWriteBuffer ¶
:class:WriteBuffer backed by a :class:tensorstore.Transaction.
Wraps a single TensorStore handle (typically the level-0 store of a
multiscale data store) in an isolated transaction. All writes are
coalesced in tensorstore's in-memory write buffer until commit
or abort is called.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
store
|
TensorStore
|
The tensorstore handle to paint against. This must be the level-0 store of the multiscale data store. |
required |
Notes
The transaction is created in isolated mode (the default) which
is what we want: paints are not visible outside the transaction
until commit.
After commit or abort the buffer is one-shot — calling
stage again will fail since the transaction has been finalised.
The paint controller wraps lifecycle management around this.
transaction
property
¶
The active transaction, or None after commit/abort.
transactional_store
property
¶
The store wrapped in this buffer's transaction (read-your-writes).
__init__ ¶
Construct.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
store
|
TensorStore
|
Untransacted level-0 store handle. |
required |
transaction
|
Transaction | None
|
Optional pre-built transaction. When provided, the same
transaction can be shared with other consumers (e.g. the
data store's read path) so they observe staged writes.
When |
None
|
cellier.paint.WriteLayer ¶
Sparse in-memory tracker of dirty level-0 bricks for a paint session.
Records which bricks have been touched; the painted voxel data
itself lives in the :class:WriteBuffer. N-dim agnostic — used
unchanged for 2-D and 3-D paint sessions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data_store_id
|
UUID of the painted store. Recorded but unused by |
required | |
block_size
|
int
|
Side length of one level-0 brick in voxels. Must match the
|
required |
voxel_to_brick_key ¶
voxel_to_brick_key(voxel_idx: ndarray) -> BrickKey
Map a level-0 voxel index to its enclosing level-0 :class:BrickKey.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
voxel_idx
|
ndarray
|
Shape |
required |
Returns:
| Type | Description |
|---|---|
BrickKey
|
|
cellier.paint.BrickKey ¶
Bases: NamedTuple
Identifier for a level-k brick by its grid coordinates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
level
|
int
|
LOD level. |
required |
grid_coords
|
tuple[int, ...]
|
One grid index per spatial axis, in data-array axis order. Length matches the painted store's ndim (2 for 2-D, 3 for 3-D). |
required |