Skip to main content

rapier3d/dynamics/soft_body/tearing/
tearing.rs

1//! The tear API of a soft body: tear marks, blade crossings, and the update of the state derived
2//! from the elements after a tear or a cut.
3
4use crate::alloc_prelude::*;
5use crate::math::{DIM, Real, Vector};
6#[cfg(not(feature = "std"))]
7#[allow(unused_imports)]
8use simba::scalar::{ComplexField as _, RealField as _};
9
10use super::super::SoftBody;
11use super::tearing_cut::blade_hit;
12use super::tearing_event::{CATASTROPHIC_STRESS, SoftBodyTearEvent};
13use super::tearing_particle_split::SplitLog;
14
15/// A sorted particle-index pair.
16pub(super) fn pair(a: u32, b: u32) -> [u32; 2] {
17    [a.min(b), a.max(b)]
18}
19
20/// Number of vertices two faces share, the vertex `v` excluded.
21pub(super) fn shared_others(a: &[u32], b: &[u32], v: u32) -> usize {
22    a.iter().filter(|&&x| x != v && b.contains(&x)).count()
23}
24
25impl SoftBody {
26    /// Marks the `i`-th edge as torn: the tear is applied at the end of the next step (use
27    /// [`crate::dynamics::SoftBodySet::tear`] to tear immediately). A tear loses no material (see
28    /// [`Self::rest_measure`]); pieces it disconnects become their own bodies ([`Self::origin`]).
29    pub fn tear_edge(&mut self, i: usize) {
30        if let Some(e) = self.edges.get_mut(i) {
31            e.torn = true;
32            // A requested tear is never paced by `max_tears_per_step`.
33            e.stress = e.stress.max(CATASTROPHIC_STRESS);
34            self.tearing_pending = true;
35        }
36    }
37
38    /// Marks the `i`-th cell as torn: the tear is applied at the end of the next step. No cell is
39    /// removed: one particle (nearest the centroid first) splits along the plane perpendicular to
40    /// the cell's principal rest stretch direction (see [`Self::tear_edge`]).
41    pub fn tear_cell(&mut self, i: usize) {
42        if let Some(c) = self.cells.get_mut(i) {
43            c.torn = true;
44            self.tearing_pending = true;
45        }
46    }
47
48    /// Whether some element has a pending tear mark.
49    pub fn has_pending_tears(&self) -> bool {
50        self.tearing_pending
51    }
52
53    /// The elements a blade (world segment in 2D, triangle in 3D) meets, as `(edge indices, cell
54    /// indices)`: an edge when they intersect (endpoints included), a cell when one of its edges
55    /// does. A preview of [`crate::dynamics::SoftBodySet::cut`], which removes none of them.
56    pub fn crossing_elements(&self, blade: &[Vector; DIM]) -> (Vec<u32>, Vec<u32>) {
57        let position = |i: u32| self.particles[i as usize].position;
58        let crosses = |a: u32, b: u32| blade_hit(blade, position(a), position(b)).is_some();
59        let edges = self
60            .edges
61            .iter()
62            .enumerate()
63            .filter(|(_, e)| crosses(e.vertices[0], e.vertices[1]))
64            .map(|(i, _)| i as u32)
65            .collect();
66        let cells = self
67            .cells
68            .iter()
69            .enumerate()
70            .filter(|(_, c)| {
71                (0..DIM + 1)
72                    .any(|a| (a + 1..DIM + 1).any(|b| crosses(c.vertices[a], c.vertices[b])))
73            })
74            .map(|(i, _)| i as u32)
75            .collect();
76        (edges, cells)
77    }
78
79    /// Takes and clears the pending tear marks as (edge, cell) indices. At most
80    /// `SoftBodyMaterial::max_tears_per_step` edges are taken (most loaded first, by smoothed
81    /// `stress`); the others lose their mark (marked again next step if still overloaded).
82    pub(crate) fn take_torn(&mut self) -> (Vec<u32>, Vec<u32>) {
83        self.tearing_pending = false;
84        let mut edges: Vec<(Real, u32)> = Vec::new();
85        for (i, e) in self.edges.iter_mut().enumerate() {
86            if core::mem::take(&mut e.torn) {
87                edges.push((e.stress, i as u32));
88            }
89        }
90        let limit = self.material.max_tears_per_step as usize;
91        if edges.len() > limit {
92            // The most loaded first; ties by index (deterministic). Edges loaded past twice
93            // their threshold (the rim of a projectile punching through) all tear at once: the
94            // limit paces the cracks that creep near the threshold, not a puncture.
95            edges.sort_by(|x, y| y.0.total_cmp(&x.0).then(x.1.cmp(&y.1)));
96            let keep = edges
97                .iter()
98                .filter(|(stress, _)| *stress > CATASTROPHIC_STRESS)
99                .count()
100                .max(limit);
101            edges.truncate(keep);
102        }
103        let mut edges: Vec<u32> = edges.into_iter().map(|(_, i)| i).collect();
104        edges.sort_unstable();
105        let mut cells = Vec::new();
106        for (i, c) in self.cells.iter_mut().enumerate() {
107            if core::mem::take(&mut c.torn) {
108                cells.push(i as u32);
109            }
110        }
111        (edges, cells)
112    }
113
114    /// Applies the given tears (see [`Self::tear_edge`]) and records them in `event`. Returns
115    /// whether the topology changed; the colliders are not updated here (see `SoftBodySet::tear`).
116    pub(crate) fn tear_topology(
117        &mut self,
118        torn_edges: &[u32],
119        torn_cells: &[u32],
120        event: &mut SoftBodyTearEvent,
121    ) -> bool {
122        self.crack_body(torn_edges, torn_cells, event)
123    }
124
125    /// Follows a topology change through the derived state: the split copies and inserted
126    /// particles of `log` join the clusters of their source or neighboring segment end, the meshes
127    /// follow, and the boundary tables, rest volume and coloring are rebuilt.
128    pub(super) fn finish_topology_change(&mut self, log: &SplitLog) {
129        let mut sources = log.split_particles.clone();
130        sources.extend(
131            log.inserted
132                .iter()
133                .flat_map(|&[a, b, p, q]| [(p, a), (q, b)]),
134        );
135        // This also matches the clusters to the updated cell list.
136        self.inherit_cluster_membership(&sources);
137        let remap_tables = super::collision_mesh::SoftTopologyRemap {
138            cells: &[],
139            particles: &[],
140            split: &log.split_particles,
141            inserted: &log.inserted,
142        };
143        self.for_each_mesh_mut(|body, mesh| {
144            mesh.remap_topology(body, &remap_tables);
145            mesh.clear_contacts();
146        });
147        self.rebuild_boundary_tables();
148        self.recolor();
149        self.modified = true;
150        self.topology_version = self.topology_version.wrapping_add(1);
151    }
152}