]> SALOME platform Git repositories - modules/smesh.git/commitdiff
Salome HOME
0020832: EDF 1359 SMESH : Automatic meshing of boundary layers
authoreap <eap@opencascade.com>
Wed, 15 Dec 2010 10:43:22 +0000 (10:43 +0000)
committereap <eap@opencascade.com>
Wed, 15 Dec 2010 10:43:22 +0000 (10:43 +0000)
  Prototype version

+   * interface of "Viscous Layers" hypothesis.
+   * This hypothesis specifies parameters of layers of prisms to build
+   * near mesh boundary. This hypothesis can be used by several 3D algorithms:
+   * NETGEN 3D, GHS3D, Hexahedron(i,j,k), 3D Extrusion, Radial Prism
+   */
+  interface StdMeshers_ViscousLayers : SMESH::SMESH_Hypothesis

15 files changed:
idl/SMESH_BasicHypothesis.idl
resources/StdMeshers.xml
src/SMESH_SWIG/smeshDC.py
src/StdMeshers/Makefile.am
src/StdMeshers/StdMeshers_ProxyMesh.cxx [new file with mode: 0644]
src/StdMeshers/StdMeshers_ProxyMesh.hxx [new file with mode: 0644]
src/StdMeshers/StdMeshers_ViscousLayers.cxx [new file with mode: 0644]
src/StdMeshers/StdMeshers_ViscousLayers.hxx [new file with mode: 0644]
src/StdMeshersGUI/StdMeshersGUI_StdHypothesisCreator.cxx
src/StdMeshersGUI/StdMeshers_images.ts
src/StdMeshersGUI/StdMeshers_msg_en.ts
src/StdMeshers_I/Makefile.am
src/StdMeshers_I/StdMeshers_ViscousLayers_i.cxx [new file with mode: 0644]
src/StdMeshers_I/StdMeshers_ViscousLayers_i.hxx [new file with mode: 0644]
src/StdMeshers_I/StdMeshers_i.cxx

index 88b0ab7512f3b2a81a814b1bb04e795d33c57f4d..ada4be5e4a94117b67999d43127fc543a09440d1 100644 (file)
@@ -838,6 +838,39 @@ module StdMeshers
     void GetCopySourceMesh(out boolean toCopyMesh,out boolean toCopyGroups);
   };
 
+  /*!
+   * interface of "Viscous Layers" hypothesis.
+   * This hypothesis specifies parameters of layers of prisms to build
+   * near mesh boundary. This hypothesis can be used by several 3D algorithms:
+   * NETGEN 3D, GHS3D, Hexahedron(i,j,k), 3D Extrusion, Radial Prism
+   */
+  interface StdMeshers_ViscousLayers : SMESH::SMESH_Hypothesis
+  {
+    /*!
+     * Set faces to exclude from treatment
+     */
+    void SetIgnoreFaces(in SMESH::long_array faceIDs) raises (SALOME::SALOME_Exception);
+    SMESH::long_array GetIgnoreFaces();
+
+    /*!
+     * Set total thickness of layers of prisms
+     */
+    void SetTotalThickness(in double thickness) raises (SALOME::SALOME_Exception);
+    double GetTotalThickness();
+
+    /*!
+     * Set number of layers of prisms
+     */
+    void SetNumberLayers(in short nb) raises (SALOME::SALOME_Exception);
+    short GetNumberLayers();
+
+    /*!
+     * Set factor (>1.0) of growth of layer thickness towards inside of mesh
+     */
+    void SetStretchFactor(in double factor) raises (SALOME::SALOME_Exception);
+    double GetStretchFactor();
+  };
+
   /*!
    * StdMeshers_SegmentAroundVertex_0D: interface of "SegmentAroundVertex" algorithm
    */
index 41c83c997d219654952a24f665582a8fa104c929..6cd63a5ee480b1276d7c10d3233cf8f3321dec54 100644 (file)
                 icon-id="mesh_hypo_length.png"
                 dim="3"/>
 
+    <hypothesis type="ViscousLayers"
+                label-id="Viscous Layers"
+                icon-id="mesh_algo_quad.png"
+                auxiliary="true"
+                dim="3"/>
+
     <hypothesis type="NumberOfLayers2D"
                 label-id="Number of Layers"
                 icon-id="mesh_hypo_length.png"
index a2476107ad5b5eb4a81e490359eb6e37a3ca77ef..96885ae140b726e5476ea930b0e689d19e368461 100644 (file)
@@ -4345,6 +4345,29 @@ class Mesh_Algorithm:
         if not entry: return ""
         return entry
 
+    ## Defines "ViscousLayers" hypothesis to give parameters of layers of prisms to build
+    #  near mesh boundary. This hypothesis can be used by several 3D algorithms:
+    #  NETGEN 3D, GHS3D, Hexahedron(i,j,k), 3D Extrusion, Radial Prism
+    #  @param thickness total thickness of layers of prisms
+    #  @param numberOfLayers number of layers of prisms
+    #  @param stretchFactor factor (>1.0) of growth of layer thickness towards inside of mesh
+    #  @param ignoreFaces geometrical face (or their ids) not to generate layers on
+    #  @ingroup l3_hypos_additi
+    def ViscousLayers(self, thickness, numberOfLayers, stretchFactor, ignoreFaces=[]):
+        if not isinstance(self.algo, SMESH._objref_SMESH_3D_Algo):
+            raise TypeError, "ViscousLayers are supported by 3D algorithms only"
+        if not "ViscousLayers" in self.GetCompatibleHypothesis():
+            raise TypeError, "ViscousLayers are not supported by %s"%self.algo.GetName()
+        if ignoreFaces and isinstance( ignoreFaces[0], geompyDC.GEOM._objref_GEOM_Object ):
+            ignoreFaces = [ self.mesh.geompyD.GetSubShapeID(self.mesh.geom, f) for f in ignoreFaces ]
+        hyp = self.Hypothesis("ViscousLayers",
+                              [thickness, numberOfLayers, stretchFactor, ignoreFaces])
+        hyp.SetTotalThickness(thickness)
+        hyp.SetNumberLayers(numberOfLayers)
+        hyp.SetStretchFactor(stretchFactor)
+        hyp.SetIgnoreFaces(ignoreFaces)
+        return hyp
+
 # Public class: Mesh_Segment
 # --------------------------
 
index 08c312ead58d3e1f86cb6626e3b6cddc4a4d44a0..6e02bb7bc31bbb097dd096c66742bb95b4740e37 100644 (file)
@@ -75,7 +75,9 @@ salomeinclude_HEADERS = \
        StdMeshers_HexaFromSkin_3D.hxx \
        StdMeshers_ImportSource.hxx \
        StdMeshers_Import_1D.hxx \
-       StdMeshers_Import_1D2D.hxx
+       StdMeshers_Import_1D2D.hxx \
+       StdMeshers_ViscousLayers.hxx \
+       StdMeshers_ProxyMesh.hxx
 
 # Libraries targets
 
@@ -129,8 +131,9 @@ dist_libStdMeshers_la_SOURCES = \
        StdMeshers_HexaFromSkin_3D.cxx \
        StdMeshers_ImportSource.cxx \
        StdMeshers_Import_1D.cxx \
-       StdMeshers_Import_1D2D.cxx
-
+       StdMeshers_Import_1D2D.cxx \
+       StdMeshers_ViscousLayers.cxx \
+       StdMeshers_ProxyMesh.cxx
 
 # additionnal information to compil and link file
 libStdMeshers_la_CPPFLAGS = \
