Skip to content

Add peniko (2d graphics) animation support#103

Merged
nixonyh merged 21 commits into
mainfrom
nixon/kurbo-support
May 15, 2026
Merged

Add peniko (2d graphics) animation support#103
nixonyh merged 21 commits into
mainfrom
nixon/kurbo-support

Conversation

@nixonyh

@nixonyh nixonyh commented May 3, 2026

Copy link
Copy Markdown
Member

Stack on top of #100

  • Added peniko_motiongfx crate that covers common interpolation & tracing implementations
  • Move Interpolation<M> trait into motiongfx and add a marker type to workaround the orphan rule.
  • Add Tracer<T> type in peniko_motiongfx to handle tracing operations.
  • Add examples to showcase animations using peniko, rendered via vello.
2026-05-15_23-41-46.mp4

@nixonyh
nixonyh requested a review from Jaghov May 3, 2026 14:20
@coderabbitai

coderabbitai Bot commented May 3, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Rate limit exceeded

@nixonyh has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 47 minutes and 11 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 07a52051-f7f3-4597-b27a-c482b7826316

📥 Commits

Reviewing files that changed from the base of the PR and between 37f4cae and 6aa6150.

📒 Files selected for processing (2)
  • crates/bevy_motiongfx/src/interpolation.rs
  • crates/motiongfx/src/interpolation.rs
📝 Walkthrough

Walkthrough

This PR refactors animation interpolation into a marker-pattern abstraction in motiongfx, splits TimelineBuilder::act() into interpolation-aware and builder variants, introduces a new peniko_motiongfx crate for geometric type interpolation, adds a Vello/winit rendering framework with a Lissajous example, and updates workspace configuration and all examples accordingly.

Changes

Core Interpolation Abstraction and Timeline API

Layer / File(s) Summary
Parameterized Interpolation trait in motiongfx
crates/motiongfx/src/interpolation.rs, crates/motiongfx/src/lib.rs
A new Interpolation<M> trait with marker-type parameter M and an interp method is introduced, along with an exported impl_float_interpolation! macro for float types. The trait is declared as a public module and re-exported through prelude.
bevy_motiongfx updated to use Interpolation
crates/bevy_motiongfx/src/interpolation.rs, crates/bevy_motiongfx/src/lib.rs, crates/bevy_motiongfx/Cargo.toml
Removes local ActionInterpTimelineExt and Interpolation traits; introduces a Bevy marker struct; updates all interpolation implementations (float, u8, Color, Transform) to implement motiongfx::prelude::Interpolation<Bevy> via imported macros. Removes sprite and pbr features from default and dependencies. Updates prelude to drop the removed traits.
TimelineBuilder::act refactored for Interpolation trait
crates/motiongfx/src/timeline.rs
TimelineBuilder::act() now requires T: Interpolation<M>, returns InterpActionBuilder, and applies interpolation via T::interp. A new act_builder() method extracts shared logic for raw action construction. act_step() is updated to use act_builder() with step-interpolation closure. A minor overlap check uses direct negation instead of == false.
peniko_motiongfx crate with Interpolation implementations
crates/peniko_motiongfx/Cargo.toml, crates/peniko_motiongfx/src/lib.rs, crates/peniko_motiongfx/src/interpolation.rs
New crate establishes a Peniko marker and implements Interpolation<Peniko> for Point, Vec2, Size, Rect, Circle, Line, CubicBez, QuadBez, RoundedRect, and Color, with helper functions for scalar and point interpolation.
Path tracing abstraction for Peniko geometry
crates/peniko_motiongfx/src/trace.rs, crates/peniko_motiongfx/src/lib.rs
Introduces Tracer<T: Trace> wrapper and a Trace trait supporting sub-path extraction via normalized range queries. Implements Trace for Line, QuadBez, CubicBez, and BezPath (with dedicated range-slicing helper). Exports type aliases (LineTracer, QuadTracer, CubicTracer, PathTracer).

Vello/Winit Demo Framework

