# SPDX-License-Identifier: LGPL-2.1-or-later # *************************************************************************** # * Copyright (c) 2016 sliptonic * # * * # * 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 Part import Path import traceback import Path.Dressup.Utils as PathDressup from PathScripts.PathUtils import loopdetect from PathScripts.PathUtils import wiredetect from PathScripts.PathUtils import horizontalEdgeLoop from PathScripts.PathUtils import tangentEdgeLoop from PathScripts.PathUtils import horizontalFaceLoop from PathScripts.PathUtils import addToJob from PathScripts.PathUtils import findParentJob from PySide.QtCore import QT_TRANSLATE_NOOP if FreeCAD.GuiUp: import FreeCADGui translate = FreeCAD.Qt.translate __title__ = "FreeCAD Path Commands" __author__ = "sliptonic" __url__ = "https://www.freecad.org" class _CommandSelectLoop: "the Path command to complete loop selection definition" def __init__(self): self.obj = None self.sub = [] self.active = False def GetResources(self): return { "Pixmap": "CAM_SelectLoop", "MenuText": QT_TRANSLATE_NOOP("CAM_SelectLoop", "Finish Selecting Loop"), "Accel": "P, L", "ToolTip": QT_TRANSLATE_NOOP( "CAM_SelectLoop", "Completes the selection of edges or faces that form a loop" "\n\nSelect faces: searching loop faces which form the walls." "\n\nSelect one edge: searching loop edges in horizontal plane" "\nor wire which contain selected edge." "\n\nSelect two edges: searching loop edges in wires of the shape" "\nor tangent edges.", ), "CmdType": "ForEdit", } def IsActive(self): if bool(FreeCADGui.Selection.getSelection()) is False: return False try: sel = FreeCADGui.Selection.getSelectionEx()[0] if sel.Object == self.obj and sel.SubElementNames == self.sub: return self.active self.obj = sel.Object self.sub = sel.SubElementNames if sel.SubObjects: # self.active = self.formsPartOfALoop(sel.Object, sel.SubObjects[0], sel.SubElementNames) self.active = True else: self.active = False return self.active except Exception as exc: Path.Log.error(exc) traceback.print_exc(exc) return False def Activated(self): from PathScripts.PathUtils import horizontalEdgeLoop, horizontalFaceLoop if not FreeCADGui.Selection.getSelectionEx(): return sel = FreeCADGui.Selection.getSelectionEx()[0] if not sel.SubObjects: return obj = sel.Object sub = sel.SubObjects names = sel.SubElementNames loop = None # Face selection if "Face" in names[0]: loop = horizontalFaceLoop(obj, sub[0], names) if loop: FreeCADGui.Selection.clearSelection() FreeCADGui.Selection.addSelection(obj, loop) return elif "Edge" in names[0]: if len(sub) == 1: # One edge selected: searching horizontal edge loop loop = horizontalEdgeLoop(obj, sub[0], verbose=True) elif len(sub) >= 2: # Two edges selected: searching wire in shape which contain both edges loop = loopdetect(obj, sub[0], sub[1]) if not loop: # Two edges selected: searching edges in tangency loop = tangentEdgeLoop(obj, sub[0]) if not loop: # Searching any wire with first selected edge loop = wiredetect(obj, names[0]) if isinstance(loop, list) and len(loop) > 0 and isinstance(loop[0], Part.Edge): # Select edges from list objEdges = obj.Shape.Edges FreeCADGui.Selection.clearSelection() for el in loop: for eo in objEdges: if eo.hashCode() == el.hashCode(): FreeCADGui.Selection.addSelection(obj, f"Edge{objEdges.index(eo) + 1}") return Path.Log.warning(translate("CAM_SelectLoop", "Closed loop detection failed.")) def formsPartOfALoop(self, obj, sub, names): try: if names[0][0:4] != "Edge": if names[0][0:4] == "Face" and horizontalFaceLoop(obj, sub, names): return True return False if len(names) == 1 and horizontalEdgeLoop(obj, sub): return True if len(names) == 1 or names[1][0:4] != "Edge": return False return True except Exception: return False if FreeCAD.GuiUp: FreeCADGui.addCommand("CAM_SelectLoop", _CommandSelectLoop()) class _ToggleOperation: "command definition to toggle Operation Active state" def GetResources(self): return { "Pixmap": "CAM_OpActive", "MenuText": QT_TRANSLATE_NOOP("CAM_OpActiveToggle", "Toggle Operation"), "Accel": "P, X", "ToolTip": QT_TRANSLATE_NOOP( "CAM_OpActiveToggle", "Toggles the active state of the operation" ), "CmdType": "ForEdit", } def IsActive(self): selection = FreeCADGui.Selection.getSelection() if not selection: return False if len(selection) == 1: # allows to toggle all operations in Job sel = selection[0] if hasattr(sel, "Group") and sel.Name.startswith("Job"): return True if hasattr(sel, "Group") and sel.Name.startswith("Operations"): return True for sel in selection: baseOp = Path.Dressup.Utils.baseOp(sel) if not hasattr(baseOp, "Active"): return False return True def Activated(self): selection = FreeCADGui.Selection.getSelection() if (len(selection) == 1 and hasattr(selection[0], "Group")) and ( selection[0].Name.startswith("Job") or selection[0].Name.startswith("Operations") ): sel = selection[0] # process all Operations in Job if sel.Name.startswith("Job"): selection = sel.Operations.Group elif sel.Name.startswith("Operations"): selection = sel.Group states = [Path.Dressup.Utils.baseOp(sel).Active for sel in selection] if all(states) or not any(states): # all operations in one state (active or inactive) - toggle state for sel in selection: baseOp = Path.Dressup.Utils.baseOp(sel) baseOp.Active = not baseOp.Active else: # operations in different states - set Active state for sel in selection: baseOp = Path.Dressup.Utils.baseOp(sel) baseOp.Active = True else: for sel in selection: baseOp = Path.Dressup.Utils.baseOp(sel) baseOp.Active = not baseOp.Active FreeCAD.ActiveDocument.recompute() if FreeCAD.GuiUp: FreeCADGui.addCommand("CAM_OpActiveToggle", _ToggleOperation()) class _CopyOperation: "the Path Copy Operation command definition" def GetResources(self): return { "Pixmap": "CAM_OpCopy", "MenuText": QT_TRANSLATE_NOOP("CAM_OperationCopy", "Copy Operation"), "ToolTip": QT_TRANSLATE_NOOP("CAM_OperationCopy", "Copies the operation in the job"), "CmdType": "ForEdit", } def IsActive(self): selection = FreeCADGui.Selection.getSelection() if not selection: return False if any([not hasattr(sel, "Path") for sel in selection]): return False if any([sel.Name.startswith("Job") for sel in selection]): return False return True def Activated(self): selection = FreeCADGui.Selection.getSelection() for sel in selection: job = findParentJob(sel) prevOp = PathDressup.baseOp(sel) prevOpCopy = FreeCAD.ActiveDocument.copyObject(prevOp, False) while prevOp != sel: # recursive processing Dressup op = sel while op.Base != prevOp: # get higher level operation op = op.Base opCopy = FreeCAD.ActiveDocument.copyObject(op, False) opCopy.Base = prevOpCopy prevOpCopy = opCopy prevOp = op # add to Job top object addToJob(prevOpCopy, job.Name) if FreeCAD.GuiUp: FreeCADGui.addCommand("CAM_OperationCopy", _CopyOperation()) # \c findShape() is referenced from Gui/Command.cpp and used by Path.Area commands. # Do not remove! def findShape(shape, subname=None, subtype=None): """To find a higher order shape containing the subshape with subname. E.g. to find the wire containing 'Edge1' in shape, findShape(shape,'Edge1','Wires') """ if not subname: return shape ret = shape.getElement(subname) if not subtype or not ret or ret.isNull(): return ret if subname.startswith("Face"): tp = "Faces" elif subname.startswith("Edge"): tp = "Edges" elif subname.startswith("Vertex"): tp = "Vertex" else: return ret for obj in getattr(shape, subtype): for sobj in getattr(obj, tp): if sobj.isEqual(ret): return obj return ret