pycram.tf_transformations
=========================

.. py:module:: pycram.tf_transformations

.. autoapi-nested-parse::

   This file was taken from> https://github.com/DLu/tf_transformations/tree/main
   Since it is not released for ROS1 we include it directly in PyCRAM

   This library is a reimplementation of the tf/transformations.py library.

   https://github.com/ros/geometry/blob/noetic-devel/tf/src/tf/transformations.py

   Original author: Christoph Gohlke <http://www.lfd.uci.edu/~gohlke/>
   Laboratory for Fluorescence Dynamics, University of California, Irvine

   Makes use of https://matthew-brett.github.io/transforms3d/ which is also
   a reimplementation of the Gohlke's work, but this maintains the API.



Attributes
----------

.. autoapisummary::

   pycram.tf_transformations.TRANSLATION_IDENTITY
   pycram.tf_transformations.ROTATION_IDENTITY
   pycram.tf_transformations.ZOOM_IDENTITY
   pycram.tf_transformations.SHEAR_IDENTITY
   pycram.tf_transformations._EPS
   pycram.tf_transformations._NEXT_AXIS
   pycram.tf_transformations._AXES2TUPLE
   pycram.tf_transformations._TUPLE2AXES


Classes
-------

.. autoapisummary::

   pycram.tf_transformations.Arcball


Functions
---------

.. autoapisummary::

   pycram.tf_transformations.identity_matrix
   pycram.tf_transformations.translation_matrix
   pycram.tf_transformations.translation_from_matrix
   pycram.tf_transformations.reflection_matrix
   pycram.tf_transformations.reflection_from_matrix
   pycram.tf_transformations.rotation_matrix
   pycram.tf_transformations.rotation_from_matrix
   pycram.tf_transformations.scale_matrix
   pycram.tf_transformations.scale_from_matrix
   pycram.tf_transformations.projection_matrix
   pycram.tf_transformations.projection_from_matrix
   pycram.tf_transformations.clip_matrix
   pycram.tf_transformations.shear_matrix
   pycram.tf_transformations.shear_from_matrix
   pycram.tf_transformations.decompose_matrix
   pycram.tf_transformations.compose_matrix
   pycram.tf_transformations.orthogonalization_matrix
   pycram.tf_transformations.superimposition_matrix
   pycram.tf_transformations.euler_matrix
   pycram.tf_transformations.euler_from_matrix
   pycram.tf_transformations.euler_from_quaternion
   pycram.tf_transformations._reorder_input_quaternion
   pycram.tf_transformations._reorder_output_quaternion
   pycram.tf_transformations.quaternion_from_euler
   pycram.tf_transformations.quaternion_about_axis
   pycram.tf_transformations.quaternion_matrix
   pycram.tf_transformations.quaternion_from_matrix
   pycram.tf_transformations.quaternion_multiply
   pycram.tf_transformations.quaternion_conjugate
   pycram.tf_transformations.quaternion_inverse
   pycram.tf_transformations.quaternion_slerp
   pycram.tf_transformations.random_quaternion
   pycram.tf_transformations.random_rotation_matrix
   pycram.tf_transformations.arcball_map_to_sphere
   pycram.tf_transformations.arcball_constrain_to_axis
   pycram.tf_transformations.arcball_nearest_axis
   pycram.tf_transformations.vector_norm
   pycram.tf_transformations.unit_vector
   pycram.tf_transformations.random_vector
   pycram.tf_transformations.inverse_matrix
   pycram.tf_transformations.concatenate_matrices
   pycram.tf_transformations.is_same_transform


Module Contents
---------------

.. py:data:: TRANSLATION_IDENTITY
   :value: [0.0, 0.0, 0.0]


.. py:data:: ROTATION_IDENTITY

.. py:data:: ZOOM_IDENTITY
   :value: [1.0, 1.0, 1.0]


.. py:data:: SHEAR_IDENTITY
   :value: [0.0, 0.0, 0.0]


