Skip to content

Instantly share code, notes, and snippets.

@freckletonj
Created October 14, 2025 19:12
Show Gist options
  • Select an option

  • Save freckletonj/a7e5f6f730ae8ee6da15ef7fcf714550 to your computer and use it in GitHub Desktop.

Select an option

Save freckletonj/a7e5f6f730ae8ee6da15ef7fcf714550 to your computer and use it in GitHub Desktop.
Custom FreeCAD CAM Simulation
'''
Use the new CAM simulator with custom stock and custom cutting paths
'''
import FreeCAD as App
import FreeCADGui as Gui
import Part
import CAMSimulator
from PathScripts import PathUtils
import Path.Dressup.Utils as PathDressup
from Path.Main.Gui import SimulatorGL
doc = App.ActiveDocument
if not doc:
App.Console.PrintError("No active document.\n")
raise RuntimeError("No active document")
# Locate the first Path Job
jobs = PathUtils.GetJobs()
if not jobs:
App.Console.PrintError("No Path Job found in the active document.\n")
raise RuntimeError("No Path Job found")
job = jobs[0]
App.Console.PrintMessage(f"Using job: {job.Label}\n")
# Extract stock and base shape (optional)
stock = job.Stock.Shape if hasattr(job, "Stock") and job.Stock else None
base_shape = None
if hasattr(job, "Model") and job.Model and len(job.Model.OutList) > 0:
base_shape = job.Model.OutList[0].Shape
elif hasattr(job, "Base") and job.Base:
base_shape = job.Base.Shape
if not stock:
App.Console.PrintWarning("Job has no stock defined, using a default box.\n")
stock = Part.makeBox(40, 40, 10)
# Collect all active operations in the job
ops = []
for obj in job.Operations.Group:
if hasattr(obj, "Path") and hasattr(obj, "Active") and obj.Active:
ops.append(obj)
App.Console.PrintMessage(f" - {obj.Label}\n")
if not ops:
App.Console.PrintError("No active operations found in job.\n")
raise RuntimeError("No active operations found")
App.Console.PrintMessage(f"Found {len(ops)} active operations.\n")
# Create the simulator instance
sim = CAMSimulator.PathSim()
sim.ResetSimulation()
sim_gl = SimulatorGL.CAMSimulation()
# Add the tools and their paths
for op in ops:
tc = PathDressup.toolController(op)
tool = tc.Tool
toolNumber = tc.ToolNumber
App.Console.PrintMessage(f"Adding tool {toolNumber} ({tool.Label})...\n")
resolution = 0.5
toolProfile = sim_gl.GetToolProfile(tool, resolution)
# AddTool expects: (profile_list, tool_number, diameter, resolution)
# The last parameter (resolution) should be 1 for tool resolution
sim.AddTool(toolProfile, toolNumber, tool.Diameter, 1)
# Add each command from the operation's path
opCommands = PathUtils.getPathWithPlacement(op).Commands
App.Console.PrintMessage(f" Adding {len(opCommands)} commands...\n")
for cmd in opCommands:
sim.AddCommand(cmd)
quality = 5 # int: 1=low, 10=high
App.Console.PrintMessage(f"Starting simulation with quality {quality}...\n")
# Begin Simulation
sim.BeginSimulation(stock, quality)
App.Console.PrintMessage("Simulation initialized successfully.\n")
# must set base shape after BeginSimulation (or else segfault)
if base_shape:
sim.SetBaseShape(base_shape, 1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment