rapier2d/dynamics/soft_body/soft_recovery_settings.rs
1//! Runtime toggles and tuning for soft-body tangle prevention, detection, passive stand-down and
2//! intersection-volume constraints. Each mechanism switches off individually and the empirical
3//! constants are exposed.
4
5use crate::math::Real;
6
7/// What the per-point constraints of the features a volume constraint acts on do (see
8/// [`SoftRecoverySettings::overlap_patch_constraints`]).
9#[derive(Copy, Clone, Debug, PartialEq, Eq)]
10#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
11pub enum SoftPatchConstraints {
12 /// Keep them as they are (they may fight the volume constraint).
13 Keep,
14 /// Stand them down: the volume constraint alone acts on those features.
15 StandDown,
16 /// Keep them but along the volume constraint's normal (the nearest grid cell's), with
17 /// their separation measured along it, so they push the way the constraint does.
18 AlongNormal,
19}
20
21/// Runtime configuration of the soft-body tangle detection and recovery stack. It lives on
22/// [`crate::dynamics::SoftBodiesSettings::recovery`] (itself on
23/// `IntegrationParameters::soft_bodies`), so it can be changed between steps.
24#[derive(Copy, Clone, Debug, PartialEq)]
25#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
26#[cfg_attr(feature = "serde-serialize", serde(default))]
27pub struct SoftRecoverySettings {
28 // --- Prevention ---
29 /// Raise the speculative contact margin from velocities authored between steps (insertion,
30 /// `set_particle_velocity`), so the coming step's reach covers them and a fast fresh body
31 /// does not tunnel. (default: `true`)
32 pub authored_velocity_margin: bool,
33 /// Give the edge-vs-edge pass a speculative reach (motion margin plus the closing-speed rule)
34 /// and, in 3D, edge constraints for closed-vs-closed pairs, so bodies crossing corner-first
35 /// collide; on, those constraints leave pressed 3D piles crossed. (default: `false`)
36 pub edge_speculation: bool,
37
38 // --- Detection ---
39 /// Detect inverted cells (material locally inside out) each step; feeds the self stand-down.
40 /// (default: `true`)
41 pub inverted_cell_detection: bool,
42 /// Detect surface self-crossings each step (the BVTT sweep); feeds the self stand-down.
43 /// (default: `true`)
44 pub self_crossing_detection: bool,
45 /// Skip the self-crossing sweep while the surface's accumulated travel could not have bridged
46 /// half a skin (crossings only form through motion). Off: sweep every step. (default: `true`)
47 pub detection_motion_gating: bool,
48 /// Detect boundary crossings between pairs of soft surfaces; feeds the cross-body expel-only
49 /// gate, the edge-pass stand-down and the intersection-volume constraints. (default: `true`)
50 pub cross_body_detection: bool,
51
52 // --- Passive recovery (stand-down) ---
53 /// Self contacts of tangled features (inverted cells, self-crossings) stand down so the
54 /// elasticity resolves the tangle instead of freezing it at skin distance. (default: `true`)
55 pub self_stand_down: bool,
56 /// A vertex constraint touching a boundary crossing between two surfaces may only expel,
57 /// never hold (a keep-apart constraint there is wrong-sided by construction).
58 /// (default: `true`)
59 pub cross_body_expel_gate: bool,
60 /// Edge constraints touching a cross-body boundary crossing stand down (thin open bodies
61 /// meet through their edge constraints, so the vertex gate alone cannot free them).
62 /// (default: `true`)
63 pub edge_stand_down: bool,
64 /// Constraints on crossing-flagged features repel instead of standing down: the vertex is
65 /// pushed to the side of the element where its one-ring centroid lies, through to skin
66 /// distance; a neighborhood straddling the element still stands down. (default: `false`)
67 pub crossing_repulsion: bool,
68 /// Guide the crossing repulsion by the pair's volume normal (the separating direction of the
69 /// two intruding patches' intersection-volume gradients, per grid cell) instead of the pierced
70 /// element's plane normal. Closed pairs only; needs no volume constraint. (default: `false`)
71 pub crossing_repulsion_guide: bool,
72 /// Guide the self-crossing repulsion by the fold's volume normal: the vertices inside their own
73 /// body's self-intersection region are pulled toward the surface they face along the region's
74 /// mean normal. Closed meshes only; needs no volume constraint. (default: `false`)
75 pub crossing_repulsion_self_guide: bool,
76
77 // --- Pacing ---
78 /// Material recovery pace, in length units per second (scaled by
79 /// `IntegrationParameters::length_unit`): the corrective rate allowed to deep recovery,
80 /// demoted intruders and untangling pulls. (default: `0.5`)
81 pub recovery_pace: Real,
82
83 // --- Intersection-volume constraints ---
84 /// Intersection-volume contact (Allard et al. 2010) for closed surfaces: one coupled constraint
85 /// per overlapping pair (soft-soft or soft-rigid) over both sides' intruding patches, corrected
86 /// by the intersection volume; master switch of the knobs below. (default: `true`)
87 pub overlap_constraints: bool,
88 /// Overlap constraints against rigid colliders too (the surface vertices inside the shape;
89 /// a dynamic rigid body takes the reaction, a fixed or kinematic one is a wall).
90 /// (default: `true`)
91 pub overlap_rigid: bool,
92 /// A self-crossed mesh takes no pair constraint: part of its winding is mirrored, so its volume
93 /// gradient points the wrong way there (two crossed eights exploded without this).
94 /// (default: `true`)
95 pub overlap_skip_self_tangled: bool,
96 /// The 3D closed-closed edge constraints stand down on a pair an overlap constraint owns (inside the
97 /// overlap they are wrong-sided and freeze it). (default: `true`)
98 pub overlap_edge_stand_down: bool,
99 /// Bound on the velocity change the coupled constraint may hand any side per step, in multiples
100 /// of [`Self::recovery_pace`]; `Real::MAX` makes it hard. (default: `1.0`)
101 pub overlap_constraint_pace: Real,
102 /// What the per-point constraints (manifold constraints against rigid colliders, vertex
103 /// constraints against soft surfaces) of the features inside a volume constraint's patch do,
104 /// feature by feature (see [`SoftPatchConstraints`]). (default: `AlongNormal`)
105 pub overlap_patch_constraints: SoftPatchConstraints,
106 /// Measure the intersection volume on the contact skins (surfaces dilated by their skin), not
107 /// the geometric surfaces: a resting pair then has a hard volume constraint with speculative
108 /// slack before the skins meet. (default: `false`)
109 pub overlap_skin_volume: bool,
110 /// The skin overlap kept at rest, as a fraction of the pair's skins (the paper's maintained
111 /// interpenetration layer): the correction removes only the volume beyond it, holding the
112 /// patch's average depth there; `0.0` rests the surfaces at their skins. (default: `0.0`)
113 pub overlap_kept_depth: Real,
114 /// Volume constraints on a body's self-overlaps between distinct surface regions: the vertices
115 /// behind another part of their own surface form one patch per connected region, each coupled
116 /// with the surface it faces like a pair of bodies. Closed meshes only. (default: `false`)
117 pub overlap_self_regions: bool,
118 /// Push along each constraint's normal instead of the volume gradients: every entry keeps its
119 /// gradient's magnitude but takes the constraint's mean direction, so the whole patch
120 /// separates along one axis. (default: `true`)
121 pub overlap_normal_push: bool,
122 /// Multi-volume grid (the paper's section 5): each pair's patch is split into a regular grid of
123 /// cells along its tangent axes, each with its own constraint and multiplier, so the pressure
124 /// and friction state vary across the patch. (default: `false`)
125 pub overlap_multi_volume: bool,
126 /// Cells per tangent axis of the multi-volume grid. (default: `3`)
127 pub overlap_split: u32,
128 /// Steps without progress of a pair's volume estimate before its positional correction
129 /// stands down (a wedged pair jiggled forever otherwise); it re-arms when the estimate
130 /// drops again. (default: `240`)
131 pub overlap_patience: u32,
132 /// Relative drop of the estimate that counts as progress for the patience.
133 /// (default: `0.02`)
134 pub overlap_progress_margin: Real,
135}
136
137impl Default for SoftRecoverySettings {
138 fn default() -> Self {
139 Self {
140 authored_velocity_margin: true,
141 edge_speculation: false,
142 inverted_cell_detection: true,
143 self_crossing_detection: true,
144 detection_motion_gating: true,
145 cross_body_detection: true,
146 self_stand_down: true,
147 cross_body_expel_gate: true,
148 edge_stand_down: true,
149 crossing_repulsion: false,
150 crossing_repulsion_guide: false,
151 crossing_repulsion_self_guide: false,
152 recovery_pace: 0.5,
153 overlap_constraints: true,
154 overlap_rigid: true,
155 overlap_skip_self_tangled: true,
156 overlap_edge_stand_down: true,
157 overlap_constraint_pace: 1.0,
158 overlap_patch_constraints: SoftPatchConstraints::AlongNormal,
159 overlap_skin_volume: false,
160 overlap_kept_depth: 0.0,
161 overlap_normal_push: true,
162 overlap_self_regions: false,
163 overlap_multi_volume: false,
164 overlap_split: 3,
165 overlap_patience: 240,
166 overlap_progress_margin: 0.02,
167 }
168 }
169}