avian3d/dynamics/joints/
distance.rs

1use crate::{
2    dynamics::joints::{EntityConstraint, JointSystems},
3    prelude::*,
4};
5use bevy::{
6    ecs::{
7        entity::{EntityMapper, MapEntities},
8        reflect::ReflectMapEntities,
9    },
10    prelude::*,
11};
12
13/// A distance [joint](dynamics::joints) maintains an upper and/or lower bound on the distance
14/// between anchor points on two bodies.
15///
16/// This can be useful for things like springs, muscles, and mass-spring networks.
17///
18/// A distance joint is defined by a [`JointAnchor`] on each body, and a [`DistanceLimit`]. The joint aims to keep
19/// the distance between the two anchor points within the specified limits.
20///
21#[doc = include_str!("./images/distance_joint.svg")]
22#[derive(Component, Clone, Debug, PartialEq, Reflect)]
23#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
24#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
25#[reflect(Component, Debug, MapEntities, PartialEq)]
26pub struct DistanceJoint {
27    /// The first body constrained by the joint.
28    pub body1: Entity,
29    /// The second body constrained by the joint.
30    pub body2: Entity,
31    /// The joint anchor point on the first body.
32    pub anchor1: JointAnchor,
33    /// The joint anchor point on the second body.
34    pub anchor2: JointAnchor,
35    /// The extents of the allowed relative translation between the attached bodies.
36    pub limits: DistanceLimit,
37    /// The joint's compliance, the inverse of stiffness (m / N).
38    pub compliance: Scalar,
39}
40
41impl EntityConstraint<2> for DistanceJoint {
42    fn entities(&self) -> [Entity; 2] {
43        [self.body1, self.body2]
44    }
45}
46
47impl DistanceJoint {
48    /// Creates a new [`DistanceJoint`] between two entities.
49    #[inline]
50    pub const fn new(body1: Entity, body2: Entity) -> Self {
51        Self {
52            body1,
53            body2,
54            anchor1: JointAnchor::ZERO,
55            anchor2: JointAnchor::ZERO,
56            limits: DistanceLimit::ZERO,
57            compliance: 0.0,
58        }
59    }
60
61    /// Sets the global anchor point on both bodies.
62    ///
63    /// This configures the [`JointAnchor`] of each [`JointFrame`].
64    #[inline]
65    pub const fn with_anchor(mut self, anchor: Vector) -> Self {
66        self.anchor1 = JointAnchor::FromGlobal(anchor);
67        self.anchor2 = JointAnchor::FromGlobal(anchor);
68        self
69    }
70
71    /// Sets the local anchor point on the first body.
72    ///
73    /// This configures the [`JointAnchor`] of the first [`JointFrame`].
74    #[inline]
75    pub const fn with_local_anchor1(mut self, anchor: Vector) -> Self {
76        self.anchor1 = JointAnchor::Local(anchor);
77        self
78    }
79
80    /// Sets the local anchor point on the second body.
81    ///
82    /// This configures the [`JointAnchor`] of the second [`JointFrame`].
83    #[inline]
84    pub const fn with_local_anchor2(mut self, anchor: Vector) -> Self {
85        self.anchor2 = JointAnchor::Local(anchor);
86        self
87    }
88
89    /// Returns the local anchor point on the first body.
90    ///
91    /// If the [`JointAnchor`] is set to [`FromGlobal`](JointAnchor::FromGlobal),
92    /// and the local anchor has not yet been computed, this will return `None`.
93    #[inline]
94    pub const fn local_anchor1(&self) -> Option<Vector> {
95        match self.anchor1 {
96            JointAnchor::Local(anchor) => Some(anchor),
97            _ => None,
98        }
99    }
100
101    /// Returns the local anchor point on the second body.
102    ///
103    /// If the [`JointAnchor`] is set to [`FromGlobal`](JointAnchor::FromGlobal),
104    /// and the local anchor has not yet been computed, this will return `None`.
105    #[inline]
106    pub const fn local_anchor2(&self) -> Option<Vector> {
107        match self.anchor2 {
108            JointAnchor::Local(anchor) => Some(anchor),
109            _ => None,
110        }
111    }
112
113    /// Sets the joint's rest length, or distance the bodies will be kept at.
114    #[inline]
115    #[deprecated(note = "Use `with_limits` instead.", since = "0.4.0")]
116    pub const fn with_rest_length(mut self, rest_length: Scalar) -> Self {
117        self.limits = DistanceLimit::new(rest_length, rest_length);
118        self
119    }
120
121    /// Sets the minimum and maximum distance between the anchor points of the bodies.
122    ///
123    /// `min` and `max` should be non-negative, and `min` should be less than or equal to `max`.
124    #[inline]
125    pub const fn with_limits(mut self, min: Scalar, max: Scalar) -> Self {
126        self.limits = DistanceLimit::new(min, max);
127        self
128    }
129
130    /// Sets the minimum distance between the anchor points of the bodies.
131    ///
132    /// If `min` is larger than the current maximum distance, the maximum distance will be set to `min`.
133    #[inline]
134    pub const fn with_min_distance(mut self, min: Scalar) -> Self {
135        self.limits.min = min;
136        if self.limits.max < min {
137            self.limits.max = min;
138        }
139        self
140    }
141
142    /// Sets the maximum distance between the anchor points of the bodies.
143    ///
144    /// If `max` is smaller than the current minimum distance, the minimum distance will be set to `max`.
145    #[inline]
146    pub const fn with_max_distance(mut self, max: Scalar) -> Self {
147        self.limits.max = max;
148        if self.limits.min > max {
149            self.limits.min = max;
150        }
151        self
152    }
153
154    /// Sets the joint's compliance (inverse of stiffness, m / N).
155    #[inline]
156    pub const fn with_compliance(mut self, compliance: Scalar) -> Self {
157        self.compliance = compliance;
158        self
159    }
160}
161
162impl MapEntities for DistanceJoint {
163    fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
164        self.body1 = entity_mapper.get_mapped(self.body1);
165        self.body2 = entity_mapper.get_mapped(self.body2);
166    }
167}
168
169pub(super) fn plugin(app: &mut App) {
170    app.add_systems(
171        PhysicsSchedule,
172        update_local_anchors.in_set(JointSystems::PrepareLocalFrames),
173    );
174}
175
176fn update_local_anchors(
177    mut joints: Query<&mut DistanceJoint, Changed<DistanceJoint>>,
178    bodies: Query<(&Position, &Rotation)>,
179) {
180    for mut joint in &mut joints {
181        if matches!(joint.anchor1, JointAnchor::Local(_))
182            && matches!(joint.anchor2, JointAnchor::Local(_))
183        {
184            continue;
185        }
186
187        let Ok([(pos1, rot1), (pos2, rot2)]) = bodies.get_many(joint.entities()) else {
188            continue;
189        };
190
191        let [anchor1, anchor2] =
192            JointAnchor::compute_local(joint.anchor1, joint.anchor2, pos1.0, pos2.0, rot1, rot2);
193        joint.anchor1 = anchor1;
194        joint.anchor2 = anchor2;
195    }
196}
197
198#[cfg(feature = "debug-plugin")]
199impl DebugRenderConstraint<2> for DistanceJoint {
200    type Context = ();
201
202    fn debug_render(
203        &self,
204        positions: [Vector; 2],
205        rotations: [Rotation; 2],
206        _context: &mut Self::Context,
207        gizmos: &mut Gizmos<PhysicsGizmos>,
208        config: &PhysicsGizmos,
209    ) {
210        let [pos1, pos2] = positions;
211        let [rot1, rot2] = rotations;
212
213        let JointAnchor::Local(local_anchor1) = self.anchor1 else {
214            return;
215        };
216        let JointAnchor::Local(local_anchor2) = self.anchor2 else {
217            return;
218        };
219
220        let anchor1 = pos1 + rot1 * local_anchor1;
221        let anchor2 = pos2 + rot2 * local_anchor2;
222
223        if let Some(anchor_color) = config.joint_anchor_color {
224            gizmos.draw_line(pos1, anchor1, anchor_color);
225            gizmos.draw_line(pos2, anchor2, anchor_color);
226        }
227
228        if let Some(color) = config.joint_separation_color {
229            gizmos.draw_line(anchor1, anchor2, color);
230        }
231    }
232}