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
parent
e7a5ba9712
commit
5481b6e636
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -125,3 +125,4 @@ class GcodeSmallSkein:
|
|||
self.output.write(';TYPE:FILL\n');
|
||||
elif line.startswith('(<alteration>'):
|
||||
self.output.write(';TYPE:CUSTOM\n');
|
||||
|
||||
|
|
Loading…
Reference in New Issue