Merge pull request #23980 from davidgilkaufman/adaptive_auto_helix

[CAM] Adaptive automatically pick diameter of helix entrance
This commit is contained in:
sliptonic
2026-01-09 11:46:27 -06:00
committed by GitHub
7 changed files with 288 additions and 101 deletions

View File

@@ -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

View File

@@ -66,7 +66,7 @@
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="threadFitLabel">
<widget class="QLabel" name="accuracyPerformanceLabel">
<property name="text">
<string>Accuracy vs performance</string>
</property>
@@ -164,29 +164,15 @@ Larger values (further to the right) will calculate faster; smaller values (furt
<property name="value">
<number>100</number>
</property>
<property name="suffix">
<string>%</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Step over percent</string>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="Gui::InputField" name="HelixConeAngle">
<property name="toolTip">
<string>Angle of the helix entry cone</string>
</property>
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Helix ramp angle</string>
<string>Step over</string>
</property>
</widget>
</item>
@@ -221,33 +207,6 @@ Larger values (further to the right) will calculate faster; smaller values (furt
</property>
</widget>
</item>
<item row="14" column="1">
<widget class="Gui::QuantitySpinBox" name="HelixDiameterLimit" native="true">
<property name="toolTip">
<string>If greater than zero it limits the helix ramp diameter, otherwise 75 percent of tool diameter is used</string>
</property>
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Helix cone angle</string>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="Gui::InputField" name="HelixAngle">
<property name="toolTip">
<string>Angle of the helix ramp entry</string>
</property>
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
<item row="16" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
@@ -272,10 +231,131 @@ Larger values (further to the right) will calculate faster; smaller values (furt
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="helixFrame">
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QGridLayout" name="helixLayout">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="helix_header">
<property name="text">
<string>&lt;b&gt;Helix Parameters&lt;/b&gt;</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="label_max_stepdown">
<property name="text">
<string>Max stepdown</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="Gui::InputField" name="HelixMaxStepdown">
<property name="toolTip">
<string>The maximum allowable descent in a single revolution of the helix.</string>
</property>
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Ramp angle</string>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="Gui::InputField" name="HelixAngle">
<property name="toolTip">
<string>Angle of the helix ramp entry</string>
</property>
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Cone angle</string>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="Gui::InputField" name="HelixConeAngle">
<property name="toolTip">
<string>Angle of the helix entry cone</string>
</property>
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
<item row="14" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Helix max diameter</string>
<string>Max diameter</string>
</property>
</widget>
</item>
<item row="14" column="1">
<widget class="QSpinBox" name="HelixMaxDiameterPercent">
<property name="toolTip">
<string>Maximum (and nominal) helix entry diameter, as a percentage of the tool diameter.</string>
</property>
<property name="minimum">
<number>10</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="suffix">
<string>%</string>
</property>
<property name="decimals">
<number>1</number>
</property>
</widget>
</item>
<item row="15" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Min diameter</string>
</property>
</widget>
</item>
<item row="15" column="1">
<widget class="QSpinBox" name="HelixMinDiameterPercent">
<property name="toolTip">
<string>Minimum acceptable entry diameter, as a percentage of the tool diameter.</string>
</property>
<property name="minimum">
<number>10</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="suffix">
<string>%</string>
</property>
<property name="decimals">
<number>1</number>
</property>
</widget>
</item>

View File

@@ -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
@@ -138,6 +138,8 @@ def GenerateGCode(op, obj, adaptiveResults, helixDiameter):
helixAngleRad = math.radians(obj.HelixAngle)
depthPerOneCircle = length * math.tan(helixAngleRad)
if obj.HelixMaxStepdown.Value != 0 and obj.HelixMaxStepdown.Value < depthPerOneCircle:
depthPerOneCircle = obj.HelixMaxStepdown.Value
stepUp = max(obj.LiftDistance.Value, 0)
@@ -538,7 +540,11 @@ def Execute(op, obj):
FreeCADGui.updateGui()
try:
helixDiameter = obj.HelixDiameterLimit.Value
obj.HelixMinDiameterPercent = max(obj.HelixMinDiameterPercent, 10)
obj.HelixMaxDiameterPercent = max(obj.HelixMaxDiameterPercent, obj.HelixMinDiameterPercent)
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
obj.StopProcessing = False
@@ -592,6 +598,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,
@@ -633,7 +640,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)
@@ -657,7 +665,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))
@@ -699,7 +707,12 @@ def ExecuteModelAware(op, obj):
FreeCADGui.updateGui()
try:
helixDiameter = obj.HelixDiameterLimit.Value
obj.HelixMinDiameterPercent = max(obj.HelixMinDiameterPercent, 10)
obj.HelixMaxDiameterPercent = max(obj.HelixMaxDiameterPercent, obj.HelixMinDiameterPercent)
obj.StepOver = max(obj.StepOver, 1)
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
obj.StopProcessing = False
@@ -774,6 +787,7 @@ def ExecuteModelAware(op, obj):
"stockGeometry": stockPaths,
"stepover": obj.StepOver,
"effectiveHelixDiameter": helixDiameter,
"helixMinDiameter": helixMinDiameter,
"operationType": "Clearing",
"side": "Outside",
"forceInsideOut": obj.ForceInsideOut,
@@ -793,6 +807,7 @@ def ExecuteModelAware(op, obj):
"stockGeometry": stockPaths,
"stepover": obj.StepOver,
"effectiveHelixDiameter": helixDiameter,
"helixMinDiameter": helixMinDiameter,
"operationType": "Clearing",
"side": "Inside",
"forceInsideOut": obj.ForceInsideOut,
@@ -857,7 +872,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 +962,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))
@@ -1800,6 +1816,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",
@@ -1810,12 +1835,21 @@ class PathAdaptive(PathOp.ObjectOp):
),
)
obj.addProperty(
"App::PropertyLength",
"HelixDiameterLimit",
"App::PropertyPercent",
"HelixMaxDiameterPercent",
"Adaptive",
QT_TRANSLATE_NOOP(
"App::Property",
"Limit helix entry diameter, if limit larger than tool diameter or 0, tool diameter is used",
"Maximum (and nominal) 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 +1908,8 @@ class PathAdaptive(PathOp.ObjectOp):
obj.StopProcessing = False
obj.HelixAngle = 5
obj.HelixConeAngle = 0
obj.HelixDiameterLimit = 0.0
obj.HelixMaxDiameterPercent = 100
obj.HelixMinDiameterPercent = 10
obj.AdaptiveInputState = ""
obj.AdaptiveOutputState = ""
obj.StockToLeave = 0
@@ -1964,6 +1999,44 @@ class PathAdaptive(PathOp.ObjectOp):
obj.addProperty("Part::PropertyPartShape", "removalshape", "Path", "")
obj.setEditorMode("removalshape", 2) # hide
if hasattr(obj, "HelixDiameterLimit"):
oldD = obj.HelixDiameterLimit.Value
obj.removeProperty("HelixDiameterLimit")
obj.addProperty(
"App::PropertyPercent",
"HelixMaxDiameterPercent",
"Adaptive",
QT_TRANSLATE_NOOP(
"App::Property",
"Maximum (and nominal) 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.HelixMaxDiameterPercent = int(
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)
@@ -1986,7 +2059,8 @@ def SetupProperties():
"AdaptiveOutputState",
"HelixAngle",
"HelixConeAngle",
"HelixDiameterLimit",
"HelixMaxDiameterPercent",
"HelixMinDiameterPercent",
"UseOutline",
"OrderCutsByRegion",
]

View File

@@ -45,10 +45,9 @@ 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.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])
@@ -61,8 +60,10 @@ 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.HelixDiameterLimit.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)
signals.append(self.form.StockToLeave.valueChanged)
@@ -88,11 +89,14 @@ 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
)
self.form.HelixDiameterLimit.setProperty("rawValue", obj.HelixDiameterLimit.Value)
self.form.HelixMaxDiameterPercent.setValue(obj.HelixMaxDiameterPercent)
self.form.HelixMinDiameterPercent.setValue(obj.HelixMinDiameterPercent)
self.form.LiftDistance.setProperty("rawValue", obj.LiftDistance.Value)
@@ -124,10 +128,16 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
if obj.StepOver != self.form.stepOverPercent.value():
obj.StepOver = self.form.stepOverPercent.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()
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, "HelixDiameterLimit", self.form.HelixDiameterLimit)
PathGuiUtil.updateInputField(obj, "LiftDistance", self.form.LiftDistance)
if hasattr(obj, "KeepToolDownRatio"):

View File

@@ -1773,17 +1773,15 @@ std::list<AdaptiveOutput> 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<AdaptiveOutput> 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<AdaptiveOutput> 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;
}

View File

@@ -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,

View File

@@ -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)