1 # -*- coding: ISO-8859-1 -*-
4 # Copyright (C) 2002-2006 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2002-2004 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
24 import attr
, canvas
, deformer
, unit
25 import bbox
as bboxmodule
27 # global epsilon (used to judge whether a matrix is singular)
30 def set(epsilon
=None):
32 if epsilon
is not None:
36 # some helper routines
39 phi
= math
.pi
*angle
/180.0
41 return ((math
.cos(phi
), -math
.sin(phi
)),
42 (math
.sin(phi
), math
.cos(phi
)))
44 def _rvector(angle
, x
, y
):
45 phi
= math
.pi
*angle
/180.0
47 return ((1-math
.cos(phi
))*x
+ math
.sin(phi
) *y
,
48 -math
.sin(phi
) *x
+ (1-math
.cos(phi
))*y
)
52 phi
= math
.pi
*angle
/180.0
54 return ( (math
.cos(phi
)*math
.cos(phi
)-math
.sin(phi
)*math
.sin(phi
),
55 -2*math
.sin(phi
)*math
.cos(phi
) ),
56 (-2*math
.sin(phi
)*math
.cos(phi
),
57 math
.sin(phi
)*math
.sin(phi
)-math
.cos(phi
)*math
.cos(phi
) ) )
63 class TrafoException(Exception):
66 # trafo: affine transformations
68 class trafo_pt(canvas
.canvasitem
, deformer
.deformer
):
70 """affine transformation (coordinates in constructor in pts)
72 Note that though the coordinates in the constructor are in
73 pts (which is useful for internal purposes), all other
74 methods only accept units in the standard user notation.
78 def __init__(self
, matrix
=((1, 0), (0, 1)), vector
=(0, 0), epsilon
=_marker
):
79 """Return trafo with given transformation matrix and vector. If epsilon
80 is passed it is used instead of the global epsilon defined in the module to
81 check whether the matrix is singular or not. Use epsilon=None to turn of this
84 if epsilon
is _marker
:
86 self
.epsilon
= epsilon
87 if epsilon
is not None and abs(matrix
[0][0]*matrix
[1][1] - matrix
[0][1]*matrix
[1][0]) < epsilon
:
88 raise TrafoException("transformation matrix must not be singular")
93 def __mul__(self
, other
):
94 if isinstance(other
, trafo_pt
):
95 if self
.epsilon
is None or other
.epsilon
is None:
97 elif self
.epsilon
<= other
.epsilon
:
98 epsilon
= self
.epsilon
100 epsilon
= other
.epsilon
101 matrix
= ( ( self
.matrix
[0][0]*other
.matrix
[0][0] +
102 self
.matrix
[0][1]*other
.matrix
[1][0],
103 self
.matrix
[0][0]*other
.matrix
[0][1] +
104 self
.matrix
[0][1]*other
.matrix
[1][1] ),
105 ( self
.matrix
[1][0]*other
.matrix
[0][0] +
106 self
.matrix
[1][1]*other
.matrix
[1][0],
107 self
.matrix
[1][0]*other
.matrix
[0][1] +
108 self
.matrix
[1][1]*other
.matrix
[1][1] )
111 vector
= ( self
.matrix
[0][0]*other
.vector
[0] +
112 self
.matrix
[0][1]*other
.vector
[1] +
114 self
.matrix
[1][0]*other
.vector
[0] +
115 self
.matrix
[1][1]*other
.vector
[1] +
118 return trafo_pt(matrix
=matrix
, vector
=vector
, epsilon
=epsilon
)
120 raise NotImplementedError("can only multiply two transformations")
123 return "[%f %f %f %f %f %f]" % \
124 ( self
.matrix
[0][0], self
.matrix
[1][0],
125 self
.matrix
[0][1], self
.matrix
[1][1],
126 self
.vector
[0], self
.vector
[1] )
128 def processPS(self
, file, writer
, context
, registry
, bbox
):
129 file.write("[%f %f %f %f %f %f] concat\n" % \
130 ( self
.matrix
[0][0], self
.matrix
[1][0],
131 self
.matrix
[0][1], self
.matrix
[1][1],
132 self
.vector
[0], self
.vector
[1] ) )
134 def processPDF(self
, file, writer
, context
, registry
, bbox
):
135 file.write("%f %f %f %f %f %f cm\n" % \
136 ( self
.matrix
[0][0], self
.matrix
[1][0],
137 self
.matrix
[0][1], self
.matrix
[1][1],
138 self
.vector
[0], self
.vector
[1] ) )
141 return bboxmodule
.empty()
143 def apply_pt(self
, x_pt
, y_pt
):
144 """apply transformation to point (x_pt, y_pt) in pts"""
145 return ( self
.matrix
[0][0]*x_pt
+ self
.matrix
[0][1]*y_pt
+ self
.vector
[0],
146 self
.matrix
[1][0]*x_pt
+ self
.matrix
[1][1]*y_pt
+ self
.vector
[1] )
148 def apply(self
, x
, y
):
149 # for the transformation we have to convert to points
150 tx
, ty
= self
.apply_pt(unit
.topt(x
), unit
.topt(y
))
151 return tx
* unit
.t_pt
, ty
* unit
.t_pt
153 def deform(self
, path
):
154 return path
.transformed(self
)
157 det
= 1.0*(self
.matrix
[0][0]*self
.matrix
[1][1] - self
.matrix
[0][1]*self
.matrix
[1][0])
158 matrix
= ( ( self
.matrix
[1][1]/det
, -self
.matrix
[0][1]/det
),
159 (-self
.matrix
[1][0]/det
, self
.matrix
[0][0]/det
) )
160 return ( trafo_pt(matrix
=matrix
, epsilon
=self
.epsilon
) *
161 trafo_pt(vector
=(-self
.vector
[0], -self
.vector
[1]), epsilon
=self
.epsilon
) )
163 def mirrored(self
, angle
):
164 return mirror(angle
, epsilon
=self
.epsilon
) * self
166 def rotated_pt(self
, angle
, x
=None, y
=None):
167 return rotate_pt(angle
, x
, y
, epsilon
=self
.epsilon
) * self
169 def rotated(self
, angle
, x
=None, y
=None):
170 return rotate(angle
, x
, y
, epsilon
=self
.epsilon
) * self
172 def scaled_pt(self
, sx
, sy
=None, x
=None, y
=None):
173 return scale_pt(sx
, sy
, x
, y
, epsilon
=self
.epsilon
) * self
175 def scaled(self
, sx
, sy
=None, x
=None, y
=None):
176 return scale(sx
, sy
, x
, y
, epsilon
=self
.epsilon
) * self
178 def slanted_pt(self
, a
, angle
=0, x
=None, y
=None):
179 return slant_pt(a
, angle
, x
, y
, epsilon
=self
.epsilon
) * self
181 def slanted(self
, a
, angle
=0, x
=None, y
=None):
182 return slant(a
, angle
, x
, y
, epsilon
=self
.epsilon
) * self
184 def translated_pt(self
, x
, y
):
185 return translate_pt(x
, y
, epsilon
=self
.epsilon
) * self
187 def translated(self
, x
, y
):
188 return translate(x
, y
, epsilon
=self
.epsilon
) * self
191 class trafo(trafo_pt
):
193 """affine transformation"""
195 def __init__(self
, matrix
=((1,0), (0,1)), vector
=(0, 0), epsilon
=_marker
):
196 trafo_pt
.__init
__(self
,
197 matrix
, (unit
.topt(vector
[0]), unit
.topt(vector
[1])),
201 # some standard transformations
205 def __init__(self
, angle
=0, epsilon
=_marker
):
206 trafo
.__init
__(self
, matrix
=_mmatrix(angle
), epsilon
=epsilon
)
209 class rotate_pt(trafo_pt
):
210 def __init__(self
, angle
, x
=None, y
=None, epsilon
=_marker
):
212 if x
is not None or y
is not None:
213 if x
is None or y
is None:
214 raise TrafoException("either specify both x and y or none of them")
215 vector
=_rvector(angle
, x
, y
)
217 trafo_pt
.__init
__(self
, matrix
=_rmatrix(angle
), vector
=vector
, epsilon
=epsilon
)
220 class rotate(trafo_pt
):
221 def __init__(self
, angle
, x
=None, y
=None, epsilon
=_marker
):
223 if x
is not None or y
is not None:
224 if x
is None or y
is None:
225 raise TrafoException("either specify both x and y or none of them")
226 vector
=_rvector(angle
, unit
.topt(x
), unit
.topt(y
))
228 trafo_pt
.__init
__(self
, matrix
=_rmatrix(angle
), vector
=vector
, epsilon
=epsilon
)
231 class scale_pt(trafo_pt
):
232 def __init__(self
, sx
, sy
=None, x
=None, y
=None, epsilon
=_marker
):
236 if x
is not None or y
is not None:
237 if x
is None or y
is None:
238 raise TrafoException("either specify both x and y or none of them")
239 vector
= (1-sx
)*x
, (1-sy
)*y
240 trafo_pt
.__init
__(self
, matrix
=((sx
, 0), (0, sy
)), vector
=vector
, epsilon
=epsilon
)
244 def __init__(self
, sx
, sy
=None, x
=None, y
=None, epsilon
=_marker
):
248 if x
is not None or y
is not None:
249 if x
is None or y
is None:
250 raise TrafoException("either specify both x and y or none of them")
251 vector
= (1-sx
)*x
, (1-sy
)*y
252 trafo
.__init
__(self
, matrix
=((sx
, 0), (0, sy
)), vector
=vector
, epsilon
=epsilon
)
255 class slant_pt(trafo_pt
):
256 def __init__(self
, a
, angle
=0, x
=None, y
=None, epsilon
=_marker
):
257 t
= ( rotate_pt(-angle
, x
, y
, epsilon
=epsilon
) *
258 trafo(matrix
=((1, a
), (0, 1)), epsilon
=epsilon
) *
259 rotate_pt(angle
, x
, y
, epsilon
=epsilon
) )
260 trafo_pt
.__init
__(self
, t
.matrix
, t
.vector
, epsilon
=epsilon
)
264 def __init__(self
, a
, angle
=0, x
=None, y
=None, epsilon
=_marker
):
265 t
= ( rotate(-angle
, x
, y
, epsilon
=epsilon
) *
266 trafo(matrix
=((1, a
), (0, 1)), epsilon
=epsilon
) *
267 rotate(angle
, x
, y
, epsilon
=epsilon
) )
268 trafo
.__init
__(self
, t
.matrix
, t
.vector
, epsilon
=epsilon
)
271 class translate_pt(trafo_pt
):
272 def __init__(self
, x
, y
, epsilon
=_marker
):
273 trafo_pt
.__init
__(self
, vector
=(x
, y
), epsilon
=epsilon
)
276 class translate(trafo
):
277 def __init__(self
, x
, y
, epsilon
=_marker
):
278 trafo
.__init
__(self
, vector
=(x
, y
), epsilon
=epsilon
)