Ë
    :vžj1$ ã            
       ó’  — d Z ddlZddlmZmZ ddlmZmZm	Z	 ddl
mZmZ  ej                  d«      Zdej                  d	<   d
„ Zd„ Zd„ Zd„ Zd„ Zdvd„Zd„ Zdwd„Zd„ Zdxd„Zdyd„Zdyd„Zd„ Zd„ Zd„ Zdzd„Zd„ Z d{d„Z!d|d„Z"d}d„Z#d}d„Z$d}d „Z%d}d!„Z&d"„ Z'd#„ Z(dyd$„Z)d%„ Z*d&„ Z+d'„ Z,d(„ Z-d)„ Z.d~d*„Z/dd+e	fd,„Z0	 	 	 	 d€d-edz  d.ed/edz  d+e	fd0„Z1 G d1„ d2«      Z2d3„ Z3d4„ Z4d5„ Z5 ejl                  e7«      jp                  d6z  Z9g d7¢Z:i dd8“d9d:“d;d<“d=d>“d?d@“dAdB“dCdD“dEdF“dGdH“dIdJ“dKdL“dMdN“dOdP“dQdR“dSdT“dUdV“dWdX“dYdZd[d\d]d^d_d`œ¥Z;e;jy                  «       D � �ci c]  \  } }|| “Œ
 c}} Z=dwda„Z>dwdb„Z?dvd+e	fdc„Z@d�dd„ZAd‚de„ZBdf„ ZCdg„ ZDdh„ ZEdi„ ZFdj„ ZGdƒdk„ZHdl„ ZId}dm„ZJ	 d„dnedoed/eKdpeej˜                     fdq„ZMd…dr„ZNd†ds„ZOdwdt„ZPdu„ ZQyc c}} w )‡a$  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.

:Author:
  `Christoph Gohlke <http://www.lfd.uci.edu/~gohlke/>`_

:Organization:
  Laboratory for Fluorescence Dynamics, University of California, Irvine

:Version: 2017.02.17

Requirements
------------
* `CPython 2.7 or 3.4 <http://www.python.org>`_
* `numpy 1.9 <http://www.np.org>`_
* `Transformations.c 2015.03.19 <http://www.lfd.uci.edu/~gohlke/>`_
  (recommended for speedup of some functions)

Notes
-----
The API is not stable yet and is expected to change between revisions.

This Python code is not optimized for speed. Refer to the transformations.c
module for a faster implementation of some functions.

Documentation in HTML format can be generated with epydoc.

Matrices (M) can be inverted using np.linalg.inv(M), be concatenated using
np.dot(M0, M1), or transform homogeneous coordinate arrays (v) using
np.dot(M, v) for shape (4, *) column vectors, respectively
np.dot(v, M.T) for shape (*, 4) row vectors ("array of points").

This module follows the "column vectors on the right" and "row major storage"
(C contiguous) conventions. The translation components are in the right column
of the transformation matrix, i.e. M[:3, 3].
The transpose of the transformation matrices may have to be used to interface
with other graphics systems, e.g. with OpenGL's glMultMatrixd(). See also [16].

Calculations are carried out with np.float64 precision.

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 w+ix+jy+kz are represented as [w, x, y, z].

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.

Other Python packages and modules for 3D transformations and quaternions:

* `Transforms3d <https://pypi.python.org/pypi/transforms3d>`_
   includes most code of this module.
* `Blender.mathutils <http://www.blender.org/api/blender_python_api>`_
* `numpy-dtypes <https://github.com/numpy/numpy-dtypes>`_

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.
(13) Quaternion in molecular modeling. CFF Karney.
     J Mol Graph Mod, 25(5):595-604
(14) New method for extracting the quaternion from a rotation matrix.
     Itzhack Y Bar-Itzhack, J Guid Contr Dynam. 2000. 23(6): 1085-1087.
(15) Multiple View Geometry in Computer Vision. Hartley and Zissermann.
     Cambridge University Press; 2nd Ed. 2004. Chapter 4, Algorithm 4.7, p 130.
(16) Column Vectors vs. Row Vectors.
     http://steve.hollasch.net/cgindex/math/matrix/column-vec.html

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')
>>> np.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(np.random.rand(3))
>>> M = concatenate_matrices(T, R, Z, S)
>>> scale, shear, angles, trans, persp = decompose_matrix(M)
>>> np.allclose(scale, 1.23)
True
>>> np.allclose(trans, [1, 2, 3])
True
>>> np.allclose(shear, [0, np.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
>>> v0, v1 = random_vector(3), random_vector(3)
>>> M = rotation_matrix(angle_between_vectors(v0, v1), vector_product(v0, v1))
>>> v2 = np.dot(v0, M[:3,:3].T)
>>> np.allclose(unit_vector(v1), unit_vector(v2))
True

é    N)Ú	ArrayLikeÚNDArrayé   )ÚIntegerÚNumberÚSeed)Údiagonal_dotÚrandom_generatoré   FÚ	WRITEABLEc                  ó,   — t        j                  d«      S )zßReturn 4x4 identity/unit matrix.

    >>> I = identity_matrix()
    >>> np.allclose(I, np.dot(I, I))
    True
    >>> float(np.sum(I)), float(np.trace(I))
    (4.0, 4.0)
    >>> np.allclose(I, np.identity(4))
    True

    r   )ÚnpÚidentity© ó    úh/workspace/moltex-experiments/.pixi/envs/default/lib/python3.12/site-packages/trimesh/transformations.pyÚidentity_matrixr   Ò   s   € ô �;‰;�q‹>Ðr   c                 ó¬   — t        | «      }t        d„ | D «       «      rddlm}  ||dz   «      }nt	        j                  |dz   «      }| d| |d|…|f<   |S )zŸ
    Return matrix to translate by direction vector.

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

    c              3   óH   K  — | ]  }d t        t        |«      «      v –— Œ y­w)ÚsympyN)ÚstrÚtype)Ú.0Úvs     r   Ú	<genexpr>z%translation_matrix.<locals>.<genexpr>ï   s   è ø€ Ð
6©I qˆ7”cœ$˜q›'“lÔ"©Iùs   ‚ "r   )Úeyer   N)ÚlenÚanyr   r   r   )Ú	directionÚdimr   ÚMs       r   Útranslation_matrixr"   á   s\   € ô ˆi‹.€Cô Ñ
6©IÓ
6Ô6åá��a‘‹L‰ä�F‰F�3˜‘7‹Oˆð ˜T˜c�?€A€d€s€dˆC€i�Là€Hr   c                 óV   — t        j                  | «      dd…df   j                  «       S )zÂReturn translation vector from translation matrix.

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

    Né   )r   ÚasarrayÚcopy©Úmatrixs    r   Útranslation_from_matrixr)   ý   s)   € ô �:‰:�fÓ˜b˜q˜b !˜eÑ$×)Ñ)Ó+Ð+r   c                 óì   — t        |dd «      }t        j                  d«      }|dd…dd…fxx   dt        j                  ||«      z  z  cc<   dt        j                  | dd |«      z  |z  |dd…df<   |S )a·  Return matrix to mirror at plane defined by point and normal vector.

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

    Nr$   r   ç       @)Úunit_vectorr   r   ÚouterÚdot)ÚpointÚnormalr!   s      r   Úreflection_matrixr1   	  s|   € ô& ˜  ˜Ó$€FÜ
�‰�A‹€AØ€b€q€bˆ"ˆ1ˆ"€fƒI�”r—x‘x ¨Ó/Ñ/Ñ/ƒIØ”b—f‘f˜U 2 A˜Y¨Ó/Ñ/°6Ñ9€A€b€q€bˆ!€e�HØ€Hr   c                 óò  — t        j                  | t         j                  ¬«      }t         j                  j	                  |dd…dd…f   «      \  }}t        j
                  t        t        j                  |«      dz   «      dk  «      d   }t        |«      st        d«      ‚t        j                  |dd…|d   f   «      j                  «       }t         j                  j	                  |«      \  }}t        j
                  t        t        j                  |«      dz
  «      dk  «      d   }t        |«      st        d«      ‚t        j                  |dd…|d	   f   «      j                  «       }||d   z  }||fS )
aK  Return mirror plane point and normal vector from reflection matrix.

    >>> v0 = np.random.random(3) - 0.5
    >>> v1 = np.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

    ©ÚdtypeNr$   ç      ð?ç:Œ0âŽyE>r   z2no unit eigenvector corresponding to eigenvalue -1ú1no unit eigenvector corresponding to eigenvalue 1éÿÿÿÿ)r   r%   Úfloat64ÚlinalgÚeigÚwhereÚabsÚrealr   Ú
ValueErrorÚsqueeze)r(   r!   ÚwÚVÚir0   r/   s          r   Úreflection_from_matrixrD   #  s,  € ô 	�
‰
�6¤§¡Ô,€Aä�9‰9�=‰=˜˜2˜A˜2˜r ˜r˜6™Ó#�D€A€qÜ
�‰””R—W‘W˜Q“Z #Ñ%Ó&¨Ñ-Ó.¨qÑ1€AÜˆqŒ6ÜÐMÓNÐNÜ�W‰W�Q’q˜!˜A™$�w‘ZÓ ×(Ñ(Ó*€Fä�9‰9�=‰=˜Ó�D€A€qÜ
�‰””R—W‘W˜Q“Z #Ñ%Ó&¨Ñ-Ó.¨qÑ1€AÜˆqŒ6ÜÐLÓMÐMÜ�G‰G�A’a˜˜2™�h‘KÓ ×(Ñ(Ó*€EØ	ˆU�1‰XÑ€EØ�&ˆ=Ðr   c           
      óè  — dt        t        | «      «      v r)ddl}d}|j                  | «      }|j	                  | «      }n,d}t        j                  | «      }t        j                  | «      }t        |dd «      }t        j                  |||dg«      }|dd…dd…fxx   t        j                  ||«      d|z
  z  z  cc<   ||z  }|dd…dd…fxx   t        j                  d|d	    |d
   g|d	   d|d    g|d
    |d   dgg«      z  cc<   |�Qt        j                  |dd t
        j                  ¬«      }|t        j                  |dd…dd…f   |«      z
  |dd…df<   |rj                  |«      S |S )aÎ  
    Return matrix to rotate about axis defined by point and
    direction.

    Parameters
    -------------
    angle     : float, or sympy.Symbol
      Angle, in radians or symbolic angle
    direction : (3,) float
      Any vector along rotation axis
    point     : (3, ) float, or None
      Origin point of rotation axis

    Returns
    -------------
    matrix : (4, 4) float, or (4, 4) sympy.Matrix
      Homogeneous transformation matrix

    Examples
    -------------
    >>> R = rotation_matrix(np.pi/2, [0, 0, 1], [1, 0, 0])
    >>> np.allclose(np.dot(R, [0, 0, 0, 1]), [1, -1, 0, 1])
    True
    >>> angle = (random.random() - 0.5) * (2*np.pi)
    >>> direc = np.random.random(3) - 0.5
    >>> point = np.random.random(3) - 0.5
    >>> R0 = rotation_matrix(angle, direc, point)
    >>> R1 = rotation_matrix(angle-2*np.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 = np.identity(4, np.float64)
    >>> np.allclose(I, rotation_matrix(np.pi*2, direc))
    True
    >>> np.allclose(2, np.trace(rotation_matrix(np.pi/2,direc,point)))
    True

    r   r   NTFr$   r5   ç        é   r   r3   )r   r   r   ÚsinÚcosr   r,   Údiagr-   Úarrayr%   r9   r.   ÚMatrix)Úangler   r/   ÚspÚsymbolicÚsinaÚcosar!   s           r   Úrotation_matrixrR   @  s‰  € ðT ”#”d˜5“kÓ"Ñ"ãàˆØ�v‰v�e‹}ˆØ�v‰v�e‹}‰àˆÜ�v‰v�e‹}ˆÜ�v‰v�e‹}ˆä˜I b q˜MÓ*€Iô 	�‰��t˜T 3Ð'Ó(€AØ€b€q€bˆ"ˆ1ˆ"€fƒI”—‘˜) YÓ/°3¸±:Ñ>Ñ>ƒIà˜DÑ €IØ€b€q€bˆ"ˆ1ˆ"€fƒI”—‘à�9˜Q‘<�- ¨1¡Ð.Ø�q‰\˜3 ¨1¡ Ð.Ø˜‰lˆ]˜I a™L¨#Ð.ð	
óñ ƒIð ÐÜ—
‘
˜5  !˜9¬B¯J©JÔ7ˆØœ2Ÿ6™6 ! B Q B¨¨¨ F¡)¨UÓ3Ñ3ˆˆ"ˆ1ˆ"ˆaˆ%‰ñ Ø�y‰y˜‹|Ðà€Hr   c                 ód  — t        j                  | t         j                  ¬«      }|dd…dd…f   }t         j                  j	                  |j
                  «      \  }}t        j                  t        t        j                  |«      dz
  «      dk  «      d   }t        |«      st        d«      ‚t        j                  |dd…|d   f   «      j                  «       }t         j                  j	                  |«      \  }}t        j                  t        t        j                  |«      dz
  «      dk  «      d   }t        |«      st        d«      ‚t        j                  |dd…|d   f   «      j                  «       }||d   z  }t        j                  |«      dz
  d	z  }	t        |d
   «      dkD  r|d   |	dz
  |d   z  |d   z  z   |d
   z  }
nLt        |d   «      dkD  r|d   |	dz
  |d   z  |d
   z  z   |d   z  }
n|d   |	dz
  |d   z  |d
   z  z   |d   z  }
