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