2 # -*- coding: ISO-8859-1 -*-
5 # Copyright (C) 2002 Jörg Lehmann <joergl@users.sourceforge.net>
6 # Copyright (C) 2002 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
, bbox
, 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(base
.PSOp
, 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
):
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(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 write(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] ) )
115 def _apply(self
, x
, y
):
116 """apply transformation to point (x,y) (coordinates in pts)"""
117 return (self
.matrix
[0][0]*x
+
118 self
.matrix
[0][1]*y
+
120 self
.matrix
[1][0]*x
+
121 self
.matrix
[1][1]*y
+
124 def apply(self
, x
, y
):
125 # before the transformation, we first have to convert to
126 # our internal unit (i.e. pts)
127 tx
, ty
= self
._apply
(unit
.topt(x
), unit
.topt(y
))
129 # the end result can be converted back to general lengths
130 return (unit
.t_pt(tx
), unit
.t_pt(ty
))
133 det
= _det(self
.matrix
) # shouldn't be zero, but
135 matrix
= ( ( self
.matrix
[1][1]/det
, -self
.matrix
[0][1]/det
),
136 (-self
.matrix
[1][0]/det
, self
.matrix
[0][0]/det
)
138 except ZeroDivisionError:
139 raise UndefinedResultError
, "transformation matrix must not be singular"
140 return _trafo(matrix
=matrix
) * \
141 _trafo(vector
=(-self
.vector
[0], -self
.vector
[1]))
143 def mirrored(self
, angle
):
144 return mirror(angle
)*self
146 def _rotated(self
, angle
, x
=None, y
=None):
147 return _rotate(angle
, x
, y
)*self
149 def rotated(self
, angle
, x
=None, y
=None):
150 return rotate(angle
, x
, y
)*self
152 def _scaled(self
, sx
, sy
=None, x
=None, y
=None):
153 return _scale(sx
, sy
, x
, y
)*self
155 def scaled(self
, sx
, sy
=None, x
=None, y
=None):
156 return scale(sx
, sy
, x
, y
)*self
158 def _slanted(self
, a
, angle
=0, x
=None, y
=None):
159 return _slant(a
, angle
, x
, y
)*self
161 def slanted(self
, a
, angle
=0, x
=None, y
=None):
162 return slant(a
, angle
, x
, y
)*self
164 def _translated(self
, x
, y
):
165 return _translate(x
,y
)*self
167 def translated(self
, x
, y
):
168 return translate(x
, y
)*self
173 """affine transformation"""
175 def __init__(self
, matrix
=((1,0),(0,1)), vector
=(0,0)):
176 _trafo
.__init
__(self
,
178 (unit
.topt(vector
[0]), unit
.topt(vector
[1])))
182 # some standard transformations
186 def __init__(self
,angle
=0):
187 trafo
.__init
__(self
, matrix
=_mmatrix(angle
))
190 class _rotate(_trafo
):
191 def __init__(self
, angle
, x
=None, y
=None):
193 if x
is not None or y
is not None:
194 if x
is None or y
is None:
195 raise (UndefinedResultError
,
196 "either specify both x and y or none of them")
197 vector
=_rvector(angle
, x
, y
)
199 _trafo
.__init
__(self
,
200 matrix
=_rmatrix(angle
),
204 class rotate(_trafo
):
205 def __init__(self
, angle
, x
=None, y
=None):
207 if x
is not None or y
is not None:
208 if x
is None or y
is None:
209 raise (UndefinedResultError
,
210 "either specify both x and y or none of them")
211 vector
=_rvector(angle
, unit
.topt(x
), unit
.topt(y
))
213 _trafo
.__init
__(self
,
214 matrix
=_rmatrix(angle
),
218 class _scale(_trafo
):
219 def __init__(self
, sx
, sy
=None, x
=None, y
=None):
222 raise (UndefinedResultError
,
223 "one scaling factor is 0")
225 if x
is not None or y
is not None:
226 if x
is None or y
is None:
227 raise (UndefinedResultError
,
228 "either specify both x and y or none of them")
229 vector
= (1-sx
)*x
, (1-sy
)*y
231 _trafo
.__init
__(self
, matrix
=((sx
,0),(0,sy
)), vector
=vector
)
235 def __init__(self
, sx
, sy
=None, x
=None, y
=None):
238 raise (UndefinedResultError
,
239 "one scaling factor is 0")
241 if x
is not None or y
is not None:
242 if x
is None or y
is None:
243 raise (UndefinedResultError
,
244 "either specify both x and y or none of them")
245 vector
= (1-sx
)*x
, (1-sy
)*y
247 trafo
.__init
__(self
, matrix
=((sx
,0),(0,sy
)), vector
=vector
)
250 class _slant(_trafo
):
251 def __init__(self
, a
, angle
=0, x
=None, y
=None):
252 t
= ( _rotate(-angle
, x
, y
)*
253 trafo(matrix
=((1, a
), (0, 1)))*
254 _rotate(angle
, x
, y
) )
255 _trafo
.__init
__(self
, t
.matrix
, t
.vector
)
259 def __init__(self
, a
, angle
=0, x
=None, y
=None):
260 t
= ( rotate(-angle
, x
, y
)*
261 trafo(matrix
=((1, a
), (0, 1)))*
262 rotate(angle
, x
, y
) )
263 trafo
.__init
__(self
, t
.matrix
, t
.vector
)
266 class _translate(_trafo
):
267 def __init__(self
, x
, y
):
268 _trafo
.__init
__(self
, vector
=(x
, y
))
271 class translate(trafo
):
272 def __init__(self
, x
, y
):
273 trafo
.__init
__(self
, vector
=(x
, y
))