// Created by: Ekaterina SMIRNOVA // Copyright (c) 2008-2014 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include IMPLEMENT_STANDARD_RTTIEXT(BRepMesh_FastDiscretFace,Standard_Transient) static Standard_Real FUN_CalcAverageDUV(TColStd_Array1OfReal& P, const Standard_Integer PLen) { Standard_Integer i, j, n = 0; Standard_Real p, result = 0.; for(i = 1; i <= PLen; i++) { // Sort for(j = i + 1; j <= PLen; j++) { if(P(i) > P(j)) { p = P(i); P(i) = P(j); P(j) = p; } } // Accumulate if (i != 1) { p = Abs(P(i) - P(i-1)); if(p > 1.e-7) { result += p; n++; } } } return (n? (result / (Standard_Real) n) : -1.); } namespace { Standard_Real deflectionOfSegment ( const gp_Pnt& theFirstPoint, const gp_Pnt& theLastPoint, const gp_Pnt& theMidPoint) { // 23.03.2010 skl for OCC21645 - change precision for comparison if (theFirstPoint.SquareDistance (theLastPoint) > Precision::SquareConfusion ()) { gp_Lin aLin (theFirstPoint, gp_Dir (gp_Vec (theFirstPoint, theLastPoint))); return aLin.Distance (theMidPoint); } return theFirstPoint.Distance (theMidPoint); } Standard_Boolean IsCompexSurface (const GeomAbs_SurfaceType theType) { return ( theType != GeomAbs_Sphere && theType != GeomAbs_Cylinder && theType != GeomAbs_Cone && theType != GeomAbs_Torus); } //! Auxiliary class used to extract geometrical parameters of fixed TopoDS_Vertex. class FixedVExplorer { public: DEFINE_STANDARD_ALLOC FixedVExplorer(const TopoDS_Vertex& theVertex) : myVertex(theVertex) { } const TopoDS_Vertex& Vertex() const { return myVertex; } Standard_Boolean IsSameUV() const { return Standard_False; } TopoDS_Vertex SameVertex() const { return TopoDS_Vertex(); } gp_Pnt Point() const { return BRep_Tool::Pnt(myVertex); } private: void operator =(const FixedVExplorer& /*theOther*/) { } private: const TopoDS_Vertex& myVertex; }; } //======================================================================= //function : BRepMesh_FastDiscretFace //purpose : //======================================================================= BRepMesh_FastDiscretFace::BRepMesh_FastDiscretFace( const Standard_Real theAngle, const Standard_Real theMinSize, const Standard_Boolean isInternalVerticesMode, const Standard_Boolean isControlSurfaceDeflection) : myAngle(theAngle), myInternalVerticesMode(isInternalVerticesMode), myMinSize(theMinSize), myIsControlSurfaceDeflection(isControlSurfaceDeflection) { } //======================================================================= //function : Perform //purpose : //======================================================================= void BRepMesh_FastDiscretFace::Perform(const Handle(BRepMesh_FaceAttribute)& theAttribute) { add(theAttribute); commitSurfaceTriangulation(); } //======================================================================= //function : initDataStructure //purpose : //======================================================================= void BRepMesh_FastDiscretFace::initDataStructure() { const Standard_Real aTolU = myAttribute->ToleranceU(); const Standard_Real aTolV = myAttribute->ToleranceV(); const Standard_Real uCellSize = 14.0 * aTolU; const Standard_Real vCellSize = 14.0 * aTolV; const Standard_Real deltaX = myAttribute->GetDeltaX(); const Standard_Real deltaY = myAttribute->GetDeltaY(); Handle(NCollection_IncAllocator) aAllocator = new NCollection_IncAllocator(BRepMesh::MEMORY_BLOCK_SIZE_HUGE); myStructure = new BRepMesh_DataStructureOfDelaun(aAllocator); myStructure->Data()->SetCellSize ( uCellSize / deltaX, vCellSize / deltaY); myStructure->Data()->SetTolerance( aTolU / deltaX, aTolV / deltaY); myAttribute->ChangeStructure() = myStructure; myAttribute->ChangeSurfacePoints() = new BRepMesh::DMapOfIntegerPnt(1, aAllocator); myAttribute->ChangeSurfaceVertices()= new BRepMesh::DMapOfVertexInteger(1, aAllocator); // Check the necessity to fill the map of parameters const Handle(BRepAdaptor_HSurface)& gFace = myAttribute->Surface(); GeomAbs_SurfaceType thetype = gFace->GetType(); const Standard_Boolean isBSpline = (thetype == GeomAbs_BezierSurface || thetype == GeomAbs_BSplineSurface); const Standard_Boolean useUVParam = (thetype == GeomAbs_Torus || IsCompexSurface (thetype)); myUParam.Clear(aAllocator); myVParam.Clear(aAllocator); // essai de determination de la longueur vraie: // akm (bug OCC16) : We must calculate these measures in non-singular // parts of face. Let`s try to compute average value of three // (umin, (umin+umax)/2, umax), and respectively for v. // vvvvv //Standard_Real longu = 0.0, longv = 0.0; //, last , first; //gp_Pnt P11, P12, P21, P22, P31, P32; BRepMesh::HVectorOfVertex& aBoundaryNodes = myAttribute->ChangeMeshNodes(); BRepMesh::VectorOfVertex::Iterator aNodesIt(*aBoundaryNodes); for (; aNodesIt.More(); aNodesIt.Next()) { BRepMesh_Vertex& aNode = aNodesIt.ChangeValue(); gp_XY aPnt2d = aNode.Coord(); if (useUVParam) { myUParam.Add(aPnt2d.X()); myVParam.Add(aPnt2d.Y()); } aNode.ChangeCoord() = myAttribute->Scale(aPnt2d, Standard_True); myStructure->AddNode(aNode, Standard_True); } aBoundaryNodes.Nullify(); if (isBSpline) { const Standard_Real aRange[2][2] = { {myAttribute->GetUMin(), myAttribute->GetUMax()}, {myAttribute->GetVMin(), myAttribute->GetVMax()} }; const GeomAbs_Shape aContinuity = GeomAbs_CN; for (Standard_Integer i = 0; i < 2; ++i) { const Standard_Boolean isU = (i == 0); const Standard_Integer aIntervalsNb = isU ? gFace->NbUIntervals(aContinuity) : gFace->NbVIntervals(aContinuity); BRepMesh::IMapOfReal& aParams = isU ? myUParam : myVParam; if (aIntervalsNb < aParams.Size()) continue; TColStd_Array1OfReal aIntervals(1, aIntervalsNb + 1); if (isU) gFace->UIntervals(aIntervals, aContinuity); else gFace->VIntervals(aIntervals, aContinuity); for (Standard_Integer j = 1; j <= aIntervals.Upper(); ++j) { const Standard_Real aParam = aIntervals(j); if (aParam > aRange[i][0] && aParam < aRange[i][1]) aParams.Add(aParam); } } } //////////////////////////////////////////////////////////// //add internal vertices after self-intersection check if ( myInternalVerticesMode ) { TopExp_Explorer anExplorer(myAttribute->Face(), TopAbs_VERTEX, TopAbs_EDGE); for ( ; anExplorer.More(); anExplorer.Next() ) add(TopoDS::Vertex(anExplorer.Current())); } const BRepMesh::HDMapOfShapePairOfPolygon& aEdges = myAttribute->ChangeInternalEdges(); TopExp_Explorer aWireIt(myAttribute->Face(), TopAbs_WIRE); for (; aWireIt.More(); aWireIt.Next()) { TopExp_Explorer aEdgeIt(aWireIt.Current(), TopAbs_EDGE); for (; aEdgeIt.More(); aEdgeIt.Next()) { const TopoDS_Edge& aEdge = TopoDS::Edge(aEdgeIt.Current()); BRepMesh_PairOfPolygon aPair; if (!aEdges->Find(aEdge, aPair)) continue; TopAbs_Orientation aOri = aEdge.Orientation(); const Handle(Poly_PolygonOnTriangulation)& aPolygon = aOri == TopAbs_REVERSED ? aPair.Last() : aPair.First(); const TColStd_Array1OfInteger& aIndices = aPolygon->Nodes(); const Standard_Integer aNodesNb = aPolygon->NbNodes(); Standard_Integer aPrevId = aIndices(1); for (Standard_Integer i = 2; i <= aNodesNb; ++i) { const Standard_Integer aCurId = aIndices(i); addLinkToMesh(aPrevId, aCurId, aOri); aPrevId = aCurId; } } } } //======================================================================= //function : addLinkToMesh //purpose : //======================================================================= void BRepMesh_FastDiscretFace::addLinkToMesh( const Standard_Integer theFirstNodeId, const Standard_Integer theLastNodeId, const TopAbs_Orientation theOrientation) { if (theOrientation == TopAbs_FORWARD) myStructure->AddLink(BRepMesh_Edge(theFirstNodeId, theLastNodeId, BRepMesh_Frontier)); else if (theOrientation == TopAbs_REVERSED) myStructure->AddLink(BRepMesh_Edge(theLastNodeId, theFirstNodeId, BRepMesh_Frontier)); else if (theOrientation == TopAbs_INTERNAL) myStructure->AddLink(BRepMesh_Edge(theFirstNodeId, theLastNodeId, BRepMesh_Fixed)); } //======================================================================= //function : Add //purpose : //======================================================================= void BRepMesh_FastDiscretFace::add(const Handle(BRepMesh_FaceAttribute)& theAttribute) { if (!theAttribute->IsValid() || theAttribute->ChangeMeshNodes()->IsEmpty()) return; myAttribute = theAttribute; initDataStructure(); BRepMesh::HIMapOfInteger& aVertexEdgeMap = myAttribute->ChangeVertexEdgeMap(); Standard_Integer nbVertices = aVertexEdgeMap->Extent(); BRepMesh::Array1OfInteger tabvert_corr(1, nbVertices); for ( Standard_Integer i = 1; i <= nbVertices; ++i ) tabvert_corr(i) = i; BRepMesh_Delaun trigu(myStructure, tabvert_corr); //removed all free edges from triangulation const Standard_Integer nbLinks = myStructure->NbLinks(); for( Standard_Integer i = 1; i <= nbLinks; i++ ) { if( myStructure->ElementsConnectedTo(i).Extent() < 1 ) { BRepMesh_Edge& anEdge = (BRepMesh_Edge&)trigu.GetEdge(i); if ( anEdge.Movability() == BRepMesh_Deleted ) continue; anEdge.SetMovability(BRepMesh_Free); myStructure->RemoveLink(i); } } const Handle(BRepAdaptor_HSurface)& gFace = myAttribute->Surface(); GeomAbs_SurfaceType thetype = gFace->GetType(); Standard_Boolean rajout = (thetype == GeomAbs_Sphere || thetype == GeomAbs_Torus); // Check the necessity to fill the map of parameters const Standard_Boolean useUVParam = (thetype == GeomAbs_Torus || thetype == GeomAbs_BezierSurface || thetype == GeomAbs_BSplineSurface); const Standard_Real umax = myAttribute->GetUMax(); const Standard_Real umin = myAttribute->GetUMin(); const Standard_Real vmax = myAttribute->GetVMax(); const Standard_Real vmin = myAttribute->GetVMin(); Standard_Boolean isaline = ((umax - umin) < Precision::PConfusion() || (vmax - vmin) < Precision::PConfusion()); Standard_Real aDef = -1; if ( !isaline && myStructure->ElementsOfDomain().Extent() > 0 ) { Handle(NCollection_IncAllocator) anAlloc = new NCollection_IncAllocator; BRepMesh::ListOfVertex aNewVertices(anAlloc); if (!rajout) { aDef = control(aNewVertices, trigu, Standard_True); rajout = (aDef > myAttribute->GetDefFace() || aDef < 0.); } if (!rajout && useUVParam) { rajout = (myVParam.Extent() > 2 && (gFace->IsUClosed() || gFace->IsVClosed())); } if (rajout) { insertInternalVertices(aNewVertices, trigu); //control internal points if (myIsControlSurfaceDeflection) aDef = control(aNewVertices, trigu, Standard_False); } } //modify myStructure back BRepMesh::HVectorOfVertex& aMeshNodes = myStructure->Data()->ChangeVertices(); for ( Standard_Integer i = 1; i <= myStructure->NbNodes(); ++i ) { BRepMesh_Vertex& aNode = aMeshNodes->ChangeValue(i - 1); aNode.ChangeCoord() = myAttribute->Scale(aNode.Coord(), Standard_False); const BRepMesh::ListOfInteger& alist = myStructure->LinksConnectedTo(i); // Register internal nodes used in triangulation if (!alist.IsEmpty() && aVertexEdgeMap->FindIndex(i) == 0) aVertexEdgeMap->Add(i); } if (!(aDef < 0.)) myAttribute->SetDefFace(aDef); } //======================================================================= //function : addVerticesToMesh //purpose : //======================================================================= Standard_Boolean BRepMesh_FastDiscretFace::addVerticesToMesh( const BRepMesh::ListOfVertex& theVertices, BRepMesh_Delaun& theMeshBuilder) { if (theVertices.IsEmpty()) return Standard_False; BRepMesh::Array1OfVertexOfDelaun aArrayOfNewVertices(1, theVertices.Extent()); BRepMesh::ListOfVertex::Iterator aVertexIt(theVertices); for (Standard_Integer aVertexId = 0; aVertexIt.More(); aVertexIt.Next()) aArrayOfNewVertices(++aVertexId) = aVertexIt.Value(); theMeshBuilder.AddVertices(aArrayOfNewVertices); return Standard_True; } //======================================================================= //function : insertInternalVertices //purpose : //======================================================================= static void filterParameters(const BRepMesh::IMapOfReal& theParams, const Standard_Real theMinDist, const Standard_Real theFilterDist, BRepMesh::SequenceOfReal& theResult) { // Sort sequence of parameters const Standard_Integer anInitLen = theParams.Extent(); TColStd_Array1OfReal aParamArray(1, anInitLen); Standard_Integer j; for (j = 1; j <= anInitLen; j++) aParamArray(j) = theParams(j); std::sort (aParamArray.begin(), aParamArray.end()); // mandatory pre-filtering using the first (minimal) filter value Standard_Integer aParamLength = 1; for (j = 2; j <= anInitLen; j++) { if ((aParamArray(j)-aParamArray(aParamLength)) > theMinDist) { if (++aParamLength < j) aParamArray(aParamLength) = aParamArray(j); } } //perform filtering on series Standard_Real aLastAdded, aLastCandidate; Standard_Boolean isCandidateDefined = Standard_False; aLastAdded = aParamArray(1); aLastCandidate = aLastAdded; theResult.Append(aLastAdded); for(j=2; j < aParamLength; j++) { Standard_Real aVal = aParamArray(j); if(aVal-aLastAdded > theFilterDist) { //adds the parameter if(isCandidateDefined) { aLastAdded = aLastCandidate; isCandidateDefined = Standard_False; j--; } else { aLastAdded = aVal; } theResult.Append(aLastAdded); continue; } aLastCandidate = aVal; isCandidateDefined = Standard_True; } theResult.Append(aParamArray(aParamLength)); } void BRepMesh_FastDiscretFace::insertInternalVertices( BRepMesh::ListOfVertex& theNewVertices, BRepMesh_Delaun& theMeshBuilder) { const Handle(BRepAdaptor_HSurface)& gFace = myAttribute->Surface(); switch (gFace->GetType()) { case GeomAbs_Sphere: insertInternalVerticesSphere(theNewVertices); break; case GeomAbs_Cylinder: insertInternalVerticesCylinder(theNewVertices); break; case GeomAbs_Cone: insertInternalVerticesCone(theNewVertices); break; case GeomAbs_Torus: insertInternalVerticesTorus(theNewVertices); break; default: insertInternalVerticesOther(theNewVertices); break; } addVerticesToMesh(theNewVertices, theMeshBuilder); } //======================================================================= //function : insertInternalVerticesSphere //purpose : //======================================================================= void BRepMesh_FastDiscretFace::insertInternalVerticesSphere( BRepMesh::ListOfVertex& theNewVertices) { Standard_Real aRange[] = { myAttribute->GetVMin(), myAttribute->GetVMax(), myAttribute->GetUMin(), myAttribute->GetUMax() }; gp_Sphere aSphere = myAttribute->Surface()->Sphere(); // Calculate parameters for iteration in V direction Standard_Real aStep = 0.7 * GCPnts_TangentialDeflection::ArcAngularStep( aSphere.Radius(), myAttribute->GetDefFace(), myAngle, myMinSize); Standard_Real aDd[2] = {aStep, aStep}; Standard_Real aPasMax[2] = {0., 0.}; for (Standard_Integer i = 0; i < 2; ++i) { const Standard_Real aMax = aRange[2 * i + 1]; const Standard_Real aDiff = aMax - aRange[2 * i + 0]; aDd[i] = aDiff / ((Standard_Integer)(aDiff / aDd[i]) + 1); aPasMax[i] = aMax - Precision::PConfusion(); } const Standard_Real aHalfDu = aDd[1] * 0.5; Standard_Boolean Shift = Standard_False; Standard_Real aPasV = aRange[0] + aDd[0]; for (; aPasV < aPasMax[0]; aPasV += aDd[0]) { Shift = !Shift; const Standard_Real d = (Shift) ? aHalfDu : 0.; Standard_Real aPasU = aRange[2] + d; for (; aPasU < aPasMax[1]; aPasU += aDd[1]) { tryToInsertAnalyticVertex(gp_Pnt2d(aPasU, aPasV), aSphere, theNewVertices); } } } //======================================================================= //function : insertInternalVerticesCylinder //purpose : //======================================================================= void BRepMesh_FastDiscretFace::insertInternalVerticesCylinder( BRepMesh::ListOfVertex& theNewVertices) { const Standard_Real umax = myAttribute->GetUMax(); const Standard_Real umin = myAttribute->GetUMin(); const Standard_Real vmax = myAttribute->GetVMax(); const Standard_Real vmin = myAttribute->GetVMin(); gp_Cylinder aCylinder = myAttribute->Surface()->Cylinder(); const Standard_Real aRadius = aCylinder.Radius(); Standard_Integer nbU = 0; Standard_Integer nbV = 0; const Standard_Real su = umax - umin; const Standard_Real sv = vmax - vmin; const Standard_Real aArcLen = su * aRadius; if (aArcLen > myAttribute->GetDefFace ()) { // Calculate parameters for iteration in U direction const Standard_Real Du = GCPnts_TangentialDeflection::ArcAngularStep ( aRadius, myAttribute->GetDefFace (), myAngle, myMinSize); nbU = (Standard_Integer)(su / Du); // Calculate parameters for iteration in V direction const Standard_Real aDv = nbU*sv / aArcLen; // Protection against overflow during casting to int in case // of long cylinder with small radius. nbV = aDv > static_cast (IntegerLast ()) ? 0 : (Standard_Integer)(aDv); nbV = Min (nbV, 100 * nbU); } const Standard_Real Du = su / (nbU + 1); const Standard_Real Dv = sv / (nbV + 1); Standard_Real pasu, pasv, pasvmax = vmax - Dv*0.5, pasumax = umax - Du*0.5; for (pasv = vmin + Dv; pasv < pasvmax; pasv += Dv) { for (pasu = umin + Du; pasu < pasumax; pasu += Du) { tryToInsertAnalyticVertex(gp_Pnt2d(pasu, pasv), aCylinder, theNewVertices); } } } //======================================================================= //function : insertInternalVerticesCone //purpose : //======================================================================= void BRepMesh_FastDiscretFace::insertInternalVerticesCone( BRepMesh::ListOfVertex& theNewVertices) { const Standard_Real umax = myAttribute->GetUMax(); const Standard_Real umin = myAttribute->GetUMin(); const Standard_Real vmax = myAttribute->GetVMax(); const Standard_Real vmin = myAttribute->GetVMin(); gp_Cone aCone = myAttribute->Surface()->Cone(); Standard_Real RefR = aCone.RefRadius(); Standard_Real SAng = aCone.SemiAngle(); Standard_Real aRadius = Max(Abs(RefR + vmin*Sin(SAng)), Abs(RefR + vmax*Sin(SAng))); Standard_Real Du = GCPnts_TangentialDeflection::ArcAngularStep( aRadius, myAttribute->GetDefFace(), myAngle, myMinSize); Standard_Real Dv, pasu, pasv; Standard_Integer nbU = (Standard_Integer)((umax - umin) / Du); Standard_Integer nbV = (Standard_Integer)(nbU*(vmax - vmin) / ((umax - umin)*aRadius)); Du = (umax - umin) / (nbU + 1); Dv = (vmax - vmin) / (nbV + 1); Standard_Real pasvmax = vmax - Dv*0.5, pasumax = umax - Du*0.5; for (pasv = vmin + Dv; pasv < pasvmax; pasv += Dv) { for (pasu = umin + Du; pasu < pasumax; pasu += Du) { tryToInsertAnalyticVertex(gp_Pnt2d(pasu, pasv), aCone, theNewVertices); } } } //======================================================================= //function : insertInternalVerticesTorus //purpose : //======================================================================= void BRepMesh_FastDiscretFace::insertInternalVerticesTorus( BRepMesh::ListOfVertex& theNewVertices) { const Standard_Real umax = myAttribute->GetUMax(); const Standard_Real umin = myAttribute->GetUMin(); const Standard_Real vmax = myAttribute->GetVMax(); const Standard_Real vmin = myAttribute->GetVMin(); const Standard_Real deltaX = myAttribute->GetDeltaX(); const Standard_Real deltaY = myAttribute->GetDeltaY(); const Standard_Real aDefFace = myAttribute->GetDefFace(); gp_Torus T = myAttribute->Surface()->Torus(); Standard_Boolean insert; Standard_Integer i, j, ParamULength, ParamVLength; Standard_Real pp, pasu, pasv; Standard_Real r = T.MinorRadius(), R = T.MajorRadius(); BRepMesh::SequenceOfReal ParamU, ParamV; Standard_Real oldDv = GCPnts_TangentialDeflection::ArcAngularStep( r, aDefFace, myAngle, myMinSize); Standard_Real Dv = 0.9*oldDv; //TWOTHIRD * oldDv; Dv = oldDv; Standard_Real Du; Standard_Integer nbV = Max((Standard_Integer)((vmax - vmin) / Dv), 2); Dv = (vmax - vmin) / (nbV + 1); Standard_Real ru = R + r; if (ru > 1.e-16) { Du = GCPnts_TangentialDeflection::ArcAngularStep( ru, aDefFace, myAngle, myMinSize); Standard_Real aa = sqrt(Du*Du + oldDv*oldDv); if (aa < gp::Resolution()) return; Du *= Min(oldDv, Du) / aa; } else Du = Dv; Standard_Integer nbU = Max((Standard_Integer)((umax - umin) / Du), 2); nbU = Max(nbU, (int)(nbV*(umax - umin)*R / ((vmax - vmin)*r) / 5.)); Du = (umax - umin) / (nbU + 1); if (R < r) { // As the points of edges are returned. // in this case, the points are not representative. //-- Choose DeltaX and DeltaY so that to avoid skipping points on the grid for (i = 0; i <= nbU; i++) ParamU.Append(umin + i* Du); }//R r { //--ofv: U // Number of mapped U parameters const Standard_Integer LenU = myUParam.Extent(); // Fill array of U parameters TColStd_Array1OfReal Up(1, LenU); for (j = 1; j <= LenU; j++) Up(j) = myUParam(j); // Calculate DU, leave array of parameters Standard_Real aDU = FUN_CalcAverageDUV(Up, LenU); aDU = Max(aDU, Abs(umax - umin) / (Standard_Real)nbU / 2.); Standard_Real dUstd = Abs(umax - umin) / (Standard_Real)LenU; if (aDU > dUstd) dUstd = aDU; // Add U parameters for (j = 1; j <= LenU; j++) { pp = Up(j); insert = Standard_True; ParamULength = ParamU.Length(); for (i = 1; i <= ParamULength && insert; i++) { insert = (Abs(ParamU.Value(i) - pp) > (0.5*dUstd)); } if (insert) ParamU.Append(pp); } } //--ofv: V // Number of mapped V parameters const Standard_Integer LenV = myVParam.Extent(); // Fill array of V parameters TColStd_Array1OfReal Vp(1, LenV); for (j = 1; j <= LenV; j++) Vp(j) = myVParam(j); // Calculate DV, sort array of parameters Standard_Real aDV = FUN_CalcAverageDUV(Vp, LenV); aDV = Max(aDV, Abs(vmax - vmin) / (Standard_Real)nbV / 2.); Standard_Real dVstd = Abs(vmax - vmin) / (Standard_Real)LenV; if (aDV > dVstd) dVstd = aDV; // Add V parameters for (j = 1; j <= LenV; j++) { pp = Vp(j); insert = Standard_True; ParamVLength = ParamV.Length(); for (i = 1; i <= ParamVLength && insert; i++) { insert = (Abs(ParamV.Value(i) - pp) > (dVstd*2. / 3.)); } if (insert) ParamV.Append(pp); } Standard_Integer Lu = ParamU.Length(), Lv = ParamV.Length(); Standard_Real uminnew = umin + deltaY*0.1; Standard_Real vminnew = vmin + deltaX*0.1; Standard_Real umaxnew = umax - deltaY*0.1; Standard_Real vmaxnew = vmax - deltaX*0.1; for (i = 1; i <= Lu; i++) { pasu = ParamU.Value(i); if (pasu >= uminnew && pasu < umaxnew) { for (j = 1; j <= Lv; j++) { pasv = ParamV.Value(j); if (pasv >= vminnew && pasv < vmaxnew) { tryToInsertAnalyticVertex(gp_Pnt2d(pasu, pasv), T, theNewVertices); } } } } } //======================================================================= //function : insertInternalVerticesOther //purpose : //======================================================================= void BRepMesh_FastDiscretFace::insertInternalVerticesOther( BRepMesh::ListOfVertex& theNewVertices) { const Standard_Real aRange[2][2] = { { myAttribute->GetUMax(), myAttribute->GetUMin() }, { myAttribute->GetVMax(), myAttribute->GetVMin() } }; const Standard_Real aDelta[2] = { myAttribute->GetDeltaX(), myAttribute->GetDeltaY() }; const Standard_Real aDefFace = myAttribute->GetDefFace(); const Handle(BRepAdaptor_HSurface)& gFace = myAttribute->Surface(); Handle(NCollection_IncAllocator) anAlloc = new NCollection_IncAllocator; BRepMesh::SequenceOfReal aParams[2] = { BRepMesh::SequenceOfReal(anAlloc), BRepMesh::SequenceOfReal(anAlloc) }; for (Standard_Integer i = 0; i < 2; ++i) { Standard_Boolean isU = (i == 0); Standard_Real aRes = isU ? gFace->UResolution(aDefFace) : gFace->VResolution(aDefFace); // Sort and filter sequence of parameters Standard_Real aMinDiff = Precision::PConfusion(); if (aDelta[i] < 1.) aMinDiff /= aDelta[i]; aMinDiff = Max(myMinSize, aMinDiff); Standard_Real aRangeDiff = aRange[i][0] - aRange[i][1]; Standard_Real aDiffMaxLim = 0.1 * aRangeDiff; Standard_Real aDiffMinLim = Max(0.005 * aRangeDiff, 2. * aRes); Standard_Real aDiff = Max(myMinSize, Min(aDiffMaxLim, aDiffMinLim)); filterParameters(isU ? myUParam : myVParam, aMinDiff, aDiff, aParams[i]); } // check intermediate isolines Handle (Geom_Surface) aSurface = gFace->ChangeSurface ().Surface ().Surface (); const BRepMesh::HClassifier& aClassifier = myAttribute->ChangeClassifier(); BRepMesh::MapOfReal aParamsToRemove[2] = { BRepMesh::MapOfReal(1, anAlloc), BRepMesh::MapOfReal(1, anAlloc) }; BRepMesh::MapOfReal aParamsForbiddenToRemove[2] = { BRepMesh::MapOfReal(1, anAlloc), BRepMesh::MapOfReal(1, anAlloc) }; // precision for compare square distances const Standard_Real aPrecision = Precision::Confusion(); for (Standard_Integer k = 0; k < 2; ++k) { const Standard_Integer aOtherIndex = (k + 1) % 2; BRepMesh::SequenceOfReal& aParams1 = aParams[k]; BRepMesh::SequenceOfReal& aParams2 = aParams[aOtherIndex]; const Standard_Boolean isU = (k == 0); Standard_Integer aStartIndex, aEndIndex; if (isU) { aStartIndex = 1; aEndIndex = aParams1.Length(); } else { aStartIndex = 2; aEndIndex = aParams1.Length() - 1; } BRepMesh::MapOfReal& aToRemove2 = aParamsToRemove[aOtherIndex]; BRepMesh::MapOfReal& aForbiddenToRemove1 = aParamsForbiddenToRemove[k]; BRepMesh::MapOfReal& aForbiddenToRemove2 = aParamsForbiddenToRemove[aOtherIndex]; for (Standard_Integer i = aStartIndex; i <= aEndIndex; ++i) { const Standard_Real aParam1 = aParams1(i); GeomAdaptor_Curve aIso(isU ? aSurface->UIso (aParam1) : aSurface->VIso (aParam1)); Standard_Real aPrevParam2 = aParams2(1); gp_Pnt aPrevPnt2; gp_Vec aPrevVec2; aIso.D1 (aPrevParam2, aPrevPnt2, aPrevVec2); for (Standard_Integer j = 2; j <= aParams2.Length();) { Standard_Real aParam2 = aParams2(j); gp_Pnt aPnt2; gp_Vec aVec2; aIso.D1 (aParam2, aPnt2, aVec2); Standard_Real aMidParam = 0.5 * (aPrevParam2 + aParam2); gp_Pnt aMidPnt = aIso.Value(aMidParam); Standard_Real aDist = deflectionOfSegment (aPrevPnt2, aPnt2, aMidPnt); if (aDist > aDefFace && aDist > myMinSize) { // insertion aParams2.InsertBefore(j, aMidParam); } else { //put regular grig for normals gp_Pnt2d aStPnt1, aStPnt2; if (isU) { aStPnt1 = gp_Pnt2d(aParam1, aPrevParam2); aStPnt2 = gp_Pnt2d(aParam1, aMidParam); } else { aStPnt1 = gp_Pnt2d(aPrevParam2, aParam1); aStPnt2 = gp_Pnt2d(aMidParam, aParam1); } gp_Dir N1(0, 0, 1), N2(0, 0, 1); Standard_Boolean aSt1 = GeomLib::NormEstim (aSurface, aStPnt1, aPrecision, N1); Standard_Boolean aSt2 = GeomLib::NormEstim (aSurface, aStPnt2, aPrecision, N2); const Standard_Real aAngle = N2.Angle(N1); if (aSt1 < 1 && aSt2 < 1 && aAngle > myAngle) { const Standard_Real aLen = GCPnts_AbscissaPoint::Length ( aIso, aPrevParam2, aMidParam, aDefFace); if (aLen > myMinSize) { // insertion aParams2.InsertBefore(j, aMidParam); continue; } } // Here we should leave at least 3 parameters as far as // we must have at least one parameter related to surface // internals in order to prevent movement of triangle body // outside the surface in case of highly curved ones, e.g. // BSpline springs. if (aDist < aDefFace && aParams2.Length () > 3 && j < aParams2.Length ()) { // Remove too dense points const Standard_Real aTmpParam = aParams2 (j + 1); gp_Pnt aTmpPnt; gp_Vec aTmpVec; aIso.D1 (aTmpParam, aTmpPnt, aTmpVec); Standard_Real aTmpMidParam = 0.5 * (aPrevParam2 + aTmpParam); gp_Pnt aTmpMidPnt = aIso.Value (aTmpMidParam); // Lets check next parameter. // If it also fits deflection, we can remove previous parameter. aDist = deflectionOfSegment (aPrevPnt2, aTmpPnt, aTmpMidPnt); if (aDist < aDefFace) { // Lets check parameters for angular deflection. if (aPrevVec2.SquareMagnitude() < gp::Resolution() || aTmpVec.SquareMagnitude() < gp::Resolution() || aPrevVec2.Angle (aTmpVec) < myAngle) { // For current Iso line we can remove this parameter. aToRemove2.Add (aParam2); aParam2 = aTmpParam; aPnt2 = aTmpPnt; aVec2 = aTmpVec; ++j; } else { // We have found a place on the surface refusing // removement of this parameter. aForbiddenToRemove1.Add (aParam1); aForbiddenToRemove2.Add (aParam2); } } } aPrevParam2 = aParam2; aPrevPnt2 = aPnt2; aPrevVec2 = aVec2; ++j; } } } } // insert nodes of the regular grid for (Standard_Integer i = 1; i <= aParams[0].Length(); ++i) { const Standard_Real aParam1 = aParams[0].Value (i); if (aParamsToRemove[0].Contains (aParam1) && !aParamsForbiddenToRemove[0].Contains (aParam1)) continue; for (Standard_Integer j = 1; j <= aParams[1].Length(); ++j) { const Standard_Real aParam2 = aParams[1].Value (j); if (aParamsToRemove[1].Contains (aParam2) && !aParamsForbiddenToRemove[1].Contains (aParam2)) continue; gp_Pnt2d aPnt2d(aParam1, aParam2); // Classify intersection point if (aClassifier->Perform(aPnt2d) == TopAbs_IN) { gp_Pnt aPnt; gFace->D0(aPnt2d.X(), aPnt2d.Y(), aPnt); insertVertex(aPnt, aPnt2d.Coord(), theNewVertices); } } } } //======================================================================= //function : checkDeflectionAndInsert //purpose : //======================================================================= Standard_Boolean BRepMesh_FastDiscretFace::checkDeflectionAndInsert( const gp_Pnt& thePnt3d, const gp_XY& theUV, const Standard_Boolean isDeflectionCheckOnly, const Standard_Real theTriangleDeflection, const Standard_Real theFaceDeflection, const BRepMesh_CircleTool& theCircleTool, BRepMesh::ListOfVertex& theVertices, Standard_Real& theMaxTriangleDeflection, const Handle(NCollection_IncAllocator)& theTempAlloc) { if (theTriangleDeflection > theMaxTriangleDeflection) theMaxTriangleDeflection = theTriangleDeflection; if (theTriangleDeflection < theFaceDeflection) return Standard_True; if (myMinSize > Precision::Confusion()) { // Iterator in the list of indexes of circles containing the node BRepMesh::ListOfInteger& aCirclesList = const_cast(theCircleTool).Select( myAttribute->Scale(theUV, Standard_True)); BRepMesh::MapOfInteger aUsedNodes(10, theTempAlloc); BRepMesh::ListOfInteger::Iterator aCircleIt(aCirclesList); for (; aCircleIt.More(); aCircleIt.Next()) { const BRepMesh_Triangle& aTriangle = myStructure->GetElement(aCircleIt.Value()); Standard_Integer aNodes[3]; myStructure->ElementNodes(aTriangle, aNodes); for (Standard_Integer i = 0; i < 3; ++i) { const Standard_Integer aNodeId = aNodes[i]; if (aUsedNodes.Contains(aNodeId)) continue; aUsedNodes.Add(aNodeId); const BRepMesh_Vertex& aNode = myStructure->GetNode(aNodeId); const gp_Pnt& aPoint = myAttribute->GetPoint(aNode); if (thePnt3d.SquareDistance(aPoint) < myMinSize * myMinSize) return Standard_True; } } } if (isDeflectionCheckOnly) return Standard_False; insertVertex(thePnt3d, theUV, theVertices); return Standard_True; } //======================================================================= //function : control //purpose : //======================================================================= Standard_Real BRepMesh_FastDiscretFace::control( BRepMesh::ListOfVertex& theNewVertices, BRepMesh_Delaun& theTrigu, const Standard_Boolean theIsFirst) { Standard_Integer aTrianglesNb = myStructure->ElementsOfDomain().Extent(); if (aTrianglesNb < 1) return -1.0; //IMPORTANT: Constants used in calculations const Standard_Real MinimalArea2d = 1.e-9; const Standard_Real MinimalSqLength3d = 1.e-12; const Standard_Real aSqDefFace = myAttribute->GetDefFace() * myAttribute->GetDefFace(); const Handle(BRepAdaptor_HSurface)& gFace = myAttribute->Surface(); Handle(Geom_Surface) aBSpline; const GeomAbs_SurfaceType aSurfType = gFace->GetType (); if (IsCompexSurface (aSurfType) && aSurfType != GeomAbs_SurfaceOfExtrusion) aBSpline = gFace->ChangeSurface ().Surface().Surface(); Handle(NCollection_IncAllocator) anAlloc = new NCollection_IncAllocator(BRepMesh::MEMORY_BLOCK_SIZE_HUGE); NCollection_DataMap aNorMap(1, anAlloc); BRepMesh::MapOfIntegerInteger aStatMap(1, anAlloc); NCollection_Map aCouples(3 * aTrianglesNb, anAlloc); const BRepMesh_CircleTool& aCircles = theTrigu.Circles(); // Perform refinement passes // Define the number of iterations Standard_Integer aIterationsNb = 11; const Standard_Integer aPassesNb = (theIsFirst ? 1 : aIterationsNb); // Initialize stop condition Standard_Real aMaxSqDef = -1.; Standard_Integer aPass = 1, aInsertedNb = 1; Standard_Boolean isAllDegenerated = Standard_False; Handle(NCollection_IncAllocator) aTempAlloc = new NCollection_IncAllocator(BRepMesh::MEMORY_BLOCK_SIZE_HUGE); for (; aPass <= aPassesNb && aInsertedNb && !isAllDegenerated; ++aPass) { aTempAlloc->Reset(Standard_False); theNewVertices.Clear(); // Reset stop condition aInsertedNb = 0; aMaxSqDef = -1.; isAllDegenerated = Standard_True; aTrianglesNb = myStructure->ElementsOfDomain().Extent(); if (aTrianglesNb < 1) break; // Iterate on current triangles const BRepMesh::MapOfInteger& aTriangles = myStructure->ElementsOfDomain(); BRepMesh::MapOfInteger::Iterator aTriangleIt(aTriangles); for (; aTriangleIt.More(); aTriangleIt.Next()) { const Standard_Integer aTriangleId = aTriangleIt.Key(); const BRepMesh_Triangle& aCurrentTriangle = myStructure->GetElement(aTriangleId); if (aCurrentTriangle.Movability() == BRepMesh_Deleted) continue; Standard_Integer v[3]; myStructure->ElementNodes(aCurrentTriangle, v); Standard_Integer e[3]; Standard_Boolean o[3]; aCurrentTriangle.Edges(e, o); gp_XY xy[3]; gp_XYZ p[3]; Standard_Boolean m[3]; for (Standard_Integer i = 0; i < 3; ++i) { m[i] = (myStructure->GetLink(e[i]).Movability() == BRepMesh_Frontier); const BRepMesh_Vertex& aVertex = myStructure->GetNode(v[i]); xy[i] = myAttribute->Scale(aVertex.Coord(), Standard_False); p [i] = myAttribute->GetPoint(aVertex).Coord(); } gp_XYZ aLinkVec[3]; Standard_Boolean isDegeneratedTri = Standard_False; for (Standard_Integer i = 0; i < 3 && !isDegeneratedTri; ++i) { aLinkVec[i] = p[(i + 1) % 3] - p[i]; isDegeneratedTri = (aLinkVec[i].SquareModulus() < MinimalSqLength3d); } if (isDegeneratedTri) continue; isAllDegenerated = Standard_False; // Check triangle area in 2d if (Abs((xy[1]-xy[0])^(xy[2]-xy[1])) < MinimalArea2d) continue; // Check triangle normal gp_Pnt pDef; Standard_Real aSqDef = -1.; Standard_Boolean isSkipped = Standard_False; gp_XYZ normal(aLinkVec[0] ^ aLinkVec[1]); if (normal.SquareModulus () < gp::Resolution()) continue; normal.Normalize(); // Check deflection at triangle centroid gp_XY aCenter2d = (xy[0] + xy[1] + xy[2]) / 3.0; gFace->D0(aCenter2d.X(), aCenter2d.Y(), pDef); aSqDef = Abs(normal * (pDef.XYZ() - p[0])); aSqDef *= aSqDef; isSkipped = !checkDeflectionAndInsert(pDef, aCenter2d, theIsFirst, aSqDef, aSqDefFace, aCircles, theNewVertices, aMaxSqDef, aTempAlloc); if (isSkipped) break; // Check deflection at triangle links for (Standard_Integer i = 0; i < 3 && !isSkipped; ++i) { if (m[i]) // is a boundary continue; Standard_Integer j = (i + 1) % 3; // Check if this link was already processed Standard_Integer aFirstVertex, aLastVertex; if (v[i] < v[j]) { aFirstVertex = v[i]; aLastVertex = v[j]; } else { aFirstVertex = v[j]; aLastVertex = v[i]; } if (aCouples.Add(BRepMesh_OrientedEdge(aFirstVertex, aLastVertex))) { // Check deflection on edge 1 gp_XY mi2d = (xy[i] + xy[j]) * 0.5; gFace->D0(mi2d.X(), mi2d.Y(), pDef); gp_Lin aLin(p[i], gp_Vec(p[i], p[j])); aSqDef = aLin.SquareDistance(pDef); isSkipped = !checkDeflectionAndInsert(pDef, mi2d, theIsFirst, aSqDef, aSqDefFace, aCircles, theNewVertices, aMaxSqDef, aTempAlloc); } } if (isSkipped) break; //check normal on bsplines if (theIsFirst && !aBSpline.IsNull()) { gp_Dir N[3] = { gp::DZ(), gp::DZ(), gp::DZ() }; Standard_Integer aSt[3]; for (Standard_Integer i = 0; i < 3; ++i) { if (aNorMap.IsBound(v[i])) { aSt[i] = aStatMap.Find(v[i]); N[i] = aNorMap.Find(v[i]); } else { aSt[i] = GeomLib::NormEstim(aBSpline, gp_Pnt2d(xy[i]), Precision::Confusion(), N[i]); aStatMap.Bind(v[i], aSt[i]); aNorMap.Bind(v[i], N[i]); } } Standard_Real aAngle[3]; for (Standard_Integer i = 0; i < 3; ++i) aAngle[i] = N[(i + 1) % 3].Angle(N[i]); if (aSt[0] < 1 && aSt[1] < 1 && aSt[2] < 1) { if (aAngle[0] > myAngle || aAngle[1] > myAngle || aAngle[2] > myAngle) { aMaxSqDef = -1.; break; } } } } if (theIsFirst) continue; if (addVerticesToMesh(theNewVertices, theTrigu)) ++aInsertedNb; } return (aMaxSqDef < 0) ? aMaxSqDef : Sqrt(aMaxSqDef); } //======================================================================= //function : add //purpose : //======================================================================= void BRepMesh_FastDiscretFace::add(const TopoDS_Vertex& theVertex) { if (theVertex.Orientation() != TopAbs_INTERNAL) return; try { OCC_CATCH_SIGNALS gp_Pnt2d aPnt2d = BRep_Tool::Parameters(theVertex, myAttribute->Face()); // check UV values for internal vertices if (myAttribute->ChangeClassifier()->Perform(aPnt2d) != TopAbs_IN) return; NCollection_Handle aFixedVExplorer = new FixedVExplorer(theVertex); Standard_Integer aIndex = myAttribute->GetVertexIndex(aFixedVExplorer); gp_XY anUV = BRepMesh_ShapeTool::FindUV(aIndex, aPnt2d, theVertex, BRep_Tool::Tolerance(theVertex), myAttribute); Standard_Integer aTmpId1, aTmpId2; anUV = myAttribute->Scale(anUV, Standard_True); myAttribute->AddNode(aIndex, anUV, BRepMesh_Fixed, aTmpId1, aTmpId2); } catch (Standard_Failure) { } } //======================================================================= //function : insertVertex //purpose : //======================================================================= void BRepMesh_FastDiscretFace::insertVertex( const gp_Pnt& thePnt3d, const gp_XY& theUV, BRepMesh::ListOfVertex& theVertices) { Standard_Integer aNbLocat = myAttribute->LastPointId(); myAttribute->ChangeSurfacePoints()->Bind(++aNbLocat, thePnt3d); gp_XY aPnt2d = myAttribute->Scale(theUV, Standard_True); BRepMesh_Vertex aVertex(aPnt2d, aNbLocat, BRepMesh_Free); theVertices.Append(aVertex); } //======================================================================= //function : commitSurfaceTriangulation //purpose : //======================================================================= void BRepMesh_FastDiscretFace::commitSurfaceTriangulation() { if (myAttribute.IsNull() || !myAttribute->IsValid()) return; const TopoDS_Face& aFace = myAttribute->Face(); BRepMesh_ShapeTool::NullifyFace(aFace); Handle(BRepMesh_DataStructureOfDelaun)& aStructure = myAttribute->ChangeStructure(); const BRepMesh::MapOfInteger& aTriangles = aStructure->ElementsOfDomain(); if (aTriangles.IsEmpty()) { myAttribute->SetStatus(BRepMesh_Failure); return; } BRepMesh::HIMapOfInteger& aVetrexEdgeMap = myAttribute->ChangeVertexEdgeMap(); // Store triangles Standard_Integer aVerticesNb = aVetrexEdgeMap->Extent(); Standard_Integer aTrianglesNb = aTriangles.Extent(); Handle(Poly_Triangulation) aNewTriangulation = new Poly_Triangulation(aVerticesNb, aTrianglesNb, Standard_True); Poly_Array1OfTriangle& aPolyTrianges = aNewTriangulation->ChangeTriangles(); Standard_Integer aTriangeId = 1; BRepMesh::MapOfInteger::Iterator aTriIt(aTriangles); for (; aTriIt.More(); aTriIt.Next()) { const BRepMesh_Triangle& aCurElem = aStructure->GetElement(aTriIt.Key()); Standard_Integer aNode[3]; aStructure->ElementNodes(aCurElem, aNode); Standard_Integer aNodeId[3]; for (Standard_Integer i = 0; i < 3; ++i) aNodeId[i] = aVetrexEdgeMap->FindIndex(aNode[i]); aPolyTrianges(aTriangeId++).Set(aNodeId[0], aNodeId[1], aNodeId[2]); } // Store mesh nodes TColgp_Array1OfPnt& aNodes = aNewTriangulation->ChangeNodes(); TColgp_Array1OfPnt2d& aNodes2d = aNewTriangulation->ChangeUVNodes(); for (Standard_Integer i = 1; i <= aVerticesNb; ++i) { Standard_Integer aVertexId = aVetrexEdgeMap->FindKey(i); const BRepMesh_Vertex& aVertex = aStructure->GetNode(aVertexId); const gp_Pnt& aPoint = myAttribute->GetPoint(aVertex); aNodes(i) = aPoint; aNodes2d(i) = aVertex.Coord(); } aNewTriangulation->Deflection(myAttribute->GetDefFace()); BRepMesh_ShapeTool::AddInFace(aFace, aNewTriangulation); // Delete unused data myUParam.Clear(0L); myVParam.Clear(0L); myAttribute->ChangeStructure().Nullify(); myAttribute->ChangeSurfacePoints().Nullify(); myAttribute->ChangeSurfaceVertices().Nullify(); myAttribute->ChangeClassifier().Nullify(); myAttribute->ChangeLocation2D().Nullify(); myAttribute->ChangeVertexEdgeMap().Nullify(); }