1use crate::dynamics::integration_parameters::SpringCoefficients;
2use crate::dynamics::joint::{GenericJoint, GenericJointBuilder, JointAxesMask};
3use crate::dynamics::{JointAxis, JointLimits, JointMotor, MotorModel};
4use crate::math::{Real, Rotation, Vector};
5
6#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
7#[derive(Copy, Clone, Debug, PartialEq)]
8#[repr(transparent)]
9pub struct RevoluteJoint {
24 pub data: GenericJoint,
26}
27
28impl RevoluteJoint {
29 #[cfg(feature = "dim2")]
31 #[allow(clippy::new_without_default)] pub fn new() -> Self {
33 let data = GenericJointBuilder::new(JointAxesMask::LOCKED_REVOLUTE_AXES);
34 Self { data: data.build() }
35 }
36
37 #[cfg(feature = "dim3")]
41 pub fn new(axis: Vector) -> Self {
42 let data = GenericJointBuilder::new(JointAxesMask::LOCKED_REVOLUTE_AXES)
43 .local_axis1(axis)
44 .local_axis2(axis)
45 .build();
46 Self { data }
47 }
48
49 pub fn data(&self) -> &GenericJoint {
51 &self.data
52 }
53
54 pub fn contacts_enabled(&self) -> bool {
56 self.data.contacts_enabled
57 }
58
59 pub fn set_contacts_enabled(&mut self, enabled: bool) -> &mut Self {
61 self.data.set_contacts_enabled(enabled);
62 self
63 }
64
65 #[must_use]
67 pub fn local_anchor1(&self) -> Vector {
68 self.data.local_anchor1()
69 }
70
71 pub fn set_local_anchor1(&mut self, anchor1: Vector) -> &mut Self {
73 self.data.set_local_anchor1(anchor1);
74 self
75 }
76
77 #[must_use]
79 pub fn local_anchor2(&self) -> Vector {
80 self.data.local_anchor2()
81 }
82
83 pub fn set_local_anchor2(&mut self, anchor2: Vector) -> &mut Self {
85 self.data.set_local_anchor2(anchor2);
86 self
87 }
88
89 pub fn angle(&self, rb_rot1: &Rotation, rb_rot2: &Rotation) -> Real {
95 let joint_rot1 = rb_rot1 * self.data.local_frame1.rotation;
96 let joint_rot2 = rb_rot2 * self.data.local_frame2.rotation;
97 let ang_err = joint_rot1.inverse() * joint_rot2;
98
99 #[cfg(feature = "dim3")]
100 if joint_rot1.dot(joint_rot2) < 0.0 {
101 -ang_err.x.clamp(-1.0, 1.0).asin() * 2.0
102 } else {
103 ang_err.x.clamp(-1.0, 1.0).asin() * 2.0
104 }
105
106 #[cfg(feature = "dim2")]
107 {
108 ang_err.angle()
109 }
110 }
111
112 #[must_use]
114 pub fn motor(&self) -> Option<&JointMotor> {
115 self.data.motor(JointAxis::AngX)
116 }
117
118 pub fn set_motor_model(&mut self, model: MotorModel) -> &mut Self {
120 self.data.set_motor_model(JointAxis::AngX, model);
121 self
122 }
123
124 pub fn set_motor_velocity(&mut self, target_vel: Real, factor: Real) -> &mut Self {
132 self.data
133 .set_motor_velocity(JointAxis::AngX, target_vel, factor);
134 self
135 }
136
137 pub fn set_motor_position(
146 &mut self,
147 target_pos: Real,
148 stiffness: Real,
149 damping: Real,
150 ) -> &mut Self {
151 self.data
152 .set_motor_position(JointAxis::AngX, target_pos, stiffness, damping);
153 self
154 }
155
156 pub fn set_motor(
160 &mut self,
161 target_pos: Real,
162 target_vel: Real,
163 stiffness: Real,
164 damping: Real,
165 ) -> &mut Self {
166 self.data
167 .set_motor(JointAxis::AngX, target_pos, target_vel, stiffness, damping);
168 self
169 }
170
171 pub fn set_motor_max_force(&mut self, max_force: Real) -> &mut Self {
175 self.data.set_motor_max_force(JointAxis::AngX, max_force);
176 self
177 }
178
179 #[must_use]
183 pub fn limits(&self) -> Option<&JointLimits<Real>> {
184 self.data.limits(JointAxis::AngX)
185 }
186
187 pub fn set_limits(&mut self, limits: [Real; 2]) -> &mut Self {
201 self.data.set_limits(JointAxis::AngX, limits);
202 self
203 }
204
205 #[must_use]
207 pub fn softness(&self) -> SpringCoefficients<Real> {
208 self.data.softness
209 }
210
211 #[must_use]
213 pub fn set_softness(&mut self, softness: SpringCoefficients<Real>) -> &mut Self {
214 self.data.softness = softness;
215 self
216 }
217}
218
219impl From<RevoluteJoint> for GenericJoint {
220 fn from(val: RevoluteJoint) -> GenericJoint {
221 val.data
222 }
223}
224
225#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
229#[derive(Copy, Clone, Debug, PartialEq)]
230pub struct RevoluteJointBuilder(pub RevoluteJoint);
231
232impl RevoluteJointBuilder {
233 #[cfg(feature = "dim2")]
235 #[allow(clippy::new_without_default)] pub fn new() -> Self {
237 Self(RevoluteJoint::new())
238 }
239
240 #[cfg(feature = "dim3")]
244 pub fn new(axis: Vector) -> Self {
245 Self(RevoluteJoint::new(axis))
246 }
247
248 #[must_use]
250 pub fn contacts_enabled(mut self, enabled: bool) -> Self {
251 self.0.set_contacts_enabled(enabled);
252 self
253 }
254
255 #[must_use]
257 pub fn local_anchor1(mut self, anchor1: Vector) -> Self {
258 self.0.set_local_anchor1(anchor1);
259 self
260 }
261
262 #[must_use]
264 pub fn local_anchor2(mut self, anchor2: Vector) -> Self {
265 self.0.set_local_anchor2(anchor2);
266 self
267 }
268
269 #[must_use]
271 pub fn motor_model(mut self, model: MotorModel) -> Self {
272 self.0.set_motor_model(model);
273 self
274 }
275
276 #[must_use]
278 pub fn motor_velocity(mut self, target_vel: Real, factor: Real) -> Self {
279 self.0.set_motor_velocity(target_vel, factor);
280 self
281 }
282
283 #[must_use]
285 pub fn motor_position(mut self, target_pos: Real, stiffness: Real, damping: Real) -> Self {
286 self.0.set_motor_position(target_pos, stiffness, damping);
287 self
288 }
289
290 #[must_use]
292 pub fn motor(
293 mut self,
294 target_pos: Real,
295 target_vel: Real,
296 stiffness: Real,
297 damping: Real,
298 ) -> Self {
299 self.0.set_motor(target_pos, target_vel, stiffness, damping);
300 self
301 }
302
303 #[must_use]
305 pub fn motor_max_force(mut self, max_force: Real) -> Self {
306 self.0.set_motor_max_force(max_force);
307 self
308 }
309
310 #[must_use]
312 pub fn limits(mut self, limits: [Real; 2]) -> Self {
313 self.0.set_limits(limits);
314 self
315 }
316
317 #[must_use]
319 pub fn softness(mut self, softness: SpringCoefficients<Real>) -> Self {
320 self.0.data.softness = softness;
321 self
322 }
323
324 #[must_use]
326 pub fn build(self) -> RevoluteJoint {
327 self.0
328 }
329}
330
331impl From<RevoluteJointBuilder> for GenericJoint {
332 fn from(val: RevoluteJointBuilder) -> GenericJoint {
333 val.0.into()
334 }
335}
336
337#[cfg(test)]
338mod test {
339 #[test]
340 fn test_revolute_joint_angle() {
341 #[cfg(feature = "dim3")]
342 use crate::math::{AngVector, Vector};
343 use crate::math::{Real, rotation_from_angle};
344 use crate::na::RealField;
345
346 #[cfg(feature = "dim2")]
347 let revolute = super::RevoluteJointBuilder::new().build();
348 #[cfg(feature = "dim2")]
349 let rot1 = rotation_from_angle(1.0);
350 #[cfg(feature = "dim3")]
351 let revolute = super::RevoluteJointBuilder::new(Vector::Y).build();
352 #[cfg(feature = "dim3")]
353 let rot1 = rotation_from_angle(AngVector::new(0.0, 1.0, 0.0));
354
355 let steps = 100;
356
357 for i in 1..steps {
359 let delta = -Real::pi() + i as Real * Real::two_pi() / steps as Real;
360 #[cfg(feature = "dim2")]
361 let rot2 = rotation_from_angle(1.0 + delta);
362 #[cfg(feature = "dim3")]
363 let rot2 = rotation_from_angle(AngVector::new(0.0, 1.0 + delta, 0.0));
364 approx::assert_relative_eq!(revolute.angle(&rot1, &rot2), delta, epsilon = 1.0e-5);
365 }
366
367 for delta in [-Real::pi(), Real::pi()] {
370 #[cfg(feature = "dim2")]
371 let rot2 = rotation_from_angle(1.0 + delta);
372 #[cfg(feature = "dim3")]
373 let rot2 = rotation_from_angle(AngVector::new(0.0, 1.0 + delta, 0.0));
374 approx::assert_relative_eq!(
375 revolute.angle(&rot1, &rot2).abs(),
376 delta.abs(),
377 epsilon = 1.0e-2
378 );
379 }
380 }
381}