Skip to main content

rapier2d/dynamics/joint/multibody_joint/
multibody_joint.rs

1use crate::dynamics::solver::GenericJointConstraint;
2use crate::dynamics::{
3    FixedJointBuilder, GenericJoint, IntegrationParameters, Multibody, MultibodyLink,
4    RigidBodyVelocity, joint,
5};
6use crate::math::{
7    ANG_DIM, DIM, DVector, JacobianViewMut, Pose, Real, Rotation, SPATIAL_DIM, SpatialVector,
8    Vector,
9};
10use parry::math::VectorExt;
11
12#[cfg(feature = "dim2")]
13use crate::math::rotation_from_angle;
14#[cfg(feature = "dim3")]
15use crate::utils::RotationOps;
16use crate::utils::vect_to_na;
17use na::DVectorViewMut;
18
19#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
20#[derive(Copy, Clone, Debug)]
21/// An joint attached to two bodies based on the reduced coordinates formalism.
22pub struct MultibodyJoint {
23    /// The joint’s description.
24    pub data: GenericJoint,
25    /// Is the joint a kinematic joint?
26    ///
27    /// Kinematic joint velocities are never changed by the physics engine. This gives the user
28    /// total control over the values of their degrees of freedoms.
29    pub kinematic: bool,
30    pub(crate) coords: SpatialVector,
31    pub(crate) joint_rot: Rotation,
32}
33
34impl MultibodyJoint {
35    /// Creates a new multibody joint from its description.
36    pub fn new(data: GenericJoint, kinematic: bool) -> Self {
37        Self {
38            data,
39            kinematic,
40            coords: Default::default(),
41            joint_rot: Rotation::IDENTITY,
42        }
43    }
44
45    pub(crate) fn free(pos: Pose) -> Self {
46        let mut result = Self::new(GenericJoint::default(), false);
47        result.set_free_pos(pos);
48        result
49    }
50
51    pub(crate) fn fixed(pos: Pose) -> Self {
52        Self::new(
53            FixedJointBuilder::new().local_frame1(pos).build().into(),
54            false,
55        )
56    }
57
58    pub(crate) fn set_free_pos(&mut self, pos: Pose) {
59        #[cfg(feature = "dim2")]
60        {
61            self.coords.x = pos.translation.x;
62            self.coords.y = pos.translation.y;
63        }
64        #[cfg(feature = "dim3")]
65        {
66            self.coords[0] = pos.translation.x;
67            self.coords[1] = pos.translation.y;
68            self.coords[2] = pos.translation.z;
69        }
70        self.joint_rot = pos.rotation;
71    }
72
73    // pub(crate) fn local_joint_rot(&self) -> &Rotation {
74    //     &self.joint_rot
75    // }
76
77    fn num_free_lin_dofs(&self) -> usize {
78        let locked_bits = self.data.locked_axes.bits();
79        DIM - (locked_bits & ((1 << DIM) - 1)).count_ones() as usize
80    }
81
82    /// The number of degrees of freedom allowed by the multibody_joint.
83    pub fn ndofs(&self) -> usize {
84        SPATIAL_DIM - self.data.locked_axes.bits().count_ones() as usize
85    }
86
87    /// The position of the multibody link containing this multibody_joint relative to its parent.
88    pub fn body_to_parent(&self) -> Pose {
89        let locked_bits = self.data.locked_axes.bits();
90        let mut transform = Pose::from_rotation(self.joint_rot) * self.data.local_frame2.inverse();
91
92        for i in 0..DIM {
93            if (locked_bits & (1 << i)) == 0 {
94                // Create a translation along axis i with the coordinate value
95                let translation = Vector::ith(i, self.coords[i]);
96                transform = Pose::from_translation(translation) * transform;
97            }
98        }
99
100        self.data.local_frame1 * transform
101    }
102
103    /// Integrate the position of this multibody_joint.
104    #[profiling::function]
105    pub fn integrate(&mut self, dt: Real, vels: &[Real]) {
106        let locked_bits = self.data.locked_axes.bits();
107        let mut curr_free_dof = 0;
108
109        for i in 0..DIM {
110            if (locked_bits & (1 << i)) == 0 {
111                self.coords[i] += vels[curr_free_dof] * dt;
112                curr_free_dof += 1;
113            }
114        }
115
116        let locked_ang_bits = locked_bits >> DIM;
117        let num_free_ang_dofs = ANG_DIM - locked_ang_bits.count_ones() as usize;
118        match num_free_ang_dofs {
119            0 => { /* No free dofs. */ }
120            1 => {
121                let dof_id = (!locked_ang_bits).trailing_zeros() as usize;
122                self.coords[DIM + dof_id] += vels[curr_free_dof] * dt;
123                #[cfg(feature = "dim2")]
124                {
125                    self.joint_rot = rotation_from_angle(self.coords[DIM + dof_id]);
126                }
127                #[cfg(feature = "dim3")]
128                {
129                    self.joint_rot = Rotation::from_axis_angle(
130                        Vector::ith(dof_id, 1.0),
131                        self.coords[DIM + dof_id],
132                    );
133                }
134            }
135            2 => {
136                todo!()
137            }
138            #[cfg(feature = "dim3")]
139            3 => {
140                let angvel = Vector::from_slice(&vels[curr_free_dof..curr_free_dof + 3]);
141                let disp = Rotation::from_scaled_axis(angvel * dt);
142                self.joint_rot = disp * self.joint_rot;
143                self.coords[3] += angvel[0] * dt;
144                self.coords[4] += angvel[1] * dt;
145                self.coords[5] += angvel[2] * dt;
146            }
147            _ => unreachable!(),
148        }
149    }
150
151    /// Apply a displacement to the multibody_joint.
152    pub fn apply_displacement(&mut self, disp: &[Real]) {
153        self.integrate(1.0, disp);
154    }
155
156    /// Sets in `out` the non-zero entries of the multibody_joint jacobian transformed by `transform`.
157    pub fn jacobian(&self, transform: &Rotation, out: &mut JacobianViewMut<Real>) {
158        let locked_bits = self.data.locked_axes.bits();
159        let mut curr_free_dof = 0;
160
161        for i in 0..DIM {
162            if (locked_bits & (1 << i)) == 0 {
163                let transformed_axis = (*transform) * Vector::ith(i, 1.0);
164                out.fixed_view_mut::<DIM, 1>(0, curr_free_dof)
165                    .copy_from(&vect_to_na(transformed_axis));
166                curr_free_dof += 1;
167            }
168        }
169
170        let locked_ang_bits = locked_bits >> DIM;
171        let num_free_ang_dofs = ANG_DIM - locked_ang_bits.count_ones() as usize;
172        match num_free_ang_dofs {
173            0 => { /* No free dofs. */ }
174            1 => {
175                #[cfg(feature = "dim2")]
176                {
177                    out[(DIM, curr_free_dof)] = 1.0;
178                }
179
180                #[cfg(feature = "dim3")]
181                {
182                    let dof_id = (!locked_ang_bits).trailing_zeros() as usize;
183                    let rotmat = transform.to_mat();
184                    out.fixed_view_mut::<ANG_DIM, 1>(DIM, curr_free_dof)
185                        .copy_from_slice(rotmat.col(dof_id).as_ref());
186                }
187            }
188            2 => {
189                todo!()
190            }
191            #[cfg(feature = "dim3")]
192            3 => {
193                let rotmat = transform.to_mat();
194                out.fixed_view_mut::<3, 3>(3, curr_free_dof)
195                    .copy_from_slice(rotmat.as_ref());
196            }
197            _ => unreachable!(),
198        }
199    }
200
201    /// Multiply the multibody_joint jacobian by generalized velocities to obtain the
202    /// relative velocity of the multibody link containing this multibody_joint.
203    pub fn jacobian_mul_coordinates(&self, acc: &[Real]) -> RigidBodyVelocity<Real> {
204        let locked_bits = self.data.locked_axes.bits();
205        let mut result = RigidBodyVelocity::zero();
206        let mut curr_free_dof = 0;
207
208        for i in 0..DIM {
209            if (locked_bits & (1 << i)) == 0 {
210                result.linvel += Vector::ith(i, acc[curr_free_dof]);
211                curr_free_dof += 1;
212            }
213        }
214
215        let locked_ang_bits = locked_bits >> DIM;
216        let num_free_ang_dofs = ANG_DIM - locked_ang_bits.count_ones() as usize;
217        match num_free_ang_dofs {
218            0 => { /* No free dofs. */ }
219            1 => {
220                #[cfg(feature = "dim2")]
221                {
222                    result.angvel += acc[curr_free_dof];
223                }
224                #[cfg(feature = "dim3")]
225                {
226                    let dof_id = (!locked_ang_bits).trailing_zeros() as usize;
227                    result.angvel[dof_id] += acc[curr_free_dof];
228                }
229            }
230            2 => {
231                todo!()
232            }
233            #[cfg(feature = "dim3")]
234            3 => {
235                let angvel = Vector::from_slice(&acc[curr_free_dof..curr_free_dof + 3]);
236                result.angvel += angvel;
237            }
238            _ => unreachable!(),
239        }
240        result
241    }
242
243    /// Fill `out` with the non-zero entries of a damping that can be applied by default to ensure a good stability of the multibody_joint.
244    pub fn default_damping(&self, out: &mut DVectorViewMut<Real>) {
245        let locked_bits = self.data.locked_axes.bits();
246        let mut curr_free_dof = self.num_free_lin_dofs();
247
248        // A default damping only for the angular dofs
249        for i in DIM..SPATIAL_DIM {
250            if locked_bits & (1 << i) == 0 {
251                // This is a free angular DOF.
252                out[curr_free_dof] = 0.1;
253                curr_free_dof += 1;
254            }
255        }
256    }
257
258    /// Maximum number of velocity constrains that can be generated by this multibody_joint.
259    pub fn num_velocity_constraints(&self) -> usize {
260        let locked_bits = self.data.locked_axes.bits();
261        let limit_bits = self.data.limit_axes.bits();
262        let motor_bits = self.data.motor_axes.bits();
263        let mut num_constraints = 0;
264
265        for i in 0..SPATIAL_DIM {
266            if (locked_bits & (1 << i)) == 0 {
267                if (limit_bits & (1 << i)) != 0 {
268                    num_constraints += 1;
269                }
270                if (motor_bits & (1 << i)) != 0 {
271                    num_constraints += 1;
272                }
273            }
274        }
275
276        num_constraints
277    }
278
279    /// Initialize and generate velocity constraints to enforce, e.g., multibody_joint limits and motors.
280    pub fn velocity_constraints(
281        &self,
282        params: &IntegrationParameters,
283        multibody: &Multibody,
284        link: &MultibodyLink,
285        mut j_id: usize,
286        jacobians: &mut DVector,
287        constraints: &mut [GenericJointConstraint],
288    ) -> usize {
289        let j_id = &mut j_id;
290        let locked_bits = self.data.locked_axes.bits();
291        let limit_bits = self.data.limit_axes.bits();
292        let motor_bits = self.data.motor_axes.bits();
293        let mut num_constraints = 0;
294        let mut curr_free_dof = 0;
295
296        for i in 0..DIM {
297            if (locked_bits & (1 << i)) == 0 {
298                let limits = if (limit_bits & (1 << i)) != 0 {
299                    Some([self.data.limits[i].min, self.data.limits[i].max])
300                } else {
301                    None
302                };
303
304                if (motor_bits & (1 << i)) != 0 {
305                    joint::unit_joint_motor_constraint(
306                        params,
307                        multibody,
308                        link,
309                        &self.data.motors[i],
310                        self.coords[i],
311                        limits,
312                        curr_free_dof,
313                        j_id,
314                        jacobians,
315                        constraints,
316                        &mut num_constraints,
317                    );
318                }
319
320                if (limit_bits & (1 << i)) != 0 {
321                    joint::unit_joint_limit_constraint(
322                        params,
323                        multibody,
324                        link,
325                        [self.data.limits[i].min, self.data.limits[i].max],
326                        self.coords[i],
327                        curr_free_dof,
328                        j_id,
329                        jacobians,
330                        constraints,
331                        &mut num_constraints,
332                        self.data.softness,
333                    );
334                }
335                curr_free_dof += 1;
336            }
337        }
338
339        /*
340        let locked_ang_bits = locked_bits >> DIM;
341        let num_free_ang_dofs = ANG_DIM - locked_ang_bits.count_ones() as usize;
342        match num_free_ang_dofs {
343            0 => { /* No free dofs. */ }
344            1 => {}
345            2 => {
346                todo!()
347            }
348            3 => {}
349            _ => unreachable!(),
350        }
351         */
352        // TODO: we should make special cases for multi-angular-dofs limits/motors
353        for i in DIM..SPATIAL_DIM {
354            if (locked_bits & (1 << i)) == 0 {
355                let limits = if (limit_bits & (1 << i)) != 0 {
356                    let limits = [self.data.limits[i].min, self.data.limits[i].max];
357                    joint::unit_joint_limit_constraint(
358                        params,
359                        multibody,
360                        link,
361                        limits,
362                        self.coords[i],
363                        curr_free_dof,
364                        j_id,
365                        jacobians,
366                        constraints,
367                        &mut num_constraints,
368                        self.data.softness,
369                    );
370                    Some(limits)
371                } else {
372                    None
373                };
374
375                if (motor_bits & (1 << i)) != 0 {
376                    joint::unit_joint_motor_constraint(
377                        params,
378                        multibody,
379                        link,
380                        &self.data.motors[i],
381                        self.coords[i],
382                        limits,
383                        curr_free_dof,
384                        j_id,
385                        jacobians,
386                        constraints,
387                        &mut num_constraints,
388                    );
389                }
390                curr_free_dof += 1;
391            }
392        }
393
394        num_constraints
395    }
396}