use crate::cirque::TouchData; use core::{ cmp, ops::Sub, }; #[derive(Copy, Clone, Debug)] pub struct Point(pub T, pub T); impl Point where T: Sub, { pub fn delta(self, p: Point) -> Point { Point(p.0 - self.0, p.1 - self.1) } } impl Point { pub fn scale(self) -> Self { Point(self.0 >> 2, self.1 >> 2) } } impl From<&TouchData> for Point { fn from(td: &TouchData) -> Self { Self(td.x as _, td.y as _) } } impl From> for Point { fn from(p: Point) -> Self { Point( cmp::min(i8::MAX as isize, cmp::max(p.0, i8::MIN as isize)) as i8, cmp::min(i8::MAX as isize, cmp::max(p.1, i8::MIN as isize)) as i8, ) } } pub struct AbsToRel { last_touch: Point, is_collecting: bool, } impl AbsToRel { pub fn new() -> Self { Self { last_touch: Point(0, 0), is_collecting: false, } } pub fn update(&mut self, td: &TouchData) -> Option> { let p: Point = td.into(); let p = p.scale(); let res = self.last_touch.delta(p); self.last_touch = p; if self.is_collecting && td.is_pressed { Some(res.into()) } else { self.is_collecting = td.is_pressed; None } } } pub struct TrackBall(Point, i8, u32); impl TrackBall where { pub fn new(friction: i8) -> Self { Self(Point(i8::default(), i8::default()), friction, 0) } pub fn update(&mut self, point: Option>, elapsed: u32) -> Point { if let Some(p) = point { self.0 = p; self.2 = 0; } else { let elapsed = elapsed + self.2; // TODO: configure time divisor let decel: i8 = self.1.saturating_mul((elapsed >> 15).try_into().unwrap_or(i8::MAX)); let rem = elapsed & (1 << 15) - 1; self.2 = rem; if decel > 0 { let x = Self::abs_add(self.0.0, decel); let y = Self::abs_add(self.0.1, decel); self.0 = Point(x, y); } } self.0 } fn abs_add(speed: i8, decel: i8) -> i8 { if speed > decel { speed - decel } else if speed < -decel { speed + decel } else { 0 } } }