1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
13 OpenGL.ERROR_CHECKING = False
14 from OpenGL.GLU import *
15 from OpenGL.GL import *
17 from Cura.gui import printWindow
18 from Cura.util import profile
19 from Cura.util import meshLoader
20 from Cura.util import objectScene
21 from Cura.util import resources
22 from Cura.util import sliceEngine
23 from Cura.util import machineCom
24 from Cura.util import removableStorage
25 from Cura.util import gcodeInterpreter
26 from Cura.gui.util import previewTools
27 from Cura.gui.util import opengl
28 from Cura.gui.util import openglGui
29 from Cura.gui.tools import youmagineGui
31 class SceneView(openglGui.glGuiPanel):
32 def __init__(self, parent):
33 super(SceneView, self).__init__(parent)
38 self._scene = objectScene.Scene()
41 self._gcodeFilename = None
42 self._gcodeLoadThread = None
43 self._objectShader = None
44 self._objectLoadShader = None
46 self._selectedObj = None
47 self._objColors = [None,None,None,None]
50 self._mouseState = None
51 self._viewTarget = numpy.array([0,0,0], numpy.float32)
54 self._platformMesh = None
55 self._isSimpleMode = True
56 self._usbPrintMonitor = printWindow.printProcessMonitor(lambda : self._queueRefresh())
59 self._modelMatrix = None
60 self._projMatrix = None
61 self.tempMatrix = None
63 self.openFileButton = openglGui.glButton(self, 4, 'Load', (0,0), self.showLoadModel)
64 self.printButton = openglGui.glButton(self, 6, 'Print', (1,0), self.OnPrintButton)
65 self.printButton.setDisabled(True)
68 self.rotateToolButton = openglGui.glRadioButton(self, 8, 'Rotate', (0,-1), group, self.OnToolSelect)
69 self.scaleToolButton = openglGui.glRadioButton(self, 9, 'Scale', (1,-1), group, self.OnToolSelect)
70 self.mirrorToolButton = openglGui.glRadioButton(self, 10, 'Mirror', (2,-1), group, self.OnToolSelect)
72 self.resetRotationButton = openglGui.glButton(self, 12, 'Reset', (0,-2), self.OnRotateReset)
73 self.layFlatButton = openglGui.glButton(self, 16, 'Lay flat', (0,-3), self.OnLayFlat)
75 self.resetScaleButton = openglGui.glButton(self, 13, 'Reset', (1,-2), self.OnScaleReset)
76 self.scaleMaxButton = openglGui.glButton(self, 17, 'To max', (1,-3), self.OnScaleMax)
78 self.mirrorXButton = openglGui.glButton(self, 14, 'Mirror X', (2,-2), lambda button: self.OnMirror(0))
79 self.mirrorYButton = openglGui.glButton(self, 18, 'Mirror Y', (2,-3), lambda button: self.OnMirror(1))
80 self.mirrorZButton = openglGui.glButton(self, 22, 'Mirror Z', (2,-4), lambda button: self.OnMirror(2))
82 self.rotateToolButton.setExpandArrow(True)
83 self.scaleToolButton.setExpandArrow(True)
84 self.mirrorToolButton.setExpandArrow(True)
86 self.scaleForm = openglGui.glFrame(self, (2, -2))
87 openglGui.glGuiLayoutGrid(self.scaleForm)
88 openglGui.glLabel(self.scaleForm, 'Scale X', (0,0))
89 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
90 openglGui.glLabel(self.scaleForm, 'Scale Y', (0,1))
91 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
92 openglGui.glLabel(self.scaleForm, 'Scale Z', (0,2))
93 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
94 openglGui.glLabel(self.scaleForm, 'Size X (mm)', (0,4))
95 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
96 openglGui.glLabel(self.scaleForm, 'Size Y (mm)', (0,5))
97 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
98 openglGui.glLabel(self.scaleForm, 'Size Z (mm)', (0,6))
99 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
100 openglGui.glLabel(self.scaleForm, 'Uniform scale', (0,8))
101 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
103 self.viewSelection = openglGui.glComboButton(self, 'View mode', [7,19,11,15,23], ['Normal', 'Overhang', 'Transparent', 'X-Ray', 'Layers'], (-1,0), self.OnViewChange)
104 self.layerSelect = openglGui.glSlider(self, 10000, 0, 1, (-1,-2), lambda : self.QueueRefresh())
106 self.youMagineButton = openglGui.glButton(self, 26, 'YouMagine upload', (2,0), lambda button: youmagineGui.youmagineManager(self.GetTopLevelParent(), self._scene))
107 self.youMagineButton.setDisabled(True)
109 self.notification = openglGui.glNotification(self, (0, 0))
111 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
112 self._sceneUpdateTimer = wx.Timer(self)
113 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
114 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
115 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
119 self.updateToolButtons()
120 self.updateProfileToControls()
122 def showLoadModel(self, button = 1):
124 dlg=wx.FileDialog(self, 'Open 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST|wx.FD_MULTIPLE)
125 dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
126 if dlg.ShowModal() != wx.ID_OK:
129 filenames = dlg.GetPaths()
131 if len(filenames) < 1:
133 profile.putPreference('lastFile', filenames[0])
135 for filename in filenames:
136 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
137 ext = filename[filename.rfind('.')+1:].upper()
138 if ext == 'G' or ext == 'GCODE':
139 gcodeFilename = filename
140 if gcodeFilename is not None:
141 if self._gcode is not None:
143 for layerVBOlist in self._gcodeVBOs:
144 for vbo in layerVBOlist:
145 self.glReleaseList.append(vbo)
147 self._gcode = gcodeInterpreter.gcode()
148 self._gcodeFilename = gcodeFilename
149 self.printButton.setBottomText('')
150 self.viewSelection.setValue(4)
151 self.printButton.setDisabled(False)
152 self.youMagineButton.setDisabled(True)
155 self.youMagineButton.setDisabled(False)
156 if self.viewSelection.getValue() == 4:
157 self.viewSelection.setValue(0)
159 self.loadScene(filenames)
161 def showSaveModel(self):
162 if len(self._scene.objects()) < 1:
164 dlg=wx.FileDialog(self, 'Save 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
165 dlg.SetWildcard(meshLoader.saveWildcardFilter())
166 if dlg.ShowModal() != wx.ID_OK:
169 filename = dlg.GetPath()
171 meshLoader.saveMeshes(filename, self._scene.objects())
173 def OnPrintButton(self, button):
175 if machineCom.machineIsConnected():
176 self.showPrintWindow()
177 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
178 drives = removableStorage.getPossibleSDcardDrives()
180 dlg = wx.SingleChoiceDialog(self, "Select SD drive", "Multiple removable drives have been found,\nplease select your SD card drive", map(lambda n: n[0], drives))
181 if dlg.ShowModal() != wx.ID_OK:
184 drive = drives[dlg.GetSelection()]
188 filename = self._scene._objectList[0].getName() + '.gcode'
189 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
194 self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, 'Print with USB'))
195 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, 'Save GCode...'))
196 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, 'Slice engine log...'))
200 def showPrintWindow(self):
201 if self._gcodeFilename is None:
203 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
204 if self._gcodeFilename == self._slicer.getGCodeFilename():
205 self._slicer.submitSliceInfoOnline()
207 def showSaveGCode(self):
208 defPath = profile.getPreference('lastFile')
209 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
210 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
211 dlg.SetFilename(self._scene._objectList[0].getName())
212 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
213 if dlg.ShowModal() != wx.ID_OK:
216 filename = dlg.GetPath()
219 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
221 def _copyFile(self, fileA, fileB, allowEject = False):
223 size = float(os.stat(fileA).st_size)
224 with open(fileA, 'rb') as fsrc:
225 with open(fileB, 'wb') as fdst:
227 buf = fsrc.read(16*1024)
231 self.printButton.setProgressBar(float(fsrc.tell()) / size)
236 self.notification.message("Failed to save")
239 self.notification.message("Saved as %s" % (fileB), lambda : self.notification.message('You can now eject the card.') if removableStorage.ejectDrive(allowEject) else self.notification.message('Safe remove failed...'))
241 self.notification.message("Saved as %s" % (fileB))
242 self.printButton.setProgressBar(None)
243 if fileA == self._slicer.getGCodeFilename():
244 self._slicer.submitSliceInfoOnline()
246 def _showSliceLog(self):
247 dlg = wx.TextEntryDialog(self, "The slicing engine reported the following", "Engine log...", '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
251 def OnToolSelect(self, button):
252 if self.rotateToolButton.getSelected():
253 self.tool = previewTools.toolRotate(self)
254 elif self.scaleToolButton.getSelected():
255 self.tool = previewTools.toolScale(self)
256 elif self.mirrorToolButton.getSelected():
257 self.tool = previewTools.toolNone(self)
259 self.tool = previewTools.toolNone(self)
260 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
261 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
262 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
263 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
264 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
265 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
266 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
267 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
269 def updateToolButtons(self):
270 if self._selectedObj is None:
274 self.rotateToolButton.setHidden(hidden)
275 self.scaleToolButton.setHidden(hidden)
276 self.mirrorToolButton.setHidden(hidden)
278 self.rotateToolButton.setSelected(False)
279 self.scaleToolButton.setSelected(False)
280 self.mirrorToolButton.setSelected(False)
283 def OnViewChange(self):
284 if self.viewSelection.getValue() == 4:
285 self.viewMode = 'gcode'
286 if self._gcode is not None and self._gcode.layerList is not None:
287 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
288 self._selectObject(None)
289 elif self.viewSelection.getValue() == 1:
290 self.viewMode = 'overhang'
291 elif self.viewSelection.getValue() == 2:
292 self.viewMode = 'transparent'
293 elif self.viewSelection.getValue() == 3:
294 self.viewMode = 'xray'
296 self.viewMode = 'normal'
297 self.layerSelect.setHidden(self.viewMode != 'gcode')
300 def OnRotateReset(self, button):
301 if self._selectedObj is None:
303 self._selectedObj.resetRotation()
304 self._scene.pushFree()
305 self._selectObject(self._selectedObj)
308 def OnLayFlat(self, button):
309 if self._selectedObj is None:
311 self._selectedObj.layFlat()
312 self._scene.pushFree()
313 self._selectObject(self._selectedObj)
316 def OnScaleReset(self, button):
317 if self._selectedObj is None:
319 self._selectedObj.resetScale()
320 self._selectObject(self._selectedObj)
321 self.updateProfileToControls()
324 def OnScaleMax(self, button):
325 if self._selectedObj is None:
327 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
328 self._scene.pushFree()
329 self._selectObject(self._selectedObj)
330 self.updateProfileToControls()
333 def OnMirror(self, axis):
334 if self._selectedObj is None:
336 self._selectedObj.mirror(axis)
339 def OnScaleEntry(self, value, axis):
340 if self._selectedObj is None:
346 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
347 self.updateProfileToControls()
348 self._scene.pushFree()
349 self._selectObject(self._selectedObj)
352 def OnScaleEntryMM(self, value, axis):
353 if self._selectedObj is None:
359 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
360 self.updateProfileToControls()
361 self._scene.pushFree()
362 self._selectObject(self._selectedObj)
365 def OnDeleteAll(self, e):
366 while len(self._scene.objects()) > 0:
367 self._deleteObject(self._scene.objects()[0])
368 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
370 def OnMultiply(self, e):
371 if self._focusObj is None:
374 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
375 if dlg.ShowModal() != wx.ID_OK:
384 self._scene.add(newObj)
385 self._scene.centerAll()
386 if not self._scene.checkPlatform(newObj):
391 self.notification.message("Could not create more then %d items" % (n - 1))
392 self._scene.remove(newObj)
393 self._scene.centerAll()
396 def OnSplitObject(self, e):
397 if self._focusObj is None:
399 self._scene.remove(self._focusObj)
400 for obj in self._focusObj.split(self._splitCallback):
401 if numpy.max(obj.getSize()) > 2.0:
403 self._scene.centerAll()
404 self._selectObject(None)
407 def _splitCallback(self, progress):
410 def OnMergeObjects(self, e):
411 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
413 self._scene.merge(self._selectedObj, self._focusObj)
416 def sceneUpdated(self):
417 self._sceneUpdateTimer.Start(500, True)
418 self._slicer.abortSlicer()
419 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
422 def _onRunSlicer(self, e):
423 if self._isSimpleMode:
424 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
425 self._slicer.runSlicer(self._scene)
426 if self._isSimpleMode:
427 profile.resetTempOverride()
429 def _updateSliceProgress(self, progressValue, ready):
431 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
433 self.printButton.setDisabled(not ready)
434 if progressValue >= 0.0:
435 self.printButton.setProgressBar(progressValue)
437 self.printButton.setProgressBar(None)
438 if self._gcode is not None:
440 for layerVBOlist in self._gcodeVBOs:
441 for vbo in layerVBOlist:
442 self.glReleaseList.append(vbo)
445 self.printButton.setProgressBar(None)
446 cost = self._slicer.getFilamentCost()
448 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
450 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
451 self._gcode = gcodeInterpreter.gcode()
452 self._gcodeFilename = self._slicer.getGCodeFilename()
454 self.printButton.setBottomText('')
457 def _loadGCode(self):
458 self._gcode.progressCallback = self._gcodeLoadCallback
459 self._gcode.load(self._gcodeFilename)
461 def _gcodeLoadCallback(self, progress):
462 if self._gcode is None:
464 if len(self._gcode.layerList) % 15 == 0:
466 if self._gcode is None:
468 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
469 if self.viewMode == 'gcode':
473 def loadScene(self, fileList):
474 for filename in fileList:
476 objList = meshLoader.loadMeshes(filename)
478 traceback.print_exc()
481 if self._objectLoadShader is not None:
482 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
486 self._scene.centerAll()
487 self._selectObject(obj)
488 if obj.getScale()[0] < 1.0:
489 self.notification.message("Warning: Object scaled down.")
492 def _deleteObject(self, obj):
493 if obj == self._selectedObj:
494 self._selectObject(None)
495 if obj == self._focusObj:
496 self._focusObj = None
497 self._scene.remove(obj)
498 for m in obj._meshList:
499 if m.vbo is not None and m.vbo.decRef():
500 self.glReleaseList.append(m.vbo)
505 def _selectObject(self, obj, zoom = True):
506 if obj != self._selectedObj:
507 self._selectedObj = obj
508 self.updateProfileToControls()
509 self.updateToolButtons()
510 if zoom and obj is not None:
511 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
512 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
513 newZoom = obj.getBoundaryCircle() * 6
514 if newZoom > numpy.max(self._machineSize) * 3:
515 newZoom = numpy.max(self._machineSize) * 3
516 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
518 def updateProfileToControls(self):
519 oldSimpleMode = self._isSimpleMode
520 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
521 if self._isSimpleMode != oldSimpleMode:
522 self._scene.arrangeAll()
524 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
525 self._objColors[0] = profile.getPreferenceColour('model_colour')
526 self._objColors[1] = profile.getPreferenceColour('model_colour2')
527 self._objColors[2] = profile.getPreferenceColour('model_colour3')
528 self._objColors[3] = profile.getPreferenceColour('model_colour4')
529 self._scene.setMachineSize(self._machineSize)
530 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
531 self._scene.setHeadSize(profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_min_y'), profile.getPreferenceFloat('extruder_head_size_max_y'), profile.getPreferenceFloat('extruder_head_size_height'))
533 if self._selectedObj is not None:
534 scale = self._selectedObj.getScale()
535 size = self._selectedObj.getSize()
536 self.scaleXctrl.setValue(round(scale[0], 2))
537 self.scaleYctrl.setValue(round(scale[1], 2))
538 self.scaleZctrl.setValue(round(scale[2], 2))
539 self.scaleXmmctrl.setValue(round(size[0], 2))
540 self.scaleYmmctrl.setValue(round(size[1], 2))
541 self.scaleZmmctrl.setValue(round(size[2], 2))
543 def OnKeyChar(self, keyCode):
544 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
545 if self._selectedObj is not None:
546 self._deleteObject(self._selectedObj)
548 if keyCode == wx.WXK_UP:
549 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
551 elif keyCode == wx.WXK_DOWN:
552 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
554 elif keyCode == wx.WXK_PAGEUP:
555 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
557 elif keyCode == wx.WXK_PAGEDOWN:
558 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
561 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
562 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
563 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
564 from collections import defaultdict
565 from gc import get_objects
566 self._beforeLeakTest = defaultdict(int)
567 for i in get_objects():
568 self._beforeLeakTest[type(i)] += 1
569 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
570 from collections import defaultdict
571 from gc import get_objects
572 self._afterLeakTest = defaultdict(int)
573 for i in get_objects():
574 self._afterLeakTest[type(i)] += 1
575 for k in self._afterLeakTest:
576 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
577 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
579 def ShaderUpdate(self, v, f):
580 s = opengl.GLShader(v, f)
582 self._objectLoadShader.release()
583 self._objectLoadShader = s
584 for obj in self._scene.objects():
585 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
588 def OnMouseDown(self,e):
589 self._mouseX = e.GetX()
590 self._mouseY = e.GetY()
591 self._mouseClick3DPos = self._mouse3Dpos
592 self._mouseClickFocus = self._focusObj
594 self._mouseState = 'doubleClick'
596 self._mouseState = 'dragOrClick'
597 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
598 p0 -= self.getObjectCenterPos() - self._viewTarget
599 p1 -= self.getObjectCenterPos() - self._viewTarget
600 if self.tool.OnDragStart(p0, p1):
601 self._mouseState = 'tool'
602 if self._mouseState == 'dragOrClick':
603 if e.GetButton() == 1:
604 if self._focusObj is not None:
605 self._selectObject(self._focusObj, False)
608 def OnMouseUp(self, e):
609 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
611 if self._mouseState == 'dragOrClick':
612 if e.GetButton() == 1:
613 self._selectObject(self._focusObj)
614 if e.GetButton() == 3:
616 if self._focusObj is not None:
617 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
618 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
619 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
620 if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1:
621 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
622 if len(self._scene.objects()) > 0:
623 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
624 if menu.MenuItemCount > 0:
627 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
628 self._scene.pushFree()
630 elif self._mouseState == 'tool':
631 if self.tempMatrix is not None and self._selectedObj is not None:
632 self._selectedObj.applyMatrix(self.tempMatrix)
633 self._scene.pushFree()
634 self._selectObject(self._selectedObj)
635 self.tempMatrix = None
636 self.tool.OnDragEnd()
638 self._mouseState = None
640 def OnMouseMotion(self,e):
641 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
642 p0 -= self.getObjectCenterPos() - self._viewTarget
643 p1 -= self.getObjectCenterPos() - self._viewTarget
645 if e.Dragging() and self._mouseState is not None:
646 if self._mouseState == 'tool':
647 self.tool.OnDrag(p0, p1)
648 elif not e.LeftIsDown() and e.RightIsDown():
649 self._mouseState = 'drag'
650 if wx.GetKeyState(wx.WXK_SHIFT):
651 a = math.cos(math.radians(self._yaw)) / 3.0
652 b = math.sin(math.radians(self._yaw)) / 3.0
653 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
654 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
655 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
656 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
658 self._yaw += e.GetX() - self._mouseX
659 self._pitch -= e.GetY() - self._mouseY
660 if self._pitch > 170:
664 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
665 self._mouseState = 'drag'
666 self._zoom += e.GetY() - self._mouseY
669 if self._zoom > numpy.max(self._machineSize) * 3:
670 self._zoom = numpy.max(self._machineSize) * 3
671 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
672 self._mouseState = 'dragObject'
673 z = max(0, self._mouseClick3DPos[2])
674 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
675 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
680 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
681 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
682 diff = cursorZ1 - cursorZ0
683 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
684 if not e.Dragging() or self._mouseState != 'tool':
685 self.tool.OnMouseMove(p0, p1)
687 self._mouseX = e.GetX()
688 self._mouseY = e.GetY()
690 def OnMouseWheel(self, e):
691 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
692 delta = max(min(delta,4),-4)
693 self._zoom *= 1.0 - delta / 10.0
696 if self._zoom > numpy.max(self._machineSize) * 3:
697 self._zoom = numpy.max(self._machineSize) * 3
700 def OnMouseLeave(self, e):
704 def getMouseRay(self, x, y):
705 if self._viewport is None:
706 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
707 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
708 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
709 p0 -= self._viewTarget
710 p1 -= self._viewTarget
713 def _init3DView(self):
714 # set viewing projection
715 size = self.GetSize()
716 glViewport(0, 0, size.GetWidth(), size.GetHeight())
719 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
721 glDisable(GL_RESCALE_NORMAL)
722 glDisable(GL_LIGHTING)
724 glEnable(GL_DEPTH_TEST)
725 glDisable(GL_CULL_FACE)
727 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
729 glClearColor(0.8, 0.8, 0.8, 1.0)
733 glMatrixMode(GL_PROJECTION)
735 aspect = float(size.GetWidth()) / float(size.GetHeight())
736 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
738 glMatrixMode(GL_MODELVIEW)
740 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
743 if machineCom.machineIsConnected():
744 self.printButton._imageID = 6
745 self.printButton._tooltip = 'Print'
746 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
747 self.printButton._imageID = 2
748 self.printButton._tooltip = 'Toolpath to SD'
750 self.printButton._imageID = 3
751 self.printButton._tooltip = 'Save toolpath'
753 if self._animView is not None:
754 self._viewTarget = self._animView.getPosition()
755 if self._animView.isDone():
756 self._animView = None
757 if self._animZoom is not None:
758 self._zoom = self._animZoom.getPosition()
759 if self._animZoom.isDone():
760 self._animZoom = None
761 if self.viewMode == 'gcode' and self._gcode is not None:
763 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
766 if self._objectShader is None:
767 if opengl.hasShaderSupport():
768 self._objectShader = opengl.GLShader("""
769 varying float light_amount;
773 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
774 gl_FrontColor = gl_Color;
776 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
780 varying float light_amount;
784 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
787 self._objectOverhangShader = opengl.GLShader("""
788 uniform float cosAngle;
789 uniform mat3 rotMatrix;
790 varying float light_amount;
794 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
795 gl_FrontColor = gl_Color;
797 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
799 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
801 light_amount = -10.0;
805 varying float light_amount;
809 if (light_amount == -10.0)
811 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
813 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
817 self._objectLoadShader = opengl.GLShader("""
818 uniform float intensity;
820 varying float light_amount;
824 vec4 tmp = gl_Vertex;
825 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
826 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
827 gl_Position = gl_ModelViewProjectionMatrix * tmp;
828 gl_FrontColor = gl_Color;
830 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
834 uniform float intensity;
835 varying float light_amount;
839 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
842 if self._objectShader == None or not self._objectShader.isValid():
843 self._objectShader = opengl.GLFakeShader()
844 self._objectOverhangShader = opengl.GLFakeShader()
845 self._objectLoadShader = None
847 glTranslate(0,0,-self._zoom)
848 glRotate(-self._pitch, 1,0,0)
849 glRotate(self._yaw, 0,0,1)
850 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
852 self._viewport = glGetIntegerv(GL_VIEWPORT)
853 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
854 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
856 glClearColor(1,1,1,1)
857 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
859 if self.viewMode != 'gcode':
860 for n in xrange(0, len(self._scene.objects())):
861 obj = self._scene.objects()[n]
862 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
863 self._renderObject(obj)
865 if self._mouseX > -1:
867 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
868 if n < len(self._scene.objects()):
869 self._focusObj = self._scene.objects()[n]
871 self._focusObj = None
872 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
873 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
874 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
875 self._mouse3Dpos -= self._viewTarget
878 glTranslate(0,0,-self._zoom)
879 glRotate(-self._pitch, 1,0,0)
880 glRotate(self._yaw, 0,0,1)
881 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
883 if self.viewMode == 'gcode':
884 if self._gcode is not None and self._gcode.layerList is None:
885 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
886 self._gcodeLoadThread.daemon = True
887 self._gcodeLoadThread.start()
888 if self._gcode is not None and self._gcode.layerList is not None:
890 if profile.getPreference('machine_center_is_zero') != 'True':
891 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
893 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
894 for n in xrange(0, drawUpTill):
895 c = 1.0 - float(drawUpTill - n) / 15
897 if len(self._gcodeVBOs) < n + 1:
898 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
899 if time.time() - t > 0.5:
902 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
903 if n == drawUpTill - 1:
904 if len(self._gcodeVBOs[n]) < 9:
905 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
907 self._gcodeVBOs[n][8].render(GL_QUADS)
909 self._gcodeVBOs[n][9].render(GL_QUADS)
911 self._gcodeVBOs[n][10].render(GL_QUADS)
913 self._gcodeVBOs[n][11].render(GL_QUADS)
916 self._gcodeVBOs[n][12].render(GL_QUADS)
917 glColor3f(c/2, c/2, 0.0)
918 self._gcodeVBOs[n][13].render(GL_QUADS)
920 self._gcodeVBOs[n][14].render(GL_QUADS)
921 self._gcodeVBOs[n][15].render(GL_QUADS)
923 self._gcodeVBOs[n][16].render(GL_LINES)
926 self._gcodeVBOs[n][0].render(GL_LINES)
928 self._gcodeVBOs[n][1].render(GL_LINES)
930 self._gcodeVBOs[n][2].render(GL_LINES)
932 self._gcodeVBOs[n][3].render(GL_LINES)
935 self._gcodeVBOs[n][4].render(GL_LINES)
936 glColor3f(c/2, c/2, 0.0)
937 self._gcodeVBOs[n][5].render(GL_LINES)
939 self._gcodeVBOs[n][6].render(GL_LINES)
940 self._gcodeVBOs[n][7].render(GL_LINES)
943 glStencilFunc(GL_ALWAYS, 1, 1)
944 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
946 if self.viewMode == 'overhang':
947 self._objectOverhangShader.bind()
948 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
950 self._objectShader.bind()
951 for obj in self._scene.objects():
952 if obj._loadAnim is not None:
953 if obj._loadAnim.isDone():
958 if self._focusObj == obj:
960 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
963 if self._selectedObj == obj or self._selectedObj is None:
964 #If we want transparent, then first render a solid black model to remove the printer size lines.
965 if self.viewMode == 'transparent':
966 glColor4f(0, 0, 0, 0)
967 self._renderObject(obj)
969 glBlendFunc(GL_ONE, GL_ONE)
970 glDisable(GL_DEPTH_TEST)
972 if self.viewMode == 'xray':
973 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
974 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
975 glEnable(GL_STENCIL_TEST)
977 if self.viewMode == 'overhang':
978 if self._selectedObj == obj and self.tempMatrix is not None:
979 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
981 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
983 if not self._scene.checkPlatform(obj):
984 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
985 self._renderObject(obj)
987 self._renderObject(obj, brightness)
988 glDisable(GL_STENCIL_TEST)
990 glEnable(GL_DEPTH_TEST)
991 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
993 if self.viewMode == 'xray':
996 glEnable(GL_STENCIL_TEST)
997 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
998 glDisable(GL_DEPTH_TEST)
999 for i in xrange(2, 15, 2):
1000 glStencilFunc(GL_EQUAL, i, 0xFF)
1001 glColor(float(i)/10, float(i)/10, float(i)/5)
1003 glVertex3f(-1000,-1000,-10)
1004 glVertex3f( 1000,-1000,-10)
1005 glVertex3f( 1000, 1000,-10)
1006 glVertex3f(-1000, 1000,-10)
1008 for i in xrange(1, 15, 2):
1009 glStencilFunc(GL_EQUAL, i, 0xFF)
1010 glColor(float(i)/10, 0, 0)
1012 glVertex3f(-1000,-1000,-10)
1013 glVertex3f( 1000,-1000,-10)
1014 glVertex3f( 1000, 1000,-10)
1015 glVertex3f(-1000, 1000,-10)
1018 glDisable(GL_STENCIL_TEST)
1019 glEnable(GL_DEPTH_TEST)
1021 self._objectShader.unbind()
1023 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1025 if self._objectLoadShader is not None:
1026 self._objectLoadShader.bind()
1027 glColor4f(0.2, 0.6, 1.0, 1.0)
1028 for obj in self._scene.objects():
1029 if obj._loadAnim is None:
1031 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1032 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1033 self._renderObject(obj)
1034 self._objectLoadShader.unbind()
1039 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1041 z = self._usbPrintMonitor.getZ()
1042 size = self._machineSize
1043 glColor4ub(255,255,0,128)
1045 glVertex3f(-size[0]/2,-size[1]/2, z)
1046 glVertex3f( size[0]/2,-size[1]/2, z)
1047 glVertex3f( size[0]/2, size[1]/2, z)
1048 glVertex3f(-size[0]/2, size[1]/2, z)
1051 if self.viewMode == 'gcode':
1052 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1053 glDisable(GL_DEPTH_TEST)
1056 glTranslate(0,-4,-10)
1057 glColor4ub(60,60,60,255)
1058 opengl.glDrawStringCenter('Loading toolpath for visualization...')
1061 #Draw the object box-shadow, so you can see where it will collide with other objects.
1062 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1063 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1065 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1067 glEnable(GL_CULL_FACE)
1068 glColor4f(0,0,0,0.12)
1070 glVertex3f(-size[0], size[1], 0.1)
1071 glVertex3f(-size[0], -size[1], 0.1)
1072 glVertex3f( size[0], -size[1], 0.1)
1073 glVertex3f( size[0], size[1], 0.1)
1075 glDisable(GL_CULL_FACE)
1078 #Draw the outline of the selected object, on top of everything else except the GUI.
1079 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1080 glDisable(GL_DEPTH_TEST)
1081 glEnable(GL_CULL_FACE)
1082 glEnable(GL_STENCIL_TEST)
1084 glStencilFunc(GL_EQUAL, 0, 255)
1086 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1088 glColor4f(1,1,1,0.5)
1089 self._renderObject(self._selectedObj)
1090 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1092 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1093 glDisable(GL_STENCIL_TEST)
1094 glDisable(GL_CULL_FACE)
1095 glEnable(GL_DEPTH_TEST)
1097 if self._selectedObj is not None:
1099 pos = self.getObjectCenterPos()
1100 glTranslate(pos[0], pos[1], pos[2])
1103 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1104 glDisable(GL_DEPTH_TEST)
1107 glTranslate(0,-4,-10)
1108 glColor4ub(60,60,60,255)
1109 opengl.glDrawStringCenter('Overhang view not working due to lack of OpenGL shaders support.')
1112 def _renderObject(self, obj, brightness = False, addSink = True):
1115 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1117 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1119 if self.tempMatrix is not None and obj == self._selectedObj:
1120 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1121 glMultMatrixf(tempMatrix)
1123 offset = obj.getDrawOffset()
1124 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1126 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1127 glMultMatrixf(tempMatrix)
1130 for m in obj._meshList:
1132 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1134 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1139 def _drawMachine(self):
1140 glEnable(GL_CULL_FACE)
1143 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
1145 if profile.getPreference('machine_type').startswith('ultimaker'):
1146 if self._platformMesh is None:
1147 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
1148 self._platformMesh._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1149 glColor4f(1,1,1,0.5)
1150 self._objectShader.bind()
1151 self._renderObject(self._platformMesh, False, False)
1152 self._objectShader.unbind()
1157 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1158 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1159 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1160 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1161 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1162 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1165 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1166 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1167 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1168 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1169 v4 = [ size[0] / 2, size[1] / 2, 0]
1170 v5 = [ size[0] / 2,-size[1] / 2, 0]
1171 v6 = [-size[0] / 2, size[1] / 2, 0]
1172 v7 = [-size[0] / 2,-size[1] / 2, 0]
1174 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1175 glEnableClientState(GL_VERTEX_ARRAY)
1176 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1178 glColor4ub(5, 171, 231, 64)
1179 glDrawArrays(GL_QUADS, 0, 4)
1180 glColor4ub(5, 171, 231, 96)
1181 glDrawArrays(GL_QUADS, 4, 8)
1182 glColor4ub(5, 171, 231, 128)
1183 glDrawArrays(GL_QUADS, 12, 8)
1184 glDisableClientState(GL_VERTEX_ARRAY)
1186 sx = self._machineSize[0]
1187 sy = self._machineSize[1]
1188 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1189 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1194 x1 = max(min(x1, sx/2), -sx/2)
1195 y1 = max(min(y1, sy/2), -sy/2)
1196 x2 = max(min(x2, sx/2), -sx/2)
1197 y2 = max(min(y2, sy/2), -sy/2)
1198 if (x & 1) == (y & 1):
1199 glColor4ub(5, 171, 231, 127)
1201 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1203 glVertex3f(x1, y1, -0.02)
1204 glVertex3f(x2, y1, -0.02)
1205 glVertex3f(x2, y2, -0.02)
1206 glVertex3f(x1, y2, -0.02)
1210 glDisable(GL_CULL_FACE)
1212 def _generateGCodeVBOs(self, layer):
1214 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1215 if ':' in extrudeType:
1216 extruder = int(extrudeType[extrudeType.find(':')+1:])
1217 extrudeType = extrudeType[0:extrudeType.find(':')]
1220 pointList = numpy.zeros((0,3), numpy.float32)
1222 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1224 a = numpy.concatenate((a[:-1], a[1:]), 1)
1225 a = a.reshape((len(a) * 2, 3))
1226 pointList = numpy.concatenate((pointList, a))
1227 ret.append(opengl.GLVBO(pointList))
1230 def _generateGCodeVBOs2(self, layer):
1231 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1232 filamentArea = math.pi * filamentRadius * filamentRadius
1233 useFilamentArea = profile.getPreference('gcode_flavor') == 'UltiGCode'
1236 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1237 if ':' in extrudeType:
1238 extruder = int(extrudeType[extrudeType.find(':')+1:])
1239 extrudeType = extrudeType[0:extrudeType.find(':')]
1242 pointList = numpy.zeros((0,3), numpy.float32)
1244 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1246 if extrudeType == 'FILL':
1249 normal = a[1:] - a[:-1]
1250 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1251 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1254 ePerDist = path['extrusion'][1:] / lens
1256 lineWidth = ePerDist / path['layerThickness'] / 2.0
1258 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1260 normal[:,0] *= lineWidth
1261 normal[:,1] *= lineWidth
1263 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1264 b = numpy.concatenate((b, a[1:] + normal), 1)
1265 b = numpy.concatenate((b, a[1:] - normal), 1)
1266 b = numpy.concatenate((b, a[:-1] - normal), 1)
1267 b = numpy.concatenate((b, a[:-1] + normal), 1)
1268 b = b.reshape((len(b) * 4, 3))
1271 normal2 = normal[:-1] + normal[1:]
1272 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1273 normal2[:,0] /= lens2
1274 normal2[:,1] /= lens2
1275 normal2[:,0] *= lineWidth[:-1]
1276 normal2[:,1] *= lineWidth[:-1]
1278 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1279 c = numpy.concatenate((c, a[1:-1]), 1)
1280 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1281 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1282 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1284 c = numpy.concatenate((c, a[1:-1]), 1)
1285 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1286 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1287 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1289 c = c.reshape((len(c) * 8, 3))
1291 pointList = numpy.concatenate((pointList, b, c))
1293 pointList = numpy.concatenate((pointList, b))
1294 ret.append(opengl.GLVBO(pointList))
1296 pointList = numpy.zeros((0,3), numpy.float32)
1298 if path['type'] == 'move':
1299 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1300 a = numpy.concatenate((a[:-1], a[1:]), 1)
1301 a = a.reshape((len(a) * 2, 3))
1302 pointList = numpy.concatenate((pointList, a))
1303 if path['type'] == 'retract':
1304 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1305 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1306 a = a.reshape((len(a) * 2, 3))
1307 pointList = numpy.concatenate((pointList, a))
1308 ret.append(opengl.GLVBO(pointList))
1312 def getObjectCenterPos(self):
1313 if self._selectedObj is None:
1314 return [0.0, 0.0, 0.0]
1315 pos = self._selectedObj.getPosition()
1316 size = self._selectedObj.getSize()
1317 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1319 def getObjectBoundaryCircle(self):
1320 if self._selectedObj is None:
1322 return self._selectedObj.getBoundaryCircle()
1324 def getObjectSize(self):
1325 if self._selectedObj is None:
1326 return [0.0, 0.0, 0.0]
1327 return self._selectedObj.getSize()
1329 def getObjectMatrix(self):
1330 if self._selectedObj is None:
1331 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1332 return self._selectedObj.getMatrix()
1334 class shaderEditor(wx.Dialog):
1335 def __init__(self, parent, callback, v, f):
1336 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1337 self._callback = callback
1338 s = wx.BoxSizer(wx.VERTICAL)
1340 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1341 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1342 s.Add(self._vertex, 1, flag=wx.EXPAND)
1343 s.Add(self._fragment, 1, flag=wx.EXPAND)
1345 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1346 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1348 self.SetPosition(self.GetParent().GetPosition())
1349 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1352 def OnText(self, e):
1353 self._callback(self._vertex.GetValue(), self._fragment.GetValue())