From 01b0225c4edebfbca703e57312712247c076085c Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Mon, 18 Sep 2017 00:44:46 -0700 Subject: [PATCH] Added loop detection for vertical faces and enhanced usability of loop select command accordingly. --- src/Mod/Path/PathCommands.py | 19 ++++++-- src/Mod/Path/PathScripts/PathPocketShape.py | 2 +- src/Mod/Path/PathScripts/PathUtils.py | 48 +++++++++++++++++++-- 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/src/Mod/Path/PathCommands.py b/src/Mod/Path/PathCommands.py index b0d41d5f2a..1b15394e79 100644 --- a/src/Mod/Path/PathCommands.py +++ b/src/Mod/Path/PathCommands.py @@ -25,7 +25,8 @@ import FreeCAD import PathScripts from PathScripts.PathUtils import loopdetect -from PathScripts.PathUtils import horizontalLoop +from PathScripts.PathUtils import horizontalEdgeLoop +from PathScripts.PathUtils import horizontalFaceLoop from PathScripts.PathUtils import addToJob from PathScripts.PathUtils import findParentJob @@ -57,8 +58,10 @@ class _CommandSelectLoop: sel = FreeCADGui.Selection.getSelectionEx()[0] sub1 = sel.SubElementNames[0] if sub1[0:4] != 'Edge': + if len(sel.SubElementNames) == 1 and sub1[0:4] == 'Face' and horizontalFaceLoop(sel.Object, sel.SubObjects[0]): + return True return False - if len(sel.SubElementNames) == 1 and horizontalLoop(sel.Object, sel.SubObjects[0]): + if len(sel.SubElementNames) == 1 and horizontalEdgeLoop(sel.Object, sel.SubObjects[0]): return True sub2 = sel.SubElementNames[1] if sub2[0:4] != 'Edge': @@ -72,11 +75,19 @@ class _CommandSelectLoop: obj = sel.Object edge1 = sel.SubObjects[0] if len(sel.SubObjects) == 1: - loopwire = horizontalLoop(obj, edge1) + if 'Face' in sel.SubElementNames[0]: + loop = horizontalFaceLoop(sel.Object, sel.SubObjects[0]) + if loop: + FreeCADGui.Selection.clearSelection() + FreeCADGui.Selection.addSelection(sel.Object, loop) + loopwire = [] + else: + loopwire = horizontalEdgeLoop(obj, edge1) else: edge2 = sel.SubObjects[1] loopwire = loopdetect(obj, edge1, edge2) - if loopwire is not None: + + if loopwire: FreeCADGui.Selection.clearSelection() elist = obj.Shape.Edges for e in elist: diff --git a/src/Mod/Path/PathScripts/PathPocketShape.py b/src/Mod/Path/PathScripts/PathPocketShape.py index aead19c6f4..8e81b8a5a4 100644 --- a/src/Mod/Path/PathScripts/PathPocketShape.py +++ b/src/Mod/Path/PathScripts/PathPocketShape.py @@ -39,7 +39,7 @@ __author__ = "sliptonic (Brad Collette)" __url__ = "http://www.freecadweb.org" __doc__ = "Class and implementation of shape based Pocket operation." -if True: +if False: PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) PathLog.trackModule(PathLog.thisModule()) else: diff --git a/src/Mod/Path/PathScripts/PathUtils.py b/src/Mod/Path/PathScripts/PathUtils.py index 56d5a2fc3b..a1558186ea 100644 --- a/src/Mod/Path/PathScripts/PathUtils.py +++ b/src/Mod/Path/PathScripts/PathUtils.py @@ -29,6 +29,7 @@ import numpy import Part import Path import PathScripts +import TechDraw from DraftGeomUtils import geomType from FreeCAD import Vector @@ -216,15 +217,56 @@ def loopdetect(obj, edge1, edge2): loopwire = next(x for x in loop)[1] return loopwire -def horizontalLoop(obj, edge): - '''horizontalLoopWire(obj, edge) ... returns a wire in the horizontal plane, if that is the only horizontal wire the given edge is a part of.''' +def horizontalEdgeLoop(obj, edge): + '''horizontalEdgeLoop(obj, edge) ... returns a wire in the horizontal plane, if that is the only horizontal wire the given edge is a part of.''' h = edge.hashCode() wires = [w for w in obj.Shape.Wires if any(e.hashCode() == h for e in w.Edges)] - loops = [w for w in wires if PathGeom.isVertical(Part.Face(w).Surface.Axis)] + loops = [w for w in wires if PathGeom.isHorizontal(Part.Face(w))] if len(loops) == 1: return loops[0] return None +def horizontalFaceLoop(obj, face): + '''horizontalFaceLoop(obj, face) ... returns a list of face names which form the walls of a vertical hole face is a part of.''' + + if not face and not obj: + sel = FreeCADGui.Selection.getSelectionEx()[0] + obj = sel.Object + face = sel.SubObjects[0] + + wires = [horizontalEdgeLoop(obj, e) for e in face.Edges] + # Not sure if sorting by Area is a premature optimization - but it seems + # the loop we're looking for is typically the biggest of the them all. + wires = sorted([w for w in wires if w], key=lambda w: Part.Face(w).Area) + + for wire in wires: + hashes = [e.hashCode() for e in wire.Edges] + + #find all faces that share a an edge with the wire and are vertical + faces = ["Face%d"%(i+1) for i,f in enumerate(obj.Shape.Faces) if any(e.hashCode() in hashes for e in f.Edges) and PathGeom.isVertical(f)] + + # verify they form a valid hole by getting the outline and comparing + # the resulting XY footprint with that of the faces + comp = Part.makeCompound([obj.Shape.getElement(f) for f in faces]) + outline = TechDraw.findShapeOutline(comp, 1, FreeCAD.Vector(0,0,1)) + + # findShapeOutline always returns closed wires, by removing the + # trace-backs single edge spikes don't contriubte to the bound box + uniqueEdges = [] + for edge in outline.Edges: + if any(PathGeom.edgesMatch(edge, e) for e in uniqueEdges): + continue + uniqueEdges.append(edge) + w = Part.Wire(uniqueEdges) + + # if the faces really form the walls of a hole then the resulting + # wire is still closed and it still has the same footprint + bb1 = comp.BoundBox + bb2 = w.BoundBox + if w.isClosed() and PathGeom.isRoughly(bb1.XMin, bb2.XMin) and PathGeom.isRoughly(bb1.XMax, bb2.XMax) and PathGeom.isRoughly(bb1.YMin, bb2.YMin) and PathGeom.isRoughly(bb1.YMax, bb2.YMax): + return faces + return None + def filterArcs(arcEdge): '''filterArcs(Edge) -used to split arcs that over 180 degrees. Returns list ''' PathLog.track()