Salome HOME
52568: Quadrangle (Mapping) differently meshes equal L-shaped faces.
[modules/smesh.git] / src / StdMeshers / StdMeshers_Quadrangle_2D.cxx
index 7adf2294f674da4706867b3d61664c14bf8891ce..4d1321497a6d08eb6ad1fe25b8c4b43227dc3647 100644 (file)
@@ -67,9 +67,7 @@
 #ifndef StdMeshers_Array2OfNode_HeaderFile
 #define StdMeshers_Array2OfNode_HeaderFile
 typedef const SMDS_MeshNode* SMDS_MeshNodePtr;
-DEFINE_BASECOLLECTION (StdMeshers_BaseCollectionNodePtr, SMDS_MeshNodePtr)
-DEFINE_ARRAY2(StdMeshers_Array2OfNode,
-              StdMeshers_BaseCollectionNodePtr, SMDS_MeshNodePtr)
+typedef NCollection_Array2<SMDS_MeshNodePtr> StdMeshers_Array2OfNode;
 #endif
 
 using namespace std;
@@ -90,6 +88,7 @@ StdMeshers_Quadrangle_2D::StdMeshers_Quadrangle_2D (int hypId, int studyId,
     myTrianglePreference(false),
     myTriaVertexID(-1),
     myNeedSmooth(false),
+    myCheckOri(false),
     myParams( NULL ),
     myQuadType(QUAD_STANDARD),
     myHelper( NULL )
@@ -200,7 +199,9 @@ bool StdMeshers_Quadrangle_2D::CheckHypothesis
     }
   }
 
-  return isOk;
+  error( StdMeshers_ViscousLayers2D::CheckHypothesis( aMesh, aShape, aStatus ));
+
+  return aStatus == HYP_OK;
 }
 
 //=============================================================================
@@ -228,6 +229,7 @@ bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh&         aMesh,
 
   _quadraticMesh = myHelper->IsQuadraticSubMesh(aShape);
   myNeedSmooth = false;
+  myCheckOri   = false;
 
   FaceQuadStruct::Ptr quad = CheckNbEdges( aMesh, F, /*considerMesh=*/true );
   if (!quad)
@@ -275,7 +277,7 @@ bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh&         aMesh,
                "two opposite sides should have same number of segments, "
                "but actual number of segments is different on all sides. "
                "'Standard' transion has been used.");
-      else
+      else if ( ! ( n1 == n3 && n2 == n4 ))
         error( COMPERR_WARNING,
                "To use 'Reduced' transition, "
                "two opposite sides should have an even difference in number of segments. "
@@ -294,6 +296,9 @@ bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh&         aMesh,
   if ( res == COMPUTE_OK && myNeedSmooth )
     smooth( quad );
 
+  if ( res == COMPUTE_OK )
+    res = check();
+
   return ( res == COMPUTE_OK );
 }
 
