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}