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._gcodeFilename = None
41 self._gcodeLoadThread = None
42 self._objectShader = None
43 self._objectLoadShader = None
45 self._selectedObj = None
46 self._objColors = [None,None,None,None]
49 self._mouseState = None
50 self._viewTarget = numpy.array([0,0,0], numpy.float32)
53 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
54 self._platformMesh._drawOffset = numpy.array([0,0,2.5], numpy.float32)
55 self._isSimpleMode = True
58 self._modelMatrix = None
59 self._projMatrix = None
60 self.tempMatrix = None
62 self.openFileButton = openglGui.glButton(self, 4, 'Load', (0,0), self.showLoadModel)
63 self.printButton = openglGui.glButton(self, 6, 'Print', (1,0), self.OnPrintButton)
64 self.printButton.setDisabled(True)
67 self.rotateToolButton = openglGui.glRadioButton(self, 8, 'Rotate', (0,-1), group, self.OnToolSelect)
68 self.scaleToolButton = openglGui.glRadioButton(self, 9, 'Scale', (1,-1), group, self.OnToolSelect)
69 self.mirrorToolButton = openglGui.glRadioButton(self, 10, 'Mirror', (2,-1), group, self.OnToolSelect)
71 self.resetRotationButton = openglGui.glButton(self, 12, 'Reset', (0,-2), self.OnRotateReset)
72 self.layFlatButton = openglGui.glButton(self, 16, 'Lay flat', (0,-3), self.OnLayFlat)
74 self.resetScaleButton = openglGui.glButton(self, 13, 'Reset', (1,-2), self.OnScaleReset)
75 self.scaleMaxButton = openglGui.glButton(self, 17, 'To max', (1,-3), self.OnScaleMax)
77 self.mirrorXButton = openglGui.glButton(self, 14, 'Mirror X', (2,-2), lambda button: self.OnMirror(0))
78 self.mirrorYButton = openglGui.glButton(self, 18, 'Mirror Y', (2,-3), lambda button: self.OnMirror(1))
79 self.mirrorZButton = openglGui.glButton(self, 22, 'Mirror Z', (2,-4), lambda button: self.OnMirror(2))
81 self.rotateToolButton.setExpandArrow(True)
82 self.scaleToolButton.setExpandArrow(True)
83 self.mirrorToolButton.setExpandArrow(True)
85 self.scaleForm = openglGui.glFrame(self, (2, -2))
86 openglGui.glGuiLayoutGrid(self.scaleForm)
87 openglGui.glLabel(self.scaleForm, 'Scale X', (0,0))
88 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
89 openglGui.glLabel(self.scaleForm, 'Scale Y', (0,1))
90 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
91 openglGui.glLabel(self.scaleForm, 'Scale Z', (0,2))
92 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
93 openglGui.glLabel(self.scaleForm, 'Size X (mm)', (0,4))
94 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
95 openglGui.glLabel(self.scaleForm, 'Size Y (mm)', (0,5))
96 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
97 openglGui.glLabel(self.scaleForm, 'Size Z (mm)', (0,6))
98 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
99 openglGui.glLabel(self.scaleForm, 'Uniform scale', (0,8))
100 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
102 self.viewSelection = openglGui.glComboButton(self, 'View mode', [7,19,11,15,23], ['Normal', 'Overhang', 'Transparent', 'X-Ray', 'Layers'], (-1,0), self.OnViewChange)
103 self.layerSelect = openglGui.glSlider(self, 10000, 0, 1, (-1,-2), lambda : self.QueueRefresh())
105 self.notification = openglGui.glNotification(self, (0, 0))
107 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
108 self._sceneUpdateTimer = wx.Timer(self)
109 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
110 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
111 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
115 self.updateToolButtons()
116 self.updateProfileToControls()
118 def showLoadModel(self, button = 1):
120 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)
121 dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
122 if dlg.ShowModal() != wx.ID_OK:
125 filenames = dlg.GetPaths()
127 if len(filenames) < 1:
129 profile.putPreference('lastFile', filenames[0])
131 for filename in filenames:
132 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
133 ext = filename[filename.rfind('.')+1:].upper()
134 if ext == 'G' or ext == 'GCODE':
135 gcodeFilename = filename
136 if gcodeFilename is not None:
137 if self._gcode is not None:
139 for layerVBOlist in self._gcodeVBOs:
140 for vbo in layerVBOlist:
141 self.glReleaseList.append(vbo)
143 self._gcode = gcodeInterpreter.gcode()
144 self._gcodeFilename = gcodeFilename
145 self.printButton.setBottomText('')
146 self.viewSelection.setValue(4)
147 self.printButton.setDisabled(False)
150 if self.viewSelection.getValue() == 4:
151 self.viewSelection.setValue(0)
153 self.loadScene(filenames)
155 def showSaveModel(self):
156 if len(self._scene.objects()) < 1:
158 dlg=wx.FileDialog(self, 'Save 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
159 dlg.SetWildcard(meshLoader.saveWildcardFilter())
160 if dlg.ShowModal() != wx.ID_OK:
163 filename = dlg.GetPath()
165 meshLoader.saveMeshes(filename, self._scene.objects())
167 def OnPrintButton(self, button):
169 if machineCom.machineIsConnected():
170 self.showPrintWindow()
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: self.showPrintWindow(), 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 showPrintWindow(self):
195 if self._gcodeFilename is None:
197 printWindow.printFile(self._gcodeFilename)
198 if self._gcodeFilename == self._slicer.getGCodeFilename():
199 self._slicer.submitSliceInfoOnline()
201 def showSaveGCode(self):
202 defPath = profile.getPreference('lastFile')
203 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
204 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
205 dlg.SetFilename(self._scene._objectList[0].getName())
206 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
207 if dlg.ShowModal() != wx.ID_OK:
210 filename = dlg.GetPath()
213 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
215 def _copyFile(self, fileA, fileB, allowEject = False):
217 size = float(os.stat(fileA).st_size)
218 with open(fileA, 'rb') as fsrc:
219 with open(fileB, 'wb') as fdst:
221 buf = fsrc.read(16*1024)
225 self.printButton.setProgressBar(float(fsrc.tell()) / size)
230 self.notification.message("Failed to save")
233 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...'))
235 self.notification.message("Saved as %s" % (fileB))
236 self.printButton.setProgressBar(None)
237 if fileA == self._slicer.getGCodeFilename():
238 self._slicer.submitSliceInfoOnline()
240 def _showSliceLog(self):
241 dlg = wx.TextEntryDialog(self, "The slicing engine reported the following", "Engine log...", '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
245 def OnToolSelect(self, button):
246 if self.rotateToolButton.getSelected():
247 self.tool = previewTools.toolRotate(self)
248 elif self.scaleToolButton.getSelected():
249 self.tool = previewTools.toolScale(self)
250 elif self.mirrorToolButton.getSelected():
251 self.tool = previewTools.toolNone(self)
253 self.tool = previewTools.toolNone(self)
254 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
255 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
256 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
257 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
258 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
259 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
260 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
261 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
263 def updateToolButtons(self):
264 if self._selectedObj is None:
268 self.rotateToolButton.setHidden(hidden)
269 self.scaleToolButton.setHidden(hidden)
270 self.mirrorToolButton.setHidden(hidden)
272 self.rotateToolButton.setSelected(False)
273 self.scaleToolButton.setSelected(False)
274 self.mirrorToolButton.setSelected(False)
277 def OnViewChange(self):
278 if self.viewSelection.getValue() == 4:
279 self.viewMode = 'gcode'
280 if self._gcode is not None and self._gcode.layerList is not None:
281 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
282 self._selectObject(None)
283 elif self.viewSelection.getValue() == 1:
284 self.viewMode = 'overhang'
285 elif self.viewSelection.getValue() == 2:
286 self.viewMode = 'transparent'
287 elif self.viewSelection.getValue() == 3:
288 self.viewMode = 'xray'
290 self.viewMode = 'normal'
291 self.layerSelect.setHidden(self.viewMode != 'gcode')
294 def OnRotateReset(self, button):
295 if self._selectedObj is None:
297 self._selectedObj.resetRotation()
298 self._scene.pushFree()
299 self._selectObject(self._selectedObj)
302 def OnLayFlat(self, button):
303 if self._selectedObj is None:
305 self._selectedObj.layFlat()
306 self._scene.pushFree()
307 self._selectObject(self._selectedObj)
310 def OnScaleReset(self, button):
311 if self._selectedObj is None:
313 self._selectedObj.resetScale()
314 self._selectObject(self._selectedObj)
315 self.updateProfileToControls()
318 def OnScaleMax(self, button):
319 if self._selectedObj is None:
321 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
322 self._scene.pushFree()
323 self._selectObject(self._selectedObj)
324 self.updateProfileToControls()
327 def OnMirror(self, axis):
328 if self._selectedObj is None:
330 self._selectedObj.mirror(axis)
333 def OnScaleEntry(self, value, axis):
334 if self._selectedObj is None:
340 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
341 self.updateProfileToControls()
342 self._scene.pushFree()
343 self._selectObject(self._selectedObj)
346 def OnScaleEntryMM(self, value, axis):
347 if self._selectedObj is None:
353 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
354 self.updateProfileToControls()
355 self._scene.pushFree()
356 self._selectObject(self._selectedObj)
359 def OnDeleteAll(self, e):
360 while len(self._scene.objects()) > 0:
361 self._deleteObject(self._scene.objects()[0])
362 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
364 def OnMultiply(self, e):
365 if self._focusObj is None:
368 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
369 if dlg.ShowModal() != wx.ID_OK:
378 self._scene.add(newObj)
379 self._scene.centerAll()
380 if not self._scene.checkPlatform(newObj):
385 self.notification.message("Could not create more then %d items" % (n - 1))
386 self._scene.remove(newObj)
387 self._scene.centerAll()
390 def OnSplitObject(self, e):
391 if self._focusObj is None:
393 self._scene.remove(self._focusObj)
394 for obj in self._focusObj.split(self._splitCallback):
395 if numpy.max(obj.getSize()) > 2.0:
397 self._scene.centerAll()
398 self._selectObject(None)
401 def _splitCallback(self, progress):
404 def OnMergeObjects(self, e):
405 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
407 self._scene.merge(self._selectedObj, self._focusObj)
410 def sceneUpdated(self):
411 self._sceneUpdateTimer.Start(500, True)
412 self._slicer.abortSlicer()
413 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
416 def _onRunSlicer(self, e):
417 if self._isSimpleMode:
418 self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
419 self._slicer.runSlicer(self._scene)
420 if self._isSimpleMode:
421 profile.resetTempOverride()
423 def _updateSliceProgress(self, progressValue, ready):
425 if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
427 self.printButton.setDisabled(not ready)
428 if progressValue >= 0.0:
429 self.printButton.setProgressBar(progressValue)
431 self.printButton.setProgressBar(None)
432 if self._gcode is not None:
434 for layerVBOlist in self._gcodeVBOs:
435 for vbo in layerVBOlist:
436 self.glReleaseList.append(vbo)
439 self.printButton.setProgressBar(None)
440 cost = self._slicer.getFilamentCost()
442 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
444 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
445 self._gcode = gcodeInterpreter.gcode()
446 self._gcodeFilename = self._slicer.getGCodeFilename()
448 self.printButton.setBottomText('')
451 def _loadGCode(self):
452 self._gcode.progressCallback = self._gcodeLoadCallback
453 self._gcode.load(self._gcodeFilename)
455 def _gcodeLoadCallback(self, progress):
456 if self._gcode is None:
458 if len(self._gcode.layerList) % 15 == 0:
460 if self._gcode is None:
462 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
463 if self.viewMode == 'gcode':
467 def loadScene(self, fileList):
468 for filename in fileList:
470 objList = meshLoader.loadMeshes(filename)
472 traceback.print_exc()
475 if self._objectLoadShader is not None:
476 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
480 self._scene.centerAll()
481 self._selectObject(obj)
484 def _deleteObject(self, obj):
485 if obj == self._selectedObj:
486 self._selectObject(None)
487 if obj == self._focusObj:
488 self._focusObj = None
489 self._scene.remove(obj)
490 for m in obj._meshList:
491 if m.vbo is not None and m.vbo.decRef():
492 self.glReleaseList.append(m.vbo)
497 def _selectObject(self, obj, zoom = True):
498 if obj != self._selectedObj:
499 self._selectedObj = obj
500 self.updateProfileToControls()
501 self.updateToolButtons()
502 if zoom and obj is not None:
503 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
504 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
505 newZoom = obj.getBoundaryCircle() * 6
506 if newZoom > numpy.max(self._machineSize) * 3:
507 newZoom = numpy.max(self._machineSize) * 3
508 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
510 def updateProfileToControls(self):
511 oldSimpleMode = self._isSimpleMode
512 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
513 if self._isSimpleMode and not oldSimpleMode:
514 self._scene.arrangeAll()
516 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
517 self._objColors[0] = profile.getPreferenceColour('model_colour')
518 self._objColors[1] = profile.getPreferenceColour('model_colour2')
519 self._objColors[2] = profile.getPreferenceColour('model_colour3')
520 self._objColors[3] = profile.getPreferenceColour('model_colour4')
521 self._scene.setMachineSize(self._machineSize)
522 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
523 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'))
525 if self._selectedObj is not None:
526 scale = self._selectedObj.getScale()
527 size = self._selectedObj.getSize()
528 self.scaleXctrl.setValue(round(scale[0], 2))
529 self.scaleYctrl.setValue(round(scale[1], 2))
530 self.scaleZctrl.setValue(round(scale[2], 2))
531 self.scaleXmmctrl.setValue(round(size[0], 2))
532 self.scaleYmmctrl.setValue(round(size[1], 2))
533 self.scaleZmmctrl.setValue(round(size[2], 2))
535 def OnKeyChar(self, keyCode):
536 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
537 if self._selectedObj is not None:
538 self._deleteObject(self._selectedObj)
540 if keyCode == wx.WXK_UP:
541 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
543 elif keyCode == wx.WXK_DOWN:
544 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
546 elif keyCode == wx.WXK_PAGEUP:
547 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
549 elif keyCode == wx.WXK_PAGEDOWN:
550 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
553 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
554 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
555 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
556 from collections import defaultdict
557 from gc import get_objects
558 self._beforeLeakTest = defaultdict(int)
559 for i in get_objects():
560 self._beforeLeakTest[type(i)] += 1
561 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
562 from collections import defaultdict
563 from gc import get_objects
564 self._afterLeakTest = defaultdict(int)
565 for i in get_objects():
566 self._afterLeakTest[type(i)] += 1
567 for k in self._afterLeakTest:
568 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
569 print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
571 def ShaderUpdate(self, v, f):
572 s = opengl.GLShader(v, f)
574 self._objectLoadShader.release()
575 self._objectLoadShader = s
576 for obj in self._scene.objects():
577 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
580 def OnMouseDown(self,e):
581 self._mouseX = e.GetX()
582 self._mouseY = e.GetY()
583 self._mouseClick3DPos = self._mouse3Dpos
584 self._mouseClickFocus = self._focusObj
586 self._mouseState = 'doubleClick'
588 self._mouseState = 'dragOrClick'
589 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
590 p0 -= self.getObjectCenterPos() - self._viewTarget
591 p1 -= self.getObjectCenterPos() - self._viewTarget
592 if self.tool.OnDragStart(p0, p1):
593 self._mouseState = 'tool'
594 if self._mouseState == 'dragOrClick':
595 if e.GetButton() == 1:
596 if self._focusObj is not None:
597 self._selectObject(self._focusObj, False)
600 def OnMouseUp(self, e):
601 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
603 if self._mouseState == 'dragOrClick':
604 if e.GetButton() == 1:
605 self._selectObject(self._focusObj)
606 if e.GetButton() == 3:
608 if self._focusObj is not None:
609 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
610 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
611 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
612 if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1:
613 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
614 if len(self._scene.objects()) > 0:
615 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
616 if menu.MenuItemCount > 0:
619 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
620 self._scene.pushFree()
622 elif self._mouseState == 'tool':
623 if self.tempMatrix is not None and self._selectedObj is not None:
624 self._selectedObj.applyMatrix(self.tempMatrix)
625 self._scene.pushFree()
626 self._selectObject(self._selectedObj)
627 self.tempMatrix = None
628 self.tool.OnDragEnd()
630 self._mouseState = None
632 def OnMouseMotion(self,e):
633 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
634 p0 -= self.getObjectCenterPos() - self._viewTarget
635 p1 -= self.getObjectCenterPos() - self._viewTarget
637 if e.Dragging() and self._mouseState is not None:
638 if self._mouseState == 'tool':
639 self.tool.OnDrag(p0, p1)
640 elif not e.LeftIsDown() and e.RightIsDown():
641 self._mouseState = 'drag'
642 if wx.GetKeyState(wx.WXK_SHIFT):
643 a = math.cos(math.radians(self._yaw)) / 3.0
644 b = math.sin(math.radians(self._yaw)) / 3.0
645 self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
646 self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
647 self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
648 self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
650 self._yaw += e.GetX() - self._mouseX
651 self._pitch -= e.GetY() - self._mouseY
652 if self._pitch > 170:
656 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
657 self._mouseState = 'drag'
658 self._zoom += e.GetY() - self._mouseY
661 if self._zoom > numpy.max(self._machineSize) * 3:
662 self._zoom = numpy.max(self._machineSize) * 3
663 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
664 self._mouseState = 'dragObject'
665 z = max(0, self._mouseClick3DPos[2])
666 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
667 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
672 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
673 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
674 diff = cursorZ1 - cursorZ0
675 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
676 if not e.Dragging() or self._mouseState != 'tool':
677 self.tool.OnMouseMove(p0, p1)
679 self._mouseX = e.GetX()
680 self._mouseY = e.GetY()
682 def OnMouseWheel(self, e):
683 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
684 delta = max(min(delta,4),-4)
685 self._zoom *= 1.0 - delta / 10.0
688 if self._zoom > numpy.max(self._machineSize) * 3:
689 self._zoom = numpy.max(self._machineSize) * 3
692 def OnMouseLeave(self, e):
695 def getMouseRay(self, x, y):
696 if self._viewport is None:
697 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
698 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
699 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
700 p0 -= self._viewTarget
701 p1 -= self._viewTarget
704 def _init3DView(self):
705 # set viewing projection
706 size = self.GetSize()
707 glViewport(0, 0, size.GetWidth(), size.GetHeight())
710 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
712 glDisable(GL_RESCALE_NORMAL)
713 glDisable(GL_LIGHTING)
715 glEnable(GL_DEPTH_TEST)
716 glDisable(GL_CULL_FACE)
718 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
720 glClearColor(0.8, 0.8, 0.8, 1.0)
724 glMatrixMode(GL_PROJECTION)
726 aspect = float(size.GetWidth()) / float(size.GetHeight())
727 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
729 glMatrixMode(GL_MODELVIEW)
731 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
734 if machineCom.machineIsConnected():
735 self.printButton._imageID = 6
736 self.printButton._tooltip = 'Print'
737 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
738 self.printButton._imageID = 2
739 self.printButton._tooltip = 'Toolpath to SD'
741 self.printButton._imageID = 3
742 self.printButton._tooltip = 'Save toolpath'
744 if self._animView is not None:
745 self._viewTarget = self._animView.getPosition()
746 if self._animView.isDone():
747 self._animView = None
748 if self._animZoom is not None:
749 self._zoom = self._animZoom.getPosition()
750 if self._animZoom.isDone():
751 self._animZoom = None
752 if self.viewMode == 'gcode' and self._gcode is not None:
754 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
757 if self._objectShader is None:
758 if opengl.hasShaderSupport():
759 self._objectShader = opengl.GLShader("""
760 varying float light_amount;
764 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
765 gl_FrontColor = gl_Color;
767 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
771 varying float light_amount;
775 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
778 self._objectOverhangShader = opengl.GLShader("""
779 uniform float cosAngle;
780 uniform mat3 rotMatrix;
781 varying float light_amount;
785 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
786 gl_FrontColor = gl_Color;
788 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
790 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
792 light_amount = -10.0;
796 varying float light_amount;
800 if (light_amount == -10.0)
802 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
804 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
808 self._objectLoadShader = opengl.GLShader("""
809 uniform float intensity;
811 varying float light_amount;
815 vec4 tmp = gl_Vertex;
816 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
817 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
818 gl_Position = gl_ModelViewProjectionMatrix * tmp;
819 gl_FrontColor = gl_Color;
821 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
825 uniform float intensity;
826 varying float light_amount;
830 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
833 if self._objectShader == None or not self._objectShader.isValid():
834 self._objectShader = opengl.GLFakeShader()
835 self._objectOverhangShader = opengl.GLFakeShader()
836 self._objectLoadShader = None
838 glTranslate(0,0,-self._zoom)
839 glRotate(-self._pitch, 1,0,0)
840 glRotate(self._yaw, 0,0,1)
841 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
843 self._viewport = glGetIntegerv(GL_VIEWPORT)
844 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
845 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
847 glClearColor(1,1,1,1)
848 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
850 if self.viewMode != 'gcode':
851 for n in xrange(0, len(self._scene.objects())):
852 obj = self._scene.objects()[n]
853 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
854 self._renderObject(obj)
856 if self._mouseX > -1:
858 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
859 if n < len(self._scene.objects()):
860 self._focusObj = self._scene.objects()[n]
862 self._focusObj = None
863 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
864 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
865 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
866 self._mouse3Dpos -= self._viewTarget
869 glTranslate(0,0,-self._zoom)
870 glRotate(-self._pitch, 1,0,0)
871 glRotate(self._yaw, 0,0,1)
872 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
874 if self.viewMode == 'gcode':
875 if self._gcode is not None and self._gcode.layerList is None:
876 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
877 self._gcodeLoadThread.daemon = True
878 self._gcodeLoadThread.start()
879 if self._gcode is not None and self._gcode.layerList is not None:
881 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
883 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
884 for n in xrange(0, drawUpTill):
885 c = 1.0 - float(drawUpTill - n) / 15
887 if len(self._gcodeVBOs) < n + 1:
888 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
889 if time.time() - t > 0.5:
892 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
893 if n == drawUpTill - 1:
894 if len(self._gcodeVBOs[n]) < 9:
895 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
897 self._gcodeVBOs[n][8].render(GL_QUADS)
899 self._gcodeVBOs[n][9].render(GL_QUADS)
901 self._gcodeVBOs[n][10].render(GL_QUADS)
903 self._gcodeVBOs[n][11].render(GL_QUADS)
906 self._gcodeVBOs[n][12].render(GL_QUADS)
907 glColor3f(c/2, c/2, 0.0)
908 self._gcodeVBOs[n][13].render(GL_QUADS)
910 self._gcodeVBOs[n][14].render(GL_QUADS)
911 self._gcodeVBOs[n][15].render(GL_QUADS)
913 self._gcodeVBOs[n][16].render(GL_LINES)
916 self._gcodeVBOs[n][0].render(GL_LINES)
918 self._gcodeVBOs[n][1].render(GL_LINES)
920 self._gcodeVBOs[n][2].render(GL_LINES)
922 self._gcodeVBOs[n][3].render(GL_LINES)
925 self._gcodeVBOs[n][4].render(GL_LINES)
926 glColor3f(c/2, c/2, 0.0)
927 self._gcodeVBOs[n][5].render(GL_LINES)
929 self._gcodeVBOs[n][6].render(GL_LINES)
930 self._gcodeVBOs[n][7].render(GL_LINES)
933 glStencilFunc(GL_ALWAYS, 1, 1)
934 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
936 if self.viewMode == 'overhang':
937 self._objectOverhangShader.bind()
938 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
940 self._objectShader.bind()
941 for obj in self._scene.objects():
942 if obj._loadAnim is not None:
943 if obj._loadAnim.isDone():
948 if self._focusObj == obj:
950 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
953 if self._selectedObj == obj or self._selectedObj is None:
954 #If we want transparent, then first render a solid black model to remove the printer size lines.
955 if self.viewMode == 'transparent':
956 glColor4f(0, 0, 0, 0)
957 self._renderObject(obj)
959 glBlendFunc(GL_ONE, GL_ONE)
960 glDisable(GL_DEPTH_TEST)
962 if self.viewMode == 'xray':
963 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
964 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
965 glEnable(GL_STENCIL_TEST)
967 if self.viewMode == 'overhang':
968 if self._selectedObj == obj and self.tempMatrix is not None:
969 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
971 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
973 if not self._scene.checkPlatform(obj):
974 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
975 self._renderObject(obj)
977 self._renderObject(obj, brightness)
978 glDisable(GL_STENCIL_TEST)
980 glEnable(GL_DEPTH_TEST)
981 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
983 if self.viewMode == 'xray':
986 glEnable(GL_STENCIL_TEST)
987 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
988 glDisable(GL_DEPTH_TEST)
989 for i in xrange(2, 15, 2):
990 glStencilFunc(GL_EQUAL, i, 0xFF)
991 glColor(float(i)/10, float(i)/10, float(i)/5)
993 glVertex3f(-1000,-1000,-10)
994 glVertex3f( 1000,-1000,-10)
995 glVertex3f( 1000, 1000,-10)
996 glVertex3f(-1000, 1000,-10)
998 for i in xrange(1, 15, 2):
999 glStencilFunc(GL_EQUAL, i, 0xFF)
1000 glColor(float(i)/10, 0, 0)
1002 glVertex3f(-1000,-1000,-10)
1003 glVertex3f( 1000,-1000,-10)
1004 glVertex3f( 1000, 1000,-10)
1005 glVertex3f(-1000, 1000,-10)
1008 glDisable(GL_STENCIL_TEST)
1009 glEnable(GL_DEPTH_TEST)
1011 self._objectShader.unbind()
1013 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1015 if self._objectLoadShader is not None:
1016 self._objectLoadShader.bind()
1017 glColor4f(0.2, 0.6, 1.0, 1.0)
1018 for obj in self._scene.objects():
1019 if obj._loadAnim is None:
1021 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1022 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1023 self._renderObject(obj)
1024 self._objectLoadShader.unbind()
1029 if self.viewMode == 'gcode':
1030 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1031 glDisable(GL_DEPTH_TEST)
1034 glTranslate(0,-4,-10)
1035 glColor4ub(60,60,60,255)
1036 opengl.glDrawStringCenter('Loading toolpath for visualization...')
1039 #Draw the object box-shadow, so you can see where it will collide with other objects.
1040 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1041 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1043 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1045 glEnable(GL_CULL_FACE)
1046 glColor4f(0,0,0,0.12)
1048 glVertex3f(-size[0], size[1], 0.1)
1049 glVertex3f(-size[0], -size[1], 0.1)
1050 glVertex3f( size[0], -size[1], 0.1)
1051 glVertex3f( size[0], size[1], 0.1)
1053 glDisable(GL_CULL_FACE)
1056 #Draw the outline of the selected object, on top of everything else except the GUI.
1057 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1058 glDisable(GL_DEPTH_TEST)
1059 glEnable(GL_CULL_FACE)
1060 glEnable(GL_STENCIL_TEST)
1062 glStencilFunc(GL_EQUAL, 0, 255)
1064 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1066 glColor4f(1,1,1,0.5)
1067 self._renderObject(self._selectedObj)
1068 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1070 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1071 glDisable(GL_STENCIL_TEST)
1072 glDisable(GL_CULL_FACE)
1073 glEnable(GL_DEPTH_TEST)
1075 if self._selectedObj is not None:
1077 pos = self.getObjectCenterPos()
1078 glTranslate(pos[0], pos[1], pos[2])
1081 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1082 glDisable(GL_DEPTH_TEST)
1085 glTranslate(0,-4,-10)
1086 glColor4ub(60,60,60,255)
1087 opengl.glDrawStringCenter('Overhang view not working due to lack of OpenGL shaders support.')
1090 def _renderObject(self, obj, brightness = False, addSink = True):
1093 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1095 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1097 if self.tempMatrix is not None and obj == self._selectedObj:
1098 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1099 glMultMatrixf(tempMatrix)
1101 offset = obj.getDrawOffset()
1102 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1104 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1105 glMultMatrixf(tempMatrix)
1108 for m in obj._meshList:
1110 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1112 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1117 def _drawMachine(self):
1118 glEnable(GL_CULL_FACE)
1121 if profile.getPreference('machine_type') == 'ultimaker':
1122 glColor4f(1,1,1,0.5)
1123 self._objectShader.bind()
1124 self._renderObject(self._platformMesh, False, False)
1125 self._objectShader.unbind()
1127 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
1128 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1129 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1130 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1131 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1132 v4 = [ size[0] / 2, size[1] / 2, 0]
1133 v5 = [ size[0] / 2,-size[1] / 2, 0]
1134 v6 = [-size[0] / 2, size[1] / 2, 0]
1135 v7 = [-size[0] / 2,-size[1] / 2, 0]
1137 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1138 glEnableClientState(GL_VERTEX_ARRAY)
1139 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1141 glColor4ub(5, 171, 231, 64)
1142 glDrawArrays(GL_QUADS, 0, 4)
1143 glColor4ub(5, 171, 231, 96)
1144 glDrawArrays(GL_QUADS, 4, 8)
1145 glColor4ub(5, 171, 231, 128)
1146 glDrawArrays(GL_QUADS, 12, 8)
1147 glDisableClientState(GL_VERTEX_ARRAY)
1149 sx = self._machineSize[0]
1150 sy = self._machineSize[1]
1151 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1152 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1157 x1 = max(min(x1, sx/2), -sx/2)
1158 y1 = max(min(y1, sy/2), -sy/2)
1159 x2 = max(min(x2, sx/2), -sx/2)
1160 y2 = max(min(y2, sy/2), -sy/2)
1161 if (x & 1) == (y & 1):
1162 glColor4ub(5, 171, 231, 127)
1164 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1166 glVertex3f(x1, y1, -0.02)
1167 glVertex3f(x2, y1, -0.02)
1168 glVertex3f(x2, y2, -0.02)
1169 glVertex3f(x1, y2, -0.02)
1173 glDisable(GL_CULL_FACE)
1175 def _generateGCodeVBOs(self, layer):
1177 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1178 if ':' in extrudeType:
1179 extruder = int(extrudeType[extrudeType.find(':')+1:])
1180 extrudeType = extrudeType[0:extrudeType.find(':')]
1183 pointList = numpy.zeros((0,3), numpy.float32)
1185 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1187 a = numpy.concatenate((a[:-1], a[1:]), 1)
1188 a = a.reshape((len(a) * 2, 3))
1189 pointList = numpy.concatenate((pointList, a))
1190 ret.append(opengl.GLVBO(pointList))
1193 def _generateGCodeVBOs2(self, layer):
1194 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1195 filamentArea = math.pi * filamentRadius * filamentRadius
1198 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1199 if ':' in extrudeType:
1200 extruder = int(extrudeType[extrudeType.find(':')+1:])
1201 extrudeType = extrudeType[0:extrudeType.find(':')]
1204 pointList = numpy.zeros((0,3), numpy.float32)
1206 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1208 if extrudeType == 'FILL':
1211 normal = a[1:] - a[:-1]
1212 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1213 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1216 ePerDist = path['extrusion'][1:] / lens
1217 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1219 normal[:,0] *= lineWidth
1220 normal[:,1] *= lineWidth
1222 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1223 b = numpy.concatenate((b, a[1:] + normal), 1)
1224 b = numpy.concatenate((b, a[1:] - normal), 1)
1225 b = numpy.concatenate((b, a[:-1] - normal), 1)
1226 b = numpy.concatenate((b, a[:-1] + normal), 1)
1227 b = b.reshape((len(b) * 4, 3))
1230 normal2 = normal[:-1] + normal[1:]
1231 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1232 normal2[:,0] /= lens2
1233 normal2[:,1] /= lens2
1234 normal2[:,0] *= lineWidth[:-1]
1235 normal2[:,1] *= lineWidth[:-1]
1237 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1238 c = numpy.concatenate((c, a[1:-1]), 1)
1239 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1240 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1241 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1243 c = numpy.concatenate((c, a[1:-1]), 1)
1244 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1245 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1246 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1248 c = c.reshape((len(c) * 8, 3))
1250 pointList = numpy.concatenate((pointList, b, c))
1252 pointList = numpy.concatenate((pointList, b))
1253 ret.append(opengl.GLVBO(pointList))
1255 pointList = numpy.zeros((0,3), numpy.float32)
1257 if path['type'] == 'move':
1258 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1259 a = numpy.concatenate((a[:-1], a[1:]), 1)
1260 a = a.reshape((len(a) * 2, 3))
1261 pointList = numpy.concatenate((pointList, a))
1262 if path['type'] == 'retract':
1263 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1264 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1265 a = a.reshape((len(a) * 2, 3))
1266 pointList = numpy.concatenate((pointList, a))
1267 ret.append(opengl.GLVBO(pointList))
1271 def getObjectCenterPos(self):
1272 if self._selectedObj is None:
1273 return [0.0, 0.0, 0.0]
1274 pos = self._selectedObj.getPosition()
1275 size = self._selectedObj.getSize()
1276 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1278 def getObjectBoundaryCircle(self):
1279 if self._selectedObj is None:
1281 return self._selectedObj.getBoundaryCircle()
1283 def getObjectSize(self):
1284 if self._selectedObj is None:
1285 return [0.0, 0.0, 0.0]
1286 return self._selectedObj.getSize()
1288 def getObjectMatrix(self):
1289 if self._selectedObj is None:
1290 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1291 return self._selectedObj.getMatrix()
1293 class shaderEditor(wx.Dialog):
1294 def __init__(self, parent, callback, v, f):
1295 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1296 self._callback = callback
1297 s = wx.BoxSizer(wx.VERTICAL)
1299 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1300 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1301 s.Add(self._vertex, 1, flag=wx.EXPAND)
1302 s.Add(self._fragment, 1, flag=wx.EXPAND)
1304 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1305 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1307 self.SetPosition(self.GetParent().GetPosition())
1308 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1311 def OnText(self, e):
1312 self._callback(self._vertex.GetValue(), self._fragment.GetValue())