Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/blended transition #296

Draft
wants to merge 4 commits into
base: refactor/sm-v2
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/chore_update_thorvg_to_v10_pre12.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
default: patch
---

# chore: Update ThorVG to v1.0-pre12
33 changes: 33 additions & 0 deletions .changeset/feat_add_animation_tweening_support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
default: minor
---

# feat: add animation tweening support

Added new tweening capabilities to smoothly animate between frames using cubic bezier easing:

- Added `tween` method to directly tween between two frames with linear progress:

```rust
animation.tween(10.0, 20.0, 0.5); // Tween from frame 10 to 20 at 50% progress
```

- Added `tween_to` method for time-based tweening with custom easing:

```rust
// Tween to frame 20 from the current frame over 2 seconds using custom easing curve
animation.tween_to(20.0, 2.0, [0.4, 0.0, 0.6, 1.0]);
```

- Added `tween_to_marker` method for marker-based tweening with custom easing:

```rust
// Tween to marker "jump" from the current frame over 2 seconds using custom easing curve
animation.tween_to_marker("jump", 2.0, [0.4, 0.0, 0.6, 1.0]);
```

- Added helper methods to manage tween state:
- `is_tweening()` - Check if animation is currently tweening
- `tween_update()` - Update tween progress based on elapsed time

The tweening implementation uses cubic bezier interpolation for smooth easing effects. The easing curve is defined by control points P1(x1,y1) and P2(x2,y2), with P0(0,0) and P3(1,1) fixed.
2 changes: 1 addition & 1 deletion deps/modules/thorvg
Submodule thorvg updated 62 files
+6 −5 examples/Example.h
+0 −152 examples/Interaction.cpp
+212 −0 examples/LottieTweening.cpp
+1 −1 examples/meson.build
+17,646 −1 examples/resources/lottie/emoji.json
+0 −10,024 examples/resources/lottie/extensions/locker.json
+1 −1 inc/thorvg.h
+ res/example_svg.png
+19 −0 src/bindings/capi/thorvg_capi.h
+10 −0 src/bindings/capi/tvgCapi.cpp
+3 −0 src/common/tvgArray.h
+11 −0 src/common/tvgMath.h
+16 −0 src/loaders/lottie/thorvg_lottie.h
+12 −2 src/loaders/lottie/tvgLottieAnimation.cpp
+256 −263 src/loaders/lottie/tvgLottieBuilder.cpp
+43 −5 src/loaders/lottie/tvgLottieBuilder.h
+8 −0 src/loaders/lottie/tvgLottieData.h
+10 −10 src/loaders/lottie/tvgLottieExpressions.cpp
+4 −5 src/loaders/lottie/tvgLottieExpressions.h
+31 −9 src/loaders/lottie/tvgLottieLoader.cpp
+4 −1 src/loaders/lottie/tvgLottieLoader.h
+15 −15 src/loaders/lottie/tvgLottieModel.cpp
+34 −28 src/loaders/lottie/tvgLottieModel.h
+126 −109 src/loaders/lottie/tvgLottieModifier.cpp
+51 −13 src/loaders/lottie/tvgLottieModifier.h
+64 −121 src/loaders/lottie/tvgLottieParser.cpp
+7 −6 src/loaders/lottie/tvgLottieParser.h
+201 −201 src/loaders/lottie/tvgLottieProperty.h
+20 −26 src/loaders/svg/tvgSvgLoader.cpp
+1 −4 src/loaders/svg/tvgSvgLoader.h
+3 −10 src/loaders/svg/tvgSvgLoaderCommon.h
+1 −4 src/loaders/svg/tvgSvgSceneBuilder.cpp
+6 −0 src/renderer/gl_engine/tvgGlRenderer.cpp
+1 −0 src/renderer/gl_engine/tvgGlRenderer.h
+6 −6 src/renderer/gl_engine/tvgGlShaderSrc.cpp
+26 −312 src/renderer/gl_engine/tvgGlTessellator.cpp
+1 −20 src/renderer/gl_engine/tvgGlTessellator.h
+2 −0 src/renderer/meson.build
+7 −7 src/renderer/sw_engine/tvgSwCommon.h
+8 −1 src/renderer/sw_engine/tvgSwRenderer.cpp
+1 −0 src/renderer/sw_engine/tvgSwRenderer.h
+63 −127 src/renderer/sw_engine/tvgSwShape.cpp
+1 −1 src/renderer/tvgFill.h
+4 −4 src/renderer/tvgPaint.cpp
+18 −44 src/renderer/tvgRender.h
+4 −1 src/renderer/tvgScene.h
+5 −0 src/renderer/tvgShape.h
+334 −0 src/renderer/tvgTrimPath.cpp
+42 −0 src/renderer/tvgTrimPath.h
+58 −7 src/renderer/wg_engine/tvgWgCompositor.cpp
+4 −0 src/renderer/wg_engine/tvgWgCompositor.h
+36 −36 src/renderer/wg_engine/tvgWgGeometry.h
+21 −0 src/renderer/wg_engine/tvgWgPipelines.cpp
+8 −0 src/renderer/wg_engine/tvgWgPipelines.h
+144 −82 src/renderer/wg_engine/tvgWgRenderData.cpp
+49 −2 src/renderer/wg_engine/tvgWgRenderData.h
+77 −13 src/renderer/wg_engine/tvgWgRenderer.cpp
+5 −1 src/renderer/wg_engine/tvgWgRenderer.h
+99 −7 src/renderer/wg_engine/tvgWgShaderSrc.cpp
+3 −1 src/renderer/wg_engine/tvgWgShaderSrc.h
+23 −0 src/renderer/wg_engine/tvgWgShaderTypes.cpp
+10 −0 src/renderer/wg_engine/tvgWgShaderTypes.h
7 changes: 7 additions & 0 deletions dotlottie-ffi/emscripten_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ EMSCRIPTEN_BINDINGS(DotLottiePlayer)
.function("stateMachineGetStringTrigger", &DotLottiePlayer::state_machine_get_string_trigger)
.function("stateMachineGetBooleanTrigger", &DotLottiePlayer::state_machine_get_boolean_trigger)
.function("getLayerBounds", &DotLottiePlayer::get_layer_bounds)
.function("tick", &DotLottiePlayer::tick)
.function("isTweening", &DotLottiePlayer::is_tweening)
.function("tweenUpdate", &DotLottiePlayer::tween_update)
.function("tween", &DotLottiePlayer::tween)
.function("tweenTo", &DotLottiePlayer::tween_to)
.function("tweenToMarker", &DotLottiePlayer::tween_to_marker)

