Skip to main content

avian3d/dynamics/joints/
mod.rs

1//! **Joints** are a way to connect rigid bodies in a way that restricts their movement relative to each other.
2//! They act as constraints that restrict different *Degrees of Freedom* depending on the joint type.
3//!
4//! # Degrees of Freedom (DOF)
5//!
6#![cfg_attr(
7    feature = "2d",
8    doc = r#"In 2D, rigid bodies can normally translate along the x and y axes and rotate about the z axis.
9Therefore, they have 2 translational DOF and 1 rotational DOF, a total of 3 DOF."#
10)]
11#![cfg_attr(
12    feature = "3d",
13    doc = r#"In 3D, rigid bodies can normally translate and rotate along the x, y, and z axes.
14Therefore, they have 3 translational DOF and 3 rotational DOF, a total of 6 DOF."#
15)]
16//!
17#![cfg_attr(feature="2d", doc = include_str!("./images/2d_dofs.svg"))]
18#![cfg_attr(feature="3d", doc = include_str!("./images/3d_dofs.svg"))]
19//!
20//! Joints limit the degrees of freedom that bodies can have. For example, a [`RevoluteJoint`] or hinge
21//! prevents any relative movement between two bodies, except for rotation about a single axis
22//! at an anchor point.
23//!
24//! Below is a table containing all joints that are currently implemented.
25//!
26//! | Joint              | Allowed 2D DOF            | Allowed 3D DOF              |
27//! | ------------------ | ------------------------- | --------------------------- |
28//! | [`FixedJoint`]     | None                      | None                        |
29//! | [`DistanceJoint`]  | 1 Translation, 1 Rotation | 2 Translations, 3 Rotations |
30//! | [`PrismaticJoint`] | 1 Translation             | 1 Translation               |
31//! | [`RevoluteJoint`]  | 1 Rotation                | 1 Rotation                  |
32#![cfg_attr(
33    feature = "3d",
34    doc = "| [`SphericalJoint`] | -                         | 3 Rotations                 |"
35)]
36//!
37//! # Using Joints
38//!
39//! In Avian, joints are modeled as components. Each joint is spawned as its own entity,
40//! providing the [`Entity`] identifiers of the bodies it should constrain.
41//!
42//! ```
43#![cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
44#![cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
45//! use bevy::prelude::*;
46//!
47//! fn setup(mut commands: Commands) {
48//!     let body1 = commands.spawn(RigidBody::Dynamic).id();
49//!     let body2 = commands.spawn(RigidBody::Dynamic).id();
50//!     
51//!     // Connect the bodies with a fixed joint.
52//!     commands.spawn(FixedJoint::new(body1, body2));
53//! }
54//! ```
55//!
56//! By default, the attached bodies can still collide with each other.
57//! This behavior can be disabled with the [`JointCollisionDisabled`] component.
58//!
59//! ## Joint Frames
60//!
61//! By default, joints use body transforms as their reference for how to constrain the connected bodies.
62//! For example, a [`RevoluteJoint`] aims to make the positions of the two bodies coincide in world space,
63//! while allowing the bodies to rotate freely around a common axis.
64//!
65//! However, it can often be useful to define the attachment point or orientation separately from the body transform.
66//! For example, you may want the [`RevoluteJoint`] to be attached to the corner of a body instead of its center,
67//! and that the other body is rotated by 90 degrees relative to the first body.
68//!
69//! This can be done by configuring the [`JointFrame`] associated with each body. Each joint frame is expressed
70//! by a local [`JointAnchor`] and [`JointBasis`] relative to the transforms of the bodies. The anchor determines
71//! the attachment point, while the basis determines the orientation of the joint frame relative to the body transform.
72//!
73#![doc = include_str!("./images/joint_frame.svg")]
74//!
75//! Storing the frames in local space allows the initial configuration to be preserved even when the bodies are moved.
76//! The frames can also be specified in global coordinates using [`JointFrame::global`], but they are automatically converted
77//! to local frames during the next simulation step.
78//!
79//! Below is an example of configuring [`JointFrame`]s for a [`RevoluteJoint`].
80//!
81//! ```
82#![cfg_attr(feature = "2d", doc = "# use avian2d::{math::RVec2, prelude::*};")]
83#![cfg_attr(feature = "3d", doc = "# use avian3d::{math::RVec3, prelude::*};")]
84//! # use bevy::prelude::*;
85//! # use core::f32::consts::PI;
86//! #
87//! # fn setup(mut commands: Commands) {
88//! #     let body1 = commands.spawn(RigidBody::Dynamic).id();
89//! #     let body2 = commands.spawn(RigidBody::Dynamic).id();
90//! #
91//! // Connect two bodies with a revolute joint.
92//! // Set the global anchor point and rotate the first frame by 45 degrees about the local z axis.
93//! commands.spawn((
94//!     RevoluteJoint::new(body1, body2)
95#![cfg_attr(feature = "2d", doc = "        .with_anchor(RVec2::new(5.0, 2.0))")]
96#![cfg_attr(
97    feature = "3d",
98    doc = "        .with_anchor(RVec3::new(5.0, 2.0, 0.0))"
99)]
100#![cfg_attr(feature = "2d", doc = "        .with_local_basis1(PI / 4.0),")]
101#![cfg_attr(
102    feature = "3d",
103    doc = "        .with_local_basis1(Quat::from_rotation_z(PI / 4.0)),"
104)]
105//! ));
106//! # }
107//! ```
108//!
109//! ## Damping
110//!
111//! By default, no work is done to dampen the movement of bodies connected by a joint.
112//! A pendulum will swing indefinitely, unless explicitly stopped or it loses energy due to simulation inaccuracies.
113//!
114//! It can often be desirable to dampen the relative velocities of bodies connected by a joint to slow them down over time.
115//! This can be done using the [`JointDamping`] component.
116//!
117//! ```
118#![cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
119#![cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
120//! # use bevy::prelude::*;
121//! #
122//! # fn setup(mut commands: Commands) {
123//! #     let body1 = commands.spawn(RigidBody::Dynamic).id();
124//! #     let body2 = commands.spawn(RigidBody::Dynamic).id();
125//! #
126//! // Connect two bodies with a distance joint.
127//! // Apply linear and angular damping to the joint.
128//! commands.spawn((
129//!     DistanceJoint::new(body1, body2),
130//!     JointDamping {
131//!         linear: 0.1,  // Linear damping
132//!         angular: 0.1, // Angular damping
133//!     },
134//! ));
135//! # }
136//! ```
137//!
138//! ## Reading Joint Forces
139//!
140//! Joints apply forces and torques to constrain the bodies they are attached to.
141//! These forces can be read by adding the [`JointForces`] component to the joint entity:
142//!
143//! ```
144#![cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
145#![cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
146//! # use bevy::prelude::*;
147//! #
148//! # fn setup(mut commands: Commands) {
149//! #     let body1 = commands.spawn(RigidBody::Dynamic).id();
150//! #     let body2 = commands.spawn(RigidBody::Dynamic).id();
151//! #
152//! // Connect two bodies with a revolute joint.
153//! // Read the forces applied by the joint.
154//! commands.spawn((
155//!     RevoluteJoint::new(body1, body2),
156//!     JointForces::new(),
157//! ));
158//! # }
159//! ```
160//!
161//! and querying for it in a system:
162//!
163//! ```
164#![cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
165#![cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
166//! # use bevy::prelude::*;
167//! #
168//! fn read_joint_forces(query: Query<&JointForces>) {
169//!     for joint_forces in &query {
170//!         println!("Joint force: {}", joint_forces.force());
171//!     }
172//! }
173//! ```
174//!
175//! This can often be useful for determining when to "break" a joint with the [`JointDisabled`] component
176//! when its forces exceed a certain threshold. An example of this can be found in the next section on disabling joints.
177//!
178//! ## Disabling Joints
179//!
180//! It can sometimes be useful to temporarily disable a joint without removing it from the world.
181//! This can be done by adding the [`JointDisabled`] component to the joint entity.
182//!
183//! A common use case is to "break" a joint when its [`JointForces`] exceed a certain threshold.
184//! This could be done with a system like the following:
185//!
186//! ```
187#![cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
188#![cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
189//! # use bevy::prelude::*;
190//! #
191//! const BREAK_THRESHOLD: f32 = 500.0; // Example threshold
192//!
193//! fn break_joints(
194//!     mut commands: Commands,
195//!     query: Query<(Entity, &JointForces), Without<JointDisabled>>,
196//! ) {
197//!     for (entity, joint_forces) in &query {
198//!         if joint_forces.force().length() > BREAK_THRESHOLD {
199//!             // Break the joint by adding the `JointDisabled` component.
200//!             // Alternatively, you could simply remove the joint component or despawn the entity.
201//!            commands.entity(entity).insert(JointDisabled);
202//!         }
203//!     }
204//! }
205//! ```
206//!
207//! Disabled joints can be re-enabled by removing the [`JointDisabled`] component.
208//!
209//! ## Other Configuration
210//!
211//! Different joints may have different configuration options. They may allow you to change the axis of allowed
212//! translation or rotation, and apply limits or motors for those axes.
213//!
214//! Take a look at the documentation and methods of each joint to see all the different configuration options.
215
216pub mod joint_graph;
217
218mod distance;
219mod fixed;
220mod motor;
221mod prismatic;
222mod revolute;
223#[cfg(feature = "3d")]
224mod spherical;
225#[cfg(test)]
226mod tests;
227
228pub use distance::DistanceJoint;
229pub use fixed::FixedJoint;
230pub use motor::{AngularMotor, LinearMotor, MotorModel};
231pub use prismatic::PrismaticJoint;
232pub use revolute::RevoluteJoint;
233#[cfg(feature = "3d")]
234pub use spherical::SphericalJoint;
235
236use crate::{dynamics::joints::joint_graph::JointGraph, prelude::*};
237use bevy::{
238    ecs::{entity::MapEntities, lifecycle::HookContext, world::DeferredWorld},
239    prelude::*,
240};
241
242#[cfg(feature = "3d")]
243use core::f32::consts::{PI, TAU};
244
245/// A plugin for managing and initializing [joints](self).
246///
247/// Note that this does *not* include the actual joint constraint solver.
248/// For a built-in joint solver, enable the `xpbd_joints` feature, and use the [`XpbdSolverPlugin`].
249pub struct JointPlugin;
250
251impl Plugin for JointPlugin {
252    fn build(&self, app: &mut App) {
253        app.add_plugins((
254            fixed::plugin,
255            distance::plugin,
256            prismatic::plugin,
257            revolute::plugin,
258            #[cfg(feature = "3d")]
259            spherical::plugin,
260        ));
261
262        app.configure_sets(
263            PhysicsSchedule,
264            JointSystems::PrepareLocalFrames
265                .after(SolverSystems::PrepareSolverBodies)
266                .before(SolverSystems::PrepareJoints),
267        );
268    }
269}
270
271/// System sets for [joints](dynamics::joints).
272#[derive(SystemSet, Clone, Debug, Hash, PartialEq, Eq)]
273pub enum JointSystems {
274    /// A system set for preparing local [`JointFrame`]s.
275    PrepareLocalFrames,
276}
277
278/// A trait for constraints between entities.
279pub trait EntityConstraint<const ENTITY_COUNT: usize>: MapEntities {
280    /// The entities participating in the constraint.
281    fn entities(&self) -> [Entity; ENTITY_COUNT];
282}
283
284/// A limit that indicates that the distance between two points should be between `min` and `max`.
285#[derive(Clone, Copy, Debug, PartialEq, Reflect)]
286#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
287#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
288#[reflect(Debug, PartialEq)]
289pub struct DistanceLimit {
290    /// The minimum distance between two points.
291    pub min: f32,
292    /// The maximum distance between two points.
293    pub max: f32,
294}
295
296impl From<f32> for DistanceLimit {
297    /// Converts the given `limit` into a [`DistanceLimit`] where `min == max`.
298    fn from(limit: f32) -> DistanceLimit {
299        DistanceLimit {
300            min: limit,
301            max: limit,
302        }
303    }
304}
305
306impl From<[f32; 2]> for DistanceLimit {
307    /// Converts the given `[min, max]` array into a [`DistanceLimit`].
308    fn from([min, max]: [f32; 2]) -> DistanceLimit {
309        DistanceLimit { min, max }
310    }
311}
312
313impl From<(f32, f32)> for DistanceLimit {
314    /// Converts the given `(min, max)` pair into a [`DistanceLimit`].
315    fn from((min, max): (f32, f32)) -> DistanceLimit {
316        DistanceLimit { min, max }
317    }
318}
319
320impl DistanceLimit {
321    /// A `DistanceLimit` with `min` and `max` set to zero.
322    pub const ZERO: Self = Self { min: 0.0, max: 0.0 };
323
324    /// Creates a new `DistanceLimit`.
325    pub const fn new(min: f32, max: f32) -> Self {
326        Self { min, max }
327    }
328
329    /// Returns the direction and magnitude of the positional correction required
330    /// to limit the given `separation` to be within the distance limit.
331    pub fn compute_correction(&self, separation: Vector) -> (Vector, f32) {
332        let distance_squared = separation.length_squared();
333
334        if distance_squared <= f32::EPSILON {
335            return (Vector::ZERO, 0.0);
336        }
337
338        let distance = distance_squared.sqrt();
339
340        // Equation 25
341        if distance < self.min {
342            // Separation distance lower limit
343            (separation / distance, (self.min - distance))
344        } else if distance > self.max {
345            // Separation distance upper limit
346            (-separation / distance, (distance - self.max))
347        } else {
348            (Vector::ZERO, 0.0)
349        }
350    }
351
352    /// Returns the positional correction required to limit the given `separation`
353    /// to be within the distance limit along a given `axis`.
354    pub fn compute_correction_along_axis(&self, separation: Vector, axis: Vector) -> Vector {
355        let a = separation.dot(axis);
356
357        // Equation 25
358        if a < self.min {
359            // Separation distance lower limit
360            axis * (self.min - a)
361        } else if a > self.max {
362            // Separation distance upper limit
363            -axis * (a - self.max)
364        } else {
365            Vector::ZERO
366        }
367    }
368}
369
370/// A limit that indicates that angles should be between `alpha` and `beta`.
371#[derive(Clone, Copy, Debug, PartialEq, Reflect)]
372#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
373#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
374#[reflect(Debug, PartialEq)]
375pub struct AngleLimit {
376    /// The minimum angle.
377    pub min: f32,
378    /// The maximum angle.
379    pub max: f32,
380}
381
382impl From<f32> for AngleLimit {
383    /// Converts the given `limit` into a [`AngleLimit`] where `min == max`.
384    fn from(limit: f32) -> AngleLimit {
385        AngleLimit {
386            min: limit,
387            max: limit,
388        }
389    }
390}
391
392impl From<[f32; 2]> for AngleLimit {
393    /// Converts the given `[min, max]` array into a [`AngleLimit`].
394    fn from([min, max]: [f32; 2]) -> AngleLimit {
395        AngleLimit { min, max }
396    }
397}
398
399impl From<(f32, f32)> for AngleLimit {
400    /// Converts the given `(min, max)` pair into a [`AngleLimit`].
401    fn from((min, max): (f32, f32)) -> AngleLimit {
402        AngleLimit { min, max }
403    }
404}
405
406impl AngleLimit {
407    /// An `AngleLimit` with `alpha` and `beta` set to zero.
408    pub const ZERO: Self = Self { min: 0.0, max: 0.0 };
409
410    /// Creates a new `AngleLimit`.
411    pub const fn new(min: f32, max: f32) -> Self {
412        Self { min, max }
413    }
414
415    /// Returns the angular correction required to limit the `angle_difference`
416    /// to be within the angle limits.
417    #[cfg(feature = "2d")]
418    pub fn compute_correction(&self, angle_difference: f32, max_correction: f32) -> Option<f32> {
419        let correction = if angle_difference < self.min {
420            angle_difference - self.min
421        } else if angle_difference > self.max {
422            angle_difference - self.max
423        } else {
424            return None;
425        };
426
427        Some(correction.min(max_correction))
428    }
429
430    /// Returns the angular correction required to limit the angle between `axis1` and `axis2`
431    /// to be within the angle limits with respect to the `limit_axis`.
432    #[cfg(feature = "3d")]
433    pub fn compute_correction(
434        &self,
435        limit_axis: Vector,
436        axis1: Vector,
437        axis2: Vector,
438        max_correction: f32,
439    ) -> Option<Vector> {
440        // [limit_axis, axis1, axis2] = [n, n1, n2] in XPBD rigid body paper.
441
442        // Angle between axis1 and axis2 with respect to limit_axis.
443        let mut phi = axis1.cross(axis2).dot(limit_axis).asin();
444
445        // `asin` returns the angle in the [-pi/2, pi/2] range.
446        // This is correct if the angle between n1 and n2 is acute,
447        // but obtuse angles must be accounted for.
448        if axis1.dot(axis2) < 0.0 {
449            phi = PI - phi;
450        }
451
452        // Map the angle to the [-pi, pi] range.
453        if phi > PI {
454            phi -= TAU;
455        }
456
457        // The XPBD rigid body paper has this, but the angle
458        // should already be in the correct range.
459        //
460        // if phi < -PI {
461        //     phi += TAU;
462        // }
463
464        // Only apply a correction if the limit is violated.
465        if phi < self.min || phi > self.max {
466            // phi now represents the angle between axis1 and axis2.
467
468            // Clamp phi to get the target angle.
469            phi = phi.clamp(self.min, self.max);
470
471            // Create a quaternion that represents the rotation.
472            let rot = Quat::from_axis_angle(limit_axis, phi);
473
474            // Rotate axis1 by the target angle and compute the correction.
475            return Some((rot * axis1).cross(axis2).clamp_length_max(max_correction));
476        }
477
478        None
479    }
480}
481
482/// A marker component that indicates that a [joint](self) is disabled and should not constrain the bodies it is attached to.
483/// Must be on the same entity as the joint.
484///
485/// This is useful for temporarily disabling a joint without removing it from the world.
486/// To re-enable the joint, simply remove this component.
487///
488/// Note that when re-enabling the joint, the bodies may snap back violently
489/// if they have moved significantly from the constrained positions while the joint was disabled.
490///
491/// # Example
492///
493/// A common use case is to "break" a joint when its [`JointForces`] exceed a certain threshold.
494/// This could be done with a system like the following:
495///
496/// ```
497#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
498#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
499/// # use bevy::prelude::*;
500/// #
501/// const BREAK_THRESHOLD: f32 = 500.0;
502///
503/// fn break_joints(
504///     mut commands: Commands,
505///     query: Query<(Entity, &JointForces), Without<JointDisabled>>,
506/// ) {
507///     for (entity, joint_forces) in &query {
508///         if joint_forces.force().length() > BREAK_THRESHOLD {
509///             // Break the joint by adding the `JointDisabled` component.
510///             // Alternatively, you could simply remove the joint component or despawn the entity.
511///            commands.entity(entity).insert(JointDisabled);
512///         }
513///     }
514/// }
515/// ```
516///
517/// Disabled joints can be re-enabled by removing the [`JointDisabled`] component.
518///
519/// # Related Components
520///
521/// - [`JointCollisionDisabled`]: Disables collision between bodies connected by a joint.
522/// - [`RigidBodyDisabled`]: Disables a rigid body.
523/// - [`ColliderDisabled`]: Disables a collider.
524#[derive(Reflect, Clone, Copy, Component, Debug, Default)]
525#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
526#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
527#[reflect(Debug, Component, Default)]
528pub struct JointDisabled;
529
530/// A marker component that disables collision between [rigid bodies](RigidBody)
531/// connected by a [joint](self). Must be on the same entity as the joint.
532///
533/// # Example
534///
535/// ```
536#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
537#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
538/// # use bevy::prelude::*;
539/// #
540/// # fn setup(mut commands: Commands) {
541/// # let entity1 = commands.spawn(RigidBody::Dynamic).id();
542/// # let entity2 = commands.spawn(RigidBody::Dynamic).id();
543/// #
544/// // Connect the bodies with a fixed joint.
545/// // Disable collision between the two bodies.
546/// commands.spawn((
547///     FixedJoint::new(entity1, entity2),
548///     JointCollisionDisabled,
549/// ));
550/// # }
551/// ```
552#[derive(Component, Debug, Default, PartialEq, Reflect)]
553#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
554#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
555#[reflect(Component, Debug, PartialEq)]
556#[component(on_add = JointCollisionDisabled::on_add, on_remove = JointCollisionDisabled::on_remove)]
557pub struct JointCollisionDisabled;
558
559// TODO: Turn these into observers.
560impl JointCollisionDisabled {
561    fn on_add(mut world: DeferredWorld, ctx: HookContext) {
562        let entity = ctx.entity;
563
564        // Update the `contacts_enabled` property of the joint edge.
565        // Note: The `JointGraphPlugin` handles the removal of contacts between the bodies.
566        let mut joint_graph = world.resource_mut::<JointGraph>();
567        if let Some(joint_edge) = joint_graph.get_mut(entity) {
568            joint_edge.collision_disabled = true;
569        }
570    }
571
572    fn on_remove(mut world: DeferredWorld, ctx: HookContext) {
573        let entity = ctx.entity;
574
575        // Update the `contacts_enabled` property of the joint edge.
576        let mut joint_graph = world.resource_mut::<JointGraph>();
577        if let Some(joint_edge) = joint_graph.get_mut(entity) {
578            joint_edge.collision_disabled = false;
579        }
580    }
581}
582
583/// A component for applying damping to the relative linear and angular velocities
584/// of bodies connected by a [joint](self).
585///
586/// # Example
587///
588/// ```
589#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
590#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
591/// # use bevy::prelude::*;
592/// #
593/// # fn setup(mut commands: Commands) {
594/// #     let body1 = commands.spawn(RigidBody::Dynamic).id();
595/// #     let body2 = commands.spawn(RigidBody::Dynamic).id();
596/// #
597/// // Connect two bodies with a distance joint.
598/// // Apply linear and angular damping to the joint.
599/// commands.spawn((
600///     DistanceJoint::new(body1, body2),
601///     JointDamping {
602///         linear: 0.1,  // Linear damping
603///         angular: 0.1, // Angular damping
604///     },
605/// ));
606/// # }
607/// ```
608#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Reflect)]
609#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
610#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
611#[reflect(Component, Debug, PartialEq)]
612pub struct JointDamping {
613    /// Linear damping applied by the joint.
614    pub linear: f32,
615    /// Angular damping applied by the joint.
616    pub angular: f32,
617}
618
619/// A component for reading the force and torque exerted by a [joint](self).
620///
621/// This is not inserted automatically for joints, and must be added manually.
622///
623/// # Example
624///
625/// The forces exerted by a joint can be read by adding the [`JointForces`] component to the joint entity:
626///
627/// ```
628#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
629#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
630/// # use bevy::prelude::*;
631/// #
632/// # fn setup(mut commands: Commands) {
633/// #     let body1 = commands.spawn(RigidBody::Dynamic).id();
634/// #     let body2 = commands.spawn(RigidBody::Dynamic).id();
635/// #
636/// // Connect two bodies with a revolute joint.
637/// // Read the forces applied by the joint.
638/// commands.spawn((
639///     RevoluteJoint::new(body1, body2),
640///     JointForces::new(),
641/// ));
642/// # }
643/// ```
644///
645/// and querying for it in a system:
646///
647/// ```
648#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
649#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
650/// # use bevy::prelude::*;
651/// #
652/// fn read_joint_forces(query: Query<&JointForces>) {
653///     for joint_forces in &query {
654///         println!("Joint force: {}", joint_forces.force());
655///     }
656/// }
657/// ```
658///
659/// This can often be useful for determining when to "break" a joint with the [`JointDisabled`] component
660/// when its forces exceed a certain threshold. An example of this can be found in the [`JointDisabled`] documentation.
661#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Reflect)]
662#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
663#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
664#[reflect(Component, Debug, PartialEq)]
665pub struct JointForces {
666    force: Vector,
667    torque: AngularVector,
668    motor_force: f32,
669}
670
671impl JointForces {
672    /// Creates a new [`JointForces`] for reading the forces applied by a joint.
673    #[inline]
674    pub const fn new() -> Self {
675        Self {
676            force: Vector::ZERO,
677            #[cfg(feature = "2d")]
678            torque: 0.0,
679            #[cfg(feature = "3d")]
680            torque: Vector::ZERO,
681            motor_force: 0.0,
682        }
683    }
684
685    /// Returns the force applied by the joint.
686    #[inline]
687    pub const fn force(&self) -> Vector {
688        self.force
689    }
690
691    /// Returns the torque applied by the joint.
692    #[inline]
693    pub const fn torque(&self) -> AngularVector {
694        self.torque
695    }
696
697    /// Returns the force or torque applied by the motor, if any.
698    ///
699    /// For angular motors ([`AngularMotor`]), this is the torque in N·m.
700    /// For linear motors ([`LinearMotor`]), this is the force in N.
701    #[inline]
702    pub const fn motor_force(&self) -> f32 {
703        self.motor_force
704    }
705
706    /// Sets the force applied by the joint.
707    ///
708    /// This should be done automatically by the joint solver.
709    #[inline]
710    pub const fn set_force(&mut self, force: Vector) {
711        self.force = force;
712    }
713
714    /// Sets the torque applied by the joint.
715    ///
716    /// This should be done automatically by the joint solver.
717    #[inline]
718    pub const fn set_torque(&mut self, torque: AngularVector) {
719        self.torque = torque;
720    }
721
722    /// Sets the motor force or torque.
723    ///
724    /// This should be done automatically by the joint solver.
725    #[inline]
726    pub const fn set_motor_force(&mut self, motor_force: f32) {
727        self.motor_force = motor_force;
728    }
729}
730
731/// The [reference frame] of a body that is being constrained by a [joint](self).
732///
733/// Each joint defines a connection between the translation and rotation of two reference frames.
734/// For example, a [`RevoluteJoint`] aims to make the positions of the two frames coincide in world space,
735/// while allowing the frames to rotate freely around a common axis.
736///
737/// Reference frames for joints are expressed by a local [`JointAnchor`] and [`JointBasis`]
738/// relative to the transforms of the bodies. The anchor determines the attachment point,
739/// while the basis determines the orientation of the joint frame relative to the body transform.
740/// Together, they form a local isometry that defines the joint frame.
741///
742#[doc = include_str!("./images/joint_frame.svg")]
743///
744/// Storing the frames in local space allows the initial configuration to be preserved even when the bodies are moved.
745/// The frames can also be specified in global coordinates using [`JointFrame::global`], but they are automatically converted
746/// to local frames during the next simulation step.
747///
748/// [reference frame]: https://en.wikipedia.org/wiki/Frame_of_reference
749///
750/// # Example
751///
752/// ```
753#[cfg_attr(feature = "2d", doc = "# use avian2d::{math::RVec2, prelude::*};")]
754#[cfg_attr(feature = "3d", doc = "# use avian3d::{math::RVec3, prelude::*};")]
755/// # use bevy::prelude::*;
756/// # use core::f32::consts::PI;
757/// #
758/// # fn setup(mut commands: Commands) {
759/// #     let body1 = commands.spawn(RigidBody::Dynamic).id();
760/// #     let body2 = commands.spawn(RigidBody::Dynamic).id();
761/// #
762/// // Connect two bodies with a revolute joint.
763/// // Set the global anchor point and rotate the first frame by 45 degrees about the local z axis.
764/// commands.spawn((
765///     RevoluteJoint::new(body1, body2)
766#[cfg_attr(feature = "2d", doc = "        .with_anchor(RVec2::new(5.0, 2.0))")]
767#[cfg_attr(
768    feature = "3d",
769    doc = "        .with_anchor(RVec3::new(5.0, 2.0, 0.0))"
770)]
771#[cfg_attr(feature = "2d", doc = "        .with_local_basis1(PI / 4.0),")]
772#[cfg_attr(
773    feature = "3d",
774    doc = "        .with_local_basis1(Quat::from_rotation_z(PI / 4.0)),"
775)]
776/// ));
777/// # }
778/// ```
779#[derive(Clone, Copy, Debug, Default, PartialEq, Reflect)]
780#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
781#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
782#[reflect(Debug, PartialEq)]
783pub struct JointFrame {
784    /// The translation of the joint frame relative to the body transform.
785    ///
786    /// This defines the anchor point where the bodies are attached to each other.
787    pub anchor: JointAnchor,
788    /// The rotation of the joint frame relative to the body transform.
789    ///
790    /// This defines the orientation of the basis relative to the body transform.
791    pub basis: JointBasis,
792}
793
794impl JointFrame {
795    /// The identity reference frame, with the local anchor and basis set to zero.
796    ///
797    /// This represents a reference frame that aligns with the body transform.
798    pub const IDENTITY: Self = Self {
799        anchor: JointAnchor::ZERO,
800        basis: JointBasis::IDENTITY,
801    };
802
803    /// Creates a [`JointFrame`] with the given local isometry.
804    #[inline]
805    pub fn local(isometry: impl Into<Isometry>) -> Self {
806        let isometry: Isometry = isometry.into();
807        #[cfg(feature = "2d")]
808        let anchor = isometry.translation;
809        #[cfg(feature = "3d")]
810        let anchor = Vec3::from(isometry.translation);
811        Self {
812            anchor: JointAnchor::Local(anchor),
813            basis: JointBasis::Local(isometry.rotation),
814        }
815    }
816
817    /// Creates a [`JointFrame`] with the given global isometry.
818    ///
819    /// The global frame will be converted to a local frame relative to the body transform
820    /// during the next simulation step.
821    #[inline]
822    pub fn global(isometry: impl Into<Isometry>) -> Self {
823        let isometry: Isometry = isometry.into();
824        #[cfg(feature = "2d")]
825        let anchor = isometry.translation.real();
826        #[cfg(feature = "3d")]
827        let anchor = Vec3::from(isometry.translation).real();
828        Self {
829            anchor: JointAnchor::FromGlobal(anchor),
830            basis: JointBasis::FromGlobal(isometry.rotation),
831        }
832    }
833
834    /// Returns the joint frame as a local isometry.
835    ///
836    /// If the frame is specified in global coordinates, this returns `None`.
837    #[inline]
838    #[allow(clippy::unnecessary_cast)]
839    pub fn get_local_isometry(&self) -> Option<Isometry> {
840        let translation = match self.anchor {
841            JointAnchor::Local(anchor) => anchor,
842            JointAnchor::FromGlobal(_) => return None,
843        };
844        let rotation = match self.basis {
845            JointBasis::Local(basis) => basis,
846            JointBasis::FromGlobal(_) => return None,
847        };
848        Some(Isometry::new(translation, rotation))
849    }
850
851    /// Returns the joint frame as a global isometry.
852    ///
853    /// If the frame is specified in local coordinates, this returns `None`.
854    #[inline]
855    #[allow(clippy::unnecessary_cast)]
856    pub fn get_global_isometry(&self) -> Option<Isometry> {
857        let translation = match self.anchor {
858            JointAnchor::FromGlobal(anchor) => anchor.f32(),
859            JointAnchor::Local(_) => return None,
860        };
861        let rotation = match self.basis {
862            JointBasis::FromGlobal(basis) => basis,
863            JointBasis::Local(_) => return None,
864        };
865        Some(Isometry::new(translation, rotation))
866    }
867
868    /// Computes a local frames for the given [`JointFrame`]s
869    /// corresponding to the transforms of two bodies constrained by a joint.
870    ///
871    /// This is used to initialize local frames when a joint is inserted.
872    #[inline]
873    #[must_use]
874    pub fn compute_local(
875        frame1: Self,
876        frame2: Self,
877        pos1: RVector,
878        pos2: RVector,
879        rot1: impl Into<Rot>,
880        rot2: impl Into<Rot>,
881    ) -> [JointFrame; 2] {
882        let rot1 = rot1.into();
883        let rot2 = rot2.into();
884
885        let [local_anchor1, local_anchor2] =
886            JointAnchor::compute_local(frame1.anchor, frame2.anchor, pos1, pos2, rot1, rot2);
887        let [local_basis1, local_basis2] =
888            JointBasis::compute_local(frame1.basis, frame2.basis, rot1, rot2);
889
890        [
891            JointFrame {
892                anchor: local_anchor1,
893                basis: local_basis1,
894            },
895            JointFrame {
896                anchor: local_anchor2,
897                basis: local_basis2,
898            },
899        ]
900    }
901}
902
903/// The translation of a [`JointFrame`], defining the anchor point where the bodies are attached to each other.
904///
905/// Each joint anchor is stored in local space relative to the transform of the body it is attached to.
906/// This way, the initial configuration of the joint is preserved even when the body is moved.
907///
908/// The initial anchor can also be specified in world space using [`JointAnchor::FromGlobal`],
909/// but it is automatically converted to [`JointAnchor::Local`] during the next simulation step.
910///
911/// By default, a local anchor of zero is used, and the anchor aligns with the body transform.
912#[derive(Clone, Copy, Debug, PartialEq, Reflect)]
913#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
914#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
915#[reflect(Debug, PartialEq)]
916pub enum JointAnchor {
917    /// The anchor point is specified in local coordinates relative to the body transform.
918    Local(Vector),
919    /// The anchor point is specified in global coordinates.
920    FromGlobal(RVector),
921}
922
923impl Default for JointAnchor {
924    fn default() -> Self {
925        Self::ZERO
926    }
927}
928
929impl JointAnchor {
930    /// The anchor point at the local origin.
931    ///
932    /// This represents an anchor that aligns with the body transform.
933    pub const ZERO: Self = Self::Local(Vector::ZERO);
934
935    /// Computes [`JointAnchor::Local`]s for the given [`JointAnchor`]s
936    /// corresponding to the transforms of two bodies constrained by a joint.
937    pub fn compute_local(
938        anchor1: Self,
939        anchor2: Self,
940        pos1: RVector,
941        pos2: RVector,
942        rot1: impl Into<Rot>,
943        rot2: impl Into<Rot>,
944    ) -> [Self; 2] {
945        let rot1 = rot1.into();
946        let rot2 = rot2.into();
947
948        let [local_anchor1, local_anchor2] = match [anchor1, anchor2] {
949            [JointAnchor::Local(anchor1), JointAnchor::Local(anchor2)] => [anchor1, anchor2],
950            [
951                JointAnchor::FromGlobal(anchor1),
952                JointAnchor::FromGlobal(anchor2),
953            ] => [
954                rot1.inverse() * (anchor1 - pos1).f32(),
955                rot2.inverse() * (anchor2 - pos2).f32(),
956            ],
957            [
958                JointAnchor::Local(anchor1),
959                JointAnchor::FromGlobal(anchor2),
960            ] => [anchor1, rot2.inverse() * (anchor2 - pos2).f32()],
961            [
962                JointAnchor::FromGlobal(anchor1),
963                JointAnchor::Local(anchor2),
964            ] => [rot1.inverse() * (anchor1 - pos1).f32(), anchor2],
965        };
966
967        [
968            JointAnchor::Local(local_anchor1),
969            JointAnchor::Local(local_anchor2),
970        ]
971    }
972}
973
974impl From<JointAnchor> for JointFrame {
975    fn from(anchor: JointAnchor) -> Self {
976        Self {
977            anchor,
978            basis: JointBasis::IDENTITY,
979        }
980    }
981}
982
983/// The rotation of a [`JointFrame`], defining the basis of the joint frame relative to the body transform.
984///
985/// Each joint basis is stored in local space relative to the transform of the body it is attached to.
986/// This way, the initial configuration of the joint is preserved even when the body is moved.
987///
988/// The initial basis can also be specified in world space using [`JointBasis::FromGlobal`],
989/// but it is automatically converted to [`JointBasis::Local`] during the next simulation step.
990///
991/// By default, a local identity basis is used, and the basis aligns with the body transform.
992#[derive(Clone, Copy, Debug, PartialEq, Reflect)]
993#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
994#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
995#[reflect(Debug, PartialEq)]
996pub enum JointBasis {
997    /// The basis is specified in local space relative to the body transform.
998    Local(Rot),
999    /// The basis is specified in world space.
1000    FromGlobal(Rot),
1001}
1002
1003impl Default for JointBasis {
1004    fn default() -> Self {
1005        Self::IDENTITY
1006    }
1007}
1008
1009impl JointBasis {
1010    /// The identity basis.
1011    ///
1012    /// This represents a basis that aligns with the body transform.
1013    pub const IDENTITY: Self = Self::Local(Rot::IDENTITY);
1014
1015    /// Creates a [`JointBasis::Local`] from the given local `x_axis`.
1016    ///
1017    /// The y-axis is computed as the counterclockwise perpendicular axis to the x-axis.
1018    #[inline]
1019    #[cfg(feature = "2d")]
1020    pub fn from_local_x(x_axis: Vec2) -> Self {
1021        Self::Local(orthonormal_basis([x_axis, Vec2::new(-x_axis.y, x_axis.x)]))
1022    }
1023
1024    /// Creates a [`JointBasis::Local`] from the given local `y_axis`.
1025    ///
1026    /// The x-axis is computed as the clockwise perpendicular axis to the y-axis.
1027    #[inline]
1028    #[cfg(feature = "2d")]
1029    pub fn from_local_y(y_axis: Vec2) -> Self {
1030        Self::Local(orthonormal_basis([Vec2::new(y_axis.y, -y_axis.x), y_axis]))
1031    }
1032
1033    /// Creates a [`JointBasis::Local`] from the given local `x_axis` and `y_axis`.
1034    ///
1035    /// The z-axis is computed as the cross product of the x and y axes.
1036    #[inline]
1037    #[cfg(feature = "3d")]
1038    pub fn from_local_xy(x_axis: Vec3, y_axis: Vec3) -> Self {
1039        Self::Local(orthonormal_basis([x_axis, y_axis, x_axis.cross(y_axis)]))
1040    }
1041
1042    /// Creates a [`JointBasis::Local`] from the given local `x_axis` and `z_axis`.
1043    ///
1044    /// The y-axis is computed as the cross product of the z and x axes.
1045    #[inline]
1046    #[cfg(feature = "3d")]
1047    pub fn from_local_xz(x_axis: Vec3, z_axis: Vec3) -> Self {
1048        Self::Local(orthonormal_basis([x_axis, z_axis.cross(x_axis), z_axis]))
1049    }
1050
1051    /// Creates a [`JointBasis::Local`] from the given local `y_axis` and `z_axis`.
1052    ///
1053    /// The x-axis is computed as the cross product of the y and z axes.
1054    #[inline]
1055    #[cfg(feature = "3d")]
1056    pub fn from_local_yz(y_axis: Vec3, z_axis: Vec3) -> Self {
1057        Self::Local(orthonormal_basis([y_axis.cross(z_axis), y_axis, z_axis]))
1058    }
1059
1060    /// Creates a [`JointBasis::FromGlobal`] from the given global `x_axis`.
1061    ///
1062    /// The y-axis is computed as the counterclockwise perpendicular axis to the x-axis.
1063    #[inline]
1064    #[cfg(feature = "2d")]
1065    pub fn from_global_x(x_axis: Vec2) -> Self {
1066        Self::FromGlobal(orthonormal_basis([x_axis, Vec2::new(-x_axis.y, x_axis.x)]))
1067    }
1068
1069    /// Creates a [`JointBasis::FromGlobal`] from the given global `y_axis`.
1070    ///
1071    /// The x-axis is computed as the clockwise perpendicular axis to the y-axis.
1072    #[inline]
1073    #[cfg(feature = "2d")]
1074    pub fn from_global_y(y_axis: Vec2) -> Self {
1075        Self::FromGlobal(orthonormal_basis([Vec2::new(y_axis.y, -y_axis.x), y_axis]))
1076    }
1077
1078    /// Creates a [`JointBasis::FromGlobal`] from the given global `x_axis` and `y_axis`.
1079    ///
1080    /// The z-axis is computed as the cross product of the x and y axes.
1081    #[inline]
1082    #[cfg(feature = "3d")]
1083    pub fn from_global_xy(x_axis: Vec3, y_axis: Vec3) -> Self {
1084        Self::FromGlobal(orthonormal_basis([x_axis, y_axis, x_axis.cross(y_axis)]))
1085    }
1086
1087    /// Creates a [`JointBasis::FromGlobal`] from the given global `x_axis` and `z_axis`.
1088    ///
1089    /// The y-axis is computed as the cross product of the z and x axes.
1090    #[inline]
1091    #[cfg(feature = "3d")]
1092    pub fn from_global_xz(x_axis: Vec3, z_axis: Vec3) -> Self {
1093        Self::FromGlobal(orthonormal_basis([x_axis, z_axis.cross(x_axis), z_axis]))
1094    }
1095
1096    /// Creates a [`JointBasis::FromGlobal`] from the given global `y_axis` and `z_axis`.
1097    ///
1098    /// The x-axis is computed as the cross product of the y and z axes.
1099    #[inline]
1100    #[cfg(feature = "3d")]
1101    pub fn from_global_yz(y_axis: Vec3, z_axis: Vec3) -> Self {
1102        Self::FromGlobal(orthonormal_basis([y_axis.cross(z_axis), y_axis, z_axis]))
1103    }
1104
1105    /// Computes a [`JointBasis::Local`] for the given [`JointBasis`]s
1106    /// corresponding to the transforms of two bodies constrained by a joint.
1107    pub fn compute_local(
1108        rotation1: Self,
1109        rotation2: Self,
1110        rot1: impl Into<Rot>,
1111        rot2: impl Into<Rot>,
1112    ) -> [Self; 2] {
1113        let rot1 = rot1.into();
1114        let rot2 = rot2.into();
1115
1116        let [local_basis1, local_basis2] = match [rotation1, rotation2] {
1117            [JointBasis::Local(basis1), JointBasis::Local(basis2)] => [basis1, basis2],
1118            [
1119                JointBasis::FromGlobal(basis1),
1120                JointBasis::FromGlobal(basis2),
1121            ] => [basis1 * rot1.inverse(), basis2 * rot2.inverse()],
1122            [JointBasis::Local(basis1), JointBasis::FromGlobal(basis2)] => {
1123                [basis1, basis2 * rot2.inverse()]
1124            }
1125            [JointBasis::FromGlobal(basis1), JointBasis::Local(basis2)] => {
1126                [basis1 * rot1.inverse(), basis2]
1127            }
1128        };
1129
1130        [
1131            JointBasis::Local(local_basis1),
1132            JointBasis::Local(local_basis2),
1133        ]
1134    }
1135}
1136
1137impl From<JointBasis> for JointFrame {
1138    fn from(basis: JointBasis) -> Self {
1139        Self {
1140            anchor: JointAnchor::ZERO,
1141            basis,
1142        }
1143    }
1144}