1use crate::CubicBezier;
5use crate::TIME_EPSILON;
6use crate::ease::Ease;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum Dir {
11 Left,
13 Right,
15 Up,
17 Down,
19}
20
21impl Dir {
22 pub(crate) fn name(self) -> &'static str {
23 match self {
24 Self::Left => "left",
25 Self::Right => "right",
26 Self::Up => "up",
27 Self::Down => "down",
28 }
29 }
30}
31
32#[derive(Debug, Clone, Copy, PartialEq)]
37pub struct LayerPose {
38 pub opacity: f64,
40 pub dx: f64,
42 pub dy: f64,
44 pub scale: f64,
46 pub wipe: Option<(Dir, f64)>,
48}
49
50impl Default for LayerPose {
51 fn default() -> Self {
52 Self {
53 opacity: 1.0,
54 dx: 0.0,
55 dy: 0.0,
56 scale: 1.0,
57 wipe: None,
58 }
59 }
60}
61
62impl LayerPose {
63 pub fn new() -> Self {
65 Self::default()
66 }
67
68 pub fn opacity(mut self, opacity: f64) -> Self {
70 self.opacity = opacity;
71 self
72 }
73
74 pub fn offset(mut self, dx: f64, dy: f64) -> Self {
76 self.dx = dx;
77 self.dy = dy;
78 self
79 }
80
81 pub fn scale(mut self, scale: f64) -> Self {
83 self.scale = scale;
84 self
85 }
86
87 pub fn wipe(mut self, dir: Dir, amount: f64) -> Self {
89 self.wipe = Some((dir, amount));
90 self
91 }
92}
93
94#[derive(Debug, Clone, Copy, PartialEq)]
99pub struct Blend {
100 pub from: LayerPose,
102 pub to: LayerPose,
104 pub incoming_on_top: bool,
106 pub mix: bool,
108}
109
110impl Default for Blend {
111 fn default() -> Self {
112 Self {
113 from: LayerPose::new(),
114 to: LayerPose::new(),
115 incoming_on_top: true,
116 mix: false,
117 }
118 }
119}
120
121#[derive(Clone, Copy)]
124pub enum Kind {
125 Fade,
127 Slide(Dir),
129 Wipe(Dir),
131 ZoomIn,
133 ZoomOut,
135 Custom(fn(f64) -> Blend),
137}
138
139impl Kind {
140 pub(crate) fn name(self) -> &'static str {
141 match self {
142 Self::Fade => "fade",
143 Self::Slide(_) => "slide",
144 Self::Wipe(_) => "wipe",
145 Self::ZoomIn => "zoom_in",
146 Self::ZoomOut => "zoom_out",
147 Self::Custom(_) => "custom",
148 }
149 }
150
151 pub(crate) fn dir(self) -> Option<Dir> {
152 match self {
153 Self::Slide(d) | Self::Wipe(d) => Some(d),
154 _ => None,
155 }
156 }
157}
158
159#[derive(Clone, Copy)]
162pub struct Recorded {
163 pub(crate) kind: Kind,
164 pub(crate) seconds: f64,
165 pub(crate) ease: CubicBezier,
166 pub(crate) overlap: bool,
167 pub(crate) start: f64,
169}
170
171impl Recorded {
172 pub fn new(kind: Kind, seconds: f64, ease: CubicBezier, overlap: bool, start: f64) -> Self {
174 Self {
175 kind,
176 seconds,
177 ease,
178 overlap,
179 start,
180 }
181 }
182
183 pub fn kind_name(self) -> &'static str {
185 self.kind.name()
186 }
187
188 pub fn dir_name(self) -> Option<&'static str> {
190 self.kind.dir().map(Dir::name)
191 }
192
193 pub fn overlap(self) -> bool {
195 self.overlap
196 }
197
198 pub fn seconds(self) -> f64 {
200 self.seconds
201 }
202
203 pub fn start(self) -> f64 {
205 self.start
206 }
207
208 pub fn active(self, now: f64) -> bool {
210 self.seconds > 0.0
211 && now + TIME_EPSILON >= self.start
212 && now + TIME_EPSILON < self.start + self.seconds
213 }
214
215 pub fn progress(self, now: f64) -> f64 {
217 if self.seconds <= 0.0 {
218 return 1.0;
219 }
220 let t = ((now - self.start) / self.seconds).clamp(0.0, 1.0);
221 self.ease.ease(t).clamp(0.0, 1.0)
222 }
223
224 pub fn blend_at(self, now: f64) -> Blend {
226 blend(self.kind, self.progress(now))
227 }
228}
229
230pub(crate) fn blend(kind: Kind, t: f64) -> Blend {
232 let t = t.clamp(0.0, 1.0);
233 match kind {
234 Kind::Fade => Blend {
235 mix: true,
236 ..Blend::default()
237 },
238 Kind::Slide(dir) => {
239 let (fx, fy, tx, ty) = shift(dir, t);
240 Blend {
241 from: LayerPose::new().offset(fx, fy),
242 to: LayerPose::new().offset(tx, ty),
243 ..Blend::default()
244 }
245 }
246 Kind::Wipe(dir) => Blend {
247 to: LayerPose::new().wipe(dir, t),
248 ..Blend::default()
249 },
250 Kind::ZoomIn => Blend {
251 to: LayerPose::new().opacity(t).scale(0.6 + 0.4 * t),
252 ..Blend::default()
253 },
254 Kind::ZoomOut => Blend {
255 from: LayerPose::new().opacity(1.0 - t).scale(1.0 + 0.4 * t),
256 incoming_on_top: false,
257 ..Blend::default()
258 },
259 Kind::Custom(f) => f(t),
260 }
261}
262
263fn shift(dir: Dir, t: f64) -> (f64, f64, f64, f64) {
264 match dir {
265 Dir::Left => (-t, 0.0, 1.0 - t, 0.0),
266 Dir::Right => (t, 0.0, t - 1.0, 0.0),
267 Dir::Up => (0.0, -t, 0.0, 1.0 - t),
268 Dir::Down => (0.0, t, 0.0, t - 1.0),
269 }
270}