.function("getStateMachine", &DotLottiePlayer::get_state_machine)
.function("activeStateMachineId", &DotLottiePlayer::active_state_machine_id)
.function("stateMachineCurrentState", &DotLottiePlayer::state_machine_current_state)
Expand Down
7 changes: 7 additions & 0 deletions dotlottie-ffi/src/dotlottie_player.udl
Original file line number Diff line number Diff line change
Expand Up @@ -184,4 +184,11 @@ interface DotLottiePlayer {
boolean state_machine_unsubscribe([ByRef] StateMachineObserver observer);
boolean state_machine_override_current_state([ByRef] string state_name, boolean do_tick);
string state_machine_status();

boolean tick();
boolean tween(f32 from, f32 to, f32 progress);
boolean tween_to(f32 to, f32 duration, sequence<f32> easing);
boolean tween_to_marker([ByRef] string marker, f32 duration, sequence<f32> easing);
boolean is_tweening();
boolean tween_update();
};
8 changes: 8 additions & 0 deletions dotlottie-ffi/src/dotlottie_player_cpp.udl
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,12 @@ interface DotLottiePlayer {
boolean state_machine_override_current_state([ByRef] string state_name, boolean do_tick);
u32 instance_id();
string state_machine_status();


boolean tick();
boolean tween(f32 from, f32 to, f32 progress);
boolean tween_to(f32 to, f32 duration, sequence<f32> easing);
boolean tween_to_marker([ByRef] string marker, f32 duration, sequence<f32> easing);
boolean is_tweening();
boolean tween_update();
};
110 changes: 110 additions & 0 deletions dotlottie-rs/src/dotlottie_player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,31 @@ impl DotLottieRuntime {
pub fn set_active_state_machine_id(&mut self, state_machine_id: &str) {
self.active_state_machine_id = state_machine_id.to_string();
}

pub fn tween(&mut self, from: f32, to: f32, progress: f32) -> bool {
self.renderer.tween(from, to, progress).is_ok()
}

pub fn tween_to(&mut self, to: f32, duration: f32, easing: [f32; 4]) -> bool {
self.renderer.tween_to(to, duration, easing).is_ok()
}

pub fn tween_to_marker(&mut self, marker: &str, duration: f32, easing: [f32; 4]) -> bool {
let markers = self.markers();
if let Some(marker) = markers.iter().find(|m| m.name == marker) {
self.tween_to(marker.time, duration, easing)
} else {
false
}
}

pub fn is_tweening(&self) -> bool {
self.renderer.is_tweening()
}

pub fn tween_update(&mut self) -> bool {
self.renderer.tween_update().is_ok()
}
}

