From 20261be78cf7f4ca9dcbd3123355ae36234b6304 Mon Sep 17 00:00:00 2001 From: st170001 Date: Sat, 14 Dec 2024 21:02:27 +0100 Subject: [PATCH 1/3] Additional groove joint 2D implementation with 1 translation + 1 rotation --- src/dynamics/joint/generic_joint.rs | 2 + src/dynamics/joint/groove_joint.rs | 272 ++++++++++++++++++++++++++++ src/dynamics/joint/mod.rs | 2 + 3 files changed, 276 insertions(+) create mode 100644 src/dynamics/joint/groove_joint.rs diff --git a/src/dynamics/joint/generic_joint.rs b/src/dynamics/joint/generic_joint.rs index 8d4066d35..23f5f2727 100644 --- a/src/dynamics/joint/generic_joint.rs +++ b/src/dynamics/joint/generic_joint.rs @@ -65,6 +65,8 @@ bitflags::bitflags! { const LOCKED_REVOLUTE_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits(); /// The set of degrees of freedom locked by a prismatic joint. const LOCKED_PRISMATIC_AXES = Self::LIN_Y.bits() | Self::ANG_X.bits(); + /// The set of degrees of freedom locked by a groove joint. + const LOCKED_GROOVE_AXES = Self::LIN_Y.bits(); /// The set of degrees of freedom locked by a fixed joint. const LOCKED_FIXED_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits() | Self::ANG_X.bits(); /// The set of degrees of freedom left free by a revolute joint. diff --git a/src/dynamics/joint/groove_joint.rs b/src/dynamics/joint/groove_joint.rs new file mode 100644 index 000000000..fa84b0815 --- /dev/null +++ b/src/dynamics/joint/groove_joint.rs @@ -0,0 +1,272 @@ +use crate::dynamics::joint::{GenericJoint, GenericJointBuilder, JointAxesMask}; +use crate::dynamics::{JointAxis, MotorModel}; +use crate::math::{Point, Real, UnitVector}; + +use super::{JointLimits, JointMotor}; + +#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +#[derive(Copy, Clone, Debug, PartialEq)] +#[repr(transparent)] +/// A groove joint, locks all relative motion between two bodies except for translation along the joint’s principal axis and relative rotations. +pub struct GrooveJoint { + /// The underlying joint data. + pub data: GenericJoint, +} + +impl GrooveJoint { + /// Creates a new groove joint allowing only relative translations along the specified axis and relative rotations. + /// + /// This axis is expressed in the local-space of both rigid-bodies. + #[cfg(feature = "dim2")] + pub fn new(axis: UnitVector) -> Self { + let data = GenericJointBuilder::new(JointAxesMask::LOCKED_GROOVE_AXES) + .local_axis1(axis) + .local_axis2(axis) + .build(); + Self { data } + } + + /// The underlying generic joint. + pub fn data(&self) -> &GenericJoint { + &self.data + } + + /// Are contacts between the attached rigid-bodies enabled? + pub fn contacts_enabled(&self) -> bool { + self.data.contacts_enabled + } + + /// Sets whether contacts between the attached rigid-bodies are enabled. + pub fn set_contacts_enabled(&mut self, enabled: bool) -> &mut Self { + self.data.set_contacts_enabled(enabled); + self + } + + /// The joint’s anchor, expressed in the local-space of the first rigid-body. + #[must_use] + pub fn local_anchor1(&self) -> Point { + self.data.local_anchor1() + } + + /// Sets the joint’s anchor, expressed in the local-space of the first rigid-body. + pub fn set_local_anchor1(&mut self, anchor1: Point) -> &mut Self { + self.data.set_local_anchor1(anchor1); + self + } + + /// The joint’s anchor, expressed in the local-space of the second rigid-body. + #[must_use] + pub fn local_anchor2(&self) -> Point { + self.data.local_anchor2() + } + + /// Sets the joint’s anchor, expressed in the local-space of the second rigid-body. + pub fn set_local_anchor2(&mut self, anchor2: Point) -> &mut Self { + self.data.set_local_anchor2(anchor2); + self + } + + /// The principal axis of the joint, expressed in the local-space of the first rigid-body. + #[must_use] + pub fn local_axis1(&self) -> UnitVector { + self.data.local_axis1() + } + + /// Sets the principal axis of the joint, expressed in the local-space of the first rigid-body. + pub fn set_local_axis1(&mut self, axis1: UnitVector) -> &mut Self { + self.data.set_local_axis1(axis1); + self + } + + /// The principal axis of the joint, expressed in the local-space of the second rigid-body. + #[must_use] + pub fn local_axis2(&self) -> UnitVector { + self.data.local_axis2() + } + + /// Sets the principal axis of the joint, expressed in the local-space of the second rigid-body. + pub fn set_local_axis2(&mut self, axis2: UnitVector) -> &mut Self { + self.data.set_local_axis2(axis2); + self + } + + /// The motor affecting the joint’s translational degree of freedom. + #[must_use] + pub fn motor(&self) -> Option<&JointMotor> { + self.data.motor(JointAxis::LinX) + } + + /// Set the spring-like model used by the motor to reach the desired target velocity and position. + pub fn set_motor_model(&mut self, model: MotorModel) -> &mut Self { + self.data.set_motor_model(JointAxis::LinX, model); + self + } + + /// Sets the target velocity this motor needs to reach. + pub fn set_motor_velocity(&mut self, target_vel: Real, factor: Real) -> &mut Self { + self.data + .set_motor_velocity(JointAxis::LinX, target_vel, factor); + self + } + + /// Sets the target angle this motor needs to reach. + pub fn set_motor_position( + &mut self, + target_pos: Real, + stiffness: Real, + damping: Real, + ) -> &mut Self { + self.data + .set_motor_position(JointAxis::LinX, target_pos, stiffness, damping); + self + } + + /// Configure both the target angle and target velocity of the motor. + pub fn set_motor( + &mut self, + target_pos: Real, + target_vel: Real, + stiffness: Real, + damping: Real, + ) -> &mut Self { + self.data + .set_motor(JointAxis::LinX, target_pos, target_vel, stiffness, damping); + self + } + + /// Sets the maximum force the motor can deliver. + pub fn set_motor_max_force(&mut self, max_force: Real) -> &mut Self { + self.data.set_motor_max_force(JointAxis::LinX, max_force); + self + } + + /// The limit distance attached bodies can translate along the joint’s principal axis. + #[must_use] + pub fn limits(&self) -> Option<&JointLimits> { + self.data.limits(JointAxis::LinX) + } + + /// Sets the `[min,max]` limit distances attached bodies can translate along the joint’s principal axis. + pub fn set_limits(&mut self, limits: [Real; 2]) -> &mut Self { + self.data.set_limits(JointAxis::LinX, limits); + self + } +} + +impl From for GenericJoint { + fn from(val: GrooveJoint) -> GenericJoint { + val.data + } +} + +/// Create groove joints using the builder pattern. +/// +/// A groove joint locks all relative motion except for translations along the joint’s principal axis and relative rotations. +#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct GrooveJointBuilder(pub GrooveJoint); + +impl GrooveJointBuilder { + /// Creates a new builder for groove joints. + /// + /// This axis is expressed in the local-space of both rigid-bodies. + #[cfg(feature = "dim2")] + pub fn new(axis: UnitVector) -> Self { + Self(GrooveJoint::new(axis)) + } + + /// Sets whether contacts between the attached rigid-bodies are enabled. + #[must_use] + pub fn contacts_enabled(mut self, enabled: bool) -> Self { + self.0.set_contacts_enabled(enabled); + self + } + + /// Sets the joint’s anchor, expressed in the local-space of the first rigid-body. + #[must_use] + pub fn local_anchor1(mut self, anchor1: Point) -> Self { + self.0.set_local_anchor1(anchor1); + self + } + + /// Sets the joint’s anchor, expressed in the local-space of the second rigid-body. + #[must_use] + pub fn local_anchor2(mut self, anchor2: Point) -> Self { + self.0.set_local_anchor2(anchor2); + self + } + + /// Sets the principal axis of the joint, expressed in the local-space of the first rigid-body. + #[must_use] + pub fn local_axis1(mut self, axis1: UnitVector) -> Self { + self.0.set_local_axis1(axis1); + self + } + + /// Sets the principal axis of the joint, expressed in the local-space of the second rigid-body. + #[must_use] + pub fn local_axis2(mut self, axis2: UnitVector) -> Self { + self.0.set_local_axis2(axis2); + self + } + + /// Set the spring-like model used by the motor to reach the desired target velocity and position. + #[must_use] + pub fn motor_model(mut self, model: MotorModel) -> Self { + self.0.set_motor_model(model); + self + } + + /// Sets the target velocity this motor needs to reach. + #[must_use] + pub fn motor_velocity(mut self, target_vel: Real, factor: Real) -> Self { + self.0.set_motor_velocity(target_vel, factor); + self + } + + /// Sets the target angle this motor needs to reach. + #[must_use] + pub fn motor_position(mut self, target_pos: Real, stiffness: Real, damping: Real) -> Self { + self.0.set_motor_position(target_pos, stiffness, damping); + self + } + + /// Configure both the target angle and target velocity of the motor. + #[must_use] + pub fn set_motor( + mut self, + target_pos: Real, + target_vel: Real, + stiffness: Real, + damping: Real, + ) -> Self { + self.0.set_motor(target_pos, target_vel, stiffness, damping); + self + } + + /// Sets the maximum force the motor can deliver. + #[must_use] + pub fn motor_max_force(mut self, max_force: Real) -> Self { + self.0.set_motor_max_force(max_force); + self + } + + /// Sets the `[min,max]` limit distances attached bodies can translate along the joint’s principal axis. + #[must_use] + pub fn limits(mut self, limits: [Real; 2]) -> Self { + self.0.set_limits(limits); + self + } + + /// Builds the groove joint. + #[must_use] + pub fn build(self) -> GrooveJoint { + self.0 + } +} + +impl From for GenericJoint { + fn from(val: GrooveJointBuilder) -> GenericJoint { + val.0.into() + } +} diff --git a/src/dynamics/joint/mod.rs b/src/dynamics/joint/mod.rs index 423d4c285..af398120a 100644 --- a/src/dynamics/joint/mod.rs +++ b/src/dynamics/joint/mod.rs @@ -1,5 +1,6 @@ pub use self::fixed_joint::*; pub use self::generic_joint::*; +pub use self::groove_joint::*; pub use self::impulse_joint::*; pub use self::motor_model::MotorModel; pub use self::multibody_joint::*; @@ -13,6 +14,7 @@ pub use self::spherical_joint::*; mod fixed_joint; mod generic_joint; +mod groove_joint; mod impulse_joint; mod motor_model; mod multibody_joint; From c4ffc423b44dca1897c0d7433d84f0ae02f8c399 Mon Sep 17 00:00:00 2001 From: st170001 Date: Sat, 14 Dec 2024 21:35:28 +0100 Subject: [PATCH 2/3] Conditional import for 2d feature flag --- src/dynamics/joint/groove_joint.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/dynamics/joint/groove_joint.rs b/src/dynamics/joint/groove_joint.rs index fa84b0815..0b7c60477 100644 --- a/src/dynamics/joint/groove_joint.rs +++ b/src/dynamics/joint/groove_joint.rs @@ -1,4 +1,7 @@ -use crate::dynamics::joint::{GenericJoint, GenericJointBuilder, JointAxesMask}; +#[cfg(feature = "dim2")] +use crate::dynamics::joint::{GenericJointBuilder, JointAxesMask}; + +use crate::dynamics::joint::GenericJoint; use crate::dynamics::{JointAxis, MotorModel}; use crate::math::{Point, Real, UnitVector}; From fe7284f77918dff1ff08cec7b1f4bd75422370fc Mon Sep 17 00:00:00 2001 From: st170001 Date: Tue, 31 Dec 2024 13:00:26 +0100 Subject: [PATCH 3/3] Groove joint 2d tests in testbed examples --- examples2d/all_examples2.rs | 2 + examples2d/groove_joint2.rs | 79 +++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 examples2d/groove_joint2.rs diff --git a/examples2d/all_examples2.rs b/examples2d/all_examples2.rs index 3d065a5c7..9dc8ec01d 100644 --- a/examples2d/all_examples2.rs +++ b/examples2d/all_examples2.rs @@ -19,6 +19,7 @@ mod debug_compression2; mod debug_total_overlap2; mod debug_vertical_column2; mod drum2; +mod groove_joint2; mod heightfield2; mod inverse_kinematics2; mod joint_motor_position2; @@ -91,6 +92,7 @@ pub fn main() { ("One-way platforms", one_way_platforms2::init_world), ("Platform", platform2::init_world), ("Polyline", polyline2::init_world), + ("Groove Joint", groove_joint2::init_world), ("Pyramid", pyramid2::init_world), ("Restitution", restitution2::init_world), ("Rope Joints", rope_joints2::init_world), diff --git a/examples2d/groove_joint2.rs b/examples2d/groove_joint2.rs new file mode 100644 index 000000000..789411572 --- /dev/null +++ b/examples2d/groove_joint2.rs @@ -0,0 +1,79 @@ +use rapier2d::prelude::*; +use rapier_testbed2d::Testbed; + +pub fn init_world(testbed: &mut Testbed) { + /* + * World + */ + let mut bodies = RigidBodySet::new(); + let mut colliders = ColliderSet::new(); + let mut impulse_joints = ImpulseJointSet::new(); + let multibody_joints = MultibodyJointSet::new(); + + /* + * Ground + */ + let ground_size = 3.0; + let ground_height = 0.1; + + let rigid_body_floor = RigidBodyBuilder::fixed().translation(vector![0.0, -ground_height]); + let floor_handle = bodies.insert(rigid_body_floor); + let floor_collider = ColliderBuilder::cuboid(ground_size, ground_height); + colliders.insert_with_parent(floor_collider, floor_handle, &mut bodies); + + /* + * Character we will control manually. + */ + let rigid_body_character = + RigidBodyBuilder::kinematic_position_based().translation(vector![0.0, 0.3]); + let character_handle = bodies.insert(rigid_body_character); + let character_collider = ColliderBuilder::cuboid(0.15, 0.3); + colliders.insert_with_parent(character_collider, character_handle, &mut bodies); + + /* + * Tethered cube. + */ + let rad = 0.4; + + let rigid_body_cube = + RigidBodyBuilder::new(RigidBodyType::Dynamic).translation(vector![1.0, 1.0]); + let cube_handle = bodies.insert(rigid_body_cube); + let cube_collider = ColliderBuilder::cuboid(rad, rad); + colliders.insert_with_parent(cube_collider, cube_handle, &mut bodies); + + /* + * Rotation axis indicator ball. + */ + let rigid_body_ball = + RigidBodyBuilder::new(RigidBodyType::Dynamic).translation(vector![1.0, 1.0]); + let ball_handle = bodies.insert(rigid_body_ball); + let ball_collider = ColliderBuilder::ball(0.1); + colliders.insert_with_parent(ball_collider, ball_handle, &mut bodies); + + /* + * Fixed joint between rotation axis indicator and cube. + */ + let fixed_joint = FixedJointBuilder::new() + .local_anchor1(point![0.0, 0.0]) + .local_anchor2(point![0.0, -0.4]) + .build(); + impulse_joints.insert(cube_handle, ball_handle, fixed_joint, true); + + /* + * Groove joint between cube and ground. + */ + let axis = UnitVector::new_normalize(vector![1.0, 1.0]); + let groove_joint = GrooveJointBuilder::new(axis) + .local_anchor1(point![2.0, 2.0]) + .local_anchor2(point![0.0, 0.4]) + .limits([-1.0, f32::INFINITY]) // Set the limits for the groove joint + .build(); + impulse_joints.insert(character_handle, cube_handle, groove_joint, true); + + /* + * Set up the testbed. + */ + testbed.set_world(bodies, colliders, impulse_joints, multibody_joints); + testbed.set_character_body(character_handle); + testbed.look_at(point![0.0, 1.0], 100.0); +}