]> Creatis software - CreaPhase.git/blob - octave_packages/geometry-1.5.0/io/private/simplepath.py
Add a useful package (from Source forge) for octave
[CreaPhase.git] / octave_packages / geometry-1.5.0 / io / private / simplepath.py
1 #!/usr/bin/env python
2 """
3 simplepath.py
4 functions for digesting paths into a simple list structure
5
6 Copyright (C) 2005 Aaron Spike, aaron@ekips.org
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21
22 """
23 import re, math
24
25 def lexPath(d):
26     """
27     returns and iterator that breaks path data 
28     identifies command and parameter tokens
29     """
30     offset = 0
31     length = len(d)
32     delim = re.compile(r'[ \t\r\n,]+')
33     command = re.compile(r'[MLHVCSQTAZmlhvcsqtaz]')
34     parameter = re.compile(r'(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)')
35     while 1:
36         m = delim.match(d, offset)
37         if m:
38             offset = m.end()
39         if offset >= length:
40             break
41         m = command.match(d, offset)
42         if m:
43             yield [d[offset:m.end()], True]
44             offset = m.end()
45             continue
46         m = parameter.match(d, offset)
47         if m:
48             yield [d[offset:m.end()], False]
49             offset = m.end()
50             continue
51         #TODO: create new exception
52         raise Exception, 'Invalid path data!'
53 '''
54 pathdefs = {commandfamily:
55     [
56     implicitnext,
57     #params,
58     [casts,cast,cast],
59     [coord type,x,y,0]
60     ]}
61 '''
62 pathdefs = {
63     'M':['L', 2, [float, float], ['x','y']], 
64     'L':['L', 2, [float, float], ['x','y']], 
65     'H':['H', 1, [float], ['x']], 
66     'V':['V', 1, [float], ['y']], 
67     'C':['C', 6, [float, float, float, float, float, float], ['x','y','x','y','x','y']], 
68     'S':['S', 4, [float, float, float, float], ['x','y','x','y']], 
69     'Q':['Q', 4, [float, float, float, float], ['x','y','x','y']], 
70     'T':['T', 2, [float, float], ['x','y']], 
71     'A':['A', 7, [float, float, float, int, int, float, float], ['r','r','a',0,'s','x','y']], 
72     'Z':['L', 0, [], []]
73     }
74 def parsePath(d):
75     """
76     Parse SVG path and return an array of segments.
77     Removes all shorthand notation.
78     Converts coordinates to absolute.
79     """
80     retval = []
81     lexer = lexPath(d)
82
83     pen = (0.0,0.0)
84     subPathStart = pen
85     lastControl = pen
86     lastCommand = ''
87     
88     while 1:
89         try:
90             token, isCommand = lexer.next()
91         except StopIteration:
92             break
93         params = []
94         needParam = True
95         if isCommand:
96             if not lastCommand and token.upper() != 'M':
97                 raise Exception, 'Invalid path, must begin with moveto.'    
98             else:                
99                 command = token
100         else:
101             #command was omited
102             #use last command's implicit next command
103             needParam = False
104             if lastCommand:
105                 if lastCommand.isupper():
106                     command = pathdefs[lastCommand][0]
107                 else:
108                     command = pathdefs[lastCommand.upper()][0].lower()
109             else:
110                 raise Exception, 'Invalid path, no initial command.'    
111         numParams = pathdefs[command.upper()][1]
112         while numParams > 0:
113             if needParam:
114                 try: 
115                     token, isCommand = lexer.next()
116                     if isCommand:
117                         raise Exception, 'Invalid number of parameters'
118                 except StopIteration:
119                     raise Exception, 'Unexpected end of path'
120             cast = pathdefs[command.upper()][2][-numParams]
121             param = cast(token)
122             if command.islower():
123                 if pathdefs[command.upper()][3][-numParams]=='x':
124                     param += pen[0]
125                 elif pathdefs[command.upper()][3][-numParams]=='y':
126                     param += pen[1]
127             params.append(param)
128             needParam = True
129             numParams -= 1
130         #segment is now absolute so
131         outputCommand = command.upper()
132     
133         #Flesh out shortcut notation    
134         if outputCommand in ('H','V'):
135             if outputCommand == 'H':
136                 params.append(pen[1])
137             if outputCommand == 'V':
138                 params.insert(0,pen[0])
139             outputCommand = 'L'
140         if outputCommand in ('S','T'):
141             params.insert(0,pen[1]+(pen[1]-lastControl[1]))
142             params.insert(0,pen[0]+(pen[0]-lastControl[0]))
143             if outputCommand == 'S':
144                 outputCommand = 'C'
145             if outputCommand == 'T':
146                 outputCommand = 'Q'
147
148         #current values become "last" values
149         if outputCommand == 'M':
150             subPathStart = tuple(params[0:2])
151             pen = subPathStart
152         if outputCommand == 'Z':
153             pen = subPathStart
154         else:
155             pen = tuple(params[-2:])
156
157         if outputCommand in ('Q','C'):
158             lastControl = tuple(params[-4:-2])
159         else:
160             lastControl = pen
161         lastCommand = command
162
163         retval.append([outputCommand,params])
164     return retval
165
166 def formatPath(a):
167     """Format SVG path data from an array"""
168     return "".join([cmd + " ".join([str(p) for p in params]) for cmd, params in a])
169
170 def translatePath(p, x, y):
171     for cmd,params in p:
172         defs = pathdefs[cmd]
173         for i in range(defs[1]):
174             if defs[3][i] == 'x':
175                 params[i] += x
176             elif defs[3][i] == 'y':
177                 params[i] += y
178
179 def scalePath(p, x, y):
180     for cmd,params in p:
181         defs = pathdefs[cmd]
182         for i in range(defs[1]):
183             if defs[3][i] == 'x':
184                 params[i] *= x
185             elif defs[3][i] == 'y':
186                 params[i] *= y
187             elif defs[3][i] == 'r':         # radius parameter
188                 params[i] *= x
189             elif defs[3][i] == 's':         # sweep-flag parameter
190                 if x*y < 0:
191                     params[i] = 1 - params[i]
192             elif defs[3][i] == 'a':         # x-axis-rotation angle
193                 if y < 0:
194                     params[i] = - params[i]
195
196 def rotatePath(p, a, cx = 0, cy = 0):
197     if a == 0:
198         return p
199     for cmd,params in p:
200         defs = pathdefs[cmd]
201         for i in range(defs[1]):
202             if defs[3][i] == 'x':
203                 x = params[i] - cx
204                 y = params[i + 1] - cy
205                 r = math.sqrt((x**2) + (y**2))
206                 if r != 0:
207                     theta = math.atan2(y, x) + a
208                     params[i] = (r * math.cos(theta)) + cx
209                     params[i + 1] = (r * math.sin(theta)) + cy
210
211
212 # vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 encoding=utf-8 textwidth=99