U
    Dvf8                     @   s   d Z ddlZddlmZ ddlmZ ddddd	gZd
Zde Zde Z	dd Z
G dd deZe ZdddZdddZeG dd	 d	ZedkrddlZddlZee j dS )a7  Affine 2D transformation matrix class.

The Transform class implements various transformation matrix operations,
both on the matrix itself, as well as on 2D coordinates.

Transform instances are effectively immutable: all methods that operate on the
transformation itself always return a new instance. This has as the
interesting side effect that Transform instances are hashable, ie. they can be
used as dictionary keys.

This module exports the following symbols:

Transform
	this is the main class
Identity
	Transform instance set to the identity transformation
Offset
	Convenience function that returns a translating transformation
Scale
	Convenience function that returns a scaling transformation

The DecomposedTransform class implements a transformation with separate
translate, rotation, scale, skew, and transformation-center components.

:Example:

	>>> t = Transform(2, 0, 0, 3, 0, 0)
	>>> t.transformPoint((100, 100))
	(200, 300)
	>>> t = Scale(2, 3)
	>>> t.transformPoint((100, 100))
	(200, 300)
	>>> t.transformPoint((0, 0))
	(0, 0)
	>>> t = Offset(2, 3)
	>>> t.transformPoint((100, 100))
	(102, 103)
	>>> t.transformPoint((0, 0))
	(2, 3)
	>>> t2 = t.scale(0.5)
	>>> t2.transformPoint((100, 100))
	(52.0, 53.0)
	>>> import math
	>>> t3 = t2.rotate(math.pi / 2)
	>>> t3.transformPoint((0, 0))
	(2.0, 3.0)
	>>> t3.transformPoint((100, 100))
	(-48.0, 53.0)
	>>> t = Identity.scale(0.5).translate(100, 200).skew(0.1, 0.2)
	>>> t.transformPoints([(0, 0), (1, 1), (100, 100)])
	[(50.0, 100.0), (50.550167336042726, 100.60135501775433), (105.01673360427253, 160.13550177543362)]
	>>>
    N)
NamedTuple)	dataclass	TransformIdentityOffsetScaleDecomposedTransformgV瞯<   c                 C   s0   t | tk rd} n| tkr d} n| tk r,d} | S )Nr   r	   r
   )abs_EPSILON_ONE_EPSILON_MINUS_ONE_EPSILON)v r   </tmp/pip-unpacked-wheel-qlge9rch/fontTools/misc/transform.py_normSinCosD   s    r   c                   @   s   e Zd ZU dZdZeed< dZeed< dZeed< dZ	eed< dZ
eed< dZeed	< d
d Zdd Zdd Zdd Zd+ddZd,ddZdd Zd-ddZdd Zdd Zdd  Zd!d" Zd#d$d%d&Zd'd( Zd)d* ZdS ).r   a	  2x2 transformation matrix plus offset, a.k.a. Affine transform.
    Transform instances are immutable: all transforming methods, eg.
    rotate(), return a new Transform instance.

    :Example:

            >>> t = Transform()
            >>> t
            <Transform [1 0 0 1 0 0]>
            >>> t.scale(2)
            <Transform [2 0 0 2 0 0]>
            >>> t.scale(2.5, 5.5)
            <Transform [2.5 0 0 5.5 0 0]>
            >>>
            >>> t.scale(2, 3).transformPoint((100, 100))
            (200, 300)

    Transform's constructor takes six arguments, all of which are
    optional, and can be used as keyword arguments::

            >>> Transform(12)
            <Transform [12 0 0 1 0 0]>
            >>> Transform(dx=12)
            <Transform [1 0 0 1 12 0]>
            >>> Transform(yx=12)
            <Transform [1 0 12 1 0 0]>

    Transform instances also behave like sequences of length 6::

            >>> len(Identity)
            6
            >>> list(Identity)
            [1, 0, 0, 1, 0, 0]
            >>> tuple(Identity)
            (1, 0, 0, 1, 0, 0)

    Transform instances are comparable::

            >>> t1 = Identity.scale(2, 3).translate(4, 6)
            >>> t2 = Identity.translate(8, 18).scale(2, 3)
            >>> t1 == t2
            1

    But beware of floating point rounding errors::

            >>> t1 = Identity.scale(0.2, 0.3).translate(0.4, 0.6)
            >>> t2 = Identity.translate(0.08, 0.18).scale(0.2, 0.3)
            >>> t1
            <Transform [0.2 0 0 0.3 0.08 0.18]>
            >>> t2
            <Transform [0.2 0 0 0.3 0.08 0.18]>
            >>> t1 == t2
            0

    Transform instances are hashable, meaning you can use them as
    keys in dictionaries::

            >>> d = {Scale(12, 13): None}
            >>> d
            {<Transform [12 0 0 13 0 0]>: None}

    But again, beware of floating point rounding errors::

            >>> t1 = Identity.scale(0.2, 0.3).translate(0.4, 0.6)
            >>> t2 = Identity.translate(0.08, 0.18).scale(0.2, 0.3)
            >>> t1
            <Transform [0.2 0 0 0.3 0.08 0.18]>
            >>> t2
            <Transform [0.2 0 0 0.3 0.08 0.18]>
            >>> d = {t1: None}
            >>> d
            {<Transform [0.2 0 0 0.3 0.08 0.18]>: None}
            >>> d[t2]
            Traceback (most recent call last):
              File "<stdin>", line 1, in ?
            KeyError: <Transform [0.2 0 0 0.3 0.08 0.18]>
    r	   xxr   xyyxyydxdyc           
      C   s@   |\}}| \}}}}}}	|| ||  | || ||  |	 fS )zTransform a point.

        :Example:

                >>> t = Transform()
                >>> t = t.scale(2.5, 5.5)
                >>> t.transformPoint((100, 100))
                (250.0, 550.0)
        r   )
