#![allow(clippy::bad_bit_mask)] #![allow(clippy::unnecessary_cast)]
use crate::dynamics::integration_parameters::SpringCoefficients;
use crate::dynamics::solver::MotorParameters;
use crate::dynamics::{
FixedJoint, MotorModel, PrismaticJoint, RevoluteJoint, RigidBody, RopeJoint,
};
use crate::math::{Pose, Real, Rotation, SPATIAL_DIM, Vector};
use crate::utils::{OrthonormalBasis, SimdRealCopy};
use parry::math::Matrix;
#[cfg(feature = "dim3")]
use crate::dynamics::SphericalJoint;
#[cfg(feature = "dim3")]
bitflags::bitflags! {
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct JointAxesMask: u8 {
const LIN_X = 1 << 0;
const LIN_Y = 1 << 1;
const LIN_Z = 1 << 2;
const ANG_X = 1 << 3;
const ANG_Y = 1 << 4;
const ANG_Z = 1 << 5;
const LOCKED_REVOLUTE_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits() | Self::LIN_Z.bits() | Self::ANG_Y.bits() | Self::ANG_Z.bits();
const LOCKED_PRISMATIC_AXES = Self::LIN_Y.bits() | Self::LIN_Z.bits() | Self::ANG_X.bits() | Self::ANG_Y.bits() | Self::ANG_Z.bits();
const LOCKED_FIXED_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits() | Self::LIN_Z.bits() | Self::ANG_X.bits() | Self::ANG_Y.bits() | Self::ANG_Z.bits();
const LOCKED_SPHERICAL_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits() | Self::LIN_Z.bits();
const FREE_REVOLUTE_AXES = Self::ANG_X.bits();
const FREE_PRISMATIC_AXES = Self::LIN_X.bits();
const FREE_FIXED_AXES = 0;
const FREE_SPHERICAL_AXES = Self::ANG_X.bits() | Self::ANG_Y.bits() | Self::ANG_Z.bits();
const LIN_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits() | Self::LIN_Z.bits();
const ANG_AXES = Self::ANG_X.bits() | Self::ANG_Y.bits() | Self::ANG_Z.bits();
}
}
#[cfg(feature = "dim2")]
bitflags::bitflags! {
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct JointAxesMask: u8 {
const LIN_X = 1 << 0;
const LIN_Y = 1 << 1;
const ANG_X = 1 << 2;
const LOCKED_REVOLUTE_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits();
const LOCKED_PRISMATIC_AXES = Self::LIN_Y.bits() | Self::ANG_X.bits();
const LOCKED_PIN_SLOT_AXES = Self::LIN_Y.bits();
const LOCKED_FIXED_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits() | Self::ANG_X.bits();
const FREE_REVOLUTE_AXES = Self::ANG_X.bits();
const FREE_PRISMATIC_AXES = Self::LIN_X.bits();
const FREE_FIXED_AXES = 0;
const LIN_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits();
const ANG_AXES = Self::ANG_X.bits();
}
}
impl Default for JointAxesMask {
fn default() -> Self {
Self::empty()
}
}
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum JointAxis {
LinX = 0,
LinY,
#[cfg(feature = "dim3")]
LinZ,
AngX,
#[cfg(feature = "dim3")]
AngY,
#[cfg(feature = "dim3")]
AngZ,
}
impl From<JointAxis> for JointAxesMask {
fn from(axis: JointAxis) -> Self {
JointAxesMask::from_bits(1 << axis as usize).unwrap()
}
}
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct JointLimits<N> {
pub min: N,
pub max: N,
pub impulse: N,
}
impl<N: SimdRealCopy> Default for JointLimits<N> {
fn default() -> Self {
Self {
min: -N::splat(Real::MAX),
max: N::splat(Real::MAX),
impulse: N::splat(0.0),
}
}
}
impl<N: SimdRealCopy> From<[N; 2]> for JointLimits<N> {
fn from(value: [N; 2]) -> Self {
Self {
min: value[0],
max: value[1],
impulse: N::splat(0.0),
}
}
}
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct JointMotor {
pub target_vel: Real,
pub target_pos: Real,
pub stiffness: Real,
pub damping: Real,
pub max_force: Real,
pub impulse: Real,
pub model: MotorModel,
}
impl Default for JointMotor {
fn default() -> Self {
Self {
target_pos: 0.0,
target_vel: 0.0,
stiffness: 0.0,
damping: 0.0,
max_force: Real::MAX,
impulse: 0.0,
model: MotorModel::AccelerationBased,
}
}
}
impl JointMotor {
pub(crate) fn motor_params(&self, dt: Real) -> MotorParameters<Real> {
let (erp_inv_dt, cfm_coeff, cfm_gain) =
self.model
.combine_coefficients(dt, self.stiffness, self.damping);
MotorParameters {
erp_inv_dt,
cfm_coeff,
cfm_gain,
target_pos: self.target_pos,
target_vel: self.target_vel,
max_impulse: self.max_force * dt,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub enum JointEnabled {
Enabled,
DisabledByAttachedBody,
Disabled,
}
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct GenericJoint {
pub local_frame1: Pose,
pub local_frame2: Pose,
pub locked_axes: JointAxesMask,
pub limit_axes: JointAxesMask,
pub motor_axes: JointAxesMask,
pub coupled_axes: JointAxesMask,
pub limits: [JointLimits<Real>; SPATIAL_DIM],
pub motors: [JointMotor; SPATIAL_DIM],
pub softness: SpringCoefficients<Real>,
pub contacts_enabled: bool,
pub enabled: JointEnabled,
pub user_data: u128,
}
impl Default for GenericJoint {
fn default() -> Self {
Self {
local_frame1: Pose::IDENTITY,
local_frame2: Pose::IDENTITY,
locked_axes: JointAxesMask::empty(),
limit_axes: JointAxesMask::empty(),
motor_axes: JointAxesMask::empty(),
coupled_axes: JointAxesMask::empty(),
limits: [JointLimits::default(); SPATIAL_DIM],
motors: [JointMotor::default(); SPATIAL_DIM],
softness: SpringCoefficients::joint_defaults(),
contacts_enabled: true,
enabled: JointEnabled::Enabled,
user_data: 0,
}
}
}
impl GenericJoint {
#[must_use]
pub fn new(locked_axes: JointAxesMask) -> Self {
*Self::default().lock_axes(locked_axes)
}
#[cfg(feature = "simd-is-enabled")]
pub(crate) fn supports_simd_constraints(&self) -> bool {
self.limit_axes.is_empty() && self.motor_axes.is_empty()
}
#[doc(hidden)]
pub fn complete_ang_frame(axis: Vector) -> Rotation {
let basis = axis.orthonormal_basis();
#[cfg(feature = "dim2")]
{
let mat = Matrix::from_cols(axis, basis[0]);
Rotation::from_matrix_unchecked(mat)
}
#[cfg(feature = "dim3")]
{
let mat = Matrix::from_cols(axis, basis[0], basis[1]);
Rotation::from_mat3(&mat)
}
}
pub fn is_enabled(&self) -> bool {
self.enabled == JointEnabled::Enabled
}
pub fn set_enabled(&mut self, enabled: bool) {
match self.enabled {
JointEnabled::Enabled | JointEnabled::DisabledByAttachedBody => {
if !enabled {
self.enabled = JointEnabled::Disabled;
}
}
JointEnabled::Disabled => {
if enabled {
self.enabled = JointEnabled::Enabled;
}
}
}
}
pub fn lock_axes(&mut self, axes: JointAxesMask) -> &mut Self {
self.locked_axes |= axes;
self
}
pub fn set_local_frame1(&mut self, local_frame: Pose) -> &mut Self {
self.local_frame1 = local_frame;
self
}
pub fn set_local_frame2(&mut self, local_frame: Pose) -> &mut Self {
self.local_frame2 = local_frame;
self
}
#[must_use]
pub fn local_axis1(&self) -> Vector {
self.local_frame1 * Vector::X
}
pub fn set_local_axis1(&mut self, local_axis: Vector) -> &mut Self {
self.local_frame1.rotation = Self::complete_ang_frame(local_axis);
self
}
#[must_use]
pub fn local_axis2(&self) -> Vector {
self.local_frame2 * Vector::X
}
pub fn set_local_axis2(&mut self, local_axis: Vector) -> &mut Self {
self.local_frame2.rotation = Self::complete_ang_frame(local_axis);
self
}
#[must_use]
pub fn local_anchor1(&self) -> Vector {
self.local_frame1.translation
}
pub fn set_local_anchor1(&mut self, anchor1: Vector) -> &mut Self {
self.local_frame1.translation = anchor1;
self
}
#[must_use]
pub fn local_anchor2(&self) -> Vector {
self.local_frame2.translation
}
pub fn set_local_anchor2(&mut self, anchor2: Vector) -> &mut Self {
self.local_frame2.translation = anchor2;
self
}
pub fn contacts_enabled(&self) -> bool {
self.contacts_enabled
}
pub fn set_contacts_enabled(&mut self, enabled: bool) -> &mut Self {
self.contacts_enabled = enabled;
self
}
#[must_use]
pub fn set_softness(&mut self, softness: SpringCoefficients<Real>) -> &mut Self {
self.softness = softness;
self
}
#[must_use]
pub fn limits(&self, axis: JointAxis) -> Option<&JointLimits<Real>> {
let i = axis as usize;
if self.limit_axes.contains(axis.into()) {
Some(&self.limits[i])
} else {
None
}
}
pub fn set_limits(&mut self, axis: JointAxis, limits: [Real; 2]) -> &mut Self {
let i = axis as usize;
self.limit_axes |= axis.into();
self.limits[i].min = limits[0];
self.limits[i].max = limits[1];
self
}
#[must_use]
pub fn motor_model(&self, axis: JointAxis) -> Option<MotorModel> {
let i = axis as usize;
if self.motor_axes.contains(axis.into()) {
Some(self.motors[i].model)
} else {
None
}
}
pub fn set_motor_model(&mut self, axis: JointAxis, model: MotorModel) -> &mut Self {
self.motors[axis as usize].model = model;
self
}
pub fn set_motor_velocity(
&mut self,
axis: JointAxis,
target_vel: Real,
factor: Real,
) -> &mut Self {
self.set_motor(
axis,
self.motors[axis as usize].target_pos,
target_vel,
0.0,
factor,
)
}
pub fn set_motor_position(
&mut self,
axis: JointAxis,
target_pos: Real,
stiffness: Real,
damping: Real,
) -> &mut Self {
self.set_motor(axis, target_pos, 0.0, stiffness, damping)
}
pub fn set_motor_max_force(&mut self, axis: JointAxis, max_force: Real) -> &mut Self {
self.motors[axis as usize].max_force = max_force;
self
}
#[must_use]
pub fn motor(&self, axis: JointAxis) -> Option<&JointMotor> {
let i = axis as usize;
if self.motor_axes.contains(axis.into()) {
Some(&self.motors[i])
} else {
None
}
}
pub fn set_motor(
&mut self,
axis: JointAxis,
target_pos: Real,
target_vel: Real,
stiffness: Real,
damping: Real,
) -> &mut Self {
self.motor_axes |= axis.into();
let i = axis as usize;
self.motors[i].target_vel = target_vel;
self.motors[i].target_pos = target_pos;
self.motors[i].stiffness = stiffness;
self.motors[i].damping = damping;
self
}
pub fn flip(&mut self) {
std::mem::swap(&mut self.local_frame1, &mut self.local_frame2);
let coupled_bits = self.coupled_axes.bits();
for dim in 0..SPATIAL_DIM {
if coupled_bits & (1 << dim) == 0 {
let limit = self.limits[dim];
self.limits[dim].min = -limit.max;
self.limits[dim].max = -limit.min;
}
self.motors[dim].target_vel = -self.motors[dim].target_vel;
self.motors[dim].target_pos = -self.motors[dim].target_pos;
}
}
pub(crate) fn transform_to_solver_body_space(&mut self, rb1: &RigidBody, rb2: &RigidBody) {
if rb1.is_fixed() {
self.local_frame1 = rb1.pos.position * self.local_frame1;
} else {
self.local_frame1.translation -= rb1.mprops.local_mprops.local_com;
}
if rb2.is_fixed() {
self.local_frame2 = rb2.pos.position * self.local_frame2;
} else {
self.local_frame2.translation -= rb2.mprops.local_mprops.local_com;
}
}
}
macro_rules! joint_conversion_methods(
($as_joint: ident, $as_joint_mut: ident, $Joint: ty, $axes: expr) => {
#[must_use]
pub fn $as_joint(&self) -> Option<&$Joint> {
if self.locked_axes == $axes {
Some(unsafe { std::mem::transmute::<&Self, &$Joint>(self) })
} else {
None
}
}
#[must_use]
pub fn $as_joint_mut(&mut self) -> Option<&mut $Joint> {
if self.locked_axes == $axes {
Some(unsafe { std::mem::transmute::<&mut Self, &mut $Joint>(self) })
} else {
None
}
}
}
);
impl GenericJoint {
joint_conversion_methods!(
as_revolute,
as_revolute_mut,
RevoluteJoint,
JointAxesMask::LOCKED_REVOLUTE_AXES
);
joint_conversion_methods!(
as_fixed,
as_fixed_mut,
FixedJoint,
JointAxesMask::LOCKED_FIXED_AXES
);
joint_conversion_methods!(
as_prismatic,
as_prismatic_mut,
PrismaticJoint,
JointAxesMask::LOCKED_PRISMATIC_AXES
);
joint_conversion_methods!(
as_rope,
as_rope_mut,
RopeJoint,
JointAxesMask::FREE_FIXED_AXES
);
#[cfg(feature = "dim3")]
joint_conversion_methods!(
as_spherical,
as_spherical_mut,
SphericalJoint,
JointAxesMask::LOCKED_SPHERICAL_AXES
);
}
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct GenericJointBuilder(pub GenericJoint);
impl GenericJointBuilder {
#[must_use]
pub fn new(locked_axes: JointAxesMask) -> Self {
Self(GenericJoint::new(locked_axes))
}
#[must_use]
pub fn locked_axes(mut self, axes: JointAxesMask) -> Self {
self.0.locked_axes = axes;
self
}
#[must_use]
pub fn contacts_enabled(mut self, enabled: bool) -> Self {
self.0.contacts_enabled = enabled;
self
}
#[must_use]
pub fn local_frame1(mut self, local_frame: Pose) -> Self {
self.0.set_local_frame1(local_frame);
self
}
#[must_use]
pub fn local_frame2(mut self, local_frame: Pose) -> Self {
self.0.set_local_frame2(local_frame);
self
}
#[must_use]
pub fn local_axis1(mut self, local_axis: Vector) -> Self {
self.0.set_local_axis1(local_axis);
self
}
#[must_use]
pub fn local_axis2(mut self, local_axis: Vector) -> Self {
self.0.set_local_axis2(local_axis);
self
}
#[must_use]
pub fn local_anchor1(mut self, anchor1: Vector) -> Self {
self.0.set_local_anchor1(anchor1);
self
}
#[must_use]
pub fn local_anchor2(mut self, anchor2: Vector) -> Self {
self.0.set_local_anchor2(anchor2);
self
}
#[must_use]
pub fn limits(mut self, axis: JointAxis, limits: [Real; 2]) -> Self {
self.0.set_limits(axis, limits);
self
}
#[must_use]
pub fn coupled_axes(mut self, axes: JointAxesMask) -> Self {
self.0.coupled_axes = axes;
self
}
#[must_use]
pub fn motor_model(mut self, axis: JointAxis, model: MotorModel) -> Self {
self.0.set_motor_model(axis, model);
self
}
#[must_use]
pub fn motor_velocity(mut self, axis: JointAxis, target_vel: Real, factor: Real) -> Self {
self.0.set_motor_velocity(axis, target_vel, factor);
self
}
#[must_use]
pub fn motor_position(
mut self,
axis: JointAxis,
target_pos: Real,
stiffness: Real,
damping: Real,
) -> Self {
self.0
.set_motor_position(axis, target_pos, stiffness, damping);
self
}
#[must_use]
pub fn set_motor(
mut self,
axis: JointAxis,
target_pos: Real,
target_vel: Real,
stiffness: Real,
damping: Real,
) -> Self {
self.0
.set_motor(axis, target_pos, target_vel, stiffness, damping);
self
}
#[must_use]
pub fn motor_max_force(mut self, axis: JointAxis, max_force: Real) -> Self {
self.0.set_motor_max_force(axis, max_force);
self
}
#[must_use]
pub fn softness(mut self, softness: SpringCoefficients<Real>) -> Self {
self.0.softness = softness;
self
}
pub fn user_data(mut self, data: u128) -> Self {
self.0.user_data = data;
self
}
#[must_use]
pub fn build(self) -> GenericJoint {
self.0
}
}
impl From<GenericJointBuilder> for GenericJoint {
fn from(val: GenericJointBuilder) -> GenericJoint {
val.0
}
}