rapier3d/dynamics/soft_body/
soft_body_volume.rs1use super::SoftBody;
3use super::soft_body_builder::surface_is_closed;
4use crate::alloc_prelude::*;
5use crate::math::{DIM, Real, Vector};
6
7#[derive(Clone, Debug, PartialEq)]
11#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
12pub struct SoftVolumePiece {
13 pub(crate) elements: Vec<u32>,
15 pub(crate) particles: Vec<u32>,
17 pub(crate) rest_volume: Real,
19 pub(crate) impulse: Real,
21 #[cfg_attr(feature = "serde-serialize", serde(skip))]
25 pub(crate) inverted: bool,
26}
27
28impl SoftVolumePiece {
29 pub fn elements(&self) -> &[u32] {
31 &self.elements
32 }
33
34 pub fn particles(&self) -> &[u32] {
36 &self.particles
37 }
38
39 pub fn rest_volume(&self) -> Real {
41 self.rest_volume
42 }
43
44 pub fn volume(&self, body: &SoftBody) -> Real {
46 SoftBody::elements_volume(self.boundary_elements(&body.boundary), |i| {
47 body.particles[i as usize].position
48 })
49 }
50
51 pub(crate) fn boundary_elements<'a>(
53 &'a self,
54 boundary: &'a [[u32; DIM]],
55 ) -> impl Iterator<Item = &'a [u32; DIM]> + Clone + 'a {
56 self.elements.iter().map(move |&e| &boundary[e as usize])
57 }
58}
59
60impl SoftBody {
61 pub(crate) fn rebuild_volume_pieces(&mut self, remap: &[u32]) {
65 self.volume_pieces =
66 self.compute_volume_pieces(remap, |i| self.particles[i as usize].rest_position);
67 self.volume_preservation &= !self.volume_pieces.is_empty();
68 }
69
70 pub(crate) fn compute_volume_pieces(
74 &self,
75 remap: &[u32],
76 rest_position: impl Fn(u32) -> Vector,
77 ) -> Vec<SoftVolumePiece> {
78 fn find(parent: &mut [u32], mut i: u32) -> u32 {
79 while parent[i as usize] != i {
80 let up = parent[parent[i as usize] as usize];
81 parent[i as usize] = up;
82 i = up;
83 }
84 i
85 }
86 fn link(parent: &mut [u32], vertices: &[u32]) {
87 let n = parent.len();
88 let mut vertices = vertices.iter().copied().filter(|&v| (v as usize) < n);
89 let Some(first) = vertices.next() else {
90 return;
91 };
92 let mut root = find(parent, first);
93 for v in vertices {
94 let other = find(parent, v);
95 if other != root {
96 let (low, high) = (root.min(other), root.max(other));
97 parent[high as usize] = low;
98 root = low;
99 }
100 }
101 }
102
103 let n = self.particles.len();
104 let mut parent: Vec<u32> = (0..n as u32).collect();
105 for edge in &self.edges {
106 link(&mut parent, &edge.vertices);
107 }
108 for cell in &self.cells {
109 link(&mut parent, &cell.vertices);
110 }
111 for element in &self.boundary {
112 link(&mut parent, element);
113 }
114
115 let mut group_of_root = vec![u32::MAX; n];
117 let mut groups: Vec<Vec<u32>> = Vec::new();
118 for (e, element) in self.boundary.iter().enumerate() {
119 let Some(&v) = element.iter().find(|&&v| (v as usize) < n) else {
120 continue;
121 };
122 let root = find(&mut parent, v) as usize;
123 if group_of_root[root] == u32::MAX {
124 group_of_root[root] = groups.len() as u32;
125 groups.push(Vec::new());
126 }
127 groups[group_of_root[root] as usize].push(e as u32);
128 }
129
130 let mut previous = vec![u32::MAX; n];
132 for (pi, piece) in self.volume_pieces.iter().enumerate() {
133 for &q in &piece.particles {
134 let p = if remap.is_empty() {
135 q
136 } else {
137 remap.get(q as usize).copied().unwrap_or(u32::MAX)
138 };
139 if let Some(slot) = previous.get_mut(p as usize) {
140 *slot = pi as u32;
141 }
142 }
143 }
144
145 let mut pieces = Vec::new();
146 let mut elements: Vec<[u32; DIM]> = Vec::new();
147 for group in groups {
148 elements.clear();
149 elements.extend(group.iter().map(|&e| self.boundary[e as usize]));
150 if !surface_is_closed(&elements) {
151 continue;
152 }
153 let mut particles: Vec<u32> = elements.iter().flatten().copied().collect();
154 particles.sort_unstable();
155 particles.dedup();
156 let rest_volume = Self::boundary_volume(&elements, &rest_position);
157 let old = self
158 .volume_pieces
159 .get(previous[particles[0] as usize] as usize);
160 pieces.push(SoftVolumePiece {
161 elements: group,
162 particles,
163 rest_volume,
164 impulse: old.map_or(0.0, |piece| piece.impulse),
165 inverted: old.is_some_and(|piece| piece.inverted),
166 });
167 }
168 pieces
169 }
170}