t        j                  |
|	«      }|||fS )a‰  Return rotation angle and axis from rotation matrix.

    >>> angle = (random.random() - 0.5) * (2*np.pi)
    >>> direc = np.random.random(3) - 0.5
    >>> point = np.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

    r3   Nr$   r5   r6   r   r7   r8   r+   rG   ©r   r   r   ©r   rG   ©rG   r   )r   r%   r9   r:   r;   ÚTr<   r=   r>   r   r?   r@   ÚtraceÚarctan2)r(   ÚRÚR33rA   ÚWrC   r   ÚQr/   rQ   rP   rM   s               r   Úrotation_from_matrixr^   ‘  s  € ô 	�
‰
�6¤§¡Ô,€AØ
ˆBˆQˆB���ˆF‰)€Cä�9‰9�=‰=˜Ÿ™Ó�D€A€qÜ
�‰””R—W‘W˜Q“Z #Ñ%Ó&¨Ñ-Ó.¨qÑ1€AÜˆqŒ6ÜÐLÓMÐMÜ—‘˜š!˜Q˜r™U˜(™Ó$×,Ñ,Ó.€Iä�9‰9�=‰=˜Ó�D€A€qÜ
�‰””R—W‘W˜Q“Z #Ñ%Ó&¨Ñ-Ó.¨qÑ1€AÜˆqŒ6ÜÐLÓMÐMÜ�G‰G�A’a˜˜2™�h‘KÓ ×(Ñ(Ó*€EØ	ˆU�1‰XÑ€Eä�H‰H�S‹M˜CÑ 3Ñ&€DÜ
ˆ9�Q‰<Ó˜4ÒØ�$‘˜4 #™:¨°1©Ñ5¸	À!¹ÑDÑDÈ	ÐRSÉÑT‰Ü	ˆY�q‰\Ó	˜TÒ	!Ø�$‘˜4 #™:¨°1©Ñ5¸	À!¹ÑDÑDÈ	ÐRSÉÑT‰à�$‘˜4 #™:¨°1©Ñ5¸	À!¹ÑDÑDÈ	ÐRSÉÑTˆÜ�J‰J�t˜TÓ"€EØ�)˜UÐ"Ð"r   c                 óx  — |€=t        j                  | | | dg«      }|� |dd |dd…df<   |dd…dfxx   d| z
  z  cc<   |S t        |dd «      }d| z
  } t        j                  d«      }|dd…dd…fxx   | t        j                  ||«      z  z  cc<   |�&| t        j
                  |dd |«      z  |z  |dd…df<   |S )aá  Return matrix to scale by factor around origin in direction.

    Use factor -1 for point symmetry.

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

    Nr5   r$   r   )r   rJ   r,   r   r-   r.   )ÚfactorÚoriginr   r!   s       r   Úscale_matrixrb   ¹  sè   € ð" Ðä�G‰G�V˜V V¨SÐ1Ó2ˆØÐØ˜b˜q�zˆAˆbˆqˆb�!ˆe‰HØˆbˆqˆb�!ˆe‹H˜˜f™Ñ$‹Hð €Hô   	¨"¨1 Ó.ˆ	Ø�v‘ˆÜ�K‰K˜‹NˆØ	ˆ"ˆ1ˆ"ˆbˆqˆbˆ&‹	�VœbŸh™h y°)Ó<Ñ<Ñ<‹	ØÐØ¤§¡¨¨r°¨
°IÓ!>Ñ>À)ÑKˆAˆbˆqˆb�!ˆe‰HØ€Hr   c                 óL  — t        j                  | t         j                  ¬«      }|dd…dd…f   }t        j                  |«      dz
  }	 t         j                  j                  |«      \  }}t        j                  t        t        j                  |«      |z
  «      dk  «      d   d   }t        j                  |dd…|f   «      j                  «       }|t        |«      z  }t         j                  j                  |«      \  }}t        j                  t        t        j                  |«      dz
  «      dk  «      d   }t        |«      st        d	«      ‚t        j                  |dd…|d
   f   «      j                  «       }||d   z  }|||fS # t        $ r |dz   dz  }d}Y ŒÁw xY w)aW  Return scaling factor, origin and direction from scaling matrix.

    >>> factor = random.random() * 10 - 5
    >>> origin = np.random.random(3) - 0.5
    >>> direct = np.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

    r3   Nr$   r+   r6   r   ç      @r5   ú,no eigenvector corresponding to eigenvalue 1r8   )r   r%   r9   rX   r:   r;   r<   r=   r>   r@   Úvector_normÚ
