1#[cfg(all(not(feature = "std"), feature = "dim3"))]
2use simba::scalar::ComplexField;
3
4use crate::dynamics::integration_parameters::SpringCoefficients;
5use crate::dynamics::joint::{GenericJoint, GenericJointBuilder, JointAxesMask};
6use crate::dynamics::{JointAxis, JointLimits, JointMotor, MotorModel};
7use crate::math::{Real, Rotation, Vector};
8
9#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
10#[derive(Copy, Clone, Debug, PartialEq)]
11#[repr(transparent)]
12pub struct RevoluteJoint {
27 pub data: GenericJoint,
29}
30
31impl RevoluteJoint {
32 #[cfg(feature = "dim2")]
34 #[allow(clippy::new_without_default)] pub fn new() -> Self {
36 let data = GenericJointBuilder::new(JointAxesMask::LOCKED_REVOLUTE_AXES);
37 Self { data: data.build() }
38 }
39
40 #[cfg(feature = "dim3")]
44 pub fn new(axis: Vector) -> Self {
45 let data = GenericJointBuilder::new(JointAxesMask::LOCKED_REVOLUTE_AXES)
46 .local_axis1(axis)
47 .local_axis2(axis)
48 .build();
49 Self { data }
50 }
51
52 pub fn data(&self) -> &GenericJoint {
54 &self.data
55 }
56
57 pub fn contacts_enabled(&self) -> bool {
59 self.data.contacts_enabled
60 }
61
62 pub fn set_contacts_enabled(&mut self, enabled: bool) -> &mut Self {
64 self.data.set_contacts_enabled(enabled);
65 self
66 }
67
68 #[must_use]
70 pub fn local_anchor1(&self) -> Vector {
71 self.data.local_anchor1()
72 }
73
74 pub fn set_local_anchor1(&mut self, anchor1: Vector) -> &mut Self {
76 self.data.set_local_anchor1(anchor1);
77 self
78 }
79
80 #[must_use]
82 pub fn local_anchor2(&self) -> Vector {
83 self.data.local_anchor2()
84 }
85
86 pub fn set_local_anchor2(&mut self, anchor2: Vector) -> &mut Self {
88 self.data.set_local_anchor2(anchor2);
89 self
90 }
91
92 pub fn angle(&self, rb_rot1: &Rotation, rb_rot2: &Rotation) -> Real {
98 let joint_rot1 = rb_rot1 * self.data.local_frame1.rotation;
99 let joint_rot2 = rb_rot2 * self.data.local_frame2.rotation;
100 let ang_err = joint_rot1.inverse() * joint_rot2;
101
102 #[cfg(feature = "dim3")]
103 if joint_rot1.dot(joint_rot2) < 0.0 {
104 -ang_err.x.clamp(-1.0, 1.0).asin() * 2.0
105 } else {
106 ang_err.x.clamp(-1.0, 1.0).asin() * 2.0
107 }
108
109 #[cfg(feature = "dim2")]
110 {
111 ang_err.angle()
112 }
113 }
114
115 #[must_use]
117 pub fn motor(&self) -> Option<&JointMotor> {
118 self.data.motor(JointAxis::AngX)
119 }
120
121 pub fn set_motor_model(&mut self, model: MotorModel) -> &mut Self {
123 self.data.set_motor_model(JointAxis::AngX, model);
124 self
125 }
126
127 pub fn set_motor_velocity(&mut self, target_vel: Real, factor: Real) -> &mut Self {
135 self.data
136 .set_motor_velocity(JointAxis::AngX, target_vel, factor);
137 self
138 }
139
140 pub fn set_motor_position(
149 &mut self,
150 target_pos: Real,
151 stiffness: Real,
152 damping: Real,
153 ) -> &mut Self {
154 self.data
155 .set_motor_position(JointAxis::AngX, target_pos, stiffness, damping);
156 self
157 }
158
159 pub fn set_motor(
163 &mut self,
164 target_pos: Real,
165 target_vel: Real,
166 stiffness: Real,
167 damping: Real,
168 ) -> &mut Self {
169 self.data
170 .set_motor(JointAxis::AngX, target_pos, target_vel, stiffness, damping);
171 self
172 }
173
174 pub fn set_motor_max_force(&mut self, max_force: Real) -> &mut Self {
178 self.data.set_motor_max_force(JointAxis::AngX, max_force);
179 self
180 }
181
182 #[must_use]
186 pub fn limits(&self) -> Option<&JointLimits<Real>> {
187 self.data.limits(JointAxis::AngX)
188 }
189
190 pub fn set_limits(&mut self, limits: [Real; 2]) -> &mut Self {
207 self.data.set_limits(JointAxis::AngX, limits);
208 self
209 }
210
211 #[must_use]
213 pub fn softness(&self) -> SpringCoefficients<Real> {
214 self.data.softness
215 }
216
217 #[must_use]
219 pub fn set_softness(&mut self, softness: SpringCoefficients<Real>) -> &mut Self {
220 self.data.softness = softness;
221 self
222 }
223}
224
225impl From<RevoluteJoint> for GenericJoint {
226 fn from(val: RevoluteJoint) -> GenericJoint {
227 val.data
228 }
229}
230
231#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
235#[derive(Copy, Clone, Debug, PartialEq)]
236pub struct RevoluteJointBuilder(pub RevoluteJoint);
237
238impl RevoluteJointBuilder {
239 #[cfg(feature = "dim2")]
241 #[allow(clippy::new_without_default)] pub fn new() -> Self {
243 Self(RevoluteJoint::new())
244 }
245
246 #[cfg(feature = "dim3")]
250 pub fn new(axis: Vector) -> Self {
251 Self(RevoluteJoint::new(axis))
252 }
253
254 #[must_use]
256 pub fn contacts_enabled(mut self, enabled: bool) -> Self {
257 self.0.set_contacts_enabled(enabled);
258 self
259 }
260
261 #[must_use]
263 pub fn local_anchor1(mut self, anchor1: Vector) -> Self {
264 self.0.set_local_anchor1(anchor1);
265 self
266 }
267
268 #[must_use]
270 pub fn local_anchor2(mut self, anchor2: Vector) -> Self {
271 self.0.set_local_anchor2(anchor2);
272 self
273 }
274
275 #[must_use]
277 pub fn motor_model(mut self, model: MotorModel) -> Self {
278 self.0.set_motor_model(model);
279 self
280 }
281
282 #[must_use]
284 pub fn motor_velocity(mut self, target_vel: Real, factor: Real) -> Self {
285 self.0.set_motor_velocity(target_vel, factor);
286 self
287 }
288
289 #[must_use]
291 pub fn motor_position(mut self, target_pos: Real, stiffness: Real, damping: Real) -> Self {
292 self.0.set_motor_position(target_pos, stiffness, damping);
293 self
294 }
295
296 #[must_use]
298 pub fn motor(
299 mut self,
300 target_pos: Real,
301 target_vel: Real,
302 stiffness: Real,
303 damping: Real,
304 ) -> Self {
305 self.0.set_motor(target_pos, target_vel, stiffness, damping);
306 self
307 }
308
309 #[must_use]
311 pub fn motor_max_force(mut self, max_force: Real) -> Self {
312 self.0.set_motor_max_force(max_force);
313 self
314 }
315
316 #[must_use]
318 pub fn limits(mut self, limits: [Real; 2]) -> Self {
319 self.0.set_limits(limits);
320 self
321 }
322
323 #[must_use]
325 pub fn softness(mut self, softness: SpringCoefficients<Real>) -> Self {
326 self.0.data.softness = softness;
327 self
328 }
329
330 #[must_use]
332 pub fn build(self) -> RevoluteJoint {
333 self.0
334 }
335}
336
337impl From<RevoluteJointBuilder> for GenericJoint {
338 fn from(val: RevoluteJointBuilder) -> GenericJoint {
339 val.0.into()
340 }
341}
342
343#[cfg(test)]
344mod test {
345 #[test]
346 fn test_revolute_joint_angle() {
347 #[cfg(feature = "dim3")]
348 use crate::math::{AngVector, Vector};
349 use crate::math::{Real, rotation_from_angle};
350 use crate::na::RealField;
351
352 #[cfg(feature = "dim2")]
353 let revolute = super::RevoluteJointBuilder::new().build();
354 #[cfg(feature = "dim2")]
355 let rot1 = rotation_from_angle(1.0);
356 #[cfg(feature = "dim3")]
357 let revolute = super::RevoluteJointBuilder::new(Vector::Y).build();
358 #[cfg(feature = "dim3")]
359 let rot1 = rotation_from_angle(AngVector::new(0.0, 1.0, 0.0));
360
361 let steps = 100;
362
363 for i in 1..steps {
365 let delta = -Real::pi() + i as Real * Real::two_pi() / steps as Real;
366 #[cfg(feature = "dim2")]
367 let rot2 = rotation_from_angle(1.0 + delta);
368 #[cfg(feature = "dim3")]
369 let rot2 = rotation_from_angle(AngVector::new(0.0, 1.0 + delta, 0.0));
370 approx::assert_relative_eq!(revolute.angle(&rot1, &rot2), delta, epsilon = 1.0e-5);
371 }
372
373 for delta in [-Real::pi(), Real::pi()] {
376 #[cfg(feature = "dim2")]
377 let rot2 = rotation_from_angle(1.0 + delta);
378 #[cfg(feature = "dim3")]
379 let rot2 = rotation_from_angle(AngVector::new(0.0, 1.0 + delta, 0.0));
380 approx::assert_relative_eq!(
381 revolute.angle(&rot1, &rot2).abs(),
382 delta.abs(),
383 epsilon = 1.0e-2
384 );
385 }
386 }
387}