@@ -725,6 +725,48 @@ def vval(v):
725725
726726 return cls (np .c_ [x , y , z ], check = True )
727727
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+
728770 @classmethod
729771 def AngleAxis (cls , theta : float , v : ArrayLike3 , * , unit : str = "rad" ) -> Self :
730772 r"""
0 commit comments