
    nie              	          S r SSKJr  SSKrSSKrSSKrSrS rS rS r	S r
S	 rS]S
 jrS rS^S jrS r  S_S jrS`S jrS`S jrS rS rS r  SaS jrS rSbS jrScS jrScS jrScS jrScS jrS rS rS rS r S  r!S! r"SdS" jr#S]S# jr$S]S$ jr% " S% S&\&5      r'S' r(S( r)S) r*\RV                  " \,5      RZ                  S*-  r./ S+Qr/0 SS,_S-S._S/S0_S1S2_S3S4_S5S6_S7S8_S9S:_S;S<_S=S>_S?S@_SASB_SCSD_SESF_SGSH_SISJ_SKSL_SMSNSOSPSQSRSSST.Er0\1" SU \0Re                  5        5       5      r3S^SV jr4S^SW jr5SX r6SY r7SZ r8S[ r9SeS\ jr:g)faZ  Homogeneous Transformation Matrices and Quaternions.

A library for calculating 4x4 matrices for translating, rotating, reflecting,
scaling, shearing, projecting, orthogonalizing, and superimposing arrays of
3D homogeneous coordinates as well as for converting between rotation matrices,
Euler angles, and quaternions. Also includes an Arcball control object and
functions to decompose transformation matrices.

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

:Version: 20090418

Requirements
------------

* `Python 2.6 <http://www.python.org>`__
* `Numpy 1.3 <http://numpy.scipy.org>`__
* `transformations.c 20090418 <http://www.lfd.uci.edu/~gohlke/>`__
  (optional implementation of some functions in C)

Notes
-----

Matrices (M) can be inverted using numpy.linalg.inv(M), concatenated using
numpy.dot(M0, M1), or used to transform homogeneous coordinates (v) using
numpy.dot(M, v) for shape (4, \*) "point of arrays", respectively
numpy.dot(v, M.T) for shape (\*, 4) "array of points".

Calculations are carried out with numpy.float64 precision.

This Python implementation is not optimized for speed.

Vector, point, quaternion, and matrix function arguments are expected to be
"array like", i.e. tuple, list, or numpy arrays.

Return types are numpy arrays unless specified otherwise.

Angles are in radians unless specified otherwise.

Quaternions ix+jy+kz+w are represented as [x, y, z, w].

Use the transpose of transformation matrices for OpenGL glMultMatrixd().

A triple of Euler angles can be applied/interpreted in 24 ways, which can
be specified using a 4 character string or encoded 4-tuple:

  *Axes 4-string*: e.g. 'sxyz' or 'ryxy'

  - first character : rotations are applied to 's'tatic or 'r'otating frame
  - remaining characters : successive rotation axis 'x', 'y', or 'z'

  *Axes 4-tuple*: e.g. (0, 0, 0, 0) or (1, 1, 1, 1)

  - inner axis: code of axis ('x':0, 'y':1, 'z':2) of rightmost matrix.
  - parity : even (0) if inner axis 'x' is followed by 'y', 'y' is followed
    by 'z', or 'z' is followed by 'x'. Otherwise odd (1).
  - repetition : first and last axis are same (1) or different (0).
  - frame : rotations are applied to static (0) or rotating (1) frame.

References
----------

(1)  Matrices and transformations. Ronald Goldman.
     In "Graphics Gems I", pp 472-475. Morgan Kaufmann, 1990.
(2)  More matrices and transformations: shear and pseudo-perspective.
     Ronald Goldman. In "Graphics Gems II", pp 320-323. Morgan Kaufmann, 1991.
(3)  Decomposing a matrix into simple transformations. Spencer Thomas.
     In "Graphics Gems II", pp 320-323. Morgan Kaufmann, 1991.
(4)  Recovering the data from the transformation matrix. Ronald Goldman.
     In "Graphics Gems II", pp 324-331. Morgan Kaufmann, 1991.
(5)  Euler angle conversion. Ken Shoemake.
     In "Graphics Gems IV", pp 222-229. Morgan Kaufmann, 1994.
(6)  Arcball rotation control. Ken Shoemake.
     In "Graphics Gems IV", pp 175-192. Morgan Kaufmann, 1994.
(7)  Representing attitude: Euler angles, unit quaternions, and rotation
     vectors. James Diebel. 2006.
(8)  A discussion of the solution for the best rotation to relate two sets
     of vectors. W Kabsch. Acta Cryst. 1978. A34, 827-828.
(9)  Closed-form solution of absolute orientation using unit quaternions.
     BKP Horn. J Opt Soc Am A. 1987. 4(4), 629-642.
(10) Quaternions. Ken Shoemake.
     http://www.sfu.ca/~jwa3/cmpt461/files/quatut.pdf
(11) From quaternion to matrix and back. JMP van Waveren. 2005.
     http://www.intel.com/cd/ids/developer/asmo-na/eng/293748.htm
(12) Uniform random rotations. Ken Shoemake.
     In "Graphics Gems III", pp 124-132. Morgan Kaufmann, 1992.


Examples
--------