diff --git a/src/StdMeshers/StdMeshers_ProxyMesh.cxx b/src/StdMeshers/StdMeshers_ProxyMesh.cxx
new file mode 100644 (file)
index 0000000..4dc21a5
--- /dev/null
@@ -0,0 +1,384 @@
+//  Copyright (C) 2007-2010  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
+//  License as published by the Free Software Foundation; either
+//  version 2.1 of the License.
+//
+//  This library is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  Lesser General Public License for more details.
+//
+//  You should have received a copy of the GNU Lesser General Public
+//  License along with this library; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+//  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File      : StdMeshers_ProxyMesh.cxx
+// Created   : Thu Dec  2 12:32:53 2010
+// Author    : Edward AGAPOV (eap)
+
+#include "StdMeshers_ProxyMesh.hxx"
+
+#include "SMDS_IteratorOnIterators.hxx"
+#include "SMDS_SetIterator.hxx"
+#include "SMESH_MesherHelper.hxx"
+
+#include <TopTools_ListIteratorOfListOfShape.hxx>
+#include <TopExp_Explorer.hxx>
+
+//================================================================================
+/*!
+ * \brief Constructor; mesh must be set by a descendant class
+ */
+//================================================================================
+
+StdMeshers_ProxyMesh::StdMeshers_ProxyMesh():_mesh(0)
+{
+}
+//================================================================================
+/*!
+ * \brief Make a proxy mesh from components. Components become empty
+ */
+//================================================================================
+
+StdMeshers_ProxyMesh::StdMeshers_ProxyMesh(vector<StdMeshers_ProxyMesh::Ptr>& components)
+{
+  if ( components.empty() ) return;
+
+  for ( unsigned i = 0; i < components.size(); ++i )
+  {
+    StdMeshers_ProxyMesh* m = components[i].get();
+
+    if ( !_mesh ) _mesh = m->_mesh;
+    if ( _allowedTypes.empty() ) _allowedTypes = m->_allowedTypes;
+
+    if ( _subMeshes.size() < m->_subMeshes.size() )
+      _subMeshes.resize( m->_subMeshes.size(), 0 );
+    for ( unsigned j = 0; j < m->_subMeshes.size(); ++j )
+    {
+      if ( !m->_subMeshes[j] ) continue;
+      if ( _subMeshes[j] )
+      {
+        // unite 2 sub-meshes
+        set< const SMDS_MeshElement * > elems( _subMeshes[j]->_elements.begin(),
+                                               _subMeshes[j]->_elements.end());
+        elems.insert( m->_subMeshes[j]->_elements.begin(),
+                      m->_subMeshes[j]->_elements.end());
+        _subMeshes[j]->_elements.assign( elems.begin(), elems.end() );
+
+        if ( !_subMeshes[j]->_n2n )
+          _subMeshes[j]->_n2n = m->_subMeshes[j]->_n2n, m->_subMeshes[j]->_n2n = 0;
+
+        else if ( _subMeshes[j]->_n2n && m->_subMeshes[j]->_n2n )
+          _subMeshes[j]->_n2n->insert( m->_subMeshes[j]->_n2n->begin(),
+                                       m->_subMeshes[j]->_n2n->end());
+      }
+      else
+      {
+        _subMeshes[j] = m->_subMeshes[j];
+        m->_subMeshes[j] = 0;
+      }
+    }
+  }
+}
+
+//================================================================================
+/*!
+ * \brief Destructor deletes proxy submeshes
+ */
+//================================================================================
+
+StdMeshers_ProxyMesh::~StdMeshers_ProxyMesh()
+{
+  for ( unsigned i = 0; i < _subMeshes.size(); ++i )
+    delete _subMeshes[i];
+  _subMeshes.clear();
+}
+
+//================================================================================
+/*!
+ * \brief Returns index of a shape
+ */
+//================================================================================
+
+int StdMeshers_ProxyMesh::shapeIndex(const TopoDS_Shape& shape) const
+{
+  return ( shape.IsNull() || !_mesh->HasShapeToMesh() ? 0 : getMeshDS()->ShapeToIndex(shape));
+}
+
+//================================================================================
+/*!
+ * \brief Returns the submesh of a shape; it can be a proxy sub-mesh
+ */
+//================================================================================
+
+const SMESHDS_SubMesh* StdMeshers_ProxyMesh::GetSubMesh(const TopoDS_Shape& shape) const
+{
+  const SMESHDS_SubMesh* sm = 0;
+
+  int i = shapeIndex(shape);
+  if ( i < _subMeshes.size() )
+    sm = _subMeshes[i];
+  if ( !sm )
+    sm = getMeshDS()->MeshElements( i );
+
+  return sm;
+}
+
+//================================================================================
+/*!
+ * \brief Returns the proxy sub-mesh of a shape; it can be NULL
+ */
+//================================================================================
+
+const StdMeshers_ProxyMesh::SubMesh*
+StdMeshers_ProxyMesh::GetProxySubMesh(const TopoDS_Shape& shape) const
+{
+  int i = shapeIndex(shape);
+  return i < _subMeshes.size() ? _subMeshes[i] : 0;
+}
+
+//================================================================================
+/*!
+ * \brief Returns the proxy node of a node; the input node is returned if no proxy exists
+ */
+//================================================================================
+
+const SMDS_MeshNode* StdMeshers_ProxyMesh::GetProxyNode( const SMDS_MeshNode* node ) const
+{
+  const SMDS_MeshNode* proxy = node;
+  if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
+  {
+    if ( const SubMesh* proxySM = findProxySubMesh( node->GetPosition()->GetShapeId() ))
+      proxy = proxySM->GetProxyNode( node );
+  }
+  else
+  {
+    TopoDS_Shape shape = SMESH_MesherHelper::GetSubShapeByNode( node, getMeshDS());
+    TopTools_ListIteratorOfListOfShape ancIt;
+    if ( !shape.IsNull() ) ancIt.Initialize( _mesh->GetAncestors( shape ));
+    for ( ; ancIt.More() && proxy == node; ancIt.Next() )
+      if ( const SubMesh* proxySM = findProxySubMesh( shapeIndex(ancIt.Value())))
+        proxy = proxySM->GetProxyNode( node );
+  }
+  return proxy;
+}
+
+namespace
+{
+  //================================================================================
+  /*!
+   * \brief Iterator filtering elements by type
+   */
+  //================================================================================
+
+  class TFilteringIterator : public SMDS_ElemIterator
+  {
+    SMDS_ElemIteratorPtr        _iter;
+    const SMDS_MeshElement *    _curElem;
+    vector< SMDSAbs_EntityType> _okTypes;
+  public:
+    TFilteringIterator( const vector< SMDSAbs_EntityType>& okTypes,
+                        const SMDS_ElemIteratorPtr&        elemIterator)
+      :_iter(elemIterator), _curElem(0), _okTypes(okTypes)
+    {
+    }
+    virtual bool more()
+    {
+      return _curElem;
+    }
+    virtual const SMDS_MeshElement* next()
+    {
+      const SMDS_MeshElement* res = _curElem;
+      _curElem = 0;
+      while ( _iter->more() && !_curElem )
+      {
+        _curElem = _iter->next();
+        if ( find( _okTypes.begin(), _okTypes.end(), _curElem->GetEntityType()) != _okTypes.end())
+          _curElem = 0;
+      }
+      return res;
+    }
+  };
+}
+
+//================================================================================
+/*!
+ * \brief Returns iterator on all faces on the shape taking into account substitutions
+ */
+//================================================================================
+
+SMDS_ElemIteratorPtr StdMeshers_ProxyMesh::GetFaces(const TopoDS_Shape& shape) const
+{
+  if ( !_mesh->HasShapeToMesh() )
+    return SMDS_ElemIteratorPtr();
+
+  _subContainer.RemoveAllSubmeshes();
+
+  TopExp_Explorer fExp( shape, TopAbs_FACE );
+  for ( ; fExp.More(); fExp.Next() )
+    if ( const SMESHDS_SubMesh* sm = GetSubMesh( fExp.Current()))
+      _subContainer.AddSubMesh( sm );
+  return _subContainer.GetElements();
+}
+
+//================================================================================
+/*!
+ * \brief Returns iterator on all faces of the mesh taking into account substitutions
+ * To be used in case of mesh without shape
+ */
+//================================================================================
+
+SMDS_ElemIteratorPtr StdMeshers_ProxyMesh::GetFaces() const
+{
+  if ( _mesh->HasShapeToMesh() )
+    return SMDS_ElemIteratorPtr();
+
+  _subContainer.RemoveAllSubmeshes();
+  for ( unsigned i = 0; i < _subMeshes.size(); ++i )
+    if ( _subMeshes[i] )
+      _subContainer.AddSubMesh( _subMeshes[i] );
+
+  if ( _subContainer.NbSubMeshes() == 0 ) // no elements substituted
+    return getMeshDS()->elementsIterator(SMDSAbs_Face);
+
+  // if _allowedTypes is empty, only elements from _subMeshes are returned,...
+  SMDS_ElemIteratorPtr proxyIter = _subContainer.GetElements();
+  if ( _allowedTypes.empty() || NbFaces() == _mesh->NbFaces() )
+    return proxyIter;
+
+  // ... else elements filtered using allowedTypes are additionally returned
+  SMDS_ElemIteratorPtr facesIter = getMeshDS()->elementsIterator(SMDSAbs_Face);
+  SMDS_ElemIteratorPtr filterIter( new TFilteringIterator( _allowedTypes, facesIter ));
+  vector< SMDS_ElemIteratorPtr > iters(2);
+  iters[0] = proxyIter;
+  iters[1] = filterIter;
+    
+  typedef vector< SMDS_ElemIteratorPtr > TElemIterVector;
+  typedef SMDS_IteratorOnIterators<const SMDS_MeshElement *, TElemIterVector> TItersIter;
+  return SMDS_ElemIteratorPtr( new TItersIter( iters ));
+}
+
+//================================================================================
+/*!
+ * \brief Return total nb of faces taking into account substitutions
+ */
+//================================================================================
+
+int StdMeshers_ProxyMesh::NbFaces() const
+{
+  // TODO
+}
+
+//================================================================================
+/*!
+ * \brief Returns a proxy sub-mesh; it is created if not yet exists
+ */
+//================================================================================
+
+StdMeshers_ProxyMesh::SubMesh* StdMeshers_ProxyMesh::getProxySubMesh(int index)
+{
+  if ( int(_subMeshes.size()) <= index )
+    _subMeshes.resize( index+1, 0 );
+  if ( !_subMeshes[index] )
+    _subMeshes[index] = new SubMesh;
+  return _subMeshes[index];
+}
+
+//================================================================================
+/*!
+ * \brief Returns a proxy sub-mesh; it is created if not yet exists
+ */
+//================================================================================
+
+StdMeshers_ProxyMesh::SubMesh* StdMeshers_ProxyMesh::getProxySubMesh(const TopoDS_Shape& shape)
+{
+  return getProxySubMesh( shapeIndex( shape ));
+}
+
+//================================================================================
+/*!
+ * \brief Returns a proxy sub-mesh
+ */
+//================================================================================
+
+StdMeshers_ProxyMesh::SubMesh* StdMeshers_ProxyMesh::findProxySubMesh(int shapeIndex) const
+{
+  return shapeIndex < int(_subMeshes.size()) ? _subMeshes[shapeIndex] : 0;
+}
+
+//================================================================================
+/*!
+ * \brief Returns mesh DS
+ */
+//================================================================================
+
+const SMESHDS_Mesh* StdMeshers_ProxyMesh::getMeshDS() const
+{
+  return _mesh ? _mesh->GetMeshDS() : 0;
+}
+
+//================================================================================
+/*!
+ * \brief Return a proxy node or an input node
+ */
+//================================================================================
+
+const SMDS_MeshNode* StdMeshers_ProxyMesh::SubMesh::GetProxyNode( const SMDS_MeshNode* n ) const
+{
+  TN2NMap::iterator n2n;
+  if ( _n2n && ( n2n = _n2n->find( n )) != _n2n->end())
+    return n2n->second;
+  return n;
+}
+
+//================================================================================
+/*!
+ * \brief Deletes temporary elements
+ */
+//================================================================================
+
+void StdMeshers_ProxyMesh::SubMesh::Clear()
+{
+  for ( unsigned i = 0; i < _elements.size(); ++i )
+    if ( _elements[i]->GetID() > 0 )
+      delete _elements[i];
+  _elements.clear();
+}
+
+//================================================================================
+/*!
+ * \brief Return number of elements in a proxy submesh
+ */
+//================================================================================
+
+int StdMeshers_ProxyMesh::SubMesh::NbElements() const
+{
+  return _elements.size();
+}
+
+//================================================================================
+/*!
+ * \brief Return elements of a proxy submesh
+ */
+//================================================================================
+
+SMDS_ElemIteratorPtr StdMeshers_ProxyMesh::SubMesh::GetElements() const
+{
+  return SMDS_ElemIteratorPtr
+    ( new SMDS_ElementVectorIterator( _elements.begin(), _elements.end() ));
+}
+
+//================================================================================
+/*!
+ * \brief Store an element
+ */
+//================================================================================
+
+void StdMeshers_ProxyMesh::SubMesh::AddElement(const SMDS_MeshElement * e)
+{
+  _elements.push_back( e );
+}
diff --git a/src/StdMeshers/StdMeshers_ProxyMesh.hxx b/src/StdMeshers/StdMeshers_ProxyMesh.hxx
new file mode 100644 (file)
index 0000000..dd24b28
--- /dev/null
@@ -0,0 +1,139 @@
+//  Copyright (C) 2007-2010  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
+//  License as published by the Free Software Foundation; either
+//  version 2.1 of the License.
+//
+//  This library is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  Lesser General Public License for more details.
+//
+//  You should have received a copy of the GNU Lesser General Public
+//  License along with this library; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+//  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+// File      : StdMeshers_ProxyMesh.hxx
+// Created   : Thu Dec  2 10:05:35 2010
+// Author    : Edward AGAPOV (eap)
+
+#ifndef __StdMeshers_ProxyMesh_HXX__
+#define __StdMeshers_ProxyMesh_HXX__
+
+#include "SMESH_StdMeshers.hxx"
+
+#include "SMDS_MeshElement.hxx"
+#include "SMESHDS_SubMesh.hxx"
+
+#include <TopoDS_Shape.hxx>
+
+#include <map>
+#include <vector>
+#include <boost/shared_ptr.hpp>
+
+class SMDS_MeshNode;
+class SMESHDS_Mesh;
+class SMESH_Mesh;
+
+/*!
+ * \brief Container of mesh faces substituting other faces in the input mesh of 3D algorithm
+ */
+class STDMESHERS_EXPORT StdMeshers_ProxyMesh
+{
+public:
+
+  typedef boost::shared_ptr<StdMeshers_ProxyMesh> Ptr;
+
+  typedef std::map<const SMDS_MeshNode*, const SMDS_MeshNode*, TIDCompare > TN2NMap;
+
+  //--------------------------------------------------------------------------------
+  /*!
+   * \brief Proxy sub-mesh
+   */
+  class SubMesh : public SMESHDS_SubMesh
+  {
+  public:
+
+    const TN2NMap* GetNodeNodeMap() const { return _n2n; }
+    const SMDS_MeshNode* GetProxyNode( const SMDS_MeshNode* n ) const;
+    virtual void AddElement(const SMDS_MeshElement * e);
+    virtual int NbElements() const;
+    virtual SMDS_ElemIteratorPtr GetElements() const;
+    virtual void Clear();
+
+    SubMesh():_n2n(0) {}
+    ~SubMesh() { Clear(); }
+
+  protected:
+    std::vector<const SMDS_MeshElement *> _elements;
+    TN2NMap*                              _n2n;
+    friend class StdMeshers_ProxyMesh;
+  };
+  //--------------------------------------------------------------------------------
+  // Public interface
+
+  StdMeshers_ProxyMesh();
+  StdMeshers_ProxyMesh(std::vector<StdMeshers_ProxyMesh::Ptr>& components);
+  virtual ~StdMeshers_ProxyMesh();
+
+  // Returns the submesh of a face; it can be a proxy sub-mesh
+  const SMESHDS_SubMesh* GetSubMesh(const TopoDS_Shape& face) const;
+
+  // Returns the proxy sub-mesh of a face; it can be NULL
+  const SubMesh* GetProxySubMesh(const TopoDS_Shape& face) const;
+
+  // Returns the proxy node of a node; the input node is returned if no proxy exists
+  const SMDS_MeshNode* GetProxyNode( const SMDS_MeshNode* node ) const;
+
+  // Returns iterator on all faces of the mesh taking into account substitutions
+  // To be used in case of mesh without shape
+  SMDS_ElemIteratorPtr GetFaces() const;
+
+  // Returns iterator on all faces on the face taking into account substitutions
+  SMDS_ElemIteratorPtr GetFaces(const TopoDS_Shape& face) const;
+
+  // Return total nb of faces taking into account substitutions
+  int NbFaces() const;
+
+
+  //--------------------------------------------------------------------------------
+  // Interface for descendants
+ protected:
+
+  void setMesh(const SMESH_Mesh& mesh) { _mesh = &mesh; }
+
+  const SMESHDS_Mesh* getMeshDS() const;
+
+  int shapeIndex(const TopoDS_Shape& shape) const;
+
+  // returns a proxy sub-mesh; zero index is for the case of mesh w/o shape
+  SubMesh* findProxySubMesh(int shapeIndex=0) const;
+
+  // returns a proxy sub-mesh; it is created if not yet exists
+  SubMesh* getProxySubMesh(int shapeIndex=0);
+
+  // returns a proxy sub-mesh; it is created if not yet exists
+  SubMesh* getProxySubMesh(const TopoDS_Shape& shape=TopoDS_Shape());
+
+
+ private:
+
+  const SMESH_Mesh*       _mesh;
+
+  // proxy sub-meshes; index in vector == shapeIndex(shape)
+  std::vector< SubMesh* > _subMeshes;
+
+  // types of elements needed to implement NbFaces() and GetFaces();
+  // if _allowedTypes is empty, only elements from _subMeshes are returned,
+  // else elements of _mesh filtered using allowedTypes are additionally returned
+  std::vector< SMDSAbs_EntityType> _allowedTypes;
+
+  // Complex submesh used to iterate over elements in other sub-meshes
+  mutable SubMesh _subContainer;
+};
+
+#endif
diff --git a/src/StdMeshers/StdMeshers_ViscousLayers.cxx b/src/StdMeshers/StdMeshers_ViscousLayers.cxx
new file mode 100644 (file)
index 0000000..63f4433
--- /dev/null
@@ -0,0 +1,2022 @@
+//  Copyright (C) 2007-2011  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
+//  License as published by the Free Software Foundation; either
+//  version 2.1 of the License.
+//
+//  This library is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  Lesser General Public License for more details.
+//
+//  You should have received a copy of the GNU Lesser General Public
+//  License along with this library; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+//  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+// File      : StdMeshers_ViscousLayers.cxx
+// Created   : Wed Dec  1 15:15:34 2010
+// Author    : Edward AGAPOV (eap)
+
+#include "StdMeshers_ViscousLayers.hxx"
+
+#include "SMDS_EdgePosition.hxx"
+#include "SMDS_FaceOfNodes.hxx"
+#include "SMDS_FacePosition.hxx"
+#include "SMDS_MeshNode.hxx"
+#include "SMESHDS_Group.hxx"
+#include "SMESHDS_Hypothesis.hxx"
+#include "SMESH_Algo.hxx"
+#include "SMESH_ComputeError.hxx"
+#include "SMESH_Gen.hxx"
+#include "SMESH_Group.hxx"
+#include "SMESH_Mesh.hxx"
+#include "SMESH_MesherHelper.hxx"
+#include "SMESH_subMesh.hxx"
+#include "SMESH_subMeshEventListener.hxx"
+#include "StdMeshers_ProxyMesh.hxx"
+
+#include "utilities.h"
+
+#include <BRep_Tool.hxx>
+#include <Geom_Curve.hxx>
+#include <Precision.hxx>
+#include <TopExp.hxx>
+#include <TopExp_Explorer.hxx>
+#include <TopTools_IndexedMapOfShape.hxx>
+#include <TopoDS.hxx>
+#include <TopoDS_Edge.hxx>
+#include <TopoDS_Face.hxx>
+#include <TopoDS_Vertex.hxx>
+#include <gp_Ax1.hxx>
+#include <gp_Vec.hxx>
+#include <gp_XY.hxx>
+#include <gp_XYZ.hxx>
+
+#include <list>
+#include <string>
+#include <math.h>
+
+using namespace std;
+
+//================================================================================
+namespace VISCOUS
+{
+  /*!
+   * \brief StdMeshers_ProxyMesh computed by _ViscousBuilder for a solid.
+   * It is stored in a SMESH_subMesh of the solid as SMESH_subMeshEventListenerData
+   */
+  struct _MeshOfSolid : public StdMeshers_ProxyMesh,
+                        public SMESH_subMeshEventListenerData
+  {
+    _MeshOfSolid( SMESH_Mesh* mesh):SMESH_subMeshEventListenerData( /*isDeletable=*/true)
+    {
+      StdMeshers_ProxyMesh::setMesh( *mesh );
+    }
+
+    // returns submesh for a geom face
+    StdMeshers_ProxyMesh::SubMesh* getFaceData(const TopoDS_Face& F, bool create=false)
+    {
+      int i = StdMeshers_ProxyMesh::shapeIndex(F);
+      return create ? StdMeshers_ProxyMesh::getProxySubMesh(i) : findProxySubMesh(i);
+    }
+  };
+  //--------------------------------------------------------------------------------
+  /*!
+   * \brief Listener of events of 3D sub-meshes computed with viscous layers.
+   * It is used to store data computed by _ViscousBuilder for a sub-mesh and to
+   * delete the data as soon as it has been used
+   */
+  struct _ViscousListener : SMESH_subMeshEventListener
+  {
+    _ViscousListener(): SMESH_subMeshEventListener(/*isDeletable=*/false) {}
+    static SMESH_subMeshEventListener* Get() { static _ViscousListener l; return &l; }
+    virtual void ProcessEvent(const int          event,
+                              const int          eventType,
+                              SMESH_subMesh*     subMesh,
+                              SMESH_subMeshEventListenerData* data,
+                              const SMESH_Hypothesis*         hyp)
+    {
+//       if ( SMESH_subMesh::COMPUTE == event &&
+//            SMESH_subMesh::COMPUTE_EVENT == eventType )
+      {
+        // delete StdMeshers_ProxyMesh containing temporary faces
+        subMesh->DeleteEventListener( _ViscousListener::Get() );
+      }
+    }
+    static _MeshOfSolid* GetSolidMesh(SMESH_Mesh*         mesh,
+                                      const TopoDS_Shape& solid,
+                                      bool                toCreate=false)
+    {
+      if ( !mesh ) return 0;
+      SMESH_subMesh* sm = mesh->GetSubMesh(solid);
+      _MeshOfSolid* data = (_MeshOfSolid*) sm->GetEventListenerData( Get() );
+      if ( !data && toCreate )
+        ( data = new _MeshOfSolid(mesh)), sm->SetEventListener( Get(), data, sm );
+      return data;
+    }
+
+//     static void RemoveSolidMesh(SMESH_Mesh*         mesh,
+//                                 const TopoDS_Shape& solid)
+//     {
+//       mesh->GetSubMesh(solid)->DeleteEventListener( _ViscousListener::Get() );
+//     }
+  };
+  //--------------------------------------------------------------------------------
+  /*!
+   * \brief Simplex (triangle or tetrahedron) based on 1 (tria) or 2 (tet) nodes of
+   * _LayerEdge and 2 nodes of the mesh surface beening smoothed.
+   * The class is used to check validity of face or volumes around a smoothed node;
+   * it stores only 2 nodes as the other nodes are stored by _LayerEdge.
+   * For _LayerEdge on geom edge, _Simplex contains just two nodes of neighbour _LayerEdge
+   * basing on the same geom edge
+   */
+  struct _Simplex
+  {
+    const SMDS_MeshNode *_nPrev, *_nNext; // nodes on a smoothed mesh surface
+    _Simplex(const SMDS_MeshNode* nPrev=0, const SMDS_MeshNode* nNext=0)
+      : _nPrev(nPrev), _nNext(nNext) {}
+    bool IsForward(const SMDS_MeshNode* nSrc, const gp_XYZ* pntTgt) const
+    {
+      const double M[3][3] =
+        {{ _nNext->X() - nSrc->X(), _nNext->Y() - nSrc->Y(), _nNext->Z() - nSrc->Z() },
+         { pntTgt->X() - nSrc->X(), pntTgt->Y() - nSrc->Y(), pntTgt->Z() - nSrc->Z() },
+         { _nPrev->X() - nSrc->X(), _nPrev->Y() - nSrc->Y(), _nPrev->Z() - nSrc->Z() }};
+      double determinant = ( + M[0][0]*M[1][1]*M[2][2]
+                             + M[0][1]*M[1][2]*M[2][0]
+                             + M[0][2]*M[1][0]*M[2][1]
+                             - M[0][0]*M[1][2]*M[2][1]
+                             - M[0][1]*M[1][0]*M[2][2]
+                             - M[0][2]*M[1][1]*M[2][0]);
+      return determinant > 1e-100;
+    }
+    bool IsForward(const gp_XY&        tgtUV,
+                   const TopoDS_Face&  face,
+                   SMESH_MesherHelper& helper,
+                   const double        refSign) const
+    {
+      gp_XY prevUV = helper.GetNodeUV( face, _nPrev );
+      gp_XY nextUV = helper.GetNodeUV( face, _nNext );
+      gp_Vec2d v1( tgtUV, prevUV ), v2( tgtUV, nextUV );
+      double d = v1 ^ v2;
+      return d*refSign > 1e-100;
+    }
+  };
+  //--------------------------------------------------------------------------------
+  /*!
+   * \brief Edge normal to surface, connecting a node on solid surface (_nodes[0])
+   * and a node of the most internal layer (_nodes.back())
+   */
+  struct _LayerEdge
+  {
+    vector< const SMDS_MeshNode*> _nodes;
+
+    gp_XYZ              _normal; // to solid surface
+    vector<gp_XYZ>      _pos; // points computed during inflation
+    double              _len; // length achived with the last step
+    double              _cosin; // of angle (_normal ^ surface)
+
+    // face or edge w/o layer along or near which _LayerEdge is inflated
+    TopoDS_Shape        _sWOL;
+    // simplices connected to _nodes[0]; for smoothing and quality check
+    // empty for edge inflating along _sWOL
+    vector<_Simplex>    _simplices;
+
+    void SetNewLength( double len, SMESH_MesherHelper& helper );
+    void InvalidateStep( int curStep );
+    bool Smooth(int& badNb);
+    bool Smooth2d(Handle(Geom_Surface)& surface, SMESH_MesherHelper& helper);
+    bool SegTriaInter( const gp_Ax1&        lastSegment,
+                       const SMDS_MeshNode* n0,
+                       const SMDS_MeshNode* n1,
+                       const SMDS_MeshNode* n2 ) const;
+    gp_Ax1 LastSegment() const;
+    bool IsOnEdge() const { return _simplices.size() == 1; }
+  };
+  //--------------------------------------------------------------------------------
+
+  typedef map< const SMDS_MeshNode*, _LayerEdge*, TIDCompare > TNode2Edge;
+  
+  //--------------------------------------------------------------------------------
+  /*!
+   * \brief Data of a SOLID
+   */
+  struct _SolidData
+  {
+    TopoDS_Shape                    _solid;
+    const StdMeshers_ViscousLayers* _hyp;
+    _MeshOfSolid*                   _proxyMesh;
+    set<int>                        _reversedFaceIds;
+    double                          _stepSize;
+
+    TNode2Edge                      _n2eMap;
+    // edges of _n2eMap. We keep same data in two containers because
+    // iteration over the map is 5 time longer that over the vector
+    vector< _LayerEdge* >           _edges;
+
+    // key: an id of shape (edge or vertex) shared by a face with
+    // layers and a face w/o layers
+    // value: the shape (face or edge) to shrink mesh on.
+    // _LayerEdge's basing on nodes on key shape are inflated along the value shape
+    map< int, TopoDS_Shape >         _shrinkShape2Shape;
+
+    // end index in _edges of _LayerEdge's (map key) inflated along FACE (map value)
+    map< int, TopoDS_Face >          _endEdge2Face;
+
+    int                              _index; // for debug
+
+    _SolidData(const TopoDS_Shape&             s=TopoDS_Shape(),
+               const StdMeshers_ViscousLayers* h=0,
+               _MeshOfSolid*                   m=0) :_solid(s), _hyp(h), _proxyMesh(m) {}
+    ~_SolidData();
+  };
+  //--------------------------------------------------------------------------------
+  /*!
+   * \brief Data of node on a shrinked face
+   */
+  struct _SmoothNode
+  {
+    const SMDS_MeshNode*         _node;
+    //vector<const SMDS_MeshNode*> _nodesAround;
+    vector<_Simplex>             _simplices; // for quality check
+
+    bool Smooth(int&                  badNb,
+                Handle(Geom_Surface)& surface,
+                TopLoc_Location&      loc,
+                SMESH_MesherHelper&   helper,
+                const double          refSign);
+  };
+  //--------------------------------------------------------------------------------
+  /*!
+   * \brief Builder of viscous layers
+   */
+  class _ViscousBuilder
+  {
+  public:
+    _ViscousBuilder();
+    // does it's job
+    SMESH_ComputeErrorPtr Compute(SMESH_Mesh& mesh, const TopoDS_Shape& theShape);
+
+  private:
+
+    bool findSolidsWithLayers();
+    bool findFacesWithLayers();
+    bool makeLayer(_SolidData& data);
+    bool setEdgeData(_LayerEdge& edge, const set<int>& subIds,
+                     SMESH_MesherHelper& helper, _SolidData& data);
+    void getSimplices( const SMDS_MeshNode* node, vector<_Simplex>& simplices,
+                       const set<int>& ingnoreShapes,
+                       const _SolidData* dataToCheckOri = 0);
+    bool inflate(_SolidData& data);
+    bool smoothAndCheck(_SolidData& data);
+    bool refine(_SolidData& data);
+    bool shrink();
+    bool prepareEdgeToShrink( _LayerEdge& edge, const TopoDS_Face& F,
+                              SMESH_MesherHelper& helper,
+                              const SMESHDS_SubMesh* faceSubMesh );
+
+    bool error( string, _SolidData* data );
+    SMESHDS_Mesh* getMeshDS() { return _mesh->GetMeshDS(); }
+
+    // debug
+    void makeGroupOfLE();
+
+    SMESH_Mesh*           _mesh;
+    SMESH_ComputeErrorPtr _error;
+
+    vector< _SolidData >  _sdVec;
+    set<int>              _ignoreShapeIds;
+  };
+}
+
+//================================================================================
+// StdMeshers_ViscousLayers hypothesis
+//
+StdMeshers_ViscousLayers::StdMeshers_ViscousLayers(int hypId, int studyId, SMESH_Gen* gen)
+  :SMESH_Hypothesis(hypId, studyId, gen),
+   _nbLayers(1), _thickness(1), _stretchFactor(1)
+{
+  _name = StdMeshers_ViscousLayers::GetHypType();
+  _param_algo_dim = -3; // auxiliary hyp used by 3D algos
+} // --------------------------------------------------------------------------------
+void StdMeshers_ViscousLayers::SetIgnoreFaces(const std::vector<int>& faceIds)
+{
+  if ( faceIds != _ignoreFaceIds )
+    _ignoreFaceIds = faceIds, NotifySubMeshesHypothesisModification();
+} // --------------------------------------------------------------------------------
+void StdMeshers_ViscousLayers::SetTotalThickness(double thickness)
+{
+  if ( thickness != _thickness )
+    _thickness = thickness, NotifySubMeshesHypothesisModification();
+} // --------------------------------------------------------------------------------
+void StdMeshers_ViscousLayers::SetNumberLayers(int nb)
+{
+  if ( _nbLayers != nb )
+    _nbLayers = nb, NotifySubMeshesHypothesisModification();
+} // --------------------------------------------------------------------------------
+void StdMeshers_ViscousLayers::SetStretchFactor(double factor)
+{
+  if ( _stretchFactor != factor )
+    _stretchFactor = factor, NotifySubMeshesHypothesisModification();
+} // --------------------------------------------------------------------------------
+StdMeshers_ProxyMesh::Ptr StdMeshers_ViscousLayers::Compute(SMESH_Mesh&         theMesh,
+                                                            const TopoDS_Shape& theShape) const
+{
+  using namespace VISCOUS;
+  _ViscousBuilder bulder;
+  SMESH_ComputeErrorPtr err = bulder.Compute( theMesh, theShape );
+  if ( err && !err->IsOK() )
+    return StdMeshers_ProxyMesh::Ptr();
+
+  vector<StdMeshers_ProxyMesh::Ptr> components;
+  TopExp_Explorer exp( theShape, TopAbs_SOLID );
+  for ( ; exp.More(); exp.Next() )
+  {
+    if ( _MeshOfSolid* pm =
+         _ViscousListener::GetSolidMesh( &theMesh, exp.Current(), /*toCreate=*/false))
+    {
+      components.push_back( StdMeshers_ProxyMesh::Ptr( pm ));
+      pm->myIsDeletable  =false; // it will de deleted by boost::shared_ptr
+    }
+    //_ViscousListener::RemoveSolidMesh ( &theMesh, exp.Current() );
+  }
+  switch ( components.size() )
+  {
+  case 0: break;
+
+  case 1: return components[0];
+
+  default: return StdMeshers_ProxyMesh::Ptr( new StdMeshers_ProxyMesh( components ));
+  }
+  return StdMeshers_ProxyMesh::Ptr();
+} // --------------------------------------------------------------------------------
+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];
+  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 );
+  return load;
+} // --------------------------------------------------------------------------------
+bool StdMeshers_ViscousLayers::SetParametersByMesh(const SMESH_Mesh*   theMesh,
+                                                   const TopoDS_Shape& theShape)
+{
+  // TODO
+  return false;
+}
+// END StdMeshers_ViscousLayers hypothesis
+//================================================================================
+
+namespace
+{
+  gp_XYZ getEdgeDir( const TopoDS_Edge& E, const TopoDS_Vertex& fromV )
+  {
+    gp_Vec dir;
+    double f,l;
+    Handle(Geom_Curve) c = BRep_Tool::Curve( E, f, l );
+    gp_Pnt p = BRep_Tool::Pnt( fromV );
+    double distF = p.SquareDistance( c->Value( f ));
+    double distL = p.SquareDistance( c->Value( l ));
+    c->D1(( distF < distL ? f : l), p, dir );
+    if ( distL < distF ) dir.Reverse();
+    return dir.XYZ();
+  }
+  //--------------------------------------------------------------------------------
+  // DEBUG. Dump intermediate nodes positions into a python script
+  //#define __PY_DUMP
+#ifdef __PY_DUMP
+  ofstream* py;
+  void initPyDump()
+  {
+    const char* fname = "/tmp/viscous.py";
+    cout << "execfile('"<<fname<<"')"<<endl;
+    py = new ofstream(fname);
+    *py << "from smesh import *" << endl
+        << "meshSO = GetCurrentStudy().FindObjectID('0:1:2:3')" << endl
+        << "mesh = Mesh( meshSO.GetObject()._narrow( SMESH.SMESH_Mesh ))"<<endl;
+  }
+  void dumpFunction(const string& fun) { *py<< "def "<<fun<<"():" <<endl; cout<<fun<<"()"<<endl;}
+  void dumpFunctionEnd() { *py << "  return"<< endl; }
+  void dumpMove(const SMDS_MeshNode* n ) {
+    *py<<"  mesh.MoveNode( "<<n->GetID()<< ", "<< n->X()<< ", "<<n->Y()<<", "<< n->Z()<< ") "<<endl;
+  }
+#else
+  void initPyDump() {}
+  void dumpFunction(const string& fun ){}
+  void dumpFunctionEnd() {}
+  void dumpMove(const SMDS_MeshNode* n ){}
+#endif
+}
+
+using namespace VISCOUS;
+
+//================================================================================
+/*!
+ * \brief Constructor of _ViscousBuilder
+ */
+//================================================================================
+
+_ViscousBuilder::_ViscousBuilder()
+{
+  _error = SMESH_ComputeError::New(COMPERR_OK);
+
+}
+//================================================================================
+/*!
+ * \brief Stores error description and returns false
+ */
+//================================================================================
+
+bool _ViscousBuilder::error( string text, _SolidData* data )
+{
+  _error->myName    = COMPERR_ALGO_FAILED;
+  _error->myComment = text;
+  if ( _mesh )
+  {
+    if ( !data && !_sdVec.empty() )
+      data = & _sdVec[0];
+    if ( data )
+    {
+      SMESH_subMesh* sm = _mesh->GetSubMesh( data->_solid );
+      SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
+      if ( smError && smError->myAlgo )
+        _error->myAlgo = smError->myAlgo;
+      smError = _error;
+    }
+  }
+  return false;
+}
+
+//================================================================================
+/*!
+ * \brief Does its job
+ */
+//================================================================================
+
+SMESH_ComputeErrorPtr _ViscousBuilder::Compute(SMESH_Mesh&         theMesh,
+                                               const TopoDS_Shape& theShape)
+{
+#ifdef __PY_DUMP
+  initPyDump();
+#endif
+  // TODO: set priority of solids during Gen::Compute()
+
+  _mesh = & theMesh;
+
+  // check if proxy mesh already computed
+  TopExp_Explorer exp( theShape, TopAbs_SOLID );
+  if ( !exp.More() )
+    return error("No SOLID's in theShape", 0), _error;
+
+  if ( _ViscousListener::GetSolidMesh( _mesh, exp.Current(), /*toCreate=*/false))
+    return SMESH_ComputeErrorPtr(); // everything already computed
+
+
+  findSolidsWithLayers();
+  if ( _error->IsOK() )
+    findFacesWithLayers();
+
+  for ( unsigned i = 0; i < _sdVec.size(); ++i )
+  {
+    if ( ! makeLayer(_sdVec[i]) )
+      break;
+    
+    if ( ! inflate(_sdVec[i]) ) {
+      makeGroupOfLE();
+      break;
+    }
+    if ( ! refine(_sdVec[i]) )
+      break;
+  }
+  if ( _error->IsOK() )
+    shrink();
+
+#ifdef __PY_DUMP
+  delete py; py = 0;
+#endif
+  return _error;
+}
+
+//================================================================================
+/*!
+ * \brief Finds SOLIDs to compute using viscous layers. Fill _sdVec
+ */
+//================================================================================
+
+bool _ViscousBuilder::findSolidsWithLayers()
+{
+  // get all solids
+  TopTools_IndexedMapOfShape allSolids;
+  TopExp::MapShapes( _mesh->GetShapeToMesh(), TopAbs_SOLID, allSolids );
+  _sdVec.reserve( allSolids.Extent());
+
+  SMESH_Gen* gen = _mesh->GetGen();
+  for ( int i = 1; i <= allSolids.Extent(); ++i )
+  {
+    // find StdMeshers_ViscousLayers hyp assigned to the i-th solid
+    SMESH_Algo* algo = gen->GetAlgo( *_mesh, allSolids(i) );
+    if ( !algo ) continue;
+    // TODO: check if algo is hidden
+    const list <const SMESHDS_Hypothesis *> & allHyps =
+      algo->GetUsedHypothesis(*_mesh, allSolids(i), /*ignoreAuxiliary=*/false);
+    list< const SMESHDS_Hypothesis *>::const_iterator hyp = allHyps.begin();
+    const StdMeshers_ViscousLayers* viscHyp = 0;
+    for ( ; hyp != allHyps.end() && !viscHyp; ++hyp )
+      viscHyp = dynamic_cast<const StdMeshers_ViscousLayers*>( *hyp );
+    if ( viscHyp )
+    {
+      _MeshOfSolid* proxyMesh = _ViscousListener::GetSolidMesh( _mesh,
+                                                                allSolids(i),
+                                                                /*toCreate=*/true);
+      _sdVec.push_back( _SolidData( allSolids(i), viscHyp, proxyMesh ));
+      _sdVec.back()._index = _sdVec.size()-1; // debug
+    }
+  }
+  if ( _sdVec.empty() )
+    return error
+      ( SMESH_Comment(StdMeshers_ViscousLayers::GetHypType()) << " hypothesis not found",0);
+
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief 
+ */
+//================================================================================
+
+bool _ViscousBuilder::findFacesWithLayers()
+{
+  // collect all faces to ignore defined by hyp
+  vector<TopoDS_Shape> ignoreFaces;
+  for ( unsigned i = 0; i < _sdVec.size(); ++i )
+  {
+    vector<int> ids = _sdVec[i]._hyp->GetIgnoreFaces();
+    for ( unsigned i = 0; i < ids.size(); ++i )
+    {
+      const TopoDS_Shape& s = getMeshDS()->IndexToShape( ids[i] );
+      if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
+      {
+        _ignoreShapeIds.insert( ids[i] );
+        ignoreFaces.push_back( s );
+      }
+    }
+  }
+
+  // ignore internal faces
+  SMESH_MesherHelper helper( *_mesh );
+  TopExp_Explorer exp;
+  for ( unsigned i = 0; i < _sdVec.size(); ++i )
+  {
+    exp.Init( _sdVec[i]._solid.Oriented( TopAbs_FORWARD ), TopAbs_FACE );
+    for ( ; exp.More(); exp.Next() )
+    {
+      int faceInd = getMeshDS()->ShapeToIndex( exp.Current() );
+      if ( helper.NbAncestors( exp.Current(), *_mesh, TopAbs_SOLID ) > 1 )
+      {     
+        _ignoreShapeIds.insert( faceInd );
+        ignoreFaces.push_back( exp.Current() );
+        if ( SMESH_Algo::IsReversedSubMesh( TopoDS::Face( exp.Current() ), getMeshDS()))
+          _sdVec[i]._reversedFaceIds.insert( faceInd );
+      }
+    }
+  }
+
+  // Find faces to shrink mesh on (solution 2 in issue 0020832);
+  // we use solution 1 for shared faces w/o layers since there is no generic way
+  // to know whether after shrinking 2D mesh remains valid for other 3D algo
+  TopTools_IndexedMapOfShape shapes;
+  for ( unsigned i = 0; i < _sdVec.size(); ++i )
+  {
+    TopExp::MapShapes(_sdVec[i]._solid, TopAbs_EDGE, shapes);
+    for ( int iE = 1; iE <= shapes.Extent(); ++iE )
+    {
+      const TopoDS_Shape& edge = shapes(iE);
+      // find 2 faces sharing an edge
+      TopoDS_Shape FF[2];
+      PShapeIteratorPtr fIt = helper.GetAncestors(edge, *_mesh, TopAbs_FACE);
+      while ( fIt->more())
+      {
+        const TopoDS_Shape* f = fIt->next();
+        if ( helper.IsSubShape( *f, _sdVec[i]._solid))
+          FF[ int( !FF[0].IsNull()) ] = *f;
+      }
+      ASSERT( !FF[1].IsNull() );
+      // check presence of layers on them
+      int ignore[2];
+      for ( int j = 0; j < 2; ++j )
+        ignore[j] = _ignoreShapeIds.count ( getMeshDS()->ShapeToIndex( FF[j] ));
+      if ( ignore[0] == ignore[1] ) continue; // nothing interesting
+      // check if a face w/o layers (WOL) is shared
+      if ( !ignore[0] ) std::swap( FF[0], FF[1] ); // set face WOL to FF[0]
+//       bool shareWOL = ( helper.NbAncestors( FF[0], *_mesh, TopAbs_SOLID ) > 1 );
+//       if ( shareWOL )
+      {
+        // add edge to maps
+        int edgeInd = getMeshDS()->ShapeToIndex( edge );
+        _sdVec[i]._shrinkShape2Shape.insert( make_pair( edgeInd, FF[0] ));
+      }
+    }
+
+    // Find shapes along which to inflate _LayerEdge on vertex
+
+    shapes.Clear();
+    TopExp::MapShapes(_sdVec[i]._solid, TopAbs_VERTEX, shapes);
+    for ( int iV = 1; iV <= shapes.Extent(); ++iV )
+    {
+      const TopoDS_Shape& vertex = shapes(iV);
+      // find faces WOL sharing the vertex
+      vector< TopoDS_Shape > faces;
+      int totalNbFaces = 0;
+      PShapeIteratorPtr fIt = helper.GetAncestors(vertex, *_mesh, TopAbs_FACE);
+      while ( fIt->more())
+      {
+        const TopoDS_Shape* f = fIt->next();
+        totalNbFaces++;
+        if ( helper.IsSubShape( *f, _sdVec[i]._solid) &&
+             _ignoreShapeIds.count ( getMeshDS()->ShapeToIndex( *f )))
+          faces.push_back( *f );
+      }
+      if ( faces.size() == totalNbFaces || faces.empty() )
+        continue; // not layers at this vertex or no WOL
+      int vInd = getMeshDS()->ShapeToIndex( vertex );
+      switch ( faces.size() )
+      {
+      case 1:
+        {
+          _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, faces[0] )); break;
+        }
+      case 2:
+        {
+          // find an edge shared by 2 faces
+          PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE);
+          while ( eIt->more())
+          {
+            const TopoDS_Shape* e = eIt->next();
+            if ( helper.IsSubShape( *e, faces[0]) &&
+                 helper.IsSubShape( *e, faces[1]) &&
+                 helper.IsSubShape( vertex, *e ))
+            {
+              _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, *e )); break;
+            }
+          }
+          break;
+        }
+      default:
+        return error("Not yet supported case", &_sdVec[i]);
+      }
+    }
+  }
+  // TODO: treat cases with solution 1.
+
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief Create the inner surface of the viscous layer and prepare data for infation
+ */
+//================================================================================
+
+bool _ViscousBuilder::makeLayer(_SolidData& data)
+{
+  // get all sub-shapes to make layers on
+  set<int> subIds, faceIds;
+  set<int>::iterator id;
+  TopExp_Explorer exp( data._solid, TopAbs_FACE );
+  for ( ; exp.More(); exp.Next() )
+    if ( ! _ignoreShapeIds.count( getMeshDS()->ShapeToIndex( exp.Current() )))
+    {
+      SMESH_subMesh* fSubM = _mesh->GetSubMesh( exp.Current() );
+      faceIds.insert( fSubM->GetId() );
+      SMESH_subMeshIteratorPtr subIt =
+        fSubM->getDependsOnIterator(/*includeSelf=*/true, /*complexShapeFirst=*/false);
+      while ( subIt->more() )
+        subIds.insert( subIt->next()->GetId() );
+    }
+
+  // make a map to find new nodes on sub-shapes shared with other solid
+  map< int, TNode2Edge* > s2neMap;
+  map< int, TNode2Edge* >::iterator s2ne;
+  map< int, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
+  for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
+  {
+    int shapeInd = s2s->first;
+    for ( unsigned i = 0; i < _sdVec.size(); ++i )
+    {
+      map< int, TopoDS_Shape >::iterator s2s2 = _sdVec[i]._shrinkShape2Shape.find( shapeInd );
+      if ( *s2s == *s2s2 && !_sdVec[i]._n2eMap.empty() )
+      {
+        s2neMap.insert( make_pair( shapeInd, &_sdVec[i]._n2eMap ));
+        break;
+      }
+    }
+  }
+
+  // Create temporary faces and _LayerEdge's
+
+  data._stepSize = numeric_limits<double>::max();
+
+  SMESH_MesherHelper helper( *_mesh );
+  helper.SetSubShape( data._solid );
+  helper.SetElementsOnShape(true);
+
+  vector< const SMDS_MeshNode*> newNodes;
+  TNode2Edge::iterator n2e2;
+
+  // collect all _LayerEdge's to inflate along a FACE
+  TopTools_IndexedMapOfShape srinkFaces; // to index only srinkFaces
+  map< int, vector<_LayerEdge*> > srinkFace2edges;
+
+  for ( set<int>::iterator id = faceIds.begin(); id != faceIds.end(); ++id )
+  {
+    SMESHDS_SubMesh* smDS = getMeshDS()->MeshElements( *id );
+    if ( !smDS ) return error(SMESH_Comment("Not meshed face ") << *id, &data );
+
+    const TopoDS_Face& F = TopoDS::Face( getMeshDS()->IndexToShape( *id ));
+    StdMeshers_ProxyMesh::SubMesh* proxySub =
+      data._proxyMesh->getFaceData( F, /*create=*/true);
+
+    SMDS_ElemIteratorPtr eIt = smDS->GetElements();
+    while ( eIt->more() )
+    {
+      const SMDS_MeshElement* face = eIt->next();
+      newNodes.resize( face->NbCornerNodes() );
+      double faceMaxCosin = -1;
+      for ( int i = 0 ; i < face->NbCornerNodes(); ++i )
+      {
+        const SMDS_MeshNode* n = face->GetNode(i);
+        TNode2Edge::iterator n2e = data._n2eMap.insert( make_pair( n, (_LayerEdge*)0 )).first;
+        if ( !(*n2e).second )
+        {
+          // add a _LayerEdge
+          _LayerEdge* edge = new _LayerEdge();
+          n2e->second = edge;
+          edge->_nodes.push_back( n );
+
+          // set edge data or find already refined _LayerEdge and get data from it
+          if ( n->GetPosition()->GetTypeOfPosition() != SMDS_TOP_FACE &&
+               ( s2ne = s2neMap.find( n->GetPosition()->GetShapeId() )) != s2neMap.end() &&
+               ( n2e2 = (*s2ne).second->find( n )) != s2ne->second->end())
+          {
+            _LayerEdge* foundEdge = (*n2e2).second;
+            edge->_nodes  = foundEdge->_nodes;
+            edge->_normal = foundEdge->_normal;
+            edge->_len    = 0;
+            edge->_cosin  = foundEdge->_cosin;
+            edge->_sWOL   = foundEdge->_sWOL;
+            // location of the last node is modified but we can restore
+            // it by node position on _sWOL stored by the node
+            const_cast< SMDS_MeshNode* >
+              ( edge->_nodes.back() )->setXYZ( n->X(), n->Y(), n->Z() );
+          }
+          else
+          {
+            edge->_nodes.push_back( helper.AddNode( n->X(), n->Y(), n->Z() ));
+            if ( !setEdgeData( *edge, subIds, helper, data ))
+              return false;
+          }
+          if ( edge->_cosin <= 1. )
+          {
+            if ( edge->_cosin > faceMaxCosin )
+              faceMaxCosin = edge->_cosin;
+          }
+          if ( edge->IsOnEdge() )
+          {
+            // _LayerEdge is based on EDGE
+            int faceId = srinkFaces.Add( edge->_sWOL );
+            srinkFace2edges[ faceId ].push_back( edge );
+          }
+        }
+        newNodes[ i ] = n2e->second->_nodes.back();
+      }
+      // create a temporary face
+      const SMDS_MeshElement* newFace;
+      if ( newNodes.size() == 3 )
+        newFace = new SMDS_FaceOfNodes( newNodes[0], newNodes[1], newNodes[2] );
+      else
+        newFace = new SMDS_FaceOfNodes( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
+      proxySub->AddElement( newFace );
+
+      // compute inflation step size by min size of element on a convex surface
+      if ( faceMaxCosin > 0.1 )
+      {
+        double elemMinSize = numeric_limits<double>::max();
+        for ( unsigned i = 1; i < newNodes.size(); ++i )
+        {
+          double len = SMESH_MeshEditor::TNodeXYZ( newNodes[i-1] ).Distance( newNodes[i] );
+          if ( len < elemMinSize && len > numeric_limits<double>::min() )
+            elemMinSize = len;
+        }
+        double newStep = 0.8 * elemMinSize / faceMaxCosin;
+        if ( newStep < data._stepSize )
+          data._stepSize = newStep;
+      }
+    } // loop on elements of a FACE
+  } // loop on FACEs of a SOLID
+
+  // Put _LayerEdge's into a vector;
+  // first we put _LayerEdge's based on EDGES to smooth them before others
+
+  data._edges.reserve( data._n2eMap.size() );
+  map< int, vector<_LayerEdge*> >::iterator fi2e = srinkFace2edges.begin();
+  for ( ; fi2e != srinkFace2edges.end(); ++fi2e )
+  {
+    const TopoDS_Face&     F = TopoDS::Face( srinkFaces( fi2e->first ));
+    vector<_LayerEdge*> eVec = fi2e->second;
+    data._edges.insert( data._edges.end(), eVec.begin(), eVec.end() );
+    data._endEdge2Face[ data._edges.size() ] = F;
+  }
+  TNode2Edge::iterator n2e = data._n2eMap.begin();
+  for ( ; n2e != data._n2eMap.end(); ++n2e )
+  {
+    _LayerEdge* e = n2e->second;
+    if ( !e->IsOnEdge()  )
+      data._edges.push_back( e );
+  }
+
+  // Set new nodes into simplices
+  //int i0 = data._endEdge2Face.empty() ? 0 : data._endEdge2Face.rbegin()->first;
+  for ( unsigned i = 0; i < data._edges.size(); ++i )
+  {
+    //if ( !data._edges[i]->_sWOL.IsNull() ) continue;
+    for ( unsigned j = 0; j < data._edges[i]->_simplices.size(); ++j )
+    {
+      _Simplex& s = data._edges[i]->_simplices[j];
+      s._nPrev = data._n2eMap[ s._nPrev ]->_nodes.back();
+      s._nNext = data._n2eMap[ s._nNext ]->_nodes.back();
+    }
+  }
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief Set data of _LayerEdge needed for smoothing
+ *  \param subIds - ids of sub-shapes of a SOLID to take into account faces from
+ */
+//================================================================================
+
+bool _ViscousBuilder::setEdgeData(_LayerEdge&          edge,
+                                  const set<int>&      subIds,
+                                  SMESH_MesherHelper&  helper,
+                                  _SolidData&          data)
+{
+  SMESH_MeshEditor editor(_mesh);
+
+  const SMDS_MeshNode* node = edge._nodes[0];
+
+  edge._len = 0;
+
+  // ---------------------
+  // Compute edge._normal
+  // ---------------------
+
+  edge._normal.SetCoord(0,0,0);
+
+  gp_Pnt p;
+  gp_Vec du, dv, geomNorm;
+  double f,l;
+  bool normOK = true;
+
+  int shapeInd = node->GetPosition()->GetShapeId();
+  map< int, TopoDS_Shape >::const_iterator s2s = data._shrinkShape2Shape.find( shapeInd );
+  bool onShrinkShape ( s2s != data._shrinkShape2Shape.end() );
+  TopoDS_Shape vertEdge;
+
+  if ( onShrinkShape ) // one of faces the node is on has no layers
+  {
+    vertEdge = getMeshDS()->IndexToShape( s2s->first ); // vertex or edge
+    if ( s2s->second.ShapeType() == TopAbs_EDGE )
+    {
+      // inflate from VERTEX along EDGE
+      edge._normal = getEdgeDir( TopoDS::Edge( s2s->second ), TopoDS::Vertex( vertEdge ));
+    }
+    else if ( vertEdge.ShapeType() == TopAbs_VERTEX )
+    {
+      // inflate from VERTEX along FACE
+      vector< TopoDS_Shape > edges; // sharing a vertex
+      PShapeIteratorPtr eIt = helper.GetAncestors( vertEdge, *_mesh, TopAbs_EDGE);
+      while ( eIt->more())
+      {
+        const TopoDS_Shape* e = eIt->next();
+        if ( helper.IsSubShape( *e, s2s->second ) &&
+             BRep_Tool::Curve( TopoDS::Edge( *e ), f, l))
+          edges.push_back( *e );
+      }
+      if ( edges.size() != 2 )
+        return error("More that 2 edges share a vertex in face", &data);
+      for ( int i = 0; i < 2; ++i )
+      {
+        gp_Vec edgeDir = getEdgeDir( TopoDS::Edge( edges[i] ), TopoDS::Vertex( vertEdge ));
+        double size2 = edgeDir.SquareMagnitude();
+        if ( size2 > numeric_limits<double>::min() )
+          edgeDir /= sqrt( size2 );
+        else
+          normOK = false;
+        edge._normal += edgeDir.XYZ() / 2;
+      }
+    }
+    else
+    {
+      // inflate from EDGE along FACE
+      gp_XY uv = helper.GetNodeUV( TopoDS::Face( s2s->second ), node, 0, &normOK );
+      Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face( s2s->second ));
+      surface->D1( uv.X(),uv.Y(), p, du,dv );
+      geomNorm = du ^ dv;
+      Handle(Geom_Curve) c = BRep_Tool::Curve( TopoDS::Edge( vertEdge), f, l );
+      f = helper.GetNodeU( TopoDS::Edge( vertEdge ), node, 0, &normOK );
+      c->D1( f, p, du );
+      TopAbs_Orientation o = helper.GetSubShapeOri( s2s->second.Oriented(TopAbs_FORWARD),vertEdge);
+      if ( o == TopAbs_REVERSED )
+        du.Reverse();
+      edge._normal = (geomNorm ^ du ).XYZ();
+    }
+  }
+  else // layers are on all faces of SOLID the node is on
+  {
+    // find indices of geom faces the node lies on
+    set<int> faceIds;
+    if  ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
+    {
+      faceIds.insert( node->GetPosition()->GetShapeId() );
+    }
+    else
+    {
+      SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
+      while ( fIt->more() )
+        faceIds.insert( editor.FindShape(fIt->next()));
+    }
+
+    set<int>::iterator id = faceIds.begin();
+    int totalNbFaces = 0/*, nbLayerFaces = 0*/;
+    for ( ; id != faceIds.end(); ++id )
+    {
+      const TopoDS_Shape& s = getMeshDS()->IndexToShape( *id );
+      if ( s.IsNull() || s.ShapeType() != TopAbs_FACE || !subIds.count( *id ))
+        continue;
+      totalNbFaces++;
+      //nbLayerFaces += subIds.count( *id );
+      const TopoDS_Face& f = TopoDS::Face( s );
+
+      gp_XY uv = helper.GetNodeUV( f, node, 0, &normOK );
+      Handle(Geom_Surface) surface = BRep_Tool::Surface( f );
+      surface->D1( uv.X(),uv.Y(), p, du,dv );
+      geomNorm = du ^ dv;
+      double size2 = geomNorm.SquareMagnitude();
+      if ( size2 > numeric_limits<double>::min() )
+        geomNorm /= sqrt( size2 );
+      else
+        normOK = false;
+      if ( helper.GetSubShapeOri( data._solid, f ) != TopAbs_REVERSED )
+        geomNorm.Reverse();
+      edge._normal += geomNorm.XYZ();
+    }
+    if ( totalNbFaces == 0 )
+      return error(SMESH_Comment("Can't get normal to node ") << node->GetID(), &data);
+
+    edge._normal /= totalNbFaces;
+  }
+
+  double normSize = edge._normal.SquareModulus();
+  if ( normSize < numeric_limits<double>::min() )
+    return error(SMESH_Comment("Bad normal at node ")<< node->GetID(), &data );
+
+  edge._normal /= sqrt( normSize );
+
+  // TODO: if ( !normOK ) then get normal by mesh faces
+
+  // Set the rest data
+  // --------------------
+  if ( onShrinkShape )
+  {
+    edge._cosin = 2;
+    edge._sWOL = (*s2s).second;
+
+    // set initial position which is parameters on _sWOL in this case
+    SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edge._nodes.back() );
+    if ( SMESHDS_SubMesh* sm = getMeshDS()->MeshElements( shapeInd ))
+      sm->RemoveNode( tgtNode , /*isNodeDeleted=*/false );
+    if ( edge._sWOL.ShapeType() == TopAbs_EDGE )
+    {
+      double u = helper.GetNodeU( TopoDS::Edge( edge._sWOL ), node, 0, &normOK );
+      edge._pos.push_back( gp_XYZ( u, 0, 0));
+      getMeshDS()->SetNodeOnEdge( tgtNode, TopoDS::Edge( edge._sWOL ), u );
+    }
+    else // TopAbs_FACE
+    {
+      gp_XY uv = helper.GetNodeUV( TopoDS::Face( edge._sWOL ), node, 0, &normOK );
+      edge._pos.push_back( gp_XYZ( uv.X(), uv.Y(), 0));
+      getMeshDS()->SetNodeOnFace( tgtNode, TopoDS::Face( edge._sWOL ), uv.X(), uv.Y() );
+    }
+    // set neighbour nodes for a _LayerEdge based on EDGE
+    if ( vertEdge.ShapeType() != TopAbs_VERTEX )
+    {
+      edge._simplices.resize( 1 );
+      _Simplex& neighborNodes = edge._simplices.back();
+      SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Edge);
+      while ( eIt->more() && !neighborNodes._nNext )
+      {
+        const SMDS_MeshElement* e = eIt->next();
+        if ( !subIds.count( editor.FindShape(e)))
+          continue;
+        const SMDS_MeshNode* n2 = e->GetNode( 0 );
+        if ( n2 == node ) n2 = e->GetNode( 1 );
+        (neighborNodes._nPrev ? neighborNodes._nNext : neighborNodes._nPrev ) = n2;
+      }
+    }
+  }
+  else
+  {
+    double angle = ( du+dv ).Angle( edge._normal ); // [0,PI]
+    edge._cosin = cos( angle );
+
+    edge._pos.push_back( SMESH_MeshEditor::TNodeXYZ( node ));
+
+    if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
+      getSimplices( node, edge._simplices, _ignoreShapeIds, &data );
+  }
+  return true;
+}
+//================================================================================
+/*!
+ * \brief Fills a vector<_Simplex > 
+ */
+//================================================================================
+
+void _ViscousBuilder::getSimplices( const SMDS_MeshNode* node,
+                                    vector<_Simplex>&    simplices,
+                                    const set<int>&      ingnoreShapes,
+                                    const _SolidData*    dataToCheckOri)
+{
+  SMESH_MeshEditor editor( _mesh );
+  SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
+  while ( fIt->more() )
+  {
+    const SMDS_MeshElement* f = fIt->next();
+    const int shapeInd = editor.FindShape( f );
+    if ( ingnoreShapes.count( shapeInd )) continue;
+    const int nbNodes = f->NbCornerNodes();
+    int srcInd = f->GetNodeIndex( node );
+    const SMDS_MeshNode* nPrev = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd-1, nbNodes ));
+    const SMDS_MeshNode* nNext = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd+1, nbNodes ));
+    if ( dataToCheckOri && dataToCheckOri->_reversedFaceIds.count( shapeInd ))
+      std::swap( nPrev, nNext );
+    simplices.push_back( _Simplex( nPrev, nNext ));
+  }
+  simplices.resize( simplices.size() );
+}
+
+//================================================================================
+/*!
+ * \brief DEBUG. Create groups of edges contating segments of _LayerEdge's
+ */
+//================================================================================
+
+void _ViscousBuilder::makeGroupOfLE()
+{
+#ifdef _DEBUG_
+  for ( unsigned i = 0 ; i < _sdVec.size(); ++i )
+  {
+    if ( _sdVec[i]._edges.empty() ) continue;
+    string name = SMESH_Comment("_LayerEdge's_") << i;
+    int id;
+    SMESH_Group* g = _mesh->AddGroup(SMDSAbs_Edge, name.c_str(), id );
+    SMESHDS_Group* gDS = (SMESHDS_Group*)g->GetGroupDS();
+    SMESHDS_Mesh* mDS = _mesh->GetMeshDS();
+
+    for ( unsigned j = 0 ; j < _sdVec[i]._edges.size(); ++j )
+    {
+      _LayerEdge* le = _sdVec[i]._edges[j];
+      for ( unsigned iN = 1; iN < le->_nodes.size(); ++iN )
+        gDS->SMDSGroup().Add( mDS->AddEdge( le->_nodes[iN-1], le->_nodes[iN]));
+    }
+
+    name = SMESH_Comment("Normals_") << i;
+    g = _mesh->AddGroup(SMDSAbs_Edge, name.c_str(), id );
+    gDS = (SMESHDS_Group*)g->GetGroupDS();
+    SMESH_MesherHelper helper( *_mesh );
+    for ( unsigned j = 0 ; j < _sdVec[i]._edges.size(); ++j )
+    {
+      _LayerEdge leCopy = *_sdVec[i]._edges[j];
+      leCopy._len = 0;
+      SMESH_MeshEditor::TNodeXYZ nXYZ( leCopy._nodes[0] );
+      SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( leCopy._nodes.back() );
+      n->setXYZ( nXYZ.X(), nXYZ.Y(), nXYZ.Z() );
+      leCopy.SetNewLength( _sdVec[i]._stepSize, helper );
+      gDS->SMDSGroup().Add( mDS->AddEdge( leCopy._nodes[0], n ));
+    }
+  }
+#endif  
+}
+
+//================================================================================
+/*!
+ * \brief Increase length of _LayerEdge's to reach the required thickness of layers
+ */
+//================================================================================
+
+bool _ViscousBuilder::inflate(_SolidData& data)
+{
+  SMESH_MesherHelper helper( *_mesh );
+
+  double tgtThick = data._hyp->GetTotalThickness();
+  if ( data._stepSize > tgtThick )
+    data._stepSize = tgtThick;
+  double avgThick = 0, curThick = 0;
+  int nbSteps = 0, nbRepeats = 0;
+  while ( 1.01 * avgThick < tgtThick )
+  {
+    dumpFunction(SMESH_Comment("inflate")<<data._index<<"_step"<<nbSteps); // debug
+
+    // new target length
+    curThick += data._stepSize;
+    if ( curThick > tgtThick )
+    {
+      curThick = tgtThick + ( tgtThick-avgThick ) * nbRepeats;
+      nbRepeats++;
+    }
+    // elongate _LayerEdge's
+    for ( unsigned i = 0; i < data._edges.size(); ++i )
+      data._edges[i]->SetNewLength( curThick, helper );
+
+    dumpFunctionEnd();
+
+    // improve aand check quality
+    if ( !smoothAndCheck( data ))
+    {
+      if ( nbSteps == 0 )
+        return error("Viscous builder failed at the very first inflation step", &data);
+
+      for ( unsigned i = 0; i < data._edges.size(); ++i )
+        data._edges[i]->InvalidateStep( nbSteps+1 );
+
+      break; // no more inflating possible
+    }
+    // evaluate achieved thickness
+    avgThick = 0;
+    for ( unsigned i = 0; i < data._edges.size(); ++i )
+      avgThick += data._edges[i]->_len;
+    avgThick /= data._edges.size();
+
+    nbSteps++;
+  }
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief Improve quality of layer inner surface and check intersection
+ */
+//================================================================================
+
+bool _ViscousBuilder::smoothAndCheck(_SolidData& data)
+{
+  // Smoothing
+
+  bool moved = true, improved = true;
+  int iOnEdgeEnd = data._endEdge2Face.empty() ? 0 : data._endEdge2Face.rbegin()->first;
+  if ( !data._endEdge2Face.empty() )
+  {
+    // first we smooth _LayerEdge's based on EDGES
+    SMESH_MesherHelper helper(*_mesh);
+    int iBeg = 0;
+    map< int, TopoDS_Face >::iterator nb2f = data._endEdge2Face.begin();
+    for ( ; nb2f != data._endEdge2Face.end(); ++nb2f )
+    {
+      Handle(Geom_Surface) surface = BRep_Tool::Surface( nb2f->second );
+      helper.SetSubShape( nb2f->second );
+      int nb = nb2f->first, step = 0;
+      do {
+        dumpFunction(SMESH_Comment("smooth")<<data._index
+                     << "_OnFace" << helper.GetSubShapeID() <<"_step"<<step++); // debug
+        moved = false;
+        for ( int i = iBeg; i < nb; ++i )
+          moved |= data._edges[i]->Smooth2d(surface, helper);
+
+        dumpFunctionEnd();
+      }
+      while ( moved );
+
+      iBeg = nb;
+
+    }
+  }
+  // smooth in 3D
+  int step = 0, badNb = 0; moved = true;
+  while (( ++step <= 5 && moved ) || improved )
+  {
+    dumpFunction(SMESH_Comment("smooth")<<data._index<<"_step"<<step); // debug
+    int oldBadNb = badNb;
+    badNb = 0;
+    moved = false;
+    for ( unsigned i = iOnEdgeEnd; i < data._edges.size(); ++i )
+      moved = data._edges[i]->Smooth(badNb) || moved;
+    improved = ( badNb < oldBadNb );
+
+    dumpFunctionEnd();
+  }
+  if ( badNb > 0 )
+    return false;
+
+  // 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 );
+  SMESH_ElementSearcher* searcher =
+    editor.GetElementSearcher( data._proxyMesh->GetFaces( data._solid ));
+
+  vector< const SMDS_MeshElement* > suspectFaces;
+  for ( unsigned i = 0; i < data._edges.size(); ++i )
+  {
+    const _LayerEdge& edge = * data._edges[i];
+    gp_Ax1 lastSegment = edge.LastSegment();
+    searcher->GetElementsNearLine( lastSegment, SMDSAbs_Face, suspectFaces );
+
+    for ( unsigned j = 0 ; j < suspectFaces.size(); ++j )
+    {
+      const SMDS_MeshElement* face = suspectFaces[j];
+      if ( face->GetNodeIndex( edge._nodes.back() ) >= 0 )
+        continue; // face sharing _LayerEdge node
+      const int nbNodes = face->NbCornerNodes();
+      bool intFound = false;
+      if ( nbNodes == 3 )
+      {
+        SMDS_MeshElement::iterator nIt = face->begin_nodes();
+        intFound = edge.SegTriaInter( lastSegment, *nIt++, *nIt++, *nIt++ );
+      }
+      else
+      {
+        const SMDS_MeshNode* tria[3];
+        tria[0] = face->GetNode(0);
+        tria[1] = face->GetNode(1);
+        for ( int n2 = 2; n2 < nbNodes && !intFound; ++n2 )
+        {
+          tria[2] = face->GetNode(n2);
+          intFound = edge.SegTriaInter(lastSegment, tria[0], tria[1], tria[2] );
+          tria[1] = tria[2];
+        }
+      }
+      if ( intFound )
+        return false;
+    }
+  }
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief Returns direction of the last segment
+ */
+//================================================================================
+
+gp_Ax1 _LayerEdge::LastSegment() const
+{
+  // find two non-coincident positions
+  gp_XYZ orig = _pos.back();
+  gp_XYZ dir;
+  int iPrev = _pos.size() - 2;
+  while ( iPrev > 0 )
+  {
+    dir = orig - _pos[iPrev--];
+    if ( dir.SquareModulus() > 1e-100 )
+      break;
+  }
+
+  // make gp_Ax1
+  gp_Ax1 segDir;
+  segDir.SetLocation( SMESH_MeshEditor::TNodeXYZ( _nodes.back() ));
+  if ( iPrev < 1 )
+  {
+    segDir.SetDirection( _normal );
+  }
+  else
+  {
+    if ( !_sWOL.IsNull() )
+    {
+      gp_Pnt pPrev;
+      const gp_XYZ& par = _pos[ iPrev ];
+      TopLoc_Location loc;
+      if ( _sWOL.ShapeType() == TopAbs_EDGE )
+      {
+        double f,l;
+        Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( _sWOL ), loc, f,l);
+        pPrev = curve->Value( par.X() ).Transformed( loc );
+      }
+      else
+      {
+        Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face(_sWOL), loc );
+        pPrev = surface->Value( par.X(), par.Y() ).Transformed( loc );
+      }
+      dir = segDir.Location().XYZ() - pPrev.XYZ();
+    }
+    segDir.SetDirection( dir );
+  }
+  return segDir;
+}
+
+//================================================================================
+/*!
+ * \brief Perform smooth in 2D space of a face
+ *  \retval bool - true if node has been moved
+ */
+//================================================================================
+
+bool _LayerEdge::Smooth2d(Handle(Geom_Surface)& surface, SMESH_MesherHelper& helper)
+{
+  // smooth _LayerEdge based on EDGE and inflated along FACE
+
+  const TopoDS_Face& face = TopoDS::Face( _sWOL );
+
+  SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _nodes.back() );
+  gp_XYZ& uv0 = _pos.back();
+
+  gp_XY uv1 = helper.GetNodeUV( face, _simplices[0]._nPrev, tgtNode );
+  gp_XY uv2 = helper.GetNodeUV( face, _simplices[0]._nNext, tgtNode );
+  gp_XY uvNew = 0.5 * (uv1 + uv2);
+
+  double dist12 = (uv1 - uv2).Modulus();
+  double distNewOld = (uvNew - gp_XY( uv0.X(), uv0.Y() )).Modulus();
+
+  SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( tgtNode->GetPosition().get() );
+  pos->SetUParameter( uvNew.X() );
+  pos->SetVParameter( uvNew.Y() );
+  uv0.SetCoord( uvNew.X(), uvNew.Y(), 0 );
+
+  bool moved = distNewOld > dist12/50;
+  if ( moved )
+  {
+    gp_Pnt p = surface->Value( uvNew.X(), uvNew.Y() );
+    tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
+    dumpMove( tgtNode ); // debug
+  }
+  return moved;
+}
+
+//================================================================================
+/*!
+ * \brief Perform laplacian smooth of nodes inflated from FACE
+ *  \retval bool - true if _tgtNode has been moved
+ */
+//================================================================================
+
+bool _LayerEdge::Smooth(int& badNb)
+{
+  if ( _simplices.size() < 2 )
+    return false; // _LayerEdge inflated along EDGE or FACE
+
+  // compute new position for the last _pos
+  gp_XYZ newPos (0,0,0);
+  for ( unsigned i = 0; i < _simplices.size(); ++i )
+    newPos += SMESH_MeshEditor::TNodeXYZ( _simplices[i]._nPrev );
+  newPos /= _simplices.size();
+
+  gp_Pnt prevPos( _pos[ _pos.size()-2 ]);
+  if ( _cosin < -0.1)
+  {
+    // Avoid decreasing length of edge on concave surface
+    //gp_Vec oldMove( _pos[ _pos.size()-2 ], _pos.back() );
+    gp_Vec newMove( prevPos, newPos );
+    newPos = _pos.back() + newMove.XYZ();
+  }
+  else if ( _cosin > 0.3 )
+  {
+    // Avoid increasing length of edge too much
+
+  }
+  // count quality metrics (orientation) of tetras around _tgtNode
+  int nbOkBefore = 0;
+  SMESH_MeshEditor::TNodeXYZ tgtXYZ( _nodes.back() );
+  for ( unsigned i = 0; i < _simplices.size(); ++i )
+    nbOkBefore += _simplices[i].IsForward( _nodes[0], &tgtXYZ );
+
+  int nbOkAfter = 0;
+  for ( unsigned i = 0; i < _simplices.size(); ++i )
+    nbOkAfter += _simplices[i].IsForward( _nodes[0], &newPos );
+
+  if ( nbOkAfter < nbOkBefore )
+    return false;
+
+  SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
+
+  _len -= prevPos.Distance(SMESH_MeshEditor::TNodeXYZ( n ));
+  _len += prevPos.Distance(newPos);
+
+  n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
+  _pos.back() = newPos;
+
+  badNb += _simplices.size() - nbOkAfter;
+
+  dumpMove( n );
+
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief Test intersection of the last segment with a given triangle
+ *   using Moller-Trumbore algorithm
+ */
+//================================================================================
+
+bool _LayerEdge::SegTriaInter( const gp_Ax1&        lastSegment,
+                               const SMDS_MeshNode* n0,
+                               const SMDS_MeshNode* n1,
+                               const SMDS_MeshNode* n2 ) const
+{
+  const double EPSILON = 1e-6;
+
+  gp_XYZ orig = lastSegment.Location().XYZ();
+  gp_XYZ dir  = lastSegment.Direction().XYZ();
+
+  SMESH_MeshEditor::TNodeXYZ vert0( n0 );
+  SMESH_MeshEditor::TNodeXYZ vert1( n1 );
+  SMESH_MeshEditor::TNodeXYZ vert2( n2 );
+
+  gp_XYZ edge1 = vert1 - vert0;
+  gp_XYZ edge2 = vert2 - vert0;
+
+  /* begin calculating determinant - also used to calculate U parameter */
+  gp_XYZ pvec = dir ^ edge2;
+
+  /* if determinant is near zero, ray lies in plane of triangle */
+  double det = edge1 * pvec;
+
+  if (det < EPSILON)
+    return false;
+
+  /* calculate distance from vert0 to ray origin */
+  gp_XYZ tvec = orig - vert0;
+
+  /* calculate U parameter and test bounds */
+  double u = tvec * pvec;
+  if (u < 0.0 || u > det)
+    return 0;
+
+  /* prepare to test V parameter */
+  gp_XYZ qvec = tvec ^ edge1;
+
+  /* calculate V parameter and test bounds */
+  double v = dir * qvec;
+  if (v < 0.0 || u + v > det)
+    return 0;
+
+  /* calculate t, scale parameters, ray intersects triangle */
+  double t = edge2 * qvec;
+  double inv_det = 1.0 / det;
+  t *= inv_det;
+  //u *= inv_det;
+  //v *= inv_det;
+
+  bool intersection = t < _len;
+  if ( intersection )
+  {
+    gp_XYZ intP( orig + dir * t );
+    cout << "src node " << _nodes.back()->GetID() << ", intersection with face "
+         << n0->GetID() << " " << n1->GetID() << " " << n2->GetID() << endl
+         << " at point " << intP.X() << ", " << intP.Y() << ", " << intP.Z() << endl;
+  }
+  return intersection;
+}
+
+//================================================================================
+/*!
+ * \brief Add a new segment to _LayerEdge
+ */
+//================================================================================
+
+void _LayerEdge::SetNewLength( double len, SMESH_MesherHelper& helper )
+{
+  if ( _len - len > -1e-6 )
+  {
+    _pos.push_back( _pos.back() );
+    return;
+  }
+  SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() );
+  SMESH_MeshEditor::TNodeXYZ oldXYZ( n );
+  gp_XYZ nXYZ = oldXYZ + _normal * ( len - _len );
+  n->setXYZ( nXYZ.X(), nXYZ.Y(), nXYZ.Z() );
+
+  _pos.push_back( nXYZ );
+  if ( _sWOL.IsNull() )
+  {
+    _len = len;
+  }
+  else
+  {
+    double distXYZ[4];
+    if ( _sWOL.ShapeType() == TopAbs_EDGE )
+    {
+      double u = Precision::Infinite(); // to force projection w/o distance check
+      helper.CheckNodeU( TopoDS::Edge( _sWOL ), n, u, 1e-10, /*force=*/true, distXYZ );
+      _pos.back().SetCoord( u, 0, 0 );
+      SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( n->GetPosition().get() );
+      pos->SetUParameter( u );
+    }
+    else //  TopAbs_FACE
+    {
+      gp_XY uv( Precision::Infinite(), 0 );
+      helper.CheckNodeUV( TopoDS::Face( _sWOL ), n, uv, 1e-10, /*force=*/true, distXYZ );
+      _pos.back().SetCoord( uv.X(), uv.Y(), 0 );
+      SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( n->GetPosition().get() );
+      pos->SetUParameter( uv.X() );
+      pos->SetVParameter( uv.Y() );
+    }
+    n->setXYZ( distXYZ[1], distXYZ[2], distXYZ[3]);
+    _len += oldXYZ.Distance( n );
+  }
+  dumpMove( n ); //debug
+}
+
+//================================================================================
+/*!
+ * \brief Remove last inflation step
+ */
+//================================================================================
+
+void _LayerEdge::InvalidateStep( int curStep )
+{
+  if ( _pos.size() > curStep+1 )
+  {
+    _pos.resize( curStep+1 );
+    gp_Pnt nXYZ = _pos.back();
+    SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() );
+    if ( !_sWOL.IsNull() )
+    {
+      TopLoc_Location loc;
+      if ( _sWOL.ShapeType() == TopAbs_EDGE )
+      {
+        double f,l;
+        Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( _sWOL ), loc, f,l);
+        nXYZ = curve->Value( nXYZ.X() ).Transformed( loc );
+      }
+      else
+      {
+        Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face(_sWOL), loc );
+        nXYZ = surface->Value( nXYZ.X(), nXYZ.Y() ).Transformed( loc );
+      }
+    }
+    n->setXYZ( nXYZ.X(), nXYZ.Y(), nXYZ.Z() );
+  }
+}
+
+//================================================================================
+/*!
+ * \brief Create layers of prisms
+ */
+//================================================================================
+
+bool _ViscousBuilder::refine(_SolidData& data)
+{
+  SMESH_MesherHelper helper( *_mesh );
+  helper.SetSubShape( data._solid );
+
+  Handle(Geom_Curve) curve;
+  Handle(Geom_Surface) surface;
+  TopoDS_Edge geomEdge;
+  TopoDS_Face geomFace;
+  TopLoc_Location loc;
+  double f,l, u/*, distXYZ[4]*/;
+  gp_XY uv;
+  bool isOnEdge;
+
+  for ( unsigned i = 0; i < data._edges.size(); ++i )
+  {
+    _LayerEdge& edge = *data._edges[i];
+
+    helper.SetElementsOnShape(edge._sWOL.IsNull());
+
+    // get accumulated length of segments
+    vector< double > segLen( edge._pos.size() );
+    segLen[0] = 0.0;
+    for ( unsigned j = 1; j < edge._pos.size(); ++j )
+      segLen[j] = segLen[j-1] + (edge._pos[j-1] - edge._pos[j] ).Modulus();
+
+    // allocate memory for new nodes if it is not yet refined
+    const SMDS_MeshNode* tgtNode = edge._nodes.back();
+    if ( edge._nodes.size() == 2 )
+    {
+      edge._nodes.resize( data._hyp->GetNumberLayers() + 1, 0 );
+      edge._nodes[1] = 0;
+      edge._nodes.back() = tgtNode;
+    }
+    if ( !edge._sWOL.IsNull() )
+    {
+      isOnEdge = ( edge._sWOL.ShapeType() == TopAbs_EDGE );
+      // restore position of the last node
+//       gp_Pnt p;
+      if ( isOnEdge )
+      {
+        geomEdge = TopoDS::Edge( edge._sWOL );
+        curve = BRep_Tool::Curve( geomEdge, loc, f,l);
+//         double u = helper.GetNodeU( tgtNode );
+//         p = curve->Value( u );
+      }
+      else
+      {
+        geomFace = TopoDS::Face( edge._sWOL );
+        surface = BRep_Tool::Surface( geomFace, loc );
+//         gp_XY uv = helper.GetNodeUV( tgtNode );
+//         p = surface->Value( uv.X(), uv.Y() );
+      }
+//       p.Transform( loc );
+//       const_cast< SMDS_MeshNode* >( tgtNode )->setXYZ( p.X(), p.Y(), p.Z() );
+    }
+    // calculate height of the first layer
+    const double T = segLen.back(); //data._hyp.GetTotalThickness();
+    const double f = data._hyp->GetStretchFactor();
+    const int    N = data._hyp->GetNumberLayers();
+    double h0 = T * ( f - 1 )/( pow( f, N ) - 1 );
+
+    const double zeroLen = std::numeric_limits<double>::min();
+
+    // create intermediate nodes
+    double hSum = 0, hi = h0/f;
+    unsigned iSeg = 1;
+    for ( unsigned iStep = 1; iStep < edge._nodes.size(); ++iStep )
+    {
+      hi *= f;
+      hSum += hi;
+      while ( hSum > segLen[iSeg] && iSeg < segLen.size()-1)
+        ++iSeg;
+      int iPrevSeg = iSeg-1;
+      while ( fabs( segLen[iPrevSeg] - segLen[iSeg]) <= zeroLen && iPrevSeg > 0 )
+        --iPrevSeg;
+      double r = ( segLen[iSeg] - hSum ) / ( segLen[iSeg] - segLen[iPrevSeg] );
+      gp_Pnt pos = r * edge._pos[iPrevSeg] + (1-r) * edge._pos[iSeg];
+      SMDS_MeshNode*& node = const_cast< SMDS_MeshNode*& >(edge._nodes[ iStep ]);
+      if ( !edge._sWOL.IsNull() )
+      {
+        // compute XYZ by parameters <pos>
+        if ( isOnEdge )
+        {
+          u = pos.X();
+          pos = curve->Value( u ).Transformed(loc);
+        }
+        else
+        {
+          uv.SetCoord( pos.X(), pos.Y() );
+          pos = surface->Value( pos.X(), pos.Y() ).Transformed(loc);
+        }
+      }
+      if ( !node )
+      {
+        node = helper.AddNode( pos.X(), pos.Y(), pos.Z());
+        if ( !edge._sWOL.IsNull() )
+        {
+          if ( isOnEdge )
+            getMeshDS()->SetNodeOnEdge( node, geomEdge, u );
+          else
+            getMeshDS()->SetNodeOnFace( node, geomFace, uv.X(), uv.Y() );
+        }
+      }
+      else
+      {
+        if ( !edge._sWOL.IsNull() )
+        {
+          // make average pos from new and current parameters
+          if ( isOnEdge )
+          {
+            u = 0.5 * ( u + helper.GetNodeU( geomEdge, node ));
+            pos = curve->Value( u );
+          }
+          else
+          {
+            uv = 0.5 * ( uv + helper.GetNodeUV( geomFace, node ));
+            pos = surface->Value( uv.X(), uv.Y());
+          }
+        }
+        node->setXYZ( pos.X(), pos.Y(), pos.Z() );
+      }
+    }
+  }
+
+  // TODO: make quadratic prisms and polyhedrons(?)
+
+  TopExp_Explorer exp( data._solid, TopAbs_FACE );
+  for ( ; exp.More(); exp.Next() )
+  {
+    if ( _ignoreShapeIds.count( getMeshDS()->ShapeToIndex( exp.Current() )))
+      continue;
+    SMESHDS_SubMesh* fSubM = getMeshDS()->MeshElements( exp.Current() );
+    SMDS_ElemIteratorPtr fIt = fSubM->GetElements();
+    vector< vector<const SMDS_MeshNode*>* > nnVec;
+    while ( fIt->more() )
+    {
+      const SMDS_MeshElement* face = fIt->next();
+      int nbNodes = face->NbCornerNodes();
+      nnVec.resize( nbNodes );
+      SMDS_ElemIteratorPtr nIt = face->nodesIterator();
+      for ( int iN = 0; iN < nbNodes; ++iN )
+      {
+        const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
+        nnVec[ iN ] = & data._n2eMap[ n ]->_nodes;
+      }
+
+      int nbZ = nnVec[0]->size();
+      switch ( nbNodes )
+      {
+      case 3:
+        for ( int iZ = 1; iZ < nbZ; ++iZ )
+          helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1], (*nnVec[2])[iZ-1],
+                            (*nnVec[0])[iZ],   (*nnVec[1])[iZ],   (*nnVec[2])[iZ]);
+        break;
+      case 4:
+        for ( int iZ = 1; iZ < nbZ; ++iZ )
+          helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1],
+                            (*nnVec[2])[iZ-1], (*nnVec[3])[iZ-1],
+                            (*nnVec[0])[iZ],   (*nnVec[1])[iZ],
+                            (*nnVec[2])[iZ],   (*nnVec[3])[iZ]);
+        break;
+      default:
+        return error("Not supported type of element", &data);
+      }
+    }
+  }
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief Shrink 2D mesh on faces to let space for inflated layers
+ */
+//================================================================================
+
+bool _ViscousBuilder::shrink()
+{
+  // make map of (ids of faces to shrink mesh on) to (_SolidData containing _LayerEdge's
+  // inflated along face or edge)
+  map< int, _SolidData* > f2sdMap;
+  for ( unsigned i = 0 ; i < _sdVec.size(); ++i )
+  {
+    _SolidData& data = _sdVec[i];
+    map< int, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
+    for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
+      if ( s2s->second.ShapeType() == TopAbs_FACE )
+        f2sdMap.insert( make_pair( getMeshDS()->ShapeToIndex( s2s->second ), &data ));
+  }
+
+  SMESH_MesherHelper helper( *_mesh );
+
+  // loop on faces to srink mesh on
+  map< int, _SolidData* >::iterator f2sd = f2sdMap.begin();
+  for ( ; f2sd != f2sdMap.end(); ++f2sd )
+  {
+    _SolidData&     data = *f2sd->second;
+    TNode2Edge&   n2eMap = data._n2eMap;
+    const TopoDS_Face& F = TopoDS::Face( getMeshDS()->IndexToShape( f2sd->first ));
+    const bool   reverse = ( data._reversedFaceIds.count( f2sd->first ));
+
+    SMESH_subMesh* sm = _mesh->GetSubMesh( F );
+    SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
+
+    helper.SetSubShape(F);
+
+    // Create _SmoothNode's on face F
+    const set<int> ignoreShapes;
+    vector< _SmoothNode > nodesToSmooth;
+    nodesToSmooth.reserve( smDS->NbElements() * 3 );
+    {
+      int i = 0;
+      SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
+      while ( nIt->more() )
+      {
+        const SMDS_MeshNode* n = nIt->next();
+        nodesToSmooth.resize( i+1 );
+        nodesToSmooth[ i ]._node = n;
+        getSimplices( n, nodesToSmooth[ i ]._simplices, ignoreShapes );
+        if ( nodesToSmooth[ i ]._simplices.empty() )
+          ;// n is created during refinement
+        else
+          ++i;
+      }
+      nodesToSmooth.resize( i );
+    }
+    if ( nodesToSmooth.empty() ) continue;
+
+    // Find out face orientation
+    double refSign = 1;
+    gp_XY uv = helper.GetNodeUV( F, nodesToSmooth[0]._node );
+    if ( nodesToSmooth[0]._simplices[0].IsForward(uv, F, helper,refSign) != (!reverse))
+      refSign = -1;
+
+    // Find _LayerEdge's inflated along F
+    vector< _LayerEdge* > lEdges;
+    {
+      SMESH_subMeshIteratorPtr subIt =
+        sm->getDependsOnIterator(/*includeSelf=*/false, /*complexShapeFirst=*/false);
+      while ( subIt->more() )
+      {
+        SMESH_subMesh* sub = subIt->next();
+        SMESHDS_SubMesh* subDS = sub->GetSubMeshDS();
+        if ( subDS->NbNodes() == 0 || !n2eMap.count( subDS->GetNodes()->next() ))
+          continue;
+        SMDS_NodeIteratorPtr nIt = subDS->GetNodes();
+        while ( nIt->more() )
+        {
+          _LayerEdge* edge = n2eMap[ nIt->next() ];
+          lEdges.push_back( edge );
+          prepareEdgeToShrink( *edge, F, helper, smDS );
+        }
+      }
+    }
+
+    // Perform shrinking
+
+    TopLoc_Location loc;
+    Handle(Geom_Surface) surface = BRep_Tool::Surface(F,loc);
+    bool shrinked = true;
+    while ( shrinked )
+    {
+      shrinked = false;
+      for ( unsigned i = 0; i < lEdges.size(); ++i )
+      {
+        _LayerEdge* edge = lEdges[i];
+        if ( !edge->_pos.empty() )
+        {
+          SMDS_MeshNode*& node = const_cast< SMDS_MeshNode*& >(edge->_nodes.back());
+          //gp_Pnt p = surface->Value( edge->_pos.back().X(), edge->_pos.back().Y() );
+          //p.Transform( loc );
+          //node->setXYZ( p.X(), p.Y(), p.Z() );
+          SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( node->GetPosition().get() );
+          pos->SetUParameter( edge->_pos.back().X() );
+          pos->SetVParameter( edge->_pos.back().Y() );
+          edge->_pos.pop_back();
+          shrinked = true;
+        }
+      }
+      if ( !shrinked )
+        break;
+
+      // smoothging
+      int nbNoImpSteps = 0, badNb = 1, step=0;
+      bool moved = true;
+      while ( nbNoImpSteps <= 5 && moved && badNb > 0)
+      {
+        dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_step"<<++step); // debug
+
+        int oldBadNb = badNb;
+        badNb = 0;
+        moved = false;
+        for ( unsigned i = 0; i < nodesToSmooth.size(); ++i )
+          moved = nodesToSmooth[i].Smooth( badNb,surface,loc,helper,refSign ) || moved;
+        if ( badNb < oldBadNb )
+          nbNoImpSteps = 0;
+        else
+          nbNoImpSteps++;
+
+        dumpFunctionEnd();
+      }
+      if ( badNb > 0 )
+        return error(SMESH_Comment("Can't shrink 2D mesh on face ") << f2sd->first, 0 );
+    }
+  }
+
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief Compute positions (UV) to set to a node on edge moved during shrinking
+ */
+//================================================================================
+
+bool _ViscousBuilder::prepareEdgeToShrink( _LayerEdge&            edge,
+                                           const TopoDS_Face&     F,
+                                           SMESH_MesherHelper&    helper,
+                                           const SMESHDS_SubMesh* faceSubMesh)
+{
+  // Compute UV to follow during shrinking
+
+  const SMDS_MeshNode* srcNode = edge._nodes[0];
+  const SMDS_MeshNode* tgtNode = edge._nodes.back();
+
+  gp_XY srcUV = helper.GetNodeUV( F, srcNode );
+  gp_XY tgtUV = helper.GetNodeUV( F, tgtNode );
+  gp_Vec2d uvDir( srcUV, tgtUV );
+  double uvLen = uvDir.Magnitude();
+  uvDir /= uvLen;
+
+  const double minStepSize = uvLen / 20;
+  double stepSize = uvLen;
+  SMDS_ElemIteratorPtr fIt = srcNode->GetInverseElementIterator(SMDSAbs_Face);
+  while ( fIt->more() )
+  {
+    const SMDS_MeshElement* f = fIt->next();
+    const int nbNodes = f->NbCornerNodes();
+    for ( int i = 0; i < nbNodes; ++i )
+    {
+      const SMDS_MeshNode* n = f->GetNode(i);
+      if ( n->GetPosition()->GetTypeOfPosition() != SMDS_TOP_FACE )
+        continue;
+      gp_XY uv = helper.GetNodeUV( F, n );
+      gp_Vec2d uvDirN( srcUV, uv );
+      double proj = uvDirN * uvDir;
+      if ( proj < stepSize && proj > minStepSize )
+        stepSize = proj;
+    }
+  }
+  stepSize *= 0.8;
+
+  const int nbSteps = uvLen / stepSize;
+  gp_XYZ srcUV0( srcUV.X(), srcUV.Y(), 0 );
+  gp_XYZ tgtUV0( tgtUV.X(), tgtUV.Y(), 0 );
+  edge._pos.resize( nbSteps );
+  edge._pos[0] = tgtUV0;
+  for ( int i = 1; i < nbSteps-1; ++i )
+  {
+    double r = i / nbSteps;
+    edge._pos[i] = (1-r) * tgtUV0 + r * srcUV0;
+  }
+
+  // Replace srcNode by tgtNode in shrinked faces
+
+  const SMDS_MeshNode* nodes[20];
+  fIt = srcNode->GetInverseElementIterator(SMDSAbs_Face);
+
+  while ( fIt->more() )
+  {
+    const SMDS_MeshElement* f = fIt->next();
+    if ( !faceSubMesh->Contains( f ))
+      continue;
+    SMDS_ElemIteratorPtr nIt = f->nodesIterator();
+    for ( int iN = 0; iN < f->NbNodes(); ++iN )
+    {
+      const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
+      nodes[iN] = ( n == srcNode ? tgtNode : n );
+    }
+    helper.GetMeshDS()->ChangeElementNodes( f, nodes, f->NbNodes() );
+  }
+
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief Perform laplacian smooth
+ *  \retval bool - true if the node has been moved
+ */
+//================================================================================
+
+bool _SmoothNode::Smooth(int&                  badNb,
+                         Handle(Geom_Surface)& surface,
+                         TopLoc_Location&      loc,
+                         SMESH_MesherHelper&   helper,
+                         const double          refSign)
+{
+  const TopoDS_Face& face = TopoDS::Face( helper.GetSubShape() );
+
+  // compute new UV for the node
+  gp_XY newPos (0,0);
+  for ( unsigned i = 0; i < _simplices.size(); ++i )
+    newPos += helper.GetNodeUV( face, _simplices[i]._nPrev );
+  newPos /= _simplices.size();
+
+  // count quality metrics (orientation) of triangles around the node
+  int nbOkBefore = 0;
+  gp_XY tgtUV = helper.GetNodeUV( face, _node );
+  for ( unsigned i = 0; i < _simplices.size(); ++i )
+    nbOkBefore += _simplices[i].IsForward( tgtUV, face, helper, refSign );
+
+  int nbOkAfter = 0;
+  for ( unsigned i = 0; i < _simplices.size(); ++i )
+    nbOkAfter += _simplices[i].IsForward( newPos, face, helper, refSign );
+
+  if ( nbOkAfter < nbOkBefore )
+    return false;
+
+  gp_Pnt p = surface->Value( newPos.X(), newPos.Y() );
+  p.Transform( loc );
+  const_cast< SMDS_MeshNode* >( _node )->setXYZ( p.X(), p.Y(), p.Z() );
+
+  dumpMove( _node );
+
+  badNb += _simplices.size() - nbOkAfter;
+  return ( (tgtUV-newPos).SquareModulus() > 1e-10 );
+}
+
+//================================================================================
+/*!
+ * \brief Delete _SolidData
+ */
+//================================================================================
+
+_SolidData::~_SolidData()
+{
+  for ( unsigned i = 0; i < _edges.size(); ++i )
+    delete _edges[i];
+  _edges.clear();
+}
diff --git a/src/StdMeshers/StdMeshers_ViscousLayers.hxx b/src/StdMeshers/StdMeshers_ViscousLayers.hxx
new file mode 100644 (file)
index 0000000..ebfa256
--- /dev/null
@@ -0,0 +1,93 @@
+//  Copyright (C) 2007-2011  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
+//  License as published by the Free Software Foundation; either
+//  version 2.1 of the License.
+//
+//  This library is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  Lesser General Public License for more details.
+//
+//  You should have received a copy of the GNU Lesser General Public
+//  License along with this library; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+//  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+// File      : StdMeshers_ViscousLayers.hxx
+// Created   : Wed Dec  1 15:15:34 2010
+// Author    : Edward AGAPOV (eap)
+
+#ifndef __StdMeshers_ViscousLayers_HXX__
+#define __StdMeshers_ViscousLayers_HXX__
+
+#include "SMESH_StdMeshers.hxx"
+
+#include "SMESH_Hypothesis.hxx"
+#include "StdMeshers_ProxyMesh.hxx"
+
+#include <vector>
+
+/*!
+ * \brief Hypothesis defining parameters of viscous layers
+ */
+class STDMESHERS_EXPORT StdMeshers_ViscousLayers : public SMESH_Hypothesis
+{
+public:
+  StdMeshers_ViscousLayers(int hypId, int studyId, SMESH_Gen* gen);
+
+  // Set faces to exclude from treatment
+  void SetIgnoreFaces(const std::vector<int>& faceIds);
+  std::vector<int> GetIgnoreFaces() const { return _ignoreFaceIds; }
+
+  // Set total thickness of layers of prisms
+  void SetTotalThickness(double thickness);
+  double GetTotalThickness() const { return _thickness; }
+
+  // Set number of layers of prisms
+  void SetNumberLayers(int nb);
+  int GetNumberLayers() const { return _nbLayers; }
+
+  // Set factor (>1.0) of growth of layer thickness towards inside of mesh
+  void SetStretchFactor(double factor);
+  double GetStretchFactor() const { return _stretchFactor; }
+
+  // Computes temporary 2D mesh to be used by 3D algorithm.
+  // Return StdMeshers_ProxyMesh for each SOLID in theShape
+  StdMeshers_ProxyMesh::Ptr Compute(SMESH_Mesh&         theMesh,
+                                    const TopoDS_Shape& theShape) const;
+
+  virtual std::ostream & SaveTo(std::ostream & save);
+  virtual std::istream & LoadFrom(std::istream & load);
+
+  /*!
+   * \brief Initialize my parameter values by the mesh built on the geometry
+    * \param theMesh - the built mesh
+    * \param theShape - the geometry of interest
+    * \retval bool - true if parameter values have been successfully defined
+    *
+    * Just return false as this hypothesis does not have parameters values
+   */
+  virtual bool SetParametersByMesh(const SMESH_Mesh* theMesh, const TopoDS_Shape& theShape);
+
+  /*!
+   * \brief Initialize my parameter values by default parameters.
+   *  \retval bool - true if parameter values have been successfully defined
+   */
+  virtual bool SetParametersByDefaults(const TDefaults& dflts, const SMESH_Mesh* theMesh=0)
+  { return false; }
+
+  static const char* GetHypType() { return "ViscousLayers"; }
+
+ private:
+
+  std::vector<int> _ignoreFaceIds;
+  int              _nbLayers;
+  double           _thickness;
+  double           _stretchFactor;
+};
+
+#endif
index 9595e67a0084357e32d576c2984d5bbd1caed484..1cd4f9f94d5ee0f045ca15d309feafa08d066828 100644 (file)
@@ -666,6 +666,25 @@ QString StdMeshersGUI_StdHypothesisCreator::storeParams() const
       QCheckBox* toCopyGroups = widget< QCheckBox >( 2 );
       h->SetCopySourceMesh( toCopyMesh->isChecked(), toCopyGroups->isChecked());
     }
+    else if( hypType()=="ViscousLayers" )
+    {
+      StdMeshers::StdMeshers_ViscousLayers_var h =
+        StdMeshers::StdMeshers_ViscousLayers::_narrow( hypothesis() );
+
+      h->SetTotalThickness( params[0].myValue.toDouble() );
+      /*  */ h->SetParameters(aVariablesList.join(":").toLatin1().constData());
+      h->SetNumberLayers  ( params[1].myValue.toInt() );
+      /*  */ h->SetParameters(aVariablesList.join(":").toLatin1().constData());
+      h->SetStretchFactor ( params[2].myValue.toInt() );
+      /*  */ h->SetParameters(aVariablesList.join(":").toLatin1().constData());
+      
+      if ( StdMeshersGUI_SubShapeSelectorWdg* idsWg = 
+           widget< StdMeshersGUI_SubShapeSelectorWdg >( 3 ))
+      {
+        h->SetIgnoreFaces( idsWg->GetListOfIDs() );
+        //h->SetObjectEntry( idsWg->GetMainShapeEntry() );
+      }
+    }
     else if( hypType()=="QuadrangleParams" )
     {
       StdMeshers::StdMeshers_QuadrangleParams_var h =
@@ -1099,6 +1118,45 @@ bool StdMeshersGUI_StdHypothesisCreator::stdParams( ListOfStdParams& p ) const
     aQCheckBox->setEnabled( toCopyMesh );
     customWidgets()->append( aQCheckBox );
   }
+  else if( hypType()=="ViscousLayers" )
+  {
+    StdMeshers::StdMeshers_ViscousLayers_var h =
+      StdMeshers::StdMeshers_ViscousLayers::_narrow( hyp );
+
+    item.myName = tr( "SMESH_TOTAL_THICKNESS" );
+    if(!initVariableName(aParameters,item,0))
+      item.myValue = h->GetTotalThickness();
+    p.append( item );
+    customWidgets()->append (0);
+
+    item.myName = tr( "SMESH_NUMBER_OF_LAYERS" );
+    if(!initVariableName(aParameters,item,1))
+      item.myValue = h->GetNumberLayers();
+    p.append( item );
+    customWidgets()->append (0);
+
+    item.myName = tr( "SMESH_STRETCH_FACTOR" );
+    if(!initVariableName(aParameters,item,2))
+      item.myValue = h->GetStretchFactor();
+    p.append( item );
+    customWidgets()->append (0);
+
+    QString aMainEntry = SMESHGUI_GenericHypothesisCreator::getMainShapeEntry();
+    if ( !aMainEntry.isEmpty() )
+    {
+      item.myName = tr( "SMESH_FACES_WO_LAYERS" );
+      p.append( item );
+
+      StdMeshersGUI_SubShapeSelectorWdg* idsWg =
+        new StdMeshersGUI_SubShapeSelectorWdg(0,TopAbs_FACE);
+
+      idsWg->SetGeomShapeEntry( aMainEntry );
+      idsWg->SetMainShapeEntry( aMainEntry );
+      idsWg->SetListOfIDs( h->GetIgnoreFaces() );
+      idsWg->showPreview( true );
+      customWidgets()->append ( idsWg );
+    }
+  }
   else if (hypType() == "QuadrangleParams")
   {
     StdMeshers::StdMeshers_QuadrangleParams_var h =
@@ -1108,9 +1166,8 @@ bool StdMeshersGUI_StdHypothesisCreator::stdParams( ListOfStdParams& p ) const
     p.append(item);
 
     StdMeshersGUI_SubShapeSelectorWdg* aDirectionWidget =
-      new StdMeshersGUI_SubShapeSelectorWdg();
+      new StdMeshersGUI_SubShapeSelectorWdg(0, TopAbs_VERTEX);
     aDirectionWidget->SetMaxSize(1);
-    aDirectionWidget->SetSubShType(TopAbs_VERTEX);
     QString anEntry = SMESHGUI_GenericHypothesisCreator::getShapeEntry();
     QString aMainEntry = SMESHGUI_GenericHypothesisCreator::getMainShapeEntry();
     if (anEntry == "")
@@ -1118,9 +1175,9 @@ bool StdMeshersGUI_StdHypothesisCreator::stdParams( ListOfStdParams& p ) const
     aDirectionWidget->SetGeomShapeEntry(anEntry);
     aDirectionWidget->SetMainShapeEntry(aMainEntry);
     if (!isCreation()) {
-      SMESH::long_array_var aVec = new SMESH::long_array;
       int vertID = h->GetTriaVertex();
       if (vertID > 0) {
+        SMESH::long_array_var aVec = new SMESH::long_array;
         aVec->length(1);
         aVec[0] = vertID;
         aDirectionWidget->SetListOfIDs(aVec);
@@ -1148,50 +1205,66 @@ bool StdMeshersGUI_StdHypothesisCreator::stdParams( ListOfStdParams& p ) const
 //================================================================================
 /*!
  * \brief tune "standard" control
 * \param w - control widget
 * \param int - parameter index
+ * \param w - control widget
+ * \param int - parameter index
  */
 //================================================================================
 
 void StdMeshersGUI_StdHypothesisCreator::attuneStdWidget (QWidget* w, const int) const
 {
   SMESHGUI_SpinBox* sb = w->inherits( "SMESHGUI_SpinBox" ) ? ( SMESHGUI_SpinBox* )w : 0;
-  if( hypType()=="LocalLength" &&  sb )
+  if ( sb )
   {
-    if (sb->objectName() == tr("SMESH_LOCAL_LENGTH_PARAM"))
+    if( hypType()=="LocalLength" )
+    {
+      if (sb->objectName() == tr("SMESH_LOCAL_LENGTH_PARAM"))
+        sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, "length_precision" );
+      else if (sb->objectName() == tr("SMESH_LOCAL_LENGTH_PRECISION"))
+        sb->RangeStepAndValidator( 0.0, 1.0, 0.05, "len_tol_precision" );
+    }
+    else if( hypType()=="Arithmetic1D" )
+    {
+      sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, "parametric_precision" );
+    }
+    else if( hypType()=="MaxLength" )
+    {
       sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, "length_precision" );
-    else if (sb->objectName() == tr("SMESH_LOCAL_LENGTH_PRECISION"))
-      sb->RangeStepAndValidator( 0.0, 1.0, 0.05, "len_tol_precision" );
-  }
-  else if( hypType()=="Arithmetic1D" && sb )
-  {
-    sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, "parametric_precision" );
-  }
-  else if( hypType()=="MaxLength" && sb )
-  {
-    sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, "length_precision" );
-    sb->setEnabled( !widget< QCheckBox >( 1 )->isChecked() );
-  }
-  else if( hypType()=="MaxElementArea" && sb )
-  {
-    sb->RangeStepAndValidator( VALUE_SMALL_2, VALUE_MAX_2, 1.0, "area_precision" );
-  }
-  else if( hypType()=="MaxElementVolume" && sb )
-  {
-    sb->RangeStepAndValidator( VALUE_SMALL_3, VALUE_MAX_3, 1.0, "vol_precision" );
-  }
-  else if( hypType()=="StartEndLength" && sb )
-  {
-    sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, "length_precision" );
-  }
-  else if( hypType()=="Deflection1D" && sb )
-  {
-    sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, "parametric_precision" );
-  }
-  else if ( sb ) // default validator for possible ancestors
-  {
-    sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, "length_precision" );
+      sb->setEnabled( !widget< QCheckBox >( 1 )->isChecked() );
+    }
+    else if( hypType()=="MaxElementArea" )
+    {
+      sb->RangeStepAndValidator( VALUE_SMALL_2, VALUE_MAX_2, 1.0, "area_precision" );
+    }
+    else if( hypType()=="MaxElementVolume" )
+    {
+      sb->RangeStepAndValidator( VALUE_SMALL_3, VALUE_MAX_3, 1.0, "vol_precision" );
+    }
+    else if( hypType()=="StartEndLength" )
+    {
+      sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, "length_precision" );
+    }
+    else if( hypType()=="Deflection1D" )
+    {
+      sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, "parametric_precision" );
+    }
+    else if( hypType()=="ViscousLayers" )
+    {
+      if (sb->objectName() == tr("SMESH_STRETCH_FACTOR"))
+        sb->RangeStepAndValidator( 1.0, VALUE_MAX, 0.1, "parametric_precision" );
+      else
+        sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, "length_precision" );
+    }
+    else // default validator for possible ancestors
+    {
+      sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, "length_precision" );
+    }
   }
