3 ############################### convenient functions for making paths
5 def poly(*data
, **kwds
):
6 errstring
= "Arguments are: poly((x1,y1), (x2,y2), ..., loop=False)"
11 if len(kwds
) > 0: raise TypeError, errstring
16 if output
== []: output
.append(("M", x
, y
))
17 else: output
.append(("L", x
, y
))
18 if loop
and len(data
) > 0:
21 except (TypeError, ValueError): raise TypeError, errstring
23 def bezier(*data
, **kwds
):
24 errstring
= "Arguments are: bezier((x,y,c1x,c1y,c2x,c2y), ..., loop=False)"
29 if len(kwds
) > 0: raise TypeError, errstring
33 for x
, y
, c1x
, c1y
, c2x
, c2y
in data
:
34 if output
== []: output
.append(("M", x
, y
))
36 output
.append(("C", c1x
, c1y
, c2x
, c2y
, x
, y
))
37 if loop
and len(data
) > 0:
40 except (TypeError, ValueError): raise TypeError, errstring
42 def velocity(*data
, **kwds
):
43 errstring
= "Arguments are: velocity((x,y,vx,vy), ..., loop=False)"
48 if len(kwds
) > 0: raise TypeError, errstring
52 indexes
= range(len(data
))
53 if loop
and len(data
) > 0: indexes
.append(0)
56 if output
== []: output
.append(("M", data
[i
][0], data
[i
][1]))
58 inext
= (i
+1) % len(data
)
59 iprev
= (i
-1) % len(data
)
61 x
, y
= data
[i
][0], data
[i
][1]
62 c1x
, c1y
= data
[iprev
][2]/3. + data
[iprev
][0], data
[iprev
][3]/3. + data
[iprev
][1]
63 c2x
, c2y
= data
[i
][2]/-3. + x
, data
[i
][3]/-3. + y
65 output
.append(("C", c1x
, c1y
, c2x
, c2y
, x
, y
))
67 if loop
and len(data
) > 0:
70 except (TypeError, ValueError): raise TypeError, errstring
72 def foreback(*data
, **kwds
):
73 errstring
= "Arguments are: foreback((x,y,vfx,vfy,vbx,vby), ..., loop=False)"
78 if len(kwds
) > 0: raise TypeError, errstring
82 indexes
= range(len(data
))
83 if loop
and len(data
) > 0: indexes
.append(0)
86 if output
== []: output
.append(("M", data
[i
][0], data
[i
][1]))
88 inext
= (i
+1) % len(data
)
89 iprev
= (i
-1) % len(data
)
91 x
, y
= data
[i
][0], data
[i
][1]
92 c1x
, c1y
= data
[iprev
][4]/3. + data
[iprev
][0], data
[iprev
][5]/3. + data
[iprev
][1]
93 c2x
, c2y
= data
[i
][2]/-3. + x
, data
[i
][3]/-3. + y
95 output
.append(("C", c1x
, c1y
, c2x
, c2y
, x
, y
))
97 if loop
and len(data
) > 0:
100 except (TypeError, ValueError): raise TypeError, errstring
102 def smooth(*data
, **kwds
):
103 errstring
= "Arguments are: smooth((x1,y1), (x2,y2), ..., loop=False)"
109 if len(kwds
) > 0: raise TypeError, errstring
113 vx
, vy
= [0.]*len(data
), [0.]*len(data
)
114 for i
in xrange(len(data
)):
115 inext
= (i
+1) % len(data
)
116 iprev
= (i
-1) % len(data
)
118 vx
[i
] = (x
[inext
] - x
[iprev
])/2.
119 vy
[i
] = (y
[inext
] - y
[iprev
])/2.
120 if not loop
and (i
== 0 or i
== len(data
)-1):
121 vx
[i
], vy
[i
] = 0., 0.
123 return velocity(zip(x
, y
, vx
, vy
), loop
)
124 except (TypeError, ValueError): raise TypeError, errstring
126 ############################### pathdata parsers
128 def parse_whitespace(index
, pathdata
):
129 while index
< len(pathdata
) and pathdata
[index
] in (" ", "\t", "\r", "\n", ","): index
+= 1
130 return index
, pathdata
132 def parse_command(index
, pathdata
):
133 index
, pathdata
= parse_whitespace(index
, pathdata
)
135 if index
>= len(pathdata
): return None, index
, pathdata
136 command
= pathdata
[index
]
137 if "A" <= command
<= "Z" or "a" <= command
<= "z":
139 return command
, index
, pathdata
141 return None, index
, pathdata
143 def parse_number(index
, pathdata
):
144 index
, pathdata
= parse_whitespace(index
, pathdata
)
146 if index
>= len(pathdata
): return None, index
, pathdata
147 first_digit
= pathdata
[index
]
149 if "0" <= first_digit
<= "9" or first_digit
in ("-", "+", "."):
151 while index
< len(pathdata
) and ("0" <= pathdata
[index
] <= "9" or pathdata
[index
] in ("-", "+", ".", "e", "E")):
156 return float(pathdata
[start
:end
]), index
, pathdata
158 return None, index
, pathdata
160 def parse_boolean(index
, pathdata
):
161 index
, pathdata
= parse_whitespace(index
, pathdata
)
163 if index
>= len(pathdata
): return None, index
, pathdata
164 first_digit
= pathdata
[index
]
166 if first_digit
in ("0", "1"):
168 return int(first_digit
), index
, pathdata
170 return None, index
, pathdata
172 ############################### main parsing function (keeps defaults from getting messy)
175 if isinstance(pathdata
, (list, tuple)): return pathdata
180 command
, index
, pathdata
= parse_command(index
, pathdata
)
181 index
, pathdata
= parse_whitespace(index
, pathdata
)
183 if command
== None and index
== len(pathdata
): break # this is the normal way out of the loop
184 if command
in ("Z", "z"):
185 output
.append((command
,))
187 ######################
188 elif command
in ("H", "h", "V", "v"):
189 errstring
= "Pathdata command \"%s\" requires a number at index %d" % (command
, index
)
190 num1
, index
, pathdata
= parse_number(index
, pathdata
)
191 if num1
== None: raise ValueError, errstring
194 output
.append((command
, num1
))
195 num1
, index
, pathdata
= parse_number(index
, pathdata
)
197 ######################
198 elif command
in ("M", "m", "L", "l", "T", "t"):
199 errstring
= "Pathdata command \"%s\" requires an x,y pair at index %d" % (command
, index
)
200 num1
, index
, pathdata
= parse_number(index
, pathdata
)
201 num2
, index
, pathdata
= parse_number(index
, pathdata
)
203 if num1
== None: raise ValueError, errstring
206 if num2
== None: raise ValueError, errstring
207 output
.append((command
, num1
, num2
))
209 num1
, index
, pathdata
= parse_number(index
, pathdata
)
210 num2
, index
, pathdata
= parse_number(index
, pathdata
)
212 ######################
213 elif command
in ("S", "s", "Q", "q"):
214 errstring
= "Pathdata command \"%s\" requires a cx,cy,x,y quadruplet at index %d" % (command
, index
)
215 num1
, index
, pathdata
= parse_number(index
, pathdata
)
216 num2
, index
, pathdata
= parse_number(index
, pathdata
)
217 num3
, index
, pathdata
= parse_number(index
, pathdata
)
218 num4
, index
, pathdata
= parse_number(index
, pathdata
)
220 if num1
== None: raise ValueError, errstring
223 if num2
== None or num3
== None or num4
== None: raise ValueError, errstring
224 output
.append((command
, num1
, num2
, num3
, num4
))
226 num1
, index
, pathdata
= parse_number(index
, pathdata
)
227 num2
, index
, pathdata
= parse_number(index
, pathdata
)
228 num3
, index
, pathdata
= parse_number(index
, pathdata
)
229 num4
, index
, pathdata
= parse_number(index
, pathdata
)
231 ######################
232 elif command
in ("C", "c"):
233 errstring
= "Pathdata command \"%s\" requires a c1x,c1y,c2x,c2y,x,y sextuplet at index %d" % (command
, index
)
234 num1
, index
, pathdata
= parse_number(index
, pathdata
)
235 num2
, index
, pathdata
= parse_number(index
, pathdata
)
236 num3
, index
, pathdata
= parse_number(index
, pathdata
)
237 num4
, index
, pathdata
= parse_number(index
, pathdata
)
238 num5
, index
, pathdata
= parse_number(index
, pathdata
)
239 num6
, index
, pathdata
= parse_number(index
, pathdata
)
241 if num1
== None: raise ValueError, errstring
244 if num2
== None or num3
== None or num4
== None or num5
== None or num6
== None: raise ValueError, errstring
246 output
.append((command
, num1
, num2
, num3
, num4
, num5
, num6
))
248 num1
, index
, pathdata
= parse_number(index
, pathdata
)
249 num2
, index
, pathdata
= parse_number(index
, pathdata
)
250 num3
, index
, pathdata
= parse_number(index
, pathdata
)
251 num4
, index
, pathdata
= parse_number(index
, pathdata
)
252 num5
, index
, pathdata
= parse_number(index
, pathdata
)
253 num6
, index
, pathdata
= parse_number(index
, pathdata
)
255 ######################
256 elif command
in ("A", "a"):
257 errstring
= "Pathdata command \"%s\" requires a rx,ry,angle,large-arc-flag,sweep-flag,x,y septuplet at index %d" % (command
, index
)
258 num1
, index
, pathdata
= parse_number(index
, pathdata
)
259 num2
, index
, pathdata
= parse_number(index
, pathdata
)
260 num3
, index
, pathdata
= parse_number(index
, pathdata
)
261 num4
, index
, pathdata
= parse_boolean(index
, pathdata
)
262 num5
, index
, pathdata
= parse_boolean(index
, pathdata
)
263 num6
, index
, pathdata
= parse_number(index
, pathdata
)
264 num7
, index
, pathdata
= parse_number(index
, pathdata
)
266 if num1
== None: raise ValueError, errstring
269 if num2
== None or num3
== None or num4
== None or num5
== None or num6
== None or num7
== None: raise ValueError, errstring
271 output
.append((command
, num1
, num2
, num3
, num4
, num5
, num6
, num7
))
273 num1
, index
, pathdata
= parse_number(index
, pathdata
)
274 num2
, index
, pathdata
= parse_number(index
, pathdata
)
275 num3
, index
, pathdata
= parse_number(index
, pathdata
)
276 num4
, index
, pathdata
= parse_boolean(index
, pathdata
)
277 num5
, index
, pathdata
= parse_boolean(index
, pathdata
)
278 num6
, index
, pathdata
= parse_number(index
, pathdata
)
279 num7
, index
, pathdata
= parse_number(index
, pathdata
)
283 ############################### transformation function (keeps defaults from getting messy)
285 def transform(func
, pathdata
):
286 x
, y
, X
, Y
= None, None, None, None
288 for datum
in pathdata
:
289 if not isinstance(datum
, (tuple, list)):
290 raise TypeError, "Pathdata elements must be lists/tuples"
295 ######################
296 if command
in ("Z", "z"):
297 x
, y
, X
, Y
= None, None, None, None
298 output
.append(("Z",))
300 ######################
301 elif command
in ("H", "h", "V", "v"):
304 if command
== "H" or (command
== "h" and x
== None): x
= num1
305 elif command
== "h": x
+= num1
306 elif command
== "V" or (command
== "v" and y
== None): y
= num1
307 elif command
== "v": y
+= num1
310 output
.append(("L", X
, Y
))
312 ######################
313 elif command
in ("M", "m", "L", "l", "T", "t"):
316 if command
.isupper() or x
== None or y
== None:
323 output
.append((command
.capitalize(), X
, Y
))
325 ######################
326 elif command
in ("S", "s", "Q", "q"):
327 num1
, num2
, num3
, num4
= args
329 if command
.isupper() or x
== None or y
== None:
335 if command
.isupper() or x
== None or y
== None:
341 CX
, CY
= func(cx
, cy
)
343 output
.append((command
.capitalize(), CX
, CY
, X
, Y
))
345 ######################
346 elif command
in ("C", "c"):
347 num1
, num2
, num3
, num4
, num5
, num6
= args
349 if command
.isupper() or x
== None or y
== None:
350 c1x
, c1y
= num1
, num2
355 if command
.isupper() or x
== None or y
== None:
356 c2x
, c2y
= num3
, num4
361 if command
.isupper() or x
== None or y
== None:
367 C1X
, C1Y
= func(c1x
, c1y
)
368 C2X
, C2Y
= func(c2x
, c2y
)
370 output
.append((command
.capitalize(), C1X
, C1Y
, C2X
, C2Y
, X
, Y
))
372 ######################
373 elif command
in ("A", "a"):
374 num1
, num2
, angle
, large_arc_flag
, sweep_flag
, num3
, num4
= args
379 if command
.isupper() or x
== None or y
== None:
386 if x
!= None and y
!= None:
387 centerx
, centery
= (x
+ oldx
)/2., (y
+ oldy
)/2.
388 CENTERX
, CENTERY
= (X
+ OLDX
)/2., (Y
+ OLDY
)/2.
392 RX
, RY
= func(rx
, ry
)
394 output
.append((command
.capitalize(), RX
- CENTERX
, RY
- CENTERY
, angle
, large_arc_flag
, sweep_flag
, X
, Y
))
398 ############################### bbox function (keeps defaults from getting messy)
402 output
= defaults
.BBox(None, None, None, None)
404 for datum
in pathdata
:
405 if not isinstance(datum
, (tuple, list)):
406 raise TypeError, "Pathdata elements must be lists/tuples"
411 ######################
412 if command
in ("Z", "z"): pass
414 ######################
415 elif command
in ("H", "h", "V", "v"):
418 if command
== "H" or (command
== "h" and x
== None): x
= num1
419 elif command
== "h": x
+= num1
420 elif command
== "V" or (command
== "v" and y
== None): y
= num1
421 elif command
== "v": y
+= num1
425 ######################
426 elif command
in ("M", "m", "L", "l", "T", "t"):
429 if command
.isupper() or x
== None or y
== None:
437 ######################
438 elif command
in ("S", "s", "Q", "q"):
439 num1
, num2
, num3
, num4
= args
441 if command
.isupper() or x
== None or y
== None:
447 if command
.isupper() or x
== None or y
== None:
455 ######################
456 elif command
in ("C", "c"):
457 num1
, num2
, num3
, num4
, num5
, num6
= args
459 if command
.isupper() or x
== None or y
== None:
460 c1x
, c1y
= num1
, num2
465 if command
.isupper() or x
== None or y
== None:
466 c2x
, c2y
= num3
, num4
471 if command
.isupper() or x
== None or y
== None:
479 ######################
480 elif command
in ("A", "a"):
481 num1
, num2
, angle
, large_arc_flag
, sweep_flag
, num3
, num4
= args
486 if command
.isupper() or x
== None or y
== None:
492 if x
!= None and y
!= None:
493 centerx
, centery
= (x
+ oldx
)/2., (y
+ oldy
)/2.
494 CENTERX
, CENTERY
= (X
+ OLDX
)/2., (Y
+ OLDY
)/2.