IndexErrorr   r?   )	r(   r!   ÚM33r`   rA   rB   rC   r   ra   s	            r   Úscale_from_matrixri   Û  sn  € ô$ 	�
‰
�6¤§¡Ô,€AØ
ˆBˆQˆB���ˆF‰)€CÜ�X‰X�c‹]˜SÑ €Fð	ä�y‰y�}‰}˜SÓ!‰ˆˆ1Ü�H‰H”SœŸ™ › fÑ,Ó-°Ñ4Ó5°aÑ8¸Ñ;ˆÜ—G‘G˜Aša ˜d™GÓ$×,Ñ,Ó.ˆ	Ø”[ Ó+Ñ+ˆ	ô �9‰9�=‰=˜Ó�D€A€qÜ
�‰””R—W‘W˜Q“Z #Ñ%Ó&¨Ñ-Ó.¨qÑ1€AÜˆqŒ6ÜÐGÓHÐHÜ�W‰W�Q’q˜!˜B™%�x‘[Ó!×)Ñ)Ó+€FØ
ˆf�Q‰iÑ€FØ�6˜9Ð$Ð$øô ò à˜3‘, #Ñ%ˆØŠ	ðús   Á
BF ÆF#Æ"F#c                 ó\  — t        j                  d«      }t        j                  | dd t         j                  ¬«      } t	        |dd «      }|��t        j                  |dd t         j                  ¬«      }t        j
                  || z
  |«      x|d<   x|d<   |d<   |dd…dd…fxx   t        j                  ||«      z  cc<   |rK|dd…dd…fxx   t        j                  ||«      z  cc<   t        j
                  | |«      ||z   z  |dd…df<   n t        j
                  | |«      |z  |dd…df<   | |ddd…f<   t        j
                  ||«      |d<   |S |��t        j                  |dd t         j                  ¬«      }t        j
                  ||«      }|dd…dd…fxx   t        j                  ||«      |z  z  cc<   |t        j
                  | |«      |z  z  |dd…df<   |S |dd…dd…fxx   t        j                  ||«      z  cc<   t        j
                  | |«      |z  |dd…df<   |S )	ae  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])
    >>> np.allclose(P[1:, 1:], np.identity(4)[1:, 1:])
    True
    >>> point = np.random.random(3) - 0.5
    >>> normal = np.random.random(3) - 0.5
    >>> direct = np.random.random(3) - 0.5
    >>> persp = np.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, np.dot(P0, P3))
    True
    >>> P = projection_matrix([3, 0, 0], [1, 1, 0], [1, 0, 0])
    >>> v0 = (np.random.rand(4, 5) - 0.5) * 20
    >>> v0[3] = 1
    >>> v1 = np.dot(P, v0)
    >>> np.allclose(v1[1], v0[1])
    True
    >>> np.allclose(v1[0], 3-v1[1])
    True

    r   Nr$   r3   ©r   r   ©r   r   ©rG   rG   ©r$   r$   )r   r   r%   r9   r,   r.   r-   )r/   r0   r   ÚperspectiveÚpseudor!   Úscales          r   Úprojection_matrixrr     s  € ô> 	�‰�A‹€AÜ�J‰J�u˜R˜a�y¬¯
©
Ô3€EÜ˜  ˜Ó$€FØÑä—j‘j ¨R¨a ¼¿
¹
ÔCˆÜ&(§f¡f¨[¸5Ñ-@À&Ó&IÐIˆˆ$‰ÐI�!�D‘'˜A˜d™GØ	ˆ"ˆ1ˆ"ˆbˆqˆbˆ&‹	”R—X‘X˜k¨6Ó2Ñ2‹	Ùàˆbˆqˆb�"�1�"ˆf‹IœŸ™ &¨&Ó1Ñ1‹IÜ—v‘v˜e VÓ,°¸fÑ0DÑEˆAˆbˆqˆb�!ˆeŠHä—v‘v˜e VÓ,¨{Ñ:ˆAˆbˆqˆb�!ˆe‰HØ�7ˆˆ!ˆRˆaˆRˆ%‰Ü—&‘&˜ fÓ-ˆˆ$‰ð €Hð 
Ð	ä—J‘J˜y¨¨!˜}´B·J±JÔ?ˆ	Ü—‘�y &Ó)ˆØ	ˆ"ˆ1ˆ"ˆbˆqˆbˆ&‹	”R—X‘X˜i¨Ó0°5Ñ8Ñ8‹	Ø¤§¡ u¨fÓ 5¸Ñ =Ñ>ˆˆ"ˆ1ˆ"ˆaˆ%‰ð
 €Hð 	
ˆ"ˆ1ˆ"ˆbˆqˆbˆ&‹	”R—X‘X˜f fÓ-Ñ-‹	Ü—6‘6˜% Ó(¨6Ñ1ˆˆ"ˆ1ˆ"ˆaˆ%‰Ø€Hr   c                 óÞ  — t        j                  | t         j                  ¬«      }|dd…dd…f   }t         j                  j	                  |«      \  }}t        j
                  t        t        j                  |«      dz
  «      dk  «      d   }|�s¢t        |«      �r–t        j                  |dd…|d   f   «      j                  «       }||d   z  }t         j                  j	                  |«      \  }}t        j
                  t        t        j                  |«      «      dk  «      d   }t        |«      st        d«      ‚t        j                  |dd…|d   f   «      j                  «       }|t        |«      z  }t         j                  j	                  |j                  «      \  }}t        j
                  t        t        j                  |«      «      dk  «      d   }t        |«      rBt        j                  |dd…|d   f   «      j                  «       }	|	t        |	«      z  }	||	|dd	fS ||ddd	fS t        j
                  t        t        j                  |«      «      dkD  «      d   }t        |«      st        d
«      ‚t        j                  |dd…|d   f   «      j                  «       }||d   z  }|ddd…f    }	|dd…df   t        j                  |dd |	«      z  }
|r|
|	z  }
||	d|
|fS )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 = np.random.random(3) - 0.5
    >>> normal = np.random.random(3) - 0.5
    >>> direct = np.random.random(3) - 0.5
    >>> persp = np.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

    r3   Nr$   r5   r6   r   r8   z,no eigenvector corresponding to eigenvalue 0Fz0no eigenvector not corresponding to eigenvalue 0)r   r%   r9   r:   r;   r<   r=   r>   r   r@   r?   rf   rW   r.   )r(   rp   r!   rh   rA   rB   rC   r/   r   r0   ro   s              r   Úprojection_from_matrixrt   @  s„  € ô@ 	�
‰
�6¤§¡Ô,€AØ
ˆBˆQˆB���ˆF‰)€CÜ�9‰9�=‰=˜Ó�D€A€qÜ
�‰””R—W‘W˜Q“Z #Ñ%Ó&¨Ñ-Ó.¨qÑ1€AÚ”c˜!•fä—‘˜š!˜Q˜r™U˜(™Ó$×,Ñ,Ó.ˆØ��q‘Ñˆä�y‰y�}‰}˜SÓ!‰ˆˆ1Ü�H‰H”SœŸ™ ›“_ tÑ+Ó,¨QÑ/ˆÜ�1ŒvÜÐKÓLÐLÜ—G‘G˜Aša  1¡˜g™JÓ'×/Ñ/Ó1ˆ	Ø”[ Ó+Ñ+ˆ	ä�y‰y�}‰}˜SŸU™UÓ#‰ˆˆ1Ü�H‰H”SœŸ™ ›“_ tÑ+Ó,¨QÑ/ˆÜˆqŒ6ä—W‘W˜Qšq ! A¡$˜w™ZÓ(×0Ñ0Ó2ˆFØ”k &Ó)Ñ)ˆFØ˜& )¨T°5Ð8Ð8ð ˜) T¨4°Ð6Ð6ô �H‰H”SœŸ™ ›“_ tÑ+Ó,¨QÑ/ˆÜ�1ŒvÜÐOÓPÐPÜ—‘˜š!˜Q˜r™U˜(™Ó$×,Ñ,Ó.ˆØ��q‘ÑˆØ�A�r˜�r�E‘(�ˆØ˜˜˜˜A˜‘h¤§¡¨¨b¨q¨	°6Ó!:Ñ:ˆÙØ˜6Ñ!ˆKØ�f˜d K°Ð7Ð7r   c                 ó¢  — | |k\  s
||k\  s||k\  rt        d«      ‚|r^|t        k  rt        d«      ‚d|z  }|| |z
  z  d|| z   || z
  z  dgd|||z
  z  ||z   ||z
  z  dgdd||z   ||z
  z  ||z  ||z
  z  gg d¢g}nAd|| z
  z  dd|| z   | |z
  z  gdd||z
  z  d||z   ||z
  z  gddd||z
  z  ||z   ||z
  z  gg d¢g}t        j                  |«      S )aC  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 = np.random.rand(6)
    >>> frustum[1] += frustum[0]
    >>> frustum[3] += frustum[2]
    >>> frustum[5] += frustum[4]
    >>> M = clip_matrix(perspective=False, *frustum)
    >>> a = np.dot(M, [frustum[0], frustum[2], frustum[4], 1])
    >>> np.allclose(a, [-1., -1., -1.,  1.])
    True
    >>> b = np.dot(M, [frustum[1], frustum[3], frustum[5], 1])
    >>> np.allclose(b, [ 1.,  1.,  1.,  1.])
    True
    >>> M = clip_matrix(perspective=True, *frustum)
    >>> v = np.dot(M, [frustum[0], frustum[2], frustum[4], 1])
    >>> c = v / v[3]
    >>> np.allclose(c, [-1., -1., -1.,  1.])
    True
    >>> v = np.dot(M, [frustum[1], frustum[3], frustum[4], 1])
    >>> d = v / v[3]
    >>> np.allclose(d, [ 1.,  1., -1.,  1.])
    True

    zinvalid frustumzinvalid frustum: near <= 0r+   rF   )rF   rF   ç      ð¿rF   ©rF   rF   rF   r5   )r?   Ú_EPSr   rK   )	ÚleftÚrightÚbottomÚtopÚnearÚfarro   Útr!   s	            r   Úclip_matrixr€   ˆ  s9  € ðL ˆu‚}˜ #š¨°ªÜÐ*Ó+Ð+ÙØ”4Š<ÜÐ9Ó:Ð:Ø�$‰Jˆà�$˜‘,Ñ  u¨t¡|¸À¹Ñ&EÀsÐKØ�!�v ‘|Ñ$ s¨V¡|¸¸f¹Ñ&EÀsÐKØ�#˜˜d™
 t¨c¡zÑ2°A¸±G¸sÀT¹zÑ4JÐKÚ!ð	
‰ð �E˜D‘LÑ! 3¨¨e°d©l¸tÀe¹|Ñ-LÐMØ�#˜˜v™Ñ&¨¨c°F©l¸vÈ¹|Ñ-LÐMØ�#�s˜c D™jÑ)¨C°$©J¸4À#¹:Ñ+FÐGÚ ð	
ˆô �8‰8�A‹;Ðr   c                 óŽ  — t        |dd «      }t        |dd «      }t        t        j                  ||«      «      dkD  rt	        d«      ‚t        j
                  | «      } t        j                  d«      }|dd…dd…fxx   | t        j                  ||«      z  z  cc<   |  t        j                  |dd |«      z  |z  |dd…df<   |S )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*np.pi
    >>> direct = np.random.random(3) - 0.5
    >>> point = np.random.random(3) - 0.5
    >>> normal = np.cross(direct, np.random.random(3))
    >>> S = shear_matrix(angle, direct, point, normal)
    >>> np.allclose(1, np.linalg.det(S))
    True

    Nr$   g�íµ ÷Æ°>z/direction and normal vectors are not orthogonalr   )r,   r=   r   r.   r?   Útanr   r-   )rM   r   r/   r0   r!   s        r   Úshear_matrixrƒ   Ä  s¿   € ô( ˜  ˜Ó$€FÜ˜I b q˜MÓ*€IÜ
Œ2�6‰6�&˜)Ó$Ó%¨Ò,ÜÐJÓKÐKÜ�F‰F�5‹M€EÜ
�‰�A‹€AØ€b€q€bˆ"ˆ1ˆ"€fƒI�œŸ™ )¨VÓ4Ñ4Ñ4ƒIØˆvœŸ™˜u R a˜y¨&Ó1Ñ1°IÑ=€A€b€q€bˆ!€e�HØ€Hr   c                 ó:  — t        j                  | t         j                  ¬«      }|dd…dd…f   }t         j                  j	                  |«      \  }}t        j
                  t        t        j                  |«      dz
  «      dk  «      d   }t        |«      dk  rt        d|› �«      ‚t        j                  |dd…|f   «      j                  «       j                  }d	}d
D ]6  \  }}t        j                  ||   ||   «      }	t        |	«      }||kD  sŒ3|}|	}
Œ8 
|z  }
t        j                  |t        j                  d«      z
  |
«      }t        |«      }||z  }t        j                   |«      }t         j                  j	                  |«      \  }}t        j
                  t        t        j                  |«      dz
  «      dk  «      d   }t        |«      st        d«      ‚t        j                  |dd…|d   f   «      j                  «       }||d   z  }||||
fS )a­  Return shear angle, direction and plane from shear matrix.

    >>> angle  = np.pi / 2.0
    >>> direct = [0.0, 1.0, 0.0]
    >>> point  = [0.0, 0.0, 0.0]
    >>> normal = np.cross(direct, np.roll(direct,1))
    >>> 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

    r3   Nr$   r5   g-Cëâ6?r   rG   z-no two linear independent eigenvectors found rv   )©r   r   rU   ©r   rG   r6   re   r8   )r   r%   r9   r:   r;   r<   r=   r>   r   r?   r@   rW   Úcrossrf   r.   r   Úarctan)r(   r!   rh   rA   rB   rC   ÚlenormÚi0Úi1Únr0   r   rM   r/   s                 r   Úshear_from_matrixr�   ã  sÓ  € ô 	�
‰
�6¤§¡Ô,€AØ
ˆBˆQˆB���ˆF‰)€Cä�9‰9�=‰=˜Ó�D€A€qä
�‰””R—W‘W˜Q“Z #Ñ%Ó&¨Ñ-Ó.¨qÑ1€AÜ
ˆ1ƒv�‚zÜÐHÈÈÐLÓMÐMÜ
�‰�’!�Q�$‘Ó× Ñ Ó"×$Ñ$€AØ€FÛ*‰ˆˆBÜ�H‰H�Q�r‘U˜A˜b™EÓ"ˆÜ˜‹NˆØˆv‹:ØˆFØ‰Fð +ð ˆfÑ€Fä—‘�sœRŸ[™[¨›^Ñ+¨VÓ4€IÜ˜	Ó"€EØ�Ñ€IÜ�I‰I�eÓ€Eä�9‰9�=‰=˜Ó�D€A€qä
�‰””R—W‘W˜Q“Z #Ñ%Ó&¨Ñ-Ó.¨qÑ1€AÜˆqŒ6ÜÐGÓHÐHÜ�G‰G�A’a˜˜2™�h‘KÓ ×(Ñ(Ó*€EØ	ˆU�1‰XÑ€EØ�)˜U FÐ*Ð*r   c                 ó8  — t        j                  | t         j                  ¬«      j                  }t	        |d   «      t
        k  rt        d«      ‚||d   z  }|j                  «       }d|dd…df<   t         j                  j                  |«      st        d«      ‚t        j                  d«      }g d	¢}g d	¢}t        t	        |dd…df   «      t
        kD  «      rNt        j                  |dd…df   t         j                  j                  |j                  «      «      }d|dd…df<   nt        j                  g d¢«      }|ddd…f   j                  «       }d
|ddd…f<   |dd…dd…f   j                  «       }t        |d   «      |d<   |dxx   |d   z  cc<   t        j                  |d   |d   «      |d<   |dxx   |d   |d   z  z  cc<   t        |d   «      |d<   |dxx   |d   z  cc<   |dxx   |d   z  cc<   t        j                  |d   |d   «      |d<   |dxx   |d   |d   z  z  cc<   t        j                  |d   |d   «      |d<   |dxx   |d   |d   z  z  cc<   t        |d   «      |d<   |dxx   |d   z  cc<   |ddxxx |d   z  ccc t        j                  |d   t        j                  |d   |d   «      «      dk  r,t        j                   ||«       t        j                   ||«       t        j"                  |d    «      |d<   t        j$                  |d   «      r?t        j&                  |d   |d   «      |d<   t        j&                  |d   |d   «      |d<   n%t        j&                  |d    |d   «      |d<   d
|d<   |||||fS )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)
    >>> np.allclose(T0, T1)
    True
    >>> S = scale_matrix(0.123)
    >>> scale, shear, angles, trans, persp = decompose_matrix(S)
    >>> bool(np.isclose(scale[0], 0.123))
    True
    >>> R0 = euler_matrix(1, 2, 3)
    >>> scale, shear, angles, trans, persp = decompose_matrix(R0)
    >>> R1 = euler_matrix(*angles)
    >>> np.allclose(R0, R1)
    True

    r3   rn   zM[3, 3] is zerorw   Nr$   zmatrix is singular)r$   )rF   rF   rF   rF   r   r   rG   rU   r†   rm   r…   rk   rV   rl   )r   rK   r9   rW   r=   rx   r?   r&   r:   ÚdetÚzerosr   r.   Úinvrf   r‡   ÚnegativeÚarcsinrI   rY   )	r(   r!   ÚPrq   ÚshearÚanglesro   Ú	translateÚrows	            r   Údecompose_matrixr™     sM  € ô> 	�‰�œrŸz™zÔ*×,Ñ,€AÜ
ˆ1ˆT‰7ƒ|”dÒÜÐ*Ó+Ð+Øˆˆ4‰�L€AØ	�‰‹€AØ €A‚aˆ€d�GÜ�9‰9�=‰=˜ÔÜÐ-Ó.Ð.ä�H‰H�T‹N€EÚ€EÚ€Fä
Œ3ˆq��!��Q�‰x‹=œ4ÑÔ Ü—f‘f˜Qšq !˜t™W¤b§i¡i§m¡m°A·C±CÓ&8Ó9ˆØ$ˆŠ!ˆQˆ$Šä—h‘hÒ3Ó4ˆà�!�R�a�R�%‘—‘“€IØ€A€aˆˆ!ˆ€e�Hà
ˆBˆQˆB���ˆF‰)�.‰.Ó
€CÜ˜3˜q™6Ó"€Eˆ!�HØˆƒFˆe�A‰hÑƒFÜ�v‰v�c˜!‘f˜c !™fÓ%€Eˆ!�HØˆƒFˆc�!‰f�u˜Q‘xÑÑƒFÜ˜3˜q™6Ó"€Eˆ!�HØˆƒFˆe�A‰hÑƒFØ	ˆ!ƒH��a‘ÑƒHÜ�v‰v�c˜!‘f˜c !™fÓ%€Eˆ!�HØˆƒFˆc�!‰f�u˜Q‘xÑÑƒFÜ�v‰v�c˜!‘f˜c !™fÓ%€Eˆ!�HØˆƒFˆc�!‰f�u˜Q‘xÑÑƒFÜ˜3˜q™6Ó"€Eˆ!�HØˆƒFˆe�A‰hÑƒFØ	ˆ!ˆ"ƒI��q‘ÑƒIä	‡v�vˆc�!‰f”b—h‘h˜s 1™v s¨1¡vÓ.Ó/°!Ò3Ü
�‰�E˜5Ô!Ü
�‰�C˜Ôä—	‘	˜3˜t™9˜*Ó%€Fˆ1�IÜ	‡v�vˆf�Q‰iÔÜ—J‘J˜s 4™y¨#¨d©)Ó4ˆˆq‰	Ü—J‘J˜s 4™y¨#¨d©)Ó4ˆˆqŠ	ä—J‘J  D¡	˜z¨3¨t©9Ó5ˆˆq‰	Øˆˆq‰	à�%˜ ¨KÐ7Ð7r   c                 ó–  — t        j                  d«      }|�7t        j                  d«      }|dd |ddd…f<   t        j                  ||«      }|�7t        j                  d«      }|dd |dd…df<   t        j                  ||«      }|�-t        |d   |d   |d   d«      }t        j                  ||«      }|�Ct        j                  d«      }	|d   |	d<   |d   |	d	<   |d   |	d
<   t        j                  ||	«      }| �Ct        j                  d«      }
| d   |
d<   | d   |
d<   | d   |
d<   t        j                  ||
«      }||d   z  }|S )aR  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 = np.random.random(3) - 0.5
    >>> shear = np.random.random(3) - 0.5
    >>> angles = (np.random.random(3) - 0.5) * (2*np.pi)
    >>> trans = np.random.random(3) - 0.5
    >>> persp = np.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   r   rG   Úsxyzr†   rU   r…   rk   rl   rm   rn   )r   r   r.   Úeuler_matrix)rq   r•   r–   r—   ro   r!   r”   rW   rZ   ÚZÚSs              r   Úcompose_matrixrŸ   f  sT  € ô0 	�‰�A‹€AØÐÜ�K‰K˜‹NˆØ˜b˜q�/ˆˆ!ŠQˆ$‰Ü�F‰F�1�a‹LˆØÐÜ�K‰K˜‹NˆØ˜R˜a�=ˆˆ"ˆ1ˆ"ˆaˆ%‰Ü�F‰F�1�a‹LˆØÐÜ˜ ™ F¨1¡I¨v°a©y¸&ÓAˆÜ�F‰F�1�a‹LˆØÐÜ�K‰K˜‹NˆØ˜‘(ˆˆ$‰Ø˜‘(ˆˆ$‰Ø˜‘(ˆˆ$‰Ü�F‰F�1�a‹LˆØÐÜ�K‰K˜‹NˆØ˜‘(ˆˆ$‰Ø˜‘(ˆˆ$‰Ø˜‘(ˆˆ$‰Ü�F‰F�1�a‹LˆØˆˆ4‰�L€AØ€Hr   c                 ód  — | \  }}}t        j                  |«      }t        j                  |«      \  }}}t        j                  |«      \  }}	}
||	z  |
z
  ||z  z  }t        j                  ||z  t        j
                  d||z  z
  «      z  dddg| |z  |z  ||z  ddg||	z  ||z  |dgg d¢g«      S )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])
    >>> np.allclose(O[:3, :3], np.identity(3, float) * 10)
    True
    >>> O = orthogonalization_matrix([9.8, 12.0, 15.5], [87.2, 80.7, 69.7])
    >>> np.allclose(np.sum(O), 43.063229)
    True

    r5   rF   rw   )r   ÚradiansrH   rI   rK   Úsqrt)Úlengthsr–   ÚaÚbÚcrP   ÚsinbÚ_rQ   ÚcosbÚcosgÚcos               r   Úorthogonalization_matrixr¬   š  sÈ   € ð �G€A€qˆ!Ü�Z‰Z˜Ó€FÜ—F‘F˜6“N�M€Dˆ$�Ü—v‘v˜f“~Ñ€Dˆ$�Ø
�‰+˜Ñ
 ¨¡Ñ	-€BÜ�8‰8à�‰XœŸ™  b¨2¡g¡Ó.Ñ.°°S¸#Ð>ØˆR�$‰Y˜‰^˜Q ™X s¨CÐ0Ø�‰X�q˜4‘x  CÐ(Ú ð		
óð r   c           	      óˆ	  — t        j                  | t         j                  ¬«      } t        j                  |t         j                  ¬«      }| j                  d   }|dk  s+| j                  d   |k  s| j                  |j                  k7  rt	        d«      ‚t        j
                  | d¬«       }t        j                  |dz   «      }||d|…|f<   | |j                  |d«      z  } t        j
                  |d¬«       }t        j                  |dz   «      }	||	d|…|f<   ||j                  |d«      z  }|rât        j                  | |fd¬«      }
t         j                  j                  |
j                  «      \  }}}|d| j                  }|d| }||d|z   }t        j                  |t         j                  j                  |«      «      }t        j                  |t        j                  |df«      fd¬«      }t        j                  |d|z  d	z   f«      }�nÿ|s|d
k7  rÛt         j                  j                  t        j                  || j                  «      «      \  }}}t        j                  ||«      }t         j                  j!                  |«      dk  r=|t        j"                  |dd…|dz
  f   ||dz
  dd…f   dz  «      z  }|dxx   dz  cc<   t        j                  |dz   «      }||d|…d|…f<   �nt        j$                  | |z  d¬«      \  }}}t        j$                  | t        j&                  |dd¬«      z  d¬«      \  }}}t        j$                  | t        j&                  |dd¬«      z  d¬«      \  }}}||z   |z   dddg||z
  ||z
  |z
  ddg||z
  ||z   ||z
  |z
  dg||z
  ||z   ||z   ||z
  |z
  gg}t         j                  j)                  |«      \  }}|dd…t        j*                  |«      f   }|t-        |«      z  }t/        |«      }|r[|sY| | z  } ||z  }|d|…d|…fxx   t        j0                  t        j$                  |«      t        j$                  | «      z  «      z  cc<   t        j                  t         j                  j3                  |	«      t        j                  ||«      «      }||||f   z  }|S )a  Return affine transform matrix to register two point sets.

    v0 and v1 are shape (ndims, *) arrays of at least ndims non-homogeneous
    coordinates, where ndims is the dimensionality of the coordinate space.

    If shear is False, a similarity transformation matrix is returned.
    If also scale is False, a rigid/Euclidean transformation matrix
    is returned.

    By default the algorithm by Hartley and Zissermann [15] is used.
    If usesvd is True, similarity and Euclidean transformation matrices
    are calculated by minimizing the weighted sum of squared deviations
    (RMSD) according to the algorithm by Kabsch [8].
    Otherwise, and if ndims is 3, the quaternion based algorithm by Horn [9]
    is used, which is slower when using this Python implementation.

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

    >>> v0 = [[0, 1031, 1031, 0], [0, 0, 1600, 1600]]
    >>> v1 = [[675, 826, 826, 677], [55, 52, 281, 277]]
    >>> mat = affine_matrix_from_points(v0, v1)
    >>> T = translation_matrix(np.random.random(3)-0.5)
    >>> R = random_rotation_matrix(np.random.random(3))
    >>> S = scale_matrix(random.random())
    >>> M = concatenate_matrices(T, R, S)
    >>> v0 = (np.random.rand(4, 100) - 0.5) * 20
    >>> v0[3] = 1
    >>> v1 = np.dot(M, v0)
    >>> v0[:3] += np.random.normal(0, 1e-8, 300).reshape(3, -1)
    >>> M = affine_matrix_from_points(v0[:3], v1[:3])
    >>> check = np.allclose(v1, np.dot(M, v0))

    More examples in superimposition_matrix()

    r3   r   rG   r   z'input arrays are of wrong shape or type©ÚaxisN©rF   )r5   r$   rF   r+   r8   rv   éþÿÿÿ)r   rK   r9   Úshaper?   Úmeanr   ÚreshapeÚconcatenater:   ÚsvdrW   r.   Úpinvr�   Úvstackr�   r-   ÚsumÚrollÚeighÚargmaxrf   Úquaternion_matrixr¢   r‘   ) Úv0Úv1r•   rq   ÚusesvdÚndimsÚt0ÚM0Út1ÚM1ÚAÚuÚsÚvhÚBÚCr   r!   rZ   ÚxxÚyyÚzzÚxyÚyzÚzxÚxzÚyxÚzyÚNrA   rB   Úqs                                    r   Úaffine_matrix_from_pointsr×   ¸  s+  € ôJ 
�‰�"œBŸJ™JÔ	'€BÜ	�‰�"œBŸJ™JÔ	'€Bà�H‰H�Q‰K€EØˆq‚y�B—H‘H˜Q‘K %Ò'¨2¯8©8°r·x±xÒ+?ÜÐBÓCÐCô �'‰'�"˜1Ô
Ð	€BÜ	�‰�U˜Q‘YÓ	€BØ€B€vˆ€vˆu€}ÑØˆ"�*‰*�U˜AÓ
Ñ€BÜ
�'‰'�"˜1Ô
Ð	€BÜ	�‰�U˜Q‘YÓ	€BØ€B€vˆ€vˆu€}ÑØˆ"�*‰*�U˜AÓ
Ñ€Báä�N‰N˜B ˜8¨!Ô,ˆÜ—9‘9—=‘= §¡Ó%‰ˆˆ1ˆbØ��ˆZ�\‰\ˆØˆv�ˆJˆØˆu�q˜5‘yÐ!ˆÜ�F‰F�1”b—i‘i—n‘n QÓ'Ó(ˆÜ�N‰N˜AœrŸx™x¨°¨
Ó3Ð4¸1Ô=ˆÜ�I‰I�q˜6 E™>¨VÑ3Ð4Ó5ŠÙ	�5˜A’:ä—9‘9—=‘=¤§¡¨¨B¯D©DÓ!1Ó2‰ˆˆ1ˆbä�F‰F�1�b‹MˆÜ�9‰9�=‰=˜Ó˜cÒ!à”—‘˜!šA˜u q™y˜L™/¨2¨e°a©iº¨lÑ+;¸cÑ+AÓBÑBˆAØˆb‹E�T‰M‹Eä�K‰K˜ ™	Ó"ˆØˆˆ&ˆ5ˆ&�&�5�&ˆ.Óô —V‘V˜B ™G¨!Ô,‰
ˆˆB�Ü—V‘V˜B¤§¡¨¨R°aÔ!8Ñ8¸qÔA‰
ˆˆB�Ü—V‘V˜B¤§¡¨¨R°aÔ!8Ñ8¸qÔA‰
ˆˆB�à�"‰W�r‰\˜3  SÐ)Ø�"‰W�b˜2‘g ‘l C¨Ð-Ø�"‰W�b˜2‘g˜r B™w¨™|¨SÐ1Ø�"‰W�b˜2‘g˜r B™w¨¨R©°"©Ð5ð	
ˆô �y‰y�~‰~˜aÓ ‰ˆˆ1ØŠa”—‘˜1“ˆoÑˆØ	Œ[˜‹^Ñˆä˜aÓ ˆá‘Uà
ˆb‰ˆØ
ˆb‰ˆØ	ˆ&ˆ5ˆ&�&�5�&ˆ.ÓœRŸW™W¤R§V¡V¨B£Z´"·&±&¸³*Ñ%<Ó=Ñ=Óô 	�‰Œr�y‰y�}‰}˜RÓ ¤"§&¡&¨¨B£-Ó0€AØˆˆ5�%ˆ<‰Ñ€AØ€Hr   c                 óÂ   — t        j                  | t         j                  ¬«      dd } t        j                  |t         j                  ¬«      dd }t        | |d||¬«      S )a#  Return matrix to transform given 3D point set into second point set.

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

    The parameters scale and usesvd are explained in the more general
    affine_matrix_from_points function.

    The returned matrix is a similarity or Euclidean transformation matrix.
    This function has a fast C implementation in transformations.c.

    >>> v0 = np.random.rand(3, 10)
    >>> M = superimposition_matrix(v0, v0)
    >>> np.allclose(M, np.identity(4))
    True
    >>> R = random_rotation_matrix(np.random.random(3))
    >>> v0 = [[1,0,0], [0,1,0], [0,0,1], [1,1,1]]
    >>> v1 = np.dot(R, v0)
    >>> M = superimposition_matrix(v0, v1)
    >>> np.allclose(v1, np.dot(M, v0))
    True
    >>> v0 = (np.random.rand(4, 100) - 0.5) * 20
    >>> v0[3] = 1
    >>> v1 = np.dot(R, v0)
    >>> M = superimposition_matrix(v0, v1)
    >>> np.allclose(v1, np.dot(M, v0))
    True
    >>> S = scale_matrix(random.random())
    >>> T = translation_matrix(np.random.random(3)-0.5)
    >>> M = concatenate_matrices(T, R, S)
    >>> v1 = np.dot(M, v0)
    >>> v0[:3] += np.random.normal(0, 1e-9, 300).reshape(3, -1)
    >>> M = superimposition_matrix(v0, v1, scale=True)
    >>> np.allclose(v1, np.dot(M, v0))
    True
    >>> M = superimposition_matrix(v0, v1, scale=True, usesvd=False)
    >>> np.allclose(v1, np.dot(M, v0))
    True
    >>> v = np.zeros((4, 100, 3))
    >>> v[:, :, 0] = v0
    >>> M = superimposition_matrix(v0, v1, scale=True, usesvd=False)
    >>> np.allclose(v1, np.dot(M, v[:, :, 0]))
    True

    r3   Nr$   F)r•   rq   rÀ   )r   r%   r9   r×   )r¾   r¿   rq   rÀ   s       r   Úsuperimposition_matrixrÙ   #  sN   € ôZ 
�‰�BœbŸj™jÔ	)¨"¨1Ð	-€BÜ	�‰�BœbŸj™jÔ	)¨"¨1Ð	-€BÜ$ R¨°5ÀÈfÔUÐUr   r›   c                 ó¬  — 	 t         |   \  }}}}|}t        ||z      }	t        ||z
  dz      }
|r|| }} |r	|  | | }}} dt        t        | «      «      v rddlm}m	}m
}  |d«      }n5t        j                  t        j                  }}t        j                  d«      } || «       ||«       ||«      }}} || «       ||«       ||«      }}}||z  ||z  }}||z  ||z  }}|rh||||f<   ||z  |||	f<   ||z  |||
f<   ||z  ||	|f<   | |z  |z   ||	|	f<   | |z  |z
  ||	|
f<   | |z  ||
|f<   ||z  |z   ||
|	f<   ||z  |z
  ||
|
f<   |S ||z  |||f<   ||z  |z
  |||	f<   ||z  |z   |||
f<   ||z  ||	|f<   ||z  |z   ||	|	f<   ||z  |z
  ||	|
f<   | ||
|f<   ||z  ||
|	f<   ||z  ||
|
f<   |S # t        t        f$ r t        |    |\  }}}}Y �ŒÂw xY w)av  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')
    >>> np.allclose(np.sum(R[0]), -1.34786452)
    True
    >>> R = euler_matrix(1, 2, 3, (0, 1, 0, 1))
    >>> np.allclose(np.sum(R[0]), -0.383436184)
    True
    >>> ai, aj, ak = (4*np.pi) * (np.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)

    r   r   r   )rI   r   rH   r   )Ú_AXES2TUPLEÚAttributeErrorÚKeyErrorÚ_TUPLE2AXESÚ
_NEXT_AXISr   r   r   rI   r   rH   r   )ÚaiÚajÚakÚaxesÚ	firstaxisÚparityÚ
repetitionÚframerC   ÚjÚkrI   r   rH   r!   ÚsiÚsjÚskÚciÚcjÚckÚccÚcsÚscÚsss                            r   rœ   rœ   U  s†  € ð&4Ü/:¸4Ñ/@Ñ,ˆ	�6˜: uð
 	€AÜ�1�v‘:Ñ€AÜ�1�v‘: ‘>Ñ"€AáØ�RˆBˆÙØ�S˜2˜# ˜s�ˆBˆà”#”d˜2“h“-Ñ÷ 	(Ñ'á�‹F‰ä—6‘6œ2Ÿ6™6ˆSˆÜ�F‰F�1‹Iˆá�R“™#˜b›'¡3 r£7ˆBˆ€BÙ�R“™#˜b›'¡3 r£7ˆBˆ€BØ�"‰W�b˜2‘gˆ€BØ�"‰W�b˜2‘gˆ€BáØˆˆ!ˆQˆ$‰Ø�r‘'ˆˆ!ˆQˆ$‰Ø�r‘'ˆˆ!ˆQˆ$‰Ø�r‘'ˆˆ!ˆQˆ$‰Ø�#˜‘(˜R‘-ˆˆ!ˆQˆ$‰Ø�#˜‘(˜R‘-ˆˆ!ˆQˆ$‰Ø�#˜‘(ˆˆ!ˆQˆ$‰Ø�r‘'˜B‘,ˆˆ!ˆQˆ$‰Ø�r‘'˜B‘,ˆˆ!ˆQˆ$‰ð €Hð �r‘'ˆˆ!ˆQˆ$‰Ø�r‘'˜B‘,ˆˆ!ˆQˆ$‰Ø�r‘'˜B‘,ˆˆ!ˆQˆ$‰Ø�r‘'ˆˆ!ˆQˆ$‰Ø�r‘'˜B‘,ˆˆ!ˆQˆ$‰Ø�r‘'˜B‘,ˆˆ!ˆQˆ$‰Ø�#ˆˆ!ˆQˆ$‰Ø�r‘'ˆˆ!ˆQˆ$‰Ø�r‘'ˆˆ!ˆQˆ$‰Ø€Høôa œHÐ%ò 4Ü�DÒØ/3Ñ,ˆ	�6˜:£uð4ús   ‚F0 Æ0GÇGc                 ó¬  — 	 t         |j                  «          \  }}}}|}t
        ||z      }t
        ||z
  dz      }t        j                  | t        j                  ¬«      dd…dd…f   }	|rØt        j                  |	||f   |	||f   z  |	||f   |	||f   z  z   «      }
|
t        kD  r^t        j                  |	||f   |	||f   «      }t        j                  |
|	||f   «      }t        j                  |	||f   |	||f    «      }�nt        j                  |	||f    |	||f   «      }t        j                  |
|	||f   «      }d}n×t        j                  |	||f   |	||f   z  |	||f   |	||f   z  z   «      }|t        kD  r]t        j                  |	||f   |	||f   «      }t        j                  |	||f    |«      }t        j                  |	||f   |	||f   «      }n?t        j                  |	||f    |	||f   «      }t        j                  |	||f    |«      }d}|r	| | | }}}|r||}}|||fS # t        t        f$ r t        |    |\  }}}}Y �Œ4w xY w)a”  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')
    >>> np.allclose(R0, R1)
    True
    >>> angles = (4*np.pi) * (np.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 np.allclose(R0, R1): print(axes, "failed")

    r   r3   Nr$   rF   )rÛ   ÚlowerrÜ   rÝ   rÞ   rß   r   r%   r9   r¢   rx   rY   )r(   rã   rä   rå   ræ   rç   rC   rè   ré   r!   ÚsyÚaxÚayÚazÚcys                  r   Úeuler_from_matrixrû   �  s†  € ð&4Ü/:¸4¿:¹:»<Ñ/HÑ,ˆ	�6˜: uð
 	€AÜ�1�v‘:Ñ€AÜ�1�v‘: ‘>Ñ"€Aä
�
‰
�6¤§¡Ô,¨R¨a¨R°°!°¨VÑ4€AÙÜ�W‰W�Q�q˜!�t‘W˜q  A ™wÑ&¨¨1¨a¨4©°1°Q¸°T±7Ñ):Ñ:Ó;ˆØ”Š9Ü—‘˜A˜a ˜d™G Q q¨! t¡WÓ-ˆBÜ—‘˜B  ! Q $¡Ó(ˆBÜ—‘˜A˜a ˜d™G a¨¨1¨¡g XÓ.ŠBä—‘˜Q˜q !˜t™W˜H a¨¨1¨¡gÓ.ˆBÜ—‘˜B  ! Q $¡Ó(ˆBØ‰Bä�W‰W�Q�q˜!�t‘W˜q  A ™wÑ&¨¨1¨a¨4©°1°Q¸°T±7Ñ):Ñ:Ó;ˆØ”Š9Ü—‘˜A˜a ˜d™G Q q¨! t¡WÓ-ˆBÜ—‘˜Q˜q !˜t™W˜H bÓ)ˆBÜ—‘˜A˜a ˜d™G Q q¨! t¡WÓ-‰Bä—‘˜Q˜q !˜t™W˜H a¨¨1¨¡gÓ.ˆBÜ—‘˜Q˜q !˜t™W˜H bÓ)ˆBØˆBáØ�S˜2˜# ˜s�ˆBˆÙØ�RˆBˆØˆr�2ˆ:ÐøôE œHÐ%ò 4Ü�DÒØ/3Ñ,ˆ	�6˜:£uð4ús   ‚H0 È0IÉIc                 ó,   — t        t        | «      |«      S )zÂReturn Euler angles from quaternion for specified axis sequence.

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

    )rû   r½   )Ú
quaternionrã   s     r   Úeuler_from_quaternionrþ   ×  s   € ô Ô.¨zÓ:¸DÓAÐAr   c                 óF  — 	 t         |j                  «          \  }}}}|dz   }t
        ||z   dz
     dz   }	t
        ||z
     dz   }
|r|| }} |r| }| dz  } |dz  }|dz  }t        j                  | «      }t        j                  | «      }t        j                  |«      }t        j                  |«      }t        j                  |«      }t        j                  |«      }||z  }||z  }||z  }||z  }t        j                  d«      }|r-|||z
  z  |d<   |||z   z  ||<   |||z   z  ||	<   |||z
  z  ||
<   n8||z  ||z  z   |d<   ||z  ||z  z
  ||<   ||z  ||z  z   ||	<   ||z  ||z  z
  ||
<   |r||	xx   dz  cc<   |S # t        t        f$ r t        |    |\  }}}}Y �Œ�w xY w)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')
    >>> np.allclose(q, [0.435953, 0.310622, -0.718287, 0.444435])
    True

    r   r+   ©r   r   rv   )
rÛ   rõ   rÜ   rÝ   rÞ   rß   r   rI   rH   r�   )rà   rá   râ   rã   rä   rå   ræ   rç   rC   rè   ré   rí   rê   rî   rë   rï   rì   rð   rñ   rò   ró   rÖ   s                         r   Úquaternion_from_eulerr  â  sæ  € ð4Ü/:¸4¿:¹:»<Ñ/HÑ,ˆ	�6˜: uð
 	�A‰€AÜ�1�v‘: ‘>Ñ" QÑ&€AÜ�1�v‘:Ñ Ñ"€AáØ�RˆBˆÙØˆSˆàˆ#�I€BØˆ#�I€BØˆ#�I€BÜ	�‰�‹€BÜ	�‰�‹€BÜ	�‰�‹€BÜ	�‰�‹€BÜ	�‰�‹€BÜ	�‰�‹€BØ	ˆb‰€BØ	ˆb‰€BØ	ˆb‰€BØ	ˆb‰€Bä
�‰�‹€AÙØ�R˜"‘W‰~ˆˆ!‰Ø�R˜"‘W‰~ˆˆ!‰Ø�R˜"‘W‰~ˆˆ!‰Ø�R˜"‘W‰~ˆˆ!Šà�B‰w˜˜b™Ñ ˆˆ!‰Ø�B‰w˜˜b™Ñ ˆˆ!‰Ø�B‰w˜˜b™Ñ ˆˆ!‰Ø�B‰w˜˜b™Ñ ˆˆ!‰ÙØ	ˆ!‹�‰‹à€HøôS œHÐ%ò 4Ü�DÒØ/3Ñ,ˆ	�6˜:£uð4ús   ‚E= Å=F ÆF c                 óä   — t        j                  d|d   |d   |d   g«      }t        |«      }|t        kD  r|t        j                  | dz  «      |z  z  }t        j
                  | dz  «      |d<   |S )z¥Return quaternion for rotation about axis.

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

    rF   r   r   rG   r+   )r   rK   rf   rx   rH   rI   )rM   r¯   rÖ   Úqlens       r   Úquaternion_about_axisr    sn   € ô 	�‰�#�t˜A‘w  Q¡¨¨a©Ð1Ó2€AÜ�q‹>€DØŒd‚{Ø	ŒR�V‰V�E˜C‘KÓ  4Ñ'Ñ'ˆÜ�6‰6�%˜#‘+Ó€A€a�DØ€Hr   c           	      óî  — t        j                  | t         j                  ¬«      j                  d«      }t	        ||«      }t        |«      }|t        k  }|| dd…fxx   t        j                  d|| df   z  «      z  cc<   |dd…ddd…f   |dd…dd…df   z  }t        j                  |ddf«      }d|dd…ddf   z
  |dd…ddf   z
  |dd…d	d	f<   |dd…d
df   |dd…dd	f   z
  |dd…d	d
f<   |dd…d
df   |dd…dd	f   z   |dd…d	df<   |dd…d
df   |dd…dd	f   z   |dd…d
d	f<   d|dd…d
d
f   z
  |dd…ddf   z
  |dd…d
d
f<   |dd…ddf   |dd…d
d	f   z
  |dd…d
df<   |dd…d
df   |dd…dd	f   z
  |dd…dd	f<   |dd…ddf   |dd…d
d	f   z   |dd…dd
f<   d|dd…d
d
f   z
  |dd…ddf   z
  |dd…ddf<   d|dd…ddf<   t        j                  d«      d   ||<   |j                  «       S )a  
    Return a homogeneous rotation matrix from quaternion.

    >>> M = quaternion_matrix([0.99810947, 0.06146124, 0, 0])
    >>> np.allclose(M, rotation_matrix(0.123, [1, 0, 0]))
    True
    >>> M = quaternion_matrix([1, 0, 0, 0])
    >>> np.allclose(M, np.identity(4))
    True
    >>> M = quaternion_matrix([0, 1, 0, 0])
    >>> np.allclose(M, np.diag([1, -1, -1, 1]))
    True
    >>> M = quaternion_matrix([[1, 0, 0, 0],[0, 1, 0, 0]])
    >>> np.allclose(M, np.array([np.identity(4), np.diag([1, -1, -1, 1])]))
    True


    r3   )r8   r   Nr+   r   r5   rG   r$   r   r   )N.)r   rK   r9   r´   r	   r   rx   r¢   r�   r   r@   )rý   rÖ   rŒ   Únum_qsÚ
identitiesÚrets         r   r½   r½   +  sO  € ô& 	�‰�¤2§:¡:Ô.×6Ñ6°wÓ?€AÜ�Q˜Ó€Aä�‹V€FØ”T‘€JØ€z€k’1€nÓœŸ™  q¨*¨°dÐ):Ñ';Ñ!;Ó<Ñ<ÓØ	Š!ˆT’1ˆ*‰˜š!šQ ˜*™Ñ%€Aô �(‰(�F˜A˜q�>Ó
"€Cð ˜š1˜a ˜7™Ñ# aª¨1¨a¨¡jÑ0€CŠˆ1ˆaˆ�LØ’Q˜˜1�W‘: ¢! Q¨ '¡
Ñ*€CŠˆ1ˆaˆ�LØ’Q˜˜1�W‘: ¢! Q¨ '¡
Ñ*€CŠˆ1ˆaˆ�LØ’Q˜˜1�W‘: ¢! Q¨ '¡
Ñ*€CŠˆ1ˆaˆ�LØ˜š1˜a ˜7™Ñ# aª¨1¨a¨¡jÑ0€CŠˆ1ˆaˆ�LØ’Q˜˜1�W‘: ¢! Q¨ '¡
Ñ*€CŠˆ1ˆaˆ�LØ’Q˜˜1�W‘: ¢! Q¨ '¡
Ñ*€CŠˆ1ˆaˆ�LØ’Q˜˜1�W‘: ¢! Q¨ '¡
Ñ*€CŠˆ1ˆaˆ�LØ˜š1˜a ˜7™Ñ# aª¨1¨a¨¡jÑ0€CŠˆ1ˆaˆ�LØ€CŠˆ1ˆaˆ�Lä—f‘f˜Q“i 	Ñ*€Cˆ
�Oà�;‰;‹=Ðr   c           
      óD  — t        j                  | t         j                  ¬«      dd…dd…f   }|�rt        j                  d«      }t        j                  |«      }||d   kD  r0||d<   |d   |d   z
  |d	<   |d
   |d   z
  |d<   |d   |d   z
  |d<   n‰d\  }}}|d   |d   kD  rd\  }}}|d   |||f   kD  rd\  }}}|||f   |||f   |||f   z   z
  |d   z   }|||<   |||f   |||f   z   ||<   |||f   |||f   z   ||<   |||f   |||f   z
  |d	<   |g d¢   }|dt        j
                  ||d   z  «      z  z  }nÃ|d   }|d   }	|d
   }
|d   }|d   }|d   }|d   }|d   }|d   }t        j                  ||z
  |z
  dddg|	|z   ||z
  |z
  ddg|
|z   ||z   ||z
  |z
  dg||z
  |
|z
  ||	z
  ||z   |z   gg«      }|dz  }t         j                  j                  |«      \  }}|g d¢t        j                  |«      f   }|d   dk  rt        j                  ||«       |S )a   Return quaternion from rotation matrix.

    If isprecise is True, the input matrix is assumed to be a precise rotation
    matrix and a faster algorithm is used.

    >>> q = quaternion_from_matrix(np.identity(4), True)
    >>> np.allclose(q, [1, 0, 0, 0])
    True
    >>> q = quaternion_from_matrix(np.diag([1, -1, -1, 1]))
    >>> np.allclose(q, [0, 1, 0, 0]) or np.allclose(q, [0, -1, 0, 0])
    True
    >>> R = rotation_matrix(0.123, (1, 2, 3))
    >>> q = quaternion_from_matrix(R, True)
    >>> np.allclose(q, [0.9981095, 0.0164262, 0.0328524, 0.0492786])
    True
    >>> R = [[-0.545, 0.797, 0.260, 0], [0.733, 0.603, -0.313, 0],
    ...      [-0.407, 0.021, -0.913, 0], [0, 0, 0, 1]]
    >>> q = quaternion_from_matrix(R)
    >>> np.allclose(q, [0.19069, 0.43736, 0.87485, -0.083611])
    True
    >>> R = [[0.395, 0.362, 0.843, 0], [-0.626, 0.796, -0.056, 0],
    ...      [-0.677, -0.498, 0.529, 0], [0, 0, 0, 1]]
    >>> q = quaternion_from_matrix(R)
    >>> np.allclose(q, [0.82336615, -0.13610694, 0.46344705, -0.29792603])
    True
    >>> R = random_rotation_matrix()
    >>> q = quaternion_from_matrix(R)
    >>> is_same_transform(R, quaternion_matrix(q))
    True
    >>> is_same_quaternion(quaternion_from_matrix(R, isprecise=False),
    ...                    quaternion_from_matrix(R, isprecise=True))
    True
    >>> R = euler_matrix(0.0, 0.0, np.pi/2.0)
    >>> is_same_quaternion(quaternion_from_matrix(R, isprecise=False),
    ...                    quaternion_from_matrix(R, isprecise=True))
    True

    r3   Nr   r   rn   r   rT   r…   r$   rU   )rG   r   rG   rV   r†   r   )r   r   rG   rl   rk   )r   rG   r   rm   )rG   r   r   )r$   r   r   rG   ç      à?rF   rd   )r   r%   r9   r�   rX   r¢   rK   r:   r»   r¼   r’   )r(   Ú	ispreciser!   rÖ   r   rC   rè   ré   Úm00Úm01Úm02Úm10Úm11Úm12Úm20Úm21Úm22ÚKrA   rB   s                       r   Úquaternion_from_matrixr  Z  sÀ  € ôN 	�
‰
�6¤§¡Ô,¨R¨a¨R°°!°¨VÑ4€AÚÜ�H‰H�T‹NˆÜ�H‰H�Q‹KˆØˆq�‰wŠ;ØˆAˆa‰DØ�T‘7˜Q˜t™WÑ$ˆAˆa‰DØ�T‘7˜Q˜t™WÑ$ˆAˆa‰DØ�T‘7˜Q˜t™WÑ$ˆAˆaŠDà‰GˆAˆq�!Ø�‰w˜˜4™Ò Ø!‘��1�aØ�‰w˜˜1˜a˜4™Ò Ø!‘��1�aØ�!�Q�$‘˜1˜Q ˜T™7 Q q¨! t¡WÑ,Ñ-°°$±Ñ7ˆAØˆAˆa‰DØ�Q˜�T‘7˜Q˜q !˜t™WÑ$ˆAˆa‰DØ�Q˜�T‘7˜Q˜q !˜t™WÑ$ˆAˆa‰DØ�Q˜�T‘7˜Q˜q !˜t™WÑ$ˆAˆa‰DØ’,‘ˆAØ	ˆS”2—7‘7˜1˜q ™w™;Ó'Ñ'Ñ'‰à�‰gˆØ�‰gˆØ�‰gˆØ�‰gˆØ�‰gˆØ�‰gˆØ�‰gˆØ�‰gˆØ�‰gˆä�H‰Hà�s‘˜S‘ # s¨CÐ0Ø�s‘˜C #™I¨™O¨S°#Ð6Ø�s‘˜C #™I s¨S¡y°3¡¸Ð<Ø�s‘˜C #™I s¨S¡y°#¸±)¸c±/ÐBð	ó
ˆð 	
ˆS‰ˆä�y‰y�~‰~˜aÓ ‰ˆˆ1ØŠlœBŸI™I a›LÐ(Ñ)ˆØˆ�tˆc‚zÜ
�‰�A�qÔØ€Hr   c                 ó  — |\  }}}}| \  }}}}	t        j                  | |z  ||z  z
  |	|z  z
  ||z  z   ||z  ||z  z   |	|z  z
  ||z  z   | |z  ||z  z   |	|z  z   ||z  z   ||z  ||z  z
  |	|z  z   ||z  z   gt         j                  ¬«      S )z¢Return multiplication of two quaternions.

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

    r3   ©r   rK   r9   )
Úquaternion1Úquaternion0Úw0Úx0Úy0Úz0Úw1Úx1Úy1Úz1s
             r   Úquaternion_multiplyr#  ³  sÉ   € ð !�N€BˆˆB�Ø �N€BˆˆB�Ü�8‰8àˆC�"‰H�r˜B‘wÑ  b¡Ñ(¨2°©7Ñ2Ø�‰G�b˜2‘gÑ  R¡Ñ'¨"¨r©'Ñ1ØˆC�"‰H�r˜B‘wÑ  b¡Ñ(¨2°©7Ñ2Ø�‰G�b˜2‘gÑ  R¡Ñ'¨"¨r©'Ñ1ð		
ô �j‰jôð r   c                 óˆ   — t        j                  | t         j                  ¬«      }t        j                  |dd |dd «       |S )z¨Return conjugate of quaternion.

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

    r3   r   N)r   rK   r9   r’   ©rý   rÖ   s     r   Úquaternion_conjugater&  È  s7   € ô 	�‰�¤2§:¡:Ô.€AÜ‡K�K��!�"��q˜˜�uÔØ€Hr   c                 ó¶   — t        j                  | t         j                  ¬«      }t        j                  |dd |dd «       |t        j                  ||«      z  S )z±Return inverse of quaternion.

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

    r3   r   N)r   rK   r9   r’   r.   r%  s     r   Úquaternion_inverser(  Ö  sG   € ô 	�‰�¤2§:¡:Ô.€AÜ‡K�K��!�"��q˜˜�uÔØŒr�v‰v�a˜‹|ÑÐr   c                 ó   — t        | d   «      S )zTReturn real part of quaternion.

    >>> quaternion_real([3, 0, 1, 2])
    3.0

    r   )Úfloat©rý   s    r   Úquaternion_realr,  ä  s   € ô �˜A‘ÓÐr   c                 óR   — t        j                  | dd t         j                  ¬«      S )ziReturn imaginary part of quaternion.

    >>> quaternion_imag([3, 0, 1, 2])
    array([0., 1., 2.])

    r   r   r3   r  r+  s    r   Úquaternion_imagr.  î  s   € ô �8‰8�J˜q �O¬2¯:©:Ô6Ð6r   c                 ó8  — t        | dd «      }t        |dd «      }|dk(  r|S |dk(  r|S t        j                  ||«      }t        t        |«      dz
  «      t        k  r|S |r|dk  r| }t        j
                  ||«       t        j                  |«      |t        j                  z  z   }t        |«      t        k  r|S dt        j                  |«      z  }	|t        j                  d|z
  |z  «      |	z  z  }|t        j                  ||z  «      |	z  z  }||z  }|S )aö  Return spherical linear interpolation between two quaternions.

    >>> q0 = random_quaternion()
    >>> q1 = random_quaternion()
    >>> q = quaternion_slerp(q0, q1, 0)
    >>> np.allclose(q, q0)
    True
    >>> q = quaternion_slerp(q0, q1, 1, 1)
    >>> np.allclose(q, q1)
    True
    >>> q = quaternion_slerp(q0, q1, 0.5)
    >>> angle = np.arccos(np.dot(q0, q))
    >>> np.allclose(2, np.arccos(np.dot(q0, q1)) / angle) or         np.allclose(2, np.arccos(-np.dot(q0, q1)) / angle)
    True

    Nr   rF   r5   )	r,   r   r.   r=   rx   r’   ÚarccosÚpirH   )
Úquat0Úquat1ÚfractionÚspinÚshortestpathÚq0Úq1ÚdrM   Úisins
             r   Úquaternion_slerpr;  ø  s  € ô$ 
�U˜2˜A�YÓ	€BÜ	�U˜2˜A�YÓ	€BØ�3‚Øˆ	Ø	�SŠØˆ	Ü
�‰ˆr�2‹€AÜ
Œ3ˆq‹6�C‰<Óœ4ÒØˆ	Ù˜˜CšàˆBˆÜ
�‰�B˜ÔÜ�I‰I�a‹L˜4¤"§%¡%™<Ñ'€EÜ
ˆ5ƒz”DÒØˆ	Ø”—‘˜“Ñ€DØŒ"�&‰&�#˜‘. EÑ)Ó
*¨TÑ
1Ñ1€BØŒ"�&‰&�˜EÑ!Ó
" TÑ
)Ñ)€BØˆ"�H€BØ€Ir   Úseedc                 óF  — | €-t        |«      j                  d|z  «      j                  d«      } n| j                  d   dk(  sJ ‚t	        j
                  d| d   z
  «      }t	        j
                  | d   «      }t        j                  dz  }|| d   z  }|| d   z  }t	        j                  t	        j                  |«      |z  t	        j                  |«      |z  t	        j                  |«      |z  t	        j                  |«      |z  g«      j                  j                  «       S )al  Return uniform random unit quaternion.

    rand: array like or None
        Three independent random variables that are uniformly distributed
        between 0 and 1.
    seed : None or int
        Seed for deterministic results, otherwise OS entropy.

    >>> q = random_quaternion()
    >>> np.allclose(1, vector_norm(q))
    True
    >>> q = random_quaternion(num=10)
    >>> np.allclose(1, vector_norm(q, axis=1))
    True
    >>> q = random_quaternion(random_vector(3))
    >>> len(q.shape), q.shape[0]==4
    (1, True)
    >>> bool(np.allclose(random_quaternion(seed=0), random_quaternion(seed=0)))
    True

    r$   )r$   r8   r   r5   r+   r   rG   )r
   Úrandomr´   r²   r   r¢   r1  rK   rI   rH   rW   r@   )ÚrandÚnumr<  Úr1Úr2Úpi2rÄ   Út2s           r   Úrandom_quaternionrE  !  sê   € ð, €|Ü Ó%×,Ñ,¨Q°©WÓ5×=Ñ=¸gÓF‰à�z‰z˜!‰} Ò!Ð!Ð!Ü	�‰��t˜A‘w‘Ó	€BÜ	�‰��a‘Ó	€BÜ
�%‰%�#‰+€CØ	ˆt�A‰w‰€BØ	ˆt�A‰w‰€BÜ�8‰8Ü	�‰�‹�b‰œ"Ÿ&™& ›* r™/¬2¯6©6°"«:¸©?¼B¿F¹FÀ2»JÈ¹OÐLóç�a�‰‹	ðr   r?  r@  r—   c                 óœ   — t        |«      }t        t        | ||¬«      «      }|r'|j                  d«      dz
  t	        |«      z  |dd…df<   |S )a\  
    Return uniform random rotation matrix.

    Parameters
    ------------
    rand : (3,)
      Three independent random variables that are uniformly distributed
      between 0 and 1 for each returned quaternion.
    num
      Number of matrices to return.
    translate
      If passed the rotation matrix will include translation
      that is random and between positive and negative half this value.
    seed
      Seed for deterministic results, otherwise OS entropy.

    >>> R = random_rotation_matrix()
    >>> np.allclose(np.dot(R.T, R), np.identity(4))
    True
    >>> R = random_rotation_matrix(num=10)
    >>> np.allclose(np.einsum('...ji,...jk->...ik', R, R), np.identity(4))
    True
    >>> bool(np.allclose(random_rotation_matrix(seed=0, translate=10),
    ...                  random_rotation_matrix(seed=0, translate=10)))
    True

    )r?  r@  r<  r$   r
  N)r
   r½   rE  r>  r*  )r?  r@  r—   r<  r>  r(   s         r   Úrandom_rotation_matrixrG  E  sT   € ôF ˜dÓ#€FÜÔ0°dÀÈ&ÔQÓR€FÙàŸ™ qÓ)¨CÑ/´5¸Ó3CÑCˆˆr�ˆr�1ˆu‰à€Mr   c                   ór   — e Zd ZdZdd„Zd„ Zd„ Zed„ «       Zej                  d„ «       Zd„ Z
d	„ Zdd
„Zd„ Zy)ÚArcballaO  Virtual Trackball Control.

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

    Nc                 óÞ  — d| _         d| _        d| _        ddg| _        t	        j
                  g d¢«      | _        d| _        |€t	        j
                  g d¢«      | _        nut	        j
                  |t        j                  ¬«      }|j                  dk(  rt        |«      | _        n0|j                  d	k(  r|t        |«      z  }|| _        nt        d
«      ‚| j                  x| _        | _        y)z`Initialize virtual trackball control.

        initial : quaternion or rotation matrix

        Nr5   rF   )rF   rF   r5   F)r5   rF   rF   rF   r3   ©r   r   r   z"initial not a quaternion or matrix)Ú_axisÚ_axesÚ_radiusÚ_centerr   rK   Ú_vdownÚ
