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.

Informações da origem

Repositório
Flaminis/Dalaran
Última atividade na origem
7 de agosto de 2026 às 09:48
Idioma detectado do SKILL.md
inglês
Estrelas
736
Forks
31

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
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.
Ver no GitHub