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 for filename in filenames:
148 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
149 ext = filename[filename.rfind('.')+1:].upper()
150 if ext == 'G' or ext == 'GCODE':
151 gcodeFilename = filename
152 if gcodeFilename is not None:
153 self.loadGCodeFile(gcodeFilename)
155 self.loadSceneFiles(filenames)
157 def showLoadModel(self, button = 1):
159 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)
160 dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
161 if dlg.ShowModal() != wx.ID_OK:
164 filenames = dlg.GetPaths()
166 if len(filenames) < 1:
168 profile.putPreference('lastFile', filenames[0])
169 self.loadFiles(filenames)
171 def showSaveModel(self):
172 if len(self._scene.objects()) < 1:
174 dlg=wx.FileDialog(self, _("Save 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
175 dlg.SetWildcard(meshLoader.saveWildcardFilter())
176 if dlg.ShowModal() != wx.ID_OK:
179 filename = dlg.GetPath()
181 meshLoader.saveMeshes(filename, self._scene.objects())
183 def OnPrintButton(self, button):
185 if machineCom.machineIsConnected():
186 self.showPrintWindow()
187 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
188 drives = removableStorage.getPossibleSDcardDrives()
190 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))
191 if dlg.ShowModal() != wx.ID_OK:
194 drive = drives[dlg.GetSelection()]
198 filename = self._scene._objectList[0].getName() + '.gcode'
199 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
204 self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB")))
205 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode...")))
206 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, _("Slice engine log...")))
210 def showPrintWindow(self):
211 if self._gcodeFilename is None:
213 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
214 if self._gcodeFilename == self._slicer.getGCodeFilename():
215 self._slicer.submitSliceInfoOnline()
217 def showSaveGCode(self):
218 defPath = profile.getPreference('lastFile')
219 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
220 dlg=wx.FileDialog(self, _("Save toolpath"), defPath, style=wx.FD_SAVE)
221 dlg.SetFilename(self._scene._objectList[0].getName())
222 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
223 if dlg.ShowModal() != wx.ID_OK:
226 filename = dlg.GetPath()
229 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
231 def _copyFile(self, fileA, fileB, allowEject = False):
233 size = float(os.stat(fileA).st_size)
234 with open(fileA, 'rb') as fsrc:
235 with open(fileB, 'wb') as fdst:
237 buf = fsrc.read(16*1024)
241 self.printButton.setProgressBar(float(fsrc.tell()) / size)
246 self.notification.message("Failed to save")
249 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...'))
251 self.notification.message("Saved as %s" % (fileB))
252 self.printButton.setProgressBar(None)
253 if fileA == self._slicer.getGCodeFilename():
254 self._slicer.submitSliceInfoOnline()
256 def _showSliceLog(self):
257 dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
261 def OnToolSelect(self, button):
262 if self.rotateToolButton.getSelected():
263 self.tool = previewTools.toolRotate(self)
264 elif self.scaleToolButton.getSelected():
265 self.tool = previewTools.toolScale(self)
266 elif self.mirrorToolButton.getSelected():
267 self.tool = previewTools.toolNone(self)
269 self.tool = previewTools.toolNone(self)
270 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
271 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
272 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
273 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
274 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
275 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
276 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
277 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
279 def updateToolButtons(self):
280 if self._selectedObj is None:
284 self.rotateToolButton.setHidden(hidden)
285 self.scaleToolButton.setHidden(hidden)
286 self.mirrorToolButton.setHidden(hidden)
288 self.rotateToolButton.setSelected(False)
289 self.scaleToolButton.setSelected(False)
290 self.mirrorToolButton.setSelected(False)
293 def OnViewChange(self):
294 if self.viewSelection.getValue() == 4:
295 self.viewMode = 'gcode'
296 if self._gcode is not None and self._gcode.layerList is not None:
297 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
298 self._selectObject(None)
299 elif self.viewSelection.getValue() == 1:
300 self.viewMode = 'overhang'
301 elif self.viewSelection.getValue() == 2:
302 self.viewMode = 'transparent'
303 elif self.viewSelection.getValue() == 3:
304 self.viewMode = 'xray'
306 self.viewMode = 'normal'
307 self.layerSelect.setHidden(self.viewMode != 'gcode')
310 def OnRotateReset(self, button):
311 if self._selectedObj is None:
313 self._selectedObj.resetRotation()
314 self._scene.pushFree()
315 self._selectObject(self._selectedObj)
318 def OnLayFlat(self, button):
319 if self._selectedObj is None:
321 self._selectedObj.layFlat()
322 self._scene.pushFree()
323 self._selectObject(self._selectedObj)
326 def OnScaleReset(self, button):
327 if self._selectedObj is None:
329 self._selectedObj.resetScale()
330 self._selectObject(self._selectedObj)
331 self.updateProfileToControls()
334 def OnScaleMax(self, button):
335 if self._selectedObj is None:
337 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
338 self._scene.pushFree()
339 self._selectObject(self._selectedObj)
340 self.updateProfileToControls()
343 def OnMirror(self, axis):
344 if self._selectedObj is None:
346 self._selectedObj.mirror(axis)
349 def OnScaleEntry(self, value, axis):
350 if self._selectedObj is None:
356 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
357 self.updateProfileToControls()
358 self._scene.pushFree()
359 self._selectObject(self._selectedObj)
362 def OnScaleEntryMM(self, value, axis):
363 if self._selectedObj is None:
369 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
370 self.updateProfileToControls()
371 self._scene.pushFree()
372 self._selectObject(self._selectedObj)
375 def OnDeleteAll(self, e):
376 while len(self._scene.objects()) > 0:
377 self._deleteObject(self._scene.objects()[0])
378 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
380 def OnMultiply(self, e):
381 if self._focusObj is None:
384 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
385 if dlg.ShowModal() != wx.ID_OK:
394 self._scene.add(newObj)
395 self._scene.centerAll()
396 if not self._scene.checkPlatform(newObj):
401 self.notification.message("Could not create more then %d items" % (n - 1))
402 self._scene.remove(newObj)
403 self._scene.centerAll()
406 def OnSplitObject(self, e):
407 if self._focusObj is None:
409 self._scene.remove(self._focusObj)
410 for obj in self._focusObj.split(self._splitCallback):
411 if numpy.max(obj.getSize()) > 2.0:
413 self._scene.centerAll()
414 self._selectObject(None)
417 def OnCenter(self, e):
418 if self._focusObj is None:
420 self._focusObj.setPosition(numpy.array([0.0, 0.0]))
421 self._scene.pushFree()
423 def _splitCallback(self, progress):
426 def OnMergeObjects(self, e):
427 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
428 if len(self._scene.objects()) == 2:
429 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
432 self._scene.merge(self._selectedObj, self._focusObj)
435 def sceneUpdated(self):
436 self._sceneUpdateTimer.Start(500, True)
437 self._slicer.abortSlicer()
438 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
441 def _onRunSlicer(self, e):
442 if self._isSimpleMode:
443 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
444 self._slicer.runSlicer(self._scene)
445 if self._isSimpleMode:
446 profile.resetTempOverride()
448 def _updateSliceProgress(self, progressValue, ready):
450 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
452 self.printButton.setDisabled(not ready)
453 if progressValue >= 0.0:
454 self.printButton.setProgressBar(progressValue)
456 self.printButton.setProgressBar(None)
457 if self._gcode is not None:
459 for layerVBOlist in self._gcodeVBOs:
460 for vbo in layerVBOlist:
461 self.glReleaseList.append(vbo)
464 self.printButton.setProgressBar(None)
465 cost = self._slicer.getFilamentCost()
467 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
469 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
470 self._gcode = gcodeInterpreter.gcode()
471 self._gcodeFilename = self._slicer.getGCodeFilename()
473 self.printButton.setBottomText('')
476 def _loadGCode(self):
477 self._gcode.progressCallback = self._gcodeLoadCallback
478 self._gcode.load(self._gcodeFilename)
480 def _gcodeLoadCallback(self, progress):
481 if self._gcode is None:
483 if len(self._gcode.layerList) % 15 == 0:
485 if self._gcode is None:
487 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
488 if self.viewMode == 'gcode':
492 def loadScene(self, fileList):
493 for filename in fileList:
495 objList = meshLoader.loadMeshes(filename)
497 traceback.print_exc()
500 if self._objectLoadShader is not None:
501 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
505 self._scene.centerAll()
506 self._selectObject(obj)
507 if obj.getScale()[0] < 1.0:
508 self.notification.message("Warning: Object scaled down.")
511 def _deleteObject(self, obj):
512 if obj == self._selectedObj:
513 self._selectObject(None)
514 if obj == self._focusObj:
515 self._focusObj = None
516 self._scene.remove(obj)
517 for m in obj._meshList:
518 if m.vbo is not None and m.vbo.decRef():
519 self.glReleaseList.append(m.vbo)
524 def _selectObject(self, obj, zoom = True):
525 if obj != self._selectedObj:
526 self._selectedObj = obj
527 self.updateProfileToControls()
528 self.updateToolButtons()
529 if zoom and obj is not None:
530 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
531 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
532 newZoom = obj.getBoundaryCircle() * 6
533 if newZoom > numpy.max(self._machineSize) * 3:
534 newZoom = numpy.max(self._machineSize) * 3
535 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
537 def updateProfileToControls(self):
538 oldSimpleMode = self._isSimpleMode
539 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
540 if self._isSimpleMode != oldSimpleMode:
541 self._scene.arrangeAll()
543 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
544 self._objColors[0] = profile.getPreferenceColour('model_colour')
545 self._objColors[1] = profile.getPreferenceColour('model_colour2')
546 self._objColors[2] = profile.getPreferenceColour('model_colour3')
547 self._objColors[3] = profile.getPreferenceColour('model_colour4')
548 self._scene.setMachineSize(self._machineSize)
549 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
550 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'))
552 if self._selectedObj is not None:
553 scale = self._selectedObj.getScale()
554 size = self._selectedObj.getSize()
555 self.scaleXctrl.setValue(round(scale[0], 2))
556 self.scaleYctrl.setValue(round(scale[1], 2))
557 self.scaleZctrl.setValue(round(scale[2], 2))
558 self.scaleXmmctrl.setValue(round(size[0], 2))
559 self.scaleYmmctrl.setValue(round(size[1], 2))
560 self.scaleZmmctrl.setValue(round(size[2], 2))
562 def OnKeyChar(self, keyCode):
563 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
564 if self._selectedObj is not None:
565 self._deleteObject(self._selectedObj)
567 if keyCode == wx.WXK_UP:
568 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
570 elif keyCode == wx.WXK_DOWN:
571 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
573 elif keyCode == wx.WXK_PAGEUP:
574 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
576 elif keyCode == wx.WXK_PAGEDOWN:
577 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
580 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
581 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
582 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
583 from collections import defaultdict
584 from gc import get_objects
585 self._beforeLeakTest = defaultdict(int)
586 for i in get_objects():
587 self._beforeLeakTest[type(i)] += 1
588 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
589 from collections import defaultdict
590 from gc import get_objects
591 self._afterLeakTest = defaultdict(int)
592 for i in get_objects():
593 self._afterLeakTest[type(i)] += 1
594 for k in self._afterLeakTest:
595 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
596 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
598 def ShaderUpdate(self, v, f):
599 s = opengl.GLShader(v, f)
601 self._objectLoadShader.release()
602 self._objectLoadShader = s
603 for obj in self._scene.objects():
604 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
607 def OnMouseDown(self,e):
608 self._mouseX = e.GetX()
609 self._mouseY = e.GetY()
610 self._mouseClick3DPos = self._mouse3Dpos
611 self._mouseClickFocus = self._focusObj
613 self._mouseState = 'doubleClick'
615 self._mouseState = 'dragOrClick'
616 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
617 p0 -= self.getObjectCenterPos() - self._viewTarget
618 p1 -= self.getObjectCenterPos() - self._viewTarget
619 if self.tool.OnDragStart(p0, p1):
620 self._mouseState = 'tool'
621 if self._mouseState == 'dragOrClick':
622 if e.GetButton() == 1:
623 if self._focusObj is not None:
624 self._selectObject(self._focusObj, False)
627 def OnMouseUp(self, e):
628 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
630 if self._mouseState == 'dragOrClick':
631 if e.GetButton() == 1:
632 self._selectObject(self._focusObj)
633 if e.GetButton() == 3:
635 if self._focusObj is not None:
636 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
637 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
638 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
639 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
640 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:
641 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
642 if len(self._scene.objects()) > 0:
643 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
644 if menu.MenuItemCount > 0:
647 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
648 self._scene.pushFree()
650 elif self._mouseState == 'tool':
651 if self.tempMatrix is not None and self._selectedObj is not None:
652 self._selectedObj.applyMatrix(self.tempMatrix)
653 self._scene.pushFree()
654 self._selectObject(self._selectedObj)
655 self.tempMatrix = None
656 self.tool.OnDragEnd()
658 self._mouseState = None
660 def OnMouseMotion(self,e):
661 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
662 p0 -= self.getObjectCenterPos() - self._viewTarget
663 p1 -= self.getObjectCenterPos() - self._viewTarget
665 if e.Dragging() and self._mouseState is not None:
666 if self._mouseState == 'tool':
667 self.tool.OnDrag(p0, p1)
668 elif not e.LeftIsDown() and e.RightIsDown():
669 self._mouseState = 'drag'
670 if wx.GetKeyState(wx.WXK_SHIFT):
671 a = math.cos(math.radians(self._yaw)) / 3.0
672 b = math.sin(math.radians(self._yaw)) / 3.0
673 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
674 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
675 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
676 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
678 self._yaw += e.GetX() - self._mouseX
679 self._pitch -= e.GetY() - self._mouseY
680 if self._pitch > 170:
684 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
685 self._mouseState = 'drag'
686 self._zoom += e.GetY() - self._mouseY
689 if self._zoom > numpy.max(self._machineSize) * 3:
690 self._zoom = numpy.max(self._machineSize) * 3
691 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
692 self._mouseState = 'dragObject'
693 z = max(0, self._mouseClick3DPos[2])
694 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
695 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
700 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
701 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
702 diff = cursorZ1 - cursorZ0
703 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
704 if not e.Dragging() or self._mouseState != 'tool':
705 self.tool.OnMouseMove(p0, p1)
707 self._mouseX = e.GetX()
708 self._mouseY = e.GetY()
710 def OnMouseWheel(self, e):
711 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
712 delta = max(min(delta,4),-4)
713 self._zoom *= 1.0 - delta / 10.0
716 if self._zoom > numpy.max(self._machineSize) * 3:
717 self._zoom = numpy.max(self._machineSize) * 3
720 def OnMouseLeave(self, e):
724 def getMouseRay(self, x, y):
725 if self._viewport is None:
726 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
727 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
728 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
729 p0 -= self._viewTarget
730 p1 -= self._viewTarget
733 def _init3DView(self):
734 # set viewing projection
735 size = self.GetSize()
736 glViewport(0, 0, size.GetWidth(), size.GetHeight())
739 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
741 glDisable(GL_RESCALE_NORMAL)
742 glDisable(GL_LIGHTING)
744 glEnable(GL_DEPTH_TEST)
745 glDisable(GL_CULL_FACE)
747 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
749 glClearColor(0.8, 0.8, 0.8, 1.0)
753 glMatrixMode(GL_PROJECTION)
755 aspect = float(size.GetWidth()) / float(size.GetHeight())
756 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
758 glMatrixMode(GL_MODELVIEW)
760 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
763 if machineCom.machineIsConnected():
764 self.printButton._imageID = 6
765 self.printButton._tooltip = _("Print")
766 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
767 self.printButton._imageID = 2
768 self.printButton._tooltip = _("Toolpath to SD")
770 self.printButton._imageID = 3
771 self.printButton._tooltip = _("Save toolpath")
773 if self._animView is not None:
774 self._viewTarget = self._animView.getPosition()
775 if self._animView.isDone():
776 self._animView = None
777 if self._animZoom is not None:
778 self._zoom = self._animZoom.getPosition()
779 if self._animZoom.isDone():
780 self._animZoom = None
781 if self.viewMode == 'gcode' and self._gcode is not None:
783 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
786 if self._objectShader is None:
787 if opengl.hasShaderSupport():
788 self._objectShader = opengl.GLShader("""
789 varying float light_amount;
793 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
794 gl_FrontColor = gl_Color;
796 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
800 varying float light_amount;
804 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
807 self._objectOverhangShader = opengl.GLShader("""
808 uniform float cosAngle;
809 uniform mat3 rotMatrix;
810 varying float light_amount;
814 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
815 gl_FrontColor = gl_Color;
817 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
819 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
821 light_amount = -10.0;
825 varying float light_amount;
829 if (light_amount == -10.0)
831 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
833 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
837 self._objectLoadShader = opengl.GLShader("""
838 uniform float intensity;
840 varying float light_amount;
844 vec4 tmp = gl_Vertex;
845 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
846 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
847 gl_Position = gl_ModelViewProjectionMatrix * tmp;
848 gl_FrontColor = gl_Color;
850 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
854 uniform float intensity;
855 varying float light_amount;
859 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
862 if self._objectShader == None or not self._objectShader.isValid():
863 self._objectShader = opengl.GLFakeShader()
864 self._objectOverhangShader = opengl.GLFakeShader()
865 self._objectLoadShader = None
867 glTranslate(0,0,-self._zoom)
868 glRotate(-self._pitch, 1,0,0)
869 glRotate(self._yaw, 0,0,1)
870 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
872 self._viewport = glGetIntegerv(GL_VIEWPORT)
873 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
874 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
876 glClearColor(1,1,1,1)
877 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
879 if self.viewMode != 'gcode':
880 for n in xrange(0, len(self._scene.objects())):
881 obj = self._scene.objects()[n]
882 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
883 self._renderObject(obj)
885 if self._mouseX > -1:
887 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
888 if n < len(self._scene.objects()):
889 self._focusObj = self._scene.objects()[n]
891 self._focusObj = None
892 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
893 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
894 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
895 self._mouse3Dpos -= self._viewTarget
898 glTranslate(0,0,-self._zoom)
899 glRotate(-self._pitch, 1,0,0)
900 glRotate(self._yaw, 0,0,1)
901 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
903 if self.viewMode == 'gcode':
904 if self._gcode is not None and self._gcode.layerList is None:
905 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
906 self._gcodeLoadThread.daemon = True
907 self._gcodeLoadThread.start()
908 if self._gcode is not None and self._gcode.layerList is not None:
910 if profile.getMachineSetting('machine_center_is_zero') != 'True':
911 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
913 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
914 for n in xrange(0, drawUpTill):
915 c = 1.0 - float(drawUpTill - n) / 15
917 if len(self._gcodeVBOs) < n + 1:
918 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
919 if time.time() - t > 0.5:
922 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
923 if n == drawUpTill - 1:
924 if len(self._gcodeVBOs[n]) < 9:
925 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
927 self._gcodeVBOs[n][8].render(GL_QUADS)
929 self._gcodeVBOs[n][9].render(GL_QUADS)
931 self._gcodeVBOs[n][10].render(GL_QUADS)
933 self._gcodeVBOs[n][11].render(GL_QUADS)
936 self._gcodeVBOs[n][12].render(GL_QUADS)
937 glColor3f(c/2, c/2, 0.0)
938 self._gcodeVBOs[n][13].render(GL_QUADS)
940 self._gcodeVBOs[n][14].render(GL_QUADS)
941 self._gcodeVBOs[n][15].render(GL_QUADS)
943 self._gcodeVBOs[n][16].render(GL_LINES)
946 self._gcodeVBOs[n][0].render(GL_LINES)
948 self._gcodeVBOs[n][1].render(GL_LINES)
950 self._gcodeVBOs[n][2].render(GL_LINES)
952 self._gcodeVBOs[n][3].render(GL_LINES)
955 self._gcodeVBOs[n][4].render(GL_LINES)
956 glColor3f(c/2, c/2, 0.0)
957 self._gcodeVBOs[n][5].render(GL_LINES)
959 self._gcodeVBOs[n][6].render(GL_LINES)
960 self._gcodeVBOs[n][7].render(GL_LINES)
963 glStencilFunc(GL_ALWAYS, 1, 1)
964 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
966 if self.viewMode == 'overhang':
967 self._objectOverhangShader.bind()
968 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
970 self._objectShader.bind()
971 for obj in self._scene.objects():
972 if obj._loadAnim is not None:
973 if obj._loadAnim.isDone():
978 if self._focusObj == obj:
980 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
983 if self._selectedObj == obj or self._selectedObj is None:
984 #If we want transparent, then first render a solid black model to remove the printer size lines.
985 if self.viewMode == 'transparent':
986 glColor4f(0, 0, 0, 0)
987 self._renderObject(obj)
989 glBlendFunc(GL_ONE, GL_ONE)
990 glDisable(GL_DEPTH_TEST)
992 if self.viewMode == 'xray':
993 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
994 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
995 glEnable(GL_STENCIL_TEST)
997 if self.viewMode == 'overhang':
998 if self._selectedObj == obj and self.tempMatrix is not None:
999 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1001 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1003 if not self._scene.checkPlatform(obj):
1004 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1005 self._renderObject(obj)
1007 self._renderObject(obj, brightness)
1008 glDisable(GL_STENCIL_TEST)
1010 glEnable(GL_DEPTH_TEST)
1011 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1013 if self.viewMode == 'xray':
1016 glEnable(GL_STENCIL_TEST)
1017 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1018 glDisable(GL_DEPTH_TEST)
1019 for i in xrange(2, 15, 2):
1020 glStencilFunc(GL_EQUAL, i, 0xFF)
1021 glColor(float(i)/10, float(i)/10, float(i)/5)
1023 glVertex3f(-1000,-1000,-10)
1024 glVertex3f( 1000,-1000,-10)
1025 glVertex3f( 1000, 1000,-10)
1026 glVertex3f(-1000, 1000,-10)
1028 for i in xrange(1, 15, 2):
1029 glStencilFunc(GL_EQUAL, i, 0xFF)
1030 glColor(float(i)/10, 0, 0)
1032 glVertex3f(-1000,-1000,-10)
1033 glVertex3f( 1000,-1000,-10)
1034 glVertex3f( 1000, 1000,-10)
1035 glVertex3f(-1000, 1000,-10)
1038 glDisable(GL_STENCIL_TEST)
1039 glEnable(GL_DEPTH_TEST)
1041 self._objectShader.unbind()
1043 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1045 if self._objectLoadShader is not None:
1046 self._objectLoadShader.bind()
1047 glColor4f(0.2, 0.6, 1.0, 1.0)
1048 for obj in self._scene.objects():
1049 if obj._loadAnim is None:
1051 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1052 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1053 self._renderObject(obj)
1054 self._objectLoadShader.unbind()
1059 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1061 z = self._usbPrintMonitor.getZ()
1062 size = self._machineSize
1063 glColor4ub(255,255,0,128)
1065 glVertex3f(-size[0]/2,-size[1]/2, z)
1066 glVertex3f( size[0]/2,-size[1]/2, z)
1067 glVertex3f( size[0]/2, size[1]/2, z)
1068 glVertex3f(-size[0]/2, size[1]/2, z)
1071 if self.viewMode == 'gcode':
1072 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1073 glDisable(GL_DEPTH_TEST)
1076 glTranslate(0,-4,-10)
1077 glColor4ub(60,60,60,255)
1078 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1081 #Draw the object box-shadow, so you can see where it will collide with other objects.
1082 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1083 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1085 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1087 glEnable(GL_CULL_FACE)
1088 glColor4f(0,0,0,0.12)
1090 glVertex3f(-size[0], size[1], 0.1)
1091 glVertex3f(-size[0], -size[1], 0.1)
1092 glVertex3f( size[0], -size[1], 0.1)
1093 glVertex3f( size[0], size[1], 0.1)
1095 glDisable(GL_CULL_FACE)
1098 #Draw the outline of the selected object, on top of everything else except the GUI.
1099 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1100 glDisable(GL_DEPTH_TEST)
1101 glEnable(GL_CULL_FACE)
1102 glEnable(GL_STENCIL_TEST)
1104 glStencilFunc(GL_EQUAL, 0, 255)
1106 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1108 glColor4f(1,1,1,0.5)
1109 self._renderObject(self._selectedObj)
1110 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1112 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1113 glDisable(GL_STENCIL_TEST)
1114 glDisable(GL_CULL_FACE)
1115 glEnable(GL_DEPTH_TEST)
1117 if self._selectedObj is not None:
1119 pos = self.getObjectCenterPos()
1120 glTranslate(pos[0], pos[1], pos[2])
1123 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1124 glDisable(GL_DEPTH_TEST)
1127 glTranslate(0,-4,-10)
1128 glColor4ub(60,60,60,255)
1129 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1132 def _renderObject(self, obj, brightness = False, addSink = True):
1135 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1137 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1139 if self.tempMatrix is not None and obj == self._selectedObj:
1140 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1141 glMultMatrixf(tempMatrix)
1143 offset = obj.getDrawOffset()
1144 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1146 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1147 glMultMatrixf(tempMatrix)
1150 for m in obj._meshList:
1152 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1154 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1159 def _drawMachine(self):
1160 glEnable(GL_CULL_FACE)
1163 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1165 machine = profile.getMachineSetting('machine_type')
1166 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
1167 if machine not in self._platformMesh:
1168 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1170 self._platformMesh[machine] = meshes[0]
1172 self._platformMesh[machine] = None
1173 if machine == 'ultimaker2':
1174 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1176 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1177 glColor4f(1,1,1,0.5)
1178 self._objectShader.bind()
1179 self._renderObject(self._platformMesh[machine], False, False)
1180 self._objectShader.unbind()
1182 #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1183 if machine == 'ultimaker2':
1184 if not hasattr(self._platformMesh[machine], 'texture'):
1185 self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1186 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1187 glEnable(GL_TEXTURE_2D)
1191 glTranslate(0,150,-5)
1196 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1199 glVertex3f( w, 0, h)
1201 glVertex3f(-w, 0, h)
1203 glVertex3f(-w, 0, 0)
1205 glVertex3f( w, 0, 0)
1208 glVertex3f(-w, d, h)
1210 glVertex3f( w, d, h)
1212 glVertex3f( w, d, 0)
1214 glVertex3f(-w, d, 0)
1216 glDisable(GL_TEXTURE_2D)
1217 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1223 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1224 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1225 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1226 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1227 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1228 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1231 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1232 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1233 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1234 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1235 v4 = [ size[0] / 2, size[1] / 2, 0]
1236 v5 = [ size[0] / 2,-size[1] / 2, 0]
1237 v6 = [-size[0] / 2, size[1] / 2, 0]
1238 v7 = [-size[0] / 2,-size[1] / 2, 0]
1240 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1241 glEnableClientState(GL_VERTEX_ARRAY)
1242 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1244 glColor4ub(5, 171, 231, 64)
1245 glDrawArrays(GL_QUADS, 0, 4)
1246 glColor4ub(5, 171, 231, 96)
1247 glDrawArrays(GL_QUADS, 4, 8)
1248 glColor4ub(5, 171, 231, 128)
1249 glDrawArrays(GL_QUADS, 12, 8)
1250 glDisableClientState(GL_VERTEX_ARRAY)
1252 sx = self._machineSize[0]
1253 sy = self._machineSize[1]
1254 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1255 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1260 x1 = max(min(x1, sx/2), -sx/2)
1261 y1 = max(min(y1, sy/2), -sy/2)
1262 x2 = max(min(x2, sx/2), -sx/2)
1263 y2 = max(min(y2, sy/2), -sy/2)
1264 if (x & 1) == (y & 1):
1265 glColor4ub(5, 171, 231, 127)
1267 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1269 glVertex3f(x1, y1, -0.02)
1270 glVertex3f(x2, y1, -0.02)
1271 glVertex3f(x2, y2, -0.02)
1272 glVertex3f(x1, y2, -0.02)
1276 glDisable(GL_CULL_FACE)
1278 def _generateGCodeVBOs(self, layer):
1280 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1281 if ':' in extrudeType:
1282 extruder = int(extrudeType[extrudeType.find(':')+1:])
1283 extrudeType = extrudeType[0:extrudeType.find(':')]
1286 pointList = numpy.zeros((0,3), numpy.float32)
1288 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1290 a = numpy.concatenate((a[:-1], a[1:]), 1)
1291 a = a.reshape((len(a) * 2, 3))
1292 pointList = numpy.concatenate((pointList, a))
1293 ret.append(opengl.GLVBO(pointList))
1296 def _generateGCodeVBOs2(self, layer):
1297 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1298 filamentArea = math.pi * filamentRadius * filamentRadius
1299 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1302 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1303 if ':' in extrudeType:
1304 extruder = int(extrudeType[extrudeType.find(':')+1:])
1305 extrudeType = extrudeType[0:extrudeType.find(':')]
1308 pointList = numpy.zeros((0,3), numpy.float32)
1310 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1312 if extrudeType == 'FILL':
1315 normal = a[1:] - a[:-1]
1316 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1317 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1320 ePerDist = path['extrusion'][1:] / lens
1322 lineWidth = ePerDist / path['layerThickness'] / 2.0
1324 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1326 normal[:,0] *= lineWidth
1327 normal[:,1] *= lineWidth
1329 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1330 b = numpy.concatenate((b, a[1:] + normal), 1)
1331 b = numpy.concatenate((b, a[1:] - normal), 1)
1332 b = numpy.concatenate((b, a[:-1] - normal), 1)
1333 b = numpy.concatenate((b, a[:-1] + normal), 1)
1334 b = b.reshape((len(b) * 4, 3))
1337 normal2 = normal[:-1] + normal[1:]
1338 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1339 normal2[:,0] /= lens2
1340 normal2[:,1] /= lens2
1341 normal2[:,0] *= lineWidth[:-1]
1342 normal2[:,1] *= lineWidth[:-1]
1344 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1345 c = numpy.concatenate((c, a[1:-1]), 1)
1346 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1347 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1348 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1350 c = numpy.concatenate((c, a[1:-1]), 1)
1351 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1352 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1353 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1355 c = c.reshape((len(c) * 8, 3))
1357 pointList = numpy.concatenate((pointList, b, c))
1359 pointList = numpy.concatenate((pointList, b))
1360 ret.append(opengl.GLVBO(pointList))
1362 pointList = numpy.zeros((0,3), numpy.float32)
1364 if path['type'] == 'move':
1365 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1366 a = numpy.concatenate((a[:-1], a[1:]), 1)
1367 a = a.reshape((len(a) * 2, 3))
1368 pointList = numpy.concatenate((pointList, a))
1369 if path['type'] == 'retract':
1370 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1371 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1372 a = a.reshape((len(a) * 2, 3))
1373 pointList = numpy.concatenate((pointList, a))
1374 ret.append(opengl.GLVBO(pointList))
1378 def getObjectCenterPos(self):
1379 if self._selectedObj is None:
1380 return [0.0, 0.0, 0.0]
1381 pos = self._selectedObj.getPosition()
1382 size = self._selectedObj.getSize()
1383 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1385 def getObjectBoundaryCircle(self):
1386 if self._selectedObj is None:
1388 return self._selectedObj.getBoundaryCircle()
1390 def getObjectSize(self):
1391 if self._selectedObj is None:
1392 return [0.0, 0.0, 0.0]
1393 return self._selectedObj.getSize()
1395 def getObjectMatrix(self):
1396 if self._selectedObj is None:
1397 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1398 return self._selectedObj.getMatrix()
1400 class shaderEditor(wx.Dialog):
1401 def __init__(self, parent, callback, v, f):
1402 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1403 self._callback = callback
1404 s = wx.BoxSizer(wx.VERTICAL)
1406 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1407 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1408 s.Add(self._vertex, 1, flag=wx.EXPAND)
1409 s.Add(self._fragment, 1, flag=wx.EXPAND)
1411 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1412 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1414 self.SetPosition(self.GetParent().GetPosition())
1415 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1418 def OnText(self, e):
1419 self._callback(self._vertex.GetValue(), self._fragment.GetValue())