1use super::multibody_link::{MultibodyLink, MultibodyLinkVec};
2use super::multibody_workspace::MultibodyWorkspace;
3use crate::dynamics::{RigidBodyHandle, RigidBodySet, RigidBodyType, RigidBodyVelocity};
4use crate::math::{
5 ANG_DIM, AngDim, AngVector, DIM, DVector, Dim, Jacobian, Pose, Real, SPATIAL_DIM,
6 SimdAngVector, Vector,
7};
8use crate::prelude::MultibodyJoint;
9#[cfg(feature = "dim3")]
10use crate::utils::mat_to_na;
11use crate::utils::{AngularInertiaOps, CrossProduct, CrossProductMatrix, IndexMut2, vect_to_na};
12use na::{
13 self, DMatrix, DVectorView, DVectorViewMut, Dyn, LU, OMatrix, SMatrix, SVector, StorageMut,
14};
15
16#[cfg(doc)]
17use crate::prelude::{GenericJoint, RigidBody};
18
19#[repr(C)]
20#[derive(Copy, Clone, Debug, Default)]
21struct Force {
22 linear: Vector,
23 angular: AngVector,
24}
25
26impl Force {
27 fn new(linear: Vector, angular: AngVector) -> Self {
28 Self { linear, angular }
29 }
30
31 fn as_vector(&self) -> &SVector<Real, SPATIAL_DIM> {
32 unsafe { std::mem::transmute(self) }
33 }
34}
35
36#[cfg(feature = "dim2")]
37fn concat_rb_mass_matrix(mass: Vector, inertia: Real) -> SMatrix<Real, SPATIAL_DIM, SPATIAL_DIM> {
38 let mut result = SMatrix::<Real, SPATIAL_DIM, SPATIAL_DIM>::zeros();
39 result[(0, 0)] = mass.x;
40 result[(1, 1)] = mass.y;
41 result[(2, 2)] = inertia;
42 result
43}
44
45#[cfg(feature = "dim3")]
46fn concat_rb_mass_matrix(
47 mass: Vector,
48 inertia: na::Matrix3<Real>,
49) -> SMatrix<Real, SPATIAL_DIM, SPATIAL_DIM> {
50 let mut result = SMatrix::<Real, SPATIAL_DIM, SPATIAL_DIM>::zeros();
51 result[(0, 0)] = mass.x;
52 result[(1, 1)] = mass.y;
53 result[(2, 2)] = mass.z;
54 result
55 .fixed_view_mut::<ANG_DIM, ANG_DIM>(DIM, DIM)
56 .copy_from(&inertia);
57 result
58}
59
60#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
62#[derive(Clone, Debug)]
63pub struct Multibody {
64 pub(crate) links: MultibodyLinkVec,
66 pub(crate) velocities: DVector,
67 pub(crate) damping: DVector,
68 pub(crate) accelerations: DVector,
69
70 body_jacobians: Vec<Jacobian<Real>>,
71 augmented_mass: DMatrix<Real>,
76 inv_augmented_mass: LU<Real, Dyn, Dyn>,
77 augmented_mass_indices: IndexSequence,
80
81 acc_augmented_mass: DMatrix<Real>,
82 acc_inv_augmented_mass: LU<Real, Dyn, Dyn>,
83
84 ndofs: usize,
85 pub(crate) root_is_dynamic: bool,
86 pub(crate) solver_id: u32,
87 self_contacts_enabled: bool,
88
89 workspace: MultibodyWorkspace,
93 coriolis_v: Vec<OMatrix<Real, Dim, Dyn>>,
94 coriolis_w: Vec<OMatrix<Real, AngDim, Dyn>>,
95 i_coriolis_dt: Jacobian<Real>,
96}
97impl Default for Multibody {
98 fn default() -> Self {
99 Multibody::new()
100 }
101}
102
103impl Multibody {
104 pub fn new() -> Self {
106 Self::with_self_contacts(true)
107 }
108
109 pub(crate) fn with_self_contacts(self_contacts_enabled: bool) -> Self {
110 Multibody {
111 links: MultibodyLinkVec(Vec::new()),
112 velocities: DVector::zeros(0),
113 damping: DVector::zeros(0),
114 accelerations: DVector::zeros(0),
115 body_jacobians: Vec::new(),
116 augmented_mass: DMatrix::zeros(0, 0),
117 inv_augmented_mass: LU::new(DMatrix::zeros(0, 0)),
118 acc_augmented_mass: DMatrix::zeros(0, 0),
119 acc_inv_augmented_mass: LU::new(DMatrix::zeros(0, 0)),
120 augmented_mass_indices: IndexSequence::new(),
121 ndofs: 0,
122 solver_id: 0,
123 workspace: MultibodyWorkspace::new(),
124 coriolis_v: Vec::new(),
125 coriolis_w: Vec::new(),
126 i_coriolis_dt: Jacobian::zeros(0),
127 root_is_dynamic: false,
128 self_contacts_enabled,
129 }
131 }
132
133 pub(crate) fn with_root(handle: RigidBodyHandle, self_contacts_enabled: bool) -> Self {
134 let mut mb = Multibody::with_self_contacts(self_contacts_enabled);
135 mb.root_is_dynamic = true;
138 let joint = MultibodyJoint::free(Pose::IDENTITY);
139 mb.add_link(None, joint, handle);
140 mb
141 }
142
143 pub(crate) fn remove_link(self, to_remove: usize, joint_only: bool) -> Vec<Multibody> {
144 let mut result = vec![];
145 let mut link2mb = vec![usize::MAX; self.links.len()];
146 let mut link_id2new_id = vec![usize::MAX; self.links.len()];
147
148 for (i, mut link) in self.links.0.into_iter().enumerate() {
150 let is_new_root = i == 0
151 || !joint_only && link.parent_internal_id == to_remove
152 || joint_only && i == to_remove;
153
154 if !joint_only && i == to_remove {
155 continue;
156 } else if is_new_root {
157 link2mb[i] = result.len();
158 result.push(Multibody::with_self_contacts(self.self_contacts_enabled));
159 } else {
160 link2mb[i] = link2mb[link.parent_internal_id]
161 }
162
163 let curr_mb = &mut result[link2mb[i]];
164 link_id2new_id[i] = curr_mb.links.len();
165
166 if is_new_root {
167 let joint = MultibodyJoint::fixed(*link.local_to_world());
168 link.joint = joint;
169 }
170
171 curr_mb.ndofs += link.joint().ndofs();
172 curr_mb.links.push(link);
173 }
174
175 for mb in &mut result {
178 mb.grow_buffers(mb.ndofs, mb.links.len());
179 mb.workspace.resize(mb.links.len(), mb.ndofs);
180
181 let mut assembly_id = 0;
182 for (i, link) in mb.links.iter_mut().enumerate() {
183 let link_ndofs = link.joint().ndofs();
184 mb.velocities
185 .rows_mut(assembly_id, link_ndofs)
186 .copy_from(&self.velocities.rows(link.assembly_id, link_ndofs));
187 mb.damping
188 .rows_mut(assembly_id, link_ndofs)
189 .copy_from(&self.damping.rows(link.assembly_id, link_ndofs));
190 mb.accelerations
191 .rows_mut(assembly_id, link_ndofs)
192 .copy_from(&self.accelerations.rows(link.assembly_id, link_ndofs));
193
194 link.internal_id = i;
195 link.assembly_id = assembly_id;
196
197 link.parent_internal_id = if i != 0 {
200 link_id2new_id[link.parent_internal_id]
201 } else {
202 0
203 };
204 assembly_id += link_ndofs;
205 }
206 }
207
208 result
209 }
210
211 pub(crate) fn append(&mut self, mut rhs: Multibody, parent: usize, joint: MultibodyJoint) {
212 let rhs_root_ndofs = rhs.links[0].joint.ndofs();
213 let rhs_copy_shift = self.ndofs + joint.ndofs();
215 let rhs_copy_ndofs = rhs.ndofs - rhs_root_ndofs;
218
219 let base_assembly_id = self.velocities.len() - rhs_root_ndofs + joint.ndofs();
221 let base_internal_id = self.links.len() + 1;
222 let base_parent_id = self.links.len();
223
224 for link in &mut rhs.links.0[1..] {
225 link.assembly_id += base_assembly_id;
226 link.internal_id += base_internal_id;
227 link.parent_internal_id += base_parent_id;
228 }
229
230 {
232 rhs.links[0].joint = joint;
233 rhs.links[0].assembly_id = self.velocities.len();
234 rhs.links[0].internal_id = self.links.len();
235 rhs.links[0].parent_internal_id = parent;
236 }
237
238 self.grow_buffers(rhs_copy_ndofs + rhs.links[0].joint.ndofs(), rhs.links.len());
240
241 if rhs_copy_ndofs > 0 {
242 self.velocities
243 .rows_mut(rhs_copy_shift, rhs_copy_ndofs)
244 .copy_from(&rhs.velocities.rows(rhs_root_ndofs, rhs_copy_ndofs));
245 self.damping
246 .rows_mut(rhs_copy_shift, rhs_copy_ndofs)
247 .copy_from(&rhs.damping.rows(rhs_root_ndofs, rhs_copy_ndofs));
248 self.accelerations
249 .rows_mut(rhs_copy_shift, rhs_copy_ndofs)
250 .copy_from(&rhs.accelerations.rows(rhs_root_ndofs, rhs_copy_ndofs));
251 }
252
253 rhs.links[0]
254 .joint
255 .default_damping(&mut self.damping.rows_mut(base_assembly_id, rhs_root_ndofs));
256
257 self.links.append(&mut rhs.links);
258 self.ndofs = self.velocities.len();
259 self.workspace.resize(self.links.len(), self.ndofs);
260 }
261
262 pub fn self_contacts_enabled(&self) -> bool {
267 self.self_contacts_enabled
268 }
269
270 pub fn set_self_contacts_enabled(&mut self, enabled: bool) {
275 self.self_contacts_enabled = enabled;
276 }
277
278 pub fn inv_augmented_mass(&self) -> &LU<Real, Dyn, Dyn> {
280 &self.inv_augmented_mass
281 }
282
283 #[inline]
285 pub fn root(&self) -> &MultibodyLink {
286 &self.links[0]
287 }
288
289 #[inline]
291 pub fn root_mut(&mut self) -> &mut MultibodyLink {
292 &mut self.links[0]
293 }
294
295 #[inline]
299 pub fn link(&self, id: usize) -> Option<&MultibodyLink> {
300 self.links.get(id)
301 }
302
303 #[inline]
307 pub fn link_mut(&mut self, id: usize) -> Option<&mut MultibodyLink> {
308 self.links.get_mut(id)
309 }
310
311 pub fn num_links(&self) -> usize {
313 self.links.len()
314 }
315
316 pub fn links(&self) -> impl Iterator<Item = &MultibodyLink> {
320 self.links.iter()
321 }
322
323 pub fn links_mut(&mut self) -> impl Iterator<Item = &mut MultibodyLink> {
327 self.links.iter_mut()
328 }
329
330 #[inline]
332 pub fn damping(&self) -> &DVector {
333 &self.damping
334 }
335
336 #[inline]
338 pub fn damping_mut(&mut self) -> &mut DVector {
339 &mut self.damping
340 }
341
342 pub(crate) fn add_link(
343 &mut self,
344 parent: Option<usize>, dof: MultibodyJoint,
346 body: RigidBodyHandle,
347 ) -> &mut MultibodyLink {
348 assert!(
349 parent.is_none() || !self.links.is_empty(),
350 "Multibody::build_body: invalid parent id."
351 );
352
353 let assembly_id = self.velocities.len();
357 let internal_id = self.links.len();
358
359 let ndofs = dof.ndofs();
363 self.grow_buffers(ndofs, 1);
364 self.ndofs += ndofs;
365
366 dof.default_damping(&mut self.damping.rows_mut(assembly_id, ndofs));
370
371 let local_to_parent = dof.body_to_parent();
375 let local_to_world;
376
377 let parent_internal_id;
378 if let Some(parent) = parent {
379 parent_internal_id = parent;
380 let parent_link = &mut self.links[parent_internal_id];
381 local_to_world = parent_link.local_to_world * local_to_parent;
382 } else {
383 parent_internal_id = 0;
384 local_to_world = local_to_parent;
385 }
386
387 let rb = MultibodyLink::new(
388 body,
389 internal_id,
390 assembly_id,
391 parent_internal_id,
392 dof,
393 local_to_world,
394 local_to_parent,
395 );
396
397 self.links.push(rb);
398 self.workspace.resize(self.links.len(), self.ndofs);
399
400 &mut self.links[internal_id]
401 }
402
403 fn grow_buffers(&mut self, ndofs: usize, num_jacobians: usize) {
404 let len = self.velocities.len();
405 self.velocities.resize_vertically_mut(len + ndofs, 0.0);
406 self.damping.resize_vertically_mut(len + ndofs, 0.0);
407 self.accelerations.resize_vertically_mut(len + ndofs, 0.0);
408 self.body_jacobians
409 .extend((0..num_jacobians).map(|_| Jacobian::zeros(0)));
410 }
411
412 pub(crate) fn update_acceleration(&mut self, bodies: &RigidBodySet) {
413 if self.ndofs == 0 {
414 return; }
416
417 self.accelerations.fill(0.0);
418
419 for i in 0..self.links.len() {
421 let link = &self.links[i];
422 let rb = &bodies[link.rigid_body];
423
424 let mut acc = RigidBodyVelocity::zero();
425
426 if i != 0 {
427 let parent_id = link.parent_internal_id;
428 let parent_link = &self.links[parent_id];
429 let parent_rb = &bodies[parent_link.rigid_body];
430
431 acc += self.workspace.accs[parent_id];
432 acc.linvel += 2.0 * parent_rb.vels.angvel.gcross(link.joint_velocity.linvel);
435 #[cfg(feature = "dim3")]
436 {
437 acc.angvel += parent_rb.vels.angvel.cross(link.joint_velocity.angvel);
438 }
439
440 acc.linvel += parent_rb
441 .vels
442 .angvel
443 .gcross(parent_rb.vels.angvel.gcross(link.shift02));
444 acc.linvel += self.workspace.accs[parent_id].angvel.gcross(link.shift02);
445 }
446
447 acc.linvel += rb.vels.angvel.gcross(rb.vels.angvel.gcross(link.shift23));
448 acc.linvel += acc.angvel.gcross(link.shift23);
449
450 self.workspace.accs[i] = acc;
451
452 let gyroscopic;
455 let rb_inertia = rb.mprops.effective_angular_inertia();
456 let rb_mass = rb.mprops.effective_mass();
457
458 #[cfg(feature = "dim3")]
459 {
460 let angvel = rb.vels.angvel;
461 let inertia_times_angvel = rb_inertia * angvel;
462 gyroscopic = angvel.cross(inertia_times_angvel);
463 }
464 #[cfg(feature = "dim2")]
465 {
466 gyroscopic = 0.0;
467 }
468
469 let external_forces = Force::new(
470 rb.forces.force - rb_mass * acc.linvel,
471 rb.forces.torque - gyroscopic - rb_inertia * acc.angvel,
472 );
473 self.accelerations.gemv_tr(
474 1.0,
475 &self.body_jacobians[i],
476 external_forces.as_vector(),
477 1.0,
478 );
479 }
480
481 self.accelerations
482 .cmpy(-1.0, &self.damping, &self.velocities, 1.0);
483
484 self.augmented_mass_indices
485 .with_rearranged_rows_mut(&mut self.accelerations, |accs| {
486 self.acc_inv_augmented_mass.solve_mut(accs);
487 });
488 }
489
490 #[profiling::function]
492 pub(crate) fn update_dynamics(&mut self, dt: Real, bodies: &mut RigidBodySet) {
493 let link = &mut self.links[0];
498 let joint_velocity = link
499 .joint
500 .jacobian_mul_coordinates(&self.velocities.as_slice()[link.assembly_id..]);
501
502 link.joint_velocity = joint_velocity;
503 bodies.index_mut_internal(link.rigid_body).vels = link.joint_velocity;
504
505 for i in 1..self.links.len() {
506 let (link, parent_link) = self.links.get_mut_with_parent(i);
507 let rb = &bodies[link.rigid_body];
508 let parent_rb = &bodies[parent_link.rigid_body];
509
510 let joint_velocity = link
511 .joint
512 .jacobian_mul_coordinates(&self.velocities.as_slice()[link.assembly_id..]);
513 link.joint_velocity = joint_velocity.transformed(
514 &(parent_link.local_to_world.rotation * link.joint.data.local_frame1.rotation),
515 );
516 let mut new_rb_vels = parent_rb.vels + link.joint_velocity;
517 let shift = rb.mprops.world_com - parent_rb.mprops.world_com;
518 new_rb_vels.linvel += parent_rb.vels.angvel.gcross(shift);
519 new_rb_vels.linvel += link.joint_velocity.angvel.gcross(link.shift23);
520
521 bodies.index_mut_internal(link.rigid_body).vels = new_rb_vels;
522 }
523
524 self.update_inertias(dt, bodies);
528 }
529
530 fn update_body_jacobians(&mut self) {
531 for i in 0..self.links.len() {
532 let link = &self.links[i];
533
534 if self.body_jacobians[i].ncols() != self.ndofs {
535 self.body_jacobians[i] = Jacobian::zeros(self.ndofs);
537 }
538
539 let parent_to_world;
540
541 if i != 0 {
542 let parent_id = link.parent_internal_id;
543 let parent_link = &self.links[parent_id];
544 parent_to_world = parent_link.local_to_world;
545
546 let (link_j, parent_j) = self.body_jacobians.index_mut_const(i, parent_id);
547 link_j.copy_from(parent_j);
548
549 {
550 let mut link_j_v = link_j.fixed_rows_mut::<DIM>(0);
551 let parent_j_w = parent_j.fixed_rows::<ANG_DIM>(DIM);
552
553 let shift_tr = vect_to_na(link.shift02).gcross_matrix_tr();
554 link_j_v.gemm(1.0, &shift_tr, &parent_j_w, 1.0);
555 }
556 } else {
557 self.body_jacobians[i].fill(0.0);
558 parent_to_world = Pose::IDENTITY;
559 }
560
561 let ndofs = link.joint.ndofs();
562 let mut tmp = SMatrix::<Real, SPATIAL_DIM, SPATIAL_DIM>::zeros();
563 let mut link_joint_j = tmp.columns_mut(0, ndofs);
564 let mut link_j_part = self.body_jacobians[i].columns_mut(link.assembly_id, ndofs);
565 link.joint.jacobian(
566 &(parent_to_world.rotation * link.joint.data.local_frame1.rotation),
567 &mut link_joint_j,
568 );
569 link_j_part += link_joint_j;
570
571 {
572 let link_j = &mut self.body_jacobians[i];
573 let (mut link_j_v, link_j_w) =
574 link_j.rows_range_pair_mut(0..DIM, DIM..DIM + ANG_DIM);
575 let shift_tr = vect_to_na(link.shift23).gcross_matrix_tr();
576 link_j_v.gemm(1.0, &shift_tr, &link_j_w, 1.0);
577 }
578 }
579 }
580
581 fn update_inertias(&mut self, dt: Real, bodies: &RigidBodySet) {
582 if self.ndofs == 0 {
583 return; }
585
586 if self.augmented_mass.ncols() != self.ndofs {
587 self.augmented_mass = DMatrix::zeros(self.ndofs, self.ndofs);
589 self.acc_augmented_mass = DMatrix::zeros(self.ndofs, self.ndofs);
590 } else {
591 self.augmented_mass.fill(0.0);
592 self.acc_augmented_mass.fill(0.0);
593 }
594
595 self.augmented_mass_indices.clear();
596
597 if self.coriolis_v.len() != self.links.len() {
598 self.coriolis_v.resize(
599 self.links.len(),
600 OMatrix::<Real, Dim, Dyn>::zeros(self.ndofs),
601 );
602 self.coriolis_w.resize(
603 self.links.len(),
604 OMatrix::<Real, AngDim, Dyn>::zeros(self.ndofs),
605 );
606 self.i_coriolis_dt = Jacobian::zeros(self.ndofs);
607 }
608
609 let mut curr_assembly_id = 0;
610
611 for i in 0..self.links.len() {
612 let link = &self.links[i];
613 let rb = &bodies[link.rigid_body];
614 let rb_mass = rb.mprops.effective_mass();
615 let rb_inertia = rb.mprops.effective_angular_inertia().into_matrix();
616 let body_jacobian = &self.body_jacobians[i];
617
618 assert_eq!(
622 curr_assembly_id, link.assembly_id,
623 "Internal error: contiguity assumption on assembly_id does not hold."
624 );
625 curr_assembly_id += link.joint.ndofs();
626
627 if link.joint.kinematic {
628 for k in link.assembly_id..link.assembly_id + link.joint.ndofs() {
629 self.augmented_mass_indices.remove(k);
630 }
631 } else {
632 for k in link.assembly_id..link.assembly_id + link.joint.ndofs() {
633 self.augmented_mass_indices.keep(k);
634 }
635 }
636
637 #[allow(unused_mut)] let mut augmented_inertia = rb_inertia;
639
640 #[cfg(feature = "dim3")]
641 {
642 let gyroscopic_matrix = rb.vels.angvel.gcross_matrix() * rb_inertia
644 - (rb_inertia * rb.vels.angvel).gcross_matrix();
645
646 augmented_inertia += gyroscopic_matrix * dt;
647 }
648
649 #[allow(clippy::useless_conversion)] let rb_mass_matrix_wo_gyro = concat_rb_mass_matrix(rb_mass, rb_inertia.into());
653 #[allow(clippy::useless_conversion)] let rb_mass_matrix = concat_rb_mass_matrix(rb_mass, augmented_inertia.into());
655 self.augmented_mass
656 .quadform(1.0, &rb_mass_matrix_wo_gyro, body_jacobian, 1.0);
657 self.acc_augmented_mass
658 .quadform(1.0, &rb_mass_matrix, body_jacobian, 1.0);
659
660 let rb_j = &self.body_jacobians[i];
666 let rb_j_w = rb_j.fixed_rows::<ANG_DIM>(DIM);
667
668 let ndofs = link.joint.ndofs();
669
670 if i != 0 {
671 let parent_id = link.parent_internal_id;
672 let parent_link = &self.links[parent_id];
673 let parent_rb = &bodies[parent_link.rigid_body];
674 let parent_j = &self.body_jacobians[parent_id];
675 let parent_j_w = parent_j.fixed_rows::<ANG_DIM>(DIM);
676 let parent_w = SimdAngVector::<Real>::from(parent_rb.vels.angvel).gcross_matrix();
677
678 let (coriolis_v, parent_coriolis_v) = self.coriolis_v.index_mut2(i, parent_id);
679 let (coriolis_w, parent_coriolis_w) = self.coriolis_w.index_mut2(i, parent_id);
680
681 coriolis_v.copy_from(parent_coriolis_v);
682 coriolis_w.copy_from(parent_coriolis_w);
683
684 let shift_cross_tr = vect_to_na(link.shift02).gcross_matrix_tr();
686 coriolis_v.gemm(1.0, &shift_cross_tr, parent_coriolis_w, 1.0);
687
688 let dvel_cross = vect_to_na(
690 rb.vels.angvel.gcross(link.shift02) + 2.0 * link.joint_velocity.linvel,
691 )
692 .gcross_matrix_tr();
693 coriolis_v.gemm(1.0, &dvel_cross, &parent_j_w, 1.0);
694
695 coriolis_v.gemm(
697 1.0,
698 &vect_to_na(link.joint_velocity.linvel).gcross_matrix_tr(),
699 &parent_j_w,
700 1.0,
701 );
702 coriolis_v.gemm(1.0, &(parent_w * shift_cross_tr), &parent_j_w, 1.0);
703
704 #[cfg(feature = "dim3")]
705 {
706 let vel_wrt_joint_w = vect_to_na(link.joint_velocity.angvel).gcross_matrix();
707 coriolis_w.gemm(-1.0, &vel_wrt_joint_w, &parent_j_w, 1.0);
708 }
709
710 if !link.joint.kinematic {
712 let mut coriolis_v_part = coriolis_v.columns_mut(link.assembly_id, ndofs);
713
714 let mut tmp1 = SMatrix::<Real, SPATIAL_DIM, SPATIAL_DIM>::zeros();
715 let mut rb_joint_j = tmp1.columns_mut(0, ndofs);
716 link.joint.jacobian(
717 &(parent_link.local_to_world.rotation
718 * link.joint.data.local_frame1.rotation),
719 &mut rb_joint_j,
720 );
721
722 let rb_joint_j_v = rb_joint_j.fixed_rows::<DIM>(0);
723 coriolis_v_part.gemm(2.0, &parent_w, &rb_joint_j_v, 1.0);
724
725 #[cfg(feature = "dim3")]
726 {
727 let rb_joint_j_w = rb_joint_j.fixed_rows::<ANG_DIM>(DIM);
728 let mut coriolis_w_part = coriolis_w.columns_mut(link.assembly_id, ndofs);
729 coriolis_w_part.gemm(1.0, &parent_w, &rb_joint_j_w, 1.0);
730 }
731 }
732 } else {
733 self.coriolis_v[i].fill(0.0);
734 self.coriolis_w[i].fill(0.0);
735 }
736
737 let coriolis_v = &mut self.coriolis_v[i];
738 let coriolis_w = &mut self.coriolis_w[i];
739
740 {
741 let shift_cross_tr = vect_to_na(link.shift23).gcross_matrix_tr();
743 coriolis_v.gemm(1.0, &shift_cross_tr, coriolis_w, 1.0);
744
745 let dvel_cross = vect_to_na(rb.vels.angvel.gcross(link.shift23)).gcross_matrix_tr();
747 coriolis_v.gemm(1.0, &dvel_cross, &rb_j_w, 1.0);
748
749 coriolis_v.gemm(
751 1.0,
752 &(SimdAngVector::<Real>::from(rb.vels.angvel).gcross_matrix() * shift_cross_tr),
753 &rb_j_w,
754 1.0,
755 );
756 }
757
758 let coriolis_v = &mut self.coriolis_v[i];
759 let coriolis_w = &mut self.coriolis_w[i];
760
761 {
765 let mut i_coriolis_dt_v = self.i_coriolis_dt.fixed_rows_mut::<DIM>(0);
766 i_coriolis_dt_v.copy_from(coriolis_v);
767 let rb_mass_dt = vect_to_na(rb_mass * dt);
768 i_coriolis_dt_v
769 .column_iter_mut()
770 .for_each(|mut c| c.component_mul_assign(&rb_mass_dt));
771 }
772
773 #[cfg(feature = "dim2")]
774 {
775 let mut i_coriolis_dt_w = self.i_coriolis_dt.fixed_rows_mut::<ANG_DIM>(DIM);
776 i_coriolis_dt_w.zip_apply(coriolis_w, |o, x| *o = x * dt * rb_inertia);
778 }
779 #[cfg(feature = "dim3")]
780 {
781 let mut i_coriolis_dt_w = self.i_coriolis_dt.fixed_rows_mut::<ANG_DIM>(DIM);
782 i_coriolis_dt_w.gemm(dt, &mat_to_na(rb_inertia), coriolis_w, 0.0);
783 }
784
785 self.acc_augmented_mass
786 .gemm_tr(1.0, rb_j, &self.i_coriolis_dt, 1.0);
787 }
788
789 for i in 0..self.ndofs {
793 self.acc_augmented_mass[(i, i)] += self.damping[i] * dt;
794 self.augmented_mass[(i, i)] += self.damping[i] * dt;
795 }
796
797 let effective_dim = self
798 .augmented_mass_indices
799 .dim_after_removal(self.acc_augmented_mass.nrows());
800
801 self.augmented_mass_indices
804 .rearrange_columns(&mut self.acc_augmented_mass, true);
805 self.augmented_mass_indices
806 .rearrange_columns(&mut self.augmented_mass, true);
807
808 self.augmented_mass_indices
809 .rearrange_rows(&mut self.acc_augmented_mass, true);
810 self.augmented_mass_indices
811 .rearrange_rows(&mut self.augmented_mass, true);
812
813 self.acc_inv_augmented_mass = LU::new(
815 self.acc_augmented_mass
816 .view((0, 0), (effective_dim, effective_dim))
817 .into_owned(),
818 );
819 self.inv_augmented_mass = LU::new(
820 self.augmented_mass
821 .view((0, 0), (effective_dim, effective_dim))
822 .into_owned(),
823 );
824 }
825
826 #[inline]
828 pub fn joint_velocity(&self, link: &MultibodyLink) -> DVectorView<'_, Real> {
829 let ndofs = link.joint().ndofs();
830 DVectorView::from_slice(
831 &self.velocities.as_slice()[link.assembly_id..link.assembly_id + ndofs],
832 ndofs,
833 )
834 }
835
836 #[inline]
838 pub fn generalized_acceleration(&self) -> DVectorView<'_, Real> {
839 self.accelerations.rows(0, self.ndofs)
840 }
841
842 #[inline]
844 pub fn generalized_velocity(&self) -> DVectorView<'_, Real> {
845 self.velocities.rows(0, self.ndofs)
846 }
847
848 #[inline]
850 pub fn body_jacobian(&self, link_id: usize) -> &Jacobian<Real> {
851 &self.body_jacobians[link_id]
852 }
853
854 #[inline]
856 pub fn generalized_velocity_mut(&mut self) -> DVectorViewMut<'_, Real> {
857 self.velocities.rows_mut(0, self.ndofs)
858 }
859
860 #[inline]
861 pub(crate) fn integrate(&mut self, dt: Real) {
862 for rb in self.links.iter_mut() {
863 rb.joint
864 .integrate(dt, &self.velocities.as_slice()[rb.assembly_id..])
865 }
866 }
867
868 pub fn apply_displacements(&mut self, disp: &[Real]) {
873 for link in self.links.iter_mut() {
874 link.joint.apply_displacement(&disp[link.assembly_id..])
875 }
876 }
877
878 pub(crate) fn update_root_type(&mut self, bodies: &RigidBodySet, take_body_pose: bool) {
879 if let Some(rb) = bodies.get(self.links[0].rigid_body) {
880 if rb.is_dynamic() != self.root_is_dynamic {
881 let root_pose = if take_body_pose {
882 *rb.position()
883 } else {
884 self.links[0].local_to_world
885 };
886
887 if rb.is_dynamic() {
888 let free_joint = MultibodyJoint::free(root_pose);
889 let prev_root_ndofs = self.links[0].joint().ndofs();
890 self.links[0].joint = free_joint;
891 self.links[0].assembly_id = 0;
892 self.ndofs += SPATIAL_DIM;
893
894 self.velocities = self.velocities.clone().insert_rows(0, SPATIAL_DIM, 0.0);
895 self.damping = self.damping.clone().insert_rows(0, SPATIAL_DIM, 0.0);
896 self.accelerations =
897 self.accelerations.clone().insert_rows(0, SPATIAL_DIM, 0.0);
898
899 for link in &mut self.links[1..] {
900 link.assembly_id += SPATIAL_DIM - prev_root_ndofs;
901 }
902 } else {
903 assert!(self.velocities.len() >= SPATIAL_DIM);
904 assert!(self.damping.len() >= SPATIAL_DIM);
905 assert!(self.accelerations.len() >= SPATIAL_DIM);
906
907 let fixed_joint = MultibodyJoint::fixed(root_pose);
908 let prev_root_ndofs = self.links[0].joint().ndofs();
909 self.links[0].joint = fixed_joint;
910 self.links[0].assembly_id = 0;
911 self.ndofs -= prev_root_ndofs;
912
913 if self.ndofs == 0 {
914 self.velocities = DVector::zeros(0);
915 self.damping = DVector::zeros(0);
916 self.accelerations = DVector::zeros(0);
917 } else {
918 self.velocities =
919 self.velocities.index((prev_root_ndofs.., 0)).into_owned();
920 self.damping = self.damping.index((prev_root_ndofs.., 0)).into_owned();
921 self.accelerations = self
922 .accelerations
923 .index((prev_root_ndofs.., 0))
924 .into_owned();
925 }
926
927 for link in &mut self.links[1..] {
928 link.assembly_id -= prev_root_ndofs;
929 }
930 }
931
932 self.root_is_dynamic = rb.is_dynamic();
933 }
934
935 if take_body_pose {
937 if self.links[0].joint.data.locked_axes.is_empty() {
938 self.links[0].joint.set_free_pos(*rb.position());
939 } else {
940 self.links[0].joint.data.local_frame1 = *rb.position();
941 }
942 }
943 }
944 }
945
946 pub fn update_rigid_bodies(&self, bodies: &mut RigidBodySet, update_mass_properties: bool) {
951 self.update_rigid_bodies_internal(bodies, update_mass_properties, false, true)
952 }
953
954 pub(crate) fn update_rigid_bodies_internal(
955 &self,
956 bodies: &mut RigidBodySet,
957 update_mass_properties: bool,
958 update_next_positions_only: bool,
959 change_tracking: bool,
960 ) {
961 for link in self.links.iter() {
963 let rb = if change_tracking {
964 bodies.get_mut_internal_with_modification_tracking(link.rigid_body)
965 } else {
966 bodies.get_mut_internal(link.rigid_body)
967 };
968
969 if let Some(rb) = rb {
970 rb.pos.next_position = link.local_to_world;
971
972 if !update_next_positions_only {
973 rb.pos.position = link.local_to_world;
974 }
975
976 if update_mass_properties {
977 rb.mprops
978 .update_world_mass_properties(rb.body_type, &link.local_to_world);
979 }
980 }
981 }
982 }
983
984 pub fn forward_kinematics(
1005 &mut self,
1006 bodies: &RigidBodySet,
1007 read_root_pose_from_rigid_body: bool,
1008 ) {
1009 self.update_root_type(bodies, read_root_pose_from_rigid_body);
1011
1012 {
1014 let link = &mut self.links[0];
1015 link.local_to_parent = link.joint.body_to_parent();
1016 link.local_to_world = link.local_to_parent;
1017 }
1018
1019 for i in 1..self.links.len() {
1021 let (link, parent_link) = self.links.get_mut_with_parent(i);
1022
1023 link.local_to_parent = link.joint.body_to_parent();
1024 link.local_to_world = parent_link.local_to_world * link.local_to_parent;
1025
1026 {
1027 let parent_rb = &bodies[parent_link.rigid_body];
1028 let link_rb = &bodies[link.rigid_body];
1029 let c0 = parent_link.local_to_world * parent_rb.mprops.local_mprops.local_com;
1030 let c2 =
1031 link.local_to_world * Vector::from(link.joint.data.local_frame2.translation);
1032 let c3 = link.local_to_world * link_rb.mprops.local_mprops.local_com;
1033
1034 link.shift02 = c2 - c0;
1035 link.shift23 = c3 - c2;
1036 }
1037
1038 assert_eq!(
1039 bodies[link.rigid_body].body_type,
1040 RigidBodyType::Dynamic,
1041 "A rigid-body that is not at the root of a multibody must be dynamic."
1042 );
1043 }
1044
1045 self.update_body_jacobians();
1049 }
1050
1051 pub fn kinematic_branch(&self, link_id: usize) -> Vec<usize> {
1053 let mut branch = vec![]; let mut curr_id = Some(link_id);
1055
1056 while let Some(id) = curr_id {
1057 branch.push(id);
1058 curr_id = self.links[id].parent_id();
1059 }
1060
1061 branch.reverse();
1062 branch
1063 }
1064
1065 pub fn forward_kinematics_single_link(
1073 &self,
1074 bodies: &RigidBodySet,
1075 link_id: usize,
1076 displacement: Option<&[Real]>,
1077 out_jacobian: Option<&mut Jacobian<Real>>,
1078 ) -> Pose {
1079 let branch = self.kinematic_branch(link_id);
1080 self.forward_kinematics_single_branch(bodies, &branch, displacement, out_jacobian)
1081 }
1082
1083 #[profiling::function]
1103 pub fn forward_kinematics_single_branch(
1104 &self,
1105 bodies: &RigidBodySet,
1106 branch: &[usize],
1107 displacement: Option<&[Real]>,
1108 mut out_jacobian: Option<&mut Jacobian<Real>>,
1109 ) -> Pose {
1110 if let Some(out_jacobian) = out_jacobian.as_deref_mut() {
1111 if out_jacobian.ncols() != self.ndofs {
1112 *out_jacobian = Jacobian::zeros(self.ndofs);
1113 } else {
1114 out_jacobian.fill(0.0);
1115 }
1116 }
1117
1118 let mut parent_link: Option<MultibodyLink> = None;
1119
1120 for i in branch {
1121 let mut link = self.links[*i];
1122
1123 if let Some(displacement) = displacement {
1124 link.joint
1125 .apply_displacement(&displacement[link.assembly_id..]);
1126 }
1127
1128 let parent_to_world;
1129
1130 if let Some(parent_link) = parent_link {
1131 link.local_to_parent = link.joint.body_to_parent();
1132 link.local_to_world = parent_link.local_to_world * link.local_to_parent;
1133
1134 {
1135 let parent_rb = &bodies[parent_link.rigid_body];
1136 let link_rb = &bodies[link.rigid_body];
1137 let c0 = parent_link.local_to_world * parent_rb.mprops.local_mprops.local_com;
1138 let c2 = link.local_to_world
1139 * Vector::from(link.joint.data.local_frame2.translation);
1140 let c3 = link.local_to_world * link_rb.mprops.local_mprops.local_com;
1141
1142 link.shift02 = c2 - c0;
1143 link.shift23 = c3 - c2;
1144 }
1145
1146 parent_to_world = parent_link.local_to_world;
1147
1148 if let Some(out_jacobian) = out_jacobian.as_deref_mut() {
1149 let (mut link_j_v, parent_j_w) =
1150 out_jacobian.rows_range_pair_mut(0..DIM, DIM..DIM + ANG_DIM);
1151 let shift_tr = vect_to_na(link.shift02).gcross_matrix_tr();
1152 link_j_v.gemm(1.0, &shift_tr, &parent_j_w, 1.0);
1153 }
1154 } else {
1155 link.local_to_parent = link.joint.body_to_parent();
1156 link.local_to_world = link.local_to_parent;
1157 parent_to_world = Pose::IDENTITY;
1158 }
1159
1160 if let Some(out_jacobian) = out_jacobian.as_deref_mut() {
1161 let ndofs = link.joint.ndofs();
1162 let mut tmp = SMatrix::<Real, SPATIAL_DIM, SPATIAL_DIM>::zeros();
1163 let mut link_joint_j = tmp.columns_mut(0, ndofs);
1164 let mut link_j_part = out_jacobian.columns_mut(link.assembly_id, ndofs);
1165 link.joint.jacobian(
1166 &(parent_to_world.rotation * link.joint.data.local_frame1.rotation),
1167 &mut link_joint_j,
1168 );
1169 link_j_part += link_joint_j;
1170
1171 {
1172 let (mut link_j_v, link_j_w) =
1173 out_jacobian.rows_range_pair_mut(0..DIM, DIM..DIM + ANG_DIM);
1174 let shift_tr = vect_to_na(link.shift23).gcross_matrix_tr();
1175 link_j_v.gemm(1.0, &shift_tr, &link_j_w, 1.0);
1176 }
1177 }
1178
1179 parent_link = Some(link);
1180 }
1181
1182 parent_link
1183 .map(|link| link.local_to_world)
1184 .unwrap_or(Pose::IDENTITY)
1185 }
1186
1187 #[inline]
1189 pub fn ndofs(&self) -> usize {
1190 self.ndofs
1191 }
1192
1193 pub(crate) fn fill_jacobians(
1194 &self,
1195 link_id: usize,
1196 unit_force: Vector,
1197 unit_torque: AngVector,
1198 j_id: &mut usize,
1199 jacobians: &mut DVector,
1200 ) -> (Real, Real) {
1201 if self.ndofs == 0 {
1202 return (0.0, 0.0);
1203 }
1204
1205 let wj_id = *j_id + self.ndofs;
1206 let force = Force {
1207 linear: unit_force,
1208 #[cfg(feature = "dim2")]
1209 angular: unit_torque,
1210 #[cfg(feature = "dim3")]
1211 angular: unit_torque,
1212 };
1213
1214 let link = &self.links[link_id];
1215 let mut out_j = jacobians.rows_mut(*j_id, self.ndofs);
1216 self.body_jacobians[link.internal_id].tr_mul_to(force.as_vector(), &mut out_j);
1217
1218 for i in 0..self.ndofs {
1220 jacobians[wj_id + i] = jacobians[*j_id + i];
1221 }
1222
1223 {
1224 let mut out_invm_j = jacobians.rows_mut(wj_id, self.ndofs);
1225 self.augmented_mass_indices
1226 .with_rearranged_rows_mut(&mut out_invm_j, |out_invm_j| {
1227 self.inv_augmented_mass.solve_mut(out_invm_j);
1228 });
1229 }
1230
1231 let j = jacobians.rows(*j_id, self.ndofs);
1232 let invm_j = jacobians.rows(wj_id, self.ndofs);
1233 *j_id += self.ndofs * 2;
1234
1235 (j.dot(&invm_j), j.dot(&self.generalized_velocity()))
1236 }
1237
1238 #[cfg(feature = "parallel")]
1246 #[inline]
1247 #[allow(dead_code)] pub(crate) fn num_active_internal_constraints_and_jacobian_lines(&self) -> (usize, usize) {
1249 let num_constraints: usize = self
1250 .links
1251 .iter()
1252 .map(|l| l.joint().num_velocity_constraints())
1253 .sum();
1254 (num_constraints, num_constraints)
1255 }
1256}
1257
1258#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1259#[derive(Clone, Debug)]
1260struct IndexSequence {
1261 first_to_remove: usize,
1262 index_map: Vec<usize>,
1263}
1264
1265impl IndexSequence {
1266 fn new() -> Self {
1267 Self {
1268 first_to_remove: usize::MAX,
1269 index_map: vec![],
1270 }
1271 }
1272
1273 fn clear(&mut self) {
1274 self.first_to_remove = usize::MAX;
1275 self.index_map.clear();
1276 }
1277
1278 fn keep(&mut self, i: usize) {
1279 if self.first_to_remove == usize::MAX {
1280 return;
1283 }
1284
1285 self.index_map.push(i);
1286 }
1287
1288 fn remove(&mut self, i: usize) {
1289 if self.first_to_remove == usize::MAX {
1290 self.first_to_remove = i;
1291 }
1292 }
1293
1294 fn dim_after_removal(&self, original_dim: usize) -> usize {
1295 if self.first_to_remove == usize::MAX {
1296 original_dim
1297 } else {
1298 self.first_to_remove + self.index_map.len()
1299 }
1300 }
1301
1302 fn rearrange_columns<R: na::Dim, C: na::Dim, S: StorageMut<Real, R, C>>(
1303 &self,
1304 mat: &mut na::Matrix<Real, R, C, S>,
1305 clear_removed: bool,
1306 ) {
1307 if self.first_to_remove == usize::MAX {
1308 return;
1310 }
1311
1312 for (target_shift, source) in self.index_map.iter().enumerate() {
1313 let target = self.first_to_remove + target_shift;
1314 let (mut target_col, source_col) = mat.columns_range_pair_mut(target, *source);
1315 target_col.copy_from(&source_col);
1316 }
1317
1318 if clear_removed {
1319 mat.columns_range_mut(self.first_to_remove + self.index_map.len()..)
1320 .fill(0.0);
1321 }
1322 }
1323
1324 fn rearrange_rows<R: na::Dim, C: na::Dim, S: StorageMut<Real, R, C>>(
1325 &self,
1326 mat: &mut na::Matrix<Real, R, C, S>,
1327 clear_removed: bool,
1328 ) {
1329 if self.first_to_remove == usize::MAX {
1330 return;
1332 }
1333
1334 for mut col in mat.column_iter_mut() {
1335 for (target_shift, source) in self.index_map.iter().enumerate() {
1336 let target = self.first_to_remove + target_shift;
1337 col[target] = col[*source];
1338 }
1339
1340 if clear_removed {
1341 col.rows_range_mut(self.first_to_remove + self.index_map.len()..)
1342 .fill(0.0);
1343 }
1344 }
1345 }
1346
1347 fn inv_rearrange_rows<R: na::Dim, C: na::Dim, S: StorageMut<Real, R, C>>(
1348 &self,
1349 mat: &mut na::Matrix<Real, R, C, S>,
1350 ) {
1351 if self.first_to_remove == usize::MAX {
1352 return;
1354 }
1355
1356 for mut col in mat.column_iter_mut() {
1357 for (target_shift, source) in self.index_map.iter().enumerate().rev() {
1358 let target = self.first_to_remove + target_shift;
1359 col[*source] = col[target];
1360 col[target] = 0.0;
1361 }
1362 }
1363 }
1364
1365 fn with_rearranged_rows_mut<C: na::Dim, S: StorageMut<Real, Dyn, C>>(
1366 &self,
1367 mat: &mut na::Matrix<Real, Dyn, C, S>,
1368 mut f: impl FnMut(&mut na::MatrixViewMut<Real, Dyn, C, S::RStride, S::CStride>),
1369 ) {
1370 self.rearrange_rows(mat, true);
1371 let effective_dim = self.dim_after_removal(mat.nrows());
1372 if effective_dim > 0 {
1373 f(&mut mat.rows_mut(0, effective_dim));
1374 }
1375 self.inv_rearrange_rows(mat);
1376 }
1377}
1378
1379#[cfg(test)]
1380mod test {
1381 use super::IndexSequence;
1382 use crate::dynamics::{ImpulseJointSet, IslandManager};
1383 #[cfg(feature = "dim3")]
1384 use crate::math::Vector;
1385 use crate::math::{Real, SPATIAL_DIM};
1386 use crate::prelude::{
1387 ColliderSet, MultibodyJointHandle, MultibodyJointSet, RevoluteJoint, RigidBodyBuilder,
1388 RigidBodySet,
1389 };
1390 use na::{DVector, RowDVector};
1391
1392 #[test]
1393 fn test_multibody_append() {
1394 let mut bodies = RigidBodySet::new();
1395 let mut joints = MultibodyJointSet::new();
1396
1397 let a = bodies.insert(RigidBodyBuilder::dynamic());
1398 let b = bodies.insert(RigidBodyBuilder::dynamic());
1399 let c = bodies.insert(RigidBodyBuilder::dynamic());
1400 let d = bodies.insert(RigidBodyBuilder::dynamic());
1401
1402 #[cfg(feature = "dim2")]
1403 let joint = RevoluteJoint::new();
1404 #[cfg(feature = "dim3")]
1405 let joint = RevoluteJoint::new(Vector::X);
1406
1407 let mb_handle = joints.insert(a, b, joint, true).unwrap();
1408 joints.insert(c, d, joint, true).unwrap();
1409 joints.insert(b, c, joint, true).unwrap();
1410
1411 assert_eq!(joints.get(mb_handle).unwrap().0.ndofs, SPATIAL_DIM + 3);
1412 }
1413
1414 #[test]
1415 fn test_multibody_insert() {
1416 let mut rnd = oorandom::Rand32::new(1234);
1417
1418 for k in 0..10 {
1419 let mut bodies = RigidBodySet::new();
1420 let mut multibody_joints = MultibodyJointSet::new();
1421
1422 let num_links = 100;
1423 let mut handles = vec![];
1424
1425 for _ in 0..num_links {
1426 handles.push(bodies.insert(RigidBodyBuilder::dynamic()));
1427 }
1428
1429 let mut insertion_id: Vec<_> = (0..num_links - 1).collect();
1430
1431 #[cfg(feature = "dim2")]
1432 let joint = RevoluteJoint::new();
1433 #[cfg(feature = "dim3")]
1434 let joint = RevoluteJoint::new(Vector::X);
1435
1436 match k {
1437 0 => {} 1 => {
1439 insertion_id.reverse();
1441 }
1442 _ => {
1443 for l in 0..num_links - 1 {
1446 insertion_id.swap(l, rnd.rand_range(0..num_links as u32 - 1) as usize);
1447 }
1448 }
1449 }
1450
1451 let mut mb_handle = MultibodyJointHandle::invalid();
1452 for i in insertion_id {
1453 mb_handle = multibody_joints
1454 .insert(handles[i], handles[i + 1], joint, true)
1455 .unwrap();
1456 }
1457
1458 assert_eq!(
1459 multibody_joints.get(mb_handle).unwrap().0.ndofs,
1460 SPATIAL_DIM + num_links - 1
1461 );
1462 }
1463 }
1464
1465 #[test]
1466 fn test_multibody_remove() {
1467 let mut rnd = oorandom::Rand32::new(1234);
1468
1469 for k in 0..10 {
1470 let mut bodies = RigidBodySet::new();
1471 let mut multibody_joints = MultibodyJointSet::new();
1472 let mut colliders = ColliderSet::new();
1473 let mut impulse_joints = ImpulseJointSet::new();
1474 let mut islands = IslandManager::new();
1475
1476 let num_links = 100;
1477 let mut handles = vec![];
1478
1479 for _ in 0..num_links {
1480 handles.push(bodies.insert(RigidBodyBuilder::dynamic()));
1481 }
1482
1483 #[cfg(feature = "dim2")]
1484 let joint = RevoluteJoint::new();
1485 #[cfg(feature = "dim3")]
1486 let joint = RevoluteJoint::new(Vector::X);
1487
1488 for i in 0..num_links - 1 {
1489 multibody_joints
1490 .insert(handles[i], handles[i + 1], joint, true)
1491 .unwrap();
1492 }
1493
1494 match k {
1495 0 => {} 1 => {
1497 handles.reverse();
1499 }
1500 _ => {
1501 for l in 0..num_links {
1504 handles.swap(l, rnd.rand_range(0..num_links as u32) as usize);
1505 }
1506 }
1507 }
1508
1509 for handle in handles {
1510 bodies.remove(
1511 handle,
1512 &mut islands,
1513 &mut colliders,
1514 &mut impulse_joints,
1515 &mut multibody_joints,
1516 true,
1517 );
1518 }
1519 }
1520 }
1521
1522 fn test_sequence() -> IndexSequence {
1523 let mut seq = IndexSequence::new();
1524 seq.remove(2);
1525 seq.remove(3);
1526 seq.remove(4);
1527 seq.keep(5);
1528 seq.keep(6);
1529 seq.remove(7);
1530 seq.keep(8);
1531 seq
1532 }
1533
1534 #[test]
1535 fn index_sequence_rearrange_columns() {
1536 let seq = test_sequence();
1537 let mut vec = RowDVector::from_fn(10, |_, c| c as Real);
1538 seq.rearrange_columns(&mut vec, true);
1539 assert_eq!(
1540 vec,
1541 RowDVector::from(vec![0.0, 1.0, 5.0, 6.0, 8.0, 0.0, 0.0, 0.0, 0.0, 0.0])
1542 );
1543 }
1544
1545 #[test]
1546 fn index_sequence_rearrange_rows() {
1547 let seq = test_sequence();
1548 let mut vec = DVector::from_fn(10, |r, _| r as Real);
1549 seq.rearrange_rows(&mut vec, true);
1550 assert_eq!(
1551 vec,
1552 DVector::from(vec![0.0, 1.0, 5.0, 6.0, 8.0, 0.0, 0.0, 0.0, 0.0, 0.0])
1553 );
1554 seq.inv_rearrange_rows(&mut vec);
1555 assert_eq!(
1556 vec,
1557 DVector::from(vec![0.0, 1.0, 0.0, 0.0, 0.0, 5.0, 6.0, 0.0, 8.0, 0.0])
1558 );
1559 }
1560
1561 #[test]
1562 fn index_sequence_with_rearranged_rows_mut() {
1563 let seq = test_sequence();
1564 let mut vec = DVector::from_fn(10, |r, _| r as Real);
1565 seq.with_rearranged_rows_mut(&mut vec, |v| {
1566 assert_eq!(v.len(), 5);
1567 assert_eq!(*v, DVector::from(vec![0.0, 1.0, 5.0, 6.0, 8.0]));
1568 *v *= 10.0;
1569 });
1570 assert_eq!(
1571 vec,
1572 DVector::from(vec![0.0, 10.0, 0.0, 0.0, 0.0, 50.0, 60.0, 0.0, 80.0, 0.0])
1573 );
1574 }
1575}