1use std::{any::Any, borrow::Cow, path::PathBuf, sync::Arc, time::Duration};
4
5use zng_app_context::{app_local, context_local};
6use zng_time::{DInstant, Deadline};
7use zng_txt::Txt;
8use zng_unit::{
9 AngleDegree, AngleGradian, AngleRadian, AngleTurn, ByteLength, CornerRadius2D, Dip, Factor, FactorPercent, FactorUnits, Orientation2D,
10 Px, Rgba, euclid,
11};
12
13use crate::{
14 animation::{Transition, Transitionable, easing::EasingStep},
15 impl_from_and_into_var,
16};
17
18impl Transitionable for f64 {
19 fn lerp(self, to: &Self, step: EasingStep) -> Self {
20 self + (*to - self) * step.0 as f64
21 }
22}
23impl Transitionable for f32 {
24 fn lerp(self, to: &Self, step: EasingStep) -> Self {
25 self + (*to - self) * step.0
26 }
27}
28macro_rules! impl_transitionable {
29 ($FT:ident => $($T:ty,)+) => {$(
30 impl Transitionable for $T {
31 fn lerp(self, to: &Self, step: EasingStep) -> Self {
32 $FT::lerp(self as $FT, &((*to) as $FT), step).round() as _
33 }
34 }
35 )+}
36}
37impl_transitionable! {
38 f32 => i8, u8, i16, u16, i32, u32,
39}
40impl_transitionable! {
41 f64 => u64, i64, u128, i128, isize, usize,
42}
43impl Transitionable for Px {
44 fn lerp(self, to: &Self, step: EasingStep) -> Self {
45 Px(self.0.lerp(&to.0, step))
46 }
47}
48impl Transitionable for Dip {
49 fn lerp(self, to: &Self, step: EasingStep) -> Self {
50 Dip::new_f32(self.to_f32().lerp(&to.to_f32(), step))
51 }
52}
53impl<T, U> Transitionable for euclid::Point2D<T, U>
54where
55 T: Transitionable,
56 U: Send + Sync + Any,
57{
58 fn lerp(self, to: &Self, step: EasingStep) -> Self {
59 euclid::point2(self.x.lerp(&to.x, step), self.y.lerp(&to.y, step))
60 }
61}
62impl<T, U> Transitionable for euclid::Box2D<T, U>
63where
64 T: Transitionable,
65 U: Send + Sync + Any,
66{
67 fn lerp(self, to: &Self, step: EasingStep) -> Self {
68 euclid::Box2D::new(self.min.lerp(&to.min, step), self.max.lerp(&to.max, step))
69 }
70}
71impl<T, U> Transitionable for euclid::Point3D<T, U>
72where
73 T: Transitionable,
74 U: Send + Sync + Any,
75{
76 fn lerp(self, to: &Self, step: EasingStep) -> Self {
77 euclid::point3(self.x.lerp(&to.x, step), self.y.lerp(&to.y, step), self.z.lerp(&to.z, step))
78 }
79}
80impl<T, U> Transitionable for euclid::Box3D<T, U>
81where
82 T: Transitionable,
83 U: Send + Sync + Any,
84{
85 fn lerp(self, to: &Self, step: EasingStep) -> Self {
86 euclid::Box3D::new(self.min.lerp(&to.min, step), self.max.lerp(&to.max, step))
87 }
88}
89impl<T, U> Transitionable for euclid::Length<T, U>
90where
91 T: Transitionable,
92 U: Send + Sync + Any,
93{
94 fn lerp(self, to: &Self, step: EasingStep) -> Self {
95 euclid::Length::new(self.get().lerp(&to.clone().get(), step))
96 }
97}
98impl<T, U> Transitionable for euclid::Size2D<T, U>
99where
100 T: Transitionable,
101 U: Send + Sync + Any,
102{
103 fn lerp(self, to: &Self, step: EasingStep) -> Self {
104 euclid::size2(self.width.lerp(&to.width, step), self.height.lerp(&to.height, step))
105 }
106}
107impl<T, U> Transitionable for euclid::Size3D<T, U>
108where
109 T: Transitionable,
110 U: Send + Sync + Any,
111{
112 fn lerp(self, to: &Self, step: EasingStep) -> Self {
113 euclid::size3(
114 self.width.lerp(&to.width, step),
115 self.height.lerp(&to.height, step),
116 self.depth.lerp(&to.depth, step),
117 )
118 }
119}
120impl<T, U> Transitionable for euclid::Rect<T, U>
121where
122 T: Transitionable,
123 U: Send + Sync + Any,
124{
125 fn lerp(self, to: &Self, step: EasingStep) -> Self {
126 euclid::Rect::new(self.origin.lerp(&to.origin, step), self.size.lerp(&to.size, step))
127 }
128}
129impl<T, U> Transitionable for euclid::Vector2D<T, U>
130where
131 T: Transitionable,
132 U: Send + Sync + Any,
133{
134 fn lerp(self, to: &Self, step: EasingStep) -> Self {
135 euclid::vec2(self.x.lerp(&to.x, step), self.y.lerp(&to.y, step))
136 }
137}
138impl<T, U> Transitionable for euclid::Vector3D<T, U>
139where
140 T: Transitionable,
141 U: Send + Sync + Any,
142{
143 fn lerp(self, to: &Self, step: EasingStep) -> Self {
144 euclid::vec3(self.x.lerp(&to.x, step), self.y.lerp(&to.y, step), self.z.lerp(&to.z, step))
145 }
146}
147impl Transitionable for Factor {
148 fn lerp(self, to: &Self, step: EasingStep) -> Self {
149 Factor(self.0.lerp(&to.0, step))
150 }
151}
152impl<T, U> Transitionable for euclid::SideOffsets2D<T, U>
153where
154 T: Transitionable,
155 U: Send + Sync + Any,
156{
157 fn lerp(self, to: &Self, step: EasingStep) -> Self {
158 euclid::SideOffsets2D::new(
159 self.top.lerp(&to.top, step),
160 self.right.lerp(&to.right, step),
161 self.bottom.lerp(&to.bottom, step),
162 self.left.lerp(&to.left, step),
163 )
164 }
165}
166impl Transitionable for bool {
167 fn lerp(self, to: &Self, step: EasingStep) -> Self {
168 if step >= 1.fct() { *to } else { self }
169 }
170}
171impl<T, U> Transitionable for CornerRadius2D<T, U>
172where
173 T: Transitionable,
174 U: Send + Sync + Any,
175{
176 fn lerp(self, to: &Self, step: EasingStep) -> Self {
177 Self {
178 top_left: self.top_left.lerp(&to.top_left, step),
179 top_right: self.top_right.lerp(&to.top_right, step),
180 bottom_right: self.bottom_right.lerp(&to.bottom_right, step),
181 bottom_left: self.bottom_left.lerp(&to.bottom_left, step),
182 }
183 }
184}
185
186impl Transitionable for ByteLength {
187 fn lerp(self, to: &Self, step: EasingStep) -> Self {
188 Self(self.0.lerp(&to.0, step))
189 }
190}
191
192impl_from_and_into_var! {
193 fn from(s: &'static str) -> Txt;
194 fn from(s: String) -> Txt;
195 fn from(s: Cow<'static, str>) -> Txt;
196 fn from(c: char) -> Txt;
197 fn from(t: Txt) -> PathBuf;
198 fn from(t: Txt) -> String;
199 fn from(t: Txt) -> Cow<'static, str>;
200
201 fn from(f: f32) -> Factor;
202 fn from(one_or_zero: bool) -> Factor;
203 fn from(f: FactorPercent) -> Factor;
204 fn from(f: Factor) -> FactorPercent;
205
206 fn from(d: DInstant) -> Deadline;
207 fn from(d: Duration) -> Deadline;
208
209 fn from(b: usize) -> ByteLength;
210
211 fn from(rad: AngleRadian) -> AngleTurn;
212 fn from(grad: AngleGradian) -> AngleTurn;
213 fn from(deg: AngleDegree) -> AngleTurn;
214
215 fn from(grad: AngleGradian) -> AngleRadian;
216 fn from(deg: AngleDegree) -> AngleRadian;
217 fn from(turn: AngleTurn) -> AngleRadian;
218
219 fn from(rad: AngleRadian) -> AngleGradian;
220 fn from(deg: AngleDegree) -> AngleGradian;
221 fn from(turn: AngleTurn) -> AngleGradian;
222
223 fn from(rad: AngleRadian) -> AngleDegree;
224 fn from(grad: AngleGradian) -> AngleDegree;
225 fn from(turn: AngleTurn) -> AngleDegree;
226}
227
228macro_rules! impl_into_var_option {
229 (
230 $($T:ty),* $(,)?
231 ) => {
232 impl_from_and_into_var! { $(
233 fn from(some: $T) -> Option<$T>;
234 )* }
235 }
236}
237impl_into_var_option! {
238 i8, i16, i32, i64, i128, isize,
239 u8, u16, u32, u64, u128, usize,
240 f32, f64,
241 char, bool,
242 Orientation2D,
243}
244
245pub fn slerp_sampler<T: Transitionable>(t: &Transition<T>, step: EasingStep) -> T {
256 slerp_enabled(true, || t.sample(step))
257}
258
259pub fn is_slerp_enabled() -> bool {
263 SLERP_ENABLED.get_clone()
264}
265
266pub fn slerp_enabled<R>(enabled: bool, f: impl FnOnce() -> R) -> R {
270 SLERP_ENABLED.with_context(&mut Some(Arc::new(enabled)), f)
271}
272
273context_local! {
274 static SLERP_ENABLED: bool = false;
275}
276
277impl Transitionable for AngleRadian {
278 fn lerp(self, to: &Self, step: EasingStep) -> Self {
279 match is_slerp_enabled() {
280 false => self.lerp(*to, step),
281 true => self.slerp(*to, step),
282 }
283 }
284}
285impl Transitionable for AngleGradian {
286 fn lerp(self, to: &Self, step: EasingStep) -> Self {
287 match is_slerp_enabled() {
288 false => self.lerp(*to, step),
289 true => self.slerp(*to, step),
290 }
291 }
292}
293impl Transitionable for AngleDegree {
294 fn lerp(self, to: &Self, step: EasingStep) -> Self {
295 match is_slerp_enabled() {
296 false => self.lerp(*to, step),
297 true => self.slerp(*to, step),
298 }
299 }
300}
301impl Transitionable for AngleTurn {
302 fn lerp(self, to: &Self, step: EasingStep) -> Self {
303 match is_slerp_enabled() {
304 false => self.lerp(*to, step),
305 true => self.slerp(*to, step),
306 }
307 }
308}
309impl Transitionable for Rgba {
310 fn lerp(self, to: &Self, step: EasingStep) -> Self {
311 let lerp = *RGBA_LERP.read();
312 lerp(self, *to, step)
313 }
314}
315
316app_local! {
317 static RGBA_LERP: fn(Rgba, Rgba, EasingStep) -> Rgba = const { lerp_rgba_linear };
319}
320fn lerp_rgba_linear(mut from: Rgba, to: Rgba, factor: Factor) -> Rgba {
321 from.red = from.red.lerp(&to.red, factor);
322 from.green = from.green.lerp(&to.green, factor);
323 from.blue = from.blue.lerp(&to.blue, factor);
324 from.alpha = from.alpha.lerp(&to.alpha, factor);
325 from
326}
327
328#[expect(non_camel_case_types)]
330pub struct TRANSITIONABLE_APP;
331impl TRANSITIONABLE_APP {
332 pub fn init_rgba_lerp(&self, lerp: fn(Rgba, Rgba, EasingStep) -> Rgba) {
336 *RGBA_LERP.write() = lerp;
337 }
338}