Make CircularHoleBase use drillableLib
debugs remove deprecated PathUtils.isDrillable drillableLib cmake make PathProfile use new drillableLib
This commit is contained in:
@@ -27,6 +27,7 @@ INSTALL(
|
||||
|
||||
SET(PathScripts_SRCS
|
||||
PathCommands.py
|
||||
PathScripts/drillableLib.py
|
||||
PathScripts/PathAdaptive.py
|
||||
PathScripts/PathAdaptiveGui.py
|
||||
PathScripts/PathAreaOp.py
|
||||
@@ -211,6 +212,7 @@ SET(Tools_Shape_SRCS
|
||||
SET(PathTests_SRCS
|
||||
PathTests/__init__.py
|
||||
PathTests/boxtest.fcstd
|
||||
PathTests/Drilling_1.FCStd
|
||||
PathTests/PathTestUtils.py
|
||||
PathTests/test_adaptive.fcstd
|
||||
PathTests/test_centroid_00.ngc
|
||||
@@ -224,6 +226,7 @@ SET(PathTests_SRCS
|
||||
PathTests/TestPathDressupDogbone.py
|
||||
PathTests/TestPathDressupHoldingTags.py
|
||||
PathTests/TestPathDrillGenerator.py
|
||||
PathTests/TestPathDrillable.py
|
||||
PathTests/TestPathRotationGenerator.py
|
||||
PathTests/TestPathGeom.py
|
||||
PathTests/TestPathHelix.py
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
import FreeCAD
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathOp as PathOp
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
|
||||
# import PathScripts.PathUtils as PathUtils
|
||||
import PathScripts.drillableLib as drillableLib
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
@@ -46,9 +48,11 @@ def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
class ObjectOp(PathOp.ObjectOp):
|
||||
"""Base class for proxy objects of all operations on circular holes."""
|
||||
@@ -172,7 +176,6 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
return False
|
||||
|
||||
holes = []
|
||||
|
||||
for base, subs in obj.Base:
|
||||
for sub in subs:
|
||||
PathLog.debug("processing {} in {}".format(sub, base.Name))
|
||||
@@ -204,85 +207,19 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
def findAllHoles(self, obj):
|
||||
"""findAllHoles(obj) ... find all holes of all base models and assign as features."""
|
||||
PathLog.track()
|
||||
if not self.getJob(obj):
|
||||
job = self.getJob(obj)
|
||||
if not job:
|
||||
return
|
||||
|
||||
matchvector = None if job.JobType == "Multiaxis" else FreeCAD.Vector(0, 0, 1)
|
||||
tooldiameter = obj.ToolController.Tool.Diameter
|
||||
|
||||
features = []
|
||||
for base in self.model:
|
||||
features.extend(self.findHoles(obj, base))
|
||||
features.extend(
|
||||
drillableLib.getDrillableTargets(
|
||||
base, ToolDiameter=tooldiameter, vector=matchvector
|
||||
)
|
||||
)
|
||||
obj.Base = features
|
||||
obj.Disabled = []
|
||||
|
||||
def findHoles(self, obj, baseobject):
|
||||
"""findHoles(obj, baseobject) ... inspect baseobject and identify all features that resemble a straight cricular hole."""
|
||||
shape = baseobject.Shape
|
||||
PathLog.track("obj: {} shape: {}".format(obj, shape))
|
||||
holelist = []
|
||||
features = []
|
||||
# tooldiameter = float(obj.ToolController.Proxy.getTool(obj.ToolController).Diameter)
|
||||
tooldiameter = None
|
||||
PathLog.debug(
|
||||
"search for holes larger than tooldiameter: {}: ".format(tooldiameter)
|
||||
)
|
||||
if DraftGeomUtils.isPlanar(shape):
|
||||
PathLog.debug("shape is planar")
|
||||
for i in range(len(shape.Edges)):
|
||||
candidateEdgeName = "Edge" + str(i + 1)
|
||||
e = shape.getElement(candidateEdgeName)
|
||||
if PathUtils.isDrillable(shape, e, tooldiameter):
|
||||
PathLog.debug(
|
||||
"edge candidate: {} (hash {})is drillable ".format(
|
||||
e, e.hashCode()
|
||||
)
|
||||
)
|
||||
x = e.Curve.Center.x
|
||||
y = e.Curve.Center.y
|
||||
diameter = e.BoundBox.XLength
|
||||
holelist.append(
|
||||
{
|
||||
"featureName": candidateEdgeName,
|
||||
"feature": e,
|
||||
"x": x,
|
||||
"y": y,
|
||||
"d": diameter,
|
||||
"enabled": True,
|
||||
}
|
||||
)
|
||||
features.append((baseobject, candidateEdgeName))
|
||||
PathLog.debug(
|
||||
"Found hole feature %s.%s"
|
||||
% (baseobject.Label, candidateEdgeName)
|
||||
)
|
||||
else:
|
||||
PathLog.debug("shape is not planar")
|
||||
for i in range(len(shape.Faces)):
|
||||
candidateFaceName = "Face" + str(i + 1)
|
||||
f = shape.getElement(candidateFaceName)
|
||||
if PathUtils.isDrillable(shape, f, tooldiameter):
|
||||
PathLog.debug("face candidate: {} is drillable ".format(f))
|
||||
if hasattr(f.Surface, "Center"):
|
||||
x = f.Surface.Center.x
|
||||
y = f.Surface.Center.y
|
||||
diameter = f.BoundBox.XLength
|
||||
else:
|
||||
center = f.Edges[0].Curve.Center
|
||||
x = center.x
|
||||
y = center.y
|
||||
diameter = f.Edges[0].Curve.Radius * 2
|
||||
holelist.append(
|
||||
{
|
||||
"featureName": candidateFaceName,
|
||||
"feature": f,
|
||||
"x": x,
|
||||
"y": y,
|
||||
"d": diameter,
|
||||
"enabled": True,
|
||||
}
|
||||
)
|
||||
features.append((baseobject, candidateFaceName))
|
||||
PathLog.debug(
|
||||
"Found hole feature %s.%s"
|
||||
% (baseobject.Label, candidateFaceName)
|
||||
)
|
||||
|
||||
PathLog.debug("holes found: {}".format(holelist))
|
||||
return features
|
||||
|
||||
@@ -43,9 +43,11 @@ __doc__ = "Path Drilling operation."
|
||||
__contributors__ = "IMBack!"
|
||||
|
||||
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
|
||||
@@ -187,6 +187,7 @@ class ObjectJob:
|
||||
"Base",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathJob", "Select the Type of Job"),
|
||||
)
|
||||
obj.setEditorMode("JobType", 2) # Hide
|
||||
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
@@ -494,6 +495,8 @@ class ObjectJob:
|
||||
"Base",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathJob", "Select the Type of Job"),
|
||||
)
|
||||
obj.setEditorMode("JobType", 2) # Hide
|
||||
|
||||
obj.JobType = ["2D", "2.5D", "Lathe", "Multiaxis"]
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
|
||||
@@ -28,6 +28,7 @@ import PathScripts.PathAreaOp as PathAreaOp
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathOp as PathOp
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import PathScripts.drillableLib as drillableLib
|
||||
import math
|
||||
import numpy
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
@@ -444,7 +445,7 @@ class ObjectProfile(PathAreaOp.ObjectOp):
|
||||
for baseShape, wire in holes:
|
||||
cont = False
|
||||
f = Part.makeFace(wire, "Part::FaceMakerSimple")
|
||||
drillable = PathUtils.isDrillable(baseShape, wire)
|
||||
drillable = drillableLib.isDrillable(baseShape, f)
|
||||
|
||||
if obj.processCircles:
|
||||
if drillable:
|
||||
|
||||
@@ -21,13 +21,13 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
'''Selection gates and observers to control selectability while building Path operations '''
|
||||
"""Selection gates and observers to control selectability while building Path operations """
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import PathScripts.drillableLib as drillableLib
|
||||
import math
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
@@ -41,12 +41,12 @@ class PathBaseGate(object):
|
||||
|
||||
class EGate(PathBaseGate):
|
||||
def allow(self, doc, obj, sub): # pylint: disable=unused-argument
|
||||
return sub and sub[0:4] == 'Edge'
|
||||
return sub and sub[0:4] == "Edge"
|
||||
|
||||
|
||||
class MESHGate(PathBaseGate):
|
||||
def allow(self, doc, obj, sub): # pylint: disable=unused-argument
|
||||
return obj.TypeId[0:4] == 'Mesh'
|
||||
return obj.TypeId[0:4] == "Mesh"
|
||||
|
||||
|
||||
class VCARVEGate:
|
||||
@@ -59,20 +59,20 @@ class VCARVEGate:
|
||||
if math.fabs(shape.Volume) < 1e-9 and len(shape.Wires) > 0:
|
||||
return True
|
||||
|
||||
if shape.ShapeType == 'Face':
|
||||
if shape.ShapeType == "Face":
|
||||
return True
|
||||
|
||||
elif shape.ShapeType == 'Solid':
|
||||
if sub and sub[0:4] == 'Face':
|
||||
elif shape.ShapeType == "Solid":
|
||||
if sub and sub[0:4] == "Face":
|
||||
return True
|
||||
|
||||
elif shape.ShapeType == 'Compound':
|
||||
if sub and sub[0:4] == 'Face':
|
||||
elif shape.ShapeType == "Compound":
|
||||
if sub and sub[0:4] == "Face":
|
||||
return True
|
||||
|
||||
if sub:
|
||||
subShape = shape.getElement(sub)
|
||||
if subShape.ShapeType == 'Edge':
|
||||
if subShape.ShapeType == "Edge":
|
||||
return False
|
||||
|
||||
return False
|
||||
@@ -88,35 +88,35 @@ class ENGRAVEGate(PathBaseGate):
|
||||
if math.fabs(shape.Volume) < 1e-9 and len(shape.Wires) > 0:
|
||||
return True
|
||||
|
||||
if shape.ShapeType == 'Edge':
|
||||
if shape.ShapeType == "Edge":
|
||||
return True
|
||||
|
||||
if sub:
|
||||
subShape = shape.getElement(sub)
|
||||
if subShape.ShapeType == 'Edge':
|
||||
if subShape.ShapeType == "Edge":
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class CHAMFERGate(PathBaseGate):
|
||||
def allow(self, doc, obj, sub): # pylint: disable=unused-argument
|
||||
def allow(self, doc, obj, sub): # pylint: disable=unused-argument
|
||||
try:
|
||||
shape = obj.Shape
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except Exception: # pylint: disable=broad-except
|
||||
return False
|
||||
|
||||
if math.fabs(shape.Volume) < 1e-9 and len(shape.Wires) > 0:
|
||||
return True
|
||||
|
||||
if 'Edge' == shape.ShapeType or 'Face' == shape.ShapeType:
|
||||
if "Edge" == shape.ShapeType or "Face" == shape.ShapeType:
|
||||
return True
|
||||
|
||||
if sub:
|
||||
subShape = shape.getElement(sub)
|
||||
if subShape.ShapeType == 'Edge':
|
||||
if subShape.ShapeType == "Edge":
|
||||
return True
|
||||
elif (subShape.ShapeType == 'Face'):
|
||||
elif subShape.ShapeType == "Face":
|
||||
return True
|
||||
|
||||
return False
|
||||
@@ -124,16 +124,19 @@ class CHAMFERGate(PathBaseGate):
|
||||
|
||||
class DRILLGate(PathBaseGate):
|
||||
def allow(self, doc, obj, sub): # pylint: disable=unused-argument
|
||||
PathLog.debug('obj: {} sub: {}'.format(obj, sub))
|
||||
if hasattr(obj, "Shape") and sub:
|
||||
shape = obj.Shape
|
||||
subobj = shape.getElement(sub)
|
||||
return PathUtils.isDrillable(shape, subobj, includePartials=True)
|
||||
else:
|
||||
PathLog.debug("obj: {} sub: {}".format(obj, sub))
|
||||
if not hasattr(obj, "Shape") and sub:
|
||||
return False
|
||||
shape = obj.Shape
|
||||
subobj = shape.getElement(sub)
|
||||
if subobj.ShapeType not in ["Edge", "Face"]:
|
||||
return False
|
||||
return drillableLib.isDrillable(shape, subobj, vector=None)
|
||||
|
||||
|
||||
class FACEGate(PathBaseGate): # formerly PROFILEGate class using allow_ORIG method as allow()
|
||||
class FACEGate(
|
||||
PathBaseGate
|
||||
): # formerly PROFILEGate class using allow_ORIG method as allow()
|
||||
def allow(self, doc, obj, sub): # pylint: disable=unused-argument
|
||||
profileable = False
|
||||
|
||||
@@ -142,15 +145,15 @@ class FACEGate(PathBaseGate): # formerly PROFILEGate class using allow_ORIG me
|
||||
except Exception: # pylint: disable=broad-except
|
||||
return False
|
||||
|
||||
if obj.ShapeType == 'Compound':
|
||||
if sub and sub[0:4] == 'Face':
|
||||
if obj.ShapeType == "Compound":
|
||||
if sub and sub[0:4] == "Face":
|
||||
profileable = True
|
||||
|
||||
elif obj.ShapeType == 'Face': # 3D Face, not flat, planar?
|
||||
profileable = True # Was False
|
||||
elif obj.ShapeType == "Face": # 3D Face, not flat, planar?
|
||||
profileable = True # Was False
|
||||
|
||||
elif obj.ShapeType == 'Solid':
|
||||
if sub and sub[0:4] == 'Face':
|
||||
elif obj.ShapeType == "Solid":
|
||||
if sub and sub[0:4] == "Face":
|
||||
profileable = True
|
||||
|
||||
return profileable
|
||||
@@ -163,27 +166,27 @@ class FACEGate(PathBaseGate): # formerly PROFILEGate class using allow_ORIG me
|
||||
except Exception: # pylint: disable=broad-except
|
||||
return False
|
||||
|
||||
if obj.ShapeType == 'Edge':
|
||||
if obj.ShapeType == "Edge":
|
||||
profileable = False
|
||||
|
||||
elif obj.ShapeType == 'Compound':
|
||||
if sub and sub[0:4] == 'Face':
|
||||
elif obj.ShapeType == "Compound":
|
||||
if sub and sub[0:4] == "Face":
|
||||
profileable = True
|
||||
|
||||
if sub and sub[0:4] == 'Edge':
|
||||
if sub and sub[0:4] == "Edge":
|
||||
profileable = False
|
||||
|
||||
elif obj.ShapeType == 'Face':
|
||||
elif obj.ShapeType == "Face":
|
||||
profileable = False
|
||||
|
||||
elif obj.ShapeType == 'Solid':
|
||||
if sub and sub[0:4] == 'Face':
|
||||
elif obj.ShapeType == "Solid":
|
||||
if sub and sub[0:4] == "Face":
|
||||
profileable = True
|
||||
|
||||
if sub and sub[0:4] == 'Edge':
|
||||
if sub and sub[0:4] == "Edge":
|
||||
profileable = False
|
||||
|
||||
elif obj.ShapeType == 'Wire':
|
||||
elif obj.ShapeType == "Wire":
|
||||
profileable = False
|
||||
|
||||
return profileable
|
||||
@@ -191,7 +194,7 @@ class FACEGate(PathBaseGate): # formerly PROFILEGate class using allow_ORIG me
|
||||
|
||||
class PROFILEGate(PathBaseGate):
|
||||
def allow(self, doc, obj, sub): # pylint: disable=unused-argument
|
||||
if sub and sub[0:4] == 'Edge':
|
||||
if sub and sub[0:4] == "Edge":
|
||||
return True
|
||||
|
||||
try:
|
||||
@@ -199,18 +202,18 @@ class PROFILEGate(PathBaseGate):
|
||||
except Exception: # pylint: disable=broad-except
|
||||
return False
|
||||
|
||||
if obj.ShapeType == 'Compound':
|
||||
if sub and sub[0:4] == 'Face':
|
||||
if obj.ShapeType == "Compound":
|
||||
if sub and sub[0:4] == "Face":
|
||||
return True
|
||||
|
||||
elif obj.ShapeType == 'Face':
|
||||
elif obj.ShapeType == "Face":
|
||||
return True
|
||||
|
||||
elif obj.ShapeType == 'Solid':
|
||||
if sub and sub[0:4] == 'Face':
|
||||
elif obj.ShapeType == "Solid":
|
||||
if sub and sub[0:4] == "Face":
|
||||
return True
|
||||
|
||||
elif obj.ShapeType == 'Wire':
|
||||
elif obj.ShapeType == "Wire":
|
||||
return True
|
||||
|
||||
return False
|
||||
@@ -225,18 +228,18 @@ class POCKETGate(PathBaseGate):
|
||||
except Exception: # pylint: disable=broad-except
|
||||
return False
|
||||
|
||||
if obj.ShapeType == 'Edge':
|
||||
if obj.ShapeType == "Edge":
|
||||
pocketable = False
|
||||
|
||||
elif obj.ShapeType == 'Face':
|
||||
elif obj.ShapeType == "Face":
|
||||
pocketable = True
|
||||
|
||||
elif obj.ShapeType == 'Solid':
|
||||
if sub and sub[0:4] == 'Face':
|
||||
elif obj.ShapeType == "Solid":
|
||||
if sub and sub[0:4] == "Face":
|
||||
pocketable = True
|
||||
|
||||
elif obj.ShapeType == 'Compound':
|
||||
if sub and sub[0:4] == 'Face':
|
||||
elif obj.ShapeType == "Compound":
|
||||
if sub and sub[0:4] == "Face":
|
||||
pocketable = True
|
||||
|
||||
return pocketable
|
||||
@@ -266,22 +269,22 @@ class PROBEGate:
|
||||
|
||||
class TURNGate(PathBaseGate):
|
||||
def allow(self, doc, obj, sub): # pylint: disable=unused-argument
|
||||
PathLog.debug('obj: {} sub: {}'.format(obj, sub))
|
||||
PathLog.debug("obj: {} sub: {}".format(obj, sub))
|
||||
if hasattr(obj, "Shape") and sub:
|
||||
shape = obj.Shape
|
||||
subobj = shape.getElement(sub)
|
||||
return PathUtils.isDrillable(shape, subobj, includePartials=True)
|
||||
return drillableLib.isDrillable(shape, subobj, vector=None)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class ALLGate(PathBaseGate):
|
||||
def allow(self, doc, obj, sub): # pylint: disable=unused-argument
|
||||
if sub and sub[0:6] == 'Vertex':
|
||||
if sub and sub[0:6] == "Vertex":
|
||||
return True
|
||||
if sub and sub[0:4] == 'Edge':
|
||||
if sub and sub[0:4] == "Edge":
|
||||
return True
|
||||
if sub and sub[0:4] == 'Face':
|
||||
if sub and sub[0:4] == "Face":
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -348,7 +351,7 @@ def slotselect():
|
||||
|
||||
def surfaceselect():
|
||||
gate = False
|
||||
if(MESHGate() or FACEGate()):
|
||||
if MESHGate() or FACEGate():
|
||||
gate = True
|
||||
FreeCADGui.Selection.addSelectionGate(gate)
|
||||
if not PathPreferences.suppressSelectionModeWarning():
|
||||
@@ -380,30 +383,30 @@ def turnselect():
|
||||
|
||||
def select(op):
|
||||
opsel = {}
|
||||
opsel['Contour'] = contourselect # (depreciated)
|
||||
opsel['Deburr'] = chamferselect
|
||||
opsel['Drilling'] = drillselect
|
||||
opsel['Engrave'] = engraveselect
|
||||
opsel['Helix'] = drillselect
|
||||
opsel['MillFace'] = pocketselect
|
||||
opsel['Pocket'] = pocketselect
|
||||
opsel['Pocket 3D'] = pocketselect
|
||||
opsel['Pocket Shape'] = pocketselect
|
||||
opsel['Profile Edges'] = eselect # (depreciated)
|
||||
opsel['Profile Faces'] = fselect # (depreciated)
|
||||
opsel['Profile'] = profileselect
|
||||
opsel['Slot'] = slotselect
|
||||
opsel['Surface'] = surfaceselect
|
||||
opsel['Waterline'] = surfaceselect
|
||||
opsel['Adaptive'] = adaptiveselect
|
||||
opsel['Vcarve'] = vcarveselect
|
||||
opsel['Probe'] = probeselect
|
||||
opsel['Custom'] = customselect
|
||||
opsel['Thread Milling'] = drillselect
|
||||
opsel['TurnFace'] = turnselect
|
||||
opsel['TurnProfile'] = turnselect
|
||||
opsel['TurnPartoff'] = turnselect
|
||||
opsel['TurnRough'] = turnselect
|
||||
opsel["Contour"] = contourselect # (depreciated)
|
||||
opsel["Deburr"] = chamferselect
|
||||
opsel["Drilling"] = drillselect
|
||||
opsel["Engrave"] = engraveselect
|
||||
opsel["Helix"] = drillselect
|
||||
opsel["MillFace"] = pocketselect
|
||||
opsel["Pocket"] = pocketselect
|
||||
opsel["Pocket 3D"] = pocketselect
|
||||
opsel["Pocket Shape"] = pocketselect
|
||||
opsel["Profile Edges"] = eselect # (depreciated)
|
||||
opsel["Profile Faces"] = fselect # (depreciated)
|
||||
opsel["Profile"] = profileselect
|
||||
opsel["Slot"] = slotselect
|
||||
opsel["Surface"] = surfaceselect
|
||||
opsel["Waterline"] = surfaceselect
|
||||
opsel["Adaptive"] = adaptiveselect
|
||||
opsel["Vcarve"] = vcarveselect
|
||||
opsel["Probe"] = probeselect
|
||||
opsel["Custom"] = customselect
|
||||
opsel["Thread Milling"] = drillselect
|
||||
opsel["TurnFace"] = turnselect
|
||||
opsel["TurnProfile"] = turnselect
|
||||
opsel["TurnPartoff"] = turnselect
|
||||
opsel["TurnRough"] = turnselect
|
||||
return opsel[op]
|
||||
|
||||
|
||||
|
||||
@@ -73,119 +73,6 @@ def waiting_effects(function):
|
||||
return new_function
|
||||
|
||||
|
||||
def isDrillable(obj, candidate, tooldiameter=None, includePartials=False):
|
||||
"""
|
||||
Checks candidates to see if they can be drilled.
|
||||
Candidates can be either faces - circular or cylindrical or circular edges.
|
||||
The tooldiameter can be optionally passed. if passed, the check will return
|
||||
False for any holes smaller than the tooldiameter.
|
||||
obj=Shape
|
||||
candidate = Face or Edge
|
||||
tooldiameter=float
|
||||
"""
|
||||
PathLog.track(
|
||||
"obj: {} candidate: {} tooldiameter {}".format(obj, candidate, tooldiameter)
|
||||
)
|
||||
if list == type(obj):
|
||||
for shape in obj:
|
||||
if isDrillable(shape, candidate, tooldiameter, includePartials):
|
||||
return (True, shape)
|
||||
return (False, None)
|
||||
|
||||
drillable = False
|
||||
try:
|
||||
if candidate.ShapeType == "Face":
|
||||
face = candidate
|
||||
# eliminate flat faces
|
||||
if (round(face.ParameterRange[0], 8) == 0.0) and (
|
||||
round(face.ParameterRange[1], 8) == round(math.pi * 2, 8)
|
||||
):
|
||||
for (
|
||||
edge
|
||||
) in face.Edges: # Find seam edge and check if aligned to Z axis.
|
||||
if isinstance(edge.Curve, Part.Line):
|
||||
PathLog.debug("candidate is a circle")
|
||||
v0 = edge.Vertexes[0].Point
|
||||
v1 = edge.Vertexes[1].Point
|
||||
# check if the cylinder seam is vertically aligned. Eliminate tilted holes
|
||||
if (
|
||||
numpy.isclose(v1.sub(v0).x, 0, rtol=1e-05, atol=1e-06)
|
||||
) and (numpy.isclose(v1.sub(v0).y, 0, rtol=1e-05, atol=1e-06)):
|
||||
drillable = True
|
||||
# vector of top center
|
||||
lsp = Vector(
|
||||
face.BoundBox.Center.x,
|
||||
face.BoundBox.Center.y,
|
||||
face.BoundBox.ZMax,
|
||||
)
|
||||
# vector of bottom center
|
||||
lep = Vector(
|
||||
face.BoundBox.Center.x,
|
||||
face.BoundBox.Center.y,
|
||||
face.BoundBox.ZMin,
|
||||
)
|
||||
# check if the cylindrical 'lids' are inside the base
|
||||
# object. This eliminates extruded circles but allows
|
||||
# actual holes.
|
||||
if obj.isInside(lsp, 1e-6, False) or obj.isInside(
|
||||
lep, 1e-6, False
|
||||
):
|
||||
PathLog.track(
|
||||
"inside check failed. lsp: {} lep: {}".format(
|
||||
lsp, lep
|
||||
)
|
||||
)
|
||||
drillable = False
|
||||
# eliminate elliptical holes
|
||||
elif not hasattr(face.Surface, "Radius"):
|
||||
PathLog.debug("candidate face has no radius attribute")
|
||||
drillable = False
|
||||
else:
|
||||
if tooldiameter is not None:
|
||||
drillable = face.Surface.Radius >= tooldiameter / 2
|
||||
else:
|
||||
drillable = True
|
||||
elif type(face.Surface) == Part.Plane and PathGeom.pointsCoincide(
|
||||
face.Surface.Axis, FreeCAD.Vector(0, 0, 1)
|
||||
):
|
||||
if len(face.Edges) == 1 and type(face.Edges[0].Curve) == Part.Circle:
|
||||
center = face.Edges[0].Curve.Center
|
||||
if obj.isInside(center, 1e-6, False):
|
||||
if tooldiameter is not None:
|
||||
drillable = face.Edges[0].Curve.Radius >= tooldiameter / 2
|
||||
else:
|
||||
drillable = True
|
||||
else:
|
||||
for edge in candidate.Edges:
|
||||
if isinstance(edge.Curve, Part.Circle) and (
|
||||
includePartials or edge.isClosed()
|
||||
):
|
||||
PathLog.debug("candidate is a circle or ellipse")
|
||||
if not hasattr(edge.Curve, "Radius"):
|
||||
PathLog.debug("No radius. Ellipse.")
|
||||
drillable = False
|
||||
else:
|
||||
PathLog.debug("Has Radius, Circle")
|
||||
if tooldiameter is not None:
|
||||
drillable = edge.Curve.Radius >= tooldiameter / 2
|
||||
if not drillable:
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"Found a drillable hole with diameter: {}: "
|
||||
"too small for the current tool with "
|
||||
"diameter: {}".format(
|
||||
edge.Curve.Radius * 2, tooldiameter
|
||||
)
|
||||
)
|
||||
else:
|
||||
drillable = True
|
||||
PathLog.debug("candidate is drillable: {}".format(drillable))
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
PathLog.warning(
|
||||
translate("Path", "Issue determine drillability: {}").format(ex)
|
||||
)
|
||||
return drillable
|
||||
|
||||
|
||||
# set at 4 decimal places for testing
|
||||
def fmt(val):
|
||||
return format(val, ".4f")
|
||||
|
||||
@@ -26,9 +26,12 @@ import PathScripts.PathLog as PathLog
|
||||
import PathTests.PathTestUtils as PathTestUtils
|
||||
import PathScripts.drillableLib as drillableLib
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
class TestPathDrillable(PathTestUtils.PathTestBase):
|
||||
def setUp(self):
|
||||
|
||||
@@ -22,19 +22,20 @@
|
||||
|
||||
import TestApp
|
||||
|
||||
# from PathTests.TestPathHelix import TestPathHelix
|
||||
# from PathTests.TestPathPost import PathPostTestCases
|
||||
from PathTests.TestPathAdaptive import TestPathAdaptive
|
||||
from PathTests.TestPathCore import TestPathCore
|
||||
from PathTests.TestPathDeburr import TestPathDeburr
|
||||
from PathTests.TestPathDepthParams import depthTestCases
|
||||
from PathTests.TestPathDressupDogbone import TestDressupDogbone
|
||||
from PathTests.TestPathDressupHoldingTags import TestHoldingTags
|
||||
from PathTests.TestPathDrillable import TestPathDrillable
|
||||
from PathTests.TestPathDrillGenerator import TestPathDrillGenerator
|
||||
from PathTests.TestPathGeom import TestPathGeom
|
||||
# from PathTests.TestPathHelix import TestPathHelix
|
||||
from PathTests.TestPathHelpers import TestPathHelpers
|
||||
from PathTests.TestPathLog import TestPathLog
|
||||
from PathTests.TestPathOpTools import TestPathOpTools
|
||||
# from PathTests.TestPathPost import PathPostTestCases
|
||||
from PathTests.TestPathPreferences import TestPathPreferences
|
||||
from PathTests.TestPathPropertyBag import TestPathPropertyBag
|
||||
from PathTests.TestPathRotationGenerator import TestPathRotationGenerator
|
||||
@@ -50,18 +51,21 @@ from PathTests.TestPathVcarve import TestPathVcarve
|
||||
from PathTests.TestPathVoronoi import TestPathVoronoi
|
||||
|
||||
# dummy usage to get flake8 and lgtm quiet
|
||||
False if depthTestCases.__name__ else True
|
||||
False if TestApp.__name__ else True
|
||||
False if TestDressupDogbone.__name__ else True
|
||||
False if TestHoldingTags.__name__ else True
|
||||
False if TestPathAdaptive.__name__ else True
|
||||
False if TestPathCore.__name__ else True
|
||||
False if TestPathDeburr.__name__ else True
|
||||
False if TestPathGeom.__name__ else True
|
||||
False if depthTestCases.__name__ else True
|
||||
False if TestDressupDogbone.__name__ else True
|
||||
False if TestHoldingTags.__name__ else True
|
||||
# False if TestPathHelix.__name__ else True
|
||||
False if TestPathDrillable.__name__ else True
|
||||
False if TestPathDrillGenerator.__name__ else True
|
||||
False if TestPathGeom.__name__ else True
|
||||
False if TestPathHelpers.__name__ else True
|
||||
False if TestPathLog.__name__ else True
|
||||
False if TestPathOpTools.__name__ else True
|
||||
# False if TestPathPost.__name__ else True
|
||||
False if TestPathPreferences.__name__ else True
|
||||
False if TestPathPropertyBag.__name__ else True
|
||||
False if TestPathRotationGenerator.__name__ else True
|
||||
@@ -75,4 +79,4 @@ False if TestPathTooltable.__name__ else True
|
||||
False if TestPathUtil.__name__ else True
|
||||
False if TestPathVcarve.__name__ else True
|
||||
False if TestPathVoronoi.__name__ else True
|
||||
False if TestPathDrillGenerator.__name__ else True
|
||||
|
||||
|
||||
Reference in New Issue
Block a user