Salome HOME
0022098: EDF 2036 SMESH: Create groups from none conected parts of a mesh
[modules/smesh.git] / src / StdMeshers / StdMeshers_ViscousLayers.cxx
index ddae235f73e5c198f2f17a4b47810424535766a6..61365b4d60b65a48368d621ea86c54826c0a9839 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2007-2012  CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2013  CEA/DEN, EDF R&D, OPEN CASCADE
 //
 // This library is free software; you can redistribute it and/or
 // modify it under the terms of the GNU Lesser General Public
@@ -36,6 +36,7 @@
 #include "SMESH_Gen.hxx"
 #include "SMESH_Group.hxx"
 #include "SMESH_Mesh.hxx"
+#include "SMESH_MeshAlgos.hxx"
 #include "SMESH_MesherHelper.hxx"
 #include "SMESH_ProxyMesh.hxx"
 #include "SMESH_subMesh.hxx"
@@ -82,7 +83,7 @@
 using namespace std;
 
 //================================================================================
-namespace VISCOUS
+namespace VISCOUS_3D
 {
   typedef int TGeomID;
 
@@ -121,13 +122,13 @@ namespace VISCOUS
    * \brief Listener of events of 3D sub-meshes computed with viscous layers.
    * It is used to clear an inferior dim sub-meshes modified by viscous layers
    */
-  class _SrinkShapeListener : SMESH_subMeshEventListener
+  class _ShrinkShapeListener : SMESH_subMeshEventListener
   {
-    _SrinkShapeListener()
+    _ShrinkShapeListener()
       : SMESH_subMeshEventListener(/*isDeletable=*/false,
-                                   "StdMeshers_ViscousLayers::_SrinkShapeListener") {}
-    static SMESH_subMeshEventListener* Get() { static _SrinkShapeListener l; return &l; }
+                                   "StdMeshers_ViscousLayers::_ShrinkShapeListener") {}
   public:
+    static SMESH_subMeshEventListener* Get() { static _ShrinkShapeListener l; return &l; }
     virtual void ProcessEvent(const int                       event,
                               const int                       eventType,
                               SMESH_subMesh*                  solidSM,
@@ -139,23 +140,6 @@ namespace VISCOUS
         SMESH_subMeshEventListener::ProcessEvent(event,eventType,solidSM,data,hyp);
       }
     }
