1use std::collections::HashMap;
7
8pub type TouchId = u64;
10
11#[derive(Clone, Copy, Debug)]
13pub struct Touch {
14 pub id: TouchId,
16
17 pub pos: (f64, f64),
19
20 pub prev_pos: (f64, f64),
22
23 pub force: Option<f64>,
25
26 pub start_time: f64,
28}
29
30impl Touch {
31 pub fn new(id: TouchId, x: f64, y: f64, time: f64) -> Self {
33 Self {
34 id,
35 pos: (x, y),
36 prev_pos: (x, y),
37 force: None,
38 start_time: time,
39 }
40 }
41
42 pub fn update_pos(&mut self, x: f64, y: f64) {
44 self.prev_pos = self.pos;
45 self.pos = (x, y);
46 }
47
48 pub fn delta(&self) -> (f64, f64) {
50 (self.pos.0 - self.prev_pos.0, self.pos.1 - self.prev_pos.1)
51 }
52
53 pub fn distance_to(&self, other: &Touch) -> f64 {
55 let dx = self.pos.0 - other.pos.0;
56 let dy = self.pos.1 - other.pos.1;
57 (dx * dx + dy * dy).sqrt()
58 }
59
60 pub fn angle_to(&self, other: &Touch) -> f64 {
62 let dx = other.pos.0 - self.pos.0;
63 let dy = other.pos.1 - self.pos.1;
64 dy.atan2(dx)
65 }
66}
67
68#[derive(Clone, Debug, Default)]
70pub struct TouchState {
71 touches: HashMap<TouchId, Touch>,
73
74 prev_touch_count: usize,
76
77 pub pinch_delta: Option<f64>,
79
80 prev_pinch_distance: Option<f64>,
82
83 pub rotation_delta: Option<f64>,
85
86 prev_rotation_angle: Option<f64>,
88
89 pub pan_delta: Option<(f64, f64)>,
91
92 primary_id: Option<TouchId>,
94}
95
96impl TouchState {
97 pub fn new() -> Self {
99 Self::default()
100 }
101
102 pub fn update_touch(&mut self, id: TouchId, x: f64, y: f64, time: f64, force: Option<f64>) {
104 if let Some(touch) = self.touches.get_mut(&id) {
105 touch.update_pos(x, y);
106 touch.force = force;
107 } else {
108 let mut touch = Touch::new(id, x, y, time);
109 touch.force = force;
110 self.touches.insert(id, touch);
111
112 if self.primary_id.is_none() {
114 self.primary_id = Some(id);
115 }
116 }
117
118 self.update_gestures();
119 }
120
121 pub fn touch_start(&mut self, id: TouchId, x: f64, y: f64, time: f64, force: Option<f64>) {
123 self.update_touch(id, x, y, time, force);
124 }
125
126 pub fn touch_move(&mut self, id: TouchId, x: f64, y: f64, time: f64, force: Option<f64>) {
128 self.update_touch(id, x, y, time, force);
129 }
130
131 pub fn touch_end(&mut self, id: TouchId) {
133 self.remove_touch(id);
134 }
135
136 pub fn touch_cancel(&mut self, id: TouchId) {
138 self.remove_touch(id);
139 }
140
141 pub fn remove_touch(&mut self, id: TouchId) {
143 self.touches.remove(&id);
144
145 if self.primary_id == Some(id) {
147 self.primary_id = self.touches.keys().next().copied();
148 }
149
150 self.update_gestures();
151 }
152
153 pub fn clear(&mut self) {
155 self.touches.clear();
156 self.primary_id = None;
157 self.clear_deltas();
158 }
159
160 pub fn clear_deltas(&mut self) {
162 self.pinch_delta = None;
163 self.rotation_delta = None;
164 self.pan_delta = None;
165 self.prev_touch_count = self.touches.len();
166 }
167
168 fn update_gestures(&mut self) {
170 let touch_count = self.touches.len();
171
172 if touch_count == 2 {
174 let touches: Vec<&Touch> = self.touches.values().collect();
175 let t1 = touches[0];
176 let t2 = touches[1];
177
178 let current_distance = t1.distance_to(t2);
180 if let Some(prev_distance) = self.prev_pinch_distance {
181 self.pinch_delta = Some(current_distance - prev_distance);
182 }
183 self.prev_pinch_distance = Some(current_distance);
184
185 let current_angle = t1.angle_to(t2);
187 if let Some(prev_angle) = self.prev_rotation_angle {
188 let mut angle_delta = current_angle - prev_angle;
189
190 while angle_delta > std::f64::consts::PI {
192 angle_delta -= 2.0 * std::f64::consts::PI;
193 }
194 while angle_delta < -std::f64::consts::PI {
195 angle_delta += 2.0 * std::f64::consts::PI;
196 }
197
198 self.rotation_delta = Some(angle_delta);
199 }
200 self.prev_rotation_angle = Some(current_angle);
201
202 let delta1 = t1.delta();
204 let delta2 = t2.delta();
205 let avg_delta = (
206 (delta1.0 + delta2.0) / 2.0,
207 (delta1.1 + delta2.1) / 2.0,
208 );
209
210 if avg_delta.0.abs() > 0.1 || avg_delta.1.abs() > 0.1 {
212 self.pan_delta = Some(avg_delta);
213 }
214 } else {
215 if self.prev_touch_count == 2 {
217 self.prev_pinch_distance = None;
219 self.prev_rotation_angle = None;
220 }
221 }
222 }
223
224 pub fn primary_touch(&self) -> Option<&Touch> {
226 self.primary_id.and_then(|id| self.touches.get(&id))
227 }
228
229 pub fn get_touch(&self, id: TouchId) -> Option<&Touch> {
231 self.touches.get(&id)
232 }
233
234 pub fn touches(&self) -> impl Iterator<Item = &Touch> {
236 self.touches.values()
237 }
238
239 pub fn touch_count(&self) -> usize {
241 self.touches.len()
242 }
243
244 pub fn has_touches(&self) -> bool {
246 !self.touches.is_empty()
247 }
248
249 pub fn centroid(&self) -> Option<(f64, f64)> {
251 if self.touches.is_empty() {
252 return None;
253 }
254
255 let count = self.touches.len() as f64;
256 let sum = self.touches.values().fold((0.0, 0.0), |acc, touch| {
257 (acc.0 + touch.pos.0, acc.1 + touch.pos.1)
258 });
259
260 Some((sum.0 / count, sum.1 / count))
261 }
262
263 pub fn is_two_finger_gesture(&self) -> bool {
265 self.touches.len() == 2
266 }
267
268 pub fn is_pinching(&self) -> bool {
270 self.pinch_delta.is_some() && self.pinch_delta.unwrap().abs() > 0.1
271 }
272
273 pub fn is_rotating(&self) -> bool {
275 self.rotation_delta.is_some() && self.rotation_delta.unwrap().abs() > 0.01
276 }
277
278 pub fn is_two_finger_panning(&self) -> bool {
280 self.pan_delta.is_some()
281 }
282}
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287
288 #[test]
289 fn test_touch_creation() {
290 let touch = Touch::new(1, 100.0, 200.0, 0.0);
291 assert_eq!(touch.id, 1);
292 assert_eq!(touch.pos, (100.0, 200.0));
293 assert_eq!(touch.prev_pos, (100.0, 200.0));
294 assert_eq!(touch.delta(), (0.0, 0.0));
295 }
296
297 #[test]
298 fn test_touch_update() {
299 let mut touch = Touch::new(1, 100.0, 200.0, 0.0);
300 touch.update_pos(150.0, 250.0);
301
302 assert_eq!(touch.pos, (150.0, 250.0));
303 assert_eq!(touch.prev_pos, (100.0, 200.0));
304 assert_eq!(touch.delta(), (50.0, 50.0));
305 }
306
307 #[test]
308 fn test_touch_distance() {
309 let t1 = Touch::new(1, 0.0, 0.0, 0.0);
310 let t2 = Touch::new(2, 3.0, 4.0, 0.0);
311
312 assert_eq!(t1.distance_to(&t2), 5.0); }
314
315 #[test]
316 fn test_touch_angle() {
317 let t1 = Touch::new(1, 0.0, 0.0, 0.0);
318 let t2 = Touch::new(2, 1.0, 0.0, 0.0);
319
320 assert_eq!(t1.angle_to(&t2), 0.0); let t3 = Touch::new(3, 0.0, 1.0, 0.0);
323 assert!((t1.angle_to(&t3) - std::f64::consts::PI / 2.0).abs() < 0.001); }
325
326 #[test]
327 fn test_touch_state_add_remove() {
328 let mut state = TouchState::new();
329 assert_eq!(state.touch_count(), 0);
330 assert!(!state.has_touches());
331
332 state.update_touch(1, 100.0, 200.0, 0.0, None);
333 assert_eq!(state.touch_count(), 1);
334 assert!(state.has_touches());
335 assert_eq!(state.primary_touch().unwrap().id, 1);
336
337 state.update_touch(2, 300.0, 400.0, 0.0, None);
338 assert_eq!(state.touch_count(), 2);
339
340 state.remove_touch(1);
341 assert_eq!(state.touch_count(), 1);
342 assert_eq!(state.primary_touch().unwrap().id, 2); state.clear();
345 assert_eq!(state.touch_count(), 0);
346 assert!(!state.has_touches());
347 }
348
349 #[test]
350 fn test_centroid() {
351 let mut state = TouchState::new();
352
353 state.update_touch(1, 0.0, 0.0, 0.0, None);
354 assert_eq!(state.centroid(), Some((0.0, 0.0)));
355
356 state.update_touch(2, 100.0, 100.0, 0.0, None);
357 assert_eq!(state.centroid(), Some((50.0, 50.0)));
358
359 state.update_touch(3, 200.0, 200.0, 0.0, None);
360 assert_eq!(state.centroid(), Some((100.0, 100.0)));
361 }
362
363 #[test]
364 fn test_pinch_gesture() {
365 let mut state = TouchState::new();
366
367 state.update_touch(1, 0.0, 0.0, 0.0, None);
369 state.update_touch(2, 100.0, 0.0, 0.0, None);
370 assert!(state.is_two_finger_gesture());
371
372 assert!(state.pinch_delta.is_none());
374
375 state.clear_deltas();
376
377 state.update_touch(1, 10.0, 0.0, 0.1, None);
379 state.update_touch(2, 90.0, 0.0, 0.1, None);
380
381 assert!(state.pinch_delta.is_some());
383 let delta = state.pinch_delta.unwrap();
384 assert!(delta < 0.0); }
386
387 #[test]
388 fn test_rotation_gesture() {
389 let mut state = TouchState::new();
390
391 state.update_touch(1, 0.0, 0.0, 0.0, None);
393 state.update_touch(2, 100.0, 0.0, 0.0, None);
394
395 state.clear_deltas();
396
397 state.update_touch(1, 0.0, 0.0, 0.1, None);
399 state.update_touch(2, 0.0, 100.0, 0.1, None);
400
401 assert!(state.rotation_delta.is_some());
402 let rotation = state.rotation_delta.unwrap();
403
404 assert!((rotation - std::f64::consts::PI / 2.0).abs() < 0.1);
406 }
407
408 #[test]
409 fn test_pan_gesture() {
410 let mut state = TouchState::new();
411
412 state.update_touch(1, 0.0, 0.0, 0.0, None);
413 state.update_touch(2, 100.0, 0.0, 0.0, None);
414
415 state.clear_deltas();
416
417 state.update_touch(1, 10.0, 0.0, 0.1, None);
419 state.update_touch(2, 110.0, 0.0, 0.1, None);
420
421 assert!(state.pan_delta.is_some());
422 let (dx, dy) = state.pan_delta.unwrap();
423 assert!((dx - 10.0).abs() < 0.1);
424 assert!(dy.abs() < 0.1);
425 }
426
427 #[test]
428 fn test_clear_deltas() {
429 let mut state = TouchState::new();
430
431 state.update_touch(1, 0.0, 0.0, 0.0, None);
432 state.update_touch(2, 100.0, 0.0, 0.0, None);
433
434 state.update_touch(1, 10.0, 0.0, 0.1, None);
436 state.update_touch(2, 90.0, 0.0, 0.1, None);
437
438 state.clear_deltas();
440
441 assert!(state.pinch_delta.is_none());
442 assert!(state.rotation_delta.is_none());
443 assert!(state.pan_delta.is_none());
444 }
445}