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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
//! Mina is a framework-independent animation library focused on ergonomics, aiming to bring the
//! simplicity and versatility of CSS transitions and animations to Rust.
//!
//! # Features
//!
//! - Turn any ordinary `struct` into an animator or animated type.
//! - Create state-driven animators that automatically blend between states.
//! - Provides (almost) all standard [easings](https://easings.net/) out of the box, with the option
//! to add custom curves or functions.
//! - Define animations using a concise, CSS-like syntax, and state transitions using a style
//! similar to CSS pseudo-classes.
//! - Animate any property type that supports [linear interpolation](crate::Lerp).
//! - Easily specify delayed, repeating or reversing animations.
//! - Merge heterogeneous animations/transitions into a single timeline; e.g. define a _single_
//! animation that pulses in and out infinitely but also scales or slides in only once.
//! - Use with any GUI or creative coding environment -
//! [integration examples](https://github.com/focustense/mina/tree/main/examples) are provided for
//! [nannou](https://nannou.cc/), [bevy](https://bevyengine.org/) and
//! [iced](https://github.com/iced-rs/iced).
//!
//! # Timeline Example
//!
//! The [`Timeline`] is the most basic abstraction and defines a single, state-independent animation
//! over a time axis.
//!
//! Suppose we want to animate both the size and position of some entity over time. It will be small
//! at each edge and largest in the middle:
//!
//! ```
//! use mina::prelude::*;
//!
//! #[derive(Animate, Clone, Debug, Default, PartialEq)]
//! struct Style {
//! x: i32,
//! size: u32,
//! }
//!
//! let timeline = timeline!(Style 10s
//! from { x: -200, size: 10 }
//! 50% { x: 0, size: 20 }
//! to { x: 200, size: 10});
//!
//! let mut style = Style::default();
//! timeline.update(&mut style, 2.5);
//! assert_eq!(style, Style { x: -100, size: 15 });
//! ```
//!
//! Note: in the above code, [`Clone`] and [`Default`] are required traits for any timeline-able
//! type, but [`Debug`] and [`PartialEq`] are only needed for the assertions and are not required
//! for regular usage.
//!
//! `from` and `to` are aliases for `0%` and `100%` respectively. Either may be used, but the former
//! are more idiomatic and tend to improve readability.
//!
//! # Animator Example
//!
//! [`StateAnimator`] types own many timelines, as well as the style or other structure being
//! animated, and are meant to be driven directly by an event loop. Instead of requesting the
//! properties at a particular time, as in the [`Timeline`] example above, you interact with
//! animators by notifying them of elapsed time and state changes.
//!
//! Suppose we are designing an animated button; when hovered, it receives an elevation, and when
//! pressed, it slightly increases in size.
//!
//! ```
//! use mina::prelude::*;
//!
//! #[derive(Animate, Clone, Debug, Default, PartialEq)]
//! struct Style {
//! elevation: f32,
//! scale: f32,
//! }
//!
//! #[derive(Clone, Default, PartialEq, State)]
//! enum State {
//! #[default] Idle,
//! Hovered,
//! Pressed,
//! }
//!
//! let mut animator = animator!(Style {
//! default(State::Idle, { elevation: 0.0, scale: 1.0 }),
//! State::Idle => 0.25s to default,
//! State::Hovered => 0.5s to { elevation: 5.0, scale: 1.0 },
//! State::Pressed => 0.1s to { scale: 1.1 }
//! });
//!
//! assert_eq!(animator.current_values(), &Style { elevation: 0.0, scale: 1.0 }); // Default
//! animator.advance(1.0); // No change in state
//! assert_eq!(animator.current_values(), &Style { elevation: 0.0, scale: 1.0 });
//! animator.set_state(&State::Hovered);
//! assert_eq!(animator.current_values(), &Style { elevation: 0.0, scale: 1.0 }); // No time elapsed
//! animator.advance(0.25);
//! assert_eq!(animator.current_values(), &Style { elevation: 2.5, scale: 1.0 });
//! animator.set_state(&State::Pressed); // Change state before animation is finished
//! assert_eq!(animator.current_values(), &Style { elevation: 2.5, scale: 1.0 }); // No time elapsed
//! animator.advance(0.05);
//! assert_eq!(animator.current_values(), &Style { elevation: 2.5, scale: 1.05 });
//! ```
//!
//! The [`Clone`], [`Default`] and [`PartialEq`] traits **are all** required for any type to be used
//! as an animator state. [`State`] is an alias for
//! [Enum](https://docs.rs/enum-map/latest/enum_map/trait.Enum.html), and currently required for the
//! [`EnumStateAnimator`] and [`animator`] macro.
//!
//! The `default(state, props)` line is not required, but supported for specific and relatively
//! common cases where the default resting values _for that animation_ do not match the [`Default`]
//! implementation for the corresponding `struct`, or in the (less common) case that the default
//! _state_ for the animation should be different from the default enum member. In the above
//! example, the derived `Default` implementation would give a `scale` of `0.0`, but the normal
//! scale of the button should be `1.0`, so we override the default.
//!
//! The same `default` term _within_ an animation state has a different meaning, and is interpreted
//! as "use the default (for this animator) values for this keyframe". This helps avoid repetitive,
//! copy-paste code for default/idle states that should simply return the widget to its base state.
//!
//! Note how the `Hovered` state specifies a `scale` that is the same as the default. The reason for
//! this is to tell the animator that when transitioning from `Pressed` back to `Hovered`, it should
//! revert the scale transform used for `Pressed`. Mina does **not** assume that undefined values in
//! a keyframe should use defaults, and this is a very important property for merged timelines and
//! more advanced animations in general. If a keyframe is missing one or more properties, those
//! properties are _ignored_:
//! - If there are earlier or later keyframes that do specify them, then it will animate smoothly
//! between those keyframes. This applies to both [`Timeline`] and [`StateAnimator`].
//! - If any given state does not specify any keyframes at all with some properties, then the
//! properties will _not animate_ when in that state; they will remain at whichever values the
//! previous state left them in.
//!
//! The actual implementation of `elevation` and `scale` are up to the underlying GUI. Mina doesn't
//! care about the meaning of these properties, it just animates their values; the plumbing will
//! vary with the specific GUI in use. Future updates may include standard integrations with those
//! GUIs, but for now, the [examples](https://github.com/focustense/mina/tree/main/examples)
//! directory serves as the unofficial integration how-to guide, as well as the repository for more
//! complex and interesting uses of the API.
//!
//! # Event Loop
//!
//! Mina doesn't use its own event loop, so that it can instead be integrated into the event loop of
//! whichever GUI is actually in use. This also allows global customizations - for example, stopping
//! all animations when a game is paused, or playing them in slow motion during some key event.
//!
//! In most cases, establishing the event loop is a one- or two-line function. Refer to the
//! [examples](https://github.com/focustense/mina/tree/main/examples) for framework-specific
//! patterns.
pub use ;
pub use ;
/// Configures and creates a [`StateAnimator`] for an [`Animate`](macro@Animate) type.
///
/// Animators hold one or more [`Timeline`] instances mapped to particular state values and will
/// automatically switch and blend timelines when the state is changed. The `animator` macro uses a
/// CSS-like syntax to define the states and timelines.
///
/// Use of this macro requires both an [`Animate`] type for the values/timeline and a
/// [`State`] derived type for the state (note: `State` is a re-export of the `Enum` derive macro
/// from the [`enum-map`](https://crates.io/crates/enum-map) crate; you will need to add this crate
/// to the downstream project in order for `State` to compile. In addition, the state type must
/// implement [`Clone`](std::clone::Clone), [`Default`](std::default::Default) and
/// [`PartialEq`](std::cmp::PartialEq).
///
/// # Example
///
/// ```
/// use mina::prelude::*;
///
/// #[derive(Clone, Default, PartialEq, State)]
/// enum State {
/// #[default] Idle,
/// Active,
/// }
///
/// #[derive(Animate, Clone, Debug, Default, PartialEq)]
/// struct Style {
/// alpha: f32,
/// size: u16,
/// }
///
/// let mut animator = animator!(Style {
/// default(State::Idle, { alpha: 0.5, size: 60 }),
/// State::Idle => 2s Easing::OutQuad to default,
/// State::Active => 1s Easing::Linear to { alpha: 1.0, size: 80 }
/// });
///
/// animator.advance(12.0);
/// assert_eq!(animator.current_values(), &Style { alpha: 0.5, size: 60 });
/// animator.set_state(&State::Active);
/// assert_eq!(animator.current_values(), &Style { alpha: 0.5, size: 60 });
/// animator.advance(0.5);
/// assert_eq!(animator.current_values(), &Style { alpha: 0.75, size: 70 });
/// animator.set_state(&State::Idle);
/// assert_eq!(animator.current_values(), &Style { alpha: 0.75, size: 70 });
/// animator.advance(0.8);
/// assert_eq!(animator.current_values(), &Style { alpha: 0.554, size: 62 });
/// animator.advance(1.2);
/// assert_eq!(animator.current_values(), &Style { alpha: 0.5, size: 60 });
/// ```
pub use animator;
/// Sets up a type for animation.
///
/// Animatable types gain two functions:
/// - `timeline()` creates a [`TimelineBuilder`] that can be used to build a single animation
/// [`Timeline`]. Timelines provide the interpolates values of the animatable type at any
/// arbitrary point in time.
/// - `keyframe()` creates a [`KeyframeBuilder`] which is used to provide [`Keyframe`] instances to
/// the timeline builder. Keyframes specify the exact values at a specific point in the timeline.
///
/// In addition, a specific timeline type is generated with a `Timeline` suffix; for example, if the
/// name of the type is `Style`, then the generated timeline type will be `StyleTimeline`. This type
/// will have the same visibility as the animatable type, and can be used directly to store the
/// timeline, or an animator based on the timeline, without boxing.
///
/// Making a type animatable also allows it to be used with the [`animator`](macro@animator) macro.
///
/// The following requirements apply to any type decorated with `#[derive(Animate}]`:
///
/// 1. Must be a `struct`. Tuple and `enum` types are not supported.
/// 2. Must implement the [`Clone`](std::clone::Clone) and [`Default`](std::default::Default)
/// traits.
/// 3. All _animated_ fields must implement [`Lerp`].
/// - A blanket implementation is provided for all primitive numeric types.
/// - Other types may need explicit implementations and/or a newtype for unowned types.
/// - **To exclude fields** from animation, either because it is not `Lerp`able or simply because
/// it is intended to be constant, add the `#[animate]` helper attribute to all fields which
/// _should_ be animated; any remaining fields not decorated will be ignored.
/// 4. Nested structures, `Option` fields, etc. are allowed, but will be treated as black-box, which
/// means the actual type of the field (e.g. the entire `struct`) must meet the `Lerp`
/// requirement above. This can be the desired behavior for a limited number of complex types
/// such as vectors or colors, but usually flat structs are more appropriate.
/// 5. Generic types are not supported (for now) at the `struct` level, although the individual
/// fields can be generic.
///
/// # Example
///
/// ```
/// use mina::prelude::*;
///
/// #[derive(Animate, Clone, Debug, Default, PartialEq)]
/// struct Style {
/// alpha: f32,
/// size: u16,
/// }
///
/// let timeline = Style::timeline()
/// .duration_seconds(5.0)
/// .delay_seconds(1.0)
/// .keyframe(Style::keyframe(1.0).alpha(1.0).size(25))
/// .build();
///
/// let mut values = Style::default();
/// timeline.update(&mut values, 3.0);
/// assert_eq!(values, Style { alpha: 0.4, size: 10 });
/// ```
pub use Animate;
/// Configures and creates a [`Timeline`] for an [`Animate`](macro@Animate) type.
///
/// Provides a more ergonomic, CSS-like alternative to the builder syntax using
/// [`TimelineConfiguration`] and [`TimelineBuilder`], producing the same result. Requires an
/// [`Animate`] type for the values.
///
/// # Example
///
/// ```
/// use mina::prelude::*;
///
/// #[derive(Animate, Clone, Debug, Default, PartialEq)]
/// struct Style {
/// alpha: f32,
/// size: u16,
/// }
///
/// let timeline = timeline!(Style 2s reverse Easing::Out
/// from { alpha: 0.5, size: 50 }
/// to { alpha: 1.0, size: 100 });
///
/// let mut values = Style::default();
/// timeline.update(&mut values, 0.25);
/// assert_eq!(values, Style { alpha: 0.578125, size: 58 });
/// timeline.update(&mut values, 0.5);
/// assert_eq!(values, Style { alpha: 0.75, size: 75 });
/// timeline.update(&mut values, 1.0);
/// assert_eq!(values, Style { alpha: 1.0, size: 100 });
/// timeline.update(&mut values, 1.25);
/// assert_eq!(values, Style { alpha: 0.921875, size: 92 });
/// timeline.update(&mut values, 1.5);
/// assert_eq!(values, Style { alpha: 0.75, size: 75 });
/// timeline.update(&mut values, 2.0);
/// assert_eq!(values, Style { alpha: 0.5, size: 50 });
/// ```
pub use timeline;