4 # Copyright (C) 2002 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 import base
, bbox
, unit
26 # some helper routines
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
)
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 return matrix
[0][0]*matrix
[1][1] - matrix
[0][1]*matrix
[1][0]
54 class UndefinedResultError(ArithmeticError):
57 # trafo: affine transformations
59 class _trafo(base
.PSOp
):
61 """affine transformation (coordinates in constructor in pts)
63 Note that though the coordinates in the constructor are in
64 pts (which is useful for internal purposes), all other
65 methods only accept units in the standard user notation.
69 def __init__(self
, matrix
=((1,0),(0,1)), vector
=(0,0)):
71 raise UndefinedResultError
, "transformation matrix must not be singular"
76 def __mul__(self
, other
):
77 if isinstance(other
, _trafo
):
78 matrix
= ( ( self
.matrix
[0][0]*other
.matrix
[0][0] +
79 self
.matrix
[0][1]*other
.matrix
[1][0],
80 self
.matrix
[0][0]*other
.matrix
[0][1] +
81 self
.matrix
[0][1]*other
.matrix
[1][1] ),
82 ( self
.matrix
[1][0]*other
.matrix
[0][0] +
83 self
.matrix
[1][1]*other
.matrix
[1][0],
84 self
.matrix
[1][0]*other
.matrix
[0][1] +
85 self
.matrix
[1][1]*other
.matrix
[1][1] )
88 vector
= ( self
.matrix
[0][0]*other
.vector
[0] +
89 self
.matrix
[0][1]*other
.vector
[1] +
91 self
.matrix
[1][0]*other
.vector
[0] +
92 self
.matrix
[1][1]*other
.vector
[1] +
95 return _trafo(matrix
=matrix
, vector
=vector
)
97 raise NotImplementedError, "can only multiply two transformations"
100 return "[%f %f %f %f %f %f]" % \
101 ( self
.matrix
[0][0], self
.matrix
[1][0],
102 self
.matrix
[0][1], self
.matrix
[1][1],
103 self
.vector
[0], self
.vector
[1] )
105 def write(self
, file):
106 file.write("[%f %f %f %f %f %f] concat\n" % \
107 ( self
.matrix
[0][0], self
.matrix
[1][0],
108 self
.matrix
[0][1], self
.matrix
[1][1],
109 self
.vector
[0], self
.vector
[1] ) )
114 def _apply(self
, x
, y
):
115 """apply transformation to point (x,y) (coordinates in pts)"""
116 return (self
.matrix
[0][0]*x
+
117 self
.matrix
[0][1]*y
+
119 self
.matrix
[1][0]*x
+
120 self
.matrix
[1][1]*y
+
123 def apply(self
, x
, y
):
124 # before the transformation, we first have to convert to
125 # our internal unit (i.e. pts)
126 tx
, ty
= self
._apply
(unit
.topt(x
), unit
.topt(y
))
128 # the end result can be converted back to general lengths
129 return (unit
.t_pt(tx
), unit
.t_pt(ty
))
132 det
= _det(self
.matrix
) # shouldn't be zero, but
134 matrix
= ( ( self
.matrix
[1][1]/det
, -self
.matrix
[0][1]/det
),
135 (-self
.matrix
[1][0]/det
, self
.matrix
[0][0]/det
)
137 except ZeroDivisionError:
138 raise UndefinedResultError
, "transformation matrix must not be singular"
139 return _trafo(matrix
=matrix
) * \
140 _trafo(vector
=(-self
.vector
[0], -self
.vector
[1]))
142 def mirrored(self
, angle
):
143 return mirror(angle
)*self
145 def _rotated(self
, angle
, x
=None, y
=None):
146 return _rotate(angle
, x
, y
)*self
148 def rotated(self
, angle
, x
=None, y
=None):
149 return rotate(angle
, x
, y
)*self
151 def _scaled(self
, sx
, sy
=None, x
=None, y
=None):
152 return _scale(sx
, sy
, x
, y
)*self
154 def scaled(self
, sx
, sy
=None, x
=None, y
=None):
155 return scale(sx
, sy
, x
, y
)*self
157 def _slanted(self
, a
, angle
=0, x
=None, y
=None):
158 return _slanted(a
, angle
, x
, y
)*self
160 def slanted(self
, a
, angle
=0, x
=None, y
=None):
161 return slanted(a
, angle
, x
, y
)*self
163 def _translated(self
, x
, y
):
164 return _translate(x
,y
)*self
166 def translated(self
, x
, y
):
167 return translate(x
, y
)*self
172 """affine transformation"""
174 def __init__(self
, matrix
=((1,0),(0,1)), vector
=(0,0)):
175 _trafo
.__init
__(self
,
177 (unit
.topt(vector
[0]), unit
.topt(vector
[1])))
181 # some standard transformations
185 def __init__(self
,angle
=0):
186 trafo
.__init
__(self
, matrix
=_mmatrix(angle
))
189 class _rotate(_trafo
):
190 def __init__(self
, angle
, x
=None, y
=None):
192 if x
is not None or y
is not None:
193 if x
is None or y
is None:
194 raise (UndefinedResultError
,
195 "either specify both x and y or none of them")
196 vector
=_rvector(angle
, x
, y
)
198 _trafo
.__init
__(self
,
199 matrix
=_rmatrix(angle
),
203 class rotate(_trafo
):
204 def __init__(self
, angle
, x
=None, y
=None):
206 if x
is not None or y
is not None:
207 if x
is None or y
is None:
208 raise (UndefinedResultError
,
209 "either specify both x and y or none of them")
210 vector
=_rvector(angle
, unit
.topt(x
), unit
.topt(y
))
212 _trafo
.__init
__(self
,
213 matrix
=_rmatrix(angle
),
217 class _scale(_trafo
):
218 def __init__(self
, sx
, sy
=None, x
=None, y
=None):
221 raise (UndefinedResultError
,
222 "one scaling factor is 0")
224 if x
is not None or y
is not None:
225 if x
is None or y
is None:
226 raise (UndefinedResultError
,
227 "either specify both x and y or none of them")
228 vector
= (1-sx
)*x
, (1-sy
)*y
230 _trafo
.__init
__(self
, matrix
=((sx
,0),(0,sy
)), vector
=vector
)
234 def __init__(self
, sx
, sy
=None, x
=None, y
=None):
237 raise (UndefinedResultError
,
238 "one scaling factor is 0")
240 if x
is not None or y
is not None:
241 if x
is None or y
is None:
242 raise (UndefinedResultError
,
243 "either specify both x and y or none of them")
244 vector
= (1-sx
)*x
, (1-sy
)*y
246 trafo
.__init
__(self
, matrix
=((sx
,0),(0,sy
)), vector
=vector
)
249 class _slant(_trafo
):
250 def __init__(self
, a
, angle
=0, x
=None, y
=None):
251 t
= ( _rotate(-angle
, x
, y
)*
252 trafo(matrix
=((1, a
), (0, 1)))*
253 _rotate(angle
, x
, y
) )
254 _trafo
.__init
__(self
, t
.matrix
, t
.vector
)
258 def __init__(self
, a
, angle
=0, x
=None, y
=None):
259 t
= ( rotate(-angle
, x
, y
)*
260 trafo(matrix
=((1, a
), (0, 1)))*
261 rotate(angle
, x
, y
) )
262 trafo
.__init
__(self
, t
.matrix
, t
.vector
)
265 class _translate(_trafo
):
266 def __init__(self
, x
, y
):
267 _trafo
.__init
__(self
, vector
=(x
, y
))
270 class translate(trafo
):
271 def __init__(self
, x
, y
):
272 trafo
.__init
__(self
, vector
=(x
, y
))