1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3pub enum TouchPhase {
4 Start,
5 Move,
6 End,
7 Cancel,
8}
9
10#[derive(Debug, Clone)]
12pub struct TouchPoint {
13 pub id: u64,
14 pub x: f32,
15 pub y: f32,
16 pub phase: TouchPhase,
17}
18
19#[derive(Debug, Default)]
21pub struct TouchState {
22 pub points: Vec<TouchPoint>,
24 pub started_this_frame: Vec<TouchPoint>,
26 pub ended_this_frame: Vec<TouchPoint>,
28 swipe_starts: Vec<(u64, f32, f32, f64)>, }
31
32impl TouchState {
33 pub fn begin_frame(&mut self) {
35 self.started_this_frame.clear();
36 self.ended_this_frame.clear();
37 }
38
39 pub fn touch_event(&mut self, id: u64, x: f32, y: f32, phase: TouchPhase, time: f64) {
41 let point = TouchPoint { id, x, y, phase };
42
43 match phase {
44 TouchPhase::Start => {
45 self.points.push(point.clone());
46 self.started_this_frame.push(point);
47 self.swipe_starts.push((id, x, y, time));
48 }
49 TouchPhase::Move => {
50 if let Some(p) = self.points.iter_mut().find(|p| p.id == id) {
51 p.x = x;
52 p.y = y;
53 p.phase = TouchPhase::Move;
54 }
55 }
56 TouchPhase::End | TouchPhase::Cancel => {
57 if let Some(p) = self.points.iter_mut().find(|p| p.id == id) {
58 p.x = x;
59 p.y = y;
60 p.phase = phase;
61 }
62 self.ended_this_frame.push(point);
63 self.points.retain(|p| p.id != id);
64 self.swipe_starts.retain(|(sid, _, _, _)| *sid != id);
65 }
66 }
67 }
68
69 pub fn count(&self) -> usize {
71 self.points.len()
72 }
73
74 pub fn get(&self, index: usize) -> Option<&TouchPoint> {
76 self.points.get(index)
77 }
78
79 pub fn is_active(&self) -> bool {
81 !self.points.is_empty()
82 }
83
84 pub fn get_position(&self, index: usize) -> Option<(f32, f32)> {
86 self.points.get(index).map(|p| (p.x, p.y))
87 }
88
89 pub fn detect_swipe(
92 &self,
93 min_distance: f32,
94 ) -> Option<(&'static str, f32, f32, f32, f32, f32)> {
95 for ended in &self.ended_this_frame {
96 let dx = ended.x; let dy = ended.y;
102 let _ = (dx, dy, min_distance);
105 }
106 None
107 }
108
109 pub fn detect_pinch(&self) -> Option<(f32, f32)> {
112 if self.points.len() >= 2 {
113 let p1 = &self.points[0];
114 let p2 = &self.points[1];
115 let cx = (p1.x + p2.x) / 2.0;
116 let cy = (p1.y + p2.y) / 2.0;
117 let _dist = ((p2.x - p1.x).powi(2) + (p2.y - p1.y).powi(2)).sqrt();
118 Some((cx, cy))
119 } else {
120 None
121 }
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn touch_start_adds_point() {
131 let mut state = TouchState::default();
132 state.touch_event(1, 100.0, 200.0, TouchPhase::Start, 0.0);
133 assert_eq!(state.count(), 1);
134 assert!(state.is_active());
135 }
136
137 #[test]
138 fn touch_end_removes_point() {
139 let mut state = TouchState::default();
140 state.touch_event(1, 100.0, 200.0, TouchPhase::Start, 0.0);
141 state.touch_event(1, 100.0, 200.0, TouchPhase::End, 0.1);
142 assert_eq!(state.count(), 0);
143 assert!(!state.is_active());
144 }
145
146 #[test]
147 fn touch_move_updates_position() {
148 let mut state = TouchState::default();
149 state.touch_event(1, 100.0, 200.0, TouchPhase::Start, 0.0);
150 state.touch_event(1, 150.0, 250.0, TouchPhase::Move, 0.016);
151 let pos = state.get_position(0).unwrap();
152 assert!((pos.0 - 150.0).abs() < f32::EPSILON);
153 assert!((pos.1 - 250.0).abs() < f32::EPSILON);
154 }
155
156 #[test]
157 fn multi_touch_tracked() {
158 let mut state = TouchState::default();
159 state.touch_event(1, 100.0, 200.0, TouchPhase::Start, 0.0);
160 state.touch_event(2, 300.0, 400.0, TouchPhase::Start, 0.0);
161 assert_eq!(state.count(), 2);
162 }
163
164 #[test]
165 fn begin_frame_clears_started_ended() {
166 let mut state = TouchState::default();
167 state.touch_event(1, 100.0, 200.0, TouchPhase::Start, 0.0);
168 assert_eq!(state.started_this_frame.len(), 1);
169
170 state.begin_frame();
171 assert_eq!(state.started_this_frame.len(), 0);
172 assert_eq!(state.count(), 1);
174 }
175
176 #[test]
177 fn cancel_removes_point() {
178 let mut state = TouchState::default();
179 state.touch_event(1, 100.0, 200.0, TouchPhase::Start, 0.0);
180 state.touch_event(1, 100.0, 200.0, TouchPhase::Cancel, 0.1);
181 assert_eq!(state.count(), 0);
182 }
183
184 #[test]
185 fn get_returns_none_for_invalid_index() {
186 let state = TouchState::default();
187 assert!(state.get(0).is_none());
188 }
189
190 #[test]
191 fn get_position_returns_correct_coords() {
192 let mut state = TouchState::default();
193 state.touch_event(1, 42.0, 84.0, TouchPhase::Start, 0.0);
194 let (x, y) = state.get_position(0).unwrap();
195 assert!((x - 42.0).abs() < f32::EPSILON);
196 assert!((y - 84.0).abs() < f32::EPSILON);
197 }
198
199 #[test]
200 fn default_state_is_empty() {
201 let state = TouchState::default();
202 assert_eq!(state.count(), 0);
203 assert!(!state.is_active());
204 assert!(state.started_this_frame.is_empty());
205 assert!(state.ended_this_frame.is_empty());
206 }
207
208 #[test]
209 fn detect_pinch_returns_center_with_two_touches() {
210 let mut state = TouchState::default();
211 state.touch_event(1, 100.0, 100.0, TouchPhase::Start, 0.0);
212 state.touch_event(2, 200.0, 200.0, TouchPhase::Start, 0.0);
213 let (cx, cy) = state.detect_pinch().unwrap();
214 assert!((cx - 150.0).abs() < f32::EPSILON);
215 assert!((cy - 150.0).abs() < f32::EPSILON);
216 }
217
218 #[test]
219 fn detect_pinch_returns_none_with_one_touch() {
220 let mut state = TouchState::default();
221 state.touch_event(1, 100.0, 100.0, TouchPhase::Start, 0.0);
222 assert!(state.detect_pinch().is_none());
223 }
224}