Pinhole camera from calibration — pinhole_camera(K, R, t, image_size=…)
builds a PinholeCamera from an OpenCV-style calibration, rendered with an
off-center projection so the principal point is honored, with a fit policy
("fit" letterbox / "fill" stretch).
Camera view descriptor — CameraView bundles a camera, a per-pane
lock set (CameraLock), and an optional background_image, passed to
SceneView(camera_view=…).
Camera image background — CameraView(background_image=ImageData(...))
renders the image as a full-viewport NDC background behind the 3D scene,
letterboxed to match the camera's fit policy.
Per-pane entity visibility — SceneView(hide=…) / SceneView(show=…)
filter which scene entities a pane renders, so e.g. a frustum shows only in an
overview pane.
Frustum entity — a visualization-only geometry entity (no multivector)
with Frustum.from_camera(camera) and a FrustumStyle (fill, plane opacity,
line thickness), rendered in the live viewer and HTML export.
2D viewport navigation — CameraView(navigation="2d", …) switches a pane
to dolly zoom + screen-space pan (no orbit) with cursor-anchored zoom and a
per-pane controls button mapping. An optional viewport (ViewportConfig)
folds a {zoom, pan} crop window into the pinhole projection and its
background image so they stay pixel-locked. Visualizer.set_viewport(view, …)
(per-pane) and set_viewport(scene_name=…) / VizSceneHandle.set_viewport(...)
(scene default) set or reset it at runtime.
Calibrated-camera example (BOP T-LESS) — pinhole_calibrated.py bundles
one real T-LESS training image with its pinhole calibration and the
ground-truth 6D pose of object 1 (CC BY 4.0), rendering the GT bounding box
pixel-accurately over the photo (left) with a frustum + GT box overview and an
attribution annotation (right).
Coordinate frames + camera calibration — Matrix (a plain numeric square
matrix) with a MatrixProvider protocol and a Matrix.from_R_t(R, t) rigid
constructor, CoordinateFrame/OpenCVFrame axis conventions, and
CameraCalibration(K, R, t, image_size, frame, units) which translate
read-in OpenCV-style calibration into the right-handed internal world and a
PinholeCamera, plus world_to_camera() / camera_to_world() transform
matrices; frames satisfy MatrixProvider, so set_transform(frame) remaps
OpenCV-frame data through the scene graph.
Data background image orientation — a data-path background image is no
longer vertically flipped: DataTexture defaults to flipY = false, which the
NDC background now flips so data and URL backgrounds line up with the 3D
projection.
Circle center update — moving a Circle's center is now a transform
patch applied to the entity's wrapping group (placement), not a content patch
the renderer must re-apply; this fixes the stale-mesh bug where a center-only
change was silently dropped.
Canonical frame + transform placement — every scene entity now renders in
a canonical frame (linear primitives along +Y, planar primitives in XY/+Z,
volumes at the origin) with all placement and rotation carried by a single
per-entity Transform whose rotation is a quaternion (the wire
transform.rotation: [x, y, z, w]). Transform and the transform math moved
into pytanga.geometry (transform.py / transforms.py); viz keeps thin
re-export shims and Transform gains @/* operator overloads for Point
and Direction.
Shape-only serialization — the serializer no longer emits
center/normal/axis/origin/direction/vertex/rotation/
startDirection; placement rides on the node transform, and set_entity
diffs shape vs placement (configurable epsilon) so float noise doesn't
recreate meshes. The glTF exporter reads the node transform directly.
Frustum re-parameterization — Frustum now carries intrinsic shape +
placement (origin/axis/horizontal/near/far/far_half_width/
far_half_height) instead of raw corner tuples, so it participates in
transform placement and diffing like the other entities.