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 _splitCallback(self, progress):
420 def OnMergeObjects(self, e):
421 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
422 if len(self._scene.objects()) == 2:
423 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
426 self._scene.merge(self._selectedObj, self._focusObj)
429 def sceneUpdated(self):
430 self._sceneUpdateTimer.Start(500, True)
431 self._slicer.abortSlicer()
432 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
435 def _onRunSlicer(self, e):
436 if self._isSimpleMode:
437 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
438 self._slicer.runSlicer(self._scene)
439 if self._isSimpleMode:
440 profile.resetTempOverride()
442 def _updateSliceProgress(self, progressValue, ready):
444 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
446 self.printButton.setDisabled(not ready)
447 if progressValue >= 0.0:
448 self.printButton.setProgressBar(progressValue)
450 self.printButton.setProgressBar(None)
451 if self._gcode is not None:
453 for layerVBOlist in self._gcodeVBOs:
454 for vbo in layerVBOlist:
455 self.glReleaseList.append(vbo)
458 self.printButton.setProgressBar(None)
459 cost = self._slicer.getFilamentCost()
461 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
463 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
464 self._gcode = gcodeInterpreter.gcode()
465 self._gcodeFilename = self._slicer.getGCodeFilename()
467 self.printButton.setBottomText('')
470 def _loadGCode(self):
471 self._gcode.progressCallback = self._gcodeLoadCallback
472 self._gcode.load(self._gcodeFilename)
474 def _gcodeLoadCallback(self, progress):
475 if self._gcode is None:
477 if len(self._gcode.layerList) % 15 == 0:
479 if self._gcode is None:
481 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
482 if self.viewMode == 'gcode':
486 def loadScene(self, fileList):
487 for filename in fileList:
489 objList = meshLoader.loadMeshes(filename)
491 traceback.print_exc()
494 if self._objectLoadShader is not None:
495 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
499 self._scene.centerAll()
500 self._selectObject(obj)
501 if obj.getScale()[0] < 1.0:
502 self.notification.message("Warning: Object scaled down.")
505 def _deleteObject(self, obj):
506 if obj == self._selectedObj:
507 self._selectObject(None)
508 if obj == self._focusObj:
509 self._focusObj = None
510 self._scene.remove(obj)
511 for m in obj._meshList:
512 if m.vbo is not None and m.vbo.decRef():
513 self.glReleaseList.append(m.vbo)
518 def _selectObject(self, obj, zoom = True):
519 if obj != self._selectedObj:
520 self._selectedObj = obj
521 self.updateProfileToControls()
522 self.updateToolButtons()
523 if zoom and obj is not None:
524 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
525 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
526 newZoom = obj.getBoundaryCircle() * 6
527 if newZoom > numpy.max(self._machineSize) * 3:
528 newZoom = numpy.max(self._machineSize) * 3
529 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
531 def updateProfileToControls(self):
532 oldSimpleMode = self._isSimpleMode
533 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
534 if self._isSimpleMode != oldSimpleMode:
535 self._scene.arrangeAll()
537 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
538 self._objColors[0] = profile.getPreferenceColour('model_colour')
539 self._objColors[1] = profile.getPreferenceColour('model_colour2')
540 self._objColors[2] = profile.getPreferenceColour('model_colour3')
541 self._objColors[3] = profile.getPreferenceColour('model_colour4')
542 self._scene.setMachineSize(self._machineSize)
543 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
544 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'))
546 if self._selectedObj is not None:
547 scale = self._selectedObj.getScale()
548 size = self._selectedObj.getSize()
549 self.scaleXctrl.setValue(round(scale[0], 2))
550 self.scaleYctrl.setValue(round(scale[1], 2))
551 self.scaleZctrl.setValue(round(scale[2], 2))
552 self.scaleXmmctrl.setValue(round(size[0], 2))
553 self.scaleYmmctrl.setValue(round(size[1], 2))
554 self.scaleZmmctrl.setValue(round(size[2], 2))
556 def OnKeyChar(self, keyCode):
557 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
558 if self._selectedObj is not None:
559 self._deleteObject(self._selectedObj)
561 if keyCode == wx.WXK_UP:
562 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
564 elif keyCode == wx.WXK_DOWN:
565 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
567 elif keyCode == wx.WXK_PAGEUP:
568 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
570 elif keyCode == wx.WXK_PAGEDOWN:
571 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
574 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
575 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
576 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
577 from collections import defaultdict
578 from gc import get_objects
579 self._beforeLeakTest = defaultdict(int)
580 for i in get_objects():
581 self._beforeLeakTest[type(i)] += 1
582 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
583 from collections import defaultdict
584 from gc import get_objects
585 self._afterLeakTest = defaultdict(int)
586 for i in get_objects():
587 self._afterLeakTest[type(i)] += 1
588 for k in self._afterLeakTest:
589 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
590 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
592 def ShaderUpdate(self, v, f):
593 s = opengl.GLShader(v, f)
595 self._objectLoadShader.release()
596 self._objectLoadShader = s
597 for obj in self._scene.objects():
598 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
601 def OnMouseDown(self,e):
602 self._mouseX = e.GetX()
603 self._mouseY = e.GetY()
604 self._mouseClick3DPos = self._mouse3Dpos
605 self._mouseClickFocus = self._focusObj
607 self._mouseState = 'doubleClick'
609 self._mouseState = 'dragOrClick'
610 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
611 p0 -= self.getObjectCenterPos() - self._viewTarget
612 p1 -= self.getObjectCenterPos() - self._viewTarget
613 if self.tool.OnDragStart(p0, p1):
614 self._mouseState = 'tool'
615 if self._mouseState == 'dragOrClick':
616 if e.GetButton() == 1:
617 if self._focusObj is not None:
618 self._selectObject(self._focusObj, False)
621 def OnMouseUp(self, e):
622 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
624 if self._mouseState == 'dragOrClick':
625 if e.GetButton() == 1:
626 self._selectObject(self._focusObj)
627 if e.GetButton() == 3:
629 if self._focusObj is not None:
630 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete")))
631 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply")))
632 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split")))
633 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:
634 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
635 if len(self._scene.objects()) > 0:
636 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all")))
637 if menu.MenuItemCount > 0:
640 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
641 self._scene.pushFree()
643 elif self._mouseState == 'tool':
644 if self.tempMatrix is not None and self._selectedObj is not None:
645 self._selectedObj.applyMatrix(self.tempMatrix)
646 self._scene.pushFree()
647 self._selectObject(self._selectedObj)
648 self.tempMatrix = None
649 self.tool.OnDragEnd()
651 self._mouseState = None
653 def OnMouseMotion(self,e):
654 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
655 p0 -= self.getObjectCenterPos() - self._viewTarget
656 p1 -= self.getObjectCenterPos() - self._viewTarget
658 if e.Dragging() and self._mouseState is not None:
659 if self._mouseState == 'tool':
660 self.tool.OnDrag(p0, p1)
661 elif not e.LeftIsDown() and e.RightIsDown():
662 self._mouseState = 'drag'
663 if wx.GetKeyState(wx.WXK_SHIFT):
664 a = math.cos(math.radians(self._yaw)) / 3.0
665 b = math.sin(math.radians(self._yaw)) / 3.0
666 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
667 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
668 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
669 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
671 self._yaw += e.GetX() - self._mouseX
672 self._pitch -= e.GetY() - self._mouseY
673 if self._pitch > 170:
677 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
678 self._mouseState = 'drag'
679 self._zoom += e.GetY() - self._mouseY
682 if self._zoom > numpy.max(self._machineSize) * 3:
683 self._zoom = numpy.max(self._machineSize) * 3
684 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
685 self._mouseState = 'dragObject'
686 z = max(0, self._mouseClick3DPos[2])
687 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
688 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
693 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
694 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
695 diff = cursorZ1 - cursorZ0
696 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
697 if not e.Dragging() or self._mouseState != 'tool':
698 self.tool.OnMouseMove(p0, p1)
700 self._mouseX = e.GetX()
701 self._mouseY = e.GetY()
703 def OnMouseWheel(self, e):
704 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
705 delta = max(min(delta,4),-4)
706 self._zoom *= 1.0 - delta / 10.0
709 if self._zoom > numpy.max(self._machineSize) * 3:
710 self._zoom = numpy.max(self._machineSize) * 3
713 def OnMouseLeave(self, e):
717 def getMouseRay(self, x, y):
718 if self._viewport is None:
719 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
720 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
721 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
722 p0 -= self._viewTarget
723 p1 -= self._viewTarget
726 def _init3DView(self):
727 # set viewing projection
728 size = self.GetSize()
729 glViewport(0, 0, size.GetWidth(), size.GetHeight())
732 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
734 glDisable(GL_RESCALE_NORMAL)
735 glDisable(GL_LIGHTING)
737 glEnable(GL_DEPTH_TEST)
738 glDisable(GL_CULL_FACE)
740 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
742 glClearColor(0.8, 0.8, 0.8, 1.0)
746 glMatrixMode(GL_PROJECTION)
748 aspect = float(size.GetWidth()) / float(size.GetHeight())
749 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
751 glMatrixMode(GL_MODELVIEW)
753 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
756 if machineCom.machineIsConnected():
757 self.printButton._imageID = 6
758 self.printButton._tooltip = _("Print")
759 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
760 self.printButton._imageID = 2
761 self.printButton._tooltip = _("Toolpath to SD")
763 self.printButton._imageID = 3
764 self.printButton._tooltip = _("Save toolpath")
766 if self._animView is not None:
767 self._viewTarget = self._animView.getPosition()
768 if self._animView.isDone():
769 self._animView = None
770 if self._animZoom is not None:
771 self._zoom = self._animZoom.getPosition()
772 if self._animZoom.isDone():
773 self._animZoom = None
774 if self.viewMode == 'gcode' and self._gcode is not None:
776 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
779 if self._objectShader is None:
780 if opengl.hasShaderSupport():
781 self._objectShader = opengl.GLShader("""
782 varying float light_amount;
786 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
787 gl_FrontColor = gl_Color;
789 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
793 varying float light_amount;
797 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
800 self._objectOverhangShader = opengl.GLShader("""
801 uniform float cosAngle;
802 uniform mat3 rotMatrix;
803 varying float light_amount;
807 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
808 gl_FrontColor = gl_Color;
810 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
812 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
814 light_amount = -10.0;
818 varying float light_amount;
822 if (light_amount == -10.0)
824 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
826 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
830 self._objectLoadShader = opengl.GLShader("""
831 uniform float intensity;
833 varying float light_amount;
837 vec4 tmp = gl_Vertex;
838 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
839 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
840 gl_Position = gl_ModelViewProjectionMatrix * tmp;
841 gl_FrontColor = gl_Color;
843 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
847 uniform float intensity;
848 varying float light_amount;
852 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
855 if self._objectShader == None or not self._objectShader.isValid():
856 self._objectShader = opengl.GLFakeShader()
857 self._objectOverhangShader = opengl.GLFakeShader()
858 self._objectLoadShader = None
860 glTranslate(0,0,-self._zoom)
861 glRotate(-self._pitch, 1,0,0)
862 glRotate(self._yaw, 0,0,1)
863 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
865 self._viewport = glGetIntegerv(GL_VIEWPORT)
866 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
867 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
869 glClearColor(1,1,1,1)
870 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
872 if self.viewMode != 'gcode':
873 for n in xrange(0, len(self._scene.objects())):
874 obj = self._scene.objects()[n]
875 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
876 self._renderObject(obj)
878 if self._mouseX > -1:
880 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
881 if n < len(self._scene.objects()):
882 self._focusObj = self._scene.objects()[n]
884 self._focusObj = None
885 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
886 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
887 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
888 self._mouse3Dpos -= self._viewTarget
891 glTranslate(0,0,-self._zoom)
892 glRotate(-self._pitch, 1,0,0)
893 glRotate(self._yaw, 0,0,1)
894 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
896 if self.viewMode == 'gcode':
897 if self._gcode is not None and self._gcode.layerList is None:
898 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
899 self._gcodeLoadThread.daemon = True
900 self._gcodeLoadThread.start()
901 if self._gcode is not None and self._gcode.layerList is not None:
903 if profile.getMachineSetting('machine_center_is_zero') != 'True':
904 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
906 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
907 for n in xrange(0, drawUpTill):
908 c = 1.0 - float(drawUpTill - n) / 15
910 if len(self._gcodeVBOs) < n + 1:
911 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
912 if time.time() - t > 0.5:
915 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
916 if n == drawUpTill - 1:
917 if len(self._gcodeVBOs[n]) < 9:
918 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
920 self._gcodeVBOs[n][8].render(GL_QUADS)
922 self._gcodeVBOs[n][9].render(GL_QUADS)
924 self._gcodeVBOs[n][10].render(GL_QUADS)
926 self._gcodeVBOs[n][11].render(GL_QUADS)
929 self._gcodeVBOs[n][12].render(GL_QUADS)
930 glColor3f(c/2, c/2, 0.0)
931 self._gcodeVBOs[n][13].render(GL_QUADS)
933 self._gcodeVBOs[n][14].render(GL_QUADS)
934 self._gcodeVBOs[n][15].render(GL_QUADS)
936 self._gcodeVBOs[n][16].render(GL_LINES)
939 self._gcodeVBOs[n][0].render(GL_LINES)
941 self._gcodeVBOs[n][1].render(GL_LINES)
943 self._gcodeVBOs[n][2].render(GL_LINES)
945 self._gcodeVBOs[n][3].render(GL_LINES)
948 self._gcodeVBOs[n][4].render(GL_LINES)
949 glColor3f(c/2, c/2, 0.0)
950 self._gcodeVBOs[n][5].render(GL_LINES)
952 self._gcodeVBOs[n][6].render(GL_LINES)
953 self._gcodeVBOs[n][7].render(GL_LINES)
956 glStencilFunc(GL_ALWAYS, 1, 1)
957 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
959 if self.viewMode == 'overhang':
960 self._objectOverhangShader.bind()
961 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
963 self._objectShader.bind()
964 for obj in self._scene.objects():
965 if obj._loadAnim is not None:
966 if obj._loadAnim.isDone():
971 if self._focusObj == obj:
973 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
976 if self._selectedObj == obj or self._selectedObj is None:
977 #If we want transparent, then first render a solid black model to remove the printer size lines.
978 if self.viewMode == 'transparent':
979 glColor4f(0, 0, 0, 0)
980 self._renderObject(obj)
982 glBlendFunc(GL_ONE, GL_ONE)
983 glDisable(GL_DEPTH_TEST)
985 if self.viewMode == 'xray':
986 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
987 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
988 glEnable(GL_STENCIL_TEST)
990 if self.viewMode == 'overhang':
991 if self._selectedObj == obj and self.tempMatrix is not None:
992 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
994 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
996 if not self._scene.checkPlatform(obj):
997 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
998 self._renderObject(obj)
1000 self._renderObject(obj, brightness)
1001 glDisable(GL_STENCIL_TEST)
1003 glEnable(GL_DEPTH_TEST)
1004 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1006 if self.viewMode == 'xray':
1009 glEnable(GL_STENCIL_TEST)
1010 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1011 glDisable(GL_DEPTH_TEST)
1012 for i in xrange(2, 15, 2):
1013 glStencilFunc(GL_EQUAL, i, 0xFF)
1014 glColor(float(i)/10, float(i)/10, float(i)/5)
1016 glVertex3f(-1000,-1000,-10)
1017 glVertex3f( 1000,-1000,-10)
1018 glVertex3f( 1000, 1000,-10)
1019 glVertex3f(-1000, 1000,-10)
1021 for i in xrange(1, 15, 2):
1022 glStencilFunc(GL_EQUAL, i, 0xFF)
1023 glColor(float(i)/10, 0, 0)
1025 glVertex3f(-1000,-1000,-10)
1026 glVertex3f( 1000,-1000,-10)
1027 glVertex3f( 1000, 1000,-10)
1028 glVertex3f(-1000, 1000,-10)
1031 glDisable(GL_STENCIL_TEST)
1032 glEnable(GL_DEPTH_TEST)
1034 self._objectShader.unbind()
1036 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1038 if self._objectLoadShader is not None:
1039 self._objectLoadShader.bind()
1040 glColor4f(0.2, 0.6, 1.0, 1.0)
1041 for obj in self._scene.objects():
1042 if obj._loadAnim is None:
1044 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1045 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1046 self._renderObject(obj)
1047 self._objectLoadShader.unbind()
1052 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1054 z = self._usbPrintMonitor.getZ()
1055 size = self._machineSize
1056 glColor4ub(255,255,0,128)
1058 glVertex3f(-size[0]/2,-size[1]/2, z)
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)
1064 if self.viewMode == 'gcode':
1065 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1066 glDisable(GL_DEPTH_TEST)
1069 glTranslate(0,-4,-10)
1070 glColor4ub(60,60,60,255)
1071 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1074 #Draw the object box-shadow, so you can see where it will collide with other objects.
1075 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1076 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1078 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1080 glEnable(GL_CULL_FACE)
1081 glColor4f(0,0,0,0.12)
1083 glVertex3f(-size[0], size[1], 0.1)
1084 glVertex3f(-size[0], -size[1], 0.1)
1085 glVertex3f( size[0], -size[1], 0.1)
1086 glVertex3f( size[0], size[1], 0.1)
1088 glDisable(GL_CULL_FACE)
1091 #Draw the outline of the selected object, on top of everything else except the GUI.
1092 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1093 glDisable(GL_DEPTH_TEST)
1094 glEnable(GL_CULL_FACE)
1095 glEnable(GL_STENCIL_TEST)
1097 glStencilFunc(GL_EQUAL, 0, 255)
1099 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1101 glColor4f(1,1,1,0.5)
1102 self._renderObject(self._selectedObj)
1103 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1105 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1106 glDisable(GL_STENCIL_TEST)
1107 glDisable(GL_CULL_FACE)
1108 glEnable(GL_DEPTH_TEST)
1110 if self._selectedObj is not None:
1112 pos = self.getObjectCenterPos()
1113 glTranslate(pos[0], pos[1], pos[2])
1116 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1117 glDisable(GL_DEPTH_TEST)
1120 glTranslate(0,-4,-10)
1121 glColor4ub(60,60,60,255)
1122 opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1125 def _renderObject(self, obj, brightness = False, addSink = True):
1128 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1130 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1132 if self.tempMatrix is not None and obj == self._selectedObj:
1133 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1134 glMultMatrixf(tempMatrix)
1136 offset = obj.getDrawOffset()
1137 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1139 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1140 glMultMatrixf(tempMatrix)
1143 for m in obj._meshList:
1145 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1147 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1152 def _drawMachine(self):
1153 glEnable(GL_CULL_FACE)
1156 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1158 machine = profile.getMachineSetting('machine_type')
1159 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
1160 if machine not in self._platformMesh:
1161 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1163 self._platformMesh[machine] = meshes[0]
1165 self._platformMesh[machine] = None
1166 if profile.getMachineSetting('machine_type') == 'ultimaker2':
1167 self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1169 self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1170 glColor4f(1,1,1,0.5)
1171 self._objectShader.bind()
1172 self._renderObject(self._platformMesh[machine], False, False)
1173 self._objectShader.unbind()
1178 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1179 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1180 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1181 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1182 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1183 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1186 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1187 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1188 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1189 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1190 v4 = [ size[0] / 2, size[1] / 2, 0]
1191 v5 = [ size[0] / 2,-size[1] / 2, 0]
1192 v6 = [-size[0] / 2, size[1] / 2, 0]
1193 v7 = [-size[0] / 2,-size[1] / 2, 0]
1195 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1196 glEnableClientState(GL_VERTEX_ARRAY)
1197 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1199 glColor4ub(5, 171, 231, 64)
1200 glDrawArrays(GL_QUADS, 0, 4)
1201 glColor4ub(5, 171, 231, 96)
1202 glDrawArrays(GL_QUADS, 4, 8)
1203 glColor4ub(5, 171, 231, 128)
1204 glDrawArrays(GL_QUADS, 12, 8)
1205 glDisableClientState(GL_VERTEX_ARRAY)
1207 sx = self._machineSize[0]
1208 sy = self._machineSize[1]
1209 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1210 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1215 x1 = max(min(x1, sx/2), -sx/2)
1216 y1 = max(min(y1, sy/2), -sy/2)
1217 x2 = max(min(x2, sx/2), -sx/2)
1218 y2 = max(min(y2, sy/2), -sy/2)
1219 if (x & 1) == (y & 1):
1220 glColor4ub(5, 171, 231, 127)
1222 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1224 glVertex3f(x1, y1, -0.02)
1225 glVertex3f(x2, y1, -0.02)
1226 glVertex3f(x2, y2, -0.02)
1227 glVertex3f(x1, y2, -0.02)
1231 glDisable(GL_CULL_FACE)
1233 def _generateGCodeVBOs(self, layer):
1235 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1236 if ':' in extrudeType:
1237 extruder = int(extrudeType[extrudeType.find(':')+1:])
1238 extrudeType = extrudeType[0:extrudeType.find(':')]
1241 pointList = numpy.zeros((0,3), numpy.float32)
1243 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1245 a = numpy.concatenate((a[:-1], a[1:]), 1)
1246 a = a.reshape((len(a) * 2, 3))
1247 pointList = numpy.concatenate((pointList, a))
1248 ret.append(opengl.GLVBO(pointList))
1251 def _generateGCodeVBOs2(self, layer):
1252 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1253 filamentArea = math.pi * filamentRadius * filamentRadius
1254 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1257 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1258 if ':' in extrudeType:
1259 extruder = int(extrudeType[extrudeType.find(':')+1:])
1260 extrudeType = extrudeType[0:extrudeType.find(':')]
1263 pointList = numpy.zeros((0,3), numpy.float32)
1265 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1267 if extrudeType == 'FILL':
1270 normal = a[1:] - a[:-1]
1271 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1272 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1275 ePerDist = path['extrusion'][1:] / lens
1277 lineWidth = ePerDist / path['layerThickness'] / 2.0
1279 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1281 normal[:,0] *= lineWidth
1282 normal[:,1] *= lineWidth
1284 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1285 b = numpy.concatenate((b, a[1:] + normal), 1)
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 = b.reshape((len(b) * 4, 3))
1292 normal2 = normal[:-1] + normal[1:]
1293 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1294 normal2[:,0] /= lens2
1295 normal2[:,1] /= lens2
1296 normal2[:,0] *= lineWidth[:-1]
1297 normal2[:,1] *= lineWidth[:-1]
1299 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1300 c = numpy.concatenate((c, a[1:-1]), 1)
1301 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1302 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1303 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1305 c = numpy.concatenate((c, a[1:-1]), 1)
1306 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1307 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1308 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1310 c = c.reshape((len(c) * 8, 3))
1312 pointList = numpy.concatenate((pointList, b, c))
1314 pointList = numpy.concatenate((pointList, b))
1315 ret.append(opengl.GLVBO(pointList))
1317 pointList = numpy.zeros((0,3), numpy.float32)
1319 if path['type'] == 'move':
1320 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1321 a = numpy.concatenate((a[:-1], a[1:]), 1)
1322 a = a.reshape((len(a) * 2, 3))
1323 pointList = numpy.concatenate((pointList, a))
1324 if path['type'] == 'retract':
1325 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1326 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1327 a = a.reshape((len(a) * 2, 3))
1328 pointList = numpy.concatenate((pointList, a))
1329 ret.append(opengl.GLVBO(pointList))
1333 def getObjectCenterPos(self):
1334 if self._selectedObj is None:
1335 return [0.0, 0.0, 0.0]
1336 pos = self._selectedObj.getPosition()
1337 size = self._selectedObj.getSize()
1338 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1340 def getObjectBoundaryCircle(self):
1341 if self._selectedObj is None:
1343 return self._selectedObj.getBoundaryCircle()
1345 def getObjectSize(self):
1346 if self._selectedObj is None:
1347 return [0.0, 0.0, 0.0]
1348 return self._selectedObj.getSize()
1350 def getObjectMatrix(self):
1351 if self._selectedObj is None:
1352 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1353 return self._selectedObj.getMatrix()
1355 class shaderEditor(wx.Dialog):
1356 def __init__(self, parent, callback, v, f):
1357 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1358 self._callback = callback
1359 s = wx.BoxSizer(wx.VERTICAL)
1361 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1362 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1363 s.Add(self._vertex, 1, flag=wx.EXPAND)
1364 s.Add(self._fragment, 1, flag=wx.EXPAND)
1366 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1367 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1369 self.SetPosition(self.GetParent().GetPosition())
1370 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1373 def OnText(self, e):
1374 self._callback(self._vertex.GetValue(), self._fragment.GetValue())