Salome HOME
22542: [CEA 1129]Convert to biquadradic creates a node ouside an element if mesh...
[modules/smesh.git] / src / SMESH / SMESH_MesherHelper.cxx
index 258c8ff8a00189fafc2cc38b01dc9659849fc630..a53e644d88f612d30667acc92b14654c36b91f69 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2007-2012  CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2014  CEA/DEN, EDF R&D, OPEN CASCADE
 //
 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
@@ -6,7 +6,7 @@
 // This library is free software; you can redistribute it and/or
 // modify it under the terms of the GNU Lesser General Public
 // License as published by the Free Software Foundation; either
-// version 2.1 of the License.
+// version 2.1 of the License, or (at your option) any later version.
 //
 // This library is distributed in the hope that it will be useful,
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -32,6 +32,7 @@
 #include "SMDS_IteratorOnIterators.hxx"
 #include "SMDS_VolumeTool.hxx"
 #include "SMESH_Block.hxx"
+#include "SMESH_MeshAlgos.hxx"
 #include "SMESH_ProxyMesh.hxx"
 #include "SMESH_subMesh.hxx"
 
@@ -165,14 +166,14 @@ bool SMESH_MesherHelper::IsQuadraticSubMesh(const TopoDS_Shape& aSh)
             }
             else {
               // fill TLinkNodeMap
-              switch ( e->NbNodes() ) {
-              case 3:
+              switch ( e->NbCornerNodes() ) {
+              case 2:
                 AddTLinkNode(e->GetNode(0),e->GetNode(1),e->GetNode(2)); break;
-              case 6:
+              case 3:
                 AddTLinkNode(e->GetNode(0),e->GetNode(1),e->GetNode(3));
                 AddTLinkNode(e->GetNode(1),e->GetNode(2),e->GetNode(4));
                 AddTLinkNode(e->GetNode(2),e->GetNode(0),e->GetNode(5)); break;
-              case 8:
+              case 4:
                 AddTLinkNode(e->GetNode(0),e->GetNode(1),e->GetNode(4));
                 AddTLinkNode(e->GetNode(1),e->GetNode(2),e->GetNode(5));
                 AddTLinkNode(e->GetNode(2),e->GetNode(3),e->GetNode(6));
@@ -244,8 +245,8 @@ void SMESH_MesherHelper::SetSubShape(const TopoDS_Shape& aSh)
     TopLoc_Location loc;
     Handle(Geom_Surface) surface = BRep_Tool::Surface( face, loc );
 
-    if ( surface->IsUPeriodic() || surface->IsVPeriodic() ||
-         surface->IsUClosed()   || surface->IsVClosed() )
+    // if ( surface->IsUPeriodic() || surface->IsVPeriodic() ||
+    //      surface->IsUClosed()   || surface->IsVClosed() )
     {
       //while ( surface->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface )))
       //surface = Handle(Geom_RectangularTrimmedSurface)::DownCast( surface )->BasisSurface();
@@ -280,7 +281,7 @@ void SMESH_MesherHelper::SetSubShape(const TopoDS_Shape& aSh)
         }
 
         // look for a degenerated edge
-        if ( BRep_Tool::Degenerated( edge )) {
+        if ( SMESH_Algo::isDegenerated( edge )) {
           myDegenShapeIds.insert( meshDS->ShapeToIndex( edge ));
           for ( TopExp_Explorer v( edge, TopAbs_VERTEX ); v.More(); v.Next() )
             myDegenShapeIds.insert( meshDS->ShapeToIndex( v.Current() ));
@@ -388,15 +389,26 @@ void SMESH_MesherHelper::AddTLinks(const SMDS_MeshFace* f)
 {
   if ( !f->IsPoly() )
     switch ( f->NbNodes() ) {
+    case 7:
+      // myMapWithCentralNode.insert
+      //   ( make_pair( TBiQuad( f->GetNode(0),f->GetNode(1),f->GetNode(2) ),
+      //                f->GetNode(6)));
+      // break; -- add medium nodes as well
     case 6:
       AddTLinkNode(f->GetNode(0),f->GetNode(1),f->GetNode(3));
       AddTLinkNode(f->GetNode(1),f->GetNode(2),f->GetNode(4));
       AddTLinkNode(f->GetNode(2),f->GetNode(0),f->GetNode(5)); break;
+
+    case 9:
+      // myMapWithCentralNode.insert
+      //   ( make_pair( TBiQuad( f->GetNode(0),f->GetNode(1),f->GetNode(2),f->GetNode(3) ),
+      //                f->GetNode(8)));
+      // break; -- add medium nodes as well
     case 8:
       AddTLinkNode(f->GetNode(0),f->GetNode(1),f->GetNode(4));
       AddTLinkNode(f->GetNode(1),f->GetNode(2),f->GetNode(5));
       AddTLinkNode(f->GetNode(2),f->GetNode(3),f->GetNode(6));
-      AddTLinkNode(f->GetNode(3),f->GetNode(0),f->GetNode(7));
+      AddTLinkNode(f->GetNode(3),f->GetNode(0),f->GetNode(7)); break;
     default:;
     }
 }
@@ -431,6 +443,15 @@ void SMESH_MesherHelper::AddTLinks(const SMDS_MeshVolume* volume)
         else
           addedLinks.erase( it_isNew.first ); // each link encounters only twice
       }
+      if ( vTool.NbNodes() == 27 )
+      {
+        const SMDS_MeshNode* nFCenter = nodes[ vTool.GetCenterNodeIndex( iF )];
+        if ( nFCenter->GetPosition()->GetTypeOfPosition() == SMDS_TOP_3DSPACE )
+          myMapWithCentralNode.insert
+            ( make_pair( TBiQuad( nodes[ iNodes[0]], nodes[ iNodes[1]],
+                                  nodes[ iNodes[2]], nodes[ iNodes[3]] ),
+                         nFCenter ));
+      }
     }
   }
 }
@@ -457,7 +478,10 @@ bool SMESH_MesherHelper::toCheckPosOnShape(int shapeID ) const
 
 void SMESH_MesherHelper::setPosOnShapeValidity(int shapeID, bool ok ) const
 {
-  ((SMESH_MesherHelper*)this)->myNodePosShapesValidity.insert( make_pair( shapeID, ok));
+  std::map< int,bool >::iterator sh_ok = 
+    ((SMESH_MesherHelper*)this)->myNodePosShapesValidity.insert( make_pair( shapeID, ok)).first;
+  if ( !ok )
+    sh_ok->second = ok;
 }
 
 //=======================================================================
@@ -579,10 +603,8 @@ gp_XY SMESH_MesherHelper::GetNodeUV(const TopoDS_Face&   F,
         for ( TopExp_Explorer vert(F,TopAbs_VERTEX); !uvOK && vert.More(); vert.Next() )
           uvOK = ( V == vert.Current() );
         if ( !uvOK ) {
-#ifdef _DEBUG_
           MESSAGE ( "SMESH_MesherHelper::GetNodeUV(); Vertex " << vertexID
-               << " not in face " << GetMeshDS()->ShapeToIndex( F ) );
-#endif
+                    << " not in face " << GetMeshDS()->ShapeToIndex( F ) );
           // get UV of a vertex closest to the node
           double dist = 1e100;
           gp_Pnt pn = XYZ( n );
@@ -642,9 +664,10 @@ bool SMESH_MesherHelper::CheckNodeUV(const TopoDS_Face&   F,
                                      const bool           force,
                                      double               distXYZ[4]) const
 {
-  int shapeID = n->getshapeId();
+  int  shapeID = n->getshapeId();
   bool infinit = ( Precision::IsInfinite( uv.X() ) || Precision::IsInfinite( uv.Y() ));
-  if ( force || toCheckPosOnShape( shapeID ) || infinit )
+  bool zero    = ( uv.X() == 0. && uv.Y() == 0. );
+  if ( force || toCheckPosOnShape( shapeID ) || infinit || zero )
   {
     // check that uv is correct
     TopLoc_Location loc;
@@ -788,6 +811,34 @@ gp_XY SMESH_MesherHelper::GetMiddleUV(const Handle(Geom_Surface)& surface,
   return applyIn2D( surf, p1, p2, & AverageUV );
 }
 
+//=======================================================================
+//function : GetCenterUV
+//purpose  : Return UV for the central node of a biquadratic triangle
+//=======================================================================
+
+gp_XY SMESH_MesherHelper::GetCenterUV(const gp_XY& uv1,
+                                      const gp_XY& uv2, 
+                                      const gp_XY& uv3, 
+                                      const gp_XY& uv12,
+                                      const gp_XY& uv23,
+                                      const gp_XY& uv31,
+                                      bool *       isBadTria/*=0*/)
+{
+  bool badTria;
+  gp_XY uvAvg = ( uv12 + uv23 + uv31 ) / 3.;
+
+  if      (( badTria = (( uvAvg - uv1 ) * ( uvAvg - uv23 ) > 0 )))
+    uvAvg = ( uv1 + uv23 ) / 2.;
+  else if (( badTria = (( uvAvg - uv2 ) * ( uvAvg - uv31 ) > 0 )))
+    uvAvg = ( uv2 + uv31 ) / 2.;
+  else if (( badTria = (( uvAvg - uv3 ) * ( uvAvg - uv12 ) > 0 )))
+    uvAvg = ( uv3 + uv12 ) / 2.;
+
+  if ( isBadTria )
+    *isBadTria = badTria;
+  return uvAvg;
+}
+
 //=======================================================================
 //function : GetNodeU
 //purpose  : Return node U on edge
@@ -796,7 +847,7 @@ gp_XY SMESH_MesherHelper::GetMiddleUV(const Handle(Geom_Surface)& surface,
 double SMESH_MesherHelper::GetNodeU(const TopoDS_Edge&   E,
                                     const SMDS_MeshNode* n,
                                     const SMDS_MeshNode* inEdgeNode,
-                                    bool*                check)
+                                    bool*                check) const
 {
   double param = Precision::Infinite();
 
@@ -849,8 +900,10 @@ bool SMESH_MesherHelper::CheckNodeU(const TopoDS_Edge&   E,
                                     const bool           force,
                                     double               distXYZ[4]) const
 {
-  int shapeID = n->getshapeId();
-  if ( force || toCheckPosOnShape( shapeID ))
+  int  shapeID = n->getshapeId();
+  bool infinit = Precision::IsInfinite( u );
+  bool zero    = ( u == 0. );
+  if ( force || toCheckPosOnShape( shapeID ) || infinit || zero )
   {
     TopLoc_Location loc; double f,l;
     Handle(Geom_Curve) curve = BRep_Tool::Curve( E,loc,f,l );
@@ -866,12 +919,17 @@ bool SMESH_MesherHelper::CheckNodeU(const TopoDS_Edge&   E,
     {
       gp_Pnt nodePnt = SMESH_TNodeXYZ( n );
       if ( !loc.IsIdentity() ) nodePnt.Transform( loc.Transformation().Inverted() );
-      gp_Pnt curvPnt = curve->Value( u );
-      double dist = nodePnt.Distance( curvPnt );
-      if ( distXYZ ) {
-        curvPnt.Transform( loc );
-        distXYZ[0] = dist;
-        distXYZ[1] = curvPnt.X(); distXYZ[2] = curvPnt.Y(); distXYZ[3]=curvPnt.Z();
+      gp_Pnt curvPnt;
+      double dist = u;
+      if ( !infinit )
+      {
+        curvPnt = curve->Value( u );
+        dist    = nodePnt.Distance( curvPnt );
+        if ( distXYZ ) {
+          curvPnt.Transform( loc );
+          distXYZ[0] = dist;
+          distXYZ[1] = curvPnt.X(); distXYZ[2] = curvPnt.Y(); distXYZ[3]=curvPnt.Z();
+        }
       }
       if ( dist > tol )
       {
@@ -895,6 +953,7 @@ bool SMESH_MesherHelper::CheckNodeU(const TopoDS_Edge&   E,
         }
         Quantity_Parameter U = projector->LowerDistanceParameter();
         u = double( U );
+        MESSAGE(" f " << f << " l " << l << " u " << u);
         curvPnt = curve->Value( u );
         dist = nodePnt.Distance( curvPnt );
         if ( distXYZ ) {
@@ -919,6 +978,7 @@ bool SMESH_MesherHelper::CheckNodeU(const TopoDS_Edge&   E,
       }
       if (( u < f-tol || u > l+tol ) && force )
       {
+        MESSAGE("u < f-tol || u > l+tol  ; u " << u << " f " << f << " l " << l);
         // node is on vertex but is set on periodic but trimmed edge (issue 0020890)
         try
         {
@@ -1027,7 +1087,7 @@ SMESH_MesherHelper::GetMediumPos(const SMDS_MeshNode* n1,
 //purpose  : Return existing or create a new central node for a quardilateral
 //           quadratic face given its 8 nodes.
 //@param   : force3d - true means node creation in between the given nodes,
-//             else node position is found on a geometrical face if any.
+//           else node position is found on a geometrical face if any.
 //=======================================================================
 
 const SMDS_MeshNode* SMESH_MesherHelper::GetCentralNode(const SMDS_MeshNode* n1,
@@ -1045,7 +1105,7 @@ const SMDS_MeshNode* SMESH_MesherHelper::GetCentralNode(const SMDS_MeshNode* n1,
   // Find an existing central node
 
   TBiQuad keyOfMap(n1,n2,n3,n4);
-  std::map<TBiQuad, SMDS_MeshNode* >::iterator itMapCentralNode;
+  std::map<TBiQuad, const SMDS_MeshNode* >::iterator itMapCentralNode;
   itMapCentralNode = myMapWithCentralNode.find( keyOfMap );
   if ( itMapCentralNode != myMapWithCentralNode.end() ) 
   {
@@ -1055,7 +1115,7 @@ const SMDS_MeshNode* SMESH_MesherHelper::GetCentralNode(const SMDS_MeshNode* n1,
   // Get type of shape for the new central node
 
   TopAbs_ShapeEnum shapeType = TopAbs_SHAPE;
-  int              shapeID = -1;
+  int              solidID = -1;
   int              faceID = -1;
   TopoDS_Shape     shape;
   TopTools_ListIteratorOfListOfShape it;
@@ -1065,7 +1125,7 @@ const SMDS_MeshNode* SMESH_MesherHelper::GetCentralNode(const SMDS_MeshNode* n1,
   
   SMESHDS_Mesh* meshDS = GetMeshDS();
   
-  // check if a face lie on a FACE, i.e. its all corner nodes lie either on the FACE or
+  // check if a face lies on a FACE, i.e. its all corner nodes lie either on the FACE or
   // on sub-shapes of the FACE
   if ( GetMesh()->HasShapeToMesh() )
   {
@@ -1076,7 +1136,7 @@ const SMDS_MeshNode* SMESH_MesherHelper::GetCentralNode(const SMDS_MeshNode* n1,
       if ( shape.IsNull() ) break;
       if ( shape.ShapeType() == TopAbs_SOLID )
       {
-        shapeID   = nodes[i]->getshapeId();
+        solidID   = nodes[i]->getshapeId();
         shapeType = TopAbs_SOLID;
         break;
       }
@@ -1098,7 +1158,7 @@ const SMDS_MeshNode* SMESH_MesherHelper::GetCentralNode(const SMDS_MeshNode* n1,
       }
     }
   }
-  if ( shapeID < 1 && !faceId2nbNodes.empty() ) // SOLID not found
+  if ( solidID < 1 && !faceId2nbNodes.empty() ) // SOLID not found
   {
     // find ID of the FACE the four corner nodes belong to
     itMapWithIdFace = faceId2nbNodes.begin();
@@ -1123,48 +1183,191 @@ const SMDS_MeshNode* SMESH_MesherHelper::GetCentralNode(const SMDS_MeshNode* n1,
 
   gp_XY  uvAvg;
   gp_Pnt P;
-  if ( !F.IsNull() )
+  bool toCheck = true;
+  if ( !F.IsNull() && !force3d )
+  {
+    uvAvg = calcTFI (0.5, 0.5,
+                     GetNodeUV(F,n1,n3,&toCheck), GetNodeUV(F,n2,n4,&toCheck),
+                     GetNodeUV(F,n3,n1,&toCheck), GetNodeUV(F,n4,n2,&toCheck), 
+                     GetNodeUV(F,n12,n3), GetNodeUV(F,n23,n4),
+                     GetNodeUV(F,n34,n2), GetNodeUV(F,n41,n2));
+    TopLoc_Location loc;
+    Handle( Geom_Surface ) S = BRep_Tool::Surface( F, loc );
+    P = S->Value( uvAvg.X(), uvAvg.Y() ).Transformed( loc );
+    centralNode = meshDS->AddNode( P.X(), P.Y(), P.Z() );
+    // if ( mySetElemOnShape ) node is not elem!
+    meshDS->SetNodeOnFace( centralNode, faceID, uvAvg.X(), uvAvg.Y() );
+  }
+  else // ( force3d || F.IsNull() )
   {
-    if ( !force3d )
+    P = calcTFI (0.5, 0.5,
+                 SMESH_TNodeXYZ(n1),  SMESH_TNodeXYZ(n2),
+                 SMESH_TNodeXYZ(n3),  SMESH_TNodeXYZ(n4), 
+                 SMESH_TNodeXYZ(n12), SMESH_TNodeXYZ(n23),
+                 SMESH_TNodeXYZ(n34), SMESH_TNodeXYZ(n41));
+    centralNode = meshDS->AddNode( P.X(), P.Y(), P.Z() );
+
+    if ( !F.IsNull() ) // force3d
     {
-      uvAvg = calcTFI (0.5, 0.5,
-                       GetNodeUV(F,n1,n3),  GetNodeUV(F,n2,n4),
-                       GetNodeUV(F,n3,n1),  GetNodeUV(F,n4,n2), 
-                       GetNodeUV(F,n12,n3), GetNodeUV(F,n23,n4),
-                       GetNodeUV(F,n34,n2), GetNodeUV(F,n41,n2));
-      TopLoc_Location loc;
-      Handle( Geom_Surface ) S = BRep_Tool::Surface( F, loc );
-      P = S->Value( uvAvg.X(), uvAvg.Y() ).Transformed( loc );
-      centralNode = meshDS->AddNode( P.X(), P.Y(), P.Z() );
-      // if ( mySetElemOnShape ) node is not elem!
+      uvAvg = (GetNodeUV(F,n1,n3,&toCheck) +
+               GetNodeUV(F,n2,n4,&toCheck) +
+               GetNodeUV(F,n3,n1,&toCheck) +
+               GetNodeUV(F,n4,n2,&toCheck)) / 4;
+      //CheckNodeUV( F, centralNode, uvAvg, 2*BRep_Tool::Tolerance( F ), /*force=*/true);
       meshDS->SetNodeOnFace( centralNode, faceID, uvAvg.X(), uvAvg.Y() );
-      myMapWithCentralNode.insert( std::make_pair( keyOfMap, centralNode ) );
-      return centralNode;
+    }
+    else if ( solidID > 0 )
+    {
+      meshDS->SetNodeInVolume( centralNode, solidID );
+    }
+    else if ( myShapeID > 0 && mySetElemOnShape )
+    {
+      meshDS->SetMeshElementOnShape( centralNode, myShapeID );
     }
   }
+  myMapWithCentralNode.insert( std::make_pair( keyOfMap, centralNode ) );
+  return centralNode;
+}
+
+//=======================================================================
+//function : GetCentralNode
+//purpose  : Return existing or create a new central node for a
+//           quadratic triangle given its 6 nodes.
+//@param   : force3d - true means node creation in between the given nodes,
+//           else node position is found on a geometrical face if any.
+//=======================================================================
 
-  P = ( SMESH_TNodeXYZ( n1 ) +
-        SMESH_TNodeXYZ( n2 ) +
-        SMESH_TNodeXYZ( n3 ) +
-        SMESH_TNodeXYZ( n4 ) ) / 4;
-  centralNode = meshDS->AddNode( P.X(), P.Y(), P.Z() );
+const SMDS_MeshNode* SMESH_MesherHelper::GetCentralNode(const SMDS_MeshNode* n1,
+                                                        const SMDS_MeshNode* n2,
+                                                        const SMDS_MeshNode* n3,
+                                                        const SMDS_MeshNode* n12,
+                                                        const SMDS_MeshNode* n23,
+                                                        const SMDS_MeshNode* n31,
+                                                        bool                 force3d)
+{
+  SMDS_MeshNode *centralNode = 0; // central node to return
+
+  // Find an existing central node
 
-  if ( !F.IsNull() )
+  TBiQuad keyOfMap(n1,n2,n3);
+  std::map<TBiQuad, const SMDS_MeshNode* >::iterator itMapCentralNode;
+  itMapCentralNode = myMapWithCentralNode.find( keyOfMap );
+  if ( itMapCentralNode != myMapWithCentralNode.end() ) 
   {
-    uvAvg = (GetNodeUV(F,n1,n3) +
-             GetNodeUV(F,n2,n4) +
-             GetNodeUV(F,n3,n1) +
-             GetNodeUV(F,n4,n2)) / 4;
-    CheckNodeUV( F, centralNode, uvAvg, 2*BRep_Tool::Tolerance( F ), /*force=*/true);
-    meshDS->SetNodeOnFace( centralNode, faceID, uvAvg.X(), uvAvg.Y() );
+    return (*itMapCentralNode).second;
   }
-  else if ( shapeID > 0 )
+
+  // Get type of shape for the new central node
+
+  TopAbs_ShapeEnum shapeType = TopAbs_SHAPE;
+  int              solidID = -1;
+  int              faceID = -1;
+  TopoDS_Shape     shape;
+  TopTools_ListIteratorOfListOfShape it;
+
+  std::map< int, int > faceId2nbNodes;
+  std::map< int, int > ::iterator itMapWithIdFace;
+  
+  SMESHDS_Mesh* meshDS = GetMeshDS();
+  
+  // check if a face lies on a FACE, i.e. its all corner nodes lie either on the FACE or
+  // on sub-shapes of the FACE
+  if ( GetMesh()->HasShapeToMesh() )
   {
-    meshDS->SetNodeInVolume( centralNode, shapeID );
+    const SMDS_MeshNode* nodes[] = { n1, n2, n3 };
+    for(int i = 0; i < 3; i++)
+    {
+      shape = GetSubShapeByNode( nodes[i], meshDS );
+      if ( shape.IsNull() ) break;
+      if ( shape.ShapeType() == TopAbs_SOLID )
+      {
+        solidID   = nodes[i]->getshapeId();
+        shapeType = TopAbs_SOLID;
+        break;
+      }
+      if ( shape.ShapeType() == TopAbs_FACE )
+      {
+        faceID          = nodes[i]->getshapeId();
+        itMapWithIdFace = faceId2nbNodes.insert( std::make_pair( faceID, 0 ) ).first;
+        itMapWithIdFace->second++;
+      }
+      else
+      {
+        PShapeIteratorPtr it = GetAncestors(shape, *GetMesh(), TopAbs_FACE );
+        while ( const TopoDS_Shape* face = it->next() )
+        {
+          faceID = meshDS->ShapeToIndex( *face );
+          itMapWithIdFace = faceId2nbNodes.insert( std::make_pair( faceID, 0 ) ).first;
+          itMapWithIdFace->second++;
+        }
+      }
+    }
   }
-  else if ( myShapeID > 0 && mySetElemOnShape )
+  if ( solidID < 1 && !faceId2nbNodes.empty() ) // SOLID not found
   {
-    meshDS->SetMeshElementOnShape( centralNode, myShapeID );
+    // find ID of the FACE the four corner nodes belong to
+    itMapWithIdFace = faceId2nbNodes.begin();
+    for ( ; itMapWithIdFace != faceId2nbNodes.end(); ++itMapWithIdFace)
+    {
+      if ( itMapWithIdFace->second == 3 ) 
+      {
+        shapeType = TopAbs_FACE;
+        faceID = (*itMapWithIdFace).first;
+        break;
+      }
+    }
+  }
+
+  TopoDS_Face F;
+  gp_XY       uvAvg;
+  bool        badTria=false;
+
+  if ( shapeType == TopAbs_FACE )
+  {
+    F = TopoDS::Face( meshDS->IndexToShape( faceID ));
+    bool check;
+    gp_XY uv1  = GetNodeUV( F, n1, n23, &check );
+    gp_XY uv2  = GetNodeUV( F, n2, n31, &check );
+    gp_XY uv3  = GetNodeUV( F, n3, n12, &check );
+    gp_XY uv12 = GetNodeUV( F, n12, n3, &check );
+    gp_XY uv23 = GetNodeUV( F, n23, n1, &check );
+    gp_XY uv31 = GetNodeUV( F, n31, n2, &check );
+    uvAvg = GetCenterUV( uv1,uv2,uv3, uv12,uv23,uv31, &badTria );
+    if ( badTria )
+      force3d = false;
+  }
+
+  // Create a central node
+
+  gp_Pnt P;
+  if ( !F.IsNull() && !force3d )
+  {
+    TopLoc_Location        loc;
+    Handle( Geom_Surface ) S = BRep_Tool::Surface( F, loc );
+    P = S->Value( uvAvg.X(), uvAvg.Y() ).Transformed( loc );
+    centralNode = meshDS->AddNode( P.X(), P.Y(), P.Z() );
+    // if ( mySetElemOnShape ) node is not elem!
+    meshDS->SetNodeOnFace( centralNode, faceID, uvAvg.X(), uvAvg.Y() );
+  }
+  else // ( force3d || F.IsNull() )
+  {
+    P = ( SMESH_TNodeXYZ( n12 ) +
+          SMESH_TNodeXYZ( n23 ) +
+          SMESH_TNodeXYZ( n31 ) ) / 3;
+    centralNode = meshDS->AddNode( P.X(), P.Y(), P.Z() );
+
+    if ( !F.IsNull() ) // force3d
+    {
+      meshDS->SetNodeOnFace( centralNode, faceID, uvAvg.X(), uvAvg.Y() );
+    }
+    else if ( solidID > 0 )
+    {
+      meshDS->SetNodeInVolume( centralNode, solidID );
+    }
+    else if ( myShapeID > 0 && mySetElemOnShape )
+    {
+      meshDS->SetMeshElementOnShape( centralNode, myShapeID );
+    }
   }
   myMapWithCentralNode.insert( std::make_pair( keyOfMap, centralNode ) );
   return centralNode;
@@ -1203,6 +1406,9 @@ const SMDS_MeshNode* SMESH_MesherHelper::GetMediumNode(const SMDS_MeshNode* n1,
   bool uvOK[2] = { false, false };
 
   pair<int, TopAbs_ShapeEnum> pos = GetMediumPos( n1, n2, mySetElemOnShape );
+  // calling GetMediumPos() with useCurSubShape=mySetElemOnShape is OK only for the
+  // case where the lower dim mesh is already constructed, else, nodes on EDGEs are
+  // assigned to FACE, for example.
 
   // get positions of the given nodes on shapes
   if ( pos.second == TopAbs_FACE )
@@ -1223,8 +1429,15 @@ const SMDS_MeshNode* SMESH_MesherHelper::GetMediumNode(const SMDS_MeshNode* n1,
       return getMediumNodeOnComposedWire(n1,n2,force3d);
     }
     E = TopoDS::Edge(meshDS->IndexToShape( edgeID = pos.first ));
-    u[0] = GetNodeU(E,n1,n2, force3d ? 0 : &uvOK[0]);
-    u[1] = GetNodeU(E,n2,n1, force3d ? 0 : &uvOK[1]);
+    try {
+      u[0] = GetNodeU(E,n1,n2, force3d ? 0 : &uvOK[0]);
+      u[1] = GetNodeU(E,n2,n1, force3d ? 0 : &uvOK[1]);
+    }
+    catch ( Standard_Failure& f )
+    {
+      // issue 22502 / a node is on VERTEX not belonging to E
+      return getMediumNodeOnComposedWire(n1,n2,force3d);
+    }
   }
 
   if ( !force3d & uvOK[0] && uvOK[1] )
@@ -1378,8 +1591,12 @@ const SMDS_MeshNode* SMESH_MesherHelper::getMediumNodeOnComposedWire(const SMDS_
   }
 
   //if ( mySetElemOnShape ) node is not elem!
-  GetMeshDS()->SetNodeOnEdge(n12, edges[iOkEdge], u);
-
+  {
+    int edgeID = GetMeshDS()->ShapeToIndex( edges[iOkEdge] );
+    if ( edgeID != n12->getshapeId() )
+      GetMeshDS()->UnSetNodeOnShape( n12 );
+    GetMeshDS()->SetNodeOnEdge(n12, edgeID, u);
+  }
   myTLinkNodeMap.insert( make_pair( SMESH_TLink(n1,n2), n12 ));
 
   return n12;
@@ -1472,11 +1689,21 @@ SMDS_MeshFace* SMESH_MesherHelper::AddFace(const SMDS_MeshNode* n1,
     const SMDS_MeshNode* n12 = GetMediumNode(n1,n2,force3d);
     const SMDS_MeshNode* n23 = GetMediumNode(n2,n3,force3d);
     const SMDS_MeshNode* n31 = GetMediumNode(n3,n1,force3d);
-
-    if(id)
-      elem = meshDS->AddFaceWithID(n1, n2, n3, n12, n23, n31, id);
+    if(myCreateBiQuadratic)
+    {
+     const SMDS_MeshNode* nCenter = GetCentralNode(n1, n2, n3, n12, n23, n31, force3d);
+     if(id)
+       elem = meshDS->AddFaceWithID(n1, n2, n3, n12, n23, n31, nCenter, id);
+     else
+       elem = meshDS->AddFace(n1, n2, n3, n12, n23, n31, nCenter);
+    }
     else
-      elem = meshDS->AddFace(n1, n2, n3, n12, n23, n31);
+    {
+      if(id)
+        elem = meshDS->AddFaceWithID(n1, n2, n3, n12, n23, n31, id);
+      else
+        elem = meshDS->AddFace(n1, n2, n3, n12, n23, n31);
+    }
   }
   if ( mySetElemOnShape && myShapeID > 0 )
     meshDS->SetMeshElementOnShape( elem, myShapeID );
@@ -2101,7 +2328,7 @@ bool SMESH_MesherHelper::LoadNodeColumns(TParam2ColumnMap &            theParam2
     const SMDS_MeshNode *n2 = nCol2[ prevNbRows-1 ];
     // find face sharing node n1 and n2 and belonging to faceSubMesh
     while ( const SMDS_MeshElement* face =
-            SMESH_MeshEditor::FindFaceInSet( n1, n2, emptySet, avoidSet, &i1, &i2))
+            SMESH_MeshAlgos::FindFaceInSet( n1, n2, emptySet, avoidSet, &i1, &i2))
     {
       if ( faceSubMesh->Contains( face ))
       {
@@ -2170,7 +2397,7 @@ bool SMESH_MesherHelper::IsStructured( SMESH_subMesh* faceSM )
   list< int > nbEdgesInWires;
   int nbWires = SMESH_Block::GetOrderedEdges( TopoDS::Face( faceSM->GetSubShape() ),
                                               edges, nbEdgesInWires );
-  if ( nbWires != 1 || nbEdgesInWires.front() != 4 )
+  if ( nbWires != 1 /*|| nbEdgesInWires.front() != 4*/ ) // allow composite sides
     return false;
 
   // algo: find corners of a structure and then analyze nb of faces and
@@ -2282,9 +2509,16 @@ bool SMESH_MesherHelper::IsReversedSubMesh (const TopoDS_Face& theFace)
     {
       SMESH_TNodeXYZ nPnt[3];
       SMDS_ElemIteratorPtr nodesIt = elem->nodesIterator();
+      int iNodeOnFace = 0, iPosDim = SMDS_TOP_VERTEX;
       for ( int iN = 0; nodesIt->more() && iN < 3; ++iN) // loop on nodes
+      {
         nPnt[ iN ] = nodesIt->next();
-
+        if ( nPnt[ iN ]._node->GetPosition()->GetTypeOfPosition() > iPosDim )
+        {
+          iNodeOnFace = iN;
+          iPosDim = nPnt[ iN ]._node->GetPosition()->GetTypeOfPosition();
+        }
+      }
       // compute normal
       gp_Vec v01( nPnt[0], nPnt[1] ), v02( nPnt[0], nPnt[2] );
       if ( v01.SquareMagnitude() > RealSmall() &&
@@ -2292,7 +2526,7 @@ bool SMESH_MesherHelper::IsReversedSubMesh (const TopoDS_Face& theFace)
       {
         Ne = v01 ^ v02;
         if (( normalOK = ( Ne.SquareMagnitude() > RealSmall() )))
-          uv = GetNodeUV( theFace, nPnt[0]._node, nPnt[2]._node, &normalOK );
+          uv = GetNodeUV( theFace, nPnt[iNodeOnFace]._node, 0, &normalOK );
       }
     }
   }
@@ -2439,6 +2673,76 @@ double SMESH_MesherHelper::MaxTolerance( const TopoDS_Shape& shape )
   return tol;
 }
 
+//================================================================================
+/*!
+ * \brief Return an angle between two EDGEs sharing a common VERTEX with reference
+ *        of the FACE normal
+ *  \return double - the angle (between -Pi and Pi), negative if the angle is concave,
+ *                   1e100 in case of failure
+ *  \waring Care about order of the EDGEs and their orientation to be as they are
+ *          within the FACE! Don't pass degenerated EDGEs neither!
+ */
+//================================================================================
+
+double SMESH_MesherHelper::GetAngle( const TopoDS_Edge & theE1,
+                                     const TopoDS_Edge & theE2,
+                                     const TopoDS_Face & theFace)
+{
+  double angle = 1e100;
+  try
+  {
+    TopoDS_Vertex vCommon;
+    if ( !TopExp::CommonVertex( theE1, theE2, vCommon ))
+      return angle;
+    double f,l;
+    Handle(Geom_Curve)     c1 = BRep_Tool::Curve( theE1, f,l );
+    Handle(Geom_Curve)     c2 = BRep_Tool::Curve( theE2, f,l );
+    Handle(Geom2d_Curve) c2d1 = BRep_Tool::CurveOnSurface( theE1, theFace, f,l );
+    Handle(Geom_Surface) surf = BRep_Tool::Surface( theFace );
+    double                 p1 = BRep_Tool::Parameter( vCommon, theE1 );
+    double                 p2 = BRep_Tool::Parameter( vCommon, theE2 );
+    if ( c1.IsNull() || c2.IsNull() )
+      return angle;
+    gp_XY uv = c2d1->Value( p1 ).XY();
+    gp_Vec du, dv; gp_Pnt p;
+    surf->D1( uv.X(), uv.Y(), p, du, dv );
+    gp_Vec vec1, vec2, vecRef = du ^ dv;
+    int  nbLoops = 0;
+    double p1tmp = p1;
+    while ( vecRef.SquareMagnitude() < std::numeric_limits<double>::min() )
+    {
+      double dp = ( l - f ) / 1000.;
+      p1tmp += dp * (( Abs( p1 - f ) > Abs( p1 - l )) ? +1. : -1.);
+      uv = c2d1->Value( p1tmp ).XY();
+      surf->D1( uv.X(), uv.Y(), p, du, dv );
+      vecRef = du ^ dv;
+      if ( ++nbLoops > 10 )
+      {
+#ifdef _DEBUG_
+        cout << "SMESH_MesherHelper::GetAngle(): Captured in a sigularity" << endl;
+#endif
+        return angle;
+      }
+    }
+    if ( theFace.Orientation() == TopAbs_REVERSED )
+      vecRef.Reverse();
+    c1->D1( p1, p, vec1 );
+    c2->D1( p2, p, vec2 );
+    TopoDS_Face F = theFace;
+    if ( F.Orientation() == TopAbs_INTERNAL )
+      F.Orientation( TopAbs_FORWARD );
+    if ( theE1.Orientation() /*GetSubShapeOri( F, theE1 )*/ == TopAbs_REVERSED )
+      vec1.Reverse();
+    if ( theE2.Orientation() /*GetSubShapeOri( F, theE2 )*/ == TopAbs_REVERSED )
+      vec2.Reverse();
+    angle = vec1.AngleWithRef( vec2, vecRef );
+  }
+  catch (...)
+  {
+  }
+  return angle;
+}
+
 //================================================================================
 /*!
  * \brief Check if the first and last vertices of an edge are the same
@@ -2629,7 +2933,7 @@ namespace { // Structures used by FixQuadraticElements()
 //=======================================================================
 
 #define __DMP__(txt) \
-  //cout << txt
+  // cout << txt
 #define MSG(txt) __DMP__(txt<<endl)
 #define MSGBEG(txt) __DMP__(txt)
 
@@ -2922,7 +3226,7 @@ namespace { // Structures used by FixQuadraticElements()
     chLink->SetFace( this );
     MSGBEG( *this );
 
-    // propagate from quadrangle to neighbour faces
+    // propagate from quadrangle to neighbour faces
     if ( link->MediumPos() >= pos ) {
       int nbLinkFaces = link->_faces.size();
       if ( nbLinkFaces == 4 || (/*nbLinkFaces < 4 && */link->OnBoundary())) {
@@ -3037,27 +3341,11 @@ namespace { // Structures used by FixQuadraticElements()
 
   gp_Vec QFace::LinkNorm(const int i, SMESH_MesherHelper* /*uvHelper*/) const
   {
-    gp_Vec norm, vecOut;
-//     if ( uvHelper ) {
-//       TopoDS_Face face = TopoDS::Face( uvHelper->GetSubShape());
-//       const SMDS_MeshNode* inFaceNode = uvHelper->GetNodeUVneedInFaceNode() ? GetNodeInFace() : 0;
-//       gp_XY uv1 = uvHelper->GetNodeUV( face, _sides[i]->node1(), inFaceNode );
-//       gp_XY uv2 = uvHelper->GetNodeUV( face, _sides[i]->node2(), inFaceNode );
-//       norm.SetCoord( uv1.Y() - uv2.Y(), uv2.X() - uv1.X(), 0 );
-
-//       const QLink* otherLink = _sides[(i + 1) % _sides.size()];
-//       const SMDS_MeshNode* otherNode =
-//         otherLink->node1() == _sides[i]->node1() ? otherLink->node2() : otherLink->node1();
-//       gp_XY pIn = uvHelper->GetNodeUV( face, otherNode, inFaceNode );
-//       vecOut.SetCoord( uv1.X() - pIn.X(), uv1.Y() - pIn.Y(), 0 );
-//     }
-//     else {
-      norm = _normal ^ gp_Vec( XYZ(_sides[i]->node1()), XYZ(_sides[i]->node2()));
-      gp_XYZ pIn = ( XYZ( _sides[0]->node1() ) +
-                     XYZ( _sides[0]->node2() ) +
-                     XYZ( _sides[1]->node1() )) / 3.;
-      vecOut.SetXYZ( _sides[i]->MiddlePnt() - pIn );
-      //}
+    gp_Vec   norm = _normal ^ gp_Vec( XYZ(_sides[i]->node1()), XYZ(_sides[i]->node2()));
+    gp_XYZ    pIn = ( _sides[ (i+1)%3 ]->MiddlePnt() +
+                      _sides[ (i+2)%3 ]->MiddlePnt() ) / 2.;
+    gp_Vec vecOut = ( _sides[i]->MiddlePnt() - pIn );
+
     if ( norm * vecOut < 0 )
       norm.Reverse();
     double mag2 = norm.SquareMagnitude();
@@ -3111,10 +3399,26 @@ namespace { // Structures used by FixQuadraticElements()
     int iL1 = (iL + 1) % 3, iL2 = (iL + 2) % 3; // indices of the two other links of triangle
     TLinkInSet link1 = theLinks.find( _sides[iL1] );
     TLinkInSet link2 = theLinks.find( _sides[iL2] );
-    if ( link1 == theLinks.end() || link2 == theLinks.end() )
-      return thePrevLen;
-    const QFace* f1 = link1->NextFace( this ); // adjacent faces
-    const QFace* f2 = link2->NextFace( this );
+
+    const QFace *f1 = 0, *f2 = 0;  // adjacent faces
+    bool isBndLink1 = true, isBndLink2 = true;
+    if ( link1 != theLinks.end() && link2 != theLinks.end() )
+    {
+      f1 = link1->NextFace( this );
+      f2 = link2->NextFace( this );
+
+      isBndLink1 = ( theLink->MediumPos() > (*link1)->MediumPos() );
+      isBndLink2 = ( theLink->MediumPos() > (*link2)->MediumPos() );
+      if ( theStep == theFirstStep ) // (issue 22541) quad-dominant mesh
+      {
+        if ( !isBndLink1 && !f1 )
+          f1 = (*link1)->GetContinuesFace( this ); // get a quadrangle face
+        if ( !isBndLink2 && !f2 )
+          f2 = (*link2)->GetContinuesFace( this );
+      }
+    }
+    else if ( _sides.size() < 4 )
+      return thePrevLen;      
 
     // propagate to adjacent faces till limit step or boundary
     double len1 = thePrevLen + (theLink->MiddlePnt() - _sides[iL1]->MiddlePnt()).Modulus();
@@ -3123,7 +3427,7 @@ namespace { // Structures used by FixQuadraticElements()
     gp_Vec linkDir2(0,0,0);
     try {
       OCC_CATCH_SIGNALS;
-      if ( f1 && theLink->MediumPos() <= (*link1)->MediumPos() )
+      if ( f1 && !isBndLink1 )
         len1 = f1->MoveByBoundary
           ( *link1, theRefVec, theLinks, theFaceHelper, len1, theStep-1, &linkDir1, theSign);
       else
@@ -3134,7 +3438,7 @@ namespace { // Structures used by FixQuadraticElements()
     }
     try {
       OCC_CATCH_SIGNALS;
-      if ( f2 && theLink->MediumPos() <= (*link2)->MediumPos() )
+      if ( f2 && !isBndLink2 )
         len2 = f2->MoveByBoundary
           ( *link2, theRefVec, theLinks, theFaceHelper, len2, theStep-1, &linkDir2, theSign);
       else
@@ -3157,7 +3461,7 @@ namespace { // Structures used by FixQuadraticElements()
 
       MSG(string(theStep,'.')<<" Move "<< theLink->_mediumNode->GetID()<<
           " by " << refProj * ( 1 - r ) << " following " <<
-          (choose1 ? *link1->_qlink : *link2->_qlink));
+          (choose1 ? *link1->_qlink : *link2->_qlink)); // warning: link1 can be invalid
 
       if ( theLinkNorm ) *theLinkNorm = linkNorm;
     }
@@ -3354,7 +3658,7 @@ namespace { // Structures used by FixQuadraticElements()
           if ( pInterLink == interLinks.end() ) continue; // not internal link
           interLink->Move( bndLink->_nodeMove );
           // treated internal links become new boundary ones
-          interLinks. erase( pInterLink );
+          interLinks.erase( pInterLink );
           newBndLinks->insert( interLink );
         }
       }
@@ -3611,7 +3915,7 @@ namespace { // Structures used by FixQuadraticElements()
       {
         // check if the EDGE needs checking
         const TopoDS_Edge& edge = TopoDS::Edge( edgeIt.Current() );
-        if ( BRep_Tool::Degenerated( edge ) )
+        if ( SMESH_Algo::isDegenerated( edge ) )
           continue;
         if ( theHelper.IsRealSeam( edge ) &&
              edge.Orientation() == TopAbs_REVERSED )
@@ -3674,7 +3978,7 @@ namespace { // Structures used by FixQuadraticElements()
           {
             const SMDS_MeshElement* f = faceIt->next();
             if ( !faceSM->Contains( f ) ||
-                 f->NbNodes() != 6      || // check quadratic triangles only
+                 f->NbNodes() < 6       || // check quadratic triangles only
                  !checkedFaces.insert( f ).second )
               continue;
 
@@ -3694,7 +3998,7 @@ namespace { // Structures used by FixQuadraticElements()
             if ( nOnFace && nOnEdge.size() == 2 )
             {
               theHelper.AddTLinks( static_cast< const SMDS_MeshFace* > ( f ));
-              if ( !SMESH_Algo::FaceNormal( f, faceNorm, /*normalized=*/false ))
+              if ( !SMESH_MeshAlgos::FaceNormal( f, faceNorm, /*normalized=*/false ))
                 continue;
               gp_XYZ edgeDir  = SMESH_TNodeXYZ( nOnEdge[0] ) - SMESH_TNodeXYZ( nOnEdge[1] );
               gp_XYZ edgeNorm = faceNorm ^ edgeDir;
@@ -3784,7 +4088,7 @@ namespace { // Structures used by FixQuadraticElements()
 
       // a seacher to check if a volume is close to a concave face
       std::auto_ptr< SMESH_ElementSearcher > faceSearcher
-        ( SMESH_MeshEditor( theHelper.GetMesh() ).GetElementSearcher( faceIter ));
+        ( SMESH_MeshAlgos::GetElementSearcher( *theHelper.GetMeshDS(), faceIter ));
 
       // classifier
       //BRepClass3d_SolidClassifier solidClassifier( shape );
@@ -3858,7 +4162,7 @@ namespace { // Structures used by FixQuadraticElements()
             // to nInSolid than the link middle
             bool isDistorted = false;
             SMDS_FaceOfNodes onFaceTria( nOnFace[0], nOnFace[1], nOnFace[2] );
-            if ( !SMESH_Algo::FaceNormal( &onFaceTria, faceNorm, /*normalized=*/false ))
+            if ( !SMESH_MeshAlgos::FaceNormal( &onFaceTria, faceNorm, /*normalized=*/false ))
               continue;
             theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* > ( vol ));
             vector< pair< SMESH_TLink, const SMDS_MeshNode* > > links;
@@ -4267,6 +4571,9 @@ void SMESH_MesherHelper::FixQuadraticElements(SMESH_ComputeErrorPtr& compError,
               gp_XY newUV   = applyIn2D( s, oldUV, gp_XY( move.X(),move.Y()), gp_XY_Added);
               gp_Pnt newPnt = s->Value( newUV.X(), newUV.Y());
               move = gp_Vec( XYZ((*link1)->_mediumNode), newPnt.Transformed(loc) );
+              if ( SMDS_FacePosition* nPos =
+                   dynamic_cast< SMDS_FacePosition* >((*link1)->_mediumNode->GetPosition()))
+                nPos->SetParameters( newUV.X(), newUV.Y() );
 #ifdef _DEBUG_
               if ( (XYZ((*link1)->node1()) - XYZ((*link1)->node2())).SquareModulus() <
                    move.SquareMagnitude())
@@ -4295,9 +4602,10 @@ void SMESH_MesherHelper::FixQuadraticElements(SMESH_ComputeErrorPtr& compError,
   // 4. Move nodes
   // -------------
 
-  TIDSortedElemSet biQuadQuas, triQuadHexa;
+  TIDSortedElemSet biQuadQuas, biQuadTris, triQuadHexa;
   const SMDS_MeshElement *biQuadQua, *triQuadHex;
   const bool toFixCentralNodes = ( myMesh->NbBiQuadQuadrangles() +
+                                   myMesh->NbBiQuadTriangles() +
                                    myMesh->NbTriQuadraticHexas() );
 
   for ( pLink = links.begin(); pLink != links.end(); ++pLink ) {
@@ -4314,23 +4622,22 @@ void SMESH_MesherHelper::FixQuadraticElements(SMESH_ComputeErrorPtr& compError,
         while ( eIt->more() )
         {
           const SMDS_MeshElement* e = eIt->next();
-          SMDSAbs_EntityType   type = e->GetEntityType();
-          if ( type == SMDSEntity_BiQuad_Quadrangle )
-            biQuadQuas.insert( e );
-          else if ( type == SMDSEntity_TriQuad_Hexa )
-            triQuadHexa.insert( e );
+          switch( e->GetEntityType() ) {
+          case SMDSEntity_BiQuad_Quadrangle: biQuadQuas.insert( e ); break;
+          case SMDSEntity_BiQuad_Triangle:   biQuadTris.insert( e ); break;
+          case SMDSEntity_TriQuad_Hexa:      triQuadHexa.insert( e ); break;
+          default:;
+          }
         }
       }
     }
   }
-
   // Fix positions of central nodes of bi-tri-quadratic elements
 
   // treat bi-quad quadrangles
   {
     vector< const SMDS_MeshNode* > nodes( 9 );
     gp_XY uv[ 9 ];
-    //TIDSortedNodeSet checkedNodes;
     TIDSortedElemSet::iterator quadIt = biQuadQuas.begin();
     for ( ; quadIt != biQuadQuas.end(); ++quadIt )
     {
@@ -4353,13 +4660,46 @@ void SMESH_MesherHelper::FixQuadraticElements(SMESH_ComputeErrorPtr& compError,
         if ( i > 3 && nodes[i]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
           CheckNodeUV( F, nodes[i], uv[ i ], 2*tol, /*force=*/true );
       }
-      // move central node
+      // move the central node
       gp_XY uvCent = calcTFI (0.5, 0.5, uv[0],uv[1],uv[2],uv[3],uv[4],uv[5],uv[6],uv[7] );
       gp_Pnt p = surf->Value( uvCent.X(), uvCent.Y() ).Transformed( loc );
       GetMeshDS()->MoveNode( nodes[8], p.X(), p.Y(), p.Z());
     }
   }
 
+  // treat bi-quad triangles
+  {
+    vector< const SMDS_MeshNode* > nodes;
+    gp_XY uv[ 6 ];
+    TIDSortedElemSet::iterator triIt = biQuadTris.begin();
+    for ( ; triIt != biQuadTris.end(); ++triIt )
+    {
+      const SMDS_MeshElement* tria = *triIt;
+      // FACE
+      const TopoDS_Shape& S = GetMeshDS()->IndexToShape( tria->getshapeId() );
+      if ( S.IsNull() || S.ShapeType() != TopAbs_FACE ) continue;
+      const TopoDS_Face& F = TopoDS::Face( S );
+      Handle( Geom_Surface ) surf = BRep_Tool::Surface( F, loc );
+      const double tol = BRep_Tool::Tolerance( F );
+
+      // nodes
+      nodes.assign( tria->begin_nodes(), tria->end_nodes() );
+      // UV
+      for ( int i = 0; i < 6; ++i )
+      {
+        uv[ i ] = GetNodeUV( F, nodes[i], nodes[(i+1)%3], &checkUV );
+        // as this method is used after mesh generation, UV of nodes is not
+        // updated according to bending links, so we update 
+        if ( nodes[i]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
+          CheckNodeUV( F, nodes[i], uv[ i ], 2*tol, /*force=*/true );
+      }
+      // move the central node
+      gp_XY uvCent = GetCenterUV( uv[0], uv[1], uv[2], uv[3], uv[4], uv[5] );
+      gp_Pnt p = surf->Value( uvCent.X(), uvCent.Y() ).Transformed( loc );
+      GetMeshDS()->MoveNode( tria->GetNode(6), p.X(), p.Y(), p.Z() );
+    }
+  }
+
   // treat tri-quadratic hexahedra
   {
     SMDS_VolumeTool volExp;
@@ -4422,62 +4762,4 @@ void SMESH_MesherHelper::FixQuadraticElements(SMESH_ComputeErrorPtr& compError,
                              nCenterCoords.X(), nCenterCoords.Y(), nCenterCoords.Z());
     }
   }
-
-  // Issue 0020982
-  // Move the apex of pyramid together with the most curved link.
-  // TIDSortedNodeSet::iterator apexIt = apexOfPyramid.begin();
-  // for ( ; apexIt != apexOfPyramid.end(); ++apexIt )
-  // {
-  //   SMESH_TNodeXYZ apex = *apexIt;
-
-  //   gp_Vec maxMove( 0,0,0 );
-  //   double maxMoveSize2 = 0;
-
-  //   // shift of node index to get medium nodes between the base nodes
-  //   const int base2MediumShift = 5;
-
-  //   // find maximal movement of medium node
-  //   SMDS_ElemIteratorPtr volIt = apex._node->GetInverseElementIterator( SMDSAbs_Volume );
-  //   vector< const SMDS_MeshElement* > pyramids;
-  //   while ( volIt->more() )
-  //   {
-  //     const SMDS_MeshElement* pyram = volIt->next();
-  //     if ( pyram->GetEntityType() != SMDSEntity_Quad_Pyramid ) continue;
-  //     pyramids.push_back( pyram );
-
-  //     for ( int iBase = 0; iBase < apexIndex; ++iBase )
-  //     {
-  //       SMESH_TNodeXYZ medium = pyram->GetNode( iBase + base2MediumShift );
-  //       if ( medium._node->GetPosition()->GetTypeOfPosition() != SMDS_TOP_3DSPACE )
-  //       {
-  //         SMESH_TNodeXYZ n1 = pyram->GetNode( iBase );
-  //         SMESH_TNodeXYZ n2 = pyram->GetNode( ( iBase+1 ) % 4 );
-  //         gp_Pnt middle = 0.5 * ( n1 + n2 );
-  //         gp_Vec move( middle, medium );
-  //         double moveSize2 = move.SquareMagnitude();
-  //         if ( moveSize2 > maxMoveSize2 )
-  //           maxMove = move, maxMoveSize2 = moveSize2;
-  //       }
-  //     }
-  //   }
-
-  //   // move the apex
-  //   if ( maxMoveSize2 > 1e-20 )
-  //   {
-  //     apex += maxMove.XYZ();
-  //     GetMeshDS()->MoveNode( apex._node, apex.X(), apex.Y(), apex.Z());
-
-  //     // move medium nodes neighboring the apex to the middle
-  //     const int base2MediumShift_2 = 9;
-  //     for ( unsigned i = 0; i < pyramids.size(); ++i )
-  //       for ( int iBase = 0; iBase < apexIndex; ++iBase )
-  //       {
-  //         SMESH_TNodeXYZ         base = pyramids[i]->GetNode( iBase );
-  //         const SMDS_MeshNode* medium = pyramids[i]->GetNode( iBase + base2MediumShift_2 );
-  //         gp_XYZ middle = 0.5 * ( apex + base );
-  //         GetMeshDS()->MoveNode( medium, middle.X(), middle.Y(), middle.Z());
-  //       }
-  //   }
-  // }
 }
-