+//   else if ( QtxIntSpinBox* ib = w->inherits( "QtxIntSpinBox" ) ? ( QtxIntSpinBox* )w : 0)
+//   {
+//     if( hypType()=="ViscousLayers" )
+//     {
+//     }
+//   }
 }
 
 //================================================================================
@@ -1266,6 +1339,7 @@ QString StdMeshersGUI_StdHypothesisCreator::hypTypeName( const QString& t ) cons
     types.insert( "SegmentLengthAroundVertex", "SEGMENT_LENGTH_AROUND_VERTEX" );
     types.insert( "MaxLength", "MAX_LENGTH" );
     types.insert( "QuadrangleParams", "QUADRANGLE_PARAMS" );
+    types.insert( "ViscousLayers", "VISCOUS_LAYERS" );
   }
 
   QString res;
index 87444b56a3b3c47e78b41eecf108d8862c08dff8..172b748543d4d7490377dd84501591a1653f07f5 100644 (file)
             <source>ICON_DLG_MAX_LENGTH</source>
             <translation>mesh_hypo_length.png</translation>
         </message>
+        <message>
+            <source>ICON_DLG_VISCOUS_LAYERS</source>
+            <translation>mesh_hypo_viscous_layers.png</translation>
+        </message>
         <message>
             <source>ICON_DLG_MAX_ELEMENT_AREA</source>
             <translation>mesh_hypo_area.png</translation>