Layer / File(s) Summary
VelloDemo trait and VelloWinitApp rendering engine
examples/vello_winit_example/src/lib.rs
Defines VelloDemo trait for window setup and scene rendering callbacks. VelloWinitApp<D: VelloDemo> manages a RenderState (Suspended or Active) and implements the rendering pipeline: scene reset, surface sync, texture acquisition with error handling, Vello render, blit/copy, and present. Implements ApplicationHandler for winit event flow (resume, suspend, window events).
Lissajous table animation example
examples/vello_winit_example/examples/lissajous.rs, examples/vello_winit_example/Cargo.toml
Complete LissajousTableDemo animating a grid of Lissajous curves via timeline. Defines grid lines and curve states as SubjectSource implementations for timeline updates. Constructs two timeline tracks (grid in, curves in/out with stagger). Renders grid, reference circles/dots, and traces curve segments via Tracer abstraction. Includes main entry point.

Workspace, Configuration, and Example Migrations

Layer / File(s) Summary
Workspace dependencies and feature setup
Cargo.toml, crates/motiongfx/Cargo.toml, .github/workflows/rust.yml, crates/bevy_motiongfx/src/manager.rs
Adds peniko_motiongfx to workspace; removes bevy_sprite/bevy_pbr; adds peniko, vello, winit, pollster. Defines std feature in motiongfx and peniko_motiongfx forwarding to dependencies. Removes bool_comparison lint override. Updates no-defaults CI check to enable libm. Changes TimelineComplete lint attribute to expect.
Example crate manifests
examples/bevy_examples/Cargo.toml, examples/vello_winit_example/Cargo.toml
bevy_examples now includes bevy_motiongfx dependency and explicitly enables default-features for bevy. New vello_winit_example declares workspace-sourced dependencies.
Documentation updates
crates/motiongfx/README.md, crates/bevy_motiongfx/README.md, crates/motiongfx/docs/world.rs, crates/motiongfx/examples/custom_world.rs
Examples remove .with_interp() chaining and demonstrate direct b.act(...) usage. Documentation reflects the simplified API and acts as illustrative code samples.
Bevy example migrations
examples/bevy_examples/examples/{custom_ease, custom_interp, easings, hello_world, minimal, recording, slide_basic}.rs
All timeline builder calls updated from act_interp(...) to act(...) (automatic interpolation) or act_builder(...) (builder-pattern construction). Easing, play timing, and compile logic remain unchanged.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • voxell-tech/motiongfx#100: Both PRs modify crates/motiongfx/src/timeline.rs's TimelineBuilder::act(...) API (main PR changes it to be Interpolation<M>/returns InterpActionBuilder and adds act_builder, while retrieved PR changes it for lazy registration using Registry/FieldAccessor and SubjectSource), so the changes are directly code-level related.

Suggested labels

documentation, enhancement

Suggested reviewers

  • Jaghov

Poem

🐰 From traits local sprung a marker grand,
Peniko curves now interpolate through land,
With Vello's brush and winit's frame,
The Lissajous dances without shame. 🎨✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main objective of the PR: adding peniko 2d graphics animation support through a new crate and associated examples.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description clearly relates to the changeset, detailing the addition of the peniko_motiongfx crate, moving the Interpolation<M> trait, adding the Tracer<T> type, and providing examples with peniko/vello.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nixonyh
nixonyh changed the base branch from main to nixon/lazy-registration May 3, 2026 14:20

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/motiongfx/src/timeline.rs (1)

438-472: ⚠️ Potential issue | 🔴 Critical

Add the missing 'static bound to both act and act_step methods.

Both methods call ActionBuilder::with_interp, which requires T: 'static (defined at crates/motiongfx/src/action.rs:380). The current signatures omit this bound and will fail to compile.

🔧 Suggested fix
 pub fn act<I, S, T, M>(
     &mut self,
     target: I,
     field_acc: FieldAccessor<S, T>,
     action: impl Action<T>,
 ) -> InterpActionBuilder<'_, T>
 where
     W: SubjectSource<M, I, S> + 'static,
     I: SubjectId,
     S: 'static,
-    T: Interpolation<M> + Clone + ThreadSafe,
+    T: Interpolation<M> + Clone + ThreadSafe + 'static,
@@
     action: impl Action<T>,
 ) -> InterpActionBuilder<'_, T>
 where
     W: SubjectSource<M, I, S> + 'static,
     I: SubjectId,
     S: 'static,
-    T: Clone + ThreadSafe,
+    T: Clone + ThreadSafe + 'static,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/motiongfx/src/timeline.rs` around lines 438 - 472, The act and
act_step methods call ActionBuilder::with_interp which requires the interpolated
type T to be 'static, so add the missing T: 'static bound to both function
where-clauses (the act(...) and act_step(...) methods) so their generics include
T: 'static alongside the existing bounds; ensure the signatures for act and
act_step mirror the required trait bounds used by ActionBuilder::with_interp.
🧹 Nitpick comments (1)
crates/motiongfx/src/interpolation.rs (1)