_constrainÚ_qdownr9   r²   r  rf   r?   Ú_qnowÚ_qpre)ÚselfÚinitials     r   Ú__init__zArcball.__init__‰  s½   € ð ˆŒ
ØˆŒ
ØˆŒØ˜S�zˆŒÜ—h‘hšÓ/ˆŒØˆŒØˆ?ÜŸ(™(Ò#7Ó8ˆD�Kä—h‘h˜w¬b¯j©jÔ9ˆGØ�}‰} Ò&Ü4°WÓ=�•Ø—‘ $Ò&Øœ; wÓ/Ñ/�Ø%�•ä Ð!EÓFÐFØ"&§+¡+Ð-ˆŒ
�T•Zr   c                 ól   — t        |«      | _        |d   | j                  d<   |d   | j                  d<   y)z×Place Arcball, e.g. when window size changes.

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

        r   r   N)r*  rN  rO  )rU  ÚcenterÚradiuss      r   ÚplacezArcball.place¢  s1   € ô ˜V“}ˆŒØ  ™)ˆ�‰�Q‰Ø  ™)ˆ�‰�QŠr   c                 ó^   — |€d| _         y|D �cg c]  }t        |«      ‘Œ c}| _         yc c}w )z Set axes to constrain rotations.N)rM  r,   )rU  rã   r¯   s      r   ÚsetaxeszArcball.setaxes¯  s,   € àˆ<ØˆD�Já8<Ó=¹°œ+ dÕ+¸Ñ=ˆD�JùÒ=s   �*c                 ó   — | j                   S )z'Return state of constrain to axis mode.)rQ  ©rU  s    r   Ú	constrainzArcball.constrain¶  s   € ð �‰Ðr   c                 ó$   — t        |«      | _        y)z$Set state of constrain to axis mode.N)ÚboolrQ  )rU  Úvalues     r   r`  zArcball.constrain»  s   € ô ˜u›+ˆ�r   c                 óT  — t        || j                  | j                  «      | _        | j                  x| _        | _        | j                  rW| j                  �Kt        | j                  | j                  «      | _
        t        | j                  | j                  «      | _        yd| _
        y)z>Set initial cursor window coordinates and pick constrain-axis.N)Úarcball_map_to_sphererO  rN  rP  rS  rR  rT  rQ  rM  Úarcball_nearest_axisrL  Úarcball_constrain_to_axis)rU  r/   s     r   ÚdownzArcball.downÀ  so   € ä+¨E°4·<±<ÀÇÁÓNˆŒØ#'§:¡:Ð-ˆŒ�d”jØ�?Š?˜tŸz™zÐ5Ü-¨d¯k©k¸4¿:¹:ÓFˆDŒJÜ3°D·K±KÀÇÁÓLˆD�KàˆD�Jr   c                 óÚ  — t        || j                  | j                  «      }| j                  �t	        || j                  «      }| j
                  | _        t        j                  | j                  |«      }t        j                  ||«      t        k  r| j                  | _        yt        j                  | j                  |«      |d   |d   |d   g}t        || j                  «      | _        y)z)Update current cursor window coordinates.Nr   r   rG   )re  rO  rN  rL  rg  rS  rT  r   r‡   rP  r.   rx   rR  r#  )rU  r/   Úvnowr   rÖ   s        r   ÚdragzArcball.dragÊ  s©   € ä$ U¨D¯L©L¸$¿,¹,ÓGˆØ�:‰:Ð!Ü,¨T°4·:±:Ó>ˆDØ—Z‘ZˆŒ
