1 from __future__ import absolute_import
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._objectShader = None
42 self._selectedObj = None
43 self._objColors = [None,None,None,None]
46 self._mouseState = None
47 self._viewTarget = numpy.array([0,0,0], numpy.float32)
50 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
51 self._platformMesh._drawOffset = numpy.array([0,0,1.5], numpy.float32)
52 self._isSimpleMode = True
55 self._modelMatrix = None
56 self._projMatrix = None
57 self.tempMatrix = None
59 self.openFileButton = openglGui.glButton(self, 4, 'Load', (0,0), self.ShowLoadModel)
60 self.printButton = openglGui.glButton(self, 6, 'Print', (1,0), self.ShowPrintWindow)
61 self.printButton.setDisabled(True)
64 self.rotateToolButton = openglGui.glRadioButton(self, 8, 'Rotate', (0,-1), group, self.OnToolSelect)
65 self.scaleToolButton = openglGui.glRadioButton(self, 9, 'Scale', (1,-1), group, self.OnToolSelect)
66 self.mirrorToolButton = openglGui.glRadioButton(self, 10, 'Mirror', (2,-1), group, self.OnToolSelect)
68 self.resetRotationButton = openglGui.glButton(self, 12, 'Reset', (0,-2), self.OnRotateReset)
69 self.layFlatButton = openglGui.glButton(self, 16, 'Lay flat', (0,-3), self.OnLayFlat)
71 self.resetScaleButton = openglGui.glButton(self, 13, 'Reset', (1,-2), self.OnScaleReset)
72 self.scaleMaxButton = openglGui.glButton(self, 17, 'To max', (1,-3), self.OnScaleMax)
74 self.mirrorXButton = openglGui.glButton(self, 14, 'Mirror X', (2,-2), lambda button: self.OnMirror(0))
75 self.mirrorYButton = openglGui.glButton(self, 18, 'Mirror Y', (2,-3), lambda button: self.OnMirror(1))
76 self.mirrorZButton = openglGui.glButton(self, 22, 'Mirror Z', (2,-4), lambda button: self.OnMirror(2))
78 self.rotateToolButton.setExpandArrow(True)
79 self.scaleToolButton.setExpandArrow(True)
80 self.mirrorToolButton.setExpandArrow(True)
82 self.scaleForm = openglGui.glFrame(self, (2, -2))
83 openglGui.glGuiLayoutGrid(self.scaleForm)
84 openglGui.glLabel(self.scaleForm, 'Scale X', (0,0))
85 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
86 openglGui.glLabel(self.scaleForm, 'Scale Y', (0,1))
87 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
88 openglGui.glLabel(self.scaleForm, 'Scale Z', (0,2))
89 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
90 openglGui.glLabel(self.scaleForm, 'Size X (mm)', (0,4))
91 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
92 openglGui.glLabel(self.scaleForm, 'Size Y (mm)', (0,5))
93 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
94 openglGui.glLabel(self.scaleForm, 'Size Z (mm)', (0,6))
95 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
96 openglGui.glLabel(self.scaleForm, 'Uniform scale', (0,8))
97 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
99 self.viewSelection = openglGui.glComboButton(self, 'View mode', [7,11,15,23], ['Normal', 'Transparent', 'X-Ray', 'Layers'], (-1,0), self.OnViewChange)
100 self.layerSelect = openglGui.glSlider(self, 100, 0, 100, (-1,-2), lambda : self.QueueRefresh())
102 self.notification = openglGui.glNotification(self, (0, 0))
104 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
105 self._sceneUpdateTimer = wx.Timer(self)
106 self.Bind(wx.EVT_TIMER, lambda e : self._slicer.runSlicer(self._scene), self._sceneUpdateTimer)
107 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
111 self.updateToolButtons()
112 self.updateProfileToControls()
114 def ShowLoadModel(self, button):
116 dlg=wx.FileDialog(self, 'Open 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
117 dlg.SetWildcard(meshLoader.wildcardFilter())
118 if dlg.ShowModal() != wx.ID_OK:
121 filename = dlg.GetPath()
123 if not(os.path.exists(filename)):
125 profile.putPreference('lastFile', filename)
126 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
127 self.loadScene([filename])
129 def ShowPrintWindow(self, button):
131 if machineCom.machineIsConnected():
132 printWindow.printFile(self._slicer.getGCodeFilename())
133 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
134 drives = removableStorage.getPossibleSDcardDrives()
136 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))
137 if dlg.ShowModal() != wx.ID_OK:
140 drive = drives[dlg.GetSelection()]
144 filename = os.path.basename(profile.getPreference('lastFile'))
145 filename = filename[0:filename.rfind('.')] + '.gcode'
147 shutil.copy(self._slicer.getGCodeFilename(), drive[1] + filename)
149 self.notification.message("Failed to save to SD card")
151 self.notification.message("Saved as %s" % (drive[1] + filename))
153 self._showSaveGCode()
156 self.Bind(wx.EVT_MENU, lambda e: printWindow.printFile(self._slicer.getGCodeFilename()), menu.Append(-1, 'Print with USB'))
157 self.Bind(wx.EVT_MENU, lambda e: self._showSaveGCode(), menu.Append(-1, 'Save GCode...'))
161 def _showSaveGCode(self):
162 defPath = profile.getPreference('lastFile')
163 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
164 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
165 dlg.SetFilename(defPath)
166 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
167 if dlg.ShowModal() != wx.ID_OK:
170 filename = dlg.GetPath()
174 shutil.copy(self._slicer.getGCodeFilename(), filename)
176 self.notification.message("Failed to save")
178 self.notification.message("Saved as %s" % (filename))
180 def OnToolSelect(self, button):
181 if self.rotateToolButton.getSelected():
182 self.tool = previewTools.toolRotate(self)
183 elif self.scaleToolButton.getSelected():
184 self.tool = previewTools.toolScale(self)
185 elif self.mirrorToolButton.getSelected():
186 self.tool = previewTools.toolNone(self)
188 self.tool = previewTools.toolNone(self)
189 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
190 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
191 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
192 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
193 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
194 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
195 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
196 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
198 def updateToolButtons(self):
199 if self._selectedObj is None:
203 self.rotateToolButton.setHidden(hidden)
204 self.scaleToolButton.setHidden(hidden)
205 self.mirrorToolButton.setHidden(hidden)
207 self.rotateToolButton.setSelected(False)
208 self.scaleToolButton.setSelected(False)
209 self.mirrorToolButton.setSelected(False)
212 def OnViewChange(self):
213 if self.viewSelection.getValue() == 3:
214 self.viewMode = 'gcode'
215 if self._gcode is not None:
216 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
217 self.layerSelect.setValue(len(self._gcode.layerList) - 1)
218 self._selectObject(None)
219 elif self.viewSelection.getValue() == 1:
220 self.viewMode = 'transparent'
221 elif self.viewSelection.getValue() == 2:
222 self.viewMode = 'xray'
224 self.viewMode = 'normal'
225 self.layerSelect.setHidden(self.viewMode != 'gcode')
226 self.openFileButton.setHidden(self.viewMode == 'gcode')
229 def OnRotateReset(self, button):
230 if self._selectedObj is None:
232 self._selectedObj.resetRotation()
233 self._scene.pushFree()
234 self._selectObject(self._selectedObj)
236 def OnLayFlat(self, button):
237 if self._selectedObj is None:
239 self._selectedObj.layFlat()
240 self._scene.pushFree()
241 self._selectObject(self._selectedObj)
243 def OnScaleReset(self, button):
244 if self._selectedObj is None:
246 self._selectedObj.resetScale()
248 def OnScaleMax(self, button):
249 if self._selectedObj is None:
251 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2)
252 self._scene.pushFree()
253 self._selectObject(self._selectedObj)
254 self.updateProfileToControls()
257 def OnMirror(self, axis):
258 if self._selectedObj is None:
260 self._selectedObj.mirror(axis)
263 def OnScaleEntry(self, value, axis):
264 if self._selectedObj is None:
270 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
271 self.updateProfileToControls()
272 self._scene.pushFree()
273 self._selectObject(self._selectedObj)
276 def OnScaleEntryMM(self, value, axis):
277 if self._selectedObj is None:
283 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
284 self.updateProfileToControls()
285 self._scene.pushFree()
286 self._selectObject(self._selectedObj)
289 def OnDeleteAll(self, e):
290 while len(self._scene.objects()) > 0:
291 self._deleteObject(self._scene.objects()[0])
292 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
294 def OnMultiply(self, e):
295 if self._focusObj is None:
298 dlg = wx.NumberEntryDialog(self, "How many copies need to be made?", "Copies", "Multiply", 1, 1, 100)
299 if dlg.ShowModal() != wx.ID_OK:
308 self._scene.add(newObj)
309 self._scene.centerAll()
310 if not self._scene.checkPlatform(newObj):
314 self._scene.remove(newObj)
315 self._scene.centerAll()
318 def OnSplitObject(self, e):
319 if self._focusObj is None:
321 self._scene.remove(self._focusObj)
322 for obj in self._focusObj.split():
324 self._scene.centerAll()
325 self._selectObject(None)
328 def OnMergeObjects(self, e):
329 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
331 self._scene.merge(self._selectedObj, self._focusObj)
334 def sceneUpdated(self):
335 self._sceneUpdateTimer.Start(1, True)
336 self._slicer.abortSlicer()
337 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
340 def _updateSliceProgress(self, progressValue, ready):
341 self.printButton.setDisabled(not ready)
342 self.printButton.setProgressBar(progressValue)
343 if self._gcode is not None:
345 for layerVBOlist in self._gcodeVBOs:
346 for vbo in layerVBOlist:
347 self.glReleaseList.append(vbo)
350 self._gcode = gcodeInterpreter.gcode()
351 self._gcode.progressCallback = self._gcodeLoadCallback
352 self._thread = threading.Thread(target=self._loadGCode)
353 self._thread.daemon = True
357 def _loadGCode(self):
358 self._gcode.load(self._slicer.getGCodeFilename())
360 def _gcodeLoadCallback(self, progress):
361 if self._gcode is None:
363 if self.layerSelect.getValue() == self.layerSelect.getMaxValue():
364 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
365 self.layerSelect.setValue(self.layerSelect.getMaxValue())
367 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
370 def loadScene(self, fileList):
371 for filename in fileList:
373 objList = meshLoader.loadMeshes(filename)
375 traceback.print_exc()
378 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
380 self._scene.centerAll()
381 self._selectObject(obj)
384 def _deleteObject(self, obj):
385 if obj == self._selectedObj:
386 self._selectObject(None)
387 if obj == self._focusObj:
388 self._focusObj = None
389 self._scene.remove(obj)
390 for m in obj._meshList:
391 if m.vbo is not None and m.vbo.decRef():
392 self.glReleaseList.append(m.vbo)
393 if self._isSimpleMode:
394 self._scene.arrangeAll()
397 def _selectObject(self, obj, zoom = True):
398 if obj != self._selectedObj:
399 self._selectedObj = obj
400 self.updateProfileToControls()
401 self.updateToolButtons()
402 if zoom and obj is not None:
403 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
404 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
405 newZoom = obj.getBoundaryCircle() * 6
406 if newZoom > numpy.max(self._machineSize) * 3:
407 newZoom = numpy.max(self._machineSize) * 3
408 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
410 def updateProfileToControls(self):
411 oldSimpleMode = self._isSimpleMode
412 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
413 if self._isSimpleMode and not oldSimpleMode:
414 self._scene.arrangeAll()
416 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
417 self._objColors[0] = profile.getPreferenceColour('model_colour')
418 self._objColors[1] = profile.getPreferenceColour('model_colour2')
419 self._objColors[2] = profile.getPreferenceColour('model_colour3')
420 self._objColors[3] = profile.getPreferenceColour('model_colour4')
421 self._scene.setMachineSize(self._machineSize)
422 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
423 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'))
425 if self._selectedObj is not None:
426 scale = self._selectedObj.getScale()
427 size = self._selectedObj.getSize()
428 self.scaleXctrl.setValue(round(scale[0], 2))
429 self.scaleYctrl.setValue(round(scale[1], 2))
430 self.scaleZctrl.setValue(round(scale[2], 2))
431 self.scaleXmmctrl.setValue(round(size[0], 2))
432 self.scaleYmmctrl.setValue(round(size[1], 2))
433 self.scaleZmmctrl.setValue(round(size[2], 2))
435 def OnKeyChar(self, keyCode):
436 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
437 if self._selectedObj is not None:
438 self._deleteObject(self._selectedObj)
440 if keyCode == wx.WXK_UP:
441 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
443 elif keyCode == wx.WXK_DOWN:
444 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
446 elif keyCode == wx.WXK_PAGEUP:
447 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
449 elif keyCode == wx.WXK_PAGEDOWN:
450 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
453 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
454 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
456 def ShaderUpdate(self, v, f):
457 s = opengl.GLShader(v, f)
459 self._objectLoadShader.release()
460 self._objectLoadShader = s
461 for obj in self._scene.objects():
462 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
465 def OnMouseDown(self,e):
466 self._mouseX = e.GetX()
467 self._mouseY = e.GetY()
468 self._mouseClick3DPos = self._mouse3Dpos
469 self._mouseClickFocus = self._focusObj
471 self._mouseState = 'doubleClick'
473 self._mouseState = 'dragOrClick'
474 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
475 p0 -= self.getObjectCenterPos() - self._viewTarget
476 p1 -= self.getObjectCenterPos() - self._viewTarget
477 if self.tool.OnDragStart(p0, p1):
478 self._mouseState = 'tool'
479 if self._mouseState == 'dragOrClick':
480 if e.GetButton() == 1:
481 if self._focusObj is not None:
482 self._selectObject(self._focusObj, False)
485 def OnMouseUp(self, e):
486 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
488 if self._mouseState == 'dragOrClick':
489 if e.GetButton() == 1:
490 self._selectObject(self._focusObj)
491 if e.GetButton() == 3:
493 if self._focusObj is not None:
494 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
495 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
496 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
497 if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1:
498 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
499 if len(self._scene.objects()) > 0:
500 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
501 if menu.MenuItemCount > 0:
504 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
505 self._scene.pushFree()
507 elif self._mouseState == 'tool':
508 if self.tempMatrix is not None and self._selectedObj is not None:
509 self._selectedObj.applyMatrix(self.tempMatrix)
510 self._scene.pushFree()
511 self._selectObject(self._selectedObj)
512 self.tempMatrix = None
513 self.tool.OnDragEnd()
515 self._mouseState = None
517 def OnMouseMotion(self,e):
518 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
519 p0 -= self.getObjectCenterPos() - self._viewTarget
520 p1 -= self.getObjectCenterPos() - self._viewTarget
522 if e.Dragging() and self._mouseState is not None:
523 if self._mouseState == 'tool':
524 self.tool.OnDrag(p0, p1)
525 elif not e.LeftIsDown() and e.RightIsDown():
526 self._mouseState = 'drag'
527 self._yaw += e.GetX() - self._mouseX
528 self._pitch -= e.GetY() - self._mouseY
529 if self._pitch > 170:
533 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
534 self._mouseState = 'drag'
535 self._zoom += e.GetY() - self._mouseY
538 if self._zoom > numpy.max(self._machineSize) * 3:
539 self._zoom = numpy.max(self._machineSize) * 3
540 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus and not self._isSimpleMode:
541 self._mouseState = 'dragObject'
542 z = max(0, self._mouseClick3DPos[2])
543 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
544 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
549 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
550 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
551 diff = cursorZ1 - cursorZ0
552 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
553 if not e.Dragging() or self._mouseState != 'tool':
554 self.tool.OnMouseMove(p0, p1)
556 self._mouseX = e.GetX()
557 self._mouseY = e.GetY()
559 def OnMouseWheel(self, e):
560 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
561 delta = max(min(delta,4),-4)
562 self._zoom *= 1.0 - delta / 10.0
565 if self._zoom > numpy.max(self._machineSize) * 3:
566 self._zoom = numpy.max(self._machineSize) * 3
569 def getMouseRay(self, x, y):
570 if self._viewport is None:
571 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
572 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
573 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
574 p0 -= self._viewTarget
575 p1 -= self._viewTarget
578 def _init3DView(self):
579 # set viewing projection
580 size = self.GetSize()
581 glViewport(0, 0, size.GetWidth(), size.GetHeight())
584 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
586 glDisable(GL_RESCALE_NORMAL)
587 glDisable(GL_LIGHTING)
589 glEnable(GL_DEPTH_TEST)
590 glDisable(GL_CULL_FACE)
592 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
594 glClearColor(0.8, 0.8, 0.8, 1.0)
598 glMatrixMode(GL_PROJECTION)
600 aspect = float(size.GetWidth()) / float(size.GetHeight())
601 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
603 glMatrixMode(GL_MODELVIEW)
605 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
608 if machineCom.machineIsConnected():
609 self.printButton._imageID = 6
610 self.printButton._tooltip = 'Print'
611 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
612 self.printButton._imageID = 2
613 self.printButton._tooltip = 'Toolpath to SD'
615 self.printButton._imageID = 3
616 self.printButton._tooltip = 'Save toolpath'
618 if self._animView is not None:
619 self._viewTarget = self._animView.getPosition()
620 if self._animView.isDone():
621 self._animView = None
622 if self._animZoom is not None:
623 self._zoom = self._animZoom.getPosition()
624 if self._animZoom.isDone():
625 self._animZoom = None
626 if self._objectShader is None:
627 self._objectShader = opengl.GLShader("""
628 varying float light_amount;
632 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
633 gl_FrontColor = gl_Color;
635 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
639 varying float light_amount;
643 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
646 self._objectLoadShader = opengl.GLShader("""
647 uniform float intensity;
649 varying float light_amount;
653 vec4 tmp = gl_Vertex;
654 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
655 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
656 gl_Position = gl_ModelViewProjectionMatrix * tmp;
657 gl_FrontColor = gl_Color;
659 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
663 uniform float intensity;
664 varying float light_amount;
668 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
672 glTranslate(0,0,-self._zoom)
673 glRotate(-self._pitch, 1,0,0)
674 glRotate(self._yaw, 0,0,1)
675 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
677 self._viewport = glGetIntegerv(GL_VIEWPORT)
678 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
679 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
681 glClearColor(1,1,1,1)
682 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
684 if self.viewMode != 'gcode':
685 for n in xrange(0, len(self._scene.objects())):
686 obj = self._scene.objects()[n]
687 glColor4ub((n >> 24) & 0xFF, (n >> 16) & 0xFF, (n >> 8) & 0xFF, n & 0xFF)
688 self._renderObject(obj)
690 if self._mouseX > -1:
691 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0]
692 if n < len(self._scene.objects()):
693 self._focusObj = self._scene.objects()[n]
695 self._focusObj = None
696 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
697 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
698 self._mouse3Dpos -= self._viewTarget
701 glTranslate(0,0,-self._zoom)
702 glRotate(-self._pitch, 1,0,0)
703 glRotate(self._yaw, 0,0,1)
704 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
706 if self.viewMode == 'gcode':
707 if self._gcode is not None:
709 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
711 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
712 for n in xrange(0, drawUpTill):
713 c = 1.0 - float(drawUpTill - n) / 15
715 if len(self._gcodeVBOs) < n + 1:
716 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
717 if time.time() - t > 0.5:
720 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
721 if n == drawUpTill - 1:
722 if len(self._gcodeVBOs[n]) < 6:
723 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
725 self._gcodeVBOs[n][5].render(GL_QUADS)
727 self._gcodeVBOs[n][6].render(GL_QUADS)
728 glColor3f(c/2, c/2, 0.0)
729 self._gcodeVBOs[n][7].render(GL_QUADS)
731 self._gcodeVBOs[n][8].render(GL_QUADS)
732 self._gcodeVBOs[n][9].render(GL_QUADS)
735 self._gcodeVBOs[n][0].render(GL_LINES)
737 self._gcodeVBOs[n][1].render(GL_LINES)
738 glColor3f(c/2, c/2, 0.0)
739 self._gcodeVBOs[n][2].render(GL_LINES)
741 self._gcodeVBOs[n][3].render(GL_LINES)
742 self._gcodeVBOs[n][4].render(GL_LINES)
745 glStencilFunc(GL_ALWAYS, 1, 1)
746 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
748 self._objectShader.bind()
749 for obj in self._scene.objects():
750 if obj._loadAnim is not None:
751 if obj._loadAnim.isDone():
756 if self._selectedObj == obj:
757 #If we want transparent, then first render a solid black model to remove the printer size lines.
758 if self.viewMode == 'transparent':
759 glColor4f(0, 0, 0, 0)
760 self._renderObject(obj)
762 glBlendFunc(GL_ONE, GL_ONE)
763 glDisable(GL_DEPTH_TEST)
765 if self.viewMode == 'xray':
766 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
767 glEnable(GL_STENCIL_TEST)
768 if self._focusObj == obj:
770 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
772 if not self._scene.checkPlatform(obj):
773 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
774 self._renderObject(obj)
776 self._renderObject(obj, brightness)
777 glDisable(GL_STENCIL_TEST)
779 glEnable(GL_DEPTH_TEST)
780 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
782 if obj == self._selectedObj and self.viewMode == 'xray':
785 glEnable(GL_STENCIL_TEST)
786 glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP)
787 glDisable(GL_DEPTH_TEST)
788 for i in xrange(2, 15, 2):
789 glStencilFunc(GL_EQUAL, i, 0xFF);
790 glColor(float(i)/10, float(i)/10, float(i)/5)
792 glVertex3f(-1000,-1000,-1)
793 glVertex3f( 1000,-1000,-1)
794 glVertex3f( 1000, 1000,-1)
795 glVertex3f(-1000, 1000,-1)
797 for i in xrange(1, 15, 2):
798 glStencilFunc(GL_EQUAL, i, 0xFF);
799 glColor(float(i)/10, 0, 0)
801 glVertex3f(-1000,-1000,-1)
802 glVertex3f( 1000,-1000,-1)
803 glVertex3f( 1000, 1000,-1)
804 glVertex3f(-1000, 1000,-1)
807 glDisable(GL_STENCIL_TEST)
808 glEnable(GL_DEPTH_TEST)
810 self._objectShader.unbind()
812 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
814 self._objectLoadShader.bind()
815 glColor4f(0.2, 0.6, 1.0, 1.0)
816 for obj in self._scene.objects():
817 if obj._loadAnim is None:
819 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
820 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
821 self._renderObject(obj)
822 self._objectLoadShader.unbind()
827 if self.viewMode == 'gcode':
830 #Draw the object box-shadow, so you can see where it will collide with other objects.
831 if self._selectedObj is not None and len(self._scene.objects()) > 1:
832 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
834 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0.0)
836 glEnable(GL_CULL_FACE)
837 glColor4f(0,0,0,0.12)
839 glVertex3f(-size[0], size[1], 0.1)
840 glVertex3f(-size[0], -size[1], 0.1)
841 glVertex3f( size[0], -size[1], 0.1)
842 glVertex3f( size[0], size[1], 0.1)
844 glDisable(GL_CULL_FACE)
847 #Draw the outline of the selected object, on top of everything else except the GUI.
848 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
849 glDisable(GL_DEPTH_TEST)
850 glEnable(GL_CULL_FACE)
851 glEnable(GL_STENCIL_TEST)
853 glStencilFunc(GL_EQUAL, 0, 255)
855 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
858 self._renderObject(self._selectedObj)
859 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
861 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
862 glDisable(GL_STENCIL_TEST)
863 glDisable(GL_CULL_FACE)
864 glEnable(GL_DEPTH_TEST)
866 if self._selectedObj is not None:
868 pos = self.getObjectCenterPos()
869 glTranslate(pos[0], pos[1], pos[2])
873 def _renderObject(self, obj, brightness = False):
875 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
877 if self.tempMatrix is not None and obj == self._selectedObj:
878 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
879 glMultMatrixf(tempMatrix)
881 offset = obj.getDrawOffset()
882 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
884 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
885 glMultMatrixf(tempMatrix)
888 for m in obj._meshList:
890 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
892 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
897 def _drawMachine(self):
898 glEnable(GL_CULL_FACE)
901 if profile.getPreference('machine_type') == 'ultimaker':
903 self._objectShader.bind()
904 self._renderObject(self._platformMesh)
905 self._objectShader.unbind()
907 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
908 v0 = [ size[0] / 2, size[1] / 2, size[2]]
909 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
910 v2 = [-size[0] / 2, size[1] / 2, size[2]]
911 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
912 v4 = [ size[0] / 2, size[1] / 2, 0]
913 v5 = [ size[0] / 2,-size[1] / 2, 0]
914 v6 = [-size[0] / 2, size[1] / 2, 0]
915 v7 = [-size[0] / 2,-size[1] / 2, 0]
917 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
918 glEnableClientState(GL_VERTEX_ARRAY)
919 glVertexPointer(3, GL_FLOAT, 3*4, vList)
921 glColor4ub(5, 171, 231, 64)
922 glDrawArrays(GL_QUADS, 0, 4)
923 glColor4ub(5, 171, 231, 96)
924 glDrawArrays(GL_QUADS, 4, 8)
925 glColor4ub(5, 171, 231, 128)
926 glDrawArrays(GL_QUADS, 12, 8)
928 sx = self._machineSize[0]
929 sy = self._machineSize[1]
930 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
931 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
936 x1 = max(min(x1, sx/2), -sx/2)
937 y1 = max(min(y1, sy/2), -sy/2)
938 x2 = max(min(x2, sx/2), -sx/2)
939 y2 = max(min(y2, sy/2), -sy/2)
940 if (x & 1) == (y & 1):
941 glColor4ub(5, 171, 231, 127)
943 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
945 glVertex3f(x1, y1, -0.02)
946 glVertex3f(x2, y1, -0.02)
947 glVertex3f(x2, y2, -0.02)
948 glVertex3f(x1, y2, -0.02)
951 glDisableClientState(GL_VERTEX_ARRAY)
953 glDisable(GL_CULL_FACE)
955 def _generateGCodeVBOs(self, layer):
957 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
958 pointList = numpy.zeros((0,3), numpy.float32)
960 if path.type == 'extrude' and path.pathType == extrudeType:
961 a = numpy.array(path.points, numpy.float32)
962 a = numpy.concatenate((a[:-1], a[1:]), 1)
963 a = a.reshape((len(a) * 2, 3))
964 pointList = numpy.concatenate((pointList, a))
965 ret.append(opengl.GLVBO(pointList))
968 def _generateGCodeVBOs2(self, layer):
969 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
970 filamentArea = math.pi * filamentRadius * filamentRadius
973 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
974 pointList = numpy.zeros((0,3), numpy.float32)
976 if path.type == 'extrude' and path.pathType == extrudeType:
977 a = numpy.array(path.points, numpy.float32)
978 if extrudeType == 'FILL':
981 normal = a[1:] - a[:-1]
982 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
983 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
986 ePerDist = path.extrusion[1:] / lens
987 lineWidth = ePerDist * (filamentArea / path.layerThickness / 2)
989 normal[:,0] *= lineWidth
990 normal[:,1] *= lineWidth
992 b = numpy.zeros((len(a)-1, 0), numpy.float32)
993 b = numpy.concatenate((b, a[:-1] + normal), 1)
994 b = numpy.concatenate((b, a[1:] + normal), 1)
995 b = numpy.concatenate((b, a[1:] - normal), 1)
996 b = numpy.concatenate((b, a[:-1] - normal), 1)
997 b = b.reshape((len(b) * 4, 3))
999 pointList = numpy.concatenate((pointList, b))
1000 ret.append(opengl.GLVBO(pointList))
1003 def getObjectCenterPos(self):
1004 if self._selectedObj is None:
1005 return [0.0, 0.0, 0.0]
1006 pos = self._selectedObj.getPosition()
1007 size = self._selectedObj.getSize()
1008 return [pos[0], pos[1], size[2]/2]
1010 def getObjectBoundaryCircle(self):
1011 if self._selectedObj is None:
1013 return self._selectedObj.getBoundaryCircle()
1015 def getObjectSize(self):
1016 if self._selectedObj is None:
1017 return [0.0, 0.0, 0.0]
1018 return self._selectedObj.getSize()
1020 def getObjectMatrix(self):
1021 if self._selectedObj is None:
1022 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1023 return self._selectedObj.getMatrix()
1025 class shaderEditor(wx.Dialog):
1026 def __init__(self, parent, callback, v, f):
1027 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1028 self._callback = callback
1029 s = wx.BoxSizer(wx.VERTICAL)
1031 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1032 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1033 s.Add(self._vertex, 1, flag=wx.EXPAND)
1034 s.Add(self._fragment, 1, flag=wx.EXPAND)
1036 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1037 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1039 self.SetPosition(self.GetParent().GetPosition())
1040 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1043 def OnText(self, e):
1044 self._callback(self._vertex.GetValue(), self._fragment.GetValue())