.. py:function:: identity_matrix()

   Return 4x4 identity/unit matrix.

   >>> I = identity_matrix()
   >>> numpy.allclose(I, numpy.dot(I, I))
   True
   >>> numpy.sum(I), numpy.trace(I)
   (4.0, 4.0)
   >>> numpy.allclose(I, numpy.identity(4, dtype=numpy.float64))
   True



.. py:function:: translation_matrix(direction)

   Return matrix to translate by direction vector.

   >>> v = numpy.random.random(3) - 0.5
   >>> numpy.allclose(v, translation_matrix(v)[:3, 3])
   True



.. py:function:: translation_from_matrix(matrix)

   Return translation vector from translation matrix.

   >>> v0 = numpy.random.random(3) - 0.5
   >>> v1 = translation_from_matrix(translation_matrix(v0))
   >>> numpy.allclose(v0, v1)
   True



.. py:function:: reflection_matrix(point, normal)

   Return matrix to mirror at plane defined by point and normal vector.

   >>> v0 = numpy.random.random(4) - 0.5
   >>> v0[3] = 1.0
   >>> v1 = numpy.random.random(3) - 0.5
   >>> R = reflection_matrix(v0, v1)
   >>> numpy.allclose(2., numpy.trace(R))
   True
   >>> numpy.allclose(v0, numpy.dot(R, v0))
   True
   >>> v2 = v0.copy()
   >>> v2[:3] += v1
   >>> v3 = v0.copy()
   >>> v2[:3] -= v1
   >>> numpy.allclose(v2, numpy.dot(R, v3))
   True



.. py:function:: reflection_from_matrix(matrix)

   Return mirror plane point and normal vector from reflection matrix.

   >>> v0 = numpy.random.random(3) - 0.5
   >>> v1 = numpy.random.random(3) - 0.5
   >>> M0 = reflection_matrix(v0, v1)
   >>> point, normal = reflection_from_matrix(M0)
   >>> M1 = reflection_matrix(point, normal)
   >>> is_same_transform(M0, M1)
   True



.. py:function:: rotation_matrix(angle, direction, point=None)

   Return matrix to rotate about axis defined by point and direction.

   >>> angle = (random.random() - 0.5) * (2*math.pi)
   >>> direc = numpy.random.random(3) - 0.5
   >>> point = numpy.random.random(3) - 0.5
   >>> R0 = rotation_matrix(angle, direc, point)
   >>> R1 = rotation_matrix(angle-2*math.pi, direc, point)
   >>> is_same_transform(R0, R1)
   True
   >>> R0 = rotation_matrix(angle, direc, point)
   >>> R1 = rotation_matrix(-angle, -direc, point)
   >>> is_same_transform(R0, R1)
   True
   >>> I = numpy.identity(4, numpy.float64)
   >>> numpy.allclose(I, rotation_matrix(math.pi*2, direc))
   True
   >>> numpy.allclose(2., numpy.trace(rotation_matrix(math.pi/2,
   ...                                                direc, point)))
   True



.. py:function:: rotation_from_matrix(matrix)

   Return rotation angle and axis from rotation matrix.

   >>> angle = (random.random() - 0.5) * (2*math.pi)
   >>> direc = numpy.random.random(3) - 0.5
   >>> point = numpy.random.random(3) - 0.5
   >>> R0 = rotation_matrix(angle, direc, point)
   >>> angle, direc, point = rotation_from_matrix(R0)
   >>> R1 = rotation_matrix(angle, direc, point)
   >>> is_same_transform(R0, R1)
   True



.. py:function:: scale_matrix(factor, origin=None, direction=None)

   Return matrix to scale by factor around origin in direction.

   Use factor -1 for point symmetry.

   >>> v = (numpy.random.rand(4, 5) - 0.5) * 20.0
   >>> v[3] = 1.0
   >>> S = scale_matrix(-1.234)
   >>> numpy.allclose(numpy.dot(S, v)[:3], -1.234*v[:3])
   True
   >>> factor = random.random() * 10 - 5
   >>> origin = numpy.random.random(3) - 0.5
   >>> direct = numpy.random.random(3) - 0.5
   >>> S = scale_matrix(factor, origin)
   >>> S = scale_matrix(factor, origin, direct)