pub struct DotLottiePlayerContainer {
Expand Down Expand Up @@ -1689,6 +1714,60 @@ impl DotLottiePlayerContainer {
pub fn instance_id(&self) -> u32 {
self.instance_id
}

pub fn tick(&self) -> bool {
let next_frame = self.request_frame();
if self.is_tweening() {
self.tween_update() && self.render()
} else {
// Todo: This is disgusting to lock on every tick
match self.state_machine.try_read() {
Ok(state_machine) => {
if state_machine.is_none() {
// return false;
}
}
Err(_) => {
// return false;
}
}

match self.state_machine.try_write() {
Ok(mut state_machine) => {
if let Some(sm) = state_machine.as_mut() {
sm.resume_from_blending();
}
}
Err(_) => {
// return false;
}
}
self.set_frame(next_frame) && self.render()
}
}

pub fn tween(&self, from: f32, to: f32, progress: f32) -> bool {
self.runtime.write().unwrap().tween(from, to, progress)
}

pub fn tween_to(&self, to: f32, duration: f32, easing: [f32; 4]) -> bool {
self.runtime.write().unwrap().tween_to(to, duration, easing)
}

pub fn tween_to_marker(&self, marker: &str, duration: f32, easing: [f32; 4]) -> bool {
self.runtime
.write()
.unwrap()
.tween_to_marker(marker, duration, easing)
}

pub fn is_tweening(&self) -> bool {
self.runtime.read().unwrap().is_tweening()
}

pub fn tween_update(&self) -> bool {
self.runtime.write().unwrap().tween_update()
}
}

pub struct DotLottiePlayer {
Expand Down Expand Up @@ -2401,6 +2480,37 @@ impl DotLottiePlayer {

"".to_string()
}

pub fn tick(&self) -> bool {
self.player.read().unwrap().tick()
}

pub fn tween(&self, from: f32, to: f32, progress: f32) -> bool {
self.player.read().unwrap().tween(from, to, progress)
}

pub fn is_tweening(&self) -> bool {
self.player.read().unwrap().is_tweening()
}

pub fn tween_update(&self) -> bool {
self.player.read().unwrap().tween_update()
}

pub fn tween_to(&self, to: f32, duration: f32, easing: Vec<f32>) -> bool {
self.player
.read()
.unwrap()
.tween_to(to, duration, easing.try_into().unwrap())
}

pub fn tween_to_marker(&self, marker_name: &str, duration: f32, easing: Vec<f32>) -> bool {
self.player.read().unwrap().tween_to_marker(
marker_name,
duration,
easing.try_into().unwrap(),
)
}
}

unsafe impl Send for DotLottiePlayer {}
Expand Down
38 changes: 38 additions & 0 deletions dotlottie-rs/src/lottie_renderer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,19 @@ pub trait LottieRenderer {
&self,
layer_name: &str,
) -> Result<(f32, f32, f32, f32), LottieRendererError>;

fn tween(&mut self, from: f32, to: f32, progress: f32) -> Result<(), LottieRendererError>;

fn tween_to(
&mut self,
to: f32,
duration: f32,
easing: [f32; 4],
) -> Result<(), LottieRendererError>;

fn is_tweening(&self) -> bool;

fn tween_update(&mut self) -> Result<bool, LottieRendererError>;
}

impl dyn LottieRenderer {
Expand Down Expand Up @@ -332,6 +345,31 @@ impl<R: Renderer> LottieRenderer for LottieRendererImpl<R> {
self.animation.set_slots(slots).map_err(into_lottie::<R>)
}

fn tween(&mut self, from: f32, to: f32, progress: f32) -> Result<(), LottieRendererError> {
self.animation
.tween(from, to, progress)
.map_err(into_lottie::<R>)
}

fn tween_to(
&mut self,
to: f32,
duration: f32,
easing: [f32; 4],
) -> Result<(), LottieRendererError> {
self.animation
.tween_to(to, duration, easing)
.map_err(into_lottie::<R>)
}

fn is_tweening(&self) -> bool {
self.animation.is_tweening()
}

fn tween_update(&mut self) -> Result<bool, LottieRendererError> {
self.animation.tween_update().map_err(into_lottie::<R>)
}

fn set_layout(&mut self, layout: &Layout) -> Result<(), LottieRendererError> {
if self.layout == *layout {
return Ok(());
Expand Down
8 changes: 8 additions & 0 deletions dotlottie-rs/src/lottie_renderer/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ pub trait Animation: Default {
fn get_frame(&self) -> Result<f32, Self::Error>;

fn set_slots(&mut self, slots: &str) -> Result<(), Self::Error>;

fn tween(&mut self, from: f32, to: f32, progress: f32) -> Result<(), Self::Error>;

fn tween_to(&mut self, to: f32, duration: f32, easing: [f32; 4]) -> Result<(), Self::Error>;

fn tween_update(&mut self) -> Result<bool, Self::Error>;

fn is_tweening(&self) -> bool;
}

pub trait Renderer: Sized + 'static {
Expand Down
Loading