nalgebra/geometry/dual_quaternion.rs
1// Needed otherwise the rkyv macros generate code incompatible with rust-2024
2#![cfg_attr(feature = "rkyv-serialize", allow(unsafe_op_in_unsafe_fn))]
3// The macros break if the references are taken out, for some reason.
4#![allow(clippy::op_ref)]
5
6#[cfg(feature = "rkyv-serialize")]
7use rkyv::bytecheck;
8
9use crate::{
10 Isometry3, Matrix4, Normed, OVector, Point3, Quaternion, Scalar, SimdRealField, Translation3,
11 U8, Unit, UnitQuaternion, Vector3, Zero,
12};
13use approx::{AbsDiffEq, RelativeEq, UlpsEq};
14#[cfg(feature = "serde-serialize-no-std")]
15use serde::{Deserialize, Deserializer, Serialize, Serializer};
16use std::fmt;
17
18use simba::scalar::{ClosedNeg, RealField};
19
20/// A dual quaternion.
21///
22/// # Indexing
23///
24/// `DualQuaternions` are stored as \[..real, ..dual\].
25/// Both of the quaternion components are laid out in `i, j, k, w` order.
26///
27/// # Example
28/// ```
29/// # use nalgebra::{DualQuaternion, Quaternion};
30///
31/// let real = Quaternion::new(1.0, 2.0, 3.0, 4.0);
32/// let dual = Quaternion::new(5.0, 6.0, 7.0, 8.0);
33///
34/// let dq = DualQuaternion::from_real_and_dual(real, dual);
35/// assert_eq!(dq[0], 2.0);
36/// assert_eq!(dq[1], 3.0);
37///
38/// assert_eq!(dq[4], 6.0);
39/// assert_eq!(dq[7], 5.0);
40/// ```
41///
42/// NOTE:
43/// As of December 2020, dual quaternion support is a work in progress.
44/// If a feature that you need is missing, feel free to open an issue or a PR.
45/// See <https://github.com/dimforge/nalgebra/issues/487>
46#[repr(C)]
47#[derive(Debug, Copy, Clone)]
48#[cfg_attr(
49 feature = "rkyv-serialize-no-std",
50 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize),
51 archive(
52 as = "DualQuaternion<T::Archived>",
53 bound(archive = "
54 T: rkyv::Archive,
55 Quaternion<T>: rkyv::Archive<Archived = Quaternion<T::Archived>>
56 ")
57 )
58)]
59#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))]
60#[cfg_attr(feature = "defmt", derive(defmt::Format))]
61pub struct DualQuaternion<T> {
62 /// The real component of the quaternion
63 pub real: Quaternion<T>,
64 /// The dual component of the quaternion
65 pub dual: Quaternion<T>,
66}
67
68impl<T: Scalar + Eq> Eq for DualQuaternion<T> {}
69
70impl<T: Scalar> PartialEq for DualQuaternion<T> {
71 #[inline]
72 fn eq(&self, right: &Self) -> bool {
73 self.real == right.real && self.dual == right.dual
74 }
75}
76
77impl<T: Scalar + Zero> Default for DualQuaternion<T> {
78 fn default() -> Self {
79 Self {
80 real: Quaternion::default(),
81 dual: Quaternion::default(),
82 }
83 }
84}
85
86impl<T: SimdRealField> DualQuaternion<T>
87where
88 T::Element: SimdRealField,
89{
90 /// Normalizes this quaternion.
91 ///
92 /// # Example
93 /// ```
94 /// # #[macro_use] extern crate approx;
95 /// # use nalgebra::{DualQuaternion, Quaternion};
96 /// let real = Quaternion::new(1.0, 2.0, 3.0, 4.0);
97 /// let dual = Quaternion::new(5.0, 6.0, 7.0, 8.0);
98 /// let dq = DualQuaternion::from_real_and_dual(real, dual);
99 ///
100 /// let dq_normalized = dq.normalize();
101 ///
102 /// assert_relative_eq!(dq_normalized.real.norm(), 1.0);
103 /// ```
104 #[inline]
105 #[must_use = "Did you mean to use normalize_mut()?"]
106 pub fn normalize(&self) -> Self {
107 let real_norm = self.real.norm();
108
109 Self::from_real_and_dual(
110 self.real.clone() / real_norm.clone(),
111 self.dual.clone() / real_norm,
112 )
113 }
114
115 /// Normalizes this quaternion.
116 ///
117 /// # Example
118 /// ```
119 /// # #[macro_use] extern crate approx;
120 /// # use nalgebra::{DualQuaternion, Quaternion};
121 /// let real = Quaternion::new(1.0, 2.0, 3.0, 4.0);
122 /// let dual = Quaternion::new(5.0, 6.0, 7.0, 8.0);
123 /// let mut dq = DualQuaternion::from_real_and_dual(real, dual);
124 ///
125 /// dq.normalize_mut();
126 ///
127 /// assert_relative_eq!(dq.real.norm(), 1.0);
128 /// ```
129 #[inline]
130 pub fn normalize_mut(&mut self) -> T {
131 let real_norm = self.real.norm();
132 self.real /= real_norm.clone();
133 self.dual /= real_norm.clone();
134 real_norm
135 }
136
137 /// The conjugate of this dual quaternion, containing the conjugate of
138 /// the real and imaginary parts..
139 ///
140 /// # Example
141 /// ```
142 /// # use nalgebra::{DualQuaternion, Quaternion};
143 /// let real = Quaternion::new(1.0, 2.0, 3.0, 4.0);
144 /// let dual = Quaternion::new(5.0, 6.0, 7.0, 8.0);
145 /// let dq = DualQuaternion::from_real_and_dual(real, dual);
146 ///
147 /// let conj = dq.conjugate();
148 /// assert!(conj.real.i == -2.0 && conj.real.j == -3.0 && conj.real.k == -4.0);
149 /// assert!(conj.real.w == 1.0);
150 /// assert!(conj.dual.i == -6.0 && conj.dual.j == -7.0 && conj.dual.k == -8.0);
151 /// assert!(conj.dual.w == 5.0);
152 /// ```
153 #[inline]
154 #[must_use = "Did you mean to use conjugate_mut()?"]
155 pub fn conjugate(&self) -> Self {
156 Self::from_real_and_dual(self.real.conjugate(), self.dual.conjugate())
157 }
158
159 /// Replaces this quaternion by its conjugate.
160 ///
161 /// # Example
162 /// ```
163 /// # use nalgebra::{DualQuaternion, Quaternion};
164 /// let real = Quaternion::new(1.0, 2.0, 3.0, 4.0);
165 /// let dual = Quaternion::new(5.0, 6.0, 7.0, 8.0);
166 /// let mut dq = DualQuaternion::from_real_and_dual(real, dual);
167 ///
168 /// dq.conjugate_mut();
169 /// assert!(dq.real.i == -2.0 && dq.real.j == -3.0 && dq.real.k == -4.0);
170 /// assert!(dq.real.w == 1.0);
171 /// assert!(dq.dual.i == -6.0 && dq.dual.j == -7.0 && dq.dual.k == -8.0);
172 /// assert!(dq.dual.w == 5.0);
173 /// ```
174 #[inline]
175 pub fn conjugate_mut(&mut self) {
176 self.real.conjugate_mut();
177 self.dual.conjugate_mut();
178 }
179
180 /// Inverts this dual quaternion if it is not zero.
181 ///
182 /// # Example
183 /// ```
184 /// # #[macro_use] extern crate approx;
185 /// # use nalgebra::{DualQuaternion, Quaternion};
186 /// let real = Quaternion::new(1.0, 2.0, 3.0, 4.0);
187 /// let dual = Quaternion::new(5.0, 6.0, 7.0, 8.0);
188 /// let dq = DualQuaternion::from_real_and_dual(real, dual);
189 /// let inverse = dq.try_inverse();
190 ///
191 /// assert!(inverse.is_some());
192 /// assert_relative_eq!(inverse.unwrap() * dq, DualQuaternion::identity());
193 ///
194 /// //Non-invertible case
195 /// let zero = Quaternion::new(0.0, 0.0, 0.0, 0.0);
196 /// let dq = DualQuaternion::from_real_and_dual(zero, zero);
197 /// let inverse = dq.try_inverse();
198 ///
199 /// assert!(inverse.is_none());
200 /// ```
201 #[inline]
202 #[must_use = "Did you mean to use try_inverse_mut()?"]
203 pub fn try_inverse(&self) -> Option<Self>
204 where
205 T: RealField,
206 {
207 let mut res = self.clone();
208 if res.try_inverse_mut() {
209 Some(res)
210 } else {
211 None
212 }
213 }
214
215 /// Inverts this dual quaternion in-place if it is not zero.
216 ///
217 /// # Example
218 /// ```
219 /// # #[macro_use] extern crate approx;
220 /// # use nalgebra::{DualQuaternion, Quaternion};
221 /// let real = Quaternion::new(1.0, 2.0, 3.0, 4.0);
222 /// let dual = Quaternion::new(5.0, 6.0, 7.0, 8.0);
223 /// let dq = DualQuaternion::from_real_and_dual(real, dual);
224 /// let mut dq_inverse = dq;
225 /// dq_inverse.try_inverse_mut();
226 ///
227 /// assert_relative_eq!(dq_inverse * dq, DualQuaternion::identity());
228 ///
229 /// //Non-invertible case
230 /// let zero = Quaternion::new(0.0, 0.0, 0.0, 0.0);
231 /// let mut dq = DualQuaternion::from_real_and_dual(zero, zero);
232 /// assert!(!dq.try_inverse_mut());
233 /// ```
234 #[inline]
235 pub fn try_inverse_mut(&mut self) -> bool
236 where
237 T: RealField,
238 {
239 let inverted = self.real.try_inverse_mut();
240 if inverted {
241 self.dual = -self.real.clone() * self.dual.clone() * self.real.clone();
242 true
243 } else {
244 false
245 }
246 }
247
248 /// Linear interpolation between two dual quaternions.
249 ///
250 /// Computes `self * (1 - t) + other * t`.
251 ///
252 /// # Example
253 /// ```
254 /// # use nalgebra::{DualQuaternion, Quaternion};
255 /// let dq1 = DualQuaternion::from_real_and_dual(
256 /// Quaternion::new(1.0, 0.0, 0.0, 4.0),
257 /// Quaternion::new(0.0, 2.0, 0.0, 0.0)
258 /// );
259 /// let dq2 = DualQuaternion::from_real_and_dual(
260 /// Quaternion::new(2.0, 0.0, 1.0, 0.0),
261 /// Quaternion::new(0.0, 2.0, 0.0, 0.0)
262 /// );
263 /// assert_eq!(dq1.lerp(&dq2, 0.25), DualQuaternion::from_real_and_dual(
264 /// Quaternion::new(1.25, 0.0, 0.25, 3.0),
265 /// Quaternion::new(0.0, 2.0, 0.0, 0.0)
266 /// ));
267 /// ```
268 #[inline]
269 #[must_use]
270 pub fn lerp(&self, other: &Self, t: T) -> Self {
271 self * (T::one() - t.clone()) + other * t
272 }
273}
274
275#[cfg(feature = "bytemuck")]
276unsafe impl<T> bytemuck::Zeroable for DualQuaternion<T>
277where
278 T: Scalar + bytemuck::Zeroable,
279 Quaternion<T>: bytemuck::Zeroable,
280{
281}
282
283#[cfg(feature = "bytemuck")]
284unsafe impl<T> bytemuck::Pod for DualQuaternion<T>
285where
286 T: Scalar + bytemuck::Pod,
287 Quaternion<T>: bytemuck::Pod,
288{
289}
290
291#[cfg(feature = "serde-serialize-no-std")]
292impl<T: SimdRealField> Serialize for DualQuaternion<T>
293where
294 T: Serialize,
295{
296 fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
297 where
298 S: Serializer,
299 {
300 self.as_ref().serialize(serializer)
301 }
302}
303
304#[cfg(feature = "serde-serialize-no-std")]
305impl<'a, T: SimdRealField> Deserialize<'a> for DualQuaternion<T>
306where
307 T: Deserialize<'a>,
308{
309 fn deserialize<Des>(deserializer: Des) -> Result<Self, Des::Error>
310 where
311 Des: Deserializer<'a>,
312 {
313 type Dq<T> = [T; 8];
314
315 let dq: Dq<T> = Dq::<T>::deserialize(deserializer)?;
316
317 Ok(Self {
318 real: Quaternion::new(dq[3].clone(), dq[0].clone(), dq[1].clone(), dq[2].clone()),
319 dual: Quaternion::new(dq[7].clone(), dq[4].clone(), dq[5].clone(), dq[6].clone()),
320 })
321 }
322}
323
324impl<T: RealField> DualQuaternion<T> {
325 #[allow(clippy::wrong_self_convention)]
326 fn to_vector(&self) -> OVector<T, U8> {
327 self.as_ref().clone().into()
328 }
329}
330
331impl<T: RealField + AbsDiffEq<Epsilon = T>> AbsDiffEq for DualQuaternion<T> {
332 type Epsilon = T;
333
334 #[inline]
335 fn default_epsilon() -> Self::Epsilon {
336 T::default_epsilon()
337 }
338
339 #[inline]
340 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
341 self.to_vector().abs_diff_eq(&other.to_vector(), epsilon.clone()) ||
342 // Account for the double-covering of S², i.e. q = -q
343 self.to_vector().iter().zip(other.to_vector().iter()).all(|(a, b)| a.abs_diff_eq(&-b.clone(), epsilon.clone()))
344 }
345}
346
347impl<T: RealField + RelativeEq<Epsilon = T>> RelativeEq for DualQuaternion<T> {
348 #[inline]
349 fn default_max_relative() -> Self::Epsilon {
350 T::default_max_relative()
351 }
352
353 #[inline]
354 fn relative_eq(
355 &self,
356 other: &Self,
357 epsilon: Self::Epsilon,
358 max_relative: Self::Epsilon,
359 ) -> bool {
360 self.to_vector().relative_eq(&other.to_vector(), epsilon.clone(), max_relative.clone()) ||
361 // Account for the double-covering of S², i.e. q = -q
362 self.to_vector().iter().zip(other.to_vector().iter()).all(|(a, b)| a.relative_eq(&-b.clone(), epsilon.clone(), max_relative.clone()))
363 }
364}
365
366impl<T: RealField + UlpsEq<Epsilon = T>> UlpsEq for DualQuaternion<T> {
367 #[inline]
368 fn default_max_ulps() -> u32 {
369 T::default_max_ulps()
370 }
371
372 #[inline]
373 fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
374 self.to_vector().ulps_eq(&other.to_vector(), epsilon.clone(), max_ulps) ||
375 // Account for the double-covering of S², i.e. q = -q.
376 self.to_vector().iter().zip(other.to_vector().iter()).all(|(a, b)| a.ulps_eq(&-b.clone(), epsilon.clone(), max_ulps))
377 }
378}
379
380/// A unit dual quaternion. May be used to represent a rotation followed by a
381/// translation.
382pub type UnitDualQuaternion<T> = Unit<DualQuaternion<T>>;
383
384impl<T: Scalar + ClosedNeg + PartialEq + SimdRealField> PartialEq for UnitDualQuaternion<T> {
385 #[inline]
386 fn eq(&self, rhs: &Self) -> bool {
387 self.as_ref().eq(rhs.as_ref())
388 }
389}
390
391impl<T: Scalar + ClosedNeg + Eq + SimdRealField> Eq for UnitDualQuaternion<T> {}
392
393impl<T: SimdRealField> Normed for DualQuaternion<T> {
394 type Norm = T::SimdRealField;
395
396 #[inline]
397 fn norm(&self) -> T::SimdRealField {
398 self.real.norm()
399 }
400
401 #[inline]
402 fn norm_squared(&self) -> T::SimdRealField {
403 self.real.norm_squared()
404 }
405
406 #[inline]
407 fn scale_mut(&mut self, n: Self::Norm) {
408 self.real.scale_mut(n.clone());
409 self.dual.scale_mut(n);
410 }
411
412 #[inline]
413 fn unscale_mut(&mut self, n: Self::Norm) {
414 self.real.unscale_mut(n.clone());
415 self.dual.unscale_mut(n);
416 }
417}
418
419impl<T: SimdRealField> UnitDualQuaternion<T>
420where
421 T::Element: SimdRealField,
422{
423 /// The underlying dual quaternion.
424 ///
425 /// Same as `self.as_ref()`.
426 ///
427 /// # Example
428 /// ```
429 /// # use nalgebra::{DualQuaternion, UnitDualQuaternion, Quaternion};
430 /// let id = UnitDualQuaternion::identity();
431 /// assert_eq!(*id.dual_quaternion(), DualQuaternion::from_real_and_dual(
432 /// Quaternion::new(1.0, 0.0, 0.0, 0.0),
433 /// Quaternion::new(0.0, 0.0, 0.0, 0.0)
434 /// ));
435 /// ```
436 #[inline]
437 #[must_use]
438 pub fn dual_quaternion(&self) -> &DualQuaternion<T> {
439 self.as_ref()
440 }
441
442 /// Compute the conjugate of this unit quaternion.
443 ///
444 /// # Example
445 /// ```
446 /// # use nalgebra::{UnitDualQuaternion, DualQuaternion, Quaternion};
447 /// let qr = Quaternion::new(1.0, 2.0, 3.0, 4.0);
448 /// let qd = Quaternion::new(5.0, 6.0, 7.0, 8.0);
449 /// let unit = UnitDualQuaternion::new_normalize(
450 /// DualQuaternion::from_real_and_dual(qr, qd)
451 /// );
452 /// let conj = unit.conjugate();
453 /// assert_eq!(conj.real, unit.real.conjugate());
454 /// assert_eq!(conj.dual, unit.dual.conjugate());
455 /// ```
456 #[inline]
457 #[must_use = "Did you mean to use conjugate_mut()?"]
458 pub fn conjugate(&self) -> Self {
459 Self::new_unchecked(self.as_ref().conjugate())
460 }
461
462 /// Compute the conjugate of this unit quaternion in-place.
463 ///
464 /// # Example
465 /// ```
466 /// # use nalgebra::{UnitDualQuaternion, DualQuaternion, Quaternion};
467 /// let qr = Quaternion::new(1.0, 2.0, 3.0, 4.0);
468 /// let qd = Quaternion::new(5.0, 6.0, 7.0, 8.0);
469 /// let unit = UnitDualQuaternion::new_normalize(
470 /// DualQuaternion::from_real_and_dual(qr, qd)
471 /// );
472 /// let mut conj = unit.clone();
473 /// conj.conjugate_mut();
474 /// assert_eq!(conj.as_ref().real, unit.as_ref().real.conjugate());
475 /// assert_eq!(conj.as_ref().dual, unit.as_ref().dual.conjugate());
476 /// ```
477 #[inline]
478 pub fn conjugate_mut(&mut self) {
479 self.as_mut_unchecked().conjugate_mut()
480 }
481
482 /// Inverts this dual quaternion if it is not zero.
483 ///
484 /// # Example
485 /// ```
486 /// # #[macro_use] extern crate approx;
487 /// # use nalgebra::{UnitDualQuaternion, Quaternion, DualQuaternion};
488 /// let qr = Quaternion::new(1.0, 2.0, 3.0, 4.0);
489 /// let qd = Quaternion::new(5.0, 6.0, 7.0, 8.0);
490 /// let unit = UnitDualQuaternion::new_normalize(DualQuaternion::from_real_and_dual(qr, qd));
491 /// let inv = unit.inverse();
492 /// assert_relative_eq!(unit * inv, UnitDualQuaternion::identity(), epsilon = 1.0e-6);
493 /// assert_relative_eq!(inv * unit, UnitDualQuaternion::identity(), epsilon = 1.0e-6);
494 /// ```
495 #[inline]
496 #[must_use = "Did you mean to use inverse_mut()?"]
497 pub fn inverse(&self) -> Self {
498 let real = Unit::new_unchecked(self.as_ref().real.clone())
499 .inverse()
500 .into_inner();
501 let dual = -real.clone() * self.as_ref().dual.clone() * real.clone();
502 UnitDualQuaternion::new_unchecked(DualQuaternion { real, dual })
503 }
504
505 /// Inverts this dual quaternion in place if it is not zero.
506 ///
507 /// # Example
508 /// ```
509 /// # #[macro_use] extern crate approx;
510 /// # use nalgebra::{UnitDualQuaternion, Quaternion, DualQuaternion};
511 /// let qr = Quaternion::new(1.0, 2.0, 3.0, 4.0);
512 /// let qd = Quaternion::new(5.0, 6.0, 7.0, 8.0);
513 /// let unit = UnitDualQuaternion::new_normalize(DualQuaternion::from_real_and_dual(qr, qd));
514 /// let mut inv = unit.clone();
515 /// inv.inverse_mut();
516 /// assert_relative_eq!(unit * inv, UnitDualQuaternion::identity(), epsilon = 1.0e-6);
517 /// assert_relative_eq!(inv * unit, UnitDualQuaternion::identity(), epsilon = 1.0e-6);
518 /// ```
519 #[inline]
520 pub fn inverse_mut(&mut self) {
521 let quat = self.as_mut_unchecked();
522 quat.real = Unit::new_unchecked(quat.real.clone())
523 .inverse()
524 .into_inner();
525 quat.dual = -quat.real.clone() * quat.dual.clone() * quat.real.clone();
526 }
527
528 /// The unit dual quaternion needed to make `self` and `other` coincide.
529 ///
530 /// The result is such that: `self.isometry_to(other) * self == other`.
531 ///
532 /// # Example
533 /// ```
534 /// # #[macro_use] extern crate approx;
535 /// # use nalgebra::{UnitDualQuaternion, DualQuaternion, Quaternion};
536 /// let qr = Quaternion::new(1.0, 2.0, 3.0, 4.0);
537 /// let qd = Quaternion::new(5.0, 6.0, 7.0, 8.0);
538 /// let dq1 = UnitDualQuaternion::new_normalize(DualQuaternion::from_real_and_dual(qr, qd));
539 /// let dq2 = UnitDualQuaternion::new_normalize(DualQuaternion::from_real_and_dual(qd, qr));
540 /// let dq_to = dq1.isometry_to(&dq2);
541 /// assert_relative_eq!(dq_to * dq1, dq2, epsilon = 1.0e-6);
542 /// ```
543 #[inline]
544 #[must_use]
545 pub fn isometry_to(&self, other: &Self) -> Self {
546 other / self
547 }
548
549 /// Linear interpolation between two unit dual quaternions.
550 ///
551 /// The result is not normalized.
552 ///
553 /// # Example
554 /// ```
555 /// # #[macro_use] extern crate approx;
556 /// # use nalgebra::{UnitDualQuaternion, DualQuaternion, Quaternion};
557 /// let dq1 = UnitDualQuaternion::new_normalize(DualQuaternion::from_real_and_dual(
558 /// Quaternion::new(0.5, 0.0, 0.5, 0.0),
559 /// Quaternion::new(0.0, 0.5, 0.0, 0.5)
560 /// ));
561 /// let dq2 = UnitDualQuaternion::new_normalize(DualQuaternion::from_real_and_dual(
562 /// Quaternion::new(0.5, 0.0, 0.0, 0.5),
563 /// Quaternion::new(0.5, 0.0, 0.5, 0.0)
564 /// ));
565 /// assert_relative_eq!(
566 /// UnitDualQuaternion::new_normalize(dq1.lerp(&dq2, 0.5)),
567 /// UnitDualQuaternion::new_normalize(
568 /// DualQuaternion::from_real_and_dual(
569 /// Quaternion::new(0.5, 0.0, 0.25, 0.25),
570 /// Quaternion::new(0.25, 0.25, 0.25, 0.25)
571 /// )
572 /// ),
573 /// epsilon = 1.0e-6
574 /// );
575 /// ```
576 #[inline]
577 #[must_use]
578 pub fn lerp(&self, other: &Self, t: T) -> DualQuaternion<T> {
579 self.as_ref().lerp(other.as_ref(), t)
580 }
581
582 /// Normalized linear interpolation between two unit quaternions.
583 ///
584 /// This is the same as `self.lerp` except that the result is normalized.
585 ///
586 /// # Example
587 /// ```
588 /// # #[macro_use] extern crate approx;
589 /// # use nalgebra::{UnitDualQuaternion, DualQuaternion, Quaternion};
590 /// let dq1 = UnitDualQuaternion::new_normalize(DualQuaternion::from_real_and_dual(
591 /// Quaternion::new(0.5, 0.0, 0.5, 0.0),
592 /// Quaternion::new(0.0, 0.5, 0.0, 0.5)
593 /// ));
594 /// let dq2 = UnitDualQuaternion::new_normalize(DualQuaternion::from_real_and_dual(
595 /// Quaternion::new(0.5, 0.0, 0.0, 0.5),
596 /// Quaternion::new(0.5, 0.0, 0.5, 0.0)
597 /// ));
598 /// assert_relative_eq!(dq1.nlerp(&dq2, 0.2), UnitDualQuaternion::new_normalize(
599 /// DualQuaternion::from_real_and_dual(
600 /// Quaternion::new(0.5, 0.0, 0.4, 0.1),
601 /// Quaternion::new(0.1, 0.4, 0.1, 0.4)
602 /// )
603 /// ), epsilon = 1.0e-6);
604 /// ```
605 #[inline]
606 #[must_use]
607 pub fn nlerp(&self, other: &Self, t: T) -> Self {
608 let mut res = self.lerp(other, t);
609 let _ = res.normalize_mut();
610
611 Self::new_unchecked(res)
612 }
613
614 /// Screw linear interpolation between two unit quaternions. This creates a
615 /// smooth arc from one dual-quaternion to another.
616 ///
617 /// Panics if the angle between both quaternion is 180 degrees (in which
618 /// case the interpolation is not well-defined). Use `.try_sclerp`
619 /// instead to avoid the panic.
620 ///
621 /// # Example
622 /// ```
623 /// # #[macro_use] extern crate approx;
624 /// # use nalgebra::{UnitDualQuaternion, DualQuaternion, UnitQuaternion, Vector3};
625 ///
626 /// let dq1 = UnitDualQuaternion::from_parts(
627 /// Vector3::new(0.0, 3.0, 0.0).into(),
628 /// UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0),
629 /// );
630 ///
631 /// let dq2 = UnitDualQuaternion::from_parts(
632 /// Vector3::new(0.0, 0.0, 3.0).into(),
633 /// UnitQuaternion::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0),
634 /// );
635 ///
636 /// let dq = dq1.sclerp(&dq2, 1.0 / 3.0);
637 ///
638 /// assert_relative_eq!(
639 /// dq.rotation().euler_angles().0, std::f32::consts::FRAC_PI_2, epsilon = 1.0e-6
640 /// );
641 /// assert_relative_eq!(dq.translation().vector.y, 3.0, epsilon = 1.0e-6);
642 /// ```
643 #[inline]
644 #[must_use]
645 pub fn sclerp(&self, other: &Self, t: T) -> Self
646 where
647 T: RealField,
648 {
649 self.try_sclerp(other, t, T::default_epsilon())
650 .expect("DualQuaternion sclerp: ambiguous configuration.")
651 }
652
653 /// Computes the screw-linear interpolation between two unit quaternions or
654 /// returns `None` if both quaternions are approximately 180 degrees
655 /// apart (in which case the interpolation is not well-defined).
656 ///
657 /// # Arguments
658 /// * `self`: the first quaternion to interpolate from.
659 /// * `other`: the second quaternion to interpolate toward.
660 /// * `t`: the interpolation parameter. Should be between 0 and 1.
661 /// * `epsilon`: the value below which the sinus of the angle separating
662 /// both quaternion must be to return `None`.
663 #[inline]
664 #[must_use]
665 pub fn try_sclerp(&self, other: &Self, t: T, epsilon: T) -> Option<Self>
666 where
667 T: RealField,
668 {
669 let two = T::one() + T::one();
670 let half = T::one() / two.clone();
671
672 // Invert one of the quaternions if we've got a longest-path
673 // interpolation.
674 let other = {
675 let dot_product = self.as_ref().real.coords.dot(&other.as_ref().real.coords);
676 if relative_eq!(dot_product, T::zero(), epsilon = epsilon.clone()) {
677 return None;
678 }
679
680 if dot_product < T::zero() {
681 -other.clone()
682 } else {
683 other.clone()
684 }
685 };
686
687 let difference = self.as_ref().conjugate() * other.as_ref();
688 let norm_squared = difference.real.vector().norm_squared();
689 if relative_eq!(norm_squared, T::zero(), epsilon = epsilon) {
690 return Some(Self::from_parts(
691 self.translation()
692 .vector
693 .lerp(&other.translation().vector, t)
694 .into(),
695 self.rotation(),
696 ));
697 }
698
699 let scalar: T = difference.real.scalar();
700 let mut angle = two.clone() * scalar.acos();
701
702 let inverse_norm_squared: T = T::one() / norm_squared;
703 let inverse_norm = inverse_norm_squared.sqrt();
704
705 let mut pitch = -two * difference.dual.scalar() * inverse_norm.clone();
706 let direction = difference.real.vector() * inverse_norm.clone();
707 let moment = (difference.dual.vector()
708 - direction.clone() * (pitch.clone() * difference.real.scalar() * half.clone()))
709 * inverse_norm;
710
711 angle *= t.clone();
712 pitch *= t;
713
714 let sin = (half.clone() * angle.clone()).sin();
715 let cos = (half.clone() * angle).cos();
716
717 let real = Quaternion::from_parts(cos.clone(), direction.clone() * sin.clone());
718 let dual = Quaternion::from_parts(
719 -pitch.clone() * half.clone() * sin.clone(),
720 moment * sin + direction * (pitch * half * cos),
721 );
722
723 Some(
724 self * UnitDualQuaternion::new_unchecked(DualQuaternion::from_real_and_dual(
725 real, dual,
726 )),
727 )
728 }
729
730 /// Return the rotation part of this unit dual quaternion.
731 ///
732 /// # Example
733 /// ```
734 /// # #[macro_use] extern crate approx;
735 /// # use nalgebra::{UnitDualQuaternion, UnitQuaternion, Vector3};
736 /// let dq = UnitDualQuaternion::from_parts(
737 /// Vector3::new(0.0, 3.0, 0.0).into(),
738 /// UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0)
739 /// );
740 ///
741 /// assert_relative_eq!(
742 /// dq.rotation().angle(), std::f32::consts::FRAC_PI_4, epsilon = 1.0e-6
743 /// );
744 /// ```
745 #[inline]
746 #[must_use]
747 pub fn rotation(&self) -> UnitQuaternion<T> {
748 Unit::new_unchecked(self.as_ref().real.clone())
749 }
750
751 /// Return the translation part of this unit dual quaternion.
752 ///
753 /// # Example
754 /// ```
755 /// # #[macro_use] extern crate approx;
756 /// # use nalgebra::{UnitDualQuaternion, UnitQuaternion, Vector3};
757 /// let dq = UnitDualQuaternion::from_parts(
758 /// Vector3::new(0.0, 3.0, 0.0).into(),
759 /// UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0)
760 /// );
761 ///
762 /// assert_relative_eq!(
763 /// dq.translation().vector, Vector3::new(0.0, 3.0, 0.0), epsilon = 1.0e-6
764 /// );
765 /// ```
766 #[inline]
767 #[must_use]
768 pub fn translation(&self) -> Translation3<T> {
769 let two = T::one() + T::one();
770 Translation3::from(
771 ((self.as_ref().dual.clone() * self.as_ref().real.clone().conjugate()) * two)
772 .vector()
773 .into_owned(),
774 )
775 }
776
777 /// Builds an isometry from this unit dual quaternion.
778 ///
779 /// # Example
780 /// ```
781 /// # #[macro_use] extern crate approx;
782 /// # use nalgebra::{UnitDualQuaternion, UnitQuaternion, Vector3};
783 /// let rotation = UnitQuaternion::from_euler_angles(std::f32::consts::PI, 0.0, 0.0);
784 /// let translation = Vector3::new(1.0, 3.0, 2.5);
785 /// let dq = UnitDualQuaternion::from_parts(
786 /// translation.into(),
787 /// rotation
788 /// );
789 /// let iso = dq.to_isometry();
790 ///
791 /// assert_relative_eq!(iso.rotation.angle(), std::f32::consts::PI, epsilon = 1.0e-6);
792 /// assert_relative_eq!(iso.translation.vector, translation, epsilon = 1.0e-6);
793 /// ```
794 #[inline]
795 #[must_use]
796 pub fn to_isometry(self) -> Isometry3<T> {
797 Isometry3::from_parts(self.translation(), self.rotation())
798 }
799
800 /// Rotate and translate a point by this unit dual quaternion interpreted
801 /// as an isometry.
802 ///
803 /// This is the same as the multiplication `self * pt`.
804 ///
805 /// # Example
806 /// ```
807 /// # #[macro_use] extern crate approx;
808 /// # use nalgebra::{UnitDualQuaternion, UnitQuaternion, Vector3, Point3};
809 /// let dq = UnitDualQuaternion::from_parts(
810 /// Vector3::new(0.0, 3.0, 0.0).into(),
811 /// UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_2, 0.0, 0.0)
812 /// );
813 /// let point = Point3::new(1.0, 2.0, 3.0);
814 ///
815 /// assert_relative_eq!(
816 /// dq.transform_point(&point), Point3::new(1.0, 0.0, 2.0), epsilon = 1.0e-6
817 /// );
818 /// ```
819 #[inline]
820 #[must_use]
821 pub fn transform_point(&self, pt: &Point3<T>) -> Point3<T> {
822 self * pt
823 }
824
825 /// Rotate a vector by this unit dual quaternion, ignoring the translational
826 /// component.
827 ///
828 /// This is the same as the multiplication `self * v`.
829 ///
830 /// # Example
831 /// ```
832 /// # #[macro_use] extern crate approx;
833 /// # use nalgebra::{UnitDualQuaternion, UnitQuaternion, Vector3};
834 /// let dq = UnitDualQuaternion::from_parts(
835 /// Vector3::new(0.0, 3.0, 0.0).into(),
836 /// UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_2, 0.0, 0.0)
837 /// );
838 /// let vector = Vector3::new(1.0, 2.0, 3.0);
839 ///
840 /// assert_relative_eq!(
841 /// dq.transform_vector(&vector), Vector3::new(1.0, -3.0, 2.0), epsilon = 1.0e-6
842 /// );
843 /// ```
844 #[inline]
845 #[must_use]
846 pub fn transform_vector(&self, v: &Vector3<T>) -> Vector3<T> {
847 self * v
848 }
849
850 /// Rotate and translate a point by the inverse of this unit quaternion.
851 ///
852 /// This may be cheaper than inverting the unit dual quaternion and
853 /// transforming the point.
854 ///
855 /// # Example
856 /// ```
857 /// # #[macro_use] extern crate approx;
858 /// # use nalgebra::{UnitDualQuaternion, UnitQuaternion, Vector3, Point3};
859 /// let dq = UnitDualQuaternion::from_parts(
860 /// Vector3::new(0.0, 3.0, 0.0).into(),
861 /// UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_2, 0.0, 0.0)
862 /// );
863 /// let point = Point3::new(1.0, 2.0, 3.0);
864 ///
865 /// assert_relative_eq!(
866 /// dq.inverse_transform_point(&point), Point3::new(1.0, 3.0, 1.0), epsilon = 1.0e-6
867 /// );
868 /// ```
869 #[inline]
870 #[must_use]
871 pub fn inverse_transform_point(&self, pt: &Point3<T>) -> Point3<T> {
872 self.inverse() * pt
873 }
874
875 /// Rotate a vector by the inverse of this unit quaternion, ignoring the
876 /// translational component.
877 ///
878 /// This may be cheaper than inverting the unit dual quaternion and
879 /// transforming the vector.
880 ///
881 /// # Example
882 /// ```
883 /// # #[macro_use] extern crate approx;
884 /// # use nalgebra::{UnitDualQuaternion, UnitQuaternion, Vector3};
885 /// let dq = UnitDualQuaternion::from_parts(
886 /// Vector3::new(0.0, 3.0, 0.0).into(),
887 /// UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_2, 0.0, 0.0)
888 /// );
889 /// let vector = Vector3::new(1.0, 2.0, 3.0);
890 ///
891 /// assert_relative_eq!(
892 /// dq.inverse_transform_vector(&vector), Vector3::new(1.0, 3.0, -2.0), epsilon = 1.0e-6
893 /// );
894 /// ```
895 #[inline]
896 #[must_use]
897 pub fn inverse_transform_vector(&self, v: &Vector3<T>) -> Vector3<T> {
898 self.inverse() * v
899 }
900
901 /// Rotate a unit vector by the inverse of this unit quaternion, ignoring
902 /// the translational component. This may be
903 /// cheaper than inverting the unit dual quaternion and transforming the
904 /// vector.
905 ///
906 /// # Example
907 /// ```
908 /// # #[macro_use] extern crate approx;
909 /// # use nalgebra::{UnitDualQuaternion, UnitQuaternion, Unit, Vector3};
910 /// let dq = UnitDualQuaternion::from_parts(
911 /// Vector3::new(0.0, 3.0, 0.0).into(),
912 /// UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_2, 0.0, 0.0)
913 /// );
914 /// let vector = Unit::new_unchecked(Vector3::new(0.0, 1.0, 0.0));
915 ///
916 /// assert_relative_eq!(
917 /// dq.inverse_transform_unit_vector(&vector),
918 /// Unit::new_unchecked(Vector3::new(0.0, 0.0, -1.0)),
919 /// epsilon = 1.0e-6
920 /// );
921 /// ```
922 #[inline]
923 #[must_use]
924 pub fn inverse_transform_unit_vector(&self, v: &Unit<Vector3<T>>) -> Unit<Vector3<T>> {
925 self.inverse() * v
926 }
927}
928
929impl<T: SimdRealField + RealField> UnitDualQuaternion<T>
930where
931 T::Element: SimdRealField,
932{
933 /// Converts this unit dual quaternion interpreted as an isometry
934 /// into its equivalent homogeneous transformation matrix.
935 ///
936 /// # Example
937 /// ```
938 /// # #[macro_use] extern crate approx;
939 /// # use nalgebra::{Matrix4, UnitDualQuaternion, UnitQuaternion, Vector3};
940 /// let dq = UnitDualQuaternion::from_parts(
941 /// Vector3::new(1.0, 3.0, 2.0).into(),
942 /// UnitQuaternion::from_axis_angle(&Vector3::z_axis(), std::f32::consts::FRAC_PI_6)
943 /// );
944 /// let expected = Matrix4::new(0.8660254, -0.5, 0.0, 1.0,
945 /// 0.5, 0.8660254, 0.0, 3.0,
946 /// 0.0, 0.0, 1.0, 2.0,
947 /// 0.0, 0.0, 0.0, 1.0);
948 ///
949 /// assert_relative_eq!(dq.to_homogeneous(), expected, epsilon = 1.0e-6);
950 /// ```
951 #[inline]
952 #[must_use]
953 pub fn to_homogeneous(self) -> Matrix4<T> {
954 self.to_isometry().to_homogeneous()
955 }
956}
957
958impl<T: RealField> Default for UnitDualQuaternion<T> {
959 fn default() -> Self {
960 Self::identity()
961 }
962}
963
964impl<T: RealField + fmt::Display> fmt::Display for UnitDualQuaternion<T> {
965 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
966 match self.rotation().axis() {
967 Some(axis) => {
968 let axis = axis.into_inner();
969 write!(
970 f,
971 "UnitDualQuaternion translation: {} − angle: {} − axis: ({}, {}, {})",
972 self.translation().vector,
973 self.rotation().angle(),
974 axis[0],
975 axis[1],
976 axis[2]
977 )
978 }
979 None => {
980 write!(
981 f,
982 "UnitDualQuaternion translation: {} − angle: {} − axis: (undefined)",
983 self.translation().vector,
984 self.rotation().angle()
985 )
986 }
987 }
988 }
989}
990
991impl<T: RealField + AbsDiffEq<Epsilon = T>> AbsDiffEq for UnitDualQuaternion<T> {
992 type Epsilon = T;
993
994 #[inline]
995 fn default_epsilon() -> Self::Epsilon {
996 T::default_epsilon()
997 }
998
999 #[inline]
1000 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
1001 self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
1002 }
1003}
1004
1005impl<T: RealField + RelativeEq<Epsilon = T>> RelativeEq for UnitDualQuaternion<T> {
1006 #[inline]
1007 fn default_max_relative() -> Self::Epsilon {
1008 T::default_max_relative()
1009 }
1010
1011 #[inline]
1012 fn relative_eq(
1013 &self,
1014 other: &Self,
1015 epsilon: Self::Epsilon,
1016 max_relative: Self::Epsilon,
1017 ) -> bool {
1018 self.as_ref()
1019 .relative_eq(other.as_ref(), epsilon, max_relative)
1020 }
1021}
1022
1023impl<T: RealField + UlpsEq<Epsilon = T>> UlpsEq for UnitDualQuaternion<T> {
1024 #[inline]
1025 fn default_max_ulps() -> u32 {
1026 T::default_max_ulps()
1027 }
1028
1029 #[inline]
1030 fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
1031 self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
1032 }
1033}