Part: TopoShape: add makeOffset2D method

Supports:
* offsetting wire, compound of wires.
* offsetting compounds in collective or independent manners
('intersection' parameter)
* two offset modes (parameter 'allowOpenResult')
* filling offset when offsetting one wire, or compound in independent
manner

Not supported yet (planned):
* filling offset when offset in collective manner (intersection= true)
* offsetting planar faces
This commit is contained in:
DeepSOIC 2016-09-02 00:33:57 +03:00 committed by wmayer
parent c0428a940b
commit 5318e03561
2 changed files with 323 additions and 1 deletions

View File

@ -44,6 +44,7 @@
# include <BRepAlgo_Fuse.hxx>
# include <BRepAlgoAPI_Section.hxx>
# include <BRepBndLib.hxx>
# include <BRepBuilderAPI_FindPlane.hxx>
# include <BRepBuilderAPI_GTransform.hxx>
# include <BRepBuilderAPI_MakeEdge.hxx>
# include <BRepBuilderAPI_MakeFace.hxx>
@ -66,6 +67,7 @@
# include <BRepMesh_Edge.hxx>
# include <BRepOffsetAPI_MakeThickSolid.hxx>
# include <BRepOffsetAPI_MakeOffsetShape.hxx>
# include <BRepOffsetAPI_MakeOffset.hxx>
# include <BRepOffsetAPI_MakePipe.hxx>
# include <BRepOffsetAPI_MakePipeShell.hxx>
# include <BRepOffsetAPI_Sewing.hxx>
@ -75,6 +77,7 @@
# include <BRepTools.hxx>
# include <BRepTools_ReShape.hxx>
# include <BRepTools_ShapeSet.hxx>
# include <BRepTools_WireExplorer.hxx>
# include <BRepFill_CompatibleWires.hxx>
# include <GCE2d_MakeSegment.hxx>
# include <GCPnts_AbscissaPoint.hxx>
@ -92,7 +95,6 @@
# include <GeomLib.hxx>
# include <Law_BSpFunc.hxx>
# include <Law_BSpline.hxx>
# include <TopTools_HSequenceOfShape.hxx>
# include <Law_BSpFunc.hxx>
# include <Law_Constant.hxx>
# include <Law_Linear.hxx>
@ -140,6 +142,7 @@
# include <gp_GTrsf.hxx>
# include <ShapeAnalysis_Shell.hxx>
# include <ShapeBuild_ReShape.hxx>
# include <ShapeExtend_Explorer.hxx>
# include <ShapeFix_Edge.hxx>
# include <ShapeFix_Face.hxx>
# include <ShapeFix_Shell.hxx>
@ -2061,6 +2064,323 @@ TopoDS_Shape TopoShape::makeOffsetShape(double offset, double tol, bool intersec
return outputShape;
}
TopoDS_Shape TopoShape::makeOffset2D(double offset, short joinType, bool fill, bool allowOpenResult, bool intersection) const
{
if (_Shape.IsNull())
throw Base::ValueError("makeOffset2D: input shape is null!");
if (fill && intersection)
throw Base::ValueError("Filling offset when 'intersection' is true is not supported yet.");
if (allowOpenResult && OCC_VERSION_HEX < 0x060900)
throw Base::AttributeError("openResult argument is not supported on OCC < 6.9.0.");
switch (_Shape.ShapeType()) {
case TopAbs_COMPOUND:{
BRep_Builder builder;
TopoDS_Compound comp;//to be returned
builder.MakeCompound(comp);
if (!intersection){
//simply recursively process the children, independently
TopoDS_Iterator it(_Shape);
for( ; it.More() ; it.Next()){
builder.Add(comp, TopoShape(it.Value()).makeOffset2D(offset, joinType, fill, allowOpenResult, intersection));
}
} else {
//collect all wires from this compound for collective offset. Process other shapes independently.
std::list<TopoDS_Wire> wiresToOffset;
TopoDS_Iterator it(_Shape);
for( ; it.More() ; it.Next()){
if(it.Value().ShapeType() == TopAbs_WIRE){
wiresToOffset.push_back(TopoDS::Wire(it.Value()));
} else if (it.Value().ShapeType() == TopAbs_EDGE){
wiresToOffset.push_back(BRepBuilderAPI_MakeWire(TopoDS::Edge(it.Value())).Wire());
} else {
builder.Add(comp, TopoShape(it.Value()).makeOffset2D(offset, joinType, fill, allowOpenResult, intersection));
}
}
//check if we have more than two wires for a collective offset. Otherwise, fall back to recursive calling.
if (wiresToOffset.size() == 0){
//nothing to do
} else if (wiresToOffset.size() == 1) {
builder.Add(comp, TopoShape(wiresToOffset.front()).makeOffset2D(offset, joinType, fill, allowOpenResult, intersection));
} else {
//collective offset
BRepOffsetAPI_MakeOffset mkOffset(wiresToOffset.front(), GeomAbs_JoinType(joinType)
#if OCC_VERSION_HEX >= 0x060900
, allowOpenResult
#endif
);
for(TopoDS_Wire &w : wiresToOffset){
if (&w == &wiresToOffset.front())
continue;
mkOffset.AddWire(w);
}
if (fabs(offset) > Precision::Confusion()){
try {
#if defined(__GNUC__) && defined (FC_OS_LINUX)
Base::SignalException se;
#endif
mkOffset.Perform(offset);
}
catch (Standard_Failure &){
throw;
}
catch (...) {
throw Base::Exception("BRepOffsetAPI_MakeOffset has crashed! (Unknown exception caught)");
}
TopoDS_Shape offsetWire = mkOffset.Shape();
if (offsetWire.IsNull())
throw Base::Exception("makeOffset2D: result of offset is null!");
ShapeExtend_Explorer xp; //using this explorer allows to avoid checking output type
Handle_TopTools_HSequenceOfShape seq = xp.SeqFromCompound(offsetWire, /*recursive*/ true);
for(int i = 0 ; i < seq->Length() ; ++i){
builder.Add(comp, seq->Value(i+1));
}
} else {
//zero offset, dump all wires straight through...
for(TopoDS_Wire &w : wiresToOffset){
builder.Add(comp, w);
}
}
}
}
return comp;
}break;
case TopAbs_EDGE:
case TopAbs_WIRE:{
//convert edge to a wire if necessary...
TopoDS_Wire sourceWire;
if (_Shape.ShapeType() == TopAbs_WIRE){
sourceWire = TopoDS::Wire(_Shape);
} else { //edge
sourceWire = BRepBuilderAPI_MakeWire(TopoDS::Edge(_Shape)).Wire();
}
//do the offset..
TopoDS_Shape offsetWire;
BRepOffsetAPI_MakeOffset mkOffset(sourceWire, GeomAbs_JoinType(joinType)
#if OCC_VERSION_HEX >= 0x060900
, allowOpenResult
#endif
);
if (fabs(offset) > Precision::Confusion()){
try {
#if defined(__GNUC__) && defined (FC_OS_LINUX)
Base::SignalException se;
#endif
mkOffset.Perform(offset);
}
catch (Standard_Failure &){
throw;
}
catch (...) {
throw Base::Exception("BRepOffsetAPI_MakeOffset has crashed! (Unknown exception caught)");
}
offsetWire = mkOffset.Shape();
} else {
offsetWire = sourceWire;
}
if(offsetWire.IsNull())
throw Base::Exception("makeOffset2D: result of offseting is null!");
if (!fill)
return offsetWire;
if (fabs(offset) < Precision::Confusion())
throw Base::ValueError("makeOffset2D: offset distance is zero. Can't fill offset.");
//Fill offset...
BRepBuilderAPI_FindPlane planefinder(sourceWire);
if (!planefinder.Found()){
// non-planar wire.
throw Base::Exception("Strange, but offset worked on a non-planar wire. Filling is not supported.");
}
//Planar wire. Make planar face...
//first up, the offset wire can be a compound. Let's break it up
std::list<TopoDS_Wire> wires;
if (offsetWire.ShapeType() == TopAbs_COMPOUND){
TopoDS_Iterator it(offsetWire);
for(; it.More(); it.Next()){
wires.push_back(TopoDS::Wire(it.Value()));
}
} else if (offsetWire.ShapeType() == TopAbs_WIRE) {
wires.push_back(TopoDS::Wire(offsetWire));
}
if(wires.size() == 0)
throw Base::Exception("makeOffset2D: offset result has no wires.");
//For the face, we also need the original wire. And we need to tell apart the outer wire for the face.
TopoDS_Wire* largestWire = nullptr;
bool sourceWireIsClosed = BRep_Tool::IsClosed(sourceWire);
if (sourceWireIsClosed && offset < 0){
//in this case, the original wire is the outer wire of the face
wires.push_front(sourceWire);
largestWire = &wires.front();
} else if (sourceWireIsClosed || !allowOpenResult) {
//Source wire may be closed or not, but the offset wire(s) is closed.
//find largest wire. It will be the outer wire of the face
double largestSizeSeenSoFar = -1.0;
for (TopoDS_Wire &w : wires){
Bnd_Box bb;
BRepBndLib::Add(w, bb);
if (bb.SquareExtent() > largestSizeSeenSoFar){
largestWire = &w;
largestSizeSeenSoFar = bb.SquareExtent();
}
}
//add source wire to the list.
if (BRep_Tool::IsClosed(sourceWire)){
wires.push_back(TopoDS::Wire(sourceWire));
}
} else {
//source wire, and one of the offset wires are open.
//find the open offset wire
TopoDS_Wire openOffsetWire;
for( std::list<TopoDS_Wire>::iterator it = wires.begin() ; it != wires.end() ; ++it){
if (!BRep_Tool::IsClosed(*it)){
openOffsetWire = *it;
wires.erase(it);
break;
}
}
if(openOffsetWire.IsNull())
throw Base::Exception("makeOffset2D: filling offset: expected to find an open wire in offset result, but there isn't one.");
//join up the (open) source wire and open offset wire. This will be
//the outer wire for the face. The remaining wires are holes.
//find open vertices of source wire
BRepTools_WireExplorer xp;
xp.Init(openOffsetWire);
TopoDS_Vertex v1 = xp.CurrentVertex();
for(;xp.More();xp.Next()){};
TopoDS_Vertex v2 = xp.CurrentVertex();
//find open vertices of offset wire
xp.Init(sourceWire);
TopoDS_Vertex v3 = xp.CurrentVertex();
for(;xp.More();xp.Next()){};
TopoDS_Vertex v4 = xp.CurrentVertex();
//check
if (v1.IsNull()) throw Base::Exception("v1 is null");
if (v2.IsNull()) throw Base::Exception("v2 is null");
if (v3.IsNull()) throw Base::Exception("v3 is null");
if (v4.IsNull()) throw Base::Exception("v4 is null");
//assemble new wire
// hack. It seems that direction of offset wire and closed offset
// wires is always consistent for good facemaking, but does not
// care of the direction of original wire. So, we will reverse
// original wire if necessary.
//we want the connection order to be
//v1 -> openOffsetWire -> v2 -> (new edge) -> v4 -> sourceWire(rev) -> v3 -> (new edge) -> v1
//let's check if it's the case. If not, we reverse source wire and swap its endpoints.
// I tried to use mkOffset.Generated(v3) for the purpose, but
//returned was an empty list. So I find vertex correspondence by
//testing if the distance between them is equal to offset. --DeepSOIC
if (fabs(gp_Vec(BRep_Tool::Pnt(v2), BRep_Tool::Pnt(v3)).Magnitude() - fabs(offset)) <= BRep_Tool::Tolerance(v2) + BRep_Tool::Tolerance(v3)){
sourceWire.Reverse();
std::swap(v3, v4);
v3.Reverse();
v4.Reverse();
} else if ((fabs(gp_Vec(BRep_Tool::Pnt(v2), BRep_Tool::Pnt(v4)).Magnitude() - fabs(offset)) <= BRep_Tool::Tolerance(v2) + BRep_Tool::Tolerance(v4))){
//orientation is as expected, nothing to do
} else {
throw Base::Exception("makeOffset2D: fill offset: failed to establish open vertex relationship.");
}
//now directions of source wire and offset wire are aligned. Finally. make new wire!
BRepBuilderAPI_MakeWire mkWire;
//add openOffsetWire
BRepTools_WireExplorer it;
for(it.Init(openOffsetWire); it.More(); it.Next()){
mkWire.Add(it.Current());
}
//add first joining edge
mkWire.Add(BRepBuilderAPI_MakeEdge(v2,v4).Edge());
//add original wire, in reverse order
sourceWire.Reverse();
for(it.Init(TopoDS::Wire(sourceWire)); it.More(); it.Next()){
mkWire.Add(it.Current());
}
//add final joining edge
mkWire.Add(BRepBuilderAPI_MakeEdge(v3,v1).Edge());
mkWire.Build();
wires.push_front(TopoDS::Wire(mkWire.Wire().Reversed())); //not sure, why need reversing here. Found by trial-and-error
largestWire = &wires.front();
}
//make the face
//TODO: replace all this reverseness alchemy with a common direction-tolerant face-with-holes-making code
BRepBuilderAPI_MakeFace mkFace(*largestWire);
for(TopoDS_Wire &w : wires){
if (&w != largestWire)
mkFace.Add(TopoDS::Wire(w.Reversed()));
}
mkFace.Build();
if (mkFace.Shape().IsNull())
throw Base::Exception("makeOffset2D: making face failed (null shape returned).");
return mkFace.Shape();
}break;
case TopAbs_FACE:{
throw Base::TypeError("2d offsetting is not yet suported on faces, yet.");
//the following code works, but returns a wire. I'd rather want a face,
//but that is complicated, and best addressed by writing a powerful
//face-with-holes-maker mentioned a few lines above. Exposing it like
//this will cause breaking changes later, so I decided to disable it
//altogether, until a proper implementation is done.
// --DeepSOIC
TopoDS_Face sourceFace = TopoDS::Face(_Shape);
BRepOffsetAPI_MakeOffset mkOffset(sourceFace, GeomAbs_JoinType(joinType)
#if OCC_VERSION_HEX >= 0x060900
, allowOpenResult
#endif
);
try {
#if defined(__GNUC__) && defined (FC_OS_LINUX)
Base::SignalException se;
#endif
mkOffset.Perform(offset);
}
catch (Standard_Failure &){
throw;
}
catch (...) {
throw Base::Exception("BRepOffsetAPI_MakeOffset has crashed! (Unknown exception caught)");
}
if (mkOffset.Shape().IsNull())
throw Base::Exception("makeOffset2D: result shape is null!");
if (fill)
throw Base::ValueError("Filling the offset is not supported for 2d-offsetting of faces, yet.");
return mkOffset.Shape();
}break;
default:
throw Base::TypeError("makeOffset2D: input shape is not an edge, wire or face or compound of those.");
break;
}
}
TopoDS_Shape TopoShape::makeThickSolid(const TopTools_ListOfShape& remFace,
double offset, double tol, bool intersection,
bool selfInter, short offsetMode, short join) const

View File

@ -207,6 +207,8 @@ public:
TopoDS_Shape makeOffsetShape(double offset, double tol,
bool intersection = false, bool selfInter = false,
short offsetMode = 0, short join = 0, bool fill = false) const;
TopoDS_Shape makeOffset2D(double offset, short joinType = 0,
bool fill = false, bool allowOpenResult = false, bool intersection = false) const;
TopoDS_Shape makeThickSolid(const TopTools_ListOfShape& remFace,
double offset, double tol,
bool intersection = false, bool selfInter = false,