From f4acd046231e86745f6099d4c26e3ebce205ed56 Mon Sep 17 00:00:00 2001 From: tarman3 Date: Mon, 12 May 2025 22:57:23 +0300 Subject: [PATCH 1/2] CAM: PathShape with Tool Controller --- src/Mod/CAM/CMakeLists.txt | 1 + src/Mod/CAM/Gui/Resources/Path.qrc | 1 + .../CAM/Gui/Resources/icons/CAM_ShapeTC.svg | 721 ++++++++++++++++++ src/Mod/CAM/InitGui.py | 1 + src/Mod/CAM/Path/GuiInit.py | 1 + src/Mod/CAM/Path/Op/Gui/PathShapeTC.py | 239 ++++++ 6 files changed, 964 insertions(+) create mode 100644 src/Mod/CAM/Gui/Resources/icons/CAM_ShapeTC.svg create mode 100644 src/Mod/CAM/Path/Op/Gui/PathShapeTC.py diff --git a/src/Mod/CAM/CMakeLists.txt b/src/Mod/CAM/CMakeLists.txt index 0221cca79a..daab112b11 100644 --- a/src/Mod/CAM/CMakeLists.txt +++ b/src/Mod/CAM/CMakeLists.txt @@ -222,6 +222,7 @@ SET(PathPythonOpGui_SRCS Path/Op/Gui/FeatureExtension.py Path/Op/Gui/Helix.py Path/Op/Gui/MillFace.py + Path/Op/Gui/PathShapeTC.py Path/Op/Gui/Pocket.py Path/Op/Gui/PocketBase.py Path/Op/Gui/PocketShape.py diff --git a/src/Mod/CAM/Gui/Resources/Path.qrc b/src/Mod/CAM/Gui/Resources/Path.qrc index 964f76bf09..5271f1c37f 100644 --- a/src/Mod/CAM/Gui/Resources/Path.qrc +++ b/src/Mod/CAM/Gui/Resources/Path.qrc @@ -47,6 +47,7 @@ icons/CAM_SelectLoop.svg icons/CAM_SetupSheet.svg icons/CAM_Shape.svg + icons/CAM_ShapeTC.svg icons/CAM_SimpleCopy.svg icons/CAM_Simulator.svg icons/CAM_SimulatorGL.svg diff --git a/src/Mod/CAM/Gui/Resources/icons/CAM_ShapeTC.svg b/src/Mod/CAM/Gui/Resources/icons/CAM_ShapeTC.svg new file mode 100644 index 0000000000..1074f7d62c --- /dev/null +++ b/src/Mod/CAM/Gui/Resources/icons/CAM_ShapeTC.svg @@ -0,0 +1,721 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + CAM_Shape + 2015-07-04 + https://www.freecad.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/Mod/CAM/Gui/Resources/icons/CAM_Shape.svg + + + FreeCAD LGPL2+ + + + https://www.gnu.org/copyleft/lesser.html + + + [agryson] Alexander Gryson + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/CAM/InitGui.py b/src/Mod/CAM/InitGui.py index 7b93f8767e..9dbfbcd019 100644 --- a/src/Mod/CAM/InitGui.py +++ b/src/Mod/CAM/InitGui.py @@ -165,6 +165,7 @@ class CAMWorkbench(Workbench): threedcmdgroup = threedopcmdlist if Path.Preferences.experimentalFeaturesEnabled(): prepcmdlist.append("CAM_Shape") + prepcmdlist.append("CAM_PathShapeTC") extracmdlist.extend(["CAM_Area", "CAM_Area_Workplane"]) specialcmdlist.append("CAM_ThreadMilling") twodopcmdlist.append("CAM_Slot") diff --git a/src/Mod/CAM/Path/GuiInit.py b/src/Mod/CAM/Path/GuiInit.py index 94a29a5638..ccf815abb4 100644 --- a/src/Mod/CAM/Path/GuiInit.py +++ b/src/Mod/CAM/Path/GuiInit.py @@ -67,6 +67,7 @@ def Startup(): from Path.Op.Gui import Engrave from Path.Op.Gui import Helix from Path.Op.Gui import MillFace + from Path.Op.Gui import PathShapeTC from Path.Op.Gui import Pocket from Path.Op.Gui import PocketShape from Path.Op.Gui import Probe diff --git a/src/Mod/CAM/Path/Op/Gui/PathShapeTC.py b/src/Mod/CAM/Path/Op/Gui/PathShapeTC.py new file mode 100644 index 0000000000..b7c5eeeec7 --- /dev/null +++ b/src/Mod/CAM/Path/Op/Gui/PathShapeTC.py @@ -0,0 +1,239 @@ +# -*- coding: utf-8 -*- +# SPDX-License-Identifier: LGPL-2.1-or-later +# *************************************************************************** +# * * +# * Copyright (c) 2022 FreeCAD Project Association * +# * * +# * This file is part of FreeCAD. * +# * * +# * FreeCAD is free software: you can redistribute it and/or modify it * +# * under the terms of the GNU Lesser General Public License as * +# * published by the Free Software Foundation, either version 2.1 of the * +# * License, or (at your option) any later version. * +# * * +# * FreeCAD 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 * +# * Lesser General Public License for more details. * +# * * +# * You should have received a copy of the GNU Lesser General Public * +# * License along with FreeCAD. If not, see * +# * . * +# * * +# *************************************************************************** + +import Draft +import FreeCAD +import FreeCADGui +import Part +import Path +import Path.Op.Base as OpBase +import PathScripts.PathUtils as PathUtils + +from PySide.QtCore import QT_TRANSLATE_NOOP + +__title__ = "CAM Path from Shape with Tool Controller" +__author__ = "" +__inspirer__ = "Russ4262" +__url__ = "https://forum.freecad.org/viewtopic.php?t=93896" +__doc__ = "" + + +if False: + Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule()) + Path.Log.trackModule(Path.Log.thisModule()) +else: + Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule()) + + +translate = FreeCAD.Qt.translate + + +# Add base set of operation properties +def _addBaseProperties(obj): + obj.addProperty( + "App::PropertyBool", + "Active", + "Path", + QT_TRANSLATE_NOOP("App::Property", "Make False, to prevent operation from generating code"), + locked=True, + ) + obj.addProperty( + "App::PropertyString", + "Comment", + "Path", + QT_TRANSLATE_NOOP("App::Property", "An optional comment for this Operation"), + locked=True, + ) + obj.addProperty( + "App::PropertyString", + "UserLabel", + "Path", + QT_TRANSLATE_NOOP("App::Property", "User Assigned Label"), + locked=True, + ) + obj.addProperty( + "App::PropertyString", + "CycleTime", + "Path", + QT_TRANSLATE_NOOP("App::Property", "Operations Cycle Time Estimation"), + locked=True, + ) + obj.setEditorMode("CycleTime", 1) # Set property read-only + obj.Active = True + + +# Add ToolController properties +def _addToolController(obj): + obj.addProperty( + "App::PropertyLink", + "ToolController", + "Path", + QT_TRANSLATE_NOOP( + "App::Property", + "The tool controller that will be used to calculate the path", + ), + ) + obj.addProperty( + "App::PropertyDistance", + "OpToolDiameter", + "Op Values", + QT_TRANSLATE_NOOP("App::Property", "Holds the diameter of the tool"), + ) + obj.setEditorMode("OpToolDiameter", 1) # Set property read-only + obj.ToolController = PathUtils.findToolController(obj, None) + if not obj.ToolController: + raise OpBase.PathNoTCException() + obj.OpToolDiameter = obj.ToolController.Tool.Diameter + + obj.FeedRate = obj.ToolController.HorizFeed.Value + obj.FeedRateVertical = obj.ToolController.VertFeed.Value + + +# Get list of tool controllers +def _getToolControllers(obj, proxy=None): + # Modified getToolControllers() from PathScripts.PathUtils + # for Path object without Proxy + job = PathUtils.findParentJob(obj) + if job: + return [tc for tc in job.Tools.Group] + else: + return [] + + +# Set safety height parameters for Path operation +def _setSafetyZ(obj): + job = PathUtils.findParentJob(obj) + if job: + safetyZ = job.Stock.Shape.BoundBox.ZMax + 10 + obj.RetractThreshold = safetyZ + obj.Retraction = safetyZ + obj.ResumeHeight = safetyZ + + +# Geometry for selected shapes +class ObjectPartShape: + def __init__(self, obj, base): + # Path.Log.info("ObjectPartShape.__init__()") + self.obj = obj + obj.addProperty( + "App::PropertyLinkSubListGlobal", + "Base", + "Path", + QT_TRANSLATE_NOOP("App::Property", "The base geometry for this operation"), + ) + obj.Base = base + + def __getstate__(self): + return None + + def __setstate__(self, state): + return None + + def onDelete(self, obj, args): + return True + + def onDocumentRestored(self, obj): + self.obj = obj + + def onChanged(self, obj, prop): + """onChanged(obj, prop) ... method called when objECT is changed, + with source propERTY of the change.""" + if "Restore" in obj.State: + pass + + def execute(self, obj): + edges = [] + if obj.Base: + (base, subNames) = obj.Base[0] + edges = [ + base.Shape.getElement(sub).copy() for sub in subNames if sub.startswith("Edge") + ] + + if edges: + obj.Shape = Part.Wire(Part.__sortEdges__(edges)) + else: + obj.Shape = Part.Shape() + + +class CommandPathShapeTC: + def GetResources(self): + return { + "Pixmap": "CAM_ShapeTC", + "MenuText": QT_TRANSLATE_NOOP("CAM_PathShapeTC", "Path from Shape TC"), + "ToolTip": QT_TRANSLATE_NOOP( + "CAM_PathShapeTC", "Creates path from selected shapes with tool controller" + ), + } + + def IsActive(self): + isJob = False + if FreeCAD.ActiveDocument is not None: + for o in FreeCAD.ActiveDocument.Objects: + if o.Name.startswith("Job"): + isJob = True + break + if isJob: + selection = FreeCADGui.Selection.getSelectionEx() + if selection: + base = selection[0].Object + subBase = selection[0].SubElementNames + if subBase and [edge for edge in subBase if "Edge" in edge]: + return True + elif base.Shape.ShapeType in ["Wire", "Edge"]: + return True + return False + + def Activated(self): + print("Create PathShape object with Tool Controller") + doc = FreeCAD.ActiveDocument + selection = FreeCADGui.Selection.getSelectionEx() + shapeObj = None + if selection: + base = selection[0].Object + subBase = selection[0].SubElementNames + if subBase: + subEdges = [edge for edge in subBase if "Edge" in edge] + shapeObj = doc.addObject("Part::FeaturePython", "PartShape") + shapeObj.ViewObject.Proxy = 0 + shapeObj.Visibility = False + shapeObj.Proxy = ObjectPartShape(shapeObj, [(base, subEdges)]) + elif base.Shape.ShapeType in ["Wire", "Edge"]: + shapeObj = Draft.make_clone(base) + + pathObj = doc.addObject("Path::FeatureShape", "PathShape") + pathObj.Sources = [shapeObj] + + # Overwrite getToolControllers() function with modified version + PathUtils.getToolControllers = _getToolControllers + + PathUtils.addToJob(pathObj) + _addBaseProperties(pathObj) + _addToolController(pathObj) + _setSafetyZ(pathObj) + doc.recompute() + + +if FreeCAD.GuiUp: + # Register the FreeCAD command + FreeCADGui.addCommand("CAM_PathShapeTC", CommandPathShapeTC()) From b37b746f7e82602900d0cf8812a1133182fd9695 Mon Sep 17 00:00:00 2001 From: tarman3 Date: Tue, 13 May 2025 07:45:29 +0300 Subject: [PATCH 2/2] CAM: Remove CAM_Shape from top menu --- src/Mod/CAM/InitGui.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Mod/CAM/InitGui.py b/src/Mod/CAM/InitGui.py index 9dbfbcd019..29aa791901 100644 --- a/src/Mod/CAM/InitGui.py +++ b/src/Mod/CAM/InitGui.py @@ -164,7 +164,6 @@ class CAMWorkbench(Workbench): ) threedcmdgroup = threedopcmdlist if Path.Preferences.experimentalFeaturesEnabled(): - prepcmdlist.append("CAM_Shape") prepcmdlist.append("CAM_PathShapeTC") extracmdlist.extend(["CAM_Area", "CAM_Area_Workplane"]) specialcmdlist.append("CAM_ThreadMilling")