1+ from __future__ import annotations
12import numpy as np
23from spatialmath import Quaternion , UnitQuaternion , SE3
34from spatialmath import base
5+ from spatialmath .base .types import *
46
57# TODO scalar multiplication
68
9+
710class DualQuaternion :
811 r"""
912 A dual number is an ordered pair :math:`\hat{a} = (a, b)` or written as
10- :math:`a + \epsilon b` where :math:`\epsilon^2 = 0`.
13+ :math:`a + \epsilon b` where :math:`\epsilon^2 = 0`.
1114
1215 A dual quaternion can be considered as either:
1316
@@ -27,7 +30,7 @@ class DualQuaternion:
2730 :seealso: :func:`UnitDualQuaternion`
2831 """
2932
30- def __init__ (self , real = None , dual = None ):
33+ def __init__ (self , real : Quaternion = None , dual : Quaternion = None ):
3134 """
3235 Construct a new dual quaternion
3336
@@ -61,23 +64,23 @@ def __init__(self, real=None, dual=None):
6164 self .dual = Quaternion (real [4 :8 ])
6265 elif real is not None and dual is not None :
6366 if not isinstance (real , Quaternion ):
64- raise ValueError (' real part must be a Quaternion subclass' )
67+ raise ValueError (" real part must be a Quaternion subclass" )
6568 if not isinstance (dual , Quaternion ):
66- raise ValueError (' real part must be a Quaternion subclass' )
69+ raise ValueError (" real part must be a Quaternion subclass" )
6770 self .real = real # quaternion, real part
6871 self .dual = dual # quaternion, dual part
6972 else :
70- raise ValueError (' expecting zero or two parameters' )
73+ raise ValueError (" expecting zero or two parameters" )
7174
7275 @classmethod
73- def Pure (cls , x ) :
76+ def Pure (cls , x : ArrayLike3 ) -> Self :
7477 x = base .getvector (x , 3 )
7578 return cls (UnitQuaternion (), Quaternion .Pure (x ))
7679
77- def __repr__ (self ):
80+ def __repr__ (self ) -> str :
7881 return str (self )
7982
80- def __str__ (self ):
83+ def __str__ (self ) -> str :
8184 """
8285 String representation of dual quaternion
8386
@@ -94,7 +97,7 @@ def __str__(self):
9497 """
9598 return str (self .real ) + " + ε " + str (self .dual )
9699
97- def norm (self ):
100+ def norm (self ) -> Tuple [ float , float ] :
98101 """
99102 Norm of a dual quaternion
100103
@@ -116,7 +119,7 @@ def norm(self):
116119 b = self .real * self .dual .conj () + self .dual * self .real .conj ()
117120 return (base .sqrt (a .s ), base .sqrt (b .s ))
118121
119- def conj (self ):
122+ def conj (self ) -> Self :
120123 r"""
121124 Conjugate of dual quaternion
122125
@@ -137,7 +140,9 @@ def conj(self):
137140 """
138141 return DualQuaternion (self .real .conj (), self .dual .conj ())
139142
140- def __add__ (left , right ): # lgtm[py/not-named-self] pylint: disable=no-self-argument
143+ def __add__ (
144+ left , right : DualQuaternion
145+ ) -> Self : # pylint: disable=no-self-argument
141146 """
142147 Sum of two dual quaternions
143148
@@ -154,7 +159,9 @@ def __add__(left, right): # lgtm[py/not-named-self] pylint: disable=no-self-arg
154159 """
155160 return DualQuaternion (left .real + right .real , left .dual + right .dual )
156161
157- def __sub__ (left , right ): # lgtm[py/not-named-self] pylint: disable=no-self-argument
162+ def __sub__ (
163+ left , right : DualQuaternion
164+ ) -> Self : # pylint: disable=no-self-argument
158165 """
159166 Difference of two dual quaternions
160167
@@ -171,7 +178,7 @@ def __sub__(left, right): # lgtm[py/not-named-self] pylint: disable=no-self-arg
171178 """
172179 return DualQuaternion (left .real - right .real , left .dual - right .dual )
173180
174- def __mul__ (left , right ) : # lgtm[py/not-named-self] pylint: disable=no-self-argument
181+ def __mul__ (left , right : Self ) -> Self : # pylint: disable=no-self-argument
175182 """
176183 Product of dual quaternion
177184
@@ -193,7 +200,9 @@ def __mul__(left, right): # lgtm[py/not-named-self] pylint: disable=no-self-arg
193200 real = left .real * right .real
194201 dual = left .real * right .dual + left .dual * right .real
195202
196- if isinstance (left , UnitDualQuaternion ) and isinstance (left , UnitDualQuaternion ):
203+ if isinstance (left , UnitDualQuaternion ) and isinstance (
204+ left , UnitDualQuaternion
205+ ):
197206 return UnitDualQuaternion (real , dual )
198207 else :
199208 return DualQuaternion (real , dual )
@@ -202,7 +211,7 @@ def __mul__(left, right): # lgtm[py/not-named-self] pylint: disable=no-self-arg
202211 vp = left * DualQuaternion .Pure (v ) * left .conj ()
203212 return vp .dual .v
204213
205- def matrix (self ):
214+ def matrix (self ) -> R8x8 :
206215 """
207216 Dual quaternion as a matrix
208217
@@ -222,13 +231,12 @@ def matrix(self):
222231 >>> d.matrix() @ d.vec
223232 >>> d * d
224233 """
225- return np .block ([
226- [self .real .matrix , np .zeros ((4 ,4 ))],
227- [self .dual .matrix , self .real .matrix ]
228- ])
234+ return np .block (
235+ [[self .real .matrix , np .zeros ((4 , 4 ))], [self .dual .matrix , self .real .matrix ]]
236+ )
229237
230238 @property
231- def vec (self ):
239+ def vec (self ) -> R8 :
232240 """
233241 Dual quaternion as a vector
234242
@@ -245,10 +253,10 @@ def vec(self):
245253 """
246254 return np .r_ [self .real .vec , self .dual .vec ]
247255
248-
249256 # def log(self):
250257 # pass
251-
258+
259+
252260class UnitDualQuaternion (DualQuaternion ):
253261 """[summary]
254262
@@ -262,6 +270,13 @@ class UnitDualQuaternion(DualQuaternion):
262270 :seealso: :func:`UnitDualQuaternion`
263271 """
264272
273+ @overload
274+ def __init__ (self , T : SE3 ):
275+ ...
276+
277+ def __init__ (self , real : Quaternion , dual : Quaternion ):
278+ ...
279+
265280 def __init__ (self , real = None , dual = None ):
266281 r"""
267282 Create new unit dual quaternion
@@ -307,13 +322,13 @@ def __init__(self, real=None, dual=None):
307322 T = real
308323 S = UnitQuaternion (T .R )
309324 D = Quaternion .Pure (T .t )
310-
325+
311326 real = S
312327 dual = 0.5 * D * S
313328
314329 super ().__init__ (real , dual )
315330
316- def SE3 (self ):
331+ def SE3 (self ) -> SE3 :
317332 """
318333 Convert unit dual quaternion to SE(3) matrix
319334
@@ -335,17 +350,18 @@ def SE3(self):
335350 t = 2 * self .dual * self .real .conj ()
336351
337352 return SE3 (base .rt2tr (R , t .v ))
338-
353+
339354 # def exp(self):
340355 # w = self.real.v
341356 # v = self.dual.v
342357 # theta = base.norm(w)
343358
359+
344360if __name__ == "__main__" : # pragma: no cover
345361
346362 from spatialmath import SE3 , UnitDualQuaternion
347363
348364 print (UnitDualQuaternion (SE3 ()))
349365 # import pathlib
350366
351- # exec(open(pathlib.Path(__file__).parent.parent.absolute() / "tests" / "test_dualquaternion.py").read()) # pylint: disable=exec-used
367+ # exec(open(pathlib.Path(__file__).parent.parent.absolute() / "tests" / "test_dualquaternion.py").read()) # pylint: disable=exec-used
0 commit comments