use crate::math::CoordsCartesian;
use crate::measurements::SensorParams;
use crate::{measurements, HandState, SensorMeasurement};
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum Gesture {
GestureNone = 0,
GestureStaticHold,
GestureSwipeRight,
GestureSwipeLeft,
GestureSwipeUp,
GestureSwipeDown,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct RecognizerResult {
pub hand_state: HandState,
pub gesture: Gesture,
}
impl Default for RecognizerResult {
fn default() -> Self {
Self {
hand_state: HandState::HandNotFound,
gesture: Gesture::GestureNone,
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct RecognizerParams {
pub gesture_threshold_dist: f32,
pub static_hold_time_ms: u32,
pub static_hold_tolerance_dist: f32,
pub swipe_tolerance_dist: f32,
pub swipe_horizontal_travel_dist: f32,
pub swipe_vertical_travel_dist: f32,
}
impl Default for RecognizerParams {
fn default() -> Self {
Self {
gesture_threshold_dist: 400.0,
static_hold_time_ms: 1500,
static_hold_tolerance_dist: 100.0,
swipe_tolerance_dist: 120.0,
swipe_horizontal_travel_dist: 80.0,
swipe_vertical_travel_dist: 70.0,
}
}
}
#[repr(C)]
#[non_exhaustive]
#[derive(Debug, Clone, Copy)]
pub enum RecognizerStatus {
RecognizerStatusOk = 0,
RecognizerStatusInitFailure,
RecognizerStatusInvalidInput,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub(crate) struct HistoryEntry<const RES_X: usize, const RES_Y: usize> {
measurement: SensorMeasurement<RES_X, RES_Y>,
hand_state: HandState,
}
impl<const RES_X: usize, const RES_Y: usize> HistoryEntry<RES_X, RES_Y> {
pub fn invalid() -> Self {
Self {
measurement: SensorMeasurement::invalid(),
hand_state: HandState::HandNotFound,
}
}
}
pub(crate) fn iter_history_newer<
const RES_X: usize,
const RES_Y: usize,
T: IntoIterator<Item = HistoryEntry<RES_X, RES_Y>>,
>(
entries: T,
newer_than_ms: u32,
now: u32,
) -> impl Iterator<Item = HistoryEntry<RES_X, RES_Y>> {
entries
.into_iter()
.filter(move |e| now - e.measurement.time_ms < newer_than_ms)
}
pub(crate) fn iter_history_older_eq<
const RES_X: usize,
const RES_Y: usize,
T: IntoIterator<Item = HistoryEntry<RES_X, RES_Y>>,
>(
entries: T,
older_eq_ms: u32,
now: u32,
) -> impl Iterator<Item = HistoryEntry<RES_X, RES_Y>> {
entries
.into_iter()
.filter(move |e| now - e.measurement.time_ms >= older_eq_ms)
}
#[repr(C)]
#[derive(Debug, Clone)]
pub struct GestureRecognizer<const RES_X: usize, const RES_Y: usize, const HISTORY_SIZE: usize> {
params: RecognizerParams,
sensor_params: SensorParams,
start_time: u32,
history: [HistoryEntry<RES_X, RES_Y>; HISTORY_SIZE],
received_measurements: usize,
}
impl<const RES_X: usize, const RES_Y: usize, const HISTORY_SIZE: usize>
GestureRecognizer<RES_X, RES_Y, HISTORY_SIZE>
{
pub fn new(params: RecognizerParams, sensor_params: SensorParams) -> Self {
Self {
params,
sensor_params,
start_time: 0,
history: [HistoryEntry::invalid(); HISTORY_SIZE],
received_measurements: 0,
}
}
pub fn reset(
&mut self,
params: RecognizerParams,
sensor_params: SensorParams,
now: u32,
) -> RecognizerStatus {
self.params = params;
self.sensor_params = sensor_params;
self.start_time = now;
self.clear_history();
RecognizerStatus::RecognizerStatusOk
}
pub fn update(
&mut self,
measurement: SensorMeasurement<RES_X, RES_Y>,
recognizer_result: &mut RecognizerResult,
) -> RecognizerStatus {
*recognizer_result = RecognizerResult::default();
let now = measurement.time_ms;
if measurement.time_ms <= self.history.last().unwrap().measurement.time_ms {
return RecognizerStatus::RecognizerStatusInvalidInput;
}
let hand_state =
measurement.recognize_hand(&self.sensor_params, self.params.gesture_threshold_dist);
self.push_to_history(HistoryEntry {
measurement,
hand_state,
});
recognizer_result.hand_state = hand_state;
recognizer_result.gesture = self.recognize_gesture(now);
RecognizerStatus::RecognizerStatusOk
}
pub fn get_sensor_params(&self) -> SensorParams {
self.sensor_params
}
pub fn get_params(&self) -> RecognizerParams {
self.params
}
fn push_to_history(&mut self, entry: HistoryEntry<RES_X, RES_Y>) {
self.history.rotate_right(1);
self.history[0] = entry;
self.received_measurements += 1;
}
fn clear_history(&mut self) {
for entry in self.history.iter_mut() {
*entry = HistoryEntry::invalid();
}
self.received_measurements = 0;
}
fn recognize_gesture(&mut self, now: u32) -> Gesture {
let mut gesture = Gesture::GestureNone;
if self.find_static_hold(now) {
gesture = Gesture::GestureStaticHold;
self.clear_history();
return gesture;
}
let swipe_gesture = self.find_swipe(now);
if swipe_gesture != Gesture::GestureNone {
gesture = swipe_gesture;
self.clear_history();
return gesture;
}
gesture
}
fn find_static_hold(&self, now: u32) -> bool {
if self.received_measurements < HISTORY_SIZE.min(15) {
return false;
}
let abs_min = measurements::find_nearest_zone(
iter_history_newer(self.history, self.params.static_hold_time_ms, now)
.map(|e| e.measurement),
);
if abs_min.2 <= 0.0 || abs_min.2 > self.params.gesture_threshold_dist {
return false;
}
!iter_history_newer(self.history, self.params.static_hold_time_ms, now).any(|e| {
let zone_dist = e.measurement.zone_dist[abs_min.1[0]][abs_min.1[1]];
if zone_dist <= 0.0 || zone_dist > self.params.gesture_threshold_dist {
return true;
}
zone_dist > abs_min.2 + self.params.static_hold_tolerance_dist
|| zone_dist < abs_min.2 - self.params.static_hold_tolerance_dist
})
}
fn find_swipe(&self, now: u32) -> Gesture {
if self.received_measurements < HISTORY_SIZE.min(15) {
return Gesture::GestureNone;
}
for e in iter_history_newer(iter_history_older_eq(self.history, 300, now), 600, now) {
if let HandState::HandFound { hand_pos } = e.hand_state {
for n in iter_history_newer(self.history, 300, now) {
if let HandState::HandFound {
hand_pos: hand_pos_newer,
} = n.hand_state
{
let hand_pos_cart = CoordsCartesian::from(hand_pos);
let hand_pos_newer_cart = CoordsCartesian::from(hand_pos_newer);
if (hand_pos_newer_cart.x
>= hand_pos_cart.x - self.params.swipe_tolerance_dist)
&& (hand_pos_newer_cart.x
< hand_pos_cart.x + self.params.swipe_tolerance_dist)
{
if hand_pos_newer_cart.y - hand_pos_cart.y
> self.params.swipe_horizontal_travel_dist
{
return Gesture::GestureSwipeRight;
}
if hand_pos_newer_cart.y - hand_pos_cart.y
< -self.params.swipe_horizontal_travel_dist
{
return Gesture::GestureSwipeLeft;
}
if hand_pos_newer_cart.z - hand_pos_cart.z
> self.params.swipe_vertical_travel_dist
{
return Gesture::GestureSwipeUp;
}
if hand_pos_newer_cart.z - hand_pos_cart.z
< -self.params.swipe_vertical_travel_dist
{
return Gesture::GestureSwipeDown;
}
}
}
}
}
}
Gesture::GestureNone
}
}