.. py:function:: scale_from_matrix(matrix)

   Return scaling factor, origin and direction from scaling matrix.

   >>> factor = random.random() * 10 - 5
   >>> origin = numpy.random.random(3) - 0.5
   >>> direct = numpy.random.random(3) - 0.5
   >>> S0 = scale_matrix(factor, origin)
   >>> factor, origin, direction = scale_from_matrix(S0)
   >>> S1 = scale_matrix(factor, origin, direction)
   >>> is_same_transform(S0, S1)
   True
   >>> S0 = scale_matrix(factor, origin, direct)
   >>> factor, origin, direction = scale_from_matrix(S0)
   >>> S1 = scale_matrix(factor, origin, direction)
   >>> is_same_transform(S0, S1)
   True



.. py:function:: projection_matrix(point, normal, direction=None, perspective=None, pseudo=False)

   Return matrix to project onto plane defined by point and normal.

   Using either perspective point, projection direction, or none of both.

   If pseudo is True, perspective projections will preserve relative depth
   such that Perspective = dot(Orthogonal, PseudoPerspective).

   >>> P = projection_matrix((0, 0, 0), (1, 0, 0))
   >>> numpy.allclose(P[1:, 1:], numpy.identity(4)[1:, 1:])
   True
   >>> point = numpy.random.random(3) - 0.5
   >>> normal = numpy.random.random(3) - 0.5
   >>> direct = numpy.random.random(3) - 0.5
   >>> persp = numpy.random.random(3) - 0.5
   >>> P0 = projection_matrix(point, normal)
   >>> P1 = projection_matrix(point, normal, direction=direct)
   >>> P2 = projection_matrix(point, normal, perspective=persp)
   >>> P3 = projection_matrix(point, normal, perspective=persp, pseudo=True)
   >>> is_same_transform(P2, numpy.dot(P0, P3))
   True
   >>> P = projection_matrix((3, 0, 0), (1, 1, 0), (1, 0, 0))
   >>> v0 = (numpy.random.rand(4, 5) - 0.5) * 20.0
   >>> v0[3] = 1.0
   >>> v1 = numpy.dot(P, v0)
   >>> numpy.allclose(v1[1], v0[1])
   True
   >>> numpy.allclose(v1[0], 3.0-v1[1])
   True



.. py:function:: projection_from_matrix(matrix, pseudo=False)

   Return projection plane and perspective point from projection matrix.

   Return values are same as arguments for projection_matrix function:
   point, normal, direction, perspective, and pseudo.

   >>> point = numpy.random.random(3) - 0.5
   >>> normal = numpy.random.random(3) - 0.5
   >>> direct = numpy.random.random(3) - 0.5
   >>> persp = numpy.random.random(3) - 0.5
   >>> P0 = projection_matrix(point, normal)
   >>> result = projection_from_matrix(P0)
   >>> P1 = projection_matrix(*result)
   >>> is_same_transform(P0, P1)
   True
   >>> P0 = projection_matrix(point, normal, direct)
   >>> result = projection_from_matrix(P0)
   >>> P1 = projection_matrix(*result)
   >>> is_same_transform(P0, P1)
   True
   >>> P0 = projection_matrix(point, normal, perspective=persp, pseudo=False)
   >>> result = projection_from_matrix(P0, pseudo=False)
   >>> P1 = projection_matrix(*result)
   >>> is_same_transform(P0, P1)
   True
   >>> P0 = projection_matrix(point, normal, perspective=persp, pseudo=True)
   >>> result = projection_from_matrix(P0, pseudo=True)
   >>> P1 = projection_matrix(*result)
   >>> is_same_transform(P0, P1)
   True



