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
42 self._objectLoadShader = None
44 self._selectedObj = None
45 self._objColors = [None,None,None,None]
48 self._mouseState = None
49 self._viewTarget = numpy.array([0,0,0], numpy.float32)
52 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
53 self._platformMesh._drawOffset = numpy.array([0,0,2.5], numpy.float32)
54 self._isSimpleMode = True
57 self._modelMatrix = None
58 self._projMatrix = None
59 self.tempMatrix = None
61 self.openFileButton = openglGui.glButton(self, 4, 'Load', (0,0), self.showLoadModel)
62 self.printButton = openglGui.glButton(self, 6, 'Print', (1,0), self.showPrintWindow)
63 self.printButton.setDisabled(True)
66 self.rotateToolButton = openglGui.glRadioButton(self, 8, 'Rotate', (0,-1), group, self.OnToolSelect)
67 self.scaleToolButton = openglGui.glRadioButton(self, 9, 'Scale', (1,-1), group, self.OnToolSelect)
68 self.mirrorToolButton = openglGui.glRadioButton(self, 10, 'Mirror', (2,-1), group, self.OnToolSelect)
70 self.resetRotationButton = openglGui.glButton(self, 12, 'Reset', (0,-2), self.OnRotateReset)
71 self.layFlatButton = openglGui.glButton(self, 16, 'Lay flat', (0,-3), self.OnLayFlat)
73 self.resetScaleButton = openglGui.glButton(self, 13, 'Reset', (1,-2), self.OnScaleReset)
74 self.scaleMaxButton = openglGui.glButton(self, 17, 'To max', (1,-3), self.OnScaleMax)
76 self.mirrorXButton = openglGui.glButton(self, 14, 'Mirror X', (2,-2), lambda button: self.OnMirror(0))
77 self.mirrorYButton = openglGui.glButton(self, 18, 'Mirror Y', (2,-3), lambda button: self.OnMirror(1))
78 self.mirrorZButton = openglGui.glButton(self, 22, 'Mirror Z', (2,-4), lambda button: self.OnMirror(2))
80 self.rotateToolButton.setExpandArrow(True)
81 self.scaleToolButton.setExpandArrow(True)
82 self.mirrorToolButton.setExpandArrow(True)
84 self.scaleForm = openglGui.glFrame(self, (2, -2))
85 openglGui.glGuiLayoutGrid(self.scaleForm)
86 openglGui.glLabel(self.scaleForm, 'Scale X', (0,0))
87 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
88 openglGui.glLabel(self.scaleForm, 'Scale Y', (0,1))
89 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
90 openglGui.glLabel(self.scaleForm, 'Scale Z', (0,2))
91 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
92 openglGui.glLabel(self.scaleForm, 'Size X (mm)', (0,4))
93 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
94 openglGui.glLabel(self.scaleForm, 'Size Y (mm)', (0,5))
95 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
96 openglGui.glLabel(self.scaleForm, 'Size Z (mm)', (0,6))
97 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
98 openglGui.glLabel(self.scaleForm, 'Uniform scale', (0,8))
99 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
101 self.viewSelection = openglGui.glComboButton(self, 'View mode', [7,19,11,15,23], ['Normal', 'Overhang', 'Transparent', 'X-Ray', 'Layers'], (-1,0), self.OnViewChange)
102 self.layerSelect = openglGui.glSlider(self, 100, 0, 100, (-1,-2), lambda : self.QueueRefresh())
104 self.notification = openglGui.glNotification(self, (0, 0))
106 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
107 self._sceneUpdateTimer = wx.Timer(self)
108 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
109 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
110 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
114 self.updateToolButtons()
115 self.updateProfileToControls()
117 def showLoadModel(self, button = 1):
119 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)
120 dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
121 if dlg.ShowModal() != wx.ID_OK:
124 filenames = dlg.GetPaths()
126 if len(filenames) < 1:
128 profile.putPreference('lastFile', filenames[0])
130 for filename in filenames:
131 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
132 ext = filename[filename.rfind('.')+1:].upper()
133 if ext == 'G' or ext == 'GCODE':
134 gcodeFilename = filename
135 if gcodeFilename is not None:
136 if self._gcode is not None:
138 for layerVBOlist in self._gcodeVBOs:
139 for vbo in layerVBOlist:
140 self.glReleaseList.append(vbo)
142 self._gcode = gcodeInterpreter.gcode()
143 self._gcodeFilename = gcodeFilename
144 self.printButton.setBottomText('')
145 self.viewSelection.setValue(4)
146 self.printButton.setDisabled(False)
149 if self.viewSelection.getValue() == 4:
150 self.viewSelection.setValue(0)
152 self.loadScene(filenames)
154 def showSaveModel(self):
155 if len(self._scene.objects()) < 1:
157 dlg=wx.FileDialog(self, 'Save 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
158 dlg.SetWildcard(meshLoader.saveWildcardFilter())
159 if dlg.ShowModal() != wx.ID_OK:
162 filename = dlg.GetPath()
164 meshLoader.saveMeshes(filename, self._scene.objects())
166 def showPrintWindow(self, button):
168 if machineCom.machineIsConnected():
169 printWindow.printFile(self._gcodeFilename)
170 if self._gcodeFilename == self._slicer.getGCodeFilename():
171 self._slicer.submitSliceInfoOnline()
172 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
173 drives = removableStorage.getPossibleSDcardDrives()
175 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))
176 if dlg.ShowModal() != wx.ID_OK:
179 drive = drives[dlg.GetSelection()]
183 filename = self._scene._objectList[0].getName() + '.gcode'
184 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
189 self.Bind(wx.EVT_MENU, lambda e: printWindow.printFile(self._gcodeFilename), menu.Append(-1, 'Print with USB'))
190 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, 'Save GCode...'))
191 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, 'Slice engine log...'))
195 def showSaveGCode(self):
196 defPath = profile.getPreference('lastFile')
197 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
198 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
199 dlg.SetFilename(self._scene._objectList[0].getName())
200 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
201 if dlg.ShowModal() != wx.ID_OK:
204 filename = dlg.GetPath()
207 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
209 def _copyFile(self, fileA, fileB, allowEject = False):
211 size = float(os.stat(fileA).st_size)
212 with open(fileA, 'rb') as fsrc:
213 with open(fileB, 'wb') as fdst:
215 buf = fsrc.read(16*1024)
219 self.printButton.setProgressBar(float(fsrc.tell()) / size)
224 self.notification.message("Failed to save")
227 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...'))
229 self.notification.message("Saved as %s" % (fileB))
230 self.printButton.setProgressBar(None)
231 if fileA == self._slicer.getGCodeFilename():
232 self._slicer.submitSliceInfoOnline()
234 def _showSliceLog(self):
235 dlg = wx.TextEntryDialog(self, "The slicing engine reported the following", "Engine log...", '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
239 def OnToolSelect(self, button):
240 if self.rotateToolButton.getSelected():
241 self.tool = previewTools.toolRotate(self)
242 elif self.scaleToolButton.getSelected():
243 self.tool = previewTools.toolScale(self)
244 elif self.mirrorToolButton.getSelected():
245 self.tool = previewTools.toolNone(self)
247 self.tool = previewTools.toolNone(self)
248 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
249 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
250 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
251 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
252 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
253 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
254 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
255 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
257 def updateToolButtons(self):
258 if self._selectedObj is None:
262 self.rotateToolButton.setHidden(hidden)
263 self.scaleToolButton.setHidden(hidden)
264 self.mirrorToolButton.setHidden(hidden)
266 self.rotateToolButton.setSelected(False)
267 self.scaleToolButton.setSelected(False)
268 self.mirrorToolButton.setSelected(False)
271 def OnViewChange(self):
272 if self.viewSelection.getValue() == 4:
273 self.viewMode = 'gcode'
274 if self._gcode is not None and self._gcode.layerList is not None:
275 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
276 self.layerSelect.setValue(len(self._gcode.layerList) - 1)
277 self._selectObject(None)
278 elif self.viewSelection.getValue() == 1:
279 self.viewMode = 'overhang'
280 elif self.viewSelection.getValue() == 2:
281 self.viewMode = 'transparent'
282 elif self.viewSelection.getValue() == 3:
283 self.viewMode = 'xray'
285 self.viewMode = 'normal'
286 self.layerSelect.setHidden(self.viewMode != 'gcode')
289 def OnRotateReset(self, button):
290 if self._selectedObj is None:
292 self._selectedObj.resetRotation()
293 self._scene.pushFree()
294 self._selectObject(self._selectedObj)
297 def OnLayFlat(self, button):
298 if self._selectedObj is None:
300 self._selectedObj.layFlat()
301 self._scene.pushFree()
302 self._selectObject(self._selectedObj)
305 def OnScaleReset(self, button):
306 if self._selectedObj is None:
308 self._selectedObj.resetScale()
309 self._selectObject(self._selectedObj)
310 self.updateProfileToControls()
313 def OnScaleMax(self, button):
314 if self._selectedObj is None:
316 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
317 self._scene.pushFree()
318 self._selectObject(self._selectedObj)
319 self.updateProfileToControls()
322 def OnMirror(self, axis):
323 if self._selectedObj is None:
325 self._selectedObj.mirror(axis)
328 def OnScaleEntry(self, value, axis):
329 if self._selectedObj is None:
335 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
336 self.updateProfileToControls()
337 self._scene.pushFree()
338 self._selectObject(self._selectedObj)
341 def OnScaleEntryMM(self, value, axis):
342 if self._selectedObj is None:
348 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
349 self.updateProfileToControls()
350 self._scene.pushFree()
351 self._selectObject(self._selectedObj)
354 def OnDeleteAll(self, e):
355 while len(self._scene.objects()) > 0:
356 self._deleteObject(self._scene.objects()[0])
357 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
359 def OnMultiply(self, e):
360 if self._focusObj is None:
363 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
364 if dlg.ShowModal() != wx.ID_OK:
373 self._scene.add(newObj)
374 self._scene.centerAll()
375 if not self._scene.checkPlatform(newObj):
380 self.notification.message("Could not create more then %d items" % (n - 1))
381 self._scene.remove(newObj)
382 self._scene.centerAll()
385 def OnSplitObject(self, e):
386 if self._focusObj is None:
388 self._scene.remove(self._focusObj)
389 for obj in self._focusObj.split(self._splitCallback):
390 if numpy.max(obj.getSize()) > 2.0:
392 self._scene.centerAll()
393 self._selectObject(None)
396 def _splitCallback(self, progress):
399 def OnMergeObjects(self, e):
400 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
402 self._scene.merge(self._selectedObj, self._focusObj)
405 def sceneUpdated(self):
406 self._sceneUpdateTimer.Start(500, True)
407 self._slicer.abortSlicer()
408 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
411 def _onRunSlicer(self, e):
412 if self._isSimpleMode:
413 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
414 self._slicer.runSlicer(self._scene)
415 if self._isSimpleMode:
416 profile.resetTempOverride()
418 def _updateSliceProgress(self, progressValue, ready):
420 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
422 self.printButton.setDisabled(not ready)
423 if progressValue >= 0.0:
424 self.printButton.setProgressBar(progressValue)
426 self.printButton.setProgressBar(None)
427 if self._gcode is not None:
429 for layerVBOlist in self._gcodeVBOs:
430 for vbo in layerVBOlist:
431 self.glReleaseList.append(vbo)
434 self.printButton.setProgressBar(None)
435 cost = self._slicer.getFilamentCost()
437 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
439 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
440 self._gcode = gcodeInterpreter.gcode()
441 self._gcodeFilename = self._slicer.getGCodeFilename()
443 self.printButton.setBottomText('')
446 def _loadGCode(self):
447 self._gcode.progressCallback = self._gcodeLoadCallback
448 self._gcode.load(self._gcodeFilename)
450 def _gcodeLoadCallback(self, progress):
451 if self._gcode is None:
453 if len(self._gcode.layerList) % 15 == 0:
455 if self._gcode is None:
457 if self.layerSelect.getValue() == self.layerSelect.getMaxValue():
458 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
459 self.layerSelect.setValue(self.layerSelect.getMaxValue())
461 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
462 if self.viewMode == 'gcode':
466 def loadScene(self, fileList):
467 for filename in fileList:
469 objList = meshLoader.loadMeshes(filename)
471 traceback.print_exc()
474 if self._objectLoadShader is not None:
475 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
479 self._scene.centerAll()
480 self._selectObject(obj)
483 def _deleteObject(self, obj):
484 if obj == self._selectedObj:
485 self._selectObject(None)
486 if obj == self._focusObj:
487 self._focusObj = None
488 self._scene.remove(obj)
489 for m in obj._meshList:
490 if m.vbo is not None and m.vbo.decRef():
491 self.glReleaseList.append(m.vbo)
496 def _selectObject(self, obj, zoom = True):
497 if obj != self._selectedObj:
498 self._selectedObj = obj
499 self.updateProfileToControls()
500 self.updateToolButtons()
501 if zoom and obj is not None:
502 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
503 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
504 newZoom = obj.getBoundaryCircle() * 6
505 if newZoom > numpy.max(self._machineSize) * 3:
506 newZoom = numpy.max(self._machineSize) * 3
507 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
509 def updateProfileToControls(self):
510 oldSimpleMode = self._isSimpleMode
511 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
512 if self._isSimpleMode and not oldSimpleMode:
513 self._scene.arrangeAll()
515 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
516 self._objColors[0] = profile.getPreferenceColour('model_colour')
517 self._objColors[1] = profile.getPreferenceColour('model_colour2')
518 self._objColors[2] = profile.getPreferenceColour('model_colour3')
519 self._objColors[3] = profile.getPreferenceColour('model_colour4')
520 self._scene.setMachineSize(self._machineSize)
521 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
522 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'))
524 if self._selectedObj is not None:
525 scale = self._selectedObj.getScale()
526 size = self._selectedObj.getSize()
527 self.scaleXctrl.setValue(round(scale[0], 2))
528 self.scaleYctrl.setValue(round(scale[1], 2))
529 self.scaleZctrl.setValue(round(scale[2], 2))
530 self.scaleXmmctrl.setValue(round(size[0], 2))
531 self.scaleYmmctrl.setValue(round(size[1], 2))
532 self.scaleZmmctrl.setValue(round(size[2], 2))
534 def OnKeyChar(self, keyCode):
535 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
536 if self._selectedObj is not None:
537 self._deleteObject(self._selectedObj)
539 if keyCode == wx.WXK_UP:
540 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
542 elif keyCode == wx.WXK_DOWN:
543 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
545 elif keyCode == wx.WXK_PAGEUP:
546 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
548 elif keyCode == wx.WXK_PAGEDOWN:
549 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
552 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
553 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
554 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
555 from collections import defaultdict
556 from gc import get_objects
557 self._beforeLeakTest = defaultdict(int)
558 for i in get_objects():
559 self._beforeLeakTest[type(i)] += 1
560 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
561 from collections import defaultdict
562 from gc import get_objects
563 self._afterLeakTest = defaultdict(int)
564 for i in get_objects():
565 self._afterLeakTest[type(i)] += 1
566 for k in self._afterLeakTest:
567 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
568 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
570 def ShaderUpdate(self, v, f):
571 s = opengl.GLShader(v, f)
573 self._objectLoadShader.release()
574 self._objectLoadShader = s
575 for obj in self._scene.objects():
576 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
579 def OnMouseDown(self,e):
580 self._mouseX = e.GetX()
581 self._mouseY = e.GetY()
582 self._mouseClick3DPos = self._mouse3Dpos
583 self._mouseClickFocus = self._focusObj
585 self._mouseState = 'doubleClick'
587 self._mouseState = 'dragOrClick'
588 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
589 p0 -= self.getObjectCenterPos() - self._viewTarget
590 p1 -= self.getObjectCenterPos() - self._viewTarget
591 if self.tool.OnDragStart(p0, p1):
592 self._mouseState = 'tool'
593 if self._mouseState == 'dragOrClick':
594 if e.GetButton() == 1:
595 if self._focusObj is not None:
596 self._selectObject(self._focusObj, False)
599 def OnMouseUp(self, e):
600 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
602 if self._mouseState == 'dragOrClick':
603 if e.GetButton() == 1:
604 self._selectObject(self._focusObj)
605 if e.GetButton() == 3:
607 if self._focusObj is not None:
608 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
609 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
610 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
611 if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1:
612 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
613 if len(self._scene.objects()) > 0:
614 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
615 if menu.MenuItemCount > 0:
618 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
619 self._scene.pushFree()
621 elif self._mouseState == 'tool':
622 if self.tempMatrix is not None and self._selectedObj is not None:
623 self._selectedObj.applyMatrix(self.tempMatrix)
624 self._scene.pushFree()
625 self._selectObject(self._selectedObj)
626 self.tempMatrix = None
627 self.tool.OnDragEnd()
629 self._mouseState = None
631 def OnMouseMotion(self,e):
632 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
633 p0 -= self.getObjectCenterPos() - self._viewTarget
634 p1 -= self.getObjectCenterPos() - self._viewTarget
636 if e.Dragging() and self._mouseState is not None:
637 if self._mouseState == 'tool':
638 self.tool.OnDrag(p0, p1)
639 elif not e.LeftIsDown() and e.RightIsDown():
640 self._mouseState = 'drag'
641 if wx.GetKeyState(wx.WXK_SHIFT):
642 a = math.cos(math.radians(self._yaw)) / 3.0
643 b = math.sin(math.radians(self._yaw)) / 3.0
644 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
645 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
646 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
647 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
649 self._yaw += e.GetX() - self._mouseX
650 self._pitch -= e.GetY() - self._mouseY
651 if self._pitch > 170:
655 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
656 self._mouseState = 'drag'
657 self._zoom += e.GetY() - self._mouseY
660 if self._zoom > numpy.max(self._machineSize) * 3:
661 self._zoom = numpy.max(self._machineSize) * 3
662 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
663 self._mouseState = 'dragObject'
664 z = max(0, self._mouseClick3DPos[2])
665 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
666 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
671 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
672 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
673 diff = cursorZ1 - cursorZ0
674 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
675 if not e.Dragging() or self._mouseState != 'tool':
676 self.tool.OnMouseMove(p0, p1)
678 self._mouseX = e.GetX()
679 self._mouseY = e.GetY()
681 def OnMouseWheel(self, e):
682 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
683 delta = max(min(delta,4),-4)
684 self._zoom *= 1.0 - delta / 10.0
687 if self._zoom > numpy.max(self._machineSize) * 3:
688 self._zoom = numpy.max(self._machineSize) * 3
691 def OnMouseLeave(self, e):
694 def getMouseRay(self, x, y):
695 if self._viewport is None:
696 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
697 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
698 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
699 p0 -= self._viewTarget
700 p1 -= self._viewTarget
703 def _init3DView(self):
704 # set viewing projection
705 size = self.GetSize()
706 glViewport(0, 0, size.GetWidth(), size.GetHeight())
709 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
711 glDisable(GL_RESCALE_NORMAL)
712 glDisable(GL_LIGHTING)
714 glEnable(GL_DEPTH_TEST)
715 glDisable(GL_CULL_FACE)
717 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
719 glClearColor(0.8, 0.8, 0.8, 1.0)
723 glMatrixMode(GL_PROJECTION)
725 aspect = float(size.GetWidth()) / float(size.GetHeight())
726 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
728 glMatrixMode(GL_MODELVIEW)
730 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
733 if machineCom.machineIsConnected():
734 self.printButton._imageID = 6
735 self.printButton._tooltip = 'Print'
736 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
737 self.printButton._imageID = 2
738 self.printButton._tooltip = 'Toolpath to SD'
740 self.printButton._imageID = 3
741 self.printButton._tooltip = 'Save toolpath'
743 if self._animView is not None:
744 self._viewTarget = self._animView.getPosition()
745 if self._animView.isDone():
746 self._animView = None
747 if self._animZoom is not None:
748 self._zoom = self._animZoom.getPosition()
749 if self._animZoom.isDone():
750 self._animZoom = None
751 if self.viewMode == 'gcode' and self._gcode is not None:
753 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
756 if self._objectShader is None:
757 if opengl.hasShaderSupport():
758 self._objectShader = opengl.GLShader("""
759 varying float light_amount;
763 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
764 gl_FrontColor = gl_Color;
766 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
770 varying float light_amount;
774 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
777 self._objectOverhangShader = opengl.GLShader("""
778 uniform float cosAngle;
779 uniform mat3 rotMatrix;
780 varying float light_amount;
784 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
785 gl_FrontColor = gl_Color;
787 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
789 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
791 light_amount = -10.0;
795 varying float light_amount;
799 if (light_amount == -10.0)
801 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
803 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
807 self._objectLoadShader = opengl.GLShader("""
808 uniform float intensity;
810 varying float light_amount;
814 vec4 tmp = gl_Vertex;
815 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
816 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
817 gl_Position = gl_ModelViewProjectionMatrix * tmp;
818 gl_FrontColor = gl_Color;
820 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
824 uniform float intensity;
825 varying float light_amount;
829 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
833 self._objectShader = opengl.GLFakeShader()
834 self._objectOverhangShader = opengl.GLFakeShader()
835 self._objectLoadShader = None
837 glTranslate(0,0,-self._zoom)
838 glRotate(-self._pitch, 1,0,0)
839 glRotate(self._yaw, 0,0,1)
840 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
842 self._viewport = glGetIntegerv(GL_VIEWPORT)
843 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
844 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
846 glClearColor(1,1,1,1)
847 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
849 if self.viewMode != 'gcode':
850 for n in xrange(0, len(self._scene.objects())):
851 obj = self._scene.objects()[n]
852 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
853 self._renderObject(obj)
855 if self._mouseX > -1:
857 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
858 if n < len(self._scene.objects()):
859 self._focusObj = self._scene.objects()[n]
861 self._focusObj = None
862 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
863 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
864 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
865 self._mouse3Dpos -= self._viewTarget
868 glTranslate(0,0,-self._zoom)
869 glRotate(-self._pitch, 1,0,0)
870 glRotate(self._yaw, 0,0,1)
871 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
873 if self.viewMode == 'gcode':
874 if self._gcode is not None and self._gcode.layerList is None:
875 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
876 self._gcodeLoadThread.daemon = True
877 self._gcodeLoadThread.start()
878 if self._gcode is not None and self._gcode.layerList is not None:
880 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
882 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
883 for n in xrange(0, drawUpTill):
884 c = 1.0 - float(drawUpTill - n) / 15
886 if len(self._gcodeVBOs) < n + 1:
887 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
888 if time.time() - t > 0.5:
891 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
892 if n == drawUpTill - 1:
893 if len(self._gcodeVBOs[n]) < 6:
894 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
896 self._gcodeVBOs[n][5].render(GL_QUADS)
898 self._gcodeVBOs[n][6].render(GL_QUADS)
899 glColor3f(c/2, c/2, 0.0)
900 self._gcodeVBOs[n][7].render(GL_QUADS)
902 self._gcodeVBOs[n][8].render(GL_QUADS)
903 self._gcodeVBOs[n][9].render(GL_QUADS)
905 self._gcodeVBOs[n][10].render(GL_LINES)
908 self._gcodeVBOs[n][0].render(GL_LINES)
910 self._gcodeVBOs[n][1].render(GL_LINES)
911 glColor3f(c/2, c/2, 0.0)
912 self._gcodeVBOs[n][2].render(GL_LINES)
914 self._gcodeVBOs[n][3].render(GL_LINES)
915 self._gcodeVBOs[n][4].render(GL_LINES)
918 glStencilFunc(GL_ALWAYS, 1, 1)
919 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
921 if self.viewMode == 'overhang':
922 self._objectOverhangShader.bind()
923 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
925 self._objectShader.bind()
926 for obj in self._scene.objects():
927 if obj._loadAnim is not None:
928 if obj._loadAnim.isDone():
933 if self._focusObj == obj:
935 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
938 if self._selectedObj == obj or self._selectedObj is None:
939 #If we want transparent, then first render a solid black model to remove the printer size lines.
940 if self.viewMode == 'transparent':
941 glColor4f(0, 0, 0, 0)
942 self._renderObject(obj)
944 glBlendFunc(GL_ONE, GL_ONE)
945 glDisable(GL_DEPTH_TEST)
947 if self.viewMode == 'xray':
948 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
949 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
950 glEnable(GL_STENCIL_TEST)
952 if self.viewMode == 'overhang':
953 if self._selectedObj == obj and self.tempMatrix is not None:
954 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
956 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
958 if not self._scene.checkPlatform(obj):
959 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
960 self._renderObject(obj)
962 self._renderObject(obj, brightness)
963 glDisable(GL_STENCIL_TEST)
965 glEnable(GL_DEPTH_TEST)
966 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
968 if self.viewMode == 'xray':
971 glEnable(GL_STENCIL_TEST)
972 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
973 glDisable(GL_DEPTH_TEST)
974 for i in xrange(2, 15, 2):
975 glStencilFunc(GL_EQUAL, i, 0xFF)
976 glColor(float(i)/10, float(i)/10, float(i)/5)
978 glVertex3f(-1000,-1000,-10)
979 glVertex3f( 1000,-1000,-10)
980 glVertex3f( 1000, 1000,-10)
981 glVertex3f(-1000, 1000,-10)
983 for i in xrange(1, 15, 2):
984 glStencilFunc(GL_EQUAL, i, 0xFF)
985 glColor(float(i)/10, 0, 0)
987 glVertex3f(-1000,-1000,-10)
988 glVertex3f( 1000,-1000,-10)
989 glVertex3f( 1000, 1000,-10)
990 glVertex3f(-1000, 1000,-10)
993 glDisable(GL_STENCIL_TEST)
994 glEnable(GL_DEPTH_TEST)
996 self._objectShader.unbind()
998 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1000 if self._objectLoadShader is not None:
1001 self._objectLoadShader.bind()
1002 glColor4f(0.2, 0.6, 1.0, 1.0)
1003 for obj in self._scene.objects():
1004 if obj._loadAnim is None:
1006 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1007 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1008 self._renderObject(obj)
1009 self._objectLoadShader.unbind()
1014 if self.viewMode == 'gcode':
1015 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1016 glDisable(GL_DEPTH_TEST)
1019 glTranslate(0,-4,-10)
1020 glColor4ub(60,60,60,255)
1021 opengl.glDrawStringCenter('Loading toolpath for visualization...')
1024 #Draw the object box-shadow, so you can see where it will collide with other objects.
1025 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1026 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1028 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1030 glEnable(GL_CULL_FACE)
1031 glColor4f(0,0,0,0.12)
1033 glVertex3f(-size[0], size[1], 0.1)
1034 glVertex3f(-size[0], -size[1], 0.1)
1035 glVertex3f( size[0], -size[1], 0.1)
1036 glVertex3f( size[0], size[1], 0.1)
1038 glDisable(GL_CULL_FACE)
1041 #Draw the outline of the selected object, on top of everything else except the GUI.
1042 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1043 glDisable(GL_DEPTH_TEST)
1044 glEnable(GL_CULL_FACE)
1045 glEnable(GL_STENCIL_TEST)
1047 glStencilFunc(GL_EQUAL, 0, 255)
1049 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1051 glColor4f(1,1,1,0.5)
1052 self._renderObject(self._selectedObj)
1053 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1055 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1056 glDisable(GL_STENCIL_TEST)
1057 glDisable(GL_CULL_FACE)
1058 glEnable(GL_DEPTH_TEST)
1060 if self._selectedObj is not None:
1062 pos = self.getObjectCenterPos()
1063 glTranslate(pos[0], pos[1], pos[2])
1066 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1067 glDisable(GL_DEPTH_TEST)
1070 glTranslate(0,-4,-10)
1071 glColor4ub(60,60,60,255)
1072 opengl.glDrawStringCenter('Overhang view not working due to lack of OpenGL shaders support.')
1075 def _renderObject(self, obj, brightness = False, addSink = True):
1078 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1080 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1082 if self.tempMatrix is not None and obj == self._selectedObj:
1083 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1084 glMultMatrixf(tempMatrix)
1086 offset = obj.getDrawOffset()
1087 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1089 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1090 glMultMatrixf(tempMatrix)
1093 for m in obj._meshList:
1095 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1097 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1102 def _drawMachine(self):
1103 glEnable(GL_CULL_FACE)
1106 if profile.getPreference('machine_type') == 'ultimaker':
1107 glColor4f(1,1,1,0.5)
1108 self._objectShader.bind()
1109 self._renderObject(self._platformMesh, False, False)
1110 self._objectShader.unbind()
1112 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
1113 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1114 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1115 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1116 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1117 v4 = [ size[0] / 2, size[1] / 2, 0]
1118 v5 = [ size[0] / 2,-size[1] / 2, 0]
1119 v6 = [-size[0] / 2, size[1] / 2, 0]
1120 v7 = [-size[0] / 2,-size[1] / 2, 0]
1122 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1123 glEnableClientState(GL_VERTEX_ARRAY)
1124 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1126 glColor4ub(5, 171, 231, 64)
1127 glDrawArrays(GL_QUADS, 0, 4)
1128 glColor4ub(5, 171, 231, 96)
1129 glDrawArrays(GL_QUADS, 4, 8)
1130 glColor4ub(5, 171, 231, 128)
1131 glDrawArrays(GL_QUADS, 12, 8)
1133 sx = self._machineSize[0]
1134 sy = self._machineSize[1]
1135 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1136 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1141 x1 = max(min(x1, sx/2), -sx/2)
1142 y1 = max(min(y1, sy/2), -sy/2)
1143 x2 = max(min(x2, sx/2), -sx/2)
1144 y2 = max(min(y2, sy/2), -sy/2)
1145 if (x & 1) == (y & 1):
1146 glColor4ub(5, 171, 231, 127)
1148 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1150 glVertex3f(x1, y1, -0.02)
1151 glVertex3f(x2, y1, -0.02)
1152 glVertex3f(x2, y2, -0.02)
1153 glVertex3f(x1, y2, -0.02)
1156 glDisableClientState(GL_VERTEX_ARRAY)
1158 glDisable(GL_CULL_FACE)
1160 def _generateGCodeVBOs(self, layer):
1162 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1163 pointList = numpy.zeros((0,3), numpy.float32)
1165 if path['type'] == 'extrude' and path['pathType'] == extrudeType:
1167 a = numpy.concatenate((a[:-1], a[1:]), 1)
1168 a = a.reshape((len(a) * 2, 3))
1169 pointList = numpy.concatenate((pointList, a))
1170 ret.append(opengl.GLVBO(pointList))
1173 def _generateGCodeVBOs2(self, layer):
1174 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1175 filamentArea = math.pi * filamentRadius * filamentRadius
1178 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1179 pointList = numpy.zeros((0,3), numpy.float32)
1181 if path['type'] == 'extrude' and path['pathType'] == extrudeType:
1183 if extrudeType == 'FILL':
1186 normal = a[1:] - a[:-1]
1187 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1188 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1191 ePerDist = path['extrusion'][1:] / lens
1192 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1194 normal[:,0] *= lineWidth
1195 normal[:,1] *= lineWidth
1197 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1198 b = numpy.concatenate((b, a[1:] + normal), 1)
1199 b = numpy.concatenate((b, a[1:] - normal), 1)
1200 b = numpy.concatenate((b, a[:-1] - normal), 1)
1201 b = numpy.concatenate((b, a[:-1] + normal), 1)
1202 #b = numpy.concatenate((b, a[:-1]), 1)
1203 #b = numpy.concatenate((b, a[:-1]), 1)
1204 b = b.reshape((len(b) * 4, 3))
1207 normal2 = normal[:-1] + normal[1:]
1208 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1209 normal2[:,0] /= lens2
1210 normal2[:,1] /= lens2
1211 normal2[:,0] *= lineWidth[:-1]
1212 normal2[:,1] *= lineWidth[:-1]
1214 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1215 c = numpy.concatenate((c, a[1:-1]), 1)
1216 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1217 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1218 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1220 c = numpy.concatenate((c, a[1:-1]), 1)
1221 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1222 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1223 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1225 c = c.reshape((len(c) * 8, 3))
1227 pointList = numpy.concatenate((pointList, b, c))
1229 pointList = numpy.concatenate((pointList, b))
1230 ret.append(opengl.GLVBO(pointList))
1232 pointList = numpy.zeros((0,3), numpy.float32)
1234 if path['type'] == 'move':
1235 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1236 a = numpy.concatenate((a[:-1], a[1:]), 1)
1237 a = a.reshape((len(a) * 2, 3))
1238 pointList = numpy.concatenate((pointList, a))
1239 if path['type'] == 'retract':
1240 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1241 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1242 a = a.reshape((len(a) * 2, 3))
1243 pointList = numpy.concatenate((pointList, a))
1244 ret.append(opengl.GLVBO(pointList))
1248 def getObjectCenterPos(self):
1249 if self._selectedObj is None:
1250 return [0.0, 0.0, 0.0]
1251 pos = self._selectedObj.getPosition()
1252 size = self._selectedObj.getSize()
1253 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1255 def getObjectBoundaryCircle(self):
1256 if self._selectedObj is None:
1258 return self._selectedObj.getBoundaryCircle()
1260 def getObjectSize(self):
1261 if self._selectedObj is None:
1262 return [0.0, 0.0, 0.0]
1263 return self._selectedObj.getSize()
1265 def getObjectMatrix(self):
1266 if self._selectedObj is None:
1267 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1268 return self._selectedObj.getMatrix()
1270 class shaderEditor(wx.Dialog):
1271 def __init__(self, parent, callback, v, f):
1272 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1273 self._callback = callback
1274 s = wx.BoxSizer(wx.VERTICAL)
1276 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1277 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1278 s.Add(self._vertex, 1, flag=wx.EXPAND)
1279 s.Add(self._fragment, 1, flag=wx.EXPAND)
1281 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1282 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1284 self.SetPosition(self.GetParent().GetPosition())
1285 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1288 def OnText(self, e):
1289 self._callback(self._vertex.GetValue(), self._fragment.GetValue())