chiark / gitweb /
Updated all lineends for py files to unix style.
[cura.git] / Cura / gui / preview3d.py
index c830711cc282db66d6ea953b1c9530503177f629..c689b7f9ad161f15868fc3a8f45462c660846050 100644 (file)
-from __future__ import division\r
-\r
-import sys, math, threading, re, time, os\r
-import numpy\r
-\r
-from wx import glcanvas\r
-import wx\r
-try:\r
-       import OpenGL\r
-       OpenGL.ERROR_CHECKING = False\r
-       from OpenGL.GLU import *\r
-       from OpenGL.GL import *\r
-       hasOpenGLlibs = True\r
-except:\r
-       print "Failed to find PyOpenGL: http://pyopengl.sourceforge.net/"\r
-       hasOpenGLlibs = False\r
-\r
-from gui import opengl\r
-from gui import toolbarUtil\r
-\r
-from util import profile\r
-from util import gcodeInterpreter\r
-from util import meshLoader\r
-from util import util3d\r
-from util import sliceRun\r
-\r
-class previewObject():\r
-       def __init__(self):\r
-               self.mesh = None\r
-               self.filename = None\r
-               self.displayList = None\r
-               self.dirty = False\r
-\r
-class previewPanel(wx.Panel):\r
-       def __init__(self, parent):\r
-               super(previewPanel, self).__init__(parent,-1)\r
-               \r
-               self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DDKSHADOW))\r
-               self.SetMinSize((440,320))\r
-               \r
-               self.objectList = []\r
-               self.errorList = []\r
-               self.gcode = None\r
-               self.objectsMinV = None\r
-               self.objectsMaxV = None\r
-               self.objectsBounderyCircleSize = None\r
-               self.loadThread = None\r
-               self.machineSize = util3d.Vector3(profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height'))\r
-               self.machineCenter = util3d.Vector3(self.machineSize.x / 2, self.machineSize.y / 2, 0)\r
-\r
-               self.glCanvas = PreviewGLCanvas(self)\r
-               #Create the popup window\r
-               self.warningPopup = wx.PopupWindow(self, flags=wx.BORDER_SIMPLE)\r
-               self.warningPopup.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_INFOBK))\r
-               self.warningPopup.text = wx.StaticText(self.warningPopup, -1, 'Reset scale, rotation and mirror?')\r
-               self.warningPopup.yesButton = wx.Button(self.warningPopup, -1, 'yes', style=wx.BU_EXACTFIT)\r
-               self.warningPopup.noButton = wx.Button(self.warningPopup, -1, 'no', style=wx.BU_EXACTFIT)\r
-               self.warningPopup.sizer = wx.BoxSizer(wx.HORIZONTAL)\r
-               self.warningPopup.SetSizer(self.warningPopup.sizer)\r
-               self.warningPopup.sizer.Add(self.warningPopup.text, 1, flag=wx.ALL|wx.ALIGN_CENTER_VERTICAL, border=1)\r
-               self.warningPopup.sizer.Add(self.warningPopup.yesButton, 0, flag=wx.EXPAND|wx.ALL, border=1)\r
-               self.warningPopup.sizer.Add(self.warningPopup.noButton, 0, flag=wx.EXPAND|wx.ALL, border=1)\r
-               self.warningPopup.Fit()\r
-               self.warningPopup.Layout()\r
-               self.warningPopup.timer = wx.Timer(self)\r
-               self.Bind(wx.EVT_TIMER, self.OnHideWarning, self.warningPopup.timer)\r
-               \r
-               self.Bind(wx.EVT_BUTTON, self.OnWarningPopup, self.warningPopup.yesButton)\r
-               self.Bind(wx.EVT_BUTTON, self.OnHideWarning, self.warningPopup.noButton)\r
-               parent.Bind(wx.EVT_MOVE, self.OnMove)\r
-               parent.Bind(wx.EVT_SIZE, self.OnMove)\r
-               \r
-               self.toolbar = toolbarUtil.Toolbar(self)\r
-\r
-               group = []\r
-               toolbarUtil.RadioButton(self.toolbar, group, 'object-3d-on.png', 'object-3d-off.png', '3D view', callback=self.On3DClick)\r
-               toolbarUtil.RadioButton(self.toolbar, group, 'object-top-on.png', 'object-top-off.png', 'Topdown view', callback=self.OnTopClick)\r
-               self.toolbar.AddSeparator()\r
-\r
-               self.showBorderButton = toolbarUtil.ToggleButton(self.toolbar, '', 'view-border-on.png', 'view-border-off.png', 'Show model borders', callback=self.OnViewChange)\r
-               self.showSteepOverhang = toolbarUtil.ToggleButton(self.toolbar, '', 'steepOverhang-on.png', 'steepOverhang-off.png', 'Show steep overhang', callback=self.OnViewChange)\r
-               self.toolbar.AddSeparator()\r
-\r
-               group = []\r
-               self.normalViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-normal-on.png', 'view-normal-off.png', 'Normal model view', callback=self.OnViewChange)\r
-               self.transparentViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-transparent-on.png', 'view-transparent-off.png', 'Transparent model view', callback=self.OnViewChange)\r
-               self.xrayViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-xray-on.png', 'view-xray-off.png', 'X-Ray view', callback=self.OnViewChange)\r
-               self.gcodeViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-gcode-on.png', 'view-gcode-off.png', 'GCode view', callback=self.OnViewChange)\r
-               self.mixedViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-mixed-on.png', 'view-mixed-off.png', 'Mixed model/GCode view', callback=self.OnViewChange)\r
-               self.toolbar.AddSeparator()\r
-\r
-               self.layerSpin = wx.SpinCtrl(self.toolbar, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS)\r
-               self.toolbar.AddControl(self.layerSpin)\r
-               self.Bind(wx.EVT_SPINCTRL, self.OnLayerNrChange, self.layerSpin)\r
-               self.toolbar.AddSeparator()\r
-               self.toolbarInfo = wx.TextCtrl(self.toolbar, -1, '', style=wx.TE_READONLY)\r
-               self.toolbar.AddControl(self.toolbarInfo)\r
-\r
-               self.toolbar2 = toolbarUtil.Toolbar(self)\r
-\r
-               # Mirror\r
-               self.mirrorX = toolbarUtil.ToggleButton(self.toolbar2, 'flip_x', 'object-mirror-x-on.png', 'object-mirror-x-off.png', 'Mirror X', callback=self.returnToModelViewAndUpdateModel)\r
-               self.mirrorY = toolbarUtil.ToggleButton(self.toolbar2, 'flip_y', 'object-mirror-y-on.png', 'object-mirror-y-off.png', 'Mirror Y', callback=self.returnToModelViewAndUpdateModel)\r
-               self.mirrorZ = toolbarUtil.ToggleButton(self.toolbar2, 'flip_z', 'object-mirror-z-on.png', 'object-mirror-z-off.png', 'Mirror Z', callback=self.returnToModelViewAndUpdateModel)\r
-               self.toolbar2.AddSeparator()\r
-\r
-               # Swap\r
-               self.swapXZ = toolbarUtil.ToggleButton(self.toolbar2, 'swap_xz', 'object-swap-xz-on.png', 'object-swap-xz-off.png', 'Swap XZ', callback=self.returnToModelViewAndUpdateModel)\r
-               self.swapYZ = toolbarUtil.ToggleButton(self.toolbar2, 'swap_yz', 'object-swap-yz-on.png', 'object-swap-yz-off.png', 'Swap YZ', callback=self.returnToModelViewAndUpdateModel)\r
-               self.toolbar2.AddSeparator()\r
-\r
-               # Scale\r
-               self.scaleReset = toolbarUtil.NormalButton(self.toolbar2, self.OnScaleReset, 'object-scale.png', 'Reset model scale')\r
-               self.scale = wx.TextCtrl(self.toolbar2, -1, profile.getProfileSetting('model_scale'), size=(21*2,21))\r
-               self.toolbar2.AddControl(self.scale)\r
-               self.scale.Bind(wx.EVT_TEXT, self.OnScale)\r
-               self.scaleMax = toolbarUtil.NormalButton(self.toolbar2, self.OnScaleMax, 'object-max-size.png', 'Scale object to fit machine size')\r
-\r
-               self.toolbar2.AddSeparator()\r
-\r
-               # Multiply\r
-               #self.mulXadd = toolbarUtil.NormalButton(self.toolbar2, self.OnMulXAddClick, 'object-mul-x-add.png', 'Increase number of models on X axis')\r
-               #self.mulXsub = toolbarUtil.NormalButton(self.toolbar2, self.OnMulXSubClick, 'object-mul-x-sub.png', 'Decrease number of models on X axis')\r
-               #self.mulYadd = toolbarUtil.NormalButton(self.toolbar2, self.OnMulYAddClick, 'object-mul-y-add.png', 'Increase number of models on Y axis')\r
-               #self.mulYsub = toolbarUtil.NormalButton(self.toolbar2, self.OnMulYSubClick, 'object-mul-y-sub.png', 'Decrease number of models on Y axis')\r
-               #self.toolbar2.AddSeparator()\r
-\r
-               # Rotate\r
-               self.rotateReset = toolbarUtil.NormalButton(self.toolbar2, self.OnRotateReset, 'object-rotate.png', 'Reset model rotation')\r
-               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
-               self.rotate.SetRange(0, 360)\r
-               self.rotate.Bind(wx.EVT_TEXT, self.OnRotate)\r
-               self.toolbar2.AddControl(self.rotate)\r
-\r
-               self.toolbar2.Realize()\r
-               self.OnViewChange()\r
-               \r
-               sizer = wx.BoxSizer(wx.VERTICAL)\r
-               sizer.Add(self.toolbar, 0, flag=wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, border=1)\r
-               sizer.Add(self.glCanvas, 1, flag=wx.EXPAND)\r
-               sizer.Add(self.toolbar2, 0, flag=wx.EXPAND|wx.BOTTOM|wx.LEFT|wx.RIGHT, border=1)\r
-               self.SetSizer(sizer)\r
-       \r
-       def returnToModelViewAndUpdateModel(self):\r
-               if self.glCanvas.viewMode == 'GCode' or self.glCanvas.viewMode == 'Mixed':\r
-                       self.setViewMode('Normal')\r
-               self.updateModelTransform()\r
-       \r
-       def OnMove(self, e = None):\r
-               if e != None:\r
-                       e.Skip()\r
-               x, y = self.glCanvas.ClientToScreenXY(0, 0)\r
-               sx, sy = self.glCanvas.GetClientSizeTuple()\r
-               self.warningPopup.SetPosition((x, y+sy-self.warningPopup.GetSize().height))\r
-       \r
-       def OnMulXAddClick(self, e):\r
-               profile.putProfileSetting('model_multiply_x', str(max(1, int(profile.getProfileSetting('model_multiply_x'))+1)))\r
-               self.glCanvas.Refresh()\r
-\r
-       def OnMulXSubClick(self, e):\r
-               profile.putProfileSetting('model_multiply_x', str(max(1, int(profile.getProfileSetting('model_multiply_x'))-1)))\r
-               self.glCanvas.Refresh()\r
-\r
-       def OnMulYAddClick(self, e):\r
-               profile.putProfileSetting('model_multiply_y', str(max(1, int(profile.getProfileSetting('model_multiply_y'))+1)))\r
-               self.glCanvas.Refresh()\r
-\r
-       def OnMulYSubClick(self, e):\r
-               profile.putProfileSetting('model_multiply_y', str(max(1, int(profile.getProfileSetting('model_multiply_y'))-1)))\r
-               self.glCanvas.Refresh()\r
-\r
-       def OnScaleReset(self, e):\r
-               self.scale.SetValue('1.0')\r
-               self.OnScale(None)\r
-\r
-       def OnScale(self, e):\r
-               scale = 1.0\r
-               if self.scale.GetValue() != '':\r
-                       scale = self.scale.GetValue()\r
-               profile.putProfileSetting('model_scale', scale)\r
-               if self.glCanvas.viewMode == 'GCode' or self.glCanvas.viewMode == 'Mixed':\r
-                       self.setViewMode('Normal')\r
-               self.glCanvas.Refresh()\r
-\r
-               if self.objectsMaxV != None:\r
-                       size = (self.objectsMaxV - self.objectsMinV) * float(scale)\r
-                       self.toolbarInfo.SetValue('%0.1f %0.1f %0.1f' % (size[0], size[1], size[2]))\r
-\r
-       def OnScaleMax(self, e = None, onlyScaleDown = False):\r
-               if self.objectsMinV == None:\r
-                       return\r
-               vMin = self.objectsMinV\r
-               vMax = self.objectsMaxV\r
-               skirtSize = 3\r
-               if profile.getProfileSettingFloat('skirt_line_count') > 0:\r
-                       skirtSize = 3 + profile.getProfileSettingFloat('skirt_line_count') * profile.calculateEdgeWidth() + profile.getProfileSettingFloat('skirt_gap')\r
-               scaleX1 = (self.machineSize.x - self.machineCenter.x - skirtSize) / ((vMax[0] - vMin[0]) / 2)\r
-               scaleY1 = (self.machineSize.y - self.machineCenter.y - skirtSize) / ((vMax[1] - vMin[1]) / 2)\r
-               scaleX2 = (self.machineCenter.x - skirtSize) / ((vMax[0] - vMin[0]) / 2)\r
-               scaleY2 = (self.machineCenter.y - skirtSize) / ((vMax[1] - vMin[1]) / 2)\r
-               scaleZ = self.machineSize.z / (vMax[2] - vMin[2])\r
-               scale = min(scaleX1, scaleY1, scaleX2, scaleY2, scaleZ)\r
-               if scale > 1.0 and onlyScaleDown:\r
-                       return\r
-               self.scale.SetValue(str(scale))\r
-               profile.putProfileSetting('model_scale', self.scale.GetValue())\r
-               if self.glCanvas.viewMode == 'GCode' or self.glCanvas.viewMode == 'Mixed':\r
-                       self.setViewMode('Normal')\r
-               self.glCanvas.Refresh()\r
-\r
-       def OnRotateReset(self, e):\r
-               self.rotate.SetValue(0)\r
-               self.OnRotate(None)\r
-\r
-       def OnRotate(self, e):\r
-               profile.putProfileSetting('model_rotate_base', self.rotate.GetValue())\r
-               self.returnToModelViewAndUpdateModel()\r
-\r
-       def On3DClick(self):\r
-               self.glCanvas.yaw = 30\r
-               self.glCanvas.pitch = 60\r
-               self.glCanvas.zoom = 300\r
-               self.glCanvas.view3D = True\r
-               self.glCanvas.Refresh()\r
-\r
-       def OnTopClick(self):\r
-               self.glCanvas.view3D = False\r
-               self.glCanvas.zoom = 100\r
-               self.glCanvas.offsetX = 0\r
-               self.glCanvas.offsetY = 0\r
-               self.glCanvas.Refresh()\r
-\r
-       def OnLayerNrChange(self, e):\r
-               self.glCanvas.Refresh()\r
-       \r
-       def setViewMode(self, mode):\r
-               if mode == "Normal":\r
-                       self.normalViewButton.SetValue(True)\r
-               if mode == "GCode":\r
-                       self.gcodeViewButton.SetValue(True)\r
-               self.glCanvas.viewMode = mode\r
-               wx.CallAfter(self.glCanvas.Refresh)\r
-       \r
-       def loadModelFiles(self, filelist, showWarning = False):\r
-               while len(filelist) > len(self.objectList):\r
-                       self.objectList.append(previewObject())\r
-               for idx in xrange(len(filelist), len(self.objectList)):\r
-                       self.objectList[idx].mesh = None\r
-                       self.objectList[idx].filename = None\r
-               for idx in xrange(0, len(filelist)):\r
-                       obj = self.objectList[idx]\r
-                       if obj.filename != filelist[idx]:\r
-                               obj.fileTime = None\r
-                               self.gcodeFileTime = None\r
-                               self.logFileTime = None\r
-                       obj.filename = filelist[idx]\r
-               \r
-               self.gcodeFilename = sliceRun.getExportFilename(filelist[0])\r
-               #Do the STL file loading in a background thread so we don't block the UI.\r
-               if self.loadThread != None and self.loadThread.isAlive():\r
-                       self.loadThread.join()\r
-               self.loadThread = threading.Thread(target=self.doFileLoadThread)\r
-               self.loadThread.daemon = True\r
-               self.loadThread.start()\r
-               \r
-               if showWarning:\r
-                       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' or len(profile.getPluginConfig()) > 0:\r
-                               self.ShowWarningPopup('Reset scale, rotation, mirror and plugins?', self.OnResetAll)\r
-       \r
-       def loadReModelFiles(self, filelist):\r
-               #Only load this again if the filename matches the file we have already loaded (for auto loading GCode after slicing)\r
-               for idx in xrange(0, len(filelist)):\r
-                       if self.objectList[idx].filename != filelist[idx]:\r
-                               return False\r
-               self.loadModelFiles(filelist)\r
-               return True\r
-       \r
-       def doFileLoadThread(self):\r
-               for obj in self.objectList:\r
-                       if obj.filename != None and os.path.isfile(obj.filename) and obj.fileTime != os.stat(obj.filename).st_mtime:\r
-                               obj.ileTime = os.stat(obj.filename).st_mtime\r
-                               mesh = meshLoader.loadMesh(obj.filename)\r
-                               obj.dirty = False\r
-                               obj.mesh = mesh\r
-                               self.updateModelTransform()\r
-                               self.OnScaleMax(None, True)\r
-                               scale = profile.getProfileSettingFloat('model_scale')\r
-                               size = (self.objectsMaxV - self.objectsMinV) * scale\r
-                               self.toolbarInfo.SetValue('%0.1f %0.1f %0.1f' % (size[0], size[1], size[2]))\r
-                               self.glCanvas.zoom = numpy.max(size) * 2.5\r
-                               self.errorList = []\r
-                               wx.CallAfter(self.updateToolbar)\r
-                               wx.CallAfter(self.glCanvas.Refresh)\r
-               \r
-               if os.path.isfile(self.gcodeFilename) and self.gcodeFileTime != os.stat(self.gcodeFilename).st_mtime:\r
-                       self.gcodeFileTime = os.stat(self.gcodeFilename).st_mtime\r
-                       gcode = gcodeInterpreter.gcode()\r
-                       gcode.progressCallback = self.loadProgress\r
-                       gcode.load(self.gcodeFilename)\r
-                       self.gcodeDirty = False\r
-                       self.gcode = gcode\r
-                       self.gcodeDirty = True\r
-\r
-                       errorList = []\r
-                       for line in open(self.gcodeFilename, "rt"):\r
-                               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
-                               if res != None:\r
-                                       v1 = util3d.Vector3(float(res.group(2)), float(res.group(3)), float(res.group(4)))\r
-                                       v2 = util3d.Vector3(float(res.group(5)), float(res.group(6)), float(res.group(7)))\r
-                                       errorList.append([v1, v2])\r
-                       self.errorList = errorList\r
-\r
-                       wx.CallAfter(self.updateToolbar)\r
-                       wx.CallAfter(self.glCanvas.Refresh)\r
-               elif not os.path.isfile(self.gcodeFilename):\r
-                       self.gcode = None\r
-       \r
-       def loadProgress(self, progress):\r
-               pass\r
-\r
-       def OnResetAll(self, e = None):\r
-               profile.putProfileSetting('model_scale', '1.0')\r
-               profile.putProfileSetting('model_rotate_base', '0')\r
-               profile.putProfileSetting('flip_x', 'False')\r
-               profile.putProfileSetting('flip_y', 'False')\r
-               profile.putProfileSetting('flip_z', 'False')\r
-               profile.putProfileSetting('swap_xz', 'False')\r
-               profile.putProfileSetting('swap_yz', 'False')\r
-               profile.setPluginConfig([])\r
-               self.GetParent().updateProfileToControls()\r
-\r
-       def ShowWarningPopup(self, text, callback = None):\r
-               self.warningPopup.text.SetLabel(text)\r
-               self.warningPopup.callback = callback\r
-               if callback == None:\r
-                       self.warningPopup.yesButton.Show(False)\r
-                       self.warningPopup.noButton.SetLabel('ok')\r
-               else:\r
-                       self.warningPopup.yesButton.Show(True)\r
-                       self.warningPopup.noButton.SetLabel('no')\r
-               self.warningPopup.Fit()\r
-               self.warningPopup.Layout()\r
-               self.OnMove()\r
-               self.warningPopup.Show(True)\r
-               self.warningPopup.timer.Start(5000)\r
-       \r
-       def OnWarningPopup(self, e):\r
-               self.warningPopup.Show(False)\r
-               self.warningPopup.timer.Stop()\r
-               self.warningPopup.callback()\r
-\r
-       def OnHideWarning(self, e):\r
-               self.warningPopup.Show(False)\r
-               self.warningPopup.timer.Stop()\r
-\r
-       def updateToolbar(self):\r
-               self.gcodeViewButton.Show(self.gcode != None)\r
-               self.mixedViewButton.Show(self.gcode != None)\r
-               self.layerSpin.Show(self.glCanvas.viewMode == "GCode" or self.glCanvas.viewMode == "Mixed")\r
-               if self.gcode != None:\r
-                       self.layerSpin.SetRange(1, len(self.gcode.layerList) - 1)\r
-               self.toolbar.Realize()\r
-               self.Update()\r
-       \r
-       def OnViewChange(self):\r
-               if self.normalViewButton.GetValue():\r
-                       self.glCanvas.viewMode = "Normal"\r
-               elif self.transparentViewButton.GetValue():\r
-                       self.glCanvas.viewMode = "Transparent"\r
-               elif self.xrayViewButton.GetValue():\r
-                       self.glCanvas.viewMode = "X-Ray"\r
-               elif self.gcodeViewButton.GetValue():\r
-                       self.glCanvas.viewMode = "GCode"\r
-               elif self.mixedViewButton.GetValue():\r
-                       self.glCanvas.viewMode = "Mixed"\r
-               self.glCanvas.drawBorders = self.showBorderButton.GetValue()\r
-               self.glCanvas.drawSteepOverhang = self.showSteepOverhang.GetValue()\r
-               self.updateToolbar()\r
-               self.glCanvas.Refresh()\r
-       \r
-       def updateModelTransform(self, f=0):\r
-               if len(self.objectList) < 1 or self.objectList[0].mesh == None:\r
-                       return\r
-               \r
-               rotate = profile.getProfileSettingFloat('model_rotate_base')\r
-               mirrorX = profile.getProfileSetting('flip_x') == 'True'\r
-               mirrorY = profile.getProfileSetting('flip_y') == 'True'\r
-               mirrorZ = profile.getProfileSetting('flip_z') == 'True'\r
-               swapXZ = profile.getProfileSetting('swap_xz') == 'True'\r
-               swapYZ = profile.getProfileSetting('swap_yz') == 'True'\r
-\r
-               for obj in self.objectList:\r
-                       if obj.mesh == None:\r
-                               continue\r
-                       obj.mesh.setRotateMirror(rotate, mirrorX, mirrorY, mirrorZ, swapXZ, swapYZ)\r
-               \r
-               minV = self.objectList[0].mesh.getMinimum()\r
-               maxV = self.objectList[0].mesh.getMaximum()\r
-               objectsBounderyCircleSize = self.objectList[0].mesh.bounderyCircleSize\r
-               for obj in self.objectList:\r
-                       if obj.mesh == None:\r
-                               continue\r
-\r
-                       obj.mesh.getMinimumZ()\r
-                       minV = numpy.minimum(minV, obj.mesh.getMinimum())\r
-                       maxV = numpy.maximum(maxV, obj.mesh.getMaximum())\r
-                       objectsBounderyCircleSize = max(objectsBounderyCircleSize, obj.mesh.bounderyCircleSize)\r
-\r
-               self.objectsMaxV = maxV\r
-               self.objectsMinV = minV\r
-               self.objectsBounderyCircleSize = objectsBounderyCircleSize\r
-               for obj in self.objectList:\r
-                       if obj.mesh == None:\r
-                               continue\r
-\r
-                       obj.mesh.vertexes -= numpy.array([minV[0] + (maxV[0] - minV[0]) / 2, minV[1] + (maxV[1] - minV[1]) / 2, minV[2]])\r
-                       #for v in obj.mesh.vertexes:\r
-                       #       v[2] -= minV[2]\r
-                       #       v[0] -= minV[0] + (maxV[0] - minV[0]) / 2\r
-                       #       v[1] -= minV[1] + (maxV[1] - minV[1]) / 2\r
-                       obj.mesh.getMinimumZ()\r
-                       obj.dirty = True\r
-\r
-               scale = profile.getProfileSettingFloat('model_scale')\r
-               size = (self.objectsMaxV - self.objectsMinV) * scale\r
-               self.toolbarInfo.SetValue('%0.1f %0.1f %0.1f' % (size[0], size[1], size[2]))\r
-\r
-               self.glCanvas.Refresh()\r
-       \r
-       def updateProfileToControls(self):\r
-               self.scale.SetValue(profile.getProfileSetting('model_scale'))\r
-               self.rotate.SetValue(profile.getProfileSettingFloat('model_rotate_base'))\r
-               self.mirrorX.SetValue(profile.getProfileSetting('flip_x') == 'True')\r
-               self.mirrorY.SetValue(profile.getProfileSetting('flip_y') == 'True')\r
-               self.mirrorZ.SetValue(profile.getProfileSetting('flip_z') == 'True')\r
-               self.swapXZ.SetValue(profile.getProfileSetting('swap_xz') == 'True')\r
-               self.swapYZ.SetValue(profile.getProfileSetting('swap_yz') == 'True')\r
-               self.updateModelTransform()\r
-               self.glCanvas.updateProfileToControls()\r
-\r
-class PreviewGLCanvas(glcanvas.GLCanvas):\r
-       def __init__(self, parent):\r
-               attribList = (glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24, glcanvas.WX_GL_STENCIL_SIZE, 8)\r
-               glcanvas.GLCanvas.__init__(self, parent, attribList = attribList)\r
-               self.parent = parent\r
-               self.context = glcanvas.GLContext(self)\r
-               wx.EVT_PAINT(self, self.OnPaint)\r
-               wx.EVT_SIZE(self, self.OnSize)\r
-               wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)\r
-               wx.EVT_MOTION(self, self.OnMouseMotion)\r
-               wx.EVT_MOUSEWHEEL(self, self.OnMouseWheel)\r
-               self.yaw = 30\r
-               self.pitch = 60\r
-               self.zoom = 300\r
-               self.offsetX = 0\r
-               self.offsetY = 0\r
-               self.view3D = True\r
-               self.gcodeDisplayList = None\r
-               self.gcodeDisplayListMade = None\r
-               self.gcodeDisplayListCount = 0\r
-               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
-               self.oldX = 0\r
-               self.oldY = 0\r
-               self.dragType = ''\r
-               self.tempRotate = 0\r
-       \r
-       def updateProfileToControls(self):\r
-               self.objColor[0] = profile.getPreferenceColour('model_colour')\r
-               self.objColor[1] = profile.getPreferenceColour('model_colour2')\r
-               self.objColor[2] = profile.getPreferenceColour('model_colour3')\r
-               self.objColor[3] = profile.getPreferenceColour('model_colour4')\r
-\r
-       def OnMouseMotion(self,e):\r
-               cursorXY = 100000\r
-               radius = 0\r
-               if self.parent.objectsMaxV != None:\r
-                       radius = self.parent.objectsBounderyCircleSize * profile.getProfileSettingFloat('model_scale')\r
-                       \r
-                       p0 = numpy.array(gluUnProject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 0, self.modelMatrix, self.projMatrix, self.viewport))\r
-                       p1 = numpy.array(gluUnProject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 1, self.modelMatrix, self.projMatrix, self.viewport))\r
-                       cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))\r
-                       cursorXY = math.sqrt((cursorZ0[0] * cursorZ0[0]) + (cursorZ0[1] * cursorZ0[1]))\r
-                       if cursorXY >= radius * 1.1 and cursorXY <= radius * 1.3:\r
-                               self.SetCursor(wx.StockCursor(wx.CURSOR_SIZING))\r
-                       else:\r
-                               self.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))\r
-\r
-               if e.Dragging() and e.LeftIsDown():\r
-                       if self.dragType == '':\r
-                               #Define the drag type depending on the cursor position.\r
-                               if cursorXY >= radius * 1.1 and cursorXY <= radius * 1.3:\r
-                                       self.dragType = 'modelRotate'\r
-                                       self.dragStart = math.atan2(cursorZ0[0], cursorZ0[1])\r
-                               else:\r
-                                       self.dragType = 'viewRotate'\r
-                               \r
-                       if self.dragType == 'viewRotate':\r
-                               if self.view3D:\r
-                                       self.yaw += e.GetX() - self.oldX\r
-                                       self.pitch -= e.GetY() - self.oldY\r
-                                       if self.pitch > 170:\r
-                                               self.pitch = 170\r
-                                       if self.pitch < 10:\r
-                                               self.pitch = 10\r
-                               else:\r
-                                       self.offsetX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2\r
-                                       self.offsetY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2\r
-                       elif self.dragType == 'modelRotate':\r
-                               angle = math.atan2(cursorZ0[0], cursorZ0[1])\r
-                               diff = self.dragStart - angle\r
-                               self.tempRotate = diff * 180 / math.pi\r
-                               rot = profile.getProfileSettingFloat('model_rotate_base')\r
-                               self.tempRotate = round((self.tempRotate + rot) / 15) * 15 - rot\r
-                       #Workaround for buggy ATI cards.\r
-                       size = self.GetSizeTuple()\r
-                       self.SetSize((size[0]+1, size[1]))\r
-                       self.SetSize((size[0], size[1]))\r
-                       self.Refresh()\r
-               else:\r
-                       if self.tempRotate != 0:\r
-                               newRotation = profile.getProfileSettingFloat('model_rotate_base') + self.tempRotate\r
-                               while newRotation >= 360:\r
-                                       newRotation -= 360\r
-                               while newRotation < 0:\r
-                                       newRotation += 360\r
-                               profile.putProfileSetting('model_rotate_base', newRotation)\r
-                               self.parent.rotate.SetValue(newRotation)\r
-                               self.parent.updateModelTransform()\r
-                               self.tempRotate = 0\r
-                               \r
-                       self.dragType = ''\r
-               if e.Dragging() and e.RightIsDown():\r
-                       self.zoom += e.GetY() - self.oldY\r
-                       if self.zoom < 1:\r
-                               self.zoom = 1\r
-                       if self.zoom > 500:\r
-                               self.zoom = 500\r
-                       self.Refresh()\r
-               self.oldX = e.GetX()\r
-               self.oldY = e.GetY()\r
-\r
-               #self.Refresh()\r
-               \r
-       \r
-       def OnMouseWheel(self,e):\r
-               self.zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0\r
-               if self.zoom < 1.0:\r
-                       self.zoom = 1.0\r
-               if self.zoom > 500:\r
-                       self.zoom = 500\r
-               self.Refresh()\r
-       \r
-       def OnEraseBackground(self,event):\r
-               #Workaround for windows background redraw flicker.\r
-               pass\r
-       \r
-       def OnSize(self,e):\r
-               self.Refresh()\r
-\r
-       def OnPaint(self,e):\r
-               dc = wx.PaintDC(self)\r
-               if not hasOpenGLlibs:\r
-                       dc.Clear()\r
-                       dc.DrawText("No PyOpenGL installation found.\nNo preview window available.", 10, 10)\r
-                       return\r
-               self.SetCurrent(self.context)\r
-               opengl.InitGL(self, self.view3D, self.zoom)\r
-               if self.view3D:\r
-                       glTranslate(0,0,-self.zoom)\r
-                       glRotate(-self.pitch, 1,0,0)\r
-                       glRotate(self.yaw, 0,0,1)\r
-                       if self.viewMode == "GCode" or self.viewMode == "Mixed":\r
-                               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
-                                       glTranslate(0,0,-self.parent.gcode.layerList[self.parent.layerSpin.GetValue()][0].list[-1].z)\r
-                       else:\r
-                               if self.parent.objectsMaxV != None:\r
-                                       glTranslate(0,0,-(self.parent.objectsMaxV[2]-self.parent.objectsMinV[2]) * profile.getProfileSettingFloat('model_scale') / 2)\r
-               else:\r
-                       glTranslate(self.offsetX, self.offsetY, 0)\r
-\r
-               self.viewport = glGetIntegerv(GL_VIEWPORT);\r
-               self.modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX);\r
-               self.projMatrix = glGetDoublev(GL_PROJECTION_MATRIX);\r
-\r
-               glTranslate(-self.parent.machineCenter.x, -self.parent.machineCenter.y, 0)\r
-\r
-               self.OnDraw()\r
-               self.SwapBuffers()\r
-\r
-       def OnDraw(self):\r
-               machineSize = self.parent.machineSize\r
-\r
-               if self.parent.gcode != None and self.parent.gcodeDirty:\r
-                       if self.gcodeDisplayListCount < len(self.parent.gcode.layerList) or self.gcodeDisplayList == None:\r
-                               if self.gcodeDisplayList != None:\r
-                                       glDeleteLists(self.gcodeDisplayList, self.gcodeDisplayListCount)\r
-                               self.gcodeDisplayList = glGenLists(len(self.parent.gcode.layerList));\r
-                               self.gcodeDisplayListCount = len(self.parent.gcode.layerList)\r
-                       self.parent.gcodeDirty = False\r
-                       self.gcodeDisplayListMade = 0\r
-               \r
-               if self.parent.gcode != None and self.gcodeDisplayListMade < len(self.parent.gcode.layerList):\r
-                       glNewList(self.gcodeDisplayList + self.gcodeDisplayListMade, GL_COMPILE)\r
-                       opengl.DrawGCodeLayer(self.parent.gcode.layerList[self.gcodeDisplayListMade])\r
-                       glEndList()\r
-                       self.gcodeDisplayListMade += 1\r
-                       wx.CallAfter(self.Refresh)\r
-               \r
-               glPushMatrix()\r
-               glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)\r
-               for obj in self.parent.objectList:\r
-                       if obj.mesh == None:\r
-                               continue\r
-                       if obj.displayList == None:\r
-                               obj.displayList = glGenLists(1)\r
-                               obj.steepDisplayList = glGenLists(1)\r
-                       if obj.dirty:\r
-                               obj.dirty = False\r
-                               glNewList(obj.displayList, GL_COMPILE)\r
-                               opengl.DrawMesh(obj.mesh)\r
-                               glEndList()\r
-                               glNewList(obj.steepDisplayList, GL_COMPILE)\r
-                               opengl.DrawMeshSteep(obj.mesh, 60)\r
-                               glEndList()\r
-                       \r
-                       if self.viewMode == "Mixed":\r
-                               glDisable(GL_BLEND)\r
-                               glColor3f(0.0,0.0,0.0)\r
-                               self.drawModel(obj)\r
-                               glColor3f(1.0,1.0,1.0)\r
-                               glClear(GL_DEPTH_BUFFER_BIT)\r
-               \r
-               glPopMatrix()\r
-               \r
-               if self.parent.gcode != None and (self.viewMode == "GCode" or self.viewMode == "Mixed"):\r
-                       glEnable(GL_COLOR_MATERIAL)\r
-                       glEnable(GL_LIGHTING)\r
-                       drawUpToLayer = min(self.gcodeDisplayListMade, self.parent.layerSpin.GetValue() + 1)\r
-                       starttime = time.time()\r
-                       for i in xrange(drawUpToLayer - 1, -1, -1):\r
-                               c = 1.0\r
-                               if i < self.parent.layerSpin.GetValue():\r
-                                       c = 0.9 - (drawUpToLayer - i) * 0.1\r
-                                       if c < 0.4:\r
-                                               c = (0.4 + c) / 2\r
-                                       if c < 0.1:\r
-                                               c = 0.1\r
-                               glLightfv(GL_LIGHT0, GL_DIFFUSE, [0,0,0,0])\r
-                               glLightfv(GL_LIGHT0, GL_AMBIENT, [c,c,c,c])\r
-                               glCallList(self.gcodeDisplayList + i)\r
-                               if time.time() - starttime > 0.1:\r
-                                       break\r
-\r
-                       glDisable(GL_LIGHTING)\r
-                       glDisable(GL_COLOR_MATERIAL)\r
-                       glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, [0.2, 0.2, 0.2, 1.0]);\r
-                       glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, [0.8, 0.8, 0.8, 1.0]);\r
-\r
-               glColor3f(1.0,1.0,1.0)\r
-               glPushMatrix()\r
-               glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)\r
-               for obj in self.parent.objectList:\r
-                       if obj.mesh == None:\r
-                               continue\r
-                       \r
-                       if self.viewMode == "Transparent" or self.viewMode == "Mixed":\r
-                               glLightfv(GL_LIGHT0, GL_DIFFUSE, map(lambda x: x / 2, self.objColor[self.parent.objectList.index(obj)]))\r
-                               glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x / 10, self.objColor[self.parent.objectList.index(obj)]))\r
-                               #If we want transparent, then first render a solid black model to remove the printer size lines.\r
-                               if self.viewMode != "Mixed":\r
-                                       glDisable(GL_BLEND)\r
-                                       glColor3f(0.0,0.0,0.0)\r
-                                       self.drawModel(obj)\r
-                                       glColor3f(1.0,1.0,1.0)\r
-                               #After the black model is rendered, render the model again but now with lighting and no depth testing.\r
-                               glDisable(GL_DEPTH_TEST)\r
-                               glEnable(GL_LIGHTING)\r
-                               glEnable(GL_BLEND)\r
-                               glBlendFunc(GL_ONE, GL_ONE)\r
-                               glEnable(GL_LIGHTING)\r
-                               self.drawModel(obj)\r
-                               glEnable(GL_DEPTH_TEST)\r
-                       elif self.viewMode == "X-Ray":\r
-                               glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)\r
-                               glDisable(GL_LIGHTING)\r
-                               glDisable(GL_DEPTH_TEST)\r
-                               glEnable(GL_STENCIL_TEST)\r
-                               glStencilFunc(GL_ALWAYS, 1, 1)\r
-                               glStencilOp(GL_INCR, GL_INCR, GL_INCR)\r
-                               self.drawModel(obj)\r
-                               glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);\r
-                               \r
-                               glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)\r
-                               glStencilFunc(GL_EQUAL, 0, 1)\r
-                               glColor(1, 1, 1)\r
-                               self.drawModel(obj)\r
-                               glStencilFunc(GL_EQUAL, 1, 1)\r
-                               glColor(1, 0, 0)\r
-                               self.drawModel(obj)\r
-\r
-                               glPushMatrix()\r
-                               glLoadIdentity()\r
-                               for i in xrange(2, 15, 2):\r
-                                       glStencilFunc(GL_EQUAL, i, 0xFF);\r
-                                       glColor(float(i)/10, float(i)/10, float(i)/5)\r
-                                       glBegin(GL_QUADS)\r
-                                       glVertex3f(-1000,-1000,-1)\r
-                                       glVertex3f( 1000,-1000,-1)\r
-                                       glVertex3f( 1000, 1000,-1)\r
-                                       glVertex3f(-1000, 1000,-1)\r
-                                       glEnd()\r
-                               for i in xrange(1, 15, 2):\r
-                                       glStencilFunc(GL_EQUAL, i, 0xFF);\r
-                                       glColor(float(i)/10, 0, 0)\r
-                                       glBegin(GL_QUADS)\r
-                                       glVertex3f(-1000,-1000,-1)\r
-                                       glVertex3f( 1000,-1000,-1)\r
-                                       glVertex3f( 1000, 1000,-1)\r
-                                       glVertex3f(-1000, 1000,-1)\r
-                                       glEnd()\r
-                               glPopMatrix()\r
-\r
-                               glDisable(GL_STENCIL_TEST)\r
-                               glEnable(GL_DEPTH_TEST)\r
-                               \r
-                               #Fix the depth buffer\r
-                               glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)\r
-                               self.drawModel(obj)\r
-                               glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)\r
-                       elif self.viewMode == "Normal":\r
-                               glLightfv(GL_LIGHT0, GL_DIFFUSE, self.objColor[self.parent.objectList.index(obj)])\r
-                               glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x * 0.4, self.objColor[self.parent.objectList.index(obj)]))\r
-                               glEnable(GL_LIGHTING)\r
-                               self.drawModel(obj)\r
-\r
-                       if self.drawBorders and (self.viewMode == "Normal" or self.viewMode == "Transparent" or self.viewMode == "X-Ray"):\r
-                               glEnable(GL_DEPTH_TEST)\r
-                               glDisable(GL_LIGHTING)\r
-                               glColor3f(1,1,1)\r
-                               glPushMatrix()\r
-                               modelScale = profile.getProfileSettingFloat('model_scale')\r
-                               glScalef(modelScale, modelScale, modelScale)\r
-                               opengl.DrawMeshOutline(obj.mesh)\r
-                               glPopMatrix()\r
-                       \r
-                       if self.drawSteepOverhang:\r
-                               glDisable(GL_LIGHTING)\r
-                               glColor3f(1,1,1)\r
-                               glPushMatrix()\r
-                               modelScale = profile.getProfileSettingFloat('model_scale')\r
-                               glScalef(modelScale, modelScale, modelScale)\r
-                               glCallList(obj.steepDisplayList)\r
-                               glPopMatrix()\r
-               \r
-               glPopMatrix()   \r
-               if self.viewMode == "Normal" or self.viewMode == "Transparent" or self.viewMode == "X-Ray":\r
-                       glDisable(GL_LIGHTING)\r
-                       glDisable(GL_DEPTH_TEST)\r
-                       glDisable(GL_BLEND)\r
-                       glColor3f(1,0,0)\r
-                       glBegin(GL_LINES)\r
-                       for err in self.parent.errorList:\r
-                               glVertex3f(err[0].x, err[0].y, err[0].z)\r
-                               glVertex3f(err[1].x, err[1].y, err[1].z)\r
-                       glEnd()\r
-                       glEnable(GL_DEPTH_TEST)\r
-\r
-               glPushMatrix()\r
-               glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)\r
-               \r
-               #Draw the rotate circle\r
-               if self.parent.objectsMaxV != None:\r
-                       glDisable(GL_LIGHTING)\r
-                       glDisable(GL_CULL_FACE)\r
-                       glEnable(GL_BLEND)\r
-                       glRotate(self.tempRotate + profile.getProfileSettingFloat('model_rotate_base'), 0, 0, 1)\r
-                       radius = self.parent.objectsBounderyCircleSize * profile.getProfileSettingFloat('model_scale')\r
-                       glScalef(radius, radius, 1)\r
-                       glBegin(GL_TRIANGLE_STRIP)\r
-                       for i in xrange(0, 64+1):\r
-                               f = i if i < 64/2 else 64 - i\r
-                               glColor4ub(255,int(f*255/(64/2)),0,255)\r
-                               glVertex3f(1.1 * math.cos(i/32.0*math.pi), 1.1 * math.sin(i/32.0*math.pi),0.1)\r
-                               glColor4ub(  0,128,0,255)\r
-                               glVertex3f(1.3 * math.cos(i/32.0*math.pi), 1.3 * math.sin(i/32.0*math.pi),0.1)\r
-                       glEnd()\r
-                       glBegin(GL_TRIANGLES)\r
-                       glColor4ub(0,0,0,192)\r
-                       glVertex3f(1, 0.1,0.15)\r
-                       glVertex3f(1,-0.1,0.15)\r
-                       glVertex3f(1.4,0,0.15)\r
-                       glEnd()\r
-                       glEnable(GL_CULL_FACE)\r
-               \r
-               glPopMatrix()\r
-\r
-               opengl.DrawMachine(machineSize)\r
-               \r
-               glFlush()\r
-       \r
-       def drawModel(self, obj):\r
-               multiX = 1 #int(profile.getProfileSetting('model_multiply_x'))\r
-               multiY = 1 #int(profile.getProfileSetting('model_multiply_y'))\r
-               modelScale = profile.getProfileSettingFloat('model_scale')\r
-               modelSize = (obj.mesh.getMaximum() - obj.mesh.getMinimum()) * modelScale\r
-               glPushMatrix()\r
-               glRotate(self.tempRotate, 0, 0, 1)\r
-               glTranslate(-(modelSize[0]+10)*(multiX-1)/2,-(modelSize[1]+10)*(multiY-1)/2, 0)\r
-               for mx in xrange(0, multiX):\r
-                       for my in xrange(0, multiY):\r
-                               glPushMatrix()\r
-                               glTranslate((modelSize[0]+10)*mx,(modelSize[1]+10)*my, 0)\r
-                               glScalef(modelScale, modelScale, modelScale)\r
-                               glCallList(obj.displayList)\r
-                               glPopMatrix()\r
-               glPopMatrix()\r
-\r
+from __future__ import division
+
+import sys, math, threading, re, time, os
+import numpy
+
+from wx import glcanvas
+import wx
+try:
+       import OpenGL
+       OpenGL.ERROR_CHECKING = False
+       from OpenGL.GLU import *
+       from OpenGL.GL import *
+       hasOpenGLlibs = True
+except:
+       print "Failed to find PyOpenGL: http://pyopengl.sourceforge.net/"
+       hasOpenGLlibs = False
+
+from gui import opengl
+from gui import toolbarUtil
+
+from util import profile
+from util import gcodeInterpreter
+from util import meshLoader
+from util import util3d
+from util import sliceRun
+
+class previewObject():
+       def __init__(self):
+               self.mesh = None
+               self.filename = None
+               self.displayList = None
+               self.dirty = False
+
+class previewPanel(wx.Panel):
+       def __init__(self, parent):
+               super(previewPanel, self).__init__(parent,-1)
+               
+               self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DDKSHADOW))
+               self.SetMinSize((440,320))
+               
+               self.objectList = []
+               self.errorList = []
+               self.gcode = None
+               self.objectsMinV = None
+               self.objectsMaxV = None
+               self.objectsBounderyCircleSize = None
+               self.loadThread = None
+               self.machineSize = util3d.Vector3(profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height'))
+               self.machineCenter = util3d.Vector3(self.machineSize.x / 2, self.machineSize.y / 2, 0)
+
+               self.glCanvas = PreviewGLCanvas(self)
+               #Create the popup window
+               self.warningPopup = wx.PopupWindow(self, flags=wx.BORDER_SIMPLE)
+               self.warningPopup.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_INFOBK))
+               self.warningPopup.text = wx.StaticText(self.warningPopup, -1, 'Reset scale, rotation and mirror?')
+               self.warningPopup.yesButton = wx.Button(self.warningPopup, -1, 'yes', style=wx.BU_EXACTFIT)
+               self.warningPopup.noButton = wx.Button(self.warningPopup, -1, 'no', style=wx.BU_EXACTFIT)
+               self.warningPopup.sizer = wx.BoxSizer(wx.HORIZONTAL)
+               self.warningPopup.SetSizer(self.warningPopup.sizer)
+               self.warningPopup.sizer.Add(self.warningPopup.text, 1, flag=wx.ALL|wx.ALIGN_CENTER_VERTICAL, border=1)
+               self.warningPopup.sizer.Add(self.warningPopup.yesButton, 0, flag=wx.EXPAND|wx.ALL, border=1)
+               self.warningPopup.sizer.Add(self.warningPopup.noButton, 0, flag=wx.EXPAND|wx.ALL, border=1)
+               self.warningPopup.Fit()
+               self.warningPopup.Layout()
+               self.warningPopup.timer = wx.Timer(self)
+               self.Bind(wx.EVT_TIMER, self.OnHideWarning, self.warningPopup.timer)
+               
+               self.Bind(wx.EVT_BUTTON, self.OnWarningPopup, self.warningPopup.yesButton)
+               self.Bind(wx.EVT_BUTTON, self.OnHideWarning, self.warningPopup.noButton)
+               parent.Bind(wx.EVT_MOVE, self.OnMove)
+               parent.Bind(wx.EVT_SIZE, self.OnMove)
+               
+               self.toolbar = toolbarUtil.Toolbar(self)
+
+               group = []
+               toolbarUtil.RadioButton(self.toolbar, group, 'object-3d-on.png', 'object-3d-off.png', '3D view', callback=self.On3DClick)
+               toolbarUtil.RadioButton(self.toolbar, group, 'object-top-on.png', 'object-top-off.png', 'Topdown view', callback=self.OnTopClick)
+               self.toolbar.AddSeparator()
+
+               self.showBorderButton = toolbarUtil.ToggleButton(self.toolbar, '', 'view-border-on.png', 'view-border-off.png', 'Show model borders', callback=self.OnViewChange)
+               self.showSteepOverhang = toolbarUtil.ToggleButton(self.toolbar, '', 'steepOverhang-on.png', 'steepOverhang-off.png', 'Show steep overhang', callback=self.OnViewChange)
+               self.toolbar.AddSeparator()
+
+               group = []
+               self.normalViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-normal-on.png', 'view-normal-off.png', 'Normal model view', callback=self.OnViewChange)
+               self.transparentViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-transparent-on.png', 'view-transparent-off.png', 'Transparent model view', callback=self.OnViewChange)
+               self.xrayViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-xray-on.png', 'view-xray-off.png', 'X-Ray view', callback=self.OnViewChange)
+               self.gcodeViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-gcode-on.png', 'view-gcode-off.png', 'GCode view', callback=self.OnViewChange)
+               self.mixedViewButton = toolbarUtil.RadioButton(self.toolbar, group, 'view-mixed-on.png', 'view-mixed-off.png', 'Mixed model/GCode view', callback=self.OnViewChange)
+               self.toolbar.AddSeparator()
+
+               self.layerSpin = wx.SpinCtrl(self.toolbar, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS)
+               self.toolbar.AddControl(self.layerSpin)
+               self.Bind(wx.EVT_SPINCTRL, self.OnLayerNrChange, self.layerSpin)
+               self.toolbar.AddSeparator()
+               self.toolbarInfo = wx.TextCtrl(self.toolbar, -1, '', style=wx.TE_READONLY)
+               self.toolbar.AddControl(self.toolbarInfo)
+
+               self.toolbar2 = toolbarUtil.Toolbar(self)
+
+               # Mirror
+               self.mirrorX = toolbarUtil.ToggleButton(self.toolbar2, 'flip_x', 'object-mirror-x-on.png', 'object-mirror-x-off.png', 'Mirror X', callback=self.returnToModelViewAndUpdateModel)
+               self.mirrorY = toolbarUtil.ToggleButton(self.toolbar2, 'flip_y', 'object-mirror-y-on.png', 'object-mirror-y-off.png', 'Mirror Y', callback=self.returnToModelViewAndUpdateModel)
+               self.mirrorZ = toolbarUtil.ToggleButton(self.toolbar2, 'flip_z', 'object-mirror-z-on.png', 'object-mirror-z-off.png', 'Mirror Z', callback=self.returnToModelViewAndUpdateModel)
+               self.toolbar2.AddSeparator()
+
+               # Swap
+               self.swapXZ = toolbarUtil.ToggleButton(self.toolbar2, 'swap_xz', 'object-swap-xz-on.png', 'object-swap-xz-off.png', 'Swap XZ', callback=self.returnToModelViewAndUpdateModel)
+               self.swapYZ = toolbarUtil.ToggleButton(self.toolbar2, 'swap_yz', 'object-swap-yz-on.png', 'object-swap-yz-off.png', 'Swap YZ', callback=self.returnToModelViewAndUpdateModel)
+               self.toolbar2.AddSeparator()
+
+               # Scale
+               self.scaleReset = toolbarUtil.NormalButton(self.toolbar2, self.OnScaleReset, 'object-scale.png', 'Reset model scale')
+               self.scale = wx.TextCtrl(self.toolbar2, -1, profile.getProfileSetting('model_scale'), size=(21*2,21))
+               self.toolbar2.AddControl(self.scale)
+               self.scale.Bind(wx.EVT_TEXT, self.OnScale)
+               self.scaleMax = toolbarUtil.NormalButton(self.toolbar2, self.OnScaleMax, 'object-max-size.png', 'Scale object to fit machine size')
+
+               self.toolbar2.AddSeparator()
+
+               # Multiply
+               #self.mulXadd = toolbarUtil.NormalButton(self.toolbar2, self.OnMulXAddClick, 'object-mul-x-add.png', 'Increase number of models on X axis')
+               #self.mulXsub = toolbarUtil.NormalButton(self.toolbar2, self.OnMulXSubClick, 'object-mul-x-sub.png', 'Decrease number of models on X axis')
+               #self.mulYadd = toolbarUtil.NormalButton(self.toolbar2, self.OnMulYAddClick, 'object-mul-y-add.png', 'Increase number of models on Y axis')
+               #self.mulYsub = toolbarUtil.NormalButton(self.toolbar2, self.OnMulYSubClick, 'object-mul-y-sub.png', 'Decrease number of models on Y axis')
+               #self.toolbar2.AddSeparator()
+
+               # Rotate
+               self.rotateReset = toolbarUtil.NormalButton(self.toolbar2, self.OnRotateReset, 'object-rotate.png', 'Reset model rotation')
+               self.rotate = wx.SpinCtrl(self.toolbar2, -1, profile.getProfileSetting('model_rotate_base'), size=(21*3,21), style=wx.SP_WRAP|wx.SP_ARROW_KEYS)
+               self.rotate.SetRange(0, 360)
+               self.rotate.Bind(wx.EVT_TEXT, self.OnRotate)
+               self.toolbar2.AddControl(self.rotate)
+
+               self.toolbar2.Realize()
+               self.OnViewChange()
+               
+               sizer = wx.BoxSizer(wx.VERTICAL)
+               sizer.Add(self.toolbar, 0, flag=wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, border=1)
+               sizer.Add(self.glCanvas, 1, flag=wx.EXPAND)
+               sizer.Add(self.toolbar2, 0, flag=wx.EXPAND|wx.BOTTOM|wx.LEFT|wx.RIGHT, border=1)
+               self.SetSizer(sizer)
+       
+       def returnToModelViewAndUpdateModel(self):
+               if self.glCanvas.viewMode == 'GCode' or self.glCanvas.viewMode == 'Mixed':
+                       self.setViewMode('Normal')
+               self.updateModelTransform()
+       
+       def OnMove(self, e = None):
+               if e != None:
+                       e.Skip()
+               x, y = self.glCanvas.ClientToScreenXY(0, 0)
+               sx, sy = self.glCanvas.GetClientSizeTuple()
+               self.warningPopup.SetPosition((x, y+sy-self.warningPopup.GetSize().height))
+       
+       def OnMulXAddClick(self, e):
+               profile.putProfileSetting('model_multiply_x', str(max(1, int(profile.getProfileSetting('model_multiply_x'))+1)))
+               self.glCanvas.Refresh()
+
+       def OnMulXSubClick(self, e):
+               profile.putProfileSetting('model_multiply_x', str(max(1, int(profile.getProfileSetting('model_multiply_x'))-1)))
+               self.glCanvas.Refresh()
+
+       def OnMulYAddClick(self, e):
+               profile.putProfileSetting('model_multiply_y', str(max(1, int(profile.getProfileSetting('model_multiply_y'))+1)))
+               self.glCanvas.Refresh()
+
+       def OnMulYSubClick(self, e):
+               profile.putProfileSetting('model_multiply_y', str(max(1, int(profile.getProfileSetting('model_multiply_y'))-1)))
+               self.glCanvas.Refresh()
+
+       def OnScaleReset(self, e):
+               self.scale.SetValue('1.0')
+               self.OnScale(None)
+
+       def OnScale(self, e):
+               scale = 1.0
+               if self.scale.GetValue() != '':
+                       scale = self.scale.GetValue()
+               profile.putProfileSetting('model_scale', scale)
+               if self.glCanvas.viewMode == 'GCode' or self.glCanvas.viewMode == 'Mixed':
+                       self.setViewMode('Normal')
+               self.glCanvas.Refresh()
+
+               if self.objectsMaxV != None:
+                       size = (self.objectsMaxV - self.objectsMinV) * float(scale)
+                       self.toolbarInfo.SetValue('%0.1f %0.1f %0.1f' % (size[0], size[1], size[2]))
+
+       def OnScaleMax(self, e = None, onlyScaleDown = False):
+               if self.objectsMinV == None:
+                       return
+               vMin = self.objectsMinV
+               vMax = self.objectsMaxV
+               skirtSize = 3
+               if profile.getProfileSettingFloat('skirt_line_count') > 0:
+                       skirtSize = 3 + profile.getProfileSettingFloat('skirt_line_count') * profile.calculateEdgeWidth() + profile.getProfileSettingFloat('skirt_gap')
+               scaleX1 = (self.machineSize.x - self.machineCenter.x - skirtSize) / ((vMax[0] - vMin[0]) / 2)
+               scaleY1 = (self.machineSize.y - self.machineCenter.y - skirtSize) / ((vMax[1] - vMin[1]) / 2)
+               scaleX2 = (self.machineCenter.x - skirtSize) / ((vMax[0] - vMin[0]) / 2)
+               scaleY2 = (self.machineCenter.y - skirtSize) / ((vMax[1] - vMin[1]) / 2)
+               scaleZ = self.machineSize.z / (vMax[2] - vMin[2])
+               scale = min(scaleX1, scaleY1, scaleX2, scaleY2, scaleZ)
+               if scale > 1.0 and onlyScaleDown:
+                       return
+               self.scale.SetValue(str(scale))
+               profile.putProfileSetting('model_scale', self.scale.GetValue())
+               if self.glCanvas.viewMode == 'GCode' or self.glCanvas.viewMode == 'Mixed':
+                       self.setViewMode('Normal')
+               self.glCanvas.Refresh()
+
+       def OnRotateReset(self, e):
+               self.rotate.SetValue(0)
+               self.OnRotate(None)
+
+       def OnRotate(self, e):
+               profile.putProfileSetting('model_rotate_base', self.rotate.GetValue())
+               self.returnToModelViewAndUpdateModel()
+
+       def On3DClick(self):
+               self.glCanvas.yaw = 30
+               self.glCanvas.pitch = 60
+               self.glCanvas.zoom = 300
+               self.glCanvas.view3D = True
+               self.glCanvas.Refresh()
+
+       def OnTopClick(self):
+               self.glCanvas.view3D = False
+               self.glCanvas.zoom = 100
+               self.glCanvas.offsetX = 0
+               self.glCanvas.offsetY = 0
+               self.glCanvas.Refresh()
+
+       def OnLayerNrChange(self, e):
+               self.glCanvas.Refresh()
+       
+       def setViewMode(self, mode):
+               if mode == "Normal":
+                       self.normalViewButton.SetValue(True)
+               if mode == "GCode":
+                       self.gcodeViewButton.SetValue(True)
+               self.glCanvas.viewMode = mode
+               wx.CallAfter(self.glCanvas.Refresh)
+       
+       def loadModelFiles(self, filelist, showWarning = False):
+               while len(filelist) > len(self.objectList):
+                       self.objectList.append(previewObject())
+               for idx in xrange(len(filelist), len(self.objectList)):
+                       self.objectList[idx].mesh = None
+                       self.objectList[idx].filename = None
+               for idx in xrange(0, len(filelist)):
+                       obj = self.objectList[idx]
+                       if obj.filename != filelist[idx]:
+                               obj.fileTime = None
+                               self.gcodeFileTime = None
+                               self.logFileTime = None
+                       obj.filename = filelist[idx]
+               
+               self.gcodeFilename = sliceRun.getExportFilename(filelist[0])
+               #Do the STL file loading in a background thread so we don't block the UI.
+               if self.loadThread != None and self.loadThread.isAlive():
+                       self.loadThread.join()
+               self.loadThread = threading.Thread(target=self.doFileLoadThread)
+               self.loadThread.daemon = True
+               self.loadThread.start()
+               
+               if showWarning:
+                       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' or len(profile.getPluginConfig()) > 0:
+                               self.ShowWarningPopup('Reset scale, rotation, mirror and plugins?', self.OnResetAll)
+       
+       def loadReModelFiles(self, filelist):
+               #Only load this again if the filename matches the file we have already loaded (for auto loading GCode after slicing)
+               for idx in xrange(0, len(filelist)):
+                       if self.objectList[idx].filename != filelist[idx]:
+                               return False
+               self.loadModelFiles(filelist)
+               return True
+       
+       def doFileLoadThread(self):
+               for obj in self.objectList:
+                       if obj.filename != None and os.path.isfile(obj.filename) and obj.fileTime != os.stat(obj.filename).st_mtime:
+                               obj.ileTime = os.stat(obj.filename).st_mtime
+                               mesh = meshLoader.loadMesh(obj.filename)
+                               obj.dirty = False
+                               obj.mesh = mesh
+                               self.updateModelTransform()
+                               self.OnScaleMax(None, True)
+                               scale = profile.getProfileSettingFloat('model_scale')
+                               size = (self.objectsMaxV - self.objectsMinV) * scale
+                               self.toolbarInfo.SetValue('%0.1f %0.1f %0.1f' % (size[0], size[1], size[2]))
+                               self.glCanvas.zoom = numpy.max(size) * 2.5
+                               self.errorList = []
+                               wx.CallAfter(self.updateToolbar)
+                               wx.CallAfter(self.glCanvas.Refresh)
+               
+               if os.path.isfile(self.gcodeFilename) and self.gcodeFileTime != os.stat(self.gcodeFilename).st_mtime:
+                       self.gcodeFileTime = os.stat(self.gcodeFilename).st_mtime
+                       gcode = gcodeInterpreter.gcode()
+                       gcode.progressCallback = self.loadProgress
+                       gcode.load(self.gcodeFilename)
+                       self.gcodeDirty = False
+                       self.gcode = gcode
+                       self.gcodeDirty = True
+
+                       errorList = []
+                       for line in open(self.gcodeFilename, "rt"):
+                               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)
+                               if res != None:
+                                       v1 = util3d.Vector3(float(res.group(2)), float(res.group(3)), float(res.group(4)))
+                                       v2 = util3d.Vector3(float(res.group(5)), float(res.group(6)), float(res.group(7)))
+                                       errorList.append([v1, v2])
+                       self.errorList = errorList
+
+                       wx.CallAfter(self.updateToolbar)
+                       wx.CallAfter(self.glCanvas.Refresh)
+               elif not os.path.isfile(self.gcodeFilename):
+                       self.gcode = None
+       
+       def loadProgress(self, progress):
+               pass
+
+       def OnResetAll(self, e = None):
+               profile.putProfileSetting('model_scale', '1.0')
+               profile.putProfileSetting('model_rotate_base', '0')
+               profile.putProfileSetting('flip_x', 'False')
+               profile.putProfileSetting('flip_y', 'False')
+               profile.putProfileSetting('flip_z', 'False')
+               profile.putProfileSetting('swap_xz', 'False')
+               profile.putProfileSetting('swap_yz', 'False')
+               profile.setPluginConfig([])
+               self.GetParent().updateProfileToControls()
+
+       def ShowWarningPopup(self, text, callback = None):
+               self.warningPopup.text.SetLabel(text)
+               self.warningPopup.callback = callback
+               if callback == None:
+                       self.warningPopup.yesButton.Show(False)
+                       self.warningPopup.noButton.SetLabel('ok')
+               else:
+                       self.warningPopup.yesButton.Show(True)
+                       self.warningPopup.noButton.SetLabel('no')
+               self.warningPopup.Fit()
+               self.warningPopup.Layout()
+               self.OnMove()
+               self.warningPopup.Show(True)
+               self.warningPopup.timer.Start(5000)
+       
+       def OnWarningPopup(self, e):
+               self.warningPopup.Show(False)
+               self.warningPopup.timer.Stop()
+               self.warningPopup.callback()
+
+       def OnHideWarning(self, e):
+               self.warningPopup.Show(False)
+               self.warningPopup.timer.Stop()
+
+       def updateToolbar(self):
+               self.gcodeViewButton.Show(self.gcode != None)
+               self.mixedViewButton.Show(self.gcode != None)
+               self.layerSpin.Show(self.glCanvas.viewMode == "GCode" or self.glCanvas.viewMode == "Mixed")
+               if self.gcode != None:
+                       self.layerSpin.SetRange(1, len(self.gcode.layerList) - 1)
+               self.toolbar.Realize()
+               self.Update()
+       
+       def OnViewChange(self):
+               if self.normalViewButton.GetValue():
+                       self.glCanvas.viewMode = "Normal"
+               elif self.transparentViewButton.GetValue():
+                       self.glCanvas.viewMode = "Transparent"
+               elif self.xrayViewButton.GetValue():
+                       self.glCanvas.viewMode = "X-Ray"
+               elif self.gcodeViewButton.GetValue():
+                       self.glCanvas.viewMode = "GCode"
+               elif self.mixedViewButton.GetValue():
+                       self.glCanvas.viewMode = "Mixed"
+               self.glCanvas.drawBorders = self.showBorderButton.GetValue()
+               self.glCanvas.drawSteepOverhang = self.showSteepOverhang.GetValue()
+               self.updateToolbar()
+               self.glCanvas.Refresh()
+       
+       def updateModelTransform(self, f=0):
+               if len(self.objectList) < 1 or self.objectList[0].mesh == None:
+                       return
+               
+               rotate = profile.getProfileSettingFloat('model_rotate_base')
+               mirrorX = profile.getProfileSetting('flip_x') == 'True'
+               mirrorY = profile.getProfileSetting('flip_y') == 'True'
+               mirrorZ = profile.getProfileSetting('flip_z') == 'True'
+               swapXZ = profile.getProfileSetting('swap_xz') == 'True'
+               swapYZ = profile.getProfileSetting('swap_yz') == 'True'
+
+               for obj in self.objectList:
+                       if obj.mesh == None:
+                               continue
+                       obj.mesh.setRotateMirror(rotate, mirrorX, mirrorY, mirrorZ, swapXZ, swapYZ)
+               
+               minV = self.objectList[0].mesh.getMinimum()
+               maxV = self.objectList[0].mesh.getMaximum()
+               objectsBounderyCircleSize = self.objectList[0].mesh.bounderyCircleSize
+               for obj in self.objectList:
+                       if obj.mesh == None:
+                               continue
+
+                       obj.mesh.getMinimumZ()
+                       minV = numpy.minimum(minV, obj.mesh.getMinimum())
+                       maxV = numpy.maximum(maxV, obj.mesh.getMaximum())
+                       objectsBounderyCircleSize = max(objectsBounderyCircleSize, obj.mesh.bounderyCircleSize)
+
+               self.objectsMaxV = maxV
+               self.objectsMinV = minV
+               self.objectsBounderyCircleSize = objectsBounderyCircleSize
+               for obj in self.objectList:
+                       if obj.mesh == None:
+                               continue
+
+                       obj.mesh.vertexes -= numpy.array([minV[0] + (maxV[0] - minV[0]) / 2, minV[1] + (maxV[1] - minV[1]) / 2, minV[2]])
+                       #for v in obj.mesh.vertexes:
+                       #       v[2] -= minV[2]
+                       #       v[0] -= minV[0] + (maxV[0] - minV[0]) / 2
+                       #       v[1] -= minV[1] + (maxV[1] - minV[1]) / 2
+                       obj.mesh.getMinimumZ()
+                       obj.dirty = True
+
+               scale = profile.getProfileSettingFloat('model_scale')
+               size = (self.objectsMaxV - self.objectsMinV) * scale
+               self.toolbarInfo.SetValue('%0.1f %0.1f %0.1f' % (size[0], size[1], size[2]))
+
+               self.glCanvas.Refresh()
+       
+       def updateProfileToControls(self):
+               self.scale.SetValue(profile.getProfileSetting('model_scale'))
+               self.rotate.SetValue(profile.getProfileSettingFloat('model_rotate_base'))
+               self.mirrorX.SetValue(profile.getProfileSetting('flip_x') == 'True')
+               self.mirrorY.SetValue(profile.getProfileSetting('flip_y') == 'True')
+               self.mirrorZ.SetValue(profile.getProfileSetting('flip_z') == 'True')
+               self.swapXZ.SetValue(profile.getProfileSetting('swap_xz') == 'True')
+               self.swapYZ.SetValue(profile.getProfileSetting('swap_yz') == 'True')
+               self.updateModelTransform()
+               self.glCanvas.updateProfileToControls()
+
+class PreviewGLCanvas(glcanvas.GLCanvas):
+       def __init__(self, parent):
+               attribList = (glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24, glcanvas.WX_GL_STENCIL_SIZE, 8)
+               glcanvas.GLCanvas.__init__(self, parent, attribList = attribList)
+               self.parent = parent
+               self.context = glcanvas.GLContext(self)
+               wx.EVT_PAINT(self, self.OnPaint)
+               wx.EVT_SIZE(self, self.OnSize)
+               wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)
+               wx.EVT_MOTION(self, self.OnMouseMotion)
+               wx.EVT_MOUSEWHEEL(self, self.OnMouseWheel)
+               self.yaw = 30
+               self.pitch = 60
+               self.zoom = 300
+               self.offsetX = 0
+               self.offsetY = 0
+               self.view3D = True
+               self.gcodeDisplayList = None
+               self.gcodeDisplayListMade = None
+               self.gcodeDisplayListCount = 0
+               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]]
+               self.oldX = 0
+               self.oldY = 0
+               self.dragType = ''
+               self.tempRotate = 0
+       
+       def updateProfileToControls(self):
+               self.objColor[0] = profile.getPreferenceColour('model_colour')
+               self.objColor[1] = profile.getPreferenceColour('model_colour2')
+               self.objColor[2] = profile.getPreferenceColour('model_colour3')
+               self.objColor[3] = profile.getPreferenceColour('model_colour4')
+
+       def OnMouseMotion(self,e):
+               cursorXY = 100000
+               radius = 0
+               if self.parent.objectsMaxV != None:
+                       radius = self.parent.objectsBounderyCircleSize * profile.getProfileSettingFloat('model_scale')
+                       
+                       p0 = numpy.array(gluUnProject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 0, self.modelMatrix, self.projMatrix, self.viewport))
+                       p1 = numpy.array(gluUnProject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 1, self.modelMatrix, self.projMatrix, self.viewport))
+                       cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
+                       cursorXY = math.sqrt((cursorZ0[0] * cursorZ0[0]) + (cursorZ0[1] * cursorZ0[1]))
+                       if cursorXY >= radius * 1.1 and cursorXY <= radius * 1.3:
+                               self.SetCursor(wx.StockCursor(wx.CURSOR_SIZING))
+                       else:
+                               self.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+
+               if e.Dragging() and e.LeftIsDown():
+                       if self.dragType == '':
+                               #Define the drag type depending on the cursor position.
+                               if cursorXY >= radius * 1.1 and cursorXY <= radius * 1.3:
+                                       self.dragType = 'modelRotate'
+                                       self.dragStart = math.atan2(cursorZ0[0], cursorZ0[1])
+                               else:
+                                       self.dragType = 'viewRotate'
+                               
+                       if self.dragType == 'viewRotate':
+                               if self.view3D:
+                                       self.yaw += e.GetX() - self.oldX
+                                       self.pitch -= e.GetY() - self.oldY
+                                       if self.pitch > 170:
+                                               self.pitch = 170
+                                       if self.pitch < 10:
+                                               self.pitch = 10
+                               else:
+                                       self.offsetX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2
+                                       self.offsetY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2
+                       elif self.dragType == 'modelRotate':
+                               angle = math.atan2(cursorZ0[0], cursorZ0[1])
+                               diff = self.dragStart - angle
+                               self.tempRotate = diff * 180 / math.pi
+                               rot = profile.getProfileSettingFloat('model_rotate_base')
+                               self.tempRotate = round((self.tempRotate + rot) / 15) * 15 - rot
+                       #Workaround for buggy ATI cards.
+                       size = self.GetSizeTuple()
+                       self.SetSize((size[0]+1, size[1]))
+                       self.SetSize((size[0], size[1]))
+                       self.Refresh()
+               else:
+                       if self.tempRotate != 0:
+                               newRotation = profile.getProfileSettingFloat('model_rotate_base') + self.tempRotate
+                               while newRotation >= 360:
+                                       newRotation -= 360
+                               while newRotation < 0:
+                                       newRotation += 360
+                               profile.putProfileSetting('model_rotate_base', newRotation)
+                               self.parent.rotate.SetValue(newRotation)
+                               self.parent.updateModelTransform()
+                               self.tempRotate = 0
+                               
+                       self.dragType = ''
+               if e.Dragging() and e.RightIsDown():
+                       self.zoom += e.GetY() - self.oldY
+                       if self.zoom < 1:
+                               self.zoom = 1
+                       if self.zoom > 500:
+                               self.zoom = 500
+                       self.Refresh()
+               self.oldX = e.GetX()
+               self.oldY = e.GetY()
+
+               #self.Refresh()
+               
+       
+       def OnMouseWheel(self,e):
+               self.zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0
+               if self.zoom < 1.0:
+                       self.zoom = 1.0
+               if self.zoom > 500:
+                       self.zoom = 500
+               self.Refresh()
+       
+       def OnEraseBackground(self,event):
+               #Workaround for windows background redraw flicker.
+               pass
+       
+       def OnSize(self,e):
+               self.Refresh()
+
+       def OnPaint(self,e):
+               dc = wx.PaintDC(self)
+               if not hasOpenGLlibs:
+                       dc.Clear()
+                       dc.DrawText("No PyOpenGL installation found.\nNo preview window available.", 10, 10)
+                       return
+               self.SetCurrent(self.context)
+               opengl.InitGL(self, self.view3D, self.zoom)
+               if self.view3D:
+                       glTranslate(0,0,-self.zoom)
+                       glRotate(-self.pitch, 1,0,0)
+                       glRotate(self.yaw, 0,0,1)
+                       if self.viewMode == "GCode" or self.viewMode == "Mixed":
+                               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:
+                                       glTranslate(0,0,-self.parent.gcode.layerList[self.parent.layerSpin.GetValue()][0].list[-1].z)
+                       else:
+                               if self.parent.objectsMaxV != None:
+                                       glTranslate(0,0,-(self.parent.objectsMaxV[2]-self.parent.objectsMinV[2]) * profile.getProfileSettingFloat('model_scale') / 2)
+               else:
+                       glTranslate(self.offsetX, self.offsetY, 0)
+
+               self.viewport = glGetIntegerv(GL_VIEWPORT);
+               self.modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX);
+               self.projMatrix = glGetDoublev(GL_PROJECTION_MATRIX);
+
+               glTranslate(-self.parent.machineCenter.x, -self.parent.machineCenter.y, 0)
+
+               self.OnDraw()
+               self.SwapBuffers()
+
+       def OnDraw(self):
+               machineSize = self.parent.machineSize
+
+               if self.parent.gcode != None and self.parent.gcodeDirty:
+                       if self.gcodeDisplayListCount < len(self.parent.gcode.layerList) or self.gcodeDisplayList == None:
+                               if self.gcodeDisplayList != None:
+                                       glDeleteLists(self.gcodeDisplayList, self.gcodeDisplayListCount)
+                               self.gcodeDisplayList = glGenLists(len(self.parent.gcode.layerList));
+                               self.gcodeDisplayListCount = len(self.parent.gcode.layerList)
+                       self.parent.gcodeDirty = False
+                       self.gcodeDisplayListMade = 0
+               
+               if self.parent.gcode != None and self.gcodeDisplayListMade < len(self.parent.gcode.layerList):
+                       glNewList(self.gcodeDisplayList + self.gcodeDisplayListMade, GL_COMPILE)
+                       opengl.DrawGCodeLayer(self.parent.gcode.layerList[self.gcodeDisplayListMade])
+                       glEndList()
+                       self.gcodeDisplayListMade += 1
+                       wx.CallAfter(self.Refresh)
+               
+               glPushMatrix()
+               glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)
+               for obj in self.parent.objectList:
+                       if obj.mesh == None:
+                               continue
+                       if obj.displayList == None:
+                               obj.displayList = glGenLists(1)
+                               obj.steepDisplayList = glGenLists(1)
+                       if obj.dirty:
+                               obj.dirty = False
+                               glNewList(obj.displayList, GL_COMPILE)
+                               opengl.DrawMesh(obj.mesh)
+                               glEndList()
+                               glNewList(obj.steepDisplayList, GL_COMPILE)
+                               opengl.DrawMeshSteep(obj.mesh, 60)
+                               glEndList()
+                       
+                       if self.viewMode == "Mixed":
+                               glDisable(GL_BLEND)
+                               glColor3f(0.0,0.0,0.0)
+                               self.drawModel(obj)
+                               glColor3f(1.0,1.0,1.0)
+                               glClear(GL_DEPTH_BUFFER_BIT)
+               
+               glPopMatrix()
+               
+               if self.parent.gcode != None and (self.viewMode == "GCode" or self.viewMode == "Mixed"):
+                       glEnable(GL_COLOR_MATERIAL)
+                       glEnable(GL_LIGHTING)
+                       drawUpToLayer = min(self.gcodeDisplayListMade, self.parent.layerSpin.GetValue() + 1)
+                       starttime = time.time()
+                       for i in xrange(drawUpToLayer - 1, -1, -1):
+                               c = 1.0
+                               if i < self.parent.layerSpin.GetValue():
+                                       c = 0.9 - (drawUpToLayer - i) * 0.1
+                                       if c < 0.4:
+                                               c = (0.4 + c) / 2
+                                       if c < 0.1:
+                                               c = 0.1
+                               glLightfv(GL_LIGHT0, GL_DIFFUSE, [0,0,0,0])
+                               glLightfv(GL_LIGHT0, GL_AMBIENT, [c,c,c,c])
+                               glCallList(self.gcodeDisplayList + i)
+                               if time.time() - starttime > 0.1:
+                                       break
+
+                       glDisable(GL_LIGHTING)
+                       glDisable(GL_COLOR_MATERIAL)
+                       glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, [0.2, 0.2, 0.2, 1.0]);
+                       glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, [0.8, 0.8, 0.8, 1.0]);
+
+               glColor3f(1.0,1.0,1.0)
+               glPushMatrix()
+               glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)
+               for obj in self.parent.objectList:
+                       if obj.mesh == None:
+                               continue
+                       
+                       if self.viewMode == "Transparent" or self.viewMode == "Mixed":
+                               glLightfv(GL_LIGHT0, GL_DIFFUSE, map(lambda x: x / 2, self.objColor[self.parent.objectList.index(obj)]))
+                               glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x / 10, self.objColor[self.parent.objectList.index(obj)]))
+                               #If we want transparent, then first render a solid black model to remove the printer size lines.
+                               if self.viewMode != "Mixed":
+                                       glDisable(GL_BLEND)
+                                       glColor3f(0.0,0.0,0.0)
+                                       self.drawModel(obj)
+                                       glColor3f(1.0,1.0,1.0)
+                               #After the black model is rendered, render the model again but now with lighting and no depth testing.
+                               glDisable(GL_DEPTH_TEST)
+                               glEnable(GL_LIGHTING)
+                               glEnable(GL_BLEND)
+                               glBlendFunc(GL_ONE, GL_ONE)
+                               glEnable(GL_LIGHTING)
+                               self.drawModel(obj)
+                               glEnable(GL_DEPTH_TEST)
+                       elif self.viewMode == "X-Ray":
+                               glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
+                               glDisable(GL_LIGHTING)
+                               glDisable(GL_DEPTH_TEST)
+                               glEnable(GL_STENCIL_TEST)
+                               glStencilFunc(GL_ALWAYS, 1, 1)
+                               glStencilOp(GL_INCR, GL_INCR, GL_INCR)
+                               self.drawModel(obj)
+                               glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
+                               
+                               glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
+                               glStencilFunc(GL_EQUAL, 0, 1)
+                               glColor(1, 1, 1)
+                               self.drawModel(obj)
+                               glStencilFunc(GL_EQUAL, 1, 1)
+                               glColor(1, 0, 0)
+                               self.drawModel(obj)
+
+                               glPushMatrix()
+                               glLoadIdentity()
+                               for i in xrange(2, 15, 2):
+                                       glStencilFunc(GL_EQUAL, i, 0xFF);
+                                       glColor(float(i)/10, float(i)/10, float(i)/5)
+                                       glBegin(GL_QUADS)
+                                       glVertex3f(-1000,-1000,-1)
+                                       glVertex3f( 1000,-1000,-1)
+                                       glVertex3f( 1000, 1000,-1)
+                                       glVertex3f(-1000, 1000,-1)
+                                       glEnd()
+                               for i in xrange(1, 15, 2):
+                                       glStencilFunc(GL_EQUAL, i, 0xFF);
+                                       glColor(float(i)/10, 0, 0)
+                                       glBegin(GL_QUADS)
+                                       glVertex3f(-1000,-1000,-1)
+                                       glVertex3f( 1000,-1000,-1)
+                                       glVertex3f( 1000, 1000,-1)
+                                       glVertex3f(-1000, 1000,-1)
+                                       glEnd()
+                               glPopMatrix()
+
+                               glDisable(GL_STENCIL_TEST)
+                               glEnable(GL_DEPTH_TEST)
+                               
+                               #Fix the depth buffer
+                               glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
+                               self.drawModel(obj)
+                               glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
+                       elif self.viewMode == "Normal":
+                               glLightfv(GL_LIGHT0, GL_DIFFUSE, self.objColor[self.parent.objectList.index(obj)])
+                               glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x * 0.4, self.objColor[self.parent.objectList.index(obj)]))
+                               glEnable(GL_LIGHTING)
+                               self.drawModel(obj)
+
+                       if self.drawBorders and (self.viewMode == "Normal" or self.viewMode == "Transparent" or self.viewMode == "X-Ray"):
+                               glEnable(GL_DEPTH_TEST)
+                               glDisable(GL_LIGHTING)
+                               glColor3f(1,1,1)
+                               glPushMatrix()
+                               modelScale = profile.getProfileSettingFloat('model_scale')
+                               glScalef(modelScale, modelScale, modelScale)
+                               opengl.DrawMeshOutline(obj.mesh)
+                               glPopMatrix()
+                       
+                       if self.drawSteepOverhang:
+                               glDisable(GL_LIGHTING)
+                               glColor3f(1,1,1)
+                               glPushMatrix()
+                               modelScale = profile.getProfileSettingFloat('model_scale')
+                               glScalef(modelScale, modelScale, modelScale)
+                               glCallList(obj.steepDisplayList)
+                               glPopMatrix()
+               
+               glPopMatrix()   
+               if self.viewMode == "Normal" or self.viewMode == "Transparent" or self.viewMode == "X-Ray":
+                       glDisable(GL_LIGHTING)
+                       glDisable(GL_DEPTH_TEST)
+                       glDisable(GL_BLEND)
+                       glColor3f(1,0,0)
+                       glBegin(GL_LINES)
+                       for err in self.parent.errorList:
+                               glVertex3f(err[0].x, err[0].y, err[0].z)
+                               glVertex3f(err[1].x, err[1].y, err[1].z)
+                       glEnd()
+                       glEnable(GL_DEPTH_TEST)
+
+               glPushMatrix()
+               glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)
+               
+               #Draw the rotate circle
+               if self.parent.objectsMaxV != None:
+                       glDisable(GL_LIGHTING)
+                       glDisable(GL_CULL_FACE)
+                       glEnable(GL_BLEND)
+                       glRotate(self.tempRotate + profile.getProfileSettingFloat('model_rotate_base'), 0, 0, 1)
+                       radius = self.parent.objectsBounderyCircleSize * profile.getProfileSettingFloat('model_scale')
+                       glScalef(radius, radius, 1)
+                       glBegin(GL_TRIANGLE_STRIP)
+                       for i in xrange(0, 64+1):
+                               f = i if i < 64/2 else 64 - i
+                               glColor4ub(255,int(f*255/(64/2)),0,255)
+                               glVertex3f(1.1 * math.cos(i/32.0*math.pi), 1.1 * math.sin(i/32.0*math.pi),0.1)
+                               glColor4ub(  0,128,0,255)
+                               glVertex3f(1.3 * math.cos(i/32.0*math.pi), 1.3 * math.sin(i/32.0*math.pi),0.1)
+                       glEnd()
+                       glBegin(GL_TRIANGLES)
+                       glColor4ub(0,0,0,192)
+                       glVertex3f(1, 0.1,0.15)
+                       glVertex3f(1,-0.1,0.15)
+                       glVertex3f(1.4,0,0.15)
+                       glEnd()
+                       glEnable(GL_CULL_FACE)
+               
+               glPopMatrix()
+
+               opengl.DrawMachine(machineSize)
+               
+               glFlush()
+       
+       def drawModel(self, obj):
+               multiX = 1 #int(profile.getProfileSetting('model_multiply_x'))
+               multiY = 1 #int(profile.getProfileSetting('model_multiply_y'))
+               modelScale = profile.getProfileSettingFloat('model_scale')
+               modelSize = (obj.mesh.getMaximum() - obj.mesh.getMinimum()) * modelScale
+               glPushMatrix()
+               glRotate(self.tempRotate, 0, 0, 1)
+               glTranslate(-(modelSize[0]+10)*(multiX-1)/2,-(modelSize[1]+10)*(multiY-1)/2, 0)
+               for mx in xrange(0, multiX):
+                       for my in xrange(0, multiY):
+                               glPushMatrix()
+                               glTranslate((modelSize[0]+10)*mx,(modelSize[1]+10)*my, 0)
+                               glScalef(modelScale, modelScale, modelScale)
+                               glCallList(obj.displayList)
+                               glPopMatrix()
+               glPopMatrix()
+