From cad82214217913944fa69aa6f1a616a189f2cd89 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 5 Mar 2025 23:45:56 +0100 Subject: [PATCH 1/9] Add InputEvent::source --- lighthouse-protocol/src/input/input_event.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lighthouse-protocol/src/input/input_event.rs b/lighthouse-protocol/src/input/input_event.rs index 82cd48c..981ee4c 100644 --- a/lighthouse-protocol/src/input/input_event.rs +++ b/lighthouse-protocol/src/input/input_event.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use super::{GamepadEvent, KeyEvent, MouseEvent}; +use super::{EventSource, GamepadEvent, KeyEvent, MouseEvent}; /// A user input event, as generated by the new frontend (LUNA). #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] @@ -11,6 +11,17 @@ pub enum InputEvent { Gamepad(GamepadEvent), } +impl InputEvent { + /// The event's source. + pub fn source(&self) -> &EventSource { + match self { + InputEvent::Key(KeyEvent { source, .. }) => source, + InputEvent::Mouse(MouseEvent { source, .. }) => source, + InputEvent::Gamepad(GamepadEvent { source, .. }) => source, + } + } +} + #[cfg(test)] mod tests { use serde_json::json; From a9b427fd04cc450739814f56646c779548d82a1d Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 6 Mar 2025 00:05:38 +0100 Subject: [PATCH 2/9] Add helpers for extracting direction from KeyEvent --- lighthouse-protocol/src/input/key_event.rs | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/lighthouse-protocol/src/input/key_event.rs b/lighthouse-protocol/src/input/key_event.rs index 4f4b445..b1bd496 100644 --- a/lighthouse-protocol/src/input/key_event.rs +++ b/lighthouse-protocol/src/input/key_event.rs @@ -1,5 +1,7 @@ use serde::{Deserialize, Serialize}; +use crate::{Delta, Unity, Zero}; + use super::{EventSource, KeyModifiers}; /// A keyboard event. @@ -17,3 +19,32 @@ pub struct KeyEvent { /// The held key modifiers. pub modifiers: KeyModifiers, } + +impl KeyEvent { + /// The direction if either the WASD or arrow keys were pressed. + pub fn direction(&self) -> Option> where T: Zero + Unity { + self.wasd_direction().or_else(|| self.arrow_direction()) + } + + /// The direction if one of the WASD keys was pressed. + pub fn wasd_direction(&self) -> Option> where T: Zero + Unity { + match self.code.as_str() { + "KeyW" => Some(Delta::UP), + "KeyA" => Some(Delta::LEFT), + "KeyS" => Some(Delta::DOWN), + "KeyD" => Some(Delta::RIGHT), + _ => None, + } + } + + /// The direction if one of the arrow keys was pressed. + pub fn arrow_direction(&self) -> Option> where T: Zero + Unity { + match self.code.as_str() { + "ArrowUp" => Some(Delta::UP), + "ArrowLeft" => Some(Delta::LEFT), + "ArrowDown" => Some(Delta::DOWN), + "ArrowRight" => Some(Delta::RIGHT), + _ => None, + } + } +} From ceba5817f7e59d403898822719df172ea62fab6d Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 6 Mar 2025 00:12:52 +0100 Subject: [PATCH 3/9] Add GamepadButtonEvent::d_pad_direction --- .../src/input/gamepad_button_event.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lighthouse-protocol/src/input/gamepad_button_event.rs b/lighthouse-protocol/src/input/gamepad_button_event.rs index daf0ac2..77f0732 100644 --- a/lighthouse-protocol/src/input/gamepad_button_event.rs +++ b/lighthouse-protocol/src/input/gamepad_button_event.rs @@ -1,5 +1,7 @@ use serde::{Deserialize, Serialize}; +use crate::{Delta, Unity, Zero}; + /// A button event on a gamepad. #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] #[serde(tag = "control", rename_all = "camelCase")] @@ -11,3 +13,17 @@ pub struct GamepadButtonEvent { /// The value of the button (between 0.0 and 1.0, modeled after the Web Gamepad API). pub value: f64, } + +impl GamepadButtonEvent { + /// The direction if one of the D-pad buttons was pressed. + /// See https://www.w3.org/TR/gamepad/#dfn-standard-gamepad + pub fn d_pad_direction(&self) -> Option> where T: Zero + Unity { + match self.index { + 12 => Some(Delta::UP), + 13 => Some(Delta::DOWN), + 14 => Some(Delta::LEFT), + 15 => Some(Delta::RIGHT), + _ => None, + } + } +} From 84aaf3c1344c8a2442175ae2533041a71fea5456 Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 6 Mar 2025 00:54:54 +0100 Subject: [PATCH 4/9] Remove legacy delta module --- lighthouse-protocol/src/utils/delta.rs | 71 -------------------------- 1 file changed, 71 deletions(-) delete mode 100644 lighthouse-protocol/src/utils/delta.rs diff --git a/lighthouse-protocol/src/utils/delta.rs b/lighthouse-protocol/src/utils/delta.rs deleted file mode 100644 index d2cdc77..0000000 --- a/lighthouse-protocol/src/utils/delta.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::{fmt, ops::{Add, Sub, Neg}}; - -use rand::{Rng, thread_rng}; - -use crate::Rotation; - -/// A 2D vector on the lighthouse display. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct Delta { - pub dx: i32, - pub dy: i32, -} - -impl Delta { - /// The empty delta. - pub const ZERO: Self = Self::new(0, 0); - - /// The vector pointing one pixel to the left. - pub const LEFT: Self = Self::new(-1, 0); - /// The vector pointing one pixel up. - pub const UP: Self = Self::new( 0, -1); - /// The vector pointing one pixel to the right. - pub const RIGHT: Self = Self::new( 1, 0); - /// The vector pointing one pixel down. - pub const DOWN: Self = Self::new( 0, 1); - - /// Creates a new vector. - pub const fn new(dx: i32, dy: i32) -> Self { - Self { dx, dy } - } - - /// Randomly one of the four cardinal directions with the given rng. - pub fn random_cardinal_with(rng: &mut impl Rng) -> Self { - Rotation::random_cardinal_with(rng) * Self::RIGHT - } - - /// Randomly one of the four cardinal directions with the thread-local rng. - pub fn random_cardinal() -> Self { - Self::random_cardinal_with(&mut thread_rng()) - } -} - -impl fmt::Display for Delta { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "({}, {})", self.dx, self.dy) - } -} - -impl Add for Delta { - type Output = Delta; - - fn add(self, rhs: Self) -> Self { - Self::new(self.dx + rhs.dx, self.dy + rhs.dy) - } -} - -impl Sub for Delta { - type Output = Delta; - - fn sub(self, rhs: Self) -> Self { - Self::new(self.dx - rhs.dx, self.dy - rhs.dy) - } -} - -impl Neg for Delta { - type Output = Delta; - - fn neg(self) -> Self { - Self::new(-self.dx, -self.dy) - } -} From 5e34aae0083d55628dcb4bc3557f74aeab3adba6 Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 6 Mar 2025 01:01:28 +0100 Subject: [PATCH 5/9] Add Direction type --- lighthouse-protocol/src/utils/direction.rs | 56 ++++++++++++++++++++++ lighthouse-protocol/src/utils/mod.rs | 2 + 2 files changed, 58 insertions(+) create mode 100644 lighthouse-protocol/src/utils/direction.rs diff --git a/lighthouse-protocol/src/utils/direction.rs b/lighthouse-protocol/src/utils/direction.rs new file mode 100644 index 0000000..82072c1 --- /dev/null +++ b/lighthouse-protocol/src/utils/direction.rs @@ -0,0 +1,56 @@ +use std::fmt::Debug; + +use rand::{prelude::Distribution, distributions::Standard}; +use serde::{Deserialize, Serialize}; + +use super::{Delta, Unity, Zero}; + +/// One of the four cardinal directions. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum Direction { + Up, + Down, + Left, + Right, +} + +impl TryFrom> for Direction where T: Zero + Unity + PartialEq + Debug { + type Error = String; + + fn try_from(delta: Delta) -> Result { + if delta == Delta::UP { + Ok(Direction::Up) + } else if delta == Delta::DOWN { + Ok(Direction::Down) + } else if delta == Delta::LEFT { + Ok(Direction::Left) + } else if delta == Delta::RIGHT { + Ok(Direction::Right) + } else { + Err(format!("Not a direction: {:?}", delta)) + } + } +} + +impl From for Delta where T: Zero + Unity { + fn from(direction: Direction) -> Self { + match direction { + Direction::Up => Delta::UP, + Direction::Down => Delta::DOWN, + Direction::Left => Delta::LEFT, + Direction::Right => Delta::RIGHT, + } + } +} + +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> Direction { + match rng.gen_range(0..4) { + 0 => Direction::Up, + 1 => Direction::Down, + 2 => Direction::Left, + 3 => Direction::Right, + _ => unreachable!(), + } + } +} diff --git a/lighthouse-protocol/src/utils/mod.rs b/lighthouse-protocol/src/utils/mod.rs index c637d92..10291c8 100644 --- a/lighthouse-protocol/src/utils/mod.rs +++ b/lighthouse-protocol/src/utils/mod.rs @@ -1,4 +1,5 @@ mod color; +mod direction; mod rect; mod rem_euclid; mod rotation; @@ -7,6 +8,7 @@ mod vec2; mod zero; pub use color::*; +pub use direction::*; pub use rect::*; pub use rem_euclid::*; pub use rotation::*; From 4f353afa706c3c1380e8d186734bd8458b781fc7 Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 6 Mar 2025 01:03:19 +0100 Subject: [PATCH 6/9] Use Direction type for the event helpers --- .../src/input/gamepad_button_event.rs | 12 +++++----- lighthouse-protocol/src/input/key_event.rs | 24 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/lighthouse-protocol/src/input/gamepad_button_event.rs b/lighthouse-protocol/src/input/gamepad_button_event.rs index 77f0732..2f80ea9 100644 --- a/lighthouse-protocol/src/input/gamepad_button_event.rs +++ b/lighthouse-protocol/src/input/gamepad_button_event.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::{Delta, Unity, Zero}; +use crate::Direction; /// A button event on a gamepad. #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] @@ -17,12 +17,12 @@ pub struct GamepadButtonEvent { impl GamepadButtonEvent { /// The direction if one of the D-pad buttons was pressed. /// See https://www.w3.org/TR/gamepad/#dfn-standard-gamepad - pub fn d_pad_direction(&self) -> Option> where T: Zero + Unity { + pub fn d_pad_direction(&self) -> Option { match self.index { - 12 => Some(Delta::UP), - 13 => Some(Delta::DOWN), - 14 => Some(Delta::LEFT), - 15 => Some(Delta::RIGHT), + 12 => Some(Direction::Up), + 13 => Some(Direction::Down), + 14 => Some(Direction::Left), + 15 => Some(Direction::Right), _ => None, } } diff --git a/lighthouse-protocol/src/input/key_event.rs b/lighthouse-protocol/src/input/key_event.rs index b1bd496..432a087 100644 --- a/lighthouse-protocol/src/input/key_event.rs +++ b/lighthouse-protocol/src/input/key_event.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::{Delta, Unity, Zero}; +use crate::Direction; use super::{EventSource, KeyModifiers}; @@ -22,28 +22,28 @@ pub struct KeyEvent { impl KeyEvent { /// The direction if either the WASD or arrow keys were pressed. - pub fn direction(&self) -> Option> where T: Zero + Unity { + pub fn direction(&self) -> Option { self.wasd_direction().or_else(|| self.arrow_direction()) } /// The direction if one of the WASD keys was pressed. - pub fn wasd_direction(&self) -> Option> where T: Zero + Unity { + pub fn wasd_direction(&self) -> Option { match self.code.as_str() { - "KeyW" => Some(Delta::UP), - "KeyA" => Some(Delta::LEFT), - "KeyS" => Some(Delta::DOWN), - "KeyD" => Some(Delta::RIGHT), + "KeyW" => Some(Direction::Up), + "KeyA" => Some(Direction::Left), + "KeyS" => Some(Direction::Down), + "KeyD" => Some(Direction::Right), _ => None, } } /// The direction if one of the arrow keys was pressed. - pub fn arrow_direction(&self) -> Option> where T: Zero + Unity { + pub fn arrow_direction(&self) -> Option { match self.code.as_str() { - "ArrowUp" => Some(Delta::UP), - "ArrowLeft" => Some(Delta::LEFT), - "ArrowDown" => Some(Delta::DOWN), - "ArrowRight" => Some(Delta::RIGHT), + "ArrowUp" => Some(Direction::Up), + "ArrowLeft" => Some(Direction::Left), + "ArrowDown" => Some(Direction::Down), + "ArrowRight" => Some(Direction::Right), _ => None, } } From fb4a5accd302b5911066ac4094a8c960f0c80381 Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 6 Mar 2025 01:19:21 +0100 Subject: [PATCH 7/9] Add Sqrt trait and Vec2::length --- lighthouse-protocol/src/utils/mod.rs | 2 ++ lighthouse-protocol/src/utils/sqrt.rs | 19 +++++++++++++++++++ lighthouse-protocol/src/utils/vec2.rs | 11 +++++++++-- 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 lighthouse-protocol/src/utils/sqrt.rs diff --git a/lighthouse-protocol/src/utils/mod.rs b/lighthouse-protocol/src/utils/mod.rs index 10291c8..822bd4d 100644 --- a/lighthouse-protocol/src/utils/mod.rs +++ b/lighthouse-protocol/src/utils/mod.rs @@ -3,6 +3,7 @@ mod direction; mod rect; mod rem_euclid; mod rotation; +mod sqrt; mod unity; mod vec2; mod zero; @@ -12,6 +13,7 @@ pub use direction::*; pub use rect::*; pub use rem_euclid::*; pub use rotation::*; +pub use sqrt::*; pub use unity::*; pub use vec2::*; pub use zero::*; diff --git a/lighthouse-protocol/src/utils/sqrt.rs b/lighthouse-protocol/src/utils/sqrt.rs new file mode 100644 index 0000000..a940ef0 --- /dev/null +++ b/lighthouse-protocol/src/utils/sqrt.rs @@ -0,0 +1,19 @@ +/// A type whose values have a square root. +pub trait Sqrt { + /// The square root. + fn sqrt(self) -> Self; +} + +macro_rules! impl_sqrt { + ($($tys:ty),*) => { + $(impl Sqrt for $tys { + fn sqrt(self) -> Self { + <$tys>::sqrt(self) + } + })* + }; +} + +impl_sqrt!( + f32, f64 +); diff --git a/lighthouse-protocol/src/utils/vec2.rs b/lighthouse-protocol/src/utils/vec2.rs index eb516c6..e22c818 100644 --- a/lighthouse-protocol/src/utils/vec2.rs +++ b/lighthouse-protocol/src/utils/vec2.rs @@ -1,9 +1,9 @@ -use std::{fmt, ops::{Add, AddAssign, Neg, Sub, SubAssign}}; +use std::{fmt, ops::{Add, AddAssign, Mul, Neg, Sub, SubAssign}}; use rand::{thread_rng, Rng}; use serde::{Deserialize, Serialize}; -use super::{Unity, Zero}; +use super::{Sqrt, Unity, Zero}; /// A 2D vector. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] @@ -59,6 +59,13 @@ impl Vec2 where T: Zero + Unity { } } +impl Vec2 where T: Add + Mul + Sqrt + Copy { + /// The vector's length. + pub fn length(&self) -> T { + (self.x * self.x + self.y * self.y).sqrt() + } +} + impl fmt::Display for Vec2 where T: fmt::Display { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "({}, {})", self.x, self.y) From 1941ff17a1f13ab50f0e096a9c8b251820d03e75 Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 6 Mar 2025 01:23:13 +0100 Subject: [PATCH 8/9] Add GamepadAxis2DEvent::direction --- .../src/input/gamepad_axis_2d_event.rs | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/lighthouse-protocol/src/input/gamepad_axis_2d_event.rs b/lighthouse-protocol/src/input/gamepad_axis_2d_event.rs index e421258..567343e 100644 --- a/lighthouse-protocol/src/input/gamepad_axis_2d_event.rs +++ b/lighthouse-protocol/src/input/gamepad_axis_2d_event.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::Vec2; +use crate::{Direction, Vec2}; /// A 2D axis event on a gamepad. #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] @@ -11,3 +11,52 @@ pub struct GamepadAxis2DEvent { /// The value of the axis (each component is between -1.0 and 1.0, modeled after the Web Gamepad API). pub value: Vec2, } + +impl GamepadAxis2DEvent { + /// The approximate direction (outside of a small deadzone). + pub fn direction(&self) -> Option { + let deadzone_radius: f64 = 0.1; + if self.value.length() < deadzone_radius { + return None; + } + + // See https://www.desmos.com/calculator/472pdoxzqa for visualization + // Note that the y-axis is flipped here, per computer graphics conventions, + // hence the sign flip (-y instead of y). + let Vec2 { x, y } = self.value; + let left_or_up = x < -y; + let right_or_up = -x < -y; + Some( + match (left_or_up, right_or_up) { + (true, true) => Direction::Up, + (true, false) => Direction::Left, + (false, true) => Direction::Right, + (false, false) => Direction::Down, + } + ) + } +} + +#[cfg(test)] +mod tests { + use crate::{Direction, Vec2, Zero}; + + use super::GamepadAxis2DEvent; + + #[test] + fn directions() { + assert_eq!(event(Vec2::UP).direction(), Some(Direction::Up)); + assert_eq!(event(Vec2::DOWN).direction(), Some(Direction::Down)); + assert_eq!(event(Vec2::LEFT).direction(), Some(Direction::Left)); + assert_eq!(event(Vec2::RIGHT).direction(), Some(Direction::Right)); + assert_eq!(event(Vec2::ZERO).direction(), None); + assert_eq!(event(Vec2::new(-0.05, 0.05)).direction(), None); // within deadzone + } + + fn event(value: Vec2) -> GamepadAxis2DEvent { + GamepadAxis2DEvent { + index: 0, + value, + } + } +} From d2bfdba60656e941b959d8edaa6321ba0043895f Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 6 Mar 2025 01:28:28 +0100 Subject: [PATCH 9/9] Add InputEvent::direction, left_direction and right_direction --- lighthouse-protocol/src/input/input_event.rs | 36 +++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/lighthouse-protocol/src/input/input_event.rs b/lighthouse-protocol/src/input/input_event.rs index 981ee4c..7bcb15f 100644 --- a/lighthouse-protocol/src/input/input_event.rs +++ b/lighthouse-protocol/src/input/input_event.rs @@ -1,6 +1,8 @@ use serde::{Deserialize, Serialize}; -use super::{EventSource, GamepadEvent, KeyEvent, MouseEvent}; +use crate::Direction; + +use super::{EventSource, GamepadControlEvent, GamepadEvent, KeyEvent, MouseEvent}; /// A user input event, as generated by the new frontend (LUNA). #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] @@ -20,6 +22,38 @@ impl InputEvent { InputEvent::Gamepad(GamepadEvent { source, .. }) => source, } } + + /// Parses the input event as an arbitrary direction. + pub fn direction(&self) -> Option { + self.left_direction().or_else(|| self.right_direction()) + } + + /// The direction if the input event represents a WASD key, D-pad or left stick. + /// Commonly used e.g. for movement in games. + pub fn left_direction(&self) -> Option { + match self { + InputEvent::Key(key) => key.wasd_direction(), + InputEvent::Gamepad(gamepad) => match &gamepad.control { + GamepadControlEvent::Button(button) => button.d_pad_direction(), + GamepadControlEvent::Axis2D(axis2d) if axis2d.index == 0 => axis2d.direction(), + _ => None, + }, + _ => None, + } + } + + /// The direction if the input event represents an arrow key or right stick. + /// Commonly used e.g. for camera control in games. + pub fn right_direction(&self) -> Option { + match self { + InputEvent::Key(key) => key.arrow_direction(), + InputEvent::Gamepad(gamepad) => match &gamepad.control { + GamepadControlEvent::Axis2D(axis2d) if axis2d.index == 1 => axis2d.direction(), + _ => None, + }, + _ => None, + } + } } #[cfg(test)]