1use crate::dynamics::integration_parameters::SpringCoefficients;
2use crate::dynamics::joint::{GenericJoint, GenericJointBuilder, JointAxesMask};
3use crate::dynamics::{JointAxis, JointLimits, JointMotor, MotorModel};
4use crate::math::{Point, Real, Rotation};
5
6#[cfg(feature = "dim3")]
7use crate::math::UnitVector;
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: UnitVector<Real>) -> 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) -> Point<Real> {
71 self.data.local_anchor1()
72 }
73
74 pub fn set_local_anchor1(&mut self, anchor1: Point<Real>) -> &mut Self {
76 self.data.set_local_anchor1(anchor1);
77 self
78 }
79
80 #[must_use]
82 pub fn local_anchor2(&self) -> Point<Real> {
83 self.data.local_anchor2()
84 }
85
86 pub fn set_local_anchor2(&mut self, anchor2: Point<Real>) -> &mut Self {
88 self.data.set_local_anchor2(anchor2);
89 self
90 }
91
92 pub fn angle(&self, rb_rot1: &Rotation<Real>, rb_rot2: &Rotation<Real>) -> 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.i.clamp(-1.0, 1.0).asin() * 2.0
105 } else {
106 ang_err.i.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 {
204 self.data.set_limits(JointAxis::AngX, limits);
205 self
206 }
207
208 #[must_use]
210 pub fn softness(&self) -> SpringCoefficients<Real> {
211 self.data.softness
212 }
213
214 #[must_use]
216 pub fn set_softness(&mut self, softness: SpringCoefficients<Real>) -> &mut Self {
217 self.data.softness = softness;
218 self
219 }
220}
221
222impl From<RevoluteJoint> for GenericJoint {
223 fn from(val: RevoluteJoint) -> GenericJoint {
224 val.data
225 }
226}
227
228#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
232#[derive(Copy, Clone, Debug, PartialEq)]
233pub struct RevoluteJointBuilder(pub RevoluteJoint);
234
235impl RevoluteJointBuilder {
236 #[cfg(feature = "dim2")]
238 #[allow(clippy::new_without_default)] pub fn new() -> Self {
240 Self(RevoluteJoint::new())
241 }
242
243 #[cfg(feature = "dim3")]
247 pub fn new(axis: UnitVector<Real>) -> Self {
248 Self(RevoluteJoint::new(axis))
249 }
250
251 #[must_use]
253 pub fn contacts_enabled(mut self, enabled: bool) -> Self {
254 self.0.set_contacts_enabled(enabled);
255 self
256 }
257
258 #[must_use]
260 pub fn local_anchor1(mut self, anchor1: Point<Real>) -> Self {
261 self.0.set_local_anchor1(anchor1);
262 self
263 }
264
265 #[must_use]
267 pub fn local_anchor2(mut self, anchor2: Point<Real>) -> Self {
268 self.0.set_local_anchor2(anchor2);
269 self
270 }
271
272 #[must_use]
274 pub fn motor_model(mut self, model: MotorModel) -> Self {
275 self.0.set_motor_model(model);
276 self
277 }
278
279 #[must_use]
281 pub fn motor_velocity(mut self, target_vel: Real, factor: Real) -> Self {
282 self.0.set_motor_velocity(target_vel, factor);
283 self
284 }
285
286 #[must_use]
288 pub fn motor_position(mut self, target_pos: Real, stiffness: Real, damping: Real) -> Self {
289 self.0.set_motor_position(target_pos, stiffness, damping);
290 self
291 }
292
293 #[must_use]
295 pub fn motor(
296 mut self,
297 target_pos: Real,
298 target_vel: Real,
299 stiffness: Real,
300 damping: Real,
301 ) -> Self {
302 self.0.set_motor(target_pos, target_vel, stiffness, damping);
303 self
304 }
305
306 #[must_use]
308 pub fn motor_max_force(mut self, max_force: Real) -> Self {
309 self.0.set_motor_max_force(max_force);
310 self
311 }
312
313 #[must_use]
315 pub fn limits(mut self, limits: [Real; 2]) -> Self {
316 self.0.set_limits(limits);
317 self
318 }
319
320 #[must_use]
322 pub fn softness(mut self, softness: SpringCoefficients<Real>) -> Self {
323 self.0.data.softness = softness;
324 self
325 }
326
327 #[must_use]
329 pub fn build(self) -> RevoluteJoint {
330 self.0
331 }
332}
333
334impl From<RevoluteJointBuilder> for GenericJoint {
335 fn from(val: RevoluteJointBuilder) -> GenericJoint {
336 val.0.into()
337 }
338}
339
340#[cfg(test)]
341mod test {
342 #[test]
343 fn test_revolute_joint_angle() {
344 use crate::math::{Real, Rotation};
345 use crate::na::RealField;
346 #[cfg(feature = "dim3")]
347 use crate::{math::Vector, na::vector};
348
349 #[cfg(feature = "dim2")]
350 let revolute = super::RevoluteJointBuilder::new().build();
351 #[cfg(feature = "dim2")]
352 let rot1 = Rotation::new(1.0);
353 #[cfg(feature = "dim3")]
354 let revolute = super::RevoluteJointBuilder::new(Vector::y_axis()).build();
355 #[cfg(feature = "dim3")]
356 let rot1 = Rotation::new(vector![0.0, 1.0, 0.0]);
357
358 let steps = 100;
359
360 for i in 1..steps {
362 let delta = -Real::pi() + i as Real * Real::two_pi() / steps as Real;
363 #[cfg(feature = "dim2")]
364 let rot2 = Rotation::new(1.0 + delta);
365 #[cfg(feature = "dim3")]
366 let rot2 = Rotation::new(vector![0.0, 1.0 + delta, 0.0]);
367 approx::assert_relative_eq!(revolute.angle(&rot1, &rot2), delta, epsilon = 1.0e-5);
368 }
369
370 for delta in [-Real::pi(), Real::pi()] {
373 #[cfg(feature = "dim2")]
374 let rot2 = Rotation::new(1.0 + delta);
375 #[cfg(feature = "dim3")]
376 let rot2 = Rotation::new(vector![0.0, 1.0 + delta, 0.0]);
377 approx::assert_relative_eq!(
378 revolute.angle(&rot1, &rot2).abs(),
379 delta.abs(),
380 epsilon = 1.0e-2
381 );
382 }
383 }
384}