582 lines
21 KiB
Python
582 lines
21 KiB
Python
import FreeCAD
|
|
import Mesh
|
|
import Part
|
|
import Path
|
|
import PathScripts.PathDressup as PathDressup
|
|
import PathScripts.PathGeom as PathGeom
|
|
import PathScripts.PathLog as PathLog
|
|
import PathSimulator
|
|
import math
|
|
import os
|
|
|
|
from FreeCAD import Vector, Base
|
|
|
|
_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
|
|
|
|
|
|
class CAMSimTaskUi:
|
|
def __init__(self, parent):
|
|
# this will create a Qt widget from our ui file
|
|
self.form = FreeCADGui.PySideUic.loadUi(":/panels/TaskPathSimulator.ui")
|
|
self.parent = parent
|
|
|
|
def accept(self):
|
|
self.parent.accept()
|
|
FreeCADGui.Control.closeDialog()
|
|
|
|
def reject(self):
|
|
self.parent.cancel()
|
|
FreeCADGui.Control.closeDialog()
|
|
|
|
|
|
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.iprogress = 0
|
|
self.numCommands = 0
|
|
self.simperiod = 20
|
|
self.accuracy = 0.1
|
|
self.resetSimulation = False
|
|
|
|
def Connect(self, but, sig):
|
|
QtCore.QObject.connect(but, QtCore.SIGNAL("clicked()"), sig)
|
|
|
|
def UpdateProgress(self):
|
|
if self.numCommands > 0:
|
|
self.taskForm.form.progressBar.setValue(self.iprogress * 100 / self.numCommands)
|
|
|
|
def Activate(self):
|
|
self.initdone = False
|
|
self.taskForm = CAMSimTaskUi(self)
|
|
form = self.taskForm.form
|
|
self.Connect(form.toolButtonStop, self.SimStop)
|
|
self.Connect(form.toolButtonPlay, self.SimPlay)
|
|
self.Connect(form.toolButtonPause, self.SimPause)
|
|
self.Connect(form.toolButtonStep, self.SimStep)
|
|
self.Connect(form.toolButtonFF, self.SimFF)
|
|
form.sliderSpeed.valueChanged.connect(self.onSpeedBarChange)
|
|
self.onSpeedBarChange()
|
|
form.sliderAccuracy.valueChanged.connect(self.onAccuracyBarChange)
|
|
self.onAccuracyBarChange()
|
|
form.comboJobs.currentIndexChanged.connect(self.onJobChange)
|
|
jobList = FreeCAD.ActiveDocument.findObjects("Path::FeaturePython", "Job.*")
|
|
form.comboJobs.clear()
|
|
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()
|
|
self.initdone = True
|
|
|
|
def SetupSimulation(self):
|
|
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)
|
|
|
|
self.stock = self.job.Stock.Shape
|
|
if (self.isVoxel):
|
|
maxlen = self.stock.BoundBox.XLength
|
|
if (maxlen < self.stock.BoundBox.YLength):
|
|
maxlen = self.stock.BoundBox.YLength
|
|
self.voxSim.BeginSimulation(self.stock, 0.01 * self.accuracy * maxlen)
|
|
(self.cutMaterial.Mesh, self.cutMaterialIn.Mesh) = self.voxSim.GetResultMesh()
|
|
else:
|
|
self.cutMaterial.Shape = self.stock
|
|
self.busy = False
|
|
self.tool = None
|
|
for i in range(len(self.activeOps)):
|
|
self.SetupOperation(0)
|
|
if (self.tool is not None):
|
|
break
|
|
self.iprogress = 0
|
|
self.UpdateProgress()
|
|
|
|
def SetupOperation(self, itool):
|
|
self.operation = self.activeOps[itool]
|
|
try:
|
|
self.tool = PathDressup.toolController(self.operation).Tool
|
|
except Exception:
|
|
self.tool = None
|
|
|
|
# if hasattr(self.operation, "ToolController"):
|
|
# 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)
|
|
self.icmd = 0
|
|
self.curpos = FreeCAD.Placement(self.initialPos, self.stdrot)
|
|
# self.cutTool.Placement = FreeCAD.Placement(self.curpos, self.stdrot)
|
|
self.cutTool.Placement = self.curpos
|
|
self.opCommands = self.operation.Path.Commands
|
|
|
|
def SimulateMill(self):
|
|
self.job = self.jobs[self.taskForm.form.comboJobs.currentIndex()]
|
|
self.busy = False
|
|
# 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.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.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.Shape = self.job.Stock.Shape
|
|
self.cutMaterial.ViewObject.Proxy = 0
|
|
self.cutMaterial.ViewObject.show()
|
|
self.cutMaterial.ViewObject.ShapeColor = (0.5, 0.25, 0.25, 0.0)
|
|
|
|
# Add cut path solid for debug
|
|
if self.debug:
|
|
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()
|
|
|
|
# def SkipStep(self):
|
|
# self.skipStep = True
|
|
# self.PerformCut()
|
|
|
|
def PerformCutBoolean(self):
|
|
if self.resetSimulation:
|
|
self.resetSimulation = False
|
|
self.SetupSimulation()
|
|
|
|
if self.busy:
|
|
return
|
|
self.busy = True
|
|
|
|
cmd = self.operation.Path.Commands[self.icmd]
|
|
# 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']:
|
|
if self.skipStep:
|
|
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('G0', {"X": 0.0, "Y": 0.0, "Z": cmd.r})
|
|
self.curpos = self.RapidMove(extendcommand, self.curpos)
|
|
self.firstDrill = False
|
|
extendcommand = Path.Command('G0', {"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:
|
|
self.cutSolid.Shape = pathSolid
|
|
newStock = self.stock.cut([pathSolid], 1e-3)
|
|
try:
|
|
if newStock.isValid():
|
|
self.stock = newStock.removeSplitter()
|
|
except Exception:
|
|
if self.debug:
|
|
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.ioperation += 1
|
|
if self.ioperation >= len(self.activeOps):
|
|
self.EndSimulation()
|
|
return
|
|
else:
|
|
self.SetupOperation(self.ioperation)
|
|
if not self.disableAnim:
|
|
self.cutMaterial.Shape = self.stock
|
|
self.busy = False
|
|
|
|
def PerformCutVoxel(self):
|
|
if self.resetSimulation:
|
|
self.resetSimulation = False
|
|
self.SetupSimulation()
|
|
|
|
if self.busy:
|
|
return
|
|
self.busy = True
|
|
|
|
cmd = self.opCommands[self.icmd]
|
|
# 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.cutMaterial.Mesh, self.cutMaterialIn.Mesh) = self.voxSim.GetResultMesh()
|
|
if cmd.Name in ['G81', 'G82', 'G83']:
|
|
extendcommands = []
|
|
if self.firstDrill:
|
|
extendcommands.append(Path.Command('G0', {"X": 0.0, "Y": 0.0, "Z": cmd.r}))
|
|
self.firstDrill = False
|
|
extendcommands.append(Path.Command('G0', {"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.opCommands):
|
|
# self.cutMaterial.Shape = self.stock.removeSplitter()
|
|
self.ioperation += 1
|
|
if self.ioperation >= len(self.activeOps):
|
|
self.EndSimulation()
|
|
return
|
|
else:
|
|
self.SetupOperation(self.ioperation)
|
|
self.busy = False
|
|
|
|
def PerformCut(self):
|
|
if (self.isVoxel):
|
|
self.PerformCutVoxel()
|
|
else:
|
|
self.PerformCutBoolean()
|
|
|
|
def RapidMove(self, cmd, curpos):
|
|
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)
|
|
# n1 = e1.tangentAt(0)
|
|
# n1[2] = 0.0
|
|
# try:
|
|
# n1.normalize()
|
|
# 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 = e1.Curve.Radius - 0.001
|
|
# # return (None, e1.valueAt(e1.LastParameter))
|
|
# xf = n1[0] * rad
|
|
# yf = n1[1] * rad
|
|
# xp = curpos[0]
|
|
# yp = curpos[1]
|
|
# zp = curpos[2]
|
|
# v1 = Vector(yf + xp, -xf + yp, zp)
|
|
# 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)
|
|
# l1 = Part.makeLine(v1, v2)
|
|
# l2 = Part.makeLine(v2, 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))
|
|
# w1 = Part.Wire([l1, l2, l3, l4])
|
|
# w2 = Part.Wire(e1)
|
|
# try:
|
|
# ex1 = w2.makePipeShell([w1], True, True)
|
|
# except:
|
|
# # 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()
|
|
# 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)
|
|
startDir = toolPath.tangentAt(0)
|
|
startDir[2] = 0.0
|
|
endPos = toolPath.valueAt(toolPath.LastParameter)
|
|
endDir = toolPath.tangentAt(toolPath.LastParameter)
|
|
try:
|
|
startDir.normalize()
|
|
endDir.normalize()
|
|
except Exception:
|
|
return (None, endPos)
|
|
# height = self.height
|
|
|
|
# hack to overcome occ bugs
|
|
rad = tool.Diameter / 2.0 - 0.001 * pos[2]
|
|
# 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)
|
|
|
|
# create the path shell
|
|
toolProf = self.CreateToolProfile(tool, startDir, pos, rad)
|
|
rotmat = Base.Matrix()
|
|
rotmat.move(pos.negative())
|
|
rotmat.rotateZ(math.pi)
|
|
rotmat.move(pos)
|
|
mirroredProf = toolProf.transformGeometry(rotmat)
|
|
fullProf = Part.Wire([toolProf, mirroredProf])
|
|
pathWire = Part.Wire(toolPath)
|
|
try:
|
|
pathShell = pathWire.makePipeShell([fullProf], False, True)
|
|
except Exception:
|
|
if self.debug:
|
|
Part.show(pathWire)
|
|
Part.show(fullProf)
|
|
return (None, endPos)
|
|
|
|
# create the start cup
|
|
startCup = toolProf.revolve(pos, Vector(0, 0, 1), -180)
|
|
|
|
# create the end cup
|
|
endProf = self.CreateToolProfile(tool, endDir, endPos, rad)
|
|
endCup = endProf.revolve(endPos, Vector(0, 0, 1), 180)
|
|
|
|
fullShell = Part.makeShell(startCup.Faces + pathShell.Faces + endCup.Faces)
|
|
return (Part.makeSolid(fullShell).removeSplitter(), endPos)
|
|
|
|
# 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
|
|
xf = dir[0] * rad
|
|
yf = dir[1] * rad
|
|
xp = pos[0]
|
|
yp = pos[1]
|
|
zp = pos[2]
|
|
h = tool.CuttingEdgeHeight
|
|
if h <= 0.0: # set default if user fails to avoid freeze
|
|
h = 1.0
|
|
PathLog.error("SET Tool Length")
|
|
# common to all tools
|
|
vTR = Vector(xp + yf, yp - xf, zp + h)
|
|
vTC = Vector(xp, yp, zp + h)
|
|
vBC = Vector(xp, yp, zp)
|
|
lT = Part.makeLine(vTR, vTC)
|
|
res = None
|
|
if type == "ChamferMill":
|
|
ang = 90 - tool.CuttingEdgeAngle / 2.0
|
|
if ang > 80:
|
|
ang = 80
|
|
if ang < 0:
|
|
ang = 0
|
|
h1 = math.tan(ang * math.pi / 180) * rad
|
|
if h1 > (h - 0.1):
|
|
h1 = h - 0.1
|
|
vBR = Vector(xp + yf, yp - xf, zp + h1)
|
|
lR = Part.makeLine(vBR, vTR)
|
|
lB = Part.makeLine(vBC, vBR)
|
|
res = Part.Wire([lB, lR, lT])
|
|
|
|
elif type == "BallEndMill":
|
|
h1 = rad
|
|
if h1 >= h:
|
|
h1 = h - 0.1
|
|
vBR = Vector(xp + yf, yp - xf, zp + h1)
|
|
r2 = h1 / 2.0
|
|
h2 = rad - math.sqrt(rad * rad - r2 * r2)
|
|
vBCR = Vector(xp + yf / 2.0, yp - xf / 2.0, zp + h2)
|
|
cB = Part.Edge(Part.Arc(vBC, vBCR, vBR))
|
|
lR = Part.makeLine(vBR, vTR)
|
|
res = Part.Wire([cB, lR, lT])
|
|
|
|
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
|
|
|
|
def onJobChange(self):
|
|
form = self.taskForm.form
|
|
j = self.jobs[form.comboJobs.currentIndex()]
|
|
self.job = j
|
|
form.listOperations.clear()
|
|
self.operations = []
|
|
for op in j.Operations.OutList:
|
|
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)
|
|
if self.initdone:
|
|
self.SetupSimulation()
|
|
|
|
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()):
|
|
self.timer.setInterval(self.simperiod)
|
|
|
|
def onAccuracyBarChange(self):
|
|
form = self.taskForm.form
|
|
self.accuracy = 1.1 - 0.1 * form.sliderAccuracy.value()
|
|
form.labelAccuracy.setText(str(self.accuracy) + "%")
|
|
|
|
def GuiBusy(self, isBusy):
|
|
form = self.taskForm.form
|
|
# form.toolButtonStop.setEnabled()
|
|
form.toolButtonPlay.setEnabled(not isBusy)
|
|
form.toolButtonPause.setEnabled(isBusy)
|
|
form.toolButtonStep.setEnabled(not isBusy)
|
|
form.toolButtonFF.setEnabled(not isBusy)
|
|
|
|
def EndSimulation(self):
|
|
self.UpdateProgress()
|
|
self.timer.stop()
|
|
self.GuiBusy(False)
|
|
self.ViewShape()
|
|
self.resetSimulation = True
|
|
|
|
def SimStop(self):
|
|
self.cutTool.ViewObject.hide()
|
|
self.iprogress = 0
|
|
self.EndSimulation()
|
|
|
|
def InvalidOperation(self):
|
|
if len(self.activeOps) == 0:
|
|
return True
|
|
if (self.tool == None):
|
|
TSError("No tool assigned for the operation")
|
|
return True
|
|
return False
|
|
|
|
def SimFF(self):
|
|
if self.InvalidOperation():
|
|
return
|
|
self.GuiBusy(True)
|
|
self.timer.start(1)
|
|
self.disableAnim = True
|
|
|
|
def SimStep(self):
|
|
if self.InvalidOperation():
|
|
return
|
|
self.disableAnim = False
|
|
self.PerformCut()
|
|
|
|
def SimPlay(self):
|
|
if self.InvalidOperation():
|
|
return
|
|
self.disableAnim = False
|
|
self.GuiBusy(True)
|
|
self.timer.start(self.simperiod)
|
|
|
|
def ViewShape(self):
|
|
if self.isVoxel:
|
|
(self.cutMaterial.Mesh, self.cutMaterialIn.Mesh) = self.voxSim.GetResultMesh()
|
|
else:
|
|
self.cutMaterial.Shape = self.stock
|
|
|
|
def SimPause(self):
|
|
if self.disableAnim:
|
|
self.ViewShape()
|
|
self.GuiBusy(False)
|
|
self.timer.stop()
|
|
|
|
def RemoveTool(self):
|
|
if self.cutTool is None:
|
|
return
|
|
FreeCAD.ActiveDocument.removeObject(self.cutTool.Name)
|
|
self.cutTool = None
|
|
|
|
def RemoveInnerMaterial(self):
|
|
if self.cutMaterialIn is not None:
|
|
if self.isVoxel and self.cutMaterial is not None:
|
|
mesh = Mesh.Mesh()
|
|
mesh.addMesh(self.cutMaterial.Mesh)
|
|
mesh.addMesh(self.cutMaterialIn.Mesh)
|
|
self.cutMaterial.Mesh = mesh
|
|
FreeCAD.ActiveDocument.removeObject(self.cutMaterialIn.Name)
|
|
self.cutMaterialIn = None
|
|
|
|
def RemoveMaterial(self):
|
|
if self.cutMaterial is not None:
|
|
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:
|
|
|
|
def GetResources(self):
|
|
return {'Pixmap': 'Path-Simulator',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Simulator", "CAM Simulator"),
|
|
'Accel': "P, M",
|
|
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Simulator", "Simulate Path G-Code on stock")}
|
|
|
|
def IsActive(self):
|
|
if FreeCAD.ActiveDocument is not None:
|
|
for o in FreeCAD.ActiveDocument.Objects:
|
|
if o.Name[:3] == "Job":
|
|
return True
|
|
return False
|
|
|
|
def Activated(self):
|
|
pathSimulation.Activate()
|
|
|
|
pathSimulation = PathSimulation()
|
|
if FreeCAD.GuiUp:
|
|
# register the FreeCAD command
|
|
FreeCADGui.addCommand('Path_Simulator', CommandPathSimulate())
|
|
FreeCAD.Console.PrintLog("Loading PathSimulator Gui... done\n")
|