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