bevy_trackball/controller/input.rs
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
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,
/// 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 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 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(),
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)
}
}