observed a case with a negative checksum, and make the code match the description...
[PyX.git] / trafo.py
blobdc050417cf2c430fe9e13abab8f70a0de7e4a574
1 # -*- encoding: utf-8 -*-
4 # Copyright (C) 2002-2011 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2002-2011 André Wobst <wobsta@users.sourceforge.net>
7 # This file is part of PyX (http://pyx.sourceforge.net/).
9 # PyX is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # PyX is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with PyX; if not, write to the Free Software
21 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 import math
24 from . import attr, baseclasses, unit
26 # some helper routines
28 def _rmatrix(angle):
29 phi = math.pi*angle/180.0
31 return ((math.cos(phi), -math.sin(phi)),
32 (math.sin(phi), math.cos(phi)))
34 def _rvector(angle, x, y):
35 phi = math.pi*angle/180.0
37 return ((1-math.cos(phi))*x + math.sin(phi) *y,
38 -math.sin(phi) *x + (1-math.cos(phi))*y)
41 def _mmatrix(angle):
42 phi = math.pi*angle/180.0
44 return ( (math.cos(phi)*math.cos(phi)-math.sin(phi)*math.sin(phi),
45 -2*math.sin(phi)*math.cos(phi) ),
46 (-2*math.sin(phi)*math.cos(phi),
47 math.sin(phi)*math.sin(phi)-math.cos(phi)*math.cos(phi) ) )
50 # trafo: affine transformations
52 class trafo_pt(baseclasses.deformer):
54 """affine transformation (coordinates in constructor in pts)
56 Note that though the coordinates in the constructor are in
57 pts (which is useful for internal purposes), all other
58 methods only accept units in the standard user notation.
60 """
62 def __init__(self, matrix=((1, 0), (0, 1)), vector=(0, 0)):
63 """Return trafo with given transformation matrix and vector.
64 """
65 self.matrix = matrix
66 self.vector = vector
68 def __mul__(self, other):
69 if isinstance(other, trafo_pt):
70 matrix = ( ( self.matrix[0][0]*other.matrix[0][0] +
71 self.matrix[0][1]*other.matrix[1][0],
72 self.matrix[0][0]*other.matrix[0][1] +
73 self.matrix[0][1]*other.matrix[1][1] ),
74 ( self.matrix[1][0]*other.matrix[0][0] +
75 self.matrix[1][1]*other.matrix[1][0],
76 self.matrix[1][0]*other.matrix[0][1] +
77 self.matrix[1][1]*other.matrix[1][1] )
80 vector = ( self.matrix[0][0]*other.vector[0] +
81 self.matrix[0][1]*other.vector[1] +
82 self.vector[0],
83 self.matrix[1][0]*other.vector[0] +
84 self.matrix[1][1]*other.vector[1] +
85 self.vector[1] )
87 return trafo_pt(matrix=matrix, vector=vector)
88 else:
89 raise NotImplementedError("can only multiply two transformations")
91 def __str__(self):
92 return "[%f %f %f %f %f %f]" % \
93 ( self.matrix[0][0], self.matrix[1][0],
94 self.matrix[0][1], self.matrix[1][1],
95 self.vector[0], self.vector[1] )
97 def processPS(self, file, writer, context, registry):
98 file.write("[%f %f %f %f %f %f] concat\n" % \
99 ( self.matrix[0][0], self.matrix[1][0],
100 self.matrix[0][1], self.matrix[1][1],
101 self.vector[0], self.vector[1] ) )
103 def processPDF(self, file, writer, context, registry):
104 file.write("%f %f %f %f %f %f cm\n" % \
105 ( self.matrix[0][0], self.matrix[1][0],
106 self.matrix[0][1], self.matrix[1][1],
107 self.vector[0], self.vector[1] ) )
109 def apply_pt(self, x_pt, y_pt):
110 """apply transformation to point (x_pt, y_pt) in pts"""
111 return ( self.matrix[0][0]*x_pt + self.matrix[0][1]*y_pt + self.vector[0],
112 self.matrix[1][0]*x_pt + self.matrix[1][1]*y_pt + self.vector[1] )
114 def apply(self, x, y):
115 # for the transformation we have to convert to points
116 tx, ty = self.apply_pt(unit.topt(x), unit.topt(y))
117 return tx * unit.t_pt, ty * unit.t_pt
119 def deform(self, path):
120 return path.transformed(self)
122 def inverse(self):
123 det = 1.0*(self.matrix[0][0]*self.matrix[1][1] - self.matrix[0][1]*self.matrix[1][0])
124 matrix = ( ( self.matrix[1][1]/det, -self.matrix[0][1]/det),
125 (-self.matrix[1][0]/det, self.matrix[0][0]/det) )
126 return ( trafo_pt(matrix=matrix) *
127 trafo_pt(vector=(-self.vector[0], -self.vector[1])) )
129 def mirrored(self, angle):
130 return mirror(angle) * self
132 def rotated_pt(self, angle, x=None, y=None):
133 return rotate_pt(angle, x, y) * self
135 def rotated(self, angle, x=None, y=None):
136 return rotate(angle, x, y) * self
138 def scaled_pt(self, sx, sy=None, x=None, y=None):
139 return scale_pt(sx, sy, x, y) * self
141 def scaled(self, sx, sy=None, x=None, y=None):
142 return scale(sx, sy, x, y) * self
144 def slanted_pt(self, a, angle=0, x=None, y=None):
145 return slant_pt(a, angle, x, y) * self
147 def slanted(self, a, angle=0, x=None, y=None):
148 return slant(a, angle, x, y) * self
150 def translated_pt(self, x, y):
151 return translate_pt(x, y) * self
153 def translated(self, x, y):
154 return translate(x, y) * self
157 class trafo(trafo_pt):
159 """affine transformation"""
161 def __init__(self, matrix=((1,0), (0,1)), vector=(0, 0)):
162 trafo_pt.__init__(self,
163 matrix, (unit.topt(vector[0]), unit.topt(vector[1])))
166 # some standard transformations
169 identity = trafo()
171 class mirror(trafo):
172 def __init__(self, angle=0):
173 trafo.__init__(self, matrix=_mmatrix(angle))
176 class rotate_pt(trafo_pt):
177 def __init__(self, angle, x=None, y=None):
178 vector = 0, 0
179 if x is not None or y is not None:
180 if x is None or y is None:
181 raise TrafoException("either specify both x and y or none of them")
182 vector=_rvector(angle, x, y)
184 trafo_pt.__init__(self, matrix=_rmatrix(angle), vector=vector)
187 class rotate(trafo_pt):
188 def __init__(self, angle, x=None, y=None):
189 vector = 0, 0
190 if x is not None or y is not None:
191 if x is None or y is None:
192 raise TrafoException("either specify both x and y or none of them")
193 vector=_rvector(angle, unit.topt(x), unit.topt(y))
195 trafo_pt.__init__(self, matrix=_rmatrix(angle), vector=vector)
198 class scale_pt(trafo_pt):
199 def __init__(self, sx, sy=None, x=None, y=None):
200 if sy is None:
201 sy = sx
202 vector = 0, 0
203 if x is not None or y is not None:
204 if x is None or y is None:
205 raise TrafoException("either specify both x and y or none of them")
206 vector = (1-sx)*x, (1-sy)*y
207 trafo_pt.__init__(self, matrix=((sx, 0), (0, sy)), vector=vector)
210 class scale(trafo):
211 def __init__(self, sx, sy=None, x=None, y=None):
212 if sy is None:
213 sy = sx
214 vector = 0, 0
215 if x is not None or y is not None:
216 if x is None or y is None:
217 raise TrafoException("either specify both x and y or none of them")
218 vector = (1-sx)*x, (1-sy)*y
219 trafo.__init__(self, matrix=((sx, 0), (0, sy)), vector=vector)
222 class slant_pt(trafo_pt):
223 def __init__(self, a, angle=0, x=None, y=None):
224 t = ( rotate_pt(-angle, x, y) *
225 trafo(matrix=((1, a), (0, 1))) *
226 rotate_pt(angle, x, y) )
227 trafo_pt.__init__(self, t.matrix, t.vector)
230 class slant(trafo):
231 def __init__(self, a, angle=0, x=None, y=None):
232 t = ( rotate(-angle, x, y) *
233 trafo(matrix=((1, a), (0, 1))) *
234 rotate(angle, x, y) )
235 trafo.__init__(self, t.matrix, t.vector)
238 class translate_pt(trafo_pt):
239 def __init__(self, x, y):
240 trafo_pt.__init__(self, vector=(x, y))
243 class translate(trafo):
244 def __init__(self, x, y):
245 trafo.__init__(self, vector=(x, y))