chiark / gitweb /
Remove debug prints.
[cura.git] / Cura / gui / sceneView.py
1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
3
4 import wx
5 import numpy
6 import time
7 import os
8 import traceback
9 import threading
10 import math
11
12 import OpenGL
13 OpenGL.ERROR_CHECKING = False
14 from OpenGL.GLU import *
15 from OpenGL.GL import *
16
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
29 from Cura.gui.tools import youmagineGui
30
31 class SceneView(openglGui.glGuiPanel):
32         def __init__(self, parent):
33                 super(SceneView, self).__init__(parent)
34
35                 self._yaw = 30
36                 self._pitch = 60
37                 self._zoom = 300
38                 self._scene = objectScene.Scene()
39                 self._gcode = None
40                 self._gcodeVBOs = []
41                 self._gcodeFilename = None
42                 self._gcodeLoadThread = None
43                 self._objectShader = None
44                 self._objectLoadShader = None
45                 self._focusObj = None
46                 self._selectedObj = None
47                 self._objColors = [None,None,None,None]
48                 self._mouseX = -1
49                 self._mouseY = -1
50                 self._mouseState = None
51                 self._viewTarget = numpy.array([0,0,0], numpy.float32)
52                 self._animView = None
53                 self._animZoom = None
54                 self._platformMesh = {}
55                 self._isSimpleMode = True
56                 self._usbPrintMonitor = printWindow.printProcessMonitor(lambda : self._queueRefresh())
57
58                 self._viewport = None
59                 self._modelMatrix = None
60                 self._projMatrix = None
61                 self.tempMatrix = None
62
63                 self.openFileButton      = openglGui.glButton(self, 4, _("Load"), (0,0), self.showLoadModel)
64                 self.printButton         = openglGui.glButton(self, 6, _("Print"), (1,0), self.OnPrintButton)
65                 self.printButton.setDisabled(True)
66
67                 group = []
68                 self.rotateToolButton = openglGui.glRadioButton(self, 8, _("Rotate"), (0,-1), group, self.OnToolSelect)
69                 self.scaleToolButton  = openglGui.glRadioButton(self, 9, _("Scale"), (1,-1), group, self.OnToolSelect)
70                 self.mirrorToolButton  = openglGui.glRadioButton(self, 10, _("Mirror"), (2,-1), group, self.OnToolSelect)
71
72                 self.resetRotationButton = openglGui.glButton(self, 12, _("Reset"), (0,-2), self.OnRotateReset)
73                 self.layFlatButton       = openglGui.glButton(self, 16, _("Lay flat"), (0,-3), self.OnLayFlat)
74
75                 self.resetScaleButton    = openglGui.glButton(self, 13, _("Reset"), (1,-2), self.OnScaleReset)
76                 self.scaleMaxButton      = openglGui.glButton(self, 17, _("To max"), (1,-3), self.OnScaleMax)
77
78                 self.mirrorXButton       = openglGui.glButton(self, 14, _("Mirror X"), (2,-2), lambda button: self.OnMirror(0))
79                 self.mirrorYButton       = openglGui.glButton(self, 18, _("Mirror Y"), (2,-3), lambda button: self.OnMirror(1))
80                 self.mirrorZButton       = openglGui.glButton(self, 22, _("Mirror Z"), (2,-4), lambda button: self.OnMirror(2))
81
82                 self.rotateToolButton.setExpandArrow(True)
83                 self.scaleToolButton.setExpandArrow(True)
84                 self.mirrorToolButton.setExpandArrow(True)
85
86                 self.scaleForm = openglGui.glFrame(self, (2, -2))
87                 openglGui.glGuiLayoutGrid(self.scaleForm)
88                 openglGui.glLabel(self.scaleForm, _("Scale X"), (0,0))
89                 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
90                 openglGui.glLabel(self.scaleForm, _("Scale Y"), (0,1))
91                 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
92                 openglGui.glLabel(self.scaleForm, _("Scale Z"), (0,2))
93                 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
94                 openglGui.glLabel(self.scaleForm, _("Size X (mm)"), (0,4))
95                 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
96                 openglGui.glLabel(self.scaleForm, _("Size Y (mm)"), (0,5))
97                 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
98                 openglGui.glLabel(self.scaleForm, _("Size Z (mm)"), (0,6))
99                 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
100                 openglGui.glLabel(self.scaleForm, _("Uniform scale"), (0,8))
101                 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
102
103                 self.viewSelection = openglGui.glComboButton(self, _("View mode"), [7,19,11,15,23], [_("Normal"), _("Overhang"), _("Transparent"), _("X-Ray"), _("Layers")], (-1,0), self.OnViewChange)
104                 self.layerSelect = openglGui.glSlider(self, 10000, 0, 1, (-1,-2), lambda : self.QueueRefresh())
105
106                 self.youMagineButton = openglGui.glButton(self, 26, _("Share on YouMagine"), (2,0), lambda button: youmagineGui.youmagineManager(self.GetTopLevelParent(), self._scene))
107                 self.youMagineButton.setDisabled(True)
108
109                 self.notification = openglGui.glNotification(self, (0, 0))
110
111                 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
112                 self._sceneUpdateTimer = wx.Timer(self)
113                 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
114                 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
115                 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
116
117                 self.OnViewChange()
118                 self.OnToolSelect(0)
119                 self.updateToolButtons()
120                 self.updateProfileToControls()
121
122         def loadGCodeFile(self, filename):
123                 self.OnDeleteAll(None)
124                 if self._gcode is not None:
125                         self._gcode = None
126                         for layerVBOlist in self._gcodeVBOs:
127                                 for vbo in layerVBOlist:
128                                         self.glReleaseList.append(vbo)
129                         self._gcodeVBOs = []
130                 self._gcode = gcodeInterpreter.gcode()
131                 self._gcodeFilename = filename
132                 self.printButton.setBottomText('')
133                 self.viewSelection.setValue(4)
134                 self.printButton.setDisabled(False)
135                 self.youMagineButton.setDisabled(True)
136                 self.OnViewChange()
137
138         def loadSceneFiles(self, filenames):
139                 self.youMagineButton.setDisabled(False)
140                 if self.viewSelection.getValue() == 4:
141                         self.viewSelection.setValue(0)
142                         self.OnViewChange()
143                 self.loadScene(filenames)
144
145         def loadFiles(self, filenames):
146                 gcodeFilename = None
147                 for filename in filenames:
148                         self.GetParent().GetParent().GetParent().addToModelMRU(filename)
149                         ext = filename[filename.rfind('.')+1:].upper()
150                         if ext == 'G' or ext == 'GCODE':
151                                 gcodeFilename = filename
152                 if gcodeFilename is not None:
153                         self.loadGCodeFile(gcodeFilename)
154                 else:
155                         self.loadSceneFiles(filenames)
156
157         def showLoadModel(self, button = 1):
158                 if button == 1:
159                         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)
160                         dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
161                         if dlg.ShowModal() != wx.ID_OK:
162                                 dlg.Destroy()
163                                 return
164                         filenames = dlg.GetPaths()
165                         dlg.Destroy()
166                         if len(filenames) < 1:
167                                 return False
168                         profile.putPreference('lastFile', filenames[0])
169                         self.loadFiles(filenames)
170
171         def showSaveModel(self):
172                 if len(self._scene.objects()) < 1:
173                         return
174                 dlg=wx.FileDialog(self, _("Save 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
175                 dlg.SetWildcard(meshLoader.saveWildcardFilter())
176                 if dlg.ShowModal() != wx.ID_OK:
177                         dlg.Destroy()
178                         return
179                 filename = dlg.GetPath()
180                 dlg.Destroy()
181                 meshLoader.saveMeshes(filename, self._scene.objects())
182
183         def OnPrintButton(self, button):
184                 if button == 1:
185                         if machineCom.machineIsConnected():
186                                 self.showPrintWindow()
187                         elif len(removableStorage.getPossibleSDcardDrives()) > 0:
188                                 drives = removableStorage.getPossibleSDcardDrives()
189                                 if len(drives) > 1:
190                                         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))
191                                         if dlg.ShowModal() != wx.ID_OK:
192                                                 dlg.Destroy()
193                                                 return
194                                         drive = drives[dlg.GetSelection()]
195                                         dlg.Destroy()
196                                 else:
197                                         drive = drives[0]
198                                 filename = self._scene._objectList[0].getName() + '.gcode'
199                                 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
200                         else:
201                                 self.showSaveGCode()
202                 if button == 3:
203                         menu = wx.Menu()
204                         self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB")))
205                         self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode...")))
206                         self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, _("Slice engine log...")))
207                         self.PopupMenu(menu)
208                         menu.Destroy()
209
210         def showPrintWindow(self):
211                 if self._gcodeFilename is None:
212                         return
213                 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
214                 if self._gcodeFilename == self._slicer.getGCodeFilename():
215                         self._slicer.submitSliceInfoOnline()
216
217         def showSaveGCode(self):
218                 defPath = profile.getPreference('lastFile')
219                 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
220                 dlg=wx.FileDialog(self, _("Save toolpath"), defPath, style=wx.FD_SAVE)
221                 dlg.SetFilename(self._scene._objectList[0].getName())
222                 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
223                 if dlg.ShowModal() != wx.ID_OK:
224                         dlg.Destroy()
225                         return
226                 filename = dlg.GetPath()
227                 dlg.Destroy()
228
229                 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
230
231         def _copyFile(self, fileA, fileB, allowEject = False):
232                 try:
233                         size = float(os.stat(fileA).st_size)
234                         with open(fileA, 'rb') as fsrc:
235                                 with open(fileB, 'wb') as fdst:
236                                         while 1:
237                                                 buf = fsrc.read(16*1024)
238                                                 if not buf:
239                                                         break
240                                                 fdst.write(buf)
241                                                 self.printButton.setProgressBar(float(fsrc.tell()) / size)
242                                                 self._queueRefresh()
243                 except:
244                         import sys
245                         print sys.exc_info()
246                         self.notification.message("Failed to save")
247                 else:
248                         if allowEject:
249                                 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...'))
250                         else:
251                                 self.notification.message("Saved as %s" % (fileB))
252                 self.printButton.setProgressBar(None)
253                 if fileA == self._slicer.getGCodeFilename():
254                         self._slicer.submitSliceInfoOnline()
255
256         def _showSliceLog(self):
257                 dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
258                 dlg.ShowModal()
259                 dlg.Destroy()
260
261         def OnToolSelect(self, button):
262                 if self.rotateToolButton.getSelected():
263                         self.tool = previewTools.toolRotate(self)
264                 elif self.scaleToolButton.getSelected():
265                         self.tool = previewTools.toolScale(self)
266                 elif self.mirrorToolButton.getSelected():
267                         self.tool = previewTools.toolNone(self)
268                 else:
269                         self.tool = previewTools.toolNone(self)
270                 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
271                 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
272                 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
273                 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
274                 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
275                 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
276                 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
277                 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
278
279         def updateToolButtons(self):
280                 if self._selectedObj is None:
281                         hidden = True
282                 else:
283                         hidden = False
284                 self.rotateToolButton.setHidden(hidden)
285                 self.scaleToolButton.setHidden(hidden)
286                 self.mirrorToolButton.setHidden(hidden)
287                 if hidden:
288                         self.rotateToolButton.setSelected(False)
289                         self.scaleToolButton.setSelected(False)
290                         self.mirrorToolButton.setSelected(False)
291                         self.OnToolSelect(0)
292
293         def OnViewChange(self):
294                 if self.viewSelection.getValue() == 4:
295                         self.viewMode = 'gcode'
296                         if self._gcode is not None and self._gcode.layerList is not None:
297                                 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
298                         self._selectObject(None)
299                 elif self.viewSelection.getValue() == 1:
300                         self.viewMode = 'overhang'
301                 elif self.viewSelection.getValue() == 2:
302                         self.viewMode = 'transparent'
303                 elif self.viewSelection.getValue() == 3:
304                         self.viewMode = 'xray'
305                 else:
306                         self.viewMode = 'normal'
307                 self.layerSelect.setHidden(self.viewMode != 'gcode')
308                 self.QueueRefresh()
309
310         def OnRotateReset(self, button):
311                 if self._selectedObj is None:
312                         return
313                 self._selectedObj.resetRotation()
314                 self._scene.pushFree()
315                 self._selectObject(self._selectedObj)
316                 self.sceneUpdated()
317
318         def OnLayFlat(self, button):
319                 if self._selectedObj is None:
320                         return
321                 self._selectedObj.layFlat()
322                 self._scene.pushFree()
323                 self._selectObject(self._selectedObj)
324                 self.sceneUpdated()
325
326         def OnScaleReset(self, button):
327                 if self._selectedObj is None:
328                         return
329                 self._selectedObj.resetScale()
330                 self._selectObject(self._selectedObj)
331                 self.updateProfileToControls()
332                 self.sceneUpdated()
333
334         def OnScaleMax(self, button):
335                 if self._selectedObj is None:
336                         return
337                 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
338                 self._scene.pushFree()
339                 self._selectObject(self._selectedObj)
340                 self.updateProfileToControls()
341                 self.sceneUpdated()
342
343         def OnMirror(self, axis):
344                 if self._selectedObj is None:
345                         return
346                 self._selectedObj.mirror(axis)
347                 self.sceneUpdated()
348
349         def OnScaleEntry(self, value, axis):
350                 if self._selectedObj is None:
351                         return
352                 try:
353                         value = float(value)
354                 except:
355                         return
356                 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
357                 self.updateProfileToControls()
358                 self._scene.pushFree()
359                 self._selectObject(self._selectedObj)
360                 self.sceneUpdated()
361
362         def OnScaleEntryMM(self, value, axis):
363                 if self._selectedObj is None:
364                         return
365                 try:
366                         value = float(value)
367                 except:
368                         return
369                 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
370                 self.updateProfileToControls()
371                 self._scene.pushFree()
372                 self._selectObject(self._selectedObj)
373                 self.sceneUpdated()
374
375         def OnDeleteAll(self, e):
376                 while len(self._scene.objects()) > 0:
377                         self._deleteObject(self._scene.objects()[0])
378                 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
379
380         def OnMultiply(self, e):
381                 if self._focusObj is None:
382                         return
383                 obj = self._focusObj
384                 dlg = wx.NumberEntryDialog(self, "How many copies do you want?", "Copies", "Multiply", 1, 1, 100)
385                 if dlg.ShowModal() != wx.ID_OK:
386                         dlg.Destroy()
387                         return
388                 cnt = dlg.GetValue()
389                 dlg.Destroy()
390                 n = 0
391                 while True:
392                         n += 1
393                         newObj = obj.copy()
394                         self._scene.add(newObj)
395                         self._scene.centerAll()
396                         if not self._scene.checkPlatform(newObj):
397                                 break
398                         if n > cnt:
399                                 break
400                 if n <= cnt:
401                         self.notification.message("Could not create more then %d items" % (n - 1))
402                 self._scene.remove(newObj)
403                 self._scene.centerAll()
404                 self.sceneUpdated()
405
406         def OnSplitObject(self, e):
407                 if self._focusObj is None:
408                         return
409                 self._scene.remove(self._focusObj)
410                 for obj in self._focusObj.split(self._splitCallback):
411                         if numpy.max(obj.getSize()) > 2.0:
412                                 self._scene.add(obj)
413                 self._scene.centerAll()
414                 self._selectObject(None)
415                 self.sceneUpdated()
416
417         def _splitCallback(self, progress):
418                 print progress
419
420         def OnMergeObjects(self, e):
421                 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
422                         if len(self._scene.objects()) == 2:
423                                 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
424                                 self.sceneUpdated()
425                         return
426                 self._scene.merge(self._selectedObj, self._focusObj)
427                 self.sceneUpdated()
428
429         def sceneUpdated(self):
430                 self._sceneUpdateTimer.Start(500, True)
431                 self._slicer.abortSlicer()
432                 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
433                 self.QueueRefresh()
434
435         def _onRunSlicer(self, e):
436                 if self._isSimpleMode:
437                         self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
438                 self._slicer.runSlicer(self._scene)
439                 if self._isSimpleMode:
440                         profile.resetTempOverride()
441
442         def _updateSliceProgress(self, progressValue, ready):
443                 if not ready:
444                         if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
445                                 return
446                 self.printButton.setDisabled(not ready)
447                 if progressValue >= 0.0:
448                         self.printButton.setProgressBar(progressValue)
449                 else:
450                         self.printButton.setProgressBar(None)
451                 if self._gcode is not None:
452                         self._gcode = None
453                         for layerVBOlist in self._gcodeVBOs:
454                                 for vbo in layerVBOlist:
455                                         self.glReleaseList.append(vbo)
456                         self._gcodeVBOs = []
457                 if ready:
458                         self.printButton.setProgressBar(None)
459                         cost = self._slicer.getFilamentCost()
460                         if cost is not None:
461                                 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
462                         else:
463                                 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
464                         self._gcode = gcodeInterpreter.gcode()
465                         self._gcodeFilename = self._slicer.getGCodeFilename()
466                 else:
467                         self.printButton.setBottomText('')
468                 self.QueueRefresh()
469
470         def _loadGCode(self):
471                 self._gcode.progressCallback = self._gcodeLoadCallback
472                 self._gcode.load(self._gcodeFilename)
473
474         def _gcodeLoadCallback(self, progress):
475                 if self._gcode is None:
476                         return True
477                 if len(self._gcode.layerList) % 15 == 0:
478                         time.sleep(0.1)
479                 if self._gcode is None:
480                         return True
481                 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
482                 if self.viewMode == 'gcode':
483                         self._queueRefresh()
484                 return False
485
486         def loadScene(self, fileList):
487                 for filename in fileList:
488                         try:
489                                 objList = meshLoader.loadMeshes(filename)
490                         except:
491                                 traceback.print_exc()
492                         else:
493                                 for obj in objList:
494                                         if self._objectLoadShader is not None:
495                                                 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
496                                         else:
497                                                 obj._loadAnim = None
498                                         self._scene.add(obj)
499                                         self._scene.centerAll()
500                                         self._selectObject(obj)
501                                         if obj.getScale()[0] < 1.0:
502                                                 self.notification.message("Warning: Object scaled down.")
503                 self.sceneUpdated()
504
505         def _deleteObject(self, obj):
506                 if obj == self._selectedObj:
507                         self._selectObject(None)
508                 if obj == self._focusObj:
509                         self._focusObj = None
510                 self._scene.remove(obj)
511                 for m in obj._meshList:
512                         if m.vbo is not None and m.vbo.decRef():
513                                 self.glReleaseList.append(m.vbo)
514                 import gc
515                 gc.collect()
516                 self.sceneUpdated()
517
518         def _selectObject(self, obj, zoom = True):
519                 if obj != self._selectedObj:
520                         self._selectedObj = obj
521                         self.updateProfileToControls()
522                         self.updateToolButtons()
523                 if zoom and obj is not None:
524                         newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
525                         self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
526                         newZoom = obj.getBoundaryCircle() * 6
527                         if newZoom > numpy.max(self._machineSize) * 3:
528                                 newZoom = numpy.max(self._machineSize) * 3
529                         self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
530
531         def updateProfileToControls(self):
532                 oldSimpleMode = self._isSimpleMode
533                 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
534                 if self._isSimpleMode != oldSimpleMode:
535                         self._scene.arrangeAll()
536                         self.sceneUpdated()
537                 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
538                 self._objColors[0] = profile.getPreferenceColour('model_colour')
539                 self._objColors[1] = profile.getPreferenceColour('model_colour2')
540                 self._objColors[2] = profile.getPreferenceColour('model_colour3')
541                 self._objColors[3] = profile.getPreferenceColour('model_colour4')
542                 self._scene.setMachineSize(self._machineSize)
543                 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
544                 self._scene.setHeadSize(profile.getMachineSettingFloat('extruder_head_size_min_x'), profile.getMachineSettingFloat('extruder_head_size_max_x'), profile.getMachineSettingFloat('extruder_head_size_min_y'), profile.getMachineSettingFloat('extruder_head_size_max_y'), profile.getMachineSettingFloat('extruder_head_size_height'))
545
546                 if self._selectedObj is not None:
547                         scale = self._selectedObj.getScale()
548                         size = self._selectedObj.getSize()
549                         self.scaleXctrl.setValue(round(scale[0], 2))
550                         self.scaleYctrl.setValue(round(scale[1], 2))
551                         self.scaleZctrl.setValue(round(scale[2], 2))
552                         self.scaleXmmctrl.setValue(round(size[0], 2))
553                         self.scaleYmmctrl.setValue(round(size[1], 2))
554                         self.scaleZmmctrl.setValue(round(size[2], 2))
555
556         def OnKeyChar(self, keyCode):
557                 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
558                         if self._selectedObj is not None:
559                                 self._deleteObject(self._selectedObj)
560                                 self.QueueRefresh()
561                 if keyCode == wx.WXK_UP:
562                         self.layerSelect.setValue(self.layerSelect.getValue() + 1)
563                         self.QueueRefresh()
564                 elif keyCode == wx.WXK_DOWN:
565                         self.layerSelect.setValue(self.layerSelect.getValue() - 1)
566                         self.QueueRefresh()
567                 elif keyCode == wx.WXK_PAGEUP:
568                         self.layerSelect.setValue(self.layerSelect.getValue() + 10)
569                         self.QueueRefresh()
570                 elif keyCode == wx.WXK_PAGEDOWN:
571                         self.layerSelect.setValue(self.layerSelect.getValue() - 10)
572                         self.QueueRefresh()
573
574                 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
575                         shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
576                 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
577                         from collections import defaultdict
578                         from gc import get_objects
579                         self._beforeLeakTest = defaultdict(int)
580                         for i in get_objects():
581                                 self._beforeLeakTest[type(i)] += 1
582                 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
583                         from collections import defaultdict
584                         from gc import get_objects
585                         self._afterLeakTest = defaultdict(int)
586                         for i in get_objects():
587                                 self._afterLeakTest[type(i)] += 1
588                         for k in self._afterLeakTest:
589                                 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
590                                         print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
591
592         def ShaderUpdate(self, v, f):
593                 s = opengl.GLShader(v, f)
594                 if s.isValid():
595                         self._objectLoadShader.release()
596                         self._objectLoadShader = s
597                         for obj in self._scene.objects():
598                                 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
599                         self.QueueRefresh()
600
601         def OnMouseDown(self,e):
602                 self._mouseX = e.GetX()
603                 self._mouseY = e.GetY()
604                 self._mouseClick3DPos = self._mouse3Dpos
605                 self._mouseClickFocus = self._focusObj
606                 if e.ButtonDClick():
607                         self._mouseState = 'doubleClick'
608                 else:
609                         self._mouseState = 'dragOrClick'
610                 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
611                 p0 -= self.getObjectCenterPos() - self._viewTarget
612                 p1 -= self.getObjectCenterPos() - self._viewTarget
613                 if self.tool.OnDragStart(p0, p1):
614                         self._mouseState = 'tool'
615                 if self._mouseState == 'dragOrClick':
616                         if e.GetButton() == 1:
617                                 if self._focusObj is not None:
618                                         self._selectObject(self._focusObj, False)
619                                         self.QueueRefresh()
620
621         def OnMouseUp(self, e):
622                 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
623                         return
624                 if self._mouseState == 'dragOrClick':
625                         if e.GetButton() == 1:
626                                 self._selectObject(self._focusObj)
627                         if e.GetButton() == 3:
628                                         menu = wx.Menu()
629                                         if self._focusObj is not None:
630                                                 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete")))
631                                                 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply")))
632                                                 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split")))
633                                         if ((self._selectedObj != self._focusObj and self._focusObj is not None and self._selectedObj is not None) or len(self._scene.objects()) == 2) and int(profile.getMachineSetting('extruder_amount')) > 1:
634                                                 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
635                                         if len(self._scene.objects()) > 0:
636                                                 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all")))
637                                         if menu.MenuItemCount > 0:
638                                                 self.PopupMenu(menu)
639                                         menu.Destroy()
640                 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
641                         self._scene.pushFree()
642                         self.sceneUpdated()
643                 elif self._mouseState == 'tool':
644                         if self.tempMatrix is not None and self._selectedObj is not None:
645                                 self._selectedObj.applyMatrix(self.tempMatrix)
646                                 self._scene.pushFree()
647                                 self._selectObject(self._selectedObj)
648                         self.tempMatrix = None
649                         self.tool.OnDragEnd()
650                         self.sceneUpdated()
651                 self._mouseState = None
652
653         def OnMouseMotion(self,e):
654                 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
655                 p0 -= self.getObjectCenterPos() - self._viewTarget
656                 p1 -= self.getObjectCenterPos() - self._viewTarget
657
658                 if e.Dragging() and self._mouseState is not None:
659                         if self._mouseState == 'tool':
660                                 self.tool.OnDrag(p0, p1)
661                         elif not e.LeftIsDown() and e.RightIsDown():
662                                 self._mouseState = 'drag'
663                                 if wx.GetKeyState(wx.WXK_SHIFT):
664                                         a = math.cos(math.radians(self._yaw)) / 3.0
665                                         b = math.sin(math.radians(self._yaw)) / 3.0
666                                         self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
667                                         self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
668                                         self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
669                                         self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
670                                 else:
671                                         self._yaw += e.GetX() - self._mouseX
672                                         self._pitch -= e.GetY() - self._mouseY
673                                 if self._pitch > 170:
674                                         self._pitch = 170
675                                 if self._pitch < 10:
676                                         self._pitch = 10
677                         elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
678                                 self._mouseState = 'drag'
679                                 self._zoom += e.GetY() - self._mouseY
680                                 if self._zoom < 1:
681                                         self._zoom = 1
682                                 if self._zoom > numpy.max(self._machineSize) * 3:
683                                         self._zoom = numpy.max(self._machineSize) * 3
684                         elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
685                                 self._mouseState = 'dragObject'
686                                 z = max(0, self._mouseClick3DPos[2])
687                                 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
688                                 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
689                                 p0[2] -= z
690                                 p1[2] -= z
691                                 p2[2] -= z
692                                 p3[2] -= z
693                                 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
694                                 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
695                                 diff = cursorZ1 - cursorZ0
696                                 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
697                 if not e.Dragging() or self._mouseState != 'tool':
698                         self.tool.OnMouseMove(p0, p1)
699
700                 self._mouseX = e.GetX()
701                 self._mouseY = e.GetY()
702
703         def OnMouseWheel(self, e):
704                 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
705                 delta = max(min(delta,4),-4)
706                 self._zoom *= 1.0 - delta / 10.0
707                 if self._zoom < 1.0:
708                         self._zoom = 1.0
709                 if self._zoom > numpy.max(self._machineSize) * 3:
710                         self._zoom = numpy.max(self._machineSize) * 3
711                 self.Refresh()
712
713         def OnMouseLeave(self, e):
714                 #self._mouseX = -1
715                 pass
716
717         def getMouseRay(self, x, y):
718                 if self._viewport is None:
719                         return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
720                 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
721                 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
722                 p0 -= self._viewTarget
723                 p1 -= self._viewTarget
724                 return p0, p1
725
726         def _init3DView(self):
727                 # set viewing projection
728                 size = self.GetSize()
729                 glViewport(0, 0, size.GetWidth(), size.GetHeight())
730                 glLoadIdentity()
731
732                 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
733
734                 glDisable(GL_RESCALE_NORMAL)
735                 glDisable(GL_LIGHTING)
736                 glDisable(GL_LIGHT0)
737                 glEnable(GL_DEPTH_TEST)
738                 glDisable(GL_CULL_FACE)
739                 glDisable(GL_BLEND)
740                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
741
742                 glClearColor(0.8, 0.8, 0.8, 1.0)
743                 glClearStencil(0)
744                 glClearDepth(1.0)
745
746                 glMatrixMode(GL_PROJECTION)
747                 glLoadIdentity()
748                 aspect = float(size.GetWidth()) / float(size.GetHeight())
749                 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
750
751                 glMatrixMode(GL_MODELVIEW)
752                 glLoadIdentity()
753                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
754
755         def OnPaint(self,e):
756                 if machineCom.machineIsConnected():
757                         self.printButton._imageID = 6
758                         self.printButton._tooltip = _("Print")
759                 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
760                         self.printButton._imageID = 2
761                         self.printButton._tooltip = _("Toolpath to SD")
762                 else:
763                         self.printButton._imageID = 3
764                         self.printButton._tooltip = _("Save toolpath")
765
766                 if self._animView is not None:
767                         self._viewTarget = self._animView.getPosition()
768                         if self._animView.isDone():
769                                 self._animView = None
770                 if self._animZoom is not None:
771                         self._zoom = self._animZoom.getPosition()
772                         if self._animZoom.isDone():
773                                 self._animZoom = None
774                 if self.viewMode == 'gcode' and self._gcode is not None:
775                         try:
776                                 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
777                         except:
778                                 pass
779                 if self._objectShader is None:
780                         if opengl.hasShaderSupport():
781                                 self._objectShader = opengl.GLShader("""
782 varying float light_amount;
783
784 void main(void)
785 {
786     gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
787     gl_FrontColor = gl_Color;
788
789         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
790         light_amount += 0.2;
791 }
792                                 ""","""
793 varying float light_amount;
794
795 void main(void)
796 {
797         gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
798 }
799                                 """)
800                                 self._objectOverhangShader = opengl.GLShader("""
801 uniform float cosAngle;
802 uniform mat3 rotMatrix;
803 varying float light_amount;
804
805 void main(void)
806 {
807     gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
808     gl_FrontColor = gl_Color;
809
810         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
811         light_amount += 0.2;
812         if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
813         {
814                 light_amount = -10.0;
815         }
816 }
817                                 ""","""
818 varying float light_amount;
819
820 void main(void)
821 {
822         if (light_amount == -10.0)
823         {
824                 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
825         }else{
826                 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
827         }
828 }
829                                 """)
830                                 self._objectLoadShader = opengl.GLShader("""
831 uniform float intensity;
832 uniform float scale;
833 varying float light_amount;
834
835 void main(void)
836 {
837         vec4 tmp = gl_Vertex;
838     tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
839     tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
840     gl_Position = gl_ModelViewProjectionMatrix * tmp;
841     gl_FrontColor = gl_Color;
842
843         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
844         light_amount += 0.2;
845 }
846                         ""","""
847 uniform float intensity;
848 varying float light_amount;
849
850 void main(void)
851 {
852         gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
853 }
854                                 """)
855                         if self._objectShader == None or not self._objectShader.isValid():
856                                 self._objectShader = opengl.GLFakeShader()
857                                 self._objectOverhangShader = opengl.GLFakeShader()
858                                 self._objectLoadShader = None
859                 self._init3DView()
860                 glTranslate(0,0,-self._zoom)
861                 glRotate(-self._pitch, 1,0,0)
862                 glRotate(self._yaw, 0,0,1)
863                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
864
865                 self._viewport = glGetIntegerv(GL_VIEWPORT)
866                 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
867                 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
868
869                 glClearColor(1,1,1,1)
870                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
871
872                 if self.viewMode != 'gcode':
873                         for n in xrange(0, len(self._scene.objects())):
874                                 obj = self._scene.objects()[n]
875                                 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
876                                 self._renderObject(obj)
877
878                 if self._mouseX > -1:
879                         glFlush()
880                         n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
881                         if n < len(self._scene.objects()):
882                                 self._focusObj = self._scene.objects()[n]
883                         else:
884                                 self._focusObj = None
885                         f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
886                         #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
887                         self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
888                         self._mouse3Dpos -= self._viewTarget
889
890                 self._init3DView()
891                 glTranslate(0,0,-self._zoom)
892                 glRotate(-self._pitch, 1,0,0)
893                 glRotate(self._yaw, 0,0,1)
894                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
895
896                 if self.viewMode == 'gcode':
897                         if self._gcode is not None and self._gcode.layerList is None:
898                                 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
899                                 self._gcodeLoadThread.daemon = True
900                                 self._gcodeLoadThread.start()
901                         if self._gcode is not None and self._gcode.layerList is not None:
902                                 glPushMatrix()
903                                 if profile.getMachineSetting('machine_center_is_zero') != 'True':
904                                         glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
905                                 t = time.time()
906                                 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
907                                 for n in xrange(0, drawUpTill):
908                                         c = 1.0 - float(drawUpTill - n) / 15
909                                         c = max(0.3, c)
910                                         if len(self._gcodeVBOs) < n + 1:
911                                                 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
912                                                 if time.time() - t > 0.5:
913                                                         self.QueueRefresh()
914                                                         break
915                                         #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
916                                         if n == drawUpTill - 1:
917                                                 if len(self._gcodeVBOs[n]) < 9:
918                                                         self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
919                                                 glColor3f(c, 0, 0)
920                                                 self._gcodeVBOs[n][8].render(GL_QUADS)
921                                                 glColor3f(c/2, 0, c)
922                                                 self._gcodeVBOs[n][9].render(GL_QUADS)
923                                                 glColor3f(0, c, c/2)
924                                                 self._gcodeVBOs[n][10].render(GL_QUADS)
925                                                 glColor3f(c, 0, 0)
926                                                 self._gcodeVBOs[n][11].render(GL_QUADS)
927
928                                                 glColor3f(0, c, 0)
929                                                 self._gcodeVBOs[n][12].render(GL_QUADS)
930                                                 glColor3f(c/2, c/2, 0.0)
931                                                 self._gcodeVBOs[n][13].render(GL_QUADS)
932                                                 glColor3f(0, c, c)
933                                                 self._gcodeVBOs[n][14].render(GL_QUADS)
934                                                 self._gcodeVBOs[n][15].render(GL_QUADS)
935                                                 glColor3f(0, 0, c)
936                                                 self._gcodeVBOs[n][16].render(GL_LINES)
937                                         else:
938                                                 glColor3f(c, 0, 0)
939                                                 self._gcodeVBOs[n][0].render(GL_LINES)
940                                                 glColor3f(c/2, 0, c)
941                                                 self._gcodeVBOs[n][1].render(GL_LINES)
942                                                 glColor3f(0, c, c/2)
943                                                 self._gcodeVBOs[n][2].render(GL_LINES)
944                                                 glColor3f(c, 0, 0)
945                                                 self._gcodeVBOs[n][3].render(GL_LINES)
946
947                                                 glColor3f(0, c, 0)
948                                                 self._gcodeVBOs[n][4].render(GL_LINES)
949                                                 glColor3f(c/2, c/2, 0.0)
950                                                 self._gcodeVBOs[n][5].render(GL_LINES)
951                                                 glColor3f(0, c, c)
952                                                 self._gcodeVBOs[n][6].render(GL_LINES)
953                                                 self._gcodeVBOs[n][7].render(GL_LINES)
954                                 glPopMatrix()
955                 else:
956                         glStencilFunc(GL_ALWAYS, 1, 1)
957                         glStencilOp(GL_INCR, GL_INCR, GL_INCR)
958
959                         if self.viewMode == 'overhang':
960                                 self._objectOverhangShader.bind()
961                                 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
962                         else:
963                                 self._objectShader.bind()
964                         for obj in self._scene.objects():
965                                 if obj._loadAnim is not None:
966                                         if obj._loadAnim.isDone():
967                                                 obj._loadAnim = None
968                                         else:
969                                                 continue
970                                 brightness = 1.0
971                                 if self._focusObj == obj:
972                                         brightness = 1.2
973                                 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
974                                         brightness = 0.8
975
976                                 if self._selectedObj == obj or self._selectedObj is None:
977                                         #If we want transparent, then first render a solid black model to remove the printer size lines.
978                                         if self.viewMode == 'transparent':
979                                                 glColor4f(0, 0, 0, 0)
980                                                 self._renderObject(obj)
981                                                 glEnable(GL_BLEND)
982                                                 glBlendFunc(GL_ONE, GL_ONE)
983                                                 glDisable(GL_DEPTH_TEST)
984                                                 brightness *= 0.5
985                                         if self.viewMode == 'xray':
986                                                 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
987                                         glStencilOp(GL_INCR, GL_INCR, GL_INCR)
988                                         glEnable(GL_STENCIL_TEST)
989
990                                 if self.viewMode == 'overhang':
991                                         if self._selectedObj == obj and self.tempMatrix is not None:
992                                                 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
993                                         else:
994                                                 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
995
996                                 if not self._scene.checkPlatform(obj):
997                                         glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
998                                         self._renderObject(obj)
999                                 else:
1000                                         self._renderObject(obj, brightness)
1001                                 glDisable(GL_STENCIL_TEST)
1002                                 glDisable(GL_BLEND)
1003                                 glEnable(GL_DEPTH_TEST)
1004                                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1005
1006                         if self.viewMode == 'xray':
1007                                 glPushMatrix()
1008                                 glLoadIdentity()
1009                                 glEnable(GL_STENCIL_TEST)
1010                                 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1011                                 glDisable(GL_DEPTH_TEST)
1012                                 for i in xrange(2, 15, 2):
1013                                         glStencilFunc(GL_EQUAL, i, 0xFF)
1014                                         glColor(float(i)/10, float(i)/10, float(i)/5)
1015                                         glBegin(GL_QUADS)
1016                                         glVertex3f(-1000,-1000,-10)
1017                                         glVertex3f( 1000,-1000,-10)
1018                                         glVertex3f( 1000, 1000,-10)
1019                                         glVertex3f(-1000, 1000,-10)
1020                                         glEnd()
1021                                 for i in xrange(1, 15, 2):
1022                                         glStencilFunc(GL_EQUAL, i, 0xFF)
1023                                         glColor(float(i)/10, 0, 0)
1024                                         glBegin(GL_QUADS)
1025                                         glVertex3f(-1000,-1000,-10)
1026                                         glVertex3f( 1000,-1000,-10)
1027                                         glVertex3f( 1000, 1000,-10)
1028                                         glVertex3f(-1000, 1000,-10)
1029                                         glEnd()
1030                                 glPopMatrix()
1031                                 glDisable(GL_STENCIL_TEST)
1032                                 glEnable(GL_DEPTH_TEST)
1033
1034                         self._objectShader.unbind()
1035
1036                         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1037                         glEnable(GL_BLEND)
1038                         if self._objectLoadShader is not None:
1039                                 self._objectLoadShader.bind()
1040                                 glColor4f(0.2, 0.6, 1.0, 1.0)
1041                                 for obj in self._scene.objects():
1042                                         if obj._loadAnim is None:
1043                                                 continue
1044                                         self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1045                                         self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1046                                         self._renderObject(obj)
1047                                 self._objectLoadShader.unbind()
1048                                 glDisable(GL_BLEND)
1049
1050                 self._drawMachine()
1051
1052                 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1053                         glEnable(GL_BLEND)
1054                         z = self._usbPrintMonitor.getZ()
1055                         size = self._machineSize
1056                         glColor4ub(255,255,0,128)
1057                         glBegin(GL_QUADS)
1058                         glVertex3f(-size[0]/2,-size[1]/2, z)
1059                         glVertex3f( size[0]/2,-size[1]/2, z)
1060                         glVertex3f( size[0]/2, size[1]/2, z)
1061                         glVertex3f(-size[0]/2, size[1]/2, z)
1062                         glEnd()
1063
1064                 if self.viewMode == 'gcode':
1065                         if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1066                                 glDisable(GL_DEPTH_TEST)
1067                                 glPushMatrix()
1068                                 glLoadIdentity()
1069                                 glTranslate(0,-4,-10)
1070                                 glColor4ub(60,60,60,255)
1071                                 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1072                                 glPopMatrix()
1073                 else:
1074                         #Draw the object box-shadow, so you can see where it will collide with other objects.
1075                         if self._selectedObj is not None and len(self._scene.objects()) > 1:
1076                                 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1077                                 glPushMatrix()
1078                                 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1079                                 glEnable(GL_BLEND)
1080                                 glEnable(GL_CULL_FACE)
1081                                 glColor4f(0,0,0,0.12)
1082                                 glBegin(GL_QUADS)
1083                                 glVertex3f(-size[0],  size[1], 0.1)
1084                                 glVertex3f(-size[0], -size[1], 0.1)
1085                                 glVertex3f( size[0], -size[1], 0.1)
1086                                 glVertex3f( size[0],  size[1], 0.1)
1087                                 glEnd()
1088                                 glDisable(GL_CULL_FACE)
1089                                 glPopMatrix()
1090
1091                         #Draw the outline of the selected object, on top of everything else except the GUI.
1092                         if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1093                                 glDisable(GL_DEPTH_TEST)
1094                                 glEnable(GL_CULL_FACE)
1095                                 glEnable(GL_STENCIL_TEST)
1096                                 glDisable(GL_BLEND)
1097                                 glStencilFunc(GL_EQUAL, 0, 255)
1098
1099                                 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1100                                 glLineWidth(2)
1101                                 glColor4f(1,1,1,0.5)
1102                                 self._renderObject(self._selectedObj)
1103                                 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1104
1105                                 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1106                                 glDisable(GL_STENCIL_TEST)
1107                                 glDisable(GL_CULL_FACE)
1108                                 glEnable(GL_DEPTH_TEST)
1109
1110                         if self._selectedObj is not None:
1111                                 glPushMatrix()
1112                                 pos = self.getObjectCenterPos()
1113                                 glTranslate(pos[0], pos[1], pos[2])
1114                                 self.tool.OnDraw()
1115                                 glPopMatrix()
1116                 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1117                         glDisable(GL_DEPTH_TEST)
1118                         glPushMatrix()
1119                         glLoadIdentity()
1120                         glTranslate(0,-4,-10)
1121                         glColor4ub(60,60,60,255)
1122                         opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1123                         glPopMatrix()
1124
1125         def _renderObject(self, obj, brightness = False, addSink = True):
1126                 glPushMatrix()
1127                 if addSink:
1128                         glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1129                 else:
1130                         glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1131
1132                 if self.tempMatrix is not None and obj == self._selectedObj:
1133                         tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1134                         glMultMatrixf(tempMatrix)
1135
1136                 offset = obj.getDrawOffset()
1137                 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1138
1139                 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1140                 glMultMatrixf(tempMatrix)
1141
1142                 n = 0
1143                 for m in obj._meshList:
1144                         if m.vbo is None:
1145                                 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1146                         if brightness:
1147                                 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1148                                 n += 1
1149                         m.vbo.render()
1150                 glPopMatrix()
1151
1152         def _drawMachine(self):
1153                 glEnable(GL_CULL_FACE)
1154                 glEnable(GL_BLEND)
1155
1156                 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1157
1158                 machine = profile.getMachineSetting('machine_type')
1159                 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
1160                         if machine not in self._platformMesh:
1161                                 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1162                                 if len(meshes) > 0:
1163                                         self._platformMesh[machine] = meshes[0]
1164                                 else:
1165                                         self._platformMesh[machine] = None
1166                                 if profile.getMachineSetting('machine_type') == 'ultimaker2':
1167                                         self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1168                                 else:
1169                                         self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1170                         glColor4f(1,1,1,0.5)
1171                         self._objectShader.bind()
1172                         self._renderObject(self._platformMesh[machine], False, False)
1173                         self._objectShader.unbind()
1174                 else:
1175                         glColor4f(0,0,0,1)
1176                         glLineWidth(3)
1177                         glBegin(GL_LINES)
1178                         glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1179                         glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1180                         glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1181                         glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1182                         glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1183                         glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1184                         glEnd()
1185
1186                 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1187                 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1188                 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1189                 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1190                 v4 = [ size[0] / 2, size[1] / 2, 0]
1191                 v5 = [ size[0] / 2,-size[1] / 2, 0]
1192                 v6 = [-size[0] / 2, size[1] / 2, 0]
1193                 v7 = [-size[0] / 2,-size[1] / 2, 0]
1194
1195                 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1196                 glEnableClientState(GL_VERTEX_ARRAY)
1197                 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1198
1199                 glColor4ub(5, 171, 231, 64)
1200                 glDrawArrays(GL_QUADS, 0, 4)
1201                 glColor4ub(5, 171, 231, 96)
1202                 glDrawArrays(GL_QUADS, 4, 8)
1203                 glColor4ub(5, 171, 231, 128)
1204                 glDrawArrays(GL_QUADS, 12, 8)
1205                 glDisableClientState(GL_VERTEX_ARRAY)
1206
1207                 sx = self._machineSize[0]
1208                 sy = self._machineSize[1]
1209                 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1210                         for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1211                                 x1 = x * 10
1212                                 x2 = x1 + 10
1213                                 y1 = y * 10
1214                                 y2 = y1 + 10
1215                                 x1 = max(min(x1, sx/2), -sx/2)
1216                                 y1 = max(min(y1, sy/2), -sy/2)
1217                                 x2 = max(min(x2, sx/2), -sx/2)
1218                                 y2 = max(min(y2, sy/2), -sy/2)
1219                                 if (x & 1) == (y & 1):
1220                                         glColor4ub(5, 171, 231, 127)
1221                                 else:
1222                                         glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1223                                 glBegin(GL_QUADS)
1224                                 glVertex3f(x1, y1, -0.02)
1225                                 glVertex3f(x2, y1, -0.02)
1226                                 glVertex3f(x2, y2, -0.02)
1227                                 glVertex3f(x1, y2, -0.02)
1228                                 glEnd()
1229
1230                 glDisable(GL_BLEND)
1231                 glDisable(GL_CULL_FACE)
1232
1233         def _generateGCodeVBOs(self, layer):
1234                 ret = []
1235                 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1236                         if ':' in extrudeType:
1237                                 extruder = int(extrudeType[extrudeType.find(':')+1:])
1238                                 extrudeType = extrudeType[0:extrudeType.find(':')]
1239                         else:
1240                                 extruder = None
1241                         pointList = numpy.zeros((0,3), numpy.float32)
1242                         for path in layer:
1243                                 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1244                                         a = path['points']
1245                                         a = numpy.concatenate((a[:-1], a[1:]), 1)
1246                                         a = a.reshape((len(a) * 2, 3))
1247                                         pointList = numpy.concatenate((pointList, a))
1248                         ret.append(opengl.GLVBO(pointList))
1249                 return ret
1250
1251         def _generateGCodeVBOs2(self, layer):
1252                 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1253                 filamentArea = math.pi * filamentRadius * filamentRadius
1254                 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1255
1256                 ret = []
1257                 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1258                         if ':' in extrudeType:
1259                                 extruder = int(extrudeType[extrudeType.find(':')+1:])
1260                                 extrudeType = extrudeType[0:extrudeType.find(':')]
1261                         else:
1262                                 extruder = None
1263                         pointList = numpy.zeros((0,3), numpy.float32)
1264                         for path in layer:
1265                                 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1266                                         a = path['points']
1267                                         if extrudeType == 'FILL':
1268                                                 a[:,2] += 0.01
1269
1270                                         normal = a[1:] - a[:-1]
1271                                         lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1272                                         normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1273                                         normal[:,2] /= lens
1274
1275                                         ePerDist = path['extrusion'][1:] / lens
1276                                         if useFilamentArea:
1277                                                 lineWidth = ePerDist / path['layerThickness'] / 2.0
1278                                         else:
1279                                                 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1280
1281                                         normal[:,0] *= lineWidth
1282                                         normal[:,1] *= lineWidth
1283
1284                                         b = numpy.zeros((len(a)-1, 0), numpy.float32)
1285                                         b = numpy.concatenate((b, a[1:] + normal), 1)
1286                                         b = numpy.concatenate((b, a[1:] - normal), 1)
1287                                         b = numpy.concatenate((b, a[:-1] - normal), 1)
1288                                         b = numpy.concatenate((b, a[:-1] + normal), 1)
1289                                         b = b.reshape((len(b) * 4, 3))
1290
1291                                         if len(a) > 2:
1292                                                 normal2 = normal[:-1] + normal[1:]
1293                                                 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1294                                                 normal2[:,0] /= lens2
1295                                                 normal2[:,1] /= lens2
1296                                                 normal2[:,0] *= lineWidth[:-1]
1297                                                 normal2[:,1] *= lineWidth[:-1]
1298
1299                                                 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1300                                                 c = numpy.concatenate((c, a[1:-1]), 1)
1301                                                 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1302                                                 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1303                                                 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1304
1305                                                 c = numpy.concatenate((c, a[1:-1]), 1)
1306                                                 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1307                                                 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1308                                                 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1309
1310                                                 c = c.reshape((len(c) * 8, 3))
1311
1312                                                 pointList = numpy.concatenate((pointList, b, c))
1313                                         else:
1314                                                 pointList = numpy.concatenate((pointList, b))
1315                         ret.append(opengl.GLVBO(pointList))
1316
1317                 pointList = numpy.zeros((0,3), numpy.float32)
1318                 for path in layer:
1319                         if path['type'] == 'move':
1320                                 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1321                                 a = numpy.concatenate((a[:-1], a[1:]), 1)
1322                                 a = a.reshape((len(a) * 2, 3))
1323                                 pointList = numpy.concatenate((pointList, a))
1324                         if path['type'] == 'retract':
1325                                 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1326                                 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1327                                 a = a.reshape((len(a) * 2, 3))
1328                                 pointList = numpy.concatenate((pointList, a))
1329                 ret.append(opengl.GLVBO(pointList))
1330
1331                 return ret
1332
1333         def getObjectCenterPos(self):
1334                 if self._selectedObj is None:
1335                         return [0.0, 0.0, 0.0]
1336                 pos = self._selectedObj.getPosition()
1337                 size = self._selectedObj.getSize()
1338                 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1339
1340         def getObjectBoundaryCircle(self):
1341                 if self._selectedObj is None:
1342                         return 0.0
1343                 return self._selectedObj.getBoundaryCircle()
1344
1345         def getObjectSize(self):
1346                 if self._selectedObj is None:
1347                         return [0.0, 0.0, 0.0]
1348                 return self._selectedObj.getSize()
1349
1350         def getObjectMatrix(self):
1351                 if self._selectedObj is None:
1352                         return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1353                 return self._selectedObj.getMatrix()
1354
1355 class shaderEditor(wx.Dialog):
1356         def __init__(self, parent, callback, v, f):
1357                 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1358                 self._callback = callback
1359                 s = wx.BoxSizer(wx.VERTICAL)
1360                 self.SetSizer(s)
1361                 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1362                 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1363                 s.Add(self._vertex, 1, flag=wx.EXPAND)
1364                 s.Add(self._fragment, 1, flag=wx.EXPAND)
1365
1366                 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1367                 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1368
1369                 self.SetPosition(self.GetParent().GetPosition())
1370                 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1371                 self.Show()
1372
1373         def OnText(self, e):
1374                 self._callback(self._vertex.GetValue(), self._fragment.GetValue())