2012-04-10 14:45:53 +00:00
from __future__ import division
2012-02-22 19:44:00 +00:00
import sys
import math
import threading
import re
2012-03-14 15:24:18 +00:00
import time
2012-03-18 12:00:31 +00:00
import os
2012-02-19 23:30:49 +00:00
2012-02-29 17:25:57 +00:00
from wx import glcanvas
2012-02-23 21:47:42 +00:00
import wx
2012-02-20 15:44:43 +00:00
try :
2012-03-22 14:12:37 +00:00
import OpenGL
OpenGL . ERROR_CHECKING = False
2012-02-20 15:44:43 +00:00
from OpenGL . GLU import *
from OpenGL . GL import *
hasOpenGLlibs = True
except :
print " Failed to find PyOpenGL: http://pyopengl.sourceforge.net/ "
hasOpenGLlibs = False
2012-04-05 20:35:52 +00:00
from gui import opengl
2012-04-18 10:15:07 +00:00
from gui import toolbarUtil
2012-04-05 20:35:52 +00:00
2012-03-28 12:26:40 +00:00
from util import profile
from util import gcodeInterpreter
2012-03-28 14:53:08 +00:00
from util import stl
2012-03-28 12:26:40 +00:00
from util import util3d
2012-03-15 16:14:20 +00:00
2012-02-23 13:08:34 +00:00
class previewPanel ( wx . Panel ) :
2012-02-19 23:30:49 +00:00
def __init__ ( self , parent ) :
2012-04-18 10:15:07 +00:00
super ( previewPanel , self ) . __init__ ( parent , - 1 )
2012-02-23 13:08:34 +00:00
self . SetBackgroundColour ( wx . SystemSettings . GetColour ( wx . SYS_COLOUR_3DDKSHADOW ) )
2012-03-22 11:33:39 +00:00
self . SetMinSize ( ( 440 , 320 ) )
2012-03-22 19:29:03 +00:00
2012-02-23 21:47:42 +00:00
self . glCanvas = PreviewGLCanvas ( self )
2012-02-19 23:30:49 +00:00
self . init = 0
self . triangleMesh = None
2012-02-28 16:39:46 +00:00
self . gcode = None
2012-03-19 15:15:16 +00:00
self . modelFilename = None
2012-03-22 15:04:53 +00:00
self . loadingProgressAmount = 0
self . loadThread = None
2012-03-28 14:53:08 +00:00
self . machineSize = util3d . Vector3 ( float ( profile . getPreference ( ' machine_width ' ) ) , float ( profile . getPreference ( ' machine_depth ' ) ) , float ( profile . getPreference ( ' 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 )
2012-02-23 13:08:34 +00:00
2012-04-18 10:15:07 +00:00
self . toolbar = toolbarUtil . Toolbar ( self )
2012-02-28 14:02:24 +00:00
2012-04-18 10:39:05 +00:00
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 )
2012-04-18 10:39:05 +00:00
self . toolbar . AddSeparator ( )
2012-02-28 16:39:46 +00:00
2012-04-18 13:14:43 +00:00
group = [ ]
2012-04-18 18:27:34 +00:00
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 )
2012-04-18 13:14:43 +00:00
self . OnViewChange ( )
self . toolbar . AddSeparator ( )
2012-03-18 12:00:31 +00:00
2012-03-05 21:30:54 +00:00
self . layerSpin = wx . SpinCtrl ( self . toolbar , - 1 , ' ' , size = ( 21 * 4 , 21 ) , style = wx . SP_ARROW_KEYS )
self . toolbar . AddControl ( self . layerSpin )
2012-02-28 16:39:46 +00:00
self . Bind ( wx . EVT_SPINCTRL , self . OnLayerNrChange , self . layerSpin )
2012-04-17 22:41:40 +00:00
2012-04-18 10:15:07 +00:00
self . toolbar2 = toolbarUtil . Toolbar ( self )
2012-03-21 14:53:29 +00:00
2012-04-18 09:00:41 +00:00
# Mirror
2012-04-18 10:15:07 +00:00
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 )
2012-04-12 20:18:34 +00:00
self . toolbar2 . AddSeparator ( )
2012-04-18 09:00:41 +00:00
# Swap
2012-04-18 10:15:07 +00:00
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 ( )
2012-04-18 09:00:41 +00:00
# Scale
2012-04-18 10:15:07 +00:00
self . scaleReset = toolbarUtil . NormalButton ( self . toolbar2 , self . OnScaleReset , ' object-scale.png ' , ' Reset model scale ' )
2012-03-21 15:53:05 +00:00
self . scale = wx . TextCtrl ( self . toolbar2 , - 1 , profile . getProfileSetting ( ' model_scale ' ) , size = ( 21 * 2 , 21 ) )
self . toolbar2 . AddControl ( self . scale )
2012-04-18 09:00:41 +00:00
self . scale . Bind ( wx . EVT_TEXT , self . OnScale )
2012-04-18 10:22:01 +00:00
self . scaleMax = toolbarUtil . NormalButton ( self . toolbar2 , self . OnScaleMax , ' object-max-size.png ' , ' Scale object to fix machine size ' )
2012-03-22 10:24:52 +00:00
2012-04-17 22:41:40 +00:00
self . toolbar2 . AddSeparator ( )
2012-04-18 09:00:41 +00:00
# Multiply
2012-04-18 10:15:07 +00:00
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 ' )
2012-04-17 22:41:40 +00:00
self . toolbar2 . AddSeparator ( )
2012-04-18 09:00:41 +00:00
# Rotate
2012-04-18 10:15:07 +00:00
self . rotateReset = toolbarUtil . NormalButton ( self . toolbar2 , self . OnRotateReset , ' object-rotate.png ' , ' Reset model rotation ' )
2012-03-22 11:33:39 +00:00
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 )
2012-04-17 23:51:34 +00:00
self . Bind ( wx . EVT_TEXT , self . OnRotate )
2012-03-22 11:33:39 +00:00
self . toolbar2 . AddControl ( self . rotate )
2012-04-17 10:08:19 +00:00
2012-03-21 15:53:05 +00:00
self . toolbar2 . Realize ( )
2012-03-05 21:30:54 +00:00
self . updateToolbar ( )
2012-02-23 13:08:34 +00:00
sizer = wx . BoxSizer ( wx . VERTICAL )
2012-03-05 21:30:54 +00:00
sizer . Add ( self . toolbar , 0 , flag = wx . EXPAND | wx . TOP | wx . LEFT | wx . RIGHT , border = 1 )
2012-02-23 13:08:34 +00:00
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 )
2012-02-23 13:08:34 +00:00
self . SetSizer ( sizer )
2012-04-12 21:13:56 +00:00
2012-03-22 10:24:52 +00:00
def OnMulXAddClick ( self , e ) :
profile . putProfileSetting ( ' model_multiply_x ' , str ( max ( 1 , int ( profile . getProfileSetting ( ' model_multiply_x ' ) ) + 1 ) ) )
2012-04-18 15:48:34 +00:00
self . glCanvas . Refresh ( )
2012-03-22 10:24:52 +00:00
def OnMulXSubClick ( self , e ) :
profile . putProfileSetting ( ' model_multiply_x ' , str ( max ( 1 , int ( profile . getProfileSetting ( ' model_multiply_x ' ) ) - 1 ) ) )
2012-04-18 15:48:34 +00:00
self . glCanvas . Refresh ( )
2012-03-22 10:24:52 +00:00
def OnMulYAddClick ( self , e ) :
profile . putProfileSetting ( ' model_multiply_y ' , str ( max ( 1 , int ( profile . getProfileSetting ( ' model_multiply_y ' ) ) + 1 ) ) )
2012-04-18 15:48:34 +00:00
self . glCanvas . Refresh ( )
2012-03-22 10:24:52 +00:00
def OnMulYSubClick ( self , e ) :
profile . putProfileSetting ( ' model_multiply_y ' , str ( max ( 1 , int ( profile . getProfileSetting ( ' model_multiply_y ' ) ) - 1 ) ) )
2012-04-18 15:48:34 +00:00
self . glCanvas . Refresh ( )
2012-03-22 10:24:52 +00:00
2012-04-17 23:51:34 +00:00
def OnScaleReset ( self , e ) :
2012-04-17 23:56:25 +00:00
self . scale . SetValue ( ' 1.0 ' )
2012-04-18 09:00:41 +00:00
self . OnScale ( None )
2012-04-17 23:51:34 +00:00
2012-03-21 15:53:05 +00:00
def OnScale ( self , e ) :
2012-04-18 14:08:58 +00:00
scale = 1.0
if self . scale . GetValue ( ) != ' ' :
2012-04-18 15:06:32 +00:00
scale = self . scale . GetValue ( )
2012-04-18 14:08:58 +00:00
profile . putProfileSetting ( ' model_scale ' , scale )
2012-04-18 15:04:03 +00:00
self . glCanvas . Refresh ( )
2012-03-22 11:33:39 +00:00
2012-04-17 10:08:19 +00:00
def OnScaleMax ( self , e ) :
if self . triangleMesh == None :
return
2012-04-18 20:51:19 +00:00
vMin = self . triangleMesh . getMinimum ( )
vMax = self . triangleMesh . getMaximum ( )
2012-04-17 10:08:19 +00:00
scaleX1 = ( self . machineSize . x - self . machineCenter . x ) / ( ( vMax . x - vMin . x ) / 2 )
scaleY1 = ( self . machineSize . y - self . machineCenter . y ) / ( ( vMax . y - vMin . y ) / 2 )
scaleX2 = ( self . machineCenter . x ) / ( ( vMax . x - vMin . x ) / 2 )
scaleY2 = ( self . machineCenter . y ) / ( ( vMax . y - vMin . y ) / 2 )
scaleZ = self . machineSize . z / ( vMax . z - vMin . z )
scale = min ( scaleX1 , scaleY1 , scaleX2 , scaleY2 , scaleZ )
self . scale . SetValue ( str ( scale ) )
profile . putProfileSetting ( ' model_scale ' , self . scale . GetValue ( ) )
2012-04-18 15:04:03 +00:00
self . glCanvas . Refresh ( )
2012-04-17 23:51:34 +00:00
def OnRotateReset ( self , e ) :
self . rotate . SetValue ( 0 )
2012-04-18 09:00:41 +00:00
self . OnRotate ( None )
2012-04-17 23:51:34 +00:00
2012-03-22 11:33:39 +00:00
def OnRotate ( self , e ) :
profile . putProfileSetting ( ' model_rotate_base ' , self . rotate . GetValue ( ) )
self . updateModelTransform ( )
2012-03-21 15:53:05 +00:00
2012-04-18 10:39:05 +00:00
def On3DClick ( self ) :
2012-02-28 14:02:24 +00:00
self . glCanvas . yaw = 30
self . glCanvas . pitch = 60
2012-04-22 11:11:21 +00:00
self . glCanvas . zoom = 300
2012-02-28 14:02:24 +00:00
self . glCanvas . view3D = True
self . glCanvas . Refresh ( )
2012-04-18 10:39:05 +00:00
def OnTopClick ( self ) :
2012-02-28 14:02:24 +00:00
self . glCanvas . view3D = False
2012-02-28 16:39:46 +00:00
self . glCanvas . zoom = 100
2012-02-28 14:02:24 +00:00
self . glCanvas . offsetX = 0
self . glCanvas . offsetY = 0
self . glCanvas . Refresh ( )
2012-02-28 16:39:46 +00:00
def OnLayerNrChange ( self , e ) :
2012-03-18 12:00:31 +00:00
self . gcodeDirty = True
2012-02-28 16:39:46 +00:00
self . glCanvas . Refresh ( )
2012-04-23 12:16:23 +00:00
def updateCenterX ( self ) :
self . machineCenter . x = profile . getProfileSettingFloat ( ' machine_center_x ' )
2012-02-24 22:01:22 +00:00
self . glCanvas . Refresh ( )
2012-04-23 12:16:23 +00:00
def updateCenterY ( self ) :
self . machineCenter . y = profile . getProfileSettingFloat ( ' machine_center_y ' )
2012-02-24 22:01:22 +00:00
self . glCanvas . Refresh ( )
2012-02-19 23:30:49 +00:00
2012-03-23 13:04:50 +00:00
def setViewMode ( self , mode ) :
2012-04-18 13:14:43 +00:00
if mode == " Normal " :
self . normalViewButton . SetValue ( True )
if mode == " GCode " :
self . gcodeViewButton . SetValue ( True )
self . glCanvas . viewMode = mode
2012-03-23 13:04:50 +00:00
wx . CallAfter ( self . glCanvas . Refresh )
2012-02-21 22:05:30 +00:00
def loadModelFile ( self , filename ) :
2012-03-19 15:15:16 +00:00
if self . modelFilename != filename :
self . modelFileTime = None
self . gcodeFileTime = None
2012-03-19 16:11:50 +00:00
self . logFileTime = None
2012-03-19 15:15:16 +00:00
2012-02-21 22:05:30 +00:00
self . modelFilename = filename
2012-03-19 16:11:50 +00:00
self . gcodeFilename = filename [ : filename . rfind ( ' . ' ) ] + " _export.gcode "
self . logFilename = filename [ : filename . rfind ( ' . ' ) ] + " _export.log "
2012-02-21 22:05:30 +00:00
#Do the STL file loading in a background thread so we don't block the UI.
2012-03-22 15:04:53 +00:00
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 ( )
2012-03-20 11:22:44 +00:00
def loadReModelFile ( self , filename ) :
#Only load this again if the filename matches the file we have already loaded (for auto loading GCode after slicing)
if self . modelFilename != filename :
2012-03-23 13:04:50 +00:00
return False
2012-03-22 15:04:53 +00:00
self . loadModelFile ( filename )
2012-03-23 13:04:50 +00:00
return True
2012-02-20 22:27:34 +00:00
2012-03-22 15:04:53 +00:00
def doFileLoadThread ( self ) :
2012-03-19 15:15:16 +00:00
if os . path . isfile ( self . modelFilename ) and self . modelFileTime != os . stat ( self . modelFilename ) . st_mtime :
self . modelFileTime = os . stat ( self . modelFilename ) . st_mtime
2012-03-28 14:53:08 +00:00
triangleMesh = stl . stlModel ( )
triangleMesh . load ( self . modelFilename )
2012-03-19 15:15:16 +00:00
triangleMesh . origonalVertexes = list ( triangleMesh . vertexes )
for i in xrange ( 0 , len ( triangleMesh . origonalVertexes ) ) :
triangleMesh . origonalVertexes [ i ] = triangleMesh . origonalVertexes [ i ] . copy ( )
triangleMesh . getMinimumZ ( )
self . modelDirty = False
2012-03-19 16:11:50 +00:00
self . errorList = [ ]
2012-03-19 15:15:16 +00:00
self . triangleMesh = triangleMesh
self . updateModelTransform ( )
wx . CallAfter ( self . updateToolbar )
wx . CallAfter ( self . glCanvas . Refresh )
2012-03-18 12:00:31 +00:00
2012-03-19 15:15:16 +00:00
if os . path . isfile ( self . gcodeFilename ) and self . gcodeFileTime != os . stat ( self . gcodeFilename ) . st_mtime :
self . gcodeFileTime = os . stat ( self . gcodeFilename ) . st_mtime
2012-03-22 15:04:53 +00:00
gcode = gcodeInterpreter . gcode ( )
gcode . progressCallback = self . loadProgress
gcode . load ( self . gcodeFilename )
self . loadingProgressAmount = 0
2012-03-19 15:15:16 +00:00
self . gcodeDirty = False
2012-03-19 16:11:50 +00:00
self . errorList = [ ]
2012-03-19 15:15:16 +00:00
self . gcode = gcode
self . gcodeDirty = True
wx . CallAfter ( self . updateToolbar )
wx . CallAfter ( self . glCanvas . Refresh )
2012-03-19 16:11:50 +00:00
elif not os . path . isfile ( self . gcodeFilename ) :
self . gcode = None
if os . path . isfile ( self . logFilename ) :
errorList = [ ]
for line in open ( self . logFilename , " 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 . glCanvas . Refresh )
2012-02-21 22:05:30 +00:00
2012-03-22 15:04:53 +00:00
def loadProgress ( self , progress ) :
self . loadingProgressAmount = progress
wx . CallAfter ( self . glCanvas . Refresh )
2012-02-28 16:39:46 +00:00
def updateToolbar ( self ) :
self . layerSpin . Show ( self . gcode != None )
if self . gcode != None :
2012-03-29 09:01:33 +00:00
self . layerSpin . SetRange ( 1 , len ( self . gcode . layerList ) )
2012-03-05 21:30:54 +00:00
self . toolbar . Realize ( )
2012-02-28 16:39:46 +00:00
2012-04-18 13:14:43 +00:00
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-02-23 21:47:42 +00:00
self . glCanvas . Refresh ( )
2012-02-20 23:54:04 +00:00
2012-03-07 15:58:04 +00:00
def updateModelTransform ( self , f = 0 ) :
if self . triangleMesh == None :
return
2012-04-18 15:04:03 +00:00
rotate = profile . getProfileSettingFloat ( ' model_rotate_base ' ) / 180.0 * math . pi
scaleX = 1.0
scaleY = 1.0
scaleZ = 1.0
2012-03-17 11:03:38 +00:00
if profile . getProfileSetting ( ' flip_x ' ) == ' True ' :
2012-03-07 15:58:04 +00:00
scaleX = - scaleX
2012-03-17 11:03:38 +00:00
if profile . getProfileSetting ( ' flip_y ' ) == ' True ' :
2012-03-07 15:58:04 +00:00
scaleY = - scaleY
2012-03-17 11:03:38 +00:00
if profile . getProfileSetting ( ' flip_z ' ) == ' True ' :
2012-03-07 15:58:04 +00:00
scaleZ = - scaleZ
2012-04-05 20:35:52 +00:00
swapXZ = profile . getProfileSetting ( ' swap_xz ' ) == ' True '
swapYZ = profile . getProfileSetting ( ' swap_yz ' ) == ' True '
2012-03-07 15:58:04 +00:00
mat00 = math . cos ( rotate ) * scaleX
mat01 = - math . sin ( rotate ) * scaleY
mat10 = math . sin ( rotate ) * scaleX
mat11 = math . cos ( rotate ) * scaleY
for i in xrange ( 0 , len ( self . triangleMesh . origonalVertexes ) ) :
2012-04-05 20:35:52 +00:00
x = self . triangleMesh . origonalVertexes [ i ] . x
y = self . triangleMesh . origonalVertexes [ i ] . y
z = self . triangleMesh . origonalVertexes [ i ] . z
if swapXZ :
x , z = z , x
if swapYZ :
y , z = z , y
self . triangleMesh . vertexes [ i ] . x = x * mat00 + y * mat01
self . triangleMesh . vertexes [ i ] . y = x * mat10 + y * mat11
self . triangleMesh . vertexes [ i ] . z = z * scaleZ
2012-03-14 15:24:18 +00:00
for face in self . triangleMesh . faces :
2012-03-28 14:53:08 +00:00
v1 = face . v [ 0 ]
v2 = face . v [ 1 ]
v3 = face . v [ 2 ]
2012-03-14 15:24:18 +00:00
face . normal = ( v2 - v1 ) . cross ( v3 - v1 )
face . normal . normalize ( )
2012-02-20 17:55:54 +00:00
minZ = self . triangleMesh . getMinimumZ ( )
2012-03-28 14:53:08 +00:00
min = self . triangleMesh . getMinimum ( )
max = self . triangleMesh . getMaximum ( )
2012-02-19 23:30:49 +00:00
for v in self . triangleMesh . vertexes :
v . z - = minZ
v . x - = min . x + ( max . x - min . x ) / 2
v . y - = min . y + ( max . y - min . y ) / 2
2012-02-20 22:27:34 +00:00
self . triangleMesh . getMinimumZ ( )
self . modelDirty = True
2012-03-07 15:58:04 +00:00
self . glCanvas . Refresh ( )
2012-02-23 21:47:42 +00:00
2012-02-29 17:25:57 +00:00
class PreviewGLCanvas ( glcanvas . GLCanvas ) :
2012-02-23 21:47:42 +00:00
def __init__ ( self , parent ) :
2012-03-09 15:39:45 +00:00
attribList = ( glcanvas . WX_GL_RGBA , glcanvas . WX_GL_DOUBLEBUFFER , glcanvas . WX_GL_DEPTH_SIZE , 24 , glcanvas . WX_GL_STENCIL_SIZE , 8 )
2012-02-29 17:25:57 +00:00
glcanvas . GLCanvas . __init__ ( self , parent , attribList = attribList )
2012-02-23 21:47:42 +00:00
self . parent = parent
2012-03-09 15:39:45 +00:00
self . context = glcanvas . GLContext ( self )
2012-02-23 21:47:42 +00:00
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 )
2012-02-28 14:02:24 +00:00
wx . EVT_MOUSEWHEEL ( self , self . OnMouseWheel )
2012-02-23 21:47:42 +00:00
self . yaw = 30
self . pitch = 60
2012-04-17 11:29:16 +00:00
self . zoom = 300
2012-02-28 14:02:24 +00:00
self . offsetX = 0
self . offsetY = 0
self . view3D = True
2012-02-23 21:47:42 +00:00
self . modelDisplayList = None
2012-03-18 12:00:31 +00:00
self . gcodeDisplayList = None
2012-03-09 19:00:22 +00:00
2012-02-19 23:30:49 +00:00
def OnMouseMotion ( self , e ) :
if e . Dragging ( ) and e . LeftIsDown ( ) :
2012-02-28 14:02:24 +00:00
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
2012-02-20 22:27:34 +00:00
self . Refresh ( )
2012-02-19 23:30:49 +00:00
if e . Dragging ( ) and e . RightIsDown ( ) :
self . zoom + = e . GetY ( ) - self . oldY
2012-02-28 14:02:24 +00:00
if self . zoom < 1 :
self . zoom = 1
2012-02-20 22:27:34 +00:00
self . Refresh ( )
2012-02-19 23:30:49 +00:00
self . oldX = e . GetX ( )
self . oldY = e . GetY ( )
2012-02-28 14:02:24 +00:00
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
2012-02-28 14:02:24 +00:00
self . Refresh ( )
2012-02-19 23:30:49 +00:00
def OnEraseBackground ( self , event ) :
2012-03-18 12:00:31 +00:00
#Workaround for windows background redraw flicker.
2012-02-19 23:30:49 +00:00
pass
def OnSize ( self , event ) :
self . Refresh ( )
def OnPaint ( self , event ) :
2012-02-20 15:44:43 +00:00
dc = wx . PaintDC ( self )
if not hasOpenGLlibs :
dc . Clear ( )
dc . DrawText ( " No PyOpenGL installation found. \n No preview window available. " , 10 , 10 )
return
2012-03-09 15:39:45 +00:00
self . SetCurrent ( self . context )
2012-04-05 20:35:52 +00:00
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 . parent . triangleMesh != None :
2012-04-18 20:51:19 +00:00
glTranslate ( 0 , 0 , - self . parent . triangleMesh . getMaximum ( ) . z * profile . getProfileSettingFloat ( ' model_scale ' ) / 2 )
2012-04-05 20:35:52 +00:00
else :
glScale ( 1.0 / self . zoom , 1.0 / self . zoom , 1.0 )
glTranslate ( self . offsetX , self . offsetY , 0.0 )
glTranslate ( - self . parent . machineCenter . x , - self . parent . machineCenter . y , 0 )
2012-02-19 23:30:49 +00:00
self . OnDraw ( )
2012-03-09 15:26:54 +00:00
self . SwapBuffers ( )
2012-02-19 23:30:49 +00:00
def OnDraw ( self ) :
2012-02-23 21:47:42 +00:00
machineSize = self . parent . machineSize
2012-04-05 20:35:52 +00:00
opengl . DrawMachine ( machineSize )
2012-02-21 22:05:30 +00:00
2012-02-28 16:39:46 +00:00
if self . parent . gcode != None :
2012-03-18 12:00:31 +00:00
if self . gcodeDisplayList == None :
self . gcodeDisplayList = glGenLists ( 1 ) ;
if self . parent . gcodeDirty :
self . parent . gcodeDirty = False
glNewList ( self . gcodeDisplayList , GL_COMPILE )
2012-03-18 22:00:33 +00:00
prevLayerZ = 0.0
curLayerZ = 0.0
layerThickness = 0.0
filamentRadius = float ( profile . getProfileSetting ( ' filament_diameter ' ) ) / 2
filamentArea = math . pi * filamentRadius * filamentRadius
2012-04-05 14:50:59 +00:00
lineWidth = float ( profile . getProfileSetting ( ' nozzle_size ' ) ) / 2 / 10
2012-03-18 22:00:33 +00:00
curLayerNum = 0
2012-03-29 09:01:33 +00:00
for layer in self . parent . gcode . layerList :
2012-04-05 14:50:59 +00:00
curLayerZ = layer [ 0 ] . list [ 1 ] . z
layerThickness = curLayerZ - prevLayerZ
prevLayerZ = layer [ - 1 ] . list [ - 1 ] . z
2012-03-29 09:01:33 +00:00
for path in layer :
c = 1.0
if curLayerNum != self . parent . layerSpin . GetValue ( ) :
if curLayerNum < self . parent . layerSpin . GetValue ( ) :
c = 0.9 - ( self . parent . layerSpin . GetValue ( ) - curLayerNum ) * 0.1
if c < 0.4 :
c = 0.4
else :
break
if path . type == ' move ' :
glColor3f ( 0 , 0 , c )
if path . type == ' extrude ' :
if path . pathType == ' FILL ' :
glColor3f ( c / 2 , c / 2 , 0 )
elif path . pathType == ' WALL-INNER ' :
glColor3f ( 0 , c , 0 )
2012-04-03 21:45:42 +00:00
elif path . pathType == ' SUPPORT ' :
glColor3f ( 0 , c , c )
elif path . pathType == ' SKIRT ' :
glColor3f ( 0 , c / 2 , c / 2 )
2012-03-29 09:01:33 +00:00
else :
glColor3f ( c , 0 , 0 )
if path . type == ' retract ' :
glColor3f ( 0 , c , c )
if c > 0.4 and path . type == ' extrude ' :
for i in xrange ( 0 , len ( path . list ) - 1 ) :
v0 = path . list [ i ]
v1 = path . list [ i + 1 ]
2012-03-19 10:45:40 +00:00
2012-03-29 09:01:33 +00:00
# Calculate line width from ePerDistance (needs layer thickness and filament diameter)
dist = ( v0 - v1 ) . vsize ( )
if dist > 0 and layerThickness > 0 :
2012-03-31 19:48:23 +00:00
extrusionMMperDist = ( v1 . e - v0 . e ) / dist
2012-03-29 09:01:33 +00:00
lineWidth = extrusionMMperDist * filamentArea / layerThickness / 2
2012-03-18 22:00:33 +00:00
2012-03-29 09:01:33 +00:00
normal = ( v0 - v1 ) . cross ( util3d . Vector3 ( 0 , 0 , 1 ) )
normal . normalize ( )
v2 = v0 + normal * lineWidth
v3 = v1 + normal * lineWidth
v0 = v0 - normal * lineWidth
v1 = v1 - normal * lineWidth
2012-02-28 16:39:46 +00:00
2012-03-29 09:01:33 +00:00
glBegin ( GL_QUADS )
if path . pathType == ' FILL ' : #Remove depth buffer fighting on infill/wall overlap
glVertex3f ( v0 . x , v0 . y , v0 . z - 0.02 )
glVertex3f ( v1 . x , v1 . y , v1 . z - 0.02 )
glVertex3f ( v3 . x , v3 . y , v3 . z - 0.02 )
glVertex3f ( v2 . x , v2 . y , v2 . z - 0.02 )
else :
glVertex3f ( v0 . x , v0 . y , v0 . z - 0.01 )
glVertex3f ( v1 . x , v1 . y , v1 . z - 0.01 )
glVertex3f ( v3 . x , v3 . y , v3 . z - 0.01 )
glVertex3f ( v2 . x , v2 . y , v2 . z - 0.01 )
glEnd ( )
2012-03-22 15:04:53 +00:00
2012-03-29 09:01:33 +00:00
#for v in path['list']:
# glBegin(GL_TRIANGLE_FAN)
# glVertex3f(v.x, v.y, v.z - 0.001)
# for i in xrange(0, 16+1):
# if path['pathType'] == 'FILL': #Remove depth buffer fighting on infill/wall overlap
# glVertex3f(v.x + math.cos(math.pi*2/16*i) * lineWidth, v.y + math.sin(math.pi*2/16*i) * lineWidth, v.z - 0.02)
# else:
# glVertex3f(v.x + math.cos(math.pi*2/16*i) * lineWidth, v.y + math.sin(math.pi*2/16*i) * lineWidth, v.z - 0.01)
# glEnd()
else :
glBegin ( GL_LINE_STRIP )
for v in path . list :
glVertex3f ( v . x , v . y , v . z )
glEnd ( )
curLayerNum + = 1
2012-02-26 13:30:34 +00:00
glEndList ( )
2012-03-18 12:00:31 +00:00
if self . viewMode == " GCode " or self . viewMode == " Mixed " :
glCallList ( self . gcodeDisplayList )
2012-02-20 23:54:04 +00:00
2012-02-23 21:47:42 +00:00
if self . parent . triangleMesh != None :
2012-02-20 23:54:04 +00:00
if self . modelDisplayList == None :
self . modelDisplayList = glGenLists ( 1 ) ;
2012-02-23 21:47:42 +00:00
if self . parent . modelDirty :
self . parent . modelDirty = False
2012-02-20 23:54:04 +00:00
glNewList ( self . modelDisplayList , GL_COMPILE )
2012-04-18 15:48:34 +00:00
opengl . DrawSTL ( self . parent . triangleMesh )
2012-02-20 23:54:04 +00:00
glEndList ( )
2012-04-05 20:35:52 +00:00
2012-04-18 15:04:03 +00:00
glTranslate ( self . parent . machineCenter . x , self . parent . machineCenter . y , 0 )
glEnable ( GL_NORMALIZE )
2012-04-18 13:14:43 +00:00
if self . viewMode == " Transparent " or self . viewMode == " Mixed " :
2012-04-05 20:35:52 +00:00
glLightfv ( GL_LIGHT0 , GL_DIFFUSE , [ 0.5 , 0.4 , 0.3 , 1.0 ] )
glLightfv ( GL_LIGHT0 , GL_AMBIENT , [ 0.1 , 0.1 , 0.1 , 0.0 ] )
2012-02-20 23:54:04 +00:00
#If we want transparent, then first render a solid black model to remove the printer size lines.
2012-03-18 12:00:31 +00:00
if self . viewMode != " Mixed " :
glDisable ( GL_BLEND )
glDisable ( GL_LIGHTING )
glColor3f ( 0 , 0 , 0 )
2012-04-18 15:48:34 +00:00
self . drawModel ( )
2012-03-18 12:00:31 +00:00
glColor3f ( 1 , 1 , 1 )
2012-02-20 23:54:04 +00:00
#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 )
2012-04-18 15:48:34 +00:00
self . drawModel ( )
2012-04-18 13:14:43 +00:00
elif self . viewMode == " X-Ray " :
2012-02-29 17:25:57 +00:00
glColorMask ( GL_FALSE , GL_FALSE , GL_FALSE , GL_FALSE )
glDisable ( GL_DEPTH_TEST )
glEnable ( GL_STENCIL_TEST ) ;
glStencilFunc ( GL_ALWAYS , 1 , 1 )
glStencilOp ( GL_INCR , GL_INCR , GL_INCR )
2012-04-18 15:48:34 +00:00
self . drawModel ( )
2012-02-29 17:25:57 +00:00
glStencilOp ( GL_KEEP , GL_KEEP , GL_KEEP ) ;
glColorMask ( GL_TRUE , GL_TRUE , GL_TRUE , GL_TRUE )
glStencilFunc ( GL_EQUAL , 0 , 1 ) ;
2012-03-18 12:00:31 +00:00
glColor ( 1 , 1 , 1 )
2012-04-18 15:48:34 +00:00
self . drawModel ( )
2012-02-29 17:25:57 +00:00
glStencilFunc ( GL_EQUAL , 1 , 1 ) ;
glColor ( 1 , 0 , 0 )
2012-04-18 15:48:34 +00:00
self . drawModel ( )
2012-02-29 17:25:57 +00:00
glPushMatrix ( )
glLoadIdentity ( )
2012-03-18 12:00:31 +00:00
for i in xrange ( 2 , 15 , 2 ) :
2012-02-29 17:25:57 +00:00
glStencilFunc ( GL_EQUAL , i , 0xFF ) ;
2012-03-18 12:00:31 +00:00
glColor ( float ( i ) / 10 , float ( i ) / 10 , float ( i ) / 5 )
2012-02-29 17:25:57 +00:00
glBegin ( GL_QUADS )
2012-03-05 21:30:54 +00:00
glVertex3f ( - 1000 , - 1000 , - 1 )
glVertex3f ( 1000 , - 1000 , - 1 )
glVertex3f ( 1000 , 1000 , - 1 )
glVertex3f ( - 1000 , 1000 , - 1 )
2012-02-29 17:25:57 +00:00
glEnd ( )
2012-03-18 12:00:31 +00:00
for i in xrange ( 1 , 15 , 2 ) :
2012-02-29 17:25:57 +00:00
glStencilFunc ( GL_EQUAL , i , 0xFF ) ;
glColor ( float ( i ) / 10 , 0 , 0 )
glBegin ( GL_QUADS )
2012-03-05 21:30:54 +00:00
glVertex3f ( - 1000 , - 1000 , - 1 )
glVertex3f ( 1000 , - 1000 , - 1 )
glVertex3f ( 1000 , 1000 , - 1 )
glVertex3f ( - 1000 , 1000 , - 1 )
2012-02-29 17:25:57 +00:00
glEnd ( )
glPopMatrix ( )
glDisable ( GL_STENCIL_TEST ) ;
glEnable ( GL_DEPTH_TEST )
2012-04-18 13:14:43 +00:00
elif self . viewMode == " Normal " :
2012-04-05 20:35:52 +00:00
glLightfv ( GL_LIGHT0 , GL_DIFFUSE , [ 1.0 , 0.8 , 0.6 , 1.0 ] )
glLightfv ( GL_LIGHT0 , GL_AMBIENT , [ 0.2 , 0.2 , 0.2 , 0.0 ] )
2012-02-20 23:54:04 +00:00
glEnable ( GL_LIGHTING )
2012-04-18 15:48:34 +00:00
self . drawModel ( )
2012-03-21 11:18:57 +00:00
2012-04-18 13:14:43 +00:00
if self . viewMode == " Normal " or self . viewMode == " Transparent " or self . viewMode == " X-Ray " :
2012-03-19 16:11:50 +00:00
glDisable ( GL_LIGHTING )
glDisable ( GL_DEPTH_TEST )
2012-03-21 11:18:57 +00:00
glDisable ( GL_BLEND )
2012-03-19 16:11:50 +00:00
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 ( )
2012-03-09 15:12:59 +00:00
glFlush ( )
2012-04-18 15:48:34 +00:00
def drawModel ( self ) :
multiX = int ( profile . getProfileSetting ( ' model_multiply_x ' ) )
multiY = int ( profile . getProfileSetting ( ' model_multiply_y ' ) )
modelScale = profile . getProfileSettingFloat ( ' model_scale ' )
modelSize = ( self . parent . triangleMesh . getMaximum ( ) - self . parent . triangleMesh . getMinimum ( ) ) * modelScale
glPushMatrix ( )
glTranslate ( - ( modelSize . x + 10 ) * ( multiX - 1 ) / 2 , - ( modelSize . y + 10 ) * ( multiY - 1 ) / 2 , 0 )
for mx in xrange ( 0 , multiX ) :
for my in xrange ( 0 , multiY ) :
glPushMatrix ( )
glTranslate ( ( modelSize . x + 10 ) * mx , ( modelSize . y + 10 ) * my , 0 )
glScalef ( modelScale , modelScale , modelScale )
glCallList ( self . modelDisplayList )
glPopMatrix ( )
glPopMatrix ( )