feat(observatory): share native result replay and expose saved TGS layers

This commit is contained in:
DCCONSTRUCTIONS
2026-09-03 13:23:56 +03:00
parent 27979854a7
commit 9db29bcdc6
15 changed files with 594 additions and 433 deletions
+35 -19
View File
@@ -45,12 +45,11 @@ class RecordedBlueprintError(RuntimeError):
class _RecordedBlueprintStream:
"""Keep one browser viewport on one mutable Rerun blueprint store.
"""Keep one bounded SDK blueprint source for each browser viewport.
Rerun persists the operator-controlled eye inside the active blueprint
store. Creating and activating a fresh store for every layer toggle drops
that eye and snaps the view back to its fallback camera. This stream keeps
the store identity stable until the operator explicitly requests a reset.
Upstream 0.36.3 activates a clone, not this source store. Explicit refresh
makes layer changes visible but cannot retain the clone's operator eye.
It is opt-in for portable replay pending native camera-state support.
"""
def __init__(
@@ -82,18 +81,24 @@ class _RecordedBlueprintStream:
def render(
self,
blueprint_factory: Callable[[bool], rrb.Blueprint],
blueprint_factory: Callable[[bool, bool], rrb.Blueprint],
*,
follow_trajectory: bool,
plan_view: bool,
reactivate_updates: bool = False,
) -> bytes:
with self._lock:
if self._closed:
raise RecordedBlueprintError("stable blueprint stream is closed")
eye_contract = (follow_trajectory, plan_view)
update_eye_controls = self._eye_contract != eye_contract
blueprint = blueprint_factory(update_eye_controls)
make_active = self._sequence == 0
# Initial admission/reset uses native framing. Explicit presets
# apply to mode transitions, after the viewer has a source cursor.
blueprint = blueprint_factory(update_eye_controls, self._eye_contract is not None)
# Appending rows alone does not refresh upstream's active clone.
# Keep legacy admission unchanged; portable replay opts into
# working layer updates with an explicitly documented eye reset.
make_active = self._sequence == 0 or reactivate_updates
self._blueprint_recording.set_time(
"blueprint",
sequence=self._sequence,
@@ -124,9 +129,9 @@ class _RecordedBlueprintStream:
_MAX_RECORDED_BLUEPRINT_STREAMS = 32
_recorded_blueprint_streams_lock = Lock()
_recorded_blueprint_streams: OrderedDict[
tuple[str, str, str], _RecordedBlueprintStream
] = OrderedDict()
_recorded_blueprint_streams: OrderedDict[tuple[str, str, str], _RecordedBlueprintStream] = (
OrderedDict()
)
def _stable_recorded_blueprint_stream(
@@ -170,8 +175,10 @@ def recorded_blueprint(
show_detections_2d: bool = False,
show_segmentation: bool = False,
show_cuboids_3d: bool = False,
show_costmap: bool = False,
follow_trajectory: bool = False,
update_eye_controls: bool = True,
explicit_spatial_preset: bool = False,
) -> rrb.Blueprint:
accumulation = max(0.0, settings.accumulation_seconds)
time_ranges: list[rr.VisibleTimeRange] | None = None
@@ -220,6 +227,9 @@ def recorded_blueprint(
if plan_view and update_eye_controls
else rrb.EyeControls3D.from_fields(
kind=rrb.Eye3DKind.Orbital if follow_trajectory else None,
position=[16.0, -16.0, 18.0] if explicit_spatial_preset else None,
look_target=[0.0, 0.0, 0.0] if explicit_spatial_preset else None,
eye_up=[0.0, 0.0, 1.0] if explicit_spatial_preset else None,
tracking_entity="/world/sensor_pose" if follow_trajectory else "",
)
if update_eye_controls
@@ -239,6 +249,7 @@ def recorded_blueprint(
# inherits the view's latest-at query and can never turn into an
# object-history trail when the operator widens the cloud window.
"/world/points": point_overrides,
"/world/costmap": rrb.EntityBehavior(visible=show_costmap),
"/world/trajectory": trajectory_overrides,
"/world/perception": rrb.EntityBehavior(visible=show_cuboids_3d),
"/world/perception/lidar": rrb.EntityBehavior(visible=False),
@@ -251,16 +262,13 @@ def recorded_blueprint(
# Log an explicit empty range set so a previous accumulated blueprint
# for this stable view id is cleared instead of surviving in Rerun.
time_ranges=rrb.VisibleTimeRanges([]),
# Follow is a property of the same operator eye, not another view.
# 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.
# Follow and plan configure the native eye, not the point coordinates.
# An empty tracking path clears tracking. Retaining this source view id
# alone does not preserve edits in upstream's activated blueprint clone.
eye_controls=spatial_eye_controls,
)
spatial_view.id = (
RECORDED_SPATIAL_RESET_VIEW_ID
if view_reset_generation
else RECORDED_SPATIAL_VIEW_ID
RECORDED_SPATIAL_RESET_VIEW_ID if view_reset_generation else RECORDED_SPATIAL_VIEW_ID
)
camera_view = rrb.Spatial2DView(
origin="/perception/camera",
@@ -309,6 +317,7 @@ def recorded_blueprint(
visible=settings.show_trajectory,
),
"/world/perception": rrb.EntityBehavior(visible=True),
"/world/costmap": rrb.EntityBehavior(visible=show_costmap),
# The overlay already carries the selected fusion support points;
# its grey diagnostic LiDAR copy would otherwise double-render the
# native cloud from /world/points.
@@ -420,11 +429,15 @@ def recorded_blueprint_rrd(
show_detections_2d: bool = False,
show_segmentation: bool = False,
show_cuboids_3d: bool = False,
show_costmap: bool = False,
follow_trajectory: bool = False,
reactivate_updates: bool = False,
) -> bytes:
"""Serialize a bounded active blueprint update without recorded data."""
def build_blueprint(update_eye_controls: bool) -> rrb.Blueprint:
def build_blueprint(
update_eye_controls: bool, use_spatial_preset: bool = False
) -> rrb.Blueprint:
return recorded_blueprint(
settings,
include_initial_playback_state=False,
@@ -436,8 +449,10 @@ def recorded_blueprint_rrd(
show_detections_2d=show_detections_2d,
show_segmentation=show_segmentation,
show_cuboids_3d=show_cuboids_3d,
show_costmap=show_costmap,
follow_trajectory=follow_trajectory,
update_eye_controls=update_eye_controls,
explicit_spatial_preset=reactivate_updates and use_spatial_preset,
)
payload: bytes | None
@@ -452,6 +467,7 @@ def recorded_blueprint_rrd(
build_blueprint,
follow_trajectory=follow_trajectory,
plan_view=plan_view,
reactivate_updates=reactivate_updates,
)
except RecordedBlueprintError:
raise
+4
View File
@@ -130,6 +130,8 @@ class RecordedBlueprintRequest(StrictApiModel):
show_detections_2d: StrictBool = False
show_segmentation: StrictBool = False
show_cuboids_3d: StrictBool = False
show_costmap: StrictBool = False
reactivate_updates: StrictBool = False
follow_trajectory: StrictBool = False
@@ -980,6 +982,8 @@ def build_session_router(
show_detections_2d=request.show_detections_2d,
show_segmentation=request.show_segmentation,
show_cuboids_3d=request.show_cuboids_3d,
show_costmap=request.show_costmap,
reactivate_updates=request.reactivate_updates,
follow_trajectory=request.follow_trajectory,
)
except SessionNotFoundError as exc: