bevy_rapier3d/dynamics/
revolute_joint.rs

1use crate::dynamics::{GenericJoint, GenericJointBuilder};
2use crate::math::{Real, Vect};
3use crate::plugin::context::RapierRigidBodySet;
4use bevy::prelude::Entity;
5use rapier::dynamics::{
6    JointAxesMask, JointAxis, JointLimits, JointMotor, MotorModel, RigidBodyHandle, RigidBodySet,
7};
8
9#[cfg(doc)]
10use crate::prelude::RapierContext;
11
12use super::TypedJoint;
13
14#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
15#[derive(Copy, Clone, Debug, PartialEq)]
16#[repr(transparent)]
17/// A revolute joint, locks all relative motion except for rotation along the joint’s principal axis.
18pub struct RevoluteJoint {
19    /// The underlying joint data.
20    pub data: GenericJoint,
21}
22
23#[cfg(feature = "dim2")]
24impl Default for RevoluteJoint {
25    fn default() -> Self {
26        Self::new()
27    }
28}
29
30impl RevoluteJoint {
31    /// Creates a new revolute joint allowing only relative rotations.
32    #[cfg(feature = "dim2")]
33    pub fn new() -> Self {
34        let data = GenericJointBuilder::new(JointAxesMask::LOCKED_REVOLUTE_AXES);
35        Self { data: data.build() }
36    }
37
38    /// Creates a new revolute joint allowing only relative rotations along the specified axis.
39    ///
40    /// This axis is expressed in the local-space of both rigid-bodies.
41    #[cfg(feature = "dim3")]
42    pub fn new(axis: Vect) -> Self {
43        let data = GenericJointBuilder::new(JointAxesMask::LOCKED_REVOLUTE_AXES)
44            .local_axis1(axis)
45            .local_axis2(axis)
46            .build();
47        Self { data }
48    }
49
50    /// Are contacts between the attached rigid-bodies enabled?
51    pub fn contacts_enabled(&self) -> bool {
52        self.data.contacts_enabled()
53    }
54
55    /// Sets whether contacts between the attached rigid-bodies are enabled.
56    pub fn set_contacts_enabled(&mut self, enabled: bool) -> &mut Self {
57        self.data.set_contacts_enabled(enabled);
58        self
59    }
60
61    /// The joint’s anchor, expressed in the local-space of the first rigid-body.
62    #[must_use]
63    pub fn local_anchor1(&self) -> Vect {
64        self.data.local_anchor1()
65    }
66
67    /// Sets the joint’s anchor, expressed in the local-space of the first rigid-body.
68    pub fn set_local_anchor1(&mut self, anchor1: Vect) -> &mut Self {
69        self.data.set_local_anchor1(anchor1);
70        self
71    }
72
73    /// The joint’s anchor, expressed in the local-space of the second rigid-body.
74    #[must_use]
75    pub fn local_anchor2(&self) -> Vect {
76        self.data.local_anchor2()
77    }
78
79    /// Sets the joint’s anchor, expressed in the local-space of the second rigid-body.
80    pub fn set_local_anchor2(&mut self, anchor2: Vect) -> &mut Self {
81        self.data.set_local_anchor2(anchor2);
82        self
83    }
84
85    /// Sets the joint's rotation axis in the local-space of the first rigid-body
86    pub fn set_local_axis1(&mut self, axis1: Vect) -> &mut Self {
87        self.data.set_local_axis1(axis1);
88        self
89    }
90
91    /// Sets the joint's rotation axis in the local-space of the second rigid-body
92    pub fn set_local_axis2(&mut self, axis2: Vect) -> &mut Self {
93        self.data.set_local_axis2(axis2);
94        self
95    }
96
97    /// The motor affecting the joint’s rotational degree of freedom.
98    #[must_use]
99    pub fn motor(&self) -> Option<&JointMotor> {
100        self.data.motor(JointAxis::AngX)
101    }
102
103    /// Set the spring-like model used by the motor to reach the desired target velocity and position.
104    pub fn set_motor_model(&mut self, model: MotorModel) -> &mut Self {
105        self.data.set_motor_model(JointAxis::AngX, model);
106        self
107    }
108
109    /// Sets the target velocity this motor needs to reach.
110    pub fn set_motor_velocity(&mut self, target_vel: Real, factor: Real) -> &mut Self {
111        self.data
112            .set_motor_velocity(JointAxis::AngX, target_vel, factor);
113        self
114    }
115
116    /// Sets the target angle this motor needs to reach.
117    pub fn set_motor_position(
118        &mut self,
119        target_pos: Real,
120        stiffness: Real,
121        damping: Real,
122    ) -> &mut Self {
123        self.data
124            .set_motor_position(JointAxis::AngX, target_pos, stiffness, damping);
125        self
126    }
127
128    /// Configure both the target angle and target velocity of the motor.
129    pub fn set_motor(
130        &mut self,
131        target_pos: Real,
132        target_vel: Real,
133        stiffness: Real,
134        damping: Real,
135    ) -> &mut Self {
136        self.data
137            .set_motor(JointAxis::AngX, target_pos, target_vel, stiffness, damping);
138        self
139    }
140
141    /// Sets the maximum force the motor can deliver.
142    pub fn set_motor_max_force(&mut self, max_force: Real) -> &mut Self {
143        self.data.set_motor_max_force(JointAxis::AngX, max_force);
144        self
145    }
146
147    /// The limit angle attached bodies can translate along the joint’s principal axis.
148    #[must_use]
149    pub fn limits(&self) -> Option<&JointLimits<Real>> {
150        self.data.limits(JointAxis::AngX)
151    }
152
153    /// Sets the `[min,max]` limit angle attached bodies can translate along the joint’s principal axis.
154    pub fn set_limits(&mut self, limits: [Real; 2]) -> &mut Self {
155        self.data.set_limits(JointAxis::AngX, limits);
156        self
157    }
158
159    /// The angle along the free degree of freedom of this revolute joint in `[-π, π]`.
160    ///
161    /// See also [`Self::angle`] for a version of this method taking entities instead of rigid-body handles.
162    /// Similarly [`RapierContext::impulse_revolute_joint_angle`] only takes a single entity as argument to compute that angle.
163    ///
164    /// # Parameters
165    /// - `bodies` : the rigid body set from [`RapierRigidBodySet`]
166    /// - `body1`: the first rigid-body attached to this revolute joint, obtained through [`rapier::dynamics::ImpulseJoint`] or [`rapier::dynamics::MultibodyJoint`].
167    /// - `body2`: the second rigid-body attached to this revolute joint, obtained through [`rapier::dynamics::ImpulseJoint`] or [`rapier::dynamics::MultibodyJoint`].
168    pub fn angle_from_handles(
169        &self,
170        bodies: &RigidBodySet,
171        body1: RigidBodyHandle,
172        body2: RigidBodyHandle,
173    ) -> f32 {
174        // NOTE: unwrap will always succeed since `Self` is known to be a revolute joint.
175        let joint = self.data.raw.as_revolute().unwrap();
176
177        let rb1 = &bodies[body1];
178        let rb2 = &bodies[body2];
179        joint.angle(rb1.rotation(), rb2.rotation())
180    }
181
182    /// The angle along the free degree of freedom of this revolute joint in `[-π, π]`.
183    ///
184    /// # Parameters
185    /// - `bodies` : the rigid body set from [`RapierRigidBodySet`]
186    /// - `body1`: the first rigid-body attached to this revolute joint.
187    /// - `body2`: the second rigid-body attached to this revolute joint.
188    pub fn angle(&self, rigidbody_set: &RapierRigidBodySet, body1: Entity, body2: Entity) -> f32 {
189        let rb1 = rigidbody_set.entity2body().get(&body1).unwrap();
190        let rb2 = rigidbody_set.entity2body().get(&body2).unwrap();
191        self.angle_from_handles(&rigidbody_set.bodies, *rb1, *rb2)
192    }
193}
194
195impl From<RevoluteJoint> for GenericJoint {
196    fn from(joint: RevoluteJoint) -> GenericJoint {
197        joint.data
198    }
199}
200
201/// Create revolute joints using the builder pattern.
202///
203/// A revolute joint locks all relative motion except for rotations along the joint’s principal axis.
204#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
205#[derive(Copy, Clone, Debug, PartialEq)]
206pub struct RevoluteJointBuilder(RevoluteJoint);
207
208#[cfg(feature = "dim2")]
209impl Default for RevoluteJointBuilder {
210    fn default() -> Self {
211        Self::new()
212    }
213}
214
215impl RevoluteJointBuilder {
216    /// Creates a new revolute joint builder.
217    #[cfg(feature = "dim2")]
218    pub fn new() -> Self {
219        Self(RevoluteJoint::new())
220    }
221
222    /// Creates a new revolute joint builder, allowing only relative rotations along the specified axis.
223    ///
224    /// This axis is expressed in the local-space of both rigid-bodies.
225    #[cfg(feature = "dim3")]
226    pub fn new(axis: Vect) -> Self {
227        Self(RevoluteJoint::new(axis))
228    }
229
230    /// Sets the joint’s anchor, expressed in the local-space of the first rigid-body.
231    #[must_use]
232    pub fn local_anchor1(mut self, anchor1: Vect) -> Self {
233        self.0.set_local_anchor1(anchor1);
234        self
235    }
236
237    /// Sets the joint’s anchor, expressed in the local-space of the second rigid-body.
238    #[must_use]
239    pub fn local_anchor2(mut self, anchor2: Vect) -> Self {
240        self.0.set_local_anchor2(anchor2);
241        self
242    }
243
244    /// Sets the joint's axis, expressed in the local-space of the first rigid-body
245    pub fn local_axis1(mut self, axis1: Vect) -> Self {
246        self.0.set_local_axis1(axis1);
247        self
248    }
249
250    /// Sets the joint's axis, expressed in the local-space of the second rigid-body
251    pub fn local_axis2(mut self, axis2: Vect) -> Self {
252        self.0.set_local_axis2(axis2);
253        self
254    }
255
256    /// Set the spring-like model used by the motor to reach the desired target velocity and position.
257    #[must_use]
258    pub fn motor_model(mut self, model: MotorModel) -> Self {
259        self.0.set_motor_model(model);
260        self
261    }
262
263    /// Sets the target velocity this motor needs to reach.
264    #[must_use]
265    pub fn motor_velocity(mut self, target_vel: Real, factor: Real) -> Self {
266        self.0.set_motor_velocity(target_vel, factor);
267        self
268    }
269
270    /// Sets the target angle this motor needs to reach.
271    #[must_use]
272    pub fn motor_position(mut self, target_pos: Real, stiffness: Real, damping: Real) -> Self {
273        self.0.set_motor_position(target_pos, stiffness, damping);
274        self
275    }
276
277    /// Configure both the target angle and target velocity of the motor.
278    #[must_use]
279    pub fn motor(
280        mut self,
281        target_pos: Real,
282        target_vel: Real,
283        stiffness: Real,
284        damping: Real,
285    ) -> Self {
286        self.0.set_motor(target_pos, target_vel, stiffness, damping);
287        self
288    }
289
290    /// Sets the maximum force the motor can deliver.
291    #[must_use]
292    pub fn motor_max_force(mut self, max_force: Real) -> Self {
293        self.0.set_motor_max_force(max_force);
294        self
295    }
296
297    /// Sets the `[min,max]` limit angles attached bodies can rotate along the joint’s principal axis.
298    #[must_use]
299    pub fn limits(mut self, limits: [Real; 2]) -> Self {
300        self.0.set_limits(limits);
301        self
302    }
303
304    /// Builds the revolute joint.
305    #[must_use]
306    pub fn build(self) -> RevoluteJoint {
307        self.0
308    }
309}
310
311impl From<RevoluteJointBuilder> for TypedJoint {
312    fn from(joint: RevoluteJointBuilder) -> TypedJoint {
313        joint.0.into()
314    }
315}
316
317impl From<RevoluteJoint> for TypedJoint {
318    fn from(joint: RevoluteJoint) -> TypedJoint {
319        TypedJoint::RevoluteJoint(joint)
320    }
321}