fix(viewer): preserve follow camera across layer toggles

This commit is contained in:
DCCONSTRUCTIONS
2026-07-23 17:14:32 +03:00
parent a7babf60db
commit ae2ce055c8
7 changed files with 292 additions and 56 deletions
+48 -19
View File
@@ -3,6 +3,7 @@
from __future__ import annotations
from collections import OrderedDict
from collections.abc import Callable
from contextlib import suppress
from threading import Lock
from typing import Any, Literal
@@ -62,6 +63,7 @@ class _RecordedBlueprintStream:
self.view_reset_generation = view_reset_generation
self._lock = Lock()
self._sequence = 0
self._follow_trajectory: bool | None = None
self._closed = False
native = bindings.new_blueprint(
application_id=application_id,
@@ -78,10 +80,20 @@ class _RecordedBlueprintStream:
)
self._transport = rr.binary_stream(self._transport_recording)
def render(self, blueprint: rrb.Blueprint) -> bytes:
def render(
self,
blueprint_factory: Callable[[bool], rrb.Blueprint],
*,
follow_trajectory: bool,
) -> bytes:
with self._lock:
if self._closed:
raise RecordedBlueprintError("stable blueprint stream is closed")
update_eye_controls = (
self._follow_trajectory is None
or self._follow_trajectory != follow_trajectory
)
blueprint = blueprint_factory(update_eye_controls)
self._blueprint_recording.set_time(
"blueprint",
sequence=self._sequence,
@@ -98,6 +110,7 @@ class _RecordedBlueprintStream:
payload = self._transport.read(flush=True, flush_timeout_sec=5.0)
if not payload:
raise RecordedBlueprintError("stable blueprint stream produced no data")
self._follow_trajectory = follow_trajectory
return payload
def close(self) -> None:
@@ -156,6 +169,7 @@ def recorded_blueprint(
show_segmentation: bool = False,
show_cuboids_3d: bool = False,
follow_trajectory: bool = False,
update_eye_controls: bool = True,
) -> rrb.Blueprint:
accumulation = max(0.0, settings.accumulation_seconds)
time_ranges: list[rr.VisibleTimeRange] | None = None
@@ -218,9 +232,13 @@ def recorded_blueprint(
# Keeping the view id stable preserves the current orbit offset when
# tracking is toggled. An explicit empty path clears tracking without
# overwriting the position/look-target saved by user interaction.
eye_controls=rrb.EyeControls3D.from_fields(
kind=rrb.Eye3DKind.Orbital if follow_trajectory else None,
tracking_entity="/world/sensor_pose" if follow_trajectory else "",
eye_controls=(
rrb.EyeControls3D.from_fields(
kind=rrb.Eye3DKind.Orbital if follow_trajectory else None,
tracking_entity="/world/sensor_pose" if follow_trajectory else "",
)
if update_eye_controls
else None
),
)
spatial_view.id = (
@@ -274,9 +292,13 @@ def recorded_blueprint(
# native cloud from /world/points.
"/world/perception/lidar": rrb.EntityBehavior(visible=False),
},
eye_controls=rrb.EyeControls3D.from_fields(
kind=rrb.Eye3DKind.Orbital if follow_trajectory else None,
tracking_entity="/world/sensor_pose" if follow_trajectory else "",
eye_controls=(
rrb.EyeControls3D.from_fields(
kind=rrb.Eye3DKind.Orbital if follow_trajectory else None,
tracking_entity="/world/sensor_pose" if follow_trajectory else "",
)
if update_eye_controls
else None
),
)
perception_3d_view.id = (
@@ -385,17 +407,20 @@ def recorded_blueprint_rrd(
) -> bytes:
"""Serialize a bounded active blueprint update without recorded data."""
blueprint = recorded_blueprint(
settings,
include_initial_playback_state=False,
active_view=active_view,
view_reset_generation=view_reset_generation,
unified_perception=unified_perception,
show_detections_2d=show_detections_2d,
show_segmentation=show_segmentation,
show_cuboids_3d=show_cuboids_3d,
follow_trajectory=follow_trajectory,
)
def build_blueprint(update_eye_controls: bool) -> rrb.Blueprint:
return recorded_blueprint(
settings,
include_initial_playback_state=False,
active_view=active_view,
view_reset_generation=view_reset_generation,
unified_perception=unified_perception,
show_detections_2d=show_detections_2d,
show_segmentation=show_segmentation,
show_cuboids_3d=show_cuboids_3d,
follow_trajectory=follow_trajectory,
update_eye_controls=update_eye_controls,
)
payload: bytes | None
if blueprint_session_id is not None:
try:
@@ -404,12 +429,16 @@ def recorded_blueprint_rrd(
recording_id=recording_id,
blueprint_session_id=blueprint_session_id,
view_reset_generation=view_reset_generation,
).render(blueprint)
).render(
build_blueprint,
follow_trajectory=follow_trajectory,
)
except RecordedBlueprintError:
raise
except Exception as exc:
raise RecordedBlueprintError("failed to serialize stable recorded blueprint") from exc
else:
blueprint = build_blueprint(True)
recording = rr.RecordingStream(
application_id,
recording_id=recording_id,