The creator's locked table (2026-10-17) gives the Chat scene its own rules in the 9:16 tier: RECT ONLY - the circle option is not offered - and a size that is either the normal small tile or ENLARGED to fill one whole 607x540 area (top or bottom). Unit 1 shipped the per-scene placement; this unit is the Chat scene's own variant of it. The rule lives on the MODEL, not on the button: - Models/SceneElement.cs: ClipShape and ToggleClipShape() are now virtual. - Models/WebcamSceneConfig.cs: VerticalShapeLocked (set per scene by the VM) OVERRIDES the ClipShape property, so no writer can slip a circle in - the toggle, the Shape button and the placement swap all go through it. The lock is cleared BEFORE the 16:9 layout comes back, so the desktop shape returns. - ViewModels/MainViewModel.Webcam.cs: CanToggleClipShape (the circle is NOT offered in the vertical Chat layout - the Shape button disappears, it is not merely greyed out), IsChatSceneCamera, ResizeWebcamInVerticalTier. - Models/VerticalCameraLayout.cs: FillsArea (the "enlarged" state, derived - no new column) and SizeFor - the Chat camera's TWO sizes, the tile or the whole area, whichever is nearer the requested width, so the resize handle has two detents instead of any size in between. - An enlarged camera FILLS the area its centre is in (top or bottom), so dragging it up/down swaps the areas. - The Chat camera is auto-tiled like the Live one when the vertical tier is picked - otherwise the vertical chat stream has no camera at all. - Controls/PreviewPane.xaml.cs: the resize routes through the VM; the drag assigns, lets the model clamp into the band, then snaps (so the enlarged camera can be dragged from the top area to the bottom one). Test: ytLive.Tests/VerticalChatCameraLayoutTests.cs - the ONE Good Dog test for the unit: the vertical tier is picked -> the Chat camera comes into the band as a small RECT tile -> the circle option is not offered and cannot be set even on the model -> dragged up and enlarged it fills the TOP area exactly -> back to 16:9 the desktop layout is restored exactly and the circle option is offered again. 430/430 tests pass, 0 warnings. Research (the creator's table is the established practice, not our invention): - Streamlabs' vertical guide: "splitting the screen in half horizontally, placing the camera feed in the top half and the main content on the bottom (or vice versa) may be the bet", and a just-chatting vertical wants the camera "to take up most of the screen" (streamlabs.com/content-hub/post/overlays-vertical-and-horizontal-streaming) - Bloopbot's vertical overlay docs describe the same per-layer model we use at scene granularity: "move or resize a layer on Vertical and it stops being Auto-placed. It keeps the place you gave it, and the horizontal layout is not touched" (bloopbot.com/docs/vertical-overlays-twitch-dual-format) - our swap-at-tier-change, stored per scene. - Landmine recorded: "about the bottom 560 px of the vertical video can be covered by Twitch's chat and player controls on phones" (Bloopbot) - the Chat camera's enlarged BOTTOM area may be half-hidden by the platform chrome; TASK 53's safe-zone work decides that. Memory in the same commit: ai.md (the Chat rules, the shape lock, the tests), TASKS.md + TASKS/task-57-vertical-camera-layout.md (TASK 57 complete), HANDOFF.md, MyMistakes.md (3 recipes: lock the model property not the UI button; two detents for a discrete size set; the edit tool cannot match an oldString with an em dash), Models/ViewModels/Controls index rows.
101 lines
6.9 KiB
Markdown
101 lines
6.9 KiB
Markdown
# TASK 57 — Vertical (9:16) tier camera placement — the "vv" feature
|
||
|
||
> Catalog: [`TASKS.md`](../TASKS.md) — status and requirements live here.
|
||
|
||
**Status:** ✅ Both units shipped 2026-10-21 (Unit 1 = the Live scene, Unit 2 = the Chat scene).
|
||
Only the creator's hands-on check (slice 3) is outstanding.
|
||
|
||
## 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; enlarged fills the top or the bottom area |
|
||
| 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.
|
||
|
||
## Design (Unit 2 — the Chat scene)
|
||
|
||
- **Rect only, and the lock sits on the model.** `WebcamSceneConfig.VerticalShapeLocked` (set per
|
||
scene by the VM) **overrides the `ClipShape` property**, so a locked camera cannot become Round
|
||
by ANY writer — the toggle, the Shape button, the placement swap. The lock is cleared *before*
|
||
the 16:9 layout comes back, so the desktop shape is restored.
|
||
- **The circle is not offered.** `MainViewModel.CanToggleClipShape` is false for a Chat camera in
|
||
the vertical tier → the Shape button in the webcam's properties disappears (not just disabled).
|
||
- **Two sizes, snapped by the nearer one.** `VerticalCameraLayout.SizeFor(band, requestedWidth)`
|
||
returns the small tile (270×152) or one whole area (607.5×540) — whichever is nearer the
|
||
requested width, so the resize handle has two detents instead of any size in between.
|
||
- **An enlarged camera fills the area its centre is in** — the top one or the bottom one
|
||
(`VerticalCameraLayout.AreaFor`).
|
||
- **The Chat camera is auto-tiled** like the Live one when the vertical tier is picked — otherwise
|
||
the vertical chat stream has no camera at all.
|
||
|
||
## 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** (shipped 2026-10-21): in the vertical tier the Chat camera comes
|
||
into the band as a small RECT tile; the circle option is not offered and cannot be set even on
|
||
the model; enlarged it fills one whole 607×540 area — the top one or the bottom one, by where
|
||
the camera is; switching back to 16:9 restores the desktop layout exactly and the circle option
|
||
is offered again. `ytLive.Tests/VerticalChatCameraLayoutTests.cs` — the ONE test for the unit.
|
||
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 (and is gone in the Chat scene), enlarge the Chat camera to see it fill a whole
|
||
area, 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.
|
||
- **Research (Unit 2, 2026-10-21).** The "one 607×540 area" rule is the established vertical-stream
|
||
layout: Streamlabs' vertical guide says "splitting the screen in half horizontally, placing the
|
||
camera feed in the top half and the main content on the bottom (or vice versa) may be the bet",
|
||
and that a just-chatting vertical wants the camera "to take up most of the screen"
|
||
(streamlabs.com/content-hub/post/overlays-vertical-and-horizontal-streaming). Bloopbot's vertical
|
||
overlay docs describe the same per-layer model we use at scene granularity: "move or resize a
|
||
layer on Vertical and it stops being Auto-placed. It keeps the place you gave it, and the
|
||
horizontal layout is not touched" (bloopbot.com/docs/vertical-overlays-twitch-dual-format).
|
||
- ⚠️ **Landmine for the bottom area:** "about the bottom 560 px of the vertical video can be covered
|
||
by Twitch's chat and player controls on phones" (Bloopbot, above) — the Chat camera's enlarged
|
||
**bottom** area may be half-hidden by the platform chrome. TASK 53's safe-zone work decides that.
|
||
- 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.
|