Compare commits
30
Commits
rofl
...
plastibot_laser
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
124c379447 | ||
|
|
728a4ca63d | ||
|
|
cb7bf5328d | ||
|
|
cae5ccef3a | ||
|
|
c0b6e032d9 | ||
|
|
e9ccf106a3 | ||
|
|
b47371f04b | ||
|
|
cb3b71f389 | ||
|
|
0c4101fbf3 | ||
|
|
dcbc3c75d8 | ||
|
|
6d09b0d724 | ||
|
|
2d5e7d3926 | ||
|
|
42e692df58 | ||
|
|
792c98d03d | ||
|
|
1d90a378bf | ||
|
|
e308bcf80c | ||
|
|
f5da12b158 | ||
|
|
25cadfa1fa | ||
|
|
516f4701e2 | ||
|
|
8f7d9886eb | ||
|
|
644f512e4d | ||
|
|
825ec8bbfe | ||
|
|
8aed5dca6a | ||
|
|
f43b3e5234 | ||
|
|
7e8a4943b9 | ||
|
|
9eb93d547d | ||
|
|
357c8a9df9 | ||
|
|
008d748988 | ||
|
|
291c3a7f57 | ||
|
|
1632c17d22 |
No files matched your search
@@ -1,7 +1,24 @@
|
||||
A fast svg to gcode compiler.
|
||||
An Inkscape extension to convert a SVG drawing to G-code for reprap machines. This plugin has been designed to work with Plastibot laser cutter. The gcode can be
|
||||
generated for two modes __vector__ and __raster__ laser scan.
|
||||
|
||||
cat svgfile | python svg2gcode.py
|
||||
Installation
|
||||
------------
|
||||
Copy the __svg2gcode.py__, __svg2gcode.inx__ and __shapes.py__ to Inkscape __extensions__ directory
|
||||
|
||||
The compiler is based on the eggbot project and it basically converts all of the SVG shapes into bezier curves. The bezier curves are then recursively sub divided until desired smoothness is achieved. The sub curves are then approximated as lines which are then converted into g-code.
|
||||
* on Windows : "C:\Program Files\Inkscape\share\extensions"
|
||||
* on Linux: "/usr/share/inkscape/extensions"
|
||||
* on OS X: "/Applications/Inkscape.app/Contents/Resources/extensions"
|
||||
|
||||
Some of the characteristics of the compiler can be changed by editing the config.py
|
||||
Copy the __plastibot_laser.svg__ to Inkscape __templates__ directory
|
||||
* on Windows : "C:\Program Files\Inkscape\share\template"
|
||||
* on Linux: "/usr/share/inkscape/template"
|
||||
* on OS X: "/Applications/Inkscape.app/Contents/Resources/template"
|
||||
|
||||
Parameters
|
||||
----------
|
||||
The size of the bed is controlled via the __bed-width__ and __bed-height__ parameters in __svg2gcode_*.inx__ file. Please note that the size of the SVG image should match with the size of the printer/plotter bed. This can be achived using the __File -> New -> plastibot_laser__ type when creating a new SVG in inkscape.
|
||||
|
||||
Output
|
||||
------
|
||||
|
||||
By default the gcode is stored in a file called __vector.gcode__ (for vector laser) or __raster.gcode__ (for raster laser) in your home directory. The file name can be changed using the options provided in the extension pop-up.
|
||||
Whitespace-only changes.
-274
@@ -1,274 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
'''
|
||||
Copyright (C) 2010 Nick Drobchenko, nick@cnc-club.ru
|
||||
Copyright (C) 2005 Aaron Spike, aaron@ekips.org
|
||||
|
||||
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
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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 math, cmath
|
||||
|
||||
def rootWrapper(a,b,c,d):
|
||||
if a:
|
||||
# Monics formula see http://en.wikipedia.org/wiki/Cubic_function#Monic_formula_of_roots
|
||||
a,b,c = (b/a, c/a, d/a)
|
||||
m = 2.0*a**3 - 9.0*a*b + 27.0*c
|
||||
k = a**2 - 3.0*b
|
||||
n = m**2 - 4.0*k**3
|
||||
w1 = -.5 + .5*cmath.sqrt(-3.0)
|
||||
w2 = -.5 - .5*cmath.sqrt(-3.0)
|
||||
if n < 0:
|
||||
m1 = pow(complex((m+cmath.sqrt(n))/2),1./3)
|
||||
n1 = pow(complex((m-cmath.sqrt(n))/2),1./3)
|
||||
else:
|
||||
if m+math.sqrt(n) < 0:
|
||||
m1 = -pow(-(m+math.sqrt(n))/2,1./3)
|
||||
else:
|
||||
m1 = pow((m+math.sqrt(n))/2,1./3)
|
||||
if m-math.sqrt(n) < 0:
|
||||
n1 = -pow(-(m-math.sqrt(n))/2,1./3)
|
||||
else:
|
||||
n1 = pow((m-math.sqrt(n))/2,1./3)
|
||||
x1 = -1./3 * (a + m1 + n1)
|
||||
x2 = -1./3 * (a + w1*m1 + w2*n1)
|
||||
x3 = -1./3 * (a + w2*m1 + w1*n1)
|
||||
return (x1,x2,x3)
|
||||
elif b:
|
||||
det=c**2.0-4.0*b*d
|
||||
if det:
|
||||
return (-c+cmath.sqrt(det))/(2.0*b),(-c-cmath.sqrt(det))/(2.0*b)
|
||||
else:
|
||||
return -c/(2.0*b),
|
||||
elif c:
|
||||
return 1.0*(-d/c),
|
||||
return ()
|
||||
|
||||
def bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3))):
|
||||
#parametric bezier
|
||||
x0=bx0
|
||||
y0=by0
|
||||
cx=3*(bx1-x0)
|
||||
bx=3*(bx2-bx1)-cx
|
||||
ax=bx3-x0-cx-bx
|
||||
cy=3*(by1-y0)
|
||||
by=3*(by2-by1)-cy
|
||||
ay=by3-y0-cy-by
|
||||
|
||||
return ax,ay,bx,by,cx,cy,x0,y0
|
||||
#ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
|
||||
|
||||
def linebezierintersect(((lx1,ly1),(lx2,ly2)),((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3))):
|
||||
#parametric line
|
||||
dd=lx1
|
||||
cc=lx2-lx1
|
||||
bb=ly1
|
||||
aa=ly2-ly1
|
||||
|
||||
if aa:
|
||||
coef1=cc/aa
|
||||
coef2=1
|
||||
else:
|
||||
coef1=1
|
||||
coef2=aa/cc
|
||||
|
||||
ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
|
||||
#cubic intersection coefficients
|
||||
a=coef1*ay-coef2*ax
|
||||
b=coef1*by-coef2*bx
|
||||
c=coef1*cy-coef2*cx
|
||||
d=coef1*(y0-bb)-coef2*(x0-dd)
|
||||
|
||||
roots = rootWrapper(a,b,c,d)
|
||||
retval = []
|
||||
for i in roots:
|
||||
if type(i) is complex and i.imag==0:
|
||||
i = i.real
|
||||
if type(i) is not complex and 0<=i<=1:
|
||||
retval.append(bezierpointatt(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)),i))
|
||||
return retval
|
||||
|
||||
def bezierpointatt(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)),t):
|
||||
ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
|
||||
x=ax*(t**3)+bx*(t**2)+cx*t+x0
|
||||
y=ay*(t**3)+by*(t**2)+cy*t+y0
|
||||
return x,y
|
||||
|
||||
def bezierslopeatt(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)),t):
|
||||
ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
|
||||
dx=3*ax*(t**2)+2*bx*t+cx
|
||||
dy=3*ay*(t**2)+2*by*t+cy
|
||||
return dx,dy
|
||||
|
||||
def beziertatslope(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)),(dy,dx)):
|
||||
ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
|
||||
#quadratic coefficents of slope formula
|
||||
if dx:
|
||||
slope = 1.0*(dy/dx)
|
||||
a=3*ay-3*ax*slope
|
||||
b=2*by-2*bx*slope
|
||||
c=cy-cx*slope
|
||||
elif dy:
|
||||
slope = 1.0*(dx/dy)
|
||||
a=3*ax-3*ay*slope
|
||||
b=2*bx-2*by*slope
|
||||
c=cx-cy*slope
|
||||
else:
|
||||
return []
|
||||
|
||||
roots = rootWrapper(0,a,b,c)
|
||||
retval = []
|
||||
for i in roots:
|
||||
if type(i) is complex and i.imag==0:
|
||||
i = i.real
|
||||
if type(i) is not complex and 0<=i<=1:
|
||||
retval.append(i)
|
||||
return retval
|
||||
|
||||
def tpoint((x1,y1),(x2,y2),t):
|
||||
return x1+t*(x2-x1),y1+t*(y2-y1)
|
||||
def beziersplitatt(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)),t):
|
||||
m1=tpoint((bx0,by0),(bx1,by1),t)
|
||||
m2=tpoint((bx1,by1),(bx2,by2),t)
|
||||
m3=tpoint((bx2,by2),(bx3,by3),t)
|
||||
m4=tpoint(m1,m2,t)
|
||||
m5=tpoint(m2,m3,t)
|
||||
m=tpoint(m4,m5,t)
|
||||
|
||||
return ((bx0,by0),m1,m4,m),(m,m5,m3,(bx3,by3))
|
||||
|
||||
'''
|
||||
Approximating the arc length of a bezier curve
|
||||
according to <http://www.cit.gu.edu.au/~anthony/info/graphics/bezier.curves>
|
||||
|
||||
if:
|
||||
L1 = |P0 P1| +|P1 P2| +|P2 P3|
|
||||
L0 = |P0 P3|
|
||||
then:
|
||||
L = 1/2*L0 + 1/2*L1
|
||||
ERR = L1-L0
|
||||
ERR approaches 0 as the number of subdivisions (m) increases
|
||||
2^-4m
|
||||
|
||||
Reference:
|
||||
Jens Gravesen <gravesen@mat.dth.dk>
|
||||
"Adaptive subdivision and the length of Bezier curves"
|
||||
mat-report no. 1992-10, Mathematical Institute, The Technical
|
||||
University of Denmark.
|
||||
'''
|
||||
def pointdistance((x1,y1),(x2,y2)):
|
||||
return math.sqrt(((x2 - x1) ** 2) + ((y2 - y1) ** 2))
|
||||
def Gravesen_addifclose(b, len, error = 0.001):
|
||||
box = 0
|
||||
for i in range(1,4):
|
||||
box += pointdistance(b[i-1], b[i])
|
||||
chord = pointdistance(b[0], b[3])
|
||||
if (box - chord) > error:
|
||||
first, second = beziersplitatt(b, 0.5)
|
||||
Gravesen_addifclose(first, len, error)
|
||||
Gravesen_addifclose(second, len, error)
|
||||
else:
|
||||
len[0] += (box / 2.0) + (chord / 2.0)
|
||||
def bezierlengthGravesen(b, error = 0.001):
|
||||
len = [0]
|
||||
Gravesen_addifclose(b, len, error)
|
||||
return len[0]
|
||||
|
||||
# balf = Bezier Arc Length Function
|
||||
balfax,balfbx,balfcx,balfay,balfby,balfcy = 0,0,0,0,0,0
|
||||
def balf(t):
|
||||
retval = (balfax*(t**2) + balfbx*t + balfcx)**2 + (balfay*(t**2) + balfby*t + balfcy)**2
|
||||
return math.sqrt(retval)
|
||||
|
||||
def Simpson(f, a, b, n_limit, tolerance):
|
||||
n = 2
|
||||
multiplier = (b - a)/6.0
|
||||
endsum = f(a) + f(b)
|
||||
interval = (b - a)/2.0
|
||||
asum = 0.0
|
||||
bsum = f(a + interval)
|
||||
est1 = multiplier * (endsum + (2.0 * asum) + (4.0 * bsum))
|
||||
est0 = 2.0 * est1
|
||||
#print multiplier, endsum, interval, asum, bsum, est1, est0
|
||||
while n < n_limit and abs(est1 - est0) > tolerance:
|
||||
n *= 2
|
||||
multiplier /= 2.0
|
||||
interval /= 2.0
|
||||
asum += bsum
|
||||
bsum = 0.0
|
||||
est0 = est1
|
||||
for i in xrange(1, n, 2):
|
||||
bsum += f(a + (i * interval))
|
||||
est1 = multiplier * (endsum + (2.0 * asum) + (4.0 * bsum))
|
||||
#print multiplier, endsum, interval, asum, bsum, est1, est0
|
||||
return est1
|
||||
|
||||
def bezierlengthSimpson(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)), tolerance = 0.001):
|
||||
global balfax,balfbx,balfcx,balfay,balfby,balfcy
|
||||
ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
|
||||
balfax,balfbx,balfcx,balfay,balfby,balfcy = 3*ax,2*bx,cx,3*ay,2*by,cy
|
||||
return Simpson(balf, 0.0, 1.0, 4096, tolerance)
|
||||
|
||||
def beziertatlength(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)), l = 0.5, tolerance = 0.001):
|
||||
global balfax,balfbx,balfcx,balfay,balfby,balfcy
|
||||
ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
|
||||
balfax,balfbx,balfcx,balfay,balfby,balfcy = 3*ax,2*bx,cx,3*ay,2*by,cy
|
||||
t = 1.0
|
||||
tdiv = t
|
||||
curlen = Simpson(balf, 0.0, t, 4096, tolerance)
|
||||
targetlen = l * curlen
|
||||
diff = curlen - targetlen
|
||||
while abs(diff) > tolerance:
|
||||
tdiv /= 2.0
|
||||
if diff < 0:
|
||||
t += tdiv
|
||||
else:
|
||||
t -= tdiv
|
||||
curlen = Simpson(balf, 0.0, t, 4096, tolerance)
|
||||
diff = curlen - targetlen
|
||||
return t
|
||||
|
||||
#default bezier length method
|
||||
bezierlength = bezierlengthSimpson
|
||||
|
||||
if __name__ == '__main__':
|
||||
# import timing
|
||||
#print linebezierintersect(((,),(,)),((,),(,),(,),(,)))
|
||||
#print linebezierintersect(((0,1),(0,-1)),((-1,0),(-.5,0),(.5,0),(1,0)))
|
||||
tol = 0.00000001
|
||||
curves = [((0,0),(1,5),(4,5),(5,5)),
|
||||
((0,0),(0,0),(5,0),(10,0)),
|
||||
((0,0),(0,0),(5,1),(10,0)),
|
||||
((-10,0),(0,0),(10,0),(10,10)),
|
||||
((15,10),(0,0),(10,0),(-5,10))]
|
||||
'''
|
||||
for curve in curves:
|
||||
timing.start()
|
||||
g = bezierlengthGravesen(curve,tol)
|
||||
timing.finish()
|
||||
gt = timing.micro()
|
||||
|
||||
timing.start()
|
||||
s = bezierlengthSimpson(curve,tol)
|
||||
timing.finish()
|
||||
st = timing.micro()
|
||||
|
||||
print g, gt
|
||||
print s, st
|
||||
'''
|
||||
for curve in curves:
|
||||
print beziertatlength(curve,0.5)
|
||||
|
||||
|
||||
# vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 encoding=utf-8 textwidth=99
|
||||
@@ -1,26 +0,0 @@
|
||||
"""G-code emitted at the start of processing the SVG file"""
|
||||
preamble = "G28\nG1 Z5.0"
|
||||
|
||||
"""G-code emitted at the end of processing the SVG file"""
|
||||
postamble = "G28"
|
||||
|
||||
"""G-code emitted before processing a SVG shape"""
|
||||
shape_preamble = "G4 P200"
|
||||
|
||||
"""G-code emitted after processing a SVG shape"""
|
||||
shape_postamble = "G4 P200"
|
||||
|
||||
"""Print bed width in mm"""
|
||||
bed_max_x = 200
|
||||
|
||||
"""Print bed height in mm"""
|
||||
bed_max_y = 200
|
||||
|
||||
"""
|
||||
Used to control the smoothness/sharpness of the curves.
|
||||
Smaller the value greater the sharpness. Make sure the
|
||||
value is greater than 0.1
|
||||
"""
|
||||
smoothness = 0.2
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
from bezmisc import *
|
||||
from ffgeom import *
|
||||
|
||||
def maxdist(((p0x,p0y),(p1x,p1y),(p2x,p2y),(p3x,p3y))):
|
||||
p0 = Point(p0x,p0y)
|
||||
p1 = Point(p1x,p1y)
|
||||
p2 = Point(p2x,p2y)
|
||||
p3 = Point(p3x,p3y)
|
||||
|
||||
s1 = Segment(p0,p3)
|
||||
return max(s1.distanceToPoint(p1),s1.distanceToPoint(p2))
|
||||
|
||||
|
||||
def cspsubdiv(csp,flat):
|
||||
for sp in csp:
|
||||
subdiv(sp,flat)
|
||||
|
||||
def subdiv(sp,flat,i=1):
|
||||
p0 = sp[i-1][1]
|
||||
p1 = sp[i-1][2]
|
||||
p2 = sp[i][0]
|
||||
p3 = sp[i][1]
|
||||
|
||||
b = (p0,p1,p2,p3)
|
||||
m = maxdist(b)
|
||||
if m <= flat:
|
||||
try:
|
||||
subdiv(sp,flat,i+1)
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
one, two = beziersplitatt(b,0.5)
|
||||
sp[i-1][2] = one[1]
|
||||
sp[i][0] = two[2]
|
||||
p = [one[2],one[3],two[1]]
|
||||
sp[i:1] = [p]
|
||||
subdiv(sp,flat,i)
|
||||
@@ -1,169 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
cubicsuperpath.py
|
||||
|
||||
Copyright (C) 2005 Aaron Spike, aaron@ekips.org
|
||||
|
||||
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
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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 simplepath
|
||||
from math import *
|
||||
|
||||
def matprod(mlist):
|
||||
prod=mlist[0]
|
||||
for m in mlist[1:]:
|
||||
a00=prod[0][0]*m[0][0]+prod[0][1]*m[1][0]
|
||||
a01=prod[0][0]*m[0][1]+prod[0][1]*m[1][1]
|
||||
a10=prod[1][0]*m[0][0]+prod[1][1]*m[1][0]
|
||||
a11=prod[1][0]*m[0][1]+prod[1][1]*m[1][1]
|
||||
prod=[[a00,a01],[a10,a11]]
|
||||
return prod
|
||||
def rotmat(teta):
|
||||
return [[cos(teta),-sin(teta)],[sin(teta),cos(teta)]]
|
||||
def applymat(mat, pt):
|
||||
x=mat[0][0]*pt[0]+mat[0][1]*pt[1]
|
||||
y=mat[1][0]*pt[0]+mat[1][1]*pt[1]
|
||||
pt[0]=x
|
||||
pt[1]=y
|
||||
def norm(pt):
|
||||
return sqrt(pt[0]*pt[0]+pt[1]*pt[1])
|
||||
|
||||
def ArcToPath(p1,params):
|
||||
A=p1[:]
|
||||
rx,ry,teta,longflag,sweepflag,x2,y2=params[:]
|
||||
teta = teta*pi/180.0
|
||||
B=[x2,y2]
|
||||
if rx==0 or ry==0:
|
||||
return([[A,A,A],[B,B,B]])
|
||||
mat=matprod((rotmat(teta),[[1/rx,0],[0,1/ry]],rotmat(-teta)))
|
||||
applymat(mat, A)
|
||||
applymat(mat, B)
|
||||
k=[-(B[1]-A[1]),B[0]-A[0]]
|
||||
d=k[0]*k[0]+k[1]*k[1]
|
||||
k[0]/=sqrt(d)
|
||||
k[1]/=sqrt(d)
|
||||
d=sqrt(max(0,1-d/4))
|
||||
if longflag==sweepflag:
|
||||
d*=-1
|
||||
O=[(B[0]+A[0])/2+d*k[0],(B[1]+A[1])/2+d*k[1]]
|
||||
OA=[A[0]-O[0],A[1]-O[1]]
|
||||
OB=[B[0]-O[0],B[1]-O[1]]
|
||||
start=acos(OA[0]/norm(OA))
|
||||
if OA[1]<0:
|
||||
start*=-1
|
||||
end=acos(OB[0]/norm(OB))
|
||||
if OB[1]<0:
|
||||
end*=-1
|
||||
|
||||
if sweepflag and start>end:
|
||||
end +=2*pi
|
||||
if (not sweepflag) and start<end:
|
||||
end -=2*pi
|
||||
|
||||
NbSectors=int(abs(start-end)*2/pi)+1
|
||||
dTeta=(end-start)/NbSectors
|
||||
#v=dTeta*2/pi*0.552
|
||||
#v=dTeta*2/pi*4*(sqrt(2)-1)/3
|
||||
v = 4*tan(dTeta/4)/3
|
||||
#if not sweepflag:
|
||||
# v*=-1
|
||||
p=[]
|
||||
for i in range(0,NbSectors+1,1):
|
||||
angle=start+i*dTeta
|
||||
v1=[O[0]+cos(angle)-(-v)*sin(angle),O[1]+sin(angle)+(-v)*cos(angle)]
|
||||
pt=[O[0]+cos(angle) ,O[1]+sin(angle) ]
|
||||
v2=[O[0]+cos(angle)- v *sin(angle),O[1]+sin(angle)+ v *cos(angle)]
|
||||
p.append([v1,pt,v2])
|
||||
p[ 0][0]=p[ 0][1][:]
|
||||
p[-1][2]=p[-1][1][:]
|
||||
|
||||
mat=matprod((rotmat(teta),[[rx,0],[0,ry]],rotmat(-teta)))
|
||||
for pts in p:
|
||||
applymat(mat, pts[0])
|
||||
applymat(mat, pts[1])
|
||||
applymat(mat, pts[2])
|
||||
return(p)
|
||||
|
||||
def CubicSuperPath(simplepath):
|
||||
csp = []
|
||||
subpath = -1
|
||||
subpathstart = []
|
||||
last = []
|
||||
lastctrl = []
|
||||
for s in simplepath:
|
||||
cmd, params = s
|
||||
if cmd == 'M':
|
||||
if last:
|
||||
csp[subpath].append([lastctrl[:],last[:],last[:]])
|
||||
subpath += 1
|
||||
csp.append([])
|
||||
subpathstart = params[:]
|
||||
last = params[:]
|
||||
lastctrl = params[:]
|
||||
elif cmd == 'L':
|
||||
csp[subpath].append([lastctrl[:],last[:],last[:]])
|
||||
last = params[:]
|
||||
lastctrl = params[:]
|
||||
elif cmd == 'C':
|
||||
csp[subpath].append([lastctrl[:],last[:],params[:2]])
|
||||
last = params[-2:]
|
||||
lastctrl = params[2:4]
|
||||
elif cmd == 'Q':
|
||||
q0=last[:]
|
||||
q1=params[0:2]
|
||||
q2=params[2:4]
|
||||
x0= q0[0]
|
||||
x1=1./3*q0[0]+2./3*q1[0]
|
||||
x2= 2./3*q1[0]+1./3*q2[0]
|
||||
x3= q2[0]
|
||||
y0= q0[1]
|
||||
y1=1./3*q0[1]+2./3*q1[1]
|
||||
y2= 2./3*q1[1]+1./3*q2[1]
|
||||
y3= q2[1]
|
||||
csp[subpath].append([lastctrl[:],[x0,y0],[x1,y1]])
|
||||
last = [x3,y3]
|
||||
lastctrl = [x2,y2]
|
||||
elif cmd == 'A':
|
||||
arcp=ArcToPath(last[:],params[:])
|
||||
arcp[ 0][0]=lastctrl[:]
|
||||
last=arcp[-1][1]
|
||||
lastctrl = arcp[-1][0]
|
||||
csp[subpath]+=arcp[:-1]
|
||||
elif cmd == 'Z':
|
||||
csp[subpath].append([lastctrl[:],last[:],last[:]])
|
||||
last = subpathstart[:]
|
||||
lastctrl = subpathstart[:]
|
||||
#append final superpoint
|
||||
csp[subpath].append([lastctrl[:],last[:],last[:]])
|
||||
return csp
|
||||
|
||||
def unCubicSuperPath(csp):
|
||||
a = []
|
||||
for subpath in csp:
|
||||
if subpath:
|
||||
a.append(['M',subpath[0][1][:]])
|
||||
for i in range(1,len(subpath)):
|
||||
a.append(['C',subpath[i-1][2][:] + subpath[i][0][:] + subpath[i][1][:]])
|
||||
return a
|
||||
|
||||
def parsePath(d):
|
||||
return CubicSuperPath(simplepath.parsePath(d))
|
||||
|
||||
def formatPath(p):
|
||||
return simplepath.formatPath(unCubicSuperPath(p))
|
||||
|
||||
|
||||
# vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 fileencoding=utf-8 textwidth=99
|
||||
@@ -1,141 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
ffgeom.py
|
||||
Copyright (C) 2005 Aaron Cyril Spike, aaron@ekips.org
|
||||
|
||||
This file is part of FretFind 2-D.
|
||||
|
||||
FretFind 2-D 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.
|
||||
|
||||
FretFind 2-D is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FretFind 2-D; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
import math
|
||||
try:
|
||||
NaN = float('NaN')
|
||||
except ValueError:
|
||||
PosInf = 1e300000
|
||||
NaN = PosInf/PosInf
|
||||
|
||||
class Point:
|
||||
precision = 5
|
||||
def __init__(self, x, y):
|
||||
self.__coordinates = {'x' : float(x), 'y' : float(y)}
|
||||
def __getitem__(self, key):
|
||||
return self.__coordinates[key]
|
||||
def __setitem__(self, key, value):
|
||||
self.__coordinates[key] = float(value)
|
||||
def __repr__(self):
|
||||
return '(%s, %s)' % (round(self['x'],self.precision),round(self['y'],self.precision))
|
||||
def copy(self):
|
||||
return Point(self['x'],self['y'])
|
||||
def translate(self, x, y):
|
||||
self['x'] += x
|
||||
self['y'] += y
|
||||
def move(self, x, y):
|
||||
self['x'] = float(x)
|
||||
self['y'] = float(y)
|
||||
|
||||
class Segment:
|
||||
def __init__(self, e0, e1):
|
||||
self.__endpoints = [e0, e1]
|
||||
def __getitem__(self, key):
|
||||
return self.__endpoints[key]
|
||||
def __setitem__(self, key, value):
|
||||
self.__endpoints[key] = value
|
||||
def __repr__(self):
|
||||
return repr(self.__endpoints)
|
||||
def copy(self):
|
||||
return Segment(self[0],self[1])
|
||||
def translate(self, x, y):
|
||||
self[0].translate(x,y)
|
||||
self[1].translate(x,y)
|
||||
def move(self,e0,e1):
|
||||
self[0] = e0
|
||||
self[1] = e1
|
||||
def delta_x(self):
|
||||
return self[1]['x'] - self[0]['x']
|
||||
def delta_y(self):
|
||||
return self[1]['y'] - self[0]['y']
|
||||
#alias functions
|
||||
run = delta_x
|
||||
rise = delta_y
|
||||
def slope(self):
|
||||
if self.delta_x() != 0:
|
||||
return self.delta_x() / self.delta_y()
|
||||
return NaN
|
||||
def intercept(self):
|
||||
if self.delta_x() != 0:
|
||||
return self[1]['y'] - (self[0]['x'] * self.slope())
|
||||
return NaN
|
||||
def distanceToPoint(self, p):
|
||||
s2 = Segment(self[0],p)
|
||||
c1 = dot(s2,self)
|
||||
if c1 <= 0:
|
||||
return Segment(p,self[0]).length()
|
||||
c2 = dot(self,self)
|
||||
if c2 <= c1:
|
||||
return Segment(p,self[1]).length()
|
||||
return self.perpDistanceToPoint(p)
|
||||
def perpDistanceToPoint(self, p):
|
||||
len = self.length()
|
||||
if len == 0: return NaN
|
||||
return math.fabs(((self[1]['x'] - self[0]['x']) * (self[0]['y'] - p['y'])) - \
|
||||
((self[0]['x'] - p['x']) * (self[1]['y'] - self[0]['y']))) / len
|
||||
def angle(self):
|
||||
return math.pi * (math.atan2(self.delta_y(), self.delta_x())) / 180
|
||||
def length(self):
|
||||
return math.sqrt((self.delta_x() ** 2) + (self.delta_y() ** 2))
|
||||
def pointAtLength(self, len):
|
||||
if self.length() == 0: return Point(NaN, NaN)
|
||||
ratio = len / self.length()
|
||||
x = self[0]['x'] + (ratio * self.delta_x())
|
||||
y = self[0]['y'] + (ratio * self.delta_y())
|
||||
return Point(x, y)
|
||||
def pointAtRatio(self, ratio):
|
||||
if self.length() == 0: return Point(NaN, NaN)
|
||||
x = self[0]['x'] + (ratio * self.delta_x())
|
||||
y = self[0]['y'] + (ratio * self.delta_y())
|
||||
return Point(x, y)
|
||||
def createParallel(self, p):
|
||||
return Segment(Point(p['x'] + self.delta_x(), p['y'] + self.delta_y()), p)
|
||||
def intersect(self, s):
|
||||
return intersectSegments(self, s)
|
||||
|
||||
def intersectSegments(s1, s2):
|
||||
x1 = s1[0]['x']
|
||||
x2 = s1[1]['x']
|
||||
x3 = s2[0]['x']
|
||||
x4 = s2[1]['x']
|
||||
|
||||
y1 = s1[0]['y']
|
||||
y2 = s1[1]['y']
|
||||
y3 = s2[0]['y']
|
||||
y4 = s2[1]['y']
|
||||
|
||||
denom = ((y4 - y3) * (x2 - x1)) - ((x4 - x3) * (y2 - y1))
|
||||
num1 = ((x4 - x3) * (y1 - y3)) - ((y4 - y3) * (x1 - x3))
|
||||
num2 = ((x2 - x1) * (y1 - y3)) - ((y2 - y1) * (x1 - x3))
|
||||
|
||||
num = num1
|
||||
|
||||
if denom != 0:
|
||||
x = x1 + ((num / denom) * (x2 - x1))
|
||||
y = y1 + ((num / denom) * (y2 - y1))
|
||||
return Point(x, y)
|
||||
return Point(NaN, NaN)
|
||||
|
||||
def dot(s1, s2):
|
||||
return s1.delta_x() * s2.delta_x() + s1.delta_y() * s2.delta_y()
|
||||
|
||||
|
||||
# vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 fileencoding=utf-8 textwidth=99
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://web.resource.org/cc/"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
width="200"
|
||||
height="200">
|
||||
<defs />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.35"
|
||||
inkscape:cx="375"
|
||||
inkscape:cy="520"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1" />
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -4,6 +4,7 @@ import logging
|
||||
import traceback
|
||||
import xml.etree.ElementTree as ET
|
||||
import simplepath
|
||||
import simpletransform
|
||||
import cubicsuperpath
|
||||
import cspsubdiv
|
||||
from bezmisc import beziersplitatt
|
||||
@@ -13,10 +14,14 @@ class svgshape(object):
|
||||
|
||||
def __init__(self, xml_node):
|
||||
self.xml_node = xml_node
|
||||
|
||||
|
||||
def d_path(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def transformation_matrix(self):
|
||||
t = self.xml_node.get('transform')
|
||||
return simpletransform.parseTransform(t) if t is not None else None
|
||||
|
||||
def svg_path(self):
|
||||
return "<path d=\"" + self.d_path() + "\"/>"
|
||||
|
||||
@@ -161,7 +166,7 @@ class polyline(polycommon):
|
||||
d += " L " + self.points[i]
|
||||
return d
|
||||
|
||||
def point_generator(path, flatness):
|
||||
def point_generator(path, mat, flatness):
|
||||
|
||||
if len(simplepath.parsePath(path)) == 0:
|
||||
return
|
||||
@@ -171,6 +176,10 @@ def point_generator(path, flatness):
|
||||
yield startX, startY
|
||||
|
||||
p = cubicsuperpath.parsePath(path)
|
||||
|
||||
if mat:
|
||||
simpletransform.applyTransformToPath(mat, p)
|
||||
|
||||
for sp in p:
|
||||
cspsubdiv.subdiv( sp, flatness)
|
||||
for csp in sp:
|
||||
|
||||
-202
@@ -1,202 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
simplepath.py
|
||||
functions for digesting paths into a simple list structure
|
||||
|
||||
Copyright (C) 2005 Aaron Spike, aaron@ekips.org
|
||||
|
||||
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
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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:
|
||||
break
|
||||
m = command.match(d, offset)
|
||||
if m:
|
||||
yield [d[offset:m.end()], True]
|
||||
offset = m.end()
|
||||
continue
|
||||
m = parameter.match(d, offset)
|
||||
if m:
|
||||
yield [d[offset:m.end()], False]
|
||||
offset = m.end()
|
||||
continue
|
||||
#TODO: create new exception
|
||||
raise Exception, 'Invalid path data!'
|
||||
'''
|
||||
pathdefs = {commandfamily:
|
||||
[
|
||||
implicitnext,
|
||||
#params,
|
||||
[casts,cast,cast],
|
||||
[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:
|
||||
try:
|
||||
token, isCommand = lexer.next()
|
||||
except StopIteration:
|
||||
break
|
||||
params = []
|
||||
needParam = True
|
||||
if isCommand:
|
||||
if not lastCommand and token.upper() != 'M':
|
||||
raise Exception, 'Invalid path, must begin with moveto.'
|
||||
else:
|
||||
command = token
|
||||
else:
|
||||
#command was omited
|
||||
#use last command's implicit next command
|
||||
needParam = False
|
||||
if lastCommand:
|
||||
if lastCommand.isupper():
|
||||
command = pathdefs[lastCommand][0]
|
||||
else:
|
||||
command = pathdefs[lastCommand.upper()][0].lower()
|
||||
else:
|
||||
raise Exception, 'Invalid path, no initial command.'
|
||||
numParams = pathdefs[command.upper()][1]
|
||||
while numParams > 0:
|
||||
if needParam:
|
||||
try:
|
||||
token, isCommand = lexer.next()
|
||||
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]
|
||||
params.append(param)
|
||||
needParam = True
|
||||
numParams -= 1
|
||||
#segment is now absolute so
|
||||
outputCommand = command.upper()
|
||||
|
||||
#Flesh out shortcut notation
|
||||
if outputCommand in ('H','V'):
|
||||
if outputCommand == 'H':
|
||||
params.append(pen[1])
|
||||
if outputCommand == 'V':
|
||||
params.insert(0,pen[0])
|
||||
outputCommand = 'L'
|
||||
if outputCommand in ('S','T'):
|
||||
params.insert(0,pen[1]+(pen[1]-lastControl[1]))
|
||||
params.insert(0,pen[0]+(pen[0]-lastControl[0]))
|
||||
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
|
||||
else:
|
||||
pen = tuple(params[-2:])
|
||||
|
||||
if outputCommand in ('Q','C'):
|
||||
lastControl = tuple(params[-4:-2])
|
||||
else:
|
||||
lastControl = pen
|
||||
lastCommand = command
|
||||
|
||||
retval.append([outputCommand,params])
|
||||
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
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
import xml.etree.ElementTree as ET
|
||||
import shapes as shapes_pkg
|
||||
from shapes import point_generator
|
||||
from config import *
|
||||
|
||||
def generate_gcode():
|
||||
svg_shapes = set(['rect', 'circle', 'ellipse', 'line', 'polyline', 'polygon', 'path'])
|
||||
|
||||
tree = ET.parse(sys.stdin)
|
||||
root = tree.getroot()
|
||||
|
||||
width = root.get('width')
|
||||
height = root.get('height')
|
||||
if width == None or height == None:
|
||||
viewbox = root.get('viewBox')
|
||||
if viewbox:
|
||||
_, _, width, height = viewbox.split()
|
||||
|
||||
if width == None or height == None:
|
||||
print "Unable to get width and height for the svg"
|
||||
sys.exit(1)
|
||||
|
||||
width = float(width)
|
||||
height = float(height)
|
||||
|
||||
scale_x = bed_max_x / max(width, height)
|
||||
scale_y = bed_max_y / max(width, height)
|
||||
|
||||
print preamble
|
||||
|
||||
for elem in root.iter():
|
||||
|
||||
try:
|
||||
_, tag_suffix = elem.tag.split('}')
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
if tag_suffix in svg_shapes:
|
||||
shape_class = getattr(shapes_pkg, tag_suffix)
|
||||
shape_obj = shape_class(elem)
|
||||
d = shape_obj.d_path()
|
||||
if d:
|
||||
print shape_preamble
|
||||
p = point_generator(d, smoothness)
|
||||
for x,y in p:
|
||||
if x > 0 and x < bed_max_x and y > 0 and y < bed_max_y:
|
||||
print "G1 X%0.1f Y%0.1f" % (scale_x*x, scale_y*y)
|
||||
print shape_postamble
|
||||
|
||||
print postamble
|
||||
|
||||
if __name__ == "__main__":
|
||||
generate_gcode()
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<inkscape-extension>
|
||||
<_name>Raster</_name>
|
||||
<id>com.plastibot.laser.svg2gcode.raster</id>
|
||||
<dependency type="executable" location="extensions">inkex.py</dependency>
|
||||
<dependency type="executable" location="extensions">bezmisc.py</dependency>
|
||||
<dependency type="executable" location="extensions">cspsubdiv.py</dependency>
|
||||
<dependency type="executable" location="extensions">cubicsuperpath.py</dependency>
|
||||
<dependency type="executable" location="extensions">ffgeom.py</dependency>
|
||||
<dependency type="executable" location="extensions">shapes.py</dependency>
|
||||
<dependency type="executable" location="extensions">simplepath.py</dependency>
|
||||
<dependency type="executable" location="extensions">svg2gcode_raster.py</dependency>
|
||||
<param name="bed-width" type="int" min="200" max="200" _gui-text="Bed Width">200</param>
|
||||
<param name="bed-height" type="int" min="200" max="200" _gui-text="Bed Height">200</param>
|
||||
<param name="gcode-file" type="string" _gui-text="G-code path">raster.gcode</param>
|
||||
<param name="preamble" type="string" _gui-text="Preamble">G28 \r\n G1 Z5.0</param>
|
||||
<param name="postamble" type="string" _gui-text="Postamble">G28</param>
|
||||
|
||||
<effect>
|
||||
<object-type>all</object-type>
|
||||
<effects-menu>
|
||||
<submenu _name="Plastibot Laser"/>
|
||||
</effects-menu>
|
||||
</effect>
|
||||
<script>
|
||||
<command reldir="extensions" interpreter="python">svg2gcode_raster.py</command>
|
||||
</script>
|
||||
</inkscape-extension>
|
||||
Executable
+133
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
import os
|
||||
import bisect
|
||||
|
||||
import inkex
|
||||
import shapes as shapes_pkg
|
||||
from shapes import point_generator
|
||||
|
||||
class svg2gcode_raster(inkex.Effect):
|
||||
|
||||
def __init__(self):
|
||||
inkex.Effect.__init__(self)
|
||||
self.OptionParser.add_option('-w', '--bed-width', action = 'store',
|
||||
type = 'int', dest = 'bed_width', default = 200,
|
||||
help = 'Bed Width')
|
||||
self.OptionParser.add_option('-x', '--bed-height', action = 'store',
|
||||
type = 'int', dest = 'bed_height', default = 200,
|
||||
help = 'Bed Height')
|
||||
self.OptionParser.add_option('-o', '--gcode-file', action = 'store',
|
||||
type = 'string', dest = 'gcode_file', default = 'plastibot.gcode',
|
||||
help = 'The generated gcode file path')
|
||||
|
||||
self.OptionParser.add_option('-p', '--preamble', action = 'store',
|
||||
type = 'string', dest = 'preamble', default = 'G28\nG1 Z5.0\n',
|
||||
help = 'Preamble G-code')
|
||||
self.OptionParser.add_option('-q', '--postamble', action = 'store',
|
||||
type = 'string', dest = 'postamble', default = 'G28\nG1 Z5.0\n',
|
||||
help = 'Postamble G-code')
|
||||
|
||||
def generate_gcode(self, svg):
|
||||
gcode_file = os.path.join(os.path.expanduser('~'), self.options.gcode_file)
|
||||
try:
|
||||
os.remove(gcode_file)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
bed_width = self.options.bed_width
|
||||
bed_height = self.options.bed_height
|
||||
|
||||
width = svg.get('width')
|
||||
height = svg.get('height')
|
||||
|
||||
width = inkex.unittouu(width)
|
||||
height = inkex.unittouu(height)
|
||||
|
||||
if width > bed_width or height > bed_height:
|
||||
raise ValueError(('The document size (%d x %d) is greater than the bedsize' %
|
||||
(round(width, 1), round(height, 1))))
|
||||
|
||||
points = []
|
||||
threshold = 0.1
|
||||
intermediate_points = 5
|
||||
svg_shapes = set(['rect', 'circle', 'ellipse', 'line', 'polyline', 'polygon', 'path'])
|
||||
|
||||
min_x = min_y = sys.maxint
|
||||
max_x = max_y = 0
|
||||
|
||||
for elem in svg.iter():
|
||||
try:
|
||||
_, tag_suffix = elem.tag.split('}')
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
if tag_suffix in svg_shapes:
|
||||
shape_class = getattr(shapes_pkg, tag_suffix)
|
||||
shape_obj = shape_class(elem)
|
||||
d = shape_obj.d_path()
|
||||
m = shape_obj.transformation_matrix()
|
||||
|
||||
if d:
|
||||
prev_x = prev_y = None
|
||||
p = point_generator(d, m, threshold)
|
||||
for x, y in p:
|
||||
|
||||
if prev_x is not None and prev_y is not None:
|
||||
|
||||
intermediate_points = \
|
||||
(5 * max(abs(x - prev_x), abs(y - prev_y)))/threshold
|
||||
|
||||
intermediate_points = int(intermediate_points)
|
||||
if intermediate_points > 0:
|
||||
x_incr = (x - prev_x)/intermediate_points
|
||||
y_incr = (y - prev_y)/intermediate_points
|
||||
for i in range(1, intermediate_points):
|
||||
bisect.insort(points, (round(prev_x + i*x_incr, 1) ,
|
||||
round(prev_y + i*y_incr, 1)))
|
||||
|
||||
min_x = x if x < min_x else min_x
|
||||
min_y = y if y < min_y else min_y
|
||||
max_x = x if x > max_x else max_x
|
||||
max_y = y if y > max_y else max_y
|
||||
bisect.insort(points, (round(x, 1) , round(y, 1)))
|
||||
prev_x = x
|
||||
prev_y = y
|
||||
|
||||
with open(gcode_file, 'w') as gcode:
|
||||
gcode.write(self.options.preamble + '\n')
|
||||
|
||||
laser_high = False
|
||||
y = min_y
|
||||
while y <= max_y:
|
||||
x = min_x
|
||||
while x <= max_x:
|
||||
i = bisect.bisect_left(points, (round(x, 1), round(y, 1)))
|
||||
if i != len(points) and cmp(points[i], (round(x, 1), round(y, 1))) == 0:
|
||||
|
||||
if not laser_high:
|
||||
gcode.write("M104 S255\n")
|
||||
laser_high = True
|
||||
|
||||
gcode.write("G1 X%0.1f Y%0.1f\n" % (x, y))
|
||||
else:
|
||||
|
||||
if laser_high:
|
||||
gcode.write("M104 S0\n")
|
||||
laser_high = False
|
||||
|
||||
gcode.write("G1 X%0.1f Y%0.1f\n" % (x, y))
|
||||
x += threshold
|
||||
y += threshold
|
||||
|
||||
gcode.write(self.options.postamble + '\n')
|
||||
|
||||
def effect(self):
|
||||
svg = self.document.getroot()
|
||||
self.generate_gcode(svg)
|
||||
|
||||
effect = svg2gcode_raster();
|
||||
effect.affect();
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<inkscape-extension>
|
||||
<_name>Vector</_name>
|
||||
<id>com.plastibot.laser.svg2gcode.vector</id>
|
||||
<dependency type="executable" location="extensions">inkex.py</dependency>
|
||||
<dependency type="executable" location="extensions">bezmisc.py</dependency>
|
||||
<dependency type="executable" location="extensions">cspsubdiv.py</dependency>
|
||||
<dependency type="executable" location="extensions">cubicsuperpath.py</dependency>
|
||||
<dependency type="executable" location="extensions">ffgeom.py</dependency>
|
||||
<dependency type="executable" location="extensions">shapes.py</dependency>
|
||||
<dependency type="executable" location="extensions">simplepath.py</dependency>
|
||||
<dependency type="executable" location="extensions">simpletransform.py</dependency>
|
||||
<dependency type="executable" location="extensions">svg2gcode_vector.py</dependency>
|
||||
<param name="bed-width" type="int" min="200" max="200" _gui-text="Bed Width">200</param>
|
||||
<param name="bed-height" type="int" min="200" max="200" _gui-text="Bed Height">200</param>
|
||||
<param name="gcode-file" type="string" _gui-text="G-code path">vector.gcode</param>
|
||||
<param name="preamble" type="string" _gui-text="Preamble">G28\nG1 Z5.0</param>
|
||||
<param name="postamble" type="string" _gui-text="Postamble">G28</param>
|
||||
<param name="shape-preamble" type="string" _gui-text="Shape Preamble">G4 P200</param>
|
||||
<param name="shape-postamble" type="string" _gui-text="Shape Postamble">G4 P200</param>
|
||||
|
||||
<effect>
|
||||
<object-type>all</object-type>
|
||||
<effects-menu>
|
||||
<submenu _name="Plastibot Laser"/>
|
||||
</effects-menu>
|
||||
</effect>
|
||||
<script>
|
||||
<command reldir="extensions" interpreter="python">svg2gcode_vector.py</command>
|
||||
</script>
|
||||
</inkscape-extension>
|
||||
Executable
+92
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
import inkex
|
||||
import shapes as shapes_pkg
|
||||
from shapes import point_generator
|
||||
|
||||
class svg2gcode_vector(inkex.Effect):
|
||||
|
||||
def __init__(self):
|
||||
inkex.Effect.__init__(self)
|
||||
self.OptionParser.add_option('-w', '--bed-width', action = 'store',
|
||||
type = 'int', dest = 'bed_width', default = 200,
|
||||
help = 'Bed Width')
|
||||
self.OptionParser.add_option('-x', '--bed-height', action = 'store',
|
||||
type = 'int', dest = 'bed_height', default = 200,
|
||||
help = 'Bed Height')
|
||||
self.OptionParser.add_option('-o', '--gcode-file', action = 'store',
|
||||
type = 'string', dest = 'gcode_file', default = 'plastibot.gcode',
|
||||
help = 'The generated gcode file path')
|
||||
|
||||
self.OptionParser.add_option('-p', '--preamble', action = 'store',
|
||||
type = 'string', dest = 'preamble', default = 'G28\nG1 Z5.0\n',
|
||||
help = 'Preamble G-code')
|
||||
self.OptionParser.add_option('-q', '--postamble', action = 'store',
|
||||
type = 'string', dest = 'postamble', default = 'G28\nG1 Z5.0\n',
|
||||
help = 'Postamble G-code')
|
||||
self.OptionParser.add_option('-s', '--shape-preamble', action = 'store',
|
||||
type = 'string', dest = 'shape_preamble', default = 'G4 P200\n',
|
||||
help = 'Shape Preamble G-code')
|
||||
self.OptionParser.add_option('-t', '--shape-postamble', action = 'store',
|
||||
type = 'string', dest = 'shape_postamble', default = 'G4 P200\n',
|
||||
help = 'Shape Postamble G-code')
|
||||
|
||||
def generate_gcode(self, svg):
|
||||
gcode_file = os.path.join(os.path.expanduser('~'), self.options.gcode_file)
|
||||
try:
|
||||
os.remove(gcode_file)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
bed_width = self.options.bed_width
|
||||
bed_height = self.options.bed_height
|
||||
|
||||
width = svg.get('width')
|
||||
height = svg.get('height')
|
||||
|
||||
width = inkex.unittouu(width)
|
||||
height = inkex.unittouu(height)
|
||||
|
||||
if width > bed_width or height > bed_height:
|
||||
raise ValueError(('The document size (%d x %d) is greater than the bedsize' %
|
||||
(round(width, 1), round(height, 1))))
|
||||
|
||||
with open(gcode_file, 'w') as gcode:
|
||||
gcode.write(self.options.preamble + '\n')
|
||||
|
||||
svg_shapes = set(['rect', 'circle', 'ellipse', 'line', 'polyline', 'polygon', 'path'])
|
||||
|
||||
for elem in svg.iter():
|
||||
try:
|
||||
_, tag_suffix = elem.tag.split('}')
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
if tag_suffix in svg_shapes:
|
||||
shape_class = getattr(shapes_pkg, tag_suffix)
|
||||
shape_obj = shape_class(elem)
|
||||
d = shape_obj.d_path()
|
||||
m = shape_obj.transformation_matrix()
|
||||
|
||||
if d:
|
||||
gcode.write(self.options.shape_preamble + '\n')
|
||||
|
||||
p = point_generator(d, m, 0.05)
|
||||
for x,y in p:
|
||||
gcode.write("G1 X%0.1f Y%0.1f\n" % (x, y))
|
||||
|
||||
gcode.write(self.options.shape_postamble + '\n')
|
||||
|
||||
gcode.write(self.options.postamble + '\n')
|
||||
|
||||
def effect(self):
|
||||
svg = self.document.getroot()
|
||||
self.generate_gcode(svg)
|
||||
|
||||
effect = svg2gcode_vector();
|
||||
effect.affect();
|
||||
|
||||
|
||||
Reference in new issue
Block a user