From 72f7fd8e9d0d1a586135fad8dc3b4dfc9523cd03 Mon Sep 17 00:00:00 2001 From: Nova Date: Tue, 6 Aug 2024 17:41:39 -0400 Subject: [PATCH] refactor: modularize input object capture system --- src/objects/input/mod.rs | 71 +++++++++++++++ src/objects/input/mouse_pointer.rs | 122 +++++-------------------- src/objects/input/sk_controller.rs | 118 +++++-------------------- src/objects/input/sk_hand.rs | 137 ++++++----------------------- 4 files changed, 144 insertions(+), 304 deletions(-) diff --git a/src/objects/input/mod.rs b/src/objects/input/mod.rs index 108ac92..b0cc9f6 100644 --- a/src/objects/input/mod.rs +++ b/src/objects/input/mod.rs @@ -2,3 +2,74 @@ pub mod eye_pointer; pub mod mouse_pointer; pub mod sk_controller; pub mod sk_hand; + +use crate::nodes::{ + fields::{Field, FieldTrait, Ray}, input::{InputDataTrait, InputDataType, InputHandler, InputMethod, INPUT_HANDLER_REGISTRY}, spatial::Spatial +}; +use glam::vec3; +use std::sync::Arc; + + +#[derive(Default)] +pub struct CaptureManager { + pub capture: Option>, +} +impl CaptureManager { + pub fn update_capture(&mut self, pointer: &InputMethod) { + if let Some(capture) = &self.capture { + if !pointer.internal_capture_requests.get_valid_contents().contains(capture) { + self.capture.take(); + } + } + } + pub fn set_new_capture(&mut self, pointer: &InputMethod, distance_calculator: DistanceCalculator) { + if self.capture.is_none() { + self.capture = find_closest_capture(pointer, distance_calculator); + } + } + pub fn apply_capture(&self, method: &InputMethod) { + method.captures.clear(); + if let Some(capture) = &self.capture { + method.set_handler_order([capture].into_iter()); + method.captures.add_raw(capture); + } + } +} + + +type DistanceCalculator = fn(&Arc, &InputDataType, &Field) -> Option; + +pub fn find_closest_capture(method: &InputMethod, distance_calculator: DistanceCalculator) -> Option> { + method + .internal_capture_requests + .get_valid_contents() + .into_iter() + .filter_map(|h| distance_calculator(&method.spatial, &method.data.lock(), &h.field).map(|dist| (h.clone(), dist))) + .min_by(|(_, dist_a), (_, dist_b)| dist_a.partial_cmp(dist_b).unwrap()) + .map(|(handler, _)| handler) +} + +pub fn get_sorted_handlers(method: &InputMethod, distance_calculator: DistanceCalculator) -> Vec> { + INPUT_HANDLER_REGISTRY + .get_valid_contents() + .into_iter() + .filter(|handler| handler.spatial.node().map_or(false, |node| node.enabled())) + .filter(|handler| handler.field.spatial.node().map_or(false, |node| node.enabled())) + .filter_map(|handler| { + distance_calculator(&method.spatial, &method.data.lock(), &handler.field) + .map(|distance| (vec![handler], distance)) + }) + .filter(|(_, distance)| *distance > 0.0) + .reduce(|(mut handlers_a, distance_a), (handlers_b, distance_b)| { + if (distance_a - distance_b).abs() < 0.001 { + handlers_a.extend(handlers_b); + (handlers_a, distance_a) + } else if distance_a < distance_b { + (handlers_a, distance_a) + } else { + (handlers_b, distance_b) + } + }) + .map(|(handlers, _)| handlers) + .unwrap_or_default() +} diff --git a/src/objects/input/mouse_pointer.rs b/src/objects/input/mouse_pointer.rs index b498a2f..061e8c0 100644 --- a/src/objects/input/mouse_pointer.rs +++ b/src/objects/input/mouse_pointer.rs @@ -20,6 +20,8 @@ use std::sync::Arc; use stereokit_rust::system::{Input, Key}; use xkbcommon::xkb::{Context, Keymap, FORMAT_TEXT_V1}; +use super::{get_sorted_handlers, CaptureManager, DistanceCalculator}; + #[derive(Deserialize, Serialize)] struct MouseEvent { select: f32, @@ -58,7 +60,7 @@ pub struct MousePointer { keymap: DefaultKey, spatial: Arc, pointer: Arc, - capture: Option>, + capture_manager: CaptureManager, mouse_datamap: MouseEvent, keyboard_datamap: KeyboardEvent, keyboard_sender: Arc, @@ -89,7 +91,7 @@ impl MousePointer { node, spatial, pointer, - capture: None, + capture_manager: CaptureManager::default(), mouse_datamap: Default::default(), keyboard_datamap: KeyboardEvent { keyboard: (), @@ -146,108 +148,30 @@ impl MousePointer { } fn target_pointer_input(&mut self) { - // remove the capture when it's removed from captures list - if let Some(capture) = &self.capture { - if !self - .pointer - .internal_capture_requests - .get_valid_contents() - .contains(capture) - { - self.capture.take(); + let distance_calculator: DistanceCalculator = |space, data, field| { + let result = field.ray_march(Ray { + origin: vec3(0.0, 0.0, 0.0), + direction: vec3(0.0, 0.0, -1.0), + space: space.clone(), + }); + if result.deepest_point_distance > 0.0 && result.min_distance < 0.05 { + Some(result.deepest_point_distance) + } else { + None } - } - // add the capture that's the closest if we don't have one - if self.capture.is_none() { - if let Some(new_capture) = self - .pointer - .internal_capture_requests - .get_valid_contents() - .into_iter() - .map(|h| { - ( - h.clone(), - h.field.ray_march(Ray { - origin: vec3(0.0, 0.0, 0.0), - direction: vec3(0.0, 0.0, -1.0), - space: self.spatial.clone(), - }), - ) - }) - .reduce(|(handlers_a, result_a), (handlers_b, result_b)| { - if result_a.min_distance < result_b.min_distance { - (handlers_a, result_a) - } else { - (handlers_b, result_b) - } - }) - .map(|(rx, _)| rx) - { - self.capture = Some(new_capture.clone()); - } - } + }; - // make sure that if something is captured only send input to it - self.pointer.captures.clear(); - if let Some(capture) = &self.capture { - self.pointer.set_handler_order([capture].into_iter()); - self.pointer.captures.add_raw(capture); + self.capture_manager.update_capture(&self.pointer); + self.capture_manager + .set_new_capture(&self.pointer, distance_calculator); + self.capture_manager.apply_capture(&self.pointer); + + if self.capture_manager.capture.is_some() { return; } - // send input to all the input handlers that are the closest to the ray as possible - self.pointer.set_handler_order( - INPUT_HANDLER_REGISTRY - .get_valid_contents() - .into_iter() - // filter out all the disabled handlers - .filter(|handler| { - let Some(node) = handler.spatial.node() else { - return false; - }; - node.enabled() - }) - // filter out all the fields with disabled handlers - .filter(|handler| { - let Some(node) = handler.field.spatial.node() else { - return false; - }; - node.enabled() - }) - // ray march to all the enabled handlers' fields - .map(|handler| { - let result = handler.field.ray_march(Ray { - origin: vec3(0.0, 0.0, 0.0), - direction: vec3(0.0, 0.0, -1.0), - space: self.spatial.clone(), - }); - (vec![handler], result) - }) - // make sure the field isn't at the pointer origin and that it's being hit - .filter(|(_, result)| { - result.deepest_point_distance > 0.01 && result.min_distance < 0.0 - }) - // .inspect(|(_, result)| { - // dbg!(result); - // }) - // now collect all handlers that are same distance if they're the closest - .reduce(|(mut handlers_a, result_a), (handlers_b, result_b)| { - if (result_a.deepest_point_distance - result_b.deepest_point_distance).abs() - < 0.001 - { - // distance is basically the same - handlers_a.extend(handlers_b); - (handlers_a, result_a) - } else if result_a.deepest_point_distance < result_b.deepest_point_distance { - (handlers_a, result_a) - } else { - (handlers_b, result_b) - } - }) - .map(|(rx, _)| rx) - .unwrap_or_default() - .iter(), - ); + let sorted_handlers = get_sorted_handlers(&self.pointer, distance_calculator); + self.pointer.set_handler_order(sorted_handlers.iter()); } fn send_keyboard_input(&mut self) { diff --git a/src/objects/input/sk_controller.rs b/src/objects/input/sk_controller.rs index 99c3af4..d87c73e 100644 --- a/src/objects/input/sk_controller.rs +++ b/src/objects/input/sk_controller.rs @@ -1,7 +1,8 @@ +use super::{get_sorted_handlers, CaptureManager}; use crate::{ core::client::INTERNAL_CLIENT, nodes::{ - fields::FieldTrait, + fields::{Field, FieldTrait}, input::{InputDataType, InputHandler, InputMethod, Tip, INPUT_HANDLER_REGISTRY}, spatial::Spatial, Node, OwnedNode, @@ -35,7 +36,7 @@ pub struct SkController { handed: Handed, model: Model, material: Material, - capture: Option>, + capture_manager: CaptureManager, datamap: ControllerDatamap, } impl SkController { @@ -69,7 +70,7 @@ impl SkController { handed, model, material, - capture: None, + capture_manager: CaptureManager::default(), datamap: Default::default(), }) } @@ -82,11 +83,12 @@ impl SkController { controller.aim.orientation.into(), controller.aim.position.into(), ); - self.material.color_tint(if self.capture.is_none() { - Color128::new_rgb(1.0, 1.0, 1.0) - } else { - Color128::new_rgb(0.0, 1.0, 0.75) - }); + self.material + .color_tint(if self.capture_manager.capture.is_none() { + Color128::new_rgb(1.0, 1.0, 1.0) + } else { + Color128::new_rgb(0.0, 1.0, 0.75) + }); self.model.draw( token, world_transform * Mat4::from_scale(Vec3::ONE * 0.02), @@ -100,98 +102,20 @@ impl SkController { self.datamap.scroll = controller.stick.into(); *self.input.datamap.lock() = Datamap::from_typed(&self.datamap).unwrap(); - // remove the capture when it's removed from captures list - if let Some(capture) = &self.capture { - if !self - .input - .internal_capture_requests - .get_valid_contents() - .contains(capture) - { - self.capture.take(); - } - } - // add the capture that's the closest if we don't have one - if self.capture.is_none() { - self.capture = self - .input - .internal_capture_requests - .get_valid_contents() - .into_iter() - .map(|handler| { - ( - handler.clone(), - handler - .field - .distance(&self.input.spatial, [0.0; 3].into()) - .abs(), - ) - }) - .reduce(|(handlers_a, distance_a), (handlers_b, distance_b)| { - if distance_a < distance_b { - (handlers_a, distance_a) - } else { - (handlers_b, distance_b) - } - }) - .map(|(rx, _)| rx); - } + let distance_calculator = |space: &Arc, _data: &InputDataType, field: &Field| { + Some(field.distance(space, [0.0; 3].into()).abs()) + }; - // make sure that if something is captured only send input to it - self.input.captures.clear(); - if let Some(capture) = &self.capture { - self.input.set_handler_order([capture].into_iter()); - self.input.captures.add_raw(capture); + self.capture_manager.update_capture(&self.input); + self.capture_manager + .set_new_capture(&self.input, distance_calculator); + self.capture_manager.apply_capture(&self.input); + + if self.capture_manager.capture.is_some() { return; } - // send input to all the input handlers that are the closest to the ray as possible - self.input.set_handler_order( - INPUT_HANDLER_REGISTRY - .get_valid_contents() - .into_iter() - // filter out all the disabled handlers - .filter(|handler| { - let Some(node) = handler.spatial.node() else { - return false; - }; - node.enabled() - }) - // filter out all the fields with disabled handlers - .filter(|handler| { - let Some(node) = handler.field.spatial.node() else { - return false; - }; - node.enabled() - }) - // get the unsigned distance to the handler's field (unsigned so giant fields won't always eat input) - .map(|handler| { - ( - vec![handler.clone()], - handler - .field - .distance(&self.input.spatial, [0.0; 3].into()) - .abs(), - ) - }) - // .inspect(|(_, result)| { - // dbg!(result); - // }) - // now collect all handlers that are same distance if they're the closest - .reduce(|(mut handlers_a, distance_a), (handlers_b, distance_b)| { - if (distance_a - distance_b).abs() < 0.001 { - // distance is basically the same (within 1mm) - handlers_a.extend(handlers_b); - (handlers_a, distance_a) - } else if distance_a < distance_b { - (handlers_a, distance_a) - } else { - (handlers_b, distance_b) - } - }) - .map(|(rx, _)| rx) - .unwrap_or_default() - .iter(), - ); + let sorted_handlers = get_sorted_handlers(&self.input, distance_calculator); + self.input.set_handler_order(sorted_handlers.iter()); } } diff --git a/src/objects/input/sk_hand.rs b/src/objects/input/sk_hand.rs index ed796b4..06b1bde 100644 --- a/src/objects/input/sk_hand.rs +++ b/src/objects/input/sk_hand.rs @@ -19,6 +19,8 @@ use stereokit_rust::system::{HandJoint, HandSource, Handed, Input, LinePoint, Li use stereokit_rust::util::Color128; use zbus::Connection; +use super::{get_sorted_handlers, CaptureManager}; + fn convert_joint(joint: HandJoint) -> Joint { Joint { position: Vec3::from(joint.position).into(), @@ -40,7 +42,7 @@ pub struct SkHand { palm_object: ObjectHandle, handed: Handed, input: Arc, - capture: Option>, + capture_manager: CaptureManager, datamap: HandDatamap, } impl SkHand { @@ -69,7 +71,7 @@ impl SkHand { palm_object, handed, input, - capture: None, + capture_manager: CaptureManager::default(), datamap: Default::default(), }) } @@ -122,7 +124,7 @@ impl SkHand { self.draw( token, - if self.capture.is_none() { + if self.capture_manager.capture.is_none() { Color128::new_rgb(1.0, 1.0, 1.0) } else { Color128::new_rgb(0.0, 1.0, 0.75) @@ -135,88 +137,34 @@ impl SkHand { self.datamap.grab_strength = sk_hand.grip_activation; *self.input.datamap.lock() = Datamap::from_typed(&self.datamap).unwrap(); - // remove the capture when it's removed from captures list - if let Some(capture) = &self.capture { - if !self - .input - .internal_capture_requests - .get_valid_contents() - .contains(capture) - { - self.capture.take(); - } - } - // add the capture that's the closest if we don't have one - if self.capture.is_none() { - self.capture = self - .input - .internal_capture_requests - .get_valid_contents() - .into_iter() - .map(|handler| (handler.clone(), self.compare_distance(&handler.field).abs())) - .reduce(|(handlers_a, distance_a), (handlers_b, distance_b)| { - if distance_a < distance_b { - (handlers_a, distance_a) - } else { - (handlers_b, distance_b) - } - }) - .map(|(rx, _)| rx); - } + let distance_calculator = |space: &Arc, data: &InputDataType, field: &Field| { + let InputDataType::Hand(hand) = data else { + return None; + }; + let thumb_tip_distance = field.distance(space, hand.thumb.tip.position.into()); + let index_tip_distance = field.distance(space, hand.index.tip.position.into()); + let middle_tip_distance = field.distance(space, hand.middle.tip.position.into()); + let ring_tip_distance = field.distance(space, hand.ring.tip.position.into()); - // make sure that if something is captured only send input to it - self.input.captures.clear(); - if let Some(capture) = &self.capture { - self.input.set_handler_order([capture].into_iter()); - self.input.captures.add_raw(capture); + Some( + (thumb_tip_distance * 0.3) + + (index_tip_distance * 0.4) + + (middle_tip_distance * 0.15) + + (ring_tip_distance * 0.15), + ) + }; + + self.capture_manager.update_capture(&self.input); + self.capture_manager + .set_new_capture(&self.input, distance_calculator); + self.capture_manager.apply_capture(&self.input); + + if self.capture_manager.capture.is_some() { return; } - // send input to all the input handlers that are the closest to the ray as possible - self.input.set_handler_order( - INPUT_HANDLER_REGISTRY - .get_valid_contents() - .into_iter() - // filter out all the disabled handlers - .filter(|handler| { - let Some(node) = handler.spatial.node() else { - return false; - }; - node.enabled() - }) - // filter out all the fields with disabled handlers - .filter(|handler| { - let Some(node) = handler.field.spatial.node() else { - return false; - }; - node.enabled() - }) - // get the unsigned distance to the handler's field (unsigned so giant fields won't always eat input) - .map(|handler| { - ( - vec![handler.clone()], - self.compare_distance(&handler.field).abs(), - ) - }) - // .inspect(|(_, result)| { - // dbg!(result); - // }) - // now collect all handlers that are same distance if they're the closest - .reduce(|(mut handlers_a, distance_a), (handlers_b, distance_b)| { - if (distance_a - distance_b).abs() < 0.001 { - // distance is basically the same (within 1mm) - handlers_a.extend(handlers_b); - (handlers_a, distance_a) - } else if distance_a < distance_b { - (handlers_a, distance_a) - } else { - (handlers_b, distance_b) - } - }) - .map(|(rx, _)| rx) - .unwrap_or_default() - .iter(), - ); + let sorted_handlers = get_sorted_handlers(&self.input, distance_calculator); + self.input.set_handler_order(sorted_handlers.iter()); } fn draw(&self, token: &MainThreadToken, color: Color128, hand: &Hand) { @@ -263,17 +211,6 @@ impl SkHand { joint_to_line_point(&hand.ring.metacarpal, color), ], ); - // little - Lines::add_list( - token, - &[ - joint_to_line_point(&hand.little.tip, color), - joint_to_line_point(&hand.little.distal, color), - joint_to_line_point(&hand.little.intermediate, color), - joint_to_line_point(&hand.little.proximal, color), - joint_to_line_point(&hand.little.metacarpal, color), - ], - ); // palm Lines::add_list( @@ -289,22 +226,6 @@ impl SkHand { ], ); } - - fn compare_distance(&self, field: &Field) -> f32 { - let InputDataType::Hand(hand) = &*self.input.data.lock() else { - return INFINITY; - }; - let spatial = &self.input.spatial; - let thumb_tip_distance = field.distance(spatial, hand.thumb.tip.position.into()); - let index_tip_distance = field.distance(spatial, hand.index.tip.position.into()); - let middle_tip_distance = field.distance(spatial, hand.middle.tip.position.into()); - let ring_tip_distance = field.distance(spatial, hand.ring.tip.position.into()); - - (thumb_tip_distance * 0.3) - + (index_tip_distance * 0.4) - + (middle_tip_distance * 0.15) - + (ring_tip_distance * 0.15) - } } fn joint_to_line_point(joint: &Joint, color: Color128) -> LinePoint {