diff --git a/crates/avian2d/examples/distance_joint_2d.rs b/crates/avian2d/examples/distance_joint_2d.rs index 77aa85d0..f2bde85b 100644 --- a/crates/avian2d/examples/distance_joint_2d.rs +++ b/crates/avian2d/examples/distance_joint_2d.rs @@ -51,7 +51,7 @@ fn setup(mut commands: Commands) { DistanceJoint::new(anchor, object) .with_local_anchor_1(Vector::ZERO) .with_local_anchor_2(Vector::ZERO) - .with_rest_length(100.0) + .with_length_limits(100.0) .with_linear_velocity_damping(0.1) .with_angular_velocity_damping(1.0) .with_compliance(0.00000001), diff --git a/crates/avian3d/examples/distance_joint_3d.rs b/crates/avian3d/examples/distance_joint_3d.rs index 8a617960..594654f2 100644 --- a/crates/avian3d/examples/distance_joint_3d.rs +++ b/crates/avian3d/examples/distance_joint_3d.rs @@ -52,7 +52,7 @@ fn setup( commands.spawn( DistanceJoint::new(static_cube, dynamic_cube) .with_local_anchor_2(0.5 * Vector::ONE) - .with_rest_length(1.5) + .with_length_limits(1.5) .with_compliance(1.0 / 400.0), ); diff --git a/src/dynamics/solver/joints/distance.rs b/src/dynamics/solver/joints/distance.rs index ce5632ad..a85f7d2e 100644 --- a/src/dynamics/solver/joints/distance.rs +++ b/src/dynamics/solver/joints/distance.rs @@ -25,10 +25,8 @@ pub struct DistanceJoint { pub local_anchor1: Vector, /// Attachment point on the second body. pub local_anchor2: Vector, - /// The distance the attached bodies will be kept relative to each other. - pub rest_length: Scalar, /// The extents of the allowed relative translation between the attached bodies. - pub length_limits: Option, + pub length_limits: DistanceLimit, /// Linear damping applied by the joint. pub damping_linear: Scalar, /// Angular damping applied by the joint. @@ -62,8 +60,7 @@ impl Joint for DistanceJoint { entity2, local_anchor1: Vector::ZERO, local_anchor2: Vector::ZERO, - rest_length: 0.0, - length_limits: None, + length_limits: DistanceLimit::ZERO, damping_linear: 0.0, damping_angular: 0.0, lagrange: 0.0, @@ -132,9 +129,7 @@ impl DistanceJoint { // If min and max limits aren't specified, use rest length // TODO: Remove rest length, just use min/max limits. - let limits = self - .length_limits - .unwrap_or(DistanceLimit::new(self.rest_length, self.rest_length)); + let limits = self.length_limits; // Compute the direction and magnitude of the positional correction required // to keep the bodies within a certain distance from each other. @@ -172,18 +167,28 @@ impl DistanceJoint { self.compute_force(self.lagrange, dir, dt) } - /// Sets the minimum and maximum distances between the attached bodies. - pub fn with_limits(self, min: Scalar, max: Scalar) -> Self { - Self { - length_limits: Some(DistanceLimit::new(min, max)), - ..self - } - } - - /// Sets the joint's rest length, or distance the bodies will be kept at. - pub fn with_rest_length(self, rest_length: Scalar) -> Self { + /// Returns self with the minimum and maximum distances between the attached + /// bodies. + /// + /// ``` + /// # #[cfg(feature = "2d")] + /// # use avian2d::prelude::*; + /// # #[cfg(feature = "3d")] + /// # use avian3d::prelude::*; + /// # use bevy::prelude::*; + /// # fn new_joint() -> DistanceJoint { DistanceJoint::new(Entity::PLACEHOLDER, Entity::PLACEHOLDER) } + /// let j: DistanceJoint = new_joint(); + /// let a = j.with_length_limits(DistanceLimit { min: 0.0, max: 1.0 }); + /// let b = j.with_length_limits((0.0, 1.0)); + /// assert_eq!(a, b); + /// + /// let c = j.with_length_limits(DistanceLimit { min: 0.5, max: 0.5 }); + /// let d = j.with_length_limits(0.5); + /// assert_eq!(c, d); + /// ``` + pub fn with_length_limits(self, limit: impl Into) -> Self { Self { - rest_length, + length_limits: limit.into(), ..self } } diff --git a/src/dynamics/solver/joints/mod.rs b/src/dynamics/solver/joints/mod.rs index 6fa37350..5a0eacbd 100644 --- a/src/dynamics/solver/joints/mod.rs +++ b/src/dynamics/solver/joints/mod.rs @@ -194,6 +194,23 @@ pub struct DistanceLimit { pub max: Scalar, } +/// Convert the `limit` into a distance limit where _min = max = limit_. +impl From for DistanceLimit { + fn from(limit: Scalar) -> DistanceLimit { + DistanceLimit { + min: limit, + max: limit, + } + } +} + +/// Convert the `(min, max)` pair into a distance limit. +impl From<(Scalar, Scalar)> for DistanceLimit { + fn from((min, max): (Scalar, Scalar)) -> DistanceLimit { + DistanceLimit { min, max } + } +} + impl DistanceLimit { /// A `DistanceLimit` with `min` and `max` set to zero. pub const ZERO: Self = Self { min: 0.0, max: 0.0 }; diff --git a/src/spatial_query/system_param.rs b/src/spatial_query/system_param.rs index 2a9401ab..5d461f18 100644 --- a/src/spatial_query/system_param.rs +++ b/src/spatial_query/system_param.rs @@ -38,7 +38,7 @@ use bevy::{ecs::system::SystemParam, prelude::*}; /// Dir3::X, // Direction /// 100.0, // Maximum time of impact (travel distance) /// true, // Does the ray treat colliders as "solid" -/// SpatialQueryFilter::default(), // Query filter +/// &SpatialQueryFilter::default(), // Query filter /// ) { /// println!("First hit: {:?}", first_hit); /// } @@ -50,7 +50,7 @@ use bevy::{ecs::system::SystemParam, prelude::*}; /// 100.0, // Maximum time of impact (travel distance) /// 20, // Maximum number of hits /// true, // Does the ray treat colliders as "solid" -/// SpatialQueryFilter::default(), // Query filter +/// &SpatialQueryFilter::default(), // Query filter /// ); /// /// // Print hits @@ -115,7 +115,7 @@ impl<'w, 's> SpatialQuery<'w, 's> { /// Dir3::X, // Direction /// 100.0, // Maximum time of impact (travel distance) /// true, // Does the ray treat colliders as "solid" - /// SpatialQueryFilter::default(), // Query filter + /// &SpatialQueryFilter::default(), // Query filter /// ) { /// println!("First hit: {:?}", first_hit); /// } @@ -167,7 +167,7 @@ impl<'w, 's> SpatialQuery<'w, 's> { /// Dir3::X, // Direction /// 100.0, // Maximum time of impact (travel distance) /// true, // Does the ray treat colliders as "solid" - /// SpatialQueryFilter::default(), // Query filter + /// &SpatialQueryFilter::default(), // Query filter /// &|entity| { // Predicate /// // Skip entities with the `Invisible` component. /// !query.contains(entity) @@ -229,7 +229,7 @@ impl<'w, 's> SpatialQuery<'w, 's> { /// 100.0, // Maximum time of impact (travel distance) /// 20, // Maximum number of hits /// true, // Does the ray treat colliders as "solid" - /// SpatialQueryFilter::default(), // Query filter + /// &SpatialQueryFilter::default(), // Query filter /// ); /// /// // Print hits @@ -291,7 +291,7 @@ impl<'w, 's> SpatialQuery<'w, 's> { /// Dir3::X, // Direction /// 100.0, // Maximum time of impact (travel distance) /// true, // Does the ray treat colliders as "solid" - /// SpatialQueryFilter::default(), // Query filter + /// &SpatialQueryFilter::default(), // Query filter /// |hit| { // Callback function /// hits.push(hit); /// true @@ -359,7 +359,7 @@ impl<'w, 's> SpatialQuery<'w, 's> { /// Dir3::X, // Direction /// 100.0, // Maximum time of impact (travel distance) /// true, // Should initial penetration at the origin be ignored - /// SpatialQueryFilter::default(), // Query filter + /// &SpatialQueryFilter::default(), // Query filter /// ) { /// println!("First hit: {:?}", first_hit); /// } @@ -424,7 +424,7 @@ impl<'w, 's> SpatialQuery<'w, 's> { /// 100.0, // Maximum time of impact (travel distance) /// 20, // Max hits /// true, // Should initial penetration at the origin be ignored - /// SpatialQueryFilter::default(), // Query filter + /// &SpatialQueryFilter::default(), // Query filter /// ); /// /// // Print hits @@ -495,7 +495,7 @@ impl<'w, 's> SpatialQuery<'w, 's> { /// Dir3::X, // Direction /// 100.0, // Maximum time of impact (travel distance) /// true, // Should initial penetration at the origin be ignored - /// SpatialQueryFilter::default(), // Query filter + /// &SpatialQueryFilter::default(), // Query filter /// |hit| { // Callback function /// hits.push(hit); /// true @@ -557,7 +557,7 @@ impl<'w, 's> SpatialQuery<'w, 's> { /// if let Some(projection) = spatial_query.project_point( /// Vec3::ZERO, // Point /// true, // Are colliders treated as "solid" - /// SpatialQueryFilter::default(), // Query filter + /// &SpatialQueryFilter::default(), // Query filter /// ) { /// println!("Projection: {:?}", projection); /// } @@ -593,7 +593,7 @@ impl<'w, 's> SpatialQuery<'w, 's> { /// # #[cfg(all(feature = "3d", feature = "f32"))] /// fn print_point_intersections(spatial_query: SpatialQuery) { /// let intersections = - /// spatial_query.point_intersections(Vec3::ZERO, SpatialQueryFilter::default()); + /// spatial_query.point_intersections(Vec3::ZERO, &SpatialQueryFilter::default()); /// /// for entity in intersections.iter() { /// println!("Entity: {:?}", entity); @@ -633,7 +633,7 @@ impl<'w, 's> SpatialQuery<'w, 's> { /// /// spatial_query.point_intersections_callback( /// Vec3::ZERO, // Point - /// SpatialQueryFilter::default(), // Query filter + /// &SpatialQueryFilter::default(), // Query filter /// |entity| { // Callback function /// intersections.push(entity); /// true @@ -745,7 +745,7 @@ impl<'w, 's> SpatialQuery<'w, 's> { /// &Collider::sphere(0.5), // Shape /// Vec3::ZERO, // Shape position /// Quat::default(), // Shape rotation - /// SpatialQueryFilter::default(), // Query filter + /// &SpatialQueryFilter::default(), // Query filter /// ); /// /// for entity in intersections.iter() { @@ -793,7 +793,7 @@ impl<'w, 's> SpatialQuery<'w, 's> { /// &Collider::sphere(0.5), // Shape /// Vec3::ZERO, // Shape position /// Quat::default(), // Shape rotation - /// SpatialQueryFilter::default(), // Query filter + /// &SpatialQueryFilter::default(), // Query filter /// |entity| { // Callback function /// intersections.push(entity); /// true