-    static void ToClearSubMeshWithSolid( SMESH_subMesh*      sm,
-                                         const TopoDS_Shape& solid)
-    {
-      SMESH_subMesh* solidSM = sm->GetFather()->GetSubMesh( solid );
-      SMESH_subMeshEventListenerData* data = solidSM->GetEventListenerData( Get());
-      if ( data )
-      {
-        if ( find( data->mySubMeshes.begin(), data->mySubMeshes.end(), sm ) ==
-             data->mySubMeshes.end())
-          data->mySubMeshes.push_back( sm );
-      }
-      else
-      {
-        data = SMESH_subMeshEventListenerData::MakeData( /*dependent=*/sm );
-        sm->SetEventListener( Get(), data, /*whereToListenTo=*/solidSM );
-      }
-    }
   };
   //--------------------------------------------------------------------------------
   /*!
@@ -205,6 +189,32 @@ namespace VISCOUS
     }
   };
   
+  //================================================================================
+  /*!
+   * \brief sets a sub-mesh event listener to clear sub-meshes of sub-shapes of
+   * the main shape when sub-mesh of the main shape is cleared,
+   * for example to clear sub-meshes of FACEs when sub-mesh of a SOLID
+   * is cleared
+   */
+  //================================================================================
+
+  void ToClearSubWithMain( SMESH_subMesh* sub, const TopoDS_Shape& main)
+  {
+    SMESH_subMesh* mainSM = sub->GetFather()->GetSubMesh( main );
+    SMESH_subMeshEventListenerData* data =
+      mainSM->GetEventListenerData( _ShrinkShapeListener::Get());
+    if ( data )
+    {
+      if ( find( data->mySubMeshes.begin(), data->mySubMeshes.end(), sub ) ==
+           data->mySubMeshes.end())
+        data->mySubMeshes.push_back( sub );
+    }
+    else
+    {
+      data = SMESH_subMeshEventListenerData::MakeData( /*dependent=*/sub );
+      sub->SetEventListener( _ShrinkShapeListener::Get(), data, /*whereToListenTo=*/mainSM );
+    }
+  }
   //--------------------------------------------------------------------------------
   /*!
    * \brief Simplex (triangle or tetrahedron) based on 1 (tria) or 2 (tet) nodes of
@@ -256,6 +266,7 @@ namespace VISCOUS
   {
     double _r; // radius
     double _k; // factor to correct node smoothed position
+    double _h2lenRatio; // avgNormProj / (2*avgDist)
   public:
     static _Curvature* New( double avgNormProj, double avgDist )
     {
@@ -266,10 +277,12 @@ namespace VISCOUS
         c->_r = avgDist * avgDist / avgNormProj;
         c->_k = avgDist * avgDist / c->_r / c->_r;
         c->_k *= ( c->_r < 0 ? 1/1.1 : 1.1 ); // not to be too restrictive
+        c->_h2lenRatio = avgNormProj / ( avgDist + avgDist );
       }
       return c;
     }
     double lenDelta(double len) const { return _k * ( _r + len ); }
+    double lenDeltaByDist(double dist) const { return dist * _h2lenRatio; }
   };
   struct _LayerEdge;
   //--------------------------------------------------------------------------------
@@ -547,7 +560,7 @@ virtual SMDS_ElemIteratorPtr elementsIterator(SMDSAbs_ElementType type) const
       _nn[3]=_le2->_nodes[0];
     }
   };
-} // namespace VISCOUS
+} // namespace VISCOUS_3D
 
 //================================================================================
 // StdMeshers_ViscousLayers hypothesis
@@ -559,10 +572,15 @@ StdMeshers_ViscousLayers::StdMeshers_ViscousLayers(int hypId, int studyId, SMESH
   _name = StdMeshers_ViscousLayers::GetHypType();
   _param_algo_dim = -3; // auxiliary hyp used by 3D algos
 } // --------------------------------------------------------------------------------
-void StdMeshers_ViscousLayers::SetIgnoreFaces(const std::vector<int>& faceIds)
+void StdMeshers_ViscousLayers::SetBndShapesToIgnore(const std::vector<int>& faceIds)
 {
-  if ( faceIds != _ignoreFaceIds )
-    _ignoreFaceIds = faceIds, NotifySubMeshesHypothesisModification();
+  if ( faceIds != _ignoreBndShapeIds )
+    _ignoreBndShapeIds = faceIds, NotifySubMeshesHypothesisModification();
+} // --------------------------------------------------------------------------------
+bool StdMeshers_ViscousLayers::IsIgnoredShape(const int shapeID) const
+{
+  return ( find( _ignoreBndShapeIds.begin(), _ignoreBndShapeIds.end(), shapeID )
+           != _ignoreBndShapeIds.end() );
 } // --------------------------------------------------------------------------------
 void StdMeshers_ViscousLayers::SetTotalThickness(double thickness)
 {
@@ -584,7 +602,7 @@ StdMeshers_ViscousLayers::Compute(SMESH_Mesh&         theMesh,
                                   const TopoDS_Shape& theShape,
                                   const bool          toMakeN2NMap) const
 {
-  using namespace VISCOUS;
+  using namespace VISCOUS_3D;
   _ViscousBuilder bulder;
   SMESH_ComputeErrorPtr err = bulder.Compute( theMesh, theShape );
   if ( err && !err->IsOK() )
@@ -620,17 +638,17 @@ std::ostream & StdMeshers_ViscousLayers::SaveTo(std::ostream & save)
   save << " " << _nbLayers
        << " " << _thickness
        << " " << _stretchFactor
-       << " " << _ignoreFaceIds.size();
-  for ( unsigned i = 0; i < _ignoreFaceIds.size(); ++i )
-    save << " " << _ignoreFaceIds[i];
+       << " " << _ignoreBndShapeIds.size();
+  for ( unsigned i = 0; i < _ignoreBndShapeIds.size(); ++i )
+    save << " " << _ignoreBndShapeIds[i];
   return save;
 } // --------------------------------------------------------------------------------
 std::istream & StdMeshers_ViscousLayers::LoadFrom(std::istream & load)
 {
   int nbFaces, faceID;
   load >> _nbLayers >> _thickness >> _stretchFactor >> nbFaces;
-  while ( _ignoreFaceIds.size() < nbFaces && load >> faceID )
-    _ignoreFaceIds.push_back( faceID );
+  while ( _ignoreBndShapeIds.size() < nbFaces && load >> faceID )
+    _ignoreBndShapeIds.push_back( faceID );
   return load;
 } // --------------------------------------------------------------------------------
 bool StdMeshers_ViscousLayers::SetParametersByMesh(const SMESH_Mesh*   theMesh,
@@ -716,27 +734,30 @@ namespace
     gp_XYZ dir(0,0,0);
     if ( !( ok = ( edges.size() > 0 ))) return dir;
     // get average dir of edges going fromV
-    gp_Vec edgeDir;
-    for ( unsigned i = 0; i < edges.size(); ++i )
-    {
-      edgeDir = getEdgeDir( edges[i], fromV );
-      double size2 = edgeDir.SquareMagnitude();
-      if ( size2 > numeric_limits<double>::min() )
-        edgeDir /= sqrt( size2 );
-      else
-        ok = false;
-      dir += edgeDir.XYZ();
-    }
+    gp_XYZ edgeDir;
+    //if ( edges.size() > 1 )
+      for ( unsigned i = 0; i < edges.size(); ++i )
+      {
+        edgeDir = getEdgeDir( edges[i], fromV );
+        double size2 = edgeDir.SquareModulus();
+        if ( size2 > numeric_limits<double>::min() )
+          edgeDir /= sqrt( size2 );
+        else
+          ok = false;
+        dir += edgeDir;
+      }
     gp_XYZ fromEdgeDir = getFaceDir( F, edges[0], node, helper, ok );
-    if ( edges.size() == 1 || dir.SquareModulus() < 1e-10)
+    if ( edges.size() == 1 )
       dir = fromEdgeDir;
+    else if ( dir.SquareModulus() < 0.1 ) // ~< 20 degrees
+      dir = fromEdgeDir + getFaceDir( F, edges[1], node, helper, ok );
     else if ( dir * fromEdgeDir < 0 )
       dir *= -1;
     if ( ok )
     {
       //dir /= edges.size();
       if ( cosin ) {
-        double angle = edgeDir.Angle( dir );
+        double angle = gp_Vec( edgeDir ).Angle( dir );
         *cosin = cos( angle );
       }
     }
@@ -849,7 +870,7 @@ namespace
 #endif
 }
 
-using namespace VISCOUS;
+using namespace VISCOUS_3D;
 
 //================================================================================
 /*!
@@ -976,6 +997,9 @@ SMESH_ComputeErrorPtr _ViscousBuilder::Compute(SMESH_Mesh&         theMesh,
   {
     if ( ! makeLayer(_sdVec[i]) )
       return _error;
+
+    if ( _sdVec[i]._edges.size() == 0 )
+      continue;
     
     if ( ! inflate(_sdVec[i]) )
       return _error;
@@ -1048,7 +1072,7 @@ bool _ViscousBuilder::findFacesWithLayers()
   vector<TopoDS_Shape> ignoreFaces;
   for ( unsigned i = 0; i < _sdVec.size(); ++i )
   {
-    vector<TGeomID> ids = _sdVec[i]._hyp->GetIgnoreFaces();
+    vector<TGeomID> ids = _sdVec[i]._hyp->GetBndShapesToIgnore();
     for ( unsigned i = 0; i < ids.size(); ++i )
     {
       const TopoDS_Shape& s = getMeshDS()->IndexToShape( ids[i] );
@@ -1073,7 +1097,7 @@ bool _ViscousBuilder::findFacesWithLayers()
       {     
         _ignoreShapeIds.insert( faceInd );
         ignoreFaces.push_back( exp.Current() );
-        if ( SMESH_Algo::IsReversedSubMesh( TopoDS::Face( exp.Current() ), getMeshDS()))
+        if ( helper.IsReversedSubMesh( TopoDS::Face( exp.Current() )))
           _sdVec[i]._reversedFaceIds.insert( faceInd );
       }
     }
@@ -1368,7 +1392,7 @@ bool _ViscousBuilder::makeLayer(_SolidData& data)
   if ( data._stepSize < 1. )
     data._epsilon *= data._stepSize;
 
-  // Put _LayerEdge's into a vector
+  // Put _LayerEdge's into the vector data._edges
 
   if ( !sortEdges( data, edgesByGeom ))
     return false;
@@ -1692,8 +1716,8 @@ bool _ViscousBuilder::setEdgeData(_LayerEdge&         edge,
     }
     case SMDS_TOP_VERTEX: {
       TopoDS_Vertex V = TopoDS::Vertex( helper.GetSubShapeByNode( node, getMeshDS()));
-      gp_Vec inFaceDir = getFaceDir( F, V, node, helper, normOK);
-      double angle = inFaceDir.Angle( edge._normal ); // [0,PI]
+      gp_XYZ inFaceDir = getFaceDir( F, V, node, helper, normOK);
+      double angle = gp_Vec( inFaceDir).Angle( edge._normal ); // [0,PI]
       edge._cosin = cos( angle );
       //cout << "Cosin on VERTEX " << edge._cosin << " node " << node->GetID() << endl;
       break;
@@ -2044,14 +2068,14 @@ bool _ViscousBuilder::inflate(_SolidData& data)
   // Limit inflation step size by geometry size found by itersecting
   // normals of _LayerEdge's with mesh faces
   double geomSize = Precision::Infinite(), intersecDist;
-  SMESH_MeshEditor editor( _mesh );
   auto_ptr<SMESH_ElementSearcher> searcher
-    ( editor.GetElementSearcher( data._proxyMesh->GetFaces( data._solid )) );
+    ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
+                                           data._proxyMesh->GetFaces( data._solid )) );
   for ( unsigned i = 0; i < data._edges.size(); ++i )
   {
     if ( data._edges[i]->IsOnEdge() ) continue;
     data._edges[i]->FindIntersection( *searcher, intersecDist, data._epsilon );
-    if ( geomSize > intersecDist )
+    if ( geomSize > intersecDist && intersecDist > 0 )
       geomSize = intersecDist;
   }
   if ( data._stepSize > 0.3 * geomSize )
@@ -2242,9 +2266,9 @@ bool _ViscousBuilder::smoothAndCheck(_SolidData& data,
   // Check if the last segments of _LayerEdge intersects 2D elements;
   // checked elements are either temporary faces or faces on surfaces w/o the layers
 
-  SMESH_MeshEditor editor( _mesh );
   auto_ptr<SMESH_ElementSearcher> searcher
-    ( editor.GetElementSearcher( data._proxyMesh->GetFaces( data._solid )) );
+    ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
+                                           data._proxyMesh->GetFaces( data._solid )) );
 
   distToIntersection = Precision::Infinite();
   double dist;
@@ -2504,22 +2528,21 @@ bool _ViscousBuilder::smoothAnalyticEdge( _SolidData&           data,
     else // 2D
     {
       const gp_XY center( center3D.X(), center3D.Y() );
-      
+
       gp_XY uv0 = helper.GetNodeUV( F, data._edges[iFrom]->_2neibors->_nodes[0]);
       gp_XY uvM = helper.GetNodeUV( F, data._edges[iFrom]->_nodes.back());
       gp_XY uv1 = helper.GetNodeUV( F, data._edges[iTo-1]->_2neibors->_nodes[1]);
       gp_Vec2d vec0( center, uv0 );
-      gp_Vec2d vecM( center, uvM);
+      gp_Vec2d vecM( center, uvM );
       gp_Vec2d vec1( center, uv1 );
       double uLast = vec0.Angle( vec1 ); // -PI - +PI
       double uMidl = vec0.Angle( vecM );
-      if ( uLast < 0 ) uLast += 2.*M_PI; // 0.0 - 2*PI
-      if ( uMidl < 0 ) uMidl += 2.*M_PI;
-      const bool sense = ( uMidl < uLast );
+      if ( uLast * uMidl < 0. )
+        uLast += ( uMidl > 0 ? +2. : -2. ) * M_PI;
       const double radius = 0.5 * ( vec0.Magnitude() + vec1.Magnitude() );
 
-      gp_Ax2d axis( center, vec0 );
-      gp_Circ2d circ ( axis, radius, sense );
+      gp_Ax2d   axis( center, vec0 );
+      gp_Circ2d circ( axis, radius );
       for ( int i = iFrom; i < iTo; ++i )
       {
         double    newU = uLast * len[i-iFrom] / len.back();
@@ -2605,10 +2628,10 @@ bool _ViscousBuilder::updateNormals( _SolidData&         data,
   // 1) to find and fix intersection
   // 2) to check that no new intersection appears as result of 1)
 
-  SMESH_MeshEditor editor( _mesh );
   SMDS_ElemIteratorPtr fIt( new SMDS_ElementVectorIterator( tmpFaces.begin(),
                                                             tmpFaces.end()));
-  auto_ptr<SMESH_ElementSearcher> searcher ( editor.GetElementSearcher( fIt ));
+  auto_ptr<SMESH_ElementSearcher> searcher
+    ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(), fIt ));
 
   // 1) Find intersections
   double dist;
@@ -2842,7 +2865,7 @@ bool _LayerEdge::FindIntersection( SMESH_ElementSearcher&   searcher,
     }
     if ( intFound )
     {
-      if ( dist < segLen*(1.01))
+      if ( dist < segLen*(1.01) && dist > -(_len-segLen) )
         segmentIntersected = true;
       if ( distance > dist )
         distance = dist, iFace = j;
@@ -3045,7 +3068,8 @@ bool _LayerEdge::SmoothOnEdge(Handle(Geom_Surface)& surface,
   double lenDelta = 0;
   if ( _curvature )
   {
-    lenDelta = _curvature->lenDelta( _len );
+    //lenDelta = _curvature->lenDelta( _len );
+    lenDelta = _curvature->lenDeltaByDist( dist01 );
     newPos.ChangeCoord() += _normal * lenDelta;
   }
 
@@ -3375,6 +3399,15 @@ bool _ViscousBuilder::refine(_SolidData& data)
     }
   }
 
+  if ( !getMeshDS()->IsEmbeddedMode() )
+    // Log node movement
+    for ( unsigned i = 0; i < data._edges.size(); ++i )
+    {
+      _LayerEdge& edge = *data._edges[i];
+      SMESH_TNodeXYZ p ( edge._nodes.back() );
+      getMeshDS()->MoveNode( p._node, p.X(), p.Y(), p.Z() );
+    }
+
   // TODO: make quadratic prisms and polyhedrons(?)
 
   helper.SetElementsOnShape(true);
@@ -3463,7 +3496,7 @@ bool _ViscousBuilder::shrink()
   helper.ToFixNodeParameters( true );
 
   // EDGE's to shrink
-  map< int, _Shrinker1D > e2shrMap;
+  map< TGeomID, _Shrinker1D > e2shrMap;
 
   // loop on FACES to srink mesh on
   map< TGeomID, _SolidData* >::iterator f2sd = f2sdMap.begin();
@@ -3588,6 +3621,7 @@ bool _ViscousBuilder::shrink()
           _Shrinker1D& srinker = e2shrMap[ edgeIndex ];
           eShri1D.insert( & srinker );
           srinker.AddEdge( edge, helper );
+          VISCOUS_3D::ToClearSubWithMain( _mesh->GetSubMesh( edge->_sWOL ), data._solid );
           // restore params of nodes on EGDE if the EDGE has been already
           // srinked while srinking another FACE
           srinker.RestoreParams();
@@ -3633,7 +3667,8 @@ bool _ViscousBuilder::shrink()
         for ( unsigned i = 0; i < nodesToSmooth.size(); ++i )
         {
           moved |= nodesToSmooth[i].Smooth( badNb,surface,helper,refSign,
-                                            /*isCentroidal=*/isConcaveFace,/*set3D=*/false );
+                                            /*isCentroidal=*/isConcaveFace,
+                                            /*set3D=*/isConcaveFace);
         }
         if ( badNb < oldBadNb )
           nbNoImpSteps = 0;
