From 1d64d1e91c8ecb4392c6b622a5720150a44cf05a Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Wed, 22 Mar 2017 04:47:38 +0800 Subject: [PATCH] Path.Area: wire sorting fix and improvement Wire sorting no longer uses libarea. Performance improvement using boost::geometry::rtree --- src/Mod/Path/App/AppPathPy.cpp | 38 +- src/Mod/Path/App/Area.cpp | 611 ++++++++++++++++---------- src/Mod/Path/App/Area.h | 43 +- src/Mod/Path/App/AreaParams.h | 32 +- src/Mod/Path/App/AreaPy.xml | 7 - src/Mod/Path/App/AreaPyImp.cpp | 75 +--- src/Mod/Path/App/FeaturePathShape.cpp | 11 +- src/Mod/Path/App/FeaturePathShape.h | 2 +- 8 files changed, 435 insertions(+), 384 deletions(-) diff --git a/src/Mod/Path/App/AppPathPy.cpp b/src/Mod/Path/App/AppPathPy.cpp index dd33b48158..77edd6f389 100644 --- a/src/Mod/Path/App/AppPathPy.cpp +++ b/src/Mod/Path/App/AppPathPy.cpp @@ -119,17 +119,16 @@ public: "fromShape(Shape): Returns a Path object from a Part Shape" ); add_keyword_method("fromShapes",&Module::fromShapes, - "fromShapes(shapes, start=Vector(), " PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_PATH) ", key=value...)\n" + "fromShapes(shapes, start=Vector(), " 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" PARAM_PY_DOC(ARG, AREA_PARAMS_PATH) - "\n* : any key supported by Path.Area, see Path.Area.getParamDesc() for description" ); add_keyword_method("sortWires",&Module::sortWires, "sortWires(shapes, start=Vector(), " PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_ARC_PLANE) - PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_SORT) ", key=value...)\n" + PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_SORT) ")\n" "\nReturns (wires,end), where 'wires' is sorted across Z value and with optimized travel distance,\n" "and 'end' is the ending position of the whole wires. If arc_plane==1, it returns (wires,end,arc_plane),\n" "where arc_plane is the found plane if any, or unchanged.\n" @@ -137,7 +136,6 @@ public: "\n* start (Vector()): optional start position.\n" PARAM_PY_DOC(ARG, AREA_PARAMS_ARC_PLANE) PARAM_PY_DOC(ARG, AREA_PARAMS_SORT) - "\n* : any key supported by Path.Area, see Path.Area.getParamDesc() for description" ); initialize("This module is the Path module."); // register with Python } @@ -325,17 +323,14 @@ private: Py::Object fromShapes(const Py::Tuple& args, const Py::Dict &kwds) { PARAM_PY_DECLARE_INIT(PARAM_FARG,AREA_PARAMS_PATH) - PARAM_PY_DECLARE_INIT(PARAM_FNAME,AREA_PARAMS_CONF) PyObject *pShapes=NULL; PyObject *start=NULL; static char* kwd_list[] = {"shapes", "start", - PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_PATH), - PARAM_FIELD_STRINGS(NAME,AREA_PARAMS_CONF), NULL}; + PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_PATH), NULL}; if (!PyArg_ParseTupleAndKeywords(args.ptr(), kwds.ptr(), - "O|O!" PARAM_PY_KWDS(AREA_PARAMS_PATH) PARAM_PY_KWDS(AREA_PARAMS_CONF), + "O|O!" PARAM_PY_KWDS(AREA_PARAMS_PATH), kwd_list, &pShapes, &(Base::VectorPy::Type), &start, - PARAM_REF(PARAM_FARG,AREA_PARAMS_PATH), - PARAM_REF(PARAM_FNAME,AREA_PARAMS_CONF))) + PARAM_REF(PARAM_FARG,AREA_PARAMS_PATH))) throw Py::Exception(); std::list shapes; @@ -355,12 +350,6 @@ private: } } -#define AREA_GET(_param) \ - params.PARAM_FNAME(_param) = \ - PARAM_TYPED(PARAM_CAST_PY_,_param)(PARAM_FNAME(_param)); - AreaParams params; - PARAM_FOREACH(AREA_GET,AREA_PARAMS_CONF) - gp_Pnt pstart; if(start) { Base::Vector3d vec = static_cast(start)->value(); @@ -369,7 +358,7 @@ private: try { std::unique_ptr path(new Toolpath); - Area::toPath(*path,shapes,¶ms, &pstart, NULL, + Area::toPath(*path,shapes,&pstart, NULL, PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_PATH)); return Py::asObject(new PathPy(path.release())); } PATH_CATCH @@ -379,22 +368,18 @@ private: { PARAM_PY_DECLARE_INIT(PARAM_FARG,AREA_PARAMS_ARC_PLANE) PARAM_PY_DECLARE_INIT(PARAM_FARG,AREA_PARAMS_SORT) - PARAM_PY_DECLARE_INIT(PARAM_FNAME,AREA_PARAMS_CONF) PyObject *pShapes=NULL; PyObject *start=NULL; static char* kwd_list[] = {"shapes", "start", PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_ARC_PLANE), - PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_SORT), - PARAM_FIELD_STRINGS(NAME,AREA_PARAMS_CONF), NULL}; + PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_SORT), NULL}; if (!PyArg_ParseTupleAndKeywords(args.ptr(), kwds.ptr(), "O|O!" PARAM_PY_KWDS(AREA_PARAMS_ARC_PLANE) - PARAM_PY_KWDS(AREA_PARAMS_SORT) - PARAM_PY_KWDS(AREA_PARAMS_CONF), + PARAM_PY_KWDS(AREA_PARAMS_SORT), kwd_list, &pShapes, &(Base::VectorPy::Type), &start, PARAM_REF(PARAM_FARG,AREA_PARAMS_ARC_PLANE), - PARAM_REF(PARAM_FARG,AREA_PARAMS_SORT), - PARAM_REF(PARAM_FNAME,AREA_PARAMS_CONF))) + PARAM_REF(PARAM_FARG,AREA_PARAMS_SORT))) throw Py::Exception(); std::list shapes; @@ -413,9 +398,6 @@ private: } } - AreaParams params; - PARAM_FOREACH(AREA_GET,AREA_PARAMS_CONF) - gp_Pnt pstart,pend; if(start) { Base::Vector3d vec = static_cast(start)->value(); @@ -424,7 +406,7 @@ private: try { bool need_arc_plane = arc_plane==Area::ArcPlaneAuto; - std::list wires = Area::sortWires(shapes,¶ms,&pstart, + std::list wires = Area::sortWires(shapes,&pstart, &pend, &arc_plane, PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_SORT)); PyObject *list = PyList_New(0); for(auto &wire : wires) diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index 199badfa7e..65406e4ab2 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -23,7 +23,11 @@ #ifndef _PreComp_ #endif -#include +#include +#include +#include +#include +#include #include #include @@ -859,60 +863,6 @@ void Area::build() { } } -list Area::sortWires(int index, int count, const gp_Pnt *pstart, - gp_Pnt *_pend, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_SORT)) -{ - std::list wires; - - build(); - - gp_Pnt pend,pt; - if(pstart) pt = *pstart; - - if(mySections.size()) { - if(index>=(int)mySections.size()) - throw Base::ValueError("index out of bound"); - if(index<0) { - index = 0; - count = mySections.size(); - } - if(count<=0 || count>(int)mySections.size()) - count = mySections.size(); - for(int i=index;isortWires(0,0,&pt,&pend, - PARAM_FIELDS(PARAM_FARG,AREA_PARAMS_SORT)); - pt = pend; - } - if(_pend) *_pend = pend; - return std::move(wires); - } - - pt.Transform(TopLoc_Location(myTrsf)); - - if(!myArea || myArea->m_curves.empty()) return wires; - - CArea area(*myArea); - Point p(pt.X(),pt.Y()); - area.ChangeStartToNearest(&p, PARAM_FIELDS(PARAM_FARG,AREA_PARAMS_MIN_DIST)); - gp_Trsf trsf(myTrsf.Inverted()); - for(const CCurve &c : area.m_curves) { - const TopoDS_Wire &wire = toShape(c,&trsf); - if(wire.IsNull()) continue; - wires.push_back(toShape(c,&trsf)); - } - if(_pend) { - gp_Pnt pend = pt; - if(area.m_curves.size() && - area.m_curves.back().m_vertices.size()) - { - const Point &pt = area.m_curves.back().m_vertices.back().m_p; - pend.SetCoord(pt.x,pt.y,0.0); - } - *_pend = pend.Transformed(TopLoc_Location(trsf)); - } - return std::move(wires); -} - TopoDS_Shape Area::toShape(CArea &area, short fill) { gp_Trsf trsf(myTrsf.Inverted()); bool bFill; @@ -1318,61 +1268,168 @@ TopoDS_Shape Area::toShape(const CArea &area, bool fill, const gp_Trsf *trsf) { return TopoDS_Shape(); } +namespace bg = boost::geometry; +namespace bgi = boost::geometry::index; + +BOOST_GEOMETRY_REGISTER_POINT_3D_GET_SET( + gp_Pnt,double,bg::cs::cartesian,X,Y,Z,SetX,SetY,SetZ) + struct WireInfo { TopoDS_Wire wire; - gp_Pnt pend; - gp_Pnt pstart; + std::deque points; + bool isClosed; + + inline const gp_Pnt &pstart() const{ + return points.front(); + } + inline const gp_Pnt &pend() const{ + return isClosed?pstart():points.back(); + } }; +typedef std::list Wires; +typedef std::pair RValue; + +struct RGetter +{ + typedef const gp_Pnt& result_type; + result_type operator()(const RValue &v) const { return v.first->points[v.second]; } +}; +typedef bgi::linear<16> RParameters; +typedef bgi::rtree RTree; + +struct RTreeParams { + double abscissa; + int k; +#ifdef AREA_TIME_ENABLE + TIME_DURATION qd; //rtree query duration + TIME_DURATION bd; //rtree build duration + TIME_DURATION rd; //rtree remove duration + TIME_DURATION xd; //BRepExtrema_DistShapeShape duration +#endif + + RTreeParams(double _a, int _k) + :abscissa(_a),k(_k) +#ifdef AREA_TIME_ENABLE + ,qd(0),bd(0),rd(0),xd(0) +#endif + {} +}; + +bool operator<(const Wires::iterator &a, const Wires::iterator &b) { + return &(*a) < &(*b); +} +typedef std::map RResults; + struct GetWires { - std::list &wires; - GetWires(std::list &ws) - :wires(ws) + Wires &wires; + RTree &rtree; + RTreeParams ¶ms; + GetWires(std::list &ws, RTree &rt, RTreeParams &rp) + :wires(ws),rtree(rt),params(rp) {} void operator()(const TopoDS_Shape &shape, int type) { - WireInfo info; + wires.push_back(WireInfo()); + WireInfo &info = wires.back(); if(type == TopAbs_WIRE) info.wire = TopoDS::Wire(shape); else info.wire = BRepBuilderAPI_MakeWire(TopoDS::Edge(shape)).Wire(); + info.isClosed = BRep_Tool::IsClosed(info.wire); + TIME_INIT(t); BRepTools_WireExplorer xp(info.wire); - info.pstart = BRep_Tool::Pnt(xp.CurrentVertex()); - for(;xp.More();xp.Next()); - info.pend = BRep_Tool::Pnt(xp.CurrentVertex()); - wires.push_back(info); + if(params.abscissa myWires; + Wires myWires; + RTree myRTree; TopoDS_Shape myShape; gp_Pnt myBestPt; - std::list::iterator myBestWire; + gp_Pnt myStartPt; + Wires::iterator myBestWire; TopoDS_Shape mySupport; + RTreeParams &myParams; + Standard_Real myBestParameter; bool mySupportEdge; bool myPlanar; bool myRebase; bool myStart; - ShapeInfo(BRepLib_FindSurface &finder, const TopoDS_Shape &_shape) + ShapeInfo(BRepLib_FindSurface &finder, const TopoDS_Shape &shape, RTreeParams ¶ms) :myPln(GeomAdaptor_Surface(finder.Surface()).Plane()) - ,myShape(_shape) - ,myPlanar(true) + ,myShape(shape),myStartPt(1e20,1e20,1e20),myParams(params),myPlanar(true) {} - ShapeInfo(const TopoDS_Shape &_shape) - :myShape(_shape) - ,myPlanar(false) + + ShapeInfo(const TopoDS_Shape &shape, RTreeParams ¶ms) + :myShape(shape),myStartPt(1e20,1e20,1e20),myParams(params),myPlanar(true) {} double nearest(const gp_Pnt &pt) { + myStartPt = pt; + if(myWires.empty()) - foreachSubshape(myShape,GetWires(myWires),TopAbs_WIRE); + foreachSubshape(myShape,GetWires(myWires,myRTree,myParams),TopAbs_WIRE); + + // Now find the ture nearest point among the wires returned. Currently + // only closed wire has a ture nearest point, using OCC's + // BRepExtrema_DistShapeShape. We don't do this on open wires, becuase + // we haven't implemented wire breaking on open wire yet, and I doubt + // its usefulness. + + RResults ret; + { + TIME_INIT(t); + myRTree.query(bgi::nearest(pt,myParams.k),bgi::inserter(ret)); + DURATION_PLUS(myParams.qd,t); + } + TopoDS_Shape v = BRepBuilderAPI_MakeVertex(pt); bool first = true; double best_d=1e20; myBestWire = myWires.begin(); - for(auto it=myWires.begin();it!=myWires.end();++it) { + for(auto r : ret) { + Wires::iterator it = r.first; const TopoDS_Shape &wire = it->wire; TopoDS_Shape support; bool support_edge; @@ -1381,26 +1438,30 @@ struct ShapeInfo{ bool done = false; bool is_start = false; if(BRep_Tool::IsClosed(wire)) { + TIME_INIT(t); BRepExtrema_DistShapeShape extss(v,wire); if(extss.IsDone() && extss.NbSolution()) { d = extss.Value(); p = extss.PointOnShape2(1); support = extss.SupportOnShape2(1); support_edge = extss.SupportTypeShape2(1)==BRepExtrema_IsOnEdge; + if(support_edge) + extss.ParOnEdgeS2(1,myBestParameter); done = true; }else AREA_WARN("BRepExtrema_DistShapeShape failed"); + DURATION_PLUS(myParams.xd,t); } if(!done){ - double d1 = pt.Distance(it->pstart); - double d2 = pt.Distance(it->pend); + double d1 = pt.Distance(it->pstart()); + double d2 = pt.Distance(it->pend()); if(d1pstart; + p = it->pstart(); is_start = true; }else{ d = d2; - p = it->pend; + p = it->pend(); is_start = false; } } @@ -1422,57 +1483,73 @@ struct ShapeInfo{ //Assumes nearest() has been called. Rebased the best wire //to begin with the best point. Currently only works with closed wire TopoDS_Shape rebaseWire(gp_Pnt &pend, double min_dist) { - AREA_TRACE("rebase wire"); BRepBuilderAPI_MakeWire mkWire; TopoDS_Shape estart; TopoDS_Edge eend; - if(min_dist < Precision::Confusion()) - min_dist = Precision::Confusion(); - for(int state=0;state<3;++state) { BRepTools_WireExplorer xp(TopoDS::Wire(myBestWire->wire)); - pend = BRep_Tool::Pnt(xp.CurrentVertex()); + gp_Pnt pprev(BRep_Tool::Pnt(xp.CurrentVertex())); //checking the case of bestpoint == wire start - if(state==0 && !mySupportEdge && pend.Distance(myBestPt)pend; + if(state==0 && !mySupportEdge && pprev.Distance(myBestPt)pend(); return myBestWire->wire; } gp_Pnt pt; - for(;xp.More();xp.Next(),pend=pt) { - //state==2 means we are in second pass. estart marks the new start of the wire. - //so seeing estart means we're done - if(state==2 && estart.IsEqual(xp.Current())) + for(;xp.More();xp.Next(),pprev=pt) { + const auto &edge = xp.Current(); + + //state==2 means we are in second pass. estart marks the new + //start of the wire. so seeing estart means we're done + if(state==2 && estart.IsEqual(edge)) break; - BRepAdaptor_Curve curve(xp.Current()); - bool reversed = (xp.Current().Orientation()==TopAbs_REVERSED); - pt = curve.Value(reversed?curve.FirstParameter():curve.LastParameter()); - //state!=0 means we've found the new start of wire, now just keep adding new edges + // Edge split not working if using BRepAdaptor_Curve. + // BRepBuilderAPI_MakeEdge always fails with + // PointProjectionFailed. Why?? + + Standard_Real first,last; + const Handle_Geom_Curve & curve = BRep_Tool::Curve(edge, first, last); + pt = curve->Value(last); + bool reversed; + if(pprev.Distance(pt)Value(first); + }else + reversed = false; + + //state!=0 means we've found the new start of wire, now just + //keep adding new edges if(state) { - mkWire.Add(xp.Current()); - pend = pt; + mkWire.Add(edge); continue; } //state==0 means we are looking for the new start if(mySupportEdge) { - //if best point is on some edge, break the edge in half - if(xp.Current().IsEqual(mySupport)) { - double d1 = pend.Distance(myBestPt); + //if best point is on some edge, split the edge in half + if(edge.IsEqual(mySupport)) { + double d1 = pprev.Distance(myBestPt); double d2 = pt.Distance(myBestPt); if(d1>min_dist && d2>min_dist) { BRepBuilderAPI_MakeEdge mkEdge1,mkEdge2; if(reversed) { - mkEdge1.Init(curve.Curve().Curve(), myBestPt, myBestPt); - mkEdge2.Init(curve.Curve().Curve(), pt, myBestPt); + mkEdge1.Init(curve, myBestPt, pprev); + mkEdge2.Init(curve, pt, myBestPt); }else{ - mkEdge1.Init(curve.Curve().Curve(), pend, myBestPt); - mkEdge2.Init(curve.Curve().Curve(), myBestPt, pt); + mkEdge1.Init(curve, pprev, myBestPt); + mkEdge2.Init(curve, myBestPt, pt); } + // Using parameter is not working, why? + // if(reversed) { + // mkEdge1.Init(curve, myBestParameter, last); + // mkEdge2.Init(curve, first, myBestParameter); + // }else{ + // mkEdge1.Init(curve, first, myBestParameter); + // mkEdge2.Init(curve, myBestParameter, last); + // } if(mkEdge1.IsDone() && mkEdge2.IsDone()) { if(reversed) { eend = TopoDS::Edge(mkEdge1.Edge().Reversed()); @@ -1481,63 +1558,80 @@ struct ShapeInfo{ eend = mkEdge1.Edge(); mkWire.Add(mkEdge2.Edge()); } + pend = myBestPt; estart = mySupport; state = 1; - AREA_TRACE("edge broken "<start"); - estart = xp.Current(); + pend = pprev; + // AREA_TRACE("split edge->start"); + estart = edge; state = 1; - mkWire.Add(xp.Current()); + mkWire.Add(edge); }else{ - AREA_TRACE("break edge->end"); + // AREA_TRACE("split edge->end"); mySupportEdge = false; myBestPt = pt; continue; } } - }else if(myBestPt.Distance(pend)pend(); + return myBestWire->wire; + } } if(!eend.IsNull()) mkWire.Add(eend); if(mkWire.IsDone()) return mkWire.Wire(); AREA_WARN("wire rebase failed"); - pend = myBestWire->pend; + pend = myBestWire->pend(); return myBestWire->wire; } - std::list sortWires3D(gp_Pnt &pend,double min_dist) { + std::list sortWires(const gp_Pnt &pstart, gp_Pnt &pend,double min_dist) { + + if(pstart.Distance(myStartPt)>Precision::Confusion()) + nearest(pstart); + std::list wires; + if(min_dist < 0.01) + min_dist = 0.01; while(true) { - AREA_TRACE("3D sort pt " << AREA_XYZ(myBestPt)); if(myRebase) { - AREA_TRACE("3D sort rebase"); pend = myBestPt; wires.push_back(rebaseWire(pend,min_dist)); }else if(!myStart){ - AREA_TRACE("3D sort reverse"); wires.push_back(myBestWire->wire.Reversed()); - pend = myBestWire->pstart; + pend = myBestWire->pstart(); }else{ wires.push_back(myBestWire->wire); - pend = myBestWire->pend; + pend = myBestWire->pend(); } - AREA_TRACE("3D sort end " << AREA_XYZ(pend)); + TIME_INIT(t); + for(size_t i=0,count=myBestWire->points.size();i &list) - :myList(list) ,myTrsf(trsf) ,myArcPlane(arc_plane), myArcPlaneFound(plane_found) + ShapeInfoBuilder(bool &plane_found, short *arc_plane, gp_Trsf &trsf, + std::list &list, RTreeParams ¶ms) + :myList(list) ,myTrsf(trsf) ,myArcPlane(arc_plane) + ,myArcPlaneFound(plane_found), myParams(params) {} void operator()(const TopoDS_Shape &shape, int type) { BRepLib_FindSurface finder(shape,-1,Standard_True); if(!finder.Found()) { - myList.push_back(ShapeInfo(shape)); + myList.push_back(ShapeInfo(shape,myParams)); return; } - myList.push_back(ShapeInfo(finder,shape)); - if(myArcPlane==NULL || *myArcPlane==Area::ArcPlaneNone || myArcPlaneFound) + myList.push_back(ShapeInfo(finder,shape,myParams)); + if(myArcPlane==NULL || myArcPlaneFound || + *myArcPlane==Area::ArcPlaneNone || + *myArcPlane==Area::ArcPlaneVariable) return; if(type == TopAbs_EDGE) { BRepAdaptor_Curve curve(TopoDS::Edge(shape)); if(curve.GetType()!=GeomAbs_Circle) return; }else{ + bool found = false; for(TopExp_Explorer it(shape,TopAbs_EDGE);it.More();it.Next()) { BRepAdaptor_Curve curve(TopoDS::Edge(it.Current())); - if(curve.GetType()==GeomAbs_Circle) goto NEXT; + if(curve.GetType()==GeomAbs_Circle) { + found = true; + break; + } } - return; + if(!found) return; } -NEXT: gp_Ax3 pos = myList.back().myPln.Position(); if(!pos.Direct()) pos = gp_Ax3(pos.Ax2()); const gp_Dir &dir = pos.Direction(); gp_Ax3 dstPos; + bool x0 = fabs(dir.X()) Area::sortWires(const std::list &shapes, - const AreaParams *params, const gp_Pnt *_pstart, gp_Pnt *_pend, - short *arc_plane, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_SORT)) + const gp_Pnt *_pstart, gp_Pnt *_pend, short *arc_plane, + PARAM_ARGS(PARAM_FARG,AREA_PARAMS_SORT)) { std::list wires; @@ -1642,31 +1754,39 @@ std::list Area::sortWires(const std::list &shapes, return std::move(wires); } + RTreeParams rparams(abscissa,nearest_k>0?nearest_k:1); std::list shape_list; TIME_INIT2(t,t1); -#define SORT_WIRE_TIME(_msg) \ - TIME_PRINT(t1,"sortWires "<< _msg) gp_Trsf trsf; bool arcPlaneFound = false; if(sort_mode == SortMode3D) { - for(auto &shape : shapes) - shape_list.push_back(ShapeInfo(shape)); + TopoDS_Builder builder; + TopoDS_Compound comp; + builder.MakeCompound(comp); + for(auto &shape : shapes) { + if(!shape.IsNull()) + builder.Add(comp,shape); + } + TopExp_Explorer xp(comp,TopAbs_EDGE); + if(xp.More()) + shape_list.push_back(ShapeInfo(comp,rparams)); }else{ //first pass, find plane of each shape for(auto &shape : shapes) { //explode the shape - foreachSubshape(shape,ShapeInfoBuilder( - arcPlaneFound,arc_plane,trsf,shape_list)); + if(!shape.IsNull()){ + foreachSubshape(shape,ShapeInfoBuilder( + arcPlaneFound,arc_plane,trsf,shape_list,rparams)); + } } - - if(shape_list.empty()) - return wires; - - SORT_WIRE_TIME("plane finding"); + TIME_PRINT(t1,"plane finding"); } + + if(shape_list.empty()) + return wires; Bnd_Box bounds; gp_Pnt pstart,pend; @@ -1678,14 +1798,20 @@ std::list Area::sortWires(const std::list &shapes, if(use_bound || sort_mode == SortMode2D5) { //Second stage, group shape by its plane, and find overall boundary + + if(arcPlaneFound || use_bound) { + for(auto &info : shape_list) { + if(arcPlaneFound) { + info.myShape.Move(trsf); + if(info.myPlanar) info.myPln.Transform(trsf); + } + if(use_bound) + BRepBndLib::Add(info.myShape, bounds, Standard_False); + } + } + for(auto itNext=shape_list.begin(),it=itNext;it!=shape_list.end();it=itNext) { ++itNext; - if(arcPlaneFound) { - it->myShape.Move(trsf); - if(it->myPlanar) it->myPln.Transform(trsf); - } - if(use_bound) - BRepBndLib::Add(it->myShape, bounds, Standard_False); if(!it->myPlanar) continue; TopoDS_Builder builder; TopoDS_Compound comp; @@ -1707,70 +1833,46 @@ std::list Area::sortWires(const std::list &shapes, it->myShape = comp; } } - SORT_WIRE_TIME("plane merging"); + TIME_PRINT(t,"plane merging"); } - Area area(params); - //We have done planar checking here, so disable Area planar check - area.myParams.Coplanar = Area::CoplanarNone; - - DURATION_INIT2(td1,td2); + DURATION_INIT(td); if(use_bound) { bounds.SetGap(0.0); Standard_Real xMin, yMin, zMin, xMax, yMax, zMax; bounds.Get(xMin, yMin, zMin, xMax, yMax, zMax); + AREA_TRACE("bound (" << xMin<<", "<myPlanar){ + if(it->myPlanar) d = it->myPln.Distance(pstart); -#define AREA_TIME_2D5 \ - DURATION_PLUS(td1,t1);\ - has_2d5=true - - AREA_TIME_2D5; - }else{ + else d = it->nearest(pstart); -#define AREA_TIME_3D \ - DURATION_PLUS(td2,t1);\ - has_3d=true - - AREA_TIME_3D; - } if(first || dmyPlanar) { - area.clean(true); - area.myWorkPlane = best_it->myShape; - area.myTrsf.SetTransformation(best_it->myPln.Position()); - area.add(best_it->myShape,Area::OperationCompound); - wires.splice(wires.end(),area.sortWires( - 0,-1,&pstart,&pend, PARAM_FIELDS(PARAM_FARG,AREA_PARAMS_SORT))); - AREA_TIME_2D5; - }else{ - wires.splice(wires.end(),best_it->sortWires3D(pend,min_dist)); - AREA_TIME_3D; - } + wires.splice(wires.end(),best_it->sortWires(pstart,pend,min_dist)); pstart = pend; shape_list.erase(best_it); } if(_pend) *_pend = pend; - - if(has_2d5) DURATION_PRINT(td1,"sortWires 2D5"); - if(has_3d) DURATION_PRINT(td2,"sortWires 3D"); + DURATION_PRINT(rparams.bd,"rtree build"); + DURATION_PRINT(rparams.qd,"rtree query"); + DURATION_PRINT(rparams.rd,"rtree clean"); + DURATION_PRINT(rparams.xd,"BRepExtrema"); TIME_PRINT(t,"sortWires total"); return std::move(wires); } @@ -1847,24 +1949,24 @@ static inline void addCommand(Toolpath &path, const char *name) { } void Area::toPath(Toolpath &path, const std::list &shapes, - const AreaParams *_params, 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; - AreaParams params; - if(_params) params =*_params; - wires = sortWires(shapes,¶ms,pstart,pend, + wires = sortWires(shapes,pstart,pend, PARAM_REF(PARAM_FARG,AREA_PARAMS_ARC_PLANE), PARAM_FIELDS(PARAM_FARG,AREA_PARAMS_SORT)); // absolute mode addCommand(path,"G90"); + short currentArcPlane = arc_plane; if(arc_plane==ArcPlaneZX) addCommand(path,"G18"); else if(arc_plane==ArcPlaneYZ) addCommand(path,"G19"); + else + currentArcPlane=ArcPlaneXY; threshold = fabs(threshold); if(threshold < Precision::Confusion()) @@ -1894,6 +1996,7 @@ void Area::toPath(Toolpath &path, const std::list &shapes, gp_Pnt plast,p; if(pstart) plast = *pstart; bool first = true; + bool arcWarned = false; for(const TopoDS_Shape &wire : wires) { BRepTools_WireExplorer xp(TopoDS::Wire(wire)); p = BRep_Tool::Pnt(xp.CurrentVertex()); @@ -1904,8 +2007,9 @@ void Area::toPath(Toolpath &path, const std::list &shapes, plast = p; first = false; for(;xp.More();xp.Next(),plast=p) { - BRepAdaptor_Curve curve(xp.Current()); - bool reversed = (xp.Current().Orientation()==TopAbs_REVERSED); + const auto &edge = xp.Current(); + BRepAdaptor_Curve curve(edge); + bool reversed = (edge.Orientation()==TopAbs_REVERSED); p = curve.Value(reversed?curve.FirstParameter():curve.LastParameter()); switch (curve.GetType()) { @@ -1934,49 +2038,80 @@ void Area::toPath(Toolpath &path, const std::list &shapes, addCommand(path,plast,p); break; } case GeomAbs_Circle:{ - double first = curve.FirstParameter(); - double last = curve.LastParameter(); const gp_Circ &circle = curve.Circle(); - // Get the normal vector after trasform the circle to the XY0 - // plane, in order to judge if the circle is facing upward or - // downward - const gp_Ax1 &axis = circle.Axis(); - const gp_Dir &dir = axis.Direction().Transformed(curve.Trsf().Inverted()); - bool clockwise = dir.Z()<0; - if(reversed) clockwise = !clockwise; - gp_Pnt center = circle.Location(); - if(segmentation > Precision::Confusion()) { - GCPnts_UniformAbscissa discretizer(curve, segmentation, - curve.FirstParameter(), curve.LastParameter()); - if (discretizer.IsDone () && discretizer.NbPoints () > 2) { - int nbPoints = discretizer.NbPoints (); - if(reversed) { - for (int i=nbPoints-1; i>=1; --i) { - gp_Pnt pt = curve.Value(discretizer.Parameter(i)); - addCommand(path,plast,pt,center,clockwise); - plast = pt; - } - }else{ - for (int i=2; i<=nbPoints; i++) { - gp_Pnt pt = curve.Value(discretizer.Parameter(i)); - addCommand(path,plast,pt,center,clockwise); - plast = pt; + const gp_Dir &dir = circle.Axis().Direction(); + short arcPlane = ArcPlaneNone; + bool clockwise; + const char *cmd; + if(fabs(dir.X()) Precision::Confusion()) { + GCPnts_UniformAbscissa discretizer(curve,segmentation,first,last); + if (discretizer.IsDone () && discretizer.NbPoints () > 2) { + int nbPoints = discretizer.NbPoints (); + if(reversed) { + for (int i=nbPoints-1; i>=1; --i) { + gp_Pnt pt = curve.Value(discretizer.Parameter(i)); + addCommand(path,plast,pt,center,clockwise); + plast = pt; + } + }else{ + for (int i=2; i<=nbPoints; i++) { + gp_Pnt pt = curve.Value(discretizer.Parameter(i)); + addCommand(path,plast,pt,center,clockwise); + plast = pt; + } } + break; } - break; } + + if(fabs(first-last)>M_PI) { + // Split arc(circle) larger than half circle. + gp_Pnt mid = curve.Value((last-first)*0.5+first); + addCommand(path,plast,mid,center,clockwise); + plast = mid; + } + addCommand(path,plast,p,center,clockwise); + break; } - if(fabs(first-last)>M_PI) { - // Split arc(circle) larger than half circle. - gp_Pnt mid = curve.Value((last-first)*0.5+first); - addCommand(path,plast,mid,center,clockwise); - plast = mid; + + if(!arcWarned){ + arcWarned = true; + AREA_WARN("arc plane not aligned, force discretization"); } - addCommand(path,plast,p,center,clockwise); - break; + AREA_TRACE("arc discretize " << AREA_XYZ(dir)); + //fall through + } default: { // Discretize all other type of curves - GCPnts_QuasiUniformDeflection discretizer(curve, params.Deflection, + GCPnts_QuasiUniformDeflection discretizer(curve, deflection, curve.FirstParameter(), curve.LastParameter()); if (discretizer.IsDone () && discretizer.NbPoints () > 1) { int nbPoints = discretizer.NbPoints (); diff --git a/src/Mod/Path/App/Area.h b/src/Mod/Path/App/Area.h index cfc89c6f16..3bb9670cb1 100644 --- a/src/Mod/Path/App/Area.h +++ b/src/Mod/Path/App/Area.h @@ -45,9 +45,9 @@ extern PartExport Py::Object shape2pyshape(const TopoDS_Shape &shape); #define _AREA_LOG(_l,_msg) do {\ if(Area::_l##Enabled()){\ std::stringstream str;\ - str << "Path.Area: " << _msg;\ + str << _msg;\ const char *_f = strrchr(__FILE__, '/');\ - Base::Console()._l("%s:(%d) - %s\n",_f?_f+1:__FILE__,__LINE__,str.str().c_str());\ + Base::Console()._l("%s:(%d): %s\n",_f?_f+1:__FILE__,__LINE__,str.str().c_str());\ }\ QCoreApplication::sendPostedEvents();\ if(Area::aborting()) {\ @@ -74,10 +74,10 @@ extern PartExport Py::Object shape2pyshape(const TopoDS_Shape &shape); #define TIME_UNIT duration #define TIME_CLOCK high_resolution_clock #define TIME_POINT std::chrono::TIME_CLOCK::time_point +#define TIME_DURATION std::chrono::TIME_UNIT -#define TIME_INIT(_t) \ - auto _t=std::chrono::TIME_CLOCK::now() - +#define _TIME_INIT(_t) _t=std::chrono::TIME_CLOCK::now() +#define TIME_INIT(_t) TIME_POINT _TIME_INIT(_t) #define TIME_INIT2(_t1,_t2) TIME_INIT(_t1),_t2=_t1 #define TIME_INIT3(_t1,_t2,_t3) TIME_INIT(_t1),_t2=_t1,_t3=_t1 @@ -93,7 +93,7 @@ extern PartExport Py::Object shape2pyshape(const TopoDS_Shape &shape); _DURATION_PRINT(TRACE,Path::getDuration(_t),_msg); #define DURATION_INIT(_d) \ - std::chrono::TIME_UNIT _d(0) + TIME_DURATION _d(0) #define DURATION_INIT2(_d1,_d2) DURATION_INIT(_d1),_d2(0) @@ -110,7 +110,8 @@ inline std::chrono::TIME_UNIT getDuration(TIME_POINT &t) #define DURATION_PLUS(_d,_t) _d += Path::getDuration(_t) #else - +#define TIME_POINT +#define _TIME_INIT(...) do{}while(0) #define TIME_INIT(...) do{}while(0) #define TIME_INIT2(...) do{}while(0) #define TIME_INIT3(...) do{}while(0) @@ -368,25 +369,6 @@ public: static void addWire(CArea &area, const TopoDS_Wire &wire, const gp_Trsf *trsf=NULL, double deflection=0.01, bool to_edges=false); - /** Output a list or sorted wire with minimize traval distance - * - * \arg \c index: index of the section, -1 for all sections. No effect on - * non-sectioned area. - * \arg \c count: number of the sections to return, <=0 for all sections - * after \c index. No effect on non-sectioned area. - * \arg \c pstart: optional start point - * \arg \c pend: optional output containing the ending point of the returned - * wires - * \arg \c allow_Break: whether allow to break open wires - * - * See #AREA_PARAMS_SORT for other arguments - * - * \return sorted wires - * */ - std::list sortWires(int index=-1, int count=0, - const gp_Pnt *pstart=NULL, gp_Pnt *pend=NULL, - PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_SORT)); - /** Add a OCC generic shape to CArea * * \arg \c area: output converted curved object to here @@ -436,8 +418,6 @@ public: * minimize traval distance * * \arg \c shapes: input list of shapes. - * \arg \c params: optional Area parameters for the Area object internally - * used for sorting * \arg \c pstart: optional start point * \arg \c pend: optional output containing the ending point of the returned * \arg \c arc_plane: optional arc plane selection, if given the found plane @@ -448,23 +428,20 @@ public: * \return sorted wires */ static std::list sortWires(const std::list &shapes, - const AreaParams *params = NULL, const gp_Pnt *pstart=NULL, - gp_Pnt *pend=NULL, short *arc_plane = NULL, + const gp_Pnt *pstart=NULL, gp_Pnt *pend=NULL, short *arc_plane = NULL, PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_SORT)); /** Convert a list of wires to gcode * * \arg \c path: output toolpath * \arg \c shapes: input list of shapes - * \arg \c params: optional Area parameters for the Area object internally - * used for sorting * \arg \c pstart: output start point, * \arg \c pend: optional output containing the ending point of the returned * * See #AREA_PARAMS_PATH for other arguments */ static void toPath(Toolpath &path, const std::list &shapes, - const AreaParams *params=NULL, const gp_Pnt *pstart=NULL, gp_Pnt *pend=NULL, + const gp_Pnt *pstart=NULL, gp_Pnt *pend=NULL, PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_PATH)); PARAM_ENUM_DECLARE(AREA_PARAMS_PATH) diff --git a/src/Mod/Path/App/AreaParams.h b/src/Mod/Path/App/AreaParams.h index 3e5fb87b99..93d92777e0 100644 --- a/src/Mod/Path/App/AreaParams.h +++ b/src/Mod/Path/App/AreaParams.h @@ -179,8 +179,10 @@ "If not 'None', the output wires will be transformed to align with the selected plane,\n"\ "and the corresponding GCode will be inserted.\n"\ "'Auto' means the plane is determined by the first encountered arc plane. If the found\n"\ - "plane does not align to any GCode plane, XY plane is used.",\ - (None)(Auto)(XY)(ZX)(YZ))) + "plane does not align to any GCode plane, XY plane is used.\n"\ + "'Variable' means the arc plane can be changed during operation to align to the the\n"\ + "arc encountered.",\ + (None)(Auto)(XY)(ZX)(YZ)(Variable))) /** Area wire sorting parameters */ #define AREA_PARAMS_SORT \ @@ -190,7 +192,11 @@ "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)))\ - AREA_PARAMS_MIN_DIST + 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 path generation parameters */ #define AREA_PARAMS_PATH \ @@ -208,15 +214,8 @@ "value of the next move",App::PropertyLength))\ ((double,segmentation,Segmentation,0.0,\ "Break long curves into segments of this length. One use case is for PCB autolevel,\n"\ - "so that more correction points can be inserted",App::PropertyLength)) - -#define AREA_PARAMS_PATH_EXTRA \ - AREA_PARAMS_DEFLECTION \ - AREA_PARAMS_FIT_ARCS - -#define AREA_PARAMS_PATH_CONF \ - AREA_PARAMS_PATH \ - AREA_PARAMS_PATH_EXTRA + "so that more correction points can be inserted",App::PropertyLength)) \ + AREA_PARAMS_DEFLECTION /** Group of all Area configuration parameters except CArea's*/ #define AREA_PARAMS_AREA \ @@ -238,8 +237,13 @@ AREA_PARAMS_OPCODE #define AREA_PARAM_LOG_LEVEL (Error)(Warning)(Log)(Trace) -#define AREA_PARAMS_LOG_LEVEL \ - ((enum, log_level, LogLevel, 1, "Area log level", AREA_PARAM_LOG_LEVEL)) +#if FC_DEBUG +# define AREA_PARAMS_LOG_LEVEL \ + ((enum, log_level, LogLevel, 3, "Area log level", AREA_PARAM_LOG_LEVEL)) +#else +# define AREA_PARAMS_LOG_LEVEL \ + ((enum, log_level, LogLevel, 1, "Area log level", AREA_PARAM_LOG_LEVEL)) +#endif #define AREA_PARAMS_EXTRA_CONF \ AREA_PARAMS_LOG_LEVEL diff --git a/src/Mod/Path/App/AreaPy.xml b/src/Mod/Path/App/AreaPy.xml index 29748b4185..d9737e0a88 100644 --- a/src/Mod/Path/App/AreaPy.xml +++ b/src/Mod/Path/App/AreaPy.xml @@ -71,13 +71,6 @@ same algorithm - - getParamsDesc(as_string=True): Returns a list of supported parameters and their descriptions.\n -* as_string: if False, then return a dictionary of documents of all supported parameters. - - - - diff --git a/src/Mod/Path/App/AreaPyImp.cpp b/src/Mod/Path/App/AreaPyImp.cpp index a648a6925f..971b429007 100644 --- a/src/Mod/Path/App/AreaPyImp.cpp +++ b/src/Mod/Path/App/AreaPyImp.cpp @@ -84,6 +84,19 @@ static PyObject* areaGetParams(PyObject *, PyObject *args) { return dict; } +static PyObject * areaGetParamsDesc(PyObject *, PyObject *args, PyObject *kwd) { + PyObject *pcObj = Py_False; + static char *kwlist[] = {"as_string", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwd, "|O",kwlist,&pcObj)) + return 0; + + if(PyObject_IsTrue(pcObj)) + return PyString_FromString(PARAM_PY_DOC(NAME,AREA_PARAMS_STATIC_CONF)); + + PyObject *dict = PyDict_New(); + PARAM_PY_DICT_SET_DOC(dict,NAME,AREA_PARAMS_STATIC_CONF) + return dict; +} static const PyMethodDef areaOverrides[] = { { @@ -128,16 +141,6 @@ static const PyMethodDef areaOverrides[] = { "\n* plane (None): optional shape to specify a section plane. If not give, the current workplane\n" "of this Area is used if section mode is 'Workplane'.", }, - { - "sortWires",NULL,0, - "sortWires(index=-1, count=0, start=Vector(), allow_break=False, " PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_SORT) "):\n" - "Returns a tuple (wires,end): sorted wires with minimized travel distance, and the endpoint\n" - "of the wires.\n" - "\n* index (-1): the index of the section. -1 means all sections. No effect on planar shape.\n" - "\n* count (0): the number of sections to return. <=0 means all sections starting from index.\n" - "\n* start (Vector()): a vector specifies the start point.\n" - PARAM_PY_DOC(ARG,AREA_PARAMS_SORT), - }, { "setDefaultParams",(PyCFunction)areaSetParams, METH_VARARGS|METH_KEYWORDS|METH_STATIC, "setDefaultParams(" PARAM_PY_ARGS_DOC(NAME,AREA_PARAMS_EXTRA_CONF) ", key=value...):\n" @@ -155,6 +158,11 @@ static const PyMethodDef areaOverrides[] = { "\nTo ensure no stray abortion is left in the previous operaion, it is advised to manually clear\n" "the aborting flag by calling abort(False) before starting a new operation.", }, + { + "getParamsDesc",(PyCFunction)areaGetParamsDesc, METH_VARARGS|METH_KEYWORDS|METH_STATIC, + "getParamsDesc(as_string=False): Returns a list of supported parameters and their descriptions.\n" + "\n* as_string: if False, then return a dictionary of documents of all supported parameters." + }, }; struct AreaPyModifier { @@ -226,39 +234,6 @@ PyObject* AreaPy::getShape(PyObject *args, PyObject *keywds) return Py::new_reference_to(Part::shape2pyshape(getAreaPtr()->getShape(index))); } -PyObject* AreaPy::sortWires(PyObject *args, PyObject *keywds){ - PARAM_PY_DECLARE_INIT(PARAM_FARG,AREA_PARAMS_SORT) - short index = -1; - short count = 0; - PyObject *start = NULL; - - static char *kwlist[] = {"index","count","start", - PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_SORT), NULL}; - if (!PyArg_ParseTupleAndKeywords(args, keywds, - "|hhO!" PARAM_PY_KWDS(AREA_PARAMS_SORT), - kwlist,&index,&count,&(Base::VectorPy::Type),&start, - PARAM_REF(PARAM_FARG,AREA_PARAMS_SORT))) - return 0; - - gp_Pnt pstart,pend; - if(start) { - Base::Vector3d vec = static_cast(start)->value(); - pstart.SetCoord(vec.x, vec.y, vec.z); - } - std::list wires = getAreaPtr()->sortWires( - index,count,&pstart,&pend, - PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_SORT)); - PyObject *list = PyList_New(0); - for(auto &wire : wires) - PyList_Append(list,Py::new_reference_to( - Part::shape2pyshape(TopoDS::Wire(wire)))); - PyObject *ret = PyTuple_New(2); - PyTuple_SetItem(ret,0,list); - PyTuple_SetItem(ret,1,new Base::VectorPy( - Base::Vector3d(pend.X(),pend.Y(),pend.Z()))); - return ret; -} - PyObject* AreaPy::add(PyObject *args, PyObject *keywds) { PARAM_PY_DECLARE_INIT(PARAM_FARG,AREA_PARAMS_OPCODE) @@ -441,19 +416,9 @@ PyObject* AreaPy::abort(PyObject *, PyObject *) { return 0; } -PyObject* AreaPy::getParamsDesc(PyObject *args, PyObject *keywds) +PyObject* AreaPy::getParamsDesc(PyObject *, PyObject *) { - PyObject *pcObj = Py_True; - static char *kwlist[] = {"as_string", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, keywds,"|O",kwlist,&pcObj)) - return 0; - - if(PyObject_IsTrue(pcObj)) - return PyString_FromString(PARAM_PY_DOC(NAME,AREA_PARAMS_CONF)); - - PyObject *dict = PyDict_New(); - PARAM_PY_DICT_SET_DOC(dict,NAME,AREA_PARAMS_CONF) - return dict; + return 0; } Py::List AreaPy::getSections(void) const { diff --git a/src/Mod/Path/App/FeaturePathShape.cpp b/src/Mod/Path/App/FeaturePathShape.cpp index 300b8739d1..78e720c295 100644 --- a/src/Mod/Path/App/FeaturePathShape.cpp +++ b/src/Mod/Path/App/FeaturePathShape.cpp @@ -56,8 +56,8 @@ FeatureShape::FeatureShape() { ADD_PROPERTY(Sources,(0)); ADD_PROPERTY_TYPE(StartPoint,(Base::Vector3d()),"Path",App::Prop_None,"Path start position"); - PARAM_PROP_ADD("Path",AREA_PARAMS_PATH_CONF); - PARAM_PROP_SET_ENUM(Enums,AREA_PARAMS_PATH_CONF); + PARAM_PROP_ADD("Path",AREA_PARAMS_PATH); + PARAM_PROP_SET_ENUM(Enums,AREA_PARAMS_PATH); } FeatureShape::~FeatureShape() @@ -86,12 +86,7 @@ App::DocumentObjectExecReturn *FeatureShape::execute(void) shapes.push_back(shape); } - AreaParams params; -#define AREA_PROP_GET(_param) \ - params.PARAM_FNAME(_param) = PARAM_FNAME(_param).getValue(); - PARAM_FOREACH(AREA_PROP_GET,AREA_PARAMS_PATH_EXTRA) - - Area::toPath(path,shapes,¶ms,&pstart,NULL,PARAM_PROP_ARGS(AREA_PARAMS_PATH)); + Area::toPath(path,shapes,&pstart,NULL,PARAM_PROP_ARGS(AREA_PARAMS_PATH)); Path.setValue(path); return App::DocumentObject::StdReturn; diff --git a/src/Mod/Path/App/FeaturePathShape.h b/src/Mod/Path/App/FeaturePathShape.h index 5caba91400..ff63aea1db 100644 --- a/src/Mod/Path/App/FeaturePathShape.h +++ b/src/Mod/Path/App/FeaturePathShape.h @@ -53,7 +53,7 @@ public: // Part::PropertyPartShape Shape; App::PropertyLinkList Sources; App::PropertyVector StartPoint; - PARAM_PROP_DECLARE(AREA_PARAMS_PATH_CONF) + PARAM_PROP_DECLARE(AREA_PARAMS_PATH) //@{ /// recalculate the feature