Path: Adaptive - keep tool down ratio option + bugfixes

This commit is contained in:
kreso-t
2018-09-07 18:26:06 +02:00
committed by wmayer
parent ae861300ed
commit 8edd584b68
6 changed files with 144 additions and 67 deletions

View File

@@ -236,7 +236,10 @@ def Execute(op,obj):
opType = area.AdaptiveOperationType.ProfilingInside
keepToolDownRatio=3.0
if hasattr(obj, 'KeepToolDownRatio'): keepToolDownRatio = float(obj.KeepToolDownRatio)
# put here all properties that influence calculation of adaptive base paths,
inputStateObject = {
"tool": float(op.tool.Diameter),
"tolerance": float(obj.Tolerance),
@@ -247,6 +250,7 @@ def Execute(op,obj):
"operationType": obj.OperationType,
"side": obj.Side,
"forceInsideOut" : obj.ForceInsideOut,
"keepToolDownRatio": keepToolDownRatio,
"stockToLeave": float(obj.StockToLeave)
}
@@ -277,6 +281,7 @@ def Execute(op,obj):
a2d.stepOverFactor = 0.01*obj.StepOver
a2d.toolDiameter = float(op.tool.Diameter)
a2d.helixRampDiameter = helixDiameter
a2d.keepToolDownDistRatio = keepToolDownRatio
a2d.stockToLeave =float(obj.StockToLeave)
a2d.tolerance = float(obj.Tolerance)
a2d.forceInsideOut = obj.ForceInsideOut
@@ -329,6 +334,7 @@ class PathAdaptive(PathOp.ObjectOp):
obj.addProperty("App::PropertyFloat", "Tolerance", "Adaptive", "Influences accuracy and performance")
obj.addProperty("App::PropertyPercent", "StepOver", "Adaptive", "Percent of cutter diameter to step over on each pass")
obj.addProperty("App::PropertyDistance", "LiftDistance", "Adaptive", "Lift distance for rapid moves")
obj.addProperty("App::PropertyDistance", "KeepToolDownRatio", "Adaptive", "Max length of keep tool down path compared to direct distance between points")
obj.addProperty("App::PropertyDistance", "StockToLeave", "Adaptive", "How much stock to leave (i.e. for finishing operation)")
# obj.addProperty("App::PropertyBool", "ProcessHoles", "Adaptive","Process holes as well as the face outline")
@@ -366,6 +372,7 @@ class PathAdaptive(PathOp.ObjectOp):
obj.AdaptiveInputState =""
obj.AdaptiveOutputState = ""
obj.StockToLeave= 0
obj.KeepToolDownRatio=3.0
def opExecute(self, obj):
'''opExecute(obj) ... called whenever the receiver needs to be recalculated.

View File

@@ -83,6 +83,15 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
form.LiftDistance.setToolTip("How much to lift the tool up during the rapid repositioning moves (used when no obstacles)")
formLayout.addRow(QtGui.QLabel("Lift Distance"),form.LiftDistance)
#KeepToolDownRatio
form.KeepToolDownRatio = QtGui.QDoubleSpinBox()
form.KeepToolDownRatio.setMinimum(1.0)
form.KeepToolDownRatio.setMaximum(10)
form.KeepToolDownRatio.setSingleStep(1)
form.KeepToolDownRatio.setValue(3.0)
form.KeepToolDownRatio.setToolTip("Max length of keep tool down path compared to direct distance between points")
formLayout.addRow(QtGui.QLabel("Keep Tool Down Ratio"),form.KeepToolDownRatio)
#stock to leave
form.StockToLeave = QtGui.QDoubleSpinBox()
form.StockToLeave.setMinimum(0.0)
@@ -124,6 +133,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
signals.append(self.form.HelixAngle.valueChanged)
signals.append(self.form.HelixDiameterLimit.valueChanged)
signals.append(self.form.LiftDistance.valueChanged)
signals.append(self.form.KeepToolDownRatio.valueChanged)
signals.append(self.form.StockToLeave.valueChanged)
# signals.append(self.form.ProcessHoles.stateChanged)
@@ -139,6 +149,9 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
self.form.HelixAngle.setValue(obj.HelixAngle)
self.form.HelixDiameterLimit.setValue(obj.HelixDiameterLimit)
self.form.LiftDistance.setValue(obj.LiftDistance)
if hasattr(obj, 'KeepToolDownRatio'):
self.form.KeepToolDownRatio.setValue(obj.KeepToolDownRatio)
if hasattr(obj, 'StockToLeave'):
self.form.StockToLeave.setValue(obj.StockToLeave)
@@ -163,6 +176,10 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
obj.HelixAngle = self.form.HelixAngle.value()
obj.HelixDiameterLimit = self.form.HelixDiameterLimit.value()
obj.LiftDistance = self.form.LiftDistance.value()
if hasattr(obj, 'KeepToolDownRatio'):
obj.KeepToolDownRatio = self.form.KeepToolDownRatio.value()
if hasattr(obj, 'StockToLeave'):
obj.StockToLeave = self.form.StockToLeave.value()

View File

@@ -412,6 +412,23 @@ namespace AdaptivePath {
return res.size()>1;
}
// finds interim points not neccesarily on the same keeptooldown linking path
bool FindClosestClearPoints(const Paths & paths,IntPoint p1,IntPoint p2, double distanceLmit, IntPoint &interim1,IntPoint &interim2) {
size_t clpPathIndex;
size_t clpSegmentIndex1;
double clpParameter1;
size_t clpSegmentIndex2;
double clpParameter2;
double limitSqrd = distanceLmit*distanceLmit;
double distSqrd=DistancePointToPathsSqrd(paths,p1,interim1,clpPathIndex,clpSegmentIndex1,clpParameter1);
if(distSqrd>limitSqrd) return false; // too far
// find second point
distSqrd=DistancePointToPathsSqrd(paths,p2,interim2,clpPathIndex,clpSegmentIndex2,clpParameter2);
if(distSqrd>limitSqrd) return false; // still too far
return true;
}
bool PopPathWithClosestPoint(Paths & paths /*closest path is removed from collection */
,IntPoint p1, Path & result) {
@@ -1038,10 +1055,10 @@ namespace AdaptivePath {
if(stockToLeave>NTOL) {
clipof.Clear();
clipof.AddPaths(inputPaths,JoinType::jtRound,EndType::etClosedPolygon);
if(opType==OperationType::otClearingOutside || opType==OperationType::otProfilingOutside)
clipof.Execute(inputPaths,stockToLeave*scaleFactor);
else
clipof.Execute(inputPaths,-stockToLeave*scaleFactor);
// if(opType==OperationType::otClearingOutside || opType==OperationType::otProfilingOutside)
// clipof.Execute(inputPaths,stockToLeave*scaleFactor);
// else
clipof.Execute(inputPaths,-stockToLeave*scaleFactor);
} else {
// fix for clipper glitches
clipof.Clear();
@@ -1145,7 +1162,7 @@ namespace AdaptivePath {
// prepare stock boundary overshooted paths
clipof.Clear();
clipof.AddPaths(stockInputPaths,JoinType::jtSquare,EndType::etClosedPolygon);
double overshootDistance =4*toolRadiusScaled ;
double overshootDistance =4*toolRadiusScaled + stockToLeave*scaleFactor ;
if(forceInsideOut) overshootDistance=0;
Paths stockOvershoot;
clipof.Execute(stockOvershoot,overshootDistance);
@@ -1156,30 +1173,7 @@ namespace AdaptivePath {
// add stock paths, with overshooting
for(auto p : stockOvershoot) inputPaths.push_back(p);
} else if(opType==OperationType::otClearingInside) {
// check if there are open paths, and try to close it through overshooted stock boundary
// Paths openInputPaths; // open paths will be removed by simplify
// Paths closedInputPaths;
// for(const auto & pth:inputPaths) {
// if(DistanceSqrd(pth.front(),pth.back())>SAME_POINT_TOL_SQRD_SCALED) {
// openInputPaths.push_back(pth);
// } else {
// closedInputPaths.push_back(pth);
// }
// }
// if(openInputPaths.size()==1) {
// cout << "SINGLE OPEN PATH MODE" << endl;
// Path & op = openInputPaths.front();
// Path connect;
// FindPathBetweenClosestPoints(stockOvershoot,op.back(),op.front(),scaleFactor*100000,connect);
// Paths cl;
// cout << op;
// cout << connect;
// cl.push_back(op);
// cl.push_back(connect);
// ConnectPaths(cl,inputPaths);
// for(auto &p: closedInputPaths) inputPaths.push_back(p);
// }
// potential TODO: check if there are open paths, and try to close it through overshooted stock boundary
}
SimplifyPolygons(inputPaths);
@@ -1398,12 +1392,12 @@ namespace AdaptivePath {
/**
* returns true if path is clear from obstacles
*/
bool Adaptive2d::IsClearPath(const Path &tp,const Paths & cleared) {
bool Adaptive2d::IsClearPath(const Path &tp,const Paths & cleared, double safetyDistanceScaled) {
Clipper clip;
ClipperOffset clipof;
clipof.AddPath(tp,JoinType::jtRound,EndType::etOpenRound);
Paths toolShape;
clipof.Execute(toolShape,toolRadiusScaled-2);
clipof.Execute(toolShape,toolRadiusScaled+safetyDistanceScaled);
clip.AddPaths(toolShape,PolyType::ptSubject,true);
clip.AddPaths(cleared,PolyType::ptClip,true);
Paths crossing;
@@ -1412,7 +1406,7 @@ namespace AdaptivePath {
for(auto &p : crossing) {
collisionArea += fabs(Area(p));
}
return collisionArea <= optimalCutAreaPD*RESOLUTION_FACTOR/2;
return collisionArea < optimalCutAreaPD*RESOLUTION_FACTOR/10+1;
}
bool Adaptive2d::IsAllowedToCutTrough(const IntPoint &p1,const IntPoint &p2,const Paths & cleared, const Paths & toolBoundPaths) {
@@ -1445,7 +1439,7 @@ namespace AdaptivePath {
return true;
}
void Adaptive2d::AppendToolPath(TPaths &progressPaths,AdaptiveOutput & output,const Path & passToolPath,const Paths & cleared, const Paths & toolBoundPaths, bool close) {
void Adaptive2d::AppendToolPath(TPaths &progressPaths,AdaptiveOutput & output,const Path & passToolPath,const Paths & cleared, const Paths & toolBoundPaths) {
if(passToolPath.size()<1) return;
IntPoint nextPoint(passToolPath[0]);
@@ -1458,7 +1452,7 @@ namespace AdaptivePath {
//first we try to cut through the linking move for short distances
bool linkFound = false;
double linkDistance = sqrt(DistanceSqrd(lastPoint,nextPoint));
if(linkDistance<4*toolRadiusScaled && IsAllowedToCutTrough(lastPoint,nextPoint,cleared,toolBoundPaths)) {
if(linkDistance<toolRadiusScaled && IsAllowedToCutTrough(lastPoint,nextPoint,cleared,toolBoundPaths)) {
//cout << "cleared through" << endl;
TPath linkPath;
linkPath.first = MotionType::mtCutting;
@@ -1478,7 +1472,7 @@ namespace AdaptivePath {
double offset=stepOverFactor*toolRadiusScaled;
bool keepDownLinkExists = false;
bool keepDownLinkTooLong = true;
for(int i=1;i<10;i++) {
for(int i=1;i<10;i+=2) {
clipof.Execute(clearedOff,-toolRadiusScaled-offset/i);
// AddPathsToProgress(progressPaths,clearedOff, MotionType::mtLinkClear);
// CheckReportProgress(progressPaths,true);
@@ -1495,7 +1489,7 @@ namespace AdaptivePath {
//cout << "not IsAllowedToCutTrough2" << endl;
keepDownLinkExists=false;
}
if(!IsClearPath(keepToolDownLinkPath,cleared)) {
if(!IsClearPath(keepToolDownLinkPath,cleared,stepOverFactor*toolRadiusScaled/2)) {
//cout << "not IsClearPath keepToolDownLinkPath" << endl;
keepDownLinkExists=false;
}
@@ -1512,7 +1506,7 @@ namespace AdaptivePath {
tp << lastInterimPoint;
tp << nextInterimPoint;
bool directLinkInterimLinkClear = IsClearPath(tp,cleared); // cleared direct line between interim points?
bool directLinkInterimLinkClear = IsClearPath(tp,cleared,stepOverFactor*toolRadiusScaled/2); // cleared direct line between interim points?
if(directLinkInterimLinkClear) { // if direct link is ok
// add disengage moves
TPath linkPath1;
@@ -1585,8 +1579,46 @@ namespace AdaptivePath {
}
}
}
if(!linkFound) { // keeptool down link not found, try lift through (any) clear interim points
ClipperOffset clipof;
clipof.AddPaths(cleared,JoinType::jtRound,EndType::etClosedPolygon);
Paths clearedOff;
double offset=stepOverFactor*toolRadiusScaled;
// see if link through interim points is possible,
clipof.Execute(clearedOff,-toolRadiusScaled-offset);
IntPoint interim1;
IntPoint interim2;
if(FindClosestClearPoints(clearedOff,lastPoint,nextPoint,toolRadiusScaled + 2*offset,interim1,interim2)) {
if(IsAllowedToCutTrough(lastPoint,interim1,cleared,toolBoundPaths)
&& IsAllowedToCutTrough(nextPoint,interim2,cleared,toolBoundPaths)) {
// add disengage moves
TPath linkPath1;
linkPath1.first = MotionType::mtCutting;
linkPath1.second.push_back(lastTPoint);
linkPath1.second.push_back(DPoint(double(interim1.X)/scaleFactor,double(interim1.Y)/scaleFactor));
output.AdaptivePaths.push_back(linkPath1);
// add linking move
TPath linkPath2;
linkPath2.first = MotionType::mtLinkNotClear;
linkPath2.second.push_back(DPoint(double(interim1.X)/scaleFactor,double(interim1.Y)/scaleFactor));
linkPath2.second.push_back(DPoint(double(interim2.X)/scaleFactor,double(interim2.Y)/scaleFactor));
output.AdaptivePaths.push_back(linkPath2);
// add engage move
TPath linkPath3;
linkPath3.first = MotionType::mtCutting;
linkPath3.second.push_back(DPoint(double(interim2.X)/scaleFactor,double(interim2.Y)/scaleFactor));
linkPath3.second.push_back(DPoint(double(nextPoint.X)/scaleFactor,double(nextPoint.Y)/scaleFactor));
output.AdaptivePaths.push_back(linkPath3);
linkFound=true;
}
}
}
if(!linkFound) { // nothing clear so far - check direct link with no interim points - either this is clear or we need to raise the tool
//cerr << "keepToolDownLinkPath NOT CLEAR" << endl;
Path tp;
tp << lastPoint;
tp << nextPoint;
@@ -1611,12 +1643,12 @@ namespace AdaptivePath {
cutPath.second.push_back(nextT);
}
if(close) {
DPoint nextT;
nextT.first = double(passToolPath[0].X)/scaleFactor;
nextT.second = double(passToolPath[0].Y)/scaleFactor;
cutPath.second.push_back(nextT);
}
// if(close) {
// DPoint nextT;
// nextT.first = double(passToolPath[0].X)/scaleFactor;
// nextT.second = double(passToolPath[0].Y)/scaleFactor;
// cutPath.second.push_back(nextT);
// }
if(cutPath.second.size()>0) output.AdaptivePaths.push_back(cutPath);
}
@@ -1761,7 +1793,7 @@ namespace AdaptivePath {
#ifdef DEV_MODE
clock_t start_clock=clock();
#endif
Paths clearedBeforePass;
/*******************************
* LOOP - PASSES
*******************************/
@@ -1791,7 +1823,7 @@ namespace AdaptivePath {
size_t clpPathIndex;
size_t clpSegmentIndex;
double clpParamter;
Paths clearedBeforePass = cleared;
clearedBeforePass = cleared;
/*******************************
* LOOP - POINTS
*******************************/
@@ -2022,14 +2054,14 @@ namespace AdaptivePath {
/* FINISHING PASS */
/**********************************/
Paths finishingPaths;
clipof.Clear();
clipof.AddPaths(boundPaths,JoinType::jtRound,EndType::etClosedPolygon);
Paths finishingPaths;
clipof.Execute(finishingPaths,-toolRadiusScaled);
IntPoint lastPoint = toolPos;
Path finShiftedPath;
bool allCutsAllowed=true;
while(PopPathWithClosestPoint(finishingPaths,lastPoint,finShiftedPath)) {
// skip finishing passes outside the stock boundary - make no sense to cut where is no material
bool allPointsOutside=true;
@@ -2048,25 +2080,40 @@ namespace AdaptivePath {
for(auto & pt:finShiftedPath) {
progressPaths.back().second.push_back(DPoint(double(pt.X)/scaleFactor,double(pt.Y)/scaleFactor));
}
Path cleaned;
CleanPath(finShiftedPath,cleaned,FINISHING_CLEAN_PATH_TOLERANCE);
AppendToolPath(progressPaths,output,cleaned,cleared,toolBoundPaths,true);
// expand cleared for finishing path
clipof.Clear();
clipof.AddPath(cleaned,JoinType::jtRound,EndType::etOpenRound);
Paths toolCoverPoly;
clipof.Execute(toolCoverPoly,toolRadiusScaled+1);
clip.Clear();
clip.AddPaths(cleared,PolyType::ptSubject,true);
clip.AddPaths(toolCoverPoly,PolyType::ptClip,true);
clip.Execute(ClipType::ctUnion,cleared);
CleanPolygons(cleared);
if(!finShiftedPath.empty()) finShiftedPath << finShiftedPath.front(); // make sure its closed
if(finShiftedPath.size()>0) {
lastPoint.X = finShiftedPath.back().X;
lastPoint.Y = finShiftedPath.back().Y;
Path finCleaned;
CleanPath(finShiftedPath,finCleaned,FINISHING_CLEAN_PATH_TOLERANCE);
//safety check for finishing paths
for(size_t i=1;i<finCleaned.size();i++) {
if(!IsAllowedToCutTrough(finCleaned.at(i-1),finCleaned.at(i),clearedBeforePass,toolBoundPaths)) {
allCutsAllowed=false;
break;
}
}
if(allCutsAllowed) {
AppendToolPath(progressPaths,output,finCleaned,clearedBeforePass,toolBoundPaths);
//expand cleared for finishing path
clipof.Clear();
clipof.AddPath(finCleaned,JoinType::jtRound,EndType::etOpenRound);
Paths toolCoverPoly;
clipof.Execute(toolCoverPoly,toolRadiusScaled+1);
clip.Clear();
clip.AddPaths(cleared,PolyType::ptSubject,true);
clip.AddPaths(toolCoverPoly,PolyType::ptClip,true);
clip.Execute(ClipType::ctUnion,cleared);
CleanPolygons(cleared);
lastPoint.X = finCleaned.back().X;
lastPoint.Y = finCleaned.back().Y;
} else {
cerr << "UNABLE TO ADD FINISHING PASS! Please try increasing accuracy." << endl;
break;
}
clearedBeforePass=cleared;
}
Path returnPath;
@@ -2096,6 +2143,10 @@ namespace AdaptivePath {
<< " linking moves:" << unclearLinkingMoveCount
<< endl;
#endif
// make sure invalid paths are not used
if(!allCutsAllowed) output.AdaptivePaths.clear();
results.push_back(output);
}

View File

@@ -61,7 +61,7 @@ namespace AdaptivePath {
double tolerance=0.1;
double stockToLeave=0;
bool forceInsideOut = true;
int keepToolDownDistRatio = 3; // keep tool down distance ratio
double keepToolDownDistRatio = 3.0; // keep tool down distance ratio
OperationType opType = OperationType::otClearingInside;
std::list<AdaptiveOutput> Execute(const DPaths &stockPaths, const DPaths &paths, std::function<bool(TPaths)> progressCallbackFn);
@@ -100,8 +100,8 @@ namespace AdaptivePath {
bool FindEntryPointOutside(TPaths &progressPaths,const Paths & toolBoundPaths,const Paths &bound, Paths &cleared /*output*/,
IntPoint &entryPoint /*output*/, IntPoint & toolPos, DoublePoint & toolDir);
double CalcCutArea(Clipper & clip,const IntPoint &toolPos, const IntPoint &newToolPos, const Paths &cleared_paths, bool preventConvetionalMode=true);
void AppendToolPath(TPaths &progressPaths,AdaptiveOutput & output,const Path & passToolPath,const Paths & cleared,const Paths & toolBoundPaths, bool close=false);
bool IsClearPath(const Path & path,const Paths & cleared);
void AppendToolPath(TPaths &progressPaths,AdaptiveOutput & output,const Path & passToolPath,const Paths & cleared,const Paths & toolBoundPaths);
bool IsClearPath(const Path & path,const Paths & cleared, double safetyDistanceScaled=0);
bool IsAllowedToCutTrough(const IntPoint &p1,const IntPoint &p2,const Paths & cleared,const Paths & toolBoundPaths);
friend class EngagePoint; // for CalcCutArea

View File

@@ -530,6 +530,7 @@ BOOST_PYTHON_MODULE(area) {
.def_readwrite("forceInsideOut", &Adaptive2d::forceInsideOut)
//.def_readwrite("polyTreeNestingLimit", &Adaptive2d::polyTreeNestingLimit)
.def_readwrite("tolerance", &Adaptive2d::tolerance)
.def_readwrite("keepToolDownDistRatio", &Adaptive2d::keepToolDownDistRatio)
.def_readwrite("opType", &Adaptive2d::opType);

View File

@@ -391,6 +391,7 @@ void init_pyarea(py::module &m){
.def_readwrite("forceInsideOut", &Adaptive2d::forceInsideOut)
//.def_readwrite("polyTreeNestingLimit", &Adaptive2d::polyTreeNestingLimit)
.def_readwrite("tolerance", &Adaptive2d::tolerance)
.def_readwrite("keepToolDownDistRatio", &Adaptive2d::keepToolDownDistRatio)
.def_readwrite("opType", &Adaptive2d::opType);
}