1 from __future__ import absolute_import
\r
4 import wx, os, platform, types, webbrowser, math, subprocess, threading, time
\r
7 from wx import glcanvas
\r
11 OpenGL.ERROR_CHECKING = False
\r
12 from OpenGL.GLU import *
\r
13 from OpenGL.GL import *
\r
14 hasOpenGLlibs = True
\r
16 print "Failed to find PyOpenGL: http://pyopengl.sourceforge.net/"
\r
17 hasOpenGLlibs = False
\r
19 from gui import opengl
\r
20 from gui import toolbarUtil
\r
21 from gui import icon
\r
22 from util import profile
\r
23 from util import util3d
\r
24 from util import stl
\r
25 from util import sliceRun
\r
30 class projectPlanner(wx.Frame):
\r
31 "Main user interface window"
\r
33 super(projectPlanner, self).__init__(None, title='Cura - Project Planner')
\r
35 self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
\r
36 wx.EVT_CLOSE(self, self.OnClose)
\r
37 #self.SetIcon(icon.getMainIcon())
\r
40 self.selection = None
\r
42 self.machineSize = util3d.Vector3(profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height'))
\r
43 self.headSizeMin = util3d.Vector3(profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_min_y'),0)
\r
44 self.headSizeMax = util3d.Vector3(profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_max_y'),0)
\r
46 self.extruderOffset = [
\r
47 util3d.Vector3(0,0,0),
\r
48 util3d.Vector3(profile.getPreferenceFloat('extruder_offset_x1'), profile.getPreferenceFloat('extruder_offset_y1'), 0),
\r
49 util3d.Vector3(profile.getPreferenceFloat('extruder_offset_x2'), profile.getPreferenceFloat('extruder_offset_y2'), 0),
\r
50 util3d.Vector3(profile.getPreferenceFloat('extruder_offset_x3'), profile.getPreferenceFloat('extruder_offset_y3'), 0)]
\r
52 self.toolbar = toolbarUtil.Toolbar(self)
\r
54 toolbarUtil.NormalButton(self.toolbar, self.OnLoadProject, 'open.png', 'Open project')
\r
55 toolbarUtil.NormalButton(self.toolbar, self.OnSaveProject, 'save.png', 'Save project')
\r
56 self.toolbar.AddSeparator()
\r
58 toolbarUtil.RadioButton(self.toolbar, group, 'object-3d-on.png', 'object-3d-off.png', '3D view', callback=self.On3DClick)
\r
59 toolbarUtil.RadioButton(self.toolbar, group, 'object-top-on.png', 'object-top-off.png', 'Topdown view', callback=self.OnTopClick).SetValue(True)
\r
60 self.toolbar.AddSeparator()
\r
61 toolbarUtil.NormalButton(self.toolbar, self.OnQuit, 'exit.png', 'Close project planner')
\r
63 self.toolbar.Realize()
\r
65 sizer = wx.GridBagSizer(2,2)
\r
66 self.SetSizer(sizer)
\r
67 self.preview = PreviewGLCanvas(self)
\r
68 self.listbox = wx.ListBox(self, -1, choices=[])
\r
69 self.addButton = wx.Button(self, -1, "Add")
\r
70 self.remButton = wx.Button(self, -1, "Remove")
\r
71 self.sliceButton = wx.Button(self, -1, "Slice")
\r
72 self.autoPlaceButton = wx.Button(self, -1, "Auto Place")
\r
74 sizer.Add(self.toolbar, (0,0), span=(1,3), flag=wx.EXPAND)
\r
75 sizer.Add(self.preview, (1,0), span=(4,1), flag=wx.EXPAND)
\r
76 sizer.Add(self.listbox, (1,1), span=(1,2), flag=wx.EXPAND)
\r
77 sizer.Add(self.addButton, (2,1), span=(1,1))
\r
78 sizer.Add(self.remButton, (2,2), span=(1,1))
\r
79 sizer.Add(self.sliceButton, (3,1), span=(1,1))
\r
80 sizer.Add(self.autoPlaceButton, (3,2), span=(1,1))
\r
81 sizer.AddGrowableCol(0)
\r
82 sizer.AddGrowableRow(1)
\r
84 self.addButton.Bind(wx.EVT_BUTTON, self.OnAddModel)
\r
85 self.remButton.Bind(wx.EVT_BUTTON, self.OnRemModel)
\r
86 self.sliceButton.Bind(wx.EVT_BUTTON, self.OnSlice)
\r
87 self.autoPlaceButton.Bind(wx.EVT_BUTTON, self.OnAutoPlace)
\r
88 self.listbox.Bind(wx.EVT_LISTBOX, self.OnListSelect)
\r
90 panel = wx.Panel(self, -1)
\r
91 sizer.Add(panel, (4,1), span=(1,2))
\r
93 sizer = wx.GridBagSizer(2,2)
\r
94 panel.SetSizer(sizer)
\r
96 self.scaleCtrl = wx.TextCtrl(panel, -1, '')
\r
97 self.rotateCtrl = wx.SpinCtrl(panel, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS)
\r
98 self.rotateCtrl.SetRange(0, 360)
\r
100 sizer.Add(wx.StaticText(panel, -1, 'Scale'), (0,0), flag=wx.ALIGN_CENTER_VERTICAL)
\r
101 sizer.Add(self.scaleCtrl, (0,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
\r
102 sizer.Add(wx.StaticText(panel, -1, 'Rotate'), (1,0), flag=wx.ALIGN_CENTER_VERTICAL)
\r
103 sizer.Add(self.rotateCtrl, (1,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
\r
105 if int(profile.getPreference('extruder_amount')) > 1:
\r
106 self.extruderCtrl = wx.ComboBox(panel, -1, '1', choices=map(str, range(1, int(profile.getPreference('extruder_amount'))+1)), style=wx.CB_DROPDOWN|wx.CB_READONLY)
\r
107 sizer.Add(wx.StaticText(panel, -1, 'Extruder'), (2,0), flag=wx.ALIGN_CENTER_VERTICAL)
\r
108 sizer.Add(self.extruderCtrl, (2,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
\r
109 self.extruderCtrl.Bind(wx.EVT_COMBOBOX, self.OnExtruderChange)
\r
111 self.scaleCtrl.Bind(wx.EVT_TEXT, self.OnScaleChange)
\r
112 self.rotateCtrl.Bind(wx.EVT_SPINCTRL, self.OnRotateChange)
\r
114 self.SetSize((800,600))
\r
116 def OnClose(self, e):
\r
119 def OnQuit(self, e):
\r
122 def OnSaveProject(self, e):
\r
123 dlg=wx.FileDialog(self, "Save project file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
\r
124 dlg.SetWildcard("Project files (*.curaproject)|*.curaproject")
\r
125 if dlg.ShowModal() == wx.ID_OK:
\r
126 cp = ConfigParser.ConfigParser()
\r
128 for item in self.list:
\r
129 section = 'model_%d' % (i)
\r
130 cp.add_section(section)
\r
131 cp.set(section, 'filename', item.filename.encode("utf-8"))
\r
132 cp.set(section, 'centerX', str(item.centerX))
\r
133 cp.set(section, 'centerY', str(item.centerY))
\r
134 cp.set(section, 'scale', str(item.scale))
\r
135 cp.set(section, 'rotate', str(item.rotate))
\r
136 cp.set(section, 'flipX', str(item.flipX))
\r
137 cp.set(section, 'flipY', str(item.flipY))
\r
138 cp.set(section, 'flipZ', str(item.flipZ))
\r
139 cp.set(section, 'swapXZ', str(item.swapXZ))
\r
140 cp.set(section, 'swapYZ', str(item.swapYZ))
\r
141 cp.set(section, 'extruder', str(item.extruder+1))
\r
143 cp.write(open(dlg.GetPath(), "w"))
\r
146 def OnLoadProject(self, e):
\r
147 dlg=wx.FileDialog(self, "Open project file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
\r
148 dlg.SetWildcard("Project files (*.curaproject)|*.curaproject")
\r
149 if dlg.ShowModal() == wx.ID_OK:
\r
150 cp = ConfigParser.ConfigParser()
\r
151 cp.read(dlg.GetPath())
\r
153 self.listbox.Clear()
\r
155 while cp.has_section('model_%d' % (i)):
\r
156 section = 'model_%d' % (i)
\r
158 item = stl.stlModel()
\r
159 item.filename = unicode(cp.get(section, 'filename'), "utf-8")
\r
160 self.loadModelFile(item)
\r
161 item.centerX = float(cp.get(section, 'centerX'))
\r
162 item.centerY = float(cp.get(section, 'centerY'))
\r
163 item.scale = float(cp.get(section, 'scale'))
\r
164 item.rotate = float(cp.get(section, 'rotate'))
\r
165 item.flipX = cp.get(section, 'flipX') == 'True'
\r
166 item.flipY = cp.get(section, 'flipY') == 'True'
\r
167 item.flipZ = cp.get(section, 'flipZ') == 'True'
\r
168 item.swapXZ = cp.get(section, 'swapXZ') == 'True'
\r
169 item.swapYZ = cp.get(section, 'swapYZ') == 'True'
\r
170 if cp.has_option(section, 'extruder'):
\r
171 item.extuder = int(cp.get(section, 'extruder'))-1
\r
174 self.list.append(item)
\r
175 self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1])
\r
177 self.listbox.SetSelection(len(self.list)-1)
\r
178 self.OnListSelect(None)
\r
182 def On3DClick(self):
\r
183 self.preview.yaw = 30
\r
184 self.preview.pitch = 60
\r
185 self.preview.zoom = 300
\r
186 self.preview.view3D = True
\r
187 self.preview.Refresh()
\r
189 def OnTopClick(self):
\r
190 self.preview.view3D = False
\r
191 self.preview.zoom = self.machineSize.x / 2 + 10
\r
192 self.preview.offsetX = 0
\r
193 self.preview.offsetY = 0
\r
194 self.preview.Refresh()
\r
196 def OnListSelect(self, e):
\r
197 if self.listbox.GetSelection() == -1:
\r
199 self.selection = self.list[self.listbox.GetSelection()]
\r
200 self.scaleCtrl.SetValue(str(self.selection.scale))
\r
201 self.rotateCtrl.SetValue(int(self.selection.rotate))
\r
202 if int(profile.getPreference('extruder_amount')) > 1:
\r
203 self.extruderCtrl.SetValue(str(self.selection.extruder+1))
\r
204 self.preview.Refresh()
\r
206 def OnAddModel(self, e):
\r
207 dlg=wx.FileDialog(self, "Open file to print", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST|wx.FD_MULTIPLE)
\r
208 dlg.SetWildcard("STL files (*.stl)|*.stl;*.STL")
\r
209 if dlg.ShowModal() == wx.ID_OK:
\r
210 for filename in dlg.GetPaths():
\r
211 item = stl.stlModel()
\r
212 item.filename=filename
\r
213 profile.putPreference('lastFile', item.filename)
\r
214 if not(os.path.exists(item.filename)):
\r
216 self.loadModelFile(item)
\r
217 self.list.append(item)
\r
218 self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1])
\r
219 self.listbox.SetSelection(len(self.list)-1)
\r
220 self.OnListSelect(None)
\r
223 def OnRemModel(self, e):
\r
224 if self.selection == None:
\r
226 self.list.remove(self.selection)
\r
227 i = self.listbox.GetSelection()
\r
228 self.listbox.Delete(i)
\r
229 if len(self.list) > i:
\r
230 self.listbox.SetSelection(i)
\r
231 elif len(self.list) > 0:
\r
232 self.listbox.SetSelection(len(self.list) - 1)
\r
233 self.selection = None
\r
234 self.OnListSelect(None)
\r
235 self.preview.Refresh()
\r
237 def OnAutoPlace(self, e):
\r
238 bestAllowedSize = int(self.machineSize.y)
\r
239 bestArea = self._doAutoPlace(bestAllowedSize)
\r
240 for i in xrange(10, int(self.machineSize.y), 10):
\r
241 area = self._doAutoPlace(i)
\r
242 if area < bestArea:
\r
243 bestAllowedSize = i
\r
245 self._doAutoPlace(bestAllowedSize)
\r
246 self.preview.Refresh()
\r
248 def _doAutoPlace(self, allowedSizeY):
\r
249 extraSizeMin = self.headSizeMin
\r
250 extraSizeMax = self.headSizeMax
\r
251 if profile.getProfileSettingFloat('skirt_line_count') > 0:
\r
252 skirtSize = profile.getProfileSettingFloat('skirt_line_count') * profile.calculateEdgeWidth() + profile.getProfileSettingFloat('skirt_gap')
\r
253 extraSizeMin = extraSizeMin + util3d.Vector3(skirtSize, skirtSize, 0)
\r
254 extraSizeMax = extraSizeMax + util3d.Vector3(skirtSize, skirtSize, 0)
\r
256 posX = self.machineSize.x
\r
258 minX = self.machineSize.x
\r
259 minY = self.machineSize.y
\r
264 for item in self.list:
\r
265 item.centerX = posX + item.getMaximum().x * item.scale * dirX
\r
266 item.centerY = posY + item.getMaximum().y * item.scale * dirY
\r
267 if item.centerY + item.getSize().y >= allowedSizeY:
\r
268 posX = minX - extraSizeMax.x - 1
\r
270 item.centerX = posX + item.getMaximum().x * item.scale * dirX
\r
271 item.centerY = posY + item.getMaximum().y * item.scale * dirY
\r
272 posY += item.getSize().y * item.scale * dirY + extraSizeMin.y + 1
\r
273 minX = min(minX, item.centerX - item.getSize().x * item.scale / 2)
\r
274 minY = min(minY, item.centerY - item.getSize().y * item.scale / 2)
\r
275 maxX = max(maxX, item.centerX + item.getSize().x * item.scale / 2)
\r
276 maxY = max(maxY, item.centerY + item.getSize().y * item.scale / 2)
\r
278 for item in self.list:
\r
279 item.centerX -= minX / 2
\r
280 item.centerY += (self.machineSize.y - maxY) / 2
\r
283 return ((maxX - minX) + (maxY - minY)) * 100
\r
285 return (maxX - minX) + (maxY - minY)
\r
287 def OnSlice(self, e):
\r
288 oldProfile = profile.getGlobalProfileString()
\r
290 put = profile.putProfileSetting
\r
292 put('model_multiply_x', '1')
\r
293 put('model_multiply_y', '1')
\r
294 put('enable_raft', 'False')
\r
295 put('add_start_end_gcode', 'False')
\r
296 put('gcode_extension', 'project_tmp')
\r
300 for item in self.list:
\r
301 put('machine_center_x', item.centerX - self.extruderOffset[item.extruder].x)
\r
302 put('machine_center_y', item.centerY - self.extruderOffset[item.extruder].y)
\r
303 put('model_scale', item.scale)
\r
304 put('flip_x', item.flipX)
\r
305 put('flip_y', item.flipY)
\r
306 put('flip_z', item.flipZ)
\r
307 put('model_rotate_base', item.rotate)
\r
308 put('swap_xz', item.swapXZ)
\r
309 put('swap_yz', item.swapYZ)
\r
312 action.sliceCmd = sliceRun.getSliceCommand(item.filename)
\r
313 action.centerX = item.centerX
\r
314 action.centerY = item.centerY
\r
315 action.extruder = item.extruder
\r
316 action.filename = item.filename
\r
317 clearZ = max(clearZ, item.getMaximum().z * item.scale)
\r
318 action.clearZ = clearZ
\r
319 actionList.append(action)
\r
321 #Restore the old profile.
\r
322 profile.loadGlobalProfileFromString(oldProfile)
\r
324 dlg=wx.FileDialog(self, "Save project gcode file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
\r
325 dlg.SetWildcard("GCode file (*.gcode)|*.gcode")
\r
326 if dlg.ShowModal() != wx.ID_OK:
\r
329 resultFilename = dlg.GetPath()
\r
332 pspw = ProjectSliceProgressWindow(actionList, resultFilename)
\r
333 pspw.extruderOffset = self.extruderOffset
\r
337 def loadModelFile(self, item):
\r
338 item.load(item.filename)
\r
339 item.origonalVertexes = list(item.vertexes)
\r
340 for i in xrange(0, len(item.origonalVertexes)):
\r
341 item.origonalVertexes[i] = item.origonalVertexes[i].copy()
\r
344 item.centerX = -item.getMinimum().x + 5
\r
345 item.centerY = -item.getMinimum().y + 5
\r
351 item.swapXZ = False
\r
352 item.swapYZ = False
\r
355 item.modelDisplayList = None
\r
356 item.modelDirty = False
\r
358 self.updateModelTransform(item)
\r
360 item.centerX = -item.getMinimum().x + 5
\r
361 item.centerY = -item.getMinimum().y + 5
\r
363 def OnScaleChange(self, e):
\r
364 if self.selection == None:
\r
367 self.selection.scale = float(self.scaleCtrl.GetValue())
\r
369 self.selection.scale = 1.0
\r
370 self.preview.Refresh()
\r
372 def OnRotateChange(self, e):
\r
373 if self.selection == None:
\r
375 self.selection.rotate = float(self.rotateCtrl.GetValue())
\r
376 self.updateModelTransform(self.selection)
\r
378 def OnExtruderChange(self, e):
\r
379 if self.selection == None:
\r
381 self.selection.extruder = int(self.extruderCtrl.GetValue()) - 1
\r
382 self.preview.Refresh()
\r
384 def updateModelTransform(self, item):
\r
385 rotate = item.rotate / 180.0 * math.pi
\r
395 swapXZ = item.swapXZ
\r
396 swapYZ = item.swapYZ
\r
397 mat00 = math.cos(rotate) * scaleX
\r
398 mat01 =-math.sin(rotate) * scaleY
\r
399 mat10 = math.sin(rotate) * scaleX
\r
400 mat11 = math.cos(rotate) * scaleY
\r
402 for i in xrange(0, len(item.origonalVertexes)):
\r
403 x = item.origonalVertexes[i].x
\r
404 y = item.origonalVertexes[i].y
\r
405 z = item.origonalVertexes[i].z
\r
410 item.vertexes[i].x = x * mat00 + y * mat01
\r
411 item.vertexes[i].y = x * mat10 + y * mat11
\r
412 item.vertexes[i].z = z * scaleZ
\r
414 for face in item.faces:
\r
418 face.normal = (v2 - v1).cross(v3 - v1)
\r
419 face.normal.normalize()
\r
421 self.moveModel(item)
\r
423 def moveModel(self, item):
\r
424 minZ = item.getMinimumZ()
\r
425 min = item.getMinimum()
\r
426 max = item.getMaximum()
\r
427 for v in item.vertexes:
\r
429 v.x -= min.x + (max.x - min.x) / 2
\r
430 v.y -= min.y + (max.y - min.y) / 2
\r
432 item.modelDirty = True
\r
433 self.preview.Refresh()
\r
435 class PreviewGLCanvas(glcanvas.GLCanvas):
\r
436 def __init__(self, parent):
\r
437 attribList = (glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24, glcanvas.WX_GL_STENCIL_SIZE, 8)
\r
438 glcanvas.GLCanvas.__init__(self, parent, attribList = attribList)
\r
439 self.parent = parent
\r
440 self.context = glcanvas.GLContext(self)
\r
441 wx.EVT_PAINT(self, self.OnPaint)
\r
442 wx.EVT_SIZE(self, self.OnSize)
\r
443 wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)
\r
444 wx.EVT_LEFT_DOWN(self, self.OnMouseLeftDown)
\r
445 wx.EVT_MOTION(self, self.OnMouseMotion)
\r
446 wx.EVT_MOUSEWHEEL(self, self.OnMouseWheel)
\r
449 self.zoom = self.parent.machineSize.x / 2 + 10
\r
452 self.view3D = False
\r
453 self.allowDrag = False
\r
455 def OnMouseLeftDown(self,e):
\r
456 self.allowDrag = True
\r
458 def OnMouseMotion(self,e):
\r
459 if self.allowDrag and e.Dragging() and e.LeftIsDown():
\r
461 self.yaw += e.GetX() - self.oldX
\r
462 self.pitch -= e.GetY() - self.oldY
\r
463 if self.pitch > 170:
\r
465 if self.pitch < 10:
\r
468 #self.offsetX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2
\r
469 #self.offsetY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2
\r
470 item = self.parent.selection
\r
472 item.centerX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2
\r
473 item.centerY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2
\r
474 if item.centerX < -item.getMinimum().x * item.scale + self.parent.extruderOffset[item.extruder].x:
\r
475 item.centerX = -item.getMinimum().x * item.scale + self.parent.extruderOffset[item.extruder].x
\r
476 if item.centerY < -item.getMinimum().y * item.scale + self.parent.extruderOffset[item.extruder].y:
\r
477 item.centerY = -item.getMinimum().y * item.scale + self.parent.extruderOffset[item.extruder].y
\r
478 if item.centerX > self.parent.machineSize.x + self.parent.extruderOffset[item.extruder].x - item.getMaximum().x * item.scale:
\r
479 item.centerX = self.parent.machineSize.x + self.parent.extruderOffset[item.extruder].x - item.getMaximum().x * item.scale
\r
480 if item.centerY > self.parent.machineSize.y + self.parent.extruderOffset[item.extruder].y - item.getMaximum().y * item.scale:
\r
481 item.centerY = self.parent.machineSize.y + self.parent.extruderOffset[item.extruder].y - item.getMaximum().y * item.scale
\r
484 self.allowDrag = False
\r
485 if e.Dragging() and e.RightIsDown():
\r
487 self.zoom += e.GetY() - self.oldY
\r
491 self.oldX = e.GetX()
\r
492 self.oldY = e.GetY()
\r
494 def OnMouseWheel(self,e):
\r
496 self.zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0
\r
497 if self.zoom < 1.0:
\r
501 def OnEraseBackground(self,event):
\r
502 #Workaround for windows background redraw flicker.
\r
505 def OnSize(self,event):
\r
508 def OnPaint(self,event):
\r
509 dc = wx.PaintDC(self)
\r
510 if not hasOpenGLlibs:
\r
512 dc.DrawText("No PyOpenGL installation found.\nNo preview window available.", 10, 10)
\r
514 self.SetCurrent(self.context)
\r
515 opengl.InitGL(self, self.view3D, self.zoom)
\r
517 glTranslate(0,0,-self.zoom)
\r
518 glRotate(-self.pitch, 1,0,0)
\r
519 glRotate(self.yaw, 0,0,1)
\r
520 if False: #self.parent.triangleMesh != None:
\r
521 glTranslate(0,0,-self.parent.triangleMesh.getMaximum().z / 2)
\r
523 glScale(1.0/self.zoom, 1.0/self.zoom, 1.0)
\r
524 glTranslate(self.offsetX, self.offsetY, 0.0)
\r
525 glTranslate(-self.parent.machineSize.x/2, -self.parent.machineSize.y/2, 0)
\r
531 machineSize = self.parent.machineSize
\r
532 opengl.DrawMachine(machineSize)
\r
533 extraSizeMin = self.parent.headSizeMin
\r
534 extraSizeMax = self.parent.headSizeMax
\r
535 if profile.getProfileSettingFloat('skirt_line_count') > 0:
\r
536 skirtSize = profile.getProfileSettingFloat('skirt_line_count') * profile.calculateEdgeWidth() + profile.getProfileSettingFloat('skirt_gap')
\r
537 extraSizeMin = extraSizeMin + util3d.Vector3(skirtSize, skirtSize, 0)
\r
538 extraSizeMax = extraSizeMax + util3d.Vector3(skirtSize, skirtSize, 0)
\r
540 for item in self.parent.list:
\r
541 item.validPlacement = True
\r
542 item.gotHit = False
\r
544 for idx1 in xrange(0, len(self.parent.list)):
\r
545 item = self.parent.list[idx1]
\r
546 iMin1 = item.getMinimum() * item.scale + util3d.Vector3(item.centerX, item.centerY, 0) - extraSizeMin - self.parent.extruderOffset[item.extruder]
\r
547 iMax1 = item.getMaximum() * item.scale + util3d.Vector3(item.centerX, item.centerY, 0) + extraSizeMax - self.parent.extruderOffset[item.extruder]
\r
548 for idx2 in xrange(0, idx1):
\r
549 item2 = self.parent.list[idx2]
\r
550 iMin2 = item2.getMinimum() * item2.scale + util3d.Vector3(item2.centerX, item2.centerY, 0)
\r
551 iMax2 = item2.getMaximum() * item2.scale + util3d.Vector3(item2.centerX, item2.centerY, 0)
\r
552 if item != item2 and iMax1.x >= iMin2.x and iMin1.x <= iMax2.x and iMax1.y >= iMin2.y and iMin1.y <= iMax2.y:
\r
553 item.validPlacement = False
\r
554 item2.gotHit = True
\r
556 seenSelected = False
\r
557 for item in self.parent.list:
\r
558 if item == self.parent.selection:
\r
559 seenSelected = True
\r
560 if item.modelDisplayList == None:
\r
561 item.modelDisplayList = glGenLists(1);
\r
562 if item.modelDirty:
\r
563 item.modelDirty = False
\r
564 modelSize = item.getMaximum() - item.getMinimum()
\r
565 glNewList(item.modelDisplayList, GL_COMPILE)
\r
566 opengl.DrawSTL(item)
\r
569 if item.validPlacement:
\r
570 if self.parent.selection == item:
\r
571 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.9, 0.7, 1.0])
\r
572 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.3, 0.2, 0.0])
\r
574 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.8, 0.6, 1.0])
\r
575 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.1, 0.1, 0.0])
\r
577 if self.parent.selection == item:
\r
578 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.0, 0.0, 0.0])
\r
579 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.0, 0.0, 0.0])
\r
581 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.0, 0.0, 0.0])
\r
582 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.0, 0.0, 0.0])
\r
585 glEnable(GL_LIGHTING)
\r
586 glTranslate(item.centerX, item.centerY, 0)
\r
588 glEnable(GL_NORMALIZE)
\r
589 glScalef(item.scale, item.scale, item.scale)
\r
590 glCallList(item.modelDisplayList)
\r
593 vMin = item.getMinimum() * item.scale
\r
594 vMax = item.getMaximum() * item.scale
\r
595 vMinHead = vMin - extraSizeMin - self.parent.extruderOffset[item.extruder]
\r
596 vMaxHead = vMax + extraSizeMax - self.parent.extruderOffset[item.extruder]
\r
598 glDisable(GL_LIGHTING)
\r
600 if self.parent.selection == item:
\r
602 glColor3f(1.0,0.0,0.3)
\r
604 glColor3f(1.0,0.0,1.0)
\r
605 opengl.DrawBox(vMin, vMax)
\r
607 glColor3f(1.0,0.3,0.0)
\r
609 glColor3f(1.0,1.0,0.0)
\r
610 opengl.DrawBox(vMinHead, vMaxHead)
\r
613 glColor3f(0.5,0.0,0.1)
\r
615 glColor3f(0.5,0.0,0.5)
\r
616 opengl.DrawBox(vMinHead, vMaxHead)
\r
619 glColor3f(0.7,0.1,0.0)
\r
621 glColor3f(0.7,0.7,0.0)
\r
622 opengl.DrawBox(vMin, vMax)
\r
628 class ProjectSliceProgressWindow(wx.Frame):
\r
629 def __init__(self, actionList, resultFilename):
\r
630 super(ProjectSliceProgressWindow, self).__init__(None, title='Cura')
\r
631 self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
\r
633 self.actionList = actionList
\r
634 self.resultFilename = resultFilename
\r
636 self.prevStep = 'start'
\r
637 self.totalDoneFactor = 0.0
\r
638 self.startTime = time.time()
\r
639 self.sliceStartTime = time.time()
\r
641 #How long does each step take compared to the others. This is used to make a better scaled progress bar, and guess time left.
\r
642 # TODO: Duplicate with sliceProgressPanel, move to sliceRun.
\r
643 self.sliceStepTimeFactor = {
\r
644 'start': 3.3713991642,
\r
645 'slice': 15.4984838963,
\r
646 'preface': 5.17178297043,
\r
647 'inset': 116.362634182,
\r
648 'fill': 215.702672005,
\r
649 'multiply': 21.9536788464,
\r
650 'speed': 12.759510994,
\r
651 'raft': 31.4580039978,
\r
652 'skirt': 19.3436040878,
\r
655 'comb': 23.7805759907,
\r
656 'cool': 27.148763895,
\r
657 'dimension': 90.4914340973
\r
659 self.totalRunTimeFactor = 0
\r
660 for v in self.sliceStepTimeFactor.itervalues():
\r
661 self.totalRunTimeFactor += v
\r
663 self.sizer = wx.GridBagSizer(2, 2)
\r
664 self.statusText = wx.StaticText(self, -1, "Building: %s" % (resultFilename))
\r
665 self.progressGauge = wx.Gauge(self, -1)
\r
666 self.progressGauge.SetRange(10000)
\r
667 self.progressGauge2 = wx.Gauge(self, -1)
\r
668 self.progressGauge2.SetRange(len(self.actionList))
\r
669 self.abortButton = wx.Button(self, -1, "Abort")
\r
670 self.sizer.Add(self.statusText, (0,0), flag=wx.ALIGN_CENTER)
\r
671 self.sizer.Add(self.progressGauge, (1, 0), flag=wx.EXPAND)
\r
672 self.sizer.Add(self.progressGauge2, (2, 0), flag=wx.EXPAND)
\r
673 self.sizer.Add(self.abortButton, (3,0), flag=wx.ALIGN_CENTER)
\r
674 self.sizer.AddGrowableCol(0)
\r
675 self.sizer.AddGrowableRow(0)
\r
677 self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton)
\r
678 self.SetSizer(self.sizer)
\r
682 threading.Thread(target=self.OnRun).start()
\r
684 def OnAbort(self, e):
\r
689 self.abortButton.SetLabel('Close')
\r
691 def SetProgress(self, stepName, layer, maxLayer):
\r
692 if self.prevStep != stepName:
\r
693 self.totalDoneFactor += self.sliceStepTimeFactor[self.prevStep]
\r
694 newTime = time.time()
\r
695 #print "#####" + str(newTime-self.startTime) + " " + self.prevStep + " -> " + stepName
\r
696 self.startTime = newTime
\r
697 self.prevStep = stepName
\r
699 progresValue = ((self.totalDoneFactor + self.sliceStepTimeFactor[stepName] * layer / maxLayer) / self.totalRunTimeFactor) * 10000
\r
700 self.progressGauge.SetValue(int(progresValue))
\r
701 self.statusText.SetLabel(stepName + " [" + str(layer) + "/" + str(maxLayer) + "]")
\r
704 resultFile = open(self.resultFilename, "w")
\r
705 put = profile.putProfileSetting
\r
706 for action in self.actionList:
\r
707 p = subprocess.Popen(action.sliceCmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
\r
708 line = p.stdout.readline()
\r
711 self.progressLog = []
\r
712 while(len(line) > 0):
\r
713 line = line.rstrip()
\r
714 if line[0:9] == "Progress[" and line[-1:] == "]":
\r
715 progress = line[9:-1].split(":")
\r
716 if len(progress) > 2:
\r
717 maxValue = int(progress[2])
\r
718 wx.CallAfter(self.SetProgress, progress[0], int(progress[1]), maxValue)
\r
721 self.progressLog.append(line)
\r
722 wx.CallAfter(self.statusText.SetLabel, line)
\r
725 wx.CallAfter(self.statusText.SetLabel, "Aborted by user.")
\r
727 line = p.stdout.readline()
\r
728 self.returnCode = p.wait()
\r
730 oldProfile = profile.getGlobalProfileString()
\r
731 put('machine_center_x', action.centerX - self.extruderOffset[action.extruder].x)
\r
732 put('machine_center_y', action.centerY - self.extruderOffset[action.extruder].y)
\r
733 put('clear_z', action.clearZ)
\r
734 put('extruder', action.extruder)
\r
736 if action == self.actionList[0]:
\r
737 resultFile.write(';TYPE:CUSTOM\n')
\r
738 resultFile.write('T%d\n' % (action.extruder))
\r
739 currentExtruder = action.extruder
\r
740 resultFile.write(profile.getAlterationFileContents('start.gcode'))
\r
742 #reset the extrusion length, and move to the next object center.
\r
743 resultFile.write(';TYPE:CUSTOM\n')
\r
744 resultFile.write(profile.getAlterationFileContents('nextobject.gcode'))
\r
745 resultFile.write(';PRINTNR:%d\n' % self.actionList.index(action))
\r
746 profile.loadGlobalProfileFromString(oldProfile)
\r
748 f = open(action.filename[: action.filename.rfind('.')] + "_export.project_tmp", "r")
\r
749 data = f.read(4096)
\r
751 resultFile.write(data)
\r
752 data = f.read(4096)
\r
754 os.remove(action.filename[: action.filename.rfind('.')] + "_export.project_tmp")
\r
756 wx.CallAfter(self.progressGauge.SetValue, 10000)
\r
757 self.totalDoneFactor = 0.0
\r
758 wx.CallAfter(self.progressGauge2.SetValue, self.actionList.index(action) + 1)
\r
760 resultFile.write(';TYPE:CUSTOM\n')
\r
761 resultFile.write(profile.getAlterationFileContents('end.gcode'))
\r
764 sliceTime = time.time() - self.sliceStartTime
\r
765 wx.CallAfter(self.statusText.SetLabel, 'Slicing took: %d:%d' % (sliceTime / 60, sliceTime % 60))
\r
766 wx.CallAfter(self.abortButton.SetLabel, 'Close')
\r
769 app = wx.App(False)
\r
770 projectPlanner().Show(True)
\r
773 if __name__ == '__main__':
\r