1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//! # A library for creating animations and transitions in Rust.
//!
//! The lib contains two main types: [`Animation`] and [`InertialValue`].
//! * [`Animation`] can be used in cases when we know start, end, and in between keyframes.
//! * [`InertialValue`] can be used to make an object smoothly follow a target value.
//!   For example, a particle following a cursor. Background color changing smoothly on theme change.
//!
//! It also contains a set of easing functions to make animations more natural. See the [`Easing`] enum for more details.
//!
//! Most of the methods receive `Instant` as a parameter to allow testing without mocks,
//! and have a consistent behavior during a single animation frame. It's expected that time is received
//! from `Instant::now()` once in the beginning of the frame, and used lately during the frame rendering.
//!
//! Animation can be applied to any type that implements [`Mix`] trait. This trait is used to interpolate between two values.
//! Mix trait is implemented for common types like `f32`, `f64`, `bool`, `i8` - `i64`, `u8` - `u64`, `Option<T: Mix>`,
//! and tuples like `(Mix, Mix)`, `(Mix, Mix, Mix)`, etc. It's also implemented for some popular libraries:
//! [`nalgebra`](https://crates.io/crates/nalgebra), [`euclid`](https://crates.io/crates/euclid),
//! [cgmath](https://crates.io/crates/cgmath), and [`palette`](https://crates.io/crates/palette).
//! To make it work, you need to enable the corresponding feature.
//!
//! # Derive macro
//!
//! The library contains a derive macro to implement the `Mix` trait for structs and tuples.
//! ```
//! # #[cfg(feature = "derive")]
//! use glissade::Mix;
//!
//! # #[cfg(feature = "derive")]
//! #[derive(Mix, PartialEq, Debug)]
//! struct Touch {
//!    x: f32,
//!    y: f32,
//!    pressure: u8,
//! }
//!
//! # #[cfg(feature = "derive")]
//! {
//!     let touch1 = Touch { x: 0.0, y: 0.0, pressure: 0 };
//!     let touch2 = Touch { x: 100.0, y: 100.0, pressure: 200 };
//!     let touch_mix = touch1.mix(touch2, 0.5);
//!     assert_eq!(touch_mix, Touch { x: 50.0, y: 50.0, pressure: 100 });
//! }
//! ```
//!
//! # Examples
//!
//! ## Simple two-step animation
//!
//! ```
//! use glissade::{Easing, transition, Transition};
//! use web_time::{Duration, Instant};
//!
//! // Create an animation template - a transition.
//! //
//! // This transition consists of two steps:
//! // 1. from 0.0 to 10.0 in 1 second linearly,
//! // 2. and then go to 5.0 with easing function.
//! let transition = transition(0.0)
//!     .go_to(10.0, Duration::from_secs(1))
//!     .ease_to(5.0, Duration::from_secs(2), Easing::QuadraticInOut);
//!
//! let now = Instant::now();
//! // Create an animation from the transition and start time.
//! let animation = transition.run(now);
//!
//! assert_eq!(animation.get(now), 0.0);
//! assert_eq!(animation.get(now + Duration::from_millis(500)), 5.0);
//! assert_eq!(animation.get(now + Duration::from_secs(1)), 10.0);
//! assert_eq!(animation.get(now + Duration::from_secs(2)), 7.5);
//! assert_eq!(animation.get(now + Duration::from_secs(3)), 5.0);
//!```
//!
//! ## Smoothly change color
//!
//!```
//! use glissade::{InertialValue, Easing};
//! use web_time::{Duration, Instant};
//!
//! type Color = (f32, f32, f32);
//!
//! let start_time = Instant::now();
//!
//! // Create initial black value
//! let value: InertialValue<Color> = InertialValue::new((0.0, 0.0, 0.0));
//!
//! assert_eq!(value.get(start_time), (0.0, 0.0, 0.0));
//! assert_eq!(value.get(start_time + Duration::from_secs(1)), (0.0, 0.0, 0.0));
//!
//! // Change color to white in one second
//! let value = value.go_to((1.0, 1.0, 1.0), start_time, Duration::from_secs(1));
//!
//! assert_eq!(value.get(start_time), (0.0, 0.0, 0.0));
//! assert_eq!(value.get(start_time + Duration::from_millis(500)), (0.5, 0.5, 0.5));
//! assert_eq!(value.get(start_time + Duration::from_secs(1)), (1.0, 1.0, 1.0));
//! assert_eq!(value.get(start_time + Duration::from_secs(2)), (1.0, 1.0, 1.0));
//!
//! // Change color to red in between the transition
//! let value = value.ease_to((1.0, 0.0, 0.0), start_time + Duration::from_millis(500), Duration::from_secs(2), Easing::Linear);
//!
//! assert_eq!(value.get(start_time + Duration::from_millis(500)), (0.5, 0.5, 0.5));
//! assert_eq!(value.get(start_time + Duration::from_secs(1)), (1.0, 0.75, 0.75));
//! assert_eq!(value.get(start_time + Duration::from_secs(2)), (1.0, 0.25, 0.25));
//! assert_eq!(value.get(start_time + Duration::from_millis(2500)), (1.0, 0.0, 0.0));
//! assert_eq!(value.get(start_time + Duration::from_secs(4)), (1.0, 0.0, 0.0));
//! ```

mod animation;
mod easing;
mod inertial_value;
mod mix;
mod transition;
mod transition_item;

#[cfg(feature = "cgmath")]
mod cgmath;
#[cfg(feature = "euclid")]
mod euclid;
#[cfg(feature = "nalgebra")]
mod nalgebra;
#[cfg(feature = "palette")]
mod palette;

pub use animation::Animation;
pub use easing::Easing;
#[cfg(feature = "derive")]
pub use glissade_macro::Mix;
pub use inertial_value::InertialValue;
pub use mix::Mix;
pub use transition::{transition, Transition};

#[cfg(test)]
#[cfg(feature = "derive")]
mod tests {

    use crate as glissade;
    use crate::Mix;

    #[derive(Mix, PartialEq, Debug)]
    struct Point {
        x: f32,
        y: f32,
    }

    #[test]
    fn test_struct_derive() {
        let p1 = Point { x: 0.0, y: 0.0 };
        let p2 = Point { x: 1.0, y: 1.0 };
        let p3 = p1.mix(p2, 0.5);
        assert_eq!(p3, Point { x: 0.5, y: 0.5 });
    }

    #[derive(Mix, PartialEq, Debug)]
    struct Color(f32, f32, f32);

    #[test]
    fn test_tuple_derive() {
        let c1 = Color(0.0, 0.0, 0.0);
        let c2 = Color(1.0, 1.0, 1.0);
        let c3 = c1.mix(c2, 0.5);
        assert_eq!(c3, Color(0.5, 0.5, 0.5));
    }

    #[derive(Mix, PartialEq, Debug)]
    struct Size<T: Mix>
    where
        T: Clone + Copy,
    {
        width: T,
        height: T,
    }

    #[test]
    fn test_generics_derive() {
        let s1 = Size {
            width: 0.0,
            height: 0.0,
        };
        let s2 = Size {
            width: 1.0,
            height: 1.0,
        };
        let s3 = s1.mix(s2, 0.5);
        assert_eq!(
            s3,
            Size {
                width: 0.5,
                height: 0.5
            }
        );
    }
}