Download src/Mod/CAM/Path/Main/Gui/Simulator.py from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 24.1 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/CAM/Path/Main/Gui/Simulator.py
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/CAM/Path/Main/Gui/Simulator.py
-
curl -L -o Simulator.py https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/CAM/Path/Main/Gui/Simulator.py
24.1 kB
| # SPDX-License-Identifier: LGPL-2.1-or-later | |
| # *************************************************************************** | |
| # * Copyright (c) 2017 Shai Seger <shaise at gmail> * | |
| # * * | |
| # * This program is free software; you can redistribute it and/or modify * | |
| # * it under the terms of the GNU Lesser General Public License (LGPL) * | |
| # * as published by the Free Software Foundation; either version 2 of * | |
| # * the License, or (at your option) any later version. * | |
| # * for detail see the LICENCE text file. * | |
| # * * | |
| # * This program is distributed in the hope that it will be useful, * | |
| # * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
| # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
| # * GNU Library General Public License for more details. * | |
| # * * | |
| # * You should have received a copy of the GNU Library General Public * | |
| # * License along with this program; if not, write to the Free Software * | |
| # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * | |
| # * USA * | |
| # * * | |
| # *************************************************************************** | |
| 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 | |
| # lazily loaded modules | |
| 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): | |
| # 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 | |
| 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): | |
| # Make Job selection combobox | |
| setJobIdx = 0 | |
| jobName = "" | |
| jIdx = 0 | |
| # Get list of Job objects in active document | |
| jobList = FreeCAD.ActiveDocument.findObjects("Path::FeaturePython", "Job.*") | |
| jCnt = len(jobList) | |
| # Check if user has selected a specific job for simulation | |
| guiSelection = FreeCADGui.Selection.getSelectionEx() | |
| if guiSelection: # Identify job selected by user | |
| sel = guiSelection[0] | |
| if hasattr(sel.Object, "Proxy") and isinstance(sel.Object.Proxy, PathJob.ObjectJob): | |
| jobName = sel.Object.Name | |
| FreeCADGui.Selection.clearSelection() | |
| # populate the job selection combobox | |
| 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 | |
| # Preselect GUI-selected job in the combobox | |
| 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) | |
| # 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 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] | |
| # for cmd in job.Path.Commands: | |
| 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) # hack to overcome occ bug | |
| if path is None: | |
| return curpos | |
| return path.valueAt(path.LastParameter) | |
| # get a solid representation of a tool going along path | |
| 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) | |
| # hack to overcome occ bugs | |
| 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) | |
| # 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 | |
| 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 | |
| Path.Log.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: | |
| 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: | |
| # register the FreeCAD command | |
| FreeCADGui.addCommand("CAM_Simulator", CommandPathSimulate()) | |
| FreeCAD.Console.PrintLog("Loading PathSimulator Gui… done\n") | |