Updated the GCode preview, now uses thick lines for extrusion. Allows you to spot slicing errors better.

This update also adds a layer selection to the GCode preview.
master
daid 2012-02-28 17:39:46 +01:00
parent e7a5ba9712
commit 5481b6e636
5 changed files with 100 additions and 22 deletions

View File

@ -123,6 +123,7 @@ class SettingRow():
sizer.SetRows(x+1)
def OnSettingTextChange(self, e):
settings.putSetting(self.configName, self.GetValue())
result = validators.SUCCESS
msgs = []
for validator in self.validators:
@ -140,7 +141,6 @@ class SettingRow():
else:
self.ctrl.SetBackgroundColour(wx.NullColour)
self.ctrl.Refresh()
settings.putSetting(self.configName, self.GetValue())
self.validationMsg = '\n'.join(msgs)
self.panel.main.UpdatePopup(self)

View File

@ -12,13 +12,18 @@ class gcode():
posOffset = Vector3()
currentE = 0
pathList = []
currentPath = {'type': 'move', 'list': [pos.copy()]}
scale = 1.0
posAbs = True
feedRate = 3600
pathType = 'CUSTOM';
layerNr = 0; #Note layer 0 will be the start code.
startCodeDone = False
currentPath = {'type': 'move', 'pathType': pathType, 'list': [pos.copy()], 'layerNr': layerNr}
for line in f:
if line.startswith(';TYPE:'):
pathType = line[6:].strip()
if pathType != "CUSTOM":
startCodeDone = True
G = self.getCodeInt(line, 'G')
if G is not None:
if G == 0 or G == 1: #Move
@ -26,6 +31,7 @@ class gcode():
y = self.getCodeFloat(line, 'Y')
z = self.getCodeFloat(line, 'Z')
e = self.getCodeFloat(line, 'E')
f = self.getCodeFloat(line, 'F')
if x is not None:
if posAbs:
pos.x = x * scale
@ -37,10 +43,15 @@ class gcode():
else:
pos.y += y * scale
if z is not None:
oldZ = pos.z
if posAbs:
pos.z = z * scale
else:
pos.z += z * scale
if oldZ != pos.z and startCodeDone:
layerNr += 1
if f is not None:
feedRate = f
newPoint = pos.copy()
moveType = 'move'
if e is not None:
@ -49,9 +60,9 @@ class gcode():
if e < currentE:
moveType = 'retract'
currentE = e
if currentPath['type'] != moveType:
if currentPath['type'] != moveType or currentPath['pathType'] != pathType:
pathList.append(currentPath)
currentPath = {'type': moveType, 'pathType': pathType, 'list': [currentPath['list'][-1]]}
currentPath = {'type': moveType, 'pathType': pathType, 'list': [currentPath['list'][-1]], 'layerNr': layerNr}
currentPath['list'].append(newPoint)
elif G == 20: #Units are inches
scale = 25.4
@ -112,6 +123,7 @@ class gcode():
pass
else:
print "Unknown M code:" + str(M)
self.layerCount = layerNr
self.pathList = pathList
def getCodeInt(self, str, id):

View File

@ -60,6 +60,7 @@ class mainWindow(configBase.configWindowBase):
c = configBase.SettingRow(left, "Wall thickness (mm)", 'wall_thickness', '0.8', 'Thickness of the walls.\nThis is used in combination with the nozzle size to define the number\nof perimeter lines and the thickness of those perimeter lines.')
validators.validFloat(c, 0.0)
validators.wallThicknessValidator(c)
configBase.settingNotify(c, self.preview3d.updateWallLineWidth)
configBase.TitleRow(left, "Fill")
c = configBase.SettingRow(left, "Bottom/Top thickness (mm)", 'solid_layer_thickness', '0.6', 'This controls the thickness of the bottom and top layers, the amount of solid layers put down is calculated by the layer thickness and this value.\nHaving this value a multiply of the layer thickness makes sense. And keep it near your wall thickness to make an evenly strong part.')
@ -109,6 +110,8 @@ class mainWindow(configBase.configWindowBase):
configBase.TitleRow(left, "Machine nozzle")
c = configBase.SettingRow(left, "Nozzle size (mm)", 'nozzle_size', '0.4', 'The nozzle size is very important, this is used to calculate the line width of the infill, and used to calculate the amount of outside wall lines and thickness for the wall thickness you entered in the print settings.')
validators.validFloat(c, 0.1, 1.0)
configBase.settingNotify(c, self.preview3d.updateWallLineWidth)
configBase.settingNotify(c, self.preview3d.updateInfillLineWidth)
configBase.TitleRow(left, "Retraction")
c = configBase.SettingRow(left, "Minimal travel (mm)", 'retraction_min_travel', '5.0', 'Minimal amount of travel needed for a retraction to happen at all. To make sure you do not get a lot of retractions in a small area')

