1f184f754862637ad9c3c32952a0756414e2b67a6jvr"""Affine 2D transformation matrix class. 2f184f754862637ad9c3c32952a0756414e2b67a6jvr 3f184f754862637ad9c3c32952a0756414e2b67a6jvrThe Transform class implements various transformation matrix operations, 4f184f754862637ad9c3c32952a0756414e2b67a6jvrboth on the matrix itself, as well as on 2D coordinates. 5f184f754862637ad9c3c32952a0756414e2b67a6jvr 6f184f754862637ad9c3c32952a0756414e2b67a6jvrTransform instances are effectively immutable: all methods that operate on the 7f184f754862637ad9c3c32952a0756414e2b67a6jvrtransformation itself always return a new instance. This has as the 8f184f754862637ad9c3c32952a0756414e2b67a6jvrinteresting side effect that Transform instances are hashable, ie. they can be 9f184f754862637ad9c3c32952a0756414e2b67a6jvrused as dictionary keys. 10f184f754862637ad9c3c32952a0756414e2b67a6jvr 11f184f754862637ad9c3c32952a0756414e2b67a6jvrThis module exports the following symbols: 12f184f754862637ad9c3c32952a0756414e2b67a6jvr 13f184f754862637ad9c3c32952a0756414e2b67a6jvr Transform -- this is the main class 14f184f754862637ad9c3c32952a0756414e2b67a6jvr Identity -- Transform instance set to the identity transformation 15f184f754862637ad9c3c32952a0756414e2b67a6jvr Offset -- Convenience function that returns a translating transformation 16f184f754862637ad9c3c32952a0756414e2b67a6jvr Scale -- Convenience function that returns a scaling transformation 17f184f754862637ad9c3c32952a0756414e2b67a6jvr 18f184f754862637ad9c3c32952a0756414e2b67a6jvrExamples: 19f184f754862637ad9c3c32952a0756414e2b67a6jvr 20f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t = Transform(2, 0, 0, 3, 0, 0) 21f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t.transformPoint((100, 100)) 22f184f754862637ad9c3c32952a0756414e2b67a6jvr (200, 300) 23f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t = Scale(2, 3) 24f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t.transformPoint((100, 100)) 25f184f754862637ad9c3c32952a0756414e2b67a6jvr (200, 300) 26f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t.transformPoint((0, 0)) 27f184f754862637ad9c3c32952a0756414e2b67a6jvr (0, 0) 28f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t = Offset(2, 3) 29f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t.transformPoint((100, 100)) 30f184f754862637ad9c3c32952a0756414e2b67a6jvr (102, 103) 31f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t.transformPoint((0, 0)) 32f184f754862637ad9c3c32952a0756414e2b67a6jvr (2, 3) 33f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t2 = t.scale(0.5) 34f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t2.transformPoint((100, 100)) 35f184f754862637ad9c3c32952a0756414e2b67a6jvr (52.0, 53.0) 36f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> import math 37f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t3 = t2.rotate(math.pi / 2) 38f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t3.transformPoint((0, 0)) 39f184f754862637ad9c3c32952a0756414e2b67a6jvr (2.0, 3.0) 40f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t3.transformPoint((100, 100)) 41f184f754862637ad9c3c32952a0756414e2b67a6jvr (-48.0, 53.0) 42f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t = Identity.scale(0.5).translate(100, 200).skew(0.1, 0.2) 43f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t.transformPoints([(0, 0), (1, 1), (100, 100)]) 44f184f754862637ad9c3c32952a0756414e2b67a6jvr [(50.0, 100.0), (50.550167336042726, 100.60135501775433), (105.01673360427253, 160.13550177543362)] 45f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> 46f184f754862637ad9c3c32952a0756414e2b67a6jvr""" 47f184f754862637ad9c3c32952a0756414e2b67a6jvr 481ae29591efbb29492ce05378909ccf4028d7c1eeBehdad Esfahbodfrom __future__ import print_function, division, absolute_import 4930e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.misc.py23 import * 50f184f754862637ad9c3c32952a0756414e2b67a6jvr 51f184f754862637ad9c3c32952a0756414e2b67a6jvr__all__ = ["Transform", "Identity", "Offset", "Scale"] 529e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr 539e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr 549e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr_EPSILON = 1e-15 559e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr_ONE_EPSILON = 1 - _EPSILON 569e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr_MINUS_ONE_EPSILON = -1 + _EPSILON 579e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr 589e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr 599e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvrdef _normSinCos(v): 609e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr if abs(v) < _EPSILON: 619e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr v = 0 629e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr elif v > _ONE_EPSILON: 639e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr v = 1 649e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr elif v < _MINUS_ONE_EPSILON: 659e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr v = -1 669e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr return v 679e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr 689e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr 69e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass Transform(object): 706385a4e9e16560212857b5eade6d0015f729a0b3jvr 719e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr """2x2 transformation matrix plus offset, a.k.a. Affine transform. 72f184f754862637ad9c3c32952a0756414e2b67a6jvr Transform instances are immutable: all transforming methods, eg. 73f184f754862637ad9c3c32952a0756414e2b67a6jvr rotate(), return a new Transform instance. 74f184f754862637ad9c3c32952a0756414e2b67a6jvr 75f184f754862637ad9c3c32952a0756414e2b67a6jvr Examples: 76f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t = Transform() 77f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t 78f184f754862637ad9c3c32952a0756414e2b67a6jvr <Transform [1 0 0 1 0 0]> 79f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t.scale(2) 80f184f754862637ad9c3c32952a0756414e2b67a6jvr <Transform [2 0 0 2 0 0]> 81f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t.scale(2.5, 5.5) 82f184f754862637ad9c3c32952a0756414e2b67a6jvr <Transform [2.5 0.0 0.0 5.5 0 0]> 83f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> 84f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t.scale(2, 3).transformPoint((100, 100)) 85f184f754862637ad9c3c32952a0756414e2b67a6jvr (200, 300) 86f184f754862637ad9c3c32952a0756414e2b67a6jvr """ 876385a4e9e16560212857b5eade6d0015f729a0b3jvr 889e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr def __init__(self, xx=1, xy=0, yx=0, yy=1, dx=0, dy=0): 89deca398915e92f24fe698e2e7d0e13122f61383ejvr """Transform's constructor takes six arguments, all of which are 90deca398915e92f24fe698e2e7d0e13122f61383ejvr optional, and can be used as keyword arguments: 91deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> Transform(12) 92deca398915e92f24fe698e2e7d0e13122f61383ejvr <Transform [12 0 0 1 0 0]> 93deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> Transform(dx=12) 94deca398915e92f24fe698e2e7d0e13122f61383ejvr <Transform [1 0 0 1 12 0]> 95deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> Transform(yx=12) 96deca398915e92f24fe698e2e7d0e13122f61383ejvr <Transform [1 0 12 1 0 0]> 97deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> 98deca398915e92f24fe698e2e7d0e13122f61383ejvr """ 999e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr self.__affine = xx, xy, yx, yy, dx, dy 1006385a4e9e16560212857b5eade6d0015f729a0b3jvr 1013a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def transformPoint(self, p): 102f184f754862637ad9c3c32952a0756414e2b67a6jvr """Transform a point. 103f184f754862637ad9c3c32952a0756414e2b67a6jvr 104f184f754862637ad9c3c32952a0756414e2b67a6jvr Example: 105f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t = Transform() 106f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t = t.scale(2.5, 5.5) 107f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t.transformPoint((100, 100)) 108f184f754862637ad9c3c32952a0756414e2b67a6jvr (250.0, 550.0) 109f184f754862637ad9c3c32952a0756414e2b67a6jvr """ 1103a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod (x, y) = p 1119e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr xx, xy, yx, yy, dx, dy = self.__affine 1129e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr return (xx*x + yx*y + dx, xy*x + yy*y + dy) 1136385a4e9e16560212857b5eade6d0015f729a0b3jvr 114f184f754862637ad9c3c32952a0756414e2b67a6jvr def transformPoints(self, points): 115f184f754862637ad9c3c32952a0756414e2b67a6jvr """Transform a list of points. 116f184f754862637ad9c3c32952a0756414e2b67a6jvr 117f184f754862637ad9c3c32952a0756414e2b67a6jvr Example: 118f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t = Scale(2, 3) 119f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t.transformPoints([(0, 0), (0, 100), (100, 100), (100, 0)]) 120f184f754862637ad9c3c32952a0756414e2b67a6jvr [(0, 0), (0, 300), (200, 300), (200, 0)] 121f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> 122f184f754862637ad9c3c32952a0756414e2b67a6jvr """ 123f184f754862637ad9c3c32952a0756414e2b67a6jvr xx, xy, yx, yy, dx, dy = self.__affine 124f184f754862637ad9c3c32952a0756414e2b67a6jvr return [(xx*x + yx*y + dx, xy*x + yy*y + dy) for x, y in points] 125f184f754862637ad9c3c32952a0756414e2b67a6jvr 1269e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr def translate(self, x=0, y=0): 127f184f754862637ad9c3c32952a0756414e2b67a6jvr """Return a new transformation, translated (offset) by x, y. 128f184f754862637ad9c3c32952a0756414e2b67a6jvr 129f184f754862637ad9c3c32952a0756414e2b67a6jvr Example: 130f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t = Transform() 131f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t.translate(20, 30) 132f184f754862637ad9c3c32952a0756414e2b67a6jvr <Transform [1 0 0 1 20 30]> 133f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> 134f184f754862637ad9c3c32952a0756414e2b67a6jvr """ 1359e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr return self.transform((1, 0, 0, 1, x, y)) 1366385a4e9e16560212857b5eade6d0015f729a0b3jvr 1379e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr def scale(self, x=1, y=None): 138f184f754862637ad9c3c32952a0756414e2b67a6jvr """Return a new transformation, scaled by x, y. The 'y' argument 139f184f754862637ad9c3c32952a0756414e2b67a6jvr may be None, which implies to use the x value for y as well. 140f184f754862637ad9c3c32952a0756414e2b67a6jvr 141f184f754862637ad9c3c32952a0756414e2b67a6jvr Example: 142f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t = Transform() 143f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t.scale(5) 144f184f754862637ad9c3c32952a0756414e2b67a6jvr <Transform [5 0 0 5 0 0]> 145f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t.scale(5, 6) 146f184f754862637ad9c3c32952a0756414e2b67a6jvr <Transform [5 0 0 6 0 0]> 147f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> 148f184f754862637ad9c3c32952a0756414e2b67a6jvr """ 1499e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr if y is None: 1509e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr y = x 1519e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr return self.transform((x, 0, 0, y, 0, 0)) 1526385a4e9e16560212857b5eade6d0015f729a0b3jvr 1539e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr def rotate(self, angle): 154f184f754862637ad9c3c32952a0756414e2b67a6jvr """Return a new transformation, rotated by 'angle' (radians). 155f184f754862637ad9c3c32952a0756414e2b67a6jvr 156f184f754862637ad9c3c32952a0756414e2b67a6jvr Example: 157f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> import math 158f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t = Transform() 159f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t.rotate(math.pi / 2) 160f184f754862637ad9c3c32952a0756414e2b67a6jvr <Transform [0 1 -1 0 0 0]> 161f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> 162f184f754862637ad9c3c32952a0756414e2b67a6jvr """ 1639e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr import math 1649e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr c = _normSinCos(math.cos(angle)) 1659e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr s = _normSinCos(math.sin(angle)) 1669e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr return self.transform((c, s, -s, c, 0, 0)) 1676385a4e9e16560212857b5eade6d0015f729a0b3jvr 1689e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr def skew(self, x=0, y=0): 169f184f754862637ad9c3c32952a0756414e2b67a6jvr """Return a new transformation, skewed by x and y. 170f184f754862637ad9c3c32952a0756414e2b67a6jvr 171f184f754862637ad9c3c32952a0756414e2b67a6jvr Example: 172f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> import math 173f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t = Transform() 174f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t.skew(math.pi / 4) 175f184f754862637ad9c3c32952a0756414e2b67a6jvr <Transform [1.0 0.0 1.0 1.0 0 0]> 176f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> 177f184f754862637ad9c3c32952a0756414e2b67a6jvr """ 1789e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr import math 1799e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr return self.transform((1, math.tan(y), math.tan(x), 1, 0, 0)) 1806385a4e9e16560212857b5eade6d0015f729a0b3jvr 1819e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr def transform(self, other): 182f184f754862637ad9c3c32952a0756414e2b67a6jvr """Return a new transformation, transformed by another 183f184f754862637ad9c3c32952a0756414e2b67a6jvr transformation. 184f184f754862637ad9c3c32952a0756414e2b67a6jvr 185f184f754862637ad9c3c32952a0756414e2b67a6jvr Example: 186f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t = Transform(2, 0, 0, 3, 1, 6) 187f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t.transform((4, 3, 2, 1, 5, 6)) 188f184f754862637ad9c3c32952a0756414e2b67a6jvr <Transform [8 9 4 3 11 24]> 189f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> 190f184f754862637ad9c3c32952a0756414e2b67a6jvr """ 1919e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr xx1, xy1, yx1, yy1, dx1, dy1 = other 1929e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr xx2, xy2, yx2, yy2, dx2, dy2 = self.__affine 1939e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr return self.__class__( 1949e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr xx1*xx2 + xy1*yx2, 1959e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr xx1*xy2 + xy1*yy2, 1969e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr yx1*xx2 + yy1*yx2, 1979e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr yx1*xy2 + yy1*yy2, 1989e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr xx2*dx1 + yx2*dy1 + dx2, 1999e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr xy2*dx1 + yy2*dy1 + dy2) 2006385a4e9e16560212857b5eade6d0015f729a0b3jvr 2019e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr def reverseTransform(self, other): 202f184f754862637ad9c3c32952a0756414e2b67a6jvr """Return a new transformation, which is the other transformation 203f184f754862637ad9c3c32952a0756414e2b67a6jvr transformed by self. self.reverseTransform(other) is equivalent to 204f184f754862637ad9c3c32952a0756414e2b67a6jvr other.transform(self). 205f184f754862637ad9c3c32952a0756414e2b67a6jvr 206f184f754862637ad9c3c32952a0756414e2b67a6jvr Example: 207f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t = Transform(2, 0, 0, 3, 1, 6) 208f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> t.reverseTransform((4, 3, 2, 1, 5, 6)) 209f184f754862637ad9c3c32952a0756414e2b67a6jvr <Transform [8 6 6 3 21 15]> 210f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> Transform(4, 3, 2, 1, 5, 6).transform((2, 0, 0, 3, 1, 6)) 211f184f754862637ad9c3c32952a0756414e2b67a6jvr <Transform [8 6 6 3 21 15]> 212f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> 213f184f754862637ad9c3c32952a0756414e2b67a6jvr """ 2149e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr xx1, xy1, yx1, yy1, dx1, dy1 = self.__affine 2159e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr xx2, xy2, yx2, yy2, dx2, dy2 = other 2169e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr return self.__class__( 2179e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr xx1*xx2 + xy1*yx2, 2189e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr xx1*xy2 + xy1*yy2, 2199e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr yx1*xx2 + yy1*yx2, 2209e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr yx1*xy2 + yy1*yy2, 2219e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr xx2*dx1 + yx2*dy1 + dx2, 2229e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr xy2*dx1 + yy2*dy1 + dy2) 2236385a4e9e16560212857b5eade6d0015f729a0b3jvr 2249e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr def inverse(self): 225f184f754862637ad9c3c32952a0756414e2b67a6jvr """Return the inverse transformation. 226f184f754862637ad9c3c32952a0756414e2b67a6jvr 227f184f754862637ad9c3c32952a0756414e2b67a6jvr Example: 228deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> t = Identity.translate(2, 3).scale(4, 5) 229deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> t.transformPoint((10, 20)) 230deca398915e92f24fe698e2e7d0e13122f61383ejvr (42, 103) 231deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> it = t.inverse() 232deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> it.transformPoint((42, 103)) 233deca398915e92f24fe698e2e7d0e13122f61383ejvr (10.0, 20.0) 234f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> 235f184f754862637ad9c3c32952a0756414e2b67a6jvr """ 2369e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr if self.__affine == (1, 0, 0, 1, 0, 0): 2379e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr return self 2389e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr xx, xy, yx, yy, dx, dy = self.__affine 23932c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbod det = xx*yy - yx*xy 2409e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr xx, xy, yx, yy = yy/det, -xy/det, -yx/det, xx/det 2419e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr dx, dy = -xx*dx - yx*dy, -xy*dx - yy*dy 2429e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr return self.__class__(xx, xy, yx, yy, dx, dy) 2436385a4e9e16560212857b5eade6d0015f729a0b3jvr 2449e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr def toPS(self): 245deca398915e92f24fe698e2e7d0e13122f61383ejvr """Return a PostScript representation: 246deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> t = Identity.scale(2, 3).translate(4, 5) 247deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> t.toPS() 248deca398915e92f24fe698e2e7d0e13122f61383ejvr '[2 0 0 3 8 15]' 249deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> 250deca398915e92f24fe698e2e7d0e13122f61383ejvr """ 2519e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr return "[%s %s %s %s %s %s]" % self.__affine 2526385a4e9e16560212857b5eade6d0015f729a0b3jvr 2539e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr def __len__(self): 254deca398915e92f24fe698e2e7d0e13122f61383ejvr """Transform instances also behave like sequences of length 6: 255deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> len(Identity) 256deca398915e92f24fe698e2e7d0e13122f61383ejvr 6 257deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> 258deca398915e92f24fe698e2e7d0e13122f61383ejvr """ 2599e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr return 6 2606385a4e9e16560212857b5eade6d0015f729a0b3jvr 2619e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr def __getitem__(self, index): 262deca398915e92f24fe698e2e7d0e13122f61383ejvr """Transform instances also behave like sequences of length 6: 263deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> list(Identity) 264deca398915e92f24fe698e2e7d0e13122f61383ejvr [1, 0, 0, 1, 0, 0] 265deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> tuple(Identity) 266deca398915e92f24fe698e2e7d0e13122f61383ejvr (1, 0, 0, 1, 0, 0) 267deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> 268deca398915e92f24fe698e2e7d0e13122f61383ejvr """ 2699e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr return self.__affine[index] 2706385a4e9e16560212857b5eade6d0015f729a0b3jvr 2718ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod def __ne__(self, other): 2728ea6439d3b66c5acc246261d761d4375bcb7cfabBehdad Esfahbod return not self.__eq__(other) 273b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod def __eq__(self, other): 274b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod """Transform instances are comparable: 275b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod >>> t1 = Identity.scale(2, 3).translate(4, 6) 276b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod >>> t2 = Identity.translate(8, 18).scale(2, 3) 277b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod >>> t1 == t2 278b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod 1 279b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod >>> 280b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod 281b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod But beware of floating point rounding errors: 282b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod >>> t1 = Identity.scale(0.2, 0.3).translate(0.4, 0.6) 283b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod >>> t2 = Identity.translate(0.08, 0.18).scale(0.2, 0.3) 284b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod >>> t1 285b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod <Transform [0.2 0.0 0.0 0.3 0.08 0.18]> 286b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod >>> t2 287b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod <Transform [0.2 0.0 0.0 0.3 0.08 0.18]> 288b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod >>> t1 == t2 289b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod 0 290b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod >>> 291b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod """ 292b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod xx1, xy1, yx1, yy1, dx1, dy1 = self.__affine 293b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod xx2, xy2, yx2, yy2, dx2, dy2 = other 294af1c9968b2edc827ee9115355a72b3c56c4fbe61Denis Jacquerye return (xx1, xy1, yx1, yy1, dx1, dy1) == \ 295af1c9968b2edc827ee9115355a72b3c56c4fbe61Denis Jacquerye (xx2, xy2, yx2, yy2, dx2, dy2) 2966385a4e9e16560212857b5eade6d0015f729a0b3jvr 2979e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr def __hash__(self): 298deca398915e92f24fe698e2e7d0e13122f61383ejvr """Transform instances are hashable, meaning you can use them as 299deca398915e92f24fe698e2e7d0e13122f61383ejvr keys in dictionaries: 300deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> d = {Scale(12, 13): None} 301deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> d 302deca398915e92f24fe698e2e7d0e13122f61383ejvr {<Transform [12 0 0 13 0 0]>: None} 303deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> 304deca398915e92f24fe698e2e7d0e13122f61383ejvr 305deca398915e92f24fe698e2e7d0e13122f61383ejvr But again, beware of floating point rounding errors: 306deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> t1 = Identity.scale(0.2, 0.3).translate(0.4, 0.6) 307deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> t2 = Identity.translate(0.08, 0.18).scale(0.2, 0.3) 308deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> t1 309deca398915e92f24fe698e2e7d0e13122f61383ejvr <Transform [0.2 0.0 0.0 0.3 0.08 0.18]> 310deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> t2 311deca398915e92f24fe698e2e7d0e13122f61383ejvr <Transform [0.2 0.0 0.0 0.3 0.08 0.18]> 312deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> d = {t1: None} 313deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> d 314deca398915e92f24fe698e2e7d0e13122f61383ejvr {<Transform [0.2 0.0 0.0 0.3 0.08 0.18]>: None} 315deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> d[t2] 316deca398915e92f24fe698e2e7d0e13122f61383ejvr Traceback (most recent call last): 317deca398915e92f24fe698e2e7d0e13122f61383ejvr File "<stdin>", line 1, in ? 318deca398915e92f24fe698e2e7d0e13122f61383ejvr KeyError: <Transform [0.2 0.0 0.0 0.3 0.08 0.18]> 319deca398915e92f24fe698e2e7d0e13122f61383ejvr >>> 320deca398915e92f24fe698e2e7d0e13122f61383ejvr """ 3219e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr return hash(self.__affine) 3226385a4e9e16560212857b5eade6d0015f729a0b3jvr 3239e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr def __repr__(self): 324af1c9968b2edc827ee9115355a72b3c56c4fbe61Denis Jacquerye return "<%s [%s %s %s %s %s %s]>" % ((self.__class__.__name__,) \ 3259e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr + tuple(map(str, self.__affine))) 3269e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr 3279e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr 3289e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvrIdentity = Transform() 3299e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr 3309e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvrdef Offset(x=0, y=0): 331f184f754862637ad9c3c32952a0756414e2b67a6jvr """Return the identity transformation offset by x, y. 332f184f754862637ad9c3c32952a0756414e2b67a6jvr 333f184f754862637ad9c3c32952a0756414e2b67a6jvr Example: 334f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> Offset(2, 3) 335f184f754862637ad9c3c32952a0756414e2b67a6jvr <Transform [1 0 0 1 2 3]> 336f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> 337f184f754862637ad9c3c32952a0756414e2b67a6jvr """ 3389e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr return Transform(1, 0, 0, 1, x, y) 3399e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr 3409e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvrdef Scale(x, y=None): 341f184f754862637ad9c3c32952a0756414e2b67a6jvr """Return the identity transformation scaled by x, y. The 'y' argument 342f184f754862637ad9c3c32952a0756414e2b67a6jvr may be None, which implies to use the x value for y as well. 343f184f754862637ad9c3c32952a0756414e2b67a6jvr 344f184f754862637ad9c3c32952a0756414e2b67a6jvr Example: 345f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> Scale(2, 3) 346f184f754862637ad9c3c32952a0756414e2b67a6jvr <Transform [2 0 0 3 0 0]> 347f184f754862637ad9c3c32952a0756414e2b67a6jvr >>> 348f184f754862637ad9c3c32952a0756414e2b67a6jvr """ 3499e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr if y is None: 3509e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr y = x 3519e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr return Transform(x, 0, 0, y, 0, 0) 352f184f754862637ad9c3c32952a0756414e2b67a6jvr 353f184f754862637ad9c3c32952a0756414e2b67a6jvr 354f184f754862637ad9c3c32952a0756414e2b67a6jvrif __name__ == "__main__": 355153ec402094adbea673e914385b87f1d99191d0bBehdad Esfahbod import doctest 356153ec402094adbea673e914385b87f1d99191d0bBehdad Esfahbod doctest.testmod() 357