Problem
The interactive rux view viewer uses VTK's default trackball camera, which makes it hard to present or record a scene. There is no way to continuously orbit the scene at a steady pace, which is useful for reviewing geometry or capturing footage.
Goal
Add a turntable mode that:
- Rotates the camera continuously around the scene's vertical axis (world Z)
- Keeps the focal point fixed at the scene centre
- Can be toggled on/off and paused with keyboard shortcuts
Implementation context
Current viewer architecture
The viewer is built on pcl::visualization::PCLVisualizer, which wraps VTK's vtkInteractorStyleTrackballCamera. The main loop runs in apps/rux/src/processing_observer.cpp:671 via viewer->spinOnce(100ms). No custom interactor style exists today.
Camera manipulation uses the VTK API directly — see apps/rux/src/view/panorama_handler.cpp:135–146 for a working example:
cam->SetPosition(x, y, z);
cam->SetFocalPoint(cx, cy, cz);
cam->SetViewUp(0, 0, 1);
Orbit math (reference)
libs/reusex/src/visualize/render_view.cpp:612–625 already implements a circular orbit:
const double azimuth = 2.0 * pi * orbit_index / orbit_count;
const double elevation = elevation_deg * pi / 180.0;
cam->SetPosition(
center[0] + reach * cos(azimuth) * cos(elevation),
center[1] + reach * sin(azimuth) * cos(elevation),
center[2] + reach * sin(elevation));
cam->SetViewUp(0.0, 0.0, 1.0);
For turntable mode the same formula is used with elevation fixed (e.g. 20°) and azimuth incrementing each frame.
State pattern
Follow the PanoramaState pattern (apps/rux/include/view/panorama_handler.hpp:24–35) — a small struct held by shared_ptr and shared with callbacks:
struct TurntableState {
bool active = false;
bool spinning = true;
double azimuth = 0.0; // radians
double angular_velocity = 0.3; // rad/s
double radius = 5.0;
double elevation_deg = 20.0;
std::array<double,3> center = {0,0,0};
};
Compute center and radius from the scene bounding box on activation (see render_view.cpp:580–582 for the bbox query pattern).
Integration points
-
State + keyboard callback — add to apps/rux/src/view.cpp after the pano_state block (around line 130). Register keys via viewer->registerKeyboardCallback(...) — see the Escape key handler at lines 220–238 for the pattern.
Suggested keys: t = toggle on/off, Space = pause/resume spin, [/] = decrease/increase speed.
-
Per-frame update — enqueue a camera update task each tick. The task queue is drained every 100 ms in processing_observer.cpp:671. Use observer.viewer_enqueue_task(...) (same pattern as other renderers).
-
Help text — add turntable controls to the help overlay following the pattern in apps/rux/src/view/component_renderer.cpp:106–145.
-
No interactor changes needed — when turntable is active, manually calling SetPosition/SetFocalPoint each frame overrides VTK's trackball. When it is off, normal mouse interaction resumes automatically.
Acceptance criteria
Key files
| File |
Lines |
Purpose |
apps/rux/src/processing_observer.cpp |
645–676 |
Viewer init and main spin loop |
apps/rux/src/view.cpp |
105–319 |
Callback registration, pano_state pattern to follow |
apps/rux/src/view/panorama_handler.cpp |
135–146 |
Camera SetPosition/FocalPoint/ViewUp example |
apps/rux/include/view/panorama_handler.hpp |
24–35 |
State struct pattern |
libs/reusex/src/visualize/render_view.cpp |
612–625 |
Orbit math to adapt |
apps/rux/src/view/component_renderer.cpp |
106–145 |
Help text addition point |
apps/rux/include/view/label_renderer.hpp |
77–173 |
Keyboard callback template pattern |
Problem
The interactive
rux viewviewer uses VTK's default trackball camera, which makes it hard to present or record a scene. There is no way to continuously orbit the scene at a steady pace, which is useful for reviewing geometry or capturing footage.Goal
Add a turntable mode that:
Implementation context
Current viewer architecture
The viewer is built on
pcl::visualization::PCLVisualizer, which wraps VTK'svtkInteractorStyleTrackballCamera. The main loop runs inapps/rux/src/processing_observer.cpp:671viaviewer->spinOnce(100ms). No custom interactor style exists today.Camera manipulation uses the VTK API directly — see
apps/rux/src/view/panorama_handler.cpp:135–146for a working example:Orbit math (reference)
libs/reusex/src/visualize/render_view.cpp:612–625already implements a circular orbit:For turntable mode the same formula is used with
elevationfixed (e.g. 20°) andazimuthincrementing each frame.State pattern
Follow the
PanoramaStatepattern (apps/rux/include/view/panorama_handler.hpp:24–35) — a small struct held byshared_ptrand shared with callbacks:Compute
centerandradiusfrom the scene bounding box on activation (seerender_view.cpp:580–582for the bbox query pattern).Integration points
State + keyboard callback — add to
apps/rux/src/view.cppafter thepano_stateblock (around line 130). Register keys viaviewer->registerKeyboardCallback(...)— see the Escape key handler at lines 220–238 for the pattern.Suggested keys:
t= toggle on/off,Space= pause/resume spin,[/]= decrease/increase speed.Per-frame update — enqueue a camera update task each tick. The task queue is drained every 100 ms in
processing_observer.cpp:671. Useobserver.viewer_enqueue_task(...)(same pattern as other renderers).Help text — add turntable controls to the help overlay following the pattern in
apps/rux/src/view/component_renderer.cpp:106–145.No interactor changes needed — when turntable is active, manually calling
SetPosition/SetFocalPointeach frame overrides VTK's trackball. When it is off, normal mouse interaction resumes automatically.Acceptance criteria
ttoggles turntable mode; viewer prints a brief status messageSpacepauses/resumes spinning while in turntable mode[/]decrease/increase angular speedKey files
apps/rux/src/processing_observer.cppapps/rux/src/view.cppapps/rux/src/view/panorama_handler.cppapps/rux/include/view/panorama_handler.hpplibs/reusex/src/visualize/render_view.cppapps/rux/src/view/component_renderer.cppapps/rux/include/view/label_renderer.hpp