ranim_anims/
creation.rs

1use ranim_core::{
2    animation::{AnimationCell, Eval},
3    core_item::vitem::DEFAULT_STROKE_WIDTH,
4    traits::{Empty, FillColor, Interpolatable, Partial, StrokeColor, StrokeWidth},
5    utils::rate_functions::smooth,
6};
7use tracing::warn;
8
9// MARK: Creation
10
11/// The requirement of [`Create`] and [`UnCreate`]
12pub trait CreationRequirement: Clone + Partial + Empty + Interpolatable {}
13impl<T: Clone + Partial + Empty + Interpolatable> CreationRequirement for T {}
14
15/// The methods to create animations for `T` that satisfies [`CreationRequirement`]
16pub trait CreationAnim<T: CreationRequirement + 'static> {
17    /// Create a [`Create`] anim for `T`.
18    fn create(&mut self) -> AnimationCell<T>;
19    /// Create an [`UnCreate`] anim for `T`.
20    fn uncreate(&mut self) -> AnimationCell<T>;
21}
22
23impl<T: CreationRequirement + 'static> CreationAnim<T> for T {
24    fn create(&mut self) -> AnimationCell<T> {
25        Create::new(self.clone())
26            .into_animation_cell()
27            .with_rate_func(smooth)
28            .apply_to(self)
29    }
30    fn uncreate(&mut self) -> AnimationCell<T> {
31        UnCreate::new(self.clone())
32            .into_animation_cell()
33            .with_rate_func(smooth)
34            .apply_to(self)
35    }
36}
37
38// MARK: Writing
39/// The requirement of [`Write`] and [`Unwrite`]
40pub trait WritingRequirement: CreationRequirement + StrokeWidth + StrokeColor + FillColor {}
41impl<T: CreationRequirement + StrokeWidth + StrokeColor + FillColor> WritingRequirement for T {}
42
43/// The methods to create animations for `T` that satisfies [`WritingRequirement`]
44pub trait WritingAnim: WritingRequirement + Sized + 'static {
45    /// Create a [`Write`] anim for `T`.
46    fn write(&mut self) -> AnimationCell<Self>;
47    /// Create a [`Unwrite`] anim for `T`.
48    fn unwrite(&mut self) -> AnimationCell<Self>;
49}
50
51impl<T: WritingRequirement + Sized + 'static> WritingAnim for T {
52    fn write(&mut self) -> AnimationCell<Self> {
53        Write::new(self.clone())
54            .into_animation_cell()
55            .with_rate_func(smooth)
56            .apply_to(self)
57    }
58    fn unwrite(&mut self) -> AnimationCell<Self> {
59        Unwrite::new(self.clone())
60            .into_animation_cell()
61            .with_rate_func(smooth)
62            .apply_to(self)
63    }
64}
65
66// ---------------------------------------------------- //
67
68// MARK: Impl
69/// The create anim.
70///
71/// This anim uses [`Partial::get_partial_closed`] to create the item
72pub struct Create<T: CreationRequirement> {
73    /// The original object
74    pub original: T,
75}
76
77impl<T: CreationRequirement> Create<T> {
78    /// Constructor
79    pub fn new(target: T) -> Self {
80        Self { original: target }
81    }
82}
83
84impl<T: CreationRequirement> Eval<T> for Create<T> {
85    fn eval_alpha(&self, alpha: f64) -> T {
86        if alpha == 0.0 {
87            T::empty()
88        } else if 0.0 < alpha && alpha < 1.0 {
89            self.original.get_partial_closed(0.0..alpha)
90        } else if alpha == 1.0 {
91            self.original.clone()
92        } else {
93            unreachable!()
94        }
95    }
96}
97
98/// The uncreate anim
99///
100/// This anim uses [`Partial::get_partial_closed`] to uncreate the item
101pub struct UnCreate<T: CreationRequirement> {
102    /// The original object
103    pub original: T,
104}
105
106impl<T: CreationRequirement> UnCreate<T> {
107    /// Constructor
108    pub fn new(target: T) -> Self {
109        Self { original: target }
110    }
111}
112
113impl<T: CreationRequirement> Eval<T> for UnCreate<T> {
114    fn eval_alpha(&self, mut alpha: f64) -> T {
115        if !(0.0..=1.0).contains(&alpha) {
116            warn!("the alpha is out of range: {alpha}, clampped to 0.0..=1.0");
117            alpha = alpha.clamp(0.0, 1.0)
118        }
119        // trace!("{alpha}");
120        if alpha == 0.0 {
121            self.original.clone()
122        } else if 0.0 < alpha && alpha < 1.0 {
123            self.original.get_partial_closed(0.0..1.0 - alpha)
124        } else if alpha == 1.0 {
125            T::empty()
126        } else {
127            panic!("the alpha is out of range: {alpha}");
128        }
129    }
130}
131
132/// Write
133///
134/// First update with partial from 0.0..0.0 to 0.0..1.0, then lerp fill_opacity to 1.0
135pub struct Write<T: WritingRequirement> {
136    pub(crate) original: T,
137    pub(crate) outline: T,
138}
139
140impl<T: WritingRequirement> Write<T> {
141    /// Constructor
142    pub fn new(target: T) -> Self {
143        let mut outline = target.clone();
144        outline.set_fill_opacity(0.0).set_stroke_opacity(1.0);
145        if outline.stroke_width() == 0.0 {
146            outline.set_stroke_width(DEFAULT_STROKE_WIDTH);
147        }
148        Self {
149            original: target,
150            outline,
151        }
152    }
153}
154
155impl<T: WritingRequirement> Eval<T> for Write<T> {
156    fn eval_alpha(&self, alpha: f64) -> T {
157        let alpha = alpha * 2.0;
158        if (0.0..1.0).contains(&alpha) {
159            self.outline.get_partial(0.0..alpha)
160        } else if alpha == 1.0 {
161            self.outline.clone()
162        } else if (1.0..2.0).contains(&alpha) {
163            self.outline.lerp(&self.original, alpha - 1.0)
164        } else if alpha == 2.0 {
165            self.original.clone()
166        } else {
167            unreachable!()
168        }
169    }
170}
171
172/// Unwrite
173///
174/// First lerp fill_opacity to 0.0, then update with partial from 0.0..1.0 to 0.0..0.0
175pub struct Unwrite<T: WritingRequirement> {
176    pub(crate) original: T,
177    pub(crate) outline: T,
178}
179
180impl<T: WritingRequirement> Unwrite<T> {
181    /// Constructor
182    pub fn new(target: T) -> Self {
183        let mut outline = target.clone();
184        outline.set_fill_opacity(0.0).set_stroke_opacity(1.0);
185        if outline.stroke_width() == 0.0 {
186            outline.set_stroke_width(DEFAULT_STROKE_WIDTH);
187        }
188        Self {
189            original: target,
190            outline,
191        }
192    }
193}
194
195impl<T: WritingRequirement> Eval<T> for Unwrite<T> {
196    fn eval_alpha(&self, alpha: f64) -> T {
197        let alpha = alpha * 2.0;
198        if (0.0..1.0).contains(&alpha) {
199            self.original.lerp(&self.outline, alpha)
200        } else if alpha == 1.0 {
201            self.outline.clone()
202        } else if (1.0..2.0).contains(&alpha) {
203            self.outline.get_partial(0.0..2.0 - alpha)
204        } else if alpha == 2.0 {
205            T::empty()
206        } else if alpha == 0.0 {
207            self.original.clone()
208        } else {
209            panic!("the alpha is out of range: {alpha}");
210        }
211    }
212}