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))
108 self.notification = openglGui.glNotification(self, (0, 0))
110 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
111 self._sceneUpdateTimer = wx.Timer(self)
112 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
113 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
114 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
118 self.updateToolButtons()
119 self.updateProfileToControls()
121 def showLoadModel(self, button = 1):
123 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)
124 dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
125 if dlg.ShowModal() != wx.ID_OK:
128 filenames = dlg.GetPaths()
130 if len(filenames) < 1:
132 profile.putPreference('lastFile', filenames[0])
134 for filename in filenames:
135 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
136 ext = filename[filename.rfind('.')+1:].upper()
137 if ext == 'G' or ext == 'GCODE':
138 gcodeFilename = filename
139 if gcodeFilename is not None:
140 if self._gcode is not None:
142 for layerVBOlist in self._gcodeVBOs:
143 for vbo in layerVBOlist:
144 self.glReleaseList.append(vbo)
146 self._gcode = gcodeInterpreter.gcode()
147 self._gcodeFilename = gcodeFilename
148 self.printButton.setBottomText('')
149 self.viewSelection.setValue(4)
150 self.printButton.setDisabled(False)
153 if self.viewSelection.getValue() == 4:
154 self.viewSelection.setValue(0)
156 self.loadScene(filenames)
158 def showSaveModel(self):
159 if len(self._scene.objects()) < 1:
161 dlg=wx.FileDialog(self, 'Save 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
162 dlg.SetWildcard(meshLoader.saveWildcardFilter())
163 if dlg.ShowModal() != wx.ID_OK:
166 filename = dlg.GetPath()
168 meshLoader.saveMeshes(filename, self._scene.objects())
170 def OnPrintButton(self, button):
172 if machineCom.machineIsConnected():
173 self.showPrintWindow()
174 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
175 drives = removableStorage.getPossibleSDcardDrives()
177 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))
178 if dlg.ShowModal() != wx.ID_OK:
181 drive = drives[dlg.GetSelection()]
185 filename = self._scene._objectList[0].getName() + '.gcode'
186 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
191 self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, 'Print with USB'))
192 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, 'Save GCode...'))
193 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, 'Slice engine log...'))
197 def showPrintWindow(self):
198 if self._gcodeFilename is None:
200 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
201 if self._gcodeFilename == self._slicer.getGCodeFilename():
202 self._slicer.submitSliceInfoOnline()
204 def showSaveGCode(self):
205 defPath = profile.getPreference('lastFile')
206 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
207 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
208 dlg.SetFilename(self._scene._objectList[0].getName())
209 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
210 if dlg.ShowModal() != wx.ID_OK:
213 filename = dlg.GetPath()
216 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
218 def _copyFile(self, fileA, fileB, allowEject = False):
220 size = float(os.stat(fileA).st_size)
221 with open(fileA, 'rb') as fsrc:
222 with open(fileB, 'wb') as fdst:
224 buf = fsrc.read(16*1024)
228 self.printButton.setProgressBar(float(fsrc.tell()) / size)
233 self.notification.message("Failed to save")
236 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...'))
238 self.notification.message("Saved as %s" % (fileB))
239 self.printButton.setProgressBar(None)
240 if fileA == self._slicer.getGCodeFilename():
241 self._slicer.submitSliceInfoOnline()
243 def _showSliceLog(self):
244 dlg = wx.TextEntryDialog(self, "The slicing engine reported the following", "Engine log...", '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
248 def OnToolSelect(self, button):
249 if self.rotateToolButton.getSelected():
250 self.tool = previewTools.toolRotate(self)
251 elif self.scaleToolButton.getSelected():
252 self.tool = previewTools.toolScale(self)
253 elif self.mirrorToolButton.getSelected():
254 self.tool = previewTools.toolNone(self)
256 self.tool = previewTools.toolNone(self)
257 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
258 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
259 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
260 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
261 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
262 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
263 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
264 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
266 def updateToolButtons(self):
267 if self._selectedObj is None:
271 self.rotateToolButton.setHidden(hidden)
272 self.scaleToolButton.setHidden(hidden)
273 self.mirrorToolButton.setHidden(hidden)
275 self.rotateToolButton.setSelected(False)
276 self.scaleToolButton.setSelected(False)
277 self.mirrorToolButton.setSelected(False)
280 def OnViewChange(self):
281 if self.viewSelection.getValue() == 4:
282 self.viewMode = 'gcode'
283 if self._gcode is not None and self._gcode.layerList is not None:
284 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
285 self._selectObject(None)
286 elif self.viewSelection.getValue() == 1:
287 self.viewMode = 'overhang'
288 elif self.viewSelection.getValue() == 2:
289 self.viewMode = 'transparent'
290 elif self.viewSelection.getValue() == 3:
291 self.viewMode = 'xray'
293 self.viewMode = 'normal'
294 self.layerSelect.setHidden(self.viewMode != 'gcode')
297 def OnRotateReset(self, button):
298 if self._selectedObj is None:
300 self._selectedObj.resetRotation()
301 self._scene.pushFree()
302 self._selectObject(self._selectedObj)
305 def OnLayFlat(self, button):
306 if self._selectedObj is None:
308 self._selectedObj.layFlat()
309 self._scene.pushFree()
310 self._selectObject(self._selectedObj)
313 def OnScaleReset(self, button):
314 if self._selectedObj is None:
316 self._selectedObj.resetScale()
317 self._selectObject(self._selectedObj)
318 self.updateProfileToControls()
321 def OnScaleMax(self, button):
322 if self._selectedObj is None:
324 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
325 self._scene.pushFree()
326 self._selectObject(self._selectedObj)
327 self.updateProfileToControls()
330 def OnMirror(self, axis):
331 if self._selectedObj is None:
333 self._selectedObj.mirror(axis)
336 def OnScaleEntry(self, value, axis):
337 if self._selectedObj is None:
343 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
344 self.updateProfileToControls()
345 self._scene.pushFree()
346 self._selectObject(self._selectedObj)
349 def OnScaleEntryMM(self, value, axis):
350 if self._selectedObj is None:
356 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
357 self.updateProfileToControls()
358 self._scene.pushFree()
359 self._selectObject(self._selectedObj)
362 def OnDeleteAll(self, e):
363 while len(self._scene.objects()) > 0:
364 self._deleteObject(self._scene.objects()[0])
365 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
367 def OnMultiply(self, e):
368 if self._focusObj is None:
371 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
372 if dlg.ShowModal() != wx.ID_OK:
381 self._scene.add(newObj)
382 self._scene.centerAll()
383 if not self._scene.checkPlatform(newObj):
388 self.notification.message("Could not create more then %d items" % (n - 1))
389 self._scene.remove(newObj)
390 self._scene.centerAll()
393 def OnSplitObject(self, e):
394 if self._focusObj is None:
396 self._scene.remove(self._focusObj)
397 for obj in self._focusObj.split(self._splitCallback):
398 if numpy.max(obj.getSize()) > 2.0:
400 self._scene.centerAll()
401 self._selectObject(None)
404 def _splitCallback(self, progress):
407 def OnMergeObjects(self, e):
408 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
410 self._scene.merge(self._selectedObj, self._focusObj)
413 def sceneUpdated(self):
414 self._sceneUpdateTimer.Start(500, True)
415 self._slicer.abortSlicer()
416 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
419 def _onRunSlicer(self, e):
420 if self._isSimpleMode:
421 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
422 self._slicer.runSlicer(self._scene)
423 if self._isSimpleMode:
424 profile.resetTempOverride()
426 def _updateSliceProgress(self, progressValue, ready):
428 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
430 self.printButton.setDisabled(not ready)
431 if progressValue >= 0.0:
432 self.printButton.setProgressBar(progressValue)
434 self.printButton.setProgressBar(None)
435 if self._gcode is not None:
437 for layerVBOlist in self._gcodeVBOs:
438 for vbo in layerVBOlist:
439 self.glReleaseList.append(vbo)
442 self.printButton.setProgressBar(None)
443 cost = self._slicer.getFilamentCost()
445 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
447 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
448 self._gcode = gcodeInterpreter.gcode()
449 self._gcodeFilename = self._slicer.getGCodeFilename()
451 self.printButton.setBottomText('')
454 def _loadGCode(self):
455 self._gcode.progressCallback = self._gcodeLoadCallback
456 self._gcode.load(self._gcodeFilename)
458 def _gcodeLoadCallback(self, progress):
459 if self._gcode is None:
461 if len(self._gcode.layerList) % 15 == 0:
463 if self._gcode is None:
465 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
466 if self.viewMode == 'gcode':
470 def loadScene(self, fileList):
471 for filename in fileList:
473 objList = meshLoader.loadMeshes(filename)
475 traceback.print_exc()
478 if self._objectLoadShader is not None:
479 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
483 self._scene.centerAll()
484 self._selectObject(obj)
485 if obj.getScale()[0] < 1.0:
486 self.notification.message("Warning: Object scaled down.")
489 def _deleteObject(self, obj):
490 if obj == self._selectedObj:
491 self._selectObject(None)
492 if obj == self._focusObj:
493 self._focusObj = None
494 self._scene.remove(obj)
495 for m in obj._meshList:
496 if m.vbo is not None and m.vbo.decRef():
497 self.glReleaseList.append(m.vbo)
502 def _selectObject(self, obj, zoom = True):
503 if obj != self._selectedObj:
504 self._selectedObj = obj
505 self.updateProfileToControls()
506 self.updateToolButtons()
507 if zoom and obj is not None:
508 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
509 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
510 newZoom = obj.getBoundaryCircle() * 6
511 if newZoom > numpy.max(self._machineSize) * 3:
512 newZoom = numpy.max(self._machineSize) * 3
513 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
515 def updateProfileToControls(self):
516 oldSimpleMode = self._isSimpleMode
517 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
518 if self._isSimpleMode != oldSimpleMode:
519 self._scene.arrangeAll()
521 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
522 self._objColors[0] = profile.getPreferenceColour('model_colour')
523 self._objColors[1] = profile.getPreferenceColour('model_colour2')
524 self._objColors[2] = profile.getPreferenceColour('model_colour3')
525 self._objColors[3] = profile.getPreferenceColour('model_colour4')
526 self._scene.setMachineSize(self._machineSize)
527 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
528 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'))
530 if self._selectedObj is not None:
531 scale = self._selectedObj.getScale()
532 size = self._selectedObj.getSize()
533 self.scaleXctrl.setValue(round(scale[0], 2))
534 self.scaleYctrl.setValue(round(scale[1], 2))
535 self.scaleZctrl.setValue(round(scale[2], 2))
536 self.scaleXmmctrl.setValue(round(size[0], 2))
537 self.scaleYmmctrl.setValue(round(size[1], 2))
538 self.scaleZmmctrl.setValue(round(size[2], 2))
540 def OnKeyChar(self, keyCode):
541 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
542 if self._selectedObj is not None:
543 self._deleteObject(self._selectedObj)
545 if keyCode == wx.WXK_UP:
546 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
548 elif keyCode == wx.WXK_DOWN:
549 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
551 elif keyCode == wx.WXK_PAGEUP:
552 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
554 elif keyCode == wx.WXK_PAGEDOWN:
555 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
558 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
559 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
560 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
561 from collections import defaultdict
562 from gc import get_objects
563 self._beforeLeakTest = defaultdict(int)
564 for i in get_objects():
565 self._beforeLeakTest[type(i)] += 1
566 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
567 from collections import defaultdict
568 from gc import get_objects
569 self._afterLeakTest = defaultdict(int)
570 for i in get_objects():
571 self._afterLeakTest[type(i)] += 1
572 for k in self._afterLeakTest:
573 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
574 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
576 def ShaderUpdate(self, v, f):
577 s = opengl.GLShader(v, f)
579 self._objectLoadShader.release()
580 self._objectLoadShader = s
581 for obj in self._scene.objects():
582 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
585 def OnMouseDown(self,e):
586 self._mouseX = e.GetX()
587 self._mouseY = e.GetY()
588 self._mouseClick3DPos = self._mouse3Dpos
589 self._mouseClickFocus = self._focusObj
591 self._mouseState = 'doubleClick'
593 self._mouseState = 'dragOrClick'
594 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
595 p0 -= self.getObjectCenterPos() - self._viewTarget
596 p1 -= self.getObjectCenterPos() - self._viewTarget
597 if self.tool.OnDragStart(p0, p1):
598 self._mouseState = 'tool'
599 if self._mouseState == 'dragOrClick':
600 if e.GetButton() == 1:
601 if self._focusObj is not None:
602 self._selectObject(self._focusObj, False)
605 def OnMouseUp(self, e):
606 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
608 if self._mouseState == 'dragOrClick':
609 if e.GetButton() == 1:
610 self._selectObject(self._focusObj)
611 if e.GetButton() == 3:
613 if self._focusObj is not None:
614 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
615 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
616 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
617 if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1:
618 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
619 if len(self._scene.objects()) > 0:
620 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
621 if menu.MenuItemCount > 0:
624 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
625 self._scene.pushFree()
627 elif self._mouseState == 'tool':
628 if self.tempMatrix is not None and self._selectedObj is not None:
629 self._selectedObj.applyMatrix(self.tempMatrix)
630 self._scene.pushFree()
631 self._selectObject(self._selectedObj)
632 self.tempMatrix = None
633 self.tool.OnDragEnd()
635 self._mouseState = None
637 def OnMouseMotion(self,e):
638 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
639 p0 -= self.getObjectCenterPos() - self._viewTarget
640 p1 -= self.getObjectCenterPos() - self._viewTarget
642 if e.Dragging() and self._mouseState is not None:
643 if self._mouseState == 'tool':
644 self.tool.OnDrag(p0, p1)
645 elif not e.LeftIsDown() and e.RightIsDown():
646 self._mouseState = 'drag'
647 if wx.GetKeyState(wx.WXK_SHIFT):
648 a = math.cos(math.radians(self._yaw)) / 3.0
649 b = math.sin(math.radians(self._yaw)) / 3.0
650 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
651 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
652 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
653 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
655 self._yaw += e.GetX() - self._mouseX
656 self._pitch -= e.GetY() - self._mouseY
657 if self._pitch > 170:
661 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
662 self._mouseState = 'drag'
663 self._zoom += e.GetY() - self._mouseY
666 if self._zoom > numpy.max(self._machineSize) * 3:
667 self._zoom = numpy.max(self._machineSize) * 3
668 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
669 self._mouseState = 'dragObject'
670 z = max(0, self._mouseClick3DPos[2])
671 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
672 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
677 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
678 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
679 diff = cursorZ1 - cursorZ0
680 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
681 if not e.Dragging() or self._mouseState != 'tool':
682 self.tool.OnMouseMove(p0, p1)
684 self._mouseX = e.GetX()
685 self._mouseY = e.GetY()
687 def OnMouseWheel(self, e):
688 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
689 delta = max(min(delta,4),-4)
690 self._zoom *= 1.0 - delta / 10.0
693 if self._zoom > numpy.max(self._machineSize) * 3:
694 self._zoom = numpy.max(self._machineSize) * 3
697 def OnMouseLeave(self, e):
701 def getMouseRay(self, x, y):
702 if self._viewport is None:
703 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
704 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
705 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
706 p0 -= self._viewTarget
707 p1 -= self._viewTarget
710 def _init3DView(self):
711 # set viewing projection
712 size = self.GetSize()
713 glViewport(0, 0, size.GetWidth(), size.GetHeight())
716 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
718 glDisable(GL_RESCALE_NORMAL)
719 glDisable(GL_LIGHTING)
721 glEnable(GL_DEPTH_TEST)
722 glDisable(GL_CULL_FACE)
724 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
726 glClearColor(0.8, 0.8, 0.8, 1.0)
730 glMatrixMode(GL_PROJECTION)
732 aspect = float(size.GetWidth()) / float(size.GetHeight())
733 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
735 glMatrixMode(GL_MODELVIEW)
737 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
740 if machineCom.machineIsConnected():
741 self.printButton._imageID = 6
742 self.printButton._tooltip = 'Print'
743 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
744 self.printButton._imageID = 2
745 self.printButton._tooltip = 'Toolpath to SD'
747 self.printButton._imageID = 3
748 self.printButton._tooltip = 'Save toolpath'
750 if self._animView is not None:
751 self._viewTarget = self._animView.getPosition()
752 if self._animView.isDone():
753 self._animView = None
754 if self._animZoom is not None:
755 self._zoom = self._animZoom.getPosition()
756 if self._animZoom.isDone():
757 self._animZoom = None
758 if self.viewMode == 'gcode' and self._gcode is not None:
760 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
763 if self._objectShader is None:
764 if opengl.hasShaderSupport():
765 self._objectShader = opengl.GLShader("""
766 varying float light_amount;
770 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
771 gl_FrontColor = gl_Color;
773 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
777 varying float light_amount;
781 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
784 self._objectOverhangShader = opengl.GLShader("""
785 uniform float cosAngle;
786 uniform mat3 rotMatrix;
787 varying float light_amount;
791 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
792 gl_FrontColor = gl_Color;
794 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
796 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
798 light_amount = -10.0;
802 varying float light_amount;
806 if (light_amount == -10.0)
808 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
810 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
814 self._objectLoadShader = opengl.GLShader("""
815 uniform float intensity;
817 varying float light_amount;
821 vec4 tmp = gl_Vertex;
822 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
823 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
824 gl_Position = gl_ModelViewProjectionMatrix * tmp;
825 gl_FrontColor = gl_Color;
827 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
831 uniform float intensity;
832 varying float light_amount;
836 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
839 if self._objectShader == None or not self._objectShader.isValid():
840 self._objectShader = opengl.GLFakeShader()
841 self._objectOverhangShader = opengl.GLFakeShader()
842 self._objectLoadShader = None
844 glTranslate(0,0,-self._zoom)
845 glRotate(-self._pitch, 1,0,0)
846 glRotate(self._yaw, 0,0,1)
847 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
849 self._viewport = glGetIntegerv(GL_VIEWPORT)
850 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
851 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
853 glClearColor(1,1,1,1)
854 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
856 if self.viewMode != 'gcode':
857 for n in xrange(0, len(self._scene.objects())):
858 obj = self._scene.objects()[n]
859 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
860 self._renderObject(obj)
862 if self._mouseX > -1:
864 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
865 if n < len(self._scene.objects()):
866 self._focusObj = self._scene.objects()[n]
868 self._focusObj = None
869 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
870 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
871 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
872 self._mouse3Dpos -= self._viewTarget
875 glTranslate(0,0,-self._zoom)
876 glRotate(-self._pitch, 1,0,0)
877 glRotate(self._yaw, 0,0,1)
878 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
880 if self.viewMode == 'gcode':
881 if self._gcode is not None and self._gcode.layerList is None:
882 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
883 self._gcodeLoadThread.daemon = True
884 self._gcodeLoadThread.start()
885 if self._gcode is not None and self._gcode.layerList is not None:
887 if profile.getPreference('machine_center_is_zero') != 'True':
888 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
890 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
891 for n in xrange(0, drawUpTill):
892 c = 1.0 - float(drawUpTill - n) / 15
894 if len(self._gcodeVBOs) < n + 1:
895 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
896 if time.time() - t > 0.5:
899 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
900 if n == drawUpTill - 1:
901 if len(self._gcodeVBOs[n]) < 9:
902 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
904 self._gcodeVBOs[n][8].render(GL_QUADS)
906 self._gcodeVBOs[n][9].render(GL_QUADS)
908 self._gcodeVBOs[n][10].render(GL_QUADS)
910 self._gcodeVBOs[n][11].render(GL_QUADS)
913 self._gcodeVBOs[n][12].render(GL_QUADS)
914 glColor3f(c/2, c/2, 0.0)
915 self._gcodeVBOs[n][13].render(GL_QUADS)
917 self._gcodeVBOs[n][14].render(GL_QUADS)
918 self._gcodeVBOs[n][15].render(GL_QUADS)
920 self._gcodeVBOs[n][16].render(GL_LINES)
923 self._gcodeVBOs[n][0].render(GL_LINES)
925 self._gcodeVBOs[n][1].render(GL_LINES)
927 self._gcodeVBOs[n][2].render(GL_LINES)
929 self._gcodeVBOs[n][3].render(GL_LINES)
932 self._gcodeVBOs[n][4].render(GL_LINES)
933 glColor3f(c/2, c/2, 0.0)
934 self._gcodeVBOs[n][5].render(GL_LINES)
936 self._gcodeVBOs[n][6].render(GL_LINES)
937 self._gcodeVBOs[n][7].render(GL_LINES)
940 glStencilFunc(GL_ALWAYS, 1, 1)
941 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
943 if self.viewMode == 'overhang':
944 self._objectOverhangShader.bind()
945 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
947 self._objectShader.bind()
948 for obj in self._scene.objects():
949 if obj._loadAnim is not None:
950 if obj._loadAnim.isDone():
955 if self._focusObj == obj:
957 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
960 if self._selectedObj == obj or self._selectedObj is None:
961 #If we want transparent, then first render a solid black model to remove the printer size lines.
962 if self.viewMode == 'transparent':
963 glColor4f(0, 0, 0, 0)
964 self._renderObject(obj)
966 glBlendFunc(GL_ONE, GL_ONE)
967 glDisable(GL_DEPTH_TEST)
969 if self.viewMode == 'xray':
970 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
971 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
972 glEnable(GL_STENCIL_TEST)
974 if self.viewMode == 'overhang':
975 if self._selectedObj == obj and self.tempMatrix is not None:
976 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
978 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
980 if not self._scene.checkPlatform(obj):
981 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
982 self._renderObject(obj)
984 self._renderObject(obj, brightness)
985 glDisable(GL_STENCIL_TEST)
987 glEnable(GL_DEPTH_TEST)
988 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
990 if self.viewMode == 'xray':
993 glEnable(GL_STENCIL_TEST)
994 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
995 glDisable(GL_DEPTH_TEST)
996 for i in xrange(2, 15, 2):
997 glStencilFunc(GL_EQUAL, i, 0xFF)
998 glColor(float(i)/10, float(i)/10, float(i)/5)
1000 glVertex3f(-1000,-1000,-10)
1001 glVertex3f( 1000,-1000,-10)
1002 glVertex3f( 1000, 1000,-10)
1003 glVertex3f(-1000, 1000,-10)
1005 for i in xrange(1, 15, 2):
1006 glStencilFunc(GL_EQUAL, i, 0xFF)
1007 glColor(float(i)/10, 0, 0)
1009 glVertex3f(-1000,-1000,-10)
1010 glVertex3f( 1000,-1000,-10)
1011 glVertex3f( 1000, 1000,-10)
1012 glVertex3f(-1000, 1000,-10)
1015 glDisable(GL_STENCIL_TEST)
1016 glEnable(GL_DEPTH_TEST)
1018 self._objectShader.unbind()
1020 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1022 if self._objectLoadShader is not None:
1023 self._objectLoadShader.bind()
1024 glColor4f(0.2, 0.6, 1.0, 1.0)
1025 for obj in self._scene.objects():
1026 if obj._loadAnim is None:
1028 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1029 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1030 self._renderObject(obj)
1031 self._objectLoadShader.unbind()
1036 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1038 z = self._usbPrintMonitor.getZ()
1039 size = self._machineSize
1040 glColor4ub(255,255,0,128)
1042 glVertex3f(-size[0]/2,-size[1]/2, z)
1043 glVertex3f( size[0]/2,-size[1]/2, z)
1044 glVertex3f( size[0]/2, size[1]/2, z)
1045 glVertex3f(-size[0]/2, size[1]/2, z)
1048 if self.viewMode == 'gcode':
1049 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1050 glDisable(GL_DEPTH_TEST)
1053 glTranslate(0,-4,-10)
1054 glColor4ub(60,60,60,255)
1055 opengl.glDrawStringCenter('Loading toolpath for visualization...')
1058 #Draw the object box-shadow, so you can see where it will collide with other objects.
1059 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1060 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1062 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1064 glEnable(GL_CULL_FACE)
1065 glColor4f(0,0,0,0.12)
1067 glVertex3f(-size[0], size[1], 0.1)
1068 glVertex3f(-size[0], -size[1], 0.1)
1069 glVertex3f( size[0], -size[1], 0.1)
1070 glVertex3f( size[0], size[1], 0.1)
1072 glDisable(GL_CULL_FACE)
1075 #Draw the outline of the selected object, on top of everything else except the GUI.
1076 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1077 glDisable(GL_DEPTH_TEST)
1078 glEnable(GL_CULL_FACE)
1079 glEnable(GL_STENCIL_TEST)
1081 glStencilFunc(GL_EQUAL, 0, 255)
1083 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1085 glColor4f(1,1,1,0.5)
1086 self._renderObject(self._selectedObj)
1087 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1089 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1090 glDisable(GL_STENCIL_TEST)
1091 glDisable(GL_CULL_FACE)
1092 glEnable(GL_DEPTH_TEST)
1094 if self._selectedObj is not None:
1096 pos = self.getObjectCenterPos()
1097 glTranslate(pos[0], pos[1], pos[2])
1100 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1101 glDisable(GL_DEPTH_TEST)
1104 glTranslate(0,-4,-10)
1105 glColor4ub(60,60,60,255)
1106 opengl.glDrawStringCenter('Overhang view not working due to lack of OpenGL shaders support.')
1109 def _renderObject(self, obj, brightness = False, addSink = True):
1112 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1114 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1116 if self.tempMatrix is not None and obj == self._selectedObj:
1117 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1118 glMultMatrixf(tempMatrix)
1120 offset = obj.getDrawOffset()
1121 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1123 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1124 glMultMatrixf(tempMatrix)
1127 for m in obj._meshList:
1129 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1131 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1136 def _drawMachine(self):
1137 glEnable(GL_CULL_FACE)
1140 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
1142 if profile.getPreference('machine_type').startswith('ultimaker'):
1143 if self._platformMesh is None:
1144 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
1145 self._platformMesh._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1146 glColor4f(1,1,1,0.5)
1147 self._objectShader.bind()
1148 self._renderObject(self._platformMesh, False, False)
1149 self._objectShader.unbind()
1154 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1155 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1156 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1157 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1158 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1159 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1162 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1163 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1164 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1165 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1166 v4 = [ size[0] / 2, size[1] / 2, 0]
1167 v5 = [ size[0] / 2,-size[1] / 2, 0]
1168 v6 = [-size[0] / 2, size[1] / 2, 0]
1169 v7 = [-size[0] / 2,-size[1] / 2, 0]
1171 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1172 glEnableClientState(GL_VERTEX_ARRAY)
1173 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1175 glColor4ub(5, 171, 231, 64)
1176 glDrawArrays(GL_QUADS, 0, 4)
1177 glColor4ub(5, 171, 231, 96)
1178 glDrawArrays(GL_QUADS, 4, 8)
1179 glColor4ub(5, 171, 231, 128)
1180 glDrawArrays(GL_QUADS, 12, 8)
1181 glDisableClientState(GL_VERTEX_ARRAY)
1183 sx = self._machineSize[0]
1184 sy = self._machineSize[1]
1185 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1186 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1191 x1 = max(min(x1, sx/2), -sx/2)
1192 y1 = max(min(y1, sy/2), -sy/2)
1193 x2 = max(min(x2, sx/2), -sx/2)
1194 y2 = max(min(y2, sy/2), -sy/2)
1195 if (x & 1) == (y & 1):
1196 glColor4ub(5, 171, 231, 127)
1198 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1200 glVertex3f(x1, y1, -0.02)
1201 glVertex3f(x2, y1, -0.02)
1202 glVertex3f(x2, y2, -0.02)
1203 glVertex3f(x1, y2, -0.02)
1207 glDisable(GL_CULL_FACE)
1209 def _generateGCodeVBOs(self, layer):
1211 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1212 if ':' in extrudeType:
1213 extruder = int(extrudeType[extrudeType.find(':')+1:])
1214 extrudeType = extrudeType[0:extrudeType.find(':')]
1217 pointList = numpy.zeros((0,3), numpy.float32)
1219 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1221 a = numpy.concatenate((a[:-1], a[1:]), 1)
1222 a = a.reshape((len(a) * 2, 3))
1223 pointList = numpy.concatenate((pointList, a))
1224 ret.append(opengl.GLVBO(pointList))
1227 def _generateGCodeVBOs2(self, layer):
1228 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1229 filamentArea = math.pi * filamentRadius * filamentRadius
1230 useFilamentArea = profile.getPreference('gcode_flavor') == 'UltiGCode'
1233 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1234 if ':' in extrudeType:
1235 extruder = int(extrudeType[extrudeType.find(':')+1:])
1236 extrudeType = extrudeType[0:extrudeType.find(':')]
1239 pointList = numpy.zeros((0,3), numpy.float32)
1241 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1243 if extrudeType == 'FILL':
1246 normal = a[1:] - a[:-1]
1247 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1248 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1251 ePerDist = path['extrusion'][1:] / lens
1253 lineWidth = ePerDist / path['layerThickness'] / 2.0
1255 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1257 normal[:,0] *= lineWidth
1258 normal[:,1] *= lineWidth
1260 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1261 b = numpy.concatenate((b, a[1:] + normal), 1)
1262 b = numpy.concatenate((b, a[1:] - normal), 1)
1263 b = numpy.concatenate((b, a[:-1] - normal), 1)
1264 b = numpy.concatenate((b, a[:-1] + normal), 1)
1265 b = b.reshape((len(b) * 4, 3))
1268 normal2 = normal[:-1] + normal[1:]
1269 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1270 normal2[:,0] /= lens2
1271 normal2[:,1] /= lens2
1272 normal2[:,0] *= lineWidth[:-1]
1273 normal2[:,1] *= lineWidth[:-1]
1275 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1276 c = numpy.concatenate((c, a[1:-1]), 1)
1277 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1278 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1279 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1281 c = numpy.concatenate((c, a[1:-1]), 1)
1282 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1283 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1284 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1286 c = c.reshape((len(c) * 8, 3))
1288 pointList = numpy.concatenate((pointList, b, c))
1290 pointList = numpy.concatenate((pointList, b))
1291 ret.append(opengl.GLVBO(pointList))
1293 pointList = numpy.zeros((0,3), numpy.float32)
1295 if path['type'] == 'move':
1296 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1297 a = numpy.concatenate((a[:-1], a[1:]), 1)
1298 a = a.reshape((len(a) * 2, 3))
1299 pointList = numpy.concatenate((pointList, a))
1300 if path['type'] == 'retract':
1301 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1302 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1303 a = a.reshape((len(a) * 2, 3))
1304 pointList = numpy.concatenate((pointList, a))
1305 ret.append(opengl.GLVBO(pointList))
1309 def getObjectCenterPos(self):
1310 if self._selectedObj is None:
1311 return [0.0, 0.0, 0.0]
1312 pos = self._selectedObj.getPosition()
1313 size = self._selectedObj.getSize()
1314 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1316 def getObjectBoundaryCircle(self):
1317 if self._selectedObj is None:
1319 return self._selectedObj.getBoundaryCircle()
1321 def getObjectSize(self):
1322 if self._selectedObj is None:
1323 return [0.0, 0.0, 0.0]
1324 return self._selectedObj.getSize()
1326 def getObjectMatrix(self):
1327 if self._selectedObj is None:
1328 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1329 return self._selectedObj.getMatrix()
1331 class shaderEditor(wx.Dialog):
1332 def __init__(self, parent, callback, v, f):
1333 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1334 self._callback = callback
1335 s = wx.BoxSizer(wx.VERTICAL)
1337 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1338 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1339 s.Add(self._vertex, 1, flag=wx.EXPAND)
1340 s.Add(self._fragment, 1, flag=wx.EXPAND)
1342 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1343 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1345 self.SetPosition(self.GetParent().GetPosition())
1346 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1349 def OnText(self, e):
1350 self._callback(self._vertex.GetValue(), self._fragment.GetValue())