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
27 class Action(object):
\r
30 class ProjectObject(stl.stlModel):
\r
31 def __init__(self, filename):
\r
32 super(ProjectObject, self).__init__()
\r
36 self.filename = filename
\r
46 self.modelDisplayList = None
\r
47 self.modelDirty = False
\r
49 self.origonalVertexes = list(self.vertexes)
\r
50 for i in xrange(0, len(self.origonalVertexes)):
\r
51 self.origonalVertexes[i] = self.origonalVertexes[i].copy()
\r
54 self.centerX = -self.getMinimum().x + 5
\r
55 self.centerY = -self.getMinimum().y + 5
\r
57 self.updateModelTransform()
\r
59 self.centerX = -self.getMinimum().x + 5
\r
60 self.centerY = -self.getMinimum().y + 5
\r
62 def updateModelTransform(self):
\r
63 rotate = self.rotate / 180.0 * math.pi
\r
73 swapXZ = self.swapXZ
\r
74 swapYZ = self.swapYZ
\r
75 mat00 = math.cos(rotate) * scaleX
\r
76 mat01 =-math.sin(rotate) * scaleY
\r
77 mat10 = math.sin(rotate) * scaleX
\r
78 mat11 = math.cos(rotate) * scaleY
\r
80 for i in xrange(0, len(self.origonalVertexes)):
\r
81 x = self.origonalVertexes[i].x
\r
82 y = self.origonalVertexes[i].y
\r
83 z = self.origonalVertexes[i].z
\r
88 self.vertexes[i].x = x * mat00 + y * mat01
\r
89 self.vertexes[i].y = x * mat10 + y * mat11
\r
90 self.vertexes[i].z = z * scaleZ
\r
92 for face in self.faces:
\r
96 face.normal = (v2 - v1).cross(v3 - v1)
\r
97 face.normal.normalize()
\r
99 minZ = self.getMinimumZ()
\r
100 minV = self.getMinimum()
\r
101 maxV = self.getMaximum()
\r
102 for v in self.vertexes:
\r
104 v.x -= minV.x + (maxV.x - minV.x) / 2
\r
105 v.y -= minV.y + (maxV.y - minV.y) / 2
\r
107 self.modelDirty = True
\r
110 p = ProjectObject(self.filename)
\r
112 p.centerX = self.centerX + 5
\r
113 p.centerY = self.centerY + 5
\r
115 p.filename = self.filename
\r
116 p.scale = self.scale
\r
117 p.rotate = self.rotate
\r
118 p.flipX = self.flipX
\r
119 p.flipY = self.flipY
\r
120 p.flipZ = self.flipZ
\r
121 p.swapXZ = self.swapXZ
\r
122 p.swapYZ = self.swapYZ
\r
123 p.extruder = self.extruder
\r
125 p.updateModelTransform()
\r
129 class projectPlanner(wx.Frame):
\r
130 "Main user interface window"
\r
131 def __init__(self):
\r
132 super(projectPlanner, self).__init__(None, title='Cura - Project Planner')
\r
134 self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
\r
135 wx.EVT_CLOSE(self, self.OnClose)
\r
136 #self.SetIcon(icon.getMainIcon())
\r
139 self.selection = None
\r
141 self.machineSize = util3d.Vector3(profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height'))
\r
142 self.headSizeMin = util3d.Vector3(profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_min_y'),0)
\r
143 self.headSizeMax = util3d.Vector3(profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_max_y'),0)
\r
145 self.extruderOffset = [
\r
146 util3d.Vector3(0,0,0),
\r
147 util3d.Vector3(profile.getPreferenceFloat('extruder_offset_x1'), profile.getPreferenceFloat('extruder_offset_y1'), 0),
\r
148 util3d.Vector3(profile.getPreferenceFloat('extruder_offset_x2'), profile.getPreferenceFloat('extruder_offset_y2'), 0),
\r
149 util3d.Vector3(profile.getPreferenceFloat('extruder_offset_x3'), profile.getPreferenceFloat('extruder_offset_y3'), 0)]
\r
151 self.toolbar = toolbarUtil.Toolbar(self)
\r
153 toolbarUtil.NormalButton(self.toolbar, self.OnLoadProject, 'open.png', 'Open project')
\r
154 toolbarUtil.NormalButton(self.toolbar, self.OnSaveProject, 'save.png', 'Save project')
\r
155 self.toolbar.AddSeparator()
\r
157 toolbarUtil.RadioButton(self.toolbar, group, 'object-3d-on.png', 'object-3d-off.png', '3D view', callback=self.On3DClick)
\r
158 toolbarUtil.RadioButton(self.toolbar, group, 'object-top-on.png', 'object-top-off.png', 'Topdown view', callback=self.OnTopClick).SetValue(True)
\r
159 self.toolbar.AddSeparator()
\r
160 toolbarUtil.NormalButton(self.toolbar, self.OnQuit, 'exit.png', 'Close project planner')
\r
162 self.toolbar.Realize()
\r
164 self.toolbar2 = toolbarUtil.Toolbar(self)
\r
165 toolbarUtil.NormalButton(self.toolbar2, self.OnAddModel, 'object-add.png', 'Add model')
\r
166 toolbarUtil.NormalButton(self.toolbar2, self.OnRemModel, 'object-remove.png', 'Remove model')
\r
167 toolbarUtil.NormalButton(self.toolbar2, self.OnMoveUp, 'move-up.png', 'Move model up in print list')
\r
168 toolbarUtil.NormalButton(self.toolbar2, self.OnMoveDown, 'move-down.png', 'Move model down in print list')
\r
169 toolbarUtil.NormalButton(self.toolbar2, self.OnCopy, 'copy.png', 'Make a copy of the current selected object')
\r
170 self.toolbar2.Realize()
\r
172 sizer = wx.GridBagSizer(2,2)
\r
173 self.SetSizer(sizer)
\r
174 self.preview = PreviewGLCanvas(self)
\r
175 self.listbox = wx.ListBox(self, -1, choices=[])
\r
176 self.addButton = wx.Button(self, -1, "Add")
\r
177 self.remButton = wx.Button(self, -1, "Remove")
\r
178 self.sliceButton = wx.Button(self, -1, "Slice")
\r
179 self.autoPlaceButton = wx.Button(self, -1, "Auto Place")
\r
181 sizer.Add(self.toolbar, (0,0), span=(1,1), flag=wx.EXPAND)
\r
182 sizer.Add(self.toolbar2, (0,1), span=(1,2), flag=wx.EXPAND)
\r
183 sizer.Add(self.preview, (1,0), span=(4,1), flag=wx.EXPAND)
\r
184 sizer.Add(self.listbox, (1,1), span=(1,2), flag=wx.EXPAND)
\r
185 sizer.Add(self.addButton, (2,1), span=(1,1))
\r
186 sizer.Add(self.remButton, (2,2), span=(1,1))
\r
187 sizer.Add(self.sliceButton, (3,1), span=(1,1))
\r
188 sizer.Add(self.autoPlaceButton, (3,2), span=(1,1))
\r
189 sizer.AddGrowableCol(0)
\r
190 sizer.AddGrowableRow(1)
\r
192 self.addButton.Bind(wx.EVT_BUTTON, self.OnAddModel)
\r
193 self.remButton.Bind(wx.EVT_BUTTON, self.OnRemModel)
\r
194 self.sliceButton.Bind(wx.EVT_BUTTON, self.OnSlice)
\r
195 self.autoPlaceButton.Bind(wx.EVT_BUTTON, self.OnAutoPlace)
\r
196 self.listbox.Bind(wx.EVT_LISTBOX, self.OnListSelect)
\r
198 panel = wx.Panel(self, -1)
\r
199 sizer.Add(panel, (4,1), span=(1,2))
\r
201 sizer = wx.GridBagSizer(2,2)
\r
202 panel.SetSizer(sizer)
\r
204 self.scaleCtrl = wx.TextCtrl(panel, -1, '')
\r
205 self.rotateCtrl = wx.SpinCtrl(panel, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS)
\r
206 self.rotateCtrl.SetRange(0, 360)
\r
208 sizer.Add(wx.StaticText(panel, -1, 'Scale'), (0,0), flag=wx.ALIGN_CENTER_VERTICAL)
\r
209 sizer.Add(self.scaleCtrl, (0,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
\r
210 sizer.Add(wx.StaticText(panel, -1, 'Rotate'), (1,0), flag=wx.ALIGN_CENTER_VERTICAL)
\r
211 sizer.Add(self.rotateCtrl, (1,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
\r
213 if int(profile.getPreference('extruder_amount')) > 1:
\r
214 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
215 sizer.Add(wx.StaticText(panel, -1, 'Extruder'), (2,0), flag=wx.ALIGN_CENTER_VERTICAL)
\r
216 sizer.Add(self.extruderCtrl, (2,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
\r
217 self.extruderCtrl.Bind(wx.EVT_COMBOBOX, self.OnExtruderChange)
\r
219 self.scaleCtrl.Bind(wx.EVT_TEXT, self.OnScaleChange)
\r
220 self.rotateCtrl.Bind(wx.EVT_SPINCTRL, self.OnRotateChange)
\r
222 self.SetSize((800,600))
\r
224 def OnClose(self, e):
\r
227 def OnQuit(self, e):
\r
230 def OnSaveProject(self, e):
\r
231 dlg=wx.FileDialog(self, "Save project file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
\r
232 dlg.SetWildcard("Project files (*.curaproject)|*.curaproject")
\r
233 if dlg.ShowModal() == wx.ID_OK:
\r
234 cp = ConfigParser.ConfigParser()
\r
236 for item in self.list:
\r
237 section = 'model_%d' % (i)
\r
238 cp.add_section(section)
\r
239 cp.set(section, 'filename', item.filename.encode("utf-8"))
\r
240 cp.set(section, 'centerX', str(item.centerX))
\r
241 cp.set(section, 'centerY', str(item.centerY))
\r
242 cp.set(section, 'scale', str(item.scale))
\r
243 cp.set(section, 'rotate', str(item.rotate))
\r
244 cp.set(section, 'flipX', str(item.flipX))
\r
245 cp.set(section, 'flipY', str(item.flipY))
\r
246 cp.set(section, 'flipZ', str(item.flipZ))
\r
247 cp.set(section, 'swapXZ', str(item.swapXZ))
\r
248 cp.set(section, 'swapYZ', str(item.swapYZ))
\r
249 cp.set(section, 'extruder', str(item.extruder+1))
\r
251 cp.write(open(dlg.GetPath(), "w"))
\r
254 def OnLoadProject(self, e):
\r
255 dlg=wx.FileDialog(self, "Open project file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
\r
256 dlg.SetWildcard("Project files (*.curaproject)|*.curaproject")
\r
257 if dlg.ShowModal() == wx.ID_OK:
\r
258 cp = ConfigParser.ConfigParser()
\r
259 cp.read(dlg.GetPath())
\r
261 self.listbox.Clear()
\r
263 while cp.has_section('model_%d' % (i)):
\r
264 section = 'model_%d' % (i)
\r
266 item = ProjectObject(unicode(cp.get(section, 'filename'), "utf-8"))
\r
267 item.centerX = float(cp.get(section, 'centerX'))
\r
268 item.centerY = float(cp.get(section, 'centerY'))
\r
269 item.scale = float(cp.get(section, 'scale'))
\r
270 item.rotate = float(cp.get(section, 'rotate'))
\r
271 item.flipX = cp.get(section, 'flipX') == 'True'
\r
272 item.flipY = cp.get(section, 'flipY') == 'True'
\r
273 item.flipZ = cp.get(section, 'flipZ') == 'True'
\r
274 item.swapXZ = cp.get(section, 'swapXZ') == 'True'
\r
275 item.swapYZ = cp.get(section, 'swapYZ') == 'True'
\r
276 if cp.has_option(section, 'extruder'):
\r
277 item.extuder = int(cp.get(section, 'extruder'))-1
\r
278 item.updateModelTransform()
\r
281 self.list.append(item)
\r
282 self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1])
\r
284 self.listbox.SetSelection(len(self.list)-1)
\r
285 self.OnListSelect(None)
\r
286 self.preview.Refresh()
\r
290 def On3DClick(self):
\r
291 self.preview.yaw = 30
\r
292 self.preview.pitch = 60
\r
293 self.preview.zoom = 300
\r
294 self.preview.view3D = True
\r
295 self.preview.Refresh()
\r
297 def OnTopClick(self):
\r
298 self.preview.view3D = False
\r
299 self.preview.zoom = self.machineSize.x / 2 + 10
\r
300 self.preview.offsetX = 0
\r
301 self.preview.offsetY = 0
\r
302 self.preview.Refresh()
\r
304 def OnListSelect(self, e):
\r
305 if self.listbox.GetSelection() == -1:
\r
307 self.selection = self.list[self.listbox.GetSelection()]
\r
308 self.scaleCtrl.SetValue(str(self.selection.scale))
\r
309 self.rotateCtrl.SetValue(int(self.selection.rotate))
\r
310 if int(profile.getPreference('extruder_amount')) > 1:
\r
311 self.extruderCtrl.SetValue(str(self.selection.extruder+1))
\r
312 self.preview.Refresh()
\r
314 def OnAddModel(self, e):
\r
315 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
316 dlg.SetWildcard("STL files (*.stl)|*.stl;*.STL")
\r
317 if dlg.ShowModal() == wx.ID_OK:
\r
318 for filename in dlg.GetPaths():
\r
319 item = ProjectObject(filename)
\r
320 profile.putPreference('lastFile', item.filename)
\r
321 self.list.append(item)
\r
322 self.selection = item
\r
323 self._updateListbox()
\r
324 self.preview.Refresh()
\r
327 def OnRemModel(self, e):
\r
328 if self.selection == None:
\r
330 self.list.remove(self.selection)
\r
331 self._updateListbox()
\r
332 self.preview.Refresh()
\r
334 def OnMoveUp(self, e):
\r
335 if self.selection == None:
\r
337 i = self.listbox.GetSelection()
\r
340 self.list.remove(self.selection)
\r
341 self.list.insert(i-1, self.selection)
\r
342 self._updateListbox()
\r
343 self.preview.Refresh()
\r
345 def OnMoveDown(self, e):
\r
346 if self.selection == None:
\r
348 i = self.listbox.GetSelection()
\r
349 if i == len(self.list) - 1:
\r
351 self.list.remove(self.selection)
\r
352 self.list.insert(i+1, self.selection)
\r
353 self._updateListbox()
\r
354 self.preview.Refresh()
\r
356 def OnCopy(self, e):
\r
357 if self.selection == None:
\r
360 item = self.selection.clone()
\r
361 self.list.append(item)
\r
362 self.selection = item
\r
364 self._updateListbox()
\r
365 self.preview.Refresh()
\r
367 def _updateListbox(self):
\r
368 self.listbox.Clear()
\r
369 for item in self.list:
\r
370 self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1])
\r
371 if self.selection in self.list:
\r
372 self.listbox.SetSelection(self.list.index(self.selection))
\r
373 elif len(self.list) > 0:
\r
374 self.selection = self.list[0]
\r
375 self.listbox.SetSelection(0)
\r
377 self.selection = None
\r
378 self.listbox.SetSelection(-1)
\r
380 def OnAutoPlace(self, e):
\r
381 bestAllowedSize = int(self.machineSize.y)
\r
382 bestArea = self._doAutoPlace(bestAllowedSize)
\r
383 for i in xrange(10, int(self.machineSize.y), 10):
\r
384 area = self._doAutoPlace(i)
\r
385 if area < bestArea:
\r
386 bestAllowedSize = i
\r
388 self._doAutoPlace(bestAllowedSize)
\r
389 self.preview.Refresh()
\r
391 def _doAutoPlace(self, allowedSizeY):
\r
392 extraSizeMin = self.headSizeMin
\r
393 extraSizeMax = self.headSizeMax
\r
394 if profile.getProfileSettingFloat('skirt_line_count') > 0:
\r
395 skirtSize = profile.getProfileSettingFloat('skirt_line_count') * profile.calculateEdgeWidth() + profile.getProfileSettingFloat('skirt_gap')
\r
396 extraSizeMin = extraSizeMin + util3d.Vector3(skirtSize, skirtSize, 0)
\r
397 extraSizeMax = extraSizeMax + util3d.Vector3(skirtSize, skirtSize, 0)
\r
399 posX = self.machineSize.x
\r
401 minX = self.machineSize.x
\r
402 minY = self.machineSize.y
\r
407 for item in self.list:
\r
408 item.centerX = posX + item.getMaximum().x * item.scale * dirX
\r
409 item.centerY = posY + item.getMaximum().y * item.scale * dirY
\r
410 if item.centerY + item.getSize().y >= allowedSizeY:
\r
411 posX = minX - extraSizeMax.x - 1
\r
413 item.centerX = posX + item.getMaximum().x * item.scale * dirX
\r
414 item.centerY = posY + item.getMaximum().y * item.scale * dirY
\r
415 posY += item.getSize().y * item.scale * dirY + extraSizeMin.y + 1
\r
416 minX = min(minX, item.centerX - item.getSize().x * item.scale / 2)
\r
417 minY = min(minY, item.centerY - item.getSize().y * item.scale / 2)
\r
418 maxX = max(maxX, item.centerX + item.getSize().x * item.scale / 2)
\r
419 maxY = max(maxY, item.centerY + item.getSize().y * item.scale / 2)
\r
421 for item in self.list:
\r
422 item.centerX -= minX / 2
\r
423 item.centerY += (self.machineSize.y - maxY) / 2
\r
426 return ((maxX - minX) + (maxY - minY)) * 100
\r
428 return (maxX - minX) + (maxY - minY)
\r
430 def OnSlice(self, e):
\r
431 put = profile.setTempOverride
\r
433 put('model_multiply_x', '1')
\r
434 put('model_multiply_y', '1')
\r
435 put('enable_raft', 'False')
\r
436 put('add_start_end_gcode', 'False')
\r
437 put('gcode_extension', 'project_tmp')
\r
441 for item in self.list:
\r
442 put('machine_center_x', item.centerX - self.extruderOffset[item.extruder].x)
\r
443 put('machine_center_y', item.centerY - self.extruderOffset[item.extruder].y)
\r
444 put('model_scale', item.scale)
\r
445 put('flip_x', item.flipX)
\r
446 put('flip_y', item.flipY)
\r
447 put('flip_z', item.flipZ)
\r
448 put('model_rotate_base', item.rotate)
\r
449 put('swap_xz', item.swapXZ)
\r
450 put('swap_yz', item.swapYZ)
\r
453 action.sliceCmd = sliceRun.getSliceCommand(item.filename)
\r
454 action.centerX = item.centerX
\r
455 action.centerY = item.centerY
\r
456 action.extruder = item.extruder
\r
457 action.filename = item.filename
\r
458 clearZ = max(clearZ, item.getMaximum().z * item.scale)
\r
459 action.clearZ = clearZ
\r
460 actionList.append(action)
\r
462 #Restore the old profile.
\r
463 profile.resetTempOverride()
\r
465 dlg=wx.FileDialog(self, "Save project gcode file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
\r
466 dlg.SetWildcard("GCode file (*.gcode)|*.gcode")
\r
467 if dlg.ShowModal() != wx.ID_OK:
\r
470 resultFilename = dlg.GetPath()
\r
473 pspw = ProjectSliceProgressWindow(actionList, resultFilename)
\r
474 pspw.extruderOffset = self.extruderOffset
\r
478 def OnScaleChange(self, e):
\r
479 if self.selection == None:
\r
482 self.selection.scale = float(self.scaleCtrl.GetValue())
\r
484 self.selection.scale = 1.0
\r
485 self.preview.Refresh()
\r
487 def OnRotateChange(self, e):
\r
488 if self.selection == None:
\r
490 self.selection.rotate = float(self.rotateCtrl.GetValue())
\r
491 self.selection.updateModelTransform()
\r
492 self.preview.Refresh()
\r
494 def OnExtruderChange(self, e):
\r
495 if self.selection == None:
\r
497 self.selection.extruder = int(self.extruderCtrl.GetValue()) - 1
\r
498 self.preview.Refresh()
\r
500 class PreviewGLCanvas(glcanvas.GLCanvas):
\r
501 def __init__(self, parent):
\r
502 attribList = (glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24, glcanvas.WX_GL_STENCIL_SIZE, 8)
\r
503 glcanvas.GLCanvas.__init__(self, parent, attribList = attribList)
\r
504 self.parent = parent
\r
505 self.context = glcanvas.GLContext(self)
\r
506 wx.EVT_PAINT(self, self.OnPaint)
\r
507 wx.EVT_SIZE(self, self.OnSize)
\r
508 wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)
\r
509 wx.EVT_LEFT_DOWN(self, self.OnMouseLeftDown)
\r
510 wx.EVT_MOTION(self, self.OnMouseMotion)
\r
511 wx.EVT_MOUSEWHEEL(self, self.OnMouseWheel)
\r
514 self.zoom = self.parent.machineSize.x / 2 + 10
\r
517 self.view3D = False
\r
518 self.allowDrag = False
\r
520 def OnMouseLeftDown(self,e):
\r
521 self.allowDrag = True
\r
523 def OnMouseMotion(self,e):
\r
524 if self.allowDrag and e.Dragging() and e.LeftIsDown():
\r
526 self.yaw += e.GetX() - self.oldX
\r
527 self.pitch -= e.GetY() - self.oldY
\r
528 if self.pitch > 170:
\r
530 if self.pitch < 10:
\r
533 #self.offsetX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2
\r
534 #self.offsetY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2
\r
535 item = self.parent.selection
\r
537 item.centerX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2
\r
538 item.centerY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2
\r
539 if item.centerX < -item.getMinimum().x * item.scale + self.parent.extruderOffset[item.extruder].x:
\r
540 item.centerX = -item.getMinimum().x * item.scale + self.parent.extruderOffset[item.extruder].x
\r
541 if item.centerY < -item.getMinimum().y * item.scale + self.parent.extruderOffset[item.extruder].y:
\r
542 item.centerY = -item.getMinimum().y * item.scale + self.parent.extruderOffset[item.extruder].y
\r
543 if item.centerX > self.parent.machineSize.x + self.parent.extruderOffset[item.extruder].x - item.getMaximum().x * item.scale:
\r
544 item.centerX = self.parent.machineSize.x + self.parent.extruderOffset[item.extruder].x - item.getMaximum().x * item.scale
\r
545 if item.centerY > self.parent.machineSize.y + self.parent.extruderOffset[item.extruder].y - item.getMaximum().y * item.scale:
\r
546 item.centerY = self.parent.machineSize.y + self.parent.extruderOffset[item.extruder].y - item.getMaximum().y * item.scale
\r
549 self.allowDrag = False
\r
550 if e.Dragging() and e.RightIsDown():
\r
552 self.zoom += e.GetY() - self.oldY
\r
556 self.oldX = e.GetX()
\r
557 self.oldY = e.GetY()
\r
559 def OnMouseWheel(self,e):
\r
561 self.zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0
\r
562 if self.zoom < 1.0:
\r
566 def OnEraseBackground(self,event):
\r
567 #Workaround for windows background redraw flicker.
\r
570 def OnSize(self,event):
\r
573 def OnPaint(self,event):
\r
574 dc = wx.PaintDC(self)
\r
575 if not hasOpenGLlibs:
\r
577 dc.DrawText("No PyOpenGL installation found.\nNo preview window available.", 10, 10)
\r
579 self.SetCurrent(self.context)
\r
580 opengl.InitGL(self, self.view3D, self.zoom)
\r
582 glTranslate(0,0,-self.zoom)
\r
583 glRotate(-self.pitch, 1,0,0)
\r
584 glRotate(self.yaw, 0,0,1)
\r
585 if False: #self.parent.triangleMesh != None:
\r
586 glTranslate(0,0,-self.parent.triangleMesh.getMaximum().z / 2)
\r
588 glScale(1.0/self.zoom, 1.0/self.zoom, 1.0)
\r
589 glTranslate(self.offsetX, self.offsetY, 0.0)
\r
590 glTranslate(-self.parent.machineSize.x/2, -self.parent.machineSize.y/2, 0)
\r
596 machineSize = self.parent.machineSize
\r
597 opengl.DrawMachine(machineSize)
\r
598 extraSizeMin = self.parent.headSizeMin
\r
599 extraSizeMax = self.parent.headSizeMax
\r
600 if profile.getProfileSettingFloat('skirt_line_count') > 0:
\r
601 skirtSize = profile.getProfileSettingFloat('skirt_line_count') * profile.calculateEdgeWidth() + profile.getProfileSettingFloat('skirt_gap')
\r
602 extraSizeMin = extraSizeMin + util3d.Vector3(skirtSize, skirtSize, 0)
\r
603 extraSizeMax = extraSizeMax + util3d.Vector3(skirtSize, skirtSize, 0)
\r
605 for item in self.parent.list:
\r
606 item.validPlacement = True
\r
607 item.gotHit = False
\r
609 for idx1 in xrange(0, len(self.parent.list)):
\r
610 item = self.parent.list[idx1]
\r
611 iMin1 = item.getMinimum() * item.scale + util3d.Vector3(item.centerX, item.centerY, 0) - extraSizeMin - self.parent.extruderOffset[item.extruder]
\r
612 iMax1 = item.getMaximum() * item.scale + util3d.Vector3(item.centerX, item.centerY, 0) + extraSizeMax - self.parent.extruderOffset[item.extruder]
\r
613 for idx2 in xrange(0, idx1):
\r
614 item2 = self.parent.list[idx2]
\r
615 iMin2 = item2.getMinimum() * item2.scale + util3d.Vector3(item2.centerX, item2.centerY, 0)
\r
616 iMax2 = item2.getMaximum() * item2.scale + util3d.Vector3(item2.centerX, item2.centerY, 0)
\r
617 if item != item2 and iMax1.x >= iMin2.x and iMin1.x <= iMax2.x and iMax1.y >= iMin2.y and iMin1.y <= iMax2.y:
\r
618 item.validPlacement = False
\r
619 item2.gotHit = True
\r
621 seenSelected = False
\r
622 for item in self.parent.list:
\r
623 if item == self.parent.selection:
\r
624 seenSelected = True
\r
625 if item.modelDisplayList == None:
\r
626 item.modelDisplayList = glGenLists(1);
\r
627 if item.modelDirty:
\r
628 item.modelDirty = False
\r
629 modelSize = item.getMaximum() - item.getMinimum()
\r
630 glNewList(item.modelDisplayList, GL_COMPILE)
\r
631 opengl.DrawSTL(item)
\r
634 if item.validPlacement:
\r
635 if self.parent.selection == item:
\r
636 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.9, 0.7, 1.0])
\r
637 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.3, 0.2, 0.0])
\r
639 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.8, 0.6, 1.0])
\r
640 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.1, 0.1, 0.0])
\r
642 if self.parent.selection == item:
\r
643 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.0, 0.0, 0.0])
\r
644 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.0, 0.0, 0.0])
\r
646 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.0, 0.0, 0.0])
\r
647 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.0, 0.0, 0.0])
\r
650 glEnable(GL_LIGHTING)
\r
651 glTranslate(item.centerX, item.centerY, 0)
\r
653 glEnable(GL_NORMALIZE)
\r
654 glScalef(item.scale, item.scale, item.scale)
\r
655 glCallList(item.modelDisplayList)
\r
658 vMin = item.getMinimum() * item.scale
\r
659 vMax = item.getMaximum() * item.scale
\r
660 vMinHead = vMin - extraSizeMin - self.parent.extruderOffset[item.extruder]
\r
661 vMaxHead = vMax + extraSizeMax - self.parent.extruderOffset[item.extruder]
\r
663 glDisable(GL_LIGHTING)
\r
665 if self.parent.selection == item:
\r
667 glColor3f(1.0,0.0,0.3)
\r
669 glColor3f(1.0,0.0,1.0)
\r
670 opengl.DrawBox(vMin, vMax)
\r
672 glColor3f(1.0,0.3,0.0)
\r
674 glColor3f(1.0,1.0,0.0)
\r
675 opengl.DrawBox(vMinHead, vMaxHead)
\r
678 glColor3f(0.5,0.0,0.1)
\r
680 glColor3f(0.5,0.0,0.5)
\r
681 opengl.DrawBox(vMinHead, vMaxHead)
\r
684 glColor3f(0.7,0.1,0.0)
\r
686 glColor3f(0.7,0.7,0.0)
\r
687 opengl.DrawBox(vMin, vMax)
\r
693 class ProjectSliceProgressWindow(wx.Frame):
\r
694 def __init__(self, actionList, resultFilename):
\r
695 super(ProjectSliceProgressWindow, self).__init__(None, title='Cura')
\r
696 self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
\r
698 self.actionList = actionList
\r
699 self.resultFilename = resultFilename
\r
701 self.prevStep = 'start'
\r
702 self.totalDoneFactor = 0.0
\r
703 self.startTime = time.time()
\r
704 self.sliceStartTime = time.time()
\r
706 self.sizer = wx.GridBagSizer(2, 2)
\r
707 self.statusText = wx.StaticText(self, -1, "Building: %s" % (resultFilename))
\r
708 self.progressGauge = wx.Gauge(self, -1)
\r
709 self.progressGauge.SetRange(10000)
\r
710 self.progressGauge2 = wx.Gauge(self, -1)
\r
711 self.progressGauge2.SetRange(len(self.actionList))
\r
712 self.abortButton = wx.Button(self, -1, "Abort")
\r
713 self.sizer.Add(self.statusText, (0,0), flag=wx.ALIGN_CENTER)
\r
714 self.sizer.Add(self.progressGauge, (1, 0), flag=wx.EXPAND)
\r
715 self.sizer.Add(self.progressGauge2, (2, 0), flag=wx.EXPAND)
\r
717 self.sizer.Add(self.abortButton, (3,0), flag=wx.ALIGN_CENTER)
\r
718 self.sizer.AddGrowableCol(0)
\r
719 self.sizer.AddGrowableRow(0)
\r
721 self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton)
\r
722 self.SetSizer(self.sizer)
\r
726 threading.Thread(target=self.OnRun).start()
\r
728 def OnAbort(self, e):
\r
733 self.abortButton.SetLabel('Close')
\r
735 def SetProgress(self, stepName, layer, maxLayer):
\r
736 if self.prevStep != stepName:
\r
737 self.totalDoneFactor += sliceRun.sliceStepTimeFactor[self.prevStep]
\r
738 newTime = time.time()
\r
739 #print "#####" + str(newTime-self.startTime) + " " + self.prevStep + " -> " + stepName
\r
740 self.startTime = newTime
\r
741 self.prevStep = stepName
\r
743 progresValue = ((self.totalDoneFactor + sliceRun.sliceStepTimeFactor[stepName] * layer / maxLayer) / sliceRun.totalRunTimeFactor) * 10000
\r
744 self.progressGauge.SetValue(int(progresValue))
\r
745 self.statusText.SetLabel(stepName + " [" + str(layer) + "/" + str(maxLayer) + "]")
\r
748 resultFile = open(self.resultFilename, "w")
\r
749 put = profile.setTempOverride
\r
750 for action in self.actionList:
\r
751 p = subprocess.Popen(action.sliceCmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
\r
752 line = p.stdout.readline()
\r
755 self.progressLog = []
\r
756 while(len(line) > 0):
\r
757 line = line.rstrip()
\r
758 if line[0:9] == "Progress[" and line[-1:] == "]":
\r
759 progress = line[9:-1].split(":")
\r
760 if len(progress) > 2:
\r
761 maxValue = int(progress[2])
\r
762 wx.CallAfter(self.SetProgress, progress[0], int(progress[1]), maxValue)
\r
765 self.progressLog.append(line)
\r
766 wx.CallAfter(self.statusText.SetLabel, line)
\r
769 wx.CallAfter(self.statusText.SetLabel, "Aborted by user.")
\r
771 line = p.stdout.readline()
\r
772 self.returnCode = p.wait()
\r
774 put('machine_center_x', action.centerX - self.extruderOffset[action.extruder].x)
\r
775 put('machine_center_y', action.centerY - self.extruderOffset[action.extruder].y)
\r
776 put('clear_z', action.clearZ)
\r
777 put('extruder', action.extruder)
\r
779 if action == self.actionList[0]:
\r
780 resultFile.write(';TYPE:CUSTOM\n')
\r
781 resultFile.write('T%d\n' % (action.extruder))
\r
782 currentExtruder = action.extruder
\r
783 resultFile.write(profile.getAlterationFileContents('start.gcode'))
\r
785 #reset the extrusion length, and move to the next object center.
\r
786 resultFile.write(';TYPE:CUSTOM\n')
\r
787 resultFile.write(profile.getAlterationFileContents('nextobject.gcode'))
\r
788 resultFile.write(';PRINTNR:%d\n' % self.actionList.index(action))
\r
789 profile.resetTempOverride()
\r
791 f = open(action.filename[: action.filename.rfind('.')] + "_export.project_tmp", "r")
\r
792 data = f.read(4096)
\r
794 resultFile.write(data)
\r
795 data = f.read(4096)
\r
797 os.remove(action.filename[: action.filename.rfind('.')] + "_export.project_tmp")
\r
799 wx.CallAfter(self.progressGauge.SetValue, 10000)
\r
800 self.totalDoneFactor = 0.0
\r
801 wx.CallAfter(self.progressGauge2.SetValue, self.actionList.index(action) + 1)
\r
803 resultFile.write(';TYPE:CUSTOM\n')
\r
804 resultFile.write(profile.getAlterationFileContents('end.gcode'))
\r
807 sliceTime = time.time() - self.sliceStartTime
\r
808 wx.CallAfter(self.statusText.SetLabel, 'Slicing took: %02d:%02d' % (sliceTime / 60, sliceTime % 60))
\r
809 wx.CallAfter(self.abortButton.SetLabel, 'Close')
\r
812 app = wx.App(False)
\r
813 projectPlanner().Show(True)
\r
816 if __name__ == '__main__':
\r