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 upload', (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 if profile.getPreference('machine_center_is_zero') != 'True':
889 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
891 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
892 for n in xrange(0, drawUpTill):
893 c = 1.0 - float(drawUpTill - n) / 15
895 if len(self._gcodeVBOs) < n + 1:
896 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
897 if time.time() - t > 0.5:
900 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
901 if n == drawUpTill - 1:
902 if len(self._gcodeVBOs[n]) < 9:
903 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
905 self._gcodeVBOs[n][8].render(GL_QUADS)
907 self._gcodeVBOs[n][9].render(GL_QUADS)
909 self._gcodeVBOs[n][10].render(GL_QUADS)
911 self._gcodeVBOs[n][11].render(GL_QUADS)
914 self._gcodeVBOs[n][12].render(GL_QUADS)
915 glColor3f(c/2, c/2, 0.0)
916 self._gcodeVBOs[n][13].render(GL_QUADS)
918 self._gcodeVBOs[n][14].render(GL_QUADS)
919 self._gcodeVBOs[n][15].render(GL_QUADS)
921 self._gcodeVBOs[n][16].render(GL_LINES)
924 self._gcodeVBOs[n][0].render(GL_LINES)
926 self._gcodeVBOs[n][1].render(GL_LINES)
928 self._gcodeVBOs[n][2].render(GL_LINES)
930 self._gcodeVBOs[n][3].render(GL_LINES)
933 self._gcodeVBOs[n][4].render(GL_LINES)
934 glColor3f(c/2, c/2, 0.0)
935 self._gcodeVBOs[n][5].render(GL_LINES)
937 self._gcodeVBOs[n][6].render(GL_LINES)
938 self._gcodeVBOs[n][7].render(GL_LINES)
941 glStencilFunc(GL_ALWAYS, 1, 1)
942 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
944 if self.viewMode == 'overhang':
945 self._objectOverhangShader.bind()
946 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
948 self._objectShader.bind()
949 for obj in self._scene.objects():
950 if obj._loadAnim is not None:
951 if obj._loadAnim.isDone():
956 if self._focusObj == obj:
958 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
961 if self._selectedObj == obj or self._selectedObj is None:
962 #If we want transparent, then first render a solid black model to remove the printer size lines.
963 if self.viewMode == 'transparent':
964 glColor4f(0, 0, 0, 0)
965 self._renderObject(obj)
967 glBlendFunc(GL_ONE, GL_ONE)
968 glDisable(GL_DEPTH_TEST)
970 if self.viewMode == 'xray':
971 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
972 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
973 glEnable(GL_STENCIL_TEST)
975 if self.viewMode == 'overhang':
976 if self._selectedObj == obj and self.tempMatrix is not None:
977 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
979 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
981 if not self._scene.checkPlatform(obj):
982 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
983 self._renderObject(obj)
985 self._renderObject(obj, brightness)
986 glDisable(GL_STENCIL_TEST)
988 glEnable(GL_DEPTH_TEST)
989 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
991 if self.viewMode == 'xray':
994 glEnable(GL_STENCIL_TEST)
995 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
996 glDisable(GL_DEPTH_TEST)
997 for i in xrange(2, 15, 2):
998 glStencilFunc(GL_EQUAL, i, 0xFF)
999 glColor(float(i)/10, float(i)/10, float(i)/5)
1001 glVertex3f(-1000,-1000,-10)
1002 glVertex3f( 1000,-1000,-10)
1003 glVertex3f( 1000, 1000,-10)
1004 glVertex3f(-1000, 1000,-10)
1006 for i in xrange(1, 15, 2):
1007 glStencilFunc(GL_EQUAL, i, 0xFF)
1008 glColor(float(i)/10, 0, 0)
1010 glVertex3f(-1000,-1000,-10)
1011 glVertex3f( 1000,-1000,-10)
1012 glVertex3f( 1000, 1000,-10)
1013 glVertex3f(-1000, 1000,-10)
1016 glDisable(GL_STENCIL_TEST)
1017 glEnable(GL_DEPTH_TEST)
1019 self._objectShader.unbind()
1021 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1023 if self._objectLoadShader is not None:
1024 self._objectLoadShader.bind()
1025 glColor4f(0.2, 0.6, 1.0, 1.0)
1026 for obj in self._scene.objects():
1027 if obj._loadAnim is None:
1029 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1030 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1031 self._renderObject(obj)
1032 self._objectLoadShader.unbind()
1037 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1039 z = self._usbPrintMonitor.getZ()
1040 size = self._machineSize
1041 glColor4ub(255,255,0,128)
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)
1046 glVertex3f(-size[0]/2, size[1]/2, z)
1049 if self.viewMode == 'gcode':
1050 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1051 glDisable(GL_DEPTH_TEST)
1054 glTranslate(0,-4,-10)
1055 glColor4ub(60,60,60,255)
1056 opengl.glDrawStringCenter('Loading toolpath for visualization...')
1059 #Draw the object box-shadow, so you can see where it will collide with other objects.
1060 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1061 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1063 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1065 glEnable(GL_CULL_FACE)
1066 glColor4f(0,0,0,0.12)
1068 glVertex3f(-size[0], size[1], 0.1)
1069 glVertex3f(-size[0], -size[1], 0.1)
1070 glVertex3f( size[0], -size[1], 0.1)
1071 glVertex3f( size[0], size[1], 0.1)
1073 glDisable(GL_CULL_FACE)
1076 #Draw the outline of the selected object, on top of everything else except the GUI.
1077 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1078 glDisable(GL_DEPTH_TEST)
1079 glEnable(GL_CULL_FACE)
1080 glEnable(GL_STENCIL_TEST)
1082 glStencilFunc(GL_EQUAL, 0, 255)
1084 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1086 glColor4f(1,1,1,0.5)
1087 self._renderObject(self._selectedObj)
1088 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1090 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1091 glDisable(GL_STENCIL_TEST)
1092 glDisable(GL_CULL_FACE)
1093 glEnable(GL_DEPTH_TEST)
1095 if self._selectedObj is not None:
1097 pos = self.getObjectCenterPos()
1098 glTranslate(pos[0], pos[1], pos[2])
1101 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1102 glDisable(GL_DEPTH_TEST)
1105 glTranslate(0,-4,-10)
1106 glColor4ub(60,60,60,255)
1107 opengl.glDrawStringCenter('Overhang view not working due to lack of OpenGL shaders support.')
1110 def _renderObject(self, obj, brightness = False, addSink = True):
1113 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1115 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1117 if self.tempMatrix is not None and obj == self._selectedObj:
1118 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1119 glMultMatrixf(tempMatrix)
1121 offset = obj.getDrawOffset()
1122 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1124 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1125 glMultMatrixf(tempMatrix)
1128 for m in obj._meshList:
1130 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1132 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1137 def _drawMachine(self):
1138 glEnable(GL_CULL_FACE)
1141 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
1143 if profile.getPreference('machine_type') == 'ultimaker':
1144 glColor4f(1,1,1,0.5)
1145 self._objectShader.bind()
1146 self._renderObject(self._platformMesh, False, False)
1147 self._objectShader.unbind()
1152 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1153 glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1154 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1155 glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1156 glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1157 glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1160 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1161 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1162 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1163 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1164 v4 = [ size[0] / 2, size[1] / 2, 0]
1165 v5 = [ size[0] / 2,-size[1] / 2, 0]
1166 v6 = [-size[0] / 2, size[1] / 2, 0]
1167 v7 = [-size[0] / 2,-size[1] / 2, 0]
1169 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1170 glEnableClientState(GL_VERTEX_ARRAY)
1171 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1173 glColor4ub(5, 171, 231, 64)
1174 glDrawArrays(GL_QUADS, 0, 4)
1175 glColor4ub(5, 171, 231, 96)
1176 glDrawArrays(GL_QUADS, 4, 8)
1177 glColor4ub(5, 171, 231, 128)
1178 glDrawArrays(GL_QUADS, 12, 8)
1179 glDisableClientState(GL_VERTEX_ARRAY)
1181 sx = self._machineSize[0]
1182 sy = self._machineSize[1]
1183 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1184 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1189 x1 = max(min(x1, sx/2), -sx/2)
1190 y1 = max(min(y1, sy/2), -sy/2)
1191 x2 = max(min(x2, sx/2), -sx/2)
1192 y2 = max(min(y2, sy/2), -sy/2)
1193 if (x & 1) == (y & 1):
1194 glColor4ub(5, 171, 231, 127)
1196 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1198 glVertex3f(x1, y1, -0.02)
1199 glVertex3f(x2, y1, -0.02)
1200 glVertex3f(x2, y2, -0.02)
1201 glVertex3f(x1, y2, -0.02)
1205 glDisable(GL_CULL_FACE)
1207 def _generateGCodeVBOs(self, layer):
1209 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1210 if ':' in extrudeType:
1211 extruder = int(extrudeType[extrudeType.find(':')+1:])
1212 extrudeType = extrudeType[0:extrudeType.find(':')]
1215 pointList = numpy.zeros((0,3), numpy.float32)
1217 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1219 a = numpy.concatenate((a[:-1], a[1:]), 1)
1220 a = a.reshape((len(a) * 2, 3))
1221 pointList = numpy.concatenate((pointList, a))
1222 ret.append(opengl.GLVBO(pointList))
1225 def _generateGCodeVBOs2(self, layer):
1226 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1227 filamentArea = math.pi * filamentRadius * filamentRadius
1228 useFilamentArea = profile.getPreference('gcode_flavor') == 'UltiGCode'
1231 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1232 if ':' in extrudeType:
1233 extruder = int(extrudeType[extrudeType.find(':')+1:])
1234 extrudeType = extrudeType[0:extrudeType.find(':')]
1237 pointList = numpy.zeros((0,3), numpy.float32)
1239 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1241 if extrudeType == 'FILL':
1244 normal = a[1:] - a[:-1]
1245 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1246 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1249 ePerDist = path['extrusion'][1:] / lens
1251 lineWidth = ePerDist / path['layerThickness'] / 2.0
1253 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1255 normal[:,0] *= lineWidth
1256 normal[:,1] *= lineWidth
1258 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1259 b = numpy.concatenate((b, a[1:] + normal), 1)
1260 b = numpy.concatenate((b, a[1:] - normal), 1)
1261 b = numpy.concatenate((b, a[:-1] - normal), 1)
1262 b = numpy.concatenate((b, a[:-1] + normal), 1)
1263 b = b.reshape((len(b) * 4, 3))
1266 normal2 = normal[:-1] + normal[1:]
1267 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1268 normal2[:,0] /= lens2
1269 normal2[:,1] /= lens2
1270 normal2[:,0] *= lineWidth[:-1]
1271 normal2[:,1] *= lineWidth[:-1]
1273 c = numpy.zeros((len(a)-2, 0), numpy.float32)
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 = numpy.concatenate((c, a[1:-1]), 1)
1280 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1281 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1282 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1284 c = c.reshape((len(c) * 8, 3))
1286 pointList = numpy.concatenate((pointList, b, c))
1288 pointList = numpy.concatenate((pointList, b))
1289 ret.append(opengl.GLVBO(pointList))
1291 pointList = numpy.zeros((0,3), numpy.float32)
1293 if path['type'] == 'move':
1294 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1295 a = numpy.concatenate((a[:-1], a[1:]), 1)
1296 a = a.reshape((len(a) * 2, 3))
1297 pointList = numpy.concatenate((pointList, a))
1298 if path['type'] == 'retract':
1299 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1300 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1301 a = a.reshape((len(a) * 2, 3))
1302 pointList = numpy.concatenate((pointList, a))
1303 ret.append(opengl.GLVBO(pointList))
1307 def getObjectCenterPos(self):
1308 if self._selectedObj is None:
1309 return [0.0, 0.0, 0.0]
1310 pos = self._selectedObj.getPosition()
1311 size = self._selectedObj.getSize()
1312 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1314 def getObjectBoundaryCircle(self):
1315 if self._selectedObj is None:
1317 return self._selectedObj.getBoundaryCircle()
1319 def getObjectSize(self):
1320 if self._selectedObj is None:
1321 return [0.0, 0.0, 0.0]
1322 return self._selectedObj.getSize()
1324 def getObjectMatrix(self):
1325 if self._selectedObj is None:
1326 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1327 return self._selectedObj.getMatrix()
1329 class shaderEditor(wx.Dialog):
1330 def __init__(self, parent, callback, v, f):
1331 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1332 self._callback = callback
1333 s = wx.BoxSizer(wx.VERTICAL)
1335 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1336 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1337 s.Add(self._vertex, 1, flag=wx.EXPAND)
1338 s.Add(self._fragment, 1, flag=wx.EXPAND)
1340 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1341 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1343 self.SetPosition(self.GetParent().GetPosition())
1344 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1347 def OnText(self, e):
1348 self._callback(self._vertex.GetValue(), self._fragment.GetValue())