API Reference#
scenex
#
Declarative scene graph library for scientific visualization.
scenex is a Python library for creating interactive 3D visualizations using a declarative scene graph model. It provides a backend-agnostic API that works with multiple rendering engines (pygfx, vispy) while maintaining a consistent, intuitive interface.
Key Features
- Declarative API: Describe how the scene should look rather than how to render it.
- Evented models: Events enable painless reaction to changes in the scene graph
- Multiple backends: Render with pygfx (WebGPU) or vispy (OpenGL)
Quick Start
Create and display a simple visualization::
import numpy as np
import scenex as snx
# Create a random image
data = np.random.rand(100, 100)
img = snx.Image(data=data)
# Show it
snx.show(img)
snx.run()
See Also
- scenex.model: Core declarative model classes
- scenex.adaptors: Backend adaptor implementations
- scenex.app: Application and event handling
Modules:
-
adaptors–Backend adaptors that translate scenex models into graphics library calls.
-
app–Application and GUI framework abstraction layer.
-
conftest–Pytest setup for doctests.
-
imgui–ImGui controls for interactive scenex visualization.
-
model–Declarative model classes for building scene graphs.
-
util–Utility functions for displaying and debugging scenex visualizations.
-
utils–Utilities for working with scenex structures.
Classes:
-
Camera–A camera that defines the viewing perspective and projection for a scene.
-
CameraController–Base class defining how a camera responds to user interaction events.
-
Canvas–A rendering surface that displays one or more views.
-
ColorModel–Base class for color models used in scene nodes.
-
Coord–Distance along a number of pixels. Expressed using CSS-style strings.
-
FaceColors–Per-face coloring strategy for mesh-like nodes.
-
Image–A 2D image rendered as a textured rectangle.
-
Layout–Style model for a view's border, padding, background, and placement.
-
Letterbox–Maintain content aspect ratio on resize via letterboxing/pillarboxing.
-
Line–A polyline defined by connected vertices.
-
Mesh–A 3D surface mesh composed of triangular faces.
-
Node–Base class for all nodes in the scene graph.
-
Orbit–3D orbit controller for rotating around a focal point.
-
PanZoom–2D pan and zoom controller for orthographic views.
-
Points–A collection of point markers rendered at specified coordinates.
-
ResizePolicy–Base class defining how a view adapts to changes in its layout dimensions.
-
Scene–The root container node for a scene graph.
-
Text–A text label positioned in 3D world space.
-
Transform–A 4x4 homogeneous transformation matrix for 3D affine transformations.
-
UniformColor–Uniform coloring strategy for scene nodes.
-
VertexColors–Per-vertex coloring strategy for mesh, line, or points nodes.
-
View–A rectangular viewport that displays a scene through a camera.
-
Volume–A 3D volumetric dataset rendered with volume rendering techniques.
Functions:
-
native–Get the native widget for the given canvas.
-
run–Start the GUI event loop to display interactive visualizations.
-
set_cursor–Set the cursor for the given canvas.
-
show–Display a visualization by creating a canvas and making it visible.
-
use–Set the graphics backend for rendering scenex visualizations.
Camera
#
Bases: Node
flowchart TD
scenex.Camera[Camera]
scenex.model._nodes.node.Node[Node]
scenex.model._base.EventedBase[EventedBase]
scenex.model._nodes.node.Node --> scenex.Camera
scenex.model._base.EventedBase --> scenex.model._nodes.node.Node
click scenex.Camera href "" "scenex.Camera"
click scenex.model._nodes.node.Node href "" "scenex.model._nodes.node.Node"
click scenex.model._base.EventedBase href "" "scenex.model._base.EventedBase"
A camera that defines the viewing perspective and projection for a scene.
The Camera is a node in the scene graph that determines how 3D world space is projected onto a 2D canvas. It combines a view transformation (positioning the camera in the scene) with a projection transformation (defining the viewing volume and perspective).
Cameras use two transforms:
- transform (inherited from Node): Maps local 3D space to world 3D space,
positioning and orienting the camera in the scene.
- projection: Maps normalized device coordinates [-1, 1] x [-1, 1] to rays in
local 3D space, defining the viewing volume and projection type.
The camera uses a right-handed coordinate system following OpenGL conventions: the positive x-axis points right, the positive y-axis points up, and the positive z-axis points out of the screen toward the viewer.
Examples:
Create a camera with pan-zoom controller: >>> camera = Camera(controller=PanZoom(), interactive=True)
Create a camera with orbit controller: >>> camera = Camera(controller=Orbit(center=(0, 0, 0)), interactive=True)
Position a camera and point it at a target: >>> camera = Camera() >>> camera.transform = Transform().translated((10, 0, 0)) >>> camera.look_at((0, 0, 0), up=(0, 0, 1))
Create a perspective camera: >>> from scenex.utils.projections import perspective >>> camera = Camera(projection=perspective(fov=70, near=0.1, far=100))
Methods:
-
add_child–Add a child node to this node.
-
iter_parents–Return list of parents starting from this node.
-
look_at–Adjusts the camera to look at a target point in the world.
-
model_post_init–Called after the model is initialized.
-
passes_through–Returns the depth t at which the provided ray intersects this node.
-
path_to_node–Return two lists describing the path from this node to another.
-
remove_child–Remove a child node from this node. Does not raise if child is missing.
-
transform_to_node–Return Transform that maps from coordinate frame of
selftoother. -
tree_repr–Return an ASCII/Unicode tree representation of self and its descendants.
Attributes:
-
children(tuple[Node, ...]) –Return a tuple of the children of this node.
-
forward(Vector3D) –The forward direction of the camera in world space, as a unit vector.
-
up(Vector3D) –The up direction of the camera in world space, as a unit vector.
Source code in src/scenex/model/_nodes/node.py
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | |
forward
property
writable
#
forward: Vector3D
The forward direction of the camera in world space, as a unit vector.
up
property
writable
#
up: Vector3D
The up direction of the camera in world space, as a unit vector.
add_child
#
add_child(child: AnyNode) -> None
Add a child node to this node.
Source code in src/scenex/model/_nodes/node.py
222 223 224 225 226 227 228 | |
iter_parents
#
Return list of parents starting from this node.
The chain ends at the first node with no parents.
Source code in src/scenex/model/_nodes/node.py
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 | |
look_at
#
Adjusts the camera to look at a target point in the world.
Parameters:
-
(target#Position3D) –The position in 3D space that the camera should look at.
-
(up#Vector3D | None, default:None) –The up direction for the camera. If provided, this vector must be perpendicular to the forward vector that results from looking at target.
Source code in src/scenex/model/_nodes/camera.py
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | |
model_post_init
#
model_post_init(__context: Any) -> None
Called after the model is initialized.
Source code in src/scenex/model/_base.py
57 58 59 60 61 62 | |
passes_through
#
Returns the depth t at which the provided ray intersects this node.
The ray, in this case, is defined by R(t) = ray_origin + ray_direction * t, where t>=0
Parameters:
-
(ray#Ray) –The ray passing through the scene
Returns:
-
t(float | None) –The depth t at which the ray intersects the node, or None if it never intersects.
Source code in src/scenex/model/_nodes/node.py
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | |
path_to_node
#
Return two lists describing the path from this node to another.
Parameters:
-
(other#instance of Node) –The other node.
Returns:
Notes
The first list starts with this node and ends with the common parent between the endpoint nodes. The second list contains the remainder of the path from the common parent to the specified ending node.
For example, consider the following scenegraph::
A --- B --- C --- D
--- E --- F
Calling D.node_path(F) will return::
([D, C, B], [E, F])
Source code in src/scenex/model/_nodes/node.py
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 | |
remove_child
#
remove_child(child: AnyNode) -> None
Remove a child node from this node. Does not raise if child is missing.
Source code in src/scenex/model/_nodes/node.py
230 231 232 233 234 235 | |
transform_to_node
#
Return Transform that maps from coordinate frame of self to other.
Note that there must be a single path in the scenegraph that connects the two entities; otherwise an exception will be raised.
Parameters:
-
(other#instance of Node) –The other node.
Returns:
-
transform(instance of ChainTransform) –The transform.
Source code in src/scenex/model/_nodes/node.py
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | |
tree_repr
#
tree_repr() -> str
Return an ASCII/Unicode tree representation of self and its descendants.
Source code in src/scenex/model/_nodes/node.py
383 384 385 386 387 | |
CameraController
#
Bases: BaseModel
flowchart TD
scenex.CameraController[CameraController]
click scenex.CameraController href "" "scenex.CameraController"
Base class defining how a camera responds to user interaction events.
A CameraController handles user input (mouse, keyboard, wheel) to manipulate
camera transforms and projections, enabling interactive behaviors like panning,
zooming, orbiting, or custom camera controls. Controllers are attached to Camera
instances via the controller field and automatically receive events when the
camera is marked as interactive=True.
Event handlers should return True if they fully handled the event (stopping further propagation) or False if other handlers should continue processing the event.
Examples:
Create a camera with pan/zoom controller: >>> camera = Camera(controller=PanZoom(), interactive=True)
Create a camera with orbit controller: >>> camera = Camera(controller=Orbit(center=(0, 0, 0)), interactive=True)
See Also
PanZoom : 2D pan and zoom controller Orbit : 3D orbit controller Camera : Camera class that uses controllers
Methods:
-
handle_event–Handle a user interaction event to control the camera.
handle_event
abstractmethod
#
Handle a user interaction event to control the camera.
This method is called automatically on all events on the camera's view that were not handled by previous handlers during scenex event processing.
Parameters:
-
(event#Event) –The input event to handle (MouseMoveEvent, MousePressEvent, WheelEvent, KeyPressEvent, etc.)
-
(view#View) –The view containing the camera to manipulate.
Returns:
-
bool–True if the event was fully handled and should not propagate to other handlers, False if not handled or other handlers should process it.
Notes
A View is passed rather than a Camera directly because controllers
need view.to_ray() to unproject screen-space event positions into world
space, which requires both the camera matrices and the viewport dimensions.
Source code in src/scenex/model/_nodes/camera.py
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 | |
Canvas
#
Bases: EventedBase
flowchart TD
scenex.Canvas[Canvas]
scenex.model._base.EventedBase[EventedBase]
scenex.model._base.EventedBase --> scenex.Canvas
click scenex.Canvas href "" "scenex.Canvas"
click scenex.model._base.EventedBase href "" "scenex.model._base.EventedBase"
A rendering surface that displays one or more views.
The Canvas represents the top-level rendering context where views are displayed. In desktop applications, a canvas corresponds to a window. In web applications, it corresponds to a DOM element. Multiple views can be arranged on a single canvas using their layout parameters.
Examples:
Create a simple canvas with default settings: >>> canvas = Canvas()
Create a canvas with custom size and title: >>> canvas = Canvas(width=800, height=600, title="My Visualization")
Create a canvas with multiple views side-by-side: >>> canvas = Canvas(width=800, height=400, views=[View(), View()])
Methods:
-
close–Close the canvas and release resources.
-
content_rect_for–The pixel rect (x, y, width, height) of the content area for a view.
-
filter_event–Pass event through the canvas-level filter, if any.
-
handle–Handle the passed event.
-
model_post_init–Post-initialization hook for the model.
-
rect_for–The pixel rect (x, y, width, height) for a view, computed from its layout.
-
render–Render the canvas to an image array.
-
set_event_filter–Register a callable to filter all canvas events before view dispatch.
Attributes:
Source code in src/scenex/model/_canvas.py
91 92 93 94 95 96 | |
close
#
close() -> None
Close the canvas and release resources.
Source code in src/scenex/model/_canvas.py
108 109 110 111 | |
content_rect_for
#
The pixel rect (x, y, width, height) of the content area for a view.
Applies the view's padding, border_width, and margin insets to the
outer rect returned by rect_for.
Source code in src/scenex/model/_canvas.py
121 122 123 124 125 126 127 128 129 130 | |
filter_event
#
filter_event(event: Event) -> bool
Pass event through the canvas-level filter, if any.
Returns True iff the event was handled and should not propagate.
Source code in src/scenex/model/_canvas.py
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | |
handle
#
handle(event: Event) -> bool
Handle the passed event.
Source code in src/scenex/model/_canvas.py
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 | |
model_post_init
#
model_post_init(__context: Any) -> None
Post-initialization hook for the model.
Source code in src/scenex/model/_canvas.py
98 99 100 101 102 103 104 105 106 | |
rect_for
#
The pixel rect (x, y, width, height) for a view, computed from its layout.
Source code in src/scenex/model/_canvas.py
113 114 115 116 117 118 119 | |
render
#
render() -> ndarray
Render the canvas to an image array.
Returns:
-
ndarray–The rendered canvas as an RGBA image array. The array is expected to follow standard image conventions, with shape
(height, width, 4).
Source code in src/scenex/model/_canvas.py
170 171 172 173 174 175 176 177 178 179 180 181 182 | |
set_event_filter
#
set_event_filter(event_filter: Callable[[Event], bool] | None) -> Callable[[Event], bool] | None
Register a callable to filter all canvas events before view dispatch.
Parameters:
-
(event_filter#Callable[[Event], bool] | None) –A callable that takes an Event and returns True if the event was handled and should not be propagated further, False otherwise. Pass None to remove any existing filter.
Returns:
Source code in src/scenex/model/_canvas.py
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | |
ColorModel
#
ColorModel(**data: Any)
Bases: BaseModel
flowchart TD
scenex.ColorModel[ColorModel]
click scenex.ColorModel href "" "scenex.ColorModel"
Base class for color models used in scene nodes.
This class should not be instantiated directly. Instead, use one of its subclasses: - UniformColor: for a single color applied to the entire geometry - FaceColors: for per-face coloring (one color per face) - VertexColors: for per-vertex coloring (one color per vertex)
The color field is typed as Any to allow flexibility in subclasses.
Source code in src/scenex/model/_color.py
23 24 25 26 | |
Coord
#
Bases: BaseModel
flowchart TD
scenex.Coord[Coord]
click scenex.Coord href "" "scenex.Coord"
Distance along a number of pixels. Expressed using CSS-style strings.
FaceColors
#
FaceColors(**data: Any)
Bases: ColorModel
flowchart TD
scenex.FaceColors[FaceColors]
scenex.model._color.ColorModel[ColorModel]
scenex.model._color.ColorModel --> scenex.FaceColors
click scenex.FaceColors href "" "scenex.FaceColors"
click scenex.model._color.ColorModel href "" "scenex.model._color.ColorModel"
Per-face coloring strategy for mesh-like nodes.
This model applies a different color to each face of a mesh.
The color field is a sequence of Color instances, one for each face.
Examples:
Per-face coloring: >>> from cmap import Color >>> from scenex import FaceColors >>> model = FaceColors(color=[Color("red"), Color("blue"), Color("green")])
Source code in src/scenex/model/_color.py
23 24 25 26 | |
Image
#
Bases: Node
flowchart TD
scenex.Image[Image]
scenex.model._nodes.node.Node[Node]
scenex.model._base.EventedBase[EventedBase]
scenex.model._nodes.node.Node --> scenex.Image
scenex.model._base.EventedBase --> scenex.model._nodes.node.Node
click scenex.Image href "" "scenex.Image"
click scenex.model._nodes.node.Node href "" "scenex.model._nodes.node.Node"
click scenex.model._base.EventedBase href "" "scenex.model._base.EventedBase"
A 2D image rendered as a textured rectangle.
Image displays a 2D array of intensity values, mapping them to colors using a colormap. The image is rendered as a rectangle in 3D space, with pixels centered at integer coordinates starting from (0, 0). The image supports various rendering options including colormapping, intensity normalization, gamma correction, and interpolation.
The image's geometry spans from (-0.5, -0.5) to (width-0.5, height-0.5), meaning that pixel centers are at integer coordinates. This convention aligns with standard image processing practices.
Examples:
Create a simple grayscale image: >>> import numpy as np >>> data = np.random.rand(100, 100) >>> img = Image(data=data)
Create an image with custom colormap and intensity range: >>> img = Image(data=data, cmap=Colormap("viridis"), clims=(0, 255))
Create a transformed and semi-transparent image: >>> img = Image( ... data=data, ... transform=Transform().translated((10, 20)).scaled((2, 2)), ... opacity=0.7, ... )
Apply gamma correction to brighten dark images: >>> img = Image(data=data, gamma=0.5)
Methods:
-
add_child–Add a child node to this node.
-
iter_parents–Return list of parents starting from this node.
-
model_post_init–Called after the model is initialized.
-
path_to_node–Return two lists describing the path from this node to another.
-
remove_child–Remove a child node from this node. Does not raise if child is missing.
-
transform_to_node–Return Transform that maps from coordinate frame of
selftoother. -
tree_repr–Return an ASCII/Unicode tree representation of self and its descendants.
Attributes:
Source code in src/scenex/model/_nodes/node.py
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | |
add_child
#
add_child(child: AnyNode) -> None
Add a child node to this node.
Source code in src/scenex/model/_nodes/node.py
222 223 224 225 226 227 228 | |
iter_parents
#
Return list of parents starting from this node.
The chain ends at the first node with no parents.
Source code in src/scenex/model/_nodes/node.py
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 | |
model_post_init
#
model_post_init(__context: Any) -> None
Called after the model is initialized.
Source code in src/scenex/model/_base.py
57 58 59 60 61 62 | |
path_to_node
#
Return two lists describing the path from this node to another.
Parameters:
-
(other#instance of Node) –The other node.
Returns:
Notes
The first list starts with this node and ends with the common parent between the endpoint nodes. The second list contains the remainder of the path from the common parent to the specified ending node.
For example, consider the following scenegraph::
A --- B --- C --- D
--- E --- F
Calling D.node_path(F) will return::
([D, C, B], [E, F])
Source code in src/scenex/model/_nodes/node.py
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 | |
remove_child
#
remove_child(child: AnyNode) -> None
Remove a child node from this node. Does not raise if child is missing.
Source code in src/scenex/model/_nodes/node.py
230 231 232 233 234 235 | |
transform_to_node
#
Return Transform that maps from coordinate frame of self to other.
Note that there must be a single path in the scenegraph that connects the two entities; otherwise an exception will be raised.
Parameters:
-
(other#instance of Node) –The other node.
Returns:
-
transform(instance of ChainTransform) –The transform.
Source code in src/scenex/model/_nodes/node.py
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | |
tree_repr
#
tree_repr() -> str
Return an ASCII/Unicode tree representation of self and its descendants.
Source code in src/scenex/model/_nodes/node.py
383 384 385 386 387 | |
Layout
#
Bases: EventedBase
flowchart TD
scenex.Layout[Layout]
scenex.model._base.EventedBase[EventedBase]
scenex.model._base.EventedBase --> scenex.Layout
click scenex.Layout href "" "scenex.Layout"
click scenex.model._base.EventedBase href "" "scenex.model._base.EventedBase"
Style model for a view's border, padding, background, and placement.
Placement is defined by four independent strings — one for each
edge of the view rect — resolved against the canvas size at render time via
Canvas.rect_for. Accepted strings must follow CSS conventions:
"XX%"— a percentage of the canvas size along that axis"XXpx"— a fixed pixel offset; negative values are measured from the far edge (right / bottom)
Examples:
Full canvas (default)::
Layout()
Fixed 400x300 region starting at (50, 50)::
Layout(x_start="50px", x_end="450px", y_start="50px", y_end="350px")
Left half, full height::
Layout(x_end="50%")
Notes
The layout follows this box model::
x_start x_end
| |
v v
y_start-> +--------------------------------+
| margin |
| +--------------------------+ |
| | border | |
| | +--------------------+ | |
| | | padding | | |
| | | +--------------+ | | |
| | | | content | | | |
| | | | | | | |
| | | +--------------+ | | |
| | +--------------------+ | |
| +--------------------------+ |
y_end-> +--------------------------------+
Methods:
-
model_post_init–Called after the model is initialized.
Attributes:
-
x(tuple[Coord, Coord]) –The x-axis start/end as a tuple.
-
y(tuple[Coord, Coord]) –The y-axis start/end as a tuple.
model_post_init
#
model_post_init(__context: Any) -> None
Called after the model is initialized.
Source code in src/scenex/model/_base.py
57 58 59 60 61 62 | |
Letterbox
#
Bases: ResizePolicy
flowchart TD
scenex.Letterbox[Letterbox]
scenex.model._view.ResizePolicy[ResizePolicy]
scenex.model._base.EventedBase[EventedBase]
scenex.model._view.ResizePolicy --> scenex.Letterbox
scenex.model._base.EventedBase --> scenex.model._view.ResizePolicy
click scenex.Letterbox href "" "scenex.Letterbox"
click scenex.model._view.ResizePolicy href "" "scenex.model._view.ResizePolicy"
click scenex.model._base.EventedBase href "" "scenex.model._base.EventedBase"
Maintain content aspect ratio on resize via letterboxing/pillarboxing.
The Letterbox strategy preserves the original aspect ratio of the camera's projection when the view is resized. When the view's aspect ratio differs from the content's aspect ratio, the projection is expanded in the narrower dimension to ensure all original content remains visible with black bars (letterboxing for wide views, pillarboxing for tall views).
The strategy tracks resize sequences (e.g., dragging a window corner) by storing the camera's projection as a reference at the start of that sequence. At any point during the sequence, the projection matrix is expanded in either width or height to retain the rectangle of that reference projection. A new sequence is defined by a change in the projection matrix, either programmatically made or through user input, signalled by a camera projection matrix different from that set during the last resize operation.
Examples:
Create a view with letterbox resizing: >>> from scenex.utils.projections import orthographic >>> view = View( ... camera=Camera(projection=orthographic(100, 100, 100)), ... on_resize=Letterbox(), ... )
When view is resized to 200x100 pixels, the projection expands horizontally to maintain the 1:1 aspect ratio, showing more content on the sides rather than stretching the image.
Notes
This approach follows the conventions of vispy's PanZoomCamera and pygfx's PerspectiveCamera. The projection matrix scales are inversely proportional to the displayed region: smaller scale values show more content.
See Also
ResizePolicy : Base class for resize policies View : View class that uses resize strategies Camera : Camera class with projection property
Methods:
-
handle_resize–Handle view resize by adjusting projection to maintain aspect ratio.
-
model_post_init–Called after the model is initialized.
handle_resize
#
handle_resize(view: View) -> None
Handle view resize by adjusting projection to maintain aspect ratio.
Source code in src/scenex/model/_view.py
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 | |
model_post_init
#
model_post_init(__context: Any) -> None
Called after the model is initialized.
Source code in src/scenex/model/_base.py
57 58 59 60 61 62 | |
Line
#
Bases: Node
flowchart TD
scenex.Line[Line]
scenex.model._nodes.node.Node[Node]
scenex.model._base.EventedBase[EventedBase]
scenex.model._nodes.node.Node --> scenex.Line
scenex.model._base.EventedBase --> scenex.model._nodes.node.Node
click scenex.Line href "" "scenex.Line"
click scenex.model._nodes.node.Node href "" "scenex.model._nodes.node.Node"
click scenex.model._base.EventedBase href "" "scenex.model._base.EventedBase"
A polyline defined by connected vertices.
Line renders a sequence of connected line segments by drawing from each vertex to the next. The line can be colored uniformly or with per-vertex colors that smoothly interpolate along the path. Lines support width control and anti-aliasing for smooth rendering.
Vertices can be 2D or 3D coordinates. For 2D vertices, the z-coordinate is assumed to be 0, placing the line in the xy-plane.
Examples:
Create a simple line connecting several points: >>> import numpy as np >>> vertices = np.array([[0, 0], [10, 5], [20, 0]]) >>> line = Line( ... vertices=vertices, ... color=UniformColor(color=Color("red")), ... )
Create a line with per-vertex colors: >>> vertices = np.array([[0, 0], [10, 10], [20, 0]]) >>> colors = [Color("red"), Color("green"), Color("blue")] >>> line = Line( ... vertices=vertices, ... color=VertexColors(color=colors), ... width=2.0, ... )
Create a 3D line: >>> vertices = np.array([[0, 0, 0], [10, 5, 3], [20, 0, 6]]) >>> line = Line(vertices=vertices, width=3.0)
Methods:
-
add_child–Add a child node to this node.
-
iter_parents–Return list of parents starting from this node.
-
model_post_init–Called after the model is initialized.
-
passes_through–Check if the ray passes through this line.
-
path_to_node–Return two lists describing the path from this node to another.
-
remove_child–Remove a child node from this node. Does not raise if child is missing.
-
transform_to_node–Return Transform that maps from coordinate frame of
selftoother. -
tree_repr–Return an ASCII/Unicode tree representation of self and its descendants.
Attributes:
Source code in src/scenex/model/_nodes/node.py
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | |
add_child
#
add_child(child: AnyNode) -> None
Add a child node to this node.
Source code in src/scenex/model/_nodes/node.py
222 223 224 225 226 227 228 | |
iter_parents
#
Return list of parents starting from this node.
The chain ends at the first node with no parents.
Source code in src/scenex/model/_nodes/node.py
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 | |
model_post_init
#
model_post_init(__context: Any) -> None
Called after the model is initialized.
Source code in src/scenex/model/_base.py
57 58 59 60 61 62 | |
passes_through
#
Check if the ray passes through this line.
Parameters:
-
(ray#Ray) –The ray to test for intersection.
Returns:
-
float | None–The distance to the closest intersection, or None if no intersection.
Source code in src/scenex/model/_nodes/line.py
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | |
path_to_node
#
Return two lists describing the path from this node to another.
Parameters:
-
(other#instance of Node) –The other node.
Returns:
Notes
The first list starts with this node and ends with the common parent between the endpoint nodes. The second list contains the remainder of the path from the common parent to the specified ending node.
For example, consider the following scenegraph::
A --- B --- C --- D
--- E --- F
Calling D.node_path(F) will return::
([D, C, B], [E, F])
Source code in src/scenex/model/_nodes/node.py
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 | |
remove_child
#
remove_child(child: AnyNode) -> None
Remove a child node from this node. Does not raise if child is missing.
Source code in src/scenex/model/_nodes/node.py
230 231 232 233 234 235 | |
transform_to_node
#
Return Transform that maps from coordinate frame of self to other.
Note that there must be a single path in the scenegraph that connects the two entities; otherwise an exception will be raised.
Parameters:
-
(other#instance of Node) –The other node.
Returns:
-
transform(instance of ChainTransform) –The transform.
Source code in src/scenex/model/_nodes/node.py
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | |
tree_repr
#
tree_repr() -> str
Return an ASCII/Unicode tree representation of self and its descendants.
Source code in src/scenex/model/_nodes/node.py
383 384 385 386 387 | |
Mesh
#
Bases: Node
flowchart TD
scenex.Mesh[Mesh]
scenex.model._nodes.node.Node[Node]
scenex.model._base.EventedBase[EventedBase]
scenex.model._nodes.node.Node --> scenex.Mesh
scenex.model._base.EventedBase --> scenex.model._nodes.node.Node
click scenex.Mesh href "" "scenex.Mesh"
click scenex.model._nodes.node.Node href "" "scenex.model._nodes.node.Node"
click scenex.model._base.EventedBase href "" "scenex.model._base.EventedBase"
A 3D surface mesh composed of triangular faces.
Mesh represents a 3D surface defined by vertices and triangular faces. Each face is specified by three indices into the vertex array, forming a triangle. The mesh uses counter-clockwise winding order: for a face (v1, v2, v3), the normal vector points in the direction of (v2 - v1) x (v3 - v1).
Meshes support ray-triangle intersection testing using the Möller-Trumbore algorithm, enabling efficient picking and interaction.
Examples:
Create a simple triangle mesh: >>> import numpy as np >>> vertices = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0]]) >>> faces = np.array([[0, 1, 2]]) >>> mesh = Mesh( ... vertices=vertices, ... faces=faces, ... color=UniformColor(color=Color("blue")), ... )
Create a square made of two triangles: >>> vertices = np.array( ... [ ... [0, 0, 0], # bottom-left ... [1, 0, 0], # bottom-right ... [1, 1, 0], # top-right ... [0, 1, 0], # top-left ... ] ... ) >>> faces = np.array( ... [ ... [0, 1, 2], # first triangle ... [0, 2, 3], # second triangle ... ] ... ) >>> mesh = Mesh(vertices=vertices, faces=faces)
Notes
Face winding order (counter-clockwise) determines which side of the triangle is considered the "front" face. The normal vector for face (v1, v2, v3) points in the direction of (v2 - v1) x (v3 - v1).
Methods:
-
add_child–Add a child node to this node.
-
intersecting_faces–Find all faces that intersect with the given ray.
-
iter_parents–Return list of parents starting from this node.
-
model_post_init–Called after the model is initialized.
-
passes_through–Check if the ray passes through this mesh and return the closest distance.
-
path_to_node–Return two lists describing the path from this node to another.
-
remove_child–Remove a child node from this node. Does not raise if child is missing.
-
transform_to_node–Return Transform that maps from coordinate frame of
selftoother. -
tree_repr–Return an ASCII/Unicode tree representation of self and its descendants.
Attributes:
Source code in src/scenex/model/_nodes/node.py
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | |
add_child
#
add_child(child: AnyNode) -> None
Add a child node to this node.
Source code in src/scenex/model/_nodes/node.py
222 223 224 225 226 227 228 | |
intersecting_faces
#
Find all faces that intersect with the given ray.
Uses the Möller-Trumbore intersection algorithm, vectorized over all triangles. Adapted from https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm#C++_implementation
Parameters:
-
(ray#Ray) –The ray to test for intersections.
Returns:
-
list[tuple[int, float]]–A list of tuples containing (face_index, distance) for each intersecting face. Sorted by distance (closest first).
Source code in src/scenex/model/_nodes/mesh.py
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | |
iter_parents
#
Return list of parents starting from this node.
The chain ends at the first node with no parents.
Source code in src/scenex/model/_nodes/node.py
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 | |
model_post_init
#
model_post_init(__context: Any) -> None
Called after the model is initialized.
Source code in src/scenex/model/_base.py
57 58 59 60 61 62 | |
passes_through
#
Check if the ray passes through this mesh and return the closest distance.
Parameters:
-
(ray#Ray) –The ray to test for intersection.
Returns:
-
float | None–The distance to the closest intersection, or None if no intersection.
Source code in src/scenex/model/_nodes/mesh.py
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | |
path_to_node
#
Return two lists describing the path from this node to another.
Parameters:
-
(other#instance of Node) –The other node.
Returns:
Notes
The first list starts with this node and ends with the common parent between the endpoint nodes. The second list contains the remainder of the path from the common parent to the specified ending node.
For example, consider the following scenegraph::
A --- B --- C --- D
--- E --- F
Calling D.node_path(F) will return::
([D, C, B], [E, F])
Source code in src/scenex/model/_nodes/node.py
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 | |
remove_child
#
remove_child(child: AnyNode) -> None
Remove a child node from this node. Does not raise if child is missing.
Source code in src/scenex/model/_nodes/node.py
230 231 232 233 234 235 | |
transform_to_node
#
Return Transform that maps from coordinate frame of self to other.
Note that there must be a single path in the scenegraph that connects the two entities; otherwise an exception will be raised.
Parameters:
-
(other#instance of Node) –The other node.
Returns:
-
transform(instance of ChainTransform) –The transform.
Source code in src/scenex/model/_nodes/node.py
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | |
tree_repr
#
tree_repr() -> str
Return an ASCII/Unicode tree representation of self and its descendants.
Source code in src/scenex/model/_nodes/node.py
383 384 385 386 387 | |
Node
#
Bases: EventedBase
flowchart TD
scenex.Node[Node]
scenex.model._base.EventedBase[EventedBase]
scenex.model._base.EventedBase --> scenex.Node
click scenex.Node href "" "scenex.Node"
click scenex.model._base.EventedBase href "" "scenex.model._base.EventedBase"
Base class for all nodes in the scene graph.
Node is the fundamental building block of scenex's scene graph architecture. Nodes form a hierarchical tree structure where each node can have a parent and children, creating a parent-child relationship that propagates transformations, visibility, and other properties through the graph.
Nodes are abstract and should not be instantiated directly. Use concrete subclasses like Image, Points, Line, Mesh, Scene, or Camera instead.
The scene graph hierarchy allows: - Hierarchical transformations: A node's transform is relative to its parent - Property inheritance: Visibility and opacity affect all descendants - Spatial relationships: Nodes can find paths to other nodes in the graph - Event handling: Interactive nodes can respond to user input
Notes
Do not instantiate Node directly. Use concrete subclasses instead.
Methods:
-
add_child–Add a child node to this node.
-
iter_parents–Return list of parents starting from this node.
-
model_post_init–Called after the model is initialized.
-
passes_through–Returns the depth t at which the provided ray intersects this node.
-
path_to_node–Return two lists describing the path from this node to another.
-
remove_child–Remove a child node from this node. Does not raise if child is missing.
-
transform_to_node–Return Transform that maps from coordinate frame of
selftoother. -
tree_repr–Return an ASCII/Unicode tree representation of self and its descendants.
Attributes:
Source code in src/scenex/model/_nodes/node.py
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | |
add_child
#
add_child(child: AnyNode) -> None
Add a child node to this node.
Source code in src/scenex/model/_nodes/node.py
222 223 224 225 226 227 228 | |
iter_parents
#
Return list of parents starting from this node.
The chain ends at the first node with no parents.
Source code in src/scenex/model/_nodes/node.py
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 | |
model_post_init
#
model_post_init(__context: Any) -> None
Called after the model is initialized.
Source code in src/scenex/model/_base.py
57 58 59 60 61 62 | |
passes_through
#
Returns the depth t at which the provided ray intersects this node.
The ray, in this case, is defined by R(t) = ray_origin + ray_direction * t, where t>=0
Parameters:
-
(ray#Ray) –The ray passing through the scene
Returns:
-
t(float | None) –The depth t at which the ray intersects the node, or None if it never intersects.
Source code in src/scenex/model/_nodes/node.py
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | |
path_to_node
#
Return two lists describing the path from this node to another.
Parameters:
-
(other#instance of Node) –The other node.
Returns:
Notes
The first list starts with this node and ends with the common parent between the endpoint nodes. The second list contains the remainder of the path from the common parent to the specified ending node.
For example, consider the following scenegraph::
A --- B --- C --- D
--- E --- F
Calling D.node_path(F) will return::
([D, C, B], [E, F])
Source code in src/scenex/model/_nodes/node.py
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 | |
remove_child
#
remove_child(child: AnyNode) -> None
Remove a child node from this node. Does not raise if child is missing.
Source code in src/scenex/model/_nodes/node.py
230 231 232 233 234 235 | |
transform_to_node
#
Return Transform that maps from coordinate frame of self to other.
Note that there must be a single path in the scenegraph that connects the two entities; otherwise an exception will be raised.
Parameters:
-
(other#instance of Node) –The other node.
Returns:
-
transform(instance of ChainTransform) –The transform.
Source code in src/scenex/model/_nodes/node.py
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | |
tree_repr
#
tree_repr() -> str
Return an ASCII/Unicode tree representation of self and its descendants.
Source code in src/scenex/model/_nodes/node.py
383 384 385 386 387 | |
Orbit
#
Bases: CameraController
flowchart TD
scenex.Orbit[Orbit]
scenex.model._nodes.camera.CameraController[CameraController]
scenex.model._nodes.camera.CameraController --> scenex.Orbit
click scenex.Orbit href "" "scenex.Orbit"
click scenex.model._nodes.camera.CameraController href "" "scenex.model._nodes.camera.CameraController"
3D orbit controller for rotating around a focal point.
Orbit provides intuitive 3D navigation for perspective views by allowing the camera to rotate around a fixed center point while maintaining its distance.
The strategy uses spherical coordinates to control camera position: - Azimuth: Rotation around the polar axis (typically Z), controlling left/right movement around the scene - Elevation: Angle from the polar axis, controlling up/down viewing angle - Distance: Radius from the center point, controlled by zooming
During rotation, foreground objects (between the camera and the center) move in the direction of mouse movement, providing intuitive control where the visible scene appears to rotate under the mouse.
The right mouse button allows panning the orbit center itself, enabling exploration of large scenes by moving the focal point while maintaining the camera's viewing angle and distance.
Attributes:
-
center(tuple[float, float, float]) –The point in 3D space around which the camera orbits. This is the focal point that remains stationary during rotation. Default is (0, 0, 0).
-
polar_axis(tuple[float, float, float]) –The axis defining the "up" direction for orbit calculations. Azimuth rotations occur around this axis. Default is (0, 0, 1) for Z-up orientation.
Examples:
Orbit around the origin: >>> import scenex as snx >>> from scenex.utils import projections >>> # Create a perspective camera... >>> camera = snx.Camera( ... interactive=True, ... projection=projections.perspective(fov=70, near=1, far=1000), ... ) >>> # ...positioned along the X axis... >>> camera.transform = snx.Transform().translated((100, 0, 0)) >>> # ...looking at the origin... >>> camera.look_at((0, 0, 0), up=(0, 0, 1)) >>> # ...that orbits around the origin >>> camera.controller = snx.Orbit(center=(0, 0, 0))
Orbit around a data volume's center: >>> import numpy as np >>> my_data = np.random.rand(100, 100, 100).astype(np.float32) >>> volume = snx.Volume(data=my_data) >>> center = np.mean(volume.bounding_box, axis=0) >>> # Create a perspective camera... >>> camera = snx.Camera( ... interactive=True, ... projection=projections.perspective(fov=70, near=1, far=1000), ... ) >>> # ...positioned along the X axis from the volume center... >>> camera.transform = ( ... snx.Transform().translated(center).translated((100, 0, 0)) ... ) >>> # ...looking at the center... >>> camera.look_at(center, up=(0, 0, 1)) >>> # ...that orbits around the center >>> camera.controller = snx.Orbit(center=center)
Custom polar axis for Y-up scenes: >>> camera = snx.Camera( ... controller=snx.Orbit(center=(0, 0, 0), polar_axis=(0, 1, 0)), ... interactive=True, ... )
Interactions
- Left mouse drag: Orbit/rotate the camera around the center point
- Right mouse drag: Pan the orbit center (translates the focal point)
- Mouse wheel: Zoom toward/away from center (change radius)
Notes
Elevation is automatically clamped to [0°, 180°] to prevent the camera from going upside down. Without this clamping, the camera could rotate past the polar axis, causing horizontal mouse movement to make the foreground rotate in the opposite direction to the actual mouse movement.
See Also
PanZoom : 2D pan and zoom controller for orthographic views CameraController : Base class for camera controllers Camera : Camera class with controller field Camera.look_at : Method to orient camera toward a point
Methods:
-
handle_event–Handle mouse and wheel events to orbit the camera.
handle_event
#
Handle mouse and wheel events to orbit the camera.
Source code in src/scenex/model/_nodes/camera.py
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 | |
PanZoom
#
Bases: CameraController
flowchart TD
scenex.PanZoom[PanZoom]
scenex.model._nodes.camera.CameraController[CameraController]
scenex.model._nodes.camera.CameraController --> scenex.PanZoom
click scenex.PanZoom href "" "scenex.PanZoom"
click scenex.model._nodes.camera.CameraController href "" "scenex.model._nodes.camera.CameraController"
2D pan and zoom controller for orthographic views.
PanZoom provides intuitive mouse-based navigation for 2D scenes and orthographic projections.
The strategy operates in two complementary ways: - Panning (left mouse drag): Modifies camera.transform to translate the camera position, maintaining the scene coordinates under the cursor. - Zooming (mouse wheel): Modifies camera.projection to scale the view, then adjusts camera.transform to keep the zoom centered on the cursor position.
Optional axis locking allows constraining interaction to horizontal or vertical movement only
Attributes:
-
lock_x(bool) –If True, prevent horizontal panning and zooming. Movement is constrained to the vertical axis only. Default is False.
-
lock_y(bool) –If True, prevent vertical panning and zooming. Movement is constrained to the horizontal axis only. Default is False.
Examples:
Standard 2D pan and zoom: >>> camera = Camera(controller=PanZoom(), interactive=True)
Lock horizontal movement for vertical scrolling only: >>> camera = Camera(controller=PanZoom(lock_x=True), interactive=True)
Create an image viewer with pan/zoom: >>> import numpy as np >>> import scenex as snx >>> from scenex.utils import projections >>> my_data = np.random.rand(512, 512).astype(np.float32) >>> view = snx.View( ... scene=snx.Scene(children=[snx.Image(data=my_data)]), ... camera=snx.Camera( ... controller=snx.PanZoom(), ... interactive=True, ... ), ... ) >>> projections.zoom_to_fit(view=view, type="orthographic")
See Also
Orbit : 3D orbit controller for perspective views CameraController : Base class for camera controllers Camera : Camera class with controller field
Methods:
-
handle_event–Handle mouse and wheel events to pan/zoom the camera.
handle_event
#
Handle mouse and wheel events to pan/zoom the camera.
Source code in src/scenex/model/_nodes/camera.py
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 | |
Points
#
Bases: Node
flowchart TD
scenex.Points[Points]
scenex.model._nodes.node.Node[Node]
scenex.model._base.EventedBase[EventedBase]
scenex.model._nodes.node.Node --> scenex.Points
scenex.model._base.EventedBase --> scenex.model._nodes.node.Node
click scenex.Points href "" "scenex.Points"
click scenex.model._nodes.node.Node href "" "scenex.model._nodes.node.Node"
click scenex.model._base.EventedBase href "" "scenex.model._base.EventedBase"
A collection of point markers rendered at specified coordinates.
Points displays symbols (markers) at 2D or 3D coordinates in the scene. Each point is rendered using a specified symbol shape (disc, square, star, etc.) with customizable size, face color, and edge styling. Points support different scaling modes to control whether they maintain constant screen size or scale with the scene.
Examples:
Create simple point markers: >>> import numpy as np >>> vertices = np.random.rand(100, 2) * 100 >>> points = Points( ... vertices=vertices, ... size=5, ... face_color=UniformColor(color=Color("red")), ... )
Create points with custom symbols and styling: >>> points = Points( ... vertices=vertices, ... symbol="star", ... size=20, ... face_color=UniformColor(color=Color("yellow")), ... edge_color=UniformColor(color=Color("orange")), ... edge_width=2, ... )
Create fixed-size points that don't scale with zoom: >>> points = Points( ... vertices=vertices, ... size=10, ... scaling="fixed", ... face_color=UniformColor(color=Color("blue")), ... )
Create 3D points: >>> vertices_3d = np.random.rand(50, 3) * 100 >>> points = Points(vertices=vertices_3d, symbol="diamond", size=15)
Methods:
-
add_child–Add a child node to this node.
-
iter_parents–Return list of parents starting from this node.
-
model_post_init–Called after the model is initialized.
-
path_to_node–Return two lists describing the path from this node to another.
-
remove_child–Remove a child node from this node. Does not raise if child is missing.
-
transform_to_node–Return Transform that maps from coordinate frame of
selftoother. -
tree_repr–Return an ASCII/Unicode tree representation of self and its descendants.
Attributes:
Source code in src/scenex/model/_nodes/node.py
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | |
add_child
#
add_child(child: AnyNode) -> None
Add a child node to this node.
Source code in src/scenex/model/_nodes/node.py
222 223 224 225 226 227 228 | |
iter_parents
#
Return list of parents starting from this node.
The chain ends at the first node with no parents.
Source code in src/scenex/model/_nodes/node.py
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 | |
model_post_init
#
model_post_init(__context: Any) -> None
Called after the model is initialized.
Source code in src/scenex/model/_base.py
57 58 59 60 61 62 | |
path_to_node
#
Return two lists describing the path from this node to another.
Parameters:
-
(other#instance of Node) –The other node.
Returns:
Notes
The first list starts with this node and ends with the common parent between the endpoint nodes. The second list contains the remainder of the path from the common parent to the specified ending node.
For example, consider the following scenegraph::
A --- B --- C --- D
--- E --- F
Calling D.node_path(F) will return::
([D, C, B], [E, F])
Source code in src/scenex/model/_nodes/node.py
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 | |
remove_child
#
remove_child(child: AnyNode) -> None
Remove a child node from this node. Does not raise if child is missing.
Source code in src/scenex/model/_nodes/node.py
230 231 232 233 234 235 | |
transform_to_node
#
Return Transform that maps from coordinate frame of self to other.
Note that there must be a single path in the scenegraph that connects the two entities; otherwise an exception will be raised.
Parameters:
-
(other#instance of Node) –The other node.
Returns:
-
transform(instance of ChainTransform) –The transform.
Source code in src/scenex/model/_nodes/node.py
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | |
tree_repr
#
tree_repr() -> str
Return an ASCII/Unicode tree representation of self and its descendants.
Source code in src/scenex/model/_nodes/node.py
383 384 385 386 387 | |
ResizePolicy
#
Bases: EventedBase
flowchart TD
scenex.ResizePolicy[ResizePolicy]
scenex.model._base.EventedBase[EventedBase]
scenex.model._base.EventedBase --> scenex.ResizePolicy
click scenex.ResizePolicy href "" "scenex.ResizePolicy"
click scenex.model._base.EventedBase href "" "scenex.model._base.EventedBase"
Base class defining how a view adapts to changes in its layout dimensions.
A ResizePolicy is invoked automatically when a view's layout dimensions change, providing a hook to adjust any aspect of the view in response. While the most common use case is adjusting the camera's projection matrix to maintain aspect ratio or fit content, strategies have full access to the view and can modify the camera, scene, layout, or any other properties as needed.
Strategies are attached to View instances and called whenever the layout width or height changes, whether from user interaction (window resize, splitter drag) or programmatic updates.
Examples:
Maintain aspect ratio when view resizes: >>> view = View(camera=Camera(), on_resize=Letterbox())
No resize behavior (omit the on_resize parameter): >>> view = View(camera=Camera())
See Also
Letterbox : Resize strategy that maintains aspect ratio View : View class that uses resize strategies Camera : Camera class with projection property
Methods:
-
handle_resize–Respond to view layout dimension changes.
-
model_post_init–Called after the model is initialized.
handle_resize
abstractmethod
#
Respond to view layout dimension changes.
This method is called automatically when the view's layout dimensions change. Implementations have full access to the view and can modify any of its properties.
Parameters:
Source code in src/scenex/model/_view.py
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 | |
model_post_init
#
model_post_init(__context: Any) -> None
Called after the model is initialized.
Source code in src/scenex/model/_base.py
57 58 59 60 61 62 | |
Scene
#
Bases: Node
flowchart TD
scenex.Scene[Scene]
scenex.model._nodes.node.Node[Node]
scenex.model._base.EventedBase[EventedBase]
scenex.model._nodes.node.Node --> scenex.Scene
scenex.model._base.EventedBase --> scenex.model._nodes.node.Node
click scenex.Scene href "" "scenex.Scene"
click scenex.model._nodes.node.Node href "" "scenex.model._nodes.node.Node"
click scenex.model._base.EventedBase href "" "scenex.model._base.EventedBase"
The root container node for a scene graph.
Scene is a specialized Node that serves as the root of a scene graph hierarchy. It contains all the visual elements (Images, Points, Lines, Meshes, etc.) and cameras that make up a complete 3D scene. While functionally identical to a Node, Scene provides semantic clarity that this is the top-level container.
A Scene is typically associated with a View, which pairs it with a Camera to define what is rendered and how. Multiple views can display the same scene from different camera perspectives.
Examples:
Create a scene with visual elements: >>> import numpy as np >>> my_image = np.random.rand(100, 100).astype(np.float32) >>> my_points = np.random.rand(100, 3).astype(np.float32) >>> scene = Scene( ... children=[ ... Image(data=my_image), ... Points( ... vertices=my_points, ... face_color=UniformColor(color=Color("red")), ... ), ... ] ... )
Create an empty scene and later add children: >>> scene = Scene() >>> scene.add_child(Image(data=my_image)) >>> scene.add_child(Points(vertices=my_points))
Create a hierarchical scene with nested nodes: >>> grandchild = Image(data=my_image) >>> parent = Points(vertices=my_points) >>> scene = Scene(children=[parent])
Use a scene with a view: >>> view = View(scene=scene, camera=Camera()) >>> canvas = Canvas(views=[view])
Notes
Scene inherits all Node attributes and methods including transform, visible, opacity, and children management. The scene itself does not have visual representation; it only serves as a container for renderable nodes.
Methods:
-
add_child–Add a child node to this node.
-
iter_parents–Return list of parents starting from this node.
-
model_post_init–Called after the model is initialized.
-
passes_through–Returns the depth t at which the provided ray intersects this node.
-
path_to_node–Return two lists describing the path from this node to another.
-
remove_child–Remove a child node from this node. Does not raise if child is missing.
-
transform_to_node–Return Transform that maps from coordinate frame of
selftoother. -
tree_repr–Return an ASCII/Unicode tree representation of self and its descendants.
Attributes:
Source code in src/scenex/model/_nodes/scene.py
67 68 69 70 71 72 | |
add_child
#
add_child(child: AnyNode) -> None
Add a child node to this node.
Source code in src/scenex/model/_nodes/node.py
222 223 224 225 226 227 228 | |
iter_parents
#
Return list of parents starting from this node.
The chain ends at the first node with no parents.
Source code in src/scenex/model/_nodes/node.py
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 | |
model_post_init
#
model_post_init(__context: Any) -> None
Called after the model is initialized.
Source code in src/scenex/model/_base.py
57 58 59 60 61 62 | |
passes_through
#
Returns the depth t at which the provided ray intersects this node.
The ray, in this case, is defined by R(t) = ray_origin + ray_direction * t, where t>=0
Parameters:
-
(ray#Ray) –The ray passing through the scene
Returns:
-
t(float | None) –The depth t at which the ray intersects the node, or None if it never intersects.
Source code in src/scenex/model/_nodes/node.py
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | |
path_to_node
#
Return two lists describing the path from this node to another.
Parameters:
-
(other#instance of Node) –The other node.
Returns:
Notes
The first list starts with this node and ends with the common parent between the endpoint nodes. The second list contains the remainder of the path from the common parent to the specified ending node.
For example, consider the following scenegraph::
A --- B --- C --- D
--- E --- F
Calling D.node_path(F) will return::
([D, C, B], [E, F])
Source code in src/scenex/model/_nodes/node.py
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 | |
remove_child
#
remove_child(child: AnyNode) -> None
Remove a child node from this node. Does not raise if child is missing.
Source code in src/scenex/model/_nodes/node.py
230 231 232 233 234 235 | |
transform_to_node
#
Return Transform that maps from coordinate frame of self to other.
Note that there must be a single path in the scenegraph that connects the two entities; otherwise an exception will be raised.
Parameters:
-
(other#instance of Node) –The other node.
Returns:
-
transform(instance of ChainTransform) –The transform.
Source code in src/scenex/model/_nodes/node.py
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | |
tree_repr
#
tree_repr() -> str
Return an ASCII/Unicode tree representation of self and its descendants.
Source code in src/scenex/model/_nodes/node.py
383 384 385 386 387 | |
Text
#
Bases: Node
flowchart TD
scenex.Text[Text]
scenex.model._nodes.node.Node[Node]
scenex.model._base.EventedBase[EventedBase]
scenex.model._nodes.node.Node --> scenex.Text
scenex.model._base.EventedBase --> scenex.model._nodes.node.Node
click scenex.Text href "" "scenex.Text"
click scenex.model._nodes.node.Node href "" "scenex.model._nodes.node.Node"
click scenex.model._base.EventedBase href "" "scenex.model._base.EventedBase"
A text label positioned in 3D world space.
The text maintains a constant screen size regardless of camera zoom or distance, making it useful for labels, annotations, and markers. The text is positioned at the node's transformed origin point.
Examples:
Create a simple text label: >>> text = Text(text="Hello World", color=Color("white"), size=14)
Notes
Text maintains constant screen size, not world size. The font size is specified in pixels and does not scale with camera zoom or distance from the viewer.
Methods:
-
add_child–Add a child node to this node.
-
iter_parents–Return list of parents starting from this node.
-
model_post_init–Called after the model is initialized.
-
passes_through–Returns the depth t at which the provided ray intersects this node.
-
path_to_node–Return two lists describing the path from this node to another.
-
remove_child–Remove a child node from this node. Does not raise if child is missing.
-
transform_to_node–Return Transform that maps from coordinate frame of
selftoother. -
tree_repr–Return an ASCII/Unicode tree representation of self and its descendants.
Attributes:
Source code in src/scenex/model/_nodes/node.py
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | |
add_child
#
add_child(child: AnyNode) -> None
Add a child node to this node.
Source code in src/scenex/model/_nodes/node.py
222 223 224 225 226 227 228 | |
iter_parents
#
Return list of parents starting from this node.
The chain ends at the first node with no parents.
Source code in src/scenex/model/_nodes/node.py
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 | |
model_post_init
#
model_post_init(__context: Any) -> None
Called after the model is initialized.
Source code in src/scenex/model/_base.py
57 58 59 60 61 62 | |
passes_through
#
Returns the depth t at which the provided ray intersects this node.
The ray, in this case, is defined by R(t) = ray_origin + ray_direction * t, where t>=0
Parameters:
-
(ray#Ray) –The ray passing through the scene
Returns:
-
t(float | None) –The depth t at which the ray intersects the node, or None if it never intersects.
Source code in src/scenex/model/_nodes/node.py
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | |
path_to_node
#
Return two lists describing the path from this node to another.
Parameters:
-
(other#instance of Node) –The other node.
Returns:
Notes
The first list starts with this node and ends with the common parent between the endpoint nodes. The second list contains the remainder of the path from the common parent to the specified ending node.
For example, consider the following scenegraph::
A --- B --- C --- D
--- E --- F
Calling D.node_path(F) will return::
([D, C, B], [E, F])
Source code in src/scenex/model/_nodes/node.py
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 | |
remove_child
#
remove_child(child: AnyNode) -> None
Remove a child node from this node. Does not raise if child is missing.
Source code in src/scenex/model/_nodes/node.py
230 231 232 233 234 235 | |
transform_to_node
#
Return Transform that maps from coordinate frame of self to other.
Note that there must be a single path in the scenegraph that connects the two entities; otherwise an exception will be raised.
Parameters:
-
(other#instance of Node) –The other node.
Returns:
-
transform(instance of ChainTransform) –The transform.
Source code in src/scenex/model/_nodes/node.py
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | |
tree_repr
#
tree_repr() -> str
Return an ASCII/Unicode tree representation of self and its descendants.
Source code in src/scenex/model/_nodes/node.py
383 384 385 386 387 | |
Transform
#
Bases: RootModel
flowchart TD
scenex.Transform[Transform]
click scenex.Transform href "" "scenex.Transform"
A 4x4 homogeneous transformation matrix for 3D affine transformations.
Transformations use homogeneous coordinates, where 3D points (x, y, z) are represented as 4-vectors (x, y, z, 1). This enables affine transformations (translation, rotation, scaling) to be represented as matrix multiplication.
The Transform class is immutable (frozen). Operations like translated(), rotated(), and scaled() return new Transform instances rather than modifying the original.
Examples:
Create an identity transform: >>> transform = Transform()
Translate an object: >>> transform = Transform().translated((10, 20, 30))
Rotate 45 degrees around the z-axis: >>> transform = Transform().rotated(45, axis=(0, 0, 1))
Scale uniformly by 2x: >>> transform = Transform().scaled((2, 2, 2))
Chain multiple transformations: >>> transform = ( ... Transform() ... .translated((10, 0, 0)) ... .rotated(45, (0, 0, 1)) ... .scaled((2, 2, 2)) ... )
Rotate around a specific point: >>> transform = Transform().rotated(90, axis=(0, 0, 1), about=(10, 10, 0))
Transform coordinates: >>> points = np.array([[0, 0, 0], [1, 1, 1]]) >>> transformed = transform.map(points)
Combine two transforms: >>> transform1 = Transform().translated((5, 0, 0)) >>> transform2 = Transform().scaled((2, 2, 2)) >>> combined = transform1 @ transform2
Invert a transform: >>> inverse = transform.inv()
Notes
- Transformations are applied in the order they are chained
- The transform is immutable; all operations return new instances
- Uses right-multiplication convention: point @ matrix
- Default rotation axis is (0, 0, 1) - the z-axis
Methods:
-
chain–Chain multiple transforms together.
-
dot–Return the dot product of this transform with another.
-
imap–Inverse map coordinates.
-
inv–Return the inverse of the transform.
-
is_null–Return True if the transform is the identity matrix.
-
map–Map coordinates.
-
rotated–Return new transform, rotated some angle about a given axis.
-
scaled–Return new transform, scaled about a given origin.
-
translated–Return new transform, translated by pos.
Attributes:
chain
classmethod
#
Chain multiple transforms together.
Parameters:
Returns:
-
transform(Transform) –Chained transform.
Source code in src/scenex/model/_transform.py
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 | |
dot
#
Return the dot product of this transform with another.
Source code in src/scenex/model/_transform.py
173 174 175 176 177 | |
imap
#
Inverse map coordinates.
Parameters:
Returns:
-
coords(ndarray) –Coordinates.
Source code in src/scenex/model/_transform.py
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 | |
inv
#
inv() -> Transform
Return the inverse of the transform.
Source code in src/scenex/model/_transform.py
184 185 186 | |
is_null
#
is_null() -> bool
Return True if the transform is the identity matrix.
Source code in src/scenex/model/_transform.py
163 164 165 | |
map
#
Map coordinates.
Parameters:
Returns:
-
coords(ndarray) –Coordinates.
Source code in src/scenex/model/_transform.py
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 | |
rotated
#
Return new transform, rotated some angle about a given axis.
The rotation is applied after the transformations already present in the matrix.
Parameters:
-
(angle#float) –The angle of rotation, in degrees.
-
(axis#array - like, default:(0, 0, 1)) –The x, y and z coordinates of the axis vector to rotate around. By default, will rotate around the z-axis:
(0, 0, 1). -
(about#array - like or None, default:None) –The x, y and z coordinates to rotate around. If None, will rotate around the origin (0, 0, 0).
Source code in src/scenex/model/_transform.py
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | |
scaled
#
scaled(scale_factor: ArrayLike, center: ArrayLike | None = None) -> Transform
Return new transform, scaled about a given origin.
The scaling is applied after the transformations already present in the matrix.
Parameters:
-
(scale_factor#array - like) –Scale factors along x, y and z axes.
-
(center#array - like or None, default:None) –The x, y and z coordinates to scale around. If None, (0, 0, 0) will be used.
Source code in src/scenex/model/_transform.py
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | |
translated
#
Return new transform, translated by pos.
The translation is applied after the transformations already present in the matrix.
Parameters:
Source code in src/scenex/model/_transform.py
188 189 190 191 192 193 194 195 196 197 198 199 200 | |
UniformColor
#
UniformColor(**data: Any)
Bases: ColorModel
flowchart TD
scenex.UniformColor[UniformColor]
scenex.model._color.ColorModel[ColorModel]
scenex.model._color.ColorModel --> scenex.UniformColor
click scenex.UniformColor href "" "scenex.UniformColor"
click scenex.model._color.ColorModel href "" "scenex.model._color.ColorModel"
Uniform coloring strategy for scene nodes.
This model applies a single color to the entire geometry (mesh, line, points, etc).
The color field is a single Color instance (e.g. Color("red")).
Examples:
Uniform coloring: >>> from cmap import Color >>> from scenex import UniformColor >>> model = UniformColor(color=Color("red"))
Source code in src/scenex/model/_color.py
23 24 25 26 | |
VertexColors
#
VertexColors(**data: Any)
Bases: ColorModel
flowchart TD
scenex.VertexColors[VertexColors]
scenex.model._color.ColorModel[ColorModel]
scenex.model._color.ColorModel --> scenex.VertexColors
click scenex.VertexColors href "" "scenex.VertexColors"
click scenex.model._color.ColorModel href "" "scenex.model._color.ColorModel"
Per-vertex coloring strategy for mesh, line, or points nodes.
This model applies a different color to each vertex.
The color field is a sequence of Color instances, one for each vertex.
Examples:
Per-vertex coloring: >>> from cmap import Color >>> from scenex import VertexColors >>> model = VertexColors( ... color=[Color("yellow"), Color("purple"), Color("cyan")] ... )
Source code in src/scenex/model/_color.py
23 24 25 26 | |
View
#
Bases: EventedBase
flowchart TD
scenex.View[View]
scenex.model._base.EventedBase[EventedBase]
scenex.model._base.EventedBase --> scenex.View
click scenex.View href "" "scenex.View"
click scenex.model._base.EventedBase href "" "scenex.model._base.EventedBase"
A rectangular viewport that displays a scene through a camera.
A View represents a rectangular area on a canvas that renders a scene graph through a specific camera perspective. Each view associates exactly one scene with one camera, defining what is displayed and how it is viewed. Multiple views can exist on a single canvas, each potentially showing different scenes or the same scene from different camera angles.
Examples:
Create a view with a scene containing an image: >>> import numpy as np >>> my_array = np.random.rand(100, 100).astype(np.float32) >>> scene = Scene(children=[Image(data=my_array)]) >>> view = View(scene=scene, camera=Camera())
Create a view with interactive camera and letterbox resizing: >>> view = View( ... scene=scene, ... camera=Camera(controller=PanZoom(), interactive=True), ... on_resize=Letterbox(), ... )
Add a view to a canvas: >>> canvas = Canvas() >>> canvas.views.append(view)
Methods:
-
filter_event–Filters the event.
-
model_post_init–Post-initialization hook for the model.
-
render–Render the view to an array.
-
set_event_filter–Registers a callable to filter events.
-
to_ray–Compute the world-space ray for a canvas position within this view.
Attributes:
-
canvas(Canvas | None) –The canvas that the view is on.
-
content_rect(tuple[int, int, int, int] | None) –Pixel content rect (x, y, width, height) of this view, excluding insets.
-
rect(tuple[int, int, int, int] | None) –Pixel rect (x, y, width, height) of this view on its canvas.
content_rect
property
#
Pixel content rect (x, y, width, height) of this view, excluding insets.
None if the view is not on a canvas.
rect
property
#
Pixel rect (x, y, width, height) of this view on its canvas.
None if the view is not on a canvas.
filter_event
#
Filters the event.
This method allows the larger view to react to events that: 1. Require summarization of multiple smaller event responses. 2. Could not be picked up by a node (e.g. mouse leaving an image).
Note the name has parity with Node.filter_event, but there's not much filtering going on.
Parameters:
-
(event#Event) –An event occurring in the view.
Returns:
-
bool–True iff the event should not be propagated to other handlers.
Source code in src/scenex/model/_view.py
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | |
model_post_init
#
model_post_init(__context: Any) -> None
Post-initialization hook for the model.
Source code in src/scenex/model/_view.py
90 91 92 93 94 95 96 97 98 99 100 | |
render
#
render() -> ndarray
Render the view to an array.
Source code in src/scenex/model/_view.py
203 204 205 206 207 | |
set_event_filter
#
Registers a callable to filter events.
Parameters:
-
(callable#Callable[[Event], bool] | None) –A callable that takes an Event and returns True if the event was handled, False otherwise. Passing None is equivalent to removing any existing filter. By returning True, the callable indicates that the event has been handled and should not be propagated to subsequent handlers.
Returns:
-
Callable[[Event], bool] | None–The previous event filter, or None if there was no filter.
-
Note the name has parity with Node.filter_event, but there's not much filtering– -
going on.–
Source code in src/scenex/model/_view.py
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | |
to_ray
#
to_ray(canvas_pos: tuple[float, float]) -> Ray | None
Compute the world-space ray for a canvas position within this view.
Parameters:
Returns:
-
Ray | None–The world-space Ray, or None if this view has no canvas.
Notes
If canvas_pos falls outside this view's rectangle, a Ray is still
returned — it simply points outside the visible frustum. Callers that
need to restrict to within-bounds positions should check
view.content_rect before calling this method.
Source code in src/scenex/model/_view.py
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | |
Volume
#
Bases: Image
flowchart TD
scenex.Volume[Volume]
scenex.model._nodes.image.Image[Image]
scenex.model._nodes.node.Node[Node]
scenex.model._base.EventedBase[EventedBase]
scenex.model._nodes.image.Image --> scenex.Volume
scenex.model._nodes.node.Node --> scenex.model._nodes.image.Image
scenex.model._base.EventedBase --> scenex.model._nodes.node.Node
click scenex.Volume href "" "scenex.Volume"
click scenex.model._nodes.image.Image href "" "scenex.model._nodes.image.Image"
click scenex.model._nodes.node.Node href "" "scenex.model._nodes.node.Node"
click scenex.model._base.EventedBase href "" "scenex.model._base.EventedBase"
A 3D volumetric dataset rendered with volume rendering techniques.
Volume extends Image to support 3D volumetric data. Unlike images which are 2D arrays, volumes are 3D arrays of intensity values that are rendered using volume rendering techniques like maximum intensity projection (MIP) or isosurface rendering.
The volume uses ZYX dimension ordering, meaning data.shape = (depth, height, width). Like Image, the volume supports colormapping, intensity normalization, and gamma correction. The rendering mode determines how the 3D data is projected onto the 2D viewing plane.
Examples:
Create a volume with MIP rendering: >>> import numpy as np >>> data = np.random.rand(50, 100, 100) # ZYX dimensions >>> volume = Volume(data=data, render_mode="mip")
Create a volume with custom colormap and intensity range: >>> volume = Volume( ... data=data, ... cmap=Colormap("viridis"), ... clims=(0, 1), ... render_mode="iso", ... )
Notes
Volume inherits all Image attributes including data, cmap, clims, gamma, and interpolation. The data should be a 3D array with shape (depth, height, width) following ZYX convention.
Methods:
-
add_child–Add a child node to this node.
-
iter_parents–Return list of parents starting from this node.
-
model_post_init–Called after the model is initialized.
-
path_to_node–Return two lists describing the path from this node to another.
-
remove_child–Remove a child node from this node. Does not raise if child is missing.
-
transform_to_node–Return Transform that maps from coordinate frame of
selftoother. -
tree_repr–Return an ASCII/Unicode tree representation of self and its descendants.
Attributes:
Source code in src/scenex/model/_nodes/node.py
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | |
add_child
#
add_child(child: AnyNode) -> None
Add a child node to this node.
Source code in src/scenex/model/_nodes/node.py
222 223 224 225 226 227 228 | |
iter_parents
#
Return list of parents starting from this node.
The chain ends at the first node with no parents.
Source code in src/scenex/model/_nodes/node.py
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 | |
model_post_init
#
model_post_init(__context: Any) -> None
Called after the model is initialized.
Source code in src/scenex/model/_base.py
57 58 59 60 61 62 | |
path_to_node
#
Return two lists describing the path from this node to another.
Parameters:
-
(other#instance of Node) –The other node.
Returns:
Notes
The first list starts with this node and ends with the common parent between the endpoint nodes. The second list contains the remainder of the path from the common parent to the specified ending node.
For example, consider the following scenegraph::
A --- B --- C --- D
--- E --- F
Calling D.node_path(F) will return::
([D, C, B], [E, F])
Source code in src/scenex/model/_nodes/node.py
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 | |
remove_child
#
remove_child(child: AnyNode) -> None
Remove a child node from this node. Does not raise if child is missing.
Source code in src/scenex/model/_nodes/node.py
230 231 232 233 234 235 | |
transform_to_node
#
Return Transform that maps from coordinate frame of self to other.
Note that there must be a single path in the scenegraph that connects the two entities; otherwise an exception will be raised.
Parameters:
-
(other#instance of Node) –The other node.
Returns:
-
transform(instance of ChainTransform) –The transform.
Source code in src/scenex/model/_nodes/node.py
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | |
tree_repr
#
tree_repr() -> str
Return an ASCII/Unicode tree representation of self and its descendants.
Source code in src/scenex/model/_nodes/node.py
383 384 385 386 387 | |
native
#
Get the native widget for the given canvas.
Parameters:
-
(canvas#Canvas) –The canvas for which to get the native widget.
-
(create#bool, default:True) –Whether to create adaptors if they do not already exist. Defaults to
True.
Returns:
-
Any–The native widget associated with the canvas.
Raises:
-
KeyError–If no adaptor yet exists for
canvasandcreate=False.
Notes
This function is a convenience that retrieves the native widget from the first adaptor associated with the canvas. If multiple adaptors are present, it returns the native widget from the first one found.
Source code in src/scenex/util.py
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | |
run
#
run() -> None
Start the GUI event loop to display interactive visualizations.
This function enters the native event loop of the graphics backend, allowing interactive visualizations to respond to user input (mouse, keyboard) and remain visible. The function blocks until the visualization window is closed.
Call this function after creating and showing your visualizations with show().
It is only needed for desktop applications; in Jupyter notebooks, visualizations
are displayed automatically without calling run().
Examples:
Basic usage with a scene: >>> import numpy as np >>> import scenex as snx >>> scene = snx.Scene( ... children=[snx.Image(data=np.random.rand(100, 100).astype(np.float32))] ... ) >>> snx.show(scene) Canvas(...) >>> snx.run() # Blocks until window is closed
Create multiple views and run: >>> canvas = snx.Canvas(views=[snx.View(), snx.View()]) >>> canvas.visible = True >>> snx.run()
Notes
- This function blocks execution until all visualization windows are closed
- Not needed in Jupyter notebooks or other interactive environments
- Must be called after
show()has been used to create visualizations - The event loop handles user interactions like pan, zoom, and picking
Source code in src/scenex/util.py
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 | |
set_cursor
#
Set the cursor for the given canvas.
Parameters:
-
(canvas#Canvas) –The canvas on which to set the cursor.
-
(cursor#CursorType) –The type of cursor to set.
Notes
Practically and generally speaking, setting the cursor is an app-level concern. Unfortunately, setting the cursor often requires access to a native widget, meaning any scenex abstractions for setting the cursor will need as input the canvas model or a derivative adaptor. Proper separation of concerns suggests that the app-level API should just take the native widget. This function is a convenience that performs the intermediate steps to get the native widget from a canvas model.
Source code in src/scenex/util.py
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 | |
show
#
Display a visualization by creating a canvas and making it visible.
This is the primary function for creating and displaying scenex visualizations. It accepts nodes, views, or canvases, automatically wrapping them in the necessary container objects and creating the appropriate backend adaptors.
The function automatically fits the camera view to show all visible content and
makes the canvas window visible. After calling show(), use run() to enter
the event loop (in desktop applications) or continue working (in notebooks).
Parameters:
-
(obj#Node | View | Canvas) –The object to visualize: - Node (Image, Points, Line, etc.): Wrapped in Scene and View automatically - Scene: Wrapped in a View with a default Camera - View: Placed on a new Canvas - Canvas: Displayed directly (already contains Views)
-
(backend#str | None, default:None) –Graphics backend to use ("pygfx" or "vispy"). If None, uses the backend specified by
use(),SCENEX_CANVAS_BACKENDenvironment variable, or auto-detection. Default isNone.
Returns:
-
Canvas–The canvas containing the visualization. Can be used to further manipulate the display or access the created views.
Examples:
Show a simple image: >>> import numpy as np >>> import scenex as snx >>> data = np.random.rand(100, 100).astype(np.float32) >>> img = snx.Image(data=data) >>> canvas = snx.show(img) >>> snx.run()
Edit the returned canvas: >>> from cmap import Color >>> canvas.background_color = Color("white") >>> canvas.width = 800 >>> snx.run()
Notes
- The camera is automatically zoomed to fit all visible content with 90% coverage
- Canvas size defaults to the view's layout dimensions
- Call
run()aftershow()to enter the event loop in desktop applications - In Jupyter notebooks, visualizations appear automatically without
run()
Source code in src/scenex/util.py
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | |
use
#
use(backend: KnownBackend | None = None) -> None
Set the graphics backend for rendering scenex visualizations.
This function allows you to explicitly select which graphics library (backend) scenex should use for rendering. It is the goal of scenex to support the full range of model API for each backend.
If not called, scenex will automatically select an arbitrary available backend. You can also set the backend via the SCENEX_CANVAS_BACKEND environment variable.
Parameters:
-
(backend#Literal['pygfx', 'vispy'] | None, default:None) –The graphics backend to use: - "pygfx": Modern WebGPU-based renderer with advanced features - "vispy": OpenGL-based renderer with broad compatibility - None: Reset to auto-detection
Raises:
-
ValueError–If the specified backend is not one of the known backends.
Examples:
Use pygfx backend explicitly: >>> import scenex as snx >>> snx.use("pygfx") # doctest: +SKIP >>> canvas = snx.show(snx.View())
Use vispy backend: >>> snx.use("vispy") # doctest: +SKIP >>> canvas = snx.show(snx.Scene())
Reset to auto-detection: >>> snx.use(None)
Notes
The backend selection follows this priority order: 1. Backend specified by this function 2. SCENEX_CANVAS_BACKEND environment variable 3. Auto-detection (pygfx preferred, then vispy)
This function should be called before creating any visualizations.
Source code in src/scenex/adaptors/_auto.py
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | |