1#![allow(clippy::bad_bit_mask)] #![allow(clippy::unnecessary_cast)] #[cfg(feature = "alloc")]
5use crate::dynamics::RigidBody;
6use crate::dynamics::integration_parameters::SpringCoefficients;
7#[cfg(feature = "alloc")]
8use crate::dynamics::solver::MotorParameters;
9use crate::dynamics::{FixedJoint, MotorModel, PrismaticJoint, RevoluteJoint, RopeJoint};
10use crate::math::{Pose, Real, Rotation, SPATIAL_DIM, Vector};
11#[cfg(feature = "dim2")]
12use crate::utils::OrthonormalBasis;
13use crate::utils::SimdRealCopy;
14#[cfg(feature = "dim2")]
15use parry::math::Matrix;
16
17#[cfg(feature = "dim3")]
18use crate::dynamics::SphericalJoint;
19
20#[cfg(feature = "dim3")]
21bitflags::bitflags! {
22 #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
24 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
25 pub struct JointAxesMask: u8 {
26 const LIN_X = 1 << 0;
28 const LIN_Y = 1 << 1;
30 const LIN_Z = 1 << 2;
32 const ANG_X = 1 << 3;
34 const ANG_Y = 1 << 4;
36 const ANG_Z = 1 << 5;
38 const LOCKED_REVOLUTE_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits() | Self::LIN_Z.bits() | Self::ANG_Y.bits() | Self::ANG_Z.bits();
40 const LOCKED_PRISMATIC_AXES = Self::LIN_Y.bits() | Self::LIN_Z.bits() | Self::ANG_X.bits() | Self::ANG_Y.bits() | Self::ANG_Z.bits();
42 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();
44 const LOCKED_SPHERICAL_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits() | Self::LIN_Z.bits();
46 const FREE_REVOLUTE_AXES = Self::ANG_X.bits();
48 const FREE_PRISMATIC_AXES = Self::LIN_X.bits();
50 const FREE_FIXED_AXES = 0;
52 const FREE_SPHERICAL_AXES = Self::ANG_X.bits() | Self::ANG_Y.bits() | Self::ANG_Z.bits();
54 const LIN_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits() | Self::LIN_Z.bits();
56 const ANG_AXES = Self::ANG_X.bits() | Self::ANG_Y.bits() | Self::ANG_Z.bits();
58 }
59}
60
61#[cfg(feature = "dim2")]
62bitflags::bitflags! {
63 #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
65 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
66 pub struct JointAxesMask: u8 {
67 const LIN_X = 1 << 0;
69 const LIN_Y = 1 << 1;
71 const ANG_X = 1 << 2;
73 const LOCKED_REVOLUTE_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits();
75 const LOCKED_PRISMATIC_AXES = Self::LIN_Y.bits() | Self::ANG_X.bits();
77 const LOCKED_PIN_SLOT_AXES = Self::LIN_Y.bits();
79 const LOCKED_FIXED_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits() | Self::ANG_X.bits();
81 const FREE_REVOLUTE_AXES = Self::ANG_X.bits();
83 const FREE_PRISMATIC_AXES = Self::LIN_X.bits();
85 const FREE_FIXED_AXES = 0;
87 const LIN_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits();
89 const ANG_AXES = Self::ANG_X.bits();
91 }
92}
93
94impl Default for JointAxesMask {
95 fn default() -> Self {
96 Self::empty()
97 }
98}
99
100#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
102#[derive(Copy, Clone, Debug, PartialEq)]
103pub enum JointAxis {
104 LinX = 0,
106 LinY,
108 #[cfg(feature = "dim3")]
110 LinZ,
111 AngX,
113 #[cfg(feature = "dim3")]
115 AngY,
116 #[cfg(feature = "dim3")]
118 AngZ,
119}
120
121impl From<JointAxis> for JointAxesMask {
122 fn from(axis: JointAxis) -> Self {
123 JointAxesMask::from_bits(1 << axis as usize).unwrap()
124 }
125}
126
127#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
141#[derive(Copy, Clone, Debug, PartialEq)]
142pub struct JointLimits<N> {
143 pub min: N,
145 pub max: N,
147 pub impulse: N,
149}
150
151impl<N: SimdRealCopy> Default for JointLimits<N> {
152 fn default() -> Self {
153 Self {
154 min: -N::splat(Real::MAX),
155 max: N::splat(Real::MAX),
156 impulse: N::splat(0.0),
157 }
158 }
159}
160
161impl<N: SimdRealCopy> From<[N; 2]> for JointLimits<N> {
162 fn from(value: [N; 2]) -> Self {
163 Self {
164 min: value[0],
165 max: value[1],
166 impulse: N::splat(0.0),
167 }
168 }
169}
170
171#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
202#[derive(Copy, Clone, Debug, PartialEq)]
203pub struct JointMotor {
204 pub target_vel: Real,
206 pub target_pos: Real,
208 pub stiffness: Real,
210 pub damping: Real,
212 pub max_force: Real,
214 pub impulse: Real,
216 pub model: MotorModel,
218}
219
220impl Default for JointMotor {
221 fn default() -> Self {
222 Self {
223 target_pos: 0.0,
224 target_vel: 0.0,
225 stiffness: 0.0,
226 damping: 0.0,
227 max_force: Real::MAX,
228 impulse: 0.0,
229 model: MotorModel::AccelerationBased,
230 }
231 }
232}
233
234#[cfg(feature = "alloc")]
235impl JointMotor {
236 pub(crate) fn motor_params(&self, dt: Real) -> MotorParameters<Real> {
237 let (erp_inv_dt, cfm_coeff, cfm_gain) =
238 self.model
239 .combine_coefficients(dt, self.stiffness, self.damping);
240 MotorParameters {
241 erp_inv_dt,
242 cfm_coeff,
243 cfm_gain,
244 target_pos: self.target_pos,
246 target_vel: self.target_vel,
247 max_impulse: self.max_force * dt,
248 }
249 }
250}
251
252#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
253#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
254pub enum JointEnabled {
256 Enabled,
258 DisabledByAttachedBody,
261 Disabled,
263}
264
265#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
266#[derive(Copy, Clone, Debug, PartialEq)]
267pub struct GenericJoint {
269 pub local_frame1: Pose,
271 pub local_frame2: Pose,
273 pub locked_axes: JointAxesMask,
275 pub limit_axes: JointAxesMask,
277 pub motor_axes: JointAxesMask,
279 pub coupled_axes: JointAxesMask,
286 pub limits: [JointLimits<Real>; SPATIAL_DIM],
292 pub motors: [JointMotor; SPATIAL_DIM],
298 pub softness: SpringCoefficients<Real>,
300 pub contacts_enabled: bool,
302 pub enabled: JointEnabled,
304 pub user_data: u128,
306}
307
308impl Default for GenericJoint {
309 fn default() -> Self {
310 Self {
311 local_frame1: Pose::IDENTITY,
312 local_frame2: Pose::IDENTITY,
313 locked_axes: JointAxesMask::empty(),
314 limit_axes: JointAxesMask::empty(),
315 motor_axes: JointAxesMask::empty(),
316 coupled_axes: JointAxesMask::empty(),
317 limits: [JointLimits::default(); SPATIAL_DIM],
318 motors: [JointMotor::default(); SPATIAL_DIM],
319 softness: SpringCoefficients::joint_defaults(),
320 contacts_enabled: true,
321 enabled: JointEnabled::Enabled,
322 user_data: 0,
323 }
324 }
325}
326
327impl GenericJoint {
328 #[must_use]
330 pub fn new(locked_axes: JointAxesMask) -> Self {
331 *Self::default().lock_axes(locked_axes)
332 }
333
334 #[cfg(feature = "alloc")]
341 pub(crate) fn supports_simd_constraints(&self) -> bool {
342 #[cfg(feature = "dim2")]
343 let motors_ok =
344 (self.motor_axes.bits() & !self.locked_axes.bits() & JointAxesMask::LIN_AXES.bits())
345 == 0;
346 #[cfg(feature = "dim3")]
347 let motors_ok = (self.motor_axes.bits() & !self.locked_axes.bits()) == 0;
348 motors_ok && (self.limit_axes & self.coupled_axes).is_empty()
349 }
350
351 #[cfg(feature = "alloc")]
355 pub(crate) fn simd_row_signature(&self) -> u32 {
356 let locked = self.locked_axes.bits() as u32;
357 let limits = (self.limit_axes.bits() & !self.locked_axes.bits()) as u32;
358 #[cfg(feature = "dim2")]
359 {
360 let motors = (self.motor_axes.bits() & !self.locked_axes.bits()) as u32;
363 let model = (self.motors[crate::math::DIM].model
364 == crate::dynamics::MotorModel::ForceBased) as u32;
365 locked | (limits << 8) | (motors << 16) | (model << 24)
366 }
367 #[cfg(feature = "dim3")]
368 {
369 locked | (limits << 8)
370 }
371 }
372
373 #[doc(hidden)]
374 pub fn complete_ang_frame(axis: Vector) -> Rotation {
375 #[cfg(feature = "dim2")]
376 {
377 let basis = axis.orthonormal_basis();
378 let mat = Matrix::from_cols(axis, basis[0]);
379 Rotation::from_matrix_unchecked(mat)
380 }
381
382 #[cfg(feature = "dim3")]
383 {
384 Rotation::from_rotation_arc(Vector::X, axis)
388 }
389 }
390
391 pub fn is_enabled(&self) -> bool {
393 self.enabled == JointEnabled::Enabled
394 }
395
396 pub fn set_enabled(&mut self, enabled: bool) {
398 match self.enabled {
399 JointEnabled::Enabled | JointEnabled::DisabledByAttachedBody => {
400 if !enabled {
401 self.enabled = JointEnabled::Disabled;
402 }
403 }
404 JointEnabled::Disabled => {
405 if enabled {
406 self.enabled = JointEnabled::Enabled;
407 }
408 }
409 }
410 }
411
412 pub fn lock_axes(&mut self, axes: JointAxesMask) -> &mut Self {
414 self.locked_axes |= axes;
415 self
416 }
417
418 pub fn set_local_frame1(&mut self, local_frame: Pose) -> &mut Self {
420 self.local_frame1 = local_frame;
421 self
422 }
423
424 pub fn set_local_frame2(&mut self, local_frame: Pose) -> &mut Self {
426 self.local_frame2 = local_frame;
427 self
428 }
429
430 #[must_use]
432 pub fn local_axis1(&self) -> Vector {
433 self.local_frame1 * Vector::X
434 }
435
436 pub fn set_local_axis1(&mut self, local_axis: Vector) -> &mut Self {
443 self.local_frame1.rotation = Self::complete_ang_frame(local_axis);
444 self
445 }
446
447 #[must_use]
449 pub fn local_axis2(&self) -> Vector {
450 self.local_frame2 * Vector::X
451 }
452
453 pub fn set_local_axis2(&mut self, local_axis: Vector) -> &mut Self {
460 self.local_frame2.rotation = Self::complete_ang_frame(local_axis);
461 self
462 }
463
464 #[must_use]
466 pub fn local_anchor1(&self) -> Vector {
467 self.local_frame1.translation
468 }
469
470 pub fn set_local_anchor1(&mut self, anchor1: Vector) -> &mut Self {
472 self.local_frame1.translation = anchor1;
473 self
474 }
475
476 #[must_use]
478 pub fn local_anchor2(&self) -> Vector {
479 self.local_frame2.translation
480 }
481
482 pub fn set_local_anchor2(&mut self, anchor2: Vector) -> &mut Self {
484 self.local_frame2.translation = anchor2;
485 self
486 }
487
488 pub fn contacts_enabled(&self) -> bool {
490 self.contacts_enabled
491 }
492
493 pub fn set_contacts_enabled(&mut self, enabled: bool) -> &mut Self {
495 self.contacts_enabled = enabled;
496 self
497 }
498
499 #[must_use]
501 pub fn set_softness(&mut self, softness: SpringCoefficients<Real>) -> &mut Self {
502 self.softness = softness;
503 self
504 }
505
506 #[must_use]
508 pub fn limits(&self, axis: JointAxis) -> Option<&JointLimits<Real>> {
509 let i = axis as usize;
510 if self.limit_axes.contains(axis.into()) {
511 Some(&self.limits[i])
512 } else {
513 None
514 }
515 }
516
517 pub fn set_limits(&mut self, axis: JointAxis, limits: [Real; 2]) -> &mut Self {
519 let i = axis as usize;
520 self.limit_axes |= axis.into();
521 self.limits[i].min = limits[0];
522 self.limits[i].max = limits[1];
523 self
524 }
525
526 #[must_use]
528 pub fn motor_model(&self, axis: JointAxis) -> Option<MotorModel> {
529 let i = axis as usize;
530 if self.motor_axes.contains(axis.into()) {
531 Some(self.motors[i].model)
532 } else {
533 None
534 }
535 }
536
537 pub fn set_motor_model(&mut self, axis: JointAxis, model: MotorModel) -> &mut Self {
539 self.motors[axis as usize].model = model;
540 self
541 }
542
543 pub fn set_motor_velocity(
545 &mut self,
546 axis: JointAxis,
547 target_vel: Real,
548 factor: Real,
549 ) -> &mut Self {
550 self.set_motor(
551 axis,
552 self.motors[axis as usize].target_pos,
553 target_vel,
554 0.0,
555 factor,
556 )
557 }
558
559 pub fn set_motor_position(
561 &mut self,
562 axis: JointAxis,
563 target_pos: Real,
564 stiffness: Real,
565 damping: Real,
566 ) -> &mut Self {
567 self.set_motor(axis, target_pos, 0.0, stiffness, damping)
568 }
569
570 pub fn set_motor_max_force(&mut self, axis: JointAxis, max_force: Real) -> &mut Self {
572 self.motors[axis as usize].max_force = max_force;
573 self
574 }
575
576 #[must_use]
578 pub fn motor(&self, axis: JointAxis) -> Option<&JointMotor> {
579 let i = axis as usize;
580 if self.motor_axes.contains(axis.into()) {
581 Some(&self.motors[i])
582 } else {
583 None
584 }
585 }
586
587 pub fn set_motor(
589 &mut self,
590 axis: JointAxis,
591 target_pos: Real,
592 target_vel: Real,
593 stiffness: Real,
594 damping: Real,
595 ) -> &mut Self {
596 self.motor_axes |= axis.into();
597 let i = axis as usize;
598 self.motors[i].target_vel = target_vel;
599 self.motors[i].target_pos = target_pos;
600 self.motors[i].stiffness = stiffness;
601 self.motors[i].damping = damping;
602 self
603 }
604
605 pub fn flip(&mut self) {
607 core::mem::swap(&mut self.local_frame1, &mut self.local_frame2);
608
609 let coupled_bits = self.coupled_axes.bits();
610
611 for dim in 0..SPATIAL_DIM {
612 if coupled_bits & (1 << dim) == 0 {
613 let limit = self.limits[dim];
614 self.limits[dim].min = -limit.max;
615 self.limits[dim].max = -limit.min;
616 }
617
618 self.motors[dim].target_vel = -self.motors[dim].target_vel;
619 self.motors[dim].target_pos = -self.motors[dim].target_pos;
620 }
621 }
622
623 #[cfg(feature = "alloc")]
624 pub(crate) fn transform_to_solver_body_space(&mut self, rb1: &RigidBody, rb2: &RigidBody) {
625 if rb1.is_fixed() {
626 self.local_frame1 = rb1.pos.position * self.local_frame1;
627 } else {
628 self.local_frame1.translation -= rb1.mprops.local_mprops.local_com;
629 }
630
631 if rb2.is_fixed() {
632 self.local_frame2 = rb2.pos.position * self.local_frame2;
633 } else {
634 self.local_frame2.translation -= rb2.mprops.local_mprops.local_com;
635 }
636 }
637}
638
639macro_rules! joint_conversion_methods(
640 ($as_joint: ident, $as_joint_mut: ident, $Joint: ty, $axes: expr) => {
641 #[must_use]
643 pub fn $as_joint(&self) -> Option<&$Joint> {
644 if self.locked_axes == $axes {
645 Some(unsafe { core::mem::transmute::<&Self, &$Joint>(self) })
648 } else {
649 None
650 }
651 }
652
653 #[must_use]
655 pub fn $as_joint_mut(&mut self) -> Option<&mut $Joint> {
656 if self.locked_axes == $axes {
657 Some(unsafe { core::mem::transmute::<&mut Self, &mut $Joint>(self) })
660 } else {
661 None
662 }
663 }
664 }
665);
666
667impl GenericJoint {
668 joint_conversion_methods!(
669 as_revolute,
670 as_revolute_mut,
671 RevoluteJoint,
672 JointAxesMask::LOCKED_REVOLUTE_AXES
673 );
674 joint_conversion_methods!(
675 as_fixed,
676 as_fixed_mut,
677 FixedJoint,
678 JointAxesMask::LOCKED_FIXED_AXES
679 );
680 joint_conversion_methods!(
681 as_prismatic,
682 as_prismatic_mut,
683 PrismaticJoint,
684 JointAxesMask::LOCKED_PRISMATIC_AXES
685 );
686 joint_conversion_methods!(
687 as_rope,
688 as_rope_mut,
689 RopeJoint,
690 JointAxesMask::FREE_FIXED_AXES
691 );
692
693 #[cfg(feature = "dim3")]
694 joint_conversion_methods!(
695 as_spherical,
696 as_spherical_mut,
697 SphericalJoint,
698 JointAxesMask::LOCKED_SPHERICAL_AXES
699 );
700}
701
702#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
704#[derive(Copy, Clone, Debug, PartialEq)]
705pub struct GenericJointBuilder(pub GenericJoint);
706
707impl GenericJointBuilder {
708 #[must_use]
710 pub fn new(locked_axes: JointAxesMask) -> Self {
711 Self(GenericJoint::new(locked_axes))
712 }
713
714 #[must_use]
716 pub fn locked_axes(mut self, axes: JointAxesMask) -> Self {
717 self.0.locked_axes = axes;
718 self
719 }
720
721 #[must_use]
723 pub fn contacts_enabled(mut self, enabled: bool) -> Self {
724 self.0.contacts_enabled = enabled;
725 self
726 }
727
728 #[must_use]
730 pub fn local_frame1(mut self, local_frame: Pose) -> Self {
731 self.0.set_local_frame1(local_frame);
732 self
733 }
734
735 #[must_use]
737 pub fn local_frame2(mut self, local_frame: Pose) -> Self {
738 self.0.set_local_frame2(local_frame);
739 self
740 }
741
742 #[must_use]
744 pub fn local_axis1(mut self, local_axis: Vector) -> Self {
745 self.0.set_local_axis1(local_axis);
746 self
747 }
748
749 #[must_use]
751 pub fn local_axis2(mut self, local_axis: Vector) -> Self {
752 self.0.set_local_axis2(local_axis);
753 self
754 }
755
756 #[must_use]
758 pub fn local_anchor1(mut self, anchor1: Vector) -> Self {
759 self.0.set_local_anchor1(anchor1);
760 self
761 }
762
763 #[must_use]
765 pub fn local_anchor2(mut self, anchor2: Vector) -> Self {
766 self.0.set_local_anchor2(anchor2);
767 self
768 }
769
770 #[must_use]
772 pub fn limits(mut self, axis: JointAxis, limits: [Real; 2]) -> Self {
773 self.0.set_limits(axis, limits);
774 self
775 }
776
777 #[must_use]
779 pub fn coupled_axes(mut self, axes: JointAxesMask) -> Self {
780 self.0.coupled_axes = axes;
781 self
782 }
783
784 #[must_use]
786 pub fn motor_model(mut self, axis: JointAxis, model: MotorModel) -> Self {
787 self.0.set_motor_model(axis, model);
788 self
789 }
790
791 #[must_use]
793 pub fn motor_velocity(mut self, axis: JointAxis, target_vel: Real, factor: Real) -> Self {
794 self.0.set_motor_velocity(axis, target_vel, factor);
795 self
796 }
797
798 #[must_use]
800 pub fn motor_position(
801 mut self,
802 axis: JointAxis,
803 target_pos: Real,
804 stiffness: Real,
805 damping: Real,
806 ) -> Self {
807 self.0
808 .set_motor_position(axis, target_pos, stiffness, damping);
809 self
810 }
811
812 #[must_use]
814 pub fn set_motor(
815 mut self,
816 axis: JointAxis,
817 target_pos: Real,
818 target_vel: Real,
819 stiffness: Real,
820 damping: Real,
821 ) -> Self {
822 self.0
823 .set_motor(axis, target_pos, target_vel, stiffness, damping);
824 self
825 }
826
827 #[must_use]
829 pub fn motor_max_force(mut self, axis: JointAxis, max_force: Real) -> Self {
830 self.0.set_motor_max_force(axis, max_force);
831 self
832 }
833
834 #[must_use]
836 pub fn softness(mut self, softness: SpringCoefficients<Real>) -> Self {
837 self.0.softness = softness;
838 self
839 }
840
841 pub fn user_data(mut self, data: u128) -> Self {
843 self.0.user_data = data;
844 self
845 }
846
847 #[must_use]
849 pub fn build(self) -> GenericJoint {
850 self.0
851 }
852}
853
854impl From<GenericJointBuilder> for GenericJoint {
855 fn from(val: GenericJointBuilder) -> GenericJoint {
856 val.0
857 }
858}