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 = {}
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, _("Share on YouMagine"), (2,0), lambda button: youmagineGui.youmagineManager(self.GetTopLevelParent(), self._scene))
107 self.youMagineButton.setDisabled(True)
109 self.notification = openglGui.glNotification(self, (0, 0))
111 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
112 self._sceneUpdateTimer = wx.Timer(self)
113 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
114 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
115 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
119 self.updateToolButtons()
120 self.updateProfileToControls()
122 def loadGCodeFile(self, filename):
123 self.OnDeleteAll(None)
124 if self._gcode is not None:
126 for layerVBOlist in self._gcodeVBOs:
127 for vbo in layerVBOlist:
128 self.glReleaseList.append(vbo)
130 self._gcode = gcodeInterpreter.gcode()
131 self._gcodeFilename = filename
132 self.printButton.setBottomText('')
133 self.viewSelection.setValue(4)
134 self.printButton.setDisabled(False)
135 self.youMagineButton.setDisabled(True)
138 def loadSceneFiles(self, filenames):
139 self.youMagineButton.setDisabled(False)
140 if self.viewSelection.getValue() == 4:
141 self.viewSelection.setValue(0)
143 self.loadScene(filenames)
145 def loadFiles(self, filenames):
147 profileFilename = None
148 for filename in filenames:
149 ext = filename[filename.rfind('.')+1:].lower()
150 if ext == 'g' or ext == 'gcode':
151 gcodeFilename = filename
153 profileFilename = filename
154 if gcodeFilename is not None:
155 self.loadGCodeFile(gcodeFilename)
156 elif profileFilename is not None:
157 profile.loadProfile(profileFilename)
158 self.GetParent().GetParent().GetParent().updateProfileToAllControls()
160 for filename in filenames:
161 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
162 self.loadSceneFiles(filenames)
164 def showLoadModel(self, button = 1):
166 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)
167 dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
168 if dlg.ShowModal() != wx.ID_OK:
171 filenames = dlg.GetPaths()
173 if len(filenames) < 1:
175 profile.putPreference('lastFile', filenames[0])
176 self.loadFiles(filenames)
178 def showSaveModel(self):
179 if len(self._scene.objects()) < 1:
181 dlg=wx.FileDialog(self, _("Save 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
182 dlg.SetWildcard(meshLoader.saveWildcardFilter())
183 if dlg.ShowModal() != wx.ID_OK:
186 filename = dlg.GetPath()
188 meshLoader.saveMeshes(filename, self._scene.objects())
190 def OnPrintButton(self, button):
192 if machineCom.machineIsConnected():
193 self.showPrintWindow()
194 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
195 drives = removableStorage.getPossibleSDcardDrives()
197 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))
198 if dlg.ShowModal() != wx.ID_OK:
201 drive = drives[dlg.GetSelection()]
205 filename = self._scene._objectList[0].getName() + '.gcode'
206 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
211 self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB")))
212 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode...")))
213 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, _("Slice engine log...")))
217 def showPrintWindow(self):
218 if self._gcodeFilename is None:
220 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
221 if self._gcodeFilename == self._slicer.getGCodeFilename():
222 self._slicer.submitSliceInfoOnline()
224 def showSaveGCode(self):
225 dlg=wx.FileDialog(self, _("Save toolpath"), os.path.dirname(profile.getPreference('lastFile')), style=wx.FD_SAVE)
226 dlg.SetFilename(self._scene._objectList[0].getName()+'.gcode')
227 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
228 if dlg.ShowModal() != wx.ID_OK:
231 filename = dlg.GetPath()
234 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
236 def _copyFile(self, fileA, fileB, allowEject = False):
238 size = float(os.stat(fileA).st_size)
239 with open(fileA, 'rb') as fsrc:
240 with open(fileB, 'wb') as fdst:
242 buf = fsrc.read(16*1024)
246 self.printButton.setProgressBar(float(fsrc.tell()) / size)
251 self.notification.message("Failed to save")
254 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...'))
256 self.notification.message("Saved as %s" % (fileB))
257 self.printButton.setProgressBar(None)
258 if fileA == self._slicer.getGCodeFilename():
259 self._slicer.submitSliceInfoOnline()
261 def _showSliceLog(self):
262 dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
266 def OnToolSelect(self, button):
267 if self.rotateToolButton.getSelected():
268 self.tool = previewTools.toolRotate(self)
269 elif self.scaleToolButton.getSelected():
270 self.tool = previewTools.toolScale(self)
271 elif self.mirrorToolButton.getSelected():
272 self.tool = previewTools.toolNone(self)
274 self.tool = previewTools.toolNone(self)
275 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
276 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
277 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
278 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
279 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
280 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
281 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
282 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
284 def updateToolButtons(self):
285 if self._selectedObj is None:
289 self.rotateToolButton.setHidden(hidden)
290 self.scaleToolButton.setHidden(hidden)
291 self.mirrorToolButton.setHidden(hidden)
293 self.rotateToolButton.setSelected(False)
294 self.scaleToolButton.setSelected(False)
295 self.mirrorToolButton.setSelected(False)
298 def OnViewChange(self):
299 if self.viewSelection.getValue() == 4:
300 self.viewMode = 'gcode'
301 if self._gcode is not None and self._gcode.layerList is not None:
302 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
303 self._selectObject(None)
304 elif self.viewSelection.getValue() == 1:
305 self.viewMode = 'overhang'
306 elif self.viewSelection.getValue() == 2:
307 self.viewMode = 'transparent'
308 elif self.viewSelection.getValue() == 3:
309 self.viewMode = 'xray'
311 self.viewMode = 'normal'
312 self.layerSelect.setHidden(self.viewMode != 'gcode')
315 def OnRotateReset(self, button):
316 if self._selectedObj is None:
318 self._selectedObj.resetRotation()
319 self._scene.pushFree()
320 self._selectObject(self._selectedObj)
323 def OnLayFlat(self, button):
324 if self._selectedObj is None:
326 self._selectedObj.layFlat()
327 self._scene.pushFree()
328 self._selectObject(self._selectedObj)
331 def OnScaleReset(self, button):
332 if self._selectedObj is None:
334 self._selectedObj.resetScale()
335 self._selectObject(self._selectedObj)
336 self.updateProfileToControls()
339 def OnScaleMax(self, button):
340 if self._selectedObj is None:
342 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
343 self._scene.pushFree()
344 self._selectObject(self._selectedObj)
345 self.updateProfileToControls()
348 def OnMirror(self, axis):
349 if self._selectedObj is None:
351 self._selectedObj.mirror(axis)
354 def OnScaleEntry(self, value, axis):
355 if self._selectedObj is None:
361 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
362 self.updateProfileToControls()
363 self._scene.pushFree()
364 self._selectObject(self._selectedObj)
367 def OnScaleEntryMM(self, value, axis):
368 if self._selectedObj is None:
374 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
375 self.updateProfileToControls()
376 self._scene.pushFree()
377 self._selectObject(self._selectedObj)
380 def OnDeleteAll(self, e):
381 while len(self._scene.objects()) > 0:
382 self._deleteObject(self._scene.objects()[0])
383 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
385 def OnMultiply(self, e):
386 if self._focusObj is None:
389 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
390 if dlg.ShowModal() != wx.ID_OK:
399 self._scene.add(newObj)
400 self._scene.centerAll()
401 if not self._scene.checkPlatform(newObj):
406 self.notification.message("Could not create more then %d items" % (n - 1))
407 self._scene.remove(newObj)
408 self._scene.centerAll()
411 def OnSplitObject(self, e):
412 if self._focusObj is None:
414 self._scene.remove(self._focusObj)
415 for obj in self._focusObj.split(self._splitCallback):
416 if numpy.max(obj.getSize()) > 2.0:
418 self._scene.centerAll()
419 self._selectObject(None)
422 def OnCenter(self, e):
423 if self._focusObj is None:
425 self._focusObj.setPosition(numpy.array([0.0, 0.0]))
426 self._scene.pushFree()
428 def _splitCallback(self, progress):
431 def OnMergeObjects(self, e):
432 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
433 if len(self._scene.objects()) == 2:
434 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
437 self._scene.merge(self._selectedObj, self._focusObj)
440 def sceneUpdated(self):
441 self._sceneUpdateTimer.Start(500, True)
442 self._slicer.abortSlicer()
443 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
446 def _onRunSlicer(self, e):
447 if self._isSimpleMode:
448 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
449 self._slicer.runSlicer(self._scene)
450 if self._isSimpleMode:
451 profile.resetTempOverride()
453 def _updateSliceProgress(self, progressValue, ready):
455 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
457 self.printButton.setDisabled(not ready)
458 if progressValue >= 0.0:
459 self.printButton.setProgressBar(progressValue)
461 self.printButton.setProgressBar(None)
462 if self._gcode is not None:
464 for layerVBOlist in self._gcodeVBOs:
465 for vbo in layerVBOlist:
466 self.glReleaseList.append(vbo)
469 self.printButton.setProgressBar(None)
470 cost = self._slicer.getFilamentCost()
472 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
474 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
475 self._gcode = gcodeInterpreter.gcode()
476 self._gcodeFilename = self._slicer.getGCodeFilename()
478 self.printButton.setBottomText('')
481 def _loadGCode(self):
482 self._gcode.progressCallback = self._gcodeLoadCallback
483 self._gcode.load(self._gcodeFilename)
485 def _gcodeLoadCallback(self, progress):
486 if self._gcode is None:
488 if len(self._gcode.layerList) % 15 == 0:
490 if self._gcode is None:
492 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
493 if self.viewMode == 'gcode':
497 def loadScene(self, fileList):
498 for filename in fileList:
500 objList = meshLoader.loadMeshes(filename)
502 traceback.print_exc()
505 if self._objectLoadShader is not None:
506 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
510 self._scene.centerAll()
511 self._selectObject(obj)
512 if obj.getScale()[0] < 1.0:
513 self.notification.message("Warning: Object scaled down.")
516 def _deleteObject(self, obj):
517 if obj == self._selectedObj:
518 self._selectObject(None)
519 if obj == self._focusObj:
520 self._focusObj = None
521 self._scene.remove(obj)
522 for m in obj._meshList:
523 if m.vbo is not None and m.vbo.decRef():
524 self.glReleaseList.append(m.vbo)
529 def _selectObject(self, obj, zoom = True):
530 if obj != self._selectedObj:
531 self._selectedObj = obj
532 self.updateProfileToControls()
533 self.updateToolButtons()
534 if zoom and obj is not None:
535 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
536 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
537 newZoom = obj.getBoundaryCircle() * 6
538 if newZoom > numpy.max(self._machineSize) * 3:
539 newZoom = numpy.max(self._machineSize) * 3
540 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
542 def updateProfileToControls(self):
543 oldSimpleMode = self._isSimpleMode
544 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
545 if self._isSimpleMode != oldSimpleMode:
546 self._scene.arrangeAll()
548 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
549 self._objColors[0] = profile.getPreferenceColour('model_colour')
550 self._objColors[1] = profile.getPreferenceColour('model_colour2')
551 self._objColors[2] = profile.getPreferenceColour('model_colour3')
552 self._objColors[3] = profile.getPreferenceColour('model_colour4')
553 self._scene.setMachineSize(self._machineSize)
554 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
555 self._scene.setHeadSize(profile.getMachineSettingFloat('extruder_head_size_min_x'), profile.getMachineSettingFloat('extruder_head_size_max_x'), profile.getMachineSettingFloat('extruder_head_size_min_y'), profile.getMachineSettingFloat('extruder_head_size_max_y'), profile.getMachineSettingFloat('extruder_head_size_height'))
557 if self._selectedObj is not None:
558 scale = self._selectedObj.getScale()
559 size = self._selectedObj.getSize()
560 self.scaleXctrl.setValue(round(scale[0], 2))
561 self.scaleYctrl.setValue(round(scale[1], 2))
562 self.scaleZctrl.setValue(round(scale[2], 2))
563 self.scaleXmmctrl.setValue(round(size[0], 2))
564 self.scaleYmmctrl.setValue(round(size[1], 2))
565 self.scaleZmmctrl.setValue(round(size[2], 2))
567 def OnKeyChar(self, keyCode):
568 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
569 if self._selectedObj is not None:
570 self._deleteObject(self._selectedObj)
572 if keyCode == wx.WXK_UP:
573 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
575 elif keyCode == wx.WXK_DOWN:
576 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
578 elif keyCode == wx.WXK_PAGEUP:
579 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
581 elif keyCode == wx.WXK_PAGEDOWN:
582 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
585 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
586 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
587 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
588 from collections import defaultdict
589 from gc import get_objects
590 self._beforeLeakTest = defaultdict(int)
591 for i in get_objects():
592 self._beforeLeakTest[type(i)] += 1
593 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
594 from collections import defaultdict
595 from gc import get_objects
596 self._afterLeakTest = defaultdict(int)
597 for i in get_objects():
598 self._afterLeakTest[type(i)] += 1
599 for k in self._afterLeakTest:
600 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
601 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
603 def ShaderUpdate(self, v, f):
604 s = opengl.GLShader(v, f)
606 self._objectLoadShader.release()
607 self._objectLoadShader = s
608 for obj in self._scene.objects():
609 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
612 def OnMouseDown(self,e):
613 self._mouseX = e.GetX()
614 self._mouseY = e.GetY()
615 self._mouseClick3DPos = self._mouse3Dpos
616 self._mouseClickFocus = self._focusObj
618 self._mouseState = 'doubleClick'
620 self._mouseState = 'dragOrClick'
621 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
622 p0 -= self.getObjectCenterPos() - self._viewTarget
623 p1 -= self.getObjectCenterPos() - self._viewTarget
624 if self.tool.OnDragStart(p0, p1):
625 self._mouseState = 'tool'
626 if self._mouseState == 'dragOrClick':
627 if e.GetButton() == 1:
628 if self._focusObj is not None:
629 self._selectObject(self._focusObj, False)
632 def OnMouseUp(self, e):
633 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
635 if self._mouseState == 'dragOrClick':
636 if e.GetButton() == 1:
637 self._selectObject(self._focusObj)
638 if e.GetButton() == 3:
640 if self._focusObj is not None:
641 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
642 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
643 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
644 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
645 if ((self._selectedObj != self._focusObj and self._focusObj is not None and self._selectedObj is not None) or len(self._scene.objects()) == 2) and int(profile.getMachineSetting('extruder_amount')) > 1:
646 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
647 if len(self._scene.objects()) > 0:
648 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
649 if menu.MenuItemCount > 0:
652 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
653 self._scene.pushFree()
655 elif self._mouseState == 'tool':
656 if self.tempMatrix is not None and self._selectedObj is not None:
657 self._selectedObj.applyMatrix(self.tempMatrix)
658 self._scene.pushFree()
659 self._selectObject(self._selectedObj)
660 self.tempMatrix = None
661 self.tool.OnDragEnd()
663 self._mouseState = None
665 def OnMouseMotion(self,e):
666 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
667 p0 -= self.getObjectCenterPos() - self._viewTarget
668 p1 -= self.getObjectCenterPos() - self._viewTarget
670 if e.Dragging() and self._mouseState is not None:
671 if self._mouseState == 'tool':
672 self.tool.OnDrag(p0, p1)
673 elif not e.LeftIsDown() and e.RightIsDown():
674 self._mouseState = 'drag'
675 if wx.GetKeyState(wx.WXK_SHIFT):
676 a = math.cos(math.radians(self._yaw)) / 3.0
677 b = math.sin(math.radians(self._yaw)) / 3.0
678 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
679 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
680 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
681 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
683 self._yaw += e.GetX() - self._mouseX
684 self._pitch -= e.GetY() - self._mouseY
685 if self._pitch > 170:
689 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
690 self._mouseState = 'drag'
691 self._zoom += e.GetY() - self._mouseY
694 if self._zoom > numpy.max(self._machineSize) * 3:
695 self._zoom = numpy.max(self._machineSize) * 3
696 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
697 self._mouseState = 'dragObject'
698 z = max(0, self._mouseClick3DPos[2])
699 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
700 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
705 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
706 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
707 diff = cursorZ1 - cursorZ0
708 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
709 if not e.Dragging() or self._mouseState != 'tool':
710 self.tool.OnMouseMove(p0, p1)
712 self._mouseX = e.GetX()
713 self._mouseY = e.GetY()
715 def OnMouseWheel(self, e):
716 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
717 delta = max(min(delta,4),-4)
718 self._zoom *= 1.0 - delta / 10.0
721 if self._zoom > numpy.max(self._machineSize) * 3:
722 self._zoom = numpy.max(self._machineSize) * 3
725 def OnMouseLeave(self, e):
729 def getMouseRay(self, x, y):
730 if self._viewport is None:
731 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
732 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
733 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
734 p0 -= self._viewTarget
735 p1 -= self._viewTarget
738 def _init3DView(self):
739 # set viewing projection
740 size = self.GetSize()
741 glViewport(0, 0, size.GetWidth(), size.GetHeight())
744 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
746 glDisable(GL_RESCALE_NORMAL)
747 glDisable(GL_LIGHTING)
749 glEnable(GL_DEPTH_TEST)
750 glDisable(GL_CULL_FACE)
752 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
754 glClearColor(0.8, 0.8, 0.8, 1.0)
758 glMatrixMode(GL_PROJECTION)
760 aspect = float(size.GetWidth()) / float(size.GetHeight())
761 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
763 glMatrixMode(GL_MODELVIEW)
765 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
768 if machineCom.machineIsConnected():
769 self.printButton._imageID = 6
770 self.printButton._tooltip = _("Print")
771 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
772 self.printButton._imageID = 2
773 self.printButton._tooltip = _("Toolpath to SD")
775 self.printButton._imageID = 3
776 self.printButton._tooltip = _("Save toolpath")
778 if self._animView is not None:
779 self._viewTarget = self._animView.getPosition()
780 if self._animView.isDone():
781 self._animView = None
782 if self._animZoom is not None:
783 self._zoom = self._animZoom.getPosition()
784 if self._animZoom.isDone():
785 self._animZoom = None
786 if self.viewMode == 'gcode' and self._gcode is not None:
788 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
791 if self._objectShader is None:
792 if opengl.hasShaderSupport():
793 self._objectShader = opengl.GLShader("""
794 varying float light_amount;
798 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
799 gl_FrontColor = gl_Color;
801 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
805 varying float light_amount;
809 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
812 self._objectOverhangShader = opengl.GLShader("""
813 uniform float cosAngle;
814 uniform mat3 rotMatrix;
815 varying float light_amount;
819 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
820 gl_FrontColor = gl_Color;
822 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
824 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
826 light_amount = -10.0;
830 varying float light_amount;
834 if (light_amount == -10.0)
836 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
838 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
842 self._objectLoadShader = opengl.GLShader("""
843 uniform float intensity;
845 varying float light_amount;
849 vec4 tmp = gl_Vertex;
850 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
851 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
852 gl_Position = gl_ModelViewProjectionMatrix * tmp;
853 gl_FrontColor = gl_Color;
855 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
859 uniform float intensity;
860 varying float light_amount;
864 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
867 if self._objectShader == None or not self._objectShader.isValid():
868 self._objectShader = opengl.GLFakeShader()
869 self._objectOverhangShader = opengl.GLFakeShader()
870 self._objectLoadShader = None
872 glTranslate(0,0,-self._zoom)
873 glRotate(-self._pitch, 1,0,0)
874 glRotate(self._yaw, 0,0,1)
875 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
877 self._viewport = glGetIntegerv(GL_VIEWPORT)
878 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
879 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
881 glClearColor(1,1,1,1)
882 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
884 if self.viewMode != 'gcode':
885 for n in xrange(0, len(self._scene.objects())):
886 obj = self._scene.objects()[n]
887 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
888 self._renderObject(obj)
890 if self._mouseX > -1:
892 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
893 if n < len(self._scene.objects()):
894 self._focusObj = self._scene.objects()[n]
896 self._focusObj = None
897 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
898 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
899 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
900 self._mouse3Dpos -= self._viewTarget
903 glTranslate(0,0,-self._zoom)
904 glRotate(-self._pitch, 1,0,0)
905 glRotate(self._yaw, 0,0,1)
906 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
908 if self.viewMode == 'gcode':
909 if self._gcode is not None and self._gcode.layerList is None:
910 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
911 self._gcodeLoadThread.daemon = True
912 self._gcodeLoadThread.start()
913 if self._gcode is not None and self._gcode.layerList is not None:
915 if profile.getMachineSetting('machine_center_is_zero') != 'True':
916 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
918 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
919 for n in xrange(0, drawUpTill):
920 c = 1.0 - float(drawUpTill - n) / 15
922 if len(self._gcodeVBOs) < n + 1:
923 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
924 if time.time() - t > 0.5:
927 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
928 if n == drawUpTill - 1:
929 if len(self._gcodeVBOs[n]) < 9:
930 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
932 self._gcodeVBOs[n][8].render(GL_QUADS)
934 self._gcodeVBOs[n][9].render(GL_QUADS)
936 self._gcodeVBOs[n][10].render(GL_QUADS)
938 self._gcodeVBOs[n][11].render(GL_QUADS)
941 self._gcodeVBOs[n][12].render(GL_QUADS)
942 glColor3f(c/2, c/2, 0.0)
943 self._gcodeVBOs[n][13].render(GL_QUADS)
945 self._gcodeVBOs[n][14].render(GL_QUADS)
946 self._gcodeVBOs[n][15].render(GL_QUADS)
948 self._gcodeVBOs[n][16].render(GL_LINES)
951 self._gcodeVBOs[n][0].render(GL_LINES)
953 self._gcodeVBOs[n][1].render(GL_LINES)
955 self._gcodeVBOs[n][2].render(GL_LINES)
957 self._gcodeVBOs[n][3].render(GL_LINES)
960 self._gcodeVBOs[n][4].render(GL_LINES)
961 glColor3f(c/2, c/2, 0.0)
962 self._gcodeVBOs[n][5].render(GL_LINES)
964 self._gcodeVBOs[n][6].render(GL_LINES)
965 self._gcodeVBOs[n][7].render(GL_LINES)
968 glStencilFunc(GL_ALWAYS, 1, 1)
969 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
971 if self.viewMode == 'overhang':
972 self._objectOverhangShader.bind()
973 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
975 self._objectShader.bind()
976 for obj in self._scene.objects():
977 if obj._loadAnim is not None:
978 if obj._loadAnim.isDone():
983 if self._focusObj == obj:
985 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
988 if self._selectedObj == obj or self._selectedObj is None:
989 #If we want transparent, then first render a solid black model to remove the printer size lines.
990 if self.viewMode == 'transparent':
991 glColor4f(0, 0, 0, 0)
992 self._renderObject(obj)
994 glBlendFunc(GL_ONE, GL_ONE)
995 glDisable(GL_DEPTH_TEST)
997 if self.viewMode == 'xray':
998 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
999 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1000 glEnable(GL_STENCIL_TEST)
1002 if self.viewMode == 'overhang':
1003 if self._selectedObj == obj and self.tempMatrix is not None:
1004 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1006 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1008 if not self._scene.checkPlatform(obj):
1009 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1010 self._renderObject(obj)
1012 self._renderObject(obj, brightness)
1013 glDisable(GL_STENCIL_TEST)
1015 glEnable(GL_DEPTH_TEST)
1016 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1018 if self.viewMode == 'xray':
1021 glEnable(GL_STENCIL_TEST)
1022 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1023 glDisable(GL_DEPTH_TEST)
1024 for i in xrange(2, 15, 2):
1025 glStencilFunc(GL_EQUAL, i, 0xFF)
1026 glColor(float(i)/10, float(i)/10, float(i)/5)
1028 glVertex3f(-1000,-1000,-10)
1029 glVertex3f( 1000,-1000,-10)
1030 glVertex3f( 1000, 1000,-10)
1031 glVertex3f(-1000, 1000,-10)
1033 for i in xrange(1, 15, 2):
1034 glStencilFunc(GL_EQUAL, i, 0xFF)
1035 glColor(float(i)/10, 0, 0)
1037 glVertex3f(-1000,-1000,-10)
1038 glVertex3f( 1000,-1000,-10)
1039 glVertex3f( 1000, 1000,-10)
1040 glVertex3f(-1000, 1000,-10)
1043 glDisable(GL_STENCIL_TEST)
1044 glEnable(GL_DEPTH_TEST)
1046 self._objectShader.unbind()
1048 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1050 if self._objectLoadShader is not None:
1051 self._objectLoadShader.bind()
1052 glColor4f(0.2, 0.6, 1.0, 1.0)
1053 for obj in self._scene.objects():
1054 if obj._loadAnim is None:
1056 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1057 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1058 self._renderObject(obj)
1059 self._objectLoadShader.unbind()
1064 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1066 z = self._usbPrintMonitor.getZ()
1067 size = self._machineSize
1068 glColor4ub(255,255,0,128)
1070 glVertex3f(-size[0]/2,-size[1]/2, z)
1071 glVertex3f( size[0]/2,-size[1]/2, z)
1072 glVertex3f( size[0]/2, size[1]/2, z)
1073 glVertex3f(-size[0]/2, size[1]/2, z)
1076 if self.viewMode == 'gcode':
1077 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1078 glDisable(GL_DEPTH_TEST)
1081 glTranslate(0,-4,-10)
1082 glColor4ub(60,60,60,255)
1083 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1086 #Draw the object box-shadow, so you can see where it will collide with other objects.
1087 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1088 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1090 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1092 glEnable(GL_CULL_FACE)
1093 glColor4f(0,0,0,0.12)
1095 glVertex3f(-size[0], size[1], 0.1)
1096 glVertex3f(-size[0], -size[1], 0.1)
1097 glVertex3f( size[0], -size[1], 0.1)
1098 glVertex3f( size[0], size[1], 0.1)
1100 glDisable(GL_CULL_FACE)
1103 #Draw the outline of the selected object, on top of everything else except the GUI.
1104 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1105 glDisable(GL_DEPTH_TEST)
1106 glEnable(GL_CULL_FACE)
1107 glEnable(GL_STENCIL_TEST)
1109 glStencilFunc(GL_EQUAL, 0, 255)
1111 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1113 glColor4f(1,1,1,0.5)
1114 self._renderObject(self._selectedObj)
1115 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1117 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1118 glDisable(GL_STENCIL_TEST)
1119 glDisable(GL_CULL_FACE)
1120 glEnable(GL_DEPTH_TEST)
1122 if self._selectedObj is not None:
1124 pos = self.getObjectCenterPos()
1125 glTranslate(pos[0], pos[1], pos[2])
1128 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1129 glDisable(GL_DEPTH_TEST)
1132 glTranslate(0,-4,-10)
1133 glColor4ub(60,60,60,255)
1134 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1137 def _renderObject(self, obj, brightness = False, addSink = True):
1140 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1142 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1144 if self.tempMatrix is not None and obj == self._selectedObj:
1145 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1146 glMultMatrixf(tempMatrix)
1148 offset = obj.getDrawOffset()
1149 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1151 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1152 glMultMatrixf(tempMatrix)
1155 for m in obj._meshList:
1157 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1159 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1164 def _drawMachine(self):
1165 glEnable(GL_CULL_FACE)
1168 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1170 machine = profile.getMachineSetting('machine_type')
1171 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
1172 if machine not in self._platformMesh:
1173 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1175 self._platformMesh[machine] = meshes[0]
1177 self._platformMesh[machine] = None
1178 if machine == 'ultimaker2':
1179 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1181 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1182 glColor4f(1,1,1,0.5)
1183 self._objectShader.bind()
1184 self._renderObject(self._platformMesh[machine], False, False)
1185 self._objectShader.unbind()
1187 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1188 if machine == 'ultimaker2':
1189 if not hasattr(self._platformMesh[machine], 'texture'):
1190 self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1191 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1192 glEnable(GL_TEXTURE_2D)
1196 glTranslate(0,150,-5)
1201 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1204 glVertex3f( w, 0, h)
1206 glVertex3f(-w, 0, h)
1208 glVertex3f(-w, 0, 0)
1210 glVertex3f( w, 0, 0)
1213 glVertex3f(-w, d, h)
1215 glVertex3f( w, d, h)
1217 glVertex3f( w, d, 0)
1219 glVertex3f(-w, d, 0)
1221 glDisable(GL_TEXTURE_2D)
1222 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1228 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1229 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1230 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1231 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1232 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1233 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1236 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1237 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1238 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1239 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1240 v4 = [ size[0] / 2, size[1] / 2, 0]
1241 v5 = [ size[0] / 2,-size[1] / 2, 0]
1242 v6 = [-size[0] / 2, size[1] / 2, 0]
1243 v7 = [-size[0] / 2,-size[1] / 2, 0]
1245 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1246 glEnableClientState(GL_VERTEX_ARRAY)
1247 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1249 glColor4ub(5, 171, 231, 64)
1250 glDrawArrays(GL_QUADS, 0, 4)
1251 glColor4ub(5, 171, 231, 96)
1252 glDrawArrays(GL_QUADS, 4, 8)
1253 glColor4ub(5, 171, 231, 128)
1254 glDrawArrays(GL_QUADS, 12, 8)
1255 glDisableClientState(GL_VERTEX_ARRAY)
1257 sx = self._machineSize[0]
1258 sy = self._machineSize[1]
1259 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1260 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1265 x1 = max(min(x1, sx/2), -sx/2)
1266 y1 = max(min(y1, sy/2), -sy/2)
1267 x2 = max(min(x2, sx/2), -sx/2)
1268 y2 = max(min(y2, sy/2), -sy/2)
1269 if (x & 1) == (y & 1):
1270 glColor4ub(5, 171, 231, 127)
1272 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1274 glVertex3f(x1, y1, -0.02)
1275 glVertex3f(x2, y1, -0.02)
1276 glVertex3f(x2, y2, -0.02)
1277 glVertex3f(x1, y2, -0.02)
1281 glDisable(GL_CULL_FACE)
1283 def _generateGCodeVBOs(self, layer):
1285 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1286 if ':' in extrudeType:
1287 extruder = int(extrudeType[extrudeType.find(':')+1:])
1288 extrudeType = extrudeType[0:extrudeType.find(':')]
1291 pointList = numpy.zeros((0,3), numpy.float32)
1293 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1295 a = numpy.concatenate((a[:-1], a[1:]), 1)
1296 a = a.reshape((len(a) * 2, 3))
1297 pointList = numpy.concatenate((pointList, a))
1298 ret.append(opengl.GLVBO(pointList))
1301 def _generateGCodeVBOs2(self, layer):
1302 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1303 filamentArea = math.pi * filamentRadius * filamentRadius
1304 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1307 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1308 if ':' in extrudeType:
1309 extruder = int(extrudeType[extrudeType.find(':')+1:])
1310 extrudeType = extrudeType[0:extrudeType.find(':')]
1313 pointList = numpy.zeros((0,3), numpy.float32)
1315 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1317 if extrudeType == 'FILL':
1320 normal = a[1:] - a[:-1]
1321 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1322 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1325 ePerDist = path['extrusion'][1:] / lens
1327 lineWidth = ePerDist / path['layerThickness'] / 2.0
1329 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1331 normal[:,0] *= lineWidth
1332 normal[:,1] *= lineWidth
1334 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1335 b = numpy.concatenate((b, a[1:] + normal), 1)
1336 b = numpy.concatenate((b, a[1:] - normal), 1)
1337 b = numpy.concatenate((b, a[:-1] - normal), 1)
1338 b = numpy.concatenate((b, a[:-1] + normal), 1)
1339 b = b.reshape((len(b) * 4, 3))
1342 normal2 = normal[:-1] + normal[1:]
1343 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1344 normal2[:,0] /= lens2
1345 normal2[:,1] /= lens2
1346 normal2[:,0] *= lineWidth[:-1]
1347 normal2[:,1] *= lineWidth[:-1]
1349 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1350 c = numpy.concatenate((c, a[1:-1]), 1)
1351 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1352 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1353 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1355 c = numpy.concatenate((c, a[1:-1]), 1)
1356 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1357 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1358 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1360 c = c.reshape((len(c) * 8, 3))
1362 pointList = numpy.concatenate((pointList, b, c))
1364 pointList = numpy.concatenate((pointList, b))
1365 ret.append(opengl.GLVBO(pointList))
1367 pointList = numpy.zeros((0,3), numpy.float32)
1369 if path['type'] == 'move':
1370 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1371 a = numpy.concatenate((a[:-1], a[1:]), 1)
1372 a = a.reshape((len(a) * 2, 3))
1373 pointList = numpy.concatenate((pointList, a))
1374 if path['type'] == 'retract':
1375 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1376 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1377 a = a.reshape((len(a) * 2, 3))
1378 pointList = numpy.concatenate((pointList, a))
1379 ret.append(opengl.GLVBO(pointList))
1383 def getObjectCenterPos(self):
1384 if self._selectedObj is None:
1385 return [0.0, 0.0, 0.0]
1386 pos = self._selectedObj.getPosition()
1387 size = self._selectedObj.getSize()
1388 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1390 def getObjectBoundaryCircle(self):
1391 if self._selectedObj is None:
1393 return self._selectedObj.getBoundaryCircle()
1395 def getObjectSize(self):
1396 if self._selectedObj is None:
1397 return [0.0, 0.0, 0.0]
1398 return self._selectedObj.getSize()
1400 def getObjectMatrix(self):
1401 if self._selectedObj is None:
1402 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1403 return self._selectedObj.getMatrix()
1405 class shaderEditor(wx.Dialog):
1406 def __init__(self, parent, callback, v, f):
1407 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1408 self._callback = callback
1409 s = wx.BoxSizer(wx.VERTICAL)
1411 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1412 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1413 s.Add(self._vertex, 1, flag=wx.EXPAND)
1414 s.Add(self._fragment, 1, flag=wx.EXPAND)
1416 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1417 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1419 self.SetPosition(self.GetParent().GetPosition())
1420 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1423 def OnText(self, e):
1424 self._callback(self._vertex.GetValue(), self._fragment.GetValue())