~ubuntu-branches/ubuntu/trusty/pyx/trusty

« back to all changes in this revision

Viewing changes to pyx/trafo.py

  • Committer: Bazaar Package Importer
  • Author(s): Graham Wilson
  • Date: 2004-12-25 06:42:57 UTC
  • Revision ID: james.westby@ubuntu.com-20041225064257-31469ij5uysqq302
Tags: upstream-0.7.1
Import upstream version 0.7.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
# -*- coding: ISO-8859-1 -*-
 
3
#
 
4
#
 
5
# Copyright (C) 2002-2004 J�rg Lehmann <joergl@users.sourceforge.net>
 
6
# Copyright (C) 2002-2004 Andr� Wobst <wobsta@users.sourceforge.net>
 
7
#
 
8
# This file is part of PyX (http://pyx.sourceforge.net/).
 
9
#
 
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.
 
14
#
 
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.
 
19
#
 
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
 
23
 
 
24
import math
 
25
import attr, base, deformer, unit
 
26
 
 
27
# some helper routines
 
28
 
 
29
def _rmatrix(angle):
 
30
    phi = math.pi*angle/180.0
 
31
 
 
32
    return  ((math.cos(phi), -math.sin(phi)), 
 
33
             (math.sin(phi),  math.cos(phi)))
 
34
 
 
35
def _rvector(angle, x, y):
 
36
    phi = math.pi*angle/180.0
 
37
 
 
38
    return  ((1-math.cos(phi))*x + math.sin(phi)    *y,
 
39
              -math.sin(phi)   *x + (1-math.cos(phi))*y)
 
40
 
 
41
 
 
42
def _mmatrix(angle):
 
43
    phi = math.pi*angle/180.0
 
44
 
 
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) ) )
 
49
 
 
50
def _det(matrix):
 
51
    return matrix[0][0]*matrix[1][1] - matrix[0][1]*matrix[1][0]
 
52
 
 
53
# Exception
 
54
 
 
55
class UndefinedResultError(ArithmeticError):
 
56
    pass
 
57
 
 
58
# trafo: affine transformations
 
59
 
 
60
class trafo_pt(base.canvasitem, deformer.deformer):
 
61
 
 
62
    """affine transformation (coordinates in constructor in pts)
 
63
 
 
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.
 
67
 
 
68
    """
 
69
 
 
70
    def __init__(self, matrix=((1,0),(0,1)), vector=(0,0)):
 
71
        if _det(matrix)==0:
 
72
            raise UndefinedResultError, "transformation matrix must not be singular" 
 
73
        else:
 
74
            self.matrix=matrix
 
75
        self.vector = vector
 
76
 
 
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] )
 
87
                     )
 
88
 
 
89
            vector = ( self.matrix[0][0]*other.vector[0] +
 
90
                       self.matrix[0][1]*other.vector[1] +
 
91
                       self.vector[0],
 
92
                       self.matrix[1][0]*other.vector[0] +
 
93
                       self.matrix[1][1]*other.vector[1] +
 
94
                       self.vector[1] )
 
95
 
 
96
            return trafo_pt(matrix=matrix, vector=vector)
 
97
        else:
 
98
            raise NotImplementedError, "can only multiply two transformations"
 
99
 
 
100
    def __str__(self):
 
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] )
 
105
 
 
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] ) )
 
111
 
 
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] ) )
 
117
 
 
118
    def bbox(self):
 
119
        return None
 
120
 
 
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 +
 
125
                self.vector[0],
 
126
                self.matrix[1][0]*x +
 
127
                self.matrix[1][1]*y +
 
128
                self.vector[1])
 
129
 
 
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
 
134
 
 
135
    def deform(self, path):
 
136
        return path.transformed(self)
 
137
 
 
138
    def inverse(self):
 
139
        det = _det(self.matrix)                       # shouldn't be zero, but
 
140
        try:
 
141
          matrix = ( ( self.matrix[1][1]/det, -self.matrix[0][1]/det),
 
142
                     (-self.matrix[1][0]/det,  self.matrix[0][0]/det)
 
143
                   )
 
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]))
 
