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 showLoadModel(self, button = 1):
124 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)
125 dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
126 if dlg.ShowModal() != wx.ID_OK:
129 filenames = dlg.GetPaths()
131 if len(filenames) < 1:
133 profile.putPreference('lastFile', filenames[0])
135 for filename in filenames:
136 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
137 ext = filename[filename.rfind('.')+1:].upper()
138 if ext == 'G' or ext == 'GCODE':
139 gcodeFilename = filename
140 if gcodeFilename is not None:
141 if self._gcode is not None:
143 for layerVBOlist in self._gcodeVBOs:
144 for vbo in layerVBOlist:
145 self.glReleaseList.append(vbo)
147 self._gcode = gcodeInterpreter.gcode()
148 self._gcodeFilename = gcodeFilename
149 self.printButton.setBottomText('')
150 self.viewSelection.setValue(4)
151 self.printButton.setDisabled(False)
152 self.youMagineButton.setDisabled(True)
155 self.youMagineButton.setDisabled(False)
156 if self.viewSelection.getValue() == 4:
157 self.viewSelection.setValue(0)
159 self.loadScene(filenames)
161 def showSaveModel(self):
162 if len(self._scene.objects()) < 1:
164 dlg=wx.FileDialog(self, _("Save 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
165 dlg.SetWildcard(meshLoader.saveWildcardFilter())
166 if dlg.ShowModal() != wx.ID_OK:
169 filename = dlg.GetPath()
171 meshLoader.saveMeshes(filename, self._scene.objects())
173 def OnPrintButton(self, button):
175 if machineCom.machineIsConnected():
176 self.showPrintWindow()
177 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
178 drives = removableStorage.getPossibleSDcardDrives()
180 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))
181 if dlg.ShowModal() != wx.ID_OK:
184 drive = drives[dlg.GetSelection()]
188 filename = self._scene._objectList[0].getName() + '.gcode'
189 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
194 self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB")))
195 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode...")))
196 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, _("Slice engine log...")))
200 def showPrintWindow(self):
201 if self._gcodeFilename is None:
203 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
204 if self._gcodeFilename == self._slicer.getGCodeFilename():
205 self._slicer.submitSliceInfoOnline()
207 def showSaveGCode(self):
208 defPath = profile.getPreference('lastFile')
209 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
210 dlg=wx.FileDialog(self, _("Save toolpath"), defPath, style=wx.FD_SAVE)
211 dlg.SetFilename(self._scene._objectList[0].getName())
212 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
213 if dlg.ShowModal() != wx.ID_OK:
216 filename = dlg.GetPath()
219 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
221 def _copyFile(self, fileA, fileB, allowEject = False):
223 size = float(os.stat(fileA).st_size)
224 with open(fileA, 'rb') as fsrc:
225 with open(fileB, 'wb') as fdst:
227 buf = fsrc.read(16*1024)
231 self.printButton.setProgressBar(float(fsrc.tell()) / size)
236 self.notification.message("Failed to save")
239 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...'))
241 self.notification.message("Saved as %s" % (fileB))
242 self.printButton.setProgressBar(None)
243 if fileA == self._slicer.getGCodeFilename():
244 self._slicer.submitSliceInfoOnline()
246 def _showSliceLog(self):
247 dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
251 def OnToolSelect(self, button):
252 if self.rotateToolButton.getSelected():
253 self.tool = previewTools.toolRotate(self)
254 elif self.scaleToolButton.getSelected():
255 self.tool = previewTools.toolScale(self)
256 elif self.mirrorToolButton.getSelected():
257 self.tool = previewTools.toolNone(self)
259 self.tool = previewTools.toolNone(self)
260 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
261 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
262 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
263 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
264 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
265 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
266 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
267 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
269 def updateToolButtons(self):
270 if self._selectedObj is None:
274 self.rotateToolButton.setHidden(hidden)
275 self.scaleToolButton.setHidden(hidden)
276 self.mirrorToolButton.setHidden(hidden)
278 self.rotateToolButton.setSelected(False)
279 self.scaleToolButton.setSelected(False)
280 self.mirrorToolButton.setSelected(False)
283 def OnViewChange(self):
284 if self.viewSelection.getValue() == 4:
285 self.viewMode = 'gcode'
286 if self._gcode is not None and self._gcode.layerList is not None:
287 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
288 self._selectObject(None)
289 elif self.viewSelection.getValue() == 1:
290 self.viewMode = 'overhang'
291 elif self.viewSelection.getValue() == 2:
292 self.viewMode = 'transparent'
293 elif self.viewSelection.getValue() == 3:
294 self.viewMode = 'xray'
296 self.viewMode = 'normal'
297 self.layerSelect.setHidden(self.viewMode != 'gcode')
300 def OnRotateReset(self, button):
301 if self._selectedObj is None:
303 self._selectedObj.resetRotation()
304 self._scene.pushFree()
305 self._selectObject(self._selectedObj)
308 def OnLayFlat(self, button):
309 if self._selectedObj is None:
311 self._selectedObj.layFlat()
312 self._scene.pushFree()
313 self._selectObject(self._selectedObj)
316 def OnScaleReset(self, button):
317 if self._selectedObj is None:
319 self._selectedObj.resetScale()
320 self._selectObject(self._selectedObj)
321 self.updateProfileToControls()
324 def OnScaleMax(self, button):
325 if self._selectedObj is None:
327 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
328 self._scene.pushFree()
329 self._selectObject(self._selectedObj)
330 self.updateProfileToControls()
333 def OnMirror(self, axis):
334 if self._selectedObj is None:
336 self._selectedObj.mirror(axis)
339 def OnScaleEntry(self, value, axis):
340 if self._selectedObj is None:
346 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
347 self.updateProfileToControls()
348 self._scene.pushFree()
349 self._selectObject(self._selectedObj)
352 def OnScaleEntryMM(self, value, axis):
353 if self._selectedObj is None:
359 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
360 self.updateProfileToControls()
361 self._scene.pushFree()
362 self._selectObject(self._selectedObj)
365 def OnDeleteAll(self, e):
366 while len(self._scene.objects()) > 0:
367 self._deleteObject(self._scene.objects()[0])
368 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
370 def OnMultiply(self, e):
371 if self._focusObj is None:
374 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
375 if dlg.ShowModal() != wx.ID_OK:
384 self._scene.add(newObj)
385 self._scene.centerAll()
386 if not self._scene.checkPlatform(newObj):
391 self.notification.message("Could not create more then %d items" % (n - 1))
392 self._scene.remove(newObj)
393 self._scene.centerAll()
396 def OnSplitObject(self, e):
397 if self._focusObj is None:
399 self._scene.remove(self._focusObj)
400 for obj in self._focusObj.split(self._splitCallback):
401 if numpy.max(obj.getSize()) > 2.0:
403 self._scene.centerAll()
404 self._selectObject(None)
407 def _splitCallback(self, progress):
410 def OnMergeObjects(self, e):
411 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
412 if len(self._scene.objects()) == 2:
413 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
416 self._scene.merge(self._selectedObj, self._focusObj)
419 def sceneUpdated(self):
420 self._sceneUpdateTimer.Start(500, True)
421 self._slicer.abortSlicer()
422 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
425 def _onRunSlicer(self, e):
426 if self._isSimpleMode:
427 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
428 self._slicer.runSlicer(self._scene)
429 if self._isSimpleMode:
430 profile.resetTempOverride()
432 def _updateSliceProgress(self, progressValue, ready):
434 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
436 self.printButton.setDisabled(not ready)
437 if progressValue >= 0.0:
438 self.printButton.setProgressBar(progressValue)
440 self.printButton.setProgressBar(None)
441 if self._gcode is not None:
443 for layerVBOlist in self._gcodeVBOs:
444 for vbo in layerVBOlist:
445 self.glReleaseList.append(vbo)
448 self.printButton.setProgressBar(None)
449 cost = self._slicer.getFilamentCost()
451 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
453 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
454 self._gcode = gcodeInterpreter.gcode()
455 self._gcodeFilename = self._slicer.getGCodeFilename()
457 self.printButton.setBottomText('')
460 def _loadGCode(self):
461 self._gcode.progressCallback = self._gcodeLoadCallback
462 self._gcode.load(self._gcodeFilename)
464 def _gcodeLoadCallback(self, progress):
465 if self._gcode is None:
467 if len(self._gcode.layerList) % 15 == 0:
469 if self._gcode is None:
471 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
472 if self.viewMode == 'gcode':
476 def loadScene(self, fileList):
477 for filename in fileList:
479 objList = meshLoader.loadMeshes(filename)
481 traceback.print_exc()
484 if self._objectLoadShader is not None:
485 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
489 self._scene.centerAll()
490 self._selectObject(obj)
491 if obj.getScale()[0] < 1.0:
492 self.notification.message("Warning: Object scaled down.")
495 def _deleteObject(self, obj):
496 if obj == self._selectedObj:
497 self._selectObject(None)
498 if obj == self._focusObj:
499 self._focusObj = None
500 self._scene.remove(obj)
501 for m in obj._meshList:
502 if m.vbo is not None and m.vbo.decRef():
503 self.glReleaseList.append(m.vbo)
508 def _selectObject(self, obj, zoom = True):
509 if obj != self._selectedObj:
510 self._selectedObj = obj
511 self.updateProfileToControls()
512 self.updateToolButtons()
513 if zoom and obj is not None:
514 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
515 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
516 newZoom = obj.getBoundaryCircle() * 6
517 if newZoom > numpy.max(self._machineSize) * 3:
518 newZoom = numpy.max(self._machineSize) * 3
519 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
521 def updateProfileToControls(self):
522 oldSimpleMode = self._isSimpleMode
523 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
524 if self._isSimpleMode != oldSimpleMode:
525 self._scene.arrangeAll()
527 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
528 self._objColors[0] = profile.getPreferenceColour('model_colour')
529 self._objColors[1] = profile.getPreferenceColour('model_colour2')
530 self._objColors[2] = profile.getPreferenceColour('model_colour3')
531 self._objColors[3] = profile.getPreferenceColour('model_colour4')
532 self._scene.setMachineSize(self._machineSize)
533 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
534 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'))
536 if self._selectedObj is not None:
537 scale = self._selectedObj.getScale()
538 size = self._selectedObj.getSize()
539 self.scaleXctrl.setValue(round(scale[0], 2))
540 self.scaleYctrl.setValue(round(scale[1], 2))
541 self.scaleZctrl.setValue(round(scale[2], 2))
542 self.scaleXmmctrl.setValue(round(size[0], 2))
543 self.scaleYmmctrl.setValue(round(size[1], 2))
544 self.scaleZmmctrl.setValue(round(size[2], 2))
546 def OnKeyChar(self, keyCode):
547 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
548 if self._selectedObj is not None:
549 self._deleteObject(self._selectedObj)
551 if keyCode == wx.WXK_UP:
552 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
554 elif keyCode == wx.WXK_DOWN:
555 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
557 elif keyCode == wx.WXK_PAGEUP:
558 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
560 elif keyCode == wx.WXK_PAGEDOWN:
561 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
564 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
565 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
566 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
567 from collections import defaultdict
568 from gc import get_objects
569 self._beforeLeakTest = defaultdict(int)
570 for i in get_objects():
571 self._beforeLeakTest[type(i)] += 1
572 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
573 from collections import defaultdict
574 from gc import get_objects
575 self._afterLeakTest = defaultdict(int)
576 for i in get_objects():
577 self._afterLeakTest[type(i)] += 1
578 for k in self._afterLeakTest:
579 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
580 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
582 def ShaderUpdate(self, v, f):
583 s = opengl.GLShader(v, f)
585 self._objectLoadShader.release()
586 self._objectLoadShader = s
587 for obj in self._scene.objects():
588 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
591 def OnMouseDown(self,e):
592 self._mouseX = e.GetX()
593 self._mouseY = e.GetY()
594 self._mouseClick3DPos = self._mouse3Dpos
595 self._mouseClickFocus = self._focusObj
597 self._mouseState = 'doubleClick'
599 self._mouseState = 'dragOrClick'
600 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
601 p0 -= self.getObjectCenterPos() - self._viewTarget
602 p1 -= self.getObjectCenterPos() - self._viewTarget
603 if self.tool.OnDragStart(p0, p1):
604 self._mouseState = 'tool'
605 if self._mouseState == 'dragOrClick':
606 if e.GetButton() == 1:
607 if self._focusObj is not None:
608 self._selectObject(self._focusObj, False)
611 def OnMouseUp(self, e):
612 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
614 if self._mouseState == 'dragOrClick':
615 if e.GetButton() == 1:
616 self._selectObject(self._focusObj)
617 if e.GetButton() == 3:
619 if self._focusObj is not None:
620 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete")))
621 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply")))
622 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split")))
623 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:
624 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
625 if len(self._scene.objects()) > 0:
626 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all")))
627 if menu.MenuItemCount > 0:
630 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
631 self._scene.pushFree()
633 elif self._mouseState == 'tool':
634 if self.tempMatrix is not None and self._selectedObj is not None:
635 self._selectedObj.applyMatrix(self.tempMatrix)
636 self._scene.pushFree()
637 self._selectObject(self._selectedObj)
638 self.tempMatrix = None
639 self.tool.OnDragEnd()
641 self._mouseState = None
643 def OnMouseMotion(self,e):
644 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
645 p0 -= self.getObjectCenterPos() - self._viewTarget
646 p1 -= self.getObjectCenterPos() - self._viewTarget
648 if e.Dragging() and self._mouseState is not None:
649 if self._mouseState == 'tool':
650 self.tool.OnDrag(p0, p1)
651 elif not e.LeftIsDown() and e.RightIsDown():
652 self._mouseState = 'drag'
653 if wx.GetKeyState(wx.WXK_SHIFT):
654 a = math.cos(math.radians(self._yaw)) / 3.0
655 b = math.sin(math.radians(self._yaw)) / 3.0
656 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
657 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
658 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
659 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
661 self._yaw += e.GetX() - self._mouseX
662 self._pitch -= e.GetY() - self._mouseY
663 if self._pitch > 170:
667 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
668 self._mouseState = 'drag'
669 self._zoom += e.GetY() - self._mouseY
672 if self._zoom > numpy.max(self._machineSize) * 3:
673 self._zoom = numpy.max(self._machineSize) * 3
674 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
675 self._mouseState = 'dragObject'
676 z = max(0, self._mouseClick3DPos[2])
677 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
678 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
683 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
684 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
685 diff = cursorZ1 - cursorZ0
686 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
687 if not e.Dragging() or self._mouseState != 'tool':
688 self.tool.OnMouseMove(p0, p1)
690 self._mouseX = e.GetX()
691 self._mouseY = e.GetY()
693 def OnMouseWheel(self, e):
694 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
695 delta = max(min(delta,4),-4)
696 self._zoom *= 1.0 - delta / 10.0
699 if self._zoom > numpy.max(self._machineSize) * 3:
700 self._zoom = numpy.max(self._machineSize) * 3
703 def OnMouseLeave(self, e):
707 def getMouseRay(self, x, y):
708 if self._viewport is None:
709 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
710 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
711 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
712 p0 -= self._viewTarget
713 p1 -= self._viewTarget
716 def _init3DView(self):
717 # set viewing projection
718 size = self.GetSize()
719 glViewport(0, 0, size.GetWidth(), size.GetHeight())
722 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
724 glDisable(GL_RESCALE_NORMAL)
725 glDisable(GL_LIGHTING)
727 glEnable(GL_DEPTH_TEST)
728 glDisable(GL_CULL_FACE)
730 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
732 glClearColor(0.8, 0.8, 0.8, 1.0)
736 glMatrixMode(GL_PROJECTION)
738 aspect = float(size.GetWidth()) / float(size.GetHeight())
739 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
741 glMatrixMode(GL_MODELVIEW)
743 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
746 if machineCom.machineIsConnected():
747 self.printButton._imageID = 6
748 self.printButton._tooltip = _("Print")
749 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
750 self.printButton._imageID = 2
751 self.printButton._tooltip = _("Toolpath to SD")
753 self.printButton._imageID = 3
754 self.printButton._tooltip = _("Save toolpath")
756 if self._animView is not None:
757 self._viewTarget = self._animView.getPosition()
758 if self._animView.isDone():
759 self._animView = None
760 if self._animZoom is not None:
761 self._zoom = self._animZoom.getPosition()
762 if self._animZoom.isDone():
763 self._animZoom = None
764 if self.viewMode == 'gcode' and self._gcode is not None:
766 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
769 if self._objectShader is None:
770 if opengl.hasShaderSupport():
771 self._objectShader = opengl.GLShader("""
772 varying float light_amount;
776 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
777 gl_FrontColor = gl_Color;
779 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
783 varying float light_amount;
787 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
790 self._objectOverhangShader = opengl.GLShader("""
791 uniform float cosAngle;
792 uniform mat3 rotMatrix;
793 varying float light_amount;
797 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
798 gl_FrontColor = gl_Color;
800 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
802 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
804 light_amount = -10.0;
808 varying float light_amount;
812 if (light_amount == -10.0)
814 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
816 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
820 self._objectLoadShader = opengl.GLShader("""
821 uniform float intensity;
823 varying float light_amount;
827 vec4 tmp = gl_Vertex;
828 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
829 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
830 gl_Position = gl_ModelViewProjectionMatrix * tmp;
831 gl_FrontColor = gl_Color;
833 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
837 uniform float intensity;
838 varying float light_amount;
842 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
845 if self._objectShader == None or not self._objectShader.isValid():
846 self._objectShader = opengl.GLFakeShader()
847 self._objectOverhangShader = opengl.GLFakeShader()
848 self._objectLoadShader = None
850 glTranslate(0,0,-self._zoom)
851 glRotate(-self._pitch, 1,0,0)
852 glRotate(self._yaw, 0,0,1)
853 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
855 self._viewport = glGetIntegerv(GL_VIEWPORT)
856 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
857 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
859 glClearColor(1,1,1,1)
860 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
862 if self.viewMode != 'gcode':
863 for n in xrange(0, len(self._scene.objects())):
864 obj = self._scene.objects()[n]
865 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
866 self._renderObject(obj)
868 if self._mouseX > -1:
870 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
871 if n < len(self._scene.objects()):
872 self._focusObj = self._scene.objects()[n]
874 self._focusObj = None
875 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
876 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
877 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
878 self._mouse3Dpos -= self._viewTarget
881 glTranslate(0,0,-self._zoom)
882 glRotate(-self._pitch, 1,0,0)
883 glRotate(self._yaw, 0,0,1)
884 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
886 if self.viewMode == 'gcode':
887 if self._gcode is not None and self._gcode.layerList is None:
888 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
889 self._gcodeLoadThread.daemon = True
890 self._gcodeLoadThread.start()
891 if self._gcode is not None and self._gcode.layerList is not None:
893 if profile.getMachineSetting('machine_center_is_zero') != 'True':
894 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
896 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
897 for n in xrange(0, drawUpTill):
898 c = 1.0 - float(drawUpTill - n) / 15
900 if len(self._gcodeVBOs) < n + 1:
901 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
902 if time.time() - t > 0.5:
905 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
906 if n == drawUpTill - 1:
907 if len(self._gcodeVBOs[n]) < 9:
908 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
910 self._gcodeVBOs[n][8].render(GL_QUADS)
912 self._gcodeVBOs[n][9].render(GL_QUADS)
914 self._gcodeVBOs[n][10].render(GL_QUADS)
916 self._gcodeVBOs[n][11].render(GL_QUADS)
919 self._gcodeVBOs[n][12].render(GL_QUADS)
920 glColor3f(c/2, c/2, 0.0)
921 self._gcodeVBOs[n][13].render(GL_QUADS)
923 self._gcodeVBOs[n][14].render(GL_QUADS)
924 self._gcodeVBOs[n][15].render(GL_QUADS)
926 self._gcodeVBOs[n][16].render(GL_LINES)
929 self._gcodeVBOs[n][0].render(GL_LINES)
931 self._gcodeVBOs[n][1].render(GL_LINES)
933 self._gcodeVBOs[n][2].render(GL_LINES)
935 self._gcodeVBOs[n][3].render(GL_LINES)
938 self._gcodeVBOs[n][4].render(GL_LINES)
939 glColor3f(c/2, c/2, 0.0)
940 self._gcodeVBOs[n][5].render(GL_LINES)
942 self._gcodeVBOs[n][6].render(GL_LINES)
943 self._gcodeVBOs[n][7].render(GL_LINES)
946 glStencilFunc(GL_ALWAYS, 1, 1)
947 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
949 if self.viewMode == 'overhang':
950 self._objectOverhangShader.bind()
951 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
953 self._objectShader.bind()
954 for obj in self._scene.objects():
955 if obj._loadAnim is not None:
956 if obj._loadAnim.isDone():
961 if self._focusObj == obj:
963 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
966 if self._selectedObj == obj or self._selectedObj is None:
967 #If we want transparent, then first render a solid black model to remove the printer size lines.
968 if self.viewMode == 'transparent':
969 glColor4f(0, 0, 0, 0)
970 self._renderObject(obj)
972 glBlendFunc(GL_ONE, GL_ONE)
973 glDisable(GL_DEPTH_TEST)
975 if self.viewMode == 'xray':
976 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
977 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
978 glEnable(GL_STENCIL_TEST)
980 if self.viewMode == 'overhang':
981 if self._selectedObj == obj and self.tempMatrix is not None:
982 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
984 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
986 if not self._scene.checkPlatform(obj):
987 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
988 self._renderObject(obj)
990 self._renderObject(obj, brightness)
991 glDisable(GL_STENCIL_TEST)
993 glEnable(GL_DEPTH_TEST)
994 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
996 if self.viewMode == 'xray':
999 glEnable(GL_STENCIL_TEST)
1000 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1001 glDisable(GL_DEPTH_TEST)
1002 for i in xrange(2, 15, 2):
1003 glStencilFunc(GL_EQUAL, i, 0xFF)
1004 glColor(float(i)/10, float(i)/10, float(i)/5)
1006 glVertex3f(-1000,-1000,-10)
1007 glVertex3f( 1000,-1000,-10)
1008 glVertex3f( 1000, 1000,-10)
1009 glVertex3f(-1000, 1000,-10)
1011 for i in xrange(1, 15, 2):
1012 glStencilFunc(GL_EQUAL, i, 0xFF)
1013 glColor(float(i)/10, 0, 0)
1015 glVertex3f(-1000,-1000,-10)
1016 glVertex3f( 1000,-1000,-10)
1017 glVertex3f( 1000, 1000,-10)
1018 glVertex3f(-1000, 1000,-10)
1021 glDisable(GL_STENCIL_TEST)
1022 glEnable(GL_DEPTH_TEST)
1024 self._objectShader.unbind()
1026 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1028 if self._objectLoadShader is not None:
1029 self._objectLoadShader.bind()
1030 glColor4f(0.2, 0.6, 1.0, 1.0)
1031 for obj in self._scene.objects():
1032 if obj._loadAnim is None:
1034 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1035 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1036 self._renderObject(obj)
1037 self._objectLoadShader.unbind()
1042 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1044 z = self._usbPrintMonitor.getZ()
1045 size = self._machineSize
1046 glColor4ub(255,255,0,128)
1048 glVertex3f(-size[0]/2,-size[1]/2, z)
1049 glVertex3f( size[0]/2,-size[1]/2, z)
1050 glVertex3f( size[0]/2, size[1]/2, z)
1051 glVertex3f(-size[0]/2, size[1]/2, z)
1054 if self.viewMode == 'gcode':
1055 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1056 glDisable(GL_DEPTH_TEST)
1059 glTranslate(0,-4,-10)
1060 glColor4ub(60,60,60,255)
1061 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1064 #Draw the object box-shadow, so you can see where it will collide with other objects.
1065 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1066 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1068 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1070 glEnable(GL_CULL_FACE)
1071 glColor4f(0,0,0,0.12)
1073 glVertex3f(-size[0], size[1], 0.1)
1074 glVertex3f(-size[0], -size[1], 0.1)
1075 glVertex3f( size[0], -size[1], 0.1)
1076 glVertex3f( size[0], size[1], 0.1)
1078 glDisable(GL_CULL_FACE)
1081 #Draw the outline of the selected object, on top of everything else except the GUI.
1082 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1083 glDisable(GL_DEPTH_TEST)
1084 glEnable(GL_CULL_FACE)
1085 glEnable(GL_STENCIL_TEST)
1087 glStencilFunc(GL_EQUAL, 0, 255)
1089 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1091 glColor4f(1,1,1,0.5)
1092 self._renderObject(self._selectedObj)
1093 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1095 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1096 glDisable(GL_STENCIL_TEST)
1097 glDisable(GL_CULL_FACE)
1098 glEnable(GL_DEPTH_TEST)
1100 if self._selectedObj is not None:
1102 pos = self.getObjectCenterPos()
1103 glTranslate(pos[0], pos[1], pos[2])
1106 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1107 glDisable(GL_DEPTH_TEST)
1110 glTranslate(0,-4,-10)
1111 glColor4ub(60,60,60,255)
1112 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1115 def _renderObject(self, obj, brightness = False, addSink = True):
1118 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1120 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1122 if self.tempMatrix is not None and obj == self._selectedObj:
1123 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1124 glMultMatrixf(tempMatrix)
1126 offset = obj.getDrawOffset()
1127 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1129 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1130 glMultMatrixf(tempMatrix)
1133 for m in obj._meshList:
1135 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1137 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1142 def _drawMachine(self):
1143 glEnable(GL_CULL_FACE)
1146 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1148 machine = profile.getMachineSetting('machine_type')
1149 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
1150 if machine not in self._platformMesh:
1151 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1153 self._platformMesh[machine] = meshes[0]
1155 self._platformMesh[machine] = None
1156 if profile.getMachineSetting('machine_type') == 'ultimaker2':
1157 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1159 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1160 glColor4f(1,1,1,0.5)
1161 self._objectShader.bind()
1162 self._renderObject(self._platformMesh[machine], False, False)
1163 self._objectShader.unbind()
1168 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1169 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1170 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1171 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1172 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1173 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1176 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1177 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1178 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1179 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1180 v4 = [ size[0] / 2, size[1] / 2, 0]
1181 v5 = [ size[0] / 2,-size[1] / 2, 0]
1182 v6 = [-size[0] / 2, size[1] / 2, 0]
1183 v7 = [-size[0] / 2,-size[1] / 2, 0]
1185 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1186 glEnableClientState(GL_VERTEX_ARRAY)
1187 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1189 glColor4ub(5, 171, 231, 64)
1190 glDrawArrays(GL_QUADS, 0, 4)
1191 glColor4ub(5, 171, 231, 96)
1192 glDrawArrays(GL_QUADS, 4, 8)
1193 glColor4ub(5, 171, 231, 128)
1194 glDrawArrays(GL_QUADS, 12, 8)
1195 glDisableClientState(GL_VERTEX_ARRAY)
1197 sx = self._machineSize[0]
1198 sy = self._machineSize[1]
1199 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1200 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1205 x1 = max(min(x1, sx/2), -sx/2)
1206 y1 = max(min(y1, sy/2), -sy/2)
1207 x2 = max(min(x2, sx/2), -sx/2)
1208 y2 = max(min(y2, sy/2), -sy/2)
1209 if (x & 1) == (y & 1):
1210 glColor4ub(5, 171, 231, 127)
1212 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1214 glVertex3f(x1, y1, -0.02)
1215 glVertex3f(x2, y1, -0.02)
1216 glVertex3f(x2, y2, -0.02)
1217 glVertex3f(x1, y2, -0.02)
1221 glDisable(GL_CULL_FACE)
1223 def _generateGCodeVBOs(self, layer):
1225 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1226 if ':' in extrudeType:
1227 extruder = int(extrudeType[extrudeType.find(':')+1:])
1228 extrudeType = extrudeType[0:extrudeType.find(':')]
1231 pointList = numpy.zeros((0,3), numpy.float32)
1233 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1235 a = numpy.concatenate((a[:-1], a[1:]), 1)
1236 a = a.reshape((len(a) * 2, 3))
1237 pointList = numpy.concatenate((pointList, a))
1238 ret.append(opengl.GLVBO(pointList))
1241 def _generateGCodeVBOs2(self, layer):
1242 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1243 filamentArea = math.pi * filamentRadius * filamentRadius
1244 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1247 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1248 if ':' in extrudeType:
1249 extruder = int(extrudeType[extrudeType.find(':')+1:])
1250 extrudeType = extrudeType[0:extrudeType.find(':')]
1253 pointList = numpy.zeros((0,3), numpy.float32)
1255 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1257 if extrudeType == 'FILL':
1260 normal = a[1:] - a[:-1]
1261 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1262 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1265 ePerDist = path['extrusion'][1:] / lens
1267 lineWidth = ePerDist / path['layerThickness'] / 2.0
1269 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1271 normal[:,0] *= lineWidth
1272 normal[:,1] *= lineWidth
1274 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1275 b = numpy.concatenate((b, a[1:] + normal), 1)
1276 b = numpy.concatenate((b, a[1:] - normal), 1)
1277 b = numpy.concatenate((b, a[:-1] - normal), 1)
1278 b = numpy.concatenate((b, a[:-1] + normal), 1)
1279 b = b.reshape((len(b) * 4, 3))
1282 normal2 = normal[:-1] + normal[1:]
1283 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1284 normal2[:,0] /= lens2
1285 normal2[:,1] /= lens2
1286 normal2[:,0] *= lineWidth[:-1]
1287 normal2[:,1] *= lineWidth[:-1]
1289 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1290 c = numpy.concatenate((c, a[1:-1]), 1)
1291 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1292 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1293 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1295 c = numpy.concatenate((c, a[1:-1]), 1)
1296 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1297 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1298 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1300 c = c.reshape((len(c) * 8, 3))
1302 pointList = numpy.concatenate((pointList, b, c))
1304 pointList = numpy.concatenate((pointList, b))
1305 ret.append(opengl.GLVBO(pointList))
1307 pointList = numpy.zeros((0,3), numpy.float32)
1309 if path['type'] == 'move':
1310 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1311 a = numpy.concatenate((a[:-1], a[1:]), 1)
1312 a = a.reshape((len(a) * 2, 3))
1313 pointList = numpy.concatenate((pointList, a))
1314 if path['type'] == 'retract':
1315 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1316 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1317 a = a.reshape((len(a) * 2, 3))
1318 pointList = numpy.concatenate((pointList, a))
1319 ret.append(opengl.GLVBO(pointList))
1323 def getObjectCenterPos(self):
1324 if self._selectedObj is None:
1325 return [0.0, 0.0, 0.0]
1326 pos = self._selectedObj.getPosition()
1327 size = self._selectedObj.getSize()
1328 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1330 def getObjectBoundaryCircle(self):
1331 if self._selectedObj is None:
1333 return self._selectedObj.getBoundaryCircle()
1335 def getObjectSize(self):
1336 if self._selectedObj is None:
1337 return [0.0, 0.0, 0.0]
1338 return self._selectedObj.getSize()
1340 def getObjectMatrix(self):
1341 if self._selectedObj is None:
1342 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1343 return self._selectedObj.getMatrix()
1345 class shaderEditor(wx.Dialog):
1346 def __init__(self, parent, callback, v, f):
1347 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1348 self._callback = callback
1349 s = wx.BoxSizer(wx.VERTICAL)
1351 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1352 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1353 s.Add(self._vertex, 1, flag=wx.EXPAND)
1354 s.Add(self._fragment, 1, flag=wx.EXPAND)
1356 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1357 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1359 self.SetPosition(self.GetParent().GetPosition())
1360 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1363 def OnText(self, e):
1364 self._callback(self._vertex.GetValue(), self._fragment.GetValue())