aura_anim_iced/timeline/
track.rs1use crate::{
2 keyframes::{Keyframes, KeyframesBuilder},
3 prelude::PropertyValueKind,
4 property::{PropertyEntry, PropertySnapshot, PropertySpec},
5 timing::{Duration, Easing, Timing},
6};
7
8#[derive(Debug, Clone, PartialEq)]
10pub struct Track {
11 name: Option<String>,
12 keyframes: Keyframes,
13}
14
15#[derive(Debug, Clone, PartialEq)]
17pub struct PropertyTrackBuilder<K: PropertyValueKind> {
18 property: PropertySpec<K>,
19 builder: KeyframesBuilder,
20}
21
22impl Track {
23 #[must_use]
25 pub const fn new(keyframes: Keyframes) -> Self {
26 Self {
27 name: None,
28 keyframes,
29 }
30 }
31
32 #[must_use]
34 pub fn from<K: PropertyValueKind>(
35 property: PropertySpec<K>,
36 value: K::Inner,
37 ) -> PropertyTrackBuilder<K> {
38 PropertyTrackBuilder {
39 property,
40 builder: KeyframesBuilder::new().at(0.0, (property, value)),
41 }
42 }
43
44 #[must_use]
46 pub fn with_name(mut self, name: impl Into<String>) -> Self {
47 self.name = Some(name.into());
48 self
49 }
50
51 #[must_use]
53 pub fn name(&self) -> Option<&str> {
54 self.name.as_deref()
55 }
56
57 #[must_use]
59 pub const fn keyframes(&self) -> &Keyframes {
60 &self.keyframes
61 }
62
63 #[must_use]
65 pub fn total_duration(&self) -> Option<Duration> {
66 self.keyframes.timing().total_duration()
67 }
68
69 #[must_use]
71 pub fn sample_at(&self, offset: impl Into<Duration>) -> Option<PropertySnapshot> {
72 let mut snapshot = PropertySnapshot::with_capacity(self.keyframes.track_count());
73
74 if self.sample_into(offset, &mut snapshot) {
75 Some(snapshot)
76 } else {
77 None
78 }
79 }
80
81 pub(crate) fn sample_into(
82 &self,
83 offset: impl Into<Duration>,
84 output: &mut PropertySnapshot,
85 ) -> bool {
86 let timing = self
87 .keyframes
88 .timing()
89 .normalize_elapsed(offset.into().as_millis());
90
91 if !timing.has_sample() {
92 output.clear();
93 return false;
94 }
95
96 #[allow(
97 clippy::cast_possible_truncation,
98 reason = "Normalized keyframe offsets are stored as f32 throughout the keyframe module."
99 )]
100 self.keyframes
101 .sample_into(timing.iteration_progress as f32, output)
102 }
103
104 #[must_use]
106 pub fn completion_snapshot(&self) -> Option<PropertySnapshot> {
107 self.keyframes.sample_completion()
108 }
109}
110
111impl<K: PropertyValueKind> PropertyTrackBuilder<K> {
112 #[must_use]
114 pub fn new(property: PropertySpec<K>) -> Self {
115 Self {
116 property,
117 builder: KeyframesBuilder::default(),
118 }
119 }
120
121 #[must_use]
123 pub fn to(self, value: K::Inner) -> Self {
124 let property = self.property;
125 let builder = self.builder.at(1.0, (property, value));
126
127 Self { property, builder }
128 }
129
130 #[must_use]
132 pub fn keyframe_at_end(mut self, property: PropertyEntry) -> Self {
133 self.builder = self.builder.at(1.0, vec![property]);
134 self
135 }
136
137 #[must_use]
139 pub fn duration(mut self, duration: impl Into<Duration>) -> Self {
140 let timing = *self.builder.timing();
141
142 self.builder = self
143 .builder
144 .with_timing(with_duration(timing, duration.into()));
145 self
146 }
147
148 #[must_use]
150 pub fn easing(mut self, easing: Easing) -> Self {
151 let timing = self.builder.timing().with_easing(easing);
152
153 self.builder = self.builder.with_timing(timing);
154 self
155 }
156
157 #[must_use]
159 pub fn finish(self) -> Track {
160 Track::new(self.builder.finish())
161 }
162}
163
164impl<K: PropertyValueKind> From<PropertyTrackBuilder<K>> for Track {
165 fn from(value: PropertyTrackBuilder<K>) -> Self {
166 value.finish()
167 }
168}
169
170impl From<Keyframes> for Track {
171 fn from(value: Keyframes) -> Self {
172 Self::new(value)
173 }
174}
175
176fn with_duration(timing: Timing, duration: Duration) -> Timing {
177 Timing::new(duration.as_millis())
178 .with_delay(timing.delay())
179 .with_direction(timing.direction())
180 .with_fill_mode(timing.fill_mode())
181 .with_easing(timing.easing())
182 .with_iterations(timing.iterations())
183 .with_playback_rate(timing.playback_rate())
184}