Merge pull request #4523 from Russ4262/slot_fixes

[Path] Stability fixes for experimental Slot operation
This commit is contained in:
sliptonic
2021-05-24 11:53:44 -05:00
committed by GitHub

View File

@@ -41,6 +41,7 @@ import math
from lazy_loader.lazy_loader import LazyLoader
Part = LazyLoader('Part', globals(), 'Part')
Arcs = LazyLoader('draftgeoutils.arcs', globals(), 'draftgeoutils.arcs')
PathGeom = LazyLoader('PathScripts.PathGeom', globals(), 'PathScripts.PathGeom')
if FreeCAD.GuiUp:
FreeCADGui = LazyLoader('FreeCADGui', globals(), 'FreeCADGui')
@@ -159,7 +160,7 @@ class ObjectSlot(PathOp.ObjectOp):
'CustomPoint1': FreeCAD.Vector(0.0, 0.0, 0.0),
'ExtendPathStart': 0.0,
'Reference1': 'Center of Mass',
'CustomPoint2': FreeCAD.Vector(10.0, 10.0, 0.0),
'CustomPoint2': FreeCAD.Vector(0.0, 0.0, 0.0),
'ExtendPathEnd': 0.0,
'Reference2': 'Center of Mass',
'LayerMode': 'Multi-pass',
@@ -353,6 +354,7 @@ class ObjectSlot(PathOp.ObjectOp):
self.arcMidPnt = None
self.arcRadius = 0.0
self.newRadius = 0.0
self.featureDetails = ["", ""]
self.isDebug = False if PathLog.getLevel(PathLog.thisModule()) != 4 else True
self.showDebugObjects = False
self.stockZMin = self.job.Stock.Shape.BoundBox.ZMin
@@ -433,35 +435,41 @@ class ObjectSlot(PathOp.ObjectOp):
# Use custom inputs here
p1 = obj.CustomPoint1
p2 = obj.CustomPoint2
if p1.z == p2.z:
if p1 == p2:
msg = translate('PathSlot',
'Custom points are identical.')
FreeCAD.Console.PrintError(msg + '\n')
return False
elif p1.z == p2.z:
pnts = (p1, p2)
featureCount = 2
else:
msg = translate('PathSlot',
'Custom points not at same Z height.')
FreeCAD.Console.PrintError(msg + '\n')
return False
baseGeom = obj.Base[0]
base, subsList = baseGeom
self.base = base
featureCount = len(subsList)
if featureCount == 1:
PathLog.debug('Reference 1: {}'.format(obj.Reference1))
sub1 = subsList[0]
shape_1 = getattr(base.Shape, sub1)
self.shape1 = shape_1
pnts = self._processSingle(obj, shape_1, sub1)
else:
PathLog.debug('Reference 1: {}'.format(obj.Reference1))
PathLog.debug('Reference 2: {}'.format(obj.Reference2))
sub1 = subsList[0]
sub2 = subsList[1]
shape_1 = getattr(base.Shape, sub1)
shape_2 = getattr(base.Shape, sub2)
self.shape1 = shape_1
self.shape2 = shape_2
pnts = self._processDouble(obj, shape_1, sub1, shape_2, sub2)
baseGeom = obj.Base[0]
base, subsList = baseGeom
self.base = base
featureCount = len(subsList)
if featureCount == 1:
PathLog.debug('Reference 1: {}'.format(obj.Reference1))
sub1 = subsList[0]
shape_1 = getattr(base.Shape, sub1)
self.shape1 = shape_1
pnts = self._processSingle(obj, shape_1, sub1)
else:
PathLog.debug('Reference 1: {}'.format(obj.Reference1))
PathLog.debug('Reference 2: {}'.format(obj.Reference2))
sub1 = subsList[0]
sub2 = subsList[1]
shape_1 = getattr(base.Shape, sub1)
shape_2 = getattr(base.Shape, sub2)
self.shape1 = shape_1
self.shape2 = shape_2
pnts = self._processDouble(obj, shape_1, sub1, shape_2, sub2)
if not pnts:
return False
@@ -608,21 +616,26 @@ class ObjectSlot(PathOp.ObjectOp):
PathLog.debug('_finishLine() Perp, featureCnt == 2')
if perpZero:
(p1, p2) = pnts
pnts = self._makePerpendicular(p1, p2, 10.0) # 10.0 offset below
initPerpDist = p1.sub(p2).Length
pnts = self._makePerpendicular(p1, p2, initPerpDist) # 10.0 offset below
else:
# Modify path points if user selected two parallel edges
if (featureCnt == 2 and self.shapeType1 == 'Edge' and
self.shapeType2 == 'Edge' and self._isParallel(self.dYdX1, self.dYdX2)):
(p1, p2) = pnts
edg1_len = self.shape1.Length
edg2_len = self.shape2.Length
set_length = max(edg1_len, edg2_len)
pnts = self._makePerpendicular(p1, p2, 10.0 + set_length) # 10.0 offset below
if edg1_len != edg2_len:
msg = obj.Label + ' '
msg += translate('PathSlot',
'Verify slot path start and end points.')
FreeCAD.Console.PrintWarning(msg + '\n')
self.shapeType2 == 'Edge'):
if self.featureDetails[0] == "arc" and self.featureDetails[1] == "arc":
perpZero = False
elif self._isParallel(self.dYdX1, self.dYdX2):
PathLog.debug('_finishLine() StE, featureCnt == 2 // edges')
(p1, p2) = pnts
edg1_len = self.shape1.Length
edg2_len = self.shape2.Length
set_length = max(edg1_len, edg2_len)
pnts = self._makePerpendicular(p1, p2, 10.0 + set_length) # 10.0 offset below
if edg1_len != edg2_len:
msg = obj.Label + ' '
msg += translate('PathSlot',
'Verify slot path start and end points.')
FreeCAD.Console.PrintWarning(msg + '\n')
else:
perpZero = False
@@ -712,21 +725,30 @@ class ObjectSlot(PathOp.ObjectOp):
pnts = False
norm = shape_1.normalAt(0.0, 0.0)
PathLog.debug('{}.normalAt(): {}'.format(sub1, norm))
if norm.z == 1 or norm.z == -1:
pnts = self._processSingleHorizFace(obj, shape_1)
elif norm.z == 0:
faceType = self._getVertFaceType(shape_1)
if faceType:
(geo, shp) = faceType
if geo == 'Face':
pnts = self._processSingleComplexFace(obj, shp)
if geo == 'Wire':
pnts = self._processSingleVertFace(obj, shp)
if geo == 'Edge':
pnts = self._processSingleVertFace(obj, shp)
if PathGeom.isRoughly(shape_1.BoundBox.ZMax, shape_1.BoundBox.ZMin):
# Horizontal face
if norm.z == 1 or norm.z == -1:
pnts = self._processSingleHorizFace(obj, shape_1)
elif norm.z == 0:
faceType = self._getVertFaceType(shape_1)
if faceType:
(geo, shp) = faceType
if geo == 'Face':
pnts = self._processSingleComplexFace(obj, shp)
if geo == 'Wire':
pnts = self._processSingleVertFace(obj, shp)
if geo == 'Edge':
pnts = self._processSingleVertFace(obj, shp)
else:
if len(shape_1.Edges) == 4:
pnts = self._processSingleHorizFace(obj, shape_1)
else:
pnts = self._processSingleComplexFace(obj, shape_1)
if not pnts:
msg = translate('PathSlot',
'The selected face is not oriented horizontally or vertically.')
'The selected face is inaccessible.')
FreeCAD.Console.PrintError(msg + '\n')
return False
@@ -862,16 +884,16 @@ class ObjectSlot(PathOp.ObjectOp):
def _processSingleComplexFace(self, obj, shape):
"""Determine slot path endpoints from a single complex face."""
PathLog.debug('_processSingleComplexFace()')
PNTS = list()
pnts = list()
def zVal(V):
return V.z
def zVal(p):
return p.z
for E in shape.Wires[0].Edges:
p = self._findLowestEdgePoint(E)
PNTS.append(p)
PNTS.sort(key=zVal)
return (PNTS[0], PNTS[1])
pnts.append(p)
pnts.sort(key=zVal)
return (pnts[0], pnts[1])
def _processSingleVertFace(self, obj, shape):
"""Determine slot path endpoints from a single vertically oriented face
@@ -925,7 +947,7 @@ class ObjectSlot(PathOp.ObjectOp):
# Check that all Z values are equal (isRoughly same)
if (abs(z1 - z2) > tolrnc or
abs(z1 - z3) > tolrnc ):
# abs(z2 - z3) > tolrnc): 3rd test redundant.
# abs(z2 - z3) > tolrnc): 3rd test redundant.
return False
return True
@@ -1182,6 +1204,9 @@ class ObjectSlot(PathOp.ObjectOp):
p = self._getHighestPoint(shape)
elif cat == 'Edge':
featDetIdx = pNum - 1
if shape.Curve.TypeId == 'Part::GeomCircle':
self.featureDetails[featDetIdx] = "arc"
# calculate slope between end vertexes
v0 = shape.Edges[0].Vertexes[0]
v1 = shape.Edges[0].Vertexes[1]
@@ -1309,13 +1334,13 @@ class ObjectSlot(PathOp.ObjectOp):
def _isParallel(self, dYdX1, dYdX2):
"""Determine if two orientation vectors are parallel."""
# if dYdX1.add(dYdX2).Length == 0:
# return True
# if ((dYdX1.x + dYdX2.x) / 2.0 == dYdX1.x and
# (dYdX1.y + dYdX2.y) / 2.0 == dYdX1.y):
# return True
# return False
return (dYdX1.cross(dYdX2) == FreeCAD.Vector(0,0,0) )
# if dYdX1.add(dYdX2).Length == 0:
# return True
# if ((dYdX1.x + dYdX2.x) / 2.0 == dYdX1.x and
# (dYdX1.y + dYdX2.y) / 2.0 == dYdX1.y):
# return True
# return False
def _makePerpendicular(self, p1, p2, length):
"""_makePerpendicular(p1, p2, length)...
@@ -1367,7 +1392,7 @@ class ObjectSlot(PathOp.ObjectOp):
def _findLowestEdgePoint(self, E):
zMin = E.BoundBox.ZMin
eLen = E.Length
L0 = 0
L0 = 0.0
L1 = eLen
p0 = None
p1 = None