.. py:function:: clip_matrix(left, right, bottom, top, near, far, perspective=False)

   Return matrix to obtain normalized device coordinates from frustum.

   The frustum bounds are axis-aligned along x (left, right),
   y (bottom, top) and z (near, far).

   Normalized device coordinates are in range [-1, 1] if coordinates are
   inside the frustum.

   If perspective is True the frustum is a truncated pyramid with the
   perspective point at origin and direction along z axis, otherwise an
   orthographic canonical view volume (a box).

   Homogeneous coordinates transformed by the perspective clip matrix
   need to be dehomogenized (divided by w coordinate).

   >>> frustum = numpy.random.rand(6)
   >>> frustum[1] += frustum[0]
   >>> frustum[3] += frustum[2]
   >>> frustum[5] += frustum[4]
   >>> M = clip_matrix(*frustum, perspective=False)
   >>> numpy.dot(M, [frustum[0], frustum[2], frustum[4], 1.0])
   array([-1., -1., -1.,  1.])
   >>> numpy.dot(M, [frustum[1], frustum[3], frustum[5], 1.0])
   array([ 1.,  1.,  1.,  1.])
   >>> M = clip_matrix(*frustum, perspective=True)
   >>> v = numpy.dot(M, [frustum[0], frustum[2], frustum[4], 1.0])
   >>> v / v[3]
   array([-1., -1., -1.,  1.])
   >>> v = numpy.dot(M, [frustum[1], frustum[3], frustum[4], 1.0])
   >>> v / v[3]
   array([ 1.,  1., -1.,  1.])



.. py:function:: shear_matrix(angle, direction, point, normal)

   Return matrix to shear by angle along direction vector on shear plane.

   The shear plane is defined by a point and normal vector. The direction
   vector must be orthogonal to the plane's normal vector.

   A point P is transformed by the shear matrix into P" such that
   the vector P-P" is parallel to the direction vector and its extent is
   given by the angle of P-P'-P", where P' is the orthogonal projection
   of P onto the shear plane.

   >>> angle = (random.random() - 0.5) * 4*math.pi
   >>> direct = numpy.random.random(3) - 0.5
   >>> point = numpy.random.random(3) - 0.5
   >>> normal = numpy.cross(direct, numpy.random.random(3))
   >>> S = shear_matrix(angle, direct, point, normal)
   >>> numpy.allclose(1.0, numpy.linalg.det(S))
   True



.. py:function:: shear_from_matrix(matrix)

   Return shear angle, direction and plane from shear matrix.

   >>> angle = (random.random() - 0.5) * 4*math.pi
   >>> direct = numpy.random.random(3) - 0.5
   >>> point = numpy.random.random(3) - 0.5
   >>> normal = numpy.cross(direct, numpy.random.random(3))
   >>> S0 = shear_matrix(angle, direct, point, normal)
   >>> angle, direct, point, normal = shear_from_matrix(S0)
   >>> S1 = shear_matrix(angle, direct, point, normal)
   >>> is_same_transform(S0, S1)
   True



.. py:function:: decompose_matrix(matrix)

   Return sequence of transformations from transformation matrix.

   matrix : array_like
       Non-degenerative homogeneous transformation matrix

   Return tuple of:
       scale : vector of 3 scaling factors
       shear : list of shear factors for x-y, x-z, y-z axes
       angles : list of Euler angles about static x, y, z axes
       translate : translation vector along x, y, z axes
       perspective : perspective partition of matrix

   Raise ValueError if matrix is of wrong type or degenerative.

   >>> T0 = translation_matrix((1, 2, 3))
   >>> scale, shear, angles, trans, persp = decompose_matrix(T0)
   >>> T1 = translation_matrix(trans)
   >>> numpy.allclose(T0, T1)
   True
   >>> S = scale_matrix(0.123)
   >>> scale, shear, angles, trans, persp = decompose_matrix(S)
   >>> scale[0]
   0.123
   >>> R0 = euler_matrix(1, 2, 3)
   >>> scale, shear, angles, trans, persp = decompose_matrix(R0)
   >>> R1 = euler_matrix(*angles)
   >>> numpy.allclose(R0, R1)
   True



