purge archpanel support

This commit is contained in:
sliptonic
2021-08-12 10:45:13 -05:00
parent c03c771839
commit 13779a6db9
7 changed files with 1611 additions and 834 deletions

View File

@@ -29,10 +29,10 @@ from PySide import QtCore
# lazily loaded modules
from lazy_loader.lazy_loader import LazyLoader
ArchPanel = LazyLoader('ArchPanel', globals(), 'ArchPanel')
Draft = LazyLoader('Draft', globals(), 'Draft')
Part = LazyLoader('Part', globals(), 'Part')
DraftGeomUtils = LazyLoader('DraftGeomUtils', globals(), 'DraftGeomUtils')
Draft = LazyLoader("Draft", globals(), "Draft")
Part = LazyLoader("Part", globals(), "Part")
DraftGeomUtils = LazyLoader("DraftGeomUtils", globals(), "DraftGeomUtils")
__title__ = "Path Circular Holes Base Operation"
@@ -51,81 +51,70 @@ PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
class ObjectOp(PathOp.ObjectOp):
'''Base class for proxy objects of all operations on circular holes.'''
"""Base class for proxy objects of all operations on circular holes."""
def opFeatures(self, obj):
'''opFeatures(obj) ... calls circularHoleFeatures(obj) and ORs in the standard features required for processing circular holes.
Do not overwrite, implement circularHoleFeatures(obj) instead'''
return PathOp.FeatureTool | PathOp.FeatureDepths | PathOp.FeatureHeights \
| PathOp.FeatureBaseFaces | self.circularHoleFeatures(obj) \
"""opFeatures(obj) ... calls circularHoleFeatures(obj) and ORs in the standard features required for processing circular holes.
Do not overwrite, implement circularHoleFeatures(obj) instead"""
return (
PathOp.FeatureTool
| PathOp.FeatureDepths
| PathOp.FeatureHeights
| PathOp.FeatureBaseFaces
| self.circularHoleFeatures(obj)
| PathOp.FeatureCoolant
)
def circularHoleFeatures(self, obj):
'''circularHoleFeatures(obj) ... overwrite to add operations specific features.
Can safely be overwritten by subclasses.'''
"""circularHoleFeatures(obj) ... overwrite to add operations specific features.
Can safely be overwritten by subclasses."""
return 0
def initOperation(self, obj):
'''initOperation(obj) ... adds Disabled properties and calls initCircularHoleOperation(obj).
Do not overwrite, implement initCircularHoleOperation(obj) instead.'''
obj.addProperty("App::PropertyStringList", "Disabled", "Base", QtCore.QT_TRANSLATE_NOOP("Path", "List of disabled features"))
"""initOperation(obj) ... adds Disabled properties and calls initCircularHoleOperation(obj).
Do not overwrite, implement initCircularHoleOperation(obj) instead."""
obj.addProperty(
"App::PropertyStringList",
"Disabled",
"Base",
QtCore.QT_TRANSLATE_NOOP("Path", "List of disabled features"),
)
self.initCircularHoleOperation(obj)
def initCircularHoleOperation(self, obj):
'''initCircularHoleOperation(obj) ... overwrite if the subclass needs initialisation.
Can safely be overwritten by subclasses.'''
"""initCircularHoleOperation(obj) ... overwrite if the subclass needs initialisation.
Can safely be overwritten by subclasses."""
pass
def baseIsArchPanel(self, obj, base):
'''baseIsArchPanel(obj, base) ... return true if op deals with an Arch.Panel.'''
return hasattr(base, "Proxy") and isinstance(base.Proxy, ArchPanel.PanelSheet)
def getArchPanelEdge(self, obj, base, sub):
'''getArchPanelEdge(obj, base, sub) ... helper function to identify a specific edge of an Arch.Panel.
Edges are identified by 3 numbers:
<holeId>.<wireId>.<edgeId>
Let's say the edge is specified as "3.2.7", then the 7th edge of the 2nd wire in the 3rd hole returned
by the panel sheet is the edge returned.
Obviously this is as fragile as can be, but currently the best we can do while the panel sheets
hide the actual features from Path and they can't be referenced directly.
'''
ids = sub.split(".")
holeId = int(ids[0])
wireId = int(ids[1])
edgeId = int(ids[2])
for holeNr, hole in enumerate(base.Proxy.getHoles(base, transform=True)):
if holeNr == holeId:
for wireNr, wire in enumerate(hole.Wires):
if wireNr == wireId:
for edgeNr, edge in enumerate(wire.Edges):
if edgeNr == edgeId:
return edge
def holeDiameter(self, obj, base, sub):
'''holeDiameter(obj, base, sub) ... returns the diameter of the specified hole.'''
if self.baseIsArchPanel(obj, base):
edge = self.getArchPanelEdge(obj, base, sub)
return edge.BoundBox.XLength
"""holeDiameter(obj, base, sub) ... returns the diameter of the specified hole."""
try:
shape = base.Shape.getElement(sub)
if shape.ShapeType == 'Vertex':
if shape.ShapeType == "Vertex":
return 0
if shape.ShapeType == 'Edge' and type(shape.Curve) == Part.Circle:
if shape.ShapeType == "Edge" and type(shape.Curve) == Part.Circle:
return shape.Curve.Radius * 2
if shape.ShapeType == 'Face':
if shape.ShapeType == "Face":
for i in range(len(shape.Edges)):
if (type(shape.Edges[i].Curve) == Part.Circle and
shape.Edges[i].Curve.Radius * 2 < shape.BoundBox.XLength*1.1 and
shape.Edges[i].Curve.Radius * 2 > shape.BoundBox.XLength*0.9):
if (
type(shape.Edges[i].Curve) == Part.Circle
and shape.Edges[i].Curve.Radius * 2
< shape.BoundBox.XLength * 1.1
and shape.Edges[i].Curve.Radius * 2
> shape.BoundBox.XLength * 0.9
):
return shape.Edges[i].Curve.Radius * 2
# for all other shapes the diameter is just the dimension in X.
# This may be inaccurate as the BoundBox is calculated on the tessellated geometry
PathLog.warning(translate("Path", "Hole diameter may be inaccurate due to tessellation on face. Consider selecting hole edge."))
PathLog.warning(
translate(
"Path",
"Hole diameter may be inaccurate due to tessellation on face. Consider selecting hole edge.",
)
)
return shape.BoundBox.XLength
except Part.OCCError as e:
PathLog.error(e)
@@ -133,44 +122,48 @@ class ObjectOp(PathOp.ObjectOp):
return 0
def holePosition(self, obj, base, sub):
'''holePosition(obj, base, sub) ... returns a Vector for the position defined by the given features.
Note that the value for Z is set to 0.'''
if self.baseIsArchPanel(obj, base):
edge = self.getArchPanelEdge(obj, base, sub)
center = edge.Curve.Center
return FreeCAD.Vector(center.x, center.y, 0)
"""holePosition(obj, base, sub) ... returns a Vector for the position defined by the given features.
Note that the value for Z is set to 0."""
try:
shape = base.Shape.getElement(sub)
if shape.ShapeType == 'Vertex':
if shape.ShapeType == "Vertex":
return FreeCAD.Vector(shape.X, shape.Y, 0)
if shape.ShapeType == 'Edge' and hasattr(shape.Curve, 'Center'):
if shape.ShapeType == "Edge" and hasattr(shape.Curve, "Center"):
return FreeCAD.Vector(shape.Curve.Center.x, shape.Curve.Center.y, 0)
if shape.ShapeType == 'Face':
if hasattr(shape.Surface, 'Center'):
return FreeCAD.Vector(shape.Surface.Center.x, shape.Surface.Center.y, 0)
if shape.ShapeType == "Face":
if hasattr(shape.Surface, "Center"):
return FreeCAD.Vector(
shape.Surface.Center.x, shape.Surface.Center.y, 0
)
if len(shape.Edges) == 1 and type(shape.Edges[0].Curve) == Part.Circle:
return shape.Edges[0].Curve.Center
except Part.OCCError as e:
PathLog.error(e)
PathLog.error(translate("Path", "Feature %s.%s cannot be processed as a circular hole - please remove from Base geometry list.") % (base.Label, sub))
PathLog.error(
translate(
"Path",
"Feature %s.%s cannot be processed as a circular hole - please remove from Base geometry list.",
)
% (base.Label, sub)
)
return None
def isHoleEnabled(self, obj, base, sub):
'''isHoleEnabled(obj, base, sub) ... return true if hole is enabled.'''
"""isHoleEnabled(obj, base, sub) ... return true if hole is enabled."""
name = "%s.%s" % (base.Name, sub)
return not name in obj.Disabled
return name not in obj.Disabled
def opExecute(self, obj):
'''opExecute(obj) ... processes all Base features and Locations and collects
"""opExecute(obj) ... processes all Base features and Locations and collects
them in a list of positions and radii which is then passed to circularHoleExecute(obj, holes).
If no Base geometries and no Locations are present, the job's Base is inspected and all
drillable features are added to Base. In this case appropriate values for depths are also
calculated and assigned.
Do not overwrite, implement circularHoleExecute(obj, holes) instead.'''
Do not overwrite, implement circularHoleExecute(obj, holes) instead."""
PathLog.track()
def haveLocations(self, obj):
@@ -182,80 +175,91 @@ class ObjectOp(PathOp.ObjectOp):
for base, subs in obj.Base:
for sub in subs:
PathLog.debug('processing {} in {}'.format(sub, base.Name))
PathLog.debug("processing {} in {}".format(sub, base.Name))
if self.isHoleEnabled(obj, base, sub):
pos = self.holePosition(obj, base, sub)
if pos:
holes.append({'x': pos.x, 'y': pos.y, 'r': self.holeDiameter(obj, base, sub)})
holes.append(
{
"x": pos.x,
"y": pos.y,
"r": self.holeDiameter(obj, base, sub),
}
)
if haveLocations(self, obj):
for location in obj.Locations:
holes.append({'x': location.x, 'y': location.y, 'r': 0})
holes.append({"x": location.x, "y": location.y, "r": 0})
if len(holes) > 0:
self.circularHoleExecute(obj, holes)
def circularHoleExecute(self, obj, holes):
'''circularHoleExecute(obj, holes) ... implement processing of holes.
"""circularHoleExecute(obj, holes) ... implement processing of holes.
holes is a list of dictionaries with 'x', 'y' and 'r' specified for each hole.
Note that for Vertexes, non-circular Edges and Locations r=0.
Must be overwritten by subclasses.'''
Must be overwritten by subclasses."""
pass
def findAllHoles(self, obj):
'''findAllHoles(obj) ... find all holes of all base models and assign as features.'''
"""findAllHoles(obj) ... find all holes of all base models and assign as features."""
PathLog.track()
if not self.getJob(obj):
return
features = []
if 1 == len(self.model) and self.baseIsArchPanel(obj, self.model[0]):
panel = self.model[0]
holeshapes = panel.Proxy.getHoles(panel, transform=True)
tooldiameter = float(obj.ToolController.Proxy.getTool(obj.ToolController).Diameter)
for holeNr, hole in enumerate(holeshapes):
PathLog.debug('Entering new HoleShape')
for wireNr, wire in enumerate(hole.Wires):
PathLog.debug('Entering new Wire')
for edgeNr, edge in enumerate(wire.Edges):
if PathUtils.isDrillable(panel, edge, tooldiameter):
PathLog.debug('Found drillable hole edges: {}'.format(edge))
features.append((panel, "%d.%d.%d" % (holeNr, wireNr, edgeNr)))
else:
for base in self.model:
features.extend(self.findHoles(obj, base))
for base in self.model:
features.extend(self.findHoles(obj, base))
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.'''
"""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))
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))
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()))
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})
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))
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'):
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
@@ -264,9 +268,21 @@ class ObjectOp(PathOp.ObjectOp):
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})
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(
"Found hole feature %s.%s"
% (baseobject.Label, candidateFaceName)
)
PathLog.debug("holes found: {}".format(holelist))
return features
return features