Path.Area: add greedy sort mode
This commit is contained in:
@@ -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<TopoDS_Shape> sortWires(const gp_Pnt &pstart, gp_Pnt &pend,double min_dist, gp_Pnt *pentry) {
|
||||
std::list<TopoDS_Shape> 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<TopoDS_Shape> Area::sortWires(const std::list<TopoDS_Shape> &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<TopoDS_Shape> Area::sortWires(const std::list<TopoDS_Shape> &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<Precision::Confusion()){
|
||||
AREA_WARN("Sort mode 'Greedy' requires a threshold value (suggestion: use the tool diameter)");
|
||||
sort_mode = SortMode2D5;
|
||||
}
|
||||
|
||||
short _arc_plane = ArcPlaneNone;
|
||||
short &arc_plane = _parc_plane?*_parc_plane:_arc_plane;
|
||||
|
||||
@@ -2716,7 +2744,7 @@ std::list<TopoDS_Shape> Area::sortWires(const std::list<TopoDS_Shape> &shapes,
|
||||
fabs(pstart.Y())<Precision::Confusion() &&
|
||||
fabs(pstart.Z())<Precision::Confusion();
|
||||
|
||||
if(use_bound || sort_mode == SortMode2D5) {
|
||||
if(use_bound || sort_mode == SortMode2D5 || sort_mode == SortModeGreedy) {
|
||||
//Second stage, group shape by its plane, and find overall boundary
|
||||
|
||||
if(arcPlaneFound || use_bound) {
|
||||
@@ -2771,6 +2799,9 @@ std::list<TopoDS_Shape> Area::sortWires(const std::list<TopoDS_Shape> &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<TopoDS_Shape> Area::sortWires(const std::list<TopoDS_Shape> &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<TopoDS_Shape> Area::sortWires(const std::list<TopoDS_Shape> &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<TopoDS_Shape> Area::sortWires(const std::list<TopoDS_Shape> &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,
|
||||
|
||||
@@ -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))\
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user