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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 import attr
, base
, 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(base
.canvasitem
, attr
.attr
):
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):
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):
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
136 det
= _det(self
.matrix
) # shouldn't be zero, but
138 matrix
= ( ( self
.matrix
[1][1]/det
, -self
.matrix
[0][1]/det
),
139 (-self
.matrix
[1][0]/det
, self
.matrix
[0][0]/det
)
141 except ZeroDivisionError:
142 raise UndefinedResultError
, "transformation matrix must not be singular"
143 return trafo_pt(matrix
=matrix
) * \
144 trafo_pt(vector
=(-self
.vector
[0], -self
.vector
[1]))
146 def mirrored(self
, angle
):
147 return mirror(angle
)*self
149 def rotated_pt(self
, angle
, x
=None, y
=None):
150 return rotate_pt(angle
, x
, y
)*self
152 def rotated(self
, angle
, x
=None, y
=None):
153 return rotate(angle
, x
, y
)*self
155 def scaled_pt(self
, sx
, sy
=None, x
=None, y
=None):
156 return scale_pt(sx
, sy
, x
, y
)*self
158 def scaled(self
, sx
, sy
=None, x
=None, y
=None):
159 return scale(sx
, sy
, x
, y
)*self
161 def slanted_pt(self
, a
, angle
=0, x
=None, y
=None):
162 return slant_pt(a
, angle
, x
, y
)*self
164 def slanted(self
, a
, angle
=0, x
=None, y
=None):
165 return slant(a
, angle
, x
, y
)*self
167 def translated_pt(self
, x
, y
):
168 return translate_pt(x
,y
)*self
170 def translated(self
, x
, y
):
171 return translate(x
, y
)*self
174 class trafo(trafo_pt
):
176 """affine transformation"""
178 def __init__(self
, matrix
=((1,0),(0,1)), vector
=(0,0)):
179 trafo_pt
.__init
__(self
,
181 (unit
.topt(vector
[0]), unit
.topt(vector
[1])))
185 # some standard transformations
189 def __init__(self
,angle
=0):
190 trafo
.__init
__(self
, matrix
=_mmatrix(angle
))
193 class rotate_pt(trafo_pt
):
194 def __init__(self
, angle
, x
=None, y
=None):
196 if x
is not None or y
is not None:
197 if x
is None or y
is None:
198 raise (UndefinedResultError
,
199 "either specify both x and y or none of them")
200 vector
=_rvector(angle
, x
, y
)
202 trafo_pt
.__init
__(self
,
203 matrix
=_rmatrix(angle
),
207 class rotate(trafo_pt
):
208 def __init__(self
, angle
, x
=None, y
=None):
210 if x
is not None or y
is not None:
211 if x
is None or y
is None:
212 raise (UndefinedResultError
,
213 "either specify both x and y or none of them")
214 vector
=_rvector(angle
, unit
.topt(x
), unit
.topt(y
))
216 trafo_pt
.__init
__(self
,
217 matrix
=_rmatrix(angle
),
221 class scale_pt(trafo_pt
):
222 def __init__(self
, sx
, sy
=None, x
=None, y
=None):
225 raise (UndefinedResultError
,
226 "one scaling factor is 0")
228 if x
is not None or y
is not None:
229 if x
is None or y
is None:
230 raise (UndefinedResultError
,
231 "either specify both x and y or none of them")
232 vector
= (1-sx
)*x
, (1-sy
)*y
234 trafo_pt
.__init
__(self
, matrix
=((sx
,0),(0,sy
)), vector
=vector
)
238 def __init__(self
, sx
, sy
=None, x
=None, y
=None):
241 raise (UndefinedResultError
,
242 "one scaling factor is 0")
244 if x
is not None or y
is not None:
245 if x
is None or y
is None:
246 raise (UndefinedResultError
,
247 "either specify both x and y or none of them")
248 vector
= (1-sx
)*x
, (1-sy
)*y
250 trafo
.__init
__(self
, matrix
=((sx
,0),(0,sy
)), vector
=vector
)
253 class slant_pt(trafo_pt
):
254 def __init__(self
, a
, angle
=0, x
=None, y
=None):
255 t
= ( rotate_pt(-angle
, x
, y
)*
256 trafo(matrix
=((1, a
), (0, 1)))*
257 rotate_pt(angle
, x
, y
) )
258 trafo_pt
.__init
__(self
, t
.matrix
, t
.vector
)
262 def __init__(self
, a
, angle
=0, x
=None, y
=None):
263 t
= ( rotate(-angle
, x
, y
)*
264 trafo(matrix
=((1, a
), (0, 1)))*
265 rotate(angle
, x
, y
) )
266 trafo
.__init
__(self
, t
.matrix
, t
.vector
)
269 class translate_pt(trafo_pt
):
270 def __init__(self
, x
, y
):
271 trafo_pt
.__init
__(self
, vector
=(x
, y
))
274 class translate(trafo
):
275 def __init__(self
, x
, y
):
276 trafo
.__init
__(self
, vector
=(x
, y
))