avian2d/dynamics/solver/softness_parameters/mod.rs
1//! Soft constraints are spring-like constraints that dampen constraint responses
2//! using intuitive tuning parameters, a damping ratio and a frequency in Hertz.
3//!
4//! The following section contains an overview of soft constraints
5//! and their mathematical background.
6//!
7#![doc = include_str!("README.md")]
8
9use bevy::reflect::Reflect;
10#[cfg(feature = "serialize")]
11use bevy::reflect::{ReflectDeserialize, ReflectSerialize};
12
13use crate::{Scalar, TAU};
14
15/// Soft constraint tuning parameters used for dampening
16/// constraint response and controlling stiffness.
17#[derive(Clone, Copy, Debug, PartialEq, Reflect)]
18pub struct SoftnessParameters {
19 /// 2x the damping ratio (zeta ζ). Controls the amount of oscillation.
20 ///
21 /// This is stored as two times the damping ratio to avoid
22 /// unnecessary computations in [`SoftnessParameters::compute_coefficients`].
23 double_damping_ratio: Scalar,
24
25 /// The angular frequency (omega ω). Controls the rate of oscillation.
26 angular_frequency: Scalar,
27}
28
29impl SoftnessParameters {
30 /// Creates a new [`SoftnessParameters`] configuration based on
31 /// a given damping ratio and a frequency in Hertz.
32 ///
33 /// The damping ratio (zeta ζ) controls the amount of oscillation,
34 /// and the frequency controls the constraint's cycles per second.
35 #[inline]
36 pub fn new(damping_ratio: Scalar, frequency_hz: Scalar) -> Self {
37 Self {
38 double_damping_ratio: 2.0 * damping_ratio,
39 angular_frequency: TAU * frequency_hz,
40 }
41 }
42
43 /// Returns the damping ratio that controls the amount of oscillation.
44 #[inline]
45 pub fn damping_ratio(self) -> Scalar {
46 self.double_damping_ratio * 0.5
47 }
48
49 /// Returns the frequency that controls the rate of oscillation.
50 #[inline]
51 pub fn frequency(self) -> Scalar {
52 self.angular_frequency / TAU
53 }
54
55 /// Returns the angular frequency that controls the rate of oscillation.
56 /// This is the [`frequency`](Self::frequency) multiplied by `2.0 * PI`.
57 #[inline]
58 pub const fn angular_frequency(self) -> Scalar {
59 self.angular_frequency
60 }
61
62 /// Computes [`SoftnessCoefficients`] based on the parameters in `self` and the time step.
63 #[inline]
64 pub fn compute_coefficients(self, delta_secs: Scalar) -> SoftnessCoefficients {
65 // Largely based on Erin Catto's Solver2D and Box2D: https://box2d.org/posts/2024/02/solver2d#soft-constraints
66 // See /docs/soft_constraint.md for in-depth explanation and derivation.
67
68 // Expressions shared by computations.
69 let a1 = self.double_damping_ratio + self.angular_frequency * delta_secs;
70 let a2 = self.angular_frequency * delta_secs * a1;
71 let a3 = 1.0 / (1.0 + a2);
72
73 // The coefficients used for soft constraints.
74 SoftnessCoefficients {
75 bias: self.angular_frequency / a1,
76 impulse_scale: a3,
77 mass_scale: a2 * a3,
78 }
79 }
80}
81
82/// Coefficients used by soft constraints.
83#[derive(Clone, Copy, Debug, PartialEq, Reflect)]
84#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
85#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
86#[reflect(Debug, PartialEq)]
87pub struct SoftnessCoefficients {
88 /// The bias coefficient used for scaling how strongly impulses
89 /// are biased based on the separation distance.
90 pub bias: Scalar,
91
92 /// The mass coefficient used for scaling the effective mass
93 /// "seen" by the constraint.
94 pub mass_scale: Scalar,
95
96 /// The impulse coefficient used for scaling the accumulated impulse
97 /// that is subtracted from the total impulse to prevent
98 /// the total impulse from becoming too large.
99 pub impulse_scale: Scalar,
100}