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