.. py:function:: compose_matrix(scale=None, shear=None, angles=None, translate=None, perspective=None)

   Return transformation matrix from sequence of transformations.

   This is the inverse of the decompose_matrix function.

   Sequence of transformations:
       scale : vector of 3 scaling factors
       shear : list of shear factors for x-y, x-z, y-z axes
       angles : list of Euler angles about static x, y, z axes
       translate : translation vector along x, y, z axes
       perspective : perspective partition of matrix

   >>> scale = numpy.random.random(3) - 0.5
   >>> shear = numpy.random.random(3) - 0.5
   >>> angles = (numpy.random.random(3) - 0.5) * (2*math.pi)
   >>> trans = numpy.random.random(3) - 0.5
   >>> persp = numpy.random.random(4) - 0.5
   >>> M0 = compose_matrix(scale, shear, angles, trans, persp)
   >>> result = decompose_matrix(M0)
   >>> M1 = compose_matrix(*result)
   >>> is_same_transform(M0, M1)
   True



.. py:function:: orthogonalization_matrix(lengths, angles)

   Return orthogonalization matrix for crystallographic cell coordinates.

   Angles are expected in degrees.

   The de-orthogonalization matrix is the inverse.

   >>> O = orthogonalization_matrix((10., 10., 10.), (90., 90., 90.))
   >>> numpy.allclose(O[:3, :3], numpy.identity(3, float) * 10)
   True
   >>> O = orthogonalization_matrix([9.8, 12.0, 15.5], [87.2, 80.7, 69.7])
   >>> numpy.allclose(numpy.sum(O), 43.063229)
   True



.. py:function:: superimposition_matrix(v0, v1, scaling=False, usesvd=True)

   Return matrix to transform given vector set into second vector set.

   v0 and v1 are shape (3, *) or (4, *) arrays of at least 3 vectors.

   If usesvd is True, the weighted sum of squared deviations (RMSD) is
   minimized according to the algorithm by W. Kabsch [8]. Otherwise the
   quaternion based algorithm by B. Horn [9] is used (slower when using
   this Python implementation).

   The returned matrix performs rotation, translation and uniform scaling
   (if specified).

   >>> v0 = numpy.random.rand(3, 10)
   >>> M = superimposition_matrix(v0, v0)
   >>> numpy.allclose(M, numpy.identity(4))
   True
   >>> R = random_rotation_matrix(numpy.random.random(3))
   >>> v0 = ((1,0,0), (0,1,0), (0,0,1), (1,1,1))
   >>> v1 = numpy.dot(R, v0)
   >>> M = superimposition_matrix(v0, v1)
   >>> numpy.allclose(v1, numpy.dot(M, v0))
   True
   >>> v0 = (numpy.random.rand(4, 100) - 0.5) * 20.0
   >>> v0[3] = 1.0
   >>> v1 = numpy.dot(R, v0)
   >>> M = superimposition_matrix(v0, v1)
   >>> numpy.allclose(v1, numpy.dot(M, v0))
   True
   >>> S = scale_matrix(random.random())
   >>> T = translation_matrix(numpy.random.random(3)-0.5)
   >>> M = concatenate_matrices(T, R, S)
   >>> v1 = numpy.dot(M, v0)
   >>> v0[:3] += numpy.random.normal(0.0, 1e-9, 300).reshape(3, -1)
   >>> M = superimposition_matrix(v0, v1, scaling=True)
   >>> numpy.allclose(v1, numpy.dot(M, v0))
   True
   >>> M = superimposition_matrix(v0, v1, scaling=True, usesvd=False)
   >>> numpy.allclose(v1, numpy.dot(M, v0))
   True
   >>> v = numpy.empty((4, 100, 3), dtype=numpy.float64)
   >>> v[:, :, 0] = v0
   >>> M = superimposition_matrix(v0, v1, scaling=True, usesvd=False)
   >>> numpy.allclose(v1, numpy.dot(M, v[:, :, 0]))
   True



