1use either::Either;
2use std::any::Any;
3use std::collections::{HashMap, HashSet};
4use std::iter;
5use std::marker::PhantomData;
6use std::ops::{AddAssign, SubAssign};
7
8#[cfg(feature = "dim3")]
9use na::Vector2;
10use na::{
11 self, Cholesky, DMatrix, DVector, DVectorSlice, DVectorSliceMut, Dynamic, RealField, Unit,
12};
13#[cfg(feature = "dim3")]
14use ncollide::procedural;
15#[cfg(feature = "dim3")]
16use ncollide::shape::TriMesh;
17use ncollide::shape::{DeformationsType, Polyline};
18use ncollide::utils::DeterministicState;
19
20use crate::math::{
21 Force, ForceType, Inertia, Isometry, Point, Translation, Vector, Velocity, DIM,
22};
23use crate::object::fem_helper;
24use crate::object::{
25 ActivationStatus, Body, BodyPart, BodyStatus, BodyUpdateStatus, FiniteElementIndices,
26};
27use crate::solver::{ForceDirection, IntegrationParameters};
28
29use crate::utils::{UserData, UserDataBox};
30
31#[derive(Clone)]
33pub struct MassSpringElement<N: RealField + Copy> {
34 indices: FiniteElementIndices,
35 phantom: PhantomData<N>,
36}
37
38#[derive(Clone)]
39struct Spring<N: RealField + Copy> {
40 nodes: (usize, usize),
41 dir: Unit<Vector<N>>,
43 length: N,
44 rest_length: N,
45 stiffness: N,
46 damping_ratio: N,
47 plastic_strain: N,
48}
49
50impl<N: RealField + Copy> Spring<N> {
51 fn from_positions(
52 nodes: (usize, usize),
53 positions: &[N],
54 stiffness: N,
55 damping_ratio: N,
56 ) -> Self {
57 let p0 = Point::from_slice(&positions[nodes.0..nodes.0 + DIM]);
58 let p1 = Point::from_slice(&positions[nodes.1..nodes.1 + DIM]);
59 let rest_length = na::distance(&p0, &p1);
60
61 Spring {
62 nodes,
63 dir: Unit::try_new(p1 - p0, N::zero()).unwrap_or_else(Vector::y_axis),
64 length: rest_length,
65 rest_length,
66 stiffness,
67 damping_ratio,
68 plastic_strain: N::zero(),
69 }
70 }
71}
72
73pub struct MassSpringSystem<N: RealField + Copy> {
75 springs: Vec<Spring<N>>,
76 elements: Vec<MassSpringElement<N>>,
77 kinematic_nodes: DVector<bool>,
78 positions: DVector<N>,
79 velocities: DVector<N>,
80 accelerations: DVector<N>,
81 forces: DVector<N>,
82 augmented_mass: DMatrix<N>,
83 inv_augmented_mass: Cholesky<N, Dynamic>,
84
85 workspace: DVector<N>,
86
87 companion_id: usize,
88 gravity_enabled: bool,
89 activation: ActivationStatus<N>,
90 status: BodyStatus,
91 update_status: BodyUpdateStatus,
92 mass: N,
93 node_mass: N,
94
95 plasticity_threshold: N,
96 plasticity_creep: N,
97 plasticity_max_force: N,
98
99 user_data: Option<Box<dyn Any + Send + Sync>>,
100}
101
102fn key(i: usize, j: usize) -> (usize, usize) {
103 if i <= j {
104 (i, j)
105 } else {
106 (j, i)
107 }
108}
109
110impl<N: RealField + Copy> MassSpringSystem<N> {
111 #[cfg(feature = "dim3")]
115 fn from_trimesh(mesh: &TriMesh<N>, mass: N, stiffness: N, damping_ratio: N) -> Self {
116 let ndofs = mesh.points().len() * DIM;
117 let mut springs = HashMap::with_hasher(DeterministicState::new());
118 let mut elements = Vec::with_capacity(mesh.faces().len());
119 let mut positions = DVector::zeros(ndofs);
120
121 for (i, pos) in positions.as_mut_slice().chunks_mut(DIM).enumerate() {
122 pos.copy_from_slice(mesh.points()[i].coords.as_slice())
123 }
124
125 for face in mesh.faces() {
126 let idx = face.indices * DIM;
127 let elt = MassSpringElement {
128 indices: FiniteElementIndices::Triangle(idx),
129 phantom: PhantomData,
130 };
131
132 elements.push(elt);
133
134 let _ = springs.entry(key(idx.x, idx.y)).or_insert_with(|| {
135 Spring::from_positions(
136 (idx.x, idx.y),
137 positions.as_slice(),
138 stiffness,
139 damping_ratio,
140 )
141 });
142 let _ = springs.entry(key(idx.y, idx.z)).or_insert_with(|| {
143 Spring::from_positions(
144 (idx.y, idx.z),
145 positions.as_slice(),
146 stiffness,
147 damping_ratio,
148 )
149 });
150 let _ = springs.entry(key(idx.z, idx.x)).or_insert_with(|| {
151 Spring::from_positions(
152 (idx.z, idx.x),
153 positions.as_slice(),
154 stiffness,
155 damping_ratio,
156 )
157 });
158 }
159
160 let node_mass = mass / na::convert((ndofs / DIM) as f64);
161
162 MassSpringSystem {
163 springs: springs.values().cloned().collect(),
164 elements,
165 kinematic_nodes: DVector::repeat(ndofs / DIM, false),
166 positions,
167 velocities: DVector::zeros(ndofs),
168 accelerations: DVector::zeros(ndofs),
169 forces: DVector::zeros(ndofs),
170 workspace: DVector::zeros(ndofs),
171 augmented_mass: DMatrix::zeros(ndofs, ndofs),
172 inv_augmented_mass: Cholesky::new(DMatrix::zeros(0, 0)).unwrap(),
173 companion_id: 0,
174 activation: ActivationStatus::new_active(),
175 status: BodyStatus::Dynamic,
176 update_status: BodyUpdateStatus::all(),
177 mass,
178 node_mass,
179 plasticity_max_force: N::zero(),
180 plasticity_creep: N::zero(),
181 plasticity_threshold: N::zero(),
182 gravity_enabled: true,
183 user_data: None,
184 }
185 }
186
187 user_data_accessors!();
188
189 fn from_polyline(polyline: &Polyline<N>, mass: N, stiffness: N, damping_ratio: N) -> Self {
191 let ndofs = polyline.points().len() * DIM;
192 let mut springs = HashMap::with_hasher(DeterministicState::new());
193 let mut elements = Vec::with_capacity(polyline.edges().len());
194 let mut positions = DVector::zeros(ndofs);
195
196 for (i, pos) in positions.as_mut_slice().chunks_mut(DIM).enumerate() {
197 pos.copy_from_slice(polyline.points()[i].coords.as_slice())
198 }
199
200 for (_i, edge) in polyline.edges().iter().enumerate() {
201 let idx = edge.indices * DIM;
202 let elt = MassSpringElement {
203 indices: FiniteElementIndices::Segment(idx),
204 phantom: PhantomData,
205 };
206
207 elements.push(elt);
208
209 let _ = springs.entry(key(idx.x, idx.y)).or_insert_with(|| {
210 Spring::from_positions(
211 (idx.x, idx.y),
212 positions.as_slice(),
213 stiffness,
214 damping_ratio,
215 )
216 });
217 }
218
219 let node_mass = mass / na::convert((ndofs / DIM) as f64);
220 MassSpringSystem {
223 springs: springs.values().cloned().collect(),
224 kinematic_nodes: DVector::repeat(ndofs / DIM, false),
225 elements,
226 positions,
227 velocities: DVector::zeros(ndofs),
228 accelerations: DVector::zeros(ndofs),
229 forces: DVector::zeros(ndofs),
230 workspace: DVector::zeros(ndofs),
231 augmented_mass: DMatrix::zeros(ndofs, ndofs),
232 inv_augmented_mass: Cholesky::new(DMatrix::zeros(0, 0)).unwrap(),
233 companion_id: 0,
234 activation: ActivationStatus::new_active(),
235 status: BodyStatus::Dynamic,
236 update_status: BodyUpdateStatus::all(),
237 gravity_enabled: true,
238 mass,
239 node_mass,
240 plasticity_max_force: N::zero(),
241 plasticity_creep: N::zero(),
242 plasticity_threshold: N::zero(),
243 user_data: None,
244 }
245 }
246
247 #[cfg(feature = "dim3")]
249 pub fn quad(
250 transform: &Isometry<N>,
251 extents: &Vector2<N>,
252 nx: usize,
253 ny: usize,
254 mass: N,
255 stiffness: N,
256 damping_ratio: N,
257 ) -> Self {
258 let mesh = procedural::quad(extents.x, extents.y, nx, ny);
259 let vertices = mesh.coords.iter().map(|pt| transform * pt).collect();
260 let indices = mesh
261 .indices
262 .unwrap_unified()
263 .into_iter()
264 .map(|tri| na::convert(tri))
265 .collect();
266 let trimesh = TriMesh::new(vertices, indices, None);
267 Self::from_trimesh(&trimesh, mass, stiffness, damping_ratio)
268 }
269
270 pub fn mass(&self) -> N {
272 self.mass
273 }
274
275 pub fn num_nodes(&self) -> usize {
277 self.positions.len() / DIM
278 }
279
280 pub fn generate_neighbor_springs(&mut self, stiffness: N, damping_ratio: N) {
285 self.update_status.set_local_inertia_changed(true);
286
287 let mut neighbor_list: Vec<_> = iter::repeat(Vec::new())
288 .take(self.positions.len() / DIM)
289 .collect();
290 let mut existing_springs = HashSet::with_hasher(DeterministicState::new());
291
292 for spring in &self.springs {
294 let key = key(spring.nodes.0, spring.nodes.1);
295 neighbor_list[key.0 / DIM].push(key.1 / DIM);
296 let _ = existing_springs.insert(key);
297 }
298
299 for (i, nbhs) in neighbor_list.iter().enumerate() {
301 for nbh in nbhs {
302 for transitive_nbh in &neighbor_list[*nbh] {
303 let key = key(i * DIM, *transitive_nbh * DIM);
304
305 if existing_springs.insert(key) {
306 let spring = Spring::from_positions(
307 key,
308 self.positions.as_slice(),
309 stiffness,
310 damping_ratio,
311 );
312 self.springs.push(spring);
313 }
314 }
315 }
316 }
317 }
318
319 pub fn add_spring(&mut self, node1: usize, node2: usize, stiffness: N, damping_ratio: N) {
321 assert!(
322 node1 < self.positions.len() / DIM,
323 "Node index out of bounds."
324 );
325 assert!(
326 node2 < self.positions.len() / DIM,
327 "Node index out of bounds."
328 );
329 self.update_status.set_local_inertia_changed(true);
330 let key = key(node1 * DIM, node2 * DIM);
331 let spring =
332 Spring::from_positions(key, self.positions.as_slice(), stiffness, damping_ratio);
333 self.springs.push(spring);
334 }
335
336 pub fn set_node_kinematic(&mut self, i: usize, is_kinematic: bool) {
339 assert!(i < self.positions.len() / DIM, "Node index out of bounds.");
340 self.update_status.set_status_changed(true);
341 self.update_status.set_local_inertia_changed(true);
342 self.kinematic_nodes[i] = is_kinematic;
343 }
344
345 pub fn clear_kinematic_nodes(&mut self) {
347 self.update_status.set_status_changed(true);
348 self.update_status.set_local_inertia_changed(true);
349 self.kinematic_nodes.fill(false)
350 }
351
352 pub fn set_plasticity(&mut self, strain_threshold: N, creep: N, max_force: N) {
354 self.plasticity_threshold = strain_threshold;
355 self.plasticity_creep = creep;
356 self.plasticity_max_force = max_force;
357 }
358
359 fn update_augmented_mass(&mut self, dt: N) {
360 self.augmented_mass.fill(N::zero());
361 self.augmented_mass.fill_diagonal(self.node_mass);
362
363 for spring in &mut self.springs {
364 let kinematic1 = self.kinematic_nodes[spring.nodes.0 / DIM];
365 let kinematic2 = self.kinematic_nodes[spring.nodes.1 / DIM];
366
367 if kinematic1 && kinematic2 {
368 continue;
369 }
370
371 let l = *spring.dir;
376
377 if spring.length != N::zero() {
378 let ll = l * l.transpose();
384 let stiffness = ll * spring.stiffness;
385 let damping_stiffness = stiffness * (dt * dt);
406
407 if !kinematic1 {
408 self.augmented_mass
409 .fixed_slice_mut::<DIM, DIM>(spring.nodes.0, spring.nodes.0)
410 .add_assign(&damping_stiffness);
411 }
412 if !kinematic2 {
413 self.augmented_mass
414 .fixed_slice_mut::<DIM, DIM>(spring.nodes.1, spring.nodes.1)
415 .add_assign(&damping_stiffness);
416 }
417 if !kinematic1 && !kinematic2 {
418 self.augmented_mass
420 .fixed_slice_mut::<DIM, DIM>(spring.nodes.0, spring.nodes.1)
421 .sub_assign(&damping_stiffness);
422 self.augmented_mass
423 .fixed_slice_mut::<DIM, DIM>(spring.nodes.1, spring.nodes.0)
424 .sub_assign(&damping_stiffness);
425 }
426 }
427 }
428
429 for i in 0..self.positions.len() / DIM {
433 if self.kinematic_nodes[i] {
434 let idof = i * DIM;
435 self.augmented_mass
436 .fixed_slice_mut::<DIM, DIM>(idof, idof)
437 .fill_diagonal(N::one());
438 }
439 }
440
441 self.inv_augmented_mass = Cholesky::new(self.augmented_mass.clone()).unwrap();
442 }
443
444 fn update_forces(&mut self, gravity: &Vector<N>, parameters: &IntegrationParameters<N>) {
445 self.accelerations.copy_from(&self.forces);
446
447 for spring in &mut self.springs {
448 let kinematic1 = self.kinematic_nodes[spring.nodes.0 / DIM];
449 let kinematic2 = self.kinematic_nodes[spring.nodes.1 / DIM];
450
451 if kinematic1 && kinematic2 {
452 continue;
453 }
454
455 let total_strain = spring.stiffness * (spring.length - spring.rest_length);
462 let strain = total_strain - spring.plastic_strain;
463
464 if strain.abs() > self.plasticity_threshold {
465 let coeff = parameters.dt() * parameters.inv_dt().min(self.plasticity_creep);
466 spring.plastic_strain += strain * coeff;
467 }
468
469 if spring.plastic_strain.abs() > self.plasticity_max_force {
470 spring.plastic_strain = spring.plastic_strain.signum() * self.plasticity_max_force;
471 }
472
473 let damping = spring.damping_ratio
478 * (spring.stiffness * self.node_mass).sqrt()
479 * na::convert(2.0);
480 let v0 = self.velocities.fixed_rows::<DIM>(spring.nodes.0);
481 let v1 = self.velocities.fixed_rows::<DIM>(spring.nodes.1);
482
483 let ldot = v1 - v0;
484 let l = *spring.dir;
485
486 let coeff =
488 spring.stiffness * (spring.length - spring.rest_length) + damping * l.dot(&ldot);
489 let f0 = l * (coeff - spring.plastic_strain);
490
491 if !kinematic1 {
495 self.accelerations
496 .fixed_rows_mut::<DIM>(spring.nodes.0)
497 .add_assign(&f0);
498 }
499 if !kinematic2 {
500 self.accelerations
501 .fixed_rows_mut::<DIM>(spring.nodes.1)
502 .sub_assign(&f0);
503 }
504 }
505
506 if self.gravity_enabled {
510 let gravity_force = gravity * self.node_mass;
511
512 for i in 0..self.positions.len() / DIM {
513 let idof = i * DIM;
514
515 if !self.kinematic_nodes[i] {
516 let mut acc = self.accelerations.fixed_rows_mut::<DIM>(idof);
517 acc += gravity_force
518 }
519 }
520 }
521
522 self.inv_augmented_mass.solve_mut(&mut self.accelerations);
523 }
524}
525
526impl<N: RealField + Copy> Body<N> for MassSpringSystem<N> {
527 #[inline]
528 fn gravity_enabled(&self) -> bool {
529 self.gravity_enabled
530 }
531
532 #[inline]
533 fn enable_gravity(&mut self, enabled: bool) {
534 self.gravity_enabled = enabled
535 }
536
537 fn update_kinematics(&mut self) {
538 if self.update_status.position_changed() {
539 for spring in &mut self.springs {
540 let p0 = self.positions.fixed_rows::<DIM>(spring.nodes.0);
541 let p1 = self.positions.fixed_rows::<DIM>(spring.nodes.1);
542 let l = p1 - p0;
543
544 if let Some((dir, length)) = Unit::try_new_and_get(l, N::zero()) {
545 spring.dir = dir;
546 spring.length = length;
547 } else {
548 spring.dir = Vector::y_axis();
549 spring.length = N::zero();
550 }
551 }
552 }
553 }
554
555 fn update_dynamics(&mut self, dt: N) {
556 if self.update_status.inertia_needs_update() && self.status == BodyStatus::Dynamic {
557 if !self.is_active() {
558 self.activate();
559 }
560
561 self.update_augmented_mass(dt);
562 }
563 }
564
565 fn update_acceleration(&mut self, gravity: &Vector<N>, parameters: &IntegrationParameters<N>) {
566 self.update_forces(gravity, parameters);
567 }
568
569 fn clear_forces(&mut self) {
570 self.forces.fill(N::zero())
571 }
572
573 fn apply_displacement(&mut self, disp: &[N]) {
574 self.update_status.set_position_changed(true);
575 let disp = DVectorSlice::from_slice(disp, self.positions.len());
576 self.positions += disp;
577 }
578
579 fn status(&self) -> BodyStatus {
580 self.status
581 }
582
583 fn set_status(&mut self, status: BodyStatus) {
584 self.update_status.set_status_changed(true);
585 self.status = status
586 }
587
588 fn clear_update_flags(&mut self) {
589 self.update_status.clear()
590 }
591
592 fn update_status(&self) -> BodyUpdateStatus {
593 self.update_status
594 }
595
596 fn activation_status(&self) -> &ActivationStatus<N> {
597 &self.activation
598 }
599
600 fn set_deactivation_threshold(&mut self, threshold: Option<N>) {
601 self.activation.set_deactivation_threshold(threshold)
602 }
603
604 fn ndofs(&self) -> usize {
605 self.positions.len()
606 }
607
608 fn generalized_acceleration(&self) -> DVectorSlice<N> {
609 DVectorSlice::from_slice(self.accelerations.as_slice(), self.accelerations.len())
610 }
611
612 fn generalized_velocity(&self) -> DVectorSlice<N> {
613 DVectorSlice::from_slice(self.velocities.as_slice(), self.velocities.len())
614 }
615
616 fn companion_id(&self) -> usize {
617 self.companion_id
618 }
619
620 fn set_companion_id(&mut self, id: usize) {
621 self.companion_id = id
622 }
623
624 fn generalized_velocity_mut(&mut self) -> DVectorSliceMut<N> {
625 self.update_status.set_velocity_changed(true);
626 let len = self.velocities.len();
627 DVectorSliceMut::from_slice(self.velocities.as_mut_slice(), len)
628 }
629
630 fn integrate(&mut self, parameters: &IntegrationParameters<N>) {
631 self.update_status.set_position_changed(true);
632 self.positions
633 .axpy(parameters.dt(), &self.velocities, N::one());
634 }
635
636 fn activate_with_energy(&mut self, energy: N) {
637 self.activation.set_energy(energy)
638 }
639
640 fn deactivate(&mut self) {
641 self.update_status.clear();
642 self.activation.set_energy(N::zero());
643 self.velocities.fill(N::zero());
644 }
645
646 fn num_parts(&self) -> usize {
647 self.elements.len()
648 }
649
650 fn part(&self, id: usize) -> Option<&dyn BodyPart<N>> {
651 self.elements.get(id).map(|e| e as &dyn BodyPart<N>)
652 }
653
654 fn deformed_positions(&self) -> Option<(DeformationsType, &[N])> {
655 Some((DeformationsType::Vectors, self.positions.as_slice()))
656 }
657
658 fn deformed_positions_mut(&mut self) -> Option<(DeformationsType, &mut [N])> {
659 self.update_status.set_position_changed(true);
660 Some((DeformationsType::Vectors, self.positions.as_mut_slice()))
661 }
662
663 fn world_point_at_material_point(&self, part: &dyn BodyPart<N>, point: &Point<N>) -> Point<N> {
664 let elt = part
665 .downcast_ref::<MassSpringElement<N>>()
666 .expect("The provided body part must be a mass-spring element");
667 fem_helper::world_point_at_material_point(elt.indices, &self.positions, point)
668 }
669
670 fn position_at_material_point(&self, part: &dyn BodyPart<N>, point: &Point<N>) -> Isometry<N> {
671 let elt = part
672 .downcast_ref::<MassSpringElement<N>>()
673 .expect("The provided body part must be a mass-spring element");
674 let pt = fem_helper::world_point_at_material_point(elt.indices, &self.positions, point);
675 Isometry::from_parts(Translation::from(pt.coords), na::one())
676 }
677
678 fn material_point_at_world_point(&self, part: &dyn BodyPart<N>, point: &Point<N>) -> Point<N> {
679 let elt = part
680 .downcast_ref::<MassSpringElement<N>>()
681 .expect("The provided body part must be a mass-spring element");
682 fem_helper::material_point_at_world_point(elt.indices, &self.positions, point)
683 }
684
685 fn fill_constraint_geometry(
686 &self,
687 part: &dyn BodyPart<N>,
688 _: usize, center: &Point<N>,
690 force_dir: &ForceDirection<N>,
691 j_id: usize,
692 wj_id: usize,
693 jacobians: &mut [N],
694 inv_r: &mut N,
695 ext_vels: Option<&DVectorSlice<N>>,
696 out_vel: Option<&mut N>,
697 ) {
698 let elt = part
699 .downcast_ref::<MassSpringElement<N>>()
700 .expect("The provided body part must be a mass-spring element");
701 fem_helper::fill_contact_geometry_fem(
702 self.ndofs(),
703 self.status,
704 elt.indices,
705 &self.positions,
706 &self.velocities,
707 &self.kinematic_nodes,
708 Either::Right(&self.inv_augmented_mass),
709 center,
710 force_dir,
711 j_id,
712 wj_id,
713 jacobians,
714 inv_r,
715 ext_vels,
716 out_vel,
717 );
718 }
719
720 #[inline]
721 fn has_active_internal_constraints(&mut self) -> bool {
722 false
723 }
724
725 #[inline]
726 fn setup_internal_velocity_constraints(
727 &mut self,
728 _: &DVectorSlice<N>,
729 _: &IntegrationParameters<N>,
730 ) {
731 }
732
733 #[inline]
734 fn warmstart_internal_velocity_constraints(&mut self, _: &mut DVectorSliceMut<N>) {}
735
736 #[inline]
737 fn step_solve_internal_velocity_constraints(&mut self, _: &mut DVectorSliceMut<N>) {}
738
739 #[inline]
740 fn step_solve_internal_position_constraints(&mut self, _: &IntegrationParameters<N>) {}
741
742 #[inline]
743 fn velocity_at_point(&self, part_id: usize, point: &Point<N>) -> Velocity<N> {
744 let element = &self.elements[part_id];
745 fem_helper::velocity_at_point(element.indices, &self.positions, &self.velocities, point)
746 }
747
748 fn apply_force_at_local_point(
749 &mut self,
750 part_id: usize,
751 force: &Vector<N>,
752 point: &Point<N>,
753 force_type: ForceType,
754 auto_wake_up: bool,
755 ) {
756 if self.status != BodyStatus::Dynamic {
757 return;
758 }
759
760 if auto_wake_up {
761 self.activate()
762 }
763
764 let element = &self.elements[part_id];
765 let indices = element.indices.as_slice();
766
767 #[cfg(feature = "dim2")]
768 let forces = [
769 force * (N::one() - point.x - point.y),
770 force * point.x,
771 force * point.y,
772 ];
773 #[cfg(feature = "dim3")]
774 let forces = [
775 force * (N::one() - point.x - point.y - point.z),
776 force * point.x,
777 force * point.y,
778 force * point.z,
779 ];
780
781 match force_type {
782 ForceType::Force => {
783 for i in 0..indices.len() {
784 if !self.kinematic_nodes[indices[i] / DIM] {
785 self.forces
786 .fixed_rows_mut::<DIM>(indices[i])
787 .add_assign(&forces[i]);
788 }
789 }
790 }
791 ForceType::Impulse => {
792 let dvel = &mut self.workspace;
793 dvel.fill(N::zero());
794 for i in 0..indices.len() {
795 if !self.kinematic_nodes[indices[i] / DIM] {
796 dvel.fixed_rows_mut::<DIM>(indices[i]).copy_from(&forces[i]);
797 }
798 }
799 self.inv_augmented_mass.solve_mut(dvel);
800 self.velocities += &*dvel;
801 }
802 ForceType::AccelerationChange => {
803 for i in 0..indices.len() {
804 if !self.kinematic_nodes[indices[i] / DIM] {
805 self.forces
806 .fixed_rows_mut::<DIM>(indices[i])
807 .add_assign(forces[i] * self.node_mass);
808 }
809 }
810 }
811 ForceType::VelocityChange => {
812 for i in 0..indices.len() {
813 if !self.kinematic_nodes[indices[i] / DIM] {
814 self.velocities
815 .fixed_rows_mut::<DIM>(indices[i])
816 .add_assign(forces[i]);
817 }
818 }
819 }
820 }
821 }
822
823 fn apply_force(
824 &mut self,
825 part_id: usize,
826 force: &Force<N>,
827 force_type: ForceType,
828 auto_wake_up: bool,
829 ) {
830 let dim = self.elements[part_id].indices.as_slice().len();
831 let inv_dim: N = na::convert(1.0 / dim as f64);
832 let barycenter = Point::from(Vector::repeat(inv_dim));
833 self.apply_force_at_local_point(
834 part_id,
835 &force.linear,
836 &barycenter,
837 force_type,
838 auto_wake_up,
839 )
840 }
841
842 fn apply_local_force(
843 &mut self,
844 part_id: usize,
845 force: &Force<N>,
846 force_type: ForceType,
847 auto_wake_up: bool,
848 ) {
849 self.apply_force(part_id, &force, force_type, auto_wake_up);
851 }
852
853 fn apply_force_at_point(
854 &mut self,
855 part_id: usize,
856 force: &Vector<N>,
857 point: &Point<N>,
858 force_type: ForceType,
859 auto_wake_up: bool,
860 ) {
861 let local_point = self.material_point_at_world_point(&self.elements[part_id], point);
862 self.apply_force_at_local_point(part_id, &force, &local_point, force_type, auto_wake_up)
863 }
864
865 fn apply_local_force_at_point(
866 &mut self,
867 part_id: usize,
868 force: &Vector<N>,
869 point: &Point<N>,
870 force_type: ForceType,
871 auto_wake_up: bool,
872 ) {
873 let local_point = self.material_point_at_world_point(&self.elements[part_id], point);
875 self.apply_force_at_local_point(part_id, &force, &local_point, force_type, auto_wake_up);
876 }
877
878 fn apply_local_force_at_local_point(
879 &mut self,
880 part_id: usize,
881 force: &Vector<N>,
882 point: &Point<N>,
883 force_type: ForceType,
884 auto_wake_up: bool,
885 ) {
886 self.apply_force_at_local_point(part_id, &force, &point, force_type, auto_wake_up);
888 }
889}
890
891impl<N: RealField + Copy> BodyPart<N> for MassSpringElement<N> {
892 fn center_of_mass(&self) -> Point<N> {
893 unimplemented!()
894 }
895 fn local_center_of_mass(&self) -> Point<N> {
896 unimplemented!()
897 }
898
899 fn position(&self) -> Isometry<N> {
900 Isometry::new(Vector::<N>::y() * na::convert::<_, N>(100.0f64), na::zero())
902 }
903
904 fn velocity(&self) -> Velocity<N> {
905 unimplemented!()
906 }
907
908 fn inertia(&self) -> Inertia<N> {
909 unimplemented!()
910 }
911
912 fn local_inertia(&self) -> Inertia<N> {
913 unimplemented!()
914 }
915}
916
917enum MassSpringSystemDescGeometry<'a, N: RealField + Copy> {
918 Quad(usize, usize),
919 Polyline(&'a Polyline<N>),
920 #[cfg(feature = "dim3")]
921 TriMesh(&'a TriMesh<N>),
922}
923
924pub struct MassSpringSystemDesc<'a, N: RealField + Copy> {
926 user_data: Option<UserDataBox>,
927 geom: MassSpringSystemDescGeometry<'a, N>,
928 stiffness: N,
929 sleep_threshold: Option<N>,
930 damping_ratio: N,
931 mass: N,
932 plasticity: (N, N, N),
933 kinematic_nodes: Vec<usize>,
934 status: BodyStatus,
935 gravity_enabled: bool,
936}
937
938impl<'a, N: RealField + Copy> MassSpringSystemDesc<'a, N> {
939 fn with_geometry(geom: MassSpringSystemDescGeometry<'a, N>) -> Self {
940 MassSpringSystemDesc {
941 user_data: None,
942 gravity_enabled: true,
943 geom,
944 stiffness: na::convert(1.0e3),
945 sleep_threshold: Some(ActivationStatus::default_threshold()),
946 damping_ratio: na::convert(0.2),
947 mass: N::one(),
948 plasticity: (N::zero(), N::zero(), N::zero()),
949 kinematic_nodes: Vec::new(),
950 status: BodyStatus::Dynamic,
951 }
952 }
953
954 #[cfg(feature = "dim3")]
956 pub fn from_trimesh(mesh: &'a TriMesh<N>) -> Self {
957 Self::with_geometry(MassSpringSystemDescGeometry::TriMesh(mesh))
958 }
959
960 pub fn from_polyline(polyline: &'a Polyline<N>) -> Self {
962 Self::with_geometry(MassSpringSystemDescGeometry::Polyline(polyline))
963 }
964
965 pub fn quad(subdiv_x: usize, subdiv_y: usize) -> Self {
967 Self::with_geometry(MassSpringSystemDescGeometry::Quad(subdiv_x, subdiv_y))
968 }
969
970 user_data_desc_accessors!();
971
972 pub fn clear_kinematic_nodes(&mut self) -> &mut Self {
974 self.kinematic_nodes.clear();
975 self
976 }
977
978 desc_custom_setters!(
979 self.plasticity, set_plasticity, strain_threshold: N, creep: N, max_force: N | { self.plasticity = (strain_threshold, creep, max_force) }
980 self.kinematic_nodes, set_nodes_kinematic, nodes: &[usize] | { self.kinematic_nodes.extend_from_slice(nodes) }
981 );
982
983 desc_setters!(
984 gravity_enabled, enable_gravity, gravity_enabled: bool
985 stiffness, set_stiffness, stiffness: N
986 sleep_threshold, set_sleep_threshold, sleep_threshold: Option<N>
987 damping_ratio, set_damping_ratio, damping_ratio: N
988 mass, set_mass, mass: N
989 status, set_status, status: BodyStatus
990 );
991
992 desc_custom_getters!(
993 self.get_plasticity_strain_threshold: N | { self.plasticity.0 }
994 self.get_plasticity_creep: N | { self.plasticity.1 }
995 self.get_plasticity_max_force: N | { self.plasticity.2 }
996 self.get_kinematic_nodes: &[usize] | { &self.kinematic_nodes[..] }
997 );
998
999 desc_getters!(
1000 [val] is_gravity_enabled -> gravity_enabled: bool
1001 [val] get_stiffness -> stiffness: N
1002 [val] get_sleep_threshold -> sleep_threshold: Option<N>
1003 [val] get_damping_ratio -> damping_ratio: N
1004 [val] get_mass -> mass: N
1005 [val] get_status -> status: BodyStatus
1006 );
1007
1008 pub fn build(&self) -> MassSpringSystem<N> {
1010 let mut vol = match self.geom {
1011 MassSpringSystemDescGeometry::Quad(nx, ny) => {
1012 let polyline = Polyline::quad(nx, ny);
1013 MassSpringSystem::from_polyline(
1014 &polyline,
1015 self.mass,
1016 self.stiffness,
1017 self.damping_ratio,
1018 )
1019 }
1020 MassSpringSystemDescGeometry::Polyline(polyline) => MassSpringSystem::from_polyline(
1021 polyline,
1022 self.mass,
1023 self.stiffness,
1024 self.damping_ratio,
1025 ),
1026 #[cfg(feature = "dim3")]
1027 MassSpringSystemDescGeometry::TriMesh(trimesh) => MassSpringSystem::from_trimesh(
1028 trimesh,
1029 self.mass,
1030 self.stiffness,
1031 self.damping_ratio,
1032 ),
1033 };
1034
1035 vol.set_deactivation_threshold(self.sleep_threshold);
1036 vol.set_plasticity(self.plasticity.0, self.plasticity.1, self.plasticity.2);
1037 vol.enable_gravity(self.gravity_enabled);
1038 vol.set_status(self.status);
1039 let _ = vol.set_user_data(self.user_data.as_ref().map(|data| data.0.to_any()));
1040
1041 for i in &self.kinematic_nodes {
1042 vol.set_node_kinematic(*i, true)
1043 }
1044
1045 vol
1046 }
1047}