avian3d/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 rotation is locked around the `X` axis.
188    #[cfg(feature = "3d")]
189    pub const fn is_rotation_x_locked(&self) -> bool {
190        (self.0 & 0b000_100) != 0
191    }
192
193    /// Returns true if rotation is locked around the `Y` axis.
194    #[cfg(feature = "3d")]
195    pub const fn is_rotation_y_locked(&self) -> bool {
196        (self.0 & 0b000_010) != 0
197    }
198
199    /// Returns true if rotation is locked around the `Z` axis.
200    #[cfg(feature = "3d")]
201    pub const fn is_rotation_z_locked(&self) -> bool {
202        (self.0 & 0b000_001) != 0
203    }
204
205    /// Returns true if all rotation is locked.
206    #[cfg(feature = "2d")]
207    pub const fn is_rotation_locked(&self) -> bool {
208        (self.0 & 0b000_001) != 0
209    }
210
211    /// Sets translational axes of the given vector to zero based on the [`LockedAxes`] configuration.
212    pub(crate) fn apply_to_vec(&self, mut vector: Vector) -> Vector {
213        if self.is_translation_x_locked() {
214            vector.x = 0.0;
215        }
216        if self.is_translation_y_locked() {
217            vector.y = 0.0;
218        }
219        #[cfg(feature = "3d")]
220        if self.is_translation_z_locked() {
221            vector.z = 0.0;
222        }
223        vector
224    }
225
226    /// Sets the given angular inertia to zero if rotational axes are locked.
227    #[cfg(feature = "2d")]
228    pub(crate) fn apply_to_angular_inertia(
229        &self,
230        angular_inertia: impl Into<ComputedAngularInertia>,
231    ) -> ComputedAngularInertia {
232        let mut angular_inertia = angular_inertia.into();
233
234        if self.is_rotation_locked() {
235            *angular_inertia.inverse_mut() = 0.0;
236        }
237
238        angular_inertia
239    }
240
241    /// Sets axes of the given angular inertia to zero based on the [`LockedAxes`] configuration.
242    #[cfg(feature = "3d")]
243    pub(crate) fn apply_to_angular_inertia(
244        &self,
245        angular_inertia: impl Into<ComputedAngularInertia>,
246    ) -> ComputedAngularInertia {
247        let mut angular_inertia = angular_inertia.into();
248
249        if self.is_rotation_x_locked() {
250            angular_inertia.inverse_mut().x_axis = Vector::ZERO;
251        }
252        if self.is_rotation_y_locked() {
253            angular_inertia.inverse_mut().y_axis = Vector::ZERO;
254        }
255        if self.is_rotation_z_locked() {
256            angular_inertia.inverse_mut().z_axis = Vector::ZERO;
257        }
258
259        angular_inertia
260    }
261
262    /// Sets the given angular velocity to zero if rotational axes are locked.
263    #[cfg(feature = "2d")]
264    pub(crate) fn apply_to_angular_velocity(&self, mut angular_velocity: Scalar) -> Scalar {
265        if self.is_rotation_locked() {
266            angular_velocity = 0.0;
267        }
268        angular_velocity
269    }
270
271    /// Sets axes of the given angular velocity to zero based on the [`LockedAxes`] configuration.
272    #[cfg(feature = "3d")]
273    pub(crate) fn apply_to_angular_velocity(&self, mut angular_velocity: Vector) -> Vector {
274        if self.is_rotation_x_locked() {
275            angular_velocity.x = 0.0;
276        }
277        if self.is_rotation_y_locked() {
278            angular_velocity.y = 0.0;
279        }
280        if self.is_rotation_z_locked() {
281            angular_velocity.z = 0.0;
282        }
283        angular_velocity
284    }
285}