mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-08-05 13:00:43 +08:00
Added start of SVG 2D slicer.
This commit is contained in:
parent
caa7d04d97
commit
e9a28830b1
@ -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)
|
||||||
|
195
Cura/gui/flatSlicerWindow.py
Normal file
195
Cura/gui/flatSlicerWindow.py
Normal 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()
|
||||||
|
|
@ -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
165
Cura/util/svg.py
Normal 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()
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user