From edefffae4bbc49c1dcd93c3a4b5e928211ee8fee Mon Sep 17 00:00:00 2001 From: sliptonic Date: Tue, 18 Jan 2022 17:02:33 -0600 Subject: [PATCH] basic retraction handling --- src/Mod/Path/Generators/helix_generator.py | 77 ++++++++++++------- .../Path/PathTests/TestPathHelixGenerator.py | 28 ++++++- 2 files changed, 72 insertions(+), 33 deletions(-) diff --git a/src/Mod/Path/Generators/helix_generator.py b/src/Mod/Path/Generators/helix_generator.py index e3535a67a2..2d4f2cf29e 100644 --- a/src/Mod/Path/Generators/helix_generator.py +++ b/src/Mod/Path/Generators/helix_generator.py @@ -57,7 +57,7 @@ def generate( endPoint = edge.Vertexes[1].Point PathLog.track( - "(helix: <{}, {}>\n hole radius {}\n inner radius {}\n step over {}\n start point {}\n end point {}\n step_down {}\n tool diameter {}\n direction {}\n startat {})".format( + "(helix: <{}, {}>\n hole radius {}\n inner radius {}\n step over {}\n start point {}\n end point {}\n step_down {}\n tool diameter {}\n direction {}\n startAt {})".format( startPoint.x, startPoint.y, hole_radius, @@ -72,18 +72,18 @@ def generate( ) ) - if not type(hole_radius) in [float, int]: + if type(hole_radius) not in [float, int]: raise ValueError("hole_radius must be a float") - if not type(inner_radius) in [float, int]: - raise ValueError("inner_radius must be a float") - - if not type(tool_diameter) in [float, int]: - raise ValueError("tool_diameter must be a float") - if hole_radius < 0.0: raise ValueError("hole_radius < 0") + if type(inner_radius) not in [float, int]: + raise ValueError("inner_radius must be a float") + + if type(tool_diameter) not in [float, int]: + raise ValueError("tool_diameter must be a float") + if inner_radius > 0 and hole_radius - inner_radius < tool_diameter: raise ValueError( "hole_radius - inner_radius = {0} is < tool diameter of {1}".format( @@ -91,7 +91,7 @@ def generate( ) ) - if inner_radius == 0.0 and not hole_radius > tool_diameter: + if not hole_radius * 2 > tool_diameter: raise ValueError( "Cannot helix a hole of diameter {0} with a tool of diameter {1}".format( 2 * hole_radius, tool_diameter @@ -115,13 +115,13 @@ def generate( if inner_radius > 0: PathLog.debug("(annulus mode)\n") - hole_radius = hole_radius - tool_diameter / 2 - inner_radius = inner_radius + tool_diameter / 2 - if abs((hole_radius - inner_radius) / step_over) < 1e-5: - radii = [(hole_radius + inner_radius) / 2] + outer_radius = hole_radius - tool_diameter / 2 + step_radius = inner_radius + tool_diameter / 2 + if abs((outer_radius - step_radius) / step_over) < 1e-5: + radii = [(outer_radius + step_radius) / 2] else: - nr = max(int(ceil((hole_radius - inner_radius) / step_over)), 2) - radii = linspace(hole_radius, inner_radius, nr) + nr = max(int(ceil((outer_radius - step_radius) / step_over)), 2) + radii = linspace(outer_radius, step_radius, nr) elif hole_radius <= 2 * step_over: PathLog.debug("(single helix mode)\n") @@ -132,13 +132,14 @@ def generate( 2 * hole_radius, tool_diameter ) ) + outer_radius = hole_radius else: PathLog.debug("(full hole mode)\n") - hole_radius = hole_radius - tool_diameter / 2 - inner_radius = step_over / 2 + outer_radius = hole_radius - tool_diameter / 2 + step_radius = step_over / 2 - nr = max(1 + int(ceil((hole_radius - inner_radius) / step_over)), 2) - radii = [r for r in linspace(hole_radius, inner_radius, nr) if r > 0] + nr = max(1 + int(ceil((outer_radius - step_radius) / step_over)), 2) + radii = [r for r in linspace(outer_radius, step_radius, nr) if r > 0] if not radii: raise ValueError( "Cannot helix a hole of diameter {0} with a tool of diameter {1}".format( @@ -207,18 +208,35 @@ def generate( }, ) ) - if hole_radius <= tool_diameter: - # no plug remains, safe to move to center for retract - commandlist.append( + return commandlist + + def retract(): + # try to move to a safe place to retract without leaving a dwell + # mark + retractcommands = [] + # Calculate retraction + if hole_radius <= tool_diameter: # simple case where center is clear + center_clear = True + + elif startAt == "Inside" and inner_radius == 0.0: # middle is clear + center_clear = True + else: + center_clear = False + + if center_clear: + retractcommands.append( Path.Command("G0", {"X": endPoint.x, "Y": endPoint.y, "Z": endPoint.z}) ) - commandlist.append(Path.Command("G0", {"Z": startPoint.z})) - commandlist.append( - Path.Command( - "G0", {"X": startPoint.x, "Y": startPoint.y, "Z": startPoint.z} - ) - ) - return commandlist + + # Technical Debt. + # If the operation is clearing multiple passes in annulus mode (inner + # radius > 0.0 and len(radii) > 1) then there is a derivable + # safe place which does not touch the inner or outer wall on all radii except + # the first. This is left as a future improvement. + + retractcommands.append(Path.Command("G0", {"Z": startPoint.z})) + + return retractcommands if startAt == "Inside": radii = radii[::-1] @@ -226,5 +244,6 @@ def generate( commands = [] for r in radii: commands.extend(helix_cut_r(r)) + commands.extend(retract()) return commands diff --git a/src/Mod/Path/PathTests/TestPathHelixGenerator.py b/src/Mod/Path/PathTests/TestPathHelixGenerator.py index 27b44117b6..ea2b477823 100644 --- a/src/Mod/Path/PathTests/TestPathHelixGenerator.py +++ b/src/Mod/Path/PathTests/TestPathHelixGenerator.py @@ -59,8 +59,8 @@ G2 I-7.500000 J0.000000 X-2.500000 Y5.000000 Z18.500000\ G2 I7.500000 J0.000000 X12.500000 Y5.000000 Z18.000000\ G2 I-7.500000 J0.000000 X-2.500000 Y5.000000 Z18.000000\ G2 I7.500000 J0.000000 X12.500000 Y5.000000 Z18.000000\ -G0 Z20.000000\ -G0 X5.000000 Y5.000000 Z20.000000" +G0 X5.000000 Y5.000000 Z18.000000\ +G0 Z20.000000" def test00(self): @@ -106,8 +106,8 @@ G0 X5.000000 Y5.000000 Z20.000000" args["tool_diameter"] = 5.0 self.assertRaises(ValueError, generator.generate, **args) - # require tool fit 2: hole radius less than tool diam with zero inner radius - args["hole_radius"] = 4.5 + # require tool fit 2: hole diameter not greater than tool diam with zero inner radius + args["hole_radius"] = 2.0 args["inner_radius"] = 0.0 args["tool_diameter"] = 5.0 self.assertRaises(ValueError, generator.generate, **args) @@ -161,3 +161,23 @@ G0 X5.000000 Y5.000000 Z20.000000" self.assertRaises(ValueError, generator.generate, **args) + def test10(self): + """Test Helix Retraction""" + + # if center is clear, the second to last move should be a rapid away + # from the wall + args = _resetArgs() + v1 = FreeCAD.Vector(0, 0, 20) + v2 = FreeCAD.Vector(0, 0, 18) + edg = Part.makeLine(v1, v2) + args["edge"] = edg + args["inner_radius"] = 0.0 + args["tool_diameter"] = 5.0 + result = generator.generate(**args) + self.assertTrue(result[-2].Name == "G0") + + # if center is not clear, retraction is one straight up on the last + # move. the second to last move should be a G2 + args["inner_radius"] = 2.0 + result = generator.generate(**args) + self.assertTrue(result[-2].Name == "G2")