@@ -334,7 +339,7 @@ bool StdMeshers_Quadrangle_2D::computeTriangles(SMESH_Mesh&         aMesh,
   FaceQuadStruct::Ptr newQuad = myQuadList.back();
   if ( quad != newQuad ) // split done
   {
-    {
+    { // update left side limit till where to make triangles
       FaceQuadStruct::Ptr botQuad = // a bottom part
         ( quad->side[ QUAD_LEFT_SIDE ].from == 0 ) ? quad : newQuad;
       if ( botQuad->nbNodeOut( QUAD_LEFT_SIDE ) > 0 )
@@ -354,12 +359,33 @@ bool StdMeshers_Quadrangle_2D::computeTriangles(SMESH_Mesh&         aMesh,
   {
     splitQuad( quad, quad->iSize-2, 0 );
   }
-  if ( quad->nbNodeOut( QUAD_LEFT_SIDE    ))
+  if ( quad->nbNodeOut( QUAD_LEFT_SIDE  ))
   {
     splitQuad( quad, 1, 0 );
+
+    if ( quad->nbNodeOut( QUAD_TOP_SIDE ))
+    {
+      newQuad = myQuadList.back();
+      if ( newQuad == quad ) // too narrow to split
+      {
+        // update left side limit till where to make triangles
+        quad->side[ QUAD_LEFT_SIDE ].to--;
+      }
+      else
+      {
+        FaceQuadStruct::Ptr leftQuad =
+          ( quad->side[ QUAD_BOTTOM_SIDE ].from == 0 ) ? quad : newQuad;
+        leftQuad->nbNodeOut( QUAD_TOP_SIDE ) = 0;
+      }
+    }
   }
 
-  return computeQuadDominant( aMesh, aFace );
+  if ( ! computeQuadDominant( aMesh, aFace ))
+    return false;
+
+  // try to fix zero-area triangles near straight-angle corners
+
+  return true;
 }
 
 //================================================================================
@@ -586,17 +612,59 @@ bool StdMeshers_Quadrangle_2D::computeQuadDominant(SMESH_Mesh&         aMesh,
       iup = nbhoriz - 1;
 
       int stop = 0;
-      // if left edge is out, we will stop at a second node
-      //if (quad->nbNodeOut(3)) stop++;
-      if ( quad->nbNodeOut( QUAD_RIGHT_SIDE ))
-        quad->UVPt( nbhoriz-1, 0 ).node = uv_e1[ nbright-2 ].node;
-      if ( quad->nbNodeOut( QUAD_LEFT_SIDE ))
-        quad->UVPt( 0, 0 ).node = uv_e3[ nbleft-2 ].node;
+      if ( quad->side[3].grid->Edge(0).IsNull() ) // left side is simulated one
+      {
+        // quad divided at I but not at J, as nbvertic==nbright==2
+        stop++; // we stop at a second node
+      }
+      else
+      {
+        if ( quad->nbNodeOut( QUAD_RIGHT_SIDE ))
+          quad->UVPt( nbhoriz-1, 0 ).node = uv_e1[ nbright-2 ].node;
+        if ( quad->nbNodeOut( QUAD_LEFT_SIDE ))
+          quad->UVPt( 0, 0 ).node = uv_e3[ nbleft-2 ].node;
 
+        if ( nbright > 2 ) // there was a split at J
+          quad->nbNodeOut( QUAD_LEFT_SIDE ) = 0;
+      }
+      const SMDS_MeshNode *a, *b, *c, *d;
+      i = nbup - 1;
+      // avoid creating zero-area triangles near a straight-angle corner
+      {
+        a = uv_e2[i].node;
+        b = uv_e2[i-1].node;
+        c = uv_e1[nbright-2].node;
+        SMESH_TNodeXYZ pa( a ), pb( b ), pc( c );
+        double area = 0.5 * (( pb - pa ) ^ ( pc - pa )).Modulus();
+        if ( Abs( area ) < 1e-20 )
+        {
+          --g;
+          d = quad->UVPt( g, nbvertic-2 ).node;
+          if ( myTrianglePreference )
+          {
+            if ( SMDS_MeshFace* face = myHelper->AddFace(a, d, c))
+              meshDS->SetMeshElementOnShape(face, geomFaceID);
+          }
+          else
+          {
+            if ( SMDS_MeshFace* face = myHelper->AddFace(a, b, d, c))
+            {
+              meshDS->SetMeshElementOnShape(face, geomFaceID);
+              SMESH_ComputeErrorPtr& err = aMesh.GetSubMesh( aFace )->GetComputeError();
+              if ( !err || err->IsOK() || err->myName < COMPERR_WARNING )
+              {
+                err.reset( new SMESH_ComputeError( COMPERR_WARNING,
+                                                   "Bad quality quad created"));
+                err->myBadElements.push_back( face );
+              }
+            }
+            --i;
+          }
+        }
+      }
       // for each node of the up edge find nearest node
       // in the first row of the regular grid and link them
-      for (i = nbup - 1; i > stop; i--) {
-        const SMDS_MeshNode *a, *b, *c, *d;
+      for ( ; i > stop; i--) {
         a = uv_e2[i].node;
         b = uv_e2[i - 1].node;
         gp_Pnt pb (b->X(), b->Y(), b->Z());
@@ -740,8 +808,43 @@ bool StdMeshers_Quadrangle_2D::computeQuadDominant(SMESH_Mesh&         aMesh,
       int g = nbvertic - 1; // last processed node in the grid
       int stop = 0;
       i = quad->side[ QUAD_LEFT_SIDE ].to-1; // nbleft - 1;
-      for (; i > stop; i--) {
-        const SMDS_MeshNode *a, *b, *c, *d;
+
+      const SMDS_MeshNode *a, *b, *c, *d;
+      // avoid creating zero-area triangles near a straight-angle corner
+      {
+        a = uv_e3[i].node;
+        b = uv_e3[i-1].node;
+        c = quad->UVPt( 1, g ).node;
+        SMESH_TNodeXYZ pa( a ), pb( b ), pc( c );
+        double area = 0.5 * (( pb - pa ) ^ ( pc - pa )).Modulus();
+        if ( Abs( area ) < 1e-20 )
+        {
+          --g;
+          d = quad->UVPt( 1, g ).node;
+          if ( myTrianglePreference )
+          {
+            if ( SMDS_MeshFace* face = myHelper->AddFace(a, d, c))
+              meshDS->SetMeshElementOnShape(face, geomFaceID);
+          }
+          else
+          {
+            if ( SMDS_MeshFace* face = myHelper->AddFace(a, b, d, c))
+            {
+              meshDS->SetMeshElementOnShape(face, geomFaceID);
+              SMESH_ComputeErrorPtr& err = aMesh.GetSubMesh( aFace )->GetComputeError();
+              if ( !err || err->IsOK() || err->myName < COMPERR_WARNING )
+              {
+                err.reset( new SMESH_ComputeError( COMPERR_WARNING,
+                                                   "Bad quality quad created"));
+                err->myBadElements.push_back( face );
+              }
+            }
+            --i;
+          }
+        }
+      }
+      for (; i > stop; i--) // loop on nodes on the left side
+      {
         a = uv_e3[i].node;
         b = uv_e3[i - 1].node;
         gp_Pnt pb (b->X(), b->Y(), b->Z());
@@ -751,12 +854,13 @@ bool StdMeshers_Quadrangle_2D::computeQuadDominant(SMESH_Mesh&         aMesh,
         if (i == stop + 1) { // down bondary reached
           c = quad->uv_grid[nbhoriz*jlow + 1].node;
           near = jlow;
-        } else {
+        }
+        else {
           double mind = RealLast();
           for (int k = g; k >= jlow; k--) {
             const SMDS_MeshNode *nk;
             if (k > jup)
-              nk = uv_e2[1].node;
+              nk = quad->uv_grid[nbhoriz*jup + 1].node; //uv_e2[1].node;
             else
               nk = quad->uv_grid[nbhoriz*k + 1].node;
             gp_Pnt pnk (nk->X(), nk->Y(), nk->Z());
@@ -777,11 +881,10 @@ bool StdMeshers_Quadrangle_2D::computeQuadDominant(SMESH_Mesh&         aMesh,
         }
         else { // make quadrangle
           if (near + 1 > jup)
-            d = uv_e2[1].node;
+            d = quad->uv_grid[nbhoriz*jup + 1].node; //uv_e2[1].node;
           else
             d = quad->uv_grid[nbhoriz*(near + 1) + 1].node;
-          //SMDS_MeshFace* face = meshDS->AddFace(a, b, c, d);
-          if (!myTrianglePreference){
+          if (!myTrianglePreference) {
             SMDS_MeshFace* face = myHelper->AddFace(a, b, c, d);
             if (face) meshDS->SetMeshElementOnShape(face, geomFaceID);
           }
@@ -793,7 +896,7 @@ bool StdMeshers_Quadrangle_2D::computeQuadDominant(SMESH_Mesh&         aMesh,
             for (int k = near + 1; k < g; k++) {
               c = quad->uv_grid[nbhoriz*k + 1].node;
               if (k + 1 > jup)
-                d = uv_e2[1].node;
+                d = quad->uv_grid[nbhoriz*jup + 1].node; //uv_e2[1].node;
               else
                 d = quad->uv_grid[nbhoriz*(k + 1) + 1].node;
               SMDS_MeshFace* face = myHelper->AddFace(a, c, d);
@@ -918,21 +1021,17 @@ bool StdMeshers_Quadrangle_2D::IsApplicable( const TopoDS_Shape & aShape, bool t
   int nbFoundFaces = 0;
   for (TopExp_Explorer exp( aShape, TopAbs_FACE ); exp.More(); exp.Next(), ++nbFoundFaces )
   {
-    TopoDS_Face aFace = TopoDS::Face(exp.Current());
-    if ( aFace.Orientation() >= TopAbs_INTERNAL ) aFace.Orientation( TopAbs_FORWARD );
-
-    list< TopoDS_Edge > aWire;
-    list< int > nbEdgesInWire;
-    int nbWire = SMESH_Block::GetOrderedEdges (aFace, aWire, nbEdgesInWire);
+    const TopoDS_Shape& aFace = exp.Current();
+    int nbWire = SMESH_MesherHelper::Count( aFace, TopAbs_WIRE, false );
     if ( nbWire != 1 ) {
       if ( toCheckAll ) return false;
       continue;
     }
 
     int nbNoDegenEdges = 0;
-    list<TopoDS_Edge>::iterator edge = aWire.begin();
-    for ( ; edge != aWire.end(); ++edge ) {
-      if ( !SMESH_Algo::isDegenerated( *edge ))
+    TopExp_Explorer eExp( aFace, TopAbs_EDGE );
+    for ( ; eExp.More() && nbNoDegenEdges < 3; eExp.Next() ) {
+      if ( !SMESH_Algo::isDegenerated( TopoDS::Edge( eExp.Current() )))
         ++nbNoDegenEdges;
     }
     if ( toCheckAll  && nbNoDegenEdges <  3 ) return false;
@@ -979,7 +1078,7 @@ FaceQuadStruct::Ptr StdMeshers_Quadrangle_2D::CheckNbEdges(SMESH_Mesh &
   if ( F.Orientation() >= TopAbs_INTERNAL ) F.Orientation( TopAbs_FORWARD );
   const bool ignoreMediumNodes = _quadraticMesh;
 
-  // verify 1 wire only, with 4 edges
+  // verify 1 wire only
   list< TopoDS_Edge > edges;
   list< int > nbEdgesInWire;
   int nbWire = SMESH_Block::GetOrderedEdges (F, edges, nbEdgesInWire);
@@ -1035,45 +1134,40 @@ FaceQuadStruct::Ptr StdMeshers_Quadrangle_2D::CheckNbEdges(SMESH_Mesh &
     {
       list< TopoDS_Edge > sideEdges;
       TopoDS_Vertex nextSideV = corners[( iSide + 1 - nbUsedDegen ) % corners.size() ];
-      while ( edgeIt != edges.end() &&
-              !nextSideV.IsSame( myHelper->IthVertex( 0, *edgeIt )))
+      bool nextSideVReached = false;
+      do
       {
-        if ( SMESH_Algo::isDegenerated( *edgeIt ) )
+        const TopoDS_Edge& edge = *edgeIt;
+        nextSideVReached = nextSideV.IsSame( myHelper->IthVertex( 1, edge ));
+        if ( SMESH_Algo::isDegenerated( edge ))
         {
-          if ( myNeedSmooth )
-          {
-            ++edgeIt; // no side on the degenerated EDGE
-          }
-          else
+          if ( !myNeedSmooth ) // need to make a side on a degen edge
           {
             if ( sideEdges.empty() )
             {
+              sideEdges.push_back( edge );
               ++nbUsedDegen;
-              sideEdges.push_back( *edgeIt++ ); // a degenerated side
-              break;
+              nextSideVReached = true;
             }
             else
             {
-              break; // do not append a degenerated EDGE to a regular side
+              break;
             }
           }
         }
         else
         {
-          sideEdges.push_back( *edgeIt++ );
+          sideEdges.push_back( edge );
         }
+        ++edgeIt;
       }
+      while ( edgeIt != edges.end() && !nextSideVReached );
+
       if ( !sideEdges.empty() )
       {
-        quad->side.push_back( StdMeshers_FaceSide::New( F, sideEdges, &aMesh, iSide < QUAD_TOP_SIDE,
-                                                        ignoreMediumNodes, myProxyMesh ));
-        ++iSide;
-      }
-      else if ( !SMESH_Algo::isDegenerated( *edgeIt ) && // closed EDGE
-                myHelper->IthVertex( 0, *edgeIt ).IsSame( myHelper->IthVertex( 1, *edgeIt )))
-      {
-        quad->side.push_back( StdMeshers_FaceSide::New( F, *edgeIt++, &aMesh, iSide < QUAD_TOP_SIDE,
-                                                        ignoreMediumNodes, myProxyMesh));
+        quad->side.push_back
+          ( StdMeshers_FaceSide::New( F, sideEdges, &aMesh, iSide < QUAD_TOP_SIDE,
+                                      ignoreMediumNodes, myProxyMesh ));
         ++iSide;
       }
       if ( quad->side.size() == 4 )
@@ -3687,6 +3781,18 @@ namespace // data for smoothing
     double d = v1 ^ v2;
     return d > 1e-100;
   }
+  //================================================================================
+  /*!
+   * \brief Returns area of a triangle
+   */
+  //================================================================================
+
+  double getArea( const gp_UV uv1, const gp_UV uv2, const gp_UV uv3 )
+  {
+    gp_XY v1 = uv1 - uv2, v2 = uv3 - uv2;
+    double a = v2 ^ v1;
+    return a;
+  }
 }
 
 //================================================================================
@@ -3737,7 +3843,7 @@ void StdMeshers_Quadrangle_2D::updateDegenUV(FaceQuadStruct::Ptr quad)
       uv1.v = uv2.v = 0.5 * ( uv1.v + uv2.v );
     }
 
-  else if ( quad->side.size() == 4 && myQuadType == QUAD_STANDARD)
+  else if ( quad->side.size() == 4 /*&& myQuadType == QUAD_STANDARD*/)
 
     // Set number of nodes on a degenerated side to be same as on an opposite side
     // ----------------------------------------------------------------------------
@@ -3929,6 +4035,145 @@ void StdMeshers_Quadrangle_2D::smooth (FaceQuadStruct::Ptr quad)
   }
 }
 
+//================================================================================
+/*!
+ * \brief Checks validity of generated faces
+ */
+//================================================================================
+
+bool StdMeshers_Quadrangle_2D::check()
+{
+  const bool isOK = true;
+  if ( !myCheckOri || myQuadList.empty() || !myQuadList.front() || !myHelper )
+    return isOK;
+
+  TopoDS_Face      geomFace = TopoDS::Face( myHelper->GetSubShape() );
+  SMESHDS_Mesh*    meshDS   = myHelper->GetMeshDS();
+  SMESHDS_SubMesh* fSubMesh = meshDS->MeshElements( geomFace );
+  bool toCheckUV;
+  if ( geomFace.Orientation() >= TopAbs_INTERNAL ) geomFace.Orientation( TopAbs_FORWARD );
+
+  // Get a reference orientation sign
+
+  double okSign;
+  {
+    TError err;
+    TSideVector wireVec =
+      StdMeshers_FaceSide::GetFaceWires( geomFace, *myHelper->GetMesh(), true, err );
+    StdMeshers_FaceSidePtr wire = wireVec[0];
+
+    // find a right angle VERTEX
+    int iVertex;
+    double maxAngle = -1e100;
+    for ( int i = 0; i < wire->NbEdges(); ++i )
+    {
+      int iPrev = myHelper->WrapIndex( i-1, wire->NbEdges() );
+      const TopoDS_Edge& e1 = wire->Edge( iPrev );
+      const TopoDS_Edge& e2 = wire->Edge( i );
+      double angle = myHelper->GetAngle( e1, e2, geomFace, wire->FirstVertex( i ));
+      if (( maxAngle < angle ) &&
+          ( 5.* M_PI/180 < angle && angle < 175.* M_PI/180  ))
+      {
+        maxAngle = angle;
+        iVertex = i;
+      }
+    }
+    if ( maxAngle < -2*M_PI ) return isOK;
+
+    // get a sign of 2D area of a corner face
+
+    int iPrev = myHelper->WrapIndex( iVertex-1, wire->NbEdges() );
+    const TopoDS_Edge& e1 = wire->Edge( iPrev );
+    const TopoDS_Edge& e2 = wire->Edge( iVertex );
+
+    gp_Vec2d v1, v2; gp_Pnt2d p;
+    double u[2];
+    {
+      bool rev = ( e1.Orientation() == TopAbs_REVERSED );
+      Handle(Geom2d_Curve) c = BRep_Tool::CurveOnSurface( e1, geomFace, u[0], u[1] );
+      c->D1( u[ !rev ], p, v1 );
+      if ( !rev )
+        v1.Reverse();
+    }
+    {
+      bool rev = ( e2.Orientation() == TopAbs_REVERSED );
+      Handle(Geom2d_Curve) c = BRep_Tool::CurveOnSurface( e2, geomFace, u[0], u[1] );
+      c->D1( u[ rev ], p, v2 );
+      if ( rev )
+        v2.Reverse();
+    }
+
+    okSign = v2 ^ v1;
+
+    if ( maxAngle < 0 )
+      okSign *= -1;
+  }
+
+  // Look for incorrectly oriented faces
+
+  std::list<const SMDS_MeshElement*> badFaces;
+
+  const SMDS_MeshNode* nn [ 8 ]; // 8 is just for safety
+  gp_UV                uv [ 8 ];
+  SMDS_ElemIteratorPtr fIt = fSubMesh->GetElements();
+  while ( fIt->more() ) // loop on faces bound to a FACE
+  {
+    const SMDS_MeshElement* f = fIt->next();
+
+    const int nbN = f->NbCornerNodes();
+    for ( int i = 0; i < nbN; ++i )
+      nn[ i ] = f->GetNode( i );
+
+    const SMDS_MeshNode* nInFace = 0;
+    if ( myHelper->HasSeam() )
+      for ( int i = 0; i < nbN && !nInFace; ++i )
+        if ( !myHelper->IsSeamShape( nn[i]->getshapeId() ))
+          nInFace = nn[i];
+
+    for ( int i = 0; i < nbN; ++i )
+      uv[ i ] = myHelper->GetNodeUV( geomFace, nn[i], nInFace, &toCheckUV );
+
+    switch ( nbN ) {
+    case 4:
+    {
+      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
+      }
+      if ( sign1 * okSign < 0 )
+        badFaces.push_back ( f );
+      break;
+    }
+    case 3:
+    {
+      double sign = getArea( uv[0], uv[1], uv[2] );
+      if ( sign * okSign < 0 )
+        badFaces.push_back ( f );
+      break;
+    }
+    default:;
+    }
+  }
+
+  if ( !badFaces.empty() )
+  {
+    SMESH_subMesh* fSM = myHelper->GetMesh()->GetSubMesh( geomFace );
+    SMESH_ComputeErrorPtr& err = fSM->GetComputeError();
+    err.reset ( new SMESH_ComputeError( COMPERR_ALGO_FAILED,
+                                        "Inverted elements generated"));
+    err->myBadElements.swap( badFaces );
+
+    return !isOK;
+  }
+
+  return isOK;
+}
+
 /*//================================================================================
 /*!
  * \brief Finds vertices at the most sharp face corners
@@ -3979,7 +4224,7 @@ int StdMeshers_Quadrangle_2D::getCorners(const TopoDS_Face&          theFace,
     TopoDS_Vertex v = helper.IthVertex( 0, *edge );
     if ( !theConsiderMesh || SMESH_Algo::VertexNode( v, helper.GetMeshDS() ))
     {
-      double angle = SMESH_MesherHelper::GetAngle( prevE, *edge, theFace );
+      double angle = SMESH_MesherHelper::GetAngle( prevE, *edge, theFace, v );
       vertexByAngle.insert( make_pair( angle, v ));
       angleByVertex.Bind( v, angle );
     }
@@ -4031,18 +4276,22 @@ int StdMeshers_Quadrangle_2D::getCorners(const TopoDS_Face&          theFace,
   if ( nbCorners == 3 )
     vMap.Add( triaVertex );
   multimap<double, TopoDS_Vertex>::reverse_iterator a2v = vertexByAngle.rbegin();
-  for ( ; a2v != vertexByAngle.rend() && vMap.Extent() < nbCorners; ++a2v )
+  for ( int iC = 0; a2v != vertexByAngle.rend() && iC < nbCorners; ++a2v, ++iC )
     vMap.Add( (*a2v).second );
 
   // check if there are possible variations in choosing corners
-  bool isThereVariants = false;
+  bool haveVariants = false;
   if ( vertexByAngle.size() > nbCorners )
   {
     double lostAngle = a2v->first;
     double lastAngle = ( --a2v, a2v->first );
-    isThereVariants  = ( lostAngle * 1.1 >= lastAngle );
+    haveVariants  = ( lostAngle * 1.1 >= lastAngle );
   }
 
+  const double angleTol = 5.* M_PI/180;
+  myCheckOri = ( vertexByAngle.size() > nbCorners ||
+                 vertexByAngle.begin()->first < angleTol );
+
   // make theWire begin from a corner vertex or triaVertex
   if ( nbCorners == 3 )
     while ( !triaVertex.IsSame( ( helper.IthVertex( 0, theWire.front() ))) ||
@@ -4058,9 +4307,10 @@ int StdMeshers_Quadrangle_2D::getCorners(const TopoDS_Face&          theFace,
   vector< double >      angles;
   vector< TopoDS_Edge > edgeVec;
   vector< int >         cornerInd, nbSeg;
-  angles.reserve( vertexByAngle.size() );
+  int nbSegTot = 0;
+  angles .reserve( vertexByAngle.size() );
   edgeVec.reserve( vertexByAngle.size() );
-  nbSeg.reserve( vertexByAngle.size() );
+  nbSeg  .reserve( vertexByAngle.size() );
   cornerInd.reserve( nbCorners );
   for ( edge = theWire.begin(); edge != theWire.end(); ++edge )
   {
@@ -4073,105 +4323,219 @@ int StdMeshers_Quadrangle_2D::getCorners(const TopoDS_Face&          theFace,
       theVertices.push_back( v );
       cornerInd.push_back( angles.size() );
     }
-    angles.push_back( angleByVertex.IsBound( v ) ? angleByVertex( v ) : -M_PI );
+    angles .push_back( angleByVertex.IsBound( v ) ? angleByVertex( v ) : -M_PI );
     edgeVec.push_back( *edge );
-    if ( theConsiderMesh && isThereVariants )
+    if ( theConsiderMesh && haveVariants )
     {
       if ( SMESHDS_SubMesh* sm = helper.GetMeshDS()->MeshElements( *edge ))
         nbSeg.push_back( sm->NbNodes() + 1 );
       else
         nbSeg.push_back( 0 );
+      nbSegTot += nbSeg.back();
     }
   }
 
-  // refine the result vector - make sides elual by length if
+  // refine the result vector - make sides equal by length if
   // there are several equal angles
-  if ( isThereVariants )
+  if ( haveVariants )
   {
     if ( nbCorners == 3 )
       angles[0] = 2 * M_PI; // not to move the base triangle VERTEX
 
-    set< int > refinedCorners;
+    // here we refer to VERTEX'es and EDGEs by indices in angles and edgeVec vectors
+    typedef int TGeoIndex;
+
+    // for each vertex find a vertex till which there are nbSegHalf segments
+    const int nbSegHalf = ( nbSegTot % 2 || nbCorners == 3 ) ? 0 : nbSegTot / 2;
+    vector< TGeoIndex > halfDivider( angles.size(), -1 );
+    int nbHalfDividers = 0;
+    if ( nbSegHalf )
+    {
+      // get min angle of corners
+      double minAngle = 10.;
+      for ( size_t iC = 0; iC < cornerInd.size(); ++iC )
+        minAngle = Min( minAngle, angles[ cornerInd[ iC ]]);
+
+      // find halfDivider's
+      for ( TGeoIndex iV1 = 0; iV1 < TGeoIndex( angles.size() ); ++iV1 )
+      {
+        int nbSegs = 0;
+        TGeoIndex iV2 = iV1;
+        do {
+          nbSegs += nbSeg[ iV2 ];
+          iV2 = helper.WrapIndex( iV2 + 1, nbSeg.size() );
+        } while ( nbSegs < nbSegHalf );
+
+        if ( nbSegs == nbSegHalf &&
+             angles[ iV1 ] + angleTol >= minAngle &&
+             angles[ iV2 ] + angleTol >= minAngle )
+        {
+          halfDivider[ iV1 ] = iV2;
+          ++nbHalfDividers;
+        }
+      }
+    }
+
+    set< TGeoIndex > refinedCorners, treatedCorners;
     for ( size_t iC = 0; iC < cornerInd.size(); ++iC )
     {
-      int iV = cornerInd[iC];
-      if ( !refinedCorners.insert( iV ).second )
+      TGeoIndex iV = cornerInd[iC];
+      if ( !treatedCorners.insert( iV ).second )
         continue;
-      list< int > equalVertices;
-      equalVertices.push_back( iV );
+      list< TGeoIndex > equVerts; // inds of vertices that can become corners
+      equVerts.push_back( iV );
       int nbC[2] = { 0, 0 };
       // find equal angles backward and forward from the iV-th corner vertex
       for ( int isFwd = 0; isFwd < 2; ++isFwd )
       {
-        int     dV = isFwd ? +1 : -1;
-        int iCNext = helper.WrapIndex( iC + dV, cornerInd.size() );
-        int iVNext = helper.WrapIndex( iV + dV, angles.size() );
+        int           dV = isFwd ? +1 : -1;
+        int       iCNext = helper.WrapIndex( iC + dV, cornerInd.size() );
+        TGeoIndex iVNext = helper.WrapIndex( iV + dV, angles.size() );
         while ( iVNext != iV )
         {
-          bool equal = Abs( angles[iV] - angles[iVNext] ) < 0.1 * angles[iV];
+          bool equal = Abs( angles[iV] - angles[iVNext] ) < angleTol;
           if ( equal )
-            equalVertices.insert( isFwd ? equalVertices.end() : equalVertices.begin(), iVNext );
+            equVerts.insert( isFwd ? equVerts.end() : equVerts.begin(), iVNext );
           if ( iVNext == cornerInd[ iCNext ])
           {
             if ( !equal )
+            {
+              if ( angles[iV] < angles[iVNext] )
+                refinedCorners.insert( iVNext );
               break;
+            }
             nbC[ isFwd ]++;
-            refinedCorners.insert( cornerInd[ iCNext ] );
+            treatedCorners.insert( cornerInd[ iCNext ] );
             iCNext = helper.WrapIndex( iCNext + dV, cornerInd.size() );
           }
           iVNext = helper.WrapIndex( iVNext + dV, angles.size() );
         }
+        if ( iVNext == iV )
+          break; // all angles equal
+      }
+
+      const bool allCornersSame = ( nbC[0] == 3 );
+      if ( allCornersSame && nbHalfDividers > 0 )
+      {
+        // select two halfDivider's as corners
+        TGeoIndex hd1, hd2 = -1;
+        int iC2;
+        for ( iC2 = 0; iC2 < cornerInd.size() && hd2 < 0; ++iC2 )
+        {
+          hd1 = cornerInd[ iC2 ];
+          hd2 = halfDivider[ hd1 ];
+          if ( std::find( equVerts.begin(), equVerts.end(), hd2 ) == equVerts.end() )
+            hd2 = -1; // hd2-th vertex can't become a corner
+          else
+            break;
+        }
+        if ( hd2 >= 0 )
+        {
+          angles[ hd1 ] = 2 * M_PI; // make hd1-th vertex no more "equal"
+          angles[ hd2 ] = 2 * M_PI;
+          refinedCorners.insert( hd1 );
+          refinedCorners.insert( hd2 );
+          treatedCorners = refinedCorners;
+          // update cornerInd
+          equVerts.push_front( equVerts.back() );
+          equVerts.push_back( equVerts.front() );
+          list< TGeoIndex >::iterator hdPos =
+            std::find( equVerts.begin(), equVerts.end(), hd2 );
+          if ( hdPos == equVerts.end() ) break;
+          cornerInd[ helper.WrapIndex( iC2 + 0, cornerInd.size()) ] = hd1;
+          cornerInd[ helper.WrapIndex( iC2 + 1, cornerInd.size()) ] = *( --hdPos );
+          cornerInd[ helper.WrapIndex( iC2 + 2, cornerInd.size()) ] = hd2;
+          cornerInd[ helper.WrapIndex( iC2 + 3, cornerInd.size()) ] = *( ++hdPos, ++hdPos );
+
+          theVertices[ 0 ] = helper.IthVertex( 0, edgeVec[ cornerInd[0] ]);
+          theVertices[ 1 ] = helper.IthVertex( 0, edgeVec[ cornerInd[1] ]);
+          theVertices[ 2 ] = helper.IthVertex( 0, edgeVec[ cornerInd[2] ]);
+          theVertices[ 3 ] = helper.IthVertex( 0, edgeVec[ cornerInd[3] ]);
+          iC = -1;
+          continue;
+        }
       }
+
       // move corners to make sides equal by length
-      int nbEqualV  = equalVertices.size();
+      int nbEqualV  = equVerts.size();
       int nbExcessV = nbEqualV - ( 1 + nbC[0] + nbC[1] );
-      if ( nbExcessV > 0 )
+      if ( nbExcessV > 0 ) // there is nbExcessV vertices that can become corners
       {
-        // calculate normalized length of each side enclosed between neighbor equalVertices
-        vector< double > curLengths;
+        // calculate normalized length of each "side" enclosed between neighbor equVerts
+        vector< double > accuLength;
         double totalLen = 0;
-        vector< int > evVec( equalVertices.begin(), equalVertices.end() );
-        int   iEV = 0;
-        int    iE = cornerInd[ helper.WrapIndex( iC - nbC[0] - 1, cornerInd.size() )];
-        int iEEnd = cornerInd[ helper.WrapIndex( iC + nbC[1] + 1, cornerInd.size() )];
-        while ( curLengths.size() < nbEqualV + 1 )
+        vector< TGeoIndex > evVec( equVerts.begin(), equVerts.end() );
+        int          iEV = 0;
+        TGeoIndex    iE = cornerInd[ helper.WrapIndex( iC - nbC[0] - 1, cornerInd.size() )];
+        TGeoIndex iEEnd = cornerInd[ helper.WrapIndex( iC + nbC[1] + 1, cornerInd.size() )];
+        while ( accuLength.size() < nbEqualV + int( !allCornersSame ) )
         {
-          curLengths.push_back( totalLen );
+          // accumulate length of edges before iEV-th equal vertex
+          accuLength.push_back( totalLen );
           do {
-            curLengths.back() += SMESH_Algo::EdgeLength( edgeVec[ iE ]);
+            accuLength.back() += SMESH_Algo::EdgeLength( edgeVec[ iE ]);
             iE = helper.WrapIndex( iE + 1, edgeVec.size());
-            if ( iEV < evVec.size() && iE == evVec[ iEV++ ] )
-              break;
+            if ( iEV < evVec.size() && iE == evVec[ iEV ] ) {
+              iEV++;
+              break; // equal vertex reached
+            }
           }
           while( iE != iEEnd );
-          totalLen = curLengths.back();
+          totalLen = accuLength.back();
         }
-        curLengths.resize( equalVertices.size() );
-        for ( size_t iS = 0; iS < curLengths.size(); ++iS )
-          curLengths[ iS ] /= totalLen;
+        accuLength.resize( equVerts.size() );
+        for ( size_t iS = 0; iS < accuLength.size(); ++iS )
+          accuLength[ iS ] /= totalLen;
 
-        // find equalVertices most close to the ideal sub-division of all sides
+        // find equVerts most close to the ideal sub-division of all sides
         int iBestEV = 0;
         int iCorner = helper.WrapIndex( iC - nbC[0], cornerInd.size() );
-        int nbSides = 2 + nbC[0] + nbC[1];
+        int nbSides = Min( nbCorners, 2 + nbC[0] + nbC[1] );
         for ( int iS = 1; iS < nbSides; ++iS, ++iBestEV )
         {
           double idealLen = iS / double( nbSides );
-          double d, bestDist = 1.;
-          for ( iEV = iBestEV; iEV < curLengths.size(); ++iEV )
-            if (( d = Abs( idealLen - curLengths[ iEV ])) < bestDist )
+          double d, bestDist = 2.;
+          for ( iEV = iBestEV; iEV < accuLength.size(); ++iEV )
+          {
+            d = Abs( idealLen - accuLength[ iEV ]);
+
+            // take into account presence of a coresponding halfDivider
+            const double cornerWgt = 0.5  / nbSides;
+            const double vertexWgt = 0.25 / nbSides;
+            TGeoIndex hd = halfDivider[ evVec[ iEV ]];
+            if ( hd < 0 )
+              d += vertexWgt;
+            else if( refinedCorners.count( hd ))
+              d -= cornerWgt;
+            else
+              d -= vertexWgt;
+
+            // choose vertex with the best d
+            if ( d < bestDist )
             {
               bestDist = d;
               iBestEV  = iEV;
             }
+          }
           if ( iBestEV > iS-1 + nbExcessV )
             iBestEV = iS-1 + nbExcessV;
           theVertices[ iCorner ] = helper.IthVertex( 0, edgeVec[ evVec[ iBestEV ]]);
+          refinedCorners.insert( evVec[ iBestEV ]);
           iCorner = helper.WrapIndex( iCorner + 1, cornerInd.size() );
         }
+
+      } // if ( nbExcessV > 0 )
+      else
+      {
+        refinedCorners.insert( cornerInd[ iC ]);
       }
-    }
-  }
+    } // loop on cornerInd
+
+    // make theWire begin from the cornerInd[0]-th EDGE
+    while ( !theWire.front().IsSame( edgeVec[ cornerInd[0] ]))
+      theWire.splice( theWire.begin(), theWire, --theWire.end() );
+
+  } // if ( haveVariants )
 
   return nbCorners;
 }
@@ -4553,7 +4917,7 @@ int StdMeshers_Quadrangle_2D::splitQuad(FaceQuadStruct::Ptr quad, int I, int J)
   myQuadList.push_back( FaceQuadStruct::Ptr( newQuad ));
 
   vector<UVPtStruct> points;
-  if ( I > 0 )
+  if ( I > 0 && I <= quad->iSize-2 )
   {
     points.reserve( quad->jSize );
     for ( int jP = 0; jP < quad->jSize; ++jP )
@@ -4592,7 +4956,7 @@ int StdMeshers_Quadrangle_2D::splitQuad(FaceQuadStruct::Ptr quad, int I, int J)
 
     return QUAD_LEFT_SIDE;
   }
-  else if ( J > 0 ) //// split horizontally, a new quad is below an old one
+  else if ( J > 0  && J <= quad->jSize-2 ) //// split horizontally, a new quad is below an old one
   {
     points.reserve( quad->iSize );
     for ( int iP = 0; iP < quad->iSize; ++iP )
@@ -5090,6 +5454,7 @@ FaceQuadStruct::Side& FaceQuadStruct::Side::operator=(const Side& otherSide)
         oSide->contacts[iOC].other_side = this;
       }
   }
+  return *this;
 }
 
 //================================================================================
@@ -5136,6 +5501,7 @@ bool FaceQuadStruct::Side::Reverse(bool keepGrid)
       grid->Reverse();
     }
   }
+  return (bool)grid;
 }
 
 //================================================================================