1 from __future__ import division
\r
3 import sys, math, threading, re, time, os
\r
5 from wx import glcanvas
\r
9 OpenGL.ERROR_CHECKING = False
\r
10 from OpenGL.GLU import *
\r
11 from OpenGL.GL import *
\r
12 hasOpenGLlibs = True
\r
14 print "Failed to find PyOpenGL: http://pyopengl.sourceforge.net/"
\r
15 hasOpenGLlibs = False
\r
17 from gui import opengl
\r
18 from gui import toolbarUtil
\r
20 from util import profile
\r
21 from util import gcodeInterpreter
\r
22 from util import stl
\r
23 from util import util3d
\r
25 class previewObject():
\r
28 self.filename = None
\r
29 self.displayList = None
\r
32 class previewPanel(wx.Panel):
\r
33 def __init__(self, parent):
\r
34 super(previewPanel, self).__init__(parent,-1)
\r
36 self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DDKSHADOW))
\r
37 self.SetMinSize((440,320))
\r
39 self.glCanvas = PreviewGLCanvas(self)
\r
40 self.objectList = []
\r
43 self.objectsMinV = None
\r
44 self.objectsMaxV = None
\r
45 self.loadThread = None
\r
46 self.machineSize = util3d.Vector3(profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height'))
\r
47 self.machineCenter = util3d.Vector3(float(profile.getProfileSetting('machine_center_x')), float(profile.getProfileSetting('machine_center_y')), 0)
\r
49 self.toolbar = toolbarUtil.Toolbar(self)
\r
52 toolbarUtil.RadioButton(self.toolbar, group, 'object-3d-on.png', 'object-3d-off.png', '3D view', callback=self.On3DClick)
\r
53 toolbarUtil.RadioButton(self.toolbar, group, 'object-top-on.png', 'object-top-off.png', 'Topdown view', callback=self.OnTopClick)
\r
54 self.toolbar.AddSeparator()
\r
57 self.normalViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-normal-on.png', 'view-normal-off.png', 'Normal model view', callback=self.OnViewChange)
\r
58 self.transparentViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-transparent-on.png', 'view-transparent-off.png', 'Transparent model view', callback=self.OnViewChange)
\r
59 self.xrayViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-xray-on.png', 'view-xray-off.png', 'X-Ray view', callback=self.OnViewChange)
\r
60 self.gcodeViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-gcode-on.png', 'view-gcode-off.png', 'GCode view', callback=self.OnViewChange)
\r
61 self.mixedViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-mixed-on.png', 'view-mixed-off.png', 'Mixed model/GCode view', callback=self.OnViewChange)
\r
62 self.toolbar.AddSeparator()
\r
64 self.layerSpin = wx.SpinCtrl(self.toolbar, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS)
\r
65 self.toolbar.AddControl(self.layerSpin)
\r
66 self.Bind(wx.EVT_SPINCTRL, self.OnLayerNrChange, self.layerSpin)
\r
68 self.toolbar2 = toolbarUtil.Toolbar(self)
\r
71 self.mirrorX = toolbarUtil.ToggleButton(self.toolbar2, 'flip_x', 'object-mirror-x-on.png', 'object-mirror-x-off.png', 'Mirror X', callback=self.updateModelTransform)
\r
72 self.mirrorY = toolbarUtil.ToggleButton(self.toolbar2, 'flip_y', 'object-mirror-y-on.png', 'object-mirror-y-off.png', 'Mirror Y', callback=self.updateModelTransform)
\r
73 self.mirrorZ = toolbarUtil.ToggleButton(self.toolbar2, 'flip_z', 'object-mirror-z-on.png', 'object-mirror-z-off.png', 'Mirror Z', callback=self.updateModelTransform)
\r
74 self.toolbar2.AddSeparator()
\r
77 self.swapXZ = toolbarUtil.ToggleButton(self.toolbar2, 'swap_xz', 'object-swap-xz-on.png', 'object-swap-xz-off.png', 'Swap XZ', callback=self.updateModelTransform)
\r
78 self.swapYZ = toolbarUtil.ToggleButton(self.toolbar2, 'swap_yz', 'object-swap-yz-on.png', 'object-swap-yz-off.png', 'Swap YZ', callback=self.updateModelTransform)
\r
79 self.toolbar2.AddSeparator()
\r
82 self.scaleReset = toolbarUtil.NormalButton(self.toolbar2, self.OnScaleReset, 'object-scale.png', 'Reset model scale')
\r
83 self.scale = wx.TextCtrl(self.toolbar2, -1, profile.getProfileSetting('model_scale'), size=(21*2,21))
\r
84 self.toolbar2.AddControl(self.scale)
\r
85 self.scale.Bind(wx.EVT_TEXT, self.OnScale)
\r
86 self.scaleMax = toolbarUtil.NormalButton(self.toolbar2, self.OnScaleMax, 'object-max-size.png', 'Scale object to fix machine size')
\r
88 self.toolbar2.AddSeparator()
\r
91 #self.mulXadd = toolbarUtil.NormalButton(self.toolbar2, self.OnMulXAddClick, 'object-mul-x-add.png', 'Increase number of models on X axis')
\r
92 #self.mulXsub = toolbarUtil.NormalButton(self.toolbar2, self.OnMulXSubClick, 'object-mul-x-sub.png', 'Decrease number of models on X axis')
\r
93 #self.mulYadd = toolbarUtil.NormalButton(self.toolbar2, self.OnMulYAddClick, 'object-mul-y-add.png', 'Increase number of models on Y axis')
\r
94 #self.mulYsub = toolbarUtil.NormalButton(self.toolbar2, self.OnMulYSubClick, 'object-mul-y-sub.png', 'Decrease number of models on Y axis')
\r
95 #self.toolbar2.AddSeparator()
\r
98 self.rotateReset = toolbarUtil.NormalButton(self.toolbar2, self.OnRotateReset, 'object-rotate.png', 'Reset model rotation')
\r
99 self.rotate = wx.SpinCtrl(self.toolbar2, -1, profile.getProfileSetting('model_rotate_base'), size=(21*3,21), style=wx.SP_WRAP|wx.SP_ARROW_KEYS)
\r
100 self.rotate.SetRange(0, 360)
\r
101 self.rotate.Bind(wx.EVT_TEXT, self.OnRotate)
\r
102 self.toolbar2.AddControl(self.rotate)
\r
104 self.toolbar2.Realize()
\r
105 self.OnViewChange()
\r
107 sizer = wx.BoxSizer(wx.VERTICAL)
\r
108 sizer.Add(self.toolbar, 0, flag=wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, border=1)
\r
109 sizer.Add(self.glCanvas, 1, flag=wx.EXPAND)
\r
110 sizer.Add(self.toolbar2, 0, flag=wx.EXPAND|wx.BOTTOM|wx.LEFT|wx.RIGHT, border=1)
\r
111 self.SetSizer(sizer)
\r
113 def OnMulXAddClick(self, e):
\r
114 profile.putProfileSetting('model_multiply_x', str(max(1, int(profile.getProfileSetting('model_multiply_x'))+1)))
\r
115 self.glCanvas.Refresh()
\r
117 def OnMulXSubClick(self, e):
\r
118 profile.putProfileSetting('model_multiply_x', str(max(1, int(profile.getProfileSetting('model_multiply_x'))-1)))
\r
119 self.glCanvas.Refresh()
\r
121 def OnMulYAddClick(self, e):
\r
122 profile.putProfileSetting('model_multiply_y', str(max(1, int(profile.getProfileSetting('model_multiply_y'))+1)))
\r
123 self.glCanvas.Refresh()
\r
125 def OnMulYSubClick(self, e):
\r
126 profile.putProfileSetting('model_multiply_y', str(max(1, int(profile.getProfileSetting('model_multiply_y'))-1)))
\r
127 self.glCanvas.Refresh()
\r
129 def OnScaleReset(self, e):
\r
130 self.scale.SetValue('1.0')
\r
133 def OnScale(self, e):
\r
135 if self.scale.GetValue() != '':
\r
136 scale = self.scale.GetValue()
\r
137 profile.putProfileSetting('model_scale', scale)
\r
138 self.glCanvas.Refresh()
\r
140 def OnScaleMax(self, e):
\r
141 if self.objectsMinV == None:
\r
143 vMin = self.objectsMinV
\r
144 vMax = self.objectsMaxV
\r
145 scaleX1 = (self.machineSize.x - self.machineCenter.x) / ((vMax.x - vMin.x) / 2)
\r
146 scaleY1 = (self.machineSize.y - self.machineCenter.y) / ((vMax.y - vMin.y) / 2)
\r
147 scaleX2 = (self.machineCenter.x) / ((vMax.x - vMin.x) / 2)
\r
148 scaleY2 = (self.machineCenter.y) / ((vMax.y - vMin.y) / 2)
\r
149 scaleZ = self.machineSize.z / (vMax.z - vMin.z)
\r
150 scale = min(scaleX1, scaleY1, scaleX2, scaleY2, scaleZ)
\r
151 self.scale.SetValue(str(scale))
\r
152 profile.putProfileSetting('model_scale', self.scale.GetValue())
\r
153 self.glCanvas.Refresh()
\r
155 def OnRotateReset(self, e):
\r
156 self.rotate.SetValue(0)
\r
157 self.OnRotate(None)
\r
159 def OnRotate(self, e):
\r
160 profile.putProfileSetting('model_rotate_base', self.rotate.GetValue())
\r
161 self.updateModelTransform()
\r
163 def On3DClick(self):
\r
164 self.glCanvas.yaw = 30
\r
165 self.glCanvas.pitch = 60
\r
166 self.glCanvas.zoom = 300
\r
167 self.glCanvas.view3D = True
\r
168 self.glCanvas.Refresh()
\r
170 def OnTopClick(self):
\r
171 self.glCanvas.view3D = False
\r
172 self.glCanvas.zoom = 100
\r
173 self.glCanvas.offsetX = 0
\r
174 self.glCanvas.offsetY = 0
\r
175 self.glCanvas.Refresh()
\r
177 def OnLayerNrChange(self, e):
\r
178 self.glCanvas.Refresh()
\r
180 def updateCenterX(self):
\r
181 self.machineCenter.x = profile.getProfileSettingFloat('machine_center_x')
\r
182 self.glCanvas.Refresh()
\r
184 def updateCenterY(self):
\r
185 self.machineCenter.y = profile.getProfileSettingFloat('machine_center_y')
\r
186 self.glCanvas.Refresh()
\r
188 def setViewMode(self, mode):
\r
189 if mode == "Normal":
\r
190 self.normalViewButton.SetValue(True)
\r
191 if mode == "GCode":
\r
192 self.gcodeViewButton.SetValue(True)
\r
193 self.glCanvas.viewMode = mode
\r
194 wx.CallAfter(self.glCanvas.Refresh)
\r
196 def loadModelFiles(self, filelist):
\r
197 while len(filelist) > len(self.objectList):
\r
198 self.objectList.append(previewObject())
\r
199 for idx in xrange(len(filelist), len(self.objectList)):
\r
200 self.objectList[idx].mesh = None
\r
201 self.objectList[idx].filename = None
\r
202 for idx in xrange(0, len(filelist)):
\r
203 obj = self.objectList[idx]
\r
204 if obj.filename != filelist[idx]:
\r
205 obj.fileTime = None
\r
206 self.gcodeFileTime = None
\r
207 self.logFileTime = None
\r
208 obj.filename = filelist[idx]
\r
210 self.gcodeFilename = filelist[0][: filelist[0].rfind('.')] + "_export.gcode"
\r
211 self.logFilename = filelist[0][: filelist[0].rfind('.')] + "_export.log"
\r
212 #Do the STL file loading in a background thread so we don't block the UI.
\r
213 if self.loadThread != None and self.loadThread.isAlive():
\r
214 self.loadThread.join()
\r
215 self.loadThread = threading.Thread(target=self.doFileLoadThread)
\r
216 self.loadThread.daemon = True
\r
217 self.loadThread.start()
\r
219 def loadReModelFiles(self, filelist):
\r
220 #Only load this again if the filename matches the file we have already loaded (for auto loading GCode after slicing)
\r
221 for idx in xrange(0, len(filelist)):
\r
222 if self.objectList[idx].filename != filelist[idx]:
\r
224 self.loadModelFiles(filelist)
\r
227 def doFileLoadThread(self):
\r
228 for obj in self.objectList:
\r
229 if obj.filename != None and os.path.isfile(obj.filename) and obj.fileTime != os.stat(obj.filename).st_mtime:
\r
230 obj.ileTime = os.stat(obj.filename).st_mtime
\r
231 mesh = stl.stlModel()
\r
232 mesh.load(obj.filename)
\r
235 self.updateModelTransform()
\r
236 wx.CallAfter(self.updateToolbar)
\r
237 wx.CallAfter(self.glCanvas.Refresh)
\r
239 if os.path.isfile(self.gcodeFilename) and self.gcodeFileTime != os.stat(self.gcodeFilename).st_mtime:
\r
240 self.gcodeFileTime = os.stat(self.gcodeFilename).st_mtime
\r
241 gcode = gcodeInterpreter.gcode()
\r
242 gcode.progressCallback = self.loadProgress
\r
243 gcode.load(self.gcodeFilename)
\r
244 self.gcodeDirty = False
\r
245 self.errorList = []
\r
247 self.gcodeDirty = True
\r
248 wx.CallAfter(self.updateToolbar)
\r
249 wx.CallAfter(self.glCanvas.Refresh)
\r
250 elif not os.path.isfile(self.gcodeFilename):
\r
253 if os.path.isfile(self.logFilename):
\r
255 for line in open(self.logFilename, "rt"):
\r
256 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)
\r
258 v1 = util3d.Vector3(float(res.group(2)), float(res.group(3)), float(res.group(4)))
\r
259 v2 = util3d.Vector3(float(res.group(5)), float(res.group(6)), float(res.group(7)))
\r
260 errorList.append([v1, v2])
\r
261 self.errorList = errorList
\r
262 wx.CallAfter(self.glCanvas.Refresh)
\r
264 def loadProgress(self, progress):
\r
267 def updateToolbar(self):
\r
268 self.gcodeViewButton.Show(self.gcode != None)
\r
269 self.mixedViewButton.Show(self.gcode != None)
\r
270 self.layerSpin.Show(self.glCanvas.viewMode == "GCode" or self.glCanvas.viewMode == "Mixed")
\r
271 if self.gcode != None:
\r
272 self.layerSpin.SetRange(1, len(self.gcode.layerList) - 1)
\r
273 self.toolbar.Realize()
\r
275 def OnViewChange(self):
\r
276 if self.normalViewButton.GetValue():
\r
277 self.glCanvas.viewMode = "Normal"
\r
278 elif self.transparentViewButton.GetValue():
\r
279 self.glCanvas.viewMode = "Transparent"
\r
280 elif self.xrayViewButton.GetValue():
\r
281 self.glCanvas.viewMode = "X-Ray"
\r
282 elif self.gcodeViewButton.GetValue():
\r
283 self.glCanvas.viewMode = "GCode"
\r
284 elif self.mixedViewButton.GetValue():
\r
285 self.glCanvas.viewMode = "Mixed"
\r
286 self.updateToolbar()
\r
287 self.glCanvas.Refresh()
\r
289 def updateModelTransform(self, f=0):
\r
290 rotate = profile.getProfileSettingFloat('model_rotate_base') / 180.0 * math.pi
\r
294 if profile.getProfileSetting('flip_x') == 'True':
\r
296 if profile.getProfileSetting('flip_y') == 'True':
\r
298 if profile.getProfileSetting('flip_z') == 'True':
\r
300 swapXZ = profile.getProfileSetting('swap_xz') == 'True'
\r
301 swapYZ = profile.getProfileSetting('swap_yz') == 'True'
\r
302 mat00 = math.cos(rotate) * scaleX
\r
303 mat01 =-math.sin(rotate) * scaleY
\r
304 mat10 = math.sin(rotate) * scaleX
\r
305 mat11 = math.cos(rotate) * scaleY
\r
307 if len(self.objectList) < 1 or self.objectList[0].mesh == None:
\r
310 for obj in self.objectList:
\r
311 if obj.mesh == None:
\r
314 for i in xrange(0, len(obj.mesh.origonalVertexes)):
\r
315 x = obj.mesh.origonalVertexes[i].x
\r
316 y = obj.mesh.origonalVertexes[i].y
\r
317 z = obj.mesh.origonalVertexes[i].z
\r
322 obj.mesh.vertexes[i].x = x * mat00 + y * mat01
\r
323 obj.mesh.vertexes[i].y = x * mat10 + y * mat11
\r
324 obj.mesh.vertexes[i].z = z * scaleZ
\r
326 for face in obj.mesh.faces:
\r
330 face.normal = (v2 - v1).cross(v3 - v1)
\r
331 face.normal.normalize()
\r
333 minV = self.objectList[0].mesh.getMinimum()
\r
334 maxV = self.objectList[0].mesh.getMaximum()
\r
335 for obj in self.objectList:
\r
336 if obj.mesh == None:
\r
339 obj.mesh.getMinimumZ()
\r
340 minV = minV.min(obj.mesh.getMinimum())
\r
341 maxV = maxV.max(obj.mesh.getMaximum())
\r
343 self.objectsMaxV = maxV
\r
344 self.objectsMinV = minV
\r
345 for obj in self.objectList:
\r
346 if obj.mesh == None:
\r
349 for v in obj.mesh.vertexes:
\r
351 v.x -= minV.x + (maxV.x - minV.x) / 2
\r
352 v.y -= minV.y + (maxV.y - minV.y) / 2
\r
353 obj.mesh.getMinimumZ()
\r
355 self.glCanvas.Refresh()
\r
357 class PreviewGLCanvas(glcanvas.GLCanvas):
\r
358 def __init__(self, parent):
\r
359 attribList = (glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24, glcanvas.WX_GL_STENCIL_SIZE, 8)
\r
360 glcanvas.GLCanvas.__init__(self, parent, attribList = attribList)
\r
361 self.parent = parent
\r
362 self.context = glcanvas.GLContext(self)
\r
363 wx.EVT_PAINT(self, self.OnPaint)
\r
364 wx.EVT_SIZE(self, self.OnSize)
\r
365 wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)
\r
366 wx.EVT_MOTION(self, self.OnMouseMotion)
\r
367 wx.EVT_MOUSEWHEEL(self, self.OnMouseWheel)
\r
374 self.gcodeDisplayList = None
\r
375 self.gcodeDisplayListCount = 0
\r
376 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]]
\r
378 def OnMouseMotion(self,e):
\r
379 if e.Dragging() and e.LeftIsDown():
\r
381 self.yaw += e.GetX() - self.oldX
\r
382 self.pitch -= e.GetY() - self.oldY
\r
383 if self.pitch > 170:
\r
385 if self.pitch < 10:
\r
388 self.offsetX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2
\r
389 self.offsetY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2
\r
391 if e.Dragging() and e.RightIsDown():
\r
392 self.zoom += e.GetY() - self.oldY
\r
396 self.oldX = e.GetX()
\r
397 self.oldY = e.GetY()
\r
399 def OnMouseWheel(self,e):
\r
400 self.zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0
\r
401 if self.zoom < 1.0:
\r
405 def OnEraseBackground(self,event):
\r
406 #Workaround for windows background redraw flicker.
\r
409 def OnSize(self,event):
\r
412 def OnPaint(self,event):
\r
413 dc = wx.PaintDC(self)
\r
414 if not hasOpenGLlibs:
\r
416 dc.DrawText("No PyOpenGL installation found.\nNo preview window available.", 10, 10)
\r
418 self.SetCurrent(self.context)
\r
419 opengl.InitGL(self, self.view3D, self.zoom)
\r
421 glTranslate(0,0,-self.zoom)
\r
422 glRotate(-self.pitch, 1,0,0)
\r
423 glRotate(self.yaw, 0,0,1)
\r
424 if self.viewMode == "GCode" or self.viewMode == "Mixed":
\r
425 if self.parent.gcode != None:
\r
426 glTranslate(0,0,-self.parent.gcode.layerList[self.parent.layerSpin.GetValue()][0].list[-1].z)
\r
428 if self.parent.objectsMaxV != None:
\r
429 glTranslate(0,0,-self.parent.objectsMaxV.z * profile.getProfileSettingFloat('model_scale') / 2)
\r
431 glScale(1.0/self.zoom, 1.0/self.zoom, 1.0)
\r
432 glTranslate(self.offsetX, self.offsetY, 0.0)
\r
433 glTranslate(-self.parent.machineCenter.x, -self.parent.machineCenter.y, 0)
\r
439 machineSize = self.parent.machineSize
\r
440 opengl.DrawMachine(machineSize)
\r
442 if self.parent.gcode != None and self.parent.gcodeDirty:
\r
443 if self.gcodeDisplayListCount < len(self.parent.gcode.layerList) or self.gcodeDisplayList == None:
\r
444 if self.gcodeDisplayList != None:
\r
445 glDeleteLists(self.gcodeDisplayList, self.gcodeDisplayListCount)
\r
446 self.gcodeDisplayList = glGenLists(len(self.parent.gcode.layerList));
\r
447 self.gcodeDisplayListCount = len(self.parent.gcode.layerList)
\r
448 self.parent.gcodeDirty = False
\r
452 layerThickness = 0.0
\r
453 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
\r
454 filamentArea = math.pi * filamentRadius * filamentRadius
\r
455 lineWidth = profile.getProfileSettingFloat('nozzle_size') / 2 / 10
\r
458 for layer in self.parent.gcode.layerList:
\r
459 glNewList(self.gcodeDisplayList + curLayerNum, GL_COMPILE)
\r
460 glDisable(GL_CULL_FACE)
\r
461 curLayerZ = layer[0].list[1].z
\r
462 layerThickness = curLayerZ - prevLayerZ
\r
463 prevLayerZ = layer[-1].list[-1].z
\r
465 if path.type == 'move':
\r
467 if path.type == 'extrude':
\r
468 if path.pathType == 'FILL':
\r
469 glColor3f(0.5,0.5,0)
\r
470 elif path.pathType == 'WALL-INNER':
\r
472 elif path.pathType == 'SUPPORT':
\r
474 elif path.pathType == 'SKIRT':
\r
475 glColor3f(0,0.5,0.5)
\r
478 if path.type == 'retract':
\r
480 if path.type == 'extrude':
\r
481 for i in xrange(0, len(path.list)-1):
\r
483 v1 = path.list[i+1]
\r
485 # Calculate line width from ePerDistance (needs layer thickness and filament diameter)
\r
486 dist = (v0 - v1).vsize()
\r
487 if dist > 0 and layerThickness > 0:
\r
488 extrusionMMperDist = (v1.e - v0.e) / dist
\r
489 lineWidth = extrusionMMperDist * filamentArea / layerThickness / 2
\r
491 normal = (v0 - v1).cross(util3d.Vector3(0,0,1))
\r
493 v2 = v0 + normal * lineWidth
\r
494 v3 = v1 + normal * lineWidth
\r
495 v0 = v0 - normal * lineWidth
\r
496 v1 = v1 - normal * lineWidth
\r
499 if path.pathType == 'FILL': #Remove depth buffer fighting on infill/wall overlap
\r
500 glVertex3f(v0.x, v0.y, v0.z - 0.02)
\r
501 glVertex3f(v1.x, v1.y, v1.z - 0.02)
\r
502 glVertex3f(v3.x, v3.y, v3.z - 0.02)
\r
503 glVertex3f(v2.x, v2.y, v2.z - 0.02)
\r
505 glVertex3f(v0.x, v0.y, v0.z - 0.01)
\r
506 glVertex3f(v1.x, v1.y, v1.z - 0.01)
\r
507 glVertex3f(v3.x, v3.y, v3.z - 0.01)
\r
508 glVertex3f(v2.x, v2.y, v2.z - 0.01)
\r
511 #for v in path['list']:
\r
512 # glBegin(GL_TRIANGLE_FAN)
\r
513 # glVertex3f(v.x, v.y, v.z - 0.001)
\r
514 # for i in xrange(0, 16+1):
\r
515 # if path['pathType'] == 'FILL': #Remove depth buffer fighting on infill/wall overlap
\r
516 # glVertex3f(v.x + math.cos(math.pi*2/16*i) * lineWidth, v.y + math.sin(math.pi*2/16*i) * lineWidth, v.z - 0.02)
\r
518 # glVertex3f(v.x + math.cos(math.pi*2/16*i) * lineWidth, v.y + math.sin(math.pi*2/16*i) * lineWidth, v.z - 0.01)
\r
521 glBegin(GL_LINE_STRIP)
\r
522 for v in path.list:
\r
523 glVertex3f(v.x, v.y, v.z)
\r
526 glEnable(GL_CULL_FACE)
\r
529 if self.parent.gcode != None and (self.viewMode == "GCode" or self.viewMode == "Mixed"):
\r
530 glEnable(GL_COLOR_MATERIAL)
\r
531 glEnable(GL_LIGHTING)
\r
532 for i in xrange(0, self.parent.layerSpin.GetValue() + 1):
\r
534 if i < self.parent.layerSpin.GetValue():
\r
535 c = 0.9 - (self.parent.layerSpin.GetValue() - i) * 0.1
\r
540 glLightfv(GL_LIGHT0, GL_DIFFUSE, [0,0,0,0])
\r
541 glLightfv(GL_LIGHT0, GL_AMBIENT, [c,c,c,c])
\r
542 glCallList(self.gcodeDisplayList + i)
\r
543 glDisable(GL_LIGHTING)
\r
544 glDisable(GL_COLOR_MATERIAL)
\r
545 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, [0.2, 0.2, 0.2, 1.0]);
\r
546 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, [0.8, 0.8, 0.8, 1.0]);
\r
548 glColor3f(1.0,1.0,1.0)
\r
549 glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)
\r
550 for obj in self.parent.objectList:
\r
551 if obj.mesh == None:
\r
553 if obj.displayList == None:
\r
554 obj.displayList = glGenLists(1);
\r
557 glNewList(obj.displayList, GL_COMPILE)
\r
558 opengl.DrawSTL(obj.mesh)
\r
561 if self.viewMode == "Transparent" or self.viewMode == "Mixed":
\r
562 glLightfv(GL_LIGHT0, GL_DIFFUSE, map(lambda x: x / 2, self.objColor[self.parent.objectList.index(obj)]))
\r
563 glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x / 10, self.objColor[self.parent.objectList.index(obj)]))
\r
564 #If we want transparent, then first render a solid black model to remove the printer size lines.
\r
565 if self.viewMode != "Mixed":
\r
566 glDisable(GL_BLEND)
\r
567 glColor3f(0.0,0.0,0.0)
\r
568 self.drawModel(obj)
\r
569 glColor3f(1.0,1.0,1.0)
\r
570 #After the black model is rendered, render the model again but now with lighting and no depth testing.
\r
571 glDisable(GL_DEPTH_TEST)
\r
572 glEnable(GL_LIGHTING)
\r
574 glBlendFunc(GL_ONE, GL_ONE)
\r
575 glEnable(GL_LIGHTING)
\r
576 self.drawModel(obj)
\r
577 elif self.viewMode == "X-Ray":
\r
578 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
\r
579 glDisable(GL_DEPTH_TEST)
\r
580 glEnable(GL_STENCIL_TEST);
\r
581 glStencilFunc(GL_ALWAYS, 1, 1)
\r
582 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
\r
583 self.drawModel(obj)
\r
584 glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
\r
586 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
\r
587 glStencilFunc(GL_EQUAL, 0, 1);
\r
589 self.drawModel(obj)
\r
590 glStencilFunc(GL_EQUAL, 1, 1);
\r
592 self.drawModel(obj)
\r
596 for i in xrange(2, 15, 2):
\r
597 glStencilFunc(GL_EQUAL, i, 0xFF);
\r
598 glColor(float(i)/10, float(i)/10, float(i)/5)
\r
600 glVertex3f(-1000,-1000,-1)
\r
601 glVertex3f( 1000,-1000,-1)
\r
602 glVertex3f( 1000, 1000,-1)
\r
603 glVertex3f(-1000, 1000,-1)
\r
605 for i in xrange(1, 15, 2):
\r
606 glStencilFunc(GL_EQUAL, i, 0xFF);
\r
607 glColor(float(i)/10, 0, 0)
\r
609 glVertex3f(-1000,-1000,-1)
\r
610 glVertex3f( 1000,-1000,-1)
\r
611 glVertex3f( 1000, 1000,-1)
\r
612 glVertex3f(-1000, 1000,-1)
\r
616 glDisable(GL_STENCIL_TEST);
\r
617 glEnable(GL_DEPTH_TEST)
\r
618 elif self.viewMode == "Normal":
\r
619 glLightfv(GL_LIGHT0, GL_DIFFUSE, self.objColor[self.parent.objectList.index(obj)])
\r
620 glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x / 5, self.objColor[self.parent.objectList.index(obj)]))
\r
621 glEnable(GL_LIGHTING)
\r
622 self.drawModel(obj)
\r
624 if self.viewMode == "Normal" or self.viewMode == "Transparent" or self.viewMode == "X-Ray":
\r
625 glDisable(GL_LIGHTING)
\r
626 glDisable(GL_DEPTH_TEST)
\r
627 glDisable(GL_BLEND)
\r
630 for err in self.parent.errorList:
\r
631 glVertex3f(err[0].x, err[0].y, err[0].z)
\r
632 glVertex3f(err[1].x, err[1].y, err[1].z)
\r
634 glEnable(GL_DEPTH_TEST)
\r
637 def drawModel(self, obj):
\r
638 multiX = 1 #int(profile.getProfileSetting('model_multiply_x'))
\r
639 multiY = 1 #int(profile.getProfileSetting('model_multiply_y'))
\r
640 modelScale = profile.getProfileSettingFloat('model_scale')
\r
641 modelSize = (obj.mesh.getMaximum() - obj.mesh.getMinimum()) * modelScale
\r
643 glTranslate(-(modelSize.x+10)*(multiX-1)/2,-(modelSize.y+10)*(multiY-1)/2, 0)
\r
644 for mx in xrange(0, multiX):
\r
645 for my in xrange(0, multiY):
\r
647 glTranslate((modelSize.x+10)*mx,(modelSize.y+10)*my, 0)
\r
648 glScalef(modelScale, modelScale, modelScale)
\r
649 glCallList(obj.displayList)
\r