Path.Area: wire sorting fix and improvement

Wire sorting no longer uses libarea. Performance improvement using
boost::geometry::rtree
This commit is contained in:
Zheng, Lei
2017-03-22 04:47:38 +08:00
committed by wmayer
parent dbfc65d535
commit 1d64d1e91c
8 changed files with 435 additions and 384 deletions

View File

@@ -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* <key>: 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* <key>: 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<TopoDS_Shape> 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<Base::VectorPy*>(start)->value();
@@ -369,7 +358,7 @@ private:
try {
std::unique_ptr<Toolpath> path(new Toolpath);
Area::toPath(*path,shapes,&params, &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<TopoDS_Shape> 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<Base::VectorPy*>(start)->value();
@@ -424,7 +406,7 @@ private:
try {
bool need_arc_plane = arc_plane==Area::ArcPlaneAuto;
std::list<TopoDS_Shape> wires = Area::sortWires(shapes,&params,&pstart,
std::list<TopoDS_Shape> 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)

View File

@@ -23,7 +23,11 @@
#ifndef _PreComp_
#endif
#include <boost/range/adaptor/reversed.hpp>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <boost/range/adaptor/indexed.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <BRepLib.hxx>
#include <BRep_Builder.hxx>
@@ -859,60 +863,6 @@ void Area::build() {
}
}
list<TopoDS_Shape> Area::sortWires(int index, int count, const gp_Pnt *pstart,
gp_Pnt *_pend, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_SORT))
{
std::list<TopoDS_Shape> 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;i<count;++i) {
wires = mySections[i]->sortWires(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<gp_Pnt> 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<WireInfo> Wires;
typedef std::pair<Wires::iterator,size_t> 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<RValue,RParameters,RGetter> 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<Wires::iterator,size_t> RResults;
struct GetWires {
std::list<WireInfo> &wires;
GetWires(std::list<WireInfo> &ws)
:wires(ws)
Wires &wires;
RTree &rtree;
RTreeParams &params;
GetWires(std::list<WireInfo> &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<Precision::Confusion() || !info.isClosed) {
info.points.push_back(BRep_Tool::Pnt(xp.CurrentVertex()));
for(;xp.More();xp.Next());
// We don't add in-between vertices of an open wire, because we
// haven't implemented open wire breaking yet.
info.points.push_back(BRep_Tool::Pnt(xp.CurrentVertex()));
} else {
// For closed wires, we are can easily rebase the wire, so we
// discretize the wires to spatial index it in order to accelerate
// nearest point searching
for(;xp.More();xp.Next()) {
// push the head point
info.points.push_back(BRep_Tool::Pnt(xp.CurrentVertex()));
BRepAdaptor_Curve curve(xp.Current());
GCPnts_UniformAbscissa discretizer(curve, params.abscissa,
curve.FirstParameter(), curve.LastParameter());
if (discretizer.IsDone()) {
int nbPoints = discretizer.NbPoints();
// OCC discretizer uses one-based index, so index one is
// the first point. The tail point is added later, and the
// head point is the tail of the previous edge. So We can
// exclude the head and tail points, which is convenient
// since we don't need to check the orientation of the
// edge.
for (int i=2; i<nbPoints; i++)
info.points.push_back(curve.Value(discretizer.Parameter(i)));
}else
AREA_WARN("discretizer failed");
}
// no need to push the final tail point, since it's a closed wire
// info.points.push_back(BRep_Tool::Pnt(xp.CurrentVertex()));
}
auto it = wires.end();
--it;
for(size_t i=0,count=info.points.size();i<count;++i)
rtree.insert(RValue(it,i));
DURATION_PLUS(params.bd,t);
}
};
struct ShapeInfo{
gp_Pln myPln;
std::list<WireInfo> myWires;
Wires myWires;
RTree myRTree;
TopoDS_Shape myShape;
gp_Pnt myBestPt;
std::list<WireInfo>::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 &params)
: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 &params)
: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(d1<d2) {
d = d1;
p = it->pstart;
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)<Precision::Confusion()) {
AREA_TRACE("keep start");
pend = myBestWire->pend;
if(state==0 && !mySupportEdge && pprev.Distance(myBestPt)<Precision::Confusion()) {
pend = myBestWire->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)<Precision::Confusion()) {
reversed = true;
pt = curve->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 "<<AREA_XYZ(pend)<<", " << AREA_XYZ(myBestPt)
<< ", " << AREA_XYZ(pt) << ", " << d1 << ", " << d2);
AREA_TRACE((reversed?"reversed ":"")<<"edge split "<<AREA_XYZ(pprev)<<", " <<
AREA_XYZ(myBestPt)<< ", "<<AREA_XYZ(pt)<<", "<<d1<<", "<<d2 <<", ("<<
first<<", " << myBestParameter << ", " << last<<')');
continue;
}
AREA_WARN("edge break failed "<<AREA_XYZ(pend)<<", " << AREA_XYZ(myBestPt)
<< ", " << AREA_XYZ(pt) << ", " << d1 << ", " << d2);
AREA_WARN((reversed?"reversed ":"")<<"edge split failed "<<AREA_XYZ(pprev)<<", " <<
AREA_XYZ(myBestPt)<< ", "<<AREA_XYZ(pt)<<", "<<d1<<", "<<d2<<", err: " <<
mkEdge1.Error() << ", " << mkEdge2.Error());
}
if(d1<d2) {
AREA_TRACE("break edge->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)<Precision::Confusion()){
AREA_TRACE("break vertex");
}else if(myBestPt.Distance(pprev)<Precision::Confusion()){
pend = pprev;
// AREA_TRACE("break vertex");
//if best point is on some vertex
estart = xp.Current();
estart = edge;
state = 1;
mkWire.Add(xp.Current());
mkWire.Add(edge);
}
}
if(state==0) {
AREA_WARN("edge break point not found");
pend = myBestWire->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<TopoDS_Shape> sortWires3D(gp_Pnt &pend,double min_dist) {
std::list<TopoDS_Shape> sortWires(const gp_Pnt &pstart, gp_Pnt &pend,double min_dist) {
if(pstart.Distance(myStartPt)>Precision::Confusion())
nearest(pstart);
std::list<TopoDS_Shape> 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<count;++i)
myRTree.remove(RValue(myBestWire,i));
DURATION_PLUS(myParams.rd,t);
myWires.erase(myBestWire);
if(myWires.empty()) break;
nearest(pend);
@@ -1551,48 +1645,59 @@ struct ShapeInfoBuilder {
gp_Trsf &myTrsf;
short *myArcPlane;
bool &myArcPlaneFound;
RTreeParams &myParams;
ShapeInfoBuilder(bool &plane_found, short *arc_plane, gp_Trsf &trsf, std::list<ShapeInfo> &list)
:myList(list) ,myTrsf(trsf) ,myArcPlane(arc_plane), myArcPlaneFound(plane_found)
ShapeInfoBuilder(bool &plane_found, short *arc_plane, gp_Trsf &trsf,
std::list<ShapeInfo> &list, RTreeParams &params)
: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())<Precision::Confusion();
bool y0 = fabs(dir.Y())<Precision::Confusion();
bool z0 = fabs(dir.Z())<Precision::Confusion();
switch(*myArcPlane) {
case Area::ArcPlaneAuto: {
bool x0 = fabs(dir.X())<Precision::Confusion();
bool y0 = fabs(dir.Y())<Precision::Confusion();
bool z0 = fabs(dir.Z())<Precision::Confusion();
if(x0&&y0)
if(x0&&y0){
AREA_TRACE("found arc plane XY");
*myArcPlane = Area::ArcPlaneXY;
else if(x0&&z0)
} else if(x0&&z0) {
AREA_TRACE("found arc plane ZX");
*myArcPlane = Area::ArcPlaneZX;
else if(z0&&y0)
} else if(z0&&y0) {
AREA_TRACE("found arc plane YZ");
*myArcPlane = Area::ArcPlaneYZ;
else {
} else {
*myArcPlane = Area::ArcPlaneXY;
dstPos = gp_Ax3(pos.Location(),gp_Dir(0,0,1));
break;
@@ -1600,27 +1705,34 @@ NEXT:
myArcPlaneFound = true;
return;
}case Area::ArcPlaneXY:
if(x0&&y0) {myArcPlaneFound=true;return;}
dstPos = gp_Ax3(pos.Location(),gp_Dir(0,0,1));
break;
case Area::ArcPlaneZX:
if(x0&&z0) {myArcPlaneFound=true;return;}
dstPos = gp_Ax3(pos.Location(),gp_Dir(0,1,0));
break;
case Area::ArcPlaneYZ:
if(z0&&y0) {myArcPlaneFound=true;return;}
dstPos = gp_Ax3(pos.Location(),gp_Dir(1,0,0));
break;
default:
return;
}
myTrsf.SetTransformation(pos,dstPos);
AREA_WARN("force arc plane " << AREA_XYZ(dir) <<
" to " << AREA_XYZ(dstPos.Direction()));
myTrsf.SetTransformation(pos);
gp_Trsf trsf;
trsf.SetTransformation(dstPos);
myTrsf.PreMultiply(trsf.Inverted());
myArcPlaneFound = true;
return;
}
};
std::list<TopoDS_Shape> Area::sortWires(const std::list<TopoDS_Shape> &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<TopoDS_Shape> wires;
@@ -1642,31 +1754,39 @@ std::list<TopoDS_Shape> Area::sortWires(const std::list<TopoDS_Shape> &shapes,
return std::move(wires);
}
RTreeParams rparams(abscissa,nearest_k>0?nearest_k:1);
std::list<ShapeInfo> 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<TopoDS_Shape> Area::sortWires(const std::list<TopoDS_Shape> &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<TopoDS_Shape> Area::sortWires(const std::list<TopoDS_Shape> &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<<", "<<xMax<<"), ("<<
yMin<<", "<<yMax<<"), ("<<zMin<<", "<<zMax<<')');
pstart.SetCoord(xMax,yMax,zMax);
}
bool has_2d5=false,has_3d=false;
while(shape_list.size()) {
AREA_TRACE("start " << shape_list.size() << ' ' << AREA_XYZ(pstart));
AREA_TRACE("sorting " << shape_list.size() << ' ' << AREA_XYZ(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){
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 || d<best_d) {
first = false;
best_it = it;
best_d = d;
}
}
if(best_it->myPlanar) {
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<TopoDS_Shape> &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<TopoDS_Shape> wires;
AreaParams params;
if(_params) params =*_params;
wires = sortWires(shapes,&params,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<TopoDS_Shape> &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<TopoDS_Shape> &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<TopoDS_Shape> &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() &&
fabs(dir.Y())<Precision::Confusion()) {
clockwise = dir.Z()<0;
arcPlane = ArcPlaneXY;
cmd = "G17";
}else if(fabs(dir.Z())<Precision::Confusion() &&
fabs(dir.X())<Precision::Confusion()){
clockwise = dir.Y()<0;
arcPlane = ArcPlaneZX;
cmd = "G18";
}else if(fabs(dir.Y())<Precision::Confusion() &&
fabs(dir.Z())<Precision::Confusion()){
clockwise = dir.X()<0;
arcPlane = ArcPlaneYZ;
cmd = "G19";
}
if(arcPlane!=ArcPlaneNone &&
(arcPlane==currentArcPlane || arc_plane==ArcPlaneVariable))
{
if(arcPlane!=currentArcPlane)
addCommand(path,cmd);
if(reversed) clockwise = !clockwise;
gp_Pnt center = circle.Location();
double first = curve.FirstParameter();
double last = curve.LastParameter();
if(segmentation > 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 ();

View File

@@ -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<double>
#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<TopoDS_Shape> 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<TopoDS_Shape> sortWires(const std::list<TopoDS_Shape> &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<TopoDS_Shape> &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)

View File

@@ -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

View File

@@ -71,13 +71,6 @@ same algorithm</UserDocu>
</Documentation>
</Methode>
<Methode Name="getParamsDesc" Keyword="true">
<Documentation>
<UserDocu>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.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="sortWires" Keyword="true">
<Documentation>
<UserDocu></UserDocu>
</Documentation>

View File

@@ -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<Base::VectorPy*>(start)->value();
pstart.SetCoord(vec.x, vec.y, vec.z);
}
std::list<TopoDS_Shape> 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 {

View File

@@ -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,&params,&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;

View File

@@ -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