Skip to main content

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