From 8aa7117af7a03f9fd1c7b89640f97ce6e199b7bb Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Fri, 29 Aug 2025 11:15:28 -0400 Subject: [PATCH 01/13] [CAM] initial implementation of automatic helix size selection for adaptive --- src/Mod/CAM/Path/Op/Adaptive.py | 60 ++++++++++++++++++++++---- src/Mod/CAM/libarea/Adaptive.cpp | 72 ++++++++++++++++++++++---------- src/Mod/CAM/libarea/Adaptive.hpp | 9 ++-- src/Mod/CAM/libarea/pyarea.cpp | 3 +- 4 files changed, 110 insertions(+), 34 deletions(-) diff --git a/src/Mod/CAM/Path/Op/Adaptive.py b/src/Mod/CAM/Path/Op/Adaptive.py index 4d8fb39dce..3e310d1e62 100644 --- a/src/Mod/CAM/Path/Op/Adaptive.py +++ b/src/Mod/CAM/Path/Op/Adaptive.py @@ -118,7 +118,7 @@ def discretize(edge, flipDirection=False): return pts -def GenerateGCode(op, obj, adaptiveResults, helixDiameter): +def GenerateGCode(op, obj, adaptiveResults): if not adaptiveResults or not adaptiveResults[0]["AdaptivePaths"]: return @@ -699,7 +699,8 @@ def ExecuteModelAware(op, obj): FreeCADGui.updateGui() try: - helixDiameter = obj.HelixDiameterLimit.Value + helixDiameter = obj.HelixIdealDiameterPercent / 100 * op.tool.Diameter.Value + helixMinDiameter = obj.HelixMinDiameterPercent / 100 * op.tool.Diameter.Value topZ = op.stock.Shape.BoundBox.ZMax obj.Stopped = False obj.StopProcessing = False @@ -774,6 +775,7 @@ def ExecuteModelAware(op, obj): "stockGeometry": stockPaths, "stepover": obj.StepOver, "effectiveHelixDiameter": helixDiameter, + "helixMinDiameter": helixMinDiameter, "operationType": "Clearing", "side": "Outside", "forceInsideOut": obj.ForceInsideOut, @@ -793,6 +795,7 @@ def ExecuteModelAware(op, obj): "stockGeometry": stockPaths, "stepover": obj.StepOver, "effectiveHelixDiameter": helixDiameter, + "helixMinDiameter": helixMinDiameter, "operationType": "Clearing", "side": "Inside", "forceInsideOut": obj.ForceInsideOut, @@ -857,7 +860,8 @@ def ExecuteModelAware(op, obj): a2d = area.Adaptive2d() a2d.stepOverFactor = 0.01 * obj.StepOver a2d.toolDiameter = op.tool.Diameter.Value - a2d.helixRampDiameter = helixDiameter + a2d.helixRampTargetDiameter = helixDiameter + a2d.helixRampMinDiameter = helixMinDiameter a2d.keepToolDownDistRatio = keepToolDownRatio # NOTE: Z stock is handled in our stepdowns a2d.stockToLeave = obj.StockToLeave.Value @@ -946,7 +950,7 @@ def ExecuteModelAware(op, obj): ) # GENERATE - GenerateGCode(op, obj, adaptiveResults, helixDiameter) + GenerateGCode(op, obj, adaptiveResults) if not obj.StopProcessing: Path.Log.info("*** Done. Elapsed time: %f sec\n\n" % (time.time() - start)) @@ -1810,12 +1814,21 @@ class PathAdaptive(PathOp.ObjectOp): ), ) obj.addProperty( - "App::PropertyLength", - "HelixDiameterLimit", + "App::PropertyPercent", + "HelixIdealDiameterPercent", "Adaptive", QT_TRANSLATE_NOOP( "App::Property", - "Limit helix entry diameter, if limit larger than tool diameter or 0, tool diameter is used", + "Ideal helix entry diameter, as a percentage of the tool diameter", + ), + ) + obj.addProperty( + "App::PropertyPercent", + "HelixMinDiameterPercent", + "Adaptive", + QT_TRANSLATE_NOOP( + "App::Property", + "Minimum acceptable helix entry diameter, as a percentage of the tool diameter", ), ) obj.addProperty( @@ -1874,7 +1887,8 @@ class PathAdaptive(PathOp.ObjectOp): obj.StopProcessing = False obj.HelixAngle = 5 obj.HelixConeAngle = 0 - obj.HelixDiameterLimit = 0.0 + obj.HelixIdealDiameterPercent = 100 + obj.HelixMinDiameterPercent = 10 obj.AdaptiveInputState = "" obj.AdaptiveOutputState = "" obj.StockToLeave = 0 @@ -1964,6 +1978,33 @@ class PathAdaptive(PathOp.ObjectOp): obj.addProperty("Part::PropertyPartShape", "removalshape", "Path", "") obj.setEditorMode("removalshape", 2) # hide + if hasattr(obj, "HelixDiameterLimit"): + oldD = obj.HelixDiameterLimit + obj.removeProperty("HelixDiameterLimit") + obj.addProperty( + "App::PropertyPercent", + "HelixIdealDiameterPercent", + "Adaptive", + QT_TRANSLATE_NOOP( + "App::Property", + "Ideal helix entry diameter, as a percentage of the tool diameter", + ), + ) + obj.addProperty( + "App::PropertyPercent", + "HelixMinDiameterPercent", + "Adaptive", + QT_TRANSLATE_NOOP( + "App::Property", + "Minimum acceptable helix entry diameter, as a percentage of the tool diameter", + ), + ) + obj.HelixMinDiameterPercent = 10 + if hasattr(obj, "ToolController"): + obj.HelixIdealDiameterPercent = ( + 75 if oldD == 0 else 100 * oldD / obj.ToolController.Tool.Diameter.Value + ) + FeatureExtensions.initialize_properties(obj) @@ -1986,7 +2027,8 @@ def SetupProperties(): "AdaptiveOutputState", "HelixAngle", "HelixConeAngle", - "HelixDiameterLimit", + "HelixIdealDiameterPercent", + "HelixMinDiameterPercent", "UseOutline", "OrderCutsByRegion", ] diff --git a/src/Mod/CAM/libarea/Adaptive.cpp b/src/Mod/CAM/libarea/Adaptive.cpp index 5a3015a190..d0a52c477d 100644 --- a/src/Mod/CAM/libarea/Adaptive.cpp +++ b/src/Mod/CAM/libarea/Adaptive.cpp @@ -1773,17 +1773,15 @@ std::list Adaptive2d::Execute( lastProgressTime = clock(); stopProcessing = false; - if (helixRampDiameter < NTOL) { - helixRampDiameter = 0.75 * toolDiameter; - } - if (helixRampDiameter > toolDiameter) { - helixRampDiameter = toolDiameter; - } - if (helixRampDiameter < toolDiameter / 8) { - helixRampDiameter = toolDiameter / 8; + if (helixRampTargetDiameter < NTOL) { + helixRampTargetDiameter = toolDiameter; } + helixRampTargetDiameter = min(helixRampTargetDiameter, toolDiameter); + helixRampMinDiameter = max(helixRampMinDiameter, toolDiameter / 8); + helixRampTargetDiameter = max(helixRampTargetDiameter, helixRampMinDiameter); - helixRampRadiusScaled = long(helixRampDiameter * scaleFactor / 2); + helixRampMaxRadiusScaled = long(helixRampTargetDiameter * scaleFactor / 2); + helixRampMinRadiusScaled = long(helixRampMinDiameter * scaleFactor / 2); if (finishingProfile) { finishPassOffsetScaled = long(stepOverScaled / 10); } @@ -1812,7 +1810,7 @@ std::list Adaptive2d::Execute( #ifdef DEV_MODE cout << "optimalCutAreaPD:" << optimalCutAreaPD << " scaleFactor:" << scaleFactor << " toolRadiusScaled:" << toolRadiusScaled - << " helixRampRadiusScaled:" << helixRampRadiusScaled << endl; + << " helixRampMaxRadiusScaled:" << helixRampMaxRadiusScaled << endl; #endif //****************************** // Convert input paths to clipper @@ -1912,8 +1910,8 @@ std::list Adaptive2d::Execute( if (opType == OperationType::otProfilingInside || opType == OperationType::otProfilingOutside) { double offset = opType == OperationType::otProfilingInside - ? -2 * (helixRampRadiusScaled + toolRadiusScaled) - MIN_STEP_CLIPPER - : 2 * (helixRampRadiusScaled + toolRadiusScaled) + MIN_STEP_CLIPPER; + ? -2 * (helixRampMaxRadiusScaled + toolRadiusScaled) - MIN_STEP_CLIPPER + : 2 * (helixRampMaxRadiusScaled + toolRadiusScaled) + MIN_STEP_CLIPPER; for (const auto& current : inputPaths) { int nesting = getPathNestingLevel(current, inputPaths); if (nesting % 2 != 0 && (polyTreeNestingLimit == 0 || nesting <= polyTreeNestingLimit)) { @@ -1977,7 +1975,8 @@ bool Adaptive2d::FindEntryPoint( ClearedArea& clearedArea /*output-initial cleared area by helix*/, IntPoint& entryPoint /*output*/, IntPoint& toolPos, - DoublePoint& toolDir + DoublePoint& toolDir, + long& helixRadiusScaled ) { Paths incOffset; @@ -2018,13 +2017,12 @@ bool Adaptive2d::FindEntryPoint( } } // check if helix fits - if (found) { - // make initial polygon cleared by helix ramp + const auto checkHelixFit = [&](long testHelixRadiusScaled) { clipof.Clear(); Path p1; p1.push_back(entryPoint); clipof.AddPath(p1, JoinType::jtRound, EndType::etOpenRound); - clipof.Execute(clearedPaths, helixRampRadiusScaled + toolRadiusScaled); + clipof.Execute(clearedPaths, (double)(testHelixRadiusScaled + toolRadiusScaled)); CleanPolygons(clearedPaths); // we got first cleared area - check if it is crossing boundary clip.Clear(); @@ -2032,11 +2030,32 @@ bool Adaptive2d::FindEntryPoint( clip.AddPaths(boundPaths, PolyType::ptClip, true); Paths crossing; clip.Execute(ClipType::ctDifference, crossing); - if (!crossing.empty()) { - // helix does not fit to the cutting area + + return crossing.empty(); + }; + + if (found) { + // check that helix fits, and make initial polygon cleared by helix ramp + if (!checkHelixFit(helixRampMinRadiusScaled)) { + // min-size helix does not fit found = false; } else { + // find the largest helix that fits + // minSize = largest known fit; maxSize = largest possible fit + long minSize = helixRampMinRadiusScaled; + long maxSize = helixRampMaxRadiusScaled; + while (minSize < maxSize) { + long testSize = (minSize + maxSize + 1) / 2; // always testSize > minSize + if (checkHelixFit(testSize)) { + minSize = testSize; + } + else { + maxSize = testSize - 1; // always maxSize >= minSize + } + } + helixRadiusScaled = minSize; + checkHelixFit(helixRadiusScaled); // set clearedPaths for final size clearedArea.SetClearedPaths(clearedPaths); } } @@ -2070,14 +2089,15 @@ bool Adaptive2d::FindEntryPoint( hp << entryPoint; clipof.AddPath(hp, JoinType::jtRound, EndType::etOpenRound); Paths hps; - clipof.Execute(hps, helixRampRadiusScaled); + clipof.Execute(hps, helixRadiusScaled); AddPathsToProgress(progressPaths, hps); - toolPos = IntPoint(entryPoint.X, entryPoint.Y - helixRampRadiusScaled); + toolPos = IntPoint(entryPoint.X, entryPoint.Y - helixRadiusScaled); toolDir = DoublePoint(1.0, 0.0); } return found; } + bool Adaptive2d::FindEntryPointOutside( TPaths& progressPaths, const Paths& toolBoundPaths, @@ -2780,6 +2800,7 @@ void Adaptive2d::ProcessPolyNode(Paths boundPaths, Paths toolBoundPaths) ClipperOffset clipof; IntPoint entryPoint; + long helixRadiusScaled; TPaths progressPaths; progressPaths.reserve(10000); @@ -2824,7 +2845,16 @@ void Adaptive2d::ProcessPolyNode(Paths boundPaths, Paths toolBoundPaths) outsideEntry = true; } else { - if (!FindEntryPoint(progressPaths, toolBoundPaths, boundPaths, cleared, entryPoint, toolPos, toolDir)) { + if (!FindEntryPoint( + progressPaths, + toolBoundPaths, + boundPaths, + cleared, + entryPoint, + toolPos, + toolDir, + helixRadiusScaled + )) { Perf_ProcessPolyNode.Stop(); return; } diff --git a/src/Mod/CAM/libarea/Adaptive.hpp b/src/Mod/CAM/libarea/Adaptive.hpp index 03b63ad270..4c0690eab8 100644 --- a/src/Mod/CAM/libarea/Adaptive.hpp +++ b/src/Mod/CAM/libarea/Adaptive.hpp @@ -87,7 +87,8 @@ class Adaptive2d public: Adaptive2d(); double toolDiameter = 5; - double helixRampDiameter = 0; + double helixRampTargetDiameter = 0; + double helixRampMinDiameter = 0; double stepOverFactor = 0.2; double tolerance = 0.1; double stockToLeave = 0; @@ -118,7 +119,8 @@ private: double stepOverScaled = 1; long toolRadiusScaled = 10; long finishPassOffsetScaled = 0; - long helixRampRadiusScaled = 0; + long helixRampMaxRadiusScaled = 0; + long helixRampMinRadiusScaled = 0; double referenceCutArea = 0; double optimalCutAreaPD = 0; bool stopProcessing = false; @@ -136,7 +138,8 @@ private: ClearedArea& cleared /*output*/, IntPoint& entryPoint /*output*/, IntPoint& toolPos, - DoublePoint& toolDir + DoublePoint& toolDir, + long& helixRadiusScaled ); bool FindEntryPointOutside( TPaths& progressPaths, diff --git a/src/Mod/CAM/libarea/pyarea.cpp b/src/Mod/CAM/libarea/pyarea.cpp index ee3cfd483b..5cecf5c1b3 100644 --- a/src/Mod/CAM/libarea/pyarea.cpp +++ b/src/Mod/CAM/libarea/pyarea.cpp @@ -426,7 +426,8 @@ void init_pyarea(py::module& m) .def_readwrite("stepOverFactor", &Adaptive2d::stepOverFactor) .def_readwrite("toolDiameter", &Adaptive2d::toolDiameter) .def_readwrite("stockToLeave", &Adaptive2d::stockToLeave) - .def_readwrite("helixRampDiameter", &Adaptive2d::helixRampDiameter) + .def_readwrite("helixRampTargetDiameter", &Adaptive2d::helixRampTargetDiameter) + .def_readwrite("helixRampMinDiameter", &Adaptive2d::helixRampMinDiameter) .def_readwrite("forceInsideOut", &Adaptive2d::forceInsideOut) .def_readwrite("finishingProfile", &Adaptive2d::finishingProfile) //.def_readwrite("polyTreeNestingLimit", &Adaptive2d::polyTreeNestingLimit) From 80d78efc6bdd583807e1474330592ffa8b6f86a3 Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Fri, 29 Aug 2025 13:22:08 -0400 Subject: [PATCH 02/13] [CAM] update/fix adaptive UI panel and parameter limits --- src/Mod/CAM/CAMTests/TestPathAdaptive.py | 11 -- .../Resources/panels/PageOpAdaptiveEdit.ui | 154 ++++++++++++------ src/Mod/CAM/Path/Op/Adaptive.py | 6 + src/Mod/CAM/Path/Op/Gui/Adaptive.py | 16 +- 4 files changed, 122 insertions(+), 65 deletions(-) diff --git a/src/Mod/CAM/CAMTests/TestPathAdaptive.py b/src/Mod/CAM/CAMTests/TestPathAdaptive.py index ab3b498e34..bbef17870a 100644 --- a/src/Mod/CAM/CAMTests/TestPathAdaptive.py +++ b/src/Mod/CAM/CAMTests/TestPathAdaptive.py @@ -115,7 +115,6 @@ class TestPathAdaptive(PathTestBase): # setDepthsAndHeights(adaptive) adaptive.FinishingProfile = False adaptive.HelixAngle = 75.0 - adaptive.HelixDiameterLimit.Value = 1.0 adaptive.LiftDistance.Value = 1.0 adaptive.StepOver = 75 adaptive.UseOutline = False @@ -149,7 +148,6 @@ class TestPathAdaptive(PathTestBase): # setDepthsAndHeights(adaptive) adaptive.FinishingProfile = False adaptive.HelixAngle = 75.0 - adaptive.HelixDiameterLimit.Value = 1.0 adaptive.LiftDistance.Value = 1.0 adaptive.StepOver = 75 adaptive.UseOutline = False @@ -177,7 +175,6 @@ class TestPathAdaptive(PathTestBase): # setDepthsAndHeights(adaptive) adaptive.FinishingProfile = False adaptive.HelixAngle = 75.0 - adaptive.HelixDiameterLimit.Value = 1.0 adaptive.LiftDistance.Value = 1.0 adaptive.StepOver = 75 adaptive.UseOutline = True @@ -218,7 +215,6 @@ class TestPathAdaptive(PathTestBase): # setDepthsAndHeights(adaptive) adaptive.FinishingProfile = False adaptive.HelixAngle = 75.0 - adaptive.HelixDiameterLimit.Value = 1.0 adaptive.LiftDistance.Value = 1.0 adaptive.StepOver = 75 adaptive.UseOutline = False @@ -261,7 +257,6 @@ class TestPathAdaptive(PathTestBase): # setDepthsAndHeights(adaptive) adaptive.FinishingProfile = False adaptive.HelixAngle = 75.0 - adaptive.HelixDiameterLimit.Value = 1.0 adaptive.LiftDistance.Value = 1.0 adaptive.StepOver = 75 adaptive.UseOutline = False @@ -304,7 +299,6 @@ class TestPathAdaptive(PathTestBase): # setDepthsAndHeights(adaptive) adaptive.FinishingProfile = False adaptive.HelixAngle = 75.0 - adaptive.HelixDiameterLimit.Value = 1.0 adaptive.LiftDistance.Value = 1.0 adaptive.StepOver = 75 adaptive.UseOutline = False @@ -347,7 +341,6 @@ class TestPathAdaptive(PathTestBase): # setDepthsAndHeights(adaptive) adaptive.FinishingProfile = False adaptive.HelixAngle = 75.0 - adaptive.HelixDiameterLimit.Value = 1.0 adaptive.LiftDistance.Value = 1.0 adaptive.StepOver = 75 adaptive.UseOutline = False @@ -408,7 +401,6 @@ class TestPathAdaptive(PathTestBase): setDepthsAndHeights(adaptive, 15, 0) adaptive.FinishingProfile = False adaptive.HelixAngle = 75.0 - adaptive.HelixDiameterLimit.Value = 1.0 adaptive.LiftDistance.Value = 1.0 adaptive.StepOver = 75 adaptive.UseOutline = False @@ -478,7 +470,6 @@ class TestPathAdaptive(PathTestBase): setDepthsAndHeights(adaptive, 15, 10) adaptive.FinishingProfile = False adaptive.HelixAngle = 75.0 - adaptive.HelixDiameterLimit.Value = 1.0 adaptive.LiftDistance.Value = 1.0 adaptive.StepOver = 75 adaptive.UseOutline = False @@ -540,7 +531,6 @@ class TestPathAdaptive(PathTestBase): setDepthsAndHeights(adaptive, 15, 0) adaptive.FinishingProfile = False adaptive.HelixAngle = 75.0 - adaptive.HelixDiameterLimit.Value = 1.0 adaptive.LiftDistance.Value = 1.0 adaptive.StepOver = 75 adaptive.UseOutline = False @@ -622,7 +612,6 @@ class TestPathAdaptive(PathTestBase): setDepthsAndHeights(adaptive, 15, 5) adaptive.FinishingProfile = False adaptive.HelixAngle = 75.0 - adaptive.HelixDiameterLimit.Value = 1.0 adaptive.LiftDistance.Value = 1.0 adaptive.StepOver = 75 adaptive.UseOutline = False diff --git a/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui b/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui index d2b1d43549..aa829329c6 100644 --- a/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui +++ b/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui @@ -66,7 +66,7 @@ - + Accuracy vs performance @@ -173,21 +173,113 @@ Larger values (further to the right) will calculate faster; smaller values (furt - - - - Angle of the helix entry cone + + + + QFrame::Shape::StyledPanel - - - - - - - - - Helix ramp angle + + QFrame::Shadow::Raised + + + + + Helix max stepdown + + + + + + + The maximum allowable descent in a single revolution of the helix. + + + + + + + + + + Helix ramp angle + + + + + + + Angle of the helix ramp entry + + + + + + + + + + Helix cone angle + + + + + + + Angle of the helix entry cone + + + + + + + + + + Helix ideal diameter + + + + + + + Ideal helix entry diameter, as a percentage of the tool diameter. + + + 10 + + + 100 + + + 5 + + + + + + + Helix min diameter + + + + + + + Minimum acceptable entry diameter, as a percentage of the tool diameter. + + + 10 + + + 100 + + + 5 + + + + @@ -221,33 +313,6 @@ Larger values (further to the right) will calculate faster; smaller values (furt - - - - If greater than zero it limits the helix ramp diameter, otherwise 75 percent of tool diameter is used - - - - - - - - - - Helix cone angle - - - - - - - Angle of the helix ramp entry - - - - - - @@ -272,13 +337,6 @@ Larger values (further to the right) will calculate faster; smaller values (furt - - - - Helix max diameter - - - diff --git a/src/Mod/CAM/Path/Op/Adaptive.py b/src/Mod/CAM/Path/Op/Adaptive.py index 3e310d1e62..7fd9688a01 100644 --- a/src/Mod/CAM/Path/Op/Adaptive.py +++ b/src/Mod/CAM/Path/Op/Adaptive.py @@ -699,6 +699,12 @@ def ExecuteModelAware(op, obj): FreeCADGui.updateGui() try: + obj.HelixMinDiameterPercent = max(obj.HelixMinDiameterPercent, 10) + obj.HelixIdealDiameterPercent = max( + obj.HelixIdealDiameterPercent, obj.HelixMinDiameterPercent + ) + obj.StepOver = max(obj.StepOver, 1) + helixDiameter = obj.HelixIdealDiameterPercent / 100 * op.tool.Diameter.Value helixMinDiameter = obj.HelixMinDiameterPercent / 100 * op.tool.Diameter.Value topZ = op.stock.Shape.BoundBox.ZMax diff --git a/src/Mod/CAM/Path/Op/Gui/Adaptive.py b/src/Mod/CAM/Path/Op/Gui/Adaptive.py index 3e94602ca0..07c4752785 100644 --- a/src/Mod/CAM/Path/Op/Gui/Adaptive.py +++ b/src/Mod/CAM/Path/Op/Gui/Adaptive.py @@ -46,9 +46,6 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): def initPage(self, obj): self.form.LiftDistance.setProperty("unit", obj.LiftDistance.getUserPreferred()[2]) - self.form.HelixDiameterLimit.setProperty( - "unit", obj.HelixDiameterLimit.getUserPreferred()[2] - ) self.form.KeepToolDownRatio.setProperty("unit", obj.KeepToolDownRatio.getUserPreferred()[2]) self.form.StockToLeave.setProperty("unit", obj.StockToLeave.getUserPreferred()[2]) @@ -62,7 +59,8 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): 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.HelixIdealDiameterPercent.valueChanged) + signals.append(self.form.HelixMinDiameterPercent.valueChanged) signals.append(self.form.LiftDistance.valueChanged) signals.append(self.form.KeepToolDownRatio.valueChanged) signals.append(self.form.StockToLeave.valueChanged) @@ -92,7 +90,8 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): FreeCAD.Units.Quantity(obj.HelixConeAngle, FreeCAD.Units.Angle).UserString ) - self.form.HelixDiameterLimit.setProperty("rawValue", obj.HelixDiameterLimit.Value) + self.form.HelixIdealDiameterPercent.setValue(obj.HelixIdealDiameterPercent) + self.form.HelixMinDiameterPercent.setValue(obj.HelixMinDiameterPercent) self.form.LiftDistance.setProperty("rawValue", obj.LiftDistance.Value) @@ -124,10 +123,15 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): if obj.StepOver != self.form.stepOverPercent.value(): obj.StepOver = self.form.stepOverPercent.value() + if obj.HelixIdealDiameterPercent != self.form.HelixIdealDiameterPercent.value(): + obj.HelixIdealDiameterPercent = self.form.HelixIdealDiameterPercent.value() + + if obj.HelixMinDiameterPercent != self.form.HelixMinDiameterPercent.value(): + obj.HelixMinDiameterPercent = self.form.HelixMinDiameterPercent.value() + obj.Tolerance = 1.0 * self.form.Tolerance.value() / 100.0 PathGuiUtil.updateInputField(obj, "HelixAngle", self.form.HelixAngle) PathGuiUtil.updateInputField(obj, "HelixConeAngle", self.form.HelixConeAngle) - PathGuiUtil.updateInputField(obj, "HelixDiameterLimit", self.form.HelixDiameterLimit) PathGuiUtil.updateInputField(obj, "LiftDistance", self.form.LiftDistance) if hasattr(obj, "KeepToolDownRatio"): From 9e830949a8c2227a979d87eb17d5604bcbead9f2 Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Fri, 29 Aug 2025 15:13:58 -0400 Subject: [PATCH 03/13] [CAM] add helix max stepdown parameter --- src/Mod/CAM/Path/Op/Adaptive.py | 23 +++++++++++++++++++++++ src/Mod/CAM/Path/Op/Gui/Adaptive.py | 6 ++++++ 2 files changed, 29 insertions(+) diff --git a/src/Mod/CAM/Path/Op/Adaptive.py b/src/Mod/CAM/Path/Op/Adaptive.py index 7fd9688a01..d278f37efd 100644 --- a/src/Mod/CAM/Path/Op/Adaptive.py +++ b/src/Mod/CAM/Path/Op/Adaptive.py @@ -138,6 +138,9 @@ def GenerateGCode(op, obj, adaptiveResults): helixAngleRad = math.radians(obj.HelixAngle) depthPerOneCircle = length * math.tan(helixAngleRad) + if obj.HelixMaxStepdown.Value != 0 and obj.HelixMaxStepdown.Value < depthPerOneCircle: + depthPerOneCircle = obj.HelixMaxStepdown.Value + helixAngleRad = math.atan(depthPerOneCircle / length) stepUp = max(obj.LiftDistance.Value, 0) @@ -1810,6 +1813,15 @@ class PathAdaptive(PathOp.ObjectOp): "Helix ramp entry angle (degrees)", ), ) + obj.addProperty( + "App::PropertyLength", + "HelixMaxStepdown", + "Adaptive", + QT_TRANSLATE_NOOP( + "App::Property", + "The maximum allowable descent in a single revolution of the helix.", + ), + ) obj.addProperty( "App::PropertyAngle", "HelixConeAngle", @@ -2011,6 +2023,17 @@ class PathAdaptive(PathOp.ObjectOp): 75 if oldD == 0 else 100 * oldD / obj.ToolController.Tool.Diameter.Value ) + if not hasattr(obj, "HelixMaxStepdown"): + obj.addProperty( + "App::PropertyLength", + "HelixMaxStepdown", + "Adaptive", + QT_TRANSLATE_NOOP( + "App::Property", + "The maximum allowable descent in a single revolution of the helix.", + ), + ) + FeatureExtensions.initialize_properties(obj) diff --git a/src/Mod/CAM/Path/Op/Gui/Adaptive.py b/src/Mod/CAM/Path/Op/Gui/Adaptive.py index 07c4752785..8f484a3e1b 100644 --- a/src/Mod/CAM/Path/Op/Gui/Adaptive.py +++ b/src/Mod/CAM/Path/Op/Gui/Adaptive.py @@ -45,6 +45,8 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): return form def initPage(self, obj): + self.form.HelixMaxStepdown.setProperty("unit", obj.HelixMaxStepdown.getUserPreferred()[2]) + self.form.LiftDistance.setProperty("unit", obj.LiftDistance.getUserPreferred()[2]) self.form.KeepToolDownRatio.setProperty("unit", obj.KeepToolDownRatio.getUserPreferred()[2]) self.form.StockToLeave.setProperty("unit", obj.StockToLeave.getUserPreferred()[2]) @@ -58,6 +60,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): signals.append(self.form.stepOverPercent.valueChanged) signals.append(self.form.Tolerance.valueChanged) signals.append(self.form.HelixAngle.valueChanged) + signals.append(self.form.HelixMaxStepdown.valueChanged) signals.append(self.form.HelixConeAngle.valueChanged) signals.append(self.form.HelixIdealDiameterPercent.valueChanged) signals.append(self.form.HelixMinDiameterPercent.valueChanged) @@ -86,6 +89,8 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): FreeCAD.Units.Quantity(obj.HelixAngle, FreeCAD.Units.Angle).UserString ) + self.form.HelixMaxStepdown.setProperty("rawValue", obj.HelixMaxStepdown.Value) + self.form.HelixConeAngle.setText( FreeCAD.Units.Quantity(obj.HelixConeAngle, FreeCAD.Units.Angle).UserString ) @@ -131,6 +136,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): obj.Tolerance = 1.0 * self.form.Tolerance.value() / 100.0 PathGuiUtil.updateInputField(obj, "HelixAngle", self.form.HelixAngle) + PathGuiUtil.updateInputField(obj, "HelixMaxStepdown", self.form.HelixMaxStepdown) PathGuiUtil.updateInputField(obj, "HelixConeAngle", self.form.HelixConeAngle) PathGuiUtil.updateInputField(obj, "LiftDistance", self.form.LiftDistance) From 09e984d1ce9935f5bbb95f289d981088abe3714b Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Wed, 8 Oct 2025 20:04:10 -0400 Subject: [PATCH 04/13] fix github warning --- src/Mod/CAM/Path/Op/Adaptive.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Mod/CAM/Path/Op/Adaptive.py b/src/Mod/CAM/Path/Op/Adaptive.py index d278f37efd..9b49e7135a 100644 --- a/src/Mod/CAM/Path/Op/Adaptive.py +++ b/src/Mod/CAM/Path/Op/Adaptive.py @@ -140,7 +140,6 @@ def GenerateGCode(op, obj, adaptiveResults): depthPerOneCircle = length * math.tan(helixAngleRad) if obj.HelixMaxStepdown.Value != 0 and obj.HelixMaxStepdown.Value < depthPerOneCircle: depthPerOneCircle = obj.HelixMaxStepdown.Value - helixAngleRad = math.atan(depthPerOneCircle / length) stepUp = max(obj.LiftDistance.Value, 0) From b19eaff00e3e83b7afe9e8e9bdefe649f47e8b2e Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Wed, 5 Nov 2025 17:08:07 -0500 Subject: [PATCH 05/13] fix rebase errors --- src/Mod/CAM/Path/Op/Adaptive.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Mod/CAM/Path/Op/Adaptive.py b/src/Mod/CAM/Path/Op/Adaptive.py index 9b49e7135a..de4fdd5e31 100644 --- a/src/Mod/CAM/Path/Op/Adaptive.py +++ b/src/Mod/CAM/Path/Op/Adaptive.py @@ -540,7 +540,13 @@ def Execute(op, obj): FreeCADGui.updateGui() try: - helixDiameter = obj.HelixDiameterLimit.Value + obj.HelixMinDiameterPercent = max(obj.HelixMinDiameterPercent, 10) + obj.HelixIdealDiameterPercent = max( + obj.HelixIdealDiameterPercent, obj.HelixMinDiameterPercent + ) + + helixDiameter = obj.HelixIdealDiameterPercent / 100 * op.tool.Diameter.Value + helixMinDiameter = obj.HelixMinDiameterPercent / 100 * op.tool.Diameter.Value topZ = op.stock.Shape.BoundBox.ZMax obj.Stopped = False obj.StopProcessing = False @@ -635,7 +641,8 @@ def Execute(op, obj): a2d = area.Adaptive2d() a2d.stepOverFactor = 0.01 * obj.StepOver a2d.toolDiameter = float(op.tool.Diameter) - a2d.helixRampDiameter = helixDiameter + a2d.helixRampTargetDiameter = helixDiameter + a2d.helixRampMinDiameter = helixMinDiameter a2d.keepToolDownDistRatio = keepToolDownRatio a2d.stockToLeave = float(obj.StockToLeave) a2d.tolerance = float(obj.Tolerance) @@ -659,7 +666,7 @@ def Execute(op, obj): ) # GENERATE - GenerateGCode(op, obj, adaptiveResults, helixDiameter) + GenerateGCode(op, obj, adaptiveResults) if not obj.StopProcessing: Path.Log.info("*** Done. Elapsed time: %f sec\n\n" % (time.time() - start)) From a6c7d7c0e7ca1e9ab95c849f91441c0a315296f6 Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Mon, 17 Nov 2025 13:57:53 -0500 Subject: [PATCH 06/13] fix units bug --- src/Mod/CAM/Path/Op/Adaptive.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mod/CAM/Path/Op/Adaptive.py b/src/Mod/CAM/Path/Op/Adaptive.py index de4fdd5e31..36e561d32d 100644 --- a/src/Mod/CAM/Path/Op/Adaptive.py +++ b/src/Mod/CAM/Path/Op/Adaptive.py @@ -2003,7 +2003,7 @@ class PathAdaptive(PathOp.ObjectOp): obj.setEditorMode("removalshape", 2) # hide if hasattr(obj, "HelixDiameterLimit"): - oldD = obj.HelixDiameterLimit + oldD = obj.HelixDiameterLimit.Value obj.removeProperty("HelixDiameterLimit") obj.addProperty( "App::PropertyPercent", @@ -2025,7 +2025,7 @@ class PathAdaptive(PathOp.ObjectOp): ) obj.HelixMinDiameterPercent = 10 if hasattr(obj, "ToolController"): - obj.HelixIdealDiameterPercent = ( + obj.HelixIdealDiameterPercent = int( 75 if oldD == 0 else 100 * oldD / obj.ToolController.Tool.Diameter.Value ) From f869295d039f33325a3ede07608bb9bdb2b72caf Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Mon, 17 Nov 2025 13:58:16 -0500 Subject: [PATCH 07/13] update UI to label helix diameter as a percentage, reorganize --- .../Resources/panels/PageOpAdaptiveEdit.ui | 218 +++++++++--------- 1 file changed, 109 insertions(+), 109 deletions(-) diff --git a/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui b/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui index aa829329c6..208c3916df 100644 --- a/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui +++ b/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui @@ -173,115 +173,6 @@ Larger values (further to the right) will calculate faster; smaller values (furt - - - - QFrame::Shape::StyledPanel - - - QFrame::Shadow::Raised - - - - - - Helix max stepdown - - - - - - - The maximum allowable descent in a single revolution of the helix. - - - - - - - - - - Helix ramp angle - - - - - - - Angle of the helix ramp entry - - - - - - - - - - Helix cone angle - - - - - - - Angle of the helix entry cone - - - - - - - - - - Helix ideal diameter - - - - - - - Ideal helix entry diameter, as a percentage of the tool diameter. - - - 10 - - - 100 - - - 5 - - - - - - - Helix min diameter - - - - - - - Minimum acceptable entry diameter, as a percentage of the tool diameter. - - - 10 - - - 100 - - - 5 - - - - - - @@ -340,6 +231,115 @@ Larger values (further to the right) will calculate faster; smaller values (furt + + + + QFrame::Shape::StyledPanel + + + QFrame::Shadow::Raised + + + + + + Helix max stepdown + + + + + + + The maximum allowable descent in a single revolution of the helix. + + + + + + + + + + Helix ramp angle + + + + + + + Angle of the helix ramp entry + + + + + + + + + + Helix cone angle + + + + + + + Angle of the helix entry cone + + + + + + + + + + Helix ideal diameter percent + + + + + + + Ideal helix entry diameter, as a percentage of the tool diameter. + + + 10 + + + 100 + + + 5 + + + + + + + Helix min diameter percent + + + + + + + Minimum acceptable entry diameter, as a percentage of the tool diameter. + + + 10 + + + 100 + + + 5 + + + + + + From ad3119306bb96e3a40dfa236e5cd6f4efed20728 Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Mon, 17 Nov 2025 14:04:10 -0500 Subject: [PATCH 08/13] fix rebase error --- src/Mod/CAM/Path/Op/Adaptive.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mod/CAM/Path/Op/Adaptive.py b/src/Mod/CAM/Path/Op/Adaptive.py index 36e561d32d..82d899e2d9 100644 --- a/src/Mod/CAM/Path/Op/Adaptive.py +++ b/src/Mod/CAM/Path/Op/Adaptive.py @@ -600,6 +600,7 @@ def Execute(op, obj): "stockGeometry": stockPath2d, "stepover": float(obj.StepOver), "effectiveHelixDiameter": float(helixDiameter), + "helixMinDiameter": float(helixMinDiameter), "operationType": obj.OperationType, "side": obj.Side, "forceInsideOut": obj.ForceInsideOut, From 13adb1fb5c91777173c0573ea5afae52a40d0ef9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 19:16:13 +0000 Subject: [PATCH 09/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/Gui/ViewProviderGeometryObject.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Gui/ViewProviderGeometryObject.cpp b/src/Gui/ViewProviderGeometryObject.cpp index 1ef6938154..9a371ca6f2 100644 --- a/src/Gui/ViewProviderGeometryObject.cpp +++ b/src/Gui/ViewProviderGeometryObject.cpp @@ -196,8 +196,7 @@ void ViewProviderGeometryObject::updateData(const App::Property* prop) App::Material defaultMaterial; auto material = geometry->getMaterialAppearance(); if ((ShapeAppearance.getSize() == 1) - && (ShapeAppearance[0] == defaultMaterial - || ShapeAppearance[0] == materialAppearance) + && (ShapeAppearance[0] == defaultMaterial || ShapeAppearance[0] == materialAppearance) && (material != defaultMaterial)) { ShapeAppearance.setValue(material); } From 07ed72fe160627ec0b15eb2a4b323ed2925adfeb Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Fri, 12 Dec 2025 15:07:15 -0500 Subject: [PATCH 10/13] rename HelixIdealDiameter to HelixMaxDiameter and update documentation --- .../Resources/panels/PageOpAdaptiveEdit.ui | 6 ++--- src/Mod/CAM/Path/Op/Adaptive.py | 26 ++++++++----------- src/Mod/CAM/Path/Op/Gui/Adaptive.py | 8 +++--- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui b/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui index 208c3916df..e20e4fafa4 100644 --- a/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui +++ b/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui @@ -294,14 +294,14 @@ Larger values (further to the right) will calculate faster; smaller values (furt - Helix ideal diameter percent + Helix max diameter percent - + - Ideal helix entry diameter, as a percentage of the tool diameter. + Maximum (and nominal) helix entry diameter, as a percentage of the tool diameter. 10 diff --git a/src/Mod/CAM/Path/Op/Adaptive.py b/src/Mod/CAM/Path/Op/Adaptive.py index 82d899e2d9..b6a69f7fd8 100644 --- a/src/Mod/CAM/Path/Op/Adaptive.py +++ b/src/Mod/CAM/Path/Op/Adaptive.py @@ -541,11 +541,9 @@ def Execute(op, obj): try: obj.HelixMinDiameterPercent = max(obj.HelixMinDiameterPercent, 10) - obj.HelixIdealDiameterPercent = max( - obj.HelixIdealDiameterPercent, obj.HelixMinDiameterPercent - ) + obj.HelixMaxDiameterPercent = max(obj.HelixMaxDiameterPercent, obj.HelixMinDiameterPercent) - helixDiameter = obj.HelixIdealDiameterPercent / 100 * op.tool.Diameter.Value + helixDiameter = obj.HelixMaxDiameterPercent / 100 * op.tool.Diameter.Value helixMinDiameter = obj.HelixMinDiameterPercent / 100 * op.tool.Diameter.Value topZ = op.stock.Shape.BoundBox.ZMax obj.Stopped = False @@ -710,12 +708,10 @@ def ExecuteModelAware(op, obj): try: obj.HelixMinDiameterPercent = max(obj.HelixMinDiameterPercent, 10) - obj.HelixIdealDiameterPercent = max( - obj.HelixIdealDiameterPercent, obj.HelixMinDiameterPercent - ) + obj.HelixMaxDiameterPercent = max(obj.HelixMaxDiameterPercent, obj.HelixMinDiameterPercent) obj.StepOver = max(obj.StepOver, 1) - helixDiameter = obj.HelixIdealDiameterPercent / 100 * op.tool.Diameter.Value + helixDiameter = obj.HelixMaxDiameterPercent / 100 * op.tool.Diameter.Value helixMinDiameter = obj.HelixMinDiameterPercent / 100 * op.tool.Diameter.Value topZ = op.stock.Shape.BoundBox.ZMax obj.Stopped = False @@ -1840,11 +1836,11 @@ class PathAdaptive(PathOp.ObjectOp): ) obj.addProperty( "App::PropertyPercent", - "HelixIdealDiameterPercent", + "HelixMaxDiameterPercent", "Adaptive", QT_TRANSLATE_NOOP( "App::Property", - "Ideal helix entry diameter, as a percentage of the tool diameter", + "Maximum (and nominal) helix entry diameter, as a percentage of the tool diameter", ), ) obj.addProperty( @@ -1912,7 +1908,7 @@ class PathAdaptive(PathOp.ObjectOp): obj.StopProcessing = False obj.HelixAngle = 5 obj.HelixConeAngle = 0 - obj.HelixIdealDiameterPercent = 100 + obj.HelixMaxDiameterPercent = 100 obj.HelixMinDiameterPercent = 10 obj.AdaptiveInputState = "" obj.AdaptiveOutputState = "" @@ -2008,11 +2004,11 @@ class PathAdaptive(PathOp.ObjectOp): obj.removeProperty("HelixDiameterLimit") obj.addProperty( "App::PropertyPercent", - "HelixIdealDiameterPercent", + "HelixMaxDiameterPercent", "Adaptive", QT_TRANSLATE_NOOP( "App::Property", - "Ideal helix entry diameter, as a percentage of the tool diameter", + "Maximum (and nominal) helix entry diameter, as a percentage of the tool diameter", ), ) obj.addProperty( @@ -2026,7 +2022,7 @@ class PathAdaptive(PathOp.ObjectOp): ) obj.HelixMinDiameterPercent = 10 if hasattr(obj, "ToolController"): - obj.HelixIdealDiameterPercent = int( + obj.HelixMaxDiameterPercent = int( 75 if oldD == 0 else 100 * oldD / obj.ToolController.Tool.Diameter.Value ) @@ -2063,7 +2059,7 @@ def SetupProperties(): "AdaptiveOutputState", "HelixAngle", "HelixConeAngle", - "HelixIdealDiameterPercent", + "HelixMaxDiameterPercent", "HelixMinDiameterPercent", "UseOutline", "OrderCutsByRegion", diff --git a/src/Mod/CAM/Path/Op/Gui/Adaptive.py b/src/Mod/CAM/Path/Op/Gui/Adaptive.py index 8f484a3e1b..70c98b3ef4 100644 --- a/src/Mod/CAM/Path/Op/Gui/Adaptive.py +++ b/src/Mod/CAM/Path/Op/Gui/Adaptive.py @@ -62,7 +62,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): signals.append(self.form.HelixAngle.valueChanged) signals.append(self.form.HelixMaxStepdown.valueChanged) signals.append(self.form.HelixConeAngle.valueChanged) - signals.append(self.form.HelixIdealDiameterPercent.valueChanged) + signals.append(self.form.HelixMaxDiameterPercent.valueChanged) signals.append(self.form.HelixMinDiameterPercent.valueChanged) signals.append(self.form.LiftDistance.valueChanged) signals.append(self.form.KeepToolDownRatio.valueChanged) @@ -95,7 +95,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): FreeCAD.Units.Quantity(obj.HelixConeAngle, FreeCAD.Units.Angle).UserString ) - self.form.HelixIdealDiameterPercent.setValue(obj.HelixIdealDiameterPercent) + self.form.HelixMaxDiameterPercent.setValue(obj.HelixMaxDiameterPercent) self.form.HelixMinDiameterPercent.setValue(obj.HelixMinDiameterPercent) self.form.LiftDistance.setProperty("rawValue", obj.LiftDistance.Value) @@ -128,8 +128,8 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): if obj.StepOver != self.form.stepOverPercent.value(): obj.StepOver = self.form.stepOverPercent.value() - if obj.HelixIdealDiameterPercent != self.form.HelixIdealDiameterPercent.value(): - obj.HelixIdealDiameterPercent = self.form.HelixIdealDiameterPercent.value() + if obj.HelixMaxDiameterPercent != self.form.HelixMaxDiameterPercent.value(): + obj.HelixMaxDiameterPercent = self.form.HelixMaxDiameterPercent.value() if obj.HelixMinDiameterPercent != self.form.HelixMinDiameterPercent.value(): obj.HelixMinDiameterPercent = self.form.HelixMinDiameterPercent.value() From 5d877d1cc3fdc0ce77881d1cbef35d6350183606 Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Fri, 12 Dec 2025 15:18:27 -0500 Subject: [PATCH 11/13] extract 'helix' header from property labels and make it the frame header --- .../Gui/Resources/panels/PageOpAdaptiveEdit.ui | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui b/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui index e20e4fafa4..0fd5deab36 100644 --- a/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui +++ b/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui @@ -240,10 +240,17 @@ Larger values (further to the right) will calculate faster; smaller values (furt QFrame::Shadow::Raised + + + + <b>Helix Parameters</b> + + + - Helix max stepdown + Max stepdown @@ -260,7 +267,7 @@ Larger values (further to the right) will calculate faster; smaller values (furt - Helix ramp angle + Ramp angle @@ -277,7 +284,7 @@ Larger values (further to the right) will calculate faster; smaller values (furt - Helix cone angle + Cone angle @@ -294,7 +301,7 @@ Larger values (further to the right) will calculate faster; smaller values (furt - Helix max diameter percent + Max diameter percent @@ -317,7 +324,7 @@ Larger values (further to the right) will calculate faster; smaller values (furt - Helix min diameter percent + Min diameter percent From f6daab5ed84c43645974948c827459c7468fde87 Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Fri, 12 Dec 2025 15:32:03 -0500 Subject: [PATCH 12/13] use % symbol in min/max helix diameter input fields, instead of the word 'percent' in the label --- .../Gui/Resources/panels/PageOpAdaptiveEdit.ui | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui b/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui index 0fd5deab36..0bda0423ff 100644 --- a/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui +++ b/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui @@ -301,7 +301,7 @@ Larger values (further to the right) will calculate faster; smaller values (furt - Max diameter percent + Max diameter @@ -319,12 +319,18 @@ Larger values (further to the right) will calculate faster; smaller values (furt 5 + + % + + + 1 + - Min diameter percent + Min diameter @@ -342,6 +348,12 @@ Larger values (further to the right) will calculate faster; smaller values (furt 5 + + % + + + 1 + From 5c3e32a42c10bfe6bc8003bdc69a04b5c510963d Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Fri, 12 Dec 2025 15:38:14 -0500 Subject: [PATCH 13/13] also use the % symbol for step over percent --- src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui b/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui index 0bda0423ff..fa6b76ab74 100644 --- a/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui +++ b/src/Mod/CAM/Gui/Resources/panels/PageOpAdaptiveEdit.ui @@ -164,12 +164,15 @@ Larger values (further to the right) will calculate faster; smaller values (furt 100 + + % + - Step over percent + Step over