Salome HOME
Fix crash at study closing. Enable canceling Prism 3D computation
[modules/smesh.git] / src / StdMeshers / StdMeshers_Prism_3D.cxx
index 6584b16652576dabb3e1e993186a55e764e716dc..afac9bebe4ec2cc0f4dad6359f90c5ccb5a9aa2f 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
 #include <gp_Ax2.hxx>
 #include <gp_Ax3.hxx>
 
+#include <limits>
+
 using namespace std;
 
 #define RETURN_BAD_RESULT(msg) { MESSAGE(")-: Error: " << msg); return false; }
-#define gpXYZ(n) gp_XYZ(n->X(),n->Y(),n->Z())
-#define SHOWYXZ(msg, xyz) // {\
-// gp_Pnt p (xyz); \
-// cout << msg << " ("<< p.X() << "; " <<p.Y() << "; " <<p.Z() << ") " <<endl;\
-// }
+#define gpXYZ(n) SMESH_TNodeXYZ(n)
+
+#ifdef _DEBUG_
+#define DBGOUT(msg) //cout << msg << endl;
+#define SHOWYXZ(msg, xyz)                                               \
+  // { gp_Pnt p (xyz);                                                     \
+  //   cout << msg << " ("<< p.X() << "; " <<p.Y() << "; " <<p.Z() << ") " <<endl; }
+#else
+#define DBGOUT(msg)
+#define SHOWYXZ(msg, xyz)
+#endif
 
 namespace TAssocTool = StdMeshers_ProjectionUtils;
 
@@ -100,7 +108,7 @@ namespace {
            algo->myProxyMesh->GetMesh() != helper->GetMesh() )
         algo->myProxyMesh.reset( new SMESH_ProxyMesh( *helper->GetMesh() ));
 
-      algo->myQuadStruct.reset();
+      algo->myQuadList.clear();
 
       if ( helper )
         algo->_quadraticMesh = helper->GetIsQuadratic();
@@ -150,6 +158,46 @@ namespace {
       return algo;
     }
   };
+  //=======================================================================
+  /*!
+   * \brief Returns already computed EDGEs
+   */
+  void getPrecomputedEdges( SMESH_MesherHelper&    theHelper,
+                            const TopoDS_Shape&    theShape,
+                            vector< TopoDS_Edge >& theEdges)
+  {
+    theEdges.clear();
+
+    SMESHDS_Mesh* meshDS = theHelper.GetMeshDS();
+    SMESHDS_SubMesh* sm;
+
+    TopTools_IndexedMapOfShape edges;
+    TopExp::MapShapes( theShape, TopAbs_EDGE, edges );
+    for ( int iE = 1; iE <= edges.Extent(); ++iE )
+    {
+      const TopoDS_Shape edge = edges( iE );
+      if (( ! ( sm = meshDS->MeshElements( edge ))) ||
+          ( sm->NbElements() == 0 ))
+        continue;
+
+      // there must not be FACEs meshed with triangles and sharing a computed EDGE
+      // as the precomputed EDGEs are used for propagation other to 'vertical' EDGEs
+      bool faceFound = false;
+      PShapeIteratorPtr faceIt =
+        theHelper.GetAncestors( edge, *theHelper.GetMesh(), TopAbs_FACE );
+      while ( const TopoDS_Shape* face = faceIt->next() )
+
+        if (( sm = meshDS->MeshElements( *face )) &&
+            ( sm->NbElements() > 0 ) &&
+            ( !theHelper.IsSameElemGeometry( sm, SMDSGeom_QUADRANGLE ) ))
+        {
+          faceFound = true;
+          break;
+        }
+      if ( !faceFound )
+        theEdges.push_back( TopoDS::Edge( edge ));
+    }
+  }
 
   //================================================================================
   /*!
@@ -159,20 +207,21 @@ namespace {
   //================================================================================
 
   bool setBottomEdge( const TopoDS_Edge&   botE,
-                      faceQuadStruct::Ptr& quad,
+                      FaceQuadStruct::Ptr& quad,
                       const TopoDS_Shape&  face)
   {
-    quad->side[ QUAD_TOP_SIDE  ]->Reverse();
-    quad->side[ QUAD_LEFT_SIDE ]->Reverse();
+    quad->side[ QUAD_TOP_SIDE  ].grid->Reverse();
+    quad->side[ QUAD_LEFT_SIDE ].grid->Reverse();
     int edgeIndex = 0;
+    bool isComposite = false;
     for ( size_t i = 0; i < quad->side.size(); ++i )
     {
-      StdMeshers_FaceSide* quadSide = quad->side[i];
+      StdMeshers_FaceSidePtr quadSide = quad->side[i];
       for ( int iE = 0; iE < quadSide->NbEdges(); ++iE )
         if ( botE.IsSame( quadSide->Edge( iE )))
         {
           if ( quadSide->NbEdges() > 1 )
-            return false;
+            isComposite = true; //return false;
           edgeIndex = i;
           i = quad->side.size(); // to quit from the outer loop
           break;
@@ -183,7 +232,7 @@ namespace {
 
     quad->face = TopoDS::Face( face );
 
-    return true;
+    return !isComposite;
   }
 
   //================================================================================
@@ -366,6 +415,118 @@ namespace {
     return nbRemoved;
   }
 
+  //================================================================================
+  /*!
+   * \brief Return and angle between two EDGEs
+   *  \return double - the angle normalized so that
+   * >~ 0  -> 2.0
+   *  PI/2 -> 1.0
+   *  PI   -> 0.0
+   * -PI/2 -> -1.0
+   * <~ 0  -> -2.0
+   */
+  //================================================================================
+
+  // double normAngle(const TopoDS_Edge & E1, const TopoDS_Edge & E2, const TopoDS_Face & F)
+  // {
+  //   return SMESH_MesherHelper::GetAngle( E1, E2, F ) / ( 0.5 * M_PI );
+  // }
+
+  //================================================================================
+  /*!
+   * Consider continuous straight EDGES as one side - mark them to unite
+   */
+  //================================================================================
+
+  int countNbSides( const Prism_3D::TPrismTopo & thePrism,
+                    vector<int> &                nbUnitePerEdge,
+                    vector< double > &           edgeLength)
+  {
+    int nbEdges = thePrism.myNbEdgesInWires.front();  // nb outer edges
+    int nbSides = nbEdges;
+
+    
+    list< TopoDS_Edge >::const_iterator edgeIt = thePrism.myBottomEdges.begin();
+    std::advance( edgeIt, nbEdges-1 );
+    TopoDS_Edge   prevE = *edgeIt;
+    // bool isPrevStraight = SMESH_Algo::IsStraight( prevE );
+    int           iPrev = nbEdges - 1;
+
+    int iUnite = -1; // the first of united EDGEs
+
+    // analyse angles between EDGEs
+    int nbCorners = 0;
+    vector< bool > isCorner( nbEdges );
+    edgeIt = thePrism.myBottomEdges.begin();
+    for ( int iE = 0; iE < nbEdges; ++iE, ++edgeIt )
+    {
+      const TopoDS_Edge&  curE = *edgeIt;
+      edgeLength[ iE ] = SMESH_Algo::EdgeLength( curE );
+
+      // double normAngle = normAngle( prevE, curE, thePrism.myBottom );
+      // isCorner[ iE ] = false;
+      // if ( normAngle < 2.0 )
+      // {
+      //   if ( normAngle < 0.001 ) // straight or obtuse angle
+      //   {
+      //     // unite EDGEs in order not to put a corner of the unit quadrangle at this VERTEX
+      //     if ( iUnite < 0 )
+      //       iUnite = iPrev;
+      //     nbUnitePerEdge[ iUnite ]++;
+      //     nbUnitePerEdge[ iE ] = -1;
+      //     --nbSides;
+      //   }
+      //   else
+      //   {
+      //     isCorner[ iE ] = true;
+      //     nbCorners++;
+      //     iUnite = -1;
+      //   }
+      // }
+      // prevE = curE;
+    }
+
+    if ( nbCorners > 4 )
+    {
+      // define which of corners to put on a side of the unit quadrangle
+    }
+    // edgeIt = thePrism.myBottomEdges.begin();
+    // for ( int iE = 0; iE < nbEdges; ++iE, ++edgeIt )
+    // {
+    //   const TopoDS_Edge&  curE = *edgeIt;
+    //   edgeLength[ iE ] = SMESH_Algo::EdgeLength( curE );
+
+    //   const bool isCurStraight = SMESH_Algo::IsStraight( curE );
+    //   if ( isPrevStraight && isCurStraight && SMESH_Algo::IsContinuous( prevE, curE ))
+    //   {
+    //     if ( iUnite < 0 )
+    //       iUnite = iPrev;
+    //     nbUnitePerEdge[ iUnite ]++;
+    //     nbUnitePerEdge[ iE ] = -1;
+    //     --nbSides;
+    //   }
+    //   else
+    //   {
+    //     iUnite = -1;
+    //   }
+    //   prevE          = curE;
+    //   isPrevStraight = isCurStraight;
+    //   iPrev = iE;
+    // }
+    
+    return nbSides;
+  }
+
+  void pointsToPython(const std::vector<gp_XYZ>& p)
+  {
+#ifdef _DEBUG_
+    for ( int i = SMESH_Block::ID_V000; i < p.size(); ++i )
+    {
+      cout << "mesh.AddNode( " << p[i].X() << ", "<< p[i].Y() << ", "<< p[i].Z() << ") # " << i <<" " ;
+      SMESH_Block::DumpShapeID( i, cout ) << endl;
+    }
+#endif
+  }
 } // namespace
 
 //=======================================================================
@@ -464,7 +625,8 @@ bool StdMeshers_Prism_3D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theSh
   // look for meshed FACEs ("source" FACEs) that must be prism bottoms
   list< TopoDS_Face > meshedFaces, notQuadMeshedFaces, notQuadFaces;
   const bool meshHasQuads = ( theMesh.NbQuadrangles() > 0 );
-  for ( int iF = 1; iF < faceToSolids.Extent(); ++iF )
+  //StdMeshers_Quadrangle_2D* quadAlgo = TQuadrangleAlgo::instance( this );
+  for ( int iF = 1; iF <= faceToSolids.Extent(); ++iF )
   {
     const TopoDS_Face& face = TopoDS::Face( faceToSolids.FindKey( iF ));
     SMESH_subMesh*   faceSM = theMesh.GetSubMesh( face );
@@ -480,10 +642,13 @@ bool StdMeshers_Prism_3D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theSh
       else
         meshedFaces.push_back( face );
     }
-    else if ( myHelper->Count( face, TopAbs_EDGE, /*ignoreSame=*/false ) != 4 )
-    {
-      notQuadFaces.push_back( face );
-    }
+    // not add not quadrilateral FACE as we can't compute it
+    // else if ( !quadAlgo->CheckNbEdges( theMesh, face ))
+    // // not add not quadrilateral FACE as it can be a prism side
+    // // else if ( myHelper->Count( face, TopAbs_EDGE, /*ignoreSame=*/false ) != 4 )
+    // {
+    //   notQuadFaces.push_back( face );
+    // }
   }
   // notQuadFaces are of medium priority, put them before ordinary meshed faces
   meshedFaces.splice( meshedFaces.begin(), notQuadFaces );
@@ -491,6 +656,7 @@ bool StdMeshers_Prism_3D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theSh
   meshedFaces.splice( meshedFaces.begin(), notQuadMeshedFaces );
 
   Prism_3D::TPrismTopo prism;
+  myPropagChains = 0;
 
   if ( nbSolids == 1 )
   {
@@ -500,6 +666,21 @@ bool StdMeshers_Prism_3D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theSh
              compute( prism ));
   }
 
+  // find propagation chains from already computed EDGEs
+  vector< TopoDS_Edge > computedEdges;
+  getPrecomputedEdges( helper, theShape, computedEdges );
+  myPropagChains = new TopTools_IndexedMapOfShape[ computedEdges.size() + 1 ];
+  SMESHUtils::ArrayDeleter< TopTools_IndexedMapOfShape > pcDel( myPropagChains );
+  for ( size_t i = 0, nb = 0; i < computedEdges.size(); ++i )
+  {
+    StdMeshers_ProjectionUtils::GetPropagationEdge( &theMesh, TopoDS_Edge(),
+                                                    computedEdges[i], myPropagChains + nb );
+    if ( myPropagChains[ nb ].Extent() < 2 ) // an empty map is a termination sign
+      myPropagChains[ nb ].Clear();
+    else
+      nb++;
+  }
+
   TopTools_MapOfShape meshedSolids;
   list< Prism_3D::TPrismTopo > meshedPrism;
   TopTools_ListIteratorOfListOfShape solidIt;
@@ -527,7 +708,11 @@ bool StdMeshers_Prism_3D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theSh
                !compute( prism ))
             return false;
 
-          meshedFaces.push_front( prism.myTop );
+          SMESHDS_SubMesh* smDS = theMesh.GetMeshDS()->MeshElements( prism.myTop );
+          if ( !myHelper->IsSameElemGeometry( smDS, SMDSGeom_QUADRANGLE ))
+          {
+            meshedFaces.push_front( prism.myTop );
+          }
           meshedPrism.push_back( prism );
         }
       }
@@ -558,7 +743,7 @@ bool StdMeshers_Prism_3D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theSh
               continue; // already computed prism
             }
             // find a source FACE of the SOLID: it's a FACE sharing a bottom EDGE with wFace
-            const TopoDS_Edge& wEdge = (*wQuad)->side[ QUAD_TOP_SIDE ]->Edge(0);
+            const TopoDS_Edge& wEdge = (*wQuad)->side[ QUAD_TOP_SIDE ].grid->Edge(0);
             PShapeIteratorPtr faceIt = myHelper->GetAncestors( wEdge, *myHelper->GetMesh(),
                                                                TopAbs_FACE);
             while ( const TopoDS_Shape* f = faceIt->next() )
@@ -568,6 +753,7 @@ bool StdMeshers_Prism_3D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theSh
               prism.myBottom  = candidateF;
               mySetErrorToSM = false;
               if ( !myHelper->IsSubShape( candidateF, prismIt->myShape3D ) &&
+                   myHelper->IsSubShape( candidateF, solid ) &&
                    !myHelper->GetMesh()->GetSubMesh( candidateF )->IsMeshComputed() &&
                    initPrism( prism, solid ) &&
                    project2dMesh( prismIt->myBottom, candidateF))
@@ -575,8 +761,12 @@ bool StdMeshers_Prism_3D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theSh
                 mySetErrorToSM = true;
                 if ( !compute( prism ))
                   return false;
