From 90334e0478f020c1943099760b6965515d840f58 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Tue, 4 Jul 2017 04:59:29 +0800 Subject: [PATCH 1/3] Path.Area: handle empty wires without vertex --- src/Mod/Path/App/Area.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index 9068e1f751..d8375abba0 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -285,6 +285,11 @@ void Area::addWire(CArea &area, const TopoDS_Wire& wire, BRepTools_WireExplorer xp(trsf?TopoDS::Wire( wire.Moved(TopLoc_Location(*trsf))):wire); + if(!xp.More()) { + AREA_TRACE("empty wire"); + return; + } + gp_Pnt p = BRep_Tool::Pnt(xp.CurrentVertex()); ccurve.append(CVertex(Point(p.X(),p.Y()))); From 120b37b0c7a5cfb7d2ea84cfbff1fec37247fa4a Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Tue, 4 Jul 2017 10:37:12 +0800 Subject: [PATCH 2/3] Path.Area: change fromShape() 'start' parameter behavior 'start' used to mean the initial resting position of the tool. Now it is changed to mean the feed start position. fromShape() has also improved to automatically guess 'retraction' and 'resume_height' parameters if not given, based on input shape boundary. --- src/Mod/Path/App/AppPathPy.cpp | 4 +- src/Mod/Path/App/Area.cpp | 140 +++++++++++++++++++------- src/Mod/Path/App/Area.h | 6 +- src/Mod/Path/App/FeaturePathShape.cpp | 2 +- 4 files changed, 111 insertions(+), 41 deletions(-) diff --git a/src/Mod/Path/App/AppPathPy.cpp b/src/Mod/Path/App/AppPathPy.cpp index 2c4832bfa6..a42721d3c5 100644 --- a/src/Mod/Path/App/AppPathPy.cpp +++ b/src/Mod/Path/App/AppPathPy.cpp @@ -122,7 +122,7 @@ public: "fromShapes(shapes, start=Vector(), return_end=False" PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_PATH) ")\n" "\nReturns a Path object from a list of shapes\n" "\n* shapes: input list of shapes.\n" - "\n* start (Vector()): optional start position.\n" + "\n* start (Vector()): feed start position, and also serves as a hint of path entry.\n" "\n* return_end (False): if True, returns tuple (path, endPosition).\n" PARAM_PY_DOC(ARG, AREA_PARAMS_PATH) ); @@ -415,7 +415,7 @@ private: try { bool need_arc_plane = arc_plane==Area::ArcPlaneAuto; std::list wires = Area::sortWires(shapes,&pstart, - &pend, &arc_plane, PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_SORT)); + &pend, 0, &arc_plane, PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_SORT)); PyObject *list = PyList_New(0); for(auto &wire : wires) PyList_Append(list,Py::new_reference_to( diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index d8375abba0..b3f242cfac 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -2435,11 +2435,13 @@ struct ShapeInfo{ return myBestWire->wire; } - std::list sortWires(const gp_Pnt &pstart, gp_Pnt &pend,double min_dist) { + std::list sortWires(const gp_Pnt &pstart, gp_Pnt &pend,double min_dist, gp_Pnt *pentry) { if(pstart.SquareDistance(myStartPt)>Precision::SquareConfusion()) nearest(pstart); + if(pentry) *pentry = myBestPt; + std::list wires; if(min_dist < 0.01) min_dist = 0.01; @@ -2607,7 +2609,7 @@ struct WireOrienter { }; std::list Area::sortWires(const std::list &shapes, - const gp_Pnt *_pstart, gp_Pnt *_pend, short *_parc_plane, + gp_Pnt *_pstart, gp_Pnt *_pend, double *stepdown_hint, short *_parc_plane, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_SORT)) { std::list wires; @@ -2732,7 +2734,12 @@ std::list Area::sortWires(const std::list &shapes, AREA_TRACE("bound (" << xMin<<", "< Area::sortWires(const std::list &shapes, best_d = d; } } - wires.splice(wires.end(),best_it->sortWires(pstart,pend,min_dist)); + gp_Pnt pentry; + wires.splice(wires.end(),best_it->sortWires(pstart,pend,min_dist,&pentry)); + if(use_bound && _pstart) { + use_bound = false; + *_pstart = pentry; + } + if(sort_mode==SortMode2D5 && stepdown_hint) { + if(!best_it->myPlanar) + hint_first = true; + else if(hint_first) + hint_first = false; + else{ + // Calculate distance of two gp_pln. + // + // Can't use gp_pln.Distance(), because it only calculate + // the distance if two plane are parallel. And it checks + // parallelity using tolerance gp::Resolution() which is + // defined as DBL_MIN (min double) in Standard_Real.hxx. + // Really? Is that a bug? + const gp_Pnt& P = pln.Position().Location(); + const gp_Pnt& loc = best_it->myPln.Position().Location (); + const gp_Dir& dir = best_it->myPln.Position().Direction(); + double d = (dir.X() * (P.X() - loc.X()) + + dir.Y() * (P.Y() - loc.Y()) + + dir.Z() * (P.Z() - loc.Z())); + if (d < 0) d = -d; + if(d>hint) + hint = d; + } + pln = best_it->myPln; + } pstart = pend; shape_list.erase(best_it); } + if(stepdown_hint && hint!=0.0) + *stepdown_hint = hint; if(_pend) *_pend = pend; FC_DURATION_LOG(rparams.bd,"rtree build"); FC_DURATION_LOG(rparams.qd,"rtree query"); @@ -2805,24 +2844,22 @@ static void addG0(bool verbose, Toolpath &path, double retraction, double resume_height, double f, double &last_f) { - if(!getter || retraction-(last.*getter)() < Precision::Confusion()) { - addGCode(verbose,path,last,next,"G0"); - return; - } gp_Pnt pt(last); - (pt.*setter)(retraction); - addGCode(verbose,path,last,pt,"G0"); - last = pt; - pt = next; - (pt.*setter)(retraction); - addGCode(verbose,path,last,pt,"G0"); - if(resume_height>Precision::Confusion() && - resume_height+(next.*getter)() < retraction) - { + if(retraction-(last.*getter)() > Precision::Confusion()) { + (pt.*setter)(retraction); + addGCode(verbose,path,last,pt,"G0"); last = pt; pt = next; - (pt.*setter)((next.*getter)()+resume_height); + (pt.*setter)(retraction); addGCode(verbose,path,last,pt,"G0"); + } + if(resume_height>Precision::Confusion()) { + if(resume_height+(next.*getter)() < retraction) { + last = pt; + pt = next; + (pt.*setter)((next.*getter)()+resume_height); + addGCode(verbose,path,last,pt,"G0"); + } addG1(verbose,path,pt,next,f,last_f); }else addGCode(verbose,path,pt,next,"G0"); @@ -2880,11 +2917,15 @@ void Area::setWireOrientation(TopoDS_Wire &wire, const gp_Dir &dir, bool wire_cc } void Area::toPath(Toolpath &path, const std::list &shapes, - const gp_Pnt *pstart, gp_Pnt *pend, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_PATH)) + const gp_Pnt *_pstart, gp_Pnt *pend, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_PATH)) { std::list wires; - wires = sortWires(shapes,pstart,pend, + gp_Pnt pstart; + if(_pstart) pstart = *_pstart; + + double stepdown_hint = 1.0; + wires = sortWires(shapes,&pstart,pend,&stepdown_hint, PARAM_REF(PARAM_FARG,AREA_PARAMS_ARC_PLANE), PARAM_FIELDS(PARAM_FARG,AREA_PARAMS_SORT)); @@ -2903,30 +2944,57 @@ void Area::toPath(Toolpath &path, const std::list &shapes, addGCode(path,"G17"); } + 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; + } + threshold = fabs(threshold); if(threshold < Precision::Confusion()) threshold = Precision::Confusion(); threshold *= threshold; - resume_height = fabs(resume_height); - AxisGetter getter = &gp_Pnt::Z; - AxisSetter setter = &gp_Pnt::SetZ; + resume_height = fabs(resume_height); + if(resume_height < Precision::Confusion()) + resume_height = stepdown_hint; + retraction = fabs(retraction); - if(retraction>Precision::Confusion()) { - 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; - } - } + if(retraction < Precision::Confusion()) + retraction = (pstart.*getter)()+resume_height; + + // in case the user didn't specify feed start, sortWire() will choose one + // based on the bound. We'll further adjust that according to resume height + if(!_pstart || pstart.SquareDistance(*_pstart)>Precision::SquareConfusion()) + (pstart.*setter)((pstart.*getter)()+resume_height); gp_Pnt plast,p; - if(pstart) plast = *pstart; + // initial vertial rapid pull up to retraction (or start Z height if higher) + (p.*setter)(std::max(retraction,(pstart.*getter)())); + addGCode(false,path,plast,p,"G0"); + plast = p; + p = pstart; + // rapid horizontal move if start Z is below retraction + if(fabs((p.*getter)()-retraction) > Precision::Confusion()) { + (p.*setter)(retraction); + addGCode(false,path,plast,p,"G0"); + plast = p; + p = pstart; + } + // vertial rapid down to feed start + addGCode(false,path,plast,p,"G0"); + + plast = p; bool first = true; bool arcWarned = false; double cur_f = 0.0; // current feed rate @@ -2947,7 +3015,7 @@ void Area::toPath(Toolpath &path, const std::list &shapes, (pTmp.*setter)(0.0); (plastTmp.*setter)(0.0); - if(first||pTmp.SquareDistance(plastTmp)>threshold) + if(!first && pTmp.SquareDistance(plastTmp)>threshold) addG0(verbose,path,plast,p,getter,setter,retraction,resume_height,vf,cur_f); else addG1(verbose,path,plast,p,vf,cur_f); diff --git a/src/Mod/Path/App/Area.h b/src/Mod/Path/App/Area.h index e2a88aa296..9d4d10eb01 100644 --- a/src/Mod/Path/App/Area.h +++ b/src/Mod/Path/App/Area.h @@ -341,6 +341,8 @@ public: * \arg \c shapes: input list of shapes. * \arg \c pstart: optional start point * \arg \c pend: optional output containing the ending point of the returned + * \arg \c stepdown_hint: optional output of a hint of step down as the max + * distance between two sections. * \arg \c arc_plane: optional arc plane selection, if given the found plane * will be returned. See #AREA_PARAMS_ARC_PLANE for more details. * @@ -349,8 +351,8 @@ public: * \return sorted wires */ static std::list sortWires(const std::list &shapes, - const gp_Pnt *pstart=NULL, gp_Pnt *pend=NULL, short *arc_plane = NULL, - PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_SORT)); + gp_Pnt *pstart=NULL, gp_Pnt *pend=NULL, double *stepdown_hint=NULL, + short *arc_plane = NULL, PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_SORT)); /** Convert a list of wires to gcode * diff --git a/src/Mod/Path/App/FeaturePathShape.cpp b/src/Mod/Path/App/FeaturePathShape.cpp index 78e720c295..5d2c79ffad 100644 --- a/src/Mod/Path/App/FeaturePathShape.cpp +++ b/src/Mod/Path/App/FeaturePathShape.cpp @@ -55,7 +55,7 @@ PARAM_ENUM_STRING_DECLARE(static const char *Enums,AREA_PARAMS_PATH) FeatureShape::FeatureShape() { ADD_PROPERTY(Sources,(0)); - ADD_PROPERTY_TYPE(StartPoint,(Base::Vector3d()),"Path",App::Prop_None,"Path start position"); + ADD_PROPERTY_TYPE(StartPoint,(Base::Vector3d()),"Path",App::Prop_None,"Feed start position"); PARAM_PROP_ADD("Path",AREA_PARAMS_PATH); PARAM_PROP_SET_ENUM(Enums,AREA_PARAMS_PATH); } From d554497adafb76161d2656d77032de633a82a580 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Wed, 5 Jul 2017 16:36:35 +0800 Subject: [PATCH 3/3] Path.Area: fix sortWires sortWires was broken on open wires. The bug was introduced when open wire direction feature is added. --- src/Mod/Path/App/Area.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index b3f242cfac..104c62b953 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -2065,13 +2065,14 @@ TopoDS_Shape Area::toShape(const CArea &area, bool fill, const gp_Trsf *trsf, in struct WireInfo { TopoDS_Wire wire; std::deque points; + gp_Pnt pt_end; bool isClosed; inline const gp_Pnt &pstart() const{ return points.front(); } inline const gp_Pnt &pend() const{ - return isClosed?pstart():points.back(); + return isClosed?pstart():pt_end; } }; @@ -2161,8 +2162,9 @@ struct GetWires { // We don't add in-between vertices of an open wire, because we // haven't implemented open wire breaking yet. info.points.push_back(p1); - if(params.direction!=Area::DirectionNone) + if(!info.isClosed && params.direction==Area::DirectionNone) info.points.push_back(p2); + info.pt_end = p2; } else { // For closed wires, we are can easily rebase the wire, so we // discretize the wires to spatial index it in order to accelerate @@ -2193,7 +2195,7 @@ struct GetWires { } auto it = wires.end(); --it; - for(size_t i=0,count=info.points.size();ipoints.size();ipstart()); - if(myParams.direction==Area::DirectionNone) { + if(myParams.direction!=Area::DirectionNone) { d = d1; p = it->pstart(); is_start = true; @@ -2437,7 +2439,8 @@ struct ShapeInfo{ std::list sortWires(const gp_Pnt &pstart, gp_Pnt &pend,double min_dist, gp_Pnt *pentry) { - if(pstart.SquareDistance(myStartPt)>Precision::SquareConfusion()) + if(myWires.empty() || + pstart.SquareDistance(myStartPt)>Precision::SquareConfusion()) nearest(pstart); if(pentry) *pentry = myBestPt;