diff --git a/src/Mod/Path/PathScripts/PathAdaptive.py b/src/Mod/Path/PathScripts/PathAdaptive.py index 162406cfb3..e171813f80 100644 --- a/src/Mod/Path/PathScripts/PathAdaptive.py +++ b/src/Mod/Path/PathScripts/PathAdaptive.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # *************************************************************************** # * Copyright (c) 2018 Kresimir Tusek * +# * Copyright (c) 2019-2021 Schildkroet * # * * # * This file is part of the FreeCAD CAx development system. * # * * @@ -20,12 +21,6 @@ # * Suite 330, Boston, MA 02111-1307, USA * # * * # *************************************************************************** -# * * -# * Additional modifications and contributions beginning 2019 * -# * by Schildkroet. (https://github.com/Schildkroet) * -# * * -# *************************************************************************** - import PathScripts.PathOp as PathOp import PathScripts.PathUtils as PathUtils @@ -88,6 +83,13 @@ def discretize(edge, flipDirection = False): return pts +def CalcHelixConePoint(height, cur_z, radius, angle): + x = ((height - cur_z) / height) * radius * math.cos(math.radians(angle)*cur_z) + y = ((height - cur_z) / height) * radius * math.sin(math.radians(angle)*cur_z) + z = cur_z + + return {'X': x, 'Y': y, 'Z': z} + def GenerateGCode(op,obj,adaptiveResults, helixDiameter): # pylint: disable=unused-argument if len(adaptiveResults) == 0 or len(adaptiveResults[0]["AdaptivePaths"]) == 0: @@ -112,6 +114,11 @@ def GenerateGCode(op,obj,adaptiveResults, helixDiameter): if float(obj.HelixAngle) < 1: obj.HelixAngle = 1 + if float(obj.HelixAngle) > 89: + obj.HelixAngle = 89 + + if float(obj.HelixConeAngle) < 0: + obj.HelixConeAngle = 0 helixAngleRad = math.pi * float(obj.HelixAngle) / 180.0 depthPerOneCircle = length * math.tan(helixAngleRad) @@ -121,7 +128,6 @@ def GenerateGCode(op,obj,adaptiveResults, helixDiameter): if stepUp < 0: stepUp = 0 - finish_step = obj.FinishDepth.Value if hasattr(obj, "FinishDepth") else 0.0 if finish_step > stepDown: finish_step = stepDown @@ -136,7 +142,6 @@ def GenerateGCode(op,obj,adaptiveResults, helixDiameter): user_depths=None) - # ml: this is dangerous because it'll hide all unused variables hence forward # however, I don't know what lx and ly signify so I'll leave them for now # pylint: disable=unused-variable @@ -160,8 +165,8 @@ def GenerateGCode(op,obj,adaptiveResults, helixDiameter): p2 = region["StartPoint"] helixRadius = math.sqrt((p1[0]-p2[0]) * (p1[0]-p2[0]) + (p1[1]-p2[1]) * (p1[1]-p2[1])) - # helix ramp - if helixRadius > 0.001: + # Helix ramp + if helixRadius > 0.01: r = helixRadius - 0.01 maxfi = passDepth / depthPerOneCircle * 2 * math.pi @@ -183,26 +188,80 @@ def GenerateGCode(op,obj,adaptiveResults, helixDiameter): # move to start depth op.commandlist.append(Path.Command("G1", {"X": helixStart[0], "Y": helixStart[1], "Z": passStartDepth, "F": op.vertFeed})) - while fi < maxfi: - x = region["HelixCenterPoint"][0] + r * math.cos(fi+offsetFi) - y = region["HelixCenterPoint"][1] + r * math.sin(fi+offsetFi) - z = passStartDepth - fi / maxfi * (passStartDepth - passEndDepth) - op.commandlist.append(Path.Command("G1", { "X": x, "Y":y, "Z":z, "F": op.vertFeed})) - lx = x - ly = y - fi=fi+math.pi/16 + if obj.HelixConeAngle == 0: + while fi < maxfi: + x = region["HelixCenterPoint"][0] + r * math.cos(fi+offsetFi) + y = region["HelixCenterPoint"][1] + r * math.sin(fi+offsetFi) + z = passStartDepth - fi / maxfi * (passStartDepth - passEndDepth) + op.commandlist.append(Path.Command("G1", { "X": x, "Y":y, "Z":z, "F": op.vertFeed})) + lx = x + ly = y + fi=fi+math.pi/16 + + # one more circle at target depth to make sure center is cleared + maxfi = maxfi + 2*math.pi + while fi < maxfi: + x = region["HelixCenterPoint"][0] + r * math.cos(fi+offsetFi) + y = region["HelixCenterPoint"][1] + r * math.sin(fi+offsetFi) + z = passEndDepth + op.commandlist.append(Path.Command("G1", { "X": x, "Y":y, "Z":z, "F": op.horizFeed})) + lx = x + ly = y + fi = fi + math.pi/16 + + else: + # Cone + _HelixAngle = 360 - (float(obj.HelixAngle) * 4) + + if obj.HelixConeAngle > 6: + obj.HelixConeAngle = 6 + + helixRadius *= 0.9 + + # Calculate everything + helix_height = passStartDepth - passEndDepth + r_extra = helix_height * math.tan(math.radians(obj.HelixConeAngle)) + HelixTopRadius = helixRadius + r_extra + helix_full_height = HelixTopRadius * (math.cos(math.radians(obj.HelixConeAngle)) / math.sin(math.radians(obj.HelixConeAngle))) + + # Start height + z = passStartDepth + i = 0 + + # Default step down + z_step = 0.05 + + # Bigger angle, smaller step down + if _HelixAngle > 120: + z_step = 0.025 + if _HelixAngle > 240: + z_step = 0.015 + + p = None + # Calculate conical helix + while(z >= passEndDepth): + if z < passEndDepth: + z = passEndDepth + + p = CalcHelixConePoint(helix_full_height, i, HelixTopRadius, _HelixAngle) + op.commandlist.append(Path.Command("G1", { "X": p['X'] + region["HelixCenterPoint"][0], "Y": p['Y'] + region["HelixCenterPoint"][1], "Z": z, "F": op.vertFeed})) + z = z - z_step + i = i + z_step + + # Calculate some stuff for arcs at bottom + p['X'] = p['X'] + region["HelixCenterPoint"][0] + p['Y'] = p['Y'] + region["HelixCenterPoint"][1] + x_m = region["HelixCenterPoint"][0] - p['X'] + region["HelixCenterPoint"][0] + y_m = region["HelixCenterPoint"][1] - p['Y'] + region["HelixCenterPoint"][1] + i_off = (x_m - p['X']) / 2 + j_off = (y_m - p['Y']) / 2 + + # One more circle at target depth to make sure center is cleared + op.commandlist.append(Path.Command("G3", { "X": x_m, "Y": y_m, "Z": passEndDepth, "I": i_off, "J": j_off, "F": op.horizFeed})) + op.commandlist.append(Path.Command("G3", { "X": p['X'], "Y": p['Y'], "Z": passEndDepth, "I": -i_off, "J": -j_off, "F": op.horizFeed})) - # one more circle at target depth to make sure center is cleared - maxfi = maxfi + 2*math.pi - while fi < maxfi: - x = region["HelixCenterPoint"][0] + r * math.cos(fi+offsetFi) - y = region["HelixCenterPoint"][1] + r * math.sin(fi+offsetFi) - z = passEndDepth - op.commandlist.append(Path.Command("G1", { "X": x, "Y":y, "Z":z, "F": op.horizFeed})) - lx = x - ly = y - fi = fi + math.pi/16 else: + # Use arcs for helix - no conical shape support helixStart = [region["HelixCenterPoint"][0] + r, region["HelixCenterPoint"][1]] # rapid move to start point @@ -220,16 +279,16 @@ def GenerateGCode(op,obj,adaptiveResults, helixDiameter): curDep = passStartDepth while curDep > (passEndDepth + depthPerOneCircle): - op.commandlist.append(Path.Command("G2", { "X": x - (2*r), "Y": y, "Z": curDep - (depthPerOneCircle/2), "I": -r, "F": op.horizFeed})) - op.commandlist.append(Path.Command("G2", { "X": x, "Y": y, "Z": curDep - depthPerOneCircle, "I": r, "F": op.horizFeed})) + op.commandlist.append(Path.Command("G2", { "X": x - (2*r), "Y": y, "Z": curDep - (depthPerOneCircle/2), "I": -r, "F": op.vertFeed})) + op.commandlist.append(Path.Command("G2", { "X": x, "Y": y, "Z": curDep - depthPerOneCircle, "I": r, "F": op.vertFeed})) curDep = curDep - depthPerOneCircle lastStep = curDep - passEndDepth if lastStep > (depthPerOneCircle/2): - op.commandlist.append(Path.Command("G2", { "X": x - (2*r), "Y": y, "Z": curDep - (lastStep/2), "I": -r, "F": op.horizFeed})) - op.commandlist.append(Path.Command("G2", { "X": x, "Y": y, "Z": passEndDepth, "I": r, "F": op.horizFeed})) + op.commandlist.append(Path.Command("G2", { "X": x - (2*r), "Y": y, "Z": curDep - (lastStep/2), "I": -r, "F": op.vertFeed})) + op.commandlist.append(Path.Command("G2", { "X": x, "Y": y, "Z": passEndDepth, "I": r, "F": op.vertFeed})) else: - op.commandlist.append(Path.Command("G2", { "X": x - (2*r), "Y": y, "Z": passEndDepth, "I": -r, "F": op.horizFeed})) + op.commandlist.append(Path.Command("G2", { "X": x - (2*r), "Y": y, "Z": passEndDepth, "I": -r, "F": op.vertFeed})) op.commandlist.append(Path.Command("G1", {"X": x, "Y": y, "Z": passEndDepth, "F": op.vertFeed})) # one more circle at target depth to make sure center is cleared @@ -512,6 +571,7 @@ class PathAdaptive(PathOp.ObjectOp): obj.setEditorMode('AdaptiveInputState', 2) #hide this property obj.setEditorMode('AdaptiveOutputState', 2) #hide this property obj.addProperty("App::PropertyAngle", "HelixAngle", "Adaptive", "Helix ramp entry angle (degrees)") + obj.addProperty("App::PropertyAngle", "HelixConeAngle", "Adaptive", "Helix cone angle (degrees)") obj.addProperty("App::PropertyLength", "HelixDiameterLimit", "Adaptive", "Limit helix entry diameter, if limit larger than tool diameter or 0, tool diameter is used") @@ -527,6 +587,7 @@ class PathAdaptive(PathOp.ObjectOp): obj.Stopped = False obj.StopProcessing = False obj.HelixAngle = 5 + obj.HelixConeAngle = 0 obj.HelixDiameterLimit = 0.0 obj.AdaptiveInputState ="" obj.AdaptiveOutputState = "" diff --git a/src/Mod/Path/PathScripts/PathAdaptiveGui.py b/src/Mod/Path/PathScripts/PathAdaptiveGui.py index e8c6d8da3f..0e25e92128 100644 --- a/src/Mod/Path/PathScripts/PathAdaptiveGui.py +++ b/src/Mod/Path/PathScripts/PathAdaptiveGui.py @@ -78,13 +78,22 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): # helix angle form.HelixAngle = QtGui.QDoubleSpinBox() - form.HelixAngle.setMinimum(0.1) - form.HelixAngle.setMaximum(90) - form.HelixAngle.setSingleStep(0.1) + form.HelixAngle.setMinimum(1) + form.HelixAngle.setMaximum(89) + form.HelixAngle.setSingleStep(1) form.HelixAngle.setValue(5) form.HelixAngle.setToolTip("Angle of the helix ramp entry") formLayout.addRow(QtGui.QLabel("Helix Ramp Angle"), form.HelixAngle) + # helix cone angle + form.HelixConeAngle = QtGui.QDoubleSpinBox() + form.HelixConeAngle.setMinimum(0) + form.HelixConeAngle.setMaximum(6) + form.HelixConeAngle.setSingleStep(1) + form.HelixConeAngle.setValue(0) + form.HelixConeAngle.setToolTip("Angle of the helix entry cone") + formLayout.addRow(QtGui.QLabel("Helix Cone Angle"), form.HelixConeAngle) + # helix diam. limit form.HelixDiameterLimit = QtGui.QDoubleSpinBox() form.HelixDiameterLimit.setMinimum(0.0) @@ -151,6 +160,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): signals.append(self.form.StepOver.valueChanged) signals.append(self.form.Tolerance.valueChanged) signals.append(self.form.HelixAngle.valueChanged) + signals.append(self.form.HelixConeAngle.valueChanged) signals.append(self.form.HelixDiameterLimit.valueChanged) signals.append(self.form.LiftDistance.valueChanged) signals.append(self.form.KeepToolDownRatio.valueChanged) @@ -169,6 +179,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): self.form.StepOver.setValue(obj.StepOver) self.form.Tolerance.setValue(int(obj.Tolerance * 100)) self.form.HelixAngle.setValue(obj.HelixAngle) + self.form.HelixConeAngle.setValue(obj.HelixConeAngle) self.form.HelixDiameterLimit.setValue(obj.HelixDiameterLimit) self.form.LiftDistance.setValue(obj.LiftDistance) if hasattr(obj, 'KeepToolDownRatio'): @@ -198,6 +209,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): obj.StepOver = self.form.StepOver.value() obj.Tolerance = 1.0 * self.form.Tolerance.value() / 100.0 obj.HelixAngle = self.form.HelixAngle.value() + obj.HelixConeAngle = self.form.HelixConeAngle.value() obj.HelixDiameterLimit = self.form.HelixDiameterLimit.value() obj.LiftDistance = self.form.LiftDistance.value()