2 # -*- coding: ISO-8859-1 -*-
5 # Copyright (C) 2002-2006 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
26 import bbox
as bboxmodule
28 # global epsilon (used to judge whether a matrix is singular)
31 def set(epsilon
=None):
33 if epsilon
is not None:
37 # some helper routines
40 phi
= math
.pi
*angle
/180.0
42 return ((math
.cos(phi
), -math
.sin(phi
)),
43 (math
.sin(phi
), math
.cos(phi
)))
45 def _rvector(angle
, x
, y
):
46 phi
= math
.pi
*angle
/180.0
48 return ((1-math
.cos(phi
))*x
+ math
.sin(phi
) *y
,
49 -math
.sin(phi
) *x
+ (1-math
.cos(phi
))*y
)
53 phi
= math
.pi
*angle
/180.0
55 return ( (math
.cos(phi
)*math
.cos(phi
)-math
.sin(phi
)*math
.sin(phi
),
56 -2*math
.sin(phi
)*math
.cos(phi
) ),
57 (-2*math
.sin(phi
)*math
.cos(phi
),
58 math
.sin(phi
)*math
.sin(phi
)-math
.cos(phi
)*math
.cos(phi
) ) )
64 class TrafoException(Exception):
67 # trafo: affine transformations
69 class trafo_pt(canvas
.canvasitem
, deformer
.deformer
):
71 """affine transformation (coordinates in constructor in pts)
73 Note that though the coordinates in the constructor are in
74 pts (which is useful for internal purposes), all other
75 methods only accept units in the standard user notation.
79 def __init__(self
, matrix
=((1, 0), (0, 1)), vector
=(0, 0), epsilon
=_marker
):
80 """Return trafo with given transformation matrix and vector. If epsilon
81 is passed it is used instead of the global epsilon defined in the module to
82 check whether the matrix is singular or not. Use epsilon=None to turn of this
85 if epsilon
is _marker
:
87 self
.epsilon
= epsilon
88 if epsilon
is not None and abs(matrix
[0][0]*matrix
[1][1] - matrix
[0][1]*matrix
[1][0]) < epsilon
:
89 raise TrafoException("transformation matrix must not be singular")
94 def __mul__(self
, other
):
95 if isinstance(other
, trafo_pt
):
96 if self
.epsilon
is None or other
.epsilon
is None:
98 elif self
.epsilon
<= other
.epsilon
:
99 epsilon
= self
.epsilon
101 epsilon
= other
.epsilon
102 matrix
= ( ( self
.matrix
[0][0]*other
.matrix
[0][0] +
103 self
.matrix
[0][1]*other
.matrix
[1][0],
104 self
.matrix
[0][0]*other
.matrix
[0][1] +
105 self
.matrix
[0][1]*other
.matrix
[1][1] ),
106 ( self
.matrix
[1][0]*other
.matrix
[0][0] +
107 self
.matrix
[1][1]*other
.matrix
[1][0],
108 self
.matrix
[1][0]*other
.matrix
[0][1] +
109 self
.matrix
[1][1]*other
.matrix
[1][1] )
112 vector
= ( self
.matrix
[0][0]*other
.vector
[0] +
113 self
.matrix
[0][1]*other
.vector
[1] +
115 self
.matrix
[1][0]*other
.vector
[0] +
116 self
.matrix
[1][1]*other
.vector
[1] +
119 return trafo_pt(matrix
=matrix
, vector
=vector
, epsilon
=epsilon
)
121 raise NotImplementedError("can only multiply two transformations")
124 return "[%f %f %f %f %f %f]" % \
125 ( self
.matrix
[0][0], self
.matrix
[1][0],
126 self
.matrix
[0][1], self
.matrix
[1][1],
127 self
.vector
[0], self
.vector
[1] )
129 def processPS(self
, file, writer
, context
, registry
, bbox
):
130 file.write("[%f %f %f %f %f %f] concat\n" % \
131 ( self
.matrix
[0][0], self
.matrix
[1][0],
132 self
.matrix
[0][1], self
.matrix
[1][1],
133 self
.vector
[0], self
.vector
[1] ) )
135 def processPDF(self
, file, writer
, context
, registry
, bbox
):
136 file.write("%f %f %f %f %f %f cm\n" % \
137 ( self
.matrix
[0][0], self
.matrix
[1][0],
138 self
.matrix
[0][1], self
.matrix
[1][1],
139 self
.vector
[0], self
.vector
[1] ) )
142 return bboxmodule
.empty()
144 def apply_pt(self
, x_pt
, y_pt
):
145 """apply transformation to point (x_pt, y_pt) in pts"""
146 return ( self
.matrix
[0][0]*x_pt
+ self
.matrix
[0][1]*y_pt
+ self
.vector
[0],
147 self
.matrix
[1][0]*x_pt
+ self
.matrix
[1][1]*y_pt
+ self
.vector
[1] )
149 def apply(self
, x
, y
):
150 # for the transformation we have to convert to points
151 tx
, ty
= self
.apply_pt(unit
.topt(x
), unit
.topt(y
))
152 return tx
* unit
.t_pt
, ty
* unit
.t_pt
154 def deform(self
, path
):
155 return path
.transformed(self
)
158 det
= 1.0*(self
.matrix
[0][0]*self
.matrix
[1][1] - self
.matrix
[0][1]*self
.matrix
[1][0])
159 matrix
= ( ( self
.matrix
[1][1]/det
, -self
.matrix
[0][1]/det
),
160 (-self
.matrix
[1][0]/det
, self
.matrix
[0][0]/det
) )
161 return ( trafo_pt(matrix
=matrix
, epsilon
=self
.epsilon
) *
162 trafo_pt(vector
=(-self
.vector
[0], -self
.vector
[1]), epsilon
=self
.epsilon
) )
164 def mirrored(self
, angle
):
165 return mirror(angle
, epsilon
=self
.epsilon
) * self
167 def rotated_pt(self
, angle
, x
=None, y
=None):
168 return rotate_pt(angle
, x
, y
, epsilon
=self
.epsilon
) * self
170 def rotated(self
, angle
, x
=None, y
=None):
171 return rotate(angle
, x
, y
, epsilon
=self
.epsilon
) * self
173 def scaled_pt(self
, sx
, sy
=None, x
=None, y
=None):
174 return scale_pt(sx
, sy
, x
, y
, epsilon
=self
.epsilon
) * self
176 def scaled(self
, sx
, sy
=None, x
=None, y
=None):
177 return scale(sx
, sy
, x
, y
, epsilon
=self
.epsilon
) * self
179 def slanted_pt(self
, a
, angle
=0, x
=None, y
=None):
180 return slant_pt(a
, angle
, x
, y
, epsilon
=self
.epsilon
) * self
182 def slanted(self
, a
, angle
=0, x
=None, y
=None):
183 return slant(a
, angle
, x
, y
, epsilon
=self
.epsilon
) * self
185 def translated_pt(self
, x
, y
):
186 return translate_pt(x
, y
, epsilon
=self
.epsilon
) * self
188 def translated(self
, x
, y
):
189 return translate(x
, y
, epsilon
=self
.epsilon
) * self
192 class trafo(trafo_pt
):
194 """affine transformation"""
196 def __init__(self
, matrix
=((1,0), (0,1)), vector
=(0, 0), epsilon
=_marker
):
197 trafo_pt
.__init
__(self
,
198 matrix
, (unit
.topt(vector
[0]), unit
.topt(vector
[1])),
202 # some standard transformations
206 def __init__(self
, angle
=0, epsilon
=_marker
):
207 trafo
.__init
__(self
, matrix
=_mmatrix(angle
), epsilon
=epsilon
)
210 class rotate_pt(trafo_pt
):
211 def __init__(self
, angle
, x
=None, y
=None, epsilon
=_marker
):
213 if x
is not None or y
is not None:
214 if x
is None or y
is None:
215 raise TrafoException("either specify both x and y or none of them")
216 vector
=_rvector(angle
, x
, y
)
218 trafo_pt
.__init
__(self
, matrix
=_rmatrix(angle
), vector
=vector
, epsilon
=epsilon
)
221 class rotate(trafo_pt
):
222 def __init__(self
, angle
, x
=None, y
=None, epsilon
=_marker
):
224 if x
is not None or y
is not None:
225 if x
is None or y
is None:
226 raise TrafoException("either specify both x and y or none of them")
227 vector
=_rvector(angle
, unit
.topt(x
), unit
.topt(y
))
229 trafo_pt
.__init
__(self
, matrix
=_rmatrix(angle
), vector
=vector
, epsilon
=epsilon
)
232 class scale_pt(trafo_pt
):
233 def __init__(self
, sx
, sy
=None, x
=None, y
=None, epsilon
=_marker
):
237 if x
is not None or y
is not None:
238 if x
is None or y
is None:
239 raise TrafoException("either specify both x and y or none of them")
240 vector
= (1-sx
)*x
, (1-sy
)*y
241 trafo_pt
.__init
__(self
, matrix
=((sx
, 0), (0, sy
)), vector
=vector
, epsilon
=epsilon
)
245 def __init__(self
, sx
, sy
=None, x
=None, y
=None, epsilon
=_marker
):
249 if x
is not None or y
is not None:
250 if x
is None or y
is None:
251 raise TrafoException("either specify both x and y or none of them")
252 vector
= (1-sx
)*x
, (1-sy
)*y
253 trafo
.__init
__(self
, matrix
=((sx
, 0), (0, sy
)), vector
=vector
, epsilon
=epsilon
)
256 class slant_pt(trafo_pt
):
257 def __init__(self
, a
, angle
=0, x
=None, y
=None, epsilon
=_marker
):
258 t
= ( rotate_pt(-angle
, x
, y
, epsilon
=epsilon
) *
259 trafo(matrix
=((1, a
), (0, 1)), epsilon
=epsilon
) *
260 rotate_pt(angle
, x
, y
, epsilon
=epsilon
) )
261 trafo_pt
.__init
__(self
, t
.matrix
, t
.vector
, epsilon
=epsilon
)
265 def __init__(self
, a
, angle
=0, x
=None, y
=None, epsilon
=_marker
):
266 t
= ( rotate(-angle
, x
, y
, epsilon
=epsilon
) *
267 trafo(matrix
=((1, a
), (0, 1)), epsilon
=epsilon
) *
268 rotate(angle
, x
, y
, epsilon
=epsilon
) )
269 trafo
.__init
__(self
, t
.matrix
, t
.vector
, epsilon
=epsilon
)
272 class translate_pt(trafo_pt
):
273 def __init__(self
, x
, y
, epsilon
=_marker
):
274 trafo_pt
.__init
__(self
, vector
=(x
, y
), epsilon
=epsilon
)
277 class translate(trafo
):
278 def __init__(self
, x
, y
, epsilon
=_marker
):
279 trafo
.__init
__(self
, vector
=(x
, y
), epsilon
=epsilon
)