// 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 #include #include #include #include #include #include #include #include #include #include #include #include IMPLEMENT_STANDARD_RTTIEXT(BRepBuilderAPI_FastSewing,Standard_Transient) //======================================================================= //function : IntersetctionOfSets //purpose : Returns minimal value of intersection result //======================================================================= static Standard_Integer IntersectionOfSets( const NCollection_List& theSet1, const NCollection_List& theSet2) { const Standard_Integer anIntMax = IntegerLast(); Standard_Integer aRetVal = anIntMax; for(NCollection_List::Iterator anIt1 = theSet1.begin().Iterator(); anIt1.More(); anIt1.Next()) { const Standard_Integer aVal1 = anIt1.Value(); for(NCollection_List::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)) throw Standard_OutOfRange("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 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 const&) { SetStatus(FS_Exception); #ifdef OCCT_DEBUG //Standard_Failure::Caught()->Print(std::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& 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(0x0001); for(Standard_Integer i = 1; i <= aNumMax; i++, anIDS = static_cast(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& theVec, const gp_Pnt& thePnt, const Standard_Real theTol) : myVecOfVertexes(theVec), myPoint(thePnt), myResID(-1), myIsFindingEnable(Standard_False) { 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& theVertexVec, const NCollection_Vector& 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: 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()); } //======================================================================= //function : ::FS_Face::CreateTopologicalWire //purpose : //======================================================================= void BRepBuilderAPI_FastSewing::FS_Face:: CreateTopologicalWire(const NCollection_Vector& 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); }