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):
692 self._focusObj = None
694 def getMouseRay(self, x, y):
695 if self._viewport is None:
696 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
697 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
698 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
699 p0 -= self._viewTarget
700 p1 -= self._viewTarget
703 def _init3DView(self):
704 # set viewing projection
705 size = self.GetSize()
706 glViewport(0, 0, size.GetWidth(), size.GetHeight())
709 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
711 glDisable(GL_RESCALE_NORMAL)
712 glDisable(GL_LIGHTING)
714 glEnable(GL_DEPTH_TEST)
715 glDisable(GL_CULL_FACE)
717 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
719 glClearColor(0.8, 0.8, 0.8, 1.0)
723 glMatrixMode(GL_PROJECTION)
725 aspect = float(size.GetWidth()) / float(size.GetHeight())
726 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
728 glMatrixMode(GL_MODELVIEW)
730 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
733 if machineCom.machineIsConnected():
734 self.printButton._imageID = 6
735 self.printButton._tooltip = 'Print'
736 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
737 self.printButton._imageID = 2
738 self.printButton._tooltip = 'Toolpath to SD'
740 self.printButton._imageID = 3
741 self.printButton._tooltip = 'Save toolpath'
743 if self._animView is not None:
744 self._viewTarget = self._animView.getPosition()
745 if self._animView.isDone():
746 self._animView = None
747 if self._animZoom is not None:
748 self._zoom = self._animZoom.getPosition()
749 if self._animZoom.isDone():
750 self._animZoom = None
751 if self.viewMode == 'gcode' and self._gcode is not None:
753 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
756 if self._objectShader is None:
757 if opengl.hasShaderSupport():
758 self._objectShader = opengl.GLShader("""
759 varying float light_amount;
763 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
764 gl_FrontColor = gl_Color;
766 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
770 varying float light_amount;
774 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
777 self._objectOverhangShader = opengl.GLShader("""
778 uniform float cosAngle;
779 uniform mat3 rotMatrix;
780 varying float light_amount;
784 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
785 gl_FrontColor = gl_Color;
787 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
789 if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
791 light_amount = -10.0;
795 varying float light_amount;
799 if (light_amount == -10.0)
801 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
803 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
807 self._objectLoadShader = opengl.GLShader("""
808 uniform float intensity;
810 varying float light_amount;
814 vec4 tmp = gl_Vertex;
815 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
816 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
817 gl_Position = gl_ModelViewProjectionMatrix * tmp;
818 gl_FrontColor = gl_Color;
820 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
824 uniform float intensity;
825 varying float light_amount;
829 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
833 self._objectShader = opengl.GLFakeShader()
834 self._objectOverhangShader = opengl.GLFakeShader()
835 self._objectLoadShader = None
837 glTranslate(0,0,-self._zoom)
838 glRotate(-self._pitch, 1,0,0)
839 glRotate(self._yaw, 0,0,1)
840 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
842 self._viewport = glGetIntegerv(GL_VIEWPORT)
843 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
844 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
846 glClearColor(1,1,1,1)
847 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
849 if self.viewMode != 'gcode':
850 for n in xrange(0, len(self._scene.objects())):
851 obj = self._scene.objects()[n]
852 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
853 self._renderObject(obj)
855 if self._mouseX > -1:
857 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
858 if n < len(self._scene.objects()):
859 self._focusObj = self._scene.objects()[n]
861 self._focusObj = None
862 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
863 #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
864 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
865 self._mouse3Dpos -= self._viewTarget
868 glTranslate(0,0,-self._zoom)
869 glRotate(-self._pitch, 1,0,0)
870 glRotate(self._yaw, 0,0,1)
871 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
873 if self.viewMode == 'gcode':
874 if self._gcode is not None and self._gcode.layerList is None:
875 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
876 self._gcodeLoadThread.daemon = True
877 self._gcodeLoadThread.start()
878 if self._gcode is not None and self._gcode.layerList is not None:
880 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
882 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
883 for n in xrange(0, drawUpTill):
884 c = 1.0 - float(drawUpTill - n) / 15
886 if len(self._gcodeVBOs) < n + 1:
887 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
888 if time.time() - t > 0.5:
891 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
892 if n == drawUpTill - 1:
893 if len(self._gcodeVBOs[n]) < 6:
894 self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
896 self._gcodeVBOs[n][5].render(GL_QUADS)
898 self._gcodeVBOs[n][6].render(GL_QUADS)
899 glColor3f(c/2, c/2, 0.0)
900 self._gcodeVBOs[n][7].render(GL_QUADS)
902 self._gcodeVBOs[n][8].render(GL_QUADS)
903 self._gcodeVBOs[n][9].render(GL_QUADS)
905 self._gcodeVBOs[n][10].render(GL_LINES)
908 self._gcodeVBOs[n][0].render(GL_LINES)
910 self._gcodeVBOs[n][1].render(GL_LINES)
911 glColor3f(c/2, c/2, 0.0)
912 self._gcodeVBOs[n][2].render(GL_LINES)
914 self._gcodeVBOs[n][3].render(GL_LINES)
915 self._gcodeVBOs[n][4].render(GL_LINES)
918 glStencilFunc(GL_ALWAYS, 1, 1)
919 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
921 if self.viewMode == 'overhang':
922 self._objectOverhangShader.bind()
923 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
925 self._objectShader.bind()
926 for obj in self._scene.objects():
927 if obj._loadAnim is not None:
928 if obj._loadAnim.isDone():
933 if self._focusObj == obj:
935 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
938 if self._selectedObj == obj or self._selectedObj is None:
939 #If we want transparent, then first render a solid black model to remove the printer size lines.
940 if self.viewMode == 'transparent':
941 glColor4f(0, 0, 0, 0)
942 self._renderObject(obj)
944 glBlendFunc(GL_ONE, GL_ONE)
945 glDisable(GL_DEPTH_TEST)
947 if self.viewMode == 'xray':
948 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
949 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
950 glEnable(GL_STENCIL_TEST)
952 if self.viewMode == 'overhang':
953 if self._selectedObj == obj and self.tempMatrix is not None:
954 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
956 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
958 if not self._scene.checkPlatform(obj):
959 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
960 self._renderObject(obj)
962 self._renderObject(obj, brightness)
963 glDisable(GL_STENCIL_TEST)
965 glEnable(GL_DEPTH_TEST)
966 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
968 if self.viewMode == 'xray':
971 glEnable(GL_STENCIL_TEST)
972 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
973 glDisable(GL_DEPTH_TEST)
974 for i in xrange(2, 15, 2):
975 glStencilFunc(GL_EQUAL, i, 0xFF)
976 glColor(float(i)/10, float(i)/10, float(i)/5)
978 glVertex3f(-1000,-1000,-10)
979 glVertex3f( 1000,-1000,-10)
980 glVertex3f( 1000, 1000,-10)
981 glVertex3f(-1000, 1000,-10)
983 for i in xrange(1, 15, 2):
984 glStencilFunc(GL_EQUAL, i, 0xFF)
985 glColor(float(i)/10, 0, 0)
987 glVertex3f(-1000,-1000,-10)
988 glVertex3f( 1000,-1000,-10)
989 glVertex3f( 1000, 1000,-10)
990 glVertex3f(-1000, 1000,-10)
993 glDisable(GL_STENCIL_TEST)
994 glEnable(GL_DEPTH_TEST)
996 self._objectShader.unbind()
998 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1000 if self._objectLoadShader is not None:
1001 self._objectLoadShader.bind()
1002 glColor4f(0.2, 0.6, 1.0, 1.0)
1003 for obj in self._scene.objects():
1004 if obj._loadAnim is None:
1006 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1007 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1008 self._renderObject(obj)
1009 self._objectLoadShader.unbind()
1014 if self.viewMode == 'gcode':
1015 if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1016 glDisable(GL_DEPTH_TEST)
1019 glTranslate(0,-4,-10)
1020 glColor4ub(60,60,60,255)
1021 opengl.glDrawStringCenter('Loading toolpath for visualization...')
1024 #Draw the object box-shadow, so you can see where it will collide with other objects.
1025 if self._selectedObj is not None and len(self._scene.objects()) > 1:
1026 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1028 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1030 glEnable(GL_CULL_FACE)
1031 glColor4f(0,0,0,0.12)
1033 glVertex3f(-size[0], size[1], 0.1)
1034 glVertex3f(-size[0], -size[1], 0.1)
1035 glVertex3f( size[0], -size[1], 0.1)
1036 glVertex3f( size[0], size[1], 0.1)
1038 glDisable(GL_CULL_FACE)
1041 #Draw the outline of the selected object, on top of everything else except the GUI.
1042 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1043 glDisable(GL_DEPTH_TEST)
1044 glEnable(GL_CULL_FACE)
1045 glEnable(GL_STENCIL_TEST)
1047 glStencilFunc(GL_EQUAL, 0, 255)
1049 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1051 glColor4f(1,1,1,0.5)
1052 self._renderObject(self._selectedObj)
1053 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1055 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1056 glDisable(GL_STENCIL_TEST)
1057 glDisable(GL_CULL_FACE)
1058 glEnable(GL_DEPTH_TEST)
1060 if self._selectedObj is not None:
1062 pos = self.getObjectCenterPos()
1063 glTranslate(pos[0], pos[1], pos[2])
1066 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1067 glDisable(GL_DEPTH_TEST)
1070 glTranslate(0,-4,-10)
1071 glColor4ub(60,60,60,255)
1072 opengl.glDrawStringCenter('Overhang view not working due to lack of OpenGL shaders support.')
1075 def _renderObject(self, obj, brightness = False, addSink = True):
1078 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1080 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1082 if self.tempMatrix is not None and obj == self._selectedObj:
1083 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1084 glMultMatrixf(tempMatrix)
1086 offset = obj.getDrawOffset()
1087 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1089 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1090 glMultMatrixf(tempMatrix)
1093 for m in obj._meshList:
1095 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1097 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1102 def _drawMachine(self):
1103 glEnable(GL_CULL_FACE)
1106 if profile.getPreference('machine_type') == 'ultimaker':
1107 glColor4f(1,1,1,0.5)
1108 self._objectShader.bind()
1109 self._renderObject(self._platformMesh, False, False)
1110 self._objectShader.unbind()
1112 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
1113 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1114 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1115 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1116 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1117 v4 = [ size[0] / 2, size[1] / 2, 0]
1118 v5 = [ size[0] / 2,-size[1] / 2, 0]
1119 v6 = [-size[0] / 2, size[1] / 2, 0]
1120 v7 = [-size[0] / 2,-size[1] / 2, 0]
1122 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1123 glEnableClientState(GL_VERTEX_ARRAY)
1124 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1126 glColor4ub(5, 171, 231, 64)
1127 glDrawArrays(GL_QUADS, 0, 4)
1128 glColor4ub(5, 171, 231, 96)
1129 glDrawArrays(GL_QUADS, 4, 8)
1130 glColor4ub(5, 171, 231, 128)
1131 glDrawArrays(GL_QUADS, 12, 8)
1133 sx = self._machineSize[0]
1134 sy = self._machineSize[1]
1135 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1136 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1141 x1 = max(min(x1, sx/2), -sx/2)
1142 y1 = max(min(y1, sy/2), -sy/2)
1143 x2 = max(min(x2, sx/2), -sx/2)
1144 y2 = max(min(y2, sy/2), -sy/2)
1145 if (x & 1) == (y & 1):
1146 glColor4ub(5, 171, 231, 127)
1148 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1150 glVertex3f(x1, y1, -0.02)
1151 glVertex3f(x2, y1, -0.02)
1152 glVertex3f(x2, y2, -0.02)
1153 glVertex3f(x1, y2, -0.02)
1156 glDisableClientState(GL_VERTEX_ARRAY)
1158 glDisable(GL_CULL_FACE)
1160 def _generateGCodeVBOs(self, layer):
1162 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1163 pointList = numpy.zeros((0,3), numpy.float32)
1165 if path['type'] == 'extrude' and path['pathType'] == extrudeType:
1167 a = numpy.concatenate((a[:-1], a[1:]), 1)
1168 a = a.reshape((len(a) * 2, 3))
1169 pointList = numpy.concatenate((pointList, a))
1170 ret.append(opengl.GLVBO(pointList))
1173 def _generateGCodeVBOs2(self, layer):
1174 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1175 filamentArea = math.pi * filamentRadius * filamentRadius
1178 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1179 pointList = numpy.zeros((0,3), numpy.float32)
1181 if path['type'] == 'extrude' and path['pathType'] == extrudeType:
1183 if extrudeType == 'FILL':
1186 normal = a[1:] - a[:-1]
1187 lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1188 normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1191 ePerDist = path['extrusion'][1:] / lens
1192 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1194 normal[:,0] *= lineWidth
1195 normal[:,1] *= lineWidth
1197 b = numpy.zeros((len(a)-1, 0), numpy.float32)
1198 b = numpy.concatenate((b, a[1:] + normal), 1)
1199 b = numpy.concatenate((b, a[1:] - normal), 1)
1200 b = numpy.concatenate((b, a[:-1] - normal), 1)
1201 b = numpy.concatenate((b, a[:-1] + normal), 1)
1202 #b = numpy.concatenate((b, a[:-1]), 1)
1203 #b = numpy.concatenate((b, a[:-1]), 1)
1204 b = b.reshape((len(b) * 4, 3))
1207 normal2 = normal[:-1] + normal[1:]
1208 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1209 normal2[:,0] /= lens2
1210 normal2[:,1] /= lens2
1211 normal2[:,0] *= lineWidth[:-1]
1212 normal2[:,1] *= lineWidth[:-1]
1214 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1215 c = numpy.concatenate((c, a[1:-1]), 1)
1216 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1217 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1218 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1220 c = numpy.concatenate((c, a[1:-1]), 1)
1221 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1222 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1223 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1225 c = c.reshape((len(c) * 8, 3))
1227 pointList = numpy.concatenate((pointList, b, c))
1229 pointList = numpy.concatenate((pointList, b))
1230 ret.append(opengl.GLVBO(pointList))
1232 pointList = numpy.zeros((0,3), numpy.float32)
1234 if path['type'] == 'move':
1235 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1236 a = numpy.concatenate((a[:-1], a[1:]), 1)
1237 a = a.reshape((len(a) * 2, 3))
1238 pointList = numpy.concatenate((pointList, a))
1239 if path['type'] == 'retract':
1240 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1241 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1242 a = a.reshape((len(a) * 2, 3))
1243 pointList = numpy.concatenate((pointList, a))
1244 ret.append(opengl.GLVBO(pointList))
1248 def getObjectCenterPos(self):
1249 if self._selectedObj is None:
1250 return [0.0, 0.0, 0.0]
1251 pos = self._selectedObj.getPosition()
1252 size = self._selectedObj.getSize()
1253 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1255 def getObjectBoundaryCircle(self):
1256 if self._selectedObj is None:
1258 return self._selectedObj.getBoundaryCircle()
1260 def getObjectSize(self):
1261 if self._selectedObj is None:
1262 return [0.0, 0.0, 0.0]
1263 return self._selectedObj.getSize()
1265 def getObjectMatrix(self):
1266 if self._selectedObj is None:
1267 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1268 return self._selectedObj.getMatrix()
1270 class shaderEditor(wx.Dialog):
1271 def __init__(self, parent, callback, v, f):
1272 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1273 self._callback = callback
1274 s = wx.BoxSizer(wx.VERTICAL)
1276 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1277 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1278 s.Add(self._vertex, 1, flag=wx.EXPAND)
1279 s.Add(self._fragment, 1, flag=wx.EXPAND)
1281 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1282 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1284 self.SetPosition(self.GetParent().GetPosition())
1285 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1288 def OnText(self, e):
1289 self._callback(self._vertex.GetValue(), self._fragment.GetValue())