Loading files
f2929057eThis file is pinned to the selected release. Download the original to open it in your design tool.
// Created on: 2015-04-24
// Created by: NIKOLAI BUKHALOV
// Copyright (c) 2015 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#include <BRepBuilderAPI_FastSewing.hxx>
#include <BRepTools_Quilt.hxx>
#include <Bnd_Box.hxx>
#include <Geom2d_Line.hxx>
#include <Geom2d_TrimmedCurve.hxx>
#include <Geom_Curve.hxx>
#include <Geom_RectangularTrimmedSurface.hxx>
#include <Geom_Surface.hxx>
#include <Precision.hxx>
#include <Standard_NullObject.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Shape.hxx>
#include <TopTools_ShapeMapHasher.hxx>
#include <NCollection_Map.hxx>
IMPLEMENT_STANDARD_RTTIEXT(BRepBuilderAPI_FastSewing, Standard_Transient)
//=======================================================================
// function : IntersetctionOfSets
// purpose : Returns minimal value of intersection result
//=======================================================================
static int IntersectionOfSets(const NCollection_List<int>& theSet1,
const NCollection_List<int>& theSet2)
{
constexpr int anIntMax = IntegerLast();
int aRetVal = anIntMax;
for (NCollection_List<int>::Iterator anIt1 = theSet1.begin().Iterator(); anIt1.More();
anIt1.Next())
{
const int aVal1 = anIt1.Value();
for (NCollection_List<int>::Iterator anIt2 = theSet2.begin().Iterator(); anIt2.More();
anIt2.Next())
{
const int aVal2 = anIt2.Value();
if (aVal1 == aVal2)
{
// theIntersectionResult.Append(aVal1);
if (aVal1 < aRetVal)
{
aRetVal = aVal1;
}
}
}
}
if (aRetVal == anIntMax)
{
return -1;
}
return aRetVal;
}
//=================================================================================================
static occ::handle<Geom2d_Curve> Get2DCurve(const int theIndex,
const double theUfirst,
const double theUlast,
const double theVfirst,
const double theVlast,
const bool theIsReverse = false)
{
if ((theIndex < 0) || (theIndex > 3))
{
throw Standard_OutOfRange("BRepBuilderAPI_FastSewing.cxx, Get2DCurve(): OUT of Range");
}
occ::handle<Geom2d_Curve> a2dCurv;
if (!theIsReverse)
{
switch (theIndex)
{
case 0:
a2dCurv = new Geom2d_TrimmedCurve(
new Geom2d_Line(gp_Pnt2d(0.0, theVfirst), gp_Dir2d(gp_Dir2d::D::X)),
theUfirst,
theUlast);
break;
case 1:
a2dCurv = new Geom2d_TrimmedCurve(
new Geom2d_Line(gp_Pnt2d(theUlast, 0.0), gp_Dir2d(gp_Dir2d::D::Y)),
theVfirst,
theVlast);
break;
case 2:
a2dCurv = new Geom2d_TrimmedCurve(
new Geom2d_Line(gp_Pnt2d(0.0, theVlast), gp_Dir2d(gp_Dir2d::D::X)),
theUfirst,
theUlast);
break;
case 3:
a2dCurv = new Geom2d_TrimmedCurve(
new Geom2d_Line(gp_Pnt2d(theUfirst, 0.0), gp_Dir2d(gp_Dir2d::D::Y)),
theVfirst,
theVlast);
break;
default:
break;
}
}
else
{
switch (theIndex)
{
case 0:
a2dCurv = new Geom2d_TrimmedCurve(
new Geom2d_Line(gp_Pnt2d(theUfirst + theUlast, theVfirst), gp_Dir2d(gp_Dir2d::D::NX)),
theUfirst,
theUlast);
break;
case 1:
a2dCurv = new Geom2d_TrimmedCurve(
new Geom2d_Line(gp_Pnt2d(theUlast, theVfirst + theVlast), gp_Dir2d(gp_Dir2d::D::NY)),
theVfirst,
theVlast);
break;
case 2:
a2dCurv = new Geom2d_TrimmedCurve(
new Geom2d_Line(gp_Pnt2d(theUfirst + theUlast, theVlast), gp_Dir2d(gp_Dir2d::D::NX)),
theUfirst,
theUlast);
break;
case 3:
a2dCurv = new Geom2d_TrimmedCurve(
new Geom2d_Line(gp_Pnt2d(theUfirst, theVfirst + theVlast), gp_Dir2d(gp_Dir2d::D::NY)),
theVfirst,
theVlast);
break;
default:
break;
}
}
return a2dCurv;
}
//=================================================================================================
BRepBuilderAPI_FastSewing::BRepBuilderAPI_FastSewing(const double theTol)
: myTolerance(theTol),
myStatusList(0)
{
}
//=================================================================================================
bool BRepBuilderAPI_FastSewing::Add(const TopoDS_Shape& theShape)
{
bool aResult = false;
if (theShape.IsNull())
{
SetStatus(FS_EmptyInput);
return aResult;
}
NCollection_Map<TopoDS_Shape, TopTools_ShapeMapHasher> aMS;
// aMS.Add(theShape);
TopExp_Explorer aFExp(theShape, TopAbs_FACE);
for (; aFExp.More(); aFExp.Next())
{
const TopoDS_Face& aFace = TopoDS::Face(aFExp.Current());
if (aMS.Add(aFace))
{
occ::handle<Geom_Surface> aSurf = BRep_Tool::Surface(aFace);
if (aSurf.IsNull())
{
SetStatus(FS_FaceWithNullSurface);
continue;
}
if (aSurf->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface)))
{
SetStatus(FS_NotNaturalBoundsFace);
continue;
}
double aUf = 0.0, aUl = 0.0, aVf = 0.0, aVl = 0.0;
aSurf->Bounds(aUf, aUl, aVf, aVl);
if (Precision::IsInfinite(aUf) || Precision::IsInfinite(aUl) || Precision::IsInfinite(aVf)
|| Precision::IsInfinite(aVl))
{
SetStatus(FS_InfiniteSurface);
continue;
}
FS_Face aFFace;
aFFace.mySrcFace = aFace;
aFFace.myID = myFaceVec.Length(); // because start index is 0
myFaceVec.Append(aFFace);
aResult = true;
}
}
return aResult;
}
//=================================================================================================
bool BRepBuilderAPI_FastSewing::Add(const occ::handle<Geom_Surface>& theSurface)
{
if (theSurface.IsNull())
{
SetStatus(FS_FaceWithNullSurface);
return false;
}
if (theSurface->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface)))
{
SetStatus(FS_NotNaturalBoundsFace);
return false;
}
double aUf = 0.0, aUl = 0.0, aVf = 0.0, aVl = 0.0;
theSurface->Bounds(aUf, aUl, aVf, aVl);
if (Precision::IsInfinite(aUf) || Precision::IsInfinite(aUl) || Precision::IsInfinite(aVf)
|| Precision::IsInfinite(aVl))
{
SetStatus(FS_InfiniteSurface);
return false;
}
FS_Face aFace;
BRep_Builder aBuilder;
aBuilder.MakeFace(aFace.mySrcFace);
aBuilder.MakeFace(aFace.mySrcFace, theSurface, myTolerance);
aBuilder.NaturalRestriction(aFace.mySrcFace, true);
aFace.myID = myFaceVec.Length(); // because start index is 0
myFaceVec.Append(aFace);
return true;
}
//=================================================================================================
void BRepBuilderAPI_FastSewing::Perform()
{
if (myFaceVec.IsEmpty())
{
SetStatus(FS_EmptyInput);
return;
}
try
{
{
// create vertices having unique coordinates
double aRange = Compute3DRange();
occ::handle<NCollection_IncAllocator> anAlloc = new NCollection_IncAllocator;
NCollection_CellFilter<NodeInspector> aCells(std::max(myTolerance, aRange / IntegerLast()),
anAlloc);
for (int i = myFaceVec.Lower(); i <= myFaceVec.Upper(); i++)
{
FindVertexes(i, aCells);
}
}
for (int i = myFaceVec.Lower(); i <= myFaceVec.Upper(); i++)
{
FindEdges(i);
}
// Create topological structures
for (int i = myVertexVec.Lower(); i <= myVertexVec.Upper(); i++)
{
myVertexVec.ChangeValue(i).CreateTopologicalVertex(myTolerance);
}
// Edges
for (int i = myEdgeVec.Lower(); i <= myEdgeVec.Upper(); i++)
{
myEdgeVec.ChangeValue(i).CreateTopologicalEdge(myVertexVec, myFaceVec, myTolerance);
}
// Shell
BRepTools_Quilt aQuilt;
// Faces
for (int i = myFaceVec.Lower(); i <= myFaceVec.Upper(); i++)
{
FS_Face& aFace = myFaceVec.ChangeValue(i);
aFace.CreateTopologicalWire(myEdgeVec, myTolerance);
aFace.CreateTopologicalFace();
aQuilt.Add(aFace.myRetFace);
}
myResShape = aQuilt.Shells();
}
catch (Standard_Failure const&)
{
SetStatus(FS_Exception);
#ifdef OCCT_DEBUG
// Standard_Failure::Caught()->Print(std::cout);
#endif
return;
}
}
//=================================================================================================
void BRepBuilderAPI_FastSewing::UpdateEdgeInfo(const int theIDPrevVertex,
const int theIDCurrVertex,
const int theFaceID,
const int theIDCurvOnFace)
{
// Indeed, two vertices combine into one edge only.
const int anEdgeID = IntersectionOfSets(myVertexVec.Value(theIDPrevVertex).myEdges,
myVertexVec.Value(theIDCurrVertex).myEdges);
// For DEBUG mode only
Standard_ProgramError_Raise_if(
anEdgeID < 0,
"BRepBuilderAPI_FastSewing::UpdateEdgeInfo: Update not existing edge.");
FS_Edge& anEdge = myEdgeVec.ChangeValue(anEdgeID);
anEdge.myFaces.Append(theFaceID);
FS_Face& aFace = myFaceVec.ChangeValue(theFaceID);
aFace.SetEdge(theIDCurvOnFace, anEdge.myID);
}
//=================================================================================================
void BRepBuilderAPI_FastSewing::CreateNewEdge(const int theIDPrevVertex,
const int theIDCurrVertex,
const int theFaceID,
const int theIDCurvOnFace)
{
FS_Edge anEdge(theIDPrevVertex, theIDCurrVertex);
anEdge.myID = myEdgeVec.Length(); // because start index is 0
anEdge.myFaces.Append(theFaceID);
FS_Face& aFace = myFaceVec.ChangeValue(theFaceID);
aFace.SetEdge(theIDCurvOnFace, anEdge.myID);
myVertexVec.ChangeValue(theIDPrevVertex).myEdges.Append(anEdge.myID);
if (theIDPrevVertex == theIDCurrVertex)
{ // the Edge is degenerated
SetStatus(FS_Degenerated);
}
else
{
myVertexVec.ChangeValue(theIDCurrVertex).myEdges.Append(anEdge.myID);
}
myEdgeVec.Append(anEdge);
}
//=================================================================================================
void BRepBuilderAPI_FastSewing::FindVertexes(const int theSurfID,
NCollection_CellFilter<NodeInspector>& theCells)
{
const int aNbPoints = 4;
FS_Face& aFace = myFaceVec.ChangeValue(theSurfID);
const occ::handle<Geom_Surface> aSurf = BRep_Tool::Surface(aFace.mySrcFace);
double aUf = 0.0, aUl = 0.0, aVf = 0.0, aVl = 0.0;
aSurf->Bounds(aUf, aUl, aVf, aVl);
const gp_Pnt aPnts[aNbPoints] = {aSurf->Value(aUf, aVf),
aSurf->Value(aUl, aVf),
aSurf->Value(aUl, aVl),
aSurf->Value(aUf, aVl)};
for (int i = 0; i < aNbPoints; i++)
{
FS_Vertex aVert;
NodeInspector anInspector(myVertexVec, aPnts[i], myTolerance);
Bnd_Box aBox;
aBox.Add(aPnts[i]);
aBox.Enlarge(myTolerance);
theCells.Inspect(aBox.CornerMin().XYZ(), aBox.CornerMax().XYZ(), anInspector);
NodeInspector::Target aResID = anInspector.GetResult();
if (aResID < 0)
{ // Add new Vertex
aVert.myID = myVertexVec.Length(); // because start index is 0
aVert.myPnt = aPnts[i];
aVert.myFaces.Append(theSurfID);
myVertexVec.Append(aVert);
aFace.SetVertex(i, aVert.myID);
theCells.Add(aVert.myID, aBox.CornerMin().XYZ(), aBox.CornerMax().XYZ());
}
else
{ // Change existing vertex
aFace.SetVertex(i, aResID);
myVertexVec.ChangeValue(aResID).myFaces.Append(theSurfID);
}
}
}
//=================================================================================================
void BRepBuilderAPI_FastSewing::FindEdges(const int theSurfID)
{
const int aNbPoints = 4;
FS_Face& aFace = myFaceVec.ChangeValue(theSurfID);
const int aFirstInd[aNbPoints] = {0, 1, 3, 0};
const int aLastInd[aNbPoints] = {1, 2, 2, 3};
for (int i = 0; i < aNbPoints; i++)
{
const int aFirstVertIndex = aFirstInd[i], aLastVertIndex = aLastInd[i];
const int aFirstVertID = aFace.myVertices[aFirstVertIndex],
aLastVertID = aFace.myVertices[aLastVertIndex];
if (aFirstVertID == aLastVertID)
{ // Edge is degenerated.
CreateNewEdge(aFirstVertID, aLastVertID, theSurfID, i);
continue;
}
// Must be minimal element from list
const int anIntRes = IntersectionOfSets(myVertexVec.Value(aFirstVertID).myFaces,
myVertexVec.Value(aLastVertID).myFaces);
if ((anIntRes < 0) || (anIntRes >= theSurfID))
{
CreateNewEdge(aFirstVertID, aLastVertID, theSurfID, i);
}
else
{ // if(theSurfID > anIntRes) => The edge has been processed earlier
UpdateEdgeInfo(aFirstVertID, aLastVertID, theSurfID, i);
}
}
}
//=================================================================================================
BRepBuilderAPI_FastSewing::FS_VARStatuses BRepBuilderAPI_FastSewing::GetStatuses(
Standard_OStream* const theOS)
{
if (!theOS)
{
return myStatusList;
}
if (!myStatusList)
{
*theOS << "Fast Sewing OK!\n";
return myStatusList;
}
// Number of bits
const int aNumMax = 8 * sizeof(myStatusList);
FS_Statuses anIDS = static_cast<FS_Statuses>(0x0001);
for (int i = 1; i <= aNumMax; i++, anIDS = static_cast<FS_Statuses>(anIDS << 1))
{
if ((anIDS & myStatusList) == 0)
{
continue;
}
switch (anIDS)
{
case FS_Degenerated:
*theOS << "Degenerated case. Try to reduce tolerance.\n";
break;
case FS_FindVertexError:
*theOS << "Error while creating list of vertices.\n";
break;
case FS_FindEdgeError:
*theOS << "Error while creating list of edges.\n";
break;
case FS_Exception:
*theOS << "Exception during the operation.\n";
break;
case FS_FaceWithNullSurface:
*theOS << "Source face has null surface.\n";
break;
case FS_NotNaturalBoundsFace:
*theOS << "Source face has trimmed surface.\n";
break;
case FS_InfiniteSurface:
*theOS << "Source face has the surface with infinite boundaries.\n";
break;
case FS_EmptyInput:
*theOS << "Empty source data.\n";
break;
default:
return myStatusList;
}
}
return myStatusList;
}
//=================================================================================================
double BRepBuilderAPI_FastSewing::Compute3DRange()
{
Bnd_Box aBox;
for (int i = myFaceVec.Lower(); i <= myFaceVec.Upper(); i++)
{
FS_Face& aFace = myFaceVec.ChangeValue(i);
const occ::handle<Geom_Surface> aSurf = BRep_Tool::Surface(aFace.mySrcFace);
if (aSurf.IsNull())
{
continue;
}
double aUf = 0.0, aUl = 0.0, aVf = 0.0, aVl = 0.0;
aSurf->Bounds(aUf, aUl, aVf, aVl);
aBox.Add(aSurf->Value(aUf, aVf));
aBox.Add(aSurf->Value(aUl, aVf));
aBox.Add(aSurf->Value(aUl, aVl));
aBox.Add(aSurf->Value(aUf, aVl));
}
double aXm = 0.0, aYm = 0.0, aZm = 0.0, aXM = 0.0, aYM = 0.0, aZM = 0.0;
aBox.Get(aXm, aYm, aZm, aXM, aYM, aZM);
double aDelta = aXM - aXm;
aDelta = std::max(aDelta, aYM - aYm);
aDelta = std::max(aDelta, aZM - aZm);
return aDelta;
}
//=================================================================================================
BRepBuilderAPI_FastSewing::NodeInspector::NodeInspector(
const NCollection_DynamicArray<FS_Vertex>& theVec,
const gp_Pnt& thePnt,
const double theTol)
: myVecOfVertexes(theVec),
myPoint(thePnt),
myResID(-1)
{
mySQToler = theTol * theTol;
}
//=================================================================================================
NCollection_CellFilter_Action BRepBuilderAPI_FastSewing::NodeInspector::Inspect(const Target theID)
{
const gp_Pnt& aPt = myVecOfVertexes.Value(theID).myPnt;
const double aSQDist = aPt.SquareDistance(myPoint);
if (aSQDist < mySQToler)
{
mySQToler = aSQDist;
myResID = theID;
}
return CellFilter_Keep;
}
//=================================================================================================
void BRepBuilderAPI_FastSewing::FS_Edge::CreateTopologicalEdge(
const NCollection_DynamicArray<FS_Vertex>& theVertexVec,
const NCollection_DynamicArray<FS_Face>& theFaceVec,
const double theTol)
{
BRep_Builder aBuilder;
TopoDS_Vertex aV1 = theVertexVec(myVertices[0]).myTopoVert;
TopoDS_Vertex aV2 = theVertexVec(myVertices[1]).myTopoVert;
aV1.Orientation(TopAbs_FORWARD);
aV2.Orientation(TopAbs_REVERSED);
occ::handle<Geom_Curve> a3dCurv;
TopLoc_Location aLocation;
const FS_Face& aFace = theFaceVec.Value(myFaces.Value(NCollection_Sequence<int>::Lower()));
// 3D-curves in 1st and 2nd faces are considered to be in same-range
const occ::handle<Geom_Surface>& aSurf = BRep_Tool::Surface(aFace.mySrcFace, aLocation);
double aUf = 0.0, aUl = 0.0, aVf = 0.0, aVl = 0.0;
aSurf->Bounds(aUf, aUl, aVf, aVl);
int anEdgeID = -1;
for (int anInd = 0; anInd < 4; anInd++)
{
if (myID == aFace.myEdges[anInd])
{
anEdgeID = anInd;
break;
}
}
// For DEBUG mode only
Standard_ProgramError_Raise_if(
anEdgeID < 0,
"BRepBuilderAPI_FastSewing::FS_Edge::CreateTopologicalEdge: Single edge.");
if (IsDegenerated())
{
occ::handle<Geom2d_Curve> a2dCurv = Get2DCurve(anEdgeID, aUf, aUl, aVf, aVl);
const double aFPar = a2dCurv->FirstParameter(), aLPar = a2dCurv->LastParameter();
aBuilder.MakeEdge(myTopoEdge);
aBuilder.UpdateEdge(myTopoEdge, a2dCurv, aSurf, aLocation, theTol);
aBuilder.Add(myTopoEdge, aV1);
aBuilder.Add(myTopoEdge, aV2);
aBuilder.Range(myTopoEdge, aFPar, aLPar);
aBuilder.Degenerated(myTopoEdge, true);
return;
}
switch (anEdgeID)
{
case 0:
a3dCurv = aSurf->VIso(aVf);
break;
case 1:
a3dCurv = aSurf->UIso(aUl);
break;
case 2:
a3dCurv = aSurf->VIso(aVl);
break;
case 3:
a3dCurv = aSurf->UIso(aUf);
break;
default:
throw Standard_OutOfRange("FS_Edge::CreateTopologicalEdge()");
break;
}
aBuilder.MakeEdge(myTopoEdge, a3dCurv, theTol);
aBuilder.Add(myTopoEdge, aV1);
aBuilder.Add(myTopoEdge, aV2);
aBuilder.Range(myTopoEdge, a3dCurv->FirstParameter(), a3dCurv->LastParameter());
}
//=================================================================================================
void BRepBuilderAPI_FastSewing::FS_Face::CreateTopologicalWire(
const NCollection_DynamicArray<FS_Edge>& theEdgeVec,
const double theToler)
{
TopLoc_Location aLocation;
// 3D-curves in 1st and 2nd faces are considered to be in same-range
const occ::handle<Geom_Surface>& aSurf = BRep_Tool::Surface(mySrcFace, aLocation);
double aUf = 0.0, aUl = 0.0, aVf = 0.0, aVl = 0.0;
aSurf->Bounds(aUf, aUl, aVf, aVl);
BRep_Builder aB;
aB.MakeWire(myWire);
for (int anEdge = 0; anEdge < 4; anEdge++)
{
Standard_ProgramError_Raise_if(
myEdges[anEdge] < 0,
"BRepBuilderAPI_FastSewing::FS_Face::CreateTopologicalWire: Wire is not closed.");
const BRepBuilderAPI_FastSewing::FS_Edge& aFSEdge = theEdgeVec.Value(myEdges[anEdge]);
TopAbs_Orientation anOri = anEdge < 2 ? TopAbs_FORWARD : TopAbs_REVERSED;
TopoDS_Edge anTopE = aFSEdge.myTopoEdge;
if (aFSEdge.IsDegenerated())
{
anTopE.Orientation(anOri);
aB.Add(myWire, anTopE);
continue;
}
// Check if 3D and 2D-curve have same-orientation.
// If it is not, 2d-curve will be reversed.
{
double aFirstPar = 0.0, aLastPar = 0.0;
const occ::handle<Geom_Curve> a3dCurv = BRep_Tool::Curve(anTopE, aFirstPar, aLastPar);
occ::handle<Geom2d_Curve> a2dCurv = Get2DCurve(anEdge, aUf, aUl, aVf, aVl);
const gp_Pnt aPref(a3dCurv->Value(aFirstPar));
const gp_Pnt2d aP2df(a2dCurv->Value(aFirstPar)), aP2dl(a2dCurv->Value(aLastPar));
gp_Pnt aP3df(aSurf->Value(aP2df.X(), aP2df.Y()));
gp_Pnt aP3dl(aSurf->Value(aP2dl.X(), aP2dl.Y()));
aP3df.Transform(aLocation);
aP3dl.Transform(aLocation);
const double aSqD1 = aP3df.SquareDistance(aPref);
const double aSqD2 = aP3dl.SquareDistance(aPref);
if (aSqD2 < aSqD1)
{
a2dCurv = Get2DCurve(anEdge, aUf, aUl, aVf, aVl, true);
anOri = TopAbs::Reverse(anOri);
}
aB.UpdateEdge(anTopE, a2dCurv, aSurf, aLocation, theToler);
}
anTopE.Orientation(anOri);
aB.Add(myWire, anTopE);
}
myWire.Closed(true);
}
//=================================================================================================
void BRepBuilderAPI_FastSewing::FS_Face::CreateTopologicalFace()
{
Standard_ProgramError_Raise_if(
myWire.IsNull(),
"BRepBuilderAPI_FastSewing::FS_Face::CreateTopologicalFace: Cannot create wire.");
BRep_Builder aBuilder;
myRetFace = TopoDS::Face(mySrcFace.EmptyCopied());
aBuilder.Add(myRetFace, myWire);
aBuilder.NaturalRestriction(myRetFace, true);
}