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