1use crate::alloc_prelude::*;
4#[cfg(feature = "dim3")]
5use crate::dynamics::SpringCoefficients;
6use crate::geometry::ColliderBuilder;
7use crate::math::{DIM, Real, Vector};
8use na::SimdRealField;
9use parry::utils::hashmap::HashMap;
10#[cfg(not(feature = "std"))]
11#[allow(unused_imports)]
12use simba::scalar::{ComplexField as _, RealField as _};
13
14use super::super::{SoftBodyCellModel, SoftBodyMaterial};
15#[cfg(feature = "dim3")]
16use super::soft_body_builder_mesh_helpers::icosphere;
17use super::soft_body_builder_mesh_helpers::mean_edge_length;
18#[cfg(any(feature = "dim3", feature = "std"))]
19use super::soft_body_builder_mesh_helpers::{cell_faces, drop_stray_pieces};
20use super::{SoftBodyBuilder, SoftBodyParticleSettings};
21
22impl SoftBodyBuilder {
23 pub fn new(positions: Vec<Vector>) -> Self {
25 Self {
26 positions,
27 masses: Vec::new(),
28 particle_mass: 1.0,
29 pinned: Vec::new(),
30 edges: Vec::new(),
31 bend_edges: Vec::new(),
32 tension_only_edges: Vec::new(),
33 edge_softness: Vec::new(),
34 edge_tear_resistance: Vec::new(),
35 #[cfg(feature = "dim3")]
36 dihedrals: Vec::new(),
37 cells: Vec::new(),
38 surface: Vec::new(),
39 material: SoftBodyMaterial::default(),
40 cell_model: SoftBodyCellModel::default(),
41 #[cfg(feature = "fem")]
42 solver: super::SoftBodySolver::default(),
43 volume_preservation: false,
44 volume_factor: 1.0,
45 shape_matching: false,
46 self_contacts: false,
47 oriented: None,
48 particle_radius: 0.01,
49 skin: None,
50 #[cfg(feature = "dim3")]
51 wire: Vec::new(),
52 skin_collision: false,
53 collider_template: Some(ColliderBuilder::ball(0.05).density(0.0)),
54 particle_settings: SoftBodyParticleSettings::default(),
55 user_data: 0,
56 }
57 }
58
59 pub fn trimesh(vertices: Vec<Vector>, indices: Vec<[u32; 3]>) -> Option<Self> {
67 if vertices.is_empty() || indices.is_empty() {
68 return None;
69 }
70 #[cfg(feature = "dim3")]
71 {
72 let mut builder = Self::new(vertices).surface(indices);
73 let edges = builder.surface_edges();
74 let dihedrals = builder.surface_dihedrals();
75 let mean_edge = mean_edge_length(&builder.positions, &edges);
76 builder = builder
77 .edges(edges)
78 .dihedrals(dihedrals)
79 .shape_matching(true)
80 .particle_radius(mean_edge * 0.5);
81 Some(builder)
82 }
83 #[cfg(feature = "dim2")]
84 {
85 let triangles: Vec<[u32; 3]> = indices
88 .iter()
89 .map(|&[a, b, c]| {
90 let (pa, pb, pc) = (
91 vertices[a as usize],
92 vertices[b as usize],
93 vertices[c as usize],
94 );
95 if (pb - pa).perp_dot(pc - pa) >= 0.0 {
96 [a, b, c]
97 } else {
98 [a, c, b]
99 }
100 })
101 .collect();
102 let mut directed: HashMap<[u32; 2], ()> = HashMap::default();
103 let mut structural: HashMap<[u32; 2], ()> = HashMap::default();
104 let mut edges = Vec::new();
105 for tri in &triangles {
106 for k in 0..3 {
107 let (u, v) = (tri[k], tri[(k + 1) % 3]);
108 directed.insert([u, v], ());
109 let key = [u.min(v), u.max(v)];
110 if structural.insert(key, ()).is_none() {
111 edges.push(key);
112 }
113 }
114 }
115 let mut boundary: Vec<[u32; 2]> = Vec::new();
117 for tri in &triangles {
118 for k in 0..3 {
119 let (u, v) = (tri[k], tri[(k + 1) % 3]);
120 if !directed.contains_key(&[v, u]) {
121 boundary.push([u, v]);
122 }
123 }
124 }
125 let mean_edge = mean_edge_length(&vertices, &edges);
126 Some(
127 Self::new(vertices)
128 .edges(edges)
129 .surface(boundary)
130 .shape_matching(true)
131 .particle_radius(mean_edge * 0.5),
132 )
133 }
134 }
135
136 #[cfg(feature = "dim2")]
140 pub fn polyline(vertices: Vec<Vector>, indices: Option<Vec<[u32; 2]>>) -> Option<Self> {
141 if vertices.len() < 2 {
142 return None;
143 }
144 let segments =
145 indices.unwrap_or_else(|| (0..vertices.len() as u32 - 1).map(|i| [i, i + 1]).collect());
146 if segments.is_empty() {
147 return None;
148 }
149 let mut neighbors: Vec<[u32; 2]> = vec![[u32::MAX; 2]; vertices.len()];
151 let mut valence: Vec<u32> = vec![0; vertices.len()];
152 for &[a, b] in &segments {
153 for (v, other) in [(a, b), (b, a)] {
154 let n = &mut neighbors[v as usize];
155 let count = &mut valence[v as usize];
156 if *count < 2 {
157 n[*count as usize] = other;
158 }
159 *count += 1;
160 }
161 }
162 let bend_edges: Vec<[u32; 2]> = neighbors
163 .iter()
164 .zip(&valence)
165 .filter(|(n, count)| **count == 2 && n[0] != n[1])
166 .map(|(n, _)| [n[0].min(n[1]), n[0].max(n[1])])
167 .collect();
168 let mean_edge = mean_edge_length(&vertices, &segments);
169 Some(
170 Self::new(vertices)
171 .edges(segments.clone())
172 .bend_edges(bend_edges)
173 .surface(segments)
174 .shape_matching(true)
175 .particle_radius(mean_edge * 0.5),
176 )
177 }
178
179 pub fn rope(start: Vector, end: Vector, num_particles: usize) -> Self {
183 let num_particles = num_particles.max(2);
184 let positions: Vec<Vector> = (0..num_particles)
185 .map(|i| start.lerp(end, i as Real / (num_particles - 1) as Real))
186 .collect();
187 let edges: Vec<[u32; 2]> = (0..num_particles as u32 - 1).map(|i| [i, i + 1]).collect();
188 let bend_edges = (0..num_particles as u32)
189 .filter_map(|i| (i + 2 < num_particles as u32).then_some([i, i + 2]))
190 .collect();
191 let segment = (end - start).length() / (num_particles - 1) as Real;
192 let builder = Self::new(positions).particle_radius(segment * 0.5);
193 #[cfg(feature = "dim2")]
195 let builder = builder.surface(edges.clone());
196 #[cfg(feature = "dim3")]
197 let builder = builder.wire(edges.clone());
198 builder.edges(edges).bend_edges(bend_edges)
199 }
200
201 #[cfg(feature = "dim3")]
205 pub fn cloth(origin: Vector, du: Vector, dv: Vector, nu: usize, nv: usize) -> Self {
206 let (nu, nv) = (nu.max(2), nv.max(2));
207 let idx = |i: usize, j: usize| (i * nv + j) as u32;
208 let mut positions = Vec::with_capacity(nu * nv);
209 for i in 0..nu {
210 for j in 0..nv {
211 positions.push(origin + du * i as Real + dv * j as Real);
212 }
213 }
214 let mut edges = Vec::new();
215 let mut bend_edges = Vec::new();
216 let mut surface = Vec::new();
217 for i in 0..nu {
218 for j in 0..nv {
219 if i + 1 < nu {
220 edges.push([idx(i, j), idx(i + 1, j)]);
221 }
222 if j + 1 < nv {
223 edges.push([idx(i, j), idx(i, j + 1)]);
224 }
225 if i + 1 < nu && j + 1 < nv {
226 edges.push([idx(i, j), idx(i + 1, j + 1)]);
228 edges.push([idx(i + 1, j), idx(i, j + 1)]);
229 if (i + j) % 2 == 0 {
231 surface.push([idx(i, j), idx(i + 1, j), idx(i + 1, j + 1)]);
232 surface.push([idx(i, j), idx(i + 1, j + 1), idx(i, j + 1)]);
233 } else {
234 surface.push([idx(i, j), idx(i + 1, j), idx(i, j + 1)]);
235 surface.push([idx(i + 1, j), idx(i + 1, j + 1), idx(i, j + 1)]);
236 }
237 }
238 if i + 2 < nu {
239 bend_edges.push([idx(i, j), idx(i + 2, j)]);
240 }
241 if j + 2 < nv {
242 bend_edges.push([idx(i, j), idx(i, j + 2)]);
243 }
244 }
245 }
246 let radius = du.length().min(dv.length()) * 0.5;
247 Self::new(positions)
248 .edges(edges)
249 .bend_edges(bend_edges)
250 .surface(surface)
251 .particle_radius(radius)
252 }
253
254 #[cfg(feature = "dim3")]
258 pub fn cloth_tube(
259 origin: Vector,
260 axis: Vector,
261 radius_start: Real,
262 radius_end: Real,
263 num_around: usize,
264 num_along: usize,
265 ) -> Self {
266 use crate::utils::OrthonormalBasis;
267 let (na, nl) = (num_around.max(3), num_along.max(2));
268 let dir = axis.try_normalize().unwrap_or(Vector::Y);
269 let [u, v] = dir.orthonormal_basis();
270 let idx = |ring: usize, k: usize| (ring * na + (k % na)) as u32;
271 let mut positions = Vec::with_capacity(na * nl);
272 for ring in 0..nl {
273 let t = ring as Real / (nl - 1) as Real;
274 let radius = radius_start + (radius_end - radius_start) * t;
275 let center = origin + axis * t;
276 for k in 0..na {
277 let a = k as Real / na as Real * Real::simd_two_pi();
278 positions.push(center + (u * a.cos() + v * a.sin()) * radius);
279 }
280 }
281 let mut edges = Vec::new();
282 let mut bend_edges = Vec::new();
283 let mut surface = Vec::new();
284 for ring in 0..nl {
285 for k in 0..na {
286 edges.push([idx(ring, k), idx(ring, k + 1)]);
287 bend_edges.push([idx(ring, k), idx(ring, k + 2)]);
288 if ring + 1 < nl {
289 edges.push([idx(ring, k), idx(ring + 1, k)]);
290 edges.push([idx(ring, k), idx(ring + 1, k + 1)]);
291 edges.push([idx(ring, k + 1), idx(ring + 1, k)]);
292 if (ring + k) % 2 == 0 {
293 surface.push([idx(ring, k), idx(ring + 1, k), idx(ring + 1, k + 1)]);
294 surface.push([idx(ring, k), idx(ring + 1, k + 1), idx(ring, k + 1)]);
295 } else {
296 surface.push([idx(ring, k), idx(ring + 1, k), idx(ring, k + 1)]);
297 surface.push([idx(ring + 1, k), idx(ring + 1, k + 1), idx(ring, k + 1)]);
298 }
299 }
300 if ring + 2 < nl {
301 bend_edges.push([idx(ring, k), idx(ring + 2, k)]);
302 }
303 }
304 }
305 let ring_step = radius_start.min(radius_end) * Real::simd_two_pi() / na as Real;
306 let radius = ring_step.min(axis.length() / (nl - 1) as Real) * 0.5;
307 Self::new(positions)
308 .edges(edges)
309 .bend_edges(bend_edges)
310 .surface(surface)
311 .particle_radius(radius)
312 }
313
314 #[cfg(feature = "dim3")]
318 pub fn cloth_anisotropic(
319 origin: Vector,
320 du: Vector,
321 dv: Vector,
322 nu: usize,
323 nv: usize,
324 warp: SpringCoefficients<Real>,
325 weft: SpringCoefficients<Real>,
326 shear: SpringCoefficients<Real>,
327 ) -> Self {
328 let mut builder = Self::cloth(origin, du, dv, nu, nv);
329 let (nu, nv) = (nu.max(2), nv.max(2));
330 let idx = |i: usize, j: usize| (i * nv + j) as u32;
331 let mut k = 0u32;
333 for i in 0..nu {
334 for j in 0..nv {
335 if i + 1 < nu {
336 debug_assert_eq!(builder.edges[k as usize], [idx(i, j), idx(i + 1, j)]);
337 builder.edge_softness.push((k, warp));
338 k += 1;
339 }
340 if j + 1 < nv {
341 builder.edge_softness.push((k, weft));
342 k += 1;
343 }
344 if i + 1 < nu && j + 1 < nv {
345 builder.edge_softness.push((k, shear));
346 builder.edge_softness.push((k + 1, shear));
347 k += 2;
348 }
349 }
350 }
351 builder
352 }
353
354 #[cfg(feature = "dim3")]
359 pub fn cuboid(center: Vector, half_extents: Vector, nx: usize, ny: usize, nz: usize) -> Self {
360 let (nx, ny, nz) = (nx.max(2), ny.max(2), nz.max(2));
361 let step = Vector::new(
362 2.0 * half_extents.x / (nx - 1) as Real,
363 2.0 * half_extents.y / (ny - 1) as Real,
364 2.0 * half_extents.z / (nz - 1) as Real,
365 );
366 let idx = |i: usize, j: usize, k: usize| ((i * ny + j) * nz + k) as u32;
367 let mut positions = Vec::with_capacity(nx * ny * nz);
368 for i in 0..nx {
369 for j in 0..ny {
370 for k in 0..nz {
371 positions.push(
372 center - half_extents
373 + Vector::new(
374 step.x * i as Real,
375 step.y * j as Real,
376 step.z * k as Real,
377 ),
378 );
379 }
380 }
381 }
382 let mut cells = Vec::new();
385 for i in 0..nx - 1 {
386 for j in 0..ny - 1 {
387 for k in 0..nz - 1 {
388 let v: [u32; 8] = core::array::from_fn(|c| {
390 idx(i + (c & 1), j + ((c >> 1) & 1), k + ((c >> 2) & 1))
391 });
392 let tets: [[usize; 4]; 5] = if (i + j + k) % 2 == 0 {
393 [
394 [0, 3, 5, 6],
395 [1, 0, 3, 5],
396 [2, 0, 3, 6],
397 [4, 0, 5, 6],
398 [7, 3, 5, 6],
399 ]
400 } else {
401 [
402 [1, 2, 4, 7],
403 [0, 1, 2, 4],
404 [3, 1, 2, 7],
405 [5, 1, 4, 7],
406 [6, 2, 4, 7],
407 ]
408 };
409 for t in tets {
410 cells.push([v[t[0]], v[t[1]], v[t[2]], v[t[3]]]);
411 }
412 }
413 }
414 }
415 let radius = step.x.min(step.y).min(step.z) * 0.5;
416 Self::new(positions).cells(cells).particle_radius(radius)
417 }
418
419 #[cfg(any(feature = "dim3", feature = "std"))]
425 pub fn volumetric(
426 vertices: &[Vector],
427 indices: &[[u32; DIM]],
428 cell_size: Real,
429 ) -> Option<Self> {
430 Self::volumetric_with(
431 vertices,
432 indices,
433 &parry::transformation::VolumeMeshParameters::new(cell_size),
434 )
435 }
436
437 #[cfg(any(feature = "dim3", feature = "std"))]
443 pub fn volumetric_with(
444 vertices: &[Vector],
445 indices: &[[u32; DIM]],
446 params: &parry::transformation::VolumeMeshParameters,
447 ) -> Option<Self> {
448 let mut mesh = parry::transformation::volume_mesh(vertices, indices, params)?;
449 let components = mesh.connected_components();
450 let count = components.iter().max().map(|id| *id as usize + 1)?;
451 if count > 1 {
452 drop_stray_pieces(&mut mesh, &components, count);
453 }
454 Self::from_cells(mesh)
455 }
456
457 #[cfg(any(feature = "dim3", feature = "std"))]
459 fn from_cells(mesh: parry::transformation::VolumeMesh) -> Option<Self> {
460 let mut builder = Self::new(mesh.vertices).cells(mesh.cells);
461 let mut counts: HashMap<[u32; DIM], u32> = HashMap::default();
464 for cell in &builder.cells {
465 for face in cell_faces(*cell) {
466 let mut key = face;
467 key.sort_unstable();
468 *counts.entry(key).or_insert(0) += 1;
469 }
470 }
471 let mut seen: HashMap<[u32; 2], ()> = HashMap::default();
472 let mut edges = Vec::new();
473 for (face, count) in &counts {
474 if *count != 1 {
475 continue;
476 }
477 for k in 0..DIM {
478 let (a, b) = (face[k], face[(k + 1) % DIM]);
479 let key = [a.min(b), a.max(b)];
480 if seen.insert(key, ()).is_none() {
481 edges.push(key);
482 }
483 }
484 }
485 let radius = mean_edge_length(&builder.positions, &edges) * 0.5;
486 builder = builder.particle_radius(radius);
487 Some(builder)
488 }
489
490 #[cfg(any(feature = "dim3", feature = "std"))]
496 pub fn volumetric_skinned(
497 vertices: &[Vector],
498 indices: &[[u32; DIM]],
499 cell_size: Real,
500 ) -> Option<Self> {
501 let builder = Self::volumetric(vertices, indices, cell_size)?;
502 Some(builder.skin(vertices.to_vec(), indices.to_vec()))
503 }
504
505 #[cfg(feature = "dim3")]
509 pub fn sphere(center: Vector, radius: Real, subdivisions: usize) -> Self {
510 let (positions, triangles) = icosphere(center, radius, subdivisions);
511 let mut builder = Self::new(positions).surface(triangles);
512 let dihedrals = builder.surface_dihedrals();
513 let edges = builder.surface_edges();
514 let mean_edge = edges
515 .iter()
516 .map(|e| (builder.positions[e[0] as usize] - builder.positions[e[1] as usize]).length())
517 .sum::<Real>()
518 / edges.len().max(1) as Real;
519 builder = builder
520 .edges(edges)
521 .dihedrals(dihedrals)
522 .volume_preservation(true)
523 .particle_radius(mean_edge * 0.5);
524 builder
525 }
526
527 #[cfg(feature = "dim2")]
532 pub fn polygon(points: Vec<Vector>) -> Self {
533 let n = points.len().max(3) as u32;
534 let edges: Vec<[u32; 2]> = (0..n).map(|i| [i, (i + 1) % n]).collect();
535 let bend_edges: Vec<[u32; 2]> = if n > 4 {
536 (0..n).map(|i| [i, (i + 2) % n]).collect()
537 } else {
538 Vec::new()
539 };
540 let surface: Vec<[u32; DIM]> = edges.clone();
541 let mean_edge = edges
542 .iter()
543 .map(|e| (points[e[0] as usize] - points[e[1] as usize]).length())
544 .sum::<Real>()
545 / n as Real;
546 Self::new(points)
547 .edges(edges)
548 .bend_edges(bend_edges)
549 .surface(surface)
550 .volume_preservation(true)
551 .particle_radius(mean_edge * 0.5)
552 }
553
554 #[cfg(feature = "dim2")]
556 pub fn disk(center: Vector, radius: Real, num_particles: usize) -> Self {
557 let n = num_particles.max(3);
558 let points = (0..n)
559 .map(|i| {
560 let angle = i as Real / n as Real * Real::simd_two_pi();
561 center + Vector::new(angle.cos(), angle.sin()) * radius
562 })
563 .collect();
564 Self::polygon(points)
565 }
566
567 #[cfg(feature = "dim2")]
572 pub fn grid(center: Vector, half_extents: Vector, nx: usize, ny: usize) -> Self {
573 let (nx, ny) = (nx.max(2), ny.max(2));
574 let step = Vector::new(
575 2.0 * half_extents.x / (nx - 1) as Real,
576 2.0 * half_extents.y / (ny - 1) as Real,
577 );
578 let idx = |i: usize, j: usize| (i * ny + j) as u32;
579 let mut positions = Vec::with_capacity(nx * ny);
580 for i in 0..nx {
581 for j in 0..ny {
582 positions.push(
583 center - half_extents + Vector::new(step.x * i as Real, step.y * j as Real),
584 );
585 }
586 }
587 let mut cells = Vec::new();
588 for i in 0..nx - 1 {
589 for j in 0..ny - 1 {
590 let (a, b, c, d) = (idx(i, j), idx(i + 1, j), idx(i + 1, j + 1), idx(i, j + 1));
591 if (i + j) % 2 == 0 {
592 cells.push([a, b, c]);
593 cells.push([a, c, d]);
594 } else {
595 cells.push([a, b, d]);
596 cells.push([b, c, d]);
597 }
598 }
599 }
600 let radius = step.x.min(step.y) * 0.5;
601 Self::new(positions).cells(cells).particle_radius(radius)
602 }
603}