transform.py revision 3a9fd301808f5a8991ca9ac44028d1ecb22d307f
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
48f184f754862637ad9c3c32952a0756414e2b67a6jvr
49f184f754862637ad9c3c32952a0756414e2b67a6jvr__all__ = ["Transform", "Identity", "Offset", "Scale"]
509e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr
519e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr
529e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr_EPSILON = 1e-15
539e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr_ONE_EPSILON = 1 - _EPSILON
549e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr_MINUS_ONE_EPSILON = -1 + _EPSILON
559e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr
569e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr
579e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvrdef _normSinCos(v):
589e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	if abs(v) < _EPSILON:
599e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		v = 0
609e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	elif v > _ONE_EPSILON:
619e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		v = 1
629e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	elif v < _MINUS_ONE_EPSILON:
639e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		v = -1
649e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	return v
659e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr
669e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr
679e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvrclass Transform:
686385a4e9e16560212857b5eade6d0015f729a0b3jvr
699e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	"""2x2 transformation matrix plus offset, a.k.a. Affine transform.
70f184f754862637ad9c3c32952a0756414e2b67a6jvr	Transform instances are immutable: all transforming methods, eg.
71f184f754862637ad9c3c32952a0756414e2b67a6jvr	rotate(), return a new Transform instance.
72f184f754862637ad9c3c32952a0756414e2b67a6jvr
73f184f754862637ad9c3c32952a0756414e2b67a6jvr	Examples:
74f184f754862637ad9c3c32952a0756414e2b67a6jvr		>>> t = Transform()
75f184f754862637ad9c3c32952a0756414e2b67a6jvr		>>> t
76f184f754862637ad9c3c32952a0756414e2b67a6jvr		<Transform [1 0 0 1 0 0]>
77f184f754862637ad9c3c32952a0756414e2b67a6jvr		>>> t.scale(2)
78f184f754862637ad9c3c32952a0756414e2b67a6jvr		<Transform [2 0 0 2 0 0]>
79f184f754862637ad9c3c32952a0756414e2b67a6jvr		>>> t.scale(2.5, 5.5)
80f184f754862637ad9c3c32952a0756414e2b67a6jvr		<Transform [2.5 0.0 0.0 5.5 0 0]>
81f184f754862637ad9c3c32952a0756414e2b67a6jvr		>>>
82f184f754862637ad9c3c32952a0756414e2b67a6jvr		>>> t.scale(2, 3).transformPoint((100, 100))
83f184f754862637ad9c3c32952a0756414e2b67a6jvr		(200, 300)
84f184f754862637ad9c3c32952a0756414e2b67a6jvr	"""
856385a4e9e16560212857b5eade6d0015f729a0b3jvr
869e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	def __init__(self, xx=1, xy=0, yx=0, yy=1, dx=0, dy=0):
87deca398915e92f24fe698e2e7d0e13122f61383ejvr		"""Transform's constructor takes six arguments, all of which are
88deca398915e92f24fe698e2e7d0e13122f61383ejvr		optional, and can be used as keyword arguments:
89deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> Transform(12)
90deca398915e92f24fe698e2e7d0e13122f61383ejvr			<Transform [12 0 0 1 0 0]>
91deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> Transform(dx=12)
92deca398915e92f24fe698e2e7d0e13122f61383ejvr			<Transform [1 0 0 1 12 0]>
93deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> Transform(yx=12)
94deca398915e92f24fe698e2e7d0e13122f61383ejvr			<Transform [1 0 12 1 0 0]>
95deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>>
96deca398915e92f24fe698e2e7d0e13122f61383ejvr		"""
979e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		self.__affine = xx, xy, yx, yy, dx, dy
986385a4e9e16560212857b5eade6d0015f729a0b3jvr
993a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def transformPoint(self, p):
100f184f754862637ad9c3c32952a0756414e2b67a6jvr		"""Transform a point.
101f184f754862637ad9c3c32952a0756414e2b67a6jvr
102f184f754862637ad9c3c32952a0756414e2b67a6jvr		Example:
103f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>> t = Transform()
104f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>> t = t.scale(2.5, 5.5)
105f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>> t.transformPoint((100, 100))
106f184f754862637ad9c3c32952a0756414e2b67a6jvr			(250.0, 550.0)
107f184f754862637ad9c3c32952a0756414e2b67a6jvr		"""
1083a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod		(x, y) = p
1099e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		xx, xy, yx, yy, dx, dy = self.__affine
1109e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		return (xx*x + yx*y + dx, xy*x + yy*y + dy)
1116385a4e9e16560212857b5eade6d0015f729a0b3jvr
112f184f754862637ad9c3c32952a0756414e2b67a6jvr	def transformPoints(self, points):
113f184f754862637ad9c3c32952a0756414e2b67a6jvr		"""Transform a list of points.
114f184f754862637ad9c3c32952a0756414e2b67a6jvr
115f184f754862637ad9c3c32952a0756414e2b67a6jvr		Example:
116f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>> t = Scale(2, 3)
117f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>> t.transformPoints([(0, 0), (0, 100), (100, 100), (100, 0)])
118f184f754862637ad9c3c32952a0756414e2b67a6jvr			[(0, 0), (0, 300), (200, 300), (200, 0)]
119f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>>
120f184f754862637ad9c3c32952a0756414e2b67a6jvr		"""
121f184f754862637ad9c3c32952a0756414e2b67a6jvr		xx, xy, yx, yy, dx, dy = self.__affine
122f184f754862637ad9c3c32952a0756414e2b67a6jvr		return [(xx*x + yx*y + dx, xy*x + yy*y + dy) for x, y in points]
123f184f754862637ad9c3c32952a0756414e2b67a6jvr
1249e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	def translate(self, x=0, y=0):
125f184f754862637ad9c3c32952a0756414e2b67a6jvr		"""Return a new transformation, translated (offset) by x, y.
126f184f754862637ad9c3c32952a0756414e2b67a6jvr
127f184f754862637ad9c3c32952a0756414e2b67a6jvr		Example:
128f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>> t = Transform()
129f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>> t.translate(20, 30)
130f184f754862637ad9c3c32952a0756414e2b67a6jvr			<Transform [1 0 0 1 20 30]>
131f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>>
132f184f754862637ad9c3c32952a0756414e2b67a6jvr		"""
1339e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		return self.transform((1, 0, 0, 1, x, y))
1346385a4e9e16560212857b5eade6d0015f729a0b3jvr
1359e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	def scale(self, x=1, y=None):
136f184f754862637ad9c3c32952a0756414e2b67a6jvr		"""Return a new transformation, scaled by x, y. The 'y' argument
137f184f754862637ad9c3c32952a0756414e2b67a6jvr		may be None, which implies to use the x value for y as well.
138f184f754862637ad9c3c32952a0756414e2b67a6jvr
139f184f754862637ad9c3c32952a0756414e2b67a6jvr		Example:
140f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>> t = Transform()
141f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>> t.scale(5)
142f184f754862637ad9c3c32952a0756414e2b67a6jvr			<Transform [5 0 0 5 0 0]>
143f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>> t.scale(5, 6)
144f184f754862637ad9c3c32952a0756414e2b67a6jvr			<Transform [5 0 0 6 0 0]>
145f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>>
146f184f754862637ad9c3c32952a0756414e2b67a6jvr		"""
1479e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		if y is None:
1489e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr			y = x
1499e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		return self.transform((x, 0, 0, y, 0, 0))
1506385a4e9e16560212857b5eade6d0015f729a0b3jvr
1519e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	def rotate(self, angle):
152f184f754862637ad9c3c32952a0756414e2b67a6jvr		"""Return a new transformation, rotated by 'angle' (radians).
153f184f754862637ad9c3c32952a0756414e2b67a6jvr
154f184f754862637ad9c3c32952a0756414e2b67a6jvr		Example:
155f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>> import math
156f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>> t = Transform()
157f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>> t.rotate(math.pi / 2)
158f184f754862637ad9c3c32952a0756414e2b67a6jvr			<Transform [0 1 -1 0 0 0]>
159f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>>
160f184f754862637ad9c3c32952a0756414e2b67a6jvr		"""
1619e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		import math
1629e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		c = _normSinCos(math.cos(angle))
1639e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		s = _normSinCos(math.sin(angle))
1649e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		return self.transform((c, s, -s, c, 0, 0))
1656385a4e9e16560212857b5eade6d0015f729a0b3jvr
1669e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	def skew(self, x=0, y=0):
167f184f754862637ad9c3c32952a0756414e2b67a6jvr		"""Return a new transformation, skewed by x and y.
168f184f754862637ad9c3c32952a0756414e2b67a6jvr
169f184f754862637ad9c3c32952a0756414e2b67a6jvr		Example:
170f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>> import math
171f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>> t = Transform()
172f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>> t.skew(math.pi / 4)
173f184f754862637ad9c3c32952a0756414e2b67a6jvr			<Transform [1.0 0.0 1.0 1.0 0 0]>
174f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>>
175f184f754862637ad9c3c32952a0756414e2b67a6jvr		"""
1769e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		import math
1779e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		return self.transform((1, math.tan(y), math.tan(x), 1, 0, 0))
1786385a4e9e16560212857b5eade6d0015f729a0b3jvr
1799e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	def transform(self, other):
180f184f754862637ad9c3c32952a0756414e2b67a6jvr		"""Return a new transformation, transformed by another
181f184f754862637ad9c3c32952a0756414e2b67a6jvr		transformation.
182f184f754862637ad9c3c32952a0756414e2b67a6jvr
183f184f754862637ad9c3c32952a0756414e2b67a6jvr		Example:
184f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>> t = Transform(2, 0, 0, 3, 1, 6)
185f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>> t.transform((4, 3, 2, 1, 5, 6))
186f184f754862637ad9c3c32952a0756414e2b67a6jvr			<Transform [8 9 4 3 11 24]>
187f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>>
188f184f754862637ad9c3c32952a0756414e2b67a6jvr		"""
1899e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		xx1, xy1, yx1, yy1, dx1, dy1 = other
1909e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		xx2, xy2, yx2, yy2, dx2, dy2 = self.__affine
1919e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		return self.__class__(
1929e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr				xx1*xx2 + xy1*yx2,
1939e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr				xx1*xy2 + xy1*yy2,
1949e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr				yx1*xx2 + yy1*yx2,
1959e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr				yx1*xy2 + yy1*yy2,
1969e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr				xx2*dx1 + yx2*dy1 + dx2,
1979e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr				xy2*dx1 + yy2*dy1 + dy2)
1986385a4e9e16560212857b5eade6d0015f729a0b3jvr
1999e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	def reverseTransform(self, other):
200f184f754862637ad9c3c32952a0756414e2b67a6jvr		"""Return a new transformation, which is the other transformation
201f184f754862637ad9c3c32952a0756414e2b67a6jvr		transformed by self. self.reverseTransform(other) is equivalent to
202f184f754862637ad9c3c32952a0756414e2b67a6jvr		other.transform(self).
203f184f754862637ad9c3c32952a0756414e2b67a6jvr
204f184f754862637ad9c3c32952a0756414e2b67a6jvr		Example:
205f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>> t = Transform(2, 0, 0, 3, 1, 6)
206f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>> t.reverseTransform((4, 3, 2, 1, 5, 6))
207f184f754862637ad9c3c32952a0756414e2b67a6jvr			<Transform [8 6 6 3 21 15]>
208f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>> Transform(4, 3, 2, 1, 5, 6).transform((2, 0, 0, 3, 1, 6))
209f184f754862637ad9c3c32952a0756414e2b67a6jvr			<Transform [8 6 6 3 21 15]>
210f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>>
211f184f754862637ad9c3c32952a0756414e2b67a6jvr		"""
2129e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		xx1, xy1, yx1, yy1, dx1, dy1 = self.__affine
2139e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		xx2, xy2, yx2, yy2, dx2, dy2 = other
2149e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		return self.__class__(
2159e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr				xx1*xx2 + xy1*yx2,
2169e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr				xx1*xy2 + xy1*yy2,
2179e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr				yx1*xx2 + yy1*yx2,
2189e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr				yx1*xy2 + yy1*yy2,
2199e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr				xx2*dx1 + yx2*dy1 + dx2,
2209e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr				xy2*dx1 + yy2*dy1 + dy2)
2216385a4e9e16560212857b5eade6d0015f729a0b3jvr
2229e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	def inverse(self):
223f184f754862637ad9c3c32952a0756414e2b67a6jvr		"""Return the inverse transformation.
224f184f754862637ad9c3c32952a0756414e2b67a6jvr
225f184f754862637ad9c3c32952a0756414e2b67a6jvr		Example:
226deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> t = Identity.translate(2, 3).scale(4, 5)
227deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> t.transformPoint((10, 20))
228deca398915e92f24fe698e2e7d0e13122f61383ejvr			(42, 103)
229deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> it = t.inverse()
230deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> it.transformPoint((42, 103))
231deca398915e92f24fe698e2e7d0e13122f61383ejvr			(10.0, 20.0)
232f184f754862637ad9c3c32952a0756414e2b67a6jvr			>>>
233f184f754862637ad9c3c32952a0756414e2b67a6jvr		"""
2349e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		if self.__affine == (1, 0, 0, 1, 0, 0):
2359e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr			return self
2369e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		xx, xy, yx, yy, dx, dy = self.__affine
2379e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		det = float(xx*yy - yx*xy)
2389e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		xx, xy, yx, yy = yy/det, -xy/det, -yx/det, xx/det
2399e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		dx, dy = -xx*dx - yx*dy, -xy*dx - yy*dy
2409e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		return self.__class__(xx, xy, yx, yy, dx, dy)
2416385a4e9e16560212857b5eade6d0015f729a0b3jvr
2429e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	def toPS(self):
243deca398915e92f24fe698e2e7d0e13122f61383ejvr		"""Return a PostScript representation:
244deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> t = Identity.scale(2, 3).translate(4, 5)
245deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> t.toPS()
246deca398915e92f24fe698e2e7d0e13122f61383ejvr			'[2 0 0 3 8 15]'
247deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>>
248deca398915e92f24fe698e2e7d0e13122f61383ejvr		"""
2499e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		return "[%s %s %s %s %s %s]" % self.__affine
2506385a4e9e16560212857b5eade6d0015f729a0b3jvr
2519e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	def __len__(self):
252deca398915e92f24fe698e2e7d0e13122f61383ejvr		"""Transform instances also behave like sequences of length 6:
253deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> len(Identity)
254deca398915e92f24fe698e2e7d0e13122f61383ejvr			6
255deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>>
256deca398915e92f24fe698e2e7d0e13122f61383ejvr		"""
2579e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		return 6
2586385a4e9e16560212857b5eade6d0015f729a0b3jvr
2599e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	def __getitem__(self, index):
260deca398915e92f24fe698e2e7d0e13122f61383ejvr		"""Transform instances also behave like sequences of length 6:
261deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> list(Identity)
262deca398915e92f24fe698e2e7d0e13122f61383ejvr			[1, 0, 0, 1, 0, 0]
263deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> tuple(Identity)
264deca398915e92f24fe698e2e7d0e13122f61383ejvr			(1, 0, 0, 1, 0, 0)
265deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>>
266deca398915e92f24fe698e2e7d0e13122f61383ejvr		"""
2679e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		return self.__affine[index]
2686385a4e9e16560212857b5eade6d0015f729a0b3jvr
2699e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	def __getslice__(self, i, j):
270deca398915e92f24fe698e2e7d0e13122f61383ejvr		"""Transform instances also behave like sequences and even support
271deca398915e92f24fe698e2e7d0e13122f61383ejvr		slicing:
272deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> t = Offset(100, 200)
273deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> t
274deca398915e92f24fe698e2e7d0e13122f61383ejvr			<Transform [1 0 0 1 100 200]>
275deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> t[4:]
276deca398915e92f24fe698e2e7d0e13122f61383ejvr			(100, 200)
277deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>>
278deca398915e92f24fe698e2e7d0e13122f61383ejvr		"""
2799e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		return self.__affine[i:j]
2806385a4e9e16560212857b5eade6d0015f729a0b3jvr
2819e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	def __cmp__(self, other):
282deca398915e92f24fe698e2e7d0e13122f61383ejvr		"""Transform instances are comparable:
283deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> t1 = Identity.scale(2, 3).translate(4, 6)
284deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> t2 = Identity.translate(8, 18).scale(2, 3)
285deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> t1 == t2
286deca398915e92f24fe698e2e7d0e13122f61383ejvr			1
287deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>>
288deca398915e92f24fe698e2e7d0e13122f61383ejvr
289deca398915e92f24fe698e2e7d0e13122f61383ejvr		But beware of floating point rounding errors:
290deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> t1 = Identity.scale(0.2, 0.3).translate(0.4, 0.6)
291deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> t2 = Identity.translate(0.08, 0.18).scale(0.2, 0.3)
292deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> t1
293deca398915e92f24fe698e2e7d0e13122f61383ejvr			<Transform [0.2 0.0 0.0 0.3 0.08 0.18]>
294deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> t2
295deca398915e92f24fe698e2e7d0e13122f61383ejvr			<Transform [0.2 0.0 0.0 0.3 0.08 0.18]>
296deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> t1 == t2
297deca398915e92f24fe698e2e7d0e13122f61383ejvr			0
298deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>>
299deca398915e92f24fe698e2e7d0e13122f61383ejvr		"""
3009e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		xx1, xy1, yx1, yy1, dx1, dy1 = self.__affine
3019e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		xx2, xy2, yx2, yy2, dx2, dy2 = other
3029e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		return cmp((xx1, xy1, yx1, yy1, dx1, dy1),
3039e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr				(xx2, xy2, yx2, yy2, dx2, dy2))
3046385a4e9e16560212857b5eade6d0015f729a0b3jvr
3059e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	def __hash__(self):
306deca398915e92f24fe698e2e7d0e13122f61383ejvr		"""Transform instances are hashable, meaning you can use them as
307deca398915e92f24fe698e2e7d0e13122f61383ejvr		keys in dictionaries:
308deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> d = {Scale(12, 13): None}
309deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> d
310deca398915e92f24fe698e2e7d0e13122f61383ejvr			{<Transform [12 0 0 13 0 0]>: None}
311deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>>
312deca398915e92f24fe698e2e7d0e13122f61383ejvr
313deca398915e92f24fe698e2e7d0e13122f61383ejvr		But again, beware of floating point rounding errors:
314deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> t1 = Identity.scale(0.2, 0.3).translate(0.4, 0.6)
315deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> t2 = Identity.translate(0.08, 0.18).scale(0.2, 0.3)
316deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> t1
317deca398915e92f24fe698e2e7d0e13122f61383ejvr			<Transform [0.2 0.0 0.0 0.3 0.08 0.18]>
318deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> t2
319deca398915e92f24fe698e2e7d0e13122f61383ejvr			<Transform [0.2 0.0 0.0 0.3 0.08 0.18]>
320deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> d = {t1: None}
321deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> d
322deca398915e92f24fe698e2e7d0e13122f61383ejvr			{<Transform [0.2 0.0 0.0 0.3 0.08 0.18]>: None}
323deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>> d[t2]
324deca398915e92f24fe698e2e7d0e13122f61383ejvr			Traceback (most recent call last):
325deca398915e92f24fe698e2e7d0e13122f61383ejvr			  File "<stdin>", line 1, in ?
326deca398915e92f24fe698e2e7d0e13122f61383ejvr			KeyError: <Transform [0.2 0.0 0.0 0.3 0.08 0.18]>
327deca398915e92f24fe698e2e7d0e13122f61383ejvr			>>>
328deca398915e92f24fe698e2e7d0e13122f61383ejvr		"""
3299e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		return hash(self.__affine)
3306385a4e9e16560212857b5eade6d0015f729a0b3jvr
3319e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	def __repr__(self):
3329e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		return "<%s [%s %s %s %s %s %s]>" % ((self.__class__.__name__,)
3339e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr				 + tuple(map(str, self.__affine)))
3349e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr
3359e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr
3369e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvrIdentity = Transform()
3379e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr
3389e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvrdef Offset(x=0, y=0):
339f184f754862637ad9c3c32952a0756414e2b67a6jvr	"""Return the identity transformation offset by x, y.
340f184f754862637ad9c3c32952a0756414e2b67a6jvr
341f184f754862637ad9c3c32952a0756414e2b67a6jvr	Example:
342f184f754862637ad9c3c32952a0756414e2b67a6jvr		>>> Offset(2, 3)
343f184f754862637ad9c3c32952a0756414e2b67a6jvr		<Transform [1 0 0 1 2 3]>
344f184f754862637ad9c3c32952a0756414e2b67a6jvr		>>>
345f184f754862637ad9c3c32952a0756414e2b67a6jvr	"""
3469e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	return Transform(1, 0, 0, 1, x, y)
3479e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr
3489e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvrdef Scale(x, y=None):
349f184f754862637ad9c3c32952a0756414e2b67a6jvr	"""Return the identity transformation scaled by x, y. The 'y' argument
350f184f754862637ad9c3c32952a0756414e2b67a6jvr	may be None, which implies to use the x value for y as well.
351f184f754862637ad9c3c32952a0756414e2b67a6jvr
352f184f754862637ad9c3c32952a0756414e2b67a6jvr	Example:
353f184f754862637ad9c3c32952a0756414e2b67a6jvr		>>> Scale(2, 3)
354f184f754862637ad9c3c32952a0756414e2b67a6jvr		<Transform [2 0 0 3 0 0]>
355f184f754862637ad9c3c32952a0756414e2b67a6jvr		>>>
356f184f754862637ad9c3c32952a0756414e2b67a6jvr	"""
3579e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	if y is None:
3589e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr		y = x
3599e58c90401ef3ce7ed4a85fbda14bbbc564ebf0ejvr	return Transform(x, 0, 0, y, 0, 0)
360f184f754862637ad9c3c32952a0756414e2b67a6jvr
361f184f754862637ad9c3c32952a0756414e2b67a6jvr
362f184f754862637ad9c3c32952a0756414e2b67a6jvrdef _test():
363deca398915e92f24fe698e2e7d0e13122f61383ejvr	import doctest, transform
364deca398915e92f24fe698e2e7d0e13122f61383ejvr	return doctest.testmod(transform)
365f184f754862637ad9c3c32952a0756414e2b67a6jvr
366f184f754862637ad9c3c32952a0756414e2b67a6jvrif __name__ == "__main__":
367f184f754862637ad9c3c32952a0756414e2b67a6jvr	_test()
368