feat(transition): add zoom_blur, the radial "tunnel" cut - #352
Merged
Merged
Conversation
zoom_in only scales a sharp frame buffer, and the per-element motion_blur effect can't help here because it accumulates samples of a single component's own animation — by the time a transition runs, both scenes are already flat RGBA buffers with no components left to sample. Closes issue #344 point 3. zoom_blur composites the same crossfade-zoom as zoom_in, then optionally layers ~10 extra copies of the outgoing frame at escalating scale and decreasing alpha, pivoting around `origin` (default: frame centre) — a directly rendered version of "sum of scaled copies sampled along a ray from the centre". `strength` scales how far those copies reach; 0 skips the streak pass entirely and degenerates to a plain zoom. The streak intensity follows the same peak-in-the-middle curve as chromatic_wipe's aberration (zero at progress 0 and 1), but instead of letting that curve fade to a negligible float near the edges, the function short-circuits and returns the sharp composite outright whenever the reach is non-positive. That guarantees byte-identical output to the plain source/destination frame at both ends — no rounding-error residue that could bleed a smear into the next scene. Also documents the interplay with the pivot: a scale transform leaves its own pivot point fixed, so content whose edge coincides with `origin` will not appear to smear no matter how large `strength` is — geometry, not a bug.
LeadcodeDev
force-pushed
the
feat/zoom-blur-transition
branch
from
September 26, 2026 23:22
4d4b784 to
6d7e5f2
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #344 — its point 3.
A reference video exits a scene through a radial zoom blur: the content streaks outward from a centre point, then the next scene appears.
zoom_inonly scales a sharp frame buffer, and the per-elementmotion_blureffect cannot help, because a transition only ever sees two already-painted RGBA buffers — it has no access to a component's own animation samples. That is why this had to be a transition rather than an effect.{ "type": "zoom_blur", "duration": 0.5, "strength": 1.5, "origin": { "x": 320, "y": 180 } }strength(default1.0) — how far the streaks reach.0collapses to a plain zoom.origin(default: frame centre) — where the streaks radiate from, in pixels.Zero at both ends is a short-circuit, not a fade
A leftover smear on the last frame bleeds into the next scene, so this needed to be exact rather than asymptotic.
peak = 1 - |2p - 1|is exactly0atp=0andp=1whateverstrengthis, and whenreach <= 0.0the function returns the sharp composite directly — there is no float-rounding path that could leave a residual.zero_at_both_ends_even_with_strong_streaksproves it atstrength: 4.0: byte-identical to the raw source frame atprogress=0and the raw destination atprogress=1.Verification
7 pixel-level tests in
crates/rustmotion-core/tests/zoom_blur.rs, no reference images:zero_at_both_ends_even_with_strong_streaksstrength_zero_is_a_plain_zoom_at_every_progressmid_transition_a_sharp_edge_is_measurably_smeared— a bright off-centre stripe shows more distinct intensity values across its edge atstrength: 3.0than at0.0a_bigger_strength_smears_furthercustom_origin_shifts_where_the_streaks_radiate_fromdeterministic_across_repeated_rendersnonzero_strength_changes_the_mid_transition_framecargo fmt --all --check,cargo clippy --workspace --all-targets -- -D warnings,cargo test --workspace(1532) all clean. End-to-endrustmotion validateon a scenario using the new type passes schema and geometry.The trap the rule file documents
An edge sitting on the pivot cannot appear to smear at any
strength: a scale transform's own pivot is a fixed point. That is geometry, not a bug, and worth knowing before concluding the feature is broken —rules/zoom-blur-transition.mdsays so.Written comment-free, per the codebase-wide rule from #345.