add staggered text
rename box to rect refactor rust
This commit is contained in:
@@ -1,241 +0,0 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::animation::timeline::Timeline;
|
||||
|
||||
use super::{
|
||||
paint::{Paint, TextPaint},
|
||||
utils::timestamp_to_frame,
|
||||
values::{AnimatedFloatVec2, AnimatedValue},
|
||||
};
|
||||
|
||||
//#region Animateable Objects
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum AnimatedEntity {
|
||||
Text(AnimatedTextEntity),
|
||||
Ellipse(AnimatedEllipseEntity),
|
||||
Box(AnimatedBoxEntity),
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum Entity {
|
||||
Text(TextEntity),
|
||||
Ellipse(EllipseEntity),
|
||||
Box(BoxEntity),
|
||||
}
|
||||
|
||||
impl AnimatedEntity {
|
||||
pub fn calculate(&mut self, timeline: &Timeline) -> Option<Entity> {
|
||||
match self {
|
||||
Self::Text(text_entity) => text_entity.calculate(timeline),
|
||||
Self::Box(box_entity) => box_entity.calculate(timeline),
|
||||
Self::Ellipse(ellipse_entity) => ellipse_entity.calculate(timeline),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AnimatedTextEntity {
|
||||
pub text: String,
|
||||
pub origin: AnimatedFloatVec2,
|
||||
pub paint: TextPaint,
|
||||
pub animation_data: AnimationData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct TextEntity {
|
||||
pub text: String,
|
||||
pub origin: (f32, f32),
|
||||
pub paint: TextPaint,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AnimatedBoxEntity {
|
||||
pub position: AnimatedFloatVec2,
|
||||
pub size: AnimatedFloatVec2,
|
||||
pub origin: AnimatedFloatVec2,
|
||||
pub paint: Paint,
|
||||
pub animation_data: AnimationData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct BoxEntity {
|
||||
pub position: (f32, f32),
|
||||
pub size: (f32, f32),
|
||||
pub origin: (f32, f32),
|
||||
pub paint: Paint,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AnimatedEllipseEntity {
|
||||
pub paint: Paint,
|
||||
pub radius: AnimatedFloatVec2,
|
||||
pub origin: AnimatedFloatVec2,
|
||||
pub position: AnimatedFloatVec2,
|
||||
pub animation_data: AnimationData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct EllipseEntity {
|
||||
pub radius: (f32, f32),
|
||||
pub position: (f32, f32),
|
||||
pub origin: (f32, f32),
|
||||
pub paint: Paint,
|
||||
}
|
||||
|
||||
pub trait Animateable {
|
||||
fn sort_keyframes(&mut self);
|
||||
|
||||
fn calculate(&mut self, timeline: &Timeline) -> Option<Entity>;
|
||||
|
||||
// Checks if the Box is visible and should be drawn
|
||||
fn should_draw(&self, animation_data: &AnimationData, timeline: &Timeline) -> bool {
|
||||
let start_frame = timestamp_to_frame(animation_data.offset, timeline.fps);
|
||||
let end_frame = timestamp_to_frame(
|
||||
animation_data.offset + animation_data.duration,
|
||||
timeline.fps,
|
||||
);
|
||||
|
||||
// println!("start {0} end {1}", start_frame, end_frame);
|
||||
|
||||
let is_before = timeline.render_state.curr_frame < start_frame;
|
||||
let is_after = timeline.render_state.curr_frame > end_frame;
|
||||
let is_between = !is_after && !is_before;
|
||||
|
||||
if is_between {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AnimatedTextEntity {
|
||||
fn into_static(&mut self, timeline: &Timeline) -> TextEntity {
|
||||
self.sort_keyframes();
|
||||
|
||||
let origin = self.origin.get_value_at_frame(
|
||||
timeline.render_state.curr_frame,
|
||||
&self.animation_data,
|
||||
timeline.fps,
|
||||
);
|
||||
|
||||
TextEntity {
|
||||
text: self.text.clone(),
|
||||
origin,
|
||||
paint: self.paint.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Animateable for AnimatedTextEntity {
|
||||
fn calculate(&mut self, timeline: &Timeline) -> Option<Entity> {
|
||||
let should_draw = self.should_draw(&self.animation_data, timeline);
|
||||
|
||||
if should_draw {
|
||||
self.sort_keyframes();
|
||||
|
||||
Some(Entity::Text(self.into_static(timeline)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn sort_keyframes(&mut self) {
|
||||
self.origin.sort_keyframes();
|
||||
}
|
||||
}
|
||||
|
||||
impl Animateable for AnimatedBoxEntity {
|
||||
fn sort_keyframes(&mut self) {
|
||||
self.position.sort_keyframes();
|
||||
self.size.sort_keyframes();
|
||||
}
|
||||
|
||||
fn calculate(&mut self, timeline: &Timeline) -> Option<Entity> {
|
||||
let should_draw = self.should_draw(&self.animation_data, timeline);
|
||||
|
||||
if should_draw {
|
||||
self.sort_keyframes();
|
||||
|
||||
let position = self.position.get_value_at_frame(
|
||||
timeline.render_state.curr_frame,
|
||||
&self.animation_data,
|
||||
timeline.fps,
|
||||
);
|
||||
|
||||
let size = self.size.get_value_at_frame(
|
||||
timeline.render_state.curr_frame,
|
||||
&self.animation_data,
|
||||
timeline.fps,
|
||||
);
|
||||
|
||||
let origin = self.origin.get_value_at_frame(
|
||||
timeline.render_state.curr_frame,
|
||||
&self.animation_data,
|
||||
timeline.fps,
|
||||
);
|
||||
|
||||
Some(Entity::Box(BoxEntity {
|
||||
position,
|
||||
size,
|
||||
origin,
|
||||
paint: self.paint.clone(),
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Animateable for AnimatedEllipseEntity {
|
||||
fn calculate(&mut self, timeline: &Timeline) -> Option<Entity> {
|
||||
let should_draw = self.should_draw(&self.animation_data, timeline);
|
||||
|
||||
if should_draw {
|
||||
self.sort_keyframes();
|
||||
|
||||
let radius = self.radius.get_value_at_frame(
|
||||
timeline.render_state.curr_frame,
|
||||
&self.animation_data,
|
||||
timeline.fps,
|
||||
);
|
||||
|
||||
let position = self.position.get_value_at_frame(
|
||||
timeline.render_state.curr_frame,
|
||||
&self.animation_data,
|
||||
timeline.fps,
|
||||
);
|
||||
|
||||
let origin = self.origin.get_value_at_frame(
|
||||
timeline.render_state.curr_frame,
|
||||
&self.animation_data,
|
||||
timeline.fps,
|
||||
);
|
||||
|
||||
Some(Entity::Ellipse(EllipseEntity {
|
||||
radius,
|
||||
position,
|
||||
origin,
|
||||
paint: self.paint.clone(),
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn sort_keyframes(&mut self) {
|
||||
self.position.sort_keyframes();
|
||||
self.radius.sort_keyframes();
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AnimationData {
|
||||
pub offset: f32,
|
||||
pub duration: f32,
|
||||
pub visible: bool,
|
||||
}
|
||||
73
app/src-tauri/src/animation/primitives/entities/common.rs
Normal file
73
app/src-tauri/src/animation/primitives/entities/common.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::animation::{primitives::utils::timestamp_to_frame, timeline::Timeline};
|
||||
|
||||
use super::{
|
||||
ellipse::{AnimatedEllipseEntity, EllipseEntity},
|
||||
rect::{AnimatedRectEntity, RectEntity},
|
||||
staggered_text::{AnimatedStaggeredTextEntity, StaggeredTextEntity},
|
||||
text::{AnimatedTextEntity, TextEntity},
|
||||
};
|
||||
|
||||
pub trait Drawable {
|
||||
fn should_draw(&self, animation_data: &AnimationData, timeline: &Timeline) -> bool {
|
||||
let start_frame = timestamp_to_frame(animation_data.offset, timeline.fps);
|
||||
let end_frame = timestamp_to_frame(
|
||||
animation_data.offset + animation_data.duration,
|
||||
timeline.fps,
|
||||
);
|
||||
|
||||
// println!("start {0} end {1}", start_frame, end_frame);
|
||||
|
||||
let is_before = timeline.render_state.curr_frame < start_frame;
|
||||
let is_after = timeline.render_state.curr_frame > end_frame;
|
||||
let is_between = !is_after && !is_before;
|
||||
|
||||
if is_between {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Animateable {
|
||||
fn sort_keyframes(&mut self);
|
||||
|
||||
fn calculate(&mut self, timeline: &Timeline) -> Option<Entity>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum AnimatedEntity {
|
||||
Text(AnimatedTextEntity),
|
||||
StaggeredText(AnimatedStaggeredTextEntity),
|
||||
Ellipse(AnimatedEllipseEntity),
|
||||
Rect(AnimatedRectEntity),
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum Entity {
|
||||
Text(TextEntity),
|
||||
StaggeredText(StaggeredTextEntity),
|
||||
Ellipse(EllipseEntity),
|
||||
Rect(RectEntity),
|
||||
}
|
||||
|
||||
impl AnimatedEntity {
|
||||
pub fn calculate(&mut self, timeline: &Timeline) -> Option<Entity> {
|
||||
match self {
|
||||
Self::Text(text_entity) => text_entity.calculate(timeline),
|
||||
Self::Rect(box_entity) => box_entity.calculate(timeline),
|
||||
Self::StaggeredText(staggered_text_entity) => staggered_text_entity.calculate(timeline),
|
||||
Self::Ellipse(ellipse_entity) => ellipse_entity.calculate(timeline),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AnimationData {
|
||||
pub offset: f32,
|
||||
pub duration: f32,
|
||||
pub visible: bool,
|
||||
}
|
||||
86
app/src-tauri/src/animation/primitives/entities/ellipse.rs
Normal file
86
app/src-tauri/src/animation/primitives/entities/ellipse.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::animation::{
|
||||
primitives::{
|
||||
paint::Paint,
|
||||
transform::{AnimatedTransform, Transform},
|
||||
values::{AnimatedFloatVec2, AnimatedValue},
|
||||
},
|
||||
timeline::Timeline,
|
||||
};
|
||||
|
||||
use super::common::{Animateable, AnimationData, Drawable, Entity};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AnimatedEllipseEntity {
|
||||
pub paint: Paint,
|
||||
pub radius: AnimatedFloatVec2,
|
||||
pub origin: AnimatedFloatVec2,
|
||||
pub position: AnimatedFloatVec2,
|
||||
pub animation_data: AnimationData,
|
||||
pub transform: Option<AnimatedTransform>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct EllipseEntity {
|
||||
pub radius: (f32, f32),
|
||||
pub position: (f32, f32),
|
||||
pub origin: (f32, f32),
|
||||
pub paint: Paint,
|
||||
pub transform: Option<Transform>,
|
||||
}
|
||||
|
||||
impl Drawable for AnimatedEllipseEntity {}
|
||||
impl Animateable for AnimatedEllipseEntity {
|
||||
fn calculate(&mut self, timeline: &Timeline) -> Option<Entity> {
|
||||
let should_draw = self.should_draw(&self.animation_data, timeline);
|
||||
|
||||
if should_draw {
|
||||
self.sort_keyframes();
|
||||
|
||||
let radius = self.radius.get_value_at_frame(
|
||||
timeline.render_state.curr_frame,
|
||||
&self.animation_data,
|
||||
timeline.fps,
|
||||
);
|
||||
|
||||
let position = self.position.get_value_at_frame(
|
||||
timeline.render_state.curr_frame,
|
||||
&self.animation_data,
|
||||
timeline.fps,
|
||||
);
|
||||
|
||||
let origin = self.origin.get_value_at_frame(
|
||||
timeline.render_state.curr_frame,
|
||||
&self.animation_data,
|
||||
timeline.fps,
|
||||
);
|
||||
|
||||
let transform: Option<Transform> = match self.transform.clone() {
|
||||
Some(mut val) => Some(val.calculate(timeline, &self.animation_data)),
|
||||
None => None,
|
||||
};
|
||||
|
||||
Some(Entity::Ellipse(EllipseEntity {
|
||||
radius,
|
||||
position,
|
||||
origin,
|
||||
paint: self.paint.clone(),
|
||||
transform,
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn sort_keyframes(&mut self) {
|
||||
let transform = self.transform.clone();
|
||||
|
||||
if let Some(mut transform) = transform {
|
||||
transform.sort_keyframes();
|
||||
}
|
||||
|
||||
self.position.sort_keyframes();
|
||||
self.radius.sort_keyframes();
|
||||
}
|
||||
}
|
||||
5
app/src-tauri/src/animation/primitives/entities/mod.rs
Normal file
5
app/src-tauri/src/animation/primitives/entities/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
pub mod common;
|
||||
pub mod ellipse;
|
||||
pub mod rect;
|
||||
pub mod staggered_text;
|
||||
pub mod text;
|
||||
84
app/src-tauri/src/animation/primitives/entities/rect.rs
Normal file
84
app/src-tauri/src/animation/primitives/entities/rect.rs
Normal file
@@ -0,0 +1,84 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::animation::{
|
||||
primitives::{
|
||||
paint::Paint,
|
||||
transform::{AnimatedTransform, Transform},
|
||||
values::{AnimatedFloatVec2, AnimatedValue},
|
||||
},
|
||||
timeline::Timeline,
|
||||
};
|
||||
|
||||
use super::common::{Animateable, AnimationData, Drawable, Entity};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AnimatedRectEntity {
|
||||
pub position: AnimatedFloatVec2,
|
||||
pub size: AnimatedFloatVec2,
|
||||
pub origin: AnimatedFloatVec2,
|
||||
pub paint: Paint,
|
||||
pub animation_data: AnimationData,
|
||||
pub transform: Option<AnimatedTransform>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct RectEntity {
|
||||
pub position: (f32, f32),
|
||||
pub size: (f32, f32),
|
||||
pub origin: (f32, f32),
|
||||
pub paint: Paint,
|
||||
pub transform: Option<Transform>,
|
||||
}
|
||||
|
||||
impl Drawable for AnimatedRectEntity {}
|
||||
impl Animateable for AnimatedRectEntity {
|
||||
fn sort_keyframes(&mut self) {
|
||||
if let Some(x) = &mut self.transform {
|
||||
x.sort_keyframes();
|
||||
}
|
||||
|
||||
self.position.sort_keyframes();
|
||||
self.size.sort_keyframes();
|
||||
}
|
||||
|
||||
fn calculate(&mut self, timeline: &Timeline) -> Option<Entity> {
|
||||
let should_draw = self.should_draw(&self.animation_data, timeline);
|
||||
|
||||
if should_draw {
|
||||
self.sort_keyframes();
|
||||
|
||||
let position = self.position.get_value_at_frame(
|
||||
timeline.render_state.curr_frame,
|
||||
&self.animation_data,
|
||||
timeline.fps,
|
||||
);
|
||||
|
||||
let size = self.size.get_value_at_frame(
|
||||
timeline.render_state.curr_frame,
|
||||
&self.animation_data,
|
||||
timeline.fps,
|
||||
);
|
||||
|
||||
let origin = self.origin.get_value_at_frame(
|
||||
timeline.render_state.curr_frame,
|
||||
&self.animation_data,
|
||||
timeline.fps,
|
||||
);
|
||||
|
||||
let transform: Option<Transform> = match self.transform.clone() {
|
||||
Some(mut val) => Some(val.calculate(timeline, &self.animation_data)),
|
||||
None => None,
|
||||
};
|
||||
|
||||
Some(Entity::Rect(RectEntity {
|
||||
position,
|
||||
size,
|
||||
origin,
|
||||
paint: self.paint.clone(),
|
||||
transform,
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
use super::common::{Animateable, AnimationData, Drawable, Entity};
|
||||
use crate::animation::{
|
||||
primitives::{
|
||||
paint::TextPaint,
|
||||
transform::{AnimatedTransform, Transform},
|
||||
},
|
||||
timeline::Timeline,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AnimatedStaggeredTextLetter {
|
||||
pub transform: Option<AnimatedTransform>,
|
||||
pub paint: TextPaint,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct StaggeredTextLetter {
|
||||
pub transform: Option<Transform>,
|
||||
pub paint: TextPaint,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AnimatedStaggeredTextEntity {
|
||||
pub text: String,
|
||||
pub stagger: f32,
|
||||
pub animation_data: AnimationData,
|
||||
pub transform: Option<AnimatedTransform>,
|
||||
pub letter: AnimatedStaggeredTextLetter,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct StaggeredTextEntity {
|
||||
pub text: String,
|
||||
pub stagger: f32,
|
||||
pub transform: Option<Transform>,
|
||||
pub animation_data: AnimationData,
|
||||
pub letter: StaggeredTextLetter,
|
||||
}
|
||||
|
||||
impl Drawable for AnimatedStaggeredTextEntity {}
|
||||
impl Animateable for AnimatedStaggeredTextEntity {
|
||||
fn calculate(&mut self, timeline: &Timeline) -> Option<Entity> {
|
||||
let should_draw: bool = self.should_draw(&self.animation_data, timeline);
|
||||
|
||||
if should_draw {
|
||||
self.sort_keyframes();
|
||||
|
||||
let transform: Option<Transform> = match self.transform.clone() {
|
||||
Some(mut val) => Some(val.calculate(timeline, &self.animation_data)),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let letter_transform: Option<Transform> = match self.letter.transform.clone() {
|
||||
Some(mut val) => Some(val.calculate(timeline, &self.animation_data)),
|
||||
None => None,
|
||||
};
|
||||
|
||||
Some(Entity::StaggeredText(StaggeredTextEntity {
|
||||
transform,
|
||||
stagger: self.stagger,
|
||||
text: self.text.clone(),
|
||||
animation_data: self.animation_data.clone(),
|
||||
letter: StaggeredTextLetter {
|
||||
transform: letter_transform,
|
||||
paint: self.letter.paint.clone(),
|
||||
},
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn sort_keyframes(&mut self) {
|
||||
if let Some(x) = &mut self.transform {
|
||||
x.sort_keyframes();
|
||||
}
|
||||
|
||||
if let Some(x) = &mut self.letter.transform {
|
||||
x.sort_keyframes();
|
||||
}
|
||||
}
|
||||
}
|
||||
76
app/src-tauri/src/animation/primitives/entities/text.rs
Normal file
76
app/src-tauri/src/animation/primitives/entities/text.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
use crate::animation::{
|
||||
primitives::{
|
||||
paint::TextPaint,
|
||||
transform::{AnimatedTransform, Transform},
|
||||
values::{AnimatedFloatVec2, AnimatedValue},
|
||||
},
|
||||
timeline::Timeline,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::common::{Animateable, AnimationData, Drawable, Entity};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct TextEntity {
|
||||
pub text: String,
|
||||
pub origin: (f32, f32),
|
||||
pub paint: TextPaint,
|
||||
pub transform: Option<Transform>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AnimatedTextEntity {
|
||||
pub text: String,
|
||||
pub origin: AnimatedFloatVec2,
|
||||
pub paint: TextPaint,
|
||||
pub animation_data: AnimationData,
|
||||
pub transform: Option<AnimatedTransform>,
|
||||
}
|
||||
|
||||
impl Drawable for AnimatedTextEntity {}
|
||||
|
||||
impl AnimatedTextEntity {
|
||||
fn into_static(&mut self, timeline: &Timeline) -> TextEntity {
|
||||
self.sort_keyframes();
|
||||
|
||||
let origin = self.origin.get_value_at_frame(
|
||||
timeline.render_state.curr_frame,
|
||||
&self.animation_data,
|
||||
timeline.fps,
|
||||
);
|
||||
|
||||
let transform: Option<Transform> = match self.transform.clone() {
|
||||
Some(mut val) => Some(val.calculate(timeline, &self.animation_data)),
|
||||
None => None,
|
||||
};
|
||||
|
||||
TextEntity {
|
||||
transform,
|
||||
text: self.text.clone(),
|
||||
origin,
|
||||
paint: self.paint.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Animateable for AnimatedTextEntity {
|
||||
fn calculate(&mut self, timeline: &Timeline) -> Option<Entity> {
|
||||
let should_draw = self.should_draw(&self.animation_data, timeline);
|
||||
|
||||
if should_draw {
|
||||
self.sort_keyframes();
|
||||
|
||||
Some(Entity::Text(self.into_static(timeline)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn sort_keyframes(&mut self) {
|
||||
if let Some(x) = &mut self.transform {
|
||||
x.sort_keyframes();
|
||||
}
|
||||
|
||||
self.origin.sort_keyframes();
|
||||
}
|
||||
}
|
||||
@@ -8,20 +8,20 @@ use simple_easing::{
|
||||
quint_in_out, quint_out,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
|
||||
pub struct SpringProperties {
|
||||
pub mass: f32,
|
||||
pub damping: f32,
|
||||
pub stiffness: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
|
||||
pub struct SpringState {
|
||||
pub velocity: f32,
|
||||
pub last_val: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(tag = "easing_function")]
|
||||
pub enum EasingFunction {
|
||||
QuintOut,
|
||||
@@ -69,7 +69,7 @@ impl EasingFunction {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum InterpolationType {
|
||||
Linear,
|
||||
|
||||
@@ -3,12 +3,12 @@ use std::cmp::Ordering;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{
|
||||
entities::AnimationData,
|
||||
entities::common::AnimationData,
|
||||
interpolations::{interpolate_rendered_keyframes, InterpolationType},
|
||||
utils::render_keyframe,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Keyframe {
|
||||
pub value: f32,
|
||||
pub offset: f32,
|
||||
@@ -24,7 +24,7 @@ pub struct RenderedKeyframe {
|
||||
pub abs_distance_from_curr: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Keyframes {
|
||||
pub values: Vec<Keyframe>,
|
||||
}
|
||||
|
||||
@@ -3,5 +3,6 @@ pub mod interpolations;
|
||||
pub mod keyframe;
|
||||
pub mod paint;
|
||||
pub mod tests;
|
||||
pub mod transform;
|
||||
pub mod utils;
|
||||
pub mod values;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#[cfg(test)]
|
||||
use crate::animation::primitives::{
|
||||
entities::AnimationData,
|
||||
entities::common::AnimationData,
|
||||
interpolations::{calculate_spring_value, SpringProperties},
|
||||
keyframe::{Keyframe, Keyframes},
|
||||
utils::timestamp_to_frame,
|
||||
|
||||
64
app/src-tauri/src/animation/primitives/transform.rs
Normal file
64
app/src-tauri/src/animation/primitives/transform.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
use super::{
|
||||
entities::common::AnimationData,
|
||||
values::{AnimatedFloatVec2, AnimatedValue},
|
||||
};
|
||||
use crate::animation::timeline::Timeline;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct AnimatedTransform {
|
||||
pub translate: AnimatedFloatVec2,
|
||||
pub scale: AnimatedFloatVec2,
|
||||
pub skew: AnimatedFloatVec2,
|
||||
pub rotate: AnimatedFloatVec2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Transform {
|
||||
pub translate: (f32, f32),
|
||||
pub scale: (f32, f32),
|
||||
pub skew: (f32, f32),
|
||||
pub rotate: (f32, f32),
|
||||
}
|
||||
|
||||
impl AnimatedTransform {
|
||||
pub fn sort_keyframes(&mut self) {
|
||||
self.rotate.sort_keyframes();
|
||||
self.skew.sort_keyframes();
|
||||
self.scale.sort_keyframes();
|
||||
self.translate.sort_keyframes();
|
||||
}
|
||||
|
||||
pub fn calculate(&mut self, timeline: &Timeline, animation_data: &AnimationData) -> Transform {
|
||||
let skew = self.skew.get_value_at_frame(
|
||||
timeline.render_state.curr_frame,
|
||||
animation_data,
|
||||
timeline.fps,
|
||||
);
|
||||
|
||||
let scale = self.scale.get_value_at_frame(
|
||||
timeline.render_state.curr_frame,
|
||||
animation_data,
|
||||
timeline.fps,
|
||||
);
|
||||
|
||||
let translate = self.translate.get_value_at_frame(
|
||||
timeline.render_state.curr_frame,
|
||||
animation_data,
|
||||
timeline.fps,
|
||||
);
|
||||
|
||||
let rotate = self.rotate.get_value_at_frame(
|
||||
timeline.render_state.curr_frame,
|
||||
animation_data,
|
||||
timeline.fps,
|
||||
);
|
||||
|
||||
Transform {
|
||||
skew,
|
||||
scale,
|
||||
translate,
|
||||
rotate,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::{
|
||||
entities::AnimationData,
|
||||
entities::common::AnimationData,
|
||||
keyframe::{Keyframe, RenderedKeyframe},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{
|
||||
entities::AnimationData,
|
||||
entities::common::AnimationData,
|
||||
keyframe::{Keyframe, Keyframes},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub trait AnimatedValue<T> {
|
||||
fn sort_keyframes(&mut self);
|
||||
fn get_value_at_frame(&self, curr_frame: i32, animation_data: &AnimationData, fps: i16) -> T;
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct AnimatedFloat {
|
||||
pub keyframes: Keyframes,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct AnimatedFloatVec2 {
|
||||
pub keyframes: (AnimatedFloat, AnimatedFloat),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct AnimatedFloatVec3 {
|
||||
pub keyframes: (AnimatedFloat, AnimatedFloat, AnimatedFloat),
|
||||
}
|
||||
|
||||
impl AnimatedFloat {
|
||||
pub fn new(val: f32) -> AnimatedFloat {
|
||||
AnimatedFloat {
|
||||
@@ -41,6 +45,18 @@ impl AnimatedFloatVec2 {
|
||||
}
|
||||
}
|
||||
|
||||
impl AnimatedFloatVec3 {
|
||||
pub fn new(x: f32, y: f32, z: f32) -> AnimatedFloatVec3 {
|
||||
AnimatedFloatVec3 {
|
||||
keyframes: (
|
||||
AnimatedFloat::new(x),
|
||||
AnimatedFloat::new(y),
|
||||
AnimatedFloat::new(z),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AnimatedValue<f32> for AnimatedFloat {
|
||||
fn sort_keyframes(&mut self) {
|
||||
self.keyframes.sort();
|
||||
@@ -52,6 +68,38 @@ impl AnimatedValue<f32> for AnimatedFloat {
|
||||
}
|
||||
}
|
||||
|
||||
impl AnimatedValue<(f32, f32, f32)> for AnimatedFloatVec3 {
|
||||
fn sort_keyframes(&mut self) {
|
||||
self.keyframes.0.sort_keyframes();
|
||||
self.keyframes.1.sort_keyframes();
|
||||
self.keyframes.2.sort_keyframes();
|
||||
}
|
||||
|
||||
fn get_value_at_frame(
|
||||
&self,
|
||||
curr_frame: i32,
|
||||
animation_data: &AnimationData,
|
||||
fps: i16,
|
||||
) -> (f32, f32, f32) {
|
||||
let x = self
|
||||
.keyframes
|
||||
.0
|
||||
.get_value_at_frame(curr_frame, animation_data, fps);
|
||||
|
||||
let y = self
|
||||
.keyframes
|
||||
.1
|
||||
.get_value_at_frame(curr_frame, animation_data, fps);
|
||||
|
||||
let z = self
|
||||
.keyframes
|
||||
.1
|
||||
.get_value_at_frame(curr_frame, animation_data, fps);
|
||||
|
||||
return (x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
impl AnimatedValue<(f32, f32)> for AnimatedFloatVec2 {
|
||||
fn sort_keyframes(&mut self) {
|
||||
self.keyframes.0.sort_keyframes();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::animation::primitives::{
|
||||
entities::{AnimatedBoxEntity, AnimatedEntity, AnimatedTextEntity, AnimationData},
|
||||
interpolations::{EasingFunction, InterpolationType, SpringProperties},
|
||||
keyframe::{Keyframe, Keyframes},
|
||||
};
|
||||
@@ -7,7 +6,11 @@ use rayon::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::primitives::{
|
||||
entities::Entity,
|
||||
entities::{
|
||||
common::{AnimatedEntity, AnimationData, Entity},
|
||||
rect::AnimatedRectEntity,
|
||||
text::AnimatedTextEntity,
|
||||
},
|
||||
paint::{Color, FillStyle, Paint, PaintStyle, StrokeStyle, TextAlign, TextPaint},
|
||||
values::{AnimatedFloat, AnimatedFloatVec2},
|
||||
};
|
||||
@@ -47,14 +50,15 @@ impl Timeline {
|
||||
}
|
||||
}
|
||||
|
||||
fn build_bg(offset: f32, paint: Paint, size: (i32, i32)) -> AnimatedBoxEntity {
|
||||
let bg_box = AnimatedBoxEntity {
|
||||
fn build_bg(offset: f32, paint: Paint, size: (i32, i32)) -> AnimatedRectEntity {
|
||||
let bg_box = AnimatedRectEntity {
|
||||
paint,
|
||||
animation_data: AnimationData {
|
||||
offset: 0.0 + offset,
|
||||
duration: 5.0,
|
||||
visible: true,
|
||||
},
|
||||
transform: None,
|
||||
origin: AnimatedFloatVec2::new(1280.0 / 2.0, 720.0 / 2.0),
|
||||
position: AnimatedFloatVec2 {
|
||||
keyframes: (
|
||||
@@ -123,19 +127,19 @@ pub fn test_timeline_entities_at_frame(
|
||||
size: (i32, i32),
|
||||
input: Input,
|
||||
) -> Vec<Entity> {
|
||||
let box1_paint = Paint {
|
||||
let rect1_paint = Paint {
|
||||
style: PaintStyle::Fill(FillStyle {
|
||||
color: Color::new(34, 189, 58, 1.0),
|
||||
}),
|
||||
};
|
||||
|
||||
let box2_paint = Paint {
|
||||
let rect2_paint = Paint {
|
||||
style: PaintStyle::Fill(FillStyle {
|
||||
color: Color::new(23, 178, 28, 1.0),
|
||||
}),
|
||||
};
|
||||
|
||||
let box3_paint = Paint {
|
||||
let rect3_paint = Paint {
|
||||
style: PaintStyle::Fill(FillStyle {
|
||||
color: Color::new(43, 128, 98, 1.0),
|
||||
}),
|
||||
@@ -163,9 +167,9 @@ pub fn test_timeline_entities_at_frame(
|
||||
duration: 5.0,
|
||||
size,
|
||||
entities: vec![
|
||||
AnimatedEntity::Box(build_bg(0.0, box1_paint, size)),
|
||||
AnimatedEntity::Box(build_bg(0.5, box2_paint, size)),
|
||||
AnimatedEntity::Box(build_bg(1.0, box3_paint, size)),
|
||||
AnimatedEntity::Rect(build_bg(0.0, rect1_paint, size)),
|
||||
AnimatedEntity::Rect(build_bg(0.5, rect2_paint, size)),
|
||||
AnimatedEntity::Rect(build_bg(1.0, rect3_paint, size)),
|
||||
AnimatedEntity::Text(AnimatedTextEntity {
|
||||
paint: title_paint,
|
||||
text: input.title,
|
||||
@@ -174,6 +178,7 @@ pub fn test_timeline_entities_at_frame(
|
||||
duration: 6.0,
|
||||
visible: true,
|
||||
},
|
||||
transform: None,
|
||||
origin: AnimatedFloatVec2 {
|
||||
keyframes: (
|
||||
AnimatedFloat {
|
||||
@@ -218,6 +223,7 @@ pub fn test_timeline_entities_at_frame(
|
||||
duration: 6.0,
|
||||
visible: true,
|
||||
},
|
||||
transform: None,
|
||||
origin: AnimatedFloatVec2 {
|
||||
keyframes: (
|
||||
AnimatedFloat {
|
||||
|
||||
Reference in New Issue
Block a user