Added start of SVG 2D slicer.

This commit is contained in:
daid 2012-06-01 18:16:07 +02:00
parent caa7d04d97
commit e9a28830b1
4 changed files with 373 additions and 0 deletions

View File

@ -46,6 +46,7 @@ def main():
parser = OptionParser(usage="usage: %prog [options] <filename>.stl") parser = OptionParser(usage="usage: %prog [options] <filename>.stl")
parser.add_option("-i", "--ini", action="store", type="string", dest="profileini", help="Load settings from a profile ini file") parser.add_option("-i", "--ini", action="store", type="string", dest="profileini", help="Load settings from a profile ini file")
parser.add_option("-P", "--project", action="store_true", dest="openprojectplanner", help="Open the project planner") parser.add_option("-P", "--project", action="store_true", dest="openprojectplanner", help="Open the project planner")
parser.add_option("-F", "--flat", action="store_true", dest="openflatslicer", help="Open the 2D SVG slicer")
parser.add_option("-r", "--print", action="store", type="string", dest="printfile", help="Open the printing interface, instead of the normal cura interface.") parser.add_option("-r", "--print", action="store", type="string", dest="printfile", help="Open the printing interface, instead of the normal cura interface.")
parser.add_option("-p", "--profile", action="store", type="string", dest="profile", help="Internal option, do not use!") parser.add_option("-p", "--profile", action="store", type="string", dest="profile", help="Internal option, do not use!")
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
@ -57,6 +58,10 @@ def main():
from gui import projectPlanner from gui import projectPlanner
projectPlanner.main() projectPlanner.main()
return return
if options.openflatslicer != None:
from gui import flatSlicerWindow
flatSlicerWindow.main()
return
if options.printfile != None: if options.printfile != None:
from gui import printWindow from gui import printWindow
printWindow.startPrintInterface(options.printfile) printWindow.startPrintInterface(options.printfile)

View File