>>> alpha, beta, gamma = 0.123, -1.234, 2.345
>>> origin, xaxis, yaxis, zaxis = (0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)
>>> I = identity_matrix()
>>> Rx = rotation_matrix(alpha, xaxis)
>>> Ry = rotation_matrix(beta, yaxis)
>>> Rz = rotation_matrix(gamma, zaxis)
>>> R = concatenate_matrices(Rx, Ry, Rz)
>>> euler = euler_from_matrix(R, 'rxyz')
>>> numpy.allclose([alpha, beta, gamma], euler)
True
>>> Re = euler_matrix(alpha, beta, gamma, 'rxyz')
>>> is_same_transform(R, Re)
True
>>> al, be, ga = euler_from_matrix(Re, 'rxyz')
>>> is_same_transform(Re, euler_matrix(al, be, ga, 'rxyz'))
True
>>> qx = quaternion_about_axis(alpha, xaxis)
>>> qy = quaternion_about_axis(beta, yaxis)
>>> qz = quaternion_about_axis(gamma, zaxis)
>>> q = quaternion_multiply(qx, qy)
>>> q = quaternion_multiply(q, qz)
>>> Rq = quaternion_matrix(q)
>>> is_same_transform(R, Rq)
True
>>> S = scale_matrix(1.23, origin)
>>> T = translation_matrix((1, 2, 3))
>>> Z = shear_matrix(beta, xaxis, origin, zaxis)
>>> R = random_rotation_matrix(numpy.random.rand(3))
>>> M = concatenate_matrices(T, R, Z, S)
>>> scale, shear, angles, trans, persp = decompose_matrix(M)
>>> numpy.allclose(scale, 1.23)
True
>>> numpy.allclose(trans, (1, 2, 3))
True
>>> numpy.allclose(shear, (0, math.tan(beta), 0))
True
>>> is_same_transform(R, euler_matrix(axes='sxyz', *angles))
True
>>> M1 = compose_matrix(scale, shear, angles, trans, persp)
>>> is_same_transform(M, M1)
True

    )divisionNzrestructuredtext enc                  H    [         R                  " S[         R                  S9$ )zReturn 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

   dtype)numpyidentityfloat64     X/home/james-whalen/.local/lib/python3.13/site-packages/pybullet_utils/transformations.pyidentity_matrixr      s     >>!5==11r   c                 J    [         R                  " S5      nU SS USS2S4'   U$ )zReturn matrix to translate by direction vector.

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

r   N   )r   r	   )	directionMs     r   translation_matrixr      s.     	qA!}Abqb!eHHr   c                 V    [         R                  " U SS9SS2S4   R                  5       $ )zReturn translation vector from translation matrix.

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

FcopyNr   )r   arrayr   matrixs    r   translation_from_matrixr      s+     ;;vE*2A2q516688r   c                     [        USS 5      n[        R                  " S5      nUSS2SS24==   S[        R                  " X5      -  -  ss'   S[        R                  " U SS U5      -  U-  USS2S4'   U$ )a  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

Nr   r          @)unit_vectorr   r	   outerdot)pointnormalr   s      r   reflection_matrixr"      s{    & $FqAbqb"1"fIu{{6222Ieiibq	622f<Abqb!eHHr   c                    [         R                  " U [         R                  SS9n[         R                  R	                  USS2SS24   5      u  p#[         R
                  " [        [         R                  " U5      S-   5      S:  5      S   n[        U5      (       d  [        S5      e[         R                  " USS2US   4   5      R                  5       n[         R                  R	                  U5      u  p#[         R
                  " [        [         R                  " U5      S-
  5      S:  5      S   n[        U5      (       d  [        S	5      e[         R                  " USS2US
   4   5      R                  5       nXfS   -  nXe4$ )a1  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

Fr   r   Nr         ?:0yE>r   z2no unit eigenvector corresponding to eigenvalue -11no unit eigenvector corresponding to eigenvalue 1)r   r   r
   linalgeigwhereabsreallen
ValueErrorsqueeze)r   r   lVir!   r    s          r   reflection_from_matrixr4      s.    	F%--e<A<<Abqb"1"fI&DAC

1+,t34Q7Aq66MNNZZ!QqT'
#++-F<<ADAC

1+,t34Q7Aq66LMMJJqAbE{#++-E	1XE=r   c                    [         R                  " U 5      n[         R                  " U 5      n[        USS 5      n[        R
                  " USS4SUS4SSU44[        R                  S9nU[        R                  " X5      SU-
  -  -  nX-  nU[        R
                  " SUS   * US   4US   SUS   * 4US   * US   S44[        R                  S9-  n[        R                  " S	5      nXVSS2SS24'   UbG  [        R
                  " USS [        R                  S
S9nU[        R                  " XR5      -
  USS2S4'   U$ )a  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

Nr           r   r%         r   r   Fr$   )
mathsincosr   r   r   r
   r   r	   r   )angler   r    sinacosaRr   s          r   rotation_matrixr@     sR   , 88E?D88E?DIbqM*IdC#&D#&C$')05	?A Y	*cDj	99AIil]Yq\B!!cYq\MB!!}ilS9; !==
* *A 	qAbqb"1"fIE"1IU]]G599Q.."1"a%Hr   c                    [         R                  " U [         R                  SS9nUSS2SS24   n[         R                  R	                  UR
                  5      u  p4[         R                  " [        [         R                  " U5      S-
  5      S:  5      S   n[        U5      (       d  [        S5      e[         R                  " USS2US	   4   5      R                  5       n[         R                  R	                  U5      u  p7[         R                  " [        [         R                  " U5      S-
  5      S:  5      S   n[        U5      (       d  [        S5      e[         R                  " USS2US	   4   5      R                  5       nXS   -  n[         R                  " U5      S-
  S
-  n	[        US   5      S:  a  US   U	S-
  US   -  US   -  -   US   -  n
OM[        US   5      S:  a  US   U	S-
  US   -  US   -  -   US   -  n
OUS   U	S-
  US   -  US   -  -   US   -  n
[        R                  " X5      nXU4$ )am  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

Fr$   Nr   r%   r&   r   r'   r(   r   r7   r8   r   r8   r   r7   r7   r8   )r   r   r
   r)   r*   Tr+   r,   r-   r.   r/   r0   tracer9   atan2)r   r?   R33r1   Wr3   r   Qr    r>   r=   r<   s               r   rotation_from_matrixrK   ?  s	    	F%--e<A
BQBF)C<<CEE"DAC

1+,t34Q7Aq66LMM

1Q"X;'//1I<<ADAC

1+,t34Q7Aq66LMMJJqAbE{#++-E	1XEKKs"c)D
9Q<4$48Yq\1)A,>>)A,N	Yq\	T	!$48Yq\1)A,>>)A,N$48Yq\1)A,>>)A,NJJt"EU""r   c                    UcX  [         R                  " U SSS4SU SS4SSU S4S4[         R                  S9nUb   USS USS2S4'   USS2S4==   SU -
  -  ss'   U$ [        USS 5      nSU -
  n [         R                  " S5      nUSS2SS24==   U [         R
                  " X"5      -  -  ss'   Ub'  U [         R                  " USS U5      -  U-  USS2S4'   U$ )a  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)

Nr6   r6   r6   r6   r%   r   r   r%   r   )r   r   r
   r   r	   r   r   )factororiginr   r   s       r   scale_matrixrP   g  s   " KK&###6&##6#&#668 @E}}N bqzAbqb!eHbqb!eHf$H H  	"1.	vNN1	"1"bqb&	Vekk)???	6"1:y!AAYNAbqb!eHHr   c                 \   [         R                  " U [         R                  SS9nUSS2SS24   n[         R                  " U5      S-
  n [         R                  R                  U5      u  pE[         R                  " [        [         R                  " U5      U-
  5      S:  5      S   S   n[         R                  " USS2U4   5      R                  5       nU[        U5      -  n[         R                  R                  U5      u  pE[         R                  " [        [         R                  " U5      S	-
  5      S:  5      S   n[        U5      (       d  [        S
5      e[         R                  " USS2US   4   5      R                  5       nXS   -  nX8U4$ ! [         a    US-   S-  nSn Nf = f)a%  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

Fr$   Nr   r   r&   r   g      @r%   ,no eigenvector corresponding to eigenvalue 1r(   )r   r   r
   rF   r)   r*   r+   r,   r-   r0   vector_norm
IndexErrorr.   r/   )	r   r   M33rN   r1   r2   r3   r   rO   s	            r   scale_from_matrixrV     so   $ 	F%--e<A
BQBF)C[[#F	||$KKEJJqMF23d:;A>qAJJqAw'//1	[++	 <<ADAC

1+,t34Q7Aq66GHHZZ!QrU($,,.F
QiF9$$  3,#%	s   
BF F+*F+c                 d   [         R                  " S5      n[         R                  " U SS [         R                  SS9n [	        USS 5      nUGb
  [         R                  " USS [         R                  SS9n[         R
                  " X0-
  U5      =US'   =US'   US'   USS2SS24==   [         R                  " X15      -  ss'   U(       aJ  USS2SS24==   [         R                  " X5      -  ss'   [         R
                  " X5      X1-   -  USS2S4'   O [         R
                  " X5      U-  USS2S4'   U* USSS24'   [         R
                  " X15      US	'   U$ Ub  [         R                  " USS [         R                  SS9n[         R
                  " X!5      nUSS2SS24==   [         R                  " X!5      U-  -  ss'   U[         R
                  " X5      U-  -  USS2S4'   U$ USS2SS24==   [         R                  " X5      -  ss'   [         R
                  " X5      U-  USS2S4'   U$ )
a(  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

r   Nr   Fr$   r   r   r8   r8   r7   r7   r   r   )r   r	   r   r
   r   r   r   )r    r!   r   perspectivepseudor   scales          r   projection_matrixr_     s   @ 	qAKKbq	UCE$Fkk+bq/',.&+ii0A6&JJ$J!D'AdG	"1"bqb&	U[[55	bqb"1"fIV44Iyy/;3EFAbqb!eHyy/+=Abqb!eH7!RaR%))K0$ H 
	KK	"1U]]O			),	"1"bqb&	U[[3e;;			% 85 @A"1"a%
 H 	
"1"bqb&	U[[00	99U+f4"1"a%Hr   c                    [         R                  " U [         R                  SS9nUSS2SS24   n[         R                  R	                  U5      u  pE[         R
                  " [        [         R                  " U5      S-
  5      S:  5      S   nU(       Gd  [        U5      (       Ga  [         R                  " USS2US   4   5      R                  5       nXwS   -  n[         R                  R	                  U5      u  pE[         R
                  " [        [         R                  " U5      5      S:  5      S   n[        U5      (       d  [        S	5      e[         R                  " USS2US   4   5      R                  5       nU[        U5      -  n[         R                  R	                  UR                  5      u  pE[         R
                  " [        [         R                  " U5      5      S:  5      S   n[        U5      (       aB  [         R                  " USS2US   4   5      R                  5       n	U	[        U	5      -  n	XyUSS4$ XxSSS4$ [         R
                  " [        [         R                  " U5      5      S:  5      S   n[        U5      (       d  [        S
5      e[         R                  " USS2US   4   5      R                  5       nXwS   -  nUSSS24   * n	USS2S4   [         R                  " USS U	5      -  n
U(       a  X-  n
XySX4$ )a.  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

Fr$   Nr   r%   r&   r   r(   z,no eigenvector corresponding to eigenvalue 0z0no eigenvector not corresponding to eigenvalue 0)r   r   r
   r)   r*   r+   r,   r-   r.   r0   r/   rS   rE   r   )r   r]   r   rU   r1   r2   r3   r    r   r!   r\   s              r   projection_from_matrixra     s   @ 	F%--e<A
BQBF)C<<ADAC

1+,t34Q7Ac!ff

1Q"X;'//1q||$KKEJJqM*T12151vvKLLJJqAaDz*224	[++	||&KKEJJqM*T1215q66ZZ!QqT'
+335Fk&))F)T588 T466 KKEJJqM*T12151vvBD D

1Q"X;'//1qQU8Ah5!9f!==!KdK77r   c                    X:  d
  X#:  d  XE:  a  [        S5      eU(       aV  U[        ::  a  [        S5      eSU-  nU* X-
  -  SX-   X-
  -  S4SU* X2-
  -  X2-   X2-
  -  S4SSXT-   * XT-
  -  Xu-  XT-
  -  4S4nO6SX-
  -  SSX-   X-
  -  4SSX2-
  -  SX2-   X#-
  -  4SSSXT-
  -  XT-   XE-
  -  4S4n[        R                  " U[        R                  S9$ )a  Return matrix to obtain normalized device coordinates from frustrum.

The frustrum 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 frustrum.

If perspective is True the frustrum 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 (devided by w coordinate).

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

zinvalid frustrumzinvalid frustrum: near <= 0r   r6   )r6   r6         r6   rM   r   )r/   _EPSr   r   r
   )	leftrightbottomtopnearfarr\   tr   s	            r   clip_matrixrl   <  s   D }+,,4<:;;$Jb%*osUZ%*$=sCA2sz?SZ#*$=sC3#(SX.sx0@A"$
 5:S5:
*CD3
#S3:
*CD3SXDH(=>!# ;;q..r   c                    [        USS 5      n[        USS 5      n[        [        R                  " X15      5      S:  a  [	        S5      e[
        R                  " U 5      n [        R                  " S5      nUSS2SS24==   U [        R                  " X5      -  -  ss'   U * [        R                  " USS U5      -  U-  USS2S4'   U$ )a  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

Nr   gư>z/direction and normal vectors are not orthogonalr   )	r   r,   r   r   r/   r9   tanr	   r   )r<   r   r    r!   r   s        r   shear_matrixro   p  s    ( $FIbqM*I
599V'(4/JKKHHUOEqAbqb"1"fIY777Iv		%)V44y@Abqb!eHHr   c                 d   [         R                  " U [         R                  SS9nUSS2SS24   n[         R                  R	                  U5      u  p4[         R
                  " [        [         R                  " U5      S-
  5      S:  5      S   n[        U5      S:  a  [        S	R                  U5      5      e[         R                  " USS2U4   5      R                  5       R                  nS
nS H6  u  px[         R                  " XG   XH   5      n	[        U	5      nX6:  d  M2  UnU	n
M8     W
U-  n
[         R                  " U[         R                   " S5      -
  U
5      n[        U5      nX-  n["        R$                  " U5      n[         R                  R	                  U5      u  p4[         R
                  " [        [         R                  " U5      S-
  5      S:  5      S   n[        U5      (       d  [        S5      e[         R                  " USS2US   4   5      R                  5       nXS   -  nXX4$ )a  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

Fr$   Nr   r%   g-C6?r   r7   z/No two linear independent eigenvectors found {}rc   )r   r8   rC   r8   r7   r&   rR   r(   )r   r   r
   r)   r*   r+   r,   r-   r.   r/   formatr0   rE   crossrS   r   r	   r9   atan)r   r   rU   r1   r2   r3   lenormi0i1nr!   r   r<   r    s                 r   shear_from_matrixrz     s    	F%--e<A
BQBF)C<<C DAC

1+,t34Q7A
1vzJQQRSTUU

1QT7##%''AF*KKqu%N:FF + fF		#q 116:I	"EIIIeE<<ADAC

1+,t34Q7Aq66GHHJJqAbE{#++-E	1XEU**r   c                 X   [         R                  " U [         R                  SS9R                  n[	        US   5      [
        :  a  [        S5      eXS   -  nUR                  5       nSUSS2S4'   [         R                  R                  U5      (       d  [        S5      e[         R                  " S	[         R                  S
9n/ SQn/ SQn[        [	        USS2S4   5      [
        :  5      (       aO  [         R                  " USS2S4   [         R                  R                  UR                  5      5      nSUSS2S4'   O#[         R                  " S[         R                  S
9nUSSS24   R                  5       nSUSSS24'   USS2SS24   R                  5       n[        US   5      US'   US==   US   -  ss'   [         R                  " US   US   5      US'   US==   US   US   -  -  ss'   [        US   5      US'   US==   US   -  ss'   US==   US   -  ss'   [         R                  " US   US   5      US'   US==   US   US   -  -  ss'   [         R                  " US   US   5      US'   US==   US   US   -  -  ss'   [        US   5      US'   US==   US   -  ss'   USS=== US   -  sss& [         R                  " US   [         R                  " US   US   5      5      S:  a
  US-  nUS-  n[         R"                  " US   * 5      US'   [         R$                  " US   5      (       aA  [         R&                  " US   US   5      US'   [         R&                  " US   US   5      US'   O&[         R&                  " US   * US   5      US'   SUS'   X4XWU4$ )a  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

Tr$   r[   zM[3, 3] is zeror   r   r   r8   Nr   zMatrix is singular)r   r   )r   r   r   r   r8   r7   r(   rC   rr   rZ   rq   rX   rD   rY   r6   )r   r   r
   rE   r,   rd   r/   r   r)   detzerosanyr   invrS   rt   r9   asinr;   rG   )	r   r   Pr^   shearanglesr\   	translaterows	            r   decompose_matrixr     sZ   > 	F%--d;==A
1T7|d*++4LA	AAadG<<A-..KKU]]3EEF
3q!Qx=4  ii!Q$)9)9!##)>?!Q$kk,emmD!RaR%IAa!eH
BQBF)..
C3q6"E!HFeAhFyyQQ(E!HFc!fuQxF3q6"E!HFeAhF	!HaHyyQQ(E!HFc!fuQxFyyQQ(E!HFc!fuQxF3q6"E!HFeAhF	!"IqIyyQSVSV459r			3t9*%F1Ixxq	JJs4y#d)4q	JJs4y#d)4q	 JJD	z3t95q	q	K77r   c                    [         R                  " S5      nUb8  [         R                  " S5      nUSS USSS24'   [         R                  " XV5      nUb8  [         R                  " S5      nUSS USS2S4'   [         R                  " XW5      nUb-  [        US   US   US   S5      n[         R                  " XX5      nUbD  [         R                  " S5      n	US   U	S'   US   U	S	'   US   U	S
'   [         R                  " XY5      nU bD  [         R                  " S5      n
U S   U
S'   U S   U
S'   U S   U
S'   [         R                  " XZ5      nXUS   -  nU$ )a  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

r   Nr   r   r8   r7   sxyzrr   rC   rq   rX   rY   rZ   r[   )r   r	   r   euler_matrix)r^   r   r   r   r\   r   r   rE   r?   ZSs              r   compose_matrixr     sM   2 	qANN1bq/!Q$IIaONN1Ra="1"a%IIaOF1Ivay&AIIaONN1($($($IIaONN1($($($IIaO4LAHr   c                 p   U u  p#n[         R                  " U5      n[         R                  " U5      u  pVn[         R                  " U5      u  pn
X-  U
-
  XV-  -  n[         R                  " X&-  [
        R                  " SX-  -
  5      -  SSS4U* U-  U-  X5-  SS4X)-  X8-  US4S4[         R                  S9$ )a  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

r%   r6   rM   r   )r   radiansr:   r;   r   r9   sqrtr
   )lengthsr   abcr=   sinb_r>   cosbcosgcos               r   orthogonalization_matrixr   F  s     GA!]]6"FIIf%MDyy(D
+
	-B;;
&3ru9%
%S9
DS9
&S99	;
 mm r   c           	         [         R                  " U [         R                  SS9SS n [         R                  " U[         R                  SS9SS nU R                  UR                  :w  d  U R                  S   S:  a  [	        S5      e[         R
                  " U SS9n[         R
                  " USS9nXR                  SS5      -
  n XR                  SS5      -
  nU(       a  [         R                  R                  [         R                  " XR                  5      5      u  pgn[         R                  " Xh5      n	[         R                  R                  U	5      S:  a8  U	[         R                  " USS2S	4   US	SS24   S
-  5      -  n	US==   S-  ss'   [         R                  " S5      n
XSS2SS24'   GO:[         R                  " X-  SS9u  pn[         R                  " U [         R                  " USSS9-  SS9u  pn[         R                  " U [         R                  " USSS9-  SS9u  nnnX-   U-   UU-
  UU-
  UU-
  4UU-
  X-
  U-
  UU-   UU-   4UU-
  UU-   U* U-   U-
  UU-   4UU-
  UU-   UU-   U* U-
  U-   44n[         R                  R!                  U5      u  nnUSS2[         R"                  " U5      4   nU[%        U5      -  n[         R                  " US5      n['        U5      n
U(       aZ  X -  n X-  nU
SS2SS24==   [(        R*                  " [         R                  " U5      [         R                  " U 5      -  5      -  ss'   XZSS2S4'   [         R                  " S5      nU* USS2S4'   [         R                  " U
U5      n
U
$ )aA  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

Fr$   Nr   r8   z'Vector sets are of wrong shape or type.axisr6   r7   r   r(   rc   r   r   )r   r   r
   shaper/   meanreshaper)   svdr   rE   r}   r   r	   sumrollr*   argmaxrS   quaternion_matrixr9   r   )v0v1scalingusesvdt0t1usvhr?   r   xxyyzzxyyzzxxzyxzyNr1   r2   qrE   s                            r   superimposition_matrixr   b  s,   ^ 
Ru}}5	9"1	=B	Ru}}5	9"1	=B	xx288rxx{QBCC 
BQ	B	BQ	B	jjA	B	jjA	B<<##EIIb$$$78bIIa<<A$Qq!tWbAhsl33AbETMENN1"1"bqb&	 YYrwQ/
YYrEJJr2A$>>QG
YYrEJJr2A$>>QG
BeBh2"R%BrE2eb"R%BrE2e2"RBrE2e2"R%B3r6"957
 ||"1aa !	[^JJq"a  

	"1"bqb&	TYYuyy}uyy}<==	 bqb!eHqAsAbqb!eH		!QAHr   r   c                     [         U   u  pEpgUn	[        X-      n
[        X-
  S-      nU(       a  X p U(       a  U * U* U* p!n [
        R                  " U 5      [
        R                  " U5      [
        R                  " U5      pn[
        R                  " U 5      [
        R                  " U5      [
        R                  " U5      nnnUU-  X-  nnUU-  X-  nn[        R                  " S5      nU(       a\  UUX4'   X-  UX4'   X-  UX4'   X-  UX4'   U* U-  U-   UX4'   U* U-  U-
  UX4'   U* U-  UX4'   UU-  U-   UX4'   UU-  U-
  UX4'   U$ UU-  UX4'   UU-  U-
  UX4'   UU-  U-   UX4'   UU-  UX4'   UU-  U-   UX4'   UU-  U-
  UX4'   U* UX4'   UU-  UX4'   UU-  UX4'   U$ ! [        [        4 a    [        U   nUu  pEpg GNf = f)aQ  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)

r8   r   )
_AXES2TUPLEAttributeErrorKeyError_TUPLE2AXES
_NEXT_AXISr9   r:   r;   r   r	   )aiajakaxes	firstaxisparity
repetitionframer   r3   jksisjskcicjckcccsscssr   s                          r   r   r     s/   &4/:4/@,	:
 	A18A18A:ABS2#s"txx|TXXb\BB"txx|TXXb\BBUBEBUBEBqA!$%!$%!$%!$#b&)!$#b&)!$#b&!$R%(!$R%(!$ H R%!$R%(!$R%(!$R%!$R%(!$R%(!$#!$R%!$R%!$HO H% 4/3,	:u4s   F, ,GGc                     [         UR                  5          u  p#pEUn[
        Xs-      n[
        Xs-
  S-      n	[        R                  " U [        R                  SS9SS2SS24   n
U(       a  [        R                  " XU4   XU4   -  XU	4   XU	4   -  -   5      nU[        :  a[  [        R                  " XU4   XU	4   5      n[        R                  " XXw4   5      n[        R                  " XU4   XU4   * 5      nGO[        R                  " XU	4   * XU4   5      n[        R                  " XXw4   5      nSnO[        R                  " XU4   XU4   -  XU4   XU4   -  -   5      nU[        :  a[  [        R                  " XU4   XU	4   5      n[        R                  " XU4   * U5      n[        R                  " XU4   XU4   5      nO>[        R                  " XU	4   * XU4   5      n[        R                  " XU4   * U5      nSnU(       a  U* U* U* pnU(       a  XpXU4$ ! [        [        4 a    [        U   nUu  p#pE GN/f = f)al  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"

r8   Fr$   Nr   r6   )r   lowerr   r   r   r   r   r   r
   r9   r   rd   rG   )r   r   r   r   r   r   r   r3   r   r   r   syaxayazcys                   r   euler_from_matrixr     s@   &4/:4::</H,	:
 	A18A18A:AF%--e<RaR!VDAYYqAwqAwa4a4899Q!tWqAw/BRw/BQ!tWqAwh/BQ!tWHqAw/BRw/BBYYqAwqAwa4a4899Q!tWqAw/BQ!tWHr*BQ!tWqAw/BQ!tWHqAw/BQ!tWHr*BBS2#sB2:E H% 4/3,	:u4s   H* *IIc                 ,    [        [        U 5      U5      $ )zReturn 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

)r   r   )
quaternionr   s     r   euler_from_quaternionr   A  s     .z:DAAr   c                 v    [         UR                  5          u  pEpgUn	[
        X-      n
[
        X-
  S-      nU(       a  X p U(       a  U* nU S-  n US-  nUS-  n[        R                  " U 5      n[        R                  " U 5      n[        R                  " U5      n[        R                  " U5      n[        R                  " U5      n[        R                  " U5      nUU-  nUU-  nUU-  nUU-  n[        R                  " S[        R                  S9nU(       a-  UUU-   -  UU	'   UUU-   -  UU
'   UUU-
  -  UU'   UUU-
  -  US'   O8UU-  UU-  -
  UU	'   UU-  UU-  -   UU
'   UU-  UU-  -
  UU'   UU-  UU-  -   US'   U(       a  UU
==   S-  ss'   U$ ! [        [        4 a    [        U   nUu  pEpg GNf = f)a  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

r8   r   r   r   r   r(   )r   r   r   r   r   r   r9   r;   r:   r   emptyr
   )r   r   r   r   r   r   r   r   r   r3   r   r   r   r   r   r   r   r   r   r   r   r   r   s                          r   quaternion_from_eulerr   L  s   4/:4::</H,	:
 	A18A18A:ABS#IB#IB#IB	"B	"B	"B	"B	"B	"B	BB	BB	BB	BBU%--8JBG
1BG
1BG
1BG
122
122
122
122
11S H% 4/3,	:u4s   F F87F8c                     [         R                  " S[         R                  S9nUSS USS& [        U5      nU[        :  a  U[
        R                  " U S-  5      U-  -  n[
        R                  " U S-  5      US'   U$ )zReturn quaternion for rotation about axis.

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

r   r   Nr   r   )r   r~   r
   rS   rd   r9   r:   r;   )r<   r   r   qlens       r   quaternion_about_axisr     sr     U%--8J"1XJrNz"Dd{dhhuSy)D00
HHU3Y'JqMr   c           	      :   [         R                  " U SS [         R                  SS9n[         R                  " X5      nU[        :  a  [         R
                  " S5      $ U[        R                  " SU-  5      -  n[         R                  " X5      n[         R                  " SUS   -
  US   -
  US	   US
   -
  US   US   -   S4US	   US
   -   SUS   -
  US   -
  US   US   -
  S4US   US   -
  US   US   -   SUS   -
  US   -
  S4S4[         R                  S9$ )zReturn 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

Nr   Tr$   r   r%   rY   rZ   rq   )r7   r   rC   )r8   r   r6   rX   rr   )r   r   rM   r   )	r   r   r
   r   rd   r	   r9   r   r   )r   r   nqs      r   r   r     s)    	JrN%--dCA	1B	Dy~~a  38	AAA;;	QtWQtW	!D'!D'/qwqwLtWQtW_c!D'k!D'1qwqwLtWQtW_!D'!D'/3qw;qw3FLL	

    r   c                 j   [         R                  " S[         R                  S9n[         R                  " U [         R                  SS9SS2SS24   n[         R                  " U5      nX2S   :  a/  X1S'   US	   US
   -
  US'   US   US   -
  US'   US   US   -
  US'   OtSu  pEnUS   US   :  a  Su  pEnUS   X$U4   :  a  Su  pEnX$U4   X%U4   X&U4   -   -
  US   -   nX1U'   X$U4   X%U4   -   X'   X&U4   X$U4   -   X'   X&U4   X%U4   -
  US'   US[
        R                  " X2S   -  5      -  -  nU$ )zReturn 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

r   r   Fr$   Nr   r[   r   rB   rq   r7   rC   )r7   r   r8   rD   rr   r   )r   r8   r7   rY   rX   )r8   r7   r   rZ   )r7   r   r8   g      ?)r   r   r
   r   rF   r9   r   )r   r   r   rk   r3   r   r   s          r   quaternion_from_matrixr     sy    	E/AF%--e<RaR!VDAAAT7{!w4 !w4 !w4 !aT7QtWGA!T7Q!tWGA!dGqAwa4()AdG3!Awa4 Awa4 Awa4 !tyytW%	%%AHr   c                     Uu  p#pEU u  pgp[         R                  " Xe-  Xt-  -   X-  -
  X-  -   U* U-  Xu-  -   X-  -   X-  -   Xc-  Xr-  -
  X-  -   X-  -   U* U-  Xs-  -
  X-  -
  X-  -   4[         R                  S9$ )zReturn multiplication of two quaternions.

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

r   r   r   r
   )
quaternion1quaternion0x0y0z0w0x1y1z1w1s
             r   quaternion_multiplyr     s     !NBB NBB;;			&	B&			&	B&	( 05}}	> >r   c                 n    [         R                  " U S   * U S   * U S   * U S   4[         R                  S9$ )zReturn conjugate of quaternion.

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

r   r8   r7   r   r   r   r   s    r   quaternion_conjugater     sB     ;;AA#A
17>CmmM Mr   c                 F    [        U 5      [        R                  " X 5      -  $ )zReturn inverse of quaternion.

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

)r   r   r   r   s    r   quaternion_inverser     s      
+eii
.OOOr   c                 .   [        U SS 5      n[        USS 5      nUS:X  a  U$ US:X  a  U$ [        R                  " XV5      n[        [        U5      S-
  5      [        :  a  U$ U(       a  US:  a  U* nUS-  n[
        R                  " U5      U[
        R                  -  -   n[        U5      [        :  a  U$ S[
        R                  " U5      -  n	U[
        R                  " SU-
  U-  5      U	-  -  nU[
        R                  " X(-  5      U	-  -  nXV-  nU$ )a  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

Nr   r6   r%   rc   )	r   r   r   r,   rd   r9   acospir:   )
quat0quat1fractionspinshortestpathq0q1dr<   isins
             r   quaternion_slerpr
    s   $ 
U2AY	B	U2AY	B3		S			"A
3q6C<4	CB
d
IIaL4$''>)E
5zD	% D$((C(Ne+
,t
33B$((8#
$t
++BHBIr   c                    U c   [         R                  R                  S5      n O[        U 5      S:X  d   e[         R                  " SU S   -
  5      n[         R                  " U S   5      n[
        R                  S-  nX0S   -  nX0S   -  n[         R                  " [         R                  " U5      U-  [         R                  " U5      U-  [         R                  " U5      U-  [         R                  " U5      U-  4[         R                  S9$ )a*  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,)

r   r%   r   r   r8   r7   r   )r   randomrandr.   r   r9   r   r   r:   r;   r
   )r  r1r2pi2r   t2s         r   random_quaternionr    s     |||  #4yA~~	C$q'M	"B	DG	B
''C-C	AwB	AwB;;		"b(		"b(		"b(		"b(* 27@ @r   c                 *    [        [        U 5      5      $ )a  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

)r   r  )r  s    r   random_rotation_matrixr  =  s     .t455r   c                   V    \ rS rSrSrSS jrS rS rS rS r	S	 r
S
 rSS jrS rSrg)ArcballiL  a  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()

Nc                    SU l         SU l        SU l        SS/U l        [        R
                  " / SQ[        R                  S9U l        SU l        Uc+  [        R
                  " / SQ[        R                  S9U l	        Ot[        R
                  " U[        R                  S9nUR                  S:X  a  [        U5      U l	        O0UR                  S	:X  a  U[        U5      -  nXl	        O[        S
5      eU R                  =U l        U l        g)zPInitialize virtual trackball control.

initial : quaternion or rotation matrix

Nr%   r6   )r   r   r8   r   Fr|   )r   r   r   z#initial not a quaternion or matrix.)_axis_axes_radius_centerr   r   r
   _vdown
_constrain_qdownr   r   rS   r/   _qnow_qpre)selfinitials     r   __init__Arcball.__init__d  s     

Szkk)5==A?++l%--HDKkk'?G}}&4W=%';w//% !FGG"&++-
TZr   c                 l    [        U5      U l        US   U R                  S'   US   U R                  S'   g)zPlace Arcball, e.g. when window size changes.

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

r   r8   N)floatr  r  )r!  centerradiuss      r   placeArcball.place  s1     V} )Q )Qr   c                 f    Uc  SU l         gU Vs/ s H  n[        U5      PM     snU l         gs  snf )z Set axes to constrain rotations.N)r  r   )r!  r   r   s      r   setaxesArcball.setaxes  s,    <DJ8<=+d+=DJ=s   .c                     US:H  U l         g)z$Set state of constrain to axis mode.TNr  )r!  	constrains     r   setconstrainArcball.setconstrain  s    #t+r   c                     U R                   $ )z'Return state of constrain to axis mode.r/  r!  s    r   getconstrainArcball.getconstrain  s    r   c                 ^   [        XR                  U R                  5      U l        U R                  =U l        U l        U R                  (       aX  U R                  bK  [        U R                  U R                  5      U l
        [        U R                  U R                  5      U l        gSU l
        g)z>Set initial cursor window coordinates and pick constrain-axis.N)arcball_map_to_spherer  r  r  r  r  r   r  r  arcball_nearest_axisr  arcball_constrain_to_axis)r!  r    s     r   downArcball.down  sm    +E<<N#'::-dj??tzz5-dkk4::FDJ3DKKLDKDJr   c                    [        XR                  U R                  5      nU R                  b  [	        X R                  5      nU R
                  U l        [        R                  " U R                  U5      n[        R                  " X35      [        :  a  U R                  U l        gUS   US   US   [        R                  " U R                  U5      /n[        X@R                  5      U l        g)z)Update current cursor window coordinates.Nr   r8   r7   )r8  r  r  r  r:  r  r   r   rt   r  r   rd   r  r   )r!  r    vnowrk   r   s        r   dragArcball.drag  s    $ULL$,,G::!,T::>DZZ
KKT*99Q?T!DJ1qtQqT599T[[$#?@A,Q<DJr   c                     [        U R                  U R                  SU-   S5      nU R                  UsU l        U l        g)z,Continue rotation in direction of last drag.r   FN)r
  r   r  )r!  accelerationr   s      r   nextArcball.next  s3    TZZS5EuM!%Q
DJr   c                 ,    [        U R                  5      $ )z#Return homogeneous rotation matrix.)r   r  r4  s    r   r   Arcball.matrix  s     ,,r   )	r  r  r  r  r  r  r   r  r  N)r6   )__name__
__module____qualname____firstlineno____doc__r#  r)  r,  r1  r5  r;  r?  rC  r   __static_attributes__r   r   r   r  r  L  s4    ..6$>,	= /
-r   r  c                 *   [         R                  " U S   US   -
  U-  US   U S   -
  U-  S4[         R                  S9nUS   US   -  US   US   -  -   nUS:  a  U[        R                  " U5      -  nU$ [        R                  " SU-
  5      US'   U$ )z7Return unit sphere coordinates from window coordinates.r   r8   r6   r   r%   r7   )r   r   r
   r9   r   )r    r'  r(  vry   s        r   r8  r8    s    eAh*f4Qi%(*f4!&	0A 	
!QqT	AaD1IA3w	TYYq\ H yyq!!Hr   c                    [         R                  " U [         R                  SS9n[         R                  " U[         R                  SS9nX#[         R                  " X25      -  -  n[	        U5      nU[
        :  a  US   S:  a  US-  nX$-  nU$ US   S:X  a%  [         R                  " / SQ[         R                  S9$ [        US	   * US
   S
/5      $ )z*Return sphere point perpendicular to axis.Tr$   r7   r6   rc   r%   )r8   r   r   r   r8   r   )r   r   r
   r   rS   rd   r   )r    r   rO  r   ry   s        r   r:  r:    s    ET:ADD9AUYYq_	AAA4xQ4#:IA	ts{{{9EMM::1qtQ'((r   c                     [         R                  " U [         R                  SS9n SnSnU H.  n[         R                  " [	        X5      U 5      nXS:  d  M*  UnUnM0     U$ )z+Return axis, which arc is nearest to point.Fr$   Nrc   )r   r   r
   r   r:  )r    r   nearestmxr   rk   s         r   r9  r9    sX    KKU]]?EG	BII/<eD6GB	 
 Nr   g      @)r8   r7   r   r8   )r   r   r   r   sxyx)r   r   r8   r   sxzy)r   r8   r   r   sxzx)r   r8   r8   r   syzx)r8   r   r   r   syzy)r8   r   r8   r   syxz)r8   r8   r   r   syxy)r8   r8   r8   r   szxy)r7   r   r   r   szxz)r7   r   r8   r   szyx)r7   r8   r   r   szyz)r7   r8   r8   r   rzyxr|   rxyx)r   r   r8   r8   ryzx)r   r8   r   r8   rxzx)r   r8   r8   r8   rxzy)r8   r   r   r8   )r8   r   r8   r8   )r8   r8   r   r8   )r8   r8   r8   r8   )r7   r   r   r8   )r7   r   r8   r8   )r7   r8   r   r8   )r7   r8   r8   r8   )ryzyrzxyryxyryxzrzxzrxyzrzyzc              #   ,   #    U  H
  u  pX!4v   M     g 7frG  r   ).0r   rO  s      r   	<genexpr>rm    s     :&9daA6&9s   c                    [         R                  " U [         R                  SS9n Uc~  U R                  S:X  a*  [        R
                  " [         R                  " X 5      5      $ X -  n [         R                  " [         R                  " XS95      n[         R
                  " X"5        U$ X -  n [         R                  " XUS9  [         R
                  " X"5        g)al  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

Tr$   Nr8   r   )r   out)	r   r   r
   ndimr9   r   r   
atleast_1dr   )datar   ro  s      r   rS   rS     s    2 ;;t5==t<D
{99>99UYYt233uyy9:

3
		$s+

3r   c                    Ucd  [         R                  " U [         R                  SS9n U R                  S:X  a/  U [        R
                  " [         R                  " X 5      5      -  n U $ OX La  [         R                  " U SS9USS& Un [         R                  " [         R                  " X -  U5      5      n[         R
                  " X35        Ub  [         R                  " X15      nX-  n Uc  U $ g)a  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]

NTr$   r8   Fr   )
r   r   r
   rp  r9   r   r   rq  r   expand_dims)rr  r   ro  lengths       r   r   r   &  s    4 {{{4u}}4@99>DIIeii344DK  ?[[E2CFeii	489F	JJv""60ND
{ r   c                 @    [         R                  R                  U 5      $ )zReturn 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

)r   r  )sizes    r   random_vectorrx  R  s     <<t$$r   c                 @    [         R                  R                  U 5      $ )aR  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

)r   r)   r   r   s    r   inverse_matrixrz  a  s     <<F##r   c                  p    [         R                  " S5      nU  H  n[         R                  " X5      nM     U$ )zReturn 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

r   )r   r	   r   )matricesr   r3   s      r   concatenate_matricesr}  q  s.     	qAIIaO Hr   c                     [         R                  " U [         R                  SS9n X S   -  n [         R                  " U[         R                  SS9nXS   -  n[         R                  " X5      $ )zReturn 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

Tr$   r[   )r   r   r
   allclose)matrix0matrix1s     r   is_same_transformr    sW     kk'TBGt}Gkk'TBGt}G>>'++r   c                     [        U 5      n[        U5       H  nU(       a  UR                  U5      (       a  M"  U(       aI  U[        5       ;   a  [        5       U   [        5       X%-   '   O U(       a  [        R
                  " SU-   5        [        XE5      [        5       U'   M     g! [         a%    U(       a  [        R
                  " SU -   5         g gf = f)zTry import all public attributes from module into global namespace.

Existing attributes with name clashes are renamed with prefix.
Attributes starting with underscore are ignored by default.

Return True on successful import.

zNo Python implementation of TzFailed to import module N)
__import__dir
startswithglobalswarningswarngetattrImportError)module_namer  prefixignoremoduleattrs         r   _import_moduler    s    K(
 KD$//&1179$/6yGIfm,MM"@4"GH%f3GIdO     DMM4{BC Ds   B% %*CCrG  )NN)NNF)F)NNNNN)FT)r   )r   T)T_py_r   );rL  
__future__r   r  r9   r   __docformat__r   r   r   r"   r4   r@   rK   rP   rV   r_   ra   rl   ro   rz   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r
  r  r  objectr  r8  r:  r9  finfor&  epsrd   r   r   dictitemsr   rS   r   rx  rz  r}  r  r  r   r   r   <module>r     sY  @GR      &2
	94:)X%#P"J&%R 04/4;|F8R1/h>*+ZR8j CG#2j8cL<~7tB6r" ,@>"
M	P&R@<6q-f q-h
) 
 {{5# 
F
LF ,F06F
LF ,F06F LF !,F 17F L	F !,	F 17	F
 LF
 !,F
 17F LF !,F 9E,,F :k&7&7&9::$N)X%$  , r   