index 75888dfa36a0efa6ee3021ecad9a1b3881b24900..2235e628eb0eec13f05ffb9b18e2aa0e4991e78a 100644 (file)
         <source>SMESH_MAX_LENGTH_HYPOTHESIS</source>
         <translation>Max Length</translation>
     </message>
+    <message>
+        <source>SMESH_VISCOUS_LAYERS_HYPOTHESIS</source>
+        <translation>Viscous Layers</translation>
+    </message>
     <message>
         <source>SMESH_USE_PREESTIMATED_LENGTH</source>
         <translation>Use preestimated length</translation>
         <source>SMESH_MAX_LENGTH_TITLE</source>
         <translation>Hypothesis Construction</translation>
     </message>
+    <message>
+        <source>SMESH_VISCOUS_LAYERS_TITLE</source>
+        <translation>Hypothesis Construction</translation>
+    </message>
     <message>
         <source>SMESH_MAX_ELEMENT_AREA_HYPOTHESIS</source>
         <translation>Max. Element Area</translation>
         <source>SMESH_NO_CONV</source>
         <translation>No conversion</translation>
     </message>
+    <message>
+        <source>SMESH_TOTAL_THICKNESS</source>
+        <translation>Total thickness</translation>
+    </message>
+    <message>
+        <source>SMESH_STRETCH_FACTOR</source>
+        <translation>Stretch factor</translation>
+    </message>
+    <message>
+        <source>SMESH_FACES_WO_LAYERS</source>
+        <translation>Faces without layers</translation>
+    </message>
     <message>
         <source>SMESH_NUMBER_OF_LAYERS</source>
         <translation>Number of Layers</translation>
