chiark / gitweb /
Fixing #328
[cura.git] / Cura / gui / preview3d.py
1 from __future__ import absolute_import
2 from __future__ import division
3
4 import math
5 import threading
6 import re
7 import time
8 import os
9 import numpy
10
11 from wx import glcanvas
12 import wx
13 try:
14         import OpenGL
15         OpenGL.ERROR_CHECKING = False
16         from OpenGL.GLU import *
17         from OpenGL.GL import *
18         hasOpenGLlibs = True
19 except:
20         print "Failed to find PyOpenGL: http://pyopengl.sourceforge.net/"
21         hasOpenGLlibs = False
22
23 from Cura.gui.util import opengl
24 from Cura.gui.util import toolbarUtil
25
26 from Cura.util import profile
27 from Cura.util import gcodeInterpreter
28 from Cura.util import meshLoader
29 from Cura.util import util3d
30 from Cura.util import sliceRun
31
32 class previewObject():
33         def __init__(self):
34                 self.mesh = None
35                 self.filename = None
36                 self.displayList = None
37                 self.dirty = False
38
39 class toolInfo(object):
40         def __init__(self, parent):
41                 self.parent = parent
42
43         def OnMouseMove(self, p0, p1):
44                 pass
45
46         def OnDragStart(self, p0, p1):
47                 return False
48
49         def OnDrag(self, p0, p1):
50                 pass
51
52         def OnDragEnd(self):
53                 pass
54
55         def OnDraw(self):
56                 glDisable(GL_LIGHTING)
57                 glDisable(GL_BLEND)
58                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
59                 glColor3ub(0,0,0)
60                 size = self.parent.getObjectSize()
61                 radius = self.parent.getObjectBoundaryCircle()
62                 glPushMatrix()
63                 glTranslate(0,0,size[2]/2 + 5)
64                 glRotate(-self.parent.yaw, 0,0,1)
65                 if self.parent.pitch < 80:
66                         glTranslate(0, radius + 5,0)
67                 elif self.parent.pitch < 100:
68                         glTranslate(0, (radius + 5) * (90 - self.parent.pitch) / 10,0)
69                 else:
70                         glTranslate(0,-(radius + 5),0)
71                 opengl.glDrawStringCenter("%dx%dx%d" % (size[0], size[1], size[2]))
72                 glPopMatrix()
73
74                 glColor(255,255,255)
75                 size = size / 2
76                 glBegin(GL_LINES)
77                 glVertex3f(size[0], size[1], size[2])
78                 glVertex3f(size[0], size[1], size[2]/4*3)
79                 glVertex3f(size[0], size[1], size[2])
80                 glVertex3f(size[0], size[1]/4*3, size[2])
81                 glVertex3f(size[0], size[1], size[2])
82                 glVertex3f(size[0]/4*3, size[1], size[2])
83
84                 glVertex3f(-size[0], -size[1], -size[2])
85                 glVertex3f(-size[0], -size[1], -size[2]/4*3)
86                 glVertex3f(-size[0], -size[1], -size[2])
87                 glVertex3f(-size[0], -size[1]/4*3, -size[2])
88                 glVertex3f(-size[0], -size[1], -size[2])
89                 glVertex3f(-size[0]/4*3, -size[1], -size[2])
90                 glEnd()
91
92 class toolRotate(object):
93         def __init__(self, parent):
94                 self.parent = parent
95                 self.rotateRingDist = 1.5
96                 self.dragPlane = None
97                 self.dragStartAngle = None
98                 self.dragEndAngle = None
99
100         def _ProjectToPlanes(self, p0, p1):
101                 pp0 = p0 - [0,0,self.parent.getObjectSize()[2]/2]
102                 pp1 = p1 - [0,0,self.parent.getObjectSize()[2]/2]
103                 cursorX0 = pp0 - (pp1 - pp0) * (pp0[0] / (pp1[0] - pp0[0]))
104                 cursorY0 = pp0 - (pp1 - pp0) * (pp0[1] / (pp1[1] - pp0[1]))
105                 cursorZ0 = pp0 - (pp1 - pp0) * (pp0[2] / (pp1[2] - pp0[2]))
106                 cursorYZ = math.sqrt((cursorX0[1] * cursorX0[1]) + (cursorX0[2] * cursorX0[2]))
107                 cursorXZ = math.sqrt((cursorY0[0] * cursorY0[0]) + (cursorY0[2] * cursorY0[2]))
108                 cursorXY = math.sqrt((cursorZ0[0] * cursorZ0[0]) + (cursorZ0[1] * cursorZ0[1]))
109                 return cursorX0, cursorY0, cursorZ0, cursorYZ, cursorXZ, cursorXY
110
111         def OnMouseMove(self, p0, p1):
112                 radius = self.parent.getObjectBoundaryCircle()
113                 cursorX0, cursorY0, cursorZ0, cursorYZ, cursorXZ, cursorXY = self._ProjectToPlanes(p0, p1)
114                 oldDragPlane = self.dragPlane
115                 if radius * (self.rotateRingDist - 0.1) <= cursorXY <= radius * (self.rotateRingDist + 0.1) or radius * (self.rotateRingDist - 0.1) <= cursorYZ <= radius * (self.rotateRingDist + 0.1) or radius * (self.rotateRingDist - 0.1) <= cursorXZ <= radius * (self.rotateRingDist + 0.1):
116                         self.parent.SetCursor(wx.StockCursor(wx.CURSOR_SIZING))
117                         if self.dragStartAngle is None:
118                                 if radius * (self.rotateRingDist - 0.1) <= cursorXY <= radius * (self.rotateRingDist + 0.1):
119                                         self.dragPlane = 'XY'
120                                 elif radius * (self.rotateRingDist - 0.1) <= cursorXZ <= radius * (self.rotateRingDist + 0.1):
121                                         self.dragPlane = 'XZ'
122                                 else:
123                                         self.dragPlane = 'YZ'
124                 else:
125                         if self.dragStartAngle is None:
126                                 self.dragPlane = ''
127                         self.parent.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
128                 if self.dragPlane != oldDragPlane:
129                         self.parent.Refresh()
130
131         def OnDragStart(self, p0, p1):
132                 radius = self.parent.getObjectBoundaryCircle()
133                 cursorX0, cursorY0, cursorZ0, cursorYZ, cursorXZ, cursorXY = self._ProjectToPlanes(p0, p1)
134                 if radius * (self.rotateRingDist - 0.1) <= cursorXY <= radius * (self.rotateRingDist + 0.1) or radius * (self.rotateRingDist - 0.1) <= cursorYZ <= radius * (self.rotateRingDist + 0.1) or radius * (self.rotateRingDist - 0.1) <= cursorXZ <= radius * (self.rotateRingDist + 0.1):
135                         if radius * (self.rotateRingDist - 0.1) <= cursorXY <= radius * (self.rotateRingDist + 0.1):
136                                 self.dragPlane = 'XY'
137                                 self.dragStartAngle = math.atan2(cursorZ0[1], cursorZ0[0]) * 180 / math.pi
138                         elif radius * (self.rotateRingDist - 0.1) <= cursorXZ <= radius * (self.rotateRingDist + 0.1):
139                                 self.dragPlane = 'XZ'
140                                 self.dragStartAngle = math.atan2(cursorY0[2], cursorY0[0]) * 180 / math.pi
141                         else:
142                                 self.dragPlane = 'YZ'
143                                 self.dragStartAngle = math.atan2(cursorX0[2], cursorX0[1]) * 180 / math.pi
144                         return True
145                 return False
146
147         def OnDrag(self, p0, p1):
148                 cursorX0, cursorY0, cursorZ0, cursorYZ, cursorXZ, cursorXY = self._ProjectToPlanes(p0, p1)
149                 if self.dragPlane == 'XY':
150                         angle = math.atan2(cursorZ0[1], cursorZ0[0]) * 180 / math.pi
151                 elif self.dragPlane == 'XZ':
152                         angle = math.atan2(cursorY0[2], cursorY0[0]) * 180 / math.pi
153                 else:
154                         angle = math.atan2(cursorX0[2], cursorX0[1]) * 180 / math.pi
155                 diff = angle - self.dragStartAngle
156                 if wx.GetKeyState(wx.WXK_SHIFT):
157                         diff = round(diff / 1) * 1
158                 else:
159                         diff = round(diff / 15) * 15
160                 if diff > 180:
161                         diff -= 360
162                 if diff < -180:
163                         diff += 360
164                 rad = diff / 180.0 * math.pi
165                 self.dragEndAngle = self.dragStartAngle + diff
166                 if self.dragPlane == 'XY':
167                         self.parent.tempMatrix = numpy.matrix([[math.cos(rad), math.sin(rad), 0], [-math.sin(rad), math.cos(rad), 0], [0,0,1]], numpy.float64)
168                 elif self.dragPlane == 'XZ':
169                         self.parent.tempMatrix = numpy.matrix([[math.cos(rad), 0, math.sin(rad)], [0,1,0], [-math.sin(rad), 0, math.cos(rad)]], numpy.float64)
170                 else:
171                         self.parent.tempMatrix = numpy.matrix([[1,0,0], [0, math.cos(rad), math.sin(rad)], [0, -math.sin(rad), math.cos(rad)]], numpy.float64)
172
173         def OnDragEnd(self):
174                 self.dragStartAngle = None
175
176         def OnDraw(self):
177                 glDisable(GL_LIGHTING)
178                 glDisable(GL_BLEND)
179                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
180                 radius = self.parent.getObjectBoundaryCircle()
181                 glScalef(self.rotateRingDist * radius, self.rotateRingDist * radius, self.rotateRingDist * radius)
182                 if self.dragPlane == 'XY':
183                         glColor4ub(255,64,64,255)
184                         if self.dragStartAngle is not None:
185                                 glPushMatrix()
186                                 glRotate(self.dragStartAngle, 0,0,1)
187                                 glBegin(GL_LINES)
188                                 glVertex3f(0,0,0)
189                                 glVertex3f(1,0,0)
190                                 glEnd()
191                                 glPopMatrix()
192                                 glPushMatrix()
193                                 glRotate(self.dragEndAngle, 0,0,1)
194                                 glBegin(GL_LINES)
195                                 glVertex3f(0,0,0)
196                                 glVertex3f(1,0,0)
197                                 glEnd()
198                                 glTranslatef(1.1,0,0)
199                                 glColor4ub(0,0,0,255)
200                                 opengl.glDrawStringCenter("%d" % (abs(self.dragEndAngle - self.dragStartAngle)))
201                                 glColor4ub(255,64,64,255)
202                                 glPopMatrix()
203                 else:
204                         glColor4ub(128,0,0,255)
205                 glBegin(GL_LINE_LOOP)
206                 for i in xrange(0, 64):
207                         glVertex3f(math.cos(i/32.0*math.pi), math.sin(i/32.0*math.pi),0)
208                 glEnd()
209                 if self.dragPlane == 'YZ':
210                         glColor4ub(64,255,64,255)
211                         if self.dragStartAngle is not None:
212                                 glPushMatrix()
213                                 glRotate(self.dragStartAngle, 1,0,0)
214                                 glBegin(GL_LINES)
215                                 glVertex3f(0,0,0)
216                                 glVertex3f(0,1,0)
217                                 glEnd()
218                                 glPopMatrix()
219                                 glPushMatrix()
220                                 glRotate(self.dragEndAngle, 1,0,0)
221                                 glBegin(GL_LINES)
222                                 glVertex3f(0,0,0)
223                                 glVertex3f(0,1,0)
224                                 glEnd()
225                                 glTranslatef(0,1.1,0)
226                                 glColor4ub(0,0,0,255)
227                                 opengl.glDrawStringCenter("%d" % (abs(self.dragEndAngle - self.dragStartAngle)))
228                                 glColor4ub(64,255,64,255)
229                                 glPopMatrix()
230                 else:
231                         glColor4ub(0,128,0,255)
232                 glBegin(GL_LINE_LOOP)
233                 for i in xrange(0, 64):
234                         glVertex3f(0, math.cos(i/32.0*math.pi), math.sin(i/32.0*math.pi))
235                 glEnd()
236                 if self.dragPlane == 'XZ':
237                         glColor4ub(255,255,0,255)
238                         if self.dragStartAngle is not None:
239                                 glPushMatrix()
240                                 glRotate(self.dragStartAngle, 0,-1,0)
241                                 glBegin(GL_LINES)
242                                 glVertex3f(0,0,0)
243                                 glVertex3f(1,0,0)
244                                 glEnd()
245                                 glPopMatrix()
246                                 glPushMatrix()
247                                 glRotate(self.dragEndAngle, 0,-1,0)
248                                 glBegin(GL_LINES)
249                                 glVertex3f(0,0,0)
250                                 glVertex3f(1,0,0)
251                                 glEnd()
252                                 glTranslatef(1.1,0,0)
253                                 glColor4ub(0,0,0,255)
254                                 opengl.glDrawStringCenter("%d" % (abs(self.dragEndAngle - self.dragStartAngle)))
255                                 glColor4ub(255,255,0,255)
256                                 glPopMatrix()
257                 else:
258                         glColor4ub(128,128,0,255)
259                 glBegin(GL_LINE_LOOP)
260                 for i in xrange(0, 64):
261                         glVertex3f(math.cos(i/32.0*math.pi), 0, math.sin(i/32.0*math.pi))
262                 glEnd()
263
264 class toolScale(object):
265         def __init__(self, parent):
266                 self.parent = parent
267
268         def OnMouseMove(self, p0, p1):
269                 pass
270
271         def OnDragStart(self, p0, p1):
272                 pass
273
274         def OnDrag(self, p0, p1):
275                 pass
276
277         def OnDragEnd(self):
278                 pass
279
280         def OnDraw(self):
281                 glDisable(GL_LIGHTING)
282                 size = self.parent.getObjectSize() / 2
283                 opengl.DrawBox(-size, size)
284
285 class previewPanel(wx.Panel):
286         def __init__(self, parent):
287                 super(previewPanel, self).__init__(parent,-1)
288                 
289                 self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DDKSHADOW))
290                 self.SetMinSize((440,320))
291                 
292                 self.objectList = []
293                 self.errorList = []
294                 self.gcode = None
295                 self.objectsMinV = None
296                 self.objectsMaxV = None
297                 self.objectsBoundaryCircleSize = None
298                 self.loadThread = None
299                 self.machineSize = util3d.Vector3(profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height'))
300                 self.machineCenter = util3d.Vector3(self.machineSize.x / 2, self.machineSize.y / 2, 0)
301
302                 self.glCanvas = PreviewGLCanvas(self)
303                 #Create the popup window
304                 self.warningPopup = wx.PopupWindow(self, flags=wx.BORDER_SIMPLE)
305                 self.warningPopup.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_INFOBK))
306                 self.warningPopup.text = wx.StaticText(self.warningPopup, -1, 'Reset scale, rotation and mirror?')
307                 self.warningPopup.yesButton = wx.Button(self.warningPopup, -1, 'yes', style=wx.BU_EXACTFIT)
308                 self.warningPopup.noButton = wx.Button(self.warningPopup, -1, 'no', style=wx.BU_EXACTFIT)
309                 self.warningPopup.sizer = wx.BoxSizer(wx.HORIZONTAL)
310                 self.warningPopup.SetSizer(self.warningPopup.sizer)
311                 self.warningPopup.sizer.Add(self.warningPopup.text, 1, flag=wx.ALL|wx.ALIGN_CENTER_VERTICAL, border=1)
312                 self.warningPopup.sizer.Add(self.warningPopup.yesButton, 0, flag=wx.EXPAND|wx.ALL, border=1)
313                 self.warningPopup.sizer.Add(self.warningPopup.noButton, 0, flag=wx.EXPAND|wx.ALL, border=1)
314                 self.warningPopup.Fit()
315                 self.warningPopup.Layout()
316                 self.warningPopup.timer = wx.Timer(self)
317                 self.Bind(wx.EVT_TIMER, self.OnHideWarning, self.warningPopup.timer)
318                 
319                 self.Bind(wx.EVT_BUTTON, self.OnWarningPopup, self.warningPopup.yesButton)
320                 self.Bind(wx.EVT_BUTTON, self.OnHideWarning, self.warningPopup.noButton)
321                 parent.Bind(wx.EVT_MOVE, self.OnMove)
322                 parent.Bind(wx.EVT_SIZE, self.OnMove)
323                 
324                 self.toolbar = toolbarUtil.Toolbar(self)
325
326                 group = []
327                 toolbarUtil.RadioButton(self.toolbar, group, 'object-3d-on.png', 'object-3d-off.png', '3D view', callback=self.On3DClick)
328                 toolbarUtil.RadioButton(self.toolbar, group, 'object-top-on.png', 'object-top-off.png', 'Topdown view', callback=self.OnTopClick)
329                 self.toolbar.AddSeparator()
330
331                 self.showBorderButton = toolbarUtil.ToggleButton(self.toolbar, '', 'view-border-on.png', 'view-border-off.png', 'Show model borders', callback=self.OnViewChange)
332                 self.showSteepOverhang = toolbarUtil.ToggleButton(self.toolbar, '', 'steepOverhang-on.png', 'steepOverhang-off.png', 'Show steep overhang', callback=self.OnViewChange)
333                 self.toolbar.AddSeparator()
334
335                 group = []
336                 self.normalViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-normal-on.png', 'view-normal-off.png', 'Normal model view', callback=self.OnViewChange)
337                 self.transparentViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-transparent-on.png', 'view-transparent-off.png', 'Transparent model view', callback=self.OnViewChange)
338                 self.xrayViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-xray-on.png', 'view-xray-off.png', 'X-Ray view', callback=self.OnViewChange)
339                 self.gcodeViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-gcode-on.png', 'view-gcode-off.png', 'GCode view', callback=self.OnViewChange)
340                 self.mixedViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-mixed-on.png', 'view-mixed-off.png', 'Mixed model/GCode view', callback=self.OnViewChange)
341                 self.toolbar.AddSeparator()
342
343                 self.layerSpin = wx.SpinCtrl(self.toolbar, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS)
344                 self.toolbar.AddControl(self.layerSpin)
345                 self.Bind(wx.EVT_SPINCTRL, self.OnLayerNrChange, self.layerSpin)
346
347                 self.toolbar2 = toolbarUtil.Toolbar(self)
348
349                 group = []
350                 self.infoToolButton = toolbarUtil.RadioButton(self.toolbar2, group, 'object-rotate.png', 'object-rotate.png', 'Object info', callback=self.OnToolChange)
351                 self.rotateToolButton = toolbarUtil.RadioButton(self.toolbar2, group, 'object-rotate.png', 'object-rotate.png', 'Rotate object', callback=self.OnToolChange)
352                 self.scaleToolButton = toolbarUtil.RadioButton(self.toolbar2, group, 'object-scale.png', 'object-scale.png', 'Scale object', callback=self.OnToolChange)
353                 self.mirrorToolButton = toolbarUtil.RadioButton(self.toolbar2, group, 'object-mirror-x-on.png', 'object-mirror-x-off.png', 'Mirror object', callback=self.OnToolChange)
354                 self.toolbar2.AddSeparator()
355                 # Mirror
356                 self.mirrorX = toolbarUtil.NormalButton(self.toolbar2, self.OnMirrorX, 'object-mirror-x-on.png', 'Mirror X')
357                 self.mirrorY = toolbarUtil.NormalButton(self.toolbar2, self.OnMirrorY, 'object-mirror-y-on.png', 'Mirror Y')
358                 self.mirrorZ = toolbarUtil.NormalButton(self.toolbar2, self.OnMirrorZ, 'object-mirror-z-on.png', 'Mirror Z')
359                 self.toolbar2.AddSeparator()
360
361                 # Scale
362                 self.scaleReset = toolbarUtil.NormalButton(self.toolbar2, self.OnScaleReset, 'object-scale.png', 'Reset model scale')
363                 self.scale = wx.TextCtrl(self.toolbar2, -1, profile.getProfileSetting('model_scale'), size=(21*2,21))
364                 self.toolbar2.AddControl(self.scale)
365                 self.scale.Bind(wx.EVT_TEXT, self.OnScale)
366                 self.scaleMax = toolbarUtil.NormalButton(self.toolbar2, self.OnScaleMax, 'object-max-size.png', 'Scale object to fit machine size')
367
368                 self.toolbar2.AddSeparator()
369
370                 # Rotate
371                 self.rotateReset = toolbarUtil.NormalButton(self.toolbar2, self.OnRotateReset, 'object-rotate.png', 'Reset model rotation')
372                 self.layFlat = toolbarUtil.NormalButton(self.toolbar2, self.OnLayFlat, 'object-rotate.png', 'Lay flat')
373
374                 self.toolbar2.Realize()
375                 self.OnViewChange()
376                 
377                 sizer = wx.BoxSizer(wx.VERTICAL)
378                 sizer.Add(self.toolbar, 0, flag=wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, border=1)
379                 sizer.Add(self.glCanvas, 1, flag=wx.EXPAND)
380                 sizer.Add(self.toolbar2, 0, flag=wx.EXPAND|wx.BOTTOM|wx.LEFT|wx.RIGHT, border=1)
381                 self.SetSizer(sizer)
382                 
383                 self.checkReloadFileTimer = wx.Timer(self)
384                 self.Bind(wx.EVT_TIMER, self.OnCheckReloadFile, self.checkReloadFileTimer)
385                 self.checkReloadFileTimer.Start(1000)
386
387                 self.matrix = numpy.matrix(numpy.array(profile.getObjectMatrix(), numpy.float64).reshape((3,3,)))
388                 self.tool = toolInfo(self.glCanvas)
389         
390         def returnToModelViewAndUpdateModel(self):
391                 if self.glCanvas.viewMode == 'GCode' or self.glCanvas.viewMode == 'Mixed':
392                         self.setViewMode('Normal')
393                 self.updateModelTransform()
394
395         def OnToolChange(self):
396                 if self.infoToolButton.GetValue():
397                         self.tool = toolInfo(self.glCanvas)
398                 if self.rotateToolButton.GetValue():
399                         self.tool = toolRotate(self.glCanvas)
400                 if self.scaleToolButton.GetValue():
401                         self.tool = toolScale(self.glCanvas)
402                 self.returnToModelViewAndUpdateModel()
403
404         def OnMirrorX(self, e):
405                 self.matrix *= numpy.matrix([[-1,0,0],[0,1,0],[0,0,1]], numpy.float64)
406                 self.returnToModelViewAndUpdateModel()
407
408         def OnMirrorY(self, e):
409                 self.matrix *= numpy.matrix([[1,0,0],[0,-1,0],[0,0,1]], numpy.float64)
410                 self.returnToModelViewAndUpdateModel()
411
412         def OnMirrorZ(self, e):
413                 self.matrix *= numpy.matrix([[1,0,0],[0,1,0],[0,0,-1]], numpy.float64)
414                 self.returnToModelViewAndUpdateModel()
415
416         def OnMove(self, e = None):
417                 if e is not None:
418                         e.Skip()
419                 x, y = self.glCanvas.ClientToScreenXY(0, 0)
420                 sx, sy = self.glCanvas.GetClientSizeTuple()
421                 self.warningPopup.SetPosition((x, y+sy-self.warningPopup.GetSize().height))
422
423         def OnScaleReset(self, e):
424                 self.scale.SetValue('1.0')
425                 self.OnScale(None)
426
427         def OnScale(self, e):
428                 scale = 1.0
429                 if self.scale.GetValue() != '':
430                         scale = self.scale.GetValue()
431                 profile.putProfileSetting('model_scale', scale)
432                 if self.glCanvas.viewMode == 'GCode' or self.glCanvas.viewMode == 'Mixed':
433                         self.setViewMode('Normal')
434                 self.glCanvas.Refresh()
435
436         def OnScaleMax(self, e = None, onlyScaleDown = False):
437                 if self.objectsMinV is None:
438                         return
439                 vMin = self.objectsMinV
440                 vMax = self.objectsMaxV
441                 skirtSize = 3
442                 if profile.getProfileSettingFloat('skirt_line_count') > 0:
443                         skirtSize = 3 + profile.getProfileSettingFloat('skirt_line_count') * profile.calculateEdgeWidth() + profile.getProfileSettingFloat('skirt_gap')
444                 scaleX1 = (self.machineSize.x - self.machineCenter.x - skirtSize) / ((vMax[0] - vMin[0]) / 2)
445                 scaleY1 = (self.machineSize.y - self.machineCenter.y - skirtSize) / ((vMax[1] - vMin[1]) / 2)
446                 scaleX2 = (self.machineCenter.x - skirtSize) / ((vMax[0] - vMin[0]) / 2)
447                 scaleY2 = (self.machineCenter.y - skirtSize) / ((vMax[1] - vMin[1]) / 2)
448                 scaleZ = self.machineSize.z / (vMax[2] - vMin[2])
449                 scale = min(scaleX1, scaleY1, scaleX2, scaleY2, scaleZ)
450                 if scale > 1.0 and onlyScaleDown:
451                         return
452                 self.scale.SetValue(str(scale))
453                 profile.putProfileSetting('model_scale', self.scale.GetValue())
454                 if self.glCanvas.viewMode == 'GCode' or self.glCanvas.viewMode == 'Mixed':
455                         self.setViewMode('Normal')
456                 self.glCanvas.Refresh()
457
458         def OnRotateReset(self, e):
459                 self.matrix = numpy.matrix([[1,0,0],[0,1,0],[0,0,1]], numpy.float64)
460                 self.updateModelTransform()
461
462         def OnLayFlat(self, e):
463                 transformedVertexes = (numpy.matrix(self.objectList[0].mesh.vertexes, copy = False) * self.matrix).getA()
464                 minZvertex = transformedVertexes[transformedVertexes.argmin(0)[2]]
465                 dotMin = 1.0
466                 dotV = None
467                 for v in transformedVertexes:
468                         diff = v - minZvertex
469                         len = math.sqrt(diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2])
470                         if len < 5:
471                                 continue
472                         dot = (diff[2] / len)
473                         if dotMin > dot:
474                                 dotMin = dot
475                                 dotV = diff
476                 if dotV is None:
477                         return
478                 rad = -math.atan2(dotV[1], dotV[0])
479                 self.matrix *= numpy.matrix([[math.cos(rad), math.sin(rad), 0], [-math.sin(rad), math.cos(rad), 0], [0,0,1]], numpy.float64)
480                 rad = -math.asin(dotMin)
481                 self.matrix *= numpy.matrix([[math.cos(rad), 0, math.sin(rad)], [0,1,0], [-math.sin(rad), 0, math.cos(rad)]], numpy.float64)
482
483
484                 transformedVertexes = (numpy.matrix(self.objectList[0].mesh.vertexes, copy = False) * self.matrix).getA()
485                 minZvertex = transformedVertexes[transformedVertexes.argmin(0)[2]]
486                 dotMin = 1.0
487                 dotV = None
488                 for v in transformedVertexes:
489                         diff = v - minZvertex
490                         len = math.sqrt(diff[1] * diff[1] + diff[2] * diff[2])
491                         if len < 5:
492                                 continue
493                         dot = (diff[2] / len)
494                         if dotMin > dot:
495                                 dotMin = dot
496                                 dotV = diff
497                 if dotV is None:
498                         return
499                 if dotV[1] < 0:
500                         rad = math.asin(dotMin)
501                 else:
502                         rad = -math.asin(dotMin)
503                 self.matrix *= numpy.matrix([[1,0,0], [0, math.cos(rad), math.sin(rad)], [0, -math.sin(rad), math.cos(rad)]], numpy.float64)
504
505                 self.updateModelTransform()
506
507         def On3DClick(self):
508                 self.glCanvas.yaw = 30
509                 self.glCanvas.pitch = 60
510                 self.glCanvas.zoom = 300
511                 self.glCanvas.view3D = True
512                 self.glCanvas.Refresh()
513
514         def OnTopClick(self):
515                 self.glCanvas.view3D = False
516                 self.glCanvas.zoom = 100
517                 self.glCanvas.offsetX = 0
518                 self.glCanvas.offsetY = 0
519                 self.glCanvas.Refresh()
520
521         def OnLayerNrChange(self, e):
522                 self.glCanvas.Refresh()
523         
524         def setViewMode(self, mode):
525                 if mode == "Normal":
526                         self.normalViewButton.SetValue(True)
527                 if mode == "GCode":
528                         self.gcodeViewButton.SetValue(True)
529                 self.glCanvas.viewMode = mode
530                 wx.CallAfter(self.glCanvas.Refresh)
531         
532         def loadModelFiles(self, filelist, showWarning = False):
533                 while len(filelist) > len(self.objectList):
534                         self.objectList.append(previewObject())
535                 for idx in xrange(len(filelist), len(self.objectList)):
536                         self.objectList[idx].mesh = None
537                         self.objectList[idx].filename = None
538                 for idx in xrange(0, len(filelist)):
539                         obj = self.objectList[idx]
540                         if obj.filename != filelist[idx]:
541                                 obj.fileTime = None
542                                 self.gcodeFileTime = None
543                                 self.logFileTime = None
544                         obj.filename = filelist[idx]
545                 
546                 self.gcodeFilename = sliceRun.getExportFilename(filelist[0])
547                 #Do the STL file loading in a background thread so we don't block the UI.
548                 if self.loadThread is not None and self.loadThread.isAlive():
549                         self.loadThread.join()
550                 self.loadThread = threading.Thread(target=self.doFileLoadThread)
551                 self.loadThread.daemon = True
552                 self.loadThread.start()
553                 
554                 if showWarning:
555                         if (self.matrix - numpy.matrix([[1,0,0],[0,1,0],[0,0,1]], numpy.float64)).any() or len(profile.getPluginConfig()) > 0:
556                                 self.ShowWarningPopup('Reset scale, rotation, mirror and plugins?', self.OnResetAll)
557         
558         def OnCheckReloadFile(self, e):
559                 #Only show the reload popup when the window has focus, because the popup goes over other programs.
560                 if self.GetParent().FindFocus() is None:
561                         return
562                 for obj in self.objectList:
563                         if obj.filename is not None and os.path.isfile(obj.filename) and obj.fileTime != os.stat(obj.filename).st_mtime:
564                                 self.checkReloadFileTimer.Stop()
565                                 self.ShowWarningPopup('File changed, reload?', self.reloadModelFiles)
566         
567         def reloadModelFiles(self, filelist = None):
568                 if filelist is not None:
569                         #Only load this again if the filename matches the file we have already loaded (for auto loading GCode after slicing)
570                         for idx in xrange(0, len(filelist)):
571                                 if self.objectList[idx].filename != filelist[idx]:
572                                         return False
573                 else:
574                         filelist = []
575                         for idx in xrange(0, len(self.objectList)):
576                                 filelist.append(self.objectList[idx].filename)
577                 self.loadModelFiles(filelist)
578                 return True
579         
580         def doFileLoadThread(self):
581                 for obj in self.objectList:
582                         if obj.filename is not None and os.path.isfile(obj.filename) and obj.fileTime != os.stat(obj.filename).st_mtime:
583                                 obj.fileTime = os.stat(obj.filename).st_mtime
584                                 mesh = meshLoader.loadMesh(obj.filename)
585                                 obj.mesh = mesh
586                                 obj.dirty = True
587                                 self.updateModelTransform()
588                                 self.OnScaleMax(None, True)
589                                 self.glCanvas.zoom = numpy.max(self.objectsSize) * 3.5
590                                 self.errorList = []
591                                 wx.CallAfter(self.updateToolbar)
592                                 wx.CallAfter(self.glCanvas.Refresh)
593                 
594                 if os.path.isfile(self.gcodeFilename) and self.gcodeFileTime != os.stat(self.gcodeFilename).st_mtime:
595                         self.gcodeFileTime = os.stat(self.gcodeFilename).st_mtime
596                         gcode = gcodeInterpreter.gcode()
597                         gcode.progressCallback = self.loadProgress
598                         gcode.load(self.gcodeFilename)
599                         self.gcodeDirty = False
600                         self.gcode = gcode
601                         self.gcodeDirty = True
602
603                         errorList = []
604                         for line in open(self.gcodeFilename, "rt"):
605                                 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)
606                                 if res is not None:
607                                         v1 = util3d.Vector3(float(res.group(2)), float(res.group(3)), float(res.group(4)))
608                                         v2 = util3d.Vector3(float(res.group(5)), float(res.group(6)), float(res.group(7)))
609                                         errorList.append([v1, v2])
610                         self.errorList = errorList
611
612                         wx.CallAfter(self.updateToolbar)
613                         wx.CallAfter(self.glCanvas.Refresh)
614                 elif not os.path.isfile(self.gcodeFilename):
615                         self.gcode = None
616                 wx.CallAfter(self.checkReloadFileTimer.Start, 1000)
617         
618         def loadProgress(self, progress):
619                 pass
620
621         def OnResetAll(self, e = None):
622                 profile.putProfileSetting('model_matrix', '1,0,0,0,1,0,0,0,1')
623                 profile.setPluginConfig([])
624                 self.GetParent().updateProfileToControls()
625
626         def ShowWarningPopup(self, text, callback = None):
627                 self.warningPopup.text.SetLabel(text)
628                 self.warningPopup.callback = callback
629                 if callback == None:
630                         self.warningPopup.yesButton.Show(False)
631                         self.warningPopup.noButton.SetLabel('ok')
632                 else:
633                         self.warningPopup.yesButton.Show(True)
634                         self.warningPopup.noButton.SetLabel('no')
635                 self.warningPopup.Fit()
636                 self.warningPopup.Layout()
637                 self.OnMove()
638                 self.warningPopup.Show(True)
639                 self.warningPopup.timer.Start(5000)
640         
641         def OnWarningPopup(self, e):
642                 self.warningPopup.Show(False)
643                 self.warningPopup.timer.Stop()
644                 self.warningPopup.callback()
645
646         def OnHideWarning(self, e):
647                 self.warningPopup.Show(False)
648                 self.warningPopup.timer.Stop()
649
650         def updateToolbar(self):
651                 self.gcodeViewButton.Show(self.gcode != None)
652                 self.mixedViewButton.Show(self.gcode != None)
653                 self.layerSpin.Show(self.glCanvas.viewMode == "GCode" or self.glCanvas.viewMode == "Mixed")
654                 if self.gcode != None:
655                         self.layerSpin.SetRange(1, len(self.gcode.layerList) - 1)
656                 self.toolbar.Realize()
657                 self.Update()
658         
659         def OnViewChange(self):
660                 if self.normalViewButton.GetValue():
661                         self.glCanvas.viewMode = "Normal"
662                 elif self.transparentViewButton.GetValue():
663                         self.glCanvas.viewMode = "Transparent"
664                 elif self.xrayViewButton.GetValue():
665                         self.glCanvas.viewMode = "X-Ray"
666                 elif self.gcodeViewButton.GetValue():
667                         self.glCanvas.viewMode = "GCode"
668                 elif self.mixedViewButton.GetValue():
669                         self.glCanvas.viewMode = "Mixed"
670                 self.glCanvas.drawBorders = self.showBorderButton.GetValue()
671                 self.glCanvas.drawSteepOverhang = self.showSteepOverhang.GetValue()
672                 self.updateToolbar()
673                 self.glCanvas.Refresh()
674         
675         def updateModelTransform(self, f=0):
676                 if len(self.objectList) < 1 or self.objectList[0].mesh is None:
677                         return
678
679                 profile.putProfileSetting('model_matrix', ','.join(map(str, list(self.matrix.getA().flatten()))))
680                 for obj in self.objectList:
681                         if obj.mesh is None:
682                                 continue
683                         obj.mesh.matrix = self.matrix
684                         obj.mesh.processMatrix()
685
686                 minV = self.objectList[0].mesh.getMinimum()
687                 maxV = self.objectList[0].mesh.getMaximum()
688                 objectsBoundaryCircleSize = self.objectList[0].mesh.bounderyCircleSize
689                 for obj in self.objectList:
690                         if obj.mesh is None:
691                                 continue
692
693                         minV = numpy.minimum(minV, obj.mesh.getMinimum())
694                         maxV = numpy.maximum(maxV, obj.mesh.getMaximum())
695                         objectsBoundaryCircleSize = max(objectsBoundaryCircleSize, obj.mesh.bounderyCircleSize)
696
697                 self.objectsMaxV = maxV
698                 self.objectsMinV = minV
699                 self.objectsSize = self.objectsMaxV - self.objectsMinV
700                 self.objectsBoundaryCircleSize = objectsBoundaryCircleSize
701
702                 self.glCanvas.Refresh()
703         
704         def updateProfileToControls(self):
705                 self.matrix = numpy.matrix(numpy.array(profile.getObjectMatrix(), numpy.float64).reshape((3,3,)))
706                 self.updateModelTransform()
707                 self.glCanvas.updateProfileToControls()
708
709 class PreviewGLCanvas(glcanvas.GLCanvas):
710         def __init__(self, parent):
711                 attribList = (glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24, glcanvas.WX_GL_STENCIL_SIZE, 8)
712                 glcanvas.GLCanvas.__init__(self, parent, attribList = attribList)
713                 self.parent = parent
714                 self.context = glcanvas.GLContext(self)
715                 wx.EVT_PAINT(self, self.OnPaint)
716                 wx.EVT_SIZE(self, self.OnSize)
717                 wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)
718                 wx.EVT_MOTION(self, self.OnMouseMotion)
719                 wx.EVT_MOUSEWHEEL(self, self.OnMouseWheel)
720                 self.yaw = 30
721                 self.pitch = 60
722                 self.zoom = 300
723                 self.offsetX = 0
724                 self.offsetY = 0
725                 self.view3D = True
726                 self.gcodeDisplayList = None
727                 self.gcodeDisplayListMade = None
728                 self.gcodeDisplayListCount = 0
729                 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]]
730                 self.oldX = 0
731                 self.oldY = 0
732                 self.dragType = ''
733                 self.tempMatrix = None
734                 self.viewport = None
735
736         def updateProfileToControls(self):
737                 self.objColor[0] = profile.getPreferenceColour('model_colour')
738                 self.objColor[1] = profile.getPreferenceColour('model_colour2')
739                 self.objColor[2] = profile.getPreferenceColour('model_colour3')
740                 self.objColor[3] = profile.getPreferenceColour('model_colour4')
741
742         def OnMouseMotion(self,e):
743                 if self.parent.objectsMaxV is not None and self.viewport is not None and self.viewMode != 'GCode' and self.viewMode != 'Mixed':
744                         p0 = numpy.array(gluUnProject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 0, self.modelMatrix, self.projMatrix, self.viewport))
745                         p1 = numpy.array(gluUnProject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 1, self.modelMatrix, self.projMatrix, self.viewport))
746                         self.parent.tool.OnMouseMove(p0, p1)
747
748                 if e.Dragging() and e.LeftIsDown():
749                         if self.dragType == '':
750                                 #Define the drag type depending on the cursor position.
751                                 self.dragType = 'viewRotate'
752                                 if self.viewMode != 'GCode' and self.viewMode != 'Mixed':
753                                         if self.parent.tool.OnDragStart(p0, p1):
754                                                 self.dragType = 'tool'
755
756                         if self.dragType == 'viewRotate':
757                                 if self.view3D:
758                                         self.yaw += e.GetX() - self.oldX
759                                         self.pitch -= e.GetY() - self.oldY
760                                         if self.pitch > 170:
761                                                 self.pitch = 170
762                                         if self.pitch < 10:
763                                                 self.pitch = 10
764                                 else:
765                                         self.offsetX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2
766                                         self.offsetY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2
767                         elif self.dragType == 'tool':
768                                 self.parent.tool.OnDrag(p0, p1)
769
770                         #Workaround for buggy ATI cards.
771                         size = self.GetSizeTuple()
772                         self.SetSize((size[0]+1, size[1]))
773                         self.SetSize((size[0], size[1]))
774                         self.Refresh()
775                 else:
776                         if self.dragType != '':
777                                 if self.tempMatrix is not None:
778                                         self.parent.matrix *= self.tempMatrix
779                                         self.parent.updateModelTransform()
780                                         self.tempMatrix = None
781                                 self.parent.tool.OnDragEnd()
782                                 self.dragType = ''
783
784                         self.dragType = ''
785                 if e.Dragging() and e.RightIsDown():
786                         self.zoom += e.GetY() - self.oldY
787                         if self.zoom < 1:
788                                 self.zoom = 1
789                         if self.zoom > 500:
790                                 self.zoom = 500
791                         self.Refresh()
792                 self.oldX = e.GetX()
793                 self.oldY = e.GetY()
794
795                 #self.Refresh()
796
797         def getObjectBoundaryCircle(self):
798                 return self.parent.objectsBoundaryCircleSize
799
800         def getObjectSize(self):
801                 return self.parent.objectsSize
802
803         def OnMouseWheel(self,e):
804                 self.zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0
805                 if self.zoom < 1.0:
806                         self.zoom = 1.0
807                 if self.zoom > 500:
808                         self.zoom = 500
809                 self.Refresh()
810         
811         def OnEraseBackground(self,event):
812                 #Workaround for windows background redraw flicker.
813                 pass
814         
815         def OnSize(self,e):
816                 self.Refresh()
817
818         def OnPaint(self,e):
819                 dc = wx.PaintDC(self)
820                 if not hasOpenGLlibs:
821                         dc.Clear()
822                         dc.DrawText("No PyOpenGL installation found.\nNo preview window available.", 10, 10)
823                         return
824                 self.SetCurrent(self.context)
825                 opengl.InitGL(self, self.view3D, self.zoom)
826                 if self.view3D:
827                         glTranslate(0,0,-self.zoom)
828                         glRotate(-self.pitch, 1,0,0)
829                         glRotate(self.yaw, 0,0,1)
830                         if self.viewMode == "GCode" or self.viewMode == "Mixed":
831                                 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:
832                                         glTranslate(0,0,-self.parent.gcode.layerList[self.parent.layerSpin.GetValue()][0].list[-1].z)
833                         else:
834                                 if self.parent.objectsMaxV is not None:
835                                         glTranslate(0,0,-self.parent.objectsSize[2] / 2)
836                 else:
837                         glTranslate(self.offsetX, self.offsetY, 0)
838
839                 self.viewport = glGetIntegerv(GL_VIEWPORT)
840                 self.modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
841                 self.projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
842
843                 glTranslate(-self.parent.machineCenter.x, -self.parent.machineCenter.y, 0)
844
845                 self.OnDraw()
846                 self.SwapBuffers()
847
848         def OnDraw(self):
849                 machineSize = self.parent.machineSize
850
851                 if self.parent.gcode is not None and self.parent.gcodeDirty:
852                         if self.gcodeDisplayListCount < len(self.parent.gcode.layerList) or self.gcodeDisplayList == None:
853                                 if self.gcodeDisplayList is not None:
854                                         glDeleteLists(self.gcodeDisplayList, self.gcodeDisplayListCount)
855                                 self.gcodeDisplayList = glGenLists(len(self.parent.gcode.layerList))
856                                 self.gcodeDisplayListCount = len(self.parent.gcode.layerList)
857                         self.parent.gcodeDirty = False
858                         self.gcodeDisplayListMade = 0
859                 
860                 if self.parent.gcode is not None and self.gcodeDisplayListMade < len(self.parent.gcode.layerList):
861                         glNewList(self.gcodeDisplayList + self.gcodeDisplayListMade, GL_COMPILE)
862                         opengl.DrawGCodeLayer(self.parent.gcode.layerList[self.gcodeDisplayListMade])
863                         glEndList()
864                         self.gcodeDisplayListMade += 1
865                         wx.CallAfter(self.Refresh)
866                 
867                 glPushMatrix()
868                 glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)
869                 for obj in self.parent.objectList:
870                         if obj.mesh == None:
871                                 continue
872                         if obj.displayList == None:
873                                 obj.displayList = glGenLists(1)
874                                 obj.steepDisplayList = glGenLists(1)
875                         if obj.dirty:
876                                 obj.dirty = False
877                                 glNewList(obj.displayList, GL_COMPILE)
878                                 opengl.DrawMesh(obj.mesh)
879                                 glEndList()
880                                 glNewList(obj.steepDisplayList, GL_COMPILE)
881                                 opengl.DrawMeshSteep(obj.mesh, 60)
882                                 glEndList()
883                         
884                         if self.viewMode == "Mixed":
885                                 glDisable(GL_BLEND)
886                                 glColor3f(0.0,0.0,0.0)
887                                 self.drawModel(obj)
888                                 glColor3f(1.0,1.0,1.0)
889                                 glClear(GL_DEPTH_BUFFER_BIT)
890                 
891                 glPopMatrix()
892                 
893                 if self.parent.gcode is not None and (self.viewMode == "GCode" or self.viewMode == "Mixed"):
894                         glPushMatrix()
895                         if profile.getPreference('machine_center_is_zero') == 'True':
896                                 glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)
897                         glEnable(GL_COLOR_MATERIAL)
898                         glEnable(GL_LIGHTING)
899                         drawUpToLayer = min(self.gcodeDisplayListMade, self.parent.layerSpin.GetValue() + 1)
900                         starttime = time.time()
901                         for i in xrange(drawUpToLayer - 1, -1, -1):
902                                 c = 1.0
903                                 if i < self.parent.layerSpin.GetValue():
904                                         c = 0.9 - (drawUpToLayer - i) * 0.1
905                                         if c < 0.4:
906                                                 c = (0.4 + c) / 2
907                                         if c < 0.1:
908                                                 c = 0.1
909                                 glLightfv(GL_LIGHT0, GL_DIFFUSE, [0,0,0,0])
910                                 glLightfv(GL_LIGHT0, GL_AMBIENT, [c,c,c,c])
911                                 glCallList(self.gcodeDisplayList + i)
912                                 if time.time() - starttime > 0.1:
913                                         break
914
915                         glDisable(GL_LIGHTING)
916                         glDisable(GL_COLOR_MATERIAL)
917                         glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, [0.2, 0.2, 0.2, 1.0]);
918                         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, [0.8, 0.8, 0.8, 1.0]);
919                         glPopMatrix()
920
921                 glColor3f(1.0,1.0,1.0)
922                 glPushMatrix()
923                 glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)
924                 for obj in self.parent.objectList:
925                         if obj.mesh is None:
926                                 continue
927
928                         if self.viewMode == "Transparent" or self.viewMode == "Mixed":
929                                 glLightfv(GL_LIGHT0, GL_DIFFUSE, map(lambda x: x / 2, self.objColor[self.parent.objectList.index(obj)]))
930                                 glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x / 10, self.objColor[self.parent.objectList.index(obj)]))
931                                 #If we want transparent, then first render a solid black model to remove the printer size lines.
932                                 if self.viewMode != "Mixed":
933                                         glDisable(GL_BLEND)
934                                         glColor3f(0.0,0.0,0.0)
935                                         self.drawModel(obj)
936                                         glColor3f(1.0,1.0,1.0)
937                                 #After the black model is rendered, render the model again but now with lighting and no depth testing.
938                                 glDisable(GL_DEPTH_TEST)
939                                 glEnable(GL_LIGHTING)
940                                 glEnable(GL_BLEND)
941                                 glBlendFunc(GL_ONE, GL_ONE)
942                                 glEnable(GL_LIGHTING)
943                                 self.drawModel(obj)
944                                 glEnable(GL_DEPTH_TEST)
945                         elif self.viewMode == "X-Ray":
946                                 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
947                                 glDisable(GL_LIGHTING)
948                                 glDisable(GL_DEPTH_TEST)
949                                 glEnable(GL_STENCIL_TEST)
950                                 glStencilFunc(GL_ALWAYS, 1, 1)
951                                 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
952                                 self.drawModel(obj)
953                                 glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
954                                 
955                                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
956                                 glStencilFunc(GL_EQUAL, 0, 1)
957                                 glColor(1, 1, 1)
958                                 self.drawModel(obj)
959                                 glStencilFunc(GL_EQUAL, 1, 1)
960                                 glColor(1, 0, 0)
961                                 self.drawModel(obj)
962
963                                 glPushMatrix()
964                                 glLoadIdentity()
965                                 for i in xrange(2, 15, 2):
966                                         glStencilFunc(GL_EQUAL, i, 0xFF);
967                                         glColor(float(i)/10, float(i)/10, float(i)/5)
968                                         glBegin(GL_QUADS)
969                                         glVertex3f(-1000,-1000,-1)
970                                         glVertex3f( 1000,-1000,-1)
971                                         glVertex3f( 1000, 1000,-1)
972                                         glVertex3f(-1000, 1000,-1)
973                                         glEnd()
974                                 for i in xrange(1, 15, 2):
975                                         glStencilFunc(GL_EQUAL, i, 0xFF);
976                                         glColor(float(i)/10, 0, 0)
977                                         glBegin(GL_QUADS)
978                                         glVertex3f(-1000,-1000,-1)
979                                         glVertex3f( 1000,-1000,-1)
980                                         glVertex3f( 1000, 1000,-1)
981                                         glVertex3f(-1000, 1000,-1)
982                                         glEnd()
983                                 glPopMatrix()
984
985                                 glDisable(GL_STENCIL_TEST)
986                                 glEnable(GL_DEPTH_TEST)
987                                 
988                                 #Fix the depth buffer
989                                 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
990                                 self.drawModel(obj)
991                                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
992                         elif self.viewMode == "Normal":
993                                 glLightfv(GL_LIGHT0, GL_DIFFUSE, self.objColor[self.parent.objectList.index(obj)])
994                                 glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x * 0.4, self.objColor[self.parent.objectList.index(obj)]))
995                                 glEnable(GL_LIGHTING)
996                                 self.drawModel(obj)
997
998                         if self.drawBorders and (self.viewMode == "Normal" or self.viewMode == "Transparent" or self.viewMode == "X-Ray"):
999                                 glEnable(GL_DEPTH_TEST)
1000                                 glDisable(GL_LIGHTING)
1001                                 glColor3f(1,1,1)
1002                                 glPushMatrix()
1003                                 modelScale = profile.getProfileSettingFloat('model_scale')
1004                                 glScalef(modelScale, modelScale, modelScale)
1005                                 opengl.DrawMeshOutline(obj.mesh)
1006                                 glPopMatrix()
1007                         
1008                         if self.drawSteepOverhang:
1009                                 glDisable(GL_LIGHTING)
1010                                 glColor3f(1,1,1)
1011                                 glPushMatrix()
1012                                 modelScale = profile.getProfileSettingFloat('model_scale')
1013                                 glScalef(modelScale, modelScale, modelScale)
1014                                 glCallList(obj.steepDisplayList)
1015                                 glPopMatrix()
1016                 
1017                 glPopMatrix()   
1018                 #if self.viewMode == "Normal" or self.viewMode == "Transparent" or self.viewMode == "X-Ray":
1019                 #       glDisable(GL_LIGHTING)
1020                 #       glDisable(GL_DEPTH_TEST)
1021                 #       glDisable(GL_BLEND)
1022                 #       glColor3f(1,0,0)
1023                 #       glBegin(GL_LINES)
1024                 #       for err in self.parent.errorList:
1025                 #               glVertex3f(err[0].x, err[0].y, err[0].z)
1026                 #               glVertex3f(err[1].x, err[1].y, err[1].z)
1027                 #       glEnd()
1028                 #       glEnable(GL_DEPTH_TEST)
1029
1030                 #Draw the current selected tool
1031                 if self.parent.objectsMaxV is not None and self.viewMode != 'GCode' and self.viewMode != 'Mixed':
1032                         glPushMatrix()
1033                         glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, self.parent.objectsSize[2]/2)
1034                         self.parent.tool.OnDraw()
1035                         glPopMatrix()
1036
1037                 opengl.DrawMachine(machineSize)
1038                 
1039                 glFlush()
1040
1041         def convert3x3MatrixTo4x4(self, matrix):
1042                 return list(matrix.getA()[0]) + [0] + list(matrix.getA()[1]) + [0] + list(matrix.getA()[2]) + [0, 0,0,0,1]
1043
1044         def drawModel(self, obj):
1045                 vMin = self.parent.objectsMinV
1046                 vMax = self.parent.objectsMaxV
1047                 offset = - vMin - (vMax - vMin) / 2
1048
1049                 matrix = self.convert3x3MatrixTo4x4(obj.mesh.matrix)
1050
1051                 glPushMatrix()
1052                 glTranslate(0, 0, self.parent.objectsSize[2]/2)
1053                 if self.tempMatrix is not None:
1054                         tempMatrix = self.convert3x3MatrixTo4x4(self.tempMatrix)
1055                         glMultMatrixf(tempMatrix)
1056                 glTranslate(0, 0, -self.parent.objectsSize[2]/2)
1057                 glTranslate(offset[0], offset[1], -vMin[2])
1058                 glMultMatrixf(matrix)
1059                 glCallList(obj.displayList)
1060                 glPopMatrix()