1use crate::dynamics::joint::{GenericJoint, GenericJointBuilder, JointAxesMask};
2use crate::dynamics::{JointAxis, JointLimits, JointMotor, MotorModel};
3use crate::math::{Point, Real, Rotation};
4
5#[cfg(feature = "dim3")]
6use crate::math::UnitVector;
7
8#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
9#[derive(Copy, Clone, Debug, PartialEq)]
10#[repr(transparent)]
11pub struct RevoluteJoint {
13 pub data: GenericJoint,
15}
16
17impl RevoluteJoint {
18 #[cfg(feature = "dim2")]
20 #[allow(clippy::new_without_default)] pub fn new() -> Self {
22 let data = GenericJointBuilder::new(JointAxesMask::LOCKED_REVOLUTE_AXES);
23 Self { data: data.build() }
24 }
25
26 #[cfg(feature = "dim3")]
30 pub fn new(axis: UnitVector<Real>) -> Self {
31 let data = GenericJointBuilder::new(JointAxesMask::LOCKED_REVOLUTE_AXES)
32 .local_axis1(axis)
33 .local_axis2(axis)
34 .build();
35 Self { data }
36 }
37
38 pub fn data(&self) -> &GenericJoint {
40 &self.data
41 }
42
43 pub fn contacts_enabled(&self) -> bool {
45 self.data.contacts_enabled
46 }
47
48 pub fn set_contacts_enabled(&mut self, enabled: bool) -> &mut Self {
50 self.data.set_contacts_enabled(enabled);
51 self
52 }
53
54 #[must_use]
56 pub fn local_anchor1(&self) -> Point<Real> {
57 self.data.local_anchor1()
58 }
59
60 pub fn set_local_anchor1(&mut self, anchor1: Point<Real>) -> &mut Self {
62 self.data.set_local_anchor1(anchor1);
63 self
64 }
65
66 #[must_use]
68 pub fn local_anchor2(&self) -> Point<Real> {
69 self.data.local_anchor2()
70 }
71
72 pub fn set_local_anchor2(&mut self, anchor2: Point<Real>) -> &mut Self {
74 self.data.set_local_anchor2(anchor2);
75 self
76 }
77
78 pub fn angle(&self, rb_rot1: &Rotation<Real>, rb_rot2: &Rotation<Real>) -> Real {
84 let joint_rot1 = rb_rot1 * self.data.local_frame1.rotation;
85 let joint_rot2 = rb_rot2 * self.data.local_frame2.rotation;
86 let ang_err = joint_rot1.inverse() * joint_rot2;
87
88 #[cfg(feature = "dim3")]
89 if joint_rot1.dot(&joint_rot2) < 0.0 {
90 -ang_err.i.clamp(-1.0, 1.0).asin() * 2.0
91 } else {
92 ang_err.i.clamp(-1.0, 1.0).asin() * 2.0
93 }
94
95 #[cfg(feature = "dim2")]
96 {
97 ang_err.angle()
98 }
99 }
100
101 #[must_use]
103 pub fn motor(&self) -> Option<&JointMotor> {
104 self.data.motor(JointAxis::AngX)
105 }
106
107 pub fn set_motor_model(&mut self, model: MotorModel) -> &mut Self {
109 self.data.set_motor_model(JointAxis::AngX, model);
110 self
111 }
112
113 pub fn set_motor_velocity(&mut self, target_vel: Real, factor: Real) -> &mut Self {
115 self.data
116 .set_motor_velocity(JointAxis::AngX, target_vel, factor);
117 self
118 }
119
120 pub fn set_motor_position(
122 &mut self,
123 target_pos: Real,
124 stiffness: Real,
125 damping: Real,
126 ) -> &mut Self {
127 self.data
128 .set_motor_position(JointAxis::AngX, target_pos, stiffness, damping);
129 self
130 }
131
132 pub fn set_motor(
134 &mut self,
135 target_pos: Real,
136 target_vel: Real,
137 stiffness: Real,
138 damping: Real,
139 ) -> &mut Self {
140 self.data
141 .set_motor(JointAxis::AngX, target_pos, target_vel, stiffness, damping);
142 self
143 }
144
145 pub fn set_motor_max_force(&mut self, max_force: Real) -> &mut Self {
147 self.data.set_motor_max_force(JointAxis::AngX, max_force);
148 self
149 }
150
151 #[must_use]
153 pub fn limits(&self) -> Option<&JointLimits<Real>> {
154 self.data.limits(JointAxis::AngX)
155 }
156
157 pub fn set_limits(&mut self, limits: [Real; 2]) -> &mut Self {
159 self.data.set_limits(JointAxis::AngX, limits);
160 self
161 }
162}
163
164impl From<RevoluteJoint> for GenericJoint {
165 fn from(val: RevoluteJoint) -> GenericJoint {
166 val.data
167 }
168}
169
170#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
174#[derive(Copy, Clone, Debug, PartialEq)]
175pub struct RevoluteJointBuilder(pub RevoluteJoint);
176
177impl RevoluteJointBuilder {
178 #[cfg(feature = "dim2")]
180 #[allow(clippy::new_without_default)] pub fn new() -> Self {
182 Self(RevoluteJoint::new())
183 }
184
185 #[cfg(feature = "dim3")]
189 pub fn new(axis: UnitVector<Real>) -> Self {
190 Self(RevoluteJoint::new(axis))
191 }
192
193 #[must_use]
195 pub fn contacts_enabled(mut self, enabled: bool) -> Self {
196 self.0.set_contacts_enabled(enabled);
197 self
198 }
199
200 #[must_use]
202 pub fn local_anchor1(mut self, anchor1: Point<Real>) -> Self {
203 self.0.set_local_anchor1(anchor1);
204 self
205 }
206
207 #[must_use]
209 pub fn local_anchor2(mut self, anchor2: Point<Real>) -> Self {
210 self.0.set_local_anchor2(anchor2);
211 self
212 }
213
214 #[must_use]
216 pub fn motor_model(mut self, model: MotorModel) -> Self {
217 self.0.set_motor_model(model);
218 self
219 }
220
221 #[must_use]
223 pub fn motor_velocity(mut self, target_vel: Real, factor: Real) -> Self {
224 self.0.set_motor_velocity(target_vel, factor);
225 self
226 }
227
228 #[must_use]
230 pub fn motor_position(mut self, target_pos: Real, stiffness: Real, damping: Real) -> Self {
231 self.0.set_motor_position(target_pos, stiffness, damping);
232 self
233 }
234
235 #[must_use]
237 pub fn motor(
238 mut self,
239 target_pos: Real,
240 target_vel: Real,
241 stiffness: Real,
242 damping: Real,
243 ) -> Self {
244 self.0.set_motor(target_pos, target_vel, stiffness, damping);
245 self
246 }
247
248 #[must_use]
250 pub fn motor_max_force(mut self, max_force: Real) -> Self {
251 self.0.set_motor_max_force(max_force);
252 self
253 }
254
255 #[must_use]
257 pub fn limits(mut self, limits: [Real; 2]) -> Self {
258 self.0.set_limits(limits);
259 self
260 }
261
262 #[must_use]
264 pub fn build(self) -> RevoluteJoint {
265 self.0
266 }
267}
268
269impl From<RevoluteJointBuilder> for GenericJoint {
270 fn from(val: RevoluteJointBuilder) -> GenericJoint {
271 val.0.into()
272 }
273}
274
275#[cfg(test)]
276mod test {
277 #[test]
278 fn test_revolute_joint_angle() {
279 use crate::math::{Real, Rotation};
280 use crate::na::RealField;
281 #[cfg(feature = "dim3")]
282 use crate::{math::Vector, na::vector};
283
284 #[cfg(feature = "dim2")]
285 let revolute = super::RevoluteJointBuilder::new().build();
286 #[cfg(feature = "dim2")]
287 let rot1 = Rotation::new(1.0);
288 #[cfg(feature = "dim3")]
289 let revolute = super::RevoluteJointBuilder::new(Vector::y_axis()).build();
290 #[cfg(feature = "dim3")]
291 let rot1 = Rotation::new(vector![0.0, 1.0, 0.0]);
292
293 let steps = 100;
294
295 for i in 1..steps {
297 let delta = -Real::pi() + i as Real * Real::two_pi() / steps as Real;
298 #[cfg(feature = "dim2")]
299 let rot2 = Rotation::new(1.0 + delta);
300 #[cfg(feature = "dim3")]
301 let rot2 = Rotation::new(vector![0.0, 1.0 + delta, 0.0]);
302 approx::assert_relative_eq!(revolute.angle(&rot1, &rot2), delta, epsilon = 1.0e-5);
303 }
304
305 for delta in [-Real::pi(), Real::pi()] {
308 #[cfg(feature = "dim2")]
309 let rot2 = Rotation::new(1.0 + delta);
310 #[cfg(feature = "dim3")]
311 let rot2 = Rotation::new(vector![0.0, 1.0 + delta, 0.0]);
312 approx::assert_relative_eq!(
313 revolute.angle(&rot1, &rot2).abs(),
314 delta.abs(),
315 epsilon = 1.0e-2
316 );
317 }
318 }
319}