Ü�H‰H�T—[‘[ $Ó'ˆÜ�6‰6�!�Q‹<œ$ÒØŸ™ˆD�Jä—‘˜Ÿ™ TÓ*¨A¨a©D°!°A±$¸¸!¹Ð=ˆAÜ,¨Q°·±Ó<ˆD�Jr   c                 ó€   — t        | j                  | j                  d|z   d«      }| j                  |c| _        | _        y)z,Continue rotation in direction of last drag.r+   FN)r;  rT  rS  )rU  ÚaccelerationrÖ   s      r   ÚnextzArcball.next×  s3   € ä˜TŸZ™Z¨¯©°S¸<Ñ5GÈÓOˆØ!%§¡¨QÐˆŒ
�D•Jr   c                 ó,   — t        | j                  «      S )z#Return homogeneous rotation matrix.)r½   rS  r_  s    r   r(   zArcball.matrixÜ  s   € ä  §¡Ó,Ð,r   ©Nr°   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__rW  r[  r]  Úpropertyr`  Úsetterrh  rk  rn  r(   r   r   r   rI  rI  q  sX   „ ñó..ò2$ò>ð ñó ðð ×Ññ&ó ð&òò=ó/ó
-r   rI  c                 ó  — | d   |d   z
  |z  }|d   | d   z
  |z  }||z  ||z  z   }|dkD  r3t        j                  |«      }t        j                  ||z  ||z  dg«      S t        j                  ||t        j                  d|z
  «      g«      S )z7Return unit sphere coordinates from window coordinates.r   r   r5   rF   )r   r¢   rK   )r/   rY  rZ  r¾   r¿   rŒ   s         r   re  re  á  s—   € à
