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] concat\n" % \
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 mirror(self
, angle
):
143 return mirroring(angle
)*self
145 def _rotate(self
, angle
, x
=None, y
=None):
146 return _rotation(angle
, x
, y
)*self
148 def rotate(self
, angle
, x
=None, y
=None):
149 return rotation(angle
, x
, y
)*self
151 def _scale(self
, sx
, sy
=None, x
=None, y
=None):
152 return _scaling(sx
, sy
, x
, y
)*self
154 def scale(self
, sx
, sy
=None, x
=None, y
=None):
155 return scaling(sx
, sy
, x
, y
)*self
157 def _slant(self
, a
, angle
=0, x
=None, y
=None):
158 return _slant(a
, angle
, x
, y
)*self
160 def slant(self
, a
, angle
=0, x
=None, y
=None):
161 return slant(a
, angle
, x
, y
)*self
163 def _translate(self
, x
, y
):
164 return _translation(x
,y
)*self
166 def translate(self
, x
, y
):
167 return translation(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
184 class mirroring(trafo
):
185 def __init__(self
,angle
=0):
186 trafo
.__init
__(self
, matrix
=_mmatrix(angle
))
189 class _rotation(_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 rotation(_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 _scaling(_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
)
233 class scaling(trafo
):
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
= ( _rotation(-angle
, x
, y
)*
252 trafo(matrix
=((1, a
), (0, 1)))*
253 _rotation(angle
, x
, y
) )
254 _trafo
.__init
__(self
, t
.matrix
, t
.vector
)
258 def __init__(self
, a
, angle
=0, x
=None, y
=None):
259 t
= ( rotation(-angle
, x
, y
)*
260 trafo(matrix
=((1, a
), (0, 1)))*
261 rotation(angle
, x
, y
) )
262 trafo
.__init
__(self
, t
.matrix
, t
.vector
)
265 class _translation(_trafo
):
266 def __init__(self
, x
, y
):
267 _trafo
.__init
__(self
, vector
=(x
, y
))
270 class translation(trafo
):
271 def __init__(self
, x
, y
):
272 trafo
.__init
__(self
, vector
=(x
, y
))