From: daid Date: Fri, 1 Jun 2012 16:16:07 +0000 (+0200) Subject: Added start of SVG 2D slicer. X-Git-Tag: 12.07~44^2~2 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=e9a28830b1640bf48425bf7dd1b8f7522c26977e;p=cura.git Added start of SVG 2D slicer. --- diff --git a/Cura/cura.py b/Cura/cura.py index 1b9fe2ff..195029f6 100644 --- a/Cura/cura.py +++ b/Cura/cura.py @@ -46,6 +46,7 @@ def main(): parser = OptionParser(usage="usage: %prog [options] .stl") 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("-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("-p", "--profile", action="store", type="string", dest="profile", help="Internal option, do not use!") (options, args) = parser.parse_args() @@ -57,6 +58,10 @@ def main(): from gui import projectPlanner projectPlanner.main() return + if options.openflatslicer != None: + from gui import flatSlicerWindow + flatSlicerWindow.main() + return if options.printfile != None: from gui import printWindow printWindow.startPrintInterface(options.printfile) diff --git a/Cura/gui/flatSlicerWindow.py b/Cura/gui/flatSlicerWindow.py new file mode 100644 index 00000000..5f28767b --- /dev/null +++ b/Cura/gui/flatSlicerWindow.py @@ -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() + diff --git a/Cura/gui/mainWindow.py b/Cura/gui/mainWindow.py index 450e2272..affdfa38 100644 --- a/Cura/gui/mainWindow.py +++ b/Cura/gui/mainWindow.py @@ -15,6 +15,7 @@ from gui import machineCom from gui import printWindow from gui import simpleMode from gui import projectPlanner +from gui import flatSlicerWindow from gui import icon from util import profile from util import version @@ -67,6 +68,8 @@ class mainWindow(configBase.configWindowBase): expertMenu = wx.Menu() i = expertMenu.Append(-1, 'Open expert settings...') 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() i = expertMenu.Append(-1, 'Install default Marlin firmware') self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, i) @@ -349,6 +352,11 @@ class mainWindow(configBase.configWindowBase): pp.Centre() pp.Show(True) + def OnSVGSlicerOpen(self, e): + svgSlicer = flatSlicerWindow.flatSlicerWindow() + svgSlicer.Centre() + svgSlicer.Show(True) + def removeSliceProgress(self, spp): self.progressPanelList.remove(spp) newSize = self.GetSize(); diff --git a/Cura/util/svg.py b/Cura/util/svg.py new file mode 100644 index 00000000..2c62c914 --- /dev/null +++ b/Cura/util/svg.py @@ -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() +