lotus_shared/axis.rs
1//! Logische Controller-Achsen und Force Feedback, getrennt von Tastenaktionen.
2//!
3//! Logical controller axes and force feedback, separate from button actions.
4
5use serde::{Deserialize, Serialize};
6
7/// Achsenwert ohne Belegung und Ruhelage eines zentrierten Gebers.
8///
9/// Axis value without a binding, and the rest position of a centered control.
10pub const AXIS_CENTER: f32 = 0.5;
11
12/// Beschreibt eine Achse, die bei der Engine registriert werden kann.
13///
14/// Describes an axis that can be registered with the engine.
15#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
16pub struct RegisterAxis {
17 /// Eindeutige Achsen-ID für Scripts und die Belegung.
18 ///
19 /// Unique axis identifier used by scripts and bindings.
20 pub id: String,
21}
22
23impl RegisterAxis {
24 /// Erstellt einen neuen Registrierungseintrag für eine Achse.
25 ///
26 /// Creates a new axis registration entry.
27 pub fn new(id: impl Into<String>) -> Self {
28 Self { id: id.into() }
29 }
30}
31
32/// Rückstellkraft einer Achse.
33///
34/// Restoring force of an axis.
35///
36/// `coefficient` und `saturation` sind unbeschränkte endliche Stärken.
37/// Der Nennbereich ist `0..=1`. Die Engine multipliziert mit der Geräte-Intensität
38/// und klemmt erst danach auf `0..=1`.
39/// `offset` verschiebt die Federmitte und wird danach auf `-1..=1` geklemmt, ohne Skalierung.
40/// `0` lässt die Federmitte unverändert.
41///
42/// `coefficient` and `saturation` are unrestricted finite strengths.
43/// The nominal range is `0..=1`. The engine multiplies by the device intensity
44/// and only then clamps to `0..=1`.
45/// `offset` shifts the spring center and is then clamped to `-1..=1`, without scaling.
46/// `0` leaves the spring center unchanged.
47#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
48pub struct SpringForce {
49 /// Steifigkeit der Feder.
50 ///
51 /// Spring stiffness.
52 pub coefficient: f32,
53 /// Obere Grenze der Kraft.
54 ///
55 /// Upper bound of the force.
56 pub saturation: f32,
57 /// Verschiebung der Federmitte.
58 ///
59 /// Shift of the spring center.
60 pub offset: f32,
61}
62
63/// Schwingung entlang einer Achse.
64///
65/// Oscillation along an axis.
66#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
67pub struct AxisVibration {
68 /// Stärke als unbeschränkter endlicher Wert. `0` schaltet die Schwingung aus.
69 /// Die Engine multipliziert mit der Geräte-Intensität und klemmt erst danach auf `0..=1`.
70 ///
71 /// Strength as an unrestricted finite value. `0` turns the oscillation off.
72 /// The engine multiplies by the device intensity and only then clamps to `0..=1`.
73 pub magnitude: f32,
74 /// Frequenz in Hertz.
75 ///
76 /// Frequency in hertz.
77 pub frequency_hz: f32,
78}
79
80/// Zuletzt gewünschtes Force Feedback einer logischen Achse.
81///
82/// Latest requested force feedback for one logical axis.
83#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
84pub struct AxisForceFeedback {
85 /// Feder. `None`, solange das Script noch keine gesetzt hat.
86 ///
87 /// Spring. `None` until the script sets one.
88 pub spring: Option<SpringForce>,
89 /// Reibung, `0` heißt aus.
90 ///
91 /// Friction; `0` means off.
92 pub friction: f32,
93 /// Schwingung. `None` heißt aus.
94 ///
95 /// Oscillation. `None` means off.
96 pub vibration: Option<AxisVibration>,
97}
98
99/// Begrenzt einen endlichen Wert auf `0..=1`. Nicht endliche Werte werden 0.
100///
101/// Clamps a finite value to `0..=1`. Non-finite values become 0.
102pub fn clamp_unit(value: f32) -> f32 {
103 if value.is_finite() {
104 value.clamp(0.0, 1.0)
105 } else {
106 0.0
107 }
108}
109
110/// Begrenzt einen endlichen Wert auf `-1..=1`. Nicht endliche Werte werden 0.
111///
112/// Clamps a finite value to `-1..=1`. Non-finite values become 0.
113pub fn clamp_signed_unit(value: f32) -> f32 {
114 if value.is_finite() {
115 value.clamp(-1.0, 1.0)
116 } else {
117 0.0
118 }
119}
120
121/// Begrenzt eine Frequenz auf `>= 0`. Nicht endliche Werte werden 0.
122///
123/// Clamps a frequency to `>= 0`. Non-finite values become 0.
124pub fn clamp_frequency_hz(value: f32) -> f32 {
125 if value.is_finite() {
126 value.max(0.0)
127 } else {
128 0.0
129 }
130}