Skip to main content

rapier3d/dynamics/soft_body/tearing/
tearing_helpers.rs

1//! Tearing-related helpers of a soft body: connected pieces, tear resistance and particle damage.
2use super::super::SoftBody;
3use crate::alloc_prelude::*;
4use crate::math::Real;
5#[cfg(not(feature = "std"))]
6#[allow(unused_imports)]
7use simba::scalar::{ComplexField as _, RealField as _};
8
9impl SoftBody {
10    /// The connected pieces of this soft body (particles joined by its elements), each sorted and
11    /// ordered by first particle; linear in the elements. Tears split pieces off as soft bodies
12    /// ([`Self::tear_edge`]), so only a body inserted as disconnected parts has more than one.
13    pub fn connected_pieces(&self) -> Vec<Vec<u32>> {
14        let n = self.particles.len();
15        let mut parent: Vec<u32> = (0..n as u32).collect();
16        fn find(parent: &mut [u32], mut i: u32) -> u32 {
17            while parent[i as usize] != i {
18                let up = parent[parent[i as usize] as usize];
19                parent[i as usize] = up;
20                i = up;
21            }
22            i
23        }
24        let mut union = |vertices: &[u32]| {
25            for w in vertices.windows(2) {
26                let (a, b) = (find(&mut parent, w[0]), find(&mut parent, w[1]));
27                if a != b {
28                    parent[a.max(b) as usize] = a.min(b);
29                }
30            }
31        };
32        for e in &self.edges {
33            union(&e.vertices);
34        }
35        for c in &self.cells {
36            union(&c.vertices);
37        }
38        #[cfg(feature = "dim3")]
39        for d in &self.dihedrals {
40            union(&d.vertices);
41        }
42        for s in &self.boundary {
43            union(s);
44        }
45        // Roots are the smallest index of their piece, so the pieces come out ordered.
46        let mut piece_of_root = vec![u32::MAX; n];
47        let mut pieces: Vec<Vec<u32>> = Vec::new();
48        for v in 0..n as u32 {
49            let root = find(&mut parent, v) as usize;
50            if piece_of_root[root] == u32::MAX {
51                piece_of_root[root] = pieces.len() as u32;
52                pieces.push(Vec::new());
53            }
54            pieces[piece_of_root[root] as usize].push(v);
55        }
56        pieces
57    }
58
59    /// The components of the body's element graph restricted to `members` (all particles when
60    /// `None`) that contain a particle of some `seeds` pair (the pairs a tear or cut separated, see
61    /// [`crate::dynamics::SoftBodyTearEvent::split_particles`]), sorted; empty unless at least two.
62    pub fn seeded_components(&self, members: Option<&[u32]>, seeds: &[[u32; 2]]) -> Vec<Vec<u32>> {
63        let n = self.particles.len();
64        let member =
65            |v: u32| (v as usize) < n && members.is_none_or(|m| m.binary_search(&v).is_ok());
66        let mut parent: Vec<u32> = (0..n as u32).collect();
67        fn find(parent: &mut [u32], mut i: u32) -> u32 {
68            while parent[i as usize] != i {
69                let up = parent[parent[i as usize] as usize];
70                parent[i as usize] = up;
71                i = up;
72            }
73            i
74        }
75        // Pairwise over the in-set vertices: a chain through an out-of-set vertex must not break.
76        let mut union = |vertices: &[u32]| {
77            for i in 0..vertices.len() {
78                if !member(vertices[i]) {
79                    continue;
80                }
81                for j in i + 1..vertices.len() {
82                    if !member(vertices[j]) {
83                        continue;
84                    }
85                    let (a, b) = (
86                        find(&mut parent, vertices[i]),
87                        find(&mut parent, vertices[j]),
88                    );
89                    if a != b {
90                        parent[a.max(b) as usize] = a.min(b);
91                    }
92                }
93            }
94        };
95        for e in &self.edges {
96            union(&e.vertices);
97        }
98        for c in &self.cells {
99            union(&c.vertices);
100        }
101        #[cfg(feature = "dim3")]
102        for d in &self.dihedrals {
103            union(&d.vertices);
104        }
105        for s in &self.boundary {
106            union(s);
107        }
108        let mut seeded = vec![false; n];
109        for pair in seeds {
110            for &v in pair {
111                if member(v) {
112                    seeded[find(&mut parent, v) as usize] = true;
113                }
114            }
115        }
116        if seeded.iter().filter(|s| **s).count() < 2 {
117            return Vec::new();
118        }
119        // Roots are the smallest index of their component, so the components come out ordered.
120        let mut piece_of_root = vec![u32::MAX; n];
121        let mut pieces: Vec<Vec<u32>> = Vec::new();
122        let mut visit = |v: u32, parent: &mut [u32]| {
123            let root = find(parent, v) as usize;
124            if !seeded[root] {
125                return;
126            }
127            if piece_of_root[root] == u32::MAX {
128                piece_of_root[root] = pieces.len() as u32;
129                pieces.push(Vec::new());
130            }
131            pieces[piece_of_root[root] as usize].push(v);
132        };
133        match members {
134            Some(members) => members.iter().for_each(|&v| visit(v, &mut parent)),
135            None => (0..n as u32).for_each(|v| visit(v, &mut parent)),
136        }
137        pieces
138    }
139
140    /// The summed nominal mass of the given particles (out-of-range indices ignored).
141    pub fn mass_of(&self, particles: &[u32]) -> Real {
142        particles
143            .iter()
144            .filter_map(|&v| self.particles.get(v as usize))
145            .map(|p| p.mass)
146            .sum()
147    }
148
149    /// Sets the tear-threshold multiplier of the `i`-th edge (see
150    /// [`crate::dynamics::SoftBodyEdge::tear_resistance`]); `1.0` restores the material's threshold.
151    pub fn set_edge_tear_resistance(&mut self, i: usize, resistance: Real) {
152        if let Some(e) = self.edges.get_mut(i) {
153            e.tear_resistance = resistance.max(0.0);
154            self.modified = true;
155        }
156    }
157
158    /// Sets the tear-threshold multiplier of the `i`-th cell (see
159    /// [`crate::dynamics::SoftBodyCell::tear_resistance`]); `1.0` restores the material's threshold.
160    pub fn set_cell_tear_resistance(&mut self, i: usize, resistance: Real) {
161        if let Some(c) = self.cells.get_mut(i) {
162            c.tear_resistance = resistance.max(0.0);
163            self.modified = true;
164        }
165    }
166
167    /// Marks the `i`-th particle as damaged, or repairs it (see
168    /// [`crate::dynamics::SoftBodyParticle::is_damaged`]): a way to seed a weak spot where a tear should start.
169    pub fn set_particle_damaged(&mut self, i: usize, damaged: bool) {
170        if let Some(p) = self.particles.get_mut(i) {
171            p.damaged = damaged;
172            self.modified = true;
173        }
174    }
175
176    /// The tear-threshold multiplier of an element of resistance `resistance` over the particles
177    /// `vertices`: the material's interior strength applies on top when every particle is
178    /// interior and undamaged.
179    pub(crate) fn effective_tear_resistance(&self, resistance: Real, vertices: &[u32]) -> Real {
180        let interior = self.material.interior_strength;
181        if interior > 1.0
182            && vertices.iter().all(|&v| {
183                let p = &self.particles[v as usize];
184                !p.on_surface && !p.damaged
185            })
186        {
187            resistance * interior
188        } else {
189            resistance
190        }
191    }
192}