Path.Area: outline now supports solid and non-planar faces

This commit is contained in:
Zheng, Lei
2017-05-11 03:44:44 +08:00
committed by wmayer
parent b05bcbb6cb
commit 6f3104c6e3
2 changed files with 174 additions and 129 deletions

View File

@@ -140,6 +140,7 @@ Area::Area(const AreaParams *params)
,myHaveFace(false)
,myHaveSolid(false)
,myShapeDone(false)
,myProjecting(false)
{
if(params)
setParams(*params);
@@ -426,6 +427,16 @@ void Area::addToBuild(CArea &area, const TopoDS_Shape &shape) {
myParams.Coplanar==CoplanarNone?NULL:&plane,
myHaveSolid||myParams.Coplanar==CoplanarForce,&areaOpen,
myParams.OpenMode==OpenModeEdges,myParams.Reorient);
if(myProjecting) {
// when projecting, we force all wires to be CCW in order to remove
// inner holes
for(auto &c : area.m_curves) {
if(c.IsClosed() && c.IsClockwise())
c.Reverse();
}
}
if(areaOpen.m_curves.size()) {
if(&area == myArea.get() || myParams.OpenMode == OpenModeNone)
myAreaOpen->m_curves.splice(myAreaOpen->m_curves.end(),areaOpen.m_curves);
@@ -530,9 +541,8 @@ struct WireJoiner {
BRep_Builder builder;
TopoDS_Compound comp;
gp_Dir dir;
WireJoiner(const gp_Dir &dir = gp_Dir()):dir(dir) {
WireJoiner() {
builder.MakeCompound(comp);
}
@@ -552,19 +562,17 @@ struct WireJoiner {
}
void add(const TopoDS_Edge &e, bool bbox=false) {
if(BRep_Tool::IsClosed(e)){
BRepBuilderAPI_MakeWire mkWire;
mkWire.Add(e);
TopoDS_Wire wire = mkWire.Wire();
if(bbox)
Area::setWireOrientation(wire,dir,true);
builder.Add(comp,wire);
return;
}
// if(BRep_Tool::IsClosed(e)){
// BRepBuilderAPI_MakeWire mkWire;
// mkWire.Add(e);
// TopoDS_Wire wire = mkWire.Wire();
// builder.Add(comp,wire);
// return;
// }
gp_Pnt p1,p2;
getEndPoints(e,p1,p2);
if(p1.SquareDistance(p2) < Precision::SquareConfusion())
return;
// if(p1.SquareDistance(p2) < Precision::SquareConfusion())
// return;
edges.emplace_front(e,p1,p2,bbox);
add(edges.begin());
}
@@ -721,8 +729,14 @@ struct WireJoiner {
// in more than one closed wires if it connects to more than one edges.
int findClosedWires() {
std::map<int,TopoDS_Edge> edgesToVisit;
for(const auto &info : edges)
edgesToVisit[info.edge.HashCode(INT_MAX)] = info.edge;
int count = 0;
for(const auto &info : edges) {
if(BRep_Tool::IsClosed(info.edge)){
builder.Add(comp,BRepBuilderAPI_MakeWire(info.edge).Wire());
++count;
}else
edgesToVisit[info.edge.HashCode(INT_MAX)] = info.edge;
}
std::deque<StackInfo> stack;
int skips = 0;
@@ -796,11 +810,12 @@ struct WireJoiner {
mkWire.Add(TopoDS::Edge(info.edge.Reversed()));
}
TopoDS_Wire wire = mkWire.Wire();
Area::setWireOrientation(wire,dir,true);
builder.Add(comp,wire);
++count;
break;
}
}
AREA_TRACE("found " << count << " closed wires, skipped " << skips);
return skips;
}
};
@@ -833,8 +848,10 @@ void Area::explode(const TopoDS_Shape &shape) {
}
}
// enable this to show intermediate shapes during projection in FC, for
// debugging.
#if 0
static void show(const TopoDS_Shape &shape, const char *name) {
static inline void showShape(const TopoDS_Shape &shape, const char *name) {
App::Document *pcDoc = App::GetApplication().getActiveDocument();
if (!pcDoc)
pcDoc = App::GetApplication().newDocument();
@@ -844,12 +861,21 @@ static void show(const TopoDS_Shape &shape, const char *name) {
pcFeature->Shape.setValue(shape);
//pcDoc->recompute();
}
#else
#define showShape(s,name) do{(void)s;}while(0)
#endif
template<class Func>
static int foreachSubshape(const TopoDS_Shape &shape, Func func, int type=TopAbs_FACE) {
bool haveShape = false;
switch(type) {
case TopAbs_SOLID:
for(TopExp_Explorer it(shape,TopAbs_SOLID); it.More(); it.Next()) {
haveShape = true;
func(it.Current(),TopAbs_SOLID);
}
if(haveShape) return TopAbs_SOLID;
//fall through
case TopAbs_FACE:
for(TopExp_Explorer it(shape,TopAbs_FACE); it.More(); it.Next()) {
haveShape = true;
@@ -926,29 +952,32 @@ TopoDS_Shape Area::findPlane(const TopoDS_Shape &shape, gp_Trsf &trsf)
return plane;
}
std::list<TopoDS_Wire> Area::project(const TopoDS_Shape &solid)
int Area::project(TopoDS_Shape &shape_out,
const TopoDS_Shape &shape_in, const AreaParams *params)
{
TIME_INIT2(t,t1);
Handle_HLRBRep_Algo brep_hlr = NULL;
gp_Dir dir(0,0,1);
try {
brep_hlr = new HLRBRep_Algo();
brep_hlr->Add(solid, 0);
brep_hlr->Add(shape_in, 0);
HLRAlgo_Projector projector(gp_Ax2(gp_Pnt(),dir));
brep_hlr->Projector(projector);
brep_hlr->Update();
brep_hlr->Hide();
} catch (...) {
AREA_ERR("error occurred while projecting shape");
return -1;
}
TIME_PRINT(t1,"HLRBrep_Algo");
WireJoiner joiner(dir);
WireJoiner joiner;
try {
#define ADD_HLR_SHAPE(_name) \
shape = hlrToShape._name##Compound();\
if(!shape.IsNull()){\
BRepLib::BuildCurves3d(shape);\
joiner.add(shape,true);\
showShape(shape,"raw_" #_name);\
}
TopoDS_Shape shape;
HLRBRep_HLRToShape hlrToShape(brep_hlr);
@@ -959,40 +988,40 @@ std::list<TopoDS_Wire> Area::project(const TopoDS_Shape &solid)
}
catch (...) {
AREA_ERR("error occurred while extracting edges");
return -1;
}
TIME_PRINT(t1,"WireJoiner init");
joiner.splitEdges();
TIME_PRINT(t1,"WireJoiner splitEdges");
// for(const auto &v : joiner.edges) {
// joiner.builder.Add(joiner.comp,BRepBuilderAPI_MakeWire(v.edge).Wire());
// }
for(const auto &v : joiner.edges) {
// joiner.builder.Add(joiner.comp,BRepBuilderAPI_MakeWire(v.edge).Wire());
showShape(v.edge,"split");
}
int skips = joiner.findClosedWires();
TIME_PRINT(t1,"WireJoiner findClosedWires");
if(skips) AREA_WARN("skipped " << skips << " open edges");
std::list<TopoDS_Wire> wires;
Area area(&myParams);
Area area(params);
area.myParams.Explode = false;
area.myParams.FitArcs = false;
area.myParams.Reorient = false;
area.myParams.Outline = false;
area.myParams.Outline = true;
area.myParams.Fill = TopExp_Explorer(shape_in,TopAbs_FACE).More()?FillFace:FillNone;
area.myParams.Coplanar = CoplanarNone;
area.myProjecting = true;
area.add(joiner.comp, OperationUnion);
TopoDS_Shape shape = area.getShape();
const TopoDS_Shape &shape = area.getShape();
showShape(shape,"projected");
TIME_PRINT(t1,"Clipper wire union");
TIME_PRINT(t,"project total");
if(shape.IsNull()) {
AREA_ERR("poject failed");
}else{
for(TopExp_Explorer xp(shape, TopAbs_WIRE); xp.More(); xp.Next())
wires.push_back(TopoDS::Wire(xp.Current()));
return -1;
}
TIME_PRINT(t,"project total");
return std::move(wires);
shape_out = shape;
return skips;
}
std::vector<shared_ptr<Area> > Area::makeSections(
@@ -1123,14 +1152,20 @@ std::vector<shared_ptr<Area> > Area::makeSections(
std::vector<shared_ptr<Area> > sections;
sections.reserve(heights.size());
std::list<Shape> projectedShapes;
if(project) {
projectedShapes = getProjectedShapes(trsf,false);
if(projectedShapes.empty()) {
AREA_ERR("empty projection");
return sections;
}
}
tolerance *= 2.0;
bool can_retry = fabs(tolerance)>Precision::Confusion();
TopLoc_Location locInverse(loc.Inverted());
std::vector<Shape> projectedShapes;
if(project) projectedShapes.reserve(myShapes.size());
bool aborted = false;
for(double z : heights) {
bool retried = !can_retry;
while(true) {
@@ -1140,10 +1175,11 @@ std::vector<shared_ptr<Area> > Area::makeSections(
BRepLib_MakeFace mkFace(pln,xMin,xMax,yMin,yMax);
const TopoDS_Shape &face = mkFace.Face();
shared_ptr<Area> area(new Area(&myParams));
shared_ptr<Area> area(std::make_shared<Area>(&myParams));
area->myParams.Outline = false;
area->setPlane(face.Moved(locInverse));
if(projectedShapes.size()) {
if(project) {
for(const auto &s : projectedShapes) {
gp_Trsf t;
t.SetTranslation(gp_Vec(0,0,-d));
@@ -1152,100 +1188,81 @@ std::vector<shared_ptr<Area> > Area::makeSections(
}
sections.push_back(area);
break;
}else{
for(auto it=myShapes.begin();it!=myShapes.end();++it) {
const auto &s = *it;
BRep_Builder builder;
TopoDS_Compound comp;
builder.MakeCompound(comp);
}
for(TopExp_Explorer xp(s.shape.Moved(loc), TopAbs_SOLID); xp.More(); xp.Next()) {
std::list<TopoDS_Wire> wires;
if(project) {
wires = this->project(xp.Current());
}else{
Part::CrossSection section(a,b,c,xp.Current());
wires = section.slice(-d);
}
for(auto it=myShapes.begin();it!=myShapes.end();++it) {
const auto &s = *it;
BRep_Builder builder;
TopoDS_Compound comp;
builder.MakeCompound(comp);
if(wires.empty()) {
AREA_LOG("Section returns no wires");
continue;
}
for(TopExp_Explorer xp(s.shape.Moved(loc), TopAbs_SOLID); xp.More(); xp.Next()) {
std::list<TopoDS_Wire> wires;
Part::CrossSection section(a,b,c,xp.Current());
wires = section.slice(-d);
if(wires.empty()) {
AREA_LOG("Section returns no wires");
continue;
}
// always try to make face to normalize wire orientation
Part::FaceMakerBullseye mkFace;
mkFace.setPlane(pln);
for(const TopoDS_Wire &wire : wires) {
if(BRep_Tool::IsClosed(wire))
mkFace.addWire(wire);
}
try {
mkFace.Build();
const TopoDS_Shape &shape = mkFace.Shape();
if (shape.IsNull())
AREA_WARN("FaceMakerBullseye return null shape on section");
else {
for(auto it=wires.begin(),itNext=it;it!=wires.end();it=itNext) {
++itNext;
if(BRep_Tool::IsClosed(*it))
wires.erase(it);
}
for(TopExp_Explorer xp(shape,myParams.Fill==FillNone?TopAbs_WIRE:TopAbs_FACE);
xp.More();xp.Next())
{
builder.Add(comp,xp.Current());
}
// always try to make face to normalize wire orientation
Part::FaceMakerBullseye mkFace;
mkFace.setPlane(pln);
for(const TopoDS_Wire &wire : wires) {
if(BRep_Tool::IsClosed(wire))
mkFace.addWire(wire);
}
try {
mkFace.Build();
const TopoDS_Shape &shape = mkFace.Shape();
if (shape.IsNull())
AREA_WARN("FaceMakerBullseye return null shape on section");
else {
for(auto it=wires.begin(),itNext=it;it!=wires.end();it=itNext) {
++itNext;
if(BRep_Tool::IsClosed(*it))
wires.erase(it);
}
for(TopExp_Explorer xp(shape,myParams.Fill==FillNone?TopAbs_WIRE:TopAbs_FACE);
xp.More();xp.Next())
{
builder.Add(comp,xp.Current());
}
}catch (Base::Exception &e){
AREA_WARN("FaceMakerBullseye failed on section: " << e.what());
}
for(const TopoDS_Wire &wire : wires)
builder.Add(comp,wire);
}catch (Base::Exception &e){
AREA_WARN("FaceMakerBullseye failed on section: " << e.what());
}
for(const TopoDS_Wire &wire : wires)
builder.Add(comp,wire);
}
// Make sure the compound has at least one edge
TopExp_Explorer xp(comp,TopAbs_EDGE);
if(xp.More()) {
if(project){
projectedShapes.push_back(Shape(s.op,comp));
gp_Trsf t;
t.SetTranslation(gp_Vec(0,0,-d));
TopLoc_Location wloc(t);
area->add(comp.Moved(wloc).Moved(locInverse),s.op);
}else
area->add(comp.Moved(locInverse),s.op);
}else if(area->myShapes.empty()){
auto itNext = it;
if(++itNext != myShapes.end() &&
(itNext->op==OperationIntersection ||
itNext->op==OperationDifference))
{
break;
}
// Make sure the compound has at least one edge
if(TopExp_Explorer(comp,TopAbs_EDGE).More())
area->add(comp.Moved(locInverse),s.op);
else if(area->myShapes.empty()){
auto itNext = it;
if(++itNext != myShapes.end() &&
(itNext->op==OperationIntersection ||
itNext->op==OperationDifference))
{
break;
}
}
if(area->myShapes.size()){
sections.push_back(area);
break;
}
if(project) {
AREA_ERR("empty projection");
aborted = true;
}
if(retried) {
AREA_WARN("Discard empty section");
break;
}else{
AREA_TRACE("retry section " <<z<<"->"<<z+tolerance);
z += tolerance;
retried = true;
}
}
TIME_PRINT(t1,"makeSection " << z);
if(area->myShapes.size()){
sections.push_back(area);
TIME_PRINT(t1,"makeSection " << z);
break;
}
if(retried) {
AREA_WARN("Discard empty section");
break;
}else{
AREA_TRACE("retry section " <<z<<"->"<<z+tolerance);
z += tolerance;
retried = true;
}
}
if(aborted) break;
}
TIME_PRINT(t,"makeSection count: " << sections.size()<<", total");
return std::move(sections);
@@ -1273,6 +1290,28 @@ bool Area::isBuilt() const {
return (myArea || mySections.size());
}
std::list<Area::Shape> Area::getProjectedShapes(const gp_Trsf &trsf, bool inverse) const
{
std::list<Shape> ret;
TopLoc_Location loc(trsf);
TopLoc_Location locInverse(loc.Inverted());
mySkippedShapes = 0;
for(auto &s : myShapes) {
TopoDS_Shape out;
int skipped = Area::project(out,s.shape.Moved(loc),&myParams);
if(skipped < 0) {
++mySkippedShapes;
continue;
}else
mySkippedShapes += skipped;
if(!out.IsNull())
ret.emplace_back(s.op,inverse?out.Moved(locInverse):out);
}
if(mySkippedShapes)
AREA_WARN("skipped " << mySkippedShapes << " sub shapes during projection");
return ret;
}
void Area::build() {
if(isBuilt()) return;
@@ -1289,7 +1328,8 @@ void Area::build() {
}
TIME_INIT(t);
getPlane();
gp_Trsf trsf;
getPlane(&trsf);
try {
myArea.reset(new CArea());
@@ -1302,7 +1342,8 @@ void Area::build() {
short op = OperationUnion;
bool pending = false;
bool exploding = myParams.Explode;
for(const Shape &s : myShapes) {
const auto &shapes = (myParams.Outline&&!myProjecting)?getProjectedShapes(trsf):myShapes;
for(const Shape &s : shapes) {
if(exploding) {
exploding = false;
explode(s.shape);

View File

@@ -125,6 +125,7 @@ inline std::chrono::TIME_UNIT getDuration(TIME_POINT &t)
class CArea;
class CCurve;
class Bnd_Box;
namespace Path
{
@@ -213,7 +214,8 @@ protected:
bool myHaveFace;
bool myHaveSolid;
bool myShapeDone;
int mySkippedShapes;
bool myProjecting;
mutable int mySkippedShapes;
static bool s_aborting;
static AreaStaticParams s_params;
@@ -246,7 +248,7 @@ protected:
TopoDS_Shape findPlane(const TopoDS_Shape &shape, gp_Trsf &trsf);
std::list<TopoDS_Wire> project(const TopoDS_Shape &solid);
std::list<Shape> getProjectedShapes(const gp_Trsf &trsf, bool inverse=true) const;
public:
/** Declare all parameters defined in #AREA_PARAMS_ALL as member variable */
@@ -279,7 +281,7 @@ public:
* If no workplane is set using setPlane(), the active workplane is derived from
* the added children shapes using the same algorithm empolyed by setPlane().
*/
TopoDS_Shape getPlane(gp_Trsf *trsf = NULL);
TopoDS_Shape getPlane(gp_Trsf *trsf=0);
/** Add a child shape with given operation code
*
@@ -446,6 +448,8 @@ public:
const gp_Pnt *pstart=NULL, gp_Pnt *pend=NULL,
PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_PATH));
static int project(TopoDS_Shape &out, const TopoDS_Shape &in, const AreaParams *params=0);
static void setWireOrientation(TopoDS_Wire& wire, const gp_Dir &dir, bool ccw);
PARAM_ENUM_DECLARE(AREA_PARAMS_PATH)