diff --git a/src/Mod/Path/PathScripts/PathDressupDogbone.py b/src/Mod/Path/PathScripts/PathDressupDogbone.py index c91ef30078..1802c1a384 100644 --- a/src/Mod/Path/PathScripts/PathDressupDogbone.py +++ b/src/Mod/Path/PathScripts/PathDressupDogbone.py @@ -34,23 +34,25 @@ from PySide import QtCore # lazily loaded modules from lazy_loader.lazy_loader import LazyLoader -DraftGeomUtils = LazyLoader('DraftGeomUtils', globals(), 'DraftGeomUtils') -Part = LazyLoader('Part', globals(), 'Part') + +DraftGeomUtils = LazyLoader("DraftGeomUtils", globals(), "DraftGeomUtils") +Part = LazyLoader("Part", globals(), "Part") LOG_MODULE = PathLog.thisModule() PathLog.setLevel(PathLog.Level.NOTICE, LOG_MODULE) -#PathLog.trackModule(LOG_MODULE) +# PathLog.trackModule(LOG_MODULE) # Qt translation handling def translate(context, text, disambig=None): return QtCore.QCoreApplication.translate(context, text, disambig) -movecommands = ['G0', 'G00', 'G1', 'G01', 'G2', 'G02', 'G3', 'G03'] -movestraight = ['G1', 'G01'] -movecw = ['G2', 'G02'] -moveccw = ['G3', 'G03'] + +movecommands = ["G0", "G00", "G1", "G01", "G2", "G02", "G3", "G03"] +movestraight = ["G1", "G01"] +movecw = ["G2", "G02"] +moveccw = ["G3", "G03"] movearc = movecw + moveccw @@ -59,7 +61,9 @@ def debugMarker(vector, label, color=None, radius=0.5): obj = FreeCAD.ActiveDocument.addObject("Part::Sphere", label) obj.Label = label obj.Radius = radius - obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0)) + obj.Placement = FreeCAD.Placement( + vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0) + ) if color: obj.ViewObject.ShapeColor = color @@ -70,7 +74,9 @@ def debugCircle(vector, r, label, color=None): obj.Label = label obj.Radius = r obj.Height = 1 - obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0)) + obj.Placement = FreeCAD.Placement( + vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0) + ) obj.ViewObject.Transparency = 90 if color: obj.ViewObject.ShapeColor = color @@ -79,9 +85,9 @@ def debugCircle(vector, r, label, color=None): def addAngle(a1, a2): a = a1 + a2 while a <= -math.pi: - a += 2*math.pi + a += 2 * math.pi while a > math.pi: - a -= 2*math.pi + a -= 2 * math.pi return a @@ -102,7 +108,7 @@ def getAngle(v): return a -def pointFromCommand(cmd, pt, X='X', Y='Y', Z='Z'): +def pointFromCommand(cmd, pt, X="X", Y="Y", Z="Z"): x = cmd.Parameters.get(X, pt.x) y = cmd.Parameters.get(Y, pt.y) z = cmd.Parameters.get(Z, pt.z) @@ -118,14 +124,16 @@ def edgesForCommands(cmds, startPt): if cmd.Name in movestraight: edges.append(Part.Edge(Part.LineSegment(lastPt, pt))) elif cmd.Name in movearc: - center = lastPt + pointFromCommand(cmd, FreeCAD.Vector(0, 0, 0), 'I', 'J', 'K') + center = lastPt + pointFromCommand( + cmd, FreeCAD.Vector(0, 0, 0), "I", "J", "K" + ) A = lastPt - center B = pt - center d = -B.x * A.y + B.y * A.x if d == 0: # we're dealing with half a circle here - angle = getAngle(A) + math.pi/2 + angle = getAngle(A) + math.pi / 2 if cmd.Name in movecw: angle -= math.pi else: @@ -143,19 +151,19 @@ def edgesForCommands(cmds, startPt): class Style(object): # pylint: disable=no-init - Dogbone = 'Dogbone' - Tbone_H = 'T-bone horizontal' - Tbone_V = 'T-bone vertical' - Tbone_L = 'T-bone long edge' - Tbone_S = 'T-bone short edge' + Dogbone = "Dogbone" + Tbone_H = "T-bone horizontal" + Tbone_V = "T-bone vertical" + Tbone_L = "T-bone long edge" + Tbone_S = "T-bone short edge" All = [Dogbone, Tbone_H, Tbone_V, Tbone_L, Tbone_S] class Side(object): # pylint: disable=no-init - Left = 'Left' - Right = 'Right' + Left = "Left" + Right = "Right" All = [Left, Right] @classmethod @@ -170,9 +178,9 @@ class Side(object): class Incision(object): # pylint: disable=no-init - Fixed = 'fixed' - Adaptive = 'adaptive' - Custom = 'custom' + Fixed = "fixed" + Adaptive = "adaptive" + Custom = "custom" All = [Adaptive, Fixed, Custom] @@ -194,8 +202,7 @@ class Smooth(object): # be constant in all calculated results. # Instances of Chord are generally considered immutable and all movement member # functions return new instances. -class Chord (object): - +class Chord(object): def __init__(self, start=None, end=None): if not start: start = FreeCAD.Vector() @@ -205,15 +212,22 @@ class Chord (object): self.End = end def __str__(self): - return "Chord([%g, %g, %g] -> [%g, %g, %g])" % (self.Start.x, self.Start.y, self.Start.z, self.End.x, self.End.y, self.End.z) + return "Chord([%g, %g, %g] -> [%g, %g, %g])" % ( + self.Start.x, + self.Start.y, + self.Start.z, + self.End.x, + self.End.y, + self.End.z, + ) def moveTo(self, newEnd): return Chord(self.End, newEnd) def moveToParameters(self, params): - x = params.get('X', self.End.x) - y = params.get('Y', self.End.y) - z = params.get('Z', self.End.z) + x = params.get("X", self.End.x) + y = params.get("Y", self.End.y) + z = params.get("Z", self.End.z) return self.moveTo(FreeCAD.Vector(x, y, z)) def moveBy(self, x, y, z): @@ -244,14 +258,14 @@ class Chord (object): # if the 2 vectors are identical, they head in the same direction PathLog.debug(" {}.getDirectionOfVector({})".format(A, B)) if PathGeom.pointsCoincide(A, B): - return 'Straight' - d = -A.x*B.y + A.y*B.x + return "Straight" + d = -A.x * B.y + A.y * B.x if d < 0: return Side.Left if d > 0: return Side.Right # at this point the only direction left is backwards - return 'Back' + return "Back" def getDirectionOf(self, chordOrVector): if type(chordOrVector) is Chord: @@ -278,7 +292,7 @@ class Chord (object): def commandParams(self, f): params = {"X": self.End.x, "Y": self.End.y, "Z": self.End.z} if f: - params['F'] = f + params["F"] = f return params def g1Command(self, f): @@ -287,9 +301,9 @@ class Chord (object): def arcCommand(self, cmd, center, f): params = self.commandParams(f) d = center - self.Start - params['I'] = d.x - params['J'] = d.y - params['K'] = 0 + params["I"] = d.x + params["J"] = d.y + params["K"] = 0 return Path.Command(cmd, params) def g2Command(self, center, f): @@ -302,13 +316,17 @@ class Chord (object): return not PathGeom.isRoughly(self.End.z, self.Start.z) def isANoopMove(self): - PathLog.debug("{}.isANoopMove(): {}".format(self, PathGeom.pointsCoincide(self.Start, self.End))) + PathLog.debug( + "{}.isANoopMove(): {}".format( + self, PathGeom.pointsCoincide(self.Start, self.End) + ) + ) return PathGeom.pointsCoincide(self.Start, self.End) def foldsBackOrTurns(self, chord, side): direction = chord.getDirectionOf(self) PathLog.info(" - direction = %s/%s" % (direction, side)) - return direction == 'Back' or direction == side + return direction == "Back" or direction == side def connectsTo(self, chord): return PathGeom.pointsCoincide(self.End, chord.Start) @@ -335,7 +353,7 @@ class Bone(object): if self.cAngle is None: baseAngle = self.inChord.getAngleXY() turnAngle = self.outChord.getAngle(self.inChord) - theta = addAngle(baseAngle, (turnAngle - math.pi)/2) + theta = addAngle(baseAngle, (turnAngle - math.pi) / 2) if self.obj.Side == Side.Left: theta = addAngle(theta, math.pi) self.tAngle = turnAngle @@ -345,7 +363,7 @@ class Bone(object): def distance(self, toolRadius): if self.cDist is None: self.angle() # make sure the angles are initialized - self.cDist = toolRadius / math.cos(self.tAngle/2) + self.cDist = toolRadius / math.cos(self.tAngle / 2) return self.cDist def corner(self, toolRadius): @@ -368,7 +386,10 @@ class Bone(object): # moving directly towards the corner PathLog.debug("adaptive - on target: %.2f - %.2f" % (distance, toolRadius)) return distance - toolRadius - PathLog.debug("adaptive - angles: corner=%.2f bone=%.2f diff=%.12f" % (theta/math.pi, boneAngle/math.pi, theta - boneAngle)) + PathLog.debug( + "adaptive - angles: corner=%.2f bone=%.2f diff=%.12f" + % (theta / math.pi, boneAngle / math.pi, theta - boneAngle) + ) # The bones root and end point form a triangle with the intersection of the tool path # with the toolRadius circle around the bone end point. @@ -376,7 +397,9 @@ class Bone(object): # c = distance # b = self.toolRadius # beta = fabs(boneAngle - theta) - beta = math.fabs(addAngle(boneAngle, -theta)) # pylint: disable=invalid-unary-operand-type + beta = math.fabs( + addAngle(boneAngle, -theta) + ) # pylint: disable=invalid-unary-operand-type D = (distance / toolRadius) * math.sin(beta) if D > 1: # no intersection PathLog.debug("adaptive - no intersection - no bone") @@ -395,28 +418,68 @@ class Bone(object): length2 = toolRadius * math.sin(alpha2) / math.sin(beta2) length = min(length, length2) - PathLog.debug("adaptive corner=%.2f * %.2f˚ -> bone=%.2f * %.2f˚" % (distance, theta, length, boneAngle)) + PathLog.debug( + "adaptive corner=%.2f * %.2f˚ -> bone=%.2f * %.2f˚" + % (distance, theta, length, boneAngle) + ) return length class ObjectDressup(object): - def __init__(self, obj, base): # Tool Properties - obj.addProperty("App::PropertyLink", "Base", "Base", QtCore.QT_TRANSLATE_NOOP("Path_DressupDogbone", "The base path to modify")) - obj.addProperty("App::PropertyEnumeration", "Side", "Dressup", QtCore.QT_TRANSLATE_NOOP("Path_DressupDogbone", "The side of path to insert bones")) + obj.addProperty( + "App::PropertyLink", + "Base", + "Base", + QtCore.QT_TRANSLATE_NOOP("Path_DressupDogbone", "The base path to modify"), + ) + obj.addProperty( + "App::PropertyEnumeration", + "Side", + "Dressup", + QtCore.QT_TRANSLATE_NOOP( + "Path_DressupDogbone", "The side of path to insert bones" + ), + ) obj.Side = [Side.Left, Side.Right] obj.Side = Side.Right - obj.addProperty("App::PropertyEnumeration", "Style", "Dressup", QtCore.QT_TRANSLATE_NOOP("Path_DressupDogbone", "The style of bones")) + obj.addProperty( + "App::PropertyEnumeration", + "Style", + "Dressup", + QtCore.QT_TRANSLATE_NOOP("Path_DressupDogbone", "The style of bones"), + ) obj.Style = Style.All obj.Style = Style.Dogbone - obj.addProperty("App::PropertyIntegerList", "BoneBlacklist", "Dressup", QtCore.QT_TRANSLATE_NOOP("Path_DressupDogbone", "Bones that aren't dressed up")) + obj.addProperty( + "App::PropertyIntegerList", + "BoneBlacklist", + "Dressup", + QtCore.QT_TRANSLATE_NOOP( + "Path_DressupDogbone", "Bones that aren't dressed up" + ), + ) obj.BoneBlacklist = [] - obj.setEditorMode('BoneBlacklist', 2) # hide this one - obj.addProperty("App::PropertyEnumeration", "Incision", "Dressup", QtCore.QT_TRANSLATE_NOOP("Path_DressupDogbone", "The algorithm to determine the bone length")) + obj.setEditorMode("BoneBlacklist", 2) # hide this one + obj.addProperty( + "App::PropertyEnumeration", + "Incision", + "Dressup", + QtCore.QT_TRANSLATE_NOOP( + "Path_DressupDogbone", "The algorithm to determine the bone length" + ), + ) obj.Incision = Incision.All obj.Incision = Incision.Adaptive - obj.addProperty("App::PropertyFloat", "Custom", "Dressup", QtCore.QT_TRANSLATE_NOOP("Path_DressupDogbone", "Dressup length if Incision == custom")) + obj.addProperty( + "App::PropertyFloat", + "Custom", + "Dressup", + QtCore.QT_TRANSLATE_NOOP( + "Path_DressupDogbone", "Dressup length if Incision == custom" + ), + ) obj.Custom = 0.0 obj.Proxy = self obj.Base = base @@ -431,7 +494,7 @@ class ObjectDressup(object): self.bones = None def onDocumentRestored(self, obj): - obj.setEditorMode('BoneBlacklist', 2) # hide this one + obj.setEditorMode("BoneBlacklist", 2) # hide this one def __getstate__(self): return None @@ -446,14 +509,29 @@ class ObjectDressup(object): # Answer true if a dogbone could be on either end of the chord, given its command def canAttachDogbone(self, cmd, chord): - return cmd.Name in movestraight and not chord.isAPlungeMove() and not chord.isANoopMove() + return ( + cmd.Name in movestraight + and not chord.isAPlungeMove() + and not chord.isANoopMove() + ) def shouldInsertDogbone(self, obj, inChord, outChord): return outChord.foldsBackOrTurns(inChord, self.theOtherSideOf(obj.Side)) def findPivotIntersection(self, pivot, pivotEdge, edge, refPt, d, color): # pylint: disable=unused-argument - PathLog.track("(%.2f, %.2f)^%.2f - [(%.2f, %.2f), (%.2f, %.2f)]" % (pivotEdge.Curve.Center.x, pivotEdge.Curve.Center.y, pivotEdge.Curve.Radius, edge.Vertexes[0].Point.x, edge.Vertexes[0].Point.y, edge.Vertexes[1].Point.x, edge.Vertexes[1].Point.y)) + PathLog.track( + "(%.2f, %.2f)^%.2f - [(%.2f, %.2f), (%.2f, %.2f)]" + % ( + pivotEdge.Curve.Center.x, + pivotEdge.Curve.Center.y, + pivotEdge.Curve.Radius, + edge.Vertexes[0].Point.x, + edge.Vertexes[0].Point.y, + edge.Vertexes[1].Point.x, + edge.Vertexes[1].Point.y, + ) + ) ppt = None pptDistance = 0 for pt in DraftGeomUtils.findIntersection(edge, pivotEdge, dts=False): @@ -469,7 +547,9 @@ class ObjectDressup(object): PathLog.debug("Taking tangent as intersect %s" % tangent) ppt = pivot + tangent else: - PathLog.debug("Taking chord start as intersect %s" % edge.Vertexes[0].Point) + PathLog.debug( + "Taking chord start as intersect %s" % edge.Vertexes[0].Point + ) ppt = edge.Vertexes[0].Point # debugMarker(ppt, "ptt.%d-%s.in" % (self.boneId, d), color, 0.2) PathLog.debug(" --> (%.2f, %.2f)" % (ppt.x, ppt.y)) @@ -479,15 +559,17 @@ class ObjectDressup(object): param = edge.Curve.parameter(point) return edge.FirstParameter <= param <= edge.LastParameter - def smoothChordCommands(self, bone, inChord, outChord, edge, wire, corner, smooth, color=None): + def smoothChordCommands( + self, bone, inChord, outChord, edge, wire, corner, smooth, color=None + ): if smooth == 0: PathLog.info(" No smoothing requested") return [bone.lastCommand, outChord.g1Command(bone.F)] - d = 'in' + d = "in" refPoint = inChord.Start if smooth == Smooth.Out: - d = 'out' + d = "out" refPoint = outChord.End if DraftGeomUtils.areColinear(inChord.asEdge(), outChord.asEdge()): @@ -497,15 +579,36 @@ class ObjectDressup(object): pivot = None pivotDistance = 0 - PathLog.info("smooth: (%.2f, %.2f)-(%.2f, %.2f)" % (edge.Vertexes[0].Point.x, edge.Vertexes[0].Point.y, edge.Vertexes[1].Point.x, edge.Vertexes[1].Point.y)) + PathLog.info( + "smooth: (%.2f, %.2f)-(%.2f, %.2f)" + % ( + edge.Vertexes[0].Point.x, + edge.Vertexes[0].Point.y, + edge.Vertexes[1].Point.x, + edge.Vertexes[1].Point.y, + ) + ) for e in wire.Edges: self.dbg.append(e) if type(e.Curve) == Part.LineSegment or type(e.Curve) == Part.Line: - PathLog.debug(" (%.2f, %.2f)-(%.2f, %.2f)" % (e.Vertexes[0].Point.x, e.Vertexes[0].Point.y, e.Vertexes[1].Point.x, e.Vertexes[1].Point.y)) + PathLog.debug( + " (%.2f, %.2f)-(%.2f, %.2f)" + % ( + e.Vertexes[0].Point.x, + e.Vertexes[0].Point.y, + e.Vertexes[1].Point.x, + e.Vertexes[1].Point.y, + ) + ) else: - PathLog.debug(" (%.2f, %.2f)^%.2f" % (e.Curve.Center.x, e.Curve.Center.y, e.Curve.Radius)) + PathLog.debug( + " (%.2f, %.2f)^%.2f" + % (e.Curve.Center.x, e.Curve.Center.y, e.Curve.Radius) + ) for pt in DraftGeomUtils.findIntersection(edge, e, True, findAll=True): - if not PathGeom.pointsCoincide(pt, corner) and self.pointIsOnEdge(pt, e): + if not PathGeom.pointsCoincide(pt, corner) and self.pointIsOnEdge( + pt, e + ): # debugMarker(pt, "candidate-%d-%s" % (self.boneId, d), color, 0.05) PathLog.debug(" -> candidate") distance = (pt - refPoint).Length @@ -518,9 +621,15 @@ class ObjectDressup(object): if pivot: # debugCircle(pivot, self.toolRadius, "pivot.%d-%s" % (self.boneId, d), color) - pivotEdge = Part.Edge(Part.Circle(pivot, FreeCAD.Vector(0, 0, 1), self.toolRadius)) - t1 = self.findPivotIntersection(pivot, pivotEdge, inChord.asEdge(), inChord.End, d, color) - t2 = self.findPivotIntersection(pivot, pivotEdge, outChord.asEdge(), inChord.End, d, color) + pivotEdge = Part.Edge( + Part.Circle(pivot, FreeCAD.Vector(0, 0, 1), self.toolRadius) + ) + t1 = self.findPivotIntersection( + pivot, pivotEdge, inChord.asEdge(), inChord.End, d, color + ) + t2 = self.findPivotIntersection( + pivot, pivotEdge, outChord.asEdge(), inChord.End, d, color + ) commands = [] if not PathGeom.pointsCoincide(t1, inChord.Start): @@ -530,7 +639,10 @@ class ObjectDressup(object): PathLog.debug(" add g3 command") commands.append(Chord(t1, t2).g3Command(pivot, bone.F)) else: - PathLog.debug(" add g2 command center=(%.2f, %.2f) -> from (%2f, %.2f) to (%.2f, %.2f" % (pivot.x, pivot.y, t1.x, t1.y, t2.x, t2.y)) + PathLog.debug( + " add g2 command center=(%.2f, %.2f) -> from (%2f, %.2f) to (%.2f, %.2f" + % (pivot.x, pivot.y, t1.x, t1.y, t2.x, t2.y) + ) commands.append(Chord(t1, t2).g2Command(pivot, bone.F)) if not PathGeom.pointsCoincide(t2, outChord.End): PathLog.debug(" add lead out") @@ -575,7 +687,12 @@ class ObjectDressup(object): bone.tip = boneInChord.End if bone.smooth == 0: - return [bone.lastCommand, boneInChord.g1Command(bone.F), boneOutChord.g1Command(bone.F), bone.outChord.g1Command(bone.F)] + return [ + bone.lastCommand, + boneInChord.g1Command(bone.F), + boneOutChord.g1Command(bone.F), + bone.outChord.g1Command(bone.F), + ] # reconstruct the corner and convert to an edge offset = corner - bone.inChord.End @@ -587,7 +704,7 @@ class ObjectDressup(object): # construct a shape representing the cut made by the bone vt0 = FreeCAD.Vector(0, self.toolRadius, 0) - vt1 = FreeCAD.Vector(length, self.toolRadius, 0) + vt1 = FreeCAD.Vector(length, self.toolRadius, 0) vb0 = FreeCAD.Vector(0, -self.toolRadius, 0) vb1 = FreeCAD.Vector(length, -self.toolRadius, 0) vm2 = FreeCAD.Vector(length + self.toolRadius, 0, 0) @@ -601,12 +718,32 @@ class ObjectDressup(object): boneArc = Part.Arc(vt1, vm2, vb1) # boneArc = Part.Circle(FreeCAD.Vector(length, 0, 0), FreeCAD.Vector(0,0,1), self.toolRadius) boneWire = Part.Shape([boneTop, boneArc, boneBot, boneLid]) - boneWire.rotate(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), boneAngle * 180 / math.pi) + boneWire.rotate( + FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), boneAngle * 180 / math.pi + ) boneWire.translate(bone.inChord.End) self.boneShapes = [cornerShape, boneWire] - bone.inCommands = self.smoothChordCommands(bone, bone.inChord, boneInChord, Part.Edge(iLine), boneWire, corner, bone.smooth & Smooth.In, (1., 0., 0.)) - bone.outCommands = self.smoothChordCommands(bone, boneOutChord, bone.outChord, Part.Edge(oLine), boneWire, corner, bone.smooth & Smooth.Out, (0., 1., 0.)) + bone.inCommands = self.smoothChordCommands( + bone, + bone.inChord, + boneInChord, + Part.Edge(iLine), + boneWire, + corner, + bone.smooth & Smooth.In, + (1.0, 0.0, 0.0), + ) + bone.outCommands = self.smoothChordCommands( + bone, + boneOutChord, + bone.outChord, + Part.Edge(oLine), + boneWire, + corner, + bone.smooth & Smooth.Out, + (0.0, 1.0, 0.0), + ) return bone.inCommands + bone.outCommands def dogbone(self, bone): @@ -617,13 +754,13 @@ class ObjectDressup(object): def tboneHorizontal(self, bone): angle = bone.angle() boneAngle = 0 - if math.fabs(angle) > math.pi/2: + if math.fabs(angle) > math.pi / 2: boneAngle = math.pi return self.inOutBoneCommands(bone, boneAngle, self.toolRadius) def tboneVertical(self, bone): angle = bone.angle() - boneAngle = math.pi/2 + boneAngle = math.pi / 2 if PathGeom.isRoughly(angle, math.pi) or angle < 0: boneAngle = -boneAngle return self.inOutBoneCommands(bone, boneAngle, self.toolRadius) @@ -635,14 +772,22 @@ class ObjectDressup(object): boneAngle = bone.outChord.getAngleXY() if Side.Right == bone.outChord.getDirectionOf(bone.inChord): - boneAngle = boneAngle - math.pi/2 + boneAngle = boneAngle - math.pi / 2 else: - boneAngle = boneAngle + math.pi/2 + boneAngle = boneAngle + math.pi / 2 - onInString = 'out' + onInString = "out" if onIn: - onInString = 'in' - PathLog.debug("tboneEdge boneAngle[%s]=%.2f (in=%.2f, out=%.2f)" % (onInString, boneAngle/math.pi, bone.inChord.getAngleXY()/math.pi, bone.outChord.getAngleXY()/math.pi)) + onInString = "in" + PathLog.debug( + "tboneEdge boneAngle[%s]=%.2f (in=%.2f, out=%.2f)" + % ( + onInString, + boneAngle / math.pi, + bone.inChord.getAngleXY() / math.pi, + bone.outChord.getAngleXY() / math.pi, + ) + ) return self.inOutBoneCommands(bone, boneAngle, self.toolRadius) def tboneLongEdge(self, bone): @@ -661,7 +806,7 @@ class ObjectDressup(object): elif bone.location() in self.locationBlacklist: bone.obj.BoneBlacklist.append(bone.boneId) blacklisted = True - elif hasattr(bone.obj.Base, 'BoneBlacklist'): + elif hasattr(bone.obj.Base, "BoneBlacklist"): parentConsumed = bone.boneId not in bone.obj.Base.BoneBlacklist blacklisted = parentConsumed if blacklisted: @@ -685,7 +830,10 @@ class ObjectDressup(object): return [bone.lastCommand, bone.outChord.g1Command(bone.F)] def insertBone(self, bone): - PathLog.debug(">----------------------------------- %d --------------------------------------" % bone.boneId) + PathLog.debug( + ">----------------------------------- %d --------------------------------------" + % bone.boneId + ) self.boneShapes = [] blacklisted, inaccessible = self.boneIsBlacklisted(bone) enabled = not blacklisted @@ -701,7 +849,10 @@ class ObjectDressup(object): bone.commands = commands self.shapes[bone.boneId] = self.boneShapes - PathLog.debug("<----------------------------------- %d --------------------------------------" % bone.boneId) + PathLog.debug( + "<----------------------------------- %d --------------------------------------" + % bone.boneId + ) return commands def removePathCrossing(self, commands, bone1, bone2): @@ -709,24 +860,26 @@ class ObjectDressup(object): bones = bone2.commands if True and hasattr(bone1, "outCommands") and hasattr(bone2, "inCommands"): inEdges = edgesForCommands(bone1.outCommands, bone1.tip) - outEdges = edgesForCommands(bone2.inCommands, bone2.inChord.Start) + outEdges = edgesForCommands(bone2.inCommands, bone2.inChord.Start) for i in range(len(inEdges)): e1 = inEdges[i] - for j in range(len(outEdges)-1, -1, -1): + for j in range(len(outEdges) - 1, -1, -1): e2 = outEdges[j] cutoff = DraftGeomUtils.findIntersection(e1, e2) for pt in cutoff: # debugCircle(e1.Curve.Center, e1.Curve.Radius, "bone.%d-1" % (self.boneId), (1.,0.,0.)) # debugCircle(e2.Curve.Center, e2.Curve.Radius, "bone.%d-2" % (self.boneId), (0.,1.,0.)) - if PathGeom.pointsCoincide(pt, e1.valueAt(e1.LastParameter)) or PathGeom.pointsCoincide(pt, e2.valueAt(e2.FirstParameter)): + if PathGeom.pointsCoincide( + pt, e1.valueAt(e1.LastParameter) + ) or PathGeom.pointsCoincide(pt, e2.valueAt(e2.FirstParameter)): continue # debugMarker(pt, "it", (0.0, 1.0, 1.0)) # 1. remove all redundant commands - commands = commands[:-(len(inEdges) - i)] + commands = commands[: -(len(inEdges) - i)] # 2., correct where c1 ends c1 = bone1.outCommands[i] c1Params = c1.Parameters - c1Params.update({'X': pt.x, 'Y': pt.y, 'Z': pt.z}) + c1Params.update({"X": pt.x, "Y": pt.y, "Z": pt.z}) c1 = Path.Command(c1.Name, c1Params) commands.append(c1) # 3. change where c2 starts, this depends on the command itself @@ -735,10 +888,12 @@ class ObjectDressup(object): center = e2.Curve.Center offset = center - pt c2Params = c2.Parameters - c2Params.update({'I': offset.x, 'J': offset.y, 'K': offset.z}) + c2Params.update( + {"I": offset.x, "J": offset.y, "K": offset.z} + ) c2 = Path.Command(c2.Name, c2Params) bones = [c2] - bones.extend(bone2.commands[j+1:]) + bones.extend(bone2.commands[j + 1 :]) else: bones = bone2.commands[j:] # there can only be the one ... @@ -758,11 +913,13 @@ class ObjectDressup(object): self.setup(obj, False) - commands = [] # the dressed commands - lastChord = Chord() # the last chord - lastCommand = None # the command that generated the last chord - lastBone = None # track last bone for optimizations - oddsAndEnds = [] # track chords that are connected to plunges - in case they form a loop + commands = [] # the dressed commands + lastChord = Chord() # the last chord + lastCommand = None # the command that generated the last chord + lastBone = None # track last bone for optimizations + oddsAndEnds = ( + [] + ) # track chords that are connected to plunges - in case they form a loop boneId = 1 self.bones = [] @@ -782,32 +939,60 @@ class ObjectDressup(object): thisChord = lastChord.moveToParameters(thisCommand.Parameters) thisIsACandidate = self.canAttachDogbone(thisCommand, thisChord) - if thisIsACandidate and lastCommand and self.shouldInsertDogbone(obj, lastChord, thisChord): + if ( + thisIsACandidate + and lastCommand + and self.shouldInsertDogbone(obj, lastChord, thisChord) + ): PathLog.info(" Found bone corner: {}".format(lastChord.End)) - bone = Bone(boneId, obj, lastCommand, lastChord, thisChord, Smooth.InAndOut, thisCommand.Parameters.get('F')) + bone = Bone( + boneId, + obj, + lastCommand, + lastChord, + thisChord, + Smooth.InAndOut, + thisCommand.Parameters.get("F"), + ) bones = self.insertBone(bone) boneId += 1 if lastBone: PathLog.info(" removing potential path crossing") # debugMarker(thisChord.Start, "it", (1.0, 0.0, 1.0)) - commands, bones = self.removePathCrossing(commands, lastBone, bone) + commands, bones = self.removePathCrossing( + commands, lastBone, bone + ) commands.extend(bones[:-1]) lastCommand = bones[-1] lastBone = bone elif lastCommand and thisChord.isAPlungeMove(): PathLog.info(" Looking for connection in odds and ends") haveNewLastCommand = False - for chord in (chord for chord in oddsAndEnds if lastChord.connectsTo(chord)): + for chord in ( + chord for chord in oddsAndEnds if lastChord.connectsTo(chord) + ): if self.shouldInsertDogbone(obj, lastChord, chord): PathLog.info(" and there is one") - PathLog.debug(" odd/end={} last={}".format(chord, lastChord)) - bone = Bone(boneId, obj, lastCommand, lastChord, chord, Smooth.In, lastCommand.Parameters.get('F')) + PathLog.debug( + " odd/end={} last={}".format(chord, lastChord) + ) + bone = Bone( + boneId, + obj, + lastCommand, + lastChord, + chord, + Smooth.In, + lastCommand.Parameters.get("F"), + ) bones = self.insertBone(bone) boneId += 1 if lastBone: PathLog.info(" removing potential path crossing") # debugMarker(chord.Start, "it", (0.0, 1.0, 1.0)) - commands, bones = self.removePathCrossing(commands, lastBone, bone) + commands, bones = self.removePathCrossing( + commands, lastBone, bone + ) commands.extend(bones[:-1]) lastCommand = bones[-1] haveNewLastCommand = True @@ -839,7 +1024,7 @@ class ObjectDressup(object): lastChord = thisChord else: - if thisCommand.Name[0] != '(': + if thisCommand.Name[0] != "(": PathLog.info(" Clean slate") if lastCommand: commands.append(lastCommand) @@ -861,12 +1046,12 @@ class ObjectDressup(object): PathLog.info("Default side = right") # otherwise dogbones are opposite of the base path's side side = Side.Right - if hasattr(obj.Base, 'Side') and obj.Base.Side == 'Inside': + if hasattr(obj.Base, "Side") and obj.Base.Side == "Inside": PathLog.info("inside -> side = left") side = Side.Left else: PathLog.info("not inside -> side stays right") - if hasattr(obj.Base, 'Direction') and obj.Base.Direction == 'CCW': + if hasattr(obj.Base, "Direction") and obj.Base.Direction == "CCW": PathLog.info("CCW -> switch sides") side = Side.oppositeOf(side) else: @@ -890,7 +1075,7 @@ class ObjectDressup(object): def boneStateList(self, obj): state = {} # If the receiver was loaded from file, then it never generated the bone list. - if not hasattr(self, 'bones'): + if not hasattr(self, "bones"): self.execute(obj) for (nr, loc, enabled, inaccessible) in self.bones: item = state.get((loc[0], loc[1])) @@ -901,14 +1086,14 @@ class ObjectDressup(object): state[(loc[0], loc[1])] = (enabled, inaccessible, [nr], [loc[2]]) return state -class Marker(object): +class Marker(object): def __init__(self, pt, r, h): if PathGeom.isRoughly(h, 0): h = 0.1 self.pt = pt - self.r = r - self.h = h + self.r = r + self.h = h self.sep = coin.SoSeparator() self.pos = coin.SoTranslation() self.pos.translation = (pt.x, pt.y, pt.z + h / 2) @@ -943,8 +1128,8 @@ class Marker(object): def color(self, id): if id == 1: - return coin.SbColor(.9, .9, .5) - return coin.SbColor(.9, .5, .9) + return coin.SbColor(0.9, 0.9, 0.5) + return coin.SbColor(0.9, 0.5, 0.9) class TaskPanel(object): @@ -957,7 +1142,9 @@ class TaskPanel(object): self.obj = obj self.form = FreeCADGui.PySideUic.loadUi(":/panels/DogboneEdit.ui") self.s = None - FreeCAD.ActiveDocument.openTransaction(translate("Path_DressupDogbone", "Edit Dogbone Dress-up")) + FreeCAD.ActiveDocument.openTransaction( + translate("Path_DressupDogbone", "Edit Dogbone Dress-up") + ) self.height = 10 self.markers = [] @@ -999,8 +1186,10 @@ class TaskPanel(object): def updateBoneList(self): itemList = [] - for loc, (enabled, inaccessible, ids, zs) in PathUtil.keyValueIter(self.obj.Proxy.boneStateList(self.obj)): - lbl = '(%.2f, %.2f): %s' % (loc[0], loc[1], ','.join(str(id) for id in ids)) + for loc, (enabled, inaccessible, ids, zs) in PathUtil.keyValueIter( + self.obj.Proxy.boneStateList(self.obj) + ): + lbl = "(%.2f, %.2f): %s" % (loc[0], loc[1], ",".join(str(id) for id in ids)) item = QtGui.QListWidgetItem(lbl) if enabled: item.setCheckState(QtCore.Qt.CheckState.Checked) @@ -1008,7 +1197,10 @@ class TaskPanel(object): item.setCheckState(QtCore.Qt.CheckState.Unchecked) flags = QtCore.Qt.ItemFlag.ItemIsSelectable if not inaccessible: - flags |= QtCore.Qt.ItemFlag.ItemIsEnabled | QtCore.Qt.ItemFlag.ItemIsUserCheckable + flags |= ( + QtCore.Qt.ItemFlag.ItemIsEnabled + | QtCore.Qt.ItemFlag.ItemIsUserCheckable + ) item.setFlags(flags) item.setData(self.DataIds, ids) item.setData(self.DataKey, ids[0]) @@ -1020,7 +1212,13 @@ class TaskPanel(object): self.form.bones.addItem(item) loc = item.data(self.DataLoc) r = max(self.obj.Proxy.length, 1) - markers.append(Marker(FreeCAD.Vector(loc[0], loc[1], min(zs)), r, max(1, max(zs) - min(zs)))) + markers.append( + Marker( + FreeCAD.Vector(loc[0], loc[1], min(zs)), + r, + max(1, max(zs) - min(zs)), + ) + ) for m in self.markers: self.viewProvider.switch.removeChild(m.sep) for m in markers: @@ -1035,9 +1233,9 @@ class TaskPanel(object): if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG: for obj in FreeCAD.ActiveDocument.Objects: - if obj.Name.startswith('Shape'): + if obj.Name.startswith("Shape"): FreeCAD.ActiveDocument.removeObject(obj.Name) - PathLog.info('object name %s' % self.obj.Name) + PathLog.info("object name %s" % self.obj.Name) if hasattr(self.obj.Proxy, "shapes"): PathLog.info("showing shapes attribute") for shapes in self.obj.Proxy.shapes.values(): @@ -1091,23 +1289,27 @@ class TaskPanel(object): for i, m in enumerate(self.markers): m.setSelected(i == index) + class SelObserver(object): def __init__(self): import PathScripts.PathSelection as PST + PST.eselect() def __del__(self): import PathScripts.PathSelection as PST + PST.clear() def addSelection(self, doc, obj, sub, pnt): # pylint: disable=unused-argument - FreeCADGui.doCommand('Gui.Selection.addSelection(FreeCAD.ActiveDocument.' + obj + ')') + FreeCADGui.doCommand( + "Gui.Selection.addSelection(FreeCAD.ActiveDocument." + obj + ")" + ) FreeCADGui.updateGui() class ViewProviderDressup(object): - def __init__(self, vobj): self.vobj = vobj self.obj = None @@ -1148,7 +1350,7 @@ class ViewProviderDressup(object): return None def onDelete(self, arg1=None, arg2=None): - '''this makes sure that the base operation is added back to the project and visible''' + """this makes sure that the base operation is added back to the project and visible""" # pylint: disable=unused-argument if arg1.Object and arg1.Object.Base: FreeCADGui.ActiveDocument.getObject(arg1.Object.Base.Name).Visibility = True @@ -1159,11 +1361,11 @@ class ViewProviderDressup(object): return True -def Create(base, name='DogboneDressup'): - ''' +def Create(base, name="DogboneDressup"): + """ Create(obj, name='DogboneDressup') ... dresses the given PathProfile/PathContour object with dogbones. - ''' - obj = FreeCAD.ActiveDocument.addObject('Path::FeaturePython', name) + """ + obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) dbo = ObjectDressup(obj, base) job = PathUtils.findParentJob(base) job.Proxy.addOperation(obj, base) @@ -1180,9 +1382,16 @@ class CommandDressupDogbone(object): # pylint: disable=no-init def GetResources(self): - return {'Pixmap': 'Path_Dressup', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_DressupDogbone", "Dogbone Dress-up"), - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_DressupDogbone", "Creates a Dogbone Dress-up object from a selected path")} + return { + "Pixmap": "Path_Dressup", + "MenuText": QtCore.QT_TRANSLATE_NOOP( + "Path_DressupDogbone", "Dogbone Dress-up" + ), + "ToolTip": QtCore.QT_TRANSLATE_NOOP( + "Path_DressupDogbone", + "Creates a Dogbone Dress-up object from a selected path", + ), + } def IsActive(self): if FreeCAD.ActiveDocument is not None: @@ -1196,17 +1405,27 @@ class CommandDressupDogbone(object): # check that the selection contains exactly what we want selection = FreeCADGui.Selection.getSelection() if len(selection) != 1: - FreeCAD.Console.PrintError(translate("Path_DressupDogbone", "Please select one path object")+"\n") + FreeCAD.Console.PrintError( + translate("Path_DressupDogbone", "Please select one path object") + "\n" + ) return baseObject = selection[0] if not baseObject.isDerivedFrom("Path::Feature"): - FreeCAD.Console.PrintError(translate("Path_DressupDogbone", "The selected object is not a path")+"\n") + FreeCAD.Console.PrintError( + translate("Path_DressupDogbone", "The selected object is not a path") + + "\n" + ) return # everything ok! - FreeCAD.ActiveDocument.openTransaction(translate("Path_DressupDogbone", "Create Dogbone Dress-up")) - FreeCADGui.addModule('PathScripts.PathDressupDogbone') - FreeCADGui.doCommand("PathScripts.PathDressupDogbone.Create(FreeCAD.ActiveDocument.%s)" % baseObject.Name) + FreeCAD.ActiveDocument.openTransaction( + translate("Path_DressupDogbone", "Create Dogbone Dress-up") + ) + FreeCADGui.addModule("PathScripts.PathDressupDogbone") + FreeCADGui.doCommand( + "PathScripts.PathDressupDogbone.Create(FreeCAD.ActiveDocument.%s)" + % baseObject.Name + ) FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() @@ -1215,6 +1434,7 @@ if FreeCAD.GuiUp: import FreeCADGui from PySide import QtGui from pivy import coin - FreeCADGui.addCommand('Path_DressupDogbone', CommandDressupDogbone()) + + FreeCADGui.addCommand("Path_DressupDogbone", CommandDressupDogbone()) FreeCAD.Console.PrintLog("Loading DressupDogbone... done\n") diff --git a/src/Mod/Path/PathScripts/PathDressupHoldingTags.py b/src/Mod/Path/PathScripts/PathDressupHoldingTags.py index 3f36d43d05..66e5061183 100644 --- a/src/Mod/Path/PathScripts/PathDressupHoldingTags.py +++ b/src/Mod/Path/PathScripts/PathDressupHoldingTags.py @@ -36,10 +36,11 @@ from PySide import QtCore # lazily loaded modules from lazy_loader.lazy_loader import LazyLoader -Part = LazyLoader('Part', globals(), 'Part') + +Part = LazyLoader("Part", globals(), "Part") PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -#PathLog.trackModule() +# PathLog.trackModule() failures = [] @@ -53,10 +54,28 @@ def debugEdge(edge, prefix, force=False): pf = edge.valueAt(edge.FirstParameter) pl = edge.valueAt(edge.LastParameter) if type(edge.Curve) == Part.Line or type(edge.Curve) == Part.LineSegment: - print("%s %s((%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" % (prefix, type(edge.Curve), pf.x, pf.y, pf.z, pl.x, pl.y, pl.z)) + print( + "%s %s((%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" + % (prefix, type(edge.Curve), pf.x, pf.y, pf.z, pl.x, pl.y, pl.z) + ) else: - pm = edge.valueAt((edge.FirstParameter+edge.LastParameter)/2) - print("%s %s((%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" % (prefix, type(edge.Curve), pf.x, pf.y, pf.z, pm.x, pm.y, pm.z, pl.x, pl.y, pl.z)) + pm = edge.valueAt((edge.FirstParameter + edge.LastParameter) / 2) + print( + "%s %s((%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" + % ( + prefix, + type(edge.Curve), + pf.x, + pf.y, + pf.z, + pm.x, + pm.y, + pm.z, + pl.x, + pl.y, + pl.z, + ) + ) def debugMarker(vector, label, color=None, radius=0.5): @@ -64,7 +83,9 @@ def debugMarker(vector, label, color=None, radius=0.5): obj = FreeCAD.ActiveDocument.addObject("Part::Sphere", label) obj.Label = label obj.Radius = radius - obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0)) + obj.Placement = FreeCAD.Placement( + vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0) + ) if color: obj.ViewObject.ShapeColor = color @@ -75,7 +96,9 @@ def debugCylinder(vector, r, height, label, color=None): obj.Label = label obj.Radius = r obj.Height = height - obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0)) + obj.Placement = FreeCAD.Placement( + vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0) + ) obj.ViewObject.Transparency = 90 if color: obj.ViewObject.ShapeColor = color @@ -88,7 +111,9 @@ def debugCone(vector, r1, r2, height, label, color=None): obj.Radius1 = r1 obj.Radius2 = r2 obj.Height = height - obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0)) + obj.Placement = FreeCAD.Placement( + vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0) + ) obj.ViewObject.Transparency = 90 if color: obj.ViewObject.ShapeColor = color @@ -96,7 +121,10 @@ def debugCone(vector, r1, r2, height, label, color=None): class Tag: def __init__(self, nr, x, y, width, height, angle, radius, enabled=True): - PathLog.track("%.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d" % (x, y, width, height, angle, radius, enabled)) + PathLog.track( + "%.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d" + % (x, y, width, height, angle, radius, enabled) + ) self.nr = nr self.x = x self.y = y @@ -104,7 +132,11 @@ class Tag: self.height = math.fabs(height) self.actualHeight = self.height self.angle = math.fabs(angle) - self.radius = radius if FreeCAD.Units.Quantity == type(radius) else FreeCAD.Units.Quantity(radius, FreeCAD.Units.Length) + self.radius = ( + radius + if FreeCAD.Units.Quantity == type(radius) + else FreeCAD.Units.Quantity(radius, FreeCAD.Units.Length) + ) self.enabled = enabled self.isSquare = False @@ -151,7 +183,7 @@ class Tag: # with top r2 = r1 - dr s = height / math.sin(rad) - radius = min(r2, s) * math.tan((math.pi - rad)/2) * 0.95 + radius = min(r2, s) * math.tan((math.pi - rad) / 2) * 0.95 else: # triangular r2 = 0 @@ -164,7 +196,9 @@ class Tag: # degenerated case - no tag PathLog.debug("Part.makeSphere(%f / 10000)" % (r1)) self.solid = Part.makeSphere(r1 / 10000) - if not PathGeom.isRoughly(0, R): # testing is easier if the solid is not rotated + if not PathGeom.isRoughly( + 0, R + ): # testing is easier if the solid is not rotated angle = -PathGeom.getAngle(self.originAt(0)) * 180 / math.pi PathLog.debug("solid.rotate(%f)" % angle) self.solid.rotate(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), angle) @@ -178,14 +212,27 @@ class Tag: self.solid = self.solid.makeFillet(radius, [self.solid.Edges[0]]) def filterIntersections(self, pts, face): - if type(face.Surface) == Part.Cone or type(face.Surface) == Part.Cylinder or type(face.Surface) == Part.Toroid: + if ( + type(face.Surface) == Part.Cone + or type(face.Surface) == Part.Cylinder + or type(face.Surface) == Part.Toroid + ): PathLog.track("it's a cone/cylinder, checking z") - return list([pt for pt in pts if pt.z >= self.bottom() and pt.z <= self.top()]) + return list( + [pt for pt in pts if pt.z >= self.bottom() and pt.z <= self.top()] + ) if type(face.Surface) == Part.Plane: PathLog.track("it's a plane, checking R") c = face.Edges[0].Curve - if (type(c) == Part.Circle): - return list([pt for pt in pts if (pt - c.Center).Length <= c.Radius or PathGeom.isRoughly((pt - c.Center).Length, c.Radius)]) + if type(c) == Part.Circle: + return list( + [ + pt + for pt in pts + if (pt - c.Center).Length <= c.Radius + or PathGeom.isRoughly((pt - c.Center).Length, c.Radius) + ] + ) PathLog.error("==== we got a %s" % face.Surface) def isPointOnEdge(self, pt, edge): @@ -194,7 +241,9 @@ class Tag: return True if edge.LastParameter <= param <= edge.FirstParameter: return True - if PathGeom.isRoughly(edge.FirstParameter, param) or PathGeom.isRoughly(edge.LastParameter, param): + if PathGeom.isRoughly(edge.FirstParameter, param) or PathGeom.isRoughly( + edge.LastParameter, param + ): return True # print("-------- X %.2f <= %.2f <=%.2f (%.2f, %.2f, %.2f) %.2f:%.2f" % (edge.FirstParameter, param, edge.LastParameter, pt.x, pt.y, pt.z, edge.Curve.parameter(edge.valueAt(edge.FirstParameter)), edge.Curve.parameter(edge.valueAt(edge.LastParameter)))) # p1 = edge.Vertexes[0] @@ -209,7 +258,11 @@ class Tag: vertexes = edge.common(solid).Vertexes if vertexes: pt = sorted(vertexes, key=lambda v: (v.Point - refPt).Length)[0].Point - debugEdge(edge, "intersects (%.2f, %.2f, %.2f) -> (%.2f, %.2f, %.2f)" % (refPt.x, refPt.y, refPt.z, pt.x, pt.y, pt.z)) + debugEdge( + edge, + "intersects (%.2f, %.2f, %.2f) -> (%.2f, %.2f, %.2f)" + % (refPt.x, refPt.y, refPt.z, pt.x, pt.y, pt.z), + ) return pt return None @@ -220,10 +273,12 @@ class Tag: if self.enabled: zFirst = edge.valueAt(edge.FirstParameter).z - zLast = edge.valueAt(edge.LastParameter).z + zLast = edge.valueAt(edge.LastParameter).z zMax = self.top() if isDefinitelySmaller(zFirst, zMax) or isDefinitelySmaller(zLast, zMax): - return self.nextIntersectionClosestTo(edge, self.solid, edge.valueAt(param)) + return self.nextIntersectionClosestTo( + edge, self.solid, edge.valueAt(param) + ) return None def bbEdges(self): @@ -240,7 +295,7 @@ class Tag: class MapWireToTag: def __init__(self, edge, tag, i, segm, maxZ, hSpeed, vSpeed): - debugEdge(edge, 'MapWireToTag(%.2f, %.2f, %.2f)' % (i.x, i.y, i.z)) + debugEdge(edge, "MapWireToTag(%.2f, %.2f, %.2f)" % (i.x, i.y, i.z)) self.tag = tag self.segm = segm self.maxZ = maxZ @@ -249,22 +304,28 @@ class MapWireToTag: if PathGeom.pointsCoincide(edge.valueAt(edge.FirstParameter), i): tail = edge self.commands = [] - debugEdge(tail, '.........=') + debugEdge(tail, ".........=") elif PathGeom.pointsCoincide(edge.valueAt(edge.LastParameter), i): - debugEdge(edge, '++++++++ .') - self.commands = PathGeom.cmdsForEdge(edge, segm=segm, hSpeed = self.hSpeed, vSpeed = self.vSpeed) + debugEdge(edge, "++++++++ .") + self.commands = PathGeom.cmdsForEdge( + edge, segm=segm, hSpeed=self.hSpeed, vSpeed=self.vSpeed + ) tail = None else: e, tail = PathGeom.splitEdgeAt(edge, i) - debugEdge(e, '++++++++ .') - self.commands = PathGeom.cmdsForEdge(e, segm=segm, hSpeed = self.hSpeed, vSpeed = self.vSpeed) - debugEdge(tail, '.........-') + debugEdge(e, "++++++++ .") + self.commands = PathGeom.cmdsForEdge( + e, segm=segm, hSpeed=self.hSpeed, vSpeed=self.vSpeed + ) + debugEdge(tail, ".........-") self.initialEdge = edge self.tail = tail self.edges = [] self.entry = i if tail: - PathLog.debug("MapWireToTag(%s - %s)" % (i, tail.valueAt(tail.FirstParameter))) + PathLog.debug( + "MapWireToTag(%s - %s)" % (i, tail.valueAt(tail.FirstParameter)) + ) else: PathLog.debug("MapWireToTag(%s - )" % i) self.complete = False @@ -283,7 +344,7 @@ class MapWireToTag: self.realExit = None def addEdge(self, edge): - debugEdge(edge, '..........') + debugEdge(edge, "..........") self.edges.append(edge) def needToFlipEdge(self, edge, p): @@ -311,7 +372,7 @@ class MapWireToTag: if not edges: return edges for e in edges: - debugEdge(e, ' ') + debugEdge(e, " ") PathLog.debug(":") self.edgesCleanup = [copy.copy(edges)] @@ -325,13 +386,19 @@ class MapWireToTag: p2 = e.valueAt(e.LastParameter) self.edgePoints.append(p1) self.edgePoints.append(p2) - if self.tag.solid.isInside(p1, PathGeom.Tolerance, False) or self.tag.solid.isInside(p2, PathGeom.Tolerance, False): + if self.tag.solid.isInside( + p1, PathGeom.Tolerance, False + ) or self.tag.solid.isInside(p2, PathGeom.Tolerance, False): edges.remove(e) - debugEdge(e, '......... X0', False) + debugEdge(e, "......... X0", False) else: - if PathGeom.pointsCoincide(p1, self.entry) or PathGeom.pointsCoincide(p2, self.entry): + if PathGeom.pointsCoincide(p1, self.entry) or PathGeom.pointsCoincide( + p2, self.entry + ): self.entryEdges.append(e) - if PathGeom.pointsCoincide(p1, self.exit) or PathGeom.pointsCoincide(p2, self.exit): + if PathGeom.pointsCoincide(p1, self.exit) or PathGeom.pointsCoincide( + p2, self.exit + ): self.exitEdges.append(e) self.edgesCleanup.append(copy.copy(edges)) @@ -339,15 +406,23 @@ class MapWireToTag: # we need to add in the missing segment and collect the new entry/exit edges. if not self.entryEdges: PathLog.debug("fill entryEdges ...") - self.realEntry = sorted(self.edgePoints, key=lambda p: (p - self.entry).Length)[0] - self.entryEdges = list([e for e in edges if PathGeom.edgeConnectsTo(e, self.realEntry)]) + self.realEntry = sorted( + self.edgePoints, key=lambda p: (p - self.entry).Length + )[0] + self.entryEdges = list( + [e for e in edges if PathGeom.edgeConnectsTo(e, self.realEntry)] + ) edges.append(Part.Edge(Part.LineSegment(self.entry, self.realEntry))) else: self.realEntry = None if not self.exitEdges: PathLog.debug("fill exitEdges ...") - self.realExit = sorted(self.edgePoints, key=lambda p: (p - self.exit).Length)[0] - self.exitEdges = list([e for e in edges if PathGeom.edgeConnectsTo(e, self.realExit)]) + self.realExit = sorted( + self.edgePoints, key=lambda p: (p - self.exit).Length + )[0] + self.exitEdges = list( + [e for e in edges if PathGeom.edgeConnectsTo(e, self.realExit)] + ) edges.append(Part.Edge(Part.LineSegment(self.realExit, self.exit))) else: self.realExit = None @@ -355,31 +430,41 @@ class MapWireToTag: # if there are 2 edges attached to entry/exit, throw away the one that is "lower" if len(self.entryEdges) > 1: - debugEdge(self.entryEdges[0], ' entry[0]', False) - debugEdge(self.entryEdges[1], ' entry[1]', False) + debugEdge(self.entryEdges[0], " entry[0]", False) + debugEdge(self.entryEdges[1], " entry[1]", False) if self.entryEdges[0].BoundBox.ZMax < self.entryEdges[1].BoundBox.ZMax: edges.remove(self.entryEdges[0]) - debugEdge(e, '......... X1', False) + debugEdge(e, "......... X1", False) else: edges.remove(self.entryEdges[1]) - debugEdge(e, '......... X2', False) + debugEdge(e, "......... X2", False) if len(self.exitEdges) > 1: - debugEdge(self.exitEdges[0], ' exit[0]', False) - debugEdge(self.exitEdges[1], ' exit[1]', False) + debugEdge(self.exitEdges[0], " exit[0]", False) + debugEdge(self.exitEdges[1], " exit[1]", False) if self.exitEdges[0].BoundBox.ZMax < self.exitEdges[1].BoundBox.ZMax: if self.exitEdges[0] in edges: edges.remove(self.exitEdges[0]) - debugEdge(e, '......... X3', False) + debugEdge(e, "......... X3", False) else: if self.exitEdges[1] in edges: edges.remove(self.exitEdges[1]) - debugEdge(e, '......... X4', False) + debugEdge(e, "......... X4", False) self.edgesCleanup.append(copy.copy(edges)) return edges def orderAndFlipEdges(self, inputEdges): - PathLog.track("entry(%.2f, %.2f, %.2f), exit(%.2f, %.2f, %.2f)" % (self.entry.x, self.entry.y, self.entry.z, self.exit.x, self.exit.y, self.exit.z)) + PathLog.track( + "entry(%.2f, %.2f, %.2f), exit(%.2f, %.2f, %.2f)" + % ( + self.entry.x, + self.entry.y, + self.entry.z, + self.exit.x, + self.exit.y, + self.exit.z, + ) + ) self.edgesOrder = [] outputEdges = [] p0 = self.entry @@ -406,7 +491,9 @@ class MapWireToTag: cnt = 0 for p in reversed(e.discretize(Deflection=0.01)): if not p0 is None: - outputEdges.append((Part.Edge(Part.LineSegment(p0, p)), True)) + outputEdges.append( + (Part.Edge(Part.LineSegment(p0, p)), True) + ) cnt = cnt + 1 p0 = p PathLog.info("replaced edge with %d straight segments" % cnt) @@ -421,18 +508,21 @@ class MapWireToTag: if lastP == p0: self.edgesOrder.append(outputEdges) self.edgesOrder.append(edges) - PathLog.debug('input edges:') + PathLog.debug("input edges:") for e in inputEdges: - debugEdge(e, ' ', False) - PathLog.debug('ordered edges:') + debugEdge(e, " ", False) + PathLog.debug("ordered edges:") for e, flip in outputEdges: - debugEdge(e, ' %c ' % ('<' if flip else '>'), False) - PathLog.debug('remaining edges:') + debugEdge(e, " %c " % ("<" if flip else ">"), False) + PathLog.debug("remaining edges:") for e in edges: - debugEdge(e, ' ', False) + debugEdge(e, " ", False) raise ValueError("No connection to %s" % (p0)) elif lastP: - PathLog.debug("xxxxxx (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)" % (p0.x, p0.y, p0.z, lastP.x, lastP.y, lastP.z)) + PathLog.debug( + "xxxxxx (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)" + % (p0.x, p0.y, p0.z, lastP.x, lastP.y, lastP.z) + ) else: PathLog.debug("xxxxxx (%.2f, %.2f, %.2f) -" % (p0.x, p0.y, p0.z)) lastP = p0 @@ -449,15 +539,24 @@ class MapWireToTag: wire = Part.Wire(self.initialEdge) else: edge = self.edges[0] - if PathGeom.pointsCoincide(edge.valueAt(edge.FirstParameter), self.finalEdge.valueAt(self.finalEdge.FirstParameter)): + if PathGeom.pointsCoincide( + edge.valueAt(edge.FirstParameter), + self.finalEdge.valueAt(self.finalEdge.FirstParameter), + ): wire = Part.Wire(self.finalEdge) - elif hasattr(self, 'initialEdge') and PathGeom.pointsCoincide(edge.valueAt(edge.FirstParameter), self.initialEdge.valueAt(self.initialEdge.FirstParameter)): + elif hasattr(self, "initialEdge") and PathGeom.pointsCoincide( + edge.valueAt(edge.FirstParameter), + self.initialEdge.valueAt(self.initialEdge.FirstParameter), + ): wire = Part.Wire(self.initialEdge) else: wire = Part.Wire(edge) for edge in self.edges[1:]: - if PathGeom.pointsCoincide(edge.valueAt(edge.FirstParameter), self.finalEdge.valueAt(self.finalEdge.FirstParameter)): + if PathGeom.pointsCoincide( + edge.valueAt(edge.FirstParameter), + self.finalEdge.valueAt(self.finalEdge.FirstParameter), + ): wire.add(self.finalEdge) else: wire.add(edge) @@ -469,37 +568,61 @@ class MapWireToTag: return shell def commandsForEdges(self): - global failures # pylint: disable=global-statement + global failures # pylint: disable=global-statement if self.edges: try: shape = self.shell().common(self.tag.solid) commands = [] rapid = None for e, flip in self.orderAndFlipEdges(self.cleanupEdges(shape.Edges)): - debugEdge(e, '++++++++ %s' % ('<' if flip else '>'), False) + debugEdge(e, "++++++++ %s" % ("<" if flip else ">"), False) p1 = e.valueAt(e.FirstParameter) p2 = e.valueAt(e.LastParameter) - if self.tag.isSquare and (PathGeom.isRoughly(p1.z, self.maxZ) or p1.z > self.maxZ) and (PathGeom.isRoughly(p2.z, self.maxZ) or p2.z > self.maxZ): + if ( + self.tag.isSquare + and (PathGeom.isRoughly(p1.z, self.maxZ) or p1.z > self.maxZ) + and (PathGeom.isRoughly(p2.z, self.maxZ) or p2.z > self.maxZ) + ): rapid = p1 if flip else p2 else: if rapid: - commands.append(Path.Command('G0', {'X': rapid.x, 'Y': rapid.y, 'Z': rapid.z})) + commands.append( + Path.Command( + "G0", {"X": rapid.x, "Y": rapid.y, "Z": rapid.z} + ) + ) rapid = None - commands.extend(PathGeom.cmdsForEdge(e, False, False, self.segm, hSpeed = self.hSpeed, vSpeed = self.vSpeed)) + commands.extend( + PathGeom.cmdsForEdge( + e, + False, + False, + self.segm, + hSpeed=self.hSpeed, + vSpeed=self.vSpeed, + ) + ) if rapid: - commands.append(Path.Command('G0', {'X': rapid.x, 'Y': rapid.y, 'Z': rapid.z})) + commands.append( + Path.Command("G0", {"X": rapid.x, "Y": rapid.y, "Z": rapid.z}) + ) # rapid = None # commented out per LGTM suggestion return commands - except Exception as e: # pylint: disable=broad-except - PathLog.error("Exception during processing tag @(%.2f, %.2f) (%s) - disabling the tag" % (self.tag.x, self.tag.y, e.args[0])) - #if sys.version_info.major < 3: + except Exception as e: # pylint: disable=broad-except + PathLog.error( + "Exception during processing tag @(%.2f, %.2f) (%s) - disabling the tag" + % (self.tag.x, self.tag.y, e.args[0]) + ) + # if sys.version_info.major < 3: # traceback.print_exc(e) - #else: + # else: # traceback.print_exc() self.tag.enabled = False commands = [] for e in self.edges: - commands.extend(PathGeom.cmdsForEdge(e, hSpeed = self.hSpeed, vSpeed = self.vSpeed)) + commands.extend( + PathGeom.cmdsForEdge(e, hSpeed=self.hSpeed, vSpeed=self.vSpeed) + ) failures.append(self) return commands return [] @@ -507,22 +630,24 @@ class MapWireToTag: def add(self, edge): self.tail = None self.finalEdge = edge - if self.tag.solid.isInside(edge.valueAt(edge.LastParameter), PathGeom.Tolerance, True): - PathLog.track('solid.isInside') + if self.tag.solid.isInside( + edge.valueAt(edge.LastParameter), PathGeom.Tolerance, True + ): + PathLog.track("solid.isInside") self.addEdge(edge) else: i = self.tag.intersects(edge, edge.LastParameter) if not i: self.offendingEdge = edge - debugEdge(edge, 'offending Edge:', False) + debugEdge(edge, "offending Edge:", False) o = self.tag.originAt(self.tag.z) - PathLog.debug('originAt: (%.2f, %.2f, %.2f)' % (o.x, o.y, o.z)) + PathLog.debug("originAt: (%.2f, %.2f, %.2f)" % (o.x, o.y, o.z)) i = edge.valueAt(edge.FirstParameter) if PathGeom.pointsCoincide(i, edge.valueAt(edge.FirstParameter)): - PathLog.track('tail') + PathLog.track("tail") self.tail = edge else: - PathLog.track('split') + PathLog.track("split") e, tail = PathGeom.splitEdgeAt(edge, i) self.addEdge(e) self.tail = tail @@ -545,7 +670,14 @@ class _RapidEdges: for r in self.rapid: r0 = r.Vertexes[0] r1 = r.Vertexes[1] - if PathGeom.isRoughly(r0.X, v0.X) and PathGeom.isRoughly(r0.Y, v0.Y) and PathGeom.isRoughly(r0.Z, v0.Z) and PathGeom.isRoughly(r1.X, v1.X) and PathGeom.isRoughly(r1.Y, v1.Y) and PathGeom.isRoughly(r1.Z, v1.Z): + if ( + PathGeom.isRoughly(r0.X, v0.X) + and PathGeom.isRoughly(r0.Y, v0.Y) + and PathGeom.isRoughly(r0.Z, v0.Z) + and PathGeom.isRoughly(r1.X, v1.X) + and PathGeom.isRoughly(r1.Y, v1.Y) + and PathGeom.isRoughly(r1.Z, v1.Z) + ): return True return False @@ -566,16 +698,21 @@ class PathData: (minZ, maxZ) = self.findZLimits(edges) self.minZ = minZ self.maxZ = maxZ - bottom = [e for e in edges if PathGeom.isRoughly(e.Vertexes[0].Point.z, minZ) and PathGeom.isRoughly(e.Vertexes[1].Point.z, minZ)] + bottom = [ + e + for e in edges + if PathGeom.isRoughly(e.Vertexes[0].Point.z, minZ) + and PathGeom.isRoughly(e.Vertexes[1].Point.z, minZ) + ] self.bottomEdges = bottom try: wire = Part.Wire(bottom) if wire.isClosed(): return wire - except Exception: # pylint: disable=broad-except - #if sys.version_info.major < 3: + except Exception: # pylint: disable=broad-except + # if sys.version_info.major < 3: # traceback.print_exc(e) - #else: + # else: # traceback.print_exc() return None @@ -600,7 +737,9 @@ class PathData: edges = sorted(self.bottomEdges, key=lambda e: e.Length) return (edges[0], edges[-1]) - def generateTags(self, obj, count, width=None, height=None, angle=None, radius=None, spacing=None): + def generateTags( + self, obj, count, width=None, height=None, angle=None, radius=None, spacing=None + ): # pylint: disable=unused-argument PathLog.track(count, width, height, angle, spacing) # for e in self.baseWire.Edges: @@ -611,9 +750,9 @@ class PathData: else: tagDistance = self.baseWire.Length / (count if count else 4) - W = width if width else self.defaultTagWidth() + W = width if width else self.defaultTagWidth() H = height if height else self.defaultTagHeight() - A = angle if angle else self.defaultTagAngle() + A = angle if angle else self.defaultTagAngle() R = radius if radius else self.defaultTagRadius() # start assigning tags on the longest segment @@ -621,7 +760,7 @@ class PathData: startIndex = 0 for i in range(0, len(self.baseWire.Edges)): edge = self.baseWire.Edges[i] - PathLog.debug(' %d: %.2f' % (i, edge.Length)) + PathLog.debug(" %d: %.2f" % (i, edge.Length)) if PathGeom.isRoughly(edge.Length, longestEdge.Length): startIndex = i break @@ -634,10 +773,23 @@ class PathData: lastTagLength = (startEdge.Length + (startCount - 1) * tagDistance) / 2 currentLength = startEdge.Length - minLength = min(2. * W, longestEdge.Length) + minLength = min(2.0 * W, longestEdge.Length) - PathLog.debug("length=%.2f shortestEdge=%.2f(%.2f) longestEdge=%.2f(%.2f) minLength=%.2f" % (self.baseWire.Length, shortestEdge.Length, shortestEdge.Length/self.baseWire.Length, longestEdge.Length, longestEdge.Length / self.baseWire.Length, minLength)) - PathLog.debug(" start: index=%-2d count=%d (length=%.2f, distance=%.2f)" % (startIndex, startCount, startEdge.Length, tagDistance)) + PathLog.debug( + "length=%.2f shortestEdge=%.2f(%.2f) longestEdge=%.2f(%.2f) minLength=%.2f" + % ( + self.baseWire.Length, + shortestEdge.Length, + shortestEdge.Length / self.baseWire.Length, + longestEdge.Length, + longestEdge.Length / self.baseWire.Length, + minLength, + ) + ) + PathLog.debug( + " start: index=%-2d count=%d (length=%.2f, distance=%.2f)" + % (startIndex, startCount, startEdge.Length, tagDistance) + ) PathLog.debug(" -> lastTagLength=%.2f)" % lastTagLength) PathLog.debug(" -> currentLength=%.2f)" % currentLength) @@ -645,10 +797,14 @@ class PathData: for i in range(startIndex + 1, len(self.baseWire.Edges)): edge = self.baseWire.Edges[i] - (currentLength, lastTagLength) = self.processEdge(i, edge, currentLength, lastTagLength, tagDistance, minLength, edgeDict) + (currentLength, lastTagLength) = self.processEdge( + i, edge, currentLength, lastTagLength, tagDistance, minLength, edgeDict + ) for i in range(0, startIndex): edge = self.baseWire.Edges[i] - (currentLength, lastTagLength) = self.processEdge(i, edge, currentLength, lastTagLength, tagDistance, minLength, edgeDict) + (currentLength, lastTagLength) = self.processEdge( + i, edge, currentLength, lastTagLength, tagDistance, minLength, edgeDict + ) tags = [] @@ -660,16 +816,19 @@ class PathData: if 0 != count: distance = (edge.LastParameter - edge.FirstParameter) / count for j in range(0, count): - tag = edge.Curve.value((j+0.5) * distance) + tag = edge.Curve.value((j + 0.5) * distance) tags.append(Tag(j, tag.x, tag.y, W, H, A, R, True)) return tags def copyTags(self, obj, fromObj, width, height, angle, radius, production=True): - print("copyTags(%s, %s, %.2f, %.2f, %.2f, %.2f" % (obj.Label, fromObj.Label, width, height, angle, radius)) - W = width if width else self.defaultTagWidth() + print( + "copyTags(%s, %s, %.2f, %.2f, %.2f, %.2f" + % (obj.Label, fromObj.Label, width, height, angle, radius) + ) + W = width if width else self.defaultTagWidth() H = height if height else self.defaultTagHeight() - A = angle if angle else self.defaultTagAngle() + A = angle if angle else self.defaultTagAngle() R = radius if radius else self.defaultTagRadius() tags = [] @@ -677,22 +836,39 @@ class PathData: for i, pos in enumerate(fromObj.Positions): print("tag[%d]" % i) if not i in fromObj.Disabled: - dist = self.baseWire.distToShape(Part.Vertex(FreeCAD.Vector(pos.x, pos.y, self.minZ))) + dist = self.baseWire.distToShape( + Part.Vertex(FreeCAD.Vector(pos.x, pos.y, self.minZ)) + ) if production or dist[0] < W: # russ4262:: `production` variable was a `True` declaration, forcing True branch to be processed always # The application of the `production` argument/variable is to appease LGTM - print("tag[%d/%d]: (%.2f, %.2f, %.2f)" % (i, j, pos.x, pos.y, self.minZ)) + print( + "tag[%d/%d]: (%.2f, %.2f, %.2f)" + % (i, j, pos.x, pos.y, self.minZ) + ) at = dist[1][0][0] - tags.append(Tag(j, at.x, at.y, W, H, A, R, True)) + tags.append(Tag(j, at.x, at.y, W, H, A, R, True)) j += 1 else: - PathLog.warning("Tag[%d] (%.2f, %.2f, %.2f) is too far away to copy: %.2f (%.2f)" % (i, pos.x, pos.y, self.minZ, dist[0], W)) + PathLog.warning( + "Tag[%d] (%.2f, %.2f, %.2f) is too far away to copy: %.2f (%.2f)" + % (i, pos.x, pos.y, self.minZ, dist[0], W) + ) else: PathLog.info("tag[%d]: not enabled, skipping" % i) print("copied %d tags" % len(tags)) return tags - def processEdge(self, index, edge, currentLength, lastTagLength, tagDistance, minLength, edgeDict): + def processEdge( + self, + index, + edge, + currentLength, + lastTagLength, + tagDistance, + minLength, + edgeDict, + ): tagCount = 0 currentLength += edge.Length if edge.Length >= minLength: @@ -708,7 +884,11 @@ class PathData: return (currentLength, lastTagLength) def defaultTagHeight(self): - if hasattr(self.obj, 'Base') and hasattr(self.obj.Base, 'StartDepth') and hasattr(self.obj.Base, 'FinalDepth'): + if ( + hasattr(self.obj, "Base") + and hasattr(self.obj.Base, "StartDepth") + and hasattr(self.obj.Base, "FinalDepth") + ): pathHeight = (self.obj.Base.StartDepth - self.obj.Base.FinalDepth).Value else: pathHeight = self.maxZ - self.minZ @@ -730,13 +910,27 @@ class PathData: def sortedTags(self, tags): ordered = [] for edge in self.bottomEdges: - ts = [t for t in tags if PathGeom.isRoughly(0, Part.Vertex(t.originAt(self.minZ)).distToShape(edge)[0], 0.1)] - for t in sorted(ts, key=lambda t, edge=edge: (t.originAt(self.minZ) - edge.valueAt(edge.FirstParameter)).Length): + ts = [ + t + for t in tags + if PathGeom.isRoughly( + 0, Part.Vertex(t.originAt(self.minZ)).distToShape(edge)[0], 0.1 + ) + ] + for t in sorted( + ts, + key=lambda t, edge=edge: ( + t.originAt(self.minZ) - edge.valueAt(edge.FirstParameter) + ).Length, + ): tags.remove(t) ordered.append(t) # disable all tags that are not on the base wire. for tag in tags: - PathLog.info("Tag #%d (%.2f, %.2f, %.2f) not on base wire - disabling\n" % (len(ordered), tag.x, tag.y, self.minZ)) + PathLog.info( + "Tag #%d (%.2f, %.2f, %.2f) not on base wire - disabling\n" + % (len(ordered), tag.x, tag.y, self.minZ) + ) tag.enabled = False ordered.append(tag) return ordered @@ -756,17 +950,65 @@ class PathData: class ObjectTagDressup: - def __init__(self, obj, base): - obj.addProperty("App::PropertyLink", "Base", "Base", QtCore.QT_TRANSLATE_NOOP("Path_DressupTag", "The base path to modify")) - obj.addProperty("App::PropertyLength", "Width", "Tag", QtCore.QT_TRANSLATE_NOOP("Path_DressupTag", "Width of tags.")) - obj.addProperty("App::PropertyLength", "Height", "Tag", QtCore.QT_TRANSLATE_NOOP("Path_DressupTag", "Height of tags.")) - obj.addProperty("App::PropertyAngle", "Angle", "Tag", QtCore.QT_TRANSLATE_NOOP("Path_DressupTag", "Angle of tag plunge and ascent.")) - obj.addProperty("App::PropertyLength", "Radius", "Tag", QtCore.QT_TRANSLATE_NOOP("Path_DressupTag", "Radius of the fillet for the tag.")) - obj.addProperty("App::PropertyVectorList", "Positions", "Tag", QtCore.QT_TRANSLATE_NOOP("Path_DressupTag", "Locations of inserted holding tags")) - obj.addProperty("App::PropertyIntegerList", "Disabled", "Tag", QtCore.QT_TRANSLATE_NOOP("Path_DressupTag", "IDs of disabled holding tags")) - obj.addProperty("App::PropertyInteger", "SegmentationFactor", "Tag", QtCore.QT_TRANSLATE_NOOP("Path_DressupTag", "Factor determining the # of segments used to approximate rounded tags.")) + obj.addProperty( + "App::PropertyLink", + "Base", + "Base", + QtCore.QT_TRANSLATE_NOOP("Path_DressupTag", "The base path to modify"), + ) + obj.addProperty( + "App::PropertyLength", + "Width", + "Tag", + QtCore.QT_TRANSLATE_NOOP("Path_DressupTag", "Width of tags."), + ) + obj.addProperty( + "App::PropertyLength", + "Height", + "Tag", + QtCore.QT_TRANSLATE_NOOP("Path_DressupTag", "Height of tags."), + ) + obj.addProperty( + "App::PropertyAngle", + "Angle", + "Tag", + QtCore.QT_TRANSLATE_NOOP( + "Path_DressupTag", "Angle of tag plunge and ascent." + ), + ) + obj.addProperty( + "App::PropertyLength", + "Radius", + "Tag", + QtCore.QT_TRANSLATE_NOOP( + "Path_DressupTag", "Radius of the fillet for the tag." + ), + ) + obj.addProperty( + "App::PropertyVectorList", + "Positions", + "Tag", + QtCore.QT_TRANSLATE_NOOP( + "Path_DressupTag", "Locations of inserted holding tags" + ), + ) + obj.addProperty( + "App::PropertyIntegerList", + "Disabled", + "Tag", + QtCore.QT_TRANSLATE_NOOP("Path_DressupTag", "IDs of disabled holding tags"), + ) + obj.addProperty( + "App::PropertyInteger", + "SegmentationFactor", + "Tag", + QtCore.QT_TRANSLATE_NOOP( + "Path_DressupTag", + "Factor determining the # of segments used to approximate rounded tags.", + ), + ) # for pylint ... self.obj = obj @@ -802,7 +1044,15 @@ class ObjectTagDressup: def generateTags(self, obj, count): if self.supportsTagGeneration(obj): if self.pathData: - self.tags = self.pathData.generateTags(obj, count, obj.Width.Value, obj.Height.Value, obj.Angle, obj.Radius.Value, None) + self.tags = self.pathData.generateTags( + obj, + count, + obj.Width.Value, + obj.Height.Value, + obj.Angle, + obj.Radius.Value, + None, + ) obj.Positions = [tag.originAt(self.pathData.minZ) for tag in self.tags] obj.Disabled = [] return False @@ -817,13 +1067,15 @@ class ObjectTagDressup: return False def copyTags(self, obj, fromObj): - obj.Width = fromObj.Width + obj.Width = fromObj.Width obj.Height = fromObj.Height - obj.Angle = fromObj.Angle + obj.Angle = fromObj.Angle obj.Radius = fromObj.Radius obj.SegmentationFactor = fromObj.SegmentationFactor - self.tags = self.pathData.copyTags(obj, fromObj, obj.Width.Value, obj.Height.Value, obj.Angle, obj.Radius.Value) + self.tags = self.pathData.copyTags( + obj, fromObj, obj.Width.Value, obj.Height.Value, obj.Angle, obj.Radius.Value + ) obj.Positions = [tag.originAt(self.pathData.minZ) for tag in self.tags] obj.Disabled = [] return False @@ -850,7 +1102,7 @@ class ObjectTagDressup: edge = None segm = 50 - if hasattr(obj, 'SegmentationFactor'): + if hasattr(obj, "SegmentationFactor"): segm = obj.SegmentationFactor if segm <= 0: segm = 50 @@ -866,10 +1118,14 @@ class ObjectTagDressup: vertRapid = tc.VertRapid.Value while edge or lastEdge < len(pathData.edges): - PathLog.debug("------- lastEdge = %d/%d.%d/%d" % (lastEdge, lastTag, t, len(tags))) + PathLog.debug( + "------- lastEdge = %d/%d.%d/%d" % (lastEdge, lastTag, t, len(tags)) + ) if not edge: edge = pathData.edges[lastEdge] - debugEdge(edge, "======= new edge: %d/%d" % (lastEdge, len(pathData.edges))) + debugEdge( + edge, "======= new edge: %d/%d" % (lastEdge, len(pathData.edges)) + ) lastEdge += 1 # sameTag = None @@ -887,23 +1143,46 @@ class ObjectTagDressup: t += 1 i = tags[tIndex].intersects(edge, edge.FirstParameter) if i and self.isValidTagStartIntersection(edge, i): - mapper = MapWireToTag(edge, tags[tIndex], i, segm, pathData.maxZ, hSpeed = horizFeed, vSpeed = vertFeed) + mapper = MapWireToTag( + edge, + tags[tIndex], + i, + segm, + pathData.maxZ, + hSpeed=horizFeed, + vSpeed=vertFeed, + ) self.mappers.append(mapper) edge = mapper.tail if not mapper and t >= len(tags): # gone through all tags, consume edge and move on if edge: - debugEdge(edge, '++++++++') + debugEdge(edge, "++++++++") if pathData.rapid.isRapid(edge): v = edge.Vertexes[1] - if not commands and PathGeom.isRoughly(0, v.X) and PathGeom.isRoughly(0, v.Y) and not PathGeom.isRoughly(0, v.Z): + if ( + not commands + and PathGeom.isRoughly(0, v.X) + and PathGeom.isRoughly(0, v.Y) + and not PathGeom.isRoughly(0, v.Z) + ): # The very first move is just to move to ClearanceHeight - commands.append(Path.Command('G0', {'Z': v.Z, 'F': horizRapid})) + commands.append( + Path.Command("G0", {"Z": v.Z, "F": horizRapid}) + ) else: - commands.append(Path.Command('G0', {'X': v.X, 'Y': v.Y, 'Z': v.Z, 'F': vertRapid})) + commands.append( + Path.Command( + "G0", {"X": v.X, "Y": v.Y, "Z": v.Z, "F": vertRapid} + ) + ) else: - commands.extend(PathGeom.cmdsForEdge(edge, segm=segm, hSpeed = horizFeed, vSpeed = vertFeed)) + commands.extend( + PathGeom.cmdsForEdge( + edge, segm=segm, hSpeed=horizFeed, vSpeed=vertFeed + ) + ) edge = None t = 0 @@ -915,7 +1194,16 @@ class ObjectTagDressup: def createTagsPositionDisabled(self, obj, positionsIn, disabledIn): rawTags = [] for i, pos in enumerate(positionsIn): - tag = Tag(i, pos.x, pos.y, obj.Width.Value, obj.Height.Value, obj.Angle, obj.Radius, not i in disabledIn) + tag = Tag( + i, + pos.x, + pos.y, + obj.Width.Value, + obj.Height.Value, + obj.Angle, + obj.Radius, + not i in disabledIn, + ) tag.createSolidsAt(self.pathData.minZ, self.toolRadius) rawTags.append(tag) # disable all tags that intersect with their previous tag @@ -927,15 +1215,21 @@ class ObjectTagDressup: if tag.enabled: if prev: if prev.solid.common(tag.solid).Faces: - PathLog.info("Tag #%d intersects with previous tag - disabling\n" % i) + PathLog.info( + "Tag #%d intersects with previous tag - disabling\n" % i + ) PathLog.debug("this tag = %d [%s]" % (i, tag.solid.BoundBox)) tag.enabled = False elif self.pathData.edges: e = self.pathData.edges[0] p0 = e.valueAt(e.FirstParameter) p1 = e.valueAt(e.LastParameter) - if tag.solid.isInside(p0, PathGeom.Tolerance, True) or tag.solid.isInside(p1, PathGeom.Tolerance, True): - PathLog.info("Tag #%d intersects with starting point - disabling\n" % i) + if tag.solid.isInside( + p0, PathGeom.Tolerance, True + ) or tag.solid.isInside(p1, PathGeom.Tolerance, True): + PathLog.info( + "Tag #%d intersects with starting point - disabling\n" % i + ) tag.enabled = False if tag.enabled: @@ -973,9 +1267,13 @@ class ObjectTagDressup: self.tags = [] if hasattr(obj, "Positions"): - self.tags, positions, disabled = self.createTagsPositionDisabled(obj, obj.Positions, obj.Disabled) + self.tags, positions, disabled = self.createTagsPositionDisabled( + obj, obj.Positions, obj.Disabled + ) if obj.Disabled != disabled: - PathLog.debug("Updating properties.... %s vs. %s" % (obj.Disabled, disabled)) + PathLog.debug( + "Updating properties.... %s vs. %s" % (obj.Disabled, disabled) + ) obj.Positions = positions obj.Disabled = disabled @@ -986,11 +1284,13 @@ class ObjectTagDressup: try: self.processTags(obj) - except Exception as e: # pylint: disable=broad-except - PathLog.error("processing tags failed clearing all tags ... '%s'" % (e.args[0])) - #if sys.version_info.major < 3: + except Exception as e: # pylint: disable=broad-except + PathLog.error( + "processing tags failed clearing all tags ... '%s'" % (e.args[0]) + ) + # if sys.version_info.major < 3: # traceback.print_exc(e) - #else: + # else: # traceback.print_exc() obj.Path = obj.Base.Path @@ -1007,14 +1307,16 @@ class ObjectTagDressup: @waiting_effects def processTags(self, obj): - global failures # pylint: disable=global-statement + global failures # pylint: disable=global-statement failures = [] tagID = 0 if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG: for tag in self.tags: tagID += 1 if tag.enabled: - PathLog.debug("x=%s, y=%s, z=%s" % (tag.x, tag.y, self.pathData.minZ)) + PathLog.debug( + "x=%s, y=%s, z=%s" % (tag.x, tag.y, self.pathData.minZ) + ) # debugMarker(FreeCAD.Vector(tag.x, tag.y, self.pathData.minZ), "tag-%02d" % tagID , (1.0, 0.0, 1.0), 0.5) # if not PathGeom.isRoughly(90, tag.angle): # debugCone(tag.originAt(self.pathData.minZ), tag.r1, tag.r2, tag.actualHeight, "tag-%02d" % tagID) @@ -1029,10 +1331,16 @@ class ObjectTagDressup: try: pathData = PathData(obj) except ValueError: - PathLog.error(translate("Path_DressupTag", "Cannot insert holding tags for this path - please select a Profile path")+"\n") - #if sys.version_info.major < 3: + PathLog.error( + translate( + "Path_DressupTag", + "Cannot insert holding tags for this path - please select a Profile path", + ) + + "\n" + ) + # if sys.version_info.major < 3: # traceback.print_exc(e) - #else: + # else: # traceback.print_exc() return None @@ -1058,7 +1366,11 @@ class ObjectTagDressup: positions.append(FreeCAD.Vector(x, y, 0)) if not enabled: disabled.append(i) - self.tags, self.obj.Positions, self.obj.Disabled = self.createTagsPositionDisabled(self.obj, positions, disabled) + ( + self.tags, + self.obj.Positions, + self.obj.Disabled, + ) = self.createTagsPositionDisabled(self.obj, positions, disabled) self.processTags(self.obj) def pointIsOnPath(self, obj, point): @@ -1072,16 +1384,18 @@ class ObjectTagDressup: return self.pathData.pointAtBottom(point) -def Create(baseObject, name='DressupTag'): - ''' +def Create(baseObject, name="DressupTag"): + """ Create(basePath, name='DressupTag') ... create tag dressup object for the given base path. - ''' - if not baseObject.isDerivedFrom('Path::Feature'): - PathLog.error(translate('Path_DressupTag', 'The selected object is not a path')+'\n') + """ + if not baseObject.isDerivedFrom("Path::Feature"): + PathLog.error( + translate("Path_DressupTag", "The selected object is not a path") + "\n" + ) return None - if baseObject.isDerivedFrom('Path::FeatureCompoundPython'): - PathLog.error(translate('Path_DressupTag', 'Please select a Profile object')) + if baseObject.isDerivedFrom("Path::FeatureCompoundPython"): + PathLog.error(translate("Path_DressupTag", "Please select a Profile object")) return None obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) @@ -1091,4 +1405,5 @@ def Create(baseObject, name='DressupTag'): dbo.setup(obj, True) return obj + PathLog.notice("Loading Path_DressupTag... done\n") diff --git a/src/Mod/Path/PathScripts/PathDressupPathBoundary.py b/src/Mod/Path/PathScripts/PathDressupPathBoundary.py index 68eda29544..6a2c737924 100644 --- a/src/Mod/Path/PathScripts/PathDressupPathBoundary.py +++ b/src/Mod/Path/PathScripts/PathDressupPathBoundary.py @@ -43,17 +43,39 @@ def translate(context, text, disambig=None): def _vstr(v): if v: return "(%.2f, %.2f, %.2f)" % (v.x, v.y, v.z) - return '-' + return "-" class DressupPathBoundary(object): - def __init__(self, obj, base, job): - obj.addProperty("App::PropertyLink", "Base", "Base", QtCore.QT_TRANSLATE_NOOP("Path_DressupPathBoundary", "The base path to modify")) + obj.addProperty( + "App::PropertyLink", + "Base", + "Base", + QtCore.QT_TRANSLATE_NOOP( + "Path_DressupPathBoundary", "The base path to modify" + ), + ) obj.Base = base - obj.addProperty("App::PropertyLink", "Stock", "Boundary", QtCore.QT_TRANSLATE_NOOP("Path_DressupPathBoundary", "Solid object to be used to limit the generated Path.")) + obj.addProperty( + "App::PropertyLink", + "Stock", + "Boundary", + QtCore.QT_TRANSLATE_NOOP( + "Path_DressupPathBoundary", + "Solid object to be used to limit the generated Path.", + ), + ) obj.Stock = PathStock.CreateFromBase(job) - obj.addProperty("App::PropertyBool", "Inside", "Boundary", QtCore.QT_TRANSLATE_NOOP("Path_DressupPathBoundary", "Determines if Boundary describes an inclusion or exclusion mask.")) + obj.addProperty( + "App::PropertyBool", + "Inside", + "Boundary", + QtCore.QT_TRANSLATE_NOOP( + "Path_DressupPathBoundary", + "Determines if Boundary describes an inclusion or exclusion mask.", + ), + ) obj.Inside = True self.obj = obj @@ -85,6 +107,8 @@ class DressupPathBoundary(object): def execute(self, obj): pb = PathBoundary(obj.Base, obj.Stock.Shape, obj.Inside) obj.Path = pb.execute() + + # Eclass @@ -114,15 +138,19 @@ class PathBoundary: if begin.z < self.clearanceHeight: cmds.append(self.strG0ZclearanceHeight) if end: - cmds.append(Path.Command('G0', {'X': end.x, 'Y': end.y})) + cmds.append(Path.Command("G0", {"X": end.x, "Y": end.y})) if end.z < self.clearanceHeight: - cmds.append(Path.Command('G0', {'Z': max(self.safeHeight, end.z)})) + cmds.append(Path.Command("G0", {"Z": max(self.safeHeight, end.z)})) if end.z < self.safeHeight: - cmds.append(Path.Command('G1', {'Z': end.z, 'F': verticalFeed})) + cmds.append(Path.Command("G1", {"Z": end.z, "F": verticalFeed})) return cmds def execute(self): - if not self.baseOp or not self.baseOp.isDerivedFrom('Path::Feature') or not self.baseOp.Path: + if ( + not self.baseOp + or not self.baseOp.isDerivedFrom("Path::Feature") + or not self.baseOp.Path + ): return None if len(self.baseOp.Path.Commands) == 0: @@ -131,23 +159,27 @@ class PathBoundary: tc = PathDressup.toolController(self.baseOp) - self.safeHeight = float(PathUtil.opProperty(self.baseOp, 'SafeHeight')) - self.clearanceHeight = float(PathUtil.opProperty(self.baseOp, 'ClearanceHeight')) - self.strG1ZsafeHeight = Path.Command('G1', {'Z': self.safeHeight, 'F': tc.VertFeed.Value}) - self.strG0ZclearanceHeight = Path.Command('G0', {'Z': self.clearanceHeight}) + self.safeHeight = float(PathUtil.opProperty(self.baseOp, "SafeHeight")) + self.clearanceHeight = float( + PathUtil.opProperty(self.baseOp, "ClearanceHeight") + ) + self.strG1ZsafeHeight = Path.Command( + "G1", {"Z": self.safeHeight, "F": tc.VertFeed.Value} + ) + self.strG0ZclearanceHeight = Path.Command("G0", {"Z": self.clearanceHeight}) cmd = self.baseOp.Path.Commands[0] - pos = cmd.Placement.Base # bogus m/c position to create first edge + pos = cmd.Placement.Base # bogus m/c position to create first edge bogusX = True bogusY = True commands = [cmd] lastExit = None for cmd in self.baseOp.Path.Commands[1:]: if cmd.Name in PathGeom.CmdMoveAll: - if bogusX == True : - bogusX = ( 'X' not in cmd.Parameters ) - if bogusY : - bogusY = ( 'Y' not in cmd.Parameters ) + if bogusX == True: + bogusX = "X" not in cmd.Parameters + if bogusY: + bogusY = "Y" not in cmd.Parameters edge = PathGeom.edgeForCmd(cmd, pos) if edge: inside = edge.common(self.boundary).Edges @@ -159,22 +191,30 @@ class PathBoundary: # it's really a shame that one cannot trust the sequence and/or # orientation of edges if 1 == len(inside) and 0 == len(outside): - PathLog.track(_vstr(pos), _vstr(lastExit), ' + ', cmd) + PathLog.track(_vstr(pos), _vstr(lastExit), " + ", cmd) # cmd fully included by boundary if lastExit: - if not ( bogusX or bogusY ) : # don't insert false paths based on bogus m/c position - commands.extend(self.boundaryCommands(lastExit, pos, tc.VertFeed.Value)) + if not ( + bogusX or bogusY + ): # don't insert false paths based on bogus m/c position + commands.extend( + self.boundaryCommands( + lastExit, pos, tc.VertFeed.Value + ) + ) lastExit = None commands.append(cmd) pos = PathGeom.commandEndPoint(cmd, pos) elif 0 == len(inside) and 1 == len(outside): - PathLog.track(_vstr(pos), _vstr(lastExit), ' - ', cmd) + PathLog.track(_vstr(pos), _vstr(lastExit), " - ", cmd) # cmd fully excluded by boundary if not lastExit: lastExit = pos pos = PathGeom.commandEndPoint(cmd, pos) else: - PathLog.track(_vstr(pos), _vstr(lastExit), len(inside), len(outside), cmd) + PathLog.track( + _vstr(pos), _vstr(lastExit), len(inside), len(outside), cmd + ) # cmd pierces boundary while inside or outside: ie = [e for e in inside if PathGeom.edgeConnectsTo(e, pos)] @@ -187,35 +227,58 @@ class PathBoundary: # inside edges are taken at this point (see swap of inside/outside # above - so we can just connect the dots ... if lastExit: - if not ( bogusX or bogusY ) : commands.extend(self.boundaryCommands(lastExit, pos, tc.VertFeed.Value)) + if not (bogusX or bogusY): + commands.extend( + self.boundaryCommands( + lastExit, pos, tc.VertFeed.Value + ) + ) lastExit = None PathLog.track(e, flip) - if not ( bogusX or bogusY ) : # don't insert false paths based on bogus m/c position - commands.extend(PathGeom.cmdsForEdge(e, flip, False, 50, tc.HorizFeed.Value, tc.VertFeed.Value)) + if not ( + bogusX or bogusY + ): # don't insert false paths based on bogus m/c position + commands.extend( + PathGeom.cmdsForEdge( + e, + flip, + False, + 50, + tc.HorizFeed.Value, + tc.VertFeed.Value, + ) + ) inside.remove(e) pos = newPos lastExit = newPos else: - oe = [e for e in outside if PathGeom.edgeConnectsTo(e, pos)] + oe = [ + e + for e in outside + if PathGeom.edgeConnectsTo(e, pos) + ] PathLog.track(oe) if oe: e = oe[0] ptL = e.valueAt(e.LastParameter) flip = PathGeom.pointsCoincide(pos, ptL) - newPos = e.valueAt(e.FirstParameter) if flip else ptL + newPos = ( + e.valueAt(e.FirstParameter) if flip else ptL + ) # outside edges are never taken at this point (see swap of # inside/outside above) - so just move along ... outside.remove(e) pos = newPos else: - PathLog.error('huh?') + PathLog.error("huh?") import Part - Part.show(Part.Vertex(pos), 'pos') + + Part.show(Part.Vertex(pos), "pos") for e in inside: - Part.show(e, 'ei') + Part.show(e, "ei") for e in outside: - Part.show(e, 'eo') - raise Exception('This is not supposed to happen') + Part.show(e, "eo") + raise Exception("This is not supposed to happen") # Eif # Eif # Ewhile @@ -223,7 +286,7 @@ class PathBoundary: # pos = PathGeom.commandEndPoint(cmd, pos) # Eif else: - PathLog.track('no-move', cmd) + PathLog.track("no-move", cmd) commands.append(cmd) if lastExit: commands.extend(self.boundaryCommands(lastExit, None, tc.VertFeed.Value)) @@ -231,16 +294,22 @@ class PathBoundary: PathLog.track(commands) return Path.Path(commands) + + # Eclass -def Create(base, name='DressupPathBoundary'): - '''Create(base, name='DressupPathBoundary') ... creates a dressup limiting base's Path to a boundary.''' - if not base.isDerivedFrom('Path::Feature'): - PathLog.error(translate('Path_DressupPathBoundary', 'The selected object is not a path')+'\n') +def Create(base, name="DressupPathBoundary"): + """Create(base, name='DressupPathBoundary') ... creates a dressup limiting base's Path to a boundary.""" + + if not base.isDerivedFrom("Path::Feature"): + PathLog.error( + translate("Path_DressupPathBoundary", "The selected object is not a path") + + "\n" + ) return None - obj = FreeCAD.ActiveDocument.addObject('Path::FeaturePython', name) + obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) job = PathUtils.findParentJob(base) obj.Proxy = DressupPathBoundary(obj, base, job) job.Proxy.addOperation(obj, base, True) diff --git a/src/Mod/Path/PathScripts/PathDressupPathBoundaryGui.py b/src/Mod/Path/PathScripts/PathDressupPathBoundaryGui.py index 6152c51433..c76f9e2042 100644 --- a/src/Mod/Path/PathScripts/PathDressupPathBoundaryGui.py +++ b/src/Mod/Path/PathScripts/PathDressupPathBoundaryGui.py @@ -22,26 +22,26 @@ import FreeCAD import FreeCADGui -import PathGui as PGui # ensure Path/Gui/Resources are loaded +import PathGui as PGui # ensure Path/Gui/Resources are loaded import PathScripts.PathDressupPathBoundary as PathDressupPathBoundary import PathScripts.PathLog as PathLog from PySide import QtGui, QtCore PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -#PathLog.trackModule() +# PathLog.trackModule() # Qt translation handling def translate(context, text, disambig=None): return QtCore.QCoreApplication.translate(context, text, disambig) -class TaskPanel(object): +class TaskPanel(object): def __init__(self, obj, viewProvider): self.obj = obj self.viewProvider = viewProvider - self.form = FreeCADGui.PySideUic.loadUi(':/panels/DressupPathBoundary.ui') + self.form = FreeCADGui.PySideUic.loadUi(":/panels/DressupPathBoundary.ui") if obj.Stock: self.visibilityBoundary = obj.Stock.ViewObject.Visibility obj.Stock.ViewObject.Visibility = True @@ -58,7 +58,11 @@ class TaskPanel(object): self.stockEdit = None def getStandardButtons(self): - return int(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Apply | QtGui.QDialogButtonBox.Cancel) + return int( + QtGui.QDialogButtonBox.Ok + | QtGui.QDialogButtonBox.Apply + | QtGui.QDialogButtonBox.Cancel + ) def modifyStandardButtons(self, buttonBox): self.buttonBox = buttonBox @@ -113,32 +117,42 @@ class TaskPanel(object): def setupFromBaseEdit(): PathLog.track(index, force) if force or not self.stockFromBase: - self.stockFromBase = PathJobGui.StockFromBaseBoundBoxEdit(self.obj, self.form, force) + self.stockFromBase = PathJobGui.StockFromBaseBoundBoxEdit( + self.obj, self.form, force + ) self.stockEdit = self.stockFromBase def setupCreateBoxEdit(): PathLog.track(index, force) if force or not self.stockCreateBox: - self.stockCreateBox = PathJobGui.StockCreateBoxEdit(self.obj, self.form, force) + self.stockCreateBox = PathJobGui.StockCreateBoxEdit( + self.obj, self.form, force + ) self.stockEdit = self.stockCreateBox def setupCreateCylinderEdit(): PathLog.track(index, force) if force or not self.stockCreateCylinder: - self.stockCreateCylinder = PathJobGui.StockCreateCylinderEdit(self.obj, self.form, force) + self.stockCreateCylinder = PathJobGui.StockCreateCylinderEdit( + self.obj, self.form, force + ) self.stockEdit = self.stockCreateCylinder def setupFromExisting(): PathLog.track(index, force) if force or not self.stockFromExisting: - self.stockFromExisting = PathJobGui.StockFromExistingEdit(self.obj, self.form, force) + self.stockFromExisting = PathJobGui.StockFromExistingEdit( + self.obj, self.form, force + ) if self.stockFromExisting.candidates(self.obj): self.stockEdit = self.stockFromExisting return True return False if index == -1: - if self.obj.Stock is None or PathJobGui.StockFromBaseBoundBoxEdit.IsStock(self.obj): + if self.obj.Stock is None or PathJobGui.StockFromBaseBoundBoxEdit.IsStock( + self.obj + ): setupFromBaseEdit() elif PathJobGui.StockCreateBoxEdit.IsStock(self.obj): setupCreateBoxEdit() @@ -147,7 +161,10 @@ class TaskPanel(object): elif PathJobGui.StockFromExistingEdit.IsStock(self.obj): setupFromExisting() else: - PathLog.error(translate('PathJob', "Unsupported stock object %s") % self.obj.Stock.Label) + PathLog.error( + translate("PathJob", "Unsupported stock object %s") + % self.obj.Stock.Label + ) else: if index == PathJobGui.StockFromBaseBoundBoxEdit.Index: setupFromBaseEdit() @@ -160,7 +177,10 @@ class TaskPanel(object): setupFromBaseEdit() index = -1 else: - PathLog.error(translate('PathJob', "Unsupported stock type %s (%d)") % (self.form.stock.currentText(), index)) + PathLog.error( + translate("PathJob", "Unsupported stock type %s (%d)") + % (self.form.stock.currentText(), index) + ) self.stockEdit.activate(self.obj, index == -1) def setupUi(self): @@ -183,16 +203,15 @@ class TaskPanel(object): class DressupPathBoundaryViewProvider(object): - def __init__(self, vobj): self.attach(vobj) def __getstate__(self): return None + def __setstate__(self, state): return None - def attach(self, vobj): self.vobj = vobj self.obj = vobj.Object @@ -225,8 +244,10 @@ class DressupPathBoundaryViewProvider(object): self.panel = None -def Create(base, name='DressupPathBoundary'): - FreeCAD.ActiveDocument.openTransaction(translate('Path_DressupPathBoundary', 'Create a Boundary dressup')) +def Create(base, name="DressupPathBoundary"): + FreeCAD.ActiveDocument.openTransaction( + translate("Path_DressupPathBoundary", "Create a Boundary dressup") + ) obj = PathDressupPathBoundary.Create(base, name) obj.ViewObject.Proxy = DressupPathBoundaryViewProvider(obj.ViewObject) obj.Base.ViewObject.Visibility = False @@ -235,18 +256,26 @@ def Create(base, name='DressupPathBoundary'): obj.ViewObject.Document.setEdit(obj.ViewObject, 0) return obj + class CommandPathDressupPathBoundary: # pylint: disable=no-init def GetResources(self): - return {'Pixmap': 'Path_Dressup', - 'MenuText': QtCore.QT_TRANSLATE_NOOP('Path_DressupPathBoundary', 'Boundary Dress-up'), - 'ToolTip': QtCore.QT_TRANSLATE_NOOP('Path_DressupPathBoundary', 'Creates a Path Boundary Dress-up object from a selected path')} + return { + "Pixmap": "Path_Dressup", + "MenuText": QtCore.QT_TRANSLATE_NOOP( + "Path_DressupPathBoundary", "Boundary Dress-up" + ), + "ToolTip": QtCore.QT_TRANSLATE_NOOP( + "Path_DressupPathBoundary", + "Creates a Path Boundary Dress-up object from a selected path", + ), + } def IsActive(self): if FreeCAD.ActiveDocument is not None: for o in FreeCAD.ActiveDocument.Objects: - if o.Name[:3] == 'Job': + if o.Name[:3] == "Job": return True return False @@ -254,19 +283,28 @@ class CommandPathDressupPathBoundary: # check that the selection contains exactly what we want selection = FreeCADGui.Selection.getSelection() if len(selection) != 1: - PathLog.error(translate('Path_DressupPathBoundary', 'Please select one path object')+'\n') + PathLog.error( + translate("Path_DressupPathBoundary", "Please select one path object") + + "\n" + ) return baseObject = selection[0] # everything ok! - FreeCAD.ActiveDocument.openTransaction(translate('Path_DressupPathBoundary', 'Create Path Boundary Dress-up')) - FreeCADGui.addModule('PathScripts.PathDressupPathBoundaryGui') - FreeCADGui.doCommand("PathScripts.PathDressupPathBoundaryGui.Create(App.ActiveDocument.%s)" % baseObject.Name) + FreeCAD.ActiveDocument.openTransaction( + translate("Path_DressupPathBoundary", "Create Path Boundary Dress-up") + ) + FreeCADGui.addModule("PathScripts.PathDressupPathBoundaryGui") + FreeCADGui.doCommand( + "PathScripts.PathDressupPathBoundaryGui.Create(App.ActiveDocument.%s)" + % baseObject.Name + ) FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() + if FreeCAD.GuiUp: # register the FreeCAD command - FreeCADGui.addCommand('Path_DressupPathBoundary', CommandPathDressupPathBoundary()) + FreeCADGui.addCommand("Path_DressupPathBoundary", CommandPathDressupPathBoundary()) -PathLog.notice('Loading PathDressupPathBoundaryGui... done\n') +PathLog.notice("Loading PathDressupPathBoundaryGui... done\n") diff --git a/src/Mod/Path/PathScripts/PathDressupTag.py b/src/Mod/Path/PathScripts/PathDressupTag.py index 30e2243a52..763b8d55ba 100644 --- a/src/Mod/Path/PathScripts/PathDressupTag.py +++ b/src/Mod/Path/PathScripts/PathDressupTag.py @@ -29,22 +29,25 @@ import math # lazily loaded modules from lazy_loader.lazy_loader import LazyLoader -DraftGeomUtils = LazyLoader('DraftGeomUtils', globals(), 'DraftGeomUtils') -Part = LazyLoader('Part', globals(), 'Part') + +DraftGeomUtils = LazyLoader("DraftGeomUtils", globals(), "DraftGeomUtils") +Part = LazyLoader("Part", globals(), "Part") from PathScripts.PathDressupTagPreferences import HoldingTagPreferences from PySide import QtCore PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -#PathLog.trackModule() +# PathLog.trackModule() # Qt translation handling def translate(context, text, disambig=None): return QtCore.QCoreApplication.translate(context, text, disambig) + MaxInt = 99999999999999 + class TagSolid: def __init__(self, proxy, z, R): self.proxy = proxy @@ -76,7 +79,7 @@ class TagSolid: # with top r2 = r1 - dr s = height / math.sin(rad) - radius = min(r2, s) * math.tan((math.pi - rad)/2) * 0.95 + radius = min(r2, s) * math.tan((math.pi - rad) / 2) * 0.95 else: # triangular r2 = 0 @@ -98,7 +101,9 @@ class TagSolid: # lastly determine the center of the model, we want to make sure the seam of # the tag solid points away (in the hopes it doesn't coincide with a path) - self.baseCenter = FreeCAD.Vector((proxy.ptMin.x+proxy.ptMax.x)/2, (proxy.ptMin.y+proxy.ptMax.y)/2, 0) + self.baseCenter = FreeCAD.Vector( + (proxy.ptMin.x + proxy.ptMax.x) / 2, (proxy.ptMin.y + proxy.ptMax.y) / 2, 0 + ) def cloneAt(self, pos): clone = self.solid.copy() @@ -111,17 +116,65 @@ class TagSolid: class ObjectDressup: - def __init__(self, obj, base): - obj.addProperty('App::PropertyLink', 'Base', 'Base', QtCore.QT_TRANSLATE_NOOP('Path_DressupTag', 'The base path to modify')) - obj.addProperty('App::PropertyLength', 'Width', 'Tag', QtCore.QT_TRANSLATE_NOOP('Path_DressupTag', 'Width of tags.')) - obj.addProperty('App::PropertyLength', 'Height', 'Tag', QtCore.QT_TRANSLATE_NOOP('Path_DressupTag', 'Height of tags.')) - obj.addProperty('App::PropertyAngle', 'Angle', 'Tag', QtCore.QT_TRANSLATE_NOOP('Path_DressupTag', 'Angle of tag plunge and ascent.')) - obj.addProperty('App::PropertyLength', 'Radius', 'Tag', QtCore.QT_TRANSLATE_NOOP('Path_DressupTag', 'Radius of the fillet for the tag.')) - obj.addProperty('App::PropertyVectorList', 'Positions', 'Tag', QtCore.QT_TRANSLATE_NOOP('Path_DressupTag', 'Locations of inserted holding tags')) - obj.addProperty('App::PropertyIntegerList', 'Disabled', 'Tag', QtCore.QT_TRANSLATE_NOOP('Path_DressupTag', 'IDs of disabled holding tags')) - obj.addProperty('App::PropertyInteger', 'SegmentationFactor', 'Tag', QtCore.QT_TRANSLATE_NOOP('Path_DressupTag', 'Factor determining the # of segments used to approximate rounded tags.')) + obj.addProperty( + "App::PropertyLink", + "Base", + "Base", + QtCore.QT_TRANSLATE_NOOP("Path_DressupTag", "The base path to modify"), + ) + obj.addProperty( + "App::PropertyLength", + "Width", + "Tag", + QtCore.QT_TRANSLATE_NOOP("Path_DressupTag", "Width of tags."), + ) + obj.addProperty( + "App::PropertyLength", + "Height", + "Tag", + QtCore.QT_TRANSLATE_NOOP("Path_DressupTag", "Height of tags."), + ) + obj.addProperty( + "App::PropertyAngle", + "Angle", + "Tag", + QtCore.QT_TRANSLATE_NOOP( + "Path_DressupTag", "Angle of tag plunge and ascent." + ), + ) + obj.addProperty( + "App::PropertyLength", + "Radius", + "Tag", + QtCore.QT_TRANSLATE_NOOP( + "Path_DressupTag", "Radius of the fillet for the tag." + ), + ) + obj.addProperty( + "App::PropertyVectorList", + "Positions", + "Tag", + QtCore.QT_TRANSLATE_NOOP( + "Path_DressupTag", "Locations of inserted holding tags" + ), + ) + obj.addProperty( + "App::PropertyIntegerList", + "Disabled", + "Tag", + QtCore.QT_TRANSLATE_NOOP("Path_DressupTag", "IDs of disabled holding tags"), + ) + obj.addProperty( + "App::PropertyInteger", + "SegmentationFactor", + "Tag", + QtCore.QT_TRANSLATE_NOOP( + "Path_DressupTag", + "Factor determining the # of segments used to approximate rounded tags.", + ), + ) obj.Proxy = self obj.Base = base @@ -152,16 +205,20 @@ class ObjectDressup: def execute(self, obj): PathLog.track() if not obj.Base: - PathLog.error(translate('Path_DressupTag', 'No Base object found.')) + PathLog.error(translate("Path_DressupTag", "No Base object found.")) return - if not obj.Base.isDerivedFrom('Path::Feature'): - PathLog.error(translate('Path_DressupTag', 'Base is not a Path::Feature object.')) + if not obj.Base.isDerivedFrom("Path::Feature"): + PathLog.error( + translate("Path_DressupTag", "Base is not a Path::Feature object.") + ) return if not obj.Base.Path: - PathLog.error(translate('Path_DressupTag', 'Base doesn\'t have a Path to dress-up.')) + PathLog.error( + translate("Path_DressupTag", "Base doesn't have a Path to dress-up.") + ) return if not obj.Base.Path.Commands: - PathLog.error(translate('Path_DressupTag', 'Base Path is empty.')) + PathLog.error(translate("Path_DressupTag", "Base Path is empty.")) return self.obj = obj @@ -190,14 +247,19 @@ class ObjectDressup: maxZ = max(pt.z, maxZ) minZ = min(pt.z, minZ) lastPt = pt - PathLog.debug("bb = (%.2f, %.2f, %.2f) ... (%.2f, %.2f, %.2f)" % (minX, minY, minZ, maxX, maxY, maxZ)) + PathLog.debug( + "bb = (%.2f, %.2f, %.2f) ... (%.2f, %.2f, %.2f)" + % (minX, minY, minZ, maxX, maxY, maxZ) + ) self.ptMin = FreeCAD.Vector(minX, minY, minZ) self.ptMax = FreeCAD.Vector(maxX, maxY, maxZ) self.masterSolid = TagSolid(self, minZ, self.toolRadius()) self.solids = [self.masterSolid.cloneAt(pos) for pos in self.obj.Positions] self.tagSolid = Part.Compound(self.solids) - self.wire, rapid = PathGeom.wireForPath(obj.Base.Path) # pylint: disable=unused-variable + self.wire, rapid = PathGeom.wireForPath( + obj.Base.Path + ) # pylint: disable=unused-variable self.edges = self.wire.Edges maxTagZ = minZ + obj.Height.Value @@ -210,7 +272,7 @@ class ObjectDressup: for cmd in obj.Base.Path.Commands: if cmd in PathGeom.CmdMove: - if lastZ <= maxTagZ or cmd.Parameters.get('Z', lastZ) <= maxTagZ: + if lastZ <= maxTagZ or cmd.Parameters.get("Z", lastZ) <= maxTagZ: pass else: commands.append(cmd) @@ -226,7 +288,7 @@ class ObjectDressup: def addTagsToDocument(self): for i, solid in enumerate(self.solids): - obj = FreeCAD.ActiveDocument.addObject('Part::Compound', "tag_%02d" % i) + obj = FreeCAD.ActiveDocument.addObject("Part::Compound", "tag_%02d" % i) obj.Shape = solid def supportsTagGeneration(self, obj): @@ -241,16 +303,18 @@ class ObjectDressup: return False -def Create(baseObject, name='DressupTag'): - ''' +def Create(baseObject, name="DressupTag"): + """ Create(basePath, name = 'DressupTag') ... create tag dressup object for the given base path. - ''' - if not baseObject.isDerivedFrom('Path::Feature'): - PathLog.error(translate('Path_DressupTag', 'The selected object is not a path')+'\n') + """ + if not baseObject.isDerivedFrom("Path::Feature"): + PathLog.error( + translate("Path_DressupTag", "The selected object is not a path") + "\n" + ) return None - if baseObject.isDerivedFrom('Path::FeatureCompoundPython'): - PathLog.error(translate('Path_DressupTag', 'Please select a Profile object')) + if baseObject.isDerivedFrom("Path::FeatureCompoundPython"): + PathLog.error(translate("Path_DressupTag", "Please select a Profile object")) return None obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) @@ -260,4 +324,5 @@ def Create(baseObject, name='DressupTag'): dbo.assignDefaultValues() return obj -PathLog.notice('Loading Path_DressupTag... done\n') + +PathLog.notice("Loading Path_DressupTag... done\n") diff --git a/src/Mod/Path/PathScripts/PathDressupTagGui.py b/src/Mod/Path/PathScripts/PathDressupTagGui.py index be312176a1..ce181d306c 100644 --- a/src/Mod/Path/PathScripts/PathDressupTagGui.py +++ b/src/Mod/Path/PathScripts/PathDressupTagGui.py @@ -22,7 +22,7 @@ import FreeCAD import FreeCADGui -import PathGui as PGui # ensure Path/Gui/Resources are loaded +import PathGui as PGui # ensure Path/Gui/Resources are loaded import PathScripts.PathGeom as PathGeom import PathScripts.PathGetPoint as PathGetPoint import PathScripts.PathDressupHoldingTags as PathDressupTag @@ -60,7 +60,9 @@ class PathDressupTagTaskPanel: self.getPoint = PathGetPoint.TaskPanel(self.form.removeEditAddGroup, True) self.jvo = PathUtils.findParentJob(obj).ViewObject if jvoVisibility is None: - FreeCAD.ActiveDocument.openTransaction(translate("PathDressup_HoldingTags", "Edit HoldingTags Dress-up")) + FreeCAD.ActiveDocument.openTransaction( + translate("PathDressup_HoldingTags", "Edit HoldingTags Dress-up") + ) self.jvoVisible = self.jvo.isVisible() if self.jvoVisible: self.jvo.hide() @@ -76,7 +78,11 @@ class PathDressupTagTaskPanel: self.editItem = None def getStandardButtons(self): - return int(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Apply | QtGui.QDialogButtonBox.Cancel) + return int( + QtGui.QDialogButtonBox.Ok + | QtGui.QDialogButtonBox.Apply + | QtGui.QDialogButtonBox.Cancel + ) def clicked(self, button): if button == QtGui.QDialogButtonBox.Apply: @@ -201,7 +207,9 @@ class PathDressupTagTaskPanel: self.Disabled = self.obj.Disabled self.updateTagsView() else: - PathLog.error(translate('Path_DressupTag', 'Cannot copy tags - internal error')+'\n') + PathLog.error( + translate("Path_DressupTag", "Cannot copy tags - internal error") + "\n" + ) def updateModel(self): self.getFields() @@ -273,10 +281,16 @@ class PathDressupTagTaskPanel: def setFields(self): self.updateTagsView() self.form.sbCount.setValue(len(self.Positions)) - self.form.ifHeight.setText(FreeCAD.Units.Quantity(self.obj.Height, FreeCAD.Units.Length).UserString) - self.form.ifWidth.setText(FreeCAD.Units.Quantity(self.obj.Width, FreeCAD.Units.Length).UserString) + self.form.ifHeight.setText( + FreeCAD.Units.Quantity(self.obj.Height, FreeCAD.Units.Length).UserString + ) + self.form.ifWidth.setText( + FreeCAD.Units.Quantity(self.obj.Width, FreeCAD.Units.Length).UserString + ) self.form.dsbAngle.setValue(self.obj.Angle) - self.form.ifRadius.setText(FreeCAD.Units.Quantity(self.obj.Radius, FreeCAD.Units.Length).UserString) + self.form.ifRadius.setText( + FreeCAD.Units.Quantity(self.obj.Radius, FreeCAD.Units.Length).UserString + ) def setupUi(self): self.Positions = self.obj.Positions @@ -292,7 +306,9 @@ class PathDressupTagTaskPanel: self.form.cbTagGeneration.setEnabled(False) enableCopy = False - for tags in sorted([o.Label for o in FreeCAD.ActiveDocument.Objects if 'DressupTag' in o.Name]): + for tags in sorted( + [o.Label for o in FreeCAD.ActiveDocument.Objects if "DressupTag" in o.Name] + ): if tags != self.obj.Label: enableCopy = True self.form.cbSource.addItem(tags) @@ -319,8 +335,8 @@ class HoldingTagMarker: self.pos = coin.SoTranslation() self.pos.translation = (point.x, point.y, point.z) self.sphere = coin.SoSphere() - self.scale = coin.SoType.fromName('SoShapeScale').createInstance() - self.scale.setPart('shape', self.sphere) + self.scale = coin.SoType.fromName("SoShapeScale").createInstance() + self.scale.setPart("shape", self.sphere) self.scale.scaleFactor.setValue(7) self.material = coin.SoMaterial() self.sep.addChild(self.pos) @@ -337,15 +353,18 @@ class HoldingTagMarker: def setEnabled(self, enabled): self.enabled = enabled if enabled: - self.material.diffuseColor = self.color[0] if not self.selected else self.color[2] + self.material.diffuseColor = ( + self.color[0] if not self.selected else self.color[2] + ) self.material.transparency = 0.0 else: - self.material.diffuseColor = self.color[1] if not self.selected else self.color[2] + self.material.diffuseColor = ( + self.color[1] if not self.selected else self.color[2] + ) self.material.transparency = 0.6 class PathDressupTagViewProvider: - def __init__(self, vobj): PathLog.track() self.vobj = vobj @@ -376,15 +395,25 @@ class PathDressupTagViewProvider: def setupColors(self): def colorForColorValue(val): - v = [((val >> n) & 0xff) / 255. for n in [24, 16, 8, 0]] + v = [((val >> n) & 0xFF) / 255.0 for n in [24, 16, 8, 0]] return coin.SbColor(v[0], v[1], v[2]) pref = PathPreferences.preferences() # R G B A - npc = pref.GetUnsigned('DefaultPathMarkerColor', ((85*256 + 255)*256 + 0) * 256 + 255) - hpc = pref.GetUnsigned('DefaultHighlightPathColor', ((255*256 + 125)*256 + 0)*256 + 255) - dpc = pref.GetUnsigned('DefaultDisabledPathColor', ((205*256 + 205)*256 + 205)*256 + 154) - self.colors = [colorForColorValue(npc), colorForColorValue(dpc), colorForColorValue(hpc)] + npc = pref.GetUnsigned( + "DefaultPathMarkerColor", ((85 * 256 + 255) * 256 + 0) * 256 + 255 + ) + hpc = pref.GetUnsigned( + "DefaultHighlightPathColor", ((255 * 256 + 125) * 256 + 0) * 256 + 255 + ) + dpc = pref.GetUnsigned( + "DefaultDisabledPathColor", ((205 * 256 + 205) * 256 + 205) * 256 + 154 + ) + self.colors = [ + colorForColorValue(npc), + colorForColorValue(dpc), + colorForColorValue(hpc), + ] def attach(self, vobj): PathLog.track() @@ -398,7 +427,9 @@ class PathDressupTagViewProvider: if self.obj and self.obj.Base: for i in self.obj.Base.InList: - if hasattr(i, 'Group') and self.obj.Base.Name in [o.Name for o in i.Group]: + if hasattr(i, "Group") and self.obj.Base.Name in [ + o.Name for o in i.Group + ]: i.Group = [o for o in i.Group if o.Name != self.obj.Base.Name] if self.obj.Base.ViewObject: self.obj.Base.ViewObject.Visibility = False @@ -416,7 +447,7 @@ class PathDressupTagViewProvider: return [self.obj.Base] def onDelete(self, arg1=None, arg2=None): - '''this makes sure that the base operation is added back to the job and visible''' + """this makes sure that the base operation is added back to the job and visible""" # pylint: disable=unused-argument PathLog.track() if self.obj.Base and self.obj.Base.ViewObject: @@ -436,7 +467,9 @@ class PathDressupTagViewProvider: self.switch.removeChild(tag.sep) tags = [] for i, p in enumerate(positions): - tag = HoldingTagMarker(self.obj.Proxy.pointAtBottom(self.obj, p), self.colors) + tag = HoldingTagMarker( + self.obj.Proxy.pointAtBottom(self.obj, p), self.colors + ) tag.setEnabled(not i in disabled) tags.append(tag) self.switch.addChild(tag.sep) @@ -444,7 +477,7 @@ class PathDressupTagViewProvider: def updateData(self, obj, propName): PathLog.track(propName) - if 'Disabled' == propName: + if "Disabled" == propName: self.updatePositions(obj.Positions, obj.Disabled) def onModelChanged(self): @@ -468,7 +501,7 @@ class PathDressupTagViewProvider: def unsetEdit(self, vobj, mode): # pylint: disable=unused-argument - if hasattr(self, 'panel') and self.panel: + if hasattr(self, "panel") and self.panel: self.panel.abort() def setupTaskPanel(self, panel): @@ -500,7 +533,9 @@ class PathDressupTagViewProvider: z = self.tags[0].point.z p = FreeCAD.Vector(x, y, z) for i, tag in enumerate(self.tags): - if PathGeom.pointsCoincide(p, tag.point, tag.sphere.radius.getValue() * 1.3): + if PathGeom.pointsCoincide( + p, tag.point, tag.sphere.radius.getValue() * 1.3 + ): return i return -1 @@ -519,12 +554,14 @@ class PathDressupTagViewProvider: FreeCADGui.updateGui() -def Create(baseObject, name='DressupTag'): - ''' +def Create(baseObject, name="DressupTag"): + """ Create(basePath, name = 'DressupTag') ... create tag dressup object for the given base path. Use this command only iff the UI is up - for batch processing see PathDressupTag.Create - ''' - FreeCAD.ActiveDocument.openTransaction(translate("Path_DressupTag", "Create a Tag dressup")) + """ + FreeCAD.ActiveDocument.openTransaction( + translate("Path_DressupTag", "Create a Tag dressup") + ) obj = PathDressupTag.Create(baseObject, name) obj.ViewObject.Proxy = PathDressupTagViewProvider(obj.ViewObject) FreeCAD.ActiveDocument.commitTransaction() @@ -536,14 +573,18 @@ class CommandPathDressupTag: # pylint: disable=no-init def GetResources(self): - return {'Pixmap': 'Path_Dressup', - 'MenuText': QtCore.QT_TRANSLATE_NOOP('Path_DressupTag', 'Tag Dress-up'), - 'ToolTip': QtCore.QT_TRANSLATE_NOOP('Path_DressupTag', 'Creates a Tag Dress-up object from a selected path')} + return { + "Pixmap": "Path_Dressup", + "MenuText": QtCore.QT_TRANSLATE_NOOP("Path_DressupTag", "Tag Dress-up"), + "ToolTip": QtCore.QT_TRANSLATE_NOOP( + "Path_DressupTag", "Creates a Tag Dress-up object from a selected path" + ), + } def IsActive(self): if FreeCAD.ActiveDocument is not None: for o in FreeCAD.ActiveDocument.Objects: - if o.Name[:3] == 'Job': + if o.Name[:3] == "Job": return True return False @@ -551,19 +592,27 @@ class CommandPathDressupTag: # check that the selection contains exactly what we want selection = FreeCADGui.Selection.getSelection() if len(selection) != 1: - PathLog.error(translate('Path_DressupTag', 'Please select one path object')+'\n') + PathLog.error( + translate("Path_DressupTag", "Please select one path object") + "\n" + ) return baseObject = selection[0] # everything ok! - FreeCAD.ActiveDocument.openTransaction(translate('Path_DressupTag', 'Create Tag Dress-up')) - FreeCADGui.addModule('PathScripts.PathDressupTagGui') - FreeCADGui.doCommand("PathScripts.PathDressupTagGui.Create(App.ActiveDocument.%s)" % baseObject.Name) + FreeCAD.ActiveDocument.openTransaction( + translate("Path_DressupTag", "Create Tag Dress-up") + ) + FreeCADGui.addModule("PathScripts.PathDressupTagGui") + FreeCADGui.doCommand( + "PathScripts.PathDressupTagGui.Create(App.ActiveDocument.%s)" + % baseObject.Name + ) FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() + if FreeCAD.GuiUp: # register the FreeCAD command - FreeCADGui.addCommand('Path_DressupTag', CommandPathDressupTag()) + FreeCADGui.addCommand("Path_DressupTag", CommandPathDressupTag()) -PathLog.notice('Loading PathDressupTagGui... done\n') +PathLog.notice("Loading PathDressupTagGui... done\n") diff --git a/src/Mod/Path/PathScripts/PathDressupTagPreferences.py b/src/Mod/Path/PathScripts/PathDressupTagPreferences.py index 99bb0888d4..6d171fd8ba 100644 --- a/src/Mod/Path/PathScripts/PathDressupTagPreferences.py +++ b/src/Mod/Path/PathScripts/PathDressupTagPreferences.py @@ -33,67 +33,102 @@ def translate(context, text, disambig=None): class HoldingTagPreferences: - DefaultHoldingTagWidth = 'DefaultHoldingTagWidth' - DefaultHoldingTagHeight = 'DefaultHoldingTagHeight' - DefaultHoldingTagAngle = 'DefaultHoldingTagAngle' - DefaultHoldingTagRadius = 'DefaultHoldingTagRadius' - DefaultHoldingTagCount = 'DefaultHoldingTagCount' + DefaultHoldingTagWidth = "DefaultHoldingTagWidth" + DefaultHoldingTagHeight = "DefaultHoldingTagHeight" + DefaultHoldingTagAngle = "DefaultHoldingTagAngle" + DefaultHoldingTagRadius = "DefaultHoldingTagRadius" + DefaultHoldingTagCount = "DefaultHoldingTagCount" @classmethod def defaultWidth(cls, ifNotSet): - value = PathPreferences.preferences().GetFloat(cls.DefaultHoldingTagWidth, ifNotSet) + value = PathPreferences.preferences().GetFloat( + cls.DefaultHoldingTagWidth, ifNotSet + ) if value == 0.0: return ifNotSet return value @classmethod def defaultHeight(cls, ifNotSet): - value = PathPreferences.preferences().GetFloat(cls.DefaultHoldingTagHeight, ifNotSet) + value = PathPreferences.preferences().GetFloat( + cls.DefaultHoldingTagHeight, ifNotSet + ) if value == 0.0: return ifNotSet return value @classmethod def defaultAngle(cls, ifNotSet=45.0): - value = PathPreferences.preferences().GetFloat(cls.DefaultHoldingTagAngle, ifNotSet) + value = PathPreferences.preferences().GetFloat( + cls.DefaultHoldingTagAngle, ifNotSet + ) if value < 10.0: return ifNotSet return value @classmethod def defaultCount(cls, ifNotSet=4): - value = PathPreferences.preferences().GetUnsigned(cls.DefaultHoldingTagCount, ifNotSet) + value = PathPreferences.preferences().GetUnsigned( + cls.DefaultHoldingTagCount, ifNotSet + ) if value < 2: return float(ifNotSet) return float(value) @classmethod def defaultRadius(cls, ifNotSet=0.0): - return PathPreferences.preferences().GetFloat(cls.DefaultHoldingTagRadius, ifNotSet) + return PathPreferences.preferences().GetFloat( + cls.DefaultHoldingTagRadius, ifNotSet + ) def __init__(self): if FreeCAD.GuiUp: import FreeCADGui - self.form = FreeCADGui.PySideUic.loadUi(":/preferences/PathDressupHoldingTags.ui") - self.label = translate("Path_DressupTag", 'Holding Tag') + + self.form = FreeCADGui.PySideUic.loadUi( + ":/preferences/PathDressupHoldingTags.ui" + ) + self.label = translate("Path_DressupTag", "Holding Tag") def loadSettings(self): - self.form.ifWidth.setText(FreeCAD.Units.Quantity(self.defaultWidth(0), FreeCAD.Units.Length).UserString) - self.form.ifHeight.setText(FreeCAD.Units.Quantity(self.defaultHeight(0), FreeCAD.Units.Length).UserString) + self.form.ifWidth.setText( + FreeCAD.Units.Quantity( + self.defaultWidth(0), FreeCAD.Units.Length + ).UserString + ) + self.form.ifHeight.setText( + FreeCAD.Units.Quantity( + self.defaultHeight(0), FreeCAD.Units.Length + ).UserString + ) self.form.dsbAngle.setValue(self.defaultAngle()) - self.form.ifRadius.setText(FreeCAD.Units.Quantity(self.defaultRadius(), FreeCAD.Units.Length).UserString) + self.form.ifRadius.setText( + FreeCAD.Units.Quantity( + self.defaultRadius(), FreeCAD.Units.Length + ).UserString + ) self.form.sbCount.setValue(self.defaultCount()) def saveSettings(self): pref = PathPreferences.preferences() - pref.SetFloat(self.DefaultHoldingTagWidth, FreeCAD.Units.Quantity(self.form.ifWidth.text()).Value) - pref.SetFloat(self.DefaultHoldingTagHeight, FreeCAD.Units.Quantity(self.form.ifHeight.text()).Value) + pref.SetFloat( + self.DefaultHoldingTagWidth, + FreeCAD.Units.Quantity(self.form.ifWidth.text()).Value, + ) + pref.SetFloat( + self.DefaultHoldingTagHeight, + FreeCAD.Units.Quantity(self.form.ifHeight.text()).Value, + ) pref.SetFloat(self.DefaultHoldingTagAngle, self.form.dsbAngle.value()) - pref.SetFloat(self.DefaultHoldingTagRadius, FreeCAD.Units.Quantity(self.form.ifRadius.text())) + pref.SetFloat( + self.DefaultHoldingTagRadius, + FreeCAD.Units.Quantity(self.form.ifRadius.text()), + ) pref.SetUnsigned(self.DefaultHoldingTagCount, self.form.sbCount.value()) @classmethod def preferencesPage(cls): return HoldingTagPreferences() + PathPreferencesPathDressup.RegisterDressup(HoldingTagPreferences) diff --git a/src/Mod/Path/PathScripts/PathFeatureExtensions.py b/src/Mod/Path/PathScripts/PathFeatureExtensions.py index d3b7ead79c..dc0d9cf23f 100644 --- a/src/Mod/Path/PathScripts/PathFeatureExtensions.py +++ b/src/Mod/Path/PathScripts/PathFeatureExtensions.py @@ -28,7 +28,8 @@ import math # lazily loaded modules from lazy_loader.lazy_loader import LazyLoader -PathUtils = LazyLoader('PathScripts.PathUtils', globals(), 'PathScripts.PathUtils') + +PathUtils = LazyLoader("PathScripts.PathUtils", globals(), "PathScripts.PathUtils") from PySide import QtCore @@ -47,7 +48,7 @@ def translate(context, text, disambig=None): def endPoints(edgeOrWire): - '''endPoints(edgeOrWire) ... return the first and last point of the wire or the edge, assuming the argument is not a closed wire.''' + """endPoints(edgeOrWire) ... return the first and last point of the wire or the edge, assuming the argument is not a closed wire.""" if Part.Wire == type(edgeOrWire): # edges = edgeOrWire.Edges pts = [e.valueAt(e.FirstParameter) for e in edgeOrWire.Edges] @@ -69,7 +70,7 @@ def endPoints(edgeOrWire): def includesPoint(p, pts): - '''includesPoint(p, pts) ... answer True if the collection of pts includes the point p''' + """includesPoint(p, pts) ... answer True if the collection of pts includes the point p""" for pt in pts: if PathGeom.pointsCoincide(p, pt): return True @@ -78,11 +79,13 @@ def includesPoint(p, pts): def selectOffsetWire(feature, wires): - '''selectOffsetWire(feature, wires) ... returns the Wire in wires which is does not intersect with feature''' + """selectOffsetWire(feature, wires) ... returns the Wire in wires which is does not intersect with feature""" closest = None for w in wires: dist = feature.distToShape(w)[0] - if closest is None or dist > closest[0]: # pylint: disable=unsubscriptable-object + if ( + closest is None or dist > closest[0] + ): # pylint: disable=unsubscriptable-object closest = (dist, w) if closest is not None: @@ -92,19 +95,23 @@ def selectOffsetWire(feature, wires): def extendWire(feature, wire, length): - '''extendWire(wire, length) ... return a closed Wire which extends wire by length''' + """extendWire(wire, length) ... return a closed Wire which extends wire by length""" PathLog.track(length) if not length or length == 0: return None - + try: off2D = wire.makeOffset2D(length) except FreeCAD.Base.FreeCADError as ee: return None endPts = endPoints(wire) # Assumes wire is NOT closed if endPts: - edges = [e for e in off2D.Edges if Part.Circle != type(e.Curve) or not includesPoint(e.Curve.Center, endPts)] + edges = [ + e + for e in off2D.Edges + if Part.Circle != type(e.Curve) or not includesPoint(e.Curve.Center, endPts) + ] wires = [Part.Wire(e) for e in Part.sortEdges(edges)] offset = selectOffsetWire(feature, wires) ePts = endPoints(offset) @@ -127,12 +134,14 @@ def extendWire(feature, wire, length): def createExtension(obj, extObj, extFeature, extSub): - return Extension(obj, - extObj, - extFeature, - extSub, - obj.ExtensionLengthDefault, - Extension.DirectionNormal) + return Extension( + obj, + extObj, + extFeature, + extSub, + obj.ExtensionLengthDefault, + Extension.DirectionNormal, + ) def readObjExtensionFeature(obj): @@ -142,7 +151,7 @@ def readObjExtensionFeature(obj): for extObj, features in obj.ExtensionFeature: for sub in features: - extFeature, extSub = sub.split(':') + extFeature, extSub = sub.split(":") extensions.append((extObj.Name, extFeature, extSub)) return extensions @@ -154,7 +163,7 @@ def getExtensions(obj): for extObj, features in obj.ExtensionFeature: for sub in features: - extFeature, extSub = sub.split(':') + extFeature, extSub = sub.split(":") extensions.append(createExtension(obj, extObj, extFeature, extSub)) i = i + 1 return extensions @@ -167,11 +176,14 @@ def setExtensions(obj, extensions): class Extension(object): DirectionNormal = 0 - DirectionX = 1 - DirectionY = 2 + DirectionX = 1 + DirectionY = 2 def __init__(self, op, obj, feature, sub, length, direction): - PathLog.debug("Extension(%s, %s, %s, %.2f, %s" % (obj.Label, feature, sub, length, direction)) + PathLog.debug( + "Extension(%s, %s, %s, %.2f, %s" + % (obj.Label, feature, sub, length, direction) + ) self.op = op self.obj = obj self.feature = feature @@ -197,8 +209,16 @@ class Extension(object): off = self.length.Value * direction e2.translate(off) e2 = PathGeom.flipEdge(e2) - e1 = Part.Edge(Part.LineSegment(e0.valueAt(e0.LastParameter), e2.valueAt(e2.FirstParameter))) - e3 = Part.Edge(Part.LineSegment(e2.valueAt(e2.LastParameter), e0.valueAt(e0.FirstParameter))) + e1 = Part.Edge( + Part.LineSegment( + e0.valueAt(e0.LastParameter), e2.valueAt(e2.FirstParameter) + ) + ) + e3 = Part.Edge( + Part.LineSegment( + e2.valueAt(e2.LastParameter), e0.valueAt(e0.FirstParameter) + ) + ) wire = Part.Wire([e0, e1, e2, e3]) self.wire = wire return wire @@ -206,8 +226,8 @@ class Extension(object): return extendWire(feature, Part.Wire([e0]), self.length.Value) def _getEdgeNumbers(self): - if 'Wire' in self.sub: - numbers = [nr for nr in self.sub[5:-1].split(',')] + if "Wire" in self.sub: + numbers = [nr for nr in self.sub[5:-1].split(",")] else: numbers = [self.sub[4:]] @@ -235,7 +255,7 @@ class Extension(object): e0 = wire.Edges[0] midparam = e0.FirstParameter + 0.5 * (e0.LastParameter - e0.FirstParameter) tangent = e0.tangentAt(midparam) - PathLog.track('tangent', tangent, self.feature, self.sub) + PathLog.track("tangent", tangent, self.feature, self.sub) normal = tangent.cross(FreeCAD.Vector(0, 0, 1)) if PathGeom.pointsCoincide(normal, FreeCAD.Vector(0, 0, 0)): return None @@ -243,21 +263,21 @@ class Extension(object): return self._getDirectedNormal(e0.valueAt(midparam), normal.normalize()) def getExtensionFaces(self, extensionWire): - '''getExtensionFace(extensionWire)... + """getExtensionFace(extensionWire)... A public helper method to retrieve the requested extension as a face, rather than a wire because some extensions require a face shape for definition that allows for two wires for boundary definition. - ''' + """ if self.extFaces: return self.extFaces - + return [Part.Face(extensionWire)] def getWire(self): - '''getWire()... Public method to retrieve the extension area, pertaining to the feature + """getWire()... Public method to retrieve the extension area, pertaining to the feature and sub element provided at class instantiation, as a closed wire. If no closed wire - is possible, a `None` value is returned.''' + is possible, a `None` value is returned.""" if self.sub[:6] == "Avoid_": feature = self.obj.Shape.getElement(self.feature) @@ -281,9 +301,9 @@ class Extension(object): return self._getRegularWire() def _getRegularWire(self): - '''_getRegularWire()... Private method to retrieve the extension area, pertaining to the feature + """_getRegularWire()... Private method to retrieve the extension area, pertaining to the feature and sub element provided at class instantiation, as a closed wire. If no closed wire - is possible, a `None` value is returned.''' + is possible, a `None` value is returned.""" PathLog.track() length = self.length.Value @@ -314,11 +334,23 @@ class Extension(object): # assuming the offset produces a valid circle - go for it if r > 0: - e3 = Part.makeCircle(r, circle.Center, circle.Axis, edge.FirstParameter * 180 / math.pi, edge.LastParameter * 180 / math.pi) + e3 = Part.makeCircle( + r, + circle.Center, + circle.Axis, + edge.FirstParameter * 180 / math.pi, + edge.LastParameter * 180 / math.pi, + ) if endPoints(edge): # need to construct the arc slice - e0 = Part.makeLine(edge.valueAt(edge.FirstParameter), e3.valueAt(e3.FirstParameter)) - e2 = Part.makeLine(edge.valueAt(edge.LastParameter), e3.valueAt(e3.LastParameter)) + e0 = Part.makeLine( + edge.valueAt(edge.FirstParameter), + e3.valueAt(e3.FirstParameter), + ) + e2 = Part.makeLine( + edge.valueAt(edge.LastParameter), + e3.valueAt(e3.LastParameter), + ) return Part.Wire([e0, edge, e2, e3]) extWire = Part.Wire([e3]) @@ -358,7 +390,7 @@ class Extension(object): off2D = sub.makeOffset2D(length) except FreeCAD.Base.FreeCADError as ee: return None - + if isOutside: self.extFaces = [Part.Face(off2D).cut(featFace)] else: @@ -369,9 +401,9 @@ class Extension(object): return extendWire(feature, sub, length) def _getOutlineWire(self): - '''_getOutlineWire()... Private method to retrieve an extended outline extension area, + """_getOutlineWire()... Private method to retrieve an extended outline extension area, pertaining to the feature and sub element provided at class instantiation, as a closed wire. - If no closed wire is possible, a `None` value is returned.''' + If no closed wire is possible, a `None` value is returned.""" PathLog.track() baseShape = self.obj.Shape @@ -411,10 +443,10 @@ class Extension(object): return None def _getWaterlineWire(self): - '''_getWaterlineWire()... Private method to retrieve a waterline extension area, + """_getWaterlineWire()... Private method to retrieve a waterline extension area, pertaining to the feature and sub element provided at class instantiation, as a closed wire. Only waterline faces touching source face are returned as part of the waterline extension area. - If no closed wire is possible, a `None` value is returned.''' + If no closed wire is possible, a `None` value is returned.""" PathLog.track() msg = translate("PathFeatureExtensions", "Waterline error") @@ -451,9 +483,9 @@ class Extension(object): return None def _makeCircularExtFace(self, edge, extWire): - '''_makeCircularExtensionFace(edge, extWire)... + """_makeCircularExtensionFace(edge, extWire)... Create proper circular extension face shape. Incoming edge is expected to be a circle. - ''' + """ # Add original outer wire to cut faces if necessary edgeFace = Part.Face(Part.Wire([edge])) edgeFace.translate(FreeCAD.Vector(0.0, 0.0, 0.0 - edgeFace.BoundBox.ZMin)) @@ -467,37 +499,60 @@ class Extension(object): extensionFace.translate(FreeCAD.Vector(0.0, 0.0, edge.BoundBox.ZMin)) return extensionFace + + # Eclass def initialize_properties(obj): """initialize_properties(obj)... Adds feature properties to object argument""" - if not hasattr(obj, 'ExtensionLengthDefault'): - obj.addProperty('App::PropertyDistance', 'ExtensionLengthDefault', 'Extension', QtCore.QT_TRANSLATE_NOOP('PathPocketShape', 'Default length of extensions.')) - if not hasattr(obj, 'ExtensionFeature'): - obj.addProperty('App::PropertyLinkSubListGlobal', 'ExtensionFeature', 'Extension', QtCore.QT_TRANSLATE_NOOP('PathPocketShape', 'List of features to extend.')) - if not hasattr(obj, 'ExtensionCorners'): - obj.addProperty('App::PropertyBool', 'ExtensionCorners', 'Extension', QtCore.QT_TRANSLATE_NOOP('PathPocketShape', 'When enabled connected extension edges are combined to wires.')) + if not hasattr(obj, "ExtensionLengthDefault"): + obj.addProperty( + "App::PropertyDistance", + "ExtensionLengthDefault", + "Extension", + QtCore.QT_TRANSLATE_NOOP( + "PathPocketShape", "Default length of extensions." + ), + ) + if not hasattr(obj, "ExtensionFeature"): + obj.addProperty( + "App::PropertyLinkSubListGlobal", + "ExtensionFeature", + "Extension", + QtCore.QT_TRANSLATE_NOOP("PathPocketShape", "List of features to extend."), + ) + if not hasattr(obj, "ExtensionCorners"): + obj.addProperty( + "App::PropertyBool", + "ExtensionCorners", + "Extension", + QtCore.QT_TRANSLATE_NOOP( + "PathPocketShape", + "When enabled connected extension edges are combined to wires.", + ), + ) obj.ExtensionCorners = True - obj.setEditorMode('ExtensionFeature', 2) + obj.setEditorMode("ExtensionFeature", 2) def set_default_property_values(obj, job): """set_default_property_values(obj, job) ... set default values for feature properties""" obj.ExtensionCorners = True - obj.setExpression('ExtensionLengthDefault', 'OpToolDiameter / 2.0') + obj.setExpression("ExtensionLengthDefault", "OpToolDiameter / 2.0") def SetupProperties(): """SetupProperties()... Returns list of feature property names""" - setup = ['ExtensionLengthDefault', 'ExtensionFeature', - 'ExtensionCorners'] + setup = ["ExtensionLengthDefault", "ExtensionFeature", "ExtensionCorners"] return setup # Extend outline face generation function -def getExtendOutlineFace(base_shape, face, extension, remHoles=False, offset_tolerance=1e-4): +def getExtendOutlineFace( + base_shape, face, extension, remHoles=False, offset_tolerance=1e-4 +): """getExtendOutlineFace(obj, base_shape, face, extension, remHoles) ... Creates an extended face for the pocket, taking into consideration lateral collision with the greater base shape. @@ -513,11 +568,9 @@ def getExtendOutlineFace(base_shape, face, extension, remHoles=False, offset_tol """ # Make offset face per user-specified extension distance so as to allow full clearing of face where possible. - offset_face = PathUtils.getOffsetArea(face, - extension, - removeHoles=remHoles, - plane=face, - tolerance=offset_tolerance) + offset_face = PathUtils.getOffsetArea( + face, extension, removeHoles=remHoles, plane=face, tolerance=offset_tolerance + ) if not offset_face: PathLog.error("Failed to offset a selected face.") return None @@ -540,9 +593,11 @@ def getExtendOutlineFace(base_shape, face, extension, remHoles=False, offset_tol for f in available.Faces: bbx = f.BoundBox zNorm = abs(f.normalAt(0.0, 0.0).z) - if (PathGeom.isRoughly(zNorm, 1.0) and - PathGeom.isRoughly(bbx.ZMax - bbx.ZMin, 0.0) and - PathGeom.isRoughly(bbx.ZMin, face.BoundBox.ZMin)): + if ( + PathGeom.isRoughly(zNorm, 1.0) + and PathGeom.isRoughly(bbx.ZMax - bbx.ZMin, 0.0) + and PathGeom.isRoughly(bbx.ZMin, face.BoundBox.ZMin) + ): if bbx.ZMin < zmin: bottom_faces.append(f) @@ -561,6 +616,7 @@ def getExtendOutlineFace(base_shape, face, extension, remHoles=False, offset_tol PathLog.error("No bottom face for extend outline.") return None + # Waterline extension face generation function def getWaterlineFace(base_shape, face): """getWaterlineFace(base_shape, face) ... @@ -580,8 +636,11 @@ def getWaterlineFace(base_shape, face): step_down=math.floor(faceHeight - baseBB.ZMin + 2.0), z_finish_step=0.0, final_depth=baseBB.ZMin, - user_depths=None) - env = PathUtils.getEnvelope(partshape=base_shape, subshape=None, depthparams=depthparams) + user_depths=None, + ) + env = PathUtils.getEnvelope( + partshape=base_shape, subshape=None, depthparams=depthparams + ) # Get top face(s) of envelope at face height rawList = list() for f in env.Faces: @@ -589,7 +648,9 @@ def getWaterlineFace(base_shape, face): rawList.append(f) # make compound and extrude downward rawComp = Part.makeCompound(rawList) - rawCompExtNeg = rawComp.extrude(FreeCAD.Vector(0.0, 0.0, baseBB.ZMin - faceHeight - 1.0)) + rawCompExtNeg = rawComp.extrude( + FreeCAD.Vector(0.0, 0.0, baseBB.ZMin - faceHeight - 1.0) + ) # Cut off bottom of base shape at face height topSolid = base_shape.cut(rawCompExtNeg) diff --git a/src/Mod/Path/PathScripts/PathFeatureExtensionsGui.py b/src/Mod/Path/PathScripts/PathFeatureExtensionsGui.py index 89d3db6e40..91690ba627 100644 --- a/src/Mod/Path/PathScripts/PathFeatureExtensionsGui.py +++ b/src/Mod/Path/PathScripts/PathFeatureExtensionsGui.py @@ -33,22 +33,26 @@ from pivy import coin # lazily loaded modules from lazy_loader.lazy_loader import LazyLoader -Part = LazyLoader('Part', globals(), 'Part') + +Part = LazyLoader("Part", globals(), "Part") __title__ = "Path Feature Extensions UI" __author__ = "sliptonic (Brad Collette)" __url__ = "https://www.freecadweb.org" __doc__ = "Extensions feature page controller." + def translate(context, text, disambig=None): return QtCore.QCoreApplication.translate(context, text, disambig) + PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -#PathLog.trackModule(PathLog.thisModule()) +# PathLog.trackModule(PathLog.thisModule()) + class _Extension(object): - ColourEnabled = (1.0, .5, 1.0) - ColourDisabled = (1.0, 1.0, .5) + ColourEnabled = (1.0, 0.5, 1.0) + ColourDisabled = (1.0, 1.0, 0.5) TransparencySelected = 0.0 TransparencyDeselected = 0.7 @@ -152,7 +156,7 @@ class _Extension(object): if self.switch: self.switch.whichChild = coin.SO_SWITCH_NONE - def enable(self, ena = True): + def enable(self, ena=True): if ena: self.material.diffuseColor = self.ColourEnabled else: @@ -173,10 +177,10 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): DataSwitch = QtCore.Qt.ItemDataRole.UserRole + 2 Direction = { - FeatureExtensions.Extension.DirectionNormal: translate('PathPocket', 'Normal'), - FeatureExtensions.Extension.DirectionX: translate('PathPocket', 'X'), - FeatureExtensions.Extension.DirectionY: translate('PathPocket', 'Y') - } + FeatureExtensions.Extension.DirectionNormal: translate("PathPocket", "Normal"), + FeatureExtensions.Extension.DirectionX: translate("PathPocket", "X"), + FeatureExtensions.Extension.DirectionY: translate("PathPocket", "Y"), + } def initPage(self, obj): self.setTitle("Extensions") @@ -193,17 +197,21 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): self.extensions = list() - self.defaultLength = PathGui.QuantitySpinBox(self.form.defaultLength, obj, 'ExtensionLengthDefault') # pylint: disable=attribute-defined-outside-init + self.defaultLength = PathGui.QuantitySpinBox( + self.form.defaultLength, obj, "ExtensionLengthDefault" + ) # pylint: disable=attribute-defined-outside-init self.form.extensionTree.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) self.form.extensionTree.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) - self.switch = coin.SoSwitch() # pylint: disable=attribute-defined-outside-init + self.switch = coin.SoSwitch() # pylint: disable=attribute-defined-outside-init self.obj.ViewObject.RootNode.addChild(self.switch) self.switch.whichChild = coin.SO_SWITCH_ALL - self.model = QtGui.QStandardItemModel(self.form.extensionTree) # pylint: disable=attribute-defined-outside-init - self.model.setHorizontalHeaderLabels(['Base', 'Extension']) + self.model = QtGui.QStandardItemModel( + self.form.extensionTree + ) # pylint: disable=attribute-defined-outside-init + self.model.setHorizontalHeaderLabels(["Base", "Extension"]) """ # russ4262: This `if` block shows all available extensions upon edit of operation with any extension enabled. @@ -216,7 +224,7 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): """ self.form.showExtensions.setCheckState(QtCore.Qt.Unchecked) - self.blockUpdateData = False # pylint: disable=attribute-defined-outside-init + self.blockUpdateData = False # pylint: disable=attribute-defined-outside-init def cleanupPage(self, obj): try: @@ -243,29 +251,33 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): def currentExtensions(self): PathLog.debug("currentExtensions()") extensions = [] + def extractExtension(item, ext): if ext and ext.edge and item.checkState() == QtCore.Qt.Checked: extensions.append(ext.ext) + if self.form.enableExtensions.isChecked(): self.forAllItemsCall(extractExtension) - PathLog.track('extensions', extensions) + PathLog.track("extensions", extensions) return extensions def updateProxyExtensions(self, obj): PathLog.debug("updateProxyExtensions()") - self.extensions = self.currentExtensions() # pylint: disable=attribute-defined-outside-init + self.extensions = ( + self.currentExtensions() + ) # pylint: disable=attribute-defined-outside-init FeatureExtensions.setExtensions(obj, self.extensions) def getFields(self, obj): PathLog.track(obj.Label, self.model.rowCount(), self.model.columnCount()) - self.blockUpdateData = True # pylint: disable=attribute-defined-outside-init + self.blockUpdateData = True # pylint: disable=attribute-defined-outside-init if obj.ExtensionCorners != self.form.extendCorners.isChecked(): obj.ExtensionCorners = self.form.extendCorners.isChecked() self.defaultLength.updateProperty() self.updateProxyExtensions(obj) - self.blockUpdateData = False # pylint: disable=attribute-defined-outside-init + self.blockUpdateData = False # pylint: disable=attribute-defined-outside-init def setFields(self, obj): PathLog.track(obj.Label) @@ -285,7 +297,9 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): Subroutine called inside `setFields()` to initialize Extensions efficiently.""" if self.enabled: self.extensions = FeatureExtensions.getExtensions(obj) - elif len(obj.ExtensionFeature) > 0: # latter test loads pre-existing extensions (editing of existing operation) + elif ( + len(obj.ExtensionFeature) > 0 + ): # latter test loads pre-existing extensions (editing of existing operation) noEdges = True for (__, __, subFeat) in FeatureExtensions.readObjExtensionFeature(obj): if subFeat.startswith("Edge") or subFeat.startswith("Wire"): @@ -311,7 +325,9 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): self._enableExtensions() # Recalculate extensions def createItemForBaseModel(self, base, sub, edges, extensions): - PathLog.track(base.Label, sub, '+', len(edges), len(base.Shape.getElement(sub).Edges)) + PathLog.track( + base.Label, sub, "+", len(edges), len(base.Shape.getElement(sub).Edges) + ) # PathLog.debug("createItemForBaseModel() label: {}, sub: {}, {}, edgeCnt: {}, subEdges: {}".format(base.Label, sub, '+', len(edges), len(base.Shape.getElement(sub).Edges))) extendCorners = self.form.extendCorners.isChecked() @@ -341,7 +357,7 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): extensionEdges = {} if includeEdges: - if self.useOutline == 1 and sub.startswith('Face'): + if self.useOutline == 1 and sub.startswith("Face"): # Only show exterior extensions if `Use Outline` is True subEdges = subShape.Wires[0].Edges else: @@ -358,26 +374,48 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): createSubItem(label, ext1) if extendCorners and includeEdges: + def edgesMatchShape(e0, e1): flipped = PathGeom.flipEdge(e1) if flipped: - return PathGeom.edgesMatch(e0, e1) or PathGeom.edgesMatch(e0, flipped) + return PathGeom.edgesMatch(e0, e1) or PathGeom.edgesMatch( + e0, flipped + ) else: return PathGeom.edgesMatch(e0, e1) self.extensionEdges = extensionEdges PathLog.debug("extensionEdges.values(): {}".format(extensionEdges.values())) - for edgeList in Part.sortEdges(list(extensionEdges.keys())): # Identify connected edges that form wires + for edgeList in Part.sortEdges( + list(extensionEdges.keys()) + ): # Identify connected edges that form wires self.edgeList = edgeList if len(edgeList) == 1: - label = "Edge%s" % [extensionEdges[keyEdge] for keyEdge in extensionEdges.keys() if edgesMatchShape(keyEdge, edgeList[0])][0] + label = ( + "Edge%s" + % [ + extensionEdges[keyEdge] + for keyEdge in extensionEdges.keys() + if edgesMatchShape(keyEdge, edgeList[0]) + ][0] + ) else: - label = "Wire(%s)" % ','.join(sorted([extensionEdges[keyEdge] for e in edgeList for keyEdge in extensionEdges.keys() if edgesMatchShape(e, keyEdge)], key=lambda s: int(s))) # pylint: disable=unnecessary-lambda + label = "Wire(%s)" % ",".join( + sorted( + [ + extensionEdges[keyEdge] + for e in edgeList + for keyEdge in extensionEdges.keys() + if edgesMatchShape(e, keyEdge) + ], + key=lambda s: int(s), + ) + ) # pylint: disable=unnecessary-lambda ext2 = self._cachedExtension(self.obj, base, sub, label) createSubItem(label, ext2) # Only add these subItems for horizontally oriented faces, not edges or vertical faces (from vertical face loops) - if sub.startswith('Face') and PathGeom.isHorizontal(subShape): + if sub.startswith("Face") and PathGeom.isHorizontal(subShape): # Add entry to extend outline of face label = "Extend_" + sub ext3 = self._cachedExtension(self.obj, base, sub, label) @@ -406,8 +444,11 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): self.form.extensionTree.blockSignals(True) # remember current visual state - if hasattr(self, 'selectionModel'): - selectedExtensions = [self.model.itemFromIndex(index).data(self.DataObject).ext for index in self.selectionModel.selectedIndexes()] + if hasattr(self, "selectionModel"): + selectedExtensions = [ + self.model.itemFromIndex(index).data(self.DataObject).ext + for index in self.selectionModel.selectedIndexes() + ] else: selectedExtensions = [] collapsedModels = [] @@ -420,7 +461,9 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): for featureRow in range(model.rowCount()): feature = model.child(featureRow, 0) if not self.form.extensionTree.isExpanded(feature.index()): - collapsedFeatures.append("%s.%s" % (modelName, feature.data(QtCore.Qt.EditRole))) + collapsedFeatures.append( + "%s.%s" % (modelName, feature.data(QtCore.Qt.EditRole)) + ) # remove current extensions and all their visuals def removeItemSwitch(item, ext): @@ -428,6 +471,7 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): ext.hide() if ext.root: self.switch.removeChild(ext.root) + self.forAllItemsCall(removeItemSwitch) self.model.clear() @@ -435,14 +479,19 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): if self.enabled: for base in self.obj.Base: show = False - edges = [(edge, "Edge%d" % (i + 1)) for i, edge in enumerate(base[0].Shape.Edges)] + edges = [ + (edge, "Edge%d" % (i + 1)) + for i, edge in enumerate(base[0].Shape.Edges) + ] baseItem = QtGui.QStandardItem() baseItem.setData(base[0].Label, QtCore.Qt.EditRole) baseItem.setSelectable(False) for sub in sorted(base[1]): - if sub.startswith('Face'): + if sub.startswith("Face"): show = True - baseItem.appendRow(self.createItemForBaseModel(base[0], sub, edges, extensions)) + baseItem.appendRow( + self.createItemForBaseModel(base[0], sub, edges, extensions) + ) if show: self.model.appendRow(baseItem) @@ -458,10 +507,10 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): self.form.extensionTree.setExpanded(model.index(), False) for featureRow in range(model.rowCount()): feature = model.child(featureRow, 0) - featureName = "%s.%s" % (modelName, feature.data(QtCore.Qt.EditRole)) + featureName = "%s.%s" % (modelName, feature.data(QtCore.Qt.EditRole)) if featureName in collapsedFeatures: self.form.extensionTree.setExpanded(feature.index(), False) - if hasattr(self, 'selectionModel') and selectedExtensions: + if hasattr(self, "selectionModel") and selectedExtensions: self.restoreSelection(selectedExtensions) self.form.extensionTree.blockSignals(False) @@ -475,22 +524,22 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): if not self.blockUpdateData: if self.fieldsSet: if self.form.enableExtensions.isChecked(): - if prop == 'ExtensionLengthDefault': - self.updateQuantitySpinBoxes() - elif prop == 'Base': - self.extensionsReady = False - self.setExtensions(FeatureExtensions.getExtensions(obj)) - elif prop == 'UseOutline': - self._getUseOutlineState() # Find `useOutline` checkbox and get its boolean value - self._includeEdgesAndWires() - elif prop == 'Base': + if prop == "ExtensionLengthDefault": + self.updateQuantitySpinBoxes() + elif prop == "Base": self.extensionsReady = False + self.setExtensions(FeatureExtensions.getExtensions(obj)) + elif prop == "UseOutline": + self._getUseOutlineState() # Find `useOutline` checkbox and get its boolean value + self._includeEdgesAndWires() + elif prop == "Base": + self.extensionsReady = False def restoreSelection(self, selection): PathLog.debug("restoreSelection()") PathLog.track() if 0 == self.model.rowCount(): - PathLog.track('-') + PathLog.track("-") self.form.buttonClear.setEnabled(False) self.form.buttonDisable.setEnabled(False) self.form.buttonEnable.setEnabled(False) @@ -515,7 +564,9 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): def setSelectionVisuals(item, ext): if selectItem(item, ext): - self.selectionModel.select(item.index(), QtCore.QItemSelectionModel.Select) + self.selectionModel.select( + item.index(), QtCore.QItemSelectionModel.Select + ) selected = self.selectionModel.isSelected(item.index()) if selected: @@ -528,6 +579,7 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): ext.show() else: ext.hide() + self.forAllItemsCall(setSelectionVisuals) def selectionChanged(self): @@ -536,6 +588,7 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): def extensionsClear(self): PathLog.debug("extensionsClear()") + def disableItem(item, ext): item.setCheckState(QtCore.Qt.Unchecked) ext.disable() @@ -572,16 +625,20 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): def showHideExtension(self): if self.form.showExtensions.isChecked(): + def enableExtensionEdit(item, ext): # pylint: disable=unused-argument ext.show() + self.forAllItemsCall(enableExtensionEdit) else: + def disableExtensionEdit(item, ext): if not self.selectionModel.isSelected(item.index()): ext.hide() + self.forAllItemsCall(disableExtensionEdit) - #self.setDirty() + # self.setDirty() def toggleExtensionCorners(self): PathLog.debug("toggleExtensionCorners()") @@ -612,7 +669,9 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): self.model.itemChanged.connect(self.updateItemEnabled) - self.selectionModel = self.form.extensionTree.selectionModel() # pylint: disable=attribute-defined-outside-init + self.selectionModel = ( + self.form.extensionTree.selectionModel() + ) # pylint: disable=attribute-defined-outside-init self.selectionModel.selectionChanged.connect(self.selectionChanged) self.selectionChanged() @@ -626,12 +685,14 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): if self.useOutlineCheckbox: self.useOutline = self.useOutlineCheckbox.isChecked() - if hasattr(self, 'parent'): - parent = getattr(self, 'parent') - if parent and hasattr(parent, 'featurePages'): + if hasattr(self, "parent"): + parent = getattr(self, "parent") + if parent and hasattr(parent, "featurePages"): for page in parent.featurePages: - if hasattr(page, 'panelTitle'): - if page.panelTitle == 'Operation' and hasattr(page.form, 'useOutline'): + if hasattr(page, "panelTitle"): + if page.panelTitle == "Operation" and hasattr( + page.form, "useOutline" + ): PathLog.debug("Found useOutline checkbox") self.useOutlineCheckbox = page.form.useOutline if page.form.useOutline.isChecked(): @@ -669,10 +730,14 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): self.form.includeEdges.blockSignals(True) # Make changes to form - msg = translate("PathPocketShape", - "Edge count greater than threshold of" + " " + - str(self.edgeCountThreshold) + ": " + - str(self.initialEdgeCount)) + msg = translate( + "PathPocketShape", + "Edge count greater than threshold of" + + " " + + str(self.edgeCountThreshold) + + ": " + + str(self.initialEdgeCount), + ) self.form.enableExtensionsWarning.setText(msg) self.form.enableExtensions.setChecked(False) self.form.enableExtensionsWarning.show() @@ -763,6 +828,8 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): reset[k] = self.extensionsCache[k] self.extensionsCache = reset self.extensionsReady = False + + # Eclass FreeCAD.Console.PrintLog("Loading PathFeatureExtensionsGui... done\n") diff --git a/src/Mod/Path/PathScripts/PathJobCmd.py b/src/Mod/Path/PathScripts/PathJobCmd.py index a3b26ac128..0e4ea2607c 100644 --- a/src/Mod/Path/PathScripts/PathJobCmd.py +++ b/src/Mod/Path/PathScripts/PathJobCmd.py @@ -44,20 +44,24 @@ PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) class CommandJobCreate: - ''' + """ Command used to create a command. When activated the command opens a dialog allowing the user to select a base object (has to be a solid) and a template to be used for the initial creation. - ''' + """ def __init__(self): pass def GetResources(self): - return {'Pixmap': 'Path_Job', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Job", "Job"), - 'Accel': "P, J", - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Job", "Creates a Path Job object")} + return { + "Pixmap": "Path_Job", + "MenuText": QtCore.QT_TRANSLATE_NOOP("Path_Job", "Job"), + "Accel": "P, J", + "ToolTip": QtCore.QT_TRANSLATE_NOOP( + "Path_Job", "Creates a Path Job object" + ), + } def IsActive(self): return FreeCAD.ActiveDocument is not None @@ -74,29 +78,35 @@ class CommandJobCreate: @classmethod def Execute(cls, base, template): - FreeCADGui.addModule('PathScripts.PathJobGui') + FreeCADGui.addModule("PathScripts.PathJobGui") if template: template = "'%s'" % template else: - template = 'None' - FreeCADGui.doCommand('PathScripts.PathJobGui.Create(%s, %s)' % ([o.Name for o in base], template)) + template = "None" + FreeCADGui.doCommand( + "PathScripts.PathJobGui.Create(%s, %s)" % ([o.Name for o in base], template) + ) class CommandJobTemplateExport: - ''' + """ Command to export a template of a given job. Opens a dialog to select the file to store the template in. If the template is stored in Path's file path (see preferences) and named in accordance with job_*.json it will automatically be found on Job creation and be available for selection. - ''' + """ def __init__(self): pass def GetResources(self): - return {'Pixmap': 'Path_ExportTemplate', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Job", "Export Template"), - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Job", "Exports Path Job as a template to be used for other jobs")} + return { + "Pixmap": "Path_ExportTemplate", + "MenuText": QtCore.QT_TRANSLATE_NOOP("Path_Job", "Export Template"), + "ToolTip": QtCore.QT_TRANSLATE_NOOP( + "Path_Job", "Exports Path Job as a template to be used for other jobs" + ), + } def GetJob(self): # if there's only one Job in the document ... @@ -109,7 +119,7 @@ class CommandJobTemplateExport: sel = FreeCADGui.Selection.getSelection() if len(sel) == 1: job = sel[0] - if hasattr(job, 'Proxy') and isinstance(job.Proxy, PathJob.ObjectJob): + if hasattr(job, "Proxy") and isinstance(job.Proxy, PathJob.ObjectJob): return job return None @@ -124,15 +134,17 @@ class CommandJobTemplateExport: @classmethod def SaveDialog(cls, job, dialog): - foo = QtGui.QFileDialog.getSaveFileName(QtGui.QApplication.activeWindow(), - "Path - Job Template", - PathPreferences.filePath(), - "job_*.json")[0] + foo = QtGui.QFileDialog.getSaveFileName( + QtGui.QApplication.activeWindow(), + "Path - Job Template", + PathPreferences.filePath(), + "job_*.json", + )[0] if foo: - if not os.path.basename(foo).startswith('job_'): - foo = os.path.join(os.path.dirname(foo), 'job_' + os.path.basename(foo)) - if not foo.endswith('.json'): - foo = foo + '.json' + if not os.path.basename(foo).startswith("job_"): + foo = os.path.join(os.path.dirname(foo), "job_" + os.path.basename(foo)) + if not foo.endswith(".json"): + foo = foo + ".json" cls.Execute(job, foo, dialog) @classmethod @@ -155,7 +167,11 @@ class CommandJobTemplateExport: stockAttrs = None if dialog: if dialog.includeStock(): - stockAttrs = PathStock.TemplateAttributes(job.Stock, dialog.includeStockExtent(), dialog.includeStockPlacement()) + stockAttrs = PathStock.TemplateAttributes( + job.Stock, + dialog.includeStockExtent(), + dialog.includeStockPlacement(), + ) else: stockAttrs = PathStock.TemplateAttributes(job.Stock) if stockAttrs: @@ -169,7 +185,8 @@ class CommandJobTemplateExport: dialog.includeSettingCoolant(), dialog.includeSettingOperationHeights(), dialog.includeSettingOperationDepths(), - dialog.includeSettingOpsSettings()) + dialog.includeSettingOpsSettings(), + ) else: setupSheetAttrs = job.Proxy.setupSheet.templateAttributes(True, True, True) if setupSheetAttrs: @@ -177,13 +194,13 @@ class CommandJobTemplateExport: encoded = job.Proxy.setupSheet.encodeTemplateAttributes(attrs) # write template - with open(PathUtil.toUnicode(path), 'w') as fp: + with open(PathUtil.toUnicode(path), "w") as fp: json.dump(encoded, fp, sort_keys=True, indent=2) if FreeCAD.GuiUp: # register the FreeCAD command - FreeCADGui.addCommand('Path_Job', CommandJobCreate()) - FreeCADGui.addCommand('Path_ExportTemplate', CommandJobTemplateExport()) + FreeCADGui.addCommand("Path_Job", CommandJobCreate()) + FreeCADGui.addCommand("Path_ExportTemplate", CommandJobTemplateExport()) FreeCAD.Console.PrintLog("Loading PathJobCmd... done\n") diff --git a/src/Mod/Path/PathScripts/PathJobDlg.py b/src/Mod/Path/PathScripts/PathJobDlg.py index 3a87af21bf..cd15adf863 100644 --- a/src/Mod/Path/PathScripts/PathJobDlg.py +++ b/src/Mod/Path/PathScripts/PathJobDlg.py @@ -43,7 +43,6 @@ PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) class _ItemDelegate(QtGui.QStyledItemDelegate): - def __init__(self, controller, parent): self.controller = controller QtGui.QStyledItemDelegate.__init__(self, parent) @@ -54,15 +53,16 @@ class _ItemDelegate(QtGui.QStyledItemDelegate): self.controller.setupColumnEditor(index, editor) return editor + class JobCreate: DataObject = QtCore.Qt.ItemDataRole.UserRole def __init__(self, parent=None, sel=None): # pylint: disable=unused-argument self.dialog = FreeCADGui.PySideUic.loadUi(":/panels/DlgJobCreate.ui") - self.itemsSolid = QtGui.QStandardItem(translate('PathJob', 'Solids')) - self.items2D = QtGui.QStandardItem(translate('PathJob', '2D')) - self.itemsJob = QtGui.QStandardItem(translate('PathJob', 'Jobs')) + self.itemsSolid = QtGui.QStandardItem(translate("PathJob", "Solids")) + self.items2D = QtGui.QStandardItem(translate("PathJob", "2D")) + self.itemsJob = QtGui.QStandardItem(translate("PathJob", "Jobs")) self.dialog.templateGroup.hide() self.dialog.modelGroup.hide() # debugging support @@ -74,27 +74,40 @@ class JobCreate: def setupTitle(self, title): self.dialog.setWindowTitle(title) - def setupModel(self, job = None): + def setupModel(self, job=None): if job: - preSelected = Counter([PathUtil.getPublicObject(job.Proxy.baseObject(job, obj)).Label for obj in job.Model.Group]) + preSelected = Counter( + [ + PathUtil.getPublicObject(job.Proxy.baseObject(job, obj)).Label + for obj in job.Model.Group + ] + ) jobResources = job.Model.Group + [job.Stock] else: - preSelected = Counter([obj.Label for obj in FreeCADGui.Selection.getSelection()]) + preSelected = Counter( + [obj.Label for obj in FreeCADGui.Selection.getSelection()] + ) jobResources = [] - self.candidates = sorted(PathJob.ObjectJob.baseCandidates(), key=lambda o: o.Label) + self.candidates = sorted( + PathJob.ObjectJob.baseCandidates(), key=lambda o: o.Label + ) # If there is only one possibility we might as well make sure it's selected if not preSelected and 1 == len(self.candidates): preSelected = Counter([self.candidates[0].Label]) expandSolids = False - expand2Ds = False - expandJobs = False + expand2Ds = False + expandJobs = False for base in self.candidates: - if not base in jobResources and not PathJob.isResourceClone(job, base, None) and not hasattr(base, 'StockType'): + if ( + not base in jobResources + and not PathJob.isResourceClone(job, base, None) + and not hasattr(base, "StockType") + ): item0 = QtGui.QStandardItem() item1 = QtGui.QStandardItem() @@ -149,7 +162,7 @@ class JobCreate: self.delegate = _ItemDelegate(self, self.dialog.modelTree) self.model = QtGui.QStandardItemModel(self.dialog) - self.model.setHorizontalHeaderLabels(['Model', 'Count']) + self.model.setHorizontalHeaderLabels(["Model", "Count"]) if self.itemsSolid.hasChildren(): self.model.appendRow(self.itemsSolid) @@ -213,7 +226,9 @@ class JobCreate: def setupTemplate(self): templateFiles = [] for path in PathPreferences.searchPaths(): - cleanPaths = [f.replace("\\", "/") for f in self.templateFilesIn(path)] # Standardize slashes used across os platforms + cleanPaths = [ + f.replace("\\", "/") for f in self.templateFilesIn(path) + ] # Standardize slashes used across os platforms templateFiles.extend(cleanPaths) template = {} @@ -229,7 +244,7 @@ class JobCreate: template[name] = tFile selectTemplate = PathPreferences.defaultJobTemplate() index = 0 - self.dialog.jobTemplate.addItem('', '') + self.dialog.jobTemplate.addItem("", "") for name in sorted(template.keys()): if template[name] == selectTemplate: index = self.dialog.jobTemplate.count() @@ -238,16 +253,18 @@ class JobCreate: self.dialog.templateGroup.show() def templateFilesIn(self, path): - '''templateFilesIn(path) ... answer all file in the given directory which fit the job template naming convention. - PathJob template files are name job_*.json''' + """templateFilesIn(path) ... answer all file in the given directory which fit the job template naming convention. + PathJob template files are name job_*.json""" PathLog.track(path) - return glob.glob(path + '/job_*.json') + return glob.glob(path + "/job_*.json") def getModels(self): models = [] for i in range(self.itemsSolid.rowCount()): - for j in range(self.itemsSolid.child(i, 1).data(QtCore.Qt.EditRole)): # pylint: disable=unused-variable + for j in range( + self.itemsSolid.child(i, 1).data(QtCore.Qt.EditRole) + ): # pylint: disable=unused-variable models.append(self.itemsSolid.child(i).data(self.DataObject)) for i in range(self.items2D.rowCount()): @@ -259,12 +276,14 @@ class JobCreate: # Note that we do want to use the models (resource clones) of the # source job as base objects for the new job in order to get the # identical placement, and anything else that's been customized. - models.extend(self.itemsJob.child(i, 0).data(self.DataObject).Model.Group) + models.extend( + self.itemsJob.child(i, 0).data(self.DataObject).Model.Group + ) return models def getTemplate(self): - '''answer the file name of the template to be assigned''' + """answer the file name of the template to be assigned""" return self.dialog.jobTemplate.itemData(self.dialog.jobTemplate.currentIndex()) def exec_(self): @@ -298,24 +317,44 @@ class JobTemplateExport: def updateUI(self): job = self.job if job.PostProcessor: - ppHint = "%s %s %s" % (job.PostProcessor, job.PostProcessorArgs, job.PostProcessorOutputFile) + ppHint = "%s %s %s" % ( + job.PostProcessor, + job.PostProcessorArgs, + job.PostProcessorOutputFile, + ) self.dialog.postProcessingHint.setText(ppHint) else: self.dialog.postProcessingGroup.setEnabled(False) self.dialog.postProcessingGroup.setChecked(False) - if job.Stock and not PathJob.isResourceClone(job, 'Stock', 'Stock'): + if job.Stock and not PathJob.isResourceClone(job, "Stock", "Stock"): stockType = PathStock.StockType.FromStock(job.Stock) if stockType == PathStock.StockType.FromBase: - seHint = translate('PathJob', "Base -/+ %.2f/%.2f %.2f/%.2f %.2f/%.2f") % (job.Stock.ExtXneg, job.Stock.ExtXpos, job.Stock.ExtYneg, job.Stock.ExtYpos, job.Stock.ExtZneg, job.Stock.ExtZpos) + seHint = translate( + "PathJob", "Base -/+ %.2f/%.2f %.2f/%.2f %.2f/%.2f" + ) % ( + job.Stock.ExtXneg, + job.Stock.ExtXpos, + job.Stock.ExtYneg, + job.Stock.ExtYpos, + job.Stock.ExtZneg, + job.Stock.ExtZpos, + ) self.dialog.stockPlacement.setChecked(False) elif stockType == PathStock.StockType.CreateBox: - seHint = translate('PathJob', "Box: %.2f x %.2f x %.2f") % (job.Stock.Length, job.Stock.Width, job.Stock.Height) + seHint = translate("PathJob", "Box: %.2f x %.2f x %.2f") % ( + job.Stock.Length, + job.Stock.Width, + job.Stock.Height, + ) elif stockType == PathStock.StockType.CreateCylinder: - seHint = translate('PathJob', "Cylinder: %.2f x %.2f") % (job.Stock.Radius, job.Stock.Height) + seHint = translate("PathJob", "Cylinder: %.2f x %.2f") % ( + job.Stock.Radius, + job.Stock.Height, + ) else: - seHint = '-' - PathLog.error(translate('PathJob', 'Unsupported stock type')) + seHint = "-" + PathLog.error(translate("PathJob", "Unsupported stock type")) self.dialog.stockExtentHint.setText(seHint) spHint = "%s" % job.Stock.Placement self.dialog.stockPlacementHint.setText(spHint) @@ -325,7 +364,13 @@ class JobTemplateExport: heightsChanged = not job.SetupSheet.Proxy.hasDefaultOperationHeights() coolantChanged = not job.SetupSheet.Proxy.hasDefaultCoolantMode() opsWithSettings = job.SetupSheet.Proxy.operationsWithSettings() - settingsChanged = rapidChanged or depthsChanged or heightsChanged or coolantChanged or 0 != len(opsWithSettings) + settingsChanged = ( + rapidChanged + or depthsChanged + or heightsChanged + or coolantChanged + or 0 != len(opsWithSettings) + ) self.dialog.settingsGroup.setChecked(settingsChanged) self.dialog.settingToolRapid.setChecked(rapidChanged) self.dialog.settingOperationDepths.setChecked(depthsChanged) @@ -346,7 +391,11 @@ class JobTemplateExport: self.dialog.toolsList.addItem(item) def checkUncheckTools(self): - state = QtCore.Qt.CheckState.Checked if self.dialog.toolsGroup.isChecked() else QtCore.Qt.CheckState.Unchecked + state = ( + QtCore.Qt.CheckState.Checked + if self.dialog.toolsGroup.isChecked() + else QtCore.Qt.CheckState.Unchecked + ) for i in range(self.dialog.toolsList.count()): self.dialog.toolsList.item(i).setCheckState(state)