1 # -*- encoding: utf-8 -*-
4 # Copyright (C) 2002-2011 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2002-2011 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 from . import attr
, baseclasses
, unit
26 # global epsilon (used to judge whether a matrix is singular)
29 def set(epsilon
=None):
31 if epsilon
is not None:
35 # some helper routines
38 phi
= math
.pi
*angle
/180.0
40 return ((math
.cos(phi
), -math
.sin(phi
)),
41 (math
.sin(phi
), math
.cos(phi
)))
43 def _rvector(angle
, x
, y
):
44 phi
= math
.pi
*angle
/180.0
46 return ((1-math
.cos(phi
))*x
+ math
.sin(phi
) *y
,
47 -math
.sin(phi
) *x
+ (1-math
.cos(phi
))*y
)
51 phi
= math
.pi
*angle
/180.0
53 return ( (math
.cos(phi
)*math
.cos(phi
)-math
.sin(phi
)*math
.sin(phi
),
54 -2*math
.sin(phi
)*math
.cos(phi
) ),
55 (-2*math
.sin(phi
)*math
.cos(phi
),
56 math
.sin(phi
)*math
.sin(phi
)-math
.cos(phi
)*math
.cos(phi
) ) )
62 class TrafoException(Exception):
65 # trafo: affine transformations
67 class trafo_pt(baseclasses
.deformer
):
69 """affine transformation (coordinates in constructor in pts)
71 Note that though the coordinates in the constructor are in
72 pts (which is useful for internal purposes), all other
73 methods only accept units in the standard user notation.
77 def __init__(self
, matrix
=((1, 0), (0, 1)), vector
=(0, 0), epsilon
=_marker
):
78 """Return trafo with given transformation matrix and vector. If epsilon
79 is passed it is used instead of the global epsilon defined in the module to
80 check whether the matrix is singular or not. Use epsilon=None to turn of this
83 if epsilon
is _marker
:
85 self
.epsilon
= epsilon
86 if epsilon
is not None and abs(matrix
[0][0]*matrix
[1][1] - matrix
[0][1]*matrix
[1][0]) < epsilon
:
87 raise TrafoException("transformation matrix must not be singular")
92 def __mul__(self
, other
):
93 if isinstance(other
, trafo_pt
):
94 if self
.epsilon
is None or other
.epsilon
is None:
96 elif self
.epsilon
<= other
.epsilon
:
97 epsilon
= self
.epsilon
99 epsilon
= other
.epsilon
100 matrix
= ( ( self
.matrix
[0][0]*other
.matrix
[0][0] +
101 self
.matrix
[0][1]*other
.matrix
[1][0],
102 self
.matrix
[0][0]*other
.matrix
[0][1] +
103 self
.matrix
[0][1]*other
.matrix
[1][1] ),
104 ( self
.matrix
[1][0]*other
.matrix
[0][0] +
105 self
.matrix
[1][1]*other
.matrix
[1][0],
106 self
.matrix
[1][0]*other
.matrix
[0][1] +
107 self
.matrix
[1][1]*other
.matrix
[1][1] )
110 vector
= ( self
.matrix
[0][0]*other
.vector
[0] +
111 self
.matrix
[0][1]*other
.vector
[1] +
113 self
.matrix
[1][0]*other
.vector
[0] +
114 self
.matrix
[1][1]*other
.vector
[1] +
117 return trafo_pt(matrix
=matrix
, vector
=vector
, epsilon
=epsilon
)
119 raise NotImplementedError("can only multiply two transformations")
122 return "[%f %f %f %f %f %f]" % \
123 ( self
.matrix
[0][0], self
.matrix
[1][0],
124 self
.matrix
[0][1], self
.matrix
[1][1],
125 self
.vector
[0], self
.vector
[1] )
127 def processPS(self
, file, writer
, context
, registry
):
128 file.write("[%f %f %f %f %f %f] concat\n" % \
129 ( self
.matrix
[0][0], self
.matrix
[1][0],
130 self
.matrix
[0][1], self
.matrix
[1][1],
131 self
.vector
[0], self
.vector
[1] ) )
133 def processPDF(self
, file, writer
, context
, registry
):
134 file.write("%f %f %f %f %f %f cm\n" % \
135 ( self
.matrix
[0][0], self
.matrix
[1][0],
136 self
.matrix
[0][1], self
.matrix
[1][1],
137 self
.vector
[0], self
.vector
[1] ) )
139 def apply_pt(self
, x_pt
, y_pt
):
140 """apply transformation to point (x_pt, y_pt) in pts"""
141 return ( self
.matrix
[0][0]*x_pt
+ self
.matrix
[0][1]*y_pt
+ self
.vector
[0],
142 self
.matrix
[1][0]*x_pt
+ self
.matrix
[1][1]*y_pt
+ self
.vector
[1] )
144 def apply(self
, x
, y
):
145 # for the transformation we have to convert to points
146 tx
, ty
= self
.apply_pt(unit
.topt(x
), unit
.topt(y
))
147 return tx
* unit
.t_pt
, ty
* unit
.t_pt
149 def deform(self
, path
):
150 return path
.transformed(self
)
153 det
= 1.0*(self
.matrix
[0][0]*self
.matrix
[1][1] - self
.matrix
[0][1]*self
.matrix
[1][0])
154 matrix
= ( ( self
.matrix
[1][1]/det
, -self
.matrix
[0][1]/det
),
155 (-self
.matrix
[1][0]/det
, self
.matrix
[0][0]/det
) )
156 return ( trafo_pt(matrix
=matrix
, epsilon
=self
.epsilon
) *
157 trafo_pt(vector
=(-self
.vector
[0], -self
.vector
[1]), epsilon
=self
.epsilon
) )
159 def mirrored(self
, angle
):
160 return mirror(angle
, epsilon
=self
.epsilon
) * self
162 def rotated_pt(self
, angle
, x
=None, y
=None):
163 return rotate_pt(angle
, x
, y
, epsilon
=self
.epsilon
) * self
165 def rotated(self
, angle
, x
=None, y
=None):
166 return rotate(angle
, x
, y
, epsilon
=self
.epsilon
) * self
168 def scaled_pt(self
, sx
, sy
=None, x
=None, y
=None):
169 return scale_pt(sx
, sy
, x
, y
, epsilon
=self
.epsilon
) * self
171 def scaled(self
, sx
, sy
=None, x
=None, y
=None):
172 return scale(sx
, sy
, x
, y
, epsilon
=self
.epsilon
) * self
174 def slanted_pt(self
, a
, angle
=0, x
=None, y
=None):
175 return slant_pt(a
, angle
, x
, y
, epsilon
=self
.epsilon
) * self
177 def slanted(self
, a
, angle
=0, x
=None, y
=None):
178 return slant(a
, angle
, x
, y
, epsilon
=self
.epsilon
) * self
180 def translated_pt(self
, x
, y
):
181 return translate_pt(x
, y
, epsilon
=self
.epsilon
) * self
183 def translated(self
, x
, y
):
184 return translate(x
, y
, epsilon
=self
.epsilon
) * self
187 class trafo(trafo_pt
):
189 """affine transformation"""
191 def __init__(self
, matrix
=((1,0), (0,1)), vector
=(0, 0), epsilon
=_marker
):
192 trafo_pt
.__init
__(self
,
193 matrix
, (unit
.topt(vector
[0]), unit
.topt(vector
[1])),
197 # some standard transformations
203 def __init__(self
, angle
=0, epsilon
=_marker
):
204 trafo
.__init
__(self
, matrix
=_mmatrix(angle
), epsilon
=epsilon
)
207 class rotate_pt(trafo_pt
):
208 def __init__(self
, angle
, x
=None, y
=None, epsilon
=_marker
):
210 if x
is not None or y
is not None:
211 if x
is None or y
is None:
212 raise TrafoException("either specify both x and y or none of them")
213 vector
=_rvector(angle
, x
, y
)
215 trafo_pt
.__init
__(self
, matrix
=_rmatrix(angle
), vector
=vector
, epsilon
=epsilon
)
218 class rotate(trafo_pt
):
219 def __init__(self
, angle
, x
=None, y
=None, epsilon
=_marker
):
221 if x
is not None or y
is not None:
222 if x
is None or y
is None:
223 raise TrafoException("either specify both x and y or none of them")
224 vector
=_rvector(angle
, unit
.topt(x
), unit
.topt(y
))
226 trafo_pt
.__init
__(self
, matrix
=_rmatrix(angle
), vector
=vector
, epsilon
=epsilon
)
229 class scale_pt(trafo_pt
):
230 def __init__(self
, sx
, sy
=None, x
=None, y
=None, epsilon
=_marker
):
234 if x
is not None or y
is not None:
235 if x
is None or y
is None:
236 raise TrafoException("either specify both x and y or none of them")
237 vector
= (1-sx
)*x
, (1-sy
)*y
238 trafo_pt
.__init
__(self
, matrix
=((sx
, 0), (0, sy
)), vector
=vector
, epsilon
=epsilon
)
242 def __init__(self
, sx
, sy
=None, x
=None, y
=None, epsilon
=_marker
):
246 if x
is not None or y
is not None:
247 if x
is None or y
is None:
248 raise TrafoException("either specify both x and y or none of them")
249 vector
= (1-sx
)*x
, (1-sy
)*y
250 trafo
.__init
__(self
, matrix
=((sx
, 0), (0, sy
)), vector
=vector
, epsilon
=epsilon
)
253 class slant_pt(trafo_pt
):
254 def __init__(self
, a
, angle
=0, x
=None, y
=None, epsilon
=_marker
):
255 t
= ( rotate_pt(-angle
, x
, y
, epsilon
=epsilon
) *
256 trafo(matrix
=((1, a
), (0, 1)), epsilon
=epsilon
) *
257 rotate_pt(angle
, x
, y
, epsilon
=epsilon
) )
258 trafo_pt
.__init
__(self
, t
.matrix
, t
.vector
, epsilon
=epsilon
)
262 def __init__(self
, a
, angle
=0, x
=None, y
=None, epsilon
=_marker
):
263 t
= ( rotate(-angle
, x
, y
, epsilon
=epsilon
) *
264 trafo(matrix
=((1, a
), (0, 1)), epsilon
=epsilon
) *
265 rotate(angle
, x
, y
, epsilon
=epsilon
) )
266 trafo
.__init
__(self
, t
.matrix
, t
.vector
, epsilon
=epsilon
)
269 class translate_pt(trafo_pt
):
270 def __init__(self
, x
, y
, epsilon
=_marker
):
271 trafo_pt
.__init
__(self
, vector
=(x
, y
), epsilon
=epsilon
)
274 class translate(trafo
):
275 def __init__(self
, x
, y
, epsilon
=_marker
):
276 trafo
.__init
__(self
, vector
=(x
, y
), epsilon
=epsilon
)