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 = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
55 self._platformMesh._drawOffset = numpy.array([0,0,2.5], numpy.float32)
56 self._isSimpleMode = True
57 self._usbPrintMonitor = printWindow.printProcessMonitor(lambda : self._queueRefresh())
60 self._modelMatrix = None
61 self._projMatrix = None
62 self.tempMatrix = None
64 self.openFileButton = openglGui.glButton(self, 4, 'Load', (0,0), self.showLoadModel)
65 self.printButton = openglGui.glButton(self, 6, 'Print', (1,0), self.OnPrintButton)
66 self.printButton.setDisabled(True)
69 self.rotateToolButton = openglGui.glRadioButton(self, 8, 'Rotate', (0,-1), group, self.OnToolSelect)
70 self.scaleToolButton = openglGui.glRadioButton(self, 9, 'Scale', (1,-1), group, self.OnToolSelect)
71 self.mirrorToolButton = openglGui.glRadioButton(self, 10, 'Mirror', (2,-1), group, self.OnToolSelect)
73 self.resetRotationButton = openglGui.glButton(self, 12, 'Reset', (0,-2), self.OnRotateReset)
74 self.layFlatButton = openglGui.glButton(self, 16, 'Lay flat', (0,-3), self.OnLayFlat)
76 self.resetScaleButton = openglGui.glButton(self, 13, 'Reset', (1,-2), self.OnScaleReset)
77 self.scaleMaxButton = openglGui.glButton(self, 17, 'To max', (1,-3), self.OnScaleMax)
79 self.mirrorXButton = openglGui.glButton(self, 14, 'Mirror X', (2,-2), lambda button: self.OnMirror(0))
80 self.mirrorYButton = openglGui.glButton(self, 18, 'Mirror Y', (2,-3), lambda button: self.OnMirror(1))
81 self.mirrorZButton = openglGui.glButton(self, 22, 'Mirror Z', (2,-4), lambda button: self.OnMirror(2))
83 self.rotateToolButton.setExpandArrow(True)
84 self.scaleToolButton.setExpandArrow(True)
85 self.mirrorToolButton.setExpandArrow(True)
87 self.scaleForm = openglGui.glFrame(self, (2, -2))
88 openglGui.glGuiLayoutGrid(self.scaleForm)
89 openglGui.glLabel(self.scaleForm, 'Scale X', (0,0))
90 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
91 openglGui.glLabel(self.scaleForm, 'Scale Y', (0,1))
92 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
93 openglGui.glLabel(self.scaleForm, 'Scale Z', (0,2))
94 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
95 openglGui.glLabel(self.scaleForm, 'Size X (mm)', (0,4))
96 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
97 openglGui.glLabel(self.scaleForm, 'Size Y (mm)', (0,5))
98 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
99 openglGui.glLabel(self.scaleForm, 'Size Z (mm)', (0,6))
100 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
101 openglGui.glLabel(self.scaleForm, 'Uniform scale', (0,8))
102 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
104 self.viewSelection = openglGui.glComboButton(self, 'View mode', [7,19,11,15,23], ['Normal', 'Overhang', 'Transparent', 'X-Ray', 'Layers'], (-1,0), self.OnViewChange)
105 self.layerSelect = openglGui.glSlider(self, 10000, 0, 1, (-1,-2), lambda : self.QueueRefresh())
107 self.youMagineButton = openglGui.glButton(self, 26, 'YouMagine', (2,0), lambda button: youmagineGui.youmagineManager(self.GetTopLevelParent(), self))
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)
154 if self.viewSelection.getValue() == 4:
155 self.viewSelection.setValue(0)
157 self.loadScene(filenames)
159 def showSaveModel(self):
160 if len(self._scene.objects()) < 1:
162 dlg=wx.FileDialog(self, 'Save 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
163 dlg.SetWildcard(meshLoader.saveWildcardFilter())
164 if dlg.ShowModal() != wx.ID_OK:
167 filename = dlg.GetPath()
169 meshLoader.saveMeshes(filename, self._scene.objects())
171 def OnPrintButton(self, button):
173 if machineCom.machineIsConnected():
174 self.showPrintWindow()
175 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
176 drives = removableStorage.getPossibleSDcardDrives()
178 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))
179 if dlg.ShowModal() != wx.ID_OK:
182 drive = drives[dlg.GetSelection()]
186 filename = self._scene._objectList[0].getName() + '.gcode'
187 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
192 self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, 'Print with USB'))
193 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, 'Save GCode...'))
194 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, 'Slice engine log...'))
198 def showPrintWindow(self):
199 if self._gcodeFilename is None:
201 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
202 if self._gcodeFilename == self._slicer.getGCodeFilename():
203 self._slicer.submitSliceInfoOnline()
205 def showSaveGCode(self):
206 defPath = profile.getPreference('lastFile')
207 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
208 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
209 dlg.SetFilename(self._scene._objectList[0].getName())
210 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
211 if dlg.ShowModal() != wx.ID_OK:
214 filename = dlg.GetPath()
217 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
219 def _copyFile(self, fileA, fileB, allowEject = False):
221 size = float(os.stat(fileA).st_size)
222 with open(fileA, 'rb') as fsrc:
223 with open(fileB, 'wb') as fdst:
225 buf = fsrc.read(16*1024)
229 self.printButton.setProgressBar(float(fsrc.tell()) / size)
234 self.notification.message("Failed to save")
237 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...'))
239 self.notification.message("Saved as %s" % (fileB))
240 self.printButton.setProgressBar(None)
241 if fileA == self._slicer.getGCodeFilename():
242 self._slicer.submitSliceInfoOnline()
244 def _showSliceLog(self):
245 dlg = wx.TextEntryDialog(self, "The slicing engine reported the following", "Engine log...", '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
249 def OnToolSelect(self, button):
250 if self.rotateToolButton.getSelected():
251 self.tool = previewTools.toolRotate(self)
252 elif self.scaleToolButton.getSelected():
253 self.tool = previewTools.toolScale(self)
254 elif self.mirrorToolButton.getSelected():
255 self.tool = previewTools.toolNone(self)
257 self.tool = previewTools.toolNone(self)
258 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
259 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
260 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
261 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
262 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
263 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
264 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
265 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
267 def updateToolButtons(self):
268 if self._selectedObj is None:
272 self.rotateToolButton.setHidden(hidden)
273 self.scaleToolButton.setHidden(hidden)
274 self.mirrorToolButton.setHidden(hidden)
276 self.rotateToolButton.setSelected(False)
277 self.scaleToolButton.setSelected(False)
278 self.mirrorToolButton.setSelected(False)
281 def OnViewChange(self):
282 if self.viewSelection.getValue() == 4:
283 self.viewMode = 'gcode'
284 if self._gcode is not None and self._gcode.layerList is not None:
285 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
286 self._selectObject(None)
287 elif self.viewSelection.getValue() == 1:
288 self.viewMode = 'overhang'
289 elif self.viewSelection.getValue() == 2:
290 self.viewMode = 'transparent'
291 elif self.viewSelection.getValue() == 3:
292 self.viewMode = 'xray'
294 self.viewMode = 'normal'
295 self.layerSelect.setHidden(self.viewMode != 'gcode')
298 def OnRotateReset(self, button):
299 if self._selectedObj is None:
301 self._selectedObj.resetRotation()
302 self._scene.pushFree()
303 self._selectObject(self._selectedObj)
306 def OnLayFlat(self, button):
307 if self._selectedObj is None:
309 self._selectedObj.layFlat()
310 self._scene.pushFree()
311 self._selectObject(self._selectedObj)
314 def OnScaleReset(self, button):
315 if self._selectedObj is None:
317 self._selectedObj.resetScale()
318 self._selectObject(self._selectedObj)
319 self.updateProfileToControls()
322 def OnScaleMax(self, button):
323 if self._selectedObj is None:
325 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
326 self._scene.pushFree()
327 self._selectObject(self._selectedObj)
328 self.updateProfileToControls()
331 def OnMirror(self, axis):
332 if self._selectedObj is None:
334 self._selectedObj.mirror(axis)
337 def OnScaleEntry(self, value, axis):
338 if self._selectedObj is None:
344 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
345 self.updateProfileToControls()
346 self._scene.pushFree()
347 self._selectObject(self._selectedObj)
350 def OnScaleEntryMM(self, value, axis):
351 if self._selectedObj is None:
357 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
358 self.updateProfileToControls()
359 self._scene.pushFree()
360 self._selectObject(self._selectedObj)
363 def OnDeleteAll(self, e):
364 while len(self._scene.objects()) > 0:
365 self._deleteObject(self._scene.objects()[0])
366 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
368 def OnMultiply(self, e):
369 if self._focusObj is None:
372 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
373 if dlg.ShowModal() != wx.ID_OK:
382 self._scene.add(newObj)
383 self._scene.centerAll()
384 if not self._scene.checkPlatform(newObj):
389 self.notification.message("Could not create more then %d items" % (n - 1))
390 self._scene.remove(newObj)
391 self._scene.centerAll()
394 def OnSplitObject(self, e):
395 if self._focusObj is None:
397 self._scene.remove(self._focusObj)
398 for obj in self._focusObj.split(self._splitCallback):
399 if numpy.max(obj.getSize()) > 2.0:
401 self._scene.centerAll()
402 self._selectObject(None)
405 def _splitCallback(self, progress):
408 def OnMergeObjects(self, e):
409 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
411 self._scene.merge(self._selectedObj, self._focusObj)
414 def sceneUpdated(self):
415 self._sceneUpdateTimer.Start(500, True)
416 self._slicer.abortSlicer()
417 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
420 def _onRunSlicer(self, e):
421 if self._isSimpleMode:
422 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
423 self._slicer.runSlicer(self._scene)
424 if self._isSimpleMode:
425 profile.resetTempOverride()
427 def _updateSliceProgress(self, progressValue, ready):
429 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
431 self.printButton.setDisabled(not ready)
432 if progressValue >= 0.0:
433 self.printButton.setProgressBar(progressValue)
435 self.printButton.setProgressBar(None)
436 if self._gcode is not None:
438 for layerVBOlist in self._gcodeVBOs:
439 for vbo in layerVBOlist:
440 self.glReleaseList.append(vbo)
443 self.printButton.setProgressBar(None)
444 cost = self._slicer.getFilamentCost()
446 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
448 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
449 self._gcode = gcodeInterpreter.gcode()
450 self._gcodeFilename = self._slicer.getGCodeFilename()
452 self.printButton.setBottomText('')
455 def _loadGCode(self):
456 self._gcode.progressCallback = self._gcodeLoadCallback
457 self._gcode.load(self._gcodeFilename)
459 def _gcodeLoadCallback(self, progress):
460 if self._gcode is None:
462 if len(self._gcode.layerList) % 15 == 0:
464 if self._gcode is None:
466 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
467 if self.viewMode == 'gcode':
471 def loadScene(self, fileList):
472 for filename in fileList:
474 objList = meshLoader.loadMeshes(filename)
476 traceback.print_exc()
479 if self._objectLoadShader is not None:
480 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
484 self._scene.centerAll()
485 self._selectObject(obj)
486 if obj.getScale()[0] < 1.0:
487 self.notification.message("Warning: Object scaled down.")
490 def _deleteObject(self, obj):
491 if obj == self._selectedObj:
492 self._selectObject(None)
493 if obj == self._focusObj:
494 self._focusObj = None
495 self._scene.remove(obj)
496 for m in obj._meshList:
497 if m.vbo is not None and m.vbo.decRef():
498 self.glReleaseList.append(m.vbo)
503 def _selectObject(self, obj, zoom = True):
504 if obj != self._selectedObj:
505 self._selectedObj = obj
506 self.updateProfileToControls()
507 self.updateToolButtons()
508 if zoom and obj is not None:
509 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
510 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
511 newZoom = obj.getBoundaryCircle() * 6
512 if newZoom > numpy.max(self._machineSize) * 3:
513 newZoom = numpy.max(self._machineSize) * 3
514 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
516 def updateProfileToControls(self):
517 oldSimpleMode = self._isSimpleMode
518 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
519 if self._isSimpleMode != oldSimpleMode:
520 self._scene.arrangeAll()
522 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
523 self._objColors[0] = profile.getPreferenceColour('model_colour')
524 self._objColors[1] = profile.getPreferenceColour('model_colour2')
525 self._objColors[2] = profile.getPreferenceColour('model_colour3')
526 self._objColors[3] = profile.getPreferenceColour('model_colour4')
527 self._scene.setMachineSize(self._machineSize)
528 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
529 self._scene.setHeadSize(profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_min_y'), profile.getPreferenceFloat('extruder_head_size_max_y'), profile.getPreferenceFloat('extruder_head_size_height'))
531 if self._selectedObj is not None:
532 scale = self._selectedObj.getScale()
533 size = self._selectedObj.getSize()
534 self.scaleXctrl.setValue(round(scale[0], 2))
535 self.scaleYctrl.setValue(round(scale[1], 2))
536 self.scaleZctrl.setValue(round(scale[2], 2))
537 self.scaleXmmctrl.setValue(round(size[0], 2))
538 self.scaleYmmctrl.setValue(round(size[1], 2))
539 self.scaleZmmctrl.setValue(round(size[2], 2))
541 def OnKeyChar(self, keyCode):
542 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
543 if self._selectedObj is not None:
544 self._deleteObject(self._selectedObj)
546 if keyCode == wx.WXK_UP:
547 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
549 elif keyCode == wx.WXK_DOWN:
550 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
552 elif keyCode == wx.WXK_PAGEUP:
553 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
555 elif keyCode == wx.WXK_PAGEDOWN:
556 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
559 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
560 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
561 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
562 from collections import defaultdict
563 from gc import get_objects
564 self._beforeLeakTest = defaultdict(int)
565 for i in get_objects():
566 self._beforeLeakTest[type(i)] += 1
567 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
568 from collections import defaultdict
569 from gc import get_objects
570 self._afterLeakTest = defaultdict(int)
571 for i in get_objects():
572 self._afterLeakTest[type(i)] += 1
573 for k in self._afterLeakTest:
574 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
575 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
577 def ShaderUpdate(self, v, f):
578 s = opengl.GLShader(v, f)
580 self._objectLoadShader.release()
581 self._objectLoadShader = s
582 for obj in self._scene.objects():
583 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
586 def OnMouseDown(self,e):
587 self._mouseX = e.GetX()
588 self._mouseY = e.GetY()
589 self._mouseClick3DPos = self._mouse3Dpos
590 self._mouseClickFocus = self._focusObj
592 self._mouseState = 'doubleClick'
594 self._mouseState = 'dragOrClick'
595 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
596 p0 -= self.getObjectCenterPos() - self._viewTarget
597 p1 -= self.getObjectCenterPos() - self._viewTarget
598 if self.tool.OnDragStart(p0, p1):
599 self._mouseState = 'tool'
600 if self._mouseState == 'dragOrClick':
601 if e.GetButton() == 1:
602 if self._focusObj is not None:
603 self._selectObject(self._focusObj, False)
606 def OnMouseUp(self, e):
607 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
609 if self._mouseState == 'dragOrClick':
610 if e.GetButton() == 1:
611 self._selectObject(self._focusObj)
612 if e.GetButton() == 3:
614 if self._focusObj is not None:
615 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
616 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
617 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
618 if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1:
619 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
620 if len(self._scene.objects()) > 0:
621 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
622 if menu.MenuItemCount > 0:
625 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
626 self._scene.pushFree()
628 elif self._mouseState == 'tool':
629 if self.tempMatrix is not None and self._selectedObj is not None:
630 self._selectedObj.applyMatrix(self.tempMatrix)
631 self._scene.pushFree()
632 self._selectObject(self._selectedObj)
633 self.tempMatrix = None
634 self.tool.OnDragEnd()
636 self._mouseState = None
638 def OnMouseMotion(self,e):
639 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
640 p0 -= self.getObjectCenterPos() - self._viewTarget
641 p1 -= self.getObjectCenterPos() - self._viewTarget
643 if e.Dragging() and self._mouseState is not None:
644 if self._mouseState == 'tool':
645 self.tool.OnDrag(p0, p1)
646 elif not e.LeftIsDown() and e.RightIsDown():
647 self._mouseState = 'drag'
648 if wx.GetKeyState(wx.WXK_SHIFT):
649 a = math.cos(math.radians(self._yaw)) / 3.0
650 b = math.sin(math.radians(self._yaw)) / 3.0
651 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
652 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
653 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
654 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
656 self._yaw += e.GetX() - self._mouseX
657 self._pitch -= e.GetY() - self._mouseY
658 if self._pitch > 170:
662 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
663 self._mouseState = 'drag'
664 self._zoom += e.GetY() - self._mouseY
667 if self._zoom > numpy.max(self._machineSize) * 3:
668 self._zoom = numpy.max(self._machineSize) * 3
669 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
670 self._mouseState = 'dragObject'
671 z = max(0, self._mouseClick3DPos[2])
672 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
673 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
678 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
679 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
680 diff = cursorZ1 - cursorZ0
681 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
682 if not e.Dragging() or self._mouseState != 'tool':
683 self.tool.OnMouseMove(p0, p1)
685 self._mouseX = e.GetX()
686 self._mouseY = e.GetY()
688 def OnMouseWheel(self, e):
689 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
690 delta = max(min(delta,4),-4)
691 self._zoom *= 1.0 - delta / 10.0
694 if self._zoom > numpy.max(self._machineSize) * 3:
695 self._zoom = numpy.max(self._machineSize) * 3
698 def OnMouseLeave(self, e):
702 def getMouseRay(self, x, y):
703 if self._viewport is None:
704 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
705 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
706 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
707 p0 -= self._viewTarget
708 p1 -= self._viewTarget
711 def _init3DView(self):
712 # set viewing projection
713 size = self.GetSize()
714 glViewport(0, 0, size.GetWidth(), size.GetHeight())
717 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
719 glDisable(GL_RESCALE_NORMAL)
720 glDisable(GL_LIGHTING)
722 glEnable(GL_DEPTH_TEST)
723 glDisable(GL_CULL_FACE)
725 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
727 glClearColor(0.8, 0.8, 0.8, 1.0)
731 glMatrixMode(GL_PROJECTION)
733 aspect = float(size.GetWidth()) / float(size.GetHeight())
734 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
736 glMatrixMode(GL_MODELVIEW)
738 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
741 if machineCom.machineIsConnected():
742 self.printButton._imageID = 6
743 self.printButton._tooltip = 'Print'
744 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
745 self.printButton._imageID = 2
746 self.printButton._tooltip = 'Toolpath to SD'
748 self.printButton._imageID = 3
749 self.printButton._tooltip = 'Save toolpath'
751 if self._animView is not None:
752 self._viewTarget = self._animView.getPosition()
753 if self._animView.isDone():
754 self._animView = None
755 if self._animZoom is not None:
756 self._zoom = self._animZoom.getPosition()
757 if self._animZoom.isDone():
758 self._animZoom = None
759 if self.viewMode == 'gcode' and self._gcode is not None:
761 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
764 if self._objectShader is None:
765 if opengl.hasShaderSupport():
766 self._objectShader = opengl.GLShader("""
767 varying float light_amount;
771 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
772 gl_FrontColor = gl_Color;
774 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
778 varying float light_amount;
782 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
785 self._objectOverhangShader = opengl.GLShader("""
786 uniform float cosAngle;
787 uniform mat3 rotMatrix;
788 varying float light_amount;
792 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
793 gl_FrontColor = gl_Color;
795 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
797 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
799 light_amount = -10.0;
803 varying float light_amount;
807 if (light_amount == -10.0)
809 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
811 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
815 self._objectLoadShader = opengl.GLShader("""
816 uniform float intensity;
818 varying float light_amount;
822 vec4 tmp = gl_Vertex;
823 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
824 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
825 gl_Position = gl_ModelViewProjectionMatrix * tmp;
826 gl_FrontColor = gl_Color;
828 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
832 uniform float intensity;
833 varying float light_amount;
837 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
840 if self._objectShader == None or not self._objectShader.isValid():
841 self._objectShader = opengl.GLFakeShader()
842 self._objectOverhangShader = opengl.GLFakeShader()
843 self._objectLoadShader = None
845 glTranslate(0,0,-self._zoom)
846 glRotate(-self._pitch, 1,0,0)
847 glRotate(self._yaw, 0,0,1)
848 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
850 self._viewport = glGetIntegerv(GL_VIEWPORT)
851 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
852 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
854 glClearColor(1,1,1,1)
855 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
857 if self.viewMode != 'gcode':
858 for n in xrange(0, len(self._scene.objects())):
859 obj = self._scene.objects()[n]
860 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
861 self._renderObject(obj)
863 if self._mouseX > -1:
865 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
866 if n < len(self._scene.objects()):
867 self._focusObj = self._scene.objects()[n]
869 self._focusObj = None
870 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
871 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
872 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
873 self._mouse3Dpos -= self._viewTarget
876 glTranslate(0,0,-self._zoom)
877 glRotate(-self._pitch, 1,0,0)
878 glRotate(self._yaw, 0,0,1)
879 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
881 if self.viewMode == 'gcode':
882 if self._gcode is not None and self._gcode.layerList is None:
883 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
884 self._gcodeLoadThread.daemon = True
885 self._gcodeLoadThread.start()
886 if self._gcode is not None and self._gcode.layerList is not None:
888 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
890 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
891 for n in xrange(0, drawUpTill):
892 c = 1.0 - float(drawUpTill - n) / 15
894 if len(self._gcodeVBOs) < n + 1:
895 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
896 if time.time() - t > 0.5:
899 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
900 if n == drawUpTill - 1:
901 if len(self._gcodeVBOs[n]) < 9:
902 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
904 self._gcodeVBOs[n][8].render(GL_QUADS)
906 self._gcodeVBOs[n][9].render(GL_QUADS)
908 self._gcodeVBOs[n][10].render(GL_QUADS)
910 self._gcodeVBOs[n][11].render(GL_QUADS)
913 self._gcodeVBOs[n][12].render(GL_QUADS)
914 glColor3f(c/2, c/2, 0.0)
915 self._gcodeVBOs[n][13].render(GL_QUADS)
917 self._gcodeVBOs[n][14].render(GL_QUADS)
918 self._gcodeVBOs[n][15].render(GL_QUADS)
920 self._gcodeVBOs[n][16].render(GL_LINES)
923 self._gcodeVBOs[n][0].render(GL_LINES)
925 self._gcodeVBOs[n][1].render(GL_LINES)
927 self._gcodeVBOs[n][2].render(GL_LINES)
929 self._gcodeVBOs[n][3].render(GL_LINES)
932 self._gcodeVBOs[n][4].render(GL_LINES)
933 glColor3f(c/2, c/2, 0.0)
934 self._gcodeVBOs[n][5].render(GL_LINES)
936 self._gcodeVBOs[n][6].render(GL_LINES)
937 self._gcodeVBOs[n][7].render(GL_LINES)
940 glStencilFunc(GL_ALWAYS, 1, 1)
941 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
943 if self.viewMode == 'overhang':
944 self._objectOverhangShader.bind()
945 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
947 self._objectShader.bind()
948 for obj in self._scene.objects():
949 if obj._loadAnim is not None:
950 if obj._loadAnim.isDone():
955 if self._focusObj == obj:
957 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
960 if self._selectedObj == obj or self._selectedObj is None:
961 #If we want transparent, then first render a solid black model to remove the printer size lines.
962 if self.viewMode == 'transparent':
963 glColor4f(0, 0, 0, 0)
964 self._renderObject(obj)
966 glBlendFunc(GL_ONE, GL_ONE)
967 glDisable(GL_DEPTH_TEST)
969 if self.viewMode == 'xray':
970 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
971 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
972 glEnable(GL_STENCIL_TEST)
974 if self.viewMode == 'overhang':
975 if self._selectedObj == obj and self.tempMatrix is not None:
976 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
978 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
980 if not self._scene.checkPlatform(obj):
981 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
982 self._renderObject(obj)
984 self._renderObject(obj, brightness)
985 glDisable(GL_STENCIL_TEST)
987 glEnable(GL_DEPTH_TEST)
988 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
990 if self.viewMode == 'xray':
993 glEnable(GL_STENCIL_TEST)
994 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
995 glDisable(GL_DEPTH_TEST)
996 for i in xrange(2, 15, 2):
997 glStencilFunc(GL_EQUAL, i, 0xFF)
998 glColor(float(i)/10, float(i)/10, float(i)/5)
1000 glVertex3f(-1000,-1000,-10)
1001 glVertex3f( 1000,-1000,-10)
1002 glVertex3f( 1000, 1000,-10)
1003 glVertex3f(-1000, 1000,-10)
1005 for i in xrange(1, 15, 2):
1006 glStencilFunc(GL_EQUAL, i, 0xFF)
1007 glColor(float(i)/10, 0, 0)
1009 glVertex3f(-1000,-1000,-10)
1010 glVertex3f( 1000,-1000,-10)
1011 glVertex3f( 1000, 1000,-10)
1012 glVertex3f(-1000, 1000,-10)
1015 glDisable(GL_STENCIL_TEST)
1016 glEnable(GL_DEPTH_TEST)
1018 self._objectShader.unbind()
1020 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1022 if self._objectLoadShader is not None:
1023 self._objectLoadShader.bind()
1024 glColor4f(0.2, 0.6, 1.0, 1.0)
1025 for obj in self._scene.objects():
1026 if obj._loadAnim is None:
1028 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1029 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1030 self._renderObject(obj)
1031 self._objectLoadShader.unbind()
1036 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1038 z = self._usbPrintMonitor.getZ()
1039 size = self._machineSize
1040 glColor4ub(255,255,0,128)
1042 glVertex3f(-size[0]/2,-size[1]/2, z)
1043 glVertex3f( size[0]/2,-size[1]/2, z)
1044 glVertex3f( size[0]/2, size[1]/2, z)
1045 glVertex3f(-size[0]/2, size[1]/2, z)
1048 if self.viewMode == 'gcode':
1049 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1050 glDisable(GL_DEPTH_TEST)
1053 glTranslate(0,-4,-10)
1054 glColor4ub(60,60,60,255)
1055 opengl.glDrawStringCenter('Loading toolpath for visualization...')
1058 #Draw the object box-shadow, so you can see where it will collide with other objects.
1059 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1060 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1062 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1064 glEnable(GL_CULL_FACE)
1065 glColor4f(0,0,0,0.12)
1067 glVertex3f(-size[0], size[1], 0.1)
1068 glVertex3f(-size[0], -size[1], 0.1)
1069 glVertex3f( size[0], -size[1], 0.1)
1070 glVertex3f( size[0], size[1], 0.1)
1072 glDisable(GL_CULL_FACE)
1075 #Draw the outline of the selected object, on top of everything else except the GUI.
1076 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1077 glDisable(GL_DEPTH_TEST)
1078 glEnable(GL_CULL_FACE)
1079 glEnable(GL_STENCIL_TEST)
1081 glStencilFunc(GL_EQUAL, 0, 255)
1083 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1085 glColor4f(1,1,1,0.5)
1086 self._renderObject(self._selectedObj)
1087 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1089 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1090 glDisable(GL_STENCIL_TEST)
1091 glDisable(GL_CULL_FACE)
1092 glEnable(GL_DEPTH_TEST)
1094 if self._selectedObj is not None:
1096 pos = self.getObjectCenterPos()
1097 glTranslate(pos[0], pos[1], pos[2])
1100 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1101 glDisable(GL_DEPTH_TEST)
1104 glTranslate(0,-4,-10)
1105 glColor4ub(60,60,60,255)
1106 opengl.glDrawStringCenter('Overhang view not working due to lack of OpenGL shaders support.')
1109 def _renderObject(self, obj, brightness = False, addSink = True):
1112 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1114 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1116 if self.tempMatrix is not None and obj == self._selectedObj:
1117 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1118 glMultMatrixf(tempMatrix)
1120 offset = obj.getDrawOffset()
1121 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1123 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1124 glMultMatrixf(tempMatrix)
1127 for m in obj._meshList:
1129 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1131 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1136 def _drawMachine(self):
1137 glEnable(GL_CULL_FACE)
1140 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
1142 if profile.getPreference('machine_type') == 'ultimaker':
1143 glColor4f(1,1,1,0.5)
1144 self._objectShader.bind()
1145 self._renderObject(self._platformMesh, False, False)
1146 self._objectShader.unbind()
1151 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1152 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1153 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1154 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1155 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1156 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1159 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1160 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1161 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1162 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1163 v4 = [ size[0] / 2, size[1] / 2, 0]
1164 v5 = [ size[0] / 2,-size[1] / 2, 0]
1165 v6 = [-size[0] / 2, size[1] / 2, 0]
1166 v7 = [-size[0] / 2,-size[1] / 2, 0]
1168 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1169 glEnableClientState(GL_VERTEX_ARRAY)
1170 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1172 glColor4ub(5, 171, 231, 64)
1173 glDrawArrays(GL_QUADS, 0, 4)
1174 glColor4ub(5, 171, 231, 96)
1175 glDrawArrays(GL_QUADS, 4, 8)
1176 glColor4ub(5, 171, 231, 128)
1177 glDrawArrays(GL_QUADS, 12, 8)
1178 glDisableClientState(GL_VERTEX_ARRAY)
1180 sx = self._machineSize[0]
1181 sy = self._machineSize[1]
1182 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1183 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1188 x1 = max(min(x1, sx/2), -sx/2)
1189 y1 = max(min(y1, sy/2), -sy/2)
1190 x2 = max(min(x2, sx/2), -sx/2)
1191 y2 = max(min(y2, sy/2), -sy/2)
1192 if (x & 1) == (y & 1):
1193 glColor4ub(5, 171, 231, 127)
1195 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1197 glVertex3f(x1, y1, -0.02)
1198 glVertex3f(x2, y1, -0.02)
1199 glVertex3f(x2, y2, -0.02)
1200 glVertex3f(x1, y2, -0.02)
1204 glDisable(GL_CULL_FACE)
1206 def _generateGCodeVBOs(self, layer):
1208 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1209 if ':' in extrudeType:
1210 extruder = int(extrudeType[extrudeType.find(':')+1:])
1211 extrudeType = extrudeType[0:extrudeType.find(':')]
1214 pointList = numpy.zeros((0,3), numpy.float32)
1216 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1218 a = numpy.concatenate((a[:-1], a[1:]), 1)
1219 a = a.reshape((len(a) * 2, 3))
1220 pointList = numpy.concatenate((pointList, a))
1221 ret.append(opengl.GLVBO(pointList))
1224 def _generateGCodeVBOs2(self, layer):
1225 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1226 filamentArea = math.pi * filamentRadius * filamentRadius
1229 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1230 if ':' in extrudeType:
1231 extruder = int(extrudeType[extrudeType.find(':')+1:])
1232 extrudeType = extrudeType[0:extrudeType.find(':')]
1235 pointList = numpy.zeros((0,3), numpy.float32)
1237 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1239 if extrudeType == 'FILL':
1242 normal = a[1:] - a[:-1]
1243 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1244 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1247 ePerDist = path['extrusion'][1:] / lens
1248 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1250 normal[:,0] *= lineWidth
1251 normal[:,1] *= lineWidth
1253 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1254 b = numpy.concatenate((b, a[1:] + normal), 1)
1255 b = numpy.concatenate((b, a[1:] - normal), 1)
1256 b = numpy.concatenate((b, a[:-1] - normal), 1)
1257 b = numpy.concatenate((b, a[:-1] + normal), 1)
1258 b = b.reshape((len(b) * 4, 3))
1261 normal2 = normal[:-1] + normal[1:]
1262 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1263 normal2[:,0] /= lens2
1264 normal2[:,1] /= lens2
1265 normal2[:,0] *= lineWidth[:-1]
1266 normal2[:,1] *= lineWidth[:-1]
1268 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1269 c = numpy.concatenate((c, a[1:-1]), 1)
1270 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1271 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1272 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1274 c = numpy.concatenate((c, a[1:-1]), 1)
1275 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1276 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1277 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1279 c = c.reshape((len(c) * 8, 3))
1281 pointList = numpy.concatenate((pointList, b, c))
1283 pointList = numpy.concatenate((pointList, b))
1284 ret.append(opengl.GLVBO(pointList))
1286 pointList = numpy.zeros((0,3), numpy.float32)
1288 if path['type'] == 'move':
1289 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1290 a = numpy.concatenate((a[:-1], a[1:]), 1)
1291 a = a.reshape((len(a) * 2, 3))
1292 pointList = numpy.concatenate((pointList, a))
1293 if path['type'] == 'retract':
1294 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1295 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1296 a = a.reshape((len(a) * 2, 3))
1297 pointList = numpy.concatenate((pointList, a))
1298 ret.append(opengl.GLVBO(pointList))
1302 def getObjectCenterPos(self):
1303 if self._selectedObj is None:
1304 return [0.0, 0.0, 0.0]
1305 pos = self._selectedObj.getPosition()
1306 size = self._selectedObj.getSize()
1307 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1309 def getObjectBoundaryCircle(self):
1310 if self._selectedObj is None:
1312 return self._selectedObj.getBoundaryCircle()
1314 def getObjectSize(self):
1315 if self._selectedObj is None:
1316 return [0.0, 0.0, 0.0]
1317 return self._selectedObj.getSize()
1319 def getObjectMatrix(self):
1320 if self._selectedObj is None:
1321 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1322 return self._selectedObj.getMatrix()
1324 class shaderEditor(wx.Dialog):
1325 def __init__(self, parent, callback, v, f):
1326 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1327 self._callback = callback
1328 s = wx.BoxSizer(wx.VERTICAL)
1330 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1331 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1332 s.Add(self._vertex, 1, flag=wx.EXPAND)
1333 s.Add(self._fragment, 1, flag=wx.EXPAND)
1335 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1336 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1338 self.SetPosition(self.GetParent().GetPosition())
1339 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1342 def OnText(self, e):
1343 self._callback(self._vertex.GetValue(), self._fragment.GetValue())