fixes from IchGucksLive for drilling simulation. Tweaks to linuxcnc post
This commit is contained in:
committed by
Yorik van Havre
parent
0c54e0dc3a
commit
5bca294d16
@@ -1,6 +1,4 @@
|
||||
import os
|
||||
_filePath = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
import FreeCAD
|
||||
import Path
|
||||
import Part
|
||||
@@ -10,12 +8,15 @@ import math
|
||||
from FreeCAD import Vector, Base
|
||||
from PathScripts.PathGeom import PathGeom
|
||||
|
||||
_filePath = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
from PySide import QtGui, QtCore
|
||||
|
||||
#compiled with pyrcc4 -py3 Resources\CAM_Sim.qrc -o CAM_Sim_rc.py
|
||||
|
||||
# compiled with pyrcc4 -py3 Resources\CAM_Sim.qrc -o CAM_Sim_rc.py
|
||||
|
||||
|
||||
class CAMSimTaskUi:
|
||||
def __init__(self, parent):
|
||||
# this will create a Qt widget from our ui file
|
||||
@@ -30,20 +31,17 @@ class CAMSimTaskUi:
|
||||
self.parent.cancel()
|
||||
FreeCADGui.Control.closeDialog()
|
||||
|
||||
#for cmd in obj.Path.Commands:
|
||||
# if cmd.Name[0] == 'G':
|
||||
# e1 =
|
||||
# print cmd.Name
|
||||
|
||||
def TSError(msg):
|
||||
QtGui.QMessageBox.information(None,"Path Simulation",msg)
|
||||
def TSError(msg):
|
||||
QtGui.QMessageBox.information(None, "Path Simulation", msg)
|
||||
|
||||
|
||||
class PathSimulation:
|
||||
def __init__(self):
|
||||
self.debug = False
|
||||
self.timer = QtCore.QTimer()
|
||||
QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.PerformCut)
|
||||
self.stdrot = FreeCAD.Rotation(Vector(0,0,1),0)
|
||||
self.stdrot = FreeCAD.Rotation(Vector(0, 0, 1), 0)
|
||||
self.iprogress = 0
|
||||
self.numCommands = 0
|
||||
self.simperiod = 20
|
||||
@@ -71,23 +69,25 @@ class PathSimulation:
|
||||
form.comboJobs.currentIndexChanged.connect(self.onJobChange)
|
||||
jobList = FreeCAD.ActiveDocument.findObjects("Path::FeaturePython", "Job.*")
|
||||
form.comboJobs.clear()
|
||||
self.jobs = []
|
||||
self.jobs = []
|
||||
for j in jobList:
|
||||
self.jobs.append(j)
|
||||
form.comboJobs.addItem(j.ViewObject.Icon, j.Label)
|
||||
FreeCADGui.Control.showDialog(self.taskForm)
|
||||
self.disableAnim = False
|
||||
self.isVoxel = True
|
||||
self.firstDrill = True
|
||||
self.voxSim = PathSimulator.PathSim()
|
||||
self.SimulateMill()
|
||||
|
||||
def SetupSimulation(self):
|
||||
form = self.taskForm.form
|
||||
form = self.taskForm.form
|
||||
self.activeOps = []
|
||||
self.numCommands = 0
|
||||
self.ioperation = 0
|
||||
for i in range(form.listOperations.count()):
|
||||
if form.listOperations.item(i).checkState() == QtCore.Qt.CheckState.Checked:
|
||||
self.firstDrill = True
|
||||
self.activeOps.append(self.operations[i])
|
||||
self.numCommands += len(self.operations[i].Path.Commands)
|
||||
if len(self.activeOps) == 0:
|
||||
@@ -104,48 +104,47 @@ class PathSimulation:
|
||||
self.busy = False
|
||||
self.tool = None
|
||||
for i in range(len(self.activeOps)):
|
||||
self.SetupOperation(0)
|
||||
if (self.tool is not None):
|
||||
break
|
||||
self.SetupOperation(0)
|
||||
if (self.tool is not None):
|
||||
break
|
||||
self.iprogress = 0
|
||||
self.UpdateProgress()
|
||||
|
||||
def SetupOperation(self, itool):
|
||||
self.operation = self.activeOps[itool]
|
||||
if hasattr(self.operation, "ToolController"):
|
||||
self.tool = self.operation.ToolController.Tool
|
||||
self.tool = self.operation.ToolController.Tool
|
||||
if (self.tool is not None):
|
||||
toolProf = self.CreateToolProfile(self.tool, Vector(0,1,0), Vector(0,0,0), self.tool.Diameter / 2.0)
|
||||
self.cutTool.Shape = Part.makeSolid(toolProf.revolve(Vector(0,0,0), Vector(0,0,1)))
|
||||
self.cutTool.ViewObject.show()
|
||||
self.voxSim.SetCurrentTool(self.tool)
|
||||
toolProf = self.CreateToolProfile(self.tool, Vector(0, 1, 0), Vector(0, 0, 0), self.tool.Diameter / 2.0)
|
||||
self.cutTool.Shape = Part.makeSolid(toolProf.revolve(Vector(0, 0, 0), Vector(0, 0, 1)))
|
||||
self.cutTool.ViewObject.show()
|
||||
self.voxSim.SetCurrentTool(self.tool)
|
||||
self.icmd = 0
|
||||
self.curpos = FreeCAD.Placement(self.initialPos, self.stdrot)
|
||||
#self.cutTool.Placement = FreeCAD.Placement(self.curpos, self.stdrot)
|
||||
# self.cutTool.Placement = FreeCAD.Placement(self.curpos, self.stdrot)
|
||||
self.cutTool.Placement = self.curpos
|
||||
|
||||
|
||||
def SimulateMill(self):
|
||||
self.job = self.jobs[self.taskForm.form.comboJobs.currentIndex()]
|
||||
self.busy = False
|
||||
#self.timer.start(100)
|
||||
# self.timer.start(100)
|
||||
self.height = 10
|
||||
self.skipStep = False
|
||||
self.initialPos = Vector(0, 0, self.job.Stock.Shape.BoundBox.ZMax)
|
||||
# Add cut tool
|
||||
self.cutTool = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","CutTool")
|
||||
self.cutTool = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "CutTool")
|
||||
self.cutTool.ViewObject.Proxy = 0
|
||||
self.cutTool.ViewObject.hide()
|
||||
|
||||
# Add cut material
|
||||
if self.isVoxel:
|
||||
self.cutMaterial = FreeCAD.ActiveDocument.addObject("Mesh::FeaturePython","CutMaterial")
|
||||
self.cutMaterialIn = FreeCAD.ActiveDocument.addObject("Mesh::FeaturePython","CutMaterialIn")
|
||||
self.cutMaterial = FreeCAD.ActiveDocument.addObject("Mesh::FeaturePython", "CutMaterial")
|
||||
self.cutMaterialIn = FreeCAD.ActiveDocument.addObject("Mesh::FeaturePython", "CutMaterialIn")
|
||||
self.cutMaterialIn.ViewObject.Proxy = 0
|
||||
self.cutMaterialIn.ViewObject.show()
|
||||
self.cutMaterialIn.ViewObject.ShapeColor = (1.0, 0.85, 0.45, 0.0)
|
||||
else:
|
||||
self.cutMaterial = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","CutMaterial")
|
||||
self.cutMaterial = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "CutMaterial")
|
||||
self.cutMaterial.Shape = self.job.Stock.Shape
|
||||
self.cutMaterial.ViewObject.Proxy = 0
|
||||
self.cutMaterial.ViewObject.show()
|
||||
@@ -153,21 +152,18 @@ class PathSimulation:
|
||||
|
||||
# Add cut path solid for debug
|
||||
if self.debug:
|
||||
self.cutSolid = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","CutDebug")
|
||||
self.cutSolid = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "CutDebug")
|
||||
self.cutSolid.ViewObject.Proxy = 0
|
||||
self.cutSolid.ViewObject.hide()
|
||||
|
||||
|
||||
self.SetupSimulation()
|
||||
self.resetSimulation = True
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
#self.dialog.show()
|
||||
|
||||
def SkipStep(self):
|
||||
self.skipStep = True
|
||||
self.PerformCut()
|
||||
|
||||
|
||||
def PerformCutBoolean(self):
|
||||
if self.resetSimulation:
|
||||
self.resetSimulation = False
|
||||
@@ -178,8 +174,9 @@ class PathSimulation:
|
||||
self.busy = True
|
||||
|
||||
cmd = self.operation.Path.Commands[self.icmd]
|
||||
#for cmd in job.Path.Commands:
|
||||
# for cmd in job.Path.Commands:
|
||||
pathSolid = None
|
||||
|
||||
if cmd.Name in ['G0']:
|
||||
self.curpos = self.RapidMove(cmd, self.curpos)
|
||||
if cmd.Name in ['G1', 'G2', 'G3']:
|
||||
@@ -187,6 +184,17 @@ class PathSimulation:
|
||||
self.curpos = self.RapidMove(cmd, self.curpos)
|
||||
else:
|
||||
(pathSolid, self.curpos) = self.GetPathSolid(self.tool, cmd, self.curpos)
|
||||
if cmd.Name in ['G81', 'G82', 'G83']:
|
||||
if self.firstDrill:
|
||||
extendcommand = Path.Command('G1', {"X": 0.0, "Y": 0.0, "Z": cmd.r})
|
||||
self.curpos = self.RapidMove(extendcommand, self.curpos)
|
||||
self.firstDrill = False
|
||||
extendcommand = Path.Command('G1', {"X": cmd.x, "Y": cmd.y, "Z": cmd.r})
|
||||
self.curpos = self.RapidMove(extendcommand, self.curpos)
|
||||
extendcommand = Path.Command('G1', {"X": cmd.x, "Y": cmd.y, "Z": cmd.z})
|
||||
self.curpos = self.RapidMove(extendcommand, self.curpos)
|
||||
extendcommand = Path.Command('G1', {"X": cmd.x, "Y": cmd.y, "Z": cmd.r})
|
||||
self.curpos = self.RapidMove(extendcommand, self.curpos)
|
||||
self.skipStep = False
|
||||
if pathSolid is not None:
|
||||
if self.debug:
|
||||
@@ -197,14 +205,14 @@ class PathSimulation:
|
||||
self.stock = newStock.removeSplitter()
|
||||
except:
|
||||
if self.debug:
|
||||
print "invalid cut at cmd #" + str(self.icmd)
|
||||
print("invalid cut at cmd #{}".format(self.icmd))
|
||||
if not self.disableAnim:
|
||||
self.cutTool.Placement = FreeCAD.Placement(self.curpos, self.stdrot)
|
||||
self.icmd += 1
|
||||
self.iprogress += 1
|
||||
self.UpdateProgress()
|
||||
if self.icmd >= len(self.operation.Path.Commands):
|
||||
#self.cutMaterial.Shape = self.stock.removeSplitter()
|
||||
# self.cutMaterial.Shape = self.stock.removeSplitter()
|
||||
self.ioperation += 1
|
||||
if self.ioperation >= len(self.activeOps):
|
||||
self.EndSimulation()
|
||||
@@ -214,7 +222,7 @@ class PathSimulation:
|
||||
if not self.disableAnim:
|
||||
self.cutMaterial.Shape = self.stock
|
||||
self.busy = False
|
||||
|
||||
|
||||
def PerformCutVoxel(self):
|
||||
if self.resetSimulation:
|
||||
self.resetSimulation = False
|
||||
@@ -225,17 +233,30 @@ class PathSimulation:
|
||||
self.busy = True
|
||||
|
||||
cmd = self.operation.Path.Commands[self.icmd]
|
||||
#for cmd in job.Path.Commands:
|
||||
# for cmd in job.Path.Commands:
|
||||
if cmd.Name in ['G0', 'G1', 'G2', 'G3']:
|
||||
self.curpos = self.voxSim.ApplyCommand(self.curpos, cmd)
|
||||
if not self.disableAnim:
|
||||
self.cutTool.Placement = self.curpos #FreeCAD.Placement(self.curpos, self.stdrot)
|
||||
self.cutTool.Placement = self.curpos # FreeCAD.Placement(self.curpos, self.stdrot)
|
||||
(self.cutMaterial.Mesh, self.cutMaterialIn.Mesh) = self.voxSim.GetResultMesh()
|
||||
if cmd.Name in ['G81', 'G82', 'G83']:
|
||||
extendcommands = []
|
||||
if self.firstDrill:
|
||||
extendcommands.append(Path.Command('G1', {"X": 0.0, "Y": 0.0, "Z": cmd.r}))
|
||||
self.firstDrill = False
|
||||
extendcommands.append(Path.Command('G1', {"X": cmd.x, "Y": cmd.y, "Z": cmd.r}))
|
||||
extendcommands.append(Path.Command('G1', {"X": cmd.x, "Y": cmd.y, "Z": cmd.z}))
|
||||
extendcommands.append(Path.Command('G1', {"X": cmd.x, "Y": cmd.y, "Z": cmd.r}))
|
||||
for ecmd in extendcommands:
|
||||
self.curpos = self.voxSim.ApplyCommand(self.curpos, ecmd)
|
||||
if not self.disableAnim:
|
||||
self.cutTool.Placement = self.curpos # FreeCAD.Placement(self.curpos, self.stdrot)
|
||||
(self.cutMaterial.Mesh, self.cutMaterialIn.Mesh) = self.voxSim.GetResultMesh()
|
||||
self.icmd += 1
|
||||
self.iprogress += 1
|
||||
self.UpdateProgress()
|
||||
if self.icmd >= len(self.operation.Path.Commands):
|
||||
#self.cutMaterial.Shape = self.stock.removeSplitter()
|
||||
# self.cutMaterial.Shape = self.stock.removeSplitter()
|
||||
self.ioperation += 1
|
||||
if self.ioperation >= len(self.activeOps):
|
||||
self.EndSimulation()
|
||||
@@ -249,16 +270,16 @@ class PathSimulation:
|
||||
self.PerformCutVoxel()
|
||||
else:
|
||||
self.PerformCutBoolean()
|
||||
|
||||
|
||||
def RapidMove(self, cmd, curpos):
|
||||
path = PathGeom.edgeForCmd(cmd, curpos) # hack to overcome occ bug
|
||||
path = PathGeom.edgeForCmd(cmd, curpos) # hack to overcome occ bug
|
||||
if path is None:
|
||||
return curpos
|
||||
return path.valueAt(path.LastParameter)
|
||||
|
||||
def GetPathSolidOld(self, tool, cmd, curpos):
|
||||
e1 = PathGeom.edgeForCmd(cmd, curpos)
|
||||
#curpos = e1.valueAt(e1.LastParameter)
|
||||
# curpos = e1.valueAt(e1.LastParameter)
|
||||
n1 = e1.tangentAt(0)
|
||||
n1[2] = 0.0
|
||||
try:
|
||||
@@ -266,10 +287,10 @@ class PathSimulation:
|
||||
except:
|
||||
return (None, e1.valueAt(e1.LastParameter))
|
||||
height = self.height
|
||||
rad = tool.Diameter / 2.0 - 0.001 * curpos[2] # hack to overcome occ bug
|
||||
if type(e1.Curve) is Part.Circle and e1.Curve.Radius <= rad: # hack to overcome occ bug
|
||||
rad = tool.Diameter / 2.0 - 0.001 * curpos[2] # hack to overcome occ bug
|
||||
if type(e1.Curve) is Part.Circle and e1.Curve.Radius <= rad: # hack to overcome occ bug
|
||||
rad = e1.Curve.Radius - 0.001
|
||||
#return (None, e1.valueAt(e1.LastParameter))
|
||||
# return (None, e1.valueAt(e1.LastParameter))
|
||||
xf = n1[0] * rad
|
||||
yf = n1[1] * rad
|
||||
xp = curpos[0]
|
||||
@@ -279,33 +300,33 @@ class PathSimulation:
|
||||
v2 = Vector(yf + xp, -xf + yp, zp + height)
|
||||
v3 = Vector(-yf + xp, xf + yp, zp + height)
|
||||
v4 = Vector(-yf + xp, xf + yp, zp)
|
||||
vc1 = Vector(xf + xp, yf + yp, zp)
|
||||
vc2 = Vector(xf + xp, yf + yp, zp + height)
|
||||
# vc1 = Vector(xf + xp, yf + yp, zp)
|
||||
# vc2 = Vector(xf + xp, yf + yp, zp + height)
|
||||
l1 = Part.makeLine(v1, v2)
|
||||
l2 = Part.makeLine(v2, v3)
|
||||
#l2 = Part.Edge(Part.Arc(v2, vc2, v3))
|
||||
# l2 = Part.Edge(Part.Arc(v2, vc2, v3))
|
||||
l3 = Part.makeLine(v3, v4)
|
||||
l4 = Part.makeLine(v4, v1)
|
||||
#l4 = Part.Edge(Part.Arc(v4, vc1, v1))
|
||||
# l4 = Part.Edge(Part.Arc(v4, vc1, v1))
|
||||
w1 = Part.Wire([l1, l2, l3, l4])
|
||||
w2 = Part.Wire(e1)
|
||||
try:
|
||||
ex1 = w2.makePipeShell([w1],True, True)
|
||||
ex1 = w2.makePipeShell([w1], True, True)
|
||||
except:
|
||||
#Part.show(w1)
|
||||
#Part.show(w2)
|
||||
# Part.show(w1)
|
||||
# Part.show(w2)
|
||||
return (None, e1.valueAt(e1.LastParameter))
|
||||
cyl1 = Part.makeCylinder(rad, height, curpos)
|
||||
curpos = e1.valueAt(e1.LastParameter)
|
||||
cyl2 = Part.makeCylinder(rad, height, curpos)
|
||||
ex1s = Part.Solid(ex1)
|
||||
f1 = ex1s.fuse([cyl1,cyl2]).removeSplitter()
|
||||
f1 = ex1s.fuse([cyl1, cyl2]).removeSplitter()
|
||||
return (f1, curpos)
|
||||
|
||||
|
||||
# get a solid representation of a tool going along path
|
||||
def GetPathSolid(self, tool, cmd, pos):
|
||||
toolPath = PathGeom.edgeForCmd(cmd, pos)
|
||||
#curpos = e1.valueAt(e1.LastParameter)
|
||||
# curpos = e1.valueAt(e1.LastParameter)
|
||||
startDir = toolPath.tangentAt(0)
|
||||
startDir[2] = 0.0
|
||||
endPos = toolPath.valueAt(toolPath.LastParameter)
|
||||
@@ -315,11 +336,11 @@ class PathSimulation:
|
||||
endDir.normalize()
|
||||
except:
|
||||
return (None, endPos)
|
||||
height = self.height
|
||||
# height = self.height
|
||||
|
||||
# hack to overcome occ bugs
|
||||
rad = tool.Diameter / 2.0 - 0.001 * pos[2]
|
||||
#rad = rad + 0.001 * self.icmd
|
||||
# rad = rad + 0.001 * self.icmd
|
||||
if type(toolPath.Curve) is Part.Circle and toolPath.Curve.Radius <= rad:
|
||||
rad = toolPath.Curve.Radius - 0.01 * (pos[2] + 1)
|
||||
return (None, endPos)
|
||||
@@ -334,7 +355,7 @@ class PathSimulation:
|
||||
fullProf = Part.Wire([toolProf, mirroredProf])
|
||||
pathWire = Part.Wire(toolPath)
|
||||
try:
|
||||
pathShell = pathWire.makePipeShell([fullProf],False, True)
|
||||
pathShell = pathWire.makePipeShell([fullProf], False, True)
|
||||
except:
|
||||
if self.debug:
|
||||
Part.show(pathWire)
|
||||
@@ -342,11 +363,11 @@ class PathSimulation:
|
||||
return (None, endPos)
|
||||
|
||||
# create the start cup
|
||||
startCup = toolProf.revolve(pos, Vector(0,0,1), -180)
|
||||
startCup = toolProf.revolve(pos, Vector(0, 0, 1), -180)
|
||||
|
||||
#create the end cup
|
||||
# create the end cup
|
||||
endProf = self.CreateToolProfile(tool, endDir, endPos, rad)
|
||||
endCup = endProf.revolve(endPos, Vector(0,0,1), 180)
|
||||
endCup = endProf.revolve(endPos, Vector(0, 0, 1), 180)
|
||||
|
||||
fullShell = Part.makeShell(startCup.Faces + pathShell.Faces + endCup.Faces)
|
||||
return (Part.makeSolid(fullShell).removeSplitter(), endPos)
|
||||
@@ -354,7 +375,7 @@ class PathSimulation:
|
||||
# create radial profile of the tool (90 degrees to the direction of the path)
|
||||
def CreateToolProfile(self, tool, dir, pos, rad):
|
||||
type = tool.ToolType
|
||||
#rad = tool.Diameter / 2.0 - 0.001 * pos[2] # hack to overcome occ bug
|
||||
# rad = tool.Diameter / 2.0 - 0.001 * pos[2] # hack to overcome occ bug
|
||||
xf = dir[0] * rad
|
||||
yf = dir[1] * rad
|
||||
xp = pos[0]
|
||||
@@ -393,13 +414,13 @@ class PathSimulation:
|
||||
lR = Part.makeLine(vBR, vTR)
|
||||
res = Part.Wire([cB, lR, lT])
|
||||
|
||||
else: # default: assume type == "EndMill"
|
||||
else: # default: assume type == "EndMill"
|
||||
vBR = Vector(xp + yf, yp - xf, zp)
|
||||
lR = Part.makeLine(vBR, vTR)
|
||||
lB = Part.makeLine(vBC, vBR)
|
||||
res = Part.Wire([lB, lR, lT])
|
||||
|
||||
return res
|
||||
return res
|
||||
|
||||
def onJobChange(self):
|
||||
form = self.taskForm.form
|
||||
@@ -407,19 +428,18 @@ class PathSimulation:
|
||||
form.listOperations.clear()
|
||||
self.operations = []
|
||||
for op in j.Operations.OutList:
|
||||
listItem = QtGui.QListWidgetItem(op.ViewObject.Icon, op.Label)
|
||||
listItem = QtGui.QListWidgetItem(op.ViewObject.Icon, op.Label)
|
||||
listItem.setFlags(listItem.flags() | QtCore.Qt.ItemIsUserCheckable)
|
||||
listItem.setCheckState(QtCore.Qt.CheckState.Checked)
|
||||
self.operations.append(op)
|
||||
form.listOperations.addItem(listItem)
|
||||
|
||||
|
||||
def onSpeedBarChange(self):
|
||||
form = self.taskForm.form
|
||||
self.simperiod = 1000 / form.sliderSpeed.value()
|
||||
form.labelGPerSec.setText(str(form.sliderSpeed.value()) + " G/s")
|
||||
#if (self.timer.isActive()):
|
||||
# if (self.timer.isActive()):
|
||||
self.timer.setInterval(self.simperiod)
|
||||
|
||||
|
||||
def onAccuracyBarChange(self):
|
||||
form = self.taskForm.form
|
||||
@@ -428,7 +448,7 @@ class PathSimulation:
|
||||
|
||||
def GuiBusy(self, isBusy):
|
||||
form = self.taskForm.form
|
||||
#form.toolButtonStop.setEnabled()
|
||||
# form.toolButtonStop.setEnabled()
|
||||
form.toolButtonPlay.setEnabled(not isBusy)
|
||||
form.toolButtonPause.setEnabled(isBusy)
|
||||
form.toolButtonStep.setEnabled(not isBusy)
|
||||
@@ -493,18 +513,17 @@ class PathSimulation:
|
||||
FreeCAD.ActiveDocument.removeObject(self.cutMaterial.Name)
|
||||
self.cutMaterial = None
|
||||
self.RemoveInnerMaterial()
|
||||
|
||||
|
||||
def accept(self):
|
||||
self.EndSimulation()
|
||||
self.RemoveInnerMaterial()
|
||||
self.RemoveTool()
|
||||
|
||||
|
||||
def cancel(self):
|
||||
self.EndSimulation()
|
||||
self.RemoveTool()
|
||||
self.RemoveMaterial()
|
||||
|
||||
|
||||
|
||||
class CommandPathSimulate:
|
||||
|
||||
@@ -527,7 +546,5 @@ class CommandPathSimulate:
|
||||
pathSimulation = PathSimulation()
|
||||
if FreeCAD.GuiUp:
|
||||
# register the FreeCAD command
|
||||
FreeCADGui.addCommand('Path_Simulator',CommandPathSimulate())
|
||||
FreeCADGui.addCommand('Path_Simulator', CommandPathSimulate())
|
||||
FreeCAD.Console.PrintLog("Loading PathSimulator Gui... done\n")
|
||||
|
||||
|
||||
|
||||
@@ -21,8 +21,16 @@
|
||||
# * *
|
||||
# ***************************************************************************/
|
||||
from __future__ import print_function
|
||||
import FreeCAD
|
||||
from FreeCAD import Units
|
||||
import Path
|
||||
import argparse
|
||||
import datetime
|
||||
import shlex
|
||||
from PathScripts import PostUtils
|
||||
from PathScripts import PathUtils
|
||||
|
||||
TOOLTIP='''
|
||||
TOOLTIP = '''
|
||||
This is a postprocessor file for the Path workbench. It is used to
|
||||
take a pseudo-gcode fragment outputted by a Path object, and output
|
||||
real GCode suitable for a linuxcnc 3 axis mill. This postprocessor, once placed
|
||||
@@ -33,14 +41,6 @@ import linuxcnc_post
|
||||
linuxcnc_post.export(object,"/path/to/file.ncc","")
|
||||
'''
|
||||
|
||||
import FreeCAD
|
||||
from FreeCAD import Units
|
||||
import argparse
|
||||
import datetime
|
||||
import shlex
|
||||
from PathScripts import PostUtils
|
||||
from PathScripts import PathUtils
|
||||
|
||||
now = datetime.datetime.now()
|
||||
|
||||
parser = argparse.ArgumentParser(prog='linuxcnc', add_help=False)
|
||||
@@ -52,12 +52,14 @@ parser.add_argument('--line-numbers', action='store_true', help='prefix with lin
|
||||
parser.add_argument('--no-line-numbers', action='store_true', help='don\'t prefix with line numbers (default)')
|
||||
parser.add_argument('--show-editor', action='store_true', help='pop up editor before writing output (default)')
|
||||
parser.add_argument('--no-show-editor', action='store_true', help='don\'t pop up editor before writing output')
|
||||
parser.add_argument('--precision', default='4', help='number of digits of precision, default=4')
|
||||
parser.add_argument('--precision', default='3', help='number of digits of precision, default=4')
|
||||
parser.add_argument('--preamble', help='set commands to be issued before the first command, default="G17\nG90"')
|
||||
parser.add_argument('--postamble', help='set commands to be issued after the last command, default="M05\nG17 G90\nM2"')
|
||||
parser.add_argument('--inches', action='store_true', help='Convert output for US imperial mode (G20)')
|
||||
parser.add_argument('--modal', action='store_true', help='Dont output the Same Gcommand Name USE Modal Mode')
|
||||
parser.add_argument('--no-doubles', action='store_true', help='Dont output the Same Axis Value Mode')
|
||||
|
||||
TOOLTIP_ARGS=parser.format_help()
|
||||
TOOLTIP_ARGS = parser.format_help()
|
||||
|
||||
# These globals set common customization preferences
|
||||
OUTPUT_COMMENTS = True
|
||||
@@ -65,6 +67,7 @@ OUTPUT_HEADER = True
|
||||
OUTPUT_LINE_NUMBERS = False
|
||||
SHOW_EDITOR = True
|
||||
MODAL = False # if true commands are suppressed if the same as previous line.
|
||||
OUTPUT_DOUBLES = True
|
||||
COMMAND_SPACE = " "
|
||||
LINENR = 100 # line number starting value
|
||||
|
||||
@@ -76,7 +79,7 @@ UNIT_FORMAT = 'mm'
|
||||
MACHINE_NAME = "LinuxCNC"
|
||||
CORNER_MIN = {'x': 0, 'y': 0, 'z': 0}
|
||||
CORNER_MAX = {'x': 500, 'y': 300, 'z': 300}
|
||||
PRECISION=4
|
||||
PRECISION = 3
|
||||
|
||||
# Preamble text will appear at the beginning of the GCODE output file.
|
||||
PREAMBLE = '''G17 G90
|
||||
@@ -103,6 +106,7 @@ TOOL_CHANGE = ''''''
|
||||
if open.__module__ == '__builtin__':
|
||||
pythonopen = open
|
||||
|
||||
|
||||
def processArguments(argstring):
|
||||
global OUTPUT_HEADER
|
||||
global OUTPUT_COMMENTS
|
||||
@@ -114,6 +118,8 @@ def processArguments(argstring):
|
||||
global UNITS
|
||||
global UNIT_SPEED_FORMAT
|
||||
global UNIT_FORMAT
|
||||
global MODAL
|
||||
global OUTPUT_DOUBLES
|
||||
|
||||
try:
|
||||
args = parser.parse_args(shlex.split(argstring))
|
||||
@@ -143,12 +149,18 @@ def processArguments(argstring):
|
||||
UNITS = 'G20'
|
||||
UNIT_SPEED_FORMAT = 'in/min'
|
||||
UNIT_FORMAT = 'in'
|
||||
PRECISION = 4
|
||||
if args.modal:
|
||||
MODAL = True
|
||||
if args.no_doubles:
|
||||
OUTPUT_DOUBLES = False
|
||||
|
||||
except:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def export(objectslist, filename, argstring):
|
||||
if not processArguments(argstring):
|
||||
return None
|
||||
@@ -183,17 +195,16 @@ def export(objectslist, filename, argstring):
|
||||
|
||||
myMachine = 'not set'
|
||||
|
||||
if hasattr(job,"MachineName"):
|
||||
if hasattr(job, "MachineName"):
|
||||
myMachine = job.MachineName
|
||||
|
||||
if hasattr(job, "MachineUnits"):
|
||||
if job.MachineUnits == "Metric":
|
||||
UNITS = "G21"
|
||||
UNIT_SPEED_FORMAT = 'mm/min'
|
||||
|
||||
UNITS = "G21"
|
||||
UNIT_SPEED_FORMAT = 'mm/min'
|
||||
else:
|
||||
UNITS = "G20"
|
||||
UNIT_SPEED_FORMAT = 'in/min'
|
||||
UNITS = "G20"
|
||||
UNIT_SPEED_FORMAT = 'in/min'
|
||||
|
||||
# do the pre_op
|
||||
if OUTPUT_COMMENTS:
|
||||
@@ -245,16 +256,24 @@ def linenumber():
|
||||
return "N" + str(LINENR) + " "
|
||||
return ""
|
||||
|
||||
|
||||
def parse(pathobj):
|
||||
global PRECISION
|
||||
global MODAL
|
||||
global OUTPUT_DOUBLES
|
||||
|
||||
out = ""
|
||||
lastcommand = None
|
||||
precision_string = '.' + str(PRECISION) +'f'
|
||||
precision_string = '.' + str(PRECISION) + 'f'
|
||||
|
||||
# params = ['X','Y','Z','A','B','I','J','K','F','S'] #This list control
|
||||
# the order of parameters
|
||||
# linuxcnc doesn't want K properties on XY plane Arcs need work.
|
||||
params = ['X', 'Y', 'Z', 'A', 'B', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L', 'H']
|
||||
params = ['X', 'Y', 'Z', 'A', 'B', 'C', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L', 'H', 'D', 'P']
|
||||
# keep track for no doubles
|
||||
currLocation = {}
|
||||
firstmove = Path.Command("G0", {"X": -1, "Y": -1, "Z": -1, "F": -1})
|
||||
currLocation.update(firstmove.Parameters)
|
||||
|
||||
if hasattr(pathobj, "Group"): # We have a compound or project.
|
||||
# if OUTPUT_COMMENTS:
|
||||
@@ -284,22 +303,31 @@ def parse(pathobj):
|
||||
# Now add the remaining parameters in order
|
||||
for param in params:
|
||||
if param in c.Parameters:
|
||||
if param == 'F':
|
||||
if c.Name not in ["G0", "G00"]: #linuxcnc doesn't use rapid speeds
|
||||
if param == 'F' and (currLocation[param] == c.Parameters[param] and OUTPUT_DOUBLES):
|
||||
if c.Name not in ["G0", "G00"]: # linuxcnc doesn't use rapid speeds
|
||||
speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity)
|
||||
outstring.append(
|
||||
param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string) )
|
||||
param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string))
|
||||
elif param == 'T':
|
||||
outstring.append(param + str(int(c.Parameters['T'])))
|
||||
elif param == 'H':
|
||||
outstring.append(param + str(int(c.Parameters['H'])))
|
||||
elif param == 'D':
|
||||
outstring.append(param + str(int(c.Parameters['D'])))
|
||||
elif param == 'S':
|
||||
outstring.append(param + str(int(c.Parameters['S'])))
|
||||
else:
|
||||
pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length)
|
||||
outstring.append(
|
||||
param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string) )
|
||||
#param + format(c.Parameters[param], precision_string))
|
||||
|
||||
if (not OUTPUT_DOUBLES) and (param in currLocation) and (currLocation[param] == c.Parameters[param]):
|
||||
continue
|
||||
else:
|
||||
pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length)
|
||||
outstring.append(
|
||||
param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string))
|
||||
# param + format(c.Parameters[param], precision_string))
|
||||
|
||||
# store the latest command
|
||||
lastcommand = command
|
||||
currLocation.update(c.Parameters)
|
||||
|
||||
# Check for Tool Change:
|
||||
if command == 'M6':
|
||||
|
||||
Reference in New Issue
Block a user