Path.Area: wire sorting fix and improvement
Wire sorting no longer uses libarea. Performance improvement using boost::geometry::rtree
This commit is contained in:
@@ -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,¶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<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,¶ms,&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)
|
||||
|
||||
@@ -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 ¶ms;
|
||||
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 ¶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(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 ¶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())<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,¶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<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 ();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user