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