index 17d7c9f6fafbd543c9473629162f8cb6e81b1e2d..a44406b6b69578a4443c5393f7c0668b3c9ca571 100644 (file)
@@ -67,7 +67,8 @@ salomeinclude_HEADERS = \
        StdMeshers_ImportSource1D_i.hxx \
        StdMeshers_ImportSource2D_i.hxx \
        StdMeshers_Import_1D_i.hxx \
-       StdMeshers_Import_1D2D_i.hxx
+       StdMeshers_Import_1D2D_i.hxx \
+       StdMeshers_ViscousLayers_i.hxx
 
 # Libraries targets
 lib_LTLIBRARIES = libStdMeshersEngine.la
@@ -113,7 +114,8 @@ dist_libStdMeshersEngine_la_SOURCES = \
        StdMeshers_ImportSource1D_i.cxx \
        StdMeshers_ImportSource2D_i.cxx \
        StdMeshers_Import_1D_i.cxx \
-       StdMeshers_Import_1D2D_i.cxx
+       StdMeshers_Import_1D2D_i.cxx \
+       StdMeshers_ViscousLayers_i.cxx
 
 # additionnal information to compil and link file
 libStdMeshersEngine_la_CPPFLAGS = \
diff --git a/src/StdMeshers_I/StdMeshers_ViscousLayers_i.cxx b/src/StdMeshers_I/StdMeshers_ViscousLayers_i.cxx
new file mode 100644 (file)
index 0000000..b461843
--- /dev/null
@@ -0,0 +1,215 @@
+//  Copyright (C) 2007-2010  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
+//
+//  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.
+//
+//  This library is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  Lesser General Public License for more details.
+//
+//  You should have received a copy of the GNU Lesser General Public
+//  License along with this library; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+//  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+//  SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses
+//  File   : StdMeshers_ViscousLayers_i.cxx
+//  Module : SMESH
+//
+#include "StdMeshers_ViscousLayers_i.hxx"
+
+#include "SMESH_Gen.hxx"
+#include "SMESH_Gen_i.hxx"
+#include "SMESH_PythonDump.hxx"
+//#include "StdMeshers_ObjRefUlils.hxx"
+
+#include "Utils_CorbaException.hxx"
+#include "utilities.h"
+
+#include <TCollection_AsciiString.hxx>
+
+#include CORBA_SERVER_HEADER(SMESH_Group)
+
+using namespace std;
+
+//=============================================================================
+/*!
+ *  StdMeshers_ViscousLayers_i::StdMeshers_ViscousLayers_i
+ *
+ *  Constructor
+ */
+//=============================================================================
+
+StdMeshers_ViscousLayers_i::StdMeshers_ViscousLayers_i( PortableServer::POA_ptr thePOA,
+                                                        int                     theStudyId,
+                                                        ::SMESH_Gen*            theGenImpl )
+  : SALOME::GenericObj_i( thePOA ), 
+    SMESH_Hypothesis_i( thePOA )
+{
+  MESSAGE( "StdMeshers_ViscousLayers_i::StdMeshers_ViscousLayers_i" );
+  myBaseImpl = new ::StdMeshers_ViscousLayers( theGenImpl->GetANewId(),
+                                               theStudyId,
+                                               theGenImpl );
+}
+
+//=============================================================================
+/*!
+ *  StdMeshers_ViscousLayers_i::~StdMeshers_ViscousLayers_i
+ *
+ *  Destructor
+ */
+//=============================================================================
+
+StdMeshers_ViscousLayers_i::~StdMeshers_ViscousLayers_i()
+{
+  MESSAGE( "StdMeshers_ViscousLayers_i::~StdMeshers_ViscousLayers_i" );
+}
+
+//================================================================================
+/*!
+ * \brief 
+ */
+//================================================================================
+
+void StdMeshers_ViscousLayers_i::SetIgnoreFaces(const ::SMESH::long_array& faceIDs)
+throw ( SALOME::SALOME_Exception )
+{
+  vector<int> ids( faceIDs.length() );
+  for ( unsigned i = 0; i < ids.size(); ++i )
+    if (( ids[i] = faceIDs[i] ) < 1 )
+      THROW_SALOME_CORBA_EXCEPTION( "Invalid face id", SALOME::BAD_PARAM );
+  GetImpl()->SetIgnoreFaces( ids );
+  SMESH::TPythonDump() << _this() << ".SetIgnoreFaces( " << faceIDs << " )";
+}
+
+//================================================================================
+/*!
+ * \brief 
+ */
+//================================================================================
+
+SMESH::long_array* StdMeshers_ViscousLayers_i::GetIgnoreFaces()
+{
+  vector<int> idsVec = GetImpl()->GetIgnoreFaces();
+  SMESH::long_array_var ids = new SMESH::long_array;
+  ids->length( idsVec.size() );
+  for ( unsigned i = 0; i < idsVec.size(); ++i )
+    ids[i] = idsVec[i];
+  return ids._retn();
+}
+
+//================================================================================
+/*!
+ * \brief 
+ */
+//================================================================================
+
+void StdMeshers_ViscousLayers_i::SetTotalThickness(::CORBA::Double thickness)
+throw ( SALOME::SALOME_Exception )
+{
+  if ( thickness < 1e-100 )
+    THROW_SALOME_CORBA_EXCEPTION( "Invalid thickness", SALOME::BAD_PARAM );
+  GetImpl()->SetTotalThickness(thickness);
+  SMESH::TPythonDump() << _this() << ".SetTotalThickness( " << thickness << " )";
+}
+
+//================================================================================
+/*!
+ * \brief 
+ */
+//================================================================================
+
+::CORBA::Double StdMeshers_ViscousLayers_i::GetTotalThickness()
+{
+  return GetImpl()->GetTotalThickness();
+}
+
+//================================================================================
+/*!
+ * \brief 
+ *  \param nb - 
+ */
+//================================================================================
+
+void StdMeshers_ViscousLayers_i::SetNumberLayers(::CORBA::Short nb)
+throw ( SALOME::SALOME_Exception )
+{
+  if ( nb < 1 )
+    THROW_SALOME_CORBA_EXCEPTION( "Invalid number of layers", SALOME::BAD_PARAM );
+  GetImpl()->SetNumberLayers( nb );
+  SMESH::TPythonDump() << _this() << ".SetNumberLayers( " << nb << " )";
+}
+
+//================================================================================
+/*!
+ * \brief 
+ */
+//================================================================================
+
+::CORBA::Short StdMeshers_ViscousLayers_i::GetNumberLayers()
+{
+  return CORBA::Short( GetImpl()->GetNumberLayers() );
+}
+
+//================================================================================
+/*!
+ * \brief 
+ *  \param factor - 
+ */
+//================================================================================
+
+void StdMeshers_ViscousLayers_i::SetStretchFactor(::CORBA::Double factor)
+throw ( SALOME::SALOME_Exception )
+{
+  if ( factor < 1 )
+    THROW_SALOME_CORBA_EXCEPTION( "Invalid stretch factor, it must be >= 1.0", SALOME::BAD_PARAM );
+  GetImpl()->SetStretchFactor(factor);
+  SMESH::TPythonDump() << _this() << ".SetStretchFactor( " << factor << " )";
+}
+
+//================================================================================
+/*!
+ * \brief 
+ * 
+ */
+//================================================================================
+
+::CORBA::Double StdMeshers_ViscousLayers_i::GetStretchFactor()
+{
+  return GetImpl()->GetStretchFactor();
+}
+
+//=============================================================================
+/*!
+ *  Get implementation
+ */
+//=============================================================================
+
+::StdMeshers_ViscousLayers* StdMeshers_ViscousLayers_i::GetImpl()
+{
+  MESSAGE( "StdMeshers_ViscousLayers_i::GetImpl" );
+  return ( ::StdMeshers_ViscousLayers* )myBaseImpl;
+}
+
+//================================================================================
+/*!
+ * \brief Verify whether hypothesis supports given entity type 
+  * \param type - dimension (see SMESH::Dimension enumeration)
+  * \retval CORBA::Boolean - TRUE if dimension is supported, FALSE otherwise
+ * 
+ * Verify whether hypothesis supports given entity type (see SMESH::Dimension enumeration)
+ */
+//================================================================================  
+CORBA::Boolean StdMeshers_ViscousLayers_i::IsDimSupported( SMESH::Dimension type )
+{
+  return type == SMESH::DIM_3D;
+}
+
diff --git a/src/StdMeshers_I/StdMeshers_ViscousLayers_i.hxx b/src/StdMeshers_I/StdMeshers_ViscousLayers_i.hxx
new file mode 100644 (file)
index 0000000..29699eb
--- /dev/null
@@ -0,0 +1,81 @@
+//  Copyright (C) 2007-2010  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
+//
+//  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.
+//
+//  This library is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  Lesser General Public License for more details.
+//
+//  You should have received a copy of the GNU Lesser General Public
+//  License along with this library; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+//  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+//  SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses
+//  File   : StdMeshers_ViscousLayers_i.hxx
+//  Module : SMESH
+//
+#ifndef _SMESH_ViscousLayers_I_HXX_
+#define _SMESH_ViscousLayers_I_HXX_
+
+#include "SMESH_StdMeshers_I.hxx"
+
+#include <SALOMEconfig.h>
+#include CORBA_SERVER_HEADER(SMESH_BasicHypothesis)
+
+#include "SMESH_Hypothesis_i.hxx"
+#include "StdMeshers_ViscousLayers.hxx"
+
+class SMESH_Gen;
+
+class STDMESHERS_I_EXPORT StdMeshers_ViscousLayers_i:
+  public virtual POA_StdMeshers::StdMeshers_ViscousLayers,
+  public virtual SMESH_Hypothesis_i
+{
+ public:
+  // Constructor
+  StdMeshers_ViscousLayers_i( PortableServer::POA_ptr thePOA,
+                              int                     theStudyId,
+                              ::SMESH_Gen*            theGenImpl );
+  // Destructor
+  virtual ~StdMeshers_ViscousLayers_i();
+
+  void SetIgnoreFaces(const ::SMESH::long_array& faceIDs) throw ( SALOME::SALOME_Exception );
+  SMESH::long_array* GetIgnoreFaces();
+
+  void SetTotalThickness(::CORBA::Double thickness) throw ( SALOME::SALOME_Exception );
+  ::CORBA::Double GetTotalThickness();
+
+  void SetNumberLayers(::CORBA::Short nb) throw ( SALOME::SALOME_Exception );
+  ::CORBA::Short GetNumberLayers();
+
+  void SetStretchFactor(::CORBA::Double factor) throw ( SALOME::SALOME_Exception );
+  ::CORBA::Double GetStretchFactor();
+
+  // Get implementation
+  ::StdMeshers_ViscousLayers* GetImpl();
+
+  // Verify whether hypothesis supports given entity type 
+  CORBA::Boolean IsDimSupported( SMESH::Dimension type );
+
+  // Redefined Persistence
+//   virtual char* SaveTo();
+//   virtual void  LoadFrom( const char* theStream );
+//   virtual void  UpdateAsMeshesRestored();
+
+ private:
+  //SMESH::string_array_var _groupEntries;
+  //std::vector< std::string > _groupIDs;
+};
+
+#endif
+
index a8f6a9824f19d2fa46cf8bb0d67375b3c7d1f6e6..3145a081dac04abccfa908f1d9d4cb86057b3d22 100644 (file)
@@ -71,6 +71,7 @@
 #include "StdMeshers_RadialQuadrangle_1D2D_i.hxx"
 #include "StdMeshers_Import_1D_i.hxx"
 #include "StdMeshers_Import_1D2D_i.hxx"
+#include "StdMeshers_ViscousLayers_i.hxx"
 
 
 template <class T> class StdHypothesisCreator_i:public HypothesisCreator_i<T>
@@ -149,6 +150,8 @@ STDMESHERS_I_EXPORT
       aCreator = new StdHypothesisCreator_i<StdMeshers_ImportSource1D_i>;
     else if (strcmp(aHypName, "ImportSource2D") == 0)
       aCreator = new StdHypothesisCreator_i<StdMeshers_ImportSource2D_i>;
+    else if (strcmp(aHypName, "ViscousLayers") == 0)
+      aCreator = new StdHypothesisCreator_i<StdMeshers_ViscousLayers_i>;
 
     // Algorithms
     else if (strcmp(aHypName, "Regular_1D") == 0)