Salome HOME
IPAL52781: Orientation of a wall face created by revolution is wrong
[modules/smesh.git] / src / SMDS / SMDS_VolumeTool.cxx
index b86dc16c377a99bae4f78b2a322e75bc97aecd23..8296de035dad4140e1a6b18f038d316e4d2157f0 100644 (file)
@@ -40,6 +40,8 @@
 #include <map>
 #include <limits>
 #include <cmath>
+#include <numeric>
+#include <algorithm>
 
 using namespace std;
 
@@ -396,7 +398,7 @@ inline double XYZ::SquareMagnitude() {
   SMDS_VolumeTool::VolumeType quadToLinear(SMDS_VolumeTool::VolumeType quadType)
   {
     SMDS_VolumeTool::VolumeType linType = SMDS_VolumeTool::VolumeType( int(quadType)-4 );
-    const int nbCornersByQuad = SMDS_VolumeTool::NbCornerNodes( quadType );
+    const int           nbCornersByQuad = SMDS_VolumeTool::NbCornerNodes( quadType );
     if ( SMDS_VolumeTool::NbCornerNodes( linType ) == nbCornersByQuad )
       return linType;
 
@@ -410,14 +412,33 @@ inline double XYZ::SquareMagnitude() {
 
 } // namespace
 
+//================================================================================
+/*!
+ * \brief Saver/restorer of a SMDS_VolumeTool::myCurFace
+ */
+//================================================================================
+
+struct SMDS_VolumeTool::SaveFacet
+{
+  SMDS_VolumeTool::Facet  mySaved;
+  SMDS_VolumeTool::Facet& myToRestore;
+  SaveFacet( SMDS_VolumeTool::Facet& facet ): myToRestore( facet )
+  {
+    mySaved = facet;
+  }
+  ~SaveFacet()
+  {
+    if ( myToRestore.myIndex != mySaved.myIndex )
+      myToRestore = mySaved;
+  }
+};
+
 //=======================================================================
 //function : SMDS_VolumeTool
-//purpose  : 
+//purpose  :
 //=======================================================================
 
 SMDS_VolumeTool::SMDS_VolumeTool ()
-  : myVolumeNodes( NULL ),
-    myFaceNodes( NULL )
 {
   Set( 0 );
 }
@@ -429,8 +450,6 @@ SMDS_VolumeTool::SMDS_VolumeTool ()
 
 SMDS_VolumeTool::SMDS_VolumeTool (const SMDS_MeshElement* theVolume,
                                   const bool              ignoreCentralNodes)
-  : myVolumeNodes( NULL ),
-    myFaceNodes( NULL )
 {
   Set( theVolume, ignoreCentralNodes );
 }
@@ -442,11 +461,7 @@ SMDS_VolumeTool::SMDS_VolumeTool (const SMDS_MeshElement* theVolume,
 
 SMDS_VolumeTool::~SMDS_VolumeTool()
 {
-  if ( myVolumeNodes != NULL ) delete [] myVolumeNodes;
-  if ( myFaceNodes != NULL   ) delete [] myFaceNodes;
-
-  myFaceNodeIndices = NULL;
-  myVolumeNodes = myFaceNodes = NULL;
+  myCurFace.myNodeIndices = NULL;
 }
 
 //=======================================================================
@@ -464,42 +479,37 @@ bool SMDS_VolumeTool::Set (const SMDS_MeshElement* theVolume,
 
   myVolForward = true;
   myNbFaces = 0;
-  myVolumeNbNodes = 0;
-  if (myVolumeNodes != NULL) {
-    delete [] myVolumeNodes;
-    myVolumeNodes = NULL;
-  }
+  myVolumeNodes.clear();
   myPolyIndices.clear();
+  myPolyQuantities.clear();
+  myPolyFacetOri.clear();
+  myFwdLinks.clear();
 
   myExternalFaces = false;
 
   myAllFacesNodeIndices_F  = 0;
-  //myAllFacesNodeIndices_FE = 0;
   myAllFacesNodeIndices_RE = 0;
   myAllFacesNbNodes        = 0;
 
-  myCurFace = -1;
-  myFaceNbNodes = 0;
-  myFaceNodeIndices = NULL;
-  if (myFaceNodes != NULL) {
-    delete [] myFaceNodes;
-    myFaceNodes = NULL;
-  }
+  myCurFace.myIndex = -1;
+  myCurFace.myNodeIndices = NULL;
+  myCurFace.myNodes.clear();
 
   // set volume data
   if ( !theVolume || theVolume->GetType() != SMDSAbs_Volume )
     return false;
 
   myVolume = theVolume;
-  if (myVolume->IsPoly())
-    myPolyedre = dynamic_cast<const SMDS_VtkVolume*>( myVolume );
-
   myNbFaces = theVolume->NbFaces();
-  myVolumeNbNodes = theVolume->NbNodes();
+  if ( myVolume->IsPoly() )
+  {
+    myPolyedre = dynamic_cast<const SMDS_VtkVolume*>( myVolume );
+    myPolyFacetOri.resize( myNbFaces, 0 );
+  }
 
   // set nodes
   int iNode = 0;
-  myVolumeNodes = new const SMDS_MeshNode* [myVolumeNbNodes];
+  myVolumeNodes.resize( myVolume->NbNodes() );
   SMDS_ElemIteratorPtr nodeIt = myVolume->nodesIterator();
   while ( nodeIt->more() )
     myVolumeNodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
@@ -512,18 +522,19 @@ bool SMDS_VolumeTool::Set (const SMDS_MeshElement* theVolume,
   {
     // define volume orientation
     XYZ botNormal;
-    GetFaceNormal( 0, botNormal.x, botNormal.y, botNormal.z );
-    const SMDS_MeshNode* botNode = myVolumeNodes[ 0 ];
-    int topNodeIndex = myVolume->NbCornerNodes() - 1;
-    while ( !IsLinked( 0, topNodeIndex, /*ignoreMediumNodes=*/true )) --topNodeIndex;
-    const SMDS_MeshNode* topNode = myVolumeNodes[ topNodeIndex ];
-    XYZ upDir (topNode->X() - botNode->X(),
-               topNode->Y() - botNode->Y(),
-               topNode->Z() - botNode->Z() );
-    myVolForward = ( botNormal.Dot( upDir ) < 0 );
-
+    if ( GetFaceNormal( 0, botNormal.x, botNormal.y, botNormal.z ))
+    {
+      const SMDS_MeshNode* botNode = myVolumeNodes[ 0 ];
+      int topNodeIndex = myVolume->NbCornerNodes() - 1;
+      while ( !IsLinked( 0, topNodeIndex, /*ignoreMediumNodes=*/true )) --topNodeIndex;
+      const SMDS_MeshNode* topNode = myVolumeNodes[ topNodeIndex ];
+      XYZ upDir (topNode->X() - botNode->X(),
+                 topNode->Y() - botNode->Y(),
+                 topNode->Z() - botNode->Z() );
+      myVolForward = ( botNormal.Dot( upDir ) < 0 );
+    }
     if ( !myVolForward )
-      myCurFace = -1; // previous setFace(0) didn't take myVolForward into account
+      myCurFace.myIndex = -1; // previous setFace(0) didn't take myVolForward into account
   }
   return true;
 }
@@ -549,10 +560,10 @@ void SMDS_VolumeTool::Inverse ()
   }
 
   myVolForward = !myVolForward;
-  myCurFace = -1;
+  myCurFace.myIndex = -1;
 
   // inverse top and bottom faces
-  switch ( myVolumeNbNodes ) {
+  switch ( myVolumeNodes.size() ) {
   case 4:
     SWAP_NODES( myVolumeNodes, 1, 2 );
     break;
@@ -626,7 +637,7 @@ SMDS_VolumeTool::VolumeType SMDS_VolumeTool::GetVolumeType() const
   if ( myPolyedre )
     return POLYHEDA;
 
-  switch( myVolumeNbNodes ) {
+  switch( myVolumeNodes.size() ) {
   case 4: return TETRA;
   case 5: return PYRAM;
   case 6: return PENTA;
@@ -653,28 +664,18 @@ static double getTetraVolume(const SMDS_MeshNode* n1,
                              const SMDS_MeshNode* n3,
                              const SMDS_MeshNode* n4)
 {
-  double X1 = n1->X();
-  double Y1 = n1->Y();
-  double Z1 = n1->Z();
-
-  double X2 = n2->X();
-  double Y2 = n2->Y();
-  double Z2 = n2->Z();
-
-  double X3 = n3->X();
-  double Y3 = n3->Y();
-  double Z3 = n3->Z();
-
-  double X4 = n4->X();
-  double Y4 = n4->Y();
-  double Z4 = n4->Z();
-
-  double Q1 = -(X1-X2)*(Y3*Z4-Y4*Z3);
-  double Q2 =  (X1-X3)*(Y2*Z4-Y4*Z2);
-  double R1 = -(X1-X4)*(Y2*Z3-Y3*Z2);
-  double R2 = -(X2-X3)*(Y1*Z4-Y4*Z1);
-  double S1 =  (X2-X4)*(Y1*Z3-Y3*Z1);
-  double S2 = -(X3-X4)*(Y1*Z2-Y2*Z1);
+  double p1[3], p2[3], p3[3], p4[3];
+  n1->GetXYZ( p1 );
+  n2->GetXYZ( p2 );
+  n3->GetXYZ( p3 );
+  n4->GetXYZ( p4 );
+
+  double Q1 = -(p1[ 0 ]-p2[ 0 ])*(p3[ 1 ]*p4[ 2 ]-p4[ 1 ]*p3[ 2 ]);
+  double Q2 =  (p1[ 0 ]-p3[ 0 ])*(p2[ 1 ]*p4[ 2 ]-p4[ 1 ]*p2[ 2 ]);
+  double R1 = -(p1[ 0 ]-p4[ 0 ])*(p2[ 1 ]*p3[ 2 ]-p3[ 1 ]*p2[ 2 ]);
+  double R2 = -(p2[ 0 ]-p3[ 0 ])*(p1[ 1 ]*p4[ 2 ]-p4[ 1 ]*p1[ 2 ]);
+  double S1 =  (p2[ 0 ]-p4[ 0 ])*(p1[ 1 ]*p3[ 2 ]-p3[ 1 ]*p1[ 2 ]);
+  double S2 = -(p3[ 0 ]-p4[ 0 ])*(p1[ 1 ]*p2[ 2 ]-p2[ 1 ]*p1[ 2 ]);
 
   return (Q1+Q2+R1+R2+S1+S2)/6.0;
 }
@@ -697,23 +698,21 @@ double SMDS_VolumeTool::GetSize() const
 
     // split a polyhedron into tetrahedrons
 
-    int saveCurFace = myCurFace;
+    SaveFacet savedFacet( myCurFace );
     SMDS_VolumeTool* me = const_cast< SMDS_VolumeTool* > ( this );
     for ( int f = 0; f < NbFaces(); ++f )
     {
       me->setFace( f );
-      XYZ area (0,0,0), p1( myFaceNodes[0] );
-      for ( int n = 0; n < myFaceNbNodes; ++n )
+      XYZ area (0,0,0), p1( myCurFace.myNodes[0] );
+      for ( int n = 0; n < myCurFace.myNbNodes; ++n )
       {
-        XYZ p2( myFaceNodes[ n+1 ]);
+        XYZ p2( myCurFace.myNodes[ n+1 ]);
         area = area + p1.Crossed( p2 );
         p1 = p2;
       }
       V += p1.Dot( area );
     }
     V /= 6;
-    if ( saveCurFace > -1 && saveCurFace != myCurFace )
-      me->setFace( myCurFace );
   }
   else 
   {
@@ -844,14 +843,14 @@ bool SMDS_VolumeTool::GetBaryCenter(double & X, double & Y, double & Z) const
   if ( !myVolume )
     return false;
 
-  for ( int i = 0; i < myVolumeNbNodes; i++ ) {
+  for ( int i = 0; i < myVolumeNodes.size(); i++ ) {
     X += myVolumeNodes[ i ]->X();
     Y += myVolumeNodes[ i ]->Y();
     Z += myVolumeNodes[ i ]->Z();
   }
-  X /= myVolumeNbNodes;
-  Y /= myVolumeNbNodes;
-  Z /= myVolumeNbNodes;
+  X /= myVolumeNodes.size();
+  Y /= myVolumeNodes.size();
+  Z /= myVolumeNodes.size();
 
   return true;
 }
@@ -875,7 +874,7 @@ bool SMDS_VolumeTool::IsOut(double X, double Y, double Z, double tol) const
     if ( !IsFaceExternal( iF ))
       faceNormal = XYZ() - faceNormal; // reverse
 
-    XYZ face2p( p - XYZ( myFaceNodes[0] ));
+    XYZ face2p( p - XYZ( myCurFace.myNodes[0] ));
     if ( face2p.Dot( faceNormal ) > tol )
       return true;
   }
@@ -890,7 +889,7 @@ bool SMDS_VolumeTool::IsOut(double X, double Y, double Z, double tol) const
 void SMDS_VolumeTool::SetExternalNormal ()
 {
   myExternalFaces = true;
-  myCurFace = -1;
+  myCurFace.myIndex = -1;
 }
 
 //=======================================================================
@@ -900,9 +899,9 @@ void SMDS_VolumeTool::SetExternalNormal ()
 
 int SMDS_VolumeTool::NbFaceNodes( int faceIndex ) const
 {
-    if ( !setFace( faceIndex ))
-      return 0;
-    return myFaceNbNodes;
+  if ( !setFace( faceIndex ))
+    return 0;
+  return myCurFace.myNbNodes;
 }
 
 //=======================================================================
@@ -917,7 +916,7 @@ const SMDS_MeshNode** SMDS_VolumeTool::GetFaceNodes( int faceIndex ) const
 {
   if ( !setFace( faceIndex ))
     return 0;
-  return myFaceNodes;
+  return &myCurFace.myNodes[0];
 }
 
 //=======================================================================
@@ -933,15 +932,7 @@ const int* SMDS_VolumeTool::GetFaceNodesIndices( int faceIndex ) const
   if ( !setFace( faceIndex ))
     return 0;
 
-  if (myPolyedre)
-  {
-    SMDS_VolumeTool* me = const_cast< SMDS_VolumeTool* > ( this );
-    me->myPolyIndices.resize( myFaceNbNodes + 1 );
-    me->myFaceNodeIndices = & me->myPolyIndices[0];
-    for ( int i = 0; i <= myFaceNbNodes; ++i )
-      me->myFaceNodeIndices[i] = myVolume->GetNodeIndex( myFaceNodes[i] );
-  }
-  return myFaceNodeIndices;
+  return myCurFace.myNodeIndices;
 }
 
 //=======================================================================
@@ -956,11 +947,44 @@ bool SMDS_VolumeTool::GetFaceNodes (int                        faceIndex,
     return false;
 
   theFaceNodes.clear();
-  theFaceNodes.insert( myFaceNodes, myFaceNodes + myFaceNbNodes );
+  theFaceNodes.insert( myCurFace.myNodes.begin(), myCurFace.myNodes.end() );
 
   return true;
 }
 
+namespace
+{
+  struct NLink : public std::pair<int,int>
+  {
+    int myOri;
+    NLink(const SMDS_MeshNode* n1=0, const SMDS_MeshNode* n2=0, int ori=1 )
+    {
+      if ( n1 )
+      {
+        if (( myOri = ( n1->GetID() < n2->GetID() )))
+        {
+          first  = n1->GetID();
+          second = n2->GetID();
+        }
+        else
+        {
+          myOri  = -1;
+          first  = n2->GetID();
+          second = n1->GetID();
+        }
+        myOri *= ori;
+      }
+      else
+      {
+        myOri = first = second = 0;
+      }
+    }
+    //int Node1() const { return myOri == -1 ? second : first; }
+
+    //bool IsSameOri( const std::pair<int,int>& link ) const { return link.first == Node1(); }
+  };
+}
+
 //=======================================================================
 //function : IsFaceExternal
 //purpose  : Check normal orientation of a given face
@@ -971,39 +995,179 @@ bool SMDS_VolumeTool::IsFaceExternal( int faceIndex ) const
   if ( myExternalFaces || !myVolume )
     return true;
 
-  if (myVolume->IsPoly()) {
-    XYZ aNormal, baryCenter, p0 (myPolyedre->GetFaceNode(faceIndex + 1, 1));
-    GetFaceNormal(faceIndex, aNormal.x, aNormal.y, aNormal.z);
-    GetBaryCenter(baryCenter.x, baryCenter.y, baryCenter.z);
-    XYZ insideVec (baryCenter - p0);
-    if (insideVec.Dot(aNormal) > 0)
-      return false;
+  if ( !myPolyedre ) // all classical volumes have external facet normals
     return true;
+
+  SMDS_VolumeTool* me = const_cast< SMDS_VolumeTool* >( this );
+
+  if ( myPolyFacetOri[ faceIndex ])
+    return myPolyFacetOri[ faceIndex ] > 0;
+
+  int ori = 0; // -1-in, +1-out, 0-undef
+  double minProj, maxProj;
+  if ( projectNodesToNormal( faceIndex, minProj, maxProj ))
+  {
+    // all nodes are on the same side of the facet
+    ori = ( minProj < 0 ? +1 : -1 );
+    me->myPolyFacetOri[ faceIndex ] = ori;
+
+    if ( !me->myFwdLinks.empty() ) // concave polyhedron; collect oriented links
+      for ( int i = 0; i < myCurFace.myNbNodes; ++i )
+      {
+        NLink link( myCurFace.myNodes[i], myCurFace.myNodes[i+1], ori );
+        me->myFwdLinks.insert( make_pair( link, link.myOri ));
+      }
+    return ori > 0;
+  }
+
+  SaveFacet savedFacet( myCurFace );
+
+  // concave polyhedron
+
+  if ( me->myFwdLinks.empty() ) // get links of the least ambiguously oriented facet
+  {
+    for ( size_t i = 0; i < myPolyFacetOri.size() && !ori; ++i )
+      ori = me->myPolyFacetOri[ i ];
+
+    if ( !ori ) // none facet is oriented yet
+    {
+      // find the least ambiguously oriented facet
+      int faceMostConvex = -1;
+      std::map< double, int > convexity2face;
+      for ( size_t iF = 0; iF < myPolyFacetOri.size() && faceMostConvex < 0; ++iF )
+      {
+        if ( projectNodesToNormal( iF, minProj, maxProj ))
+        {
+          // all nodes are on the same side of the facet
+          me->myPolyFacetOri[ iF ] = ( minProj < 0 ? +1 : -1 );
+          faceMostConvex = iF;
+        }
+        else
+        {
+          ori = ( -minProj < maxProj ? -1 : +1 );
+          double convexity = std::min( -minProj, maxProj ) / std::max( -minProj, maxProj );
+          convexity2face.insert( make_pair( convexity, iF * ori ));
+        }
+      }
+      if ( faceMostConvex < 0 ) // none facet has nodes on the same side
+      {
+        // use the least ambiguous facet
+        faceMostConvex = convexity2face.begin()->second;
+        ori = ( faceMostConvex < 0 ? -1 : +1 );
+        faceMostConvex = std::abs( faceMostConvex );
+        me->myPolyFacetOri[ faceMostConvex ] = ori;
+      }
+    }
+    // collect links of the oriented facets in me->myFwdLinks
+    for ( size_t iF = 0; iF < myPolyFacetOri.size(); ++iF )
+    {
+      ori = me->myPolyFacetOri[ iF ];
+      if ( !ori ) continue;
+      setFace( iF );
+      for ( int i = 0; i < myCurFace.myNbNodes; ++i )
+      {
+        NLink link( myCurFace.myNodes[i], myCurFace.myNodes[i+1], ori );
+        me->myFwdLinks.insert( make_pair( link, link.myOri ));
+      }
+    }
+  }
+
+  // compare orientation of links of the facet with myFwdLinks
+  ori = 0;
+  setFace( faceIndex );
+  vector< NLink > links( myCurFace.myNbNodes ), links2;
+  for ( int i = 0; i < myCurFace.myNbNodes && !ori; ++i )
+  {
+    NLink link( myCurFace.myNodes[i], myCurFace.myNodes[i+1] );
+    std::map<Link, int>::iterator l2o = me->myFwdLinks.find( link );
+    if ( l2o != me->myFwdLinks.end() )
+      ori = link.myOri * l2o->second * -1;
+    links[ i ] = link;
+  }
+  while ( !ori ) // the facet has no common links with already oriented facets
+  {
+    // orient and collect links of other non-oriented facets
+    for ( size_t iF = 0; iF < myPolyFacetOri.size(); ++iF )
+    {
+      if ( me->myPolyFacetOri[ iF ] ) continue; // already oriented
+      setFace( iF );
+      links2.clear();
+      ori = 0;
+      for ( int i = 0; i < myCurFace.myNbNodes && !ori; ++i )
+      {
+        NLink link( myCurFace.myNodes[i], myCurFace.myNodes[i+1] );
+        std::map<Link, int>::iterator l2o = me->myFwdLinks.find( link );
+        if ( l2o != me->myFwdLinks.end() )
+          ori = link.myOri * l2o->second * -1;
+        links2.push_back( link );
+      }
+      if ( ori ) // one more facet oriented
+      {
+        me->myPolyFacetOri[ iF ] = ori;
+        for ( size_t i = 0; i < links2.size(); ++i )
+          me->myFwdLinks.insert( make_pair( links2[i], links2[i].myOri * ori ));
+        break;
+      }
+    }
+    if ( !ori )
+      return false; // error in algorithm: infinite loop
+
+    // try to orient the facet again
+    ori = 0;
+    for ( size_t i = 0; i < links.size() && !ori; ++i )
+    {
+      std::map<Link, int>::iterator l2o = me->myFwdLinks.find( links[i] );
+      if ( l2o != me->myFwdLinks.end() )
+        ori = links[i].myOri * l2o->second * -1;
+    }
+    me->myPolyFacetOri[ faceIndex ] = ori;
   }
 
-  // switch ( myVolumeNbNodes ) {
-  // case 4:
-  // case 5:
-  // case 10:
-  // case 13:
-  //   // only the bottom of a reversed tetrahedron can be internal
-  //   return ( myVolForward || faceIndex != 0 );
-  // case 6:
-  // case 15:
-  // case 12:
-  //   // in a forward prism, the top is internal, in a reversed one - bottom
-  //   return ( myVolForward ? faceIndex != 1 : faceIndex != 0 );
-  // case 8:
-  // case 20:
-  // case 27: {
-  //   // in a forward hexahedron, even face normal is external, odd - internal
-  //   bool odd = faceIndex % 2;
-  //   return ( myVolForward ? !odd : odd );
+  return ori > 0;
+}
+
+//=======================================================================
+//function : projectNodesToNormal
+//purpose  : compute min and max projections of all nodes to normal of a facet.
+//=======================================================================
+
+bool SMDS_VolumeTool::projectNodesToNormal( int     faceIndex,
+                                            double& minProj,
+                                            double& maxProj ) const
+{
+  minProj = std::numeric_limits<double>::max();
+  maxProj = std::numeric_limits<double>::min();
+
+  XYZ normal;
+  if ( !GetFaceNormal( faceIndex, normal.x, normal.y, normal.z ))
+    return false;
+  XYZ p0 ( myCurFace.myNodes[0] );
+  for ( size_t i = 0; i < myVolumeNodes.size(); ++i )
+  {
+    if ( std::find( myCurFace.myNodes.begin() + 1,
+                    myCurFace.myNodes.end(),
+                    myVolumeNodes[ i ] ) != myCurFace.myNodes.end() )
+      continue; // node of the faceIndex-th facet
+
+    double proj = normal.Dot( XYZ( myVolumeNodes[ i ]) - p0 );
+    if ( proj < minProj ) minProj = proj;
+    if ( proj > maxProj ) maxProj = proj;
+  }
+  const double tol = 1e-7;
+  minProj += tol;
+  maxProj -= tol;
+  bool diffSize = ( minProj * maxProj < 0 );
+  // if ( diffSize )
+  // {
+  //   minProj = -minProj;
   // }
-  // default:;
+  // else if ( minProj < 0 )
+  // {
+  //   minProj = -minProj;
+  //   maxProj = -maxProj;
   // }
-  // return false;
-  return true;
+
+  return !diffSize; // ? 0 : (minProj >= 0);
 }
 
 //=======================================================================
@@ -1016,16 +1180,16 @@ bool SMDS_VolumeTool::GetFaceNormal (int faceIndex, double & X, double & Y, doub
   if ( !setFace( faceIndex ))
     return false;
 
-  const int iQuad = ( myFaceNbNodes > 6 && !myPolyedre ) ? 2 : 1;
-  XYZ p1 ( myFaceNodes[0*iQuad] );
-  XYZ p2 ( myFaceNodes[1*iQuad] );
-  XYZ p3 ( myFaceNodes[2*iQuad] );
+  const int iQuad = ( !myPolyedre && myCurFace.myNbNodes > 6 ) ? 2 : 1;
+  XYZ p1 ( myCurFace.myNodes[0*iQuad] );
+  XYZ p2 ( myCurFace.myNodes[1*iQuad] );
+  XYZ p3 ( myCurFace.myNodes[2*iQuad] );
   XYZ aVec12( p2 - p1 );
   XYZ aVec13( p3 - p1 );
   XYZ cross = aVec12.Crossed( aVec13 );
 
-  if ( myFaceNbNodes >3*iQuad ) {
-    XYZ p4 ( myFaceNodes[3*iQuad] );
+  if ( myCurFace.myNbNodes >3*iQuad ) {
+    XYZ p4 ( myCurFace.myNodes[3*iQuad] );
     XYZ aVec14( p4 - p1 );
     XYZ cross2 = aVec13.Crossed( aVec14 );
     cross = cross + cross2;
@@ -1054,11 +1218,11 @@ bool SMDS_VolumeTool::GetFaceBaryCenter (int faceIndex, double & X, double & Y,
     return false;
 
   X = Y = Z = 0.0;
-  for ( int i = 0; i < myFaceNbNodes; ++i )
+  for ( int i = 0; i < myCurFace.myNbNodes; ++i )
   {
-    X += myFaceNodes[i]->X() / myFaceNbNodes;
-    Y += myFaceNodes[i]->Y() / myFaceNbNodes;
-    Z += myFaceNodes[i]->Z() / myFaceNbNodes;
+    X += myCurFace.myNodes[i]->X() / myCurFace.myNbNodes;
+    Y += myCurFace.myNodes[i]->Y() / myCurFace.myNbNodes;
+    Z += myCurFace.myNodes[i]->Z() / myCurFace.myNbNodes;
   }
   return true;
 }
@@ -1070,27 +1234,36 @@ bool SMDS_VolumeTool::GetFaceBaryCenter (int faceIndex, double & X, double & Y,
 
 double SMDS_VolumeTool::GetFaceArea( int faceIndex ) const
 {
-  if (myVolume->IsPoly()) {
-    MESSAGE("Warning: attempt to obtain area of a face of polyhedral volume");
-    return 0;
-  }
-
+  double area = 0;
   if ( !setFace( faceIndex ))
-    return 0;
+    return area;
 
-  XYZ p1 ( myFaceNodes[0] );
-  XYZ p2 ( myFaceNodes[1] );
-  XYZ p3 ( myFaceNodes[2] );
+  XYZ p1 ( myCurFace.myNodes[0] );
+  XYZ p2 ( myCurFace.myNodes[1] );
+  XYZ p3 ( myCurFace.myNodes[2] );
   XYZ aVec12( p2 - p1 );
   XYZ aVec13( p3 - p1 );
-  double area = aVec12.Crossed( aVec13 ).Magnitude() * 0.5;
+  area += aVec12.Crossed( aVec13 ).Magnitude();
 
-  if ( myFaceNbNodes == 4 ) {
-    XYZ p4 ( myFaceNodes[3] );
-    XYZ aVec14( p4 - p1 );
-    area += aVec14.Crossed( aVec13 ).Magnitude() * 0.5;
+  if (myVolume->IsPoly())
+  {
+    for ( int i = 3; i < myCurFace.myNbNodes; ++i )
+    {
+      XYZ pI ( myCurFace.myNodes[i] );
+      XYZ aVecI( pI - p1 );
+      area += aVec13.Crossed( aVecI ).Magnitude();
+      aVec13 = aVecI;
+    }
   }
-  return area;
+  else
+  {
+    if ( myCurFace.myNbNodes == 4 ) {
+      XYZ p4 ( myCurFace.myNodes[3] );
+      XYZ aVec14( p4 - p1 );
+      area += aVec14.Crossed( aVec13 ).Magnitude();
+    }
+  }
+  return area / 2;
 }
 
 //================================================================================
@@ -1101,7 +1274,7 @@ double SMDS_VolumeTool::GetFaceArea( int faceIndex ) const
 
 int SMDS_VolumeTool::GetCenterNodeIndex( int faceIndex ) const
 {
-  if ( myAllFacesNbNodes && myVolumeNbNodes == 27 ) // classic element with 27 nodes
+  if ( myAllFacesNbNodes && myVolumeNodes.size() == 27 ) // classic element with 27 nodes
   {
     switch ( faceIndex ) {
     case 0: return 20;
@@ -1129,7 +1302,7 @@ int SMDS_VolumeTool::GetOppFaceIndex( int faceIndex ) const
   const int nbHoriFaces = 2;
 
   if ( faceIndex >= 0 && faceIndex < NbFaces() ) {
-    switch ( myVolumeNbNodes ) {
+    switch ( myVolumeNodes.size() ) {
     case 6:
     case 15:
       if ( faceIndex == 0 || faceIndex == 1 )
@@ -1182,31 +1355,42 @@ bool SMDS_VolumeTool::IsLinked (const SMDS_MeshNode* theNode1,
       MESSAGE("Warning: bad volumic element");
       return false;
     }
-    bool isLinked = false;
-    int iface;
-    for (iface = 1; iface <= myNbFaces && !isLinked; iface++) {
-      int inode, nbFaceNodes = myPolyedre->NbFaceNodes(iface);
-
-      for (inode = 1; inode <= nbFaceNodes && !isLinked; inode++) {
-        const SMDS_MeshNode* curNode = myPolyedre->GetFaceNode(iface, inode);
-
-        if (curNode == theNode1 || curNode == theNode2) {
-          int inextnode = (inode == nbFaceNodes) ? 1 : inode + 1;
-          const SMDS_MeshNode* nextNode = myPolyedre->GetFaceNode(iface, inextnode);
-
-          if ((curNode == theNode1 && nextNode == theNode2) ||
-              (curNode == theNode2 && nextNode == theNode1)) {
-            isLinked = true;
-          }
-        }
+    if ( !myAllFacesNbNodes ) {
+      SMDS_VolumeTool*  me = const_cast< SMDS_VolumeTool* >( this );
+      me->myPolyQuantities = myPolyedre->GetQuantities();
+      myAllFacesNbNodes    = &myPolyQuantities[0];
+    }
+    int from, to = 0, d1 = 1, d2 = 2;
+    if ( myPolyedre->IsQuadratic() ) {
+      if ( theIgnoreMediumNodes ) {
+        d1 = 2; d2 = 0;
       }
+    } else {
+      d2 = 0;
     }
-    return isLinked;
+    vector<const SMDS_MeshNode*>::const_iterator i;
+    for (int iface = 0; iface < myNbFaces; iface++)
+    {
+      from = to;
+      to  += myPolyQuantities[iface];
+      i    = std::find( myVolumeNodes.begin() + from, myVolumeNodes.begin() + to, theNode1 );
+      if ( i != myVolumeNodes.end() )
+      {
+        if ((  theNode2 == *( i-d1 ) ||
+               theNode2 == *( i+d1 )))
+          return true;
+        if (( d2 ) &&
+            (( theNode2 == *( i-d2 ) ||
+               theNode2 == *( i+d2 ))))
+          return true;
+      }
+    }
+    return false;
   }
 
   // find nodes indices
   int i1 = -1, i2 = -1, nbFound = 0;
-  for ( int i = 0; i < myVolumeNbNodes && nbFound < 2; i++ )
+  for ( int i = 0; i < myVolumeNodes.size() && nbFound < 2; i++ )
   {
     if ( myVolumeNodes[ i ] == theNode1 )
       i1 = i, ++nbFound;
@@ -1234,7 +1418,7 @@ bool SMDS_VolumeTool::IsLinked (const int theNode1Index,
   int minInd = min( theNode1Index, theNode2Index );
   int maxInd = max( theNode1Index, theNode2Index );
 
-  if ( minInd < 0 || maxInd > myVolumeNbNodes - 1 || maxInd == minInd )
+  if ( minInd < 0 || maxInd > myVolumeNodes.size() - 1 || maxInd == minInd )
     return false;
 
   VolumeType type = GetVolumeType();
@@ -1351,7 +1535,7 @@ bool SMDS_VolumeTool::IsLinked (const int theNode1Index,
 int SMDS_VolumeTool::GetNodeIndex(const SMDS_MeshNode* theNode) const
 {
   if ( myVolume ) {
-    for ( int i = 0; i < myVolumeNbNodes; i++ ) {
+    for ( int i = 0; i < myVolumeNodes.size(); i++ ) {
       if ( myVolumeNodes[ i ] == theNode )
         return i;
     }
@@ -1370,24 +1554,32 @@ int SMDS_VolumeTool::GetNodeIndex(const SMDS_MeshNode* theNode) const
 int SMDS_VolumeTool::GetAllExistingFaces(vector<const SMDS_MeshElement*> & faces) const
 {
   faces.clear();
-  for ( int iF = 0; iF < NbFaces(); ++iF ) {
-    const SMDS_MeshFace* face = 0;
-    const SMDS_MeshNode** nodes = GetFaceNodes( iF );
-    switch ( NbFaceNodes( iF )) {
-    case 3:
-      face = SMDS_Mesh::FindFace( nodes[0], nodes[1], nodes[2] ); break;
-    case 4:
-      face = SMDS_Mesh::FindFace( nodes[0], nodes[1], nodes[2], nodes[3] ); break;
-    case 6:
-      face = SMDS_Mesh::FindFace( nodes[0], nodes[1], nodes[2],
-                                  nodes[3], nodes[4], nodes[5]); break;
-    case 8:
-      face = SMDS_Mesh::FindFace( nodes[0], nodes[1], nodes[2], nodes[3],
-                                  nodes[4], nodes[5], nodes[6], nodes[7]); break;
+  SaveFacet savedFacet( myCurFace );
+  if ( IsPoly() )
+    for ( int iF = 0; iF < NbFaces(); ++iF ) {
+      if ( setFace( iF ))
+        if ( const SMDS_MeshElement* face = SMDS_Mesh::FindFace( myCurFace.myNodes ))
+          faces.push_back( face );
+    }
+  else
+    for ( int iF = 0; iF < NbFaces(); ++iF ) {
+      const SMDS_MeshFace* face = 0;
+      const SMDS_MeshNode** nodes = GetFaceNodes( iF );
+      switch ( NbFaceNodes( iF )) {
+      case 3:
+        face = SMDS_Mesh::FindFace( nodes[0], nodes[1], nodes[2] ); break;
+      case 4:
+        face = SMDS_Mesh::FindFace( nodes[0], nodes[1], nodes[2], nodes[3] ); break;
+      case 6:
+        face = SMDS_Mesh::FindFace( nodes[0], nodes[1], nodes[2],
+                                    nodes[3], nodes[4], nodes[5]); break;
+      case 8:
+        face = SMDS_Mesh::FindFace( nodes[0], nodes[1], nodes[2], nodes[3],
+                                    nodes[4], nodes[5], nodes[6], nodes[7]); break;
+      }
+      if ( face )
+        faces.push_back( face );
     }
-    if ( face )
-      faces.push_back( face );
-  }
   return faces.size();
 }
 
@@ -1395,17 +1587,17 @@ int SMDS_VolumeTool::GetAllExistingFaces(vector<const SMDS_MeshElement*> & faces
 //================================================================================
 /*!
  * \brief Fill vector with boundary edges existing in the mesh
 * \param edges - vector of found edges
 * \retval int - nb of found faces
+ * \param edges - vector of found edges
+ * \retval int - nb of found faces
  */
 //================================================================================
 
 int SMDS_VolumeTool::GetAllExistingEdges(vector<const SMDS_MeshElement*> & edges) const
 {
   edges.clear();
-  edges.reserve( myVolumeNbNodes * 2 );
-  for ( int i = 0; i < myVolumeNbNodes-1; ++i ) {
-    for ( int j = i + 1; j < myVolumeNbNodes; ++j ) {
+  edges.reserve( myVolumeNodes.size() * 2 );
+  for ( int i = 0; i < myVolumeNodes.size()-1; ++i ) {
+    for ( int j = i + 1; j < myVolumeNodes.size(); ++j ) {
       if ( IsLinked( i, j )) {
         const SMDS_MeshElement* edge =
           SMDS_Mesh::FindEdge( myVolumeNodes[i], myVolumeNodes[j] );
@@ -1428,30 +1620,20 @@ double SMDS_VolumeTool::MinLinearSize2() const
   double minSize = 1e+100;
   int iQ = myVolume->IsQuadratic() ? 2 : 1;
 
-  // store current face data
-  int curFace = myCurFace, nbN = myFaceNbNodes;
-  int* ind = myFaceNodeIndices;
-  myFaceNodeIndices = NULL;
-  const SMDS_MeshNode** nodes = myFaceNodes;
-  myFaceNodes = NULL;
-  
+  SaveFacet savedFacet( myCurFace );
+
   // it seems that compute distance twice is faster than organization of a sole computing
-  myCurFace = -1;
+  myCurFace.myIndex = -1;
   for ( int iF = 0; iF < myNbFaces; ++iF )
   {
     setFace( iF );
-    for ( int iN = 0; iN < myFaceNbNodes; iN += iQ )
+    for ( int iN = 0; iN < myCurFace.myNbNodes; iN += iQ )
     {
-      XYZ n1( myFaceNodes[ iN ]);
-      XYZ n2( myFaceNodes[(iN + iQ) % myFaceNbNodes]);
+      XYZ n1( myCurFace.myNodes[ iN ]);
+      XYZ n2( myCurFace.myNodes[(iN + iQ) % myCurFace.myNbNodes]);
       minSize = std::min( minSize, (n1 - n2).SquareMagnitude());
     }
   }
-  // restore current face data
-  myCurFace = curFace;
-  myFaceNbNodes = nbN;
-  myFaceNodeIndices = ind;
-  delete [] myFaceNodes; myFaceNodes = nodes;
 
   return minSize;
 }
@@ -1467,30 +1649,20 @@ double SMDS_VolumeTool::MaxLinearSize2() const
   double maxSize = -1e+100;
   int iQ = myVolume->IsQuadratic() ? 2 : 1;
 
-  // store current face data
-  int curFace = myCurFace, nbN = myFaceNbNodes;
-  int* ind = myFaceNodeIndices;
-  myFaceNodeIndices = NULL;
-  const SMDS_MeshNode** nodes = myFaceNodes;
-  myFaceNodes = NULL;
+  SaveFacet savedFacet( myCurFace );
   
   // it seems that compute distance twice is faster than organization of a sole computing
-  myCurFace = -1;
+  myCurFace.myIndex = -1;
   for ( int iF = 0; iF < myNbFaces; ++iF )
   {
     setFace( iF );
-    for ( int iN = 0; iN < myFaceNbNodes; iN += iQ )
+    for ( int iN = 0; iN < myCurFace.myNbNodes; iN += iQ )
     {
-      XYZ n1( myFaceNodes[ iN ]);
-      XYZ n2( myFaceNodes[(iN + iQ) % myFaceNbNodes]);
+      XYZ n1( myCurFace.myNodes[ iN ]);
+      XYZ n2( myCurFace.myNodes[(iN + iQ) % myCurFace.myNbNodes]);
       maxSize = std::max( maxSize, (n1 - n2).SquareMagnitude());
     }
   }
-  // restore current face data
-  myCurFace         = curFace;
-  myFaceNbNodes     = nbN;
-  myFaceNodeIndices = ind;
-  delete [] myFaceNodes; myFaceNodes = nodes;
 
   return maxSize;
 }
@@ -1506,13 +1678,13 @@ bool SMDS_VolumeTool::IsFreeFace( int faceIndex, const SMDS_MeshElement** otherV
 {
   const bool isFree = true;
 
-  if (!setFace( faceIndex ))
+  if ( !setFace( faceIndex ))
     return !isFree;
 
   const SMDS_MeshNode** nodes = GetFaceNodes( faceIndex );
 
   const int  di = myVolume->IsQuadratic() ? 2 : 1;
-  const int nbN = ( myFaceNbNodes/di <= 4 && !IsPoly()) ? 3 : myFaceNbNodes/di; // nb nodes to check
+  const int nbN = ( myCurFace.myNbNodes/di <= 4 && !IsPoly()) ? 3 : myCurFace.myNbNodes/di; // nb nodes to check
 
   SMDS_ElemIteratorPtr eIt = nodes[0]->GetInverseElementIterator( SMDSAbs_Volume );
   while ( eIt->more() )
@@ -1528,7 +1700,7 @@ bool SMDS_VolumeTool::IsFreeFace( int faceIndex, const SMDS_MeshElement** otherV
     {
       // if ( vol->IsPoly() || vol->NbFaces() > 6 ) // vol is polyhed or hex prism 
       // {
-      //   int nb = myFaceNbNodes;
+      //   int nb = myCurFace.myNbNodes;
       //   if ( myVolume->GetEntityType() != vol->GetEntityType() )
       //     nb -= ( GetCenterNodeIndex(0) > 0 );
       //   set<const SMDS_MeshNode*> faceNodes( nodes, nodes + nb );
@@ -1557,7 +1729,7 @@ bool SMDS_VolumeTool::IsFreeFaceAdv( int faceIndex, const SMDS_MeshElement** oth
     return !isFree;
 
   const SMDS_MeshNode** nodes = GetFaceNodes( faceIndex );
-  const int nbFaceNodes = myFaceNbNodes;
+  const int nbFaceNodes = myCurFace.myNbNodes;
 
   // evaluate nb of face nodes shared by other volumes
   int maxNbShared = -1;
@@ -1736,19 +1908,14 @@ bool SMDS_VolumeTool::setFace( int faceIndex ) const
   if ( !myVolume )
     return false;
 
-  if ( myCurFace == faceIndex )
+  if ( myCurFace.myIndex == faceIndex )
     return true;
 
-  myCurFace = -1;
+  myCurFace.myIndex = -1;
 
   if ( faceIndex < 0 || faceIndex >= NbFaces() )
     return false;
 
-  if (myFaceNodes != NULL) {
-    delete [] myFaceNodes;
-    myFaceNodes = NULL;
-  }
-
   if (myVolume->IsPoly())
   {
     if (!myPolyedre) {
@@ -1757,21 +1924,31 @@ bool SMDS_VolumeTool::setFace( int faceIndex ) const
     }
 
     // set face nodes
-    int iNode;
-    myFaceNbNodes = myPolyedre->NbFaceNodes(faceIndex + 1);
-    myFaceNodes = new const SMDS_MeshNode* [myFaceNbNodes + 1];
-    for ( iNode = 0; iNode < myFaceNbNodes; iNode++ )
-      myFaceNodes[ iNode ] = myPolyedre->GetFaceNode(faceIndex + 1, iNode + 1);
-    myFaceNodes[ myFaceNbNodes ] = myFaceNodes[ 0 ]; // last = first
+    SMDS_VolumeTool* me = const_cast< SMDS_VolumeTool* >( this );
+    if ( !myAllFacesNbNodes ) {
+      me->myPolyQuantities = myPolyedre->GetQuantities();
+      myAllFacesNbNodes    = &myPolyQuantities[0];
+    }
+    myCurFace.myNbNodes = myAllFacesNbNodes[ faceIndex ];
+    myCurFace.myNodes.resize( myCurFace.myNbNodes + 1 );
+    me->myPolyIndices.resize( myCurFace.myNbNodes + 1 );
+    myCurFace.myNodeIndices = & me->myPolyIndices[0];
+    int shift = std::accumulate( myAllFacesNbNodes, myAllFacesNbNodes+faceIndex, 0 );
+    for ( int iNode = 0; iNode < myCurFace.myNbNodes; iNode++ )
+    {
+      myCurFace.myNodes      [ iNode ] = myVolumeNodes[ shift + iNode ];
+      myCurFace.myNodeIndices[ iNode ] = shift + iNode;
+    }
+    myCurFace.myNodes      [ myCurFace.myNbNodes ] = myCurFace.myNodes[ 0 ]; // last = first
+    myCurFace.myNodeIndices[ myCurFace.myNbNodes ] = myCurFace.myNodeIndices[ 0 ];
 
     // check orientation
     if (myExternalFaces)
     {
-      myCurFace = faceIndex; // avoid infinite recursion in IsFaceExternal()
+      myCurFace.myIndex = faceIndex; // avoid infinite recursion in IsFaceExternal()
       myExternalFaces = false; // force normal computation by IsFaceExternal()
       if ( !IsFaceExternal( faceIndex ))
-        for ( int i = 0, j = myFaceNbNodes; i < j; ++i, --j )
-          std::swap( myFaceNodes[i], myFaceNodes[j] );
+        std::reverse( myCurFace.myNodes.begin(), myCurFace.myNodes.end() );
       myExternalFaces = true;
     }
   }
@@ -1780,7 +1957,7 @@ bool SMDS_VolumeTool::setFace( int faceIndex ) const
     if ( !myAllFacesNodeIndices_F )
     {
       // choose data for an element type
-      switch ( myVolumeNbNodes ) {
+      switch ( myVolumeNodes.size() ) {
       case 4:
         myAllFacesNodeIndices_F  = &Tetra_F [0][0];
         //myAllFacesNodeIndices_FE = &Tetra_F [0][0];
@@ -1837,7 +2014,7 @@ bool SMDS_VolumeTool::setFace( int faceIndex ) const
         myAllFacesNodeIndices_RE = &QuadHexa_RE[0][0];
         myAllFacesNbNodes        = QuadHexa_nbN;
         myMaxFaceNbNodes         = sizeof(QuadHexa_F[0])/sizeof(QuadHexa_F[0][0]);
-        if ( !myIgnoreCentralNodes && myVolumeNbNodes == 27 )
+        if ( !myIgnoreCentralNodes && myVolumeNodes.size() == 27 )
         {
           myAllFacesNodeIndices_F  = &TriQuadHexa_F [0][0];
           //myAllFacesNodeIndices_FE = &TriQuadHexa_FE[0][0];
@@ -1857,21 +2034,21 @@ bool SMDS_VolumeTool::setFace( int faceIndex ) const
         return false;
       }
     }
-    myFaceNbNodes = myAllFacesNbNodes[ faceIndex ];
+    myCurFace.myNbNodes = myAllFacesNbNodes[ faceIndex ];
     // if ( myExternalFaces )
-    //   myFaceNodeIndices = (int*)( myVolForward ? myAllFacesNodeIndices_FE + faceIndex*myMaxFaceNbNodes : myAllFacesNodeIndices_RE + faceIndex*myMaxFaceNbNodes );
+    //   myCurFace.myNodeIndices = (int*)( myVolForward ? myAllFacesNodeIndices_FE + faceIndex*myMaxFaceNbNodes : myAllFacesNodeIndices_RE + faceIndex*myMaxFaceNbNodes );
     // else
-    //   myFaceNodeIndices = (int*)( myAllFacesNodeIndices_F + faceIndex*myMaxFaceNbNodes );
-    myFaceNodeIndices = (int*)( myVolForward ? myAllFacesNodeIndices_F + faceIndex*myMaxFaceNbNodes : myAllFacesNodeIndices_RE + faceIndex*myMaxFaceNbNodes );
+    //   myCurFace.myNodeIndices = (int*)( myAllFacesNodeIndices_F + faceIndex*myMaxFaceNbNodes );
+    myCurFace.myNodeIndices = (int*)( myVolForward ? myAllFacesNodeIndices_F + faceIndex*myMaxFaceNbNodes : myAllFacesNodeIndices_RE + faceIndex*myMaxFaceNbNodes );
 
     // set face nodes
-    myFaceNodes = new const SMDS_MeshNode* [myFaceNbNodes + 1];
-    for ( int iNode = 0; iNode < myFaceNbNodes; iNode++ )
-      myFaceNodes[ iNode ] = myVolumeNodes[ myFaceNodeIndices[ iNode ]];
-    myFaceNodes[ myFaceNbNodes ] = myFaceNodes[ 0 ];
+    myCurFace.myNodes.resize( myCurFace.myNbNodes + 1 );
+    for ( int iNode = 0; iNode < myCurFace.myNbNodes; iNode++ )
+      myCurFace.myNodes[ iNode ] = myVolumeNodes[ myCurFace.myNodeIndices[ iNode ]];
+    myCurFace.myNodes[ myCurFace.myNbNodes ] = myCurFace.myNodes[ 0 ];
   }
 
-  myCurFace = faceIndex;
+  myCurFace.myIndex = faceIndex;
 
   return true;
 }