@ -0,0 +1,195 @@
import wx, os, platform, types, webbrowser
from wx import glcanvas
import wx
try:
import OpenGL
OpenGL.ERROR_CHECKING = False
from OpenGL.GLU import *
from OpenGL.GL import *
hasOpenGLlibs = True
except:
print "Failed to find PyOpenGL: http://pyopengl.sourceforge.net/"
hasOpenGLlibs = False
from gui import toolbarUtil
from gui import opengl
from util import util3d
from util import svg
from util import profile
from util import version
class flatSlicerWindow(wx.Frame):
"Cura 2D SVG slicer"
def __init__(self):
super(flatSlicerWindow, self).__init__(None, title='Cura - ' + version.getVersion())
self.machineSize = util3d.Vector3(profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height'))
self.filename = None
self.svg = None
wx.EVT_CLOSE(self, self.OnClose)
self.panel = wx.Panel(self, -1)
self.SetSizer(wx.BoxSizer(wx.VERTICAL))
self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
#self.SetIcon(icon.getMainIcon())
self.toolbar = toolbarUtil.Toolbar(self.panel)
toolbarUtil.NormalButton(self.toolbar, self.OnOpenSVG, 'open.png', 'Open SVG')
self.toolbar.AddSeparator()
group = []
toolbarUtil.RadioButton(self.toolbar, group, 'object-3d-on.png', 'object-3d-off.png', '3D view', callback=self.On3DClick)
toolbarUtil.RadioButton(self.toolbar, group, 'object-top-on.png', 'object-top-off.png', 'Topdown view', callback=self.OnTopClick).SetValue(True)
self.toolbar.AddSeparator()
toolbarUtil.NormalButton(self.toolbar, self.OnQuit, 'exit.png', 'Close project planner')
self.toolbar.Realize()
sizer = wx.GridBagSizer(2,2)
self.panel.SetSizer(sizer)
self.preview = PreviewGLCanvas(self.panel, self)
sizer.Add(self.toolbar, (0,0), span=(1,1), flag=wx.EXPAND|wx.LEFT|wx.RIGHT)
sizer.Add(self.preview, (1,0), span=(5,1), flag=wx.EXPAND)
sizer.AddGrowableCol(0)
sizer.AddGrowableRow(1)
self.SetSize((600,400))
def OnClose(self, e):
self.Destroy()
def OnQuit(self, e):
self.Close()
def On3DClick(self):
self.preview.yaw = 30
self.preview.pitch = 60
self.preview.zoom = 300
self.preview.view3D = True
self.preview.Refresh()
def OnTopClick(self):
self.preview.view3D = False
self.preview.zoom = self.machineSize.x / 2 + 10
self.preview.offsetX = 0
self.preview.offsetY = 0
self.preview.Refresh()
def OnOpenSVG(self, e):
dlg=wx.FileDialog(self, "Open SVG file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
dlg.SetWildcard("SVG files (*.svg)|*.svg;*.SVG")
if dlg.ShowModal() == wx.ID_OK:
self.filename = dlg.GetPath()
self.svg = svg.SVG(self.filename)
self.svg.center(complex(profile.getProfileSettingFloat('machine_center_x'), profile.getProfileSettingFloat('machine_center_y')))
self.preview.Refresh()
dlg.Destroy()
class PreviewGLCanvas(glcanvas.GLCanvas):
def __init__(self, parent, realParent):
attribList = (glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24, glcanvas.WX_GL_STENCIL_SIZE, 8)
glcanvas.GLCanvas.__init__(self, parent, attribList = attribList)
self.parent = realParent
self.context = glcanvas.GLContext(self)
wx.EVT_PAINT(self, self.OnPaint)
wx.EVT_SIZE(self, self.OnSize)
wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)
wx.EVT_LEFT_DOWN(self, self.OnMouseLeftDown)
wx.EVT_MOTION(self, self.OnMouseMotion)
wx.EVT_MOUSEWHEEL(self, self.OnMouseWheel)
self.yaw = 30
self.pitch = 60
self.zoom = self.parent.machineSize.x / 2 + 10
self.offsetX = 0
self.offsetY = 0
self.view3D = False
self.allowDrag = False
def OnMouseLeftDown(self,e):
self.allowDrag = True
def OnMouseMotion(self,e):
if self.allowDrag and e.Dragging() and e.LeftIsDown():
if self.view3D:
self.yaw += e.GetX() - self.oldX
self.pitch -= e.GetY() - self.oldY
if self.pitch > 170:
self.pitch = 170
if self.pitch < 10:
self.pitch = 10
else:
self.offsetX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2
self.offsetY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2
self.Refresh()
else:
self.allowDrag = False
if e.Dragging() and e.RightIsDown():
if self.view3D:
self.zoom += e.GetY() - self.oldY
if self.zoom < 1:
self.zoom = 1
self.Refresh()
self.oldX = e.GetX()
self.oldY = e.GetY()
def OnMouseWheel(self,e):
if self.view3D:
self.zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0
if self.zoom < 1.0:
self.zoom = 1.0
self.Refresh()
def OnEraseBackground(self,event):
#Workaround for windows background redraw flicker.
pass
def OnSize(self,event):
self.Refresh()
def OnPaint(self,event):
dc = wx.PaintDC(self)
if not hasOpenGLlibs:
dc.Clear()
dc.DrawText("No PyOpenGL installation found.\nNo preview window available.", 10, 10)
return
self.SetCurrent(self.context)
opengl.InitGL(self, self.view3D, self.zoom)
if self.view3D:
glTranslate(0,0,-self.zoom)
glRotate(-self.pitch, 1,0,0)
glRotate(self.yaw, 0,0,1)
if False: #self.parent.triangleMesh != None:
glTranslate(0,0,-self.parent.triangleMesh.getMaximum().z / 2)
else:
glScale(1.0/self.zoom, 1.0/self.zoom, 1.0)
glTranslate(self.offsetX, self.offsetY, 0.0)
glTranslate(-self.parent.machineSize.x/2, -self.parent.machineSize.y/2, 0)
self.OnDraw()
self.SwapBuffers()
def OnDraw(self):
machineSize = self.parent.machineSize
opengl.DrawMachine(machineSize)
if self.parent.svg != None:
for path in self.parent.svg.paths:
glColor3f(1.0,0.8,0.6)
glBegin(GL_LINE_STRIP)
for p in path:
glVertex3f(p.real, p.imag, 1)
glEnd()
glFlush()
def main():
app = wx.App(False)
flatSlicerWindow().Show(True)
app.MainLoop()
if __name__ == '__main__':
main()

View File

@ -15,6 +15,7 @@ from gui import machineCom
from gui import printWindow from gui import printWindow
from gui import simpleMode from gui import simpleMode
from gui import projectPlanner from gui import projectPlanner
from gui import flatSlicerWindow
from gui import icon from gui import icon
from util import profile from util import profile
from util import version from util import version
@ -67,6 +68,8 @@ class mainWindow(configBase.configWindowBase):
expertMenu = wx.Menu() expertMenu = wx.Menu()
i = expertMenu.Append(-1, 'Open expert settings...') i = expertMenu.Append(-1, 'Open expert settings...')
self.Bind(wx.EVT_MENU, self.OnExpertOpen, i) self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
i = expertMenu.Append(-1, 'Open SVG (2D) slicer...')
self.Bind(wx.EVT_MENU, self.OnSVGSlicerOpen, i)
expertMenu.AppendSeparator() expertMenu.AppendSeparator()
i = expertMenu.Append(-1, 'Install default Marlin firmware') i = expertMenu.Append(-1, 'Install default Marlin firmware')
self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, i) self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, i)
@ -349,6 +352,11 @@ class mainWindow(configBase.configWindowBase):
pp.Centre() pp.Centre()
pp.Show(True) pp.Show(True)
def OnSVGSlicerOpen(self, e):
svgSlicer = flatSlicerWindow.flatSlicerWindow()
svgSlicer.Centre()
svgSlicer.Show(True)
def removeSliceProgress(self, spp): def removeSliceProgress(self, spp):
self.progressPanelList.remove(spp) self.progressPanelList.remove(spp)
newSize = self.GetSize(); newSize = self.GetSize();

