#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TouchPhase {
Start,
Move,
End,
Cancel,
}
#[derive(Debug, Clone)]
pub struct TouchPoint {
pub id: u64,
pub x: f32,
pub y: f32,
pub phase: TouchPhase,
}
#[derive(Debug, Default)]
pub struct TouchState {
pub points: Vec<TouchPoint>,
pub started_this_frame: Vec<TouchPoint>,
pub ended_this_frame: Vec<TouchPoint>,
swipe_starts: Vec<(u64, f32, f32, f64)>, }
impl TouchState {
pub fn begin_frame(&mut self) {
self.started_this_frame.clear();
self.ended_this_frame.clear();
}
pub fn touch_event(&mut self, id: u64, x: f32, y: f32, phase: TouchPhase, time: f64) {
let point = TouchPoint { id, x, y, phase };
match phase {
TouchPhase::Start => {
self.points.push(point.clone());
self.started_this_frame.push(point);
self.swipe_starts.push((id, x, y, time));
}
TouchPhase::Move => {
if let Some(p) = self.points.iter_mut().find(|p| p.id == id) {
p.x = x;
p.y = y;
p.phase = TouchPhase::Move;
}
}
TouchPhase::End | TouchPhase::Cancel => {
if let Some(p) = self.points.iter_mut().find(|p| p.id == id) {
p.x = x;
p.y = y;
p.phase = phase;
}
self.ended_this_frame.push(point);
self.points.retain(|p| p.id != id);
self.swipe_starts.retain(|(sid, _, _, _)| *sid != id);
}
}
}
pub fn count(&self) -> usize {
self.points.len()
}
pub fn get(&self, index: usize) -> Option<&TouchPoint> {
self.points.get(index)
}
pub fn is_active(&self) -> bool {
!self.points.is_empty()
}
pub fn get_position(&self, index: usize) -> Option<(f32, f32)> {
self.points.get(index).map(|p| (p.x, p.y))
}
pub fn detect_swipe(
&self,
min_distance: f32,
) -> Option<(&'static str, f32, f32, f32, f32, f32)> {
for ended in &self.ended_this_frame {
let dx = ended.x; let dy = ended.y;
let _ = (dx, dy, min_distance);
}
None
}
pub fn detect_pinch(&self) -> Option<(f32, f32)> {
if self.points.len() >= 2 {
let p1 = &self.points[0];
let p2 = &self.points[1];
let cx = (p1.x + p2.x) / 2.0;
let cy = (p1.y + p2.y) / 2.0;
let _dist = ((p2.x - p1.x).powi(2) + (p2.y - p1.y).powi(2)).sqrt();
Some((cx, cy))
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn touch_start_adds_point() {
let mut state = TouchState::default();
state.touch_event(1, 100.0, 200.0, TouchPhase::Start, 0.0);
assert_eq!(state.count(), 1);
assert!(state.is_active());
}
#[test]
fn touch_end_removes_point() {
let mut state = TouchState::default();
state.touch_event(1, 100.0, 200.0, TouchPhase::Start, 0.0);
state.touch_event(1, 100.0, 200.0, TouchPhase::End, 0.1);
assert_eq!(state.count(), 0);
assert!(!state.is_active());
}
#[test]
fn touch_move_updates_position() {
let mut state = TouchState::default();
state.touch_event(1, 100.0, 200.0, TouchPhase::Start, 0.0);
state.touch_event(1, 150.0, 250.0, TouchPhase::Move, 0.016);
let pos = state.get_position(0).unwrap();
assert!((pos.0 - 150.0).abs() < f32::EPSILON);
assert!((pos.1 - 250.0).abs() < f32::EPSILON);
}
#[test]
fn multi_touch_tracked() {
let mut state = TouchState::default();
state.touch_event(1, 100.0, 200.0, TouchPhase::Start, 0.0);
state.touch_event(2, 300.0, 400.0, TouchPhase::Start, 0.0);
assert_eq!(state.count(), 2);
}
#[test]
fn begin_frame_clears_started_ended() {
let mut state = TouchState::default();
state.touch_event(1, 100.0, 200.0, TouchPhase::Start, 0.0);
assert_eq!(state.started_this_frame.len(), 1);
state.begin_frame();
assert_eq!(state.started_this_frame.len(), 0);
assert_eq!(state.count(), 1);
}
#[test]
fn cancel_removes_point() {
let mut state = TouchState::default();
state.touch_event(1, 100.0, 200.0, TouchPhase::Start, 0.0);
state.touch_event(1, 100.0, 200.0, TouchPhase::Cancel, 0.1);
assert_eq!(state.count(), 0);
}
#[test]
fn get_returns_none_for_invalid_index() {
let state = TouchState::default();
assert!(state.get(0).is_none());
}
#[test]
fn get_position_returns_correct_coords() {
let mut state = TouchState::default();
state.touch_event(1, 42.0, 84.0, TouchPhase::Start, 0.0);
let (x, y) = state.get_position(0).unwrap();
assert!((x - 42.0).abs() < f32::EPSILON);
assert!((y - 84.0).abs() < f32::EPSILON);
}
#[test]
fn default_state_is_empty() {
let state = TouchState::default();
assert_eq!(state.count(), 0);
assert!(!state.is_active());
assert!(state.started_this_frame.is_empty());
assert!(state.ended_this_frame.is_empty());
}
#[test]
fn detect_pinch_returns_center_with_two_touches() {
let mut state = TouchState::default();
state.touch_event(1, 100.0, 100.0, TouchPhase::Start, 0.0);
state.touch_event(2, 200.0, 200.0, TouchPhase::Start, 0.0);
let (cx, cy) = state.detect_pinch().unwrap();
assert!((cx - 150.0).abs() < f32::EPSILON);
assert!((cy - 150.0).abs() < f32::EPSILON);
}
#[test]
fn detect_pinch_returns_none_with_one_touch() {
let mut state = TouchState::default();
state.touch_event(1, 100.0, 100.0, TouchPhase::Start, 0.0);
assert!(state.detect_pinch().is_none());
}
}