Improves the point-based TSP solver to match tunnel solver behavior
for open routes (no endpoint constraint). Now applies 2-opt and
relocation optimizations that allow reversing or relocating segments
to the end of the route, resulting in better path optimization when
the ending point is flexible. Now links tsp_solver with
Python3::Python and uses add_library for compatibility with FreeCAD
and Fedora packaging.
src/Mod/CAM/App/tsp_solver.cpp:
- Add optimization limit variables for controlled iteration
- Add 2-opt and relocation optimizations for open routes
- Use Base::Precision::Confusion() for epsilon values
- Track last improvement step for efficient loop control
src/Mod/CAM/App/CMakeLists.txt:
- Switch tsp_solver from pybind11_add_module to add_library
- Link tsp_solver with pybind11::module and Python3::Python
- Update include directories for consistency
Update TSP tunnel solver to match revised Pythonlogic, including
improved early exit, open-ended route handling, and performance tweaks.
Also ensure extra data in tunnel dictionaries is preserved through the
C++/Python interface and add a test for passthrough of extra keys.
src/Mod/CAM/App/tsp_solver.cpp:
- Refactor optimization loop to use lastImprovementAtStep and limit
variables
- Add special handling for open-ended routes (no end point)
- Change epsilon to 10e-6 for consistency with Python
- Cache distance calculations for relocation step
src/Mod/CAM/App/tsp_solver_pybind.cpp:
- Preserve extra keys from input tunnel dicts in output
- Set tunnel index for passthrough
src/Mod/CAM/CAMTests/TestTSPSolver.py:
- Add test to verify extra data in tunnel dicts is preserved through
TSP solver
- Print extra data for debugging
Ensures that all extra keys in tunnel dictionaries are preserved after
TSP solving by copying the original input dict and updating solver
results. Adds a dedicated test to verify passthrough of extra data and
prints extra keys for debugging.
src/Mod/CAM/App/tsp_solver_pybind.cpp:
- Copy original tunnel dict and update with solver results to preserve extra keys
src/Mod/CAM/CAMTests/TestTSPSolver.py:
- Add test_09_tunnels_extra_data_passthrough to verify extra data preservation
- Print extra tunnel data in print_tunnels for easier debugging
Added extensive test coverage for the TSP tunnel solver including linear
tunnels, pentagram diagonals, and complex wire geometries with various
constraint combinations. Introduced a Python wrapper function for
tunnel sorting that integrates with the C++ solver.
src/Mod/CAM/CAMTests/TestTSPSolver.py:
- Renumbered existing tests to run in sequential order
(test_01_simple_tsp, test_02_start_point, etc.)
- Added print_tunnels() helper function for displaying tunnel
information with original indices
- Added test_06_tunnels_tsp: Tests 7 linear tunnels with varying
lengths, connectivity, and flipping behavior
- Added test_07_pentagram_tunnels_tsp: Tests pentagram diagonal tunnels
with no constraints, start+end constraints, and start-only constraints
- Added test_08_open_wire_end_only: Tests end-only constraint on complex
wire with 8 tunnels including crossings and diagonals
src/Mod/CAM/PathScripts/PathUtils.py:
- Added sort_tunnels_tsp() wrapper function that interfaces with the C++
tsp_solver.solveTunnels()
- Supports allowFlipping, routeStartPoint, and routeEndPoint parameters
Introduce TSPTunnel struct and implement TSPSolver::solveTunnels
for optimizing tunnel order with support for flipping and start/end points.
Expose the new functionality to Python via pybind11, returning tunnel
dictionaries with flipped status.
src/Mod/CAM/App/tsp_solver.cpp:
- Add solveTunnels implementation for tunnel TSP with flipping and route endpoints
src/Mod/CAM/App/tsp_solver.h:
- Define TSPTunnel struct
- Declare solveTunnels static method in TSPSolver
src/Mod/CAM/App/tsp_solver_pybind.cpp:
- Add Python wrapper for solveTunnels
- Expose solveTunnels to Python with argument parsing and result conversion
Add detailed test coverage for the new Command constructor that accepts
annotations parameter, including positional and keyword arguments,
string and numeric annotations, empty annotations, and backward
compatibility. Split existing save/restore tests for better granularity.
src/Mod/CAM/CAMTests/TestPathCommandAnnotations.py:
- Added test14-test20 for Command constructor with annotations
- Split test12 into test12 (empty annotations) and test13
(complex annotations) for focused testing
- Completely re-implemented the TSP algorithm in C++ for better path quality
- Added detailed comments and documentation to clarify each step
- Improved nearest neighbor, 2-opt, and relocation logic
- Enhanced handling of start/end point constraints
- Updated PathUtils.py docstring to accurately describe start point behavior
- Enhanced the C++ TSP solver to accept optional start and end points, so the route can begin and/or end at the closest point to specified coordinates.
- Updated the Python pybind11 wrapper and PathUtils.sort_locations_tsp to support startPoint and endPoint as named parameters.
- Added a new Python test suite (TestTSPSolver.py) to verify correct handling of start/end points and integration with PathUtils.
- Registered the new test in TestCAMApp.py and CMakeLists.txt for automatic test discovery.
This update introduces a new C++ 2-Opt TSP solver with Python bindings.
src/Mod/CAM/App/CMakeLists.txt:
- Add build and install rules for the new pybind11-based tsp_solver Python module
src/Mod/CAM/App/tsp_solver.cpp:
- Add new C++ implementation of a 2-Opt TSP solver with nearest-neighbor initialization
src/Mod/CAM/App/tsp_solver.h:
- Add TSPPoint struct and TSPSolver class with 2-Opt solve method
src/Mod/CAM/App/tsp_solver_pybind.cpp:
- Add pybind11 wrapper exposing the TSP solver to Python as tsp_solver.solve
src/Mod/CAM/PathScripts/PathUtils.py:
- Add sort_locations_tsp Python wrapper for the C++ TSP solver
- Use tsp_solver.solve for TSP-based
Used TestMach3Mach4LegacyPost.py as the starting point with input from
other test scripts too.
The test demonstrate a fix for #25723, as the Fanuc post processor has not been not updated to the latest changes in
the Path module.
This is related to #24676, where thread tapping was added to LinuxCNC, but the equivalent code in the Fanuc
postprocessor were not adjusted to cope, and #25677 where it was observed that the Fanuc postprocessor was
broken in versions 0.20 and 1.0.
Added MockTool class to provide ShapeName to fanuc_post.py, used to
convert drill cycles to threading cycles if the tool has shapeid "tap".
Several methods introduced 2025-05-04 are preferred, but the methods
available in version 1.0 are used as fallback sources
for active status and coolent enabled.
The difference according to the documentation is that M30 will rewind
the paper tape while M2 will not. The effect on a CNC from 1994 is
that M30 turn on the indicator light marking that the program has
completed, while M2 do not, and the light is wanted to know when
the machine is done.
Fanuc only understand upper case letters, no point in providing the
file name in lower case letters. The provided file name is always "-",
so drop it completely. The first comment is presented in the Fanuc user
interface, it should get more relevant content.
Dropped useless semicolon.
Enabling tool height compensation will cause the axis to move up
the length of the tool, which will cause a Z overtravel error when
the tool change take place close to the top of the machine. To
counter this move, ask the machine to move its commanded position
down the length of the tool height, which in effect causes no upward
movement after the tool change. The #4120 variable contain the
current tool number, and #2000 - #20XX contain the tool heights.
The Fanuc post processor add a trailing space to all M6 lines.
This make it problematic to write test for the generated output,
when compiled with the automatic policy enforcer on github
removing trialing space from the expected output. Avoid the
problem by removing the trailing space from the generated
output.
The thread tapping implementation in the Fanuc post processor change
behaviour of M3, G81 and G82 when the tool ShapeID matches "tap".
but the code not not expect that the parse() method will be
called with two different classes as arguments.
Rewrite the M3 handling to handle ToolController arguments
instead of crashing with an AttributeError.
Issues:
Fixes#14016Fixes#25723
Some Fanuc machines do not understand the 'M6 T0' instructions in the
preamble. Move it out of the preamble and controlled by a new
command line argument --no-end-spindle-empty for the machines
where running to cause a "Tool Number Alarm" and the program to crash
as tool zero is not a valid tool.
Fixes: #25677
Reset line number when using --line-numbers. Restore all default values when a
command line argument is not used.
This helps the test scripts ensure that the arguments passed during one test is the only
one taking effect during this test.
This PR allows each Toolbit to have its own Units property (Metric/Imperial) for better unit management.
Short Summary:
- Adds a Units property (Metric/Imperial) to ToolBit objects for better unit management.
- Ensures ToolBit schema is set and restored properly in the editor and utilities.
- Updates formatting and property handling to respect the selected units.
- Improves the ToolBit editor widget to refresh and sync schema/UI when units change.
- Uses FreeCAD.Units.setSchema and getSchema to switch between unit schemas.
NOTE: Toolbit dimensions are read from JSON in their native units (as specified by the Units property),
converted to metric for all internal calculations, and displayed in the UI using the
toolbit's selected units. This ensures both accurate internal computation and user-friendly
display, while storing the correct units in the JSON. This can cause some rounding differences
when switching units. Example: 2 mm becomes 0.0787 inches. If you save that as imperial and then
switch back to metric, it will show 1.9999 mm
src/Mod/CAM/Path/Tool/toolbit/models/base.py:
- Add Units property to ToolBit and ensure it's set and synced with toolbit shape parameters.
- Update property creation and value formatting to use Units.
src/Mod/CAM/Path/Tool/toolbit/ui/editor.py:
- Use setToolBitSchema to set schema based on toolbit units.
- Add logic to refresh property editor widget when units change.
- Restore original schema on close.
- Improve docstrings and signal handling.
src/Mod/CAM/Path/Tool/toolbit/util.py:
- Add setToolBitSchema function for robust schema switching.
- Update format_value to use schema and units.
- Add docstrings and clarify formatting logic.
src/Mod/CAM/Path/Tool/library/ui/browser.py:
- Restore original schema after editing toolbit.
src/Mod/CAM/Path/Tool/library/ui/editor.py:
- Ensure correct schema is set for new toolbits.
This commit makes boundary stock objects distinct from regular stock by
setting a boundary flag and label, and prevents editing of boundary
objects in the job view provider.
src/Mod/CAM/Path/Dressup/Boundary.py:
- Add promoteStockToBoundary method, set boundary properties, handle
missing stock/shape
src/Mod/CAM/Path/Main/Gui/Job.py:
- Block editing for objects flagged as boundary stock
This commit introduces support for passing an 'annotations' dictionary
to the Command class constructor and its Python bindings. The annotations
dictionary can contain string or numeric values for each key, allowing for
richer metadata on CAM commands. Each annotation must be provided as a
key-value pair within a dictionary.
Examples:
cmd = Command("G1", {"X": 10.0, "Y": 5.0}, {"note": "Rapid move"})
cmd = Command("G2", {"X": 20.0, "Y": 15.0}, {"priority": 1})
cmd = Command("G3", {"X": 30.0, "Y": 25.0}, {"note": "Arc move", "speed": 1500})
cmd = Command("G0", {"X": 0.0, "Y": 0.0}, {})
cmd = Command("G0", {"X": 0.0, "Y": 0.0})
cmd = Command("G1", {"X": 10.0, "Y": 5.0}, annotations={"note": "Rapid move"})
src/Mod/CAM/App/Command.cpp:
- Added new Command constructor accepting annotations
src/Mod/CAM/App/Command.h:
- Declared new Command constructor with annotations parameter
src/Mod/CAM/App/Command.pyi:
- Updated docstring to describe annotations argument
src/Mod/CAM/App/CommandPyImp.cpp:
- Extended Python init to parse and set annotations dictionary
This PR fixes a bug introduced with PR #25800
It updates the ObjectOp class to correctly assign the job, model,
and stock properties from the parentJob object. Previously, only
PathUtils.addToJob was called, which did not set these attributes directly.
This change ensures that when creating a new operation, the job, model,
and stock fields are properly initialized from the parent job. This
fixes issues where operations did not inherit the correct job context,
leading to missing or incorrect references to the model and stock, and
potential errors in downstream processing.
- Added new migration system to handle legacy parameter conversion for ToolBit assets and objects.
- Implemented ParameterAccessor abstraction for consistent access to dicts and FreeCAD objects.
- Centralized migration logic for CornerRadius from TorusRadius or FlatRadius/Diameter, restricted to Bullnose shape-type.
- Updated asset and object initialization to use migration logic and filter Bullnose parameters.
src/Mod/CAM/Path/Tool/camassets.py:
- Integrate new migration logic for ToolBit assets using ParameterAccessor and migrate_parameters.
- Ensure shape-type is set before parameter migration; only write changes if migration occurred.
src/Mod/CAM/Path/Tool/toolbit/migration.py:
- Add ParameterAccessor class and migrate_parameters function for unified migration.
- Handle legacy parameter conversion for Bullnose tools.
src/Mod/CAM/Path/Tool/toolbit/models/base.py:
- Apply migration logic to ToolBit objects on restore.
- Filter Bullnose parameters to remove FlatRadius after migration.
src/Mod/CAM/Path/Tool/shape/models/bullnose.py:
- Add filter_parameters method to remove FlatRadius for Bullnose.
src/Mod/CAM/Path/Op/SurfaceSupport.py:
- Derive FlatRadius from CornerRadius and Diameter if both are present.
src/Mod/CAM/Path/Op/Surface.py:
- Remove obsolete setOclCutter method.