1-8: ⚡ Quick win

Clarify the t contract in docs.

At Line 7, please document whether t is expected in [0.0, 1.0] or whether extrapolation is intentionally supported. That will prevent divergent downstream implementations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/motiongfx/src/interpolation.rs` around lines 1 - 8, Document the
contract for the interpolation parameter `t` on the Interpolation trait: update
the doc comment for trait Interpolation and the interp method
(Interpolation::interp) to state explicitly whether `t` is clamped to [0.0, 1.0]
or if extrapolation outside that range is allowed, and if clamped describe who
is responsible for clamping (caller or implementor); include expected behavior
for edge cases (e.g., t == 0.0 returns a, t == 1.0 returns b) so downstream
implementors have a single authoritative spec to follow.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/kurbo_motiongfx/src/trace.rs`:
- Around line 24-26: The public function trace_bez_path currently treats
multi-subpath BezPath as a single stroke; fix by either preserving subpaths or
enforcing single-subpath input: implement detection of MoveTo elements and split
the input BezPath into subpaths, call the existing single-subpath tracing logic
for each subpath, and append results to a new output BezPath preserving MoveTo
boundaries (ensuring you handle closing/First/ClosePath logic), or change
trace_bez_path to return a Result/Option or panic when more than one subpath is
detected and document the behavior; locate and update trace_bez_path and any
helpers that operate on path elements (see comments around lines 44–55) to
perform the split-or-validate step and then aggregate per-subpath traces into
the final BezPath (or return an error) accordingly.
- Around line 7-17: The trace_cubic_bez and trace_quad_bez helpers currently
take an unbounded t and forward it to curve.subsegment, causing inconsistent
behavior with trace_bez_path which clamps t; update both trace_cubic_bez and
trace_quad_bez to clamp t into the [0.0, 1.0] range (e.g. min(max(t, 0.0), 1.0))
before converting to f64 and calling curve.subsegment so their prefix semantics
match trace_bez_path.

---

Outside diff comments:
In `@crates/motiongfx/src/timeline.rs`:
- Around line 438-472: The act and act_step methods call
ActionBuilder::with_interp which requires the interpolated type T to be 'static,
so add the missing T: 'static bound to both function where-clauses (the act(...)
and act_step(...) methods) so their generics include T: 'static alongside the
existing bounds; ensure the signatures for act and act_step mirror the required
trait bounds used by ActionBuilder::with_interp.

---

Nitpick comments:
In `@crates/motiongfx/src/interpolation.rs`:
- Around line 1-8: Document the contract for the interpolation parameter `t` on
the Interpolation trait: update the doc comment for trait Interpolation and the
interp method (Interpolation::interp) to state explicitly whether `t` is clamped
to [0.0, 1.0] or if extrapolation outside that range is allowed, and if clamped
describe who is responsible for clamping (caller or implementor); include
expected behavior for edge cases (e.g., t == 0.0 returns a, t == 1.0 returns b)
so downstream implementors have a single authoritative spec to follow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2bd8cfa6-0cff-45be-9f85-eca05403b7c1

📥 Commits

Reviewing files that changed from the base of the PR and between f95e9a0 and 8d37bd8.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (21)
  • Cargo.toml
  • crates/bevy_motiongfx/Cargo.toml
  • crates/bevy_motiongfx/src/interpolation.rs
  • crates/bevy_motiongfx/src/lib.rs
  • crates/kurbo_motiongfx/Cargo.toml
  • crates/kurbo_motiongfx/src/interpolation.rs
  • crates/kurbo_motiongfx/src/lib.rs
  • crates/kurbo_motiongfx/src/trace.rs
  • crates/motiongfx/Cargo.toml
  • crates/motiongfx/docs/world.rs
  • crates/motiongfx/examples/custom_world.rs
  • crates/motiongfx/src/interpolation.rs
  • crates/motiongfx/src/lib.rs
  • crates/motiongfx/src/timeline.rs
  • examples/bevy_examples/examples/custom_ease.rs
  • examples/bevy_examples/examples/custom_interp.rs
  • examples/bevy_examples/examples/easings.rs
  • examples/bevy_examples/examples/hello_world.rs
  • examples/bevy_examples/examples/minimal.rs
  • examples/bevy_examples/examples/recording.rs
  • examples/bevy_examples/examples/slide_basic.rs
