Skip to content

fix(transition): iris honours origin, and a new whip cut - #393

Merged
LeadcodeDev merged 2 commits into
mainfrom
feat/transition-iris-whip
Sep 27, 2026
Merged

LeadcodeDev merged 2 commits into
mainfrom
feat/transition-iris-whip

Conversation

@LeadcodeDev

Copy link
Copy Markdown
Owner

Closes #364. Closes #361. Refs #388.

#364 — iris accepted origin and ignored it

iris_transition hardcoded cx = w/2, cy = h/2. origin was parsed into Transition and into TransitionOptions, and read by nothing — the same silent-no-op class as #339.

Red proof, from reverting just the origin line:

with `origin` pinned to the top-left corner, that corner must already show the
incoming scene early in the transition — the old hardcoded-centre mask left it
on the outgoing scene regardless of `origin`
  left:  [5, 6, 17]
  right: [225, 231, 239]

moving `origin` to a corner must change which pixels the mask has revealed —
`origin` was previously accepted and ignored

The coverage radius moved with it: it is now measured to the farthest corner from that origin, not half the frame diagonal, so a corner-pinned iris needs a full diagonal to finish rather than stopping short.

The rest of the issue's vocabulary came with it: shape (circle | pill), fill + hold (a solid-colour hold between grow and fade), ring (stroked on the mask's actual path each frame, not a lagging circle), and reverse (the mask closes onto the origin).

#361 — a whip cut

A directional slide that streaks along its own axis. direction_vector and directional_slide were extracted out of chromatic_wipe and are now shared rather than duplicated.

The difference from zoom_blur is worth stating because it is the reason this is a separate type: zoom_blur streaks only the outgoing frame; whip streaks both — the outgoing one smears away, the incoming one arrives smeared and lands sharp.

Zero at both ends uses zoom_blur's exact short-circuit, peak = 1 − |2p−1| with if reach <= 0.0 { return sharp; }, so it is algebraically zero rather than a fade that gets small. zero_at_both_ends_even_with_a_strong_streak asserts byte-identity with the raw frames at strength: 6.0.

Verification

18 tests across two new files — 11 for iris (origin, boundary invariants at 0 and 1 for plain/fill/reverse, pill vs circle, fill+hold solid frame, hold-without-fill as a no-op, ring colour, reverse) and 7 for whip (zero at both ends, strength: 0 byte-identical to a plain slide, bigger strength streaks further, every direction lands on B, determinism).

cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings (with and without the studio feature), cargo test --workspace (1619) all clean. Comment count still 0.

One flake seen on the way: integration_audio_track_from_embedded_video failed once in a full run under heavy parallel load, then passed 4/4 in isolation and on a clean rerun of the whole suite. It shells out to ffmpeg; unrelated to this branch.

Three things worth knowing

origin is pixels, not fractions. The issue describes it as "frame fractions, same type as zoom_blur" — but ZoomBlurOrigin is documented and implemented in pixels. Reusing it as-is avoids a second, differently-scaled origin type. The issue's own repro (x: 0.1, y: 0.1) is pinned to the corner under either reading, so it did not disambiguate. Called out in the rule file.

pill coverage is an approximation. A ~45% safety margin rather than an exact stadium-geometry inversion. Documented as such rather than left to be discovered.

The issue also asked for a validator warning — "origin has no effect on iris". That is a semantic per-field check in the validator, outside this change's files, and it is a general risk rather than an iris-specific one: Transition is a flat struct where every type-specific field is silently ignored by the other types. Worth its own issue.

Written comment-free, per the codebase-wide rule from #345.

… add whip

iris_transition hardcoded its mask at the frame centre, so a scenario
could set `origin` and pass validation while the pixel never moved
(#364) - the class of silent-ignore bug this repo treats as a real
defect. origin now drives both the mask centre and the coverage radius
(computed from the farthest corner *from that point*, not half the
frame diagonal). Riding on the same fix, iris gains the rest of the
requested vocabulary: shape (circle/pill, the latter stretched by
aspect), fill+hold (a solid-colour hold before the next scene fades
in), ring (a border traced from the mask's live radius every frame,
so it can't lag behind like the old stroked-circle workaround did),
and reverse (closes onto origin instead of opening from it).

Also adds `whip` (#361): a directional slide whose axis grows a
motion-blur streak peaking at the transition's midpoint, collapsing to
a plain `slide` via the same hard short-circuit `zoom_blur` uses at
strength 0 or at either end - no leftover streak can bleed into the
next scene. `direction_vector` and `directional_slide` are pulled out
of `chromatic_wipe` so `whip` reuses the same hard-edged slide instead
of re-deriving it.
@LeadcodeDev LeadcodeDev added the enhancement New feature or request label Sep 27, 2026
@LeadcodeDev LeadcodeDev self-assigned this Sep 27, 2026
@LeadcodeDev
LeadcodeDev merged commit ab07d4a into main Sep 27, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

1 participant