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