avian2d/dynamics/rigid_body/
locked_axes.rs

1use bevy::prelude::*;
2use derive_more::From;
3
4use crate::prelude::*;
5
6/// A component that specifies which translational and rotational axes of a [rigid body](RigidBody) are locked.
7///
8/// The axes are represented using a total of six bits, one for each axis. The easiest way to lock or unlock
9/// specific axes is to use methods like [`lock_translation_x`](Self::lock_translation_x), but you can also
10/// use bits directly with the [`from_bits`](Self::from_bits) and [`to_bits`](Self::to_bits) methods.
11///
12/// # Example
13///
14/// ```
15#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
16#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
17/// use bevy::prelude::*;
18///
19/// fn spawn(mut commands: Commands) {
20///     commands.spawn((
21///         RigidBody::Dynamic,
22///         Collider::capsule(0.5, 1.0),
23#[cfg_attr(feature = "2d", doc = "        LockedAxes::ROTATION_LOCKED,")]
24#[cfg_attr(feature = "3d", doc = "        LockedAxes::new().lock_rotation_z(),")]
25///     ));
26/// }
27/// ```
28#[derive(Component, Reflect, Clone, Copy, Debug, Default, From)]
29#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
30#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
31#[reflect(Debug, Component, Default)]
32pub struct LockedAxes(u8);
33
34impl LockedAxes {
35    /// All translational axes are locked, but all rotational axes are unlocked.
36    pub const TRANSLATION_LOCKED: Self = Self(0b111_000);
37    /// All rotational axes are locked, but all translational axes are unlocked.
38    pub const ROTATION_LOCKED: Self = Self(0b000_111);
39    /// All translational and rotational axes are locked.
40    pub const ALL_LOCKED: Self = Self(0b111_111);
41
42    /// Creates a new [`LockedAxes`] configuration with all axes unlocked by default.
43    pub const fn new() -> Self {
44        Self(0)
45    }
46
47    /// Creates a new [`LockedAxes`] configuration using bits.
48    ///
49    /// The first three bits correspond to translational axes, while the last three bits correspond to rotational
50    /// axes. For example, `0b100_010` would lock translation along the `X` axis and rotation around the `Y` axis.
51    pub const fn from_bits(bits: u8) -> Self {
52        Self(bits)
53    }
54
55    /// Returns the locked axes as bits.
56    ///
57    /// The first three bits correspond to translational axes, while the last three bits correspond to rotational
58    /// axes. For example, `0b100_010` would mean that translation along the `X` axis and rotation around the `Y` axis
59    /// are locked.
60    pub const fn to_bits(&self) -> u8 {
61        self.0
62    }
63
64    /// Locks translation along the `X` axis.
65    #[must_use]
66    pub const fn lock_translation_x(mut self) -> Self {
67        self.0 |= 0b100_000;
68        self
69    }
70
71    /// Locks translation along the `Y` axis.
72    #[must_use]
73    pub const fn lock_translation_y(mut self) -> Self {
74        self.0 |= 0b010_000;
75        self
76    }
77
78    /// Locks translation along the `Z` axis.
79    #[cfg(feature = "3d")]
80    #[must_use]
81    pub const fn lock_translation_z(mut self) -> Self {
82        self.0 |= 0b001_000;
83        self
84    }
85
86    /// Locks rotation around the `X` axis.
87    #[cfg(feature = "3d")]
88    pub const fn lock_rotation_x(mut self) -> Self {
89        self.0 |= 0b000_100;
90        self
91    }
92
93    /// Locks rotation around the `Y` axis.
94    #[cfg(feature = "3d")]
95    #[must_use]
96    pub const fn lock_rotation_y(mut self) -> Self {
97        self.0 |= 0b000_010;
98        self
99    }
100
101    /// Locks rotation around the `Z` axis.
102    #[cfg(feature = "3d")]
103    #[must_use]
104    pub const fn lock_rotation_z(mut self) -> Self {
105        self.0 |= 0b000_001;
106        self
107    }
108
109    /// Locks all rotation.
110    #[cfg(feature = "2d")]
111    #[must_use]
112    pub const fn lock_rotation(mut self) -> Self {
113        self.0 |= 0b000_001;
114        self
115    }
116
117    /// Unlocks translation along the `X` axis.
118    #[must_use]
119    pub const fn unlock_translation_x(mut self) -> Self {
120        self.0 &= !0b100_000;
121        self
122    }
123
124    /// Unlocks translation along the `Y` axis.
125    #[must_use]
126    pub const fn unlock_translation_y(mut self) -> Self {
127        self.0 &= !0b010_000;
128        self
129    }
130
131    /// Unlocks translation along the `Z` axis.
132    #[cfg(feature = "3d")]
133    #[must_use]
134    pub const fn unlock_translation_z(mut self) -> Self {
135        self.0 &= !0b001_000;
136        self
137    }
138
139    /// Unlocks rotation around the `X` axis.
140    #[cfg(feature = "3d")]
141    #[must_use]
142    pub const fn unlock_rotation_x(mut self) -> Self {
143        self.0 &= !0b000_100;
144        self
145    }
146
147    /// Unlocks rotation around the `Y` axis.
148    #[cfg(feature = "3d")]
149    #[must_use]
150    pub const fn unlock_rotation_y(mut self) -> Self {
151        self.0 &= !0b000_010;
152        self
153    }
154
155    /// Unlocks rotation around the `Z` axis.
156    #[cfg(feature = "3d")]
157    #[must_use]
158    pub const fn unlock_rotation_z(mut self) -> Self {
159        self.0 &= !0b000_001;
160        self
161    }
162
163    /// Unlocks all rotation.
164    #[cfg(feature = "2d")]
165    #[must_use]
166    pub const fn unlock_rotation(mut self) -> Self {
167        self.0 &= !0b000_001;
168        self
169    }
170
171    /// Returns true if translation is locked along the `X` axis.
172    pub const fn is_translation_x_locked(&self) -> bool {
173        (self.0 & 0b100_000) != 0
174    }
175
176    /// Returns true if translation is locked along the `X` axis.
177    pub const fn is_translation_y_locked(&self) -> bool {
178        (self.0 & 0b010_000) != 0
179    }
180
181    /// Returns true if translation is locked along the `X` axis.
182    #[cfg(feature = "3d")]
183    pub const fn is_translation_z_locked(&self) -> bool {
184        (self.0 & 0b001_000) != 0
185    }
186
187    /// Returns true if all translation is locked.
188    #[cfg(feature = "2d")]
189    pub const fn is_translation_locked(&self) -> bool {
190        (self.0 & 0b110_000) == 0b110_000
191    }
192
193    /// Returns true if all translation is locked.
194    #[cfg(feature = "3d")]
195    pub const fn is_translation_locked(&self) -> bool {
196        (self.0 & 0b111_000) == 0b111_000
197    }
198
199    /// Returns true if rotation is locked around the `X` axis.
200    #[cfg(feature = "3d")]
201    pub const fn is_rotation_x_locked(&self) -> bool {
202        (self.0 & 0b000_100) != 0
203    }
204
205    /// Returns true if rotation is locked around the `Y` axis.
206    #[cfg(feature = "3d")]
207    pub const fn is_rotation_y_locked(&self) -> bool {
208        (self.0 & 0b000_010) != 0
209    }
210
211    /// Returns true if rotation is locked around the `Z` axis.
212    #[cfg(feature = "3d")]
213    pub const fn is_rotation_z_locked(&self) -> bool {
214        (self.0 & 0b000_001) != 0
215    }
216
217    /// Returns true if all rotation is locked.
218    #[cfg(feature = "2d")]
219    pub const fn is_rotation_locked(&self) -> bool {
220        (self.0 & 0b000_001) != 0
221    }
222
223    /// Returns true if all rotation is locked.
224    #[cfg(feature = "3d")]
225    pub const fn is_rotation_locked(&self) -> bool {
226        (self.0 & 0b000_111) == 0b000_111
227    }
228
229    /// Sets translational axes of the given vector to zero based on the [`LockedAxes`] configuration.
230    pub(crate) fn apply_to_vec(&self, mut vector: Vector) -> Vector {
231        if self.is_translation_x_locked() {
232            vector.x = 0.0;
233        }
234        if self.is_translation_y_locked() {
235            vector.y = 0.0;
236        }
237        #[cfg(feature = "3d")]
238        if self.is_translation_z_locked() {
239            vector.z = 0.0;
240        }
241        vector
242    }
243
244    /// Sets the given angular inertia to zero if rotational axes are locked.
245    #[cfg(feature = "2d")]
246    pub(crate) fn apply_to_angular_inertia(
247        &self,
248        angular_inertia: impl Into<ComputedAngularInertia>,
249    ) -> ComputedAngularInertia {
250        let mut angular_inertia = angular_inertia.into();
251        let angular_inertia_mut = angular_inertia.inverse_mut();
252        if self.is_rotation_locked() {
253            *angular_inertia_mut = 0.0;
254        }
255        angular_inertia
256    }
257
258    /// Sets axes of the given angular inertia to zero based on the [`LockedAxes`] configuration.
259    #[cfg(feature = "3d")]
260    pub(crate) fn apply_to_angular_inertia(
261        &self,
262        angular_inertia: impl Into<ComputedAngularInertia>,
263    ) -> ComputedAngularInertia {
264        let mut angular_inertia = angular_inertia.into();
265        let angular_inertia_mut = angular_inertia.inverse_mut();
266        if self.is_rotation_x_locked() {
267            angular_inertia_mut.m00 = 0.0;
268            angular_inertia_mut.m01 = 0.0;
269            angular_inertia_mut.m02 = 0.0;
270        }
271        if self.is_rotation_y_locked() {
272            angular_inertia_mut.m11 = 0.0;
273            angular_inertia_mut.m01 = 0.0;
274            angular_inertia_mut.m12 = 0.0;
275        }
276        if self.is_rotation_z_locked() {
277            angular_inertia_mut.m22 = 0.0;
278            angular_inertia_mut.m02 = 0.0;
279            angular_inertia_mut.m12 = 0.0;
280        }
281        angular_inertia
282    }
283
284    /// Sets the given angular velocity to zero if rotational axes are locked.
285    #[cfg(feature = "2d")]
286    pub(crate) fn apply_to_angular_velocity(&self, mut angular_velocity: Scalar) -> Scalar {
287        if self.is_rotation_locked() {
288            angular_velocity = 0.0;
289        }
290        angular_velocity
291    }
292
293    /// Sets axes of the given angular velocity to zero based on the [`LockedAxes`] configuration.
294    #[cfg(feature = "3d")]
295    pub(crate) fn apply_to_angular_velocity(&self, mut angular_velocity: Vector) -> Vector {
296        if self.is_rotation_x_locked() {
297            angular_velocity.x = 0.0;
298        }
299        if self.is_rotation_y_locked() {
300            angular_velocity.y = 0.0;
301        }
302        if self.is_rotation_z_locked() {
303            angular_velocity.z = 0.0;
304        }
305        angular_velocity
306    }
307}