Files
LlamaCasty/TASKS/task-57-vertical-camera-layout.md
T
gramps 6be062c45f vv webcam Unit 1: the vertical (9:16) tier keeps its OWN camera placement per scene
The vertical tier is a centred band crop of the SAME 1920x1080 master (band x
656.25, w 607.5, h 1080), so the 16:9 camera placement (x~1408) falls outside the
cropped window and the creator is simply not in the vertical stream. Each scene
now keeps its own vertical position, size and shape; switching back to 16:9
restores the desktop layout exactly (the creator's locked table, 2026-10-17).

Geometry only - NO render-path change. The preview and the compositor both read
the element's own X/Y/W/H + ClipShape, so the tier switch swaps the LIVE geometry:
- Models/VerticalCameraLayout.cs (new): the pure math - the band, its two 607x540
  areas, the six spots (top/bottom x left/centre/right), the 32px inset and the
  270x152 default tile (480x270 in the 1080x1920 output) in the bottom-right spot,
  the corner the 16:9 default webcam uses.
- Models/WebcamSceneConfig.cs: the vertical placement lives in seven additive
  columns (VerticalX/Y/Width/Height/ClipShape/RectWidth/RectHeight; NULL = "this
  scene has no vertical placement yet"). The 16:9 layout stays in the old columns
  and is parked in the transient Landscape* fields while the vertical tier is
  live; Begin/EndVerticalLayout + CommitLiveLayoutToVertical do the swap. The
  X/Y/W/H overrides clamp every write into the band - outside it the camera is
  invisible in the vertical output.
- Models/SceneElement.cs: X/Y/Width/Height became virtual so the derived element
  constrains every writer (drag, resize handle, typed fields) in ONE choke point
  instead of three writers each re-implementing the rule.
- ViewModels/MainViewModel.Webcam.cs + .Streaming.cs: IsVerticalTier/VerticalBand,
  ResolveVerticalCameraLayout(s) (the tier swap), MoveWebcamInVerticalTier /
  SnapWebcamInVerticalTier (the six-spot snap, by the tile's centre),
  EnsureVerticalCameraLayout; ApplyStreamQuality is the tier-change hook. The
  Live scene gets its small tile automatically on the first vertical switch (the
  vertical stream would otherwise have no camera at all); the other scenes keep
  their 16:9 placement until the creator drags them into the band.
- Controls/PreviewPane.xaml.cs: the drag/resize route through the VM snap under
  the vertical tier instead of free-floating over the master.
  Controls/LeftPanel.xaml: the webcam properties gained a Geometry section
  (X/Y/W/H) for the manual entry the creator asked for, bound to the LIVE
  placement, gated on the VM's IsWebcamSelected (NOT SelectedElement.IsWebcam -
  the TASK 51 test finds that panel by binding text and would match twice).
- The store: seven guarded ADD COLUMNs, the INSERT/SELECT extended, and the
  X/Y/W/H/ClipShape columns stay the 16:9 layout - while the vertical tier is
  live the save writes the parked values there and the live ones into Vertical*.

Test: ytLive.Tests/VerticalWebcamLayoutTests.cs - the ONE Good Dog test. The
vertical tier brings the Live camera INTO the band as a small tile; dragging it to
the top-right lands it top-right inside the band; back to 16:9 the desktop layout
is restored exactly (1408/778/480x270); both placements survive a save + reload.
Full suite 429/429, 0 warnings. LayerReorderPersistenceTests.RealMouseDrag fails
ALONE on a clean tree in this environment - pre-existing flake, proven by stashing
this work, not a regression.

Derivative work: OBS has no built-in per-aspect-ratio layout storage - the
maintainers' answer is "create a second scene and add the scene you want to send
as source" (obsproject.com/forum/threads/161307) - and Streamyard's dual-aspect
guide says the same ("no built-in way to store separate layouts per aspect ratio",
so you rely on manual workarounds and plugins):
https://streamyard.com/blog/dual-aspect-ratio-streaming-tool
Our per-scene, per-tier stored placement, resolved on the tier change, is the
step past both.

Memory in the same commit: ai.md (new section), TASKS.md + TASKS/task-57-*.md
(Unit 1 shipped, Unit 2 = the Chat scene still open), HANDOFF.md, MyMistakes.md
(the per-tier geometry-swap recipe, the binding-text panel trap, the store's
webcam-identity rule) and the Models/Services/ViewModels/Controls index rows.
2026-10-21 08:58:24 -07:00

70 lines
4.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TASK 57 — Vertical (9:16) tier camera placement — the "vv" feature
> Catalog: [`TASKS.md`](../TASKS.md) — status and requirements live here.
**Status:** 🔶 Unit 1 shipped 2026-10-21 (the Live scene). **Unit 2 open** (the Chat scene).
## Why
The vertical tier (1080×1920) is a centred **band crop** of the same 1920×1080 master every
16:9 tier uses: `OutputRect*` = x 656.25, y 0, w 607.5, h 1080. The default webcam sits at
x≈1408 — **outside the cropped window entirely**, so in a vertical stream the creator is simply
not in the picture. Switching tiers must not cost the creator their camera.
## The creator's locked table (2026-10-17)
| mode | shape | size | position |
|---|---|---|---|
| 16:9 | as today | as today | as today |
| **vv · Live** | circle **or** rect, the creator's choice | small tile | one of **six** spots: top/bottom × left/centre/right |
| vv · Chat | rect only | normal, or enlarged to one 607×540 area | the same six spots (Unit 2) |
| Starting / BRB / Ending | unchanged | unchanged | unchanged |
Plus: each scene keeps its **own** vertical position, size and shape; switching back to 16:9
restores the desktop layout **exactly**; placement is set by drag with snap (like the socials)
**and** by manual entry in the properties.
## Design (Unit 1)
- **Geometry only — no render-path change.** The preview and the compositor read the element's
own `X`/`Y`/`Width`/`Height` + `ClipShape`, so the tier switch swaps the live geometry:
`WebcamSceneConfig.BeginVerticalLayout(...)` parks the 16:9 layout and brings the vertical one
in; `EndVerticalLayout()` commits the vertical placement back to its stored slot and restores
the parked 16:9 layout.
- **Storage.** Seven additive columns on `WebcamSceneConfig` (`VerticalX/Y/Width/Height/
VerticalClipShape/VerticalRectWidth/VerticalRectHeight`), NULL = "this scene has no vertical
placement yet". The X/Y/W/H/ClipShape columns stay the **16:9** layout, so a save never loses
the desktop placement.
- **The band and the six spots** (`Models/VerticalCameraLayout.cs`, pure math): the band splits
into two 607×540 areas (top/bottom), each with three columns (left/centre/right). A tile snaps
to the spot its **centre** falls in, inset 32 master px from the band's outer edge and never
outside the band. Default tile: 270×152 master px (480×270 in the 1080×1920 output), bottom-right
spot — the corner the 16:9 default webcam uses. The Live scene gets it automatically the first
time the vertical tier is picked; the other scenes keep their 16:9 placement until the creator
drags them into the band.
- **One clamp, one choke point.** `SceneElement.X/Y/Width/Height` became `virtual` and the webcam
constrains every write (drag, resize handle, typed Geometry fields) into the band — outside it
the camera is invisible in the vertical stream.
- **Manual entry.** The webcam's Layers properties gained a Geometry section (X/Y/W/H) bound to
the **live** placement — the one the current tier shows.
## Slices (one integration test each)
1. ✅ **Unit 1 — the Live scene** (shipped 2026-10-21): the vertical tier brings the Live camera
into the band as a small tile; dragging it to the top-right snaps it top-right inside the
band; switching back to 16:9 restores the desktop layout exactly; both placements survive a
save + reload. `ytLive.Tests/VerticalWebcamLayoutTests.cs` — the ONE test for the unit.
2. ☐ **Unit 2 — the Chat scene**: rect only (no circle) in the vertical tier, either the normal
size or enlarged to fill one 607×540 area; the same six-spot snap. The Chat scene's 16:9
exemption from the half-frame cap must not leak into the vertical tier.
3. ☐ **Creator check** (needs the creator's hands): switch the dropdown to "Vertical 1080p60",
confirm the camera tile lands inside the band and the Shape button toggles circle/rect in the
vertical tier, then switch back and confirm the desktop layout is untouched.
## Notes
- OBS cannot do this (no per-aspect layout storage — the maintainers' answer is a second scene
fed as a source); Streamyard documents the same gap. Our per-scene, per-tier stored placement
resolved on the tier change is the step past both.
- The vertical tier's output crop is TASK 53's business (safe zones, caption clearance); the
camera tile is inside the band, but the band is not yet proven against TikTok/Shorts chrome.