selfpxyr   r   r   r   r   r   r   r   r   transformPoint   s    
zTransform.transformPointc                    s,   | \  fdd|D S )zTransform a list of points.

        :Example:

                >>> t = Scale(2, 3)
                >>> t.transformPoints([(0, 0), (0, 100), (100, 100), (100, 0)])
                [(0, 0), (0, 300), (200, 300), (200, 0)]
                >>>
        c                    s8   g | ]0\}}| |    | |   fqS r   r   ).0r   r   r   r   r   r   r   r   r   r   
<listcomp>   s     z-Transform.transformPoints.<locals>.<listcomp>r   )r   Zpointsr   r   r   transformPoints   s    
zTransform.transformPointsc                 C   s<   |\}}| dd \}}}}|| ||  || ||  fS )zTransform an (dx, dy) vector, treating translation as zero.

        :Example:

                >>> t = Transform(2, 0, 0, 2, 10, 20)
                >>> t.transformVector((3, -4))
                (6, -8)
                >>>
        N   r   )r   r   r   r   r   r   r   r   r   r   r   transformVector   s    
zTransform.transformVectorc                    s,   | dd \  fdd|D S )a  Transform a list of (dx, dy) vector, treating translation as zero.

        :Example:
                >>> t = Transform(2, 0, 0, 2, 10, 20)
                >>> t.transformVectors([(3, -4), (5, -6)])
                [(6, -8), (10, -12)]
                >>>
        Nr"   c                    s0   g | ](\}} | |  | |  fqS r   r   )r   r   r   r   r   r   r   r   r   r       s     z.Transform.transformVectors.<locals>.<listcomp>r   )r   Zvectorsr   r$   r   transformVectors   s    	zTransform.transformVectorsc                 C   s   |  dddd||fS )zReturn a new transformation, translated (offset) by x, y.

        :Example:
                >>> t = Transform()
                >>> t.translate(20, 30)
                <Transform [1 0 0 1 20 30]>
                >>>
        r	   r   	transformr   r   r   r   r   r   	translate   s    	zTransform.translateNc                 C   s"   |dkr|}|  |dd|ddfS )ak  Return a new transformation, scaled by x, y. The 'y' argument
        may be None, which implies to use the x value for y as well.

        :Example:
                >>> t = Transform()
                >>> t.scale(5)
                <Transform [5 0 0 5 0 0]>
                >>> t.scale(5, 6)
                <Transform [5 0 0 6 0 0]>
                >>>
        Nr   r&   r(   r   r   r   scale   s    zTransform.scalec                 C   s<   ddl }t||}t||}| ||| |ddfS )a  Return a new transformation, rotated by 'angle' (radians).

        :Example:
                >>> import math
                >>> t = Transform()
                >>> t.rotate(math.pi / 2)
                <Transform [0 1 -1 0 0 0]>
                >>>
        r   N)mathr   cossinr'   )r   Zangler+   csr   r   r   rotate   s    
zTransform.rotatec                 C   s*   ddl }| d||||dddfS )zReturn a new transformation, skewed by x and y.

        :Example:
                >>> import math
                >>> t = Transform()
                >>> t.skew(math.pi / 4)
                <Transform [1 0 1 1 0 0]>
                >>>
        r   Nr	   )r+   r'   tan)r   r   r   r+   r   r   r   skew  s    
zTransform.skewc              
   C   s   |\}}}}}}| \}}	}
}}}|  || ||
  ||	 ||  || ||
  ||	 ||  || |
|  | |	| ||  | S )a  Return a new transformation, transformed by another
        transformation.

        :Example:
                >>> t = Transform(2, 0, 0, 3, 1, 6)
                >>> t.transform((4, 3, 2, 1, 5, 6))
                <Transform [8 9 4 3 11 24]>
                >>>
        	__class__r   otherZxx1Zxy1Zyx1Zyy1Zdx1Zdy1Zxx2Zxy2Zyx2Zyy2Zdx2Zdy2r   r   r   r'     s    
zTransform.transformc              
   C   s   | \}}}}}}|\}}	}
}}}|  || ||
  ||	 ||  || ||
  ||	 ||  || |
