add support for virtual fonts in virtual fonts
[PyX.git] / pyx / trafo.py
blobfff83490d3293fb22293550d5f61ab61ac9f0699
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
23 import math
24 from . import attr, baseclasses, unit
26 # some helper routines
28 def _rmatrix(angle):
29 phi = math.pi*angle/180.0
31 return ((math.cos(phi), -math.sin(phi)),
32 (math.sin(phi), math.cos(phi)))
34 def _rvector(angle, x, y):
35 phi = math.pi*angle/180.0
37 return ((1-math.cos(phi))*x + math.sin(phi) *y,
38 -math.sin(phi) *x + (1-math.cos(phi))*y)
41 def _mmatrix(angle):
42 phi = math.pi*angle/180.0
44 return ( (math.cos(phi)*math.cos(phi)-math.sin(phi)*math.sin(phi),
45 -2*math.sin(phi)*math.cos(phi) ),
46 (-2*math.sin(phi)*math.cos(phi),
47 math.sin(phi)*math.sin(phi)-math.cos(phi)*math.cos(phi) ) )
50 # trafo: affine transformations
52 class trafo_pt(baseclasses.deformer):
54 """affine transformation (coordinates in constructor in pts)
56 Note that though the coordinates in the constructor are in
57 pts (which is useful for internal purposes), all other
58 methods only accept units in the standard user notation.
60 """
62 def __init__(self, matrix=((1, 0), (0, 1)), vector=(0, 0)):
63 """Return trafo with given transformation matrix and vector.
64 """
65 self.matrix = matrix
66 self.vector = vector
68 def __mul__(self, other):
69 if isinstance(other, trafo_pt):
70 matrix = ( ( self.matrix[0][0]*other.matrix[0][0] +
71 self.matrix[0][1]*other.matrix[1][0],
72 self.matrix[0][0]*other.matrix[0][1] +
73 self.matrix[0][1]*other.matrix[1][1] ),
74 ( self.matrix[1][0]*other.matrix[0][0] +
75 self.matrix[1][1]*other.matrix[1][0],
76 self.matrix[1][0]*other.matrix[0][1] +
77 self.matrix[1][1]*other.matrix[1][1] )
80 vector = ( self.matrix[0][0]*other.vector[0] +
81 self.matrix[0][1]*other.vector[1] +
82 self.vector[0],
83 self.matrix[1][0]*other.vector[0] +
84 self.matrix[1][1]*other.vector[1] +
85 self.vector[1] )
87 return trafo_pt(matrix=matrix, vector=vector)
88 else:
89 raise NotImplementedError("can only multiply two transformations")
91 def __str__(self):
92 return "[%f %f %f %f %f %f]" % \
93 ( self.matrix[0][0], self.matrix[1][0],
94 self.matrix[0][1], self.matrix[1][1],
95 self.vector[0], self.vector[1] )
97 def processPS(self, file, writer, context, registry):
98 file.write("[%f %f %f %f %f %f] concat\n" % \
99 ( self.matrix[0][0], self.matrix[1][0],
100 self.matrix[0][1], self.matrix[1][1],
101 self.vector[0], self.vector[1] ) )
103 def processPDF(self, file, writer, context, registry):
104 file.write("%f %f %f %f %f %f cm\n" % \
105 ( self.matrix[0][0], self.matrix[1][0],
106 self.matrix[0][1], self.matrix[1][1],
107 self.vector[0], self.vector[1] ) )
109 def processSVGattrs(self, attrs, writer, context, registry):
110 assert "transform" not in attrs
111 attrs["transform"] = "matrix(%f,%f,%f,%f,%f,%f)" % \
112 ( self.matrix[0][0], -self.matrix[1][0],
113 -self.matrix[0][1], self.matrix[1][1],
114 self.vector[0], -self.vector[1] )
116 def apply_pt(self, x_pt, y_pt):
117 """apply transformation to point (x_pt, y_pt) in pts"""
118 return ( self.matrix[0][0]*x_pt + self.matrix[0][1]*y_pt + self.vector[0],
119 self.matrix[1][0]*x_pt + self.matrix[1][1]*y_pt + self.vector[1] )
121 def apply(self, x, y):
122 # for the transformation we have to convert to points
123 tx, ty = self.apply_pt(unit.topt(x), unit.topt(y))
124 return tx * unit.t_pt, ty * unit.t_pt
126 def deform(self, path):
127 return path.transformed(self)
129 def inverse(self):
130 det = 1.0*(self.matrix[0][0]*self.matrix[1][1] - self.matrix[0][1]*self.matrix[1][0])
131 matrix = ( ( self.matrix[1][1]/det, -self.matrix[0][1]/det),
132 (-self.matrix[1][0]/det, self.matrix[0][0]/det) )
133 return ( trafo_pt(matrix=matrix) *
134 trafo_pt(vector=(-self.vector[0], -self.vector[1])) )
136 def mirrored(self, angle):
137 return mirror(angle) * self
139 def rotated_pt(self, angle, x=None, y=None):
140 return rotate_pt(angle, x, y) * self
142 def rotated(self, angle, x=None, y=None):
143 return rotate(angle, x, y) * self
145 def scaled_pt(self, sx, sy=None, x=None, y=None):
146 return scale_pt(sx, sy, x, y) * self
148 def scaled(self, sx, sy=None, x=None, y=None):
149 return scale(sx, sy, x, y) * self
151 def slanted_pt(self, a, angle=0, x=None, y=None):
152 return slant_pt(a, angle, x, y) * self
154 def slanted(self, a, angle=0, x=None, y=None):
155 return slant(a, angle, x, y) * self
157 def translated_pt(self, x, y):
158 return translate_pt(x, y) * self
160 def translated(self, x, y):
161 return translate(x, y) * self
164 class trafo(trafo_pt):
166 """affine transformation"""
168 def __init__(self, matrix=((1,0), (0,1)), vector=(0, 0)):
169 trafo_pt.__init__(self,
170 matrix, (unit.topt(vector[0]), unit.topt(vector[1])))
173 # some standard transformations
176 identity = trafo()
178 class mirror(trafo):
179 def __init__(self, angle=0):
180 trafo.__init__(self, matrix=_mmatrix(angle))
183 class rotate_pt(trafo_pt):
184 def __init__(self, angle, x=None, y=None):
185 vector = 0, 0
186 if x is not None or y is not None:
187 if x is None or y is None:
188 raise TrafoException("either specify both x and y or none of them")
189 vector=_rvector(angle, x, y)
191 trafo_pt.__init__(self, matrix=_rmatrix(angle), vector=vector)
194 class rotate(trafo_pt):
195 def __init__(self, angle, x=None, y=None):
196 vector = 0, 0
197 if x is not None or y is not None:
198 if x is None or y is None:
199 raise TrafoException("either specify both x and y or none of them")
200 vector=_rvector(angle, unit.topt(x), unit.topt(y))
202 trafo_pt.__init__(self, matrix=_rmatrix(angle), vector=vector)
205 class scale_pt(trafo_pt):
206 def __init__(self, sx, sy=None, x=None, y=None):
207 if sy is None:
208 sy = sx
209 vector = 0, 0
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 = (1-sx)*x, (1-sy)*y
214 trafo_pt.__init__(self, matrix=((sx, 0), (0, sy)), vector=vector)
217 class scale(trafo):
218 def __init__(self, sx, sy=None, x=None, y=None):
219 if sy is None:
220 sy = sx
221 vector = 0, 0
222 if x is not None or y is not None:
223 if x is None or y is None:
224 raise TrafoException("either specify both x and y or none of them")
225 vector = (1-sx)*x, (1-sy)*y
226 trafo.__init__(self, matrix=((sx, 0), (0, sy)), vector=vector)
229 class slant_pt(trafo_pt):
230 def __init__(self, a, angle=0, x=None, y=None):
231 t = ( rotate_pt(-angle, x, y) *
232 trafo(matrix=((1, a), (0, 1))) *
233 rotate_pt(angle, x, y) )
234 trafo_pt.__init__(self, t.matrix, t.vector)
237 class slant(trafo):
238 def __init__(self, a, angle=0, x=None, y=None):
239 t = ( rotate(-angle, x, y) *
240 trafo(matrix=((1, a), (0, 1))) *
241 rotate(angle, x, y) )
242 trafo.__init__(self, t.matrix, t.vector)
245 class translate_pt(trafo_pt):
246 def __init__(self, x, y):
247 trafo_pt.__init__(self, vector=(x, y))
250 class translate(trafo):
251 def __init__(self, x, y):
252 trafo.__init__(self, vector=(x, y))