rapier2d/dynamics/joint/
spring_joint.rs

1use crate::dynamics::joint::{GenericJoint, GenericJointBuilder, JointAxesMask};
2use crate::dynamics::{JointAxis, MotorModel};
3use crate::math::{Point, Real};
4
5#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
6#[derive(Copy, Clone, Debug, PartialEq)]
7#[repr(transparent)]
8/// A spring joint that pulls/pushes two bodies toward a target distance (like a spring or shock absorber).
9///
10/// Springs apply force based on:
11/// - **Stiffness**: How strong the spring force is (higher = stiffer spring)
12/// - **Damping**: Resistance to motion (higher = less bouncy, settles faster)
13/// - **Rest length**: The distance the spring "wants" to be
14///
15/// Use for:
16/// - Suspension systems (vehicles, furniture)
17/// - Bouncy connections
18/// - Soft constraints between objects
19/// - Procedural animation (following targets softly)
20///
21/// **Technical note**: Springs use implicit integration which adds some numerical damping.
22/// This means even zero-damping springs will eventually settle (more iterations = less damping).
23pub struct SpringJoint {
24    /// The underlying joint data.
25    pub data: GenericJoint,
26}
27
28impl SpringJoint {
29    /// Creates a new spring joint limiting the max distance between two bodies.
30    ///
31    /// The `max_dist` must be strictly greater than 0.0.
32    pub fn new(rest_length: Real, stiffness: Real, damping: Real) -> Self {
33        let data = GenericJointBuilder::new(JointAxesMask::empty())
34            .coupled_axes(JointAxesMask::LIN_AXES)
35            .motor_position(JointAxis::LinX, rest_length, stiffness, damping)
36            .motor_model(JointAxis::LinX, MotorModel::ForceBased)
37            .build();
38        Self { data }
39    }
40
41    /// The underlying generic joint.
42    pub fn data(&self) -> &GenericJoint {
43        &self.data
44    }
45
46    /// Are contacts between the attached rigid-bodies enabled?
47    pub fn contacts_enabled(&self) -> bool {
48        self.data.contacts_enabled
49    }
50
51    /// Sets whether contacts between the attached rigid-bodies are enabled.
52    pub fn set_contacts_enabled(&mut self, enabled: bool) -> &mut Self {
53        self.data.set_contacts_enabled(enabled);
54        self
55    }
56
57    /// The joint’s anchor, expressed in the local-space of the first rigid-body.
58    #[must_use]
59    pub fn local_anchor1(&self) -> Point<Real> {
60        self.data.local_anchor1()
61    }
62
63    /// Sets the joint’s anchor, expressed in the local-space of the first rigid-body.
64    pub fn set_local_anchor1(&mut self, anchor1: Point<Real>) -> &mut Self {
65        self.data.set_local_anchor1(anchor1);
66        self
67    }
68
69    /// The joint’s anchor, expressed in the local-space of the second rigid-body.
70    #[must_use]
71    pub fn local_anchor2(&self) -> Point<Real> {
72        self.data.local_anchor2()
73    }
74
75    /// Sets the joint’s anchor, expressed in the local-space of the second rigid-body.
76    pub fn set_local_anchor2(&mut self, anchor2: Point<Real>) -> &mut Self {
77        self.data.set_local_anchor2(anchor2);
78        self
79    }
80
81    /// Sets whether spring constants are mass-dependent or mass-independent.
82    ///
83    /// - `MotorModel::ForceBased` (default): Stiffness/damping are absolute values.
84    ///   Heavier objects will respond differently to the same spring constants.
85    /// - `MotorModel::AccelerationBased`: Spring constants auto-scale with mass.
86    ///   Objects of different masses behave more similarly.
87    ///
88    /// Most users can ignore this and use the default.
89    pub fn set_spring_model(&mut self, model: MotorModel) -> &mut Self {
90        self.data.set_motor_model(JointAxis::LinX, model);
91        self
92    }
93
94    // /// The maximum distance allowed between the attached objects.
95    // #[must_use]
96    // pub fn rest_length(&self) -> Option<Real> {
97    //     self.data.limits(JointAxis::X).map(|l| l.max)
98    // }
99    //
100    // /// Sets the maximum allowed distance between the attached objects.
101    // ///
102    // /// The `max_dist` must be strictly greater than 0.0.
103    // pub fn set_rest_length(&mut self, max_dist: Real) -> &mut Self {
104    //     self.data.set_limits(JointAxis::X, [0.0, max_dist]);
105    //     self
106    // }
107}
108
109impl From<SpringJoint> for GenericJoint {
110    fn from(val: SpringJoint) -> GenericJoint {
111        val.data
112    }
113}
114
115/// A [SpringJoint] joint using the builder pattern.
116///
117/// This builds a spring-damper joint which applies a force proportional to the distance between two objects.
118/// See the documentation of [SpringJoint] for more information on its behavior.
119#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
120#[derive(Copy, Clone, Debug, PartialEq)]
121pub struct SpringJointBuilder(pub SpringJoint);
122
123impl SpringJointBuilder {
124    /// Creates a new builder for spring joints.
125    ///
126    /// This axis is expressed in the local-space of both rigid-bodies.
127    pub fn new(rest_length: Real, stiffness: Real, damping: Real) -> Self {
128        Self(SpringJoint::new(rest_length, stiffness, damping))
129    }
130
131    /// Sets whether contacts between the attached rigid-bodies are enabled.
132    #[must_use]
133    pub fn contacts_enabled(mut self, enabled: bool) -> Self {
134        self.0.set_contacts_enabled(enabled);
135        self
136    }
137
138    /// Sets the joint’s anchor, expressed in the local-space of the first rigid-body.
139    #[must_use]
140    pub fn local_anchor1(mut self, anchor1: Point<Real>) -> Self {
141        self.0.set_local_anchor1(anchor1);
142        self
143    }
144
145    /// Sets the joint’s anchor, expressed in the local-space of the second rigid-body.
146    #[must_use]
147    pub fn local_anchor2(mut self, anchor2: Point<Real>) -> Self {
148        self.0.set_local_anchor2(anchor2);
149        self
150    }
151
152    /// Set the spring used by this joint to reach the desired target velocity and position.
153    ///
154    /// Setting this to `MotorModel::ForceBased` (which is the default value for this joint) makes the spring constants
155    /// (stiffness and damping) parameter understood as in the regular spring-mass-damper system. With
156    /// `MotorModel::AccelerationBased`, the spring constants will be automatically scaled by the attached masses,
157    /// making the spring more mass-independent.
158    #[must_use]
159    pub fn spring_model(mut self, model: MotorModel) -> Self {
160        self.0.set_spring_model(model);
161        self
162    }
163
164    // /// Sets the maximum allowed distance between the attached bodies.
165    // ///
166    // /// The `max_dist` must be strictly greater than 0.0.
167    // #[must_use]
168    // pub fn max_distance(mut self, max_dist: Real) -> Self {
169    //     self.0.set_max_distance(max_dist);
170    //     self
171    // }
172
173    /// Builds the spring joint.
174    #[must_use]
175    pub fn build(self) -> SpringJoint {
176        self.0
177    }
178}
179
180impl From<SpringJointBuilder> for GenericJoint {
181    fn from(val: SpringJointBuilder) -> GenericJoint {
182        val.0.into()
183    }
184}