feat(observatory): share native result replay and expose saved TGS layers
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user