�‰(�V˜A‘YÑ
 &Ñ	(€BØ
�‰)�e˜A‘hÑ
 &Ñ	(€BØ
ˆR‰�"�r‘'Ñ€AØˆ3‚wä�G‰G�A‹JˆÜ�x‰x˜˜a™  a¡¨Ð-Ó.Ð.ä�x‰x˜˜R¤§¡¨¨q©Ó!1Ð2Ó3Ð3r   c                 ó¨  — t        j                  | t         j                  ¬«      }t        j                  |t         j                  ¬«      }||t        j                  ||«      z  z  }t	        |«      }|t
        kD  r%|d   dk  rt        j                  ||«       ||z  }|S |d   dk(  rt        j                  g d¢«      S t        |d    |d   dg«      S )z*Return sphere point perpendicular to axis.r3   rG   rF   r5   )r5   rF   rF   r   r   )r   rK   r9   r.   rf   rx   r’   r,   )r/   r¯   r   r¤   rŒ   s        r   rg  rg  î  s°   € ä
�‰�œbŸj™jÔ)€AÜ
�‰�œRŸZ™ZÔ(€AØˆŒR�V‰V�A�q‹\Ñ	Ñ€AÜ�A‹€AØŒ4‚xØˆQ‰4�#Š:Ü�K‰K˜˜1ÔØ	ˆQ‰ˆØˆØˆ�tˆs‚{Ü�x‰xšÓ(Ð(Ü˜˜1™˜˜q ™t SÐ)Ó*Ð*r   c                 óº   — t        j                  | t         j                  ¬«      } d}d}|D ],  }t        j                  t	        | |«      | «      }||kD  sŒ)|}|}Œ. |S )z+Return axis, which arc is nearest to point.r3   Nrv   )r   r%   r9   r.   rg  )r/   rã   ÚnearestÚmxr¯   r   s         r   rf  rf  þ  sZ   € ä�J‰J�u¤B§J¡JÔ/€EØ€GØ	€BÛˆÜ�F‰FÔ,¨U°DÓ9¸5ÓAˆØˆr‹6ØˆGØ‰Bð	 ð
 €Nr   g      @)r   rG   r   r   )r   r   r   r   Úsxyx)r   r   r   r   Úsxzy)r   r   r   r   Úsxzx)r   r   r   r   Úsyzx)r   r   r   r   Úsyzy)r   r   r   r   Úsyxz)r   r   r   r   Úsyxy)r   r   r   r   Úszxy)rG   r   r   r   Úszxz)rG   r   r   r   Úszyx)rG   r   r   r   Úszyz)rG   r   r   r   Úrzyx©r   r   r   r   Úrxyx)r   r   r   r   Úryzx)r   r   r   r   Úrxzx)r   r   r   r   Úrxzy)r   r   r   r   )r   r   r   r   )r   r   r   r   )r   r   r   r   )rG   r   r   r   )rG   r   r   r   )rG   r   r   r   )rG   r   r   r   )ÚryzyÚrzxyÚryxyÚryxzÚrzxzÚrxyzÚrzyzc                 ó¶  — t        j                  | t         j                  ¬«      } |€| j                  dk(  r)t        j                  t        j
                  | | «      «      S | | z  } t        j                  t        j                  | |¬«      «      }t        j                  ||«       |S | | z  } t        j                  | ||¬«       t        j                  ||«       y)aŠ  Return length, i.e. Euclidean norm, of ndarray along axis.

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

    r3   Nr   r®   )r¯   Úout)r   rK   r9   Úndimr¢   r.   Ú
