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