rapier3d/dynamics/soft_body/tearing/
tearing_validate.rs1use crate::alloc_prelude::*;
4#[cfg(feature = "dim3")]
5use crate::math::DIM;
6use alloc::format;
7use parry::utils::hashmap::HashMap;
8
9use super::super::SoftBody;
10use super::super::soft_body::SOFT_BODY_OVERFLOW_COLOR;
11use super::super::soft_body_builder::element_vertices;
12#[cfg(feature = "dim3")]
13use super::tearing::pair;
14
15impl SoftBody {
16 pub fn validate_topology(&self) -> Result<(), String> {
20 let n = self.particles.len() as u32;
21 let check = |what: &str, vertices: &[u32]| -> Result<(), String> {
22 for &v in vertices {
23 if v >= n {
24 return Err(format!("{what} references particle {v} of {n}"));
25 }
26 }
27 Ok(())
28 };
29 for (i, e) in self.edges.iter().enumerate() {
30 check(&format!("edge {i}"), &e.vertices)?;
31 if e.vertices[0] == e.vertices[1] {
32 return Err(format!("edge {i} is degenerate"));
33 }
34 }
35 for (i, c) in self.cells.iter().enumerate() {
36 check(&format!("cell {i}"), &c.vertices)?;
37 }
38 #[cfg(feature = "dim3")]
39 for (i, d) in self.dihedrals.iter().enumerate() {
40 check(&format!("dihedral {i}"), &d.vertices)?;
41 }
42 for (i, s) in self.boundary.iter().enumerate() {
43 check(&format!("surface element {i}"), s)?;
44 }
45 if let Some(kind) = self.measure_kind() {
48 let mut supported = vec![false; n as usize];
49 self.for_each_measure_element(kind, |_, vertices| {
50 for &v in vertices {
51 if let Some(s) = supported.get_mut(v as usize) {
52 *s = true;
53 }
54 }
55 });
56 if let Some(v) = supported.iter().position(|s| !s) {
57 return Err(format!("particle {v} belongs to no measure element"));
58 }
59 }
60 for (mi, mesh) in self.meshes().enumerate() {
61 let nv = mesh.vertex_count();
62 if let super::collision_mesh::SoftMeshMapping::Skinned { bindings } = mesh.binding() {
63 if bindings.len() != nv {
64 return Err(format!(
65 "mesh {mi} has {} bindings for {nv} vertices",
66 bindings.len()
67 ));
68 }
69 let dead = |b: &super::collision_mesh::SoftMeshCellBinding| {
71 !self.cells.is_empty() && b.cell as usize >= self.cells.len()
72 };
73 if let Some(v) = bindings.iter().position(dead) {
74 return Err(format!("mesh {mi} vertex {v} is bound to a dead cell"));
75 }
76 }
77 for (i, s) in mesh.indices().iter().enumerate() {
78 for &v in element_vertices(s) {
79 if v as usize >= nv {
80 return Err(format!("mesh {mi} element {i} has an out-of-range vertex"));
81 }
82 }
83 }
84 if mesh.ring_offsets.len() != nv + 1 {
85 return Err(format!("mesh {mi} ring offsets do not cover every vertex"));
86 }
87 if mesh.vertex_elements_offsets.len() != nv + 1 {
88 return Err(format!(
89 "mesh {mi} vertex-element offsets do not cover every vertex"
90 ));
91 }
92 for (i, s) in mesh.indices().iter().enumerate() {
93 let s = element_vertices(s);
94 for &v in s {
95 let (start, end) = (
96 mesh.vertex_elements_offsets[v as usize] as usize,
97 mesh.vertex_elements_offsets[v as usize + 1] as usize,
98 );
99 if !mesh.vertex_elements[start..end].contains(&(i as u32)) {
100 return Err(format!(
101 "mesh {mi} element {i} missing from vertex {v}'s list"
102 ));
103 }
104 let (start, end) = (
105 mesh.ring_offsets[v as usize] as usize,
106 mesh.ring_offsets[v as usize + 1] as usize,
107 );
108 let ring = &mesh.ring[start..end];
109 for &w in s.iter().filter(|&&w| w != v) {
110 if ring.binary_search(&w).is_err() {
111 return Err(format!("mesh {mi}: vertex {w} missing from {v}'s ring"));
112 }
113 }
114 }
115 }
116 for (v, window) in mesh.vertex_elements_offsets.windows(2).enumerate() {
117 for &e in &mesh.vertex_elements[window[0] as usize..window[1] as usize] {
118 let ok = mesh
119 .indices()
120 .get(e as usize)
121 .is_some_and(|s| s.contains(&(v as u32)));
122 if !ok {
123 return Err(format!(
124 "mesh {mi}: vertex {v} lists element {e} it is not in"
125 ));
126 }
127 }
128 }
129 #[cfg(feature = "dim3")]
130 {
131 if mesh.element_edges.len() != mesh.indices().len()
132 || mesh.edge_owners.len() != mesh.edges.len()
133 {
134 return Err(format!("mesh {mi} edge tables do not cover its elements"));
135 }
136 for (i, s) in mesh.indices().iter().enumerate() {
137 if element_vertices(s).len() < DIM {
139 let id = mesh.element_edges[i][0] as usize;
140 if mesh.edges.get(id) != Some(&pair(s[0], s[1])) {
141 return Err(format!("mesh {mi} segment {i} edge mismatch"));
142 }
143 continue;
144 }
145 for k in 0..DIM {
146 let id = mesh.element_edges[i][k] as usize;
147 let expected = pair(s[(k + 1) % DIM], s[(k + 2) % DIM]);
148 if mesh.edges.get(id) != Some(&expected) {
149 return Err(format!("mesh {mi} element {i} edge {k} mismatch"));
150 }
151 }
152 }
153 for (id, &owner) in mesh.edge_owners.iter().enumerate() {
154 let ok = mesh
155 .element_edges
156 .get(owner as usize)
157 .is_some_and(|edges| edges.contains(&(id as u32)));
158 if !ok {
159 return Err(format!("mesh {mi} edge {id} owner mismatch"));
160 }
161 }
162 }
163 }
164 if !self.cells.is_empty() {
165 if self.boundary_element_cells.len() != self.boundary.len() {
166 return Err("boundary element owners do not cover the boundary".into());
167 }
168 for (i, &c) in self.boundary_element_cells.iter().enumerate() {
169 if c == u32::MAX {
170 continue;
171 }
172 let ok = self
173 .cells
174 .get(c as usize)
175 .is_some_and(|cell| self.boundary[i].iter().all(|v| cell.vertices.contains(v)));
176 if !ok {
177 return Err(format!(
178 "boundary element {i} owned by a cell not containing it"
179 ));
180 }
181 }
182 }
183 let mut seen: HashMap<(u8, u32), ()> = HashMap::default();
185 let mut check_color = |color: u8, vertices: &[u32]| -> Result<(), String> {
186 if color == SOFT_BODY_OVERFLOW_COLOR {
187 return Ok(());
188 }
189 if color >= self.num_colors {
190 return Err(format!(
191 "color {color} beyond num_colors {}",
192 self.num_colors
193 ));
194 }
195 for &v in vertices {
196 if seen.insert((color, v), ()).is_some() {
197 return Err(format!(
198 "particle {v} shared by two elements of color {color}"
199 ));
200 }
201 }
202 Ok(())
203 };
204 for e in &self.edges {
205 check_color(e.color, &e.vertices)?;
206 }
207 for c in &self.cells {
208 check_color(c.color, &c.vertices)?;
209 }
210 #[cfg(feature = "dim3")]
211 for d in &self.dihedrals {
212 check_color(d.color, &d.vertices)?;
213 }
214 Ok(())
215 }
216}