chiark / gitweb /
Added start of SVG 2D slicer.
authordaid <daid303@gmail.com>
Fri, 1 Jun 2012 16:16:07 +0000 (18:16 +0200)
committerdaid <daid303@gmail.com>
Fri, 1 Jun 2012 16:16:07 +0000 (18:16 +0200)
Cura/cura.py
Cura/gui/flatSlicerWindow.py [new file with mode: 0644]
Cura/gui/mainWindow.py
Cura/util/svg.py [new file with mode: 0644]

index 1b9fe2ffa6873b4b2d715ba2ae4b49f41c30a056..195029f6cdc7e104b3d1228695c7df46451ccaaa 100644 (file)
@@ -46,6 +46,7 @@ def main():
        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("-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 (file)
index 0000000..5f28767
--- /dev/null
@@ -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()
+
index 450e227240aeb7f639c9b2cc5d2e9de42d55f5fa..affdfa38507176b2a21ee283bbf332f8b1f687b6 100644 (file)
@@ -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 (file)
index 0000000..2c62c91
--- /dev/null
@@ -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()
+