@@ -3650,12 +3685,10 @@ bool _ViscousBuilder::shrink()
     bool highQuality;
     {
       const bool hasTria = _mesh->NbTriangles(), hasQuad = _mesh->NbQuadrangles();
-      if ( hasTria != hasQuad )
-      {
+      if ( hasTria != hasQuad ) {
         highQuality = hasQuad;
       }
-      else
-      {
+      else {
         set<int> nbNodesSet;
         SMDS_ElemIteratorPtr fIt = smDS->GetElements();
         while ( fIt->more() && nbNodesSet.size() < 2 )
@@ -3674,7 +3707,15 @@ bool _ViscousBuilder::shrink()
       dumpFunctionEnd();
     }
     // Set an event listener to clear FACE sub-mesh together with SOLID sub-mesh
-    _SrinkShapeListener::ToClearSubMeshWithSolid( sm, data._solid );
+    VISCOUS_3D::ToClearSubWithMain( sm, data._solid );
+
+    if ( !getMeshDS()->IsEmbeddedMode() )
+      // Log node movement
+      for ( unsigned i = 0; i < nodesToSmooth.size(); ++i )
+      {
+        SMESH_TNodeXYZ p ( nodesToSmooth[i]._node );
+        getMeshDS()->MoveNode( nodesToSmooth[i]._node, p.X(), p.Y(), p.Z() );
+      }
 
   } // loop on FACES to srink mesh on
 
@@ -3918,8 +3959,8 @@ void _ViscousBuilder::fixBadFaces(const TopoDS_Face& F, SMESH_MesherHelper& help
       const SMDS_MeshNode* n1 = badTrias[iTia]->GetNode( iSide );
       const SMDS_MeshNode* n2 = badTrias[iTia]->GetNode(( iSide+1 ) % 3 );
       trias [iSide].first  = badTrias[iTia];
-      trias [iSide].second = SMESH_MeshEditor::FindFaceInSet( n1, n2, emptySet, involvedFaces,
-                                                              & i1, & i2 );
+      trias [iSide].second = SMESH_MeshAlgos::FindFaceInSet( n1, n2, emptySet, involvedFaces,
+                                                             & i1, & i2 );
       if ( ! trias[iSide].second || trias[iSide].second->NbCornerNodes() != 3 )
         continue;
 
@@ -4227,7 +4268,7 @@ void _Shrinker1D::AddEdge( const _LayerEdge* e, SMESH_MesherHelper& helper )
     GeomAdaptor_Curve aCurve(C, f,l);
     const double totLen = GCPnts_AbscissaPoint::Length(aCurve, f, l);
 
-    int nbExpectNodes = eSubMesh->NbNodes() - e->_nodes.size();
+    int nbExpectNodes = eSubMesh->NbNodes();
     _initU  .reserve( nbExpectNodes );
     _normPar.reserve( nbExpectNodes );
     _nodes  .reserve( nbExpectNodes );
@@ -4439,6 +4480,8 @@ bool _ViscousBuilder::addBoundaryElements()
         F = e2f->second.Oriented( TopAbs_FORWARD );
         reverse = ( helper.GetSubShapeOri( F, E ) == TopAbs_REVERSED );
         if ( helper.GetSubShapeOri( data._solid, F ) == TopAbs_REVERSED )
+          reverse = !reverse, F.Reverse();
+        if ( helper.IsReversedSubMesh( TopoDS::Face(F) ))
           reverse = !reverse;
       }
       else
@@ -4470,12 +4513,28 @@ bool _ViscousBuilder::addBoundaryElements()
         vector< const SMDS_MeshNode*>&  nn1 = ledges[j-dj1]->_nodes;
         vector< const SMDS_MeshNode*>&  nn2 = ledges[j-dj2]->_nodes;
         if ( isOnFace )
-          for ( unsigned z = 1; z < nn1.size(); ++z )
+          for ( size_t z = 1; z < nn1.size(); ++z )
             sm->AddElement( getMeshDS()->AddFace( nn1[z-1], nn2[z-1], nn2[z], nn1[z] ));
         else
-          for ( unsigned z = 1; z < nn1.size(); ++z )
+          for ( size_t z = 1; z < nn1.size(); ++z )
             sm->AddElement( new SMDS_FaceOfNodes( nn1[z-1], nn2[z-1], nn2[z], nn1[z]));
       }
+
+      // Make edges
+      for ( int isFirst = 0; isFirst < 2; ++isFirst )
+      {
+        _LayerEdge* edge = isFirst ? ledges.front() : ledges.back();
+        if ( !edge->_sWOL.IsNull() && edge->_sWOL.ShapeType() == TopAbs_EDGE )
+        {
+          vector< const SMDS_MeshNode*>&  nn = edge->_nodes;
+          if ( nn[1]->GetInverseElementIterator( SMDSAbs_Edge )->more() )
+            continue;
+          helper.SetSubShape( edge->_sWOL );
+          helper.SetElementsOnShape( true );
+          for ( size_t z = 1; z < nn.size(); ++z )
+            helper.AddEdge( nn[z-1], nn[z] );
+        }
+      }
     }
   }