atleast_1dr¹   )Údatar¯   r•  s      r   rf   rf   0  sž   € ô2 �8‰8�D¤§
¡
Ô+€DØ
€{Ø�9‰9˜Š>Ü—7‘7œ2Ÿ6™6 $¨Ó-Ó.Ð.Ø�‰ˆÜ�m‰mœBŸF™F 4¨dÔ3Ó4ˆÜ
�‰��SÔØˆ
à�‰ˆÜ
�‰ˆt˜$ CÕ(Ü
�‰��SÕr   c                 óÎ  — |€bt        j                  | t         j                  ¬«      } | j                  dk(  rL| t        j                  t        j
                  | | «      «      z  } | S || urt        j                  | «      |dd |} t        j                  t        j                  | | z  |«      «      }t        j                  ||«       |�t        j                  ||«      }| |z  } |€| S y)aÐ  Return ndarray normalized by length, i.e. Euclidean norm, along axis.

    >>> v0 = np.random.random(3)
    >>> v1 = unit_vector(v0)
    >>> np.allclose(v1, v0 / np.linalg.norm(v0))
    True
    >>> v0 = np.random.rand(5, 4, 3)
    >>> v1 = unit_vector(v0, axis=-1)
    >>> v2 = v0 / np.expand_dims(np.sqrt(np.sum(v0*v0, axis=2)), 2)
    >>> np.allclose(v1, v2)
    True
    >>> v1 = unit_vector(v0, axis=1)
    >>> v2 = v0 / np.expand_dims(np.sqrt(np.sum(v0*v0, axis=1)), 1)
    >>> np.allclose(v1, v2)
    True
    >>> v1 = np.zeros((5, 4, 3))
    >>> unit_vector(v0, axis=1, out=v1)
    >>> np.allclose(v1, v2)
    True
    >>> list(unit_vector([]))
    []
    >>> [float(i) for i in unit_vector([1])]
    [1.0]

    Nr3   r   )
r   rK   r9   r–  r¢   r.   r%   r—  r¹   Úexpand_dims)r˜  r¯   r•  Úlengths       r   r,   r,   W  sÂ   € ð4 €{Ü�x‰x˜¤B§J¡JÔ/ˆØ�9‰9˜Š>Ø”B—G‘GœBŸF™F 4¨Ó.Ó/Ñ/ˆDØˆKà�d‰?Ü—Z‘Z Ó%ˆC‘ˆFØˆÜ�]‰]œ2Ÿ6™6 $¨¡+¨tÓ4Ó5€FÜ‡G�GˆF�FÔØÐÜ—‘ ¨Ó-ˆØˆF�N€DØ
€{Øˆð r   c                 ó6   — t        |«      j                  | «      S )ad  Return array of random doubles in the half-open interval [0.0, 1.0).

    >>> v = random_vector(10000)
    >>> bool(np.all(v >= 0) and np.all(v < 1))
    True
    >>> v0 = random_vector(10)
    >>> v1 = random_vector(10)
    >>> bool(np.any(v0 == v1))
    False
    >>> bool(np.allclose(random_vector(10, seed=0), random_vector(10, seed=0)))
    True

    )r
   r>  )Úsizer<  s     r   Úrandom_vectorrž  ƒ  s   € ô ˜DÓ!×(Ñ(¨Ó.Ð.r   c                 ó2   — t        j                  | ||¬«      S )a?  Return vector perpendicular to vectors.

    >>> v = vector_product([2, 0, 0], [0, 3, 0])
    >>> np.allclose(v, [0, 0, 6])
    True
    >>> v0 = [[2, 0, 0, 2], [0, 2, 0, 2], [0, 0, 2, 2]]
    >>> v1 = [[3], [0], [0]]
    >>> v = vector_product(v0, v1)
    >>> np.allclose(v, [[0, 0, 0, 0], [0, 0, 6, 6], [0, -6, 0, -6]])
    True
    >>> v0 = [[2, 0, 0], [2, 0, 0], [0, 2, 0], [2, 0, 0]]
    >>> v1 = [[0, 3, 0], [0, 0, 3], [0, 0, 3], [3, 3, 3]]
    >>> v = vector_product(v0, v1, axis=1)
    >>> np.allclose(v, [[0, 0, 6], [0, -6, 0], [6, 0, 0], [0, -6, 6]])
    True

    r®   )r   r‡   )r¾   r¿   r¯   s      r   Úvector_productr   ”  s   € ô$ �8‰8�B˜ Ô&Ð&r   c                 ó”  — t        j                  | t         j                  ¬«      } t        j                  |t         j                  ¬«      }t        j                  | |z  |¬«      }|t	        | |¬«      t	        ||¬«      z  z  }t        j
                  |dd«      }t        j                  |r|«      S t        j                  |«      «      S )a  Return angle between vectors.

    If directed is False, the input vectors are interpreted as undirected axes,
    i.e. the maximum angle is pi/2.

    >>> a = angle_between_vectors([1, -2, 3], [-1, 2, -3])
    >>> np.allclose(a, np.pi)
    True
    >>> a = angle_between_vectors([1, -2, 3], [-1, 2, -3], directed=False)
    >>> np.allclose(a, 0)
    True
    >>> v0 = [[2, 0, 0, 2], [0, 2, 0, 2], [0, 0, 2, 2]]
    >>> v1 = [[3], [0], [0]]
    >>> a = angle_between_vectors(v0, v1)
    >>> np.allclose(a, [0, 1.5708, 1.5708, 0.95532])
    True
    >>> v0 = [[2, 0, 0], [2, 0, 0], [0, 2, 0], [2, 0, 0]]
    >>> v1 = [[0, 3, 0], [0, 0, 3], [0, 0, 3], [3, 3, 3]]
    >>> a = angle_between_vectors(v0, v1, axis=1)
    >>> np.allclose(a, [1.5708, 1.5708, 1.5708, 0.95532])
    True

    r3   r®   rv   r5   )r   r%   r9   r¹   rf   Úclipr0  Úfabs)r¾   r¿   Údirectedr¯   r.   s        r   Úangle_between_vectorsr¥  ©  s’   € ô0 
�‰�BœbŸj™jÔ	)€BÜ	�‰�BœbŸj™jÔ	)€BÜ
�&‰&��b‘˜tÔ
$€CØŒ;�r Ô%¬°B¸TÔ(BÑBÑB€Cô �'‰'�#�t˜SÓ
!€CÜ�9‰9™H�SÓ7Ð7¬"¯'©'°#«,Ó7Ð7r   c                 ó@   — t         j                  j                  | «      S )ah  Return inverse of square transformation matrix.

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

    )r   r:   r‘   r'   s    r   Úinverse_matrixr§  Ë  s   € ô �9‰9�=‰=˜Ó Ð r   c                  ój   — t        j                  d«      }| D ]  }t        j                  ||«      }Œ |S )zúReturn concatenation of series of transformation matrices.

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

    r   )r   r   r.   )Úmatricesr!   rC   s      r   Úconcatenate_matricesrª  Û  s/   € ô 	�‰�A‹€AÛˆÜ�F‰F�1�a‹L‰ð à€Hr   c                 óâ   — t        j                  | t         j                  ¬«      } | | d   z  } t        j                  |t         j                  ¬«      }||d   z  }t        j                  | |«      S )zÐReturn True if two matrices perform same transformation.

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

    r3   rn   )r   rK   r9   Úallclose)Úmatrix0Úmatrix1s     r   Úis_same_transformr¯  ë  sY   € ô �h‰h�w¤b§j¡jÔ1€GØˆw�t‰}Ñ€GÜ�h‰h�w¤b§j¡jÔ1€GØˆw�t‰}Ñ€GÜ�;‰;�w Ó(Ð(r   c                 ó´   — t        j                  | «      } t        j                  |«      }t        j                  | |«      xs t        j                  | | «      S )z)Return True if two quaternions are equal.)r   rK   r¬  )r7  r8  s     r   Úis_same_quaternionr±  û  s?   € ä	�‰�"‹€BÜ	�‰�"‹€BÜ�;‰;�r˜2ÓÒ6¤"§+¡+¨b°2°#Ó"6Ð6r   c                 ób  — t        j                  |«      }t        j                  | «      } t        |«      }| j                  |dz   |dz   fk7  rt	        d«      ‚t        j
                  |dz   «      }| |d|…|f<   t        j                  | |«      }||d|…|f<   t        j                  ||«      }|S )a#  
    Given a transformation matrix, apply its rotation
    around a point in space.

    Parameters
    ----------
    matrix: (4,4) or (3, 3) float, transformation matrix
    point:  (3,) or (2,)  float, point in space

    Returns
    ---------
    result: (4,4) transformation matrix
    r   zmatrix must be (d+1, d+1)N)r   Ú
