Atlas builder (#38) #88

Merged
crowmaki merged 2 commits from issue-38-atlas-builder into main 2026-10-08 05:21:36 +00:00
Owner

Closes #38

What

studio-core builds Atlases from Look Lock snapshots (ADR-0005):

  • Layout v1 (ATLAS_LAYOUT_VERSION = 1):
    • One row per Look (1–3), in the given order.
    • Columns follow the shared Parity in Tile order (face → front → side → back). A missing Tile collapses its column.
    • Uniform 4:9 cells. Each Tile is scaled to fit its cell, never cropped, and padded on mid-grey #808080.
    • The gap between rows is 3× the gap between Tiles. No text.
    • Looks with a different Parity are refused.
  • Size: AtlasSize::Native or AtlasSize::Budget { pixels }.
    • Set per Project. The default is 1.0 MP, stored in project.atlas_budget_pixels, where NULL means native. Changing it is undoable.
    • A budget only shrinks the grid below its native size, so the Atlas is never larger than native.
  • Cache: atlas(input_key, blob_id, cell_width, cell_height, built_at).
    • The key is a blake3 hash of the layout version, the size setting and the ordered snapshot ids. The same key reuses the same row.
    • atlas.blob_id is added to BLOB_REFERENCES.
  • API:
    • ProjectSession::atlas(&[LockSnapshotId], AtlasSize). This is the entry point for the later job builder and for per-Setup overrides.
    • look_atlas_preview(&[LookId]), which uses each Look's current snapshot and the Project's size.
    • set_atlas_size.
  • UI:
    • An Atlas size field in Project properties.
    • An Atlas preview block in the Character inspector. It has three row pickers that list any Looks, and shows the image or the reason the Looks can't combine.

Decisions to check

  • In native size, the cell fits the largest Tile at 1:1. A smaller Tile in the same Atlas is scaled up to fill its cell. "Never upscaled" is applied to the Atlas as a whole, as in #4 point 4. Tell me if it should apply per Tile instead.
  • The gutter is max(1, 3·unit/20) px, where a cell is 4·unit × 9·unit. For a 480×1080 cell this is 18 px, and the row gap is 54 px.
  • The cache key uses the size setting. With small Tiles, a native Atlas and a 1 MP Atlas can therefore have the same bytes under two keys. ADR-0005 says the key uses the size settings, so this is expected.

Tests

  • crates/studio-core/tests/atlases.rs: 13 tests through ProjectSession. They cover:
    • the layout, checked by pixels
    • fit-and-pad
    • collapsed columns
    • Parity, count and duplicate refusals
    • order and re-lock
    • cache reuse
    • budget scaling and never upscaling
    • the Project setting with undo and reopen
    • golden hashes: native size, a budget-scaled build (the Lanczos path), and three rows with a collapsed column
  • A smoke test of the adapter, in src-tauri/tests/lock_snapshots.rs.
  • src/lib/references/atlas.test.ts, for the size labels and the Look choices.
  • Full cargo test --workspace --all-features, bun test and svelte-check all pass.

The UI was checked only by typecheck. It was not checked by hand in the running app.

Closes #38 ## What `studio-core` builds Atlases from Look Lock snapshots (ADR-0005): - **Layout v1** (`ATLAS_LAYOUT_VERSION = 1`): - One row per Look (1–3), in the given order. - Columns follow the shared Parity in Tile order (face → front → side → back). A missing Tile collapses its column. - Uniform 4:9 cells. Each Tile is scaled to fit its cell, never cropped, and padded on mid-grey `#808080`. - The gap between rows is 3× the gap between Tiles. No text. - Looks with a different Parity are refused. - **Size**: `AtlasSize::Native` or `AtlasSize::Budget { pixels }`. - Set per Project. The default is 1.0 MP, stored in `project.atlas_budget_pixels`, where NULL means native. Changing it is undoable. - A budget only shrinks the grid below its native size, so the Atlas is never larger than native. - **Cache**: `atlas(input_key, blob_id, cell_width, cell_height, built_at)`. - The key is a blake3 hash of the layout version, the size setting and the ordered snapshot ids. The same key reuses the same row. - `atlas.blob_id` is added to `BLOB_REFERENCES`. - **API**: - `ProjectSession::atlas(&[LockSnapshotId], AtlasSize)`. This is the entry point for the later job builder and for per-Setup overrides. - `look_atlas_preview(&[LookId])`, which uses each Look's current snapshot and the Project's size. - `set_atlas_size`. - **UI**: - An *Atlas size* field in Project properties. - An *Atlas preview* block in the Character inspector. It has three row pickers that list any Looks, and shows the image or the reason the Looks can't combine. ## Decisions to check - In native size, the cell fits the largest Tile at 1:1. A smaller Tile in the same Atlas is scaled up to fill its cell. "Never upscaled" is applied to the Atlas as a whole, as in #4 point 4. Tell me if it should apply per Tile instead. - The gutter is `max(1, 3·unit/20)` px, where a cell is `4·unit × 9·unit`. For a 480×1080 cell this is 18 px, and the row gap is 54 px. - The cache key uses the size *setting*. With small Tiles, a native Atlas and a 1 MP Atlas can therefore have the same bytes under two keys. ADR-0005 says the key uses the size settings, so this is expected. ## Tests - `crates/studio-core/tests/atlases.rs`: 13 tests through `ProjectSession`. They cover: - the layout, checked by pixels - fit-and-pad - collapsed columns - Parity, count and duplicate refusals - order and re-lock - cache reuse - budget scaling and never upscaling - the Project setting with undo and reopen - **golden hashes**: native size, a budget-scaled build (the Lanczos path), and three rows with a collapsed column - A smoke test of the adapter, in `src-tauri/tests/lock_snapshots.rs`. - `src/lib/references/atlas.test.ts`, for the size labels and the Look choices. - Full `cargo test --workspace --all-features`, `bun test` and `svelte-check` all pass. The UI was checked only by typecheck. It was not checked by hand in the running app.
Builds Atlases from Look Lock snapshots: one row per Look (max 3), columns
in Parity order, uniform 4:9 cells fit-and-padded on mid-grey, wider row
gutters, no text. Sized to a per-Project megapixel budget (1.0 MP default,
never upscaled) or native. Atlases are a cache keyed by the ordered
snapshot ids, layout version and size (ADR-0005); golden tests pin bytes.

Adds an Atlas size field to Project properties and an Atlas preview block
to the Character inspector.

Co-Authored-By: Claude <noreply@anthropic.com>
- Draw only role picks, so columns and Parity always agree
- One AtlasSize <-> budget mapping instead of five
- Golden for a three-row Atlas with a collapsed column
- Correct the Atlas size note; name rows, not slots, in the preview

Co-Authored-By: Claude <noreply@anthropic.com>
crowmaki deleted branch issue-38-atlas-builder 2026-10-08 05:21:36 +00:00
Sign in to join this conversation.
No description provided.