-                meshedFaces.push_front( prism.myTop );
-                meshedFaces.push_front( prism.myBottom );
+                SMESHDS_SubMesh* smDS = theMesh.GetMeshDS()->MeshElements( prism.myTop );
+                if ( !myHelper->IsSameElemGeometry( smDS, SMDSGeom_QUADRANGLE ))
+                {
+                  meshedFaces.push_front( prism.myTop );
+                  meshedFaces.push_front( prism.myBottom );
+                }
                 meshedPrism.push_back( prism );
                 meshedSolids.Add( solid );
               }
@@ -696,10 +886,11 @@ bool StdMeshers_Prism_3D::getWallFaces( Prism_3D::TPrismTopo & thePrism,
   list< TopoDS_Edge >::iterator edge = thePrism.myBottomEdges.begin();
   std::list< int >::iterator     nbE = thePrism.myNbEdgesInWires.begin();
   int iE = 0;
+  double f,l;
   while ( edge != thePrism.myBottomEdges.end() )
   {
     ++iE;
-    if ( BRep_Tool::Degenerated( *edge ))
+    if ( BRep_Tool::Curve( *edge, f,l ).IsNull() )
     {
       edge = thePrism.myBottomEdges.erase( edge );
       --iE;
@@ -717,10 +908,17 @@ bool StdMeshers_Prism_3D::getWallFaces( Prism_3D::TPrismTopo & thePrism,
           if ( !quadList.back() )
             return toSM( error(TCom("Side face #") << shapeID( face )
                                << " not meshable with quadrangles"));
-          if ( ! setBottomEdge( *edge, quadList.back(), face ))
-            return toSM( error(TCom("Composite 'horizontal' edges are not supported")));
-          thePrism.myWallQuads.push_back( quadList );
-          faceMap.Add( face );
+          bool isCompositeBase = ! setBottomEdge( *edge, quadList.back(), face );
+          if ( isCompositeBase )
+          {
+            // it's OK if all EDGEs of the bottom side belongs to the bottom FACE
+            StdMeshers_FaceSidePtr botSide = quadList.back()->side[ QUAD_BOTTOM_SIDE ];
+            for ( int iE = 0; iE < botSide->NbEdges(); ++iE )
+              if ( !myHelper->IsSubShape( botSide->Edge(iE), thePrism.myBottom ))
+                return toSM( error(TCom("Composite 'horizontal' edges are not supported")));
+          }
+          if ( faceMap.Add( face ))
+            thePrism.myWallQuads.push_back( quadList );
           break;
         }
       }
@@ -755,7 +953,7 @@ bool StdMeshers_Prism_3D::getWallFaces( Prism_3D::TPrismTopo & thePrism,
     int nbKnownFaces;
     do {
       nbKnownFaces = faceMap.Extent();
-      StdMeshers_FaceSide *rightSide, *topSide; // sides of the quad
+      StdMeshers_FaceSidePtr rightSide, topSide; // sides of the quad
       for ( size_t i = 0; i < thePrism.myWallQuads.size(); ++i )
       {
         rightSide = thePrism.myWallQuads[i].back()->side[ QUAD_RIGHT_SIDE ];
@@ -787,8 +985,8 @@ bool StdMeshers_Prism_3D::getWallFaces( Prism_3D::TPrismTopo & thePrism,
     {
       for ( size_t i = 0; i < thePrism.myWallQuads.size(); ++i )
       {
-        StdMeshers_FaceSide* topSide = thePrism.myWallQuads[i].back()->side[ QUAD_TOP_SIDE ];
-        const TopoDS_Edge &     topE = topSide->Edge( 0 );
+        StdMeshers_FaceSidePtr topSide = thePrism.myWallQuads[i].back()->side[ QUAD_TOP_SIDE ];
+        const TopoDS_Edge &       topE = topSide->Edge( 0 );
         if ( topSide->NbEdges() > 1 )
           return toSM( error(COMPERR_BAD_SHAPE, TCom("Side face #") <<
                              shapeID( thePrism.myWallQuads[i].back()->face )
@@ -834,8 +1032,8 @@ bool StdMeshers_Prism_3D::getWallFaces( Prism_3D::TPrismTopo & thePrism,
   // Check that the top FACE shares all the top EDGEs
   for ( size_t i = 0; i < thePrism.myWallQuads.size(); ++i )
   {
-    StdMeshers_FaceSide* topSide = thePrism.myWallQuads[i].back()->side[ QUAD_TOP_SIDE ];
-    const TopoDS_Edge &     topE = topSide->Edge( 0 );
+    StdMeshers_FaceSidePtr topSide = thePrism.myWallQuads[i].back()->side[ QUAD_TOP_SIDE ];
+    const TopoDS_Edge &       topE = topSide->Edge( 0 );
     if ( !myHelper->IsSubShape( topE, thePrism.myTop ))
       return toSM( error( TCom("Wrong source face (#") << shapeID( thePrism.myBottom )));
   }
@@ -858,7 +1056,7 @@ bool StdMeshers_Prism_3D::compute(const Prism_3D::TPrismTopo& thePrism)
   if ( !computeWalls( thePrism ))
     return false;
 
-  // Analyse mesh and geometry to find block sub-shapes and submeshes
+  // Analyse mesh and geometry to find all block sub-shapes and submeshes
   if ( !myBlock.Init( myHelper, thePrism ))
     return toSM( error( myBlock.GetError()));
 
@@ -866,6 +1064,13 @@ bool StdMeshers_Prism_3D::compute(const Prism_3D::TPrismTopo& thePrism)
 
   int volumeID = meshDS->ShapeToIndex( thePrism.myShape3D );
 
+  // Try to get gp_Trsf to get all nodes from bottom ones
+  vector<gp_Trsf> trsf;
+  gp_Trsf bottomToTopTrsf;
+  if ( !myBlock.GetLayersTransformation( trsf, thePrism ))
+    trsf.clear();
+  else if ( !trsf.empty() )
+    bottomToTopTrsf = trsf.back();
 
   // To compute coordinates of a node inside a block, it is necessary to know
   // 1. normalized parameters of the node by which
@@ -881,15 +1086,14 @@ bool StdMeshers_Prism_3D::compute(const Prism_3D::TPrismTopo& thePrism)
   // Projections on the top and bottom faces are taken from nodes existing
   // on these faces; find correspondence between bottom and top nodes
   myBotToColumnMap.clear();
-  if ( !assocOrProjBottom2Top() ) // it also fills myBotToColumnMap
+  if ( !assocOrProjBottom2Top( bottomToTopTrsf, thePrism ) ) // it also fills myBotToColumnMap
     return false;
 
 
   // Create nodes inside the block
 
   // try to use transformation (issue 0020680)
-  vector<gp_Trsf> trsf;
-  if ( myBlock.GetLayersTransformation( trsf, thePrism ))
+  if ( !trsf.empty() )
   {
     // loop on nodes inside the bottom face
     TNode2ColumnMap::iterator bot_column = myBotToColumnMap.begin();
@@ -923,36 +1127,45 @@ bool StdMeshers_Prism_3D::compute(const Prism_3D::TPrismTopo& thePrism)
     {
       const Prism_3D::TNode& tBotNode = bot_column->first; // bottom TNode
       if ( tBotNode.GetPositionType() != SMDS_TOP_FACE )
-        continue; // node is not inside face 
+        continue; // node is not inside the FACE 
 
       // column nodes; middle part of the column are zero pointers
       TNodeColumn& column = bot_column->second;
 
-      // compute bottom node parameters
-      gp_XYZ paramHint(-1,-1,-1);
-      if ( prevBNode.IsNeighbor( tBotNode ))
-        paramHint = prevBNode.GetParams();
-      if ( !myBlock.ComputeParameters( tBotNode.GetCoords(), tBotNode.ChangeParams(),
-                                       ID_BOT_FACE, paramHint ))
-        return toSM( error(TCom("Can't compute normalized parameters for node ")
-                           << tBotNode.myNode->GetID() << " on the face #"
-                           << myBlock.SubMesh( ID_BOT_FACE )->GetId() ));
-      prevBNode = tBotNode;
+      gp_XYZ botParams, topParams;
+      if ( !tBotNode.HasParams() )
+      {
+        // compute bottom node parameters
+        gp_XYZ paramHint(-1,-1,-1);
+        if ( prevBNode.IsNeighbor( tBotNode ))
+          paramHint = prevBNode.GetParams();
+        if ( !myBlock.ComputeParameters( tBotNode.GetCoords(), tBotNode.ChangeParams(),
+                                         ID_BOT_FACE, paramHint ))
+          return toSM( error(TCom("Can't compute normalized parameters for node ")
+                             << tBotNode.myNode->GetID() << " on the face #"
+                             << myBlock.SubMesh( ID_BOT_FACE )->GetId() ));
+        prevBNode = tBotNode;
+
+        botParams = topParams = tBotNode.GetParams();
+        topParams.SetZ( 1 );
+
+        // compute top node parameters
+        if ( column.size() > 2 ) {
+          gp_Pnt topCoords = gpXYZ( column.back() );
+          if ( !myBlock.ComputeParameters( topCoords, topParams, ID_TOP_FACE, topParams ))
+            return toSM( error(TCom("Can't compute normalized parameters ")
+                               << "for node " << column.back()->GetID()
+                               << " on the face #"<< column.back()->getshapeId() ));
+        }
+      }
+      else // top nodes are created by projection using parameters
+      {
+        botParams = topParams = tBotNode.GetParams();
+        topParams.SetZ( 1 );
+      }
 
       myShapeXYZ[ ID_BOT_FACE ] = tBotNode.GetCoords();
-      gp_XYZ botParams          = tBotNode.GetParams();
-
-      // compute top node parameters
       myShapeXYZ[ ID_TOP_FACE ] = gpXYZ( column.back() );
-      gp_XYZ topParams = botParams;
-      topParams.SetZ( 1 );
-      if ( column.size() > 2 ) {
-        gp_Pnt topCoords = myShapeXYZ[ ID_TOP_FACE ];
-        if ( !myBlock.ComputeParameters( topCoords, topParams, ID_TOP_FACE, topParams ))
-          return toSM( error(TCom("Can't compute normalized parameters ")
-                             << "for node " << column.back()->GetID()
-                             << " on the face #"<< column.back()->getshapeId() ));
-      }
 
       // vertical loop
       TNodeColumn::iterator columnNodes = column.begin();
@@ -980,6 +1193,9 @@ bool StdMeshers_Prism_3D::compute(const Prism_3D::TPrismTopo& thePrism)
         if ( !SMESH_Block::ShellPoint( params, myShapeXYZ, coords ))
           return toSM( error("Can't compute coordinates by normalized parameters"));
 
+        // if ( !meshDS->MeshElements( volumeID ) ||
+        //      meshDS->MeshElements( volumeID )->NbNodes() == 0 )
+        //   pointsToPython(myShapeXYZ);
         SHOWYXZ("TOPFacePoint ",myShapeXYZ[ ID_TOP_FACE]);
         SHOWYXZ("BOT Node "<< tBotNode.myNode->GetID(),gpXYZ(tBotNode.myNode));
         SHOWYXZ("ShellPoint ",coords);
@@ -987,6 +1203,9 @@ bool StdMeshers_Prism_3D::compute(const Prism_3D::TPrismTopo& thePrism)
         // create a node
         node = meshDS->AddNode( coords.X(), coords.Y(), coords.Z() );
         meshDS->SetNodeInVolume( node, volumeID );
+
+        if ( _computeCanceled )
+          return false;
       }
     } // loop on bottom nodes
   }
@@ -1043,47 +1262,53 @@ bool StdMeshers_Prism_3D::computeWalls(const Prism_3D::TPrismTopo& thePrism)
 {
   SMESH_Mesh*     mesh = myHelper->GetMesh();
   SMESHDS_Mesh* meshDS = myHelper->GetMeshDS();
+  DBGOUT( endl << "COMPUTE Prism " << meshDS->ShapeToIndex( thePrism.myShape3D ));
 
-  TProjction1dAlgo* projector1D = TProjction1dAlgo::instance( this );
+  TProjction1dAlgo*      projector1D = TProjction1dAlgo::instance( this );
   StdMeshers_Quadrangle_2D* quadAlgo = TQuadrangleAlgo::instance( this, myHelper );
 
-  SMESH_HypoFilter hyp1dFilter( SMESH_HypoFilter::IsAlgo(),/*not=*/true);
-  hyp1dFilter.And( SMESH_HypoFilter::HasDim( 1 ));
-  hyp1dFilter.And( SMESH_HypoFilter::IsMoreLocalThan( thePrism.myShape3D, *mesh ));
+  // SMESH_HypoFilter hyp1dFilter( SMESH_HypoFilter::IsAlgo(),/*not=*/true);
+  // hyp1dFilter.And( SMESH_HypoFilter::HasDim( 1 ));
+  // hyp1dFilter.And( SMESH_HypoFilter::IsMoreLocalThan( thePrism.myShape3D, *mesh ));
 
   // Discretize equally 'vertical' EDGEs
   // -----------------------------------
   // find source FACE sides for projection: either already computed ones or
   // the 'most composite' ones
-  multimap< int, int > wgt2quad;
-  for ( size_t iW = 0; iW != thePrism.myWallQuads.size(); ++iW )
+  const size_t nbWalls = thePrism.myWallQuads.size();
+  vector< int > wgt( nbWalls, 0 ); // "weight" of a wall
+  for ( size_t iW = 0; iW != nbWalls; ++iW )
   {
     Prism_3D::TQuadList::const_iterator quad = thePrism.myWallQuads[iW].begin();
-    int wgt = 0; // "weight"
     for ( ; quad != thePrism.myWallQuads[iW].end(); ++quad )
     {
-      StdMeshers_FaceSide* lftSide = (*quad)->side[ QUAD_LEFT_SIDE ];
+      StdMeshers_FaceSidePtr lftSide = (*quad)->side[ QUAD_LEFT_SIDE ];
       for ( int i = 0; i < lftSide->NbEdges(); ++i )
       {
-        ++wgt;
+        ++wgt[ iW ];
         const TopoDS_Edge& E = lftSide->Edge(i);
         if ( mesh->GetSubMesh( E )->IsMeshComputed() )
-          wgt += 10;
-        else if ( mesh->GetHypothesis( E, hyp1dFilter, true )) // local hypothesis!
-          wgt += 100;
+        {
+          wgt[ iW ] += 100;
+          wgt[ myHelper->WrapIndex( iW+1, nbWalls)] += 10;
+          wgt[ myHelper->WrapIndex( iW-1, nbWalls)] += 10;
+        }
+        // else if ( mesh->GetHypothesis( E, hyp1dFilter, true )) // local hypothesis!
+        //   wgt += 100;
       }
     }
-    wgt2quad.insert( make_pair( wgt, iW ));
-
     // in quadratic mesh, pass ignoreMediumNodes to quad sides
     if ( myHelper->GetIsQuadratic() )
     {
       quad = thePrism.myWallQuads[iW].begin();
       for ( ; quad != thePrism.myWallQuads[iW].end(); ++quad )
         for ( int i = 0; i < NB_QUAD_SIDES; ++i )
-          (*quad)->side[ i ]->SetIgnoreMediumNodes( true );
+          (*quad)->side[ i ].grid->SetIgnoreMediumNodes( true );
     }
   }
+  multimap< int, int > wgt2quad;
+  for ( size_t iW = 0; iW != nbWalls; ++iW )
+    wgt2quad.insert( make_pair( wgt[ iW ], iW ));
 
   // Project 'vertical' EDGEs, from left to right
   multimap< int, int >::reverse_iterator w2q = wgt2quad.rbegin();
@@ -1094,8 +1319,8 @@ bool StdMeshers_Prism_3D::computeWalls(const Prism_3D::TPrismTopo& thePrism)
     Prism_3D::TQuadList::const_iterator quad = quads.begin();
     for ( ; quad != quads.end(); ++quad )
     {
-      StdMeshers_FaceSide* rgtSide = (*quad)->side[ QUAD_RIGHT_SIDE ]; // tgt
-      StdMeshers_FaceSide* lftSide = (*quad)->side[ QUAD_LEFT_SIDE ];  // src
+      StdMeshers_FaceSidePtr rgtSide = (*quad)->side[ QUAD_RIGHT_SIDE ]; // tgt
+      StdMeshers_FaceSidePtr lftSide = (*quad)->side[ QUAD_LEFT_SIDE ];  // src
       bool swapLeftRight = ( lftSide->NbSegments( /*update=*/true ) == 0 &&
                              rgtSide->NbSegments( /*update=*/true )  > 0 );
       if ( swapLeftRight )
@@ -1108,10 +1333,20 @@ bool StdMeshers_Prism_3D::computeWalls(const Prism_3D::TPrismTopo& thePrism)
         const TopoDS_Edge& srcE = lftSide->Edge(i);
         SMESH_subMesh*    srcSM = mesh->GetSubMesh( srcE );
         if ( !srcSM->IsMeshComputed() ) {
-          srcSM->ComputeSubMeshStateEngine( SMESH_subMesh::COMPUTE );
-          srcSM->ComputeStateEngine       ( SMESH_subMesh::COMPUTE );
+          DBGOUT( "COMPUTE V edge " << srcSM->GetId() );
+          TopoDS_Edge prpgSrcE = findPropagationSource( srcE );
+          if ( !prpgSrcE.IsNull() ) {
+            srcSM->ComputeSubMeshStateEngine( SMESH_subMesh::COMPUTE );
+            projector1D->myHyp.SetSourceEdge( prpgSrcE );
+            projector1D->Compute( *mesh, srcE );
+            srcSM->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
+          }
+          else {
+            srcSM->ComputeSubMeshStateEngine( SMESH_subMesh::COMPUTE );
+            srcSM->ComputeStateEngine       ( SMESH_subMesh::COMPUTE );
+          }
           if ( !srcSM->IsMeshComputed() )
-            return false;
+            return toSM( error( "Can't compute 1D mesh" ));
         }
         nbSrcSegments += srcSM->GetSubMeshDS()->NbElements();
       }
@@ -1122,7 +1357,11 @@ bool StdMeshers_Prism_3D::computeWalls(const Prism_3D::TPrismTopo& thePrism)
       {
         const TopoDS_Edge& tgtE = rgtSide->Edge(i);
         SMESH_subMesh*    tgtSM = mesh->GetSubMesh( tgtE );
-        if (( isTgtEdgeComputed[ i ] = tgtSM->IsMeshComputed() )) {
+        if ( !( isTgtEdgeComputed[ i ] = tgtSM->IsMeshComputed() )) {
+          tgtSM->ComputeSubMeshStateEngine( SMESH_subMesh::COMPUTE );
+          tgtSM->ComputeStateEngine       ( SMESH_subMesh::COMPUTE );
+        }
+        if ( tgtSM->IsMeshComputed() ) {
           ++nbTgtMeshed;
           nbTgtSegments += tgtSM->GetSubMeshDS()->NbElements();
         }
@@ -1131,18 +1370,35 @@ bool StdMeshers_Prism_3D::computeWalls(const Prism_3D::TPrismTopo& thePrism)
       {
         if ( nbTgtSegments != nbSrcSegments )
         {
-          for ( int i = 0; i < lftSide->NbEdges(); ++i )
-            addBadInputElements( meshDS->MeshElements( lftSide->Edge( i )));
+          bool badMeshRemoved = false;
+          // remove just computed segments
           for ( int i = 0; i < rgtSide->NbEdges(); ++i )
-            addBadInputElements( meshDS->MeshElements( rgtSide->Edge( i )));
-          return toSM( error( TCom("Different nb of segment on logically vertical edges #")
-                              << shapeID( lftSide->Edge(0) ) << " and #"
-                              << shapeID( rgtSide->Edge(0) ) << ": "
-                              << nbSrcSegments << " != " << nbTgtSegments ));
+            if ( !isTgtEdgeComputed[ i ])
+            {
+              const TopoDS_Edge& tgtE = rgtSide->Edge(i);
+              SMESH_subMesh*    tgtSM = mesh->GetSubMesh( tgtE );
+              tgtSM->ComputeStateEngine( SMESH_subMesh::CLEAN );
+              badMeshRemoved = true;
+              nbTgtMeshed--;
+            }
+          if ( !badMeshRemoved )
+          {
+            for ( int i = 0; i < lftSide->NbEdges(); ++i )
+              addBadInputElements( meshDS->MeshElements( lftSide->Edge( i )));
+            for ( int i = 0; i < rgtSide->NbEdges(); ++i )
+              addBadInputElements( meshDS->MeshElements( rgtSide->Edge( i )));
+            return toSM( error( TCom("Different nb of segment on logically vertical edges #")
+                                << shapeID( lftSide->Edge(0) ) << " and #"
+                                << shapeID( rgtSide->Edge(0) ) << ": "
+                                << nbSrcSegments << " != " << nbTgtSegments ));
+          }
+        }
+        else // if ( nbTgtSegments == nbSrcSegments )
+        {
+          continue;
         }
-        continue;
       }
-      // Compute
+      // Compute 'vertical projection'
       if ( nbTgtMeshed == 0 )
       {
         // compute nodes on target VERTEXes
@@ -1161,8 +1417,9 @@ bool StdMeshers_Prism_3D::computeWalls(const Prism_3D::TPrismTopo& thePrism)
         }
 
         // compute nodes on target EDGEs
+        DBGOUT( "COMPUTE V edge (proj) " << shapeID( lftSide->Edge(0)));
         rgtSide->Reverse(); // direct it same as the lftSide
-        myHelper->SetElementsOnShape( false );
+        myHelper->SetElementsOnShape( false ); // myHelper holds the prism shape
         TopoDS_Edge tgtEdge;
         for ( size_t iN = 1; iN < srcNodeStr.size()-1; ++iN ) // add nodes
         {
@@ -1173,25 +1430,33 @@ bool StdMeshers_Prism_3D::computeWalls(const Prism_3D::TPrismTopo& thePrism)
         }
         for ( size_t iN = 1; iN < srcNodeStr.size(); ++iN ) // add segments
         {
-          SMDS_MeshElement* newEdge = myHelper->AddEdge( newNodes[ iN-1 ], newNodes[ iN ] );
+          // find an EDGE to set a new segment
           std::pair<int, TopAbs_ShapeEnum> id2type = 
             myHelper->GetMediumPos( newNodes[ iN-1 ], newNodes[ iN ] );
-          if ( id2type.second == TopAbs_EDGE )
-          {
-            meshDS->SetMeshElementOnShape( newEdge, id2type.first );
-          }
-          else // new nodes are on different EDGEs; put one of them on VERTEX
+          if ( id2type.second != TopAbs_EDGE )
           {
+            // new nodes are on different EDGEs; put one of them on VERTEX
             const int      edgeIndex = rgtSide->EdgeIndex( srcNodeStr[ iN-1 ].normParam );
             const double vertexParam = rgtSide->LastParameter( edgeIndex );
-            const gp_Pnt           p = BRep_Tool::Pnt( rgtSide->LastVertex( edgeIndex ));
+            TopoDS_Vertex     vertex = rgtSide->LastVertex( edgeIndex );
+            const SMDS_MeshNode*  vn = SMESH_Algo::VertexNode( vertex, meshDS );
+            const gp_Pnt           p = BRep_Tool::Pnt( vertex );
             const int         isPrev = ( Abs( srcNodeStr[ iN-1 ].normParam - vertexParam ) <
                                          Abs( srcNodeStr[ iN   ].normParam - vertexParam ));
-            meshDS->SetMeshElementOnShape( newEdge, newNodes[ iN-(1-isPrev) ]->getshapeId() );
             meshDS->UnSetNodeOnShape( newNodes[ iN-isPrev ] );
-            meshDS->SetNodeOnVertex ( newNodes[ iN-isPrev ], rgtSide->LastVertex( edgeIndex ));
-            meshDS->MoveNode( newNodes[ iN-isPrev ], p.X(), p.Y(), p.Z() );
+            meshDS->SetNodeOnVertex ( newNodes[ iN-isPrev ], vertex );
+            meshDS->MoveNode        ( newNodes[ iN-isPrev ], p.X(), p.Y(), p.Z() );
+            id2type.first = newNodes[ iN-(1-isPrev) ]->getshapeId();
+            if ( vn )
+            {
+              SMESH_MeshEditor::TListOfListOfNodes lln( 1, list< const SMDS_MeshNode* >() );
+              lln.back().push_back ( vn );
+              lln.back().push_front( newNodes[ iN-isPrev ] ); // to keep 
+              SMESH_MeshEditor( mesh ).MergeNodes( lln );
+            }
           }
+          SMDS_MeshElement* newEdge = myHelper->AddEdge( newNodes[ iN-1 ], newNodes[ iN ] );
+          meshDS->SetMeshElementOnShape( newEdge, id2type.first );
         }
         myHelper->SetElementsOnShape( true );
         for ( int i = 0; i < rgtSide->NbEdges(); ++i ) // update state of sub-meshes
@@ -1225,38 +1490,74 @@ bool StdMeshers_Prism_3D::computeWalls(const Prism_3D::TPrismTopo& thePrism)
     Prism_3D::TQuadList::const_iterator quad = thePrism.myWallQuads[iW].begin();
     for ( ; quad != thePrism.myWallQuads[iW].end(); ++quad )
     {
-      // Top EDGEs must be projections from the bottom ones
-      // to compute stuctured quad mesh on wall FACEs
-      // ---------------------------------------------------
-      const TopoDS_Edge& botE = (*quad)->side[ QUAD_BOTTOM_SIDE ]->Edge(0);
-      const TopoDS_Edge& topE = (*quad)->side[ QUAD_TOP_SIDE    ]->Edge(0);
-
-      projector1D->myHyp.SetSourceEdge( botE );
-
-      SMESH_subMesh* tgtEdgeSm = mesh->GetSubMesh( topE );
-      if ( !tgtEdgeSm->IsMeshComputed() )
-      {
-        // compute nodes on VERTEXes
-        tgtEdgeSm->ComputeSubMeshStateEngine( SMESH_subMesh::COMPUTE );
-        // project segments
-        projector1D->InitComputeError();
-        bool ok = projector1D->Compute( *mesh, topE );
-        if ( !ok )
-        {
-          SMESH_ComputeErrorPtr err = projector1D->GetComputeError();
-          if ( err->IsOK() ) err->myName = COMPERR_ALGO_FAILED;
-          tgtEdgeSm->GetComputeError() = err;
-          return false;
-        }
-      }
-      tgtEdgeSm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
-
-      // Compute quad mesh on wall FACEs
-      // -------------------------------
       const TopoDS_Face& face = (*quad)->face;
       SMESH_subMesh* fSM = mesh->GetSubMesh( face );
       if ( ! fSM->IsMeshComputed() )
       {
+        // Top EDGEs must be projections from the bottom ones
+        // to compute stuctured quad mesh on wall FACEs
+        // ---------------------------------------------------
+        const TopoDS_Edge& botE = (*quad)->side[ QUAD_BOTTOM_SIDE ].grid->Edge(0);
+        const TopoDS_Edge& topE = (*quad)->side[ QUAD_TOP_SIDE    ].grid->Edge(0);
+        SMESH_subMesh*    botSM = mesh->GetSubMesh( botE );
+        SMESH_subMesh*    topSM = mesh->GetSubMesh( topE );
+        SMESH_subMesh*    srcSM = botSM;
+        SMESH_subMesh*    tgtSM = topSM;
+        if ( !srcSM->IsMeshComputed() && tgtSM->IsMeshComputed() )
+          std::swap( srcSM, tgtSM );
+
+        if ( !srcSM->IsMeshComputed() )
+        {
+          DBGOUT( "COMPUTE H edge " << srcSM->GetId());
+          srcSM->ComputeSubMeshStateEngine( SMESH_subMesh::COMPUTE ); // nodes on VERTEXes
+          srcSM->ComputeStateEngine( SMESH_subMesh::COMPUTE );        // segments on the EDGE
+        }
+        srcSM->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
+
+        if ( tgtSM->IsMeshComputed() &&
+             tgtSM->GetSubMeshDS()->NbNodes() != srcSM->GetSubMeshDS()->NbNodes() )
+        {
+          // the top EDGE is computed differently than the bottom one,
+          // try to clear a wrong mesh
+          bool isAdjFaceMeshed = false;
+          PShapeIteratorPtr fIt = myHelper->GetAncestors( tgtSM->GetSubShape(),
+                                                          *mesh, TopAbs_FACE );
+          while ( const TopoDS_Shape* f = fIt->next() )
+            if (( isAdjFaceMeshed = mesh->GetSubMesh( *f )->IsMeshComputed() ))
+              break;
+          if ( isAdjFaceMeshed )
+            return toSM( error( TCom("Different nb of segment on logically horizontal edges #")
+                                << shapeID( botE ) << " and #"
+                                << shapeID( topE ) << ": "
+                                << tgtSM->GetSubMeshDS()->NbElements() << " != "
+                                << srcSM->GetSubMeshDS()->NbElements() ));
+          tgtSM->ComputeStateEngine( SMESH_subMesh::CLEAN );
+        }
+        if ( !tgtSM->IsMeshComputed() )
+        {
+          // compute nodes on VERTEXes
+          SMESH_subMeshIteratorPtr smIt = tgtSM->getDependsOnIterator(/*includeSelf=*/false);
+          while ( smIt->more() )
+            smIt->next()->ComputeStateEngine( SMESH_subMesh::COMPUTE );
+          // project segments
+          DBGOUT( "COMPUTE H edge (proj) " << tgtSM->GetId());
+          projector1D->myHyp.SetSourceEdge( TopoDS::Edge( srcSM->GetSubShape() ));
+          projector1D->InitComputeError();
+          bool ok = projector1D->Compute( *mesh, tgtSM->GetSubShape() );
+          if ( !ok )
+          {
+            SMESH_ComputeErrorPtr err = projector1D->GetComputeError();
+            if ( err->IsOK() ) err->myName = COMPERR_ALGO_FAILED;
+            tgtSM->GetComputeError() = err;
+            return false;
+          }
+        }
+        tgtSM->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
+
+
+        // Compute quad mesh on wall FACEs
+        // -------------------------------
+
         // make all EDGES meshed
         fSM->ComputeSubMeshStateEngine( SMESH_subMesh::COMPUTE );
         if ( !fSM->SubMeshesComputed() )
@@ -1264,6 +1565,7 @@ bool StdMeshers_Prism_3D::computeWalls(const Prism_3D::TPrismTopo& thePrism)
                               "Not all edges have valid algorithm and hypothesis"));
         // mesh the <face>
         quadAlgo->InitComputeError();
+        DBGOUT( "COMPUTE Quad face " << fSM->GetId());
         bool ok = quadAlgo->Compute( *mesh, face );
         fSM->GetComputeError() = quadAlgo->GetComputeError();
         if ( !ok )
@@ -1283,6 +1585,22 @@ bool StdMeshers_Prism_3D::computeWalls(const Prism_3D::TPrismTopo& thePrism)
   return true;
 }
 
+//=======================================================================
+/*!
+ * \brief Returns a source EDGE of propagation to a given EDGE
+ */
+//=======================================================================
+
+TopoDS_Edge StdMeshers_Prism_3D::findPropagationSource( const TopoDS_Edge& E )
+{
+  if ( myPropagChains )
+    for ( size_t i = 0; !myPropagChains[i].IsEmpty(); ++i )
+      if ( myPropagChains[i].Contains( E ))
+        return TopoDS::Edge( myPropagChains[i].FindKey( 1 ));
+
+  return TopoDS_Edge();
+}
+
 //=======================================================================
 //function : Evaluate
 //purpose  : 
@@ -1532,7 +1850,8 @@ void StdMeshers_Prism_3D::AddPrisms( vector<const TNodeColumn*> & columns,
  */
 //================================================================================
 
-bool StdMeshers_Prism_3D::assocOrProjBottom2Top()
+bool StdMeshers_Prism_3D::assocOrProjBottom2Top( const gp_Trsf & bottomToTopTrsf,
+                                                 const Prism_3D::TPrismTopo& thePrism)
 {
   SMESH_subMesh * botSM = myBlock.SubMesh( ID_BOT_FACE );
   SMESH_subMesh * topSM = myBlock.SubMesh( ID_TOP_FACE );
@@ -1542,14 +1861,14 @@ bool StdMeshers_Prism_3D::assocOrProjBottom2Top()
 
   if ( !botSMDS || botSMDS->NbElements() == 0 )
   {
-    _gen->Compute( *myHelper->GetMesh(), botSM->GetSubShape() );
+    _gen->Compute( *myHelper->GetMesh(), botSM->GetSubShape(), /*aShapeOnly=*/true );
     botSMDS = botSM->GetSubMeshDS();
     if ( !botSMDS || botSMDS->NbElements() == 0 )
-      return toSM( error(TCom("No elememts on face #") << botSM->GetId() ));
+      return toSM( error(TCom("No elements on face #") << botSM->GetId() ));
   }
 
   bool needProject = !topSM->IsMeshComputed();
-  if ( !needProject && 
+  if ( !needProject &&
        (botSMDS->NbElements() != topSMDS->NbElements() ||
         botSMDS->NbNodes()    != topSMDS->NbNodes()))
   {
@@ -1568,26 +1887,77 @@ bool StdMeshers_Prism_3D::assocOrProjBottom2Top()
 
   if ( needProject )
   {
-    return projectBottomToTop();
+    return projectBottomToTop( bottomToTopTrsf );
   }
 
   TopoDS_Face botFace = TopoDS::Face( myBlock.Shape( ID_BOT_FACE ));
   TopoDS_Face topFace = TopoDS::Face( myBlock.Shape( ID_TOP_FACE ));
   // associate top and bottom faces
   TAssocTool::TShapeShapeMap shape2ShapeMap;
-  if ( !TAssocTool::FindSubShapeAssociation( botFace, myBlock.Mesh(),
-                                             topFace, myBlock.Mesh(),
-                                             shape2ShapeMap) )
-    return toSM( error(TCom("Topology of faces #") << botSM->GetId()
-                       <<" and #"<< topSM->GetId() << " seems different" ));
+  const bool sameTopo = 
+    TAssocTool::FindSubShapeAssociation( botFace, myBlock.Mesh(),
+                                         topFace, myBlock.Mesh(),
+                                         shape2ShapeMap);
+  if ( !sameTopo )
+    for ( size_t iQ = 0; iQ < thePrism.myWallQuads.size(); ++iQ )
+    {
+      const Prism_3D::TQuadList& quadList = thePrism.myWallQuads[iQ];
+      StdMeshers_FaceSidePtr      botSide = quadList.front()->side[ QUAD_BOTTOM_SIDE ];
+      StdMeshers_FaceSidePtr      topSide = quadList.back ()->side[ QUAD_TOP_SIDE ];
+      if ( botSide->NbEdges() == topSide->NbEdges() )
+      {
+        for ( int iE = 0; iE < botSide->NbEdges(); ++iE )
+        {
+          TAssocTool::InsertAssociation( botSide->Edge( iE ),
+                                         topSide->Edge( iE ), shape2ShapeMap );
+          TAssocTool::InsertAssociation( myHelper->IthVertex( 0, botSide->Edge( iE )),
+                                         myHelper->IthVertex( 0, topSide->Edge( iE )),
+                                         shape2ShapeMap );
+        }
+      }
+      else
+      {
+        TopoDS_Vertex vb, vt;
+        StdMeshers_FaceSidePtr sideB, sideT;
+        vb = myHelper->IthVertex( 0, botSide->Edge( 0 ));
+        vt = myHelper->IthVertex( 0, topSide->Edge( 0 ));
+        sideB = quadList.front()->side[ QUAD_LEFT_SIDE ];
+        sideT = quadList.back ()->side[ QUAD_LEFT_SIDE ];
+        if ( vb.IsSame( sideB->FirstVertex() ) &&
+             vt.IsSame( sideT->LastVertex() ))
+        {
+          TAssocTool::InsertAssociation( botSide->Edge( 0 ),
+                                         topSide->Edge( 0 ), shape2ShapeMap );
+          TAssocTool::InsertAssociation( vb, vt, shape2ShapeMap );
+        }
+        vb = myHelper->IthVertex( 1, botSide->Edge( botSide->NbEdges()-1 ));
+        vt = myHelper->IthVertex( 1, topSide->Edge( topSide->NbEdges()-1 ));
+        sideB = quadList.front()->side[ QUAD_RIGHT_SIDE ];
+        sideT = quadList.back ()->side[ QUAD_RIGHT_SIDE ];
+        if ( vb.IsSame( sideB->FirstVertex() ) &&
+             vt.IsSame( sideT->LastVertex() ))
+        {
+          TAssocTool::InsertAssociation( botSide->Edge( botSide->NbEdges()-1 ),
+                                         topSide->Edge( topSide->NbEdges()-1 ),
+                                         shape2ShapeMap );
+          TAssocTool::InsertAssociation( vb, vt, shape2ShapeMap );
+        }
+      }
+    }
 
   // Find matching nodes of top and bottom faces
   TNodeNodeMap n2nMap;
   if ( ! TAssocTool::FindMatchingNodesOnFaces( botFace, myBlock.Mesh(),
                                                topFace, myBlock.Mesh(),
                                                shape2ShapeMap, n2nMap ))
-    return toSM( error(TCom("Mesh on faces #") << botSM->GetId()
-                       <<" and #"<< topSM->GetId() << " seems different" ));
+  {
+    if ( sameTopo )
+      return toSM( error(TCom("Mesh on faces #") << botSM->GetId()
+                         <<" and #"<< topSM->GetId() << " seems different" ));
+    else
+      return toSM( error(TCom("Topology of faces #") << botSM->GetId()
+                         <<" and #"<< topSM->GetId() << " seems different" ));
+  }
 
   // Fill myBotToColumnMap
 
@@ -1620,7 +1990,7 @@ bool StdMeshers_Prism_3D::assocOrProjBottom2Top()
  */
 //================================================================================
 
-bool StdMeshers_Prism_3D::projectBottomToTop()
+bool StdMeshers_Prism_3D::projectBottomToTop( const gp_Trsf & bottomToTopTrsf )
 {
   SMESHDS_Mesh*  meshDS = myBlock.MeshDS();
   SMESH_subMesh * botSM = myBlock.SubMesh( ID_BOT_FACE );
@@ -1632,10 +2002,19 @@ bool StdMeshers_Prism_3D::projectBottomToTop()
   if ( topSMDS && topSMDS->NbElements() > 0 )
     topSM->ComputeStateEngine( SMESH_subMesh::CLEAN );
 
-  const TopoDS_Shape& botFace = myBlock.Shape( ID_BOT_FACE ); // oriented within the 3D SHAPE
-  const TopoDS_Shape& topFace = myBlock.Shape( ID_TOP_FACE);
+  const TopoDS_Face& botFace = TopoDS::Face( myBlock.Shape( ID_BOT_FACE )); // oriented within
+  const TopoDS_Face& topFace = TopoDS::Face( myBlock.Shape( ID_TOP_FACE )); //    the 3D SHAPE
   int topFaceID = meshDS->ShapeToIndex( topFace );
 
+  SMESH_MesherHelper botHelper( *myHelper->GetMesh() );
+  botHelper.SetSubShape( botFace );
+  botHelper.ToFixNodeParameters( true );
+  bool checkUV;
+  SMESH_MesherHelper topHelper( *myHelper->GetMesh() );
+  topHelper.SetSubShape( topFace );
+  topHelper.ToFixNodeParameters( true );
+  double distXYZ[4], fixTol = 10 * topHelper.MaxTolerance( topFace );
+
   // Fill myBotToColumnMap
 
   int zSize = myBlock.VerticalSize();
@@ -1644,26 +2023,47 @@ bool StdMeshers_Prism_3D::projectBottomToTop()
   while ( nIt->more() )
   {
     const SMDS_MeshNode* botNode = nIt->next();
+    const SMDS_MeshNode* topNode = 0;
     if ( botNode->GetPosition()->GetTypeOfPosition() != SMDS_TOP_FACE )
       continue; // strange
-    // compute bottom node params
+
     Prism_3D::TNode bN( botNode );
-    gp_XYZ paramHint(-1,-1,-1);
-    if ( prevTNode.IsNeighbor( bN ))
-      paramHint = prevTNode.GetParams();
-    if ( !myBlock.ComputeParameters( bN.GetCoords(), bN.ChangeParams(),
-                                     ID_BOT_FACE, paramHint ))
-      return toSM( error(TCom("Can't compute normalized parameters for node ")
-                         << botNode->GetID() << " on the face #"<< botSM->GetId() ));
-    prevTNode = bN;
-    // compute top node coords
-    gp_XYZ topXYZ; gp_XY topUV;
-    if ( !myBlock.FacePoint( ID_TOP_FACE, bN.GetParams(), topXYZ ) ||
-         !myBlock.FaceUV   ( ID_TOP_FACE, bN.GetParams(), topUV ))
-      return toSM( error(TCom("Can't compute coordinates "
-                              "by normalized parameters on the face #")<< topSM->GetId() ));
-    SMDS_MeshNode * topNode = meshDS->AddNode( topXYZ.X(),topXYZ.Y(),topXYZ.Z() );
-    meshDS->SetNodeOnFace( topNode, topFaceID, topUV.X(), topUV.Y() );
+    if ( bottomToTopTrsf.Form() == gp_Identity )
+    {
+      // compute bottom node params
+      gp_XYZ paramHint(-1,-1,-1);
+      if ( prevTNode.IsNeighbor( bN ))
+      {
+        paramHint = prevTNode.GetParams();
+        // double tol = 1e-2 * ( prevTNode.GetCoords() - bN.GetCoords() ).Modulus();
+        // myBlock.SetTolerance( Min( myBlock.GetTolerance(), tol ));
+      }
+      if ( !myBlock.ComputeParameters( bN.GetCoords(), bN.ChangeParams(),
+                                       ID_BOT_FACE, paramHint ))
+        return toSM( error(TCom("Can't compute normalized parameters for node ")
+                           << botNode->GetID() << " on the face #"<< botSM->GetId() ));
+      prevTNode = bN;
+      // compute top node coords
+      gp_XYZ topXYZ; gp_XY topUV;
+      if ( !myBlock.FacePoint( ID_TOP_FACE, bN.GetParams(), topXYZ ) ||
+           !myBlock.FaceUV   ( ID_TOP_FACE, bN.GetParams(), topUV ))
+        return toSM( error(TCom("Can't compute coordinates "
+                                "by normalized parameters on the face #")<< topSM->GetId() ));
+      topNode = meshDS->AddNode( topXYZ.X(),topXYZ.Y(),topXYZ.Z() );
+      meshDS->SetNodeOnFace( topNode, topFaceID, topUV.X(), topUV.Y() );
+    }
+    else // use bottomToTopTrsf
+    {
+      gp_XYZ coords = bN.GetCoords();
+      bottomToTopTrsf.Transforms( coords );
+      topNode = meshDS->AddNode( coords.X(), coords.Y(), coords.Z() );
+      gp_XY topUV = botHelper.GetNodeUV( botFace, botNode, 0, &checkUV );
+      meshDS->SetNodeOnFace( topNode, topFaceID, topUV.X(), topUV.Y() );
+      distXYZ[0] = -1;
+      if ( topHelper.CheckNodeUV( topFace, topNode, topUV, fixTol, /*force=*/false, distXYZ ) &&
+           distXYZ[0] > fixTol && distXYZ[0] < fixTol * 1e+3 )
+        meshDS->MoveNode( topNode, distXYZ[1], distXYZ[2], distXYZ[3] ); // transform can be inaccurate
+    }
     // create node column
     TNode2ColumnMap::iterator bN_col = 
       myBotToColumnMap.insert( make_pair ( bN, TNodeColumn() )).first;
@@ -1681,7 +2081,7 @@ bool StdMeshers_Prism_3D::projectBottomToTop()
   // if the bottom faces is orienetd OK then top faces must be reversed
   bool reverseTop = true;
   if ( myHelper->NbAncestors( botFace, *myBlock.Mesh(), TopAbs_SOLID ) > 1 )
-    reverseTop = ! myHelper->IsReversedSubMesh( TopoDS::Face( botFace ));
+    reverseTop = ! myHelper->IsReversedSubMesh( botFace );
   int iFrw, iRev, *iPtr = &( reverseTop ? iRev : iFrw );
 
   // loop on bottom mesh faces
@@ -1826,6 +2226,341 @@ int StdMeshers_Prism_3D::shapeID( const TopoDS_Shape& S )
   return myHelper->GetMeshDS()->ShapeToIndex( S );
 }
 
+namespace // utils used by StdMeshers_Prism_3D::IsApplicable()
+{
+  struct EdgeWithNeighbors
+  {
+    TopoDS_Edge _edge;
+    int         _iL, _iR;
+    EdgeWithNeighbors(const TopoDS_Edge& E, int iE, int nbE, int shift = 0 ):
+      _edge( E ),
+      _iL( SMESH_MesherHelper::WrapIndex( iE-1, nbE ) + shift ),
+      _iR( SMESH_MesherHelper::WrapIndex( iE+1, nbE ) + shift )
+    {
+    }
+    EdgeWithNeighbors() {}
+  };
+  struct PrismSide
+  {
+    TopoDS_Face                 _face;
+    TopTools_IndexedMapOfShape *_faces; // pointer because its copy constructor is private
+    TopoDS_Edge                 _topEdge;
+    vector< EdgeWithNeighbors >*_edges;
+    int                         _iBotEdge;
+    vector< bool >              _isCheckedEdge;
+    int                         _nbCheckedEdges; // nb of EDGEs whose location is defined
+    PrismSide                  *_leftSide;
+    PrismSide                  *_rightSide;
+    const TopoDS_Edge& Edge( int i ) const
+    {
+      return (*_edges)[ i ]._edge;
+    }
+    int FindEdge( const TopoDS_Edge& E ) const
+    {
+      for ( size_t i = 0; i < _edges->size(); ++i )
+        if ( E.IsSame( Edge( i ))) return i;
+      return -1;
+    }
+  };
+  //--------------------------------------------------------------------------------
+  /*!
+   * \brief Return ordered edges of a face
+   */
+  bool getEdges( const TopoDS_Face&            face,
+                 vector< EdgeWithNeighbors > & edges,
+                 const bool                    noHolesAllowed)
+  {
+    list< TopoDS_Edge > ee;
+    list< int >         nbEdgesInWires;
+    int nbW = SMESH_Block::GetOrderedEdges( face, ee, nbEdgesInWires );
+    if ( nbW > 1 && noHolesAllowed )
+      return false;
+
+    int iE, nbTot = 0;
+    list< TopoDS_Edge >::iterator e = ee.begin();
+    list< int >::iterator       nbE = nbEdgesInWires.begin();
+    for ( ; nbE != nbEdgesInWires.end(); ++nbE )
+      for ( iE = 0; iE < *nbE; ++e, ++iE )
+        if ( SMESH_Algo::isDegenerated( *e ))
+        {
+          ee.erase( e );
+          --(*nbE);
+          --iE;
+        }
+        else
+        {
+          e->Orientation( TopAbs_FORWARD ); // for operator==() to work
+        }
+
+    edges.clear();
+    e = ee.begin();
+    for ( nbE = nbEdgesInWires.begin(); nbE != nbEdgesInWires.end(); ++nbE )
+    {
+      for ( iE = 0; iE < *nbE; ++e, ++iE )
+        edges.push_back( EdgeWithNeighbors( *e, iE, *nbE, nbTot ));
+      nbTot += *nbE;
+    }
+    return edges.size();
+  }
+  //--------------------------------------------------------------------------------
+  /*!
+   * \brief Return another faces sharing an edge
+   */
+  const TopoDS_Shape & getAnotherFace( const TopoDS_Face& face,
+                                       const TopoDS_Edge& edge,
+                                       TopTools_IndexedDataMapOfShapeListOfShape& facesOfEdge)
+  {
+    TopTools_ListIteratorOfListOfShape faceIt( facesOfEdge.FindFromKey( edge ));
+    for ( ; faceIt.More(); faceIt.Next() )
+      if ( !face.IsSame( faceIt.Value() ))
+        return faceIt.Value();
+    return face;
+  }
+}
+
+//================================================================================
+/*!
+ * \brief Return true if the algorithm can mesh this shape
+ *  \param [in] aShape - shape to check
+ *  \param [in] toCheckAll - if true, this check returns OK if all shapes are OK,
+ *              else, returns OK if at least one shape is OK
+ */
+//================================================================================
+
+bool StdMeshers_Prism_3D::IsApplicable(const TopoDS_Shape & shape, bool toCheckAll)
+{
+  TopExp_Explorer sExp( shape, TopAbs_SOLID );
+  if ( !sExp.More() )
+    return false;
+
+  for ( ; sExp.More(); sExp.Next() )
+  {
+    // check nb shells
+    TopoDS_Shape shell;
+    TopExp_Explorer shExp( sExp.Current(), TopAbs_SHELL );
+    if ( shExp.More() ) {
+      shell = shExp.Current();
+      shExp.Next();
+      if ( shExp.More() )
+        shell.Nullify();
+    }
+    if ( shell.IsNull() ) {
+      if ( toCheckAll ) return false;
+      continue;
+    }
+    // get all faces
+    TopTools_IndexedMapOfShape allFaces;
+    TopExp::MapShapes( shell, TopAbs_FACE, allFaces );
+    if ( allFaces.Extent() < 3 ) {
+      if ( toCheckAll ) return false;
+      continue;
+    }
+    // is a box?
+    if ( allFaces.Extent() == 6 )
+    {
+      TopTools_IndexedMapOfOrientedShape map;
+      bool isBox = SMESH_Block::FindBlockShapes( TopoDS::Shell( shell ),
+                                                 TopoDS_Vertex(), TopoDS_Vertex(), map );
+      if ( isBox ) {
+        if ( !toCheckAll ) return true;
+        continue;
+      }
+    }
+#ifdef _DEBUG_
+    TopTools_IndexedMapOfShape allShapes;
+    TopExp::MapShapes( shape, allShapes );
+#endif
+
+    TopTools_IndexedDataMapOfShapeListOfShape facesOfEdge;
+    TopTools_ListIteratorOfListOfShape faceIt;
+    TopExp::MapShapesAndAncestors( sExp.Current(), TopAbs_EDGE, TopAbs_FACE , facesOfEdge );
+    if ( facesOfEdge.IsEmpty() ) {
+      if ( toCheckAll ) return false;
+      continue;
+    }
+
+    typedef vector< EdgeWithNeighbors > TEdgeWithNeighborsVec;
+    vector< TEdgeWithNeighborsVec > faceEdgesVec( allFaces.Extent() + 1 );
+    TopTools_IndexedMapOfShape* facesOfSide = new TopTools_IndexedMapOfShape[ faceEdgesVec.size() ];
+    SMESHUtils::ArrayDeleter<TopTools_IndexedMapOfShape> delFacesOfSide( facesOfSide );
+
+    // try to use each face as a bottom one
+    bool prismDetected = false;
+    for ( int iF = 1; iF < allFaces.Extent() && !prismDetected; ++iF )
+    {
+      const TopoDS_Face& botF = TopoDS::Face( allFaces( iF ));
+
+      TEdgeWithNeighborsVec& botEdges = faceEdgesVec[ iF ];
+      if ( botEdges.empty() )
+      {
+        if ( !getEdges( botF, botEdges, /*noHoles=*/false ))
+          break;
+        if ( allFaces.Extent()-1 <= (int) botEdges.size() )
+          continue; // all faces are adjacent to botF - no top FACE
+      }
+      // init data of side FACEs
+      vector< PrismSide > sides( botEdges.size() );
+      for ( int iS = 0; iS < botEdges.size(); ++iS )
+      {
+        sides[ iS ]._topEdge = botEdges[ iS ]._edge;
+        sides[ iS ]._face    = botF;
+        sides[ iS ]._leftSide  = & sides[ botEdges[ iS ]._iR ];
+        sides[ iS ]._rightSide = & sides[ botEdges[ iS ]._iL ];
+        sides[ iS ]._faces = & facesOfSide[ iS ];
+        sides[ iS ]._faces->Clear();
+      }
+
+      bool isOK = true; // ok for a current botF
+      bool isAdvanced = true;
+      int nbFoundSideFaces = 0;
+      for ( int iLoop = 0; isOK && isAdvanced; ++iLoop )
+      {
+        isAdvanced = false;
+        for ( size_t iS = 0; iS < sides.size() && isOK; ++iS )
+        {
+          PrismSide& side = sides[ iS ];
+          if ( side._face.IsNull() )
+            continue;
+          if ( side._topEdge.IsNull() )
+          {
+            // find vertical EDGEs --- EGDEs shared with neighbor side FACEs
+            for ( int is2nd = 0; is2nd < 2 && isOK; ++is2nd ) // 2 adjacent neighbors
+            {
+              int di = is2nd ? 1 : -1;
+              const PrismSide* adjSide = is2nd ? side._rightSide : side._leftSide;
+              for ( size_t i = 1; i < side._edges->size(); ++i )
+              {
+                int iE = SMESH_MesherHelper::WrapIndex( i*di + side._iBotEdge, side._edges->size());
+                if ( side._isCheckedEdge[ iE ] ) continue;
+                const TopoDS_Edge&      vertE = side.Edge( iE );
+                const TopoDS_Shape& neighborF = getAnotherFace( side._face, vertE, facesOfEdge );
+                bool isEdgeShared = adjSide->_faces->Contains( neighborF );
+                if ( isEdgeShared )
+                {
+                  isAdvanced = true;
+                  side._isCheckedEdge[ iE ] = true;
+                  side._nbCheckedEdges++;
+                  int nbNotCheckedE = side._edges->size() - side._nbCheckedEdges;
+                  if ( nbNotCheckedE == 1 )
+                    break;
+                }
+                else
+                {
+                  if ( i == 1 && iLoop == 0 ) isOK = false;
+                  break;
+                }
+              }
+            }
+            // find a top EDGE
+            int nbNotCheckedE = side._edges->size() - side._nbCheckedEdges;
+            if ( nbNotCheckedE == 1 )
+            {
+              vector<bool>::iterator ii = std::find( side._isCheckedEdge.begin(),
+                                                     side._isCheckedEdge.end(), false );
+              if ( ii != side._isCheckedEdge.end() )
+              {
+                size_t iE = std::distance( side._isCheckedEdge.begin(), ii );
+                side._topEdge = side.Edge( iE );
+              }
+            }
+            isOK = ( nbNotCheckedE >= 1 );
+          }
+          else //if ( !side._topEdge.IsNull() )
+          {
+            // get a next face of a side
+            const TopoDS_Shape& f = getAnotherFace( side._face, side._topEdge, facesOfEdge );
+            side._faces->Add( f );
+            bool stop = false;
+            if ( f.IsSame( side._face ) || // _topEdge is a seam
+                 SMESH_MesherHelper::Count( f, TopAbs_WIRE, false ) != 1 )
+            {
+              stop = true;
+            }
+            else if ( side._leftSide != & side ) // not closed side face
+            {
+              if ( side._leftSide->_faces->Contains( f ))
+              {
+                stop = true;
+                side._leftSide->_face.Nullify();
+                side._leftSide->_topEdge.Nullify();
+              }
+              if ( side._rightSide->_faces->Contains( f ))
+              {
+                stop = true;
+                side._rightSide->_face.Nullify();
+                side._rightSide->_topEdge.Nullify();
+              }
+            }
+            if ( stop )
+            {
+              side._face.Nullify();
+              side._topEdge.Nullify();
+              continue;
+            }
+            side._face = TopoDS::Face( f );
+            int faceID = allFaces.FindIndex( side._face );
+            side._edges = & faceEdgesVec[ faceID ];
+            if ( side._edges->empty() )
+              if ( !getEdges( side._face, * side._edges, /*noHoles=*/true ))
+                break;
+            const int nbE = side._edges->size();
+            if ( nbE >= 4 )
+            {
+              isAdvanced = true;
+              ++nbFoundSideFaces;
+              side._iBotEdge = side.FindEdge( side._topEdge );
+              side._isCheckedEdge.clear();
+              side._isCheckedEdge.resize( nbE, false );
+              side._isCheckedEdge[ side._iBotEdge ] = true;
+              side._nbCheckedEdges = 1; // bottom EDGE is known
+            }
+            side._topEdge.Nullify();
+            isOK = ( !side._edges->empty() || side._faces->Extent() > 1 );
+
+          } //if ( !side._topEdge.IsNull() )
+
+        } // loop on prism sides
+
+        if ( nbFoundSideFaces > allFaces.Extent() )
+        {
+          isOK = false;
+        }
+        if ( iLoop > allFaces.Extent() * 10 )
+        {
+          isOK = false;
+#ifdef _DEBUG_
+          cerr << "BUG: infinite loop in StdMeshers_Prism_3D::IsApplicable()" << endl;
+#endif
+        }
+      } // while isAdvanced
+
+      if ( isOK && sides[0]._faces->Extent() > 1 )
+      {
+        const int nbFaces = sides[0]._faces->Extent();
+        if ( botEdges.size() == 1 ) // cylinder
+        {
+          prismDetected = ( nbFaces == allFaces.Extent()-1 );
+        }
+        else
+        {
+          const TopoDS_Shape& topFace = sides[0]._faces->FindKey( nbFaces );
+          size_t iS;
+          for ( iS = 1; iS < sides.size(); ++iS )
+            if ( !sides[ iS ]._faces->Contains( topFace ))
+              break;
+          prismDetected = ( iS == sides.size() );
+        }
+      }
+    } // loop on allFaces
+
+    if ( !prismDetected && toCheckAll ) return false;
+    if ( prismDetected && !toCheckAll ) return true;
+
+  } // loop on solids
+
+  return toCheckAll;
+}
+
 namespace Prism_3D
 {
   //================================================================================
@@ -1862,6 +2597,29 @@ namespace Prism_3D
     myWallQuads.clear();
   }
 
+  //================================================================================
+  /*!
+   * \brief Set upside-down
+   */
+  //================================================================================
+
+  void TPrismTopo::SetUpsideDown()
+  {
+    std::swap( myBottom, myTop );
+    myBottomEdges.clear();
+    std::reverse( myBottomEdges.begin(), myBottomEdges.end() );
+    for ( size_t i = 0; i < myWallQuads.size(); ++i )
+    {
+      myWallQuads[i].reverse();
+      TQuadList::iterator q = myWallQuads[i].begin();
+      for ( ; q != myWallQuads[i].end(); ++q )
+      {
+        (*q)->shift( 2, /*keepUnitOri=*/true );
+      }
+      myBottomEdges.push_back( myWallQuads[i].front()->side[ QUAD_BOTTOM_SIDE ].grid->Edge(0) );
+    }
+  }
+
 } // namespace Prism_3D
 
 //================================================================================
@@ -1968,7 +2726,7 @@ bool StdMeshers_Prism_3D::initPrism(Prism_3D::TPrismTopo& thePrism,
   SMESH_subMesh * botSM = 0;
   SMESH_subMesh * topSM = 0;
 
-  if ( hasNotQuad ) // can chose a bottom FACE
+  if ( hasNotQuad ) // can choose a bottom FACE
   {
     if ( nbNotQuadMeshed > 0 ) botSM = notQuadElemSubMesh.front();
     else                       botSM = notQuadGeomSubMesh.front();
@@ -2002,7 +2760,7 @@ bool StdMeshers_Prism_3D::initPrism(Prism_3D::TPrismTopo& thePrism,
     if ( botSM ) {
       if ( ! botSM->GetSubShape().IsSame( thePrism.myBottom )) {
         std::swap( botSM, topSM );
-        if ( ! botSM->GetSubShape().IsSame( thePrism.myBottom ))
+        if ( !botSM || ! botSM->GetSubShape().IsSame( thePrism.myBottom ))
           return toSM( error( COMPERR_BAD_INPUT_MESH,
                               "Incompatible non-structured sub-meshes"));
       }
@@ -2076,6 +2834,12 @@ bool StdMeshers_Prism_3D::initPrism(Prism_3D::TPrismTopo& thePrism,
                       "Non-quadrilateral faces are not opposite"));
   }
 
+  if ( thePrism.myBottomEdges.size() > thePrism.myWallQuads.size() )
+  {
+    // composite bottom sides => set thePrism upside-down
+    thePrism.SetUpsideDown();
+  }
+
   return true;
 }
 
@@ -2083,7 +2847,7 @@ bool StdMeshers_Prism_3D::initPrism(Prism_3D::TPrismTopo& thePrism,
 /*!
  * \brief Initialization.
  * \param helper - helper loaded with mesh and 3D shape
- * \param thePrism - a prosm data
+ * \param thePrism - a prism data
  * \retval bool - false if a mesh or a shape are KO
  */
 //================================================================================
@@ -2091,15 +2855,17 @@ bool StdMeshers_Prism_3D::initPrism(Prism_3D::TPrismTopo& thePrism,
 bool StdMeshers_PrismAsBlock::Init(SMESH_MesherHelper*         helper,
                                    const Prism_3D::TPrismTopo& thePrism)
 {
+  myHelper = helper;
+  SMESHDS_Mesh* meshDS = myHelper->GetMeshDS();
+  SMESH_Mesh*     mesh = myHelper->GetMesh();
+
   if ( mySide ) {
     delete mySide; mySide = 0;
   }
   vector< TSideFace* >         sideFaces( NB_WALL_FACES, 0 );
   vector< pair< double, double> > params( NB_WALL_FACES );
-  mySide = new TSideFace( sideFaces, params );
+  mySide = new TSideFace( *mesh, sideFaces, params );
 
-  myHelper = helper;
-  SMESHDS_Mesh* meshDS = myHelper->GetMeshDS();
 
   SMESH_Block::init();
   myShapeIDMap.Clear();
@@ -2121,9 +2887,16 @@ bool StdMeshers_PrismAsBlock::Init(SMESH_MesherHelper*         helper,
   myParam2ColumnMaps.resize( thePrism.myBottomEdges.size() ); // total nb edges
 
   size_t iE, nbEdges = thePrism.myNbEdgesInWires.front(); // nb outer edges
-  vector< double >    edgeLength( nbEdges );
+  vector< double > edgeLength( nbEdges );
   multimap< double, int > len2edgeMap;
 
+  // for each EDGE: either split into several parts, or join with several next EDGEs
+  vector<int> nbSplitPerEdge( nbEdges, 0 );
+  vector<int> nbUnitePerEdge( nbEdges, 0 ); // -1 means "joined to a previous"
+
+  // consider continuous straight EDGEs as one side
+  const int nbSides = countNbSides( thePrism, nbUnitePerEdge, edgeLength );
+
   list< TopoDS_Edge >::const_iterator edgeIt = thePrism.myBottomEdges.begin();
   for ( iE = 0; iE < nbEdges; ++iE, ++edgeIt )
   {
@@ -2132,7 +2905,7 @@ bool StdMeshers_PrismAsBlock::Init(SMESH_MesherHelper*         helper,
     Prism_3D::TQuadList::const_iterator quad = thePrism.myWallQuads[ iE ].begin();
     for ( ; quad != thePrism.myWallQuads[ iE ].end(); ++quad )
     {
-      const TopoDS_Edge& quadBot = (*quad)->side[ QUAD_BOTTOM_SIDE ]->Edge( 0 );
+      const TopoDS_Edge& quadBot = (*quad)->side[ QUAD_BOTTOM_SIDE ].grid->Edge( 0 );
       if ( !myHelper->LoadNodeColumns( faceColumns, (*quad)->face, quadBot, meshDS ))
         return error(COMPERR_BAD_INPUT_MESH, TCom("Can't find regular quadrangle mesh ")
                      << "on a side face #" << MeshDS()->ShapeToIndex( (*quad)->face ));
@@ -2141,16 +2914,8 @@ bool StdMeshers_PrismAsBlock::Init(SMESH_MesherHelper*         helper,
     SHOWYXZ("p2 F "   <<iE, gpXYZ(faceColumns.rbegin()->second.front() ));
     SHOWYXZ("V First "<<iE, BRep_Tool::Pnt( TopExp::FirstVertex(*edgeIt,true )));
 
-    edgeLength[ iE ] = SMESH_Algo::EdgeLength( *edgeIt );
-
-    if ( nbEdges < NB_WALL_FACES ) // fill map used to split faces
-    {
-      SMESHDS_SubMesh* smDS = meshDS->MeshElements( *edgeIt);
-      if ( !smDS )
-        return error(COMPERR_BAD_INPUT_MESH, TCom("Null submesh on the edge #")
-                     << MeshDS()->ShapeToIndex( *edgeIt ));
-      len2edgeMap.insert( make_pair( edgeLength[ iE ], iE ));
-    }
+    if ( nbSides < NB_WALL_FACES ) // fill map used to split faces
+      len2edgeMap.insert( make_pair( edgeLength[ iE ], iE )); // sort edges by length
   }
   // Load columns of internal edges (forming holes)
   // and fill map ShapeIndex to TParam2ColumnMap for them
@@ -2161,7 +2926,7 @@ bool StdMeshers_PrismAsBlock::Init(SMESH_MesherHelper*         helper,
     Prism_3D::TQuadList::const_iterator quad = thePrism.myWallQuads[ iE ].begin();
     for ( ; quad != thePrism.myWallQuads[ iE ].end(); ++quad )
     {
-      const TopoDS_Edge& quadBot = (*quad)->side[ QUAD_BOTTOM_SIDE ]->Edge( 0 );
+      const TopoDS_Edge& quadBot = (*quad)->side[ QUAD_BOTTOM_SIDE ].grid->Edge( 0 );
       if ( !myHelper->LoadNodeColumns( faceColumns, (*quad)->face, quadBot, meshDS ))
         return error(COMPERR_BAD_INPUT_MESH, TCom("Can't find regular quadrangle mesh ")
                      << "on a side face #" << MeshDS()->ShapeToIndex( (*quad)->face ));
@@ -2188,95 +2953,122 @@ bool StdMeshers_PrismAsBlock::Init(SMESH_MesherHelper*         helper,
   // Create 4 wall faces of a block
   // -------------------------------
 
-  if ( nbEdges <= NB_WALL_FACES ) // ************* Split faces if necessary
+  if ( nbSides <= NB_WALL_FACES ) // ************* Split faces if necessary
   {
-    map< int, int > iE2nbSplit;
-    if ( nbEdges != NB_WALL_FACES ) // define how to split
+    if ( nbSides != NB_WALL_FACES ) // define how to split
     {
       if ( len2edgeMap.size() != nbEdges )
         RETURN_BAD_RESULT("Uniqueness of edge lengths not assured");
-      map< double, int >::reverse_iterator maxLen_i = len2edgeMap.rbegin();
-      map< double, int >::reverse_iterator midLen_i = ++len2edgeMap.rbegin();
+
+      multimap< double, int >::reverse_iterator maxLen_i = len2edgeMap.rbegin();
+      multimap< double, int >::reverse_iterator midLen_i = ++len2edgeMap.rbegin();
+
       double maxLen = maxLen_i->first;
       double midLen = ( len2edgeMap.size() == 1 ) ? 0 : midLen_i->first;
       switch ( nbEdges ) {
       case 1: // 0-th edge is split into 4 parts
-        iE2nbSplit.insert( make_pair( 0, 4 )); break;
+        nbSplitPerEdge[ 0 ] = 4;
+        break;
       case 2: // either the longest edge is split into 3 parts, or both edges into halves
         if ( maxLen / 3 > midLen / 2 ) {
-          iE2nbSplit.insert( make_pair( maxLen_i->second, 3 ));
+          nbSplitPerEdge[ maxLen_i->second ] = 3;
         }
         else {
-          iE2nbSplit.insert( make_pair( maxLen_i->second, 2 ));
-          iE2nbSplit.insert( make_pair( midLen_i->second, 2 ));
+          nbSplitPerEdge[ maxLen_i->second ] = 2;
+          nbSplitPerEdge[ midLen_i->second ] = 2;
         }
         break;
       case 3:
-        // split longest into halves
-        iE2nbSplit.insert( make_pair( maxLen_i->second, 2 ));
+        if ( nbSides == 2 )
+          // split longest into 3 parts
+          nbSplitPerEdge[ maxLen_i->second ] = 3;
+        else
+          // split longest into halves
+          nbSplitPerEdge[ maxLen_i->second ] = 2;
       }
     }
-    // Create TSideFace's
-    int iSide = 0;
-    list< TopoDS_Edge >::const_iterator botE = thePrism.myBottomEdges.begin();
-    for ( iE = 0; iE < nbEdges; ++iE, ++botE )
-    {
-      TFaceQuadStructPtr quad = thePrism.myWallQuads[ iE ].front();
-      // split?
-      map< int, int >::iterator i_nb = iE2nbSplit.find( iE );
-      if ( i_nb != iE2nbSplit.end() ) {
-        // split!
-        int nbSplit = i_nb->second;
-        vector< double > params;
-        splitParams( nbSplit, &myParam2ColumnMaps[ iE ], params );
-        const bool isForward =
-          StdMeshers_PrismAsBlock::IsForwardEdge( myHelper->GetMeshDS(),
-                                                  myParam2ColumnMaps[iE],
-                                                  *botE, SMESH_Block::ID_Fx0z );
-        for ( int i = 0; i < nbSplit; ++i ) {
-          double f = ( isForward ? params[ i ]   : params[ nbSplit - i-1 ]);
-          double l = ( isForward ? params[ i+1 ] : params[ nbSplit - i ]);
-          TSideFace* comp = new TSideFace( myHelper, wallFaceIds[ iSide ],
-                                           thePrism.myWallQuads[ iE ], *botE,
-                                           &myParam2ColumnMaps[ iE ], f, l );
-          mySide->SetComponent( iSide++, comp );
-        }
+  }
+  else // **************************** Unite faces
+  {
+    int nbExraFaces = nbSides - 4; // nb of faces to fuse
+    for ( iE = 0; iE < nbEdges; ++iE )
+    {
+      if ( nbUnitePerEdge[ iE ] < 0 )
+        continue;
+      // look for already united faces
+      for ( int i = iE; i < iE + nbExraFaces; ++i )
+      {
+        if ( nbUnitePerEdge[ i ] > 0 ) // a side including nbUnitePerEdge[i]+1 edge
+          nbExraFaces += nbUnitePerEdge[ i ];
+        nbUnitePerEdge[ i ] = -1;
       }
-      else {
-        TSideFace* comp = new TSideFace( myHelper, wallFaceIds[ iSide ],
+      nbUnitePerEdge[ iE ] = nbExraFaces;
+      break;
+    }
+  }
+
+  // Create TSideFace's
+  int iSide = 0;
+  list< TopoDS_Edge >::const_iterator botE = thePrism.myBottomEdges.begin();
+  for ( iE = 0; iE < nbEdges; ++iE, ++botE )
+  {
+    TFaceQuadStructPtr quad = thePrism.myWallQuads[ iE ].front();
+    const int       nbSplit = nbSplitPerEdge[ iE ];
+    const int   nbExraFaces = nbUnitePerEdge[ iE ] + 1;
+    if ( nbSplit > 0 ) // split
+    {
+      vector< double > params;
+      splitParams( nbSplit, &myParam2ColumnMaps[ iE ], params );
+      const bool isForward =
+        StdMeshers_PrismAsBlock::IsForwardEdge( myHelper->GetMeshDS(),
+                                                myParam2ColumnMaps[iE],
+                                                *botE, SMESH_Block::ID_Fx0z );
+      for ( int i = 0; i < nbSplit; ++i ) {
+        double f = ( isForward ? params[ i ]   : params[ nbSplit - i-1 ]);
+        double l = ( isForward ? params[ i+1 ] : params[ nbSplit - i ]);
+        TSideFace* comp = new TSideFace( *mesh, wallFaceIds[ iSide ],
                                          thePrism.myWallQuads[ iE ], *botE,
-                                         &myParam2ColumnMaps[ iE ]);
+                                         &myParam2ColumnMaps[ iE ], f, l );
         mySide->SetComponent( iSide++, comp );
       }
     }
-  }
-  else { // **************************** Unite faces
-
-    // unite first faces
-    int nbExraFaces = nbEdges - 3;
-    int iSide = 0, iE;
-    double u0 = 0, sumLen = 0;
-    for ( iE = 0; iE < nbExraFaces; ++iE )
-      sumLen += edgeLength[ iE ];
-
-    vector< TSideFace* >        components( nbExraFaces );
-    vector< pair< double, double> > params( nbExraFaces );
-    list< TopoDS_Edge >::const_iterator botE = thePrism.myBottomEdges.begin();
-    for ( iE = 0; iE < nbExraFaces; ++iE, ++botE )
+    else if ( nbExraFaces > 1 ) // unite
+    {
+      double u0 = 0, sumLen = 0;
+      for ( int i = iE; i < iE + nbExraFaces; ++i )
+        sumLen += edgeLength[ i ];
+
+      vector< TSideFace* >        components( nbExraFaces );
+      vector< pair< double, double> > params( nbExraFaces );
+      bool endReached = false;
+      for ( int i = 0; i < nbExraFaces; ++i, ++botE, ++iE )
+      {
+        if ( iE == nbEdges )
+        {
+          endReached = true;
+          botE = thePrism.myBottomEdges.begin();
+          iE = 0;
+        }
+        components[ i ] = new TSideFace( *mesh, wallFaceIds[ iSide ],
+                                         thePrism.myWallQuads[ iE ], *botE,
+                                         &myParam2ColumnMaps[ iE ]);
+        double u1 = u0 + edgeLength[ iE ] / sumLen;
+        params[ i ] = make_pair( u0 , u1 );
+        u0 = u1;
+      }
+      TSideFace* comp = new TSideFace( *mesh, components, params );
+      mySide->SetComponent( iSide++, comp );
+      if ( endReached )
+        break;
+      --iE; // for increment in an external loop on iE
+      --botE;
+    }
+    else if ( nbExraFaces < 0 ) // skip already united face
     {
-      components[ iE ] = new TSideFace( myHelper, wallFaceIds[ iSide ],
-                                        thePrism.myWallQuads[ iE ], *botE,
-                                        &myParam2ColumnMaps[ iE ]);
-      double u1 = u0 + edgeLength[ iE ] / sumLen;
-      params[ iE ] = make_pair( u0 , u1 );
-      u0 = u1;
     }
-    mySide->SetComponent( iSide++, new TSideFace( components, params ));
-
-    // fill the rest faces
-    for ( ; iE < nbEdges; ++iE, ++botE )
+    else // use as is
     {
-      TSideFace* comp = new TSideFace( myHelper, wallFaceIds[ iSide ],
+      TSideFace* comp = new TSideFace( *mesh, wallFaceIds[ iSide ],
                                        thePrism.myWallQuads[ iE ], *botE,
                                        &myParam2ColumnMaps[ iE ]);
       mySide->SetComponent( iSide++, comp );
@@ -2361,6 +3153,8 @@ bool StdMeshers_PrismAsBlock::Init(SMESH_MesherHelper*         helper,
     tFace.Set( ID_TOP_FACE, new BRepAdaptor_Surface( thePrism.myTop ), topPcurves, isForward );
     SMESH_Block::Insert( thePrism.myTop, ID_TOP_FACE, myShapeIDMap );
   }
+  //faceGridToPythonDump( SMESH_Block::ID_Fxy0, 50 );
+  //faceGridToPythonDump( SMESH_Block::ID_Fxy1 );
 
   // Fill map ShapeIndex to TParam2ColumnMap
   // ----------------------------------------
@@ -2392,15 +3186,26 @@ bool StdMeshers_PrismAsBlock::Init(SMESH_MesherHelper*         helper,
     }
   }
 
-//   gp_XYZ testPar(0.25, 0.25, 0), testCoord;
-//   if ( !FacePoint( ID_BOT_FACE, testPar, testCoord ))
-//     RETURN_BAD_RESULT("TEST FacePoint() FAILED");
-//   SHOWYXZ("IN TEST PARAM" , testPar);
-//   SHOWYXZ("OUT TEST CORD" , testCoord);
-//   if ( !ComputeParameters( testCoord, testPar , ID_BOT_FACE))
-//     RETURN_BAD_RESULT("TEST ComputeParameters() FAILED");
-//   SHOWYXZ("OUT TEST PARAM" , testPar);
-
+// #define SHOWYXZ(msg, xyz) {                     \
+//     gp_Pnt p (xyz);                                                     \
+//     cout << msg << " ("<< p.X() << "; " <<p.Y() << "; " <<p.Z() << ") " <<endl; \
+//   }
+//   double _u[]={ 0.1, 0.1, 0.9, 0.9 };
+//   double _v[]={ 0.1, 0.9, 0.1, 0.9 };
+//   for ( int z = 0; z < 2; ++z )
+//     for ( int i = 0; i < 4; ++i )
+//     {
+//       //gp_XYZ testPar(0.25, 0.25, 0), testCoord;
+//       int iFace = (z ? ID_TOP_FACE : ID_BOT_FACE);
+//       gp_XYZ testPar(_u[i], _v[i], z), testCoord;
+//       if ( !FacePoint( iFace, testPar, testCoord ))
+//         RETURN_BAD_RESULT("TEST FacePoint() FAILED");
+//       SHOWYXZ("IN TEST PARAM" , testPar);
+//       SHOWYXZ("OUT TEST CORD" , testCoord);
+//       if ( !ComputeParameters( testCoord, testPar , iFace))
+//         RETURN_BAD_RESULT("TEST ComputeParameters() FAILED");
+//       SHOWYXZ("OUT TEST PARAM" , testPar);
+//     }
   return true;
 }
 
@@ -2433,14 +3238,17 @@ const TNodeColumn* StdMeshers_PrismAsBlock::GetNodeColumn(const SMDS_MeshNode* n
 //purpose  : Return transformations to get coordinates of nodes of each layer
 //           by nodes of the bottom. Layer is a set of nodes at a certain step
 //           from bottom to top.
+//           Transformation to get top node from bottom ones is computed
+//           only if the top FACE is not meshed.
 //=======================================================================
 
 bool StdMeshers_PrismAsBlock::GetLayersTransformation(vector<gp_Trsf> &           trsf,
                                                       const Prism_3D::TPrismTopo& prism) const
 {
+  const bool itTopMeshed = !SubMesh( ID_BOT_FACE )->IsEmpty();
   const int zSize = VerticalSize();
-  if ( zSize < 3 ) return true;
-  trsf.resize( zSize - 2 );
+  if ( zSize < 3 && !itTopMeshed ) return true;
+  trsf.resize( zSize - 1 );
 
   // Select some node columns by which we will define coordinate system of layers
 
@@ -2450,7 +3258,7 @@ bool StdMeshers_PrismAsBlock::GetLayersTransformation(vector<gp_Trsf> &
     list< TopoDS_Edge >::const_iterator edgeIt = prism.myBottomEdges.begin();
     for ( int iE = 0; iE < prism.myNbEdgesInWires.front(); ++iE, ++edgeIt )
     {
-      if ( BRep_Tool::Degenerated( *edgeIt )) continue;
+      if ( SMESH_Algo::isDegenerated( *edgeIt )) continue;
       const TParam2ColumnMap* u2colMap =
         GetParam2ColumnMap( MeshDS()->ShapeToIndex( *edgeIt ), isReverse );
       if ( !u2colMap ) return false;
@@ -2485,7 +3293,7 @@ bool StdMeshers_PrismAsBlock::GetLayersTransformation(vector<gp_Trsf> &
   gp_Ax3 cs0 = getLayerCoordSys(0, columns, xCol );
   //double dist0 = cs0.Location().Distance( gpXYZ( (*columns[0])[0]));
   toCs0.SetTransformation( cs0 );
-  for ( int z = 1; z < zSize-1; ++z )
+  for ( int z = 1; z < zSize; ++z )
   {
     gp_Ax3 csZ = getLayerCoordSys(z, columns, xCol );
     //double distZ = csZ.Location().Distance( gpXYZ( (*columns[0])[z]));
@@ -2502,7 +3310,10 @@ bool StdMeshers_PrismAsBlock::GetLayersTransformation(vector<gp_Trsf> &
       gp_Pnt pz = gpXYZ( (*columns[i])[z] );
       t.Transforms( p0.ChangeCoord() );
       if ( p0.SquareDistance( pz ) > tol2 )
-        return false;
+      {
+        t = gp_Trsf();
+        return ( z == zSize - 1 ); // OK if fails only botton->top trsf
+      }
     }
   }
   return true;
@@ -2542,6 +3353,54 @@ bool StdMeshers_PrismAsBlock::IsForwardEdge(SMESHDS_Mesh*           meshDS,
   return isForward;
 }
 
+//=======================================================================
+//function : faceGridToPythonDump
+//purpose  : Prints a script creating a normal grid on the prism side
+//=======================================================================
+
+void StdMeshers_PrismAsBlock::faceGridToPythonDump(const SMESH_Block::TShapeID face,
+                                                   const int                   nb)
+{
+#ifdef _DEBUG_
+  gp_XYZ pOnF[6] = { gp_XYZ(0,0,0), gp_XYZ(0,0,1),
+                     gp_XYZ(0,0,0), gp_XYZ(0,1,0),
+                     gp_XYZ(0,0,0), gp_XYZ(1,0,0) };
+  gp_XYZ p2;
+  cout << "mesh = smesh.Mesh( 'Face " << face << "')" << endl;
+  SMESH_Block::TFace& f = myFace[ face - ID_FirstF ];
+  gp_XYZ params = pOnF[ face - ID_FirstF ];
+  //const int nb = 10; // nb face rows
+  for ( int j = 0; j <= nb; ++j )
+  {
+    params.SetCoord( f.GetVInd(), double( j )/ nb );
+    for ( int i = 0; i <= nb; ++i )
+    {
+      params.SetCoord( f.GetUInd(), double( i )/ nb );
+      gp_XYZ p = f.Point( params );
+      gp_XY uv = f.GetUV( params );
+      cout << "mesh.AddNode( " << p.X() << ", " << p.Y() << ", " << p.Z() << " )"
+           << " # " << 1 + i + j * ( nb + 1 )
+           << " ( " << i << ", " << j << " ) "
+           << " UV( " << uv.X() << ", " << uv.Y() << " )" << endl;
+      ShellPoint( params, p2 );
+      double dist = ( p2 - p ).Modulus();
+      if ( dist > 1e-4 )
+        cout << "#### dist from ShellPoint " << dist
+             << " (" << p2.X() << ", " << p2.Y() << ", " << p2.Z() << " ) " << endl;
+    }
+  }
+  for ( int j = 0; j < nb; ++j )
+    for ( int i = 0; i < nb; ++i )
+    {
+      int n = 1 + i + j * ( nb + 1 );
+      cout << "mesh.AddFace([ "
+           << n << ", " << n+1 << ", "
+           << n+nb+2 << ", " << n+nb+1 << "]) " << endl;
+    }
+  
+#endif
+}
+
 //================================================================================
 /*!
  * \brief Constructor
@@ -2554,7 +3413,7 @@ bool StdMeshers_PrismAsBlock::IsForwardEdge(SMESHDS_Mesh*           meshDS,
  */
 //================================================================================
 
-StdMeshers_PrismAsBlock::TSideFace::TSideFace(SMESH_MesherHelper*        helper,
+StdMeshers_PrismAsBlock::TSideFace::TSideFace(SMESH_Mesh&                mesh,
                                               const int                  faceID,
                                               const Prism_3D::TQuadList& quadList,
                                               const TopoDS_Edge&         baseEdge,
@@ -2563,20 +3422,22 @@ StdMeshers_PrismAsBlock::TSideFace::TSideFace(SMESH_MesherHelper*        helper,
                                               const double               last):
   myID( faceID ),
   myParamToColumnMap( columnsMap ),
-  myHelper( helper )
+  myHelper( mesh )
 {
   myParams.resize( 1 );
   myParams[ 0 ] = make_pair( first, last );
   mySurface     = PSurface( new BRepAdaptor_Surface( quadList.front()->face ));
   myBaseEdge    = baseEdge;
-  myIsForward   = StdMeshers_PrismAsBlock::IsForwardEdge( myHelper->GetMeshDS(),
+  myIsForward   = StdMeshers_PrismAsBlock::IsForwardEdge( myHelper.GetMeshDS(),
                                                           *myParamToColumnMap,
                                                           myBaseEdge, myID );
+  myHelper.SetSubShape( quadList.front()->face );
+
   if ( quadList.size() > 1 ) // side is vertically composite
   {
     // fill myShapeID2Surf map to enable finding a right surface by any sub-shape ID
 
-    SMESHDS_Mesh* meshDS = myHelper->GetMeshDS();
+    SMESHDS_Mesh* meshDS = myHelper.GetMeshDS();
 
     TopTools_IndexedDataMapOfShapeListOfShape subToFaces;
     Prism_3D::TQuadList::const_iterator quad = quadList.begin();
@@ -2601,20 +3462,34 @@ StdMeshers_PrismAsBlock::TSideFace::TSideFace(SMESH_MesherHelper*        helper,
 
 //================================================================================
 /*!
- * \brief Constructor of complex side face
+ * \brief Constructor of complex side face
  */
 //================================================================================
 
 StdMeshers_PrismAsBlock::TSideFace::
-TSideFace(const vector< TSideFace* >&             components,
+TSideFace(SMESH_Mesh&                             mesh,
+          const vector< TSideFace* >&             components,
           const vector< pair< double, double> > & params)
   :myID( components[0] ? components[0]->myID : 0 ),
    myParamToColumnMap( 0 ),
    myParams( params ),
    myIsForward( true ),
    myComponents( components ),
-   myHelper( components[0] ? components[0]->myHelper : 0 )
-{}
+   myHelper( mesh )
+{
+  if ( myID == ID_Fx1z || myID == ID_F0yz )
+  {
+    // reverse components
+    std::reverse( myComponents.begin(), myComponents.end() );
+    std::reverse( myParams.begin(),     myParams.end() );
+    for ( size_t i = 0; i < myParams.size(); ++i )
+    {
+      const double f = myParams[i].first;
+      const double l = myParams[i].second;
+      myParams[i] = make_pair( 1. - l, 1. - f );
+    }
+  }
+}
 //================================================================================
 /*!
  * \brief Copy constructor
@@ -2622,17 +3497,17 @@ TSideFace(const vector< TSideFace* >&             components,
  */
 //================================================================================
 
-StdMeshers_PrismAsBlock::TSideFace::TSideFace( const TSideFace& other )
+StdMeshers_PrismAsBlock::TSideFace::TSideFace( const TSideFace& other ):
+  myID               ( other.myID ),
+  myParamToColumnMap ( other.myParamToColumnMap ),
+  mySurface          ( other.mySurface ),
+  myBaseEdge         ( other.myBaseEdge ),
+  myShapeID2Surf     ( other.myShapeID2Surf ),
+  myParams           ( other.myParams ),
+  myIsForward        ( other.myIsForward ),
+  myComponents       ( other.myComponents.size() ),
+  myHelper           ( *other.myHelper.GetMesh() )
 {
-  myID               = other.myID;
-  mySurface          = other.mySurface;
-  myBaseEdge         = other.myBaseEdge;
-  myParams           = other.myParams;
-  myIsForward        = other.myIsForward;
-  myHelper           = other.myHelper;
-  myParamToColumnMap = other.myParamToColumnMap;
-
-  myComponents.resize( other.myComponents.size());
   for (int i = 0 ; i < myComponents.size(); ++i )
     myComponents[ i ] = new TSideFace( *other.myComponents[ i ]);
 }
@@ -2795,6 +3670,52 @@ double StdMeshers_PrismAsBlock::TSideFace::GetColumns(const double      U,
   return r;
 }
 
+//================================================================================
+/*!
+ * \brief Return all nodes at a given height together with their normalized parameters
+ *  \param [in] Z - the height of interest
+ *  \param [out] nodes - map of parameter to node
+ */
+//================================================================================
+
+void StdMeshers_PrismAsBlock::
+TSideFace::GetNodesAtZ(const int Z,
+                       map<double, const SMDS_MeshNode* >& nodes ) const
+{
+  if ( !myComponents.empty() )
+  {
+    double u0 = 0.;
+    for ( size_t i = 0; i < myComponents.size(); ++i )
+    {
+      map<double, const SMDS_MeshNode* > nn;
+      myComponents[i]->GetNodesAtZ( Z, nn );
+      map<double, const SMDS_MeshNode* >::iterator u2n = nn.begin();
+      if ( !nodes.empty() && nodes.rbegin()->second == u2n->second )
+        ++u2n;
+      const double uRange = myParams[i].second - myParams[i].first;
+      for ( ; u2n != nn.end(); ++u2n )
+        nodes.insert( nodes.end(), make_pair( u0 + uRange * u2n->first, u2n->second ));
+      u0 += uRange;
+    }
+  }
+  else
+  {
+    double f = myParams[0].first, l = myParams[0].second;
+    if ( !myIsForward )
+      std::swap( f, l );
+    const double uRange = l - f;
+    if ( Abs( uRange ) < std::numeric_limits<double>::min() )
+      return;
+    TParam2ColumnIt u2col = getColumn( myParamToColumnMap, myParams[0].first + 1e-3 );
+    for ( ; u2col != myParamToColumnMap->end(); ++u2col )
+      if ( u2col->first > myParams[0].second + 1e-9 )
+        break;
+      else
+        nodes.insert( nodes.end(),
+                      make_pair( ( u2col->first - f ) / uRange, u2col->second[ Z ] ));
+  }
+}
+
 //================================================================================
 /*!
  * \brief Return coordinates by normalized params
@@ -2835,16 +3756,16 @@ gp_Pnt StdMeshers_PrismAsBlock::TSideFace::Value(const Standard_Real U,
     }
     else
     {
-      TopoDS_Shape s = myHelper->GetSubShapeByNode( nn[0], myHelper->GetMeshDS() );
+      TopoDS_Shape s = myHelper.GetSubShapeByNode( nn[0], myHelper.GetMeshDS() );
       if ( s.ShapeType() != TopAbs_EDGE )
-        s = myHelper->GetSubShapeByNode( nn[2], myHelper->GetMeshDS() );
+        s = myHelper.GetSubShapeByNode( nn[2], myHelper.GetMeshDS() );
       if ( s.ShapeType() == TopAbs_EDGE )
         edge = TopoDS::Edge( s );
     }
     if ( !edge.IsNull() )
     {
-      double u1 = myHelper->GetNodeU( edge, nn[0] );
-      double u3 = myHelper->GetNodeU( edge, nn[2] );
+      double u1 = myHelper.GetNodeU( edge, nn[0], nn[2] );
+      double u3 = myHelper.GetNodeU( edge, nn[2], nn[0] );
       double u = u1 * ( 1 - hR ) + u3 * hR;
       TopLoc_Location loc; double f,l;
       Handle(Geom_Curve) curve = BRep_Tool::Curve( edge,loc,f,l );
@@ -2880,10 +3801,10 @@ gp_Pnt StdMeshers_PrismAsBlock::TSideFace::Value(const Standard_Real U,
       }
     if ( notFaceID2 ) // no nodes of FACE and nodes are on different FACEs
     {
-      SMESHDS_Mesh* meshDS = myHelper->GetMeshDS();
-      TopoDS_Shape face = myHelper->GetCommonAncestor( meshDS->IndexToShape( notFaceID1 ),
+      SMESHDS_Mesh* meshDS = myHelper.GetMeshDS();
+      TopoDS_Shape face = myHelper.GetCommonAncestor( meshDS->IndexToShape( notFaceID1 ),
                                                        meshDS->IndexToShape( notFaceID2 ),
-                                                       *myHelper->GetMesh(),
+                                                       *myHelper.GetMesh(),
                                                        TopAbs_FACE );
       if ( face.IsNull() ) 
         throw SALOME_Exception("StdMeshers_PrismAsBlock::TSideFace::Value() face.IsNull()");
@@ -2893,13 +3814,14 @@ gp_Pnt StdMeshers_PrismAsBlock::TSideFace::Value(const Standard_Real U,
         throw SALOME_Exception("StdMeshers_PrismAsBlock::TSideFace::Value() !mySurface");
     }
   }
-  
-  gp_XY uv1 = myHelper->GetNodeUV( mySurface->Face(), nn[0], nn[2]);
-  gp_XY uv2 = myHelper->GetNodeUV( mySurface->Face(), nn[1], nn[3]);
+  ((TSideFace*) this)->myHelper.SetSubShape( mySurface->Face() );
+
+  gp_XY uv1 = myHelper.GetNodeUV( mySurface->Face(), nn[0], nn[2]);
+  gp_XY uv2 = myHelper.GetNodeUV( mySurface->Face(), nn[1], nn[3]);
   gp_XY uv12 = uv1 * ( 1 - vR ) + uv2 * vR;
 
-  gp_XY uv3 = myHelper->GetNodeUV( mySurface->Face(), nn[2], nn[0]);
-  gp_XY uv4 = myHelper->GetNodeUV( mySurface->Face(), nn[3], nn[1]);
+  gp_XY uv3 = myHelper.GetNodeUV( mySurface->Face(), nn[2], nn[0]);
+  gp_XY uv4 = myHelper.GetNodeUV( mySurface->Face(), nn[3], nn[1]);
   gp_XY uv34 = uv3 * ( 1 - vR ) + uv4 * vR;
 
   gp_XY uv = uv12 * ( 1 - hR ) + uv34 * hR;
@@ -2928,7 +3850,7 @@ TopoDS_Edge StdMeshers_PrismAsBlock::TSideFace::GetEdge(const int iEdge) const
   }
   TopoDS_Shape edge;
   const SMDS_MeshNode* node = 0;
-  SMESHDS_Mesh * meshDS = myHelper->GetMesh()->GetMeshDS();
+  SMESHDS_Mesh * meshDS = myHelper.GetMesh()->GetMeshDS();
   TNodeColumn* column;
 
   switch ( iEdge ) {
@@ -2936,7 +3858,7 @@ TopoDS_Edge StdMeshers_PrismAsBlock::TSideFace::GetEdge(const int iEdge) const
   case BOTTOM_EDGE:
     column = & (( ++myParamToColumnMap->begin())->second );
     node = ( iEdge == TOP_EDGE ) ? column->back() : column->front();
-    edge = myHelper->GetSubShapeByNode ( node, meshDS );
+    edge = myHelper.GetSubShapeByNode ( node, meshDS );
     if ( edge.ShapeType() == TopAbs_VERTEX ) {
       column = & ( myParamToColumnMap->begin()->second );
       node = ( iEdge == TOP_EDGE ) ? column->back() : column->front();
@@ -2951,7 +3873,7 @@ TopoDS_Edge StdMeshers_PrismAsBlock::TSideFace::GetEdge(const int iEdge) const
     else
       column = & ( myParamToColumnMap->begin()->second );
     if ( column->size() > 0 )
-      edge = myHelper->GetSubShapeByNode( (*column)[ 1 ], meshDS );
+      edge = myHelper.GetSubShapeByNode( (*column)[ 1 ], meshDS );
     if ( edge.IsNull() || edge.ShapeType() == TopAbs_VERTEX )
       node = column->front();
     break;
@@ -2963,10 +3885,10 @@ TopoDS_Edge StdMeshers_PrismAsBlock::TSideFace::GetEdge(const int iEdge) const
 
   // find edge by 2 vertices
   TopoDS_Shape V1 = edge;
-  TopoDS_Shape V2 = myHelper->GetSubShapeByNode( node, meshDS );
+  TopoDS_Shape V2 = myHelper.GetSubShapeByNode( node, meshDS );
   if ( !V2.IsNull() && V2.ShapeType() == TopAbs_VERTEX && !V2.IsSame( V1 ))
   {
-    TopoDS_Shape ancestor = myHelper->GetCommonAncestor( V1, V2, *myHelper->GetMesh(), TopAbs_EDGE);
+    TopoDS_Shape ancestor = myHelper.GetCommonAncestor( V1, V2, *myHelper.GetMesh(), TopAbs_EDGE);
     if ( !ancestor.IsNull() )
       return TopoDS::Edge( ancestor );
   }
@@ -3006,8 +3928,8 @@ int StdMeshers_PrismAsBlock::TSideFace::InsertSubShapes(TBlockShapes& shapeMap)
   GetColumns(0, col1, col2 );
   const SMDS_MeshNode* node0 = col1->second.front();
   const SMDS_MeshNode* node1 = col1->second.back();
-  TopoDS_Shape v0 = myHelper->GetSubShapeByNode( node0, myHelper->GetMeshDS());
-  TopoDS_Shape v1 = myHelper->GetSubShapeByNode( node1, myHelper->GetMeshDS());
+  TopoDS_Shape v0 = myHelper.GetSubShapeByNode( node0, myHelper.GetMeshDS());
+  TopoDS_Shape v1 = myHelper.GetSubShapeByNode( node1, myHelper.GetMeshDS());
   if ( v0.ShapeType() == TopAbs_VERTEX ) {
     nbInserted += SMESH_Block::Insert( v0, vertIdVec[ 0 ], shapeMap);
   }
@@ -3020,8 +3942,8 @@ int StdMeshers_PrismAsBlock::TSideFace::InsertSubShapes(TBlockShapes& shapeMap)
   GetColumns(1, col1, col2 );
   node0 = col2->second.front();
   node1 = col2->second.back();
-  v0 = myHelper->GetSubShapeByNode( node0, myHelper->GetMeshDS());
-  v1 = myHelper->GetSubShapeByNode( node1, myHelper->GetMeshDS());
+  v0 = myHelper.GetSubShapeByNode( node0, myHelper.GetMeshDS());
+  v1 = myHelper.GetSubShapeByNode( node1, myHelper.GetMeshDS());
   if ( v0.ShapeType() == TopAbs_VERTEX ) {
     nbInserted += SMESH_Block::Insert( v0, vertIdVec[ 0 ], shapeMap);
   }
@@ -3201,6 +4123,81 @@ void StdMeshers_PrismAsBlock::THorizontalEdgeAdaptor::dumpNodes(int nbNodes) con
     cout << col->second[ i ]->GetID();
 #endif
 }
+
+//================================================================================
+/*!
+ * \brief Costructor of TPCurveOnHorFaceAdaptor fills its map of
+ * normalized parameter to node UV on a horizontal face
+ *  \param [in] sideFace - lateral prism side
+ *  \param [in] isTop - is \a horFace top or bottom of the prism
+ *  \param [in] horFace - top or bottom face of the prism
+ */
+//================================================================================
+
+StdMeshers_PrismAsBlock::
+TPCurveOnHorFaceAdaptor::TPCurveOnHorFaceAdaptor( const TSideFace*   sideFace,
+                                                  const bool         isTop,
+                                                  const TopoDS_Face& horFace)
+{
+  if ( sideFace && !horFace.IsNull() )
+  {
+    //cout << "\n\t FACE " << sideFace->FaceID() << endl;
+    const int Z = isTop ? sideFace->ColumnHeight() - 1 : 0;
+    map<double, const SMDS_MeshNode* > u2nodes;
+    sideFace->GetNodesAtZ( Z, u2nodes );
+    if ( u2nodes.empty() )
+      return;
+
+    SMESH_MesherHelper helper( *sideFace->GetMesh() );
+    helper.SetSubShape( horFace );
+
+    bool okUV;
+    gp_XY uv;
+    double f,l;
+    Handle(Geom2d_Curve) C2d;
+    int edgeID = -1;
+    const double tol = 10 * helper.MaxTolerance( horFace );
+    const SMDS_MeshNode* prevNode = u2nodes.rbegin()->second;
+
+    map<double, const SMDS_MeshNode* >::iterator u2n = u2nodes.begin();
+    for ( ; u2n != u2nodes.end(); ++u2n )
+    {
+      const SMDS_MeshNode* n = u2n->second;
+      okUV = false;
+      if ( n->GetPosition()->GetTypeOfPosition() == SMDS_TOP_EDGE )
+      {
+        if ( n->getshapeId() != edgeID )
+        {
+          C2d.Nullify();
+          edgeID = n->getshapeId();
+          TopoDS_Shape S = helper.GetSubShapeByNode( n, helper.GetMeshDS() );
+          if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
+          {
+            C2d = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), horFace, f,l );
+          }
+        }
+        if ( !C2d.IsNull() )
+        {
+          double u = static_cast< const SMDS_EdgePosition* >( n->GetPosition() )->GetUParameter();
+          if ( f <= u && u <= l )
+          {
+            uv = C2d->Value( u ).XY();
+            okUV = helper.CheckNodeUV( horFace, n, uv, tol );
+          }
+        }
+      }
+      if ( !okUV )
+        uv = helper.GetNodeUV( horFace, n, prevNode, &okUV );
+
+      myUVmap.insert( myUVmap.end(), make_pair( u2n->first, uv ));
+      // cout << n->getshapeId() << " N " << n->GetID()
+      //      << " \t" << uv.X() << ", " << uv.Y() << " \t" << u2n->first << endl;
+
+      prevNode = n;
+    }
+  }
+}
+
 //================================================================================
 /*!
  * \brief Return UV on pcurve for the given normalized parameter
@@ -3211,9 +4208,16 @@ void StdMeshers_PrismAsBlock::THorizontalEdgeAdaptor::dumpNodes(int nbNodes) con
 
 gp_Pnt2d StdMeshers_PrismAsBlock::TPCurveOnHorFaceAdaptor::Value(const Standard_Real U) const
 {
-  TParam2ColumnIt u_col1, u_col2;
-  double r = mySide->GetColumns( U, u_col1, u_col2 );
-  gp_XY uv1 = mySide->GetNodeUV( myFace, u_col1->second[ myZ ]);
-  gp_XY uv2 = mySide->GetNodeUV( myFace, u_col2->second[ myZ ]);
-  return uv1 * ( 1 - r ) + uv2 * r;
+  map< double, gp_XY >::const_iterator i1 = myUVmap.upper_bound( U );
+
+  if ( i1 == myUVmap.end() )
+    return myUVmap.empty() ? gp_XY(0,0) : myUVmap.rbegin()->second;
+
+  if ( i1 == myUVmap.begin() )
+    return (*i1).second;
+
+  map< double, gp_XY >::const_iterator i2 = i1--;
+
+  double r = ( U - i1->first ) / ( i2->first - i1->first );
+  return i1->second * ( 1 - r ) + i2->second * r;
 }