bevy_rapier2d/dynamics/spring_joint.rs
1use crate::dynamics::{GenericJoint, GenericJointBuilder, JointAxesMask};
2use crate::dynamics::{JointAxis, MotorModel};
3use crate::math::{Real, Vect};
4
5use super::TypedJoint;
6
7#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
8#[derive(Copy, Clone, Debug, PartialEq)]
9#[repr(transparent)]
10/// A spring-damper joint, applies a force proportional to the distance between two objects.
11///
12/// The spring is integrated implicitly, implying that even an undamped spring will be subject to some
13/// amount of numerical damping (so it will eventually come to a rest). More solver iterations, or smaller
14/// timesteps, will lower the effect of numerical damping, providing a more realistic result.
15pub struct SpringJoint {
16 /// The underlying joint data.
17 pub data: GenericJoint,
18}
19
20impl SpringJoint {
21 /// Creates a new spring joint limiting the max distance between two bodies.
22 ///
23 /// The `max_dist` must be strictly greater than 0.0.
24 pub fn new(rest_length: Real, stiffness: Real, damping: Real) -> Self {
25 let data = GenericJointBuilder::new(JointAxesMask::empty())
26 .coupled_axes(JointAxesMask::LIN_AXES)
27 .motor_position(JointAxis::LinX, rest_length, stiffness, damping)
28 .motor_model(JointAxis::LinX, MotorModel::ForceBased)
29 .build();
30 Self { data }
31 }
32
33 /// Are contacts between the attached rigid-bodies enabled?
34 pub fn contacts_enabled(&self) -> bool {
35 self.data.contacts_enabled()
36 }
37
38 /// Sets whether contacts between the attached rigid-bodies are enabled.
39 pub fn set_contacts_enabled(&mut self, enabled: bool) -> &mut Self {
40 self.data.set_contacts_enabled(enabled);
41 self
42 }
43
44 /// The joint’s anchor, expressed in the local-space of the first rigid-body.
45 #[must_use]
46 pub fn local_anchor1(&self) -> Vect {
47 self.data.local_anchor1()
48 }
49
50 /// Sets the joint’s anchor, expressed in the local-space of the first rigid-body.
51 pub fn set_local_anchor1(&mut self, anchor1: Vect) -> &mut Self {
52 self.data.set_local_anchor1(anchor1);
53 self
54 }
55
56 /// The joint’s anchor, expressed in the local-space of the second rigid-body.
57 #[must_use]
58 pub fn local_anchor2(&self) -> Vect {
59 self.data.local_anchor2()
60 }
61
62 /// Sets the joint’s anchor, expressed in the local-space of the second rigid-body.
63 pub fn set_local_anchor2(&mut self, anchor2: Vect) -> &mut Self {
64 self.data.set_local_anchor2(anchor2);
65 self
66 }
67
68 /// Set the spring model used by this joint to reach the desired target velocity and position.
69 ///
70 /// Setting this to `MotorModel::ForceBased` (which is the default value for this joint) makes the spring constants
71 /// (stiffness and damping) parameter understood as in the regular spring-mass-damper system. With
72 /// `MotorModel::AccelerationBased`, the spring constants will be automatically scaled by the attached masses,
73 /// making the spring more mass-independent.
74 pub fn set_spring_model(&mut self, model: MotorModel) -> &mut Self {
75 self.data.set_motor_model(JointAxis::LinX, model);
76 self
77 }
78}
79
80impl From<SpringJoint> for GenericJoint {
81 fn from(val: SpringJoint) -> GenericJoint {
82 val.data
83 }
84}
85
86/// A [SpringJoint] joint using the builder pattern.
87///
88/// This builds a spring-damper joint which applies a force proportional to the distance between two objects.
89/// See the documentation of [SpringJoint] for more information on its behavior.
90#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
91#[derive(Copy, Clone, Debug, PartialEq)]
92pub struct SpringJointBuilder(pub SpringJoint);
93
94impl SpringJointBuilder {
95 /// Creates a new builder for spring joints.
96 ///
97 /// This axis is expressed in the local-space of both rigid-bodies.
98 pub fn new(rest_length: Real, stiffness: Real, damping: Real) -> Self {
99 Self(SpringJoint::new(rest_length, stiffness, damping))
100 }
101
102 /// Sets whether contacts between the attached rigid-bodies are enabled.
103 #[must_use]
104 pub fn contacts_enabled(mut self, enabled: bool) -> Self {
105 self.0.set_contacts_enabled(enabled);
106 self
107 }
108
109 /// Sets the joint’s anchor, expressed in the local-space of the first rigid-body.
110 #[must_use]
111 pub fn local_anchor1(mut self, anchor1: Vect) -> Self {
112 self.0.set_local_anchor1(anchor1);
113 self
114 }
115
116 /// Sets the joint’s anchor, expressed in the local-space of the second rigid-body.
117 #[must_use]
118 pub fn local_anchor2(mut self, anchor2: Vect) -> Self {
119 self.0.set_local_anchor2(anchor2);
120 self
121 }
122
123 /// Set the spring used by this joint to reach the desired target velocity and position.
124 ///
125 /// Setting this to `MotorModel::ForceBased` (which is the default value for this joint) makes the spring constants
126 /// (stiffness and damping) parameter understood as in the regular spring-mass-damper system. With
127 /// `MotorModel::AccelerationBased`, the spring constants will be automatically scaled by the attached masses,
128 /// making the spring more mass-independent.
129 #[must_use]
130 pub fn spring_model(mut self, model: MotorModel) -> Self {
131 self.0.set_spring_model(model);
132 self
133 }
134
135 /// Builds the spring joint.
136 #[must_use]
137 pub fn build(self) -> SpringJoint {
138 self.0
139 }
140}
141
142impl From<SpringJointBuilder> for TypedJoint {
143 fn from(joint: SpringJointBuilder) -> TypedJoint {
144 joint.0.into()
145 }
146}
147
148impl From<SpringJoint> for TypedJoint {
149 fn from(joint: SpringJoint) -> TypedJoint {
150 TypedJoint::SpringJoint(joint)
151 }
152}