CAM: Drillable - Optimizations for Auto-select

This commit is contained in:
tarman3
2026-02-24 21:20:54 +02:00
parent 0a4f6ec7c4
commit e643328599
3 changed files with 64 additions and 22 deletions
+1 -1
View File
@@ -256,5 +256,5 @@ class TestPathDrillable(PathTestUtils.PathTestBase):
results = Drillable.getDrillableTargets(self.obj, vector=None)
self.assertEqual(len(results), 20)
results = Drillable.getDrillableTargets(self.obj, ToolDiameter=20, vector=None)
results = Drillable.getDrillableTargets(self.obj, toolDiameter=20, vector=None)
self.assertEqual(len(results), 5)
+61 -19
View File
@@ -249,35 +249,77 @@ def compareVecs(vec1, vec2, exact=False):
)
def getDrillableTargets(obj, ToolDiameter=None, vector=App.Vector(0, 0, 1)):
def getStraightEdge(candidate):
# Finds the seam edge in a cylinder
for e in candidate.Edges:
if isinstance(e.Curve, Part.Line):
return e
for e in candidate.Edges:
if isinstance(e.Curve, Part.BSplineCurve):
p0 = e.Vertexes[0].Point
p1 = e.Vertexes[-1].Point
length = p0.distanceToPoint(p1)
if Path.Geom.isRoughly(e.Length, length):
return Part.Edge(Part.LineSegment(p0, p1))
return App.Vector(0, 0, 1)
def getDrillableTargets(obj, toolDiameter=None, vector=App.Vector(0, 0, 1)):
"""
Returns a list of tuples for drillable subelements from the given object
[(obj,'Face1'),(obj,'Face3')]
Finds cylindrical faces that are larger than the tool diameter (if provided) and
oriented with the vector. If vector is None, all drillables are returned
"""
shp = obj.Shape
toolRadius = toolDiameter / 2 if toolDiameter else 0
shape = obj.Shape
drillables = []
candidates = [] # simple holes
results = []
for i in range(1, len(shp.Faces) + 1):
fname = "Face{}".format(i)
Path.Log.debug(fname)
candidate = obj.getSubObject(fname)
if not isinstance(candidate.Surface, Part.Cylinder):
for face in shape.Faces:
if not isinstance(face.Surface, Part.Cylinder):
continue
try:
drillable = isDrillable(shp, candidate, tooldiameter=ToolDiameter, vector=vector)
Path.Log.debug("fname: {} : drillable {}".format(fname, drillable))
except Exception as e:
Path.Log.debug(e)
if toolRadius:
cylinderRadius = face.Surface.Radius
if toolRadius > cylinderRadius and not Path.Geom.isRoughly(cylinderRadius, toolRadius):
continue
if face.Volume > 0: # hole should have negative volume
continue
if len(face.Edges) == 3:
candidates.append(face)
if drillable:
results.append((obj, fname))
circularFaces = [] # faces which can be a bottom of hole
for face in shape.Faces:
if all(isinstance(e.Curve, Part.Circle) for e in face.OuterWire.Edges):
center = face.OuterWire.Edges[0].Curve.Center
if all(Path.Geom.pointsCoincide(center, e.Curve.Center) for e in face.OuterWire.Edges):
hashList = [e.hashCode() for e in face.OuterWire.Edges]
circularFaces.append((face, hashList))
return results
for candidate in candidates:
if vector is None: # do not check direction
drillables.append(candidate)
continue
bottomFace = None
for face, hashList in circularFaces:
if any(
e.hashCode() in hashList
for e in candidate.Edges
if isinstance(e.Curve, Part.Circle)
):
bottomFace = face
break
if bottomFace: # blind holes only drillable at exact vector
if compareVecs(bottomFace.normalAt(0, 0), vector, exact=True):
drillables.append(candidate)
elif compareVecs(getStraightEdge(candidate).Curve.Direction, vector):
drillables.append(candidate)
if drillables:
hashList = [f.hashCode() for f in drillables]
faces = shape.Faces
return [(obj, f"Face{i + 1}") for i, f in enumerate(faces) if f.hashCode() in hashList]
return []
+2 -2
View File
@@ -207,12 +207,12 @@ class ObjectOp(PathOp.ObjectOp):
return
matchvector = None if job.JobType == "Multiaxis" else FreeCAD.Vector(0, 0, 1)
tooldiameter = obj.ToolController.Tool.Diameter
tooldiameter = obj.ToolController.Tool.Diameter.Value
features = []
for base in self.model:
features.extend(
Drillable.getDrillableTargets(base, ToolDiameter=tooldiameter, vector=matchvector)
Drillable.getDrillableTargets(base, toolDiameter=tooldiameter, vector=matchvector)
)
obj.Base = features
obj.Disabled = []