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 defPath = profile.getPreference('lastFile')
226 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
227 dlg=wx.FileDialog(self, _("Save toolpath"), defPath, style=wx.FD_SAVE)
228 dlg.SetFilename(self._scene._objectList[0].getName())
229 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
230 if dlg.ShowModal() != wx.ID_OK:
233 filename = dlg.GetPath()
236 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
238 def _copyFile(self, fileA, fileB, allowEject = False):
240 size = float(os.stat(fileA).st_size)
241 with open(fileA, 'rb') as fsrc:
242 with open(fileB, 'wb') as fdst:
244 buf = fsrc.read(16*1024)
248 self.printButton.setProgressBar(float(fsrc.tell()) / size)
253 self.notification.message("Failed to save")
256 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...'))
258 self.notification.message("Saved as %s" % (fileB))
259 self.printButton.setProgressBar(None)
260 if fileA == self._slicer.getGCodeFilename():
261 self._slicer.submitSliceInfoOnline()
263 def _showSliceLog(self):
264 dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
268 def OnToolSelect(self, button):
269 if self.rotateToolButton.getSelected():
270 self.tool = previewTools.toolRotate(self)
271 elif self.scaleToolButton.getSelected():
272 self.tool = previewTools.toolScale(self)
273 elif self.mirrorToolButton.getSelected():
274 self.tool = previewTools.toolNone(self)
276 self.tool = previewTools.toolNone(self)
277 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
278 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
279 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
280 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
281 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
282 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
283 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
284 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
286 def updateToolButtons(self):
287 if self._selectedObj is None:
291 self.rotateToolButton.setHidden(hidden)
292 self.scaleToolButton.setHidden(hidden)
293 self.mirrorToolButton.setHidden(hidden)
295 self.rotateToolButton.setSelected(False)
296 self.scaleToolButton.setSelected(False)
297 self.mirrorToolButton.setSelected(False)
300 def OnViewChange(self):
301 if self.viewSelection.getValue() == 4:
302 self.viewMode = 'gcode'
303 if self._gcode is not None and self._gcode.layerList is not None:
304 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
305 self._selectObject(None)
306 elif self.viewSelection.getValue() == 1:
307 self.viewMode = 'overhang'
308 elif self.viewSelection.getValue() == 2:
309 self.viewMode = 'transparent'
310 elif self.viewSelection.getValue() == 3:
311 self.viewMode = 'xray'
313 self.viewMode = 'normal'
314 self.layerSelect.setHidden(self.viewMode != 'gcode')
317 def OnRotateReset(self, button):
318 if self._selectedObj is None:
320 self._selectedObj.resetRotation()
321 self._scene.pushFree()
322 self._selectObject(self._selectedObj)
325 def OnLayFlat(self, button):
326 if self._selectedObj is None:
328 self._selectedObj.layFlat()
329 self._scene.pushFree()
330 self._selectObject(self._selectedObj)
333 def OnScaleReset(self, button):
334 if self._selectedObj is None:
336 self._selectedObj.resetScale()
337 self._selectObject(self._selectedObj)
338 self.updateProfileToControls()
341 def OnScaleMax(self, button):
342 if self._selectedObj is None:
344 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
345 self._scene.pushFree()
346 self._selectObject(self._selectedObj)
347 self.updateProfileToControls()
350 def OnMirror(self, axis):
351 if self._selectedObj is None:
353 self._selectedObj.mirror(axis)
356 def OnScaleEntry(self, value, axis):
357 if self._selectedObj is None:
363 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
364 self.updateProfileToControls()
365 self._scene.pushFree()
366 self._selectObject(self._selectedObj)
369 def OnScaleEntryMM(self, value, axis):
370 if self._selectedObj is None:
376 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
377 self.updateProfileToControls()
378 self._scene.pushFree()
379 self._selectObject(self._selectedObj)
382 def OnDeleteAll(self, e):
383 while len(self._scene.objects()) > 0:
384 self._deleteObject(self._scene.objects()[0])
385 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
387 def OnMultiply(self, e):
388 if self._focusObj is None:
391 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
392 if dlg.ShowModal() != wx.ID_OK:
401 self._scene.add(newObj)
402 self._scene.centerAll()
403 if not self._scene.checkPlatform(newObj):
408 self.notification.message("Could not create more then %d items" % (n - 1))
409 self._scene.remove(newObj)
410 self._scene.centerAll()
413 def OnSplitObject(self, e):
414 if self._focusObj is None:
416 self._scene.remove(self._focusObj)
417 for obj in self._focusObj.split(self._splitCallback):
418 if numpy.max(obj.getSize()) > 2.0:
420 self._scene.centerAll()
421 self._selectObject(None)
424 def OnCenter(self, e):
425 if self._focusObj is None:
427 self._focusObj.setPosition(numpy.array([0.0, 0.0]))
428 self._scene.pushFree()
430 def _splitCallback(self, progress):
433 def OnMergeObjects(self, e):
434 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
435 if len(self._scene.objects()) == 2:
436 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
439 self._scene.merge(self._selectedObj, self._focusObj)
442 def sceneUpdated(self):
443 self._sceneUpdateTimer.Start(500, True)
444 self._slicer.abortSlicer()
445 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
448 def _onRunSlicer(self, e):
449 if self._isSimpleMode:
450 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
451 self._slicer.runSlicer(self._scene)
452 if self._isSimpleMode:
453 profile.resetTempOverride()
455 def _updateSliceProgress(self, progressValue, ready):
457 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
459 self.printButton.setDisabled(not ready)
460 if progressValue >= 0.0:
461 self.printButton.setProgressBar(progressValue)
463 self.printButton.setProgressBar(None)
464 if self._gcode is not None:
466 for layerVBOlist in self._gcodeVBOs:
467 for vbo in layerVBOlist:
468 self.glReleaseList.append(vbo)
471 self.printButton.setProgressBar(None)
472 cost = self._slicer.getFilamentCost()
474 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
476 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
477 self._gcode = gcodeInterpreter.gcode()
478 self._gcodeFilename = self._slicer.getGCodeFilename()
480 self.printButton.setBottomText('')
483 def _loadGCode(self):
484 self._gcode.progressCallback = self._gcodeLoadCallback
485 self._gcode.load(self._gcodeFilename)
487 def _gcodeLoadCallback(self, progress):
488 if self._gcode is None:
490 if len(self._gcode.layerList) % 15 == 0:
492 if self._gcode is None:
494 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
495 if self.viewMode == 'gcode':
499 def loadScene(self, fileList):
500 for filename in fileList:
502 objList = meshLoader.loadMeshes(filename)
504 traceback.print_exc()
507 if self._objectLoadShader is not None:
508 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
512 self._scene.centerAll()
513 self._selectObject(obj)
514 if obj.getScale()[0] < 1.0:
515 self.notification.message("Warning: Object scaled down.")
518 def _deleteObject(self, obj):
519 if obj == self._selectedObj:
520 self._selectObject(None)
521 if obj == self._focusObj:
522 self._focusObj = None
523 self._scene.remove(obj)
524 for m in obj._meshList:
525 if m.vbo is not None and m.vbo.decRef():
526 self.glReleaseList.append(m.vbo)
531 def _selectObject(self, obj, zoom = True):
532 if obj != self._selectedObj:
533 self._selectedObj = obj
534 self.updateProfileToControls()
535 self.updateToolButtons()
536 if zoom and obj is not None:
537 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
538 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
539 newZoom = obj.getBoundaryCircle() * 6
540 if newZoom > numpy.max(self._machineSize) * 3:
541 newZoom = numpy.max(self._machineSize) * 3
542 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
544 def updateProfileToControls(self):
545 oldSimpleMode = self._isSimpleMode
546 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
547 if self._isSimpleMode != oldSimpleMode:
548 self._scene.arrangeAll()
550 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
551 self._objColors[0] = profile.getPreferenceColour('model_colour')
552 self._objColors[1] = profile.getPreferenceColour('model_colour2')
553 self._objColors[2] = profile.getPreferenceColour('model_colour3')
554 self._objColors[3] = profile.getPreferenceColour('model_colour4')
555 self._scene.setMachineSize(self._machineSize)
556 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
557 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'))
559 if self._selectedObj is not None:
560 scale = self._selectedObj.getScale()
561 size = self._selectedObj.getSize()
562 self.scaleXctrl.setValue(round(scale[0], 2))
563 self.scaleYctrl.setValue(round(scale[1], 2))
564 self.scaleZctrl.setValue(round(scale[2], 2))
565 self.scaleXmmctrl.setValue(round(size[0], 2))
566 self.scaleYmmctrl.setValue(round(size[1], 2))
567 self.scaleZmmctrl.setValue(round(size[2], 2))
569 def OnKeyChar(self, keyCode):
570 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
571 if self._selectedObj is not None:
572 self._deleteObject(self._selectedObj)
574 if keyCode == wx.WXK_UP:
575 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
577 elif keyCode == wx.WXK_DOWN:
578 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
580 elif keyCode == wx.WXK_PAGEUP:
581 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
583 elif keyCode == wx.WXK_PAGEDOWN:
584 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
587 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
588 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
589 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
590 from collections import defaultdict
591 from gc import get_objects
592 self._beforeLeakTest = defaultdict(int)
593 for i in get_objects():
594 self._beforeLeakTest[type(i)] += 1
595 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
596 from collections import defaultdict
597 from gc import get_objects
598 self._afterLeakTest = defaultdict(int)
599 for i in get_objects():
600 self._afterLeakTest[type(i)] += 1
601 for k in self._afterLeakTest:
602 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
603 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
605 def ShaderUpdate(self, v, f):
606 s = opengl.GLShader(v, f)
608 self._objectLoadShader.release()
609 self._objectLoadShader = s
610 for obj in self._scene.objects():
611 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
614 def OnMouseDown(self,e):
615 self._mouseX = e.GetX()
616 self._mouseY = e.GetY()
617 self._mouseClick3DPos = self._mouse3Dpos
618 self._mouseClickFocus = self._focusObj
620 self._mouseState = 'doubleClick'
622 self._mouseState = 'dragOrClick'
623 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
624 p0 -= self.getObjectCenterPos() - self._viewTarget
625 p1 -= self.getObjectCenterPos() - self._viewTarget
626 if self.tool.OnDragStart(p0, p1):
627 self._mouseState = 'tool'
628 if self._mouseState == 'dragOrClick':
629 if e.GetButton() == 1:
630 if self._focusObj is not None:
631 self._selectObject(self._focusObj, False)
634 def OnMouseUp(self, e):
635 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
637 if self._mouseState == 'dragOrClick':
638 if e.GetButton() == 1:
639 self._selectObject(self._focusObj)
640 if e.GetButton() == 3:
642 if self._focusObj is not None:
643 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
644 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
645 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
646 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
647 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:
648 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
649 if len(self._scene.objects()) > 0:
650 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
651 if menu.MenuItemCount > 0:
654 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
655 self._scene.pushFree()
657 elif self._mouseState == 'tool':
658 if self.tempMatrix is not None and self._selectedObj is not None:
659 self._selectedObj.applyMatrix(self.tempMatrix)
660 self._scene.pushFree()
661 self._selectObject(self._selectedObj)
662 self.tempMatrix = None
663 self.tool.OnDragEnd()
665 self._mouseState = None
667 def OnMouseMotion(self,e):
668 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
669 p0 -= self.getObjectCenterPos() - self._viewTarget
670 p1 -= self.getObjectCenterPos() - self._viewTarget
672 if e.Dragging() and self._mouseState is not None:
673 if self._mouseState == 'tool':
674 self.tool.OnDrag(p0, p1)
675 elif not e.LeftIsDown() and e.RightIsDown():
676 self._mouseState = 'drag'
677 if wx.GetKeyState(wx.WXK_SHIFT):
678 a = math.cos(math.radians(self._yaw)) / 3.0
679 b = math.sin(math.radians(self._yaw)) / 3.0
680 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
681 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
682 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
683 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
685 self._yaw += e.GetX() - self._mouseX
686 self._pitch -= e.GetY() - self._mouseY
687 if self._pitch > 170:
691 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
692 self._mouseState = 'drag'
693 self._zoom += e.GetY() - self._mouseY
696 if self._zoom > numpy.max(self._machineSize) * 3:
697 self._zoom = numpy.max(self._machineSize) * 3
698 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
699 self._mouseState = 'dragObject'
700 z = max(0, self._mouseClick3DPos[2])
701 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
702 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
707 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
708 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
709 diff = cursorZ1 - cursorZ0
710 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
711 if not e.Dragging() or self._mouseState != 'tool':
712 self.tool.OnMouseMove(p0, p1)
714 self._mouseX = e.GetX()
715 self._mouseY = e.GetY()
717 def OnMouseWheel(self, e):
718 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
719 delta = max(min(delta,4),-4)
720 self._zoom *= 1.0 - delta / 10.0
723 if self._zoom > numpy.max(self._machineSize) * 3:
724 self._zoom = numpy.max(self._machineSize) * 3
727 def OnMouseLeave(self, e):
731 def getMouseRay(self, x, y):
732 if self._viewport is None:
733 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
734 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
735 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
736 p0 -= self._viewTarget
737 p1 -= self._viewTarget
740 def _init3DView(self):
741 # set viewing projection
742 size = self.GetSize()
743 glViewport(0, 0, size.GetWidth(), size.GetHeight())
746 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
748 glDisable(GL_RESCALE_NORMAL)
749 glDisable(GL_LIGHTING)
751 glEnable(GL_DEPTH_TEST)
752 glDisable(GL_CULL_FACE)
754 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
756 glClearColor(0.8, 0.8, 0.8, 1.0)
760 glMatrixMode(GL_PROJECTION)
762 aspect = float(size.GetWidth()) / float(size.GetHeight())
763 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
765 glMatrixMode(GL_MODELVIEW)
767 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
770 if machineCom.machineIsConnected():
771 self.printButton._imageID = 6
772 self.printButton._tooltip = _("Print")
773 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
774 self.printButton._imageID = 2
775 self.printButton._tooltip = _("Toolpath to SD")
777 self.printButton._imageID = 3
778 self.printButton._tooltip = _("Save toolpath")
780 if self._animView is not None:
781 self._viewTarget = self._animView.getPosition()
782 if self._animView.isDone():
783 self._animView = None
784 if self._animZoom is not None:
785 self._zoom = self._animZoom.getPosition()
786 if self._animZoom.isDone():
787 self._animZoom = None
788 if self.viewMode == 'gcode' and self._gcode is not None:
790 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
793 if self._objectShader is None:
794 if opengl.hasShaderSupport():
795 self._objectShader = opengl.GLShader("""
796 varying float light_amount;
800 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
801 gl_FrontColor = gl_Color;
803 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
807 varying float light_amount;
811 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
814 self._objectOverhangShader = opengl.GLShader("""
815 uniform float cosAngle;
816 uniform mat3 rotMatrix;
817 varying float light_amount;
821 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
822 gl_FrontColor = gl_Color;
824 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
826 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
828 light_amount = -10.0;
832 varying float light_amount;
836 if (light_amount == -10.0)
838 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
840 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
844 self._objectLoadShader = opengl.GLShader("""
845 uniform float intensity;
847 varying float light_amount;
851 vec4 tmp = gl_Vertex;
852 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
853 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
854 gl_Position = gl_ModelViewProjectionMatrix * tmp;
855 gl_FrontColor = gl_Color;
857 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
861 uniform float intensity;
862 varying float light_amount;
866 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
869 if self._objectShader == None or not self._objectShader.isValid():
870 self._objectShader = opengl.GLFakeShader()
871 self._objectOverhangShader = opengl.GLFakeShader()
872 self._objectLoadShader = None
874 glTranslate(0,0,-self._zoom)
875 glRotate(-self._pitch, 1,0,0)
876 glRotate(self._yaw, 0,0,1)
877 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
879 self._viewport = glGetIntegerv(GL_VIEWPORT)
880 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
881 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
883 glClearColor(1,1,1,1)
884 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
886 if self.viewMode != 'gcode':
887 for n in xrange(0, len(self._scene.objects())):
888 obj = self._scene.objects()[n]
889 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
890 self._renderObject(obj)
892 if self._mouseX > -1:
894 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
895 if n < len(self._scene.objects()):
896 self._focusObj = self._scene.objects()[n]
898 self._focusObj = None
899 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
900 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
901 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
902 self._mouse3Dpos -= self._viewTarget
905 glTranslate(0,0,-self._zoom)
906 glRotate(-self._pitch, 1,0,0)
907 glRotate(self._yaw, 0,0,1)
908 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
910 if self.viewMode == 'gcode':
911 if self._gcode is not None and self._gcode.layerList is None:
912 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
913 self._gcodeLoadThread.daemon = True
914 self._gcodeLoadThread.start()
915 if self._gcode is not None and self._gcode.layerList is not None:
917 if profile.getMachineSetting('machine_center_is_zero') != 'True':
918 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
920 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
921 for n in xrange(0, drawUpTill):
922 c = 1.0 - float(drawUpTill - n) / 15
924 if len(self._gcodeVBOs) < n + 1:
925 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
926 if time.time() - t > 0.5:
929 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
930 if n == drawUpTill - 1:
931 if len(self._gcodeVBOs[n]) < 9:
932 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
934 self._gcodeVBOs[n][8].render(GL_QUADS)
936 self._gcodeVBOs[n][9].render(GL_QUADS)
938 self._gcodeVBOs[n][10].render(GL_QUADS)
940 self._gcodeVBOs[n][11].render(GL_QUADS)
943 self._gcodeVBOs[n][12].render(GL_QUADS)
944 glColor3f(c/2, c/2, 0.0)
945 self._gcodeVBOs[n][13].render(GL_QUADS)
947 self._gcodeVBOs[n][14].render(GL_QUADS)
948 self._gcodeVBOs[n][15].render(GL_QUADS)
950 self._gcodeVBOs[n][16].render(GL_LINES)
953 self._gcodeVBOs[n][0].render(GL_LINES)
955 self._gcodeVBOs[n][1].render(GL_LINES)
957 self._gcodeVBOs[n][2].render(GL_LINES)
959 self._gcodeVBOs[n][3].render(GL_LINES)
962 self._gcodeVBOs[n][4].render(GL_LINES)
963 glColor3f(c/2, c/2, 0.0)
964 self._gcodeVBOs[n][5].render(GL_LINES)
966 self._gcodeVBOs[n][6].render(GL_LINES)
967 self._gcodeVBOs[n][7].render(GL_LINES)
970 glStencilFunc(GL_ALWAYS, 1, 1)
971 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
973 if self.viewMode == 'overhang':
974 self._objectOverhangShader.bind()
975 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
977 self._objectShader.bind()
978 for obj in self._scene.objects():
979 if obj._loadAnim is not None:
980 if obj._loadAnim.isDone():
985 if self._focusObj == obj:
987 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
990 if self._selectedObj == obj or self._selectedObj is None:
991 #If we want transparent, then first render a solid black model to remove the printer size lines.
992 if self.viewMode == 'transparent':
993 glColor4f(0, 0, 0, 0)
994 self._renderObject(obj)
996 glBlendFunc(GL_ONE, GL_ONE)
997 glDisable(GL_DEPTH_TEST)
999 if self.viewMode == 'xray':
1000 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1001 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1002 glEnable(GL_STENCIL_TEST)
1004 if self.viewMode == 'overhang':
1005 if self._selectedObj == obj and self.tempMatrix is not None:
1006 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1008 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1010 if not self._scene.checkPlatform(obj):
1011 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1012 self._renderObject(obj)
1014 self._renderObject(obj, brightness)
1015 glDisable(GL_STENCIL_TEST)
1017 glEnable(GL_DEPTH_TEST)
1018 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1020 if self.viewMode == 'xray':
1023 glEnable(GL_STENCIL_TEST)
1024 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1025 glDisable(GL_DEPTH_TEST)
1026 for i in xrange(2, 15, 2):
1027 glStencilFunc(GL_EQUAL, i, 0xFF)
1028 glColor(float(i)/10, float(i)/10, float(i)/5)
1030 glVertex3f(-1000,-1000,-10)
1031 glVertex3f( 1000,-1000,-10)
1032 glVertex3f( 1000, 1000,-10)
1033 glVertex3f(-1000, 1000,-10)
1035 for i in xrange(1, 15, 2):
1036 glStencilFunc(GL_EQUAL, i, 0xFF)
1037 glColor(float(i)/10, 0, 0)
1039 glVertex3f(-1000,-1000,-10)
1040 glVertex3f( 1000,-1000,-10)
1041 glVertex3f( 1000, 1000,-10)
1042 glVertex3f(-1000, 1000,-10)
1045 glDisable(GL_STENCIL_TEST)
1046 glEnable(GL_DEPTH_TEST)
1048 self._objectShader.unbind()
1050 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1052 if self._objectLoadShader is not None:
1053 self._objectLoadShader.bind()
1054 glColor4f(0.2, 0.6, 1.0, 1.0)
1055 for obj in self._scene.objects():
1056 if obj._loadAnim is None:
1058 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1059 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1060 self._renderObject(obj)
1061 self._objectLoadShader.unbind()
1066 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1068 z = self._usbPrintMonitor.getZ()
1069 size = self._machineSize
1070 glColor4ub(255,255,0,128)
1072 glVertex3f(-size[0]/2,-size[1]/2, z)
1073 glVertex3f( size[0]/2,-size[1]/2, z)
1074 glVertex3f( size[0]/2, size[1]/2, z)
1075 glVertex3f(-size[0]/2, size[1]/2, z)
1078 if self.viewMode == 'gcode':
1079 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1080 glDisable(GL_DEPTH_TEST)
1083 glTranslate(0,-4,-10)
1084 glColor4ub(60,60,60,255)
1085 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1088 #Draw the object box-shadow, so you can see where it will collide with other objects.
1089 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1090 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1092 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1094 glEnable(GL_CULL_FACE)
1095 glColor4f(0,0,0,0.12)
1097 glVertex3f(-size[0], size[1], 0.1)
1098 glVertex3f(-size[0], -size[1], 0.1)
1099 glVertex3f( size[0], -size[1], 0.1)
1100 glVertex3f( size[0], size[1], 0.1)
1102 glDisable(GL_CULL_FACE)
1105 #Draw the outline of the selected object, on top of everything else except the GUI.
1106 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1107 glDisable(GL_DEPTH_TEST)
1108 glEnable(GL_CULL_FACE)
1109 glEnable(GL_STENCIL_TEST)
1111 glStencilFunc(GL_EQUAL, 0, 255)
1113 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1115 glColor4f(1,1,1,0.5)
1116 self._renderObject(self._selectedObj)
1117 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1119 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1120 glDisable(GL_STENCIL_TEST)
1121 glDisable(GL_CULL_FACE)
1122 glEnable(GL_DEPTH_TEST)
1124 if self._selectedObj is not None:
1126 pos = self.getObjectCenterPos()
1127 glTranslate(pos[0], pos[1], pos[2])
1130 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1131 glDisable(GL_DEPTH_TEST)
1134 glTranslate(0,-4,-10)
1135 glColor4ub(60,60,60,255)
1136 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1139 def _renderObject(self, obj, brightness = False, addSink = True):
1142 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1144 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1146 if self.tempMatrix is not None and obj == self._selectedObj:
1147 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1148 glMultMatrixf(tempMatrix)
1150 offset = obj.getDrawOffset()
1151 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1153 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1154 glMultMatrixf(tempMatrix)
1157 for m in obj._meshList:
1159 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1161 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1166 def _drawMachine(self):
1167 glEnable(GL_CULL_FACE)
1170 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1172 machine = profile.getMachineSetting('machine_type')
1173 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
1174 if machine not in self._platformMesh:
1175 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1177 self._platformMesh[machine] = meshes[0]
1179 self._platformMesh[machine] = None
1180 if machine == 'ultimaker2':
1181 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1183 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1184 glColor4f(1,1,1,0.5)
1185 self._objectShader.bind()
1186 self._renderObject(self._platformMesh[machine], False, False)
1187 self._objectShader.unbind()
1189 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1190 if machine == 'ultimaker2':
1191 if not hasattr(self._platformMesh[machine], 'texture'):
1192 self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1193 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1194 glEnable(GL_TEXTURE_2D)
1198 glTranslate(0,150,-5)
1203 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1206 glVertex3f( w, 0, h)
1208 glVertex3f(-w, 0, h)
1210 glVertex3f(-w, 0, 0)
1212 glVertex3f( w, 0, 0)
1215 glVertex3f(-w, d, h)
1217 glVertex3f( w, d, h)
1219 glVertex3f( w, d, 0)
1221 glVertex3f(-w, d, 0)
1223 glDisable(GL_TEXTURE_2D)
1224 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1230 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1231 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1232 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1233 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1234 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1235 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1238 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1239 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1240 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1241 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1242 v4 = [ size[0] / 2, size[1] / 2, 0]
1243 v5 = [ size[0] / 2,-size[1] / 2, 0]
1244 v6 = [-size[0] / 2, size[1] / 2, 0]
1245 v7 = [-size[0] / 2,-size[1] / 2, 0]
1247 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1248 glEnableClientState(GL_VERTEX_ARRAY)
1249 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1251 glColor4ub(5, 171, 231, 64)
1252 glDrawArrays(GL_QUADS, 0, 4)
1253 glColor4ub(5, 171, 231, 96)
1254 glDrawArrays(GL_QUADS, 4, 8)
1255 glColor4ub(5, 171, 231, 128)
1256 glDrawArrays(GL_QUADS, 12, 8)
1257 glDisableClientState(GL_VERTEX_ARRAY)
1259 sx = self._machineSize[0]
1260 sy = self._machineSize[1]
1261 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1262 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1267 x1 = max(min(x1, sx/2), -sx/2)
1268 y1 = max(min(y1, sy/2), -sy/2)
1269 x2 = max(min(x2, sx/2), -sx/2)
1270 y2 = max(min(y2, sy/2), -sy/2)
1271 if (x & 1) == (y & 1):
1272 glColor4ub(5, 171, 231, 127)
1274 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1276 glVertex3f(x1, y1, -0.02)
1277 glVertex3f(x2, y1, -0.02)
1278 glVertex3f(x2, y2, -0.02)
1279 glVertex3f(x1, y2, -0.02)
1283 glDisable(GL_CULL_FACE)
1285 def _generateGCodeVBOs(self, layer):
1287 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1288 if ':' in extrudeType:
1289 extruder = int(extrudeType[extrudeType.find(':')+1:])
1290 extrudeType = extrudeType[0:extrudeType.find(':')]
1293 pointList = numpy.zeros((0,3), numpy.float32)
1295 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1297 a = numpy.concatenate((a[:-1], a[1:]), 1)
1298 a = a.reshape((len(a) * 2, 3))
1299 pointList = numpy.concatenate((pointList, a))
1300 ret.append(opengl.GLVBO(pointList))
1303 def _generateGCodeVBOs2(self, layer):
1304 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1305 filamentArea = math.pi * filamentRadius * filamentRadius
1306 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1309 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1310 if ':' in extrudeType:
1311 extruder = int(extrudeType[extrudeType.find(':')+1:])
1312 extrudeType = extrudeType[0:extrudeType.find(':')]
1315 pointList = numpy.zeros((0,3), numpy.float32)
1317 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1319 if extrudeType == 'FILL':
1322 normal = a[1:] - a[:-1]
1323 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1324 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1327 ePerDist = path['extrusion'][1:] / lens
1329 lineWidth = ePerDist / path['layerThickness'] / 2.0
1331 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1333 normal[:,0] *= lineWidth
1334 normal[:,1] *= lineWidth
1336 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1337 b = numpy.concatenate((b, a[1:] + normal), 1)
1338 b = numpy.concatenate((b, a[1:] - normal), 1)
1339 b = numpy.concatenate((b, a[:-1] - normal), 1)
1340 b = numpy.concatenate((b, a[:-1] + normal), 1)
1341 b = b.reshape((len(b) * 4, 3))
1344 normal2 = normal[:-1] + normal[1:]
1345 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1346 normal2[:,0] /= lens2
1347 normal2[:,1] /= lens2
1348 normal2[:,0] *= lineWidth[:-1]
1349 normal2[:,1] *= lineWidth[:-1]
1351 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1352 c = numpy.concatenate((c, a[1:-1]), 1)
1353 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1354 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1355 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1357 c = numpy.concatenate((c, a[1:-1]), 1)
1358 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1359 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1360 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1362 c = c.reshape((len(c) * 8, 3))
1364 pointList = numpy.concatenate((pointList, b, c))
1366 pointList = numpy.concatenate((pointList, b))
1367 ret.append(opengl.GLVBO(pointList))
1369 pointList = numpy.zeros((0,3), numpy.float32)
1371 if path['type'] == 'move':
1372 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1373 a = numpy.concatenate((a[:-1], a[1:]), 1)
1374 a = a.reshape((len(a) * 2, 3))
1375 pointList = numpy.concatenate((pointList, a))
1376 if path['type'] == 'retract':
1377 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1378 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1379 a = a.reshape((len(a) * 2, 3))
1380 pointList = numpy.concatenate((pointList, a))
1381 ret.append(opengl.GLVBO(pointList))
1385 def getObjectCenterPos(self):
1386 if self._selectedObj is None:
1387 return [0.0, 0.0, 0.0]
1388 pos = self._selectedObj.getPosition()
1389 size = self._selectedObj.getSize()
1390 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1392 def getObjectBoundaryCircle(self):
1393 if self._selectedObj is None:
1395 return self._selectedObj.getBoundaryCircle()
1397 def getObjectSize(self):
1398 if self._selectedObj is None:
1399 return [0.0, 0.0, 0.0]
1400 return self._selectedObj.getSize()
1402 def getObjectMatrix(self):
1403 if self._selectedObj is None:
1404 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1405 return self._selectedObj.getMatrix()
1407 class shaderEditor(wx.Dialog):
1408 def __init__(self, parent, callback, v, f):
1409 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1410 self._callback = callback
1411 s = wx.BoxSizer(wx.VERTICAL)
1413 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1414 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1415 s.Add(self._vertex, 1, flag=wx.EXPAND)
1416 s.Add(self._fragment, 1, flag=wx.EXPAND)
1418 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1419 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1421 self.SetPosition(self.GetParent().GetPosition())
1422 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1425 def OnText(self, e):
1426 self._callback(self._vertex.GetValue(), self._fragment.GetValue())