2 # -*- coding: ISO-8859-1 -*-
5 # Copyright (C) 2002-2004 Jörg Lehmann <joergl@users.sourceforge.net>
6 # Copyright (C) 2002-2004 André Wobst <wobsta@users.sourceforge.net>
8 # This file is part of PyX (http://pyx.sourceforge.net/).
10 # PyX is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # PyX is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with PyX; if not, write to the Free Software
22 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 import attr
, canvas
, deformer
, unit
27 # some helper routines
30 phi
= math
.pi
*angle
/180.0
32 return ((math
.cos(phi
), -math
.sin(phi
)),
33 (math
.sin(phi
), math
.cos(phi
)))
35 def _rvector(angle
, x
, y
):
36 phi
= math
.pi
*angle
/180.0
38 return ((1-math
.cos(phi
))*x
+ math
.sin(phi
) *y
,
39 -math
.sin(phi
) *x
+ (1-math
.cos(phi
))*y
)
43 phi
= math
.pi
*angle
/180.0
45 return ( (math
.cos(phi
)*math
.cos(phi
)-math
.sin(phi
)*math
.sin(phi
),
46 -2*math
.sin(phi
)*math
.cos(phi
) ),
47 (-2*math
.sin(phi
)*math
.cos(phi
),
48 math
.sin(phi
)*math
.sin(phi
)-math
.cos(phi
)*math
.cos(phi
) ) )
51 return matrix
[0][0]*matrix
[1][1] - matrix
[0][1]*matrix
[1][0]
55 class UndefinedResultError(ArithmeticError):
58 # trafo: affine transformations
60 class trafo_pt(canvas
.canvasitem
, deformer
.deformer
):
62 """affine transformation (coordinates in constructor in pts)
64 Note that though the coordinates in the constructor are in
65 pts (which is useful for internal purposes), all other
66 methods only accept units in the standard user notation.
70 def __init__(self
, matrix
=((1,0),(0,1)), vector
=(0,0)):
72 raise UndefinedResultError
, "transformation matrix must not be singular"
77 def __mul__(self
, other
):
78 if isinstance(other
, trafo_pt
):
79 matrix
= ( ( self
.matrix
[0][0]*other
.matrix
[0][0] +
80 self
.matrix
[0][1]*other
.matrix
[1][0],
81 self
.matrix
[0][0]*other
.matrix
[0][1] +
82 self
.matrix
[0][1]*other
.matrix
[1][1] ),
83 ( self
.matrix
[1][0]*other
.matrix
[0][0] +
84 self
.matrix
[1][1]*other
.matrix
[1][0],
85 self
.matrix
[1][0]*other
.matrix
[0][1] +
86 self
.matrix
[1][1]*other
.matrix
[1][1] )
89 vector
= ( self
.matrix
[0][0]*other
.vector
[0] +
90 self
.matrix
[0][1]*other
.vector
[1] +
92 self
.matrix
[1][0]*other
.vector
[0] +
93 self
.matrix
[1][1]*other
.vector
[1] +
96 return trafo_pt(matrix
=matrix
, vector
=vector
)
98 raise NotImplementedError, "can only multiply two transformations"
101 return "[%f %f %f %f %f %f]" % \
102 ( self
.matrix
[0][0], self
.matrix
[1][0],
103 self
.matrix
[0][1], self
.matrix
[1][1],
104 self
.vector
[0], self
.vector
[1] )
106 def outputPS(self
, file, writer
, context
):
107 file.write("[%f %f %f %f %f %f] concat\n" % \
108 ( self
.matrix
[0][0], self
.matrix
[1][0],
109 self
.matrix
[0][1], self
.matrix
[1][1],
110 self
.vector
[0], self
.vector
[1] ) )
112 def outputPDF(self
, file, writer
, context
):
113 file.write("%f %f %f %f %f %f cm\n" % \
114 ( self
.matrix
[0][0], self
.matrix
[1][0],
115 self
.matrix
[0][1], self
.matrix
[1][1],
116 self
.vector
[0], self
.vector
[1] ) )
121 def _apply(self
, x
, y
):
122 """apply transformation to point (x,y) (coordinates in pts)"""
123 return (self
.matrix
[0][0]*x
+
124 self
.matrix
[0][1]*y
+
126 self
.matrix
[1][0]*x
+
127 self
.matrix
[1][1]*y
+
130 def apply(self
, x
, y
):
131 # for the transformation we have to convert to points
132 tx
, ty
= self
._apply
(unit
.topt(x
), unit
.topt(y
))
133 return tx
* unit
.t_pt
, ty
* unit
.t_pt
135 def deform(self
, path
):
136 return path
.transformed(self
)
139 det
= _det(self
.matrix
) # shouldn't be zero, but
141 matrix
= ( ( self
.matrix
[1][1]/det
, -self
.matrix
[0][1]/det
),
142 (-self
.matrix
[1][0]/det
, self
.matrix
[0][0]/det
)
144 except ZeroDivisionError:
145 raise UndefinedResultError
, "transformation matrix must not be singular"
146 return trafo_pt(matrix
=matrix
) * \
147 trafo_pt(vector
=(-self
.vector
[0], -self
.vector
[1]))
149 def mirrored(self
, angle
):
150 return mirror(angle
)*self
152 def rotated_pt(self
, angle
, x
=None, y
=None):
153 return rotate_pt(angle
, x
, y
)*self
155 def rotated(self
, angle
, x
=None, y
=None):
156 return rotate(angle
, x
, y
)*self
158 def scaled_pt(self
, sx
, sy
=None, x
=None, y
=None):
159 return scale_pt(sx
, sy
, x
, y
)*self
161 def scaled(self
, sx
, sy
=None, x
=None, y
=None):
162 return scale(sx
, sy
, x
, y
)*self
164 def slanted_pt(self
, a
, angle
=0, x
=None, y
=None):
165 return slant_pt(a
, angle
, x
, y
)*self
167 def slanted(self
, a
, angle
=0, x
=None, y
=None):
168 return slant(a
, angle
, x
, y
)*self
170 def translated_pt(self
, x
, y
):
171 return translate_pt(x
,y
)*self
173 def translated(self
, x
, y
):
174 return translate(x
, y
)*self
177 class trafo(trafo_pt
):
179 """affine transformation"""
181 def __init__(self
, matrix
=((1,0),(0,1)), vector
=(0,0)):
182 trafo_pt
.__init
__(self
,
184 (unit
.topt(vector
[0]), unit
.topt(vector
[1])))
188 # some standard transformations
192 def __init__(self
,angle
=0):
193 trafo
.__init
__(self
, matrix
=_mmatrix(angle
))
196 class rotate_pt(trafo_pt
):
197 def __init__(self
, angle
, x
=None, y
=None):
199 if x
is not None or y
is not None:
200 if x
is None or y
is None:
201 raise (UndefinedResultError
,
202 "either specify both x and y or none of them")
203 vector
=_rvector(angle
, x
, y
)
205 trafo_pt
.__init
__(self
,
206 matrix
=_rmatrix(angle
),
210 class rotate(trafo_pt
):
211 def __init__(self
, angle
, x
=None, y
=None):
213 if x
is not None or y
is not None:
214 if x
is None or y
is None:
215 raise (UndefinedResultError
,
216 "either specify both x and y or none of them")
217 vector
=_rvector(angle
, unit
.topt(x
), unit
.topt(y
))
219 trafo_pt
.__init
__(self
,
220 matrix
=_rmatrix(angle
),
224 class scale_pt(trafo_pt
):
225 def __init__(self
, sx
, sy
=None, x
=None, y
=None):
228 raise (UndefinedResultError
,
229 "one scaling factor is 0")
231 if x
is not None or y
is not None:
232 if x
is None or y
is None:
233 raise (UndefinedResultError
,
234 "either specify both x and y or none of them")
235 vector
= (1-sx
)*x
, (1-sy
)*y
237 trafo_pt
.__init
__(self
, matrix
=((sx
,0),(0,sy
)), vector
=vector
)
241 def __init__(self
, sx
, sy
=None, x
=None, y
=None):
244 raise (UndefinedResultError
,
245 "one scaling factor is 0")
247 if x
is not None or y
is not None:
248 if x
is None or y
is None:
249 raise (UndefinedResultError
,
250 "either specify both x and y or none of them")
251 vector
= (1-sx
)*x
, (1-sy
)*y
253 trafo
.__init
__(self
, matrix
=((sx
,0),(0,sy
)), vector
=vector
)
256 class slant_pt(trafo_pt
):
257 def __init__(self
, a
, angle
=0, x
=None, y
=None):
258 t
= ( rotate_pt(-angle
, x
, y
)*
259 trafo(matrix
=((1, a
), (0, 1)))*
260 rotate_pt(angle
, x
, y
) )
261 trafo_pt
.__init
__(self
, t
.matrix
, t
.vector
)
265 def __init__(self
, a
, angle
=0, x
=None, y
=None):
266 t
= ( rotate(-angle
, x
, y
)*
267 trafo(matrix
=((1, a
), (0, 1)))*
268 rotate(angle
, x
, y
) )
269 trafo
.__init
__(self
, t
.matrix
, t
.vector
)
272 class translate_pt(trafo_pt
):
273 def __init__(self
, x
, y
):
274 trafo_pt
.__init
__(self
, vector
=(x
, y
))
277 class translate(trafo
):
278 def __init__(self
, x
, y
):
279 trafo
.__init
__(self
, vector
=(x
, y
))