From de8a04ed0e1438c3fb36db95056f0aa08bbb94bb Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Thu, 17 Dec 2020 22:13:40 -0600 Subject: [PATCH 1/3] Path: LGTM and spelling correction --- src/Mod/Path/PathScripts/PathSlot.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathSlot.py b/src/Mod/Path/PathScripts/PathSlot.py index 4795b9b1d1..a63212d007 100644 --- a/src/Mod/Path/PathScripts/PathSlot.py +++ b/src/Mod/Path/PathScripts/PathSlot.py @@ -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': From 6bf18f93c3fac1175006fcd6f45153424884f7a0 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Thu, 17 Dec 2020 22:14:51 -0600 Subject: [PATCH 2/3] Path: Add docStrings to some methods --- src/Mod/Path/PathScripts/PathSlot.py | 32 ++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathSlot.py b/src/Mod/Path/PathScripts/PathSlot.py index a63212d007..ad4f8d3197 100644 --- a/src/Mod/Path/PathScripts/PathSlot.py +++ b/src/Mod/Path/PathScripts/PathSlot.py @@ -971,9 +971,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 @@ -1019,6 +1019,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 @@ -1027,6 +1029,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) @@ -1059,6 +1063,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 @@ -1079,6 +1084,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 @@ -1099,9 +1105,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) @@ -1161,6 +1173,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 @@ -1221,6 +1237,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) @@ -1230,6 +1250,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() @@ -1247,7 +1270,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) @@ -1255,6 +1279,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 @@ -1263,6 +1288,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 From a02306d6ac59a6ed7bc347e08b033bbabe2a06bf Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Thu, 17 Dec 2020 22:18:27 -0600 Subject: [PATCH 3/3] Path: Fixes to path generation for certain selections Fixes path generation for selection of two non-parallel edges. Fixes path generation for selection of single, bottom face of slot with two non-parallel ends. Fixes path generation for selection of two parallel, bottom bounding edges of slot. --- src/Mod/Path/PathScripts/PathSlot.py | 75 ++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 15 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathSlot.py b/src/Mod/Path/PathScripts/PathSlot.py index ad4f8d3197..113e46260c 100644 --- a/src/Mod/Path/PathScripts/PathSlot.py +++ b/src/Mod/Path/PathScripts/PathSlot.py @@ -607,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: @@ -769,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] @@ -788,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', @@ -808,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', @@ -862,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): @@ -1005,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)