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
30 class SceneView(openglGui.glGuiPanel):
31 def __init__(self, parent):
32 super(SceneView, self).__init__(parent)
37 self._scene = objectScene.Scene()
40 self._gcodeLoadThread = None
41 self._objectShader = None
43 self._selectedObj = None
44 self._objColors = [None,None,None,None]
47 self._mouseState = None
48 self._viewTarget = numpy.array([0,0,0], numpy.float32)
51 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
52 self._platformMesh._drawOffset = numpy.array([0,0,2.5], numpy.float32)
53 self._isSimpleMode = True
56 self._modelMatrix = None
57 self._projMatrix = None
58 self.tempMatrix = None
60 self.openFileButton = openglGui.glButton(self, 4, 'Load', (0,0), self.showLoadModel)
61 self.printButton = openglGui.glButton(self, 6, 'Print', (1,0), self.showPrintWindow)
62 self.printButton.setDisabled(True)
65 self.rotateToolButton = openglGui.glRadioButton(self, 8, 'Rotate', (0,-1), group, self.OnToolSelect)
66 self.scaleToolButton = openglGui.glRadioButton(self, 9, 'Scale', (1,-1), group, self.OnToolSelect)
67 self.mirrorToolButton = openglGui.glRadioButton(self, 10, 'Mirror', (2,-1), group, self.OnToolSelect)
69 self.resetRotationButton = openglGui.glButton(self, 12, 'Reset', (0,-2), self.OnRotateReset)
70 self.layFlatButton = openglGui.glButton(self, 16, 'Lay flat', (0,-3), self.OnLayFlat)
72 self.resetScaleButton = openglGui.glButton(self, 13, 'Reset', (1,-2), self.OnScaleReset)
73 self.scaleMaxButton = openglGui.glButton(self, 17, 'To max', (1,-3), self.OnScaleMax)
75 self.mirrorXButton = openglGui.glButton(self, 14, 'Mirror X', (2,-2), lambda button: self.OnMirror(0))
76 self.mirrorYButton = openglGui.glButton(self, 18, 'Mirror Y', (2,-3), lambda button: self.OnMirror(1))
77 self.mirrorZButton = openglGui.glButton(self, 22, 'Mirror Z', (2,-4), lambda button: self.OnMirror(2))
79 self.rotateToolButton.setExpandArrow(True)
80 self.scaleToolButton.setExpandArrow(True)
81 self.mirrorToolButton.setExpandArrow(True)
83 self.scaleForm = openglGui.glFrame(self, (2, -2))
84 openglGui.glGuiLayoutGrid(self.scaleForm)
85 openglGui.glLabel(self.scaleForm, 'Scale X', (0,0))
86 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
87 openglGui.glLabel(self.scaleForm, 'Scale Y', (0,1))
88 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
89 openglGui.glLabel(self.scaleForm, 'Scale Z', (0,2))
90 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
91 openglGui.glLabel(self.scaleForm, 'Size X (mm)', (0,4))
92 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
93 openglGui.glLabel(self.scaleForm, 'Size Y (mm)', (0,5))
94 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
95 openglGui.glLabel(self.scaleForm, 'Size Z (mm)', (0,6))
96 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
97 openglGui.glLabel(self.scaleForm, 'Uniform scale', (0,8))
98 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
100 self.viewSelection = openglGui.glComboButton(self, 'View mode', [7,19,11,15,23], ['Normal', 'Overhang', 'Transparent', 'X-Ray', 'Layers'], (-1,0), self.OnViewChange)
101 self.layerSelect = openglGui.glSlider(self, 100, 0, 100, (-1,-2), lambda : self.QueueRefresh())
103 self.notification = openglGui.glNotification(self, (0, 0))
105 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
106 self._sceneUpdateTimer = wx.Timer(self)
107 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
108 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
112 self.updateToolButtons()
113 self.updateProfileToControls()
115 def showLoadModel(self, button = 1):
117 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)
118 dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
119 if dlg.ShowModal() != wx.ID_OK:
122 filenames = dlg.GetPaths()
124 if len(filenames) < 1:
126 profile.putPreference('lastFile', filenames[0])
128 for filename in filenames:
129 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
130 ext = filename[filename.rfind('.')+1:].upper()
131 if ext == 'G' or ext == 'GCODE':
132 gcodeFilename = filename
133 if gcodeFilename is not None:
134 if self._gcode is not None:
136 for layerVBOlist in self._gcodeVBOs:
137 for vbo in layerVBOlist:
138 self.glReleaseList.append(vbo)
140 self._gcode = gcodeInterpreter.gcode()
141 self._gcodeFilename = gcodeFilename
142 self.printButton.setBottomText('')
143 self.viewSelection.setValue(4)
144 self.printButton.setDisabled(False)
147 if self.viewSelection.getValue() == 4:
148 self.viewSelection.setValue(0)
150 self.loadScene(filenames)
152 def showSaveModel(self):
153 if len(self._scene.objects()) < 1:
155 dlg=wx.FileDialog(self, 'Save 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
156 dlg.SetWildcard(meshLoader.saveWildcardFilter())
157 if dlg.ShowModal() != wx.ID_OK:
160 filename = dlg.GetPath()
162 meshLoader.saveMeshes(filename, self._scene.objects())
164 def showPrintWindow(self, button):
166 if machineCom.machineIsConnected():
167 printWindow.printFile(self._gcodeFilename)
168 if self._gcodeFilename == self._slicer.getGCodeFilename():
169 self._slicer.submitSliceInfoOnline()
170 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
171 drives = removableStorage.getPossibleSDcardDrives()
173 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))
174 if dlg.ShowModal() != wx.ID_OK:
177 drive = drives[dlg.GetSelection()]
181 filename = self._scene._objectList[0].getName() + '.gcode'
182 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
187 self.Bind(wx.EVT_MENU, lambda e: printWindow.printFile(self._gcodeFilename), menu.Append(-1, 'Print with USB'))
188 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, 'Save GCode...'))
189 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, 'Slice engine log...'))
193 def showSaveGCode(self):
194 defPath = profile.getPreference('lastFile')
195 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
196 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
197 dlg.SetFilename(self._scene._objectList[0].getName())
198 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
199 if dlg.ShowModal() != wx.ID_OK:
202 filename = dlg.GetPath()
205 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
207 def _copyFile(self, fileA, fileB, allowEject = False):
209 size = float(os.stat(fileA).st_size)
210 with open(fileA, 'rb') as fsrc:
211 with open(fileB, 'wb') as fdst:
213 buf = fsrc.read(16*1024)
217 self.printButton.setProgressBar(float(fsrc.tell()) / size)
222 self.notification.message("Failed to save")
225 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...'))
227 self.notification.message("Saved as %s" % (fileB))
228 self.printButton.setProgressBar(None)
229 if fileA == self._slicer.getGCodeFilename():
230 self._slicer.submitSliceInfoOnline()
232 def _showSliceLog(self):
233 dlg = wx.TextEntryDialog(self, "The slicing engine reported the following", "Engine log...", '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
237 def OnToolSelect(self, button):
238 if self.rotateToolButton.getSelected():
239 self.tool = previewTools.toolRotate(self)
240 elif self.scaleToolButton.getSelected():
241 self.tool = previewTools.toolScale(self)
242 elif self.mirrorToolButton.getSelected():
243 self.tool = previewTools.toolNone(self)
245 self.tool = previewTools.toolNone(self)
246 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
247 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
248 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
249 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
250 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
251 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
252 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
253 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
255 def updateToolButtons(self):
256 if self._selectedObj is None:
260 self.rotateToolButton.setHidden(hidden)
261 self.scaleToolButton.setHidden(hidden)
262 self.mirrorToolButton.setHidden(hidden)
264 self.rotateToolButton.setSelected(False)
265 self.scaleToolButton.setSelected(False)
266 self.mirrorToolButton.setSelected(False)
269 def OnViewChange(self):
270 if self.viewSelection.getValue() == 4:
271 self.viewMode = 'gcode'
272 if self._gcode is not None and self._gcode.layerList is not None:
273 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
274 self.layerSelect.setValue(len(self._gcode.layerList) - 1)
275 self._selectObject(None)
276 elif self.viewSelection.getValue() == 1:
277 self.viewMode = 'overhang'
278 elif self.viewSelection.getValue() == 2:
279 self.viewMode = 'transparent'
280 elif self.viewSelection.getValue() == 3:
281 self.viewMode = 'xray'
283 self.viewMode = 'normal'
284 self.layerSelect.setHidden(self.viewMode != 'gcode')
287 def OnRotateReset(self, button):
288 if self._selectedObj is None:
290 self._selectedObj.resetRotation()
291 self._scene.pushFree()
292 self._selectObject(self._selectedObj)
295 def OnLayFlat(self, button):
296 if self._selectedObj is None:
298 self._selectedObj.layFlat()
299 self._scene.pushFree()
300 self._selectObject(self._selectedObj)
303 def OnScaleReset(self, button):
304 if self._selectedObj is None:
306 self._selectedObj.resetScale()
307 self._selectObject(self._selectedObj)
308 self.updateProfileToControls()
311 def OnScaleMax(self, button):
312 if self._selectedObj is None:
314 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
315 self._scene.pushFree()
316 self._selectObject(self._selectedObj)
317 self.updateProfileToControls()
320 def OnMirror(self, axis):
321 if self._selectedObj is None:
323 self._selectedObj.mirror(axis)
326 def OnScaleEntry(self, value, axis):
327 if self._selectedObj is None:
333 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
334 self.updateProfileToControls()
335 self._scene.pushFree()
336 self._selectObject(self._selectedObj)
339 def OnScaleEntryMM(self, value, axis):
340 if self._selectedObj is None:
346 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
347 self.updateProfileToControls()
348 self._scene.pushFree()
349 self._selectObject(self._selectedObj)
352 def OnDeleteAll(self, e):
353 while len(self._scene.objects()) > 0:
354 self._deleteObject(self._scene.objects()[0])
355 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
357 def OnMultiply(self, e):
358 if self._focusObj is None:
361 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
362 if dlg.ShowModal() != wx.ID_OK:
371 self._scene.add(newObj)
372 self._scene.centerAll()
373 if not self._scene.checkPlatform(newObj):
378 self.notification.message("Could not create more then %d items" % (n - 1))
379 self._scene.remove(newObj)
380 self._scene.centerAll()
383 def OnSplitObject(self, e):
384 if self._focusObj is None:
386 self._scene.remove(self._focusObj)
387 for obj in self._focusObj.split(self._splitCallback):
388 if numpy.max(obj.getSize()) > 2.0:
390 self._scene.centerAll()
391 self._selectObject(None)
394 def _splitCallback(self, progress):
397 def OnMergeObjects(self, e):
398 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
400 self._scene.merge(self._selectedObj, self._focusObj)
403 def sceneUpdated(self):
404 self._sceneUpdateTimer.Start(500, True)
405 self._slicer.abortSlicer()
406 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
409 def _onRunSlicer(self, e):
410 if self._isSimpleMode:
411 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
412 self._slicer.runSlicer(self._scene)
413 if self._isSimpleMode:
414 profile.resetTempOverride()
416 def _updateSliceProgress(self, progressValue, ready):
418 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
420 self.printButton.setDisabled(not ready)
421 if progressValue >= 0.0:
422 self.printButton.setProgressBar(progressValue)
424 self.printButton.setProgressBar(None)
425 if self._gcode is not None:
427 for layerVBOlist in self._gcodeVBOs:
428 for vbo in layerVBOlist:
429 self.glReleaseList.append(vbo)
432 self.printButton.setProgressBar(None)
433 cost = self._slicer.getFilamentCost()
435 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
437 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
438 self._gcode = gcodeInterpreter.gcode()
439 self._gcodeFilename = self._slicer.getGCodeFilename()
441 self.printButton.setBottomText('')
444 def _loadGCode(self):
445 self._gcode.progressCallback = self._gcodeLoadCallback
446 self._gcode.load(self._gcodeFilename)
448 def _gcodeLoadCallback(self, progress):
449 if self._gcode is None:
451 if len(self._gcode.layerList) % 15 == 0:
453 if self._gcode is None:
455 if self.layerSelect.getValue() == self.layerSelect.getMaxValue():
456 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
457 self.layerSelect.setValue(self.layerSelect.getMaxValue())
459 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
460 if self.viewMode == 'gcode':
464 def loadScene(self, fileList):
465 for filename in fileList:
467 objList = meshLoader.loadMeshes(filename)
469 traceback.print_exc()
472 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
474 self._scene.centerAll()
475 self._selectObject(obj)
478 def _deleteObject(self, obj):
479 if obj == self._selectedObj:
480 self._selectObject(None)
481 if obj == self._focusObj:
482 self._focusObj = None
483 self._scene.remove(obj)
484 for m in obj._meshList:
485 if m.vbo is not None and m.vbo.decRef():
486 self.glReleaseList.append(m.vbo)
491 def _selectObject(self, obj, zoom = True):
492 if obj != self._selectedObj:
493 self._selectedObj = obj
494 self.updateProfileToControls()
495 self.updateToolButtons()
496 if zoom and obj is not None:
497 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
498 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
499 newZoom = obj.getBoundaryCircle() * 6
500 if newZoom > numpy.max(self._machineSize) * 3:
501 newZoom = numpy.max(self._machineSize) * 3
502 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
504 def updateProfileToControls(self):
505 oldSimpleMode = self._isSimpleMode
506 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
507 if self._isSimpleMode and not oldSimpleMode:
508 self._scene.arrangeAll()
510 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
511 self._objColors[0] = profile.getPreferenceColour('model_colour')
512 self._objColors[1] = profile.getPreferenceColour('model_colour2')
513 self._objColors[2] = profile.getPreferenceColour('model_colour3')
514 self._objColors[3] = profile.getPreferenceColour('model_colour4')
515 self._scene.setMachineSize(self._machineSize)
516 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
517 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'))
519 if self._selectedObj is not None:
520 scale = self._selectedObj.getScale()
521 size = self._selectedObj.getSize()
522 self.scaleXctrl.setValue(round(scale[0], 2))
523 self.scaleYctrl.setValue(round(scale[1], 2))
524 self.scaleZctrl.setValue(round(scale[2], 2))
525 self.scaleXmmctrl.setValue(round(size[0], 2))
526 self.scaleYmmctrl.setValue(round(size[1], 2))
527 self.scaleZmmctrl.setValue(round(size[2], 2))
529 def OnKeyChar(self, keyCode):
530 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
531 if self._selectedObj is not None:
532 self._deleteObject(self._selectedObj)
534 if keyCode == wx.WXK_UP:
535 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
537 elif keyCode == wx.WXK_DOWN:
538 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
540 elif keyCode == wx.WXK_PAGEUP:
541 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
543 elif keyCode == wx.WXK_PAGEDOWN:
544 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
547 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
548 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
549 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
550 from collections import defaultdict
551 from gc import get_objects
552 self._beforeLeakTest = defaultdict(int)
553 for i in get_objects():
554 self._beforeLeakTest[type(i)] += 1
555 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
556 from collections import defaultdict
557 from gc import get_objects
558 self._afterLeakTest = defaultdict(int)
559 for i in get_objects():
560 self._afterLeakTest[type(i)] += 1
561 for k in self._afterLeakTest:
562 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
563 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
565 def ShaderUpdate(self, v, f):
566 s = opengl.GLShader(v, f)
568 self._objectLoadShader.release()
569 self._objectLoadShader = s
570 for obj in self._scene.objects():
571 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
574 def OnMouseDown(self,e):
575 self._mouseX = e.GetX()
576 self._mouseY = e.GetY()
577 self._mouseClick3DPos = self._mouse3Dpos
578 self._mouseClickFocus = self._focusObj
580 self._mouseState = 'doubleClick'
582 self._mouseState = 'dragOrClick'
583 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
584 p0 -= self.getObjectCenterPos() - self._viewTarget
585 p1 -= self.getObjectCenterPos() - self._viewTarget
586 if self.tool.OnDragStart(p0, p1):
587 self._mouseState = 'tool'
588 if self._mouseState == 'dragOrClick':
589 if e.GetButton() == 1:
590 if self._focusObj is not None:
591 self._selectObject(self._focusObj, False)
594 def OnMouseUp(self, e):
595 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
597 if self._mouseState == 'dragOrClick':
598 if e.GetButton() == 1:
599 self._selectObject(self._focusObj)
600 if e.GetButton() == 3:
602 if self._focusObj is not None:
603 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
604 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
605 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
606 if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1:
607 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
608 if len(self._scene.objects()) > 0:
609 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
610 if menu.MenuItemCount > 0:
613 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
614 self._scene.pushFree()
616 elif self._mouseState == 'tool':
617 if self.tempMatrix is not None and self._selectedObj is not None:
618 self._selectedObj.applyMatrix(self.tempMatrix)
619 self._scene.pushFree()
620 self._selectObject(self._selectedObj)
621 self.tempMatrix = None
622 self.tool.OnDragEnd()
624 self._mouseState = None
626 def OnMouseMotion(self,e):
627 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
628 p0 -= self.getObjectCenterPos() - self._viewTarget
629 p1 -= self.getObjectCenterPos() - self._viewTarget
631 if e.Dragging() and self._mouseState is not None:
632 if self._mouseState == 'tool':
633 self.tool.OnDrag(p0, p1)
634 elif not e.LeftIsDown() and e.RightIsDown():
635 self._mouseState = 'drag'
636 if wx.GetKeyState(wx.WXK_SHIFT):
637 a = math.cos(math.radians(self._yaw)) / 3.0
638 b = math.sin(math.radians(self._yaw)) / 3.0
639 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
640 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
641 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
642 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
644 self._yaw += e.GetX() - self._mouseX
645 self._pitch -= e.GetY() - self._mouseY
646 if self._pitch > 170:
650 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
651 self._mouseState = 'drag'
652 self._zoom += e.GetY() - self._mouseY
655 if self._zoom > numpy.max(self._machineSize) * 3:
656 self._zoom = numpy.max(self._machineSize) * 3
657 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
658 self._mouseState = 'dragObject'
659 z = max(0, self._mouseClick3DPos[2])
660 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
661 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
666 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
667 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
668 diff = cursorZ1 - cursorZ0
669 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
670 if not e.Dragging() or self._mouseState != 'tool':
671 self.tool.OnMouseMove(p0, p1)
673 self._mouseX = e.GetX()
674 self._mouseY = e.GetY()
676 def OnMouseWheel(self, e):
677 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
678 delta = max(min(delta,4),-4)
679 self._zoom *= 1.0 - delta / 10.0
682 if self._zoom > numpy.max(self._machineSize) * 3:
683 self._zoom = numpy.max(self._machineSize) * 3
686 def getMouseRay(self, x, y):
687 if self._viewport is None:
688 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
689 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
690 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
691 p0 -= self._viewTarget
692 p1 -= self._viewTarget
695 def _init3DView(self):
696 # set viewing projection
697 size = self.GetSize()
698 glViewport(0, 0, size.GetWidth(), size.GetHeight())
701 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
703 glDisable(GL_RESCALE_NORMAL)
704 glDisable(GL_LIGHTING)
706 glEnable(GL_DEPTH_TEST)
707 glDisable(GL_CULL_FACE)
709 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
711 glClearColor(0.8, 0.8, 0.8, 1.0)
715 glMatrixMode(GL_PROJECTION)
717 aspect = float(size.GetWidth()) / float(size.GetHeight())
718 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
720 glMatrixMode(GL_MODELVIEW)
722 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
725 if machineCom.machineIsConnected():
726 self.printButton._imageID = 6
727 self.printButton._tooltip = 'Print'
728 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
729 self.printButton._imageID = 2
730 self.printButton._tooltip = 'Toolpath to SD'
732 self.printButton._imageID = 3
733 self.printButton._tooltip = 'Save toolpath'
735 if self._animView is not None:
736 self._viewTarget = self._animView.getPosition()
737 if self._animView.isDone():
738 self._animView = None
739 if self._animZoom is not None:
740 self._zoom = self._animZoom.getPosition()
741 if self._animZoom.isDone():
742 self._animZoom = None
743 if self.viewMode == 'gcode' and self._gcode is not None:
745 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
748 if self._objectShader is None:
749 self._objectShader = opengl.GLShader("""
750 varying float light_amount;
754 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
755 gl_FrontColor = gl_Color;
757 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
761 varying float light_amount;
765 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
768 self._objectOverhangShader = opengl.GLShader("""
769 uniform float cosAngle;
770 uniform mat3 rotMatrix;
771 varying float light_amount;
775 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
776 gl_FrontColor = gl_Color;
778 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
780 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
782 light_amount = -10.0;
786 varying float light_amount;
790 if (light_amount == -10.0)
792 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
794 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
798 self._objectLoadShader = opengl.GLShader("""
799 uniform float intensity;
801 varying float light_amount;
805 vec4 tmp = gl_Vertex;
806 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
807 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
808 gl_Position = gl_ModelViewProjectionMatrix * tmp;
809 gl_FrontColor = gl_Color;
811 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
815 uniform float intensity;
816 varying float light_amount;
820 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
824 glTranslate(0,0,-self._zoom)
825 glRotate(-self._pitch, 1,0,0)
826 glRotate(self._yaw, 0,0,1)
827 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
829 self._viewport = glGetIntegerv(GL_VIEWPORT)
830 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
831 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
833 glClearColor(1,1,1,1)
834 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
836 if self.viewMode != 'gcode':
837 for n in xrange(0, len(self._scene.objects())):
838 obj = self._scene.objects()[n]
839 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
840 self._renderObject(obj)
842 if self._mouseX > -1:
844 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
845 if n < len(self._scene.objects()):
846 self._focusObj = self._scene.objects()[n]
848 self._focusObj = None
849 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
850 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
851 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
852 self._mouse3Dpos -= self._viewTarget
855 glTranslate(0,0,-self._zoom)
856 glRotate(-self._pitch, 1,0,0)
857 glRotate(self._yaw, 0,0,1)
858 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
860 if self.viewMode == 'gcode':
861 if self._gcode is not None and self._gcode.layerList is None:
862 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
863 self._gcodeLoadThread.daemon = True
864 self._gcodeLoadThread.start()
865 if self._gcode is not None and self._gcode.layerList is not None:
867 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
869 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
870 for n in xrange(0, drawUpTill):
871 c = 1.0 - float(drawUpTill - n) / 15
873 if len(self._gcodeVBOs) < n + 1:
874 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
875 if time.time() - t > 0.5:
878 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
879 if n == drawUpTill - 1:
880 if len(self._gcodeVBOs[n]) < 6:
881 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
883 self._gcodeVBOs[n][5].render(GL_QUADS)
885 self._gcodeVBOs[n][6].render(GL_QUADS)
886 glColor3f(c/2, c/2, 0.0)
887 self._gcodeVBOs[n][7].render(GL_QUADS)
889 self._gcodeVBOs[n][8].render(GL_QUADS)
890 self._gcodeVBOs[n][9].render(GL_QUADS)
892 self._gcodeVBOs[n][10].render(GL_LINES)
895 self._gcodeVBOs[n][0].render(GL_LINES)
897 self._gcodeVBOs[n][1].render(GL_LINES)
898 glColor3f(c/2, c/2, 0.0)
899 self._gcodeVBOs[n][2].render(GL_LINES)
901 self._gcodeVBOs[n][3].render(GL_LINES)
902 self._gcodeVBOs[n][4].render(GL_LINES)
905 glStencilFunc(GL_ALWAYS, 1, 1)
906 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
908 if self.viewMode == 'overhang':
909 self._objectOverhangShader.bind()
910 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
912 self._objectShader.bind()
913 for obj in self._scene.objects():
914 if obj._loadAnim is not None:
915 if obj._loadAnim.isDone():
920 if self._focusObj == obj:
922 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
925 if self._selectedObj == obj or self._selectedObj is None:
926 #If we want transparent, then first render a solid black model to remove the printer size lines.
927 if self.viewMode == 'transparent':
928 glColor4f(0, 0, 0, 0)
929 self._renderObject(obj)
931 glBlendFunc(GL_ONE, GL_ONE)
932 glDisable(GL_DEPTH_TEST)
934 if self.viewMode == 'xray':
935 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
936 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
937 glEnable(GL_STENCIL_TEST)
939 if self.viewMode == 'overhang':
940 if self._selectedObj == obj and self.tempMatrix is not None:
941 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
943 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
945 if not self._scene.checkPlatform(obj):
946 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
947 self._renderObject(obj)
949 self._renderObject(obj, brightness)
950 glDisable(GL_STENCIL_TEST)
952 glEnable(GL_DEPTH_TEST)
953 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
955 if self.viewMode == 'xray':
958 glEnable(GL_STENCIL_TEST)
959 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
960 glDisable(GL_DEPTH_TEST)
961 for i in xrange(2, 15, 2):
962 glStencilFunc(GL_EQUAL, i, 0xFF)
963 glColor(float(i)/10, float(i)/10, float(i)/5)
965 glVertex3f(-1000,-1000,-10)
966 glVertex3f( 1000,-1000,-10)
967 glVertex3f( 1000, 1000,-10)
968 glVertex3f(-1000, 1000,-10)
970 for i in xrange(1, 15, 2):
971 glStencilFunc(GL_EQUAL, i, 0xFF)
972 glColor(float(i)/10, 0, 0)
974 glVertex3f(-1000,-1000,-10)
975 glVertex3f( 1000,-1000,-10)
976 glVertex3f( 1000, 1000,-10)
977 glVertex3f(-1000, 1000,-10)
980 glDisable(GL_STENCIL_TEST)
981 glEnable(GL_DEPTH_TEST)
983 self._objectShader.unbind()
985 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
987 self._objectLoadShader.bind()
988 glColor4f(0.2, 0.6, 1.0, 1.0)
989 for obj in self._scene.objects():
990 if obj._loadAnim is None:
992 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
993 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
994 self._renderObject(obj)
995 self._objectLoadShader.unbind()
1000 if self.viewMode == 'gcode':
1001 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1002 glDisable(GL_DEPTH_TEST)
1005 glTranslate(0,-4,-10)
1006 glColor4ub(60,60,60,255)
1007 opengl.glDrawStringCenter('Loading toolpath for visualization...')
1010 #Draw the object box-shadow, so you can see where it will collide with other objects.
1011 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1012 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1014 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1016 glEnable(GL_CULL_FACE)
1017 glColor4f(0,0,0,0.12)
1019 glVertex3f(-size[0], size[1], 0.1)
1020 glVertex3f(-size[0], -size[1], 0.1)
1021 glVertex3f( size[0], -size[1], 0.1)
1022 glVertex3f( size[0], size[1], 0.1)
1024 glDisable(GL_CULL_FACE)
1027 #Draw the outline of the selected object, on top of everything else except the GUI.
1028 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1029 glDisable(GL_DEPTH_TEST)
1030 glEnable(GL_CULL_FACE)
1031 glEnable(GL_STENCIL_TEST)
1033 glStencilFunc(GL_EQUAL, 0, 255)
1035 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1037 glColor4f(1,1,1,0.5)
1038 self._renderObject(self._selectedObj)
1039 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1041 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1042 glDisable(GL_STENCIL_TEST)
1043 glDisable(GL_CULL_FACE)
1044 glEnable(GL_DEPTH_TEST)
1046 if self._selectedObj is not None:
1048 pos = self.getObjectCenterPos()
1049 glTranslate(pos[0], pos[1], pos[2])
1053 def _renderObject(self, obj, brightness = False, addSink = True):
1056 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1058 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1060 if self.tempMatrix is not None and obj == self._selectedObj:
1061 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1062 glMultMatrixf(tempMatrix)
1064 offset = obj.getDrawOffset()
1065 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1067 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1068 glMultMatrixf(tempMatrix)
1071 for m in obj._meshList:
1073 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1075 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1080 def _drawMachine(self):
1081 glEnable(GL_CULL_FACE)
1084 if profile.getPreference('machine_type') == 'ultimaker':
1085 glColor4f(1,1,1,0.5)
1086 self._objectShader.bind()
1087 self._renderObject(self._platformMesh, False, False)
1088 self._objectShader.unbind()
1090 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
1091 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1092 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1093 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1094 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1095 v4 = [ size[0] / 2, size[1] / 2, 0]
1096 v5 = [ size[0] / 2,-size[1] / 2, 0]
1097 v6 = [-size[0] / 2, size[1] / 2, 0]
1098 v7 = [-size[0] / 2,-size[1] / 2, 0]
1100 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1101 glEnableClientState(GL_VERTEX_ARRAY)
1102 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1104 glColor4ub(5, 171, 231, 64)
1105 glDrawArrays(GL_QUADS, 0, 4)
1106 glColor4ub(5, 171, 231, 96)
1107 glDrawArrays(GL_QUADS, 4, 8)
1108 glColor4ub(5, 171, 231, 128)
1109 glDrawArrays(GL_QUADS, 12, 8)
1111 sx = self._machineSize[0]
1112 sy = self._machineSize[1]
1113 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1114 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1119 x1 = max(min(x1, sx/2), -sx/2)
1120 y1 = max(min(y1, sy/2), -sy/2)
1121 x2 = max(min(x2, sx/2), -sx/2)
1122 y2 = max(min(y2, sy/2), -sy/2)
1123 if (x & 1) == (y & 1):
1124 glColor4ub(5, 171, 231, 127)
1126 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1128 glVertex3f(x1, y1, -0.02)
1129 glVertex3f(x2, y1, -0.02)
1130 glVertex3f(x2, y2, -0.02)
1131 glVertex3f(x1, y2, -0.02)
1134 glDisableClientState(GL_VERTEX_ARRAY)
1136 glDisable(GL_CULL_FACE)
1138 def _generateGCodeVBOs(self, layer):
1140 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1141 pointList = numpy.zeros((0,3), numpy.float32)
1143 if path['type'] == 'extrude' and path['pathType'] == extrudeType:
1145 a = numpy.concatenate((a[:-1], a[1:]), 1)
1146 a = a.reshape((len(a) * 2, 3))
1147 pointList = numpy.concatenate((pointList, a))
1148 ret.append(opengl.GLVBO(pointList))
1151 def _generateGCodeVBOs2(self, layer):
1152 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1153 filamentArea = math.pi * filamentRadius * filamentRadius
1156 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1157 pointList = numpy.zeros((0,3), numpy.float32)
1159 if path['type'] == 'extrude' and path['pathType'] == extrudeType:
1161 if extrudeType == 'FILL':
1164 normal = a[1:] - a[:-1]
1165 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1166 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1169 ePerDist = path['extrusion'][1:] / lens
1170 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1172 normal[:,0] *= lineWidth
1173 normal[:,1] *= lineWidth
1175 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1176 b = numpy.concatenate((b, a[1:] + normal), 1)
1177 b = numpy.concatenate((b, a[1:] - normal), 1)
1178 b = numpy.concatenate((b, a[:-1] - normal), 1)
1179 b = numpy.concatenate((b, a[:-1] + normal), 1)
1180 #b = numpy.concatenate((b, a[:-1]), 1)
1181 #b = numpy.concatenate((b, a[:-1]), 1)
1182 b = b.reshape((len(b) * 4, 3))
1185 normal2 = normal[:-1] + normal[1:]
1186 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1187 normal2[:,0] /= lens2
1188 normal2[:,1] /= lens2
1189 normal2[:,0] *= lineWidth[:-1]
1190 normal2[:,1] *= lineWidth[:-1]
1192 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1193 c = numpy.concatenate((c, a[1:-1]), 1)
1194 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1195 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1196 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1198 c = numpy.concatenate((c, a[1:-1]), 1)
1199 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1200 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1201 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1203 c = c.reshape((len(c) * 8, 3))
1205 pointList = numpy.concatenate((pointList, b, c))
1207 pointList = numpy.concatenate((pointList, b))
1208 ret.append(opengl.GLVBO(pointList))
1210 pointList = numpy.zeros((0,3), numpy.float32)
1212 if path['type'] == 'move':
1213 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1214 a = numpy.concatenate((a[:-1], a[1:]), 1)
1215 a = a.reshape((len(a) * 2, 3))
1216 pointList = numpy.concatenate((pointList, a))
1217 if path['type'] == 'retract':
1218 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1219 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1220 a = a.reshape((len(a) * 2, 3))
1221 pointList = numpy.concatenate((pointList, a))
1222 ret.append(opengl.GLVBO(pointList))
1226 def getObjectCenterPos(self):
1227 if self._selectedObj is None:
1228 return [0.0, 0.0, 0.0]
1229 pos = self._selectedObj.getPosition()
1230 size = self._selectedObj.getSize()
1231 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1233 def getObjectBoundaryCircle(self):
1234 if self._selectedObj is None:
1236 return self._selectedObj.getBoundaryCircle()
1238 def getObjectSize(self):
1239 if self._selectedObj is None:
1240 return [0.0, 0.0, 0.0]
1241 return self._selectedObj.getSize()
1243 def getObjectMatrix(self):
1244 if self._selectedObj is None:
1245 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1246 return self._selectedObj.getMatrix()
1248 class shaderEditor(wx.Dialog):
1249 def __init__(self, parent, callback, v, f):
1250 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1251 self._callback = callback
1252 s = wx.BoxSizer(wx.VERTICAL)
1254 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1255 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1256 s.Add(self._vertex, 1, flag=wx.EXPAND)
1257 s.Add(self._fragment, 1, flag=wx.EXPAND)
1259 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1260 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1262 self.SetPosition(self.GetParent().GetPosition())
1263 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1266 def OnText(self, e):
1267 self._callback(self._vertex.GetValue(), self._fragment.GetValue())