bevy_trackball/controller/
input.rs

1use bevy::prelude::*;
2use trackball::Fixed;
3
4/// Trackball controller input mappings and settings.
5#[derive(Component, Debug, Clone)]
6pub struct TrackballInput {
7	/// Trackball velocity for time-based input like pressed keys.
8	pub velocity: TrackballVelocity,
9	/// Wheel unit for coherent scaling. Default is 24 clicks per turn.
10	///
11	/// Device dependent setting as mouse wheel events usually lack a reference unit.
12	pub wheel_unit: TrackballWheelUnit,
13
14	/// Enables focus operation. Default is `true`.
15	///
16	/// Whether to slide towards mouse or single-finger touch position when [`Self::orbit_button`]
17	/// is just pressed and released again or single-finger gesture is just started and ended again.
18	/// Moving the cursor/finger slightly between pressed/started and released/ended events discards
19	/// the focus operation in favor of the orbit operation.
20	pub focus: bool,
21
22	/// Key used to toggle `esdf`/`wasd` mapping. Default is [`KeyCode::KeyM`].
23	pub gamer_key: Option<KeyCode>,
24	/// Key used to toggle projection mode. Default is [`KeyCode::KeyP`].
25	pub ortho_key: Option<KeyCode>,
26
27	/// Key used to reset frame. Default is [`KeyCode::Enter`].
28	pub reset_key: Option<KeyCode>,
29
30	/// Mouse button used to look around. Default is [`MouseButton::Middle`].
31	pub first_button: Option<MouseButton>,
32	/// Key used to look around with single-finger touch. Default is [`KeyCode::ShiftLeft`].
33	pub first_key: Option<KeyCode>,
34	/// Key used to look left. Default is [`KeyCode::ArrowLeft`].
35	pub first_left_key: Option<KeyCode>,
36	/// Key used to look right. Default is [`KeyCode::ArrowRight`].
37	pub first_right_key: Option<KeyCode>,
38	/// Key used to look up. Default is [`KeyCode::ArrowUp`].
39	pub first_up_key: Option<KeyCode>,
40	/// Key used to look down. Default is [`KeyCode::ArrowDown`].
41	pub first_down_key: Option<KeyCode>,
42
43	/// Mouse button used to orbit camera. Default is [`MouseButton::Left`].
44	pub orbit_button: Option<MouseButton>,
45	/// Key used to screw/roll left. Default is [`KeyCode::KeyU`].
46	pub screw_left_key: Option<KeyCode>,
47	/// Key used to screw/roll right. Default is [`KeyCode::KeyO`].
48	pub screw_right_key: Option<KeyCode>,
49	/// Key used to orbit left. Default is [`KeyCode::KeyJ`].
50	pub orbit_left_key: Option<KeyCode>,
51	/// Key used to orbit right. Default is [`KeyCode::KeyL`].
52	pub orbit_right_key: Option<KeyCode>,
53	/// Key used to orbit up. Default is [`KeyCode::KeyI`].
54	pub orbit_up_key: Option<KeyCode>,
55	/// Key used to orbit down. Default is [`KeyCode::KeyK`].
56	pub orbit_down_key: Option<KeyCode>,
57
58	/// Mouse button used to slide camera. Default is [`MouseButton::Right`].
59	pub slide_button: Option<MouseButton>,
60	/// Key used to slide left. Default is [`KeyCode::KeyS`].
61	pub slide_left_key: Option<KeyCode>,
62	/// Key used to slide right. Default is [`KeyCode::KeyF`].
63	pub slide_right_key: Option<KeyCode>,
64	/// Key used to slide up. Default is [`KeyCode::KeyE`].
65	pub slide_up_key: Option<KeyCode>,
66	/// Key used to slide up. Default is [`KeyCode::KeyD`].
67	pub slide_down_key: Option<KeyCode>,
68	/// Key used to slide far. Default is [`KeyCode::KeyG`].
69	pub slide_far_key: Option<KeyCode>,
70	/// Key used to slide near. Default is [`KeyCode::KeyV`].
71	pub slide_near_key: Option<KeyCode>,
72
73	/// Key used to scale/zoom in. Default is [`KeyCode::KeyH`].
74	pub scale_in_key: Option<KeyCode>,
75	/// Key used to scale/zoom out. Default is [`KeyCode::KeyN`].
76	pub scale_out_key: Option<KeyCode>,
77}
78
79impl TrackballInput {
80	/// Maps `esdf`/`gv` to slide operations.
81	///
82	/// Key | Operation
83	/// --- | ---------------------------
84	/// `e` | Slides up.
85	/// `s` | Slides left.
86	/// `d` | Slides down.
87	/// `f` | Slides right.
88	/// `g` | Slides far (in/forward).
89	/// `v` | Slides near (out/backward).
90	///
91	/// This mapping is symmetric to the `ijkl`/`hn` orbit mapping but less intuitive to gamers
92	/// compared with [`Self::map_wasd`].
93	pub const fn map_esdf(&mut self) {
94		self.slide_up_key = Some(KeyCode::KeyE);
95		self.slide_down_key = Some(KeyCode::KeyD);
96		self.slide_left_key = Some(KeyCode::KeyS);
97		self.slide_right_key = Some(KeyCode::KeyF);
98		self.slide_far_key = Some(KeyCode::KeyG);
99		self.slide_near_key = Some(KeyCode::KeyV);
100	}
101	/// Maps `wasd`/`Space`/`ControlLeft` to slide operations.
102	///
103	/// Key           | Operation
104	/// ------------- | ---------------------------
105	/// `w`           | Slides far (in/forward).
106	/// `a`           | Slides left.
107	/// `s`           | Slides near (out/backward).
108	/// `d`           | Slides right.
109	/// `Space`       | Slides up (jump).
110	/// `ControlLeft` | Slides down (crouch).
111	///
112	/// This mapping isn't symmetric to the `ijkl`/`hn` orbit mapping but more intuitive to gamers
113	/// compared with [`Self::map_esdf`].
114	pub const fn map_wasd(&mut self) {
115		self.slide_up_key = Some(KeyCode::Space);
116		self.slide_down_key = Some(KeyCode::ControlLeft);
117		self.slide_left_key = Some(KeyCode::KeyA);
118		self.slide_right_key = Some(KeyCode::KeyD);
119		self.slide_far_key = Some(KeyCode::KeyW);
120		self.slide_near_key = Some(KeyCode::KeyS);
121	}
122}
123
124impl Default for TrackballInput {
125	fn default() -> Self {
126		Self {
127			velocity: TrackballVelocity::default(),
128			wheel_unit: TrackballWheelUnit::default(),
129
130			focus: true,
131
132			gamer_key: Some(KeyCode::KeyM),
133
134			ortho_key: Some(KeyCode::KeyP),
135
136			reset_key: Some(KeyCode::Enter),
137
138			first_key: Some(KeyCode::ShiftLeft),
139			first_button: Some(MouseButton::Middle),
140			first_left_key: Some(KeyCode::ArrowLeft),
141			first_right_key: Some(KeyCode::ArrowRight),
142			first_up_key: Some(KeyCode::ArrowUp),
143			first_down_key: Some(KeyCode::ArrowDown),
144
145			orbit_button: Some(MouseButton::Left),
146			screw_left_key: Some(KeyCode::KeyU),
147			screw_right_key: Some(KeyCode::KeyO),
148			orbit_left_key: Some(KeyCode::KeyJ),
149			orbit_right_key: Some(KeyCode::KeyL),
150			orbit_up_key: Some(KeyCode::KeyI),
151			orbit_down_key: Some(KeyCode::KeyK),
152
153			slide_button: Some(MouseButton::Right),
154			slide_up_key: Some(KeyCode::KeyE),
155			slide_down_key: Some(KeyCode::KeyD),
156			slide_left_key: Some(KeyCode::KeyS),
157			slide_right_key: Some(KeyCode::KeyF),
158			slide_far_key: Some(KeyCode::KeyG),
159			slide_near_key: Some(KeyCode::KeyV),
160
161			scale_in_key: Some(KeyCode::KeyH),
162			scale_out_key: Some(KeyCode::KeyN),
163		}
164	}
165}
166
167/// [`TrackballInput`] setting translating between linear and angular velocity.
168#[derive(Debug, Clone, Copy)]
169pub enum TrackballVelocity {
170	/// Linear velocity.
171	Linear(f32),
172	/// Angular velocity.
173	Angular(f32),
174}
175
176impl TrackballVelocity {
177	/// Converts to angular velocity where `r` is the radius.
178	#[must_use]
179	pub fn to_angular(self, r: f32) -> Self {
180		match self {
181			Self::Angular(w) => Self::Angular(w),
182			Self::Linear(v) => Self::Angular(v / r),
183		}
184	}
185	/// Converts to linear velocity where `r` is the radius.
186	#[must_use]
187	pub fn to_linear(self, r: f32) -> Self {
188		match self {
189			Self::Angular(w) => Self::Linear(w * r),
190			Self::Linear(v) => Self::Linear(v),
191		}
192	}
193	/// Underlying quantity.
194	#[must_use]
195	pub const fn into_inner(self) -> f32 {
196		match self {
197			Self::Angular(w) => w,
198			Self::Linear(v) => v,
199		}
200	}
201}
202
203impl Default for TrackballVelocity {
204	/// Angular velocity of 45 degrees per second.
205	///
206	/// That is the default fixed vertical field of view per second.
207	fn default() -> Self {
208		Self::Angular(Fixed::default().into_inner())
209	}
210}
211
212/// [`TrackballInput`] setting translating wheel units in coherent scale denominators.
213#[derive(Debug, Clone, Copy)]
214#[non_exhaustive]
215pub enum TrackballWheelUnit {
216	/// Wheel clicks per turn (cpt).
217	///
218	/// Defaults to 24 cpt, that are 15°=360°/24 per click which agrees with most devices. Some
219	/// devices have 18 cpt instead, that are 20°=360/18 per click.
220	///
221	/// You can count the cpt of your device by marking the start of your wheel before rotating it a
222	/// full turn. Each noticeable step when rotating is called a wheel click, not to be confused
223	/// with a middle mouse button click.
224	Cpt(f32),
225	/// Wheel clicks per second (cps).
226	///
227	/// Some devices scroll smooth without noticeable steps when rotating. They specify their units
228	/// in cps (e.g., 1 000 cps). This unit will be translated to the trackball's angular velocity.
229	Cps(f32),
230}
231
232impl TrackballWheelUnit {
233	/// Coherent [`Scale`] denominator.
234	///
235	/// [`Scale`]: trackball::Scale
236	#[must_use]
237	pub fn denominator(self, w: f32) -> f32 {
238		match self {
239			Self::Cpt(cpt) => cpt,
240			Self::Cps(cps) => cps / w,
241		}
242	}
243}
244
245impl Default for TrackballWheelUnit {
246	/// 24 cpt, that are 15°=360°/24 per click.
247	fn default() -> Self {
248		Self::Cpt(24.0)
249	}
250}