1 from __future__ import division
\r
3 import sys, math, threading, re, time, os
\r
6 from wx import glcanvas
\r
10 OpenGL.ERROR_CHECKING = False
\r
11 from OpenGL.GLU import *
\r
12 from OpenGL.GL import *
\r
13 hasOpenGLlibs = True
\r
15 print "Failed to find PyOpenGL: http://pyopengl.sourceforge.net/"
\r
16 hasOpenGLlibs = False
\r
18 from gui import opengl
\r
19 from gui import toolbarUtil
\r
21 from util import profile
\r
22 from util import gcodeInterpreter
\r
23 from util import meshLoader
\r
24 from util import util3d
\r
25 from util import sliceRun
\r
27 class previewObject():
\r
30 self.filename = None
\r
31 self.displayList = None
\r
34 class previewPanel(wx.Panel):
\r
35 def __init__(self, parent):
\r
36 super(previewPanel, self).__init__(parent,-1)
\r
38 self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DDKSHADOW))
\r
39 self.SetMinSize((440,320))
\r
41 self.objectList = []
\r
44 self.objectsMinV = None
\r
45 self.objectsMaxV = None
\r
46 self.loadThread = None
\r
47 self.machineSize = util3d.Vector3(profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height'))
\r
48 self.machineCenter = util3d.Vector3(float(profile.getProfileSetting('machine_center_x')), float(profile.getProfileSetting('machine_center_y')), 0)
\r
50 self.glCanvas = PreviewGLCanvas(self)
\r
51 #Create the popup window
\r
52 self.warningPopup = wx.PopupWindow(self, flags=wx.BORDER_SIMPLE)
\r
53 self.warningPopup.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_INFOBK))
\r
54 self.warningPopup.text = wx.StaticText(self.warningPopup, -1, 'Reset scale, rotation and mirror?')
\r
55 self.warningPopup.yesButton = wx.Button(self.warningPopup, -1, 'yes', style=wx.BU_EXACTFIT)
\r
56 self.warningPopup.noButton = wx.Button(self.warningPopup, -1, 'no', style=wx.BU_EXACTFIT)
\r
57 self.warningPopup.sizer = wx.BoxSizer(wx.HORIZONTAL)
\r
58 self.warningPopup.SetSizer(self.warningPopup.sizer)
\r
59 self.warningPopup.sizer.Add(self.warningPopup.text, 1, flag=wx.ALL|wx.ALIGN_CENTER_VERTICAL, border=1)
\r
60 self.warningPopup.sizer.Add(self.warningPopup.yesButton, 0, flag=wx.EXPAND|wx.ALL, border=1)
\r
61 self.warningPopup.sizer.Add(self.warningPopup.noButton, 0, flag=wx.EXPAND|wx.ALL, border=1)
\r
62 self.warningPopup.Fit()
\r
63 self.warningPopup.Layout()
\r
64 self.warningPopup.timer = wx.Timer(self)
\r
65 self.Bind(wx.EVT_TIMER, self.OnHideWarning, self.warningPopup.timer)
\r
67 self.Bind(wx.EVT_BUTTON, self.OnWarningPopup, self.warningPopup.yesButton)
\r
68 self.Bind(wx.EVT_BUTTON, self.OnHideWarning, self.warningPopup.noButton)
\r
69 parent.Bind(wx.EVT_MOVE, self.OnMove)
\r
70 parent.Bind(wx.EVT_SIZE, self.OnMove)
\r
72 self.toolbar = toolbarUtil.Toolbar(self)
\r
75 toolbarUtil.RadioButton(self.toolbar, group, 'object-3d-on.png', 'object-3d-off.png', '3D view', callback=self.On3DClick)
\r
76 toolbarUtil.RadioButton(self.toolbar, group, 'object-top-on.png', 'object-top-off.png', 'Topdown view', callback=self.OnTopClick)
\r
77 self.toolbar.AddSeparator()
\r
79 self.showBorderButton = toolbarUtil.ToggleButton(self.toolbar, '', 'view-border-on.png', 'view-border-off.png', 'Show model borders', callback=self.OnViewChange)
\r
80 self.toolbar.AddSeparator()
\r
83 self.normalViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-normal-on.png', 'view-normal-off.png', 'Normal model view', callback=self.OnViewChange)
\r
84 self.transparentViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-transparent-on.png', 'view-transparent-off.png', 'Transparent model view', callback=self.OnViewChange)
\r
85 self.xrayViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-xray-on.png', 'view-xray-off.png', 'X-Ray view', callback=self.OnViewChange)
\r
86 self.gcodeViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-gcode-on.png', 'view-gcode-off.png', 'GCode view', callback=self.OnViewChange)
\r
87 self.mixedViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-mixed-on.png', 'view-mixed-off.png', 'Mixed model/GCode view', callback=self.OnViewChange)
\r
88 self.toolbar.AddSeparator()
\r
90 self.layerSpin = wx.SpinCtrl(self.toolbar, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS)
\r
91 self.toolbar.AddControl(self.layerSpin)
\r
92 self.Bind(wx.EVT_SPINCTRL, self.OnLayerNrChange, self.layerSpin)
\r
94 self.toolbar2 = toolbarUtil.Toolbar(self)
\r
97 self.mirrorX = toolbarUtil.ToggleButton(self.toolbar2, 'flip_x', 'object-mirror-x-on.png', 'object-mirror-x-off.png', 'Mirror X', callback=self.updateModelTransform)
\r
98 self.mirrorY = toolbarUtil.ToggleButton(self.toolbar2, 'flip_y', 'object-mirror-y-on.png', 'object-mirror-y-off.png', 'Mirror Y', callback=self.updateModelTransform)
\r
99 self.mirrorZ = toolbarUtil.ToggleButton(self.toolbar2, 'flip_z', 'object-mirror-z-on.png', 'object-mirror-z-off.png', 'Mirror Z', callback=self.updateModelTransform)
\r
100 self.toolbar2.AddSeparator()
\r
103 self.swapXZ = toolbarUtil.ToggleButton(self.toolbar2, 'swap_xz', 'object-swap-xz-on.png', 'object-swap-xz-off.png', 'Swap XZ', callback=self.updateModelTransform)
\r
104 self.swapYZ = toolbarUtil.ToggleButton(self.toolbar2, 'swap_yz', 'object-swap-yz-on.png', 'object-swap-yz-off.png', 'Swap YZ', callback=self.updateModelTransform)
\r
105 self.toolbar2.AddSeparator()
\r
108 self.scaleReset = toolbarUtil.NormalButton(self.toolbar2, self.OnScaleReset, 'object-scale.png', 'Reset model scale')
\r
109 self.scale = wx.TextCtrl(self.toolbar2, -1, profile.getProfileSetting('model_scale'), size=(21*2,21))
\r
110 self.toolbar2.AddControl(self.scale)
\r
111 self.scale.Bind(wx.EVT_TEXT, self.OnScale)
\r
112 self.scaleMax = toolbarUtil.NormalButton(self.toolbar2, self.OnScaleMax, 'object-max-size.png', 'Scale object to fit machine size')
\r
114 self.toolbar2.AddSeparator()
\r
117 #self.mulXadd = toolbarUtil.NormalButton(self.toolbar2, self.OnMulXAddClick, 'object-mul-x-add.png', 'Increase number of models on X axis')
\r
118 #self.mulXsub = toolbarUtil.NormalButton(self.toolbar2, self.OnMulXSubClick, 'object-mul-x-sub.png', 'Decrease number of models on X axis')
\r
119 #self.mulYadd = toolbarUtil.NormalButton(self.toolbar2, self.OnMulYAddClick, 'object-mul-y-add.png', 'Increase number of models on Y axis')
\r
120 #self.mulYsub = toolbarUtil.NormalButton(self.toolbar2, self.OnMulYSubClick, 'object-mul-y-sub.png', 'Decrease number of models on Y axis')
\r
121 #self.toolbar2.AddSeparator()
\r
124 self.rotateReset = toolbarUtil.NormalButton(self.toolbar2, self.OnRotateReset, 'object-rotate.png', 'Reset model rotation')
\r
125 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
126 self.rotate.SetRange(0, 360)
\r
127 self.rotate.Bind(wx.EVT_TEXT, self.OnRotate)
\r
128 self.toolbar2.AddControl(self.rotate)
\r
130 self.toolbar2.Realize()
\r
131 self.OnViewChange()
\r
133 sizer = wx.BoxSizer(wx.VERTICAL)
\r
134 sizer.Add(self.toolbar, 0, flag=wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, border=1)
\r
135 sizer.Add(self.glCanvas, 1, flag=wx.EXPAND)
\r
136 sizer.Add(self.toolbar2, 0, flag=wx.EXPAND|wx.BOTTOM|wx.LEFT|wx.RIGHT, border=1)
\r
137 self.SetSizer(sizer)
\r
139 def OnMove(self, e = None):
\r
142 x, y = self.glCanvas.ClientToScreenXY(0, 0)
\r
143 sx, sy = self.glCanvas.GetClientSizeTuple()
\r
144 self.warningPopup.SetPosition((x, y+sy-self.warningPopup.GetSize().height))
\r
146 def OnMulXAddClick(self, e):
\r
147 profile.putProfileSetting('model_multiply_x', str(max(1, int(profile.getProfileSetting('model_multiply_x'))+1)))
\r
148 self.glCanvas.Refresh()
\r
150 def OnMulXSubClick(self, e):
\r
151 profile.putProfileSetting('model_multiply_x', str(max(1, int(profile.getProfileSetting('model_multiply_x'))-1)))
\r
152 self.glCanvas.Refresh()
\r
154 def OnMulYAddClick(self, e):
\r
155 profile.putProfileSetting('model_multiply_y', str(max(1, int(profile.getProfileSetting('model_multiply_y'))+1)))
\r
156 self.glCanvas.Refresh()
\r
158 def OnMulYSubClick(self, e):
\r
159 profile.putProfileSetting('model_multiply_y', str(max(1, int(profile.getProfileSetting('model_multiply_y'))-1)))
\r
160 self.glCanvas.Refresh()
\r
162 def OnScaleReset(self, e):
\r
163 self.scale.SetValue('1.0')
\r
166 def OnScale(self, e):
\r
168 if self.scale.GetValue() != '':
\r
169 scale = self.scale.GetValue()
\r
170 profile.putProfileSetting('model_scale', scale)
\r
171 self.glCanvas.Refresh()
\r
173 def OnScaleMax(self, e):
\r
174 if self.objectsMinV == None:
\r
176 vMin = self.objectsMinV
\r
177 vMax = self.objectsMaxV
\r
178 scaleX1 = (self.machineSize.x - self.machineCenter.x) / ((vMax[0] - vMin[0]) / 2)
\r
179 scaleY1 = (self.machineSize.y - self.machineCenter.y) / ((vMax[1] - vMin[1]) / 2)
\r
180 scaleX2 = (self.machineCenter.x) / ((vMax[0] - vMin[0]) / 2)
\r
181 scaleY2 = (self.machineCenter.y) / ((vMax[1] - vMin[1]) / 2)
\r
182 scaleZ = self.machineSize.z / (vMax[2] - vMin[2])
\r
183 scale = min(scaleX1, scaleY1, scaleX2, scaleY2, scaleZ)
\r
184 self.scale.SetValue(str(scale))
\r
185 profile.putProfileSetting('model_scale', self.scale.GetValue())
\r
186 self.glCanvas.Refresh()
\r
188 def OnRotateReset(self, e):
\r
189 self.rotate.SetValue(0)
\r
190 self.OnRotate(None)
\r
192 def OnRotate(self, e):
\r
193 profile.putProfileSetting('model_rotate_base', self.rotate.GetValue())
\r
194 self.updateModelTransform()
\r
196 def On3DClick(self):
\r
197 self.glCanvas.yaw = 30
\r
198 self.glCanvas.pitch = 60
\r
199 self.glCanvas.zoom = 300
\r
200 self.glCanvas.view3D = True
\r
201 self.glCanvas.Refresh()
\r
203 def OnTopClick(self):
\r
204 self.glCanvas.view3D = False
\r
205 self.glCanvas.zoom = 100
\r
206 self.glCanvas.offsetX = 0
\r
207 self.glCanvas.offsetY = 0
\r
208 self.glCanvas.Refresh()
\r
210 def OnLayerNrChange(self, e):
\r
211 self.glCanvas.Refresh()
\r
213 def updateCenterX(self):
\r
214 self.machineCenter.x = profile.getProfileSettingFloat('machine_center_x')
\r
215 self.glCanvas.Refresh()
\r
217 def updateCenterY(self):
\r
218 self.machineCenter.y = profile.getProfileSettingFloat('machine_center_y')
\r
219 self.glCanvas.Refresh()
\r
221 def setViewMode(self, mode):
\r
222 if mode == "Normal":
\r
223 self.normalViewButton.SetValue(True)
\r
224 if mode == "GCode":
\r
225 self.gcodeViewButton.SetValue(True)
\r
226 self.glCanvas.viewMode = mode
\r
227 wx.CallAfter(self.glCanvas.Refresh)
\r
229 def loadModelFiles(self, filelist, showWarning = False):
\r
230 while len(filelist) > len(self.objectList):
\r
231 self.objectList.append(previewObject())
\r
232 for idx in xrange(len(filelist), len(self.objectList)):
\r
233 self.objectList[idx].mesh = None
\r
234 self.objectList[idx].filename = None
\r
235 for idx in xrange(0, len(filelist)):
\r
236 obj = self.objectList[idx]
\r
237 if obj.filename != filelist[idx]:
\r
238 obj.fileTime = None
\r
239 self.gcodeFileTime = None
\r
240 self.logFileTime = None
\r
241 obj.filename = filelist[idx]
\r
243 self.gcodeFilename = sliceRun.getExportFilename(filelist[0])
\r
244 #Do the STL file loading in a background thread so we don't block the UI.
\r
245 if self.loadThread != None and self.loadThread.isAlive():
\r
246 self.loadThread.join()
\r
247 self.loadThread = threading.Thread(target=self.doFileLoadThread)
\r
248 self.loadThread.daemon = True
\r
249 self.loadThread.start()
\r
252 if profile.getProfileSettingFloat('model_scale') != 1.0 or profile.getProfileSettingFloat('model_rotate_base') != 0 or profile.getProfileSetting('flip_x') != 'False' or profile.getProfileSetting('flip_y') != 'False' or profile.getProfileSetting('flip_z') != 'False' or profile.getProfileSetting('swap_xz') != 'False' or profile.getProfileSetting('swap_yz') != 'False':
\r
253 self.ShowWarningPopup('Reset scale, rotation and mirror?', self.OnResetAll)
\r
255 def ShowWarningPopup(self, text, callback):
\r
256 self.warningPopup.text.SetLabel(text)
\r
257 self.warningPopup.callback = callback
\r
259 self.warningPopup.Show(True)
\r
260 self.warningPopup.timer.Start(5000)
\r
263 def loadReModelFiles(self, filelist):
\r
264 #Only load this again if the filename matches the file we have already loaded (for auto loading GCode after slicing)
\r
265 for idx in xrange(0, len(filelist)):
\r
266 if self.objectList[idx].filename != filelist[idx]:
\r
268 self.loadModelFiles(filelist)
\r
271 def doFileLoadThread(self):
\r
272 for obj in self.objectList:
\r
273 if obj.filename != None and os.path.isfile(obj.filename) and obj.fileTime != os.stat(obj.filename).st_mtime:
\r
274 obj.ileTime = os.stat(obj.filename).st_mtime
\r
275 mesh = meshLoader.loadMesh(obj.filename)
\r
278 self.updateModelTransform()
\r
279 scale = profile.getProfileSettingFloat('model_scale')
\r
280 size = (self.objectsMaxV - self.objectsMinV) * scale
\r
281 if size[0] > self.machineSize.x or size[1] > self.machineSize.y or size[2] > self.machineSize.z:
\r
282 self.OnScaleMax(None)
\r
283 self.glCanvas.zoom = numpy.max(size) * 2.5
\r
284 self.errorList = []
\r
285 wx.CallAfter(self.updateToolbar)
\r
286 wx.CallAfter(self.glCanvas.Refresh)
\r
288 if os.path.isfile(self.gcodeFilename) and self.gcodeFileTime != os.stat(self.gcodeFilename).st_mtime:
\r
289 self.gcodeFileTime = os.stat(self.gcodeFilename).st_mtime
\r
290 gcode = gcodeInterpreter.gcode()
\r
291 gcode.progressCallback = self.loadProgress
\r
292 gcode.load(self.gcodeFilename)
\r
293 self.gcodeDirty = False
\r
295 self.gcodeDirty = True
\r
298 for line in open(self.gcodeFilename, "rt"):
\r
299 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
301 v1 = util3d.Vector3(float(res.group(2)), float(res.group(3)), float(res.group(4)))
\r
302 v2 = util3d.Vector3(float(res.group(5)), float(res.group(6)), float(res.group(7)))
\r
303 errorList.append([v1, v2])
\r
304 self.errorList = errorList
\r
306 wx.CallAfter(self.updateToolbar)
\r
307 wx.CallAfter(self.glCanvas.Refresh)
\r
308 elif not os.path.isfile(self.gcodeFilename):
\r
311 def loadProgress(self, progress):
\r
314 def OnResetAll(self, e = None):
\r
315 profile.putProfileSetting('model_scale', '1.0')
\r
316 profile.putProfileSetting('model_rotate_base', '0')
\r
317 profile.putProfileSetting('flip_x', 'False')
\r
318 profile.putProfileSetting('flip_y', 'False')
\r
319 profile.putProfileSetting('flip_z', 'False')
\r
320 profile.putProfileSetting('swap_xz', 'False')
\r
321 profile.putProfileSetting('swap_yz', 'False')
\r
322 self.updateProfileToControls()
\r
324 def OnWarningPopup(self, e):
\r
325 self.warningPopup.Show(False)
\r
326 self.warningPopup.timer.Stop()
\r
327 self.warningPopup.callback()
\r
329 def OnHideWarning(self, e):
\r
330 self.warningPopup.Show(False)
\r
331 self.warningPopup.timer.Stop()
\r
333 def updateToolbar(self):
\r
334 self.gcodeViewButton.Show(self.gcode != None)
\r
335 self.mixedViewButton.Show(self.gcode != None)
\r
336 self.layerSpin.Show(self.glCanvas.viewMode == "GCode" or self.glCanvas.viewMode == "Mixed")
\r
337 if self.gcode != None:
\r
338 self.layerSpin.SetRange(1, len(self.gcode.layerList) - 1)
\r
339 self.toolbar.Realize()
\r
342 def OnViewChange(self):
\r
343 if self.normalViewButton.GetValue():
\r
344 self.glCanvas.viewMode = "Normal"
\r
345 elif self.transparentViewButton.GetValue():
\r
346 self.glCanvas.viewMode = "Transparent"
\r
347 elif self.xrayViewButton.GetValue():
\r
348 self.glCanvas.viewMode = "X-Ray"
\r
349 elif self.gcodeViewButton.GetValue():
\r
350 self.glCanvas.viewMode = "GCode"
\r
351 elif self.mixedViewButton.GetValue():
\r
352 self.glCanvas.viewMode = "Mixed"
\r
353 self.glCanvas.drawBorders = self.showBorderButton.GetValue()
\r
354 self.updateToolbar()
\r
355 self.glCanvas.Refresh()
\r
357 def updateModelTransform(self, f=0):
\r
358 if len(self.objectList) < 1 or self.objectList[0].mesh == None:
\r
361 rotate = profile.getProfileSettingFloat('model_rotate_base')
\r
362 mirrorX = profile.getProfileSetting('flip_x') == 'True'
\r
363 mirrorY = profile.getProfileSetting('flip_y') == 'True'
\r
364 mirrorZ = profile.getProfileSetting('flip_z') == 'True'
\r
365 swapXZ = profile.getProfileSetting('swap_xz') == 'True'
\r
366 swapYZ = profile.getProfileSetting('swap_yz') == 'True'
\r
368 for obj in self.objectList:
\r
369 if obj.mesh == None:
\r
371 obj.mesh.setRotateMirror(rotate, mirrorX, mirrorY, mirrorZ, swapXZ, swapYZ)
\r
373 minV = self.objectList[0].mesh.getMinimum()
\r
374 maxV = self.objectList[0].mesh.getMaximum()
\r
375 for obj in self.objectList:
\r
376 if obj.mesh == None:
\r
379 obj.mesh.getMinimumZ()
\r
380 minV = numpy.minimum(minV, obj.mesh.getMinimum())
\r
381 maxV = numpy.maximum(maxV, obj.mesh.getMaximum())
\r
383 self.objectsMaxV = maxV
\r
384 self.objectsMinV = minV
\r
385 for obj in self.objectList:
\r
386 if obj.mesh == None:
\r
389 obj.mesh.vertexes -= numpy.array([minV[0] + (maxV[0] - minV[0]) / 2, minV[1] + (maxV[1] - minV[1]) / 2, minV[2]])
\r
390 #for v in obj.mesh.vertexes:
\r
392 # v[0] -= minV[0] + (maxV[0] - minV[0]) / 2
\r
393 # v[1] -= minV[1] + (maxV[1] - minV[1]) / 2
\r
394 obj.mesh.getMinimumZ()
\r
396 self.glCanvas.Refresh()
\r
398 def updateProfileToControls(self):
\r
399 self.scale.SetValue(profile.getProfileSetting('model_scale'))
\r
400 self.rotate.SetValue(profile.getProfileSettingFloat('model_rotate_base'))
\r
401 self.mirrorX.SetValue(profile.getProfileSetting('flip_x') == 'True')
\r
402 self.mirrorY.SetValue(profile.getProfileSetting('flip_y') == 'True')
\r
403 self.mirrorZ.SetValue(profile.getProfileSetting('flip_z') == 'True')
\r
404 self.swapXZ.SetValue(profile.getProfileSetting('swap_xz') == 'True')
\r
405 self.swapYZ.SetValue(profile.getProfileSetting('swap_yz') == 'True')
\r
406 self.updateModelTransform()
\r
407 self.glCanvas.updateProfileToControls()
\r
409 class PreviewGLCanvas(glcanvas.GLCanvas):
\r
410 def __init__(self, parent):
\r
411 attribList = (glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24, glcanvas.WX_GL_STENCIL_SIZE, 8)
\r
412 glcanvas.GLCanvas.__init__(self, parent, attribList = attribList)
\r
413 self.parent = parent
\r
414 self.context = glcanvas.GLContext(self)
\r
415 wx.EVT_PAINT(self, self.OnPaint)
\r
416 wx.EVT_SIZE(self, self.OnSize)
\r
417 wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)
\r
418 wx.EVT_MOTION(self, self.OnMouseMotion)
\r
419 wx.EVT_MOUSEWHEEL(self, self.OnMouseWheel)
\r
426 self.gcodeDisplayList = None
\r
427 self.gcodeDisplayListMade = None
\r
428 self.gcodeDisplayListCount = 0
\r
429 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
433 self.tempRotate = 0
\r
435 def updateProfileToControls(self):
\r
436 self.objColor[0] = profile.getPreferenceColour('model_colour')
\r
437 self.objColor[1] = profile.getPreferenceColour('model_colour2')
\r
438 self.objColor[2] = profile.getPreferenceColour('model_colour3')
\r
439 self.objColor[3] = profile.getPreferenceColour('model_colour3')
\r
441 def OnMouseMotion(self,e):
\r
444 if self.parent.objectsMaxV != None:
\r
445 size = (self.parent.objectsMaxV - self.parent.objectsMinV)
\r
446 sizeXY = math.sqrt((size[0] * size[0]) + (size[1] * size[1]))
\r
448 p0 = numpy.array(gluUnProject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 0, self.modelMatrix, self.projMatrix, self.viewport))
\r
449 p1 = numpy.array(gluUnProject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 1, self.modelMatrix, self.projMatrix, self.viewport))
\r
450 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
\r
451 cursorXY = math.sqrt((cursorZ0[0] * cursorZ0[0]) + (cursorZ0[1] * cursorZ0[1]))
\r
452 if cursorXY >= sizeXY * 0.7 and cursorXY <= sizeXY * 0.7 + 3 and False:
\r
453 self.SetCursor(wx.StockCursor(wx.CURSOR_SIZING))
\r
455 self.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
\r
457 if e.Dragging() and e.LeftIsDown():
\r
458 if self.dragType == '':
\r
459 #Define the drag type depending on the cursor position.
\r
460 if cursorXY >= sizeXY * 0.7 and cursorXY <= sizeXY * 0.7 + 3 and False:
\r
461 self.dragType = 'modelRotate'
\r
462 self.dragStart = math.atan2(cursorZ0[0], cursorZ0[1])
\r
464 self.dragType = 'viewRotate'
\r
466 if self.dragType == 'viewRotate':
\r
468 self.yaw += e.GetX() - self.oldX
\r
469 self.pitch -= e.GetY() - self.oldY
\r
470 if self.pitch > 170:
\r
472 if self.pitch < 10:
\r
475 self.offsetX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2
\r
476 self.offsetY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2
\r
477 elif self.dragType == 'modelRotate':
\r
478 angle = math.atan2(cursorZ0[0], cursorZ0[1])
\r
479 diff = self.dragStart - angle
\r
480 self.tempRotate = diff * 180 / math.pi
\r
481 #Workaround for buggy ATI cards.
\r
482 size = self.GetSizeTuple()
\r
483 self.SetSize((size[0]+1, size[1]))
\r
484 self.SetSize((size[0], size[1]))
\r
487 if self.tempRotate != 0:
\r
488 profile.putProfileSetting('model_rotate_base', profile.getProfileSettingFloat('model_rotate_base') + self.tempRotate)
\r
489 self.parent.updateModelTransform()
\r
490 self.tempRotate = 0
\r
493 if e.Dragging() and e.RightIsDown():
\r
494 self.zoom += e.GetY() - self.oldY
\r
497 if self.zoom > 500:
\r
500 self.oldX = e.GetX()
\r
501 self.oldY = e.GetY()
\r
506 def OnMouseWheel(self,e):
\r
507 self.zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0
\r
508 if self.zoom < 1.0:
\r
510 if self.zoom > 500:
\r
514 def OnEraseBackground(self,event):
\r
515 #Workaround for windows background redraw flicker.
\r
518 def OnSize(self,e):
\r
521 def OnPaint(self,e):
\r
522 dc = wx.PaintDC(self)
\r
523 if not hasOpenGLlibs:
\r
525 dc.DrawText("No PyOpenGL installation found.\nNo preview window available.", 10, 10)
\r
527 self.SetCurrent(self.context)
\r
528 opengl.InitGL(self, self.view3D, self.zoom)
\r
530 glTranslate(0,0,-self.zoom)
\r
531 glRotate(-self.pitch, 1,0,0)
\r
532 glRotate(self.yaw, 0,0,1)
\r
533 if self.viewMode == "GCode" or self.viewMode == "Mixed":
\r
534 if self.parent.gcode != None and len(self.parent.gcode.layerList) > self.parent.layerSpin.GetValue() and len(self.parent.gcode.layerList[self.parent.layerSpin.GetValue()]) > 0:
\r
535 glTranslate(0,0,-self.parent.gcode.layerList[self.parent.layerSpin.GetValue()][0].list[-1].z)
\r
537 if self.parent.objectsMaxV != None:
\r
538 glTranslate(0,0,-(self.parent.objectsMaxV[2]-self.parent.objectsMinV[2]) * profile.getProfileSettingFloat('model_scale') / 2)
\r
540 glScale(1.0/self.zoom, 1.0/self.zoom, 1.0)
\r
541 glTranslate(self.offsetX, self.offsetY, 0.0)
\r
543 self.viewport = glGetIntegerv(GL_VIEWPORT);
\r
544 self.modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX);
\r
545 self.projMatrix = glGetDoublev(GL_PROJECTION_MATRIX);
\r
547 glTranslate(-self.parent.machineCenter.x, -self.parent.machineCenter.y, 0)
\r
553 machineSize = self.parent.machineSize
\r
555 if self.parent.gcode != None and self.parent.gcodeDirty:
\r
556 if self.gcodeDisplayListCount < len(self.parent.gcode.layerList) or self.gcodeDisplayList == None:
\r
557 if self.gcodeDisplayList != None:
\r
558 glDeleteLists(self.gcodeDisplayList, self.gcodeDisplayListCount)
\r
559 self.gcodeDisplayList = glGenLists(len(self.parent.gcode.layerList));
\r
560 self.gcodeDisplayListCount = len(self.parent.gcode.layerList)
\r
561 self.parent.gcodeDirty = False
\r
562 self.gcodeDisplayListMade = 0
\r
564 if self.parent.gcode != None and self.gcodeDisplayListMade < len(self.parent.gcode.layerList):
\r
565 glNewList(self.gcodeDisplayList + self.gcodeDisplayListMade, GL_COMPILE)
\r
566 opengl.DrawGCodeLayer(self.parent.gcode.layerList[self.gcodeDisplayListMade])
\r
568 self.gcodeDisplayListMade += 1
\r
569 wx.CallAfter(self.Refresh)
\r
572 glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)
\r
573 for obj in self.parent.objectList:
\r
574 if obj.mesh == None:
\r
576 if obj.displayList == None:
\r
577 obj.displayList = glGenLists(1);
\r
580 glNewList(obj.displayList, GL_COMPILE)
\r
581 opengl.DrawMesh(obj.mesh)
\r
584 if self.viewMode == "Mixed":
\r
585 glDisable(GL_BLEND)
\r
586 glColor3f(0.0,0.0,0.0)
\r
587 self.drawModel(obj)
\r
588 glColor3f(1.0,1.0,1.0)
\r
589 glClear(GL_DEPTH_BUFFER_BIT)
\r
593 if self.parent.gcode != None and (self.viewMode == "GCode" or self.viewMode == "Mixed"):
\r
594 glEnable(GL_COLOR_MATERIAL)
\r
595 glEnable(GL_LIGHTING)
\r
596 drawUpToLayer = min(self.gcodeDisplayListMade, self.parent.layerSpin.GetValue() + 1)
\r
597 starttime = time.time()
\r
598 for i in xrange(drawUpToLayer - 1, -1, -1):
\r
600 if i < self.parent.layerSpin.GetValue():
\r
601 c = 0.9 - (drawUpToLayer - i) * 0.1
\r
606 glLightfv(GL_LIGHT0, GL_DIFFUSE, [0,0,0,0])
\r
607 glLightfv(GL_LIGHT0, GL_AMBIENT, [c,c,c,c])
\r
608 glCallList(self.gcodeDisplayList + i)
\r
609 if time.time() - starttime > 0.1:
\r
612 glDisable(GL_LIGHTING)
\r
613 glDisable(GL_COLOR_MATERIAL)
\r
614 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, [0.2, 0.2, 0.2, 1.0]);
\r
615 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, [0.8, 0.8, 0.8, 1.0]);
\r
617 glColor3f(1.0,1.0,1.0)
\r
619 glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)
\r
620 for obj in self.parent.objectList:
\r
621 if obj.mesh == None:
\r
624 if self.viewMode == "Transparent" or self.viewMode == "Mixed":
\r
625 glLightfv(GL_LIGHT0, GL_DIFFUSE, map(lambda x: x / 2, self.objColor[self.parent.objectList.index(obj)]))
\r
626 glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x / 10, self.objColor[self.parent.objectList.index(obj)]))
\r
627 #If we want transparent, then first render a solid black model to remove the printer size lines.
\r
628 if self.viewMode != "Mixed":
\r
629 glDisable(GL_BLEND)
\r
630 glColor3f(0.0,0.0,0.0)
\r
631 self.drawModel(obj)
\r
632 glColor3f(1.0,1.0,1.0)
\r
633 #After the black model is rendered, render the model again but now with lighting and no depth testing.
\r
634 glDisable(GL_DEPTH_TEST)
\r
635 glEnable(GL_LIGHTING)
\r
637 glBlendFunc(GL_ONE, GL_ONE)
\r
638 glEnable(GL_LIGHTING)
\r
639 self.drawModel(obj)
\r
640 glEnable(GL_DEPTH_TEST)
\r
641 elif self.viewMode == "X-Ray":
\r
642 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
\r
643 glDisable(GL_LIGHTING)
\r
644 glDisable(GL_DEPTH_TEST)
\r
645 glEnable(GL_STENCIL_TEST)
\r
646 glStencilFunc(GL_ALWAYS, 1, 1)
\r
647 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
\r
648 self.drawModel(obj)
\r
649 glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
\r
651 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
\r
652 glStencilFunc(GL_EQUAL, 0, 1)
\r
654 self.drawModel(obj)
\r
655 glStencilFunc(GL_EQUAL, 1, 1)
\r
657 self.drawModel(obj)
\r
661 for i in xrange(2, 15, 2):
\r
662 glStencilFunc(GL_EQUAL, i, 0xFF);
\r
663 glColor(float(i)/10, float(i)/10, float(i)/5)
\r
665 glVertex3f(-1000,-1000,-1)
\r
666 glVertex3f( 1000,-1000,-1)
\r
667 glVertex3f( 1000, 1000,-1)
\r
668 glVertex3f(-1000, 1000,-1)
\r
670 for i in xrange(1, 15, 2):
\r
671 glStencilFunc(GL_EQUAL, i, 0xFF);
\r
672 glColor(float(i)/10, 0, 0)
\r
674 glVertex3f(-1000,-1000,-1)
\r
675 glVertex3f( 1000,-1000,-1)
\r
676 glVertex3f( 1000, 1000,-1)
\r
677 glVertex3f(-1000, 1000,-1)
\r
681 glDisable(GL_STENCIL_TEST)
\r
682 glEnable(GL_DEPTH_TEST)
\r
684 #Fix the depth buffer
\r
685 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
\r
686 self.drawModel(obj)
\r
687 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
\r
688 elif self.viewMode == "Normal":
\r
689 glLightfv(GL_LIGHT0, GL_DIFFUSE, self.objColor[self.parent.objectList.index(obj)])
\r
690 glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x * 0.4, self.objColor[self.parent.objectList.index(obj)]))
\r
691 glEnable(GL_LIGHTING)
\r
692 self.drawModel(obj)
\r
694 if self.drawBorders and (self.viewMode == "Normal" or self.viewMode == "Transparent" or self.viewMode == "X-Ray"):
\r
695 glEnable(GL_DEPTH_TEST)
\r
696 glDisable(GL_LIGHTING)
\r
699 modelScale = profile.getProfileSettingFloat('model_scale')
\r
700 glScalef(modelScale, modelScale, modelScale)
\r
701 opengl.DrawMeshOutline(obj.mesh)
\r
705 if self.viewMode == "Normal" or self.viewMode == "Transparent" or self.viewMode == "X-Ray":
\r
706 glDisable(GL_LIGHTING)
\r
707 glDisable(GL_DEPTH_TEST)
\r
708 glDisable(GL_BLEND)
\r
711 for err in self.parent.errorList:
\r
712 glVertex3f(err[0].x, err[0].y, err[0].z)
\r
713 glVertex3f(err[1].x, err[1].y, err[1].z)
\r
715 glEnable(GL_DEPTH_TEST)
\r
717 opengl.DrawMachine(machineSize)
\r
720 glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)
\r
722 #Draw the rotate circle
\r
723 if self.parent.objectsMaxV != None and False:
\r
724 glDisable(GL_LIGHTING)
\r
725 glDisable(GL_CULL_FACE)
\r
727 glBegin(GL_TRIANGLE_STRIP)
\r
728 size = (self.parent.objectsMaxV - self.parent.objectsMinV)
\r
729 sizeXY = math.sqrt((size[0] * size[0]) + (size[1] * size[1]))
\r
730 for i in xrange(0, 64+1):
\r
731 f = i if i < 64/2 else 64 - i
\r
732 glColor4ub(255,int(f*255/(64/2)),0,128)
\r
733 glVertex3f(sizeXY * 0.7 * math.cos(i/32.0*math.pi), sizeXY * 0.7 * math.sin(i/32.0*math.pi),0.1)
\r
734 glColor4ub( 0,128,0,128)
\r
735 glVertex3f((sizeXY * 0.7 + 3) * math.cos(i/32.0*math.pi), (sizeXY * 0.7 + 3) * math.sin(i/32.0*math.pi),0.1)
\r
737 glEnable(GL_CULL_FACE)
\r
743 def drawModel(self, obj):
\r
744 multiX = 1 #int(profile.getProfileSetting('model_multiply_x'))
\r
745 multiY = 1 #int(profile.getProfileSetting('model_multiply_y'))
\r
746 modelScale = profile.getProfileSettingFloat('model_scale')
\r
747 modelSize = (obj.mesh.getMaximum() - obj.mesh.getMinimum()) * modelScale
\r
749 glRotate(self.tempRotate, 0, 0, 1)
\r
750 glTranslate(-(modelSize[0]+10)*(multiX-1)/2,-(modelSize[1]+10)*(multiY-1)/2, 0)
\r
751 for mx in xrange(0, multiX):
\r
752 for my in xrange(0, multiY):
\r
754 glTranslate((modelSize[0]+10)*mx,(modelSize[1]+10)*my, 0)
\r
755 glScalef(modelScale, modelScale, modelScale)
\r
756 glCallList(obj.displayList)
\r