1#![allow(clippy::bad_bit_mask)] #![allow(clippy::unnecessary_cast)] use crate::dynamics::integration_parameters::SpringCoefficients;
5use crate::dynamics::solver::MotorParameters;
6use crate::dynamics::{
7 FixedJoint, MotorModel, PrismaticJoint, RevoluteJoint, RigidBody, RopeJoint,
8};
9use crate::math::{Pose, Real, Rotation, SPATIAL_DIM, Vector};
10use crate::utils::{OrthonormalBasis, SimdRealCopy};
11use parry::math::Matrix;
12
13#[cfg(feature = "dim3")]
14use crate::dynamics::SphericalJoint;
15
16#[cfg(feature = "dim3")]
17bitflags::bitflags! {
18 #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
20 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
21 pub struct JointAxesMask: u8 {
22 const LIN_X = 1 << 0;
24 const LIN_Y = 1 << 1;
26 const LIN_Z = 1 << 2;
28 const ANG_X = 1 << 3;
30 const ANG_Y = 1 << 4;
32 const ANG_Z = 1 << 5;
34 const LOCKED_REVOLUTE_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits() | Self::LIN_Z.bits() | Self::ANG_Y.bits() | Self::ANG_Z.bits();
36 const LOCKED_PRISMATIC_AXES = Self::LIN_Y.bits() | Self::LIN_Z.bits() | Self::ANG_X.bits() | Self::ANG_Y.bits() | Self::ANG_Z.bits();
38 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();
40 const LOCKED_SPHERICAL_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits() | Self::LIN_Z.bits();
42 const FREE_REVOLUTE_AXES = Self::ANG_X.bits();
44 const FREE_PRISMATIC_AXES = Self::LIN_X.bits();
46 const FREE_FIXED_AXES = 0;
48 const FREE_SPHERICAL_AXES = Self::ANG_X.bits() | Self::ANG_Y.bits() | Self::ANG_Z.bits();
50 const LIN_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits() | Self::LIN_Z.bits();
52 const ANG_AXES = Self::ANG_X.bits() | Self::ANG_Y.bits() | Self::ANG_Z.bits();
54 }
55}
56
57#[cfg(feature = "dim2")]
58bitflags::bitflags! {
59 #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
61 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
62 pub struct JointAxesMask: u8 {
63 const LIN_X = 1 << 0;
65 const LIN_Y = 1 << 1;
67 const ANG_X = 1 << 2;
69 const LOCKED_REVOLUTE_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits();
71 const LOCKED_PRISMATIC_AXES = Self::LIN_Y.bits() | Self::ANG_X.bits();
73 const LOCKED_PIN_SLOT_AXES = Self::LIN_Y.bits();
75 const LOCKED_FIXED_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits() | Self::ANG_X.bits();
77 const FREE_REVOLUTE_AXES = Self::ANG_X.bits();
79 const FREE_PRISMATIC_AXES = Self::LIN_X.bits();
81 const FREE_FIXED_AXES = 0;
83 const LIN_AXES = Self::LIN_X.bits() | Self::LIN_Y.bits();
85 const ANG_AXES = Self::ANG_X.bits();
87 }
88}
89
90impl Default for JointAxesMask {
91 fn default() -> Self {
92 Self::empty()
93 }
94}
95
96#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
98#[derive(Copy, Clone, Debug, PartialEq)]
99pub enum JointAxis {
100 LinX = 0,
102 LinY,
104 #[cfg(feature = "dim3")]
106 LinZ,
107 AngX,
109 #[cfg(feature = "dim3")]
111 AngY,
112 #[cfg(feature = "dim3")]
114 AngZ,
115}
116
117impl From<JointAxis> for JointAxesMask {
118 fn from(axis: JointAxis) -> Self {
119 JointAxesMask::from_bits(1 << axis as usize).unwrap()
120 }
121}
122
123#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
132#[derive(Copy, Clone, Debug, PartialEq)]
133pub struct JointLimits<N> {
134 pub min: N,
136 pub max: N,
138 pub impulse: N,
140}
141
142impl<N: SimdRealCopy> Default for JointLimits<N> {
143 fn default() -> Self {
144 Self {
145 min: -N::splat(Real::MAX),
146 max: N::splat(Real::MAX),
147 impulse: N::splat(0.0),
148 }
149 }
150}
151
152impl<N: SimdRealCopy> From<[N; 2]> for JointLimits<N> {
153 fn from(value: [N; 2]) -> Self {
154 Self {
155 min: value[0],
156 max: value[1],
157 impulse: N::splat(0.0),
158 }
159 }
160}
161
162#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
193#[derive(Copy, Clone, Debug, PartialEq)]
194pub struct JointMotor {
195 pub target_vel: Real,
197 pub target_pos: Real,
199 pub stiffness: Real,
201 pub damping: Real,
203 pub max_force: Real,
205 pub impulse: Real,
207 pub model: MotorModel,
209}
210
211impl Default for JointMotor {
212 fn default() -> Self {
213 Self {
214 target_pos: 0.0,
215 target_vel: 0.0,
216 stiffness: 0.0,
217 damping: 0.0,
218 max_force: Real::MAX,
219 impulse: 0.0,
220 model: MotorModel::AccelerationBased,
221 }
222 }
223}
224
225impl JointMotor {
226 pub(crate) fn motor_params(&self, dt: Real) -> MotorParameters<Real> {
227 let (erp_inv_dt, cfm_coeff, cfm_gain) =
228 self.model
229 .combine_coefficients(dt, self.stiffness, self.damping);
230 MotorParameters {
231 erp_inv_dt,
232 cfm_coeff,
233 cfm_gain,
234 target_pos: self.target_pos,
236 target_vel: self.target_vel,
237 max_impulse: self.max_force * dt,
238 }
239 }
240}
241
242#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
243#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
244pub enum JointEnabled {
246 Enabled,
248 DisabledByAttachedBody,
251 Disabled,
253}
254
255#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
256#[derive(Copy, Clone, Debug, PartialEq)]
257pub struct GenericJoint {
259 pub local_frame1: Pose,
261 pub local_frame2: Pose,
263 pub locked_axes: JointAxesMask,
265 pub limit_axes: JointAxesMask,
267 pub motor_axes: JointAxesMask,
269 pub coupled_axes: JointAxesMask,
276 pub limits: [JointLimits<Real>; SPATIAL_DIM],
282 pub motors: [JointMotor; SPATIAL_DIM],
288 pub softness: SpringCoefficients<Real>,
290 pub contacts_enabled: bool,
292 pub enabled: JointEnabled,
294 pub user_data: u128,
296}
297
298impl Default for GenericJoint {
299 fn default() -> Self {
300 Self {
301 local_frame1: Pose::IDENTITY,
302 local_frame2: Pose::IDENTITY,
303 locked_axes: JointAxesMask::empty(),
304 limit_axes: JointAxesMask::empty(),
305 motor_axes: JointAxesMask::empty(),
306 coupled_axes: JointAxesMask::empty(),
307 limits: [JointLimits::default(); SPATIAL_DIM],
308 motors: [JointMotor::default(); SPATIAL_DIM],
309 softness: SpringCoefficients::joint_defaults(),
310 contacts_enabled: true,
311 enabled: JointEnabled::Enabled,
312 user_data: 0,
313 }
314 }
315}
316
317impl GenericJoint {
318 #[must_use]
320 pub fn new(locked_axes: JointAxesMask) -> Self {
321 *Self::default().lock_axes(locked_axes)
322 }
323
324 #[cfg(feature = "simd-is-enabled")]
325 pub(crate) fn supports_simd_constraints(&self) -> bool {
327 self.limit_axes.is_empty() && self.motor_axes.is_empty()
328 }
329
330 #[doc(hidden)]
331 pub fn complete_ang_frame(axis: Vector) -> Rotation {
332 let basis = axis.orthonormal_basis();
333
334 #[cfg(feature = "dim2")]
335 {
336 let mat = Matrix::from_cols(axis, basis[0]);
337 Rotation::from_matrix_unchecked(mat)
338 }
339
340 #[cfg(feature = "dim3")]
341 {
342 let mat = Matrix::from_cols(axis, basis[0], basis[1]);
343 Rotation::from_mat3(&mat)
344 }
345 }
346
347 pub fn is_enabled(&self) -> bool {
349 self.enabled == JointEnabled::Enabled
350 }
351
352 pub fn set_enabled(&mut self, enabled: bool) {
354 match self.enabled {
355 JointEnabled::Enabled | JointEnabled::DisabledByAttachedBody => {
356 if !enabled {
357 self.enabled = JointEnabled::Disabled;
358 }
359 }
360 JointEnabled::Disabled => {
361 if enabled {
362 self.enabled = JointEnabled::Enabled;
363 }
364 }
365 }
366 }
367
368 pub fn lock_axes(&mut self, axes: JointAxesMask) -> &mut Self {
370 self.locked_axes |= axes;
371 self
372 }
373
374 pub fn set_local_frame1(&mut self, local_frame: Pose) -> &mut Self {
376 self.local_frame1 = local_frame;
377 self
378 }
379
380 pub fn set_local_frame2(&mut self, local_frame: Pose) -> &mut Self {
382 self.local_frame2 = local_frame;
383 self
384 }
385
386 #[must_use]
388 pub fn local_axis1(&self) -> Vector {
389 self.local_frame1 * Vector::X
390 }
391
392 pub fn set_local_axis1(&mut self, local_axis: Vector) -> &mut Self {
394 self.local_frame1.rotation = Self::complete_ang_frame(local_axis);
395 self
396 }
397
398 #[must_use]
400 pub fn local_axis2(&self) -> Vector {
401 self.local_frame2 * Vector::X
402 }
403
404 pub fn set_local_axis2(&mut self, local_axis: Vector) -> &mut Self {
406 self.local_frame2.rotation = Self::complete_ang_frame(local_axis);
407 self
408 }
409
410 #[must_use]
412 pub fn local_anchor1(&self) -> Vector {
413 self.local_frame1.translation
414 }
415
416 pub fn set_local_anchor1(&mut self, anchor1: Vector) -> &mut Self {
418 self.local_frame1.translation = anchor1;
419 self
420 }
421
422 #[must_use]
424 pub fn local_anchor2(&self) -> Vector {
425 self.local_frame2.translation
426 }
427
428 pub fn set_local_anchor2(&mut self, anchor2: Vector) -> &mut Self {
430 self.local_frame2.translation = anchor2;
431 self
432 }
433
434 pub fn contacts_enabled(&self) -> bool {
436 self.contacts_enabled
437 }
438
439 pub fn set_contacts_enabled(&mut self, enabled: bool) -> &mut Self {
441 self.contacts_enabled = enabled;
442 self
443 }
444
445 #[must_use]
447 pub fn set_softness(&mut self, softness: SpringCoefficients<Real>) -> &mut Self {
448 self.softness = softness;
449 self
450 }
451
452 #[must_use]
454 pub fn limits(&self, axis: JointAxis) -> Option<&JointLimits<Real>> {
455 let i = axis as usize;
456 if self.limit_axes.contains(axis.into()) {
457 Some(&self.limits[i])
458 } else {
459 None
460 }
461 }
462
463 pub fn set_limits(&mut self, axis: JointAxis, limits: [Real; 2]) -> &mut Self {
465 let i = axis as usize;
466 self.limit_axes |= axis.into();
467 self.limits[i].min = limits[0];
468 self.limits[i].max = limits[1];
469 self
470 }
471
472 #[must_use]
474 pub fn motor_model(&self, axis: JointAxis) -> Option<MotorModel> {
475 let i = axis as usize;
476 if self.motor_axes.contains(axis.into()) {
477 Some(self.motors[i].model)
478 } else {
479 None
480 }
481 }
482
483 pub fn set_motor_model(&mut self, axis: JointAxis, model: MotorModel) -> &mut Self {
485 self.motors[axis as usize].model = model;
486 self
487 }
488
489 pub fn set_motor_velocity(
491 &mut self,
492 axis: JointAxis,
493 target_vel: Real,
494 factor: Real,
495 ) -> &mut Self {
496 self.set_motor(
497 axis,
498 self.motors[axis as usize].target_pos,
499 target_vel,
500 0.0,
501 factor,
502 )
503 }
504
505 pub fn set_motor_position(
507 &mut self,
508 axis: JointAxis,
509 target_pos: Real,
510 stiffness: Real,
511 damping: Real,
512 ) -> &mut Self {
513 self.set_motor(axis, target_pos, 0.0, stiffness, damping)
514 }
515
516 pub fn set_motor_max_force(&mut self, axis: JointAxis, max_force: Real) -> &mut Self {
518 self.motors[axis as usize].max_force = max_force;
519 self
520 }
521
522 #[must_use]
524 pub fn motor(&self, axis: JointAxis) -> Option<&JointMotor> {
525 let i = axis as usize;
526 if self.motor_axes.contains(axis.into()) {
527 Some(&self.motors[i])
528 } else {
529 None
530 }
531 }
532
533 pub fn set_motor(
535 &mut self,
536 axis: JointAxis,
537 target_pos: Real,
538 target_vel: Real,
539 stiffness: Real,
540 damping: Real,
541 ) -> &mut Self {
542 self.motor_axes |= axis.into();
543 let i = axis as usize;
544 self.motors[i].target_vel = target_vel;
545 self.motors[i].target_pos = target_pos;
546 self.motors[i].stiffness = stiffness;
547 self.motors[i].damping = damping;
548 self
549 }
550
551 pub fn flip(&mut self) {
553 std::mem::swap(&mut self.local_frame1, &mut self.local_frame2);
554
555 let coupled_bits = self.coupled_axes.bits();
556
557 for dim in 0..SPATIAL_DIM {
558 if coupled_bits & (1 << dim) == 0 {
559 let limit = self.limits[dim];
560 self.limits[dim].min = -limit.max;
561 self.limits[dim].max = -limit.min;
562 }
563
564 self.motors[dim].target_vel = -self.motors[dim].target_vel;
565 self.motors[dim].target_pos = -self.motors[dim].target_pos;
566 }
567 }
568
569 pub(crate) fn transform_to_solver_body_space(&mut self, rb1: &RigidBody, rb2: &RigidBody) {
570 if rb1.is_fixed() {
571 self.local_frame1 = rb1.pos.position * self.local_frame1;
572 } else {
573 self.local_frame1.translation -= rb1.mprops.local_mprops.local_com;
574 }
575
576 if rb2.is_fixed() {
577 self.local_frame2 = rb2.pos.position * self.local_frame2;
578 } else {
579 self.local_frame2.translation -= rb2.mprops.local_mprops.local_com;
580 }
581 }
582}
583
584macro_rules! joint_conversion_methods(
585 ($as_joint: ident, $as_joint_mut: ident, $Joint: ty, $axes: expr) => {
586 #[must_use]
588 pub fn $as_joint(&self) -> Option<&$Joint> {
589 if self.locked_axes == $axes {
590 Some(unsafe { std::mem::transmute::<&Self, &$Joint>(self) })
593 } else {
594 None
595 }
596 }
597
598 #[must_use]
600 pub fn $as_joint_mut(&mut self) -> Option<&mut $Joint> {
601 if self.locked_axes == $axes {
602 Some(unsafe { std::mem::transmute::<&mut Self, &mut $Joint>(self) })
605 } else {
606 None
607 }
608 }
609 }
610);
611
612impl GenericJoint {
613 joint_conversion_methods!(
614 as_revolute,
615 as_revolute_mut,
616 RevoluteJoint,
617 JointAxesMask::LOCKED_REVOLUTE_AXES
618 );
619 joint_conversion_methods!(
620 as_fixed,
621 as_fixed_mut,
622 FixedJoint,
623 JointAxesMask::LOCKED_FIXED_AXES
624 );
625 joint_conversion_methods!(
626 as_prismatic,
627 as_prismatic_mut,
628 PrismaticJoint,
629 JointAxesMask::LOCKED_PRISMATIC_AXES
630 );
631 joint_conversion_methods!(
632 as_rope,
633 as_rope_mut,
634 RopeJoint,
635 JointAxesMask::FREE_FIXED_AXES
636 );
637
638 #[cfg(feature = "dim3")]
639 joint_conversion_methods!(
640 as_spherical,
641 as_spherical_mut,
642 SphericalJoint,
643 JointAxesMask::LOCKED_SPHERICAL_AXES
644 );
645}
646
647#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
649#[derive(Copy, Clone, Debug, PartialEq)]
650pub struct GenericJointBuilder(pub GenericJoint);
651
652impl GenericJointBuilder {
653 #[must_use]
655 pub fn new(locked_axes: JointAxesMask) -> Self {
656 Self(GenericJoint::new(locked_axes))
657 }
658
659 #[must_use]
661 pub fn locked_axes(mut self, axes: JointAxesMask) -> Self {
662 self.0.locked_axes = axes;
663 self
664 }
665
666 #[must_use]
668 pub fn contacts_enabled(mut self, enabled: bool) -> Self {
669 self.0.contacts_enabled = enabled;
670 self
671 }
672
673 #[must_use]
675 pub fn local_frame1(mut self, local_frame: Pose) -> Self {
676 self.0.set_local_frame1(local_frame);
677 self
678 }
679
680 #[must_use]
682 pub fn local_frame2(mut self, local_frame: Pose) -> Self {
683 self.0.set_local_frame2(local_frame);
684 self
685 }
686
687 #[must_use]
689 pub fn local_axis1(mut self, local_axis: Vector) -> Self {
690 self.0.set_local_axis1(local_axis);
691 self
692 }
693
694 #[must_use]
696 pub fn local_axis2(mut self, local_axis: Vector) -> Self {
697 self.0.set_local_axis2(local_axis);
698 self
699 }
700
701 #[must_use]
703 pub fn local_anchor1(mut self, anchor1: Vector) -> Self {
704 self.0.set_local_anchor1(anchor1);
705 self
706 }
707
708 #[must_use]
710 pub fn local_anchor2(mut self, anchor2: Vector) -> Self {
711 self.0.set_local_anchor2(anchor2);
712 self
713 }
714
715 #[must_use]
717 pub fn limits(mut self, axis: JointAxis, limits: [Real; 2]) -> Self {
718 self.0.set_limits(axis, limits);
719 self
720 }
721
722 #[must_use]
724 pub fn coupled_axes(mut self, axes: JointAxesMask) -> Self {
725 self.0.coupled_axes = axes;
726 self
727 }
728
729 #[must_use]
731 pub fn motor_model(mut self, axis: JointAxis, model: MotorModel) -> Self {
732 self.0.set_motor_model(axis, model);
733 self
734 }
735
736 #[must_use]
738 pub fn motor_velocity(mut self, axis: JointAxis, target_vel: Real, factor: Real) -> Self {
739 self.0.set_motor_velocity(axis, target_vel, factor);
740 self
741 }
742
743 #[must_use]
745 pub fn motor_position(
746 mut self,
747 axis: JointAxis,
748 target_pos: Real,
749 stiffness: Real,
750 damping: Real,
751 ) -> Self {
752 self.0
753 .set_motor_position(axis, target_pos, stiffness, damping);
754 self
755 }
756
757 #[must_use]
759 pub fn set_motor(
760 mut self,
761 axis: JointAxis,
762 target_pos: Real,
763 target_vel: Real,
764 stiffness: Real,
765 damping: Real,
766 ) -> Self {
767 self.0
768 .set_motor(axis, target_pos, target_vel, stiffness, damping);
769 self
770 }
771
772 #[must_use]
774 pub fn motor_max_force(mut self, axis: JointAxis, max_force: Real) -> Self {
775 self.0.set_motor_max_force(axis, max_force);
776 self
777 }
778
779 #[must_use]
781 pub fn softness(mut self, softness: SpringCoefficients<Real>) -> Self {
782 self.0.softness = softness;
783 self
784 }
785
786 pub fn user_data(mut self, data: u128) -> Self {
788 self.0.user_data = data;
789 self
790 }
791
792 #[must_use]
794 pub fn build(self) -> GenericJoint {
795 self.0
796 }
797}
798
799impl From<GenericJointBuilder> for GenericJoint {
800 fn from(val: GenericJointBuilder) -> GenericJoint {
801 val.0
802 }
803}