Skip to content

Commit eb0f3ab

Browse files
authored
Merge pull request #21 from ProjectLighthouseCAU/input-helpers
Add helper methods to input events
2 parents 734673b + d2bfdba commit eb0f3ab

File tree

9 files changed

+231
-75
lines changed

9 files changed

+231
-75
lines changed

lighthouse-protocol/src/input/gamepad_axis_2d_event.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use serde::{Deserialize, Serialize};
22

3-
use crate::Vec2;
3+
use crate::{Direction, Vec2};
44

55
/// A 2D axis event on a gamepad.
66
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
@@ -11,3 +11,52 @@ pub struct GamepadAxis2DEvent {
1111
/// The value of the axis (each component is between -1.0 and 1.0, modeled after the Web Gamepad API).
1212
pub value: Vec2<f64>,
1313
}
14+
15+
impl GamepadAxis2DEvent {
16+
/// The approximate direction (outside of a small deadzone).
17+
pub fn direction(&self) -> Option<Direction> {
18+
let deadzone_radius: f64 = 0.1;
19+
if self.value.length() < deadzone_radius {
20+
return None;
21+
}
22+
23+
// See https://www.desmos.com/calculator/472pdoxzqa for visualization
24+
// Note that the y-axis is flipped here, per computer graphics conventions,
25+
// hence the sign flip (-y instead of y).
26+
let Vec2 { x, y } = self.value;
27+
let left_or_up = x < -y;
28+
let right_or_up = -x < -y;
29+
Some(
30+
match (left_or_up, right_or_up) {
31+
(true, true) => Direction::Up,
32+
(true, false) => Direction::Left,
33+
(false, true) => Direction::Right,
34+
(false, false) => Direction::Down,
35+
}
36+
)
37+
}
38+
}
39+
40+
#[cfg(test)]
41+
mod tests {
42+
use crate::{Direction, Vec2, Zero};
43+
44+
use super::GamepadAxis2DEvent;
45+
46+
#[test]
47+
fn directions() {
48+
assert_eq!(event(Vec2::UP).direction(), Some(Direction::Up));
49+
assert_eq!(event(Vec2::DOWN).direction(), Some(Direction::Down));
50+
assert_eq!(event(Vec2::LEFT).direction(), Some(Direction::Left));
51+
assert_eq!(event(Vec2::RIGHT).direction(), Some(Direction::Right));
52+
assert_eq!(event(Vec2::ZERO).direction(), None);
53+
assert_eq!(event(Vec2::new(-0.05, 0.05)).direction(), None); // within deadzone
54+
}
55+
56+
fn event(value: Vec2<f64>) -> GamepadAxis2DEvent {
57+
GamepadAxis2DEvent {
58+
index: 0,
59+
value,
60+
}
61+
}
62+
}

lighthouse-protocol/src/input/gamepad_button_event.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use serde::{Deserialize, Serialize};
22

3+
use crate::Direction;
4+
35
/// A button event on a gamepad.
46
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
57
#[serde(tag = "control", rename_all = "camelCase")]
@@ -11,3 +13,17 @@ pub struct GamepadButtonEvent {
1113
/// The value of the button (between 0.0 and 1.0, modeled after the Web Gamepad API).
1214
pub value: f64,
1315
}
16+
17+
impl GamepadButtonEvent {
18+
/// The direction if one of the D-pad buttons was pressed.
19+
/// See https://www.w3.org/TR/gamepad/#dfn-standard-gamepad
20+
pub fn d_pad_direction(&self) -> Option<Direction> {
21+
match self.index {
22+
12 => Some(Direction::Up),
23+
13 => Some(Direction::Down),
24+
14 => Some(Direction::Left),
25+
15 => Some(Direction::Right),
26+
_ => None,
27+
}
28+
}
29+
}

lighthouse-protocol/src/input/input_event.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use serde::{Deserialize, Serialize};
22

