2
# -*- coding: ISO-8859-1 -*-
5
# Copyright (C) 2002-2004 J�rg Lehmann <joergl@users.sourceforge.net>
6
# Copyright (C) 2002-2004 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, deformer, 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_pt(base.canvasitem, deformer.deformer):
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_pt):
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_pt(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 outputPS(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] ) )
112
def outputPDF(self, file):
113
file.write("%f %f %f %f %f %f cm\n" % \
114
( self.matrix[0][0], self.matrix[1][0],
115
self.matrix[0][1], self.matrix[1][1],
116
self.vector[0], self.vector[1] ) )
121
def _apply(self, x, y):
122
"""apply transformation to point (x,y) (coordinates in pts)"""
123
return (self.matrix[0][0]*x +
124
self.matrix[0][1]*y +
126
self.matrix[1][0]*x +
127
self.matrix[1][1]*y +
130
def apply(self, x, y):
131
# for the transformation we have to convert to points
132
tx, ty = self._apply(unit.topt(x), unit.topt(y))
133
return tx * unit.t_pt, ty * unit.t_pt
135
def deform(self, path):
136
return path.transformed(self)
139
det = _det(self.matrix) # shouldn't be zero, but
141
matrix = ( ( self.matrix[1][1]/det, -self.matrix[0][1]/det),
142
(-self.matrix[1][0]/det, self.matrix[0][0]/det)
144
except ZeroDivisionError:
145
raise UndefinedResultError, "transformation matrix must not be singular"
146
return trafo_pt(matrix=matrix) * \
147
trafo_pt(vector=(-self.vector[0], -self.vector[1]))
149
def mirrored(self, angle):
150
return mirror(angle)*self
152
def rotated_pt(self, angle, x=None, y=None):
153
return rotate_pt(angle, x, y)*self
155
def rotated(self, angle, x=None, y=None):
156
return rotate(angle, x, y)*self
158
def scaled_pt(self, sx, sy=None, x=None, y=None):
159
return scale_pt(sx, sy, x, y)*self
161
def scaled(self, sx, sy=None, x=None, y=None):
162
return scale(sx, sy, x, y)*self
164
def slanted_pt(self, a, angle=0, x=None, y=None):
165
return slant_pt(a, angle, x, y)*self
167
def slanted(self, a, angle=0, x=None, y=None):
168
return slant(a, angle, x, y)*self
170
def translated_pt(self, x, y):
171
return translate_pt(x,y)*self
173
def translated(self, x, y):
174
return translate(x, y)*self
177
class trafo(trafo_pt):
179
"""affine transformation"""
181
def __init__(self, matrix=((1,0),(0,1)), vector=(0,0)):
182
trafo_pt.__init__(self,
184
(unit.topt(vector[0]), unit.topt(vector[1])))
188
# some standard transformations
192
def __init__(self,angle=0):
193
trafo.__init__(self, matrix=_mmatrix(angle))
196
class rotate_pt(trafo_pt):
197
def __init__(self, angle, x=None, y=None):
199
if x is not None or y is not None:
200
if x is None or y is None:
201
raise (UndefinedResultError,
202
"either specify both x and y or none of them")
203
vector=_rvector(angle, x, y)
205
trafo_pt.__init__(self,
206
matrix=_rmatrix(angle),
210
class rotate(trafo_pt):
211
def __init__(self, angle, x=None, y=None):
213
if x is not None or y is not None:
214
if x is None or y is None:
215
raise (UndefinedResultError,
216
"either specify both x and y or none of them")
217
vector=_rvector(angle, unit.topt(x), unit.topt(y))
219
trafo_pt.__init__(self,
220
matrix=_rmatrix(angle),
224
class scale_pt(trafo_pt):
225
def __init__(self, sx, sy=None, x=None, y=None):
228
raise (UndefinedResultError,
229
"one scaling factor is 0")
231
if x is not None or y is not None:
232
if x is None or y is None:
233
raise (UndefinedResultError,
234
"either specify both x and y or none of them")
235
vector = (1-sx)*x, (1-sy)*y
237
trafo_pt.__init__(self, matrix=((sx,0),(0,sy)), vector=vector)
241
def __init__(self, sx, sy=None, x=None, y=None):
244
raise (UndefinedResultError,
245
"one scaling factor is 0")
247
if x is not None or y is not None:
248
if x is None or y is None:
249
raise (UndefinedResultError,
250
"either specify both x and y or none of them")
251
vector = (1-sx)*x, (1-sy)*y
253
trafo.__init__(self, matrix=((sx,0),(0,sy)), vector=vector)
256
class slant_pt(trafo_pt):
257
def __init__(self, a, angle=0, x=None, y=None):
258
t = ( rotate_pt(-angle, x, y)*
259
trafo(matrix=((1, a), (0, 1)))*
260
rotate_pt(angle, x, y) )
261
trafo_pt.__init__(self, t.matrix, t.vector)
265
def __init__(self, a, angle=0, x=None, y=None):
266
t = ( rotate(-angle, x, y)*
267
trafo(matrix=((1, a), (0, 1)))*
268
rotate(angle, x, y) )
269
trafo.__init__(self, t.matrix, t.vector)
272
class translate_pt(trafo_pt):
273
def __init__(self, x, y):
274
trafo_pt.__init__(self, vector=(x, y))
277
class translate(trafo):
278
def __init__(self, x, y):
279
trafo.__init__(self, vector=(x, y))