148
 
 
149
    def mirrored(self, angle):
 
150
        return mirror(angle)*self
 
151
 
 
152
    def rotated_pt(self, angle, x=None, y=None):
 
153
        return rotate_pt(angle, x, y)*self
 
154
 
 
155
    def rotated(self, angle, x=None, y=None):
 
156
        return rotate(angle, x, y)*self
 
157
 
 
158
    def scaled_pt(self, sx, sy=None, x=None, y=None):
 
159
        return scale_pt(sx, sy, x, y)*self
 
160
 
 
161
    def scaled(self, sx, sy=None, x=None, y=None):
 
162
        return scale(sx, sy, x, y)*self
 
163
 
 
164
    def slanted_pt(self, a, angle=0, x=None, y=None):
 
165
        return slant_pt(a, angle, x, y)*self
 
166
 
 
167
    def slanted(self, a, angle=0, x=None, y=None):
 
168
        return slant(a, angle, x, y)*self
 
169
 
 
170
    def translated_pt(self, x, y):
 
171
        return translate_pt(x,y)*self
 
172
 
 
173
    def translated(self, x, y):
 
174
        return translate(x, y)*self
 
175
 
 
176
 
 
177
class trafo(trafo_pt):
 
178
 
 
179
    """affine transformation"""
 
180
 
 
181
    def __init__(self, matrix=((1,0),(0,1)), vector=(0,0)):
 
182
        trafo_pt.__init__(self,
 
183
                        matrix,
 
184
                        (unit.topt(vector[0]), unit.topt(vector[1])))
 
185
 
 
186
 
 
187
#
 
188
# some standard transformations 
 
189
#
 
190
 
 
191
class mirror(trafo):
 
192
    def __init__(self,angle=0):
 
193
        trafo.__init__(self, matrix=_mmatrix(angle))
 
194
 
 
195
 
 
196
class rotate_pt(trafo_pt):
 
197
    def __init__(self, angle, x=None, y=None):
 
198
        vector = 0, 0
 
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)
 
204
 
 
205
        trafo_pt.__init__(self,
 
206
                       matrix=_rmatrix(angle),
 
207
                       vector=vector)
 
208
 
 
209
 
 
210
class rotate(trafo_pt):
 
211
    def __init__(self, angle, x=None, y=None):
 
212
        vector = 0, 0 
 
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))
 
218
 
 
219
        trafo_pt.__init__(self,
 
220
                       matrix=_rmatrix(angle),
 
221
                       vector=vector)
 
222
 
 
223
 
 
224
class scale_pt(trafo_pt):
 
225
    def __init__(self, sx, sy=None, x=None, y=None):
 
226
        sy = sy or sx
 
227
        if not sx or not sy:
 
228
            raise (UndefinedResultError, 
 
229
                   "one scaling factor is 0")
 
230
        vector = 0, 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
 
236
 
 
237
        trafo_pt.__init__(self, matrix=((sx,0),(0,sy)), vector=vector)
 
238
 
 
239
 
 
240
class scale(trafo):
 
241
    def __init__(self, sx, sy=None, x=None, y=None):
 
242
        sy = sy or sx
 
243
        if not sx or not sy:
 
244
            raise (UndefinedResultError, 
 
245
                   "one scaling factor is 0")
 
246
        vector = 0, 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
 
252
 
 
253
        trafo.__init__(self, matrix=((sx,0),(0,sy)), vector=vector)
 
254
 
 
255
 
 
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)
 
262
 
 
263
 
 
264
class slant(trafo):
 
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)
 
270
 
 
271
 
 
272
class translate_pt(trafo_pt):
 
273
    def __init__(self, x, y):
 
274
        trafo_pt.__init__(self, vector=(x, y))
 
275
 
 
276
 
 
277
class translate(trafo):
 
278
    def __init__(self, x, y):
 
279
        trafo.__init__(self, vector=(x, y))