|  | |	| ||  | S )a  Return a new transformation, which is the other transformation
        transformed by self. self.reverseTransform(other) is equivalent to
        other.transform(self).

        :Example:
                >>> t = Transform(2, 0, 0, 3, 1, 6)
                >>> t.reverseTransform((4, 3, 2, 1, 5, 6))
                <Transform [8 6 6 3 21 15]>
                >>> Transform(4, 3, 2, 1, 5, 6).transform((2, 0, 0, 3, 1, 6))
                <Transform [8 6 6 3 21 15]>
                >>>
        r3   r5   r   r   r   reverseTransform'  s    zTransform.reverseTransformc                 C   s   | t kr| S | \}}}}}}|| ||  }|| | | | | || f\}}}}| | ||  | | ||   }}| ||||||S )aK  Return the inverse transformation.

        :Example:
                >>> t = Identity.translate(2, 3).scale(4, 5)
                >>> t.transformPoint((10, 20))
                (42, 103)
                >>> it = t.inverse()
                >>> it.transformPoint((42, 103))
                (10.0, 20.0)
                >>>
        )r   r4   )r   r   r   r   r   r   r   Zdetr   r   r   inverse?  s    (&zTransform.inversec                 C   s   d|  S )zReturn a PostScript representation

        :Example:

                >>> t = Identity.scale(2, 3).translate(4, 5)
                >>> t.toPS()
                '[2 0 0 3 8 15]'
                >>>
        z[%s %s %s %s %s %s]r   r   r   r   r   toPSS  s    
zTransform.toPSr   )returnc                 C   s
   t | S )z%Decompose into a DecomposedTransform.)r   fromTransformr9   r   r   r   toDecomposed_  s    zTransform.toDecomposedc                 C   s   | t kS )a  Returns True if transform is not identity, False otherwise.

        :Example:

                >>> bool(Identity)
                False
                >>> bool(Transform())
                False
                >>> bool(Scale(1.))
                False
                >>> bool(Scale(2))
                True
                >>> bool(Offset())
                False
                >>> bool(Offset(0))
                False
                >>> bool(Offset(2))
                True
        )r   r9   r   r   r   __bool__c  s    zTransform.__bool__c                 C   s   d| j jf|   S )Nz<%s [%g %g %g %g %g %g]>)r4   __name__r9   r   r   r   __repr__y  s    zTransform.__repr__)r   r   )r	   N)r   r   )r?   
__module____qualname____doc__r   float__annotations__r   r   r   r   r   r   r!   r#   r%   r)   r*   r0   r2   r'   r7   r8   r:   r=   r>   r@   r   r   r   r   r   N   s,   
N


c                 C   s   t dddd| |S )zReturn the identity transformation offset by x, y.

    :Example:
            >>> Offset(2, 3)
            <Transform [1 0 0 1 2 3]>
            >>>
    r	   r   r   r   r   r   r   r   r     s    c                 C   s   |dkr| }t | dd|ddS )zReturn the identity transformation scaled by x, y. The 'y' argument
    may be None, which implies to use the x value for y as well.

    :Example:
            >>> Scale(2, 3)
            <Transform [2 0 0 3 0 0]>
            >>>
    Nr   rF   rG   r   r   r   r     s    	c                   @   s   e Zd ZU dZdZeed< dZeed< dZeed< dZ	eed< dZ
eed< dZeed	< dZeed
< dZeed< dZeed< edd Zdd ZdS )r   zThe DecomposedTransform class implements a transformation with separate
    translate, rotation, scale, skew, and transformation-center components.
    r   
translateX
translateYrotationr	   scaleXscaleYskewXskewYtCenterXtCenterYc              
   C   s  |\}}}}}}t d|}|dk r4||9 }||9 }|| ||  }	d}
d }}d }}|dksh|dkrt || ||  }|dkrt || nt ||  }
||	|  }}t || ||  ||  d }}n|dks|dkrht || ||  }t jd |dkr"t | | nt ||   }
|	| | }}dt || ||  ||   }}n t||t |
|| |t || t |dd	S )Nr	   r      )r+   copysignsqrtacosatanpir   degrees)r   r'   abr.   dr   r   ZsxdeltarJ   rK   rL   rM   rN   rr/   r   r   r   r<     s@    &&(&z!DecomposedTransform.fromTransformc                 C   sx   t  }|| j| j | j| j }|t| j	}|
| j| j}|t| jt| j}|| j | j }|S )zReturn the Transform() equivalent of this transformation.

        :Example:
                >>> DecomposedTransform(scaleX=2, scaleY=2).toTransform()
                <Transform [2 0 0 2 0 0]>
                >>>
        )r   r)   rH   rO   rI   rP   r0   r+   radiansrJ   r*   rK   rL   r2   rM   rN   )r   tr   r   r   toTransform  s    
 
zDecomposedTransform.toTransformN)r?   rA   rB   rC   rH   rD   rE   rI   rJ   rK   rL   rM   rN   rO   rP   classmethodr<   r_   r   r   r   r   r     s   

-__main__)r   r   )N)rC   r+   typingr   Zdataclassesr   __all__r   r   r   r   r   r   r   r   r   r?   sysdoctestexittestmodfailedr   r   r   r   <module>   s(   6
  1

P