asanyarrayr   r²   r?   r   r.   )r(   r/   r    r—   Úresults        r   Útransform_aroundrµ    sª   € ô �M‰M˜%Ó €EÜ�]‰]˜6Ó"€FÜ
ˆe‹*€CØ‡|�|˜˜a™  q¡Ð)Ò)ÜÐ4Ó5Ð5ä—‘�s˜Q‘w“€IØ!˜6€Iˆdˆsˆd�CˆiÑÜ�V‰V�F˜IÓ&€FØ €Iˆdˆsˆd�CˆiÑÜ�V‰V�I˜vÓ&€Fà€Mr   c                 ój  — | €ddg} |€d}t        j                  | t         j                  ¬«      } t        |«      }t        j                  |«      st        d«      ‚| j                  dk7  rt        d«      ‚t        j                  dt         j                  ¬«      }t        j                  |«      }t        j                  |«      }||g|ddd	…f<   | |g|d
dd	…f<   | |dd	…d	f<   |�t        ||¬«      }|�>t        j                  d«      }|dd	…dd	…fxx   |z  cc<   t        j                  ||«      }|S )au  
    2D homogeonous transformation matrix.

    Parameters
    ----------
    offset : (2,) float
      XY offset
    theta : float
      Rotation around Z in radians
    point :  (2, ) float
      Point to rotate around
    scale : (2,) float or None
      Scale to apply

    Returns
    ----------
    matrix : (3, 3) flat
      Homogeneous 2D transformation matrix
    NrF   r3   ztheta must be finite angle!)rG   zoffset must be length 2!r$   r   rG   r   )r(   r/   )r   r³  r9   r*  Úisfiniter?   r²   r   rH   rI   rµ  r.   )ÚoffsetÚthetar/   rq   rW   rÈ   r¦   rž   s           r   Úplanar_matrixrº    s'  € ð( €~Ø�s�ˆØ€}ØˆÜ�]‰]˜6¬¯©Ô4€FÜ�%‹L€EÜ�;‰;�uÔÜÐ6Ó7Ð7Ø‡|�|�tÒÜÐ3Ó4Ð4ä
�‰ˆqœŸ
™
Ô#€AÜ
�‰ˆu‹€AÜ
�‰ˆu‹€Aà�1ˆv€A€aˆˆ!ˆ€e�HØ��Aˆw€A€aˆˆ!ˆ€e�HØ€A€b€q€bˆ!€e�HàÐÜ A¨UÔ3ˆàÐÜ�F‰F�1‹IˆØ	ˆ"ˆ1ˆ"ˆbˆqˆbˆ&‹	�UÑ‹	Ü�F‰F�1�a‹Lˆà€Hr   c                 óö   — t        j                  | t         j                  ¬«      } | j                  dk7  rt	        d«      ‚t        j
                  d«      }| dd…df   |dd…df<   | dd…dd…f   |dd…dd…f<   |S )a+  
    Given a 2D homogeneous rotation matrix convert it to a 3D rotation
    matrix that is rotating around the Z axis

    Parameters
    ----------
    matrix_2D: (3,3) float, homogeneous 2D rotation matrix

    Returns
    ----------
    matrix_3D: (4,4) float, homogeneous 3D rotation matrix
    r3   rn   z.Homogeneous 2D transformation matrix required!r   NrG   r$   )r   r³  r9   r²   r?   r   )Ú	matrix_2DÚ	matrix_3Ds     r   Úplanar_matrix_to_3Dr¾  Q  s‰   € ô —‘˜i¬r¯z©zÔ:€IØ‡�˜&Ò ÜÐIÓJÐJä—‘�q“	€Ià   !  Q Ñ'€Iˆbˆqˆb�!ˆeÑà! " 1 " b q b &Ñ)€Iˆbˆqˆb�"�1�"ˆfÑàÐr   c                 ó$   — t        d|| |¬«      }|S )aÜ  
    Give a spherical coordinate vector, find the rotation that will
    transform a [0,0,1] vector to those coordinates

    Parameters
    -----------
    theta: float, rotation angle in radians
    phi:   float, rotation angle in radians

    Returns
    ----------
    matrix: (4,4) rotation matrix where the following will
             be a cartesian vector in the direction of the
             input spherical coordinates:
                np.dot(matrix, [0,0,1,0])

    rF   )rã   )rœ   )r¹  Úphirã   r´  s       r   Úspherical_matrixrÁ  l  s   € ô$ ˜#˜s E°Ô5€FØ€Mr   Úpointsr(   Úreturnc                 óÖ  — t        j                  | t         j                  ¬«      } t        | «      dk(  s|€| j	                  «       S t        j                  |t         j                  ¬«      }| j
                  \  }}t        j                  |t        d|dz   …d|dz   …f   z
  «      j                  «       dk  r#t        j                  | j	                  «       «      S |r]t        j                  | t        j                  |«      f«      }t        j                  ||j                  «      j                  dd…d|…f   S t        j                  |d|…d|…f   | j                  «      j                  S )aé  
    Returns points rotated by a homogeneous
    transformation matrix.

    If points are (n, 2) matrix must be (3, 3)
    If points are (n, 3) matrix must be (4, 4)

    Parameters
    ----------
    points : (n, dim) float
      Points where `dim` is 2 or 3.
    matrix : (3, 3) or (4, 4) float
      Homogeneous rotation matrix.
    translate : bool
      Apply translation from matrix or not.

    Returns
    ----------
    transformed : (n, dim) float
      Transformed points.
    r3   r   Nr   r6   )r   r³  r9   r   r&   r²   r=   Ú	_IDENTITYÚmaxÚascontiguousarrayÚcolumn_stackÚonesr.   rW   )rÂ  r(   r—   Úcountr    Ústacks         r   Útransform_pointsrÌ  ‚  s  € ô0 �]‰]˜6¬¯©Ô4€FÜ
ˆ6ƒ{�aÒ˜6˜>Ø�{‰{‹}Ðô �]‰]˜6¬¯©Ô4€Fà—‘�J€Eˆ3ô 
‡v�vˆf”y  3¨¡7 ¨I¨c°A©g¨IÐ!5Ñ6Ñ6Ó7×;Ñ;Ó=ÀÒDÜ×#Ñ# F§K¡K£MÓ2Ð2áä—‘ ¬¯©°«Ð 8Ó9ˆÜ�v‰v�f˜eŸg™gÓ&×(Ñ(ª¨D¨S¨D¨Ñ1Ð1ô �6‰6�&˜˜#˜˜t ˜t˜Ñ$ f§h¡hÓ/×1Ñ1Ð1r   c           	      ó"  — | j                   d   dz
  }t        j                  t        j                  | d|…d|…f   | d|…d|…f   j                  «      t
        d|…d|…f   z
  «      j                  «       }|dkD  r”||k  r�t        j                  j                  | d|…d|…f   «      \  }}}t        j                  |dz   «      }t        j                  ||«      |d|…d|…f<   | d|…|f   |d|…|f<   t        j                  || |¬«      sJ ‚|S | S )a  
    If a homogeneous transformation matrix is *almost* a rigid
    transform but many matrix-multiplies have accumulated some
    floating point error try to restore the matrix using SVD.

    Parameters
    -----------
    matrix : (4, 4) or (3, 3) float
      Homogeneous transformation matrix.
    max_deviance : float
      Do not alter the matrix if it is not rigid by more
      than this amount.

    Returns
    ----------
    repaired : (4, 4) or (3, 3) float
      Repaired homogeneous transformation matrix
    r   r   Ng‚vIhÂ%<=)Úatol)r²   r   r=   r.   rW   rÅ  rÆ  r:   r¶   r   r¬  )r(   Úmax_deviancer    ÚcheckÚUr¨   rB   Úrepaireds           r   Ú	fix_rigidrÓ  °  s#  € ð& �,‰,�q‰/˜AÑ
€CÜ�F‰FÜ
�‰ˆv�d�s�d˜D˜S˜D�jÑ! 6¨$¨3¨$°°°¨*Ñ#5×#7Ñ#7Ó8¼9ÀTÀcÀTÈ4ÈCÈ4ÀZÑ;PÑPóç	�cƒeð 
ð
 ˆu‚}˜ Ò-ä—)‘)—-‘-  t¨ t¨T¨c¨T zÑ 2Ó3‰ˆˆ1ˆaÜ—6‘6˜# ™'“?ˆÜ!Ÿv™v a¨›|ˆ��#��t˜�t�Ñà$ T c T¨3 YÑ/ˆ��#��s�Ñä�{‰{˜8 V°,Õ?Ð?Ð?Øˆà€Mr   c                 ób  — t        j                  | t         j                  ¬«      } | j                  dk7  ryt        j                  | d   g d¢z
  «      |kD  ryt        j
                  | dd…dd…f   | dd…dd…f   j                  «      t        dd…dd…f   z
  }t        j                  |«      |k  S )a7  
    Check to make sure a homogeonous transformation
    matrix is a rigid transform.

    Parameters
    -----------
    matrix : (4, 4) float
      A transformation matrix

    Returns
    -----------
    check : bool
      True if matrix is a a transform with
      only translation, scale, and rotation
    r3   rK  Fr8   rˆ  Nr$   )r   r³  r9   r²   Úptpr.   rW   rÅ  )r(   ÚepsilonrÐ  s      r   Úis_rigidr×  ×  s§   € ô" �]‰]˜6¬¯©Ô4€Fà‡|�|�vÒØô 
‡v�vˆf�R‰jš<Ñ'Ó(¨7Ò2Øô �F‰F�6˜"˜1˜"˜b˜q˜b˜&‘> 6¨"¨1¨"¨b¨q¨b¨&¡>×#3Ñ#3Ó4´yÀÀ!ÀÀRÀaÀRÀÑ7HÑH€Eä�6‰6�%‹=˜7Ñ"Ð"r   c                 óœ   — t        j                  d«      }t        j                  | dk7  «      r|dd…dd…fxx   | z  cc<   |�	||dd…df<   |S )a  
    Optimized version of `compose_matrix` for just
    scaling then translating.

    Scalar args are broadcast to arrays of shape (3,)

    Parameters
    --------------
    scale : float or (3,) float
      Scale factor
    translate : float or (3,) float
      Translation
    r   r   Nr$   )r   r   r   )rq   r—   r!   s      r   Úscale_and_translaterÙ  ÷  sW   € ô 	�‰ˆq‹	€AÜ	‡v�vˆe�q‰jÔØ	ˆ"ˆ1ˆ"ˆbˆqˆbˆ&‹	�UÑ‹	ØÐØˆˆ"ˆ1ˆ"ˆaˆ%‰Ø€Hr   c                 ó   — t        j                  | t         j                  ¬«      } | j                  dvrt	        d| j                  ›�«      ‚t        t         j                  j                  | dd…dd…f   «      dk  «      S )a  
    Check to see if a matrix will invert triangles.

    Parameters
    -------------
    matrix : (4, 4) float
      Homogeneous transformation matrix

    Returns
    --------------
    flip : bool
      True if matrix will flip winding of triangles.
    r3   )rn   rK  z%matrix must be (3, 3) or (4, 4), not Nr$   rF   )r   r³  r9   r²   r?   rb  r:   r�   r'   s    r   Úflips_windingrÛ  	  sj   € ô �]‰]˜6¬¯©Ô4€FØ‡|�|Ð+Ñ+ÜÐ@ÀÇÁÐ@PÐQÓRÐRä”—	‘	—‘˜f R a R¨¨!¨ V™nÓ-°Ñ3Ó4Ð4r   rp  )NN)NNF)F)NNNNN)TTT)FT)r›   )r   T)Nr   N)Nr   NN)r   )Tr   )NNNN)T)gñhãˆµøä>)r6   )Rrt  Únumpyr   Únumpy.typingr   r   Útypedr   r   r   Úutilr	   r
   r   rÅ  Úflagsr   r"   r)   r1   rD   rR   r^   rb   ri   rr   rt   r€   rƒ   r�   r™   rŸ   r¬   r×   rÙ   rœ   rû   rþ   r  r  r½   r  r#  r&  r(  r,  r.  r;  rE  rG  rI  re  rg  rf  Úfinfor*  Úepsrx   rß   rÛ   ÚitemsrÞ   rf   r,   rž  r   r¥  r§  rª  r¯  r±  rµ  rº  r¾  rÁ  rb  r9   rÌ  rÓ  r×  rÙ  rÛ  )ré   r   s   00r   Ú<module>rä     s5  ðñHaóF ß +ç (Ñ (ß 0ð ˆB�F‰F�1‹I€	Ø$€	‡��Ñ òòò8	,òò4ó:Nòb%#óPòD&%óR9óxE8óP9òxò>,+ò^Q8óh1òhó<hóV/VódEóP7ótBó6òrò ,ó^Vòrò*òò ò7ó&ñR!¨dó !ðJ "ØØ#Øñ	)Ø
�dÑ
ð)à	ð)ð ˜‰}ð)ð ó	)÷Xm-ñ m-ò`
4ò+ò 
ð €r‡x�x�ƒ×Ñ˜SÑ €ò €
ðØ
ˆLðà
ˆLðð ˆLðð ˆLð	ð
 ˆLðð ˆLðð ˆLðð ˆLðð ˆLðð ˆLðð ˆLðð ˆLðð ˆLðð ˆLðð ˆLðð  ˆLð!ð" ˆLð#ð$ ØØØØØØò1€ð6 !,× 1Ñ 1Ô 3Ô4Ñ 3™˜˜1ˆq�!‰tÐ 3Ò4€ó$óN)ñX/˜dó /ó"'ó*8òD!ò ò )ò 7òó:/òdó6ð. =Añ+2Øð+2Ø(ð+2Ø59ð+2àˆR�Z‰ZÑó+2ó\$óN#ó@ó,5ùóA 5s   ÅG