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)
109 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
113 self.updateToolButtons()
114 self.updateProfileToControls()
116 def showLoadModel(self, button = 1):
118 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)
119 dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
120 if dlg.ShowModal() != wx.ID_OK:
123 filenames = dlg.GetPaths()
125 if len(filenames) < 1:
127 profile.putPreference('lastFile', filenames[0])
129 for filename in filenames:
130 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
131 ext = filename[filename.rfind('.')+1:].upper()
132 if ext == 'G' or ext == 'GCODE':
133 gcodeFilename = filename
134 if gcodeFilename is not None:
135 if self._gcode is not None:
137 for layerVBOlist in self._gcodeVBOs:
138 for vbo in layerVBOlist:
139 self.glReleaseList.append(vbo)
141 self._gcode = gcodeInterpreter.gcode()
142 self._gcodeFilename = gcodeFilename
143 self.printButton.setBottomText('')
144 self.viewSelection.setValue(4)
145 self.printButton.setDisabled(False)
148 if self.viewSelection.getValue() == 4:
149 self.viewSelection.setValue(0)
151 self.loadScene(filenames)
153 def showSaveModel(self):
154 if len(self._scene.objects()) < 1:
156 dlg=wx.FileDialog(self, 'Save 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
157 dlg.SetWildcard(meshLoader.saveWildcardFilter())
158 if dlg.ShowModal() != wx.ID_OK:
161 filename = dlg.GetPath()
163 meshLoader.saveMeshes(filename, self._scene.objects())
165 def showPrintWindow(self, button):
167 if machineCom.machineIsConnected():
168 printWindow.printFile(self._gcodeFilename)
169 if self._gcodeFilename == self._slicer.getGCodeFilename():
170 self._slicer.submitSliceInfoOnline()
171 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
172 drives = removableStorage.getPossibleSDcardDrives()
174 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))
175 if dlg.ShowModal() != wx.ID_OK:
178 drive = drives[dlg.GetSelection()]
182 filename = self._scene._objectList[0].getName() + '.gcode'
183 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
188 self.Bind(wx.EVT_MENU, lambda e: printWindow.printFile(self._gcodeFilename), menu.Append(-1, 'Print with USB'))
189 self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, 'Save GCode...'))
190 self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, 'Slice engine log...'))
194 def showSaveGCode(self):
195 defPath = profile.getPreference('lastFile')
196 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
197 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
198 dlg.SetFilename(self._scene._objectList[0].getName())
199 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
200 if dlg.ShowModal() != wx.ID_OK:
203 filename = dlg.GetPath()
206 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
208 def _copyFile(self, fileA, fileB, allowEject = False):
210 size = float(os.stat(fileA).st_size)
211 with open(fileA, 'rb') as fsrc:
212 with open(fileB, 'wb') as fdst:
214 buf = fsrc.read(16*1024)
218 self.printButton.setProgressBar(float(fsrc.tell()) / size)
223 self.notification.message("Failed to save")
226 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...'))
228 self.notification.message("Saved as %s" % (fileB))
229 self.printButton.setProgressBar(None)
230 if fileA == self._slicer.getGCodeFilename():
231 self._slicer.submitSliceInfoOnline()
233 def _showSliceLog(self):
234 dlg = wx.TextEntryDialog(self, "The slicing engine reported the following", "Engine log...", '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
238 def OnToolSelect(self, button):
239 if self.rotateToolButton.getSelected():
240 self.tool = previewTools.toolRotate(self)
241 elif self.scaleToolButton.getSelected():
242 self.tool = previewTools.toolScale(self)
243 elif self.mirrorToolButton.getSelected():
244 self.tool = previewTools.toolNone(self)
246 self.tool = previewTools.toolNone(self)
247 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
248 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
249 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
250 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
251 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
252 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
253 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
254 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
256 def updateToolButtons(self):
257 if self._selectedObj is None:
261 self.rotateToolButton.setHidden(hidden)
262 self.scaleToolButton.setHidden(hidden)
263 self.mirrorToolButton.setHidden(hidden)
265 self.rotateToolButton.setSelected(False)
266 self.scaleToolButton.setSelected(False)
267 self.mirrorToolButton.setSelected(False)
270 def OnViewChange(self):
271 if self.viewSelection.getValue() == 4:
272 self.viewMode = 'gcode'
273 if self._gcode is not None and self._gcode.layerList is not None:
274 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
275 self.layerSelect.setValue(len(self._gcode.layerList) - 1)
276 self._selectObject(None)
277 elif self.viewSelection.getValue() == 1:
278 self.viewMode = 'overhang'
279 elif self.viewSelection.getValue() == 2:
280 self.viewMode = 'transparent'
281 elif self.viewSelection.getValue() == 3:
282 self.viewMode = 'xray'
284 self.viewMode = 'normal'
285 self.layerSelect.setHidden(self.viewMode != 'gcode')
288 def OnRotateReset(self, button):
289 if self._selectedObj is None:
291 self._selectedObj.resetRotation()
292 self._scene.pushFree()
293 self._selectObject(self._selectedObj)
296 def OnLayFlat(self, button):
297 if self._selectedObj is None:
299 self._selectedObj.layFlat()
300 self._scene.pushFree()
301 self._selectObject(self._selectedObj)
304 def OnScaleReset(self, button):
305 if self._selectedObj is None:
307 self._selectedObj.resetScale()
308 self._selectObject(self._selectedObj)
309 self.updateProfileToControls()
312 def OnScaleMax(self, button):
313 if self._selectedObj is None:
315 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
316 self._scene.pushFree()
317 self._selectObject(self._selectedObj)
318 self.updateProfileToControls()
321 def OnMirror(self, axis):
322 if self._selectedObj is None:
324 self._selectedObj.mirror(axis)
327 def OnScaleEntry(self, value, axis):
328 if self._selectedObj is None:
334 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
335 self.updateProfileToControls()
336 self._scene.pushFree()
337 self._selectObject(self._selectedObj)
340 def OnScaleEntryMM(self, value, axis):
341 if self._selectedObj is None:
347 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
348 self.updateProfileToControls()
349 self._scene.pushFree()
350 self._selectObject(self._selectedObj)
353 def OnDeleteAll(self, e):
354 while len(self._scene.objects()) > 0:
355 self._deleteObject(self._scene.objects()[0])
356 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
358 def OnMultiply(self, e):
359 if self._focusObj is None:
362 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
363 if dlg.ShowModal() != wx.ID_OK:
372 self._scene.add(newObj)
373 self._scene.centerAll()
374 if not self._scene.checkPlatform(newObj):
379 self.notification.message("Could not create more then %d items" % (n - 1))
380 self._scene.remove(newObj)
381 self._scene.centerAll()
384 def OnSplitObject(self, e):
385 if self._focusObj is None:
387 self._scene.remove(self._focusObj)
388 for obj in self._focusObj.split(self._splitCallback):
389 if numpy.max(obj.getSize()) > 2.0:
391 self._scene.centerAll()
392 self._selectObject(None)
395 def _splitCallback(self, progress):
398 def OnMergeObjects(self, e):
399 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
401 self._scene.merge(self._selectedObj, self._focusObj)
404 def sceneUpdated(self):
405 self._sceneUpdateTimer.Start(500, True)
406 self._slicer.abortSlicer()
407 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
410 def _onRunSlicer(self, e):
411 if self._isSimpleMode:
412 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
413 self._slicer.runSlicer(self._scene)
414 if self._isSimpleMode:
415 profile.resetTempOverride()
417 def _updateSliceProgress(self, progressValue, ready):
419 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
421 self.printButton.setDisabled(not ready)
422 if progressValue >= 0.0:
423 self.printButton.setProgressBar(progressValue)
425 self.printButton.setProgressBar(None)
426 if self._gcode is not None:
428 for layerVBOlist in self._gcodeVBOs:
429 for vbo in layerVBOlist:
430 self.glReleaseList.append(vbo)
433 self.printButton.setProgressBar(None)
434 cost = self._slicer.getFilamentCost()
436 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
438 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
439 self._gcode = gcodeInterpreter.gcode()
440 self._gcodeFilename = self._slicer.getGCodeFilename()
442 self.printButton.setBottomText('')
445 def _loadGCode(self):
446 self._gcode.progressCallback = self._gcodeLoadCallback
447 self._gcode.load(self._gcodeFilename)
449 def _gcodeLoadCallback(self, progress):
450 if self._gcode is None:
452 if len(self._gcode.layerList) % 15 == 0:
454 if self._gcode is None:
456 if self.layerSelect.getValue() == self.layerSelect.getMaxValue():
457 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
458 self.layerSelect.setValue(self.layerSelect.getMaxValue())
460 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
461 if self.viewMode == 'gcode':
465 def loadScene(self, fileList):
466 for filename in fileList:
468 objList = meshLoader.loadMeshes(filename)
470 traceback.print_exc()
473 if self._objectLoadShader is not None:
474 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
478 self._scene.centerAll()
479 self._selectObject(obj)
482 def _deleteObject(self, obj):
483 if obj == self._selectedObj:
484 self._selectObject(None)
485 if obj == self._focusObj:
486 self._focusObj = None
487 self._scene.remove(obj)
488 for m in obj._meshList:
489 if m.vbo is not None and m.vbo.decRef():
490 self.glReleaseList.append(m.vbo)
495 def _selectObject(self, obj, zoom = True):
496 if obj != self._selectedObj:
497 self._selectedObj = obj
498 self.updateProfileToControls()
499 self.updateToolButtons()
500 if zoom and obj is not None:
501 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
502 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
503 newZoom = obj.getBoundaryCircle() * 6
504 if newZoom > numpy.max(self._machineSize) * 3:
505 newZoom = numpy.max(self._machineSize) * 3
506 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
508 def updateProfileToControls(self):
509 oldSimpleMode = self._isSimpleMode
510 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
511 if self._isSimpleMode and not oldSimpleMode:
512 self._scene.arrangeAll()
514 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
515 self._objColors[0] = profile.getPreferenceColour('model_colour')
516 self._objColors[1] = profile.getPreferenceColour('model_colour2')
517 self._objColors[2] = profile.getPreferenceColour('model_colour3')
518 self._objColors[3] = profile.getPreferenceColour('model_colour4')
519 self._scene.setMachineSize(self._machineSize)
520 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
521 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'))
523 if self._selectedObj is not None:
524 scale = self._selectedObj.getScale()
525 size = self._selectedObj.getSize()
526 self.scaleXctrl.setValue(round(scale[0], 2))
527 self.scaleYctrl.setValue(round(scale[1], 2))
528 self.scaleZctrl.setValue(round(scale[2], 2))
529 self.scaleXmmctrl.setValue(round(size[0], 2))
530 self.scaleYmmctrl.setValue(round(size[1], 2))
531 self.scaleZmmctrl.setValue(round(size[2], 2))
533 def OnKeyChar(self, keyCode):
534 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
535 if self._selectedObj is not None:
536 self._deleteObject(self._selectedObj)
538 if keyCode == wx.WXK_UP:
539 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
541 elif keyCode == wx.WXK_DOWN:
542 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
544 elif keyCode == wx.WXK_PAGEUP:
545 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
547 elif keyCode == wx.WXK_PAGEDOWN:
548 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
551 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
552 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
553 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
554 from collections import defaultdict
555 from gc import get_objects
556 self._beforeLeakTest = defaultdict(int)
557 for i in get_objects():
558 self._beforeLeakTest[type(i)] += 1
559 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
560 from collections import defaultdict
561 from gc import get_objects
562 self._afterLeakTest = defaultdict(int)
563 for i in get_objects():
564 self._afterLeakTest[type(i)] += 1
565 for k in self._afterLeakTest:
566 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
567 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
569 def ShaderUpdate(self, v, f):
570 s = opengl.GLShader(v, f)
572 self._objectLoadShader.release()
573 self._objectLoadShader = s
574 for obj in self._scene.objects():
575 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
578 def OnMouseDown(self,e):
579 self._mouseX = e.GetX()
580 self._mouseY = e.GetY()
581 self._mouseClick3DPos = self._mouse3Dpos
582 self._mouseClickFocus = self._focusObj
584 self._mouseState = 'doubleClick'
586 self._mouseState = 'dragOrClick'
587 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
588 p0 -= self.getObjectCenterPos() - self._viewTarget
589 p1 -= self.getObjectCenterPos() - self._viewTarget
590 if self.tool.OnDragStart(p0, p1):
591 self._mouseState = 'tool'
592 if self._mouseState == 'dragOrClick':
593 if e.GetButton() == 1:
594 if self._focusObj is not None:
595 self._selectObject(self._focusObj, False)
598 def OnMouseUp(self, e):
599 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
601 if self._mouseState == 'dragOrClick':
602 if e.GetButton() == 1:
603 self._selectObject(self._focusObj)
604 if e.GetButton() == 3:
606 if self._focusObj is not None:
607 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
608 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
609 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
610 if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1:
611 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
612 if len(self._scene.objects()) > 0:
613 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
614 if menu.MenuItemCount > 0:
617 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
618 self._scene.pushFree()
620 elif self._mouseState == 'tool':
621 if self.tempMatrix is not None and self._selectedObj is not None:
622 self._selectedObj.applyMatrix(self.tempMatrix)
623 self._scene.pushFree()
624 self._selectObject(self._selectedObj)
625 self.tempMatrix = None
626 self.tool.OnDragEnd()
628 self._mouseState = None
630 def OnMouseMotion(self,e):
631 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
632 p0 -= self.getObjectCenterPos() - self._viewTarget
633 p1 -= self.getObjectCenterPos() - self._viewTarget
635 if e.Dragging() and self._mouseState is not None:
636 if self._mouseState == 'tool':
637 self.tool.OnDrag(p0, p1)
638 elif not e.LeftIsDown() and e.RightIsDown():
639 self._mouseState = 'drag'
640 if wx.GetKeyState(wx.WXK_SHIFT):
641 a = math.cos(math.radians(self._yaw)) / 3.0
642 b = math.sin(math.radians(self._yaw)) / 3.0
643 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
644 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
645 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
646 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
648 self._yaw += e.GetX() - self._mouseX
649 self._pitch -= e.GetY() - self._mouseY
650 if self._pitch > 170:
654 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
655 self._mouseState = 'drag'
656 self._zoom += e.GetY() - self._mouseY
659 if self._zoom > numpy.max(self._machineSize) * 3:
660 self._zoom = numpy.max(self._machineSize) * 3
661 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
662 self._mouseState = 'dragObject'
663 z = max(0, self._mouseClick3DPos[2])
664 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
665 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
670 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
671 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
672 diff = cursorZ1 - cursorZ0
673 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
674 if not e.Dragging() or self._mouseState != 'tool':
675 self.tool.OnMouseMove(p0, p1)
677 self._mouseX = e.GetX()
678 self._mouseY = e.GetY()
680 def OnMouseWheel(self, e):
681 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
682 delta = max(min(delta,4),-4)
683 self._zoom *= 1.0 - delta / 10.0
686 if self._zoom > numpy.max(self._machineSize) * 3:
687 self._zoom = numpy.max(self._machineSize) * 3
690 def OnMouseLeave(self, e):
693 def getMouseRay(self, x, y):
694 if self._viewport is None:
695 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
696 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
697 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
698 p0 -= self._viewTarget
699 p1 -= self._viewTarget
702 def _init3DView(self):
703 # set viewing projection
704 size = self.GetSize()
705 glViewport(0, 0, size.GetWidth(), size.GetHeight())
708 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
710 glDisable(GL_RESCALE_NORMAL)
711 glDisable(GL_LIGHTING)
713 glEnable(GL_DEPTH_TEST)
714 glDisable(GL_CULL_FACE)
716 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
718 glClearColor(0.8, 0.8, 0.8, 1.0)
722 glMatrixMode(GL_PROJECTION)
724 aspect = float(size.GetWidth()) / float(size.GetHeight())
725 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
727 glMatrixMode(GL_MODELVIEW)
729 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
732 if machineCom.machineIsConnected():
733 self.printButton._imageID = 6
734 self.printButton._tooltip = 'Print'
735 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
736 self.printButton._imageID = 2
737 self.printButton._tooltip = 'Toolpath to SD'
739 self.printButton._imageID = 3
740 self.printButton._tooltip = 'Save toolpath'
742 if self._animView is not None:
743 self._viewTarget = self._animView.getPosition()
744 if self._animView.isDone():
745 self._animView = None
746 if self._animZoom is not None:
747 self._zoom = self._animZoom.getPosition()
748 if self._animZoom.isDone():
749 self._animZoom = None
750 if self.viewMode == 'gcode' and self._gcode is not None:
752 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
755 if self._objectShader is None:
756 if opengl.hasShaderSupport():
757 self._objectShader = opengl.GLShader("""
758 varying float light_amount;
762 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
763 gl_FrontColor = gl_Color;
765 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
769 varying float light_amount;
773 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
776 self._objectOverhangShader = opengl.GLShader("""
777 uniform float cosAngle;
778 uniform mat3 rotMatrix;
779 varying float light_amount;
783 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
784 gl_FrontColor = gl_Color;
786 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
788 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
790 light_amount = -10.0;
794 varying float light_amount;
798 if (light_amount == -10.0)
800 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
802 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
806 self._objectLoadShader = opengl.GLShader("""
807 uniform float intensity;
809 varying float light_amount;
813 vec4 tmp = gl_Vertex;
814 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
815 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
816 gl_Position = gl_ModelViewProjectionMatrix * tmp;
817 gl_FrontColor = gl_Color;
819 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
823 uniform float intensity;
824 varying float light_amount;
828 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
832 self._objectShader = opengl.GLFakeShader()
833 self._objectOverhangShader = opengl.GLFakeShader()
834 self._objectLoadShader = None
836 glTranslate(0,0,-self._zoom)
837 glRotate(-self._pitch, 1,0,0)
838 glRotate(self._yaw, 0,0,1)
839 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
841 self._viewport = glGetIntegerv(GL_VIEWPORT)
842 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
843 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
845 glClearColor(1,1,1,1)
846 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
848 if self.viewMode != 'gcode':
849 for n in xrange(0, len(self._scene.objects())):
850 obj = self._scene.objects()[n]
851 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
852 self._renderObject(obj)
854 if self._mouseX > -1:
856 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
857 if n < len(self._scene.objects()):
858 self._focusObj = self._scene.objects()[n]
860 self._focusObj = None
861 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
862 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
863 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
864 self._mouse3Dpos -= self._viewTarget
867 glTranslate(0,0,-self._zoom)
868 glRotate(-self._pitch, 1,0,0)
869 glRotate(self._yaw, 0,0,1)
870 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
872 if self.viewMode == 'gcode':
873 if self._gcode is not None and self._gcode.layerList is None:
874 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
875 self._gcodeLoadThread.daemon = True
876 self._gcodeLoadThread.start()
877 if self._gcode is not None and self._gcode.layerList is not None:
879 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
881 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
882 for n in xrange(0, drawUpTill):
883 c = 1.0 - float(drawUpTill - n) / 15
885 if len(self._gcodeVBOs) < n + 1:
886 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
887 if time.time() - t > 0.5:
890 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
891 if n == drawUpTill - 1:
892 if len(self._gcodeVBOs[n]) < 6:
893 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
895 self._gcodeVBOs[n][5].render(GL_QUADS)
897 self._gcodeVBOs[n][6].render(GL_QUADS)
898 glColor3f(c/2, c/2, 0.0)
899 self._gcodeVBOs[n][7].render(GL_QUADS)
901 self._gcodeVBOs[n][8].render(GL_QUADS)
902 self._gcodeVBOs[n][9].render(GL_QUADS)
904 self._gcodeVBOs[n][10].render(GL_LINES)
907 self._gcodeVBOs[n][0].render(GL_LINES)
909 self._gcodeVBOs[n][1].render(GL_LINES)
910 glColor3f(c/2, c/2, 0.0)
911 self._gcodeVBOs[n][2].render(GL_LINES)
913 self._gcodeVBOs[n][3].render(GL_LINES)
914 self._gcodeVBOs[n][4].render(GL_LINES)
917 glStencilFunc(GL_ALWAYS, 1, 1)
918 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
920 if self.viewMode == 'overhang':
921 self._objectOverhangShader.bind()
922 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
924 self._objectShader.bind()
925 for obj in self._scene.objects():
926 if obj._loadAnim is not None:
927 if obj._loadAnim.isDone():
932 if self._focusObj == obj:
934 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
937 if self._selectedObj == obj or self._selectedObj is None:
938 #If we want transparent, then first render a solid black model to remove the printer size lines.
939 if self.viewMode == 'transparent':
940 glColor4f(0, 0, 0, 0)
941 self._renderObject(obj)
943 glBlendFunc(GL_ONE, GL_ONE)
944 glDisable(GL_DEPTH_TEST)
946 if self.viewMode == 'xray':
947 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
948 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
949 glEnable(GL_STENCIL_TEST)
951 if self.viewMode == 'overhang':
952 if self._selectedObj == obj and self.tempMatrix is not None:
953 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
955 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
957 if not self._scene.checkPlatform(obj):
958 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
959 self._renderObject(obj)
961 self._renderObject(obj, brightness)
962 glDisable(GL_STENCIL_TEST)
964 glEnable(GL_DEPTH_TEST)
965 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
967 if self.viewMode == 'xray':
970 glEnable(GL_STENCIL_TEST)
971 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
972 glDisable(GL_DEPTH_TEST)
973 for i in xrange(2, 15, 2):
974 glStencilFunc(GL_EQUAL, i, 0xFF)
975 glColor(float(i)/10, float(i)/10, float(i)/5)
977 glVertex3f(-1000,-1000,-10)
978 glVertex3f( 1000,-1000,-10)
979 glVertex3f( 1000, 1000,-10)
980 glVertex3f(-1000, 1000,-10)
982 for i in xrange(1, 15, 2):
983 glStencilFunc(GL_EQUAL, i, 0xFF)
984 glColor(float(i)/10, 0, 0)
986 glVertex3f(-1000,-1000,-10)
987 glVertex3f( 1000,-1000,-10)
988 glVertex3f( 1000, 1000,-10)
989 glVertex3f(-1000, 1000,-10)
992 glDisable(GL_STENCIL_TEST)
993 glEnable(GL_DEPTH_TEST)
995 self._objectShader.unbind()
997 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
999 if self._objectLoadShader is not None:
1000 self._objectLoadShader.bind()
1001 glColor4f(0.2, 0.6, 1.0, 1.0)
1002 for obj in self._scene.objects():
1003 if obj._loadAnim is None:
1005 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1006 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1007 self._renderObject(obj)
1008 self._objectLoadShader.unbind()
1013 if self.viewMode == 'gcode':
1014 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1015 glDisable(GL_DEPTH_TEST)
1018 glTranslate(0,-4,-10)
1019 glColor4ub(60,60,60,255)
1020 opengl.glDrawStringCenter('Loading toolpath for visualization...')
1023 #Draw the object box-shadow, so you can see where it will collide with other objects.
1024 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1025 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1027 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1029 glEnable(GL_CULL_FACE)
1030 glColor4f(0,0,0,0.12)
1032 glVertex3f(-size[0], size[1], 0.1)
1033 glVertex3f(-size[0], -size[1], 0.1)
1034 glVertex3f( size[0], -size[1], 0.1)
1035 glVertex3f( size[0], size[1], 0.1)
1037 glDisable(GL_CULL_FACE)
1040 #Draw the outline of the selected object, on top of everything else except the GUI.
1041 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1042 glDisable(GL_DEPTH_TEST)
1043 glEnable(GL_CULL_FACE)
1044 glEnable(GL_STENCIL_TEST)
1046 glStencilFunc(GL_EQUAL, 0, 255)
1048 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1050 glColor4f(1,1,1,0.5)
1051 self._renderObject(self._selectedObj)
1052 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1054 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1055 glDisable(GL_STENCIL_TEST)
1056 glDisable(GL_CULL_FACE)
1057 glEnable(GL_DEPTH_TEST)
1059 if self._selectedObj is not None:
1061 pos = self.getObjectCenterPos()
1062 glTranslate(pos[0], pos[1], pos[2])
1065 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1066 glDisable(GL_DEPTH_TEST)
1069 glTranslate(0,-4,-10)
1070 glColor4ub(60,60,60,255)
1071 opengl.glDrawStringCenter('Overhang view not working due to lack of OpenGL shaders support.')
1074 def _renderObject(self, obj, brightness = False, addSink = True):
1077 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1079 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1081 if self.tempMatrix is not None and obj == self._selectedObj:
1082 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1083 glMultMatrixf(tempMatrix)
1085 offset = obj.getDrawOffset()
1086 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1088 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1089 glMultMatrixf(tempMatrix)
1092 for m in obj._meshList:
1094 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1096 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1101 def _drawMachine(self):
1102 glEnable(GL_CULL_FACE)
1105 if profile.getPreference('machine_type') == 'ultimaker':
1106 glColor4f(1,1,1,0.5)
1107 self._objectShader.bind()
1108 self._renderObject(self._platformMesh, False, False)
1109 self._objectShader.unbind()
1111 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
1112 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1113 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1114 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1115 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1116 v4 = [ size[0] / 2, size[1] / 2, 0]
1117 v5 = [ size[0] / 2,-size[1] / 2, 0]
1118 v6 = [-size[0] / 2, size[1] / 2, 0]
1119 v7 = [-size[0] / 2,-size[1] / 2, 0]
1121 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1122 glEnableClientState(GL_VERTEX_ARRAY)
1123 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1125 glColor4ub(5, 171, 231, 64)
1126 glDrawArrays(GL_QUADS, 0, 4)
1127 glColor4ub(5, 171, 231, 96)
1128 glDrawArrays(GL_QUADS, 4, 8)
1129 glColor4ub(5, 171, 231, 128)
1130 glDrawArrays(GL_QUADS, 12, 8)
1132 sx = self._machineSize[0]
1133 sy = self._machineSize[1]
1134 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1135 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1140 x1 = max(min(x1, sx/2), -sx/2)
1141 y1 = max(min(y1, sy/2), -sy/2)
1142 x2 = max(min(x2, sx/2), -sx/2)
1143 y2 = max(min(y2, sy/2), -sy/2)
1144 if (x & 1) == (y & 1):
1145 glColor4ub(5, 171, 231, 127)
1147 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1149 glVertex3f(x1, y1, -0.02)
1150 glVertex3f(x2, y1, -0.02)
1151 glVertex3f(x2, y2, -0.02)
1152 glVertex3f(x1, y2, -0.02)
1155 glDisableClientState(GL_VERTEX_ARRAY)
1157 glDisable(GL_CULL_FACE)
1159 def _generateGCodeVBOs(self, layer):
1161 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1162 pointList = numpy.zeros((0,3), numpy.float32)
1164 if path['type'] == 'extrude' and path['pathType'] == extrudeType:
1166 a = numpy.concatenate((a[:-1], a[1:]), 1)
1167 a = a.reshape((len(a) * 2, 3))
1168 pointList = numpy.concatenate((pointList, a))
1169 ret.append(opengl.GLVBO(pointList))
1172 def _generateGCodeVBOs2(self, layer):
1173 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1174 filamentArea = math.pi * filamentRadius * filamentRadius
1177 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1178 pointList = numpy.zeros((0,3), numpy.float32)
1180 if path['type'] == 'extrude' and path['pathType'] == extrudeType:
1182 if extrudeType == 'FILL':
1185 normal = a[1:] - a[:-1]
1186 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1187 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1190 ePerDist = path['extrusion'][1:] / lens
1191 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1193 normal[:,0] *= lineWidth
1194 normal[:,1] *= lineWidth
1196 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1197 b = numpy.concatenate((b, a[1:] + normal), 1)
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]), 1)
1202 #b = numpy.concatenate((b, a[:-1]), 1)
1203 b = b.reshape((len(b) * 4, 3))
1206 normal2 = normal[:-1] + normal[1:]
1207 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1208 normal2[:,0] /= lens2
1209 normal2[:,1] /= lens2
1210 normal2[:,0] *= lineWidth[:-1]
1211 normal2[:,1] *= lineWidth[:-1]
1213 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1214 c = numpy.concatenate((c, a[1:-1]), 1)
1215 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1216 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1217 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1219 c = numpy.concatenate((c, a[1:-1]), 1)
1220 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1221 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1222 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1224 c = c.reshape((len(c) * 8, 3))
1226 pointList = numpy.concatenate((pointList, b, c))
1228 pointList = numpy.concatenate((pointList, b))
1229 ret.append(opengl.GLVBO(pointList))
1231 pointList = numpy.zeros((0,3), numpy.float32)
1233 if path['type'] == 'move':
1234 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1235 a = numpy.concatenate((a[:-1], a[1:]), 1)
1236 a = a.reshape((len(a) * 2, 3))
1237 pointList = numpy.concatenate((pointList, a))
1238 if path['type'] == 'retract':
1239 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1240 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1241 a = a.reshape((len(a) * 2, 3))
1242 pointList = numpy.concatenate((pointList, a))
1243 ret.append(opengl.GLVBO(pointList))
1247 def getObjectCenterPos(self):
1248 if self._selectedObj is None:
1249 return [0.0, 0.0, 0.0]
1250 pos = self._selectedObj.getPosition()
1251 size = self._selectedObj.getSize()
1252 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1254 def getObjectBoundaryCircle(self):
1255 if self._selectedObj is None:
1257 return self._selectedObj.getBoundaryCircle()
1259 def getObjectSize(self):
1260 if self._selectedObj is None:
1261 return [0.0, 0.0, 0.0]
1262 return self._selectedObj.getSize()
1264 def getObjectMatrix(self):
1265 if self._selectedObj is None:
1266 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1267 return self._selectedObj.getMatrix()
1269 class shaderEditor(wx.Dialog):
1270 def __init__(self, parent, callback, v, f):
1271 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1272 self._callback = callback
1273 s = wx.BoxSizer(wx.VERTICAL)
1275 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1276 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1277 s.Add(self._vertex, 1, flag=wx.EXPAND)
1278 s.Add(self._fragment, 1, flag=wx.EXPAND)
1280 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1281 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1283 self.SetPosition(self.GetParent().GetPosition())
1284 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1287 def OnText(self, e):
1288 self._callback(self._vertex.GetValue(), self._fragment.GetValue())