rapier3d/dynamics/joint/
generic_joint.rs

1#![allow(clippy::bad_bit_mask)] // Clippy will complain about the bitmasks due to JointAxesMask::FREE_FIXED_AXES being 0.
2#![allow(clippy::unnecessary_cast)] // Casts are needed for switching between f32/f64.
3
4use crate::dynamics::integration_parameters::SpringCoefficients;
5use crate::dynamics::solver::MotorParameters;
6use crate::dynamics::{
7    FixedJoint, MotorModel, PrismaticJoint, RevoluteJoint, RigidBody, RopeJoint,
8};
9use crate::math::{Isometry, Point, Real, Rotation, SPATIAL_DIM, UnitVector, Vector};
10use crate::utils::{SimdBasis, SimdRealCopy};
11
12#[cfg(feature = "dim3")]
13use crate::dynamics::SphericalJoint;
14
15#[cfg(feature = "dim3")]
16bitflags::bitflags! {
17    /// A bit mask identifying multiple degrees of freedom of a joint.
18    #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
19    #[derive(Copy, Clone, PartialEq, Eq, Debug)]
20    pub struct JointAxesMask: u8 {
21        /// The linear (translational) degree of freedom along the local X axis of a joint.
22        const LIN_X = 1 << 0;
23        /// The linear (translational) degree of freedom along the local Y axis of a joint.
24        const LIN_Y = 1 << 1;
25        /// The linear (translational) degree of freedom along the local Z axis of a joint.
26        const LIN_Z = 1 << 2;
27        /// The angular degree of freedom along the local X axis of a joint.
28        const ANG_X = 1 << 3;
29        /// The angular degree of freedom along the local Y axis of a joint.
30        const ANG_Y = 1 << 4;
31        /// The angular degree of freedom along the local Z axis of a joint.
32        const ANG_Z = 1 << 5;
33        /// The set of degrees of freedom locked by a revolute joint.
34        const LOCKED_REVOLUTE_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits() | Self::LIN_Z.bits() | Self::ANG_Y.bits() | Self::ANG_Z.bits();
35        /// The set of degrees of freedom locked by a prismatic joint.
36        const LOCKED_PRISMATIC_AXES = Self::LIN_Y.bits() | Self::LIN_Z.bits() | Self::ANG_X.bits() | Self::ANG_Y.bits() | Self::ANG_Z.bits();
37        /// The set of degrees of freedom locked by a fixed joint.
38        const LOCKED_FIXED_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits() | Self::LIN_Z.bits() | Self::ANG_X.bits() | Self::ANG_Y.bits() | Self::ANG_Z.bits();
39        /// The set of degrees of freedom locked by a spherical joint.
40        const LOCKED_SPHERICAL_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits() | Self::LIN_Z.bits();
41        /// The set of degrees of freedom left free by a revolute joint.
42        const FREE_REVOLUTE_AXES = Self::ANG_X.bits();
43        /// The set of degrees of freedom left free by a prismatic joint.
44        const FREE_PRISMATIC_AXES = Self::LIN_X.bits();
45        /// The set of degrees of freedom left free by a fixed joint.
46        const FREE_FIXED_AXES = 0;
47        /// The set of degrees of freedom left free by a spherical joint.
48        const FREE_SPHERICAL_AXES = Self::ANG_X.bits() | Self::ANG_Y.bits() | Self::ANG_Z.bits();
49        /// The set of all translational degrees of freedom.
50        const LIN_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits() | Self::LIN_Z.bits();
51        /// The set of all angular degrees of freedom.
52        const ANG_AXES = Self::ANG_X.bits() | Self::ANG_Y.bits() | Self::ANG_Z.bits();
53    }
54}
55
56#[cfg(feature = "dim2")]
57bitflags::bitflags! {
58    /// A bit mask identifying multiple degrees of freedom of a joint.
59    #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
60    #[derive(Copy, Clone, PartialEq, Eq, Debug)]
61    pub struct JointAxesMask: u8 {
62        /// The linear (translational) degree of freedom along the local X axis of a joint.
63        const LIN_X = 1 << 0;
64        /// The linear (translational) degree of freedom along the local Y axis of a joint.
65        const LIN_Y = 1 << 1;
66        /// The angular degree of freedom of a joint.
67        const ANG_X = 1 << 2;
68        /// The set of degrees of freedom locked by a revolute joint.
69        const LOCKED_REVOLUTE_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits();
70        /// The set of degrees of freedom locked by a prismatic joint.
71        const LOCKED_PRISMATIC_AXES = Self::LIN_Y.bits() | Self::ANG_X.bits();
72        /// The set of degrees of freedom locked by a pin slot joint.
73        const LOCKED_PIN_SLOT_AXES = Self::LIN_Y.bits();
74        /// The set of degrees of freedom locked by a fixed joint.
75        const LOCKED_FIXED_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits() | Self::ANG_X.bits();
76        /// The set of degrees of freedom left free by a revolute joint.
77        const FREE_REVOLUTE_AXES = Self::ANG_X.bits();
78        /// The set of degrees of freedom left free by a prismatic joint.
79        const FREE_PRISMATIC_AXES = Self::LIN_X.bits();
80        /// The set of degrees of freedom left free by a fixed joint.
81        const FREE_FIXED_AXES = 0;
82        /// The set of all translational degrees of freedom.
83        const LIN_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits();
84        /// The set of all angular degrees of freedom.
85        const ANG_AXES = Self::ANG_X.bits();
86    }
87}
88
89impl Default for JointAxesMask {
90    fn default() -> Self {
91        Self::empty()
92    }
93}
94
95/// Identifiers of degrees of freedoms of a joint.
96#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
97#[derive(Copy, Clone, Debug, PartialEq)]
98pub enum JointAxis {
99    /// The linear (translational) degree of freedom along the joint’s local X axis.
100    LinX = 0,
101    /// The linear (translational) degree of freedom along the joint’s local Y axis.
102    LinY,
103    /// The linear (translational) degree of freedom along the joint’s local Z axis.
104    #[cfg(feature = "dim3")]
105    LinZ,
106    /// The rotational degree of freedom along the joint’s local X axis.
107    AngX,
108    /// The rotational degree of freedom along the joint’s local Y axis.
109    #[cfg(feature = "dim3")]
110    AngY,
111    /// The rotational degree of freedom along the joint’s local Z axis.
112    #[cfg(feature = "dim3")]
113    AngZ,
114}
115
116impl From<JointAxis> for JointAxesMask {
117    fn from(axis: JointAxis) -> Self {
118        JointAxesMask::from_bits(1 << axis as usize).unwrap()
119    }
120}
121
122/// Limits that restrict a joint's range of motion along one axis.
123///
124/// Use to constrain how far a joint can move/rotate. Examples:
125/// - Door that only opens 90°: revolute joint with limits `[0.0, PI/2.0]`
126/// - Piston with 2-unit stroke: prismatic joint with limits `[0.0, 2.0]`
127/// - Elbow that bends 0-150°: revolute joint with limits `[0.0, 5*PI/6]`
128///
129/// When a joint hits its limit, forces are applied to prevent further movement in that direction.
130#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
131#[derive(Copy, Clone, Debug, PartialEq)]
132pub struct JointLimits<N> {
133    /// Minimum allowed value (angle for revolute, distance for prismatic).
134    pub min: N,
135    /// Maximum allowed value (angle for revolute, distance for prismatic).
136    pub max: N,
137    /// Internal: impulse being applied to enforce the limit.
138    pub impulse: N,
139}
140
141impl<N: SimdRealCopy> Default for JointLimits<N> {
142    fn default() -> Self {
143        Self {
144            min: -N::splat(Real::MAX),
145            max: N::splat(Real::MAX),
146            impulse: N::splat(0.0),
147        }
148    }
149}
150
151impl<N: SimdRealCopy> From<[N; 2]> for JointLimits<N> {
152    fn from(value: [N; 2]) -> Self {
153        Self {
154            min: value[0],
155            max: value[1],
156            impulse: N::splat(0.0),
157        }
158    }
159}
160
161/// A powered motor that drives a joint toward a target position/velocity.
162///
163/// Motors add actuation to joints - they apply forces to make the joint move toward
164/// a desired state. Think of them as servos, electric motors, or hydraulic actuators.
165///
166/// ## Two control modes
167///
168/// 1. **Velocity control**: Set `target_vel` to make the motor spin/slide at constant speed
169/// 2. **Position control**: Set `target_pos` with `stiffness`/`damping` to reach a target angle/position
170///
171/// You can combine both for precise control.
172///
173/// ## Parameters
174///
175/// - `stiffness`: How strongly to pull toward target (spring constant)
176/// - `damping`: Resistance to motion (prevents oscillation)
177/// - `max_force`: Maximum force/torque the motor can apply
178///
179/// # Example
180/// ```
181/// # use rapier3d::prelude::*;
182/// # use rapier3d::dynamics::{RevoluteJoint, PrismaticJoint};
183/// # let mut revolute_joint = RevoluteJoint::new(Vector::x_axis());
184/// # let mut prismatic_joint = PrismaticJoint::new(Vector::x_axis());
185/// // Motor that spins a wheel at 10 rad/s
186/// revolute_joint.set_motor_velocity(10.0, 0.8);
187///
188/// // Motor that moves to position 5.0
189/// prismatic_joint.set_motor_position(5.0, 100.0, 10.0);  // stiffness=100, damping=10
190/// ```
191#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
192#[derive(Copy, Clone, Debug, PartialEq)]
193pub struct JointMotor {
194    /// Target velocity (units/sec for prismatic, rad/sec for revolute).
195    pub target_vel: Real,
196    /// Target position (units for prismatic, radians for revolute).
197    pub target_pos: Real,
198    /// Spring constant - how strongly to pull toward target position.
199    pub stiffness: Real,
200    /// Damping coefficient - resistance to motion (prevents oscillation).
201    pub damping: Real,
202    /// Maximum force the motor can apply (Newtons for prismatic, Nm for revolute).
203    pub max_force: Real,
204    /// Internal: current impulse being applied.
205    pub impulse: Real,
206    /// Force-based or acceleration-based motor model.
207    pub model: MotorModel,
208}
209
210impl Default for JointMotor {
211    fn default() -> Self {
212        Self {
213            target_pos: 0.0,
214            target_vel: 0.0,
215            stiffness: 0.0,
216            damping: 0.0,
217            max_force: Real::MAX,
218            impulse: 0.0,
219            model: MotorModel::AccelerationBased,
220        }
221    }
222}
223
224impl JointMotor {
225    pub(crate) fn motor_params(&self, dt: Real) -> MotorParameters<Real> {
226        let (erp_inv_dt, cfm_coeff, cfm_gain) =
227            self.model
228                .combine_coefficients(dt, self.stiffness, self.damping);
229        MotorParameters {
230            erp_inv_dt,
231            cfm_coeff,
232            cfm_gain,
233            // keep_lhs,
234            target_pos: self.target_pos,
235            target_vel: self.target_vel,
236            max_impulse: self.max_force * dt,
237        }
238    }
239}
240
241#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
242#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
243/// Enum indicating whether or not a joint is enabled.
244pub enum JointEnabled {
245    /// The joint is enabled.
246    Enabled,
247    /// The joint wasn’t disabled by the user explicitly but it is attached to
248    /// a disabled rigid-body.
249    DisabledByAttachedBody,
250    /// The joint is disabled by the user explicitly.
251    Disabled,
252}
253
254#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
255#[derive(Copy, Clone, Debug, PartialEq)]
256/// A generic joint.
257pub struct GenericJoint {
258    /// The joint’s frame, expressed in the first rigid-body’s local-space.
259    pub local_frame1: Isometry<Real>,
260    /// The joint’s frame, expressed in the second rigid-body’s local-space.
261    pub local_frame2: Isometry<Real>,
262    /// The degrees-of-freedoms locked by this joint.
263    pub locked_axes: JointAxesMask,
264    /// The degrees-of-freedoms limited by this joint.
265    pub limit_axes: JointAxesMask,
266    /// The degrees-of-freedoms motorised by this joint.
267    pub motor_axes: JointAxesMask,
268    /// The coupled degrees of freedom of this joint.
269    ///
270    /// Note that coupling degrees of freedoms (DoF) changes the interpretation of the coupled joint’s limits and motors.
271    /// If multiple linear DoF are limited/motorized, only the limits/motor configuration for the first
272    /// coupled linear DoF is applied to all coupled linear DoF. Similarly, if multiple angular DoF are limited/motorized
273    /// only the limits/motor configuration for the first coupled angular DoF is applied to all coupled angular DoF.
274    pub coupled_axes: JointAxesMask,
275    /// The limits, along each degree of freedoms of this joint.
276    ///
277    /// Note that the limit must also be explicitly enabled by the `limit_axes` bitmask.
278    /// For coupled degrees of freedoms (DoF), only the first linear (resp. angular) coupled DoF limit and `limit_axis`
279    /// bitmask is applied to the coupled linear (resp. angular) axes.
280    pub limits: [JointLimits<Real>; SPATIAL_DIM],
281    /// The motors, along each degree of freedoms of this joint.
282    ///
283    /// Note that the motor must also be explicitly enabled by the `motor_axes` bitmask.
284    /// For coupled degrees of freedoms (DoF), only the first linear (resp. angular) coupled DoF motor and `motor_axes`
285    /// bitmask is applied to the coupled linear (resp. angular) axes.
286    pub motors: [JointMotor; SPATIAL_DIM],
287    /// The coefficients controlling the joint constraints’ softness.
288    pub softness: SpringCoefficients<Real>,
289    /// Are contacts between the attached rigid-bodies enabled?
290    pub contacts_enabled: bool,
291    /// Whether the joint is enabled.
292    pub enabled: JointEnabled,
293    /// User-defined data associated to this joint.
294    pub user_data: u128,
295}
296
297impl Default for GenericJoint {
298    fn default() -> Self {
299        Self {
300            local_frame1: Isometry::identity(),
301            local_frame2: Isometry::identity(),
302            locked_axes: JointAxesMask::empty(),
303            limit_axes: JointAxesMask::empty(),
304            motor_axes: JointAxesMask::empty(),
305            coupled_axes: JointAxesMask::empty(),
306            limits: [JointLimits::default(); SPATIAL_DIM],
307            motors: [JointMotor::default(); SPATIAL_DIM],
308            softness: SpringCoefficients::joint_defaults(),
309            contacts_enabled: true,
310            enabled: JointEnabled::Enabled,
311            user_data: 0,
312        }
313    }
314}
315
316impl GenericJoint {
317    /// Creates a new generic joint that locks the specified degrees of freedom.
318    #[must_use]
319    pub fn new(locked_axes: JointAxesMask) -> Self {
320        *Self::default().lock_axes(locked_axes)
321    }
322
323    #[cfg(feature = "simd-is-enabled")]
324    /// Can this joint use SIMD-accelerated constraint formulations?
325    pub(crate) fn supports_simd_constraints(&self) -> bool {
326        self.limit_axes.is_empty() && self.motor_axes.is_empty()
327    }
328
329    #[doc(hidden)]
330    pub fn complete_ang_frame(axis: UnitVector<Real>) -> Rotation<Real> {
331        let basis = axis.orthonormal_basis();
332
333        #[cfg(feature = "dim2")]
334        {
335            use na::{Matrix2, Rotation2, UnitComplex};
336            let mat = Matrix2::from_columns(&[axis.into_inner(), basis[0]]);
337            let rotmat = Rotation2::from_matrix_unchecked(mat);
338            UnitComplex::from_rotation_matrix(&rotmat)
339        }
340
341        #[cfg(feature = "dim3")]
342        {
343            use na::{Matrix3, Rotation3, UnitQuaternion};
344            let mat = Matrix3::from_columns(&[axis.into_inner(), basis[0], basis[1]]);
345            let rotmat = Rotation3::from_matrix_unchecked(mat);
346            UnitQuaternion::from_rotation_matrix(&rotmat)
347        }
348    }
349
350    /// Is this joint enabled?
351    pub fn is_enabled(&self) -> bool {
352        self.enabled == JointEnabled::Enabled
353    }
354
355    /// Set whether this joint is enabled or not.
356    pub fn set_enabled(&mut self, enabled: bool) {
357        match self.enabled {
358            JointEnabled::Enabled | JointEnabled::DisabledByAttachedBody => {
359                if !enabled {
360                    self.enabled = JointEnabled::Disabled;
361                }
362            }
363            JointEnabled::Disabled => {
364                if enabled {
365                    self.enabled = JointEnabled::Enabled;
366                }
367            }
368        }
369    }
370
371    /// Add the specified axes to the set of axes locked by this joint.
372    pub fn lock_axes(&mut self, axes: JointAxesMask) -> &mut Self {
373        self.locked_axes |= axes;
374        self
375    }
376
377    /// Sets the joint’s frame, expressed in the first rigid-body’s local-space.
378    pub fn set_local_frame1(&mut self, local_frame: Isometry<Real>) -> &mut Self {
379        self.local_frame1 = local_frame;
380        self
381    }
382
383    /// Sets the joint’s frame, expressed in the second rigid-body’s local-space.
384    pub fn set_local_frame2(&mut self, local_frame: Isometry<Real>) -> &mut Self {
385        self.local_frame2 = local_frame;
386        self
387    }
388
389    /// The principal (local X) axis of this joint, expressed in the first rigid-body’s local-space.
390    #[must_use]
391    pub fn local_axis1(&self) -> UnitVector<Real> {
392        self.local_frame1 * Vector::x_axis()
393    }
394
395    /// Sets the principal (local X) axis of this joint, expressed in the first rigid-body’s local-space.
396    pub fn set_local_axis1(&mut self, local_axis: UnitVector<Real>) -> &mut Self {
397        self.local_frame1.rotation = Self::complete_ang_frame(local_axis);
398        self
399    }
400
401    /// The principal (local X) axis of this joint, expressed in the second rigid-body’s local-space.
402    #[must_use]
403    pub fn local_axis2(&self) -> UnitVector<Real> {
404        self.local_frame2 * Vector::x_axis()
405    }
406
407    /// Sets the principal (local X) axis of this joint, expressed in the second rigid-body’s local-space.
408    pub fn set_local_axis2(&mut self, local_axis: UnitVector<Real>) -> &mut Self {
409        self.local_frame2.rotation = Self::complete_ang_frame(local_axis);
410        self
411    }
412
413    /// The anchor of this joint, expressed in the first rigid-body’s local-space.
414    #[must_use]
415    pub fn local_anchor1(&self) -> Point<Real> {
416        self.local_frame1.translation.vector.into()
417    }
418
419    /// Sets anchor of this joint, expressed in the first rigid-body’s local-space.
420    pub fn set_local_anchor1(&mut self, anchor1: Point<Real>) -> &mut Self {
421        self.local_frame1.translation.vector = anchor1.coords;
422        self
423    }
424
425    /// The anchor of this joint, expressed in the second rigid-body’s local-space.
426    #[must_use]
427    pub fn local_anchor2(&self) -> Point<Real> {
428        self.local_frame2.translation.vector.into()
429    }
430
431    /// Sets anchor of this joint, expressed in the second rigid-body’s local-space.
432    pub fn set_local_anchor2(&mut self, anchor2: Point<Real>) -> &mut Self {
433        self.local_frame2.translation.vector = anchor2.coords;
434        self
435    }
436
437    /// Are contacts between the attached rigid-bodies enabled?
438    pub fn contacts_enabled(&self) -> bool {
439        self.contacts_enabled
440    }
441
442    /// Sets whether contacts between the attached rigid-bodies are enabled.
443    pub fn set_contacts_enabled(&mut self, enabled: bool) -> &mut Self {
444        self.contacts_enabled = enabled;
445        self
446    }
447
448    /// Sets the spring coefficients controlling this joint constraint’s softness.
449    #[must_use]
450    pub fn set_softness(&mut self, softness: SpringCoefficients<Real>) -> &mut Self {
451        self.softness = softness;
452        self
453    }
454
455    /// The joint limits along the specified axis.
456    #[must_use]
457    pub fn limits(&self, axis: JointAxis) -> Option<&JointLimits<Real>> {
458        let i = axis as usize;
459        if self.limit_axes.contains(axis.into()) {
460            Some(&self.limits[i])
461        } else {
462            None
463        }
464    }
465
466    /// Sets the joint limits along the specified axis.
467    pub fn set_limits(&mut self, axis: JointAxis, limits: [Real; 2]) -> &mut Self {
468        let i = axis as usize;
469        self.limit_axes |= axis.into();
470        self.limits[i].min = limits[0];
471        self.limits[i].max = limits[1];
472        self
473    }
474
475    /// The spring-like motor model along the specified axis of this joint.
476    #[must_use]
477    pub fn motor_model(&self, axis: JointAxis) -> Option<MotorModel> {
478        let i = axis as usize;
479        if self.motor_axes.contains(axis.into()) {
480            Some(self.motors[i].model)
481        } else {
482            None
483        }
484    }
485
486    /// Set the spring-like model used by the motor to reach the desired target velocity and position.
487    pub fn set_motor_model(&mut self, axis: JointAxis, model: MotorModel) -> &mut Self {
488        self.motors[axis as usize].model = model;
489        self
490    }
491
492    /// Sets the target velocity this motor needs to reach.
493    pub fn set_motor_velocity(
494        &mut self,
495        axis: JointAxis,
496        target_vel: Real,
497        factor: Real,
498    ) -> &mut Self {
499        self.set_motor(
500            axis,
501            self.motors[axis as usize].target_pos,
502            target_vel,
503            0.0,
504            factor,
505        )
506    }
507
508    /// Sets the target angle this motor needs to reach.
509    pub fn set_motor_position(
510        &mut self,
511        axis: JointAxis,
512        target_pos: Real,
513        stiffness: Real,
514        damping: Real,
515    ) -> &mut Self {
516        self.set_motor(axis, target_pos, 0.0, stiffness, damping)
517    }
518
519    /// Sets the maximum force the motor can deliver along the specified axis.
520    pub fn set_motor_max_force(&mut self, axis: JointAxis, max_force: Real) -> &mut Self {
521        self.motors[axis as usize].max_force = max_force;
522        self
523    }
524
525    /// The motor affecting the joint’s degree of freedom along the specified axis.
526    #[must_use]
527    pub fn motor(&self, axis: JointAxis) -> Option<&JointMotor> {
528        let i = axis as usize;
529        if self.motor_axes.contains(axis.into()) {
530            Some(&self.motors[i])
531        } else {
532            None
533        }
534    }
535
536    /// Configure both the target angle and target velocity of the motor.
537    pub fn set_motor(
538        &mut self,
539        axis: JointAxis,
540        target_pos: Real,
541        target_vel: Real,
542        stiffness: Real,
543        damping: Real,
544    ) -> &mut Self {
545        self.motor_axes |= axis.into();
546        let i = axis as usize;
547        self.motors[i].target_vel = target_vel;
548        self.motors[i].target_pos = target_pos;
549        self.motors[i].stiffness = stiffness;
550        self.motors[i].damping = damping;
551        self
552    }
553
554    /// Flips the orientation of the joint, including limits and motors.
555    pub fn flip(&mut self) {
556        std::mem::swap(&mut self.local_frame1, &mut self.local_frame2);
557
558        let coupled_bits = self.coupled_axes.bits();
559
560        for dim in 0..SPATIAL_DIM {
561            if coupled_bits & (1 << dim) == 0 {
562                let limit = self.limits[dim];
563                self.limits[dim].min = -limit.max;
564                self.limits[dim].max = -limit.min;
565            }
566
567            self.motors[dim].target_vel = -self.motors[dim].target_vel;
568            self.motors[dim].target_pos = -self.motors[dim].target_pos;
569        }
570    }
571
572    pub(crate) fn transform_to_solver_body_space(&mut self, rb1: &RigidBody, rb2: &RigidBody) {
573        if rb1.is_fixed() {
574            self.local_frame1 = rb1.pos.position * self.local_frame1;
575        } else {
576            self.local_frame1.translation.vector -= rb1.mprops.local_mprops.local_com.coords;
577        }
578
579        if rb2.is_fixed() {
580            self.local_frame2 = rb2.pos.position * self.local_frame2;
581        } else {
582            self.local_frame2.translation.vector -= rb2.mprops.local_mprops.local_com.coords;
583        }
584    }
585}
586
587macro_rules! joint_conversion_methods(
588    ($as_joint: ident, $as_joint_mut: ident, $Joint: ty, $axes: expr) => {
589        /// Converts the joint to its specific variant, if it is one.
590        #[must_use]
591        pub fn $as_joint(&self) -> Option<&$Joint> {
592            if self.locked_axes == $axes {
593                // SAFETY: this is OK because the target joint type is
594                //         a `repr(transparent)` newtype of `Joint`.
595                Some(unsafe { std::mem::transmute::<&Self, &$Joint>(self) })
596            } else {
597                None
598            }
599        }
600
601        /// Converts the joint to its specific mutable variant, if it is one.
602        #[must_use]
603        pub fn $as_joint_mut(&mut self) -> Option<&mut $Joint> {
604            if self.locked_axes == $axes {
605                // SAFETY: this is OK because the target joint type is
606                //         a `repr(transparent)` newtype of `Joint`.
607                Some(unsafe { std::mem::transmute::<&mut Self, &mut $Joint>(self) })
608            } else {
609                None
610            }
611        }
612    }
613);
614
615impl GenericJoint {
616    joint_conversion_methods!(
617        as_revolute,
618        as_revolute_mut,
619        RevoluteJoint,
620        JointAxesMask::LOCKED_REVOLUTE_AXES
621    );
622    joint_conversion_methods!(
623        as_fixed,
624        as_fixed_mut,
625        FixedJoint,
626        JointAxesMask::LOCKED_FIXED_AXES
627    );
628    joint_conversion_methods!(
629        as_prismatic,
630        as_prismatic_mut,
631        PrismaticJoint,
632        JointAxesMask::LOCKED_PRISMATIC_AXES
633    );
634    joint_conversion_methods!(
635        as_rope,
636        as_rope_mut,
637        RopeJoint,
638        JointAxesMask::FREE_FIXED_AXES
639    );
640
641    #[cfg(feature = "dim3")]
642    joint_conversion_methods!(
643        as_spherical,
644        as_spherical_mut,
645        SphericalJoint,
646        JointAxesMask::LOCKED_SPHERICAL_AXES
647    );
648}
649
650/// Create generic joints using the builder pattern.
651#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
652#[derive(Copy, Clone, Debug, PartialEq)]
653pub struct GenericJointBuilder(pub GenericJoint);
654
655impl GenericJointBuilder {
656    /// Creates a new generic joint builder.
657    #[must_use]
658    pub fn new(locked_axes: JointAxesMask) -> Self {
659        Self(GenericJoint::new(locked_axes))
660    }
661
662    /// Sets the degrees of freedom locked by the joint.
663    #[must_use]
664    pub fn locked_axes(mut self, axes: JointAxesMask) -> Self {
665        self.0.locked_axes = axes;
666        self
667    }
668
669    /// Sets whether contacts between the attached rigid-bodies are enabled.
670    #[must_use]
671    pub fn contacts_enabled(mut self, enabled: bool) -> Self {
672        self.0.contacts_enabled = enabled;
673        self
674    }
675
676    /// Sets the joint’s frame, expressed in the first rigid-body’s local-space.
677    #[must_use]
678    pub fn local_frame1(mut self, local_frame: Isometry<Real>) -> Self {
679        self.0.set_local_frame1(local_frame);
680        self
681    }
682
683    /// Sets the joint’s frame, expressed in the second rigid-body’s local-space.
684    #[must_use]
685    pub fn local_frame2(mut self, local_frame: Isometry<Real>) -> Self {
686        self.0.set_local_frame2(local_frame);
687        self
688    }
689
690    /// Sets the principal (local X) axis of this joint, expressed in the first rigid-body’s local-space.
691    #[must_use]
692    pub fn local_axis1(mut self, local_axis: UnitVector<Real>) -> Self {
693        self.0.set_local_axis1(local_axis);
694        self
695    }
696
697    /// Sets the principal (local X) axis of this joint, expressed in the second rigid-body’s local-space.
698    #[must_use]
699    pub fn local_axis2(mut self, local_axis: UnitVector<Real>) -> Self {
700        self.0.set_local_axis2(local_axis);
701        self
702    }
703
704    /// Sets the anchor of this joint, expressed in the first rigid-body’s local-space.
705    #[must_use]
706    pub fn local_anchor1(mut self, anchor1: Point<Real>) -> Self {
707        self.0.set_local_anchor1(anchor1);
708        self
709    }
710
711    /// Sets the anchor of this joint, expressed in the second rigid-body’s local-space.
712    #[must_use]
713    pub fn local_anchor2(mut self, anchor2: Point<Real>) -> Self {
714        self.0.set_local_anchor2(anchor2);
715        self
716    }
717
718    /// Sets the joint limits along the specified axis.
719    #[must_use]
720    pub fn limits(mut self, axis: JointAxis, limits: [Real; 2]) -> Self {
721        self.0.set_limits(axis, limits);
722        self
723    }
724
725    /// Sets the coupled degrees of freedom for this joint’s limits and motor.
726    #[must_use]
727    pub fn coupled_axes(mut self, axes: JointAxesMask) -> Self {
728        self.0.coupled_axes = axes;
729        self
730    }
731
732    /// Set the spring-like model used by the motor to reach the desired target velocity and position.
733    #[must_use]
734    pub fn motor_model(mut self, axis: JointAxis, model: MotorModel) -> Self {
735        self.0.set_motor_model(axis, model);
736        self
737    }
738
739    /// Sets the target velocity this motor needs to reach.
740    #[must_use]
741    pub fn motor_velocity(mut self, axis: JointAxis, target_vel: Real, factor: Real) -> Self {
742        self.0.set_motor_velocity(axis, target_vel, factor);
743        self
744    }
745
746    /// Sets the target angle this motor needs to reach.
747    #[must_use]
748    pub fn motor_position(
749        mut self,
750        axis: JointAxis,
751        target_pos: Real,
752        stiffness: Real,
753        damping: Real,
754    ) -> Self {
755        self.0
756            .set_motor_position(axis, target_pos, stiffness, damping);
757        self
758    }
759
760    /// Configure both the target angle and target velocity of the motor.
761    #[must_use]
762    pub fn set_motor(
763        mut self,
764        axis: JointAxis,
765        target_pos: Real,
766        target_vel: Real,
767        stiffness: Real,
768        damping: Real,
769    ) -> Self {
770        self.0
771            .set_motor(axis, target_pos, target_vel, stiffness, damping);
772        self
773    }
774
775    /// Sets the maximum force the motor can deliver along the specified axis.
776    #[must_use]
777    pub fn motor_max_force(mut self, axis: JointAxis, max_force: Real) -> Self {
778        self.0.set_motor_max_force(axis, max_force);
779        self
780    }
781
782    /// Sets the softness of this joint’s locked degrees of freedom.
783    #[must_use]
784    pub fn softness(mut self, softness: SpringCoefficients<Real>) -> Self {
785        self.0.softness = softness;
786        self
787    }
788
789    /// An arbitrary user-defined 128-bit integer associated to the joints built by this builder.
790    pub fn user_data(mut self, data: u128) -> Self {
791        self.0.user_data = data;
792        self
793    }
794
795    /// Builds the generic joint.
796    #[must_use]
797    pub fn build(self) -> GenericJoint {
798        self.0
799    }
800}
801
802impl From<GenericJointBuilder> for GenericJoint {
803    fn from(val: GenericJointBuilder) -> GenericJoint {
804        val.0
805    }
806}