@@ -725,6 +725,48 @@ def vval(v):
725
725
726
726
return cls (np .c_ [x , y , z ], check = True )
727
727
728
+ @classmethod
729
+ def RotatedVector (cls , v1 : ArrayLike3 , v2 : ArrayLike3 , tol = 20 ) -> Self :
730
+ """
731
+ Construct a new SO(3) from a vector and its rotated image
732
+
733
+ :param v1: initial vector
734
+ :type v1: array_like(3)
735
+ :param v2: vector after rotation
736
+ :type v2: array_like(3)
737
+ :param tol: tolerance for singularity in units of eps, defaults to 20
738
+ :type tol: float
739
+ :return: SO(3) rotation
740
+ :rtype: :class:`SO3` instance
741
+
742
+ ``SO3.RotatedVector(v1, v2)`` is an SO(3) rotation defined in terms of
743
+ two vectors. The rotation takes vector ``v1`` to ``v2``.
744
+
745
+ .. runblock:: pycon
746
+
747
+ >>> from spatialmath import SO3
748
+ >>> v1 = [1, 2, 3]
749
+ >>> v2 = SO3.Eul(0.3, 0.4, 0.5) * v1
750
+ >>> print(v2)
751
+ >>> R = SO3.RotatedVector(v1, v2)
752
+ >>> print(R)
753
+ >>> print(R * v1)
754
+
755
+ .. note:: The vectors do not have to be unit-length.
756
+ """
757
+ # https://math.stackexchange.com/questions/180418/calculate-rotation-matrix-to-align-vector-a-to-vector-b-in-3d
758
+ v1 = smb .unitvec (v1 )
759
+ v2 = smb .unitvec (v2 )
760
+ v = smb .cross (v1 , v2 )
761
+ s = smb .norm (v )
762
+ if abs (s ) < tol * np .finfo (float ).eps :
763
+ return cls (np .eye (3 ), check = False )
764
+ else :
765
+ c = np .dot (v1 , v2 )
766
+ V = smb .skew (v )
767
+ R = np .eye (3 ) + V + V @ V * (1 - c ) / (s ** 2 )
768
+ return cls (R , check = False )
769
+
728
770
@classmethod
729
771
def AngleAxis (cls , theta : float , v : ArrayLike3 , * , unit : str = "rad" ) -> Self :
730
772
r"""
0 commit comments