Part of #83.
Add a 3D-space viewer that renders the whole scene in one 3D world view (not per-camera tiles): the floor/line plan, every camera's position + orientation drawn as a view-cone (frustum), the workstation markers, and the operator's path + skeleton animated over frames. This is the view that makes camera coverage/overlap seeable.
Everything needed is already in the manifest: each Camera carries intrinsics + world->camera R/t (camera centre C = -R^T t), and the manifest has per-frame ground-truth 3D positions.
Scope
- New viewer, e.g.
scripts/view_scene_3d.py (or src/multicam_sim/viz3d.py + a thin script), consuming a manifest (+ optional order sidecar).
- Draw: floor/ground grid; each camera as a small pyramid frustum pointing along its optical axis (derive centre/axes from
R,t,K); workstation markers (from topology / station positions); the operator's 3D trajectory; the COCO-17 skeleton at the current frame.
- Animate across frames (slider or auto-play) OR export a rotating GIF/MP4, matching the lazy-import + headless-fallback pattern already used in
scripts/record_multiview.py and dsl/render.py (configure_headless).
- Backend decision to make in the issue/PR: Rerun (interactive orbit, best UX; extra dep) vs matplotlib 3D (
mplot3d, zero heavy deps, exports GIF). Recommend matplotlib-3D for a dependency-light default with an optional Rerun path. Keep it OUT of the core package import path and OUT of CI's required deps (lazy import, like the other viz).
Acceptance
python scripts/view_scene_3d.py <manifest.json> renders/exports a 3D-space view of the assembly-line scene showing all cameras' frustums, stations, and the operator moving station-to-station.
- Runs on macOS with no GPU; heavy/optional deps imported lazily, never at package import; not required by CI.
- A tiny smoke test (headless export produces a non-empty image), consistent with repo test style.
Pointers
src/multicam_sim/cameras.py (Camera/Intrinsics, R/t conventions), scripts/record_multiview.py (lazy-import + contact-sheet/GIF pattern), src/multicam_sim/dsl/depth.py (configure_headless), manifest schema in src/multicam_sim/manifest.py.
Part of #83.
Add a 3D-space viewer that renders the whole scene in one 3D world view (not per-camera tiles): the floor/line plan, every camera's position + orientation drawn as a view-cone (frustum), the workstation markers, and the operator's path + skeleton animated over frames. This is the view that makes camera coverage/overlap seeable.
Everything needed is already in the manifest: each
Cameracarries intrinsics + world->cameraR/t(camera centreC = -R^T t), and the manifest has per-frame ground-truth 3D positions.Scope
scripts/view_scene_3d.py(orsrc/multicam_sim/viz3d.py+ a thin script), consuming a manifest (+ optional order sidecar).R,t,K); workstation markers (from topology / station positions); the operator's 3D trajectory; the COCO-17 skeleton at the current frame.scripts/record_multiview.pyanddsl/render.py(configure_headless).mplot3d, zero heavy deps, exports GIF). Recommend matplotlib-3D for a dependency-light default with an optional Rerun path. Keep it OUT of the core package import path and OUT of CI's required deps (lazy import, like the other viz).Acceptance
python scripts/view_scene_3d.py <manifest.json>renders/exports a 3D-space view of the assembly-line scene showing all cameras' frustums, stations, and the operator moving station-to-station.Pointers
src/multicam_sim/cameras.py(Camera/Intrinsics,R/tconventions),scripts/record_multiview.py(lazy-import + contact-sheet/GIF pattern),src/multicam_sim/dsl/depth.py(configure_headless), manifest schema insrc/multicam_sim/manifest.py.