Convert vector images (SVG) to gcode for usage with a laser plotter.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

202 lines
6.7 KiB

#!/usr/bin/env python
functions for digesting paths into a simple list structure
Copyright (C) 2005 Aaron Spike,
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import re, math
def lexPath(d):
returns and iterator that breaks path data
identifies command and parameter tokens
offset = 0
length = len(d)
delim = re.compile(r'[ \t\r\n,]+')
command = re.compile(r'[MLHVCSQTAZmlhvcsqtaz]')
parameter = re.compile(r'(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)')
while 1:
m = delim.match(d, offset)
if m:
offset = m.end()
if offset >= length:
m = command.match(d, offset)
if m:
yield [d[offset:m.end()], True]
offset = m.end()
m = parameter.match(d, offset)
if m:
yield [d[offset:m.end()], False]
offset = m.end()
#TODO: create new exception
raise Exception('Invalid path data!')
pathdefs = {commandfamily:
[coord type,x,y,0]
pathdefs = {
'M':['L', 2, [float, float], ['x','y']],
'L':['L', 2, [float, float], ['x','y']],
'H':['H', 1, [float], ['x']],
'V':['V', 1, [float], ['y']],
'C':['C', 6, [float, float, float, float, float, float], ['x','y','x','y','x','y']],
'S':['S', 4, [float, float, float, float], ['x','y','x','y']],
'Q':['Q', 4, [float, float, float, float], ['x','y','x','y']],
'T':['T', 2, [float, float], ['x','y']],
'A':['A', 7, [float, float, float, int, int, float, float], [0,0,0,0,0,'x','y']],
'Z':['L', 0, [], []]
def parsePath(d):
Parse SVG path and return an array of segments.
Removes all shorthand notation.
Converts coordinates to absolute.
retval = []
lexer = lexPath(d)
pen = (0.0,0.0)
subPathStart = pen
lastControl = pen
lastCommand = ''
while 1:
token, isCommand = next(lexer)
except StopIteration:
params = []
needParam = True
if isCommand:
if not lastCommand and token.upper() != 'M':
raise Exception('Invalid path, must begin with moveto.')
command = token
#command was omited
#use last command's implicit next command
needParam = False
if lastCommand:
if lastCommand.isupper():
command = pathdefs[lastCommand][0]
command = pathdefs[lastCommand.upper()][0].lower()
raise Exception('Invalid path, no initial command.')
numParams = pathdefs[command.upper()][1]
while numParams > 0:
if needParam:
token, isCommand = next(lexer)
if isCommand:
raise Exception('Invalid number of parameters')
except StopIteration:
raise Exception('Unexpected end of path')
cast = pathdefs[command.upper()][2][-numParams]
param = cast(token)
if command.islower():
if pathdefs[command.upper()][3][-numParams]=='x':
param += pen[0]
elif pathdefs[command.upper()][3][-numParams]=='y':
param += pen[1]
needParam = True
numParams -= 1
#segment is now absolute so
outputCommand = command.upper()
#Flesh out shortcut notation
if outputCommand in ('H','V'):
if outputCommand == 'H':
if outputCommand == 'V':
outputCommand = 'L'
if outputCommand in ('S','T'):
if outputCommand == 'S':
outputCommand = 'C'
if outputCommand == 'T':
outputCommand = 'Q'
#current values become "last" values
if outputCommand == 'M':
subPathStart = tuple(params[0:2])
pen = subPathStart
if outputCommand == 'Z':
pen = subPathStart
pen = tuple(params[-2:])
if outputCommand in ('Q','C'):
lastControl = tuple(params[-4:-2])
lastControl = pen
lastCommand = command
return retval
def formatPath(a):
"""Format SVG path data from an array"""
return "".join([cmd + " ".join([str(p) for p in params]) for cmd, params in a])
def translatePath(p, x, y):
for cmd,params in p:
defs = pathdefs[cmd]
for i in range(defs[1]):
if defs[3][i] == 'x':
params[i] += x
elif defs[3][i] == 'y':
params[i] += y
def scalePath(p, x, y):
for cmd,params in p:
defs = pathdefs[cmd]
for i in range(defs[1]):
if defs[3][i] == 'x':
params[i] *= x
elif defs[3][i] == 'y':
params[i] *= y
def rotatePath(p, a, cx = 0, cy = 0):
if a == 0:
return p
for cmd,params in p:
defs = pathdefs[cmd]
for i in range(defs[1]):
if defs[3][i] == 'x':
x = params[i] - cx
y = params[i + 1] - cy
r = math.sqrt((x**2) + (y**2))
if r != 0:
theta = math.atan2(y, x) + a
params[i] = (r * math.cos(theta)) + cx
params[i + 1] = (r * math.sin(theta)) + cy

Du besuchst diese Seite mit einem veralteten IPv4-Internetzugang. Möglicherweise treten in Zukunft Probleme mit der Erreichbarkeit und Performance auf. Bitte frage deinen Internetanbieter oder Netzwerkadministrator nach IPv6-Unterstützung.
You are visiting this site with an outdated IPv4 internet access. You may experience problems with accessibility and performance in the future. Please ask your ISP or network administrator for IPv6 support.
Weitere Infos | More Information
Klicke zum schließen | Click to close