--- /dev/null
+// 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 <TopTools_MapOfShape.hxx>
+
+IMPLEMENT_STANDARD_HANDLE(BRepBuilderAPI_FastSewing, Standard_Transient)
+IMPLEMENT_STANDARD_RTTIEXT(BRepBuilderAPI_FastSewing, Standard_Transient)
+
+//=======================================================================
+//function : IntersetctionOfSets
+//purpose : Returns minimal value of intersection result
+//=======================================================================
+static Standard_Integer
+ IntersectionOfSets( const NCollection_List<Standard_Integer>& theSet1,
+ const NCollection_List<Standard_Integer>& theSet2)
+{
+ const Standard_Integer anIntMax = IntegerLast();
+ Standard_Integer aRetVal = anIntMax;
+ for(NCollection_List<Standard_Integer>::Iterator
+ anIt1 = theSet1.begin().Iterator();
+ anIt1.More(); anIt1.Next())
+ {
+ const Standard_Integer aVal1 = anIt1.Value();
+ for(NCollection_List<Standard_Integer>::Iterator
+ anIt2 = theSet2.begin().Iterator();
+ anIt2.More(); anIt2.Next())
+ {
+ const Standard_Integer aVal2 = anIt2.Value();
+ if(aVal1 == aVal2)
+ {
+ //theIntersectionResult.Append(aVal1);
+ if(aVal1 < aRetVal)
+ aRetVal = aVal1;
+ }
+ }
+ }
+
+ if(aRetVal == anIntMax)
+ return -1;
+
+ return aRetVal;
+}
+
+//=======================================================================
+//function : Get2DCurve
+//purpose :
+//=======================================================================
+static Handle(Geom2d_Curve)
+ Get2DCurve( const Standard_Integer theIndex,
+ const Standard_Real theUfirst,
+ const Standard_Real theUlast,
+ const Standard_Real theVfirst,
+ const Standard_Real theVlast,
+ const Standard_Boolean theIsReverse = Standard_False)
+{
+ if((theIndex < 0) || (theIndex > 3))
+ Standard_OutOfRange::Raise("BRepBuilderAPI_FastSewing.cxx, Get2DCurve(): OUT of Range");
+
+ Handle(Geom2d_Curve) a2dCurv;
+
+ if(!theIsReverse)
+ {
+ switch(theIndex)
+ {
+ case 0:
+ a2dCurv =
+ new Geom2d_TrimmedCurve(new Geom2d_Line(
+ gp_Pnt2d(0.0, theVfirst), gp_Dir2d(1.0,0.0)),
+ theUfirst, theUlast);
+ break;
+ case 1:
+ a2dCurv =
+ new Geom2d_TrimmedCurve(new Geom2d_Line(
+ gp_Pnt2d(theUlast, 0.0), gp_Dir2d(0.0,1.0)),
+ theVfirst, theVlast);
+ break;
+ case 2:
+ a2dCurv =
+ new Geom2d_TrimmedCurve(new Geom2d_Line(
+ gp_Pnt2d(0.0, theVlast), gp_Dir2d(1.0,0.0)),
+ theUfirst, theUlast);
+ break;
+ case 3:
+ a2dCurv =
+ new Geom2d_TrimmedCurve(new Geom2d_Line(
+ gp_Pnt2d(theUfirst, 0.0), gp_Dir2d(0.0,1.0)),
+ theVfirst, theVlast);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ switch(theIndex)
+ {
+ case 0:
+ a2dCurv =
+ new Geom2d_TrimmedCurve(new Geom2d_Line(
+ gp_Pnt2d(theUfirst+theUlast, theVfirst),
+ gp_Dir2d(-1.0,0.0)),
+ theUfirst, theUlast);
+ break;
+ case 1:
+ a2dCurv =
+ new Geom2d_TrimmedCurve(new Geom2d_Line(
+ gp_Pnt2d(theUlast, theVfirst+theVlast),
+ gp_Dir2d(0.0,-1.0)),
+ theVfirst, theVlast);
+ break;
+ case 2:
+ a2dCurv =
+ new Geom2d_TrimmedCurve(new Geom2d_Line(
+ gp_Pnt2d(theUfirst+theUlast, theVlast),
+ gp_Dir2d(-1.0,0.0)),
+ theUfirst, theUlast);
+ break;
+ case 3:
+ a2dCurv =
+ new Geom2d_TrimmedCurve(new Geom2d_Line(
+ gp_Pnt2d(theUfirst, theVfirst+theVlast),
+ gp_Dir2d(0.0,-1.0)),
+ theVfirst, theVlast);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return a2dCurv;
+}
+
+//=======================================================================
+//function : Constructor
+//purpose :
+//=======================================================================
+BRepBuilderAPI_FastSewing::
+ BRepBuilderAPI_FastSewing( const Standard_Real theTol):
+ myTolerance(theTol),
+ myStatusList(0)
+{
+}
+
+//=======================================================================
+//function : Add
+//purpose :
+//=======================================================================
+Standard_Boolean BRepBuilderAPI_FastSewing::Add(const TopoDS_Shape& theShape)
+{
+ Standard_Boolean aResult = Standard_False;
+ if(theShape.IsNull())
+ {
+ SetStatus(FS_EmptyInput);
+ return aResult;
+ }
+
+ TopTools_MapOfShape 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))
+ {
+ 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;
+ }
+
+ Standard_Real 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 = Standard_True;
+ }
+ }
+
+ return aResult;
+}
+
+//=======================================================================
+//function : Add
+//purpose :
+//=======================================================================
+Standard_Boolean BRepBuilderAPI_FastSewing::Add(const Handle(Geom_Surface)& theSurface)
+{
+ if(theSurface.IsNull())
+ {
+ SetStatus(FS_FaceWithNullSurface);
+ return Standard_False;
+ }
+
+ if(theSurface->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface)))
+ {
+ SetStatus(FS_NotNaturalBoundsFace);
+ return Standard_False;
+ }
+
+ Standard_Real 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 Standard_False;
+ }
+
+ FS_Face aFace;
+
+ BRep_Builder aBuilder;
+ aBuilder.MakeFace(aFace.mySrcFace);
+ aBuilder.MakeFace(aFace.mySrcFace, theSurface, myTolerance);
+ aBuilder.NaturalRestriction(aFace.mySrcFace, Standard_True);
+
+ aFace.myID = myFaceVec.Length();//because start index is 0
+ myFaceVec.Append(aFace);
+
+ return Standard_True;
+}
+
+
+//=======================================================================
+//function : Perform
+//purpose :
+//=======================================================================
+void BRepBuilderAPI_FastSewing::Perform(void)
+{
+ if(myFaceVec.IsEmpty())
+ {
+ SetStatus(FS_EmptyInput);
+ return;
+ }
+
+ try
+ {
+ {
+ // create vertices having unique coordinates
+ Standard_Real aRange = Compute3DRange();
+ Handle(NCollection_IncAllocator) anAlloc = new NCollection_IncAllocator;
+ NCollection_CellFilter<NodeInspector>
+ aCells(Max(myTolerance, aRange/IntegerLast()), anAlloc);
+
+ for(Standard_Integer i = myFaceVec.Lower(); i <= myFaceVec.Upper(); i++)
+ {
+ FindVertexes(i, aCells);
+ }
+ }
+
+ for(Standard_Integer i = myFaceVec.Lower(); i <= myFaceVec.Upper(); i++)
+ {
+ FindEdges(i);
+ }
+
+ //Create topological structures
+
+ for(Standard_Integer i = myVertexVec.Lower(); i <= myVertexVec.Upper(); i++)
+ {
+ myVertexVec.ChangeValue(i).CreateTopologicalVertex(myTolerance);
+ }
+
+ //Edges
+ for(Standard_Integer i = myEdgeVec.Lower(); i <= myEdgeVec.Upper(); i++)
+ {
+ myEdgeVec.ChangeValue(i).CreateTopologicalEdge(myVertexVec, myFaceVec, myTolerance);
+ }
+
+ //Shell
+ BRepTools_Quilt aQuilt;
+
+ //Faces
+ for(Standard_Integer 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)
+ {
+ SetStatus(FS_Exception);
+#ifdef OCCT_DEBUG
+ //Standard_Failure::Caught()->Print(cout);
+#endif
+ return;
+ }
+}
+
+//=======================================================================
+//function : UpdateEdgeInfo
+//purpose :
+//=======================================================================
+void BRepBuilderAPI_FastSewing::UpdateEdgeInfo( const Standard_Integer theIDPrevVertex,
+ const Standard_Integer theIDCurrVertex,
+ const Standard_Integer theFaceID,
+ const Standard_Integer theIDCurvOnFace)
+{
+ //Indeed, two vertices combine into one edge only.
+ const Standard_Integer 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);
+}
+
+//=======================================================================
+//function : CreateNewEdge
+//purpose : Creates FS_Edge
+//=======================================================================
+void BRepBuilderAPI_FastSewing::CreateNewEdge(const Standard_Integer theIDPrevVertex,
+ const Standard_Integer theIDCurrVertex,
+ const Standard_Integer theFaceID,
+ const Standard_Integer 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);
+}
+
+//=======================================================================
+//function : FindVertexes
+//purpose :
+//=======================================================================
+void BRepBuilderAPI_FastSewing::
+ FindVertexes(const Standard_Integer theSurfID,
+ NCollection_CellFilter<NodeInspector>& theCells)
+{
+ const Standard_Integer aNbPoints = 4;
+ FS_Face& aFace = myFaceVec.ChangeValue(theSurfID);
+ const Handle(Geom_Surface) aSurf = BRep_Tool::Surface(aFace.mySrcFace);
+ Standard_Real 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(Standard_Integer 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);
+ }
+ }
+}
+
+//=======================================================================
+//function : FindEdges
+//purpose :
+//=======================================================================
+void BRepBuilderAPI_FastSewing::FindEdges(const Standard_Integer theSurfID)
+{
+ const Standard_Integer aNbPoints = 4;
+ FS_Face& aFace = myFaceVec.ChangeValue(theSurfID);
+
+ const Standard_Integer aFirstInd[aNbPoints] = {0, 1, 3, 0};
+ const Standard_Integer aLastInd[aNbPoints] = {1, 2, 2, 3};
+
+ for(Standard_Integer i = 0; i < aNbPoints; i++)
+ {
+ const Standard_Integer aFirstVertIndex = aFirstInd[i],
+ aLastVertIndex = aLastInd[i];
+ const Standard_Integer 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 Standard_Integer 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);
+ }
+ }
+}
+
+//=======================================================================
+//function : GetStatuses
+//purpose :
+//=======================================================================
+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 Standard_Integer aNumMax = 8*sizeof(myStatusList);
+ FS_Statuses anIDS = static_cast<FS_Statuses>(0x0001);
+ for(Standard_Integer 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;
+}
+
+//=======================================================================
+//function : Compute3DRange
+//purpose :
+//=======================================================================
+Standard_Real BRepBuilderAPI_FastSewing::Compute3DRange()
+{
+ Bnd_Box aBox;
+
+ for(Standard_Integer i = myFaceVec.Lower(); i <= myFaceVec.Upper(); i++)
+ {
+ FS_Face& aFace = myFaceVec.ChangeValue(i);
+ const Handle(Geom_Surface) aSurf = BRep_Tool::Surface(aFace.mySrcFace);
+ if(aSurf.IsNull())
+ continue;
+ Standard_Real 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));
+ }
+
+ Standard_Real 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);
+ Standard_Real aDelta = aXM - aXm;
+ aDelta = Max(aDelta, aYM - aYm);
+ aDelta = Max(aDelta, aZM - aZm);
+
+ return aDelta;
+}
+
+//=======================================================================
+//function : NodeInspector constructor
+//purpose :
+//=======================================================================
+BRepBuilderAPI_FastSewing::NodeInspector::
+ NodeInspector(const NCollection_Vector<FS_Vertex>& theVec,
+ const gp_Pnt& thePnt,
+ const Standard_Real theTol):
+myVecOfVertexes(theVec), myPoint(thePnt), myResID(-1)
+{
+ mySQToler = theTol*theTol;
+}
+
+//=======================================================================
+//function : ::NodeInspector::Inspect
+//purpose :
+//=======================================================================
+NCollection_CellFilter_Action BRepBuilderAPI_FastSewing::
+ NodeInspector::Inspect(const Target theID)
+{
+ const gp_Pnt& aPt = myVecOfVertexes.Value(theID).myPnt;
+ const Standard_Real aSQDist = aPt.SquareDistance(myPoint);
+ if(aSQDist < mySQToler)
+ {
+ mySQToler = aSQDist;
+ myResID = theID;
+ }
+
+ return CellFilter_Keep;
+}
+
+//=======================================================================
+//function : ::FS_Edge::CreateTopologicalEdge
+//purpose :
+//=======================================================================
+void BRepBuilderAPI_FastSewing::FS_Edge::
+ CreateTopologicalEdge(const NCollection_Vector<FS_Vertex>& theVertexVec,
+ const NCollection_Vector<FS_Face>& theFaceVec,
+ const Standard_Real 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);
+
+ Handle(Geom_Curve) a3dCurv;
+ TopLoc_Location aLocation;
+
+ const FS_Face& aFace = theFaceVec.Value(myFaces.Value(myFaces.Lower()));
+
+ //3D-curves in 1st and 2nd faces are considered to be in same-range
+ const Handle(Geom_Surface)& aSurf = BRep_Tool::Surface(aFace.mySrcFace, aLocation);
+
+ Standard_Real aUf = 0.0, aUl = 0.0, aVf = 0.0, aVl = 0.0;
+ aSurf->Bounds(aUf, aUl, aVf, aVl);
+
+ Standard_Integer anEdgeID = -1;
+ for(Standard_Integer 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())
+ {
+ Handle(Geom2d_Curve) a2dCurv = Get2DCurve(anEdgeID, aUf, aUl, aVf, aVl);
+ const Standard_Real 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, Standard_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:
+ Standard_OutOfRange::Raise("FS_Edge::CreateTopologicalEdge()");
+ break;
+ }
+
+ aBuilder.MakeEdge(myTopoEdge, a3dCurv, theTol);
+ aBuilder.Add(myTopoEdge, aV1);
+ aBuilder.Add(myTopoEdge, aV2);
+ aBuilder.Range(myTopoEdge, a3dCurv->FirstParameter(), a3dCurv->LastParameter());
+}
+
+//=======================================================================
+//function : ::FS_Face::CreateTopologicalWire
+//purpose :
+//=======================================================================
+void BRepBuilderAPI_FastSewing::FS_Face::
+ CreateTopologicalWire(const NCollection_Vector<FS_Edge>& theEdgeVec,
+ const Standard_Real theToler)
+{
+ TopLoc_Location aLocation;
+ //3D-curves in 1st and 2nd faces are considered to be in same-range
+ const Handle(Geom_Surface)& aSurf = BRep_Tool::Surface(mySrcFace, aLocation);
+ Standard_Real 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(Standard_Integer 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.
+ {
+ Standard_Real aFirstPar = 0.0, aLastPar = 0.0;
+
+ const Handle(Geom_Curve) a3dCurv = BRep_Tool::Curve(anTopE, aFirstPar, aLastPar);
+ 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 Standard_Real aSqD1 = aP3df.SquareDistance(aPref);
+ const Standard_Real aSqD2 = aP3dl.SquareDistance(aPref);
+
+ if(aSqD2 < aSqD1)
+ {
+ a2dCurv = Get2DCurve(anEdge, aUf, aUl, aVf, aVl, Standard_True);
+ anOri = TopAbs::Reverse(anOri);
+ }
+
+ aB.UpdateEdge(anTopE, a2dCurv, aSurf, aLocation, theToler);
+ }
+
+ anTopE.Orientation(anOri);
+
+ aB.Add(myWire, anTopE);
+ }
+
+ myWire.Closed(Standard_True);
+}
+
+//=======================================================================
+//function : ::FS_Face::CreateTopologicalFace
+//purpose :
+//=======================================================================
+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, Standard_True);
+}
--- /dev/null
+//! 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.
+
+#ifndef _BRepBuilderAPI_FastSewing_HeaderFile
+#define _BRepBuilderAPI_FastSewing_HeaderFile
+
+#include <Standard_Transient.hxx>
+#include <BRep_Builder.hxx>
+
+#include <NCollection_List.hxx>
+#include <NCollection_Sequence.hxx>
+#include <NCollection_Vector.hxx>
+#include <NCollection_CellFilter.hxx>
+
+#include <TopoDS_Edge.hxx>
+#include <TopoDS_Face.hxx>
+#include <TopoDS_Vertex.hxx>
+#include <TopoDS_Wire.hxx>
+
+class Handle(NCollection_IncAllocator);
+class Handle(Geom_Surface);
+
+//! This class performs fast sewing of surfaces (faces). It supposes
+//! that all surfaces are finite and are naturally restricted by their bounds.
+//! Moreover, it supposes that stitched together surfaces have the same parameterization
+//! along common boundaries, therefore it does not perform time-consuming check for
+//! SameParameter property of edges.
+//!
+//! For sewing, use this function as following:
+//! - set tolerance value (default tolerance is 1.E-06)
+//! - add all necessary surfaces (faces)
+//! - check status if adding is correctly completed.
+//! - compute -> Perform
+//! - retrieve the error status if any
+//! - retrieve the resulted shape
+class BRepBuilderAPI_FastSewing : public Standard_Transient
+{
+public:
+ typedef unsigned int FS_VARStatuses;
+
+ //! Enumeration of result statuses
+ //ATTENTION!!! If you add new status, please
+ // describe it in GetStatuses() method
+ enum FS_Statuses
+ {
+ FS_OK = 0x00000000,
+ FS_Degenerated = 0x00000001,
+ FS_FindVertexError = 0x00000002,
+ FS_FindEdgeError = 0x00000004,
+ FS_FaceWithNullSurface = 0x00000008,
+ FS_NotNaturalBoundsFace = 0x00000010,
+ FS_InfiniteSurface = 0x00000020,
+ FS_EmptyInput = 0x00000040,
+ FS_Exception = 0x00000080
+ };
+
+
+ //! Creates an object with tolerance of connexity
+ Standard_EXPORT BRepBuilderAPI_FastSewing(const Standard_Real theTolerance = 1.0e-06);
+
+ //! Adds faces of a shape
+ Standard_EXPORT Standard_Boolean Add(const TopoDS_Shape& theShape);
+
+ //! Adds a surface
+ Standard_EXPORT Standard_Boolean Add(const Handle(Geom_Surface)& theSurface);
+
+ //! Compute resulted shape
+ Standard_EXPORT void Perform (void) ;
+
+ //! Sets tolerance
+ void SetTolerance (const Standard_Real theToler)
+ {
+ myTolerance = theToler;
+ }
+
+ //! Returns tolerance
+ Standard_Real GetTolerance() const
+ {
+ return myTolerance;
+ }
+
+ //! Returns resulted shape
+ const TopoDS_Shape& GetResult() const
+ {
+ return myResShape;
+ }
+
+ //! Returns list of statuses. Print message if theOS != 0
+ Standard_EXPORT FS_VARStatuses GetStatuses(Standard_OStream* const theOS = 0);
+
+ DEFINE_STANDARD_RTTI(BRepBuilderAPI_FastSewing)
+
+protected:
+ class NodeInspector;
+
+ Standard_EXPORT void FindVertexes(const Standard_Integer theSurfID,
+ NCollection_CellFilter<NodeInspector>& theCells);
+ Standard_EXPORT void FindEdges(const Standard_Integer theSurfID);
+ Standard_EXPORT void UpdateEdgeInfo(const Standard_Integer theIDPrevVertex,
+ const Standard_Integer theIDCurrVertex,
+ const Standard_Integer theFaceID,
+ const Standard_Integer theIDCurvOnFace);
+ Standard_EXPORT void CreateNewEdge( const Standard_Integer theIDPrevVertex,
+ const Standard_Integer theIDCurrVertex,
+ const Standard_Integer theFaceID,
+ const Standard_Integer theIDCurvOnFace);
+
+ Standard_EXPORT Standard_Real Compute3DRange();
+
+ //! Sets status. Returns numeric value of the status set
+ FS_VARStatuses SetStatus(FS_Statuses theStatus)
+ {
+ const FS_VARStatuses aStatusID = (FS_VARStatuses)(theStatus);
+ myStatusList |= aStatusID;
+ return aStatusID;
+ }
+
+ class FS_Edge;
+
+ // Classes FS_Vertex, FS_Face and FS_Edge keep information about
+ // relations between resulted members (e.g. which faces share this vertex? etc.)
+
+ //! The struct corresponding to a vertex
+ struct FS_Vertex
+ {
+ public:
+ FS_Vertex(): myID(-1){};
+
+ //! Creates topological member (vertex)
+ void CreateTopologicalVertex(const Standard_Real theToler)
+ {
+ BRep_Builder aBuilder;
+ aBuilder.MakeVertex(myTopoVert, myPnt, theToler);
+ }
+
+ //! Geometry point of this Vertex
+ gp_Pnt myPnt;
+ TopoDS_Vertex myTopoVert;
+
+ //! List of faces and edges which share this vertex
+ NCollection_List<Standard_Integer> myFaces;
+ NCollection_List<Standard_Integer> myEdges;
+
+ //! Indentifies the place of this Vertex in
+ //! BRepBuilderAPI_FastSewing::myVertexVec list
+ Standard_Integer myID;
+ };
+
+ //! The struct corresponding to an face
+ struct FS_Face
+ {
+ FS_Face(): myID(-1)
+ {
+ for (Standard_Integer i = 0; i < 4; i++)
+ {
+ myVertices[i] = -1;
+ myEdges[i] = -1;
+ }
+ };
+ //! Creates topological members (wire and face)
+ void CreateTopologicalWire(const NCollection_Vector<FS_Edge>& theEdgeVec,
+ const Standard_Real theToler);
+ void CreateTopologicalFace();
+
+ //! Sets vertex
+ void SetVertex(const Standard_Integer thePlaceID, const Standard_Integer theVertID)
+ {
+ Standard_RangeError_Raise_if((thePlaceID < 0) || (thePlaceID > 3),
+ "FS_Face::SetVertex(): OUT of Range");
+
+ myVertices[thePlaceID] = theVertID;
+ }
+
+ //! Sets edge
+ void SetEdge(const Standard_Integer thePlaceID, const Standard_Integer theEdgeID)
+ {
+ Standard_RangeError_Raise_if((thePlaceID < 0) || (thePlaceID > 3),
+ "FS_Face::SetEdge(): OUT of Range");
+
+ myEdges[thePlaceID] = theEdgeID;
+ }
+
+ TopoDS_Face mySrcFace;
+ TopoDS_Wire myWire;
+ TopoDS_Face myRetFace;
+
+ //! myEdges[i] number of the edge in myEdgeVec
+ //! (i==0) <-> (V=Vf); (i==1) <-> (U=Ul);
+ //! (i==2) <-> (V=Vl); (i==3) <-> (U=Uf)
+ Standard_Integer myEdges[4];
+ //! myVertices[i] is Start point of myEdges[i]
+ Standard_Integer myVertices[4];
+
+ //! Indentifies the place of this Face in
+ //! BRepBuilderAPI_FastSewing::myFaceVec list
+ Standard_Integer myID;
+ };
+
+ //! The struct corresponding to a edge
+ class FS_Edge
+ {
+ public:
+ FS_Edge(): myID(-1)
+ {
+ myVertices[0] = -1;
+ myVertices[1] = -1;
+ }
+
+ FS_Edge(const Standard_Integer theIDVert1, const Standard_Integer theIDVert2): myID(-1)
+ {
+ myVertices[0] = theIDVert1;
+ myVertices[1] = theIDVert2;
+ };
+
+ //! Creates topological member (TopoDS_Edge)
+ void CreateTopologicalEdge( const NCollection_Vector<FS_Vertex>& theVertexVec,
+ const NCollection_Vector<FS_Face>& theFaceVec,
+ const Standard_Real theTol);
+
+ //! Sets vertex
+ void SetVertex(const Standard_Integer thePlaceID, const Standard_Integer theVertID)
+ {
+ Standard_RangeError_Raise_if((thePlaceID < 0) || (thePlaceID > 1),
+ "FS_Face::SetVertex(): OUT of Range");
+
+ myVertices[thePlaceID] = theVertID;
+ }
+
+ Standard_Boolean IsDegenerated() const
+ {
+ return (myVertices[0] == myVertices[1]);
+ }
+
+ //! List of faces which are shared with this edge
+ //! Value is the index of this shape in myFaceVec array
+ NCollection_Sequence<Standard_Integer> myFaces;
+
+ //! Indentifies the place of this Edge in
+ //! BRepBuilderAPI_FastSewing::myEdgeVec list
+ Standard_Integer myID;
+
+ TopoDS_Edge myTopoEdge;
+ private:
+ //! Index of the vertex in myVertexVec array
+ Standard_Integer myVertices[2];
+ };
+
+ //! This inspector will find a node nearest to the given point
+ //! not far than on the given tolerance
+ class NodeInspector: public NCollection_CellFilter_InspectorXYZ
+ {
+ public:
+ typedef Standard_Integer Target;
+
+ NodeInspector(const NCollection_Vector<FS_Vertex>& theVec,
+ const gp_Pnt& thePnt, const Standard_Real theTol);
+
+ Standard_EXPORT NCollection_CellFilter_Action Inspect (const Target theId);
+
+ Target GetResult()
+ {
+ return myResID;
+ }
+
+ private:
+ NodeInspector& operator = (const NodeInspector&);
+ const NCollection_Vector<FS_Vertex>& myVecOfVertexes;
+ gp_Pnt myPoint;
+ Standard_Real mySQToler;
+ Target myResID;
+ Standard_Boolean myIsFindingEnable;
+ };
+private:
+ TopoDS_Shape myResShape;
+
+ // myFaceVec, myVertexVec and myEdgeVec lists are filled only once!!!!!
+
+ //! Vector of faces
+ NCollection_Vector<FS_Face> myFaceVec;
+ //! Vector of Vertices
+ NCollection_Vector<FS_Vertex> myVertexVec;
+ //! Vector of edges
+ NCollection_Vector<FS_Edge> myEdgeVec;
+
+ //! Tolerance
+ Standard_Real myTolerance;
+
+ //! Bits of computation status
+ FS_VARStatuses myStatusList;
+};
+
+#endif // _BRepBuilderAPI_FastSewing_HeaderFile
+
+DEFINE_STANDARD_HANDLE(BRepBuilderAPI_FastSewing, Standard_Transient)
#include <Approx_CurvilinearParameter.hxx>
#include <Geom_BSplineSurface.hxx>
+#include <Poly_Triangulation.hxx>
+#include <TShort_HArray1OfShortReal.hxx>
+#include <GeomLProp_SLProps.hxx>
+#include <Poly_PolygonOnTriangulation.hxx>
// TODO - not thread-safe static variables
static Standard_Real thePrecision = Precision::Confusion();
}
}
+//=======================================================================
+// function : EnsureNormalConsistency
+// purpose : Corrects the normals in Poly_Triangulation of faces.
+// Returns TRUE if any correction is done.
+//=======================================================================
+Standard_Boolean BRepLib::
+ EnsureNormalConsistency(const TopoDS_Shape& theShape,
+ const Standard_Real theAngTol,
+ const Standard_Boolean theForceComputeNormals)
+{
+ const Standard_Real aThresDot = cos(theAngTol);
+
+ Standard_Boolean aRetVal = Standard_False, isNormalsFound = Standard_False;
+
+ // compute normals if they are absent
+ TopExp_Explorer anExpFace(theShape,TopAbs_FACE);
+ for (; anExpFace.More(); anExpFace.Next())
+ {
+ const TopoDS_Face& aFace = TopoDS::Face(anExpFace.Current());
+ const Handle(Geom_Surface) aSurf = BRep_Tool::Surface(aFace);
+ if(aSurf.IsNull())
+ continue;
+ TopLoc_Location aLoc;
+ const Handle(Poly_Triangulation)& aPT = BRep_Tool::Triangulation(aFace, aLoc);
+ if(aPT.IsNull())
+ continue;
+ if (!theForceComputeNormals && aPT->HasNormals())
+ {
+ isNormalsFound = Standard_True;
+ continue;
+ }
+
+ GeomLProp_SLProps aSLP(aSurf, 2, Precision::Confusion());
+ const Standard_Integer anArrDim = 3*aPT->NbNodes();
+ Handle(TShort_HArray1OfShortReal) aNormArr = new TShort_HArray1OfShortReal(1, anArrDim);
+ Standard_Integer anNormInd = aNormArr->Lower();
+ for(Standard_Integer i = aPT->UVNodes().Lower(); i <= aPT->UVNodes().Upper(); i++)
+ {
+ const gp_Pnt2d &aP2d = aPT->UVNodes().Value(i);
+ aSLP.SetParameters(aP2d.X(), aP2d.Y());
+
+ gp_XYZ aNorm(0.,0.,0.);
+ if(!aSLP.IsNormalDefined())
+ {
+#ifdef OCCT_DEBUG
+ cout << "BRepLib::EnsureNormalConsistency(): Cannot find normal!" << endl;
+#endif
+ }
+ else
+ {
+ aNorm = aSLP.Normal().XYZ();
+ if (aFace.Orientation() == TopAbs_REVERSED)
+ aNorm.Reverse();
+ }
+ aNormArr->ChangeValue(anNormInd++) = static_cast<Standard_ShortReal>(aNorm.X());
+ aNormArr->ChangeValue(anNormInd++) = static_cast<Standard_ShortReal>(aNorm.Y());
+ aNormArr->ChangeValue(anNormInd++) = static_cast<Standard_ShortReal>(aNorm.Z());
+ }
+
+ aRetVal = Standard_True;
+ isNormalsFound = Standard_True;
+ aPT->SetNormals(aNormArr);
+ }
+
+ if(!isNormalsFound)
+ {
+ return aRetVal;
+ }
+
+ // loop by edges
+ TopTools_IndexedDataMapOfShapeListOfShape aMapEF;
+ TopExp::MapShapesAndAncestors(theShape,TopAbs_EDGE,TopAbs_FACE,aMapEF);
+ for(Standard_Integer anInd = 1; anInd <= aMapEF.Extent(); anInd++)
+ {
+ const TopoDS_Edge& anEdg = TopoDS::Edge(aMapEF.FindKey(anInd));
+ const TopTools_ListOfShape& anEdgList = aMapEF.FindFromIndex(anInd);
+ if (anEdgList.Extent() != 2)
+ continue;
+ TopTools_ListIteratorOfListOfShape anItF(anEdgList);
+ const TopoDS_Face aFace1 = TopoDS::Face(anItF.Value());
+ anItF.Next();
+ const TopoDS_Face aFace2 = TopoDS::Face(anItF.Value());
+ TopLoc_Location aLoc1, aLoc2;
+ const Handle(Poly_Triangulation)& aPT1 = BRep_Tool::Triangulation(aFace1, aLoc1);
+ const Handle(Poly_Triangulation)& aPT2 = BRep_Tool::Triangulation(aFace2, aLoc2);
+
+ if(aPT1.IsNull() || aPT2.IsNull())
+ continue;
+
+ if(!aPT1->HasNormals() || !aPT2->HasNormals())
+ continue;
+
+ const Handle(Poly_PolygonOnTriangulation)& aPTEF1 =
+ BRep_Tool::PolygonOnTriangulation(anEdg, aPT1, aLoc1);
+ const Handle(Poly_PolygonOnTriangulation)& aPTEF2 =
+ BRep_Tool::PolygonOnTriangulation(anEdg, aPT2, aLoc2);
+
+ TShort_Array1OfShortReal& aNormArr1 = aPT1->ChangeNormals();
+ TShort_Array1OfShortReal& aNormArr2 = aPT2->ChangeNormals();
+
+ for(Standard_Integer anEdgNode = aPTEF1->Nodes().Lower();
+ anEdgNode <= aPTEF1->Nodes().Upper(); anEdgNode++)
+ {
+ //Number of node
+ const Standard_Integer aFNodF1 = aPTEF1->Nodes().Value(anEdgNode);
+ const Standard_Integer aFNodF2 = aPTEF2->Nodes().Value(anEdgNode);
+
+ const Standard_Integer aFNorm1FirstIndex = aNormArr1.Lower() + 3*
+ (aFNodF1 - aPT1->Nodes().Lower());
+ const Standard_Integer aFNorm2FirstIndex = aNormArr2.Lower() + 3*
+ (aFNodF2 - aPT2->Nodes().Lower());
+
+ gp_XYZ aNorm1(aNormArr1.Value(aFNorm1FirstIndex),
+ aNormArr1.Value(aFNorm1FirstIndex+1),
+ aNormArr1.Value(aFNorm1FirstIndex+2));
+ gp_XYZ aNorm2(aNormArr2.Value(aFNorm2FirstIndex),
+ aNormArr2.Value(aFNorm2FirstIndex+1),
+ aNormArr2.Value(aFNorm2FirstIndex+2));
+ const Standard_Real aDot = aNorm1 * aNorm2;
+
+ if(aDot > aThresDot)
+ {
+ gp_XYZ aNewNorm = (aNorm1 + aNorm2).Normalized();
+ aNormArr1.ChangeValue(aFNorm1FirstIndex) =
+ aNormArr2.ChangeValue(aFNorm2FirstIndex) =
+ static_cast<Standard_ShortReal>(aNewNorm.X());
+ aNormArr1.ChangeValue(aFNorm1FirstIndex+1) =
+ aNormArr2.ChangeValue(aFNorm2FirstIndex+1) =
+ static_cast<Standard_ShortReal>(aNewNorm.Y());
+ aNormArr1.ChangeValue(aFNorm1FirstIndex+2) =
+ aNormArr2.ChangeValue(aFNorm2FirstIndex+2) =
+ static_cast<Standard_ShortReal>(aNewNorm.Z());
+ aRetVal = Standard_True;
+ }
+ }
+ }
+
+ return aRetVal;
+}
+
//=======================================================================
//function : SortFaces
//purpose :