From 598fff8b401cd85b1ace47f05f7d69937e900428 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Thu, 21 Sep 2017 18:23:24 +0800 Subject: [PATCH] Path.Area: add greedy sort mode --- src/Mod/Path/App/Area.cpp | 71 +++++++++++++++++++--- src/Mod/Path/App/AreaParams.h | 19 +++--- src/Mod/Path/PathScripts/PathPocketBase.py | 10 ++- 3 files changed, 80 insertions(+), 20 deletions(-) diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index 1f50b770d5..d94515dd90 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -2290,6 +2290,7 @@ struct ShapeInfo{ BRepExtrema_DistShapeShape extss(v,wire); if(extss.IsDone() && extss.NbSolution()) { d = extss.Value(); + d *= d; p = extss.PointOnShape2(1); support = extss.SupportOnShape2(1); support_edge = extss.SupportTypeShape2(1)==BRepExtrema_IsOnEdge; @@ -2465,7 +2466,8 @@ struct ShapeInfo{ return myBestWire->wire; } - std::list sortWires(const gp_Pnt &pstart, gp_Pnt &pend,double min_dist, gp_Pnt *pentry) { + std::list sortWires(const gp_Pnt &pstart, gp_Pnt &pend, + double min_dist, double max_dist, gp_Pnt *pentry) { if(myWires.empty() || pstart.SquareDistance(myStartPt)>Precision::SquareConfusion()) @@ -2493,7 +2495,9 @@ struct ShapeInfo{ FC_DURATION_PLUS(myParams.rd,t); myWires.erase(myBestWire); if(myWires.empty()) break; - nearest(pend); + double d = nearest(pend); + if(max_dist>0 && d>max_dist) + break; } return std::move(wires); } @@ -2639,6 +2643,9 @@ struct WireOrienter { } }; +typedef Standard_Real (gp_Pnt::*AxisGetter)() const; +typedef void (gp_Pnt::*AxisSetter)(Standard_Real); + std::list Area::sortWires(const std::list &shapes, gp_Pnt *_pstart, gp_Pnt *_pend, double *stepdown_hint, short *_parc_plane, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_SORT)) @@ -2647,6 +2654,27 @@ std::list Area::sortWires(const std::list &shapes, if(shapes.empty()) return wires; + AxisGetter getter; + AxisSetter setter; + switch(retract_axis) { + case RetractAxisX: + getter = &gp_Pnt::X; + setter = &gp_Pnt::SetX; + break; + case RetractAxisY: + getter = &gp_Pnt::Y; + setter = &gp_Pnt::SetY; + break; + default: + getter = &gp_Pnt::Z; + setter = &gp_Pnt::SetZ; + } + + if(sort_mode==SortModeGreedy && threshold Area::sortWires(const std::list &shapes, fabs(pstart.Y()) Area::sortWires(const std::list &shapes, gp_Pln pln; double hint = 0.0; bool hint_first = true; + auto current_it = shape_list.end(); + double current_height = (pstart.*getter)(); + double max_dist = sort_mode==SortModeGreedy?threshold*threshold:0; while(shape_list.size()) { AREA_TRACE("sorting " << shape_list.size() << ' ' << AREA_XYZ(pstart)); double best_d; @@ -2779,7 +2810,7 @@ std::list Area::sortWires(const std::list &shapes, for(auto it=best_it;it!=shape_list.end();++it) { double d; gp_Pnt pt; - if(it->myPlanar) + if(it->myPlanar && current_it==shape_list.end()) d = it->myPln.SquareDistance(pstart); else d = it->nearest(pstart); @@ -2790,12 +2821,30 @@ std::list Area::sortWires(const std::list &shapes, } } gp_Pnt pentry; - wires.splice(wires.end(),best_it->sortWires(pstart,pend,min_dist,&pentry)); + if(sort_mode==SortModeGreedy) { + // greedy sort will go down to the next layer even if the current + // layer is not finished, as long as the path jump distance is + // within the threshold. + if(current_it==shape_list.end()) { + current_it = best_it; + current_height = (pstart.*getter)(); + } else if(best_d>max_dist) { + // If the path jumps beyond the threshold, bail out and go back + // to the first unfinished layer. + (pstart.*setter)(current_height); + current_it->nearest(pstart); + best_it = current_it; + } + } + + wires.splice(wires.end(), + best_it->sortWires(pstart,pend,min_dist,max_dist,&pentry)); + if(use_bound && _pstart) { use_bound = false; *_pstart = pentry; } - if(sort_mode==SortMode2D5 && stepdown_hint) { + if((sort_mode==SortMode2D5||sort_mode==SortMode3D) && stepdown_hint) { if(!best_it->myPlanar) hint_first = true; else if(hint_first) @@ -2821,7 +2870,12 @@ std::list Area::sortWires(const std::list &shapes, pln = best_it->myPln; } pstart = pend; - shape_list.erase(best_it); + + if(best_it->myWires.empty()) { + if(current_it == best_it) + current_it = shape_list.end(); + shape_list.erase(best_it); + } } if(stepdown_hint && hint!=0.0) *stepdown_hint = hint; @@ -2866,9 +2920,6 @@ static inline void addG1(bool verbose,Toolpath &path, const gp_Pnt &last, return; } -typedef Standard_Real (gp_Pnt::*AxisGetter)() const; -typedef void (gp_Pnt::*AxisSetter)(Standard_Real); - static void addG0(bool verbose, Toolpath &path, gp_Pnt last, const gp_Pnt &next, AxisGetter getter, AxisSetter setter, diff --git a/src/Mod/Path/App/AreaParams.h b/src/Mod/Path/App/AreaParams.h index 70f57b5b7e..3604016089 100644 --- a/src/Mod/Path/App/AreaParams.h +++ b/src/Mod/Path/App/AreaParams.h @@ -203,27 +203,30 @@ "'2D5' explode shapes into wires, and groups the shapes by its plane. The 'start' position\n"\ "chooses the first plane to start. The algorithm will then sort within the plane and then\n"\ "move on to the next nearest plane.\n"\ - "'3D' makes no assumption of planarity. The sorting is done across 3D space\n",\ - (None)(2D5)(3D)))\ + "'3D' makes no assumption of planarity. The sorting is done across 3D space.\n"\ + "'Greedy' like '2D5' but will try to minimize travel by searching for nearest path below\n"\ + "the current milling layer. The path in lower layer is only selected if the moving distance\n"\ + "is within the value given in 'threshold'.",\ + (None)(2D5)(3D)(Greedy)))\ AREA_PARAMS_MIN_DIST \ ((double, abscissa, SortAbscissa, 3.0, "Controls vertex sampling on wire for nearest point searching\n"\ "The sampling is dong using OCC GCPnts_UniformAbscissa",App::PropertyLength))\ ((short, nearest_k, NearestK, 3, "Nearest k sampling vertices are considered during sorting"))\ AREA_PARAMS_ORIENTATION \ ((enum, direction, Direction, 0, "Enforce open path direction",\ - (None)(XPositive)(XNegative)(YPositive)(YNegative)(ZPositive)(ZNegative))) + (None)(XPositive)(XNegative)(YPositive)(YNegative)(ZPositive)(ZNegative)))\ + ((double, threshold, RetractThreshold, 0.0,\ + "If two wire's end points are separated within this threshold, they are consider\n"\ + "as connected. You may want to set this to the tool diameter to keep the tool down.",\ + App::PropertyLength))\ + ((enum, retract_axis, RetractAxis, 2,"Tool retraction axis",(X)(Y)(Z))) /** Area path generation parameters */ #define AREA_PARAMS_PATH \ AREA_PARAMS_ARC_PLANE \ AREA_PARAMS_SORT \ - ((double, threshold, RetractThreshold, 0.0,\ - "If two wire's end points are separated within this threshold, they are consider\n"\ - "as connected. You may want to set this to the tool diameter to keep the tool down.",\ - App::PropertyLength))\ ((double, retraction, Retraction, 0.0,"Tool retraction absolute coordinate along retraction axis",\ App::PropertyLength))\ - ((enum, retract_axis, RetractAxis, 2,"Tool retraction axis",(X)(Y)(Z)))\ ((double, resume_height, ResumeHeight, 0.0,\ "When return from last retraction, this gives the pause height relative to the Z\n"\ "value of the next move.", App::PropertyLength))\ diff --git a/src/Mod/Path/PathScripts/PathPocketBase.py b/src/Mod/Path/PathScripts/PathPocketBase.py index 4ef7a436e5..386894469f 100644 --- a/src/Mod/Path/PathScripts/PathPocketBase.py +++ b/src/Mod/Path/PathScripts/PathPocketBase.py @@ -118,8 +118,14 @@ class ObjectPocket(PathAreaOp.ObjectOp): # if MinTravel is turned on, set path sorting to 3DSort # 3DSort shouldn't be used without a valid start point. Can cause # tool crash without it. + # # ml: experimental feature, turning off for now (see https://forum.freecadweb.org/viewtopic.php?f=15&t=24422&start=30#p192458) - #if obj.MinTravel and obj.UseStartPoint and obj.StartPoint is not None: - # params['sort_mode'] = 2 + # realthunder: I've fixed it with a new sorting algorithm, which I + # tested fine, but of course need more test. Please let know if there is + # any problem + # + if obj.MinTravel and obj.UseStartPoint and obj.StartPoint is not None: + params['sort_mode'] = 3 + params['threshold'] = self.radius * 2 return params