Skip to main content

ranim_anims/
creation.rs

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