Skip to main content

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}