💤 Files with no reviewable changes (1)
  • crates/bevy_motiongfx/src/lib.rs

Comment thread crates/peniko_motiongfx/src/trace.rs Outdated
Comment thread crates/peniko_motiongfx/src/trace.rs Outdated
@nixonyh
nixonyh force-pushed the nixon/kurbo-support branch from 535f251 to 3eacd70 Compare May 4, 2026 16:26
@nixonyh nixonyh changed the title Add kurbo animation support Add peniko (kurbo & color) animation support May 5, 2026
@nixonyh nixonyh changed the title Add peniko (kurbo & color) animation support Add peniko (2d graphics) animation support May 5, 2026
Base automatically changed from nixon/lazy-registration to main May 6, 2026 15:45
@nixonyh
nixonyh force-pushed the nixon/kurbo-support branch from 2e46e80 to f089dd9 Compare May 11, 2026 02:03
@nixonyh
nixonyh force-pushed the nixon/kurbo-support branch from 468c0e8 to 4013fb5 Compare May 15, 2026 14:46
@nixonyh
nixonyh marked this pull request as ready for review May 15, 2026 15:41

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
crates/motiongfx/src/interpolation.rs (2)

10-25: 💤 Low value

Use the monotonic lerp form to guarantee endpoint exactness.

(*a) * (1.0 - t) + (*b) * t is the imprecise lerp form: due to floating-point rounding, the result at t == 1.0 is not guaranteed to equal b (and at t == 0.0 not guaranteed to equal a). For animation, this can leave residual error at keyframe endpoints. The standard "monotonic" form a + (b - a) * t guarantees t == 0 → a and t == 1 → b exactly. See also f32::lerp (currently nightly) which uses this form.

Note: switching changes results for Vec*/DVec*/Color impls in bevy_motiongfx too, since they reuse this macro.

♻️ Proposed change
     ($ty:ty, $base:ty, $marker:ty) => {
         impl $crate::interpolation::Interpolation<$marker> for $ty {
             #[inline]
             fn interp(a: &Self, b: &Self, t: f32) -> Self {
                 let t = <$base>::from(t);
-                (*a) * (1.0 - t) + (*b) * t
+                *a + (*b - *a) * t
             }
         }
     };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/motiongfx/src/interpolation.rs` around lines 10 - 25, The
interpolation macro impl_float_interpolation emits a non-monotonic lerp (*a) *
(1.0 - t) + (*b) * t which can produce endpoint rounding errors; update the
Interpolation implementation inside impl_float_interpolation (the fn interp for
types produced by the macro) to compute the monotonic form a + (b - a) * t using
the same base conversion for t (i.e. let t = <$base>::from(t)), ensuring all
operations use the concrete type variables ($ty and $base) so t is applied to (b
- a) then added to a to guarantee exact endpoints for t == 0.0 and t == 1.0.

6-8: 💤 Low value

Consider idiomatic &self receiver for interp.

The associated function takes a: &Self, b: &Self instead of &self, other: &Self. Using a &self receiver would let callers write a.interp(&b, t) and aligns with idiomatic Rust trait design. The current static-style call site Interpolation::interp(&a, &b, t) is more verbose at every use (as visible in the Transform impl in bevy_motiongfx). Since this is a brand new public trait, now is the cheap time to lock in the shape.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/motiongfx/src/interpolation.rs` around lines 6 - 8, Change the trait
method signature from a static associated function to an instance method: update
pub trait Interpolation<M> { fn interp(a: &Self, b: &Self, t: f32) -> Self; } so
it becomes fn interp(&self, other: &Self, t: f32) -> Self; and then update all
implementations and call sites (e.g., usages in Transform impls in
bevy_motiongfx that currently call Interpolation::interp(&a,&b,t)) to use the
instance form a.interp(&b, t). Keep the generic parameter M as-is and ensure
lifetimes/borrows still compile when converting impl blocks and tests.
crates/bevy_motiongfx/src/interpolation.rs (2)

5-5: 💤 Low value

Add standard derives to the Bevy marker.

