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):
146 print "load ", filenames
148 for filename in filenames:
149 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
150 ext = filename[filename.rfind('.')+1:].upper()
151 if ext == 'G' or ext == 'GCODE':
152 gcodeFilename = filename
153 if gcodeFilename is not None:
154 self.loadGCodeFile(gcodeFilename)
156 self.loadSceneFiles(filenames)
158 def showLoadModel(self, button = 1):
160 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)
161 dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
162 if dlg.ShowModal() != wx.ID_OK:
165 filenames = dlg.GetPaths()
167 if len(filenames) < 1:
169 profile.putPreference('lastFile', filenames[0])
170 self.loadFiles(filenames)
172 def showSaveModel(self):
173 if len(self._scene.objects()) < 1:
175 dlg=wx.FileDialog(self, _("Save 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
176 dlg.SetWildcard(meshLoader.saveWildcardFilter())
177 if dlg.ShowModal() != wx.ID_OK:
180 filename = dlg.GetPath()
182 meshLoader.saveMeshes(filename, self._scene.objects())
184 def OnPrintButton(self, button):
186 if machineCom.machineIsConnected():
187 self.showPrintWindow()
188 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
189 drives = removableStorage.getPossibleSDcardDrives()
191 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))
192 if dlg.ShowModal() != wx.ID_OK:
195 drive = drives[dlg.GetSelection()]
199 filename = self._scene._objectList[0].getName() + '.gcode'
200 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
205 self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB")))
206 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode...")))
207 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, _("Slice engine log...")))
211 def showPrintWindow(self):
212 if self._gcodeFilename is None:
214 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
215 if self._gcodeFilename == self._slicer.getGCodeFilename():
216 self._slicer.submitSliceInfoOnline()
218 def showSaveGCode(self):
219 defPath = profile.getPreference('lastFile')
220 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
221 dlg=wx.FileDialog(self, _("Save toolpath"), defPath, style=wx.FD_SAVE)
222 dlg.SetFilename(self._scene._objectList[0].getName())
223 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
224 if dlg.ShowModal() != wx.ID_OK:
227 filename = dlg.GetPath()
230 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
232 def _copyFile(self, fileA, fileB, allowEject = False):
234 size = float(os.stat(fileA).st_size)
235 with open(fileA, 'rb') as fsrc:
236 with open(fileB, 'wb') as fdst:
238 buf = fsrc.read(16*1024)
242 self.printButton.setProgressBar(float(fsrc.tell()) / size)
247 self.notification.message("Failed to save")
250 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...'))
252 self.notification.message("Saved as %s" % (fileB))
253 self.printButton.setProgressBar(None)
254 if fileA == self._slicer.getGCodeFilename():
255 self._slicer.submitSliceInfoOnline()
257 def _showSliceLog(self):
258 dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
262 def OnToolSelect(self, button):
263 if self.rotateToolButton.getSelected():
264 self.tool = previewTools.toolRotate(self)
265 elif self.scaleToolButton.getSelected():
266 self.tool = previewTools.toolScale(self)
267 elif self.mirrorToolButton.getSelected():
268 self.tool = previewTools.toolNone(self)
270 self.tool = previewTools.toolNone(self)
271 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
272 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
273 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
274 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
275 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
276 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
277 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
278 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
280 def updateToolButtons(self):
281 if self._selectedObj is None:
285 self.rotateToolButton.setHidden(hidden)
286 self.scaleToolButton.setHidden(hidden)
287 self.mirrorToolButton.setHidden(hidden)
289 self.rotateToolButton.setSelected(False)
290 self.scaleToolButton.setSelected(False)
291 self.mirrorToolButton.setSelected(False)
294 def OnViewChange(self):
295 if self.viewSelection.getValue() == 4:
296 self.viewMode = 'gcode'
297 if self._gcode is not None and self._gcode.layerList is not None:
298 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
299 self._selectObject(None)
300 elif self.viewSelection.getValue() == 1:
301 self.viewMode = 'overhang'
302 elif self.viewSelection.getValue() == 2:
303 self.viewMode = 'transparent'
304 elif self.viewSelection.getValue() == 3:
305 self.viewMode = 'xray'
307 self.viewMode = 'normal'
308 self.layerSelect.setHidden(self.viewMode != 'gcode')
311 def OnRotateReset(self, button):
312 if self._selectedObj is None:
314 self._selectedObj.resetRotation()
315 self._scene.pushFree()
316 self._selectObject(self._selectedObj)
319 def OnLayFlat(self, button):
320 if self._selectedObj is None:
322 self._selectedObj.layFlat()
323 self._scene.pushFree()
324 self._selectObject(self._selectedObj)
327 def OnScaleReset(self, button):
328 if self._selectedObj is None:
330 self._selectedObj.resetScale()
331 self._selectObject(self._selectedObj)
332 self.updateProfileToControls()
335 def OnScaleMax(self, button):
336 if self._selectedObj is None:
338 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
339 self._scene.pushFree()
340 self._selectObject(self._selectedObj)
341 self.updateProfileToControls()
344 def OnMirror(self, axis):
345 if self._selectedObj is None:
347 self._selectedObj.mirror(axis)
350 def OnScaleEntry(self, value, axis):
351 if self._selectedObj is None:
357 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
358 self.updateProfileToControls()
359 self._scene.pushFree()
360 self._selectObject(self._selectedObj)
363 def OnScaleEntryMM(self, value, axis):
364 if self._selectedObj is None:
370 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
371 self.updateProfileToControls()
372 self._scene.pushFree()
373 self._selectObject(self._selectedObj)
376 def OnDeleteAll(self, e):
377 while len(self._scene.objects()) > 0:
378 self._deleteObject(self._scene.objects()[0])
379 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
381 def OnMultiply(self, e):
382 if self._focusObj is None:
385 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
386 if dlg.ShowModal() != wx.ID_OK:
395 self._scene.add(newObj)
396 self._scene.centerAll()
397 if not self._scene.checkPlatform(newObj):
402 self.notification.message("Could not create more then %d items" % (n - 1))
403 self._scene.remove(newObj)
404 self._scene.centerAll()
407 def OnSplitObject(self, e):
408 if self._focusObj is None:
410 self._scene.remove(self._focusObj)
411 for obj in self._focusObj.split(self._splitCallback):
412 if numpy.max(obj.getSize()) > 2.0:
414 self._scene.centerAll()
415 self._selectObject(None)
418 def _splitCallback(self, progress):
421 def OnMergeObjects(self, e):
422 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
423 if len(self._scene.objects()) == 2:
424 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
427 self._scene.merge(self._selectedObj, self._focusObj)
430 def sceneUpdated(self):
431 self._sceneUpdateTimer.Start(500, True)
432 self._slicer.abortSlicer()
433 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
436 def _onRunSlicer(self, e):
437 if self._isSimpleMode:
438 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
439 self._slicer.runSlicer(self._scene)
440 if self._isSimpleMode:
441 profile.resetTempOverride()
443 def _updateSliceProgress(self, progressValue, ready):
445 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
447 self.printButton.setDisabled(not ready)
448 if progressValue >= 0.0:
449 self.printButton.setProgressBar(progressValue)
451 self.printButton.setProgressBar(None)
452 if self._gcode is not None:
454 for layerVBOlist in self._gcodeVBOs:
455 for vbo in layerVBOlist:
456 self.glReleaseList.append(vbo)
459 self.printButton.setProgressBar(None)
460 cost = self._slicer.getFilamentCost()
462 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
464 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
465 self._gcode = gcodeInterpreter.gcode()
466 self._gcodeFilename = self._slicer.getGCodeFilename()
468 self.printButton.setBottomText('')
471 def _loadGCode(self):
472 self._gcode.progressCallback = self._gcodeLoadCallback
473 self._gcode.load(self._gcodeFilename)
475 def _gcodeLoadCallback(self, progress):
476 if self._gcode is None:
478 if len(self._gcode.layerList) % 15 == 0:
480 if self._gcode is None:
482 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
483 if self.viewMode == 'gcode':
487 def loadScene(self, fileList):
488 for filename in fileList:
490 objList = meshLoader.loadMeshes(filename)
492 traceback.print_exc()
495 if self._objectLoadShader is not None:
496 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
500 self._scene.centerAll()
501 self._selectObject(obj)
502 if obj.getScale()[0] < 1.0:
503 self.notification.message("Warning: Object scaled down.")
506 def _deleteObject(self, obj):
507 if obj == self._selectedObj:
508 self._selectObject(None)
509 if obj == self._focusObj:
510 self._focusObj = None
511 self._scene.remove(obj)
512 for m in obj._meshList:
513 if m.vbo is not None and m.vbo.decRef():
514 self.glReleaseList.append(m.vbo)
519 def _selectObject(self, obj, zoom = True):
520 if obj != self._selectedObj:
521 self._selectedObj = obj
522 self.updateProfileToControls()
523 self.updateToolButtons()
524 if zoom and obj is not None:
525 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
526 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
527 newZoom = obj.getBoundaryCircle() * 6
528 if newZoom > numpy.max(self._machineSize) * 3:
529 newZoom = numpy.max(self._machineSize) * 3
530 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
532 def updateProfileToControls(self):
533 oldSimpleMode = self._isSimpleMode
534 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
535 if self._isSimpleMode != oldSimpleMode:
536 self._scene.arrangeAll()
538 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
539 self._objColors[0] = profile.getPreferenceColour('model_colour')
540 self._objColors[1] = profile.getPreferenceColour('model_colour2')
541 self._objColors[2] = profile.getPreferenceColour('model_colour3')
542 self._objColors[3] = profile.getPreferenceColour('model_colour4')
543 self._scene.setMachineSize(self._machineSize)
544 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
545 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'))
547 if self._selectedObj is not None:
548 scale = self._selectedObj.getScale()
549 size = self._selectedObj.getSize()
550 self.scaleXctrl.setValue(round(scale[0], 2))
551 self.scaleYctrl.setValue(round(scale[1], 2))
552 self.scaleZctrl.setValue(round(scale[2], 2))
553 self.scaleXmmctrl.setValue(round(size[0], 2))
554 self.scaleYmmctrl.setValue(round(size[1], 2))
555 self.scaleZmmctrl.setValue(round(size[2], 2))
557 def OnKeyChar(self, keyCode):
558 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
559 if self._selectedObj is not None:
560 self._deleteObject(self._selectedObj)
562 if keyCode == wx.WXK_UP:
563 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
565 elif keyCode == wx.WXK_DOWN:
566 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
568 elif keyCode == wx.WXK_PAGEUP:
569 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
571 elif keyCode == wx.WXK_PAGEDOWN:
572 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
575 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
576 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
577 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
578 from collections import defaultdict
579 from gc import get_objects
580 self._beforeLeakTest = defaultdict(int)
581 for i in get_objects():
582 self._beforeLeakTest[type(i)] += 1
583 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
584 from collections import defaultdict
585 from gc import get_objects
586 self._afterLeakTest = defaultdict(int)
587 for i in get_objects():
588 self._afterLeakTest[type(i)] += 1
589 for k in self._afterLeakTest:
590 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
591 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
593 def ShaderUpdate(self, v, f):
594 s = opengl.GLShader(v, f)
596 self._objectLoadShader.release()
597 self._objectLoadShader = s
598 for obj in self._scene.objects():
599 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
602 def OnMouseDown(self,e):
603 self._mouseX = e.GetX()
604 self._mouseY = e.GetY()
605 self._mouseClick3DPos = self._mouse3Dpos
606 self._mouseClickFocus = self._focusObj
608 self._mouseState = 'doubleClick'
610 self._mouseState = 'dragOrClick'
611 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
612 p0 -= self.getObjectCenterPos() - self._viewTarget
613 p1 -= self.getObjectCenterPos() - self._viewTarget
614 if self.tool.OnDragStart(p0, p1):
615 self._mouseState = 'tool'
616 if self._mouseState == 'dragOrClick':
617 if e.GetButton() == 1:
618 if self._focusObj is not None:
619 self._selectObject(self._focusObj, False)
622 def OnMouseUp(self, e):
623 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
625 if self._mouseState == 'dragOrClick':
626 if e.GetButton() == 1:
627 self._selectObject(self._focusObj)
628 if e.GetButton() == 3:
630 if self._focusObj is not None:
631 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete")))
632 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply")))
633 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split")))
634 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:
635 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
636 if len(self._scene.objects()) > 0:
637 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all")))
638 if menu.MenuItemCount > 0:
641 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
642 self._scene.pushFree()
644 elif self._mouseState == 'tool':
645 if self.tempMatrix is not None and self._selectedObj is not None:
646 self._selectedObj.applyMatrix(self.tempMatrix)
647 self._scene.pushFree()
648 self._selectObject(self._selectedObj)
649 self.tempMatrix = None
650 self.tool.OnDragEnd()
652 self._mouseState = None
654 def OnMouseMotion(self,e):
655 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
656 p0 -= self.getObjectCenterPos() - self._viewTarget
657 p1 -= self.getObjectCenterPos() - self._viewTarget
659 if e.Dragging() and self._mouseState is not None:
660 if self._mouseState == 'tool':
661 self.tool.OnDrag(p0, p1)
662 elif not e.LeftIsDown() and e.RightIsDown():
663 self._mouseState = 'drag'
664 if wx.GetKeyState(wx.WXK_SHIFT):
665 a = math.cos(math.radians(self._yaw)) / 3.0
666 b = math.sin(math.radians(self._yaw)) / 3.0
667 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
668 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
669 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
670 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
672 self._yaw += e.GetX() - self._mouseX
673 self._pitch -= e.GetY() - self._mouseY
674 if self._pitch > 170:
678 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
679 self._mouseState = 'drag'
680 self._zoom += e.GetY() - self._mouseY
683 if self._zoom > numpy.max(self._machineSize) * 3:
684 self._zoom = numpy.max(self._machineSize) * 3
685 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
686 self._mouseState = 'dragObject'
687 z = max(0, self._mouseClick3DPos[2])
688 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
689 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
694 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
695 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
696 diff = cursorZ1 - cursorZ0
697 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
698 if not e.Dragging() or self._mouseState != 'tool':
699 self.tool.OnMouseMove(p0, p1)
701 self._mouseX = e.GetX()
702 self._mouseY = e.GetY()
704 def OnMouseWheel(self, e):
705 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
706 delta = max(min(delta,4),-4)
707 self._zoom *= 1.0 - delta / 10.0
710 if self._zoom > numpy.max(self._machineSize) * 3:
711 self._zoom = numpy.max(self._machineSize) * 3
714 def OnMouseLeave(self, e):
718 def getMouseRay(self, x, y):
719 if self._viewport is None:
720 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
721 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
722 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
723 p0 -= self._viewTarget
724 p1 -= self._viewTarget
727 def _init3DView(self):
728 # set viewing projection
729 size = self.GetSize()
730 glViewport(0, 0, size.GetWidth(), size.GetHeight())
733 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
735 glDisable(GL_RESCALE_NORMAL)
736 glDisable(GL_LIGHTING)
738 glEnable(GL_DEPTH_TEST)
739 glDisable(GL_CULL_FACE)
741 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
743 glClearColor(0.8, 0.8, 0.8, 1.0)
747 glMatrixMode(GL_PROJECTION)
749 aspect = float(size.GetWidth()) / float(size.GetHeight())
750 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
752 glMatrixMode(GL_MODELVIEW)
754 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
757 if machineCom.machineIsConnected():
758 self.printButton._imageID = 6
759 self.printButton._tooltip = _("Print")
760 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
761 self.printButton._imageID = 2
762 self.printButton._tooltip = _("Toolpath to SD")
764 self.printButton._imageID = 3
765 self.printButton._tooltip = _("Save toolpath")
767 if self._animView is not None:
768 self._viewTarget = self._animView.getPosition()
769 if self._animView.isDone():
770 self._animView = None
771 if self._animZoom is not None:
772 self._zoom = self._animZoom.getPosition()
773 if self._animZoom.isDone():
774 self._animZoom = None
775 if self.viewMode == 'gcode' and self._gcode is not None:
777 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
780 if self._objectShader is None:
781 if opengl.hasShaderSupport():
782 self._objectShader = opengl.GLShader("""
783 varying float light_amount;
787 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
788 gl_FrontColor = gl_Color;
790 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
794 varying float light_amount;
798 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
801 self._objectOverhangShader = opengl.GLShader("""
802 uniform float cosAngle;
803 uniform mat3 rotMatrix;
804 varying float light_amount;
808 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
809 gl_FrontColor = gl_Color;
811 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
813 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
815 light_amount = -10.0;
819 varying float light_amount;
823 if (light_amount == -10.0)
825 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
827 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
831 self._objectLoadShader = opengl.GLShader("""
832 uniform float intensity;
834 varying float light_amount;
838 vec4 tmp = gl_Vertex;
839 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
840 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
841 gl_Position = gl_ModelViewProjectionMatrix * tmp;
842 gl_FrontColor = gl_Color;
844 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
848 uniform float intensity;
849 varying float light_amount;
853 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
856 if self._objectShader == None or not self._objectShader.isValid():
857 self._objectShader = opengl.GLFakeShader()
858 self._objectOverhangShader = opengl.GLFakeShader()
859 self._objectLoadShader = None
861 glTranslate(0,0,-self._zoom)
862 glRotate(-self._pitch, 1,0,0)
863 glRotate(self._yaw, 0,0,1)
864 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
866 self._viewport = glGetIntegerv(GL_VIEWPORT)
867 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
868 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
870 glClearColor(1,1,1,1)
871 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
873 if self.viewMode != 'gcode':
874 for n in xrange(0, len(self._scene.objects())):
875 obj = self._scene.objects()[n]
876 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
877 self._renderObject(obj)
879 if self._mouseX > -1:
881 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
882 if n < len(self._scene.objects()):
883 self._focusObj = self._scene.objects()[n]
885 self._focusObj = None
886 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
887 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
888 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
889 self._mouse3Dpos -= self._viewTarget
892 glTranslate(0,0,-self._zoom)
893 glRotate(-self._pitch, 1,0,0)
894 glRotate(self._yaw, 0,0,1)
895 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
897 if self.viewMode == 'gcode':
898 if self._gcode is not None and self._gcode.layerList is None:
899 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
900 self._gcodeLoadThread.daemon = True
901 self._gcodeLoadThread.start()
902 if self._gcode is not None and self._gcode.layerList is not None:
904 if profile.getMachineSetting('machine_center_is_zero') != 'True':
905 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
907 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
908 for n in xrange(0, drawUpTill):
909 c = 1.0 - float(drawUpTill - n) / 15
911 if len(self._gcodeVBOs) < n + 1:
912 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
913 if time.time() - t > 0.5:
916 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
917 if n == drawUpTill - 1:
918 if len(self._gcodeVBOs[n]) < 9:
919 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
921 self._gcodeVBOs[n][8].render(GL_QUADS)
923 self._gcodeVBOs[n][9].render(GL_QUADS)
925 self._gcodeVBOs[n][10].render(GL_QUADS)
927 self._gcodeVBOs[n][11].render(GL_QUADS)
930 self._gcodeVBOs[n][12].render(GL_QUADS)
931 glColor3f(c/2, c/2, 0.0)
932 self._gcodeVBOs[n][13].render(GL_QUADS)
934 self._gcodeVBOs[n][14].render(GL_QUADS)
935 self._gcodeVBOs[n][15].render(GL_QUADS)
937 self._gcodeVBOs[n][16].render(GL_LINES)
940 self._gcodeVBOs[n][0].render(GL_LINES)
942 self._gcodeVBOs[n][1].render(GL_LINES)
944 self._gcodeVBOs[n][2].render(GL_LINES)
946 self._gcodeVBOs[n][3].render(GL_LINES)
949 self._gcodeVBOs[n][4].render(GL_LINES)
950 glColor3f(c/2, c/2, 0.0)
951 self._gcodeVBOs[n][5].render(GL_LINES)
953 self._gcodeVBOs[n][6].render(GL_LINES)
954 self._gcodeVBOs[n][7].render(GL_LINES)
957 glStencilFunc(GL_ALWAYS, 1, 1)
958 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
960 if self.viewMode == 'overhang':
961 self._objectOverhangShader.bind()
962 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
964 self._objectShader.bind()
965 for obj in self._scene.objects():
966 if obj._loadAnim is not None:
967 if obj._loadAnim.isDone():
972 if self._focusObj == obj:
974 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
977 if self._selectedObj == obj or self._selectedObj is None:
978 #If we want transparent, then first render a solid black model to remove the printer size lines.
979 if self.viewMode == 'transparent':
980 glColor4f(0, 0, 0, 0)
981 self._renderObject(obj)
983 glBlendFunc(GL_ONE, GL_ONE)
984 glDisable(GL_DEPTH_TEST)
986 if self.viewMode == 'xray':
987 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
988 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
989 glEnable(GL_STENCIL_TEST)
991 if self.viewMode == 'overhang':
992 if self._selectedObj == obj and self.tempMatrix is not None:
993 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
995 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
997 if not self._scene.checkPlatform(obj):
998 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
999 self._renderObject(obj)
1001 self._renderObject(obj, brightness)
1002 glDisable(GL_STENCIL_TEST)
1004 glEnable(GL_DEPTH_TEST)
1005 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1007 if self.viewMode == 'xray':
1010 glEnable(GL_STENCIL_TEST)
1011 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1012 glDisable(GL_DEPTH_TEST)
1013 for i in xrange(2, 15, 2):
1014 glStencilFunc(GL_EQUAL, i, 0xFF)
1015 glColor(float(i)/10, float(i)/10, float(i)/5)
1017 glVertex3f(-1000,-1000,-10)
1018 glVertex3f( 1000,-1000,-10)
1019 glVertex3f( 1000, 1000,-10)
1020 glVertex3f(-1000, 1000,-10)
1022 for i in xrange(1, 15, 2):
1023 glStencilFunc(GL_EQUAL, i, 0xFF)
1024 glColor(float(i)/10, 0, 0)
1026 glVertex3f(-1000,-1000,-10)
1027 glVertex3f( 1000,-1000,-10)
1028 glVertex3f( 1000, 1000,-10)
1029 glVertex3f(-1000, 1000,-10)
1032 glDisable(GL_STENCIL_TEST)
1033 glEnable(GL_DEPTH_TEST)
1035 self._objectShader.unbind()
1037 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1039 if self._objectLoadShader is not None:
1040 self._objectLoadShader.bind()
1041 glColor4f(0.2, 0.6, 1.0, 1.0)
1042 for obj in self._scene.objects():
1043 if obj._loadAnim is None:
1045 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1046 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1047 self._renderObject(obj)
1048 self._objectLoadShader.unbind()
1053 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1055 z = self._usbPrintMonitor.getZ()
1056 size = self._machineSize
1057 glColor4ub(255,255,0,128)
1059 glVertex3f(-size[0]/2,-size[1]/2, z)
1060 glVertex3f( size[0]/2,-size[1]/2, z)
1061 glVertex3f( size[0]/2, size[1]/2, z)
1062 glVertex3f(-size[0]/2, size[1]/2, z)
1065 if self.viewMode == 'gcode':
1066 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1067 glDisable(GL_DEPTH_TEST)
1070 glTranslate(0,-4,-10)
1071 glColor4ub(60,60,60,255)
1072 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1075 #Draw the object box-shadow, so you can see where it will collide with other objects.
1076 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1077 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1079 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1081 glEnable(GL_CULL_FACE)
1082 glColor4f(0,0,0,0.12)
1084 glVertex3f(-size[0], size[1], 0.1)
1085 glVertex3f(-size[0], -size[1], 0.1)
1086 glVertex3f( size[0], -size[1], 0.1)
1087 glVertex3f( size[0], size[1], 0.1)
1089 glDisable(GL_CULL_FACE)
1092 #Draw the outline of the selected object, on top of everything else except the GUI.
1093 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1094 glDisable(GL_DEPTH_TEST)
1095 glEnable(GL_CULL_FACE)
1096 glEnable(GL_STENCIL_TEST)
1098 glStencilFunc(GL_EQUAL, 0, 255)
1100 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1102 glColor4f(1,1,1,0.5)
1103 self._renderObject(self._selectedObj)
1104 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1106 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1107 glDisable(GL_STENCIL_TEST)
1108 glDisable(GL_CULL_FACE)
1109 glEnable(GL_DEPTH_TEST)
1111 if self._selectedObj is not None:
1113 pos = self.getObjectCenterPos()
1114 glTranslate(pos[0], pos[1], pos[2])
1117 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1118 glDisable(GL_DEPTH_TEST)
1121 glTranslate(0,-4,-10)
1122 glColor4ub(60,60,60,255)
1123 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1126 def _renderObject(self, obj, brightness = False, addSink = True):
1129 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1131 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1133 if self.tempMatrix is not None and obj == self._selectedObj:
1134 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1135 glMultMatrixf(tempMatrix)
1137 offset = obj.getDrawOffset()
1138 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1140 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1141 glMultMatrixf(tempMatrix)
1144 for m in obj._meshList:
1146 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1148 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1153 def _drawMachine(self):
1154 glEnable(GL_CULL_FACE)
1157 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1159 machine = profile.getMachineSetting('machine_type')
1160 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
1161 if machine not in self._platformMesh:
1162 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1164 self._platformMesh[machine] = meshes[0]
1166 self._platformMesh[machine] = None
1167 if profile.getMachineSetting('machine_type') == 'ultimaker2':
1168 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1170 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1171 glColor4f(1,1,1,0.5)
1172 self._objectShader.bind()
1173 self._renderObject(self._platformMesh[machine], False, False)
1174 self._objectShader.unbind()
1179 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1180 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1181 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1182 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1183 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1184 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1187 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1188 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1189 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1190 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1191 v4 = [ size[0] / 2, size[1] / 2, 0]
1192 v5 = [ size[0] / 2,-size[1] / 2, 0]
1193 v6 = [-size[0] / 2, size[1] / 2, 0]
1194 v7 = [-size[0] / 2,-size[1] / 2, 0]
1196 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1197 glEnableClientState(GL_VERTEX_ARRAY)
1198 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1200 glColor4ub(5, 171, 231, 64)
1201 glDrawArrays(GL_QUADS, 0, 4)
1202 glColor4ub(5, 171, 231, 96)
1203 glDrawArrays(GL_QUADS, 4, 8)
1204 glColor4ub(5, 171, 231, 128)
1205 glDrawArrays(GL_QUADS, 12, 8)
1206 glDisableClientState(GL_VERTEX_ARRAY)
1208 sx = self._machineSize[0]
1209 sy = self._machineSize[1]
1210 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1211 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1216 x1 = max(min(x1, sx/2), -sx/2)
1217 y1 = max(min(y1, sy/2), -sy/2)
1218 x2 = max(min(x2, sx/2), -sx/2)
1219 y2 = max(min(y2, sy/2), -sy/2)
1220 if (x & 1) == (y & 1):
1221 glColor4ub(5, 171, 231, 127)
1223 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1225 glVertex3f(x1, y1, -0.02)
1226 glVertex3f(x2, y1, -0.02)
1227 glVertex3f(x2, y2, -0.02)
1228 glVertex3f(x1, y2, -0.02)
1232 glDisable(GL_CULL_FACE)
1234 def _generateGCodeVBOs(self, layer):
1236 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1237 if ':' in extrudeType:
1238 extruder = int(extrudeType[extrudeType.find(':')+1:])
1239 extrudeType = extrudeType[0:extrudeType.find(':')]
1242 pointList = numpy.zeros((0,3), numpy.float32)
1244 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1246 a = numpy.concatenate((a[:-1], a[1:]), 1)
1247 a = a.reshape((len(a) * 2, 3))
1248 pointList = numpy.concatenate((pointList, a))
1249 ret.append(opengl.GLVBO(pointList))
1252 def _generateGCodeVBOs2(self, layer):
1253 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1254 filamentArea = math.pi * filamentRadius * filamentRadius
1255 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1258 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1259 if ':' in extrudeType:
1260 extruder = int(extrudeType[extrudeType.find(':')+1:])
1261 extrudeType = extrudeType[0:extrudeType.find(':')]
1264 pointList = numpy.zeros((0,3), numpy.float32)
1266 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1268 if extrudeType == 'FILL':
1271 normal = a[1:] - a[:-1]
1272 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1273 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1276 ePerDist = path['extrusion'][1:] / lens
1278 lineWidth = ePerDist / path['layerThickness'] / 2.0
1280 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1282 normal[:,0] *= lineWidth
1283 normal[:,1] *= lineWidth
1285 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1286 b = numpy.concatenate((b, a[1:] + normal), 1)
1287 b = numpy.concatenate((b, a[1:] - normal), 1)
1288 b = numpy.concatenate((b, a[:-1] - normal), 1)
1289 b = numpy.concatenate((b, a[:-1] + normal), 1)
1290 b = b.reshape((len(b) * 4, 3))
1293 normal2 = normal[:-1] + normal[1:]
1294 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1295 normal2[:,0] /= lens2
1296 normal2[:,1] /= lens2
1297 normal2[:,0] *= lineWidth[:-1]
1298 normal2[:,1] *= lineWidth[:-1]
1300 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1301 c = numpy.concatenate((c, a[1:-1]), 1)
1302 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1303 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1304 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1306 c = numpy.concatenate((c, a[1:-1]), 1)
1307 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1308 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1309 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1311 c = c.reshape((len(c) * 8, 3))
1313 pointList = numpy.concatenate((pointList, b, c))
1315 pointList = numpy.concatenate((pointList, b))
1316 ret.append(opengl.GLVBO(pointList))
1318 pointList = numpy.zeros((0,3), numpy.float32)
1320 if path['type'] == 'move':
1321 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1322 a = numpy.concatenate((a[:-1], a[1:]), 1)
1323 a = a.reshape((len(a) * 2, 3))
1324 pointList = numpy.concatenate((pointList, a))
1325 if path['type'] == 'retract':
1326 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1327 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1328 a = a.reshape((len(a) * 2, 3))
1329 pointList = numpy.concatenate((pointList, a))
1330 ret.append(opengl.GLVBO(pointList))
1334 def getObjectCenterPos(self):
1335 if self._selectedObj is None:
1336 return [0.0, 0.0, 0.0]
1337 pos = self._selectedObj.getPosition()
1338 size = self._selectedObj.getSize()
1339 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1341 def getObjectBoundaryCircle(self):
1342 if self._selectedObj is None:
1344 return self._selectedObj.getBoundaryCircle()
1346 def getObjectSize(self):
1347 if self._selectedObj is None:
1348 return [0.0, 0.0, 0.0]
1349 return self._selectedObj.getSize()
1351 def getObjectMatrix(self):
1352 if self._selectedObj is None:
1353 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1354 return self._selectedObj.getMatrix()
1356 class shaderEditor(wx.Dialog):
1357 def __init__(self, parent, callback, v, f):
1358 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1359 self._callback = callback
1360 s = wx.BoxSizer(wx.VERTICAL)
1362 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1363 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1364 s.Add(self._vertex, 1, flag=wx.EXPAND)
1365 s.Add(self._fragment, 1, flag=wx.EXPAND)
1367 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1368 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1370 self.SetPosition(self.GetParent().GetPosition())
1371 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1374 def OnText(self, e):
1375 self._callback(self._vertex.GetValue(), self._fragment.GetValue())