.. py:function:: euler_matrix(ai, aj, ak, axes='sxyz')

   Return homogeneous rotation matrix from Euler angles and axis sequence.

   ai, aj, ak : Euler's roll, pitch and yaw angles
   axes : One of 24 axis sequences as string or encoded tuple

   >>> R = euler_matrix(1, 2, 3, 'syxz')
   >>> numpy.allclose(numpy.sum(R[0]), -1.34786452)
   True
   >>> R = euler_matrix(1, 2, 3, (0, 1, 0, 1))
   >>> numpy.allclose(numpy.sum(R[0]), -0.383436184)
   True
   >>> ai, aj, ak = (4.0*math.pi) * (numpy.random.random(3) - 0.5)
   >>> for axes in _AXES2TUPLE.keys():
   ...    R = euler_matrix(ai, aj, ak, axes)
   >>> for axes in _TUPLE2AXES.keys():
   ...    R = euler_matrix(ai, aj, ak, axes)



.. py:function:: euler_from_matrix(matrix, axes='sxyz')

   Return Euler angles from rotation matrix for specified axis sequence.

   axes : One of 24 axis sequences as string or encoded tuple

   Note that many Euler angle triplets can describe one matrix.

   >>> R0 = euler_matrix(1, 2, 3, 'syxz')
   >>> al, be, ga = euler_from_matrix(R0, 'syxz')
   >>> R1 = euler_matrix(al, be, ga, 'syxz')
   >>> numpy.allclose(R0, R1)
   True
   >>> angles = (4.0*math.pi) * (numpy.random.random(3) - 0.5)
   >>> for axes in _AXES2TUPLE.keys():
   ...    R0 = euler_matrix(axes=axes, *angles)
   ...    R1 = euler_matrix(axes=axes, *euler_from_matrix(R0, axes))
   ...    if not numpy.allclose(R0, R1): print axes, "failed"



.. py:function:: euler_from_quaternion(quaternion, axes='sxyz')

   Return Euler angles from quaternion for specified axis sequence.

   >>> angles = euler_from_quaternion([0.06146124, 0, 0, 0.99810947])
   >>> numpy.allclose(angles, [0.123, 0, 0])
   True



.. py:function:: _reorder_input_quaternion(quaternion)

   Reorder quaternion to have w term first.


.. py:function:: _reorder_output_quaternion(quaternion)

   Reorder quaternion to have w term last.


.. py:function:: quaternion_from_euler(ai, aj, ak, axes='sxyz')

   Return quaternion from Euler angles and axis sequence.

   ai, aj, ak : Euler's roll, pitch and yaw angles
   axes : One of 24 axis sequences as string or encoded tuple

   >>> q = quaternion_from_euler(1, 2, 3, 'ryxz')
   >>> numpy.allclose(q, [0.310622, -0.718287, 0.444435, 0.435953])
   True



.. py:function:: quaternion_about_axis(angle, axis)

   Return quaternion for rotation about axis.

   >>> q = quaternion_about_axis(0.123, (1, 0, 0))
   >>> numpy.allclose(q, [0.06146124, 0, 0, 0.99810947])
   True



.. py:function:: quaternion_matrix(quaternion)

   Return 4x4 homogeneous rotation matrix from quaternion.

   >>> R = quaternion_matrix([0.06146124, 0, 0, 0.99810947])
   >>> numpy.allclose(R, rotation_matrix(0.123, (1, 0, 0)))
   True



.. py:function:: quaternion_from_matrix(matrix)

   Return quaternion from rotation matrix.

   >>> R = rotation_matrix(0.123, (1, 2, 3))
   >>> q = quaternion_from_matrix(R)
   >>> numpy.allclose(q, [0.0164262, 0.0328524, 0.0492786, 0.9981095])
   True



.. py:function:: quaternion_multiply(quaternion1, quaternion0)

   Return multiplication of two quaternions.

   >>> q = quaternion_multiply([1, -2, 3, 4], [-5, 6, 7, 8])
   >>> numpy.allclose(q, [-44, -14, 48, 28])
   True



.. py:function:: quaternion_conjugate(quaternion)

   Return conjugate of quaternion.

   >>> q0 = random_quaternion()
   >>> q1 = quaternion_conjugate(q0)
   >>> q1[3] == q0[3] and all(q1[:3] == -q0[:3])
   True