For a public marker type that downstream code may pass as a generic argument or store in PhantomData-bearing structs, the conventional derives are helpful. Without them, downstream users get worse error messages and can't, e.g., debug-print or copy the marker.

♻️ Proposed change
-pub struct Bevy;
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct Bevy;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/bevy_motiongfx/src/interpolation.rs` at line 5, The public marker type
`Bevy` lacks standard derives which makes it harder for downstream users to
print, copy, or compare it; add a derive attribute to the `Bevy` struct such as
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] so it can be cheaply
copied, debug-printed, and used in PhantomData-bearing generics—apply this
derive to the `pub struct Bevy` declaration.

85-101: ⚡ Quick win

Inference may become ambiguous if foreign Interpolation<_> impls land on Vec3/Quat.

The calls Interpolation::interp(&a.translation, ...) rely on the compiler inferring M = Bevy from there being a single matching impl. If any future impl adds Interpolation<OtherMarker> for Vec3 / Quat / Vec3, these calls become ambiguous and fail to compile. Since the orphan-rule workaround in this PR (peniko_motiongfx::Peniko) is explicitly designed to enable exactly that pattern downstream, consider disambiguating now.

♻️ Proposed change
-    impl Interpolation<Bevy> for Transform {
-        fn interp(a: &Self, b: &Self, t: f32) -> Self {
-            Self {
-                translation: Interpolation::interp(
-                    &a.translation,
-                    &b.translation,
-                    t,
-                ),
-                rotation: Interpolation::interp(
-                    &a.rotation,
-                    &b.rotation,
-                    t,
-                ),
-                scale: Interpolation::interp(&a.scale, &b.scale, t),
-            }
-        }
-    }
+    impl Interpolation<Bevy> for Transform {
+        fn interp(a: &Self, b: &Self, t: f32) -> Self {
+            Self {
+                translation: <_ as Interpolation<Bevy>>::interp(
+                    &a.translation,
+                    &b.translation,
+                    t,
+                ),
+                rotation: <_ as Interpolation<Bevy>>::interp(
+                    &a.rotation,
+                    &b.rotation,
+                    t,
+                ),
+                scale: <_ as Interpolation<Bevy>>::interp(&a.scale, &b.scale, t),
+            }
+        }
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/bevy_motiongfx/src/interpolation.rs` around lines 85 - 101, The calls
inside the Interpolation<Bevy> for Transform impl are relying on type inference
(Interpolation::interp(&a.translation, ...)) which can become ambiguous if other
Interpolation<...> impls for Vec3/Quat appear; disambiguate by calling the trait
impls directly on the concrete field types with the Bevy marker, e.g. replace
Interpolation::interp(&a.translation, &b.translation, t) with <Vec3 as
Interpolation<Bevy>>::interp(&a.translation, &b.translation, t), do the same for
rotation using <Quat as Interpolation<Bevy>>::interp(...) and for scale using
<Vec3 as Interpolation<Bevy>>::interp(...), keeping this change inside the impl
Interpolation<Bevy> for Transform block.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/bevy_motiongfx/src/interpolation.rs`:
- Line 5: The public marker type `Bevy` lacks standard derives which makes it
harder for downstream users to print, copy, or compare it; add a derive
attribute to the `Bevy` struct such as #[derive(Clone, Copy, Debug, Default,
PartialEq, Eq, Hash)] so it can be cheaply copied, debug-printed, and used in
PhantomData-bearing generics—apply this derive to the `pub struct Bevy`
declaration.
- Around line 85-101: The calls inside the Interpolation<Bevy> for Transform
impl are relying on type inference (Interpolation::interp(&a.translation, ...))
which can become ambiguous if other Interpolation<...> impls for Vec3/Quat
appear; disambiguate by calling the trait impls directly on the concrete field
types with the Bevy marker, e.g. replace Interpolation::interp(&a.translation,
&b.translation, t) with <Vec3 as Interpolation<Bevy>>::interp(&a.translation,
&b.translation, t), do the same for rotation using <Quat as
Interpolation<Bevy>>::interp(...) and for scale using <Vec3 as
Interpolation<Bevy>>::interp(...), keeping this change inside the impl
Interpolation<Bevy> for Transform block.

In `@crates/motiongfx/src/interpolation.rs`:
- Around line 10-25: The interpolation macro impl_float_interpolation emits a
non-monotonic lerp (*a) * (1.0 - t) + (*b) * t which can produce endpoint
rounding errors; update the Interpolation implementation inside
impl_float_interpolation (the fn interp for types produced by the macro) to
compute the monotonic form a + (b - a) * t using the same base conversion for t
(i.e. let t = <$base>::from(t)), ensuring all operations use the concrete type
variables ($ty and $base) so t is applied to (b - a) then added to a to
guarantee exact endpoints for t == 0.0 and t == 1.0.
- Around line 6-8: Change the trait method signature from a static associated
function to an instance method: update pub trait Interpolation<M> { fn interp(a:
&Self, b: &Self, t: f32) -> Self; } so it becomes fn interp(&self, other: &Self,
t: f32) -> Self; and then update all implementations and call sites (e.g.,
usages in Transform impls in bevy_motiongfx that currently call
Interpolation::interp(&a,&b,t)) to use the instance form a.interp(&b, t). Keep
the generic parameter M as-is and ensure lifetimes/borrows still compile when
converting impl blocks and tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 85ce7ed0-2b51-48bc-99e1-f63286d0f6de

📥 Commits

Reviewing files that changed from the base of the PR and between 8d37bd8 and 37f4cae.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (29)
  • .github/workflows/rust.yml
  • Cargo.toml
  • crates/bevy_motiongfx/Cargo.toml
  • crates/bevy_motiongfx/README.md
  • crates/bevy_motiongfx/src/interpolation.rs
  • crates/bevy_motiongfx/src/lib.rs
  • crates/bevy_motiongfx/src/manager.rs
  • crates/motiongfx/Cargo.toml
  • crates/motiongfx/README.md
  • crates/motiongfx/docs/world.rs
  • crates/motiongfx/examples/custom_world.rs
  • crates/motiongfx/src/interpolation.rs
  • crates/motiongfx/src/lib.rs
  • crates/motiongfx/src/timeline.rs
  • crates/peniko_motiongfx/Cargo.toml
  • crates/peniko_motiongfx/src/interpolation.rs
  • crates/peniko_motiongfx/src/lib.rs
  • crates/peniko_motiongfx/src/trace.rs
  • examples/bevy_examples/Cargo.toml
  • examples/bevy_examples/examples/custom_ease.rs
  • examples/bevy_examples/examples/custom_interp.rs
  • examples/bevy_examples/examples/easings.rs
  • examples/bevy_examples/examples/hello_world.rs
  • examples/bevy_examples/examples/minimal.rs
  • examples/bevy_examples/examples/recording.rs
  • examples/bevy_examples/examples/slide_basic.rs
  • examples/vello_winit_example/Cargo.toml
  • examples/vello_winit_example/examples/lissajous.rs
  • examples/vello_winit_example/src/lib.rs
💤 Files with no reviewable changes (2)
  • crates/bevy_motiongfx/src/lib.rs
  • examples/bevy_examples/Cargo.toml
✅ Files skipped from review due to trivial changes (1)
  • crates/motiongfx/README.md
🚧 Files skipped from review as they are similar to previous changes (11)
  • crates/motiongfx/src/lib.rs
  • examples/bevy_examples/examples/custom_interp.rs
  • examples/bevy_examples/examples/recording.rs
  • examples/bevy_examples/examples/custom_ease.rs
  • crates/motiongfx/Cargo.toml
  • examples/bevy_examples/examples/easings.rs
  • examples/bevy_examples/examples/hello_world.rs
  • crates/motiongfx/src/timeline.rs
  • crates/motiongfx/docs/world.rs
  • examples/bevy_examples/examples/slide_basic.rs
  • crates/motiongfx/examples/custom_world.rs

@nixonyh
nixonyh merged commit 1a77b8a into main May 15, 2026
8 checks passed
@nixonyh
nixonyh deleted the nixon/kurbo-support branch May 15, 2026 16:09
nixonyh added a commit that referenced this pull request May 16, 2026
- Added `peniko_motiongfx` crate that covers common interpolation &
tracing implementations
- Move `Interpolation<M>` trait into `motiongfx` and add a marker type
to workaround the orphan rule.
- Add `Tracer<T>` type in `peniko_motiongfx` to handle tracing
operations.
- Add examples to showcase animations using `peniko`, rendered via
`vello`.

https://github.com/user-attachments/assets/69354a82-53d3-48e1-b3a3-8e86ea8b54fa

---------
@coderabbitai coderabbitai Bot mentioned this pull request Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant