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
27 # - switch to affine space description (i.e. represent transformation by
28 # 3x3 matrix (cf. PLRM Sect. 4.3.3)? Cooler!
30 # some helper routines
33 phi
= math
.pi
*angle
/180.0
35 return (( math
.cos(phi
), math
.sin(phi
)),
36 (-math
.sin(phi
), math
.cos(phi
)))
38 def _rvector(angle
, x
, y
):
39 phi
= math
.pi
*angle
/180.0
41 return ((1-math
.cos(phi
))*x
+ math
.sin(phi
) *y
,
42 -math
.sin(phi
) *x
+ (1-math
.cos(phi
))*y
)
46 phi
= math
.pi
*angle
/180.0
48 return ( (math
.cos(phi
)*math
.cos(phi
)-math
.sin(phi
)*math
.sin(phi
),
49 -2*math
.sin(phi
)*math
.cos(phi
) ),
50 (-2*math
.sin(phi
)*math
.cos(phi
),
51 math
.sin(phi
)*math
.sin(phi
)-math
.cos(phi
)*math
.cos(phi
) ) )
54 return matrix
[0][0]*matrix
[1][1] - matrix
[0][1]*matrix
[1][0]
58 class UndefinedResultError(ArithmeticError):
61 # trafo: affine transformations
63 class _trafo(base
.PSOp
):
65 """affine transformation (coordinates in constructor in pts)
67 Note that though the coordinates in the constructor are in
68 pts (which is useful for internal purposes), all other
69 methods only accept units in the standard user notation.
73 def __init__(self
, matrix
=((1,0),(0,1)), vector
=(0,0)):
75 raise UndefinedResultError
, "transformation matrix must not be singular"
80 def __mul__(self
, other
):
81 if isinstance(other
, _trafo
):
82 matrix
= ( ( self
.matrix
[0][0]*other
.matrix
[0][0] +
83 self
.matrix
[0][1]*other
.matrix
[1][0],
84 self
.matrix
[0][0]*other
.matrix
[0][1] +
85 self
.matrix
[0][1]*other
.matrix
[1][1] ),
86 ( self
.matrix
[1][0]*other
.matrix
[0][0] +
87 self
.matrix
[1][1]*other
.matrix
[1][0],
88 self
.matrix
[1][0]*other
.matrix
[0][1] +
89 self
.matrix
[1][1]*other
.matrix
[1][1] )
92 vector
= ( self
.matrix
[0][0]*other
.vector
[0] +
93 self
.matrix
[1][0]*other
.vector
[1] +
95 self
.matrix
[0][1]*other
.vector
[0] +
96 self
.matrix
[1][1]*other
.vector
[1] +
99 return _trafo(matrix
=matrix
, vector
=vector
)
101 raise NotImplementedError, "can only multiply two transformations"
104 return "[%f %f %f %f %f %f] concat\n" % \
105 ( self
.matrix
[0][0], self
.matrix
[0][1],
106 self
.matrix
[1][0], self
.matrix
[1][1],
107 self
.vector
[0], self
.vector
[1] )
109 def write(self
, file):
110 file.write("[%f %f %f %f %f %f] concat\n" % \
111 ( self
.matrix
[0][0], self
.matrix
[0][1],
112 self
.matrix
[1][0], self
.matrix
[1][1],
113 self
.vector
[0], self
.vector
[1] ) )
118 def _apply(self
, x
, y
):
119 """apply transformation to point (x,y) (coordinates in pts)"""
120 return (self
.matrix
[0][0]*x
+
121 self
.matrix
[1][0]*y
+
123 self
.matrix
[0][1]*x
+
124 self
.matrix
[1][1]*y
+
127 def apply(self
, x
, y
):
128 # before the transformation, we first have to convert to
129 # our internal unit (i.e. pts)
130 tx
, ty
= self
._apply
(unit
.topt(x
), unit
.topt(y
))
132 # the end result can be converted back to general lengths
133 return (unit
.t_pt(tx
), unit
.t_pt(ty
))
136 det
= _det(self
.matrix
) # shouldn't be zero, but
138 matrix
= ( ( self
.matrix
[1][1]/det
, -self
.matrix
[0][1]/det
),
139 (-self
.matrix
[1][0]/det
, self
.matrix
[0][0]/det
)
141 except ZeroDivisionError:
142 raise UndefinedResultError
, "transformation matrix must not be singular"
143 return _trafo(matrix
=matrix
) * \
144 _trafo(vector
=(-self
.vector
[0], -self
.vector
[1]))
146 def mirror(self
, angle
):
147 return mirroring(angle
)*self
149 def _rotate(self
, angle
, x
=None, y
=None):
150 return _rotation(angle
, x
, y
)*self
152 def rotate(self
, angle
, x
=None, y
=None):
153 return rotation(angle
, x
, y
)*self
155 def _scale(self
, sx
, sy
=None, x
=None, y
=None):
156 return _scaling(sx
, sy
, x
, y
)*self
158 def scale(self
, sx
, sy
=None, x
=None, y
=None):
159 return scaling(sx
, sy
, x
, y
)*self
161 def _translate(self
, x
, y
):
162 return _translation(x
,y
)*self
164 def translate(self
, x
, y
):
165 return translation(x
, y
)*self
170 """affine transformation"""
172 def __init__(self
, matrix
=((1,0),(0,1)), vector
=(0,0)):
173 _trafo
.__init
__(self
,
175 (unit
.topt(vector
[0]), unit
.topt(vector
[1])))
179 # some standard transformations
182 class mirroring(trafo
):
183 def __init__(self
,angle
=0):
184 trafo
.__init
__(self
, matrix
=_mmatrix(angle
))
187 class _rotation(_trafo
):
188 def __init__(self
, angle
, x
=None, y
=None):
190 if x
is not None or y
is not None:
191 if x
is None or y
is None:
192 raise (UndefinedResultError
,
193 "either specify both x and y or none of them")
194 vector
=_rvector(angle
, x
, y
)
196 _trafo
.__init
__(self
,
197 matrix
=_rmatrix(angle
),
201 class rotation(_trafo
):
202 def __init__(self
, angle
, x
=None, y
=None):
204 if x
is not None or y
is not None:
205 if x
is None or y
is None:
206 raise (UndefinedResultError
,
207 "either specify both x and y or none of them")
208 vector
=_rvector(angle
, unit
.topt(x
), unit
.topt(y
))
210 _trafo
.__init
__(self
,
211 matrix
=_rmatrix(angle
),
215 class _scaling(_trafo
):
216 def __init__(self
, sx
, sy
=None, x
=None, y
=None):
219 raise (UndefinedResultError
,
220 "one scaling factor is 0")
222 if x
is not None or y
is not None:
223 if x
is None or y
is None:
224 raise (UndefinedResultError
,
225 "either specify both x and y or none of them")
226 vector
=(1-sx
)*x
, (1-sy
)*y
228 _trafo
.__init
__(self
, matrix
=((sx
,0),(0,sy
)), vector
=vector
)
231 class scaling(trafo
):
232 def __init__(self
, sx
, sy
=None, x
=None, y
=None):
235 raise (UndefinedResultError
,
236 "one scaling factor is 0")
238 if x
is not None or y
is not None:
239 if x
is None or y
is None:
240 raise (UndefinedResultError
,
241 "either specify both x and y or none of them")
242 vector
=(1-sx
)*x
, (1-sy
)*y
244 trafo
.__init
__(self
, matrix
=((sx
,0),(0,sy
)), vector
=vector
)
247 class _translation(_trafo
):
248 def __init__(self
, x
, y
):
249 _trafo
.__init
__(self
, vector
=(x
, y
))
252 class translation(trafo
):
253 def __init__(self
, x
, y
):
254 trafo
.__init
__(self
, vector
=(x
, y
))