avian3d/dynamics/joints/
distance.rs1use 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#[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 pub body1: Entity,
29 pub body2: Entity,
31 pub anchor1: JointAnchor,
33 pub anchor2: JointAnchor,
35 pub limits: DistanceLimit,
37 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 #[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 #[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 #[inline]
75 pub const fn with_local_anchor1(mut self, anchor: Vector) -> Self {
76 self.anchor1 = JointAnchor::Local(anchor);
77 self
78 }
79
80 #[inline]
84 pub const fn with_local_anchor2(mut self, anchor: Vector) -> Self {
85 self.anchor2 = JointAnchor::Local(anchor);
86 self
87 }
88
89 #[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 #[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 #[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 #[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 #[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 #[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 #[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}