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