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