CAM: Dogbone - fix for Pocket (#22250)

This commit is contained in:
tarman3
2025-09-08 18:52:03 +03:00
committed by GitHub
parent c3a6d0b03a
commit d87fe5084a
2 changed files with 151 additions and 7 deletions

View File

@@ -344,6 +344,7 @@ class CAMWorkbench(Workbench):
"Profile" in selectedName
or "Contour" in selectedName
or "Dressup" in selectedName
or "Pocket" in selectedName
):
self.appendContextMenu("", "Separator")
# self.appendContextMenu("", ["Set_StartPoint"])

View File

@@ -154,7 +154,7 @@ class Incision(object):
def insertBone(obj, kink):
"""insertBone(kink, side) - return True if a bone should be inserted into the kink"""
if not kink.isKink():
Path.Log.debug(f"not a kink")
Path.Log.debug("not a kink")
return False
if obj.Side == Side.Right and kink.goesRight():
@@ -255,6 +255,15 @@ class Proxy(object):
)
obj.BoneBlacklist = []
obj.addProperty(
"App::PropertyBool",
"OnlyClosedProfiles",
"Dressup",
QT_TRANSLATE_NOOP(
"App::Property",
"Create bones only for outer closed profiles\nCan be useful for multi profile operations, e.g. Pocket with ZigZagOffset pattern",
),
)
self.onDocumentRestored(obj)
def onDocumentRestored(self, obj):
@@ -283,6 +292,124 @@ class Proxy(object):
return dogboneII.generate(kink, generator, calc_length, nominal, custom)
return None
# Get start index of closed loop in Area
def findStartIndexClosedProfile(self, source, startAreaIndex, endAreaIndex):
points = []
points.append(source[endAreaIndex].positionEnd())
points.append(source[endAreaIndex - 1].positionEnd())
for i in range(endAreaIndex - 2, startAreaIndex - 1, -1):
point = source[i].positionBegin()
for j, p in enumerate(points):
# compare this point with all points before
if Path.Geom.pointsCoincide(point, p):
return i, endAreaIndex - j
points.append(point)
return None, None
# Compare two boundboxes
def isEquelBoundboxes(self, bb1, bb2):
if not Path.Geom.isRoughly(bb1.XMin, bb2.XMin):
return False
if not Path.Geom.isRoughly(bb1.XMax, bb2.XMax):
return False
if not Path.Geom.isRoughly(bb1.YMin, bb2.YMin):
return False
if not Path.Geom.isRoughly(bb1.YMax, bb2.YMax):
return False
if not Path.Geom.isRoughly(bb1.ZMin, bb2.ZMin):
return False
if not Path.Geom.isRoughly(bb1.ZMax, bb2.ZMax):
return False
return True
# Search inner profiles which should be excluded
def getIndexInnerProfiles(self, source, indexList):
boundboxList = []
for i, areaIndexList in enumerate(indexList):
minX, minY, maxX, maxY = None, None, None, None
for index in areaIndexList:
point = source[index].positionEnd()
minX = point.x if minX is None or point.x < minX else minX
maxX = point.x if maxX is None or point.x > maxX else maxX
minY = point.y if minY is None or point.y < minY else minY
maxY = point.y if maxY is None or point.y > maxY else maxY
boundbox = FreeCAD.BoundBox(minX, minY, 0, maxX, maxY, 0)
boundboxList.append(boundbox)
# print("boundboxList", boundboxList)
excludeList = []
for i, boundbox in enumerate(boundboxList):
# print(" ", i, boundbox)
for bb in boundboxList:
# print(" ", bb)
if not self.isEquelBoundboxes(boundbox, bb) and bb.isInside(boundbox):
# print(" exclude", i)
excludeList.append(i)
break
return excludeList
# Check command
def isCuttingMove(self, instr):
result = instr.isMove() and not instr.isRapid() and not instr.isPlunge()
return result
def getIndexOuterClosedProfiles(self, source):
closedProfilesIndex = []
startArea = None
endArea = None
for i, instr in enumerate(source):
if (
startArea is None
and endArea is None
and self.isCuttingMove(source[i])
and (i == 0 or not self.isCuttingMove(source[i - 1]))
):
# start mill index of the area
startArea = i
if (
startArea is not None
and endArea is None
and (i == len(source) - 1 or not self.isCuttingMove(source[i + 1]))
):
# end mill index of the area
endArea = i
if startArea and endArea:
print("startArea", startArea, source[startArea])
print("endArea", endArea, source[endArea])
p1 = source[startArea].positionBegin()
p2 = source[endArea].positionEnd()
if Path.Geom.pointsCoincide(p1, p2):
# simple case
# one closed profile in the area
closedProfilesIndex.append(list(range(startArea, endArea + 1)))
else:
# points is not coincide
# try to find last closed profile in the area
startIndex, endIndex = self.findStartIndexClosedProfile(
source, startArea, endArea
)
if startIndex is not None and endIndex is not None:
print("startIndex", startIndex, source[startIndex])
print("endIndex", endIndex, source[endIndex])
closedProfilesIndex.append(list(range(startIndex, endIndex + 1)))
startArea = None
endArea = None
print(closedProfilesIndex)
excludeList = self.getIndexInnerProfiles(source, closedProfilesIndex)
print(excludeList)
outerClosedProfilesIndex = []
for i, area in enumerate(closedProfilesIndex):
if i not in excludeList:
for j in area:
outerClosedProfilesIndex.append(j)
return outerClosedProfilesIndex
def execute(self, obj):
Path.Log.track(obj.Label)
maneuver = PathLanguage.Maneuver()
@@ -290,27 +417,43 @@ class Proxy(object):
lastMove = None
moveAfterPlunge = None
dressingUpDogbone = hasattr(obj.Base, "BoneBlacklist")
if obj.Base and obj.Base.Path and obj.Base.Path.Commands:
for i, instr in enumerate(
PathLanguage.Maneuver.FromPath(PathUtils.getPathWithPlacement(obj.Base)).instr
):
source = PathLanguage.Maneuver.FromPath(PathUtils.getPathWithPlacement(obj.Base)).instr
# get indexes of outer closed profile in each multi work area
if hasattr(obj, "OnlyClosedProfiles") and obj.OnlyClosedProfiles:
closedProfilesIndex = self.getIndexOuterClosedProfiles(source)
else:
closedProfilesIndex = None
print("closedProfilesIndex", closedProfilesIndex)
for index, instr in enumerate(source):
print(index, instr)
# Path.Log.debug(f"instr: {instr}")
if instr.isMove():
thisMove = instr
bone = None
if thisMove.isPlunge():
if thisMove.isPlunge() or (
closedProfilesIndex is not None and index not in closedProfilesIndex
):
print(" is Plunge")
if lastMove and moveAfterPlunge and lastMove.leadsInto(moveAfterPlunge):
print(" create last Bone")
bone = self.createBone(obj, lastMove, moveAfterPlunge)
lastMove = None
moveAfterPlunge = None
else:
print(" is not Plunge")
if moveAfterPlunge is None:
moveAfterPlunge = thisMove
if lastMove:
print(" create Bone")
bone = self.createBone(obj, lastMove, thisMove)
lastMove = thisMove
if bone:
enabled = not len(bones) in obj.BoneBlacklist
print(" 3 bone was created")
enabled = len(bones) not in obj.BoneBlacklist
if enabled and not (
dressingUpDogbone and obj.Base.Proxy.includesBoneAt(bone.position())
):
@@ -348,7 +491,7 @@ class Proxy(object):
if hasattr(self, "bones"):
for nr, bone in enumerate(self.bones):
if Path.Geom.pointsCoincide(bone.position(), pos):
return not (nr in self.obj.BoneBlacklist)
return nr not in self.obj.BoneBlacklist
return False