.. py:function:: quaternion_inverse(quaternion)

   Return inverse of quaternion.

   >>> q0 = random_quaternion()
   >>> q1 = quaternion_inverse(q0)
   >>> numpy.allclose(quaternion_multiply(q0, q1), [0, 0, 0, 1])
   True



.. py:function:: quaternion_slerp(quat0, quat1, fraction, spin=0, shortestpath=True)

   Return spherical linear interpolation between two quaternions.

   >>> q0 = random_quaternion()
   >>> q1 = random_quaternion()
   >>> q = quaternion_slerp(q0, q1, 0.0)
   >>> numpy.allclose(q, q0)
   True
   >>> q = quaternion_slerp(q0, q1, 1.0, 1)
   >>> numpy.allclose(q, q1)
   True
   >>> q = quaternion_slerp(q0, q1, 0.5)
   >>> angle = math.acos(numpy.dot(q0, q))
   >>> numpy.allclose(2.0, math.acos(numpy.dot(q0, q1)) / angle) or
       numpy.allclose(2.0, math.acos(-numpy.dot(q0, q1)) / angle)
   True



.. py:function:: random_quaternion(rand=None)

   Return uniform random unit quaternion.

   rand: array like or None
       Three independent random variables that are uniformly distributed
       between 0 and 1.

   >>> q = random_quaternion()
   >>> numpy.allclose(1.0, vector_norm(q))
   True
   >>> q = random_quaternion(numpy.random.random(3))
   >>> q.shape
   (4,)



.. py:function:: random_rotation_matrix(rand=None)

   Return uniform random rotation matrix.

   rnd: array like
       Three independent random variables that are uniformly distributed
       between 0 and 1 for each returned quaternion.

   >>> R = random_rotation_matrix()
   >>> numpy.allclose(numpy.dot(R.T, R), numpy.identity(4))
   True



.. py:class:: Arcball(initial=None)

   Bases: :py:obj:`object`


   Virtual Trackball Control.

   >>> ball = Arcball()
   >>> ball = Arcball(initial=numpy.identity(4))
   >>> ball.place([320, 320], 320)
   >>> ball.down([500, 250])
   >>> ball.drag([475, 275])
   >>> R = ball.matrix()
   >>> numpy.allclose(numpy.sum(R), 3.90583455)
   True
   >>> ball = Arcball(initial=[0, 0, 0, 1])
   >>> ball.place([320, 320], 320)
   >>> ball.setaxes([1,1,0], [-1, 1, 0])
   >>> ball.setconstrain(True)
   >>> ball.down([400, 200])
   >>> ball.drag([200, 400])
   >>> R = ball.matrix()
   >>> numpy.allclose(numpy.sum(R), 0.2055924)
   True
   >>> ball.next()



   .. py:attribute:: _axis
      :value: None



   .. py:attribute:: _axes
      :value: None



   .. py:attribute:: _radius
      :value: 1.0



   .. py:attribute:: _center
      :value: [0.0, 0.0]



   .. py:attribute:: _vdown


   .. py:attribute:: _constrain
      :value: False



   .. py:method:: place(center, radius)

      Place Arcball, e.g. when window size changes.

      center : sequence[2]
          Window coordinates of trackball center.
      radius : float
          Radius of trackball in window coordinates.




   .. py:method:: setaxes(*axes)

      Set axes to constrain rotations.



   .. py:method:: setconstrain(constrain)

      Set state of constrain to axis mode.



   .. py:method:: getconstrain()

      Return state of constrain to axis mode.



   .. py:method:: down(point)

      Set initial cursor window coordinates and pick constrain-axis.



   .. py:method:: drag(point)

      Update current cursor window coordinates.



   .. py:method:: next(acceleration=0.0)

      Continue rotation in direction of last drag.



   .. py:method:: matrix()

      Return homogeneous rotation matrix.



.. py:function:: arcball_map_to_sphere(point, center, radius)

   Return unit sphere coordinates from window coordinates.


.. py:function:: arcball_constrain_to_axis(point, axis)

   Return sphere point perpendicular to axis.


