Make CircularHoleBase use drillableLib

debugs

remove deprecated PathUtils.isDrillable

drillableLib cmake

make PathProfile use new drillableLib
This commit is contained in:
sliptonic
2021-12-05 20:16:23 -06:00
parent f156d9daea
commit fc2d6c172b
9 changed files with 133 additions and 290 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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):

View File

@@ -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):

View File

@@ -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:

View File

@@ -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]

View File

@@ -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")

View File

@@ -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):

View File

@@ -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