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