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