[CAM] initial implementation of automatic helix size selection for adaptive
This commit is contained in:
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user