Salome HOME
[bos #40653][CEA] New mesh import export formats with meshio.
[modules/smesh.git] / src / StdMeshers / StdMeshers_Quadrangle_2D.cxx
index 19461a7744fc0c6d7acf98e2da70bdd51eb5f962..e05279c3598008534aabff0fae37bece57051c9f 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2024  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
@@ -51,6 +51,7 @@
 #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>
@@ -203,12 +204,12 @@ bool StdMeshers_Quadrangle_2D::CheckHypothesis
 
 //=============================================================================
 /*!
- *
+ * Compute the mesh on the given shape
  */
 //=============================================================================
 
-bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh&         aMesh,
-                                        const TopoDS_Shape& aShape)
+bool StdMeshers_Quadrangle_2D::ComputeSMESH_Mesh&         aMesh,
+                                        const TopoDS_Shape& aShape )
 {
   const TopoDS_Face& F = TopoDS::Face(aShape);
   aMesh.GetSubMesh( F );
@@ -916,7 +917,7 @@ bool StdMeshers_Quadrangle_2D::Evaluate(SMESH_Mesh&         aMesh,
   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));
@@ -965,8 +966,7 @@ bool StdMeshers_Quadrangle_2D::Evaluate(SMESH_Mesh&         aMesh,
   //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;
@@ -1049,6 +1049,74 @@ namespace
     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
@@ -1058,6 +1126,7 @@ namespace
     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
@@ -1086,6 +1155,7 @@ namespace
 
     // quality criteria to minimize
     int    myOppDiff;
+    int    myIsFixedCorner;
     double myQuartDiff;
     double mySumAngle;
 
@@ -1113,20 +1183,23 @@ namespace
       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; }
@@ -1144,17 +1217,19 @@ namespace
   //================================================================================
   /*!
    * \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)
   {
@@ -1184,23 +1259,28 @@ namespace
 
     // 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 );
@@ -1228,8 +1308,10 @@ namespace
       // 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 )
@@ -1351,6 +1433,39 @@ namespace
     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
 
 //================================================================================
@@ -1381,6 +1496,9 @@ int StdMeshers_Quadrangle_2D::getCorners(const TopoDS_Face&          theFace,
   if ( myHelper )
     helper.CopySubShapeInfo( *myHelper );
 
+  if ( removeInternalSeam( theWire, helper ))
+    theNbDegenEdges = 1;
+
   StdMeshers_FaceSide faceSide( theFace, theWire, &theMesh,
                                 /*isFwd=*/true, /*skipMedium=*/true, &helper );
 
@@ -1411,7 +1529,7 @@ int StdMeshers_Quadrangle_2D::getCorners(const TopoDS_Face&          theFace,
 
   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);
   }
@@ -1445,7 +1563,20 @@ int StdMeshers_Quadrangle_2D::getCorners(const TopoDS_Face&          theFace,
   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] ))
@@ -1473,7 +1604,8 @@ int StdMeshers_Quadrangle_2D::getCorners(const TopoDS_Face&          theFace,
 
 //=============================================================================
 /*!
- *
+ * Return FaceQuadStruct where sides ordered CCW, top and left sides
+ *        reversed to be co-directed with bottom and right sides
  */
 //=============================================================================
 
@@ -1634,7 +1766,7 @@ bool StdMeshers_Quadrangle_2D::checkNbEdgesForEvaluate(SMESH_Mesh&          aMes
   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) {
@@ -1656,7 +1788,7 @@ bool StdMeshers_Quadrangle_2D::checkNbEdgesForEvaluate(SMESH_Mesh&          aMes
         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
@@ -1690,7 +1822,7 @@ bool StdMeshers_Quadrangle_2D::checkNbEdgesForEvaluate(SMESH_Mesh&          aMes
       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
@@ -1718,6 +1850,8 @@ bool StdMeshers_Quadrangle_2D::checkNbEdgesForEvaluate(SMESH_Mesh&          aMes
         }
       }
       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);
@@ -1725,7 +1859,7 @@ bool StdMeshers_Quadrangle_2D::checkNbEdgesForEvaluate(SMESH_Mesh&          aMes
         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
@@ -1766,7 +1900,7 @@ bool StdMeshers_Quadrangle_2D::checkNbEdgesForEvaluate(SMESH_Mesh&          aMes
           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
@@ -3043,7 +3177,7 @@ bool StdMeshers_Quadrangle_2D::evaluateQuadPref(SMESH_Mesh &        aMesh,
     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;
@@ -3073,8 +3207,8 @@ bool StdMeshers_Quadrangle_2D::evaluateQuadPref(SMESH_Mesh &        aMesh,
  */
 //=============================================================================
 
-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,
@@ -4335,6 +4469,7 @@ void StdMeshers_Quadrangle_2D::smooth (FaceQuadStruct::Ptr quad)
       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 );
@@ -4346,13 +4481,39 @@ void StdMeshers_Quadrangle_2D::smooth (FaceQuadStruct::Ptr quad)
 
           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();
+          }
         }
       }
     }
@@ -4613,6 +4774,7 @@ bool StdMeshers_Quadrangle_2D::check()
 
     const SMDS_MeshNode* nInFace = 0;
     if ( myHelper->HasSeam() )
+    {
       for ( int i = 0; i < nbN && !nInFace; ++i )
         if ( !myHelper->IsSeamShape( nn[i]->getshapeId() ))
         {
@@ -4621,6 +4783,33 @@ bool StdMeshers_Quadrangle_2D::check()
           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 )
@@ -4651,20 +4840,35 @@ bool StdMeshers_Quadrangle_2D::check()
     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 );
   }