165
Cura/util/svg.py Normal file
View File

@ -0,0 +1,165 @@
import sys, math, re, os, struct, time
from xml.etree import ElementTree
import profile
def processRect(e):
x = float(e.get('x'))
y = float(e.get('y'))
width = float(e.get('width'))
height = float(e.get('height'))
return [[complex(x, -y), complex(x+width, -y), complex(x+width, -(y+height)), complex(x, -(y+height)), complex(x, -y)]]
def processPath(e):
d = e.get('d').replace(',', ' ')
num = ""
cmd = None
paths = []
curPath = None
p = complex(0, 0)
for c in d + "#":
if c in "-+.0123456789e":
num += c
if c in " \t\n\r#":
if len(num) > 0:
param.append(float(num))
num = ""
if c in "MmZzLlHhVvCcSsQqTtAa#":
if cmd == 'M':
p = complex(param[0], -param[1])
curPath = None
i = 2
while i < len(param):
if curPath == None:
curPath = [p]
paths.append(curPath)
p = complex(param[i], -param[i+1])
curPath.append(p)
i += 2
elif cmd == 'm':
p += complex(param[0], -param[1])
curPath = None
i = 2
while i < len(param):
if curPath == None:
curPath = [p]
paths.append(curPath)
p += complex(param[i], -param[i+1])
curPath.append(p)
i += 2
elif cmd == 'L':
if curPath == None:
curPath = [p]
paths.append(curPath)
i = 0
while i < len(param):
p = complex(param[i], -param[i+1])
curPath.append(p)
i += 2
elif cmd == 'l':
if curPath == None:
curPath = [p]
paths.append(curPath)
i = 0
while i < len(param):
p += complex(param[i], -param[i+1])
curPath.append(p)
i += 2
curPath.append(p)
elif cmd == 'c':
if curPath == None:
curPath = [p]
paths.append(curPath)
i = 0
while i < len(param):
addCurve(curPath, p, p + complex(param[i], -param[i+1]), p + complex(param[i+2], -param[i+3]), p + complex(param[i+4], -param[i+5]))
p += complex(param[i+4], -param[i+5])
curPath.append(p)
i += 6
elif cmd == 'Z' or cmd == 'z':
curPath.append(curPath[0])
elif cmd != None:
print cmd
cmd = c
param = []
return paths
def interpolate(p0, p1, f):
return complex(p0.real + (p1.real - p0.real) * f, p0.imag + (p1.imag - p0.imag) * f)
def addCurve(path, p0, q0, q1, p1):
oldPoint = p0
for n in xrange(0, 100):
k = n / 100.0
r0 = interpolate(p0, q0, k);
r1 = interpolate(q0, q1, k);
r2 = interpolate(q1, p1, k);
b0 = interpolate(r0, r1, k);
b1 = interpolate(r1, r2, k);
s = interpolate(b0, b1, k);
if abs(s - oldPoint) > 1.0:
path.append(s)
oldPoint = s
def movePath(p, offset):
return map(lambda _p: _p - offset, p)
class SVG(object):
def __init__(self, filename):
tagProcess = {}
tagProcess['rect'] = processRect
tagProcess['path'] = processPath
self.paths = []
for e in ElementTree.parse(open(filename, "r")).getiterator():
tag = e.tag[e.tag.find('}')+1:]
if not tag in tagProcess:
#print 'unknown tag: %s' % (tag)
continue
self.paths.extend(tagProcess[tag](e))
def center(self, centerPoint):
offset = complex(0, 0)
n = 0
for path in self.paths:
for point in path:
offset += point
n += 1
offset /= n
offset -= centerPoint
self.paths = [movePath(p, offset) for p in self.paths]
if __name__ == '__main__':
svg = SVG("../logo.svg")
f = open("../../test_export.gcode", "w")
f.write(';TYPE:CUSTOM\n')
f.write(profile.getAlterationFileContents('start.gcode'))
svg.center(complex(profile.getProfileSettingFloat('machine_center_x'), profile.getProfileSettingFloat('machine_center_y')))
layerThickness = 0.4
filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
filamentArea = math.pi * filamentRadius * filamentRadius
lineWidth = profile.getProfileSettingFloat('nozzle_size') * 2
e = 0
z = layerThickness
for n in xrange(0, 20):
f.write("G1 Z%f F%f\n" % (z, profile.getProfileSettingFloat('max_z_speed')*60))
for path in svg.paths:
oldPoint = path[0]
extrusionMMperDist = lineWidth * layerThickness / filamentArea
f.write("G1 X%f Y%f F%f\n" % (oldPoint.real, oldPoint.imag, profile.getProfileSettingFloat('travel_speed')*60))
f.write("G1 F%f\n" % (profile.getProfileSettingFloat('print_speed')*60))
for point in path[1:]:
dist = abs(oldPoint - point)
e += dist * extrusionMMperDist
f.write("G1 X%f Y%f E%f\n" % (point.real, point.imag, e))
oldPoint = point
z += layerThickness
f.write(profile.getAlterationFileContents('end.gcode'))
f.close()