From 365227a049e8ea2eb291d7adccf91b3303df4cec Mon Sep 17 00:00:00 2001 From: kreso-t Date: Sat, 1 Sep 2018 17:45:31 +0200 Subject: [PATCH] Path: Adaptive - feature to clear from outside stock bounday inwards --- src/Mod/Path/PathScripts/PathAdaptive.py | 107 +++++++----- src/Mod/Path/PathScripts/PathAdaptiveGui.py | 22 ++- src/Mod/Path/libarea/Adaptive.cpp | 182 +++++++++++++++----- src/Mod/Path/libarea/Adaptive.hpp | 18 +- src/Mod/Path/libarea/PythonStuff.cpp | 23 ++- src/Mod/Path/libarea/pyarea.cpp | 6 +- 6 files changed, 249 insertions(+), 109 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathAdaptive.py b/src/Mod/Path/PathScripts/PathAdaptive.py index 2f533f1ea3..d4a710004c 100644 --- a/src/Mod/Path/PathScripts/PathAdaptive.py +++ b/src/Mod/Path/PathScripts/PathAdaptive.py @@ -160,35 +160,44 @@ def GenerateGCode(op,obj,adaptiveResults, helixDiameter): lx=region["HelixCenterPoint"][0] ly=region["HelixCenterPoint"][1] - r = helixRadius - 0.01 - #helix ramp passDepth = (passStartDepth - passEndDepth) - maxfi = passDepth / depthPerOneCircle * 2 * math.pi - fi = 0 - offsetFi =-maxfi + startAngle-math.pi/16 - helixStart = [region["HelixCenterPoint"][0] + r * math.cos(offsetFi), region["HelixCenterPoint"][1] + r * math.sin(offsetFi)] - op.commandlist.append(Path.Command("(helix to depth: %f)"%passEndDepth)) - #if step == 1: - #rapid move to start point - op.commandlist.append(Path.Command( - "G0", {"X": helixStart[0], "Y": helixStart[1], "Z": obj.ClearanceHeight.Value})) - #rapid move to safe height - op.commandlist.append(Path.Command( - "G0", {"X": helixStart[0], "Y": helixStart[1], "Z": obj.SafeHeight.Value})) + #helix ramp + if helixRadius>0.0001: + r = helixRadius - 0.01 + maxfi = passDepth / depthPerOneCircle * 2 * math.pi + fi = 0 + offsetFi =-maxfi + startAngle-math.pi/16 - op.commandlist.append(Path.Command("G1", { - "X": helixStart[0], "Y": helixStart[1], "Z": passStartDepth, "F": op.vertFeed})) + helixStart = [region["HelixCenterPoint"][0] + r * math.cos(offsetFi), region["HelixCenterPoint"][1] + r * math.sin(offsetFi)] + + op.commandlist.append(Path.Command("(helix to depth: %f)"%passEndDepth)) + #if step == 1: + #rapid move to start point + op.commandlist.append(Path.Command( + "G0", {"X": helixStart[0], "Y": helixStart[1], "Z": obj.ClearanceHeight.Value})) + #rapid move to safe height + op.commandlist.append(Path.Command( + "G0", {"X": helixStart[0], "Y": helixStart[1], "Z": obj.SafeHeight.Value})) + + op.commandlist.append(Path.Command("G1", { + "X": helixStart[0], "Y": helixStart[1], "Z": passStartDepth, "F": op.vertFeed})) + + while fi Adaptive2d::Execute(const DPaths &paths, std::function progressCallbackFn) { + std::list Adaptive2d::Execute(const DPaths &stockPaths, const DPaths &paths, std::function progressCallbackFn) { //********************************** // Initializations // ********************************** @@ -845,6 +845,7 @@ namespace AdaptivePath { // ********************** // Convert input paths to clipper //************************ + inputPaths.clear(); for(size_t i=0;i pt = stockPaths[i][j]; + cpth.push_back(IntPoint(long(pt.first*scaleFactor),long(pt.second*scaleFactor))); + } + stockInputPaths.push_back(cpth); + } + SimplifyPolygons(stockInputPaths); + + if(stockToLeave>NTOL) { clipof.Clear(); clipof.AddPaths(inputPaths,JoinType::jtRound,EndType::etClosedPolygon); @@ -874,7 +890,18 @@ namespace AdaptivePath { // Resolve hierarchy and run processing // ******************************** - if(opType==OperationType::otClearing) { + if(opType==OperationType::otClearingInside || opType==OperationType::otClearingOutside) { + // add stock paths, with overshooting + if(opType==OperationType::otClearingOutside) { + clipof.Clear(); + clipof.AddPaths(stockInputPaths,JoinType::jtRound,EndType::etClosedPolygon); + double overshootDistance =2*toolRadiusScaled + toolRadiusScaled*stepOverFactor; + if(forceInsideOut) overshootDistance=0; + Paths stockOvershoot; + clipof.Execute(stockOvershoot,overshootDistance); + ReversePaths(stockOvershoot); + for(auto p : stockOvershoot) inputPaths.push_back(p); + } clipof.Clear(); clipof.AddPaths(inputPaths,JoinType::jtRound,EndType::etClosedPolygon); Paths paths; @@ -942,7 +969,10 @@ namespace AdaptivePath { return results; } - bool Adaptive2d::FindEntryPoint(const Paths & toolBoundPaths,const Paths &boundPaths, Paths &cleared /*output-initial cleard area by helix*/, IntPoint &entryPoint /*output*/) { + bool Adaptive2d::FindEntryPoint(TPaths &progressPaths, const Paths & toolBoundPaths,const Paths &boundPaths, + Paths &cleared /*output-initial cleard area by helix*/, + IntPoint &entryPoint /*output*/, + IntPoint & toolPos, DoublePoint & toolDir) { Paths incOffset; Paths lastValidOffset; Clipper clip; @@ -1018,8 +1048,66 @@ namespace AdaptivePath { } //DrawCircle(entryPoint,scaleFactor,10); if(!found) cerr<<"Start point not found!"<0 ? pth[i-1] : pth.back(); + // if point is outside the stock + if(PointInPolygon(checkPoint,stockInputPaths.front())==0) { + clipof.Clear(); + clipof.AddPaths(toolBoundPaths,JoinType::jtRound,EndType::etClosedPolygon); + Paths sol2; + clipof.Execute(sol2,toolRadiusScaled*stepOverFactor); + clipof.Clear(); + clipof.AddPaths(sol2,JoinType::jtRound,EndType::etClosedLine); + clipof.Execute(cleared,toolRadiusScaled); + // trim cleared paths to stock boundary (only outside part remain) + clip.Clear(); + clip.AddPaths(cleared,PolyType::ptSubject, true); + clip.AddPaths(stockInputPaths,PolyType::ptClip, true); + clip.Execute(ClipType::ctDifference,cleared); + + AddPathsToProgress(progressPaths,cleared); + entryPoint=checkPoint; + toolPos = entryPoint; + // find tool dir + double len=sqrt(DistanceSqrd(lastPoint,checkPoint)); + toolDir = DoublePoint((checkPoint.X - lastPoint.X)/len,(checkPoint.Y - lastPoint.Y)/len); + return true; + } + } + } + //AddPathsToProgress(progressPaths,outsidePaths); + return false; + } /** * returns true if line from lastPoint to nextPoint is clear from obstacles @@ -1130,6 +1218,18 @@ namespace AdaptivePath { while(progressPaths.front().second.size()>0) progressPaths.front().second.pop_back(); progressPaths.front().second.push_back(next); } + + void Adaptive2d::AddPathsToProgress(TPaths &progressPaths,Paths paths) { + for(const auto & pth :paths) { + if(pth.size()>0) { + progressPaths.push_back(TPath()); + for(const auto pt: pth) + progressPaths.back().second.push_back(DPoint(double(pt.X)/scaleFactor,double(pt.Y)/scaleFactor)); + progressPaths.back().second.push_back(DPoint(double(pth.front().X)/scaleFactor,double(pth.front().Y)/scaleFactor)); + } + } + } + void Adaptive2d::ProcessPolyNode(Paths & boundPaths, Paths & toolBoundPaths) { //cout << " Adaptive2d::ProcessPolyNode" << endl; Perf_ProcessPolyNode.Start(); @@ -1142,10 +1242,21 @@ namespace AdaptivePath { //SimplifyPolygons(toolBoundPaths); CleanPolygons(toolBoundPaths); //SimplifyPolygons(boundPaths); - CleanPolygons(toolBoundPaths); + //CleanPolygons(toolBoundPaths); + //AddPathsToProgress(progressPaths,toolBoundPaths); + IntPoint toolPos; + DoublePoint toolDir; Paths cleared; - if(!FindEntryPoint(toolBoundPaths, boundPaths, cleared, entryPoint)) return; + bool outsideEntry = false; + if(FindEntryPointOutside(progressPaths, toolBoundPaths, boundPaths, cleared, entryPoint, toolPos,toolDir)) { + toolPos = IntPoint(entryPoint.X,entryPoint.Y); + toolDir = DoublePoint(1.0,0.0); + outsideEntry=true; + } else { + if(!FindEntryPoint(progressPaths, toolBoundPaths, boundPaths, cleared, entryPoint, toolPos,toolDir)) return; + } + //cout << "Entry point:" << entryPoint << endl; Clipper clip; ClipperOffset clipof; @@ -1156,42 +1267,14 @@ namespace AdaptivePath { long stepScaled = long(RESOLUTION_FACTOR); IntPoint engagePoint; - IntPoint toolPos; - DoublePoint toolDir; IntPoint newToolPos; DoublePoint newToolDir; - // visualize/progress for boundPath - for(const auto & pth :toolBoundPaths) { - if(pth.size()>0) { - progressPaths.push_back(TPath()); - for(const auto pt: pth) - progressPaths.back().second.push_back(DPoint(double(pt.X)/scaleFactor,double(pt.Y)/scaleFactor)); - progressPaths.back().second.push_back(DPoint(double(pth.front().X)/scaleFactor,double(pth.front().Y)/scaleFactor)); - } - } + CheckReportProgress(progressPaths, true); - - // visualize/progress for helix - clipof.Clear(); - Path hp; - hp << entryPoint; - clipof.AddPath(hp,JoinType::jtRound,EndType::etOpenRound); - Paths hps; - clipof.Execute(hps,helixRampRadiusScaled); - progressPaths.push_back(TPath()); - - // show in progress cb - for(auto & pt:hps[0]) { - progressPaths.back().second.push_back(DPoint(double(pt.X)/scaleFactor,double(pt.Y)/scaleFactor)); - } - CheckReportProgress(progressPaths); - - // find the first tool position and direction - toolPos = IntPoint(entryPoint.X,entryPoint.Y - helixRampRadiusScaled); - toolDir = DoublePoint(1.0,0.0); - output.StartPoint =DPoint(double(toolPos.X)/scaleFactor,double(toolPos.Y)/scaleFactor); + IntPoint startPoint = toolPos; + output.StartPoint =DPoint(double(startPoint.X)/scaleFactor,double(startPoint.Y)/scaleFactor); bool firstEngagePoint=true; Path passToolPath; // to store pass toolpath @@ -1211,6 +1294,8 @@ namespace AdaptivePath { long over_cut_count =0; unclearLinkingMoveCount=0; //long engage_no_cut_count=0; + double prevDistFromStart =0; + bool prevDistTrend = false; double perf_total_len=0; #ifdef DEV_MODE @@ -1247,7 +1332,7 @@ namespace AdaptivePath { *******************************/ for(long point_index=0;point_indexcirc/(16*RESOLUTION_FACTOR)) { + + // update cleared paths when trend of distance from start point changes sign (starts to get closer, or start to get farther) + double distFromStart = sqrt(DistanceSqrd(toolPos,startPoint)); + bool distanceTrend = distFromStart > prevDistFromStart ? true : false; + + if(distFromStart!=prevDistTrend) { Perf_ExpandCleared.Start(); // expand cleared clipof.Clear(); @@ -1381,8 +1467,10 @@ namespace AdaptivePath { CleanPolygons(cleared); toClearPath.clear(); Perf_ExpandCleared.Stop(); - } } + prevDistTrend = distanceTrend; + prevDistFromStart = distFromStart; + if(area>0) { // cut is ok - record it if(toClearPath.size()==0) toClearPath.push_back(toolPos); @@ -1463,6 +1551,14 @@ namespace AdaptivePath { IntPoint lastPoint; for(auto & pth: finishingPaths) { + // trim finishing passes outside the stock boundary - make no sense to cut where is no material + Paths diff; + clip.Clear(); + clip.AddPath(pth,PolyType::ptSubject,true); + clip.AddPaths(stockInputPaths,PolyType::ptClip,true); + clip.Execute(ClipType::ctDifference,diff); + if(diff.size()>0) continue; + progressPaths.push_back(TPath()); // show in progress cb for(auto & pt:pth) { diff --git a/src/Mod/Path/libarea/Adaptive.hpp b/src/Mod/Path/libarea/Adaptive.hpp index e46774f541..d06b8d6d7d 100644 --- a/src/Mod/Path/libarea/Adaptive.hpp +++ b/src/Mod/Path/libarea/Adaptive.hpp @@ -29,7 +29,7 @@ namespace AdaptivePath { enum MotionType { mtCutting = 0, mtLinkClear = 1, mtLinkNotClear = 2, mtLinkClearAtPrevPass = 3 }; - enum OperationType { otClearing = 0, otProfilingInside = 1, otProfilingOutside = 2 }; + enum OperationType { otClearingInside = 0, otClearingOutside = 1, otProfilingInside = 2, otProfilingOutside = 3 }; typedef std::pair DPoint; typedef std::vector DPath; @@ -58,12 +58,12 @@ namespace AdaptivePath { double toolDiameter=5; double helixRampDiameter=0; double stepOverFactor = 0.2; - int polyTreeNestingLimit=0; double tolerance=0.1; double stockToLeave=0; - OperationType opType = OperationType::otClearing; + bool forceInsideOut = true; + OperationType opType = OperationType::otClearingInside; - std::list Execute(const DPaths &paths, std::function progressCallbackFn); + std::list Execute(const DPaths &stockPaths, const DPaths &paths, std::function progressCallbackFn); #ifdef DEV_MODE /*for debugging*/ @@ -75,7 +75,8 @@ namespace AdaptivePath { private: std::list results; Paths inputPaths; - + Paths stockInputPaths; + int polyTreeNestingLimit=0; double scaleFactor=100; long toolRadiusScaled=10; long finishPassOffsetScaled=0; @@ -92,14 +93,17 @@ namespace AdaptivePath { Path toolGeometry; // tool geometry at coord 0,0, should not be modified void ProcessPolyNode(Paths & boundPaths, Paths & toolBoundPaths); - bool FindEntryPoint(const Paths & toolBoundPaths,const Paths &bound, Paths &cleared /*output*/, IntPoint &entryPoint /*output*/); + bool FindEntryPoint(TPaths &progressPaths,const Paths & toolBoundPaths,const Paths &bound, Paths &cleared /*output*/, + IntPoint &entryPoint /*output*/, IntPoint & toolPos, DoublePoint & toolDir); + 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); void AppendToolPath(AdaptiveOutput & output,const Path & passToolPath,const Paths & cleared,const Paths & toolBoundPaths, bool close=false); bool CheckCollision(const IntPoint &lastPoint,const IntPoint &nextPoint,const Paths & cleared); friend class EngagePoint; // for CalcCutArea void CheckReportProgress(TPaths &progressPaths,bool force=false); - + void AddPathsToProgress(TPaths &progressPaths,Paths paths); private: // constants for fine tuning const bool preventConvetionalMode = true; const double RESOLUTION_FACTOR = 8.0; diff --git a/src/Mod/Path/libarea/PythonStuff.cpp b/src/Mod/Path/libarea/PythonStuff.cpp index ae2ea6fe4d..f399abd92d 100644 --- a/src/Mod/Path/libarea/PythonStuff.cpp +++ b/src/Mod/Path/libarea/PythonStuff.cpp @@ -295,8 +295,21 @@ double AreaGetArea(const CArea& a) // Adaptive2d.Execute wrapper -bp::list AdaptiveExecute(AdaptivePath::Adaptive2d& ada,const boost::python::list &in_paths, boost::python::object progressCallbackFn) { +bp::list AdaptiveExecute(AdaptivePath::Adaptive2d& ada,const boost::python::list &stock_paths, const boost::python::list &in_paths, boost::python::object progressCallbackFn) { bp::list out_list; + + // convert stock paths + AdaptivePath::DPaths stock_dpaths; + for(bp::ssize_t i=0;i(stock_paths[i]); + AdaptivePath::DPath dpath; + for(bp::ssize_t j=0;j(in_path[j]); + dpath.push_back(pair(bp::extract(in_point[0]),bp::extract(in_point[1]))); + } + stock_dpaths.push_back(dpath); + } + // convert inputs AdaptivePath::DPaths dpaths; for(bp::ssize_t i=0;i result=ada.Execute(dpaths,[progressCallbackFn](AdaptivePath::TPaths tp)->bool { + std::list result=ada.Execute(stock_dpaths,dpaths,[progressCallbackFn](AdaptivePath::TPaths tp)->bool { bp::list out_paths; for(const auto & in_pair : tp) { bp::list path; @@ -495,7 +508,8 @@ BOOST_PYTHON_MODULE(area) { .value("LinkClearAtPrevPass", MotionType::mtLinkClearAtPrevPass); bp::enum_("AdaptiveOperationType") - .value("Clearing", OperationType::otClearing) + .value("ClearingInside", OperationType::otClearingInside) + .value("ClearingOutside", OperationType::otClearingOutside) .value("ProfilingInside", OperationType::otProfilingInside) .value("ProfilingOutside", OperationType::otProfilingOutside); @@ -513,7 +527,8 @@ BOOST_PYTHON_MODULE(area) { .def_readwrite("toolDiameter", &Adaptive2d::toolDiameter) .def_readwrite("stockToLeave", &Adaptive2d::stockToLeave) .def_readwrite("helixRampDiameter", &Adaptive2d::helixRampDiameter) - .def_readwrite("polyTreeNestingLimit", &Adaptive2d::polyTreeNestingLimit) + .def_readwrite("forceInsideOut", &Adaptive2d::forceInsideOut) + //.def_readwrite("polyTreeNestingLimit", &Adaptive2d::polyTreeNestingLimit) .def_readwrite("tolerance", &Adaptive2d::tolerance) .def_readwrite("opType", &Adaptive2d::opType); diff --git a/src/Mod/Path/libarea/pyarea.cpp b/src/Mod/Path/libarea/pyarea.cpp index 45bb30b0e4..9abf5c3b97 100644 --- a/src/Mod/Path/libarea/pyarea.cpp +++ b/src/Mod/Path/libarea/pyarea.cpp @@ -369,7 +369,8 @@ void init_pyarea(py::module &m){ .value("LinkClearAtPrevPass", MotionType::mtLinkClearAtPrevPass); py::enum_(m, "AdaptiveOperationType") - .value("Clearing", OperationType::otClearing) + .value("ClearingInside", OperationType::otClearingInside) + .value("ClearingOutside", OperationType::otClearingOutside) .value("ProfilingInside", OperationType::otProfilingInside) .value("ProfilingOutside", OperationType::otProfilingOutside); @@ -387,7 +388,8 @@ void init_pyarea(py::module &m){ .def_readwrite("toolDiameter", &Adaptive2d::toolDiameter) .def_readwrite("stockToLeave", &Adaptive2d::stockToLeave) .def_readwrite("helixRampDiameter", &Adaptive2d::helixRampDiameter) - .def_readwrite("polyTreeNestingLimit", &Adaptive2d::polyTreeNestingLimit) + .def_readwrite("forceInsideOut", &Adaptive2d::forceInsideOut) + //.def_readwrite("polyTreeNestingLimit", &Adaptive2d::polyTreeNestingLimit) .def_readwrite("tolerance", &Adaptive2d::tolerance) .def_readwrite("opType", &Adaptive2d::opType); }