View File

@ -16,6 +16,7 @@ except:
from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import settings
from newui import gcodeInterpreter
class previewPanel(wx.Panel):
@ -28,16 +29,13 @@ class previewPanel(wx.Panel):
self.glCanvas = PreviewGLCanvas(self)
self.init = 0
self.triangleMesh = None
self.pathList = None
self.gcode = None
self.machineSize = Vector3(210, 210, 200)
self.machineCenter = Vector3(0, 0, 0)
tb = wx.ToolBar( self, -1 )
self.ToolBar = tb
tb.SetToolBitmapSize( ( 21, 21 ) )
transparentButton = wx.Button(tb, -1, "T", size=(21,21))
tb.AddControl(transparentButton)
self.Bind(wx.EVT_BUTTON, self.OnConfigClick, transparentButton)
button = wx.Button(tb, -1, "3D", size=(21*2,21))
tb.AddControl(button)
@ -46,9 +44,15 @@ class previewPanel(wx.Panel):
button = wx.Button(tb, -1, "Top", size=(21*2,21))
tb.AddControl(button)
self.Bind(wx.EVT_BUTTON, self.OnTopClick, button)
self.transparentButton = wx.Button(tb, -1, "T", size=(21,21))
tb.AddControl(self.transparentButton)
self.Bind(wx.EVT_BUTTON, self.OnConfigClick, self.transparentButton)
self.layerSpin = wx.SpinCtrl(tb, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS)
tb.AddControl(self.layerSpin)
self.Bind(wx.EVT_SPINCTRL, self.OnLayerNrChange, self.layerSpin)
self.transparentButton.Show(False)
self.layerSpin.Show(False)
tb.Realize()
@ -67,11 +71,15 @@ class previewPanel(wx.Panel):
def OnTopClick(self, e):
self.glCanvas.view3D = False
self.glCanvas.zoom = 150
self.glCanvas.zoom = 100
self.glCanvas.offsetX = 0
self.glCanvas.offsetY = 0
self.glCanvas.Refresh()
def OnLayerNrChange(self, e):
self.modelDirty = True
self.glCanvas.Refresh()
def updateCenterX(self, x):
self.machineCenter.x = x
self.moveModel()
@ -82,6 +90,12 @@ class previewPanel(wx.Panel):
self.moveModel()
self.glCanvas.Refresh()
def updateWallLineWidth(self, setting):
self.glCanvas.lineWidth = settings.calculateEdgeWidth(setting)
def updateInfillLineWidth(self, setting):
self.glCanvas.infillLineWidth = settings.getSetting('nozzle_size')
def loadModelFile(self, filename):
self.modelFilename = filename
#Do the STL file loading in a background thread so we don't block the UI.
@ -97,18 +111,26 @@ class previewPanel(wx.Panel):
def DoModelLoad(self):
self.modelDirty = False
self.triangleMesh = fabmetheus_interpret.getCarving(self.modelFilename)
self.pathList = None
self.gcode = None
self.moveModel()
wx.CallAfter(self.updateToolbar)
wx.CallAfter(self.glCanvas.Refresh)
def DoGCodeLoad(self):
gcode = gcodeInterpreter.gcode(self.gcodeFilename)
self.modelDirty = False
self.pathList = gcode.pathList
self.gcode = gcode
self.triangleMesh = None
self.modelDirty = True
wx.CallAfter(self.updateToolbar)
wx.CallAfter(self.glCanvas.Refresh)
def updateToolbar(self):
self.transparentButton.Show(self.triangleMesh != None)
self.layerSpin.Show(self.gcode != None)
if self.gcode != None:
self.layerSpin.SetRange(1, self.gcode.layerCount)
def OnConfigClick(self, e):
self.glCanvas.renderTransparent = not self.glCanvas.renderTransparent
self.glCanvas.Refresh()
@ -142,6 +164,8 @@ class PreviewGLCanvas(GLCanvas):
self.zoom = 150
self.offsetX = 0
self.offsetY = 0
self.lineWidth = 0.4
self.fillLineWidth = 0.4
self.view3D = True
self.renderTransparent = False
self.modelDisplayList = None
@ -233,28 +257,66 @@ class PreviewGLCanvas(GLCanvas):
glVertex3f(0, machineSize.y, machineSize.z)
glEnd()
if self.parent.pathList != None:
if self.parent.gcode != None:
if self.modelDisplayList == None:
self.modelDisplayList = glGenLists(1);
if self.parent.modelDirty:
self.parent.modelDirty = False
glNewList(self.modelDisplayList, GL_COMPILE)
for path in self.parent.pathList:
for path in self.parent.gcode.pathList:
c = 1.0
if path['layerNr'] != self.parent.layerSpin.GetValue():
if path['layerNr'] < self.parent.layerSpin.GetValue():
c = 0.5 - (self.parent.layerSpin.GetValue() - path['layerNr']) * 0.1
if c < -0.5:
continue
if c < 0.1:
c = 0.1
else:
break
if path['type'] == 'move':
glColor3f(0,0,1)
glColor3f(0,0,c)
if path['type'] == 'extrude':
if path['pathType'] == 'FILL':
glColor3f(0.5,0.5,0)
glColor3f(c/2,c/2,0)
elif path['pathType'] == 'WALL-INNER':
glColor3f(0,1,0)
glColor3f(0,c,0)
else:
glColor3f(1,0,0)
glColor3f(c,0,0)
if path['type'] == 'retract':
glColor3f(0,1,1)
glBegin(GL_LINE_STRIP)
for v in path['list']:
glVertex3f(v.x, v.y, v.z)
glEnd()
glColor3f(0,c,c)
if path['type'] == 'extrude':
if path['pathType'] == 'FILL':
lineWidth = self.fillLineWidth / 2
else:
lineWidth = self.lineWidth / 2
for i in xrange(0, len(path['list'])-1):
v0 = path['list'][i]
v1 = path['list'][i+1]
normal = (v0 - v1).cross(Vector3(0,0,1))
normal.normalize()
v2 = v0 + normal * lineWidth
v3 = v1 + normal * lineWidth
v0 = v0 - normal * lineWidth
v1 = v1 - normal * lineWidth
glBegin(GL_QUADS)
glVertex3f(v0.x, v0.y, v0.z - 0.001)
glVertex3f(v1.x, v1.y, v1.z - 0.001)
glVertex3f(v3.x, v3.y, v3.z - 0.001)
glVertex3f(v2.x, v2.y, v2.z - 0.001)
glEnd()
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):
glVertex3f(v.x + math.cos(math.pi*2/16*i) * lineWidth, v.y + math.sin(math.pi*2/16*i) * lineWidth, v.z - 0.001)
glEnd()
else:
glBegin(GL_LINE_STRIP)
for v in path['list']:
glVertex3f(v.x, v.y, v.z)
glEnd()
glEndList()
glCallList(self.modelDisplayList)

View File

@ -125,3 +125,4 @@ class GcodeSmallSkein:
self.output.write(';TYPE:FILL\n');
elif line.startswith('(<alteration>'):
self.output.write(';TYPE:CUSTOM\n');