Merge pull request #4159 from Russ4262/fix_slot_op
Path: Fixes to Slot op
This commit is contained in:
@@ -41,9 +41,8 @@ import math
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
Part = LazyLoader('Part', globals(), 'Part')
|
||||
Arcs = LazyLoader('draftgeoutils.arcs', globals(), 'draftgeoutils.arcs')
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
FreeCADGui = LazyLoader('FreeCADGui', globals(), 'FreeCADGui')
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
@@ -597,7 +596,7 @@ class ObjectSlot(PathOp.ObjectOp):
|
||||
return CMDS
|
||||
|
||||
def _finishLine(self, obj, pnts, featureCnt):
|
||||
"""This method finishes an Line Slot operation."""
|
||||
"""This method finishes a Line Slot operation."""
|
||||
# Apply perpendicular rotation if requested
|
||||
perpZero = True
|
||||
if obj.PathOrientation == 'Perpendicular':
|
||||
@@ -608,11 +607,27 @@ class ObjectSlot(PathOp.ObjectOp):
|
||||
BE = self.bottomEdges[0]
|
||||
pnts = self._processSingleVertFace(obj, BE)
|
||||
perpZero = False
|
||||
elif self.shapeType1 == 'Edge' and self.shapeType2 == 'Edge':
|
||||
PathLog.debug('_finishLine() Perp, featureCnt == 2')
|
||||
if perpZero:
|
||||
(p1, p2) = pnts
|
||||
pnts = self._makePerpendicular(p1, p2, 10.0) # 10.0 offset below
|
||||
else:
|
||||
perpZero = False
|
||||
# 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')
|
||||
else:
|
||||
perpZero = False
|
||||
|
||||
# Reverse direction of path if requested
|
||||
if obj.ReverseDirection:
|
||||
@@ -770,11 +785,16 @@ class ObjectSlot(PathOp.ObjectOp):
|
||||
|
||||
# Sort tuples by edge angle
|
||||
eTups.sort(key=lambda tup: tup[2])
|
||||
|
||||
# Identify parallel edges
|
||||
pairs = list()
|
||||
parallel_edge_pairs = list()
|
||||
parallel_edge_flags = list()
|
||||
flag = 1
|
||||
eCnt = len(shape.Edges)
|
||||
lstE = eCnt - 1
|
||||
for i in range(0, eCnt):
|
||||
for i in range(0, eCnt): # populate empty parrallel edge flag list
|
||||
parallel_edge_flags.append(0)
|
||||
for i in range(0, eCnt): # Cycle through edges to identify parallel pairs
|
||||
if i < lstE:
|
||||
ni = i + 1
|
||||
A = eTups[i]
|
||||
@@ -789,19 +809,24 @@ class ObjectSlot(PathOp.ObjectOp):
|
||||
if eB.Curve.TypeId not in lineTypes:
|
||||
debug = eB.Curve.TypeId
|
||||
else:
|
||||
pairs.append((eA, eB))
|
||||
parallel_edge_pairs.append((eA, eB))
|
||||
# set parallel flags for this pair of edges
|
||||
parallel_edge_flags[A[0]] = flag
|
||||
parallel_edge_flags[B[0]] = flag
|
||||
flag += 1
|
||||
if debug:
|
||||
msg = 'Erroneous Curve.TypeId: {}'.format(debug)
|
||||
PathLog.debug(msg)
|
||||
|
||||
pairCnt = len(pairs)
|
||||
pairCnt = len(parallel_edge_pairs)
|
||||
if pairCnt > 1:
|
||||
pairs.sort(key=lambda tup: tup[0].Length, reverse=True)
|
||||
parallel_edge_pairs.sort(key=lambda tup: tup[0].Length, reverse=True)
|
||||
|
||||
if self.isDebug:
|
||||
PathLog.debug(' -pairCnt: {}'.format(pairCnt))
|
||||
for (a, b) in pairs:
|
||||
for (a, b) in parallel_edge_pairs:
|
||||
PathLog.debug(' -pair: {}, {}'.format(round(a.Length, 4), round(b.Length,4)))
|
||||
PathLog.debug(' -parallel_edge_flags: {}'.format(parallel_edge_flags))
|
||||
|
||||
if pairCnt == 0:
|
||||
msg = translate('PathSlot',
|
||||
@@ -809,12 +834,24 @@ class ObjectSlot(PathOp.ObjectOp):
|
||||
FreeCAD.Console.PrintError(msg + '\n')
|
||||
return False
|
||||
elif pairCnt == 1:
|
||||
same = pairs[0]
|
||||
# One pair of parallel edges identified
|
||||
if eCnt == 4:
|
||||
flag_set = list()
|
||||
for i in range(0, 4):
|
||||
e = parallel_edge_flags[i]
|
||||
if e == 0:
|
||||
flag_set.append(shape.Edges[i])
|
||||
if len(flag_set) == 2:
|
||||
same = (flag_set[0], flag_set[1])
|
||||
else:
|
||||
same = parallel_edge_pairs[0]
|
||||
else:
|
||||
same = parallel_edge_pairs[0]
|
||||
else:
|
||||
if obj.Reference1 == 'Long Edge':
|
||||
same = pairs[1]
|
||||
same = parallel_edge_pairs[1]
|
||||
elif obj.Reference1 == 'Short Edge':
|
||||
same = pairs[0]
|
||||
same = parallel_edge_pairs[0]
|
||||
else:
|
||||
msg = 'Reference1 '
|
||||
msg += translate('PathSlot',
|
||||
@@ -863,6 +900,12 @@ class ObjectSlot(PathOp.ObjectOp):
|
||||
b1 = v0.sub(perpVect)
|
||||
b2 = v1.sub(perpVect)
|
||||
(p1, p2) = self._getCutSidePoints(obj, v0, v1, a1, a2, b1, b2)
|
||||
|
||||
msg = obj.Label + ' '
|
||||
msg += translate('PathSlot',
|
||||
'Verify slot path start and end points.')
|
||||
FreeCAD.Console.PrintWarning(msg + '\n')
|
||||
|
||||
return (p1, p2)
|
||||
|
||||
def _processSingleEdge(self, obj, edge):
|
||||
@@ -972,9 +1015,9 @@ class ObjectSlot(PathOp.ObjectOp):
|
||||
|
||||
# Methods for processing double geometry
|
||||
def _processDouble(self, obj, shape_1, sub1, shape_2, sub2):
|
||||
PathLog.debug('_processDouble()')
|
||||
"""This is the control method for slots based on a
|
||||
two Base Geometry features."""
|
||||
PathLog.debug('_processDouble()')
|
||||
|
||||
p1 = None
|
||||
p2 = None
|
||||
@@ -1006,12 +1049,13 @@ class ObjectSlot(PathOp.ObjectOp):
|
||||
|
||||
# Parallel check for twin face, and face-edge cases
|
||||
if dYdX1 and dYdX2:
|
||||
PathLog.debug('dYdX1, dYdX2: {}, {}'.format(dYdX1, dYdX2))
|
||||
if not self._isParallel(dYdX1, dYdX2):
|
||||
PathLog.debug('dYdX1, dYdX2: {}, {}'.format(dYdX1, dYdX2))
|
||||
msg = translate('PathSlot',
|
||||
'Selected geometry not parallel.')
|
||||
FreeCAD.Console.PrintError(msg + '\n')
|
||||
return False
|
||||
if self.shapeType1 != 'Edge' or self.shapeType2 != 'Edge':
|
||||
msg = translate('PathSlot',
|
||||
'Selected geometry not parallel.')
|
||||
FreeCAD.Console.PrintError(msg + '\n')
|
||||
return False
|
||||
|
||||
if p2:
|
||||
return (p1, p2)
|
||||
@@ -1020,6 +1064,8 @@ class ObjectSlot(PathOp.ObjectOp):
|
||||
|
||||
# Support methods
|
||||
def _dXdYdZ(self, E):
|
||||
"""_dXdYdZ(E) Calculates delta-X, delta-Y, and delta-Z between two vertexes
|
||||
of edge passed in. Returns these three values as vector."""
|
||||
v1 = E.Vertexes[0]
|
||||
v2 = E.Vertexes[1]
|
||||
dX = v2.X - v1.X
|
||||
@@ -1028,6 +1074,8 @@ class ObjectSlot(PathOp.ObjectOp):
|
||||
return FreeCAD.Vector(dX, dY, dZ)
|
||||
|
||||
def _normalizeVector(self, v):
|
||||
"""_normalizeVector(v)...
|
||||
Returns a copy of the vector recieved with values rounded to 10 decimal places."""
|
||||
posTol = 0.0000000001
|
||||
negTol = -1 * posTol
|
||||
V = FreeCAD.Vector(v.x, v.y, v.z)
|
||||
@@ -1060,6 +1108,7 @@ class ObjectSlot(PathOp.ObjectOp):
|
||||
return FreeCAD.Vector(x, y, z)
|
||||
|
||||
def _getLowestPoint(self, shape_1):
|
||||
"""_getLowestPoint(shape)... Returns lowest vertex of shape as vector."""
|
||||
# find lowest vertex
|
||||
vMin = shape_1.Vertexes[0]
|
||||
zmin = vMin.Z
|
||||
@@ -1080,6 +1129,7 @@ class ObjectSlot(PathOp.ObjectOp):
|
||||
return FreeCAD.Vector(V.X, V.Y, V.Z)
|
||||
|
||||
def _getHighestPoint(self, shape_1):
|
||||
"""_getHighestPoint(shape)... Returns highest vertex of shape as vector."""
|
||||
# find highest vertex
|
||||
vMax = shape_1.Vertexes[0]
|
||||
zmax = vMax.Z
|
||||
@@ -1100,9 +1150,15 @@ class ObjectSlot(PathOp.ObjectOp):
|
||||
return FreeCAD.Vector(V.X, V.Y, V.Z)
|
||||
|
||||
def _processFeature(self, obj, shape, sub, pNum):
|
||||
"""_processFeature(obj, shape, sub, pNum)...
|
||||
This function analyzes a shape and returns a three item tuple containing:
|
||||
working point,
|
||||
shape orientation/slope,
|
||||
shape category as face, edge, or vert."""
|
||||
p = None
|
||||
dYdX = None
|
||||
cat = sub[:4]
|
||||
PathLog.debug('sub-feature is {}'.format(cat))
|
||||
Ref = getattr(obj, 'Reference' + str(pNum))
|
||||
if cat == 'Face':
|
||||
BE = self._getBottomEdge(shape)
|
||||
@@ -1162,6 +1218,10 @@ class ObjectSlot(PathOp.ObjectOp):
|
||||
return False
|
||||
|
||||
def _extendArcSlot(self, p1, p2, cent, begExt, endExt):
|
||||
"""_extendArcSlot(p1, p2, cent, begExt, endExt)...
|
||||
This function extends an arc defined by two end points, p1 and p2, and the center.
|
||||
The arc is extended along the circumferance with begExt and endExt values.
|
||||
The function returns the new end points as tuple (n1, n2) to replace p1 and p2."""
|
||||
cancel = True
|
||||
n1 = p1
|
||||
n2 = p2
|
||||
@@ -1222,6 +1282,10 @@ class ObjectSlot(PathOp.ObjectOp):
|
||||
return (n1, n2)
|
||||
|
||||
def _makeOffsetArc(self, p1, p2, center, newRadius):
|
||||
"""_makeOffsetArc(p1, p2, center, newRadius)...
|
||||
This function offsets an arc defined by endpoints, p1 and p2, and the center.
|
||||
New end points are returned at the radius passed by newRadius.
|
||||
The angle of the original arc is maintained."""
|
||||
n1 = p1.sub(center).normalize()
|
||||
n2 = p2.sub(center).normalize()
|
||||
n1.multiply(newRadius)
|
||||
@@ -1231,6 +1295,9 @@ class ObjectSlot(PathOp.ObjectOp):
|
||||
return (p1, p2)
|
||||
|
||||
def _extendLineSlot(self, p1, p2, begExt, endExt):
|
||||
"""_extendLineSlot(p1, p2, begExt, endExt)...
|
||||
This function extends a line defined by endpoints, p1 and p2.
|
||||
The beginning is extended by begExt value and the end by endExt value."""
|
||||
if begExt:
|
||||
beg = p1.sub(p2)
|
||||
beg.normalize()
|
||||
@@ -1248,7 +1315,8 @@ class ObjectSlot(PathOp.ObjectOp):
|
||||
return (n1, n2)
|
||||
|
||||
def _getOppMidPoints(self, same):
|
||||
# Find mid-points between ends of equal, oppossing edges
|
||||
"""_getOppMidPoints(same)...
|
||||
Find mid-points between ends of equal, oppossing edges passed in tuple (edge1, edge2)."""
|
||||
com1 = same[0].CenterOfMass
|
||||
com2 = same[1].CenterOfMass
|
||||
p1 = FreeCAD.Vector(com1.x, com1.y, 0.0)
|
||||
@@ -1256,6 +1324,7 @@ class ObjectSlot(PathOp.ObjectOp):
|
||||
return (p1, p2)
|
||||
|
||||
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
|
||||
@@ -1264,6 +1333,9 @@ class ObjectSlot(PathOp.ObjectOp):
|
||||
return False
|
||||
|
||||
def _makePerpendicular(self, p1, p2, length):
|
||||
"""_makePerpendicular(p1, p2, length)...
|
||||
Using a line defined by p1 and p2, returns a perpendicular vector centered
|
||||
at the midpoint of the line, with length value."""
|
||||
line = Part.makeLine(p1, p2)
|
||||
midPnt = line.CenterOfMass
|
||||
|
||||
|
||||
Reference in New Issue
Block a user