-// Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2023 CEA, EDF, OPEN CASCADE
//
// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
#include <Geom_Surface.hxx>
#include <NCollection_DefineArray2.hxx>
#include <Precision.hxx>
+#include <ShapeAnalysis.hxx>
#include <TColStd_SequenceOfInteger.hxx>
#include <TColStd_SequenceOfReal.hxx>
#include <TColgp_SequenceOfXY.hxx>
//=============================================================================
/*!
- *
+ * Compute the mesh on the given shape
*/
//=============================================================================
-bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh& aMesh,
- const TopoDS_Shape& aShape)
+bool StdMeshers_Quadrangle_2D::Compute( SMESH_Mesh& aMesh,
+ const TopoDS_Shape& aShape )
{
const TopoDS_Face& F = TopoDS::Face(aShape);
aMesh.GetSubMesh( F );
std::vector<int> aNbNodes(4);
bool IsQuadratic = false;
if (!checkNbEdgesForEvaluate(aMesh, aFace, aResMap, aNbNodes, IsQuadratic)) {
- std::vector<int> aResVec(SMDSEntity_Last);
+ std::vector<smIdType> aResVec(SMDSEntity_Last);
for (int i=SMDSEntity_Node; i<SMDSEntity_Last; i++) aResVec[i] = 0;
SMESH_subMesh * sm = aMesh.GetSubMesh(aFace);
aResMap.insert(std::make_pair(sm,aResVec));
//int nbFaces4 = (nbhoriz-1-kdh)*(nbvertic-1-kdv);
int nbFaces4 = (nbhoriz-1)*(nbvertic-1);
- std::vector<int> aVec(SMDSEntity_Last);
- for (int i=SMDSEntity_Node; i<SMDSEntity_Last; i++) aVec[i] = 0;
+ std::vector<smIdType> aVec(SMDSEntity_Last,0);
if (IsQuadratic) {
aVec[SMDSEntity_Quad_Triangle] = nbFaces3;
aVec[SMDSEntity_Quad_Quadrangle] = nbFaces4;
return true;
}
+ //================================================================================
+ /*!
+ * \brief Return angle between mesh segments of given EDGEs meeting at theVertexNode
+ */
+ //================================================================================
+
+ double getAngleByNodes( const int theE1Index,
+ const int theE2Index,
+ const SMDS_MeshNode* theVertexNode,
+ const StdMeshers_FaceSide& theFaceSide,
+ const gp_Vec& theFaceNormal)
+ {
+ int eID1 = theFaceSide.EdgeID( theE1Index );
+ int eID2 = theFaceSide.EdgeID( theE2Index );
+
+ const SMDS_MeshNode *n1 = 0, *n2 = 0;
+ bool is1st;
+ SMDS_ElemIteratorPtr segIt = theVertexNode->GetInverseElementIterator( SMDSAbs_Edge );
+ while ( segIt->more() )
+ {
+ const SMDS_MeshElement* seg = segIt->next();
+ int shapeID = seg->GetShapeID();
+ if ( shapeID == eID1 )
+ is1st = true;
+ else if ( shapeID == eID2 )
+ is1st = false;
+ else
+ continue;
+ ( is1st ? n1 : n2 ) = seg->GetNodeWrap( 1 + seg->GetNodeIndex( theVertexNode ));
+ }
+
+ if ( !n1 || !n2 )
+ {
+ std::vector<const SMDS_MeshNode*> nodes;
+ for ( int is2nd = 0; is2nd < 2; ++is2nd )
+ {
+ const SMDS_MeshNode* & n = is2nd ? n2 : n1;
+ if ( n ) continue;
+ nodes.clear();
+ if ( is2nd ) theFaceSide.GetEdgeNodes( theE2Index, nodes );
+ else theFaceSide.GetEdgeNodes( theE1Index, nodes );
+ if ( nodes.size() >= 2 )
+ {
+ if ( nodes[0] == theVertexNode )
+ n = nodes[1];
+ else
+ n = nodes[ nodes.size() - 2 ];
+ }
+ }
+ }
+ double angle = -2 * M_PI;
+ if ( n1 && n2 )
+ {
+ SMESH_NodeXYZ p1 = n1, p2 = theVertexNode, p3 = n2;
+ gp_Vec v1( p1, p2 ), v2( p2, p3 );
+ try
+ {
+ angle = v1.AngleWithRef( v2, theFaceNormal );
+ }
+ catch(...)
+ {
+ }
+ if ( std::isnan( angle ))
+ angle = -2 * M_PI;
+ }
+ return angle;
+ }
+
//--------------------------------------------------------------------------------
/*!
* \brief EDGE of a FACE
TopoDS_Edge myEdge;
TopoDS_Vertex my1stVertex;
int myIndex;
+ bool myIsCorner; // is fixed corner
double myAngle; // angle at my1stVertex
int myNbSegments; // discretization
Edge* myPrev; // preceding EDGE
// quality criteria to minimize
int myOppDiff;
+ int myIsFixedCorner;
double myQuartDiff;
double mySumAngle;
myOppDiff = ( Abs( myNbSeg[0] - myNbSeg[2] ) +
Abs( myNbSeg[1] - myNbSeg[3] ));
+ myIsFixedCorner = - totNbSeg * ( myCornerE[0]->myIsCorner +
+ myCornerE[1]->myIsCorner +
+ myCornerE[2]->myIsCorner +
+ myCornerE[3]->myIsCorner );
+
double nbSideIdeal = totNbSeg / 4.;
myQuartDiff = -( Min( Min( myNbSeg[0], myNbSeg[1] ),
Min( myNbSeg[2], myNbSeg[3] )) / nbSideIdeal );
theVariants.insert( *this );
-#ifndef _DEBUG_
- if ( theVariants.size() > 1 ) // erase a worse variant
+ if (SALOME::VerbosityActivated() && theVariants.size() > 1 ) // erase a worse variant
theVariants.erase( ++theVariants.begin() );
-#endif
};
// first criterion - equality of nbSeg of opposite sides
- int crit1() const { return myOppDiff; }
+ int crit1() const { return myOppDiff + myIsFixedCorner; }
// second criterion - equality of nbSeg of adjacent sides and sharpness of angles
double crit2() const { return myQuartDiff + mySumAngle; }
//================================================================================
/*!
* \brief Unite EDGEs to get a required number of sides
- * \param [in] theNbCorners - the required number of sides
+ * \param [in] theNbCorners - the required number of sides, 3 or 4
* \param [in] theConsiderMesh - to considered only meshed VERTEXes
* \param [in] theFaceSide - the FACE EDGEs
+ * \param [in] theFixedVertices - VERTEXes to be used as corners
* \param [out] theVertices - the found corner vertices
+ * \param [out] theHaveConcaveVertices - return if there are concave vertices
*/
//================================================================================
void uniteEdges( const int theNbCorners,
const bool theConsiderMesh,
const StdMeshers_FaceSide& theFaceSide,
- const TopoDS_Shape& theBaseVertex,
+ const TopTools_MapOfShape& theFixedVertices,
std::vector<TopoDS_Vertex>& theVertices,
bool& theHaveConcaveVertices)
{
// sort edges by angle
std::multimap< double, Edge* > edgeByAngle;
- int i, iBase = -1, nbConvexAngles = 0, nbSharpAngles = 0;
+ int i, nbConvexAngles = 0, nbSharpAngles = 0;
+ const SMDS_MeshNode* vertNode = 0;
+ gp_Vec faceNormal;
const double angTol = 5. / 180 * M_PI;
const double sharpAngle = 0.5 * M_PI - angTol;
Edge* e = edge0;
for ( i = 0; i < nbEdges; ++i, e = e->myNext )
{
e->my1stVertex = SMESH_MesherHelper::IthVertex( 0, e->myEdge );
- if ( e->my1stVertex.IsSame( theBaseVertex ))
- iBase = e->myIndex;
+ e->myIsCorner = theFixedVertices.Contains( e->my1stVertex );
e->myAngle = -2 * M_PI;
- if ( !theConsiderMesh || theFaceSide.VertexNode( e->myIndex ))
+ if ( !theConsiderMesh || ( vertNode = theFaceSide.VertexNode( e->myIndex )))
{
e->myAngle = SMESH_MesherHelper::GetAngle( e->myPrev->myEdge, e->myEdge,
- theFaceSide.Face(), e->my1stVertex );
+ theFaceSide.Face(), e->my1stVertex,
+ &faceNormal );
if ( e->myAngle > 2 * M_PI ) // GetAngle() failed
e->myAngle *= -1.;
+ else if ( vertNode && ( 0. <= e->myAngle ) && ( e->myAngle <= angTol ))
+ e->myAngle = getAngleByNodes( e->myPrev->myIndex, e->myIndex,
+ vertNode, theFaceSide, faceNormal );
}
edgeByAngle.insert( std::make_pair( e->myAngle, e ));
nbConvexAngles += ( e->myAngle > angTol );
// return corners with maximal angles
std::set< int > cornerIndices;
- if ( iBase != -1 )
- cornerIndices.insert( iBase );
+ if ( !theFixedVertices.IsEmpty() )
+ for ( i = 0, e = edge0; i < nbEdges; ++i, e = e->myNext )
+ if ( e->myIsCorner )
+ cornerIndices.insert( e->myIndex );
std::multimap< double, Edge* >::reverse_iterator a2e = edgeByAngle.rbegin();
for (; (int) cornerIndices.size() < theNbCorners; ++a2e )
return;
}
+ //================================================================================
+ /*!
+ * \brief Remove a seam and degenerated edge from a wire if the shape is
+ * a quadrangle with a seam inside.
+ */
+ //================================================================================
+
+ bool removeInternalSeam( std::list<TopoDS_Edge>& theWire,
+ SMESH_MesherHelper& theHelper)
+ {
+ if ( !theHelper.HasRealSeam() ||
+ theHelper.NbDegeneratedEdges() != 2 ) // 1 EDGE + 1 VERTEX
+ return false;
+
+ typedef std::list<TopoDS_Edge>::iterator TEdgeIter;
+ std::vector< TEdgeIter > edgesToRemove;
+ edgesToRemove.reserve( 5 );
+ for ( TEdgeIter eIt = theWire.begin(); eIt != theWire.end(); ++eIt )
+ {
+ int eID = theHelper.ShapeToIndex( *eIt );
+ if ( theHelper.IsRealSeam( eID ) || theHelper.IsDegenShape( eID ))
+ edgesToRemove.push_back( eIt );
+ }
+
+ if ( theWire.size() - edgesToRemove.size() < 4 )
+ return false; // cone e.g.
+
+ for ( size_t i = 0; i < edgesToRemove.size(); ++i )
+ theWire.erase( edgesToRemove[ i ]);
+
+ return true;
+ }
+
} // namespace
//================================================================================
if ( myHelper )
helper.CopySubShapeInfo( *myHelper );
+ if ( removeInternalSeam( theWire, helper ))
+ theNbDegenEdges = 1;
+
StdMeshers_FaceSide faceSide( theFace, theWire, &theMesh,
/*isFwd=*/true, /*skipMedium=*/true, &helper );
if ( theConsiderMesh )
{
- const int nbSegments = Max( faceSide.NbPoints()-1, faceSide.NbSegments() );
+ const smIdType nbSegments = std::max( faceSide.NbPoints()-1, faceSide.NbSegments() );
if ( nbSegments < nbCorners )
return error(COMPERR_BAD_INPUT_MESH, TComm("Too few boundary nodes: ") << nbSegments);
}
myCheckOri = false;
if ( theVertices.size() > 3 )
{
- uniteEdges( nbCorners, theConsiderMesh, faceSide, triaVertex, theVertices, myCheckOri );
+ TopTools_MapOfShape fixedVertices;
+ if ( !triaVertex.IsNull() )
+ fixedVertices.Add( triaVertex );
+ if ( myParams )
+ {
+ const std::vector< int >& vIDs = myParams->GetCorners();
+ for ( size_t i = 0; i < vIDs.size(); ++i )
+ {
+ const TopoDS_Shape& vertex = helper.GetMeshDS()->IndexToShape( vIDs[ i ]);
+ if ( !vertex.IsNull() )
+ fixedVertices.Add( vertex );
+ }
+ }
+ uniteEdges( nbCorners, theConsiderMesh, faceSide, fixedVertices, theVertices, myCheckOri );
}
if ( nbCorners == 3 && !triaVertex.IsSame( theVertices[0] ))
//=============================================================================
/*!
- *
+ * Return FaceQuadStruct where sides ordered CCW, top and left sides
+ * reversed to be co-directed with bottom and right sides
*/
//=============================================================================
if (anIt==aResMap.end()) {
return false;
}
- std::vector<int> aVec = (*anIt).second;
+ std::vector<smIdType> aVec = (*anIt).second;
IsQuadratic = (aVec[SMDSEntity_Quad_Edge] > aVec[SMDSEntity_Edge]);
if (nbEdgesInWire.front() == 3) { // exactly 3 edges
if (myTriaVertexID>0) {
SMESH_subMesh * sm = aMesh.GetSubMesh(E1);
MapShapeNbElemsItr anIt = aResMap.find(sm);
if (anIt==aResMap.end()) return false;
- std::vector<int> aVec = (*anIt).second;
+ std::vector<smIdType> aVec = (*anIt).second;
if (IsQuadratic)
aNbNodes[0] = (aVec[SMDSEntity_Node]-1)/2 + 2;
else
if (anIt==aResMap.end()) {
return false;
}
- std::vector<int> aVec = (*anIt).second;
+ std::vector<smIdType> aVec = (*anIt).second;
if (IsQuadratic)
aNbNodes[nbSides] = (aVec[SMDSEntity_Node]-1)/2 + 2;
else
}
}
list<TopoDS_Edge>::iterator ite = sideEdges.begin();
+ if ( nbSides >= (int)aNbNodes.size() )
+ return false;
aNbNodes[nbSides] = 1;
for (; ite!=sideEdges.end(); ite++) {
SMESH_subMesh * sm = aMesh.GetSubMesh(*ite);
if (anIt==aResMap.end()) {
return false;
}
- std::vector<int> aVec = (*anIt).second;
+ std::vector<smIdType> aVec = (*anIt).second;
if (IsQuadratic)
aNbNodes[nbSides] += (aVec[SMDSEntity_Node]-1)/2 + 1;
else
if (anIt==aResMap.end()) {
return false;
}
- std::vector<int> aVec = (*anIt).second;
+ std::vector<smIdType> aVec = (*anIt).second;
if (IsQuadratic)
aNbNodes[nbSides] += (aVec[SMDSEntity_Node]-1)/2 + 1;
else
nbFaces += (drl+addv)*(nb-1) + (nt-1);
} // end new version implementation
- std::vector<int> aVec(SMDSEntity_Last);
+ std::vector<smIdType> aVec(SMDSEntity_Last);
for (int i=SMDSEntity_Node; i<SMDSEntity_Last; i++) aVec[i] = 0;
if (IsQuadratic) {
aVec[SMDSEntity_Quad_Quadrangle] = nbFaces;
*/
//=============================================================================
-void StdMeshers_Quadrangle_2D::splitQuadFace(SMESHDS_Mesh * theMeshDS,
- int theFaceID,
+void StdMeshers_Quadrangle_2D::splitQuadFace(SMESHDS_Mesh * /*theMeshDS*/,
+ int /*theFaceID*/,
const SMDS_MeshNode* theNode1,
const SMDS_MeshNode* theNode2,
const SMDS_MeshNode* theNode3,
SMESH_TNodeXYZ a2( quad->UVPt( nbhoriz-1, nbvertic-1 ).node );
SMESH_TNodeXYZ a3( quad->UVPt( 0, nbvertic-1 ).node );
+ // compute TFI
for (int i = 1; i < nbhoriz-1; i++)
{
SMESH_TNodeXYZ p0( quad->UVPt( i, 0 ).node );
UVPtStruct& uvp = quad->UVPt( i, j );
- gp_Pnt p = myHelper->calcTFI(uvp.x,uvp.y, a0,a1,a2,a3, p0,p1,p2,p3);
+ gp_Pnt pnew = myHelper->calcTFI(uvp.x,uvp.y, a0,a1,a2,a3, p0,p1,p2,p3);
+ meshDS->MoveNode( uvp.node, pnew.X(), pnew.Y(), pnew.Z() );
+ }
+ }
+ // project to surface
+ double cellSize;
+ for (int i = 1; i < nbhoriz-1; i++)
+ {
+ for (int j = 1; j < nbvertic-1; j++)
+ {
+ UVPtStruct& uvp = quad->UVPt( i, j );
+ SMESH_NodeXYZ p = uvp.node;
+
+ cellSize = Max( p.SquareDistance( quad->UVPt( i+1, j ).node ),
+ p.SquareDistance( quad->UVPt( i-1, j ).node ));
+ cellSize = Max( p.SquareDistance( quad->UVPt( i, j+1 ).node ), cellSize );
+ cellSize = Max( p.SquareDistance( quad->UVPt( i, j-1 ).node ), cellSize );
+
gp_Pnt2d uv = surface->NextValueOfUV( uvp.UV(), p, 10*tol );
gp_Pnt pnew = surface->Value( uv );
-
- meshDS->MoveNode( uvp.node, pnew.X(), pnew.Y(), pnew.Z() );
- uvp.u = uv.X();
- uvp.v = uv.Y();
+ bool ok = ( pnew.SquareDistance( p ) < 2 * cellSize );
+ if ( !ok )
+ {
+ uv = surface->ValueOfUV( p, 10*tol );
+ pnew = surface->Value( uv );
+ ok = ( pnew.SquareDistance( p ) < 2 * cellSize );
+ }
+ if ( ok )
+ {
+ meshDS->MoveNode( uvp.node, pnew.X(), pnew.Y(), pnew.Z() );
+ uvp.u = uv.X();
+ uvp.v = uv.Y();
+ }
}
}
}
const SMDS_MeshNode* nInFace = 0;
if ( myHelper->HasSeam() )
+ {
for ( int i = 0; i < nbN && !nInFace; ++i )
if ( !myHelper->IsSeamShape( nn[i]->getshapeId() ))
{
if ( myHelper->IsOnSeam( uv ))
nInFace = NULL;
}
+ }
+ if ( myHelper->GetPeriodicIndex() && !nInFace )
+ {
+ for ( int i = 0; i < nbN && !nInFace; ++i )
+ if ( fSubMesh->Contains( nn[i] ))
+ nInFace = nn[i];
+ if ( !nInFace )
+ for ( int i = 0; i < nbN && !nInFace; ++i )
+ {
+ SMDS_ElemIteratorPtr fIt = nn[i]->GetInverseElementIterator( SMDSAbs_Face );
+ while ( fIt->more() && !nInFace )
+ {
+ const SMDS_MeshElement* face = fIt->next();
+ if ( !fSubMesh->Contains( face ))
+ continue;
+ for ( int iN = 0, nN = face->NbCornerNodes(); iN < nN; ++iN )
+ {
+ const SMDS_MeshNode* n = face->GetNode( iN );
+ if ( fSubMesh->Contains( n ))
+ {
+ nInFace = n;
+ break;
+ }
+ }
+ }
+ }
+ }
toCheckUV = true;
for ( int i = 0; i < nbN; ++i )
default:;
}
- // if ( isBad && myHelper->HasRealSeam() )
- // {
- // // detect a case where a face intersects the seam
- // for ( int iPar = 1; iPar < 3; ++iPar )
- // if ( iPar & myHelper->GetPeriodicIndex() )
- // {
- // double min = uv[0].Coord( iPar ), max = uv[0].Coord( iPar );
- // for ( int i = 1; i < nbN; ++i )
- // {
- // min = Min( min, uv[i].Coord( iPar ));
- // max = Max( max, uv[i].Coord( iPar ));
- // }
- // }
- // }
+ if ( isBad && myHelper->HasRealSeam() )
+ {
+ // fix uv for a case where a face intersects the seam
+ for ( int iPar = 1; iPar < 3; ++iPar )
+ if ( iPar & myHelper->GetPeriodicIndex() )
+ {
+ double max = uv[0].Coord( iPar );
+ for ( int i = 1; i < nbN; ++i )
+ max = Max( max, uv[i].Coord( iPar ));
+
+ for ( int i = 0; i < nbN; ++i )
+ {
+ double par = uv[i].Coord( iPar );
+ double shift = ShapeAnalysis::AdjustByPeriod( par, max, myHelper->GetPeriod( iPar ));
+ uv[i].SetCoord( iPar, par + shift );
+ }
+ }
+ double sign1 = getArea( uv[0], uv[1], uv[2] );
+ double sign2 = getArea( uv[0], uv[2], uv[3] );
+ if ( sign1 * sign2 < 0 )
+ {
+ sign2 = getArea( uv[1], uv[2], uv[3] );
+ sign1 = getArea( uv[1], uv[3], uv[0] );
+ if ( sign1 * sign2 < 0 )
+ continue; // this should not happen
+ }
+ isBad = ( sign1 * okSign < 0 );
+ }
+
if ( isBad )
badFaces.push_back ( f );
}