3-
use super::{GamepadEvent, KeyEvent, MouseEvent};
3+
use crate::Direction;
4+
5+
use super::{EventSource, GamepadControlEvent, GamepadEvent, KeyEvent, MouseEvent};
46

57
/// A user input event, as generated by the new frontend (LUNA).
68
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
@@ -11,6 +13,49 @@ pub enum InputEvent {
1113
Gamepad(GamepadEvent),
1214
}
1315

16+
impl InputEvent {
17+
/// The event's source.
18+
pub fn source(&self) -> &EventSource {
19+
match self {
20+
InputEvent::Key(KeyEvent { source, .. }) => source,
21+
InputEvent::Mouse(MouseEvent { source, .. }) => source,
22+
InputEvent::Gamepad(GamepadEvent { source, .. }) => source,
23+
}
24+
}
25+
26+
/// Parses the input event as an arbitrary direction.
27+
pub fn direction(&self) -> Option<Direction> {
28+
self.left_direction().or_else(|| self.right_direction())
29+
}
30+
31+
/// The direction if the input event represents a WASD key, D-pad or left stick.
32+
/// Commonly used e.g. for movement in games.
33+
pub fn left_direction(&self) -> Option<Direction> {
34+
match self {
35+
InputEvent::Key(key) => key.wasd_direction(),
36+
InputEvent::Gamepad(gamepad) => match &gamepad.control {
37+
GamepadControlEvent::Button(button) => button.d_pad_direction(),
38+
GamepadControlEvent::Axis2D(axis2d) if axis2d.index == 0 => axis2d.direction(),
39+
_ => None,
40+
},
41+
_ => None,
42+
}
43+
}
44+
45+
/// The direction if the input event represents an arrow key or right stick.
46+
/// Commonly used e.g. for camera control in games.
47+
pub fn right_direction(&self) -> Option<Direction> {
48+
match self {
49+
InputEvent::Key(key) => key.arrow_direction(),
50+
InputEvent::Gamepad(gamepad) => match &gamepad.control {
51+
GamepadControlEvent::Axis2D(axis2d) if axis2d.index == 1 => axis2d.direction(),
52+
_ => None,
53+
},
54+
_ => None,
55+
}
56+
}
57+
}
58+
1459
#[cfg(test)]
1560
mod tests {
1661
use serde_json::json;

lighthouse-protocol/src/input/key_event.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use serde::{Deserialize, Serialize};
22

3+
use crate::Direction;
4+
35
use super::{EventSource, KeyModifiers};
46

57
/// A keyboard event.
@@ -17,3 +19,32 @@ pub struct KeyEvent {
1719
/// The held key modifiers.
1820
pub modifiers: KeyModifiers,
1921
}
22+
23+
impl KeyEvent {
24+
/// The direction if either the WASD or arrow keys were pressed.
25+
pub fn direction(&self) -> Option<Direction> {
26+
self.wasd_direction().or_else(|| self.arrow_direction())
27+
}
28+
29+
/// The direction if one of the WASD keys was pressed.
30+
pub fn wasd_direction(&self) -> Option<Direction> {
31+
match self.code.as_str() {
32+
"KeyW" => Some(Direction::Up),
33+
"KeyA" => Some(Direction::Left),
34+
"KeyS" => Some(Direction::Down),
35+
"KeyD" => Some(Direction::Right),
36+
_ => None,
37+
}
38+
}
39+
40+
/// The direction if one of the arrow keys was pressed.
41+
pub fn arrow_direction(&self) -> Option<Direction> {
42+
match self.code.as_str() {
43+
"ArrowUp" => Some(Direction::Up),
44+
"ArrowLeft" => Some(Direction::Left),
45+
"ArrowDown" => Some(Direction::Down),
46+
"ArrowRight" => Some(Direction::Right),
47+
_ => None,
48+
}
49+
}
50+
}

lighthouse-protocol/src/utils/delta.rs

Lines changed: 0 additions & 71 deletions
This file was deleted.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use std::fmt::Debug;
2+
3+
use rand::{prelude::Distribution, distributions::Standard};
4+
use serde::{Deserialize, Serialize};
5+
6+
use super::{Delta, Unity, Zero};
7+
8+
/// One of the four cardinal directions.
9+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10+
pub enum Direction {
11+
Up,
12+
Down,
13+
Left,
14+
Right,
15+
}
16+
17+
impl<T> TryFrom<Delta<T>> for Direction where T: Zero + Unity + PartialEq + Debug {
18+
type Error = String;
19+
20+
fn try_from(delta: Delta<T>) -> Result<Self, Self::Error> {
21+
if delta == Delta::UP {
22+
Ok(Direction::Up)
23+
} else if delta == Delta::DOWN {
24+
Ok(Direction::Down)
25+
} else if delta == Delta::LEFT {
26+
Ok(Direction::Left)
27+
} else if delta == Delta::RIGHT {
28+
Ok(Direction::Right)
29+
} else {
30+
Err(format!("Not a direction: {:?}", delta))
31+
}
32+
}
33+
}
34+
35+
impl<T> From<Direction> for Delta<T> where T: Zero + Unity {
36+
fn from(direction: Direction) -> Self {
37+
match direction {
38+
Direction::Up => Delta::UP,
39+
Direction::Down => Delta::DOWN,
40+
Direction::Left => Delta::LEFT,
41+
Direction::Right => Delta::RIGHT,
42+
}
43+
}
44+
}
45+
46+
impl Distribution<Direction> for Standard {
47+
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Direction {
48+
match rng.gen_range(0..4) {
49+
0 => Direction::Up,
50+
1 => Direction::Down,
51+
2 => Direction::Left,
52+
3 => Direction::Right,
53+
_ => unreachable!(),
54+
}
55+
}
56+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
mod color;
2+
mod direction;
23
mod rect;
34
mod rem_euclid;
45
mod rotation;
6+
mod sqrt;
57
mod unity;
68
mod vec2;
79
mod zero;
810

911
pub use color::*;
12+
pub use direction::*;
1013
pub use rect::*;
1114
pub use rem_euclid::*;
1215
pub use rotation::*;
16+
pub use sqrt::*;
1317
pub use unity::*;
1418
pub use vec2::*;
1519
pub use zero::*;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/// A type whose values have a square root.
2+
pub trait Sqrt {
3+
/// The square root.
4+
fn sqrt(self) -> Self;
5+
}
6+
7+
macro_rules! impl_sqrt {
8+
($($tys:ty),*) => {
9+
$(impl Sqrt for $tys {
10+
fn sqrt(self) -> Self {
11+
<$tys>::sqrt(self)
12+
}
13+
})*
14+
};
15+
}
16+
17+
impl_sqrt!(
18+
f32, f64
19+
);

lighthouse-protocol/src/utils/vec2.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
use std::{fmt, ops::{Add, AddAssign, Neg, Sub, SubAssign}};
1+
use std::{fmt, ops::{Add, AddAssign, Mul, Neg, Sub, SubAssign}};
22

33
use rand::{thread_rng, Rng};
44
use serde::{Deserialize, Serialize};
55

6-
use super::{Unity, Zero};
6+
use super::{Sqrt, Unity, Zero};
77

88
/// A 2D vector.
99
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
@@ -59,6 +59,13 @@ impl<T> Vec2<T> where T: Zero + Unity {
5959
}
6060
}
6161

62+
impl<T> Vec2<T> where T: Add<Output = T> + Mul<Output = T> + Sqrt + Copy {
63+
/// The vector's length.
64+
pub fn length(&self) -> T {
65+
(self.x * self.x + self.y * self.y).sqrt()
66+
}
67+
}
68+
6269
impl<T> fmt::Display for Vec2<T> where T: fmt::Display {
6370
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
6471
write!(f, "({}, {})", self.x, self.y)

0 commit comments

Comments
 (0)