1#![cfg_attr(feature = "std", doc = include_str!("../README.md"))]
2#![cfg_attr(not(feature = "std"), doc = "no-std stand in")]
3#![deny(unsafe_code)]
4#![deny(rust_2018_idioms)]
5#![deny(missing_docs)]
6#![deny(rustdoc::broken_intra_doc_links)]
7#![no_std]
8
9#[cfg(feature = "std")]
10#[macro_use]
11extern crate std;
12
13#[cfg(all(feature = "std", feature = "libm"))]
14compile_error!("Please disable feature `libm` or disable default features -- both cannot be active at once.");
15
16#[macro_use]
17mod macros;
18
19mod math;
20mod tweener;
21mod tweens;
22
23pub use tweener::*;
24pub use tweens::*;
25
26pub trait Tween<Value> {
28 fn tween(&mut self, value_delta: Value, percent: f32) -> Value;
36
37 #[inline(always)]
50 fn is_finite(&self) -> bool {
51 true
52 }
53}
54
55#[cfg(test)]
56static_assertions::assert_obj_safe!(Tween<i32>);
57
58#[cfg(feature = "std")]
59impl<Value> Tween<Value> for &'_ mut dyn Tween<Value>
60where
61 Value: TweenValue,
62{
63 #[inline(always)]
64 fn tween(&mut self, value_delta: Value, percent: f32) -> Value {
65 (**self).tween(value_delta, percent)
66 }
67
68 fn is_finite(&self) -> bool {
69 true
70 }
71}
72
73impl_tween_for_box!();
74impl_tween_for_box!(Send);
75impl_tween_for_box!(Sync);
76impl_tween_for_box!(Send, Sync);
77impl_tween_for_box!(Unpin);
78impl_tween_for_box!(Send, Unpin);
79impl_tween_for_box!(Send, Sync, Unpin);
80
81impl<Value, F> Tween<Value> for F
82where
83 F: FnMut(Value, f32) -> Value,
84 Value: TweenValue,
85{
86 #[inline(always)]
87 fn tween(&mut self, value_delta: Value, percent: f32) -> Value {
88 self(value_delta, percent)
89 }
90}
91
92pub trait TweenValue: Copy + core::fmt::Debug + core::ops::Add<Output = Self> + core::ops::Sub<Output = Self> {
101 fn scale(self, scale: f32) -> Self;
104}
105
106pub trait TweenTime:
112 Copy
113 + PartialEq
114 + PartialOrd
115 + core::fmt::Debug
116 + core::ops::Add<Output = Self>
117 + core::ops::AddAssign
118 + core::ops::Rem<Output = Self>
119 + core::ops::Sub<Output = Self>
120{
121 const ZERO: Self;
123
124 fn to_f32(self) -> f32;
126}
127
128declare_time!(u8, i8, i16, u16, i32, i64, u32, u64, i128, u128, usize, isize);
129
130impl TweenTime for f32 {
131 const ZERO: Self = 0.0;
132
133 #[inline(always)]
134 fn to_f32(self) -> f32 {
135 self
136 }
137}
138impl TweenTime for f64 {
139 const ZERO: Self = 0.0;
140
141 #[inline(always)]
142 fn to_f32(self) -> f32 {
143 self as f32
144 }
145}
146
147declare_value!(u8, i8, i16, u16, i32, i64, u32, u64, i128, u128, usize, isize);
148
149impl TweenValue for f32 {
150 #[inline(always)]
151 fn scale(self, scale: f32) -> Self {
152 self * scale
153 }
154}
155
156impl TweenValue for f64 {
157 #[inline(always)]
158 fn scale(self, scale: f32) -> Self {
159 (self as f32 * scale) as Self
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 #[test]
168 fn lambda_test() {
169 let (start, end) = (0, 1); let length = 4;
172 let mut pct_50_or_over = false;
173 let mut tweener = Tweener::new(start, end, length, |_vd, pct| {
174 if pct >= 0.5 {
175 pct_50_or_over = true;
176 }
177 100
178 });
179 assert_eq!(tweener.move_by(1), 100);
180 assert_eq!(tweener.move_by(1), 100);
181 assert_eq!(tweener.move_by(1), 100);
182 assert_eq!(tweener.move_by(1), 100);
183 assert_eq!(tweener.move_by(1), 1);
185 assert!(tweener.is_finished());
186 assert!(pct_50_or_over);
187 }
188
189 #[test]
190 fn cubic_bezier() {
191 use core::ops::{Add, Sub};
192 #[derive(Debug, Clone, Copy, PartialEq)]
193 struct Point(f32, f32);
194 impl Add for Point {
195 type Output = Self;
196
197 fn add(self, rhs: Self) -> Self::Output {
198 Self(self.0 + rhs.0, self.1 + rhs.1)
199 }
200 }
201 impl Sub for Point {
202 type Output = Self;
203
204 fn sub(self, rhs: Self) -> Self::Output {
205 Self(self.0 - rhs.0, self.1 - rhs.1)
206 }
207 }
208 impl TweenValue for Point {
209 fn scale(self, scale: f32) -> Self {
210 Self(self.0 * scale, self.1 * scale)
211 }
212 }
213
214 let start = Point(0.0, 0.0);
215 let destination = Point(10.0, 0.0);
216
217 let ctrl_one = Point(2.5, 10.0);
218 let ctrl_two = Point(7.5, 10.0);
219
220 let mut tweener = Tweener::new(start, destination, 10.0, |delta, t| {
222 fn lerp(a: Point, b: Point, t: f32) -> Point {
223 a.scale(1.0 - t) + b.scale(t)
224 }
225
226 let a = lerp(Point(0.0, 0.0), ctrl_one, t);
227 let b = lerp(ctrl_two, ctrl_one, t);
228 let c = lerp(ctrl_two, delta, t);
229
230 let d = lerp(a, b, t);
231 let e = lerp(b, c, t);
232
233 lerp(d, e, t)
234 });
235
236 assert_eq!(tweener.move_to(5.0), Point(5.0, 7.5));
237 assert_eq!(tweener.move_to(10.0), Point(10.0, 0.0));
238 }
239}