Source code for irfpy.util.eulerangle

r""" Implementation on Euler angle.

Euler angle is a set of three angles to represent any rotation in 3D.

See https://en.wikipedia.org/wiki/Euler_angles for better description.

- :class:`EulerAngleZXZ` reporesents zxz-Euler angle.
- :func:`matrix3d_rot_vec_x`, :func:`matrix3d_rot_vec_y`, :func:`matrix3d_rot_vec_z` gives the rotation matrix
  for vectors with respective to x, y, and z axis by a given angle.
- :func:`matrix3d_rot_frm_x`, :func:`matrix3d_rot_frm_y`, :func:`matrix3d_rot_frm_z` gives the conversion matrix
  of the components of a vector when the axes of the frame are rotated around x, y, and z with a given angle.
"""

import numpy as _np


[docs]class EulerAngleZXZ: r""" Z-X-Z Euler angle """ def __init__(self, alpha, beta, gamma): r""" Z-X-Z Euler angle Z-X-Z Euler angle, represented by the angles :math:`\alpha, \beta, \gamma`. Also, see figure https://commons.wikimedia.org/wiki/File:Eulerangles.svg for reference. :param alpha: |alpha| in radians. No restriction of the range. :param beta: |beta| in radians. No restriction of the range. :param gamma: |gamma| in radians. No restriction of the range. 1. Rotate |alpha| with respective to z. Range -|pi| to |pi|, theoretically 2. Rotate |beta| with respective to new x. Range 0 to |pi|, theoretically 3. Rotate |gamma| with respective to new z. Range -|pi| to |pi|, theoretically """ self._alpha = alpha self._beta = beta self._gamma = gamma
[docs] def get_matrix3d_rot_frm(self): r""" Return the conversion matrix of the vector components. Let us think a fixed vector. The vector is expressed by components of the original frame (x, y, z). Then, the frame (x, y, z) is rotated to (X, Y, Z) according to the Euler angles. The fixed vector is expressed by components of (X, Y, Z). The obtained matrix, R, shows the relation of .. math:: \left(\begin{array}{c} X \\ Y \\ Z \end{array}\right) = R\cdot \left(\begin{array}{c} x \\ y \\ z \end{array}\right) :return: (3, 3) shaped np.array, R """ m1 = matrix3d_rot_frm_z(self._alpha) m2 = matrix3d_rot_frm_x(self._beta) m3 = matrix3d_rot_frm_z(self._gamma) return m3.dot(m2).dot(m1)
[docs] def get_matrix3d_rot_vec(self): r""" Return the rotation matrix for the vector. Let us think a vector in the original system, (x, y, z). The vector is rotated according to the Euler angles. The rotated vector is expressed in the original frame :math:`(x', y', z')`. The obtained matrix, M, exhibids the relation of .. math:: \left(\begin{array}{c} x' \\ y' \\ z' \end{array}\right) = M\cdot \left(\begin{array}{c} x \\ y \\ z \end{array}\right) :return: (3,3) shaped np.array, M >>> import numpy as np >>> from irfpy.util.nptools import trim_epsilon as trim >>> alpha = np.pi / 2 # 90 deg >>> euler_zxz = EulerAngleZXZ(alpha, 0, 0) # This will give the rotation of axes of x -> y and y -> -x. >>> euler_m_vec = euler_zxz.get_matrix3d_rot_vec() >>> v2 = euler_m_vec.dot((1, 0, 0)) # x-axis is converted to y >>> print('{v[0]:.2f} {v[1]:.2f} {v[2]:.2f}'.format(v=trim(v2))) 0.00 1.00 0.00 >>> v2 = euler_m_vec.dot((0, 1, 0)) # y-axis is conveted to -x >>> print('{v[0]:.2f} {v[1]:.2f} {v[2]:.2f}'.format(v=trim(v2))) -1.00 0.00 0.00 >>> beta = np.pi / 2 # 90 deg >>> euler_zxz = EulerAngleZXZ(alpha, beta, 0) # This will give x->y->y, y->-x->z, z->z->x. >>> euler_m_vec = euler_zxz.get_matrix3d_rot_vec() >>> v2 = euler_m_vec.dot((1, 0, 0)) # x-axis is converted to y >>> print('{v[0]:.2f} {v[1]:.2f} {v[2]:.2f}'.format(v=trim(v2))) 0.00 1.00 0.00 >>> v2 = euler_m_vec.dot((0, 1, 0)) # y-axis is conveted to z >>> print('{v[0]:.2f} {v[1]:.2f} {v[2]:.2f}'.format(v=trim(v2))) 0.00 0.00 1.00 >>> v2 = euler_m_vec.dot((0, 0, 1)) # z-axis is conveted to x >>> print('{v[0]:.2f} {v[1]:.2f} {v[2]:.2f}'.format(v=trim(v2))) 1.00 0.00 0.00 >>> gamma = np.pi / 2 # 90 deg >>> euler_zxz = EulerAngleZXZ(alpha, beta, gamma) # This will give x->y->y->z, y->-x->z->-y, z->z->x->x. >>> euler_m_vec = euler_zxz.get_matrix3d_rot_vec() >>> v2 = euler_m_vec.dot((1, 0, 0)) # x-axis is converted to z >>> print('{v[0]:.2f} {v[1]:.2f} {v[2]:.2f}'.format(v=trim(v2))) 0.00 0.00 1.00 >>> v2 = euler_m_vec.dot((0, 1, 0)) # y-axis is conveted to -y >>> print('{v[0]:.2f} {v[1]:.2f} {v[2]:.2f}'.format(v=trim(v2))) 0.00 -1.00 0.00 >>> v2 = euler_m_vec.dot((0, 0, 1)) # z-axis is conveted to x >>> print('{v[0]:.2f} {v[1]:.2f} {v[2]:.2f}'.format(v=trim(v2))) 1.00 0.00 0.00 """ return _np.transpose(self.get_matrix3d_rot_frm())
[docs] def rotate_vectors(self, vec): """ Rotate the vectors. :param vec: Vector, shaped with (..., 3) :return: The rotated vector. Same shape as input. The vector is rotated according to the Euler angle. See :meth:`get_matrix3d_rot_vec`. >>> import numpy as np >>> from irfpy.util.nptools import trim_epsilon as trimep # To trim the very small floating points from matrix >>> alpha = beta = gamma = np.pi / 2. >>> euler_zxz = EulerAngleZXZ(alpha, beta, gamma) # This will give x->y->y->z, y->-x->z->-y, z->z->x->x. >>> v1 = [1, 0, 0] >>> v2 = [0, 1, 0] >>> v3 = [0, 0, 1] >>> v4 = [-1, 0, 0] >>> v5 = [0, -1, 0] >>> v6 = [0, 0, -1] >>> v = np.array([v1, v2, v3, v4, v5, v6]) >>> print(v.shape) (6, 3) >>> vr = euler_zxz.rotate_vectors(v) # Rotate six vectors at once. >>> print(vr.shape) (6, 3) >>> print('{v[0]:.2f} {v[1]:.2f} {v[2]:.2f}'.format(v=trimep(vr[0]))) # +x to +z 0.00 0.00 1.00 >>> print('{v[0]:.2f} {v[1]:.2f} {v[2]:.2f}'.format(v=trimep(vr[1]))) # +y to -y 0.00 -1.00 0.00 >>> print('{v[0]:.2f} {v[1]:.2f} {v[2]:.2f}'.format(v=trimep(vr[2]))) # +z to +x 1.00 0.00 0.00 >>> print('{v[0]:.2f} {v[1]:.2f} {v[2]:.2f}'.format(v=trimep(vr[3]))) # -x to -z 0.00 0.00 -1.00 >>> print('{v[0]:.2f} {v[1]:.2f} {v[2]:.2f}'.format(v=trimep(vr[4]))) # -y to +y 0.00 1.00 0.00 >>> print('{v[0]:.2f} {v[1]:.2f} {v[2]:.2f}'.format(v=trimep(vr[5]))) # -z to -x -1.00 0.00 0.00 """ m = self.get_matrix3d_rot_vec() vec2 = _np.transpose(m.dot(_np.transpose(vec))) return vec2
def __str__(self): s = "Euler angle (zxz): alpha={:.2g} beta={:.2g} gamma={:.2g}".format(self._alpha, self._beta, self._gamma) return s
[docs]def matrix3d_rot_vec_z(angle_radian): r""" Return the 3D matrix that rotate a vector by a given angle with respective to z. :param angle_radian: The angle |theta|, in radians. :return: Matrix, M. *Theory* .. math:: \mathbf{M} = \left(\begin{array}{ccc} \cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{array}\right) Any vector :math:`\vec{v}` will be rotated by |theta| with .. math:: \vec{v'} = \mathbf{M}\vec{v} :: y v' ^ v \ | / \ | / theta \<-|--/ \ | / \|/ +-----------------> x *Example* Obtain the 30 degree's rotation matrix. >>> import numpy as np >>> rot30z = matrix3d_rot_vec_z(np.deg2rad(30)) >>> print(rot30z) [[ 0.8660254 -0.5 0. ] [ 0.5 0.8660254 0. ] [ 0. 0. 1. ]] The vector ``(1, 0, 0)`` will be rotated to ``(sqrt(3)/2, 1/2, 0)`` >>> x0 = (1, 0, 0) >>> x1 = rot30z.dot(x0) >>> print(x1) [0.8660254 0.5 0. ] The vector ``(0, 1, 0)`` will be rotated to ``(-1/2, sqrt(3)/2, 0)`` >>> y0 = (0, 1, 0) >>> y1 = rot30z.dot(y0) >>> print(y1) [-0.5 0.8660254 0. ] """ c = _np.cos(angle_radian) s = _np.sin(angle_radian) return _np.array([[c, -s, 0], [s, c, 0], [0, 0, 1]])
[docs]def matrix3d_rot_frm_z(angle_radian): r""" Return the 3D matrix that convert a vector to the frame rotated by a given angle with respective to z. :param angle_radian: The angle |alpha|, in radians. :return: The conversion matrix, R. *Theory* .. math:: \mathbf{R} = \left(\begin{array}{ccc} \cos\theta & \sin\theta & 0 \\ -\sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{array}\right) It is the inverse matrix of the matrix *M* obtained in :func:`matrix3d_rot_vec_z`. As *M* is an orthogonal matrix, the inverse is the same as transpose. .. math:: R = M^{-1} (=M^{T}) Let the original frame, *(x, y, z)*, and rotate the frame by 45 degrees with respective to z axis. Let us denote the new frame as *(X, Y, Z)*. Then the components of the vector :math:`\vec{v}` expressed in *(x, y, z)* frame is different from those in *(X, Y, Z)*. The relation is .. math:: \vec{v}_{XYZ} = R \vec{v}_{xyz} :: y X Y ^ / v \ | / / \ | /\ / \ | / / \ | / / \ theta \|// \ +-----------------> x >>> import numpy as np >>> vec_xyz = (1, 0, 0) Then, consider a vector, :math:`\vec{v}=(1, 0, 0)` in the original frame, *(x, y, z)*. If we see the vector in the new frame *(X, Y, Z)*. It can be done with >>> r_mat = matrix3d_rot_frm_z(np.deg2rad(30)) >>> vec_XYZ = r_mat.dot(vec_xyz) >>> print(vec_XYZ) [ 0.8660254 -0.5 0. ] """ c = _np.cos(angle_radian) s = _np.sin(angle_radian) return _np.array([[c, s, 0], [-s, c, 0], [0, 0, 1]])
[docs]def matrix3d_rot_vec_x(angle_radian): r""" Return the 3D matrix that rotate a vector by a given angle with respective to x. :param angle_radian: The angle |theta|, in radians. :return: Matrix, M. *Theory* .. math:: \mathbf{M} = \left(\begin{array}{ccc} 1 & 0 & 0 \\ 0 & \cos\theta & -\sin\theta\\ 0 & \sin\theta & \cos\theta \end{array}\right) :: z v' ^ v \ | / \ | / theta \<-|--/ \ | / \|/ +-----------------> y *Example* Obtain the 30 degree's rotation matrix. >>> import numpy as np >>> rot30x = matrix3d_rot_vec_x(np.deg2rad(30)) >>> print(rot30x) [[ 1. 0. 0. ] [ 0. 0.8660254 -0.5 ] [ 0. 0.5 0.8660254]] The vector ``(0, 1, 0)`` will be rotated to ``(0, sqrt(3)/2, 1/2)`` >>> y0 = (0, 1, 0) >>> y1 = rot30x.dot(y0) >>> print(y1) [0. 0.8660254 0.5 ] The vector ``(0, 0, 1)`` will be rotated to ``(0, -1/2, sqrt(3)/2)`` >>> z0 = (0, 0, 1) >>> z1 = rot30x.dot(z0) >>> print(z1) [ 0. -0.5 0.8660254] """ c = _np.cos(angle_radian) s = _np.sin(angle_radian) return _np.array([[1, 0 ,0], [0, c, -s], [0, s, c]])
[docs]def matrix3d_rot_frm_x(angle_radian): r""" Return the 3D matrix that convert a vector to the frame rotated by a given angle with respective to x. :param angle_radian: The angle |alpha|, in radians. :return: The conversion matrix, R. *Theory* .. math:: \mathbf{R} = \left(\begin{array}{ccc} 1 & 0 & 0 \\ 0 & \cos\theta & \sin\theta \\ 0 & -\sin\theta & \cos\theta \end{array}\right) It is the inverse matrix of the matrix *M* obtained in :func:`matrix3d_rot_vec_x`. As *M* is an orthogonal matrix, the inverse is the same as transpose. .. math:: R = M^{-1} (=M^{T}) Let the original frame, *(x, y, z)*, and rotate the frame by 45 degrees with respective to x axis. Let us denote the new frame as *(X, Y, Z)*. Then the components of the vector :math:`\vec{v}` expressed in *(x, y, z)* frame is different from those in *(X, Y, Z)*. The relation is .. math:: \vec{v}_{XYZ} = R \vec{v}_{xyz} :: z Y Z ^ / v \ | / / \ | /\ / \ | / / \ | / / \ theta \|// \ +-----------------> y >>> import numpy as np >>> vec_xyz = (0, 1, 0) Then, consider a vector, :math:`\vec{v}=(0, 1, 0)` in the original frame, *(x, y, z)*. If we see the vector in the new frame *(X, Y, Z)*. It can be done with >>> r_mat = matrix3d_rot_frm_x(np.deg2rad(30)) >>> vec_XYZ = r_mat.dot(vec_xyz) >>> print(vec_XYZ) [ 0. 0.8660254 -0.5 ] """ c = _np.cos(angle_radian) s = _np.sin(angle_radian) return _np.array([[1, 0, 0], [0, c, s], [0, -s, c]])
[docs]def matrix3d_rot_vec_y(angle_radian): r""" Return the 3D matrix that rotate a vector by a given angle with respective to y. :param angle_radian: The angle |theta|, in radians. :return: Matrix, M. *Theory* .. math:: \mathbf{M} = \left(\begin{array}{ccc} \cos\theta & 0 & \sin\theta \\ 0 & 1 & 0 \\ -\sin\theta & 0 & \cos\theta \end{array}\right) Any vector :math:`\vec{v}` will be rotated by |theta| with .. math:: \vec{v'} = \mathbf{M}\vec{v} :: x v' ^ v \ | / \ | / theta \<-|--/ \ | / \|/ +-----------------> z *Example* Obtain the 30 degree's rotation matrix. >>> import numpy as np >>> rot30y = matrix3d_rot_vec_y(np.deg2rad(30)) >>> print(rot30y) [[ 0.8660254 0. 0.5 ] [ 0. 1. 0. ] [-0.5 0. 0.8660254]] The vector ``(1, 0, 0)`` will be rotated to ``(sqrt(3)/2, 0, -1/2)`` >>> x0 = (1, 0, 0) >>> x1 = rot30y.dot(x0) >>> print(x1) [ 0.8660254 0. -0.5 ] The vector ``(0, 0, 1)`` will be rotated to ``(1/2, 0, sqrt(3)/2)`` >>> z0 = (0, 0, 1) >>> z1 = rot30y.dot(z0) >>> print(z1) [0.5 0. 0.8660254] """ c = _np.cos(angle_radian) s = _np.sin(angle_radian) return _np.array([[c, 0, s], [0, 1, 0], [-s, 0, c]])
[docs]def matrix3d_rot_frm_y(angle_radian): r""" Return the 3D matrix that convert a vector to the frame rotated by a given angle with respective to y. :param angle_radian: The angle |alpha|, in radians. :return: The conversion matrix, R. *Theory* .. math:: \mathbf{R} = \left(\begin{array}{ccc} \cos\theta & 0 & \sin\theta \\ 0 & 1 & 0 \\ -\sin\theta & 0 & \cos\theta \end{array}\right) It is the inverse matrix of the matrix *M* obtained in :func:`matrix3d_rot_vec_y`. As *M* is an orthogonal matrix, the inverse is the same as transpose. .. math:: R = M^{-1} (=M^{T}) Let the original frame, *(x, y, z)*, and rotate the frame by 45 degrees with respective to z axis. Let us denote the new frame as *(X, Y, Z)*. Then the components of the vector :math:`\vec{v}` expressed in *(x, y, z)* frame is different from those in *(X, Y, Z)*. The relation is .. math:: \vec{v}_{XYZ} = R \vec{v}_{xyz} :: x Z X ^ / v \ | / / \ | /\ / \ | / / \ | / / \ theta \|// \ +-----------------> z >>> import numpy as np >>> vec_xyz = (1, 0, 0) Then, consider a vector, :math:`\vec{v}=(1, 0, 0)` in the original frame, *(x, y, z)*. If we see the vector in the new frame *(X, Y, Z)*. It can be done with >>> r_mat = matrix3d_rot_frm_y(np.deg2rad(30)) >>> vec_XYZ = r_mat.dot(vec_xyz) >>> print(vec_XYZ) [0.8660254 0. 0.5 ] """ c = _np.cos(angle_radian) s = _np.sin(angle_radian) return _np.array([[c, 0, -s], [0, 1, 0], [s, 0, c]])