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)
108 self.youMagineButton.setHidden(True)
110 self.notification = openglGui.glNotification(self, (0, 0))
112 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
113 self._sceneUpdateTimer = wx.Timer(self)
114 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
115 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
116 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
120 self.updateToolButtons()
121 self.updateProfileToControls()
123 def showLoadModel(self, button = 1):
125 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)
126 dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
127 if dlg.ShowModal() != wx.ID_OK:
130 filenames = dlg.GetPaths()
132 if len(filenames) < 1:
134 profile.putPreference('lastFile', filenames[0])
136 for filename in filenames:
137 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
138 ext = filename[filename.rfind('.')+1:].upper()
139 if ext == 'G' or ext == 'GCODE':
140 gcodeFilename = filename
141 if gcodeFilename is not None:
142 if self._gcode is not None:
144 for layerVBOlist in self._gcodeVBOs:
145 for vbo in layerVBOlist:
146 self.glReleaseList.append(vbo)
148 self._gcode = gcodeInterpreter.gcode()
149 self._gcodeFilename = gcodeFilename
150 self.printButton.setBottomText('')
151 self.viewSelection.setValue(4)
152 self.printButton.setDisabled(False)
153 self.youMagineButton.setDisabled(True)
156 self.youMagineButton.setDisabled(False)
157 if self.viewSelection.getValue() == 4:
158 self.viewSelection.setValue(0)
160 self.loadScene(filenames)
162 def showSaveModel(self):
163 if len(self._scene.objects()) < 1:
165 dlg=wx.FileDialog(self, 'Save 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
166 dlg.SetWildcard(meshLoader.saveWildcardFilter())
167 if dlg.ShowModal() != wx.ID_OK:
170 filename = dlg.GetPath()
172 meshLoader.saveMeshes(filename, self._scene.objects())
174 def OnPrintButton(self, button):
176 if machineCom.machineIsConnected():
177 self.showPrintWindow()
178 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
179 drives = removableStorage.getPossibleSDcardDrives()
181 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))
182 if dlg.ShowModal() != wx.ID_OK:
185 drive = drives[dlg.GetSelection()]
189 filename = self._scene._objectList[0].getName() + '.gcode'
190 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
195 self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, 'Print with USB'))
196 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, 'Save GCode...'))
197 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, 'Slice engine log...'))
201 def showPrintWindow(self):
202 if self._gcodeFilename is None:
204 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
205 if self._gcodeFilename == self._slicer.getGCodeFilename():
206 self._slicer.submitSliceInfoOnline()
208 def showSaveGCode(self):
209 defPath = profile.getPreference('lastFile')
210 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
211 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
212 dlg.SetFilename(self._scene._objectList[0].getName())
213 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
214 if dlg.ShowModal() != wx.ID_OK:
217 filename = dlg.GetPath()
220 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
222 def _copyFile(self, fileA, fileB, allowEject = False):
224 size = float(os.stat(fileA).st_size)
225 with open(fileA, 'rb') as fsrc:
226 with open(fileB, 'wb') as fdst:
228 buf = fsrc.read(16*1024)
232 self.printButton.setProgressBar(float(fsrc.tell()) / size)
237 self.notification.message("Failed to save")
240 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...'))
242 self.notification.message("Saved as %s" % (fileB))
243 self.printButton.setProgressBar(None)
244 if fileA == self._slicer.getGCodeFilename():
245 self._slicer.submitSliceInfoOnline()
247 def _showSliceLog(self):
248 dlg = wx.TextEntryDialog(self, "The slicing engine reported the following", "Engine log...", '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
252 def OnToolSelect(self, button):
253 if self.rotateToolButton.getSelected():
254 self.tool = previewTools.toolRotate(self)
255 elif self.scaleToolButton.getSelected():
256 self.tool = previewTools.toolScale(self)
257 elif self.mirrorToolButton.getSelected():
258 self.tool = previewTools.toolNone(self)
260 self.tool = previewTools.toolNone(self)
261 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
262 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
263 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
264 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
265 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
266 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
267 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
268 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
270 def updateToolButtons(self):
271 if self._selectedObj is None:
275 self.rotateToolButton.setHidden(hidden)
276 self.scaleToolButton.setHidden(hidden)
277 self.mirrorToolButton.setHidden(hidden)
279 self.rotateToolButton.setSelected(False)
280 self.scaleToolButton.setSelected(False)
281 self.mirrorToolButton.setSelected(False)
284 def OnViewChange(self):
285 if self.viewSelection.getValue() == 4:
286 self.viewMode = 'gcode'
287 if self._gcode is not None and self._gcode.layerList is not None:
288 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
289 self._selectObject(None)
290 elif self.viewSelection.getValue() == 1:
291 self.viewMode = 'overhang'
292 elif self.viewSelection.getValue() == 2:
293 self.viewMode = 'transparent'
294 elif self.viewSelection.getValue() == 3:
295 self.viewMode = 'xray'
297 self.viewMode = 'normal'
298 self.layerSelect.setHidden(self.viewMode != 'gcode')
301 def OnRotateReset(self, button):
302 if self._selectedObj is None:
304 self._selectedObj.resetRotation()
305 self._scene.pushFree()
306 self._selectObject(self._selectedObj)
309 def OnLayFlat(self, button):
310 if self._selectedObj is None:
312 self._selectedObj.layFlat()
313 self._scene.pushFree()
314 self._selectObject(self._selectedObj)
317 def OnScaleReset(self, button):
318 if self._selectedObj is None:
320 self._selectedObj.resetScale()
321 self._selectObject(self._selectedObj)
322 self.updateProfileToControls()
325 def OnScaleMax(self, button):
326 if self._selectedObj is None:
328 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
329 self._scene.pushFree()
330 self._selectObject(self._selectedObj)
331 self.updateProfileToControls()
334 def OnMirror(self, axis):
335 if self._selectedObj is None:
337 self._selectedObj.mirror(axis)
340 def OnScaleEntry(self, value, axis):
341 if self._selectedObj is None:
347 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
348 self.updateProfileToControls()
349 self._scene.pushFree()
350 self._selectObject(self._selectedObj)
353 def OnScaleEntryMM(self, value, axis):
354 if self._selectedObj is None:
360 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
361 self.updateProfileToControls()
362 self._scene.pushFree()
363 self._selectObject(self._selectedObj)
366 def OnDeleteAll(self, e):
367 while len(self._scene.objects()) > 0:
368 self._deleteObject(self._scene.objects()[0])
369 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
371 def OnMultiply(self, e):
372 if self._focusObj is None:
375 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
376 if dlg.ShowModal() != wx.ID_OK:
385 self._scene.add(newObj)
386 self._scene.centerAll()
387 if not self._scene.checkPlatform(newObj):
392 self.notification.message("Could not create more then %d items" % (n - 1))
393 self._scene.remove(newObj)
394 self._scene.centerAll()
397 def OnSplitObject(self, e):
398 if self._focusObj is None:
400 self._scene.remove(self._focusObj)
401 for obj in self._focusObj.split(self._splitCallback):
402 if numpy.max(obj.getSize()) > 2.0:
404 self._scene.centerAll()
405 self._selectObject(None)
408 def _splitCallback(self, progress):
411 def OnMergeObjects(self, e):
412 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
414 self._scene.merge(self._selectedObj, self._focusObj)
417 def sceneUpdated(self):
418 self._sceneUpdateTimer.Start(500, True)
419 self._slicer.abortSlicer()
420 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
423 def _onRunSlicer(self, e):
424 if self._isSimpleMode:
425 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
426 self._slicer.runSlicer(self._scene)
427 if self._isSimpleMode:
428 profile.resetTempOverride()
430 def _updateSliceProgress(self, progressValue, ready):
432 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
434 self.printButton.setDisabled(not ready)
435 if progressValue >= 0.0:
436 self.printButton.setProgressBar(progressValue)
438 self.printButton.setProgressBar(None)
439 if self._gcode is not None:
441 for layerVBOlist in self._gcodeVBOs:
442 for vbo in layerVBOlist:
443 self.glReleaseList.append(vbo)
446 self.printButton.setProgressBar(None)
447 cost = self._slicer.getFilamentCost()
449 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
451 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
452 self._gcode = gcodeInterpreter.gcode()
453 self._gcodeFilename = self._slicer.getGCodeFilename()
455 self.printButton.setBottomText('')
458 def _loadGCode(self):
459 self._gcode.progressCallback = self._gcodeLoadCallback
460 self._gcode.load(self._gcodeFilename)
462 def _gcodeLoadCallback(self, progress):
463 if self._gcode is None:
465 if len(self._gcode.layerList) % 15 == 0:
467 if self._gcode is None:
469 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
470 if self.viewMode == 'gcode':
474 def loadScene(self, fileList):
475 for filename in fileList:
477 objList = meshLoader.loadMeshes(filename)
479 traceback.print_exc()
482 if self._objectLoadShader is not None:
483 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
487 self._scene.centerAll()
488 self._selectObject(obj)
489 if obj.getScale()[0] < 1.0:
490 self.notification.message("Warning: Object scaled down.")
493 def _deleteObject(self, obj):
494 if obj == self._selectedObj:
495 self._selectObject(None)
496 if obj == self._focusObj:
497 self._focusObj = None
498 self._scene.remove(obj)
499 for m in obj._meshList:
500 if m.vbo is not None and m.vbo.decRef():
501 self.glReleaseList.append(m.vbo)
506 def _selectObject(self, obj, zoom = True):
507 if obj != self._selectedObj:
508 self._selectedObj = obj
509 self.updateProfileToControls()
510 self.updateToolButtons()
511 if zoom and obj is not None:
512 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
513 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
514 newZoom = obj.getBoundaryCircle() * 6
515 if newZoom > numpy.max(self._machineSize) * 3:
516 newZoom = numpy.max(self._machineSize) * 3
517 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
519 def updateProfileToControls(self):
520 oldSimpleMode = self._isSimpleMode
521 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
522 if self._isSimpleMode != oldSimpleMode:
523 self._scene.arrangeAll()
525 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
526 self._objColors[0] = profile.getPreferenceColour('model_colour')
527 self._objColors[1] = profile.getPreferenceColour('model_colour2')
528 self._objColors[2] = profile.getPreferenceColour('model_colour3')
529 self._objColors[3] = profile.getPreferenceColour('model_colour4')
530 self._scene.setMachineSize(self._machineSize)
531 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
532 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'))
534 if self._selectedObj is not None:
535 scale = self._selectedObj.getScale()
536 size = self._selectedObj.getSize()
537 self.scaleXctrl.setValue(round(scale[0], 2))
538 self.scaleYctrl.setValue(round(scale[1], 2))
539 self.scaleZctrl.setValue(round(scale[2], 2))
540 self.scaleXmmctrl.setValue(round(size[0], 2))
541 self.scaleYmmctrl.setValue(round(size[1], 2))
542 self.scaleZmmctrl.setValue(round(size[2], 2))
544 def OnKeyChar(self, keyCode):
545 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
546 if self._selectedObj is not None:
547 self._deleteObject(self._selectedObj)
549 if keyCode == wx.WXK_UP:
550 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
552 elif keyCode == wx.WXK_DOWN:
553 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
555 elif keyCode == wx.WXK_PAGEUP:
556 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
558 elif keyCode == wx.WXK_PAGEDOWN:
559 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
562 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
563 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
564 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
565 from collections import defaultdict
566 from gc import get_objects
567 self._beforeLeakTest = defaultdict(int)
568 for i in get_objects():
569 self._beforeLeakTest[type(i)] += 1
570 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
571 from collections import defaultdict
572 from gc import get_objects
573 self._afterLeakTest = defaultdict(int)
574 for i in get_objects():
575 self._afterLeakTest[type(i)] += 1
576 for k in self._afterLeakTest:
577 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
578 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
580 def ShaderUpdate(self, v, f):
581 s = opengl.GLShader(v, f)
583 self._objectLoadShader.release()
584 self._objectLoadShader = s
585 for obj in self._scene.objects():
586 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
589 def OnMouseDown(self,e):
590 self._mouseX = e.GetX()
591 self._mouseY = e.GetY()
592 self._mouseClick3DPos = self._mouse3Dpos
593 self._mouseClickFocus = self._focusObj
595 self._mouseState = 'doubleClick'
597 self._mouseState = 'dragOrClick'
598 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
599 p0 -= self.getObjectCenterPos() - self._viewTarget
600 p1 -= self.getObjectCenterPos() - self._viewTarget
601 if self.tool.OnDragStart(p0, p1):
602 self._mouseState = 'tool'
603 if self._mouseState == 'dragOrClick':
604 if e.GetButton() == 1:
605 if self._focusObj is not None:
606 self._selectObject(self._focusObj, False)
609 def OnMouseUp(self, e):
610 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
612 if self._mouseState == 'dragOrClick':
613 if e.GetButton() == 1:
614 self._selectObject(self._focusObj)
615 if e.GetButton() == 3:
617 if self._focusObj is not None:
618 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
619 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
620 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
621 if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1:
622 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
623 if len(self._scene.objects()) > 0:
624 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
625 if menu.MenuItemCount > 0:
628 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
629 self._scene.pushFree()
631 elif self._mouseState == 'tool':
632 if self.tempMatrix is not None and self._selectedObj is not None:
633 self._selectedObj.applyMatrix(self.tempMatrix)
634 self._scene.pushFree()
635 self._selectObject(self._selectedObj)
636 self.tempMatrix = None
637 self.tool.OnDragEnd()
639 self._mouseState = None
641 def OnMouseMotion(self,e):
642 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
643 p0 -= self.getObjectCenterPos() - self._viewTarget
644 p1 -= self.getObjectCenterPos() - self._viewTarget
646 if e.Dragging() and self._mouseState is not None:
647 if self._mouseState == 'tool':
648 self.tool.OnDrag(p0, p1)
649 elif not e.LeftIsDown() and e.RightIsDown():
650 self._mouseState = 'drag'
651 if wx.GetKeyState(wx.WXK_SHIFT):
652 a = math.cos(math.radians(self._yaw)) / 3.0
653 b = math.sin(math.radians(self._yaw)) / 3.0
654 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
655 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
656 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
657 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
659 self._yaw += e.GetX() - self._mouseX
660 self._pitch -= e.GetY() - self._mouseY
661 if self._pitch > 170:
665 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
666 self._mouseState = 'drag'
667 self._zoom += e.GetY() - self._mouseY
670 if self._zoom > numpy.max(self._machineSize) * 3:
671 self._zoom = numpy.max(self._machineSize) * 3
672 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
673 self._mouseState = 'dragObject'
674 z = max(0, self._mouseClick3DPos[2])
675 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
676 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
681 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
682 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
683 diff = cursorZ1 - cursorZ0
684 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
685 if not e.Dragging() or self._mouseState != 'tool':
686 self.tool.OnMouseMove(p0, p1)
688 self._mouseX = e.GetX()
689 self._mouseY = e.GetY()
691 def OnMouseWheel(self, e):
692 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
693 delta = max(min(delta,4),-4)
694 self._zoom *= 1.0 - delta / 10.0
697 if self._zoom > numpy.max(self._machineSize) * 3:
698 self._zoom = numpy.max(self._machineSize) * 3
701 def OnMouseLeave(self, e):
705 def getMouseRay(self, x, y):
706 if self._viewport is None:
707 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
708 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
709 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
710 p0 -= self._viewTarget
711 p1 -= self._viewTarget
714 def _init3DView(self):
715 # set viewing projection
716 size = self.GetSize()
717 glViewport(0, 0, size.GetWidth(), size.GetHeight())
720 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
722 glDisable(GL_RESCALE_NORMAL)
723 glDisable(GL_LIGHTING)
725 glEnable(GL_DEPTH_TEST)
726 glDisable(GL_CULL_FACE)
728 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
730 glClearColor(0.8, 0.8, 0.8, 1.0)
734 glMatrixMode(GL_PROJECTION)
736 aspect = float(size.GetWidth()) / float(size.GetHeight())
737 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
739 glMatrixMode(GL_MODELVIEW)
741 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
744 if machineCom.machineIsConnected():
745 self.printButton._imageID = 6
746 self.printButton._tooltip = 'Print'
747 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
748 self.printButton._imageID = 2
749 self.printButton._tooltip = 'Toolpath to SD'
751 self.printButton._imageID = 3
752 self.printButton._tooltip = 'Save toolpath'
754 if self._animView is not None:
755 self._viewTarget = self._animView.getPosition()
756 if self._animView.isDone():
757 self._animView = None
758 if self._animZoom is not None:
759 self._zoom = self._animZoom.getPosition()
760 if self._animZoom.isDone():
761 self._animZoom = None
762 if self.viewMode == 'gcode' and self._gcode is not None:
764 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
767 if self._objectShader is None:
768 if opengl.hasShaderSupport():
769 self._objectShader = opengl.GLShader("""
770 varying float light_amount;
774 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
775 gl_FrontColor = gl_Color;
777 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
781 varying float light_amount;
785 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
788 self._objectOverhangShader = opengl.GLShader("""
789 uniform float cosAngle;
790 uniform mat3 rotMatrix;
791 varying float light_amount;
795 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
796 gl_FrontColor = gl_Color;
798 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
800 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
802 light_amount = -10.0;
806 varying float light_amount;
810 if (light_amount == -10.0)
812 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
814 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
818 self._objectLoadShader = opengl.GLShader("""
819 uniform float intensity;
821 varying float light_amount;
825 vec4 tmp = gl_Vertex;
826 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
827 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
828 gl_Position = gl_ModelViewProjectionMatrix * tmp;
829 gl_FrontColor = gl_Color;
831 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
835 uniform float intensity;
836 varying float light_amount;
840 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
843 if self._objectShader == None or not self._objectShader.isValid():
844 self._objectShader = opengl.GLFakeShader()
845 self._objectOverhangShader = opengl.GLFakeShader()
846 self._objectLoadShader = None
848 glTranslate(0,0,-self._zoom)
849 glRotate(-self._pitch, 1,0,0)
850 glRotate(self._yaw, 0,0,1)
851 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
853 self._viewport = glGetIntegerv(GL_VIEWPORT)
854 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
855 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
857 glClearColor(1,1,1,1)
858 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
860 if self.viewMode != 'gcode':
861 for n in xrange(0, len(self._scene.objects())):
862 obj = self._scene.objects()[n]
863 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
864 self._renderObject(obj)
866 if self._mouseX > -1:
868 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
869 if n < len(self._scene.objects()):
870 self._focusObj = self._scene.objects()[n]
872 self._focusObj = None
873 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
874 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
875 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
876 self._mouse3Dpos -= self._viewTarget
879 glTranslate(0,0,-self._zoom)
880 glRotate(-self._pitch, 1,0,0)
881 glRotate(self._yaw, 0,0,1)
882 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
884 if self.viewMode == 'gcode':
885 if self._gcode is not None and self._gcode.layerList is None:
886 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
887 self._gcodeLoadThread.daemon = True
888 self._gcodeLoadThread.start()
889 if self._gcode is not None and self._gcode.layerList is not None:
891 if profile.getPreference('machine_center_is_zero') != 'True':
892 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
894 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
895 for n in xrange(0, drawUpTill):
896 c = 1.0 - float(drawUpTill - n) / 15
898 if len(self._gcodeVBOs) < n + 1:
899 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
900 if time.time() - t > 0.5:
903 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
904 if n == drawUpTill - 1:
905 if len(self._gcodeVBOs[n]) < 9:
906 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
908 self._gcodeVBOs[n][8].render(GL_QUADS)
910 self._gcodeVBOs[n][9].render(GL_QUADS)
912 self._gcodeVBOs[n][10].render(GL_QUADS)
914 self._gcodeVBOs[n][11].render(GL_QUADS)
917 self._gcodeVBOs[n][12].render(GL_QUADS)
918 glColor3f(c/2, c/2, 0.0)
919 self._gcodeVBOs[n][13].render(GL_QUADS)
921 self._gcodeVBOs[n][14].render(GL_QUADS)
922 self._gcodeVBOs[n][15].render(GL_QUADS)
924 self._gcodeVBOs[n][16].render(GL_LINES)
927 self._gcodeVBOs[n][0].render(GL_LINES)
929 self._gcodeVBOs[n][1].render(GL_LINES)
931 self._gcodeVBOs[n][2].render(GL_LINES)
933 self._gcodeVBOs[n][3].render(GL_LINES)
936 self._gcodeVBOs[n][4].render(GL_LINES)
937 glColor3f(c/2, c/2, 0.0)
938 self._gcodeVBOs[n][5].render(GL_LINES)
940 self._gcodeVBOs[n][6].render(GL_LINES)
941 self._gcodeVBOs[n][7].render(GL_LINES)
944 glStencilFunc(GL_ALWAYS, 1, 1)
945 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
947 if self.viewMode == 'overhang':
948 self._objectOverhangShader.bind()
949 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
951 self._objectShader.bind()
952 for obj in self._scene.objects():
953 if obj._loadAnim is not None:
954 if obj._loadAnim.isDone():
959 if self._focusObj == obj:
961 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
964 if self._selectedObj == obj or self._selectedObj is None:
965 #If we want transparent, then first render a solid black model to remove the printer size lines.
966 if self.viewMode == 'transparent':
967 glColor4f(0, 0, 0, 0)
968 self._renderObject(obj)
970 glBlendFunc(GL_ONE, GL_ONE)
971 glDisable(GL_DEPTH_TEST)
973 if self.viewMode == 'xray':
974 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
975 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
976 glEnable(GL_STENCIL_TEST)
978 if self.viewMode == 'overhang':
979 if self._selectedObj == obj and self.tempMatrix is not None:
980 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
982 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
984 if not self._scene.checkPlatform(obj):
985 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
986 self._renderObject(obj)
988 self._renderObject(obj, brightness)
989 glDisable(GL_STENCIL_TEST)
991 glEnable(GL_DEPTH_TEST)
992 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
994 if self.viewMode == 'xray':
997 glEnable(GL_STENCIL_TEST)
998 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
999 glDisable(GL_DEPTH_TEST)
1000 for i in xrange(2, 15, 2):
1001 glStencilFunc(GL_EQUAL, i, 0xFF)
1002 glColor(float(i)/10, float(i)/10, float(i)/5)
1004 glVertex3f(-1000,-1000,-10)
1005 glVertex3f( 1000,-1000,-10)
1006 glVertex3f( 1000, 1000,-10)
1007 glVertex3f(-1000, 1000,-10)
1009 for i in xrange(1, 15, 2):
1010 glStencilFunc(GL_EQUAL, i, 0xFF)
1011 glColor(float(i)/10, 0, 0)
1013 glVertex3f(-1000,-1000,-10)
1014 glVertex3f( 1000,-1000,-10)
1015 glVertex3f( 1000, 1000,-10)
1016 glVertex3f(-1000, 1000,-10)
1019 glDisable(GL_STENCIL_TEST)
1020 glEnable(GL_DEPTH_TEST)
1022 self._objectShader.unbind()
1024 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1026 if self._objectLoadShader is not None:
1027 self._objectLoadShader.bind()
1028 glColor4f(0.2, 0.6, 1.0, 1.0)
1029 for obj in self._scene.objects():
1030 if obj._loadAnim is None:
1032 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1033 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1034 self._renderObject(obj)
1035 self._objectLoadShader.unbind()
1040 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1042 z = self._usbPrintMonitor.getZ()
1043 size = self._machineSize
1044 glColor4ub(255,255,0,128)
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)
1049 glVertex3f(-size[0]/2, size[1]/2, z)
1052 if self.viewMode == 'gcode':
1053 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1054 glDisable(GL_DEPTH_TEST)
1057 glTranslate(0,-4,-10)
1058 glColor4ub(60,60,60,255)
1059 opengl.glDrawStringCenter('Loading toolpath for visualization...')
1062 #Draw the object box-shadow, so you can see where it will collide with other objects.
1063 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1064 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1066 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1068 glEnable(GL_CULL_FACE)
1069 glColor4f(0,0,0,0.12)
1071 glVertex3f(-size[0], size[1], 0.1)
1072 glVertex3f(-size[0], -size[1], 0.1)
1073 glVertex3f( size[0], -size[1], 0.1)
1074 glVertex3f( size[0], size[1], 0.1)
1076 glDisable(GL_CULL_FACE)
1079 #Draw the outline of the selected object, on top of everything else except the GUI.
1080 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1081 glDisable(GL_DEPTH_TEST)
1082 glEnable(GL_CULL_FACE)
1083 glEnable(GL_STENCIL_TEST)
1085 glStencilFunc(GL_EQUAL, 0, 255)
1087 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1089 glColor4f(1,1,1,0.5)
1090 self._renderObject(self._selectedObj)
1091 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1093 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1094 glDisable(GL_STENCIL_TEST)
1095 glDisable(GL_CULL_FACE)
1096 glEnable(GL_DEPTH_TEST)
1098 if self._selectedObj is not None:
1100 pos = self.getObjectCenterPos()
1101 glTranslate(pos[0], pos[1], pos[2])
1104 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1105 glDisable(GL_DEPTH_TEST)
1108 glTranslate(0,-4,-10)
1109 glColor4ub(60,60,60,255)
1110 opengl.glDrawStringCenter('Overhang view not working due to lack of OpenGL shaders support.')
1113 def _renderObject(self, obj, brightness = False, addSink = True):
1116 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1118 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1120 if self.tempMatrix is not None and obj == self._selectedObj:
1121 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1122 glMultMatrixf(tempMatrix)
1124 offset = obj.getDrawOffset()
1125 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1127 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1128 glMultMatrixf(tempMatrix)
1131 for m in obj._meshList:
1133 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1135 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1140 def _drawMachine(self):
1141 glEnable(GL_CULL_FACE)
1144 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
1146 if profile.getPreference('machine_type').startswith('ultimaker'):
1147 if self._platformMesh is None:
1148 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
1149 self._platformMesh._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1150 glColor4f(1,1,1,0.5)
1151 self._objectShader.bind()
1152 self._renderObject(self._platformMesh, False, False)
1153 self._objectShader.unbind()
1158 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1159 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1160 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1161 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1162 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1163 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1166 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1167 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1168 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1169 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1170 v4 = [ size[0] / 2, size[1] / 2, 0]
1171 v5 = [ size[0] / 2,-size[1] / 2, 0]
1172 v6 = [-size[0] / 2, size[1] / 2, 0]
1173 v7 = [-size[0] / 2,-size[1] / 2, 0]
1175 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1176 glEnableClientState(GL_VERTEX_ARRAY)
1177 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1179 glColor4ub(5, 171, 231, 64)
1180 glDrawArrays(GL_QUADS, 0, 4)
1181 glColor4ub(5, 171, 231, 96)
1182 glDrawArrays(GL_QUADS, 4, 8)
1183 glColor4ub(5, 171, 231, 128)
1184 glDrawArrays(GL_QUADS, 12, 8)
1185 glDisableClientState(GL_VERTEX_ARRAY)
1187 sx = self._machineSize[0]
1188 sy = self._machineSize[1]
1189 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1190 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1195 x1 = max(min(x1, sx/2), -sx/2)
1196 y1 = max(min(y1, sy/2), -sy/2)
1197 x2 = max(min(x2, sx/2), -sx/2)
1198 y2 = max(min(y2, sy/2), -sy/2)
1199 if (x & 1) == (y & 1):
1200 glColor4ub(5, 171, 231, 127)
1202 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1204 glVertex3f(x1, y1, -0.02)
1205 glVertex3f(x2, y1, -0.02)
1206 glVertex3f(x2, y2, -0.02)
1207 glVertex3f(x1, y2, -0.02)
1211 glDisable(GL_CULL_FACE)
1213 def _generateGCodeVBOs(self, layer):
1215 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1216 if ':' in extrudeType:
1217 extruder = int(extrudeType[extrudeType.find(':')+1:])
1218 extrudeType = extrudeType[0:extrudeType.find(':')]
1221 pointList = numpy.zeros((0,3), numpy.float32)
1223 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1225 a = numpy.concatenate((a[:-1], a[1:]), 1)
1226 a = a.reshape((len(a) * 2, 3))
1227 pointList = numpy.concatenate((pointList, a))
1228 ret.append(opengl.GLVBO(pointList))
1231 def _generateGCodeVBOs2(self, layer):
1232 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1233 filamentArea = math.pi * filamentRadius * filamentRadius
1234 useFilamentArea = profile.getPreference('gcode_flavor') == 'UltiGCode'
1237 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1238 if ':' in extrudeType:
1239 extruder = int(extrudeType[extrudeType.find(':')+1:])
1240 extrudeType = extrudeType[0:extrudeType.find(':')]
1243 pointList = numpy.zeros((0,3), numpy.float32)
1245 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1247 if extrudeType == 'FILL':
1250 normal = a[1:] - a[:-1]
1251 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1252 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1255 ePerDist = path['extrusion'][1:] / lens
1257 lineWidth = ePerDist / path['layerThickness'] / 2.0
1259 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1261 normal[:,0] *= lineWidth
1262 normal[:,1] *= lineWidth
1264 b = numpy.zeros((len(a)-1, 0), numpy.float32)
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 = numpy.concatenate((b, a[:-1] + normal), 1)
1269 b = b.reshape((len(b) * 4, 3))
1272 normal2 = normal[:-1] + normal[1:]
1273 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1274 normal2[:,0] /= lens2
1275 normal2[:,1] /= lens2
1276 normal2[:,0] *= lineWidth[:-1]
1277 normal2[:,1] *= lineWidth[:-1]
1279 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1280 c = numpy.concatenate((c, a[1:-1]), 1)
1281 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1282 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1283 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1285 c = numpy.concatenate((c, a[1:-1]), 1)
1286 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1287 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1288 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1290 c = c.reshape((len(c) * 8, 3))
1292 pointList = numpy.concatenate((pointList, b, c))
1294 pointList = numpy.concatenate((pointList, b))
1295 ret.append(opengl.GLVBO(pointList))
1297 pointList = numpy.zeros((0,3), numpy.float32)
1299 if path['type'] == 'move':
1300 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1301 a = numpy.concatenate((a[:-1], a[1:]), 1)
1302 a = a.reshape((len(a) * 2, 3))
1303 pointList = numpy.concatenate((pointList, a))
1304 if path['type'] == 'retract':
1305 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1306 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1307 a = a.reshape((len(a) * 2, 3))
1308 pointList = numpy.concatenate((pointList, a))
1309 ret.append(opengl.GLVBO(pointList))
1313 def getObjectCenterPos(self):
1314 if self._selectedObj is None:
1315 return [0.0, 0.0, 0.0]
1316 pos = self._selectedObj.getPosition()
1317 size = self._selectedObj.getSize()
1318 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1320 def getObjectBoundaryCircle(self):
1321 if self._selectedObj is None:
1323 return self._selectedObj.getBoundaryCircle()
1325 def getObjectSize(self):
1326 if self._selectedObj is None:
1327 return [0.0, 0.0, 0.0]
1328 return self._selectedObj.getSize()
1330 def getObjectMatrix(self):
1331 if self._selectedObj is None:
1332 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1333 return self._selectedObj.getMatrix()
1335 class shaderEditor(wx.Dialog):
1336 def __init__(self, parent, callback, v, f):
1337 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1338 self._callback = callback
1339 s = wx.BoxSizer(wx.VERTICAL)
1341 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1342 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1343 s.Add(self._vertex, 1, flag=wx.EXPAND)
1344 s.Add(self._fragment, 1, flag=wx.EXPAND)
1346 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1347 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1349 self.SetPosition(self.GetParent().GetPosition())
1350 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1353 def OnText(self, e):
1354 self._callback(self._vertex.GetValue(), self._fragment.GetValue())