Skip to main content

dalaran-blueprint

Design a Dalaran blueprint from the data, then iterate on it from headless screenshots. Read this when laying out a recording or dataset in the viewer, designing a default blueprint, or deciding which views show which entities. Covers archetype-to-view mapping, layout reasoning, the rrb construction API, the contents grammar, and the screenshot loop.

Quellinformationen

Repository
Flaminis/Dalaran
Letzte Quellaktivität
7. August 2026 um 09:48
Erkannte Sprache von SKILL.md
Englisch
Sterne
736
Forks
31

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
dalaran-blueprint
description
Design a Dalaran blueprint from the data, then iterate on it from headless screenshots. Read this when laying out a recording or dataset in the viewer, designing a default blueprint, or deciding which views show which entities. Covers archetype-to-view mapping, layout reasoning, the rrb construction API, the contents grammar, and the screenshot loop.
user_invocable
true
allowed-tools
Read, Grep, Bash, WebFetch
# Dalaran blueprint A blueprint decides how a recording is shown; the recording decides what exists. Read the data, design a first layout, then **iterate from screenshots** until it reads at a glance. The constructors are discoverable (`dir(rrb)`, `help(rrb.Spatial3DView)`); this skill covers what you can't guess. Import as `import dalaran.blueprint as rrb`. ## 1. Read the data Enumerate every `(entity_path, archetype)` pair first — the archetype picks the view, the entity path scopes it. For a dataset (see `dalaran-catalog-queries`): ```python for c in dataset.schema().component_columns(): print(c.entity_path, c.archetype, c.component_name) ``` For a local recording, stream it with `RrdReader` and read `entity_path` and the archetype off each chunk (see `dalaran-chunk-processing`). ## 2. Map archetype to view | Archetype family | View | | ------------------------------------------------------------------------------------- | --------------------------------------------------- | | `Points2D`, `Image`, `EncodedImage`, `Boxes2D`, `LineStrips2D`, `Pinhole` projections | `Spatial2DView` | | `Points3D`, `Mesh3D`, `Boxes3D`, `LineStrips3D`, `Transform3D`, `Asset3D` | `Spatial3DView` | | `Scalars`, `SeriesLines`, `SeriesPoints` | `TimeSeriesView` | | `TextLog` / `TextDocument` / `BarChart` | `TextLogView` / `TextDocumentView` / `BarChartView` | | `Tensor`, `DepthImage` (heatmap) | `TensorView` | | `GeoPoints`, `GeoLineStrings` | `MapView` | | `GraphNodes`, `GraphEdges` | `GraphView` | | tabular / catalog data | `DataframeView` | ## 3. Group entities into views This step is just deciding which entities share a view. Sizing and placement are mechanics you tune by eye in the screenshot loop, not rules to settle up front. - **Group by shared path prefix.** Entities under a common prefix usually belong in one view. Don't give every entity its own pane: dozens of raw message entities go in one `DataframeView`, or get left out, rather than a pane each. - **One spatial frame per spatial view.** A whole `Transform3D` tree goes in one `Spatial3DView` at the common ancestor; a `Pinhole` camera gets its own `Spatial2DView` rooted at the camera so images inherit the projection. - **Collapse scalars.** Many `Scalars` under a prefix → one `TimeSeriesView` over the prefix, not one each. Split only when value ranges clash. - **`column_shares`/`row_shares` are relative weights** on a container's children, equal by default. Tune them from the screenshots. ## 4. Construct it Containers (`Grid`, `Horizontal`, `Vertical`, `Tabs`) hold views. Default a flat set to `Grid`; use the others for a deliberate split, `Tabs` for alternatives competing for one slot (left/right/depth cameras). **Always set an explicit `origin` and `name`** — `origin` defaults to `/`, which dumps the whole tree into one view (the usual cause of an unreadable blob). ```python blueprint = rrb.Blueprint( rrb.Horizontal( rrb.Spatial3DView(origin="/world", name="Scene"), rrb.Vertical( rrb.Spatial2DView(origin="/world/camera", name="Camera"), rrb.TimeSeriesView(origin="/sensors", name="Sensors"), ), column_shares=[3, 2], ), rrb.TextLogView(origin="/logs", name="Logs"), collapse_panels=True, ) ``` `contents` defaults to `"$origin/**"`. Scope a view with include/exclude rules, e.g. `contents=["+ $origin/**", "- $origin/internal/**"]`. A bare line is an include; `/**` is the only wildcard (matches a subtree). Most-specific rule wins, ties go to the last, unmatched paths are excluded. **Coordinate frames.** A spatial view only renders entities it can place relative to its target frame. `Transform3D`/`Pinhole` logged on entities compose down the tree, so `origin` is enough. But **named frames** (`CoordinateFrame(frame=...)`, common in ROS/MCAP) live in a separate frame graph — point the view at a frame the data occupies via `spatial_information=rrb.SpatialInformation(target_frame="<frame>")`. The tell is an empty 3D view with "No transform path from `<frame>`…" errors: `origin="/"` targets the root `tf#/`, which connects to nothing if the tf tree was never materialized. Read the `CoordinateFrame:frame` values, target the one the main 3D content sits in, and exclude entities in unconnected frames. ## 5. Iterate from screenshots Spawn the viewer and load the recording once, then re-send blueprints into the same viewer; each send + screenshot is one cheap iteration. Render, look, revise. The blueprint binds to the data by store identity, **both application id and recording id**. A loaded `.dlr` keeps its own identity, so build the stream from the dlr's values (set both; `recording_id` otherwise defaults to a random one). Mismatch either and the viewer keeps blueprint and data as separate recordings and never applies the blueprint. ```python import time import dalaran as rr import dalaran.blueprint as rrb from dalaran.experimental import ViewerClient, RrdReader store = RrdReader("segment.dlr").recordings()[0] # the dlr's application id + recording id with ViewerClient.spawn(headless=True, port=9879) as viewer: rec = rr.RecordingStream(store.application_id, recording_id=store.recording_id) rec.connect_grpc(url=viewer.url) rec.log_file_from_path("segment.dlr") def shot(blueprint, path): rec.send_blueprint(blueprint, make_active=True, make_default=True) time.sleep(2) # let the import finish and a frame render; bump if a view is blank viewer.save_screenshot(path) # view_id=view.id for a single view shot(blueprint_v1, "bp_v1.png") # Read bp_v1.png, revise, send the next one into the same viewer. shot(blueprint_v2, "bp_v2.png") ``` To bake a finished blueprint in instead, pass `default_blueprint=` to `rr.init` / `spawn` / `connect_grpc` / `save`, or `blueprint=` to `notebook_show`. ## Gotchas - A `DataframeView` shows "Unknown timeline" without a query: `query=rrb.archetypes.DataframeQuery(timeline="<timeline>", apply_latest_at=True)`. - A view back blank? The importer may not have finished (bump the settle) or the cursor sits before the data (add `rrb.TimePanel(play_state=rrb.components.PlayState.Following)`). - `rrb` views and `ViewerClient` (`dalaran.experimental`) are unstable. Check `help()` if a constructor argument is rejected. ## See also - `dalaran-data-model` — entities, archetypes, timelines. - `dalaran-catalog-queries` — enumerate entities in a dataset.
Auf GitHub ansehen