viewport_lib/runtime/
snapshot.rs1use std::collections::HashMap;
13
14use crate::interaction::select::selection::NodeId;
15
16#[derive(Debug, Clone, Copy)]
18pub struct TransformSnapshot {
19 pub prev: glam::Affine3A,
21 pub curr: glam::Affine3A,
23}
24
25pub struct TransformSnapshotTable {
30 entries: HashMap<NodeId, TransformSnapshot>,
31}
32
33impl TransformSnapshotTable {
34 pub(super) fn new() -> Self {
35 Self {
36 entries: HashMap::new(),
37 }
38 }
39
40 pub fn update(&mut self, id: NodeId, transform: glam::Affine3A) {
45 let entry = self.entries.entry(id).or_insert(TransformSnapshot {
46 prev: transform,
47 curr: transform,
48 });
49 entry.prev = entry.curr;
50 entry.curr = transform;
51 }
52
53 pub fn get(&self, id: NodeId) -> Option<&TransformSnapshot> {
55 self.entries.get(&id)
56 }
57
58 pub fn interpolated(&self, id: NodeId, alpha: f32) -> Option<glam::Affine3A> {
66 let s = self.entries.get(&id)?;
67 let (prev_scale, prev_rot, prev_trans) =
68 glam::Mat4::from(s.prev).to_scale_rotation_translation();
69 let (curr_scale, curr_rot, curr_trans) =
70 glam::Mat4::from(s.curr).to_scale_rotation_translation();
71 Some(glam::Affine3A::from_scale_rotation_translation(
72 prev_scale.lerp(curr_scale, alpha),
73 prev_rot.slerp(curr_rot, alpha),
74 prev_trans.lerp(curr_trans, alpha),
75 ))
76 }
77
78 pub fn remove(&mut self, id: NodeId) {
80 self.entries.remove(&id);
81 }
82
83 pub fn clear(&mut self) {
85 self.entries.clear();
86 }
87
88 pub fn len(&self) -> usize {
90 self.entries.len()
91 }
92
93 pub fn is_empty(&self) -> bool {
95 self.entries.is_empty()
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 fn translation(x: f32) -> glam::Affine3A {
104 glam::Affine3A::from_translation(glam::Vec3::new(x, 0.0, 0.0))
105 }
106
107 #[test]
108 fn test_first_update_sets_both_to_same() {
109 let mut table = TransformSnapshotTable::new();
110 let t = translation(1.0);
111 table.update(1, t);
112 let s = table.get(1).unwrap();
113 assert_eq!(s.prev, t);
114 assert_eq!(s.curr, t);
115 }
116
117 #[test]
118 fn test_second_update_shifts_prev() {
119 let mut table = TransformSnapshotTable::new();
120 let t0 = translation(0.0);
121 let t1 = translation(1.0);
122 table.update(1, t0);
123 table.update(1, t1);
124 let s = table.get(1).unwrap();
125 assert_eq!(s.prev, t0);
126 assert_eq!(s.curr, t1);
127 }
128
129 #[test]
130 fn test_interpolated_at_zero_returns_prev() {
131 let mut table = TransformSnapshotTable::new();
132 table.update(1, translation(0.0));
133 table.update(1, translation(4.0));
134 let t = table.interpolated(1, 0.0).unwrap();
135 assert!((t.translation.x).abs() < 1e-5, "x was {}", t.translation.x);
136 }
137
138 #[test]
139 fn test_interpolated_at_one_returns_curr() {
140 let mut table = TransformSnapshotTable::new();
141 table.update(1, translation(0.0));
142 table.update(1, translation(4.0));
143 let t = table.interpolated(1, 1.0).unwrap();
144 assert!(
145 (t.translation.x - 4.0).abs() < 1e-5,
146 "x was {}",
147 t.translation.x
148 );
149 }
150
151 #[test]
152 fn test_interpolated_midpoint() {
153 let mut table = TransformSnapshotTable::new();
154 table.update(1, translation(0.0));
155 table.update(1, translation(4.0));
156 let t = table.interpolated(1, 0.5).unwrap();
157 assert!(
158 (t.translation.x - 2.0).abs() < 1e-5,
159 "x was {}",
160 t.translation.x
161 );
162 }
163
164 #[test]
165 fn test_missing_node_returns_none() {
166 let table = TransformSnapshotTable::new();
167 assert!(table.interpolated(999, 0.5).is_none());
168 assert!(table.get(999).is_none());
169 }
170
171 #[test]
172 fn test_remove() {
173 let mut table = TransformSnapshotTable::new();
174 table.update(1, translation(1.0));
175 table.remove(1);
176 assert!(table.get(1).is_none());
177 assert!(table.is_empty());
178 }
179
180 #[test]
181 fn test_clear() {
182 let mut table = TransformSnapshotTable::new();
183 table.update(1, translation(1.0));
184 table.update(2, translation(2.0));
185 table.clear();
186 assert!(table.is_empty());
187 assert_eq!(table.len(), 0);
188 }
189}