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
30 class SceneView(openglGui.glGuiPanel):
31 def __init__(self, parent):
32 super(SceneView, self).__init__(parent)
37 self._scene = objectScene.Scene()
40 self._gcodeFilename = None
41 self._gcodeLoadThread = None
42 self._objectShader = None
43 self._objectLoadShader = None
45 self._selectedObj = None
46 self._objColors = [None,None,None,None]
49 self._mouseState = None
50 self._viewTarget = numpy.array([0,0,0], numpy.float32)
53 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
54 self._platformMesh._drawOffset = numpy.array([0,0,2.5], numpy.float32)
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.notification = openglGui.glNotification(self, (0, 0))
108 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
109 self._sceneUpdateTimer = wx.Timer(self)
110 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
111 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
112 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
116 self.updateToolButtons()
117 self.updateProfileToControls()
119 def showLoadModel(self, button = 1):
121 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)
122 dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
123 if dlg.ShowModal() != wx.ID_OK:
126 filenames = dlg.GetPaths()
128 if len(filenames) < 1:
130 profile.putPreference('lastFile', filenames[0])
132 for filename in filenames:
133 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
134 ext = filename[filename.rfind('.')+1:].upper()
135 if ext == 'G' or ext == 'GCODE':
136 gcodeFilename = filename
137 if gcodeFilename is not None:
138 if self._gcode is not None:
140 for layerVBOlist in self._gcodeVBOs:
141 for vbo in layerVBOlist:
142 self.glReleaseList.append(vbo)
144 self._gcode = gcodeInterpreter.gcode()
145 self._gcodeFilename = gcodeFilename
146 self.printButton.setBottomText('')
147 self.viewSelection.setValue(4)
148 self.printButton.setDisabled(False)
151 if self.viewSelection.getValue() == 4:
152 self.viewSelection.setValue(0)
154 self.loadScene(filenames)
156 def showSaveModel(self):
157 if len(self._scene.objects()) < 1:
159 dlg=wx.FileDialog(self, 'Save 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
160 dlg.SetWildcard(meshLoader.saveWildcardFilter())
161 if dlg.ShowModal() != wx.ID_OK:
164 filename = dlg.GetPath()
166 meshLoader.saveMeshes(filename, self._scene.objects())
168 def OnPrintButton(self, button):
170 if machineCom.machineIsConnected():
171 self.showPrintWindow()
172 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
173 drives = removableStorage.getPossibleSDcardDrives()
175 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))
176 if dlg.ShowModal() != wx.ID_OK:
179 drive = drives[dlg.GetSelection()]
183 filename = self._scene._objectList[0].getName() + '.gcode'
184 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
189 self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, 'Print with USB'))
190 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, 'Save GCode...'))
191 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, 'Slice engine log...'))
195 def showPrintWindow(self):
196 if self._gcodeFilename is None:
198 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
199 if self._gcodeFilename == self._slicer.getGCodeFilename():
200 self._slicer.submitSliceInfoOnline()
202 def showSaveGCode(self):
203 defPath = profile.getPreference('lastFile')
204 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
205 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
206 dlg.SetFilename(self._scene._objectList[0].getName())
207 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
208 if dlg.ShowModal() != wx.ID_OK:
211 filename = dlg.GetPath()
214 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
216 def _copyFile(self, fileA, fileB, allowEject = False):
218 size = float(os.stat(fileA).st_size)
219 with open(fileA, 'rb') as fsrc:
220 with open(fileB, 'wb') as fdst:
222 buf = fsrc.read(16*1024)
226 self.printButton.setProgressBar(float(fsrc.tell()) / size)
231 self.notification.message("Failed to save")
234 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...'))
236 self.notification.message("Saved as %s" % (fileB))
237 self.printButton.setProgressBar(None)
238 if fileA == self._slicer.getGCodeFilename():
239 self._slicer.submitSliceInfoOnline()
241 def _showSliceLog(self):
242 dlg = wx.TextEntryDialog(self, "The slicing engine reported the following", "Engine log...", '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
246 def OnToolSelect(self, button):
247 if self.rotateToolButton.getSelected():
248 self.tool = previewTools.toolRotate(self)
249 elif self.scaleToolButton.getSelected():
250 self.tool = previewTools.toolScale(self)
251 elif self.mirrorToolButton.getSelected():
252 self.tool = previewTools.toolNone(self)
254 self.tool = previewTools.toolNone(self)
255 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
256 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
257 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
258 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
259 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
260 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
261 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
262 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
264 def updateToolButtons(self):
265 if self._selectedObj is None:
269 self.rotateToolButton.setHidden(hidden)
270 self.scaleToolButton.setHidden(hidden)
271 self.mirrorToolButton.setHidden(hidden)
273 self.rotateToolButton.setSelected(False)
274 self.scaleToolButton.setSelected(False)
275 self.mirrorToolButton.setSelected(False)
278 def OnViewChange(self):
279 if self.viewSelection.getValue() == 4:
280 self.viewMode = 'gcode'
281 if self._gcode is not None and self._gcode.layerList is not None:
282 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
283 self._selectObject(None)
284 elif self.viewSelection.getValue() == 1:
285 self.viewMode = 'overhang'
286 elif self.viewSelection.getValue() == 2:
287 self.viewMode = 'transparent'
288 elif self.viewSelection.getValue() == 3:
289 self.viewMode = 'xray'
291 self.viewMode = 'normal'
292 self.layerSelect.setHidden(self.viewMode != 'gcode')
295 def OnRotateReset(self, button):
296 if self._selectedObj is None:
298 self._selectedObj.resetRotation()
299 self._scene.pushFree()
300 self._selectObject(self._selectedObj)
303 def OnLayFlat(self, button):
304 if self._selectedObj is None:
306 self._selectedObj.layFlat()
307 self._scene.pushFree()
308 self._selectObject(self._selectedObj)
311 def OnScaleReset(self, button):
312 if self._selectedObj is None:
314 self._selectedObj.resetScale()
315 self._selectObject(self._selectedObj)
316 self.updateProfileToControls()
319 def OnScaleMax(self, button):
320 if self._selectedObj is None:
322 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
323 self._scene.pushFree()
324 self._selectObject(self._selectedObj)
325 self.updateProfileToControls()
328 def OnMirror(self, axis):
329 if self._selectedObj is None:
331 self._selectedObj.mirror(axis)
334 def OnScaleEntry(self, value, axis):
335 if self._selectedObj is None:
341 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
342 self.updateProfileToControls()
343 self._scene.pushFree()
344 self._selectObject(self._selectedObj)
347 def OnScaleEntryMM(self, value, axis):
348 if self._selectedObj is None:
354 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
355 self.updateProfileToControls()
356 self._scene.pushFree()
357 self._selectObject(self._selectedObj)
360 def OnDeleteAll(self, e):
361 while len(self._scene.objects()) > 0:
362 self._deleteObject(self._scene.objects()[0])
363 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
365 def OnMultiply(self, e):
366 if self._focusObj is None:
369 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
370 if dlg.ShowModal() != wx.ID_OK:
379 self._scene.add(newObj)
380 self._scene.centerAll()
381 if not self._scene.checkPlatform(newObj):
386 self.notification.message("Could not create more then %d items" % (n - 1))
387 self._scene.remove(newObj)
388 self._scene.centerAll()
391 def OnSplitObject(self, e):
392 if self._focusObj is None:
394 self._scene.remove(self._focusObj)
395 for obj in self._focusObj.split(self._splitCallback):
396 if numpy.max(obj.getSize()) > 2.0:
398 self._scene.centerAll()
399 self._selectObject(None)
402 def _splitCallback(self, progress):
405 def OnMergeObjects(self, e):
406 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
408 self._scene.merge(self._selectedObj, self._focusObj)
411 def sceneUpdated(self):
412 self._sceneUpdateTimer.Start(500, True)
413 self._slicer.abortSlicer()
414 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
417 def _onRunSlicer(self, e):
418 if self._isSimpleMode:
419 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
420 self._slicer.runSlicer(self._scene)
421 if self._isSimpleMode:
422 profile.resetTempOverride()
424 def _updateSliceProgress(self, progressValue, ready):
426 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
428 self.printButton.setDisabled(not ready)
429 if progressValue >= 0.0:
430 self.printButton.setProgressBar(progressValue)
432 self.printButton.setProgressBar(None)
433 if self._gcode is not None:
435 for layerVBOlist in self._gcodeVBOs:
436 for vbo in layerVBOlist:
437 self.glReleaseList.append(vbo)
440 self.printButton.setProgressBar(None)
441 cost = self._slicer.getFilamentCost()
443 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
445 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
446 self._gcode = gcodeInterpreter.gcode()
447 self._gcodeFilename = self._slicer.getGCodeFilename()
449 self.printButton.setBottomText('')
452 def _loadGCode(self):
453 self._gcode.progressCallback = self._gcodeLoadCallback
454 self._gcode.load(self._gcodeFilename)
456 def _gcodeLoadCallback(self, progress):
457 if self._gcode is None:
459 if len(self._gcode.layerList) % 15 == 0:
461 if self._gcode is None:
463 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
464 if self.viewMode == 'gcode':
468 def loadScene(self, fileList):
469 for filename in fileList:
471 objList = meshLoader.loadMeshes(filename)
473 traceback.print_exc()
476 if self._objectLoadShader is not None:
477 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
481 self._scene.centerAll()
482 self._selectObject(obj)
483 if obj.getScale()[0] < 1.0:
484 self.notification.message("Warning: Object scaled down.")
487 def _deleteObject(self, obj):
488 if obj == self._selectedObj:
489 self._selectObject(None)
490 if obj == self._focusObj:
491 self._focusObj = None
492 self._scene.remove(obj)
493 for m in obj._meshList:
494 if m.vbo is not None and m.vbo.decRef():
495 self.glReleaseList.append(m.vbo)
500 def _selectObject(self, obj, zoom = True):
501 if obj != self._selectedObj:
502 self._selectedObj = obj
503 self.updateProfileToControls()
504 self.updateToolButtons()
505 if zoom and obj is not None:
506 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
507 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
508 newZoom = obj.getBoundaryCircle() * 6
509 if newZoom > numpy.max(self._machineSize) * 3:
510 newZoom = numpy.max(self._machineSize) * 3
511 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
513 def updateProfileToControls(self):
514 oldSimpleMode = self._isSimpleMode
515 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
516 if self._isSimpleMode != oldSimpleMode:
517 self._scene.arrangeAll()
519 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
520 self._objColors[0] = profile.getPreferenceColour('model_colour')
521 self._objColors[1] = profile.getPreferenceColour('model_colour2')
522 self._objColors[2] = profile.getPreferenceColour('model_colour3')
523 self._objColors[3] = profile.getPreferenceColour('model_colour4')
524 self._scene.setMachineSize(self._machineSize)
525 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
526 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'))
528 if self._selectedObj is not None:
529 scale = self._selectedObj.getScale()
530 size = self._selectedObj.getSize()
531 self.scaleXctrl.setValue(round(scale[0], 2))
532 self.scaleYctrl.setValue(round(scale[1], 2))
533 self.scaleZctrl.setValue(round(scale[2], 2))
534 self.scaleXmmctrl.setValue(round(size[0], 2))
535 self.scaleYmmctrl.setValue(round(size[1], 2))
536 self.scaleZmmctrl.setValue(round(size[2], 2))
538 def OnKeyChar(self, keyCode):
539 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
540 if self._selectedObj is not None:
541 self._deleteObject(self._selectedObj)
543 if keyCode == wx.WXK_UP:
544 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
546 elif keyCode == wx.WXK_DOWN:
547 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
549 elif keyCode == wx.WXK_PAGEUP:
550 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
552 elif keyCode == wx.WXK_PAGEDOWN:
553 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
556 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
557 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
558 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
559 from collections import defaultdict
560 from gc import get_objects
561 self._beforeLeakTest = defaultdict(int)
562 for i in get_objects():
563 self._beforeLeakTest[type(i)] += 1
564 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
565 from collections import defaultdict
566 from gc import get_objects
567 self._afterLeakTest = defaultdict(int)
568 for i in get_objects():
569 self._afterLeakTest[type(i)] += 1
570 for k in self._afterLeakTest:
571 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
572 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
574 def ShaderUpdate(self, v, f):
575 s = opengl.GLShader(v, f)
577 self._objectLoadShader.release()
578 self._objectLoadShader = s
579 for obj in self._scene.objects():
580 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
583 def OnMouseDown(self,e):
584 self._mouseX = e.GetX()
585 self._mouseY = e.GetY()
586 self._mouseClick3DPos = self._mouse3Dpos
587 self._mouseClickFocus = self._focusObj
589 self._mouseState = 'doubleClick'
591 self._mouseState = 'dragOrClick'
592 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
593 p0 -= self.getObjectCenterPos() - self._viewTarget
594 p1 -= self.getObjectCenterPos() - self._viewTarget
595 if self.tool.OnDragStart(p0, p1):
596 self._mouseState = 'tool'
597 if self._mouseState == 'dragOrClick':
598 if e.GetButton() == 1:
599 if self._focusObj is not None:
600 self._selectObject(self._focusObj, False)
603 def OnMouseUp(self, e):
604 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
606 if self._mouseState == 'dragOrClick':
607 if e.GetButton() == 1:
608 self._selectObject(self._focusObj)
609 if e.GetButton() == 3:
611 if self._focusObj is not None:
612 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
613 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
614 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
615 if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1:
616 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
617 if len(self._scene.objects()) > 0:
618 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
619 if menu.MenuItemCount > 0:
622 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
623 self._scene.pushFree()
625 elif self._mouseState == 'tool':
626 if self.tempMatrix is not None and self._selectedObj is not None:
627 self._selectedObj.applyMatrix(self.tempMatrix)
628 self._scene.pushFree()
629 self._selectObject(self._selectedObj)
630 self.tempMatrix = None
631 self.tool.OnDragEnd()
633 self._mouseState = None
635 def OnMouseMotion(self,e):
636 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
637 p0 -= self.getObjectCenterPos() - self._viewTarget
638 p1 -= self.getObjectCenterPos() - self._viewTarget
640 if e.Dragging() and self._mouseState is not None:
641 if self._mouseState == 'tool':
642 self.tool.OnDrag(p0, p1)
643 elif not e.LeftIsDown() and e.RightIsDown():
644 self._mouseState = 'drag'
645 if wx.GetKeyState(wx.WXK_SHIFT):
646 a = math.cos(math.radians(self._yaw)) / 3.0
647 b = math.sin(math.radians(self._yaw)) / 3.0
648 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
649 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
650 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
651 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
653 self._yaw += e.GetX() - self._mouseX
654 self._pitch -= e.GetY() - self._mouseY
655 if self._pitch > 170:
659 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
660 self._mouseState = 'drag'
661 self._zoom += e.GetY() - self._mouseY
664 if self._zoom > numpy.max(self._machineSize) * 3:
665 self._zoom = numpy.max(self._machineSize) * 3
666 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
667 self._mouseState = 'dragObject'
668 z = max(0, self._mouseClick3DPos[2])
669 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
670 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
675 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
676 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
677 diff = cursorZ1 - cursorZ0
678 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
679 if not e.Dragging() or self._mouseState != 'tool':
680 self.tool.OnMouseMove(p0, p1)
682 self._mouseX = e.GetX()
683 self._mouseY = e.GetY()
685 def OnMouseWheel(self, e):
686 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
687 delta = max(min(delta,4),-4)
688 self._zoom *= 1.0 - delta / 10.0
691 if self._zoom > numpy.max(self._machineSize) * 3:
692 self._zoom = numpy.max(self._machineSize) * 3
695 def OnMouseLeave(self, e):
699 def getMouseRay(self, x, y):
700 if self._viewport is None:
701 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
702 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
703 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
704 p0 -= self._viewTarget
705 p1 -= self._viewTarget
708 def _init3DView(self):
709 # set viewing projection
710 size = self.GetSize()
711 glViewport(0, 0, size.GetWidth(), size.GetHeight())
714 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
716 glDisable(GL_RESCALE_NORMAL)
717 glDisable(GL_LIGHTING)
719 glEnable(GL_DEPTH_TEST)
720 glDisable(GL_CULL_FACE)
722 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
724 glClearColor(0.8, 0.8, 0.8, 1.0)
728 glMatrixMode(GL_PROJECTION)
730 aspect = float(size.GetWidth()) / float(size.GetHeight())
731 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
733 glMatrixMode(GL_MODELVIEW)
735 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
738 if machineCom.machineIsConnected():
739 self.printButton._imageID = 6
740 self.printButton._tooltip = 'Print'
741 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
742 self.printButton._imageID = 2
743 self.printButton._tooltip = 'Toolpath to SD'
745 self.printButton._imageID = 3
746 self.printButton._tooltip = 'Save toolpath'
748 if self._animView is not None:
749 self._viewTarget = self._animView.getPosition()
750 if self._animView.isDone():
751 self._animView = None
752 if self._animZoom is not None:
753 self._zoom = self._animZoom.getPosition()
754 if self._animZoom.isDone():
755 self._animZoom = None
756 if self.viewMode == 'gcode' and self._gcode is not None:
758 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
761 if self._objectShader is None:
762 if opengl.hasShaderSupport():
763 self._objectShader = opengl.GLShader("""
764 varying float light_amount;
768 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
769 gl_FrontColor = gl_Color;
771 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
775 varying float light_amount;
779 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
782 self._objectOverhangShader = opengl.GLShader("""
783 uniform float cosAngle;
784 uniform mat3 rotMatrix;
785 varying float light_amount;
789 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
790 gl_FrontColor = gl_Color;
792 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
794 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
796 light_amount = -10.0;
800 varying float light_amount;
804 if (light_amount == -10.0)
806 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
808 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
812 self._objectLoadShader = opengl.GLShader("""
813 uniform float intensity;
815 varying float light_amount;
819 vec4 tmp = gl_Vertex;
820 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
821 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
822 gl_Position = gl_ModelViewProjectionMatrix * tmp;
823 gl_FrontColor = gl_Color;
825 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
829 uniform float intensity;
830 varying float light_amount;
834 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
837 if self._objectShader == None or not self._objectShader.isValid():
838 self._objectShader = opengl.GLFakeShader()
839 self._objectOverhangShader = opengl.GLFakeShader()
840 self._objectLoadShader = None
842 glTranslate(0,0,-self._zoom)
843 glRotate(-self._pitch, 1,0,0)
844 glRotate(self._yaw, 0,0,1)
845 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
847 self._viewport = glGetIntegerv(GL_VIEWPORT)
848 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
849 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
851 glClearColor(1,1,1,1)
852 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
854 if self.viewMode != 'gcode':
855 for n in xrange(0, len(self._scene.objects())):
856 obj = self._scene.objects()[n]
857 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
858 self._renderObject(obj)
860 if self._mouseX > -1:
862 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
863 if n < len(self._scene.objects()):
864 self._focusObj = self._scene.objects()[n]
866 self._focusObj = None
867 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
868 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
869 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
870 self._mouse3Dpos -= self._viewTarget
873 glTranslate(0,0,-self._zoom)
874 glRotate(-self._pitch, 1,0,0)
875 glRotate(self._yaw, 0,0,1)
876 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
878 if self.viewMode == 'gcode':
879 if self._gcode is not None and self._gcode.layerList is None:
880 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
881 self._gcodeLoadThread.daemon = True
882 self._gcodeLoadThread.start()
883 if self._gcode is not None and self._gcode.layerList is not None:
885 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
887 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
888 for n in xrange(0, drawUpTill):
889 c = 1.0 - float(drawUpTill - n) / 15
891 if len(self._gcodeVBOs) < n + 1:
892 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
893 if time.time() - t > 0.5:
896 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
897 if n == drawUpTill - 1:
898 if len(self._gcodeVBOs[n]) < 9:
899 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
901 self._gcodeVBOs[n][8].render(GL_QUADS)
903 self._gcodeVBOs[n][9].render(GL_QUADS)
905 self._gcodeVBOs[n][10].render(GL_QUADS)
907 self._gcodeVBOs[n][11].render(GL_QUADS)
910 self._gcodeVBOs[n][12].render(GL_QUADS)
911 glColor3f(c/2, c/2, 0.0)
912 self._gcodeVBOs[n][13].render(GL_QUADS)
914 self._gcodeVBOs[n][14].render(GL_QUADS)
915 self._gcodeVBOs[n][15].render(GL_QUADS)
917 self._gcodeVBOs[n][16].render(GL_LINES)
920 self._gcodeVBOs[n][0].render(GL_LINES)
922 self._gcodeVBOs[n][1].render(GL_LINES)
924 self._gcodeVBOs[n][2].render(GL_LINES)
926 self._gcodeVBOs[n][3].render(GL_LINES)
929 self._gcodeVBOs[n][4].render(GL_LINES)
930 glColor3f(c/2, c/2, 0.0)
931 self._gcodeVBOs[n][5].render(GL_LINES)
933 self._gcodeVBOs[n][6].render(GL_LINES)
934 self._gcodeVBOs[n][7].render(GL_LINES)
937 glStencilFunc(GL_ALWAYS, 1, 1)
938 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
940 if self.viewMode == 'overhang':
941 self._objectOverhangShader.bind()
942 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
944 self._objectShader.bind()
945 for obj in self._scene.objects():
946 if obj._loadAnim is not None:
947 if obj._loadAnim.isDone():
952 if self._focusObj == obj:
954 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
957 if self._selectedObj == obj or self._selectedObj is None:
958 #If we want transparent, then first render a solid black model to remove the printer size lines.
959 if self.viewMode == 'transparent':
960 glColor4f(0, 0, 0, 0)
961 self._renderObject(obj)
963 glBlendFunc(GL_ONE, GL_ONE)
964 glDisable(GL_DEPTH_TEST)
966 if self.viewMode == 'xray':
967 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
968 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
969 glEnable(GL_STENCIL_TEST)
971 if self.viewMode == 'overhang':
972 if self._selectedObj == obj and self.tempMatrix is not None:
973 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
975 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
977 if not self._scene.checkPlatform(obj):
978 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
979 self._renderObject(obj)
981 self._renderObject(obj, brightness)
982 glDisable(GL_STENCIL_TEST)
984 glEnable(GL_DEPTH_TEST)
985 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
987 if self.viewMode == 'xray':
990 glEnable(GL_STENCIL_TEST)
991 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
992 glDisable(GL_DEPTH_TEST)
993 for i in xrange(2, 15, 2):
994 glStencilFunc(GL_EQUAL, i, 0xFF)
995 glColor(float(i)/10, float(i)/10, float(i)/5)
997 glVertex3f(-1000,-1000,-10)
998 glVertex3f( 1000,-1000,-10)
999 glVertex3f( 1000, 1000,-10)
1000 glVertex3f(-1000, 1000,-10)
1002 for i in xrange(1, 15, 2):
1003 glStencilFunc(GL_EQUAL, i, 0xFF)
1004 glColor(float(i)/10, 0, 0)
1006 glVertex3f(-1000,-1000,-10)
1007 glVertex3f( 1000,-1000,-10)
1008 glVertex3f( 1000, 1000,-10)
1009 glVertex3f(-1000, 1000,-10)
1012 glDisable(GL_STENCIL_TEST)
1013 glEnable(GL_DEPTH_TEST)
1015 self._objectShader.unbind()
1017 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1019 if self._objectLoadShader is not None:
1020 self._objectLoadShader.bind()
1021 glColor4f(0.2, 0.6, 1.0, 1.0)
1022 for obj in self._scene.objects():
1023 if obj._loadAnim is None:
1025 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1026 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1027 self._renderObject(obj)
1028 self._objectLoadShader.unbind()
1033 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1035 z = self._usbPrintMonitor.getZ()
1036 size = self._machineSize
1037 glColor4ub(255,255,0,128)
1039 glVertex3f(-size[0]/2,-size[1]/2, z)
1040 glVertex3f( size[0]/2,-size[1]/2, z)
1041 glVertex3f( size[0]/2, size[1]/2, z)
1042 glVertex3f(-size[0]/2, size[1]/2, z)
1045 if self.viewMode == 'gcode':
1046 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1047 glDisable(GL_DEPTH_TEST)
1050 glTranslate(0,-4,-10)
1051 glColor4ub(60,60,60,255)
1052 opengl.glDrawStringCenter('Loading toolpath for visualization...')
1055 #Draw the object box-shadow, so you can see where it will collide with other objects.
1056 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1057 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1059 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1061 glEnable(GL_CULL_FACE)
1062 glColor4f(0,0,0,0.12)
1064 glVertex3f(-size[0], size[1], 0.1)
1065 glVertex3f(-size[0], -size[1], 0.1)
1066 glVertex3f( size[0], -size[1], 0.1)
1067 glVertex3f( size[0], size[1], 0.1)
1069 glDisable(GL_CULL_FACE)
1072 #Draw the outline of the selected object, on top of everything else except the GUI.
1073 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1074 glDisable(GL_DEPTH_TEST)
1075 glEnable(GL_CULL_FACE)
1076 glEnable(GL_STENCIL_TEST)
1078 glStencilFunc(GL_EQUAL, 0, 255)
1080 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1082 glColor4f(1,1,1,0.5)
1083 self._renderObject(self._selectedObj)
1084 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1086 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1087 glDisable(GL_STENCIL_TEST)
1088 glDisable(GL_CULL_FACE)
1089 glEnable(GL_DEPTH_TEST)
1091 if self._selectedObj is not None:
1093 pos = self.getObjectCenterPos()
1094 glTranslate(pos[0], pos[1], pos[2])
1097 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1098 glDisable(GL_DEPTH_TEST)
1101 glTranslate(0,-4,-10)
1102 glColor4ub(60,60,60,255)
1103 opengl.glDrawStringCenter('Overhang view not working due to lack of OpenGL shaders support.')
1106 def _renderObject(self, obj, brightness = False, addSink = True):
1109 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1111 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1113 if self.tempMatrix is not None and obj == self._selectedObj:
1114 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1115 glMultMatrixf(tempMatrix)
1117 offset = obj.getDrawOffset()
1118 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1120 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1121 glMultMatrixf(tempMatrix)
1124 for m in obj._meshList:
1126 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1128 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1133 def _drawMachine(self):
1134 glEnable(GL_CULL_FACE)
1137 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
1139 if profile.getPreference('machine_type') == 'ultimaker':
1140 glColor4f(1,1,1,0.5)
1141 self._objectShader.bind()
1142 self._renderObject(self._platformMesh, False, False)
1143 self._objectShader.unbind()
1148 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1149 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1150 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1151 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1152 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1153 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1156 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1157 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1158 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1159 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1160 v4 = [ size[0] / 2, size[1] / 2, 0]
1161 v5 = [ size[0] / 2,-size[1] / 2, 0]
1162 v6 = [-size[0] / 2, size[1] / 2, 0]
1163 v7 = [-size[0] / 2,-size[1] / 2, 0]
1165 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1166 glEnableClientState(GL_VERTEX_ARRAY)
1167 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1169 glColor4ub(5, 171, 231, 64)
1170 glDrawArrays(GL_QUADS, 0, 4)
1171 glColor4ub(5, 171, 231, 96)
1172 glDrawArrays(GL_QUADS, 4, 8)
1173 glColor4ub(5, 171, 231, 128)
1174 glDrawArrays(GL_QUADS, 12, 8)
1175 glDisableClientState(GL_VERTEX_ARRAY)
1177 sx = self._machineSize[0]
1178 sy = self._machineSize[1]
1179 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1180 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1185 x1 = max(min(x1, sx/2), -sx/2)
1186 y1 = max(min(y1, sy/2), -sy/2)
1187 x2 = max(min(x2, sx/2), -sx/2)
1188 y2 = max(min(y2, sy/2), -sy/2)
1189 if (x & 1) == (y & 1):
1190 glColor4ub(5, 171, 231, 127)
1192 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1194 glVertex3f(x1, y1, -0.02)
1195 glVertex3f(x2, y1, -0.02)
1196 glVertex3f(x2, y2, -0.02)
1197 glVertex3f(x1, y2, -0.02)
1201 glDisable(GL_CULL_FACE)
1203 def _generateGCodeVBOs(self, layer):
1205 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1206 if ':' in extrudeType:
1207 extruder = int(extrudeType[extrudeType.find(':')+1:])
1208 extrudeType = extrudeType[0:extrudeType.find(':')]
1211 pointList = numpy.zeros((0,3), numpy.float32)
1213 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1215 a = numpy.concatenate((a[:-1], a[1:]), 1)
1216 a = a.reshape((len(a) * 2, 3))
1217 pointList = numpy.concatenate((pointList, a))
1218 ret.append(opengl.GLVBO(pointList))
1221 def _generateGCodeVBOs2(self, layer):
1222 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1223 filamentArea = math.pi * filamentRadius * filamentRadius
1226 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1227 if ':' in extrudeType:
1228 extruder = int(extrudeType[extrudeType.find(':')+1:])
1229 extrudeType = extrudeType[0:extrudeType.find(':')]
1232 pointList = numpy.zeros((0,3), numpy.float32)
1234 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1236 if extrudeType == 'FILL':
1239 normal = a[1:] - a[:-1]
1240 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1241 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1244 ePerDist = path['extrusion'][1:] / lens
1245 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1247 normal[:,0] *= lineWidth
1248 normal[:,1] *= lineWidth
1250 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1251 b = numpy.concatenate((b, a[1:] + normal), 1)
1252 b = numpy.concatenate((b, a[1:] - normal), 1)
1253 b = numpy.concatenate((b, a[:-1] - normal), 1)
1254 b = numpy.concatenate((b, a[:-1] + normal), 1)
1255 b = b.reshape((len(b) * 4, 3))
1258 normal2 = normal[:-1] + normal[1:]
1259 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1260 normal2[:,0] /= lens2
1261 normal2[:,1] /= lens2
1262 normal2[:,0] *= lineWidth[:-1]
1263 normal2[:,1] *= lineWidth[:-1]
1265 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1266 c = numpy.concatenate((c, a[1:-1]), 1)
1267 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1268 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1269 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1271 c = numpy.concatenate((c, a[1:-1]), 1)
1272 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1273 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1274 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1276 c = c.reshape((len(c) * 8, 3))
1278 pointList = numpy.concatenate((pointList, b, c))
1280 pointList = numpy.concatenate((pointList, b))
1281 ret.append(opengl.GLVBO(pointList))
1283 pointList = numpy.zeros((0,3), numpy.float32)
1285 if path['type'] == 'move':
1286 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1287 a = numpy.concatenate((a[:-1], a[1:]), 1)
1288 a = a.reshape((len(a) * 2, 3))
1289 pointList = numpy.concatenate((pointList, a))
1290 if path['type'] == 'retract':
1291 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1292 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1293 a = a.reshape((len(a) * 2, 3))
1294 pointList = numpy.concatenate((pointList, a))
1295 ret.append(opengl.GLVBO(pointList))
1299 def getObjectCenterPos(self):
1300 if self._selectedObj is None:
1301 return [0.0, 0.0, 0.0]
1302 pos = self._selectedObj.getPosition()
1303 size = self._selectedObj.getSize()
1304 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1306 def getObjectBoundaryCircle(self):
1307 if self._selectedObj is None:
1309 return self._selectedObj.getBoundaryCircle()
1311 def getObjectSize(self):
1312 if self._selectedObj is None:
1313 return [0.0, 0.0, 0.0]
1314 return self._selectedObj.getSize()
1316 def getObjectMatrix(self):
1317 if self._selectedObj is None:
1318 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1319 return self._selectedObj.getMatrix()
1321 class shaderEditor(wx.Dialog):
1322 def __init__(self, parent, callback, v, f):
1323 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1324 self._callback = callback
1325 s = wx.BoxSizer(wx.VERTICAL)
1327 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1328 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1329 s.Add(self._vertex, 1, flag=wx.EXPAND)
1330 s.Add(self._fragment, 1, flag=wx.EXPAND)
1332 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1333 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1335 self.SetPosition(self.GetParent().GetPosition())
1336 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1339 def OnText(self, e):
1340 self._callback(self._vertex.GetValue(), self._fragment.GetValue())