OctoPrint/Cura/gui/preview3d.py

799 lines
32 KiB
Python
Raw Normal View History

2012-04-10 14:45:53 +00:00
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
2012-03-28 12:26:40 +00:00
from util import profile
from util import gcodeInterpreter
2012-09-26 12:35:35 +00:00
from util import meshLoader
2012-03-28 12:26:40 +00:00
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))
2012-03-22 19:29:03 +00:00
self.objectList = []
self.errorList = []
self.gcode = None
self.objectsMinV = None
self.objectsMaxV = None
self.loadThread = None
self.machineSize = util3d.Vector3(profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height'))
2012-03-29 12:45:14 +00:00
self.machineCenter = util3d.Vector3(float(profile.getProfileSetting('machine_center_x')), float(profile.getProfileSetting('machine_center_y')), 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 = []
2012-04-18 17:03:31 +00:00
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()
2012-07-31 14:39:31 +00:00
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, '', 'view-border-on.png', 'view-border-off.png', 'Show steep overhang', callback=self.OnViewChange)
2012-07-31 14:39:31 +00:00
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)
2012-04-17 22:41:40 +00:00
self.toolbar2 = toolbarUtil.Toolbar(self)
2012-03-21 14:53:29 +00:00
# Mirror
self.mirrorX = toolbarUtil.ToggleButton(self.toolbar2, 'flip_x', 'object-mirror-x-on.png', 'object-mirror-x-off.png', 'Mirror X', callback=self.updateModelTransform)
self.mirrorY = toolbarUtil.ToggleButton(self.toolbar2, 'flip_y', 'object-mirror-y-on.png', 'object-mirror-y-off.png', 'Mirror Y', callback=self.updateModelTransform)
self.mirrorZ = toolbarUtil.ToggleButton(self.toolbar2, 'flip_z', 'object-mirror-z-on.png', 'object-mirror-z-off.png', 'Mirror Z', callback=self.updateModelTransform)
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.updateModelTransform)
self.swapYZ = toolbarUtil.ToggleButton(self.toolbar2, 'swap_yz', 'object-swap-yz-on.png', 'object-swap-yz-off.png', 'Swap YZ', callback=self.updateModelTransform)
2012-04-17 22:41:40 +00:00
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')
2012-04-17 22:41:40 +00:00
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()
2012-04-17 22:41:40 +00:00
# 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)
2012-03-21 14:53:29 +00:00
sizer.Add(self.toolbar2, 0, flag=wx.EXPAND|wx.BOTTOM|wx.LEFT|wx.RIGHT, border=1)
self.SetSizer(sizer)
2012-04-12 21:13:56 +00:00
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):
2012-04-17 23:56:25 +00:00
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)
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())
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.updateModelTransform()
def On3DClick(self):
self.glCanvas.yaw = 30
self.glCanvas.pitch = 60
2012-04-22 11:11:21 +00:00
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 updateCenterX(self):
self.machineCenter.x = profile.getProfileSettingFloat('machine_center_x')
self.glCanvas.Refresh()
def updateCenterY(self):
self.machineCenter.y = profile.getProfileSettingFloat('machine_center_y')
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
2012-09-26 12:35:35 +00:00
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')
2012-11-06 10:06:09 +00:00
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"
2012-07-31 14:39:31 +00:00
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()
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())
self.objectsMaxV = maxV
self.objectsMinV = minV
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()
2012-07-30 10:26:30 +00:00
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
2012-07-30 10:26:30 +00:00
def updateProfileToControls(self):
self.objColor[0] = profile.getPreferenceColour('model_colour')
self.objColor[1] = profile.getPreferenceColour('model_colour2')
self.objColor[2] = profile.getPreferenceColour('model_colour3')
2012-11-08 07:22:05 +00:00
self.objColor[3] = profile.getPreferenceColour('model_colour4')
2012-07-30 10:26:30 +00:00
def OnMouseMotion(self,e):
cursorXY = 100000
sizeXY = 0
if self.parent.objectsMaxV != None:
size = (self.parent.objectsMaxV - self.parent.objectsMinV)
sizeXY = math.sqrt((size[0] * size[0]) + (size[1] * size[1]))
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 >= sizeXY * 0.7 and cursorXY <= sizeXY * 0.7 + 3 and False:
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 >= sizeXY * 0.7 and cursorXY <= sizeXY * 0.7 + 3 and False:
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
#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:
profile.putProfileSetting('model_rotate_base', profile.getProfileSettingFloat('model_rotate_base') + self.tempRotate)
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):
2012-03-07 21:45:41 +00:00
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()
2012-03-09 15:26:54 +00:00
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)
2012-09-07 15:42:40 +00:00
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)
2012-09-07 15:42:40 +00:00
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()
2012-09-07 15:42:40 +00:00
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)
2012-09-07 15:42:40 +00:00
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)
2012-09-07 15:42:40 +00:00
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)
2012-07-31 14:39:31 +00:00
2012-09-07 15:42:40 +00:00
#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)
2012-07-31 14:39:31 +00:00
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)
2012-07-31 14:39:31 +00:00
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)
opengl.DrawMachine(machineSize)
glPushMatrix()
glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)
#Draw the rotate circle
if self.parent.objectsMaxV != None and False:
glDisable(GL_LIGHTING)
glDisable(GL_CULL_FACE)
glEnable(GL_BLEND)
glBegin(GL_TRIANGLE_STRIP)
size = (self.parent.objectsMaxV - self.parent.objectsMinV)
sizeXY = math.sqrt((size[0] * size[0]) + (size[1] * size[1]))
for i in xrange(0, 64+1):
f = i if i < 64/2 else 64 - i
glColor4ub(255,int(f*255/(64/2)),0,128)
glVertex3f(sizeXY * 0.7 * math.cos(i/32.0*math.pi), sizeXY * 0.7 * math.sin(i/32.0*math.pi),0.1)
glColor4ub( 0,128,0,128)
glVertex3f((sizeXY * 0.7 + 3) * math.cos(i/32.0*math.pi), (sizeXY * 0.7 + 3) * math.sin(i/32.0*math.pi),0.1)
glEnd()
glEnable(GL_CULL_FACE)
glPopMatrix()
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()