1 from __future__ import absolute_import
2 from __future__ import division
11 from wx import glcanvas
14 OpenGL.ERROR_CHECKING = False
15 from OpenGL.GLU import *
16 from OpenGL.GL import *
18 from Cura.util import profile
19 from Cura.util import gcodeInterpreter
20 from Cura.util import meshLoader
21 from Cura.util import util3d
22 from Cura.util import sliceRun
24 from Cura.gui.util import opengl
25 from Cura.gui.util import toolbarUtil
26 from Cura.gui.util import previewTools
27 from Cura.gui.util import openglGui
29 class previewObject():
33 self.displayList = None
36 class previewPanel(wx.Panel):
37 def __init__(self, parent):
38 super(previewPanel, self).__init__(parent,-1)
40 self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DDKSHADOW))
41 self.SetMinSize((440,320))
46 self.objectsMinV = None
47 self.objectsMaxV = None
48 self.objectsBoundaryCircleSize = None
49 self.loadThread = None
50 self.machineSize = util3d.Vector3(profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height'))
51 self.machineCenter = util3d.Vector3(self.machineSize.x / 2, self.machineSize.y / 2, 0)
53 self.glCanvas = PreviewGLCanvas(self)
54 #Create the popup window
55 self.warningPopup = wx.PopupWindow(self, flags=wx.BORDER_SIMPLE)
56 self.warningPopup.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_INFOBK))
57 self.warningPopup.text = wx.StaticText(self.warningPopup, -1, 'Reset scale, rotation and mirror?')
58 self.warningPopup.yesButton = wx.Button(self.warningPopup, -1, 'yes', style=wx.BU_EXACTFIT)
59 self.warningPopup.noButton = wx.Button(self.warningPopup, -1, 'no', style=wx.BU_EXACTFIT)
60 self.warningPopup.sizer = wx.BoxSizer(wx.HORIZONTAL)
61 self.warningPopup.SetSizer(self.warningPopup.sizer)
62 self.warningPopup.sizer.Add(self.warningPopup.text, 1, flag=wx.ALL|wx.ALIGN_CENTER_VERTICAL, border=1)
63 self.warningPopup.sizer.Add(self.warningPopup.yesButton, 0, flag=wx.EXPAND|wx.ALL, border=1)
64 self.warningPopup.sizer.Add(self.warningPopup.noButton, 0, flag=wx.EXPAND|wx.ALL, border=1)
65 self.warningPopup.Fit()
66 self.warningPopup.Layout()
67 self.warningPopup.timer = wx.Timer(self)
68 self.Bind(wx.EVT_TIMER, self.OnHideWarning, self.warningPopup.timer)
70 self.Bind(wx.EVT_BUTTON, self.OnWarningPopup, self.warningPopup.yesButton)
71 self.Bind(wx.EVT_BUTTON, self.OnHideWarning, self.warningPopup.noButton)
72 parent.Bind(wx.EVT_MOVE, self.OnMove)
73 parent.Bind(wx.EVT_SIZE, self.OnMove)
75 self.toolbar = toolbarUtil.Toolbar(self)
78 toolbarUtil.RadioButton(self.toolbar, group, 'object-3d-on.png', 'object-3d-off.png', '3D view', callback=self.On3DClick)
79 toolbarUtil.RadioButton(self.toolbar, group, 'object-top-on.png', 'object-top-off.png', 'Topdown view', callback=self.OnTopClick)
80 self.toolbar.AddSeparator()
82 self.showBorderButton = toolbarUtil.ToggleButton(self.toolbar, '', 'view-border-on.png', 'view-border-off.png', 'Show model borders', callback=self.OnViewChange)
83 self.showSteepOverhang = toolbarUtil.ToggleButton(self.toolbar, '', 'steepOverhang-on.png', 'steepOverhang-off.png', 'Show steep overhang', callback=self.OnViewChange)
84 self.toolbar.AddSeparator()
87 self.normalViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-normal-on.png', 'view-normal-off.png', 'Normal model view', callback=self.OnViewChange)
88 self.transparentViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-transparent-on.png', 'view-transparent-off.png', 'Transparent model view', callback=self.OnViewChange)
89 self.xrayViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-xray-on.png', 'view-xray-off.png', 'X-Ray view', callback=self.OnViewChange)
90 self.gcodeViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-gcode-on.png', 'view-gcode-off.png', 'GCode view', callback=self.OnViewChange)
91 self.mixedViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-mixed-on.png', 'view-mixed-off.png', 'Mixed model/GCode view', callback=self.OnViewChange)
92 self.toolbar.AddSeparator()
94 self.layerSpin = wx.SpinCtrl(self.toolbar, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS)
95 self.toolbar.AddControl(self.layerSpin)
96 self.Bind(wx.EVT_SPINCTRL, self.OnLayerNrChange, self.layerSpin)
100 sizer = wx.BoxSizer(wx.VERTICAL)
101 sizer.Add(self.toolbar, 0, flag=wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, border=1)
102 sizer.Add(self.glCanvas, 1, flag=wx.EXPAND)
105 self.checkReloadFileTimer = wx.Timer(self)
106 self.Bind(wx.EVT_TIMER, self.OnCheckReloadFile, self.checkReloadFileTimer)
107 self.checkReloadFileTimer.Start(1000)
109 self.infoToolButton = openglGui.glButton(self.glCanvas, 0, 'Info', 0,0, self.OnInfoSelect)
110 self.rotateToolButton = openglGui.glButton(self.glCanvas, 1, 'Rotate', 0,1, self.OnRotateSelect)
111 self.scaleToolButton = openglGui.glButton(self.glCanvas, 2, 'Scale', 0,2, self.OnScaleSelect)
113 self.resetRotationButton = openglGui.glButton(self.glCanvas, 4, 'Reset rotation', 1,0, self.OnRotateReset)
114 self.layFlatButton = openglGui.glButton(self.glCanvas, 5, 'Lay flat', 2,0, self.OnLayFlat)
116 self.resetScaleButton = openglGui.glButton(self.glCanvas, 8, 'Scale reset', 1,0, self.OnScaleReset)
117 self.scaleMaxButton = openglGui.glButton(self.glCanvas, 9, 'Scale to machine size', 2,0, self.OnScaleMax)
119 self.openFileButton = openglGui.glButton(self.glCanvas, 3, 'Load file', -1,0, lambda : self.GetParent().GetParent().GetParent()._showModelLoadDialog(1))
121 self.infoToolButton.setSelected(True)
122 self.returnToModelViewAndUpdateModel()
124 self.matrix = numpy.matrix(numpy.array(profile.getObjectMatrix(), numpy.float64).reshape((3,3,)))
125 self.tool = previewTools.toolInfo(self.glCanvas)
127 def returnToModelViewAndUpdateModel(self):
128 if self.glCanvas.viewMode == 'GCode' or self.glCanvas.viewMode == 'Mixed':
129 self.setViewMode('Normal')
130 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
131 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
132 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
133 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
134 self.updateModelTransform()
136 def OnInfoSelect(self):
137 self.infoToolButton.setSelected(True)
138 self.rotateToolButton.setSelected(False)
139 self.scaleToolButton.setSelected(False)
140 self.tool = previewTools.toolInfo(self.glCanvas)
141 self.returnToModelViewAndUpdateModel()
143 def OnRotateSelect(self):
144 self.infoToolButton.setSelected(False)
145 self.rotateToolButton.setSelected(True)
146 self.scaleToolButton.setSelected(False)
147 self.tool = previewTools.toolRotate(self.glCanvas)
148 self.returnToModelViewAndUpdateModel()
150 def OnScaleSelect(self):
151 self.infoToolButton.setSelected(False)
152 self.rotateToolButton.setSelected(False)
153 self.scaleToolButton.setSelected(True)
154 self.tool = previewTools.toolScale(self.glCanvas)
155 self.returnToModelViewAndUpdateModel()
157 def OnMove(self, e = None):
160 x, y = self.glCanvas.ClientToScreenXY(0, 0)
161 sx, sy = self.glCanvas.GetClientSizeTuple()
162 self.warningPopup.SetPosition((x, y+sy-self.warningPopup.GetSize().height))
164 def OnScaleMax(self, onlyScaleDown = False):
165 if self.objectsMinV is None:
167 vMin = self.objectsMinV
168 vMax = self.objectsMaxV
170 if profile.getProfileSettingFloat('skirt_line_count') > 0:
171 skirtSize = 3 + profile.getProfileSettingFloat('skirt_line_count') * profile.calculateEdgeWidth() + profile.getProfileSettingFloat('skirt_gap')
172 scaleX1 = (self.machineSize.x - self.machineCenter.x - skirtSize) / ((vMax[0] - vMin[0]) / 2)
173 scaleY1 = (self.machineSize.y - self.machineCenter.y - skirtSize) / ((vMax[1] - vMin[1]) / 2)
174 scaleX2 = (self.machineCenter.x - skirtSize) / ((vMax[0] - vMin[0]) / 2)
175 scaleY2 = (self.machineCenter.y - skirtSize) / ((vMax[1] - vMin[1]) / 2)
176 scaleZ = self.machineSize.z / (vMax[2] - vMin[2])
177 scale = min(scaleX1, scaleY1, scaleX2, scaleY2, scaleZ)
178 if scale > 1.0 and onlyScaleDown:
180 self.matrix *= numpy.matrix([[scale,0,0],[0,scale,0],[0,0,scale]], numpy.float64)
181 if self.glCanvas.viewMode == 'GCode' or self.glCanvas.viewMode == 'Mixed':
182 self.setViewMode('Normal')
183 self.updateModelTransform()
185 def OnRotateReset(self):
186 x = numpy.linalg.norm(self.matrix[0].getA().flatten())
187 y = numpy.linalg.norm(self.matrix[1].getA().flatten())
188 z = numpy.linalg.norm(self.matrix[2].getA().flatten())
189 self.matrix = numpy.matrix([[x,0,0],[0,y,0],[0,0,z]], numpy.float64)
190 self.updateModelTransform()
192 def OnScaleReset(self):
193 x = 1/numpy.linalg.norm(self.matrix[0].getA().flatten())
194 y = 1/numpy.linalg.norm(self.matrix[1].getA().flatten())
195 z = 1/numpy.linalg.norm(self.matrix[2].getA().flatten())
196 self.matrix *= numpy.matrix([[x,0,0],[0,y,0],[0,0,z]], numpy.float64)
197 self.updateModelTransform()
200 transformedVertexes = (numpy.matrix(self.objectList[0].mesh.vertexes, copy = False) * self.matrix).getA()
201 minZvertex = transformedVertexes[transformedVertexes.argmin(0)[2]]
204 for v in transformedVertexes:
205 diff = v - minZvertex
206 len = math.sqrt(diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2])
209 dot = (diff[2] / len)
215 rad = -math.atan2(dotV[1], dotV[0])
216 self.matrix *= numpy.matrix([[math.cos(rad), math.sin(rad), 0], [-math.sin(rad), math.cos(rad), 0], [0,0,1]], numpy.float64)
217 rad = -math.asin(dotMin)
218 self.matrix *= numpy.matrix([[math.cos(rad), 0, math.sin(rad)], [0,1,0], [-math.sin(rad), 0, math.cos(rad)]], numpy.float64)
221 transformedVertexes = (numpy.matrix(self.objectList[0].mesh.vertexes, copy = False) * self.matrix).getA()
222 minZvertex = transformedVertexes[transformedVertexes.argmin(0)[2]]
225 for v in transformedVertexes:
226 diff = v - minZvertex
227 len = math.sqrt(diff[1] * diff[1] + diff[2] * diff[2])
230 dot = (diff[2] / len)
237 rad = math.asin(dotMin)
239 rad = -math.asin(dotMin)
240 self.matrix *= numpy.matrix([[1,0,0], [0, math.cos(rad), math.sin(rad)], [0, -math.sin(rad), math.cos(rad)]], numpy.float64)
242 self.updateModelTransform()
245 self.glCanvas.yaw = 30
246 self.glCanvas.pitch = 60
247 self.glCanvas.zoom = 300
248 self.glCanvas.view3D = True
249 self.glCanvas.Refresh()
251 def OnTopClick(self):
252 self.glCanvas.view3D = False
253 self.glCanvas.zoom = 100
254 self.glCanvas.offsetX = 0
255 self.glCanvas.offsetY = 0
256 self.glCanvas.Refresh()
258 def OnLayerNrChange(self, e):
259 self.glCanvas.Refresh()
261 def setViewMode(self, mode):
263 self.normalViewButton.SetValue(True)
265 self.gcodeViewButton.SetValue(True)
266 self.glCanvas.viewMode = mode
267 wx.CallAfter(self.glCanvas.Refresh)
269 def loadModelFiles(self, filelist, showWarning = False):
270 while len(filelist) > len(self.objectList):
271 self.objectList.append(previewObject())
272 for idx in xrange(len(filelist), len(self.objectList)):
273 self.objectList[idx].mesh = None
274 self.objectList[idx].filename = None
275 for idx in xrange(0, len(filelist)):
276 obj = self.objectList[idx]
277 if obj.filename != filelist[idx]:
279 self.gcodeFileTime = None
280 self.logFileTime = None
281 obj.filename = filelist[idx]
283 self.gcodeFilename = sliceRun.getExportFilename(filelist[0])
284 #Do the STL file loading in a background thread so we don't block the UI.
285 if self.loadThread is not None and self.loadThread.isAlive():
286 self.loadThread.join()
287 self.loadThread = threading.Thread(target=self.doFileLoadThread)
288 self.loadThread.daemon = True
289 self.loadThread.start()
292 if (self.matrix - numpy.matrix([[1,0,0],[0,1,0],[0,0,1]], numpy.float64)).any() or len(profile.getPluginConfig()) > 0:
293 self.ShowWarningPopup('Reset scale, rotation, mirror and plugins?', self.OnResetAll)
295 def OnCheckReloadFile(self, e):
296 #Only show the reload popup when the window has focus, because the popup goes over other programs.
297 if self.GetParent().FindFocus() is None:
299 for obj in self.objectList:
300 if obj.filename is not None and os.path.isfile(obj.filename) and obj.fileTime != os.stat(obj.filename).st_mtime:
301 self.checkReloadFileTimer.Stop()
302 self.ShowWarningPopup('File changed, reload?', self.reloadModelFiles)
304 def reloadModelFiles(self, filelist = None):
305 if filelist is not None:
306 #Only load this again if the filename matches the file we have already loaded (for auto loading GCode after slicing)
307 for idx in xrange(0, len(filelist)):
308 if self.objectList[idx].filename != filelist[idx]:
312 for idx in xrange(0, len(self.objectList)):
313 filelist.append(self.objectList[idx].filename)
314 self.loadModelFiles(filelist)
317 def doFileLoadThread(self):
318 for obj in self.objectList:
319 if obj.filename is not None and os.path.isfile(obj.filename) and obj.fileTime != os.stat(obj.filename).st_mtime:
320 obj.fileTime = os.stat(obj.filename).st_mtime
321 mesh = meshLoader.loadMesh(obj.filename)
324 self.updateModelTransform()
325 self.OnScaleMax(True)
326 self.glCanvas.zoom = numpy.max(self.objectsSize) * 3.5
328 wx.CallAfter(self.updateToolbar)
329 wx.CallAfter(self.glCanvas.Refresh)
331 if os.path.isfile(self.gcodeFilename) and self.gcodeFileTime != os.stat(self.gcodeFilename).st_mtime:
332 self.gcodeFileTime = os.stat(self.gcodeFilename).st_mtime
333 gcode = gcodeInterpreter.gcode()
334 gcode.progressCallback = self.loadProgress
335 gcode.load(self.gcodeFilename)
336 self.gcodeDirty = False
338 self.gcodeDirty = True
341 for line in open(self.gcodeFilename, "rt"):
342 res = re.search(';Model error\(([a-z ]*)\): \(([0-9\.\-e]*), ([0-9\.\-e]*), ([0-9\.\-e]*)\) \(([0-9\.\-e]*), ([0-9\.\-e]*), ([0-9\.\-e]*)\)', line)
344 v1 = util3d.Vector3(float(res.group(2)), float(res.group(3)), float(res.group(4)))
345 v2 = util3d.Vector3(float(res.group(5)), float(res.group(6)), float(res.group(7)))
346 errorList.append([v1, v2])
347 self.errorList = errorList
349 wx.CallAfter(self.updateToolbar)
350 wx.CallAfter(self.glCanvas.Refresh)
351 elif not os.path.isfile(self.gcodeFilename):
353 wx.CallAfter(self.checkReloadFileTimer.Start, 1000)
355 def loadProgress(self, progress):
358 def OnResetAll(self, e = None):
359 profile.putProfileSetting('model_matrix', '1,0,0,0,1,0,0,0,1')
360 profile.setPluginConfig([])
361 self.updateProfileToControls()
363 def ShowWarningPopup(self, text, callback = None):
364 self.warningPopup.text.SetLabel(text)
365 self.warningPopup.callback = callback
367 self.warningPopup.yesButton.Show(False)
368 self.warningPopup.noButton.SetLabel('ok')
370 self.warningPopup.yesButton.Show(True)
371 self.warningPopup.noButton.SetLabel('no')
372 self.warningPopup.Fit()
373 self.warningPopup.Layout()
375 self.warningPopup.Show(True)
376 self.warningPopup.timer.Start(5000)
378 def OnWarningPopup(self, e):
379 self.warningPopup.Show(False)
380 self.warningPopup.timer.Stop()
381 self.warningPopup.callback()
383 def OnHideWarning(self, e):
384 self.warningPopup.Show(False)
385 self.warningPopup.timer.Stop()
387 def updateToolbar(self):
388 self.gcodeViewButton.Show(self.gcode != None)
389 self.mixedViewButton.Show(self.gcode != None)
390 self.layerSpin.Show(self.glCanvas.viewMode == "GCode" or self.glCanvas.viewMode == "Mixed")
391 if self.gcode != None:
392 self.layerSpin.SetRange(1, len(self.gcode.layerList) - 1)
393 self.toolbar.Realize()
396 def OnViewChange(self):
397 if self.normalViewButton.GetValue():
398 self.glCanvas.viewMode = "Normal"
399 elif self.transparentViewButton.GetValue():
400 self.glCanvas.viewMode = "Transparent"
401 elif self.xrayViewButton.GetValue():
402 self.glCanvas.viewMode = "X-Ray"
403 elif self.gcodeViewButton.GetValue():
404 self.glCanvas.viewMode = "GCode"
405 elif self.mixedViewButton.GetValue():
406 self.glCanvas.viewMode = "Mixed"
407 self.glCanvas.drawBorders = self.showBorderButton.GetValue()
408 self.glCanvas.drawSteepOverhang = self.showSteepOverhang.GetValue()
410 self.glCanvas.Refresh()
412 def updateModelTransform(self, f=0):
413 if len(self.objectList) < 1 or self.objectList[0].mesh is None:
416 profile.putProfileSetting('model_matrix', ','.join(map(str, list(self.matrix.getA().flatten()))))
417 for obj in self.objectList:
420 obj.mesh.matrix = self.matrix
421 obj.mesh.processMatrix()
423 minV = self.objectList[0].mesh.getMinimum()
424 maxV = self.objectList[0].mesh.getMaximum()
425 objectsBoundaryCircleSize = self.objectList[0].mesh.bounderyCircleSize
426 for obj in self.objectList:
430 minV = numpy.minimum(minV, obj.mesh.getMinimum())
431 maxV = numpy.maximum(maxV, obj.mesh.getMaximum())
432 objectsBoundaryCircleSize = max(objectsBoundaryCircleSize, obj.mesh.bounderyCircleSize)
434 self.objectsMaxV = maxV
435 self.objectsMinV = minV
436 self.objectsSize = self.objectsMaxV - self.objectsMinV
437 self.objectsBoundaryCircleSize = objectsBoundaryCircleSize
439 self.glCanvas.Refresh()
441 def updateProfileToControls(self):
442 self.matrix = numpy.matrix(numpy.array(profile.getObjectMatrix(), numpy.float64).reshape((3,3,)))
443 self.updateModelTransform()
444 self.glCanvas.updateProfileToControls()
446 class PreviewGLCanvas(openglGui.glGuiPanel):
447 def __init__(self, parent):
448 super(PreviewGLCanvas, self).__init__(parent)
449 wx.EVT_MOUSEWHEEL(self, self.OnMouseWheel)
457 self.gcodeDisplayList = None
458 self.gcodeDisplayListMade = None
459 self.gcodeDisplayListCount = 0
460 self.objColor = [[1.0, 0.8, 0.6, 1.0], [0.2, 1.0, 0.1, 1.0], [1.0, 0.2, 0.1, 1.0], [0.1, 0.2, 1.0, 1.0]]
464 self.tempMatrix = None
467 def updateProfileToControls(self):
468 self.objColor[0] = profile.getPreferenceColour('model_colour')
469 self.objColor[1] = profile.getPreferenceColour('model_colour2')
470 self.objColor[2] = profile.getPreferenceColour('model_colour3')
471 self.objColor[3] = profile.getPreferenceColour('model_colour4')
473 def OnMouseMotion(self,e):
474 if self.parent.objectsMaxV is not None and self.viewport is not None and self.viewMode != 'GCode' and self.viewMode != 'Mixed':
475 p0 = opengl.unproject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 0, self.modelMatrix, self.projMatrix, self.viewport)
476 p1 = opengl.unproject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 1, self.modelMatrix, self.projMatrix, self.viewport)
477 if not e.Dragging() or self.dragType != 'tool':
478 self.parent.tool.OnMouseMove(p0, p1)
480 if e.Dragging() and e.LeftIsDown():
481 if self.dragType == '':
482 #Define the drag type depending on the cursor position.
483 self.dragType = 'viewRotate'
484 if self.viewMode != 'GCode' and self.viewMode != 'Mixed':
485 if self.parent.tool.OnDragStart(p0, p1):
486 self.dragType = 'tool'
488 if self.dragType == 'viewRotate':
490 self.yaw += e.GetX() - self.oldX
491 self.pitch -= e.GetY() - self.oldY
497 self.offsetX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2
498 self.offsetY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2
499 elif self.dragType == 'tool':
500 self.parent.tool.OnDrag(p0, p1)
502 #Workaround for buggy ATI cards.
503 size = self.GetSizeTuple()
504 self.SetSize((size[0]+1, size[1]))
505 self.SetSize((size[0], size[1]))
508 if self.dragType != '':
509 if self.tempMatrix is not None:
510 self.parent.matrix *= self.tempMatrix
511 self.parent.updateModelTransform()
512 self.tempMatrix = None
513 self.parent.tool.OnDragEnd()
517 if e.Dragging() and e.RightIsDown():
518 self.zoom += e.GetY() - self.oldY
526 def getObjectBoundaryCircle(self):
527 return self.parent.objectsBoundaryCircleSize
529 def getObjectSize(self):
530 return self.parent.objectsSize
532 def OnMouseWheel(self,e):
533 self.zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0
541 opengl.InitGL(self, self.view3D, self.zoom)
543 glTranslate(0,0,-self.zoom)
544 glRotate(-self.pitch, 1,0,0)
545 glRotate(self.yaw, 0,0,1)
546 if self.viewMode == "GCode" or self.viewMode == "Mixed":
547 if self.parent.gcode is not None and len(self.parent.gcode.layerList) > self.parent.layerSpin.GetValue() and len(self.parent.gcode.layerList[self.parent.layerSpin.GetValue()]) > 0:
548 glTranslate(0,0,-self.parent.gcode.layerList[self.parent.layerSpin.GetValue()][0].list[-1].z)
550 if self.parent.objectsMaxV is not None:
551 glTranslate(0,0,-self.parent.objectsSize[2] / 2)
553 glTranslate(self.offsetX, self.offsetY, 0)
555 self.viewport = glGetIntegerv(GL_VIEWPORT)
556 self.modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
557 self.projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
559 glTranslate(-self.parent.machineCenter.x, -self.parent.machineCenter.y, 0)
564 machineSize = self.parent.machineSize
566 if self.parent.gcode is not None and self.parent.gcodeDirty:
567 if self.gcodeDisplayListCount < len(self.parent.gcode.layerList) or self.gcodeDisplayList is None:
568 if self.gcodeDisplayList is not None:
569 glDeleteLists(self.gcodeDisplayList, self.gcodeDisplayListCount)
570 self.gcodeDisplayList = glGenLists(len(self.parent.gcode.layerList))
571 self.gcodeDisplayListCount = len(self.parent.gcode.layerList)
572 self.parent.gcodeDirty = False
573 self.gcodeDisplayListMade = 0
575 if self.parent.gcode is not None and self.gcodeDisplayListMade < len(self.parent.gcode.layerList):
576 glNewList(self.gcodeDisplayList + self.gcodeDisplayListMade, GL_COMPILE)
577 opengl.DrawGCodeLayer(self.parent.gcode.layerList[self.gcodeDisplayListMade])
579 self.gcodeDisplayListMade += 1
580 wx.CallAfter(self.Refresh)
583 glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)
584 for obj in self.parent.objectList:
587 if obj.displayList is None:
588 obj.displayList = glGenLists(1)
589 obj.steepDisplayList = glGenLists(1)
590 obj.outlineDisplayList = glGenLists(1)
593 glNewList(obj.displayList, GL_COMPILE)
594 opengl.DrawMesh(obj.mesh)
596 glNewList(obj.steepDisplayList, GL_COMPILE)
597 opengl.DrawMeshSteep(obj.mesh, 60)
599 glNewList(obj.outlineDisplayList, GL_COMPILE)
600 opengl.DrawMeshOutline(obj.mesh)
603 if self.viewMode == "Mixed":
605 glColor3f(0.0,0.0,0.0)
606 self.drawModel(obj.displayList)
607 glColor3f(1.0,1.0,1.0)
608 glClear(GL_DEPTH_BUFFER_BIT)
612 if self.parent.gcode is not None and (self.viewMode == "GCode" or self.viewMode == "Mixed"):
614 if profile.getPreference('machine_center_is_zero') == 'True':
615 glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)
616 glEnable(GL_COLOR_MATERIAL)
617 glEnable(GL_LIGHTING)
618 drawUpToLayer = min(self.gcodeDisplayListMade, self.parent.layerSpin.GetValue() + 1)
619 starttime = time.time()
620 for i in xrange(drawUpToLayer - 1, -1, -1):
622 if i < self.parent.layerSpin.GetValue():
623 c = 0.9 - (drawUpToLayer - i) * 0.1
628 glLightfv(GL_LIGHT0, GL_DIFFUSE, [0,0,0,0])
629 glLightfv(GL_LIGHT0, GL_AMBIENT, [c,c,c,c])
630 glCallList(self.gcodeDisplayList + i)
631 if time.time() - starttime > 0.1:
634 glDisable(GL_LIGHTING)
635 glDisable(GL_COLOR_MATERIAL)
636 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, [0.2, 0.2, 0.2, 1.0]);
637 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, [0.8, 0.8, 0.8, 1.0]);
640 glColor3f(1.0,1.0,1.0)
642 glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)
643 for obj in self.parent.objectList:
647 if self.viewMode == "Transparent" or self.viewMode == "Mixed":
648 glLightfv(GL_LIGHT0, GL_DIFFUSE, map(lambda x: x / 2, self.objColor[self.parent.objectList.index(obj)]))
649 glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x / 10, self.objColor[self.parent.objectList.index(obj)]))
650 #If we want transparent, then first render a solid black model to remove the printer size lines.
651 if self.viewMode != "Mixed":
653 glColor3f(0.0,0.0,0.0)
654 self.drawModel(obj.displayList)
655 glColor3f(1.0,1.0,1.0)
656 #After the black model is rendered, render the model again but now with lighting and no depth testing.
657 glDisable(GL_DEPTH_TEST)
658 glEnable(GL_LIGHTING)
660 glBlendFunc(GL_ONE, GL_ONE)
661 glEnable(GL_LIGHTING)
662 self.drawModel(obj.displayList)
663 glEnable(GL_DEPTH_TEST)
664 elif self.viewMode == "X-Ray":
665 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
666 glDisable(GL_LIGHTING)
667 glDisable(GL_DEPTH_TEST)
668 glEnable(GL_STENCIL_TEST)
669 glStencilFunc(GL_ALWAYS, 1, 1)
670 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
671 self.drawModel(obj.displayList)
672 glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
674 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
675 glStencilFunc(GL_EQUAL, 0, 1)
677 self.drawModel(obj.displayList)
678 glStencilFunc(GL_EQUAL, 1, 1)
680 self.drawModel(obj.displayList)
684 for i in xrange(2, 15, 2):
685 glStencilFunc(GL_EQUAL, i, 0xFF);
686 glColor(float(i)/10, float(i)/10, float(i)/5)
688 glVertex3f(-1000,-1000,-1)
689 glVertex3f( 1000,-1000,-1)
690 glVertex3f( 1000, 1000,-1)
691 glVertex3f(-1000, 1000,-1)
693 for i in xrange(1, 15, 2):
694 glStencilFunc(GL_EQUAL, i, 0xFF);
695 glColor(float(i)/10, 0, 0)
697 glVertex3f(-1000,-1000,-1)
698 glVertex3f( 1000,-1000,-1)
699 glVertex3f( 1000, 1000,-1)
700 glVertex3f(-1000, 1000,-1)
704 glDisable(GL_STENCIL_TEST)
705 glEnable(GL_DEPTH_TEST)
707 #Fix the depth buffer
708 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
709 self.drawModel(obj.displayList)
710 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
711 elif self.viewMode == "Normal":
712 glLightfv(GL_LIGHT0, GL_DIFFUSE, self.objColor[self.parent.objectList.index(obj)])
713 glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x * 0.4, self.objColor[self.parent.objectList.index(obj)]))
714 glEnable(GL_LIGHTING)
715 self.drawModel(obj.displayList)
717 if self.drawBorders and (self.viewMode == "Normal" or self.viewMode == "Transparent" or self.viewMode == "X-Ray"):
718 glEnable(GL_DEPTH_TEST)
719 glDisable(GL_LIGHTING)
721 self.drawModel(obj.outlineDisplayList)
723 if self.drawSteepOverhang:
724 glDisable(GL_LIGHTING)
726 self.drawModel(obj.steepDisplayList)
729 #if self.viewMode == "Normal" or self.viewMode == "Transparent" or self.viewMode == "X-Ray":
730 # glDisable(GL_LIGHTING)
731 # glDisable(GL_DEPTH_TEST)
732 # glDisable(GL_BLEND)
735 # for err in self.parent.errorList:
736 # glVertex3f(err[0].x, err[0].y, err[0].z)
737 # glVertex3f(err[1].x, err[1].y, err[1].z)
739 # glEnable(GL_DEPTH_TEST)
741 opengl.DrawMachine(machineSize)
743 #Draw the current selected tool
744 if self.parent.objectsMaxV is not None and self.viewMode != 'GCode' and self.viewMode != 'Mixed':
746 glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, self.parent.objectsSize[2]/2)
747 self.parent.tool.OnDraw()
750 def drawModel(self, displayList):
751 vMin = self.parent.objectsMinV
752 vMax = self.parent.objectsMaxV
753 offset = - vMin - (vMax - vMin) / 2
755 matrix = opengl.convert3x3MatrixTo4x4(self.parent.matrix)
758 glTranslate(0, 0, self.parent.objectsSize[2]/2)
759 if self.tempMatrix is not None:
760 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
761 glMultMatrixf(tempMatrix)
762 glTranslate(0, 0, -self.parent.objectsSize[2]/2)
763 glTranslate(offset[0], offset[1], -vMin[2])
764 glMultMatrixf(matrix)
765 glCallList(displayList)