use bevy_ecs::prelude::*;
use bevy_ecs::{reflect::ReflectResource, system::SystemParam};
use bevy_input::touch::{Touch, Touches};
use bevy_math::Vec2;
use bevy_reflect::Reflect;
#[derive(Resource, Clone, Copy, Reflect)]
#[reflect(Resource)]
pub struct TouchInputSettings {
allowed_pinch_delta_diff: f32,
pinch_threshold: f32,
drag_threshold: f32,
}
impl Default for TouchInputSettings {
fn default() -> Self {
Self {
pinch_threshold: 1.0,
allowed_pinch_delta_diff: 1.0,
drag_threshold: 5.0,
}
}
}
pub(super) struct Pinch {
pub distance_delta: f32,
pub middle: Vec2,
}
#[derive(SystemParam)]
pub(super) struct TouchInputs<'w, 's> {
touch_settings: Res<'w, TouchInputSettings>,
touches: Res<'w, Touches>,
last_touch_1: Local<'s, Option<Vec2>>,
last_touch_2: Local<'s, Option<Vec2>>,
}
impl<'w, 's> TouchInputs<'w, 's> {
fn get_two_touches(&mut self) -> Option<[(Touch, Vec2); 2]> {
if self.touches.any_just_released() {
*self.last_touch_1 = None;
*self.last_touch_2 = None;
}
let touches: Vec<&Touch> = self.touches.iter().collect();
if touches.len() != 2 {
return None;
}
let touch1 = touches[0].clone();
let touch2 = touches[1].clone();
let last1 = self.last_touch_1.unwrap_or(touch1.position());
let last2 = self.last_touch_2.unwrap_or(touch2.position());
*self.last_touch_1 = Some(touch1.position());
*self.last_touch_2 = Some(touch2.position());
Some([(touch1, last1), (touch2, last2)])
}
pub fn get_pinch(&mut self) -> Option<Pinch> {
let [(touch1, last_a), (touch2, last_b)] = self.get_two_touches()?;
let d1 = touch1.position() - last_a;
let d2 = touch2.position() - last_b;
if d1 == Vec2::ZERO || d2 == Vec2::ZERO {
return None;
}
let distance = touch1.position().distance(touch2.position());
if d1.dot(d2) > self.touch_settings.allowed_pinch_delta_diff {
return None;
}
let previous_distance = touch1
.previous_position()
.distance(touch2.previous_position());
let distance_delta = distance - previous_distance;
if distance_delta.abs() < self.touch_settings.pinch_threshold {
return None;
}
let middle = (touch1.previous_position() + touch2.previous_position()) / 2.0;
Some(Pinch {
distance_delta,
middle,
})
}
pub fn get_two_touch_drag(&mut self) -> Option<Vec2> {
let [(touch1, last_a), (touch2, last_b)] = self.get_two_touches()?;
let d1 = touch1.position() - last_a;
let d2 = touch2.position() - last_b;
let dist = d1.distance(d2);
let avg = (d1 + d2) / 2.0;
if dist > self.touch_settings.drag_threshold
|| avg.length_squared() < self.touch_settings.drag_threshold
{
return None;
}
Some(avg)
}
pub fn clear_last_touches(&mut self) {
*self.last_touch_1 = None;
*self.last_touch_2 = None;
}
}