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
26 # - switch to affine space description (i.e. represent transformation by
27 # 3x3 matrix (cf. PLRM Sect. 4.3.3)? Cooler!
30 # some helper routines
33 from math
import pi
, cos
, sin
36 return (( cos(phi
), sin(phi
)),
37 (-sin(phi
), cos(phi
)))
40 from math
import pi
, cos
, sin
43 return ( (cos(phi
)*cos(phi
)-sin(phi
)*sin(phi
), -2*sin(phi
)*cos(phi
) ),
44 (-2*sin(phi
)*cos(phi
), sin(phi
)*sin(phi
)-cos(phi
)*cos(phi
) ) )
47 return matrix
[0][0]*matrix
[1][1] - matrix
[0][1]*matrix
[1][0]
51 class UndefinedResultError(ArithmeticError):
54 # transformation: affine transformations
57 def __init__(self
, matrix
=((1,0),(0,1)), vector
=(0,0)):
59 raise UndefinedResultError
, "transformation matrix must not be singular"
62 self
.vector
=tuple(map(lambda x
: unit
.length(x
), vector
))
64 def __mul__(self
, other
):
65 if isinstance(other
, transformation
):
66 matrix
= ( ( self
.matrix
[0][0]*other
.matrix
[0][0] + self
.matrix
[0][1]*other
.matrix
[1][0],
67 self
.matrix
[0][0]*other
.matrix
[0][1] + self
.matrix
[0][1]*other
.matrix
[1][1] ),
68 ( self
.matrix
[1][0]*other
.matrix
[0][0] + self
.matrix
[1][1]*other
.matrix
[1][0],
69 self
.matrix
[1][0]*other
.matrix
[0][1] + self
.matrix
[1][1]*other
.matrix
[1][1] )
72 vector
= ( self
.matrix
[0][0]*other
.vector
[0] + self
.matrix
[1][0]*other
.vector
[1] + self
.vector
[0],
73 self
.matrix
[0][1]*other
.vector
[0] + self
.matrix
[1][1]*other
.vector
[1] + self
.vector
[1] )
75 # print " ( %s * %s => %s ) "% (self, other, transformation(angle=angle, vector=vector))
77 return transformation(matrix
=matrix
, vector
=vector
)
79 raise NotImplementedError, "can only multiply two transformations"
80 def __rmul__(self
, other
): # TODO: not needed!?
81 return other
.__mul
__(self
)
83 def apply(self
, point
):
84 return (self
.matrix
[0][0]*point
[0] + self
.matrix
[1][0]*point
[1]+self
.vector
[0],
85 self
.matrix
[0][1]*point
[0] + self
.matrix
[1][1]*point
[1]+self
.vector
[1])
96 def translate(self
,x
,y
):
97 return transformation(vector
=(x
,y
))*self
99 def rotate(self
,angle
):
100 return transformation(matrix
=_rmatrix(angle
))*self
102 def mirror(self
,angle
):
103 return transformation(matrix
=_mmatrix(angle
))*self
105 def scale(self
, x
, y
=None):
106 return transformation(matrix
=((x
, 0), (0,y
or x
)))*self
109 det
= _det(self
.matrix
) # shouldn't be zero, but
111 matrix
= ( ( self
.matrix
[1][1]/det
, -self
.matrix
[0][1]/det
),
112 (-self
.matrix
[1][0]/det
, self
.matrix
[0][0]/det
)
114 except ZeroDivisionError:
115 raise UndefinedResultError
, "transformation matrix must not be singular"
116 return transformation(matrix
=matrix
) * transformation(vector
=(-self
.vector
[0],-self
.vector
[1]))
118 def bbox(self
, acanvas
):
119 # assert 0, "this shouldn't be called!"
122 def write(self
, canvas
, file):
123 file.write("[%f %f %f %f %f %f] concat\n" % \
124 ( self
.matrix
[0][0], self
.matrix
[0][1],
125 self
.matrix
[1][0], self
.matrix
[1][1],
126 canvas
.unit
.pt(self
.vector
[0]), canvas
.unit
.pt(self
.vector
[1])))
129 class translate(transformation
):
130 def __init__(self
,x
,y
):
131 transformation
.__init
__(self
, vector
=(x
,y
))
133 class rotate(transformation
):
134 def __init__(self
,angle
):
135 transformation
.__init
__(self
, matrix
=_rmatrix(angle
))
137 class mirror(transformation
):
138 def __init__(self
,angle
=0):
139 transformation
.__init
__(self
, matrix
=_mmatrix(angle
))
141 class scale(transformation
):
142 def __init__(self
,x
,y
=None):
143 transformation
.__init
__(self
, matrix
=((x
,0),(0,y
or x
)))
146 if __name__
=="__main__":
147 # test for some invariants:
149 def checkforidentity(trafo
):
151 m
= max(map(abs,[trafo
.matrix
[0][0]-1,
154 trafo
.matrix
[1][1]-1,
155 u
.pt(trafo
.vector
[0]),
156 u
.pt(trafo
.vector
[1])]))
158 assert m
<1e-7, "tests for invariants failed"
161 # transformation(angle=angle, vector=(x,y)) == translate(x,y) * rotate(angle)
162 checkforidentity( translate(1,3) * rotate(15) * transformation(matrix
=_rmatrix(15),vector
=(1,3)).inverse())
165 t
= translate(-1,-1)*rotate(72)*translate(1,1)
166 checkforidentity(t
*t
.inverse())
168 # -mirroring two times should yield identiy
169 # -mirror(phi)=mirror(phi+180)
170 checkforidentity( mirror(20)*mirror(20))
171 checkforidentity( mirror(20).mirror(20))
172 checkforidentity( mirror(20)*mirror(180+20))
174 # equivalent notations
175 checkforidentity( translate(1,2).rotate(72).translate(-3,-1)*(translate(-3,-1)*rotate(72)*translate(1,2)).inverse() )
177 checkforidentity( rotate(40).rotate(120).rotate(90).rotate(110) )
179 checkforidentity( scale(2,3).scale(1/2.0, 1/3.0) )
189 print " (1,0) => (%f, %f)" % (u
.m(esx
[0])*100, u
.m(esx
[1])*100)
190 print " (0,1) => (%f, %f)" % (u
.m(esy
[0])*100, u
.m(esy
[1])*100)
192 applyonbasis("translate(1,0)")
193 applyonbasis("translate(0,1)")
194 applyonbasis("rotate(90)")
195 applyonbasis("scale(0.5)")
196 applyonbasis("translate(1,0)*rotate(90)")
197 applyonbasis("translate(1,0)*scale(0.5)")
198 applyonbasis("rotate(90)*translate(1,0)")
199 applyonbasis("scale(0.5)*translate(1,0)")
200 applyonbasis("translate(1,0)*rotate(90)*scale(0.5)")
201 applyonbasis("translate(1,0)*scale(0.5)*rotate(90)")
202 applyonbasis("rotate(90)*scale(0.5)*translate(1,0)")
203 applyonbasis("scale(0.5)*rotate(90)*translate(1,0)")