1use super::multibody_link::{MultibodyLink, MultibodyLinkVec};
2use super::multibody_workspace::MultibodyWorkspace;
3use crate::alloc_prelude::*;
4use crate::dynamics::integration_parameters::SpringCoefficients;
5use crate::dynamics::solver::{GenericJointConstraint, WritebackId};
6use crate::dynamics::{
7 IntegrationParameters, RigidBodyHandle, RigidBodySet, RigidBodyType, RigidBodyVelocity,
8};
9use crate::math::{
10 ANG_DIM, AngDim, AngVector, DIM, DVector, Dim, Jacobian, Pose, Real, SPATIAL_DIM,
11 SimdAngVector, Vector,
12};
13use crate::prelude::MultibodyJoint;
14#[cfg(feature = "dim3")]
15use crate::utils::mat_to_na;
16use crate::utils::{AngularInertiaOps, CrossProduct, CrossProductMatrix, IndexMut2, vect_to_na};
17use na::{
18 self, DMatrix, DVectorView, DVectorViewMut, Dyn, LU, OMatrix, SMatrix, SVector, StorageMut,
19};
20
21#[cfg(doc)]
22use crate::prelude::{GenericJoint, RigidBody};
23
24#[repr(C)]
25#[derive(Copy, Clone, Debug, Default)]
26struct Force {
27 linear: Vector,
28 angular: AngVector,
29}
30
31impl Force {
32 fn new(linear: Vector, angular: AngVector) -> Self {
33 Self { linear, angular }
34 }
35
36 fn as_vector(&self) -> &SVector<Real, SPATIAL_DIM> {
37 unsafe { core::mem::transmute(self) }
38 }
39}
40
41#[cfg(feature = "dim2")]
42fn concat_rb_mass_matrix(mass: Vector, inertia: Real) -> SMatrix<Real, SPATIAL_DIM, SPATIAL_DIM> {
43 let mut result = SMatrix::<Real, SPATIAL_DIM, SPATIAL_DIM>::zeros();
44 result[(0, 0)] = mass.x;
45 result[(1, 1)] = mass.y;
46 result[(2, 2)] = inertia;
47 result
48}
49
50#[cfg(feature = "dim3")]
51fn concat_rb_mass_matrix(
52 mass: Vector,
53 inertia: na::Matrix3<Real>,
54) -> SMatrix<Real, SPATIAL_DIM, SPATIAL_DIM> {
55 let mut result = SMatrix::<Real, SPATIAL_DIM, SPATIAL_DIM>::zeros();
56 result[(0, 0)] = mass.x;
57 result[(1, 1)] = mass.y;
58 result[(2, 2)] = mass.z;
59 result
60 .fixed_view_mut::<ANG_DIM, ANG_DIM>(DIM, DIM)
61 .copy_from(&inertia);
62 result
63}
64
65#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
71#[derive(Copy, Clone, Debug)]
72pub struct MultibodyDofCoupling {
73 pub link1: usize,
75 pub dof1: usize,
78 pub axis1: usize,
81 pub link2: usize,
83 pub dof2: usize,
85 pub axis2: usize,
87 pub coeff: Real,
89 pub offset: Real,
91}
92
93#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
95#[derive(Clone, Debug)]
96pub struct Multibody {
97 pub(crate) links: MultibodyLinkVec,
99 pub(crate) velocities: DVector,
100 pub(crate) damping: DVector,
101 pub(crate) armature: DVector,
103 pub(crate) accelerations: DVector,
104
105 body_jacobians: Vec<Jacobian<Real>>,
106 augmented_mass: DMatrix<Real>,
111 inv_augmented_mass: LU<Real, Dyn, Dyn>,
112 augmented_mass_indices: IndexSequence,
115
116 acc_augmented_mass: DMatrix<Real>,
117 acc_inv_augmented_mass: LU<Real, Dyn, Dyn>,
118
119 ndofs: usize,
120 pub(crate) root_is_dynamic: bool,
121 pub(crate) solver_id: u32,
122 self_contacts_enabled: bool,
123 couplings: Vec<MultibodyDofCoupling>,
127
128 workspace: MultibodyWorkspace,
132 coriolis_v: Vec<OMatrix<Real, Dim, Dyn>>,
133 coriolis_w: Vec<OMatrix<Real, AngDim, Dyn>>,
134 i_coriolis_dt: Jacobian<Real>,
135}
136impl Default for Multibody {
137 fn default() -> Self {
138 Multibody::new()
139 }
140}
141
142impl Multibody {
143 pub fn new() -> Self {
145 Self::with_self_contacts(true)
146 }
147
148 pub(crate) fn with_self_contacts(self_contacts_enabled: bool) -> Self {
149 Multibody {
150 links: MultibodyLinkVec(Vec::new()),
151 velocities: DVector::zeros(0),
152 damping: DVector::zeros(0),
153 armature: DVector::zeros(0),
154 accelerations: DVector::zeros(0),
155 body_jacobians: Vec::new(),
156 augmented_mass: DMatrix::zeros(0, 0),
157 inv_augmented_mass: LU::new(DMatrix::zeros(0, 0)),
158 acc_augmented_mass: DMatrix::zeros(0, 0),
159 acc_inv_augmented_mass: LU::new(DMatrix::zeros(0, 0)),
160 augmented_mass_indices: IndexSequence::new(),
161 ndofs: 0,
162 solver_id: 0,
163 workspace: MultibodyWorkspace::new(),
164 coriolis_v: Vec::new(),
165 coriolis_w: Vec::new(),
166 i_coriolis_dt: Jacobian::zeros(0),
167 root_is_dynamic: false,
168 self_contacts_enabled,
169 couplings: Vec::new(),
170 }
172 }
173
174 pub(crate) fn with_root(handle: RigidBodyHandle, self_contacts_enabled: bool) -> Self {
175 let mut mb = Multibody::with_self_contacts(self_contacts_enabled);
176 mb.root_is_dynamic = true;
179 let joint = MultibodyJoint::free(Pose::IDENTITY);
180 mb.add_link(None, joint, handle);
181 mb
182 }
183
184 pub(crate) fn remove_link(self, to_remove: usize, joint_only: bool) -> Vec<Multibody> {
185 let mut result = vec![];
186 let mut link2mb = vec![usize::MAX; self.links.len()];
187 let mut link_id2new_id = vec![usize::MAX; self.links.len()];
188
189 for (i, mut link) in self.links.0.into_iter().enumerate() {
191 let is_new_root = i == 0
192 || !joint_only && link.parent_internal_id == to_remove
193 || joint_only && i == to_remove;
194
195 if !joint_only && i == to_remove {
196 continue;
197 } else if is_new_root {
198 link2mb[i] = result.len();
199 result.push(Multibody::with_self_contacts(self.self_contacts_enabled));
200 } else {
201 link2mb[i] = link2mb[link.parent_internal_id]
202 }
203
204 let curr_mb = &mut result[link2mb[i]];
205 link_id2new_id[i] = curr_mb.links.len();
206
207 if is_new_root {
208 let joint = MultibodyJoint::fixed(*link.local_to_world());
209 link.joint = joint;
210 }
211
212 curr_mb.ndofs += link.joint().ndofs();
213 curr_mb.links.push(link);
214 }
215
216 for mb in &mut result {
219 mb.grow_buffers(mb.ndofs, mb.links.len());
220 mb.workspace.resize(mb.links.len(), mb.ndofs);
221
222 let mut assembly_id = 0;
223 for (i, link) in mb.links.iter_mut().enumerate() {
224 let link_ndofs = link.joint().ndofs();
225 mb.velocities
226 .rows_mut(assembly_id, link_ndofs)
227 .copy_from(&self.velocities.rows(link.assembly_id, link_ndofs));
228 mb.damping
229 .rows_mut(assembly_id, link_ndofs)
230 .copy_from(&self.damping.rows(link.assembly_id, link_ndofs));
231 mb.armature
232 .rows_mut(assembly_id, link_ndofs)
233 .copy_from(&self.armature.rows(link.assembly_id, link_ndofs));
234 mb.accelerations
235 .rows_mut(assembly_id, link_ndofs)
236 .copy_from(&self.accelerations.rows(link.assembly_id, link_ndofs));
237
238 link.internal_id = i;
239 link.assembly_id = assembly_id;
240
241 link.parent_internal_id = if i != 0 {
244 link_id2new_id[link.parent_internal_id]
245 } else {
246 0
247 };
248 assembly_id += link_ndofs;
249 }
250 }
251
252 result
253 }
254
255 pub(crate) fn append(&mut self, mut rhs: Multibody, parent: usize, joint: MultibodyJoint) {
256 let joint_ndofs = joint.ndofs();
257 let rhs_root_ndofs = rhs.links[0].joint.ndofs();
258 let ndofs_before_append = self.velocities.len();
259 let base_internal_id = self.links.len();
260 let rhs_copy_shift = ndofs_before_append + joint_ndofs;
262 let rhs_copy_ndofs = rhs.ndofs - rhs_root_ndofs;
265
266 for link in &mut rhs.links.0[1..] {
268 link.assembly_id =
269 (link.assembly_id + ndofs_before_append + joint_ndofs) - rhs_root_ndofs;
270 link.internal_id += base_internal_id;
271 link.parent_internal_id += base_internal_id;
272 }
273
274 {
276 rhs.links[0].joint = joint;
277 rhs.links[0].assembly_id = ndofs_before_append;
278 rhs.links[0].internal_id = base_internal_id;
279 rhs.links[0].parent_internal_id = parent;
280 }
281
282 self.grow_buffers(rhs_copy_ndofs + joint_ndofs, rhs.links.len());
284
285 if rhs_copy_ndofs > 0 {
286 self.velocities
287 .rows_mut(rhs_copy_shift, rhs_copy_ndofs)
288 .copy_from(&rhs.velocities.rows(rhs_root_ndofs, rhs_copy_ndofs));
289 self.damping
290 .rows_mut(rhs_copy_shift, rhs_copy_ndofs)
291 .copy_from(&rhs.damping.rows(rhs_root_ndofs, rhs_copy_ndofs));
292 self.armature
293 .rows_mut(rhs_copy_shift, rhs_copy_ndofs)
294 .copy_from(&rhs.armature.rows(rhs_root_ndofs, rhs_copy_ndofs));
295 self.accelerations
296 .rows_mut(rhs_copy_shift, rhs_copy_ndofs)
297 .copy_from(&rhs.accelerations.rows(rhs_root_ndofs, rhs_copy_ndofs));
298 }
299
300 rhs.links[0]
302 .joint
303 .default_damping(&mut self.damping.rows_mut(ndofs_before_append, joint_ndofs));
304
305 self.links.append(&mut rhs.links);
306 self.ndofs = self.velocities.len();
307 self.workspace.resize(self.links.len(), self.ndofs);
308 }
309
310 pub fn self_contacts_enabled(&self) -> bool {
315 self.self_contacts_enabled
316 }
317
318 pub fn set_self_contacts_enabled(&mut self, enabled: bool) {
323 self.self_contacts_enabled = enabled;
324 }
325
326 pub fn inv_augmented_mass(&self) -> &LU<Real, Dyn, Dyn> {
328 &self.inv_augmented_mass
329 }
330
331 #[inline]
333 pub fn root(&self) -> &MultibodyLink {
334 &self.links[0]
335 }
336
337 #[inline]
339 pub fn root_mut(&mut self) -> &mut MultibodyLink {
340 &mut self.links[0]
341 }
342
343 #[inline]
347 pub fn link(&self, id: usize) -> Option<&MultibodyLink> {
348 self.links.get(id)
349 }
350
351 #[inline]
355 pub fn link_mut(&mut self, id: usize) -> Option<&mut MultibodyLink> {
356 self.links.get_mut(id)
357 }
358
359 pub fn num_links(&self) -> usize {
361 self.links.len()
362 }
363
364 pub fn links(&self) -> impl Iterator<Item = &MultibodyLink> {
368 self.links.iter()
369 }
370
371 pub fn links_mut(&mut self) -> impl Iterator<Item = &mut MultibodyLink> {
375 self.links.iter_mut()
376 }
377
378 #[inline]
380 pub fn damping(&self) -> &DVector {
381 &self.damping
382 }
383
384 #[inline]
386 pub fn damping_mut(&mut self) -> &mut DVector {
387 &mut self.damping
388 }
389
390 #[inline]
393 pub fn armature(&self) -> &DVector {
394 &self.armature
395 }
396
397 #[inline]
400 pub fn armature_mut(&mut self) -> &mut DVector {
401 &mut self.armature
402 }
403
404 pub(crate) fn add_link(
405 &mut self,
406 parent: Option<usize>, dof: MultibodyJoint,
408 body: RigidBodyHandle,
409 ) -> &mut MultibodyLink {
410 assert!(
411 parent.is_none() || !self.links.is_empty(),
412 "Multibody::build_body: invalid parent id."
413 );
414
415 let assembly_id = self.velocities.len();
419 let internal_id = self.links.len();
420
421 let ndofs = dof.ndofs();
425 self.grow_buffers(ndofs, 1);
426 self.ndofs += ndofs;
427
428 dof.default_damping(&mut self.damping.rows_mut(assembly_id, ndofs));
432
433 let local_to_parent = dof.body_to_parent();
437 let local_to_world;
438
439 let parent_internal_id;
440 if let Some(parent) = parent {
441 parent_internal_id = parent;
442 let parent_link = &mut self.links[parent_internal_id];
443 local_to_world = parent_link.local_to_world * local_to_parent;
444 } else {
445 parent_internal_id = 0;
446 local_to_world = local_to_parent;
447 }
448
449 let rb = MultibodyLink::new(
450 body,
451 internal_id,
452 assembly_id,
453 parent_internal_id,
454 dof,
455 local_to_world,
456 local_to_parent,
457 );
458
459 self.links.push(rb);
460 self.workspace.resize(self.links.len(), self.ndofs);
461
462 &mut self.links[internal_id]
463 }
464
465 fn grow_buffers(&mut self, ndofs: usize, num_jacobians: usize) {
466 let len = self.velocities.len();
467 self.velocities.resize_vertically_mut(len + ndofs, 0.0);
468 self.damping.resize_vertically_mut(len + ndofs, 0.0);
469 self.armature.resize_vertically_mut(len + ndofs, 0.0);
470 self.accelerations.resize_vertically_mut(len + ndofs, 0.0);
471 self.body_jacobians
472 .extend((0..num_jacobians).map(|_| Jacobian::zeros(0)));
473 }
474
475 pub(crate) fn update_acceleration(&mut self, dt: Real, bodies: &RigidBodySet) {
476 if self.ndofs == 0 {
477 return; }
479
480 self.accelerations.fill(0.0);
481
482 for i in 0..self.links.len() {
484 let link = &self.links[i];
485 let rb = &bodies[link.rigid_body];
486
487 let mut acc = RigidBodyVelocity::zero();
488
489 if i != 0 {
490 let parent_id = link.parent_internal_id;
491 let parent_link = &self.links[parent_id];
492 let parent_rb = &bodies[parent_link.rigid_body];
493
494 acc += self.workspace.accs[parent_id];
495 acc.linvel += 2.0 * parent_rb.vels.angvel.gcross(link.joint_velocity.linvel);
498 #[cfg(feature = "dim3")]
499 {
500 acc.angvel += parent_rb.vels.angvel.cross(link.joint_velocity.angvel);
501 }
502
503 acc.linvel += parent_rb
504 .vels
505 .angvel
506 .gcross(parent_rb.vels.angvel.gcross(link.shift02));
507 acc.linvel += self.workspace.accs[parent_id].angvel.gcross(link.shift02);
508 }
509
510 acc.linvel += rb.vels.angvel.gcross(rb.vels.angvel.gcross(link.shift23));
511 acc.linvel += acc.angvel.gcross(link.shift23);
512
513 self.workspace.accs[i] = acc;
514
515 let gyroscopic;
518 let rb_inertia = rb.mprops.effective_angular_inertia();
519 let rb_mass = rb.mprops.effective_mass();
520
521 #[cfg(feature = "dim3")]
522 {
523 let angvel = rb.vels.angvel;
524 let inertia_times_angvel = rb_inertia * angvel;
525 gyroscopic = angvel.cross(inertia_times_angvel);
526 }
527 #[cfg(feature = "dim2")]
528 {
529 gyroscopic = 0.0;
530 }
531
532 let external_forces = Force::new(
533 rb.forces.force - rb_mass * acc.linvel,
534 rb.forces.torque - gyroscopic - rb_inertia * acc.angvel,
535 );
536 self.accelerations.gemv_tr(
537 1.0,
538 &self.body_jacobians[i],
539 external_forces.as_vector(),
540 1.0,
541 );
542 }
543
544 self.accelerations
545 .cmpy(-1.0, &self.damping, &self.velocities, 1.0);
546
547 for li in 0..self.links.len() {
551 let mut idx = self.links[li].assembly_id;
552 let locked = self.links[li].joint.data.locked_axes.bits();
553 for a in 0..SPATIAL_DIM {
554 if (locked >> a) & 1 == 0 {
555 let k = self.links[li].joint.spring_stiffness[a];
556 if k != 0.0 {
557 let q = self.links[li].joint.coords[a];
558 let rest = self.links[li].joint.spring_ref[a];
559 self.accelerations[idx] += -k * (q - rest) - k * dt * self.velocities[idx];
560 }
561 idx += 1;
562 }
563 }
564 }
565
566 self.augmented_mass_indices
567 .with_rearranged_rows_mut(&mut self.accelerations, |accs| {
568 self.acc_inv_augmented_mass.solve_mut(accs);
569 });
570 }
571
572 #[profiling::function]
574 pub(crate) fn update_velocities(&mut self, bodies: &mut RigidBodySet) {
575 let link = &mut self.links[0];
580 let joint_velocity = link
581 .joint
582 .jacobian_mul_coordinates(&self.velocities.as_slice()[link.assembly_id..]);
583
584 link.joint_velocity = joint_velocity;
585 bodies.index_mut_internal(link.rigid_body).vels = link.joint_velocity;
586
587 for i in 1..self.links.len() {
588 let (link, parent_link) = self.links.get_mut_with_parent(i);
589 let rb = &bodies[link.rigid_body];
590 let parent_rb = &bodies[parent_link.rigid_body];
591
592 let joint_velocity = link
593 .joint
594 .jacobian_mul_coordinates(&self.velocities.as_slice()[link.assembly_id..]);
595 link.joint_velocity = joint_velocity.transformed(
596 &(parent_link.local_to_world.rotation * link.joint.data.local_frame1.rotation),
597 );
598 let mut new_rb_vels = parent_rb.vels + link.joint_velocity;
599 let shift = rb.mprops.world_com - parent_rb.mprops.world_com;
600 new_rb_vels.linvel += parent_rb.vels.angvel.gcross(shift);
601 new_rb_vels.linvel += link.joint_velocity.angvel.gcross(link.shift23);
602
603 bodies.index_mut_internal(link.rigid_body).vels = new_rb_vels;
604 }
605 }
606
607 fn update_body_jacobians(&mut self) {
608 for i in 0..self.links.len() {
609 let link = &self.links[i];
610
611 if self.body_jacobians[i].ncols() != self.ndofs {
612 self.body_jacobians[i] = Jacobian::zeros(self.ndofs);
614 }
615
616 let parent_to_world;
617
618 if i != 0 {
619 let parent_id = link.parent_internal_id;
620 let parent_link = &self.links[parent_id];
621 parent_to_world = parent_link.local_to_world;
622
623 let (link_j, parent_j) = self.body_jacobians.index_mut_const(i, parent_id);
624 link_j.copy_from(parent_j);
625
626 {
627 let mut link_j_v = link_j.fixed_rows_mut::<DIM>(0);
628 let parent_j_w = parent_j.fixed_rows::<ANG_DIM>(DIM);
629
630 let shift_tr = vect_to_na(link.shift02).gcross_matrix_tr();
631 link_j_v.gemm(1.0, &shift_tr, &parent_j_w, 1.0);
632 }
633 } else {
634 self.body_jacobians[i].fill(0.0);
635 parent_to_world = Pose::IDENTITY;
636 }
637
638 let ndofs = link.joint.ndofs();
639 let mut tmp = SMatrix::<Real, SPATIAL_DIM, SPATIAL_DIM>::zeros();
640 let mut link_joint_j = tmp.columns_mut(0, ndofs);
641 let mut link_j_part = self.body_jacobians[i].columns_mut(link.assembly_id, ndofs);
642 link.joint.jacobian(
643 &(parent_to_world.rotation * link.joint.data.local_frame1.rotation),
644 &mut link_joint_j,
645 );
646 link_j_part += link_joint_j;
647
648 {
649 let link_j = &mut self.body_jacobians[i];
650 let (mut link_j_v, link_j_w) =
651 link_j.rows_range_pair_mut(0..DIM, DIM..DIM + ANG_DIM);
652 let shift_tr = vect_to_na(link.shift23).gcross_matrix_tr();
653 link_j_v.gemm(1.0, &shift_tr, &link_j_w, 1.0);
654 }
655 }
656 }
657
658 pub(crate) fn update_mass_matrix(&mut self, dt: Real, bodies: &RigidBodySet) {
659 if self.ndofs == 0 {
660 return; }
662
663 if self.augmented_mass.ncols() != self.ndofs {
664 self.augmented_mass = DMatrix::zeros(self.ndofs, self.ndofs);
666 self.acc_augmented_mass = DMatrix::zeros(self.ndofs, self.ndofs);
667 } else {
668 self.augmented_mass.fill(0.0);
669 self.acc_augmented_mass.fill(0.0);
670 }
671
672 self.augmented_mass_indices.clear();
673
674 let coriolis_ndofs = self.coriolis_v.first().map(|m| m.ncols());
676 if self.coriolis_v.len() != self.links.len() || coriolis_ndofs != Some(self.ndofs) {
677 self.coriolis_v = vec![OMatrix::<Real, Dim, Dyn>::zeros(self.ndofs); self.links.len()];
678 self.coriolis_w =
679 vec![OMatrix::<Real, AngDim, Dyn>::zeros(self.ndofs); self.links.len()];
680 self.i_coriolis_dt = Jacobian::zeros(self.ndofs);
681 }
682
683 let mut curr_assembly_id = 0;
684
685 for i in 0..self.links.len() {
686 let link = &self.links[i];
687 let rb = &bodies[link.rigid_body];
688 let rb_mass = rb.mprops.effective_mass();
689 let rb_inertia = rb.mprops.effective_angular_inertia().into_matrix();
690 let body_jacobian = &self.body_jacobians[i];
691
692 assert_eq!(
696 curr_assembly_id, link.assembly_id,
697 "Internal error: contiguity assumption on assembly_id does not hold."
698 );
699 curr_assembly_id += link.joint.ndofs();
700
701 if link.joint.kinematic {
702 for k in link.assembly_id..link.assembly_id + link.joint.ndofs() {
703 self.augmented_mass_indices.remove(k);
704 }
705 } else {
706 for k in link.assembly_id..link.assembly_id + link.joint.ndofs() {
707 self.augmented_mass_indices.keep(k);
708 }
709 }
710
711 #[allow(unused_mut)] let mut augmented_inertia = rb_inertia;
713
714 #[cfg(feature = "dim3")]
715 {
716 let gyroscopic_matrix = rb.vels.angvel.gcross_matrix() * rb_inertia
718 - (rb_inertia * rb.vels.angvel).gcross_matrix();
719
720 augmented_inertia += gyroscopic_matrix * dt;
721 }
722
723 #[allow(clippy::useless_conversion)] let rb_mass_matrix_wo_gyro = concat_rb_mass_matrix(rb_mass, rb_inertia.into());
727 #[allow(clippy::useless_conversion)] let rb_mass_matrix = concat_rb_mass_matrix(rb_mass, augmented_inertia.into());
729 self.augmented_mass
730 .quadform(1.0, &rb_mass_matrix_wo_gyro, body_jacobian, 1.0);
731 self.acc_augmented_mass
732 .quadform(1.0, &rb_mass_matrix, body_jacobian, 1.0);
733
734 let rb_j = &self.body_jacobians[i];
740 let rb_j_w = rb_j.fixed_rows::<ANG_DIM>(DIM);
741
742 let ndofs = link.joint.ndofs();
743
744 if i != 0 {
745 let parent_id = link.parent_internal_id;
746 let parent_link = &self.links[parent_id];
747 let parent_rb = &bodies[parent_link.rigid_body];
748 let parent_j = &self.body_jacobians[parent_id];
749 let parent_j_w = parent_j.fixed_rows::<ANG_DIM>(DIM);
750 let parent_w = SimdAngVector::<Real>::from(parent_rb.vels.angvel).gcross_matrix();
751
752 let (coriolis_v, parent_coriolis_v) = self.coriolis_v.index_mut2(i, parent_id);
753 let (coriolis_w, parent_coriolis_w) = self.coriolis_w.index_mut2(i, parent_id);
754
755 coriolis_v.copy_from(parent_coriolis_v);
756 coriolis_w.copy_from(parent_coriolis_w);
757
758 let shift_cross_tr = vect_to_na(link.shift02).gcross_matrix_tr();
760 coriolis_v.gemm(1.0, &shift_cross_tr, parent_coriolis_w, 1.0);
761
762 let dvel_cross = vect_to_na(
764 rb.vels.angvel.gcross(link.shift02) + 2.0 * link.joint_velocity.linvel,
765 )
766 .gcross_matrix_tr();
767 coriolis_v.gemm(1.0, &dvel_cross, &parent_j_w, 1.0);
768
769 coriolis_v.gemm(
771 1.0,
772 &vect_to_na(link.joint_velocity.linvel).gcross_matrix_tr(),
773 &parent_j_w,
774 1.0,
775 );
776 coriolis_v.gemm(1.0, &(parent_w * shift_cross_tr), &parent_j_w, 1.0);
777
778 #[cfg(feature = "dim3")]
779 {
780 let vel_wrt_joint_w = vect_to_na(link.joint_velocity.angvel).gcross_matrix();
781 coriolis_w.gemm(-1.0, &vel_wrt_joint_w, &parent_j_w, 1.0);
782 }
783
784 if !link.joint.kinematic {
786 let mut coriolis_v_part = coriolis_v.columns_mut(link.assembly_id, ndofs);
787
788 let mut tmp1 = SMatrix::<Real, SPATIAL_DIM, SPATIAL_DIM>::zeros();
789 let mut rb_joint_j = tmp1.columns_mut(0, ndofs);
790 link.joint.jacobian(
791 &(parent_link.local_to_world.rotation
792 * link.joint.data.local_frame1.rotation),
793 &mut rb_joint_j,
794 );
795
796 let rb_joint_j_v = rb_joint_j.fixed_rows::<DIM>(0);
797 coriolis_v_part.gemm(2.0, &parent_w, &rb_joint_j_v, 1.0);
798
799 #[cfg(feature = "dim3")]
800 {
801 let rb_joint_j_w = rb_joint_j.fixed_rows::<ANG_DIM>(DIM);
802 let mut coriolis_w_part = coriolis_w.columns_mut(link.assembly_id, ndofs);
803 coriolis_w_part.gemm(1.0, &parent_w, &rb_joint_j_w, 1.0);
804 }
805 }
806 } else {
807 self.coriolis_v[i].fill(0.0);
808 self.coriolis_w[i].fill(0.0);
809 }
810
811 let coriolis_v = &mut self.coriolis_v[i];
812 let coriolis_w = &mut self.coriolis_w[i];
813
814 {
815 let shift_cross_tr = vect_to_na(link.shift23).gcross_matrix_tr();
817 coriolis_v.gemm(1.0, &shift_cross_tr, coriolis_w, 1.0);
818
819 let dvel_cross = vect_to_na(rb.vels.angvel.gcross(link.shift23)).gcross_matrix_tr();
821 coriolis_v.gemm(1.0, &dvel_cross, &rb_j_w, 1.0);
822
823 coriolis_v.gemm(
825 1.0,
826 &(SimdAngVector::<Real>::from(rb.vels.angvel).gcross_matrix() * shift_cross_tr),
827 &rb_j_w,
828 1.0,
829 );
830 }
831
832 let coriolis_v = &mut self.coriolis_v[i];
833 let coriolis_w = &mut self.coriolis_w[i];
834
835 {
839 let mut i_coriolis_dt_v = self.i_coriolis_dt.fixed_rows_mut::<DIM>(0);
840 i_coriolis_dt_v.copy_from(coriolis_v);
841 let rb_mass_dt = vect_to_na(rb_mass * dt);
842 i_coriolis_dt_v
843 .column_iter_mut()
844 .for_each(|mut c| c.component_mul_assign(&rb_mass_dt));
845 }
846
847 #[cfg(feature = "dim2")]
848 {
849 let mut i_coriolis_dt_w = self.i_coriolis_dt.fixed_rows_mut::<ANG_DIM>(DIM);
850 i_coriolis_dt_w.zip_apply(coriolis_w, |o, x| *o = x * dt * rb_inertia);
852 }
853 #[cfg(feature = "dim3")]
854 {
855 let mut i_coriolis_dt_w = self.i_coriolis_dt.fixed_rows_mut::<ANG_DIM>(DIM);
856 i_coriolis_dt_w.gemm(dt, &mat_to_na(rb_inertia), coriolis_w, 0.0);
857 }
858
859 self.acc_augmented_mass
860 .gemm_tr(1.0, rb_j, &self.i_coriolis_dt, 1.0);
861 }
862
863 for i in 0..self.ndofs {
872 let diag = self.damping[i] * dt + self.armature[i];
873 self.acc_augmented_mass[(i, i)] += diag;
874 self.augmented_mass[(i, i)] += diag;
875 }
876
877 let dt2 = dt * dt;
881 for li in 0..self.links.len() {
882 let mut idx = self.links[li].assembly_id;
883 let locked = self.links[li].joint.data.locked_axes.bits();
884 for a in 0..SPATIAL_DIM {
885 if (locked >> a) & 1 == 0 {
886 let k = self.links[li].joint.spring_stiffness[a];
887 if k != 0.0 {
888 let d = k * dt2;
889 self.acc_augmented_mass[(idx, idx)] += d;
890 self.augmented_mass[(idx, idx)] += d;
891 }
892 idx += 1;
893 }
894 }
895 }
896
897 let effective_dim = self
898 .augmented_mass_indices
899 .dim_after_removal(self.acc_augmented_mass.nrows());
900
901 self.augmented_mass_indices
904 .rearrange_columns(&mut self.acc_augmented_mass, true);
905 self.augmented_mass_indices
906 .rearrange_columns(&mut self.augmented_mass, true);
907
908 self.augmented_mass_indices
909 .rearrange_rows(&mut self.acc_augmented_mass, true);
910 self.augmented_mass_indices
911 .rearrange_rows(&mut self.augmented_mass, true);
912
913 self.acc_inv_augmented_mass = LU::new(
915 self.acc_augmented_mass
916 .view((0, 0), (effective_dim, effective_dim))
917 .into_owned(),
918 );
919 self.inv_augmented_mass = LU::new(
920 self.augmented_mass
921 .view((0, 0), (effective_dim, effective_dim))
922 .into_owned(),
923 );
924 }
925
926 pub fn dof_inverse_inertia(&mut self, bodies: &RigidBodySet) -> DVector {
932 self.forward_kinematics(bodies, false);
936 self.update_mass_matrix(0.0, bodies);
937
938 let n = self.ndofs;
939 let mut out = DVector::zeros(n);
940 if n == 0 {
941 return out;
942 }
943 let mut e = DVector::zeros(n);
947 for i in 0..n {
948 e.fill(0.0);
949 e[i] = 1.0;
950 self.augmented_mass_indices
951 .with_rearranged_rows_mut(&mut e, |b| {
952 self.inv_augmented_mass.solve_mut(b);
953 });
954 out[i] = e[i];
955 }
956 out
957 }
958
959 pub fn add_dof_coupling(&mut self, coupling: MultibodyDofCoupling) {
963 self.couplings.push(coupling);
964 }
965
966 pub fn couplings(&self) -> &[MultibodyDofCoupling] {
968 &self.couplings
969 }
970
971 pub(crate) fn num_couplings_owned_by(&self, owner_link: usize) -> usize {
975 self.couplings
976 .iter()
977 .filter(|c| c.link1 == owner_link)
978 .count()
979 }
980
981 pub(crate) fn coupling_velocity_constraints(
985 &self,
986 owner_link: usize,
987 params: &IntegrationParameters,
988 mut j_id: usize,
989 jacobians: &mut DVector,
990 out: &mut [GenericJointConstraint],
991 ) -> usize {
992 let ndofs = self.ndofs;
993 let erp_inv_dt = SpringCoefficients::<Real>::joint_defaults().erp_inv_dt(params.dt);
994
995 let mut i = 0;
996 for c in self.couplings.iter().filter(|c| c.link1 == owner_link) {
997 let g1 = self.links[c.link1].assembly_id + c.dof1;
998 let g2 = self.links[c.link2].assembly_id + c.dof2;
999 let q1 = self.links[c.link1].joint().coords()[c.axis1];
1000 let q2 = self.links[c.link2].joint().coords()[c.axis2];
1001
1002 jacobians.rows_mut(j_id, ndofs * 2).fill(0.0);
1004 jacobians[j_id + g2] += 1.0;
1005 jacobians[j_id + g1] -= c.coeff;
1006 for k in 0..ndofs {
1007 jacobians[j_id + ndofs + k] = jacobians[j_id + k];
1008 }
1009 self.inv_augmented_mass
1010 .solve_mut(&mut jacobians.rows_mut(j_id + ndofs, ndofs));
1011
1012 let lhs = jacobians[j_id + ndofs + g2] - c.coeff * jacobians[j_id + ndofs + g1];
1014
1015 let drift = q2 - c.coeff * q1 - c.offset;
1016
1017 out[i] = GenericJointConstraint {
1018 is_rigid_body1: false,
1019 solver_vel1: u32::MAX,
1020 ndofs1: 0,
1021 j_id1: 0,
1022 is_rigid_body2: false,
1023 solver_vel2: self.solver_id,
1024 ndofs2: ndofs,
1025 j_id2: j_id,
1026 joint_id: usize::MAX, impulse: 0.0,
1028 impulse_bounds: [-Real::MAX, Real::MAX],
1029 inv_lhs: crate::utils::inv(lhs),
1030 rhs: drift * erp_inv_dt,
1031 rhs_wo_bias: 0.0,
1032 cfm_coeff: 0.0,
1033 cfm_gain: 0.0,
1034 writeback_id: WritebackId::Dof(0),
1035 };
1036
1037 j_id += 2 * ndofs;
1038 i += 1;
1039 }
1040
1041 i
1042 }
1043
1044 #[inline]
1046 pub fn joint_velocity(&self, link: &MultibodyLink) -> DVectorView<'_, Real> {
1047 let ndofs = link.joint().ndofs();
1048 DVectorView::from_slice(
1049 &self.velocities.as_slice()[link.assembly_id..link.assembly_id + ndofs],
1050 ndofs,
1051 )
1052 }
1053
1054 #[inline]
1056 pub fn generalized_acceleration(&self) -> DVectorView<'_, Real> {
1057 self.accelerations.rows(0, self.ndofs)
1058 }
1059
1060 #[inline]
1062 pub fn generalized_velocity(&self) -> DVectorView<'_, Real> {
1063 self.velocities.rows(0, self.ndofs)
1064 }
1065
1066 #[inline]
1068 pub fn body_jacobian(&self, link_id: usize) -> &Jacobian<Real> {
1069 &self.body_jacobians[link_id]
1070 }
1071
1072 #[inline]
1074 pub fn generalized_velocity_mut(&mut self) -> DVectorViewMut<'_, Real> {
1075 self.velocities.rows_mut(0, self.ndofs)
1076 }
1077
1078 #[inline]
1079 pub(crate) fn integrate(&mut self, dt: Real) {
1080 for rb in self.links.iter_mut() {
1081 rb.joint
1082 .integrate(dt, &self.velocities.as_slice()[rb.assembly_id..])
1083 }
1084 }
1085
1086 pub fn apply_displacements(&mut self, disp: &[Real]) {
1091 for link in self.links.iter_mut() {
1092 link.joint.apply_displacement(&disp[link.assembly_id..])
1093 }
1094 }
1095
1096 pub(crate) fn update_root_type(&mut self, bodies: &RigidBodySet, take_body_pose: bool) {
1097 if let Some(rb) = bodies.get(self.links[0].rigid_body) {
1098 if rb.is_dynamic() != self.root_is_dynamic {
1099 let root_pose = if take_body_pose {
1100 *rb.position()
1101 } else {
1102 self.links[0].local_to_world
1103 };
1104
1105 if rb.is_dynamic() {
1106 let free_joint = MultibodyJoint::free(root_pose);
1107 let prev_root_ndofs = self.links[0].joint().ndofs();
1108 self.links[0].joint = free_joint;
1109 self.links[0].assembly_id = 0;
1110 self.ndofs += SPATIAL_DIM;
1111
1112 self.velocities = self.velocities.clone().insert_rows(0, SPATIAL_DIM, 0.0);
1113 self.damping = self.damping.clone().insert_rows(0, SPATIAL_DIM, 0.0);
1114 self.armature = self.armature.clone().insert_rows(0, SPATIAL_DIM, 0.0);
1115 self.accelerations =
1116 self.accelerations.clone().insert_rows(0, SPATIAL_DIM, 0.0);
1117
1118 for link in &mut self.links[1..] {
1119 link.assembly_id += SPATIAL_DIM - prev_root_ndofs;
1120 }
1121 } else {
1122 assert!(self.velocities.len() >= SPATIAL_DIM);
1123 assert!(self.damping.len() >= SPATIAL_DIM);
1124 assert!(self.armature.len() >= SPATIAL_DIM);
1125 assert!(self.accelerations.len() >= SPATIAL_DIM);
1126
1127 let fixed_joint = MultibodyJoint::fixed(root_pose);
1128 let prev_root_ndofs = self.links[0].joint().ndofs();
1129 self.links[0].joint = fixed_joint;
1130 self.links[0].assembly_id = 0;
1131 self.ndofs -= prev_root_ndofs;
1132
1133 if self.ndofs == 0 {
1134 self.velocities = DVector::zeros(0);
1135 self.damping = DVector::zeros(0);
1136 self.armature = DVector::zeros(0);
1137 self.accelerations = DVector::zeros(0);
1138 } else {
1139 self.velocities =
1140 self.velocities.index((prev_root_ndofs.., 0)).into_owned();
1141 self.damping = self.damping.index((prev_root_ndofs.., 0)).into_owned();
1142 self.armature = self.armature.index((prev_root_ndofs.., 0)).into_owned();
1143 self.accelerations = self
1144 .accelerations
1145 .index((prev_root_ndofs.., 0))
1146 .into_owned();
1147 }
1148
1149 for link in &mut self.links[1..] {
1150 link.assembly_id -= prev_root_ndofs;
1151 }
1152 }
1153
1154 self.root_is_dynamic = rb.is_dynamic();
1155 }
1156
1157 if take_body_pose {
1159 if self.links[0].joint.data.locked_axes.is_empty() {
1160 self.links[0].joint.set_free_pos(*rb.position());
1161 } else {
1162 self.links[0].joint.data.local_frame1 = *rb.position();
1163 }
1164 }
1165 }
1166 }
1167
1168 pub fn update_rigid_bodies(&self, bodies: &mut RigidBodySet, update_mass_properties: bool) {
1173 self.update_rigid_bodies_internal(bodies, update_mass_properties, false, true)
1174 }
1175
1176 pub(crate) fn update_rigid_bodies_internal(
1177 &self,
1178 bodies: &mut RigidBodySet,
1179 update_mass_properties: bool,
1180 update_next_positions_only: bool,
1181 change_tracking: bool,
1182 ) {
1183 for link in self.links.iter() {
1185 let rb = if change_tracking {
1186 bodies.get_mut_internal_with_modification_tracking(link.rigid_body)
1187 } else {
1188 bodies.get_mut_internal(link.rigid_body)
1189 };
1190
1191 if let Some(rb) = rb {
1192 rb.pos.next_position = link.local_to_world;
1193
1194 if !update_next_positions_only {
1195 rb.pos.position = link.local_to_world;
1196 }
1197
1198 if update_mass_properties {
1199 rb.mprops
1200 .update_world_mass_properties(rb.body_type, &link.local_to_world);
1201 }
1202 }
1203 }
1204 }
1205
1206 pub fn forward_kinematics(
1227 &mut self,
1228 bodies: &RigidBodySet,
1229 read_root_pose_from_rigid_body: bool,
1230 ) {
1231 self.update_root_type(bodies, read_root_pose_from_rigid_body);
1233
1234 {
1236 let link = &mut self.links[0];
1237 link.local_to_parent = link.joint.body_to_parent();
1238 link.local_to_world = link.local_to_parent;
1239 }
1240
1241 for i in 1..self.links.len() {
1243 let (link, parent_link) = self.links.get_mut_with_parent(i);
1244
1245 link.local_to_parent = link.joint.body_to_parent();
1246 link.local_to_world = parent_link.local_to_world * link.local_to_parent;
1247
1248 {
1249 let parent_rb = &bodies[parent_link.rigid_body];
1250 let link_rb = &bodies[link.rigid_body];
1251 let c0 = parent_link.local_to_world * parent_rb.mprops.local_mprops.local_com;
1252 let c2 = link.local_to_world * link.joint.data.local_frame2.translation;
1253 let c3 = link.local_to_world * link_rb.mprops.local_mprops.local_com;
1254
1255 link.shift02 = c2 - c0;
1256 link.shift23 = c3 - c2;
1257 }
1258
1259 assert_eq!(
1260 bodies[link.rigid_body].body_type,
1261 RigidBodyType::Dynamic,
1262 "A rigid-body that is not at the root of a multibody must be dynamic."
1263 );
1264 }
1265
1266 self.update_body_jacobians();
1270 }
1271
1272 pub fn kinematic_branch(&self, link_id: usize) -> Vec<usize> {
1274 let mut branch = vec![]; let mut curr_id = Some(link_id);
1276
1277 while let Some(id) = curr_id {
1278 branch.push(id);
1279 curr_id = self.links[id].parent_id();
1280 }
1281
1282 branch.reverse();
1283 branch
1284 }
1285
1286 pub fn forward_kinematics_single_link(
1294 &self,
1295 bodies: &RigidBodySet,
1296 link_id: usize,
1297 displacement: Option<&[Real]>,
1298 out_jacobian: Option<&mut Jacobian<Real>>,
1299 ) -> Pose {
1300 let branch = self.kinematic_branch(link_id);
1301 self.forward_kinematics_single_branch(bodies, &branch, displacement, out_jacobian)
1302 }
1303
1304 #[profiling::function]
1324 pub fn forward_kinematics_single_branch(
1325 &self,
1326 bodies: &RigidBodySet,
1327 branch: &[usize],
1328 displacement: Option<&[Real]>,
1329 mut out_jacobian: Option<&mut Jacobian<Real>>,
1330 ) -> Pose {
1331 if let Some(out_jacobian) = out_jacobian.as_deref_mut() {
1332 if out_jacobian.ncols() != self.ndofs {
1333 *out_jacobian = Jacobian::zeros(self.ndofs);
1334 } else {
1335 out_jacobian.fill(0.0);
1336 }
1337 }
1338
1339 let mut parent_link: Option<MultibodyLink> = None;
1340
1341 for i in branch {
1342 let mut link = self.links[*i];
1343
1344 if let Some(displacement) = displacement {
1345 link.joint
1346 .apply_displacement(&displacement[link.assembly_id..]);
1347 }
1348
1349 let parent_to_world;
1350
1351 if let Some(parent_link) = parent_link {
1352 link.local_to_parent = link.joint.body_to_parent();
1353 link.local_to_world = parent_link.local_to_world * link.local_to_parent;
1354
1355 {
1356 let parent_rb = &bodies[parent_link.rigid_body];
1357 let link_rb = &bodies[link.rigid_body];
1358 let c0 = parent_link.local_to_world * parent_rb.mprops.local_mprops.local_com;
1359 let c2 = link.local_to_world
1360 * Vector::from(link.joint.data.local_frame2.translation);
1361 let c3 = link.local_to_world * link_rb.mprops.local_mprops.local_com;
1362
1363 link.shift02 = c2 - c0;
1364 link.shift23 = c3 - c2;
1365 }
1366
1367 parent_to_world = parent_link.local_to_world;
1368
1369 if let Some(out_jacobian) = out_jacobian.as_deref_mut() {
1370 let (mut link_j_v, parent_j_w) =
1371 out_jacobian.rows_range_pair_mut(0..DIM, DIM..DIM + ANG_DIM);
1372 let shift_tr = vect_to_na(link.shift02).gcross_matrix_tr();
1373 link_j_v.gemm(1.0, &shift_tr, &parent_j_w, 1.0);
1374 }
1375 } else {
1376 link.local_to_parent = link.joint.body_to_parent();
1377 link.local_to_world = link.local_to_parent;
1378 parent_to_world = Pose::IDENTITY;
1379 }
1380
1381 if let Some(out_jacobian) = out_jacobian.as_deref_mut() {
1382 let ndofs = link.joint.ndofs();
1383 let mut tmp = SMatrix::<Real, SPATIAL_DIM, SPATIAL_DIM>::zeros();
1384 let mut link_joint_j = tmp.columns_mut(0, ndofs);
1385 let mut link_j_part = out_jacobian.columns_mut(link.assembly_id, ndofs);
1386 link.joint.jacobian(
1387 &(parent_to_world.rotation * link.joint.data.local_frame1.rotation),
1388 &mut link_joint_j,
1389 );
1390 link_j_part += link_joint_j;
1391
1392 {
1393 let (mut link_j_v, link_j_w) =
1394 out_jacobian.rows_range_pair_mut(0..DIM, DIM..DIM + ANG_DIM);
1395 let shift_tr = vect_to_na(link.shift23).gcross_matrix_tr();
1396 link_j_v.gemm(1.0, &shift_tr, &link_j_w, 1.0);
1397 }
1398 }
1399
1400 parent_link = Some(link);
1401 }
1402
1403 parent_link
1404 .map(|link| link.local_to_world)
1405 .unwrap_or(Pose::IDENTITY)
1406 }
1407
1408 #[inline]
1410 pub fn ndofs(&self) -> usize {
1411 self.ndofs
1412 }
1413
1414 pub(crate) fn fill_jacobians(
1415 &self,
1416 link_id: usize,
1417 unit_force: Vector,
1418 unit_torque: AngVector,
1419 j_id: &mut usize,
1420 jacobians: &mut DVector,
1421 ) -> (Real, Real) {
1422 if self.ndofs == 0 {
1423 return (0.0, 0.0);
1424 }
1425
1426 let wj_id = *j_id + self.ndofs;
1427 let force = Force {
1428 linear: unit_force,
1429 #[cfg(feature = "dim2")]
1430 angular: unit_torque,
1431 #[cfg(feature = "dim3")]
1432 angular: unit_torque,
1433 };
1434
1435 let link = &self.links[link_id];
1436 let mut out_j = jacobians.rows_mut(*j_id, self.ndofs);
1437 self.body_jacobians[link.internal_id].tr_mul_to(force.as_vector(), &mut out_j);
1438
1439 for i in 0..self.ndofs {
1441 jacobians[wj_id + i] = jacobians[*j_id + i];
1442 }
1443
1444 {
1445 let mut out_invm_j = jacobians.rows_mut(wj_id, self.ndofs);
1446 self.augmented_mass_indices
1447 .with_rearranged_rows_mut(&mut out_invm_j, |out_invm_j| {
1448 self.inv_augmented_mass.solve_mut(out_invm_j);
1449 });
1450 }
1451
1452 let j = jacobians.rows(*j_id, self.ndofs);
1453 let invm_j = jacobians.rows(wj_id, self.ndofs);
1454 *j_id += self.ndofs * 2;
1455
1456 (j.dot(&invm_j), j.dot(&self.generalized_velocity()))
1457 }
1458
1459 pub(crate) fn fill_relative_jacobians(
1463 &self,
1464 link_id1: usize,
1465 unit_force1: Vector,
1466 unit_torque1: AngVector,
1467 link_id2: usize,
1468 unit_force2: Vector,
1469 unit_torque2: AngVector,
1470 j_id: &mut usize,
1471 jacobians: &mut DVector,
1472 ) {
1473 if self.ndofs == 0 {
1474 return;
1475 }
1476
1477 let wj_id = *j_id + self.ndofs;
1478 let force1 = Force {
1479 linear: unit_force1,
1480 angular: unit_torque1,
1481 };
1482 let force2 = Force {
1483 linear: unit_force2,
1484 angular: unit_torque2,
1485 };
1486
1487 let link1 = &self.links[link_id1];
1488 let link2 = &self.links[link_id2];
1489
1490 {
1491 let jb1 = &self.body_jacobians[link1.internal_id];
1492 let jb2 = &self.body_jacobians[link2.internal_id];
1493
1494 let (mut out_j, mut scratch) =
1496 jacobians.rows_range_pair_mut(*j_id..*j_id + self.ndofs, wj_id..wj_id + self.ndofs);
1497 jb2.tr_mul_to(force2.as_vector(), &mut out_j);
1498 jb1.tr_mul_to(force1.as_vector(), &mut scratch);
1499 out_j.axpy(-1.0, &scratch, 1.0);
1500
1501 let scale_sq = jb1.norm_squared() * force1.as_vector().norm_squared()
1505 + jb2.norm_squared() * force2.as_vector().norm_squared();
1506 let eps = Real::EPSILON * 1.0e3;
1507 if out_j.norm_squared() <= eps * eps * scale_sq {
1508 out_j.fill(0.0);
1509 }
1510 }
1511
1512 for i in 0..self.ndofs {
1514 jacobians[wj_id + i] = jacobians[*j_id + i];
1515 }
1516
1517 {
1518 let mut out_invm_j = jacobians.rows_mut(wj_id, self.ndofs);
1519 self.augmented_mass_indices
1520 .with_rearranged_rows_mut(&mut out_invm_j, |out_invm_j| {
1521 self.inv_augmented_mass.solve_mut(out_invm_j);
1522 });
1523 }
1524
1525 *j_id += self.ndofs * 2;
1526 }
1527}
1528
1529#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1530#[derive(Clone, Debug)]
1531struct IndexSequence {
1532 first_to_remove: u32,
1533 index_map: Vec<usize>,
1534}
1535
1536impl IndexSequence {
1537 const NONE: u32 = u32::MAX;
1538
1539 fn new() -> Self {
1540 Self {
1541 first_to_remove: Self::NONE,
1542 index_map: vec![],
1543 }
1544 }
1545
1546 fn start(&self) -> usize {
1548 self.first_to_remove as usize
1549 }
1550
1551 fn clear(&mut self) {
1552 self.first_to_remove = Self::NONE;
1553 self.index_map.clear();
1554 }
1555
1556 fn keep(&mut self, i: usize) {
1557 if self.first_to_remove == Self::NONE {
1558 return;
1561 }
1562
1563 self.index_map.push(i);
1564 }
1565
1566 fn remove(&mut self, i: usize) {
1567 if self.first_to_remove == Self::NONE {
1568 self.first_to_remove = i as u32;
1569 }
1570 }
1571
1572 fn dim_after_removal(&self, original_dim: usize) -> usize {
1573 if self.first_to_remove == Self::NONE {
1574 original_dim
1575 } else {
1576 self.start() + self.index_map.len()
1577 }
1578 }
1579
1580 fn rearrange_columns<R: na::Dim, C: na::Dim, S: StorageMut<Real, R, C>>(
1581 &self,
1582 mat: &mut na::Matrix<Real, R, C, S>,
1583 clear_removed: bool,
1584 ) {
1585 if self.first_to_remove == Self::NONE {
1586 return;
1588 }
1589
1590 for (target_shift, source) in self.index_map.iter().enumerate() {
1591 let target = self.start() + target_shift;
1592 let (mut target_col, source_col) = mat.columns_range_pair_mut(target, *source);
1593 target_col.copy_from(&source_col);
1594 }
1595
1596 if clear_removed {
1597 mat.columns_range_mut(self.start() + self.index_map.len()..)
1598 .fill(0.0);
1599 }
1600 }
1601
1602 fn rearrange_rows<R: na::Dim, C: na::Dim, S: StorageMut<Real, R, C>>(
1603 &self,
1604 mat: &mut na::Matrix<Real, R, C, S>,
1605 clear_removed: bool,
1606 ) {
1607 if self.first_to_remove == Self::NONE {
1608 return;
1610 }
1611
1612 for mut col in mat.column_iter_mut() {
1613 for (target_shift, source) in self.index_map.iter().enumerate() {
1614 let target = self.start() + target_shift;
1615 col[target] = col[*source];
1616 }
1617
1618 if clear_removed {
1619 col.rows_range_mut(self.start() + self.index_map.len()..)
1620 .fill(0.0);
1621 }
1622 }
1623 }
1624
1625 fn inv_rearrange_rows<R: na::Dim, C: na::Dim, S: StorageMut<Real, R, C>>(
1626 &self,
1627 mat: &mut na::Matrix<Real, R, C, S>,
1628 ) {
1629 if self.first_to_remove == Self::NONE {
1630 return;
1632 }
1633
1634 for mut col in mat.column_iter_mut() {
1635 for (target_shift, source) in self.index_map.iter().enumerate().rev() {
1636 let target = self.start() + target_shift;
1637 col[*source] = col[target];
1638 col[target] = 0.0;
1639 }
1640 }
1641 }
1642
1643 fn with_rearranged_rows_mut<C: na::Dim, S: StorageMut<Real, Dyn, C>>(
1644 &self,
1645 mat: &mut na::Matrix<Real, Dyn, C, S>,
1646 mut f: impl FnMut(&mut na::MatrixViewMut<Real, Dyn, C, S::RStride, S::CStride>),
1647 ) {
1648 self.rearrange_rows(mat, true);
1649 let effective_dim = self.dim_after_removal(mat.nrows());
1650 if effective_dim > 0 {
1651 f(&mut mat.rows_mut(0, effective_dim));
1652 }
1653 self.inv_rearrange_rows(mat);
1654 }
1655}
1656
1657#[cfg(test)]
1658mod test {
1659 use super::IndexSequence;
1660 use crate::alloc_prelude::*;
1661 use crate::dynamics::{ImpulseJointSet, IslandManager};
1662 #[cfg(feature = "dim3")]
1663 use crate::math::Vector;
1664 use crate::math::{Real, SPATIAL_DIM};
1665 use crate::prelude::{
1666 ColliderSet, MultibodyJointHandle, MultibodyJointSet, RevoluteJoint, RigidBodyBuilder,
1667 RigidBodySet,
1668 };
1669 use na::{DVector, RowDVector};
1670
1671 #[test]
1672 fn test_multibody_append() {
1673 let mut bodies = RigidBodySet::new();
1674 let mut joints = MultibodyJointSet::new();
1675
1676 let a = bodies.insert(RigidBodyBuilder::dynamic());
1677 let b = bodies.insert(RigidBodyBuilder::dynamic());
1678 let c = bodies.insert(RigidBodyBuilder::dynamic());
1679 let d = bodies.insert(RigidBodyBuilder::dynamic());
1680
1681 #[cfg(feature = "dim2")]
1682 let joint = RevoluteJoint::new();
1683 #[cfg(feature = "dim3")]
1684 let joint = RevoluteJoint::new(Vector::X);
1685
1686 let mb_handle = joints.insert(a, b, joint, true).unwrap();
1687 joints.insert(c, d, joint, true).unwrap();
1688 joints.insert(b, c, joint, true).unwrap();
1689
1690 assert_eq!(joints.get(mb_handle).unwrap().0.ndofs, SPATIAL_DIM + 3);
1691 }
1692
1693 #[test]
1694 fn test_multibody_insert() {
1695 let mut rnd = oorandom::Rand32::new(1234);
1696
1697 for k in 0..10 {
1698 let mut bodies = RigidBodySet::new();
1699 let mut multibody_joints = MultibodyJointSet::new();
1700
1701 let num_links = 100;
1702 let mut handles = vec![];
1703
1704 for _ in 0..num_links {
1705 handles.push(bodies.insert(RigidBodyBuilder::dynamic()));
1706 }
1707
1708 let mut insertion_id: Vec<_> = (0..num_links - 1).collect();
1709
1710 #[cfg(feature = "dim2")]
1711 let joint = RevoluteJoint::new();
1712 #[cfg(feature = "dim3")]
1713 let joint = RevoluteJoint::new(Vector::X);
1714
1715 match k {
1716 0 => {} 1 => {
1718 insertion_id.reverse();
1720 }
1721 _ => {
1722 for l in 0..num_links - 1 {
1725 insertion_id.swap(l, rnd.rand_range(0..num_links as u32 - 1) as usize);
1726 }
1727 }
1728 }
1729
1730 let mut mb_handle = MultibodyJointHandle::invalid();
1731 for i in insertion_id {
1732 mb_handle = multibody_joints
1733 .insert(handles[i], handles[i + 1], joint, true)
1734 .unwrap();
1735 }
1736
1737 assert_eq!(
1738 multibody_joints.get(mb_handle).unwrap().0.ndofs,
1739 SPATIAL_DIM + num_links - 1
1740 );
1741 }
1742 }
1743
1744 #[test]
1745 fn test_multibody_remove() {
1746 let mut rnd = oorandom::Rand32::new(1234);
1747
1748 for k in 0..10 {
1749 let mut bodies = RigidBodySet::new();
1750 let mut multibody_joints = MultibodyJointSet::new();
1751 let mut colliders = ColliderSet::new();
1752 let mut impulse_joints = ImpulseJointSet::new();
1753 let mut islands = IslandManager::new();
1754
1755 let num_links = 100;
1756 let mut handles = vec![];
1757
1758 for _ in 0..num_links {
1759 handles.push(bodies.insert(RigidBodyBuilder::dynamic()));
1760 }
1761
1762 #[cfg(feature = "dim2")]
1763 let joint = RevoluteJoint::new();
1764 #[cfg(feature = "dim3")]
1765 let joint = RevoluteJoint::new(Vector::X);
1766
1767 for i in 0..num_links - 1 {
1768 multibody_joints
1769 .insert(handles[i], handles[i + 1], joint, true)
1770 .unwrap();
1771 }
1772
1773 match k {
1774 0 => {} 1 => {
1776 handles.reverse();
1778 }
1779 _ => {
1780 for l in 0..num_links {
1783 handles.swap(l, rnd.rand_range(0..num_links as u32) as usize);
1784 }
1785 }
1786 }
1787
1788 for handle in handles {
1789 bodies.remove(
1790 handle,
1791 &mut islands,
1792 &mut colliders,
1793 &mut impulse_joints,
1794 &mut multibody_joints,
1795 true,
1796 );
1797 }
1798 }
1799 }
1800
1801 fn test_sequence() -> IndexSequence {
1802 let mut seq = IndexSequence::new();
1803 seq.remove(2);
1804 seq.remove(3);
1805 seq.remove(4);
1806 seq.keep(5);
1807 seq.keep(6);
1808 seq.remove(7);
1809 seq.keep(8);
1810 seq
1811 }
1812
1813 #[test]
1814 fn index_sequence_rearrange_columns() {
1815 let seq = test_sequence();
1816 let mut vec = RowDVector::from_fn(10, |_, c| c as Real);
1817 seq.rearrange_columns(&mut vec, true);
1818 assert_eq!(
1819 vec,
1820 RowDVector::from(vec![0.0, 1.0, 5.0, 6.0, 8.0, 0.0, 0.0, 0.0, 0.0, 0.0])
1821 );
1822 }
1823
1824 #[test]
1825 fn index_sequence_rearrange_rows() {
1826 let seq = test_sequence();
1827 let mut vec = DVector::from_fn(10, |r, _| r as Real);
1828 seq.rearrange_rows(&mut vec, true);
1829 assert_eq!(
1830 vec,
1831 DVector::from(vec![0.0, 1.0, 5.0, 6.0, 8.0, 0.0, 0.0, 0.0, 0.0, 0.0])
1832 );
1833 seq.inv_rearrange_rows(&mut vec);
1834 assert_eq!(
1835 vec,
1836 DVector::from(vec![0.0, 1.0, 0.0, 0.0, 0.0, 5.0, 6.0, 0.0, 8.0, 0.0])
1837 );
1838 }
1839
1840 #[test]
1841 fn index_sequence_with_rearranged_rows_mut() {
1842 let seq = test_sequence();
1843 let mut vec = DVector::from_fn(10, |r, _| r as Real);
1844 seq.with_rearranged_rows_mut(&mut vec, |v| {
1845 assert_eq!(v.len(), 5);
1846 assert_eq!(*v, DVector::from(vec![0.0, 1.0, 5.0, 6.0, 8.0]));
1847 *v *= 10.0;
1848 });
1849 assert_eq!(
1850 vec,
1851 DVector::from(vec![0.0, 10.0, 0.0, 0.0, 0.0, 50.0, 60.0, 0.0, 80.0, 0.0])
1852 );
1853 }
1854}