# 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 a path from the selected shapes with the 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())