| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | import FreeCAD |
| | import Path |
| | import Path.Base.Util as PathUtil |
| | import Path.Dressup.Utils as PathDressup |
| | import PathScripts.PathUtils as PathUtils |
| | import Path.Main.Job as PathJob |
| | import PathGui |
| | import PathSimulator |
| | import math |
| | import os |
| |
|
| | from FreeCAD import Vector, Base |
| |
|
| | |
| | from lazy_loader.lazy_loader import LazyLoader |
| |
|
| | Mesh = LazyLoader("Mesh", globals(), "Mesh") |
| | Part = LazyLoader("Part", globals(), "Part") |
| |
|
| | if FreeCAD.GuiUp: |
| | import FreeCADGui |
| | from PySide import QtGui, QtCore |
| |
|
| | _filePath = os.path.dirname(os.path.abspath(__file__)) |
| |
|
| |
|
| | class CAMSimTaskUi: |
| | def __init__(self, parent): |
| | |
| | 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 |
| | self.jobs = [] |
| |
|
| | 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() |
| | self._populateJobSelection(form) |
| | form.comboJobs.currentIndexChanged.connect(self.onJobChange) |
| | self.onJobChange() |
| | FreeCADGui.Control.showDialog(self.taskForm) |
| | self.disableAnim = False |
| | self.isVoxel = True |
| | self.firstDrill = True |
| | self.voxSim = PathSimulator.PathSim() |
| | self.SimulateMill() |
| | self.initdone = True |
| |
|
| | def _populateJobSelection(self, form): |
| | |
| | setJobIdx = 0 |
| | jobName = "" |
| | jIdx = 0 |
| | |
| | jobList = FreeCAD.ActiveDocument.findObjects("Path::FeaturePython", "Job.*") |
| | jCnt = len(jobList) |
| |
|
| | |
| | guiSelection = FreeCADGui.Selection.getSelectionEx() |
| | if guiSelection: |
| | sel = guiSelection[0] |
| | if hasattr(sel.Object, "Proxy") and isinstance(sel.Object.Proxy, PathJob.ObjectJob): |
| | jobName = sel.Object.Name |
| | FreeCADGui.Selection.clearSelection() |
| |
|
| | |
| | form.comboJobs.blockSignals(True) |
| | form.comboJobs.clear() |
| | form.comboJobs.blockSignals(False) |
| | for j in jobList: |
| | form.comboJobs.addItem(j.ViewObject.Icon, j.Label) |
| | self.jobs.append(j) |
| | if j.Name == jobName or jCnt == 1: |
| | setJobIdx = jIdx |
| | jIdx += 1 |
| |
|
| | |
| | if jobName or jCnt == 1: |
| | form.comboJobs.setCurrentIndex(setJobIdx) |
| | else: |
| | form.comboJobs.setCurrentIndex(0) |
| |
|
| | 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.resolution = 0.01 * self.accuracy * maxlen |
| | self.voxSim.BeginSimulation(self.stock, self.resolution) |
| | ( |
| | 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 self.tool is not None: |
| | self.cutTool.Shape = self.tool.Shape |
| |
|
| | if not self.cutTool.Shape.isValid() or self.cutTool.Shape.isNull(): |
| | self.EndSimulation() |
| | raise RuntimeError( |
| | "Path Simulation: Error in tool geometry - {}".format(self.tool.Name) |
| | ) |
| |
|
| | self.cutTool.ViewObject.show() |
| | self.voxSim.SetToolShape(self.cutTool.Shape, 0.05 * self.accuracy) |
| | self.icmd = 0 |
| | self.curpos = FreeCAD.Placement(self.initialPos, self.stdrot) |
| | self.cutTool.Placement = self.curpos |
| | self.opCommands = PathUtils.getPathWithPlacement(self.operation).Commands |
| |
|
| | def SimulateMill(self): |
| | self.job = self.jobs[self.taskForm.form.comboJobs.currentIndex()] |
| | self.busy = False |
| | self.height = 10 |
| | self.skipStep = False |
| | self.initialPos = Vector(0, 0, self.job.Stock.Shape.BoundBox.ZMax) |
| | |
| | self.cutTool = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "CutTool") |
| | self.cutTool.ViewObject.Proxy = 0 |
| | self.cutTool.ViewObject.hide() |
| |
|
| | |
| | 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) |
| |
|
| | |
| | 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 PerformCutBoolean(self): |
| | if self.resetSimulation: |
| | self.resetSimulation = False |
| | self.SetupSimulation() |
| |
|
| | if self.busy: |
| | return |
| | self.busy = True |
| |
|
| | cmd = self.opCommands[self.icmd] |
| | pathSolid = None |
| |
|
| | if cmd.Name in ("G0", "G00"): |
| | self.firstDrill = True |
| | self.curpos = self.RapidMove(cmd, self.curpos) |
| | if cmd.Name in ("G1", "G01", "G2", "G02", "G3", "G03"): |
| | self.firstDrill = True |
| | if self.skipStep: |
| | self.curpos = self.RapidMove(cmd, self.curpos) |
| | else: |
| | (pathSolid, self.curpos) = self.GetPathSolid(self.tool, cmd, self.curpos) |
| |
|
| | if cmd.Name == "G80": |
| | self.firstDrill = True |
| | if cmd.Name in ("G73", "G81", "G82", "G83"): |
| | if self.firstDrill: |
| | extendcommand = Path.Command("G0", {"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.opCommands): |
| | 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] |
| | |
| | if cmd.Name in ("G0", "G00", "G1", "G01", "G2", "G02", "G3", "G03"): |
| | index = self.icmd |
| | if cmd.Name in ("G2", "G02", "G3", "G03"): |
| | while cmd.z is None: |
| | index -= 1 |
| | if index < 0: |
| | cmd.z = 0 |
| | break |
| | cmd.z = self.opCommands[index].z |
| |
|
| | self.firstDrill = True |
| | if cmd.Name in ("G2", "G02", "G3", "G03") and (cmd.k or 0) == 0: |
| | cx = self.curpos.Base.x + (cmd.i or 0) |
| | cy = self.curpos.Base.y + (cmd.j or 0) |
| | a0 = math.atan2(self.curpos.Base.y - cy, self.curpos.Base.x - cx) |
| | a1 = math.atan2(cmd.y - cy, cmd.x - cx) |
| | da = a1 - a0 |
| | if cmd.Name in ("G3", "G03"): |
| | da = da % (2 * math.pi) |
| | else: |
| | da = -((-da) % (2 * math.pi)) |
| | r = math.sqrt((cmd.i or 0) ** 2 + (cmd.j or 0) ** 2) |
| | n = math.ceil(math.sqrt(r / self.resolution * da * da)) |
| | da = da / n |
| | dz = (cmd.z - self.curpos.Base.z) / n |
| | cmd.Name = "G1" |
| | for i in range(n): |
| | a0 += da |
| | cmd.x = cx + r * math.cos(a0) |
| | cmd.y = cy + r * math.sin(a0) |
| | cmd.z = self.curpos.Base.z + dz |
| | self.curpos = self.voxSim.ApplyCommand(self.curpos, cmd) |
| | else: |
| | self.curpos = self.voxSim.ApplyCommand(self.curpos, cmd) |
| | if not self.disableAnim: |
| | self.cutTool.Placement = self.curpos |
| | ( |
| | self.cutMaterial.Mesh, |
| | self.cutMaterialIn.Mesh, |
| | ) = self.voxSim.GetResultMesh() |
| | if cmd.Name == "G80": |
| | self.firstDrill = True |
| | if cmd.Name in ("G73", "G81", "G82", "G83"): |
| | extendcommands = [] |
| | if self.firstDrill: |
| | extendcommands.append(Path.Command("G0", {"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 |
| | ( |
| | self.cutMaterial.Mesh, |
| | self.cutMaterialIn.Mesh, |
| | ) = self.voxSim.GetResultMesh() |
| | self.icmd += 1 |
| | self.iprogress += 1 |
| | self.UpdateProgress() |
| | if self.icmd >= len(self.opCommands): |
| | 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 = Path.Geom.edgeForCmd(cmd, curpos) |
| | if path is None: |
| | return curpos |
| | return path.valueAt(path.LastParameter) |
| |
|
| | |
| | def GetPathSolid(self, tool, cmd, pos): |
| | toolPath = Path.Geom.edgeForCmd(cmd, pos) |
| | 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) |
| |
|
| | |
| | rad = float(tool.Diameter) / 2.0 - 0.001 * pos[2] |
| | if type(toolPath.Curve) is Part.Circle and toolPath.Curve.Radius <= rad: |
| | rad = toolPath.Curve.Radius - 0.01 * (pos[2] + 1) |
| | return (None, endPos) |
| |
|
| | |
| | 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) |
| |
|
| | |
| | startCup = toolProf.revolve(pos, Vector(0, 0, 1), -180) |
| |
|
| | |
| | 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) |
| |
|
| | |
| | def CreateToolProfile(self, tool, dir, pos, rad): |
| | type = tool.ToolType |
| | xf = dir[0] * rad |
| | yf = dir[1] * rad |
| | xp = pos[0] |
| | yp = pos[1] |
| | zp = pos[2] |
| | h = tool.CuttingEdgeHeight |
| | if h <= 0.0: |
| | h = 1.0 |
| | Path.Log.error("SET Tool Length") |
| | |
| | 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: |
| | 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: |
| | if PathUtil.opProperty(op, "Active"): |
| | 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") |
| | 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(round(self.accuracy, 1)) + "%") |
| |
|
| | def GuiBusy(self, isBusy): |
| | form = self.taskForm.form |
| | 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 is 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": "CAM_Simulator", |
| | "MenuText": QtCore.QT_TRANSLATE_NOOP("CAM_Simulator", "Legacy CAM Simulator"), |
| | "Accel": "P, M", |
| | "ToolTip": QtCore.QT_TRANSLATE_NOOP("CAM_Simulator", "Simulates 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 = PathSimulation() |
| | pathSimulation.Activate() |
| |
|
| |
|
| | if FreeCAD.GuiUp: |
| | |
| | FreeCADGui.addCommand("CAM_Simulator", CommandPathSimulate()) |
| | FreeCAD.Console.PrintLog("Loading PathSimulator Gui… done\n") |
| |
|