Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 50 additions & 1 deletion lighthouse-protocol/src/input/gamepad_axis_2d_event.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand All @@ -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<f64>,
}

impl GamepadAxis2DEvent {
/// The approximate direction (outside of a small deadzone).
pub fn direction(&self) -> Option<Direction> {
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<f64>) -> GamepadAxis2DEvent {
GamepadAxis2DEvent {
index: 0,
value,
}
}
}
16 changes: 16 additions & 0 deletions lighthouse-protocol/src/input/gamepad_button_event.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use serde::{Deserialize, Serialize};

use crate::Direction;

/// A button event on a gamepad.
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
#[serde(tag = "control", rename_all = "camelCase")]
Expand All @@ -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<Direction> {
match self.index {
12 => Some(Direction::Up),
13 => Some(Direction::Down),
14 => Some(Direction::Left),
15 => Some(Direction::Right),
_ => None,
}
}
}
47 changes: 46 additions & 1 deletion lighthouse-protocol/src/input/input_event.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use serde::{Deserialize, Serialize};

use super::{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)]
Expand All @@ -11,6 +13,49 @@ 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,
}
}

/// Parses the input event as an arbitrary direction.
pub fn direction(&self) -> Option<Direction> {
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<Direction> {
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<Direction> {
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)]
mod tests {
use serde_json::json;
Expand Down
31 changes: 31 additions & 0 deletions lighthouse-protocol/src/input/key_event.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use serde::{Deserialize, Serialize};

use crate::Direction;

use super::{EventSource, KeyModifiers};

/// A keyboard event.
Expand All @@ -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<Direction> {
self.wasd_direction().or_else(|| self.arrow_direction())
}

/// The direction if one of the WASD keys was pressed.
pub fn wasd_direction(&self) -> Option<Direction> {
match self.code.as_str() {
"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<Direction> {
match self.code.as_str() {
"ArrowUp" => Some(Direction::Up),
"ArrowLeft" => Some(Direction::Left),
"ArrowDown" => Some(Direction::Down),
"ArrowRight" => Some(Direction::Right),
_ => None,
}
}
}
71 changes: 0 additions & 71 deletions lighthouse-protocol/src/utils/delta.rs

This file was deleted.

56 changes: 56 additions & 0 deletions lighthouse-protocol/src/utils/direction.rs
Original file line number Diff line number Diff line change
@@ -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<T> TryFrom<Delta<T>> for Direction where T: Zero + Unity + PartialEq + Debug {
type Error = String;

fn try_from(delta: Delta<T>) -> Result<Self, Self::Error> {
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<T> From<Direction> for Delta<T> 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<Direction> for Standard {
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Direction {
match rng.gen_range(0..4) {
0 => Direction::Up,
1 => Direction::Down,
2 => Direction::Left,
3 => Direction::Right,
_ => unreachable!(),
}
}
}
4 changes: 4 additions & 0 deletions lighthouse-protocol/src/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
mod color;
mod direction;
mod rect;
mod rem_euclid;
mod rotation;
mod sqrt;
mod unity;
mod vec2;
mod zero;

pub use color::*;
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::*;
19 changes: 19 additions & 0 deletions lighthouse-protocol/src/utils/sqrt.rs
Original file line number Diff line number Diff line change
@@ -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
);
11 changes: 9 additions & 2 deletions lighthouse-protocol/src/utils/vec2.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand Down Expand Up @@ -59,6 +59,13 @@ impl<T> Vec2<T> where T: Zero + Unity {
}
}

impl<T> Vec2<T> where T: Add<Output = T> + Mul<Output = T> + Sqrt + Copy {
/// The vector's length.
pub fn length(&self) -> T {
(self.x * self.x + self.y * self.y).sqrt()
}
}

impl<T> fmt::Display for Vec2<T> where T: fmt::Display {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
Expand Down