Salome HOME
Typo-fix by Kunda
[modules/smesh.git] / src / StdMeshers / StdMeshers_ViscousLayers.cxx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 // File      : StdMeshers_ViscousLayers.cxx
21 // Created   : Wed Dec  1 15:15:34 2010
22 // Author    : Edward AGAPOV (eap)
23
24 #include "StdMeshers_ViscousLayers.hxx"
25
26 #include "SMDS_EdgePosition.hxx"
27 #include "SMDS_FaceOfNodes.hxx"
28 #include "SMDS_FacePosition.hxx"
29 #include "SMDS_MeshNode.hxx"
30 #include "SMDS_SetIterator.hxx"
31 #include "SMESHDS_Group.hxx"
32 #include "SMESHDS_Hypothesis.hxx"
33 #include "SMESHDS_Mesh.hxx"
34 #include "SMESH_Algo.hxx"
35 #include "SMESH_ComputeError.hxx"
36 #include "SMESH_ControlsDef.hxx"
37 #include "SMESH_Gen.hxx"
38 #include "SMESH_Group.hxx"
39 #include "SMESH_HypoFilter.hxx"
40 #include "SMESH_Mesh.hxx"
41 #include "SMESH_MeshAlgos.hxx"
42 #include "SMESH_MesherHelper.hxx"
43 #include "SMESH_ProxyMesh.hxx"
44 #include "SMESH_subMesh.hxx"
45 #include "SMESH_subMeshEventListener.hxx"
46 #include "StdMeshers_FaceSide.hxx"
47 #include "StdMeshers_ViscousLayers2D.hxx"
48
49 #include <Adaptor3d_HSurface.hxx>
50 #include <BRepAdaptor_Curve.hxx>
51 #include <BRepAdaptor_Curve2d.hxx>
52 #include <BRepAdaptor_Surface.hxx>
53 //#include <BRepLProp_CLProps.hxx>
54 #include <BRepLProp_SLProps.hxx>
55 #include <BRepOffsetAPI_MakeOffsetShape.hxx>
56 #include <BRep_Tool.hxx>
57 #include <Bnd_B2d.hxx>
58 #include <Bnd_B3d.hxx>
59 #include <ElCLib.hxx>
60 #include <GCPnts_AbscissaPoint.hxx>
61 #include <GCPnts_TangentialDeflection.hxx>
62 #include <Geom2d_Circle.hxx>
63 #include <Geom2d_Line.hxx>
64 #include <Geom2d_TrimmedCurve.hxx>
65 #include <GeomAdaptor_Curve.hxx>
66 #include <GeomLib.hxx>
67 #include <Geom_Circle.hxx>
68 #include <Geom_Curve.hxx>
69 #include <Geom_Line.hxx>
70 #include <Geom_TrimmedCurve.hxx>
71 #include <Precision.hxx>
72 #include <Standard_ErrorHandler.hxx>
73 #include <Standard_Failure.hxx>
74 #include <TColStd_Array1OfReal.hxx>
75 #include <TopExp.hxx>
76 #include <TopExp_Explorer.hxx>
77 #include <TopTools_IndexedMapOfShape.hxx>
78 #include <TopTools_ListOfShape.hxx>
79 #include <TopTools_MapIteratorOfMapOfShape.hxx>
80 #include <TopTools_MapOfShape.hxx>
81 #include <TopoDS.hxx>
82 #include <TopoDS_Edge.hxx>
83 #include <TopoDS_Face.hxx>
84 #include <TopoDS_Vertex.hxx>
85 #include <gp_Ax1.hxx>
86 #include <gp_Cone.hxx>
87 #include <gp_Sphere.hxx>
88 #include <gp_Vec.hxx>
89 #include <gp_XY.hxx>
90
91 #include <cmath>
92 #include <limits>
93 #include <list>
94 #include <queue>
95 #include <string>
96
97 #ifdef _DEBUG_
98 #define __myDEBUG
99 //#define __NOT_INVALIDATE_BAD_SMOOTH
100 //#define __NODES_AT_POS
101 #endif
102
103 #define INCREMENTAL_SMOOTH // smooth only if min angle is too small
104 #define BLOCK_INFLATION // of individual _LayerEdge's
105 #define OLD_NEF_POLYGON
106
107 using namespace std;
108
109 //================================================================================
110 namespace VISCOUS_3D
111 {
112   typedef int TGeomID;
113
114   enum UIndex { U_TGT = 1, U_SRC, LEN_TGT };
115
116   const double theMinSmoothCosin = 0.1;
117   const double theSmoothThickToElemSizeRatio = 0.6;
118   const double theMinSmoothTriaAngle = 30;
119   const double theMinSmoothQuadAngle = 45;
120
121   // what part of thickness is allowed till intersection
122   // (defined by SALOME_TESTS/Grids/smesh/viscous_layers_00/A5)
123   const double theThickToIntersection = 1.5;
124
125   bool needSmoothing( double cosin, double tgtThick, double elemSize )
126   {
127     return cosin * tgtThick > theSmoothThickToElemSizeRatio * elemSize;
128   }
129   double getSmoothingThickness( double cosin, double elemSize )
130   {
131     return theSmoothThickToElemSizeRatio * elemSize / cosin;
132   }
133
134   /*!
135    * \brief SMESH_ProxyMesh computed by _ViscousBuilder for a SOLID.
136    * It is stored in a SMESH_subMesh of the SOLID as SMESH_subMeshEventListenerData
137    */
138   struct _MeshOfSolid : public SMESH_ProxyMesh,
139                         public SMESH_subMeshEventListenerData
140   {
141     bool                  _n2nMapComputed;
142     SMESH_ComputeErrorPtr _warning;
143
144     _MeshOfSolid( SMESH_Mesh* mesh)
145       :SMESH_subMeshEventListenerData( /*isDeletable=*/true),_n2nMapComputed(false)
146     {
147       SMESH_ProxyMesh::setMesh( *mesh );
148     }
149
150     // returns submesh for a geom face
151     SMESH_ProxyMesh::SubMesh* getFaceSubM(const TopoDS_Face& F, bool create=false)
152     {
153       TGeomID i = SMESH_ProxyMesh::shapeIndex(F);
154       return create ? SMESH_ProxyMesh::getProxySubMesh(i) : findProxySubMesh(i);
155     }
156     void setNode2Node(const SMDS_MeshNode*                 srcNode,
157                       const SMDS_MeshNode*                 proxyNode,
158                       const SMESH_ProxyMesh::SubMesh* subMesh)
159     {
160       SMESH_ProxyMesh::setNode2Node( srcNode,proxyNode,subMesh);
161     }
162   };
163   //--------------------------------------------------------------------------------
164   /*!
165    * \brief Listener of events of 3D sub-meshes computed with viscous layers.
166    * It is used to clear an inferior dim sub-meshes modified by viscous layers
167    */
168   class _ShrinkShapeListener : SMESH_subMeshEventListener
169   {
170     _ShrinkShapeListener()
171       : SMESH_subMeshEventListener(/*isDeletable=*/false,
172                                    "StdMeshers_ViscousLayers::_ShrinkShapeListener") {}
173   public:
174     static SMESH_subMeshEventListener* Get() { static _ShrinkShapeListener l; return &l; }
175     virtual void ProcessEvent(const int                       event,
176                               const int                       eventType,
177                               SMESH_subMesh*                  solidSM,
178                               SMESH_subMeshEventListenerData* data,
179                               const SMESH_Hypothesis*         hyp)
180     {
181       if ( SMESH_subMesh::COMPUTE_EVENT == eventType && solidSM->IsEmpty() && data )
182       {
183         SMESH_subMeshEventListener::ProcessEvent(event,eventType,solidSM,data,hyp);
184       }
185     }
186   };
187   //--------------------------------------------------------------------------------
188   /*!
189    * \brief Listener of events of 3D sub-meshes computed with viscous layers.
190    * It is used to store data computed by _ViscousBuilder for a sub-mesh and to
191    * delete the data as soon as it has been used
192    */
193   class _ViscousListener : SMESH_subMeshEventListener
194   {
195     _ViscousListener():
196       SMESH_subMeshEventListener(/*isDeletable=*/false,
197                                  "StdMeshers_ViscousLayers::_ViscousListener") {}
198     static SMESH_subMeshEventListener* Get() { static _ViscousListener l; return &l; }
199   public:
200     virtual void ProcessEvent(const int                       event,
201                               const int                       eventType,
202                               SMESH_subMesh*                  subMesh,
203                               SMESH_subMeshEventListenerData* data,
204                               const SMESH_Hypothesis*         hyp)
205     {
206       if (( SMESH_subMesh::COMPUTE_EVENT       == eventType ) &&
207           ( SMESH_subMesh::CHECK_COMPUTE_STATE != event &&
208             SMESH_subMesh::SUBMESH_COMPUTED    != event ))
209       {
210         // delete SMESH_ProxyMesh containing temporary faces
211         subMesh->DeleteEventListener( this );
212       }
213     }
214     // Finds or creates proxy mesh of the solid
215     static _MeshOfSolid* GetSolidMesh(SMESH_Mesh*         mesh,
216                                       const TopoDS_Shape& solid,
217                                       bool                toCreate=false)
218     {
219       if ( !mesh ) return 0;
220       SMESH_subMesh* sm = mesh->GetSubMesh(solid);
221       _MeshOfSolid* data = (_MeshOfSolid*) sm->GetEventListenerData( Get() );
222       if ( !data && toCreate )
223       {
224         data = new _MeshOfSolid(mesh);
225         data->mySubMeshes.push_back( sm ); // to find SOLID by _MeshOfSolid
226         sm->SetEventListener( Get(), data, sm );
227       }
228       return data;
229     }
230     // Removes proxy mesh of the solid
231     static void RemoveSolidMesh(SMESH_Mesh* mesh, const TopoDS_Shape& solid)
232     {
233       mesh->GetSubMesh(solid)->DeleteEventListener( _ViscousListener::Get() );
234     }
235   };
236   
237   //================================================================================
238   /*!
239    * \brief sets a sub-mesh event listener to clear sub-meshes of sub-shapes of
240    * the main shape when sub-mesh of the main shape is cleared,
241    * for example to clear sub-meshes of FACEs when sub-mesh of a SOLID
242    * is cleared
243    */
244   //================================================================================
245
246   void ToClearSubWithMain( SMESH_subMesh* sub, const TopoDS_Shape& main)
247   {
248     SMESH_subMesh* mainSM = sub->GetFather()->GetSubMesh( main );
249     SMESH_subMeshEventListenerData* data =
250       mainSM->GetEventListenerData( _ShrinkShapeListener::Get());
251     if ( data )
252     {
253       if ( find( data->mySubMeshes.begin(), data->mySubMeshes.end(), sub ) ==
254            data->mySubMeshes.end())
255         data->mySubMeshes.push_back( sub );
256     }
257     else
258     {
259       data = SMESH_subMeshEventListenerData::MakeData( /*dependent=*/sub );
260       sub->SetEventListener( _ShrinkShapeListener::Get(), data, /*whereToListenTo=*/mainSM );
261     }
262   }
263   struct _SolidData;
264   //--------------------------------------------------------------------------------
265   /*!
266    * \brief Simplex (triangle or tetrahedron) based on 1 (tria) or 2 (tet) nodes of
267    * _LayerEdge and 2 nodes of the mesh surface beening smoothed.
268    * The class is used to check validity of face or volumes around a smoothed node;
269    * it stores only 2 nodes as the other nodes are stored by _LayerEdge.
270    */
271   struct _Simplex
272   {
273     const SMDS_MeshNode *_nPrev, *_nNext; // nodes on a smoothed mesh surface
274     const SMDS_MeshNode *_nOpp; // in 2D case, a node opposite to a smoothed node in QUAD
275     _Simplex(const SMDS_MeshNode* nPrev=0,
276              const SMDS_MeshNode* nNext=0,
277              const SMDS_MeshNode* nOpp=0)
278       : _nPrev(nPrev), _nNext(nNext), _nOpp(nOpp) {}
279     bool IsForward(const gp_XYZ* pntSrc, const gp_XYZ* pntTgt, double& vol) const
280     {
281       const double M[3][3] =
282         {{ _nNext->X() - pntSrc->X(), _nNext->Y() - pntSrc->Y(), _nNext->Z() - pntSrc->Z() },
283          { pntTgt->X() - pntSrc->X(), pntTgt->Y() - pntSrc->Y(), pntTgt->Z() - pntSrc->Z() },
284          { _nPrev->X() - pntSrc->X(), _nPrev->Y() - pntSrc->Y(), _nPrev->Z() - pntSrc->Z() }};
285       vol = ( + M[0][0] * M[1][1] * M[2][2]
286               + M[0][1] * M[1][2] * M[2][0]
287               + M[0][2] * M[1][0] * M[2][1]
288               - M[0][0] * M[1][2] * M[2][1]
289               - M[0][1] * M[1][0] * M[2][2]
290               - M[0][2] * M[1][1] * M[2][0]);
291       return vol > 1e-100;
292     }
293     bool IsForward(const SMDS_MeshNode* nSrc, const gp_XYZ& pTgt, double& vol) const
294     {
295       SMESH_TNodeXYZ pSrc( nSrc );
296       return IsForward( &pSrc, &pTgt, vol );
297     }
298     bool IsForward(const gp_XY&         tgtUV,
299                    const SMDS_MeshNode* smoothedNode,
300                    const TopoDS_Face&   face,
301                    SMESH_MesherHelper&  helper,
302                    const double         refSign) const
303     {
304       gp_XY prevUV = helper.GetNodeUV( face, _nPrev, smoothedNode );
305       gp_XY nextUV = helper.GetNodeUV( face, _nNext, smoothedNode );
306       gp_Vec2d v1( tgtUV, prevUV ), v2( tgtUV, nextUV );
307       double d = v1 ^ v2;
308       return d*refSign > 1e-100;
309     }
310     bool IsMinAngleOK( const gp_XYZ& pTgt, double& minAngle ) const
311     {
312       SMESH_TNodeXYZ pPrev( _nPrev ), pNext( _nNext );
313       if ( !_nOpp ) // triangle
314       {
315         gp_Vec tp( pPrev - pTgt ), pn( pNext - pPrev ), nt( pTgt - pNext );
316         double tp2 = tp.SquareMagnitude();
317         double pn2 = pn.SquareMagnitude();
318         double nt2 = nt.SquareMagnitude();
319
320         if ( tp2 < pn2 && tp2 < nt2 )
321           minAngle = ( nt * -pn ) * ( nt * -pn ) / nt2 / pn2;
322         else if ( pn2 < nt2 )
323           minAngle = ( tp * -nt ) * ( tp * -nt ) / tp2 / nt2;
324         else
325           minAngle = ( pn * -tp ) * ( pn * -tp ) / pn2 / tp2;
326
327         static double theMaxCos2 = ( Cos( theMinSmoothTriaAngle * M_PI / 180. ) *
328                                      Cos( theMinSmoothTriaAngle * M_PI / 180. ));
329         return minAngle < theMaxCos2;
330       }
331       else // quadrangle
332       {
333         SMESH_TNodeXYZ pOpp( _nOpp );
334         gp_Vec tp( pPrev - pTgt ), po( pOpp - pPrev ), on( pNext - pOpp), nt( pTgt - pNext );
335         double tp2 = tp.SquareMagnitude();
336         double po2 = po.SquareMagnitude();
337         double on2 = on.SquareMagnitude();
338         double nt2 = nt.SquareMagnitude();
339         minAngle = Max( Max((( tp * -nt ) * ( tp * -nt ) / tp2 / nt2 ),
340                             (( po * -tp ) * ( po * -tp ) / po2 / tp2 )),
341                         Max((( on * -po ) * ( on * -po ) / on2 / po2 ),
342                             (( nt * -on ) * ( nt * -on ) / nt2 / on2 )));
343
344         static double theMaxCos2 = ( Cos( theMinSmoothQuadAngle * M_PI / 180. ) *
345                                      Cos( theMinSmoothQuadAngle * M_PI / 180. ));
346         return minAngle < theMaxCos2;
347       }
348     }
349     bool IsNeighbour(const _Simplex& other) const
350     {
351       return _nPrev == other._nNext || _nNext == other._nPrev;
352     }
353     bool Includes( const SMDS_MeshNode* node ) const { return _nPrev == node || _nNext == node; }
354     static void GetSimplices( const SMDS_MeshNode* node,
355                               vector<_Simplex>&   simplices,
356                               const set<TGeomID>& ingnoreShapes,
357                               const _SolidData*   dataToCheckOri = 0,
358                               const bool          toSort = false);
359     static void SortSimplices(vector<_Simplex>& simplices);
360   };
361   //--------------------------------------------------------------------------------
362   /*!
363    * Structure used to take into account surface curvature while smoothing
364    */
365   struct _Curvature
366   {
367     double   _r; // radius
368     double   _k; // factor to correct node smoothed position
369     double   _h2lenRatio; // avgNormProj / (2*avgDist)
370     gp_Pnt2d _uv; // UV used in putOnOffsetSurface()
371   public:
372     static _Curvature* New( double avgNormProj, double avgDist )
373     {
374       _Curvature* c = 0;
375       if ( fabs( avgNormProj / avgDist ) > 1./200 )
376       {
377         c = new _Curvature;
378         c->_r = avgDist * avgDist / avgNormProj;
379         c->_k = avgDist * avgDist / c->_r / c->_r;
380         //c->_k = avgNormProj / c->_r;
381         c->_k *= ( c->_r < 0 ? 1/1.1 : 1.1 ); // not to be too restrictive
382         c->_h2lenRatio = avgNormProj / ( avgDist + avgDist );
383
384         c->_uv.SetCoord( 0., 0. );
385       }
386       return c;
387     }
388     double lenDelta(double len) const { return _k * ( _r + len ); }
389     double lenDeltaByDist(double dist) const { return dist * _h2lenRatio; }
390   };
391   //--------------------------------------------------------------------------------
392
393   struct _2NearEdges;
394   struct _LayerEdge;
395   struct _EdgesOnShape;
396   struct _Smoother1D;
397   typedef map< const SMDS_MeshNode*, _LayerEdge*, TIDCompare > TNode2Edge;
398
399   //--------------------------------------------------------------------------------
400   /*!
401    * \brief Edge normal to surface, connecting a node on solid surface (_nodes[0])
402    * and a node of the most internal layer (_nodes.back())
403    */
404   struct _LayerEdge
405   {
406     typedef gp_XYZ (_LayerEdge::*PSmooFun)();
407
408     vector< const SMDS_MeshNode*> _nodes;
409
410     gp_XYZ              _normal;    // to boundary of solid
411     vector<gp_XYZ>      _pos;       // points computed during inflation
412     double              _len;       // length achieved with the last inflation step
413     double              _maxLen;    // maximal possible length
414     double              _cosin;     // of angle (_normal ^ surface)
415     double              _minAngle;  // of _simplices
416     double              _lenFactor; // to compute _len taking _cosin into account
417     int                 _flags;
418
419     // simplices connected to the source node (_nodes[0]);
420     // used for smoothing and quality check of _LayerEdge's based on the FACE
421     vector<_Simplex>    _simplices;
422     vector<_LayerEdge*> _neibors; // all surrounding _LayerEdge's
423     PSmooFun            _smooFunction; // smoothing function
424     _Curvature*         _curvature;
425     // data for smoothing of _LayerEdge's based on the EDGE
426     _2NearEdges*        _2neibors;
427
428     enum EFlags { TO_SMOOTH       = 0x0000001,
429                   MOVED           = 0x0000002, // set by _neibors[i]->SetNewLength()
430                   SMOOTHED        = 0x0000004, // set by _LayerEdge::Smooth()
431                   DIFFICULT       = 0x0000008, // near concave VERTEX
432                   ON_CONCAVE_FACE = 0x0000010,
433                   BLOCKED         = 0x0000020, // not to inflate any more
434                   INTERSECTED     = 0x0000040, // close intersection with a face found
435                   NORMAL_UPDATED  = 0x0000080,
436                   UPD_NORMAL_CONV = 0x0000100, // to update normal on boundary of concave FACE
437                   MARKED          = 0x0000200, // local usage
438                   MULTI_NORMAL    = 0x0000400, // a normal is invisible by some of surrounding faces
439                   NEAR_BOUNDARY   = 0x0000800, // is near FACE boundary forcing smooth
440                   SMOOTHED_C1     = 0x0001000, // is on _eosC1
441                   DISTORTED       = 0x0002000, // was bad before smoothing
442                   RISKY_SWOL      = 0x0004000, // SWOL is parallel to a source FACE
443                   SHRUNK          = 0x0008000, // target node reached a tgt position while shrink()
444                   UNUSED_FLAG     = 0x0100000  // to add user flags after
445     };
446     bool Is   ( int flag ) const { return _flags & flag; }
447     void Set  ( int flag ) { _flags |= flag; }
448     void Unset( int flag ) { _flags &= ~flag; }
449     std::string DumpFlags() const; // debug
450
451     void SetNewLength( double len, _EdgesOnShape& eos, SMESH_MesherHelper& helper );
452     bool SetNewLength2d( Handle(Geom_Surface)& surface,
453                          const TopoDS_Face&    F,
454                          _EdgesOnShape&        eos,
455                          SMESH_MesherHelper&   helper );
456     void SetDataByNeighbors( const SMDS_MeshNode* n1,
457                              const SMDS_MeshNode* n2,
458                              const _EdgesOnShape& eos,
459                              SMESH_MesherHelper&  helper);
460     void Block( _SolidData& data );
461     void InvalidateStep( size_t curStep, const _EdgesOnShape& eos, bool restoreLength=false );
462     void ChooseSmooFunction(const set< TGeomID >& concaveVertices,
463                             const TNode2Edge&     n2eMap);
464     void SmoothPos( const vector< double >& segLen, const double tol );
465     int  GetSmoothedPos( const double tol );
466     int  Smooth(const int step, const bool isConcaveFace, bool findBest);
467     int  Smooth(const int step, bool findBest, vector< _LayerEdge* >& toSmooth );
468     int  CheckNeiborsOnBoundary(vector< _LayerEdge* >* badNeibors = 0, bool * needSmooth = 0 );
469     void SmoothWoCheck();
470     bool SmoothOnEdge(Handle(ShapeAnalysis_Surface)& surface,
471                       const TopoDS_Face&             F,
472                       SMESH_MesherHelper&            helper);
473     void MoveNearConcaVer( const _EdgesOnShape*    eov,
474                            const _EdgesOnShape*    eos,
475                            const int               step,
476                            vector< _LayerEdge* > & badSmooEdges);
477     bool FindIntersection( SMESH_ElementSearcher&   searcher,
478                            double &                 distance,
479                            const double&            epsilon,
480                            _EdgesOnShape&           eos,
481                            const SMDS_MeshElement** face = 0);
482     bool SegTriaInter( const gp_Ax1&        lastSegment,
483                        const gp_XYZ&        p0,
484                        const gp_XYZ&        p1,
485                        const gp_XYZ&        p2,
486                        double&              dist,
487                        const double&        epsilon) const;
488     bool SegTriaInter( const gp_Ax1&        lastSegment,
489                        const SMDS_MeshNode* n0,
490                        const SMDS_MeshNode* n1,
491                        const SMDS_MeshNode* n2,
492                        double&              dist,
493                        const double&        epsilon) const
494     { return SegTriaInter( lastSegment,
495                            SMESH_TNodeXYZ( n0 ), SMESH_TNodeXYZ( n1 ), SMESH_TNodeXYZ( n2 ),
496                            dist, epsilon );
497     }
498     const gp_XYZ& PrevPos() const { return _pos[ _pos.size() - 2 ]; }
499     gp_XYZ PrevCheckPos( _EdgesOnShape* eos=0 ) const;
500     gp_Ax1 LastSegment(double& segLen, _EdgesOnShape& eos) const;
501     gp_XY  LastUV( const TopoDS_Face& F, _EdgesOnShape& eos, int which=-1 ) const;
502     bool   IsOnEdge() const { return _2neibors; }
503     bool   IsOnFace() const { return ( _nodes[0]->GetPosition()->GetDim() == 2 ); }
504     int    BaseShapeDim() const { return _nodes[0]->GetPosition()->GetDim(); }
505     gp_XYZ Copy( _LayerEdge& other, _EdgesOnShape& eos, SMESH_MesherHelper& helper );
506     void   SetCosin( double cosin );
507     void   SetNormal( const gp_XYZ& n ) { _normal = n; }
508     void   SetMaxLen( double l ) { _maxLen = l; }
509     int    NbSteps() const { return _pos.size() - 1; } // nb inlation steps
510     bool   IsNeiborOnEdge( const _LayerEdge* edge ) const;
511     void   SetSmooLen( double len ) { // set _len at which smoothing is needed
512       _cosin = len; // as for _LayerEdge's on FACE _cosin is not used
513     }
514     double GetSmooLen() { return _cosin; } // for _LayerEdge's on FACE _cosin is not used
515
516     gp_XYZ smoothLaplacian();
517     gp_XYZ smoothAngular();
518     gp_XYZ smoothLengthWeighted();
519     gp_XYZ smoothCentroidal();
520     gp_XYZ smoothNefPolygon();
521
522     enum { FUN_LAPLACIAN, FUN_LENWEIGHTED, FUN_CENTROIDAL, FUN_NEFPOLY, FUN_ANGULAR, FUN_NB };
523     static const int theNbSmooFuns = FUN_NB;
524     static PSmooFun _funs[theNbSmooFuns];
525     static const char* _funNames[theNbSmooFuns+1];
526     int smooFunID( PSmooFun fun=0) const;
527   };
528   _LayerEdge::PSmooFun _LayerEdge::_funs[theNbSmooFuns] = { &_LayerEdge::smoothLaplacian,
529                                                             &_LayerEdge::smoothLengthWeighted,
530                                                             &_LayerEdge::smoothCentroidal,
531                                                             &_LayerEdge::smoothNefPolygon,
532                                                             &_LayerEdge::smoothAngular };
533   const char* _LayerEdge::_funNames[theNbSmooFuns+1] = { "Laplacian",
534                                                          "LengthWeighted",
535                                                          "Centroidal",
536                                                          "NefPolygon",
537                                                          "Angular",
538                                                          "None"};
539   struct _LayerEdgeCmp
540   {
541     bool operator () (const _LayerEdge* e1, const _LayerEdge* e2) const
542     {
543       const bool cmpNodes = ( e1 && e2 && e1->_nodes.size() && e2->_nodes.size() );
544       return cmpNodes ? ( e1->_nodes[0]->GetID() < e2->_nodes[0]->GetID()) : ( e1 < e2 );
545     }
546   };
547   //--------------------------------------------------------------------------------
548   /*!
549    * A 2D half plane used by _LayerEdge::smoothNefPolygon()
550    */
551   struct _halfPlane
552   {
553     gp_XY _pos, _dir, _inNorm;
554     bool IsOut( const gp_XY p, const double tol ) const
555     {
556       return _inNorm * ( p - _pos ) < -tol;
557     }
558     bool FindIntersection( const _halfPlane& hp, gp_XY & intPnt )
559     {
560       //const double eps = 1e-10;
561       double D = _dir.Crossed( hp._dir );
562       if ( fabs(D) < std::numeric_limits<double>::min())
563         return false;
564       gp_XY vec21 = _pos - hp._pos; 
565       double u = hp._dir.Crossed( vec21 ) / D; 
566       intPnt = _pos + _dir * u;
567       return true;
568     }
569   };
570   //--------------------------------------------------------------------------------
571   /*!
572    * Structure used to smooth a _LayerEdge based on an EDGE.
573    */
574   struct _2NearEdges
575   {
576     double               _wgt  [2]; // weights of _nodes
577     _LayerEdge*          _edges[2];
578
579      // normal to plane passing through _LayerEdge._normal and tangent of EDGE
580     gp_XYZ*              _plnNorm;
581
582     _2NearEdges() { _edges[0]=_edges[1]=0; _plnNorm = 0; }
583     const SMDS_MeshNode* tgtNode(bool is2nd) {
584       return _edges[is2nd] ? _edges[is2nd]->_nodes.back() : 0;
585     }
586     const SMDS_MeshNode* srcNode(bool is2nd) {
587       return _edges[is2nd] ? _edges[is2nd]->_nodes[0] : 0;
588     }
589     void reverse() {
590       std::swap( _wgt  [0], _wgt  [1] );
591       std::swap( _edges[0], _edges[1] );
592     }
593     void set( _LayerEdge* e1, _LayerEdge* e2, double w1, double w2 ) {
594       _edges[0] = e1; _edges[1] = e2; _wgt[0] = w1; _wgt[1] = w2;
595     }
596     bool include( const _LayerEdge* e ) {
597       return ( _edges[0] == e || _edges[1] == e );
598     }
599   };
600
601
602   //--------------------------------------------------------------------------------
603   /*!
604    * \brief Layers parameters got by averaging several hypotheses
605    */
606   struct AverageHyp
607   {
608     AverageHyp( const StdMeshers_ViscousLayers* hyp = 0 )
609       :_nbLayers(0), _nbHyps(0), _method(0), _thickness(0), _stretchFactor(0)
610     {
611       Add( hyp );
612     }
613     void Add( const StdMeshers_ViscousLayers* hyp )
614     {
615       if ( hyp )
616       {
617         _nbHyps++;
618         _nbLayers       = hyp->GetNumberLayers();
619         //_thickness     += hyp->GetTotalThickness();
620         _thickness      = Max( _thickness, hyp->GetTotalThickness() );
621         _stretchFactor += hyp->GetStretchFactor();
622         _method         = hyp->GetMethod();
623       }
624     }
625     double GetTotalThickness() const { return _thickness; /*_nbHyps ? _thickness / _nbHyps : 0;*/ }
626     double GetStretchFactor()  const { return _nbHyps ? _stretchFactor / _nbHyps : 0; }
627     int    GetNumberLayers()   const { return _nbLayers; }
628     int    GetMethod()         const { return _method; }
629
630     bool   UseSurfaceNormal()  const
631     { return _method == StdMeshers_ViscousLayers::SURF_OFFSET_SMOOTH; }
632     bool   ToSmooth()          const
633     { return _method == StdMeshers_ViscousLayers::SURF_OFFSET_SMOOTH; }
634     bool   IsOffsetMethod()    const
635     { return _method == StdMeshers_ViscousLayers::FACE_OFFSET; }
636
637   private:
638     int     _nbLayers, _nbHyps, _method;
639     double  _thickness, _stretchFactor;
640   };
641
642   //--------------------------------------------------------------------------------
643   /*!
644    * \brief _LayerEdge's on a shape and other shape data
645    */
646   struct _EdgesOnShape
647   {
648     vector< _LayerEdge* > _edges;
649
650     TopoDS_Shape          _shape;
651     TGeomID               _shapeID;
652     SMESH_subMesh *       _subMesh;
653     // face or edge w/o layer along or near which _edges are inflated
654     TopoDS_Shape          _sWOL;
655     bool                  _isRegularSWOL; // w/o singularities
656     // averaged StdMeshers_ViscousLayers parameters
657     AverageHyp            _hyp;
658     bool                  _toSmooth;
659     _Smoother1D*          _edgeSmoother;
660     vector< _EdgesOnShape* > _eosConcaVer; // edges at concave VERTEXes of a FACE
661     vector< _EdgesOnShape* > _eosC1; // to smooth together several C1 continues shapes
662
663     vector< gp_XYZ >         _faceNormals; // if _shape is FACE
664     vector< _EdgesOnShape* > _faceEOS; // to get _faceNormals of adjacent FACEs
665
666     Handle(ShapeAnalysis_Surface) _offsetSurf;
667     _LayerEdge*                   _edgeForOffset;
668
669     _SolidData*            _data; // parent SOLID
670
671     _LayerEdge*      operator[](size_t i) const { return (_LayerEdge*) _edges[i]; }
672     size_t           size() const { return _edges.size(); }
673     TopAbs_ShapeEnum ShapeType() const
674     { return _shape.IsNull() ? TopAbs_SHAPE : _shape.ShapeType(); }
675     TopAbs_ShapeEnum SWOLType() const
676     { return _sWOL.IsNull() ? TopAbs_SHAPE : _sWOL.ShapeType(); }
677     bool             HasC1( const _EdgesOnShape* other ) const
678     { return std::find( _eosC1.begin(), _eosC1.end(), other ) != _eosC1.end(); }
679     bool             GetNormal( const SMDS_MeshElement* face, gp_Vec& norm );
680     _SolidData&      GetData() const { return *_data; }
681
682     _EdgesOnShape(): _shapeID(-1), _subMesh(0), _toSmooth(false), _edgeSmoother(0) {}
683   };
684
685   //--------------------------------------------------------------------------------
686   /*!
687    * \brief Convex FACE whose radius of curvature is less than the thickness of
688    *        layers. It is used to detect distortion of prisms based on a convex
689    *        FACE and to update normals to enable further increasing the thickness
690    */
691   struct _ConvexFace
692   {
693     TopoDS_Face                     _face;
694
695     // edges whose _simplices are used to detect prism distortion
696     vector< _LayerEdge* >           _simplexTestEdges;
697
698     // map a sub-shape to _SolidData::_edgesOnShape
699     map< TGeomID, _EdgesOnShape* >  _subIdToEOS;
700
701     bool                            _isTooCurved;
702     bool                            _normalsFixed;
703     bool                            _normalsFixedOnBorders; // used in putOnOffsetSurface()
704
705     double GetMaxCurvature( _SolidData&         data,
706                             _EdgesOnShape&      eof,
707                             BRepLProp_SLProps&  surfProp,
708                             SMESH_MesherHelper& helper);
709
710     bool GetCenterOfCurvature( _LayerEdge*         ledge,
711                                BRepLProp_SLProps&  surfProp,
712                                SMESH_MesherHelper& helper,
713                                gp_Pnt &            center ) const;
714     bool CheckPrisms() const;
715   };
716
717   //--------------------------------------------------------------------------------
718   /*!
719    * \brief Structure holding _LayerEdge's based on EDGEs that will collide
720    *        at inflation up to the full thickness. A detected collision
721    *        is fixed in updateNormals()
722    */
723   struct _CollisionEdges
724   {
725     _LayerEdge*           _edge;
726     vector< _LayerEdge* > _intEdges; // each pair forms an intersected quadrangle
727     const SMDS_MeshNode* nSrc(int i) const { return _intEdges[i]->_nodes[0]; }
728     const SMDS_MeshNode* nTgt(int i) const { return _intEdges[i]->_nodes.back(); }
729   };
730
731   //--------------------------------------------------------------------------------
732   /*!
733    * \brief Data of a SOLID
734    */
735   struct _SolidData
736   {
737     typedef const StdMeshers_ViscousLayers* THyp;
738     TopoDS_Shape                    _solid;
739     TopTools_MapOfShape             _before; // SOLIDs to be computed before _solid
740     TGeomID                         _index; // SOLID id
741     _MeshOfSolid*                   _proxyMesh;
742     list< THyp >                    _hyps;
743     list< TopoDS_Shape >            _hypShapes;
744     map< TGeomID, THyp >            _face2hyp; // filled if _hyps.size() > 1
745     set< TGeomID >                  _reversedFaceIds;
746     set< TGeomID >                  _ignoreFaceIds; // WOL FACEs and FACEs of other SOLIDs
747
748     double                          _stepSize, _stepSizeCoeff, _geomSize;
749     const SMDS_MeshNode*            _stepSizeNodes[2];
750
751     TNode2Edge                      _n2eMap; // nodes and _LayerEdge's based on them
752
753     // map to find _n2eMap of another _SolidData by a shrink shape shared by two _SolidData's
754     map< TGeomID, TNode2Edge* >     _s2neMap;
755     // _LayerEdge's with underlying shapes
756     vector< _EdgesOnShape >         _edgesOnShape;
757
758     // key:   an id of shape (EDGE or VERTEX) shared by a FACE with
759     //        layers and a FACE w/o layers
760     // value: the shape (FACE or EDGE) to shrink mesh on.
761     //       _LayerEdge's basing on nodes on key shape are inflated along the value shape
762     map< TGeomID, TopoDS_Shape >     _shrinkShape2Shape;
763
764     // Convex FACEs whose radius of curvature is less than the thickness of layers
765     map< TGeomID, _ConvexFace >      _convexFaces;
766
767     // shapes (EDGEs and VERTEXes) srink from which is forbidden due to collisions with
768     // the adjacent SOLID
769     set< TGeomID >                   _noShrinkShapes;
770
771     int                              _nbShapesToSmooth;
772
773     vector< _CollisionEdges >        _collisionEdges;
774     set< TGeomID >                   _concaveFaces;
775
776     double                           _maxThickness; // of all _hyps
777     double                           _minThickness; // of all _hyps
778
779     double                           _epsilon; // precision for SegTriaInter()
780
781     SMESH_MesherHelper*              _helper;
782
783     _SolidData(const TopoDS_Shape& s=TopoDS_Shape(),
784                _MeshOfSolid*       m=0)
785       :_solid(s), _proxyMesh(m), _helper(0) {}
786     ~_SolidData();
787
788     void SortOnEdge( const TopoDS_Edge& E, vector< _LayerEdge* >& edges);
789     void Sort2NeiborsOnEdge( vector< _LayerEdge* >& edges );
790
791     _ConvexFace* GetConvexFace( const TGeomID faceID ) {
792       map< TGeomID, _ConvexFace >::iterator id2face = _convexFaces.find( faceID );
793       return id2face == _convexFaces.end() ? 0 : & id2face->second;
794     }
795     _EdgesOnShape* GetShapeEdges(const TGeomID       shapeID );
796     _EdgesOnShape* GetShapeEdges(const TopoDS_Shape& shape );
797     _EdgesOnShape* GetShapeEdges(const _LayerEdge*   edge )
798     { return GetShapeEdges( edge->_nodes[0]->getshapeId() ); }
799
800     SMESH_MesherHelper& GetHelper() const { return *_helper; }
801
802     void UnmarkEdges( int flag = _LayerEdge::MARKED ) {
803       for ( size_t i = 0; i < _edgesOnShape.size(); ++i )
804         for ( size_t j = 0; j < _edgesOnShape[i]._edges.size(); ++j )
805           _edgesOnShape[i]._edges[j]->Unset( flag );
806     }
807     void AddShapesToSmooth( const set< _EdgesOnShape* >& shape,
808                             const set< _EdgesOnShape* >* edgesNoAnaSmooth=0 );
809
810     void PrepareEdgesToSmoothOnFace( _EdgesOnShape* eof, bool substituteSrcNodes );
811   };
812   //--------------------------------------------------------------------------------
813   /*!
814    * \brief Offset plane used in getNormalByOffset()
815    */
816   struct _OffsetPlane
817   {
818     gp_Pln _plane;
819     int    _faceIndex;
820     int    _faceIndexNext[2];
821     gp_Lin _lines[2]; // line of intersection with neighbor _OffsetPlane's
822     bool   _isLineOK[2];
823     _OffsetPlane() {
824       _isLineOK[0] = _isLineOK[1] = false; _faceIndexNext[0] = _faceIndexNext[1] = -1;
825     }
826     void   ComputeIntersectionLine( _OffsetPlane&        pln, 
827                                     const TopoDS_Edge&   E,
828                                     const TopoDS_Vertex& V );
829     gp_XYZ GetCommonPoint(bool& isFound, const TopoDS_Vertex& V) const;
830     int    NbLines() const { return _isLineOK[0] + _isLineOK[1]; }
831   };
832   //--------------------------------------------------------------------------------
833   /*!
834    * \brief Container of centers of curvature at nodes on an EDGE bounding _ConvexFace
835    */
836   struct _CentralCurveOnEdge
837   {
838     bool                  _isDegenerated;
839     vector< gp_Pnt >      _curvaCenters;
840     vector< _LayerEdge* > _ledges;
841     vector< gp_XYZ >      _normals; // new normal for each of _ledges
842     vector< double >      _segLength2;
843
844     TopoDS_Edge           _edge;
845     TopoDS_Face           _adjFace;
846     bool                  _adjFaceToSmooth;
847
848     void Append( const gp_Pnt& center, _LayerEdge* ledge )
849     {
850       if ( ledge->Is( _LayerEdge::MULTI_NORMAL ))
851         return;
852       if ( _curvaCenters.size() > 0 )
853         _segLength2.push_back( center.SquareDistance( _curvaCenters.back() ));
854       _curvaCenters.push_back( center );
855       _ledges.push_back( ledge );
856       _normals.push_back( ledge->_normal );
857     }
858     bool FindNewNormal( const gp_Pnt& center, gp_XYZ& newNormal );
859     void SetShapes( const TopoDS_Edge&  edge,
860                     const _ConvexFace&  convFace,
861                     _SolidData&         data,
862                     SMESH_MesherHelper& helper);
863   };
864   //--------------------------------------------------------------------------------
865   /*!
866    * \brief Data of node on a shrinked FACE
867    */
868   struct _SmoothNode
869   {
870     const SMDS_MeshNode*         _node;
871     vector<_Simplex>             _simplices; // for quality check
872
873     enum SmoothType { LAPLACIAN, CENTROIDAL, ANGULAR, TFI };
874
875     bool Smooth(int&                  badNb,
876                 Handle(Geom_Surface)& surface,
877                 SMESH_MesherHelper&   helper,
878                 const double          refSign,
879                 SmoothType            how,
880                 bool                  set3D);
881
882     gp_XY computeAngularPos(vector<gp_XY>& uv,
883                             const gp_XY&   uvToFix,
884                             const double   refSign );
885   };
886   struct PyDump;
887   //--------------------------------------------------------------------------------
888   /*!
889    * \brief Builder of viscous layers
890    */
891   class _ViscousBuilder
892   {
893   public:
894     _ViscousBuilder();
895     // does it's job
896     SMESH_ComputeErrorPtr Compute(SMESH_Mesh&         mesh,
897                                   const TopoDS_Shape& shape);
898     // check validity of hypotheses
899     SMESH_ComputeErrorPtr CheckHypotheses( SMESH_Mesh&         mesh,
900                                            const TopoDS_Shape& shape );
901
902     // restore event listeners used to clear an inferior dim sub-mesh modified by viscous layers
903     void RestoreListeners();
904
905     // computes SMESH_ProxyMesh::SubMesh::_n2n;
906     bool MakeN2NMap( _MeshOfSolid* pm );
907
908   private:
909
910     bool findSolidsWithLayers();
911     bool setBefore( _SolidData& solidBefore, _SolidData& solidAfter );
912     bool findFacesWithLayers(const bool onlyWith=false);
913     void getIgnoreFaces(const TopoDS_Shape&             solid,
914                         const StdMeshers_ViscousLayers* hyp,
915                         const TopoDS_Shape&             hypShape,
916                         set<TGeomID>&                   ignoreFaces);
917     bool makeLayer(_SolidData& data);
918     void setShapeData( _EdgesOnShape& eos, SMESH_subMesh* sm, _SolidData& data );
919     bool setEdgeData( _LayerEdge& edge, _EdgesOnShape& eos,
920                       SMESH_MesherHelper& helper, _SolidData& data);
921     gp_XYZ getFaceNormal(const SMDS_MeshNode* n,
922                          const TopoDS_Face&   face,
923                          SMESH_MesherHelper&  helper,
924                          bool&                isOK,
925                          bool                 shiftInside=false);
926     bool getFaceNormalAtSingularity(const gp_XY&        uv,
927                                     const TopoDS_Face&  face,
928                                     SMESH_MesherHelper& helper,
929                                     gp_Dir&             normal );
930     gp_XYZ getWeigthedNormal( const _LayerEdge*                edge );
931     gp_XYZ getNormalByOffset( _LayerEdge*                      edge,
932                               std::pair< TopoDS_Face, gp_XYZ > fId2Normal[],
933                               int                              nbFaces,
934                               bool                             lastNoOffset = false);
935     bool findNeiborsOnEdge(const _LayerEdge*     edge,
936                            const SMDS_MeshNode*& n1,
937                            const SMDS_MeshNode*& n2,
938                            _EdgesOnShape&        eos,
939                            _SolidData&           data);
940     void findSimplexTestEdges( _SolidData&                    data,
941                                vector< vector<_LayerEdge*> >& edgesByGeom);
942     void computeGeomSize( _SolidData& data );
943     bool findShapesToSmooth( _SolidData& data);
944     void limitStepSizeByCurvature( _SolidData&  data );
945     void limitStepSize( _SolidData&             data,
946                         const SMDS_MeshElement* face,
947                         const _LayerEdge*       maxCosinEdge );
948     void limitStepSize( _SolidData& data, const double minSize);
949     bool inflate(_SolidData& data);
950     bool smoothAndCheck(_SolidData& data, const int nbSteps, double & distToIntersection);
951     int  invalidateBadSmooth( _SolidData&               data,
952                               SMESH_MesherHelper&       helper,
953                               vector< _LayerEdge* >&    badSmooEdges,
954                               vector< _EdgesOnShape* >& eosC1,
955                               const int                 infStep );
956     void makeOffsetSurface( _EdgesOnShape& eos, SMESH_MesherHelper& );
957     void putOnOffsetSurface( _EdgesOnShape& eos, int infStep,
958                              vector< _EdgesOnShape* >& eosC1,
959                              int smooStep=0, int moveAll=false );
960     void findCollisionEdges( _SolidData& data, SMESH_MesherHelper& helper );
961     void findEdgesToUpdateNormalNearConvexFace( _ConvexFace &       convFace,
962                                                 _SolidData&         data,
963                                                 SMESH_MesherHelper& helper );
964     void limitMaxLenByCurvature( _SolidData& data, SMESH_MesherHelper& helper );
965     void limitMaxLenByCurvature( _LayerEdge* e1, _LayerEdge* e2,
966                                  _EdgesOnShape& eos1, _EdgesOnShape& eos2,
967                                  const bool isSmoothable );
968     bool updateNormals( _SolidData& data, SMESH_MesherHelper& helper, int stepNb, double stepSize );
969     bool updateNormalsOfConvexFaces( _SolidData&         data,
970                                      SMESH_MesherHelper& helper,
971                                      int                 stepNb );
972     void updateNormalsOfC1Vertices( _SolidData& data );
973     bool updateNormalsOfSmoothed( _SolidData&         data,
974                                   SMESH_MesherHelper& helper,
975                                   const int           nbSteps,
976                                   const double        stepSize );
977     bool isNewNormalOk( _SolidData&   data,
978                         _LayerEdge&   edge,
979                         const gp_XYZ& newNormal);
980     bool refine(_SolidData& data);
981     bool shrink(_SolidData& data);
982     bool prepareEdgeToShrink( _LayerEdge& edge, _EdgesOnShape& eos,
983                               SMESH_MesherHelper& helper,
984                               const SMESHDS_SubMesh* faceSubMesh );
985     void restoreNoShrink( _LayerEdge& edge ) const;
986     void fixBadFaces(const TopoDS_Face&          F,
987                      SMESH_MesherHelper&         helper,
988                      const bool                  is2D,
989                      const int                   step,
990                      set<const SMDS_MeshNode*> * involvedNodes=NULL);
991     bool addBoundaryElements(_SolidData& data);
992
993     bool error( const string& text, int solidID=-1 );
994     SMESHDS_Mesh* getMeshDS() const { return _mesh->GetMeshDS(); }
995
996     // debug
997     void makeGroupOfLE();
998
999     SMESH_Mesh*                _mesh;
1000     SMESH_ComputeErrorPtr      _error;
1001
1002     vector<                    _SolidData >  _sdVec;
1003     TopTools_IndexedMapOfShape _solids; // to find _SolidData by a solid
1004     TopTools_MapOfShape        _shrinkedFaces;
1005
1006     int                        _tmpFaceID;
1007     PyDump*                    _pyDump;
1008   };
1009   //--------------------------------------------------------------------------------
1010   /*!
1011    * \brief Shrinker of nodes on the EDGE
1012    */
1013   class _Shrinker1D
1014   {
1015     TopoDS_Edge                   _geomEdge;
1016     vector<double>                _initU;
1017     vector<double>                _normPar;
1018     vector<const SMDS_MeshNode*>  _nodes;
1019     const _LayerEdge*             _edges[2];
1020     bool                          _done;
1021   public:
1022     void AddEdge( const _LayerEdge* e, _EdgesOnShape& eos, SMESH_MesherHelper& helper );
1023     void Compute(bool set3D, SMESH_MesherHelper& helper);
1024     void RestoreParams();
1025     void SwapSrcTgtNodes(SMESHDS_Mesh* mesh);
1026     const TopoDS_Edge& GeomEdge() const { return _geomEdge; }
1027     const SMDS_MeshNode* TgtNode( bool is2nd ) const
1028     { return _edges[is2nd] ? _edges[is2nd]->_nodes.back() : 0; }
1029     const SMDS_MeshNode* SrcNode( bool is2nd ) const
1030     { return _edges[is2nd] ? _edges[is2nd]->_nodes[0] : 0; }
1031   };
1032   //--------------------------------------------------------------------------------
1033   /*!
1034    * \brief Smoother of _LayerEdge's on EDGE.
1035    */
1036   struct _Smoother1D
1037   {
1038     struct OffPnt // point of the offsetted EDGE
1039     {
1040       gp_XYZ      _xyz;    // coord of a point inflated from EDGE w/o smooth
1041       double      _len;    // length reached at previous inflation step
1042       double      _param;  // on EDGE
1043       _2NearEdges _2edges; // 2 neighbor _LayerEdge's
1044       gp_XYZ      _edgeDir;// EDGE tangent at _param
1045       double Distance( const OffPnt& p ) const { return ( _xyz - p._xyz ).Modulus(); }
1046     };
1047     vector< OffPnt >   _offPoints;
1048     vector< double >   _leParams; // normalized param of _eos._edges on EDGE
1049     Handle(Geom_Curve) _anaCurve; // for analytic smooth
1050     _LayerEdge         _leOnV[2]; // _LayerEdge's holding normal to the EDGE at VERTEXes
1051     gp_XYZ             _edgeDir[2]; // tangent at VERTEXes
1052     size_t             _iSeg[2];  // index of segment where extreme tgt node is projected
1053     _EdgesOnShape&     _eos;
1054     double             _curveLen; // length of the EDGE
1055     std::pair<int,int> _eToSmooth[2]; // <from,to> indices of _LayerEdge's in _eos
1056
1057     static Handle(Geom_Curve) CurveForSmooth( const TopoDS_Edge&  E,
1058                                               _EdgesOnShape&      eos,
1059                                               SMESH_MesherHelper& helper);
1060
1061     _Smoother1D( Handle(Geom_Curve) curveForSmooth,
1062                  _EdgesOnShape&     eos )
1063       : _anaCurve( curveForSmooth ), _eos( eos )
1064     {
1065     }
1066     bool Perform(_SolidData&                    data,
1067                  Handle(ShapeAnalysis_Surface)& surface,
1068                  const TopoDS_Face&             F,
1069                  SMESH_MesherHelper&            helper );
1070
1071     void prepare(_SolidData& data );
1072
1073     void findEdgesToSmooth();
1074
1075     bool isToSmooth( int iE );
1076
1077     bool smoothAnalyticEdge( _SolidData&                    data,
1078                              Handle(ShapeAnalysis_Surface)& surface,
1079                              const TopoDS_Face&             F,
1080                              SMESH_MesherHelper&            helper);
1081     bool smoothComplexEdge( _SolidData&                    data,
1082                             Handle(ShapeAnalysis_Surface)& surface,
1083                             const TopoDS_Face&             F,
1084                             SMESH_MesherHelper&            helper);
1085     gp_XYZ getNormalNormal( const gp_XYZ & normal,
1086                             const gp_XYZ&  edgeDir);
1087     _LayerEdge* getLEdgeOnV( bool is2nd )
1088     {
1089       return _eos._edges[ is2nd ? _eos._edges.size()-1 : 0 ]->_2neibors->_edges[ is2nd ];
1090     }
1091     bool isAnalytic() const { return !_anaCurve.IsNull(); }
1092
1093     void offPointsToPython() const; // debug
1094   };
1095   //--------------------------------------------------------------------------------
1096   /*!
1097    * \brief Class of temporary mesh face.
1098    * We can't use SMDS_FaceOfNodes since it's impossible to set it's ID which is
1099    * needed because SMESH_ElementSearcher internaly uses set of elements sorted by ID
1100    */
1101   struct _TmpMeshFace : public SMDS_MeshElement
1102   {
1103     vector<const SMDS_MeshNode* > _nn;
1104     _TmpMeshFace( const vector<const SMDS_MeshNode*>& nodes,
1105                   int id, int faceID=-1, int idInFace=-1):
1106       SMDS_MeshElement(id), _nn(nodes) { setShapeId(faceID); setIdInShape(idInFace); }
1107     virtual const SMDS_MeshNode* GetNode(const int ind) const { return _nn[ind]; }
1108     virtual SMDSAbs_ElementType  GetType() const              { return SMDSAbs_Face; }
1109     virtual vtkIdType GetVtkType() const                      { return -1; }
1110     virtual SMDSAbs_EntityType   GetEntityType() const        { return SMDSEntity_Last; }
1111     virtual SMDSAbs_GeometryType GetGeomType() const
1112     { return _nn.size() == 3 ? SMDSGeom_TRIANGLE : SMDSGeom_QUADRANGLE; }
1113     virtual SMDS_ElemIteratorPtr elementsIterator(SMDSAbs_ElementType) const
1114     { return SMDS_ElemIteratorPtr( new SMDS_NodeVectorElemIterator( _nn.begin(), _nn.end()));}
1115   };
1116   //--------------------------------------------------------------------------------
1117   /*!
1118    * \brief Class of temporary mesh face storing _LayerEdge it's based on
1119    */
1120   struct _TmpMeshFaceOnEdge : public _TmpMeshFace
1121   {
1122     _LayerEdge *_le1, *_le2;
1123     _TmpMeshFaceOnEdge( _LayerEdge* le1, _LayerEdge* le2, int ID ):
1124       _TmpMeshFace( vector<const SMDS_MeshNode*>(4), ID ), _le1(le1), _le2(le2)
1125     {
1126       _nn[0]=_le1->_nodes[0];
1127       _nn[1]=_le1->_nodes.back();
1128       _nn[2]=_le2->_nodes.back();
1129       _nn[3]=_le2->_nodes[0];
1130     }
1131     gp_XYZ GetDir() const // return average direction of _LayerEdge's, normal to EDGE
1132     {
1133       SMESH_TNodeXYZ p0s( _nn[0] );
1134       SMESH_TNodeXYZ p0t( _nn[1] );
1135       SMESH_TNodeXYZ p1t( _nn[2] );
1136       SMESH_TNodeXYZ p1s( _nn[3] );
1137       gp_XYZ  v0 = p0t - p0s;
1138       gp_XYZ  v1 = p1t - p1s;
1139       gp_XYZ v01 = p1s - p0s;
1140       gp_XYZ   n = ( v0 ^ v01 ) + ( v1 ^ v01 );
1141       gp_XYZ   d = v01 ^ n;
1142       d.Normalize();
1143       return d;
1144     }
1145     gp_XYZ GetDir(_LayerEdge* le1, _LayerEdge* le2) // return average direction of _LayerEdge's
1146     {
1147       _nn[0]=le1->_nodes[0];
1148       _nn[1]=le1->_nodes.back();
1149       _nn[2]=le2->_nodes.back();
1150       _nn[3]=le2->_nodes[0];
1151       return GetDir();
1152     }
1153   };
1154   //--------------------------------------------------------------------------------
1155   /*!
1156    * \brief Retriever of node coordinates either directly or from a surface by node UV.
1157    * \warning Location of a surface is ignored
1158    */
1159   struct _NodeCoordHelper
1160   {
1161     SMESH_MesherHelper&        _helper;
1162     const TopoDS_Face&         _face;
1163     Handle(Geom_Surface)       _surface;
1164     gp_XYZ (_NodeCoordHelper::* _fun)(const SMDS_MeshNode* n) const;
1165
1166     _NodeCoordHelper(const TopoDS_Face& F, SMESH_MesherHelper& helper, bool is2D)
1167       : _helper( helper ), _face( F )
1168     {
1169       if ( is2D )
1170       {
1171         TopLoc_Location loc;
1172         _surface = BRep_Tool::Surface( _face, loc );
1173       }
1174       if ( _surface.IsNull() )
1175         _fun = & _NodeCoordHelper::direct;
1176       else
1177         _fun = & _NodeCoordHelper::byUV;
1178     }
1179     gp_XYZ operator()(const SMDS_MeshNode* n) const { return (this->*_fun)( n ); }
1180
1181   private:
1182     gp_XYZ direct(const SMDS_MeshNode* n) const
1183     {
1184       return SMESH_TNodeXYZ( n );
1185     }
1186     gp_XYZ byUV  (const SMDS_MeshNode* n) const
1187     {
1188       gp_XY uv = _helper.GetNodeUV( _face, n );
1189       return _surface->Value( uv.X(), uv.Y() ).XYZ();
1190     }
1191   };
1192
1193   //================================================================================
1194   /*!
1195    * \brief Check angle between vectors 
1196    */
1197   //================================================================================
1198
1199   inline bool isLessAngle( const gp_Vec& v1, const gp_Vec& v2, const double cos )
1200   {
1201     double dot = v1 * v2; // cos * |v1| * |v2|
1202     double l1  = v1.SquareMagnitude();
1203     double l2  = v2.SquareMagnitude();
1204     return (( dot * cos >= 0 ) && 
1205             ( dot * dot ) / l1 / l2 >= ( cos * cos ));
1206   }
1207
1208 } // namespace VISCOUS_3D
1209
1210
1211
1212 //================================================================================
1213 // StdMeshers_ViscousLayers hypothesis
1214 //
1215 StdMeshers_ViscousLayers::StdMeshers_ViscousLayers(int hypId, int studyId, SMESH_Gen* gen)
1216   :SMESH_Hypothesis(hypId, studyId, gen),
1217    _isToIgnoreShapes(1), _nbLayers(1), _thickness(1), _stretchFactor(1),
1218    _method( SURF_OFFSET_SMOOTH )
1219 {
1220   _name = StdMeshers_ViscousLayers::GetHypType();
1221   _param_algo_dim = -3; // auxiliary hyp used by 3D algos
1222 } // --------------------------------------------------------------------------------
1223 void StdMeshers_ViscousLayers::SetBndShapes(const std::vector<int>& faceIds, bool toIgnore)
1224 {
1225   if ( faceIds != _shapeIds )
1226     _shapeIds = faceIds, NotifySubMeshesHypothesisModification();
1227   if ( _isToIgnoreShapes != toIgnore )
1228     _isToIgnoreShapes = toIgnore, NotifySubMeshesHypothesisModification();
1229 } // --------------------------------------------------------------------------------
1230 void StdMeshers_ViscousLayers::SetTotalThickness(double thickness)
1231 {
1232   if ( thickness != _thickness )
1233     _thickness = thickness, NotifySubMeshesHypothesisModification();
1234 } // --------------------------------------------------------------------------------
1235 void StdMeshers_ViscousLayers::SetNumberLayers(int nb)
1236 {
1237   if ( _nbLayers != nb )
1238     _nbLayers = nb, NotifySubMeshesHypothesisModification();
1239 } // --------------------------------------------------------------------------------
1240 void StdMeshers_ViscousLayers::SetStretchFactor(double factor)
1241 {
1242   if ( _stretchFactor != factor )
1243     _stretchFactor = factor, NotifySubMeshesHypothesisModification();
1244 } // --------------------------------------------------------------------------------
1245 void StdMeshers_ViscousLayers::SetMethod( ExtrusionMethod method )
1246 {
1247   if ( _method != method )
1248     _method = method, NotifySubMeshesHypothesisModification();
1249 } // --------------------------------------------------------------------------------
1250 SMESH_ProxyMesh::Ptr
1251 StdMeshers_ViscousLayers::Compute(SMESH_Mesh&         theMesh,
1252                                   const TopoDS_Shape& theShape,
1253                                   const bool          toMakeN2NMap) const
1254 {
1255   using namespace VISCOUS_3D;
1256   _ViscousBuilder builder;
1257   SMESH_ComputeErrorPtr err = builder.Compute( theMesh, theShape );
1258   if ( err && !err->IsOK() )
1259     return SMESH_ProxyMesh::Ptr();
1260
1261   vector<SMESH_ProxyMesh::Ptr> components;
1262   TopExp_Explorer exp( theShape, TopAbs_SOLID );
1263   for ( ; exp.More(); exp.Next() )
1264   {
1265     if ( _MeshOfSolid* pm =
1266          _ViscousListener::GetSolidMesh( &theMesh, exp.Current(), /*toCreate=*/false))
1267     {
1268       if ( toMakeN2NMap && !pm->_n2nMapComputed )
1269         if ( !builder.MakeN2NMap( pm ))
1270           return SMESH_ProxyMesh::Ptr();
1271       components.push_back( SMESH_ProxyMesh::Ptr( pm ));
1272       pm->myIsDeletable = false; // it will de deleted by boost::shared_ptr
1273
1274       if ( pm->_warning && !pm->_warning->IsOK() )
1275       {
1276         SMESH_subMesh* sm = theMesh.GetSubMesh( exp.Current() );
1277         SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1278         if ( !smError || smError->IsOK() )
1279           smError = pm->_warning;
1280       }
1281     }
1282     _ViscousListener::RemoveSolidMesh ( &theMesh, exp.Current() );
1283   }
1284   switch ( components.size() )
1285   {
1286   case 0: break;
1287
1288   case 1: return components[0];
1289
1290   default: return SMESH_ProxyMesh::Ptr( new SMESH_ProxyMesh( components ));
1291   }
1292   return SMESH_ProxyMesh::Ptr();
1293 } // --------------------------------------------------------------------------------
1294 std::ostream & StdMeshers_ViscousLayers::SaveTo(std::ostream & save)
1295 {
1296   save << " " << _nbLayers
1297        << " " << _thickness
1298        << " " << _stretchFactor
1299        << " " << _shapeIds.size();
1300   for ( size_t i = 0; i < _shapeIds.size(); ++i )
1301     save << " " << _shapeIds[i];
1302   save << " " << !_isToIgnoreShapes; // negate to keep the behavior in old studies.
1303   save << " " << _method;
1304   return save;
1305 } // --------------------------------------------------------------------------------
1306 std::istream & StdMeshers_ViscousLayers::LoadFrom(std::istream & load)
1307 {
1308   int nbFaces, faceID, shapeToTreat, method;
1309   load >> _nbLayers >> _thickness >> _stretchFactor >> nbFaces;
1310   while ( (int) _shapeIds.size() < nbFaces && load >> faceID )
1311     _shapeIds.push_back( faceID );
1312   if ( load >> shapeToTreat ) {
1313     _isToIgnoreShapes = !shapeToTreat;
1314     if ( load >> method )
1315       _method = (ExtrusionMethod) method;
1316   }
1317   else {
1318     _isToIgnoreShapes = true; // old behavior
1319   }
1320   return load;
1321 } // --------------------------------------------------------------------------------
1322 bool StdMeshers_ViscousLayers::SetParametersByMesh(const SMESH_Mesh*   theMesh,
1323                                                    const TopoDS_Shape& theShape)
1324 {
1325   // TODO
1326   return false;
1327 } // --------------------------------------------------------------------------------
1328 SMESH_ComputeErrorPtr
1329 StdMeshers_ViscousLayers::CheckHypothesis(SMESH_Mesh&                          theMesh,
1330                                           const TopoDS_Shape&                  theShape,
1331                                           SMESH_Hypothesis::Hypothesis_Status& theStatus)
1332 {
1333   VISCOUS_3D::_ViscousBuilder builder;
1334   SMESH_ComputeErrorPtr err = builder.CheckHypotheses( theMesh, theShape );
1335   if ( err && !err->IsOK() )
1336     theStatus = SMESH_Hypothesis::HYP_INCOMPAT_HYPS;
1337   else
1338     theStatus = SMESH_Hypothesis::HYP_OK;
1339
1340   return err;
1341 }
1342 // --------------------------------------------------------------------------------
1343 bool StdMeshers_ViscousLayers::IsShapeWithLayers(int shapeIndex) const
1344 {
1345   bool isIn =
1346     ( std::find( _shapeIds.begin(), _shapeIds.end(), shapeIndex ) != _shapeIds.end() );
1347   return IsToIgnoreShapes() ? !isIn : isIn;
1348 }
1349 // END StdMeshers_ViscousLayers hypothesis
1350 //================================================================================
1351
1352 namespace VISCOUS_3D
1353 {
1354   gp_XYZ getEdgeDir( const TopoDS_Edge& E, const TopoDS_Vertex& fromV )
1355   {
1356     gp_Vec dir;
1357     double f,l;
1358     Handle(Geom_Curve) c = BRep_Tool::Curve( E, f, l );
1359     if ( c.IsNull() ) return gp_XYZ( Precision::Infinite(), 1e100, 1e100 );
1360     gp_Pnt p = BRep_Tool::Pnt( fromV );
1361     double distF = p.SquareDistance( c->Value( f ));
1362     double distL = p.SquareDistance( c->Value( l ));
1363     c->D1(( distF < distL ? f : l), p, dir );
1364     if ( distL < distF ) dir.Reverse();
1365     return dir.XYZ();
1366   }
1367   //--------------------------------------------------------------------------------
1368   gp_XYZ getEdgeDir( const TopoDS_Edge& E, const SMDS_MeshNode* atNode,
1369                      SMESH_MesherHelper& helper)
1370   {
1371     gp_Vec dir;
1372     double f,l; gp_Pnt p;
1373     Handle(Geom_Curve) c = BRep_Tool::Curve( E, f, l );
1374     if ( c.IsNull() ) return gp_XYZ( Precision::Infinite(), 1e100, 1e100 );
1375     double u = helper.GetNodeU( E, atNode );
1376     c->D1( u, p, dir );
1377     return dir.XYZ();
1378   }
1379   //--------------------------------------------------------------------------------
1380   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Vertex& fromV,
1381                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper, bool& ok,
1382                      double* cosin=0);
1383   //--------------------------------------------------------------------------------
1384   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Edge& fromE,
1385                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper, bool& ok)
1386   {
1387     double f,l;
1388     Handle(Geom_Curve) c = BRep_Tool::Curve( fromE, f, l );
1389     if ( c.IsNull() )
1390     {
1391       TopoDS_Vertex v = helper.IthVertex( 0, fromE );
1392       return getFaceDir( F, v, node, helper, ok );
1393     }
1394     gp_XY uv = helper.GetNodeUV( F, node, 0, &ok );
1395     Handle(Geom_Surface) surface = BRep_Tool::Surface( F );
1396     gp_Pnt p; gp_Vec du, dv, norm;
1397     surface->D1( uv.X(),uv.Y(), p, du,dv );
1398     norm = du ^ dv;
1399
1400     double u = helper.GetNodeU( fromE, node, 0, &ok );
1401     c->D1( u, p, du );
1402     TopAbs_Orientation o = helper.GetSubShapeOri( F.Oriented(TopAbs_FORWARD), fromE);
1403     if ( o == TopAbs_REVERSED )
1404       du.Reverse();
1405
1406     gp_Vec dir = norm ^ du;
1407
1408     if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX &&
1409          helper.IsClosedEdge( fromE ))
1410     {
1411       if ( fabs(u-f) < fabs(u-l)) c->D1( l, p, dv );
1412       else                        c->D1( f, p, dv );
1413       if ( o == TopAbs_REVERSED )
1414         dv.Reverse();
1415       gp_Vec dir2 = norm ^ dv;
1416       dir = dir.Normalized() + dir2.Normalized();
1417     }
1418     return dir.XYZ();
1419   }
1420   //--------------------------------------------------------------------------------
1421   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Vertex& fromV,
1422                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper,
1423                      bool& ok, double* cosin)
1424   {
1425     TopoDS_Face faceFrw = F;
1426     faceFrw.Orientation( TopAbs_FORWARD );
1427     //double f,l; TopLoc_Location loc;
1428     TopoDS_Edge edges[2]; // sharing a vertex
1429     size_t nbEdges = 0;
1430     {
1431       TopoDS_Vertex VV[2];
1432       TopExp_Explorer exp( faceFrw, TopAbs_EDGE );
1433       for ( ; exp.More() && nbEdges < 2; exp.Next() )
1434       {
1435         const TopoDS_Edge& e = TopoDS::Edge( exp.Current() );
1436         if ( SMESH_Algo::isDegenerated( e )) continue;
1437         TopExp::Vertices( e, VV[0], VV[1], /*CumOri=*/true );
1438         if ( VV[1].IsSame( fromV )) {
1439           nbEdges += edges[ 0 ].IsNull();
1440           edges[ 0 ] = e;
1441         }
1442         else if ( VV[0].IsSame( fromV )) {
1443           nbEdges += edges[ 1 ].IsNull();
1444           edges[ 1 ] = e;
1445         }
1446       }
1447     }
1448     gp_XYZ dir(0,0,0), edgeDir[2];
1449     if ( nbEdges == 2 )
1450     {
1451       // get dirs of edges going fromV
1452       ok = true;
1453       for ( size_t i = 0; i < nbEdges && ok; ++i )
1454       {
1455         edgeDir[i] = getEdgeDir( edges[i], fromV );
1456         double size2 = edgeDir[i].SquareModulus();
1457         if (( ok = size2 > numeric_limits<double>::min() ))
1458           edgeDir[i] /= sqrt( size2 );
1459       }
1460       if ( !ok ) return dir;
1461
1462       // get angle between the 2 edges
1463       gp_Vec faceNormal;
1464       double angle = helper.GetAngle( edges[0], edges[1], faceFrw, fromV, &faceNormal );
1465       if ( Abs( angle ) < 5 * M_PI/180 )
1466       {
1467         dir = ( faceNormal.XYZ() ^ edgeDir[0].Reversed()) + ( faceNormal.XYZ() ^ edgeDir[1] );
1468       }
1469       else
1470       {
1471         dir = edgeDir[0] + edgeDir[1];
1472         if ( angle < 0 )
1473           dir.Reverse();
1474       }
1475       if ( cosin ) {
1476         double angle = gp_Vec( edgeDir[0] ).Angle( dir );
1477         *cosin = Cos( angle );
1478       }
1479     }
1480     else if ( nbEdges == 1 )
1481     {
1482       dir = getFaceDir( faceFrw, edges[ edges[0].IsNull() ], node, helper, ok );
1483       if ( cosin ) *cosin = 1.;
1484     }
1485     else
1486     {
1487       ok = false;
1488     }
1489
1490     return dir;
1491   }
1492
1493   //================================================================================
1494   /*!
1495    * \brief Finds concave VERTEXes of a FACE
1496    */
1497   //================================================================================
1498
1499   bool getConcaveVertices( const TopoDS_Face&  F,
1500                            SMESH_MesherHelper& helper,
1501                            set< TGeomID >*     vertices = 0)
1502   {
1503     // check angles at VERTEXes
1504     TError error;
1505     TSideVector wires = StdMeshers_FaceSide::GetFaceWires( F, *helper.GetMesh(), 0, error );
1506     for ( size_t iW = 0; iW < wires.size(); ++iW )
1507     {
1508       const int nbEdges = wires[iW]->NbEdges();
1509       if ( nbEdges < 2 && SMESH_Algo::isDegenerated( wires[iW]->Edge(0)))
1510         continue;
1511       for ( int iE1 = 0; iE1 < nbEdges; ++iE1 )
1512       {
1513         if ( SMESH_Algo::isDegenerated( wires[iW]->Edge( iE1 ))) continue;
1514         int iE2 = ( iE1 + 1 ) % nbEdges;
1515         while ( SMESH_Algo::isDegenerated( wires[iW]->Edge( iE2 )))
1516           iE2 = ( iE2 + 1 ) % nbEdges;
1517         TopoDS_Vertex V = wires[iW]->FirstVertex( iE2 );
1518         double angle = helper.GetAngle( wires[iW]->Edge( iE1 ),
1519                                         wires[iW]->Edge( iE2 ), F, V );
1520         if ( angle < -5. * M_PI / 180. )
1521         {
1522           if ( !vertices )
1523             return true;
1524           vertices->insert( helper.GetMeshDS()->ShapeToIndex( V ));
1525         }
1526       }
1527     }
1528     return vertices ? !vertices->empty() : false;
1529   }
1530
1531   //================================================================================
1532   /*!
1533    * \brief Returns true if a FACE is bound by a concave EDGE
1534    */
1535   //================================================================================
1536
1537   bool isConcave( const TopoDS_Face&  F,
1538                   SMESH_MesherHelper& helper,
1539                   set< TGeomID >*     vertices = 0 )
1540   {
1541     bool isConcv = false;
1542     // if ( helper.Count( F, TopAbs_WIRE, /*useMap=*/false) > 1 )
1543     //   return true;
1544     gp_Vec2d drv1, drv2;
1545     gp_Pnt2d p;
1546     TopExp_Explorer eExp( F.Oriented( TopAbs_FORWARD ), TopAbs_EDGE );
1547     for ( ; eExp.More(); eExp.Next() )
1548     {
1549       const TopoDS_Edge& E = TopoDS::Edge( eExp.Current() );
1550       if ( SMESH_Algo::isDegenerated( E )) continue;
1551       // check if 2D curve is concave
1552       BRepAdaptor_Curve2d curve( E, F );
1553       const int nbIntervals = curve.NbIntervals( GeomAbs_C2 );
1554       TColStd_Array1OfReal intervals(1, nbIntervals + 1 );
1555       curve.Intervals( intervals, GeomAbs_C2 );
1556       bool isConvex = true;
1557       for ( int i = 1; i <= nbIntervals && isConvex; ++i )
1558       {
1559         double u1 = intervals( i );
1560         double u2 = intervals( i+1 );
1561         curve.D2( 0.5*( u1+u2 ), p, drv1, drv2 );
1562         double cross = drv1 ^ drv2;
1563         if ( E.Orientation() == TopAbs_REVERSED )
1564           cross = -cross;
1565         isConvex = ( cross > -1e-9 ); // 0.1 );
1566       }
1567       if ( !isConvex )
1568       {
1569         //cout << "Concave FACE " << helper.GetMeshDS()->ShapeToIndex( F ) << endl;
1570         isConcv = true;
1571         if ( vertices )
1572           break;
1573         else
1574           return true;
1575       }
1576     }
1577
1578     // check angles at VERTEXes
1579     if ( getConcaveVertices( F, helper, vertices ))
1580       isConcv = true;
1581
1582     return isConcv;
1583   }
1584
1585   //================================================================================
1586   /*!
1587    * \brief Computes mimimal distance of face in-FACE nodes from an EDGE
1588    *  \param [in] face - the mesh face to treat
1589    *  \param [in] nodeOnEdge - a node on the EDGE
1590    *  \param [out] faceSize - the computed distance
1591    *  \return bool - true if faceSize computed
1592    */
1593   //================================================================================
1594
1595   bool getDistFromEdge( const SMDS_MeshElement* face,
1596                         const SMDS_MeshNode*    nodeOnEdge,
1597                         double &                faceSize )
1598   {
1599     faceSize = Precision::Infinite();
1600     bool done = false;
1601
1602     int nbN  = face->NbCornerNodes();
1603     int iOnE = face->GetNodeIndex( nodeOnEdge );
1604     int iNext[2] = { SMESH_MesherHelper::WrapIndex( iOnE+1, nbN ),
1605                      SMESH_MesherHelper::WrapIndex( iOnE-1, nbN ) };
1606     const SMDS_MeshNode* nNext[2] = { face->GetNode( iNext[0] ),
1607                                       face->GetNode( iNext[1] ) };
1608     gp_XYZ segVec, segEnd = SMESH_TNodeXYZ( nodeOnEdge ); // segment on EDGE
1609     double segLen = -1.;
1610     // look for two neighbor not in-FACE nodes of face
1611     for ( int i = 0; i < 2; ++i )
1612     {
1613       if (( nNext[i]->GetPosition()->GetDim() != 2 ) &&
1614           ( nodeOnEdge->GetPosition()->GetDim() == 0 || nNext[i]->GetID() < nodeOnEdge->GetID() ))
1615       {
1616         // look for an in-FACE node
1617         for ( int iN = 0; iN < nbN; ++iN )
1618         {
1619           if ( iN == iOnE || iN == iNext[i] )
1620             continue;
1621           SMESH_TNodeXYZ pInFace = face->GetNode( iN );
1622           gp_XYZ v = pInFace - segEnd;
1623           if ( segLen < 0 )
1624           {
1625             segVec = SMESH_TNodeXYZ( nNext[i] ) - segEnd;
1626             segLen = segVec.Modulus();
1627           }
1628           double distToSeg = v.Crossed( segVec ).Modulus() / segLen;
1629           faceSize = Min( faceSize, distToSeg );
1630           done = true;
1631         }
1632         segLen = -1;
1633       }
1634     }
1635     return done;
1636   }
1637   //================================================================================
1638   /*!
1639    * \brief Return direction of axis or revolution of a surface
1640    */
1641   //================================================================================
1642
1643   bool getRovolutionAxis( const Adaptor3d_Surface& surface,
1644                           gp_Dir &                 axis )
1645   {
1646     switch ( surface.GetType() ) {
1647     case GeomAbs_Cone:
1648     {
1649       gp_Cone cone = surface.Cone();
1650       axis = cone.Axis().Direction();
1651       break;
1652     }
1653     case GeomAbs_Sphere:
1654     {
1655       gp_Sphere sphere = surface.Sphere();
1656       axis = sphere.Position().Direction();
1657       break;
1658     }
1659     case GeomAbs_SurfaceOfRevolution:
1660     {
1661       axis = surface.AxeOfRevolution().Direction();
1662       break;
1663     }
1664     //case GeomAbs_SurfaceOfExtrusion:
1665     case GeomAbs_OffsetSurface:
1666     {
1667       Handle(Adaptor3d_HSurface) base = surface.BasisSurface();
1668       return getRovolutionAxis( base->Surface(), axis );
1669     }
1670     default: return false;
1671     }
1672     return true;
1673   }
1674
1675   //--------------------------------------------------------------------------------
1676   // DEBUG. Dump intermediate node positions into a python script
1677   // HOWTO use: run python commands written in a console to see
1678   //  construction steps of viscous layers
1679 #ifdef __myDEBUG
1680   ostream* py;
1681   int      theNbPyFunc;
1682   struct PyDump
1683   {
1684     PyDump(SMESH_Mesh& m) {
1685       int tag = 3 + m.GetId();
1686       const char* fname = "/tmp/viscous.py";
1687       cout << "execfile('"<<fname<<"')"<<endl;
1688       py = _pyStream = new ofstream(fname);
1689       *py << "import SMESH" << endl
1690           << "from salome.smesh import smeshBuilder" << endl
1691           << "smesh  = smeshBuilder.New(salome.myStudy)" << endl
1692           << "meshSO = smesh.GetCurrentStudy().FindObjectID('0:1:2:" << tag <<"')" << endl
1693           << "mesh   = smesh.Mesh( meshSO.GetObject() )"<<endl;
1694       theNbPyFunc = 0;
1695     }
1696     void Finish() {
1697       if (py) {
1698         *py << "mesh.GroupOnFilter(SMESH.VOLUME,'Viscous Prisms',"
1699           "smesh.GetFilter(SMESH.VOLUME,SMESH.FT_ElemGeomType,'=',SMESH.Geom_PENTA))"<<endl;
1700         *py << "mesh.GroupOnFilter(SMESH.VOLUME,'Neg Volumes',"
1701           "smesh.GetFilter(SMESH.VOLUME,SMESH.FT_Volume3D,'<',0))"<<endl;
1702       }
1703       delete py; py=0;
1704     }
1705     ~PyDump() { Finish(); cout << "NB FUNCTIONS: " << theNbPyFunc << endl; }
1706     struct MyStream : public ostream
1707     {
1708       template <class T> ostream & operator<<( const T &anything ) { return *this ; }
1709     };
1710     void Pause() { py = &_mystream; }
1711     void Resume() { py = _pyStream; }
1712     MyStream _mystream;
1713     ostream* _pyStream;
1714   };
1715 #define dumpFunction(f) { _dumpFunction(f, __LINE__);}
1716 #define dumpMove(n)     { _dumpMove(n, __LINE__);}
1717 #define dumpMoveComm(n,txt) { _dumpMove(n, __LINE__, txt);}
1718 #define dumpCmd(txt)    { _dumpCmd(txt, __LINE__);}
1719   void _dumpFunction(const string& fun, int ln)
1720   { if (py) *py<< "def "<<fun<<"(): # "<< ln <<endl; cout<<fun<<"()"<<endl; ++theNbPyFunc; }
1721   void _dumpMove(const SMDS_MeshNode* n, int ln, const char* txt="")
1722   { if (py) *py<< "  mesh.MoveNode( "<<n->GetID()<< ", "<< n->X()
1723                << ", "<<n->Y()<<", "<< n->Z()<< ")\t\t # "<< ln <<" "<< txt << endl; }
1724   void _dumpCmd(const string& txt, int ln)
1725   { if (py) *py<< "  "<<txt<<" # "<< ln <<endl; }
1726   void dumpFunctionEnd()
1727   { if (py) *py<< "  return"<< endl; }
1728   void dumpChangeNodes( const SMDS_MeshElement* f )
1729   { if (py) { *py<< "  mesh.ChangeElemNodes( " << f->GetID()<<", [";
1730       for ( int i=1; i < f->NbNodes(); ++i ) *py << f->GetNode(i-1)->GetID()<<", ";
1731       *py << f->GetNode( f->NbNodes()-1 )->GetID() << " ])"<< endl; }}
1732 #define debugMsg( txt ) { cout << "# "<< txt << " (line: " << __LINE__ << ")" << endl; }
1733
1734 #else
1735
1736   struct PyDump { PyDump(SMESH_Mesh&) {} void Finish() {} void Pause() {} void Resume() {} };
1737 #define dumpFunction(f) f
1738 #define dumpMove(n)
1739 #define dumpMoveComm(n,txt)
1740 #define dumpCmd(txt)
1741 #define dumpFunctionEnd()
1742 #define dumpChangeNodes(f) { if(f) {} } // prevent "unused variable 'f'" warning
1743 #define debugMsg( txt ) {}
1744
1745 #endif
1746 }
1747
1748 using namespace VISCOUS_3D;
1749
1750 //================================================================================
1751 /*!
1752  * \brief Constructor of _ViscousBuilder
1753  */
1754 //================================================================================
1755
1756 _ViscousBuilder::_ViscousBuilder()
1757 {
1758   _error = SMESH_ComputeError::New(COMPERR_OK);
1759   _tmpFaceID = 0;
1760 }
1761
1762 //================================================================================
1763 /*!
1764  * \brief Stores error description and returns false
1765  */
1766 //================================================================================
1767
1768 bool _ViscousBuilder::error(const string& text, int solidId )
1769 {
1770   const string prefix = string("Viscous layers builder: ");
1771   _error->myName    = COMPERR_ALGO_FAILED;
1772   _error->myComment = prefix + text;
1773   if ( _mesh )
1774   {
1775     SMESH_subMesh* sm = _mesh->GetSubMeshContaining( solidId );
1776     if ( !sm && !_sdVec.empty() )
1777       sm = _mesh->GetSubMeshContaining( solidId = _sdVec[0]._index );
1778     if ( sm && sm->GetSubShape().ShapeType() == TopAbs_SOLID )
1779     {
1780       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1781       if ( smError && smError->myAlgo )
1782         _error->myAlgo = smError->myAlgo;
1783       smError = _error;
1784       sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
1785     }
1786     // set KO to all solids
1787     for ( size_t i = 0; i < _sdVec.size(); ++i )
1788     {
1789       if ( _sdVec[i]._index == solidId )
1790         continue;
1791       sm = _mesh->GetSubMesh( _sdVec[i]._solid );
1792       if ( !sm->IsEmpty() )
1793         continue;
1794       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1795       if ( !smError || smError->IsOK() )
1796       {
1797         smError = SMESH_ComputeError::New( COMPERR_ALGO_FAILED, prefix + "failed");
1798         sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
1799       }
1800     }
1801   }
1802   makeGroupOfLE(); // debug
1803
1804   return false;
1805 }
1806
1807 //================================================================================
1808 /*!
1809  * \brief At study restoration, restore event listeners used to clear an inferior
1810  *  dim sub-mesh modified by viscous layers
1811  */
1812 //================================================================================
1813
1814 void _ViscousBuilder::RestoreListeners()
1815 {
1816   // TODO
1817 }
1818
1819 //================================================================================
1820 /*!
1821  * \brief computes SMESH_ProxyMesh::SubMesh::_n2n
1822  */
1823 //================================================================================
1824
1825 bool _ViscousBuilder::MakeN2NMap( _MeshOfSolid* pm )
1826 {
1827   SMESH_subMesh* solidSM = pm->mySubMeshes.front();
1828   TopExp_Explorer fExp( solidSM->GetSubShape(), TopAbs_FACE );
1829   for ( ; fExp.More(); fExp.Next() )
1830   {
1831     SMESHDS_SubMesh* srcSmDS = pm->GetMeshDS()->MeshElements( fExp.Current() );
1832     const SMESH_ProxyMesh::SubMesh* prxSmDS = pm->GetProxySubMesh( fExp.Current() );
1833
1834     if ( !srcSmDS || !prxSmDS || !srcSmDS->NbElements() || !prxSmDS->NbElements() )
1835       continue;
1836     if ( srcSmDS->GetElements()->next() == prxSmDS->GetElements()->next())
1837       continue;
1838
1839     if ( srcSmDS->NbElements() != prxSmDS->NbElements() )
1840       return error( "Different nb elements in a source and a proxy sub-mesh", solidSM->GetId());
1841
1842     SMDS_ElemIteratorPtr srcIt = srcSmDS->GetElements();
1843     SMDS_ElemIteratorPtr prxIt = prxSmDS->GetElements();
1844     while( prxIt->more() )
1845     {
1846       const SMDS_MeshElement* fSrc = srcIt->next();
1847       const SMDS_MeshElement* fPrx = prxIt->next();
1848       if ( fSrc->NbNodes() != fPrx->NbNodes())
1849         return error( "Different elements in a source and a proxy sub-mesh", solidSM->GetId());
1850       for ( int i = 0 ; i < fPrx->NbNodes(); ++i )
1851         pm->setNode2Node( fSrc->GetNode(i), fPrx->GetNode(i), prxSmDS );
1852     }
1853   }
1854   pm->_n2nMapComputed = true;
1855   return true;
1856 }
1857
1858 //================================================================================
1859 /*!
1860  * \brief Does its job
1861  */
1862 //================================================================================
1863
1864 SMESH_ComputeErrorPtr _ViscousBuilder::Compute(SMESH_Mesh&         theMesh,
1865                                                const TopoDS_Shape& theShape)
1866 {
1867   _mesh = & theMesh;
1868
1869   // check if proxy mesh already computed
1870   TopExp_Explorer exp( theShape, TopAbs_SOLID );
1871   if ( !exp.More() )
1872     return error("No SOLID's in theShape"), _error;
1873
1874   if ( _ViscousListener::GetSolidMesh( _mesh, exp.Current(), /*toCreate=*/false))
1875     return SMESH_ComputeErrorPtr(); // everything already computed
1876
1877   PyDump debugDump( theMesh );
1878   _pyDump = &debugDump;
1879
1880   // TODO: ignore already computed SOLIDs 
1881   if ( !findSolidsWithLayers())
1882     return _error;
1883
1884   if ( !findFacesWithLayers() )
1885     return _error;
1886
1887   for ( size_t i = 0; i < _sdVec.size(); ++i )
1888   {
1889     size_t iSD = 0;
1890     for ( iSD = 0; iSD < _sdVec.size(); ++iSD ) // find next SOLID to compute
1891       if ( _sdVec[iSD]._before.IsEmpty() &&
1892            !_sdVec[iSD]._solid.IsNull() &&
1893            _sdVec[iSD]._n2eMap.empty() )
1894         break;
1895
1896     if ( ! makeLayer(_sdVec[iSD]) )   // create _LayerEdge's
1897       return _error;
1898
1899     if ( _sdVec[iSD]._n2eMap.size() == 0 ) // no layers in a SOLID
1900     {
1901       _sdVec[iSD]._solid.Nullify();
1902       continue;
1903     }
1904
1905     if ( ! inflate(_sdVec[iSD]) )     // increase length of _LayerEdge's
1906       return _error;
1907
1908     if ( ! refine(_sdVec[iSD]) )      // create nodes and prisms
1909       return _error;
1910
1911     if ( ! shrink(_sdVec[iSD]) )      // shrink 2D mesh on FACEs w/o layer
1912       return _error;
1913
1914     addBoundaryElements(_sdVec[iSD]); // create quadrangles on prism bare sides
1915
1916     const TopoDS_Shape& solid = _sdVec[iSD]._solid;
1917     for ( iSD = 0; iSD < _sdVec.size(); ++iSD )
1918       _sdVec[iSD]._before.Remove( solid );
1919   }
1920
1921   makeGroupOfLE(); // debug
1922   debugDump.Finish();
1923
1924   return _error;
1925 }
1926
1927 //================================================================================
1928 /*!
1929  * \brief Check validity of hypotheses
1930  */
1931 //================================================================================
1932
1933 SMESH_ComputeErrorPtr _ViscousBuilder::CheckHypotheses( SMESH_Mesh&         mesh,
1934                                                         const TopoDS_Shape& shape )
1935 {
1936   _mesh = & mesh;
1937
1938   if ( _ViscousListener::GetSolidMesh( _mesh, shape, /*toCreate=*/false))
1939     return SMESH_ComputeErrorPtr(); // everything already computed
1940
1941
1942   findSolidsWithLayers();
1943   bool ok = findFacesWithLayers( true );
1944
1945   // remove _MeshOfSolid's of _SolidData's
1946   for ( size_t i = 0; i < _sdVec.size(); ++i )
1947     _ViscousListener::RemoveSolidMesh( _mesh, _sdVec[i]._solid );
1948
1949   if ( !ok )
1950     return _error;
1951
1952   return SMESH_ComputeErrorPtr();
1953 }
1954
1955 //================================================================================
1956 /*!
1957  * \brief Finds SOLIDs to compute using viscous layers. Fills _sdVec
1958  */
1959 //================================================================================
1960
1961 bool _ViscousBuilder::findSolidsWithLayers()
1962 {
1963   // get all solids
1964   TopTools_IndexedMapOfShape allSolids;
1965   TopExp::MapShapes( _mesh->GetShapeToMesh(), TopAbs_SOLID, allSolids );
1966   _sdVec.reserve( allSolids.Extent());
1967
1968   SMESH_HypoFilter filter;
1969   for ( int i = 1; i <= allSolids.Extent(); ++i )
1970   {
1971     // find StdMeshers_ViscousLayers hyp assigned to the i-th solid
1972     SMESH_subMesh* sm = _mesh->GetSubMesh( allSolids(i) );
1973     if ( sm->GetSubMeshDS() && sm->GetSubMeshDS()->NbElements() > 0 )
1974       continue; // solid is already meshed
1975     SMESH_Algo* algo = sm->GetAlgo();
1976     if ( !algo ) continue;
1977     // TODO: check if algo is hidden
1978     const list <const SMESHDS_Hypothesis *> & allHyps =
1979       algo->GetUsedHypothesis(*_mesh, allSolids(i), /*ignoreAuxiliary=*/false);
1980     _SolidData* soData = 0;
1981     list< const SMESHDS_Hypothesis *>::const_iterator hyp = allHyps.begin();
1982     const StdMeshers_ViscousLayers* viscHyp = 0;
1983     for ( ; hyp != allHyps.end(); ++hyp )
1984       if (( viscHyp = dynamic_cast<const StdMeshers_ViscousLayers*>( *hyp )))
1985       {
1986         TopoDS_Shape hypShape;
1987         filter.Init( filter.Is( viscHyp ));
1988         _mesh->GetHypothesis( allSolids(i), filter, true, &hypShape );
1989
1990         if ( !soData )
1991         {
1992           _MeshOfSolid* proxyMesh = _ViscousListener::GetSolidMesh( _mesh,
1993                                                                     allSolids(i),
1994                                                                     /*toCreate=*/true);
1995           _sdVec.push_back( _SolidData( allSolids(i), proxyMesh ));
1996           soData = & _sdVec.back();
1997           soData->_index = getMeshDS()->ShapeToIndex( allSolids(i));
1998           soData->_helper = new SMESH_MesherHelper( *_mesh );
1999           soData->_helper->SetSubShape( allSolids(i) );
2000           _solids.Add( allSolids(i) );
2001         }
2002         soData->_hyps.push_back( viscHyp );
2003         soData->_hypShapes.push_back( hypShape );
2004       }
2005   }
2006   if ( _sdVec.empty() )
2007     return error
2008       ( SMESH_Comment(StdMeshers_ViscousLayers::GetHypType()) << " hypothesis not found",0);
2009
2010   return true;
2011 }
2012
2013 //================================================================================
2014 /*!
2015  * \brief Set a _SolidData to be computed before another
2016  */
2017 //================================================================================
2018
2019 bool _ViscousBuilder::setBefore( _SolidData& solidBefore, _SolidData& solidAfter )
2020 {
2021   // check possibility to set this order; get all solids before solidBefore
2022   TopTools_IndexedMapOfShape allSolidsBefore;
2023   allSolidsBefore.Add( solidBefore._solid );
2024   for ( int i = 1; i <= allSolidsBefore.Extent(); ++i )
2025   {
2026     int iSD = _solids.FindIndex( allSolidsBefore(i) );
2027     if ( iSD )
2028     {
2029       TopTools_MapIteratorOfMapOfShape soIt( _sdVec[ iSD-1 ]._before );
2030       for ( ; soIt.More(); soIt.Next() )
2031         allSolidsBefore.Add( soIt.Value() );
2032     }
2033   }
2034   if ( allSolidsBefore.Contains( solidAfter._solid ))
2035     return false;
2036
2037   for ( int i = 1; i <= allSolidsBefore.Extent(); ++i )
2038     solidAfter._before.Add( allSolidsBefore(i) );
2039
2040   return true;
2041 }
2042
2043 //================================================================================
2044 /*!
2045  * \brief
2046  */
2047 //================================================================================
2048
2049 bool _ViscousBuilder::findFacesWithLayers(const bool onlyWith)
2050 {
2051   SMESH_MesherHelper helper( *_mesh );
2052   TopExp_Explorer exp;
2053
2054   // collect all faces-to-ignore defined by hyp
2055   for ( size_t i = 0; i < _sdVec.size(); ++i )
2056   {
2057     // get faces-to-ignore defined by each hyp
2058     typedef const StdMeshers_ViscousLayers* THyp;
2059     typedef std::pair< set<TGeomID>, THyp > TFacesOfHyp;
2060     list< TFacesOfHyp > ignoreFacesOfHyps;
2061     list< THyp >::iterator              hyp = _sdVec[i]._hyps.begin();
2062     list< TopoDS_Shape >::iterator hypShape = _sdVec[i]._hypShapes.begin();
2063     for ( ; hyp != _sdVec[i]._hyps.end(); ++hyp, ++hypShape )
2064     {
2065       ignoreFacesOfHyps.push_back( TFacesOfHyp( set<TGeomID>(), *hyp ));
2066       getIgnoreFaces( _sdVec[i]._solid, *hyp, *hypShape, ignoreFacesOfHyps.back().first );
2067     }
2068
2069     // fill _SolidData::_face2hyp and check compatibility of hypotheses
2070     const int nbHyps = _sdVec[i]._hyps.size();
2071     if ( nbHyps > 1 )
2072     {
2073       // check if two hypotheses define different parameters for the same FACE
2074       list< TFacesOfHyp >::iterator igFacesOfHyp;
2075       for ( exp.Init( _sdVec[i]._solid, TopAbs_FACE ); exp.More(); exp.Next() )
2076       {
2077         const TGeomID faceID = getMeshDS()->ShapeToIndex( exp.Current() );
2078         THyp hyp = 0;
2079         igFacesOfHyp = ignoreFacesOfHyps.begin();
2080         for ( ; igFacesOfHyp != ignoreFacesOfHyps.end(); ++igFacesOfHyp )
2081           if ( ! igFacesOfHyp->first.count( faceID ))
2082           {
2083             if ( hyp )
2084               return error(SMESH_Comment("Several hypotheses define "
2085                                          "Viscous Layers on the face #") << faceID );
2086             hyp = igFacesOfHyp->second;
2087           }
2088         if ( hyp )
2089           _sdVec[i]._face2hyp.insert( make_pair( faceID, hyp ));
2090         else
2091           _sdVec[i]._ignoreFaceIds.insert( faceID );
2092       }
2093
2094       // check if two hypotheses define different number of viscous layers for
2095       // adjacent faces of a solid
2096       set< int > nbLayersSet;
2097       igFacesOfHyp = ignoreFacesOfHyps.begin();
2098       for ( ; igFacesOfHyp != ignoreFacesOfHyps.end(); ++igFacesOfHyp )
2099       {
2100         nbLayersSet.insert( igFacesOfHyp->second->GetNumberLayers() );
2101       }
2102       if ( nbLayersSet.size() > 1 )
2103       {
2104         for ( exp.Init( _sdVec[i]._solid, TopAbs_EDGE ); exp.More(); exp.Next() )
2105         {
2106           PShapeIteratorPtr fIt = helper.GetAncestors( exp.Current(), *_mesh, TopAbs_FACE );
2107           THyp hyp1 = 0, hyp2 = 0;
2108           while( const TopoDS_Shape* face = fIt->next() )
2109           {
2110             const TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
2111             map< TGeomID, THyp >::iterator f2h = _sdVec[i]._face2hyp.find( faceID );
2112             if ( f2h != _sdVec[i]._face2hyp.end() )
2113             {
2114               ( hyp1 ? hyp2 : hyp1 ) = f2h->second;
2115             }
2116           }
2117           if ( hyp1 && hyp2 &&
2118                hyp1->GetNumberLayers() != hyp2->GetNumberLayers() )
2119           {
2120             return error("Two hypotheses define different number of "
2121                          "viscous layers on adjacent faces");
2122           }
2123         }
2124       }
2125     } // if ( nbHyps > 1 )
2126     else
2127     {
2128       _sdVec[i]._ignoreFaceIds.swap( ignoreFacesOfHyps.back().first );
2129     }
2130   } // loop on _sdVec
2131
2132   if ( onlyWith ) // is called to check hypotheses compatibility only
2133     return true;
2134
2135   // fill _SolidData::_reversedFaceIds
2136   for ( size_t i = 0; i < _sdVec.size(); ++i )
2137   {
2138     exp.Init( _sdVec[i]._solid.Oriented( TopAbs_FORWARD ), TopAbs_FACE );
2139     for ( ; exp.More(); exp.Next() )
2140     {
2141       const TopoDS_Face& face = TopoDS::Face( exp.Current() );
2142       const TGeomID faceID = getMeshDS()->ShapeToIndex( face );
2143       if ( //!sdVec[i]._ignoreFaceIds.count( faceID ) &&
2144           helper.NbAncestors( face, *_mesh, TopAbs_SOLID ) > 1 &&
2145           helper.IsReversedSubMesh( face ))
2146       {
2147         _sdVec[i]._reversedFaceIds.insert( faceID );
2148       }
2149     }
2150   }
2151
2152   // Find FACEs to shrink mesh on (solution 2 in issue 0020832): fill in _shrinkShape2Shape
2153   TopTools_IndexedMapOfShape shapes;
2154   std::string structAlgoName = "Hexa_3D";
2155   for ( size_t i = 0; i < _sdVec.size(); ++i )
2156   {
2157     shapes.Clear();
2158     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_EDGE, shapes);
2159     for ( int iE = 1; iE <= shapes.Extent(); ++iE )
2160     {
2161       const TopoDS_Shape& edge = shapes(iE);
2162       // find 2 FACEs sharing an EDGE
2163       TopoDS_Shape FF[2];
2164       PShapeIteratorPtr fIt = helper.GetAncestors(edge, *_mesh, TopAbs_FACE, &_sdVec[i]._solid);
2165       while ( fIt->more())
2166       {
2167         const TopoDS_Shape* f = fIt->next();
2168         FF[ int( !FF[0].IsNull()) ] = *f;
2169       }
2170       if( FF[1].IsNull() ) continue; // seam edge can be shared by 1 FACE only
2171
2172       // check presence of layers on them
2173       int ignore[2];
2174       for ( int j = 0; j < 2; ++j )
2175         ignore[j] = _sdVec[i]._ignoreFaceIds.count( getMeshDS()->ShapeToIndex( FF[j] ));
2176       if ( ignore[0] == ignore[1] )
2177         continue; // nothing interesting
2178       TopoDS_Shape fWOL = FF[ ignore[0] ? 0 : 1 ];
2179
2180       // add EDGE to maps
2181       if ( !fWOL.IsNull())
2182       {
2183         TGeomID edgeInd = getMeshDS()->ShapeToIndex( edge );
2184         _sdVec[i]._shrinkShape2Shape.insert( make_pair( edgeInd, fWOL ));
2185       }
2186     }
2187   }
2188
2189   // Find the SHAPE along which to inflate _LayerEdge based on VERTEX
2190
2191   for ( size_t i = 0; i < _sdVec.size(); ++i )
2192   {
2193     shapes.Clear();
2194     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_VERTEX, shapes);
2195     for ( int iV = 1; iV <= shapes.Extent(); ++iV )
2196     {
2197       const TopoDS_Shape& vertex = shapes(iV);
2198       // find faces WOL sharing the vertex
2199       vector< TopoDS_Shape > facesWOL;
2200       size_t totalNbFaces = 0;
2201       PShapeIteratorPtr fIt = helper.GetAncestors(vertex, *_mesh, TopAbs_FACE, &_sdVec[i]._solid );
2202       while ( fIt->more())
2203       {
2204         const TopoDS_Shape* f = fIt->next();
2205         totalNbFaces++;
2206         const int fID = getMeshDS()->ShapeToIndex( *f );
2207         if ( _sdVec[i]._ignoreFaceIds.count ( fID ) /*&& !_sdVec[i]._noShrinkShapes.count( fID )*/)
2208           facesWOL.push_back( *f );
2209       }
2210       if ( facesWOL.size() == totalNbFaces || facesWOL.empty() )
2211         continue; // no layers at this vertex or no WOL
2212       TGeomID vInd = getMeshDS()->ShapeToIndex( vertex );
2213       switch ( facesWOL.size() )
2214       {
2215       case 1:
2216       {
2217         helper.SetSubShape( facesWOL[0] );
2218         if ( helper.IsRealSeam( vInd )) // inflate along a seam edge?
2219         {
2220           TopoDS_Shape seamEdge;
2221           PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE);
2222           while ( eIt->more() && seamEdge.IsNull() )
2223           {
2224             const TopoDS_Shape* e = eIt->next();
2225             if ( helper.IsRealSeam( *e ) )
2226               seamEdge = *e;
2227           }
2228           if ( !seamEdge.IsNull() )
2229           {
2230             _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, seamEdge ));
2231             break;
2232           }
2233         }
2234         _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, facesWOL[0] ));
2235         break;
2236       }
2237       case 2:
2238       {
2239         // find an edge shared by 2 faces
2240         PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE);
2241         while ( eIt->more())
2242         {
2243           const TopoDS_Shape* e = eIt->next();
2244           if ( helper.IsSubShape( *e, facesWOL[0]) &&
2245                helper.IsSubShape( *e, facesWOL[1]))
2246           {
2247             _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, *e )); break;
2248           }
2249         }
2250         break;
2251       }
2252       default:
2253         return error("Not yet supported case", _sdVec[i]._index);
2254       }
2255     }
2256   }
2257
2258   // Add to _noShrinkShapes sub-shapes of FACE's that can't be shrinked since
2259   // the algo of the SOLID sharing the FACE does not support it or for other reasons
2260   set< string > notSupportAlgos; notSupportAlgos.insert( structAlgoName );
2261   for ( size_t i = 0; i < _sdVec.size(); ++i )
2262   {
2263     map< TGeomID, TopoDS_Shape >::iterator e2f = _sdVec[i]._shrinkShape2Shape.begin();
2264     for ( ; e2f != _sdVec[i]._shrinkShape2Shape.end(); ++e2f )
2265     {
2266       const TopoDS_Shape& fWOL = e2f->second;
2267       const TGeomID     edgeID = e2f->first;
2268       TGeomID           faceID = getMeshDS()->ShapeToIndex( fWOL );
2269       TopoDS_Shape        edge = getMeshDS()->IndexToShape( edgeID );
2270       if ( edge.ShapeType() != TopAbs_EDGE )
2271         continue; // shrink shape is VERTEX
2272
2273       TopoDS_Shape solid;
2274       PShapeIteratorPtr soIt = helper.GetAncestors(fWOL, *_mesh, TopAbs_SOLID);
2275       while ( soIt->more() && solid.IsNull() )
2276       {
2277         const TopoDS_Shape* so = soIt->next();
2278         if ( !so->IsSame( _sdVec[i]._solid ))
2279           solid = *so;
2280       }
2281       if ( solid.IsNull() )
2282         continue;
2283
2284       bool noShrinkE = false;
2285       SMESH_Algo*  algo = _mesh->GetSubMesh( solid )->GetAlgo();
2286       bool isStructured = ( algo && algo->GetName() == structAlgoName );
2287       size_t     iSolid = _solids.FindIndex( solid ) - 1;
2288       if ( iSolid < _sdVec.size() && _sdVec[ iSolid ]._ignoreFaceIds.count( faceID ))
2289       {
2290         // the adjacent SOLID has NO layers on fWOL;
2291         // shrink allowed if
2292         // - there are layers on the EDGE in the adjacent SOLID
2293         // - there are NO layers in the adjacent SOLID && algo is unstructured and computed later
2294         bool hasWLAdj = (_sdVec[iSolid]._shrinkShape2Shape.count( edgeID ));
2295         bool shrinkAllowed = (( hasWLAdj ) ||
2296                               ( !isStructured && setBefore( _sdVec[ i ], _sdVec[ iSolid ] )));
2297         noShrinkE = !shrinkAllowed;
2298       }
2299       else if ( iSolid < _sdVec.size() )
2300       {
2301         // the adjacent SOLID has layers on fWOL;
2302         // check if SOLID's mesh is unstructured and then try to set it
2303         // to be computed after the i-th solid
2304         if ( isStructured || !setBefore( _sdVec[ i ], _sdVec[ iSolid ] ))
2305           noShrinkE = true; // don't shrink fWOL
2306       }
2307       else
2308       {
2309         // the adjacent SOLID has NO layers at all
2310         noShrinkE = isStructured;
2311       }
2312
2313       if ( noShrinkE )
2314       {
2315         _sdVec[i]._noShrinkShapes.insert( edgeID );
2316
2317         // check if there is a collision with to-shrink-from EDGEs in iSolid
2318         // if ( iSolid < _sdVec.size() )
2319         // {
2320         //   shapes.Clear();
2321         //   TopExp::MapShapes( fWOL, TopAbs_EDGE, shapes);
2322         //   for ( int iE = 1; iE <= shapes.Extent(); ++iE )
2323         //   {
2324         //     const TopoDS_Edge& E = TopoDS::Edge( shapes( iE ));
2325         //     const TGeomID    eID = getMeshDS()->ShapeToIndex( E );
2326         //     if ( eID == edgeID ||
2327         //          !_sdVec[iSolid]._shrinkShape2Shape.count( eID ) ||
2328         //          _sdVec[i]._noShrinkShapes.count( eID ))
2329         //       continue;
2330         //     for ( int is1st = 0; is1st < 2; ++is1st )
2331         //     {
2332         //       TopoDS_Vertex V = helper.IthVertex( is1st, E );
2333         //       if ( _sdVec[i]._noShrinkShapes.count( getMeshDS()->ShapeToIndex( V ) ))
2334         //       {
2335         //         return error("No way to make a conformal mesh with "
2336         //                      "the given set of faces with layers", _sdVec[i]._index);
2337         //       }
2338         //     }
2339         //   }
2340         // }
2341       }
2342
2343       // add VERTEXes of the edge in _noShrinkShapes, which is necessary if
2344       // _shrinkShape2Shape is different in the adjacent SOLID
2345       for ( TopoDS_Iterator vIt( edge ); vIt.More(); vIt.Next() )
2346       {
2347         TGeomID vID = getMeshDS()->ShapeToIndex( vIt.Value() );
2348         bool noShrinkV = false, noShrinkIfAdjMeshed = false;
2349
2350         if ( iSolid < _sdVec.size() )
2351         {
2352           if ( _sdVec[ iSolid ]._ignoreFaceIds.count( faceID ))
2353           {
2354             map< TGeomID, TopoDS_Shape >::iterator i2S, i2SAdj;
2355             i2S    = _sdVec[i     ]._shrinkShape2Shape.find( vID );
2356             i2SAdj = _sdVec[iSolid]._shrinkShape2Shape.find( vID );
2357             if ( i2SAdj == _sdVec[iSolid]._shrinkShape2Shape.end() )
2358               noShrinkV = (( isStructured ) ||
2359                            ( noShrinkIfAdjMeshed = i2S->second.ShapeType() == TopAbs_EDGE ));
2360             else
2361               noShrinkV = ( ! i2S->second.IsSame( i2SAdj->second ));
2362           }
2363           else
2364           {
2365             noShrinkV = noShrinkE;
2366           }
2367         }
2368         else
2369         {
2370           // the adjacent SOLID has NO layers at all
2371           if ( isStructured )
2372           {
2373             noShrinkV = true;
2374           }
2375           else
2376           {
2377             noShrinkV = noShrinkIfAdjMeshed =
2378               ( _sdVec[i]._shrinkShape2Shape[ vID ].ShapeType() == TopAbs_EDGE );
2379           }
2380         }
2381
2382         if ( noShrinkV && noShrinkIfAdjMeshed )
2383         {
2384           // noShrinkV if FACEs in the adjacent SOLID are meshed
2385           PShapeIteratorPtr fIt = helper.GetAncestors( _sdVec[i]._shrinkShape2Shape[ vID ],
2386                                                        *_mesh, TopAbs_FACE, &solid );
2387           while ( fIt->more() )
2388           {
2389             const TopoDS_Shape* f = fIt->next();
2390             if ( !f->IsSame( fWOL ))
2391             {
2392               noShrinkV = ! _mesh->GetSubMesh( *f )->IsEmpty();
2393               break;
2394             }
2395           }
2396         }
2397         if ( noShrinkV )
2398           _sdVec[i]._noShrinkShapes.insert( vID );
2399       }
2400
2401     } // loop on _sdVec[i]._shrinkShape2Shape
2402   } // loop on _sdVec to fill in _SolidData::_noShrinkShapes
2403
2404
2405     // add FACEs of other SOLIDs to _ignoreFaceIds
2406   for ( size_t i = 0; i < _sdVec.size(); ++i )
2407   {
2408     shapes.Clear();
2409     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_FACE, shapes);
2410
2411     for ( exp.Init( _mesh->GetShapeToMesh(), TopAbs_FACE ); exp.More(); exp.Next() )
2412     {
2413       if ( !shapes.Contains( exp.Current() ))
2414         _sdVec[i]._ignoreFaceIds.insert( getMeshDS()->ShapeToIndex( exp.Current() ));
2415     }
2416   }
2417
2418   return true;
2419 }
2420
2421 //================================================================================
2422 /*!
2423  * \brief Finds FACEs w/o layers for a given SOLID by an hypothesis
2424  */
2425 //================================================================================
2426
2427 void _ViscousBuilder::getIgnoreFaces(const TopoDS_Shape&             solid,
2428                                      const StdMeshers_ViscousLayers* hyp,
2429                                      const TopoDS_Shape&             hypShape,
2430                                      set<TGeomID>&                   ignoreFaceIds)
2431 {
2432   TopExp_Explorer exp;
2433
2434   vector<TGeomID> ids = hyp->GetBndShapes();
2435   if ( hyp->IsToIgnoreShapes() ) // FACEs to ignore are given
2436   {
2437     for ( size_t ii = 0; ii < ids.size(); ++ii )
2438     {
2439       const TopoDS_Shape& s = getMeshDS()->IndexToShape( ids[ii] );
2440       if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2441         ignoreFaceIds.insert( ids[ii] );
2442     }
2443   }
2444   else // FACEs with layers are given
2445   {
2446     exp.Init( solid, TopAbs_FACE );
2447     for ( ; exp.More(); exp.Next() )
2448     {
2449       TGeomID faceInd = getMeshDS()->ShapeToIndex( exp.Current() );
2450       if ( find( ids.begin(), ids.end(), faceInd ) == ids.end() )
2451         ignoreFaceIds.insert( faceInd );
2452     }
2453   }
2454
2455   // ignore internal FACEs if inlets and outlets are specified
2456   if ( hyp->IsToIgnoreShapes() )
2457   {
2458     TopTools_IndexedDataMapOfShapeListOfShape solidsOfFace;
2459     TopExp::MapShapesAndAncestors( hypShape,
2460                                    TopAbs_FACE, TopAbs_SOLID, solidsOfFace);
2461
2462     for ( exp.Init( solid, TopAbs_FACE ); exp.More(); exp.Next() )
2463     {
2464       const TopoDS_Face& face = TopoDS::Face( exp.Current() );
2465       if ( SMESH_MesherHelper::NbAncestors( face, *_mesh, TopAbs_SOLID ) < 2 )
2466         continue;
2467
2468       int nbSolids = solidsOfFace.FindFromKey( face ).Extent();
2469       if ( nbSolids > 1 )
2470         ignoreFaceIds.insert( getMeshDS()->ShapeToIndex( face ));
2471     }
2472   }
2473 }
2474
2475 //================================================================================
2476 /*!
2477  * \brief Create the inner surface of the viscous layer and prepare data for infation
2478  */
2479 //================================================================================
2480
2481 bool _ViscousBuilder::makeLayer(_SolidData& data)
2482 {
2483   // get all sub-shapes to make layers on
2484   set<TGeomID> subIds, faceIds;
2485   subIds = data._noShrinkShapes;
2486   TopExp_Explorer exp( data._solid, TopAbs_FACE );
2487   for ( ; exp.More(); exp.Next() )
2488   {
2489     SMESH_subMesh* fSubM = _mesh->GetSubMesh( exp.Current() );
2490     if ( ! data._ignoreFaceIds.count( fSubM->GetId() ))
2491       faceIds.insert( fSubM->GetId() );
2492   }
2493
2494   // make a map to find new nodes on sub-shapes shared with other SOLID
2495   map< TGeomID, TNode2Edge* >::iterator s2ne;
2496   map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
2497   for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
2498   {
2499     TGeomID shapeInd = s2s->first;
2500     for ( size_t i = 0; i < _sdVec.size(); ++i )
2501     {
2502       if ( _sdVec[i]._index == data._index ) continue;
2503       map< TGeomID, TopoDS_Shape >::iterator s2s2 = _sdVec[i]._shrinkShape2Shape.find( shapeInd );
2504       if ( s2s2 != _sdVec[i]._shrinkShape2Shape.end() &&
2505            *s2s == *s2s2 && !_sdVec[i]._n2eMap.empty() )
2506       {
2507         data._s2neMap.insert( make_pair( shapeInd, &_sdVec[i]._n2eMap ));
2508         break;
2509       }
2510     }
2511   }
2512
2513   // Create temporary faces and _LayerEdge's
2514
2515   dumpFunction(SMESH_Comment("makeLayers_")<<data._index);
2516
2517   data._stepSize = Precision::Infinite();
2518   data._stepSizeNodes[0] = 0;
2519
2520   SMESH_MesherHelper helper( *_mesh );
2521   helper.SetSubShape( data._solid );
2522   helper.SetElementsOnShape( true );
2523
2524   vector< const SMDS_MeshNode*> newNodes; // of a mesh face
2525   TNode2Edge::iterator n2e2;
2526
2527   // collect _LayerEdge's of shapes they are based on
2528   vector< _EdgesOnShape >& edgesByGeom = data._edgesOnShape;
2529   const int nbShapes = getMeshDS()->MaxShapeIndex();
2530   edgesByGeom.resize( nbShapes+1 );
2531
2532   // set data of _EdgesOnShape's
2533   if ( SMESH_subMesh* sm = _mesh->GetSubMesh( data._solid ))
2534   {
2535     SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(/*includeSelf=*/false);
2536     while ( smIt->more() )
2537     {
2538       sm = smIt->next();
2539       if ( sm->GetSubShape().ShapeType() == TopAbs_FACE &&
2540            !faceIds.count( sm->GetId() ))
2541         continue;
2542       setShapeData( edgesByGeom[ sm->GetId() ], sm, data );
2543     }
2544   }
2545   // make _LayerEdge's
2546   for ( set<TGeomID>::iterator id = faceIds.begin(); id != faceIds.end(); ++id )
2547   {
2548     const TopoDS_Face& F = TopoDS::Face( getMeshDS()->IndexToShape( *id ));
2549     SMESH_subMesh* sm = _mesh->GetSubMesh( F );
2550     SMESH_ProxyMesh::SubMesh* proxySub =
2551       data._proxyMesh->getFaceSubM( F, /*create=*/true);
2552
2553     SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
2554     if ( !smDS ) return error(SMESH_Comment("Not meshed face ") << *id, data._index );
2555
2556     SMDS_ElemIteratorPtr eIt = smDS->GetElements();
2557     while ( eIt->more() )
2558     {
2559       const SMDS_MeshElement* face = eIt->next();
2560       double          faceMaxCosin = -1;
2561       _LayerEdge*     maxCosinEdge = 0;
2562       int             nbDegenNodes = 0;
2563
2564       newNodes.resize( face->NbCornerNodes() );
2565       for ( size_t i = 0 ; i < newNodes.size(); ++i )
2566       {
2567         const SMDS_MeshNode* n = face->GetNode( i );
2568         const int      shapeID = n->getshapeId();
2569         const bool onDegenShap = helper.IsDegenShape( shapeID );
2570         const bool onDegenEdge = ( onDegenShap && n->GetPosition()->GetDim() == 1 );
2571         if ( onDegenShap )
2572         {
2573           if ( onDegenEdge )
2574           {
2575             // substitute n on a degenerated EDGE with a node on a corresponding VERTEX
2576             const TopoDS_Shape& E = getMeshDS()->IndexToShape( shapeID );
2577             TopoDS_Vertex       V = helper.IthVertex( 0, TopoDS::Edge( E ));
2578             if ( const SMDS_MeshNode* vN = SMESH_Algo::VertexNode( V, getMeshDS() )) {
2579               n = vN;
2580               nbDegenNodes++;
2581             }
2582           }
2583           else
2584           {
2585             nbDegenNodes++;
2586           }
2587         }
2588         TNode2Edge::iterator n2e = data._n2eMap.insert( make_pair( n, (_LayerEdge*)0 )).first;
2589         if ( !(*n2e).second )
2590         {
2591           // add a _LayerEdge
2592           _LayerEdge* edge = new _LayerEdge();
2593           edge->_nodes.push_back( n );
2594           n2e->second = edge;
2595           edgesByGeom[ shapeID ]._edges.push_back( edge );
2596           const bool noShrink = data._noShrinkShapes.count( shapeID );
2597
2598           SMESH_TNodeXYZ xyz( n );
2599
2600           // set edge data or find already refined _LayerEdge and get data from it
2601           if (( !noShrink                                                     ) &&
2602               ( n->GetPosition()->GetTypeOfPosition() != SMDS_TOP_FACE        ) &&
2603               (( s2ne = data._s2neMap.find( shapeID )) != data._s2neMap.end() ) &&
2604               (( n2e2 = (*s2ne).second->find( n )) != s2ne->second->end()     ))
2605           {
2606             _LayerEdge* foundEdge = (*n2e2).second;
2607             gp_XYZ        lastPos = edge->Copy( *foundEdge, edgesByGeom[ shapeID ], helper );
2608             foundEdge->_pos.push_back( lastPos );
2609             // location of the last node is modified and we restore it by foundEdge->_pos.back()
2610             const_cast< SMDS_MeshNode* >
2611               ( edge->_nodes.back() )->setXYZ( xyz.X(), xyz.Y(), xyz.Z() );
2612           }
2613           else
2614           {
2615             if ( !noShrink )
2616             {
2617               edge->_nodes.push_back( helper.AddNode( xyz.X(), xyz.Y(), xyz.Z() ));
2618             }
2619             if ( !setEdgeData( *edge, edgesByGeom[ shapeID ], helper, data ))
2620               return false;
2621
2622             if ( edge->_nodes.size() < 2 )
2623               edge->Block( data );
2624               //data._noShrinkShapes.insert( shapeID );
2625           }
2626           dumpMove(edge->_nodes.back());
2627
2628           if ( edge->_cosin > faceMaxCosin )
2629           {
2630             faceMaxCosin = edge->_cosin;
2631             maxCosinEdge = edge;
2632           }
2633         }
2634         newNodes[ i ] = n2e->second->_nodes.back();
2635
2636         if ( onDegenEdge )
2637           data._n2eMap.insert( make_pair( face->GetNode( i ), n2e->second ));
2638       }
2639       if ( newNodes.size() - nbDegenNodes < 2 )
2640         continue;
2641
2642       // create a temporary face
2643       const SMDS_MeshElement* newFace =
2644         new _TmpMeshFace( newNodes, --_tmpFaceID, face->getshapeId(), face->getIdInShape() );
2645       proxySub->AddElement( newFace );
2646
2647       // compute inflation step size by min size of element on a convex surface
2648       if ( faceMaxCosin > theMinSmoothCosin )
2649         limitStepSize( data, face, maxCosinEdge );
2650
2651     } // loop on 2D elements on a FACE
2652   } // loop on FACEs of a SOLID to create _LayerEdge's
2653
2654
2655   // Set _LayerEdge::_neibors
2656   TNode2Edge::iterator n2e;
2657   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
2658   {
2659     _EdgesOnShape& eos = data._edgesOnShape[iS];
2660     for ( size_t i = 0; i < eos._edges.size(); ++i )
2661     {
2662       _LayerEdge* edge = eos._edges[i];
2663       TIDSortedNodeSet nearNodes;
2664       SMDS_ElemIteratorPtr fIt = edge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
2665       while ( fIt->more() )
2666       {
2667         const SMDS_MeshElement* f = fIt->next();
2668         if ( !data._ignoreFaceIds.count( f->getshapeId() ))
2669           nearNodes.insert( f->begin_nodes(), f->end_nodes() );
2670       }
2671       nearNodes.erase( edge->_nodes[0] );
2672       edge->_neibors.reserve( nearNodes.size() );
2673       TIDSortedNodeSet::iterator node = nearNodes.begin();
2674       for ( ; node != nearNodes.end(); ++node )
2675         if (( n2e = data._n2eMap.find( *node )) != data._n2eMap.end() )
2676           edge->_neibors.push_back( n2e->second );
2677     }
2678   }
2679
2680   data._epsilon = 1e-7;
2681   if ( data._stepSize < 1. )
2682     data._epsilon *= data._stepSize;
2683
2684   if ( !findShapesToSmooth( data )) // _LayerEdge::_maxLen is computed here
2685     return false;
2686
2687   // limit data._stepSize depending on surface curvature and fill data._convexFaces
2688   limitStepSizeByCurvature( data ); // !!! it must be before node substitution in _Simplex
2689
2690   // Set target nodes into _Simplex and _LayerEdge's to _2NearEdges
2691   const SMDS_MeshNode* nn[2];
2692   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
2693   {
2694     _EdgesOnShape& eos = data._edgesOnShape[iS];
2695     for ( size_t i = 0; i < eos._edges.size(); ++i )
2696     {
2697       _LayerEdge* edge = eos._edges[i];
2698       if ( edge->IsOnEdge() )
2699       {
2700         // get neighbor nodes
2701         bool hasData = ( edge->_2neibors->_edges[0] );
2702         if ( hasData ) // _LayerEdge is a copy of another one
2703         {
2704           nn[0] = edge->_2neibors->srcNode(0);
2705           nn[1] = edge->_2neibors->srcNode(1);
2706         }
2707         else if ( !findNeiborsOnEdge( edge, nn[0],nn[1], eos, data ))
2708         {
2709           return false;
2710         }
2711         // set neighbor _LayerEdge's
2712         for ( int j = 0; j < 2; ++j )
2713         {
2714           if (( n2e = data._n2eMap.find( nn[j] )) == data._n2eMap.end() )
2715             return error("_LayerEdge not found by src node", data._index);
2716           edge->_2neibors->_edges[j] = n2e->second;
2717         }
2718         if ( !hasData )
2719           edge->SetDataByNeighbors( nn[0], nn[1], eos, helper );
2720       }
2721
2722       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
2723       {
2724         _Simplex& s = edge->_simplices[j];
2725         s._nNext = data._n2eMap[ s._nNext ]->_nodes.back();
2726         s._nPrev = data._n2eMap[ s._nPrev ]->_nodes.back();
2727       }
2728
2729       // For an _LayerEdge on a degenerated EDGE, copy some data from
2730       // a corresponding _LayerEdge on a VERTEX
2731       // (issue 52453, pb on a downloaded SampleCase2-Tet-netgen-mephisto.hdf)
2732       if ( helper.IsDegenShape( edge->_nodes[0]->getshapeId() ))
2733       {
2734         // Generally we should not get here
2735         if ( eos.ShapeType() != TopAbs_EDGE )
2736           continue;
2737         TopoDS_Vertex V = helper.IthVertex( 0, TopoDS::Edge( eos._shape ));
2738         const SMDS_MeshNode* vN = SMESH_Algo::VertexNode( V, getMeshDS() );
2739         if (( n2e = data._n2eMap.find( vN )) == data._n2eMap.end() )
2740           continue;
2741         const _LayerEdge* vEdge = n2e->second;
2742         edge->_normal    = vEdge->_normal;
2743         edge->_lenFactor = vEdge->_lenFactor;
2744         edge->_cosin     = vEdge->_cosin;
2745       }
2746
2747     } // loop on data._edgesOnShape._edges
2748   } // loop on data._edgesOnShape
2749
2750   // fix _LayerEdge::_2neibors on EDGEs to smooth
2751   // map< TGeomID,Handle(Geom_Curve)>::iterator e2c = data._edge2curve.begin();
2752   // for ( ; e2c != data._edge2curve.end(); ++e2c )
2753   //   if ( !e2c->second.IsNull() )
2754   //   {
2755   //     if ( _EdgesOnShape* eos = data.GetShapeEdges( e2c->first ))
2756   //       data.Sort2NeiborsOnEdge( eos->_edges );
2757   //   }
2758
2759   dumpFunctionEnd();
2760   return true;
2761 }
2762
2763 //================================================================================
2764 /*!
2765  * \brief Compute inflation step size by min size of element on a convex surface
2766  */
2767 //================================================================================
2768
2769 void _ViscousBuilder::limitStepSize( _SolidData&             data,
2770                                      const SMDS_MeshElement* face,
2771                                      const _LayerEdge*       maxCosinEdge )
2772 {
2773   int iN = 0;
2774   double minSize = 10 * data._stepSize;
2775   const int nbNodes = face->NbCornerNodes();
2776   for ( int i = 0; i < nbNodes; ++i )
2777   {
2778     const SMDS_MeshNode* nextN = face->GetNode( SMESH_MesherHelper::WrapIndex( i+1, nbNodes ));
2779     const SMDS_MeshNode*  curN = face->GetNode( i );
2780     if ( nextN->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ||
2781          curN-> GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2782     {
2783       double dist = SMESH_TNodeXYZ( curN ).Distance( nextN );
2784       if ( dist < minSize )
2785         minSize = dist, iN = i;
2786     }
2787   }
2788   double newStep = 0.8 * minSize / maxCosinEdge->_lenFactor;
2789   if ( newStep < data._stepSize )
2790   {
2791     data._stepSize = newStep;
2792     data._stepSizeCoeff = 0.8 / maxCosinEdge->_lenFactor;
2793     data._stepSizeNodes[0] = face->GetNode( iN );
2794     data._stepSizeNodes[1] = face->GetNode( SMESH_MesherHelper::WrapIndex( iN+1, nbNodes ));
2795   }
2796 }
2797
2798 //================================================================================
2799 /*!
2800  * \brief Compute inflation step size by min size of element on a convex surface
2801  */
2802 //================================================================================
2803
2804 void _ViscousBuilder::limitStepSize( _SolidData& data, const double minSize )
2805 {
2806   if ( minSize < data._stepSize )
2807   {
2808     data._stepSize = minSize;
2809     if ( data._stepSizeNodes[0] )
2810     {
2811       double dist =
2812         SMESH_TNodeXYZ(data._stepSizeNodes[0]).Distance(data._stepSizeNodes[1]);
2813       data._stepSizeCoeff = data._stepSize / dist;
2814     }
2815   }
2816 }
2817
2818 //================================================================================
2819 /*!
2820  * \brief Limit data._stepSize by evaluating curvature of shapes and fill data._convexFaces
2821  */
2822 //================================================================================
2823
2824 void _ViscousBuilder::limitStepSizeByCurvature( _SolidData& data )
2825 {
2826   SMESH_MesherHelper helper( *_mesh );
2827
2828   BRepLProp_SLProps surfProp( 2, 1e-6 );
2829   data._convexFaces.clear();
2830
2831   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
2832   {
2833     _EdgesOnShape& eof = data._edgesOnShape[iS];
2834     if ( eof.ShapeType() != TopAbs_FACE ||
2835          data._ignoreFaceIds.count( eof._shapeID ))
2836       continue;
2837
2838     TopoDS_Face        F = TopoDS::Face( eof._shape );
2839     const TGeomID faceID = eof._shapeID;
2840
2841     BRepAdaptor_Surface surface( F, false );
2842     surfProp.SetSurface( surface );
2843
2844     _ConvexFace cnvFace;
2845     cnvFace._face = F;
2846     cnvFace._normalsFixed = false;
2847     cnvFace._isTooCurved = false;
2848
2849     double maxCurvature = cnvFace.GetMaxCurvature( data, eof, surfProp, helper );
2850     if ( maxCurvature > 0 )
2851     {
2852       limitStepSize( data, 0.9 / maxCurvature );
2853       findEdgesToUpdateNormalNearConvexFace( cnvFace, data, helper );
2854     }
2855     if ( !cnvFace._isTooCurved ) continue;
2856
2857     _ConvexFace & convFace =
2858       data._convexFaces.insert( make_pair( faceID, cnvFace )).first->second;
2859
2860     // skip a closed surface (data._convexFaces is useful anyway)
2861     bool isClosedF = false;
2862     helper.SetSubShape( F );
2863     if ( helper.HasRealSeam() )
2864     {
2865       // in the closed surface there must be a closed EDGE
2866       for ( TopExp_Explorer eIt( F, TopAbs_EDGE ); eIt.More() && !isClosedF; eIt.Next() )
2867         isClosedF = helper.IsClosedEdge( TopoDS::Edge( eIt.Current() ));
2868     }
2869     if ( isClosedF )
2870     {
2871       // limit _LayerEdge::_maxLen on the FACE
2872       const double oriFactor    = ( F.Orientation() == TopAbs_REVERSED ? +1. : -1. );
2873       const double minCurvature =
2874         1. / ( eof._hyp.GetTotalThickness() * ( 1 + theThickToIntersection ));
2875       map< TGeomID, _EdgesOnShape* >::iterator id2eos = cnvFace._subIdToEOS.find( faceID );
2876       if ( id2eos != cnvFace._subIdToEOS.end() )
2877       {
2878         _EdgesOnShape& eos = * id2eos->second;
2879         for ( size_t i = 0; i < eos._edges.size(); ++i )
2880         {
2881           _LayerEdge* ledge = eos._edges[ i ];
2882           gp_XY uv = helper.GetNodeUV( F, ledge->_nodes[0] );
2883           surfProp.SetParameters( uv.X(), uv.Y() );
2884           if ( surfProp.IsCurvatureDefined() )
2885           {
2886             double curvature = Max( surfProp.MaxCurvature() * oriFactor,
2887                                     surfProp.MinCurvature() * oriFactor );
2888             if ( curvature > minCurvature )
2889               ledge->SetMaxLen( Min( ledge->_maxLen, 1. / curvature ));
2890           }
2891         }
2892       }
2893       continue;
2894     }
2895
2896     // Fill _ConvexFace::_simplexTestEdges. These _LayerEdge's are used to detect
2897     // prism distortion.
2898     map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.find( faceID );
2899     if ( id2eos != convFace._subIdToEOS.end() && !id2eos->second->_edges.empty() )
2900     {
2901       // there are _LayerEdge's on the FACE it-self;
2902       // select _LayerEdge's near EDGEs
2903       _EdgesOnShape& eos = * id2eos->second;
2904       for ( size_t i = 0; i < eos._edges.size(); ++i )
2905       {
2906         _LayerEdge* ledge = eos._edges[ i ];
2907         for ( size_t j = 0; j < ledge->_simplices.size(); ++j )
2908           if ( ledge->_simplices[j]._nNext->GetPosition()->GetDim() < 2 )
2909           {
2910             // do not select _LayerEdge's neighboring sharp EDGEs
2911             bool sharpNbr = false;
2912             for ( size_t iN = 0; iN < ledge->_neibors.size()  && !sharpNbr; ++iN )
2913               sharpNbr = ( ledge->_neibors[iN]->_cosin > theMinSmoothCosin );
2914             if ( !sharpNbr )
2915               convFace._simplexTestEdges.push_back( ledge );
2916             break;
2917           }
2918       }
2919     }
2920     else
2921     {
2922       // where there are no _LayerEdge's on a _ConvexFace,
2923       // as e.g. on a fillet surface with no internal nodes - issue 22580,
2924       // so that collision of viscous internal faces is not detected by check of
2925       // intersection of _LayerEdge's with the viscous internal faces.
2926
2927       set< const SMDS_MeshNode* > usedNodes;
2928
2929       // look for _LayerEdge's with null _sWOL
2930       id2eos = convFace._subIdToEOS.begin();
2931       for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
2932       {
2933         _EdgesOnShape& eos = * id2eos->second;
2934         if ( !eos._sWOL.IsNull() )
2935           continue;
2936         for ( size_t i = 0; i < eos._edges.size(); ++i )
2937         {
2938           _LayerEdge* ledge = eos._edges[ i ];
2939           const SMDS_MeshNode* srcNode = ledge->_nodes[0];
2940           if ( !usedNodes.insert( srcNode ).second ) continue;
2941
2942           for ( size_t i = 0; i < ledge->_simplices.size(); ++i )
2943           {
2944             usedNodes.insert( ledge->_simplices[i]._nPrev );
2945             usedNodes.insert( ledge->_simplices[i]._nNext );
2946           }
2947           convFace._simplexTestEdges.push_back( ledge );
2948         }
2949       }
2950     }
2951   } // loop on FACEs of data._solid
2952 }
2953
2954 //================================================================================
2955 /*!
2956  * \brief Detect shapes (and _LayerEdge's on them) to smooth
2957  */
2958 //================================================================================
2959
2960 bool _ViscousBuilder::findShapesToSmooth( _SolidData& data )
2961 {
2962   // define allowed thickness
2963   computeGeomSize( data ); // compute data._geomSize and _LayerEdge::_maxLen
2964
2965
2966   // Find shapes needing smoothing; such a shape has _LayerEdge._normal on it's
2967   // boundary inclined to the shape at a sharp angle
2968
2969   TopTools_MapOfShape edgesOfSmooFaces;
2970   SMESH_MesherHelper helper( *_mesh );
2971   bool ok = true;
2972
2973   vector< _EdgesOnShape >& edgesByGeom = data._edgesOnShape;
2974   data._nbShapesToSmooth = 0;
2975
2976   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check FACEs
2977   {
2978     _EdgesOnShape& eos = edgesByGeom[iS];
2979     eos._toSmooth = false;
2980     if ( eos._edges.empty() || eos.ShapeType() != TopAbs_FACE )
2981       continue;
2982
2983     double tgtThick = eos._hyp.GetTotalThickness();
2984     SMESH_subMeshIteratorPtr subIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false );
2985     while ( subIt->more() && !eos._toSmooth )
2986     {
2987       TGeomID iSub = subIt->next()->GetId();
2988       const vector<_LayerEdge*>& eSub = edgesByGeom[ iSub ]._edges;
2989       if ( eSub.empty() ) continue;
2990
2991       double faceSize;
2992       for ( size_t i = 0; i < eSub.size() && !eos._toSmooth; ++i )
2993         if ( eSub[i]->_cosin > theMinSmoothCosin )
2994         {
2995           SMDS_ElemIteratorPtr fIt = eSub[i]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
2996           while ( fIt->more() && !eos._toSmooth )
2997           {
2998             const SMDS_MeshElement* face = fIt->next();
2999             if ( face->getshapeId() == eos._shapeID &&
3000                  getDistFromEdge( face, eSub[i]->_nodes[0], faceSize ))
3001             {
3002               eos._toSmooth = needSmoothing( eSub[i]->_cosin,
3003                                              tgtThick * eSub[i]->_lenFactor,
3004                                              faceSize);
3005             }
3006           }
3007         }
3008     }
3009     if ( eos._toSmooth )
3010     {
3011       for ( TopExp_Explorer eExp( edgesByGeom[iS]._shape, TopAbs_EDGE ); eExp.More(); eExp.Next() )
3012         edgesOfSmooFaces.Add( eExp.Current() );
3013
3014       data.PrepareEdgesToSmoothOnFace( &edgesByGeom[iS], /*substituteSrcNodes=*/false );
3015     }
3016     data._nbShapesToSmooth += eos._toSmooth;
3017
3018   }  // check FACEs
3019
3020   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check EDGEs
3021   {
3022     _EdgesOnShape& eos = edgesByGeom[iS];
3023     eos._edgeSmoother = NULL;
3024     if ( eos._edges.empty() || eos.ShapeType() != TopAbs_EDGE ) continue;
3025     if ( !eos._hyp.ToSmooth() ) continue;
3026
3027     const TopoDS_Edge& E = TopoDS::Edge( edgesByGeom[iS]._shape );
3028     if ( SMESH_Algo::isDegenerated( E ) || !edgesOfSmooFaces.Contains( E ))
3029       continue;
3030
3031     double tgtThick = eos._hyp.GetTotalThickness();
3032     for ( TopoDS_Iterator vIt( E ); vIt.More() && !eos._toSmooth; vIt.Next() )
3033     {
3034       TGeomID iV = getMeshDS()->ShapeToIndex( vIt.Value() );
3035       vector<_LayerEdge*>& eV = edgesByGeom[ iV ]._edges;
3036       if ( eV.empty() || eV[0]->Is( _LayerEdge::MULTI_NORMAL )) continue;
3037       gp_Vec  eDir    = getEdgeDir( E, TopoDS::Vertex( vIt.Value() ));
3038       double angle    = eDir.Angle( eV[0]->_normal );
3039       double cosin    = Cos( angle );
3040       double cosinAbs = Abs( cosin );
3041       if ( cosinAbs > theMinSmoothCosin )
3042       {
3043         // always smooth analytic EDGEs
3044         Handle(Geom_Curve) curve = _Smoother1D::CurveForSmooth( E, eos, helper );
3045         eos._toSmooth = ! curve.IsNull();
3046
3047         // compare tgtThick with the length of an end segment
3048         SMDS_ElemIteratorPtr eIt = eV[0]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Edge);
3049         while ( eIt->more() && !eos._toSmooth )
3050         {
3051           const SMDS_MeshElement* endSeg = eIt->next();
3052           if ( endSeg->getshapeId() == (int) iS )
3053           {
3054             double segLen =
3055               SMESH_TNodeXYZ( endSeg->GetNode( 0 )).Distance( endSeg->GetNode( 1 ));
3056             eos._toSmooth = needSmoothing( cosinAbs, tgtThick * eV[0]->_lenFactor, segLen );
3057           }
3058         }
3059         if ( eos._toSmooth )
3060         {
3061           eos._edgeSmoother = new _Smoother1D( curve, eos );
3062
3063           // for ( size_t i = 0; i < eos._edges.size(); ++i )
3064           //   eos._edges[i]->Set( _LayerEdge::TO_SMOOTH );
3065         }
3066       }
3067     }
3068     data._nbShapesToSmooth += eos._toSmooth;
3069
3070   } // check EDGEs
3071
3072   // Reset _cosin if no smooth is allowed by the user
3073   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS )
3074   {
3075     _EdgesOnShape& eos = edgesByGeom[iS];
3076     if ( eos._edges.empty() ) continue;
3077
3078     if ( !eos._hyp.ToSmooth() )
3079       for ( size_t i = 0; i < eos._edges.size(); ++i )
3080         //eos._edges[i]->SetCosin( 0 ); // keep _cosin to use in limitMaxLenByCurvature()
3081         eos._edges[i]->_lenFactor = 1;
3082   }
3083
3084
3085   // Fill _eosC1 to make that C1 FACEs and EGDEs between them to be smoothed as a whole
3086
3087   TopTools_MapOfShape c1VV;
3088
3089   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check FACEs
3090   {
3091     _EdgesOnShape& eos = edgesByGeom[iS];
3092     if ( eos._edges.empty() ||
3093          eos.ShapeType() != TopAbs_FACE ||
3094          !eos._toSmooth )
3095       continue;
3096
3097     // check EDGEs of a FACE
3098     TopTools_MapOfShape checkedEE, allVV;
3099     list< SMESH_subMesh* > smQueue( 1, eos._subMesh ); // sm of FACEs
3100     while ( !smQueue.empty() )
3101     {
3102       SMESH_subMesh* sm = smQueue.front();
3103       smQueue.pop_front();
3104       SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(/*includeSelf=*/false);
3105       while ( smIt->more() )
3106       {
3107         sm = smIt->next();
3108         if ( sm->GetSubShape().ShapeType() == TopAbs_VERTEX )
3109           allVV.Add( sm->GetSubShape() );
3110         if ( sm->GetSubShape().ShapeType() != TopAbs_EDGE ||
3111              !checkedEE.Add( sm->GetSubShape() ))
3112           continue;
3113
3114         _EdgesOnShape*      eoe = data.GetShapeEdges( sm->GetId() );
3115         vector<_LayerEdge*>& eE = eoe->_edges;
3116         if ( eE.empty() || !eoe->_sWOL.IsNull() )
3117           continue;
3118
3119         bool isC1 = true; // check continuity along an EDGE
3120         for ( size_t i = 0; i < eE.size() && isC1; ++i )
3121           isC1 = ( Abs( eE[i]->_cosin ) < theMinSmoothCosin );
3122         if ( !isC1 )
3123           continue;
3124
3125         // check that mesh faces are C1 as well
3126         {
3127           gp_XYZ norm1, norm2;
3128           const SMDS_MeshNode*   n = eE[ eE.size() / 2 ]->_nodes[0];
3129           SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
3130           if ( !SMESH_MeshAlgos::FaceNormal( fIt->next(), norm1, /*normalized=*/true ))
3131             continue;
3132           while ( fIt->more() && isC1 )
3133             isC1 = ( SMESH_MeshAlgos::FaceNormal( fIt->next(), norm2, /*normalized=*/true ) &&
3134                      Abs( norm1 * norm2 ) >= ( 1. - theMinSmoothCosin ));
3135           if ( !isC1 )
3136             continue;
3137         }
3138
3139         // add the EDGE and an adjacent FACE to _eosC1
3140         PShapeIteratorPtr fIt = helper.GetAncestors( sm->GetSubShape(), *_mesh, TopAbs_FACE );
3141         while ( const TopoDS_Shape* face = fIt->next() )
3142         {
3143           _EdgesOnShape* eof = data.GetShapeEdges( *face );
3144           if ( !eof ) continue; // other solid
3145           if ( eos._shapeID == eof->_shapeID ) continue;
3146           if ( !eos.HasC1( eof ))
3147           {
3148             // check the FACEs
3149             eos._eosC1.push_back( eof );
3150             eof->_toSmooth = false;
3151             data.PrepareEdgesToSmoothOnFace( eof, /*substituteSrcNodes=*/false );
3152             smQueue.push_back( eof->_subMesh );
3153           }
3154           if ( !eos.HasC1( eoe ))
3155           {
3156             eos._eosC1.push_back( eoe );
3157             eoe->_toSmooth = false;
3158             data.PrepareEdgesToSmoothOnFace( eoe, /*substituteSrcNodes=*/false );
3159           }
3160         }
3161       }
3162     }
3163     if ( eos._eosC1.empty() )
3164       continue;
3165
3166     // check VERTEXes of C1 FACEs
3167     TopTools_MapIteratorOfMapOfShape vIt( allVV );
3168     for ( ; vIt.More(); vIt.Next() )
3169     {
3170       _EdgesOnShape* eov = data.GetShapeEdges( vIt.Key() );
3171       if ( !eov || eov->_edges.empty() || !eov->_sWOL.IsNull() )
3172         continue;
3173
3174       bool isC1 = true; // check if all adjacent FACEs are in eos._eosC1
3175       PShapeIteratorPtr fIt = helper.GetAncestors( vIt.Key(), *_mesh, TopAbs_FACE );
3176       while ( const TopoDS_Shape* face = fIt->next() )
3177       {
3178         _EdgesOnShape* eof = data.GetShapeEdges( *face );
3179         if ( !eof ) continue; // other solid
3180         isC1 = ( face->IsSame( eos._shape ) || eos.HasC1( eof ));
3181         if ( !isC1 )
3182           break;
3183       }
3184       if ( isC1 )
3185       {
3186         eos._eosC1.push_back( eov );
3187         data.PrepareEdgesToSmoothOnFace( eov, /*substituteSrcNodes=*/false );
3188         c1VV.Add( eov->_shape );
3189       }
3190     }
3191
3192   } // fill _eosC1 of FACEs
3193
3194
3195   // Find C1 EDGEs
3196
3197   vector< pair< _EdgesOnShape*, gp_XYZ > > dirOfEdges;
3198
3199   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check VERTEXes
3200   {
3201     _EdgesOnShape& eov = edgesByGeom[iS];
3202     if ( eov._edges.empty() ||
3203          eov.ShapeType() != TopAbs_VERTEX ||
3204          c1VV.Contains( eov._shape ))
3205       continue;
3206     const TopoDS_Vertex& V = TopoDS::Vertex( eov._shape );
3207
3208     // get directions of surrounding EDGEs
3209     dirOfEdges.clear();
3210     PShapeIteratorPtr fIt = helper.GetAncestors( eov._shape, *_mesh, TopAbs_EDGE );
3211     while ( const TopoDS_Shape* e = fIt->next() )
3212     {
3213       _EdgesOnShape* eoe = data.GetShapeEdges( *e );
3214       if ( !eoe ) continue; // other solid
3215       gp_XYZ eDir = getEdgeDir( TopoDS::Edge( *e ), V );
3216       if ( !Precision::IsInfinite( eDir.X() ))
3217         dirOfEdges.push_back( make_pair( eoe, eDir.Normalized() ));
3218     }
3219
3220     // find EDGEs with C1 directions
3221     for ( size_t i = 0; i < dirOfEdges.size(); ++i )
3222       for ( size_t j = i+1; j < dirOfEdges.size(); ++j )
3223         if ( dirOfEdges[i].first && dirOfEdges[j].first )
3224         {
3225           double dot = dirOfEdges[i].second * dirOfEdges[j].second;
3226           bool isC1 = ( dot < - ( 1. - theMinSmoothCosin ));
3227           if ( isC1 )
3228           {
3229             double maxEdgeLen = 3 * Min( eov._edges[0]->_maxLen, eov._hyp.GetTotalThickness() );
3230             double eLen1 = SMESH_Algo::EdgeLength( TopoDS::Edge( dirOfEdges[i].first->_shape ));
3231             double eLen2 = SMESH_Algo::EdgeLength( TopoDS::Edge( dirOfEdges[j].first->_shape ));
3232             if ( eLen1 < maxEdgeLen ) eov._eosC1.push_back( dirOfEdges[i].first );
3233             if ( eLen2 < maxEdgeLen ) eov._eosC1.push_back( dirOfEdges[j].first );
3234             dirOfEdges[i].first = 0;
3235             dirOfEdges[j].first = 0;
3236           }
3237         }
3238   } // fill _eosC1 of VERTEXes
3239
3240
3241
3242   return ok;
3243 }
3244
3245 //================================================================================
3246 /*!
3247  * \brief initialize data of _EdgesOnShape
3248  */
3249 //================================================================================
3250
3251 void _ViscousBuilder::setShapeData( _EdgesOnShape& eos,
3252                                     SMESH_subMesh* sm,
3253                                     _SolidData&    data )
3254 {
3255   if ( !eos._shape.IsNull() ||
3256        sm->GetSubShape().ShapeType() == TopAbs_WIRE )
3257     return;
3258
3259   SMESH_MesherHelper helper( *_mesh );
3260
3261   eos._subMesh = sm;
3262   eos._shapeID = sm->GetId();
3263   eos._shape   = sm->GetSubShape();
3264   if ( eos.ShapeType() == TopAbs_FACE )
3265     eos._shape.Orientation( helper.GetSubShapeOri( data._solid, eos._shape ));
3266   eos._toSmooth = false;
3267   eos._data = &data;
3268
3269   // set _SWOL
3270   map< TGeomID, TopoDS_Shape >::const_iterator s2s =
3271     data._shrinkShape2Shape.find( eos._shapeID );
3272   if ( s2s != data._shrinkShape2Shape.end() )
3273     eos._sWOL = s2s->second;
3274
3275   eos._isRegularSWOL = true;
3276   if ( eos.SWOLType() == TopAbs_FACE )
3277   {
3278     const TopoDS_Face& F = TopoDS::Face( eos._sWOL );
3279     Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( F );
3280     eos._isRegularSWOL = ( ! surface->HasSingularities( 1e-7 ));
3281   }
3282
3283   // set _hyp
3284   if ( data._hyps.size() == 1 )
3285   {
3286     eos._hyp = data._hyps.back();
3287   }
3288   else
3289   {
3290     // compute average StdMeshers_ViscousLayers parameters
3291     map< TGeomID, const StdMeshers_ViscousLayers* >::iterator f2hyp;
3292     if ( eos.ShapeType() == TopAbs_FACE )
3293     {
3294       if (( f2hyp = data._face2hyp.find( eos._shapeID )) != data._face2hyp.end() )
3295         eos._hyp = f2hyp->second;
3296     }
3297     else
3298     {
3299       PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
3300       while ( const TopoDS_Shape* face = fIt->next() )
3301       {
3302         TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
3303         if (( f2hyp = data._face2hyp.find( faceID )) != data._face2hyp.end() )
3304           eos._hyp.Add( f2hyp->second );
3305       }
3306     }
3307   }
3308
3309   // set _faceNormals
3310   if ( ! eos._hyp.UseSurfaceNormal() )
3311   {
3312     if ( eos.ShapeType() == TopAbs_FACE ) // get normals to elements on a FACE
3313     {
3314       SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
3315       if ( !smDS ) return;
3316       eos._faceNormals.resize( smDS->NbElements() );
3317
3318       SMDS_ElemIteratorPtr eIt = smDS->GetElements();
3319       for ( int iF = 0; eIt->more(); ++iF )
3320       {
3321         const SMDS_MeshElement* face = eIt->next();
3322         if ( !SMESH_MeshAlgos::FaceNormal( face, eos._faceNormals[iF], /*normalized=*/true ))
3323           eos._faceNormals[iF].SetCoord( 0,0,0 );
3324       }
3325
3326       if ( !helper.IsReversedSubMesh( TopoDS::Face( eos._shape )))
3327         for ( size_t iF = 0; iF < eos._faceNormals.size(); ++iF )
3328           eos._faceNormals[iF].Reverse();
3329     }
3330     else // find EOS of adjacent FACEs
3331     {
3332       PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
3333       while ( const TopoDS_Shape* face = fIt->next() )
3334       {
3335         TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
3336         eos._faceEOS.push_back( & data._edgesOnShape[ faceID ]);
3337         if ( eos._faceEOS.back()->_shape.IsNull() )
3338           // avoid using uninitialised _shapeID in GetNormal()
3339           eos._faceEOS.back()->_shapeID = faceID;
3340       }
3341     }
3342   }
3343 }
3344
3345 //================================================================================
3346 /*!
3347  * \brief Returns normal of a face
3348  */
3349 //================================================================================
3350
3351 bool _EdgesOnShape::GetNormal( const SMDS_MeshElement* face, gp_Vec& norm )
3352 {
3353   bool ok = false;
3354   const _EdgesOnShape* eos = 0;
3355
3356   if ( face->getshapeId() == _shapeID )
3357   {
3358     eos = this;
3359   }
3360   else
3361   {
3362     for ( size_t iF = 0; iF < _faceEOS.size() && !eos; ++iF )
3363       if ( face->getshapeId() == _faceEOS[ iF ]->_shapeID )
3364         eos = _faceEOS[ iF ];
3365   }
3366
3367   if (( eos ) &&
3368       ( ok = ( face->getIdInShape() < (int) eos->_faceNormals.size() )))
3369   {
3370     norm = eos->_faceNormals[ face->getIdInShape() ];
3371   }
3372   else if ( !eos )
3373   {
3374     debugMsg( "_EdgesOnShape::Normal() failed for face "<<face->GetID()
3375               << " on _shape #" << _shapeID );
3376   }
3377   return ok;
3378 }
3379
3380
3381 //================================================================================
3382 /*!
3383  * \brief Set data of _LayerEdge needed for smoothing
3384  */
3385 //================================================================================
3386
3387 bool _ViscousBuilder::setEdgeData(_LayerEdge&         edge,
3388                                   _EdgesOnShape&      eos,
3389                                   SMESH_MesherHelper& helper,
3390                                   _SolidData&         data)
3391 {
3392   const SMDS_MeshNode* node = edge._nodes[0]; // source node
3393
3394   edge._len       = 0;
3395   edge._maxLen    = Precision::Infinite();
3396   edge._minAngle  = 0;
3397   edge._2neibors  = 0;
3398   edge._curvature = 0;
3399   edge._flags     = 0;
3400
3401   // --------------------------
3402   // Compute _normal and _cosin
3403   // --------------------------
3404
3405   edge._cosin     = 0;
3406   edge._lenFactor = 1.;
3407   edge._normal.SetCoord(0,0,0);
3408   _Simplex::GetSimplices( node, edge._simplices, data._ignoreFaceIds, &data );
3409
3410   int totalNbFaces = 0;
3411   TopoDS_Face F;
3412   std::pair< TopoDS_Face, gp_XYZ > face2Norm[20];
3413   gp_Vec geomNorm;
3414   bool normOK = true;
3415
3416   const bool onShrinkShape = !eos._sWOL.IsNull();
3417   const bool useGeometry   = (( eos._hyp.UseSurfaceNormal() ) ||
3418                               ( eos.ShapeType() != TopAbs_FACE /*&& !onShrinkShape*/ ));
3419
3420   // get geom FACEs the node lies on
3421   //if ( useGeometry )
3422   {
3423     set<TGeomID> faceIds;
3424     if  ( eos.ShapeType() == TopAbs_FACE )
3425     {
3426       faceIds.insert( eos._shapeID );
3427     }
3428     else
3429     {
3430       SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
3431       while ( fIt->more() )
3432         faceIds.insert( fIt->next()->getshapeId() );
3433     }
3434     set<TGeomID>::iterator id = faceIds.begin();
3435     for ( ; id != faceIds.end(); ++id )
3436     {
3437       const TopoDS_Shape& s = getMeshDS()->IndexToShape( *id );
3438       if ( s.IsNull() || s.ShapeType() != TopAbs_FACE || data._ignoreFaceIds.count( *id ))
3439         continue;
3440       F = TopoDS::Face( s );
3441       face2Norm[ totalNbFaces ].first = F;
3442       totalNbFaces++;
3443     }
3444   }
3445
3446   // find _normal
3447   bool fromVonF = false;
3448   if ( useGeometry )
3449   {
3450     fromVonF = ( eos.ShapeType() == TopAbs_VERTEX &&
3451                  eos.SWOLType()  == TopAbs_FACE  &&
3452                  totalNbFaces > 1 );
3453
3454     if ( onShrinkShape && !fromVonF ) // one of faces the node is on has no layers
3455     {
3456       if ( eos.SWOLType() == TopAbs_EDGE )
3457       {
3458         // inflate from VERTEX along EDGE
3459         edge._normal = getEdgeDir( TopoDS::Edge( eos._sWOL ), TopoDS::Vertex( eos._shape ));
3460       }
3461       else if ( eos.ShapeType() == TopAbs_VERTEX )
3462       {
3463         // inflate from VERTEX along FACE
3464         edge._normal = getFaceDir( TopoDS::Face( eos._sWOL ), TopoDS::Vertex( eos._shape ),
3465                                    node, helper, normOK, &edge._cosin);
3466       }
3467       else
3468       {
3469         // inflate from EDGE along FACE
3470         edge._normal = getFaceDir( TopoDS::Face( eos._sWOL ), TopoDS::Edge( eos._shape ),
3471                                    node, helper, normOK);
3472       }
3473     }
3474     else // layers are on all FACEs of SOLID the node is on (or fromVonF)
3475     {
3476       if ( fromVonF )
3477         face2Norm[ totalNbFaces++ ].first = TopoDS::Face( eos._sWOL );
3478
3479       int nbOkNorms = 0;
3480       for ( int iF = totalNbFaces - 1; iF >= 0; --iF )
3481       {
3482         F = face2Norm[ iF ].first;
3483         geomNorm = getFaceNormal( node, F, helper, normOK );
3484         if ( !normOK ) continue;
3485         nbOkNorms++;
3486
3487         if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3488           geomNorm.Reverse();
3489         face2Norm[ iF ].second = geomNorm.XYZ();
3490         edge._normal += geomNorm.XYZ();
3491       }
3492       if ( nbOkNorms == 0 )
3493         return error(SMESH_Comment("Can't get normal to node ") << node->GetID(), data._index);
3494
3495       if ( totalNbFaces >= 3 )
3496       {
3497         edge._normal = getNormalByOffset( &edge, face2Norm, totalNbFaces, fromVonF );
3498       }
3499
3500       if ( edge._normal.Modulus() < 1e-3 && nbOkNorms > 1 )
3501       {
3502         // opposite normals, re-get normals at shifted positions (IPAL 52426)
3503         edge._normal.SetCoord( 0,0,0 );
3504         for ( int iF = 0; iF < totalNbFaces - fromVonF; ++iF )
3505         {
3506           const TopoDS_Face& F = face2Norm[iF].first;
3507           geomNorm = getFaceNormal( node, F, helper, normOK, /*shiftInside=*/true );
3508           if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3509             geomNorm.Reverse();
3510           if ( normOK )
3511             face2Norm[ iF ].second = geomNorm.XYZ();
3512           edge._normal += face2Norm[ iF ].second;
3513         }
3514       }
3515     }
3516   }
3517   else // !useGeometry - get _normal using surrounding mesh faces
3518   {
3519     edge._normal = getWeigthedNormal( &edge );
3520
3521     // set<TGeomID> faceIds;
3522     //
3523     // SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
3524     // while ( fIt->more() )
3525     // {
3526     //   const SMDS_MeshElement* face = fIt->next();
3527     //   if ( eos.GetNormal( face, geomNorm ))
3528     //   {
3529     //     if ( onShrinkShape && !faceIds.insert( face->getshapeId() ).second )
3530     //       continue; // use only one mesh face on FACE
3531     //     edge._normal += geomNorm.XYZ();
3532     //     totalNbFaces++;
3533     //   }
3534     // }
3535   }
3536
3537   // compute _cosin
3538   //if ( eos._hyp.UseSurfaceNormal() )
3539   {
3540     switch ( eos.ShapeType() )
3541     {
3542     case TopAbs_FACE: {
3543       edge._cosin = 0;
3544       break;
3545     }
3546     case TopAbs_EDGE: {
3547       TopoDS_Edge E    = TopoDS::Edge( eos._shape );
3548       gp_Vec inFaceDir = getFaceDir( F, E, node, helper, normOK );
3549       double angle     = inFaceDir.Angle( edge._normal ); // [0,PI]
3550       edge._cosin      = Cos( angle );
3551       break;
3552     }
3553     case TopAbs_VERTEX: {
3554       if ( fromVonF )
3555       {
3556         getFaceDir( TopoDS::Face( eos._sWOL ), TopoDS::Vertex( eos._shape ),
3557                     node, helper, normOK, &edge._cosin );
3558       }
3559       else if ( eos.SWOLType() != TopAbs_FACE ) // else _cosin is set by getFaceDir()
3560       {
3561         TopoDS_Vertex V  = TopoDS::Vertex( eos._shape );
3562         gp_Vec inFaceDir = getFaceDir( F, V, node, helper, normOK );
3563         double angle     = inFaceDir.Angle( edge._normal ); // [0,PI]
3564         edge._cosin      = Cos( angle );
3565         if ( totalNbFaces > 2 || helper.IsSeamShape( node->getshapeId() ))
3566           for ( int iF = 1; iF < totalNbFaces; ++iF )
3567           {
3568             F = face2Norm[ iF ].first;
3569             inFaceDir = getFaceDir( F, V, node, helper, normOK=true );
3570             if ( normOK ) {
3571               double angle = inFaceDir.Angle( edge._normal );
3572               double cosin = Cos( angle );
3573               if ( Abs( cosin ) > Abs( edge._cosin ))
3574                 edge._cosin = cosin;
3575             }
3576           }
3577       }
3578       break;
3579     }
3580     default:
3581       return error(SMESH_Comment("Invalid shape position of node ")<<node, data._index);
3582     }
3583   }
3584
3585   double normSize = edge._normal.SquareModulus();
3586   if ( normSize < numeric_limits<double>::min() )
3587     return error(SMESH_Comment("Bad normal at node ")<< node->GetID(), data._index );
3588
3589   edge._normal /= sqrt( normSize );
3590
3591   if ( edge.Is( _LayerEdge::MULTI_NORMAL ) && edge._nodes.size() == 2 )
3592   {
3593     getMeshDS()->RemoveFreeNode( edge._nodes.back(), 0, /*fromGroups=*/false );
3594     edge._nodes.resize( 1 );
3595     edge._normal.SetCoord( 0,0,0 );
3596     edge.SetMaxLen( 0 );
3597   }
3598
3599   // Set the rest data
3600   // --------------------
3601
3602   edge.SetCosin( edge._cosin ); // to update edge._lenFactor
3603
3604   if ( onShrinkShape )
3605   {
3606     const SMDS_MeshNode* tgtNode = edge._nodes.back();
3607     if ( SMESHDS_SubMesh* sm = getMeshDS()->MeshElements( data._solid ))
3608       sm->RemoveNode( tgtNode , /*isNodeDeleted=*/false );
3609
3610     // set initial position which is parameters on _sWOL in this case
3611     if ( eos.SWOLType() == TopAbs_EDGE )
3612     {
3613       double u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), node, 0, &normOK );
3614       edge._pos.push_back( gp_XYZ( u, 0, 0 ));
3615       if ( edge._nodes.size() > 1 )
3616         getMeshDS()->SetNodeOnEdge( tgtNode, TopoDS::Edge( eos._sWOL ), u );
3617     }
3618     else // eos.SWOLType() == TopAbs_FACE
3619     {
3620       gp_XY uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), node, 0, &normOK );
3621       edge._pos.push_back( gp_XYZ( uv.X(), uv.Y(), 0));
3622       if ( edge._nodes.size() > 1 )
3623         getMeshDS()->SetNodeOnFace( tgtNode, TopoDS::Face( eos._sWOL ), uv.X(), uv.Y() );
3624     }
3625
3626     if ( edge._nodes.size() > 1 )
3627     {
3628       // check if an angle between a FACE with layers and SWOL is sharp,
3629       // else the edge should not inflate
3630       F.Nullify();
3631       for ( int iF = 0; iF < totalNbFaces  &&  F.IsNull();  ++iF ) // find a FACE with VL
3632         if ( ! helper.IsSubShape( eos._sWOL, face2Norm[iF].first ))
3633           F = face2Norm[iF].first;
3634       if ( !F.IsNull())
3635       {
3636         geomNorm = getFaceNormal( node, F, helper, normOK );
3637         if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3638           geomNorm.Reverse(); // inside the SOLID
3639         if ( geomNorm * edge._normal < -0.001 )
3640         {
3641           getMeshDS()->RemoveFreeNode( tgtNode, 0, /*fromGroups=*/false );
3642           edge._nodes.resize( 1 );
3643         }
3644         else if ( edge._lenFactor > 3 )
3645         {
3646           edge._lenFactor = 2;
3647           edge.Set( _LayerEdge::RISKY_SWOL );
3648         }
3649       }
3650     }
3651   }
3652   else
3653   {
3654     edge._pos.push_back( SMESH_TNodeXYZ( node ));
3655
3656     if ( eos.ShapeType() == TopAbs_FACE )
3657     {
3658       double angle;
3659       for ( size_t i = 0; i < edge._simplices.size(); ++i )
3660       {
3661         edge._simplices[i].IsMinAngleOK( edge._pos.back(), angle );
3662         edge._minAngle = Max( edge._minAngle, angle ); // "angle" is actually cosine
3663       }
3664     }
3665   }
3666
3667   // Set neighbor nodes for a _LayerEdge based on EDGE
3668
3669   if ( eos.ShapeType() == TopAbs_EDGE /*||
3670        ( onShrinkShape && posType == SMDS_TOP_VERTEX && fabs( edge._cosin ) < 1e-10 )*/)
3671   {
3672     edge._2neibors = new _2NearEdges;
3673     // target nodes instead of source ones will be set later
3674   }
3675
3676   return true;
3677 }
3678
3679 //================================================================================
3680 /*!
3681  * \brief Return normal to a FACE at a node
3682  *  \param [in] n - node
3683  *  \param [in] face - FACE
3684  *  \param [in] helper - helper
3685  *  \param [out] isOK - true or false
3686  *  \param [in] shiftInside - to find normal at a position shifted inside the face
3687  *  \return gp_XYZ - normal
3688  */
3689 //================================================================================
3690
3691 gp_XYZ _ViscousBuilder::getFaceNormal(const SMDS_MeshNode* node,
3692                                       const TopoDS_Face&   face,
3693                                       SMESH_MesherHelper&  helper,
3694                                       bool&                isOK,
3695                                       bool                 shiftInside)
3696 {
3697   gp_XY uv;
3698   if ( shiftInside )
3699   {
3700     // get a shifted position
3701     gp_Pnt p = SMESH_TNodeXYZ( node );
3702     gp_XYZ shift( 0,0,0 );
3703     TopoDS_Shape S = helper.GetSubShapeByNode( node, helper.GetMeshDS() );
3704     switch ( S.ShapeType() ) {
3705     case TopAbs_VERTEX:
3706     {
3707       shift = getFaceDir( face, TopoDS::Vertex( S ), node, helper, isOK );
3708       break;
3709     }
3710     case TopAbs_EDGE:
3711     {
3712       shift = getFaceDir( face, TopoDS::Edge( S ), node, helper, isOK );
3713       break;
3714     }
3715     default:
3716       isOK = false;
3717     }
3718     if ( isOK )
3719       shift.Normalize();
3720     p.Translate( shift * 1e-5 );
3721
3722     TopLoc_Location loc;
3723     GeomAPI_ProjectPointOnSurf& projector = helper.GetProjector( face, loc, 1e-7 );
3724
3725     if ( !loc.IsIdentity() ) p.Transform( loc.Transformation().Inverted() );
3726     
3727     projector.Perform( p );
3728     if ( !projector.IsDone() || projector.NbPoints() < 1 )
3729     {
3730       isOK = false;
3731       return p.XYZ();
3732     }
3733     Standard_Real U,V;
3734     projector.LowerDistanceParameters(U,V);
3735     uv.SetCoord( U,V );
3736   }
3737   else
3738   {
3739     uv = helper.GetNodeUV( face, node, 0, &isOK );
3740   }
3741
3742   gp_Dir normal;
3743   isOK = false;
3744
3745   Handle(Geom_Surface) surface = BRep_Tool::Surface( face );
3746
3747   if ( !shiftInside &&
3748        helper.IsDegenShape( node->getshapeId() ) &&
3749        getFaceNormalAtSingularity( uv, face, helper, normal ))
3750   {
3751     isOK = true;
3752     return normal.XYZ();
3753   }
3754
3755   int pointKind = GeomLib::NormEstim( surface, uv, 1e-5, normal );
3756   enum { REGULAR = 0, QUASYSINGULAR, CONICAL, IMPOSSIBLE };
3757
3758   if ( pointKind == IMPOSSIBLE &&
3759        node->GetPosition()->GetDim() == 2 ) // node inside the FACE
3760   {
3761     // probably NormEstim() failed due to a too high tolerance
3762     pointKind = GeomLib::NormEstim( surface, uv, 1e-20, normal );
3763     isOK = ( pointKind < IMPOSSIBLE );
3764   }
3765   if ( pointKind < IMPOSSIBLE )
3766   {
3767     if ( pointKind != REGULAR &&
3768          !shiftInside &&
3769          node->GetPosition()->GetDim() < 2 ) // FACE boundary
3770     {
3771       gp_XYZ normShift = getFaceNormal( node, face, helper, isOK, /*shiftInside=*/true );
3772       if ( normShift * normal.XYZ() < 0. )
3773         normal = normShift;
3774     }
3775     isOK = true;
3776   }
3777
3778   if ( !isOK ) // hard singularity, to call with shiftInside=true ?
3779   {
3780     const TGeomID faceID = helper.GetMeshDS()->ShapeToIndex( face );
3781
3782     SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
3783     while ( fIt->more() )
3784     {
3785       const SMDS_MeshElement* f = fIt->next();
3786       if ( f->getshapeId() == faceID )
3787       {
3788         isOK = SMESH_MeshAlgos::FaceNormal( f, (gp_XYZ&) normal.XYZ(), /*normalized=*/true );
3789         if ( isOK )
3790         {
3791           TopoDS_Face ff = face;
3792           ff.Orientation( TopAbs_FORWARD );
3793           if ( helper.IsReversedSubMesh( ff ))
3794             normal.Reverse();
3795           break;
3796         }
3797       }
3798     }
3799   }
3800   return normal.XYZ();
3801 }
3802
3803 //================================================================================
3804 /*!
3805  * \brief Try to get normal at a singularity of a surface basing on it's nature
3806  */
3807 //================================================================================
3808
3809 bool _ViscousBuilder::getFaceNormalAtSingularity( const gp_XY&        uv,
3810                                                   const TopoDS_Face&  face,
3811                                                   SMESH_MesherHelper& helper,
3812                                                   gp_Dir&             normal )
3813 {
3814   BRepAdaptor_Surface surface( face );
3815   gp_Dir axis;
3816   if ( !getRovolutionAxis( surface, axis ))
3817     return false;
3818
3819   double f,l, d, du, dv;
3820   f = surface.FirstUParameter();
3821   l = surface.LastUParameter();
3822   d = ( uv.X() - f ) / ( l - f );
3823   du = ( d < 0.5 ? +1. : -1 ) * 1e-5 * ( l - f );
3824   f = surface.FirstVParameter();
3825   l = surface.LastVParameter();
3826   d = ( uv.Y() - f ) / ( l - f );
3827   dv = ( d < 0.5 ? +1. : -1 ) * 1e-5 * ( l - f );
3828
3829   gp_Dir refDir;
3830   gp_Pnt2d testUV = uv;
3831   enum { REGULAR = 0, QUASYSINGULAR, CONICAL, IMPOSSIBLE };
3832   double tol = 1e-5;
3833   Handle(Geom_Surface) geomsurf = surface.Surface().Surface();
3834   for ( int iLoop = 0; true ; ++iLoop )
3835   {
3836     testUV.SetCoord( testUV.X() + du, testUV.Y() + dv );
3837     if ( GeomLib::NormEstim( geomsurf, testUV, tol, refDir ) == REGULAR )
3838       break;
3839     if ( iLoop > 20 )
3840       return false;
3841     tol /= 10.;
3842   }
3843
3844   if ( axis * refDir < 0. )
3845     axis.Reverse();
3846
3847   normal = axis;
3848
3849   return true;
3850 }
3851
3852 //================================================================================
3853 /*!
3854  * \brief Return a normal at a node weighted with angles taken by faces
3855  */
3856 //================================================================================
3857
3858 gp_XYZ _ViscousBuilder::getWeigthedNormal( const _LayerEdge* edge )
3859 {
3860   const SMDS_MeshNode* n = edge->_nodes[0];
3861
3862   gp_XYZ resNorm(0,0,0);
3863   SMESH_TNodeXYZ p0( n ), pP, pN;
3864   for ( size_t i = 0; i < edge->_simplices.size(); ++i )
3865   {
3866     pP.Set( edge->_simplices[i]._nPrev );
3867     pN.Set( edge->_simplices[i]._nNext );
3868     gp_Vec v0P( p0, pP ), v0N( p0, pN ), vPN( pP, pN ), norm = v0P ^ v0N;
3869     double l0P = v0P.SquareMagnitude();
3870     double l0N = v0N.SquareMagnitude();
3871     double lPN = vPN.SquareMagnitude();
3872     if ( l0P < std::numeric_limits<double>::min() ||
3873          l0N < std::numeric_limits<double>::min() ||
3874          lPN < std::numeric_limits<double>::min() )
3875       continue;
3876     double lNorm = norm.SquareMagnitude();
3877     double  sin2 = lNorm / l0P / l0N;
3878     double angle = ACos(( v0P * v0N ) / Sqrt( l0P ) / Sqrt( l0N ));
3879
3880     double weight = sin2 * angle / lPN;
3881     resNorm += weight * norm.XYZ() / Sqrt( lNorm );
3882   }
3883
3884   return resNorm;
3885 }
3886
3887 //================================================================================
3888 /*!
3889  * \brief Return a normal at a node by getting a common point of offset planes
3890  *        defined by the FACE normals
3891  */
3892 //================================================================================
3893
3894 gp_XYZ _ViscousBuilder::getNormalByOffset( _LayerEdge*                      edge,
3895                                            std::pair< TopoDS_Face, gp_XYZ > f2Normal[],
3896                                            int                              nbFaces,
3897                                            bool                             lastNoOffset)
3898 {
3899   SMESH_TNodeXYZ p0 = edge->_nodes[0];
3900
3901   gp_XYZ resNorm(0,0,0);
3902   TopoDS_Shape V = SMESH_MesherHelper::GetSubShapeByNode( p0._node, getMeshDS() );
3903   if ( V.ShapeType() != TopAbs_VERTEX || nbFaces < 3 )
3904   {
3905     for ( int i = 0; i < nbFaces; ++i )
3906       resNorm += f2Normal[i].second;
3907     return resNorm;
3908   }
3909
3910   // prepare _OffsetPlane's
3911   vector< _OffsetPlane > pln( nbFaces );
3912   for ( int i = 0; i < nbFaces - lastNoOffset; ++i )
3913   {
3914     pln[i]._faceIndex = i;
3915     pln[i]._plane = gp_Pln( p0 + f2Normal[i].second, f2Normal[i].second );
3916   }
3917   if ( lastNoOffset )
3918   {
3919     pln[ nbFaces - 1 ]._faceIndex = nbFaces - 1;
3920     pln[ nbFaces - 1 ]._plane = gp_Pln( p0, f2Normal[ nbFaces - 1 ].second );
3921   }
3922
3923   // intersect neighboring OffsetPlane's
3924   PShapeIteratorPtr edgeIt = SMESH_MesherHelper::GetAncestors( V, *_mesh, TopAbs_EDGE );
3925   while ( const TopoDS_Shape* edge = edgeIt->next() )
3926   {
3927     int f1 = -1, f2 = -1;
3928     for ( int i = 0; i < nbFaces &&  f2 < 0;  ++i )
3929       if ( SMESH_MesherHelper::IsSubShape( *edge, f2Normal[i].first ))
3930         (( f1 < 0 ) ? f1 : f2 ) = i;
3931
3932     if ( f2 >= 0 )
3933       pln[ f1 ].ComputeIntersectionLine( pln[ f2 ], TopoDS::Edge( *edge ), TopoDS::Vertex( V ));
3934   }
3935
3936   // get a common point
3937   gp_XYZ commonPnt( 0, 0, 0 );
3938   int nbPoints = 0;
3939   bool isPointFound;
3940   for ( int i = 0; i < nbFaces; ++i )
3941   {
3942     commonPnt += pln[ i ].GetCommonPoint( isPointFound, TopoDS::Vertex( V ));
3943     nbPoints  += isPointFound;
3944   }
3945   gp_XYZ wgtNorm = getWeigthedNormal( edge );
3946   if ( nbPoints == 0 )
3947     return wgtNorm;
3948
3949   commonPnt /= nbPoints;
3950   resNorm = commonPnt - p0;
3951   if ( lastNoOffset )
3952     return resNorm;
3953
3954   // choose the best among resNorm and wgtNorm
3955   resNorm.Normalize();
3956   wgtNorm.Normalize();
3957   double resMinDot = std::numeric_limits<double>::max();
3958   double wgtMinDot = std::numeric_limits<double>::max();
3959   for ( int i = 0; i < nbFaces - lastNoOffset; ++i )
3960   {
3961     resMinDot = Min( resMinDot, resNorm * f2Normal[i].second );
3962     wgtMinDot = Min( wgtMinDot, wgtNorm * f2Normal[i].second );
3963   }
3964
3965   if ( Max( resMinDot, wgtMinDot ) < theMinSmoothCosin )
3966   {
3967     edge->Set( _LayerEdge::MULTI_NORMAL );
3968   }
3969
3970   return ( resMinDot > wgtMinDot ) ? resNorm : wgtNorm;
3971 }
3972
3973 //================================================================================
3974 /*!
3975  * \brief Compute line of intersection of 2 planes
3976  */
3977 //================================================================================
3978
3979 void _OffsetPlane::ComputeIntersectionLine( _OffsetPlane&        pln,
3980                                             const TopoDS_Edge&   E,
3981                                             const TopoDS_Vertex& V )
3982 {
3983   int iNext = bool( _faceIndexNext[0] >= 0 );
3984   _faceIndexNext[ iNext ] = pln._faceIndex;
3985
3986   gp_XYZ n1 = _plane.Axis().Direction().XYZ();
3987   gp_XYZ n2 = pln._plane.Axis().Direction().XYZ();
3988
3989   gp_XYZ lineDir = n1 ^ n2;
3990
3991   double x = Abs( lineDir.X() );
3992   double y = Abs( lineDir.Y() );
3993   double z = Abs( lineDir.Z() );
3994
3995   int cooMax; // max coordinate
3996   if (x > y) {
3997     if (x > z) cooMax = 1;
3998     else       cooMax = 3;
3999   }
4000   else {
4001     if (y > z) cooMax = 2;
4002     else       cooMax = 3;
4003   }
4004
4005   gp_Pnt linePos;
4006   if ( Abs( lineDir.Coord( cooMax )) < 0.05 )
4007   {
4008     // parallel planes - intersection is an offset of the common EDGE
4009     gp_Pnt p = BRep_Tool::Pnt( V );
4010     linePos  = 0.5 * (( p.XYZ() + n1 ) + ( p.XYZ() + n2 ));
4011     lineDir  = getEdgeDir( E, V );
4012   }
4013   else
4014   {
4015     // the constants in the 2 plane equations
4016     double d1 = - ( _plane.Axis().Direction().XYZ()     * _plane.Location().XYZ() );
4017     double d2 = - ( pln._plane.Axis().Direction().XYZ() * pln._plane.Location().XYZ() );
4018
4019     switch ( cooMax ) {
4020     case 1:
4021       linePos.SetX(  0 );
4022       linePos.SetY(( d2*n1.Z() - d1*n2.Z()) / lineDir.X() );
4023       linePos.SetZ(( d1*n2.Y() - d2*n1.Y()) / lineDir.X() );
4024       break;
4025     case 2:
4026       linePos.SetX(( d1*n2.Z() - d2*n1.Z()) / lineDir.Y() );
4027       linePos.SetY(  0 );
4028       linePos.SetZ(( d2*n1.X() - d1*n2.X()) / lineDir.Y() );
4029       break;
4030     case 3:
4031       linePos.SetX(( d2*n1.Y() - d1*n2.Y()) / lineDir.Z() );
4032       linePos.SetY(( d1*n2.X() - d2*n1.X()) / lineDir.Z() );
4033       linePos.SetZ(  0 );
4034     }
4035   }
4036   gp_Lin& line = _lines[ iNext ];
4037   line.SetDirection( lineDir );
4038   line.SetLocation ( linePos );
4039
4040   _isLineOK[ iNext ] = true;
4041
4042
4043   iNext = bool( pln._faceIndexNext[0] >= 0 );
4044   pln._lines        [ iNext ] = line;
4045   pln._faceIndexNext[ iNext ] = this->_faceIndex;
4046   pln._isLineOK     [ iNext ] = true;
4047 }
4048
4049 //================================================================================
4050 /*!
4051  * \brief Computes intersection point of two _lines
4052  */
4053 //================================================================================
4054
4055 gp_XYZ _OffsetPlane::GetCommonPoint(bool&                 isFound,
4056                                     const TopoDS_Vertex & V) const
4057 {
4058   gp_XYZ p( 0,0,0 );
4059   isFound = false;
4060
4061   if ( NbLines() == 2 )
4062   {
4063     gp_Vec lPerp0 = _lines[0].Direction().XYZ() ^ _plane.Axis().Direction().XYZ();
4064     double  dot01 = lPerp0 * _lines[1].Direction().XYZ();
4065     if ( Abs( dot01 ) > 0.05 )
4066     {
4067       gp_Vec l0l1 = _lines[1].Location().XYZ() - _lines[0].Location().XYZ();
4068       double   u1 = - ( lPerp0 * l0l1 ) / dot01;
4069       p = ( _lines[1].Location().XYZ() + _lines[1].Direction().XYZ() * u1 );
4070       isFound = true;
4071     }
4072     else
4073     {
4074       gp_Pnt  pV ( BRep_Tool::Pnt( V ));
4075       gp_Vec  lv0( _lines[0].Location(), pV    ),  lv1(_lines[1].Location(), pV     );
4076       double dot0( lv0 * _lines[0].Direction() ), dot1( lv1 * _lines[1].Direction() );
4077       p += 0.5 * ( _lines[0].Location().XYZ() + _lines[0].Direction().XYZ() * dot0 );
4078       p += 0.5 * ( _lines[1].Location().XYZ() + _lines[1].Direction().XYZ() * dot1 );
4079       isFound = true;
4080     }
4081   }
4082
4083   return p;
4084 }
4085
4086 //================================================================================
4087 /*!
4088  * \brief Find 2 neigbor nodes of a node on EDGE
4089  */
4090 //================================================================================
4091
4092 bool _ViscousBuilder::findNeiborsOnEdge(const _LayerEdge*     edge,
4093                                         const SMDS_MeshNode*& n1,
4094                                         const SMDS_MeshNode*& n2,
4095                                         _EdgesOnShape&        eos,
4096                                         _SolidData&           data)
4097 {
4098   const SMDS_MeshNode* node = edge->_nodes[0];
4099   const int        shapeInd = eos._shapeID;
4100   SMESHDS_SubMesh*   edgeSM = 0;
4101   if ( eos.ShapeType() == TopAbs_EDGE )
4102   {
4103     edgeSM = eos._subMesh->GetSubMeshDS();
4104     if ( !edgeSM || edgeSM->NbElements() == 0 )
4105       return error(SMESH_Comment("Not meshed EDGE ") << shapeInd, data._index);
4106   }
4107   int iN = 0;
4108   n2 = 0;
4109   SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Edge);
4110   while ( eIt->more() && !n2 )
4111   {
4112     const SMDS_MeshElement* e = eIt->next();
4113     const SMDS_MeshNode*   nNeibor = e->GetNode( 0 );
4114     if ( nNeibor == node ) nNeibor = e->GetNode( 1 );
4115     if ( edgeSM )
4116     {
4117       if (!edgeSM->Contains(e)) continue;
4118     }
4119     else
4120     {
4121       TopoDS_Shape s = SMESH_MesherHelper::GetSubShapeByNode( nNeibor, getMeshDS() );
4122       if ( !SMESH_MesherHelper::IsSubShape( s, eos._sWOL )) continue;
4123     }
4124     ( iN++ ? n2 : n1 ) = nNeibor;
4125   }
4126   if ( !n2 )
4127     return error(SMESH_Comment("Wrongly meshed EDGE ") << shapeInd, data._index);
4128   return true;
4129 }
4130
4131 //================================================================================
4132 /*!
4133  * \brief Set _curvature and _2neibors->_plnNorm by 2 neigbor nodes residing the same EDGE
4134  */
4135 //================================================================================
4136
4137 void _LayerEdge::SetDataByNeighbors( const SMDS_MeshNode* n1,
4138                                      const SMDS_MeshNode* n2,
4139                                      const _EdgesOnShape& eos,
4140                                      SMESH_MesherHelper&  helper)
4141 {
4142   if ( eos.ShapeType() != TopAbs_EDGE )
4143     return;
4144   if ( _curvature && Is( SMOOTHED_C1 ))
4145     return;
4146
4147   gp_XYZ  pos = SMESH_TNodeXYZ( _nodes[0] );
4148   gp_XYZ vec1 = pos - SMESH_TNodeXYZ( n1 );
4149   gp_XYZ vec2 = pos - SMESH_TNodeXYZ( n2 );
4150
4151   // Set _curvature
4152
4153   double      sumLen = vec1.Modulus() + vec2.Modulus();
4154   _2neibors->_wgt[0] = 1 - vec1.Modulus() / sumLen;
4155   _2neibors->_wgt[1] = 1 - vec2.Modulus() / sumLen;
4156   double avgNormProj = 0.5 * ( _normal * vec1 + _normal * vec2 );
4157   double      avgLen = 0.5 * ( vec1.Modulus() + vec2.Modulus() );
4158   if ( _curvature ) delete _curvature;
4159   _curvature = _Curvature::New( avgNormProj, avgLen );
4160   // if ( _curvature )
4161   //   debugMsg( _nodes[0]->GetID()
4162   //             << " CURV r,k: " << _curvature->_r<<","<<_curvature->_k
4163   //             << " proj = "<<avgNormProj<< " len = " << avgLen << "| lenDelta(0) = "
4164   //             << _curvature->lenDelta(0) );
4165
4166   // Set _plnNorm
4167
4168   if ( eos._sWOL.IsNull() )
4169   {
4170     TopoDS_Edge  E = TopoDS::Edge( eos._shape );
4171     // if ( SMESH_Algo::isDegenerated( E ))
4172     //   return;
4173     gp_XYZ dirE    = getEdgeDir( E, _nodes[0], helper );
4174     gp_XYZ plnNorm = dirE ^ _normal;
4175     double proj0   = plnNorm * vec1;
4176     double proj1   = plnNorm * vec2;
4177     if ( fabs( proj0 ) > 1e-10 || fabs( proj1 ) > 1e-10 )
4178     {
4179       if ( _2neibors->_plnNorm ) delete _2neibors->_plnNorm;
4180       _2neibors->_plnNorm = new gp_XYZ( plnNorm.Normalized() );
4181     }
4182   }
4183 }
4184
4185 //================================================================================
4186 /*!
4187  * \brief Copy data from a _LayerEdge of other SOLID and based on the same node;
4188  * this and the other _LayerEdge are inflated along a FACE or an EDGE
4189  */
4190 //================================================================================
4191
4192 gp_XYZ _LayerEdge::Copy( _LayerEdge&         other,
4193                          _EdgesOnShape&      eos,
4194                          SMESH_MesherHelper& helper )
4195 {
4196   _nodes     = other._nodes;
4197   _normal    = other._normal;
4198   _len       = 0;
4199   _lenFactor = other._lenFactor;
4200   _cosin     = other._cosin;
4201   _2neibors  = other._2neibors;
4202   _curvature = 0; std::swap( _curvature, other._curvature );
4203   _2neibors  = 0; std::swap( _2neibors,  other._2neibors );
4204
4205   gp_XYZ lastPos( 0,0,0 );
4206   if ( eos.SWOLType() == TopAbs_EDGE )
4207   {
4208     double u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), _nodes[0] );
4209     _pos.push_back( gp_XYZ( u, 0, 0));
4210
4211     u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), _nodes.back() );
4212     lastPos.SetX( u );
4213   }
4214   else // TopAbs_FACE
4215   {
4216     gp_XY uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), _nodes[0]);
4217     _pos.push_back( gp_XYZ( uv.X(), uv.Y(), 0));
4218
4219     uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), _nodes.back() );
4220     lastPos.SetX( uv.X() );
4221     lastPos.SetY( uv.Y() );
4222   }
4223   return lastPos;
4224 }
4225
4226 //================================================================================
4227 /*!
4228  * \brief Set _cosin and _lenFactor
4229  */
4230 //================================================================================
4231
4232 void _LayerEdge::SetCosin( double cosin )
4233 {
4234   _cosin = cosin;
4235   cosin = Abs( _cosin );
4236   //_lenFactor = ( cosin < 1.-1e-12 ) ?  Min( 2., 1./sqrt(1-cosin*cosin )) : 1.0;
4237   _lenFactor = ( cosin < 1.-1e-12 ) ?  1./sqrt(1-cosin*cosin ) : 1.0;
4238 }
4239
4240 //================================================================================
4241 /*!
4242  * \brief Check if another _LayerEdge is a neighbor on EDGE
4243  */
4244 //================================================================================
4245
4246 bool _LayerEdge::IsNeiborOnEdge( const _LayerEdge* edge ) const
4247 {
4248   return (( this->_2neibors && this->_2neibors->include( edge )) ||
4249           ( edge->_2neibors && edge->_2neibors->include( this )));
4250 }
4251
4252 //================================================================================
4253 /*!
4254  * \brief Fills a vector<_Simplex > 
4255  */
4256 //================================================================================
4257
4258 void _Simplex::GetSimplices( const SMDS_MeshNode* node,
4259                              vector<_Simplex>&    simplices,
4260                              const set<TGeomID>&  ingnoreShapes,
4261                              const _SolidData*    dataToCheckOri,
4262                              const bool           toSort)
4263 {
4264   simplices.clear();
4265   SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
4266   while ( fIt->more() )
4267   {
4268     const SMDS_MeshElement* f = fIt->next();
4269     const TGeomID    shapeInd = f->getshapeId();
4270     if ( ingnoreShapes.count( shapeInd )) continue;
4271     const int nbNodes = f->NbCornerNodes();
4272     const int  srcInd = f->GetNodeIndex( node );
4273     const SMDS_MeshNode* nPrev = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd-1, nbNodes ));
4274     const SMDS_MeshNode* nNext = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd+1, nbNodes ));
4275     const SMDS_MeshNode* nOpp  = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd+2, nbNodes ));
4276     if ( dataToCheckOri && dataToCheckOri->_reversedFaceIds.count( shapeInd ))
4277       std::swap( nPrev, nNext );
4278     simplices.push_back( _Simplex( nPrev, nNext, ( nbNodes == 3 ? 0 : nOpp )));
4279   }
4280
4281   if ( toSort )
4282     SortSimplices( simplices );
4283 }
4284
4285 //================================================================================
4286 /*!
4287  * \brief Set neighbor simplices side by side
4288  */
4289 //================================================================================
4290
4291 void _Simplex::SortSimplices(vector<_Simplex>& simplices)
4292 {
4293   vector<_Simplex> sortedSimplices( simplices.size() );
4294   sortedSimplices[0] = simplices[0];
4295   size_t nbFound = 0;
4296   for ( size_t i = 1; i < simplices.size(); ++i )
4297   {
4298     for ( size_t j = 1; j < simplices.size(); ++j )
4299       if ( sortedSimplices[i-1]._nNext == simplices[j]._nPrev )
4300       {
4301         sortedSimplices[i] = simplices[j];
4302         nbFound++;
4303         break;
4304       }
4305   }
4306   if ( nbFound == simplices.size() - 1 )
4307     simplices.swap( sortedSimplices );
4308 }
4309
4310 //================================================================================
4311 /*!
4312  * \brief DEBUG. Create groups containing temporary data of _LayerEdge's
4313  */
4314 //================================================================================
4315
4316 void _ViscousBuilder::makeGroupOfLE()
4317 {
4318 #ifdef _DEBUG_
4319   for ( size_t i = 0 ; i < _sdVec.size(); ++i )
4320   {
4321     if ( _sdVec[i]._n2eMap.empty() ) continue;
4322
4323     dumpFunction( SMESH_Comment("make_LayerEdge_") << i );
4324     TNode2Edge::iterator n2e;
4325     for ( n2e = _sdVec[i]._n2eMap.begin(); n2e != _sdVec[i]._n2eMap.end(); ++n2e )
4326     {
4327       _LayerEdge* le = n2e->second;
4328       // for ( size_t iN = 1; iN < le->_nodes.size(); ++iN )
4329       //   dumpCmd(SMESH_Comment("mesh.AddEdge([ ") <<le->_nodes[iN-1]->GetID()
4330       //           << ", " << le->_nodes[iN]->GetID() <<"])");
4331       if ( le ) {
4332         dumpCmd(SMESH_Comment("mesh.AddEdge([ ") <<le->_nodes[0]->GetID()
4333                 << ", " << le->_nodes.back()->GetID() <<"]) # " << le->_flags );
4334       }
4335     }
4336     dumpFunctionEnd();
4337
4338     dumpFunction( SMESH_Comment("makeNormals") << i );
4339     for ( n2e = _sdVec[i]._n2eMap.begin(); n2e != _sdVec[i]._n2eMap.end(); ++n2e )
4340     {
4341       _LayerEdge* edge = n2e->second;
4342       SMESH_TNodeXYZ nXYZ( edge->_nodes[0] );
4343       nXYZ += edge->_normal * _sdVec[i]._stepSize;
4344       dumpCmd(SMESH_Comment("mesh.AddEdge([ ") << edge->_nodes[0]->GetID()
4345               << ", mesh.AddNode( "<< nXYZ.X()<<","<< nXYZ.Y()<<","<< nXYZ.Z()<<")])");
4346     }
4347     dumpFunctionEnd();
4348
4349     dumpFunction( SMESH_Comment("makeTmpFaces_") << i );
4350     dumpCmd( "faceId1 = mesh.NbElements()" );
4351     TopExp_Explorer fExp( _sdVec[i]._solid, TopAbs_FACE );
4352     for ( ; fExp.More(); fExp.Next() )
4353     {
4354       if ( const SMESHDS_SubMesh* sm = _sdVec[i]._proxyMesh->GetProxySubMesh( fExp.Current() ))
4355       {
4356         if ( sm->NbElements() == 0 ) continue;
4357         SMDS_ElemIteratorPtr fIt = sm->GetElements();
4358         while ( fIt->more())
4359         {
4360           const SMDS_MeshElement* e = fIt->next();
4361           SMESH_Comment cmd("mesh.AddFace([");
4362           for ( int j = 0; j < e->NbCornerNodes(); ++j )
4363             cmd << e->GetNode(j)->GetID() << (j+1 < e->NbCornerNodes() ? ",": "])");
4364           dumpCmd( cmd );
4365         }
4366       }
4367     }
4368     dumpCmd( "faceId2 = mesh.NbElements()" );
4369     dumpCmd( SMESH_Comment( "mesh.MakeGroup( 'tmpFaces_" ) << i << "',"
4370              << "SMESH.FACE, SMESH.FT_RangeOfIds,'=',"
4371              << "'%s-%s' % (faceId1+1, faceId2))");
4372     dumpFunctionEnd();
4373   }
4374 #endif
4375 }
4376
4377 //================================================================================
4378 /*!
4379  * \brief Find maximal _LayerEdge length (layer thickness) limited by geometry
4380  */
4381 //================================================================================
4382
4383 void _ViscousBuilder::computeGeomSize( _SolidData& data )
4384 {
4385   data._geomSize = Precision::Infinite();
4386   double intersecDist;
4387   const SMDS_MeshElement* face;
4388   SMESH_MesherHelper helper( *_mesh );
4389
4390   SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
4391     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
4392                                            data._proxyMesh->GetFaces( data._solid )));
4393
4394   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4395   {
4396     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4397     if ( eos._edges.empty() )
4398       continue;
4399     // get neighbor faces, intersection with which should not be considered since
4400     // collisions are avoided by means of smoothing
4401     set< TGeomID > neighborFaces;
4402     if ( eos._hyp.ToSmooth() )
4403     {
4404       SMESH_subMeshIteratorPtr subIt =
4405         eos._subMesh->getDependsOnIterator(/*includeSelf=*/eos.ShapeType() != TopAbs_FACE );
4406       while ( subIt->more() )
4407       {
4408         SMESH_subMesh* sm = subIt->next();
4409         PShapeIteratorPtr fIt = helper.GetAncestors( sm->GetSubShape(), *_mesh, TopAbs_FACE );
4410         while ( const TopoDS_Shape* face = fIt->next() )
4411           neighborFaces.insert( getMeshDS()->ShapeToIndex( *face ));
4412       }
4413     }
4414     // find intersections
4415     double thinkness = eos._hyp.GetTotalThickness();
4416     for ( size_t i = 0; i < eos._edges.size(); ++i )
4417     {
4418       if ( eos._edges[i]->Is( _LayerEdge::BLOCKED )) continue;
4419       eos._edges[i]->SetMaxLen( thinkness );
4420       eos._edges[i]->FindIntersection( *searcher, intersecDist, data._epsilon, eos, &face );
4421       if ( intersecDist > 0 && face )
4422       {
4423         data._geomSize = Min( data._geomSize, intersecDist );
4424         if ( !neighborFaces.count( face->getshapeId() ))
4425           eos[i]->SetMaxLen( Min( thinkness, intersecDist / ( face->GetID() < 0 ? 3. : 2. )));
4426       }
4427     }
4428   }
4429
4430   data._maxThickness = 0;
4431   data._minThickness = 1e100;
4432   list< const StdMeshers_ViscousLayers* >::iterator hyp = data._hyps.begin();
4433   for ( ; hyp != data._hyps.end(); ++hyp )
4434   {
4435     data._maxThickness = Max( data._maxThickness, (*hyp)->GetTotalThickness() );
4436     data._minThickness = Min( data._minThickness, (*hyp)->GetTotalThickness() );
4437   }
4438
4439   // Limit inflation step size by geometry size found by intersecting
4440   // normals of _LayerEdge's with mesh faces
4441   if ( data._stepSize > 0.3 * data._geomSize )
4442     limitStepSize( data, 0.3 * data._geomSize );
4443
4444   if ( data._stepSize > data._minThickness )
4445     limitStepSize( data, data._minThickness );
4446
4447
4448   // -------------------------------------------------------------------------
4449   // Detect _LayerEdge which can't intersect with opposite or neighbor layer,
4450   // so no need in detecting intersection at each inflation step
4451   // -------------------------------------------------------------------------
4452
4453   int nbSteps = data._maxThickness / data._stepSize;
4454   if ( nbSteps < 3 || nbSteps * data._n2eMap.size() < 100000 )
4455     return;
4456
4457   vector< const SMDS_MeshElement* > closeFaces;
4458   int nbDetected = 0;
4459
4460   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4461   {
4462     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4463     if ( eos._edges.empty() || eos.ShapeType() != TopAbs_FACE )
4464       continue;
4465
4466     for ( size_t i = 0; i < eos.size(); ++i )
4467     {
4468       SMESH_NodeXYZ p( eos[i]->_nodes[0] );
4469       double radius = data._maxThickness + 2 * eos[i]->_maxLen;
4470       closeFaces.clear();
4471       searcher->GetElementsInSphere( p, radius, SMDSAbs_Face, closeFaces );
4472
4473       bool toIgnore = true;
4474       for ( size_t iF = 0; iF < closeFaces.size()  && toIgnore; ++iF )
4475         if ( !( toIgnore = ( closeFaces[ iF ]->getshapeId() == eos._shapeID ||
4476                              data._ignoreFaceIds.count( closeFaces[ iF ]->getshapeId() ))))
4477         {
4478           // check if a _LayerEdge will inflate in a direction opposite to a direction
4479           // toward a close face
4480           bool allBehind = true;
4481           for ( int iN = 0; iN < closeFaces[ iF ]->NbCornerNodes()  && allBehind; ++iN )
4482           {
4483             SMESH_NodeXYZ pi( closeFaces[ iF ]->GetNode( iN ));
4484             allBehind = (( pi - p ) * eos[i]->_normal < 0.1 * data._stepSize );
4485           }
4486           toIgnore = allBehind;
4487         }
4488
4489
4490       if ( toIgnore ) // no need to detect intersection
4491       {
4492         eos[i]->Set( _LayerEdge::INTERSECTED );
4493         ++nbDetected;
4494       }
4495     }
4496   }
4497
4498   debugMsg( "Nb LE to intersect " << data._n2eMap.size()-nbDetected << ", ignore " << nbDetected );
4499
4500   return;
4501 }
4502
4503 //================================================================================
4504 /*!
4505  * \brief Increase length of _LayerEdge's to reach the required thickness of layers
4506  */
4507 //================================================================================
4508
4509 bool _ViscousBuilder::inflate(_SolidData& data)
4510 {
4511   SMESH_MesherHelper helper( *_mesh );
4512
4513   const double tgtThick = data._maxThickness;
4514
4515   if ( data._stepSize < 1. )
4516     data._epsilon = data._stepSize * 1e-7;
4517
4518   debugMsg( "-- geomSize = " << data._geomSize << ", stepSize = " << data._stepSize );
4519   _pyDump->Pause();
4520
4521   findCollisionEdges( data, helper );
4522
4523   limitMaxLenByCurvature( data, helper );
4524
4525   _pyDump->Resume();
4526
4527   // limit length of _LayerEdge's around MULTI_NORMAL _LayerEdge's
4528   for ( size_t i = 0; i < data._edgesOnShape.size(); ++i )
4529     if ( data._edgesOnShape[i].ShapeType() == TopAbs_VERTEX &&
4530          data._edgesOnShape[i]._edges.size() > 0 &&
4531          data._edgesOnShape[i]._edges[0]->Is( _LayerEdge::MULTI_NORMAL ))
4532     {
4533       data._edgesOnShape[i]._edges[0]->Unset( _LayerEdge::BLOCKED );
4534       data._edgesOnShape[i]._edges[0]->Block( data );
4535     }
4536
4537   const double safeFactor = ( 2*data._maxThickness < data._geomSize ) ? 1 : theThickToIntersection;
4538
4539   double avgThick = 0, curThick = 0, distToIntersection = Precision::Infinite();
4540   int nbSteps = 0, nbRepeats = 0;
4541   while ( avgThick < 0.99 )
4542   {
4543     // new target length
4544     double prevThick = curThick;
4545     curThick += data._stepSize;
4546     if ( curThick > tgtThick )
4547     {
4548       curThick = tgtThick + tgtThick*( 1.-avgThick ) * nbRepeats;
4549       nbRepeats++;
4550     }
4551
4552     double stepSize = curThick - prevThick;
4553     updateNormalsOfSmoothed( data, helper, nbSteps, stepSize ); // to ease smoothing
4554
4555     // Elongate _LayerEdge's
4556     dumpFunction(SMESH_Comment("inflate")<<data._index<<"_step"<<nbSteps); // debug
4557     for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4558     {
4559       _EdgesOnShape& eos = data._edgesOnShape[iS];
4560       if ( eos._edges.empty() ) continue;
4561
4562       const double shapeCurThick = Min( curThick, eos._hyp.GetTotalThickness() );
4563       for ( size_t i = 0; i < eos._edges.size(); ++i )
4564       {
4565         eos._edges[i]->SetNewLength( shapeCurThick, eos, helper );
4566       }
4567     }
4568     dumpFunctionEnd();
4569
4570     if ( !updateNormals( data, helper, nbSteps, stepSize )) // to avoid collisions
4571       return false;
4572
4573     // Improve and check quality
4574     if ( !smoothAndCheck( data, nbSteps, distToIntersection ))
4575     {
4576       if ( nbSteps > 0 )
4577       {
4578 #ifdef __NOT_INVALIDATE_BAD_SMOOTH
4579         debugMsg("NOT INVALIDATED STEP!");
4580         return error("Smoothing failed", data._index);
4581 #endif
4582         dumpFunction(SMESH_Comment("invalidate")<<data._index<<"_step"<<nbSteps); // debug
4583         for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4584         {
4585           _EdgesOnShape& eos = data._edgesOnShape[iS];
4586           for ( size_t i = 0; i < eos._edges.size(); ++i )
4587             eos._edges[i]->InvalidateStep( nbSteps+1, eos );
4588         }
4589         dumpFunctionEnd();
4590       }
4591       break; // no more inflating possible
4592     }
4593     nbSteps++;
4594
4595     // Evaluate achieved thickness
4596     avgThick = 0;
4597     int nbActiveEdges = 0;
4598     for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4599     {
4600       _EdgesOnShape& eos = data._edgesOnShape[iS];
4601       if ( eos._edges.empty() ) continue;
4602
4603       const double shapeTgtThick = eos._hyp.GetTotalThickness();
4604       for ( size_t i = 0; i < eos._edges.size(); ++i )
4605       {
4606         if ( eos._edges[i]->_nodes.size() > 1 )
4607           avgThick    += Min( 1., eos._edges[i]->_len / shapeTgtThick );
4608         else
4609           avgThick    += shapeTgtThick;
4610         nbActiveEdges += ( ! eos._edges[i]->Is( _LayerEdge::BLOCKED ));
4611       }
4612     }
4613     avgThick /= data._n2eMap.size();
4614     debugMsg( "-- Thickness " << curThick << " ("<< avgThick*100 << "%) reached" );
4615
4616 #ifdef BLOCK_INFLATION
4617     if ( nbActiveEdges == 0 )
4618     {
4619       debugMsg( "-- Stop inflation since all _LayerEdge's BLOCKED " );
4620       break;
4621     }
4622 #else
4623     if ( distToIntersection < tgtThick * avgThick * safeFactor && avgThick < 0.9 )
4624     {
4625       debugMsg( "-- Stop inflation since "
4626                 << " distToIntersection( "<<distToIntersection<<" ) < avgThick( "
4627                 << tgtThick * avgThick << " ) * " << safeFactor );
4628       break;
4629     }
4630 #endif
4631
4632     // new step size
4633     limitStepSize( data, 0.25 * distToIntersection );
4634     if ( data._stepSizeNodes[0] )
4635       data._stepSize = data._stepSizeCoeff *
4636         SMESH_TNodeXYZ(data._stepSizeNodes[0]).Distance(data._stepSizeNodes[1]);
4637
4638   } // while ( avgThick < 0.99 )
4639
4640   if ( nbSteps == 0 )
4641     return error("failed at the very first inflation step", data._index);
4642
4643   if ( avgThick < 0.99 )
4644   {
4645     if ( !data._proxyMesh->_warning || data._proxyMesh->_warning->IsOK() )
4646     {
4647       data._proxyMesh->_warning.reset
4648         ( new SMESH_ComputeError (COMPERR_WARNING,
4649                                   SMESH_Comment("Thickness ") << tgtThick <<
4650                                   " of viscous layers not reached,"
4651                                   " average reached thickness is " << avgThick*tgtThick));
4652     }
4653   }
4654
4655   // Restore position of src nodes moved by inflation on _noShrinkShapes
4656   dumpFunction(SMESH_Comment("restoNoShrink_So")<<data._index); // debug
4657   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4658   {
4659     _EdgesOnShape& eos = data._edgesOnShape[iS];
4660     if ( !eos._edges.empty() && eos._edges[0]->_nodes.size() == 1 )
4661       for ( size_t i = 0; i < eos._edges.size(); ++i )
4662       {
4663         restoreNoShrink( *eos._edges[ i ] );
4664       }
4665   }
4666   dumpFunctionEnd();
4667
4668   return safeFactor > 0; // == true (avoid warning: unused variable 'safeFactor')
4669 }
4670
4671 //================================================================================
4672 /*!
4673  * \brief Improve quality of layer inner surface and check intersection
4674  */
4675 //================================================================================
4676
4677 bool _ViscousBuilder::smoothAndCheck(_SolidData& data,
4678                                      const int   infStep,
4679                                      double &    distToIntersection)
4680 {
4681   if ( data._nbShapesToSmooth == 0 )
4682     return true; // no shapes needing smoothing
4683
4684   bool moved, improved;
4685   double vol;
4686   vector< _LayerEdge* >    movedEdges, badEdges;
4687   vector< _EdgesOnShape* > eosC1; // C1 continues shapes
4688   vector< bool >           isConcaveFace;
4689
4690   SMESH_MesherHelper helper(*_mesh);
4691   Handle(ShapeAnalysis_Surface) surface;
4692   TopoDS_Face F;
4693
4694   for ( int isFace = 0; isFace < 2; ++isFace ) // smooth on [ EDGEs, FACEs ]
4695   {
4696     const TopAbs_ShapeEnum shapeType = isFace ? TopAbs_FACE : TopAbs_EDGE;
4697
4698     for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4699     {
4700       _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4701       if ( !eos._toSmooth ||
4702            eos.ShapeType() != shapeType ||
4703            eos._edges.empty() )
4704         continue;
4705
4706       // already smoothed?
4707       // bool toSmooth = ( eos._edges[ 0 ]->NbSteps() >= infStep+1 );
4708       // if ( !toSmooth ) continue;
4709
4710       if ( !eos._hyp.ToSmooth() )
4711       {
4712         // smooth disabled by the user; check validy only
4713         if ( !isFace ) continue;
4714         badEdges.clear();
4715         for ( size_t i = 0; i < eos._edges.size(); ++i )
4716         {
4717           _LayerEdge* edge = eos._edges[i];
4718           for ( size_t iF = 0; iF < edge->_simplices.size(); ++iF )
4719             if ( !edge->_simplices[iF].IsForward( edge->_nodes[0], edge->_pos.back(), vol ))
4720             {
4721               // debugMsg( "-- Stop inflation. Bad simplex ("
4722               //           << " "<< edge->_nodes[0]->GetID()
4723               //           << " "<< edge->_nodes.back()->GetID()
4724               //           << " "<< edge->_simplices[iF]._nPrev->GetID()
4725               //           << " "<< edge->_simplices[iF]._nNext->GetID() << " ) ");
4726               // return false;
4727               badEdges.push_back( edge );
4728             }
4729         }
4730         if ( !badEdges.empty() )
4731         {
4732           eosC1.resize(1);
4733           eosC1[0] = &eos;
4734           int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
4735           if ( nbBad > 0 )
4736             return false;
4737         }
4738         continue; // goto the next EDGE or FACE
4739       }
4740
4741       // prepare data
4742       if ( eos.SWOLType() == TopAbs_FACE )
4743       {
4744         if ( !F.IsSame( eos._sWOL )) {
4745           F = TopoDS::Face( eos._sWOL );
4746           helper.SetSubShape( F );
4747           surface = helper.GetSurface( F );
4748         }
4749       }
4750       else
4751       {
4752         F.Nullify(); surface.Nullify();
4753       }
4754       const TGeomID sInd = eos._shapeID;
4755
4756       // perform smoothing
4757
4758       if ( eos.ShapeType() == TopAbs_EDGE )
4759       {
4760         dumpFunction(SMESH_Comment("smooth")<<data._index << "_Ed"<<sInd <<"_InfStep"<<infStep);
4761
4762         if ( !eos._edgeSmoother->Perform( data, surface, F, helper ))
4763         {
4764           // smooth on EDGE's (normally we should not get here)
4765           int step = 0;
4766           do {
4767             moved = false;
4768             for ( size_t i = 0; i < eos._edges.size(); ++i )
4769             {
4770               moved |= eos._edges[i]->SmoothOnEdge( surface, F, helper );
4771             }
4772             dumpCmd( SMESH_Comment("# end step ")<<step);
4773           }
4774           while ( moved && step++ < 5 );
4775         }
4776         dumpFunctionEnd();
4777       }
4778
4779       else // smooth on FACE
4780       {
4781         eosC1.clear();
4782         eosC1.push_back( & eos );
4783         eosC1.insert( eosC1.end(), eos._eosC1.begin(), eos._eosC1.end() );
4784
4785         movedEdges.clear();
4786         isConcaveFace.resize( eosC1.size() );
4787         for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
4788         {
4789           isConcaveFace[ iEOS ] = data._concaveFaces.count( eosC1[ iEOS ]->_shapeID  );
4790           vector< _LayerEdge* > & edges = eosC1[ iEOS ]->_edges;
4791           for ( size_t i = 0; i < edges.size(); ++i )
4792             if ( edges[i]->Is( _LayerEdge::MOVED ) ||
4793                  edges[i]->Is( _LayerEdge::NEAR_BOUNDARY ))
4794               movedEdges.push_back( edges[i] );
4795
4796           makeOffsetSurface( *eosC1[ iEOS ], helper );
4797         }
4798
4799         int step = 0, stepLimit = 5, nbBad = 0;
4800         while (( ++step <= stepLimit ) || improved )
4801         {
4802           dumpFunction(SMESH_Comment("smooth")<<data._index<<"_Fa"<<sInd
4803                        <<"_InfStep"<<infStep<<"_"<<step); // debug
4804           int oldBadNb = nbBad;
4805           badEdges.clear();
4806
4807 #ifdef INCREMENTAL_SMOOTH
4808           bool findBest = false; // ( step == stepLimit );
4809           for ( size_t i = 0; i < movedEdges.size(); ++i )
4810           {
4811             movedEdges[i]->Unset( _LayerEdge::SMOOTHED );
4812             if ( movedEdges[i]->Smooth( step, findBest, movedEdges ) > 0 )
4813               badEdges.push_back( movedEdges[i] );
4814           }
4815 #else
4816           bool findBest = ( step == stepLimit || isConcaveFace[ iEOS ]);
4817           for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
4818           {
4819             vector< _LayerEdge* > & edges = eosC1[ iEOS ]->_edges;
4820             for ( size_t i = 0; i < edges.size(); ++i )
4821             {
4822               edges[i]->Unset( _LayerEdge::SMOOTHED );
4823               if ( edges[i]->Smooth( step, findBest, false ) > 0 )
4824                 badEdges.push_back( eos._edges[i] );
4825             }
4826           }
4827 #endif
4828           nbBad = badEdges.size();
4829
4830           if ( nbBad > 0 )
4831             debugMsg(SMESH_Comment("nbBad = ") << nbBad );
4832
4833           if ( !badEdges.empty() && step >= stepLimit / 2 )
4834           {
4835             if ( badEdges[0]->Is( _LayerEdge::ON_CONCAVE_FACE ))
4836               stepLimit = 9;
4837
4838             // resolve hard smoothing situation around concave VERTEXes
4839             for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
4840             {
4841               vector< _EdgesOnShape* > & eosCoVe = eosC1[ iEOS ]->_eosConcaVer;
4842               for ( size_t i = 0; i < eosCoVe.size(); ++i )
4843                 eosCoVe[i]->_edges[0]->MoveNearConcaVer( eosCoVe[i], eosC1[ iEOS ],
4844                                                          step, badEdges );
4845             }
4846             // look for the best smooth of _LayerEdge's neighboring badEdges
4847             nbBad = 0;
4848             for ( size_t i = 0; i < badEdges.size(); ++i )
4849             {
4850               _LayerEdge* ledge = badEdges[i];
4851               for ( size_t iN = 0; iN < ledge->_neibors.size(); ++iN )
4852               {
4853                 ledge->_neibors[iN]->Unset( _LayerEdge::SMOOTHED );
4854                 nbBad += ledge->_neibors[iN]->Smooth( step, true, /*findBest=*/true );
4855               }
4856               ledge->Unset( _LayerEdge::SMOOTHED );
4857               nbBad += ledge->Smooth( step, true, /*findBest=*/true );
4858             }
4859             debugMsg(SMESH_Comment("nbBad = ") << nbBad );
4860           }
4861
4862           if ( nbBad == oldBadNb  &&
4863                nbBad > 0 &&
4864                step < stepLimit ) // smooth w/o chech of validity
4865           {
4866             dumpFunctionEnd();
4867             dumpFunction(SMESH_Comment("smoothWoCheck")<<data._index<<"_Fa"<<sInd
4868                          <<"_InfStep"<<infStep<<"_"<<step); // debug
4869             for ( size_t i = 0; i < movedEdges.size(); ++i )
4870             {
4871               movedEdges[i]->SmoothWoCheck();
4872             }
4873             if ( stepLimit < 9 )
4874               stepLimit++;
4875           }
4876
4877           improved = ( nbBad < oldBadNb );
4878
4879           dumpFunctionEnd();
4880
4881           if (( step % 3 == 1 ) || ( nbBad > 0 && step >= stepLimit / 2 ))
4882             for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
4883             {
4884               putOnOffsetSurface( *eosC1[ iEOS ], infStep, eosC1, step, /*moveAll=*/step == 1 );
4885             }
4886
4887         } // smoothing steps
4888
4889         // project -- to prevent intersections or fix bad simplices
4890         for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
4891         {
4892           if ( ! eosC1[ iEOS ]->_eosConcaVer.empty() || nbBad > 0 )
4893             putOnOffsetSurface( *eosC1[ iEOS ], infStep, eosC1 );
4894         }
4895
4896         //if ( !badEdges.empty() )
4897         {
4898           badEdges.clear();
4899           for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
4900           {
4901             for ( size_t i = 0; i < eosC1[ iEOS ]->_edges.size(); ++i )
4902             {
4903               if ( !eosC1[ iEOS ]->_sWOL.IsNull() ) continue;
4904
4905               _LayerEdge* edge = eosC1[ iEOS ]->_edges[i];
4906               edge->CheckNeiborsOnBoundary( & badEdges );
4907               if (( nbBad > 0 ) ||
4908                   ( edge->Is( _LayerEdge::BLOCKED ) && edge->Is( _LayerEdge::NEAR_BOUNDARY )))
4909               {
4910                 SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
4911                 gp_XYZ        prevXYZ = edge->PrevCheckPos();
4912                 for ( size_t j = 0; j < edge->_simplices.size(); ++j )
4913                   if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
4914                   {
4915                     debugMsg("Bad simplex ( " << edge->_nodes[0]->GetID()
4916                              << " "<< tgtXYZ._node->GetID()
4917                              << " "<< edge->_simplices[j]._nPrev->GetID()
4918                              << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
4919                     badEdges.push_back( edge );
4920                     break;
4921                   }
4922               }
4923             }
4924           }
4925
4926           // try to fix bad simplices by removing the last inflation step of some _LayerEdge's
4927           nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
4928
4929           if ( nbBad > 0 )
4930             return false;
4931         }
4932
4933       } // // smooth on FACE's
4934     } // loop on shapes
4935   } // smooth on [ EDGEs, FACEs ]
4936
4937   // Check orientation of simplices of _LayerEdge's on EDGEs and VERTEXes
4938   eosC1.resize(1);
4939   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4940   {
4941     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4942     if ( eos.ShapeType() == TopAbs_FACE ||
4943          eos._edges.empty() ||
4944          !eos._sWOL.IsNull() )
4945       continue;
4946
4947     badEdges.clear();
4948     for ( size_t i = 0; i < eos._edges.size(); ++i )
4949     {
4950       _LayerEdge*      edge = eos._edges[i];
4951       if ( edge->_nodes.size() < 2 ) continue;
4952       SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
4953       //SMESH_TNodeXYZ prevXYZ = edge->_nodes[0];
4954       gp_XYZ        prevXYZ = edge->PrevCheckPos( &eos );
4955       //const gp_XYZ& prevXYZ = edge->PrevPos();
4956       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
4957         if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
4958         {
4959           debugMsg("Bad simplex on bnd ( " << edge->_nodes[0]->GetID()
4960                    << " "<< tgtXYZ._node->GetID()
4961                    << " "<< edge->_simplices[j]._nPrev->GetID()
4962                    << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
4963           badEdges.push_back( edge );
4964           break;
4965         }
4966     }
4967
4968     // try to fix bad simplices by removing the last inflation step of some _LayerEdge's
4969     eosC1[0] = &eos;
4970     int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
4971     if ( nbBad > 0 )
4972       return false;
4973   }
4974
4975
4976   // Check if the last segments of _LayerEdge intersects 2D elements;
4977   // checked elements are either temporary faces or faces on surfaces w/o the layers
4978
4979   SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
4980     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
4981                                            data._proxyMesh->GetFaces( data._solid )) );
4982
4983 #ifdef BLOCK_INFLATION
4984   const bool toBlockInfaltion = true;
4985 #else
4986   const bool toBlockInfaltion = false;
4987 #endif
4988   distToIntersection = Precision::Infinite();
4989   double dist;
4990   const SMDS_MeshElement* intFace = 0;
4991   const SMDS_MeshElement* closestFace = 0;
4992   _LayerEdge* le = 0;
4993   bool is1stBlocked = true; // dbg
4994   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4995   {
4996     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4997     if ( eos._edges.empty() || !eos._sWOL.IsNull() )
4998       continue;
4999     for ( size_t i = 0; i < eos._edges.size(); ++i )
5000     {
5001       if ( eos._edges[i]->Is( _LayerEdge::INTERSECTED ) ||
5002            eos._edges[i]->Is( _LayerEdge::MULTI_NORMAL ))
5003         continue;
5004       if ( eos._edges[i]->FindIntersection( *searcher, dist, data._epsilon, eos, &intFace ))
5005       {
5006         return false;
5007         // commented due to "Illegal hash-positionPosition" error in NETGEN
5008         // on Debian60 on viscous_layers_01/B2 case
5009         // Collision; try to deflate _LayerEdge's causing it
5010         // badEdges.clear();
5011         // badEdges.push_back( eos._edges[i] );
5012         // eosC1[0] = & eos;
5013         // int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5014         // if ( nbBad > 0 )
5015         //   return false;
5016
5017         // badEdges.clear();
5018         // if ( _EdgesOnShape* eof = data.GetShapeEdges( intFace->getshapeId() ))
5019         // {
5020         //   if ( const _TmpMeshFace* f = dynamic_cast< const _TmpMeshFace*>( intFace ))
5021         //   {
5022         //     const SMDS_MeshElement* srcFace =
5023         //       eof->_subMesh->GetSubMeshDS()->GetElement( f->getIdInShape() );
5024         //     SMDS_ElemIteratorPtr nIt = srcFace->nodesIterator();
5025         //     while ( nIt->more() )
5026         //     {
5027         //       const SMDS_MeshNode* srcNode = static_cast<const SMDS_MeshNode*>( nIt->next() );
5028         //       TNode2Edge::iterator n2e = data._n2eMap.find( srcNode );
5029         //       if ( n2e != data._n2eMap.end() )
5030         //         badEdges.push_back( n2e->second );
5031         //     }
5032         //     eosC1[0] = eof;
5033         //     nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5034         //     if ( nbBad > 0 )
5035         //       return false;
5036         //   }
5037         // }
5038         // if ( eos._edges[i]->FindIntersection( *searcher, dist, data._epsilon, eos, &intFace ))
5039         //   return false;
5040         // else
5041         //   continue;
5042       }
5043       if ( !intFace )
5044       {
5045         SMESH_Comment msg("Invalid? normal at node "); msg << eos._edges[i]->_nodes[0]->GetID();
5046         debugMsg( msg );
5047         continue;
5048       }
5049
5050       const bool isShorterDist = ( distToIntersection > dist );
5051       if ( toBlockInfaltion || isShorterDist )
5052       {
5053         // ignore intersection of a _LayerEdge based on a _ConvexFace with a face
5054         // lying on this _ConvexFace
5055         if ( _ConvexFace* convFace = data.GetConvexFace( intFace->getshapeId() ))
5056           if ( convFace->_isTooCurved && convFace->_subIdToEOS.count ( eos._shapeID ))
5057             continue;
5058
5059         // ignore intersection of a _LayerEdge based on a FACE with an element on this FACE
5060         // ( avoid limiting the thickness on the case of issue 22576)
5061         if ( intFace->getshapeId() == eos._shapeID  )
5062           continue;
5063
5064         // ignore intersection with intFace of an adjacent FACE
5065         if ( dist > 0.1 * eos._edges[i]->_len )
5066         {
5067           bool toIgnore = false;
5068           if (  eos._toSmooth )
5069           {
5070             const TopoDS_Shape& S = getMeshDS()->IndexToShape( intFace->getshapeId() );
5071             if ( !S.IsNull() && S.ShapeType() == TopAbs_FACE )
5072             {
5073               TopExp_Explorer sub( eos._shape,
5074                                    eos.ShapeType() == TopAbs_FACE ? TopAbs_EDGE : TopAbs_VERTEX );
5075               for ( ; !toIgnore && sub.More(); sub.Next() )
5076                 // is adjacent - has a common EDGE or VERTEX
5077                 toIgnore = ( helper.IsSubShape( sub.Current(), S ));
5078
5079               if ( toIgnore ) // check angle between normals
5080               {
5081                 gp_XYZ normal;
5082                 if ( SMESH_MeshAlgos::FaceNormal( intFace, normal, /*normalized=*/true ))
5083                   toIgnore  = ( normal * eos._edges[i]->_normal > -0.5 );
5084               }
5085             }
5086           }
5087           if ( !toIgnore ) // check if the edge is a neighbor of intFace
5088           {
5089             for ( size_t iN = 0; !toIgnore &&  iN < eos._edges[i]->_neibors.size(); ++iN )
5090             {
5091               int nInd = intFace->GetNodeIndex( eos._edges[i]->_neibors[ iN ]->_nodes.back() );
5092               toIgnore = ( nInd >= 0 );
5093             }
5094           }
5095           if ( toIgnore )
5096             continue;
5097         }
5098
5099         // intersection not ignored
5100
5101         if ( toBlockInfaltion &&
5102              dist < ( eos._edges[i]->_len * theThickToIntersection ))
5103         {
5104           if ( is1stBlocked ) { is1stBlocked = false; // debug
5105             dumpFunction(SMESH_Comment("blockIntersected") <<data._index<<"_InfStep"<<infStep);
5106           }
5107           eos._edges[i]->Set( _LayerEdge::INTERSECTED ); // not to intersect
5108           eos._edges[i]->Block( data );                  // not to inflate
5109
5110           if ( _EdgesOnShape* eof = data.GetShapeEdges( intFace->getshapeId() ))
5111           {
5112             // block _LayerEdge's, on top of which intFace is
5113             if ( const _TmpMeshFace* f = dynamic_cast< const _TmpMeshFace*>( intFace ))
5114             {
5115               const SMDS_MeshElement* srcFace =
5116                 eof->_subMesh->GetSubMeshDS()->GetElement( f->getIdInShape() );
5117               SMDS_ElemIteratorPtr nIt = srcFace->nodesIterator();
5118               while ( nIt->more() )
5119               {
5120                 const SMDS_MeshNode* srcNode = static_cast<const SMDS_MeshNode*>( nIt->next() );
5121                 TNode2Edge::iterator n2e = data._n2eMap.find( srcNode );
5122                 if ( n2e != data._n2eMap.end() )
5123                   n2e->second->Block( data );
5124               }
5125             }
5126           }
5127         }
5128
5129         if ( isShorterDist )
5130         {
5131           distToIntersection = dist;
5132           le = eos._edges[i];
5133           closestFace = intFace;
5134         }
5135
5136       } // if ( toBlockInfaltion || isShorterDist )
5137     } // loop on eos._edges
5138   } // loop on data._edgesOnShape
5139
5140   if ( !is1stBlocked )
5141     dumpFunctionEnd();
5142
5143   if ( closestFace && le )
5144   {
5145 #ifdef __myDEBUG
5146     SMDS_MeshElement::iterator nIt = closestFace->begin_nodes();
5147     cout << "#Shortest distance: _LayerEdge nodes: tgt " << le->_nodes.back()->GetID()
5148          << " src " << le->_nodes[0]->GetID()<< ", intersection with face ("
5149          << (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()
5150          << ") distance = " << distToIntersection<< endl;
5151 #endif
5152   }
5153
5154   return true;
5155 }
5156
5157 //================================================================================
5158 /*!
5159  * \brief try to fix bad simplices by removing the last inflation step of some _LayerEdge's
5160  *  \param [in,out] badSmooEdges - _LayerEdge's to fix
5161  *  \return int - resulting nb of bad _LayerEdge's
5162  */
5163 //================================================================================
5164
5165 int _ViscousBuilder::invalidateBadSmooth( _SolidData&               data,
5166                                           SMESH_MesherHelper&       helper,
5167                                           vector< _LayerEdge* >&    badSmooEdges,
5168                                           vector< _EdgesOnShape* >& eosC1,
5169                                           const int                 infStep )
5170 {
5171   if ( badSmooEdges.empty() || infStep == 0 ) return 0;
5172
5173   dumpFunction(SMESH_Comment("invalidateBadSmooth")<<"_S"<<eosC1[0]->_shapeID<<"_InfStep"<<infStep);
5174
5175   enum {
5176     INVALIDATED   = _LayerEdge::UNUSED_FLAG,
5177     TO_INVALIDATE = _LayerEdge::UNUSED_FLAG * 2,
5178     ADDED         = _LayerEdge::UNUSED_FLAG * 4
5179   };
5180   data.UnmarkEdges( TO_INVALIDATE & INVALIDATED & ADDED );
5181
5182   double vol;
5183   bool haveInvalidated = true;
5184   while ( haveInvalidated )
5185   {
5186     haveInvalidated = false;
5187     for ( size_t i = 0; i < badSmooEdges.size(); ++i )
5188     {
5189       _LayerEdge*   edge = badSmooEdges[i];
5190       _EdgesOnShape* eos = data.GetShapeEdges( edge );
5191       edge->Set( ADDED );
5192       bool invalidated = false;
5193       if ( edge->Is( TO_INVALIDATE ) && edge->NbSteps() > 1 )
5194       {
5195         edge->InvalidateStep( edge->NbSteps(), *eos, /*restoreLength=*/true );
5196         edge->Block( data );
5197         edge->Set( INVALIDATED );
5198         edge->Unset( TO_INVALIDATE );
5199         invalidated = true;
5200         haveInvalidated = true;
5201       }
5202
5203       // look for _LayerEdge's of bad _simplices
5204       int nbBad = 0;
5205       SMESH_TNodeXYZ tgtXYZ  = edge->_nodes.back();
5206       gp_XYZ        prevXYZ1 = edge->PrevCheckPos( eos );
5207       //const gp_XYZ& prevXYZ2 = edge->PrevPos();
5208       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5209       {
5210         if (( edge->_simplices[j].IsForward( &prevXYZ1, &tgtXYZ, vol ))/* &&
5211             ( &prevXYZ1 == &prevXYZ2 || edge->_simplices[j].IsForward( &prevXYZ2, &tgtXYZ, vol ))*/)
5212           continue;
5213
5214         bool isBad = true;
5215         _LayerEdge* ee[2] = { 0,0 };
5216         for ( size_t iN = 0; iN < edge->_neibors.size() &&   !ee[1]  ; ++iN )
5217           if ( edge->_simplices[j].Includes( edge->_neibors[iN]->_nodes.back() ))
5218             ee[ ee[0] != 0 ] = edge->_neibors[iN];
5219
5220         int maxNbSteps = Max( ee[0]->NbSteps(), ee[1]->NbSteps() );
5221         while ( maxNbSteps > edge->NbSteps() && isBad )
5222         {
5223           --maxNbSteps;
5224           for ( int iE = 0; iE < 2; ++iE )
5225           {
5226             if ( ee[ iE ]->NbSteps() > maxNbSteps &&
5227                  ee[ iE ]->NbSteps() > 1 )
5228             {
5229               _EdgesOnShape* eos = data.GetShapeEdges( ee[ iE ] );
5230               ee[ iE ]->InvalidateStep( ee[ iE ]->NbSteps(), *eos, /*restoreLength=*/true );
5231               ee[ iE ]->Block( data );
5232               ee[ iE ]->Set( INVALIDATED );
5233               haveInvalidated = true;
5234             }
5235           }
5236           if (( edge->_simplices[j].IsForward( &prevXYZ1, &tgtXYZ, vol )) /*&&
5237               ( &prevXYZ1 == &prevXYZ2 || edge->_simplices[j].IsForward( &prevXYZ2, &tgtXYZ, vol ))*/)
5238             isBad = false;
5239         }
5240         nbBad += isBad;
5241         if ( !ee[0]->Is( ADDED )) badSmooEdges.push_back( ee[0] );
5242         if ( !ee[1]->Is( ADDED )) badSmooEdges.push_back( ee[1] );
5243         ee[0]->Set( ADDED );
5244         ee[1]->Set( ADDED );
5245         if ( isBad )
5246         {
5247           ee[0]->Set( TO_INVALIDATE );
5248           ee[1]->Set( TO_INVALIDATE );
5249         }
5250       }
5251
5252       if ( !invalidated &&  nbBad > 0  &&  edge->NbSteps() > 1 )
5253       {
5254         _EdgesOnShape* eos = data.GetShapeEdges( edge );
5255         edge->InvalidateStep( edge->NbSteps(), *eos, /*restoreLength=*/true );
5256         edge->Block( data );
5257         edge->Set( INVALIDATED );
5258         edge->Unset( TO_INVALIDATE );
5259         haveInvalidated = true;
5260       }
5261     } // loop on badSmooEdges
5262   } // while ( haveInvalidated )
5263
5264   // re-smooth on analytical EDGEs
5265   for ( size_t i = 0; i < badSmooEdges.size(); ++i )
5266   {
5267     _LayerEdge* edge = badSmooEdges[i];
5268     if ( !edge->Is( INVALIDATED )) continue;
5269
5270     _EdgesOnShape* eos = data.GetShapeEdges( edge );
5271     if ( eos->ShapeType() == TopAbs_VERTEX )
5272     {
5273       PShapeIteratorPtr eIt = helper.GetAncestors( eos->_shape, *_mesh, TopAbs_EDGE );
5274       while ( const TopoDS_Shape* e = eIt->next() )
5275         if ( _EdgesOnShape* eoe = data.GetShapeEdges( *e ))
5276           if ( eoe->_edgeSmoother && eoe->_edgeSmoother->isAnalytic() )
5277           {
5278             // TopoDS_Face F; Handle(ShapeAnalysis_Surface) surface;
5279             // if ( eoe->SWOLType() == TopAbs_FACE ) {
5280             //   F       = TopoDS::Face( eoe->_sWOL );
5281             //   surface = helper.GetSurface( F );
5282             // }
5283             // eoe->_edgeSmoother->Perform( data, surface, F, helper );
5284             eoe->_edgeSmoother->_anaCurve.Nullify();
5285           }
5286     }
5287   }
5288
5289
5290   // check result of invalidation
5291
5292   int nbBad = 0;
5293   for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5294   {
5295     for ( size_t i = 0; i < eosC1[ iEOS ]->_edges.size(); ++i )
5296     {
5297       if ( !eosC1[ iEOS ]->_sWOL.IsNull() ) continue;
5298       _LayerEdge*      edge = eosC1[ iEOS ]->_edges[i];
5299       SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
5300       gp_XYZ        prevXYZ = edge->PrevCheckPos( eosC1[ iEOS ]);
5301       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5302         if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
5303         {
5304           ++nbBad;
5305           debugMsg("Bad simplex remains ( " << edge->_nodes[0]->GetID()
5306                    << " "<< tgtXYZ._node->GetID()
5307                    << " "<< edge->_simplices[j]._nPrev->GetID()
5308                    << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
5309         }
5310     }
5311   }
5312   dumpFunctionEnd();
5313
5314   return nbBad;
5315 }
5316
5317 //================================================================================
5318 /*!
5319  * \brief Create an offset surface
5320  */
5321 //================================================================================
5322
5323 void _ViscousBuilder::makeOffsetSurface( _EdgesOnShape& eos, SMESH_MesherHelper& helper )
5324 {
5325   if ( eos._offsetSurf.IsNull() ||
5326        eos._edgeForOffset == 0 ||
5327        eos._edgeForOffset->Is( _LayerEdge::BLOCKED ))
5328     return;
5329
5330   Handle(ShapeAnalysis_Surface) baseSurface = helper.GetSurface( TopoDS::Face( eos._shape ));
5331
5332   // find offset
5333   gp_Pnt   tgtP = SMESH_TNodeXYZ( eos._edgeForOffset->_nodes.back() );
5334   /*gp_Pnt2d uv=*/baseSurface->ValueOfUV( tgtP, Precision::Confusion() );
5335   double offset = baseSurface->Gap();
5336
5337   eos._offsetSurf.Nullify();
5338
5339   try
5340   {
5341     BRepOffsetAPI_MakeOffsetShape offsetMaker;
5342     offsetMaker.PerformByJoin( eos._shape, -offset, Precision::Confusion() );
5343     if ( !offsetMaker.IsDone() ) return;
5344
5345     TopExp_Explorer fExp( offsetMaker.Shape(), TopAbs_FACE );
5346     if ( !fExp.More() ) return;
5347
5348     TopoDS_Face F = TopoDS::Face( fExp.Current() );
5349     Handle(Geom_Surface) surf = BRep_Tool::Surface( F );
5350     if ( surf.IsNull() ) return;
5351
5352     eos._offsetSurf = new ShapeAnalysis_Surface( surf );
5353   }
5354   catch ( Standard_Failure )
5355   {
5356   }
5357 }
5358
5359 //================================================================================
5360 /*!
5361  * \brief Put nodes of a curved FACE to its offset surface
5362  */
5363 //================================================================================
5364
5365 void _ViscousBuilder::putOnOffsetSurface( _EdgesOnShape&            eos,
5366                                           int                       infStep,
5367                                           vector< _EdgesOnShape* >& eosC1,
5368                                           int                       smooStep,
5369                                           int                       moveAll )
5370 {
5371   _EdgesOnShape * eof = & eos;
5372   if ( eos.ShapeType() != TopAbs_FACE ) // eos is a boundary of C1 FACE, look for the FACE eos
5373   {
5374     eof = 0;
5375     for ( size_t i = 0; i < eosC1.size() && !eof; ++i )
5376     {
5377       if ( eosC1[i]->_offsetSurf.IsNull() ||
5378            eosC1[i]->ShapeType() != TopAbs_FACE ||
5379            eosC1[i]->_edgeForOffset == 0 ||
5380            eosC1[i]->_edgeForOffset->Is( _LayerEdge::BLOCKED ))
5381         continue;
5382       if ( SMESH_MesherHelper::IsSubShape( eos._shape, eosC1[i]->_shape ))
5383         eof = eosC1[i];
5384     }
5385   }
5386   if ( !eof ||
5387        eof->_offsetSurf.IsNull() ||
5388        eof->ShapeType() != TopAbs_FACE ||
5389        eof->_edgeForOffset == 0 ||
5390        eof->_edgeForOffset->Is( _LayerEdge::BLOCKED ))
5391     return;
5392
5393   double preci = BRep_Tool::Tolerance( TopoDS::Face( eof->_shape )), vol;
5394   for ( size_t i = 0; i < eos._edges.size(); ++i )
5395   {
5396     _LayerEdge* edge = eos._edges[i];
5397     edge->Unset( _LayerEdge::MARKED );
5398     if ( edge->Is( _LayerEdge::BLOCKED ) || !edge->_curvature )
5399       continue;
5400     if ( moveAll == _LayerEdge::UPD_NORMAL_CONV )
5401     {
5402       if ( !edge->Is( _LayerEdge::UPD_NORMAL_CONV ))
5403         continue;
5404     }
5405     else if ( !moveAll && !edge->Is( _LayerEdge::MOVED ))
5406       continue;
5407
5408     int nbBlockedAround = 0;
5409     for ( size_t iN = 0; iN < edge->_neibors.size(); ++iN )
5410       nbBlockedAround += edge->_neibors[iN]->Is( _LayerEdge::BLOCKED );
5411     if ( nbBlockedAround > 1 )
5412       continue;
5413
5414     gp_Pnt tgtP = SMESH_TNodeXYZ( edge->_nodes.back() );
5415     gp_Pnt2d uv = eof->_offsetSurf->NextValueOfUV( edge->_curvature->_uv, tgtP, preci );
5416     if ( eof->_offsetSurf->Gap() > edge->_len ) continue; // NextValueOfUV() bug
5417     edge->_curvature->_uv = uv;
5418     if ( eof->_offsetSurf->Gap() < 10 * preci ) continue; // same pos
5419
5420     gp_XYZ  newP = eof->_offsetSurf->Value( uv ).XYZ();
5421     gp_XYZ prevP = edge->PrevCheckPos();
5422     bool      ok = true;
5423     if ( !moveAll )
5424       for ( size_t iS = 0; iS < edge->_simplices.size() && ok; ++iS )
5425       {
5426         ok = edge->_simplices[iS].IsForward( &prevP, &newP, vol );
5427       }
5428     if ( ok )
5429     {
5430       SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( edge->_nodes.back() );
5431       n->setXYZ( newP.X(), newP.Y(), newP.Z());
5432       edge->_pos.back() = newP;
5433
5434       edge->Set( _LayerEdge::MARKED );
5435       if ( moveAll == _LayerEdge::UPD_NORMAL_CONV )
5436       {
5437         edge->_normal = ( newP - prevP ).Normalized();
5438       }
5439     }
5440   }
5441
5442
5443
5444 #ifdef _DEBUG_
5445   // dumpMove() for debug
5446   size_t i = 0;
5447   for ( ; i < eos._edges.size(); ++i )
5448     if ( eos._edges[i]->Is( _LayerEdge::MARKED ))
5449       break;
5450   if ( i < eos._edges.size() )
5451   {
5452     dumpFunction(SMESH_Comment("putOnOffsetSurface_S") << eos._shapeID
5453                  << "_InfStep" << infStep << "_" << smooStep );
5454     for ( ; i < eos._edges.size(); ++i )
5455     {
5456       if ( eos._edges[i]->Is( _LayerEdge::MARKED ))
5457         dumpMove( eos._edges[i]->_nodes.back() );
5458     }
5459     dumpFunctionEnd();
5460   }
5461 #endif
5462
5463   _ConvexFace* cnvFace;
5464   if ( moveAll != _LayerEdge::UPD_NORMAL_CONV &&
5465        eos.ShapeType() == TopAbs_FACE &&
5466        (cnvFace = eos.GetData().GetConvexFace( eos._shapeID )) &&
5467        !cnvFace->_normalsFixedOnBorders )
5468   {
5469     // put on the surface nodes built on FACE boundaries
5470     SMESH_subMeshIteratorPtr smIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false);
5471     while ( smIt->more() )
5472     {
5473       SMESH_subMesh* sm = smIt->next();
5474       _EdgesOnShape* subEOS = eos.GetData().GetShapeEdges( sm->GetId() );
5475       if ( !subEOS->_sWOL.IsNull() ) continue;
5476       if ( std::find( eosC1.begin(), eosC1.end(), subEOS ) != eosC1.end() ) continue;
5477
5478       putOnOffsetSurface( *subEOS, infStep, eosC1, smooStep, _LayerEdge::UPD_NORMAL_CONV );
5479     }
5480     cnvFace->_normalsFixedOnBorders = true;
5481   }
5482 }
5483
5484 //================================================================================
5485 /*!
5486  * \brief Return a curve of the EDGE to be used for smoothing and arrange
5487  *        _LayerEdge's to be in a consequent order
5488  */
5489 //================================================================================
5490
5491 Handle(Geom_Curve) _Smoother1D::CurveForSmooth( const TopoDS_Edge&  E,
5492                                                 _EdgesOnShape&      eos,
5493                                                 SMESH_MesherHelper& helper)
5494 {
5495   SMESHDS_SubMesh* smDS = eos._subMesh->GetSubMeshDS();
5496
5497   TopLoc_Location loc; double f,l;
5498
5499   Handle(Geom_Line)   line;
5500   Handle(Geom_Circle) circle;
5501   bool isLine, isCirc;
5502   if ( eos._sWOL.IsNull() ) /////////////////////////////////////////// 3D case
5503   {
5504     // check if the EDGE is a line
5505     Handle(Geom_Curve) curve = BRep_Tool::Curve( E, f, l);
5506     if ( curve->IsKind( STANDARD_TYPE( Geom_TrimmedCurve )))
5507       curve = Handle(Geom_TrimmedCurve)::DownCast( curve )->BasisCurve();
5508
5509     line   = Handle(Geom_Line)::DownCast( curve );
5510     circle = Handle(Geom_Circle)::DownCast( curve );
5511     isLine = (!line.IsNull());
5512     isCirc = (!circle.IsNull());
5513
5514     if ( !isLine && !isCirc ) // Check if the EDGE is close to a line
5515     {
5516       isLine = SMESH_Algo::IsStraight( E );
5517
5518       if ( isLine )
5519         line = new Geom_Line( gp::OX() ); // only type does matter
5520     }
5521     if ( !isLine && !isCirc && eos._edges.size() > 2) // Check if the EDGE is close to a circle
5522     {
5523       // TODO
5524     }
5525   }
5526   else //////////////////////////////////////////////////////////////////////// 2D case
5527   {
5528     if ( !eos._isRegularSWOL ) // 23190
5529       return NULL;
5530
5531     const TopoDS_Face& F = TopoDS::Face( eos._sWOL );
5532
5533     // check if the EDGE is a line
5534     Handle(Geom2d_Curve) curve = BRep_Tool::CurveOnSurface( E, F, f, l );
5535     if ( curve->IsKind( STANDARD_TYPE( Geom2d_TrimmedCurve )))
5536       curve = Handle(Geom2d_TrimmedCurve)::DownCast( curve )->BasisCurve();
5537
5538     Handle(Geom2d_Line)   line2d   = Handle(Geom2d_Line)::DownCast( curve );
5539     Handle(Geom2d_Circle) circle2d = Handle(Geom2d_Circle)::DownCast( curve );
5540     isLine = (!line2d.IsNull());
5541     isCirc = (!circle2d.IsNull());
5542
5543     if ( !isLine && !isCirc ) // Check if the EDGE is close to a line
5544     {
5545       Bnd_B2d bndBox;
5546       SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
5547       while ( nIt->more() )
5548         bndBox.Add( helper.GetNodeUV( F, nIt->next() ));
5549       gp_XY size = bndBox.CornerMax() - bndBox.CornerMin();
5550
5551       const double lineTol = 1e-2 * sqrt( bndBox.SquareExtent() );
5552       for ( int i = 0; i < 2 && !isLine; ++i )
5553         isLine = ( size.Coord( i+1 ) <= lineTol );
5554     }
5555     if ( !isLine && !isCirc && eos._edges.size() > 2 ) // Check if the EDGE is close to a circle
5556     {
5557       // TODO
5558     }
5559     if ( isLine )
5560     {
5561       line = new Geom_Line( gp::OX() ); // only type does matter
5562     }
5563     else if ( isCirc )
5564     {
5565       gp_Pnt2d p = circle2d->Location();
5566       gp_Ax2 ax( gp_Pnt( p.X(), p.Y(), 0), gp::DX());
5567       circle = new Geom_Circle( ax, 1.); // only center position does matter
5568     }
5569   }
5570
5571   if ( isLine )
5572     return line;
5573   if ( isCirc )
5574     return circle;
5575
5576   return Handle(Geom_Curve)();
5577 }
5578
5579 //================================================================================
5580 /*!
5581  * \brief Smooth edges on EDGE
5582  */
5583 //================================================================================
5584
5585 bool _Smoother1D::Perform(_SolidData&                    data,
5586                           Handle(ShapeAnalysis_Surface)& surface,
5587                           const TopoDS_Face&             F,
5588                           SMESH_MesherHelper&            helper )
5589 {
5590   if ( _leParams.empty() || ( !isAnalytic() && _offPoints.empty() ))
5591     prepare( data );
5592
5593   findEdgesToSmooth();
5594   if ( isAnalytic() )
5595     return smoothAnalyticEdge( data, surface, F, helper );
5596   else
5597     return smoothComplexEdge ( data, surface, F, helper );
5598 }
5599
5600 //================================================================================
5601 /*!
5602  * \brief Find edges to smooth
5603  */
5604 //================================================================================
5605
5606 void _Smoother1D::findEdgesToSmooth()
5607 {
5608   _LayerEdge* leOnV[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
5609   for ( int iEnd = 0; iEnd < 2; ++iEnd )
5610     if ( leOnV[iEnd]->Is( _LayerEdge::NORMAL_UPDATED ))
5611       _leOnV[iEnd]._cosin = Abs( _edgeDir[iEnd].Normalized() * leOnV[iEnd]->_normal );
5612
5613   _eToSmooth[0].first = _eToSmooth[0].second = 0;
5614
5615   for ( size_t i = 0; i < _eos.size(); ++i )
5616   {
5617     if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH ))
5618     {
5619       if ( needSmoothing( _leOnV[0]._cosin,
5620                           _eos[i]->_len * leOnV[0]->_lenFactor, _curveLen * _leParams[i] ) ||
5621            isToSmooth( i )
5622            )
5623         _eos[i]->Set( _LayerEdge::TO_SMOOTH );
5624       else
5625         break;
5626     }
5627     _eToSmooth[0].second = i+1;
5628   }
5629
5630   _eToSmooth[1].first = _eToSmooth[1].second = _eos.size();
5631
5632   for ( int i = _eos.size() - 1; i >= _eToSmooth[0].second; --i )
5633   {
5634     if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH ))
5635     {
5636       if ( needSmoothing( _leOnV[1]._cosin,
5637                           _eos[i]->_len * leOnV[1]->_lenFactor, _curveLen * ( 1.-_leParams[i] )) ||
5638            isToSmooth( i ))
5639         _eos[i]->Set( _LayerEdge::TO_SMOOTH );
5640       else
5641         break;
5642     }
5643     _eToSmooth[1].first = i;
5644   }
5645 }
5646
5647 //================================================================================
5648 /*!
5649  * \brief Check if iE-th _LayerEdge needs smoothing
5650  */
5651 //================================================================================
5652
5653 bool _Smoother1D::isToSmooth( int iE )
5654 {
5655   SMESH_NodeXYZ pi( _eos[iE]->_nodes[0] );
5656   SMESH_NodeXYZ p0( _eos[iE]->_2neibors->srcNode(0) );
5657   SMESH_NodeXYZ p1( _eos[iE]->_2neibors->srcNode(1) );
5658   gp_XYZ       seg0 = pi - p0;
5659   gp_XYZ       seg1 = p1 - pi;
5660   gp_XYZ    tangent =  seg0 + seg1;
5661   double tangentLen = tangent.Modulus();
5662   double  segMinLen = Min( seg0.Modulus(), seg1.Modulus() );
5663   if ( tangentLen < std::numeric_limits<double>::min() )
5664     return false;
5665   tangent /= tangentLen;
5666
5667   for ( size_t i = 0; i < _eos[iE]->_neibors.size(); ++i )
5668   {
5669     _LayerEdge* ne = _eos[iE]->_neibors[i];
5670     if ( !ne->Is( _LayerEdge::TO_SMOOTH ) ||
5671          ne->_nodes.size() < 2 ||
5672          ne->_nodes[0]->GetPosition()->GetDim() != 2 )
5673       continue;
5674     gp_XYZ edgeVec = SMESH_NodeXYZ( ne->_nodes.back() ) - SMESH_NodeXYZ( ne->_nodes[0] );
5675     double    proj = edgeVec * tangent;
5676     if ( needSmoothing( 1., proj, segMinLen ))
5677       return true;
5678   }
5679   return false;
5680 }
5681
5682 //================================================================================
5683 /*!
5684  * \brief smooth _LayerEdge's on a staight EDGE or circular EDGE
5685  */
5686 //================================================================================
5687
5688 bool _Smoother1D::smoothAnalyticEdge( _SolidData&                    data,
5689                                       Handle(ShapeAnalysis_Surface)& surface,
5690                                       const TopoDS_Face&             F,
5691                                       SMESH_MesherHelper&            helper)
5692 {
5693   if ( !isAnalytic() ) return false;
5694
5695   size_t iFrom = 0, iTo = _eos._edges.size();
5696
5697   if ( _anaCurve->IsKind( STANDARD_TYPE( Geom_Line )))
5698   {
5699     if ( F.IsNull() ) // 3D
5700     {
5701       SMESH_TNodeXYZ pSrc0( _eos._edges[iFrom]->_2neibors->srcNode(0) );
5702       SMESH_TNodeXYZ pSrc1( _eos._edges[iTo-1]->_2neibors->srcNode(1) );
5703       //const   gp_XYZ lineDir = pSrc1 - pSrc0;
5704       //_LayerEdge* vLE0 = getLEdgeOnV( 0 );
5705       //_LayerEdge* vLE1 = getLEdgeOnV( 1 );
5706       // bool shiftOnly = ( vLE0->Is( _LayerEdge::NORMAL_UPDATED ) ||
5707       //                    vLE0->Is( _LayerEdge::BLOCKED ) ||
5708       //                    vLE1->Is( _LayerEdge::NORMAL_UPDATED ) ||
5709       //                    vLE1->Is( _LayerEdge::BLOCKED ));
5710       for ( int iEnd = 0; iEnd < 2; ++iEnd )
5711       {
5712         iFrom = _eToSmooth[ iEnd ].first, iTo = _eToSmooth[ iEnd ].second;
5713         if ( iFrom >= iTo ) continue;
5714         SMESH_TNodeXYZ p0( _eos[iFrom]->_2neibors->tgtNode(0) );
5715         SMESH_TNodeXYZ p1( _eos[iTo-1]->_2neibors->tgtNode(1) );
5716         double param0 = ( iFrom == 0 ) ? 0. : _leParams[ iFrom-1 ];
5717         double param1 = _leParams[ iTo ];
5718         for ( size_t i = iFrom; i < iTo; ++i )
5719         {
5720           _LayerEdge*       edge = _eos[i];
5721           SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edge->_nodes.back() );
5722           double           param = ( _leParams[i] - param0 ) / ( param1 - param0 );
5723           gp_XYZ          newPos = p0 * ( 1. - param ) + p1 * param;
5724
5725           // if ( shiftOnly || edge->Is( _LayerEdge::NORMAL_UPDATED ))
5726           // {
5727           //   gp_XYZ curPos = SMESH_TNodeXYZ ( tgtNode );
5728           //   double  shift = ( lineDir * ( newPos - pSrc0 ) -
5729           //                     lineDir * ( curPos - pSrc0 ));
5730           //   newPos = curPos + lineDir * shift / lineDir.SquareModulus();
5731           // }
5732           if ( edge->Is( _LayerEdge::BLOCKED ))
5733           {
5734             SMESH_TNodeXYZ pSrc( edge->_nodes[0] );
5735             double curThick = pSrc.SquareDistance( tgtNode );
5736             double newThink = ( pSrc - newPos ).SquareModulus();
5737             if ( newThink > curThick )
5738               continue;
5739           }
5740           edge->_pos.back() = newPos;
5741           tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
5742           dumpMove( tgtNode );
5743         }
5744       }
5745     }
5746     else // 2D
5747     {
5748       _LayerEdge* eV0 = getLEdgeOnV( 0 );
5749       _LayerEdge* eV1 = getLEdgeOnV( 1 );
5750       gp_XY      uvV0 = eV0->LastUV( F, *data.GetShapeEdges( eV0 ));
5751       gp_XY      uvV1 = eV1->LastUV( F, *data.GetShapeEdges( eV1 ));
5752       if ( eV0->_nodes.back() == eV1->_nodes.back() ) // closed edge
5753       {
5754         int iPeriodic = helper.GetPeriodicIndex();
5755         if ( iPeriodic == 1 || iPeriodic == 2 )
5756         {
5757           uvV1.SetCoord( iPeriodic, helper.GetOtherParam( uvV1.Coord( iPeriodic )));
5758           if ( uvV0.Coord( iPeriodic ) > uvV1.Coord( iPeriodic ))
5759             std::swap( uvV0, uvV1 );
5760         }
5761       }
5762       for ( int iEnd = 0; iEnd < 2; ++iEnd )
5763       {
5764         iFrom = _eToSmooth[ iEnd ].first, iTo = _eToSmooth[ iEnd ].second;
5765         if ( iFrom >= iTo ) continue;
5766         _LayerEdge* e0 = _eos[iFrom]->_2neibors->_edges[0];
5767         _LayerEdge* e1 = _eos[iTo-1]->_2neibors->_edges[1];
5768         gp_XY      uv0 = ( e0 == eV0 ) ? uvV0 : e0->LastUV( F, _eos );
5769         gp_XY      uv1 = ( e1 == eV1 ) ? uvV1 : e1->LastUV( F, _eos );
5770         double  param0 = ( iFrom == 0 ) ? 0. : _leParams[ iFrom-1 ];
5771         double  param1 = _leParams[ iTo ];
5772         gp_XY  rangeUV = uv1 - uv0;
5773         for ( size_t i = iFrom; i < iTo; ++i )
5774         {
5775           if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
5776           double param = ( _leParams[i] - param0 ) / ( param1 - param0 );
5777           gp_XY newUV = uv0 + param * rangeUV;
5778
5779           gp_Pnt newPos = surface->Value( newUV.X(), newUV.Y() );
5780           SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos[i]->_nodes.back() );
5781           tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
5782           dumpMove( tgtNode );
5783
5784           SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( tgtNode->GetPosition() );
5785           pos->SetUParameter( newUV.X() );
5786           pos->SetVParameter( newUV.Y() );
5787
5788           gp_XYZ newUV0( newUV.X(), newUV.Y(), 0 );
5789
5790           if ( !_eos[i]->Is( _LayerEdge::SMOOTHED ))
5791           {
5792             _eos[i]->Set( _LayerEdge::SMOOTHED ); // to check in refine() (IPAL54237)
5793             if ( _eos[i]->_pos.size() > 2 )
5794             {
5795               // modify previous positions to make _LayerEdge less sharply bent
5796               vector<gp_XYZ>& uvVec = _eos[i]->_pos;
5797               const gp_XYZ  uvShift = newUV0 - uvVec.back();
5798               const double     len2 = ( uvVec.back() - uvVec[ 0 ] ).SquareModulus();
5799               int iPrev = uvVec.size() - 2;
5800               while ( iPrev > 0 )
5801               {
5802                 double r = ( uvVec[ iPrev ] - uvVec[0] ).SquareModulus() / len2;
5803                 uvVec[ iPrev ] += uvShift * r;
5804                 --iPrev;
5805               }
5806             }
5807           }
5808           _eos[i]->_pos.back() = newUV0;
5809         }
5810       }
5811     }
5812     return true;
5813   }
5814
5815   if ( _anaCurve->IsKind( STANDARD_TYPE( Geom_Circle )))
5816   {
5817     Handle(Geom_Circle) circle = Handle(Geom_Circle)::DownCast( _anaCurve );
5818     gp_Pnt center3D = circle->Location();
5819
5820     if ( F.IsNull() ) // 3D
5821     {
5822       if ( getLEdgeOnV( 0 )->_nodes.back() == getLEdgeOnV( 1 )->_nodes.back() )
5823         return true; // closed EDGE - nothing to do
5824
5825       // circle is a real curve of EDGE
5826       gp_Circ circ = circle->Circ();
5827
5828       // new center is shifted along its axis
5829       const gp_Dir& axis = circ.Axis().Direction();
5830       _LayerEdge*     e0 = getLEdgeOnV(0);
5831       _LayerEdge*     e1 = getLEdgeOnV(1);
5832       SMESH_TNodeXYZ  p0 = e0->_nodes.back();
5833       SMESH_TNodeXYZ  p1 = e1->_nodes.back();
5834       double      shift1 = axis.XYZ() * ( p0 - center3D.XYZ() );
5835       double      shift2 = axis.XYZ() * ( p1 - center3D.XYZ() );
5836       gp_Pnt   newCenter = center3D.XYZ() + axis.XYZ() * 0.5 * ( shift1 + shift2 );
5837
5838       double newRadius = 0.5 * ( newCenter.Distance( p0 ) + newCenter.Distance( p1 ));
5839
5840       gp_Ax2  newAxis( newCenter, axis, gp_Vec( newCenter, p0 ));
5841       gp_Circ newCirc( newAxis, newRadius );
5842       gp_Vec  vecC1  ( newCenter, p1 );
5843
5844       double uLast = newAxis.XDirection().AngleWithRef( vecC1, newAxis.Direction() ); // -PI - +PI
5845       if ( uLast < 0 )
5846         uLast += 2 * M_PI;
5847       
5848       for ( size_t i = 0; i < _eos.size(); ++i )
5849       {
5850         if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
5851         //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
5852         double u = uLast * _leParams[i];
5853         gp_Pnt p = ElCLib::Value( u, newCirc );
5854         _eos._edges[i]->_pos.back() = p.XYZ();
5855
5856         SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos._edges[i]->_nodes.back() );
5857         tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
5858         dumpMove( tgtNode );
5859       }
5860       return true;
5861     }
5862     else // 2D
5863     {
5864       const gp_XY center( center3D.X(), center3D.Y() );
5865
5866       _LayerEdge* e0 = getLEdgeOnV(0);
5867       _LayerEdge* eM = _eos._edges[ 0 ];
5868       _LayerEdge* e1 = getLEdgeOnV(1);
5869       gp_XY      uv0 = e0->LastUV( F, *data.GetShapeEdges( e0 ) );
5870       gp_XY      uvM = eM->LastUV( F, *data.GetShapeEdges( eM ) );
5871       gp_XY      uv1 = e1->LastUV( F, *data.GetShapeEdges( e1 ) );
5872       gp_Vec2d vec0( center, uv0 );
5873       gp_Vec2d vecM( center, uvM );
5874       gp_Vec2d vec1( center, uv1 );
5875       double uLast = vec0.Angle( vec1 ); // -PI - +PI
5876       double uMidl = vec0.Angle( vecM );
5877       if ( uLast * uMidl <= 0. )
5878         uLast += ( uMidl > 0 ? +2. : -2. ) * M_PI;
5879       const double radius = 0.5 * ( vec0.Magnitude() + vec1.Magnitude() );
5880
5881       gp_Ax2d   axis( center, vec0 );
5882       gp_Circ2d circ( axis, radius );
5883       for ( size_t i = 0; i < _eos.size(); ++i )
5884       {
5885         if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
5886         //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
5887         double    newU = uLast * _leParams[i];
5888         gp_Pnt2d newUV = ElCLib::Value( newU, circ );
5889         _eos._edges[i]->_pos.back().SetCoord( newUV.X(), newUV.Y(), 0 );
5890
5891         gp_Pnt newPos = surface->Value( newUV.X(), newUV.Y() );
5892         SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos._edges[i]->_nodes.back() );
5893         tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
5894         dumpMove( tgtNode );
5895
5896         SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( tgtNode->GetPosition() );
5897         pos->SetUParameter( newUV.X() );
5898         pos->SetVParameter( newUV.Y() );
5899
5900         _eos[i]->Set( _LayerEdge::SMOOTHED ); // to check in refine() (IPAL54237)
5901       }
5902     }
5903     return true;
5904   }
5905
5906   return false;
5907 }
5908
5909 //================================================================================
5910 /*!
5911  * \brief smooth _LayerEdge's on a an EDGE
5912  */
5913 //================================================================================
5914
5915 bool _Smoother1D::smoothComplexEdge( _SolidData&                    data,
5916                                      Handle(ShapeAnalysis_Surface)& surface,
5917                                      const TopoDS_Face&             F,
5918                                      SMESH_MesherHelper&            helper)
5919 {
5920   if ( _offPoints.empty() )
5921     return false;
5922
5923   // ----------------------------------------------
5924   // move _offPoints along normals of _LayerEdge's
5925   // ----------------------------------------------
5926
5927   _LayerEdge* e[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
5928   if ( e[0]->Is( _LayerEdge::NORMAL_UPDATED ))
5929     _leOnV[0]._normal = getNormalNormal( e[0]->_normal, _edgeDir[0] );
5930   if ( e[1]->Is( _LayerEdge::NORMAL_UPDATED )) 
5931     _leOnV[1]._normal = getNormalNormal( e[1]->_normal, _edgeDir[1] );
5932   _leOnV[0]._len = e[0]->_len;
5933   _leOnV[1]._len = e[1]->_len;
5934   for ( size_t i = 0; i < _offPoints.size(); i++ )
5935   {
5936     _LayerEdge*  e0 = _offPoints[i]._2edges._edges[0];
5937     _LayerEdge*  e1 = _offPoints[i]._2edges._edges[1];
5938     const double w0 = _offPoints[i]._2edges._wgt[0];
5939     const double w1 = _offPoints[i]._2edges._wgt[1];
5940     gp_XYZ  avgNorm = ( e0->_normal    * w0 + e1->_normal    * w1 ).Normalized();
5941     double  avgLen  = ( e0->_len       * w0 + e1->_len       * w1 );
5942     double  avgFact = ( e0->_lenFactor * w0 + e1->_lenFactor * w1 );
5943     if ( e0->Is( _LayerEdge::NORMAL_UPDATED ) ||
5944          e1->Is( _LayerEdge::NORMAL_UPDATED ))
5945       avgNorm = getNormalNormal( avgNorm, _offPoints[i]._edgeDir );
5946
5947     _offPoints[i]._xyz += avgNorm * ( avgLen - _offPoints[i]._len ) * avgFact;
5948     _offPoints[i]._len  = avgLen;
5949   }
5950
5951   double fTol = 0;
5952   if ( !surface.IsNull() ) // project _offPoints to the FACE
5953   {
5954     fTol = 100 * BRep_Tool::Tolerance( F );
5955     //const double segLen = _offPoints[0].Distance( _offPoints[1] );
5956
5957     gp_Pnt2d uv = surface->ValueOfUV( _offPoints[0]._xyz, fTol );
5958     //if ( surface->Gap() < 0.5 * segLen )
5959       _offPoints[0]._xyz = surface->Value( uv ).XYZ();
5960
5961     for ( size_t i = 1; i < _offPoints.size(); ++i )
5962     {
5963       uv = surface->NextValueOfUV( uv, _offPoints[i]._xyz, fTol );
5964       //if ( surface->Gap() < 0.5 * segLen )
5965         _offPoints[i]._xyz = surface->Value( uv ).XYZ();
5966     }
5967   }
5968
5969   // -----------------------------------------------------------------
5970   // project tgt nodes of extreme _LayerEdge's to the offset segments
5971   // -----------------------------------------------------------------
5972
5973   const int updatedOrBlocked = _LayerEdge::NORMAL_UPDATED | _LayerEdge::BLOCKED;
5974   if ( e[0]->Is( updatedOrBlocked )) _iSeg[0] = 0;
5975   if ( e[1]->Is( updatedOrBlocked )) _iSeg[1] = _offPoints.size()-2;
5976
5977   gp_Pnt pExtreme[2], pProj[2];
5978   bool isProjected[2];
5979   for ( int is2nd = 0; is2nd < 2; ++is2nd )
5980   {
5981     pExtreme[ is2nd ] = SMESH_TNodeXYZ( e[is2nd]->_nodes.back() );
5982     int  i = _iSeg[ is2nd ];
5983     int di = is2nd ? -1 : +1;
5984     bool & projected = isProjected[ is2nd ];
5985     projected = false;
5986     double uOnSeg, distMin = Precision::Infinite(), dist, distPrev = 0;
5987     int nbWorse = 0;
5988     do {
5989       gp_Vec v0p( _offPoints[i]._xyz, pExtreme[ is2nd ]    );
5990       gp_Vec v01( _offPoints[i]._xyz, _offPoints[i+1]._xyz );
5991       uOnSeg     = ( v0p * v01 ) / v01.SquareMagnitude();  // param [0,1] along v01
5992       projected  = ( Abs( uOnSeg - 0.5 ) <= 0.5 );
5993       dist       =  pExtreme[ is2nd ].SquareDistance( _offPoints[ i + ( uOnSeg > 0.5 )]._xyz );
5994       if ( dist < distMin || projected )
5995       {
5996         _iSeg[ is2nd ] = i;
5997         pProj[ is2nd ] = _offPoints[i]._xyz + ( v01 * uOnSeg ).XYZ();
5998         distMin = dist;
5999       }
6000       else if ( dist > distPrev )
6001       {
6002         if ( ++nbWorse > 3 ) // avoid projection to the middle of a closed EDGE
6003           break;
6004       }
6005       distPrev = dist;
6006       i += di;
6007     }
6008     while ( !projected &&
6009             i >= 0 && i+1 < (int)_offPoints.size() );
6010
6011     if ( !projected )
6012     {
6013       if (( is2nd && _iSeg[1] != _offPoints.size()-2 ) || ( !is2nd && _iSeg[0] != 0 ))
6014       {
6015         _iSeg[0] = 0;
6016         _iSeg[1] = _offPoints.size()-2;
6017         debugMsg( "smoothComplexEdge() failed to project nodes of extreme _LayerEdge's" );
6018         return false;
6019       }
6020     }
6021   }
6022   if ( _iSeg[0] > _iSeg[1] )
6023   {
6024     debugMsg( "smoothComplexEdge() incorrectly projected nodes of extreme _LayerEdge's" );
6025     return false;
6026   }
6027
6028   // adjust length of extreme LE (test viscous_layers_01/B7)
6029   gp_Vec vDiv0( pExtreme[0], pProj[0] );
6030   gp_Vec vDiv1( pExtreme[1], pProj[1] );
6031   double d0 = vDiv0.Magnitude();
6032   double d1 = isProjected[1] ? vDiv1.Magnitude() : 0;
6033   if ( e[0]->Is( _LayerEdge::BLOCKED )) {
6034     if ( e[0]->_normal * vDiv0.XYZ() < 0 ) e[0]->_len += d0;
6035     else                                   e[0]->_len -= d0;
6036   }
6037   if ( e[1]->Is( _LayerEdge::BLOCKED )) {
6038     if ( e[1]->_normal * vDiv1.XYZ() < 0 ) e[1]->_len += d1;
6039     else                                   e[1]->_len -= d1;
6040   }
6041
6042   // ---------------------------------------------------------------------------------
6043   // compute normalized length of the offset segments located between the projections
6044   // ---------------------------------------------------------------------------------
6045
6046   // temporary replace extreme _offPoints by pExtreme
6047   gp_XYZ opXYZ[2] = { _offPoints[ _iSeg[0]   ]._xyz,
6048                       _offPoints[ _iSeg[1]+1 ]._xyz };
6049   _offPoints[ _iSeg[0]   ]._xyz = pExtreme[0].XYZ();
6050   _offPoints[ _iSeg[1]+ 1]._xyz = pExtreme[1].XYZ();
6051
6052   size_t iSeg = 0, nbSeg = _iSeg[1] - _iSeg[0] + 1;
6053   vector< double > len( nbSeg + 1 );
6054   len[ iSeg++ ] = 0;
6055   len[ iSeg++ ] = pProj[ 0 ].Distance( _offPoints[ _iSeg[0]+1 ]._xyz );
6056   for ( size_t i = _iSeg[0]+1; i <= _iSeg[1]; ++i, ++iSeg )
6057   {
6058     len[ iSeg ] = len[ iSeg-1 ] + _offPoints[i].Distance( _offPoints[i+1] );
6059   }
6060   // if ( isProjected[ 1 ])
6061   //   len[ nbSeg ] -= pProj[ 1 ].Distance( _offPoints[ _iSeg[1]+1 ]._xyz );
6062   // else
6063   //   len[ nbSeg ] += pExtreme[ 1 ].Distance( _offPoints[ _iSeg[1]+1 ]._xyz );
6064
6065   double fullLen = len.back() - d0 - d1;
6066   for ( iSeg = 0; iSeg < len.size(); ++iSeg )
6067     len[iSeg] = ( len[iSeg] - d0 ) / fullLen;
6068
6069   // -------------------------------------------------------------
6070   // distribute tgt nodes of _LayerEdge's between the projections
6071   // -------------------------------------------------------------
6072
6073   iSeg = 0;
6074   for ( size_t i = 0; i < _eos.size(); ++i )
6075   {
6076     if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
6077     //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
6078     while ( iSeg+2 < len.size() && _leParams[i] > len[ iSeg+1 ] )
6079       iSeg++;
6080     double r = ( _leParams[i] - len[ iSeg ]) / ( len[ iSeg+1 ] - len[ iSeg ]);
6081     gp_XYZ p = ( _offPoints[ iSeg + _iSeg[0]     ]._xyz * ( 1 - r ) +
6082                  _offPoints[ iSeg + _iSeg[0] + 1 ]._xyz * r );
6083
6084     if ( surface.IsNull() )
6085     {
6086       _eos[i]->_pos.back() = p;
6087     }
6088     else // project a new node position to a FACE
6089     {
6090       gp_Pnt2d uv ( _eos[i]->_pos.back().X(), _eos[i]->_pos.back().Y() );
6091       gp_Pnt2d uv2( surface->NextValueOfUV( uv, p, fTol ));
6092
6093       p = surface->Value( uv2 ).XYZ();
6094       _eos[i]->_pos.back().SetCoord( uv2.X(), uv2.Y(), 0 );
6095     }
6096     SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos[i]->_nodes.back() );
6097     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
6098     dumpMove( tgtNode );
6099   }
6100
6101   _offPoints[ _iSeg[0]   ]._xyz = opXYZ[0];
6102   _offPoints[ _iSeg[1]+1 ]._xyz = opXYZ[1];
6103
6104   return true;
6105 }
6106
6107 //================================================================================
6108 /*!
6109  * \brief Prepare for smoothing
6110  */
6111 //================================================================================
6112
6113 void _Smoother1D::prepare(_SolidData& data)
6114 {
6115   const TopoDS_Edge& E = TopoDS::Edge( _eos._shape );
6116   _curveLen = SMESH_Algo::EdgeLength( E );
6117
6118   // sort _LayerEdge's by position on the EDGE
6119   data.SortOnEdge( E, _eos._edges );
6120
6121   // compute normalized param of _eos._edges on EDGE
6122   _leParams.resize( _eos._edges.size() + 1 );
6123   {
6124     double curLen;
6125     gp_Pnt pPrev = SMESH_TNodeXYZ( getLEdgeOnV( 0 )->_nodes[0] );
6126     _leParams[0] = 0;
6127     for ( size_t i = 0; i < _eos._edges.size(); ++i )
6128     {
6129       gp_Pnt p       = SMESH_TNodeXYZ( _eos._edges[i]->_nodes[0] );
6130       curLen         = p.Distance( pPrev );
6131       _leParams[i+1] = _leParams[i] + curLen;
6132       pPrev          = p;
6133     }
6134     double fullLen = _leParams.back() + pPrev.Distance( SMESH_TNodeXYZ( getLEdgeOnV(1)->_nodes[0]));
6135     for ( size_t i = 0; i < _leParams.size()-1; ++i )
6136       _leParams[i] = _leParams[i+1] / fullLen;
6137     _leParams.back() = 1.;
6138   }
6139
6140   _LayerEdge* leOnV[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
6141
6142   // get cosin to use in findEdgesToSmooth()
6143   _edgeDir[0] = getEdgeDir( E, leOnV[0]->_nodes[0], data.GetHelper() );
6144   _edgeDir[1] = getEdgeDir( E, leOnV[1]->_nodes[0], data.GetHelper() );
6145   _leOnV[0]._cosin = Abs( leOnV[0]->_cosin );
6146   _leOnV[1]._cosin = Abs( leOnV[1]->_cosin );
6147   if ( _eos._sWOL.IsNull() ) // 3D
6148     for ( int iEnd = 0; iEnd < 2; ++iEnd )
6149       _leOnV[iEnd]._cosin = Abs( _edgeDir[iEnd].Normalized() * leOnV[iEnd]->_normal );
6150
6151   if ( isAnalytic() )
6152     return;
6153
6154   // divide E to have offset segments with low deflection
6155   BRepAdaptor_Curve c3dAdaptor( E );
6156   const double curDeflect = 0.1; //0.01; // Curvature deflection == |p1p2]*sin(p1p2,p1pM)
6157   const double angDeflect = 0.1; //0.09; // Angular deflection == sin(p1pM,pMp2)
6158   GCPnts_TangentialDeflection discret(c3dAdaptor, angDeflect, curDeflect);
6159   if ( discret.NbPoints() <= 2 )
6160   {
6161     _anaCurve = new Geom_Line( gp::OX() ); // only type does matter
6162     return;
6163   }
6164
6165   const double u0 = c3dAdaptor.FirstParameter();
6166   gp_Pnt p; gp_Vec tangent;
6167   if ( discret.NbPoints() >= (int) _eos.size() + 2 )
6168   {
6169     _offPoints.resize( discret.NbPoints() );
6170     for ( size_t i = 0; i < _offPoints.size(); i++ )
6171     {
6172       double u = discret.Parameter( i+1 );
6173       c3dAdaptor.D1( u, p, tangent );
6174       _offPoints[i]._xyz     = p.XYZ();
6175       _offPoints[i]._edgeDir = tangent.XYZ();
6176       _offPoints[i]._param = GCPnts_AbscissaPoint::Length( c3dAdaptor, u0, u ) / _curveLen;
6177     }
6178   }
6179   else
6180   {
6181     std::vector< double > params( _eos.size() + 2 );
6182
6183     params[0]     = data.GetHelper().GetNodeU( E, leOnV[0]->_nodes[0] );
6184     params.back() = data.GetHelper().GetNodeU( E, leOnV[1]->_nodes[0] );
6185     for ( size_t i = 0; i < _eos.size(); i++ )
6186       params[i+1] = data.GetHelper().GetNodeU( E, _eos[i]->_nodes[0] );
6187
6188     if ( params[1] > params[ _eos.size() ] )
6189       std::reverse( params.begin() + 1, params.end() - 1 );
6190
6191     _offPoints.resize( _eos.size() + 2 );
6192     for ( size_t i = 0; i < _offPoints.size(); i++ )
6193     {
6194       const double u = params[i];
6195       c3dAdaptor.D1( u, p, tangent );
6196       _offPoints[i]._xyz     = p.XYZ();
6197       _offPoints[i]._edgeDir = tangent.XYZ();
6198       _offPoints[i]._param = GCPnts_AbscissaPoint::Length( c3dAdaptor, u0, u ) / _curveLen;
6199     }
6200   }
6201
6202   // set _2edges
6203   _offPoints    [0]._2edges.set( &_leOnV[0], &_leOnV[0], 0.5, 0.5 );
6204   _offPoints.back()._2edges.set( &_leOnV[1], &_leOnV[1], 0.5, 0.5 );
6205   _2NearEdges tmp2edges;
6206   tmp2edges._edges[1] = _eos._edges[0];
6207   _leOnV[0]._2neibors = & tmp2edges;
6208   _leOnV[0]._nodes    = leOnV[0]->_nodes;
6209   _leOnV[1]._nodes    = leOnV[1]->_nodes;
6210   _LayerEdge* eNext, *ePrev = & _leOnV[0];
6211   for ( size_t iLE = 0, i = 1; i < _offPoints.size()-1; i++ )
6212   {
6213     // find _LayerEdge's located before and after an offset point
6214     // (_eos._edges[ iLE ] is next after ePrev)
6215     while ( iLE < _eos._edges.size() && _offPoints[i]._param > _leParams[ iLE ] )
6216       ePrev = _eos._edges[ iLE++ ];
6217     eNext = ePrev->_2neibors->_edges[1];
6218
6219     gp_Pnt p0 = SMESH_TNodeXYZ( ePrev->_nodes[0] );
6220     gp_Pnt p1 = SMESH_TNodeXYZ( eNext->_nodes[0] );
6221     double  r = p0.Distance( _offPoints[i]._xyz ) / p0.Distance( p1 );
6222     _offPoints[i]._2edges.set( ePrev, eNext, 1-r, r );
6223   }
6224
6225   // replace _LayerEdge's on VERTEX by _leOnV in _offPoints._2edges
6226   for ( size_t i = 0; i < _offPoints.size(); i++ )
6227     if ( _offPoints[i]._2edges._edges[0] == leOnV[0] )
6228       _offPoints[i]._2edges._edges[0] = & _leOnV[0];
6229     else break;
6230   for ( size_t i = _offPoints.size()-1; i > 0; i-- )
6231     if ( _offPoints[i]._2edges._edges[1] == leOnV[1] )
6232       _offPoints[i]._2edges._edges[1] = & _leOnV[1];
6233     else break;
6234
6235   // set _normal of _leOnV[0] and _leOnV[1] to be normal to the EDGE
6236
6237   int iLBO = _offPoints.size() - 2; // last but one
6238
6239   if ( leOnV[ 0 ]->Is( _LayerEdge::MULTI_NORMAL ))
6240     _leOnV[ 0 ]._normal = getNormalNormal( _eos._edges[1]->_normal, _edgeDir[0] );
6241   else
6242     _leOnV[ 0 ]._normal = getNormalNormal( leOnV[0]->_normal,       _edgeDir[0] );
6243   if ( leOnV[ 1 ]->Is( _LayerEdge::MULTI_NORMAL ))
6244     _leOnV[ 1 ]._normal = getNormalNormal( _eos._edges.back()->_normal, _edgeDir[1] );
6245   else
6246     _leOnV[ 1 ]._normal = getNormalNormal( leOnV[1]->_normal,           _edgeDir[1] );
6247   _leOnV[ 0 ]._len = 0;
6248   _leOnV[ 1 ]._len = 0;
6249   _leOnV[ 0 ]._lenFactor = _offPoints[1   ]._2edges._edges[1]->_lenFactor;
6250   _leOnV[ 1 ]._lenFactor = _offPoints[iLBO]._2edges._edges[0]->_lenFactor;
6251
6252   _iSeg[0] = 0;
6253   _iSeg[1] = _offPoints.size()-2;
6254
6255   // initialize OffPnt::_len
6256   for ( size_t i = 0; i < _offPoints.size(); ++i )
6257     _offPoints[i]._len = 0;
6258
6259   if ( _eos._edges[0]->NbSteps() > 1 ) // already inflated several times, init _xyz
6260   {
6261     _leOnV[0]._len = leOnV[0]->_len;
6262     _leOnV[1]._len = leOnV[1]->_len;
6263     for ( size_t i = 0; i < _offPoints.size(); i++ )
6264     {
6265       _LayerEdge*  e0 = _offPoints[i]._2edges._edges[0];
6266       _LayerEdge*  e1 = _offPoints[i]._2edges._edges[1];
6267       const double w0 = _offPoints[i]._2edges._wgt[0];
6268       const double w1 = _offPoints[i]._2edges._wgt[1];
6269       double  avgLen  = ( e0->_len * w0 + e1->_len * w1 );
6270       gp_XYZ  avgXYZ  = ( SMESH_TNodeXYZ( e0->_nodes.back() ) * w0 +
6271                           SMESH_TNodeXYZ( e1->_nodes.back() ) * w1 );
6272       _offPoints[i]._xyz = avgXYZ;
6273       _offPoints[i]._len = avgLen;
6274     }
6275   }
6276 }
6277
6278 //================================================================================
6279 /*!
6280  * \brief return _normal of _leOnV[is2nd] normal to the EDGE
6281  */
6282 //================================================================================
6283
6284 gp_XYZ _Smoother1D::getNormalNormal( const gp_XYZ & normal,
6285                                      const gp_XYZ&  edgeDir)
6286 {
6287   gp_XYZ cross = normal ^ edgeDir;
6288   gp_XYZ  norm = edgeDir ^ cross;
6289   double  size = norm.Modulus();
6290
6291   // if ( size == 0 ) // MULTI_NORMAL _LayerEdge
6292   //   return gp_XYZ( 1e-100, 1e-100, 1e-100 );
6293
6294   return norm / size;
6295 }
6296
6297 //================================================================================
6298 /*!
6299  * \brief Writes a script creating a mesh composed of _offPoints
6300  */
6301 //================================================================================
6302
6303 void _Smoother1D::offPointsToPython() const
6304 {
6305   const char* fname = "/tmp/offPoints.py";
6306   cout << "execfile('"<<fname<<"')"<<endl;
6307   ofstream py(fname);
6308   py << "import SMESH" << endl
6309      << "from salome.smesh import smeshBuilder" << endl
6310      << "smesh  = smeshBuilder.New(salome.myStudy)" << endl
6311      << "mesh   = smesh.Mesh( 'offPoints' )"<<endl;
6312   for ( size_t i = 0; i < _offPoints.size(); i++ )
6313   {
6314     py << "mesh.AddNode( "
6315        << _offPoints[i]._xyz.X() << ", "
6316        << _offPoints[i]._xyz.Y() << ", "
6317        << _offPoints[i]._xyz.Z() << " )" << endl;
6318   }
6319 }
6320
6321 //================================================================================
6322 /*!
6323  * \brief Sort _LayerEdge's by a parameter on a given EDGE
6324  */
6325 //================================================================================
6326
6327 void _SolidData::SortOnEdge( const TopoDS_Edge&     E,
6328                              vector< _LayerEdge* >& edges)
6329 {
6330   map< double, _LayerEdge* > u2edge;
6331   for ( size_t i = 0; i < edges.size(); ++i )
6332     u2edge.insert( u2edge.end(),
6333                    make_pair( _helper->GetNodeU( E, edges[i]->_nodes[0] ), edges[i] ));
6334
6335   ASSERT( u2edge.size() == edges.size() );
6336   map< double, _LayerEdge* >::iterator u2e = u2edge.begin();
6337   for ( size_t i = 0; i < edges.size(); ++i, ++u2e )
6338     edges[i] = u2e->second;
6339
6340   Sort2NeiborsOnEdge( edges );
6341 }
6342
6343 //================================================================================
6344 /*!
6345  * \brief Set _2neibors according to the order of _LayerEdge on EDGE
6346  */
6347 //================================================================================
6348
6349 void _SolidData::Sort2NeiborsOnEdge( vector< _LayerEdge* >& edges )
6350 {
6351   if ( edges.size() < 2 || !edges[0]->_2neibors ) return;
6352
6353   for ( size_t i = 0; i < edges.size()-1; ++i )
6354     if ( edges[i]->_2neibors->tgtNode(1) != edges[i+1]->_nodes.back() )
6355       edges[i]->_2neibors->reverse();
6356
6357   const size_t iLast = edges.size() - 1;
6358   if ( edges.size() > 1 &&
6359        edges[iLast]->_2neibors->tgtNode(0) != edges[iLast-1]->_nodes.back() )
6360     edges[iLast]->_2neibors->reverse();
6361 }
6362
6363 //================================================================================
6364 /*!
6365  * \brief Return _EdgesOnShape* corresponding to the shape
6366  */
6367 //================================================================================
6368
6369 _EdgesOnShape* _SolidData::GetShapeEdges(const TGeomID shapeID )
6370 {
6371   if ( shapeID < (int)_edgesOnShape.size() &&
6372        _edgesOnShape[ shapeID ]._shapeID == shapeID )
6373     return _edgesOnShape[ shapeID ]._subMesh ? & _edgesOnShape[ shapeID ] : 0;
6374
6375   for ( size_t i = 0; i < _edgesOnShape.size(); ++i )
6376     if ( _edgesOnShape[i]._shapeID == shapeID )
6377       return _edgesOnShape[i]._subMesh ? & _edgesOnShape[i] : 0;
6378
6379   return 0;
6380 }
6381
6382 //================================================================================
6383 /*!
6384  * \brief Return _EdgesOnShape* corresponding to the shape
6385  */
6386 //================================================================================
6387
6388 _EdgesOnShape* _SolidData::GetShapeEdges(const TopoDS_Shape& shape )
6389 {
6390   SMESHDS_Mesh* meshDS = _proxyMesh->GetMesh()->GetMeshDS();
6391   return GetShapeEdges( meshDS->ShapeToIndex( shape ));
6392 }
6393
6394 //================================================================================
6395 /*!
6396  * \brief Prepare data of the _LayerEdge for smoothing on FACE
6397  */
6398 //================================================================================
6399
6400 void _SolidData::PrepareEdgesToSmoothOnFace( _EdgesOnShape* eos, bool substituteSrcNodes )
6401 {
6402   SMESH_MesherHelper helper( *_proxyMesh->GetMesh() );
6403
6404   set< TGeomID > vertices;
6405   TopoDS_Face F;
6406   if ( eos->ShapeType() == TopAbs_FACE )
6407   {
6408     // check FACE concavity and get concave VERTEXes
6409     F = TopoDS::Face( eos->_shape );
6410     if ( isConcave( F, helper, &vertices ))
6411       _concaveFaces.insert( eos->_shapeID );
6412
6413     // set eos._eosConcaVer
6414     eos->_eosConcaVer.clear();
6415     eos->_eosConcaVer.reserve( vertices.size() );
6416     for ( set< TGeomID >::iterator v = vertices.begin(); v != vertices.end(); ++v )
6417     {
6418       _EdgesOnShape* eov = GetShapeEdges( *v );
6419       if ( eov && eov->_edges.size() == 1 )
6420       {
6421         eos->_eosConcaVer.push_back( eov );
6422         for ( size_t i = 0; i < eov->_edges[0]->_neibors.size(); ++i )
6423           eov->_edges[0]->_neibors[i]->Set( _LayerEdge::DIFFICULT );
6424       }
6425     }
6426
6427     // SetSmooLen() to _LayerEdge's on FACE
6428     // for ( size_t i = 0; i < eos->_edges.size(); ++i )
6429     // {
6430     //   eos->_edges[i]->SetSmooLen( Precision::Infinite() );
6431     // }
6432     // SMESH_subMeshIteratorPtr smIt = eos->_subMesh->getDependsOnIterator(/*includeSelf=*/false);
6433     // while ( smIt->more() ) // loop on sub-shapes of the FACE
6434     // {
6435     //   _EdgesOnShape* eoe = GetShapeEdges( smIt->next()->GetId() );
6436     //   if ( !eoe ) continue;
6437
6438     //   vector<_LayerEdge*>& eE = eoe->_edges;
6439     //   for ( size_t iE = 0; iE < eE.size(); ++iE ) // loop on _LayerEdge's on EDGE or VERTEX
6440     //   {
6441     //     if ( eE[iE]->_cosin <= theMinSmoothCosin )
6442     //       continue;
6443
6444     //     SMDS_ElemIteratorPtr segIt = eE[iE]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Edge);
6445     //     while ( segIt->more() )
6446     //     {
6447     //       const SMDS_MeshElement* seg = segIt->next();
6448     //       if ( !eos->_subMesh->DependsOn( seg->getshapeId() ))
6449     //         continue;
6450     //       if ( seg->GetNode(0) != eE[iE]->_nodes[0] )
6451     //         continue; // not to check a seg twice
6452     //       for ( size_t iN = 0; iN < eE[iE]->_neibors.size(); ++iN )
6453     //       {
6454     //         _LayerEdge* eN = eE[iE]->_neibors[iN];
6455     //         if ( eN->_nodes[0]->getshapeId() != eos->_shapeID )
6456     //           continue;
6457     //         double dist    = SMESH_MeshAlgos::GetDistance( seg, SMESH_TNodeXYZ( eN->_nodes[0] ));
6458     //         double smooLen = getSmoothingThickness( eE[iE]->_cosin, dist );
6459     //         eN->SetSmooLen( Min( smooLen, eN->GetSmooLen() ));
6460     //         eN->Set( _LayerEdge::NEAR_BOUNDARY );
6461     //       }
6462     //     }
6463     //   }
6464     // }
6465   } // if ( eos->ShapeType() == TopAbs_FACE )
6466
6467   for ( size_t i = 0; i < eos->_edges.size(); ++i )
6468   {
6469     eos->_edges[i]->_smooFunction = 0;
6470     eos->_edges[i]->Set( _LayerEdge::TO_SMOOTH );
6471   }
6472   bool isCurved = false;
6473   for ( size_t i = 0; i < eos->_edges.size(); ++i )
6474   {
6475     _LayerEdge* edge = eos->_edges[i];
6476
6477     // get simplices sorted
6478     _Simplex::SortSimplices( edge->_simplices );
6479
6480     // smoothing function
6481     edge->ChooseSmooFunction( vertices, _n2eMap );
6482
6483     // set _curvature
6484     double avgNormProj = 0, avgLen = 0;
6485     for ( size_t iS = 0; iS < edge->_simplices.size(); ++iS )
6486     {
6487       _Simplex& s = edge->_simplices[iS];
6488
6489       gp_XYZ  vec = edge->_pos.back() - SMESH_TNodeXYZ( s._nPrev );
6490       avgNormProj += edge->_normal * vec;
6491       avgLen      += vec.Modulus();
6492       if ( substituteSrcNodes )
6493       {
6494         s._nNext = _n2eMap[ s._nNext ]->_nodes.back();
6495         s._nPrev = _n2eMap[ s._nPrev ]->_nodes.back();
6496       }
6497     }
6498     avgNormProj /= edge->_simplices.size();
6499     avgLen      /= edge->_simplices.size();
6500     if (( edge->_curvature = _Curvature::New( avgNormProj, avgLen )))
6501     {
6502       edge->Set( _LayerEdge::SMOOTHED_C1 );
6503       isCurved = true;
6504       SMDS_FacePosition* fPos = dynamic_cast<SMDS_FacePosition*>( edge->_nodes[0]->GetPosition() );
6505       if ( !fPos )
6506         for ( size_t iS = 0; iS < edge->_simplices.size()  &&  !fPos; ++iS )
6507           fPos = dynamic_cast<SMDS_FacePosition*>( edge->_simplices[iS]._nPrev->GetPosition() );
6508       if ( fPos )
6509         edge->_curvature->_uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
6510     }
6511   }
6512
6513   // prepare for putOnOffsetSurface()
6514   if (( eos->ShapeType() == TopAbs_FACE ) &&
6515       ( isCurved || !eos->_eosConcaVer.empty() ))
6516   {
6517     eos->_offsetSurf = helper.GetSurface( TopoDS::Face( eos->_shape ));
6518     eos->_edgeForOffset = 0;
6519
6520     double maxCosin = -1;
6521     for ( TopExp_Explorer eExp( eos->_shape, TopAbs_EDGE ); eExp.More(); eExp.Next() )
6522     {
6523       _EdgesOnShape* eoe = GetShapeEdges( eExp.Current() );
6524       if ( !eoe || eoe->_edges.empty() ) continue;
6525
6526       vector<_LayerEdge*>& eE = eoe->_edges;
6527       _LayerEdge* e = eE[ eE.size() / 2 ];
6528       if ( e->_cosin > maxCosin )
6529       {
6530         eos->_edgeForOffset = e;
6531         maxCosin = e->_cosin;
6532       }
6533     }
6534   }
6535 }
6536
6537 //================================================================================
6538 /*!
6539  * \brief Add faces for smoothing
6540  */
6541 //================================================================================
6542
6543 void _SolidData::AddShapesToSmooth( const set< _EdgesOnShape* >& eosToSmooth,
6544                                     const set< _EdgesOnShape* >* edgesNoAnaSmooth )
6545 {
6546   set< _EdgesOnShape * >::const_iterator eos = eosToSmooth.begin();
6547   for ( ; eos != eosToSmooth.end(); ++eos )
6548   {
6549     if ( !*eos || (*eos)->_toSmooth ) continue;
6550
6551     (*eos)->_toSmooth = true;
6552
6553     if ( (*eos)->ShapeType() == TopAbs_FACE )
6554     {
6555       PrepareEdgesToSmoothOnFace( *eos, /*substituteSrcNodes=*/false );
6556       (*eos)->_toSmooth = true;
6557     }
6558   }
6559
6560   // avoid _Smoother1D::smoothAnalyticEdge() of edgesNoAnaSmooth
6561   if ( edgesNoAnaSmooth )
6562     for ( eos = edgesNoAnaSmooth->begin(); eos != edgesNoAnaSmooth->end(); ++eos )
6563     {
6564       if ( (*eos)->_edgeSmoother )
6565         (*eos)->_edgeSmoother->_anaCurve.Nullify();
6566     }
6567 }
6568
6569 //================================================================================
6570 /*!
6571  * \brief Limit _LayerEdge::_maxLen according to local curvature
6572  */
6573 //================================================================================
6574
6575 void _ViscousBuilder::limitMaxLenByCurvature( _SolidData& data, SMESH_MesherHelper& helper )
6576 {
6577   // find intersection of neighbor _LayerEdge's to limit _maxLen
6578   // according to local curvature (IPAL52648)
6579
6580   // This method must be called after findCollisionEdges() where _LayerEdge's
6581   // get _lenFactor initialized in the case of eos._hyp.IsOffsetMethod()
6582
6583   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
6584   {
6585     _EdgesOnShape& eosI = data._edgesOnShape[iS];
6586     if ( eosI._edges.empty() ) continue;
6587     if ( !eosI._hyp.ToSmooth() )
6588     {
6589       for ( size_t i = 0; i < eosI._edges.size(); ++i )
6590       {
6591         _LayerEdge* eI = eosI._edges[i];
6592         for ( size_t iN = 0; iN < eI->_neibors.size(); ++iN )
6593         {
6594           _LayerEdge* eN = eI->_neibors[iN];
6595           if ( eI->_nodes[0]->GetID() < eN->_nodes[0]->GetID() ) // treat this pair once
6596           {
6597             _EdgesOnShape* eosN = data.GetShapeEdges( eN );
6598             limitMaxLenByCurvature( eI, eN, eosI, *eosN, eosI._hyp.ToSmooth() );
6599           }
6600         }
6601       }
6602     }
6603     else if ( eosI.ShapeType() == TopAbs_EDGE )
6604     {
6605       const TopoDS_Edge& E = TopoDS::Edge( eosI._shape );
6606       if ( SMESH_Algo::IsStraight( E, /*degenResult=*/true )) continue;
6607
6608       _LayerEdge* e0 = eosI._edges[0];
6609       for ( size_t i = 1; i < eosI._edges.size(); ++i )
6610       {
6611         _LayerEdge* eI = eosI._edges[i];
6612         limitMaxLenByCurvature( eI, e0, eosI, eosI, eosI._hyp.ToSmooth() );
6613         e0 = eI;
6614       }
6615     }
6616   }
6617 }
6618
6619 //================================================================================
6620 /*!
6621  * \brief Limit _LayerEdge::_maxLen according to local curvature
6622  */
6623 //================================================================================
6624
6625 void _ViscousBuilder::limitMaxLenByCurvature( _LayerEdge*    e1,
6626                                               _LayerEdge*    e2,
6627                                               _EdgesOnShape& eos1,
6628                                               _EdgesOnShape& eos2,
6629                                               const bool     isSmoothable )
6630 {
6631   if (( e1->_nodes[0]->GetPosition()->GetDim() !=
6632         e2->_nodes[0]->GetPosition()->GetDim() ) &&
6633       ( e1->_cosin < 0.75 ))
6634     return; // angle > 90 deg at e1
6635
6636   gp_XYZ plnNorm = e1->_normal ^ e2->_normal;
6637   double norSize = plnNorm.SquareModulus();
6638   if ( norSize < std::numeric_limits<double>::min() )
6639     return; // parallel normals
6640
6641   // find closest points of skew _LayerEdge's
6642   SMESH_TNodeXYZ src1( e1->_nodes[0] ), src2( e2->_nodes[0] );
6643   gp_XYZ dir12 = src2 - src1;
6644   gp_XYZ perp1 = e1->_normal ^ plnNorm;
6645   gp_XYZ perp2 = e2->_normal ^ plnNorm;
6646   double  dot1 = perp2 * e1->_normal;
6647   double  dot2 = perp1 * e2->_normal;
6648   double    u1 =   ( perp2 * dir12 ) / dot1;
6649   double    u2 = - ( perp1 * dir12 ) / dot2;
6650   if ( u1 > 0 && u2 > 0 )
6651   {
6652     double ovl = ( u1 * e1->_normal * dir12 -
6653                    u2 * e2->_normal * dir12 ) / dir12.SquareModulus();
6654     if ( ovl > theSmoothThickToElemSizeRatio )
6655     {
6656       const double coef = 0.75;
6657       e1->SetMaxLen( Min( e1->_maxLen, coef * u1 / e1->_lenFactor ));
6658       e2->SetMaxLen( Min( e2->_maxLen, coef * u2 / e2->_lenFactor ));
6659     }
6660   }
6661 }
6662
6663 //================================================================================
6664 /*!
6665  * \brief Fill data._collisionEdges
6666  */
6667 //================================================================================
6668
6669 void _ViscousBuilder::findCollisionEdges( _SolidData& data, SMESH_MesherHelper& helper )
6670 {
6671   data._collisionEdges.clear();
6672
6673   // set the full thickness of the layers to LEs
6674   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
6675   {
6676     _EdgesOnShape& eos = data._edgesOnShape[iS];
6677     if ( eos._edges.empty() ) continue;
6678     if ( eos.ShapeType() != TopAbs_EDGE && eos.ShapeType() != TopAbs_VERTEX ) continue;
6679
6680     for ( size_t i = 0; i < eos._edges.size(); ++i )
6681     {
6682       if ( eos._edges[i]->Is( _LayerEdge::BLOCKED )) continue;
6683       double maxLen = eos._edges[i]->_maxLen;
6684       eos._edges[i]->_maxLen = Precision::Infinite(); // avoid blocking
6685       eos._edges[i]->SetNewLength( 1.5 * maxLen, eos, helper );
6686       eos._edges[i]->_maxLen = maxLen;
6687     }
6688   }
6689
6690   // make temporary quadrangles got by extrusion of
6691   // mesh edges along _LayerEdge._normal's
6692
6693   vector< const SMDS_MeshElement* > tmpFaces;
6694
6695   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
6696   {
6697     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
6698     if ( eos.ShapeType() != TopAbs_EDGE )
6699       continue;
6700     if ( eos._edges.empty() )
6701     {
6702       _LayerEdge* edge[2] = { 0, 0 }; // LE of 2 VERTEX'es
6703       SMESH_subMeshIteratorPtr smIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false);
6704       while ( smIt->more() )
6705         if ( _EdgesOnShape* eov = data.GetShapeEdges( smIt->next()->GetId() ))
6706           if ( eov->_edges.size() == 1 )
6707             edge[ bool( edge[0]) ] = eov->_edges[0];
6708
6709       if ( edge[1] )
6710       {
6711         _TmpMeshFaceOnEdge* f = new _TmpMeshFaceOnEdge( edge[0], edge[1], --_tmpFaceID );
6712         tmpFaces.push_back( f );
6713       }
6714     }
6715     for ( size_t i = 0; i < eos._edges.size(); ++i )
6716     {
6717       _LayerEdge* edge = eos._edges[i];
6718       for ( int j = 0; j < 2; ++j ) // loop on _2NearEdges
6719       {
6720         const SMDS_MeshNode* src2 = edge->_2neibors->srcNode(j);
6721         if ( src2->GetPosition()->GetDim() > 0 &&
6722              src2->GetID() < edge->_nodes[0]->GetID() )
6723           continue; // avoid using same segment twice
6724
6725         // a _LayerEdge containing tgt2
6726         _LayerEdge* neiborEdge = edge->_2neibors->_edges[j];
6727
6728         _TmpMeshFaceOnEdge* f = new _TmpMeshFaceOnEdge( edge, neiborEdge, --_tmpFaceID );
6729         tmpFaces.push_back( f );
6730       }
6731     }
6732   }
6733
6734   // Find _LayerEdge's intersecting tmpFaces.
6735
6736   SMDS_ElemIteratorPtr fIt( new SMDS_ElementVectorIterator( tmpFaces.begin(),
6737                                                             tmpFaces.end()));
6738   SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
6739     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(), fIt ));
6740
6741   double dist1, dist2, segLen, eps = 0.5;
6742   _CollisionEdges collEdges;
6743   vector< const SMDS_MeshElement* > suspectFaces;
6744   const double angle45 = Cos( 45. * M_PI / 180. );
6745
6746   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
6747   {
6748     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
6749     if ( eos.ShapeType() == TopAbs_FACE || !eos._sWOL.IsNull() )
6750       continue;
6751     // find sub-shapes whose VL can influence VL on eos
6752     set< TGeomID > neighborShapes;
6753     PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
6754     while ( const TopoDS_Shape* face = fIt->next() )
6755     {
6756       TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
6757       if ( _EdgesOnShape* eof = data.GetShapeEdges( faceID ))
6758       {
6759         SMESH_subMeshIteratorPtr subIt = eof->_subMesh->getDependsOnIterator(/*includeSelf=*/false);
6760         while ( subIt->more() )
6761           neighborShapes.insert( subIt->next()->GetId() );
6762       }
6763     }
6764     if ( eos.ShapeType() == TopAbs_VERTEX )
6765     {
6766       PShapeIteratorPtr eIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_EDGE );
6767       while ( const TopoDS_Shape* edge = eIt->next() )
6768         neighborShapes.erase( getMeshDS()->ShapeToIndex( *edge ));
6769     }
6770     // find intersecting _LayerEdge's
6771     for ( size_t i = 0; i < eos._edges.size(); ++i )
6772     {
6773       if ( eos._edges[i]->Is( _LayerEdge::MULTI_NORMAL )) continue;
6774       _LayerEdge*   edge = eos._edges[i];
6775       gp_Ax1 lastSegment = edge->LastSegment( segLen, eos );
6776       segLen *= 1.2;
6777
6778       gp_Vec eSegDir0, eSegDir1;
6779       if ( edge->IsOnEdge() )
6780       {
6781         SMESH_TNodeXYZ eP( edge->_nodes[0] );
6782         eSegDir0 = SMESH_TNodeXYZ( edge->_2neibors->srcNode(0) ) - eP;
6783         eSegDir1 = SMESH_TNodeXYZ( edge->_2neibors->srcNode(1) ) - eP;
6784       }
6785       suspectFaces.clear();
6786       searcher->GetElementsInSphere( SMESH_TNodeXYZ( edge->_nodes.back()), edge->_len * 2,
6787                                      SMDSAbs_Face, suspectFaces );
6788       collEdges._intEdges.clear();
6789       for ( size_t j = 0 ; j < suspectFaces.size(); ++j )
6790       {
6791         const _TmpMeshFaceOnEdge* f = (const _TmpMeshFaceOnEdge*) suspectFaces[j];
6792         if ( f->_le1 == edge || f->_le2 == edge ) continue;
6793         if ( !neighborShapes.count( f->_le1->_nodes[0]->getshapeId() )) continue;
6794         if ( !neighborShapes.count( f->_le2->_nodes[0]->getshapeId() )) continue;
6795         if ( edge->IsOnEdge() ) {
6796           if ( edge->_2neibors->include( f->_le1 ) ||
6797                edge->_2neibors->include( f->_le2 )) continue;
6798         }
6799         else {
6800           if (( f->_le1->IsOnEdge() && f->_le1->_2neibors->include( edge )) ||
6801               ( f->_le2->IsOnEdge() && f->_le2->_2neibors->include( edge )))  continue;
6802         }
6803         dist1 = dist2 = Precision::Infinite();
6804         if ( !edge->SegTriaInter( lastSegment, f->_nn[0], f->_nn[1], f->_nn[2], dist1, eps ))
6805           dist1 = Precision::Infinite();
6806         if ( !edge->SegTriaInter( lastSegment, f->_nn[3], f->_nn[2], f->_nn[0], dist2, eps ))
6807           dist2 = Precision::Infinite();
6808         if (( dist1 > segLen ) && ( dist2 > segLen ))
6809           continue;
6810
6811         if ( edge->IsOnEdge() )
6812         {
6813           // skip perpendicular EDGEs
6814           gp_Vec fSegDir  = SMESH_TNodeXYZ( f->_nn[0] ) - SMESH_TNodeXYZ( f->_nn[3] );
6815           bool isParallel = ( isLessAngle( eSegDir0, fSegDir, angle45 ) ||
6816                               isLessAngle( eSegDir1, fSegDir, angle45 ) ||
6817                               isLessAngle( eSegDir0, fSegDir.Reversed(), angle45 ) ||
6818                               isLessAngle( eSegDir1, fSegDir.Reversed(), angle45 ));
6819           if ( !isParallel )
6820             continue;
6821         }
6822
6823         // either limit inflation of edges or remember them for updating _normal
6824         // double dot = edge->_normal * f->GetDir();
6825         // if ( dot > 0.1 )
6826         {
6827           collEdges._intEdges.push_back( f->_le1 );
6828           collEdges._intEdges.push_back( f->_le2 );
6829         }
6830         // else
6831         // {
6832         //   double shortLen = 0.75 * ( Min( dist1, dist2 ) / edge->_lenFactor );
6833         //   edge->SetMaxLen( Min( shortLen, edge->_maxLen ));
6834         // }
6835       }
6836
6837       if ( !collEdges._intEdges.empty() )
6838       {
6839         collEdges._edge = edge;
6840         data._collisionEdges.push_back( collEdges );
6841       }
6842     }
6843   }
6844
6845   for ( size_t i = 0 ; i < tmpFaces.size(); ++i )
6846     delete tmpFaces[i];
6847
6848   // restore the zero thickness
6849   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
6850   {
6851     _EdgesOnShape& eos = data._edgesOnShape[iS];
6852     if ( eos._edges.empty() ) continue;
6853     if ( eos.ShapeType() != TopAbs_EDGE && eos.ShapeType() != TopAbs_VERTEX ) continue;
6854
6855     for ( size_t i = 0; i < eos._edges.size(); ++i )
6856     {
6857       eos._edges[i]->InvalidateStep( 1, eos );
6858       eos._edges[i]->_len = 0;
6859     }
6860   }
6861 }
6862
6863 //================================================================================
6864 /*!
6865  * \brief Find _LayerEdge's located on boundary of a convex FACE whose normal
6866  *        will be updated at each inflation step
6867  */
6868 //================================================================================
6869
6870 void _ViscousBuilder::findEdgesToUpdateNormalNearConvexFace( _ConvexFace &       convFace,
6871                                                              _SolidData&         data,
6872                                                              SMESH_MesherHelper& helper )
6873 {
6874   const TGeomID convFaceID = getMeshDS()->ShapeToIndex( convFace._face );
6875   const double       preci = BRep_Tool::Tolerance( convFace._face );
6876   Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( convFace._face );
6877
6878   bool edgesToUpdateFound = false;
6879
6880   map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.begin();
6881   for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
6882   {
6883     _EdgesOnShape& eos = * id2eos->second;
6884     if ( !eos._sWOL.IsNull() ) continue;
6885     if ( !eos._hyp.ToSmooth() ) continue;
6886     for ( size_t i = 0; i < eos._edges.size(); ++i )
6887     {
6888       _LayerEdge* ledge = eos._edges[ i ];
6889       if ( ledge->Is( _LayerEdge::UPD_NORMAL_CONV )) continue; // already checked
6890       if ( ledge->Is( _LayerEdge::MULTI_NORMAL )) continue; // not inflatable
6891
6892       gp_XYZ tgtPos = ( SMESH_NodeXYZ( ledge->_nodes[0] ) +
6893                         ledge->_normal * ledge->_lenFactor * ledge->_maxLen );
6894
6895       // the normal must be updated if distance from tgtPos to surface is less than
6896       // target thickness
6897
6898       // find an initial UV for search of a projection of tgtPos to surface
6899       const SMDS_MeshNode* nodeInFace = 0;
6900       SMDS_ElemIteratorPtr fIt = ledge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
6901       while ( fIt->more() && !nodeInFace )
6902       {
6903         const SMDS_MeshElement* f = fIt->next();
6904         if ( convFaceID != f->getshapeId() ) continue;
6905
6906         SMDS_ElemIteratorPtr nIt = f->nodesIterator();
6907         while ( nIt->more() && !nodeInFace )
6908         {
6909           const SMDS_MeshElement* n = nIt->next();
6910           if ( n->getshapeId() == convFaceID )
6911             nodeInFace = static_cast< const SMDS_MeshNode* >( n );
6912         }
6913       }
6914       if ( !nodeInFace )
6915         continue;
6916       gp_XY uv = helper.GetNodeUV( convFace._face, nodeInFace );
6917
6918       // projection
6919       surface->NextValueOfUV( uv, tgtPos, preci );
6920       double  dist = surface->Gap();
6921       if ( dist < 0.95 * ledge->_maxLen )
6922       {
6923         ledge->Set( _LayerEdge::UPD_NORMAL_CONV );
6924         if ( !ledge->_curvature ) ledge->_curvature = new _Curvature;
6925         ledge->_curvature->_uv.SetCoord( uv.X(), uv.Y() );
6926         edgesToUpdateFound = true;
6927       }
6928     }
6929   }
6930
6931   if ( !convFace._isTooCurved && edgesToUpdateFound )
6932   {
6933     data._convexFaces.insert( make_pair( convFaceID, convFace )).first->second;
6934   }
6935 }
6936
6937 //================================================================================
6938 /*!
6939  * \brief Modify normals of _LayerEdge's on EDGE's to avoid intersection with
6940  * _LayerEdge's on neighbor EDGE's
6941  */
6942 //================================================================================
6943
6944 bool _ViscousBuilder::updateNormals( _SolidData&         data,
6945                                      SMESH_MesherHelper& helper,
6946                                      int                 stepNb,
6947                                      double              stepSize)
6948 {
6949   updateNormalsOfC1Vertices( data );
6950
6951   if ( stepNb > 0 && !updateNormalsOfConvexFaces( data, helper, stepNb ))
6952     return false;
6953
6954   // map to store new _normal and _cosin for each intersected edge
6955   map< _LayerEdge*, _LayerEdge, _LayerEdgeCmp >           edge2newEdge;
6956   map< _LayerEdge*, _LayerEdge, _LayerEdgeCmp >::iterator e2neIt;
6957   _LayerEdge zeroEdge;
6958   zeroEdge._normal.SetCoord( 0,0,0 );
6959   zeroEdge._maxLen = Precision::Infinite();
6960   zeroEdge._nodes.resize(1); // to init _TmpMeshFaceOnEdge
6961
6962   set< _EdgesOnShape* > shapesToSmooth, edgesNoAnaSmooth;
6963
6964   double segLen, dist1, dist2, dist;
6965   vector< pair< _LayerEdge*, double > > intEdgesDist;
6966   _TmpMeshFaceOnEdge quad( &zeroEdge, &zeroEdge, 0 );
6967
6968   for ( int iter = 0; iter < 5; ++iter )
6969   {
6970     edge2newEdge.clear();
6971
6972     for ( size_t iE = 0; iE < data._collisionEdges.size(); ++iE )
6973     {
6974       _CollisionEdges& ce = data._collisionEdges[iE];
6975       _LayerEdge*   edge1 = ce._edge;
6976       if ( !edge1 /*|| edge1->Is( _LayerEdge::BLOCKED )*/) continue;
6977       _EdgesOnShape* eos1 = data.GetShapeEdges( edge1 );
6978       if ( !eos1 ) continue;
6979
6980       // detect intersections
6981       gp_Ax1 lastSeg = edge1->LastSegment( segLen, *eos1 );
6982       double testLen = 1.5 * edge1->_maxLen * edge1->_lenFactor;
6983       double     eps = 0.5;
6984       intEdgesDist.clear();
6985       double minIntDist = Precision::Infinite();
6986       for ( size_t i = 0; i < ce._intEdges.size(); i += 2 )
6987       {
6988         if ( edge1->Is( _LayerEdge::BLOCKED ) &&
6989              ce._intEdges[i  ]->Is( _LayerEdge::BLOCKED ) &&
6990              ce._intEdges[i+1]->Is( _LayerEdge::BLOCKED ))
6991           continue;
6992         double dot  = edge1->_normal * quad.GetDir( ce._intEdges[i], ce._intEdges[i+1] );
6993         double fact = ( 1.1 + dot * dot );
6994         SMESH_TNodeXYZ pSrc0( ce.nSrc(i) ), pSrc1( ce.nSrc(i+1) );
6995         SMESH_TNodeXYZ pTgt0( ce.nTgt(i) ), pTgt1( ce.nTgt(i+1) );
6996         gp_XYZ pLast0 = pSrc0 + ( pTgt0 - pSrc0 ) * fact;
6997         gp_XYZ pLast1 = pSrc1 + ( pTgt1 - pSrc1 ) * fact;
6998         dist1 = dist2 = Precision::Infinite();
6999         if ( !edge1->SegTriaInter( lastSeg, pSrc0, pLast0, pSrc1,  dist1, eps ) &&
7000              !edge1->SegTriaInter( lastSeg, pSrc1, pLast1, pLast0, dist2, eps ))
7001           continue;
7002         dist = dist1;
7003         if ( dist > testLen || dist <= 0 )
7004         {
7005           dist = dist2;
7006           if ( dist > testLen || dist <= 0 )
7007             continue;
7008         }
7009         // choose a closest edge
7010         gp_Pnt intP( lastSeg.Location().XYZ() + lastSeg.Direction().XYZ() * ( dist + segLen ));
7011         double d1 = intP.SquareDistance( pSrc0 );
7012         double d2 = intP.SquareDistance( pSrc1 );
7013         int iClose = i + ( d2 < d1 );
7014         _LayerEdge* edge2 = ce._intEdges[iClose];
7015         edge2->Unset( _LayerEdge::MARKED );
7016
7017         // choose a closest edge among neighbors
7018         gp_Pnt srcP( SMESH_TNodeXYZ( edge1->_nodes[0] ));
7019         d1 = srcP.SquareDistance( SMESH_TNodeXYZ( edge2->_nodes[0] ));
7020         for ( size_t j = 0; j < intEdgesDist.size(); ++j )
7021         {
7022           _LayerEdge * edgeJ = intEdgesDist[j].first;
7023           if ( edge2->IsNeiborOnEdge( edgeJ ))
7024           {
7025             d2 = srcP.SquareDistance( SMESH_TNodeXYZ( edgeJ->_nodes[0] ));
7026             ( d1 < d2 ? edgeJ : edge2 )->Set( _LayerEdge::MARKED );
7027           }
7028         }
7029         intEdgesDist.push_back( make_pair( edge2, dist ));
7030         // if ( Abs( d2 - d1 ) / Max( d2, d1 ) < 0.5 )
7031         // {
7032         //   iClose = i + !( d2 < d1 );
7033         //   intEdges.push_back( ce._intEdges[iClose] );
7034         //   ce._intEdges[iClose]->Unset( _LayerEdge::MARKED );
7035         // }
7036         minIntDist = Min( edge1->_len * edge1->_lenFactor - segLen + dist, minIntDist );
7037       }
7038
7039       //ce._edge = 0;
7040
7041       // compute new _normals
7042       for ( size_t i = 0; i < intEdgesDist.size(); ++i )
7043       {
7044         _LayerEdge* edge2   = intEdgesDist[i].first;
7045         double      distWgt = edge1->_len / intEdgesDist[i].second;
7046         // if ( edge1->Is( _LayerEdge::BLOCKED ) &&
7047         //      edge2->Is( _LayerEdge::BLOCKED )) continue;        
7048         if ( edge2->Is( _LayerEdge::MARKED )) continue;
7049         edge2->Set( _LayerEdge::MARKED );
7050
7051         // get a new normal
7052         gp_XYZ dir1 = edge1->_normal, dir2 = edge2->_normal;
7053
7054         double cos1 = Abs( edge1->_cosin ), cos2 = Abs( edge2->_cosin );
7055         double wgt1 = ( cos1 + 0.001 ) / ( cos1 + cos2 + 0.002 );
7056         double wgt2 = ( cos2 + 0.001 ) / ( cos1 + cos2 + 0.002 );
7057         // double cos1 = Abs( edge1->_cosin ),        cos2 = Abs( edge2->_cosin );
7058         // double sgn1 = 0.1 * ( 1 + edge1->_cosin ), sgn2 = 0.1 * ( 1 + edge2->_cosin );
7059         // double wgt1 = ( cos1 + sgn1 ) / ( cos1 + cos2 + sgn1 + sgn2 );
7060         // double wgt2 = ( cos2 + sgn2 ) / ( cos1 + cos2 + sgn1 + sgn2 );
7061         gp_XYZ newNormal = wgt1 * dir1 + wgt2 * dir2;
7062         newNormal.Normalize();
7063
7064         // get new cosin
7065         double newCos;
7066         double sgn1 = edge1->_cosin / cos1, sgn2 = edge2->_cosin / cos2;
7067         if ( cos1 < theMinSmoothCosin )
7068         {
7069           newCos = cos2 * sgn1;
7070         }
7071         else if ( cos2 > theMinSmoothCosin ) // both cos1 and cos2 > theMinSmoothCosin
7072         {
7073           newCos = ( wgt1 * cos1 + wgt2 * cos2 ) * edge1->_cosin / cos1;
7074         }
7075         else
7076         {
7077           newCos = edge1->_cosin;
7078         }
7079
7080         e2neIt = edge2newEdge.insert( make_pair( edge1, zeroEdge )).first;
7081         e2neIt->second._normal += distWgt * newNormal;
7082         e2neIt->second._cosin   = newCos;
7083         e2neIt->second.SetMaxLen( 0.7 * minIntDist / edge1->_lenFactor );
7084         if ( iter > 0 && sgn1 * sgn2 < 0 && edge1->_cosin < 0 )
7085           e2neIt->second._normal += dir2;
7086
7087         e2neIt = edge2newEdge.insert( make_pair( edge2, zeroEdge )).first;
7088         e2neIt->second._normal += distWgt * newNormal;
7089         if ( Precision::IsInfinite( zeroEdge._maxLen ))
7090         {
7091           e2neIt->second._cosin  = edge2->_cosin;
7092           e2neIt->second.SetMaxLen( 1.3 * minIntDist / edge1->_lenFactor );
7093         }
7094         if ( iter > 0 && sgn1 * sgn2 < 0 && edge2->_cosin < 0 )
7095           e2neIt->second._normal += dir1;
7096       }
7097     }
7098
7099     if ( edge2newEdge.empty() )
7100       break; //return true;
7101
7102     dumpFunction(SMESH_Comment("updateNormals")<< data._index << "_" << stepNb << "_it" << iter);
7103
7104     // Update data of edges depending on a new _normal
7105
7106     data.UnmarkEdges();
7107     for ( e2neIt = edge2newEdge.begin(); e2neIt != edge2newEdge.end(); ++e2neIt )
7108     {
7109       _LayerEdge*    edge = e2neIt->first;
7110       _LayerEdge& newEdge = e2neIt->second;
7111       _EdgesOnShape*  eos = data.GetShapeEdges( edge );
7112       if ( edge->Is( _LayerEdge::BLOCKED && newEdge._maxLen > edge->_len ))
7113         continue;
7114
7115       // Check if a new _normal is OK:
7116       newEdge._normal.Normalize();
7117       if ( !isNewNormalOk( data, *edge, newEdge._normal ))
7118       {
7119         if ( newEdge._maxLen < edge->_len && iter > 0 ) // limit _maxLen
7120         {
7121           edge->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true  );
7122           edge->SetMaxLen( newEdge._maxLen );
7123           edge->SetNewLength( newEdge._maxLen, *eos, helper );
7124         }
7125         continue; // the new _normal is bad
7126       }
7127       // the new _normal is OK
7128
7129       // find shapes that need smoothing due to change of _normal
7130       if ( edge->_cosin   < theMinSmoothCosin &&
7131            newEdge._cosin > theMinSmoothCosin )
7132       {
7133         if ( eos->_sWOL.IsNull() )
7134         {
7135           SMDS_ElemIteratorPtr fIt = edge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
7136           while ( fIt->more() )
7137             shapesToSmooth.insert( data.GetShapeEdges( fIt->next()->getshapeId() ));
7138         }
7139         else // edge inflates along a FACE
7140         {
7141           TopoDS_Shape V = helper.GetSubShapeByNode( edge->_nodes[0], getMeshDS() );
7142           PShapeIteratorPtr eIt = helper.GetAncestors( V, *_mesh, TopAbs_EDGE, &eos->_sWOL );
7143           while ( const TopoDS_Shape* E = eIt->next() )
7144           {
7145             gp_Vec edgeDir = getEdgeDir( TopoDS::Edge( *E ), TopoDS::Vertex( V ));
7146             double   angle = edgeDir.Angle( newEdge._normal ); // [0,PI]
7147             if ( angle < M_PI / 2 )
7148               shapesToSmooth.insert( data.GetShapeEdges( *E ));
7149           }
7150         }
7151       }
7152
7153       double len = edge->_len;
7154       edge->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true  );
7155       edge->SetNormal( newEdge._normal );
7156       edge->SetCosin( newEdge._cosin );
7157       edge->SetNewLength( len, *eos, helper );
7158       edge->Set( _LayerEdge::MARKED );
7159       edge->Set( _LayerEdge::NORMAL_UPDATED );
7160       edgesNoAnaSmooth.insert( eos );
7161     }
7162
7163     // Update normals and other dependent data of not intersecting _LayerEdge's
7164     // neighboring the intersecting ones
7165
7166     for ( e2neIt = edge2newEdge.begin(); e2neIt != edge2newEdge.end(); ++e2neIt )
7167     {
7168       _LayerEdge*   edge1 = e2neIt->first;
7169       _EdgesOnShape* eos1 = data.GetShapeEdges( edge1 );
7170       if ( !edge1->Is( _LayerEdge::MARKED ))
7171         continue;
7172
7173       if ( edge1->IsOnEdge() )
7174       {
7175         const SMDS_MeshNode * n1 = edge1->_2neibors->srcNode(0);
7176         const SMDS_MeshNode * n2 = edge1->_2neibors->srcNode(1);
7177         edge1->SetDataByNeighbors( n1, n2, *eos1, helper );
7178       }
7179
7180       if ( !edge1->_2neibors || !eos1->_sWOL.IsNull() )
7181         continue;
7182       for ( int j = 0; j < 2; ++j ) // loop on 2 neighbors
7183       {
7184         _LayerEdge* neighbor = edge1->_2neibors->_edges[j];
7185         if ( neighbor->Is( _LayerEdge::MARKED ) /*edge2newEdge.count ( neighbor )*/)
7186           continue; // j-th neighbor is also intersected
7187         _LayerEdge* prevEdge = edge1;
7188         const int nbSteps = 10;
7189         for ( int step = nbSteps; step; --step ) // step from edge1 in j-th direction
7190         {
7191           if ( neighbor->Is( _LayerEdge::BLOCKED ) ||
7192                neighbor->Is( _LayerEdge::MARKED ))
7193             break;
7194           _EdgesOnShape* eos = data.GetShapeEdges( neighbor );
7195           if ( !eos ) continue;
7196           _LayerEdge* nextEdge = neighbor;
7197           if ( neighbor->_2neibors )
7198           {
7199             int iNext = 0;
7200             nextEdge = neighbor->_2neibors->_edges[iNext];
7201             if ( nextEdge == prevEdge )
7202               nextEdge = neighbor->_2neibors->_edges[ ++iNext ];
7203           }
7204           double r = double(step-1)/nbSteps/(iter+1);
7205           if ( !nextEdge->_2neibors )
7206             r = Min( r, 0.5 );
7207
7208           gp_XYZ newNorm = prevEdge->_normal * r + nextEdge->_normal * (1-r);
7209           newNorm.Normalize();
7210           if ( !isNewNormalOk( data, *neighbor, newNorm ))
7211             break;
7212
7213           double len = neighbor->_len;
7214           neighbor->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true  );
7215           neighbor->SetNormal( newNorm );
7216           neighbor->SetCosin( prevEdge->_cosin * r + nextEdge->_cosin * (1-r) );
7217           if ( neighbor->_2neibors )
7218             neighbor->SetDataByNeighbors( prevEdge->_nodes[0], nextEdge->_nodes[0], *eos, helper );
7219           neighbor->SetNewLength( len, *eos, helper );
7220           neighbor->Set( _LayerEdge::MARKED );
7221           neighbor->Set( _LayerEdge::NORMAL_UPDATED );
7222           edgesNoAnaSmooth.insert( eos );
7223
7224           if ( !neighbor->_2neibors )
7225             break; // neighbor is on VERTEX
7226
7227           // goto the next neighbor
7228           prevEdge = neighbor;
7229           neighbor = nextEdge;
7230         }
7231       }
7232     }
7233     dumpFunctionEnd();
7234   } // iterations
7235
7236   data.AddShapesToSmooth( shapesToSmooth, &edgesNoAnaSmooth );
7237
7238   return true;
7239 }
7240
7241 //================================================================================
7242 /*!
7243  * \brief Check if a new normal is OK
7244  */
7245 //================================================================================
7246
7247 bool _ViscousBuilder::isNewNormalOk( _SolidData&   data,
7248                                      _LayerEdge&   edge,
7249                                      const gp_XYZ& newNormal)
7250 {
7251   // check a min angle between the newNormal and surrounding faces
7252   vector<_Simplex> simplices;
7253   SMESH_TNodeXYZ n0( edge._nodes[0] ), n1, n2;
7254   _Simplex::GetSimplices( n0._node, simplices, data._ignoreFaceIds, &data );
7255   double newMinDot = 1, curMinDot = 1;
7256   for ( size_t i = 0; i < simplices.size(); ++i )
7257   {
7258     n1.Set( simplices[i]._nPrev );
7259     n2.Set( simplices[i]._nNext );
7260     gp_XYZ normFace = ( n1 - n0 ) ^ ( n2 - n0 );
7261     double normLen2 = normFace.SquareModulus();
7262     if ( normLen2 < std::numeric_limits<double>::min() )
7263       continue;
7264     normFace /= Sqrt( normLen2 );
7265     newMinDot = Min( newNormal    * normFace, newMinDot );
7266     curMinDot = Min( edge._normal * normFace, curMinDot );
7267   }
7268   bool ok = true;
7269   if ( newMinDot < 0.5 )
7270   {
7271     ok = ( newMinDot >= curMinDot * 0.9 );
7272     //return ( newMinDot >= ( curMinDot * ( 0.8 + 0.1 * edge.NbSteps() )));
7273     // double initMinDot2 = 1. - edge._cosin * edge._cosin;
7274     // return ( newMinDot * newMinDot ) >= ( 0.8 * initMinDot2 );
7275   }
7276
7277   return ok;
7278 }
7279
7280 //================================================================================
7281 /*!
7282  * \brief Modify normals of _LayerEdge's on FACE to reflex smoothing
7283  */
7284 //================================================================================
7285
7286 bool _ViscousBuilder::updateNormalsOfSmoothed( _SolidData&         data,
7287                                                SMESH_MesherHelper& helper,
7288                                                const int           nbSteps,
7289                                                const double        stepSize )
7290 {
7291   if ( data._nbShapesToSmooth == 0 || nbSteps == 0 )
7292     return true; // no shapes needing smoothing
7293
7294   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7295   {
7296     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
7297     if ( //!eos._toSmooth ||  _eosC1 have _toSmooth == false
7298          !eos._hyp.ToSmooth() ||
7299          eos.ShapeType() != TopAbs_FACE ||
7300          eos._edges.empty() )
7301       continue;
7302
7303     bool toSmooth = ( eos._edges[ 0 ]->NbSteps() >= nbSteps+1 );
7304     if ( !toSmooth ) continue;
7305
7306     for ( size_t i = 0; i < eos._edges.size(); ++i )
7307     {
7308       _LayerEdge* edge = eos._edges[i];
7309       if ( !edge->Is( _LayerEdge::SMOOTHED ))
7310         continue;
7311       if ( edge->Is( _LayerEdge::DIFFICULT ) && nbSteps != 1 )
7312         continue;
7313
7314       const gp_XYZ& pPrev = edge->PrevPos();
7315       const gp_XYZ& pLast = edge->_pos.back();
7316       gp_XYZ      stepVec = pLast - pPrev;
7317       double realStepSize = stepVec.Modulus();
7318       if ( realStepSize < numeric_limits<double>::min() )
7319         continue;
7320
7321       edge->_lenFactor = realStepSize / stepSize;
7322       edge->_normal    = stepVec / realStepSize;
7323       edge->Set( _LayerEdge::NORMAL_UPDATED );
7324     }
7325   }
7326
7327   return true;
7328 }
7329
7330 //================================================================================
7331 /*!
7332  * \brief Modify normals of _LayerEdge's on C1 VERTEXes
7333  */
7334 //================================================================================
7335
7336 void _ViscousBuilder::updateNormalsOfC1Vertices( _SolidData& data )
7337 {
7338   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7339   {
7340     _EdgesOnShape& eov = data._edgesOnShape[ iS ];
7341     if ( eov._eosC1.empty() ||
7342          eov.ShapeType() != TopAbs_VERTEX ||
7343          eov._edges.empty() )
7344       continue;
7345
7346     gp_XYZ newNorm   = eov._edges[0]->_normal;
7347     double curThick  = eov._edges[0]->_len * eov._edges[0]->_lenFactor;
7348     bool normChanged = false;
7349
7350     for ( size_t i = 0; i < eov._eosC1.size(); ++i )
7351     {
7352       _EdgesOnShape*   eoe = eov._eosC1[i];
7353       const TopoDS_Edge& e = TopoDS::Edge( eoe->_shape );
7354       const double    eLen = SMESH_Algo::EdgeLength( e );
7355       TopoDS_Shape    oppV = SMESH_MesherHelper::IthVertex( 0, e );
7356       if ( oppV.IsSame( eov._shape ))
7357         oppV = SMESH_MesherHelper::IthVertex( 1, e );
7358       _EdgesOnShape* eovOpp = data.GetShapeEdges( oppV );
7359       if ( !eovOpp || eovOpp->_edges.empty() ) continue;
7360       if ( eov._edges[0]->Is( _LayerEdge::BLOCKED )) continue;
7361
7362       double curThickOpp = eovOpp->_edges[0]->_len * eovOpp->_edges[0]->_lenFactor;
7363       if ( curThickOpp + curThick < eLen )
7364         continue;
7365
7366       double wgt = 2. * curThick / eLen;
7367       newNorm += wgt * eovOpp->_edges[0]->_normal;
7368       normChanged = true;
7369     }
7370     if ( normChanged )
7371     {
7372       eov._edges[0]->SetNormal( newNorm.Normalized() );
7373       eov._edges[0]->Set( _LayerEdge::NORMAL_UPDATED );
7374     }
7375   }
7376 }
7377
7378 //================================================================================
7379 /*!
7380  * \brief Modify normals of _LayerEdge's on _ConvexFace's
7381  */
7382 //================================================================================
7383
7384 bool _ViscousBuilder::updateNormalsOfConvexFaces( _SolidData&         data,
7385                                                   SMESH_MesherHelper& helper,
7386                                                   int                 stepNb )
7387 {
7388   SMESHDS_Mesh* meshDS = helper.GetMeshDS();
7389   bool isOK;
7390
7391   map< TGeomID, _ConvexFace >::iterator id2face = data._convexFaces.begin();
7392   for ( ; id2face != data._convexFaces.end(); ++id2face )
7393   {
7394     _ConvexFace & convFace = (*id2face).second;
7395     convFace._normalsFixedOnBorders = false; // to update at each inflation step
7396
7397     if ( convFace._normalsFixed )
7398       continue; // already fixed
7399     if ( convFace.CheckPrisms() )
7400       continue; // nothing to fix
7401
7402     convFace._normalsFixed = true;
7403
7404     BRepAdaptor_Surface surface ( convFace._face, false );
7405     BRepLProp_SLProps   surfProp( surface, 2, 1e-6 );
7406
7407     // check if the convex FACE is of spherical shape
7408
7409     Bnd_B3d centersBox; // bbox of centers of curvature of _LayerEdge's on VERTEXes
7410     Bnd_B3d nodesBox;
7411     gp_Pnt  center;
7412
7413     map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.begin();
7414     for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7415     {
7416       _EdgesOnShape& eos = *(id2eos->second);
7417       if ( eos.ShapeType() == TopAbs_VERTEX )
7418       {
7419         _LayerEdge* ledge = eos._edges[ 0 ];
7420         if ( convFace.GetCenterOfCurvature( ledge, surfProp, helper, center ))
7421           centersBox.Add( center );
7422       }
7423       for ( size_t i = 0; i < eos._edges.size(); ++i )
7424         nodesBox.Add( SMESH_TNodeXYZ( eos._edges[ i ]->_nodes[0] ));
7425     }
7426     if ( centersBox.IsVoid() )
7427     {
7428       debugMsg( "Error: centersBox.IsVoid()" );
7429       return false;
7430     }
7431     const bool isSpherical =
7432       ( centersBox.SquareExtent() < 1e-6 * nodesBox.SquareExtent() );
7433
7434     int nbEdges = helper.Count( convFace._face, TopAbs_EDGE, /*ignoreSame=*/false );
7435     vector < _CentralCurveOnEdge > centerCurves( nbEdges );
7436
7437     if ( isSpherical )
7438     {
7439       // set _LayerEdge::_normal as average of all normals
7440
7441       // WARNING: different density of nodes on EDGEs is not taken into account that
7442       // can lead to an improper new normal
7443
7444       gp_XYZ avgNormal( 0,0,0 );
7445       nbEdges = 0;
7446       id2eos = convFace._subIdToEOS.begin();
7447       for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7448       {
7449         _EdgesOnShape& eos = *(id2eos->second);
7450         // set data of _CentralCurveOnEdge
7451         if ( eos.ShapeType() == TopAbs_EDGE )
7452         {
7453           _CentralCurveOnEdge& ceCurve = centerCurves[ nbEdges++ ];
7454           ceCurve.SetShapes( TopoDS::Edge( eos._shape ), convFace, data, helper );
7455           if ( !eos._sWOL.IsNull() )
7456             ceCurve._adjFace.Nullify();
7457           else
7458             ceCurve._ledges.insert( ceCurve._ledges.end(),
7459                                     eos._edges.begin(), eos._edges.end());
7460         }
7461         // summarize normals
7462         for ( size_t i = 0; i < eos._edges.size(); ++i )
7463           avgNormal += eos._edges[ i ]->_normal;
7464       }
7465       double normSize = avgNormal.SquareModulus();
7466       if ( normSize < 1e-200 )
7467       {
7468         debugMsg( "updateNormalsOfConvexFaces(): zero avgNormal" );
7469         return false;
7470       }
7471       avgNormal /= Sqrt( normSize );
7472
7473       // compute new _LayerEdge::_cosin on EDGEs
7474       double avgCosin = 0;
7475       int     nbCosin = 0;
7476       gp_Vec inFaceDir;
7477       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
7478       {
7479         _CentralCurveOnEdge& ceCurve = centerCurves[ iE ];
7480         if ( ceCurve._adjFace.IsNull() )
7481           continue;
7482         for ( size_t iLE = 0; iLE < ceCurve._ledges.size(); ++iLE )
7483         {
7484           const SMDS_MeshNode* node = ceCurve._ledges[ iLE ]->_nodes[0];
7485           inFaceDir = getFaceDir( ceCurve._adjFace, ceCurve._edge, node, helper, isOK );
7486           if ( isOK )
7487           {
7488             double angle = inFaceDir.Angle( avgNormal ); // [0,PI]
7489             ceCurve._ledges[ iLE ]->_cosin = Cos( angle );
7490             avgCosin += ceCurve._ledges[ iLE ]->_cosin;
7491             nbCosin++;
7492           }
7493         }
7494       }
7495       if ( nbCosin > 0 )
7496         avgCosin /= nbCosin;
7497
7498       // set _LayerEdge::_normal = avgNormal
7499       id2eos = convFace._subIdToEOS.begin();
7500       for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7501       {
7502         _EdgesOnShape& eos = *(id2eos->second);
7503         if ( eos.ShapeType() != TopAbs_EDGE )
7504           for ( size_t i = 0; i < eos._edges.size(); ++i )
7505             eos._edges[ i ]->_cosin = avgCosin;
7506
7507         for ( size_t i = 0; i < eos._edges.size(); ++i )
7508         {
7509           eos._edges[ i ]->SetNormal( avgNormal );
7510           eos._edges[ i ]->Set( _LayerEdge::NORMAL_UPDATED );
7511         }
7512       }
7513     }
7514     else // if ( isSpherical )
7515     {
7516       // We suppose that centers of curvature at all points of the FACE
7517       // lie on some curve, let's call it "central curve". For all _LayerEdge's
7518       // having a common center of curvature we define the same new normal
7519       // as a sum of normals of _LayerEdge's on EDGEs among them.
7520
7521       // get all centers of curvature for each EDGE
7522
7523       helper.SetSubShape( convFace._face );
7524       _LayerEdge* vertexLEdges[2], **edgeLEdge, **edgeLEdgeEnd;
7525
7526       TopExp_Explorer edgeExp( convFace._face, TopAbs_EDGE );
7527       for ( int iE = 0; edgeExp.More(); edgeExp.Next(), ++iE )
7528       {
7529         const TopoDS_Edge& edge = TopoDS::Edge( edgeExp.Current() );
7530
7531         // set adjacent FACE
7532         centerCurves[ iE ].SetShapes( edge, convFace, data, helper );
7533
7534         // get _LayerEdge's of the EDGE
7535         TGeomID edgeID = meshDS->ShapeToIndex( edge );
7536         _EdgesOnShape* eos = data.GetShapeEdges( edgeID );
7537         if ( !eos || eos->_edges.empty() )
7538         {
7539           // no _LayerEdge's on EDGE, use _LayerEdge's on VERTEXes
7540           for ( int iV = 0; iV < 2; ++iV )
7541           {
7542             TopoDS_Vertex v = helper.IthVertex( iV, edge );
7543             TGeomID     vID = meshDS->ShapeToIndex( v );
7544             eos = data.GetShapeEdges( vID );
7545             vertexLEdges[ iV ] = eos->_edges[ 0 ];
7546           }
7547           edgeLEdge    = &vertexLEdges[0];
7548           edgeLEdgeEnd = edgeLEdge + 2;
7549
7550           centerCurves[ iE ]._adjFace.Nullify();
7551         }
7552         else
7553         {
7554           if ( ! eos->_toSmooth )
7555             data.SortOnEdge( edge, eos->_edges );
7556           edgeLEdge    = &eos->_edges[ 0 ];
7557           edgeLEdgeEnd = edgeLEdge + eos->_edges.size();
7558           vertexLEdges[0] = eos->_edges.front()->_2neibors->_edges[0];
7559           vertexLEdges[1] = eos->_edges.back() ->_2neibors->_edges[1];
7560
7561           if ( ! eos->_sWOL.IsNull() )
7562             centerCurves[ iE ]._adjFace.Nullify();
7563         }
7564
7565         // Get curvature centers
7566
7567         centersBox.Clear();
7568
7569         if ( edgeLEdge[0]->IsOnEdge() &&
7570              convFace.GetCenterOfCurvature( vertexLEdges[0], surfProp, helper, center ))
7571         { // 1st VERTEX
7572           centerCurves[ iE ].Append( center, vertexLEdges[0] );
7573           centersBox.Add( center );
7574         }
7575         for ( ; edgeLEdge < edgeLEdgeEnd; ++edgeLEdge )
7576           if ( convFace.GetCenterOfCurvature( *edgeLEdge, surfProp, helper, center ))
7577           { // EDGE or VERTEXes
7578             centerCurves[ iE ].Append( center, *edgeLEdge );
7579             centersBox.Add( center );
7580           }
7581         if ( edgeLEdge[-1]->IsOnEdge() &&
7582              convFace.GetCenterOfCurvature( vertexLEdges[1], surfProp, helper, center ))
7583         { // 2nd VERTEX
7584           centerCurves[ iE ].Append( center, vertexLEdges[1] );
7585           centersBox.Add( center );
7586         }
7587         centerCurves[ iE ]._isDegenerated =
7588           ( centersBox.IsVoid() || centersBox.SquareExtent() < 1e-6 * nodesBox.SquareExtent() );
7589
7590       } // loop on EDGES of convFace._face to set up data of centerCurves
7591
7592       // Compute new normals for _LayerEdge's on EDGEs
7593
7594       double avgCosin = 0;
7595       int     nbCosin = 0;
7596       gp_Vec inFaceDir;
7597       for ( size_t iE1 = 0; iE1 < centerCurves.size(); ++iE1 )
7598       {
7599         _CentralCurveOnEdge& ceCurve = centerCurves[ iE1 ];
7600         if ( ceCurve._isDegenerated )
7601           continue;
7602         const vector< gp_Pnt >& centers = ceCurve._curvaCenters;
7603         vector< gp_XYZ > &   newNormals = ceCurve._normals;
7604         for ( size_t iC1 = 0; iC1 < centers.size(); ++iC1 )
7605         {
7606           isOK = false;
7607           for ( size_t iE2 = 0; iE2 < centerCurves.size() && !isOK; ++iE2 )
7608           {
7609             if ( iE1 != iE2 )
7610               isOK = centerCurves[ iE2 ].FindNewNormal( centers[ iC1 ], newNormals[ iC1 ]);
7611           }
7612           if ( isOK && !ceCurve._adjFace.IsNull() )
7613           {
7614             // compute new _LayerEdge::_cosin
7615             const SMDS_MeshNode* node = ceCurve._ledges[ iC1 ]->_nodes[0];
7616             inFaceDir = getFaceDir( ceCurve._adjFace, ceCurve._edge, node, helper, isOK );
7617             if ( isOK )
7618             {
7619               double angle = inFaceDir.Angle( newNormals[ iC1 ] ); // [0,PI]
7620               ceCurve._ledges[ iC1 ]->_cosin = Cos( angle );
7621               avgCosin += ceCurve._ledges[ iC1 ]->_cosin;
7622               nbCosin++;
7623             }
7624           }
7625         }
7626       }
7627       // set new normals to _LayerEdge's of NOT degenerated central curves
7628       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
7629       {
7630         if ( centerCurves[ iE ]._isDegenerated )
7631           continue;
7632         for ( size_t iLE = 0; iLE < centerCurves[ iE ]._ledges.size(); ++iLE )
7633         {
7634           centerCurves[ iE ]._ledges[ iLE ]->SetNormal( centerCurves[ iE ]._normals[ iLE ]);
7635           centerCurves[ iE ]._ledges[ iLE ]->Set( _LayerEdge::NORMAL_UPDATED );
7636         }
7637       }
7638       // set new normals to _LayerEdge's of     degenerated central curves
7639       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
7640       {
7641         if ( !centerCurves[ iE ]._isDegenerated ||
7642              centerCurves[ iE ]._ledges.size() < 3 )
7643           continue;
7644         // new normal is an average of new normals at VERTEXes that
7645         // was computed on non-degenerated _CentralCurveOnEdge's
7646         gp_XYZ newNorm = ( centerCurves[ iE ]._ledges.front()->_normal +
7647                            centerCurves[ iE ]._ledges.back ()->_normal );
7648         double sz = newNorm.Modulus();
7649         if ( sz < 1e-200 )
7650           continue;
7651         newNorm /= sz;
7652         double newCosin = ( 0.5 * centerCurves[ iE ]._ledges.front()->_cosin +
7653                             0.5 * centerCurves[ iE ]._ledges.back ()->_cosin );
7654         for ( size_t iLE = 1, nb = centerCurves[ iE ]._ledges.size() - 1; iLE < nb; ++iLE )
7655         {
7656           centerCurves[ iE ]._ledges[ iLE ]->SetNormal( newNorm );
7657           centerCurves[ iE ]._ledges[ iLE ]->_cosin   = newCosin;
7658           centerCurves[ iE ]._ledges[ iLE ]->Set( _LayerEdge::NORMAL_UPDATED );
7659         }
7660       }
7661
7662       // Find new normals for _LayerEdge's based on FACE
7663
7664       if ( nbCosin > 0 )
7665         avgCosin /= nbCosin;
7666       const TGeomID faceID = meshDS->ShapeToIndex( convFace._face );
7667       map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.find( faceID );
7668       if ( id2eos != convFace._subIdToEOS.end() )
7669       {
7670         int iE = 0;
7671         gp_XYZ newNorm;
7672         _EdgesOnShape& eos = * ( id2eos->second );
7673         for ( size_t i = 0; i < eos._edges.size(); ++i )
7674         {
7675           _LayerEdge* ledge = eos._edges[ i ];
7676           if ( !convFace.GetCenterOfCurvature( ledge, surfProp, helper, center ))
7677             continue;
7678           for ( size_t i = 0; i < centerCurves.size(); ++i, ++iE )
7679           {
7680             iE = iE % centerCurves.size();
7681             if ( centerCurves[ iE ]._isDegenerated )
7682               continue;
7683             newNorm.SetCoord( 0,0,0 );
7684             if ( centerCurves[ iE ].FindNewNormal( center, newNorm ))
7685             {
7686               ledge->SetNormal( newNorm );
7687               ledge->_cosin  = avgCosin;
7688               ledge->Set( _LayerEdge::NORMAL_UPDATED );
7689               break;
7690             }
7691           }
7692         }
7693       }
7694
7695     } // not a quasi-spherical FACE
7696
7697     // Update _LayerEdge's data according to a new normal
7698
7699     dumpFunction(SMESH_Comment("updateNormalsOfConvexFaces")<<data._index
7700                  <<"_F"<<meshDS->ShapeToIndex( convFace._face ));
7701
7702     id2eos = convFace._subIdToEOS.begin();
7703     for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7704     {
7705       _EdgesOnShape& eos = * ( id2eos->second );
7706       for ( size_t i = 0; i < eos._edges.size(); ++i )
7707       {
7708         _LayerEdge* & ledge = eos._edges[ i ];
7709         double len = ledge->_len;
7710         ledge->InvalidateStep( stepNb + 1, eos, /*restoreLength=*/true );
7711         ledge->SetCosin( ledge->_cosin );
7712         ledge->SetNewLength( len, eos, helper );
7713       }
7714       if ( eos.ShapeType() != TopAbs_FACE )
7715         for ( size_t i = 0; i < eos._edges.size(); ++i )
7716         {
7717           _LayerEdge* ledge = eos._edges[ i ];
7718           for ( size_t iN = 0; iN < ledge->_neibors.size(); ++iN )
7719           {
7720             _LayerEdge* neibor = ledge->_neibors[iN];
7721             if ( neibor->_nodes[0]->GetPosition()->GetDim() == 2 )
7722             {
7723               neibor->Set( _LayerEdge::NEAR_BOUNDARY );
7724               neibor->Set( _LayerEdge::MOVED );
7725               neibor->SetSmooLen( neibor->_len );
7726             }
7727           }
7728         }
7729     } // loop on sub-shapes of convFace._face
7730
7731     // Find FACEs adjacent to convFace._face that got necessity to smooth
7732     // as a result of normals modification
7733
7734     set< _EdgesOnShape* > adjFacesToSmooth;
7735     for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
7736     {
7737       if ( centerCurves[ iE ]._adjFace.IsNull() ||
7738            centerCurves[ iE ]._adjFaceToSmooth )
7739         continue;
7740       for ( size_t iLE = 0; iLE < centerCurves[ iE ]._ledges.size(); ++iLE )
7741       {
7742         if ( centerCurves[ iE ]._ledges[ iLE ]->_cosin > theMinSmoothCosin )
7743         {
7744           adjFacesToSmooth.insert( data.GetShapeEdges( centerCurves[ iE ]._adjFace ));
7745           break;
7746         }
7747       }
7748     }
7749     data.AddShapesToSmooth( adjFacesToSmooth );
7750
7751     dumpFunctionEnd();
7752
7753
7754   } // loop on data._convexFaces
7755
7756   return true;
7757 }
7758
7759 //================================================================================
7760 /*!
7761  * \brief Return max curvature of a FACE
7762  */
7763 //================================================================================
7764
7765 double _ConvexFace::GetMaxCurvature( _SolidData&         data,
7766                                      _EdgesOnShape&      eof,
7767                                      BRepLProp_SLProps&  surfProp,
7768                                      SMESH_MesherHelper& helper)
7769 {
7770   double maxCurvature = 0;
7771
7772   TopoDS_Face F = TopoDS::Face( eof._shape );
7773
7774   const int           nbTestPnt = 5;
7775   const double        oriFactor = ( F.Orientation() == TopAbs_REVERSED ? +1. : -1. );
7776   SMESH_subMeshIteratorPtr smIt = eof._subMesh->getDependsOnIterator(/*includeSelf=*/true);
7777   while ( smIt->more() )
7778   {
7779     SMESH_subMesh* sm = smIt->next();
7780     const TGeomID subID = sm->GetId();
7781
7782     // find _LayerEdge's of a sub-shape
7783     _EdgesOnShape* eos;
7784     if (( eos = data.GetShapeEdges( subID )))
7785       this->_subIdToEOS.insert( make_pair( subID, eos ));
7786     else
7787       continue;
7788
7789     // check concavity and curvature and limit data._stepSize
7790     const double minCurvature =
7791       1. / ( eos->_hyp.GetTotalThickness() * ( 1 + theThickToIntersection ));
7792     size_t iStep = Max( 1, eos->_edges.size() / nbTestPnt );
7793     for ( size_t i = 0; i < eos->_edges.size(); i += iStep )
7794     {
7795       gp_XY uv = helper.GetNodeUV( F, eos->_edges[ i ]->_nodes[0] );
7796       surfProp.SetParameters( uv.X(), uv.Y() );
7797       if ( surfProp.IsCurvatureDefined() )
7798       {
7799         double curvature = Max( surfProp.MaxCurvature() * oriFactor,
7800                                 surfProp.MinCurvature() * oriFactor );
7801         maxCurvature = Max( maxCurvature, curvature );
7802
7803         if ( curvature > minCurvature )
7804           this->_isTooCurved = true;
7805       }
7806     }
7807   } // loop on sub-shapes of the FACE
7808
7809   return maxCurvature;
7810 }
7811
7812 //================================================================================
7813 /*!
7814  * \brief Finds a center of curvature of a surface at a _LayerEdge
7815  */
7816 //================================================================================
7817
7818 bool _ConvexFace::GetCenterOfCurvature( _LayerEdge*         ledge,
7819                                         BRepLProp_SLProps&  surfProp,
7820                                         SMESH_MesherHelper& helper,
7821                                         gp_Pnt &            center ) const
7822 {
7823   gp_XY uv = helper.GetNodeUV( _face, ledge->_nodes[0] );
7824   surfProp.SetParameters( uv.X(), uv.Y() );
7825   if ( !surfProp.IsCurvatureDefined() )
7826     return false;
7827
7828   const double oriFactor = ( _face.Orientation() == TopAbs_REVERSED ? +1. : -1. );
7829   double surfCurvatureMax = surfProp.MaxCurvature() * oriFactor;
7830   double surfCurvatureMin = surfProp.MinCurvature() * oriFactor;
7831   if ( surfCurvatureMin > surfCurvatureMax )
7832     center = surfProp.Value().Translated( surfProp.Normal().XYZ() / surfCurvatureMin * oriFactor );
7833   else
7834     center = surfProp.Value().Translated( surfProp.Normal().XYZ() / surfCurvatureMax * oriFactor );
7835
7836   return true;
7837 }
7838
7839 //================================================================================
7840 /*!
7841  * \brief Check that prisms are not distorted
7842  */
7843 //================================================================================
7844
7845 bool _ConvexFace::CheckPrisms() const
7846 {
7847   double vol = 0;
7848   for ( size_t i = 0; i < _simplexTestEdges.size(); ++i )
7849   {
7850     const _LayerEdge* edge = _simplexTestEdges[i];
7851     SMESH_TNodeXYZ tgtXYZ( edge->_nodes.back() );
7852     for ( size_t j = 0; j < edge->_simplices.size(); ++j )
7853       if ( !edge->_simplices[j].IsForward( edge->_nodes[0], tgtXYZ, vol ))
7854       {
7855         debugMsg( "Bad simplex of _simplexTestEdges ("
7856                   << " "<< edge->_nodes[0]->GetID()<< " "<< tgtXYZ._node->GetID()
7857                   << " "<< edge->_simplices[j]._nPrev->GetID()
7858                   << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
7859         return false;
7860       }
7861   }
7862   return true;
7863 }
7864
7865 //================================================================================
7866 /*!
7867  * \brief Try to compute a new normal by interpolating normals of _LayerEdge's
7868  *        stored in this _CentralCurveOnEdge.
7869  *  \param [in] center - curvature center of a point of another _CentralCurveOnEdge.
7870  *  \param [in,out] newNormal - current normal at this point, to be redefined
7871  *  \return bool - true if succeeded.
7872  */
7873 //================================================================================
7874
7875 bool _CentralCurveOnEdge::FindNewNormal( const gp_Pnt& center, gp_XYZ& newNormal )
7876 {
7877   if ( this->_isDegenerated )
7878     return false;
7879
7880   // find two centers the given one lies between
7881
7882   for ( size_t i = 0, nb = _curvaCenters.size()-1;  i < nb;  ++i )
7883   {
7884     double sl2 = 1.001 * _segLength2[ i ];
7885
7886     double d1 = center.SquareDistance( _curvaCenters[ i ]);
7887     if ( d1 > sl2 )
7888       continue;
7889     
7890     double d2 = center.SquareDistance( _curvaCenters[ i+1 ]);
7891     if ( d2 > sl2 || d2 + d1 < 1e-100 )
7892       continue;
7893
7894     d1 = Sqrt( d1 );
7895     d2 = Sqrt( d2 );
7896     double r = d1 / ( d1 + d2 );
7897     gp_XYZ norm = (( 1. - r ) * _ledges[ i   ]->_normal +
7898                    (      r ) * _ledges[ i+1 ]->_normal );
7899     norm.Normalize();
7900
7901     newNormal += norm;
7902     double sz = newNormal.Modulus();
7903     if ( sz < 1e-200 )
7904       break;
7905     newNormal /= sz;
7906     return true;
7907   }
7908   return false;
7909 }
7910
7911 //================================================================================
7912 /*!
7913  * \brief Set shape members
7914  */
7915 //================================================================================
7916
7917 void _CentralCurveOnEdge::SetShapes( const TopoDS_Edge&  edge,
7918                                      const _ConvexFace&  convFace,
7919                                      _SolidData&         data,
7920                                      SMESH_MesherHelper& helper)
7921 {
7922   _edge = edge;
7923
7924   PShapeIteratorPtr fIt = helper.GetAncestors( edge, *helper.GetMesh(), TopAbs_FACE );
7925   while ( const TopoDS_Shape* F = fIt->next())
7926     if ( !convFace._face.IsSame( *F ))
7927     {
7928       _adjFace = TopoDS::Face( *F );
7929       _adjFaceToSmooth = false;
7930       // _adjFace already in a smoothing queue ?
7931       if ( _EdgesOnShape* eos = data.GetShapeEdges( _adjFace ))
7932         _adjFaceToSmooth = eos->_toSmooth;
7933       break;
7934     }
7935 }
7936
7937 //================================================================================
7938 /*!
7939  * \brief Looks for intersection of it's last segment with faces
7940  *  \param distance - returns shortest distance from the last node to intersection
7941  */
7942 //================================================================================
7943
7944 bool _LayerEdge::FindIntersection( SMESH_ElementSearcher&   searcher,
7945                                    double &                 distance,
7946                                    const double&            epsilon,
7947                                    _EdgesOnShape&           eos,
7948                                    const SMDS_MeshElement** intFace)
7949 {
7950   vector< const SMDS_MeshElement* > suspectFaces;
7951   double segLen;
7952   gp_Ax1 lastSegment = LastSegment( segLen, eos );
7953   searcher.GetElementsNearLine( lastSegment, SMDSAbs_Face, suspectFaces );
7954
7955   bool segmentIntersected = false;
7956   distance = Precision::Infinite();
7957   int iFace = -1; // intersected face
7958   for ( size_t j = 0 ; j < suspectFaces.size() /*&& !segmentIntersected*/; ++j )
7959   {
7960     const SMDS_MeshElement* face = suspectFaces[j];
7961     if ( face->GetNodeIndex( _nodes.back() ) >= 0 ||
7962          face->GetNodeIndex( _nodes[0]     ) >= 0 )
7963       continue; // face sharing _LayerEdge node
7964     const int nbNodes = face->NbCornerNodes();
7965     bool intFound = false;
7966     double dist;
7967     SMDS_MeshElement::iterator nIt = face->begin_nodes();
7968     if ( nbNodes == 3 )
7969     {
7970       intFound = SegTriaInter( lastSegment, *nIt++, *nIt++, *nIt++, dist, epsilon );
7971     }
7972     else
7973     {
7974       const SMDS_MeshNode* tria[3];
7975       tria[0] = *nIt++;
7976       tria[1] = *nIt++;
7977       for ( int n2 = 2; n2 < nbNodes && !intFound; ++n2 )
7978       {
7979         tria[2] = *nIt++;
7980         intFound = SegTriaInter(lastSegment, tria[0], tria[1], tria[2], dist, epsilon );
7981         tria[1] = tria[2];
7982       }
7983     }
7984     if ( intFound )
7985     {
7986       if ( dist < segLen*(1.01) && dist > -(_len*_lenFactor-segLen) )
7987         segmentIntersected = true;
7988       if ( distance > dist )
7989         distance = dist, iFace = j;
7990     }
7991   }
7992   if ( intFace ) *intFace = ( iFace != -1 ) ? suspectFaces[iFace] : 0;
7993
7994   distance -= segLen;
7995
7996   if ( segmentIntersected )
7997   {
7998 #ifdef __myDEBUG
7999     SMDS_MeshElement::iterator nIt = suspectFaces[iFace]->begin_nodes();
8000     gp_XYZ intP( lastSegment.Location().XYZ() + lastSegment.Direction().XYZ() * ( distance+segLen ));
8001     cout << "nodes: tgt " << _nodes.back()->GetID() << " src " << _nodes[0]->GetID()
8002          << ", intersection with face ("
8003          << (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()
8004          << ") at point (" << intP.X() << ", " << intP.Y() << ", " << intP.Z()
8005          << ") distance = " << distance << endl;
8006 #endif
8007   }
8008
8009   return segmentIntersected;
8010 }
8011
8012 //================================================================================
8013 /*!
8014  * \brief Returns a point used to check orientation of _simplices
8015  */
8016 //================================================================================
8017
8018 gp_XYZ _LayerEdge::PrevCheckPos( _EdgesOnShape* eos ) const
8019 {
8020   size_t i = Is( NORMAL_UPDATED ) && IsOnFace() ? _pos.size()-2 : 0;
8021
8022   if ( !eos || eos->_sWOL.IsNull() )
8023     return _pos[ i ];
8024
8025   if ( eos->SWOLType() == TopAbs_EDGE )
8026   {
8027     return BRepAdaptor_Curve( TopoDS::Edge( eos->_sWOL )).Value( _pos[i].X() ).XYZ();
8028   }
8029   //else //  TopAbs_FACE
8030
8031   return BRepAdaptor_Surface( TopoDS::Face( eos->_sWOL )).Value(_pos[i].X(), _pos[i].Y() ).XYZ();
8032 }
8033
8034 //================================================================================
8035 /*!
8036  * \brief Returns size and direction of the last segment
8037  */
8038 //================================================================================
8039
8040 gp_Ax1 _LayerEdge::LastSegment(double& segLen, _EdgesOnShape& eos) const
8041 {
8042   // find two non-coincident positions
8043   gp_XYZ orig = _pos.back();
8044   gp_XYZ vec;
8045   int iPrev = _pos.size() - 2;
8046   //const double tol = ( _len > 0 ) ? 0.3*_len : 1e-100; // adjusted for IPAL52478 + PAL22576
8047   const double tol = ( _len > 0 ) ? ( 1e-6 * _len ) : 1e-100;
8048   while ( iPrev >= 0 )
8049   {
8050     vec = orig - _pos[iPrev];
8051     if ( vec.SquareModulus() > tol*tol )
8052       break;
8053     else
8054       iPrev--;
8055   }
8056
8057   // make gp_Ax1
8058   gp_Ax1 segDir;
8059   if ( iPrev < 0 )
8060   {
8061     segDir.SetLocation( SMESH_TNodeXYZ( _nodes[0] ));
8062     segDir.SetDirection( _normal );
8063     segLen = 0;
8064   }
8065   else
8066   {
8067     gp_Pnt pPrev = _pos[ iPrev ];
8068     if ( !eos._sWOL.IsNull() )
8069     {
8070       TopLoc_Location loc;
8071       if ( eos.SWOLType() == TopAbs_EDGE )
8072       {
8073         double f,l;
8074         Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( eos._sWOL ), loc, f,l);
8075         pPrev = curve->Value( pPrev.X() ).Transformed( loc );
8076       }
8077       else
8078       {
8079         Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face( eos._sWOL ), loc );
8080         pPrev = surface->Value( pPrev.X(), pPrev.Y() ).Transformed( loc );
8081       }
8082       vec = SMESH_TNodeXYZ( _nodes.back() ) - pPrev.XYZ();
8083     }
8084     segDir.SetLocation( pPrev );
8085     segDir.SetDirection( vec );
8086     segLen = vec.Modulus();
8087   }
8088
8089   return segDir;
8090 }
8091
8092 //================================================================================
8093 /*!
8094  * \brief Return the last (or \a which) position of the target node on a FACE. 
8095  *  \param [in] F - the FACE this _LayerEdge is inflated along
8096  *  \param [in] which - index of position
8097  *  \return gp_XY - result UV
8098  */
8099 //================================================================================
8100
8101 gp_XY _LayerEdge::LastUV( const TopoDS_Face& F, _EdgesOnShape& eos, int which ) const
8102 {
8103   if ( F.IsSame( eos._sWOL )) // F is my FACE
8104     return gp_XY( _pos.back().X(), _pos.back().Y() );
8105
8106   if ( eos.SWOLType() != TopAbs_EDGE ) // wrong call
8107     return gp_XY( 1e100, 1e100 );
8108
8109   // _sWOL is EDGE of F; _pos.back().X() is the last U on the EDGE
8110   double f, l, u = _pos[ which < 0 ? _pos.size()-1 : which ].X();
8111   Handle(Geom2d_Curve) C2d = BRep_Tool::CurveOnSurface( TopoDS::Edge(eos._sWOL), F, f,l);
8112   if ( !C2d.IsNull() && f <= u && u <= l )
8113     return C2d->Value( u ).XY();
8114
8115   return gp_XY( 1e100, 1e100 );
8116 }
8117
8118 //================================================================================
8119 /*!
8120  * \brief Test intersection of the last segment with a given triangle
8121  *   using Moller-Trumbore algorithm
8122  * Intersection is detected if distance to intersection is less than _LayerEdge._len
8123  */
8124 //================================================================================
8125
8126 bool _LayerEdge::SegTriaInter( const gp_Ax1& lastSegment,
8127                                const gp_XYZ& vert0,
8128                                const gp_XYZ& vert1,
8129                                const gp_XYZ& vert2,
8130                                double&       t,
8131                                const double& EPSILON) const
8132 {
8133   const gp_Pnt& orig = lastSegment.Location();
8134   const gp_Dir& dir  = lastSegment.Direction();
8135
8136   /* calculate distance from vert0 to ray origin */
8137   //gp_XYZ tvec = orig.XYZ() - vert0;
8138
8139   //if ( tvec * dir > EPSILON )
8140     // intersected face is at back side of the temporary face this _LayerEdge belongs to
8141     //return false;
8142
8143   gp_XYZ edge1 = vert1 - vert0;
8144   gp_XYZ edge2 = vert2 - vert0;
8145
8146   /* begin calculating determinant - also used to calculate U parameter */
8147   gp_XYZ pvec = dir.XYZ() ^ edge2;
8148
8149   /* if determinant is near zero, ray lies in plane of triangle */
8150   double det = edge1 * pvec;
8151
8152   const double ANGL_EPSILON = 1e-12;
8153   if ( det > -ANGL_EPSILON && det < ANGL_EPSILON )
8154     return false;
8155
8156   /* calculate distance from vert0 to ray origin */
8157   gp_XYZ tvec = orig.XYZ() - vert0;
8158
8159   /* calculate U parameter and test bounds */
8160   double u = ( tvec * pvec ) / det;
8161   //if (u < 0.0 || u > 1.0)
8162   if ( u < -EPSILON || u > 1.0 + EPSILON )
8163     return false;
8164
8165   /* prepare to test V parameter */
8166   gp_XYZ qvec = tvec ^ edge1;
8167
8168   /* calculate V parameter and test bounds */
8169   double v = (dir.XYZ() * qvec) / det;
8170   //if ( v < 0.0 || u + v > 1.0 )
8171   if ( v < -EPSILON || u + v > 1.0 + EPSILON )
8172     return false;
8173
8174   /* calculate t, ray intersects triangle */
8175   t = (edge2 * qvec) / det;
8176
8177   //return true;
8178   return t > 0.;
8179 }
8180
8181 //================================================================================
8182 /*!
8183  * \brief _LayerEdge, located at a concave VERTEX of a FACE, moves target nodes of
8184  *        neighbor _LayerEdge's by it's own inflation vector.
8185  *  \param [in] eov - EOS of the VERTEX
8186  *  \param [in] eos - EOS of the FACE
8187  *  \param [in] step - inflation step
8188  *  \param [in,out] badSmooEdges - tangled _LayerEdge's
8189  */
8190 //================================================================================
8191
8192 void _LayerEdge::MoveNearConcaVer( const _EdgesOnShape*    eov,
8193                                    const _EdgesOnShape*    eos,
8194                                    const int               step,
8195                                    vector< _LayerEdge* > & badSmooEdges )
8196 {
8197   // check if any of _neibors is in badSmooEdges
8198   if ( std::find_first_of( _neibors.begin(), _neibors.end(),
8199                            badSmooEdges.begin(), badSmooEdges.end() ) == _neibors.end() )
8200     return;
8201
8202   // get all edges to move
8203
8204   set< _LayerEdge* > edges;
8205
8206   // find a distance between _LayerEdge on VERTEX and its neighbors
8207   gp_XYZ  curPosV = SMESH_TNodeXYZ( _nodes.back() );
8208   double dist2 = 0;
8209   for ( size_t i = 0; i < _neibors.size(); ++i )
8210   {
8211     _LayerEdge* nEdge = _neibors[i];
8212     if ( nEdge->_nodes[0]->getshapeId() == eos->_shapeID )
8213     {
8214       edges.insert( nEdge );
8215       dist2 = Max( dist2, ( curPosV - nEdge->_pos.back() ).SquareModulus() );
8216     }
8217   }
8218   // add _LayerEdge's close to curPosV
8219   size_t nbE;
8220   do {
8221     nbE = edges.size();
8222     for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8223     {
8224       _LayerEdge* edgeF = *e;
8225       for ( size_t i = 0; i < edgeF->_neibors.size(); ++i )
8226       {
8227         _LayerEdge* nEdge = edgeF->_neibors[i];
8228         if ( nEdge->_nodes[0]->getshapeId() == eos->_shapeID &&
8229              dist2 > ( curPosV - nEdge->_pos.back() ).SquareModulus() )
8230           edges.insert( nEdge );
8231       }
8232     }
8233   }
8234   while ( nbE < edges.size() );
8235
8236   // move the target node of the got edges
8237
8238   gp_XYZ prevPosV = PrevPos();
8239   if ( eov->SWOLType() == TopAbs_EDGE )
8240   {
8241     BRepAdaptor_Curve curve ( TopoDS::Edge( eov->_sWOL ));
8242     prevPosV = curve.Value( prevPosV.X() ).XYZ();
8243   }
8244   else if ( eov->SWOLType() == TopAbs_FACE )
8245   {
8246     BRepAdaptor_Surface surface( TopoDS::Face( eov->_sWOL ));
8247     prevPosV = surface.Value( prevPosV.X(), prevPosV.Y() ).XYZ();
8248   }
8249
8250   SMDS_FacePosition* fPos;
8251   //double r = 1. - Min( 0.9, step / 10. );
8252   for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8253   {
8254     _LayerEdge*       edgeF = *e;
8255     const gp_XYZ     prevVF = edgeF->PrevPos() - prevPosV;
8256     const gp_XYZ    newPosF = curPosV + prevVF;
8257     SMDS_MeshNode* tgtNodeF = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
8258     tgtNodeF->setXYZ( newPosF.X(), newPosF.Y(), newPosF.Z() );
8259     edgeF->_pos.back() = newPosF;
8260     dumpMoveComm( tgtNodeF, "MoveNearConcaVer" ); // debug
8261
8262     // set _curvature to make edgeF updated by putOnOffsetSurface()
8263     if ( !edgeF->_curvature )
8264       if (( fPos = dynamic_cast<SMDS_FacePosition*>( edgeF->_nodes[0]->GetPosition() )))
8265       {
8266         edgeF->_curvature = new _Curvature;
8267         edgeF->_curvature->_r = 0;
8268         edgeF->_curvature->_k = 0;
8269         edgeF->_curvature->_h2lenRatio = 0;
8270         edgeF->_curvature->_uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
8271       }
8272   }
8273   // gp_XYZ inflationVec( SMESH_TNodeXYZ( _nodes.back() ) -
8274   //                      SMESH_TNodeXYZ( _nodes[0]    ));
8275   // for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8276   // {
8277   //   _LayerEdge*      edgeF = *e;
8278   //   gp_XYZ          newPos = SMESH_TNodeXYZ( edgeF->_nodes[0] ) + inflationVec;
8279   //   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
8280   //   tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8281   //   edgeF->_pos.back() = newPosF;
8282   //   dumpMoveComm( tgtNode, "MoveNearConcaVer" ); // debug
8283   // }
8284
8285   // smooth _LayerEdge's around moved nodes
8286   //size_t nbBadBefore = badSmooEdges.size();
8287   for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8288   {
8289     _LayerEdge* edgeF = *e;
8290     for ( size_t j = 0; j < edgeF->_neibors.size(); ++j )
8291       if ( edgeF->_neibors[j]->_nodes[0]->getshapeId() == eos->_shapeID )
8292         //&& !edges.count( edgeF->_neibors[j] ))
8293       {
8294         _LayerEdge* edgeFN = edgeF->_neibors[j];
8295         edgeFN->Unset( SMOOTHED );
8296         int nbBad = edgeFN->Smooth( step, /*isConcaFace=*/true, /*findBest=*/true );
8297         // if ( nbBad > 0 )
8298         // {
8299         //   gp_XYZ         newPos = SMESH_TNodeXYZ( edgeFN->_nodes[0] ) + inflationVec;
8300         //   const gp_XYZ& prevPos = edgeFN->_pos[ edgeFN->_pos.size()-2 ];
8301         //   int        nbBadAfter = edgeFN->_simplices.size();
8302         //   double vol;
8303         //   for ( size_t iS = 0; iS < edgeFN->_simplices.size(); ++iS )
8304         //   {
8305         //     nbBadAfter -= edgeFN->_simplices[iS].IsForward( &prevPos, &newPos, vol );
8306         //   }
8307         //   if ( nbBadAfter <= nbBad )
8308         //   {
8309         //     SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeFN->_nodes.back() );
8310         //     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8311         //     edgeF->_pos.back() = newPosF;
8312         //     dumpMoveComm( tgtNode, "MoveNearConcaVer 2" ); // debug
8313         //     nbBad = nbBadAfter;
8314         //   }
8315         // }
8316         if ( nbBad > 0 )
8317           badSmooEdges.push_back( edgeFN );
8318       }
8319   }
8320     // move a bit not smoothed around moved nodes
8321   //   for ( size_t i = nbBadBefore; i < badSmooEdges.size(); ++i )
8322   //   {
8323   //   _LayerEdge*      edgeF = badSmooEdges[i];
8324   //   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
8325   //   gp_XYZ         newPos1 = SMESH_TNodeXYZ( edgeF->_nodes[0] ) + inflationVec;
8326   //   gp_XYZ         newPos2 = 0.5 * ( newPos1 + SMESH_TNodeXYZ( tgtNode ));
8327   //   tgtNode->setXYZ( newPos2.X(), newPos2.Y(), newPos2.Z() );
8328   //   edgeF->_pos.back() = newPosF;
8329   //   dumpMoveComm( tgtNode, "MoveNearConcaVer 2" ); // debug
8330   // }
8331 }
8332
8333 //================================================================================
8334 /*!
8335  * \brief Perform smooth of _LayerEdge's based on EDGE's
8336  *  \retval bool - true if node has been moved
8337  */
8338 //================================================================================
8339
8340 bool _LayerEdge::SmoothOnEdge(Handle(ShapeAnalysis_Surface)& surface,
8341                               const TopoDS_Face&             F,
8342                               SMESH_MesherHelper&            helper)
8343 {
8344   ASSERT( IsOnEdge() );
8345
8346   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _nodes.back() );
8347   SMESH_TNodeXYZ oldPos( tgtNode );
8348   double dist01, distNewOld;
8349   
8350   SMESH_TNodeXYZ p0( _2neibors->tgtNode(0));
8351   SMESH_TNodeXYZ p1( _2neibors->tgtNode(1));
8352   dist01 = p0.Distance( _2neibors->tgtNode(1) );
8353
8354   gp_Pnt newPos = p0 * _2neibors->_wgt[0] + p1 * _2neibors->_wgt[1];
8355   double lenDelta = 0;
8356   if ( _curvature )
8357   {
8358     //lenDelta = _curvature->lenDelta( _len );
8359     lenDelta = _curvature->lenDeltaByDist( dist01 );
8360     newPos.ChangeCoord() += _normal * lenDelta;
8361   }
8362
8363   distNewOld = newPos.Distance( oldPos );
8364
8365   if ( F.IsNull() )
8366   {
8367     if ( _2neibors->_plnNorm )
8368     {
8369       // put newPos on the plane defined by source node and _plnNorm
8370       gp_XYZ new2src = SMESH_TNodeXYZ( _nodes[0] ) - newPos.XYZ();
8371       double new2srcProj = (*_2neibors->_plnNorm) * new2src;
8372       newPos.ChangeCoord() += (*_2neibors->_plnNorm) * new2srcProj;
8373     }
8374     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8375     _pos.back() = newPos.XYZ();
8376   }
8377   else
8378   {
8379     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8380     gp_XY uv( Precision::Infinite(), 0 );
8381     helper.CheckNodeUV( F, tgtNode, uv, 1e-10, /*force=*/true );
8382     _pos.back().SetCoord( uv.X(), uv.Y(), 0 );
8383
8384     newPos = surface->Value( uv );
8385     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8386   }
8387
8388   // commented for IPAL0052478
8389   // if ( _curvature && lenDelta < 0 )
8390   // {
8391   //   gp_Pnt prevPos( _pos[ _pos.size()-2 ]);
8392   //   _len -= prevPos.Distance( oldPos );
8393   //   _len += prevPos.Distance( newPos );
8394   // }
8395   bool moved = distNewOld > dist01/50;
8396   //if ( moved )
8397   dumpMove( tgtNode ); // debug
8398
8399   return moved;
8400 }
8401
8402 //================================================================================
8403 /*!
8404  * \brief Perform 3D smooth of nodes inflated from FACE. No check of validity
8405  */
8406 //================================================================================
8407
8408 void _LayerEdge::SmoothWoCheck()
8409 {
8410   if ( Is( DIFFICULT ))
8411     return;
8412
8413   bool moved = Is( SMOOTHED );
8414   for ( size_t i = 0; i < _neibors.size()  &&  !moved; ++i )
8415     moved = _neibors[i]->Is( SMOOTHED );
8416   if ( !moved )
8417     return;
8418
8419   gp_XYZ newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
8420
8421   SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
8422   n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
8423   _pos.back() = newPos;
8424
8425   dumpMoveComm( n, SMESH_Comment("No check - ") << _funNames[ smooFunID() ]);
8426 }
8427
8428 //================================================================================
8429 /*!
8430  * \brief Checks validity of _neibors on EDGEs and VERTEXes
8431  */
8432 //================================================================================
8433
8434 int _LayerEdge::CheckNeiborsOnBoundary( vector< _LayerEdge* >* badNeibors, bool * needSmooth )
8435 {
8436   if ( ! Is( NEAR_BOUNDARY ))
8437     return 0;
8438
8439   int nbBad = 0;
8440   double vol;
8441   for ( size_t iN = 0; iN < _neibors.size(); ++iN )
8442   {
8443     _LayerEdge* eN = _neibors[iN];
8444     if ( eN->_nodes[0]->getshapeId() == _nodes[0]->getshapeId() )
8445       continue;
8446     if ( needSmooth )
8447       *needSmooth |= ( eN->Is( _LayerEdge::BLOCKED ) ||
8448                        eN->Is( _LayerEdge::NORMAL_UPDATED ) ||
8449                        eN->_pos.size() != _pos.size() );
8450
8451     SMESH_TNodeXYZ curPosN ( eN->_nodes.back() );
8452     SMESH_TNodeXYZ prevPosN( eN->_nodes[0] );
8453     for ( size_t i = 0; i < eN->_simplices.size(); ++i )
8454       if ( eN->_nodes.size() > 1 &&
8455            eN->_simplices[i].Includes( _nodes.back() ) &&
8456            !eN->_simplices[i].IsForward( &prevPosN, &curPosN, vol ))
8457       {
8458         ++nbBad;
8459         if ( badNeibors )
8460         {
8461           badNeibors->push_back( eN );
8462           debugMsg("Bad boundary simplex ( "
8463                    << " "<< eN->_nodes[0]->GetID()
8464                    << " "<< eN->_nodes.back()->GetID()
8465                    << " "<< eN->_simplices[i]._nPrev->GetID()
8466                    << " "<< eN->_simplices[i]._nNext->GetID() << " )" );
8467         }
8468         else
8469         {
8470           break;
8471         }
8472       }
8473   }
8474   return nbBad;
8475 }
8476
8477 //================================================================================
8478 /*!
8479  * \brief Perform 'smart' 3D smooth of nodes inflated from FACE
8480  *  \retval int - nb of bad simplices around this _LayerEdge
8481  */
8482 //================================================================================
8483
8484 int _LayerEdge::Smooth(const int step, bool findBest, vector< _LayerEdge* >& toSmooth )
8485 {
8486   if ( !Is( MOVED ) || Is( SMOOTHED ) || Is( BLOCKED ))
8487     return 0; // shape of simplices not changed
8488   if ( _simplices.size() < 2 )
8489     return 0; // _LayerEdge inflated along EDGE or FACE
8490
8491   if ( Is( DIFFICULT )) // || Is( ON_CONCAVE_FACE )
8492     findBest = true;
8493
8494   const gp_XYZ& curPos  = _pos.back();
8495   const gp_XYZ& prevPos = _pos[0]; //PrevPos();
8496
8497   // quality metrics (orientation) of tetras around _tgtNode
8498   int nbOkBefore = 0;
8499   double vol, minVolBefore = 1e100;
8500   for ( size_t i = 0; i < _simplices.size(); ++i )
8501   {
8502     nbOkBefore += _simplices[i].IsForward( &prevPos, &curPos, vol );
8503     minVolBefore = Min( minVolBefore, vol );
8504   }
8505   int nbBad = _simplices.size() - nbOkBefore;
8506
8507   bool bndNeedSmooth = false;
8508   if ( nbBad == 0 )
8509     nbBad = CheckNeiborsOnBoundary( 0, & bndNeedSmooth );
8510   if ( nbBad > 0 )
8511     Set( DISTORTED );
8512
8513   // evaluate min angle
8514   if ( nbBad == 0 && !findBest && !bndNeedSmooth )
8515   {
8516     size_t nbGoodAngles = _simplices.size();
8517     double angle;
8518     for ( size_t i = 0; i < _simplices.size(); ++i )
8519     {
8520       if ( !_simplices[i].IsMinAngleOK( curPos, angle ) && angle > _minAngle )
8521         --nbGoodAngles;
8522     }
8523     if ( nbGoodAngles == _simplices.size() )
8524     {
8525       Unset( MOVED );
8526       return 0;
8527     }
8528   }
8529   if ( Is( ON_CONCAVE_FACE ))
8530     findBest = true;
8531
8532   if ( step % 2 == 0 )
8533     findBest = false;
8534
8535   if ( Is( ON_CONCAVE_FACE ) && !findBest ) // alternate FUN_CENTROIDAL and FUN_LAPLACIAN
8536   {
8537     if ( _smooFunction == _funs[ FUN_LAPLACIAN ] )
8538       _smooFunction = _funs[ FUN_CENTROIDAL ];
8539     else
8540       _smooFunction = _funs[ FUN_LAPLACIAN ];
8541   }
8542
8543   // compute new position for the last _pos using different _funs
8544   gp_XYZ newPos;
8545   bool moved = false;
8546   for ( int iFun = -1; iFun < theNbSmooFuns; ++iFun )
8547   {
8548     if ( iFun < 0 )
8549       newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
8550     else if ( _funs[ iFun ] == _smooFunction )
8551       continue; // _smooFunction again
8552     else if ( step > 1 )
8553       newPos = (this->*_funs[ iFun ])(); // try other smoothing fun
8554     else
8555       break; // let "easy" functions improve elements around distorted ones
8556
8557     if ( _curvature )
8558     {
8559       double delta  = _curvature->lenDelta( _len );
8560       if ( delta > 0 )
8561         newPos += _normal * delta;
8562       else
8563       {
8564         double segLen = _normal * ( newPos - prevPos );
8565         if ( segLen + delta > 0 )
8566           newPos += _normal * delta;
8567       }
8568       // double segLenChange = _normal * ( curPos - newPos );
8569       // newPos += 0.5 * _normal * segLenChange;
8570     }
8571
8572     int nbOkAfter = 0;
8573     double minVolAfter = 1e100;
8574     for ( size_t i = 0; i < _simplices.size(); ++i )
8575     {
8576       nbOkAfter += _simplices[i].IsForward( &prevPos, &newPos, vol );
8577       minVolAfter = Min( minVolAfter, vol );
8578     }
8579     // get worse?
8580     if ( nbOkAfter < nbOkBefore )
8581       continue;
8582
8583     if (( findBest ) &&
8584         ( nbOkAfter == nbOkBefore ) &&
8585         ( minVolAfter <= minVolBefore ))
8586       continue;
8587
8588     nbBad        = _simplices.size() - nbOkAfter;
8589     minVolBefore = minVolAfter;
8590     nbOkBefore   = nbOkAfter;
8591     moved        = true;
8592
8593     SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
8594     n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
8595     _pos.back() = newPos;
8596
8597     dumpMoveComm( n, SMESH_Comment( _funNames[ iFun < 0 ? smooFunID() : iFun ] )
8598                   << (nbBad ? " --BAD" : ""));
8599
8600     if ( iFun > -1 )
8601     {
8602       continue; // look for a better function
8603     }
8604
8605     if ( !findBest )
8606       break;
8607
8608   } // loop on smoothing functions
8609
8610   if ( moved ) // notify _neibors
8611   {
8612     Set( SMOOTHED );
8613     for ( size_t i = 0; i < _neibors.size(); ++i )
8614       if ( !_neibors[i]->Is( MOVED ))
8615       {
8616         _neibors[i]->Set( MOVED );
8617         toSmooth.push_back( _neibors[i] );
8618       }
8619   }
8620
8621   return nbBad;
8622 }
8623
8624 //================================================================================
8625 /*!
8626  * \brief Perform 'smart' 3D smooth of nodes inflated from FACE
8627  *  \retval int - nb of bad simplices around this _LayerEdge
8628  */
8629 //================================================================================
8630
8631 int _LayerEdge::Smooth(const int step, const bool isConcaveFace, bool findBest )
8632 {
8633   if ( !_smooFunction )
8634     return 0; // _LayerEdge inflated along EDGE or FACE
8635   if ( Is( BLOCKED ))
8636     return 0; // not inflated
8637
8638   const gp_XYZ& curPos  = _pos.back();
8639   const gp_XYZ& prevPos = _pos[0]; //PrevCheckPos();
8640
8641   // quality metrics (orientation) of tetras around _tgtNode
8642   int nbOkBefore = 0;
8643   double vol, minVolBefore = 1e100;
8644   for ( size_t i = 0; i < _simplices.size(); ++i )
8645   {
8646     nbOkBefore += _simplices[i].IsForward( &prevPos, &curPos, vol );
8647     minVolBefore = Min( minVolBefore, vol );
8648   }
8649   int nbBad = _simplices.size() - nbOkBefore;
8650
8651   if ( isConcaveFace ) // alternate FUN_CENTROIDAL and FUN_LAPLACIAN
8652   {
8653     if      ( _smooFunction == _funs[ FUN_CENTROIDAL ] && step % 2 )
8654       _smooFunction = _funs[ FUN_LAPLACIAN ];
8655     else if ( _smooFunction == _funs[ FUN_LAPLACIAN ] && !( step % 2 ))
8656       _smooFunction = _funs[ FUN_CENTROIDAL ];
8657   }
8658
8659   // compute new position for the last _pos using different _funs
8660   gp_XYZ newPos;
8661   for ( int iFun = -1; iFun < theNbSmooFuns; ++iFun )
8662   {
8663     if ( iFun < 0 )
8664       newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
8665     else if ( _funs[ iFun ] == _smooFunction )
8666       continue; // _smooFunction again
8667     else if ( step > 1 )
8668       newPos = (this->*_funs[ iFun ])(); // try other smoothing fun
8669     else
8670       break; // let "easy" functions improve elements around distorted ones
8671
8672     if ( _curvature )
8673     {
8674       double delta  = _curvature->lenDelta( _len );
8675       if ( delta > 0 )
8676         newPos += _normal * delta;
8677       else
8678       {
8679         double segLen = _normal * ( newPos - prevPos );
8680         if ( segLen + delta > 0 )
8681           newPos += _normal * delta;
8682       }
8683       // double segLenChange = _normal * ( curPos - newPos );
8684       // newPos += 0.5 * _normal * segLenChange;
8685     }
8686
8687     int nbOkAfter = 0;
8688     double minVolAfter = 1e100;
8689     for ( size_t i = 0; i < _simplices.size(); ++i )
8690     {
8691       nbOkAfter += _simplices[i].IsForward( &prevPos, &newPos, vol );
8692       minVolAfter = Min( minVolAfter, vol );
8693     }
8694     // get worse?
8695     if ( nbOkAfter < nbOkBefore )
8696       continue;
8697     if (( isConcaveFace || findBest ) &&
8698         ( nbOkAfter == nbOkBefore ) &&
8699         ( minVolAfter <= minVolBefore )
8700         )
8701       continue;
8702
8703     nbBad        = _simplices.size() - nbOkAfter;
8704     minVolBefore = minVolAfter;
8705     nbOkBefore   = nbOkAfter;
8706
8707     SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
8708     n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
8709     _pos.back() = newPos;
8710
8711     dumpMoveComm( n, SMESH_Comment( _funNames[ iFun < 0 ? smooFunID() : iFun ] )
8712                   << ( nbBad ? "--BAD" : ""));
8713
8714     // commented for IPAL0052478
8715     // _len -= prevPos.Distance(SMESH_TNodeXYZ( n ));
8716     // _len += prevPos.Distance(newPos);
8717
8718     if ( iFun > -1 ) // findBest || the chosen _fun makes worse
8719     {
8720       //_smooFunction = _funs[ iFun ];
8721       // cout << "# " << _funNames[ iFun ] << "\t N:" << _nodes.back()->GetID()
8722       // << "\t nbBad: " << _simplices.size() - nbOkAfter
8723       // << " minVol: " << minVolAfter
8724       // << " " << newPos.X() << " " << newPos.Y() << " " << newPos.Z()
8725       // << endl;
8726       continue; // look for a better function
8727     }
8728
8729     if ( !findBest )
8730       break;
8731
8732   } // loop on smoothing functions
8733
8734   return nbBad;
8735 }
8736
8737 //================================================================================
8738 /*!
8739  * \brief Chooses a smoothing technic giving a position most close to an initial one.
8740  *        For a correct result, _simplices must contain nodes lying on geometry.
8741  */
8742 //================================================================================
8743
8744 void _LayerEdge::ChooseSmooFunction( const set< TGeomID >& concaveVertices,
8745                                      const TNode2Edge&     n2eMap)
8746 {
8747   if ( _smooFunction ) return;
8748
8749   // use smoothNefPolygon() near concaveVertices
8750   if ( !concaveVertices.empty() )
8751   {
8752     _smooFunction = _funs[ FUN_CENTROIDAL ];
8753
8754     Set( ON_CONCAVE_FACE );
8755
8756     for ( size_t i = 0; i < _simplices.size(); ++i )
8757     {
8758       if ( concaveVertices.count( _simplices[i]._nPrev->getshapeId() ))
8759       {
8760         _smooFunction = _funs[ FUN_NEFPOLY ];
8761
8762         // set FUN_CENTROIDAL to neighbor edges
8763         for ( i = 0; i < _neibors.size(); ++i )
8764         {
8765           if ( _neibors[i]->_nodes[0]->GetPosition()->GetDim() == 2 )
8766           {
8767             _neibors[i]->_smooFunction = _funs[ FUN_CENTROIDAL ];
8768           }
8769         }
8770         return;
8771       }
8772     }
8773
8774     // // this coice is done only if ( !concaveVertices.empty() ) for Grids/smesh/bugs_19/X1
8775     // // where the nodes are smoothed too far along a sphere thus creating
8776     // // inverted _simplices
8777     // double dist[theNbSmooFuns];
8778     // //double coef[theNbSmooFuns] = { 1., 1.2, 1.4, 1.4 };
8779     // double coef[theNbSmooFuns] = { 1., 1., 1., 1. };
8780
8781     // double minDist = Precision::Infinite();
8782     // gp_Pnt p = SMESH_TNodeXYZ( _nodes[0] );
8783     // for ( int i = 0; i < FUN_NEFPOLY; ++i )
8784     // {
8785     //   gp_Pnt newP = (this->*_funs[i])();
8786     //   dist[i] = p.SquareDistance( newP );
8787     //   if ( dist[i]*coef[i] < minDist )
8788     //   {
8789     //     _smooFunction = _funs[i];
8790     //     minDist = dist[i]*coef[i];
8791     //   }
8792     // }
8793   }
8794   else
8795   {
8796     _smooFunction = _funs[ FUN_LAPLACIAN ];
8797   }
8798   // int minDim = 3;
8799   // for ( size_t i = 0; i < _simplices.size(); ++i )
8800   //   minDim = Min( minDim, _simplices[i]._nPrev->GetPosition()->GetDim() );
8801   // if ( minDim == 0 )
8802   //   _smooFunction = _funs[ FUN_CENTROIDAL ];
8803   // else if ( minDim == 1 )
8804   //   _smooFunction = _funs[ FUN_CENTROIDAL ];
8805
8806
8807   // int iMin;
8808   // for ( int i = 0; i < FUN_NB; ++i )
8809   // {
8810   //   //cout << dist[i] << " ";
8811   //   if ( _smooFunction == _funs[i] ) {
8812   //     iMin = i;
8813   //     //debugMsg( fNames[i] );
8814   //     break;
8815   //   }
8816   // }
8817   // cout << _funNames[ iMin ] << "\t N:" << _nodes.back()->GetID() << endl;
8818 }
8819
8820 //================================================================================
8821 /*!
8822  * \brief Returns a name of _SmooFunction
8823  */
8824 //================================================================================
8825
8826 int _LayerEdge::smooFunID( _LayerEdge::PSmooFun fun) const
8827 {
8828   if ( !fun )
8829     fun = _smooFunction;
8830   for ( int i = 0; i < theNbSmooFuns; ++i )
8831     if ( fun == _funs[i] )
8832       return i;
8833
8834   return theNbSmooFuns;
8835 }
8836
8837 //================================================================================
8838 /*!
8839  * \brief Computes a new node position using Laplacian smoothing
8840  */
8841 //================================================================================
8842
8843 gp_XYZ _LayerEdge::smoothLaplacian()
8844 {
8845   gp_XYZ newPos (0,0,0);
8846   for ( size_t i = 0; i < _simplices.size(); ++i )
8847     newPos += SMESH_TNodeXYZ( _simplices[i]._nPrev );
8848   newPos /= _simplices.size();
8849
8850   return newPos;
8851 }
8852
8853 //================================================================================
8854 /*!
8855  * \brief Computes a new node position using angular-based smoothing
8856  */
8857 //================================================================================
8858
8859 gp_XYZ _LayerEdge::smoothAngular()
8860 {
8861   vector< gp_Vec > edgeDir;  edgeDir. reserve( _simplices.size() + 1 );
8862   vector< double > edgeSize; edgeSize.reserve( _simplices.size()     );
8863   vector< gp_XYZ > points;   points.  reserve( _simplices.size() + 1 );
8864
8865   gp_XYZ pPrev = SMESH_TNodeXYZ( _simplices.back()._nPrev );
8866   gp_XYZ pN( 0,0,0 );
8867   for ( size_t i = 0; i < _simplices.size(); ++i )
8868   {
8869     gp_XYZ p = SMESH_TNodeXYZ( _simplices[i]._nPrev );
8870     edgeDir.push_back( p - pPrev );
8871     edgeSize.push_back( edgeDir.back().Magnitude() );
8872     if ( edgeSize.back() < numeric_limits<double>::min() )
8873     {
8874       edgeDir.pop_back();
8875       edgeSize.pop_back();
8876     }
8877     else
8878     {
8879       edgeDir.back() /= edgeSize.back();
8880       points.push_back( p );
8881       pN += p;
8882     }
8883     pPrev = p;
8884   }
8885   edgeDir.push_back ( edgeDir[0] );
8886   edgeSize.push_back( edgeSize[0] );
8887   pN /= points.size();
8888
8889   gp_XYZ newPos(0,0,0);
8890   double sumSize = 0;
8891   for ( size_t i = 0; i < points.size(); ++i )
8892   {
8893     gp_Vec toN    = pN - points[i];
8894     double toNLen = toN.Magnitude();
8895     if ( toNLen < numeric_limits<double>::min() )
8896     {
8897       newPos += pN;
8898       continue;
8899     }
8900     gp_Vec bisec    = edgeDir[i] + edgeDir[i+1];
8901     double bisecLen = bisec.SquareMagnitude();
8902     if ( bisecLen < numeric_limits<double>::min() )
8903     {
8904       gp_Vec norm = edgeDir[i] ^ toN;
8905       bisec = norm ^ edgeDir[i];
8906       bisecLen = bisec.SquareMagnitude();
8907     }
8908     bisecLen = Sqrt( bisecLen );
8909     bisec /= bisecLen;
8910
8911 #if 1
8912     gp_XYZ pNew = ( points[i] + bisec.XYZ() * toNLen ) * bisecLen;
8913     sumSize += bisecLen;
8914 #else
8915     gp_XYZ pNew = ( points[i] + bisec.XYZ() * toNLen ) * ( edgeSize[i] + edgeSize[i+1] );
8916     sumSize += ( edgeSize[i] + edgeSize[i+1] );
8917 #endif
8918     newPos += pNew;
8919   }
8920   newPos /= sumSize;
8921
8922   // project newPos to an average plane
8923
8924   gp_XYZ norm(0,0,0); // plane normal
8925   points.push_back( points[0] );
8926   for ( size_t i = 1; i < points.size(); ++i )
8927   {
8928     gp_XYZ vec1 = points[ i-1 ] - pN;
8929     gp_XYZ vec2 = points[ i   ] - pN;
8930     gp_XYZ cross = vec1 ^ vec2;
8931     try {
8932       cross.Normalize();
8933       if ( cross * norm < numeric_limits<double>::min() )
8934         norm += cross.Reversed();
8935       else
8936         norm += cross;
8937     }
8938     catch (Standard_Failure) { // if |cross| == 0.
8939     }
8940   }
8941   gp_XYZ vec = newPos - pN;
8942   double r   = ( norm * vec ) / norm.SquareModulus();  // param [0,1] on norm
8943   newPos     = newPos - r * norm;
8944
8945   return newPos;
8946 }
8947
8948 //================================================================================
8949 /*!
8950  * \brief Computes a new node position using weigthed node positions
8951  */
8952 //================================================================================
8953
8954 gp_XYZ _LayerEdge::smoothLengthWeighted()
8955 {
8956   vector< double > edgeSize; edgeSize.reserve( _simplices.size() + 1);
8957   vector< gp_XYZ > points;   points.  reserve( _simplices.size() );
8958
8959   gp_XYZ pPrev = SMESH_TNodeXYZ( _simplices.back()._nPrev );
8960   for ( size_t i = 0; i < _simplices.size(); ++i )
8961   {
8962     gp_XYZ p = SMESH_TNodeXYZ( _simplices[i]._nPrev );
8963     edgeSize.push_back( ( p - pPrev ).Modulus() );
8964     if ( edgeSize.back() < numeric_limits<double>::min() )
8965     {
8966       edgeSize.pop_back();
8967     }
8968     else
8969     {
8970       points.push_back( p );
8971     }
8972     pPrev = p;
8973   }
8974   edgeSize.push_back( edgeSize[0] );
8975
8976   gp_XYZ newPos(0,0,0);
8977   double sumSize = 0;
8978   for ( size_t i = 0; i < points.size(); ++i )
8979   {
8980     newPos += points[i] * ( edgeSize[i] + edgeSize[i+1] );
8981     sumSize += edgeSize[i] + edgeSize[i+1];
8982   }
8983   newPos /= sumSize;
8984   return newPos;
8985 }
8986
8987 //================================================================================
8988 /*!
8989  * \brief Computes a new node position using angular-based smoothing
8990  */
8991 //================================================================================
8992
8993 gp_XYZ _LayerEdge::smoothCentroidal()
8994 {
8995   gp_XYZ newPos(0,0,0);
8996   gp_XYZ pN = SMESH_TNodeXYZ( _nodes.back() );
8997   double sumSize = 0;
8998   for ( size_t i = 0; i < _simplices.size(); ++i )
8999   {
9000     gp_XYZ p1 = SMESH_TNodeXYZ( _simplices[i]._nPrev );
9001     gp_XYZ p2 = SMESH_TNodeXYZ( _simplices[i]._nNext );
9002     gp_XYZ gc = ( pN + p1 + p2 ) / 3.;
9003     double size = (( p1 - pN ) ^ ( p2 - pN )).Modulus();
9004
9005     sumSize += size;
9006     newPos += gc * size;
9007   }
9008   newPos /= sumSize;
9009
9010   return newPos;
9011 }
9012
9013 //================================================================================
9014 /*!
9015  * \brief Computes a new node position located inside a Nef polygon
9016  */
9017 //================================================================================
9018
9019 gp_XYZ _LayerEdge::smoothNefPolygon()
9020 #ifdef OLD_NEF_POLYGON
9021 {
9022   gp_XYZ newPos(0,0,0);
9023
9024   // get a plane to search a solution on
9025
9026   vector< gp_XYZ > vecs( _simplices.size() + 1 );
9027   size_t i;
9028   const double tol = numeric_limits<double>::min();
9029   gp_XYZ center(0,0,0);
9030   for ( i = 0; i < _simplices.size(); ++i )
9031   {
9032     vecs[i] = ( SMESH_TNodeXYZ( _simplices[i]._nNext ) -
9033                 SMESH_TNodeXYZ( _simplices[i]._nPrev ));
9034     center += SMESH_TNodeXYZ( _simplices[i]._nPrev );
9035   }
9036   vecs.back() = vecs[0];
9037   center /= _simplices.size();
9038
9039   gp_XYZ zAxis(0,0,0);
9040   for ( i = 0; i < _simplices.size(); ++i )
9041     zAxis += vecs[i] ^ vecs[i+1];
9042
9043   gp_XYZ yAxis;
9044   for ( i = 0; i < _simplices.size(); ++i )
9045   {
9046     yAxis = vecs[i];
9047     if ( yAxis.SquareModulus() > tol )
9048       break;
9049   }
9050   gp_XYZ xAxis = yAxis ^ zAxis;
9051   // SMESH_TNodeXYZ p0( _simplices[0]._nPrev );
9052   // const double tol = 1e-6 * ( p0.Distance( _simplices[1]._nPrev ) +
9053   //                             p0.Distance( _simplices[2]._nPrev ));
9054   // gp_XYZ center = smoothLaplacian();
9055   // gp_XYZ xAxis, yAxis, zAxis;
9056   // for ( i = 0; i < _simplices.size(); ++i )
9057   // {
9058   //   xAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9059   //   if ( xAxis.SquareModulus() > tol*tol )
9060   //     break;
9061   // }
9062   // for ( i = 1; i < _simplices.size(); ++i )
9063   // {
9064   //   yAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9065   //   zAxis = xAxis ^ yAxis;
9066   //   if ( zAxis.SquareModulus() > tol*tol )
9067   //     break;
9068   // }
9069   // if ( i == _simplices.size() ) return newPos;
9070
9071   yAxis = zAxis ^ xAxis;
9072   xAxis /= xAxis.Modulus();
9073   yAxis /= yAxis.Modulus();
9074
9075   // get half-planes of _simplices
9076
9077   vector< _halfPlane > halfPlns( _simplices.size() );
9078   int nbHP = 0;
9079   for ( size_t i = 0; i < _simplices.size(); ++i )
9080   {
9081     gp_XYZ OP1 = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9082     gp_XYZ OP2 = SMESH_TNodeXYZ( _simplices[i]._nNext ) - center;
9083     gp_XY  p1( OP1 * xAxis, OP1 * yAxis );
9084     gp_XY  p2( OP2 * xAxis, OP2 * yAxis );
9085     gp_XY  vec12 = p2 - p1;
9086     double dist12 = vec12.Modulus();
9087     if ( dist12 < tol )
9088       continue;
9089     vec12 /= dist12;
9090     halfPlns[ nbHP ]._pos = p1;
9091     halfPlns[ nbHP ]._dir = vec12;
9092     halfPlns[ nbHP ]._inNorm.SetCoord( -vec12.Y(), vec12.X() );
9093     ++nbHP;
9094   }
9095
9096   // intersect boundaries of half-planes, define state of intersection points
9097   // in relation to all half-planes and calculate internal point of a 2D polygon
9098
9099   double sumLen = 0;
9100   gp_XY newPos2D (0,0);
9101
9102   enum { UNDEF = -1, NOT_OUT, IS_OUT, NO_INT };
9103   typedef std::pair< gp_XY, int > TIntPntState; // coord and isOut state
9104   TIntPntState undefIPS( gp_XY(1e100,1e100), UNDEF );
9105
9106   vector< vector< TIntPntState > > allIntPnts( nbHP );
9107   for ( int iHP1 = 0; iHP1 < nbHP; ++iHP1 )
9108   {
9109     vector< TIntPntState > & intPnts1 = allIntPnts[ iHP1 ];
9110     if ( intPnts1.empty() ) intPnts1.resize( nbHP, undefIPS );
9111
9112     int iPrev = SMESH_MesherHelper::WrapIndex( iHP1 - 1, nbHP );
9113     int iNext = SMESH_MesherHelper::WrapIndex( iHP1 + 1, nbHP );
9114
9115     int nbNotOut = 0;
9116     const gp_XY* segEnds[2] = { 0, 0 }; // NOT_OUT points
9117
9118     for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9119     {
9120       if ( iHP1 == iHP2 ) continue;
9121
9122       TIntPntState & ips1 = intPnts1[ iHP2 ];
9123       if ( ips1.second == UNDEF )
9124       {
9125         // find an intersection point of boundaries of iHP1 and iHP2
9126
9127         if ( iHP2 == iPrev ) // intersection with neighbors is known
9128           ips1.first = halfPlns[ iHP1 ]._pos;
9129         else if ( iHP2 == iNext )
9130           ips1.first = halfPlns[ iHP2 ]._pos;
9131         else if ( !halfPlns[ iHP1 ].FindIntersection( halfPlns[ iHP2 ], ips1.first ))
9132           ips1.second = NO_INT;
9133
9134         // classify the found intersection point
9135         if ( ips1.second != NO_INT )
9136         {
9137           ips1.second = NOT_OUT;
9138           for ( int i = 0; i < nbHP && ips1.second == NOT_OUT; ++i )
9139             if ( i != iHP1 && i != iHP2 &&
9140                  halfPlns[ i ].IsOut( ips1.first, tol ))
9141               ips1.second = IS_OUT;
9142         }
9143         vector< TIntPntState > & intPnts2 = allIntPnts[ iHP2 ];
9144         if ( intPnts2.empty() ) intPnts2.resize( nbHP, undefIPS );
9145         TIntPntState & ips2 = intPnts2[ iHP1 ];
9146         ips2 = ips1;
9147       }
9148       if ( ips1.second == NOT_OUT )
9149       {
9150         ++nbNotOut;
9151         segEnds[ bool(segEnds[0]) ] = & ips1.first;
9152       }
9153     }
9154
9155     // find a NOT_OUT segment of boundary which is located between
9156     // two NOT_OUT int points
9157
9158     if ( nbNotOut < 2 )
9159       continue; // no such a segment
9160
9161     if ( nbNotOut > 2 )
9162     {
9163       // sort points along the boundary
9164       map< double, TIntPntState* > ipsByParam;
9165       for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9166       {
9167         TIntPntState & ips1 = intPnts1[ iHP2 ];
9168         if ( ips1.second != NO_INT )
9169         {
9170           gp_XY     op = ips1.first - halfPlns[ iHP1 ]._pos;
9171           double param = op * halfPlns[ iHP1 ]._dir;
9172           ipsByParam.insert( make_pair( param, & ips1 ));
9173         }
9174       }
9175       // look for two neighboring NOT_OUT points
9176       nbNotOut = 0;
9177       map< double, TIntPntState* >::iterator u2ips = ipsByParam.begin();
9178       for ( ; u2ips != ipsByParam.end(); ++u2ips )
9179       {
9180         TIntPntState & ips1 = *(u2ips->second);
9181         if ( ips1.second == NOT_OUT )
9182           segEnds[ bool( nbNotOut++ ) ] = & ips1.first;
9183         else if ( nbNotOut >= 2 )
9184           break;
9185         else
9186           nbNotOut = 0;
9187       }
9188     }
9189
9190     if ( nbNotOut >= 2 )
9191     {
9192       double len = ( *segEnds[0] - *segEnds[1] ).Modulus();
9193       sumLen += len;
9194
9195       newPos2D += 0.5 * len * ( *segEnds[0] + *segEnds[1] );
9196     }
9197   }
9198
9199   if ( sumLen > 0 )
9200   {
9201     newPos2D /= sumLen;
9202     newPos = center + xAxis * newPos2D.X() + yAxis * newPos2D.Y();
9203   }
9204   else
9205   {
9206     newPos = center;
9207   }
9208
9209   return newPos;
9210 }
9211 #else // OLD_NEF_POLYGON
9212 { ////////////////////////////////// NEW
9213   gp_XYZ newPos(0,0,0);
9214
9215   // get a plane to search a solution on
9216
9217   size_t i;
9218   gp_XYZ center(0,0,0);
9219   for ( i = 0; i < _simplices.size(); ++i )
9220     center += SMESH_TNodeXYZ( _simplices[i]._nPrev );
9221   center /= _simplices.size();
9222
9223   vector< gp_XYZ > vecs( _simplices.size() + 1 );
9224   for ( i = 0; i < _simplices.size(); ++i )
9225     vecs[i] = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9226   vecs.back() = vecs[0];
9227
9228   const double tol = numeric_limits<double>::min();
9229   gp_XYZ zAxis(0,0,0);
9230   for ( i = 0; i < _simplices.size(); ++i )
9231   {
9232     gp_XYZ cross = vecs[i] ^ vecs[i+1];
9233     try {
9234       cross.Normalize();
9235       if ( cross * zAxis < tol )
9236         zAxis += cross.Reversed();
9237       else
9238         zAxis += cross;
9239     }
9240     catch (Standard_Failure) { // if |cross| == 0.
9241     }
9242   }
9243
9244   gp_XYZ yAxis;
9245   for ( i = 0; i < _simplices.size(); ++i )
9246   {
9247     yAxis = vecs[i];
9248     if ( yAxis.SquareModulus() > tol )
9249       break;
9250   }
9251   gp_XYZ xAxis = yAxis ^ zAxis;
9252   // SMESH_TNodeXYZ p0( _simplices[0]._nPrev );
9253   // const double tol = 1e-6 * ( p0.Distance( _simplices[1]._nPrev ) +
9254   //                             p0.Distance( _simplices[2]._nPrev ));
9255   // gp_XYZ center = smoothLaplacian();
9256   // gp_XYZ xAxis, yAxis, zAxis;
9257   // for ( i = 0; i < _simplices.size(); ++i )
9258   // {
9259   //   xAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9260   //   if ( xAxis.SquareModulus() > tol*tol )
9261   //     break;
9262   // }
9263   // for ( i = 1; i < _simplices.size(); ++i )
9264   // {
9265   //   yAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9266   //   zAxis = xAxis ^ yAxis;
9267   //   if ( zAxis.SquareModulus() > tol*tol )
9268   //     break;
9269   // }
9270   // if ( i == _simplices.size() ) return newPos;
9271
9272   yAxis = zAxis ^ xAxis;
9273   xAxis /= xAxis.Modulus();
9274   yAxis /= yAxis.Modulus();
9275
9276   // get half-planes of _simplices
9277
9278   vector< _halfPlane > halfPlns( _simplices.size() );
9279   int nbHP = 0;
9280   for ( size_t i = 0; i < _simplices.size(); ++i )
9281   {
9282     const gp_XYZ& OP1 = vecs[ i   ];
9283     const gp_XYZ& OP2 = vecs[ i+1 ];
9284     gp_XY  p1( OP1 * xAxis, OP1 * yAxis );
9285     gp_XY  p2( OP2 * xAxis, OP2 * yAxis );
9286     gp_XY  vec12 = p2 - p1;
9287     double dist12 = vec12.Modulus();
9288     if ( dist12 < tol )
9289       continue;
9290     vec12 /= dist12;
9291     halfPlns[ nbHP ]._pos = p1;
9292     halfPlns[ nbHP ]._dir = vec12;
9293     halfPlns[ nbHP ]._inNorm.SetCoord( -vec12.Y(), vec12.X() );
9294     ++nbHP;
9295   }
9296
9297   // intersect boundaries of half-planes, define state of intersection points
9298   // in relation to all half-planes and calculate internal point of a 2D polygon
9299
9300   double sumLen = 0;
9301   gp_XY newPos2D (0,0);
9302
9303   enum { UNDEF = -1, NOT_OUT, IS_OUT, NO_INT };
9304   typedef std::pair< gp_XY, int > TIntPntState; // coord and isOut state
9305   TIntPntState undefIPS( gp_XY(1e100,1e100), UNDEF );
9306
9307   vector< vector< TIntPntState > > allIntPnts( nbHP );
9308   for ( int iHP1 = 0; iHP1 < nbHP; ++iHP1 )
9309   {
9310     vector< TIntPntState > & intPnts1 = allIntPnts[ iHP1 ];
9311     if ( intPnts1.empty() ) intPnts1.resize( nbHP, undefIPS );
9312
9313     int iPrev = SMESH_MesherHelper::WrapIndex( iHP1 - 1, nbHP );
9314     int iNext = SMESH_MesherHelper::WrapIndex( iHP1 + 1, nbHP );
9315
9316     int nbNotOut = 0;
9317     const gp_XY* segEnds[2] = { 0, 0 }; // NOT_OUT points
9318
9319     for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9320     {
9321       if ( iHP1 == iHP2 ) continue;
9322
9323       TIntPntState & ips1 = intPnts1[ iHP2 ];
9324       if ( ips1.second == UNDEF )
9325       {
9326         // find an intersection point of boundaries of iHP1 and iHP2
9327
9328         if ( iHP2 == iPrev ) // intersection with neighbors is known
9329           ips1.first = halfPlns[ iHP1 ]._pos;
9330         else if ( iHP2 == iNext )
9331           ips1.first = halfPlns[ iHP2 ]._pos;
9332         else if ( !halfPlns[ iHP1 ].FindIntersection( halfPlns[ iHP2 ], ips1.first ))
9333           ips1.second = NO_INT;
9334
9335         // classify the found intersection point
9336         if ( ips1.second != NO_INT )
9337         {
9338           ips1.second = NOT_OUT;
9339           for ( int i = 0; i < nbHP && ips1.second == NOT_OUT; ++i )
9340             if ( i != iHP1 && i != iHP2 &&
9341                  halfPlns[ i ].IsOut( ips1.first, tol ))
9342               ips1.second = IS_OUT;
9343         }
9344         vector< TIntPntState > & intPnts2 = allIntPnts[ iHP2 ];
9345         if ( intPnts2.empty() ) intPnts2.resize( nbHP, undefIPS );
9346         TIntPntState & ips2 = intPnts2[ iHP1 ];
9347         ips2 = ips1;
9348       }
9349       if ( ips1.second == NOT_OUT )
9350       {
9351         ++nbNotOut;
9352         segEnds[ bool(segEnds[0]) ] = & ips1.first;
9353       }
9354     }
9355
9356     // find a NOT_OUT segment of boundary which is located between
9357     // two NOT_OUT int points
9358
9359     if ( nbNotOut < 2 )
9360       continue; // no such a segment
9361
9362     if ( nbNotOut > 2 )
9363     {
9364       // sort points along the boundary
9365       map< double, TIntPntState* > ipsByParam;
9366       for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9367       {
9368         TIntPntState & ips1 = intPnts1[ iHP2 ];
9369         if ( ips1.second != NO_INT )
9370         {
9371           gp_XY     op = ips1.first - halfPlns[ iHP1 ]._pos;
9372           double param = op * halfPlns[ iHP1 ]._dir;
9373           ipsByParam.insert( make_pair( param, & ips1 ));
9374         }
9375       }
9376       // look for two neighboring NOT_OUT points
9377       nbNotOut = 0;
9378       map< double, TIntPntState* >::iterator u2ips = ipsByParam.begin();
9379       for ( ; u2ips != ipsByParam.end(); ++u2ips )
9380       {
9381         TIntPntState & ips1 = *(u2ips->second);
9382         if ( ips1.second == NOT_OUT )
9383           segEnds[ bool( nbNotOut++ ) ] = & ips1.first;
9384         else if ( nbNotOut >= 2 )
9385           break;
9386         else
9387           nbNotOut = 0;
9388       }
9389     }
9390
9391     if ( nbNotOut >= 2 )
9392     {
9393       double len = ( *segEnds[0] - *segEnds[1] ).Modulus();
9394       sumLen += len;
9395
9396       newPos2D += 0.5 * len * ( *segEnds[0] + *segEnds[1] );
9397     }
9398   }
9399
9400   if ( sumLen > 0 )
9401   {
9402     newPos2D /= sumLen;
9403     newPos = center + xAxis * newPos2D.X() + yAxis * newPos2D.Y();
9404   }
9405   else
9406   {
9407     newPos = center;
9408   }
9409
9410   return newPos;
9411 }
9412 #endif // OLD_NEF_POLYGON
9413
9414 //================================================================================
9415 /*!
9416  * \brief Add a new segment to _LayerEdge during inflation
9417  */
9418 //================================================================================
9419
9420 void _LayerEdge::SetNewLength( double len, _EdgesOnShape& eos, SMESH_MesherHelper& helper )
9421 {
9422   if ( Is( BLOCKED ))
9423     return;
9424
9425   if ( len > _maxLen )
9426   {
9427     len = _maxLen;
9428     Block( eos.GetData() );
9429   }
9430   const double lenDelta = len - _len;
9431   if ( lenDelta < len * 1e-3  )
9432   {
9433     Block( eos.GetData() );
9434     return;
9435   }
9436
9437   SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() );
9438   gp_XYZ oldXYZ = SMESH_TNodeXYZ( n );
9439   gp_XYZ newXYZ;
9440   if ( eos._hyp.IsOffsetMethod() )
9441   {
9442     newXYZ = oldXYZ;
9443     gp_Vec faceNorm;
9444     SMDS_ElemIteratorPtr faceIt = _nodes[0]->GetInverseElementIterator( SMDSAbs_Face );
9445     while ( faceIt->more() )
9446     {
9447       const SMDS_MeshElement* face = faceIt->next();
9448       if ( !eos.GetNormal( face, faceNorm ))
9449         continue;
9450
9451       // translate plane of a face
9452       gp_XYZ baryCenter = oldXYZ + faceNorm.XYZ() * lenDelta;
9453
9454       // find point of intersection of the face plane located at baryCenter
9455       // and _normal located at newXYZ
9456       double d   = -( faceNorm.XYZ() * baryCenter ); // d of plane equation ax+by+cz+d=0
9457       double dot =  ( faceNorm.XYZ() * _normal );
9458       if ( dot < std::numeric_limits<double>::min() )
9459         dot = lenDelta * 1e-3;
9460       double step = -( faceNorm.XYZ() * newXYZ + d ) / dot;
9461       newXYZ += step * _normal;
9462     }
9463     _lenFactor = _normal * ( newXYZ - oldXYZ ) / lenDelta; // _lenFactor is used in InvalidateStep()
9464   }
9465   else
9466   {
9467     newXYZ = oldXYZ + _normal * lenDelta * _lenFactor;
9468   }
9469
9470   n->setXYZ( newXYZ.X(), newXYZ.Y(), newXYZ.Z() );
9471   _pos.push_back( newXYZ );
9472
9473   if ( !eos._sWOL.IsNull() )
9474   {
9475     double distXYZ[4];
9476     bool uvOK = false;
9477     if ( eos.SWOLType() == TopAbs_EDGE )
9478     {
9479       double u = Precision::Infinite(); // to force projection w/o distance check
9480       uvOK = helper.CheckNodeU( TopoDS::Edge( eos._sWOL ), n, u,
9481                                 /*tol=*/2*lenDelta, /*force=*/true, distXYZ );
9482       _pos.back().SetCoord( u, 0, 0 );
9483       if ( _nodes.size() > 1 && uvOK )
9484       {
9485         SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( n->GetPosition() );
9486         pos->SetUParameter( u );
9487       }
9488     }
9489     else //  TopAbs_FACE
9490     {
9491       gp_XY uv( Precision::Infinite(), 0 );
9492       uvOK = helper.CheckNodeUV( TopoDS::Face( eos._sWOL ), n, uv,
9493                                  /*tol=*/2*lenDelta, /*force=*/true, distXYZ );
9494       _pos.back().SetCoord( uv.X(), uv.Y(), 0 );
9495       if ( _nodes.size() > 1 && uvOK )
9496       {
9497         SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( n->GetPosition() );
9498         pos->SetUParameter( uv.X() );
9499         pos->SetVParameter( uv.Y() );
9500       }
9501     }
9502     if ( uvOK )
9503     {
9504       n->setXYZ( distXYZ[1], distXYZ[2], distXYZ[3]);
9505     }
9506     else
9507     {
9508       n->setXYZ( oldXYZ.X(), oldXYZ.Y(), oldXYZ.Z() );
9509       _pos.pop_back();
9510       Block( eos.GetData() );
9511       return;
9512     }
9513   }
9514
9515   _len = len;
9516
9517   // notify _neibors
9518   if ( eos.ShapeType() != TopAbs_FACE )
9519   {
9520     for ( size_t i = 0; i < _neibors.size(); ++i )
9521       //if (  _len > _neibors[i]->GetSmooLen() )
9522         _neibors[i]->Set( MOVED );
9523
9524     Set( MOVED );
9525   }
9526   dumpMove( n ); //debug
9527 }
9528
9529 //================================================================================
9530 /*!
9531  * \brief Set BLOCKED flag and propagate limited _maxLen to _neibors
9532  */
9533 //================================================================================
9534
9535 void _LayerEdge::Block( _SolidData& data )
9536 {
9537   //if ( Is( BLOCKED )) return;
9538   Set( BLOCKED );
9539
9540   SMESH_Comment msg( "#BLOCK shape=");
9541   msg << data.GetShapeEdges( this )->_shapeID
9542       << ", nodes " << _nodes[0]->GetID() << ", " << _nodes.back()->GetID();
9543   dumpCmd( msg + " -- BEGIN");
9544
9545   SetMaxLen( _len );
9546   std::queue<_LayerEdge*> queue;
9547   queue.push( this );
9548
9549   gp_Pnt pSrc, pTgt, pSrcN, pTgtN;
9550   while ( !queue.empty() )
9551   {
9552     _LayerEdge* edge = queue.front(); queue.pop();
9553     pSrc = SMESH_TNodeXYZ( edge->_nodes[0] );
9554     pTgt = SMESH_TNodeXYZ( edge->_nodes.back() );
9555     for ( size_t iN = 0; iN < edge->_neibors.size(); ++iN )
9556     {
9557       _LayerEdge* neibor = edge->_neibors[iN];
9558       if ( neibor->_maxLen < edge->_maxLen * 1.01 )
9559         continue;
9560       pSrcN = SMESH_TNodeXYZ( neibor->_nodes[0] );
9561       pTgtN = SMESH_TNodeXYZ( neibor->_nodes.back() );
9562       double minDist = pSrc.SquareDistance( pSrcN );
9563       minDist   = Min( pTgt.SquareDistance( pTgtN ), minDist );
9564       minDist   = Min( pSrc.SquareDistance( pTgtN ), minDist );
9565       minDist   = Min( pTgt.SquareDistance( pSrcN ), minDist );
9566       double newMaxLen = edge->_maxLen + 0.5 * Sqrt( minDist );
9567       //if ( edge->_nodes[0]->getshapeId() == neibor->_nodes[0]->getshapeId() ) viscous_layers_00/A3
9568       {
9569         //newMaxLen *= edge->_lenFactor / neibor->_lenFactor;
9570         // newMaxLen *= Min( edge->_lenFactor / neibor->_lenFactor,
9571         //                   neibor->_lenFactor / edge->_lenFactor );
9572       }
9573       if ( neibor->_maxLen > newMaxLen )
9574       {
9575         neibor->SetMaxLen( newMaxLen );
9576         if ( neibor->_maxLen < neibor->_len )
9577         {
9578           _EdgesOnShape* eos = data.GetShapeEdges( neibor );
9579           int       lastStep = neibor->Is( BLOCKED ) ? 1 : 0;
9580           while ( neibor->_len > neibor->_maxLen &&
9581                   neibor->NbSteps() > lastStep )
9582             neibor->InvalidateStep( neibor->NbSteps(), *eos, /*restoreLength=*/true );
9583           neibor->SetNewLength( neibor->_maxLen, *eos, data.GetHelper() );
9584           //neibor->Block( data );
9585         }
9586         queue.push( neibor );
9587       }
9588     }
9589   }
9590   dumpCmd( msg + " -- END");
9591 }
9592
9593 //================================================================================
9594 /*!
9595  * \brief Remove last inflation step
9596  */
9597 //================================================================================
9598
9599 void _LayerEdge::InvalidateStep( size_t curStep, const _EdgesOnShape& eos, bool restoreLength )
9600 {
9601   if ( _pos.size() > curStep && _nodes.size() > 1 )
9602   {
9603     _pos.resize( curStep );
9604
9605     gp_Pnt      nXYZ = _pos.back();
9606     SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() );
9607     SMESH_TNodeXYZ curXYZ( n );
9608     if ( !eos._sWOL.IsNull() )
9609     {
9610       TopLoc_Location loc;
9611       if ( eos.SWOLType() == TopAbs_EDGE )
9612       {
9613         SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( n->GetPosition() );
9614         pos->SetUParameter( nXYZ.X() );
9615         double f,l;
9616         Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( eos._sWOL ), loc, f,l);
9617         nXYZ = curve->Value( nXYZ.X() ).Transformed( loc );
9618       }
9619       else
9620       {
9621         SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( n->GetPosition() );
9622         pos->SetUParameter( nXYZ.X() );
9623         pos->SetVParameter( nXYZ.Y() );
9624         Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face(eos._sWOL), loc );
9625         nXYZ = surface->Value( nXYZ.X(), nXYZ.Y() ).Transformed( loc );
9626       }
9627     }
9628     n->setXYZ( nXYZ.X(), nXYZ.Y(), nXYZ.Z() );
9629     dumpMove( n );
9630
9631     if ( restoreLength )
9632     {
9633       if ( NbSteps() == 0 )
9634         _len = 0.;
9635       else if ( IsOnFace() && Is( MOVED ))
9636         _len = ( nXYZ.XYZ() - SMESH_NodeXYZ( _nodes[0] )) * _normal;
9637       else
9638         _len -= ( nXYZ.XYZ() - curXYZ ).Modulus() / _lenFactor;
9639     }
9640   }
9641   return;
9642 }
9643
9644 //================================================================================
9645 /*!
9646  * \brief Return index of a _pos distant from _normal
9647  */
9648 //================================================================================
9649
9650 int _LayerEdge::GetSmoothedPos( const double tol )
9651 {
9652   int iSmoothed = 0;
9653   for ( size_t i = 1; i < _pos.size() && !iSmoothed; ++i )
9654   {
9655     double normDist = ( _pos[i] - _pos[0] ).Crossed( _normal ).SquareModulus();
9656     if ( normDist > tol * tol )
9657       iSmoothed = i;
9658   }
9659   return iSmoothed;
9660 }
9661
9662 //================================================================================
9663 /*!
9664  * \brief Smooth a path formed by _pos of a _LayerEdge smoothed on FACE
9665  */
9666 //================================================================================
9667
9668 void _LayerEdge::SmoothPos( const vector< double >& segLen, const double tol )
9669 {
9670   if ( /*Is( NORMAL_UPDATED ) ||*/ _pos.size() <= 2 )
9671     return;
9672
9673   // find the 1st smoothed _pos
9674   int iSmoothed = GetSmoothedPos( tol );
9675   if ( !iSmoothed ) return;
9676
9677   gp_XYZ normal = _normal;
9678   if ( Is( NORMAL_UPDATED ))
9679   {
9680     double minDot = 1;
9681     for ( size_t i = 0; i < _neibors.size(); ++i )
9682     {
9683       if ( _neibors[i]->IsOnFace() )
9684       {
9685         double dot = _normal * _neibors[i]->_normal;
9686         if ( dot < minDot )
9687         {
9688           normal = _neibors[i]->_normal;
9689           minDot = dot;
9690         }
9691       }
9692     }
9693     if ( minDot == 1. )
9694       for ( size_t i = 1; i < _pos.size(); ++i )
9695       {
9696         normal = _pos[i] - _pos[0];
9697         double size = normal.Modulus();
9698         if ( size > RealSmall() )
9699         {
9700           normal /= size;
9701           break;
9702         }
9703       }
9704   }
9705   const double r = 0.2;
9706   for ( int iter = 0; iter < 50; ++iter )
9707   {
9708     double minDot = 1;
9709     for ( size_t i = Max( 1, iSmoothed-1-iter ); i < _pos.size()-1; ++i )
9710     {
9711       gp_XYZ midPos = 0.5 * ( _pos[i-1] + _pos[i+1] );
9712       gp_XYZ newPos = ( 1-r ) * midPos + r * _pos[i];
9713       _pos[i] = newPos;
9714       double midLen = 0.5 * ( segLen[i-1] + segLen[i+1] );
9715       double newLen = ( 1-r ) * midLen + r * segLen[i];
9716       const_cast< double& >( segLen[i] ) = newLen;
9717       // check angle between normal and (_pos[i+1], _pos[i] )
9718       gp_XYZ posDir = _pos[i+1] - _pos[i];
9719       double size   = posDir.SquareModulus();
9720       if ( size > RealSmall() )
9721         minDot = Min( minDot, ( normal * posDir ) * ( normal * posDir ) / size );
9722     }
9723     if ( minDot > 0.5 * 0.5 )
9724       break;
9725   }
9726   return;
9727 }
9728
9729 //================================================================================
9730 /*!
9731  * \brief Print flags
9732  */
9733 //================================================================================
9734
9735 std::string _LayerEdge::DumpFlags() const
9736 {
9737   SMESH_Comment dump;
9738   for ( int flag = 1; flag < 0x1000000; flag *= 2 )
9739     if ( _flags & flag )
9740     {
9741       EFlags f = (EFlags) flag;
9742       switch ( f ) {
9743       case TO_SMOOTH:       dump << "TO_SMOOTH";       break;
9744       case MOVED:           dump << "MOVED";           break;
9745       case SMOOTHED:        dump << "SMOOTHED";        break;
9746       case DIFFICULT:       dump << "DIFFICULT";       break;
9747       case ON_CONCAVE_FACE: dump << "ON_CONCAVE_FACE"; break;
9748       case BLOCKED:         dump << "BLOCKED";         break;
9749       case INTERSECTED:     dump << "INTERSECTED";     break;
9750       case NORMAL_UPDATED:  dump << "NORMAL_UPDATED";  break;
9751       case UPD_NORMAL_CONV: dump << "UPD_NORMAL_CONV"; break;
9752       case MARKED:          dump << "MARKED";          break;
9753       case MULTI_NORMAL:    dump << "MULTI_NORMAL";    break;
9754       case NEAR_BOUNDARY:   dump << "NEAR_BOUNDARY";   break;
9755       case SMOOTHED_C1:     dump << "SMOOTHED_C1";     break;
9756       case DISTORTED:       dump << "DISTORTED";       break;
9757       case RISKY_SWOL:      dump << "RISKY_SWOL";      break;
9758       case SHRUNK:          dump << "SHRUNK";          break;
9759       case UNUSED_FLAG:     dump << "UNUSED_FLAG";     break;
9760       }
9761       dump << " ";
9762     }
9763   cout << dump << endl;
9764   return dump;
9765 }
9766
9767
9768 //================================================================================
9769 /*!
9770  * \brief Create layers of prisms
9771  */
9772 //================================================================================
9773
9774 bool _ViscousBuilder::refine(_SolidData& data)
9775 {
9776   SMESH_MesherHelper& helper = data.GetHelper();
9777   helper.SetElementsOnShape(false);
9778
9779   Handle(Geom_Curve) curve;
9780   Handle(ShapeAnalysis_Surface) surface;
9781   TopoDS_Edge geomEdge;
9782   TopoDS_Face geomFace;
9783   TopLoc_Location loc;
9784   double f,l, u = 0;
9785   gp_XY uv;
9786   vector< gp_XYZ > pos3D;
9787   bool isOnEdge, isTooConvexFace = false;
9788   TGeomID prevBaseId = -1;
9789   TNode2Edge* n2eMap = 0;
9790   TNode2Edge::iterator n2e;
9791
9792   // Create intermediate nodes on each _LayerEdge
9793
9794   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
9795   {
9796     _EdgesOnShape& eos = data._edgesOnShape[iS];
9797     if ( eos._edges.empty() ) continue;
9798
9799     if ( eos._edges[0]->_nodes.size() < 2 )
9800       continue; // on _noShrinkShapes
9801
9802     // get data of a shrink shape
9803     isOnEdge = false;
9804     geomEdge.Nullify(); geomFace.Nullify();
9805     curve.Nullify(); surface.Nullify();
9806     if ( !eos._sWOL.IsNull() )
9807     {
9808       isOnEdge = ( eos.SWOLType() == TopAbs_EDGE );
9809       if ( isOnEdge )
9810       {
9811         geomEdge = TopoDS::Edge( eos._sWOL );
9812         curve    = BRep_Tool::Curve( geomEdge, loc, f,l);
9813       }
9814       else
9815       {
9816         geomFace = TopoDS::Face( eos._sWOL );
9817         surface  = helper.GetSurface( geomFace );
9818       }
9819     }
9820     else if ( eos.ShapeType() == TopAbs_FACE && eos._toSmooth )
9821     {
9822       geomFace = TopoDS::Face( eos._shape );
9823       surface  = helper.GetSurface( geomFace );
9824       // propagate _toSmooth back to _eosC1, which was unset in findShapesToSmooth()
9825       for ( size_t i = 0; i < eos._eosC1.size(); ++i )
9826         eos._eosC1[ i ]->_toSmooth = true;
9827
9828       isTooConvexFace = false;
9829       if ( _ConvexFace* cf = data.GetConvexFace( eos._shapeID ))
9830         isTooConvexFace = cf->_isTooCurved;
9831     }
9832
9833     vector< double > segLen;
9834     for ( size_t i = 0; i < eos._edges.size(); ++i )
9835     {
9836       _LayerEdge& edge = *eos._edges[i];
9837       if ( edge._pos.size() < 2 )
9838         continue;
9839
9840       // get accumulated length of segments
9841       segLen.resize( edge._pos.size() );
9842       segLen[0] = 0.0;
9843       if ( eos._sWOL.IsNull() )
9844       {
9845         bool useNormal = true;
9846         bool    usePos = false;
9847         bool  smoothed = false;
9848         double   preci = 0.1 * edge._len;
9849         if ( eos._toSmooth && edge._pos.size() > 2 )
9850         {
9851           smoothed = edge.GetSmoothedPos( preci );
9852         }
9853         if ( smoothed )
9854         {
9855           if ( !surface.IsNull() && !isTooConvexFace ) // edge smoothed on FACE
9856           {
9857             useNormal = usePos = false;
9858             gp_Pnt2d uv = helper.GetNodeUV( geomFace, edge._nodes[0] );
9859             for ( size_t j = 1; j < edge._pos.size() && !useNormal; ++j )
9860             {
9861               uv = surface->NextValueOfUV( uv, edge._pos[j], preci );
9862               if ( surface->Gap() < 2. * edge._len )
9863                 segLen[j] = surface->Gap();
9864               else
9865                 useNormal = true;
9866             }
9867           }
9868         }
9869         else if ( !edge.Is( _LayerEdge::NORMAL_UPDATED ))
9870         {
9871 #ifndef __NODES_AT_POS
9872           useNormal = usePos = false;
9873           edge._pos[1] = edge._pos.back();
9874           edge._pos.resize( 2 );
9875           segLen.resize( 2 );
9876           segLen[ 1 ] = edge._len;
9877 #endif
9878         }
9879         if ( useNormal && edge.Is( _LayerEdge::NORMAL_UPDATED ))
9880         {
9881           useNormal = usePos = false;
9882           _LayerEdge tmpEdge; // get original _normal
9883           tmpEdge._nodes.push_back( edge._nodes[0] );
9884           if ( !setEdgeData( tmpEdge, eos, helper, data ))
9885             usePos = true;
9886           else
9887             for ( size_t j = 1; j < edge._pos.size(); ++j )
9888               segLen[j] = ( edge._pos[j] - edge._pos[0] ) * tmpEdge._normal;
9889         }
9890         if ( useNormal )
9891         {
9892           for ( size_t j = 1; j < edge._pos.size(); ++j )
9893             segLen[j] = ( edge._pos[j] - edge._pos[0] ) * edge._normal;
9894         }
9895         if ( usePos )
9896         {
9897           for ( size_t j = 1; j < edge._pos.size(); ++j )
9898             segLen[j] = segLen[j-1] + ( edge._pos[j-1] - edge._pos[j] ).Modulus();
9899         }
9900         else
9901         {
9902           bool swapped = ( edge._pos.size() > 2 );
9903           while ( swapped )
9904           {
9905             swapped = false;
9906             for ( size_t j = 1; j < edge._pos.size()-1; ++j )
9907               if ( segLen[j] > segLen.back() )
9908               {
9909                 segLen.erase( segLen.begin() + j );
9910                 edge._pos.erase( edge._pos.begin() + j );
9911                 --j;
9912               }
9913               else if ( segLen[j] < segLen[j-1] )
9914               {
9915                 std::swap( segLen[j], segLen[j-1] );
9916                 std::swap( edge._pos[j], edge._pos[j-1] );
9917                 swapped = true;
9918               }
9919           }
9920         }
9921         // smooth a path formed by edge._pos
9922 #ifndef __NODES_AT_POS
9923         if (( smoothed ) /*&&
9924             ( eos.ShapeType() == TopAbs_FACE || edge.Is( _LayerEdge::SMOOTHED_C1 ))*/)
9925           edge.SmoothPos( segLen, preci );
9926 #endif
9927       }
9928       else if ( eos._isRegularSWOL ) // usual SWOL
9929       {
9930         if ( edge.Is( _LayerEdge::SMOOTHED ))
9931         {
9932           SMESH_NodeXYZ p0( edge._nodes[0] );
9933           for ( size_t j = 1; j < edge._pos.size(); ++j )
9934           {
9935             gp_XYZ pj = surface->Value( edge._pos[j].X(), edge._pos[j].Y() ).XYZ();
9936             segLen[j] = ( pj - p0 ) * edge._normal;
9937           }
9938         }
9939         else
9940         {
9941           for ( size_t j = 1; j < edge._pos.size(); ++j )
9942             segLen[j] = segLen[j-1] + (edge._pos[j-1] - edge._pos[j] ).Modulus();
9943         }
9944       }
9945       else if ( !surface.IsNull() ) // SWOL surface with singularities
9946       {
9947         pos3D.resize( edge._pos.size() );
9948         for ( size_t j = 0; j < edge._pos.size(); ++j )
9949           pos3D[j] = surface->Value( edge._pos[j].X(), edge._pos[j].Y() ).XYZ();
9950
9951         for ( size_t j = 1; j < edge._pos.size(); ++j )
9952           segLen[j] = segLen[j-1] + ( pos3D[j-1] - pos3D[j] ).Modulus();
9953       }
9954
9955       // allocate memory for new nodes if it is not yet refined
9956       const SMDS_MeshNode* tgtNode = edge._nodes.back();
9957       if ( edge._nodes.size() == 2 )
9958       {
9959 #ifdef __NODES_AT_POS
9960         int nbNodes = edge._pos.size();
9961 #else
9962         int nbNodes = eos._hyp.GetNumberLayers() + 1;
9963 #endif
9964         edge._nodes.resize( nbNodes, 0 );
9965         edge._nodes[1] = 0;
9966         edge._nodes.back() = tgtNode;
9967       }
9968       // restore shapePos of the last node by already treated _LayerEdge of another _SolidData
9969       const TGeomID baseShapeId = edge._nodes[0]->getshapeId();
9970       if ( baseShapeId != prevBaseId )
9971       {
9972         map< TGeomID, TNode2Edge* >::iterator s2ne = data._s2neMap.find( baseShapeId );
9973         n2eMap = ( s2ne == data._s2neMap.end() ) ? 0 : s2ne->second;
9974         prevBaseId = baseShapeId;
9975       }
9976       _LayerEdge* edgeOnSameNode = 0;
9977       bool        useExistingPos = false;
9978       if ( n2eMap && (( n2e = n2eMap->find( edge._nodes[0] )) != n2eMap->end() ))
9979       {
9980         edgeOnSameNode = n2e->second;
9981         useExistingPos = ( edgeOnSameNode->_len < edge._len );
9982         const gp_XYZ& otherTgtPos = edgeOnSameNode->_pos.back();
9983         SMDS_PositionPtr  lastPos = tgtNode->GetPosition();
9984         if ( isOnEdge )
9985         {
9986           SMDS_EdgePosition* epos = static_cast<SMDS_EdgePosition*>( lastPos );
9987           epos->SetUParameter( otherTgtPos.X() );
9988         }
9989         else
9990         {
9991           SMDS_FacePosition* fpos = static_cast<SMDS_FacePosition*>( lastPos );
9992           fpos->SetUParameter( otherTgtPos.X() );
9993           fpos->SetVParameter( otherTgtPos.Y() );
9994         }
9995       }
9996       // calculate height of the first layer
9997       double h0;
9998       const double T = segLen.back(); //data._hyp.GetTotalThickness();
9999       const double f = eos._hyp.GetStretchFactor();
10000       const int    N = eos._hyp.GetNumberLayers();
10001       const double fPowN = pow( f, N );
10002       if ( fPowN - 1 <= numeric_limits<double>::min() )
10003         h0 = T / N;
10004       else
10005         h0 = T * ( f - 1 )/( fPowN - 1 );
10006
10007       const double zeroLen = std::numeric_limits<double>::min();
10008
10009       // create intermediate nodes
10010       double hSum = 0, hi = h0/f;
10011       size_t iSeg = 1;
10012       for ( size_t iStep = 1; iStep < edge._nodes.size(); ++iStep )
10013       {
10014         // compute an intermediate position
10015         hi *= f;
10016         hSum += hi;
10017         while ( hSum > segLen[iSeg] && iSeg < segLen.size()-1 )
10018           ++iSeg;
10019         int iPrevSeg = iSeg-1;
10020         while ( fabs( segLen[iPrevSeg] - segLen[iSeg]) <= zeroLen && iPrevSeg > 0 )
10021           --iPrevSeg;
10022         double   r = ( segLen[iSeg] - hSum ) / ( segLen[iSeg] - segLen[iPrevSeg] );
10023         gp_Pnt pos = r * edge._pos[iPrevSeg] + (1-r) * edge._pos[iSeg];
10024 #ifdef __NODES_AT_POS
10025         pos = edge._pos[ iStep ];
10026 #endif
10027         SMDS_MeshNode*& node = const_cast< SMDS_MeshNode*& >( edge._nodes[ iStep ]);
10028         if ( !eos._sWOL.IsNull() )
10029         {
10030           // compute XYZ by parameters <pos>
10031           if ( isOnEdge )
10032           {
10033             u = pos.X();
10034             if ( !node )
10035               pos = curve->Value( u ).Transformed(loc);
10036           }
10037           else if ( eos._isRegularSWOL )
10038           {
10039             uv.SetCoord( pos.X(), pos.Y() );
10040             if ( !node )
10041               pos = surface->Value( pos.X(), pos.Y() );
10042           }
10043           else
10044           {
10045             uv.SetCoord( pos.X(), pos.Y() );
10046             gp_Pnt p = r * pos3D[ iPrevSeg ] + (1-r) * pos3D[ iSeg ];
10047             uv = surface->NextValueOfUV( uv, p, BRep_Tool::Tolerance( geomFace )).XY();
10048             if ( !node )
10049               pos = surface->Value( uv );
10050           }
10051         }
10052         // create or update the node
10053         if ( !node )
10054         {
10055           node = helper.AddNode( pos.X(), pos.Y(), pos.Z());
10056           if ( !eos._sWOL.IsNull() )
10057           {
10058             if ( isOnEdge )
10059               getMeshDS()->SetNodeOnEdge( node, geomEdge, u );
10060             else
10061               getMeshDS()->SetNodeOnFace( node, geomFace, uv.X(), uv.Y() );
10062           }
10063           else
10064           {
10065             getMeshDS()->SetNodeInVolume( node, helper.GetSubShapeID() );
10066           }
10067         }
10068         else
10069         {
10070           if ( !eos._sWOL.IsNull() )
10071           {
10072             // make average pos from new and current parameters
10073             if ( isOnEdge )
10074             {
10075               //u = 0.5 * ( u + helper.GetNodeU( geomEdge, node ));
10076               if ( useExistingPos )
10077                 u = helper.GetNodeU( geomEdge, node );
10078               pos = curve->Value( u ).Transformed(loc);
10079
10080               SMDS_EdgePosition* epos = static_cast<SMDS_EdgePosition*>( node->GetPosition() );
10081               epos->SetUParameter( u );
10082             }
10083             else
10084             {
10085               //uv = 0.5 * ( uv + helper.GetNodeUV( geomFace, node ));
10086               if ( useExistingPos )
10087                 uv = helper.GetNodeUV( geomFace, node );
10088               pos = surface->Value( uv );
10089
10090               SMDS_FacePosition* fpos = static_cast<SMDS_FacePosition*>( node->GetPosition() );
10091               fpos->SetUParameter( uv.X() );
10092               fpos->SetVParameter( uv.Y() );
10093             }
10094           }
10095           node->setXYZ( pos.X(), pos.Y(), pos.Z() );
10096         }
10097       } // loop on edge._nodes
10098
10099       if ( !eos._sWOL.IsNull() ) // prepare for shrink()
10100       {
10101         if ( isOnEdge )
10102           edge._pos.back().SetCoord( u, 0,0);
10103         else
10104           edge._pos.back().SetCoord( uv.X(), uv.Y() ,0);
10105
10106         if ( edgeOnSameNode )
10107           edgeOnSameNode->_pos.back() = edge._pos.back();
10108       }
10109
10110     } // loop on eos._edges to create nodes
10111
10112
10113     if ( !getMeshDS()->IsEmbeddedMode() )
10114       // Log node movement
10115       for ( size_t i = 0; i < eos._edges.size(); ++i )
10116       {
10117         SMESH_TNodeXYZ p ( eos._edges[i]->_nodes.back() );
10118         getMeshDS()->MoveNode( p._node, p.X(), p.Y(), p.Z() );
10119       }
10120   }
10121
10122
10123   // Create volumes
10124
10125   helper.SetElementsOnShape(true);
10126
10127   vector< vector<const SMDS_MeshNode*>* > nnVec;
10128   set< vector<const SMDS_MeshNode*>* >    nnSet;
10129   set< int >                       degenEdgeInd;
10130   vector<const SMDS_MeshElement*>     degenVols;
10131
10132   TopExp_Explorer exp( data._solid, TopAbs_FACE );
10133   for ( ; exp.More(); exp.Next() )
10134   {
10135     const TGeomID faceID = getMeshDS()->ShapeToIndex( exp.Current() );
10136     if ( data._ignoreFaceIds.count( faceID ))
10137       continue;
10138     const bool isReversedFace = data._reversedFaceIds.count( faceID );
10139     SMESHDS_SubMesh*    fSubM = getMeshDS()->MeshElements( exp.Current() );
10140     SMDS_ElemIteratorPtr  fIt = fSubM->GetElements();
10141     while ( fIt->more() )
10142     {
10143       const SMDS_MeshElement* face = fIt->next();
10144       const int            nbNodes = face->NbCornerNodes();
10145       nnVec.resize( nbNodes );
10146       nnSet.clear();
10147       degenEdgeInd.clear();
10148       size_t maxZ = 0, minZ = std::numeric_limits<size_t>::max();
10149       SMDS_NodeIteratorPtr nIt = face->nodeIterator();
10150       for ( int iN = 0; iN < nbNodes; ++iN )
10151       {
10152         const SMDS_MeshNode* n = nIt->next();
10153         _LayerEdge*       edge = data._n2eMap[ n ];
10154         const int i = isReversedFace ? nbNodes-1-iN : iN;
10155         nnVec[ i ] = & edge->_nodes;
10156         maxZ = std::max( maxZ, nnVec[ i ]->size() );
10157         minZ = std::min( minZ, nnVec[ i ]->size() );
10158
10159         if ( helper.HasDegeneratedEdges() )
10160           nnSet.insert( nnVec[ i ]);
10161       }
10162
10163       if ( maxZ == 0 )
10164         continue;
10165       if ( 0 < nnSet.size() && nnSet.size() < 3 )
10166         continue;
10167
10168       switch ( nbNodes )
10169       {
10170       case 3: // TRIA
10171       {
10172         // PENTA
10173         for ( size_t iZ = 1; iZ < minZ; ++iZ )
10174           helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1], (*nnVec[2])[iZ-1],
10175                             (*nnVec[0])[iZ],   (*nnVec[1])[iZ],   (*nnVec[2])[iZ]);
10176
10177         for ( size_t iZ = minZ; iZ < maxZ; ++iZ )
10178         {
10179           for ( int iN = 0; iN < nbNodes; ++iN )
10180             if ( nnVec[ iN ]->size() < iZ+1 )
10181               degenEdgeInd.insert( iN );
10182
10183           if ( degenEdgeInd.size() == 1 )  // PYRAM
10184           {
10185             int i2 = *degenEdgeInd.begin();
10186             int i0 = helper.WrapIndex( i2 - 1, nbNodes );
10187             int i1 = helper.WrapIndex( i2 + 1, nbNodes );
10188             helper.AddVolume( (*nnVec[i0])[iZ-1], (*nnVec[i1])[iZ-1],
10189                               (*nnVec[i1])[iZ  ], (*nnVec[i0])[iZ  ], (*nnVec[i2]).back());
10190           }
10191           else  // TETRA
10192           {
10193             int i3 = !degenEdgeInd.count(0) ? 0 : !degenEdgeInd.count(1) ? 1 : 2;
10194             helper.AddVolume( (*nnVec[  0 ])[ i3 == 0 ? iZ-1 : nnVec[0]->size()-1 ],
10195                               (*nnVec[  1 ])[ i3 == 1 ? iZ-1 : nnVec[1]->size()-1 ],
10196                               (*nnVec[  2 ])[ i3 == 2 ? iZ-1 : nnVec[2]->size()-1 ],
10197                               (*nnVec[ i3 ])[ iZ ]);
10198           }
10199         }
10200         break; // TRIA
10201       }
10202       case 4: // QUAD
10203       {
10204         // HEX
10205         for ( size_t iZ = 1; iZ < minZ; ++iZ )
10206           helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1],
10207                             (*nnVec[2])[iZ-1], (*nnVec[3])[iZ-1],
10208                             (*nnVec[0])[iZ],   (*nnVec[1])[iZ],
10209                             (*nnVec[2])[iZ],   (*nnVec[3])[iZ]);
10210
10211         for ( size_t iZ = minZ; iZ < maxZ; ++iZ )
10212         {
10213           for ( int iN = 0; iN < nbNodes; ++iN )
10214             if ( nnVec[ iN ]->size() < iZ+1 )
10215               degenEdgeInd.insert( iN );
10216
10217           switch ( degenEdgeInd.size() )
10218           {
10219           case 2: // PENTA
10220           {
10221             int i2 = *degenEdgeInd.begin();
10222             int i3 = *degenEdgeInd.rbegin();
10223             bool ok = ( i3 - i2 == 1 );
10224             if ( i2 == 0 && i3 == 3 ) { i2 = 3; i3 = 0; ok = true; }
10225             int i0 = helper.WrapIndex( i3 + 1, nbNodes );
10226             int i1 = helper.WrapIndex( i0 + 1, nbNodes );
10227
10228             const SMDS_MeshElement* vol =
10229               helper.AddVolume( nnVec[i3]->back(), (*nnVec[i0])[iZ], (*nnVec[i0])[iZ-1],
10230                                 nnVec[i2]->back(), (*nnVec[i1])[iZ], (*nnVec[i1])[iZ-1]);
10231             if ( !ok && vol )
10232               degenVols.push_back( vol );
10233           }
10234           break;
10235
10236           default: // degen HEX
10237           {
10238             const SMDS_MeshElement* vol =
10239               helper.AddVolume( nnVec[0]->size() > iZ-1 ? (*nnVec[0])[iZ-1] : nnVec[0]->back(),
10240                                 nnVec[1]->size() > iZ-1 ? (*nnVec[1])[iZ-1] : nnVec[1]->back(),
10241                                 nnVec[2]->size() > iZ-1 ? (*nnVec[2])[iZ-1] : nnVec[2]->back(),
10242                                 nnVec[3]->size() > iZ-1 ? (*nnVec[3])[iZ-1] : nnVec[3]->back(),
10243                                 nnVec[0]->size() > iZ   ? (*nnVec[0])[iZ]   : nnVec[0]->back(),
10244                                 nnVec[1]->size() > iZ   ? (*nnVec[1])[iZ]   : nnVec[1]->back(),
10245                                 nnVec[2]->size() > iZ   ? (*nnVec[2])[iZ]   : nnVec[2]->back(),
10246                                 nnVec[3]->size() > iZ   ? (*nnVec[3])[iZ]   : nnVec[3]->back());
10247             degenVols.push_back( vol );
10248           }
10249           }
10250         }
10251         break; // HEX
10252       }
10253       default:
10254         return error("Not supported type of element", data._index);
10255
10256       } // switch ( nbNodes )
10257     } // while ( fIt->more() )
10258   } // loop on FACEs
10259
10260   if ( !degenVols.empty() )
10261   {
10262     SMESH_ComputeErrorPtr& err = _mesh->GetSubMesh( data._solid )->GetComputeError();
10263     if ( !err || err->IsOK() )
10264     {
10265       err.reset( new SMESH_ComputeError( COMPERR_WARNING,
10266                                          "Bad quality volumes created" ));
10267       err->myBadElements.insert( err->myBadElements.end(),
10268                                  degenVols.begin(),degenVols.end() );
10269     }
10270   }
10271
10272   return true;
10273 }
10274
10275 //================================================================================
10276 /*!
10277  * \brief Shrink 2D mesh on faces to let space for inflated layers
10278  */
10279 //================================================================================
10280
10281 bool _ViscousBuilder::shrink(_SolidData& theData)
10282 {
10283   // make map of (ids of FACEs to shrink mesh on) to (list of _SolidData containing
10284   // _LayerEdge's inflated along FACE or EDGE)
10285   map< TGeomID, list< _SolidData* > > f2sdMap;
10286   for ( size_t i = 0 ; i < _sdVec.size(); ++i )
10287   {
10288     _SolidData& data = _sdVec[i];
10289     map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
10290     for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
10291       if ( s2s->second.ShapeType() == TopAbs_FACE && !_shrinkedFaces.Contains( s2s->second ))
10292       {
10293         f2sdMap[ getMeshDS()->ShapeToIndex( s2s->second )].push_back( &data );
10294
10295         // Put mesh faces on the shrinked FACE to the proxy sub-mesh to avoid
10296         // usage of mesh faces made in addBoundaryElements() by the 3D algo or
10297         // by StdMeshers_QuadToTriaAdaptor
10298         if ( SMESHDS_SubMesh* smDS = getMeshDS()->MeshElements( s2s->second ))
10299         {
10300           SMESH_ProxyMesh::SubMesh* proxySub =
10301             data._proxyMesh->getFaceSubM( TopoDS::Face( s2s->second ), /*create=*/true);
10302           if ( proxySub->NbElements() == 0 )
10303           {
10304             SMDS_ElemIteratorPtr fIt = smDS->GetElements();
10305             while ( fIt->more() )
10306             {
10307               const SMDS_MeshElement* f = fIt->next();
10308               // as a result 3D algo will use elements from proxySub and not from smDS
10309               proxySub->AddElement( f );
10310               f->setIsMarked( true );
10311
10312               // Mark nodes on the FACE to discriminate them from nodes
10313               // added by addBoundaryElements(); marked nodes are to be smoothed while shrink()
10314               for ( int iN = 0, nbN = f->NbNodes(); iN < nbN; ++iN )
10315               {
10316                 const SMDS_MeshNode* n = f->GetNode( iN );
10317                 if ( n->GetPosition()->GetDim() == 2 )
10318                   n->setIsMarked( true );
10319               }
10320             }
10321           }
10322         }
10323       }
10324   }
10325
10326   SMESH_MesherHelper helper( *_mesh );
10327   helper.ToFixNodeParameters( true );
10328
10329   // EDGEs to shrink
10330   map< TGeomID, _Shrinker1D > e2shrMap;
10331   vector< _EdgesOnShape* > subEOS;
10332   vector< _LayerEdge* > lEdges;
10333
10334   // loop on FACEs to srink mesh on
10335   map< TGeomID, list< _SolidData* > >::iterator f2sd = f2sdMap.begin();
10336   for ( ; f2sd != f2sdMap.end(); ++f2sd )
10337   {
10338     list< _SolidData* > & dataList = f2sd->second;
10339     if ( dataList.front()->_n2eMap.empty() ||
10340          dataList.back() ->_n2eMap.empty() )
10341       continue; // not yet computed
10342     if ( dataList.front() != &theData &&
10343          dataList.back()  != &theData )
10344       continue;
10345
10346     _SolidData&      data = *dataList.front();
10347     _SolidData*     data2 = dataList.size() > 1 ? dataList.back() : 0;
10348     const TopoDS_Face&  F = TopoDS::Face( getMeshDS()->IndexToShape( f2sd->first ));
10349     SMESH_subMesh*     sm = _mesh->GetSubMesh( F );
10350     SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
10351
10352     Handle(Geom_Surface) surface = BRep_Tool::Surface( F );
10353
10354     _shrinkedFaces.Add( F );
10355     helper.SetSubShape( F );
10356
10357     // ===========================
10358     // Prepare data for shrinking
10359     // ===========================
10360
10361     // Collect nodes to smooth (they are marked at the beginning of this method)
10362     vector < const SMDS_MeshNode* > smoothNodes;
10363     {
10364       SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
10365       while ( nIt->more() )
10366       {
10367         const SMDS_MeshNode* n = nIt->next();
10368         if ( n->isMarked() )
10369           smoothNodes.push_back( n );
10370       }
10371     }
10372     // Find out face orientation
10373     double refSign = 1;
10374     const set<TGeomID> ignoreShapes;
10375     bool isOkUV;
10376     if ( !smoothNodes.empty() )
10377     {
10378       vector<_Simplex> simplices;
10379       _Simplex::GetSimplices( smoothNodes[0], simplices, ignoreShapes );
10380       helper.GetNodeUV( F, simplices[0]._nPrev, 0, &isOkUV ); // fix UV of simplex nodes
10381       helper.GetNodeUV( F, simplices[0]._nNext, 0, &isOkUV );
10382       gp_XY uv = helper.GetNodeUV( F, smoothNodes[0], 0, &isOkUV );
10383       if ( !simplices[0].IsForward(uv, smoothNodes[0], F, helper, refSign ))
10384         refSign = -1;
10385     }
10386
10387     // Find _LayerEdge's inflated along F
10388     subEOS.clear();
10389     lEdges.clear();
10390     {
10391       SMESH_subMeshIteratorPtr subIt = sm->getDependsOnIterator(/*includeSelf=*/false,
10392                                                                 /*complexFirst=*/true); //!!!
10393       while ( subIt->more() )
10394       {
10395         const TGeomID subID = subIt->next()->GetId();
10396         if ( data._noShrinkShapes.count( subID ))
10397           continue;
10398         _EdgesOnShape* eos = data.GetShapeEdges( subID );
10399         if ( !eos || eos->_sWOL.IsNull() )
10400           if ( data2 ) // check in adjacent SOLID
10401           {
10402             eos = data2->GetShapeEdges( subID );
10403             if ( !eos || eos->_sWOL.IsNull() )
10404               continue;
10405           }
10406         subEOS.push_back( eos );
10407
10408         for ( size_t i = 0; i < eos->_edges.size(); ++i )
10409         {
10410           lEdges.push_back( eos->_edges[ i ] );
10411           prepareEdgeToShrink( *eos->_edges[ i ], *eos, helper, smDS );
10412         }
10413       }
10414     }
10415
10416     dumpFunction(SMESH_Comment("beforeShrinkFace")<<f2sd->first); // debug
10417     SMDS_ElemIteratorPtr fIt = smDS->GetElements();
10418     while ( fIt->more() )
10419       if ( const SMDS_MeshElement* f = fIt->next() )
10420         dumpChangeNodes( f );
10421     dumpFunctionEnd();
10422
10423     // Replace source nodes by target nodes in mesh faces to shrink
10424     dumpFunction(SMESH_Comment("replNodesOnFace")<<f2sd->first); // debug
10425     const SMDS_MeshNode* nodes[20];
10426     for ( size_t iS = 0; iS < subEOS.size(); ++iS )
10427     {
10428       _EdgesOnShape& eos = * subEOS[ iS ];
10429       for ( size_t i = 0; i < eos._edges.size(); ++i )
10430       {
10431         _LayerEdge& edge = *eos._edges[i];
10432         const SMDS_MeshNode* srcNode = edge._nodes[0];
10433         const SMDS_MeshNode* tgtNode = edge._nodes.back();
10434         SMDS_ElemIteratorPtr fIt = srcNode->GetInverseElementIterator(SMDSAbs_Face);
10435         while ( fIt->more() )
10436         {
10437           const SMDS_MeshElement* f = fIt->next();
10438           if ( !smDS->Contains( f ) || !f->isMarked() )
10439             continue;
10440           SMDS_NodeIteratorPtr nIt = f->nodeIterator();
10441           for ( int iN = 0; nIt->more(); ++iN )
10442           {
10443             const SMDS_MeshNode* n = nIt->next();
10444             nodes[iN] = ( n == srcNode ? tgtNode : n );
10445           }
10446           helper.GetMeshDS()->ChangeElementNodes( f, nodes, f->NbNodes() );
10447           dumpChangeNodes( f );
10448         }
10449       }
10450     }
10451     dumpFunctionEnd();
10452
10453     // find out if a FACE is concave
10454     const bool isConcaveFace = isConcave( F, helper );
10455
10456     // Create _SmoothNode's on face F
10457     vector< _SmoothNode > nodesToSmooth( smoothNodes.size() );
10458     {
10459       dumpFunction(SMESH_Comment("fixUVOnFace")<<f2sd->first); // debug
10460       const bool sortSimplices = isConcaveFace;
10461       for ( size_t i = 0; i < smoothNodes.size(); ++i )
10462       {
10463         const SMDS_MeshNode* n = smoothNodes[i];
10464         nodesToSmooth[ i ]._node = n;
10465         // src nodes must be already replaced by tgt nodes to have tgt nodes in _simplices
10466         _Simplex::GetSimplices( n, nodesToSmooth[ i ]._simplices, ignoreShapes, 0, sortSimplices);
10467         // fix up incorrect uv of nodes on the FACE
10468         helper.GetNodeUV( F, n, 0, &isOkUV);
10469         dumpMove( n );
10470       }
10471       dumpFunctionEnd();
10472     }
10473     //if ( nodesToSmooth.empty() ) continue;
10474
10475     // Find EDGE's to shrink and set simpices to LayerEdge's
10476     set< _Shrinker1D* > eShri1D;
10477     {
10478       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
10479       {
10480         _EdgesOnShape& eos = * subEOS[ iS ];
10481         if ( eos.SWOLType() == TopAbs_EDGE )
10482         {
10483           SMESH_subMesh* edgeSM = _mesh->GetSubMesh( eos._sWOL );
10484           _Shrinker1D& srinker  = e2shrMap[ edgeSM->GetId() ];
10485           eShri1D.insert( & srinker );
10486           srinker.AddEdge( eos._edges[0], eos, helper );
10487           VISCOUS_3D::ToClearSubWithMain( edgeSM, data._solid );
10488           // restore params of nodes on EGDE if the EDGE has been already
10489           // srinked while srinking other FACE
10490           srinker.RestoreParams();
10491         }
10492         for ( size_t i = 0; i < eos._edges.size(); ++i )
10493         {
10494           _LayerEdge& edge = * eos._edges[i];
10495           _Simplex::GetSimplices( /*tgtNode=*/edge._nodes.back(), edge._simplices, ignoreShapes );
10496
10497           // additionally mark tgt node; only marked nodes will be used in SetNewLength2d()
10498           // not-marked nodes are those added by refine()
10499           edge._nodes.back()->setIsMarked( true );
10500         }
10501       }
10502     }
10503
10504     bool toFixTria = false; // to improve quality of trias by diagonal swap
10505     if ( isConcaveFace )
10506     {
10507       const bool hasTria = _mesh->NbTriangles(), hasQuad = _mesh->NbQuadrangles();
10508       if ( hasTria != hasQuad ) {
10509         toFixTria = hasTria;
10510       }
10511       else {
10512         set<int> nbNodesSet;
10513         SMDS_ElemIteratorPtr fIt = smDS->GetElements();
10514         while ( fIt->more() && nbNodesSet.size() < 2 )
10515           nbNodesSet.insert( fIt->next()->NbCornerNodes() );
10516         toFixTria = ( *nbNodesSet.begin() == 3 );
10517       }
10518     }
10519
10520     // ==================
10521     // Perform shrinking
10522     // ==================
10523
10524     bool shrinked = true;
10525     int nbBad, shriStep=0, smooStep=0;
10526     _SmoothNode::SmoothType smoothType
10527       = isConcaveFace ? _SmoothNode::ANGULAR : _SmoothNode::LAPLACIAN;
10528     SMESH_Comment errMsg;
10529     while ( shrinked )
10530     {
10531       shriStep++;
10532       // Move boundary nodes (actually just set new UV)
10533       // -----------------------------------------------
10534       dumpFunction(SMESH_Comment("moveBoundaryOnF")<<f2sd->first<<"_st"<<shriStep ); // debug
10535       shrinked = false;
10536       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
10537       {
10538         _EdgesOnShape& eos = * subEOS[ iS ];
10539         for ( size_t i = 0; i < eos._edges.size(); ++i )
10540         {
10541           shrinked |= eos._edges[i]->SetNewLength2d( surface, F, eos, helper );
10542         }
10543       }
10544       dumpFunctionEnd();
10545
10546       // Move nodes on EDGE's
10547       // (XYZ is set as soon as a needed length reached in SetNewLength2d())
10548       set< _Shrinker1D* >::iterator shr = eShri1D.begin();
10549       for ( ; shr != eShri1D.end(); ++shr )
10550         (*shr)->Compute( /*set3D=*/false, helper );
10551
10552       // Smoothing in 2D
10553       // -----------------
10554       int nbNoImpSteps = 0;
10555       bool       moved = true;
10556       nbBad = 1;
10557       while (( nbNoImpSteps < 5 && nbBad > 0) && moved)
10558       {
10559         dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
10560
10561         int oldBadNb = nbBad;
10562         nbBad = 0;
10563         moved = false;
10564         // '% 5' minimizes NB FUNCTIONS on viscous_layers_00/B2 case
10565         _SmoothNode::SmoothType smooTy = ( smooStep % 5 ) ? smoothType : _SmoothNode::LAPLACIAN;
10566         for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
10567         {
10568           moved |= nodesToSmooth[i].Smooth( nbBad, surface, helper, refSign,
10569                                             smooTy, /*set3D=*/isConcaveFace);
10570         }
10571         if ( nbBad < oldBadNb )
10572           nbNoImpSteps = 0;
10573         else
10574           nbNoImpSteps++;
10575
10576         dumpFunctionEnd();
10577       }
10578
10579       errMsg.clear();
10580       if ( nbBad > 0 )
10581         errMsg << "Can't shrink 2D mesh on face " << f2sd->first;
10582       if ( shriStep > 200 )
10583         errMsg << "Infinite loop at shrinking 2D mesh on face " << f2sd->first;
10584       if ( !errMsg.empty() )
10585         break;
10586
10587       // Fix narrow triangles by swapping diagonals
10588       // ---------------------------------------
10589       if ( toFixTria )
10590       {
10591         set<const SMDS_MeshNode*> usedNodes;
10592         fixBadFaces( F, helper, /*is2D=*/true, shriStep, & usedNodes); // swap diagonals
10593
10594         // update working data
10595         set<const SMDS_MeshNode*>::iterator n;
10596         for ( size_t i = 0; i < nodesToSmooth.size() && !usedNodes.empty(); ++i )
10597         {
10598           n = usedNodes.find( nodesToSmooth[ i ]._node );
10599           if ( n != usedNodes.end())
10600           {
10601             _Simplex::GetSimplices( nodesToSmooth[ i ]._node,
10602                                     nodesToSmooth[ i ]._simplices,
10603                                     ignoreShapes, NULL,
10604                                     /*sortSimplices=*/ smoothType == _SmoothNode::ANGULAR );
10605             usedNodes.erase( n );
10606           }
10607         }
10608         for ( size_t i = 0; i < lEdges.size() && !usedNodes.empty(); ++i )
10609         {
10610           n = usedNodes.find( /*tgtNode=*/ lEdges[i]->_nodes.back() );
10611           if ( n != usedNodes.end())
10612           {
10613             _Simplex::GetSimplices( lEdges[i]->_nodes.back(),
10614                                     lEdges[i]->_simplices,
10615                                     ignoreShapes );
10616             usedNodes.erase( n );
10617           }
10618         }
10619       }
10620       // TODO: check effect of this additional smooth
10621       // additional laplacian smooth to increase allowed shrink step
10622       // for ( int st = 1; st; --st )
10623       // {
10624       //   dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
10625       //   for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
10626       //   {
10627       //     nodesToSmooth[i].Smooth( nbBad,surface,helper,refSign,
10628       //                              _SmoothNode::LAPLACIAN,/*set3D=*/false);
10629       //   }
10630       // }
10631
10632     } // while ( shrinked )
10633
10634     if ( !errMsg.empty() ) // Try to re-compute the shrink FACE
10635     {
10636       debugMsg( "Re-compute FACE " << f2sd->first << " because " << errMsg );
10637
10638       // remove faces
10639       SMESHDS_SubMesh* psm = data._proxyMesh->getFaceSubM( F );
10640       {
10641         vector< const SMDS_MeshElement* > facesToRm;
10642         if ( psm )
10643         {
10644           facesToRm.reserve( psm->NbElements() );
10645           for ( SMDS_ElemIteratorPtr ite = psm->GetElements(); ite->more(); )
10646             facesToRm.push_back( ite->next() );
10647
10648           for ( size_t i = 0 ; i < _sdVec.size(); ++i )
10649             if (( psm = _sdVec[i]._proxyMesh->getFaceSubM( F )))
10650               psm->Clear();
10651         }
10652         for ( size_t i = 0; i < facesToRm.size(); ++i )
10653           getMeshDS()->RemoveFreeElement( facesToRm[i], smDS, /*fromGroups=*/false );
10654       }
10655       // remove nodes
10656       {
10657         TIDSortedNodeSet nodesToKeep; // nodes of _LayerEdge to keep
10658         for ( size_t iS = 0; iS < subEOS.size(); ++iS ) {
10659           for ( size_t i = 0; i < subEOS[iS]->_edges.size(); ++i )
10660             nodesToKeep.insert( ++( subEOS[iS]->_edges[i]->_nodes.begin() ),
10661                                 subEOS[iS]->_edges[i]->_nodes.end() );
10662         }
10663         SMDS_NodeIteratorPtr itn = smDS->GetNodes();
10664         while ( itn->more() ) {
10665           const SMDS_MeshNode* n = itn->next();
10666           if ( !nodesToKeep.count( n ))
10667             getMeshDS()->RemoveFreeNode( n, smDS, /*fromGroups=*/false );
10668         }
10669       }
10670       // restore position and UV of target nodes
10671       gp_Pnt p;
10672       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
10673         for ( size_t i = 0; i < subEOS[iS]->_edges.size(); ++i )
10674         {
10675           _LayerEdge*       edge = subEOS[iS]->_edges[i];
10676           SMDS_MeshNode* tgtNode = const_cast< SMDS_MeshNode*& >( edge->_nodes.back() );
10677           if ( edge->_pos.empty() ||
10678                edge->Is( _LayerEdge::SHRUNK )) continue;
10679           if ( subEOS[iS]->SWOLType() == TopAbs_FACE )
10680           {
10681             SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( tgtNode->GetPosition() );
10682             pos->SetUParameter( edge->_pos[0].X() );
10683             pos->SetVParameter( edge->_pos[0].Y() );
10684             p = surface->Value( edge->_pos[0].X(), edge->_pos[0].Y() );
10685           }
10686           else
10687           {
10688             SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( tgtNode->GetPosition() );
10689             pos->SetUParameter( edge->_pos[0].Coord( U_TGT ));
10690             p = BRepAdaptor_Curve( TopoDS::Edge( subEOS[iS]->_sWOL )).Value( pos->GetUParameter() );
10691           }
10692           tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
10693           dumpMove( tgtNode );
10694         }
10695       // shrink EDGE sub-meshes and set proxy sub-meshes
10696       UVPtStructVec uvPtVec;
10697       set< _Shrinker1D* >::iterator shrIt = eShri1D.begin();
10698       for ( shrIt = eShri1D.begin(); shrIt != eShri1D.end(); ++shrIt )
10699       {
10700         _Shrinker1D* shr = (*shrIt);
10701         shr->Compute( /*set3D=*/true, helper );
10702
10703         // set proxy mesh of EDGEs w/o layers
10704         map< double, const SMDS_MeshNode* > nodes;
10705         SMESH_Algo::GetSortedNodesOnEdge( getMeshDS(), shr->GeomEdge(),/*skipMedium=*/true, nodes);
10706         // remove refinement nodes
10707         const SMDS_MeshNode* sn0 = shr->SrcNode(0), *sn1 = shr->SrcNode(1);
10708         const SMDS_MeshNode* tn0 = shr->TgtNode(0), *tn1 = shr->TgtNode(1);
10709         map< double, const SMDS_MeshNode* >::iterator u2n = nodes.begin();
10710         if ( u2n->second == sn0 || u2n->second == sn1 )
10711         {
10712           while ( u2n->second != tn0 && u2n->second != tn1 )
10713             ++u2n;
10714           nodes.erase( nodes.begin(), u2n );
10715         }
10716         u2n = --nodes.end();
10717         if ( u2n->second == sn0 || u2n->second == sn1 )
10718         {
10719           while ( u2n->second != tn0 && u2n->second != tn1 )
10720             --u2n;
10721           nodes.erase( ++u2n, nodes.end() );
10722         }
10723         // set proxy sub-mesh
10724         uvPtVec.resize( nodes.size() );
10725         u2n = nodes.begin();
10726         BRepAdaptor_Curve2d curve( shr->GeomEdge(), F );
10727         for ( size_t i = 0; i < nodes.size(); ++i, ++u2n )
10728         {
10729           uvPtVec[ i ].node = u2n->second;
10730           uvPtVec[ i ].param = u2n->first;
10731           uvPtVec[ i ].SetUV( curve.Value( u2n->first ).XY() );
10732         }
10733         StdMeshers_FaceSide fSide( uvPtVec, F, shr->GeomEdge(), _mesh );
10734         StdMeshers_ViscousLayers2D::SetProxyMeshOfEdge( fSide );
10735       }
10736
10737       // set proxy mesh of EDGEs with layers
10738       vector< _LayerEdge* > edges;
10739       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
10740       {
10741         _EdgesOnShape& eos = * subEOS[ iS ];
10742         if ( eos.ShapeType() != TopAbs_EDGE ) continue;
10743
10744         const TopoDS_Edge& E = TopoDS::Edge( eos._shape );
10745         data.SortOnEdge( E, eos._edges );
10746
10747         edges.clear();
10748         if ( _EdgesOnShape* eov = data.GetShapeEdges( helper.IthVertex( 0, E, /*CumOri=*/false )))
10749           if ( !eov->_edges.empty() )
10750             edges.push_back( eov->_edges[0] ); // on 1st VERTEX
10751
10752         edges.insert( edges.end(), eos._edges.begin(), eos._edges.end() );
10753
10754         if ( _EdgesOnShape* eov = data.GetShapeEdges( helper.IthVertex( 1, E, /*CumOri=*/false )))
10755           if ( !eov->_edges.empty() )
10756             edges.push_back( eov->_edges[0] ); // on last VERTEX
10757
10758         uvPtVec.resize( edges.size() );
10759         for ( size_t i = 0; i < edges.size(); ++i )
10760         {
10761           uvPtVec[ i ].node = edges[i]->_nodes.back();
10762           uvPtVec[ i ].param = helper.GetNodeU( E, edges[i]->_nodes[0] );
10763           uvPtVec[ i ].SetUV( helper.GetNodeUV( F, edges[i]->_nodes.back() ));
10764         }
10765         BRep_Tool::Range( E, uvPtVec[0].param, uvPtVec.back().param );
10766         StdMeshers_FaceSide fSide( uvPtVec, F, E, _mesh );
10767         StdMeshers_ViscousLayers2D::SetProxyMeshOfEdge( fSide );
10768       }
10769       // temporary clear the FACE sub-mesh from faces made by refine()
10770       vector< const SMDS_MeshElement* > elems;
10771       elems.reserve( smDS->NbElements() + smDS->NbNodes() );
10772       for ( SMDS_ElemIteratorPtr ite = smDS->GetElements(); ite->more(); )
10773         elems.push_back( ite->next() );
10774       for ( SMDS_NodeIteratorPtr ite = smDS->GetNodes(); ite->more(); )
10775         elems.push_back( ite->next() );
10776       smDS->Clear();
10777
10778       // compute the mesh on the FACE
10779       sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
10780       sm->ComputeStateEngine( SMESH_subMesh::COMPUTE_SUBMESH );
10781
10782       // re-fill proxy sub-meshes of the FACE
10783       for ( size_t i = 0 ; i < _sdVec.size(); ++i )
10784         if (( psm = _sdVec[i]._proxyMesh->getFaceSubM( F )))
10785           for ( SMDS_ElemIteratorPtr ite = smDS->GetElements(); ite->more(); )
10786             psm->AddElement( ite->next() );
10787
10788       // re-fill smDS
10789       for ( size_t i = 0; i < elems.size(); ++i )
10790         smDS->AddElement( elems[i] );
10791
10792       if ( sm->GetComputeState() != SMESH_subMesh::COMPUTE_OK )
10793         return error( errMsg );
10794
10795     } // end of re-meshing in case of failed smoothing
10796     else
10797     {
10798       // No wrongly shaped faces remain; final smooth. Set node XYZ.
10799       bool isStructuredFixed = false;
10800       if ( SMESH_2D_Algo* algo = dynamic_cast<SMESH_2D_Algo*>( sm->GetAlgo() ))
10801         isStructuredFixed = algo->FixInternalNodes( *data._proxyMesh, F );
10802       if ( !isStructuredFixed )
10803       {
10804         if ( isConcaveFace ) // fix narrow faces by swapping diagonals
10805           fixBadFaces( F, helper, /*is2D=*/false, ++shriStep );
10806
10807         for ( int st = 3; st; --st )
10808         {
10809           switch( st ) {
10810           case 1: smoothType = _SmoothNode::LAPLACIAN; break;
10811           case 2: smoothType = _SmoothNode::LAPLACIAN; break;
10812           case 3: smoothType = _SmoothNode::ANGULAR; break;
10813           }
10814           dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
10815           for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
10816           {
10817             nodesToSmooth[i].Smooth( nbBad,surface,helper,refSign,
10818                                      smoothType,/*set3D=*/st==1 );
10819           }
10820           dumpFunctionEnd();
10821         }
10822       }
10823       if ( !getMeshDS()->IsEmbeddedMode() )
10824         // Log node movement
10825         for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
10826         {
10827           SMESH_TNodeXYZ p ( nodesToSmooth[i]._node );
10828           getMeshDS()->MoveNode( nodesToSmooth[i]._node, p.X(), p.Y(), p.Z() );
10829         }
10830     }
10831
10832     // Set an event listener to clear FACE sub-mesh together with SOLID sub-mesh
10833     VISCOUS_3D::ToClearSubWithMain( sm, data._solid );
10834     if ( data2 )
10835       VISCOUS_3D::ToClearSubWithMain( sm, data2->_solid );
10836
10837   } // loop on FACES to srink mesh on
10838
10839
10840   // Replace source nodes by target nodes in shrinked mesh edges
10841
10842   map< int, _Shrinker1D >::iterator e2shr = e2shrMap.begin();
10843   for ( ; e2shr != e2shrMap.end(); ++e2shr )
10844     e2shr->second.SwapSrcTgtNodes( getMeshDS() );
10845
10846   return true;
10847 }
10848
10849 //================================================================================
10850 /*!
10851  * \brief Computes 2d shrink direction and finds nodes limiting shrinking
10852  */
10853 //================================================================================
10854
10855 bool _ViscousBuilder::prepareEdgeToShrink( _LayerEdge&            edge,
10856                                            _EdgesOnShape&         eos,
10857                                            SMESH_MesherHelper&    helper,
10858                                            const SMESHDS_SubMesh* faceSubMesh)
10859 {
10860   const SMDS_MeshNode* srcNode = edge._nodes[0];
10861   const SMDS_MeshNode* tgtNode = edge._nodes.back();
10862
10863   if ( eos.SWOLType() == TopAbs_FACE )
10864   {
10865     if ( tgtNode->GetPosition()->GetDim() != 2 ) // not inflated edge
10866     {
10867       edge._pos.clear();
10868       edge.Set( _LayerEdge::SHRUNK );
10869       return srcNode == tgtNode;
10870     }
10871     gp_XY srcUV ( edge._pos[0].X(), edge._pos[0].Y() );          //helper.GetNodeUV( F, srcNode );
10872     gp_XY tgtUV = edge.LastUV( TopoDS::Face( eos._sWOL ), eos ); //helper.GetNodeUV( F, tgtNode );
10873     gp_Vec2d uvDir( srcUV, tgtUV );
10874     double uvLen = uvDir.Magnitude();
10875     uvDir /= uvLen;
10876     edge._normal.SetCoord( uvDir.X(),uvDir.Y(), 0 );
10877     edge._len = uvLen;
10878
10879     //edge._pos.resize(1);
10880     edge._pos[0].SetCoord( tgtUV.X(), tgtUV.Y(), 0 );
10881
10882     // set UV of source node to target node
10883     SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( tgtNode->GetPosition() );
10884     pos->SetUParameter( srcUV.X() );
10885     pos->SetVParameter( srcUV.Y() );
10886   }
10887   else // _sWOL is TopAbs_EDGE
10888   {
10889     if ( tgtNode->GetPosition()->GetDim() != 1 ) // not inflated edge
10890     {
10891       edge._pos.clear();
10892       edge.Set( _LayerEdge::SHRUNK );
10893       return srcNode == tgtNode;
10894     }
10895     const TopoDS_Edge&    E = TopoDS::Edge( eos._sWOL );
10896     SMESHDS_SubMesh* edgeSM = getMeshDS()->MeshElements( E );
10897     if ( !edgeSM || edgeSM->NbElements() == 0 )
10898       return error(SMESH_Comment("Not meshed EDGE ") << getMeshDS()->ShapeToIndex( E ));
10899
10900     const SMDS_MeshNode* n2 = 0;
10901     SMDS_ElemIteratorPtr eIt = srcNode->GetInverseElementIterator(SMDSAbs_Edge);
10902     while ( eIt->more() && !n2 )
10903     {
10904       const SMDS_MeshElement* e = eIt->next();
10905       if ( !edgeSM->Contains(e)) continue;
10906       n2 = e->GetNode( 0 );
10907       if ( n2 == srcNode ) n2 = e->GetNode( 1 );
10908     }
10909     if ( !n2 )
10910       return error(SMESH_Comment("Wrongly meshed EDGE ") << getMeshDS()->ShapeToIndex( E ));
10911
10912     double uSrc = helper.GetNodeU( E, srcNode, n2 );
10913     double uTgt = helper.GetNodeU( E, tgtNode, srcNode );
10914     double u2   = helper.GetNodeU( E, n2,      srcNode );
10915
10916     //edge._pos.clear();
10917
10918     if ( fabs( uSrc-uTgt ) < 0.99 * fabs( uSrc-u2 ))
10919     {
10920       // tgtNode is located so that it does not make faces with wrong orientation
10921       edge.Set( _LayerEdge::SHRUNK );
10922       return true;
10923     }
10924     //edge._pos.resize(1);
10925     edge._pos[0].SetCoord( U_TGT, uTgt );
10926     edge._pos[0].SetCoord( U_SRC, uSrc );
10927     edge._pos[0].SetCoord( LEN_TGT, fabs( uSrc-uTgt ));
10928
10929     edge._simplices.resize( 1 );
10930     edge._simplices[0]._nPrev = n2;
10931
10932     // set U of source node to the target node
10933     SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( tgtNode->GetPosition() );
10934     pos->SetUParameter( uSrc );
10935   }
10936   return true;
10937 }
10938
10939 //================================================================================
10940 /*!
10941  * \brief Restore position of a sole node of a _LayerEdge based on _noShrinkShapes
10942  */
10943 //================================================================================
10944
10945 void _ViscousBuilder::restoreNoShrink( _LayerEdge& edge ) const
10946 {
10947   if ( edge._nodes.size() == 1 )
10948   {
10949     edge._pos.clear();
10950     edge._len = 0;
10951
10952     const SMDS_MeshNode* srcNode = edge._nodes[0];
10953     TopoDS_Shape S = SMESH_MesherHelper::GetSubShapeByNode( srcNode, getMeshDS() );
10954     if ( S.IsNull() ) return;
10955
10956     gp_Pnt p;
10957
10958     switch ( S.ShapeType() )
10959     {
10960     case TopAbs_EDGE:
10961     {
10962       double f,l;
10963       TopLoc_Location loc;
10964       Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( S ), loc, f, l );
10965       if ( curve.IsNull() ) return;
10966       SMDS_EdgePosition* ePos = static_cast<SMDS_EdgePosition*>( srcNode->GetPosition() );
10967       p = curve->Value( ePos->GetUParameter() );
10968       break;
10969     }
10970     case TopAbs_VERTEX:
10971     {
10972       p = BRep_Tool::Pnt( TopoDS::Vertex( S ));
10973       break;
10974     }
10975     default: return;
10976     }
10977     getMeshDS()->MoveNode( srcNode, p.X(), p.Y(), p.Z() );
10978     dumpMove( srcNode );
10979   }
10980 }
10981
10982 //================================================================================
10983 /*!
10984  * \brief Try to fix triangles with high aspect ratio by swapping diagonals
10985  */
10986 //================================================================================
10987
10988 void _ViscousBuilder::fixBadFaces(const TopoDS_Face&          F,
10989                                   SMESH_MesherHelper&         helper,
10990                                   const bool                  is2D,
10991                                   const int                   step,
10992                                   set<const SMDS_MeshNode*> * involvedNodes)
10993 {
10994   SMESH::Controls::AspectRatio qualifier;
10995   SMESH::Controls::TSequenceOfXYZ points(3), points1(3), points2(3);
10996   const double maxAspectRatio = is2D ? 4. : 2;
10997   _NodeCoordHelper xyz( F, helper, is2D );
10998
10999   // find bad triangles
11000
11001   vector< const SMDS_MeshElement* > badTrias;
11002   vector< double >                  badAspects;
11003   SMESHDS_SubMesh*      sm = helper.GetMeshDS()->MeshElements( F );
11004   SMDS_ElemIteratorPtr fIt = sm->GetElements();
11005   while ( fIt->more() )
11006   {
11007     const SMDS_MeshElement * f = fIt->next();
11008     if ( f->NbCornerNodes() != 3 ) continue;
11009     for ( int iP = 0; iP < 3; ++iP ) points(iP+1) = xyz( f->GetNode(iP));
11010     double aspect = qualifier.GetValue( points );
11011     if ( aspect > maxAspectRatio )
11012     {
11013       badTrias.push_back( f );
11014       badAspects.push_back( aspect );
11015     }
11016   }
11017   if ( step == 1 )
11018   {
11019     dumpFunction(SMESH_Comment("beforeSwapDiagonals_F")<<helper.GetSubShapeID());
11020     SMDS_ElemIteratorPtr fIt = sm->GetElements();
11021     while ( fIt->more() )
11022     {
11023       const SMDS_MeshElement * f = fIt->next();
11024       if ( f->NbCornerNodes() == 3 )
11025         dumpChangeNodes( f );
11026     }
11027     dumpFunctionEnd();
11028   }
11029   if ( badTrias.empty() )
11030     return;
11031
11032   // find couples of faces to swap diagonal
11033
11034   typedef pair < const SMDS_MeshElement* , const SMDS_MeshElement* > T2Trias;
11035   vector< T2Trias > triaCouples; 
11036
11037   TIDSortedElemSet involvedFaces, emptySet;
11038   for ( size_t iTia = 0; iTia < badTrias.size(); ++iTia )
11039   {
11040     T2Trias trias    [3];
11041     double  aspRatio [3];
11042     int i1, i2, i3;
11043
11044     if ( !involvedFaces.insert( badTrias[iTia] ).second )
11045       continue;
11046     for ( int iP = 0; iP < 3; ++iP )
11047       points(iP+1) = xyz( badTrias[iTia]->GetNode(iP));
11048
11049     // find triangles adjacent to badTrias[iTia] with better aspect ratio after diag-swaping
11050     int bestCouple = -1;
11051     for ( int iSide = 0; iSide < 3; ++iSide )
11052     {
11053       const SMDS_MeshNode* n1 = badTrias[iTia]->GetNode( iSide );
11054       const SMDS_MeshNode* n2 = badTrias[iTia]->GetNode(( iSide+1 ) % 3 );
11055       trias [iSide].first  = badTrias[iTia];
11056       trias [iSide].second = SMESH_MeshAlgos::FindFaceInSet( n1, n2, emptySet, involvedFaces,
11057                                                              & i1, & i2 );
11058       if (( ! trias[iSide].second ) ||
11059           ( trias[iSide].second->NbCornerNodes() != 3 ) ||
11060           ( ! sm->Contains( trias[iSide].second )))
11061         continue;
11062
11063       // aspect ratio of an adjacent tria
11064       for ( int iP = 0; iP < 3; ++iP )
11065         points2(iP+1) = xyz( trias[iSide].second->GetNode(iP));
11066       double aspectInit = qualifier.GetValue( points2 );
11067
11068       // arrange nodes as after diag-swaping
11069       if ( helper.WrapIndex( i1+1, 3 ) == i2 )
11070         i3 = helper.WrapIndex( i1-1, 3 );
11071       else
11072         i3 = helper.WrapIndex( i1+1, 3 );
11073       points1 = points;
11074       points1( 1+ iSide ) = points2( 1+ i3 );
11075       points2( 1+ i2    ) = points1( 1+ ( iSide+2 ) % 3 );
11076
11077       // aspect ratio after diag-swaping
11078       aspRatio[ iSide ] = qualifier.GetValue( points1 ) + qualifier.GetValue( points2 );
11079       if ( aspRatio[ iSide ] > aspectInit + badAspects[ iTia ] )
11080         continue;
11081
11082       // prevent inversion of a triangle
11083       gp_Vec norm1 = gp_Vec( points1(1), points1(3) ) ^ gp_Vec( points1(1), points1(2) );
11084       gp_Vec norm2 = gp_Vec( points2(1), points2(3) ) ^ gp_Vec( points2(1), points2(2) );
11085       if ( norm1 * norm2 < 0. && norm1.Angle( norm2 ) > 70./180.*M_PI )
11086         continue;
11087
11088       if ( bestCouple < 0 || aspRatio[ bestCouple ] > aspRatio[ iSide ] )
11089         bestCouple = iSide;
11090     }
11091
11092     if ( bestCouple >= 0 )
11093     {
11094       triaCouples.push_back( trias[bestCouple] );
11095       involvedFaces.insert ( trias[bestCouple].second );
11096     }
11097     else
11098     {
11099       involvedFaces.erase( badTrias[iTia] );
11100     }
11101   }
11102   if ( triaCouples.empty() )
11103     return;
11104
11105   // swap diagonals
11106
11107   SMESH_MeshEditor editor( helper.GetMesh() );
11108   dumpFunction(SMESH_Comment("beforeSwapDiagonals_F")<<helper.GetSubShapeID()<<"_"<<step);
11109   for ( size_t i = 0; i < triaCouples.size(); ++i )
11110   {
11111     dumpChangeNodes( triaCouples[i].first );
11112     dumpChangeNodes( triaCouples[i].second );
11113     editor.InverseDiag( triaCouples[i].first, triaCouples[i].second );
11114   }
11115
11116   if ( involvedNodes )
11117     for ( size_t i = 0; i < triaCouples.size(); ++i )
11118     {
11119       involvedNodes->insert( triaCouples[i].first->begin_nodes(),
11120                              triaCouples[i].first->end_nodes() );
11121       involvedNodes->insert( triaCouples[i].second->begin_nodes(),
11122                              triaCouples[i].second->end_nodes() );
11123     }
11124
11125   // just for debug dump resulting triangles
11126   dumpFunction(SMESH_Comment("swapDiagonals_F")<<helper.GetSubShapeID()<<"_"<<step);
11127   for ( size_t i = 0; i < triaCouples.size(); ++i )
11128   {
11129     dumpChangeNodes( triaCouples[i].first );
11130     dumpChangeNodes( triaCouples[i].second );
11131   }
11132 }
11133
11134 //================================================================================
11135 /*!
11136  * \brief Move target node to it's final position on the FACE during shrinking
11137  */
11138 //================================================================================
11139
11140 bool _LayerEdge::SetNewLength2d( Handle(Geom_Surface)& surface,
11141                                  const TopoDS_Face&    F,
11142                                  _EdgesOnShape&        eos,
11143                                  SMESH_MesherHelper&   helper )
11144 {
11145   if ( Is( SHRUNK ))
11146     return false; // already at the target position
11147
11148   SMDS_MeshNode* tgtNode = const_cast< SMDS_MeshNode*& >( _nodes.back() );
11149
11150   if ( eos.SWOLType() == TopAbs_FACE )
11151   {
11152     gp_XY    curUV = helper.GetNodeUV( F, tgtNode );
11153     gp_Pnt2d tgtUV( _pos[0].X(), _pos[0].Y() );
11154     gp_Vec2d uvDir( _normal.X(), _normal.Y() );
11155     const double uvLen = tgtUV.Distance( curUV );
11156     const double kSafe = Max( 0.5, 1. - 0.1 * _simplices.size() );
11157
11158     // Select shrinking step such that not to make faces with wrong orientation.
11159     double stepSize = 1e100;
11160     for ( size_t i = 0; i < _simplices.size(); ++i )
11161     {
11162       if ( !_simplices[i]._nPrev->isMarked() ||
11163            !_simplices[i]._nNext->isMarked() )
11164         continue; // simplex of quadrangle created by addBoundaryElements()
11165
11166       // find intersection of 2 lines: curUV-tgtUV and that connecting simplex nodes
11167       gp_XY uvN1 = helper.GetNodeUV( F, _simplices[i]._nPrev );
11168       gp_XY uvN2 = helper.GetNodeUV( F, _simplices[i]._nNext );
11169       gp_XY dirN = uvN2 - uvN1;
11170       double det = uvDir.Crossed( dirN );
11171       if ( Abs( det )  < std::numeric_limits<double>::min() ) continue;
11172       gp_XY dirN2Cur = curUV - uvN1;
11173       double step = dirN.Crossed( dirN2Cur ) / det;
11174       if ( step > 0 )
11175         stepSize = Min( step, stepSize );
11176     }
11177     gp_Pnt2d newUV;
11178     if ( uvLen <= stepSize )
11179     {
11180       newUV = tgtUV;
11181       Set( SHRUNK );
11182       //_pos.clear();
11183     }
11184     else if ( stepSize > 0 )
11185     {
11186       newUV = curUV + uvDir.XY() * stepSize * kSafe;
11187     }
11188     else
11189     {
11190       return true;
11191     }
11192     SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( tgtNode->GetPosition() );
11193     pos->SetUParameter( newUV.X() );
11194     pos->SetVParameter( newUV.Y() );
11195
11196 #ifdef __myDEBUG
11197     gp_Pnt p = surface->Value( newUV.X(), newUV.Y() );
11198     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
11199     dumpMove( tgtNode );
11200 #endif
11201   }
11202   else // _sWOL is TopAbs_EDGE
11203   {
11204     const TopoDS_Edge&      E = TopoDS::Edge( eos._sWOL );
11205     const SMDS_MeshNode*   n2 = _simplices[0]._nPrev;
11206     SMDS_EdgePosition* tgtPos = static_cast<SMDS_EdgePosition*>( tgtNode->GetPosition() );
11207
11208     const double u2     = helper.GetNodeU( E, n2, tgtNode );
11209     const double uSrc   = _pos[0].Coord( U_SRC );
11210     const double lenTgt = _pos[0].Coord( LEN_TGT );
11211
11212     double newU = _pos[0].Coord( U_TGT );
11213     if ( lenTgt < 0.99 * fabs( uSrc-u2 )) // n2 got out of src-tgt range
11214     {
11215       Set( _LayerEdge::SHRUNK );
11216       //_pos.clear();
11217     }
11218     else
11219     {
11220       newU = 0.1 * tgtPos->GetUParameter() + 0.9 * u2;
11221     }
11222     tgtPos->SetUParameter( newU );
11223 #ifdef __myDEBUG
11224     gp_XY newUV = helper.GetNodeUV( F, tgtNode, _nodes[0]);
11225     gp_Pnt p = surface->Value( newUV.X(), newUV.Y() );
11226     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
11227     dumpMove( tgtNode );
11228 #endif
11229   }
11230
11231   return true;
11232 }
11233
11234 //================================================================================
11235 /*!
11236  * \brief Perform smooth on the FACE
11237  *  \retval bool - true if the node has been moved
11238  */
11239 //================================================================================
11240
11241 bool _SmoothNode::Smooth(int&                  nbBad,
11242                          Handle(Geom_Surface)& surface,
11243                          SMESH_MesherHelper&   helper,
11244                          const double          refSign,
11245                          SmoothType            how,
11246                          bool                  set3D)
11247 {
11248   const TopoDS_Face& face = TopoDS::Face( helper.GetSubShape() );
11249
11250   // get uv of surrounding nodes
11251   vector<gp_XY> uv( _simplices.size() );
11252   for ( size_t i = 0; i < _simplices.size(); ++i )
11253     uv[i] = helper.GetNodeUV( face, _simplices[i]._nPrev, _node );
11254
11255   // compute new UV for the node
11256   gp_XY newPos (0,0);
11257   if ( how == TFI && _simplices.size() == 4 )
11258   {
11259     gp_XY corners[4];
11260     for ( size_t i = 0; i < _simplices.size(); ++i )
11261       if ( _simplices[i]._nOpp )
11262         corners[i] = helper.GetNodeUV( face, _simplices[i]._nOpp, _node );
11263       else
11264         throw SALOME_Exception(LOCALIZED("TFI smoothing: _Simplex::_nOpp not set!"));
11265
11266     newPos = helper.calcTFI ( 0.5, 0.5,
11267                               corners[0], corners[1], corners[2], corners[3],
11268                               uv[1], uv[2], uv[3], uv[0] );
11269   }
11270   else if ( how == ANGULAR )
11271   {
11272     newPos = computeAngularPos( uv, helper.GetNodeUV( face, _node ), refSign );
11273   }
11274   else if ( how == CENTROIDAL && _simplices.size() > 3 )
11275   {
11276     // average centers of diagonals wieghted with their reciprocal lengths
11277     if ( _simplices.size() == 4 )
11278     {
11279       double w1 = 1. / ( uv[2]-uv[0] ).SquareModulus();
11280       double w2 = 1. / ( uv[3]-uv[1] ).SquareModulus();
11281       newPos = ( w1 * ( uv[2]+uv[0] ) + w2 * ( uv[3]+uv[1] )) / ( w1+w2 ) / 2;
11282     }
11283     else
11284     {
11285       double sumWeight = 0;
11286       int nb = _simplices.size() == 4 ? 2 : _simplices.size();
11287       for ( int i = 0; i < nb; ++i )
11288       {
11289         int iFrom = i + 2;
11290         int iTo   = i + _simplices.size() - 1;
11291         for ( int j = iFrom; j < iTo; ++j )
11292         {
11293           int i2 = SMESH_MesherHelper::WrapIndex( j, _simplices.size() );
11294           double w = 1. / ( uv[i]-uv[i2] ).SquareModulus();
11295           sumWeight += w;
11296           newPos += w * ( uv[i]+uv[i2] );
11297         }
11298       }
11299       newPos /= 2 * sumWeight; // 2 is to get a middle between uv's
11300     }
11301   }
11302   else
11303   {
11304     // Laplacian smooth
11305     for ( size_t i = 0; i < _simplices.size(); ++i )
11306       newPos += uv[i];
11307     newPos /= _simplices.size();
11308   }
11309
11310   // count quality metrics (orientation) of triangles around the node
11311   int nbOkBefore = 0;
11312   gp_XY tgtUV = helper.GetNodeUV( face, _node );
11313   for ( size_t i = 0; i < _simplices.size(); ++i )
11314     nbOkBefore += _simplices[i].IsForward( tgtUV, _node, face, helper, refSign );
11315
11316   int nbOkAfter = 0;
11317   for ( size_t i = 0; i < _simplices.size(); ++i )
11318     nbOkAfter += _simplices[i].IsForward( newPos, _node, face, helper, refSign );
11319
11320   if ( nbOkAfter < nbOkBefore )
11321   {
11322     nbBad += _simplices.size() - nbOkBefore;
11323     return false;
11324   }
11325
11326   SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( _node->GetPosition() );
11327   pos->SetUParameter( newPos.X() );
11328   pos->SetVParameter( newPos.Y() );
11329
11330 #ifdef __myDEBUG
11331   set3D = true;
11332 #endif
11333   if ( set3D )
11334   {
11335     gp_Pnt p = surface->Value( newPos.X(), newPos.Y() );
11336     const_cast< SMDS_MeshNode* >( _node )->setXYZ( p.X(), p.Y(), p.Z() );
11337     dumpMove( _node );
11338   }
11339
11340   nbBad += _simplices.size() - nbOkAfter;
11341   return ( (tgtUV-newPos).SquareModulus() > 1e-10 );
11342 }
11343
11344 //================================================================================
11345 /*!
11346  * \brief Computes new UV using angle based smoothing technic
11347  */
11348 //================================================================================
11349
11350 gp_XY _SmoothNode::computeAngularPos(vector<gp_XY>& uv,
11351                                      const gp_XY&   uvToFix,
11352                                      const double   refSign)
11353 {
11354   uv.push_back( uv.front() );
11355
11356   vector< gp_XY >  edgeDir ( uv.size() );
11357   vector< double > edgeSize( uv.size() );
11358   for ( size_t i = 1; i < edgeDir.size(); ++i )
11359   {
11360     edgeDir [i-1] = uv[i] - uv[i-1];
11361     edgeSize[i-1] = edgeDir[i-1].Modulus();
11362     if ( edgeSize[i-1] < numeric_limits<double>::min() )
11363       edgeDir[i-1].SetX( 100 );
11364     else
11365       edgeDir[i-1] /= edgeSize[i-1] * refSign;
11366   }
11367   edgeDir.back()  = edgeDir.front();
11368   edgeSize.back() = edgeSize.front();
11369
11370   gp_XY  newPos(0,0);
11371   //int    nbEdges = 0;
11372   double sumSize = 0;
11373   for ( size_t i = 1; i < edgeDir.size(); ++i )
11374   {
11375     if ( edgeDir[i-1].X() > 1. ) continue;
11376     int i1 = i-1;
11377     while ( edgeDir[i].X() > 1. && ++i < edgeDir.size() );
11378     if ( i == edgeDir.size() ) break;
11379     gp_XY p = uv[i];
11380     gp_XY norm1( -edgeDir[i1].Y(), edgeDir[i1].X() );
11381     gp_XY norm2( -edgeDir[i].Y(),  edgeDir[i].X() );
11382     gp_XY bisec = norm1 + norm2;
11383     double bisecSize = bisec.Modulus();
11384     if ( bisecSize < numeric_limits<double>::min() )
11385     {
11386       bisec = -edgeDir[i1] + edgeDir[i];
11387       bisecSize = bisec.Modulus();
11388     }
11389     bisec /= bisecSize;
11390
11391     gp_XY  dirToN  = uvToFix - p;
11392     double distToN = dirToN.Modulus();
11393     if ( bisec * dirToN < 0 )
11394       distToN = -distToN;
11395
11396     newPos += ( p + bisec * distToN ) * ( edgeSize[i1] + edgeSize[i] );
11397     //++nbEdges;
11398     sumSize += edgeSize[i1] + edgeSize[i];
11399   }
11400   newPos /= /*nbEdges * */sumSize;
11401   return newPos;
11402 }
11403
11404 //================================================================================
11405 /*!
11406  * \brief Delete _SolidData
11407  */
11408 //================================================================================
11409
11410 _SolidData::~_SolidData()
11411 {
11412   TNode2Edge::iterator n2e = _n2eMap.begin();
11413   for ( ; n2e != _n2eMap.end(); ++n2e )
11414   {
11415     _LayerEdge* & e = n2e->second;
11416     if ( e )
11417     {
11418       delete e->_curvature;
11419       if ( e->_2neibors )
11420         delete e->_2neibors->_plnNorm;
11421       delete e->_2neibors;
11422     }
11423     delete e;
11424     e = 0;
11425   }
11426   _n2eMap.clear();
11427
11428   delete _helper;
11429   _helper = 0;
11430 }
11431
11432 //================================================================================
11433 /*!
11434  * \brief Keep a _LayerEdge inflated along the EDGE
11435  */
11436 //================================================================================
11437
11438 void _Shrinker1D::AddEdge( const _LayerEdge*   e,
11439                            _EdgesOnShape&      eos,
11440                            SMESH_MesherHelper& helper )
11441 {
11442   // init
11443   if ( _nodes.empty() )
11444   {
11445     _edges[0] = _edges[1] = 0;
11446     _done = false;
11447   }
11448   // check _LayerEdge
11449   if ( e == _edges[0] || e == _edges[1] || e->_nodes.size() < 2 )
11450     return;
11451   if ( eos.SWOLType() != TopAbs_EDGE )
11452     throw SALOME_Exception(LOCALIZED("Wrong _LayerEdge is added"));
11453   if ( _edges[0] && !_geomEdge.IsSame( eos._sWOL ))
11454     throw SALOME_Exception(LOCALIZED("Wrong _LayerEdge is added"));
11455
11456   // store _LayerEdge
11457   _geomEdge = TopoDS::Edge( eos._sWOL );
11458   double f,l;
11459   BRep_Tool::Range( _geomEdge, f,l );
11460   double u = helper.GetNodeU( _geomEdge, e->_nodes[0], e->_nodes.back());
11461   _edges[ u < 0.5*(f+l) ? 0 : 1 ] = e;
11462
11463   // Update _nodes
11464
11465   const SMDS_MeshNode* tgtNode0 = TgtNode( 0 );
11466   const SMDS_MeshNode* tgtNode1 = TgtNode( 1 );
11467
11468   if ( _nodes.empty() )
11469   {
11470     SMESHDS_SubMesh * eSubMesh = helper.GetMeshDS()->MeshElements( _geomEdge );
11471     if ( !eSubMesh || eSubMesh->NbNodes() < 1 )
11472       return;
11473     TopLoc_Location loc;
11474     Handle(Geom_Curve) C = BRep_Tool::Curve( _geomEdge, loc, f,l );
11475     GeomAdaptor_Curve aCurve(C, f,l);
11476     const double totLen = GCPnts_AbscissaPoint::Length(aCurve, f, l);
11477
11478     int nbExpectNodes = eSubMesh->NbNodes();
11479     _initU  .reserve( nbExpectNodes );
11480     _normPar.reserve( nbExpectNodes );
11481     _nodes  .reserve( nbExpectNodes );
11482     SMDS_NodeIteratorPtr nIt = eSubMesh->GetNodes();
11483     while ( nIt->more() )
11484     {
11485       const SMDS_MeshNode* node = nIt->next();
11486
11487       // skip refinement nodes
11488       if ( node->NbInverseElements(SMDSAbs_Edge) == 0 ||
11489            node == tgtNode0 || node == tgtNode1 )
11490         continue;
11491       bool hasMarkedFace = false;
11492       SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
11493       while ( fIt->more() && !hasMarkedFace )
11494         hasMarkedFace = fIt->next()->isMarked();
11495       if ( !hasMarkedFace )
11496         continue;
11497
11498       _nodes.push_back( node );
11499       _initU.push_back( helper.GetNodeU( _geomEdge, node ));
11500       double len = GCPnts_AbscissaPoint::Length(aCurve, f, _initU.back());
11501       _normPar.push_back(  len / totLen );
11502     }
11503   }
11504   else
11505   {
11506     // remove target node of the _LayerEdge from _nodes
11507     size_t nbFound = 0;
11508     for ( size_t i = 0; i < _nodes.size(); ++i )
11509       if ( !_nodes[i] || _nodes[i] == tgtNode0 || _nodes[i] == tgtNode1 )
11510         _nodes[i] = 0, nbFound++;
11511     if ( nbFound == _nodes.size() )
11512       _nodes.clear();
11513   }
11514 }
11515
11516 //================================================================================
11517 /*!
11518  * \brief Move nodes on EDGE from ends where _LayerEdge's are inflated
11519  */
11520 //================================================================================
11521
11522 void _Shrinker1D::Compute(bool set3D, SMESH_MesherHelper& helper)
11523 {
11524   if ( _done || _nodes.empty())
11525     return;
11526   const _LayerEdge* e = _edges[0];
11527   if ( !e ) e = _edges[1];
11528   if ( !e ) return;
11529
11530   _done =  (( !_edges[0] || _edges[0]->Is( _LayerEdge::SHRUNK )) &&
11531             ( !_edges[1] || _edges[1]->Is( _LayerEdge::SHRUNK )));
11532
11533   double f,l;
11534   if ( set3D || _done )
11535   {
11536     Handle(Geom_Curve) C = BRep_Tool::Curve(_geomEdge, f,l);
11537     GeomAdaptor_Curve aCurve(C, f,l);
11538
11539     if ( _edges[0] )
11540       f = helper.GetNodeU( _geomEdge, _edges[0]->_nodes.back(), _nodes[0] );
11541     if ( _edges[1] )
11542       l = helper.GetNodeU( _geomEdge, _edges[1]->_nodes.back(), _nodes.back() );
11543     double totLen = GCPnts_AbscissaPoint::Length( aCurve, f, l );
11544
11545     for ( size_t i = 0; i < _nodes.size(); ++i )
11546     {
11547       if ( !_nodes[i] ) continue;
11548       double len = totLen * _normPar[i];
11549       GCPnts_AbscissaPoint discret( aCurve, len, f );
11550       if ( !discret.IsDone() )
11551         return throw SALOME_Exception(LOCALIZED("GCPnts_AbscissaPoint failed"));
11552       double u = discret.Parameter();
11553       SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( _nodes[i]->GetPosition() );
11554       pos->SetUParameter( u );
11555       gp_Pnt p = C->Value( u );
11556       const_cast< SMDS_MeshNode*>( _nodes[i] )->setXYZ( p.X(), p.Y(), p.Z() );
11557     }
11558   }
11559   else
11560   {
11561     BRep_Tool::Range( _geomEdge, f,l );
11562     if ( _edges[0] )
11563       f = helper.GetNodeU( _geomEdge, _edges[0]->_nodes.back(), _nodes[0] );
11564     if ( _edges[1] )
11565       l = helper.GetNodeU( _geomEdge, _edges[1]->_nodes.back(), _nodes.back() );
11566     
11567     for ( size_t i = 0; i < _nodes.size(); ++i )
11568     {
11569       if ( !_nodes[i] ) continue;
11570       double u = f * ( 1-_normPar[i] ) + l * _normPar[i];
11571       SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( _nodes[i]->GetPosition() );
11572       pos->SetUParameter( u );
11573     }
11574   }
11575 }
11576
11577 //================================================================================
11578 /*!
11579  * \brief Restore initial parameters of nodes on EDGE
11580  */
11581 //================================================================================
11582
11583 void _Shrinker1D::RestoreParams()
11584 {
11585   if ( _done )
11586     for ( size_t i = 0; i < _nodes.size(); ++i )
11587     {
11588       if ( !_nodes[i] ) continue;
11589       SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( _nodes[i]->GetPosition() );
11590       pos->SetUParameter( _initU[i] );
11591     }
11592   _done = false;
11593 }
11594
11595 //================================================================================
11596 /*!
11597  * \brief Replace source nodes by target nodes in shrinked mesh edges
11598  */
11599 //================================================================================
11600
11601 void _Shrinker1D::SwapSrcTgtNodes( SMESHDS_Mesh* mesh )
11602 {
11603   const SMDS_MeshNode* nodes[3];
11604   for ( int i = 0; i < 2; ++i )
11605   {
11606     if ( !_edges[i] ) continue;
11607
11608     SMESHDS_SubMesh * eSubMesh = mesh->MeshElements( _geomEdge );
11609     if ( !eSubMesh ) return;
11610     const SMDS_MeshNode* srcNode = _edges[i]->_nodes[0];
11611     const SMDS_MeshNode* tgtNode = _edges[i]->_nodes.back();
11612     const SMDS_MeshNode* scdNode = _edges[i]->_nodes[1];
11613     SMDS_ElemIteratorPtr eIt = srcNode->GetInverseElementIterator(SMDSAbs_Edge);
11614     while ( eIt->more() )
11615     {
11616       const SMDS_MeshElement* e = eIt->next();
11617       if ( !eSubMesh->Contains( e ) || e->GetNodeIndex( scdNode ) >= 0 )
11618           continue;
11619       SMDS_ElemIteratorPtr nIt = e->nodesIterator();
11620       for ( int iN = 0; iN < e->NbNodes(); ++iN )
11621       {
11622         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
11623         nodes[iN] = ( n == srcNode ? tgtNode : n );
11624       }
11625       mesh->ChangeElementNodes( e, nodes, e->NbNodes() );
11626     }
11627   }
11628 }
11629
11630 //================================================================================
11631 /*!
11632  * \brief Creates 2D and 1D elements on boundaries of new prisms
11633  */
11634 //================================================================================
11635
11636 bool _ViscousBuilder::addBoundaryElements(_SolidData& data)
11637 {
11638   SMESH_MesherHelper helper( *_mesh );
11639
11640   vector< const SMDS_MeshNode* > faceNodes;
11641
11642   //for ( size_t i = 0; i < _sdVec.size(); ++i )
11643   {
11644     //_SolidData& data = _sdVec[i];
11645     TopTools_IndexedMapOfShape geomEdges;
11646     TopExp::MapShapes( data._solid, TopAbs_EDGE, geomEdges );
11647     for ( int iE = 1; iE <= geomEdges.Extent(); ++iE )
11648     {
11649       const TopoDS_Edge& E = TopoDS::Edge( geomEdges(iE));
11650       const TGeomID edgeID = getMeshDS()->ShapeToIndex( E );
11651       if ( data._noShrinkShapes.count( edgeID ))
11652         continue;
11653
11654       // Get _LayerEdge's based on E
11655
11656       map< double, const SMDS_MeshNode* > u2nodes;
11657       if ( !SMESH_Algo::GetSortedNodesOnEdge( getMeshDS(), E, /*ignoreMedium=*/false, u2nodes))
11658         continue;
11659
11660       vector< _LayerEdge* > ledges; ledges.reserve( u2nodes.size() );
11661       TNode2Edge & n2eMap = data._n2eMap;
11662       map< double, const SMDS_MeshNode* >::iterator u2n = u2nodes.begin();
11663       {
11664         //check if 2D elements are needed on E
11665         TNode2Edge::iterator n2e = n2eMap.find( u2n->second );
11666         if ( n2e == n2eMap.end() ) continue; // no layers on vertex
11667         ledges.push_back( n2e->second );
11668         u2n++;
11669         if (( n2e = n2eMap.find( u2n->second )) == n2eMap.end() )
11670           continue; // no layers on E
11671         ledges.push_back( n2eMap[ u2n->second ]);
11672
11673         const SMDS_MeshNode* tgtN0 = ledges[0]->_nodes.back();
11674         const SMDS_MeshNode* tgtN1 = ledges[1]->_nodes.back();
11675         int nbSharedPyram = 0;
11676         SMDS_ElemIteratorPtr vIt = tgtN1->GetInverseElementIterator(SMDSAbs_Volume);
11677         while ( vIt->more() )
11678         {
11679           const SMDS_MeshElement* v = vIt->next();
11680           nbSharedPyram += int( v->GetNodeIndex( tgtN0 ) >= 0 );
11681         }
11682         if ( nbSharedPyram > 1 )
11683           continue; // not free border of the pyramid
11684
11685         faceNodes.clear();
11686         faceNodes.push_back( ledges[0]->_nodes[0] );
11687         faceNodes.push_back( ledges[1]->_nodes[0] );
11688         if ( ledges[0]->_nodes.size() > 1 ) faceNodes.push_back( ledges[0]->_nodes[1] );
11689         if ( ledges[1]->_nodes.size() > 1 ) faceNodes.push_back( ledges[1]->_nodes[1] );
11690
11691         if ( getMeshDS()->FindElement( faceNodes, SMDSAbs_Face, /*noMedium=*/true))
11692           continue; // faces already created
11693       }
11694       for ( ++u2n; u2n != u2nodes.end(); ++u2n )
11695         ledges.push_back( n2eMap[ u2n->second ]);
11696
11697       // Find out orientation and type of face to create
11698
11699       bool reverse = false, isOnFace;
11700       TopoDS_Shape F;
11701
11702       map< TGeomID, TopoDS_Shape >::iterator e2f = data._shrinkShape2Shape.find( edgeID );
11703       if (( isOnFace = ( e2f != data._shrinkShape2Shape.end() )))
11704       {
11705         F = e2f->second.Oriented( TopAbs_FORWARD );
11706         reverse = ( helper.GetSubShapeOri( F, E ) == TopAbs_REVERSED );
11707         if ( helper.GetSubShapeOri( data._solid, F ) == TopAbs_REVERSED )
11708           reverse = !reverse, F.Reverse();
11709         if ( helper.IsReversedSubMesh( TopoDS::Face(F) ))
11710           reverse = !reverse;
11711       }
11712       else if ( !data._ignoreFaceIds.count( e2f->first ))
11713       {
11714         // find FACE with layers sharing E
11715         PShapeIteratorPtr fIt = helper.GetAncestors( E, *_mesh, TopAbs_FACE, &data._solid );
11716         if ( fIt->more() )
11717           F = *( fIt->next() );
11718       }
11719       // Find the sub-mesh to add new faces
11720       SMESHDS_SubMesh* sm = 0;
11721       if ( isOnFace )
11722         sm = getMeshDS()->MeshElements( F );
11723       else
11724         sm = data._proxyMesh->getFaceSubM( TopoDS::Face(F), /*create=*/true );
11725       if ( !sm )
11726         return error("error in addBoundaryElements()", data._index);
11727
11728       // Find a proxy sub-mesh of the FACE of an adjacent SOLID, which will use the new boundary
11729       // faces for 3D meshing (PAL23414)
11730       SMESHDS_SubMesh* adjSM = 0;
11731       if ( isOnFace )
11732       {
11733         const TGeomID   faceID = sm->GetID();
11734         PShapeIteratorPtr soIt = helper.GetAncestors( F, *_mesh, TopAbs_SOLID );
11735         while ( const TopoDS_Shape* solid = soIt->next() )
11736           if ( !solid->IsSame( data._solid ))
11737           {
11738             size_t iData = _solids.FindIndex( *solid ) - 1;
11739             if ( iData < _sdVec.size() &&
11740                  _sdVec[ iData ]._ignoreFaceIds.count( faceID ) &&
11741                  _sdVec[ iData ]._shrinkShape2Shape.count( edgeID ) == 0 )
11742             {
11743               SMESH_ProxyMesh::SubMesh* proxySub =
11744                 _sdVec[ iData ]._proxyMesh->getFaceSubM( TopoDS::Face( F ), /*create=*/false);
11745               if ( proxySub && proxySub->NbElements() > 0 )
11746                 adjSM = proxySub;
11747             }
11748           }
11749       }
11750
11751       // Make faces
11752       const int dj1 = reverse ? 0 : 1;
11753       const int dj2 = reverse ? 1 : 0;
11754       vector< const SMDS_MeshElement*> ff; // new faces row
11755       SMESHDS_Mesh* m = getMeshDS();
11756       for ( size_t j = 1; j < ledges.size(); ++j )
11757       {
11758         vector< const SMDS_MeshNode*>&  nn1 = ledges[j-dj1]->_nodes;
11759         vector< const SMDS_MeshNode*>&  nn2 = ledges[j-dj2]->_nodes;
11760         ff.resize( std::max( nn1.size(), nn2.size() ), NULL );
11761         if ( nn1.size() == nn2.size() )
11762         {
11763           if ( isOnFace )
11764             for ( size_t z = 1; z < nn1.size(); ++z )
11765               sm->AddElement( ff[z-1] = m->AddFace( nn1[z-1], nn2[z-1], nn2[z], nn1[z] ));
11766           else
11767             for ( size_t z = 1; z < nn1.size(); ++z )
11768               sm->AddElement( new SMDS_FaceOfNodes( nn1[z-1], nn2[z-1], nn2[z], nn1[z] ));
11769         }
11770         else if ( nn1.size() == 1 )
11771         {
11772           if ( isOnFace )
11773             for ( size_t z = 1; z < nn2.size(); ++z )
11774               sm->AddElement( ff[z-1] = m->AddFace( nn1[0], nn2[z-1], nn2[z] ));
11775           else
11776             for ( size_t z = 1; z < nn2.size(); ++z )
11777               sm->AddElement( new SMDS_FaceOfNodes( nn1[0], nn2[z-1], nn2[z] ));
11778         }
11779         else
11780         {
11781           if ( isOnFace )
11782             for ( size_t z = 1; z < nn1.size(); ++z )
11783               sm->AddElement( ff[z-1] = m->AddFace( nn1[z-1], nn2[0], nn1[z] ));
11784           else
11785             for ( size_t z = 1; z < nn1.size(); ++z )
11786               sm->AddElement( new SMDS_FaceOfNodes( nn1[z-1], nn2[0], nn2[z] ));
11787         }
11788
11789         if ( adjSM ) // add faces to a proxy SM of the adjacent SOLID
11790         {
11791           for ( size_t z = 0; z < ff.size(); ++z )
11792             if ( ff[ z ])
11793               adjSM->AddElement( ff[ z ]);
11794           ff.clear();
11795         }
11796       }
11797
11798       // Make edges
11799       for ( int isFirst = 0; isFirst < 2; ++isFirst )
11800       {
11801         _LayerEdge* edge = isFirst ? ledges.front() : ledges.back();
11802         _EdgesOnShape* eos = data.GetShapeEdges( edge );
11803         if ( eos && eos->SWOLType() == TopAbs_EDGE )
11804         {
11805           vector< const SMDS_MeshNode*>&  nn = edge->_nodes;
11806           if ( nn.size() < 2 || nn[1]->NbInverseElements( SMDSAbs_Edge ) >= 2 )
11807             continue;
11808           helper.SetSubShape( eos->_sWOL );
11809           helper.SetElementsOnShape( true );
11810           for ( size_t z = 1; z < nn.size(); ++z )
11811             helper.AddEdge( nn[z-1], nn[z] );
11812         }
11813       }
11814
11815     } // loop on EDGE's
11816   } // loop on _SolidData's
11817
11818   return true;
11819 }