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, 10000, 0, 1, (-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._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 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
457 if self.viewMode == 'gcode':
461 def loadScene(self, fileList):
462 for filename in fileList:
464 objList = meshLoader.loadMeshes(filename)
466 traceback.print_exc()
469 if self._objectLoadShader is not None:
470 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 OnMouseLeave(self, e):
689 def getMouseRay(self, x, y):
690 if self._viewport is None:
691 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
692 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
693 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
694 p0 -= self._viewTarget
695 p1 -= self._viewTarget
698 def _init3DView(self):
699 # set viewing projection
700 size = self.GetSize()
701 glViewport(0, 0, size.GetWidth(), size.GetHeight())
704 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
706 glDisable(GL_RESCALE_NORMAL)
707 glDisable(GL_LIGHTING)
709 glEnable(GL_DEPTH_TEST)
710 glDisable(GL_CULL_FACE)
712 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
714 glClearColor(0.8, 0.8, 0.8, 1.0)
718 glMatrixMode(GL_PROJECTION)
720 aspect = float(size.GetWidth()) / float(size.GetHeight())
721 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
723 glMatrixMode(GL_MODELVIEW)
725 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
728 if machineCom.machineIsConnected():
729 self.printButton._imageID = 6
730 self.printButton._tooltip = 'Print'
731 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
732 self.printButton._imageID = 2
733 self.printButton._tooltip = 'Toolpath to SD'
735 self.printButton._imageID = 3
736 self.printButton._tooltip = 'Save toolpath'
738 if self._animView is not None:
739 self._viewTarget = self._animView.getPosition()
740 if self._animView.isDone():
741 self._animView = None
742 if self._animZoom is not None:
743 self._zoom = self._animZoom.getPosition()
744 if self._animZoom.isDone():
745 self._animZoom = None
746 if self.viewMode == 'gcode' and self._gcode is not None:
748 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
751 if self._objectShader is None:
752 if opengl.hasShaderSupport():
753 self._objectShader = opengl.GLShader("""
754 varying float light_amount;
758 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
759 gl_FrontColor = gl_Color;
761 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
765 varying float light_amount;
769 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
772 self._objectOverhangShader = opengl.GLShader("""
773 uniform float cosAngle;
774 uniform mat3 rotMatrix;
775 varying float light_amount;
779 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
780 gl_FrontColor = gl_Color;
782 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
784 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
786 light_amount = -10.0;
790 varying float light_amount;
794 if (light_amount == -10.0)
796 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
798 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
802 self._objectLoadShader = opengl.GLShader("""
803 uniform float intensity;
805 varying float light_amount;
809 vec4 tmp = gl_Vertex;
810 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
811 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
812 gl_Position = gl_ModelViewProjectionMatrix * tmp;
813 gl_FrontColor = gl_Color;
815 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
819 uniform float intensity;
820 varying float light_amount;
824 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
827 if self._objectShader == None or not self._objectShader.isValid():
828 self._objectShader = opengl.GLFakeShader()
829 self._objectOverhangShader = opengl.GLFakeShader()
830 self._objectLoadShader = None
832 glTranslate(0,0,-self._zoom)
833 glRotate(-self._pitch, 1,0,0)
834 glRotate(self._yaw, 0,0,1)
835 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
837 self._viewport = glGetIntegerv(GL_VIEWPORT)
838 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
839 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
841 glClearColor(1,1,1,1)
842 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
844 if self.viewMode != 'gcode':
845 for n in xrange(0, len(self._scene.objects())):
846 obj = self._scene.objects()[n]
847 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
848 self._renderObject(obj)
850 if self._mouseX > -1:
852 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
853 if n < len(self._scene.objects()):
854 self._focusObj = self._scene.objects()[n]
856 self._focusObj = None
857 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
858 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
859 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
860 self._mouse3Dpos -= self._viewTarget
863 glTranslate(0,0,-self._zoom)
864 glRotate(-self._pitch, 1,0,0)
865 glRotate(self._yaw, 0,0,1)
866 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
868 if self.viewMode == 'gcode':
869 if self._gcode is not None and self._gcode.layerList is None:
870 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
871 self._gcodeLoadThread.daemon = True
872 self._gcodeLoadThread.start()
873 if self._gcode is not None and self._gcode.layerList is not None:
875 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
877 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
878 for n in xrange(0, drawUpTill):
879 c = 1.0 - float(drawUpTill - n) / 15
881 if len(self._gcodeVBOs) < n + 1:
882 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
883 if time.time() - t > 0.5:
886 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
887 if n == drawUpTill - 1:
888 if len(self._gcodeVBOs[n]) < 9:
889 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
891 self._gcodeVBOs[n][8].render(GL_QUADS)
893 self._gcodeVBOs[n][9].render(GL_QUADS)
895 self._gcodeVBOs[n][10].render(GL_QUADS)
897 self._gcodeVBOs[n][11].render(GL_QUADS)
900 self._gcodeVBOs[n][12].render(GL_QUADS)
901 glColor3f(c/2, c/2, 0.0)
902 self._gcodeVBOs[n][13].render(GL_QUADS)
904 self._gcodeVBOs[n][14].render(GL_QUADS)
905 self._gcodeVBOs[n][15].render(GL_QUADS)
907 self._gcodeVBOs[n][16].render(GL_LINES)
910 self._gcodeVBOs[n][0].render(GL_LINES)
912 self._gcodeVBOs[n][1].render(GL_LINES)
914 self._gcodeVBOs[n][2].render(GL_LINES)
916 self._gcodeVBOs[n][3].render(GL_LINES)
919 self._gcodeVBOs[n][4].render(GL_LINES)
920 glColor3f(c/2, c/2, 0.0)
921 self._gcodeVBOs[n][5].render(GL_LINES)
923 self._gcodeVBOs[n][6].render(GL_LINES)
924 self._gcodeVBOs[n][7].render(GL_LINES)
927 glStencilFunc(GL_ALWAYS, 1, 1)
928 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
930 if self.viewMode == 'overhang':
931 self._objectOverhangShader.bind()
932 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
934 self._objectShader.bind()
935 for obj in self._scene.objects():
936 if obj._loadAnim is not None:
937 if obj._loadAnim.isDone():
942 if self._focusObj == obj:
944 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
947 if self._selectedObj == obj or self._selectedObj is None:
948 #If we want transparent, then first render a solid black model to remove the printer size lines.
949 if self.viewMode == 'transparent':
950 glColor4f(0, 0, 0, 0)
951 self._renderObject(obj)
953 glBlendFunc(GL_ONE, GL_ONE)
954 glDisable(GL_DEPTH_TEST)
956 if self.viewMode == 'xray':
957 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
958 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
959 glEnable(GL_STENCIL_TEST)
961 if self.viewMode == 'overhang':
962 if self._selectedObj == obj and self.tempMatrix is not None:
963 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
965 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
967 if not self._scene.checkPlatform(obj):
968 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
969 self._renderObject(obj)
971 self._renderObject(obj, brightness)
972 glDisable(GL_STENCIL_TEST)
974 glEnable(GL_DEPTH_TEST)
975 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
977 if self.viewMode == 'xray':
980 glEnable(GL_STENCIL_TEST)
981 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
982 glDisable(GL_DEPTH_TEST)
983 for i in xrange(2, 15, 2):
984 glStencilFunc(GL_EQUAL, i, 0xFF)
985 glColor(float(i)/10, float(i)/10, float(i)/5)
987 glVertex3f(-1000,-1000,-10)
988 glVertex3f( 1000,-1000,-10)
989 glVertex3f( 1000, 1000,-10)
990 glVertex3f(-1000, 1000,-10)
992 for i in xrange(1, 15, 2):
993 glStencilFunc(GL_EQUAL, i, 0xFF)
994 glColor(float(i)/10, 0, 0)
996 glVertex3f(-1000,-1000,-10)
997 glVertex3f( 1000,-1000,-10)
998 glVertex3f( 1000, 1000,-10)
999 glVertex3f(-1000, 1000,-10)
1002 glDisable(GL_STENCIL_TEST)
1003 glEnable(GL_DEPTH_TEST)
1005 self._objectShader.unbind()
1007 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1009 if self._objectLoadShader is not None:
1010 self._objectLoadShader.bind()
1011 glColor4f(0.2, 0.6, 1.0, 1.0)
1012 for obj in self._scene.objects():
1013 if obj._loadAnim is None:
1015 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1016 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1017 self._renderObject(obj)
1018 self._objectLoadShader.unbind()
1023 if self.viewMode == 'gcode':
1024 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1025 glDisable(GL_DEPTH_TEST)
1028 glTranslate(0,-4,-10)
1029 glColor4ub(60,60,60,255)
1030 opengl.glDrawStringCenter('Loading toolpath for visualization...')
1033 #Draw the object box-shadow, so you can see where it will collide with other objects.
1034 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1035 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1037 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1039 glEnable(GL_CULL_FACE)
1040 glColor4f(0,0,0,0.12)
1042 glVertex3f(-size[0], size[1], 0.1)
1043 glVertex3f(-size[0], -size[1], 0.1)
1044 glVertex3f( size[0], -size[1], 0.1)
1045 glVertex3f( size[0], size[1], 0.1)
1047 glDisable(GL_CULL_FACE)
1050 #Draw the outline of the selected object, on top of everything else except the GUI.
1051 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1052 glDisable(GL_DEPTH_TEST)
1053 glEnable(GL_CULL_FACE)
1054 glEnable(GL_STENCIL_TEST)
1056 glStencilFunc(GL_EQUAL, 0, 255)
1058 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1060 glColor4f(1,1,1,0.5)
1061 self._renderObject(self._selectedObj)
1062 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1064 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1065 glDisable(GL_STENCIL_TEST)
1066 glDisable(GL_CULL_FACE)
1067 glEnable(GL_DEPTH_TEST)
1069 if self._selectedObj is not None:
1071 pos = self.getObjectCenterPos()
1072 glTranslate(pos[0], pos[1], pos[2])
1075 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1076 glDisable(GL_DEPTH_TEST)
1079 glTranslate(0,-4,-10)
1080 glColor4ub(60,60,60,255)
1081 opengl.glDrawStringCenter('Overhang view not working due to lack of OpenGL shaders support.')
1084 def _renderObject(self, obj, brightness = False, addSink = True):
1087 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1089 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1091 if self.tempMatrix is not None and obj == self._selectedObj:
1092 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1093 glMultMatrixf(tempMatrix)
1095 offset = obj.getDrawOffset()
1096 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1098 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1099 glMultMatrixf(tempMatrix)
1102 for m in obj._meshList:
1104 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1106 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1111 def _drawMachine(self):
1112 glEnable(GL_CULL_FACE)
1115 if profile.getPreference('machine_type') == 'ultimaker':
1116 glColor4f(1,1,1,0.5)
1117 self._objectShader.bind()
1118 self._renderObject(self._platformMesh, False, False)
1119 self._objectShader.unbind()
1121 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
1122 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1123 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1124 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1125 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1126 v4 = [ size[0] / 2, size[1] / 2, 0]
1127 v5 = [ size[0] / 2,-size[1] / 2, 0]
1128 v6 = [-size[0] / 2, size[1] / 2, 0]
1129 v7 = [-size[0] / 2,-size[1] / 2, 0]
1131 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1132 glEnableClientState(GL_VERTEX_ARRAY)
1133 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1135 glColor4ub(5, 171, 231, 64)
1136 glDrawArrays(GL_QUADS, 0, 4)
1137 glColor4ub(5, 171, 231, 96)
1138 glDrawArrays(GL_QUADS, 4, 8)
1139 glColor4ub(5, 171, 231, 128)
1140 glDrawArrays(GL_QUADS, 12, 8)
1141 glDisableClientState(GL_VERTEX_ARRAY)
1143 sx = self._machineSize[0]
1144 sy = self._machineSize[1]
1145 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1146 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1151 x1 = max(min(x1, sx/2), -sx/2)
1152 y1 = max(min(y1, sy/2), -sy/2)
1153 x2 = max(min(x2, sx/2), -sx/2)
1154 y2 = max(min(y2, sy/2), -sy/2)
1155 if (x & 1) == (y & 1):
1156 glColor4ub(5, 171, 231, 127)
1158 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1160 glVertex3f(x1, y1, -0.02)
1161 glVertex3f(x2, y1, -0.02)
1162 glVertex3f(x2, y2, -0.02)
1163 glVertex3f(x1, y2, -0.02)
1167 glDisable(GL_CULL_FACE)
1169 def _generateGCodeVBOs(self, layer):
1171 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1172 if ':' in extrudeType:
1173 extruder = int(extrudeType[extrudeType.find(':')+1:])
1174 extrudeType = extrudeType[0:extrudeType.find(':')]
1177 pointList = numpy.zeros((0,3), numpy.float32)
1179 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1181 a = numpy.concatenate((a[:-1], a[1:]), 1)
1182 a = a.reshape((len(a) * 2, 3))
1183 pointList = numpy.concatenate((pointList, a))
1184 ret.append(opengl.GLVBO(pointList))
1187 def _generateGCodeVBOs2(self, layer):
1188 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1189 filamentArea = math.pi * filamentRadius * filamentRadius
1192 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1193 if ':' in extrudeType:
1194 extruder = int(extrudeType[extrudeType.find(':')+1:])
1195 extrudeType = extrudeType[0:extrudeType.find(':')]
1198 pointList = numpy.zeros((0,3), numpy.float32)
1200 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1202 if extrudeType == 'FILL':
1205 normal = a[1:] - a[:-1]
1206 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1207 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1210 ePerDist = path['extrusion'][1:] / lens
1211 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1213 normal[:,0] *= lineWidth
1214 normal[:,1] *= lineWidth
1216 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1217 b = numpy.concatenate((b, a[1:] + normal), 1)
1218 b = numpy.concatenate((b, a[1:] - normal), 1)
1219 b = numpy.concatenate((b, a[:-1] - normal), 1)
1220 b = numpy.concatenate((b, a[:-1] + normal), 1)
1221 b = b.reshape((len(b) * 4, 3))
1224 normal2 = normal[:-1] + normal[1:]
1225 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1226 normal2[:,0] /= lens2
1227 normal2[:,1] /= lens2
1228 normal2[:,0] *= lineWidth[:-1]
1229 normal2[:,1] *= lineWidth[:-1]
1231 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1232 c = numpy.concatenate((c, a[1:-1]), 1)
1233 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1234 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1235 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1237 c = numpy.concatenate((c, a[1:-1]), 1)
1238 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1239 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1240 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1242 c = c.reshape((len(c) * 8, 3))
1244 pointList = numpy.concatenate((pointList, b, c))
1246 pointList = numpy.concatenate((pointList, b))
1247 ret.append(opengl.GLVBO(pointList))
1249 pointList = numpy.zeros((0,3), numpy.float32)
1251 if path['type'] == 'move':
1252 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1253 a = numpy.concatenate((a[:-1], a[1:]), 1)
1254 a = a.reshape((len(a) * 2, 3))
1255 pointList = numpy.concatenate((pointList, a))
1256 if path['type'] == 'retract':
1257 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1258 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1259 a = a.reshape((len(a) * 2, 3))
1260 pointList = numpy.concatenate((pointList, a))
1261 ret.append(opengl.GLVBO(pointList))
1265 def getObjectCenterPos(self):
1266 if self._selectedObj is None:
1267 return [0.0, 0.0, 0.0]
1268 pos = self._selectedObj.getPosition()
1269 size = self._selectedObj.getSize()
1270 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1272 def getObjectBoundaryCircle(self):
1273 if self._selectedObj is None:
1275 return self._selectedObj.getBoundaryCircle()
1277 def getObjectSize(self):
1278 if self._selectedObj is None:
1279 return [0.0, 0.0, 0.0]
1280 return self._selectedObj.getSize()
1282 def getObjectMatrix(self):
1283 if self._selectedObj is None:
1284 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1285 return self._selectedObj.getMatrix()
1287 class shaderEditor(wx.Dialog):
1288 def __init__(self, parent, callback, v, f):
1289 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1290 self._callback = callback
1291 s = wx.BoxSizer(wx.VERTICAL)
1293 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1294 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1295 s.Add(self._vertex, 1, flag=wx.EXPAND)
1296 s.Add(self._fragment, 1, flag=wx.EXPAND)
1298 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1299 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1301 self.SetPosition(self.GetParent().GetPosition())
1302 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1305 def OnText(self, e):
1306 self._callback(self._vertex.GetValue(), self._fragment.GetValue())