diff --git a/src/Mod/Path/App/AppPathPy.cpp b/src/Mod/Path/App/AppPathPy.cpp index 36371b451144..f7b53cde1b0f 100644 --- a/src/Mod/Path/App/AppPathPy.cpp +++ b/src/Mod/Path/App/AppPathPy.cpp @@ -126,13 +126,13 @@ class Module : public Py::ExtensionModule PARAM_PY_DOC(ARG, AREA_PARAMS_PATH) ); add_keyword_method("sortWires",&Module::sortWires, - "sortWires(shapes, start=Vector(), params=None, " PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_SORT_WIRES) ")\n" + "sortWires(shapes, start=Vector(), params=None, " PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_SORT) ")\n" "\nReturns (wires,end), where 'wires' is sorted accross Z value and with optimized travel distance,\n" "and 'end' is the ending position of the whole wires\n" "\n* shapes: input shape list\n" "\n* start (Vector()): optional start position.\n" "\n* params (None): optional dictionary for configuring Path.Area internally used to sort the wires.\n" - PARAM_PY_DOC(ARG, AREA_PARAMS_SORT_WIRES) + PARAM_PY_DOC(ARG, AREA_PARAMS_SORT) ); initialize("This module is the Path module."); // register with Python } @@ -320,14 +320,17 @@ class Module : public Py::ExtensionModule 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), NULL}; + PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_PATH), + PARAM_FIELD_STRINGS(NAME,AREA_PARAMS_CONF), NULL}; if (!PyArg_ParseTupleAndKeywords(args.ptr(), kwds.ptr(), "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_FARG,AREA_PARAMS_PATH), + PARAM_REF(PARAM_FNAME,AREA_PARAMS_CONF))) throw Py::Exception(); std::list shapes; @@ -347,6 +350,12 @@ class Module : public Py::ExtensionModule } } +#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(); @@ -355,24 +364,26 @@ class Module : public Py::ExtensionModule try { std::unique_ptr path(new Toolpath); - Area::toPath(*path,shapes,&pstart,PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_PATH)); + Area::toPath(*path,shapes,¶ms, &pstart, NULL, + PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_PATH)); return Py::asObject(new PathPy(path.release())); } PATH_CATCH } Py::Object sortWires(const Py::Tuple& args, const Py::Dict &kwds) { - AreaParams params; - PARAM_PY_DECLARE_INIT(PARAM_FARG,AREA_PARAMS_SORT_WIRES) + PARAM_PY_DECLARE_INIT(PARAM_FARG,AREA_PARAMS_SORT) + PARAM_PY_DECLARE_INIT(PARAM_FNAME,AREA_PARAMS_CONF) PyObject *pShapes=NULL; PyObject *start=NULL; - PyObject *pParams=NULL; - static char* kwd_list[] = {"shapes", "start", "params", - PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_SORT_WIRES), NULL}; + static char* kwd_list[] = {"shapes", "start", + PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_SORT), + PARAM_FIELD_STRINGS(NAME,AREA_PARAMS_CONF), NULL}; if (!PyArg_ParseTupleAndKeywords(args.ptr(), kwds.ptr(), - "O|O!O!" PARAM_PY_KWDS(AREA_PARAMS_SORT_WIRES), kwd_list, - &pShapes, &(Base::VectorPy::Type), &start, &PyDict_Type, &pParams, - PARAM_REF(PARAM_FARG,AREA_PARAMS_SORT_WIRES))) + "O|O!" PARAM_PY_KWDS(AREA_PARAMS_SORT), kwd_list, + &pShapes, &(Base::VectorPy::Type), &start, + PARAM_REF(PARAM_FARG,AREA_PARAMS_SORT), + PARAM_REF(PARAM_FNAME,AREA_PARAMS_CONF))) throw Py::Exception(); std::list shapes; @@ -391,23 +402,8 @@ class Module : public Py::ExtensionModule } } - if(pParams) { - static char *kwlist[] = {PARAM_FIELD_STRINGS(NAME,AREA_PARAMS_CONF),NULL}; - PARAM_PY_DECLARE(PARAM_FNAME,AREA_PARAMS_CONF); -#define AREA_SET(_param) \ - PARAM_FNAME(_param) = \ - PARAM_TYPED(PARAM_PY_CAST_,_param)(params.PARAM_FNAME(_param)); - PARAM_FOREACH(AREA_SET,AREA_PARAMS_CONF) - if (!PyArg_ParseTupleAndKeywords(NULL, pParams, - "|" PARAM_PY_KWDS(AREA_PARAMS_CONF), kwlist, - PARAM_REF(PARAM_FNAME,AREA_PARAMS_CONF))) - throw Py::Exception(); - -#define AREA_GET(_param) \ - params.PARAM_FNAME(_param) = \ - PARAM_TYPED(PARAM_CAST_PY_,_param)(PARAM_FNAME(_param)); - PARAM_FOREACH(AREA_GET,AREA_PARAMS_CONF) - } + AreaParams params; + PARAM_FOREACH(AREA_GET,AREA_PARAMS_CONF) gp_Pnt pstart,pend; if(start) { @@ -417,7 +413,7 @@ class Module : public Py::ExtensionModule try { std::list wires = Area::sortWires(shapes,¶ms,&pstart, - &pend, PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_SORT_WIRES)); + &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( diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index b2f8bd92c0a0..07bcda2a775a 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -23,8 +23,6 @@ #ifndef _PreComp_ #endif - -#include "boost/date_time/posix_time/posix_time.hpp" #include #include @@ -54,14 +52,16 @@ #include #include #include +#include #include #include #include #include +#include +#include #include #include -#include #include #include @@ -73,7 +73,6 @@ #include "../libarea/Area.h" using namespace Path; -using namespace boost::posix_time; CAreaParams::CAreaParams() :PARAM_INIT(PARAM_FNAME,AREA_PARAMS_CAREA) @@ -310,7 +309,7 @@ void Area::add(CArea &area, const TopoDS_Wire& wire, } if(!to_edges) { if(BRep_Tool::IsClosed(wire) && !ccurve.IsClosed()) { - Base::Console().Warning("ccurve not closed\n"); + AREA_WARN("ccurve not closed"); ccurve.append(ccurve.m_vertices.front()); } area.append(ccurve); @@ -403,7 +402,7 @@ void Area::addToBuild(CArea &area, const TopoDS_Shape &shape) { if(&area == myArea.get() || myParams.OpenMode == OpenModeNone) myAreaOpen->m_curves.splice(myAreaOpen->m_curves.end(),areaOpen.m_curves); else - Base::Console().Warning("open wires discarded in clipping shapes\n"); + AREA_WARN("open wires discarded in clipping shapes"); } } @@ -452,70 +451,92 @@ static void show(const TopoDS_Shape &shape, const char *name) { } #endif -TopoDS_Shape Area::findPlane(const TopoDS_Shape &shape, gp_Trsf &trsf) -{ - TopoDS_Shape plane; - double top_z; - if(!findPlane(shape,TopAbs_FACE,plane,trsf,top_z) && - !findPlane(shape,TopAbs_WIRE,plane,trsf,top_z)) - findPlane(shape,TopAbs_EDGE,plane,trsf,top_z); - return plane; +template +static int foreachSubshape(const TopoDS_Shape &shape, Func func, int type=TopAbs_FACE) { + bool haveShape = false; + switch(type) { + case TopAbs_FACE: + for(TopExp_Explorer it(shape,TopAbs_FACE); it.More(); it.Next()) { + haveShape = true; + func(it.Current(),TopAbs_FACE); + } + if(haveShape) return TopAbs_FACE; + //fall through + case TopAbs_WIRE: + for(TopExp_Explorer it(shape,TopAbs_WIRE); it.More(); it.Next()) { + haveShape = true; + func(it.Current(),TopAbs_WIRE); + } + if(haveShape) return TopAbs_WIRE; + //fall through + default: + for(TopExp_Explorer it(shape,TopAbs_EDGE); it.More(); it.Next()) { + haveShape = true; + func(it.Current(),TopAbs_EDGE); + } + } + return haveShape?TopAbs_EDGE:-1; } -bool Area::findPlane(const TopoDS_Shape &shape, int type, - TopoDS_Shape &dst, gp_Trsf &dst_trsf, double &top_z) -{ - bool haveShape = false; - bool top_found = !dst.IsNull(); - gp_Trsf trsf; - for(TopExp_Explorer it(shape,(TopAbs_ShapeEnum)type); it.More(); it.Next()) { - haveShape = true; - const TopoDS_Shape &plane = it.Current(); - BRepLib_FindSurface planeFinder(plane,-1,Standard_True); - if (!planeFinder.Found()) - continue; - gp_Ax3 pos = GeomAdaptor_Surface(planeFinder.Surface()).Plane().Position(); +struct FindPlane { + TopoDS_Shape &myShape; + gp_Trsf &myTrsf; + double &myZ; + FindPlane(TopoDS_Shape &s, gp_Trsf &t, double &z) + :myShape(s),myTrsf(t),myZ(z) + {} + void operator()(const TopoDS_Shape &shape, int) { + gp_Trsf trsf; + BRepLib_FindSurface finder(shape,-1,Standard_True); + if (!finder.Found()) + return; + + gp_Ax3 pos = GeomAdaptor_Surface(finder.Surface()).Plane().Position(); //force plane to be right handed if(!pos.Direct()) pos = gp_Ax3(pos.Ax2()); gp_Dir dir(pos.Direction()); - trsf.SetTransformation(pos); - - gp_Pnt origin = pos.Location(); + trsf.SetTransformation(pos); if(fabs(dir.X())Precision::Confusion()) { - Base::Console().Warning("XY plane has wrong Z height %lf, %lf\n",origin.Z(),z); + AREA_WARN("XY plane has wrong Z height "< Precision::Confusion() || - fabs(pt.Y()) > Precision::Confusion() || - fabs(pt.Z()) > Precision::Confusion()) { - Base::Console().Warning("wrong transformation %lf, %lf, %lf\n",pt.X(),pt.Y(),pt.Z()); + fabs(pt.Y()) > Precision::Confusion() || + fabs(pt.Z()) > Precision::Confusion()) { + AREA_WARN("wrong transformation "< z) - continue; - top_found = true; - top_z = z; - }else if(!dst.IsNull()) - continue; - dst = plane; - dst_trsf = trsf; - + if(!myShape.IsNull() && myZ > z) + return; + myZ = z; + }else if(!myShape.IsNull()) + return; + myShape = shape; + myTrsf = trsf; } - return haveShape; +}; + +TopoDS_Shape Area::findPlane(const TopoDS_Shape &shape, gp_Trsf &trsf) +{ + TopoDS_Shape plane; + double top_z; + foreachSubshape(shape,FindPlane(plane,trsf,top_z)); + return plane; } std::vector > Area::makeSections( @@ -534,6 +555,8 @@ std::vector > Area::makeSections( if(plane.IsNull()) throw Base::ValueError("failed to obtain section plane"); + TIME_INIT2(t,t1); + TopLoc_Location loc(trsf); Bnd_Box bounds; @@ -630,7 +653,7 @@ std::vector > Area::makeSections( Part::CrossSection section(a,b,c,it.Current()); std::list wires = section.slice(-d); if(wires.empty()) { - Base::Console().Log("Section returns no wires\n"); + AREA_LOG("Section returns no wires"); continue; } @@ -641,13 +664,13 @@ std::vector > Area::makeSections( try { mkFace.Build(); if (mkFace.Shape().IsNull()) - Base::Console().Warning("FaceMakerBullseye return null shape on section\n"); + AREA_WARN("FaceMakerBullseye return null shape on section"); else { builder.Add(comp,mkFace.Shape()); continue; } }catch (Base::Exception &e){ - Base::Console().Warning("FaceMakerBullseye failed on section: %s\n", e.what()); + AREA_WARN("FaceMakerBullseye failed on section: " << e.what()); } for(const TopoDS_Wire &wire : wires) builder.Add(comp,wire); @@ -662,8 +685,10 @@ std::vector > Area::makeSections( if(area->myShapes.size()) sections.push_back(area); else - Base::Console().Warning("Discard empty section\n"); + AREA_WARN("Discard empty section"); + TIME_PRINT(t1,"makeSection " << z); } + TIME_PRINT(t,"makeSection count: " << sections.size()<<", total"); return std::move(sections); } @@ -676,11 +701,8 @@ TopoDS_Shape Area::getPlane(gp_Trsf *trsf) { if(myShapes.empty()) throw Base::ValueError("no shape added"); double top_z; - for(auto &s : myShapes) { - if(!findPlane(s.shape,TopAbs_FACE,myShapePlane,myTrsf,top_z) && - !findPlane(s.shape,TopAbs_WIRE,myShapePlane,myTrsf,top_z)) - findPlane(s.shape,TopAbs_EDGE,myShapePlane,myTrsf,top_z); - } + for(auto &s : myShapes) + foreachSubshape(s.shape,FindPlane(myShapePlane,myTrsf,top_z)); if(myShapePlane.IsNull()) throw Base::ValueError("shapes are not planar"); } @@ -707,6 +729,7 @@ void Area::build() { return; } + TIME_INIT(t); getPlane(); try { @@ -744,8 +767,8 @@ void Area::build() { pending = true; } if(mySkippedShapes && !myHaveSolid) - Base::Console().Warning("%s %d non coplanar shapes\n", - myParams.Coplanar==CoplanarForce?"Skipped":"Found",mySkippedShapes); + AREA_WARN((myParams.Coplanar==CoplanarForce?"Skipped ":"Found ")<< + mySkippedShapes<<" non coplanar shapes"); if(pending){ if(myParams.OpenMode!=OpenModeNone) @@ -783,6 +806,8 @@ void Area::build() { myArea = std::move(area.myArea); } + TIME_PRINT(t,"prepare"); + }catch(...) { clean(); throw; @@ -790,7 +815,7 @@ void Area::build() { } list Area::sortWires(int index, int count, const gp_Pnt *pstart, - gp_Pnt *_pend, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_MIN_DIST)) + gp_Pnt *_pend, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_SORT)) { std::list wires; @@ -813,23 +838,22 @@ list Area::sortWires(int index, int count, const gp_Pnt *pstart, count = mySections.size(); for(int i=index;i ws = - mySections[i]->sortWires(-1,0,&pt,&pend, - PARAM_FIELDS(PARAM_FARG,AREA_PARAMS_MIN_DIST)); + mySections[i]->sortWires(0,0,&pt,&pend, + PARAM_FIELDS(PARAM_FARG,AREA_PARAMS_SORT)); for(auto &wire : ws) wires.push_back(wire.Moved(loc)); pt = pend; } if(_pend) *_pend = pend.Transformed(loc); - return wires; + return std::move(wires); } 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)); + 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); @@ -846,7 +870,7 @@ list Area::sortWires(int index, int count, const gp_Pnt *pstart, } *_pend = pend.Transformed(TopLoc_Location(trsf)); } - return wires; + return std::move(wires); } TopoDS_Shape Area::toShape(CArea &area, short fill) { @@ -930,6 +954,8 @@ TopoDS_Shape Area::getShape(int index) { return myShape; } + TIME_INIT(t); + // do offset first, then pocket the inner most offseted shape std::list > areas; makeOffset(areas,PARAM_FIELDS(AREA_MY,AREA_PARAMS_OFFSET)); @@ -962,13 +988,20 @@ TopoDS_Shape Area::getShape(int index) { builder.MakeCompound(compound); short fill = myParams.Thicken?FillFace:FillNone; + TIME_INIT(t2); + DURATION_INIT(d); for(shared_ptr area : areas) { - if(myParams.Thicken) + if(myParams.Thicken){ area->Thicken(myParams.ToolRadius); + DURATION_PLUS(d,t2); + } const TopoDS_Shape &shape = toShape(*area,fill); if(shape.IsNull()) continue; builder.Add(compound,shape); } + if(myParams.Thicken) + DURATION_PRINT(d,"Thicken"); + // make sure the compound has at least one edge for(TopExp_Explorer it(compound,TopAbs_EDGE);it.More();) { builder.Add(compound,areaPocket.makePocket( @@ -977,6 +1010,7 @@ TopoDS_Shape Area::getShape(int index) { break; } myShapeDone = true; + TIME_PRINT(t,"total"); return myShape; } @@ -989,7 +1023,9 @@ TopoDS_Shape Area::makeOffset(int index,PARAM_ARGS(PARAM_FARG,AREA_PARAMS_OFFSET if(areas.empty()) { if(myParams.Thicken && myParams.ToolRadius>Precision::Confusion()) { CArea area(*myArea); + TIME_INIT(t); area.Thicken(myParams.ToolRadius); + TIME_PRINT(t,"Thicken"); return toShape(area,FillFace); } return TopoDS_Shape(); @@ -997,10 +1033,13 @@ TopoDS_Shape Area::makeOffset(int index,PARAM_ARGS(PARAM_FARG,AREA_PARAMS_OFFSET BRep_Builder builder; TopoDS_Compound compound; builder.MakeCompound(compound); + TIME_INIT(t); + DURATION_INIT(d); for(shared_ptr area : areas) { short fill; if(myParams.Thicken && myParams.ToolRadius>Precision::Confusion()) { area->Thicken(myParams.ToolRadius); + DURATION_PLUS(d,t); fill = FillFace; }else if(areas.size()==1) fill = myParams.Fill; @@ -1010,6 +1049,8 @@ TopoDS_Shape Area::makeOffset(int index,PARAM_ARGS(PARAM_FARG,AREA_PARAMS_OFFSET if(shape.IsNull()) continue; builder.Add(compound,shape); } + if(myParams.Thicken && myParams.ToolRadius>Precision::Confusion()) + DURATION_PRINT(d,"Thicken"); for(TopExp_Explorer it(compound,TopAbs_EDGE);it.More();) return compound; return TopoDS_Shape(); @@ -1021,6 +1062,8 @@ void Area::makeOffset(list > &areas, if(fabs(offset) > &areas, break; } #endif - + if(count>1) + TIME_PRINT(t1,"makeOffset " << i << '/' << count); if(area.m_curves.empty()) return; } + TIME_PRINT(t,"makeOffset count: " << count); } TopoDS_Shape Area::makePocket(int index, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_POCKET)) { @@ -1098,6 +1143,8 @@ TopoDS_Shape Area::makePocket(int index, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_POCKE build(); AREA_SECTION(makePocket,index,PARAM_FIELDS(PARAM_FARG,AREA_PARAMS_POCKET)); + TIME_INIT(t); + PocketMode pm; switch(mode) { case Area::PocketModeZigZag: @@ -1128,6 +1175,8 @@ TopoDS_Shape Area::makePocket(int index, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_POCKE in.Reorder(); in.MakePocketToolpath(out.m_curves,params); + TIME_PRINT(t,"makePocket"); + if(myParams.Thicken){ out.Thicken(tool_radius); return toShape(out,FillFace); @@ -1170,9 +1219,8 @@ TopoDS_Wire Area::toShape(const CCurve &c, const gp_Trsf *trsf) { newCenter.SetX(x - dx); newCenter.SetY(y - dy); } - Base::Console().Warning( - "Arc correction: %lf,%lf, center(%lf,%lf)->(%lf,%lf)\n", - r,r2,center.X(),center.Y(),newCenter.X(),newCenter.Y()); + AREA_WARN("Arc correction: "<"< &wires; + GetWires(std::list &ws) + :wires(ws) + {} + void operator()(const TopoDS_Shape &shape, int type) { + WireInfo info; + if(type == TopAbs_WIRE) + info.wire = shape; + else + info.wire = BRepBuilderAPI_MakeWire(TopoDS::Edge(shape)).Wire(); + + BRepTools_WireExplorer xp(TopoDS::Wire(shape)); + info.pstart = BRep_Tool::Pnt(xp.CurrentVertex()); + for(;xp.More();xp.Next()); + info.pend = BRep_Tool::Pnt(xp.CurrentVertex()); + info.wire = shape; + wires.push_back(info); + } +}; + +struct ShapeInfo{ + gp_Pln myPln; + std::list myWires; + TopoDS_Shape myShape; + gp_Pnt myBestPt; + std::list::iterator myBestWire; + TopoDS_Shape mySupport; + bool mySupportEdge; + bool myPlanar; + bool myRebase; + bool myStart; + + ShapeInfo(BRepLib_FindSurface &finder, const TopoDS_Shape &_shape) + :myPln(GeomAdaptor_Surface(finder.Surface()).Plane()) + ,myShape(_shape) + ,myPlanar(true) + {} + ShapeInfo(const TopoDS_Shape &_shape) + :myShape(_shape) + ,myPlanar(false) + {} + double nearest(const gp_Pnt &pt) { + if(myWires.empty()) + foreachSubshape(myShape,GetWires(myWires),TopAbs_WIRE); + 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) { + const TopoDS_Shape &wire = it->wire; + TopoDS_Shape support; + bool support_edge; + double d; + gp_Pnt p; + bool done = false; + bool is_start = false; + if(BRep_Tool::IsClosed(wire)) { + BRepExtrema_DistShapeShape extss(v,wire); + if(extss.IsDone() && extss.NbSolution()) { + d = extss.Value(); + p = extss.PointOnShape2(0); + support = extss.SupportOnShape2(0); + support_edge = extss.SupportTypeShape2(0)==BRepExtrema_IsOnEdge; + done = true; + }else + AREA_WARN("BRepExtrema_DistShapeShape failed"); + } + if(!done){ + double d1 = p.Distance(it->pstart); + double d2 = p.Distance(it->pend); + AREA_TRACE("start "<pstart)<<", " << d1 << + AREA_PT(it->pend)<<", " <pstart; + is_start = true; + }else{ + d = d2; + p = it->pend; + is_start = false; + } + } + if(!first && d>=best_d) continue; + first = false; + myBestPt = p; + myBestWire = it; + best_d = d; + myRebase = done; + myStart = is_start; + if(done) { + mySupport = support; + mySupportEdge = support_edge; + } + } + return best_d; + } + + //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) { + BRepBuilderAPI_MakeWire mkWire; + TopoDS_Shape estart; + TopoDS_Edge eend; + for(int state=0;state<3;++state) { + BRepTools_WireExplorer xp(TopoDS::Wire(myBestWire->wire)); + pend = BRep_Tool::Pnt(xp.CurrentVertex()); + + //checking the case of bestpoint == wire start + if(state==0 && !mySupportEdge && pend.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 + if(state==2 && estart.IsSame(xp.Current())) + 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 + if(state) { + mkWire.Add(xp.Current()); + pend = pt; + 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)) { + estart = mySupport; + state = 1; + eend = BRepBuilderAPI_MakeEdge(curve.Curve().Curve(), pend, myBestPt); + mkWire.Add(BRepBuilderAPI_MakeEdge(curve.Curve().Curve(), myBestPt, pt)); + } + }else if(myBestPt.Distance(pend)pend; + return myBestWire->wire; + } + + std::list sortWires(gp_Pnt &pend) { + std::list wires; + while(true) { + AREA_TRACE("3D sort pt " << AREA_PT(myBestPt)); + if(myRebase) { + AREA_TRACE("3D sort rebase"); + wires.push_back(rebaseWire(pend)); + }else if(!myStart){ + AREA_TRACE("3D sort reverse"); + wires.push_back(myBestWire->wire.Reversed()); + pend = myBestWire->pstart; + }else{ + wires.push_back(myBestWire->wire); + pend = myBestWire->pend; + } + AREA_TRACE("3D sort end " << AREA_PT(pend)); + myWires.erase(myBestWire); + if(myWires.empty()) break; + nearest(pend); + } + return std::move(wires); + } +}; + +struct ShapeInfoBuilder { + std::list &myList; + + ShapeInfoBuilder(std::list &list) + :myList(list) + {} + + void operator()(const TopoDS_Shape &shape, int) { + BRepLib_FindSurface finder(shape,-1,Standard_True); + if(finder.Found()) + myList.push_back(ShapeInfo(finder,shape)); + else + myList.push_back(ShapeInfo(shape)); + } +}; + + std::list Area::sortWires(const std::list &shapes, const AreaParams *params, const gp_Pnt *_pstart, gp_Pnt *_pend, - PARAM_ARGS(PARAM_FARG,AREA_PARAMS_SORT_WIRES)) + PARAM_ARGS(PARAM_FARG,AREA_PARAMS_SORT)) { std::list wires; - //Heristic sorting by shape's vertex Z value. For performance's sake, we don't - //perform any planar checking here - std::multimap shape_map; + if(shapes.empty()) return wires; - for (auto &shape : shapes) { - std::list subshapes; - if(!explode) - subshapes.push_back(shape); - else{ + if(sort_mode == SortModeNone) { + for(auto &shape : shapes) { + if (shape.IsNull()) + continue; bool haveShape=false; for(TopExp_Explorer it(shape,TopAbs_WIRE);it.More();it.Next()) { haveShape=true; - subshapes.push_back(it.Current()); - } - if(!haveShape) { - for(TopExp_Explorer it(shape,TopAbs_EDGE);it.More();it.Next()) - subshapes.push_back(it.Current()); + wires.push_back(it.Current()); } + if(haveShape) continue; + for(TopExp_Explorer it(shape,TopAbs_EDGE);it.More();it.Next()) + wires.push_back(BRepBuilderAPI_MakeWire(TopoDS::Edge(it.Current())).Wire()); } - //Order the shapes by its vertex Z value. - for(auto &s : subshapes) { - bool first=true; - double z=0.0; - for(TopExp_Explorer it(s,TopAbs_VERTEX);it.More();) { - gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(it.Current())); - if(first || z < p.Z()) { - first = false; - z = p.Z(); - } - if(!top_z) break; + return std::move(wires); + } + + std::list shape_list; + + TIME_INIT2(t,t1); +#define SORT_WIRE_TIME(_msg) \ + TIME_PRINT(t1,"sortWires "<< _msg) + + if(sort_mode == SortMode3D) { + for(auto &shape : shapes) + shape_list.push_back(ShapeInfo(shape)); + }else{ + //first pass, find plane of each shape + for(auto &shape : shapes) { + //explode the shape + foreachSubshape(shape,ShapeInfoBuilder(shape_list)); + } + + if(shape_list.empty()) + return wires; + + SORT_WIRE_TIME("plan finding"); + } + + Bnd_Box bounds; + gp_Pnt pstart,pend; + if(_pstart) + pstart = *_pstart; + bool use_bound = fabs(pstart.X())myShape, bounds, Standard_False); + if(!it->myPlanar) continue; + TopoDS_Builder builder; + TopoDS_Compound comp; + builder.MakeCompound(comp); + bool empty = true; + for(auto itNext3=itNext,itNext2=itNext;itNext2!=shape_list.end();itNext2=itNext3) { + ++itNext3; + if(!itNext2->myPlanar || + !it->myPln.Position().IsCoplanar(itNext2->myPln.Position(), + Precision::Confusion(),Precision::Confusion())) + continue; + if(itNext == itNext2) ++itNext; + builder.Add(comp,itNext2->myShape); + shape_list.erase(itNext2); + empty = false; + } + if(!empty) { + builder.Add(comp,it->myShape); + it->myShape = comp; } - shape_map.insert(std::pair(z,s)); } + SORT_WIRE_TIME("plane merging"); } - if(!shape_map.size()) - return wires; Area area(params); - //We'll do planar checking here, so disable Area planar check + //We have done planar checking here, so disable Area planar check area.myParams.Coplanar = Area::CoplanarNone; - gp_Pnt pstart,pend; - if(_pstart) pstart = *_pstart; - TopoDS_Shape plane = shape_map.rbegin()->second; - area.setPlane(plane); - for(auto &item : boost::adaptors::reverse(shape_map)) { - //do planar checking, and sort wires grouped by plane - if(!Area::isCoplanar(plane,item.second)) { - wires.splice(wires.end(),area.sortWires( - -1,0,&pstart,&pend, PARAM_FIELDS(PARAM_FARG,AREA_PARAMS_MIN_DIST))); - pstart = pend; + DURATION_INIT2(td1,td2); + + if(use_bound) { + bounds.SetGap(0.0); + Standard_Real xMin, yMin, zMin, xMax, yMax, zMax; + bounds.Get(xMin, yMin, zMin, xMax, yMax, zMax); + pstart.SetCoord(xMax,yMax,zMax); + } + bool has_2d5=false,has_3d=false; + while(shape_list.size()) { + AREA_TRACE("start " << shape_list.size() << ' ' << AREA_PT(pstart)); + double best_d; + auto best_it = shape_list.begin(); + bool first = true; + for(auto it=best_it;it!=shape_list.end();++it) { + double d; + gp_Pnt pt; + if(it->myPlanar){ + d = it->myPln.Distance(pstart); +#define AREA_TIME_2D5 \ + DURATION_PLUS(td1,t1);\ + has_2d5=true + + AREA_TIME_2D5; + }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); - plane = item.second; - area.setPlane(plane); + 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->sortWires(pend)); + AREA_TIME_3D; } - area.add(item.second,Area::OperationCompound); + pstart = pend; + shape_list.erase(best_it); } - wires.splice(wires.end(),area.sortWires( - -1,0,&pstart,&pend, PARAM_FIELDS(PARAM_FARG,AREA_PARAMS_MIN_DIST))); if(_pend) *_pend = pend; - return wires; + + if(has_2d5) DURATION_PRINT(td1,"sortWires 2D5"); + if(has_3d) DURATION_PRINT(td2,"sortWires 3D"); + TIME_PRINT(t,"sortWires total"); + return std::move(wires); } static void addCommand(Toolpath &path, const gp_Pnt &p, @@ -1345,25 +1672,15 @@ static void addCommand(Toolpath &path, } void Area::toPath(Toolpath &path, const std::list &shapes, - const gp_Pnt *pstart, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_PATH)) + const AreaParams *_params, const gp_Pnt *pstart, gp_Pnt *pend, + PARAM_ARGS(PARAM_FARG,AREA_PARAMS_PATH)) { std::list wires; - if(sort) - wires = sortWires(shapes,NULL,pstart); - else{ - for(auto &shape : shapes) { - if (shape.IsNull()) - continue; - bool haveShape=false; - for(TopExp_Explorer it(shape,TopAbs_WIRE);it.More();it.Next()) { - haveShape=true; - wires.push_back(it.Current()); - } - if(haveShape) continue; - for(TopExp_Explorer it(shape,TopAbs_EDGE);it.More();it.Next()) - wires.push_back(BRepBuilderAPI_MakeWire(TopoDS::Edge(it.Current())).Wire()); - } - } + + AreaParams params; + if(_params) params =*_params; + wires = sortWires(shapes,¶ms,pstart,pend, + PARAM_FIELDS(PARAM_FARG,AREA_PARAMS_SORT)); if(threshold < Precision::Confusion()) threshold = Precision::Confusion(); @@ -1373,7 +1690,7 @@ void Area::toPath(Toolpath &path, const std::list &shapes, for(const TopoDS_Shape &wire : wires) { BRepTools_WireExplorer xp(TopoDS::Wire(wire)); p = BRep_Tool::Pnt(xp.CurrentVertex()); - if(first||(p.Z()>=plast.Z()&&p.Distance(plast)>threshold)) + if(first||p.Distance(plast)>threshold) addCommand(path,p,true,height,clearance); else addCommand(path,p); @@ -1386,6 +1703,25 @@ void Area::toPath(Toolpath &path, const std::list &shapes, switch (curve.GetType()) { case GeomAbs_Line: { + 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,pt); + } + }else{ + for (int i=2; i<=nbPoints; i++) { + gp_Pnt pt = curve.Value(discretizer.Parameter(i)); + addCommand(path,pt); + } + } + break; + } + } addCommand(path,p); break; } case GeomAbs_Circle:{ @@ -1396,6 +1732,27 @@ void Area::toPath(Toolpath &path, const std::list &shapes, bool clockwise = axis.Direction().Z()<0; if(reversed) clockwise = !clockwise; gp_Pnt center = axis.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; + } + } + break; + } + } if(fabs(first-last)>M_PI) { // Split arc(circle) larger than half circle. gp_Pnt mid = curve.Value((last-first)*0.5+first); @@ -1406,7 +1763,7 @@ void Area::toPath(Toolpath &path, const std::list &shapes, break; } default: { // Discretize all other type of curves - GCPnts_QuasiUniformDeflection discretizer(curve, deflection, + GCPnts_QuasiUniformDeflection discretizer(curve, params.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 de9cca972a8d..9db1cc2bc9cc 100644 --- a/src/Mod/Path/App/Area.h +++ b/src/Mod/Path/App/Area.h @@ -23,6 +23,8 @@ #ifndef PATH_AREA_H #define PATH_AREA_H +#include +#include #include #include #include @@ -31,9 +33,78 @@ #include #include +#include #include "Path.h" #include "AreaParams.h" +// #define AREA_TRACE_ENABLE + +#define _AREA_LOG(_l,_msg) do {\ + std::stringstream str;\ + str << "Path.Area: " << _msg;\ + Base::Console()._l("%s\n",str.str().c_str());\ + qApp->sendPostedEvents();\ +}while(0) + +#define AREA_LOG(_msg) _AREA_LOG(Log,_msg) +#define AREA_WARN(_msg) _AREA_LOG(Warning,_msg) +#define AREA_PT(_pt) '('<<(_pt).X()<<", " << (_pt).Y()<<", " << (_pt).Z()<<')' +#define AREA_PT2(_pt) '('<<(_pt).x<<", " << (_pt).y<<')' +#ifdef AREA_TRACE_ENABLE +# define AREA_TRACE AREA_LOG +#else +# define AREA_TRACE(...) do{}while(0) +#endif + +#define AREA_TIME_ENABLE + +#ifdef AREA_TIME_ENABLE +#define TIME_UNIT duration +#define TIME_CLOCK high_resolution_clock +#define TIME_POINT std::chrono::TIME_CLOCK::time_point + +#define TIME_INIT(_t) \ + auto _t=std::chrono::TIME_CLOCK::now() + +#define TIME_INIT2(_t1,_t2) TIME_INIT(_t1),_t2=_t1 +#define TIME_INIT3(_t1,_t2,_t3) TIME_INIT(_t1),_t2=_t1,_t3=_t1 + +#define DURATION_PRINT(_d,_msg) \ + AREA_LOG(_msg<< " time: " << _d.count()<<'s'); + +#define TIME_PRINT(_t,_msg) \ + DURATION_PRINT(Path::getDuration(_t),_msg); + +#define DURATION_INIT(_d) \ + std::chrono::TIME_UNIT _d(0) + +#define DURATION_INIT2(_d1,_d2) DURATION_INIT(_d1),_d2(0) + +namespace Path { +inline std::chrono::TIME_UNIT getDuration(TIME_POINT &t) +{ + auto tnow = std::chrono::TIME_CLOCK::now(); + auto d = std::chrono::duration_cast(tnow-t); + t = tnow; + return d; +} +} + +#define DURATION_PLUS(_d,_t) _d += Path::getDuration(_t) + +#else + +#define TIME_INIT(...) do{}while(0) +#define TIME_INIT2(...) do{}while(0) +#define TIME_INIT3(...) do{}while(0) +#define TIME_PRINT(...) do{}while(0) +#define DURATION_PRINT(...) do{}while(0) +#define DURATION_INIT(...) do{}while(0) +#define DURATION_INIT2(...) do{}while(0) +#define DURATION_PLUS(...) do{}while(0) + +#endif + class CArea; class CCurve; @@ -148,8 +219,6 @@ class PathExport Area: public Base::BaseClass { bool isBuilt() const; - static bool findPlane(const TopoDS_Shape &shape, int type, - TopoDS_Shape &plane, gp_Trsf &trsf, double &top_z); TopoDS_Shape findPlane(const TopoDS_Shape &shape, gp_Trsf &trsf); public: @@ -271,14 +340,15 @@ class PathExport Area: public Base::BaseClass { * \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_MIN_DIST for other arguments + * 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_MIN_DIST)); + const gp_Pnt *pstart=NULL, gp_Pnt *pend=NULL, + PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_SORT)); /** Add a OCC generic shape to CArea * @@ -333,27 +403,31 @@ class PathExport Area: public Base::BaseClass { * used for sorting * \arg \c pstart: optional start point * \arg \c pend: optional output containing the ending point of the returned - * maybe broken if the algorithm see fits. * - * See #AREA_PARAMS_SORT_WIRES for other arguments + * See #AREA_PARAMS_SORT for other arguments * * \return sorted wires */ static std::list sortWires(const std::list &shapes, const AreaParams *params = NULL, const gp_Pnt *pstart=NULL, - gp_Pnt *pend=NULL, PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_SORT_WIRES)); + gp_Pnt *pend=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 gp_Pnt *pstart=NULL, PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_PATH)); + const AreaParams *params=NULL, const gp_Pnt *pstart=NULL, gp_Pnt *pend=NULL, + PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_PATH)); + PARAM_ENUM_DECLARE(AREA_PARAMS_PATH) }; } //namespace Path diff --git a/src/Mod/Path/App/AreaParams.h b/src/Mod/Path/App/AreaParams.h index 0f10d923be2c..9a6583ff15b9 100644 --- a/src/Mod/Path/App/AreaParams.h +++ b/src/Mod/Path/App/AreaParams.h @@ -71,10 +71,13 @@ AREA_PARAMS_DEFLECTION \ AREA_PARAMS_CLIPPER_FILL +#define AREA_PARAMS_FIT_ARCS \ + ((bool,fit_arcs,FitArcs,true,"Enable arc fitting")) + /** libarea algorithm option parameters */ #define AREA_PARAMS_CAREA \ ((double,tolerance,Tolerance,Precision::Confusion(),"Point coincidence tolerance"))\ - ((bool,fit_arcs,FitArcs,true,"Enable arc fitting"))\ + AREA_PARAMS_FIT_ARCS \ ((bool,clipper_simple,Simplify,false,\ "Simplify polygons after operation. See https://goo.gl/Mh9XK1"))\ ((double,clipper_clean_distance,CleanDistance,0.0,\ @@ -150,11 +153,24 @@ ((double,round_precision,RoundPreceision,0.0,\ "Round joint precision. If =0, it defaults to Accuracy. \nSee https://goo.gl/4odfQh")) +#define AREA_PARAMS_MIN_DIST \ + ((double, min_dist, MinDistance, 0.0, \ + "minimum distance for the generated new wires. Wires maybe broken if the algorithm see fits.\n"\ + "Set to zero to disable wire breaking.")) + +/** Area wire sorting parameters */ +#define AREA_PARAMS_SORT \ + ((enum, sort_mode, SortMode, 1, "Wire sorting mode to optimize travel distance.\n"\ + "'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)))\ + AREA_PARAMS_MIN_DIST + /** Area path generation parameters */ #define AREA_PARAMS_PATH \ - ((bool, sort, SortShape, true, \ - "Whether to sort the shapes to optimize travel distance. You can customize wire\n"\ - "sorting by calling sortWires() manually."))\ + 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."))\ @@ -162,23 +178,17 @@ ((double, clearance, Clearance, 0.0,\ "When return from last retraction, this gives the pause height relative to the Z\n"\ "value of the next move"))\ - AREA_PARAMS_DEFLECTION + ((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")) -#define AREA_PARAMS_MIN_DIST \ - ((double, min_dist, MinDistance, 1.0, \ - "minimum distance for the generate new wires. Wires maybe broken if the\n"\ - "algorithm see fits."))\ - -/** Area wire sorting parameters */ -#define AREA_PARAMS_SORT_WIRES \ - ((bool, explode, Explode, true,\ - "If ture, the input shape will be exploded into wires before doing planar checking.\n"\ - "Otherwise, and whole shape is considered to be in one plane without checking."))\ - ((bool, top_z, TopZ, false, \ - "If ture, the planes is ordered by the first the shapes first vertex Z value.\n"\ - "Otherwise, by the highest Z of all of its vertexes."))\ - AREA_PARAMS_MIN_DIST +#define AREA_PARAMS_PATH_EXTRA \ + AREA_PARAMS_DEFLECTION \ + AREA_PARAMS_FIT_ARCS +#define AREA_PARAMS_PATH_CONF \ + AREA_PARAMS_PATH \ + AREA_PARAMS_PATH_EXTRA /** Group of all Area configuration parameters except CArea's*/ #define AREA_PARAMS_AREA \ diff --git a/src/Mod/Path/App/AreaPyImp.cpp b/src/Mod/Path/App/AreaPyImp.cpp index 3f44a6c98d46..bec2817f0cc3 100644 --- a/src/Mod/Path/App/AreaPyImp.cpp +++ b/src/Mod/Path/App/AreaPyImp.cpp @@ -91,12 +91,12 @@ static const AreaDoc myDocs[] = { { "sortWires", - "sortWires(index=-1, count=0, start=Vector()" PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_MIN_DIST) "):\n" + "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 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_MIN_DIST), + PARAM_PY_DOC(ARG,AREA_PARAMS_SORT), }, }; @@ -151,7 +151,8 @@ PyObject* AreaPy::setPlane(PyObject *args) { #define GET_TOPOSHAPE(_p) static_cast(_p)->getTopoShapePtr()->getShape() getAreaPtr()->setPlane(GET_TOPOSHAPE(pcObj)); - return Py_None; + Py_INCREF(this); + return this; } PyObject* AreaPy::getShape(PyObject *args, PyObject *keywds) @@ -168,17 +169,17 @@ PyObject* AreaPy::getShape(PyObject *args, PyObject *keywds) } PyObject* AreaPy::sortWires(PyObject *args, PyObject *keywds){ - PARAM_PY_DECLARE_INIT(PARAM_FARG,AREA_PARAMS_MIN_DIST) + 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_MIN_DIST), NULL}; + PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_SORT), NULL}; if (!PyArg_ParseTupleAndKeywords(args, keywds, - "|hhO!" PARAM_PY_KWDS(AREA_PARAMS_MIN_DIST), + "|hhO!" PARAM_PY_KWDS(AREA_PARAMS_SORT), kwlist,&index,&count,&(Base::VectorPy::Type),&start, - PARAM_REF(PARAM_FARG,AREA_PARAMS_MIN_DIST))) + PARAM_REF(PARAM_FARG,AREA_PARAMS_SORT))) return 0; gp_Pnt pstart,pend; @@ -188,7 +189,7 @@ PyObject* AreaPy::sortWires(PyObject *args, PyObject *keywds){ } std::list wires = getAreaPtr()->sortWires( index,count,&pstart,&pend, - PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_MIN_DIST)); + PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_SORT)); PyObject *list = PyList_New(0); for(auto &wire : wires) PyList_Append(list,Py::new_reference_to( @@ -217,7 +218,8 @@ PyObject* AreaPy::add(PyObject *args, PyObject *keywds) if (PyObject_TypeCheck(pcObj, &(Part::TopoShapePy::Type))) { getAreaPtr()->add(GET_TOPOSHAPE(pcObj),op); - return Py_None; + Py_INCREF(this); + return this; } else if (PyObject_TypeCheck(pcObj, &(PyList_Type)) || PyObject_TypeCheck(pcObj, &(PyTuple_Type))) { Py::Sequence shapeSeq(pcObj); @@ -233,7 +235,8 @@ PyObject* AreaPy::add(PyObject *args, PyObject *keywds) getAreaPtr()->add(GET_TOPOSHAPE(item), PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_OPCODE)); } - return Py_None; + Py_INCREF(this); + return this; } PyErr_SetString(PyExc_TypeError, "shape must be 'TopoShape' or list of 'TopoShape'"); @@ -356,7 +359,8 @@ PyObject* AreaPy::setParams(PyObject *args, PyObject *keywds) PARAM_FOREACH(AREA_GET,AREA_PARAMS_CONF) getAreaPtr()->setParams(params); - return Py_None; + Py_INCREF(this); + return this; } PyObject* AreaPy::getParams(PyObject *args) diff --git a/src/Mod/Path/App/FeatureArea.cpp b/src/Mod/Path/App/FeatureArea.cpp index f016a4611f22..5d399a599c2c 100644 --- a/src/Mod/Path/App/FeatureArea.cpp +++ b/src/Mod/Path/App/FeatureArea.cpp @@ -64,10 +64,8 @@ FeatureArea::~FeatureArea() } Area &FeatureArea::getArea() { - if(!myBuild) { - myBuild = true; + if(!myBuild) execute(); - } return myArea; } @@ -85,6 +83,9 @@ App::DocumentObjectExecReturn *FeatureArea::execute(void) return new App::DocumentObjectExecReturn("Linked shape object is empty"); } + TIME_INIT(t); + + myBuild = true; AreaParams params; #define AREA_PROP_GET(_param) \ @@ -102,19 +103,35 @@ App::DocumentObjectExecReturn *FeatureArea::execute(void) PARAM_PROP_ARGS(AREA_PARAMS_OPCODE)); } - this->Shape.setValue(myArea.getShape(-1)); + myShapes.clear(); + if(myArea.getSectionCount()==0) + myShapes.push_back(myArea.getShape(-1)); + else { + myShapes.reserve(myArea.getSectionCount()); + for(int i=0;i<(int)myArea.getSectionCount();++i) + myShapes.push_back(myArea.getShape(i)); + } + + if(myShapes.empty()) + Shape.setValue(TopoDS_Shape()); + else if(myShapes.size()==1) + Shape.setValue(myShapes.front()); + else{ + BRep_Builder builder; + TopoDS_Compound compound; + builder.MakeCompound(compound); + for(auto &shape : myShapes) + builder.Add(compound,shape); + Shape.setValue(compound); + } + + TIME_PRINT(t,"feature execute"); return Part::Feature::execute(); } -std::list FeatureArea::getShapes() { - std::list shapes; - Area &area = getArea(); - if(area.getSectionCount()) { - for(int i=0;i<(int)area.getSectionCount();++i) - shapes.push_back(area.getShape(i)); - }else - shapes.push_back(area.getShape()); - return shapes; +const std::vector &FeatureArea::getShapes() { + getArea(); + return myShapes; } short FeatureArea::mustExecute(void) const @@ -156,32 +173,31 @@ std::list FeatureAreaView::getShapes() { if (!pObj) return shapes; if(!pObj->isDerivedFrom(FeatureArea::getClassTypeId())) return shapes; - Area &area = static_cast(pObj)->getArea(); - if(!area.getSectionCount()) { - shapes.push_back(area.getShape()); + auto all_shapes = static_cast(pObj)->getShapes(); + + if(all_shapes.empty()) return shapes; - } int index=SectionIndex.getValue(),count=SectionCount.getValue(); if(index<0) { - index += ((int)area.getSectionCount()); + index += ((int)all_shapes.size()); if(index<0) return shapes; if(count<=0 || index+1-count<0) { count = index+1; index = 0; }else index -= count-1; - }else if(index >= (int)area.getSectionCount()) + }else if(index >= (int)all_shapes.size()) return shapes; - if(count<=0) count = area.getSectionCount(); + if(count<=0) count = all_shapes.size(); count += index; - if(count>(int)area.getSectionCount()) - count = area.getSectionCount(); + if(count>(int)all_shapes.size()) + count = all_shapes.size(); for(int i=index;i getShapes(); + const std::vector &getShapes(); /// returns the type name of the ViewProvider virtual const char* getViewProviderName(void) const { @@ -63,6 +63,7 @@ class PathExport FeatureArea : public Part::Feature private: bool myBuild; Area myArea; + std::vector myShapes; }; typedef App::FeaturePythonT FeatureAreaPython; diff --git a/src/Mod/Path/App/FeaturePathShape.cpp b/src/Mod/Path/App/FeaturePathShape.cpp index 5f148b487a40..300b8739d18a 100644 --- a/src/Mod/Path/App/FeaturePathShape.cpp +++ b/src/Mod/Path/App/FeaturePathShape.cpp @@ -50,12 +50,14 @@ using namespace Path; PROPERTY_SOURCE(Path::FeatureShape, Path::Feature) +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"); - PARAM_PROP_ADD("Path",AREA_PARAMS_PATH); + PARAM_PROP_ADD("Path",AREA_PARAMS_PATH_CONF); + PARAM_PROP_SET_ENUM(Enums,AREA_PARAMS_PATH_CONF); } FeatureShape::~FeatureShape() @@ -83,7 +85,13 @@ App::DocumentObjectExecReturn *FeatureShape::execute(void) continue; shapes.push_back(shape); } - Area::toPath(path,shapes,&pstart,PARAM_PROP_ARGS(AREA_PARAMS_PATH)); + + 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)); Path.setValue(path); return App::DocumentObject::StdReturn; diff --git a/src/Mod/Path/App/FeaturePathShape.h b/src/Mod/Path/App/FeaturePathShape.h index 7478a258995f..4a539761ca15 100644 --- a/src/Mod/Path/App/FeaturePathShape.h +++ b/src/Mod/Path/App/FeaturePathShape.h @@ -52,7 +52,7 @@ class PathExport FeatureShape : public Path::Feature // Part::PropertyPartShape Shape; App::PropertyLinkList Sources; App::PropertyVector StartPoint; - PARAM_PROP_DECLARE(AREA_PARAMS_PATH) + PARAM_PROP_DECLARE(AREA_PARAMS_PATH_CONF) //@{ /// recalculate the feature