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.OnResetAll, 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):
\r
141 x, y = self.glCanvas.ClientToScreenXY(0, 0)
\r
142 sx, sy = self.glCanvas.GetClientSizeTuple()
\r
143 self.warningPopup.SetPosition((x, y+sy-self.warningPopup.GetSize().height))
\r
145 def OnMulXAddClick(self, e):
\r
146 profile.putProfileSetting('model_multiply_x', str(max(1, int(profile.getProfileSetting('model_multiply_x'))+1)))
\r
147 self.glCanvas.Refresh()
\r
149 def OnMulXSubClick(self, e):
\r
150 profile.putProfileSetting('model_multiply_x', str(max(1, int(profile.getProfileSetting('model_multiply_x'))-1)))
\r
151 self.glCanvas.Refresh()
\r
153 def OnMulYAddClick(self, e):
\r
154 profile.putProfileSetting('model_multiply_y', str(max(1, int(profile.getProfileSetting('model_multiply_y'))+1)))
\r
155 self.glCanvas.Refresh()
\r
157 def OnMulYSubClick(self, e):
\r
158 profile.putProfileSetting('model_multiply_y', str(max(1, int(profile.getProfileSetting('model_multiply_y'))-1)))
\r
159 self.glCanvas.Refresh()
\r
161 def OnScaleReset(self, e):
\r
162 self.scale.SetValue('1.0')
\r
165 def OnScale(self, e):
\r
167 if self.scale.GetValue() != '':
\r
168 scale = self.scale.GetValue()
\r
169 profile.putProfileSetting('model_scale', scale)
\r
170 self.glCanvas.Refresh()
\r
172 def OnScaleMax(self, e):
\r
173 if self.objectsMinV == None:
\r
175 vMin = self.objectsMinV
\r
176 vMax = self.objectsMaxV
\r
177 scaleX1 = (self.machineSize.x - self.machineCenter.x) / ((vMax[0] - vMin[0]) / 2)
\r
178 scaleY1 = (self.machineSize.y - self.machineCenter.y) / ((vMax[1] - vMin[1]) / 2)
\r
179 scaleX2 = (self.machineCenter.x) / ((vMax[0] - vMin[0]) / 2)
\r
180 scaleY2 = (self.machineCenter.y) / ((vMax[1] - vMin[1]) / 2)
\r
181 scaleZ = self.machineSize.z / (vMax[2] - vMin[2])
\r
182 scale = min(scaleX1, scaleY1, scaleX2, scaleY2, scaleZ)
\r
183 self.scale.SetValue(str(scale))
\r
184 profile.putProfileSetting('model_scale', self.scale.GetValue())
\r
185 self.glCanvas.Refresh()
\r
187 def OnRotateReset(self, e):
\r
188 self.rotate.SetValue(0)
\r
189 self.OnRotate(None)
\r
191 def OnRotate(self, e):
\r
192 profile.putProfileSetting('model_rotate_base', self.rotate.GetValue())
\r
193 self.updateModelTransform()
\r
195 def On3DClick(self):
\r
196 self.glCanvas.yaw = 30
\r
197 self.glCanvas.pitch = 60
\r
198 self.glCanvas.zoom = 300
\r
199 self.glCanvas.view3D = True
\r
200 self.glCanvas.Refresh()
\r
202 def OnTopClick(self):
\r
203 self.glCanvas.view3D = False
\r
204 self.glCanvas.zoom = 100
\r
205 self.glCanvas.offsetX = 0
\r
206 self.glCanvas.offsetY = 0
\r
207 self.glCanvas.Refresh()
\r
209 def OnLayerNrChange(self, e):
\r
210 self.glCanvas.Refresh()
\r
212 def updateCenterX(self):
\r
213 self.machineCenter.x = profile.getProfileSettingFloat('machine_center_x')
\r
214 self.glCanvas.Refresh()
\r
216 def updateCenterY(self):
\r
217 self.machineCenter.y = profile.getProfileSettingFloat('machine_center_y')
\r
218 self.glCanvas.Refresh()
\r
220 def setViewMode(self, mode):
\r
221 if mode == "Normal":
\r
222 self.normalViewButton.SetValue(True)
\r
223 if mode == "GCode":
\r
224 self.gcodeViewButton.SetValue(True)
\r
225 self.glCanvas.viewMode = mode
\r
226 wx.CallAfter(self.glCanvas.Refresh)
\r
228 def loadModelFiles(self, filelist, showWarning = False):
\r
229 while len(filelist) > len(self.objectList):
\r
230 self.objectList.append(previewObject())
\r
231 for idx in xrange(len(filelist), len(self.objectList)):
\r
232 self.objectList[idx].mesh = None
\r
233 self.objectList[idx].filename = None
\r
234 for idx in xrange(0, len(filelist)):
\r
235 obj = self.objectList[idx]
\r
236 if obj.filename != filelist[idx]:
\r
237 obj.fileTime = None
\r
238 self.gcodeFileTime = None
\r
239 self.logFileTime = None
\r
240 obj.filename = filelist[idx]
\r
242 self.gcodeFilename = sliceRun.getExportFilename(filelist[0])
\r
243 #Do the STL file loading in a background thread so we don't block the UI.
\r
244 if self.loadThread != None and self.loadThread.isAlive():
\r
245 self.loadThread.join()
\r
246 self.loadThread = threading.Thread(target=self.doFileLoadThread)
\r
247 self.loadThread.daemon = True
\r
248 self.loadThread.start()
\r
251 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
252 self.warningPopup.Show(True)
\r
253 self.warningPopup.timer.Start(5000)
\r
255 def loadReModelFiles(self, filelist):
\r
256 #Only load this again if the filename matches the file we have already loaded (for auto loading GCode after slicing)
\r
257 for idx in xrange(0, len(filelist)):
\r
258 if self.objectList[idx].filename != filelist[idx]:
\r
260 self.loadModelFiles(filelist)
\r
263 def doFileLoadThread(self):
\r
264 for obj in self.objectList:
\r
265 if obj.filename != None and os.path.isfile(obj.filename) and obj.fileTime != os.stat(obj.filename).st_mtime:
\r
266 obj.ileTime = os.stat(obj.filename).st_mtime
\r
267 mesh = meshLoader.loadMesh(obj.filename)
\r
270 self.updateModelTransform()
\r
271 scale = profile.getProfileSettingFloat('model_scale')
\r
272 size = (self.objectsMaxV - self.objectsMinV) * scale
\r
273 if size[0] > self.machineSize.x or size[1] > self.machineSize.y or size[2] > self.machineSize.z:
\r
274 self.OnScaleMax(None)
\r
275 self.glCanvas.zoom = numpy.max(size) * 2.5
\r
276 self.errorList = []
\r
277 wx.CallAfter(self.updateToolbar)
\r
278 wx.CallAfter(self.glCanvas.Refresh)
\r
280 if os.path.isfile(self.gcodeFilename) and self.gcodeFileTime != os.stat(self.gcodeFilename).st_mtime:
\r
281 self.gcodeFileTime = os.stat(self.gcodeFilename).st_mtime
\r
282 gcode = gcodeInterpreter.gcode()
\r
283 gcode.progressCallback = self.loadProgress
\r
284 gcode.load(self.gcodeFilename)
\r
285 self.gcodeDirty = False
\r
287 self.gcodeDirty = True
\r
290 for line in open(self.gcodeFilename, "rt"):
\r
291 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
293 v1 = util3d.Vector3(float(res.group(2)), float(res.group(3)), float(res.group(4)))
\r
294 v2 = util3d.Vector3(float(res.group(5)), float(res.group(6)), float(res.group(7)))
\r
295 errorList.append([v1, v2])
\r
296 self.errorList = errorList
\r
298 wx.CallAfter(self.updateToolbar)
\r
299 wx.CallAfter(self.glCanvas.Refresh)
\r
300 elif not os.path.isfile(self.gcodeFilename):
\r
303 def loadProgress(self, progress):
\r
306 def OnResetAll(self, e):
\r
307 profile.putProfileSetting('model_scale', '1.0')
\r
308 profile.putProfileSetting('model_rotate_base', '0')
\r
309 profile.putProfileSetting('flip_x', 'False')
\r
310 profile.putProfileSetting('flip_y', 'False')
\r
311 profile.putProfileSetting('flip_z', 'False')
\r
312 profile.putProfileSetting('swap_xz', 'False')
\r
313 profile.putProfileSetting('swap_yz', 'False')
\r
314 self.updateProfileToControls()
\r
315 self.warningPopup.Show(False)
\r
316 self.warningPopup.timer.Stop()
\r
318 def OnHideWarning(self, e):
\r
319 self.warningPopup.Show(False)
\r
320 self.warningPopup.timer.Stop()
\r
322 def updateToolbar(self):
\r
323 self.gcodeViewButton.Show(self.gcode != None)
\r
324 self.mixedViewButton.Show(self.gcode != None)
\r
325 self.layerSpin.Show(self.glCanvas.viewMode == "GCode" or self.glCanvas.viewMode == "Mixed")
\r
326 if self.gcode != None:
\r
327 self.layerSpin.SetRange(1, len(self.gcode.layerList) - 1)
\r
328 self.toolbar.Realize()
\r
331 def OnViewChange(self):
\r
332 if self.normalViewButton.GetValue():
\r
333 self.glCanvas.viewMode = "Normal"
\r
334 elif self.transparentViewButton.GetValue():
\r
335 self.glCanvas.viewMode = "Transparent"
\r
336 elif self.xrayViewButton.GetValue():
\r
337 self.glCanvas.viewMode = "X-Ray"
\r
338 elif self.gcodeViewButton.GetValue():
\r
339 self.glCanvas.viewMode = "GCode"
\r
340 elif self.mixedViewButton.GetValue():
\r
341 self.glCanvas.viewMode = "Mixed"
\r
342 self.glCanvas.drawBorders = self.showBorderButton.GetValue()
\r
343 self.updateToolbar()
\r
344 self.glCanvas.Refresh()
\r
346 def updateModelTransform(self, f=0):
\r
347 if len(self.objectList) < 1 or self.objectList[0].mesh == None:
\r
350 rotate = profile.getProfileSettingFloat('model_rotate_base')
\r
351 mirrorX = profile.getProfileSetting('flip_x') == 'True'
\r
352 mirrorY = profile.getProfileSetting('flip_y') == 'True'
\r
353 mirrorZ = profile.getProfileSetting('flip_z') == 'True'
\r
354 swapXZ = profile.getProfileSetting('swap_xz') == 'True'
\r
355 swapYZ = profile.getProfileSetting('swap_yz') == 'True'
\r
357 for obj in self.objectList:
\r
358 if obj.mesh == None:
\r
360 obj.mesh.setRotateMirror(rotate, mirrorX, mirrorY, mirrorZ, swapXZ, swapYZ)
\r
362 minV = self.objectList[0].mesh.getMinimum()
\r
363 maxV = self.objectList[0].mesh.getMaximum()
\r
364 for obj in self.objectList:
\r
365 if obj.mesh == None:
\r
368 obj.mesh.getMinimumZ()
\r
369 minV = numpy.minimum(minV, obj.mesh.getMinimum())
\r
370 maxV = numpy.maximum(maxV, obj.mesh.getMaximum())
\r
372 self.objectsMaxV = maxV
\r
373 self.objectsMinV = minV
\r
374 for obj in self.objectList:
\r
375 if obj.mesh == None:
\r
378 obj.mesh.vertexes -= numpy.array([minV[0] + (maxV[0] - minV[0]) / 2, minV[1] + (maxV[1] - minV[1]) / 2, minV[2]])
\r
379 #for v in obj.mesh.vertexes:
\r
381 # v[0] -= minV[0] + (maxV[0] - minV[0]) / 2
\r
382 # v[1] -= minV[1] + (maxV[1] - minV[1]) / 2
\r
383 obj.mesh.getMinimumZ()
\r
385 self.glCanvas.Refresh()
\r
387 def updateProfileToControls(self):
\r
388 self.scale.SetValue(profile.getProfileSetting('model_scale'))
\r
389 self.rotate.SetValue(profile.getProfileSettingFloat('model_rotate_base'))
\r
390 self.mirrorX.SetValue(profile.getProfileSetting('flip_x') == 'True')
\r
391 self.mirrorY.SetValue(profile.getProfileSetting('flip_y') == 'True')
\r
392 self.mirrorZ.SetValue(profile.getProfileSetting('flip_z') == 'True')
\r
393 self.swapXZ.SetValue(profile.getProfileSetting('swap_xz') == 'True')
\r
394 self.swapYZ.SetValue(profile.getProfileSetting('swap_yz') == 'True')
\r
395 self.updateModelTransform()
\r
396 self.glCanvas.updateProfileToControls()
\r
398 class PreviewGLCanvas(glcanvas.GLCanvas):
\r
399 def __init__(self, parent):
\r
400 attribList = (glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24, glcanvas.WX_GL_STENCIL_SIZE, 8)
\r
401 glcanvas.GLCanvas.__init__(self, parent, attribList = attribList)
\r
402 self.parent = parent
\r
403 self.context = glcanvas.GLContext(self)
\r
404 wx.EVT_PAINT(self, self.OnPaint)
\r
405 wx.EVT_SIZE(self, self.OnSize)
\r
406 wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)
\r
407 wx.EVT_MOTION(self, self.OnMouseMotion)
\r
408 wx.EVT_MOUSEWHEEL(self, self.OnMouseWheel)
\r
415 self.gcodeDisplayList = None
\r
416 self.gcodeDisplayListMade = None
\r
417 self.gcodeDisplayListCount = 0
\r
418 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
422 self.tempRotate = 0
\r
424 def updateProfileToControls(self):
\r
425 self.objColor[0] = profile.getPreferenceColour('model_colour')
\r
426 self.objColor[1] = profile.getPreferenceColour('model_colour2')
\r
427 self.objColor[2] = profile.getPreferenceColour('model_colour3')
\r
428 self.objColor[3] = profile.getPreferenceColour('model_colour3')
\r
430 def OnMouseMotion(self,e):
\r
433 if self.parent.objectsMaxV != None:
\r
434 size = (self.parent.objectsMaxV - self.parent.objectsMinV)
\r
435 sizeXY = math.sqrt((size[0] * size[0]) + (size[1] * size[1]))
\r
437 p0 = numpy.array(gluUnProject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 0, self.modelMatrix, self.projMatrix, self.viewport))
\r
438 p1 = numpy.array(gluUnProject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 1, self.modelMatrix, self.projMatrix, self.viewport))
\r
439 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
\r
440 cursorXY = math.sqrt((cursorZ0[0] * cursorZ0[0]) + (cursorZ0[1] * cursorZ0[1]))
\r
441 if cursorXY >= sizeXY * 0.7 and cursorXY <= sizeXY * 0.7 + 3 and False:
\r
442 self.SetCursor(wx.StockCursor(wx.CURSOR_SIZING))
\r
444 self.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
\r
446 if e.Dragging() and e.LeftIsDown():
\r
447 if self.dragType == '':
\r
448 #Define the drag type depending on the cursor position.
\r
449 if cursorXY >= sizeXY * 0.7 and cursorXY <= sizeXY * 0.7 + 3 and False:
\r
450 self.dragType = 'modelRotate'
\r
451 self.dragStart = math.atan2(cursorZ0[0], cursorZ0[1])
\r
453 self.dragType = 'viewRotate'
\r
455 if self.dragType == 'viewRotate':
\r
457 self.yaw += e.GetX() - self.oldX
\r
458 self.pitch -= e.GetY() - self.oldY
\r
459 if self.pitch > 170:
\r
461 if self.pitch < 10:
\r
464 self.offsetX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2
\r
465 self.offsetY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2
\r
466 elif self.dragType == 'modelRotate':
\r
467 angle = math.atan2(cursorZ0[0], cursorZ0[1])
\r
468 diff = self.dragStart - angle
\r
469 self.tempRotate = diff * 180 / math.pi
\r
470 #Workaround for buggy ATI cards.
\r
471 size = self.GetSizeTuple()
\r
472 self.SetSize((size[0]+1, size[1]))
\r
473 self.SetSize((size[0], size[1]))
\r
476 if self.tempRotate != 0:
\r
477 profile.putProfileSetting('model_rotate_base', profile.getProfileSettingFloat('model_rotate_base') + self.tempRotate)
\r
478 self.parent.updateModelTransform()
\r
479 self.tempRotate = 0
\r
482 if e.Dragging() and e.RightIsDown():
\r
483 self.zoom += e.GetY() - self.oldY
\r
486 if self.zoom > 500:
\r
489 self.oldX = e.GetX()
\r
490 self.oldY = e.GetY()
\r
495 def OnMouseWheel(self,e):
\r
496 self.zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0
\r
497 if self.zoom < 1.0:
\r
499 if self.zoom > 500:
\r
503 def OnEraseBackground(self,event):
\r
504 #Workaround for windows background redraw flicker.
\r
507 def OnSize(self,e):
\r
510 def OnPaint(self,e):
\r
511 dc = wx.PaintDC(self)
\r
512 if not hasOpenGLlibs:
\r
514 dc.DrawText("No PyOpenGL installation found.\nNo preview window available.", 10, 10)
\r
516 self.SetCurrent(self.context)
\r
517 opengl.InitGL(self, self.view3D, self.zoom)
\r
519 glTranslate(0,0,-self.zoom)
\r
520 glRotate(-self.pitch, 1,0,0)
\r
521 glRotate(self.yaw, 0,0,1)
\r
522 if self.viewMode == "GCode" or self.viewMode == "Mixed":
\r
523 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
524 glTranslate(0,0,-self.parent.gcode.layerList[self.parent.layerSpin.GetValue()][0].list[-1].z)
\r
526 if self.parent.objectsMaxV != None:
\r
527 glTranslate(0,0,-(self.parent.objectsMaxV[2]-self.parent.objectsMinV[2]) * profile.getProfileSettingFloat('model_scale') / 2)
\r
529 glScale(1.0/self.zoom, 1.0/self.zoom, 1.0)
\r
530 glTranslate(self.offsetX, self.offsetY, 0.0)
\r
532 self.viewport = glGetIntegerv(GL_VIEWPORT);
\r
533 self.modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX);
\r
534 self.projMatrix = glGetDoublev(GL_PROJECTION_MATRIX);
\r
536 glTranslate(-self.parent.machineCenter.x, -self.parent.machineCenter.y, 0)
\r
542 machineSize = self.parent.machineSize
\r
544 if self.parent.gcode != None and self.parent.gcodeDirty:
\r
545 if self.gcodeDisplayListCount < len(self.parent.gcode.layerList) or self.gcodeDisplayList == None:
\r
546 if self.gcodeDisplayList != None:
\r
547 glDeleteLists(self.gcodeDisplayList, self.gcodeDisplayListCount)
\r
548 self.gcodeDisplayList = glGenLists(len(self.parent.gcode.layerList));
\r
549 self.gcodeDisplayListCount = len(self.parent.gcode.layerList)
\r
550 self.parent.gcodeDirty = False
\r
551 self.gcodeDisplayListMade = 0
\r
553 if self.parent.gcode != None and self.gcodeDisplayListMade < len(self.parent.gcode.layerList):
\r
554 glNewList(self.gcodeDisplayList + self.gcodeDisplayListMade, GL_COMPILE)
\r
555 opengl.DrawGCodeLayer(self.parent.gcode.layerList[self.gcodeDisplayListMade])
\r
557 self.gcodeDisplayListMade += 1
\r
561 glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)
\r
562 for obj in self.parent.objectList:
\r
563 if obj.mesh == None:
\r
565 if obj.displayList == None:
\r
566 obj.displayList = glGenLists(1);
\r
569 glNewList(obj.displayList, GL_COMPILE)
\r
570 opengl.DrawMesh(obj.mesh)
\r
573 if self.viewMode == "Mixed":
\r
574 glDisable(GL_BLEND)
\r
575 glColor3f(0.0,0.0,0.0)
\r
576 self.drawModel(obj)
\r
577 glColor3f(1.0,1.0,1.0)
\r
578 glClear(GL_DEPTH_BUFFER_BIT)
\r
582 if self.parent.gcode != None and (self.viewMode == "GCode" or self.viewMode == "Mixed"):
\r
583 glEnable(GL_COLOR_MATERIAL)
\r
584 glEnable(GL_LIGHTING)
\r
585 drawUpToLayer = min(self.gcodeDisplayListMade, self.parent.layerSpin.GetValue() + 1)
\r
586 starttime = time.time()
\r
587 for i in xrange(drawUpToLayer - 1, -1, -1):
\r
589 if i < self.parent.layerSpin.GetValue():
\r
590 c = 0.9 - (drawUpToLayer - i) * 0.1
\r
595 glLightfv(GL_LIGHT0, GL_DIFFUSE, [0,0,0,0])
\r
596 glLightfv(GL_LIGHT0, GL_AMBIENT, [c,c,c,c])
\r
597 glCallList(self.gcodeDisplayList + i)
\r
598 if time.time() - starttime > 0.1:
\r
601 glDisable(GL_LIGHTING)
\r
602 glDisable(GL_COLOR_MATERIAL)
\r
603 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, [0.2, 0.2, 0.2, 1.0]);
\r
604 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, [0.8, 0.8, 0.8, 1.0]);
\r
606 glColor3f(1.0,1.0,1.0)
\r
608 glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)
\r
609 for obj in self.parent.objectList:
\r
610 if obj.mesh == None:
\r
613 if self.viewMode == "Transparent" or self.viewMode == "Mixed":
\r
614 glLightfv(GL_LIGHT0, GL_DIFFUSE, map(lambda x: x / 2, self.objColor[self.parent.objectList.index(obj)]))
\r
615 glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x / 10, self.objColor[self.parent.objectList.index(obj)]))
\r
616 #If we want transparent, then first render a solid black model to remove the printer size lines.
\r
617 if self.viewMode != "Mixed":
\r
618 glDisable(GL_BLEND)
\r
619 glColor3f(0.0,0.0,0.0)
\r
620 self.drawModel(obj)
\r
621 glColor3f(1.0,1.0,1.0)
\r
622 #After the black model is rendered, render the model again but now with lighting and no depth testing.
\r
623 glDisable(GL_DEPTH_TEST)
\r
624 glEnable(GL_LIGHTING)
\r
626 glBlendFunc(GL_ONE, GL_ONE)
\r
627 glEnable(GL_LIGHTING)
\r
628 self.drawModel(obj)
\r
629 glEnable(GL_DEPTH_TEST)
\r
630 elif self.viewMode == "X-Ray":
\r
631 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
\r
632 glDisable(GL_LIGHTING)
\r
633 glDisable(GL_DEPTH_TEST)
\r
634 glEnable(GL_STENCIL_TEST)
\r
635 glStencilFunc(GL_ALWAYS, 1, 1)
\r
636 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
\r
637 self.drawModel(obj)
\r
638 glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
\r
640 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
\r
641 glStencilFunc(GL_EQUAL, 0, 1)
\r
643 self.drawModel(obj)
\r
644 glStencilFunc(GL_EQUAL, 1, 1)
\r
646 self.drawModel(obj)
\r
650 for i in xrange(2, 15, 2):
\r
651 glStencilFunc(GL_EQUAL, i, 0xFF);
\r
652 glColor(float(i)/10, float(i)/10, float(i)/5)
\r
654 glVertex3f(-1000,-1000,-1)
\r
655 glVertex3f( 1000,-1000,-1)
\r
656 glVertex3f( 1000, 1000,-1)
\r
657 glVertex3f(-1000, 1000,-1)
\r
659 for i in xrange(1, 15, 2):
\r
660 glStencilFunc(GL_EQUAL, i, 0xFF);
\r
661 glColor(float(i)/10, 0, 0)
\r
663 glVertex3f(-1000,-1000,-1)
\r
664 glVertex3f( 1000,-1000,-1)
\r
665 glVertex3f( 1000, 1000,-1)
\r
666 glVertex3f(-1000, 1000,-1)
\r
670 glDisable(GL_STENCIL_TEST)
\r
671 glEnable(GL_DEPTH_TEST)
\r
673 #Fix the depth buffer
\r
674 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
\r
675 self.drawModel(obj)
\r
676 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
\r
677 elif self.viewMode == "Normal":
\r
678 glLightfv(GL_LIGHT0, GL_DIFFUSE, self.objColor[self.parent.objectList.index(obj)])
\r
679 glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x / 5, self.objColor[self.parent.objectList.index(obj)]))
\r
680 glEnable(GL_LIGHTING)
\r
681 self.drawModel(obj)
\r
683 if self.drawBorders and (self.viewMode == "Normal" or self.viewMode == "Transparent" or self.viewMode == "X-Ray"):
\r
684 glEnable(GL_DEPTH_TEST)
\r
685 glDisable(GL_LIGHTING)
\r
688 modelScale = profile.getProfileSettingFloat('model_scale')
\r
689 glScalef(modelScale, modelScale, modelScale)
\r
690 opengl.DrawMeshOutline(obj.mesh)
\r
694 if self.viewMode == "Normal" or self.viewMode == "Transparent" or self.viewMode == "X-Ray":
\r
695 glDisable(GL_LIGHTING)
\r
696 glDisable(GL_DEPTH_TEST)
\r
697 glDisable(GL_BLEND)
\r
700 for err in self.parent.errorList:
\r
701 glVertex3f(err[0].x, err[0].y, err[0].z)
\r
702 glVertex3f(err[1].x, err[1].y, err[1].z)
\r
704 glEnable(GL_DEPTH_TEST)
\r
706 opengl.DrawMachine(machineSize)
\r
709 glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)
\r
711 #Draw the rotate circle
\r
712 if self.parent.objectsMaxV != None and False:
\r
713 glDisable(GL_LIGHTING)
\r
714 glDisable(GL_CULL_FACE)
\r
716 glBegin(GL_TRIANGLE_STRIP)
\r
717 size = (self.parent.objectsMaxV - self.parent.objectsMinV)
\r
718 sizeXY = math.sqrt((size[0] * size[0]) + (size[1] * size[1]))
\r
719 for i in xrange(0, 64+1):
\r
720 f = i if i < 64/2 else 64 - i
\r
721 glColor4ub(255,int(f*255/(64/2)),0,128)
\r
722 glVertex3f(sizeXY * 0.7 * math.cos(i/32.0*math.pi), sizeXY * 0.7 * math.sin(i/32.0*math.pi),0.1)
\r
723 glColor4ub( 0,128,0,128)
\r
724 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
726 glEnable(GL_CULL_FACE)
\r
732 def drawModel(self, obj):
\r
733 multiX = 1 #int(profile.getProfileSetting('model_multiply_x'))
\r
734 multiY = 1 #int(profile.getProfileSetting('model_multiply_y'))
\r
735 modelScale = profile.getProfileSettingFloat('model_scale')
\r
736 modelSize = (obj.mesh.getMaximum() - obj.mesh.getMinimum()) * modelScale
\r
738 glRotate(self.tempRotate, 0, 0, 1)
\r
739 glTranslate(-(modelSize[0]+10)*(multiX-1)/2,-(modelSize[1]+10)*(multiY-1)/2, 0)
\r
740 for mx in xrange(0, multiX):
\r
741 for my in xrange(0, multiY):
\r
743 glTranslate((modelSize[0]+10)*mx,(modelSize[1]+10)*my, 0)
\r
744 glScalef(modelScale, modelScale, modelScale)
\r
745 glCallList(obj.displayList)
\r