.. py:function:: arcball_nearest_axis(point, axes)

   Return axis, which arc is nearest to point.


.. py:data:: _EPS

.. py:data:: _NEXT_AXIS
   :value: [1, 2, 0, 1]


.. py:data:: _AXES2TUPLE

.. py:data:: _TUPLE2AXES

.. py:function:: vector_norm(data, axis=None, out=None)

   Return length, i.e. eucledian norm, of ndarray along axis.

   >>> v = numpy.random.random(3)
   >>> n = vector_norm(v)
   >>> numpy.allclose(n, numpy.linalg.norm(v))
   True
   >>> v = numpy.random.rand(6, 5, 3)
   >>> n = vector_norm(v, axis=-1)
   >>> numpy.allclose(n, numpy.sqrt(numpy.sum(v*v, axis=2)))
   True
   >>> n = vector_norm(v, axis=1)
   >>> numpy.allclose(n, numpy.sqrt(numpy.sum(v*v, axis=1)))
   True
   >>> v = numpy.random.rand(5, 4, 3)
   >>> n = numpy.empty((5, 3), dtype=numpy.float64)
   >>> vector_norm(v, axis=1, out=n)
   >>> numpy.allclose(n, numpy.sqrt(numpy.sum(v*v, axis=1)))
   True
   >>> vector_norm([])
   0.0
   >>> vector_norm([1.0])
   1.0



.. py:function:: unit_vector(data, axis=None, out=None)

   Return ndarray normalized by length, i.e. eucledian norm, along axis.

   >>> v0 = numpy.random.random(3)
   >>> v1 = unit_vector(v0)
   >>> numpy.allclose(v1, v0 / numpy.linalg.norm(v0))
   True
   >>> v0 = numpy.random.rand(5, 4, 3)
   >>> v1 = unit_vector(v0, axis=-1)
   >>> v2 = v0 / numpy.expand_dims(numpy.sqrt(numpy.sum(v0*v0, axis=2)), 2)
   >>> numpy.allclose(v1, v2)
   True
   >>> v1 = unit_vector(v0, axis=1)
   >>> v2 = v0 / numpy.expand_dims(numpy.sqrt(numpy.sum(v0*v0, axis=1)), 1)
   >>> numpy.allclose(v1, v2)
   True
   >>> v1 = numpy.empty((5, 4, 3), dtype=numpy.float64)
   >>> unit_vector(v0, axis=1, out=v1)
   >>> numpy.allclose(v1, v2)
   True
   >>> list(unit_vector([]))
   []
   >>> list(unit_vector([1.0]))
   [1.0]



.. py:function:: random_vector(size)

   Return array of random doubles in the half-open interval [0.0, 1.0).

   >>> v = random_vector(10000)
   >>> numpy.all(v >= 0.0) and numpy.all(v < 1.0)
   True
   >>> v0 = random_vector(10)
   >>> v1 = random_vector(10)
   >>> numpy.any(v0 == v1)
   False



.. py:function:: inverse_matrix(matrix)

   Return inverse of square transformation matrix.

   >>> M0 = random_rotation_matrix()
   >>> M1 = inverse_matrix(M0.T)
   >>> numpy.allclose(M1, numpy.linalg.inv(M0.T))
   True
   >>> for size in range(1, 7):
   ...     M0 = numpy.random.rand(size, size)
   ...     M1 = inverse_matrix(M0)
   ...     if not numpy.allclose(M1, numpy.linalg.inv(M0)): print size



.. py:function:: concatenate_matrices(*matrices)

   Return concatenation of series of transformation matrices.

   >>> M = numpy.random.rand(16).reshape((4, 4)) - 0.5
   >>> numpy.allclose(M, concatenate_matrices(M))
   True
   >>> numpy.allclose(numpy.dot(M, M.T), concatenate_matrices(M, M.T))
   True



.. py:function:: is_same_transform(matrix0, matrix1)

   Return True if two matrices perform same transformation.

   >>> is_same_transform(numpy.identity(4), numpy.identity(4))
   True
   >>> is_same_transform(numpy.identity(4), random_rotation_matrix())
   False



