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#[cfg_attr(
14 feature = "2d",
15 doc = "A revolute [joint](dynamics::joints) or hinge prevents any relative movement between two bodies, except for rotation about a pivot point defined by the joint anchor."
16)]
17#[cfg_attr(
18 feature = "3d",
19 doc = "A revolute [joint](dynamics::joints) or hinge prevents any relative movement between two bodies, except for rotation about the [`hinge_axis`](Self::hinge_axis) at a pivot point defined by the joint anchor."
20)]
21#[cfg_attr(
25 feature = "2d",
26 doc = "Each revolute joint is defined by a [`JointFrame`] on each body,"
27)]
28#[cfg_attr(
29 feature = "3d",
30 doc = "Each revolute joint is defined by a [`JointFrame`] on each body, a [`hinge_axis`](Self::hinge_axis) about which the bodies can rotate,"
31)]
32#[cfg_attr(feature = "2d", doc = "while allowing rotation at the anchor point.")]
34#[cfg_attr(
35 feature = "3d",
36 doc = "while allowing rotation about the hinge axis at the anchor point."
37)]
38#[doc = include_str!("./images/revolute_joint.svg")]
40#[derive(Component, Clone, Debug, PartialEq, Reflect)]
41#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
42#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
43#[reflect(Component, Debug, MapEntities, PartialEq)]
44#[doc(alias = "HingeJoint")]
45pub struct RevoluteJoint {
46 pub body1: Entity,
48 pub body2: Entity,
50 pub frame1: JointFrame,
53 pub frame2: JointFrame,
56 #[cfg(feature = "3d")]
60 pub hinge_axis: Vector,
61 pub angle_limit: Option<AngleLimit>,
63 pub point_compliance: Scalar,
65 #[cfg(feature = "3d")]
67 pub align_compliance: Scalar,
68 pub limit_compliance: Scalar,
70}
71
72impl EntityConstraint<2> for RevoluteJoint {
73 fn entities(&self) -> [Entity; 2] {
74 [self.body1, self.body2]
75 }
76}
77
78impl RevoluteJoint {
79 #[cfg(feature = "3d")]
81 pub const DEFAULT_HINGE_AXIS: Vector = Vector::Z;
82
83 #[inline]
85 pub const fn new(body1: Entity, body2: Entity) -> Self {
86 Self {
87 body1,
88 body2,
89 frame1: JointFrame::IDENTITY,
90 frame2: JointFrame::IDENTITY,
91 #[cfg(feature = "3d")]
92 hinge_axis: Self::DEFAULT_HINGE_AXIS,
93 angle_limit: None,
94 point_compliance: 0.0,
95 #[cfg(feature = "3d")]
96 align_compliance: 0.0,
97 limit_compliance: 0.0,
98 }
99 }
100
101 #[inline]
105 #[cfg(feature = "3d")]
106 pub const fn with_hinge_axis(mut self, axis: Vector) -> Self {
107 self.hinge_axis = axis;
108 self
109 }
110
111 #[inline]
117 #[deprecated(since = "0.4.0", note = "Use `with_hinge_axis` instead.")]
118 #[cfg(feature = "3d")]
119 pub const fn with_aligned_axis(self, axis: Vector) -> Self {
120 self.with_hinge_axis(axis)
121 }
122
123 #[inline]
125 pub fn with_local_frame1(mut self, frame: impl Into<Isometry>) -> Self {
126 self.frame1 = JointFrame::local(frame);
127 self
128 }
129
130 #[inline]
132 pub fn with_local_frame2(mut self, frame: impl Into<Isometry>) -> Self {
133 self.frame2 = JointFrame::local(frame);
134 self
135 }
136
137 #[inline]
141 pub const fn with_anchor(mut self, anchor: Vector) -> Self {
142 self.frame1.anchor = JointAnchor::FromGlobal(anchor);
143 self.frame2.anchor = JointAnchor::FromGlobal(anchor);
144 self
145 }
146
147 #[inline]
151 pub const fn with_local_anchor1(mut self, anchor: Vector) -> Self {
152 self.frame1.anchor = JointAnchor::Local(anchor);
153 self
154 }
155
156 #[inline]
160 pub const fn with_local_anchor2(mut self, anchor: Vector) -> Self {
161 self.frame2.anchor = JointAnchor::Local(anchor);
162 self
163 }
164
165 #[inline]
169 pub fn with_basis(mut self, basis: impl Into<Rot>) -> Self {
170 let basis = basis.into();
171 self.frame1.basis = JointBasis::FromGlobal(basis);
172 self.frame2.basis = JointBasis::FromGlobal(basis);
173 self
174 }
175
176 #[inline]
180 pub fn with_local_basis1(mut self, basis: impl Into<Rot>) -> Self {
181 self.frame1.basis = JointBasis::Local(basis.into());
182 self
183 }
184
185 #[inline]
189 pub fn with_local_basis2(mut self, basis: impl Into<Rot>) -> Self {
190 self.frame2.basis = JointBasis::Local(basis.into());
191 self
192 }
193
194 #[inline]
201 pub fn local_frame1(&self) -> Option<Isometry> {
202 self.frame1.get_local_isometry()
203 }
204
205 #[inline]
212 pub fn local_frame2(&self) -> Option<Isometry> {
213 self.frame2.get_local_isometry()
214 }
215
216 #[inline]
221 pub const fn local_anchor1(&self) -> Option<Vector> {
222 match self.frame1.anchor {
223 JointAnchor::Local(anchor) => Some(anchor),
224 _ => None,
225 }
226 }
227
228 #[inline]
233 pub const fn local_anchor2(&self) -> Option<Vector> {
234 match self.frame2.anchor {
235 JointAnchor::Local(anchor) => Some(anchor),
236 _ => None,
237 }
238 }
239
240 #[inline]
245 pub fn local_basis1(&self) -> Option<Rot> {
246 match self.frame1.basis {
247 JointBasis::Local(basis) => Some(basis),
248 _ => None,
249 }
250 }
251
252 #[inline]
257 pub fn local_basis2(&self) -> Option<Rot> {
258 match self.frame2.basis {
259 JointBasis::Local(basis) => Some(basis),
260 _ => None,
261 }
262 }
263
264 #[inline]
272 #[cfg(feature = "3d")]
273 pub fn local_hinge_axis1(&self) -> Option<Vector> {
274 match self.frame1.basis {
275 JointBasis::Local(basis) => Some(basis * self.hinge_axis),
276 _ => None,
277 }
278 }
279
280 #[inline]
288 #[cfg(feature = "3d")]
289 pub fn local_hinge_axis2(&self) -> Option<Vector> {
290 match self.frame2.basis {
291 JointBasis::Local(basis) => Some(basis * self.hinge_axis),
292 _ => None,
293 }
294 }
295
296 #[inline]
298 pub const fn with_angle_limits(mut self, min: Scalar, max: Scalar) -> Self {
299 self.angle_limit = Some(AngleLimit::new(min, max));
300 self
301 }
302
303 #[inline]
305 #[deprecated(
306 since = "0.4.0",
307 note = "Use `with_point_compliance`, `with_align_compliance`, and `with_limit_compliance` instead."
308 )]
309 pub const fn with_compliance(mut self, compliance: Scalar) -> Self {
310 self.point_compliance = compliance;
311 #[cfg(feature = "3d")]
312 {
313 self.align_compliance = compliance;
314 }
315 self.limit_compliance = compliance;
316 self
317 }
318
319 #[inline]
321 pub const fn with_point_compliance(mut self, compliance: Scalar) -> Self {
322 self.point_compliance = compliance;
323 self
324 }
325
326 #[inline]
328 #[cfg(feature = "3d")]
329 pub const fn with_align_compliance(mut self, compliance: Scalar) -> Self {
330 self.align_compliance = compliance;
331 self
332 }
333
334 #[inline]
336 pub const fn with_limit_compliance(mut self, compliance: Scalar) -> Self {
337 self.limit_compliance = compliance;
338 self
339 }
340}
341
342impl MapEntities for RevoluteJoint {
343 fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
344 self.body1 = entity_mapper.get_mapped(self.body1);
345 self.body2 = entity_mapper.get_mapped(self.body2);
346 }
347}
348
349pub(super) fn plugin(app: &mut App) {
350 app.add_systems(
351 PhysicsSchedule,
352 update_local_frames.in_set(JointSystems::PrepareLocalFrames),
353 );
354}
355
356fn update_local_frames(
357 mut joints: Query<&mut RevoluteJoint, Changed<RevoluteJoint>>,
358 bodies: Query<(&Position, &Rotation)>,
359) {
360 for mut joint in &mut joints {
361 if matches!(joint.frame1.anchor, JointAnchor::Local(_))
362 && matches!(joint.frame2.anchor, JointAnchor::Local(_))
363 && matches!(joint.frame1.basis, JointBasis::Local(_))
364 && matches!(joint.frame2.basis, JointBasis::Local(_))
365 {
366 continue;
367 }
368
369 let Ok([(pos1, rot1), (pos2, rot2)]) = bodies.get_many(joint.entities()) else {
370 continue;
371 };
372
373 let [frame1, frame2] =
374 JointFrame::compute_local(joint.frame1, joint.frame2, pos1.0, pos2.0, rot1, rot2);
375 joint.frame1 = frame1;
376 joint.frame2 = frame2;
377 }
378}
379
380#[cfg(feature = "debug-plugin")]
381impl DebugRenderConstraint<2> for RevoluteJoint {
382 type Context = ();
383
384 fn debug_render(
385 &self,
386 positions: [Vector; 2],
387 rotations: [Rotation; 2],
388 _context: &mut Self::Context,
389 gizmos: &mut Gizmos<PhysicsGizmos>,
390 config: &PhysicsGizmos,
391 ) {
392 let [pos1, pos2] = positions;
393 let [rot1, rot2] = rotations;
394
395 let Some(local_anchor1) = self.local_anchor1() else {
396 return;
397 };
398 let Some(local_anchor2) = self.local_anchor2() else {
399 return;
400 };
401
402 let anchor1 = pos1 + rot1 * local_anchor1;
403 let anchor2 = pos2 + rot2 * local_anchor2;
404
405 if let Some(anchor_color) = config.joint_anchor_color {
406 gizmos.draw_line(pos1, anchor1, anchor_color);
407 gizmos.draw_line(pos2, anchor2, anchor_color);
408 }
409
410 if let Some(color) = config.joint_separation_color {
411 gizmos.draw_line(anchor1, anchor2, color);
412 }
413 }
414}