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