aura_anim_iced/runtime/
tick.rs1use super::{AnimationHandle, AnimationPlaybackState};
2use crate::runtime::{
3 registry::AnimationRegistry,
4 target::{AnimationTargetId, TargetedPropertySnapshot},
5};
6use crate::{property::PropertySnapshot, timing::Duration};
7
8#[derive(Debug, Clone, PartialEq)]
10pub struct AnimationTick {
11 timestamp: Duration,
12 properties: TargetedPropertySnapshot,
13 completed: Vec<AnimationHandle>,
14 removed: Vec<AnimationHandle>,
15 scratch: PropertySnapshot,
16}
17
18impl AnimationTick {
19 fn new(
20 timestamp: Duration,
21 properties: TargetedPropertySnapshot,
22 completed: Vec<AnimationHandle>,
23 ) -> Self {
24 Self {
25 timestamp,
26 properties,
27 completed,
28 removed: Vec::new(),
29 scratch: PropertySnapshot::new(),
30 }
31 }
32
33 #[must_use]
35 pub fn empty() -> Self {
36 Self::new(Duration::ZERO, TargetedPropertySnapshot::new(), Vec::new())
37 }
38
39 #[must_use]
41 pub const fn timestamp(&self) -> Duration {
42 self.timestamp
43 }
44
45 #[must_use]
47 pub fn properties(&self) -> &TargetedPropertySnapshot {
48 &self.properties
49 }
50
51 #[must_use]
53 pub fn properties_for(&self, target: AnimationTargetId) -> Option<&PropertySnapshot> {
54 self.properties.get(target)
55 }
56
57 #[must_use]
59 pub fn completed(&self) -> &[AnimationHandle] {
60 &self.completed
61 }
62
63 #[must_use]
65 pub fn is_empty(&self) -> bool {
66 self.properties.is_empty()
67 }
68}
69
70pub(super) fn tick_registry(registry: &mut AnimationRegistry, now: Duration) -> AnimationTick {
71 let mut tick = AnimationTick::empty();
72
73 tick_registry_into(registry, now, &mut tick);
74
75 tick
76}
77
78pub(super) fn tick_registry_into(
79 registry: &mut AnimationRegistry,
80 now: Duration,
81 tick: &mut AnimationTick,
82) {
83 tick.timestamp = now;
84 tick.properties.clear();
85 tick.completed.clear();
86 tick.removed.clear();
87 tick.scratch.clear();
88
89 for entry in registry.entries_mut() {
90 let target = entry.target();
91 match entry.state() {
92 AnimationPlaybackState::Playing => {
93 let delta = now.checked_sub(entry.last_tick()).unwrap_or(Duration::ZERO);
94 entry.update_position(delta);
95 entry.set_last_tick(now);
96
97 if let Some(duration) = entry.source().total_duration()
98 && source_is_complete(duration, entry.position())
99 {
100 entry.set_position(duration);
101 let has_snapshot = entry.source().completion_snapshot_into(&mut tick.scratch);
102
103 update_last_snapshot(entry, has_snapshot, &tick.scratch);
104 entry.mark_completed(now);
105 tick.completed.push(entry.handle());
106 tick.removed.push(entry.handle());
107 merge_sampled_snapshot(
108 &mut tick.properties,
109 target,
110 has_snapshot,
111 &tick.scratch,
112 );
113 } else {
114 let has_snapshot = entry
115 .source()
116 .sample_into(entry.position(), &mut tick.scratch);
117
118 update_last_snapshot(entry, has_snapshot, &tick.scratch);
119 merge_sampled_snapshot(
120 &mut tick.properties,
121 target,
122 has_snapshot,
123 &tick.scratch,
124 );
125 }
126 }
127 AnimationPlaybackState::Paused => {
128 entry.set_last_tick(now);
129 if let Some(snapshot) = entry.last_snapshot() {
130 tick.properties.merge_entries(target, snapshot.entries());
131 }
132 }
133 AnimationPlaybackState::Canceled => {
134 tick.removed.push(entry.handle());
135 }
136 AnimationPlaybackState::Completed => {
137 tick.completed.push(entry.handle());
138 tick.removed.push(entry.handle());
139 }
140 }
141 }
142
143 tick.scratch.clear();
144
145 for handle in &tick.removed {
146 registry.remove_by_handle(*handle);
147 }
148
149 #[cfg(feature = "tracing")]
150 tracing::trace!(
151 target: "aura_anim_iced::runtime",
152 timestamp_ms = now.as_millis(),
153 output_targets = tick.properties.targets().count(),
154 completed = tick.completed.len(),
155 active = registry.active_count(),
156 "runtime tick"
157 );
158
159 #[cfg(feature = "inspector")]
160 tracing::debug!(
161 target: "aura_anim_iced::inspector",
162 timestamp_ms = now.as_millis(),
163 output_targets = tick.properties.targets().count(),
164 completed = tick.completed.len(),
165 removed = tick.removed.len(),
166 active = registry.active_count(),
167 "runtime inspector tick"
168 );
169}
170
171fn source_is_complete(total_duration: Duration, elapsed: Duration) -> bool {
172 elapsed.as_millis() >= total_duration.as_millis()
173}
174
175fn update_last_snapshot(
176 entry: &mut crate::runtime::entry::ActiveAnimation,
177 has_snapshot: bool,
178 snapshot: &PropertySnapshot,
179) {
180 if has_snapshot {
181 entry.replace_last_snapshot(snapshot);
182 } else {
183 entry.clear_last_snapshot();
184 }
185}
186
187fn merge_sampled_snapshot(
188 properties: &mut TargetedPropertySnapshot,
189 target: AnimationTargetId,
190 has_snapshot: bool,
191 snapshot: &PropertySnapshot,
192) {
193 if has_snapshot {
194 properties.merge_entries(target, snapshot.entries());
195 }
196}