Salome HOME
Cleanup of parallel meshing + documentation
[modules/smesh.git] / src / StdMeshers / StdMeshers_ViscousLayers.cxx
1 // Copyright (C) 2007-2022  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 "ObjectPool.hxx"
27 #include "SMDS_EdgePosition.hxx"
28 #include "SMDS_FaceOfNodes.hxx"
29 #include "SMDS_FacePosition.hxx"
30 #include "SMDS_MeshNode.hxx"
31 #include "SMDS_PolygonalFaceOfNodes.hxx"
32 #include "SMDS_SetIterator.hxx"
33 #include "SMESHDS_Group.hxx"
34 #include "SMESHDS_Hypothesis.hxx"
35 #include "SMESHDS_Mesh.hxx"
36 #include "SMESH_Algo.hxx"
37 #include "SMESH_Block.hxx"
38 #include "SMESH_ComputeError.hxx"
39 #include "SMESH_ControlsDef.hxx"
40 #include "SMESH_Gen.hxx"
41 #include "SMESH_Group.hxx"
42 #include "SMESH_HypoFilter.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MeshEditor.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_ProxyMesh.hxx"
48 #include "SMESH_subMesh.hxx"
49 #include "SMESH_subMeshEventListener.hxx"
50 #include "StdMeshers_FaceSide.hxx"
51 #include "StdMeshers_ProjectionUtils.hxx"
52 #include "StdMeshers_Quadrangle_2D.hxx"
53 #include "StdMeshers_ViscousLayers2D.hxx"
54
55 #include <Basics_OCCTVersion.hxx>
56
57 #if OCC_VERSION_LARGE < 0x07070000
58 #include <Adaptor3d_HSurface.hxx>
59 #else
60 #include <Adaptor3d_Surface.hxx>
61 #endif
62
63 #include <BRepAdaptor_Curve.hxx>
64 #include <BRepAdaptor_Curve2d.hxx>
65 #include <BRepAdaptor_Surface.hxx>
66 #include <BRepLProp_SLProps.hxx>
67 #include <BRepOffsetAPI_MakeOffsetShape.hxx>
68 #include <BRep_Tool.hxx>
69 #include <Bnd_B2d.hxx>
70 #include <Bnd_B3d.hxx>
71 #include <ElCLib.hxx>
72 #include <GCPnts_AbscissaPoint.hxx>
73 #include <GCPnts_TangentialDeflection.hxx>
74 #include <Geom2d_Circle.hxx>
75 #include <Geom2d_Line.hxx>
76 #include <Geom2d_TrimmedCurve.hxx>
77 #include <GeomAdaptor_Curve.hxx>
78 #include <GeomLib.hxx>
79 #include <Geom_Circle.hxx>
80 #include <Geom_Curve.hxx>
81 #include <Geom_Line.hxx>
82 #include <Geom_TrimmedCurve.hxx>
83 #include <Precision.hxx>
84 #include <Standard_ErrorHandler.hxx>
85 #include <Standard_Failure.hxx>
86 #include <TColStd_Array1OfReal.hxx>
87 #include <TopExp.hxx>
88 #include <TopExp_Explorer.hxx>
89 #include <TopTools_IndexedMapOfShape.hxx>
90 #include <TopTools_ListOfShape.hxx>
91 #include <TopTools_MapIteratorOfMapOfShape.hxx>
92 #include <TopTools_MapOfShape.hxx>
93 #include <TopoDS.hxx>
94 #include <TopoDS_Edge.hxx>
95 #include <TopoDS_Face.hxx>
96 #include <TopoDS_Vertex.hxx>
97 #include <gp_Ax1.hxx>
98 #include <gp_Cone.hxx>
99 #include <gp_Sphere.hxx>
100 #include <gp_Vec.hxx>
101 #include <gp_XY.hxx>
102
103 #include <cmath>
104 #include <limits>
105 #include <list>
106 #include <queue>
107 #include <string>
108 #include <unordered_map>
109
110 #ifdef _DEBUG_
111 #ifndef WIN32
112 #define __myDEBUG
113 #endif
114 //#define __NOT_INVALIDATE_BAD_SMOOTH
115 //#define __NODES_AT_POS
116 #endif
117
118 #define INCREMENTAL_SMOOTH // smooth only if min angle is too small
119 #define BLOCK_INFLATION // of individual _LayerEdge's
120 #define OLD_NEF_POLYGON
121
122 using namespace std;
123
124 //================================================================================
125 namespace VISCOUS_3D
126 {
127   typedef int TGeomID;
128
129   enum UIndex { U_TGT = 1, U_SRC, LEN_TGT };
130
131   const double theMinSmoothCosin = 0.1;
132   const double theSmoothThickToElemSizeRatio = 0.6;
133   const double theMinSmoothTriaAngle = 30;
134   const double theMinSmoothQuadAngle = 45;
135
136   // what part of thickness is allowed till intersection
137   // (defined by SALOME_TESTS/Grids/smesh/viscous_layers_00/A5)
138   const double theThickToIntersection = 1.5;
139
140   bool needSmoothing( double cosin, double tgtThick, double elemSize )
141   {
142     return cosin * tgtThick > theSmoothThickToElemSizeRatio * elemSize;
143   }
144   double getSmoothingThickness( double cosin, double elemSize )
145   {
146     return theSmoothThickToElemSizeRatio * elemSize / cosin;
147   }
148
149   /*!
150    * \brief SMESH_ProxyMesh computed by _ViscousBuilder for a SOLID.
151    * It is stored in a SMESH_subMesh of the SOLID as SMESH_subMeshEventListenerData
152    */
153   struct _MeshOfSolid : public SMESH_ProxyMesh,
154                         public SMESH_subMeshEventListenerData
155   {
156     bool                  _n2nMapComputed;
157     SMESH_ComputeErrorPtr _warning;
158
159     _MeshOfSolid( SMESH_Mesh* mesh)
160       :SMESH_subMeshEventListenerData( /*isDeletable=*/true),_n2nMapComputed(false)
161     {
162       SMESH_ProxyMesh::setMesh( *mesh );
163     }
164
165     // returns submesh for a geom face
166     SMESH_ProxyMesh::SubMesh* getFaceSubM(const TopoDS_Face& F, bool create=false)
167     {
168       TGeomID i = SMESH_ProxyMesh::shapeIndex(F);
169       return create ? SMESH_ProxyMesh::getProxySubMesh(i) : findProxySubMesh(i);
170     }
171     void setNode2Node(const SMDS_MeshNode*                 srcNode,
172                       const SMDS_MeshNode*                 proxyNode,
173                       const SMESH_ProxyMesh::SubMesh* subMesh)
174     {
175       SMESH_ProxyMesh::setNode2Node( srcNode,proxyNode,subMesh);
176     }
177   };
178   //--------------------------------------------------------------------------------
179   /*!
180    * \brief Listener of events of 3D sub-meshes computed with viscous layers.
181    * It is used to clear an inferior dim sub-meshes modified by viscous layers
182    */
183   class _ShrinkShapeListener : SMESH_subMeshEventListener
184   {
185     _ShrinkShapeListener()
186       : SMESH_subMeshEventListener(/*isDeletable=*/false,
187                                    "StdMeshers_ViscousLayers::_ShrinkShapeListener") {}
188   public:
189     static SMESH_subMeshEventListener* Get() { static _ShrinkShapeListener l; return &l; }
190     virtual void ProcessEvent(const int                       event,
191                               const int                       eventType,
192                               SMESH_subMesh*                  solidSM,
193                               SMESH_subMeshEventListenerData* data,
194                               const SMESH_Hypothesis*         hyp)
195     {
196       if ( SMESH_subMesh::COMPUTE_EVENT == eventType && solidSM->IsEmpty() && data )
197       {
198         SMESH_subMeshEventListener::ProcessEvent(event,eventType,solidSM,data,hyp);
199       }
200     }
201   };
202   //--------------------------------------------------------------------------------
203   /*!
204    * \brief Listener of events of 3D sub-meshes computed with viscous layers.
205    * It is used to store data computed by _ViscousBuilder for a sub-mesh and to
206    * delete the data as soon as it has been used
207    */
208   class _ViscousListener : SMESH_subMeshEventListener
209   {
210     _ViscousListener():
211       SMESH_subMeshEventListener(/*isDeletable=*/false,
212                                  "StdMeshers_ViscousLayers::_ViscousListener") {}
213     static SMESH_subMeshEventListener* Get() { static _ViscousListener l; return &l; }
214   public:
215     virtual void ProcessEvent(const int                       event,
216                               const int                       eventType,
217                               SMESH_subMesh*                  subMesh,
218                               SMESH_subMeshEventListenerData* /*data*/,
219                               const SMESH_Hypothesis*         /*hyp*/)
220     {
221       if (( SMESH_subMesh::COMPUTE_EVENT       == eventType ) &&
222           ( SMESH_subMesh::CHECK_COMPUTE_STATE != event &&
223             SMESH_subMesh::SUBMESH_COMPUTED    != event ))
224       {
225         // delete SMESH_ProxyMesh containing temporary faces
226         subMesh->DeleteEventListener( this );
227       }
228     }
229     // Finds or creates proxy mesh of the solid
230     static _MeshOfSolid* GetSolidMesh(SMESH_Mesh*         mesh,
231                                       const TopoDS_Shape& solid,
232                                       bool                toCreate=false)
233     {
234       if ( !mesh ) return 0;
235       SMESH_subMesh* sm = mesh->GetSubMesh(solid);
236       _MeshOfSolid* data = (_MeshOfSolid*) sm->GetEventListenerData( Get() );
237       if ( !data && toCreate )
238       {
239         data = new _MeshOfSolid(mesh);
240         data->mySubMeshes.push_back( sm ); // to find SOLID by _MeshOfSolid
241         sm->SetEventListener( Get(), data, sm );
242       }
243       return data;
244     }
245     // Removes proxy mesh of the solid
246     static void RemoveSolidMesh(SMESH_Mesh* mesh, const TopoDS_Shape& solid)
247     {
248       mesh->GetSubMesh(solid)->DeleteEventListener( _ViscousListener::Get() );
249     }
250   };
251   
252   //================================================================================
253   /*!
254    * \brief sets a sub-mesh event listener to clear sub-meshes of sub-shapes of
255    * the main shape when sub-mesh of the main shape is cleared,
256    * for example to clear sub-meshes of FACEs when sub-mesh of a SOLID
257    * is cleared
258    */
259   //================================================================================
260
261   void ToClearSubWithMain( SMESH_subMesh* sub, const TopoDS_Shape& main)
262   {
263     SMESH_subMesh* mainSM = sub->GetFather()->GetSubMesh( main );
264     SMESH_subMeshEventListenerData* data =
265       mainSM->GetEventListenerData( _ShrinkShapeListener::Get());
266     if ( data )
267     {
268       if ( find( data->mySubMeshes.begin(), data->mySubMeshes.end(), sub ) ==
269            data->mySubMeshes.end())
270         data->mySubMeshes.push_back( sub );
271     }
272     else
273     {
274       data = SMESH_subMeshEventListenerData::MakeData( /*dependent=*/sub );
275       sub->SetEventListener( _ShrinkShapeListener::Get(), data, /*whereToListenTo=*/mainSM );
276     }
277   }
278   struct _SolidData;
279   //--------------------------------------------------------------------------------
280   /*!
281    * \brief Simplex (triangle or tetrahedron) based on 1 (tria) or 2 (tet) nodes of
282    * _LayerEdge and 2 nodes of the mesh surface beening smoothed.
283    * The class is used to check validity of face or volumes around a smoothed node;
284    * it stores only 2 nodes as the other nodes are stored by _LayerEdge.
285    */
286   struct _Simplex
287   {
288     const SMDS_MeshNode *_nPrev, *_nNext; // nodes on a smoothed mesh surface
289     const SMDS_MeshNode *_nOpp; // in 2D case, a node opposite to a smoothed node in QUAD
290     _Simplex(const SMDS_MeshNode* nPrev=0,
291              const SMDS_MeshNode* nNext=0,
292              const SMDS_MeshNode* nOpp=0)
293       : _nPrev(nPrev), _nNext(nNext), _nOpp(nOpp) {}
294     bool IsForward(const gp_XYZ* pntSrc, const gp_XYZ* pntTgt, double& vol) const
295     {
296       const double M[3][3] =
297         {{ _nNext->X() - pntSrc->X(), _nNext->Y() - pntSrc->Y(), _nNext->Z() - pntSrc->Z() },
298          { pntTgt->X() - pntSrc->X(), pntTgt->Y() - pntSrc->Y(), pntTgt->Z() - pntSrc->Z() },
299          { _nPrev->X() - pntSrc->X(), _nPrev->Y() - pntSrc->Y(), _nPrev->Z() - pntSrc->Z() }};
300       vol = ( + M[0][0] * M[1][1] * M[2][2]
301               + M[0][1] * M[1][2] * M[2][0]
302               + M[0][2] * M[1][0] * M[2][1]
303               - M[0][0] * M[1][2] * M[2][1]
304               - M[0][1] * M[1][0] * M[2][2]
305               - M[0][2] * M[1][1] * M[2][0]);
306       return vol > 1e-100;
307     }
308     bool IsForward(const SMDS_MeshNode* nSrc, const gp_XYZ& pTgt, double& vol) const
309     {
310       SMESH_TNodeXYZ pSrc( nSrc );
311       return IsForward( &pSrc, &pTgt, vol );
312     }
313     bool IsForward(const gp_XY&         tgtUV,
314                    const SMDS_MeshNode* smoothedNode,
315                    const TopoDS_Face&   face,
316                    SMESH_MesherHelper&  helper,
317                    const double         refSign) const
318     {
319       gp_XY prevUV = helper.GetNodeUV( face, _nPrev, smoothedNode );
320       gp_XY nextUV = helper.GetNodeUV( face, _nNext, smoothedNode );
321       gp_Vec2d v1( tgtUV, prevUV ), v2( tgtUV, nextUV );
322       double d = v1 ^ v2;
323       return d*refSign > 1e-100;
324     }
325     bool IsMinAngleOK( const gp_XYZ& pTgt, double& minAngle ) const
326     {
327       SMESH_TNodeXYZ pPrev( _nPrev ), pNext( _nNext );
328       if ( !_nOpp ) // triangle
329       {
330         gp_Vec tp( pPrev - pTgt ), pn( pNext - pPrev ), nt( pTgt - pNext );
331         double tp2 = tp.SquareMagnitude();
332         double pn2 = pn.SquareMagnitude();
333         double nt2 = nt.SquareMagnitude();
334
335         if ( tp2 < pn2 && tp2 < nt2 )
336           minAngle = ( nt * -pn ) * ( nt * -pn ) / nt2 / pn2;
337         else if ( pn2 < nt2 )
338           minAngle = ( tp * -nt ) * ( tp * -nt ) / tp2 / nt2;
339         else
340           minAngle = ( pn * -tp ) * ( pn * -tp ) / pn2 / tp2;
341
342         static double theMaxCos2 = ( Cos( theMinSmoothTriaAngle * M_PI / 180. ) *
343                                      Cos( theMinSmoothTriaAngle * M_PI / 180. ));
344         return minAngle < theMaxCos2;
345       }
346       else // quadrangle
347       {
348         SMESH_TNodeXYZ pOpp( _nOpp );
349         gp_Vec tp( pPrev - pTgt ), po( pOpp - pPrev ), on( pNext - pOpp), nt( pTgt - pNext );
350         double tp2 = tp.SquareMagnitude();
351         double po2 = po.SquareMagnitude();
352         double on2 = on.SquareMagnitude();
353         double nt2 = nt.SquareMagnitude();
354         minAngle = Max( Max((( tp * -nt ) * ( tp * -nt ) / tp2 / nt2 ),
355                             (( po * -tp ) * ( po * -tp ) / po2 / tp2 )),
356                         Max((( on * -po ) * ( on * -po ) / on2 / po2 ),
357                             (( nt * -on ) * ( nt * -on ) / nt2 / on2 )));
358
359         static double theMaxCos2 = ( Cos( theMinSmoothQuadAngle * M_PI / 180. ) *
360                                      Cos( theMinSmoothQuadAngle * M_PI / 180. ));
361         return minAngle < theMaxCos2;
362       }
363     }
364     bool IsNeighbour(const _Simplex& other) const
365     {
366       return _nPrev == other._nNext || _nNext == other._nPrev;
367     }
368     bool Includes( const SMDS_MeshNode* node ) const { return _nPrev == node || _nNext == node; }
369     static void GetSimplices( const SMDS_MeshNode* node,
370                               vector<_Simplex>&   simplices,
371                               const set<TGeomID>& ingnoreShapes,
372                               const _SolidData*   dataToCheckOri = 0,
373                               const bool          toSort = false);
374     static void SortSimplices(vector<_Simplex>& simplices);
375   };
376   //--------------------------------------------------------------------------------
377   /*!
378    * Structure used to take into account surface curvature while smoothing
379    */
380   struct _Curvature
381   {
382     double   _r; // radius
383     double   _k; // factor to correct node smoothed position
384     double   _h2lenRatio; // avgNormProj / (2*avgDist)
385     gp_Pnt2d _uv; // UV used in putOnOffsetSurface()
386   public:
387     static _Curvature* New( double avgNormProj, double avgDist );
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   struct _Mapper2D;
398   typedef map< const SMDS_MeshNode*, _LayerEdge*, TIDCompare > TNode2Edge;
399
400   //--------------------------------------------------------------------------------
401   /*!
402    * \brief Edge normal to surface, connecting a node on solid surface (_nodes[0])
403    * and a node of the most internal layer (_nodes.back())
404    */
405   struct _LayerEdge
406   {
407     typedef gp_XYZ (_LayerEdge::*PSmooFun)();
408
409     vector< const SMDS_MeshNode*> _nodes;
410
411     gp_XYZ              _normal;    // to boundary of solid
412     vector<gp_XYZ>      _pos;       // points computed during inflation
413     double              _len;       // length achieved with the last inflation step
414     double              _maxLen;    // maximal possible length
415     double              _cosin;     // of angle (_normal ^ surface)
416     double              _minAngle;  // of _simplices
417     double              _lenFactor; // to compute _len taking _cosin into account
418     int                 _flags;
419
420     // simplices connected to the source node (_nodes[0]);
421     // used for smoothing and quality check of _LayerEdge's based on the FACE
422     vector<_Simplex>    _simplices;
423     vector<_LayerEdge*> _neibors; // all surrounding _LayerEdge's
424     PSmooFun            _smooFunction; // smoothing function
425     _Curvature*         _curvature;
426     // data for smoothing of _LayerEdge's based on the EDGE
427     _2NearEdges*        _2neibors;
428
429     enum EFlags { TO_SMOOTH       = 0x0000001,
430                   MOVED           = 0x0000002, // set by _neibors[i]->SetNewLength()
431                   SMOOTHED        = 0x0000004, // set by _LayerEdge::Smooth()
432                   DIFFICULT       = 0x0000008, // near concave VERTEX
433                   ON_CONCAVE_FACE = 0x0000010,
434                   BLOCKED         = 0x0000020, // not to inflate any more
435                   INTERSECTED     = 0x0000040, // close intersection with a face found
436                   NORMAL_UPDATED  = 0x0000080,
437                   UPD_NORMAL_CONV = 0x0000100, // to update normal on boundary of concave FACE
438                   MARKED          = 0x0000200, // local usage
439                   MULTI_NORMAL    = 0x0000400, // a normal is invisible by some of surrounding faces
440                   NEAR_BOUNDARY   = 0x0000800, // is near FACE boundary forcing smooth
441                   SMOOTHED_C1     = 0x0001000, // is on _eosC1
442                   DISTORTED       = 0x0002000, // was bad before smoothing
443                   RISKY_SWOL      = 0x0004000, // SWOL is parallel to a source FACE
444                   SHRUNK          = 0x0008000, // target node reached a tgt position while shrink()
445                   UNUSED_FLAG     = 0x0100000  // to add user flags after
446     };
447     bool Is   ( int flag ) const { return _flags & flag; }
448     void Set  ( int flag ) { _flags |= flag; }
449     void Unset( int flag ) { _flags &= ~flag; }
450     std::string DumpFlags() const; // debug
451
452     void SetNewLength( double len, _EdgesOnShape& eos, SMESH_MesherHelper& helper );
453     bool SetNewLength2d( Handle(Geom_Surface)& surface,
454                          const TopoDS_Face&    F,
455                          _EdgesOnShape&        eos,
456                          SMESH_MesherHelper&   helper );
457     bool UpdatePositionOnSWOL( SMDS_MeshNode*      n,
458                                double              tol,
459                                _EdgesOnShape&      eos,
460                                SMESH_MesherHelper& helper );
461     void SetDataByNeighbors( const SMDS_MeshNode* n1,
462                              const SMDS_MeshNode* n2,
463                              const _EdgesOnShape& eos,
464                              SMESH_MesherHelper&  helper);
465     void Block( _SolidData& data );
466     void InvalidateStep( size_t curStep, const _EdgesOnShape& eos, bool restoreLength=false );
467     void ChooseSmooFunction(const set< TGeomID >& concaveVertices,
468                             const TNode2Edge&     n2eMap);
469     void SmoothPos( const vector< double >& segLen, const double tol );
470     int  GetSmoothedPos( const double tol );
471     int  Smooth(const int step, const bool isConcaveFace, bool findBest);
472     int  Smooth(const int step, bool findBest, vector< _LayerEdge* >& toSmooth );
473     int  CheckNeiborsOnBoundary(vector< _LayerEdge* >* badNeibors = 0, bool * needSmooth = 0 );
474     void SmoothWoCheck();
475     bool SmoothOnEdge(Handle(ShapeAnalysis_Surface)& surface,
476                       const TopoDS_Face&             F,
477                       SMESH_MesherHelper&            helper);
478     void MoveNearConcaVer( const _EdgesOnShape*    eov,
479                            const _EdgesOnShape*    eos,
480                            const int               step,
481                            vector< _LayerEdge* > & badSmooEdges);
482     bool FindIntersection( SMESH_ElementSearcher&   searcher,
483                            double &                 distance,
484                            const double&            epsilon,
485                            _EdgesOnShape&           eos,
486                            const SMDS_MeshElement** face = 0);
487     bool SegTriaInter( const gp_Ax1&        lastSegment,
488                        const gp_XYZ&        p0,
489                        const gp_XYZ&        p1,
490                        const gp_XYZ&        p2,
491                        double&              dist,
492                        const double&        epsilon) const;
493     bool SegTriaInter( const gp_Ax1&        lastSegment,
494                        const SMDS_MeshNode* n0,
495                        const SMDS_MeshNode* n1,
496                        const SMDS_MeshNode* n2,
497                        double&              dist,
498                        const double&        epsilon) const
499     { return SegTriaInter( lastSegment,
500                            SMESH_TNodeXYZ( n0 ), SMESH_TNodeXYZ( n1 ), SMESH_TNodeXYZ( n2 ),
501                            dist, epsilon );
502     }
503     const gp_XYZ& PrevPos() const { return _pos[ _pos.size() - 2 ]; }
504     gp_XYZ PrevCheckPos( _EdgesOnShape* eos=0 ) const;
505     gp_Ax1 LastSegment(double& segLen, _EdgesOnShape& eos) const;
506     gp_XY  LastUV( const TopoDS_Face& F, _EdgesOnShape& eos, int which=-1 ) const;
507     bool   IsOnEdge() const { return _2neibors; }
508     bool   IsOnFace() const { return ( _nodes[0]->GetPosition()->GetDim() == 2 ); }
509     int    BaseShapeDim() const { return _nodes[0]->GetPosition()->GetDim(); }
510     gp_XYZ Copy( _LayerEdge& other, _EdgesOnShape& eos, SMESH_MesherHelper& helper );
511     double SetCosin( double cosin );
512     void   SetNormal( const gp_XYZ& n ) { _normal = n; }
513     void   SetMaxLen( double l ) { _maxLen = l; }
514     int    NbSteps() const { return _pos.size() - 1; } // nb inlation steps
515     bool   IsNeiborOnEdge( const _LayerEdge* edge ) const;
516     void   SetSmooLen( double len ) { // set _len at which smoothing is needed
517       _cosin = len; // as for _LayerEdge's on FACE _cosin is not used
518     }
519     double GetSmooLen() { return _cosin; } // for _LayerEdge's on FACE _cosin is not used
520
521     gp_XYZ smoothLaplacian();
522     gp_XYZ smoothAngular();
523     gp_XYZ smoothLengthWeighted();
524     gp_XYZ smoothCentroidal();
525     gp_XYZ smoothNefPolygon();
526
527     enum { FUN_LAPLACIAN, FUN_LENWEIGHTED, FUN_CENTROIDAL, FUN_NEFPOLY, FUN_ANGULAR, FUN_NB };
528     static const int theNbSmooFuns = FUN_NB;
529     static PSmooFun _funs[theNbSmooFuns];
530     static const char* _funNames[theNbSmooFuns+1];
531     int smooFunID( PSmooFun fun=0) const;
532   };
533   _LayerEdge::PSmooFun _LayerEdge::_funs[theNbSmooFuns] = { &_LayerEdge::smoothLaplacian,
534                                                             &_LayerEdge::smoothLengthWeighted,
535                                                             &_LayerEdge::smoothCentroidal,
536                                                             &_LayerEdge::smoothNefPolygon,
537                                                             &_LayerEdge::smoothAngular };
538   const char* _LayerEdge::_funNames[theNbSmooFuns+1] = { "Laplacian",
539                                                          "LengthWeighted",
540                                                          "Centroidal",
541                                                          "NefPolygon",
542                                                          "Angular",
543                                                          "None"};
544   struct _LayerEdgeCmp
545   {
546     bool operator () (const _LayerEdge* e1, const _LayerEdge* e2) const
547     {
548       const bool cmpNodes = ( e1 && e2 && e1->_nodes.size() && e2->_nodes.size() );
549       return cmpNodes ? ( e1->_nodes[0]->GetID() < e2->_nodes[0]->GetID()) : ( e1 < e2 );
550     }
551   };
552   //--------------------------------------------------------------------------------
553   /*!
554    * A 2D half plane used by _LayerEdge::smoothNefPolygon()
555    */
556   struct _halfPlane
557   {
558     gp_XY _pos, _dir, _inNorm;
559     bool IsOut( const gp_XY p, const double tol ) const
560     {
561       return _inNorm * ( p - _pos ) < -tol;
562     }
563     bool FindIntersection( const _halfPlane& hp, gp_XY & intPnt )
564     {
565       //const double eps = 1e-10;
566       double D = _dir.Crossed( hp._dir );
567       if ( fabs(D) < std::numeric_limits<double>::min())
568         return false;
569       gp_XY vec21 = _pos - hp._pos; 
570       double u = hp._dir.Crossed( vec21 ) / D; 
571       intPnt = _pos + _dir * u;
572       return true;
573     }
574   };
575   //--------------------------------------------------------------------------------
576   /*!
577    * Structure used to smooth a _LayerEdge based on an EDGE.
578    */
579   struct _2NearEdges
580   {
581     double               _wgt  [2]; // weights of _nodes
582     _LayerEdge*          _edges[2];
583
584      // normal to plane passing through _LayerEdge._normal and tangent of EDGE
585     gp_XYZ*              _plnNorm;
586
587     _2NearEdges() { _edges[0]=_edges[1]=0; _plnNorm = 0; }
588     ~_2NearEdges(){ delete _plnNorm; }
589     const SMDS_MeshNode* tgtNode(bool is2nd) {
590       return _edges[is2nd] ? _edges[is2nd]->_nodes.back() : 0;
591     }
592     const SMDS_MeshNode* srcNode(bool is2nd) {
593       return _edges[is2nd] ? _edges[is2nd]->_nodes[0] : 0;
594     }
595     void reverse() {
596       std::swap( _wgt  [0], _wgt  [1] );
597       std::swap( _edges[0], _edges[1] );
598     }
599     void set( _LayerEdge* e1, _LayerEdge* e2, double w1, double w2 ) {
600       _edges[0] = e1; _edges[1] = e2; _wgt[0] = w1; _wgt[1] = w2;
601     }
602     bool include( const _LayerEdge* e ) {
603       return ( _edges[0] == e || _edges[1] == e );
604     }
605   };
606
607
608   //--------------------------------------------------------------------------------
609   /*!
610    * \brief Layers parameters got by averaging several hypotheses
611    */
612   struct AverageHyp
613   {
614     AverageHyp( const StdMeshers_ViscousLayers* hyp = 0 )
615       :_nbLayers(0), _nbHyps(0), _method(0), _thickness(0), _stretchFactor(0)
616     {
617       Add( hyp );
618     }
619     void Add( const StdMeshers_ViscousLayers* hyp )
620     {
621       if ( hyp )
622       {
623         _nbHyps++;
624         _nbLayers       = hyp->GetNumberLayers();
625         //_thickness     += hyp->GetTotalThickness();
626         _thickness      = Max( _thickness, hyp->GetTotalThickness() );
627         _stretchFactor += hyp->GetStretchFactor();
628         _method         = hyp->GetMethod();
629         if ( _groupName.empty() )
630           _groupName = hyp->GetGroupName();
631       }
632     }
633     double GetTotalThickness() const { return _thickness; /*_nbHyps ? _thickness / _nbHyps : 0;*/ }
634     double GetStretchFactor()  const { return _nbHyps ? _stretchFactor / _nbHyps : 0; }
635     int    GetNumberLayers()   const { return _nbLayers; }
636     int    GetMethod()         const { return _method; }
637     bool   ToCreateGroup()     const { return !_groupName.empty(); }
638     const std::string& GetGroupName() const { return _groupName; }
639
640     double Get1stLayerThickness( double realThickness = 0.) const
641     {
642       const double T = ( realThickness > 0 ) ? realThickness : GetTotalThickness();
643       const double f = GetStretchFactor();
644       const int    N = GetNumberLayers();
645       const double fPowN = pow( f, N );
646       double h0;
647       if ( fPowN - 1 <= numeric_limits<double>::min() )
648         h0 = T / N;
649       else
650         h0 = T * ( f - 1 )/( fPowN - 1 );
651       return h0;
652     }
653
654     bool   UseSurfaceNormal()  const
655     { return _method == StdMeshers_ViscousLayers::SURF_OFFSET_SMOOTH; }
656     bool   ToSmooth()          const
657     { return _method == StdMeshers_ViscousLayers::SURF_OFFSET_SMOOTH; }
658     bool   IsOffsetMethod()    const
659     { return _method == StdMeshers_ViscousLayers::FACE_OFFSET; }
660
661     bool operator==( const AverageHyp& other ) const
662     {
663       return ( _nbLayers == other._nbLayers &&
664                _method   == other._method   &&
665                Equals( GetTotalThickness(), other.GetTotalThickness() ) &&
666                Equals( GetStretchFactor(), other.GetStretchFactor() ));
667     }
668     static bool Equals( double v1, double v2 ) { return Abs( v1 - v2 ) < 0.01 * ( v1 + v2 ); }
669
670   private:
671     int         _nbLayers, _nbHyps, _method;
672     double      _thickness, _stretchFactor;
673     std::string _groupName;
674   };
675
676   //--------------------------------------------------------------------------------
677   /*!
678    * \brief _LayerEdge's on a shape and other shape data
679    */
680   struct _EdgesOnShape
681   {
682     vector< _LayerEdge* > _edges;
683
684     TopoDS_Shape          _shape;
685     TGeomID               _shapeID;
686     SMESH_subMesh *       _subMesh;
687     // face or edge w/o layer along or near which _edges are inflated
688     TopoDS_Shape          _sWOL;
689     bool                  _isRegularSWOL; // w/o singularities
690     // averaged StdMeshers_ViscousLayers parameters
691     AverageHyp            _hyp;
692     bool                  _toSmooth;
693     _Smoother1D*          _edgeSmoother;
694     vector< _EdgesOnShape* > _eosConcaVer; // edges at concave VERTEXes of a FACE
695     vector< _EdgesOnShape* > _eosC1; // to smooth together several C1 continues shapes
696
697     typedef std::unordered_map< const SMDS_MeshElement*, gp_XYZ > TFace2NormMap;
698     TFace2NormMap            _faceNormals; // if _shape is FACE
699     vector< _EdgesOnShape* > _faceEOS; // to get _faceNormals of adjacent FACEs
700
701     Handle(ShapeAnalysis_Surface) _offsetSurf;
702     _LayerEdge*                   _edgeForOffset;
703     double                        _offsetValue;
704     _Mapper2D*                    _mapper2D;
705
706     _SolidData*            _data; // parent SOLID
707
708     _LayerEdge*      operator[](size_t i) const { return (_LayerEdge*) _edges[i]; }
709     size_t           size() const { return _edges.size(); }
710     TopAbs_ShapeEnum ShapeType() const
711     { return _shape.IsNull() ? TopAbs_SHAPE : _shape.ShapeType(); }
712     TopAbs_ShapeEnum SWOLType() const
713     { return _sWOL.IsNull() ? TopAbs_SHAPE : _sWOL.ShapeType(); }
714     bool             HasC1( const _EdgesOnShape* other ) const
715     { return std::find( _eosC1.begin(), _eosC1.end(), other ) != _eosC1.end(); }
716     bool             GetNormal( const SMDS_MeshElement* face, gp_Vec& norm );
717     _SolidData&      GetData() const { return *_data; }
718     char             ShapeTypeLetter() const
719     { switch ( ShapeType() ) { case TopAbs_FACE: return 'F'; case TopAbs_EDGE: return 'E';
720       case TopAbs_VERTEX: return 'V'; default: return 'S'; }}
721
722     _EdgesOnShape(): _shapeID(-1), _subMesh(0), _toSmooth(false), _edgeSmoother(0), _mapper2D(0) {}
723     ~_EdgesOnShape();
724   };
725
726   //--------------------------------------------------------------------------------
727   /*!
728    * \brief Convex FACE whose radius of curvature is less than the thickness of
729    *        layers. It is used to detect distortion of prisms based on a convex
730    *        FACE and to update normals to enable further increasing the thickness
731    */
732   struct _ConvexFace
733   {
734     TopoDS_Face                     _face;
735
736     // edges whose _simplices are used to detect prism distortion
737     vector< _LayerEdge* >           _simplexTestEdges;
738
739     // map a sub-shape to _SolidData::_edgesOnShape
740     map< TGeomID, _EdgesOnShape* >  _subIdToEOS;
741
742     bool                            _isTooCurved;
743     bool                            _normalsFixed;
744     bool                            _normalsFixedOnBorders; // used in putOnOffsetSurface()
745
746     double GetMaxCurvature( _SolidData&         data,
747                             _EdgesOnShape&      eof,
748                             BRepLProp_SLProps&  surfProp,
749                             SMESH_MesherHelper& helper);
750
751     bool GetCenterOfCurvature( _LayerEdge*         ledge,
752                                BRepLProp_SLProps&  surfProp,
753                                SMESH_MesherHelper& helper,
754                                gp_Pnt &            center ) const;
755     bool CheckPrisms() const;
756   };
757
758   //--------------------------------------------------------------------------------
759   /*!
760    * \brief Structure holding _LayerEdge's based on EDGEs that will collide
761    *        at inflation up to the full thickness. A detected collision
762    *        is fixed in updateNormals()
763    */
764   struct _CollisionEdges
765   {
766     _LayerEdge*           _edge;
767     vector< _LayerEdge* > _intEdges; // each pair forms an intersected quadrangle
768     const SMDS_MeshNode* nSrc(int i) const { return _intEdges[i]->_nodes[0]; }
769     const SMDS_MeshNode* nTgt(int i) const { return _intEdges[i]->_nodes.back(); }
770   };
771
772   //--------------------------------------------------------------------------------
773   /*!
774    * \brief Data of a SOLID
775    */
776   struct _SolidData
777   {
778     typedef const StdMeshers_ViscousLayers* THyp;
779     TopoDS_Shape                    _solid;
780     TopTools_MapOfShape             _before; // SOLIDs to be computed before _solid
781     TGeomID                         _index; // SOLID id
782     _MeshOfSolid*                   _proxyMesh;
783     bool                            _done;
784     list< THyp >                    _hyps;
785     list< TopoDS_Shape >            _hypShapes;
786     map< TGeomID, THyp >            _face2hyp; // filled if _hyps.size() > 1
787     set< TGeomID >                  _reversedFaceIds;
788     set< TGeomID >                  _ignoreFaceIds; // WOL FACEs and FACEs of other SOLIDs
789
790     double                          _stepSize, _stepSizeCoeff, _geomSize;
791     const SMDS_MeshNode*            _stepSizeNodes[2];
792
793     TNode2Edge                      _n2eMap; // nodes and _LayerEdge's based on them
794
795     // map to find _n2eMap of another _SolidData by a shrink shape shared by two _SolidData's
796     map< TGeomID, TNode2Edge* >     _s2neMap;
797     // _LayerEdge's with underlying shapes
798     vector< _EdgesOnShape >         _edgesOnShape;
799
800     // key:   an ID of shape (EDGE or VERTEX) shared by a FACE with
801     //        layers and a FACE w/o layers
802     // value: the shape (FACE or EDGE) to shrink mesh on.
803     //       _LayerEdge's basing on nodes on key shape are inflated along the value shape
804     map< TGeomID, TopoDS_Shape >     _shrinkShape2Shape;
805
806     // Convex FACEs whose radius of curvature is less than the thickness of layers
807     map< TGeomID, _ConvexFace >      _convexFaces;
808
809     // shapes (EDGEs and VERTEXes) shrink from which is forbidden due to collisions with
810     // the adjacent SOLID
811     set< TGeomID >                   _noShrinkShapes;
812
813     int                              _nbShapesToSmooth;
814
815     vector< _CollisionEdges >        _collisionEdges;
816     set< TGeomID >                   _concaveFaces;
817
818     double                           _maxThickness; // of all _hyps
819     double                           _minThickness; // of all _hyps
820
821     double                           _epsilon; // precision for SegTriaInter()
822
823     SMESH_MesherHelper*              _helper;
824
825     _SolidData(const TopoDS_Shape& s=TopoDS_Shape(),
826                _MeshOfSolid*       m=0)
827       :_solid(s), _proxyMesh(m), _done(false),_helper(0) {}
828     ~_SolidData() { delete _helper; _helper = 0; }
829
830     void SortOnEdge( const TopoDS_Edge& E, vector< _LayerEdge* >& edges);
831     void Sort2NeiborsOnEdge( vector< _LayerEdge* >& edges );
832
833     _ConvexFace* GetConvexFace( const TGeomID faceID ) {
834       map< TGeomID, _ConvexFace >::iterator id2face = _convexFaces.find( faceID );
835       return id2face == _convexFaces.end() ? 0 : & id2face->second;
836     }
837     _EdgesOnShape* GetShapeEdges(const TGeomID       shapeID );
838     _EdgesOnShape* GetShapeEdges(const TopoDS_Shape& shape );
839     _EdgesOnShape* GetShapeEdges(const _LayerEdge*   edge )
840     { return GetShapeEdges( edge->_nodes[0]->getshapeId() ); }
841
842     SMESH_MesherHelper& GetHelper() const { return *_helper; }
843
844     void UnmarkEdges( int flag = _LayerEdge::MARKED ) {
845       for ( size_t i = 0; i < _edgesOnShape.size(); ++i )
846         for ( size_t j = 0; j < _edgesOnShape[i]._edges.size(); ++j )
847           _edgesOnShape[i]._edges[j]->Unset( flag );
848     }
849     void AddShapesToSmooth( const set< _EdgesOnShape* >& shape,
850                             const set< _EdgesOnShape* >* edgesNoAnaSmooth=0 );
851
852     void PrepareEdgesToSmoothOnFace( _EdgesOnShape* eof, bool substituteSrcNodes );
853   };
854   //--------------------------------------------------------------------------------
855   /*!
856    * \brief Offset plane used in getNormalByOffset()
857    */
858   struct _OffsetPlane
859   {
860     gp_Pln _plane;
861     int    _faceIndex;
862     int    _faceIndexNext[2];
863     gp_Lin _lines[2]; // line of intersection with neighbor _OffsetPlane's
864     bool   _isLineOK[2];
865     _OffsetPlane() {
866       _isLineOK[0] = _isLineOK[1] = false; _faceIndexNext[0] = _faceIndexNext[1] = -1;
867     }
868     void   ComputeIntersectionLine( _OffsetPlane&        pln, 
869                                     const TopoDS_Edge&   E,
870                                     const TopoDS_Vertex& V );
871     gp_XYZ GetCommonPoint(bool& isFound, const TopoDS_Vertex& V) const;
872     int    NbLines() const { return _isLineOK[0] + _isLineOK[1]; }
873   };
874   //--------------------------------------------------------------------------------
875   /*!
876    * \brief Container of centers of curvature at nodes on an EDGE bounding _ConvexFace
877    */
878   struct _CentralCurveOnEdge
879   {
880     bool                  _isDegenerated;
881     vector< gp_Pnt >      _curvaCenters;
882     vector< _LayerEdge* > _ledges;
883     vector< gp_XYZ >      _normals; // new normal for each of _ledges
884     vector< double >      _segLength2;
885
886     TopoDS_Edge           _edge;
887     TopoDS_Face           _adjFace;
888     bool                  _adjFaceToSmooth;
889
890     void Append( const gp_Pnt& center, _LayerEdge* ledge )
891     {
892       if ( ledge->Is( _LayerEdge::MULTI_NORMAL ))
893         return;
894       if ( _curvaCenters.size() > 0 )
895         _segLength2.push_back( center.SquareDistance( _curvaCenters.back() ));
896       _curvaCenters.push_back( center );
897       _ledges.push_back( ledge );
898       _normals.push_back( ledge->_normal );
899     }
900     bool FindNewNormal( const gp_Pnt& center, gp_XYZ& newNormal );
901     void SetShapes( const TopoDS_Edge&  edge,
902                     const _ConvexFace&  convFace,
903                     _SolidData&         data,
904                     SMESH_MesherHelper& helper);
905   };
906   //--------------------------------------------------------------------------------
907   /*!
908    * \brief Data of node on a shrinked FACE
909    */
910   struct _SmoothNode
911   {
912     const SMDS_MeshNode*         _node;
913     vector<_Simplex>             _simplices; // for quality check
914
915     enum SmoothType { LAPLACIAN, CENTROIDAL, ANGULAR, TFI };
916
917     bool Smooth(int&                  badNb,
918                 Handle(Geom_Surface)& surface,
919                 SMESH_MesherHelper&   helper,
920                 const double          refSign,
921                 SmoothType            how,
922                 bool                  set3D);
923
924     gp_XY computeAngularPos(vector<gp_XY>& uv,
925                             const gp_XY&   uvToFix,
926                             const double   refSign );
927   };
928   struct PyDump;
929   struct Periodicity;
930   //--------------------------------------------------------------------------------
931   /*!
932    * \brief Builder of viscous layers
933    */
934   class _ViscousBuilder
935   {
936   public:
937     _ViscousBuilder();
938     // does it's job
939     SMESH_ComputeErrorPtr Compute(SMESH_Mesh&         mesh,
940                                   const TopoDS_Shape& shape);
941     // check validity of hypotheses
942     SMESH_ComputeErrorPtr CheckHypotheses( SMESH_Mesh&         mesh,
943                                            const TopoDS_Shape& shape );
944
945     // restore event listeners used to clear an inferior dim sub-mesh modified by viscous layers
946     void RestoreListeners();
947
948     // computes SMESH_ProxyMesh::SubMesh::_n2n;
949     bool MakeN2NMap( _MeshOfSolid* pm );
950
951   private:
952
953     bool findSolidsWithLayers(const bool checkFaceMesh=true);
954     bool setBefore( _SolidData& solidBefore, _SolidData& solidAfter );
955     bool findFacesWithLayers(const bool onlyWith=false);
956     void findPeriodicFaces();
957     void getIgnoreFaces(const TopoDS_Shape&             solid,
958                         const StdMeshers_ViscousLayers* hyp,
959                         const TopoDS_Shape&             hypShape,
960                         set<TGeomID>&                   ignoreFaces);
961     int makeEdgesOnShape();
962     bool makeLayer(_SolidData& data);
963     void setShapeData( _EdgesOnShape& eos, SMESH_subMesh* sm, _SolidData& data );
964     bool setEdgeData( _LayerEdge& edge, _EdgesOnShape& eos,
965                       SMESH_MesherHelper& helper, _SolidData& data);
966     gp_XYZ getFaceNormal(const SMDS_MeshNode* n,
967                          const TopoDS_Face&   face,
968                          SMESH_MesherHelper&  helper,
969                          bool&                isOK,
970                          bool                 shiftInside=false);
971     bool getFaceNormalAtSingularity(const gp_XY&        uv,
972                                     const TopoDS_Face&  face,
973                                     SMESH_MesherHelper& helper,
974                                     gp_Dir&             normal );
975     gp_XYZ getWeigthedNormal( const _LayerEdge*                edge );
976     gp_XYZ getNormalByOffset( _LayerEdge*                      edge,
977                               std::pair< TopoDS_Face, gp_XYZ > fId2Normal[],
978                               int                              nbFaces,
979                               bool                             lastNoOffset = false);
980     bool findNeiborsOnEdge(const _LayerEdge*     edge,
981                            const SMDS_MeshNode*& n1,
982                            const SMDS_MeshNode*& n2,
983                            _EdgesOnShape&        eos,
984                            _SolidData&           data);
985     void findSimplexTestEdges( _SolidData&                    data,
986                                vector< vector<_LayerEdge*> >& edgesByGeom);
987     void computeGeomSize( _SolidData& data );
988     bool findShapesToSmooth( _SolidData& data);
989     void limitStepSizeByCurvature( _SolidData&  data );
990     void limitStepSize( _SolidData&             data,
991                         const SMDS_MeshElement* face,
992                         const _LayerEdge*       maxCosinEdge );
993     void limitStepSize( _SolidData& data, const double minSize);
994     bool inflate(_SolidData& data);
995     bool smoothAndCheck(_SolidData& data, const int nbSteps, double & distToIntersection);
996     int  invalidateBadSmooth( _SolidData&               data,
997                               SMESH_MesherHelper&       helper,
998                               vector< _LayerEdge* >&    badSmooEdges,
999                               vector< _EdgesOnShape* >& eosC1,
1000                               const int                 infStep );
1001     void makeOffsetSurface( _EdgesOnShape& eos, SMESH_MesherHelper& );
1002     void putOnOffsetSurface( _EdgesOnShape& eos, int infStep,
1003                              vector< _EdgesOnShape* >& eosC1,
1004                              int smooStep=0, int moveAll=false );
1005     void findCollisionEdges( _SolidData& data, SMESH_MesherHelper& helper );
1006     void findEdgesToUpdateNormalNearConvexFace( _ConvexFace &       convFace,
1007                                                 _SolidData&         data,
1008                                                 SMESH_MesherHelper& helper );
1009     void limitMaxLenByCurvature( _SolidData& data, SMESH_MesherHelper& helper );
1010     void limitMaxLenByCurvature( _LayerEdge* e1, _LayerEdge* e2,
1011                                  _EdgesOnShape& eos1, _EdgesOnShape& eos2,
1012                                  const bool isSmoothable );
1013     bool updateNormals( _SolidData& data, SMESH_MesherHelper& helper, int stepNb, double stepSize );
1014     bool updateNormalsOfConvexFaces( _SolidData&         data,
1015                                      SMESH_MesherHelper& helper,
1016                                      int                 stepNb );
1017     void updateNormalsOfC1Vertices( _SolidData& data );
1018     bool updateNormalsOfSmoothed( _SolidData&         data,
1019                                   SMESH_MesherHelper& helper,
1020                                   const int           nbSteps,
1021                                   const double        stepSize );
1022     bool isNewNormalOk( _SolidData&   data,
1023                         _LayerEdge&   edge,
1024                         const gp_XYZ& newNormal);
1025     bool refine(_SolidData& data);
1026     bool shrink(_SolidData& data);
1027     bool prepareEdgeToShrink( _LayerEdge& edge, _EdgesOnShape& eos,
1028                               SMESH_MesherHelper& helper,
1029                               const SMESHDS_SubMesh* faceSubMesh );
1030     void restoreNoShrink( _LayerEdge& edge ) const;
1031     void fixBadFaces(const TopoDS_Face&          F,
1032                      SMESH_MesherHelper&         helper,
1033                      const bool                  is2D,
1034                      const int                   step,
1035                      set<const SMDS_MeshNode*> * involvedNodes=NULL);
1036     bool addBoundaryElements(_SolidData& data);
1037
1038     bool error( const string& text, int solidID=-1 );
1039     SMESHDS_Mesh* getMeshDS() const { return _mesh->GetMeshDS(); }
1040
1041     // debug
1042     void makeGroupOfLE();
1043
1044     SMESH_Mesh*                  _mesh;
1045     SMESH_ComputeErrorPtr        _error;
1046
1047     vector<                      _SolidData >  _sdVec;
1048     TopTools_IndexedMapOfShape   _solids; // to find _SolidData by a solid
1049     TopTools_MapOfShape          _shrunkFaces;
1050     std::unique_ptr<Periodicity> _periodicity;
1051
1052     int                          _tmpFaceID;
1053     PyDump*                      _pyDump;
1054   };
1055   //--------------------------------------------------------------------------------
1056   /*!
1057    * \brief Shrinker of nodes on the EDGE
1058    */
1059   class _Shrinker1D
1060   {
1061     TopoDS_Edge                   _geomEdge;
1062     vector<double>                _initU;
1063     vector<double>                _normPar;
1064     vector<const SMDS_MeshNode*>  _nodes;
1065     const _LayerEdge*             _edges[2];
1066     bool                          _done;
1067   public:
1068     void AddEdge( const _LayerEdge* e, _EdgesOnShape& eos, SMESH_MesherHelper& helper );
1069     void Compute(bool set3D, SMESH_MesherHelper& helper);
1070     void RestoreParams();
1071     void SwapSrcTgtNodes(SMESHDS_Mesh* mesh);
1072     const TopoDS_Edge& GeomEdge() const { return _geomEdge; }
1073     const SMDS_MeshNode* TgtNode( bool is2nd ) const
1074     { return _edges[is2nd] ? _edges[is2nd]->_nodes.back() : 0; }
1075     const SMDS_MeshNode* SrcNode( bool is2nd ) const
1076     { return _edges[is2nd] ? _edges[is2nd]->_nodes[0] : 0; }
1077   };
1078   //--------------------------------------------------------------------------------
1079   /*!
1080    * \brief Smoother of _LayerEdge's on EDGE.
1081    */
1082   struct _Smoother1D
1083   {
1084     struct OffPnt // point of the offsetted EDGE
1085     {
1086       gp_XYZ      _xyz;    // coord of a point inflated from EDGE w/o smooth
1087       double      _len;    // length reached at previous inflation step
1088       double      _param;  // on EDGE
1089       _2NearEdges _2edges; // 2 neighbor _LayerEdge's
1090       gp_XYZ      _edgeDir;// EDGE tangent at _param
1091       double Distance( const OffPnt& p ) const { return ( _xyz - p._xyz ).Modulus(); }
1092     };
1093     vector< OffPnt >   _offPoints;
1094     vector< double >   _leParams; // normalized param of _eos._edges on EDGE
1095     Handle(Geom_Curve) _anaCurve; // for analytic smooth
1096     _LayerEdge         _leOnV[2]; // _LayerEdge's holding normal to the EDGE at VERTEXes
1097     gp_XYZ             _edgeDir[2]; // tangent at VERTEXes
1098     size_t             _iSeg[2];  // index of segment where extreme tgt node is projected
1099     _EdgesOnShape&     _eos;
1100     double             _curveLen; // length of the EDGE
1101     std::pair<int,int> _eToSmooth[2]; // <from,to> indices of _LayerEdge's in _eos
1102
1103     static Handle(Geom_Curve) CurveForSmooth( const TopoDS_Edge&  E,
1104                                               _EdgesOnShape&      eos,
1105                                               SMESH_MesherHelper& helper);
1106
1107     _Smoother1D( Handle(Geom_Curve) curveForSmooth,
1108                  _EdgesOnShape&     eos )
1109       : _anaCurve( curveForSmooth ), _eos( eos )
1110     {
1111     }
1112     bool Perform(_SolidData&                    data,
1113                  Handle(ShapeAnalysis_Surface)& surface,
1114                  const TopoDS_Face&             F,
1115                  SMESH_MesherHelper&            helper );
1116
1117     void prepare(_SolidData& data );
1118
1119     void findEdgesToSmooth();
1120
1121     bool isToSmooth( int iE );
1122
1123     bool smoothAnalyticEdge( _SolidData&                    data,
1124                              Handle(ShapeAnalysis_Surface)& surface,
1125                              const TopoDS_Face&             F,
1126                              SMESH_MesherHelper&            helper);
1127     bool smoothComplexEdge( _SolidData&                     data,
1128                             Handle(ShapeAnalysis_Surface)& surface,
1129                             const TopoDS_Face&             F,
1130                             SMESH_MesherHelper&            helper);
1131     gp_XYZ getNormalNormal( const gp_XYZ & normal,
1132                             const gp_XYZ&  edgeDir);
1133     _LayerEdge* getLEdgeOnV( bool is2nd )
1134     {
1135       return _eos._edges[ is2nd ? _eos._edges.size()-1 : 0 ]->_2neibors->_edges[ is2nd ];
1136     }
1137     bool isAnalytic() const { return !_anaCurve.IsNull(); }
1138
1139     void offPointsToPython() const; // debug
1140   };
1141
1142   //--------------------------------------------------------------------------------
1143   /*!
1144    * \brief Compute positions of nodes of 2D structured mesh using TFI
1145    */
1146   class _Mapper2D
1147   {
1148     FaceQuadStruct _quadPoints;
1149
1150     UVPtStruct& uvPnt( size_t i, size_t j ) { return _quadPoints.UVPt( i, j ); }
1151
1152   public:
1153     _Mapper2D( const TParam2ColumnMap & param2ColumnMap, const TNode2Edge& n2eMap );
1154     bool ComputeNodePositions();
1155   };
1156
1157   //--------------------------------------------------------------------------------
1158   /*!
1159    * \brief Class of temporary mesh face.
1160    * We can't use SMDS_FaceOfNodes since it's impossible to set it's ID which is
1161    * needed because SMESH_ElementSearcher internally uses set of elements sorted by ID
1162    */
1163   struct _TmpMeshFace : public SMDS_PolygonalFaceOfNodes
1164   {
1165     const SMDS_MeshElement* _srcFace;
1166
1167     _TmpMeshFace( const vector<const SMDS_MeshNode*>& nodes,
1168                   int                                 ID,
1169                   int                                 faceID=-1,
1170                   const SMDS_MeshElement*             srcFace=0 ):
1171       SMDS_PolygonalFaceOfNodes(nodes), _srcFace( srcFace ) { setID( ID ); setShapeID( faceID ); }
1172     virtual SMDSAbs_EntityType  GetEntityType() const
1173     { return _srcFace ? _srcFace->GetEntityType() : SMDSEntity_Quadrangle; }
1174     virtual SMDSAbs_GeometryType GetGeomType()  const
1175     { return _srcFace ? _srcFace->GetGeomType() : SMDSGeom_QUADRANGLE; }
1176   };
1177   //--------------------------------------------------------------------------------
1178   /*!
1179    * \brief Class of temporary mesh quadrangle face storing _LayerEdge it's based on
1180    */
1181   struct _TmpMeshFaceOnEdge : public _TmpMeshFace
1182   {
1183     _LayerEdge *_le1, *_le2;
1184     _TmpMeshFaceOnEdge( _LayerEdge* le1, _LayerEdge* le2, int ID ):
1185       _TmpMeshFace( vector<const SMDS_MeshNode*>(4), ID ), _le1(le1), _le2(le2)
1186     {
1187       myNodes[0]=_le1->_nodes[0];
1188       myNodes[1]=_le1->_nodes.back();
1189       myNodes[2]=_le2->_nodes.back();
1190       myNodes[3]=_le2->_nodes[0];
1191     }
1192     const SMDS_MeshNode* n( size_t i ) const
1193     {
1194       return myNodes[ i ];
1195     }
1196     gp_XYZ GetDir() const // return average direction of _LayerEdge's, normal to EDGE
1197     {
1198       SMESH_TNodeXYZ p0s( myNodes[0] );
1199       SMESH_TNodeXYZ p0t( myNodes[1] );
1200       SMESH_TNodeXYZ p1t( myNodes[2] );
1201       SMESH_TNodeXYZ p1s( myNodes[3] );
1202       gp_XYZ  v0 = p0t - p0s;
1203       gp_XYZ  v1 = p1t - p1s;
1204       gp_XYZ v01 = p1s - p0s;
1205       gp_XYZ   n = ( v0 ^ v01 ) + ( v1 ^ v01 );
1206       gp_XYZ   d = v01 ^ n;
1207       d.Normalize();
1208       return d;
1209     }
1210     gp_XYZ GetDir(_LayerEdge* le1, _LayerEdge* le2) // return average direction of _LayerEdge's
1211     {
1212       myNodes[0]=le1->_nodes[0];
1213       myNodes[1]=le1->_nodes.back();
1214       myNodes[2]=le2->_nodes.back();
1215       myNodes[3]=le2->_nodes[0];
1216       return GetDir();
1217     }
1218   };
1219   //--------------------------------------------------------------------------------
1220   /*!
1221    * \brief Retriever of node coordinates either directly or from a surface by node UV.
1222    * \warning Location of a surface is ignored
1223    */
1224   struct _NodeCoordHelper
1225   {
1226     SMESH_MesherHelper&        _helper;
1227     const TopoDS_Face&         _face;
1228     Handle(Geom_Surface)       _surface;
1229     gp_XYZ (_NodeCoordHelper::* _fun)(const SMDS_MeshNode* n) const;
1230
1231     _NodeCoordHelper(const TopoDS_Face& F, SMESH_MesherHelper& helper, bool is2D)
1232       : _helper( helper ), _face( F )
1233     {
1234       if ( is2D )
1235       {
1236         TopLoc_Location loc;
1237         _surface = BRep_Tool::Surface( _face, loc );
1238       }
1239       if ( _surface.IsNull() )
1240         _fun = & _NodeCoordHelper::direct;
1241       else
1242         _fun = & _NodeCoordHelper::byUV;
1243     }
1244     gp_XYZ operator()(const SMDS_MeshNode* n) const { return (this->*_fun)( n ); }
1245
1246   private:
1247     gp_XYZ direct(const SMDS_MeshNode* n) const
1248     {
1249       return SMESH_TNodeXYZ( n );
1250     }
1251     gp_XYZ byUV  (const SMDS_MeshNode* n) const
1252     {
1253       gp_XY uv = _helper.GetNodeUV( _face, n );
1254       return _surface->Value( uv.X(), uv.Y() ).XYZ();
1255     }
1256   };
1257
1258   //================================================================================
1259   /*!
1260    * \brief Check angle between vectors 
1261    */
1262   //================================================================================
1263
1264   inline bool isLessAngle( const gp_Vec& v1, const gp_Vec& v2, const double cos )
1265   {
1266     double dot = v1 * v2; // cos * |v1| * |v2|
1267     double l1  = v1.SquareMagnitude();
1268     double l2  = v2.SquareMagnitude();
1269     return (( dot * cos >= 0 ) && 
1270             ( dot * dot ) / l1 / l2 >= ( cos * cos ));
1271   }
1272
1273   class _Factory
1274   {
1275     ObjectPool< _LayerEdge >  _edgePool;
1276     ObjectPool< _Curvature >  _curvaturePool;
1277     ObjectPool< _2NearEdges > _nearEdgesPool;
1278
1279     static _Factory* & me()
1280     {
1281       static _Factory* theFactory = 0;
1282       return theFactory;
1283     }
1284   public:
1285
1286     _Factory()  { me() = this; }
1287     ~_Factory() { me() = 0; }
1288
1289     static _LayerEdge*  NewLayerEdge() { return me()->_edgePool.getNew(); }
1290     static _Curvature * NewCurvature() { return me()->_curvaturePool.getNew(); }
1291     static _2NearEdges* NewNearEdges() { return me()->_nearEdgesPool.getNew(); }
1292   };
1293
1294 } // namespace VISCOUS_3D
1295
1296
1297
1298 //================================================================================
1299 // StdMeshers_ViscousLayers hypothesis
1300 //
1301 StdMeshers_ViscousLayers::StdMeshers_ViscousLayers(int hypId, SMESH_Gen* gen)
1302   :SMESH_Hypothesis(hypId, gen),
1303    _isToIgnoreShapes(1), _nbLayers(1), _thickness(1), _stretchFactor(1),
1304    _method( SURF_OFFSET_SMOOTH ),
1305    _groupName("")
1306 {
1307   _name = StdMeshers_ViscousLayers::GetHypType();
1308   _param_algo_dim = -3; // auxiliary hyp used by 3D algos
1309 } // --------------------------------------------------------------------------------
1310 void StdMeshers_ViscousLayers::SetBndShapes(const std::vector<int>& faceIds, bool toIgnore)
1311 {
1312   if ( faceIds != _shapeIds )
1313     _shapeIds = faceIds, NotifySubMeshesHypothesisModification();
1314   if ( _isToIgnoreShapes != toIgnore )
1315     _isToIgnoreShapes = toIgnore, NotifySubMeshesHypothesisModification();
1316 } // --------------------------------------------------------------------------------
1317 void StdMeshers_ViscousLayers::SetTotalThickness(double thickness)
1318 {
1319   if ( thickness != _thickness )
1320     _thickness = thickness, NotifySubMeshesHypothesisModification();
1321 } // --------------------------------------------------------------------------------
1322 void StdMeshers_ViscousLayers::SetNumberLayers(int nb)
1323 {
1324   if ( _nbLayers != nb )
1325     _nbLayers = nb, NotifySubMeshesHypothesisModification();
1326 } // --------------------------------------------------------------------------------
1327 void StdMeshers_ViscousLayers::SetStretchFactor(double factor)
1328 {
1329   if ( _stretchFactor != factor )
1330     _stretchFactor = factor, NotifySubMeshesHypothesisModification();
1331 } // --------------------------------------------------------------------------------
1332 void StdMeshers_ViscousLayers::SetMethod( ExtrusionMethod method )
1333 {
1334   if ( _method != method )
1335     _method = method, NotifySubMeshesHypothesisModification();
1336 } // --------------------------------------------------------------------------------
1337 void StdMeshers_ViscousLayers::SetGroupName(const std::string& name)
1338 {
1339   if ( _groupName != name )
1340   {
1341     _groupName = name;
1342     if ( !_groupName.empty() )
1343       NotifySubMeshesHypothesisModification();
1344   }
1345 } // --------------------------------------------------------------------------------
1346 SMESH_ProxyMesh::Ptr
1347 StdMeshers_ViscousLayers::Compute(SMESH_Mesh&         theMesh,
1348                                   const TopoDS_Shape& theShape,
1349                                   const bool          toMakeN2NMap) const
1350 {
1351   using namespace VISCOUS_3D;
1352   _ViscousBuilder builder;
1353   SMESH_ComputeErrorPtr err = builder.Compute( theMesh, theShape );
1354   if ( err && !err->IsOK() )
1355     return SMESH_ProxyMesh::Ptr();
1356
1357   vector<SMESH_ProxyMesh::Ptr> components;
1358   TopExp_Explorer exp( theShape, TopAbs_SOLID );
1359   for ( ; exp.More(); exp.Next() )
1360   {
1361     if ( _MeshOfSolid* pm =
1362          _ViscousListener::GetSolidMesh( &theMesh, exp.Current(), /*toCreate=*/false))
1363     {
1364       if ( toMakeN2NMap && !pm->_n2nMapComputed )
1365         if ( !builder.MakeN2NMap( pm ))
1366           return SMESH_ProxyMesh::Ptr();
1367       components.push_back( SMESH_ProxyMesh::Ptr( pm ));
1368       pm->myIsDeletable = false; // it will de deleted by boost::shared_ptr
1369
1370       if ( pm->_warning && !pm->_warning->IsOK() )
1371       {
1372         SMESH_subMesh* sm = theMesh.GetSubMesh( exp.Current() );
1373         SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1374         if ( !smError || smError->IsOK() )
1375           smError = pm->_warning;
1376       }
1377     }
1378     _ViscousListener::RemoveSolidMesh ( &theMesh, exp.Current() );
1379   }
1380   switch ( components.size() )
1381   {
1382   case 0: break;
1383
1384   case 1: return components[0];
1385
1386   default: return SMESH_ProxyMesh::Ptr( new SMESH_ProxyMesh( components ));
1387   }
1388   return SMESH_ProxyMesh::Ptr();
1389 } // --------------------------------------------------------------------------------
1390 std::ostream & StdMeshers_ViscousLayers::SaveTo(std::ostream & save)
1391 {
1392   save << " " << _nbLayers
1393        << " " << _thickness
1394        << " " << _stretchFactor
1395        << " " << _shapeIds.size();
1396   for ( size_t i = 0; i < _shapeIds.size(); ++i )
1397     save << " " << _shapeIds[i];
1398   save << " " << !_isToIgnoreShapes; // negate to keep the behavior in old studies.
1399   save << " " << _method;
1400   save << " " << _groupName.size();
1401   if ( !_groupName.empty() )
1402     save << " " << _groupName;
1403   return save;
1404 } // --------------------------------------------------------------------------------
1405 std::istream & StdMeshers_ViscousLayers::LoadFrom(std::istream & load)
1406 {
1407   int nbFaces, faceID, shapeToTreat, method;
1408   load >> _nbLayers >> _thickness >> _stretchFactor >> nbFaces;
1409   while ( (int) _shapeIds.size() < nbFaces && load >> faceID )
1410     _shapeIds.push_back( faceID );
1411   if ( load >> shapeToTreat ) {
1412     _isToIgnoreShapes = !shapeToTreat;
1413     if ( load >> method )
1414       _method = (ExtrusionMethod) method;
1415     int nameSize = 0;
1416     if ( load >> nameSize && nameSize > 0 )
1417     {
1418       _groupName.resize( nameSize );
1419       load.get( _groupName[0] ); // remove a white-space
1420       load.getline( &_groupName[0], nameSize + 1 );
1421     }
1422   }
1423   else {
1424     _isToIgnoreShapes = true; // old behavior
1425   }
1426   return load;
1427 } // --------------------------------------------------------------------------------
1428 bool StdMeshers_ViscousLayers::SetParametersByMesh(const SMESH_Mesh*   /*theMesh*/,
1429                                                    const TopoDS_Shape& /*theShape*/)
1430 {
1431   // TODO
1432   return false;
1433 } // --------------------------------------------------------------------------------
1434 SMESH_ComputeErrorPtr
1435 StdMeshers_ViscousLayers::CheckHypothesis(SMESH_Mesh&                          theMesh,
1436                                           const TopoDS_Shape&                  theShape,
1437                                           SMESH_Hypothesis::Hypothesis_Status& theStatus)
1438 {
1439   VISCOUS_3D::_ViscousBuilder builder;
1440   SMESH_ComputeErrorPtr err = builder.CheckHypotheses( theMesh, theShape );
1441   if ( err && !err->IsOK() )
1442     theStatus = SMESH_Hypothesis::HYP_INCOMPAT_HYPS;
1443   else
1444     theStatus = SMESH_Hypothesis::HYP_OK;
1445
1446   return err;
1447 }
1448 // --------------------------------------------------------------------------------
1449 bool StdMeshers_ViscousLayers::IsShapeWithLayers(int shapeIndex) const
1450 {
1451   bool isIn =
1452     ( std::find( _shapeIds.begin(), _shapeIds.end(), shapeIndex ) != _shapeIds.end() );
1453   return IsToIgnoreShapes() ? !isIn : isIn;
1454 }
1455
1456 // --------------------------------------------------------------------------------
1457 SMDS_MeshGroup* StdMeshers_ViscousLayers::CreateGroup( const std::string&  theName,
1458                                                        SMESH_Mesh&         theMesh,
1459                                                        SMDSAbs_ElementType theType)
1460 {
1461   SMESH_Group*      group = 0;
1462   SMDS_MeshGroup* groupDS = 0;
1463
1464   if ( theName.empty() )
1465     return groupDS;
1466        
1467   if ( SMESH_Mesh::GroupIteratorPtr grIt = theMesh.GetGroups() )
1468     while( grIt->more() && !group )
1469     {
1470       group = grIt->next();
1471       if ( !group ||
1472            group->GetGroupDS()->GetType() != theType ||
1473            group->GetName()               != theName ||
1474            !dynamic_cast< SMESHDS_Group* >( group->GetGroupDS() ))
1475         group = 0;
1476     }
1477   if ( !group )
1478     group = theMesh.AddGroup( theType, theName.c_str() );
1479
1480   groupDS = & dynamic_cast< SMESHDS_Group* >( group->GetGroupDS() )->SMDSGroup();
1481
1482   return groupDS;
1483 }
1484
1485 // END StdMeshers_ViscousLayers hypothesis
1486 //================================================================================
1487
1488 namespace VISCOUS_3D
1489 {
1490   gp_XYZ getEdgeDir( const TopoDS_Edge& E, const TopoDS_Vertex& fromV,
1491                      const double h0, bool* isRegularEdge = nullptr )
1492   {
1493     gp_Vec dir;
1494     double f,l;
1495     Handle(Geom_Curve) c = BRep_Tool::Curve( E, f, l );
1496     if ( c.IsNull() ) return gp_XYZ( Precision::Infinite(), 1e100, 1e100 );
1497     gp_Pnt  p = BRep_Tool::Pnt( fromV );
1498     gp_Pnt pf = c->Value( f ), pl = c->Value( l );
1499     double distF = p.SquareDistance( pf );
1500     double distL = p.SquareDistance( pl );
1501     c->D1(( distF < distL ? f : l), p, dir );
1502     if ( distL < distF ) dir.Reverse();
1503     bool isDifficult = false;
1504     if ( dir.SquareMagnitude() < h0 * h0 ) // check dir orientation
1505     {
1506       gp_Pnt& pClose = distF < distL ? pf : pl;
1507       gp_Pnt&   pFar = distF < distL ? pl : pf;
1508       gp_Pnt    pMid = 0.9 * pClose.XYZ() + 0.1 * pFar.XYZ();
1509       gp_Vec vMid( p, pMid );
1510       double     dot = vMid * dir;
1511       double    cos2 = dot * dot / dir.SquareMagnitude() / vMid.SquareMagnitude();
1512       if ( cos2 < 0.7 * 0.7 || dot < 0 ) // large angle between dir and vMid
1513       {
1514         double uClose = distF < distL ? f : l;
1515         double   uFar = distF < distL ? l : f;
1516         double      r = h0 / SMESH_Algo::EdgeLength( E );
1517         double   uMid = ( 1 - r ) * uClose + r * uFar;
1518         pMid = c->Value( uMid );
1519         dir = gp_Vec( p, pMid );
1520         isDifficult = true;
1521       }
1522     }
1523     if ( isRegularEdge )
1524       *isRegularEdge = !isDifficult;
1525
1526     return dir.XYZ();
1527   }
1528   //--------------------------------------------------------------------------------
1529   gp_XYZ getEdgeDir( const TopoDS_Edge& E, const SMDS_MeshNode* atNode,
1530                      SMESH_MesherHelper& helper)
1531   {
1532     gp_Vec dir;
1533     double f,l; gp_Pnt p;
1534     Handle(Geom_Curve) c = BRep_Tool::Curve( E, f, l );
1535     if ( c.IsNull() ) return gp_XYZ( Precision::Infinite(), 1e100, 1e100 );
1536     double u = helper.GetNodeU( E, atNode );
1537     c->D1( u, p, dir );
1538     return dir.XYZ();
1539   }
1540   //--------------------------------------------------------------------------------
1541   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Vertex& fromV,
1542                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper, bool& ok/*,
1543                      double* cosin=0*/);
1544   //--------------------------------------------------------------------------------
1545   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Edge& fromE,
1546                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper, bool& ok)
1547   {
1548     double f,l;
1549     Handle(Geom_Curve) c = BRep_Tool::Curve( fromE, f, l );
1550     if ( c.IsNull() )
1551     {
1552       TopoDS_Vertex v = helper.IthVertex( 0, fromE );
1553       return getFaceDir( F, v, node, helper, ok );
1554     }
1555     gp_XY uv = helper.GetNodeUV( F, node, 0, &ok );
1556     Handle(Geom_Surface) surface = BRep_Tool::Surface( F );
1557     gp_Pnt p; gp_Vec du, dv, norm;
1558     surface->D1( uv.X(),uv.Y(), p, du,dv );
1559     norm = du ^ dv;
1560
1561     double u = helper.GetNodeU( fromE, node, 0, &ok );
1562     c->D1( u, p, du );
1563     TopAbs_Orientation o = helper.GetSubShapeOri( F.Oriented(TopAbs_FORWARD), fromE);
1564     if ( o == TopAbs_REVERSED )
1565       du.Reverse();
1566
1567     gp_Vec dir = norm ^ du;
1568
1569     if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX &&
1570          helper.IsClosedEdge( fromE ))
1571     {
1572       if ( fabs(u-f) < fabs(u-l)) c->D1( l, p, dv );
1573       else                        c->D1( f, p, dv );
1574       if ( o == TopAbs_REVERSED )
1575         dv.Reverse();
1576       gp_Vec dir2 = norm ^ dv;
1577       dir = dir.Normalized() + dir2.Normalized();
1578     }
1579     return dir.XYZ();
1580   }
1581   //--------------------------------------------------------------------------------
1582   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Vertex& fromV,
1583                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper,
1584                      bool& ok/*, double* cosin*/)
1585   {
1586     TopoDS_Face faceFrw = F;
1587     faceFrw.Orientation( TopAbs_FORWARD );
1588     //double f,l; TopLoc_Location loc;
1589     TopoDS_Edge edges[2]; // sharing a vertex
1590     size_t nbEdges = 0;
1591     {
1592       TopoDS_Vertex VV[2];
1593       TopExp_Explorer exp( faceFrw, TopAbs_EDGE );
1594       for ( ; exp.More() && nbEdges < 2; exp.Next() )
1595       {
1596         const TopoDS_Edge& e = TopoDS::Edge( exp.Current() );
1597         if ( SMESH_Algo::isDegenerated( e )) continue;
1598         TopExp::Vertices( e, VV[0], VV[1], /*CumOri=*/true );
1599         if ( VV[1].IsSame( fromV )) {
1600           nbEdges += edges[ 0 ].IsNull();
1601           edges[ 0 ] = e;
1602         }
1603         else if ( VV[0].IsSame( fromV )) {
1604           nbEdges += edges[ 1 ].IsNull();
1605           edges[ 1 ] = e;
1606         }
1607       }
1608     }
1609     gp_XYZ dir(0,0,0), edgeDir[2];
1610     if ( nbEdges == 2 )
1611     {
1612       // get dirs of edges going fromV
1613       ok = true;
1614       for ( size_t i = 0; i < nbEdges && ok; ++i )
1615       {
1616         edgeDir[i] = getEdgeDir( edges[i], fromV, 0.1 * SMESH_Algo::EdgeLength( edges[i] ));
1617         double size2 = edgeDir[i].SquareModulus();
1618         if (( ok = size2 > numeric_limits<double>::min() ))
1619           edgeDir[i] /= sqrt( size2 );
1620       }
1621       if ( !ok ) return dir;
1622
1623       // get angle between the 2 edges
1624       gp_Vec faceNormal;
1625       double angle = helper.GetAngle( edges[0], edges[1], faceFrw, fromV, &faceNormal );
1626       if ( Abs( angle ) < 5 * M_PI/180 )
1627       {
1628         dir = ( faceNormal.XYZ() ^ edgeDir[0].Reversed()) + ( faceNormal.XYZ() ^ edgeDir[1] );
1629       }
1630       else
1631       {
1632         dir = edgeDir[0] + edgeDir[1];
1633         if ( angle < 0 )
1634           dir.Reverse();
1635       }
1636       // if ( cosin ) {
1637       //   double angle = faceNormal.Angle( dir );
1638       //   *cosin = Cos( angle );
1639       // }
1640     }
1641     else if ( nbEdges == 1 )
1642     {
1643       dir = getFaceDir( faceFrw, edges[ edges[0].IsNull() ], node, helper, ok );
1644       //if ( cosin ) *cosin = 1.;
1645     }
1646     else
1647     {
1648       ok = false;
1649     }
1650
1651     return dir;
1652   }
1653
1654   //================================================================================
1655   /*!
1656    * \brief Finds concave VERTEXes of a FACE
1657    */
1658   //================================================================================
1659
1660   bool getConcaveVertices( const TopoDS_Face&  F,
1661                            SMESH_MesherHelper& helper,
1662                            set< TGeomID >*     vertices = 0)
1663   {
1664     // check angles at VERTEXes
1665     TError error;
1666     TSideVector wires = StdMeshers_FaceSide::GetFaceWires( F, *helper.GetMesh(), 0, error );
1667     for ( size_t iW = 0; iW < wires.size(); ++iW )
1668     {
1669       const int nbEdges = wires[iW]->NbEdges();
1670       if ( nbEdges < 2 && SMESH_Algo::isDegenerated( wires[iW]->Edge(0)))
1671         continue;
1672       for ( int iE1 = 0; iE1 < nbEdges; ++iE1 )
1673       {
1674         if ( SMESH_Algo::isDegenerated( wires[iW]->Edge( iE1 ))) continue;
1675         int iE2 = ( iE1 + 1 ) % nbEdges;
1676         while ( SMESH_Algo::isDegenerated( wires[iW]->Edge( iE2 )))
1677           iE2 = ( iE2 + 1 ) % nbEdges;
1678         TopoDS_Vertex V = wires[iW]->FirstVertex( iE2 );
1679         double angle = helper.GetAngle( wires[iW]->Edge( iE1 ),
1680                                         wires[iW]->Edge( iE2 ), F, V );
1681         if ( angle < -5. * M_PI / 180. )
1682         {
1683           if ( !vertices )
1684             return true;
1685           vertices->insert( helper.GetMeshDS()->ShapeToIndex( V ));
1686         }
1687       }
1688     }
1689     return vertices ? !vertices->empty() : false;
1690   }
1691
1692   //================================================================================
1693   /*!
1694    * \brief Returns true if a FACE is bound by a concave EDGE
1695    */
1696   //================================================================================
1697
1698   bool isConcave( const TopoDS_Face&  F,
1699                   SMESH_MesherHelper& helper,
1700                   set< TGeomID >*     vertices = 0 )
1701   {
1702     bool isConcv = false;
1703     // if ( helper.Count( F, TopAbs_WIRE, /*useMap=*/false) > 1 )
1704     //   return true;
1705     gp_Vec2d drv1, drv2;
1706     gp_Pnt2d p;
1707     TopExp_Explorer eExp( F.Oriented( TopAbs_FORWARD ), TopAbs_EDGE );
1708     for ( ; eExp.More(); eExp.Next() )
1709     {
1710       const TopoDS_Edge& E = TopoDS::Edge( eExp.Current() );
1711       if ( SMESH_Algo::isDegenerated( E )) continue;
1712       // check if 2D curve is concave
1713       BRepAdaptor_Curve2d curve( E, F );
1714       const int nbIntervals = curve.NbIntervals( GeomAbs_C2 );
1715       TColStd_Array1OfReal intervals(1, nbIntervals + 1 );
1716       curve.Intervals( intervals, GeomAbs_C2 );
1717       bool isConvex = true;
1718       for ( int i = 1; i <= nbIntervals && isConvex; ++i )
1719       {
1720         double u1 = intervals( i );
1721         double u2 = intervals( i+1 );
1722         curve.D2( 0.5*( u1+u2 ), p, drv1, drv2 );
1723         double cross = drv1 ^ drv2;
1724         if ( E.Orientation() == TopAbs_REVERSED )
1725           cross = -cross;
1726         isConvex = ( cross > -1e-9 ); // 0.1 );
1727       }
1728       if ( !isConvex )
1729       {
1730         //cout << "Concave FACE " << helper.GetMeshDS()->ShapeToIndex( F ) << endl;
1731         isConcv = true;
1732         if ( vertices )
1733           break;
1734         else
1735           return true;
1736       }
1737     }
1738
1739     // check angles at VERTEXes
1740     if ( getConcaveVertices( F, helper, vertices ))
1741       isConcv = true;
1742
1743     return isConcv;
1744   }
1745
1746   //================================================================================
1747   /*!
1748    * \brief Computes minimal distance of face in-FACE nodes from an EDGE
1749    *  \param [in] face - the mesh face to treat
1750    *  \param [in] nodeOnEdge - a node on the EDGE
1751    *  \param [out] faceSize - the computed distance
1752    *  \return bool - true if faceSize computed
1753    */
1754   //================================================================================
1755
1756   bool getDistFromEdge( const SMDS_MeshElement* face,
1757                         const SMDS_MeshNode*    nodeOnEdge,
1758                         double &                faceSize )
1759   {
1760     faceSize = Precision::Infinite();
1761     bool done = false;
1762
1763     int nbN  = face->NbCornerNodes();
1764     int iOnE = face->GetNodeIndex( nodeOnEdge );
1765     int iNext[2] = { SMESH_MesherHelper::WrapIndex( iOnE+1, nbN ),
1766                      SMESH_MesherHelper::WrapIndex( iOnE-1, nbN ) };
1767     const SMDS_MeshNode* nNext[2] = { face->GetNode( iNext[0] ),
1768                                       face->GetNode( iNext[1] ) };
1769     gp_XYZ segVec, segEnd = SMESH_TNodeXYZ( nodeOnEdge ); // segment on EDGE
1770     double segLen = -1.;
1771     // look for two neighbor not in-FACE nodes of face
1772     for ( int i = 0; i < 2; ++i )
1773     {
1774       if (( nNext[i]->GetPosition()->GetDim() != 2 ) &&
1775           ( nodeOnEdge->GetPosition()->GetDim() == 0 || nNext[i]->GetID() < nodeOnEdge->GetID() ))
1776       {
1777         // look for an in-FACE node
1778         for ( int iN = 0; iN < nbN; ++iN )
1779         {
1780           if ( iN == iOnE || iN == iNext[i] )
1781             continue;
1782           SMESH_TNodeXYZ pInFace = face->GetNode( iN );
1783           gp_XYZ v = pInFace - segEnd;
1784           if ( segLen < 0 )
1785           {
1786             segVec = SMESH_TNodeXYZ( nNext[i] ) - segEnd;
1787             segLen = segVec.Modulus();
1788           }
1789           double distToSeg = v.Crossed( segVec ).Modulus() / segLen;
1790           faceSize = Min( faceSize, distToSeg );
1791           done = true;
1792         }
1793         segLen = -1;
1794       }
1795     }
1796     return done;
1797   }
1798   //================================================================================
1799   /*!
1800    * \brief Return direction of axis or revolution of a surface
1801    */
1802   //================================================================================
1803
1804   bool getRovolutionAxis( const Adaptor3d_Surface& surface,
1805                           gp_Dir &                 axis )
1806   {
1807     switch ( surface.GetType() ) {
1808     case GeomAbs_Cone:
1809     {
1810       gp_Cone cone = surface.Cone();
1811       axis = cone.Axis().Direction();
1812       break;
1813     }
1814     case GeomAbs_Sphere:
1815     {
1816       gp_Sphere sphere = surface.Sphere();
1817       axis = sphere.Position().Direction();
1818       break;
1819     }
1820     case GeomAbs_SurfaceOfRevolution:
1821     {
1822       axis = surface.AxeOfRevolution().Direction();
1823       break;
1824     }
1825     //case GeomAbs_SurfaceOfExtrusion:
1826     case GeomAbs_OffsetSurface:
1827     {
1828 #if OCC_VERSION_LARGE < 0x07070000
1829       Handle(Adaptor3d_HSurface) base = surface.BasisSurface();
1830       return getRovolutionAxis( base->Surface(), axis );
1831 #else
1832       Handle(Adaptor3d_Surface) base = surface.BasisSurface();
1833       return getRovolutionAxis( *base, axis );
1834 #endif
1835     }
1836     default: return false;
1837     }
1838     return true;
1839   }
1840
1841   //--------------------------------------------------------------------------------
1842   // DEBUG. Dump intermediate node positions into a python script
1843   // HOWTO use: run python commands written in a console and defined in /tmp/viscous.py
1844   // to see construction steps of viscous layers
1845 #ifdef __myDEBUG
1846   ostream* py;
1847   int      theNbPyFunc;
1848   struct PyDump
1849   {
1850     PyDump(SMESH_Mesh& m) {
1851       int tag = 3 + m.GetId();
1852       const char* fname = "/tmp/viscous.py";
1853       cout << "exec(open('"<<fname<<"','rb').read() )"<<endl;
1854       py = _pyStream = new ofstream(fname);
1855       *py << "import SMESH" << endl
1856           << "from salome.smesh import smeshBuilder" << endl
1857           << "smesh  = smeshBuilder.New()" << endl
1858           << "meshSO = salome.myStudy.FindObjectID('0:1:2:" << tag <<"')" << endl
1859           << "mesh   = smesh.Mesh( meshSO.GetObject() )"<<endl;
1860       theNbPyFunc = 0;
1861     }
1862     void Finish() {
1863       if (py) {
1864         *py << "mesh.GroupOnFilter(SMESH.VOLUME,'Viscous Prisms',"
1865           "smesh.GetFilter(SMESH.VOLUME,SMESH.FT_ElemGeomType,'=',SMESH.Geom_PENTA))"<<endl;
1866         *py << "mesh.GroupOnFilter(SMESH.VOLUME,'Neg Volumes',"
1867           "smesh.GetFilter(SMESH.VOLUME,SMESH.FT_Volume3D,'<',0))"<<endl;
1868       }
1869       delete py; py=0;
1870     }
1871     ~PyDump() { Finish(); cout << "NB FUNCTIONS: " << theNbPyFunc << endl; }
1872     struct MyStream : public ostream
1873     {
1874       template <class T> ostream & operator<<( const T &anything ) { return *this ; }
1875     };
1876     void Pause() { py = &_mystream; }
1877     void Resume() { py = _pyStream; }
1878     MyStream _mystream;
1879     ostream* _pyStream;
1880   };
1881 #define dumpFunction(f) { _dumpFunction(f, __LINE__);}
1882 #define dumpMove(n)     { _dumpMove(n, __LINE__);}
1883 #define dumpMoveComm(n,txt) { _dumpMove(n, __LINE__, txt);}
1884 #define dumpCmd(txt)    { _dumpCmd(txt, __LINE__);}
1885   void _dumpFunction(const string& fun, int ln)
1886   { if (py) *py<< "def "<<fun<<"(): # "<< ln <<endl; cout<<fun<<"()"<<endl; ++theNbPyFunc; }
1887   void _dumpMove(const SMDS_MeshNode* n, int ln, const char* txt="")
1888   { if (py) *py<< "  mesh.MoveNode( "<<n->GetID()<< ", "<< n->X()
1889                << ", "<<n->Y()<<", "<< n->Z()<< ")\t\t # "<< ln <<" "<< txt << endl; }
1890   void _dumpCmd(const string& txt, int ln)
1891   { if (py) *py<< "  "<<txt<<" # "<< ln <<endl; }
1892   void dumpFunctionEnd()
1893   { if (py) *py<< "  return"<< endl; }
1894   void dumpChangeNodes( const SMDS_MeshElement* f )
1895   { if (py) { *py<< "  mesh.ChangeElemNodes( " << f->GetID()<<", [";
1896       for ( int i=1; i < f->NbNodes(); ++i ) *py << f->GetNode(i-1)->GetID()<<", ";
1897       *py << f->GetNode( f->NbNodes()-1 )->GetID() << " ])"<< endl; }}
1898 #define debugMsg( txt ) { cout << "# "<< txt << " (line: " << __LINE__ << ")" << endl; }
1899
1900 #else
1901
1902   struct PyDump { PyDump(SMESH_Mesh&) {} void Finish() {} void Pause() {} void Resume() {} };
1903 #define dumpFunction(f) f
1904 #define dumpMove(n)
1905 #define dumpMoveComm(n,txt)
1906 #define dumpCmd(txt)
1907 #define dumpFunctionEnd()
1908 #define dumpChangeNodes(f) { if(f) {} } // prevent "unused variable 'f'" warning
1909 #define debugMsg( txt ) {}
1910
1911 #endif
1912 }
1913
1914 using namespace VISCOUS_3D;
1915
1916 //================================================================================
1917 /*!
1918  * \brief Constructor of _ViscousBuilder
1919  */
1920 //================================================================================
1921
1922 _ViscousBuilder::_ViscousBuilder()
1923 {
1924   _error = SMESH_ComputeError::New(COMPERR_OK);
1925   _tmpFaceID = 0;
1926 }
1927
1928 //================================================================================
1929 /*!
1930  * \brief Stores error description and returns false
1931  */
1932 //================================================================================
1933
1934 bool _ViscousBuilder::error(const string& text, int solidId )
1935 {
1936   const string prefix = string("Viscous layers builder: ");
1937   _error->myName    = COMPERR_ALGO_FAILED;
1938   _error->myComment = prefix + text;
1939   if ( _mesh )
1940   {
1941     SMESH_subMesh* sm = _mesh->GetSubMeshContaining( solidId );
1942     if ( !sm && !_sdVec.empty() )
1943       sm = _mesh->GetSubMeshContaining( solidId = _sdVec[0]._index );
1944     if ( sm && sm->GetSubShape().ShapeType() == TopAbs_SOLID )
1945     {
1946       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1947       if ( smError && smError->myAlgo )
1948         _error->myAlgo = smError->myAlgo;
1949       smError = _error;
1950       sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
1951     }
1952     // set KO to all solids
1953     for ( size_t i = 0; i < _sdVec.size(); ++i )
1954     {
1955       if ( _sdVec[i]._index == solidId )
1956         continue;
1957       sm = _mesh->GetSubMesh( _sdVec[i]._solid );
1958       if ( !sm->IsEmpty() )
1959         continue;
1960       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1961       if ( !smError || smError->IsOK() )
1962       {
1963         smError = SMESH_ComputeError::New( COMPERR_ALGO_FAILED, prefix + "failed");
1964         sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
1965       }
1966     }
1967   }
1968   makeGroupOfLE(); // debug
1969
1970   return false;
1971 }
1972
1973 //================================================================================
1974 /*!
1975  * \brief At study restoration, restore event listeners used to clear an inferior
1976  *  dim sub-mesh modified by viscous layers
1977  */
1978 //================================================================================
1979
1980 void _ViscousBuilder::RestoreListeners()
1981 {
1982   // TODO
1983 }
1984
1985 //================================================================================
1986 /*!
1987  * \brief computes SMESH_ProxyMesh::SubMesh::_n2n
1988  */
1989 //================================================================================
1990
1991 bool _ViscousBuilder::MakeN2NMap( _MeshOfSolid* pm )
1992 {
1993   SMESH_subMesh* solidSM = pm->mySubMeshes.front();
1994   TopExp_Explorer fExp( solidSM->GetSubShape(), TopAbs_FACE );
1995   for ( ; fExp.More(); fExp.Next() )
1996   {
1997     SMESHDS_SubMesh* srcSmDS = pm->GetMeshDS()->MeshElements( fExp.Current() );
1998     const SMESH_ProxyMesh::SubMesh* prxSmDS = pm->GetProxySubMesh( fExp.Current() );
1999
2000     if ( !srcSmDS || !prxSmDS || !srcSmDS->NbElements() || !prxSmDS->NbElements() )
2001       continue;
2002     if ( srcSmDS->GetElements()->next() == prxSmDS->GetElements()->next())
2003       continue;
2004
2005     if ( srcSmDS->NbElements() != prxSmDS->NbElements() )
2006       return error( "Different nb elements in a source and a proxy sub-mesh", solidSM->GetId());
2007
2008     SMDS_ElemIteratorPtr srcIt = srcSmDS->GetElements();
2009     SMDS_ElemIteratorPtr prxIt = prxSmDS->GetElements();
2010     while( prxIt->more() )
2011     {
2012       const SMDS_MeshElement* fSrc = srcIt->next();
2013       const SMDS_MeshElement* fPrx = prxIt->next();
2014       if ( fSrc->NbNodes() != fPrx->NbNodes())
2015         return error( "Different elements in a source and a proxy sub-mesh", solidSM->GetId());
2016       for ( int i = 0 ; i < fPrx->NbNodes(); ++i )
2017         pm->setNode2Node( fSrc->GetNode(i), fPrx->GetNode(i), prxSmDS );
2018     }
2019   }
2020   pm->_n2nMapComputed = true;
2021   return true;
2022 }
2023
2024 //================================================================================
2025 /*!
2026  * \brief Does its job
2027  */
2028 //================================================================================
2029
2030 SMESH_ComputeErrorPtr _ViscousBuilder::Compute(SMESH_Mesh&         theMesh,
2031                                                const TopoDS_Shape& theShape)
2032 {
2033   _mesh = & theMesh;
2034
2035   _Factory factory;
2036
2037   // check if proxy mesh already computed
2038   TopExp_Explorer exp( theShape, TopAbs_SOLID );
2039   if ( !exp.More() )
2040     return error("No SOLID's in theShape"), _error;
2041
2042   if ( _ViscousListener::GetSolidMesh( _mesh, exp.Current(), /*toCreate=*/false))
2043     return SMESH_ComputeErrorPtr(); // everything already computed
2044
2045   // TODO: ignore already computed SOLIDs
2046   if ( !findSolidsWithLayers())
2047     return _error;
2048
2049   if ( !findFacesWithLayers() )
2050     return _error;
2051
2052   if ( !makeEdgesOnShape() )
2053     return _error;
2054
2055   findPeriodicFaces();
2056
2057   PyDump debugDump( theMesh );
2058   _pyDump = &debugDump;
2059
2060
2061   for ( size_t i = 0; i < _sdVec.size(); ++i )
2062   {
2063     size_t iSD = 0;
2064     for ( iSD = 0; iSD < _sdVec.size(); ++iSD ) // find next SOLID to compute
2065       if ( _sdVec[iSD]._before.IsEmpty() &&
2066            !_sdVec[iSD]._solid.IsNull() &&
2067            !_sdVec[iSD]._done )
2068         break;
2069     if ( iSD == _sdVec.size() )
2070       break; // all done
2071
2072     if ( ! makeLayer(_sdVec[iSD]) )   // create _LayerEdge's
2073       return _error;
2074
2075     if ( _sdVec[iSD]._n2eMap.size() == 0 ) // no layers in a SOLID
2076     {
2077       _sdVec[iSD]._solid.Nullify();
2078       continue;
2079     }
2080
2081     if ( ! inflate(_sdVec[iSD]) )     // increase length of _LayerEdge's
2082       return _error;
2083
2084     if ( ! refine(_sdVec[iSD]) )      // create nodes and prisms
2085       return _error;
2086
2087     if ( ! shrink(_sdVec[iSD]) )      // shrink 2D mesh on FACEs w/o layer
2088       return _error;
2089
2090     addBoundaryElements(_sdVec[iSD]); // create quadrangles on prism bare sides
2091
2092     _sdVec[iSD]._done = true;
2093
2094     const TopoDS_Shape& solid = _sdVec[iSD]._solid;
2095     for ( iSD = 0; iSD < _sdVec.size(); ++iSD )
2096       _sdVec[iSD]._before.Remove( solid );
2097   }
2098
2099   makeGroupOfLE(); // debug
2100   debugDump.Finish();
2101
2102   return _error;
2103 }
2104
2105 //================================================================================
2106 /*!
2107  * \brief Check validity of hypotheses
2108  */
2109 //================================================================================
2110
2111 SMESH_ComputeErrorPtr _ViscousBuilder::CheckHypotheses( SMESH_Mesh&         mesh,
2112                                                         const TopoDS_Shape& shape )
2113 {
2114   _mesh = & mesh;
2115
2116   if ( _ViscousListener::GetSolidMesh( _mesh, shape, /*toCreate=*/false))
2117     return SMESH_ComputeErrorPtr(); // everything already computed
2118
2119
2120   findSolidsWithLayers( /*checkFaceMesh=*/false );
2121   bool ok = findFacesWithLayers( true );
2122
2123   // remove _MeshOfSolid's of _SolidData's
2124   for ( size_t i = 0; i < _sdVec.size(); ++i )
2125     _ViscousListener::RemoveSolidMesh( _mesh, _sdVec[i]._solid );
2126
2127   if ( !ok )
2128     return _error;
2129
2130   return SMESH_ComputeErrorPtr();
2131 }
2132
2133 //================================================================================
2134 /*!
2135  * \brief Finds SOLIDs to compute using viscous layers. Fills _sdVec
2136  */
2137 //================================================================================
2138
2139 bool _ViscousBuilder::findSolidsWithLayers(const bool checkFaceMesh)
2140 {
2141   // get all solids
2142   TopTools_IndexedMapOfShape allSolids;
2143   TopExp::MapShapes( _mesh->GetShapeToMesh(), TopAbs_SOLID, allSolids );
2144   _sdVec.reserve( allSolids.Extent());
2145
2146   SMESH_HypoFilter filter;
2147   for ( int i = 1; i <= allSolids.Extent(); ++i )
2148   {
2149     SMESH_subMesh* sm = _mesh->GetSubMesh( allSolids(i) );
2150     if ( sm->GetSubMeshDS() && sm->GetSubMeshDS()->NbElements() > 0 )
2151       continue; // solid is already meshed
2152     // TODO: check if algo is hidden
2153     SMESH_Algo* algo = sm->GetAlgo();
2154     if ( !algo ) continue;
2155     // check if all FACEs are meshed, which can be false if Compute() a sub-shape
2156     if ( checkFaceMesh )
2157     {
2158       bool facesMeshed = true;
2159       SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(false,true);
2160       while ( smIt->more() && facesMeshed )
2161       {
2162         SMESH_subMesh * faceSM = smIt->next();
2163         if ( faceSM->GetSubShape().ShapeType() != TopAbs_FACE )
2164           break;
2165         facesMeshed = faceSM->IsMeshComputed();
2166       }
2167       if ( !facesMeshed )
2168         continue;
2169     }
2170     // find StdMeshers_ViscousLayers hyp assigned to the i-th solid
2171     const list <const SMESHDS_Hypothesis *> & allHyps =
2172       algo->GetUsedHypothesis(*_mesh, allSolids(i), /*ignoreAuxiliary=*/false);
2173     _SolidData* soData = 0;
2174     list< const SMESHDS_Hypothesis *>::const_iterator hyp = allHyps.begin();
2175     const StdMeshers_ViscousLayers* viscHyp = 0;
2176     for ( ; hyp != allHyps.end(); ++hyp )
2177       if (( viscHyp = dynamic_cast<const StdMeshers_ViscousLayers*>( *hyp )))
2178       {
2179         TopoDS_Shape hypShape;
2180         filter.Init( filter.Is( viscHyp ));
2181         _mesh->GetHypothesis( allSolids(i), filter, true, &hypShape );
2182
2183         if ( !soData )
2184         {
2185           _MeshOfSolid* proxyMesh = _ViscousListener::GetSolidMesh( _mesh,
2186                                                                     allSolids(i),
2187                                                                     /*toCreate=*/true);
2188           _sdVec.push_back( _SolidData( allSolids(i), proxyMesh ));
2189           soData = & _sdVec.back();
2190           soData->_index = getMeshDS()->ShapeToIndex( allSolids(i));
2191           soData->_helper = new SMESH_MesherHelper( *_mesh );
2192           soData->_helper->SetSubShape( allSolids(i) );
2193           _solids.Add( allSolids(i) );
2194         }
2195         soData->_hyps.push_back( viscHyp );
2196         soData->_hypShapes.push_back( hypShape );
2197       }
2198   }
2199   if ( _sdVec.empty() )
2200     return error
2201       ( SMESH_Comment(StdMeshers_ViscousLayers::GetHypType()) << " hypothesis not found",0);
2202
2203   return true;
2204 }
2205
2206 //================================================================================
2207 /*!
2208  * \brief Set a _SolidData to be computed before another
2209  */
2210 //================================================================================
2211
2212 bool _ViscousBuilder::setBefore( _SolidData& solidBefore, _SolidData& solidAfter )
2213 {
2214   // check possibility to set this order; get all solids before solidBefore
2215   TopTools_IndexedMapOfShape allSolidsBefore;
2216   allSolidsBefore.Add( solidBefore._solid );
2217   for ( int i = 1; i <= allSolidsBefore.Extent(); ++i )
2218   {
2219     int iSD = _solids.FindIndex( allSolidsBefore(i) );
2220     if ( iSD )
2221     {
2222       TopTools_MapIteratorOfMapOfShape soIt( _sdVec[ iSD-1 ]._before );
2223       for ( ; soIt.More(); soIt.Next() )
2224         allSolidsBefore.Add( soIt.Value() );
2225     }
2226   }
2227   if ( allSolidsBefore.Contains( solidAfter._solid ))
2228     return false;
2229
2230   for ( int i = 1; i <= allSolidsBefore.Extent(); ++i )
2231     solidAfter._before.Add( allSolidsBefore(i) );
2232
2233   return true;
2234 }
2235
2236 //================================================================================
2237 /*!
2238  * \brief
2239  */
2240 //================================================================================
2241
2242 bool _ViscousBuilder::findFacesWithLayers(const bool onlyWith)
2243 {
2244   SMESH_MesherHelper helper( *_mesh );
2245   TopExp_Explorer exp;
2246
2247   // collect all faces-to-ignore defined by hyp
2248   for ( size_t i = 0; i < _sdVec.size(); ++i )
2249   {
2250     // get faces-to-ignore defined by each hyp
2251     typedef const StdMeshers_ViscousLayers* THyp;
2252     typedef std::pair< set<TGeomID>, THyp > TFacesOfHyp;
2253     list< TFacesOfHyp > ignoreFacesOfHyps;
2254     list< THyp >::iterator              hyp = _sdVec[i]._hyps.begin();
2255     list< TopoDS_Shape >::iterator hypShape = _sdVec[i]._hypShapes.begin();
2256     for ( ; hyp != _sdVec[i]._hyps.end(); ++hyp, ++hypShape )
2257     {
2258       ignoreFacesOfHyps.push_back( TFacesOfHyp( set<TGeomID>(), *hyp ));
2259       getIgnoreFaces( _sdVec[i]._solid, *hyp, *hypShape, ignoreFacesOfHyps.back().first );
2260     }
2261
2262     // fill _SolidData::_face2hyp and check compatibility of hypotheses
2263     const int nbHyps = _sdVec[i]._hyps.size();
2264     if ( nbHyps > 1 )
2265     {
2266       // check if two hypotheses define different parameters for the same FACE
2267       list< TFacesOfHyp >::iterator igFacesOfHyp;
2268       for ( exp.Init( _sdVec[i]._solid, TopAbs_FACE ); exp.More(); exp.Next() )
2269       {
2270         const TGeomID faceID = getMeshDS()->ShapeToIndex( exp.Current() );
2271         THyp hyp = 0;
2272         igFacesOfHyp = ignoreFacesOfHyps.begin();
2273         for ( ; igFacesOfHyp != ignoreFacesOfHyps.end(); ++igFacesOfHyp )
2274           if ( ! igFacesOfHyp->first.count( faceID ))
2275           {
2276             if ( hyp )
2277               return error(SMESH_Comment("Several hypotheses define "
2278                                          "Viscous Layers on the face #") << faceID );
2279             hyp = igFacesOfHyp->second;
2280           }
2281         if ( hyp )
2282           _sdVec[i]._face2hyp.insert( make_pair( faceID, hyp ));
2283         else
2284           _sdVec[i]._ignoreFaceIds.insert( faceID );
2285       }
2286
2287       // check if two hypotheses define different number of viscous layers for
2288       // adjacent faces of a solid
2289       set< int > nbLayersSet;
2290       igFacesOfHyp = ignoreFacesOfHyps.begin();
2291       for ( ; igFacesOfHyp != ignoreFacesOfHyps.end(); ++igFacesOfHyp )
2292       {
2293         nbLayersSet.insert( igFacesOfHyp->second->GetNumberLayers() );
2294       }
2295       if ( nbLayersSet.size() > 1 )
2296       {
2297         for ( exp.Init( _sdVec[i]._solid, TopAbs_EDGE ); exp.More(); exp.Next() )
2298         {
2299           PShapeIteratorPtr fIt = helper.GetAncestors( exp.Current(), *_mesh, TopAbs_FACE );
2300           THyp hyp1 = 0, hyp2 = 0;
2301           while( const TopoDS_Shape* face = fIt->next() )
2302           {
2303             const TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
2304             map< TGeomID, THyp >::iterator f2h = _sdVec[i]._face2hyp.find( faceID );
2305             if ( f2h != _sdVec[i]._face2hyp.end() )
2306             {
2307               ( hyp1 ? hyp2 : hyp1 ) = f2h->second;
2308             }
2309           }
2310           if ( hyp1 && hyp2 &&
2311                hyp1->GetNumberLayers() != hyp2->GetNumberLayers() )
2312           {
2313             return error("Two hypotheses define different number of "
2314                          "viscous layers on adjacent faces");
2315           }
2316         }
2317       }
2318     } // if ( nbHyps > 1 )
2319     else
2320     {
2321       _sdVec[i]._ignoreFaceIds.swap( ignoreFacesOfHyps.back().first );
2322     }
2323   } // loop on _sdVec
2324
2325   if ( onlyWith ) // is called to check hypotheses compatibility only
2326     return true;
2327
2328   // fill _SolidData::_reversedFaceIds
2329   for ( size_t i = 0; i < _sdVec.size(); ++i )
2330   {
2331     exp.Init( _sdVec[i]._solid.Oriented( TopAbs_FORWARD ), TopAbs_FACE );
2332     for ( ; exp.More(); exp.Next() )
2333     {
2334       const TopoDS_Face& face = TopoDS::Face( exp.Current() );
2335       const TGeomID    faceID = getMeshDS()->ShapeToIndex( face );
2336       if ( //!sdVec[i]._ignoreFaceIds.count( faceID ) &&
2337           helper.NbAncestors( face, *_mesh, TopAbs_SOLID ) > 1 &&
2338           helper.IsReversedSubMesh( face ))
2339       {
2340         _sdVec[i]._reversedFaceIds.insert( faceID );
2341       }
2342     }
2343   }
2344
2345   // Find FACEs to shrink mesh on (solution 2 in issue 0020832): fill in _shrinkShape2Shape
2346   TopTools_IndexedMapOfShape shapes;
2347   std::string structAlgoName = "Hexa_3D";
2348   for ( size_t i = 0; i < _sdVec.size(); ++i )
2349   {
2350     shapes.Clear();
2351     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_EDGE, shapes);
2352     for ( int iE = 1; iE <= shapes.Extent(); ++iE )
2353     {
2354       const TopoDS_Shape& edge = shapes(iE);
2355       // find 2 FACEs sharing an EDGE
2356       TopoDS_Shape FF[2];
2357       PShapeIteratorPtr fIt = helper.GetAncestors(edge, *_mesh, TopAbs_FACE, &_sdVec[i]._solid);
2358       while ( fIt->more())
2359       {
2360         const TopoDS_Shape* f = fIt->next();
2361         FF[ int( !FF[0].IsNull()) ] = *f;
2362       }
2363       if( FF[1].IsNull() ) continue; // seam edge can be shared by 1 FACE only
2364
2365       // check presence of layers on them
2366       int ignore[2];
2367       for ( int j = 0; j < 2; ++j )
2368         ignore[j] = _sdVec[i]._ignoreFaceIds.count( getMeshDS()->ShapeToIndex( FF[j] ));
2369       if ( ignore[0] == ignore[1] )
2370         continue; // nothing interesting
2371       TopoDS_Shape fWOL = FF[ ignore[0] ? 0 : 1 ]; // FACE w/o layers
2372
2373       // add EDGE to maps
2374       if ( !fWOL.IsNull())
2375       {
2376         TGeomID edgeInd = getMeshDS()->ShapeToIndex( edge );
2377         _sdVec[i]._shrinkShape2Shape.insert( make_pair( edgeInd, fWOL ));
2378       }
2379     }
2380   }
2381
2382   // Find the SHAPE along which to inflate _LayerEdge based on VERTEX
2383
2384   for ( size_t i = 0; i < _sdVec.size(); ++i )
2385   {
2386     shapes.Clear();
2387     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_VERTEX, shapes);
2388     for ( int iV = 1; iV <= shapes.Extent(); ++iV )
2389     {
2390       const TopoDS_Shape& vertex = shapes(iV);
2391       // find faces WOL sharing the vertex
2392       vector< TopoDS_Shape > facesWOL;
2393       size_t totalNbFaces = 0;
2394       PShapeIteratorPtr fIt = helper.GetAncestors(vertex, *_mesh, TopAbs_FACE, &_sdVec[i]._solid );
2395       while ( fIt->more())
2396       {
2397         const TopoDS_Shape* f = fIt->next();
2398         totalNbFaces++;
2399         const int fID = getMeshDS()->ShapeToIndex( *f );
2400         if ( _sdVec[i]._ignoreFaceIds.count ( fID ) /*&& !_sdVec[i]._noShrinkShapes.count( fID )*/)
2401           facesWOL.push_back( *f );
2402       }
2403       if ( facesWOL.size() == totalNbFaces || facesWOL.empty() )
2404         continue; // no layers at this vertex or no WOL
2405       TGeomID vInd = getMeshDS()->ShapeToIndex( vertex );
2406       switch ( facesWOL.size() )
2407       {
2408       case 1:
2409       {
2410         helper.SetSubShape( facesWOL[0] );
2411         if ( helper.IsRealSeam( vInd )) // inflate along a seam edge?
2412         {
2413           TopoDS_Shape seamEdge;
2414           PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE);
2415           while ( eIt->more() && seamEdge.IsNull() )
2416           {
2417             const TopoDS_Shape* e = eIt->next();
2418             if ( helper.IsRealSeam( *e ) )
2419               seamEdge = *e;
2420           }
2421           if ( !seamEdge.IsNull() )
2422           {
2423             _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, seamEdge ));
2424             break;
2425           }
2426         }
2427         _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, facesWOL[0] ));
2428         break;
2429       }
2430       case 2:
2431       {
2432         // find an edge shared by 2 faces
2433         PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE);
2434         while ( eIt->more())
2435         {
2436           const TopoDS_Shape* e = eIt->next();
2437           if ( helper.IsSubShape( *e, facesWOL[0]) &&
2438                helper.IsSubShape( *e, facesWOL[1]))
2439           {
2440             _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, *e )); break;
2441           }
2442         }
2443         break;
2444       }
2445       default:
2446         std::ostringstream msg;
2447         msg << "Not yet supported case: vertex bounded by ";
2448         msg << facesWOL.size();
2449         msg << " faces without layer at coordinates (";
2450         TopoDS_Vertex v = TopoDS::Vertex(vertex);
2451         gp_Pnt p = BRep_Tool::Pnt(v);
2452         msg << p.X() << ", " << p.Y() << ", " << p.Z() << ")";
2453         return error(msg.str().c_str(), _sdVec[i]._index);
2454       }
2455     }
2456   }
2457
2458   // Add to _noShrinkShapes sub-shapes of FACE's that can't be shrunk since
2459   // the algo of the SOLID sharing the FACE does not support it or for other reasons
2460   set< string > notSupportAlgos; notSupportAlgos.insert( structAlgoName );
2461   for ( size_t i = 0; i < _sdVec.size(); ++i )
2462   {
2463     map< TGeomID, TopoDS_Shape >::iterator e2f = _sdVec[i]._shrinkShape2Shape.begin();
2464     for ( ; e2f != _sdVec[i]._shrinkShape2Shape.end(); ++e2f )
2465     {
2466       const TopoDS_Shape& fWOL = e2f->second;
2467       const TGeomID     edgeID = e2f->first;
2468       TGeomID           faceID = getMeshDS()->ShapeToIndex( fWOL );
2469       TopoDS_Shape        edge = getMeshDS()->IndexToShape( edgeID );
2470       if ( edge.ShapeType() != TopAbs_EDGE )
2471         continue; // shrink shape is VERTEX
2472
2473       TopoDS_Shape solid;
2474       PShapeIteratorPtr soIt = helper.GetAncestors(fWOL, *_mesh, TopAbs_SOLID);
2475       while ( soIt->more() && solid.IsNull() )
2476       {
2477         const TopoDS_Shape* so = soIt->next();
2478         if ( !so->IsSame( _sdVec[i]._solid ))
2479           solid = *so;
2480       }
2481       if ( solid.IsNull() )
2482         continue;
2483
2484       bool noShrinkE = false;
2485       SMESH_Algo*  algo = _mesh->GetSubMesh( solid )->GetAlgo();
2486       bool isStructured = ( algo && algo->GetName() == structAlgoName );
2487       size_t     iSolid = _solids.FindIndex( solid ) - 1;
2488       if ( iSolid < _sdVec.size() && _sdVec[ iSolid ]._ignoreFaceIds.count( faceID ))
2489       {
2490         // the adjacent SOLID has NO layers on fWOL;
2491         // shrink allowed if
2492         // - there are layers on the EDGE in the adjacent SOLID
2493         // - there are NO layers in the adjacent SOLID && algo is unstructured and computed later
2494         bool hasWLAdj = (_sdVec[iSolid]._shrinkShape2Shape.count( edgeID ));
2495         bool shrinkAllowed = (( hasWLAdj ) ||
2496                               ( !isStructured && setBefore( _sdVec[ i ], _sdVec[ iSolid ] )));
2497         noShrinkE = !shrinkAllowed;
2498       }
2499       else if ( iSolid < _sdVec.size() )
2500       {
2501         // the adjacent SOLID has layers on fWOL;
2502         // check if SOLID's mesh is unstructured and then try to set it
2503         // to be computed after the i-th solid
2504         if ( isStructured || !setBefore( _sdVec[ i ], _sdVec[ iSolid ] ))
2505           noShrinkE = true; // don't shrink fWOL
2506       }
2507       else
2508       {
2509         // the adjacent SOLID has NO layers at all
2510         noShrinkE = isStructured;
2511       }
2512
2513       if ( noShrinkE )
2514       {
2515         _sdVec[i]._noShrinkShapes.insert( edgeID );
2516
2517         // check if there is a collision with to-shrink-from EDGEs in iSolid
2518         // if ( iSolid < _sdVec.size() )
2519         // {
2520         //   shapes.Clear();
2521         //   TopExp::MapShapes( fWOL, TopAbs_EDGE, shapes);
2522         //   for ( int iE = 1; iE <= shapes.Extent(); ++iE )
2523         //   {
2524         //     const TopoDS_Edge& E = TopoDS::Edge( shapes( iE ));
2525         //     const TGeomID    eID = getMeshDS()->ShapeToIndex( E );
2526         //     if ( eID == edgeID ||
2527         //          !_sdVec[iSolid]._shrinkShape2Shape.count( eID ) ||
2528         //          _sdVec[i]._noShrinkShapes.count( eID ))
2529         //       continue;
2530         //     for ( int is1st = 0; is1st < 2; ++is1st )
2531         //     {
2532         //       TopoDS_Vertex V = helper.IthVertex( is1st, E );
2533         //       if ( _sdVec[i]._noShrinkShapes.count( getMeshDS()->ShapeToIndex( V ) ))
2534         //       {
2535         //         return error("No way to make a conformal mesh with "
2536         //                      "the given set of faces with layers", _sdVec[i]._index);
2537         //       }
2538         //     }
2539         //   }
2540         // }
2541       }
2542
2543       // add VERTEXes of the edge in _noShrinkShapes, which is necessary if
2544       // _shrinkShape2Shape is different in the adjacent SOLID
2545       for ( TopoDS_Iterator vIt( edge ); vIt.More(); vIt.Next() )
2546       {
2547         TGeomID vID = getMeshDS()->ShapeToIndex( vIt.Value() );
2548         bool noShrinkV = false, noShrinkIfAdjMeshed = false;
2549
2550         if ( iSolid < _sdVec.size() )
2551         {
2552           if ( _sdVec[ iSolid ]._ignoreFaceIds.count( faceID ))
2553           {
2554             map< TGeomID, TopoDS_Shape >::iterator i2S, i2SAdj;
2555             i2S    = _sdVec[i     ]._shrinkShape2Shape.find( vID );
2556             i2SAdj = _sdVec[iSolid]._shrinkShape2Shape.find( vID );
2557             if ( i2SAdj == _sdVec[iSolid]._shrinkShape2Shape.end() )
2558               noShrinkV = (( isStructured ) ||
2559                            ( noShrinkIfAdjMeshed = i2S->second.ShapeType() == TopAbs_EDGE ));
2560             else
2561               noShrinkV = ( ! i2S->second.IsSame( i2SAdj->second ));
2562           }
2563           else
2564           {
2565             noShrinkV = noShrinkE;
2566           }
2567         }
2568         else
2569         {
2570           // the adjacent SOLID has NO layers at all
2571           if ( isStructured )
2572           {
2573             noShrinkV = true;
2574           }
2575           else
2576           {
2577             noShrinkV = noShrinkIfAdjMeshed =
2578               ( _sdVec[i]._shrinkShape2Shape[ vID ].ShapeType() == TopAbs_EDGE );
2579           }
2580         }
2581
2582         if ( noShrinkV && noShrinkIfAdjMeshed )
2583         {
2584           // noShrinkV if FACEs in the adjacent SOLID are meshed
2585           PShapeIteratorPtr fIt = helper.GetAncestors( _sdVec[i]._shrinkShape2Shape[ vID ],
2586                                                        *_mesh, TopAbs_FACE, &solid );
2587           while ( fIt->more() )
2588           {
2589             const TopoDS_Shape* f = fIt->next();
2590             if ( !f->IsSame( fWOL ))
2591             {
2592               noShrinkV = ! _mesh->GetSubMesh( *f )->IsEmpty();
2593               break;
2594             }
2595           }
2596         }
2597         if ( noShrinkV )
2598           _sdVec[i]._noShrinkShapes.insert( vID );
2599       }
2600
2601     } // loop on _sdVec[i]._shrinkShape2Shape
2602   } // loop on _sdVec to fill in _SolidData::_noShrinkShapes
2603
2604
2605     // add FACEs of other SOLIDs to _ignoreFaceIds
2606   for ( size_t i = 0; i < _sdVec.size(); ++i )
2607   {
2608     shapes.Clear();
2609     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_FACE, shapes);
2610
2611     for ( exp.Init( _mesh->GetShapeToMesh(), TopAbs_FACE ); exp.More(); exp.Next() )
2612     {
2613       if ( !shapes.Contains( exp.Current() ))
2614         _sdVec[i]._ignoreFaceIds.insert( getMeshDS()->ShapeToIndex( exp.Current() ));
2615     }
2616   }
2617
2618   return true;
2619 }
2620
2621 //================================================================================
2622 /*!
2623  * \brief Finds FACEs w/o layers for a given SOLID by an hypothesis
2624  */
2625 //================================================================================
2626
2627 void _ViscousBuilder::getIgnoreFaces(const TopoDS_Shape&             solid,
2628                                      const StdMeshers_ViscousLayers* hyp,
2629                                      const TopoDS_Shape&             hypShape,
2630                                      set<TGeomID>&                   ignoreFaceIds)
2631 {
2632   TopExp_Explorer exp;
2633
2634   vector<TGeomID> ids = hyp->GetBndShapes();
2635   if ( hyp->IsToIgnoreShapes() ) // FACEs to ignore are given
2636   {
2637     for ( size_t ii = 0; ii < ids.size(); ++ii )
2638     {
2639       const TopoDS_Shape& s = getMeshDS()->IndexToShape( ids[ii] );
2640       if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2641         ignoreFaceIds.insert( ids[ii] );
2642     }
2643   }
2644   else // FACEs with layers are given
2645   {
2646     exp.Init( solid, TopAbs_FACE );
2647     for ( ; exp.More(); exp.Next() )
2648     {
2649       TGeomID faceInd = getMeshDS()->ShapeToIndex( exp.Current() );
2650       if ( find( ids.begin(), ids.end(), faceInd ) == ids.end() )
2651         ignoreFaceIds.insert( faceInd );
2652     }
2653   }
2654
2655   // ignore internal FACEs if inlets and outlets are specified
2656   if ( hyp->IsToIgnoreShapes() )
2657   {
2658     TopTools_IndexedDataMapOfShapeListOfShape solidsOfFace;
2659     TopExp::MapShapesAndAncestors( hypShape,
2660                                    TopAbs_FACE, TopAbs_SOLID, solidsOfFace);
2661
2662     for ( exp.Init( solid, TopAbs_FACE ); exp.More(); exp.Next() )
2663     {
2664       const TopoDS_Face& face = TopoDS::Face( exp.Current() );
2665       if ( SMESH_MesherHelper::NbAncestors( face, *_mesh, TopAbs_SOLID ) < 2 )
2666         continue;
2667
2668       int nbSolids = solidsOfFace.FindFromKey( face ).Extent();
2669       if ( nbSolids > 1 )
2670         ignoreFaceIds.insert( getMeshDS()->ShapeToIndex( face ));
2671     }
2672   }
2673 }
2674
2675 //================================================================================
2676 /*!
2677  * \brief Create the inner surface of the viscous layer and prepare data for infation
2678  */
2679 //================================================================================
2680
2681 bool _ViscousBuilder::makeLayer(_SolidData& data)
2682 {
2683   // make a map to find new nodes on sub-shapes shared with other SOLID
2684   map< TGeomID, TNode2Edge* >::iterator s2ne;
2685   map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
2686   for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
2687   {
2688     TGeomID shapeInd = s2s->first;
2689     for ( size_t i = 0; i < _sdVec.size(); ++i )
2690     {
2691       if ( _sdVec[i]._index == data._index ) continue;
2692       map< TGeomID, TopoDS_Shape >::iterator s2s2 = _sdVec[i]._shrinkShape2Shape.find( shapeInd );
2693       if ( s2s2 != _sdVec[i]._shrinkShape2Shape.end() &&
2694            *s2s == *s2s2 && !_sdVec[i]._n2eMap.empty() )
2695       {
2696         data._s2neMap.insert( make_pair( shapeInd, &_sdVec[i]._n2eMap ));
2697         break;
2698       }
2699     }
2700   }
2701
2702   // Create temporary faces and _LayerEdge's
2703
2704   debugMsg( "######################" );
2705   dumpFunction(SMESH_Comment("makeLayers_")<<data._index);
2706
2707   vector< _EdgesOnShape >& edgesByGeom = data._edgesOnShape;
2708
2709   data._stepSize = Precision::Infinite();
2710   data._stepSizeNodes[0] = 0;
2711
2712   SMESH_MesherHelper helper( *_mesh );
2713   helper.SetSubShape( data._solid );
2714   helper.SetElementsOnShape( true );
2715
2716   vector< const SMDS_MeshNode*> newNodes; // of a mesh face
2717   TNode2Edge::iterator n2e2;
2718
2719   // make _LayerEdge's
2720   for ( TopExp_Explorer exp( data._solid, TopAbs_FACE ); exp.More(); exp.Next() )
2721   {
2722     const TopoDS_Face& F = TopoDS::Face( exp.Current() );
2723     SMESH_subMesh*    sm = _mesh->GetSubMesh( F );
2724     const TGeomID     id = sm->GetId();
2725     if ( edgesByGeom[ id ]._shape.IsNull() )
2726       continue; // no layers
2727     SMESH_ProxyMesh::SubMesh* proxySub =
2728       data._proxyMesh->getFaceSubM( F, /*create=*/true);
2729
2730     SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
2731     if ( !smDS ) return error(SMESH_Comment("Not meshed face ") << id, data._index );
2732
2733     SMDS_ElemIteratorPtr eIt = smDS->GetElements();
2734     while ( eIt->more() )
2735     {
2736       const SMDS_MeshElement* face = eIt->next();
2737       double          faceMaxCosin = -1;
2738       _LayerEdge*     maxCosinEdge = 0;
2739       int             nbDegenNodes = 0;
2740
2741       newNodes.resize( face->NbCornerNodes() );
2742       for ( size_t i = 0 ; i < newNodes.size(); ++i )
2743       {
2744         const SMDS_MeshNode* n = face->GetNode( i );
2745         const int      shapeID = n->getshapeId();
2746         const bool onDegenShap = helper.IsDegenShape( shapeID );
2747         const bool onDegenEdge = ( onDegenShap && n->GetPosition()->GetDim() == 1 );
2748         if ( onDegenShap )
2749         {
2750           if ( onDegenEdge )
2751           {
2752             // substitute n on a degenerated EDGE with a node on a corresponding VERTEX
2753             const TopoDS_Shape& E = getMeshDS()->IndexToShape( shapeID );
2754             TopoDS_Vertex       V = helper.IthVertex( 0, TopoDS::Edge( E ));
2755             if ( const SMDS_MeshNode* vN = SMESH_Algo::VertexNode( V, getMeshDS() )) {
2756               n = vN;
2757               nbDegenNodes++;
2758             }
2759           }
2760           else
2761           {
2762             nbDegenNodes++;
2763           }
2764         }
2765         TNode2Edge::iterator n2e = data._n2eMap.insert({ n, nullptr }).first;
2766         if ( !(*n2e).second )
2767         {
2768           // add a _LayerEdge
2769           _LayerEdge* edge = _Factory::NewLayerEdge();
2770           edge->_nodes.push_back( n );
2771           n2e->second = edge;
2772           edgesByGeom[ shapeID ]._edges.push_back( edge );
2773           const bool noShrink = data._noShrinkShapes.count( shapeID );
2774
2775           SMESH_TNodeXYZ xyz( n );
2776
2777           // set edge data or find already refined _LayerEdge and get data from it
2778           if (( !noShrink                                                     ) &&
2779               ( n->GetPosition()->GetTypeOfPosition() != SMDS_TOP_FACE        ) &&
2780               (( s2ne = data._s2neMap.find( shapeID )) != data._s2neMap.end() ) &&
2781               (( n2e2 = (*s2ne).second->find( n )) != s2ne->second->end()     ))
2782           {
2783             _LayerEdge* foundEdge = (*n2e2).second;
2784             gp_XYZ        lastPos = edge->Copy( *foundEdge, edgesByGeom[ shapeID ], helper );
2785             foundEdge->_pos.push_back( lastPos );
2786             // location of the last node is modified and we restore it by foundEdge->_pos.back()
2787             const_cast< SMDS_MeshNode* >
2788               ( edge->_nodes.back() )->setXYZ( xyz.X(), xyz.Y(), xyz.Z() );
2789           }
2790           else
2791           {
2792             if ( !noShrink )
2793             {
2794               edge->_nodes.push_back( helper.AddNode( xyz.X(), xyz.Y(), xyz.Z() ));
2795             }
2796             if ( !setEdgeData( *edge, edgesByGeom[ shapeID ], helper, data ))
2797               return false;
2798
2799             if ( edge->_nodes.size() < 2 && !noShrink )
2800               edge->Block( data ); // a sole node is moved only if noShrink
2801           }
2802           dumpMove(edge->_nodes.back());
2803
2804           if ( edge->_cosin > faceMaxCosin && edge->_nodes.size() > 1 )
2805           {
2806             faceMaxCosin = edge->_cosin;
2807             maxCosinEdge = edge;
2808           }
2809         }
2810         newNodes[ i ] = n2e->second->_nodes.back();
2811
2812         if ( onDegenEdge )
2813           data._n2eMap.insert( make_pair( face->GetNode( i ), n2e->second ));
2814       }
2815       if ( newNodes.size() - nbDegenNodes < 2 )
2816         continue;
2817
2818       // create a temporary face
2819       const SMDS_MeshElement* newFace =
2820         new _TmpMeshFace( newNodes, --_tmpFaceID, face->GetShapeID(), face );
2821       proxySub->AddElement( newFace );
2822
2823       // compute inflation step size by min size of element on a convex surface
2824       if ( faceMaxCosin > theMinSmoothCosin )
2825         limitStepSize( data, face, maxCosinEdge );
2826
2827     } // loop on 2D elements on a FACE
2828   } // loop on FACEs of a SOLID to create _LayerEdge's
2829
2830
2831   // Set _LayerEdge::_neibors
2832   TNode2Edge::iterator n2e;
2833   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
2834   {
2835     _EdgesOnShape& eos = data._edgesOnShape[iS];
2836     for ( size_t i = 0; i < eos._edges.size(); ++i )
2837     {
2838       _LayerEdge* edge = eos._edges[i];
2839       TIDSortedNodeSet nearNodes;
2840       SMDS_ElemIteratorPtr fIt = edge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
2841       while ( fIt->more() )
2842       {
2843         const SMDS_MeshElement* f = fIt->next();
2844         if ( !data._ignoreFaceIds.count( f->getshapeId() ))
2845           nearNodes.insert( f->begin_nodes(), f->end_nodes() );
2846       }
2847       nearNodes.erase( edge->_nodes[0] );
2848       edge->_neibors.reserve( nearNodes.size() );
2849       TIDSortedNodeSet::iterator node = nearNodes.begin();
2850       for ( ; node != nearNodes.end(); ++node )
2851         if (( n2e = data._n2eMap.find( *node )) != data._n2eMap.end() )
2852           edge->_neibors.push_back( n2e->second );
2853     }
2854
2855     // Fix uv of nodes on periodic FACEs (bos #20643)
2856
2857     if ( eos.ShapeType() != TopAbs_EDGE ||
2858          eos.SWOLType()  != TopAbs_FACE ||
2859          eos.size() == 0 )
2860       continue;
2861
2862     const TopoDS_Face& F = TopoDS::Face( eos._sWOL );
2863     SMESH_MesherHelper faceHelper( *_mesh );
2864     faceHelper.SetSubShape( F );
2865     faceHelper.ToFixNodeParameters( true );
2866     if ( faceHelper.GetPeriodicIndex() == 0 )
2867       continue;
2868
2869     SMESHDS_SubMesh* smDS = getMeshDS()->MeshElements( F );
2870     if ( !smDS || smDS->GetNodes() == 0 )
2871       continue;
2872
2873     bool toCheck = true;
2874     const double tol = 2 * helper.MaxTolerance( F );
2875     for ( SMDS_NodeIteratorPtr nIt = smDS->GetNodes(); nIt->more(); )
2876     {
2877       const SMDS_MeshNode* node = nIt->next();
2878       gp_XY uvNew( Precision::Infinite(), 0 );
2879       if ( toCheck )
2880       {
2881         toCheck = false;
2882         gp_XY uv = faceHelper.GetNodeUV( F, node );
2883         if ( ! faceHelper.CheckNodeUV( F, node, uvNew, tol, /*force=*/true ))
2884           break; // projection on F failed
2885         if (( uv - uvNew ).Modulus() < Precision::Confusion() )
2886           break; // current uv is OK
2887       }
2888       faceHelper.CheckNodeUV( F, node, uvNew, tol, /*force=*/true );
2889     }
2890   }
2891
2892   data._epsilon = 1e-7;
2893   if ( data._stepSize < 1. )
2894     data._epsilon *= data._stepSize;
2895
2896   if ( !findShapesToSmooth( data )) // _LayerEdge::_maxLen is computed here
2897     return false;
2898
2899   // limit data._stepSize depending on surface curvature and fill data._convexFaces
2900   limitStepSizeByCurvature( data ); // !!! it must be before node substitution in _Simplex
2901
2902   // Set target nodes into _Simplex and _LayerEdge's to _2NearEdges
2903   const SMDS_MeshNode* nn[2];
2904   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
2905   {
2906     _EdgesOnShape& eos = data._edgesOnShape[iS];
2907     for ( size_t i = 0; i < eos._edges.size(); ++i )
2908     {
2909       _LayerEdge* edge = eos._edges[i];
2910       if ( edge->IsOnEdge() )
2911       {
2912         // get neighbor nodes
2913         bool hasData = ( edge->_2neibors->_edges[0] );
2914         if ( hasData ) // _LayerEdge is a copy of another one
2915         {
2916           nn[0] = edge->_2neibors->srcNode(0);
2917           nn[1] = edge->_2neibors->srcNode(1);
2918         }
2919         else if ( !findNeiborsOnEdge( edge, nn[0],nn[1], eos, data ))
2920         {
2921           return false;
2922         }
2923         // set neighbor _LayerEdge's
2924         for ( int j = 0; j < 2; ++j )
2925         {
2926           if (( n2e = data._n2eMap.find( nn[j] )) == data._n2eMap.end() )
2927             return error("_LayerEdge not found by src node", data._index);
2928           edge->_2neibors->_edges[j] = n2e->second;
2929         }
2930         if ( !hasData )
2931           edge->SetDataByNeighbors( nn[0], nn[1], eos, helper );
2932       }
2933
2934       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
2935       {
2936         _Simplex& s = edge->_simplices[j];
2937         s._nNext = data._n2eMap[ s._nNext ]->_nodes.back();
2938         s._nPrev = data._n2eMap[ s._nPrev ]->_nodes.back();
2939       }
2940
2941       // For an _LayerEdge on a degenerated EDGE, copy some data from
2942       // a corresponding _LayerEdge on a VERTEX
2943       // (issue 52453, pb on a downloaded SampleCase2-Tet-netgen-mephisto.hdf)
2944       if ( helper.IsDegenShape( edge->_nodes[0]->getshapeId() ))
2945       {
2946         // Generally we should not get here
2947         if ( eos.ShapeType() != TopAbs_EDGE )
2948           continue;
2949         TopoDS_Vertex V = helper.IthVertex( 0, TopoDS::Edge( eos._shape ));
2950         const SMDS_MeshNode* vN = SMESH_Algo::VertexNode( V, getMeshDS() );
2951         if (( n2e = data._n2eMap.find( vN )) == data._n2eMap.end() )
2952           continue;
2953         const _LayerEdge* vEdge = n2e->second;
2954         edge->_normal    = vEdge->_normal;
2955         edge->_lenFactor = vEdge->_lenFactor;
2956         edge->_cosin     = vEdge->_cosin;
2957       }
2958
2959     } // loop on data._edgesOnShape._edges
2960   } // loop on data._edgesOnShape
2961
2962   // fix _LayerEdge::_2neibors on EDGEs to smooth
2963   // map< TGeomID,Handle(Geom_Curve)>::iterator e2c = data._edge2curve.begin();
2964   // for ( ; e2c != data._edge2curve.end(); ++e2c )
2965   //   if ( !e2c->second.IsNull() )
2966   //   {
2967   //     if ( _EdgesOnShape* eos = data.GetShapeEdges( e2c->first ))
2968   //       data.Sort2NeiborsOnEdge( eos->_edges );
2969   //   }
2970
2971   dumpFunctionEnd();
2972   return true;
2973 }
2974
2975 //================================================================================
2976 /*!
2977  * \brief Compute inflation step size by min size of element on a convex surface
2978  */
2979 //================================================================================
2980
2981 void _ViscousBuilder::limitStepSize( _SolidData&             data,
2982                                      const SMDS_MeshElement* face,
2983                                      const _LayerEdge*       maxCosinEdge )
2984 {
2985   int iN = 0;
2986   double minSize = 10 * data._stepSize;
2987   const int nbNodes = face->NbCornerNodes();
2988   for ( int i = 0; i < nbNodes; ++i )
2989   {
2990     const SMDS_MeshNode* nextN = face->GetNode( SMESH_MesherHelper::WrapIndex( i+1, nbNodes ));
2991     const SMDS_MeshNode*  curN = face->GetNode( i );
2992     if ( nextN->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ||
2993          curN-> GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2994     {
2995       double dist = SMESH_TNodeXYZ( curN ).Distance( nextN );
2996       if ( dist < minSize )
2997         minSize = dist, iN = i;
2998     }
2999   }
3000   double newStep = 0.8 * minSize / maxCosinEdge->_lenFactor;
3001   if ( newStep < data._stepSize )
3002   {
3003     data._stepSize = newStep;
3004     data._stepSizeCoeff = 0.8 / maxCosinEdge->_lenFactor;
3005     data._stepSizeNodes[0] = face->GetNode( iN );
3006     data._stepSizeNodes[1] = face->GetNode( SMESH_MesherHelper::WrapIndex( iN+1, nbNodes ));
3007   }
3008 }
3009
3010 //================================================================================
3011 /*!
3012  * \brief Compute inflation step size by min size of element on a convex surface
3013  */
3014 //================================================================================
3015
3016 void _ViscousBuilder::limitStepSize( _SolidData& data, const double minSize )
3017 {
3018   if ( minSize < data._stepSize )
3019   {
3020     data._stepSize = minSize;
3021     if ( data._stepSizeNodes[0] )
3022     {
3023       double dist =
3024         SMESH_TNodeXYZ(data._stepSizeNodes[0]).Distance(data._stepSizeNodes[1]);
3025       data._stepSizeCoeff = data._stepSize / dist;
3026     }
3027   }
3028 }
3029
3030 //================================================================================
3031 /*!
3032  * \brief Limit data._stepSize by evaluating curvature of shapes and fill data._convexFaces
3033  */
3034 //================================================================================
3035
3036 void _ViscousBuilder::limitStepSizeByCurvature( _SolidData& data )
3037 {
3038   SMESH_MesherHelper helper( *_mesh );
3039
3040   BRepLProp_SLProps surfProp( 2, 1e-6 );
3041   data._convexFaces.clear();
3042
3043   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
3044   {
3045     _EdgesOnShape& eof = data._edgesOnShape[iS];
3046     if ( eof.ShapeType() != TopAbs_FACE ||
3047          data._ignoreFaceIds.count( eof._shapeID ))
3048       continue;
3049
3050     TopoDS_Face        F = TopoDS::Face( eof._shape );
3051     const TGeomID faceID = eof._shapeID;
3052
3053     BRepAdaptor_Surface surface( F, false );
3054     surfProp.SetSurface( surface );
3055
3056     _ConvexFace cnvFace;
3057     cnvFace._face = F;
3058     cnvFace._normalsFixed = false;
3059     cnvFace._isTooCurved = false;
3060     cnvFace._normalsFixedOnBorders = false;
3061
3062     double maxCurvature = cnvFace.GetMaxCurvature( data, eof, surfProp, helper );
3063     if ( maxCurvature > 0 )
3064     {
3065       limitStepSize( data, 0.9 / maxCurvature );
3066       findEdgesToUpdateNormalNearConvexFace( cnvFace, data, helper );
3067     }
3068     if ( !cnvFace._isTooCurved ) continue;
3069
3070     _ConvexFace & convFace =
3071       data._convexFaces.insert( make_pair( faceID, cnvFace )).first->second;
3072
3073     // skip a closed surface (data._convexFaces is useful anyway)
3074     bool isClosedF = false;
3075     helper.SetSubShape( F );
3076     if ( helper.HasRealSeam() )
3077     {
3078       // in the closed surface there must be a closed EDGE
3079       for ( TopExp_Explorer eIt( F, TopAbs_EDGE ); eIt.More() && !isClosedF; eIt.Next() )
3080         isClosedF = helper.IsClosedEdge( TopoDS::Edge( eIt.Current() ));
3081     }
3082     if ( isClosedF )
3083     {
3084       // limit _LayerEdge::_maxLen on the FACE
3085       const double oriFactor    = ( F.Orientation() == TopAbs_REVERSED ? +1. : -1. );
3086       const double minCurvature =
3087         1. / ( eof._hyp.GetTotalThickness() * ( 1 + theThickToIntersection ));
3088       map< TGeomID, _EdgesOnShape* >::iterator id2eos = cnvFace._subIdToEOS.find( faceID );
3089       if ( id2eos != cnvFace._subIdToEOS.end() )
3090       {
3091         _EdgesOnShape& eos = * id2eos->second;
3092         for ( size_t i = 0; i < eos._edges.size(); ++i )
3093         {
3094           _LayerEdge* ledge = eos._edges[ i ];
3095           gp_XY uv = helper.GetNodeUV( F, ledge->_nodes[0] );
3096           surfProp.SetParameters( uv.X(), uv.Y() );
3097           if ( surfProp.IsCurvatureDefined() )
3098           {
3099             double curvature = Max( surfProp.MaxCurvature() * oriFactor,
3100                                     surfProp.MinCurvature() * oriFactor );
3101             if ( curvature > minCurvature )
3102               ledge->SetMaxLen( Min( ledge->_maxLen, 1. / curvature ));
3103           }
3104         }
3105       }
3106       continue;
3107     }
3108
3109     // Fill _ConvexFace::_simplexTestEdges. These _LayerEdge's are used to detect
3110     // prism distortion.
3111     map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.find( faceID );
3112     if ( id2eos != convFace._subIdToEOS.end() && !id2eos->second->_edges.empty() )
3113     {
3114       // there are _LayerEdge's on the FACE it-self;
3115       // select _LayerEdge's near EDGEs
3116       _EdgesOnShape& eos = * id2eos->second;
3117       for ( size_t i = 0; i < eos._edges.size(); ++i )
3118       {
3119         _LayerEdge* ledge = eos._edges[ i ];
3120         for ( size_t j = 0; j < ledge->_simplices.size(); ++j )
3121           if ( ledge->_simplices[j]._nNext->GetPosition()->GetDim() < 2 )
3122           {
3123             // do not select _LayerEdge's neighboring sharp EDGEs
3124             bool sharpNbr = false;
3125             for ( size_t iN = 0; iN < ledge->_neibors.size()  && !sharpNbr; ++iN )
3126               sharpNbr = ( ledge->_neibors[iN]->_cosin > theMinSmoothCosin );
3127             if ( !sharpNbr )
3128               convFace._simplexTestEdges.push_back( ledge );
3129             break;
3130           }
3131       }
3132     }
3133     else
3134     {
3135       // where there are no _LayerEdge's on a _ConvexFace,
3136       // as e.g. on a fillet surface with no internal nodes - issue 22580,
3137       // so that collision of viscous internal faces is not detected by check of
3138       // intersection of _LayerEdge's with the viscous internal faces.
3139
3140       set< const SMDS_MeshNode* > usedNodes;
3141
3142       // look for _LayerEdge's with null _sWOL
3143       id2eos = convFace._subIdToEOS.begin();
3144       for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
3145       {
3146         _EdgesOnShape& eos = * id2eos->second;
3147         if ( !eos._sWOL.IsNull() )
3148           continue;
3149         for ( size_t i = 0; i < eos._edges.size(); ++i )
3150         {
3151           _LayerEdge* ledge = eos._edges[ i ];
3152           const SMDS_MeshNode* srcNode = ledge->_nodes[0];
3153           if ( !usedNodes.insert( srcNode ).second ) continue;
3154
3155           for ( size_t i = 0; i < ledge->_simplices.size(); ++i )
3156           {
3157             usedNodes.insert( ledge->_simplices[i]._nPrev );
3158             usedNodes.insert( ledge->_simplices[i]._nNext );
3159           }
3160           convFace._simplexTestEdges.push_back( ledge );
3161         }
3162       }
3163     }
3164   } // loop on FACEs of data._solid
3165 }
3166
3167 //================================================================================
3168 /*!
3169  * \brief Detect shapes (and _LayerEdge's on them) to smooth
3170  */
3171 //================================================================================
3172
3173 bool _ViscousBuilder::findShapesToSmooth( _SolidData& data )
3174 {
3175   // define allowed thickness
3176   computeGeomSize( data ); // compute data._geomSize and _LayerEdge::_maxLen
3177
3178
3179   // Find shapes needing smoothing; such a shape has _LayerEdge._normal on it's
3180   // boundary inclined to the shape at a sharp angle
3181
3182   TopTools_MapOfShape edgesOfSmooFaces;
3183   SMESH_MesherHelper helper( *_mesh );
3184   bool ok = true;
3185
3186   vector< _EdgesOnShape >& edgesByGeom = data._edgesOnShape;
3187   data._nbShapesToSmooth = 0;
3188
3189   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check FACEs
3190   {
3191     _EdgesOnShape& eos = edgesByGeom[iS];
3192     eos._toSmooth = false;
3193     if ( eos._edges.empty() || eos.ShapeType() != TopAbs_FACE )
3194       continue;
3195
3196     double tgtThick = eos._hyp.GetTotalThickness();
3197     SMESH_subMeshIteratorPtr subIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false );
3198     while ( subIt->more() && !eos._toSmooth )
3199     {
3200       TGeomID iSub = subIt->next()->GetId();
3201       const vector<_LayerEdge*>& eSub = edgesByGeom[ iSub ]._edges;
3202       if ( eSub.empty() ) continue;
3203
3204       double faceSize;
3205       for ( size_t i = 0; i < eSub.size() && !eos._toSmooth; ++i )
3206         if ( eSub[i]->_cosin > theMinSmoothCosin )
3207         {
3208           SMDS_ElemIteratorPtr fIt = eSub[i]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
3209           while ( fIt->more() && !eos._toSmooth )
3210           {
3211             const SMDS_MeshElement* face = fIt->next();
3212             if ( face->getshapeId() == eos._shapeID &&
3213                  getDistFromEdge( face, eSub[i]->_nodes[0], faceSize ))
3214             {
3215               eos._toSmooth = needSmoothing( eSub[i]->_cosin,
3216                                              tgtThick * eSub[i]->_lenFactor,
3217                                              faceSize);
3218             }
3219           }
3220         }
3221     }
3222     if ( eos._toSmooth )
3223     {
3224       for ( TopExp_Explorer eExp( edgesByGeom[iS]._shape, TopAbs_EDGE ); eExp.More(); eExp.Next() )
3225         edgesOfSmooFaces.Add( eExp.Current() );
3226
3227       data.PrepareEdgesToSmoothOnFace( &edgesByGeom[iS], /*substituteSrcNodes=*/false );
3228     }
3229     data._nbShapesToSmooth += eos._toSmooth;
3230
3231   }  // check FACEs
3232
3233   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check EDGEs
3234   {
3235     _EdgesOnShape& eos = edgesByGeom[iS];
3236     eos._edgeSmoother = NULL;
3237     if ( eos._edges.empty() || eos.ShapeType() != TopAbs_EDGE ) continue;
3238     if ( !eos._hyp.ToSmooth() ) continue;
3239
3240     const TopoDS_Edge& E = TopoDS::Edge( edgesByGeom[iS]._shape );
3241     if ( SMESH_Algo::isDegenerated( E ) || !edgesOfSmooFaces.Contains( E ))
3242       continue;
3243
3244     double tgtThick = eos._hyp.GetTotalThickness(), h0 = eos._hyp.Get1stLayerThickness();
3245     for ( TopoDS_Iterator vIt( E ); vIt.More() && !eos._toSmooth; vIt.Next() )
3246     {
3247       TGeomID iV = getMeshDS()->ShapeToIndex( vIt.Value() );
3248       vector<_LayerEdge*>& eV = edgesByGeom[ iV ]._edges;
3249       if ( eV.empty() || eV[0]->Is( _LayerEdge::MULTI_NORMAL )) continue;
3250       gp_Vec  eDir    = getEdgeDir( E, TopoDS::Vertex( vIt.Value() ), h0 );
3251       double angle    = eDir.Angle( eV[0]->_normal );
3252       double cosin    = Cos( angle );
3253       double cosinAbs = Abs( cosin );
3254       if ( cosinAbs > theMinSmoothCosin )
3255       {
3256         // always smooth analytic EDGEs
3257         Handle(Geom_Curve) curve = _Smoother1D::CurveForSmooth( E, eos, helper );
3258         eos._toSmooth = ! curve.IsNull();
3259
3260         // compare tgtThick with the length of an end segment
3261         SMDS_ElemIteratorPtr eIt = eV[0]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Edge);
3262         while ( eIt->more() && !eos._toSmooth )
3263         {
3264           const SMDS_MeshElement* endSeg = eIt->next();
3265           if ( endSeg->getshapeId() == (int) iS )
3266           {
3267             double segLen =
3268               SMESH_TNodeXYZ( endSeg->GetNode( 0 )).Distance( endSeg->GetNode( 1 ));
3269             eos._toSmooth = needSmoothing( cosinAbs, tgtThick * eV[0]->_lenFactor, segLen );
3270           }
3271         }
3272         if ( eos._toSmooth )
3273         {
3274           eos._edgeSmoother = new _Smoother1D( curve, eos );
3275
3276           // for ( size_t i = 0; i < eos._edges.size(); ++i )
3277           //   eos._edges[i]->Set( _LayerEdge::TO_SMOOTH );
3278         }
3279       }
3280     }
3281     data._nbShapesToSmooth += eos._toSmooth;
3282
3283   } // check EDGEs
3284
3285   // Reset _cosin if no smooth is allowed by the user
3286   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS )
3287   {
3288     _EdgesOnShape& eos = edgesByGeom[iS];
3289     if ( eos._edges.empty() ) continue;
3290
3291     if ( !eos._hyp.ToSmooth() )
3292       for ( size_t i = 0; i < eos._edges.size(); ++i )
3293         //eos._edges[i]->SetCosin( 0 ); // keep _cosin to use in limitMaxLenByCurvature()
3294         eos._edges[i]->_lenFactor = 1;
3295   }
3296
3297
3298   // Fill _eosC1 to make that C1 FACEs and EDGEs between them to be smoothed as a whole
3299
3300   TopTools_MapOfShape c1VV;
3301
3302   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check FACEs
3303   {
3304     _EdgesOnShape& eos = edgesByGeom[iS];
3305     if ( eos._edges.empty() ||
3306          eos.ShapeType() != TopAbs_FACE ||
3307          !eos._toSmooth )
3308       continue;
3309
3310     // check EDGEs of a FACE
3311     TopTools_MapOfShape checkedEE, allVV;
3312     list< SMESH_subMesh* > smQueue( 1, eos._subMesh ); // sm of FACEs
3313     while ( !smQueue.empty() )
3314     {
3315       SMESH_subMesh* sm = smQueue.front();
3316       smQueue.pop_front();
3317       SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(/*includeSelf=*/false);
3318       while ( smIt->more() )
3319       {
3320         sm = smIt->next();
3321         if ( sm->GetSubShape().ShapeType() == TopAbs_VERTEX )
3322           allVV.Add( sm->GetSubShape() );
3323         if ( sm->GetSubShape().ShapeType() != TopAbs_EDGE ||
3324              !checkedEE.Add( sm->GetSubShape() ))
3325           continue;
3326
3327         _EdgesOnShape*      eoe = data.GetShapeEdges( sm->GetId() );
3328         vector<_LayerEdge*>& eE = eoe->_edges;
3329         if ( eE.empty() || !eoe->_sWOL.IsNull() )
3330           continue;
3331
3332         bool isC1 = true; // check continuity along an EDGE
3333         for ( size_t i = 0; i < eE.size() && isC1; ++i )
3334           isC1 = ( Abs( eE[i]->_cosin ) < theMinSmoothCosin );
3335         if ( !isC1 )
3336           continue;
3337
3338         // check that mesh faces are C1 as well
3339         {
3340           gp_XYZ norm1, norm2;
3341           const SMDS_MeshNode*   n = eE[ eE.size() / 2 ]->_nodes[0];
3342           SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
3343           if ( !SMESH_MeshAlgos::FaceNormal( fIt->next(), norm1, /*normalized=*/true ))
3344             continue;
3345           while ( fIt->more() && isC1 )
3346             isC1 = ( SMESH_MeshAlgos::FaceNormal( fIt->next(), norm2, /*normalized=*/true ) &&
3347                      Abs( norm1 * norm2 ) >= ( 1. - theMinSmoothCosin ));
3348           if ( !isC1 )
3349             continue;
3350         }
3351
3352         // add the EDGE and an adjacent FACE to _eosC1
3353         PShapeIteratorPtr fIt = helper.GetAncestors( sm->GetSubShape(), *_mesh, TopAbs_FACE );
3354         while ( const TopoDS_Shape* face = fIt->next() )
3355         {
3356           _EdgesOnShape* eof = data.GetShapeEdges( *face );
3357           if ( !eof ) continue; // other solid
3358           if ( eos._shapeID == eof->_shapeID ) continue;
3359           if ( !eos.HasC1( eof ))
3360           {
3361             // check the FACEs
3362             eos._eosC1.push_back( eof );
3363             eof->_toSmooth = false;
3364             data.PrepareEdgesToSmoothOnFace( eof, /*substituteSrcNodes=*/false );
3365             smQueue.push_back( eof->_subMesh );
3366           }
3367           if ( !eos.HasC1( eoe ))
3368           {
3369             eos._eosC1.push_back( eoe );
3370             eoe->_toSmooth = false;
3371             data.PrepareEdgesToSmoothOnFace( eoe, /*substituteSrcNodes=*/false );
3372           }
3373         }
3374       }
3375     }
3376     if ( eos._eosC1.empty() )
3377       continue;
3378
3379     // check VERTEXes of C1 FACEs
3380     TopTools_MapIteratorOfMapOfShape vIt( allVV );
3381     for ( ; vIt.More(); vIt.Next() )
3382     {
3383       _EdgesOnShape* eov = data.GetShapeEdges( vIt.Key() );
3384       if ( !eov || eov->_edges.empty() || !eov->_sWOL.IsNull() )
3385         continue;
3386
3387       bool isC1 = true; // check if all adjacent FACEs are in eos._eosC1
3388       PShapeIteratorPtr fIt = helper.GetAncestors( vIt.Key(), *_mesh, TopAbs_FACE );
3389       while ( const TopoDS_Shape* face = fIt->next() )
3390       {
3391         _EdgesOnShape* eof = data.GetShapeEdges( *face );
3392         if ( !eof ) continue; // other solid
3393         isC1 = ( face->IsSame( eos._shape ) || eos.HasC1( eof ));
3394         if ( !isC1 )
3395           break;
3396       }
3397       if ( isC1 )
3398       {
3399         eos._eosC1.push_back( eov );
3400         data.PrepareEdgesToSmoothOnFace( eov, /*substituteSrcNodes=*/false );
3401         c1VV.Add( eov->_shape );
3402       }
3403     }
3404
3405   } // fill _eosC1 of FACEs
3406
3407
3408   // Find C1 EDGEs
3409
3410   vector< pair< _EdgesOnShape*, gp_XYZ > > dirOfEdges;
3411
3412   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check VERTEXes
3413   {
3414     _EdgesOnShape& eov = edgesByGeom[iS];
3415     if ( eov._edges.empty() ||
3416          eov.ShapeType() != TopAbs_VERTEX ||
3417          c1VV.Contains( eov._shape ))
3418       continue;
3419     const TopoDS_Vertex& V = TopoDS::Vertex( eov._shape );
3420
3421     // get directions of surrounding EDGEs
3422     dirOfEdges.clear();
3423     PShapeIteratorPtr fIt = helper.GetAncestors( eov._shape, *_mesh, TopAbs_EDGE );
3424     while ( const TopoDS_Shape* e = fIt->next() )
3425     {
3426       _EdgesOnShape* eoe = data.GetShapeEdges( *e );
3427       if ( !eoe ) continue; // other solid
3428       gp_XYZ eDir = getEdgeDir( TopoDS::Edge( *e ), V, eoe->_hyp.Get1stLayerThickness() );
3429       if ( !Precision::IsInfinite( eDir.X() ))
3430         dirOfEdges.push_back( make_pair( eoe, eDir.Normalized() ));
3431     }
3432
3433     // find EDGEs with C1 directions
3434     for ( size_t i = 0; i < dirOfEdges.size(); ++i )
3435       for ( size_t j = i+1; j < dirOfEdges.size(); ++j )
3436         if ( dirOfEdges[i].first && dirOfEdges[j].first )
3437         {
3438           double dot = dirOfEdges[i].second * dirOfEdges[j].second;
3439           bool isC1 = ( dot < - ( 1. - theMinSmoothCosin ));
3440           if ( isC1 )
3441           {
3442             double maxEdgeLen = 3 * Min( eov._edges[0]->_maxLen, eov._hyp.GetTotalThickness() );
3443             for ( int isJ = 0; isJ < 2; ++isJ ) // loop on [i,j]
3444             {
3445               size_t k = isJ ? j : i;
3446               const TopoDS_Edge& e = TopoDS::Edge( dirOfEdges[k].first->_shape );
3447               double eLen = SMESH_Algo::EdgeLength( e );
3448               if ( eLen < maxEdgeLen )
3449               {
3450                 TopoDS_Shape oppV = SMESH_MesherHelper::IthVertex( 0, e );
3451                 if ( oppV.IsSame( V ))
3452                   oppV = SMESH_MesherHelper::IthVertex( 1, e );
3453                 _EdgesOnShape* eovOpp = data.GetShapeEdges( oppV );
3454                 if ( dirOfEdges[k].second * eovOpp->_edges[0]->_normal < 0 )
3455                   eov._eosC1.push_back( dirOfEdges[k].first );
3456               }
3457               dirOfEdges[k].first = 0;
3458             }
3459           }
3460         }
3461   } // fill _eosC1 of VERTEXes
3462
3463
3464
3465   return ok;
3466 }
3467
3468 //================================================================================
3469 /*!
3470  * \brief Set up _SolidData::_edgesOnShape
3471  */
3472 //================================================================================
3473
3474 int _ViscousBuilder::makeEdgesOnShape()
3475 {
3476   const int nbShapes = getMeshDS()->MaxShapeIndex();
3477   int nbSolidsWL = 0;
3478
3479   for ( size_t i = 0; i < _sdVec.size(); ++i )
3480   {
3481     _SolidData& data = _sdVec[ i ];
3482     vector< _EdgesOnShape >& edgesByGeom = data._edgesOnShape;
3483     edgesByGeom.resize( nbShapes+1 );
3484
3485     // set data of _EdgesOnShape's
3486     int nbShapesWL = 0;
3487     if ( SMESH_subMesh* sm = _mesh->GetSubMesh( data._solid ))
3488     {
3489       SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(/*includeSelf=*/false);
3490       while ( smIt->more() )
3491       {
3492         sm = smIt->next();
3493         if ( sm->GetSubShape().ShapeType() == TopAbs_FACE &&
3494              data._ignoreFaceIds.count( sm->GetId() ))
3495           continue;
3496
3497         setShapeData( edgesByGeom[ sm->GetId() ], sm, data );
3498
3499         nbShapesWL += ( sm->GetSubShape().ShapeType() == TopAbs_FACE );
3500       }
3501     }
3502     if ( nbShapesWL == 0 ) // no shapes with layers in a SOLID
3503     {
3504       data._done = true;
3505       SMESHUtils::FreeVector( edgesByGeom );
3506     }
3507     else
3508     {
3509       ++nbSolidsWL;
3510     }
3511   }
3512   return nbSolidsWL;
3513 }
3514
3515 //================================================================================
3516 /*!
3517  * \brief initialize data of _EdgesOnShape
3518  */
3519 //================================================================================
3520
3521 void _ViscousBuilder::setShapeData( _EdgesOnShape& eos,
3522                                     SMESH_subMesh* sm,
3523                                     _SolidData&    data )
3524 {
3525   if ( !eos._shape.IsNull() ||
3526        sm->GetSubShape().ShapeType() == TopAbs_WIRE )
3527     return;
3528
3529   SMESH_MesherHelper helper( *_mesh );
3530
3531   eos._subMesh = sm;
3532   eos._shapeID = sm->GetId();
3533   eos._shape   = sm->GetSubShape();
3534   if ( eos.ShapeType() == TopAbs_FACE )
3535     eos._shape.Orientation( helper.GetSubShapeOri( data._solid, eos._shape ));
3536   eos._toSmooth = false;
3537   eos._data = &data;
3538   eos._mapper2D = nullptr;
3539
3540   // set _SWOL
3541   map< TGeomID, TopoDS_Shape >::const_iterator s2s =
3542     data._shrinkShape2Shape.find( eos._shapeID );
3543   if ( s2s != data._shrinkShape2Shape.end() )
3544     eos._sWOL = s2s->second;
3545
3546   eos._isRegularSWOL = true;
3547   if ( eos.SWOLType() == TopAbs_FACE )
3548   {
3549     const TopoDS_Face& F = TopoDS::Face( eos._sWOL );
3550     Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( F );
3551     eos._isRegularSWOL = ( ! surface->HasSingularities( 1e-7 ));
3552   }
3553
3554   // set _hyp
3555   if ( data._hyps.size() == 1 )
3556   {
3557     eos._hyp = data._hyps.back();
3558   }
3559   else
3560   {
3561     // compute average StdMeshers_ViscousLayers parameters
3562     map< TGeomID, const StdMeshers_ViscousLayers* >::iterator f2hyp;
3563     if ( eos.ShapeType() == TopAbs_FACE )
3564     {
3565       if (( f2hyp = data._face2hyp.find( eos._shapeID )) != data._face2hyp.end() )
3566         eos._hyp = f2hyp->second;
3567     }
3568     else
3569     {
3570       PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
3571       while ( const TopoDS_Shape* face = fIt->next() )
3572       {
3573         TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
3574         if (( f2hyp = data._face2hyp.find( faceID )) != data._face2hyp.end() )
3575           eos._hyp.Add( f2hyp->second );
3576       }
3577     }
3578   }
3579
3580   // set _faceNormals
3581   if ( ! eos._hyp.UseSurfaceNormal() )
3582   {
3583     if ( eos.ShapeType() == TopAbs_FACE ) // get normals to elements on a FACE
3584     {
3585       SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
3586       if ( !smDS ) return;
3587       eos._faceNormals.reserve( smDS->NbElements() );
3588
3589       double oriFactor = helper.IsReversedSubMesh( TopoDS::Face( eos._shape )) ? 1.: -1.;
3590       SMDS_ElemIteratorPtr eIt = smDS->GetElements();
3591       for ( ; eIt->more(); )
3592       {
3593         const SMDS_MeshElement* face = eIt->next();
3594         gp_XYZ&                 norm = eos._faceNormals[face];
3595         if ( !SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
3596           norm.SetCoord( 0,0,0 );
3597         norm *= oriFactor;
3598       }
3599     }
3600     else // find EOS of adjacent FACEs
3601     {
3602       PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
3603       while ( const TopoDS_Shape* face = fIt->next() )
3604       {
3605         TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
3606         eos._faceEOS.push_back( & data._edgesOnShape[ faceID ]);
3607         if ( eos._faceEOS.back()->_shape.IsNull() )
3608           // avoid using uninitialised _shapeID in GetNormal()
3609           eos._faceEOS.back()->_shapeID = faceID;
3610       }
3611     }
3612   }
3613 }
3614
3615 //================================================================================
3616 /*!
3617  * \brief Returns normal of a face
3618  */
3619 //================================================================================
3620
3621 bool _EdgesOnShape::GetNormal( const SMDS_MeshElement* face, gp_Vec& norm )
3622 {
3623   bool ok = false;
3624   _EdgesOnShape* eos = 0;
3625
3626   if ( face->getshapeId() == _shapeID )
3627   {
3628     eos = this;
3629   }
3630   else
3631   {
3632     for ( size_t iF = 0; iF < _faceEOS.size() && !eos; ++iF )
3633       if ( face->getshapeId() == _faceEOS[ iF ]->_shapeID )
3634         eos = _faceEOS[ iF ];
3635   }
3636
3637   if (( eos ) &&
3638       ( ok = ( eos->_faceNormals.count( face ) )))
3639   {
3640     norm = eos->_faceNormals[ face ];
3641   }
3642   else if ( !eos )
3643   {
3644     debugMsg( "_EdgesOnShape::Normal() failed for face "<<face->GetID()
3645               << " on _shape #" << _shapeID );
3646   }
3647   return ok;
3648 }
3649
3650 //================================================================================
3651 /*!
3652  * \brief EdgesOnShape destructor
3653  */
3654 //================================================================================
3655
3656 _EdgesOnShape::~_EdgesOnShape()
3657 {
3658   delete _edgeSmoother;
3659   delete _mapper2D;
3660 }
3661
3662 //================================================================================
3663 /*!
3664  * \brief Set data of _LayerEdge needed for smoothing
3665  */
3666 //================================================================================
3667
3668 bool _ViscousBuilder::setEdgeData(_LayerEdge&         edge,
3669                                   _EdgesOnShape&      eos,
3670                                   SMESH_MesherHelper& helper,
3671                                   _SolidData&         data)
3672 {
3673   const SMDS_MeshNode* node = edge._nodes[0]; // source node
3674
3675   edge._len          = 0;
3676   edge._maxLen       = Precision::Infinite();
3677   edge._minAngle     = 0;
3678   edge._2neibors     = 0;
3679   edge._curvature    = 0;
3680   edge._flags        = 0;
3681   edge._smooFunction = 0;
3682
3683   // --------------------------
3684   // Compute _normal and _cosin
3685   // --------------------------
3686
3687   edge._cosin     = 0;
3688   edge._lenFactor = 1.;
3689   edge._normal.SetCoord(0,0,0);
3690   _Simplex::GetSimplices( node, edge._simplices, data._ignoreFaceIds, &data );
3691
3692   int totalNbFaces = 0;
3693   TopoDS_Face F;
3694   std::pair< TopoDS_Face, gp_XYZ > face2Norm[20];
3695   gp_Vec geomNorm;
3696   bool normOK = true;
3697
3698   const bool onShrinkShape = !eos._sWOL.IsNull();
3699   const bool useGeometry   = (( eos._hyp.UseSurfaceNormal() ) ||
3700                               ( eos.ShapeType() != TopAbs_FACE /*&& !onShrinkShape*/ ));
3701
3702   // get geom FACEs the node lies on
3703   //if ( useGeometry )
3704   {
3705     set<TGeomID> faceIds;
3706     if  ( eos.ShapeType() == TopAbs_FACE )
3707     {
3708       faceIds.insert( eos._shapeID );
3709     }
3710     else
3711     {
3712       SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
3713       while ( fIt->more() )
3714         faceIds.insert( fIt->next()->getshapeId() );
3715     }
3716     set<TGeomID>::iterator id = faceIds.begin();
3717     for ( ; id != faceIds.end(); ++id )
3718     {
3719       const TopoDS_Shape& s = getMeshDS()->IndexToShape( *id );
3720       if ( s.IsNull() || s.ShapeType() != TopAbs_FACE || data._ignoreFaceIds.count( *id ))
3721         continue;
3722       F = TopoDS::Face( s );
3723       face2Norm[ totalNbFaces ].first = F;
3724       totalNbFaces++;
3725     }
3726   }
3727
3728   // find _normal
3729   bool fromVonF = false;
3730   if ( useGeometry )
3731   {
3732     fromVonF = ( eos.ShapeType() == TopAbs_VERTEX &&
3733                  eos.SWOLType()  == TopAbs_FACE  &&
3734                  totalNbFaces > 1 );
3735
3736     if ( onShrinkShape && !fromVonF ) // one of faces the node is on has no layers
3737     {
3738       if ( eos.SWOLType() == TopAbs_EDGE )
3739       {
3740         // inflate from VERTEX along EDGE
3741         edge._normal = getEdgeDir( TopoDS::Edge( eos._sWOL ), TopoDS::Vertex( eos._shape ),
3742                                    eos._hyp.Get1stLayerThickness(), &eos._isRegularSWOL );
3743       }
3744       else if ( eos.ShapeType() == TopAbs_VERTEX )
3745       {
3746         // inflate from VERTEX along FACE
3747         edge._normal = getFaceDir( TopoDS::Face( eos._sWOL ), TopoDS::Vertex( eos._shape ),
3748                                    node, helper, normOK/*, &edge._cosin*/);
3749       }
3750       else
3751       {
3752         // inflate from EDGE along FACE
3753         edge._normal = getFaceDir( TopoDS::Face( eos._sWOL ), TopoDS::Edge( eos._shape ),
3754                                    node, helper, normOK);
3755       }
3756     }
3757     else // layers are on all FACEs of SOLID the node is on (or fromVonF)
3758     {
3759       if ( fromVonF )
3760         face2Norm[ totalNbFaces++ ].first = TopoDS::Face( eos._sWOL );
3761
3762       int nbOkNorms = 0;
3763       for ( int iF = totalNbFaces - 1; iF >= 0; --iF )
3764       {
3765         F = face2Norm[ iF ].first;
3766         geomNorm = getFaceNormal( node, F, helper, normOK );
3767         if ( !normOK ) continue;
3768         nbOkNorms++;
3769
3770         if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3771           geomNorm.Reverse();
3772         face2Norm[ iF ].second = geomNorm.XYZ();
3773         edge._normal += geomNorm.XYZ();
3774       }
3775       if ( nbOkNorms == 0 )
3776         return error(SMESH_Comment("Can't get normal to node ") << node->GetID(), data._index);
3777
3778       if ( totalNbFaces >= 3 )
3779       {
3780         edge._normal = getNormalByOffset( &edge, face2Norm, totalNbFaces, fromVonF );
3781       }
3782
3783       if ( edge._normal.Modulus() < 1e-3 && nbOkNorms > 1 )
3784       {
3785         // opposite normals, re-get normals at shifted positions (IPAL 52426)
3786         edge._normal.SetCoord( 0,0,0 );
3787         for ( int iF = 0; iF < totalNbFaces - fromVonF; ++iF )
3788         {
3789           const TopoDS_Face& F = face2Norm[iF].first;
3790           geomNorm = getFaceNormal( node, F, helper, normOK, /*shiftInside=*/true );
3791           if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3792             geomNorm.Reverse();
3793           if ( normOK )
3794             face2Norm[ iF ].second = geomNorm.XYZ();
3795           edge._normal += face2Norm[ iF ].second;
3796         }
3797       }
3798     }
3799   }
3800   else // !useGeometry - get _normal using surrounding mesh faces
3801   {
3802     edge._normal = getWeigthedNormal( &edge );
3803
3804     // set<TGeomID> faceIds;
3805     //
3806     // SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
3807     // while ( fIt->more() )
3808     // {
3809     //   const SMDS_MeshElement* face = fIt->next();
3810     //   if ( eos.GetNormal( face, geomNorm ))
3811     //   {
3812     //     if ( onShrinkShape && !faceIds.insert( face->getshapeId() ).second )
3813     //       continue; // use only one mesh face on FACE
3814     //     edge._normal += geomNorm.XYZ();
3815     //     totalNbFaces++;
3816     //   }
3817     // }
3818   }
3819
3820   // compute _cosin
3821   //if ( eos._hyp.UseSurfaceNormal() )
3822   {
3823     switch ( eos.ShapeType() )
3824     {
3825     case TopAbs_FACE: {
3826       edge._cosin = 0;
3827       break;
3828     }
3829     case TopAbs_EDGE: {
3830       TopoDS_Edge E    = TopoDS::Edge( eos._shape );
3831       gp_Vec inFaceDir = getFaceDir( F, E, node, helper, normOK );
3832       double angle     = inFaceDir.Angle( edge._normal ); // [0,PI]
3833       edge._cosin      = Cos( angle );
3834       break;
3835     }
3836     case TopAbs_VERTEX: {
3837       TopoDS_Vertex V  = TopoDS::Vertex( eos._shape );
3838       gp_Vec inFaceDir = getFaceDir( face2Norm[0].first , V, node, helper, normOK );
3839       double angle     = inFaceDir.Angle( edge._normal ); // [0,PI]
3840       edge._cosin      = Cos( angle );
3841       if ( fromVonF )
3842         totalNbFaces--;
3843       if ( totalNbFaces > 1 || helper.IsSeamShape( node->getshapeId() ))
3844         for ( int iF = 1; iF < totalNbFaces; ++iF )
3845         {
3846           F = face2Norm[ iF ].first;
3847           inFaceDir = getFaceDir( F, V, node, helper, normOK=true );
3848           if ( normOK ) {
3849             if ( onShrinkShape )
3850             {
3851               gp_Vec faceNorm = getFaceNormal( node, F, helper, normOK );
3852               if ( !normOK ) continue;
3853               if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3854                 faceNorm.Reverse();
3855               angle = 0.5 * M_PI - faceNorm.Angle( edge._normal );
3856               if ( inFaceDir * edge._normal < 0 )
3857                 angle = M_PI - angle;
3858             }
3859             else
3860             {
3861               angle = inFaceDir.Angle( edge._normal );
3862             }
3863             double cosin = Cos( angle );
3864             if ( Abs( cosin ) > Abs( edge._cosin ))
3865               edge._cosin = cosin;
3866           }
3867         }
3868       break;
3869     }
3870     default:
3871       return error(SMESH_Comment("Invalid shape position of node ")<<node, data._index);
3872     }
3873   }
3874
3875   double normSize = edge._normal.SquareModulus();
3876   if ( normSize < numeric_limits<double>::min() )
3877     return error(SMESH_Comment("Bad normal at node ")<< node->GetID(), data._index );
3878
3879   edge._normal /= sqrt( normSize );
3880
3881   if ( edge.Is( _LayerEdge::MULTI_NORMAL ) && edge._nodes.size() == 2 )
3882   {
3883     getMeshDS()->RemoveFreeNode( edge._nodes.back(), 0, /*fromGroups=*/false );
3884     edge._nodes.resize( 1 );
3885     edge._normal.SetCoord( 0,0,0 );
3886     edge.SetMaxLen( 0 );
3887   }
3888
3889   // Set the rest data
3890   // --------------------
3891
3892   double realLenFactor = edge.SetCosin( edge._cosin ); // to update edge._lenFactor
3893   // if ( realLenFactor > 3 )
3894   // {
3895   //   edge._cosin = 1;
3896   //   if ( onShrinkShape )
3897   //   {
3898   //     edge.Set( _LayerEdge::RISKY_SWOL );
3899   //     edge._lenFactor = 2;
3900   //   }
3901   //   else
3902   //   {
3903   //     edge._lenFactor = 1;
3904   //   }
3905   // }
3906
3907   if ( onShrinkShape )
3908   {
3909     const SMDS_MeshNode* tgtNode = edge._nodes.back();
3910     if ( SMESHDS_SubMesh* sm = getMeshDS()->MeshElements( data._solid ))
3911       sm->RemoveNode( tgtNode );
3912
3913     // set initial position which is parameters on _sWOL in this case
3914     if ( eos.SWOLType() == TopAbs_EDGE )
3915     {
3916       double u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), node, 0, &normOK );
3917       edge._pos.push_back( gp_XYZ( u, 0, 0 ));
3918       if ( edge._nodes.size() > 1 )
3919         getMeshDS()->SetNodeOnEdge( tgtNode, TopoDS::Edge( eos._sWOL ), u );
3920     }
3921     else // eos.SWOLType() == TopAbs_FACE
3922     {
3923       gp_XY uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), node, 0, &normOK );
3924       edge._pos.push_back( gp_XYZ( uv.X(), uv.Y(), 0));
3925       if ( edge._nodes.size() > 1 )
3926         getMeshDS()->SetNodeOnFace( tgtNode, TopoDS::Face( eos._sWOL ), uv.X(), uv.Y() );
3927     }
3928
3929     //if ( edge._nodes.size() > 1 ) -- allow RISKY_SWOL on noShrink shape
3930     {
3931       // check if an angle between a FACE with layers and SWOL is sharp,
3932       // else the edge should not inflate
3933       F.Nullify();
3934       for ( int iF = 0; iF < totalNbFaces  &&  F.IsNull();  ++iF ) // find a FACE with VL
3935         if ( ! helper.IsSubShape( eos._sWOL, face2Norm[iF].first ))
3936           F = face2Norm[iF].first;
3937       if ( !F.IsNull())
3938       {
3939         geomNorm = getFaceNormal( node, F, helper, normOK );
3940         if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3941           geomNorm.Reverse(); // inside the SOLID
3942         if ( geomNorm * edge._normal < -0.001 )
3943         {
3944           if ( edge._nodes.size() > 1 )
3945           {
3946             getMeshDS()->RemoveFreeNode( tgtNode, 0, /*fromGroups=*/false );
3947             edge._nodes.resize( 1 );
3948           }
3949         }
3950         else if ( realLenFactor > 3 ) ///  -- moved to SetCosin()
3951           //else if ( edge._lenFactor > 3 )
3952         {
3953           edge._lenFactor = 2;
3954           edge.Set( _LayerEdge::RISKY_SWOL );
3955         }
3956       }
3957     }
3958   }
3959   else
3960   {
3961     edge._pos.push_back( SMESH_TNodeXYZ( node ));
3962
3963     if ( eos.ShapeType() == TopAbs_FACE )
3964     {
3965       double angle;
3966       for ( size_t i = 0; i < edge._simplices.size(); ++i )
3967       {
3968         edge._simplices[i].IsMinAngleOK( edge._pos.back(), angle );
3969         edge._minAngle = Max( edge._minAngle, angle ); // "angle" is actually cosine
3970       }
3971     }
3972   }
3973
3974   // Set neighbor nodes for a _LayerEdge based on EDGE
3975
3976   if ( eos.ShapeType() == TopAbs_EDGE /*||
3977        ( onShrinkShape && posType == SMDS_TOP_VERTEX && fabs( edge._cosin ) < 1e-10 )*/)
3978   {
3979     edge._2neibors = _Factory::NewNearEdges();
3980     // target nodes instead of source ones will be set later
3981   }
3982
3983   return true;
3984 }
3985
3986 //================================================================================
3987 /*!
3988  * \brief Return normal to a FACE at a node
3989  *  \param [in] n - node
3990  *  \param [in] face - FACE
3991  *  \param [in] helper - helper
3992  *  \param [out] isOK - true or false
3993  *  \param [in] shiftInside - to find normal at a position shifted inside the face
3994  *  \return gp_XYZ - normal
3995  */
3996 //================================================================================
3997
3998 gp_XYZ _ViscousBuilder::getFaceNormal(const SMDS_MeshNode* node,
3999                                       const TopoDS_Face&   face,
4000                                       SMESH_MesherHelper&  helper,
4001                                       bool&                isOK,
4002                                       bool                 shiftInside)
4003 {
4004   gp_XY uv;
4005   if ( shiftInside )
4006   {
4007     // get a shifted position
4008     gp_Pnt p = SMESH_TNodeXYZ( node );
4009     gp_XYZ shift( 0,0,0 );
4010     TopoDS_Shape S = helper.GetSubShapeByNode( node, helper.GetMeshDS() );
4011     switch ( S.ShapeType() ) {
4012     case TopAbs_VERTEX:
4013     {
4014       shift = getFaceDir( face, TopoDS::Vertex( S ), node, helper, isOK );
4015       break;
4016     }
4017     case TopAbs_EDGE:
4018     {
4019       shift = getFaceDir( face, TopoDS::Edge( S ), node, helper, isOK );
4020       break;
4021     }
4022     default:
4023       isOK = false;
4024     }
4025     if ( isOK )
4026       shift.Normalize();
4027     p.Translate( shift * 1e-5 );
4028
4029     TopLoc_Location loc;
4030     GeomAPI_ProjectPointOnSurf& projector = helper.GetProjector( face, loc, 1e-7 );
4031
4032     if ( !loc.IsIdentity() ) p.Transform( loc.Transformation().Inverted() );
4033     
4034     projector.Perform( p );
4035     if ( !projector.IsDone() || projector.NbPoints() < 1 )
4036     {
4037       isOK = false;
4038       return p.XYZ();
4039     }
4040     Standard_Real U,V;
4041     projector.LowerDistanceParameters(U,V);
4042     uv.SetCoord( U,V );
4043   }
4044   else
4045   {
4046     uv = helper.GetNodeUV( face, node, 0, &isOK );
4047   }
4048
4049   gp_Dir normal;
4050   isOK = false;
4051
4052   Handle(Geom_Surface) surface = BRep_Tool::Surface( face );
4053
4054   if ( !shiftInside &&
4055        helper.IsDegenShape( node->getshapeId() ) &&
4056        getFaceNormalAtSingularity( uv, face, helper, normal ))
4057   {
4058     isOK = true;
4059     return normal.XYZ();
4060   }
4061
4062   int pointKind = GeomLib::NormEstim( surface, uv, 1e-5, normal );
4063   enum { REGULAR = 0, QUASYSINGULAR, CONICAL, IMPOSSIBLE };
4064
4065   if ( pointKind == IMPOSSIBLE &&
4066        node->GetPosition()->GetDim() == 2 ) // node inside the FACE
4067   {
4068     // probably NormEstim() failed due to a too high tolerance
4069     pointKind = GeomLib::NormEstim( surface, uv, 1e-20, normal );
4070     isOK = ( pointKind < IMPOSSIBLE );
4071   }
4072   if ( pointKind < IMPOSSIBLE )
4073   {
4074     if ( pointKind != REGULAR &&
4075          !shiftInside &&
4076          node->GetPosition()->GetDim() < 2 ) // FACE boundary
4077     {
4078       gp_XYZ normShift = getFaceNormal( node, face, helper, isOK, /*shiftInside=*/true );
4079       if ( normShift * normal.XYZ() < 0. )
4080         normal = normShift;
4081     }
4082     isOK = true;
4083   }
4084
4085   if ( !isOK ) // hard singularity, to call with shiftInside=true ?
4086   {
4087     const TGeomID faceID = helper.GetMeshDS()->ShapeToIndex( face );
4088
4089     SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
4090     while ( fIt->more() )
4091     {
4092       const SMDS_MeshElement* f = fIt->next();
4093       if ( f->getshapeId() == faceID )
4094       {
4095         isOK = SMESH_MeshAlgos::FaceNormal( f, (gp_XYZ&) normal.XYZ(), /*normalized=*/true );
4096         if ( isOK )
4097         {
4098           TopoDS_Face ff = face;
4099           ff.Orientation( TopAbs_FORWARD );
4100           if ( helper.IsReversedSubMesh( ff ))
4101             normal.Reverse();
4102           break;
4103         }
4104       }
4105     }
4106   }
4107   return normal.XYZ();
4108 }
4109
4110 //================================================================================
4111 /*!
4112  * \brief Try to get normal at a singularity of a surface basing on it's nature
4113  */
4114 //================================================================================
4115
4116 bool _ViscousBuilder::getFaceNormalAtSingularity( const gp_XY&        uv,
4117                                                   const TopoDS_Face&  face,
4118                                                   SMESH_MesherHelper& /*helper*/,
4119                                                   gp_Dir&             normal )
4120 {
4121   BRepAdaptor_Surface surface( face );
4122   gp_Dir axis;
4123   if ( !getRovolutionAxis( surface, axis ))
4124     return false;
4125
4126   double f,l, d, du, dv;
4127   f = surface.FirstUParameter();
4128   l = surface.LastUParameter();
4129   d = ( uv.X() - f ) / ( l - f );
4130   du = ( d < 0.5 ? +1. : -1 ) * 1e-5 * ( l - f );
4131   f = surface.FirstVParameter();
4132   l = surface.LastVParameter();
4133   d = ( uv.Y() - f ) / ( l - f );
4134   dv = ( d < 0.5 ? +1. : -1 ) * 1e-5 * ( l - f );
4135
4136   gp_Dir refDir;
4137   gp_Pnt2d testUV = uv;
4138   enum { REGULAR = 0, QUASYSINGULAR, CONICAL, IMPOSSIBLE };
4139   double tol = 1e-5;
4140   Handle(Geom_Surface) geomsurf = surface.Surface().Surface();
4141   for ( int iLoop = 0; true ; ++iLoop )
4142   {
4143     testUV.SetCoord( testUV.X() + du, testUV.Y() + dv );
4144     if ( GeomLib::NormEstim( geomsurf, testUV, tol, refDir ) == REGULAR )
4145       break;
4146     if ( iLoop > 20 )
4147       return false;
4148     tol /= 10.;
4149   }
4150
4151   if ( axis * refDir < 0. )
4152     axis.Reverse();
4153
4154   normal = axis;
4155
4156   return true;
4157 }
4158
4159 //================================================================================
4160 /*!
4161  * \brief Return a normal at a node weighted with angles taken by faces
4162  */
4163 //================================================================================
4164
4165 gp_XYZ _ViscousBuilder::getWeigthedNormal( const _LayerEdge* edge )
4166 {
4167   const SMDS_MeshNode* n = edge->_nodes[0];
4168
4169   gp_XYZ resNorm(0,0,0);
4170   SMESH_TNodeXYZ p0( n ), pP, pN;
4171   for ( size_t i = 0; i < edge->_simplices.size(); ++i )
4172   {
4173     pP.Set( edge->_simplices[i]._nPrev );
4174     pN.Set( edge->_simplices[i]._nNext );
4175     gp_Vec v0P( p0, pP ), v0N( p0, pN ), vPN( pP, pN ), norm = v0P ^ v0N;
4176     double l0P = v0P.SquareMagnitude();
4177     double l0N = v0N.SquareMagnitude();
4178     double lPN = vPN.SquareMagnitude();
4179     if ( l0P < std::numeric_limits<double>::min() ||
4180          l0N < std::numeric_limits<double>::min() ||
4181          lPN < std::numeric_limits<double>::min() )
4182       continue;
4183     double lNorm = norm.SquareMagnitude();
4184     double  sin2 = lNorm / l0P / l0N;
4185     double angle = ACos(( v0P * v0N ) / Sqrt( l0P ) / Sqrt( l0N ));
4186
4187     double weight = sin2 * angle / lPN;
4188     resNorm += weight * norm.XYZ() / Sqrt( lNorm );
4189   }
4190
4191   return resNorm;
4192 }
4193
4194 //================================================================================
4195 /*!
4196  * \brief Return a normal at a node by getting a common point of offset planes
4197  *        defined by the FACE normals
4198  */
4199 //================================================================================
4200
4201 gp_XYZ _ViscousBuilder::getNormalByOffset( _LayerEdge*                      edge,
4202                                            std::pair< TopoDS_Face, gp_XYZ > f2Normal[],
4203                                            int                              nbFaces,
4204                                            bool                             lastNoOffset)
4205 {
4206   SMESH_TNodeXYZ p0 = edge->_nodes[0];
4207
4208   gp_XYZ resNorm(0,0,0);
4209   TopoDS_Shape V = SMESH_MesherHelper::GetSubShapeByNode( p0._node, getMeshDS() );
4210   if ( V.ShapeType() != TopAbs_VERTEX || nbFaces < 3 )
4211   {
4212     for ( int i = 0; i < nbFaces; ++i )
4213       resNorm += f2Normal[i].second;
4214     return resNorm;
4215   }
4216
4217   // prepare _OffsetPlane's
4218   vector< _OffsetPlane > pln( nbFaces );
4219   for ( int i = 0; i < nbFaces - lastNoOffset; ++i )
4220   {
4221     pln[i]._faceIndex = i;
4222     pln[i]._plane = gp_Pln( p0 + f2Normal[i].second, f2Normal[i].second );
4223   }
4224   if ( lastNoOffset )
4225   {
4226     pln[ nbFaces - 1 ]._faceIndex = nbFaces - 1;
4227     pln[ nbFaces - 1 ]._plane = gp_Pln( p0, f2Normal[ nbFaces - 1 ].second );
4228   }
4229
4230   // intersect neighboring OffsetPlane's
4231   PShapeIteratorPtr edgeIt = SMESH_MesherHelper::GetAncestors( V, *_mesh, TopAbs_EDGE );
4232   while ( const TopoDS_Shape* edge = edgeIt->next() )
4233   {
4234     int f1 = -1, f2 = -1;
4235     for ( int i = 0; i < nbFaces &&  f2 < 0;  ++i )
4236       if ( SMESH_MesherHelper::IsSubShape( *edge, f2Normal[i].first ))
4237         (( f1 < 0 ) ? f1 : f2 ) = i;
4238
4239     if ( f2 >= 0 )
4240       pln[ f1 ].ComputeIntersectionLine( pln[ f2 ], TopoDS::Edge( *edge ), TopoDS::Vertex( V ));
4241   }
4242
4243   // get a common point
4244   gp_XYZ commonPnt( 0, 0, 0 );
4245   int nbPoints = 0;
4246   bool isPointFound;
4247   for ( int i = 0; i < nbFaces; ++i )
4248   {
4249     commonPnt += pln[ i ].GetCommonPoint( isPointFound, TopoDS::Vertex( V ));
4250     nbPoints  += isPointFound;
4251   }
4252   gp_XYZ wgtNorm = getWeigthedNormal( edge );
4253   if ( nbPoints == 0 )
4254     return wgtNorm;
4255
4256   commonPnt /= nbPoints;
4257   resNorm = commonPnt - p0;
4258   if ( lastNoOffset )
4259     return resNorm;
4260
4261   // choose the best among resNorm and wgtNorm
4262   resNorm.Normalize();
4263   wgtNorm.Normalize();
4264   double resMinDot = std::numeric_limits<double>::max();
4265   double wgtMinDot = std::numeric_limits<double>::max();
4266   for ( int i = 0; i < nbFaces - lastNoOffset; ++i )
4267   {
4268     resMinDot = Min( resMinDot, resNorm * f2Normal[i].second );
4269     wgtMinDot = Min( wgtMinDot, wgtNorm * f2Normal[i].second );
4270   }
4271
4272   if ( Max( resMinDot, wgtMinDot ) < theMinSmoothCosin )
4273   {
4274     edge->Set( _LayerEdge::MULTI_NORMAL );
4275   }
4276
4277   return ( resMinDot > wgtMinDot ) ? resNorm : wgtNorm;
4278 }
4279
4280 //================================================================================
4281 /*!
4282  * \brief Compute line of intersection of 2 planes
4283  */
4284 //================================================================================
4285
4286 void _OffsetPlane::ComputeIntersectionLine( _OffsetPlane&        pln,
4287                                             const TopoDS_Edge&   E,
4288                                             const TopoDS_Vertex& V )
4289 {
4290   int iNext = bool( _faceIndexNext[0] >= 0 );
4291   _faceIndexNext[ iNext ] = pln._faceIndex;
4292
4293   gp_XYZ n1 = _plane.Axis().Direction().XYZ();
4294   gp_XYZ n2 = pln._plane.Axis().Direction().XYZ();
4295
4296   gp_XYZ lineDir = n1 ^ n2;
4297
4298   double x = Abs( lineDir.X() );
4299   double y = Abs( lineDir.Y() );
4300   double z = Abs( lineDir.Z() );
4301
4302   int cooMax; // max coordinate
4303   if (x > y) {
4304     if (x > z) cooMax = 1;
4305     else       cooMax = 3;
4306   }
4307   else {
4308     if (y > z) cooMax = 2;
4309     else       cooMax = 3;
4310   }
4311
4312   gp_Pnt linePos;
4313   if ( Abs( lineDir.Coord( cooMax )) < 0.05 )
4314   {
4315     // parallel planes - intersection is an offset of the common EDGE
4316     gp_Pnt p = BRep_Tool::Pnt( V );
4317     linePos  = 0.5 * (( p.XYZ() + n1 ) + ( p.XYZ() + n2 ));
4318     lineDir  = getEdgeDir( E, V, 0.1 * SMESH_Algo::EdgeLength( E ));
4319   }
4320   else
4321   {
4322     // the constants in the 2 plane equations
4323     double d1 = - ( _plane.Axis().Direction().XYZ()     * _plane.Location().XYZ() );
4324     double d2 = - ( pln._plane.Axis().Direction().XYZ() * pln._plane.Location().XYZ() );
4325
4326     switch ( cooMax ) {
4327     case 1:
4328       linePos.SetX(  0 );
4329       linePos.SetY(( d2*n1.Z() - d1*n2.Z()) / lineDir.X() );
4330       linePos.SetZ(( d1*n2.Y() - d2*n1.Y()) / lineDir.X() );
4331       break;
4332     case 2:
4333       linePos.SetX(( d1*n2.Z() - d2*n1.Z()) / lineDir.Y() );
4334       linePos.SetY(  0 );
4335       linePos.SetZ(( d2*n1.X() - d1*n2.X()) / lineDir.Y() );
4336       break;
4337     case 3:
4338       linePos.SetX(( d2*n1.Y() - d1*n2.Y()) / lineDir.Z() );
4339       linePos.SetY(( d1*n2.X() - d2*n1.X()) / lineDir.Z() );
4340       linePos.SetZ(  0 );
4341     }
4342   }
4343   gp_Lin& line = _lines[ iNext ];
4344   line.SetDirection( lineDir );
4345   line.SetLocation ( linePos );
4346
4347   _isLineOK[ iNext ] = true;
4348
4349
4350   iNext = bool( pln._faceIndexNext[0] >= 0 );
4351   pln._lines        [ iNext ] = line;
4352   pln._faceIndexNext[ iNext ] = this->_faceIndex;
4353   pln._isLineOK     [ iNext ] = true;
4354 }
4355
4356 //================================================================================
4357 /*!
4358  * \brief Computes intersection point of two _lines
4359  */
4360 //================================================================================
4361
4362 gp_XYZ _OffsetPlane::GetCommonPoint(bool&                 isFound,
4363                                     const TopoDS_Vertex & V) const
4364 {
4365   gp_XYZ p( 0,0,0 );
4366   isFound = false;
4367
4368   if ( NbLines() == 2 )
4369   {
4370     gp_Vec lPerp0 = _lines[0].Direction().XYZ() ^ _plane.Axis().Direction().XYZ();
4371     double  dot01 = lPerp0 * _lines[1].Direction().XYZ();
4372     if ( Abs( dot01 ) > 0.05 )
4373     {
4374       gp_Vec l0l1 = _lines[1].Location().XYZ() - _lines[0].Location().XYZ();
4375       double   u1 = - ( lPerp0 * l0l1 ) / dot01;
4376       p = ( _lines[1].Location().XYZ() + _lines[1].Direction().XYZ() * u1 );
4377       isFound = true;
4378     }
4379     else
4380     {
4381       gp_Pnt  pV ( BRep_Tool::Pnt( V ));
4382       gp_Vec  lv0( _lines[0].Location(), pV    ),  lv1(_lines[1].Location(), pV     );
4383       double dot0( lv0 * _lines[0].Direction() ), dot1( lv1 * _lines[1].Direction() );
4384       p += 0.5 * ( _lines[0].Location().XYZ() + _lines[0].Direction().XYZ() * dot0 );
4385       p += 0.5 * ( _lines[1].Location().XYZ() + _lines[1].Direction().XYZ() * dot1 );
4386       isFound = true;
4387     }
4388   }
4389
4390   return p;
4391 }
4392
4393 //================================================================================
4394 /*!
4395  * \brief Find 2 neighbor nodes of a node on EDGE
4396  */
4397 //================================================================================
4398
4399 bool _ViscousBuilder::findNeiborsOnEdge(const _LayerEdge*     edge,
4400                                         const SMDS_MeshNode*& n1,
4401                                         const SMDS_MeshNode*& n2,
4402                                         _EdgesOnShape&        eos,
4403                                         _SolidData&           data)
4404 {
4405   const SMDS_MeshNode* node = edge->_nodes[0];
4406   const int        shapeInd = eos._shapeID;
4407   SMESHDS_SubMesh*   edgeSM = 0;
4408   if ( eos.ShapeType() == TopAbs_EDGE )
4409   {
4410     edgeSM = eos._subMesh->GetSubMeshDS();
4411     if ( !edgeSM || edgeSM->NbElements() == 0 )
4412       return error(SMESH_Comment("Not meshed EDGE ") << shapeInd, data._index);
4413   }
4414   int iN = 0;
4415   n2 = 0;
4416   SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Edge);
4417   while ( eIt->more() && !n2 )
4418   {
4419     const SMDS_MeshElement* e = eIt->next();
4420     const SMDS_MeshNode*   nNeibor = e->GetNode( 0 );
4421     if ( nNeibor == node ) nNeibor = e->GetNode( 1 );
4422     if ( edgeSM )
4423     {
4424       if (!edgeSM->Contains(e)) continue;
4425     }
4426     else
4427     {
4428       TopoDS_Shape s = SMESH_MesherHelper::GetSubShapeByNode( nNeibor, getMeshDS() );
4429       if ( !SMESH_MesherHelper::IsSubShape( s, eos._sWOL )) continue;
4430     }
4431     ( iN++ ? n2 : n1 ) = nNeibor;
4432   }
4433   if ( !n2 )
4434     return error(SMESH_Comment("Wrongly meshed EDGE ") << shapeInd, data._index);
4435   return true;
4436 }
4437
4438 //================================================================================
4439 /*!
4440  * \brief Create _Curvature
4441  */
4442 //================================================================================
4443
4444 _Curvature* _Curvature::New( double avgNormProj, double avgDist )
4445 {
4446   // double   _r; // radius
4447   // double   _k; // factor to correct node smoothed position
4448   // double   _h2lenRatio; // avgNormProj / (2*avgDist)
4449   // gp_Pnt2d _uv; // UV used in putOnOffsetSurface()
4450
4451   _Curvature* c = 0;
4452   if ( fabs( avgNormProj / avgDist ) > 1./200 )
4453   {
4454     c = _Factory::NewCurvature();
4455     c->_r = avgDist * avgDist / avgNormProj;
4456     c->_k = avgDist * avgDist / c->_r / c->_r;
4457     //c->_k = avgNormProj / c->_r;
4458     c->_k *= ( c->_r < 0 ? 1/1.1 : 1.1 ); // not to be too restrictive
4459     c->_h2lenRatio = avgNormProj / ( avgDist + avgDist );
4460
4461     c->_uv.SetCoord( 0., 0. );
4462   }
4463   return c;
4464 }
4465
4466 //================================================================================
4467 /*!
4468  * \brief Set _curvature and _2neibors->_plnNorm by 2 neighbor nodes residing the same EDGE
4469  */
4470 //================================================================================
4471
4472 void _LayerEdge::SetDataByNeighbors( const SMDS_MeshNode* n1,
4473                                      const SMDS_MeshNode* n2,
4474                                      const _EdgesOnShape& eos,
4475                                      SMESH_MesherHelper&  helper)
4476 {
4477   if ( eos.ShapeType() != TopAbs_EDGE )
4478     return;
4479   if ( _curvature && Is( SMOOTHED_C1 ))
4480     return;
4481
4482   gp_XYZ  pos = SMESH_TNodeXYZ( _nodes[0] );
4483   gp_XYZ vec1 = pos - SMESH_TNodeXYZ( n1 );
4484   gp_XYZ vec2 = pos - SMESH_TNodeXYZ( n2 );
4485
4486   // Set _curvature
4487
4488   double      sumLen = vec1.Modulus() + vec2.Modulus();
4489   _2neibors->_wgt[0] = 1 - vec1.Modulus() / sumLen;
4490   _2neibors->_wgt[1] = 1 - vec2.Modulus() / sumLen;
4491   double avgNormProj = 0.5 * ( _normal * vec1 + _normal * vec2 );
4492   double      avgLen = 0.5 * ( vec1.Modulus() + vec2.Modulus() );
4493   _curvature = _Curvature::New( avgNormProj, avgLen );
4494   // if ( _curvature )
4495   //   debugMsg( _nodes[0]->GetID()
4496   //             << " CURV r,k: " << _curvature->_r<<","<<_curvature->_k
4497   //             << " proj = "<<avgNormProj<< " len = " << avgLen << "| lenDelta(0) = "
4498   //             << _curvature->lenDelta(0) );
4499
4500   // Set _plnNorm
4501
4502   if ( eos._sWOL.IsNull() )
4503   {
4504     TopoDS_Edge  E = TopoDS::Edge( eos._shape );
4505     // if ( SMESH_Algo::isDegenerated( E ))
4506     //   return;
4507     gp_XYZ dirE    = getEdgeDir( E, _nodes[0], helper );
4508     gp_XYZ plnNorm = dirE ^ _normal;
4509     double proj0   = plnNorm * vec1;
4510     double proj1   = plnNorm * vec2;
4511     if ( fabs( proj0 ) > 1e-10 || fabs( proj1 ) > 1e-10 )
4512     {
4513       if ( _2neibors->_plnNorm ) delete _2neibors->_plnNorm;
4514       _2neibors->_plnNorm = new gp_XYZ( plnNorm.Normalized() );
4515     }
4516   }
4517 }
4518
4519 //================================================================================
4520 /*!
4521  * \brief Copy data from a _LayerEdge of other SOLID and based on the same node;
4522  * this and the other _LayerEdge are inflated along a FACE or an EDGE
4523  */
4524 //================================================================================
4525
4526 gp_XYZ _LayerEdge::Copy( _LayerEdge&         other,
4527                          _EdgesOnShape&      eos,
4528                          SMESH_MesherHelper& helper )
4529 {
4530   _nodes     = other._nodes;
4531   _normal    = other._normal;
4532   _len       = 0;
4533   _lenFactor = other._lenFactor;
4534   _cosin     = other._cosin;
4535   _2neibors  = other._2neibors;
4536   _curvature = other._curvature;
4537   _2neibors  = other._2neibors;
4538   _maxLen    = Precision::Infinite();//other._maxLen;
4539   _flags     = 0;
4540   _smooFunction = 0;
4541
4542   gp_XYZ lastPos( 0,0,0 );
4543   if ( eos.SWOLType() == TopAbs_EDGE )
4544   {
4545     double u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), _nodes[0] );
4546     _pos.push_back( gp_XYZ( u, 0, 0));
4547
4548     u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), _nodes.back() );
4549     lastPos.SetX( u );
4550   }
4551   else // TopAbs_FACE
4552   {
4553     gp_XY uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), _nodes[0]);
4554     _pos.push_back( gp_XYZ( uv.X(), uv.Y(), 0));
4555
4556     uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), _nodes.back() );
4557     lastPos.SetX( uv.X() );
4558     lastPos.SetY( uv.Y() );
4559   }
4560   return lastPos;
4561 }
4562
4563 //================================================================================
4564 /*!
4565  * \brief Set _cosin and _lenFactor
4566  */
4567 //================================================================================
4568
4569 double _LayerEdge::SetCosin( double cosin )
4570 {
4571   _cosin = cosin;
4572   cosin = Abs( _cosin );
4573   //_lenFactor = ( cosin < 1.-1e-12 ) ?  1./sqrt(1-cosin*cosin ) : 1.0;
4574   double realLenFactor;
4575   if ( cosin < 1.-1e-12 )
4576   {
4577     _lenFactor = realLenFactor = 1./sqrt(1-cosin*cosin );
4578   }
4579   else
4580   {
4581     _lenFactor = 1;
4582     realLenFactor = Precision::Infinite();
4583   }
4584
4585   return realLenFactor;
4586 }
4587
4588 //================================================================================
4589 /*!
4590  * \brief Check if another _LayerEdge is a neighbor on EDGE
4591  */
4592 //================================================================================
4593
4594 bool _LayerEdge::IsNeiborOnEdge( const _LayerEdge* edge ) const
4595 {
4596   return (( this->_2neibors && this->_2neibors->include( edge )) ||
4597           ( edge->_2neibors && edge->_2neibors->include( this )));
4598 }
4599
4600 //================================================================================
4601 /*!
4602  * \brief Fills a vector<_Simplex > 
4603  */
4604 //================================================================================
4605
4606 void _Simplex::GetSimplices( const SMDS_MeshNode* node,
4607                              vector<_Simplex>&    simplices,
4608                              const set<TGeomID>&  ingnoreShapes,
4609                              const _SolidData*    dataToCheckOri,
4610                              const bool           toSort)
4611 {
4612   simplices.clear();
4613   SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
4614   while ( fIt->more() )
4615   {
4616     const SMDS_MeshElement* f = fIt->next();
4617     const TGeomID    shapeInd = f->getshapeId();
4618     if ( ingnoreShapes.count( shapeInd )) continue;
4619     const int nbNodes = f->NbCornerNodes();
4620     const int  srcInd = f->GetNodeIndex( node );
4621     const SMDS_MeshNode* nPrev = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd-1, nbNodes ));
4622     const SMDS_MeshNode* nNext = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd+1, nbNodes ));
4623     const SMDS_MeshNode* nOpp  = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd+2, nbNodes ));
4624     if ( dataToCheckOri && dataToCheckOri->_reversedFaceIds.count( shapeInd ))
4625       std::swap( nPrev, nNext );
4626     simplices.push_back( _Simplex( nPrev, nNext, ( nbNodes == 3 ? 0 : nOpp )));
4627   }
4628
4629   if ( toSort )
4630     SortSimplices( simplices );
4631 }
4632
4633 //================================================================================
4634 /*!
4635  * \brief Set neighbor simplices side by side
4636  */
4637 //================================================================================
4638
4639 void _Simplex::SortSimplices(vector<_Simplex>& simplices)
4640 {
4641   vector<_Simplex> sortedSimplices( simplices.size() );
4642   sortedSimplices[0] = simplices[0];
4643   size_t nbFound = 0;
4644   for ( size_t i = 1; i < simplices.size(); ++i )
4645   {
4646     for ( size_t j = 1; j < simplices.size(); ++j )
4647       if ( sortedSimplices[i-1]._nNext == simplices[j]._nPrev )
4648       {
4649         sortedSimplices[i] = simplices[j];
4650         nbFound++;
4651         break;
4652       }
4653   }
4654   if ( nbFound == simplices.size() - 1 )
4655     simplices.swap( sortedSimplices );
4656 }
4657
4658 //================================================================================
4659 /*!
4660  * \brief DEBUG. Create groups containing temporary data of _LayerEdge's
4661  */
4662 //================================================================================
4663
4664 void _ViscousBuilder::makeGroupOfLE()
4665 {
4666   if (!SALOME::VerbosityActivated())
4667     return;
4668
4669   for ( size_t i = 0 ; i < _sdVec.size(); ++i )
4670   {
4671     if ( _sdVec[i]._n2eMap.empty() ) continue;
4672
4673     dumpFunction( SMESH_Comment("make_LayerEdge_") << i );
4674     TNode2Edge::iterator n2e;
4675     for ( n2e = _sdVec[i]._n2eMap.begin(); n2e != _sdVec[i]._n2eMap.end(); ++n2e )
4676     {
4677       _LayerEdge* le = n2e->second;
4678       // for ( size_t iN = 1; iN < le->_nodes.size(); ++iN )
4679       //   dumpCmd(SMESH_Comment("mesh.AddEdge([ ") <<le->_nodes[iN-1]->GetID()
4680       //           << ", " << le->_nodes[iN]->GetID() <<"])");
4681       if ( le ) {
4682         dumpCmd(SMESH_Comment("mesh.AddEdge([ ") <<le->_nodes[0]->GetID()
4683                 << ", " << le->_nodes.back()->GetID() <<"]) # " << le->_flags );
4684       }
4685     }
4686     dumpFunctionEnd();
4687
4688     dumpFunction( SMESH_Comment("makeNormals") << i );
4689     for ( n2e = _sdVec[i]._n2eMap.begin(); n2e != _sdVec[i]._n2eMap.end(); ++n2e )
4690     {
4691       _LayerEdge* edge = n2e->second;
4692       SMESH_TNodeXYZ nXYZ( edge->_nodes[0] );
4693       nXYZ += edge->_normal * _sdVec[i]._stepSize;
4694       dumpCmd(SMESH_Comment("mesh.AddEdge([ ") << edge->_nodes[0]->GetID()
4695               << ", mesh.AddNode( "<< nXYZ.X()<<","<< nXYZ.Y()<<","<< nXYZ.Z()<<")])");
4696     }
4697     dumpFunctionEnd();
4698
4699     dumpFunction( SMESH_Comment("makeTmpFaces_") << i );
4700     dumpCmd( "faceId1 = mesh.NbElements()" );
4701     TopExp_Explorer fExp( _sdVec[i]._solid, TopAbs_FACE );
4702     for ( ; fExp.More(); fExp.Next() )
4703     {
4704       if ( const SMESHDS_SubMesh* sm = _sdVec[i]._proxyMesh->GetProxySubMesh( fExp.Current() ))
4705       {
4706         if ( sm->NbElements() == 0 ) continue;
4707         SMDS_ElemIteratorPtr fIt = sm->GetElements();
4708         while ( fIt->more())
4709         {
4710           const SMDS_MeshElement* e = fIt->next();
4711           SMESH_Comment cmd("mesh.AddFace([");
4712           for ( int j = 0; j < e->NbCornerNodes(); ++j )
4713             cmd << e->GetNode(j)->GetID() << (j+1 < e->NbCornerNodes() ? ",": "])");
4714           dumpCmd( cmd );
4715         }
4716       }
4717     }
4718     dumpCmd( "faceId2 = mesh.NbElements()" );
4719     dumpCmd( SMESH_Comment( "mesh.MakeGroup( 'tmpFaces_" ) << i << "',"
4720              << "SMESH.FACE, SMESH.FT_RangeOfIds,'=',"
4721              << "'%s-%s' % (faceId1+1, faceId2))");
4722     dumpFunctionEnd();
4723   }
4724 }
4725
4726 //================================================================================
4727 /*!
4728  * \brief Find maximal _LayerEdge length (layer thickness) limited by geometry
4729  */
4730 //================================================================================
4731
4732 void _ViscousBuilder::computeGeomSize( _SolidData& data )
4733 {
4734   data._geomSize = Precision::Infinite();
4735   double intersecDist;
4736   const SMDS_MeshElement* face;
4737   SMESH_MesherHelper helper( *_mesh );
4738
4739   SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
4740     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
4741                                            data._proxyMesh->GetFaces( data._solid )));
4742
4743   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4744   {
4745     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4746     if ( eos._edges.empty() )
4747       continue;
4748     // get neighbor faces, intersection with which should not be considered since
4749     // collisions are avoided by means of smoothing
4750     set< TGeomID > neighborFaces;
4751     if ( eos._hyp.ToSmooth() )
4752     {
4753       SMESH_subMeshIteratorPtr subIt =
4754         eos._subMesh->getDependsOnIterator(/*includeSelf=*/eos.ShapeType() != TopAbs_FACE );
4755       while ( subIt->more() )
4756       {
4757         SMESH_subMesh* sm = subIt->next();
4758         PShapeIteratorPtr fIt = helper.GetAncestors( sm->GetSubShape(), *_mesh, TopAbs_FACE );
4759         while ( const TopoDS_Shape* face = fIt->next() )
4760           neighborFaces.insert( getMeshDS()->ShapeToIndex( *face ));
4761       }
4762     }
4763     // find intersections
4764     double thinkness = eos._hyp.GetTotalThickness();
4765     for ( size_t i = 0; i < eos._edges.size(); ++i )
4766     {
4767       if ( eos._edges[i]->_nodes.size() < 2 ) continue;
4768       eos._edges[i]->SetMaxLen( thinkness );
4769       eos._edges[i]->FindIntersection( *searcher, intersecDist, data._epsilon, eos, &face );
4770       if ( intersecDist > 0 && face )
4771       {
4772         data._geomSize = Min( data._geomSize, intersecDist );
4773         if ( !neighborFaces.count( face->getshapeId() ))
4774           eos[i]->SetMaxLen( Min( thinkness, intersecDist / ( face->GetID() < 0 ? 3. : 2. )));
4775       }
4776     }
4777   }
4778
4779   data._maxThickness = 0;
4780   data._minThickness = 1e100;
4781   list< const StdMeshers_ViscousLayers* >::iterator hyp = data._hyps.begin();
4782   for ( ; hyp != data._hyps.end(); ++hyp )
4783   {
4784     data._maxThickness = Max( data._maxThickness, (*hyp)->GetTotalThickness() );
4785     data._minThickness = Min( data._minThickness, (*hyp)->GetTotalThickness() );
4786   }
4787
4788   // Limit inflation step size by geometry size found by intersecting
4789   // normals of _LayerEdge's with mesh faces
4790   if ( data._stepSize > 0.3 * data._geomSize )
4791     limitStepSize( data, 0.3 * data._geomSize );
4792
4793   if ( data._stepSize > data._minThickness )
4794     limitStepSize( data, data._minThickness );
4795
4796
4797   // -------------------------------------------------------------------------
4798   // Detect _LayerEdge which can't intersect with opposite or neighbor layer,
4799   // so no need in detecting intersection at each inflation step
4800   // -------------------------------------------------------------------------
4801
4802   int nbSteps = data._maxThickness / data._stepSize;
4803   if ( nbSteps < 3 || nbSteps * data._n2eMap.size() < 100000 )
4804     return;
4805
4806   vector< const SMDS_MeshElement* > closeFaces;
4807   int nbDetected = 0;
4808
4809   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4810   {
4811     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4812     if ( eos._edges.empty() || eos.ShapeType() != TopAbs_FACE )
4813       continue;
4814
4815     for ( size_t i = 0; i < eos.size(); ++i )
4816     {
4817       SMESH_NodeXYZ p( eos[i]->_nodes[0] );
4818       double radius = data._maxThickness + 2 * eos[i]->_maxLen;
4819       closeFaces.clear();
4820       searcher->GetElementsInSphere( p, radius, SMDSAbs_Face, closeFaces );
4821
4822       bool toIgnore = true;
4823       for ( size_t iF = 0; iF < closeFaces.size()  && toIgnore; ++iF )
4824         if ( !( toIgnore = ( closeFaces[ iF ]->getshapeId() == eos._shapeID ||
4825                              data._ignoreFaceIds.count( closeFaces[ iF ]->getshapeId() ))))
4826         {
4827           // check if a _LayerEdge will inflate in a direction opposite to a direction
4828           // toward a close face
4829           bool allBehind = true;
4830           for ( int iN = 0; iN < closeFaces[ iF ]->NbCornerNodes()  && allBehind; ++iN )
4831           {
4832             SMESH_NodeXYZ pi( closeFaces[ iF ]->GetNode( iN ));
4833             allBehind = (( pi - p ) * eos[i]->_normal < 0.1 * data._stepSize );
4834           }
4835           toIgnore = allBehind;
4836         }
4837
4838
4839       if ( toIgnore ) // no need to detect intersection
4840       {
4841         eos[i]->Set( _LayerEdge::INTERSECTED );
4842         ++nbDetected;
4843       }
4844     }
4845   }
4846
4847   debugMsg( "Nb LE to intersect " << data._n2eMap.size()-nbDetected << ", ignore " << nbDetected );
4848
4849   return;
4850 }
4851
4852 //================================================================================
4853 /*!
4854  * \brief Increase length of _LayerEdge's to reach the required thickness of layers
4855  */
4856 //================================================================================
4857
4858 bool _ViscousBuilder::inflate(_SolidData& data)
4859 {
4860   SMESH_MesherHelper helper( *_mesh );
4861
4862   const double tgtThick = data._maxThickness;
4863
4864   if ( data._stepSize < 1. )
4865     data._epsilon = data._stepSize * 1e-7;
4866
4867   debugMsg( "-- geomSize = " << data._geomSize << ", stepSize = " << data._stepSize );
4868   _pyDump->Pause();
4869
4870   findCollisionEdges( data, helper );
4871
4872   limitMaxLenByCurvature( data, helper );
4873
4874   _pyDump->Resume();
4875
4876   // limit length of _LayerEdge's around MULTI_NORMAL _LayerEdge's
4877   for ( size_t i = 0; i < data._edgesOnShape.size(); ++i )
4878     if ( data._edgesOnShape[i].ShapeType() == TopAbs_VERTEX &&
4879          data._edgesOnShape[i]._edges.size() > 0 &&
4880          data._edgesOnShape[i]._edges[0]->Is( _LayerEdge::MULTI_NORMAL ))
4881     {
4882       data._edgesOnShape[i]._edges[0]->Unset( _LayerEdge::BLOCKED );
4883       data._edgesOnShape[i]._edges[0]->Block( data );
4884     }
4885
4886   const double safeFactor = ( 2*data._maxThickness < data._geomSize ) ? 1 : theThickToIntersection;
4887
4888   double avgThick = 0, curThick = 0, distToIntersection = Precision::Infinite();
4889   int nbSteps = 0, nbRepeats = 0;
4890   while ( avgThick < 0.99 )
4891   {
4892     // new target length
4893     double prevThick = curThick;
4894     curThick += data._stepSize;
4895     if ( curThick > tgtThick )
4896     {
4897       curThick = tgtThick + tgtThick*( 1.-avgThick ) * nbRepeats;
4898       nbRepeats++;
4899     }
4900
4901     double stepSize = curThick - prevThick;
4902     updateNormalsOfSmoothed( data, helper, nbSteps, stepSize ); // to ease smoothing
4903
4904     // Elongate _LayerEdge's
4905     dumpFunction(SMESH_Comment("inflate")<<data._index<<"_step"<<nbSteps); // debug
4906     for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4907     {
4908       _EdgesOnShape& eos = data._edgesOnShape[iS];
4909       if ( eos._edges.empty() ) continue;
4910
4911       const double shapeCurThick = Min( curThick, eos._hyp.GetTotalThickness() );
4912       for ( size_t i = 0; i < eos._edges.size(); ++i )
4913       {
4914         eos._edges[i]->SetNewLength( shapeCurThick, eos, helper );
4915       }
4916     }
4917     dumpFunctionEnd();
4918
4919     if ( !updateNormals( data, helper, nbSteps, stepSize )) // to avoid collisions
4920       return false;
4921
4922     // Improve and check quality
4923     if ( !smoothAndCheck( data, nbSteps, distToIntersection ))
4924     {
4925       if ( nbSteps > 0 )
4926       {
4927 #ifdef __NOT_INVALIDATE_BAD_SMOOTH
4928         debugMsg("NOT INVALIDATED STEP!");
4929         return error("Smoothing failed", data._index);
4930 #endif
4931         dumpFunction(SMESH_Comment("invalidate")<<data._index<<"_step"<<nbSteps); // debug
4932         for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4933         {
4934           _EdgesOnShape& eos = data._edgesOnShape[iS];
4935           for ( size_t i = 0; i < eos._edges.size(); ++i )
4936             eos._edges[i]->InvalidateStep( nbSteps+1, eos );
4937         }
4938         dumpFunctionEnd();
4939       }
4940       break; // no more inflating possible
4941     }
4942     nbSteps++;
4943
4944     // Evaluate achieved thickness
4945     avgThick = 0;
4946     int nbActiveEdges = 0;
4947     for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4948     {
4949       _EdgesOnShape& eos = data._edgesOnShape[iS];
4950       if ( eos._edges.empty() ) continue;
4951
4952       const double shapeTgtThick = eos._hyp.GetTotalThickness();
4953       for ( size_t i = 0; i < eos._edges.size(); ++i )
4954       {
4955         if ( eos._edges[i]->_nodes.size() > 1 )
4956           avgThick    += Min( 1., eos._edges[i]->_len / shapeTgtThick );
4957         else
4958           avgThick    += 1;
4959         nbActiveEdges += ( ! eos._edges[i]->Is( _LayerEdge::BLOCKED ));
4960       }
4961     }
4962     avgThick /= data._n2eMap.size();
4963     debugMsg( "-- Thickness " << curThick << " ("<< avgThick*100 << "%) reached" );
4964
4965 #ifdef BLOCK_INFLATION
4966     if ( nbActiveEdges == 0 )
4967     {
4968       debugMsg( "-- Stop inflation since all _LayerEdge's BLOCKED " );
4969       break;
4970     }
4971 #else
4972     if ( distToIntersection < tgtThick * avgThick * safeFactor && avgThick < 0.9 )
4973     {
4974       debugMsg( "-- Stop inflation since "
4975                 << " distToIntersection( "<<distToIntersection<<" ) < avgThick( "
4976                 << tgtThick * avgThick << " ) * " << safeFactor );
4977       break;
4978     }
4979 #endif
4980
4981     // new step size
4982     limitStepSize( data, 0.25 * distToIntersection );
4983     if ( data._stepSizeNodes[0] )
4984       data._stepSize = data._stepSizeCoeff *
4985         SMESH_TNodeXYZ(data._stepSizeNodes[0]).Distance(data._stepSizeNodes[1]);
4986
4987   } // while ( avgThick < 0.99 )
4988
4989   if ( nbSteps == 0 )
4990     return error("failed at the very first inflation step", data._index);
4991
4992   if ( avgThick < 0.99 )
4993   {
4994     if ( !data._proxyMesh->_warning || data._proxyMesh->_warning->IsOK() )
4995     {
4996       data._proxyMesh->_warning.reset
4997         ( new SMESH_ComputeError (COMPERR_WARNING,
4998                                   SMESH_Comment("Thickness ") << tgtThick <<
4999                                   " of viscous layers not reached,"
5000                                   " average reached thickness is " << avgThick*tgtThick));
5001     }
5002   }
5003
5004   // Restore position of src nodes moved by inflation on _noShrinkShapes
5005   dumpFunction(SMESH_Comment("restoNoShrink_So")<<data._index); // debug
5006   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
5007   {
5008     _EdgesOnShape& eos = data._edgesOnShape[iS];
5009     if ( !eos._edges.empty() && eos._edges[0]->_nodes.size() == 1 )
5010       for ( size_t i = 0; i < eos._edges.size(); ++i )
5011       {
5012         restoreNoShrink( *eos._edges[ i ] );
5013       }
5014   }
5015   dumpFunctionEnd();
5016
5017   return safeFactor > 0; // == true (avoid warning: unused variable 'safeFactor')
5018 }
5019
5020 //================================================================================
5021 /*!
5022  * \brief Improve quality of layer inner surface and check intersection
5023  */
5024 //================================================================================
5025
5026 bool _ViscousBuilder::smoothAndCheck(_SolidData& data,
5027                                      const int   infStep,
5028                                      double &    distToIntersection)
5029 {
5030   if ( data._nbShapesToSmooth == 0 )
5031     return true; // no shapes needing smoothing
5032
5033   bool moved, improved;
5034   double vol;
5035   vector< _LayerEdge* >    movedEdges, badEdges;
5036   vector< _EdgesOnShape* > eosC1; // C1 continues shapes
5037   vector< bool >           isConcaveFace;
5038
5039   SMESH_MesherHelper helper(*_mesh);
5040   Handle(ShapeAnalysis_Surface) surface;
5041   TopoDS_Face F;
5042
5043   for ( int isFace = 0; isFace < 2; ++isFace ) // smooth on [ EDGEs, FACEs ]
5044   {
5045     const TopAbs_ShapeEnum shapeType = isFace ? TopAbs_FACE : TopAbs_EDGE;
5046
5047     for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
5048     {
5049       _EdgesOnShape& eos = data._edgesOnShape[ iS ];
5050       if ( !eos._toSmooth ||
5051            eos.ShapeType() != shapeType ||
5052            eos._edges.empty() )
5053         continue;
5054
5055       // already smoothed?
5056       // bool toSmooth = ( eos._edges[ 0 ]->NbSteps() >= infStep+1 );
5057       // if ( !toSmooth ) continue;
5058
5059       if ( !eos._hyp.ToSmooth() )
5060       {
5061         // smooth disabled by the user; check validy only
5062         if ( !isFace ) continue;
5063         badEdges.clear();
5064         for ( size_t i = 0; i < eos._edges.size(); ++i )
5065         {
5066           _LayerEdge* edge = eos._edges[i];
5067           for ( size_t iF = 0; iF < edge->_simplices.size(); ++iF )
5068             if ( !edge->_simplices[iF].IsForward( edge->_nodes[0], edge->_pos.back(), vol ))
5069             {
5070               // debugMsg( "-- Stop inflation. Bad simplex ("
5071               //           << " "<< edge->_nodes[0]->GetID()
5072               //           << " "<< edge->_nodes.back()->GetID()
5073               //           << " "<< edge->_simplices[iF]._nPrev->GetID()
5074               //           << " "<< edge->_simplices[iF]._nNext->GetID() << " ) ");
5075               // return false;
5076               badEdges.push_back( edge );
5077             }
5078         }
5079         if ( !badEdges.empty() )
5080         {
5081           eosC1.resize(1);
5082           eosC1[0] = &eos;
5083           int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5084           if ( nbBad > 0 )
5085             return false;
5086         }
5087         continue; // goto the next EDGE or FACE
5088       }
5089
5090       // prepare data
5091       if ( eos.SWOLType() == TopAbs_FACE )
5092       {
5093         if ( !F.IsSame( eos._sWOL )) {
5094           F = TopoDS::Face( eos._sWOL );
5095           helper.SetSubShape( F );
5096           surface = helper.GetSurface( F );
5097         }
5098       }
5099       else
5100       {
5101         F.Nullify(); surface.Nullify();
5102       }
5103       const TGeomID sInd = eos._shapeID;
5104
5105       // perform smoothing
5106
5107       if ( eos.ShapeType() == TopAbs_EDGE )
5108       {
5109         dumpFunction(SMESH_Comment("smooth")<<data._index << "_Ed"<<sInd <<"_InfStep"<<infStep);
5110
5111         if ( !eos._edgeSmoother->Perform( data, surface, F, helper ))
5112         {
5113           // smooth on EDGE's (normally we should not get here)
5114           int step = 0;
5115           do {
5116             moved = false;
5117             for ( size_t i = 0; i < eos._edges.size(); ++i )
5118             {
5119               moved |= eos._edges[i]->SmoothOnEdge( surface, F, helper );
5120             }
5121             dumpCmd( SMESH_Comment("# end step ")<<step);
5122           }
5123           while ( moved && step++ < 5 );
5124         }
5125         dumpFunctionEnd();
5126       }
5127
5128       else // smooth on FACE
5129       {
5130         eosC1.clear();
5131         eosC1.push_back( & eos );
5132         eosC1.insert( eosC1.end(), eos._eosC1.begin(), eos._eosC1.end() );
5133
5134         movedEdges.clear();
5135         isConcaveFace.resize( eosC1.size() );
5136         for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5137         {
5138           isConcaveFace[ iEOS ] = data._concaveFaces.count( eosC1[ iEOS ]->_shapeID  );
5139
5140           if ( eosC1[ iEOS ]->_mapper2D )
5141           {
5142             // compute node position by boundary node position in structured mesh
5143             dumpFunction(SMESH_Comment("map2dS")<<data._index<<"_Fa"<<eos._shapeID
5144                          <<"_InfStep"<<infStep);
5145
5146             eosC1[ iEOS ]->_mapper2D->ComputeNodePositions();
5147
5148             for ( _LayerEdge* le : eosC1[ iEOS ]->_edges )
5149               le->_pos.back() = SMESH_NodeXYZ( le->_nodes.back() );
5150
5151             dumpFunctionEnd();
5152           }
5153           else
5154           {
5155             for ( _LayerEdge* le : eosC1[ iEOS ]->_edges )
5156               if ( le->Is( _LayerEdge::MOVED ) ||
5157                    le->Is( _LayerEdge::NEAR_BOUNDARY ))
5158                 movedEdges.push_back( le );
5159           }
5160           makeOffsetSurface( *eosC1[ iEOS ], helper );
5161         }
5162
5163         int step = 0, stepLimit = 5, nbBad = 0;
5164         while (( ++step <= stepLimit ) || improved )
5165         {
5166           int oldBadNb = nbBad;
5167           badEdges.clear();
5168
5169 #ifdef INCREMENTAL_SMOOTH
5170           // smooth moved only
5171           if ( !movedEdges.empty() )
5172             dumpFunction(SMESH_Comment("smooth")<<data._index<<"_Fa"<<sInd
5173                          <<"_InfStep"<<infStep<<"_"<<step); // debug
5174           bool findBest = false; // ( step == stepLimit );
5175           for ( size_t i = 0; i < movedEdges.size(); ++i )
5176           {
5177             movedEdges[i]->Unset( _LayerEdge::SMOOTHED );
5178             if ( movedEdges[i]->Smooth( step, findBest, movedEdges ) > 0 )
5179               badEdges.push_back( movedEdges[i] );
5180           }
5181 #else
5182           // smooth all
5183           dumpFunction(SMESH_Comment("smooth")<<data._index<<"_Fa"<<sInd
5184                        <<"_InfStep"<<infStep<<"_"<<step); // debug
5185           bool findBest = ( step == stepLimit || isConcaveFace[ iEOS ]);
5186           for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5187           {
5188             if ( eosC1[ iEOS ]->_mapper2D )
5189               continue;
5190             vector< _LayerEdge* > & edges = eosC1[ iEOS ]->_edges;
5191             for ( size_t i = 0; i < edges.size(); ++i )
5192             {
5193               edges[i]->Unset( _LayerEdge::SMOOTHED );
5194               if ( edges[i]->Smooth( step, findBest, false ) > 0 )
5195                 badEdges.push_back( eos._edges[i] );
5196             }
5197           }
5198 #endif
5199           nbBad = badEdges.size();
5200
5201           if ( nbBad > 0 )
5202             debugMsg(SMESH_Comment("nbBad = ") << nbBad );
5203
5204           if ( !badEdges.empty() && step >= stepLimit / 2 )
5205           {
5206             if ( badEdges[0]->Is( _LayerEdge::ON_CONCAVE_FACE ))
5207               stepLimit = 9;
5208
5209             // resolve hard smoothing situation around concave VERTEXes
5210             for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5211             {
5212               vector< _EdgesOnShape* > & eosCoVe = eosC1[ iEOS ]->_eosConcaVer;
5213               for ( size_t i = 0; i < eosCoVe.size(); ++i )
5214                 eosCoVe[i]->_edges[0]->MoveNearConcaVer( eosCoVe[i], eosC1[ iEOS ],
5215                                                          step, badEdges );
5216             }
5217             // look for the best smooth of _LayerEdge's neighboring badEdges
5218             nbBad = 0;
5219             for ( size_t i = 0; i < badEdges.size(); ++i )
5220             {
5221               _LayerEdge* ledge = badEdges[i];
5222               for ( size_t iN = 0; iN < ledge->_neibors.size(); ++iN )
5223               {
5224                 ledge->_neibors[iN]->Unset( _LayerEdge::SMOOTHED );
5225                 nbBad += ledge->_neibors[iN]->Smooth( step, true, /*findBest=*/true );
5226               }
5227               ledge->Unset( _LayerEdge::SMOOTHED );
5228               nbBad += ledge->Smooth( step, true, /*findBest=*/true );
5229             }
5230             debugMsg(SMESH_Comment("nbBad = ") << nbBad );
5231           }
5232
5233           if ( nbBad == oldBadNb  &&
5234                nbBad > 0 &&
5235                step < stepLimit ) // smooth w/o check of validity
5236           {
5237             dumpFunctionEnd();
5238             dumpFunction(SMESH_Comment("smoothWoCheck")<<data._index<<"_Fa"<<sInd
5239                          <<"_InfStep"<<infStep<<"_"<<step); // debug
5240             for ( size_t i = 0; i < movedEdges.size(); ++i )
5241             {
5242               movedEdges[i]->SmoothWoCheck();
5243             }
5244             if ( stepLimit < 9 )
5245               stepLimit++;
5246           }
5247
5248           improved = ( nbBad < oldBadNb );
5249
5250           dumpFunctionEnd();
5251
5252           if (( step % 3 == 1 ) || ( nbBad > 0 && step >= stepLimit / 2 ))
5253             for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5254             {
5255               putOnOffsetSurface( *eosC1[ iEOS ], infStep, eosC1, step, /*moveAll=*/step == 1 );
5256             }
5257
5258         } // smoothing steps
5259
5260         // project -- to prevent intersections or to fix bad simplices
5261         for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5262         {
5263           if ( ! eosC1[ iEOS ]->_eosConcaVer.empty() || nbBad > 0 )
5264             putOnOffsetSurface( *eosC1[ iEOS ], -infStep, eosC1 );
5265         }
5266
5267         //if ( !badEdges.empty() )
5268         {
5269           badEdges.clear();
5270           for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5271           {
5272             for ( size_t i = 0; i < eosC1[ iEOS ]->_edges.size(); ++i )
5273             {
5274               if ( !eosC1[ iEOS ]->_sWOL.IsNull() ) continue;
5275
5276               _LayerEdge* edge = eosC1[ iEOS ]->_edges[i];
5277               edge->CheckNeiborsOnBoundary( & badEdges );
5278               if (( nbBad > 0 ) ||
5279                   ( edge->Is( _LayerEdge::BLOCKED ) && edge->Is( _LayerEdge::NEAR_BOUNDARY )))
5280               {
5281                 SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
5282                 gp_XYZ        prevXYZ = edge->PrevCheckPos();
5283                 for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5284                   if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
5285                   {
5286                     debugMsg("Bad simplex ( " << edge->_nodes[0]->GetID()
5287                              << " "<< tgtXYZ._node->GetID()
5288                              << " "<< edge->_simplices[j]._nPrev->GetID()
5289                              << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
5290                     badEdges.push_back( edge );
5291                     break;
5292                   }
5293               }
5294             }
5295           }
5296
5297           // try to fix bad simplices by removing the last inflation step of some _LayerEdge's
5298           nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5299
5300           if ( nbBad > 0 )
5301             return false;
5302         }
5303
5304       } // // smooth on FACE's
5305     } // loop on shapes
5306   } // smooth on [ EDGEs, FACEs ]
5307
5308   // Check orientation of simplices of _LayerEdge's on EDGEs and VERTEXes
5309   eosC1.resize(1);
5310   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
5311   {
5312     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
5313     if ( eos.ShapeType() == TopAbs_FACE ||
5314          eos._edges.empty() ||
5315          !eos._sWOL.IsNull() )
5316       continue;
5317
5318     badEdges.clear();
5319     for ( size_t i = 0; i < eos._edges.size(); ++i )
5320     {
5321       _LayerEdge*      edge = eos._edges[i];
5322       if ( edge->_nodes.size() < 2 ) continue;
5323       SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
5324       //SMESH_TNodeXYZ prevXYZ = edge->_nodes[0];
5325       gp_XYZ        prevXYZ = edge->PrevCheckPos( &eos );
5326       //const gp_XYZ& prevXYZ = edge->PrevPos();
5327       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5328         if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
5329         {
5330           debugMsg("Bad simplex on bnd ( " << edge->_nodes[0]->GetID()
5331                    << " "<< tgtXYZ._node->GetID()
5332                    << " "<< edge->_simplices[j]._nPrev->GetID()
5333                    << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
5334           badEdges.push_back( edge );
5335           break;
5336         }
5337     }
5338
5339     // try to fix bad simplices by removing the last inflation step of some _LayerEdge's
5340     eosC1[0] = &eos;
5341     int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5342     if ( nbBad > 0 )
5343       return false;
5344   }
5345
5346
5347   // Check if the last segments of _LayerEdge intersects 2D elements;
5348   // checked elements are either temporary faces or faces on surfaces w/o the layers
5349
5350   SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
5351     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
5352                                            data._proxyMesh->GetFaces( data._solid )) );
5353
5354 #ifdef BLOCK_INFLATION
5355   const bool toBlockInfaltion = true;
5356 #else
5357   const bool toBlockInfaltion = false;
5358 #endif
5359   distToIntersection = Precision::Infinite();
5360   double dist;
5361   const SMDS_MeshElement* intFace = 0;
5362   const SMDS_MeshElement* closestFace = 0;
5363   _LayerEdge* le = 0;
5364   bool is1stBlocked = true; // dbg
5365   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
5366   {
5367     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
5368     if ( eos._edges.empty() || !eos._sWOL.IsNull() )
5369       continue;
5370     for ( size_t i = 0; i < eos._edges.size(); ++i )
5371     {
5372       if ( eos._edges[i]->Is( _LayerEdge::INTERSECTED ) ||
5373            eos._edges[i]->Is( _LayerEdge::MULTI_NORMAL ))
5374         continue;
5375       if ( eos._edges[i]->FindIntersection( *searcher, dist, data._epsilon, eos, &intFace ))
5376       {
5377         return false;
5378         // commented due to "Illegal hash-positionPosition" error in NETGEN
5379         // on Debian60 on viscous_layers_01/B2 case
5380         // Collision; try to deflate _LayerEdge's causing it
5381         // badEdges.clear();
5382         // badEdges.push_back( eos._edges[i] );
5383         // eosC1[0] = & eos;
5384         // int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5385         // if ( nbBad > 0 )
5386         //   return false;
5387
5388         // badEdges.clear();
5389         // if ( _EdgesOnShape* eof = data.GetShapeEdges( intFace->getshapeId() ))
5390         // {
5391         //   if ( const _TmpMeshFace* f = dynamic_cast< const _TmpMeshFace*>( intFace ))
5392         //   {
5393         //     const SMDS_MeshElement* srcFace =
5394         //       eof->_subMesh->GetSubMeshDS()->GetElement( f->getIdInShape() );
5395         //     SMDS_ElemIteratorPtr nIt = srcFace->nodesIterator();
5396         //     while ( nIt->more() )
5397         //     {
5398         //       const SMDS_MeshNode* srcNode = static_cast<const SMDS_MeshNode*>( nIt->next() );
5399         //       TNode2Edge::iterator n2e = data._n2eMap.find( srcNode );
5400         //       if ( n2e != data._n2eMap.end() )
5401         //         badEdges.push_back( n2e->second );
5402         //     }
5403         //     eosC1[0] = eof;
5404         //     nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5405         //     if ( nbBad > 0 )
5406         //       return false;
5407         //   }
5408         // }
5409         // if ( eos._edges[i]->FindIntersection( *searcher, dist, data._epsilon, eos, &intFace ))
5410         //   return false;
5411         // else
5412         //   continue;
5413       }
5414       if ( !intFace )
5415       {
5416         SMESH_Comment msg("Invalid? normal at node "); msg << eos._edges[i]->_nodes[0]->GetID();
5417         debugMsg( msg );
5418         continue;
5419       }
5420
5421       const bool isShorterDist = ( distToIntersection > dist );
5422       if ( toBlockInfaltion || isShorterDist )
5423       {
5424         // ignore intersection of a _LayerEdge based on a _ConvexFace with a face
5425         // lying on this _ConvexFace
5426         if ( _ConvexFace* convFace = data.GetConvexFace( intFace->getshapeId() ))
5427           if ( convFace->_isTooCurved && convFace->_subIdToEOS.count ( eos._shapeID ))
5428             continue;
5429
5430         // ignore intersection of a _LayerEdge based on a FACE with an element on this FACE
5431         // ( avoid limiting the thickness on the case of issue 22576)
5432         if ( intFace->getshapeId() == eos._shapeID  )
5433           continue;
5434
5435         // ignore intersection with intFace of an adjacent FACE
5436         if ( dist > 0.01 * eos._edges[i]->_len )
5437         {
5438           bool toIgnore = false;
5439           if (  eos._toSmooth )
5440           {
5441             const TopoDS_Shape& S = getMeshDS()->IndexToShape( intFace->getshapeId() );
5442             if ( !S.IsNull() && S.ShapeType() == TopAbs_FACE )
5443             {
5444               TopExp_Explorer sub( eos._shape,
5445                                    eos.ShapeType() == TopAbs_FACE ? TopAbs_EDGE : TopAbs_VERTEX );
5446               for ( ; !toIgnore && sub.More(); sub.Next() )
5447                 // is adjacent - has a common EDGE or VERTEX
5448                 toIgnore = ( helper.IsSubShape( sub.Current(), S ));
5449
5450               if ( toIgnore ) // check angle between normals
5451               {
5452                 gp_XYZ normal;
5453                 if ( SMESH_MeshAlgos::FaceNormal( intFace, normal, /*normalized=*/true ))
5454                   toIgnore  = ( normal * eos._edges[i]->_normal > -0.5 );
5455               }
5456             }
5457           }
5458           if ( !toIgnore ) // check if the edge is a neighbor of intFace
5459           {
5460             for ( size_t iN = 0; !toIgnore &&  iN < eos._edges[i]->_neibors.size(); ++iN )
5461             {
5462               int nInd = intFace->GetNodeIndex( eos._edges[i]->_neibors[ iN ]->_nodes.back() );
5463               toIgnore = ( nInd >= 0 );
5464             }
5465           }
5466           if ( toIgnore )
5467             continue;
5468         }
5469
5470         // intersection not ignored
5471
5472         double minDist = 0;
5473         if ( eos._edges[i]->_maxLen < 0.99 * eos._hyp.GetTotalThickness() ) // limited length
5474           minDist = eos._edges[i]->_len * theThickToIntersection;
5475
5476         if ( toBlockInfaltion && dist < minDist  )
5477         {
5478           if ( is1stBlocked ) { is1stBlocked = false; // debug
5479             dumpFunction(SMESH_Comment("blockIntersected") <<data._index<<"_InfStep"<<infStep);
5480           }
5481           eos._edges[i]->Set( _LayerEdge::INTERSECTED ); // not to intersect
5482           eos._edges[i]->Block( data );                  // not to inflate
5483
5484           //if ( _EdgesOnShape* eof = data.GetShapeEdges( intFace->getshapeId() ))
5485           {
5486             // block _LayerEdge's, on top of which intFace is
5487             if ( const _TmpMeshFace* f = dynamic_cast< const _TmpMeshFace*>( intFace ))
5488             {
5489               const SMDS_MeshElement* srcFace = f->_srcFace;
5490               SMDS_ElemIteratorPtr        nIt = srcFace->nodesIterator();
5491               while ( nIt->more() )
5492               {
5493                 const SMDS_MeshNode* srcNode = static_cast<const SMDS_MeshNode*>( nIt->next() );
5494                 TNode2Edge::iterator n2e = data._n2eMap.find( srcNode );
5495                 if ( n2e != data._n2eMap.end() )
5496                   n2e->second->Block( data );
5497               }
5498             }
5499           }
5500         }
5501
5502         if ( isShorterDist )
5503         {
5504           distToIntersection = dist;
5505           le = eos._edges[i];
5506           closestFace = intFace;
5507         }
5508
5509       } // if ( toBlockInfaltion || isShorterDist )
5510     } // loop on eos._edges
5511   } // loop on data._edgesOnShape
5512
5513   if ( !is1stBlocked )
5514   {
5515     dumpFunctionEnd();
5516   }
5517
5518   if ( closestFace && le )
5519   {
5520 #ifdef __myDEBUG
5521     SMDS_MeshElement::iterator nIt = closestFace->begin_nodes();
5522     cout << "#Shortest distance: _LayerEdge nodes: tgt " << le->_nodes.back()->GetID()
5523          << " src " << le->_nodes[0]->GetID()<< ", intersection with face ("
5524          << (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()
5525          << ") distance = " << distToIntersection<< endl;
5526 #endif
5527   }
5528
5529   return true;
5530 }
5531
5532 //================================================================================
5533 /*!
5534  * \brief try to fix bad simplices by removing the last inflation step of some _LayerEdge's
5535  *  \param [in,out] badSmooEdges - _LayerEdge's to fix
5536  *  \return int - resulting nb of bad _LayerEdge's
5537  */
5538 //================================================================================
5539
5540 int _ViscousBuilder::invalidateBadSmooth( _SolidData&               data,
5541                                           SMESH_MesherHelper&       helper,
5542                                           vector< _LayerEdge* >&    badSmooEdges,
5543                                           vector< _EdgesOnShape* >& eosC1,
5544                                           const int                 infStep )
5545 {
5546   if ( badSmooEdges.empty() || infStep == 0 ) return 0;
5547
5548   dumpFunction(SMESH_Comment("invalidateBadSmooth")<<"_S"<<eosC1[0]->_shapeID<<"_InfStep"<<infStep);
5549
5550   enum {
5551     INVALIDATED   = _LayerEdge::UNUSED_FLAG,
5552     TO_INVALIDATE = _LayerEdge::UNUSED_FLAG * 2,
5553     ADDED         = _LayerEdge::UNUSED_FLAG * 4
5554   };
5555   data.UnmarkEdges( TO_INVALIDATE & INVALIDATED & ADDED );
5556
5557   double vol;
5558   bool haveInvalidated = true;
5559   while ( haveInvalidated )
5560   {
5561     haveInvalidated = false;
5562     for ( size_t i = 0; i < badSmooEdges.size(); ++i )
5563     {
5564       _LayerEdge*   edge = badSmooEdges[i];
5565       _EdgesOnShape* eos = data.GetShapeEdges( edge );
5566       edge->Set( ADDED );
5567       bool invalidated = false;
5568       if ( edge->Is( TO_INVALIDATE ) && edge->NbSteps() > 1 )
5569       {
5570         edge->InvalidateStep( edge->NbSteps(), *eos, /*restoreLength=*/true );
5571         edge->Block( data );
5572         edge->Set( INVALIDATED );
5573         edge->Unset( TO_INVALIDATE );
5574         invalidated = true;
5575         haveInvalidated = true;
5576       }
5577
5578       // look for _LayerEdge's of bad _simplices
5579       int nbBad = 0;
5580       SMESH_TNodeXYZ tgtXYZ  = edge->_nodes.back();
5581       gp_XYZ        prevXYZ1 = edge->PrevCheckPos( eos );
5582       //const gp_XYZ& prevXYZ2 = edge->PrevPos();
5583       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5584       {
5585         if (( edge->_simplices[j].IsForward( &prevXYZ1, &tgtXYZ, vol ))/* &&
5586             ( &prevXYZ1 == &prevXYZ2 || edge->_simplices[j].IsForward( &prevXYZ2, &tgtXYZ, vol ))*/)
5587           continue;
5588
5589         bool isBad = true;
5590         _LayerEdge* ee[2] = { 0,0 };
5591         for ( size_t iN = 0; iN < edge->_neibors.size() &&   !ee[1]  ; ++iN )
5592           if ( edge->_simplices[j].Includes( edge->_neibors[iN]->_nodes.back() ))
5593             ee[ ee[0] != 0 ] = edge->_neibors[iN];
5594
5595         int maxNbSteps = Max( ee[0]->NbSteps(), ee[1]->NbSteps() );
5596         while ( maxNbSteps > edge->NbSteps() && isBad )
5597         {
5598           --maxNbSteps;
5599           for ( int iE = 0; iE < 2; ++iE )
5600           {
5601             if ( ee[ iE ]->NbSteps() > maxNbSteps &&
5602                  ee[ iE ]->NbSteps() > 1 )
5603             {
5604               _EdgesOnShape* eos = data.GetShapeEdges( ee[ iE ] );
5605               ee[ iE ]->InvalidateStep( ee[ iE ]->NbSteps(), *eos, /*restoreLength=*/true );
5606               ee[ iE ]->Block( data );
5607               ee[ iE ]->Set( INVALIDATED );
5608               haveInvalidated = true;
5609             }
5610           }
5611           if (( edge->_simplices[j].IsForward( &prevXYZ1, &tgtXYZ, vol )) /*&&
5612               ( &prevXYZ1 == &prevXYZ2 || edge->_simplices[j].IsForward( &prevXYZ2, &tgtXYZ, vol ))*/)
5613             isBad = false;
5614         }
5615         nbBad += isBad;
5616         if ( !ee[0]->Is( ADDED )) badSmooEdges.push_back( ee[0] );
5617         if ( !ee[1]->Is( ADDED )) badSmooEdges.push_back( ee[1] );
5618         ee[0]->Set( ADDED );
5619         ee[1]->Set( ADDED );
5620         if ( isBad )
5621         {
5622           ee[0]->Set( TO_INVALIDATE );
5623           ee[1]->Set( TO_INVALIDATE );
5624         }
5625       }
5626
5627       if ( !invalidated &&  nbBad > 0  &&  edge->NbSteps() > 1 )
5628       {
5629         _EdgesOnShape* eos = data.GetShapeEdges( edge );
5630         edge->InvalidateStep( edge->NbSteps(), *eos, /*restoreLength=*/true );
5631         edge->Block( data );
5632         edge->Set( INVALIDATED );
5633         edge->Unset( TO_INVALIDATE );
5634         haveInvalidated = true;
5635       }
5636     } // loop on badSmooEdges
5637   } // while ( haveInvalidated )
5638
5639   // re-smooth on analytical EDGEs
5640   for ( size_t i = 0; i < badSmooEdges.size(); ++i )
5641   {
5642     _LayerEdge* edge = badSmooEdges[i];
5643     if ( !edge->Is( INVALIDATED )) continue;
5644
5645     _EdgesOnShape* eos = data.GetShapeEdges( edge );
5646     if ( eos->ShapeType() == TopAbs_VERTEX )
5647     {
5648       PShapeIteratorPtr eIt = helper.GetAncestors( eos->_shape, *_mesh, TopAbs_EDGE );
5649       while ( const TopoDS_Shape* e = eIt->next() )
5650         if ( _EdgesOnShape* eoe = data.GetShapeEdges( *e ))
5651           if ( eoe->_edgeSmoother && eoe->_edgeSmoother->isAnalytic() )
5652           {
5653             // TopoDS_Face F; Handle(ShapeAnalysis_Surface) surface;
5654             // if ( eoe->SWOLType() == TopAbs_FACE ) {
5655             //   F       = TopoDS::Face( eoe->_sWOL );
5656             //   surface = helper.GetSurface( F );
5657             // }
5658             // eoe->_edgeSmoother->Perform( data, surface, F, helper );
5659             eoe->_edgeSmoother->_anaCurve.Nullify();
5660           }
5661     }
5662   }
5663
5664
5665   // check result of invalidation
5666
5667   int nbBad = 0;
5668   for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5669   {
5670     for ( size_t i = 0; i < eosC1[ iEOS ]->_edges.size(); ++i )
5671     {
5672       if ( !eosC1[ iEOS ]->_sWOL.IsNull() ) continue;
5673       _LayerEdge*      edge = eosC1[ iEOS ]->_edges[i];
5674       SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
5675       gp_XYZ        prevXYZ = edge->PrevCheckPos( eosC1[ iEOS ]);
5676       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5677         if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
5678         {
5679           ++nbBad;
5680           debugMsg("Bad simplex remains ( " << edge->_nodes[0]->GetID()
5681                    << " "<< tgtXYZ._node->GetID()
5682                    << " "<< edge->_simplices[j]._nPrev->GetID()
5683                    << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
5684         }
5685     }
5686   }
5687   dumpFunctionEnd();
5688
5689   return nbBad;
5690 }
5691
5692 //================================================================================
5693 /*!
5694  * \brief Create an offset surface
5695  */
5696 //================================================================================
5697
5698 void _ViscousBuilder::makeOffsetSurface( _EdgesOnShape& eos, SMESH_MesherHelper& helper )
5699 {
5700   if ( eos._offsetSurf.IsNull() ||
5701        eos._edgeForOffset == 0 ||
5702        eos._edgeForOffset->Is( _LayerEdge::BLOCKED ))
5703     return;
5704
5705   Handle(ShapeAnalysis_Surface) baseSurface = helper.GetSurface( TopoDS::Face( eos._shape ));
5706
5707   // find offset
5708   gp_Pnt   tgtP = SMESH_TNodeXYZ( eos._edgeForOffset->_nodes.back() );
5709   /*gp_Pnt2d uv=*/baseSurface->ValueOfUV( tgtP, Precision::Confusion() );
5710   eos._offsetValue = baseSurface->Gap();
5711
5712   eos._offsetSurf.Nullify();
5713
5714   try
5715   {
5716     BRepOffsetAPI_MakeOffsetShape offsetMaker;
5717     offsetMaker.PerformByJoin( eos._shape, -eos._offsetValue, Precision::Confusion() );
5718     if ( !offsetMaker.IsDone() ) return;
5719
5720     TopExp_Explorer fExp( offsetMaker.Shape(), TopAbs_FACE );
5721     if ( !fExp.More() ) return;
5722
5723     TopoDS_Face F = TopoDS::Face( fExp.Current() );
5724     Handle(Geom_Surface) surf = BRep_Tool::Surface( F );
5725     if ( surf.IsNull() ) return;
5726
5727     eos._offsetSurf = new ShapeAnalysis_Surface( surf );
5728   }
5729   catch ( Standard_Failure& )
5730   {
5731   }
5732 }
5733
5734 //================================================================================
5735 /*!
5736  * \brief Put nodes of a curved FACE to its offset surface
5737  */
5738 //================================================================================
5739
5740 void _ViscousBuilder::putOnOffsetSurface( _EdgesOnShape&            eos,
5741                                           int                       infStep,
5742                                           vector< _EdgesOnShape* >& eosC1,
5743                                           int                       smooStep,
5744                                           int                       moveAll )
5745 {
5746   _EdgesOnShape * eof = & eos;
5747   if ( eos.ShapeType() != TopAbs_FACE ) // eos is a boundary of C1 FACE, look for the FACE eos
5748   {
5749     eof = 0;
5750     for ( size_t i = 0; i < eosC1.size() && !eof; ++i )
5751     {
5752       if ( eosC1[i]->_offsetSurf.IsNull() ||
5753            eosC1[i]->ShapeType() != TopAbs_FACE ||
5754            eosC1[i]->_edgeForOffset == 0 ||
5755            eosC1[i]->_edgeForOffset->Is( _LayerEdge::BLOCKED ))
5756         continue;
5757       if ( SMESH_MesherHelper::IsSubShape( eos._shape, eosC1[i]->_shape ))
5758         eof = eosC1[i];
5759     }
5760   }
5761   if ( !eof ||
5762        eof->_offsetSurf.IsNull() ||
5763        eof->ShapeType() != TopAbs_FACE ||
5764        eof->_edgeForOffset == 0 ||
5765        eof->_edgeForOffset->Is( _LayerEdge::BLOCKED ))
5766     return;
5767
5768   double preci = BRep_Tool::Tolerance( TopoDS::Face( eof->_shape )), vol;
5769   bool neighborHasRiskySWOL = false;
5770   for ( size_t i = 0; i < eos._edges.size(); ++i )
5771   {
5772     _LayerEdge* edge = eos._edges[i];
5773     edge->Unset( _LayerEdge::MARKED );
5774     if ( edge->Is( _LayerEdge::BLOCKED ) || !edge->_curvature )
5775       continue;
5776     if ( moveAll == _LayerEdge::UPD_NORMAL_CONV )
5777     {
5778       if ( !edge->Is( _LayerEdge::UPD_NORMAL_CONV ))
5779         continue;
5780     }
5781     else if ( moveAll == _LayerEdge::RISKY_SWOL )
5782     {
5783       if ( !edge->Is( _LayerEdge::RISKY_SWOL ) ||
5784            edge->_cosin < 0 )
5785         continue;
5786     }
5787     else if ( !moveAll && !edge->Is( _LayerEdge::MOVED ))
5788       continue;
5789
5790     int nbBlockedAround = 0;
5791     for ( size_t iN = 0; iN < edge->_neibors.size(); ++iN )
5792     {
5793       nbBlockedAround += edge->_neibors[iN]->Is( _LayerEdge::BLOCKED );
5794       if ( edge->_neibors[iN]->Is( _LayerEdge::RISKY_SWOL ) &&
5795            edge->_neibors[iN]->_cosin > 0 )
5796         neighborHasRiskySWOL = true;
5797     }
5798     if ( nbBlockedAround > 1 )
5799       continue;
5800
5801     gp_Pnt tgtP = SMESH_TNodeXYZ( edge->_nodes.back() );
5802     gp_Pnt2d uv = eof->_offsetSurf->NextValueOfUV( edge->_curvature->_uv, tgtP, preci );
5803     if ( eof->_offsetSurf->Gap() > edge->_len ) continue; // NextValueOfUV() bug
5804     edge->_curvature->_uv = uv;
5805     if ( eof->_offsetSurf->Gap() < 10 * preci ) continue; // same pos
5806
5807     gp_XYZ  newP = eof->_offsetSurf->Value( uv ).XYZ();
5808     gp_XYZ prevP = edge->PrevCheckPos();
5809     bool      ok = true;
5810     if ( !moveAll )
5811       for ( size_t iS = 0; iS < edge->_simplices.size() && ok; ++iS )
5812       {
5813         ok = edge->_simplices[iS].IsForward( &prevP, &newP, vol );
5814       }
5815     if ( ok )
5816     {
5817       SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( edge->_nodes.back() );
5818       n->setXYZ( newP.X(), newP.Y(), newP.Z());
5819       edge->_pos.back() = newP;
5820
5821       edge->Set( _LayerEdge::MARKED );
5822       if ( moveAll == _LayerEdge::UPD_NORMAL_CONV )
5823       {
5824         edge->_normal = ( newP - prevP ).Normalized();
5825       }
5826       // if ( edge->_len < eof->_offsetValue )
5827       //   edge->_len = eof->_offsetValue;
5828
5829       if ( !eos._sWOL.IsNull() ) // RISKY_SWOL
5830       {
5831         double change = eof->_offsetSurf->Gap() / eof->_offsetValue;
5832         if (( newP - tgtP.XYZ() ) * edge->_normal < 0 )
5833           change = 1 - change;
5834         else
5835           change = 1 + change;
5836         gp_XYZ shitfVec    = tgtP.XYZ() - SMESH_NodeXYZ( edge->_nodes[0] );
5837         gp_XYZ newShiftVec = shitfVec * change;
5838         double shift       = edge->_normal * shitfVec;
5839         double newShift    = edge->_normal * newShiftVec;
5840         newP = tgtP.XYZ() + edge->_normal * ( newShift - shift );
5841
5842         uv = eof->_offsetSurf->NextValueOfUV( edge->_curvature->_uv, newP, preci );
5843         if ( eof->_offsetSurf->Gap() < edge->_len )
5844         {
5845           edge->_curvature->_uv = uv;
5846           newP = eof->_offsetSurf->Value( uv ).XYZ();
5847         }
5848         n->setXYZ( newP.X(), newP.Y(), newP.Z());
5849         if ( !edge->UpdatePositionOnSWOL( n, /*tol=*/10 * edge->_len / ( edge->NbSteps() + 1 ),
5850                                           eos, eos.GetData().GetHelper() ))
5851         {
5852           debugMsg("UpdatePositionOnSWOL fails in putOnOffsetSurface()" );
5853         }
5854       }
5855     }
5856   }
5857
5858   if (SALOME::VerbosityActivated())
5859   {
5860     // dumpMove() for debug
5861     size_t i = 0;
5862     for ( ; i < eos._edges.size(); ++i )
5863       if ( eos._edges[i]->Is( _LayerEdge::MARKED ))
5864         break;
5865     if ( i < eos._edges.size() )
5866     {
5867       dumpFunction(SMESH_Comment("putOnOffsetSurface_") << eos.ShapeTypeLetter() << eos._shapeID
5868                   << "_InfStep" << infStep << "_" << Abs( smooStep ));
5869       for ( ; i < eos._edges.size(); ++i )
5870       {
5871         if ( eos._edges[i]->Is( _LayerEdge::MARKED )) {
5872           dumpMove( eos._edges[i]->_nodes.back() );
5873         }
5874       }
5875       dumpFunctionEnd();
5876     }
5877   }
5878
5879   _ConvexFace* cnvFace;
5880   if ( moveAll != _LayerEdge::UPD_NORMAL_CONV &&
5881        eos.ShapeType() == TopAbs_FACE &&
5882        (cnvFace = eos.GetData().GetConvexFace( eos._shapeID )) &&
5883        !cnvFace->_normalsFixedOnBorders )
5884   {
5885     // put on the surface nodes built on FACE boundaries
5886     SMESH_subMeshIteratorPtr smIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false);
5887     while ( smIt->more() )
5888     {
5889       SMESH_subMesh*     sm = smIt->next();
5890       _EdgesOnShape* subEOS = eos.GetData().GetShapeEdges( sm->GetId() );
5891       if ( !subEOS->_sWOL.IsNull() ) continue;
5892       if ( std::find( eosC1.begin(), eosC1.end(), subEOS ) != eosC1.end() ) continue;
5893
5894       putOnOffsetSurface( *subEOS, infStep, eosC1, smooStep, _LayerEdge::UPD_NORMAL_CONV );
5895     }
5896     cnvFace->_normalsFixedOnBorders = true;
5897   }
5898
5899
5900   // bos #20643
5901   // negative smooStep means "final step", where we don't treat RISKY_SWOL edges
5902   // as edges based on FACE are a bit late comparing with them
5903   if ( smooStep >= 0 &&
5904        neighborHasRiskySWOL &&
5905        moveAll != _LayerEdge::RISKY_SWOL &&
5906        eos.ShapeType() == TopAbs_FACE )
5907   {
5908     // put on the surface nodes built on FACE boundaries
5909     SMESH_subMeshIteratorPtr smIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false);
5910     while ( smIt->more() )
5911     {
5912       SMESH_subMesh*     sm = smIt->next();
5913       _EdgesOnShape* subEOS = eos.GetData().GetShapeEdges( sm->GetId() );
5914       if ( subEOS->_sWOL.IsNull() ) continue;
5915       if ( std::find( eosC1.begin(), eosC1.end(), subEOS ) != eosC1.end() ) continue;
5916
5917       putOnOffsetSurface( *subEOS, infStep, eosC1, smooStep, _LayerEdge::RISKY_SWOL );
5918     }
5919   }
5920 }
5921
5922 //================================================================================
5923 /*!
5924  * \brief Return a curve of the EDGE to be used for smoothing and arrange
5925  *        _LayerEdge's to be in a consequent order
5926  */
5927 //================================================================================
5928
5929 Handle(Geom_Curve) _Smoother1D::CurveForSmooth( const TopoDS_Edge&  E,
5930                                                 _EdgesOnShape&      eos,
5931                                                 SMESH_MesherHelper& helper)
5932 {
5933   SMESHDS_SubMesh* smDS = eos._subMesh->GetSubMeshDS();
5934
5935   TopLoc_Location loc; double f,l;
5936
5937   Handle(Geom_Line)   line;
5938   Handle(Geom_Circle) circle;
5939   bool isLine, isCirc;
5940   if ( eos._sWOL.IsNull() ) /////////////////////////////////////////// 3D case
5941   {
5942     // check if the EDGE is a line
5943     Handle(Geom_Curve) curve = BRep_Tool::Curve( E, f, l);
5944     if ( curve->IsKind( STANDARD_TYPE( Geom_TrimmedCurve )))
5945       curve = Handle(Geom_TrimmedCurve)::DownCast( curve )->BasisCurve();
5946
5947     line   = Handle(Geom_Line)::DownCast( curve );
5948     circle = Handle(Geom_Circle)::DownCast( curve );
5949     isLine = (!line.IsNull());
5950     isCirc = (!circle.IsNull());
5951
5952     if ( !isLine && !isCirc ) // Check if the EDGE is close to a line
5953     {
5954       isLine = SMESH_Algo::IsStraight( E );
5955
5956       if ( isLine )
5957         line = new Geom_Line( gp::OX() ); // only type does matter
5958     }
5959     if ( !isLine && !isCirc && eos._edges.size() > 2) // Check if the EDGE is close to a circle
5960     {
5961       // TODO
5962     }
5963   }
5964   else //////////////////////////////////////////////////////////////////////// 2D case
5965   {
5966     if ( !eos._isRegularSWOL ) // 23190
5967       return NULL;
5968
5969     const TopoDS_Face& F = TopoDS::Face( eos._sWOL );
5970
5971     // check if the EDGE is a line
5972     Handle(Geom2d_Curve) curve = BRep_Tool::CurveOnSurface( E, F, f, l );
5973     if ( curve->IsKind( STANDARD_TYPE( Geom2d_TrimmedCurve )))
5974       curve = Handle(Geom2d_TrimmedCurve)::DownCast( curve )->BasisCurve();
5975
5976     Handle(Geom2d_Line)   line2d   = Handle(Geom2d_Line)::DownCast( curve );
5977     Handle(Geom2d_Circle) circle2d = Handle(Geom2d_Circle)::DownCast( curve );
5978     isLine = (!line2d.IsNull());
5979     isCirc = (!circle2d.IsNull());
5980
5981     if ( !isLine && !isCirc ) // Check if the EDGE is close to a line
5982     {
5983       Bnd_B2d bndBox;
5984       SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
5985       while ( nIt->more() )
5986         bndBox.Add( helper.GetNodeUV( F, nIt->next() ));
5987       gp_XY size = bndBox.CornerMax() - bndBox.CornerMin();
5988
5989       const double lineTol = 1e-2 * sqrt( bndBox.SquareExtent() );
5990       for ( int i = 0; i < 2 && !isLine; ++i )
5991         isLine = ( size.Coord( i+1 ) <= lineTol );
5992     }
5993     if ( !isLine && !isCirc && eos._edges.size() > 2 ) // Check if the EDGE is close to a circle
5994     {
5995       // TODO
5996     }
5997     if ( isLine )
5998     {
5999       line = new Geom_Line( gp::OX() ); // only type does matter
6000     }
6001     else if ( isCirc )
6002     {
6003       gp_Pnt2d p = circle2d->Location();
6004       gp_Ax2 ax( gp_Pnt( p.X(), p.Y(), 0), gp::DX());
6005       circle = new Geom_Circle( ax, 1.); // only center position does matter
6006     }
6007   }
6008
6009   if ( isLine )
6010     return line;
6011   if ( isCirc )
6012     return circle;
6013
6014   return Handle(Geom_Curve)();
6015 }
6016
6017 //================================================================================
6018 /*!
6019  * \brief Smooth edges on EDGE
6020  */
6021 //================================================================================
6022
6023 bool _Smoother1D::Perform(_SolidData&                    data,
6024                           Handle(ShapeAnalysis_Surface)& surface,
6025                           const TopoDS_Face&             F,
6026                           SMESH_MesherHelper&            helper )
6027 {
6028   if ( _leParams.empty() || ( !isAnalytic() && _offPoints.empty() ))
6029     prepare( data );
6030
6031   findEdgesToSmooth();
6032   if ( isAnalytic() )
6033     return smoothAnalyticEdge( data, surface, F, helper );
6034   else
6035     return smoothComplexEdge ( data, surface, F, helper );
6036 }
6037
6038 //================================================================================
6039 /*!
6040  * \brief Find edges to smooth
6041  */
6042 //================================================================================
6043
6044 void _Smoother1D::findEdgesToSmooth()
6045 {
6046   _LayerEdge* leOnV[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
6047   for ( int iEnd = 0; iEnd < 2; ++iEnd )
6048     if ( leOnV[iEnd]->Is( _LayerEdge::NORMAL_UPDATED ))
6049       _leOnV[iEnd]._cosin = Abs( _edgeDir[iEnd].Normalized() * leOnV[iEnd]->_normal );
6050
6051   _eToSmooth[0].first = _eToSmooth[0].second = 0;
6052
6053   for ( size_t i = 0; i < _eos.size(); ++i )
6054   {
6055     if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH ))
6056     {
6057       if ( needSmoothing( _leOnV[0]._cosin,
6058                           _eos[i]->_len * leOnV[0]->_lenFactor, _curveLen * _leParams[i] ) ||
6059            isToSmooth( i )
6060            )
6061         _eos[i]->Set( _LayerEdge::TO_SMOOTH );
6062       else
6063         break;
6064     }
6065     _eToSmooth[0].second = i+1;
6066   }
6067
6068   _eToSmooth[1].first = _eToSmooth[1].second = _eos.size();
6069
6070   for ( int i = _eos.size() - 1; i >= _eToSmooth[0].second; --i )
6071   {
6072     if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH ))
6073     {
6074       if ( needSmoothing( _leOnV[1]._cosin,
6075                           _eos[i]->_len * leOnV[1]->_lenFactor, _curveLen * ( 1.-_leParams[i] )) ||
6076            isToSmooth( i ))
6077         _eos[i]->Set( _LayerEdge::TO_SMOOTH );
6078       else
6079         break;
6080     }
6081     _eToSmooth[1].first = i;
6082   }
6083 }
6084
6085 //================================================================================
6086 /*!
6087  * \brief Check if iE-th _LayerEdge needs smoothing
6088  */
6089 //================================================================================
6090
6091 bool _Smoother1D::isToSmooth( int iE )
6092 {
6093   SMESH_NodeXYZ pi( _eos[iE]->_nodes[0] );
6094   SMESH_NodeXYZ p0( _eos[iE]->_2neibors->srcNode(0) );
6095   SMESH_NodeXYZ p1( _eos[iE]->_2neibors->srcNode(1) );
6096   gp_XYZ       seg0 = pi - p0;
6097   gp_XYZ       seg1 = p1 - pi;
6098   gp_XYZ    tangent =  seg0 + seg1;
6099   double tangentLen = tangent.Modulus();
6100   double  segMinLen = Min( seg0.Modulus(), seg1.Modulus() );
6101   if ( tangentLen < std::numeric_limits<double>::min() )
6102     return false;
6103   tangent /= tangentLen;
6104
6105   for ( size_t i = 0; i < _eos[iE]->_neibors.size(); ++i )
6106   {
6107     _LayerEdge* ne = _eos[iE]->_neibors[i];
6108     if ( !ne->Is( _LayerEdge::TO_SMOOTH ) ||
6109          ne->_nodes.size() < 2 ||
6110          ne->_nodes[0]->GetPosition()->GetDim() != 2 )
6111       continue;
6112     gp_XYZ edgeVec = SMESH_NodeXYZ( ne->_nodes.back() ) - SMESH_NodeXYZ( ne->_nodes[0] );
6113     double    proj = edgeVec * tangent;
6114     if ( needSmoothing( 1., proj, segMinLen ))
6115       return true;
6116   }
6117   return false;
6118 }
6119
6120 //================================================================================
6121 /*!
6122  * \brief smooth _LayerEdge's on a staight EDGE or circular EDGE
6123  */
6124 //================================================================================
6125
6126 bool _Smoother1D::smoothAnalyticEdge( _SolidData&                    data,
6127                                       Handle(ShapeAnalysis_Surface)& surface,
6128                                       const TopoDS_Face&             F,
6129                                       SMESH_MesherHelper&            helper)
6130 {
6131   if ( !isAnalytic() ) return false;
6132
6133   size_t iFrom = 0, iTo = _eos._edges.size();
6134
6135   if ( _anaCurve->IsKind( STANDARD_TYPE( Geom_Line )))
6136   {
6137     if ( F.IsNull() ) // 3D
6138     {
6139       SMESH_TNodeXYZ pSrc0( _eos._edges[iFrom]->_2neibors->srcNode(0) );
6140       SMESH_TNodeXYZ pSrc1( _eos._edges[iTo-1]->_2neibors->srcNode(1) );
6141       //const   gp_XYZ lineDir = pSrc1 - pSrc0;
6142       //_LayerEdge* vLE0 = getLEdgeOnV( 0 );
6143       //_LayerEdge* vLE1 = getLEdgeOnV( 1 );
6144       // bool shiftOnly = ( vLE0->Is( _LayerEdge::NORMAL_UPDATED ) ||
6145       //                    vLE0->Is( _LayerEdge::BLOCKED ) ||
6146       //                    vLE1->Is( _LayerEdge::NORMAL_UPDATED ) ||
6147       //                    vLE1->Is( _LayerEdge::BLOCKED ));
6148       for ( int iEnd = 0; iEnd < 2; ++iEnd )
6149       {
6150         iFrom = _eToSmooth[ iEnd ].first, iTo = _eToSmooth[ iEnd ].second;
6151         if ( iFrom >= iTo ) continue;
6152         SMESH_TNodeXYZ p0( _eos[iFrom]->_2neibors->tgtNode(0) );
6153         SMESH_TNodeXYZ p1( _eos[iTo-1]->_2neibors->tgtNode(1) );
6154         double param0 = ( iFrom == 0 ) ? 0. : _leParams[ iFrom-1 ];
6155         double param1 = _leParams[ iTo ];
6156         for ( size_t i = iFrom; i < iTo; ++i )
6157         {
6158           _LayerEdge*       edge = _eos[i];
6159           SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edge->_nodes.back() );
6160           double           param = ( _leParams[i] - param0 ) / ( param1 - param0 );
6161           gp_XYZ          newPos = p0 * ( 1. - param ) + p1 * param;
6162
6163           // if ( shiftOnly || edge->Is( _LayerEdge::NORMAL_UPDATED ))
6164           // {
6165           //   gp_XYZ curPos = SMESH_TNodeXYZ ( tgtNode );
6166           //   double  shift = ( lineDir * ( newPos - pSrc0 ) -
6167           //                     lineDir * ( curPos - pSrc0 ));
6168           //   newPos = curPos + lineDir * shift / lineDir.SquareModulus();
6169           // }
6170           if ( edge->Is( _LayerEdge::BLOCKED ))
6171           {
6172             SMESH_TNodeXYZ pSrc( edge->_nodes[0] );
6173             double curThick = pSrc.SquareDistance( tgtNode );
6174             double newThink = ( pSrc - newPos ).SquareModulus();
6175             if ( newThink > curThick )
6176               continue;
6177           }
6178           edge->_pos.back() = newPos;
6179           tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
6180           dumpMove( tgtNode );
6181         }
6182       }
6183     }
6184     else // 2D
6185     {
6186       _LayerEdge* eV0 = getLEdgeOnV( 0 );
6187       _LayerEdge* eV1 = getLEdgeOnV( 1 );
6188       gp_XY      uvV0 = eV0->LastUV( F, *data.GetShapeEdges( eV0 ));
6189       gp_XY      uvV1 = eV1->LastUV( F, *data.GetShapeEdges( eV1 ));
6190       if ( eV0->_nodes.back() == eV1->_nodes.back() ) // closed edge
6191       {
6192         int iPeriodic = helper.GetPeriodicIndex();
6193         if ( iPeriodic == 1 || iPeriodic == 2 )
6194         {
6195           uvV1.SetCoord( iPeriodic, helper.GetOtherParam( uvV1.Coord( iPeriodic )));
6196           if ( uvV0.Coord( iPeriodic ) > uvV1.Coord( iPeriodic ))
6197             std::swap( uvV0, uvV1 );
6198         }
6199       }
6200       for ( int iEnd = 0; iEnd < 2; ++iEnd )
6201       {
6202         iFrom = _eToSmooth[ iEnd ].first, iTo = _eToSmooth[ iEnd ].second;
6203         if ( iFrom >= iTo ) continue;
6204         _LayerEdge* e0 = _eos[iFrom]->_2neibors->_edges[0];
6205         _LayerEdge* e1 = _eos[iTo-1]->_2neibors->_edges[1];
6206         gp_XY      uv0 = ( e0 == eV0 ) ? uvV0 : e0->LastUV( F, _eos );
6207         gp_XY      uv1 = ( e1 == eV1 ) ? uvV1 : e1->LastUV( F, _eos );
6208         double  param0 = ( iFrom == 0 ) ? 0. : _leParams[ iFrom-1 ];
6209         double  param1 = _leParams[ iTo ];
6210         gp_XY  rangeUV = uv1 - uv0;
6211         for ( size_t i = iFrom; i < iTo; ++i )
6212         {
6213           if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
6214           double param = ( _leParams[i] - param0 ) / ( param1 - param0 );
6215           gp_XY newUV = uv0 + param * rangeUV;
6216
6217           gp_Pnt newPos = surface->Value( newUV.X(), newUV.Y() );
6218           SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos[i]->_nodes.back() );
6219           tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
6220           dumpMove( tgtNode );
6221
6222           if ( SMDS_FacePositionPtr pos = tgtNode->GetPosition() ) // NULL if F is noShrink
6223           {
6224             pos->SetUParameter( newUV.X() );
6225             pos->SetVParameter( newUV.Y() );
6226           }
6227
6228           gp_XYZ newUV0( newUV.X(), newUV.Y(), 0 );
6229
6230           if ( !_eos[i]->Is( _LayerEdge::SMOOTHED ))
6231           {
6232             _eos[i]->Set( _LayerEdge::SMOOTHED ); // to check in refine() (IPAL54237)
6233             if ( _eos[i]->_pos.size() > 2 )
6234             {
6235               // modify previous positions to make _LayerEdge less sharply bent
6236               vector<gp_XYZ>& uvVec = _eos[i]->_pos;
6237               const gp_XYZ  uvShift = newUV0 - uvVec.back();
6238               const double     len2 = ( uvVec.back() - uvVec[ 0 ] ).SquareModulus();
6239               int iPrev = uvVec.size() - 2;
6240               while ( iPrev > 0 )
6241               {
6242                 double r = ( uvVec[ iPrev ] - uvVec[0] ).SquareModulus() / len2;
6243                 uvVec[ iPrev ] += uvShift * r;
6244                 --iPrev;
6245               }
6246             }
6247           }
6248           _eos[i]->_pos.back() = newUV0;
6249         }
6250       }
6251     }
6252     return true;
6253   }
6254
6255   if ( _anaCurve->IsKind( STANDARD_TYPE( Geom_Circle )))
6256   {
6257     Handle(Geom_Circle) circle = Handle(Geom_Circle)::DownCast( _anaCurve );
6258     gp_Pnt center3D = circle->Location();
6259
6260     if ( F.IsNull() ) // 3D
6261     {
6262       if ( getLEdgeOnV( 0 )->_nodes.back() == getLEdgeOnV( 1 )->_nodes.back() )
6263         return true; // closed EDGE - nothing to do
6264
6265       // circle is a real curve of EDGE
6266       gp_Circ circ = circle->Circ();
6267
6268       // new center is shifted along its axis
6269       const gp_Dir& axis = circ.Axis().Direction();
6270       _LayerEdge*     e0 = getLEdgeOnV(0);
6271       _LayerEdge*     e1 = getLEdgeOnV(1);
6272       SMESH_TNodeXYZ  p0 = e0->_nodes.back();
6273       SMESH_TNodeXYZ  p1 = e1->_nodes.back();
6274       double      shift1 = axis.XYZ() * ( p0 - center3D.XYZ() );
6275       double      shift2 = axis.XYZ() * ( p1 - center3D.XYZ() );
6276       gp_Pnt   newCenter = center3D.XYZ() + axis.XYZ() * 0.5 * ( shift1 + shift2 );
6277
6278       double newRadius = 0.5 * ( newCenter.Distance( p0 ) + newCenter.Distance( p1 ));
6279
6280       gp_Ax2  newAxis( newCenter, axis, gp_Vec( newCenter, p0 ));
6281       gp_Circ newCirc( newAxis, newRadius );
6282       gp_Vec  vecC1  ( newCenter, p1 );
6283
6284       double uLast = newAxis.XDirection().AngleWithRef( vecC1, newAxis.Direction() ); // -PI - +PI
6285       if ( uLast < 0 )
6286         uLast += 2 * M_PI;
6287       
6288       for ( size_t i = 0; i < _eos.size(); ++i )
6289       {
6290         if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
6291         //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
6292         double u = uLast * _leParams[i];
6293         gp_Pnt p = ElCLib::Value( u, newCirc );
6294         _eos._edges[i]->_pos.back() = p.XYZ();
6295
6296         SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos._edges[i]->_nodes.back() );
6297         tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
6298         dumpMove( tgtNode );
6299       }
6300       return true;
6301     }
6302     else // 2D
6303     {
6304       const gp_XY center( center3D.X(), center3D.Y() );
6305
6306       _LayerEdge* e0 = getLEdgeOnV(0);
6307       _LayerEdge* eM = _eos._edges[ 0 ];
6308       _LayerEdge* e1 = getLEdgeOnV(1);
6309       gp_XY      uv0 = e0->LastUV( F, *data.GetShapeEdges( e0 ) );
6310       gp_XY      uvM = eM->LastUV( F, *data.GetShapeEdges( eM ) );
6311       gp_XY      uv1 = e1->LastUV( F, *data.GetShapeEdges( e1 ) );
6312       gp_Vec2d vec0( center, uv0 );
6313       gp_Vec2d vecM( center, uvM );
6314       gp_Vec2d vec1( center, uv1 );
6315       double uLast = vec0.Angle( vec1 ); // -PI - +PI
6316       double uMidl = vec0.Angle( vecM );
6317       if ( uLast * uMidl <= 0. )
6318         uLast += ( uMidl > 0 ? +2. : -2. ) * M_PI;
6319       const double radius = 0.5 * ( vec0.Magnitude() + vec1.Magnitude() );
6320
6321       gp_Ax2d   axis( center, vec0 );
6322       gp_Circ2d circ( axis, radius );
6323       for ( size_t i = 0; i < _eos.size(); ++i )
6324       {
6325         if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
6326         //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
6327         double    newU = uLast * _leParams[i];
6328         gp_Pnt2d newUV = ElCLib::Value( newU, circ );
6329         _eos._edges[i]->_pos.back().SetCoord( newUV.X(), newUV.Y(), 0 );
6330
6331         gp_Pnt newPos = surface->Value( newUV.X(), newUV.Y() );
6332         SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos._edges[i]->_nodes.back() );
6333         tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
6334         dumpMove( tgtNode );
6335
6336         if ( SMDS_FacePositionPtr pos = tgtNode->GetPosition() ) // NULL if F is noShrink
6337         {
6338           pos->SetUParameter( newUV.X() );
6339           pos->SetVParameter( newUV.Y() );
6340         }
6341         _eos[i]->Set( _LayerEdge::SMOOTHED ); // to check in refine() (IPAL54237)
6342       }
6343     }
6344     return true;
6345   }
6346
6347   return false;
6348 }
6349
6350 //================================================================================
6351 /*!
6352  * \brief smooth _LayerEdge's on a an EDGE
6353  */
6354 //================================================================================
6355
6356 bool _Smoother1D::smoothComplexEdge( _SolidData&                    /*data*/,
6357                                      Handle(ShapeAnalysis_Surface)& surface,
6358                                      const TopoDS_Face&             F,
6359                                      SMESH_MesherHelper&            /*helper*/)
6360 {
6361   if ( _offPoints.empty() )
6362     return false;
6363
6364   // ----------------------------------------------
6365   // move _offPoints along normals of _LayerEdge's
6366   // ----------------------------------------------
6367
6368   _LayerEdge* e[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
6369   if ( e[0]->Is( _LayerEdge::NORMAL_UPDATED ))
6370     _leOnV[0]._normal = getNormalNormal( e[0]->_normal, _edgeDir[0] );
6371   if ( e[1]->Is( _LayerEdge::NORMAL_UPDATED )) 
6372     _leOnV[1]._normal = getNormalNormal( e[1]->_normal, _edgeDir[1] );
6373   _leOnV[0]._len = e[0]->_len;
6374   _leOnV[1]._len = e[1]->_len;
6375   for ( size_t i = 0; i < _offPoints.size(); i++ )
6376   {
6377     _LayerEdge*  e0 = _offPoints[i]._2edges._edges[0];
6378     _LayerEdge*  e1 = _offPoints[i]._2edges._edges[1];
6379     const double w0 = _offPoints[i]._2edges._wgt[0];
6380     const double w1 = _offPoints[i]._2edges._wgt[1];
6381     gp_XYZ  avgNorm = ( e0->_normal    * w0 + e1->_normal    * w1 ).Normalized();
6382     double  avgLen  = ( e0->_len       * w0 + e1->_len       * w1 );
6383     double  avgFact = ( e0->_lenFactor * w0 + e1->_lenFactor * w1 );
6384     if ( e0->Is( _LayerEdge::NORMAL_UPDATED ) ||
6385          e1->Is( _LayerEdge::NORMAL_UPDATED ))
6386       avgNorm = getNormalNormal( avgNorm, _offPoints[i]._edgeDir );
6387
6388     _offPoints[i]._xyz += avgNorm * ( avgLen - _offPoints[i]._len ) * avgFact;
6389     _offPoints[i]._len  = avgLen;
6390   }
6391
6392   double fTol = 0;
6393   if ( !surface.IsNull() ) // project _offPoints to the FACE
6394   {
6395     fTol = 100 * BRep_Tool::Tolerance( F );
6396     //const double segLen = _offPoints[0].Distance( _offPoints[1] );
6397
6398     gp_Pnt2d uv = surface->ValueOfUV( _offPoints[0]._xyz, fTol );
6399     //if ( surface->Gap() < 0.5 * segLen )
6400       _offPoints[0]._xyz = surface->Value( uv ).XYZ();
6401
6402     for ( size_t i = 1; i < _offPoints.size(); ++i )
6403     {
6404       uv = surface->NextValueOfUV( uv, _offPoints[i]._xyz, fTol );
6405       //if ( surface->Gap() < 0.5 * segLen )
6406         _offPoints[i]._xyz = surface->Value( uv ).XYZ();
6407     }
6408   }
6409
6410   // -----------------------------------------------------------------
6411   // project tgt nodes of extreme _LayerEdge's to the offset segments
6412   // -----------------------------------------------------------------
6413
6414   const int updatedOrBlocked = _LayerEdge::NORMAL_UPDATED | _LayerEdge::BLOCKED;
6415   if ( e[0]->Is( updatedOrBlocked )) _iSeg[0] = 0;
6416   if ( e[1]->Is( updatedOrBlocked )) _iSeg[1] = _offPoints.size()-2;
6417
6418   gp_Pnt pExtreme[2], pProj[2];
6419   bool isProjected[2];
6420   for ( int is2nd = 0; is2nd < 2; ++is2nd )
6421   {
6422     pExtreme[ is2nd ] = SMESH_TNodeXYZ( e[is2nd]->_nodes.back() );
6423     int  i = _iSeg[ is2nd ];
6424     int di = is2nd ? -1 : +1;
6425     bool & projected = isProjected[ is2nd ];
6426     projected = false;
6427     double uOnSeg, distMin = Precision::Infinite(), dist, distPrev = 0;
6428     int nbWorse = 0;
6429     do {
6430       gp_Vec v0p( _offPoints[i]._xyz, pExtreme[ is2nd ]    );
6431       gp_Vec v01( _offPoints[i]._xyz, _offPoints[i+1]._xyz );
6432       uOnSeg     = ( v0p * v01 ) / v01.SquareMagnitude();  // param [0,1] along v01
6433       projected  = ( Abs( uOnSeg - 0.5 ) <= 0.5 );
6434       dist       =  pExtreme[ is2nd ].SquareDistance( _offPoints[ i + ( uOnSeg > 0.5 )]._xyz );
6435       if ( dist < distMin || projected )
6436       {
6437         _iSeg[ is2nd ] = i;
6438         pProj[ is2nd ] = _offPoints[i]._xyz + ( v01 * uOnSeg ).XYZ();
6439         distMin = dist;
6440       }
6441       else if ( dist > distPrev )
6442       {
6443         if ( ++nbWorse > 3 ) // avoid projection to the middle of a closed EDGE
6444           break;
6445       }
6446       distPrev = dist;
6447       i += di;
6448     }
6449     while ( !projected &&
6450             i >= 0 && i+1 < (int)_offPoints.size() );
6451
6452     if ( !projected )
6453     {
6454       if (( is2nd && _iSeg[1] != _offPoints.size()-2 ) || ( !is2nd && _iSeg[0] != 0 ))
6455       {
6456         _iSeg[0] = 0;
6457         _iSeg[1] = _offPoints.size()-2;
6458         debugMsg( "smoothComplexEdge() failed to project nodes of extreme _LayerEdge's" );
6459         return false;
6460       }
6461     }
6462   }
6463   if ( _iSeg[0] > _iSeg[1] )
6464   {
6465     debugMsg( "smoothComplexEdge() incorrectly projected nodes of extreme _LayerEdge's" );
6466     return false;
6467   }
6468
6469   // adjust length of extreme LE (test viscous_layers_01/B7)
6470   gp_Vec vDiv0( pExtreme[0], pProj[0] );
6471   gp_Vec vDiv1( pExtreme[1], pProj[1] );
6472   double d0 = vDiv0.Magnitude();
6473   double d1 = isProjected[1] ? vDiv1.Magnitude() : 0;
6474   if ( e[0]->Is( _LayerEdge::BLOCKED )) {
6475     if ( e[0]->_normal * vDiv0.XYZ() < 0 ) e[0]->_len += d0;
6476     else                                   e[0]->_len -= d0;
6477   }
6478   if ( e[1]->Is( _LayerEdge::BLOCKED )) {
6479     if ( e[1]->_normal * vDiv1.XYZ() < 0 ) e[1]->_len += d1;
6480     else                                   e[1]->_len -= d1;
6481   }
6482
6483   // ---------------------------------------------------------------------------------
6484   // compute normalized length of the offset segments located between the projections
6485   // ---------------------------------------------------------------------------------
6486
6487   // temporary replace extreme _offPoints by pExtreme
6488   gp_XYZ opXYZ[2] = { _offPoints[ _iSeg[0]   ]._xyz,
6489                       _offPoints[ _iSeg[1]+1 ]._xyz };
6490   _offPoints[ _iSeg[0]   ]._xyz = pExtreme[0].XYZ();
6491   _offPoints[ _iSeg[1]+ 1]._xyz = pExtreme[1].XYZ();
6492
6493   size_t iSeg = 0, nbSeg = _iSeg[1] - _iSeg[0] + 1;
6494   vector< double > len( nbSeg + 1 );
6495   len[ iSeg++ ] = 0;
6496   len[ iSeg++ ] = pProj[ 0 ].Distance( _offPoints[ _iSeg[0]+1 ]._xyz );
6497   for ( size_t i = _iSeg[0]+1; i <= _iSeg[1]; ++i, ++iSeg )
6498   {
6499     len[ iSeg ] = len[ iSeg-1 ] + _offPoints[i].Distance( _offPoints[i+1] );
6500   }
6501   // if ( isProjected[ 1 ])
6502   //   len[ nbSeg ] -= pProj[ 1 ].Distance( _offPoints[ _iSeg[1]+1 ]._xyz );
6503   // else
6504   //   len[ nbSeg ] += pExtreme[ 1 ].Distance( _offPoints[ _iSeg[1]+1 ]._xyz );
6505
6506   double fullLen = len.back() - d0 - d1;
6507   for ( iSeg = 0; iSeg < len.size(); ++iSeg )
6508     len[iSeg] = ( len[iSeg] - d0 ) / fullLen;
6509
6510   // -------------------------------------------------------------
6511   // distribute tgt nodes of _LayerEdge's between the projections
6512   // -------------------------------------------------------------
6513
6514   iSeg = 0;
6515   for ( size_t i = 0; i < _eos.size(); ++i )
6516   {
6517     if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
6518     //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
6519     while ( iSeg+2 < len.size() && _leParams[i] > len[ iSeg+1 ] )
6520       iSeg++;
6521     double r = ( _leParams[i] - len[ iSeg ]) / ( len[ iSeg+1 ] - len[ iSeg ]);
6522     gp_XYZ p = ( _offPoints[ iSeg + _iSeg[0]     ]._xyz * ( 1 - r ) +
6523                  _offPoints[ iSeg + _iSeg[0] + 1 ]._xyz * r );
6524
6525     if ( surface.IsNull() )
6526     {
6527       _eos[i]->_pos.back() = p;
6528     }
6529     else // project a new node position to a FACE
6530     {
6531       gp_Pnt2d uv ( _eos[i]->_pos.back().X(), _eos[i]->_pos.back().Y() );
6532       gp_Pnt2d uv2( surface->NextValueOfUV( uv, p, fTol ));
6533
6534       p = surface->Value( uv2 ).XYZ();
6535       _eos[i]->_pos.back().SetCoord( uv2.X(), uv2.Y(), 0 );
6536     }
6537     SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos[i]->_nodes.back() );
6538     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
6539     dumpMove( tgtNode );
6540   }
6541
6542   _offPoints[ _iSeg[0]   ]._xyz = opXYZ[0];
6543   _offPoints[ _iSeg[1]+1 ]._xyz = opXYZ[1];
6544
6545   return true;
6546 }
6547
6548 //================================================================================
6549 /*!
6550  * \brief Prepare for smoothing
6551  */
6552 //================================================================================
6553
6554 void _Smoother1D::prepare(_SolidData& data)
6555 {
6556   const TopoDS_Edge& E = TopoDS::Edge( _eos._shape );
6557   _curveLen = SMESH_Algo::EdgeLength( E );
6558
6559   // sort _LayerEdge's by position on the EDGE
6560   data.SortOnEdge( E, _eos._edges );
6561
6562   // compute normalized param of _eos._edges on EDGE
6563   _leParams.resize( _eos._edges.size() + 1 );
6564   {
6565     double curLen;
6566     gp_Pnt pPrev = SMESH_TNodeXYZ( getLEdgeOnV( 0 )->_nodes[0] );
6567     _leParams[0] = 0;
6568     for ( size_t i = 0; i < _eos._edges.size(); ++i )
6569     {
6570       gp_Pnt p       = SMESH_TNodeXYZ( _eos._edges[i]->_nodes[0] );
6571       curLen         = p.Distance( pPrev );
6572       _leParams[i+1] = _leParams[i] + curLen;
6573       pPrev          = p;
6574     }
6575     double fullLen = _leParams.back() + pPrev.Distance( SMESH_TNodeXYZ( getLEdgeOnV(1)->_nodes[0]));
6576     for ( size_t i = 0; i < _leParams.size()-1; ++i )
6577       _leParams[i] = _leParams[i+1] / fullLen;
6578     _leParams.back() = 1.;
6579   }
6580
6581   _LayerEdge* leOnV[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
6582
6583   // get cosin to use in findEdgesToSmooth()
6584   _edgeDir[0] = getEdgeDir( E, leOnV[0]->_nodes[0], data.GetHelper() );
6585   _edgeDir[1] = getEdgeDir( E, leOnV[1]->_nodes[0], data.GetHelper() );
6586   _leOnV[0]._cosin = Abs( leOnV[0]->_cosin );
6587   _leOnV[1]._cosin = Abs( leOnV[1]->_cosin );
6588   _leOnV[0]._flags = _leOnV[1]._flags = 0;
6589   if ( _eos._sWOL.IsNull() ) // 3D
6590     for ( int iEnd = 0; iEnd < 2; ++iEnd )
6591       _leOnV[iEnd]._cosin = Abs( _edgeDir[iEnd].Normalized() * leOnV[iEnd]->_normal );
6592
6593   if ( isAnalytic() )
6594     return;
6595
6596   // divide E to have offset segments with low deflection
6597   BRepAdaptor_Curve c3dAdaptor( E );
6598   const double curDeflect = 0.1; //0.01; // Curvature deflection == |p1p2|*sin(p1p2,p1pM)
6599   const double angDeflect = 0.1; //0.09; // Angular deflection == sin(p1pM,pMp2)
6600   GCPnts_TangentialDeflection discret(c3dAdaptor, angDeflect, curDeflect);
6601   if ( discret.NbPoints() <= 2 )
6602   {
6603     _anaCurve = new Geom_Line( gp::OX() ); // only type does matter
6604     return;
6605   }
6606
6607   const double u0 = c3dAdaptor.FirstParameter();
6608   gp_Pnt p; gp_Vec tangent;
6609   if ( discret.NbPoints() >= (int) _eos.size() + 2 )
6610   {
6611     _offPoints.resize( discret.NbPoints() );
6612     for ( size_t i = 0; i < _offPoints.size(); i++ )
6613     {
6614       double u = discret.Parameter( i+1 );
6615       c3dAdaptor.D1( u, p, tangent );
6616       _offPoints[i]._xyz     = p.XYZ();
6617       _offPoints[i]._edgeDir = tangent.XYZ();
6618       _offPoints[i]._param = GCPnts_AbscissaPoint::Length( c3dAdaptor, u0, u ) / _curveLen;
6619     }
6620   }
6621   else
6622   {
6623     std::vector< double > params( _eos.size() + 2 );
6624
6625     params[0]     = data.GetHelper().GetNodeU( E, leOnV[0]->_nodes[0] );
6626     params.back() = data.GetHelper().GetNodeU( E, leOnV[1]->_nodes[0] );
6627     for ( size_t i = 0; i < _eos.size(); i++ )
6628       params[i+1] = data.GetHelper().GetNodeU( E, _eos[i]->_nodes[0] );
6629
6630     if ( params[1] > params[ _eos.size() ] )
6631       std::reverse( params.begin() + 1, params.end() - 1 );
6632
6633     _offPoints.resize( _eos.size() + 2 );
6634     for ( size_t i = 0; i < _offPoints.size(); i++ )
6635     {
6636       const double u = params[i];
6637       c3dAdaptor.D1( u, p, tangent );
6638       _offPoints[i]._xyz     = p.XYZ();
6639       _offPoints[i]._edgeDir = tangent.XYZ();
6640       _offPoints[i]._param = GCPnts_AbscissaPoint::Length( c3dAdaptor, u0, u ) / _curveLen;
6641     }
6642   }
6643
6644   // set _2edges
6645   _offPoints    [0]._2edges.set( &_leOnV[0], &_leOnV[0], 0.5, 0.5 );
6646   _offPoints.back()._2edges.set( &_leOnV[1], &_leOnV[1], 0.5, 0.5 );
6647   _2NearEdges tmp2edges;
6648   tmp2edges._edges[1] = _eos._edges[0];
6649   _leOnV[0]._2neibors = & tmp2edges;
6650   _leOnV[0]._nodes    = leOnV[0]->_nodes;
6651   _leOnV[1]._nodes    = leOnV[1]->_nodes;
6652   _LayerEdge* eNext, *ePrev = & _leOnV[0];
6653   for ( size_t iLE = 0, i = 1; i < _offPoints.size()-1; i++ )
6654   {
6655     // find _LayerEdge's located before and after an offset point
6656     // (_eos._edges[ iLE ] is next after ePrev)
6657     while ( iLE < _eos._edges.size() && _offPoints[i]._param > _leParams[ iLE ] )
6658       ePrev = _eos._edges[ iLE++ ];
6659     eNext = ePrev->_2neibors->_edges[1];
6660
6661     gp_Pnt p0 = SMESH_TNodeXYZ( ePrev->_nodes[0] );
6662     gp_Pnt p1 = SMESH_TNodeXYZ( eNext->_nodes[0] );
6663     double  r = p0.Distance( _offPoints[i]._xyz ) / p0.Distance( p1 );
6664     _offPoints[i]._2edges.set( ePrev, eNext, 1-r, r );
6665   }
6666
6667   // replace _LayerEdge's on VERTEX by _leOnV in _offPoints._2edges
6668   for ( size_t i = 0; i < _offPoints.size(); i++ )
6669     if ( _offPoints[i]._2edges._edges[0] == leOnV[0] )
6670       _offPoints[i]._2edges._edges[0] = & _leOnV[0];
6671     else break;
6672   for ( size_t i = _offPoints.size()-1; i > 0; i-- )
6673     if ( _offPoints[i]._2edges._edges[1] == leOnV[1] )
6674       _offPoints[i]._2edges._edges[1] = & _leOnV[1];
6675     else break;
6676
6677   // set _normal of _leOnV[0] and _leOnV[1] to be normal to the EDGE
6678
6679   int iLBO = _offPoints.size() - 2; // last but one
6680
6681   if ( leOnV[ 0 ]->Is( _LayerEdge::MULTI_NORMAL ))
6682     _leOnV[ 0 ]._normal = getNormalNormal( _eos._edges[1]->_normal, _edgeDir[0] );
6683   else
6684     _leOnV[ 0 ]._normal = getNormalNormal( leOnV[0]->_normal,       _edgeDir[0] );
6685   if ( leOnV[ 1 ]->Is( _LayerEdge::MULTI_NORMAL ))
6686     _leOnV[ 1 ]._normal = getNormalNormal( _eos._edges.back()->_normal, _edgeDir[1] );
6687   else
6688     _leOnV[ 1 ]._normal = getNormalNormal( leOnV[1]->_normal,           _edgeDir[1] );
6689   _leOnV[ 0 ]._len = 0;
6690   _leOnV[ 1 ]._len = 0;
6691   _leOnV[ 0 ]._lenFactor = _offPoints[1   ]._2edges._edges[1]->_lenFactor;
6692   _leOnV[ 1 ]._lenFactor = _offPoints[iLBO]._2edges._edges[0]->_lenFactor;
6693
6694   _iSeg[0] = 0;
6695   _iSeg[1] = _offPoints.size()-2;
6696
6697   // initialize OffPnt::_len
6698   for ( size_t i = 0; i < _offPoints.size(); ++i )
6699     _offPoints[i]._len = 0;
6700
6701   if ( _eos._edges[0]->NbSteps() > 1 ) // already inflated several times, init _xyz
6702   {
6703     _leOnV[0]._len = leOnV[0]->_len;
6704     _leOnV[1]._len = leOnV[1]->_len;
6705     for ( size_t i = 0; i < _offPoints.size(); i++ )
6706     {
6707       _LayerEdge*  e0 = _offPoints[i]._2edges._edges[0];
6708       _LayerEdge*  e1 = _offPoints[i]._2edges._edges[1];
6709       const double w0 = _offPoints[i]._2edges._wgt[0];
6710       const double w1 = _offPoints[i]._2edges._wgt[1];
6711       double  avgLen  = ( e0->_len * w0 + e1->_len * w1 );
6712       gp_XYZ  avgXYZ  = ( SMESH_TNodeXYZ( e0->_nodes.back() ) * w0 +
6713                           SMESH_TNodeXYZ( e1->_nodes.back() ) * w1 );
6714       _offPoints[i]._xyz = avgXYZ;
6715       _offPoints[i]._len = avgLen;
6716     }
6717   }
6718 }
6719
6720 //================================================================================
6721 /*!
6722  * \brief return _normal of _leOnV[is2nd] normal to the EDGE
6723  */
6724 //================================================================================
6725
6726 gp_XYZ _Smoother1D::getNormalNormal( const gp_XYZ & normal,
6727                                      const gp_XYZ&  edgeDir)
6728 {
6729   gp_XYZ cross = normal ^ edgeDir;
6730   gp_XYZ  norm = edgeDir ^ cross;
6731   double  size = norm.Modulus();
6732
6733   // if ( size == 0 ) // MULTI_NORMAL _LayerEdge
6734   //   return gp_XYZ( 1e-100, 1e-100, 1e-100 );
6735
6736   if ( size < 1e-5 ) // normal || edgeDir (almost) at inflation along EDGE (bos #20643)
6737   {
6738     const _LayerEdge* le = _eos._edges[ _eos._edges.size() / 2 ];
6739     const gp_XYZ& leNorm = le->_normal;
6740
6741     cross = leNorm ^ edgeDir;
6742     norm = edgeDir ^ cross;
6743     size = norm.Modulus();
6744   }
6745
6746   return norm / size;
6747 }
6748
6749 //================================================================================
6750 /*!
6751  * \brief Writes a script creating a mesh composed of _offPoints
6752  */
6753 //================================================================================
6754
6755 void _Smoother1D::offPointsToPython() const
6756 {
6757   const char* fname = "/tmp/offPoints.py";
6758   cout << "exec(open('"<<fname<<"','rb').read() )"<<endl;
6759   ofstream py(fname);
6760   py << "import SMESH" << endl
6761      << "from salome.smesh import smeshBuilder" << endl
6762      << "smesh  = smeshBuilder.New(salome.myStudy)" << endl
6763      << "mesh   = smesh.Mesh( 'offPoints' )"<<endl;
6764   for ( size_t i = 0; i < _offPoints.size(); i++ )
6765   {
6766     py << "mesh.AddNode( "
6767        << _offPoints[i]._xyz.X() << ", "
6768        << _offPoints[i]._xyz.Y() << ", "
6769        << _offPoints[i]._xyz.Z() << " )" << endl;
6770   }
6771 }
6772
6773 //================================================================================
6774 /*!
6775  * \brief Sort _LayerEdge's by a parameter on a given EDGE
6776  */
6777 //================================================================================
6778
6779 void _SolidData::SortOnEdge( const TopoDS_Edge&     E,
6780                              vector< _LayerEdge* >& edges)
6781 {
6782   map< double, _LayerEdge* > u2edge;
6783   for ( size_t i = 0; i < edges.size(); ++i )
6784     u2edge.insert( u2edge.end(),
6785                    make_pair( _helper->GetNodeU( E, edges[i]->_nodes[0] ), edges[i] ));
6786
6787   ASSERT( u2edge.size() == edges.size() );
6788   map< double, _LayerEdge* >::iterator u2e = u2edge.begin();
6789   for ( size_t i = 0; i < edges.size(); ++i, ++u2e )
6790     edges[i] = u2e->second;
6791
6792   Sort2NeiborsOnEdge( edges );
6793 }
6794
6795 //================================================================================
6796 /*!
6797  * \brief Set _2neibors according to the order of _LayerEdge on EDGE
6798  */
6799 //================================================================================
6800
6801 void _SolidData::Sort2NeiborsOnEdge( vector< _LayerEdge* >& edges )
6802 {
6803   if ( edges.size() < 2 || !edges[0]->_2neibors ) return;
6804
6805   for ( size_t i = 0; i < edges.size()-1; ++i )
6806     if ( edges[i]->_2neibors->tgtNode(1) != edges[i+1]->_nodes.back() )
6807       edges[i]->_2neibors->reverse();
6808
6809   const size_t iLast = edges.size() - 1;
6810   if ( edges.size() > 1 &&
6811        edges[iLast]->_2neibors->tgtNode(0) != edges[iLast-1]->_nodes.back() )
6812     edges[iLast]->_2neibors->reverse();
6813 }
6814
6815 //================================================================================
6816 /*!
6817  * \brief Return _EdgesOnShape* corresponding to the shape
6818  */
6819 //================================================================================
6820
6821 _EdgesOnShape* _SolidData::GetShapeEdges(const TGeomID shapeID )
6822 {
6823   if ( shapeID < (int)_edgesOnShape.size() &&
6824        _edgesOnShape[ shapeID ]._shapeID == shapeID )
6825     return _edgesOnShape[ shapeID ]._subMesh ? & _edgesOnShape[ shapeID ] : 0;
6826
6827   for ( size_t i = 0; i < _edgesOnShape.size(); ++i )
6828     if ( _edgesOnShape[i]._shapeID == shapeID )
6829       return _edgesOnShape[i]._subMesh ? & _edgesOnShape[i] : 0;
6830
6831   return 0;
6832 }
6833
6834 //================================================================================
6835 /*!
6836  * \brief Return _EdgesOnShape* corresponding to the shape
6837  */
6838 //================================================================================
6839
6840 _EdgesOnShape* _SolidData::GetShapeEdges(const TopoDS_Shape& shape )
6841 {
6842   SMESHDS_Mesh* meshDS = _proxyMesh->GetMesh()->GetMeshDS();
6843   return GetShapeEdges( meshDS->ShapeToIndex( shape ));
6844 }
6845
6846 //================================================================================
6847 /*!
6848  * \brief Prepare data of the _LayerEdge for smoothing on FACE
6849  */
6850 //================================================================================
6851
6852 void _SolidData::PrepareEdgesToSmoothOnFace( _EdgesOnShape* eos, bool substituteSrcNodes )
6853 {
6854   SMESH_MesherHelper helper( *_proxyMesh->GetMesh() );
6855
6856   set< TGeomID > vertices;
6857   TopoDS_Face F;
6858   if ( eos->ShapeType() == TopAbs_FACE )
6859   {
6860     // check FACE concavity and get concave VERTEXes
6861     F = TopoDS::Face( eos->_shape );
6862     if ( isConcave( F, helper, &vertices ))
6863       _concaveFaces.insert( eos->_shapeID );
6864
6865     // set eos._eosConcaVer
6866     eos->_eosConcaVer.clear();
6867     eos->_eosConcaVer.reserve( vertices.size() );
6868     for ( set< TGeomID >::iterator v = vertices.begin(); v != vertices.end(); ++v )
6869     {
6870       _EdgesOnShape* eov = GetShapeEdges( *v );
6871       if ( eov && eov->_edges.size() == 1 )
6872       {
6873         eos->_eosConcaVer.push_back( eov );
6874         for ( size_t i = 0; i < eov->_edges[0]->_neibors.size(); ++i )
6875           eov->_edges[0]->_neibors[i]->Set( _LayerEdge::DIFFICULT );
6876       }
6877     }
6878
6879     // SetSmooLen() to _LayerEdge's on FACE
6880     // for ( size_t i = 0; i < eos->_edges.size(); ++i )
6881     // {
6882     //   eos->_edges[i]->SetSmooLen( Precision::Infinite() );
6883     // }
6884     // SMESH_subMeshIteratorPtr smIt = eos->_subMesh->getDependsOnIterator(/*includeSelf=*/false);
6885     // while ( smIt->more() ) // loop on sub-shapes of the FACE
6886     // {
6887     //   _EdgesOnShape* eoe = GetShapeEdges( smIt->next()->GetId() );
6888     //   if ( !eoe ) continue;
6889
6890     //   vector<_LayerEdge*>& eE = eoe->_edges;
6891     //   for ( size_t iE = 0; iE < eE.size(); ++iE ) // loop on _LayerEdge's on EDGE or VERTEX
6892     //   {
6893     //     if ( eE[iE]->_cosin <= theMinSmoothCosin )
6894     //       continue;
6895
6896     //     SMDS_ElemIteratorPtr segIt = eE[iE]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Edge);
6897     //     while ( segIt->more() )
6898     //     {
6899     //       const SMDS_MeshElement* seg = segIt->next();
6900     //       if ( !eos->_subMesh->DependsOn( seg->getshapeId() ))
6901     //         continue;
6902     //       if ( seg->GetNode(0) != eE[iE]->_nodes[0] )
6903     //         continue; // not to check a seg twice
6904     //       for ( size_t iN = 0; iN < eE[iE]->_neibors.size(); ++iN )
6905     //       {
6906     //         _LayerEdge* eN = eE[iE]->_neibors[iN];
6907     //         if ( eN->_nodes[0]->getshapeId() != eos->_shapeID )
6908     //           continue;
6909     //         double dist    = SMESH_MeshAlgos::GetDistance( seg, SMESH_TNodeXYZ( eN->_nodes[0] ));
6910     //         double smooLen = getSmoothingThickness( eE[iE]->_cosin, dist );
6911     //         eN->SetSmooLen( Min( smooLen, eN->GetSmooLen() ));
6912     //         eN->Set( _LayerEdge::NEAR_BOUNDARY );
6913     //       }
6914     //     }
6915     //   }
6916     // }
6917   } // if ( eos->ShapeType() == TopAbs_FACE )
6918
6919   for ( size_t i = 0; i < eos->_edges.size(); ++i )
6920   {
6921     eos->_edges[i]->_smooFunction = 0;
6922     eos->_edges[i]->Set( _LayerEdge::TO_SMOOTH );
6923   }
6924   bool isCurved = false;
6925   for ( size_t i = 0; i < eos->_edges.size(); ++i )
6926   {
6927     _LayerEdge* edge = eos->_edges[i];
6928
6929     // get simplices sorted
6930     _Simplex::SortSimplices( edge->_simplices );
6931
6932     // smoothing function
6933     edge->ChooseSmooFunction( vertices, _n2eMap );
6934
6935     // set _curvature
6936     double avgNormProj = 0, avgLen = 0;
6937     for ( size_t iS = 0; iS < edge->_simplices.size(); ++iS )
6938     {
6939       _Simplex& s = edge->_simplices[iS];
6940
6941       gp_XYZ  vec = edge->_pos.back() - SMESH_TNodeXYZ( s._nPrev );
6942       avgNormProj += edge->_normal * vec;
6943       avgLen      += vec.Modulus();
6944       if ( substituteSrcNodes )
6945       {
6946         s._nNext = _n2eMap[ s._nNext ]->_nodes.back();
6947         s._nPrev = _n2eMap[ s._nPrev ]->_nodes.back();
6948       }
6949     }
6950     avgNormProj /= edge->_simplices.size();
6951     avgLen      /= edge->_simplices.size();
6952     if (( edge->_curvature = _Curvature::New( avgNormProj, avgLen )))
6953     {
6954       edge->Set( _LayerEdge::SMOOTHED_C1 );
6955       isCurved = true;
6956       SMDS_FacePositionPtr fPos = edge->_nodes[0]->GetPosition();
6957       if ( !fPos )
6958         for ( size_t iS = 0; iS < edge->_simplices.size()  &&  !fPos; ++iS )
6959           fPos = edge->_simplices[iS]._nPrev->GetPosition();
6960       if ( fPos )
6961         edge->_curvature->_uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
6962     }
6963   }
6964
6965   // prepare for putOnOffsetSurface()
6966   if (( eos->ShapeType() == TopAbs_FACE ) &&
6967       ( isCurved || !eos->_eosConcaVer.empty() ))
6968   {
6969     eos->_offsetSurf = helper.GetSurface( TopoDS::Face( eos->_shape ));
6970     eos->_edgeForOffset = 0;
6971
6972     double maxCosin = -1;
6973     //bool hasNoShrink = false;
6974     for ( TopExp_Explorer eExp( eos->_shape, TopAbs_EDGE ); eExp.More(); eExp.Next() )
6975     {
6976       _EdgesOnShape* eoe = GetShapeEdges( eExp.Current() );
6977       if ( !eoe || eoe->_edges.empty() ) continue;
6978
6979       // if ( eos->GetData()._noShrinkShapes.count( eoe->_shapeID ))
6980       //   hasNoShrink = true;
6981
6982       vector<_LayerEdge*>& eE = eoe->_edges;
6983       _LayerEdge* e = eE[ eE.size() / 2 ];
6984       if ( !e->Is( _LayerEdge::RISKY_SWOL ) && e->_cosin > maxCosin )
6985       {
6986         eos->_edgeForOffset = e;
6987         maxCosin = e->_cosin;
6988       }
6989
6990       if ( !eoe->_sWOL.IsNull() )
6991         for ( _LayerEdge* le : eoe->_edges )
6992           if ( le->Is( _LayerEdge::RISKY_SWOL ) && e->_cosin > 0 )
6993           {
6994             // make _neibors on FACE be smoothed after le->Is( BLOCKED )
6995             for ( _LayerEdge* neibor : le->_neibors )
6996             {
6997               int shapeDim =  neibor->BaseShapeDim();
6998               if ( shapeDim == 2 )
6999                 neibor->Set( _LayerEdge::NEAR_BOUNDARY ); // on FACE
7000               else if ( shapeDim == 0 )
7001                 neibor->Set( _LayerEdge::RISKY_SWOL );    // on VERTEX
7002
7003               if ( !neibor->_curvature )
7004               {
7005                 gp_XY uv = helper.GetNodeUV( F, neibor->_nodes[0] );
7006                 neibor->_curvature = _Factory::NewCurvature();
7007                 neibor->_curvature->_r = 0;
7008                 neibor->_curvature->_k = 0;
7009                 neibor->_curvature->_h2lenRatio = 0;
7010                 neibor->_curvature->_uv = uv;
7011               }
7012             }
7013           }
7014     } // loop on EDGEs
7015
7016     // Try to initialize _Mapper2D
7017
7018     // if ( hasNoShrink )
7019     //   return;
7020
7021     SMDS_ElemIteratorPtr fIt = eos->_subMesh->GetSubMeshDS()->GetElements();
7022     if ( !fIt->more() || fIt->next()->NbCornerNodes() != 4 )
7023       return;
7024
7025     // get EDGEs of quadrangle bottom
7026     std::list< TopoDS_Edge > edges;
7027     std::list< int > nbEdgesInWire;
7028     int nbWire = SMESH_Block::GetOrderedEdges( F, edges, nbEdgesInWire );
7029     if ( nbWire != 1 || nbEdgesInWire.front() < 4 )
7030       return;
7031     const SMDS_MeshNode* node;
7032     while ( true ) // make edges start at a corner VERTEX
7033     {
7034       node = SMESH_Algo::VertexNode( helper.IthVertex( 0, edges.front() ), helper.GetMeshDS() );
7035       if ( node && helper.IsCornerOfStructure( node, eos->_subMesh->GetSubMeshDS(), helper ))
7036         break;
7037       edges.pop_front();
7038       if ( edges.empty() )
7039         return;
7040     }
7041     std::list< TopoDS_Edge >::iterator edgeIt = edges.begin();
7042     while ( true ) // make edges finish at a corner VERTEX
7043     {
7044       node = SMESH_Algo::VertexNode( helper.IthVertex( 1, *edgeIt ), helper.GetMeshDS() );
7045       ++edgeIt;
7046       if ( node && helper.IsCornerOfStructure( node, eos->_subMesh->GetSubMeshDS(), helper ))
7047       {
7048         edges.erase( edgeIt, edges.end() );
7049         break;
7050       }
7051       if ( edgeIt == edges.end() )
7052         return;
7053     }
7054
7055     // get structure of nodes
7056     TParam2ColumnMap param2ColumnMap;
7057     if ( !helper.LoadNodeColumns( param2ColumnMap, F, edges, helper.GetMeshDS() ))
7058       return;
7059
7060     eos->_mapper2D = new _Mapper2D( param2ColumnMap, eos->GetData()._n2eMap );
7061
7062   } // if eos is of curved FACE
7063
7064   return;
7065 }
7066
7067 //================================================================================
7068 /*!
7069  * \brief Add faces for smoothing
7070  */
7071 //================================================================================
7072
7073 void _SolidData::AddShapesToSmooth( const set< _EdgesOnShape* >& eosToSmooth,
7074                                     const set< _EdgesOnShape* >* edgesNoAnaSmooth )
7075 {
7076   set< _EdgesOnShape * >::const_iterator eos = eosToSmooth.begin();
7077   for ( ; eos != eosToSmooth.end(); ++eos )
7078   {
7079     if ( !*eos || (*eos)->_toSmooth ) continue;
7080
7081     (*eos)->_toSmooth = true;
7082
7083     if ( (*eos)->ShapeType() == TopAbs_FACE )
7084     {
7085       PrepareEdgesToSmoothOnFace( *eos, /*substituteSrcNodes=*/false );
7086       (*eos)->_toSmooth = true;
7087     }
7088   }
7089
7090   // avoid _Smoother1D::smoothAnalyticEdge() of edgesNoAnaSmooth
7091   if ( edgesNoAnaSmooth )
7092     for ( eos = edgesNoAnaSmooth->begin(); eos != edgesNoAnaSmooth->end(); ++eos )
7093     {
7094       if ( (*eos)->_edgeSmoother )
7095         (*eos)->_edgeSmoother->_anaCurve.Nullify();
7096     }
7097 }
7098
7099 //================================================================================
7100 /*!
7101  * \brief Limit _LayerEdge::_maxLen according to local curvature
7102  */
7103 //================================================================================
7104
7105 void _ViscousBuilder::limitMaxLenByCurvature( _SolidData& data, SMESH_MesherHelper& /*helper*/ )
7106 {
7107   // find intersection of neighbor _LayerEdge's to limit _maxLen
7108   // according to local curvature (IPAL52648)
7109
7110   // This method must be called after findCollisionEdges() where _LayerEdge's
7111   // get _lenFactor initialized in the case of eos._hyp.IsOffsetMethod()
7112
7113   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7114   {
7115     _EdgesOnShape& eosI = data._edgesOnShape[iS];
7116     if ( eosI._edges.empty() ) continue;
7117     if ( !eosI._hyp.ToSmooth() )
7118     {
7119       for ( size_t i = 0; i < eosI._edges.size(); ++i )
7120       {
7121         _LayerEdge* eI = eosI._edges[i];
7122         for ( size_t iN = 0; iN < eI->_neibors.size(); ++iN )
7123         {
7124           _LayerEdge* eN = eI->_neibors[iN];
7125           if ( eI->_nodes[0]->GetID() < eN->_nodes[0]->GetID() ) // treat this pair once
7126           {
7127             _EdgesOnShape* eosN = data.GetShapeEdges( eN );
7128             limitMaxLenByCurvature( eI, eN, eosI, *eosN, eosI._hyp.ToSmooth() );
7129           }
7130         }
7131       }
7132     }
7133     else if ( eosI.ShapeType() == TopAbs_EDGE )
7134     {
7135       const TopoDS_Edge& E = TopoDS::Edge( eosI._shape );
7136       if ( SMESH_Algo::IsStraight( E, /*degenResult=*/true )) continue;
7137
7138       _LayerEdge* e0 = eosI._edges[0];
7139       for ( size_t i = 1; i < eosI._edges.size(); ++i )
7140       {
7141         _LayerEdge* eI = eosI._edges[i];
7142         limitMaxLenByCurvature( eI, e0, eosI, eosI, eosI._hyp.ToSmooth() );
7143         e0 = eI;
7144       }
7145     }
7146   }
7147 }
7148
7149 //================================================================================
7150 /*!
7151  * \brief Limit _LayerEdge::_maxLen according to local curvature
7152  */
7153 //================================================================================
7154
7155 void _ViscousBuilder::limitMaxLenByCurvature( _LayerEdge*    e1,
7156                                               _LayerEdge*    e2,
7157                                               _EdgesOnShape& /*eos1*/,
7158                                               _EdgesOnShape& /*eos2*/,
7159                                               const bool     /*isSmoothable*/ )
7160 {
7161   if (( e1->_nodes[0]->GetPosition()->GetDim() !=
7162         e2->_nodes[0]->GetPosition()->GetDim() ) &&
7163       ( e1->_cosin < 0.75 ))
7164     return; // angle > 90 deg at e1
7165
7166   gp_XYZ plnNorm = e1->_normal ^ e2->_normal;
7167   double norSize = plnNorm.SquareModulus();
7168   if ( norSize < std::numeric_limits<double>::min() )
7169     return; // parallel normals
7170
7171   // find closest points of skew _LayerEdge's
7172   SMESH_TNodeXYZ src1( e1->_nodes[0] ), src2( e2->_nodes[0] );
7173   gp_XYZ dir12 = src2 - src1;
7174   gp_XYZ perp1 = e1->_normal ^ plnNorm;
7175   gp_XYZ perp2 = e2->_normal ^ plnNorm;
7176   double  dot1 = perp2 * e1->_normal;
7177   double  dot2 = perp1 * e2->_normal;
7178   double    u1 =   ( perp2 * dir12 ) / dot1;
7179   double    u2 = - ( perp1 * dir12 ) / dot2;
7180   if ( u1 > 0 && u2 > 0 )
7181   {
7182     double ovl = ( u1 * e1->_normal * dir12 -
7183                    u2 * e2->_normal * dir12 ) / dir12.SquareModulus();
7184     if ( ovl > theSmoothThickToElemSizeRatio )
7185     {
7186       const double coef = 0.75;
7187       e1->SetMaxLen( Min( e1->_maxLen, coef * u1 / e1->_lenFactor ));
7188       e2->SetMaxLen( Min( e2->_maxLen, coef * u2 / e2->_lenFactor ));
7189     }
7190   }
7191 }
7192
7193 //================================================================================
7194 /*!
7195  * \brief Fill data._collisionEdges
7196  */
7197 //================================================================================
7198
7199 void _ViscousBuilder::findCollisionEdges( _SolidData& data, SMESH_MesherHelper& helper )
7200 {
7201   data._collisionEdges.clear();
7202
7203   // set the full thickness of the layers to LEs
7204   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7205   {
7206     _EdgesOnShape& eos = data._edgesOnShape[iS];
7207     if ( eos._edges.empty() ) continue;
7208     if ( eos.ShapeType() != TopAbs_EDGE && eos.ShapeType() != TopAbs_VERTEX ) continue;
7209     if ( !eos._sWOL.IsNull() ) continue; // PAL23566
7210
7211     for ( size_t i = 0; i < eos._edges.size(); ++i )
7212     {
7213       if ( eos._edges[i]->Is( _LayerEdge::BLOCKED )) continue;
7214       double maxLen = eos._edges[i]->_maxLen;
7215       eos._edges[i]->_maxLen = Precision::Infinite(); // avoid blocking
7216       eos._edges[i]->SetNewLength( 1.5 * maxLen, eos, helper );
7217       eos._edges[i]->_maxLen = maxLen;
7218     }
7219   }
7220
7221   // make temporary quadrangles got by extrusion of
7222   // mesh edges along _LayerEdge._normal's
7223
7224   vector< const SMDS_MeshElement* > tmpFaces;
7225
7226   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7227   {
7228     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
7229     if ( eos.ShapeType() != TopAbs_EDGE )
7230       continue;
7231     if ( eos._edges.empty() )
7232     {
7233       _LayerEdge* edge[2] = { 0, 0 }; // LE of 2 VERTEX'es
7234       SMESH_subMeshIteratorPtr smIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false);
7235       while ( smIt->more() )
7236         if ( _EdgesOnShape* eov = data.GetShapeEdges( smIt->next()->GetId() ))
7237           if ( eov->_edges.size() == 1 )
7238             edge[ bool( edge[0]) ] = eov->_edges[0];
7239
7240       if ( edge[1] )
7241       {
7242         _TmpMeshFaceOnEdge* f = new _TmpMeshFaceOnEdge( edge[0], edge[1], --_tmpFaceID );
7243         tmpFaces.push_back( f );
7244       }
7245     }
7246     for ( size_t i = 0; i < eos._edges.size(); ++i )
7247     {
7248       _LayerEdge* edge = eos._edges[i];
7249       for ( int j = 0; j < 2; ++j ) // loop on _2NearEdges
7250       {
7251         const SMDS_MeshNode* src2 = edge->_2neibors->srcNode(j);
7252         if ( src2->GetPosition()->GetDim() > 0 &&
7253              src2->GetID() < edge->_nodes[0]->GetID() )
7254           continue; // avoid using same segment twice
7255
7256         // a _LayerEdge containing tgt2
7257         _LayerEdge* neiborEdge = edge->_2neibors->_edges[j];
7258
7259         _TmpMeshFaceOnEdge* f = new _TmpMeshFaceOnEdge( edge, neiborEdge, --_tmpFaceID );
7260         tmpFaces.push_back( f );
7261       }
7262     }
7263   }
7264
7265   // Find _LayerEdge's intersecting tmpFaces.
7266
7267   SMDS_ElemIteratorPtr fIt( new SMDS_ElementVectorIterator( tmpFaces.begin(),
7268                                                             tmpFaces.end()));
7269   SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
7270     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(), fIt ));
7271
7272   double dist1, dist2, segLen, eps = 0.5;
7273   _CollisionEdges collEdges;
7274   vector< const SMDS_MeshElement* > suspectFaces;
7275   const double angle45 = Cos( 45. * M_PI / 180. );
7276
7277   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7278   {
7279     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
7280     if ( eos.ShapeType() == TopAbs_FACE || !eos._sWOL.IsNull() )
7281       continue;
7282     // find sub-shapes whose VL can influence VL on eos
7283     set< TGeomID > neighborShapes;
7284     PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
7285     while ( const TopoDS_Shape* face = fIt->next() )
7286     {
7287       TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
7288       if ( _EdgesOnShape* eof = data.GetShapeEdges( faceID ))
7289       {
7290         SMESH_subMeshIteratorPtr subIt = eof->_subMesh->getDependsOnIterator(/*includeSelf=*/false);
7291         while ( subIt->more() )
7292           neighborShapes.insert( subIt->next()->GetId() );
7293       }
7294     }
7295     if ( eos.ShapeType() == TopAbs_VERTEX )
7296     {
7297       PShapeIteratorPtr eIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_EDGE );
7298       while ( const TopoDS_Shape* edge = eIt->next() )
7299         neighborShapes.erase( getMeshDS()->ShapeToIndex( *edge ));
7300     }
7301     // find intersecting _LayerEdge's
7302     for ( size_t i = 0; i < eos._edges.size(); ++i )
7303     {
7304       if ( eos._edges[i]->Is( _LayerEdge::MULTI_NORMAL )) continue;
7305       _LayerEdge*   edge = eos._edges[i];
7306       gp_Ax1 lastSegment = edge->LastSegment( segLen, eos );
7307       segLen *= 1.2;
7308
7309       gp_Vec eSegDir0, eSegDir1;
7310       if ( edge->IsOnEdge() )
7311       {
7312         SMESH_TNodeXYZ eP( edge->_nodes[0] );
7313         eSegDir0 = SMESH_TNodeXYZ( edge->_2neibors->srcNode(0) ) - eP;
7314         eSegDir1 = SMESH_TNodeXYZ( edge->_2neibors->srcNode(1) ) - eP;
7315       }
7316       suspectFaces.clear();
7317       searcher->GetElementsInSphere( SMESH_TNodeXYZ( edge->_nodes.back()), edge->_len * 2,
7318                                      SMDSAbs_Face, suspectFaces );
7319       collEdges._intEdges.clear();
7320       for ( size_t j = 0 ; j < suspectFaces.size(); ++j )
7321       {
7322         const _TmpMeshFaceOnEdge* f = (const _TmpMeshFaceOnEdge*) suspectFaces[j];
7323         if ( f->_le1 == edge || f->_le2 == edge ) continue;
7324         if ( !neighborShapes.count( f->_le1->_nodes[0]->getshapeId() )) continue;
7325         if ( !neighborShapes.count( f->_le2->_nodes[0]->getshapeId() )) continue;
7326         if ( edge->IsOnEdge() ) {
7327           if ( edge->_2neibors->include( f->_le1 ) ||
7328                edge->_2neibors->include( f->_le2 )) continue;
7329         }
7330         else {
7331           if (( f->_le1->IsOnEdge() && f->_le1->_2neibors->include( edge )) ||
7332               ( f->_le2->IsOnEdge() && f->_le2->_2neibors->include( edge )))  continue;
7333         }
7334         dist1 = dist2 = Precision::Infinite();
7335         if ( !edge->SegTriaInter( lastSegment, f->n(0), f->n(1), f->n(2), dist1, eps ))
7336           dist1 = Precision::Infinite();
7337         if ( !edge->SegTriaInter( lastSegment, f->n(3), f->n(2), f->n(0), dist2, eps ))
7338           dist2 = Precision::Infinite();
7339         if (( dist1 > segLen ) && ( dist2 > segLen ))
7340           continue;
7341
7342         if ( edge->IsOnEdge() )
7343         {
7344           // skip perpendicular EDGEs
7345           gp_Vec fSegDir  = SMESH_TNodeXYZ( f->n(0) ) - SMESH_TNodeXYZ( f->n(3) );
7346           bool isParallel = ( isLessAngle( eSegDir0, fSegDir, angle45 ) ||
7347                               isLessAngle( eSegDir1, fSegDir, angle45 ) ||
7348                               isLessAngle( eSegDir0, fSegDir.Reversed(), angle45 ) ||
7349                               isLessAngle( eSegDir1, fSegDir.Reversed(), angle45 ));
7350           if ( !isParallel )
7351             continue;
7352         }
7353
7354         // either limit inflation of edges or remember them for updating _normal
7355         // double dot = edge->_normal * f->GetDir();
7356         // if ( dot > 0.1 )
7357         {
7358           collEdges._intEdges.push_back( f->_le1 );
7359           collEdges._intEdges.push_back( f->_le2 );
7360         }
7361         // else
7362         // {
7363         //   double shortLen = 0.75 * ( Min( dist1, dist2 ) / edge->_lenFactor );
7364         //   edge->SetMaxLen( Min( shortLen, edge->_maxLen ));
7365         // }
7366       }
7367
7368       if ( !collEdges._intEdges.empty() )
7369       {
7370         collEdges._edge = edge;
7371         data._collisionEdges.push_back( collEdges );
7372       }
7373     }
7374   }
7375
7376   for ( size_t i = 0 ; i < tmpFaces.size(); ++i )
7377     delete tmpFaces[i];
7378
7379   // restore the zero thickness
7380   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7381   {
7382     _EdgesOnShape& eos = data._edgesOnShape[iS];
7383     if ( eos._edges.empty() ) continue;
7384     if ( eos.ShapeType() != TopAbs_EDGE && eos.ShapeType() != TopAbs_VERTEX ) continue;
7385
7386     for ( size_t i = 0; i < eos._edges.size(); ++i )
7387     {
7388       eos._edges[i]->InvalidateStep( 1, eos );
7389       eos._edges[i]->_len = 0;
7390     }
7391   }
7392 }
7393
7394 //================================================================================
7395 /*!
7396  * \brief Find _LayerEdge's located on boundary of a convex FACE whose normal
7397  *        will be updated at each inflation step
7398  */
7399 //================================================================================
7400
7401 void _ViscousBuilder::findEdgesToUpdateNormalNearConvexFace( _ConvexFace &       convFace,
7402                                                              _SolidData&         data,
7403                                                              SMESH_MesherHelper& helper )
7404 {
7405   const TGeomID convFaceID = getMeshDS()->ShapeToIndex( convFace._face );
7406   const double       preci = BRep_Tool::Tolerance( convFace._face );
7407   Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( convFace._face );
7408
7409   bool edgesToUpdateFound = false;
7410
7411   map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.begin();
7412   for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7413   {
7414     _EdgesOnShape& eos = * id2eos->second;
7415     if ( !eos._sWOL.IsNull() ) continue;
7416     if ( !eos._hyp.ToSmooth() ) continue;
7417     for ( size_t i = 0; i < eos._edges.size(); ++i )
7418     {
7419       _LayerEdge* ledge = eos._edges[ i ];
7420       if ( ledge->Is( _LayerEdge::UPD_NORMAL_CONV )) continue; // already checked
7421       if ( ledge->Is( _LayerEdge::MULTI_NORMAL )) continue; // not inflatable
7422
7423       gp_XYZ tgtPos = ( SMESH_NodeXYZ( ledge->_nodes[0] ) +
7424                         ledge->_normal * ledge->_lenFactor * ledge->_maxLen );
7425
7426       // the normal must be updated if distance from tgtPos to surface is less than
7427       // target thickness
7428
7429       // find an initial UV for search of a projection of tgtPos to surface
7430       const SMDS_MeshNode* nodeInFace = 0;
7431       SMDS_ElemIteratorPtr fIt = ledge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
7432       while ( fIt->more() && !nodeInFace )
7433       {
7434         const SMDS_MeshElement* f = fIt->next();
7435         if ( convFaceID != f->getshapeId() ) continue;
7436
7437         SMDS_ElemIteratorPtr nIt = f->nodesIterator();
7438         while ( nIt->more() && !nodeInFace )
7439         {
7440           const SMDS_MeshElement* n = nIt->next();
7441           if ( n->getshapeId() == convFaceID )
7442             nodeInFace = static_cast< const SMDS_MeshNode* >( n );
7443         }
7444       }
7445       if ( !nodeInFace )
7446         continue;
7447       gp_XY uv = helper.GetNodeUV( convFace._face, nodeInFace );
7448
7449       // projection
7450       surface->NextValueOfUV( uv, tgtPos, preci );
7451       double  dist = surface->Gap();
7452       if ( dist < 0.95 * ledge->_maxLen )
7453       {
7454         ledge->Set( _LayerEdge::UPD_NORMAL_CONV );
7455         if ( !ledge->_curvature ) ledge->_curvature = _Factory::NewCurvature();
7456         ledge->_curvature->_uv.SetCoord( uv.X(), uv.Y() );
7457         edgesToUpdateFound = true;
7458       }
7459     }
7460   }
7461
7462   if ( !convFace._isTooCurved && edgesToUpdateFound )
7463   {
7464     data._convexFaces.insert( make_pair( convFaceID, convFace )).first->second;
7465   }
7466 }
7467
7468 //================================================================================
7469 /*!
7470  * \brief Modify normals of _LayerEdge's on EDGE's to avoid intersection with
7471  * _LayerEdge's on neighbor EDGE's
7472  */
7473 //================================================================================
7474
7475 bool _ViscousBuilder::updateNormals( _SolidData&         data,
7476                                      SMESH_MesherHelper& helper,
7477                                      int                 stepNb,
7478                                      double              /*stepSize*/)
7479 {
7480   updateNormalsOfC1Vertices( data );
7481
7482   if ( stepNb > 0 && !updateNormalsOfConvexFaces( data, helper, stepNb ))
7483     return false;
7484
7485   // map to store new _normal and _cosin for each intersected edge
7486   map< _LayerEdge*, _LayerEdge, _LayerEdgeCmp >           edge2newEdge;
7487   map< _LayerEdge*, _LayerEdge, _LayerEdgeCmp >::iterator e2neIt;
7488   _LayerEdge zeroEdge;
7489   zeroEdge._normal.SetCoord( 0,0,0 );
7490   zeroEdge._maxLen = Precision::Infinite();
7491   zeroEdge._nodes.resize(1); // to init _TmpMeshFaceOnEdge
7492
7493   set< _EdgesOnShape* > shapesToSmooth, edgesNoAnaSmooth;
7494
7495   double segLen, dist1, dist2, dist;
7496   vector< pair< _LayerEdge*, double > > intEdgesDist;
7497   _TmpMeshFaceOnEdge quad( &zeroEdge, &zeroEdge, 0 );
7498
7499   for ( int iter = 0; iter < 5; ++iter )
7500   {
7501     edge2newEdge.clear();
7502
7503     for ( size_t iE = 0; iE < data._collisionEdges.size(); ++iE )
7504     {
7505       _CollisionEdges& ce = data._collisionEdges[iE];
7506       _LayerEdge*   edge1 = ce._edge;
7507       if ( !edge1 /*|| edge1->Is( _LayerEdge::BLOCKED )*/) continue;
7508       _EdgesOnShape* eos1 = data.GetShapeEdges( edge1 );
7509       if ( !eos1 ) continue;
7510
7511       // detect intersections
7512       gp_Ax1 lastSeg = edge1->LastSegment( segLen, *eos1 );
7513       double testLen = 1.5 * edge1->_maxLen * edge1->_lenFactor;
7514       double     eps = 0.5;
7515       intEdgesDist.clear();
7516       double minIntDist = Precision::Infinite();
7517       for ( size_t i = 0; i < ce._intEdges.size(); i += 2 )
7518       {
7519         if ( edge1->Is( _LayerEdge::BLOCKED ) &&
7520              ce._intEdges[i  ]->Is( _LayerEdge::BLOCKED ) &&
7521              ce._intEdges[i+1]->Is( _LayerEdge::BLOCKED ))
7522           continue;
7523         double dot  = edge1->_normal * quad.GetDir( ce._intEdges[i], ce._intEdges[i+1] );
7524         double fact = ( 1.1 + dot * dot );
7525         SMESH_TNodeXYZ pSrc0( ce.nSrc(i) ), pSrc1( ce.nSrc(i+1) );
7526         SMESH_TNodeXYZ pTgt0( ce.nTgt(i) ), pTgt1( ce.nTgt(i+1) );
7527         gp_XYZ pLast0 = pSrc0 + ( pTgt0 - pSrc0 ) * fact;
7528         gp_XYZ pLast1 = pSrc1 + ( pTgt1 - pSrc1 ) * fact;
7529         dist1 = dist2 = Precision::Infinite();
7530         if ( !edge1->SegTriaInter( lastSeg, pSrc0, pLast0, pSrc1,  dist1, eps ) &&
7531              !edge1->SegTriaInter( lastSeg, pSrc1, pLast1, pLast0, dist2, eps ))
7532           continue;
7533         dist = dist1;
7534         if ( dist > testLen || dist <= 0 )
7535         {
7536           dist = dist2;
7537           if ( dist > testLen || dist <= 0 )
7538             continue;
7539         }
7540         // choose a closest edge
7541         gp_Pnt intP( lastSeg.Location().XYZ() + lastSeg.Direction().XYZ() * ( dist + segLen ));
7542         double d1 = intP.SquareDistance( pSrc0 );
7543         double d2 = intP.SquareDistance( pSrc1 );
7544         int iClose = i + ( d2 < d1 );
7545         _LayerEdge* edge2 = ce._intEdges[iClose];
7546         edge2->Unset( _LayerEdge::MARKED );
7547
7548         // choose a closest edge among neighbors
7549         gp_Pnt srcP( SMESH_TNodeXYZ( edge1->_nodes[0] ));
7550         d1 = srcP.SquareDistance( SMESH_TNodeXYZ( edge2->_nodes[0] ));
7551         for ( size_t j = 0; j < intEdgesDist.size(); ++j )
7552         {
7553           _LayerEdge * edgeJ = intEdgesDist[j].first;
7554           if ( edge2->IsNeiborOnEdge( edgeJ ))
7555           {
7556             d2 = srcP.SquareDistance( SMESH_TNodeXYZ( edgeJ->_nodes[0] ));
7557             ( d1 < d2 ? edgeJ : edge2 )->Set( _LayerEdge::MARKED );
7558           }
7559         }
7560         intEdgesDist.push_back( make_pair( edge2, dist ));
7561         // if ( Abs( d2 - d1 ) / Max( d2, d1 ) < 0.5 )
7562         // {
7563         //   iClose = i + !( d2 < d1 );
7564         //   intEdges.push_back( ce._intEdges[iClose] );
7565         //   ce._intEdges[iClose]->Unset( _LayerEdge::MARKED );
7566         // }
7567         minIntDist = Min( edge1->_len * edge1->_lenFactor - segLen + dist, minIntDist );
7568       }
7569
7570       //ce._edge = 0;
7571
7572       // compute new _normals
7573       for ( size_t i = 0; i < intEdgesDist.size(); ++i )
7574       {
7575         _LayerEdge* edge2   = intEdgesDist[i].first;
7576         double      distWgt = edge1->_len / intEdgesDist[i].second;
7577         // if ( edge1->Is( _LayerEdge::BLOCKED ) &&
7578         //      edge2->Is( _LayerEdge::BLOCKED )) continue;        
7579         if ( edge2->Is( _LayerEdge::MARKED )) continue;
7580         edge2->Set( _LayerEdge::MARKED );
7581
7582         // get a new normal
7583         gp_XYZ dir1 = edge1->_normal, dir2 = edge2->_normal;
7584
7585         double cos1 = Abs( edge1->_cosin ), cos2 = Abs( edge2->_cosin );
7586         double wgt1 = ( cos1 + 0.001 ) / ( cos1 + cos2 + 0.002 );
7587         double wgt2 = ( cos2 + 0.001 ) / ( cos1 + cos2 + 0.002 );
7588         // double cos1 = Abs( edge1->_cosin ),        cos2 = Abs( edge2->_cosin );
7589         // double sgn1 = 0.1 * ( 1 + edge1->_cosin ), sgn2 = 0.1 * ( 1 + edge2->_cosin );
7590         // double wgt1 = ( cos1 + sgn1 ) / ( cos1 + cos2 + sgn1 + sgn2 );
7591         // double wgt2 = ( cos2 + sgn2 ) / ( cos1 + cos2 + sgn1 + sgn2 );
7592         gp_XYZ newNormal = wgt1 * dir1 + wgt2 * dir2;
7593         newNormal.Normalize();
7594
7595         // get new cosin
7596         double newCos;
7597         double sgn1 = edge1->_cosin / cos1, sgn2 = edge2->_cosin / cos2;
7598         if ( cos1 < theMinSmoothCosin )
7599         {
7600           newCos = cos2 * sgn1;
7601         }
7602         else if ( cos2 > theMinSmoothCosin ) // both cos1 and cos2 > theMinSmoothCosin
7603         {
7604           newCos = ( wgt1 * cos1 + wgt2 * cos2 ) * edge1->_cosin / cos1;
7605         }
7606         else
7607         {
7608           newCos = edge1->_cosin;
7609         }
7610
7611         e2neIt = edge2newEdge.insert( make_pair( edge1, zeroEdge )).first;
7612         e2neIt->second._normal += distWgt * newNormal;
7613         e2neIt->second._cosin   = newCos;
7614         e2neIt->second.SetMaxLen( 0.7 * minIntDist / edge1->_lenFactor );
7615         if ( iter > 0 && sgn1 * sgn2 < 0 && edge1->_cosin < 0 )
7616           e2neIt->second._normal += dir2;
7617
7618         e2neIt = edge2newEdge.insert( make_pair( edge2, zeroEdge )).first;
7619         e2neIt->second._normal += distWgt * newNormal;
7620         if ( Precision::IsInfinite( zeroEdge._maxLen ))
7621         {
7622           e2neIt->second._cosin  = edge2->_cosin;
7623           e2neIt->second.SetMaxLen( 1.3 * minIntDist / edge1->_lenFactor );
7624         }
7625         if ( iter > 0 && sgn1 * sgn2 < 0 && edge2->_cosin < 0 )
7626           e2neIt->second._normal += dir1;
7627       }
7628     }
7629
7630     if ( edge2newEdge.empty() )
7631       break; //return true;
7632
7633     dumpFunction(SMESH_Comment("updateNormals")<< data._index << "_" << stepNb << "_it" << iter);
7634
7635     // Update data of edges depending on a new _normal
7636
7637     data.UnmarkEdges();
7638     for ( e2neIt = edge2newEdge.begin(); e2neIt != edge2newEdge.end(); ++e2neIt )
7639     {
7640       _LayerEdge*    edge = e2neIt->first;
7641       _LayerEdge& newEdge = e2neIt->second;
7642       _EdgesOnShape*  eos = data.GetShapeEdges( edge );
7643       if ( edge->Is( _LayerEdge::BLOCKED ) && newEdge._maxLen > edge->_len )
7644         continue;
7645
7646       // Check if a new _normal is OK:
7647       newEdge._normal.Normalize();
7648       if ( !isNewNormalOk( data, *edge, newEdge._normal ))
7649       {
7650         if ( newEdge._maxLen < edge->_len && iter > 0 ) // limit _maxLen
7651         {
7652           edge->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true  );
7653           edge->SetMaxLen( newEdge._maxLen );
7654           edge->SetNewLength( newEdge._maxLen, *eos, helper );
7655         }
7656         continue; // the new _normal is bad
7657       }
7658       // the new _normal is OK
7659
7660       // find shapes that need smoothing due to change of _normal
7661       if ( edge->_cosin   < theMinSmoothCosin &&
7662            newEdge._cosin > theMinSmoothCosin )
7663       {
7664         if ( eos->_sWOL.IsNull() )
7665         {
7666           SMDS_ElemIteratorPtr fIt = edge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
7667           while ( fIt->more() )
7668             shapesToSmooth.insert( data.GetShapeEdges( fIt->next()->getshapeId() ));
7669         }
7670         else // edge inflates along a FACE
7671         {
7672           TopoDS_Shape V = helper.GetSubShapeByNode( edge->_nodes[0], getMeshDS() );
7673           PShapeIteratorPtr eIt = helper.GetAncestors( V, *_mesh, TopAbs_EDGE, &eos->_sWOL );
7674           while ( const TopoDS_Shape* E = eIt->next() )
7675           {
7676             gp_Vec edgeDir = getEdgeDir( TopoDS::Edge( *E ), TopoDS::Vertex( V ),
7677                                          eos->_hyp.Get1stLayerThickness() );
7678             double   angle = edgeDir.Angle( newEdge._normal ); // [0,PI]
7679             if ( angle < M_PI / 2 )
7680               shapesToSmooth.insert( data.GetShapeEdges( *E ));
7681           }
7682         }
7683       }
7684
7685       double len = edge->_len;
7686       edge->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true  );
7687       edge->SetNormal( newEdge._normal );
7688       edge->SetCosin( newEdge._cosin );
7689       edge->SetNewLength( len, *eos, helper );
7690       edge->Set( _LayerEdge::MARKED );
7691       edge->Set( _LayerEdge::NORMAL_UPDATED );
7692       edgesNoAnaSmooth.insert( eos );
7693     }
7694
7695     // Update normals and other dependent data of not intersecting _LayerEdge's
7696     // neighboring the intersecting ones
7697
7698     for ( e2neIt = edge2newEdge.begin(); e2neIt != edge2newEdge.end(); ++e2neIt )
7699     {
7700       _LayerEdge*   edge1 = e2neIt->first;
7701       _EdgesOnShape* eos1 = data.GetShapeEdges( edge1 );
7702       if ( !edge1->Is( _LayerEdge::MARKED ))
7703         continue;
7704
7705       if ( edge1->IsOnEdge() )
7706       {
7707         const SMDS_MeshNode * n1 = edge1->_2neibors->srcNode(0);
7708         const SMDS_MeshNode * n2 = edge1->_2neibors->srcNode(1);
7709         edge1->SetDataByNeighbors( n1, n2, *eos1, helper );
7710       }
7711
7712       if ( !edge1->_2neibors || !eos1->_sWOL.IsNull() )
7713         continue;
7714       for ( int j = 0; j < 2; ++j ) // loop on 2 neighbors
7715       {
7716         _LayerEdge* neighbor = edge1->_2neibors->_edges[j];
7717         if ( neighbor->Is( _LayerEdge::MARKED ) /*edge2newEdge.count ( neighbor )*/)
7718           continue; // j-th neighbor is also intersected
7719         _LayerEdge* prevEdge = edge1;
7720         const int nbSteps = 10;
7721         for ( int step = nbSteps; step; --step ) // step from edge1 in j-th direction
7722         {
7723           if ( neighbor->Is( _LayerEdge::BLOCKED ) ||
7724                neighbor->Is( _LayerEdge::MARKED ))
7725             break;
7726           _EdgesOnShape* eos = data.GetShapeEdges( neighbor );
7727           if ( !eos ) continue;
7728           _LayerEdge* nextEdge = neighbor;
7729           if ( neighbor->_2neibors )
7730           {
7731             int iNext = 0;
7732             nextEdge = neighbor->_2neibors->_edges[iNext];
7733             if ( nextEdge == prevEdge )
7734               nextEdge = neighbor->_2neibors->_edges[ ++iNext ];
7735           }
7736           double r = double(step-1)/nbSteps/(iter+1);
7737           if ( !nextEdge->_2neibors )
7738             r = Min( r, 0.5 );
7739
7740           gp_XYZ newNorm = prevEdge->_normal * r + nextEdge->_normal * (1-r);
7741           newNorm.Normalize();
7742           if ( !isNewNormalOk( data, *neighbor, newNorm ))
7743             break;
7744
7745           double len = neighbor->_len;
7746           neighbor->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true  );
7747           neighbor->SetNormal( newNorm );
7748           neighbor->SetCosin( prevEdge->_cosin * r + nextEdge->_cosin * (1-r) );
7749           if ( neighbor->_2neibors )
7750             neighbor->SetDataByNeighbors( prevEdge->_nodes[0], nextEdge->_nodes[0], *eos, helper );
7751           neighbor->SetNewLength( len, *eos, helper );
7752           neighbor->Set( _LayerEdge::MARKED );
7753           neighbor->Set( _LayerEdge::NORMAL_UPDATED );
7754           edgesNoAnaSmooth.insert( eos );
7755
7756           if ( !neighbor->_2neibors )
7757             break; // neighbor is on VERTEX
7758
7759           // goto the next neighbor
7760           prevEdge = neighbor;
7761           neighbor = nextEdge;
7762         }
7763       }
7764     }
7765     dumpFunctionEnd();
7766   } // iterations
7767
7768   data.AddShapesToSmooth( shapesToSmooth, &edgesNoAnaSmooth );
7769
7770   return true;
7771 }
7772
7773 //================================================================================
7774 /*!
7775  * \brief Check if a new normal is OK
7776  */
7777 //================================================================================
7778
7779 bool _ViscousBuilder::isNewNormalOk( _SolidData&   data,
7780                                      _LayerEdge&   edge,
7781                                      const gp_XYZ& newNormal)
7782 {
7783   // check a min angle between the newNormal and surrounding faces
7784   vector<_Simplex> simplices;
7785   SMESH_TNodeXYZ n0( edge._nodes[0] ), n1, n2;
7786   _Simplex::GetSimplices( n0._node, simplices, data._ignoreFaceIds, &data );
7787   double newMinDot = 1, curMinDot = 1;
7788   for ( size_t i = 0; i < simplices.size(); ++i )
7789   {
7790     n1.Set( simplices[i]._nPrev );
7791     n2.Set( simplices[i]._nNext );
7792     gp_XYZ normFace = ( n1 - n0 ) ^ ( n2 - n0 );
7793     double normLen2 = normFace.SquareModulus();
7794     if ( normLen2 < std::numeric_limits<double>::min() )
7795       continue;
7796     normFace /= Sqrt( normLen2 );
7797     newMinDot = Min( newNormal    * normFace, newMinDot );
7798     curMinDot = Min( edge._normal * normFace, curMinDot );
7799   }
7800   bool ok = true;
7801   if ( newMinDot < 0.5 )
7802   {
7803     ok = ( newMinDot >= curMinDot * 0.9 );
7804     //return ( newMinDot >= ( curMinDot * ( 0.8 + 0.1 * edge.NbSteps() )));
7805     // double initMinDot2 = 1. - edge._cosin * edge._cosin;
7806     // return ( newMinDot * newMinDot ) >= ( 0.8 * initMinDot2 );
7807   }
7808
7809   return ok;
7810 }
7811
7812 //================================================================================
7813 /*!
7814  * \brief Modify normals of _LayerEdge's on FACE to reflex smoothing
7815  */
7816 //================================================================================
7817
7818 bool _ViscousBuilder::updateNormalsOfSmoothed( _SolidData&         data,
7819                                                SMESH_MesherHelper& /*helper*/,
7820                                                const int           nbSteps,
7821                                                const double        stepSize )
7822 {
7823   if ( data._nbShapesToSmooth == 0 || nbSteps == 0 )
7824     return true; // no shapes needing smoothing
7825
7826   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7827   {
7828     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
7829     if ( //!eos._toSmooth ||  _eosC1 have _toSmooth == false
7830          !eos._hyp.ToSmooth() ||
7831          eos.ShapeType() != TopAbs_FACE ||
7832          eos._edges.empty() )
7833       continue;
7834
7835     bool toSmooth = ( eos._edges[ 0 ]->NbSteps() >= nbSteps+1 );
7836     if ( !toSmooth ) continue;
7837
7838     for ( size_t i = 0; i < eos._edges.size(); ++i )
7839     {
7840       _LayerEdge* edge = eos._edges[i];
7841       if ( !edge->Is( _LayerEdge::SMOOTHED ))
7842         continue;
7843       if ( edge->Is( _LayerEdge::DIFFICULT ) && nbSteps != 1 )
7844         continue;
7845
7846       const gp_XYZ& pPrev = edge->PrevPos();
7847       const gp_XYZ& pLast = edge->_pos.back();
7848       gp_XYZ      stepVec = pLast - pPrev;
7849       double realStepSize = stepVec.Modulus();
7850       if ( realStepSize < numeric_limits<double>::min() )
7851         continue;
7852
7853       edge->_lenFactor = realStepSize / stepSize;
7854       edge->_normal    = stepVec / realStepSize;
7855       edge->Set( _LayerEdge::NORMAL_UPDATED );
7856     }
7857   }
7858
7859   return true;
7860 }
7861
7862 //================================================================================
7863 /*!
7864  * \brief Modify normals of _LayerEdge's on C1 VERTEXes
7865  */
7866 //================================================================================
7867
7868 void _ViscousBuilder::updateNormalsOfC1Vertices( _SolidData& data )
7869 {
7870   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7871   {
7872     _EdgesOnShape& eov = data._edgesOnShape[ iS ];
7873     if ( eov._eosC1.empty() ||
7874          eov.ShapeType() != TopAbs_VERTEX ||
7875          eov._edges.empty() )
7876       continue;
7877
7878     gp_XYZ newNorm   = eov._edges[0]->_normal;
7879     double curThick  = eov._edges[0]->_len * eov._edges[0]->_lenFactor;
7880     bool normChanged = false;
7881
7882     for ( size_t i = 0; i < eov._eosC1.size(); ++i )
7883     {
7884       _EdgesOnShape*   eoe = eov._eosC1[i];
7885       const TopoDS_Edge& e = TopoDS::Edge( eoe->_shape );
7886       const double    eLen = SMESH_Algo::EdgeLength( e );
7887       TopoDS_Shape    oppV = SMESH_MesherHelper::IthVertex( 0, e );
7888       if ( oppV.IsSame( eov._shape ))
7889         oppV = SMESH_MesherHelper::IthVertex( 1, e );
7890       _EdgesOnShape* eovOpp = data.GetShapeEdges( oppV );
7891       if ( !eovOpp || eovOpp->_edges.empty() ) continue;
7892       if ( eov._edges[0]->Is( _LayerEdge::BLOCKED )) continue;
7893
7894       double curThickOpp = eovOpp->_edges[0]->_len * eovOpp->_edges[0]->_lenFactor;
7895       if ( curThickOpp + curThick < eLen )
7896         continue;
7897
7898       double wgt = 2. * curThick / eLen;
7899       newNorm += wgt * eovOpp->_edges[0]->_normal;
7900       normChanged = true;
7901     }
7902     if ( normChanged )
7903     {
7904       eov._edges[0]->SetNormal( newNorm.Normalized() );
7905       eov._edges[0]->Set( _LayerEdge::NORMAL_UPDATED );
7906     }
7907   }
7908 }
7909
7910 //================================================================================
7911 /*!
7912  * \brief Modify normals of _LayerEdge's on _ConvexFace's
7913  */
7914 //================================================================================
7915
7916 bool _ViscousBuilder::updateNormalsOfConvexFaces( _SolidData&         data,
7917                                                   SMESH_MesherHelper& helper,
7918                                                   int                 stepNb )
7919 {
7920   SMESHDS_Mesh* meshDS = helper.GetMeshDS();
7921   bool isOK;
7922
7923   map< TGeomID, _ConvexFace >::iterator id2face = data._convexFaces.begin();
7924   for ( ; id2face != data._convexFaces.end(); ++id2face )
7925   {
7926     _ConvexFace & convFace = (*id2face).second;
7927     convFace._normalsFixedOnBorders = false; // to update at each inflation step
7928
7929     if ( convFace._normalsFixed )
7930       continue; // already fixed
7931     if ( convFace.CheckPrisms() )
7932       continue; // nothing to fix
7933
7934     convFace._normalsFixed = true;
7935
7936     BRepAdaptor_Surface surface ( convFace._face, false );
7937     BRepLProp_SLProps   surfProp( surface, 2, 1e-6 );
7938
7939     // check if the convex FACE is of spherical shape
7940
7941     Bnd_B3d centersBox; // bbox of centers of curvature of _LayerEdge's on VERTEXes
7942     Bnd_B3d nodesBox;
7943     gp_Pnt  center;
7944
7945     map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.begin();
7946     for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7947     {
7948       _EdgesOnShape& eos = *(id2eos->second);
7949       if ( eos.ShapeType() == TopAbs_VERTEX )
7950       {
7951         _LayerEdge* ledge = eos._edges[ 0 ];
7952         if ( convFace.GetCenterOfCurvature( ledge, surfProp, helper, center ))
7953           centersBox.Add( center );
7954       }
7955       for ( size_t i = 0; i < eos._edges.size(); ++i )
7956         nodesBox.Add( SMESH_TNodeXYZ( eos._edges[ i ]->_nodes[0] ));
7957     }
7958     if ( centersBox.IsVoid() )
7959     {
7960       debugMsg( "Error: centersBox.IsVoid()" );
7961       return false;
7962     }
7963     const bool isSpherical =
7964       ( centersBox.SquareExtent() < 1e-6 * nodesBox.SquareExtent() );
7965
7966     int nbEdges = helper.Count( convFace._face, TopAbs_EDGE, /*ignoreSame=*/false );
7967     vector < _CentralCurveOnEdge > centerCurves( nbEdges );
7968
7969     if ( isSpherical )
7970     {
7971       // set _LayerEdge::_normal as average of all normals
7972
7973       // WARNING: different density of nodes on EDGEs is not taken into account that
7974       // can lead to an improper new normal
7975
7976       gp_XYZ avgNormal( 0,0,0 );
7977       nbEdges = 0;
7978       id2eos = convFace._subIdToEOS.begin();
7979       for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7980       {
7981         _EdgesOnShape& eos = *(id2eos->second);
7982         // set data of _CentralCurveOnEdge
7983         if ( eos.ShapeType() == TopAbs_EDGE )
7984         {
7985           _CentralCurveOnEdge& ceCurve = centerCurves[ nbEdges++ ];
7986           ceCurve.SetShapes( TopoDS::Edge( eos._shape ), convFace, data, helper );
7987           if ( !eos._sWOL.IsNull() )
7988             ceCurve._adjFace.Nullify();
7989           else
7990             ceCurve._ledges.insert( ceCurve._ledges.end(),
7991                                     eos._edges.begin(), eos._edges.end());
7992         }
7993         // summarize normals
7994         for ( size_t i = 0; i < eos._edges.size(); ++i )
7995           avgNormal += eos._edges[ i ]->_normal;
7996       }
7997       double normSize = avgNormal.SquareModulus();
7998       if ( normSize < 1e-200 )
7999       {
8000         debugMsg( "updateNormalsOfConvexFaces(): zero avgNormal" );
8001         return false;
8002       }
8003       avgNormal /= Sqrt( normSize );
8004
8005       // compute new _LayerEdge::_cosin on EDGEs
8006       double avgCosin = 0;
8007       int     nbCosin = 0;
8008       gp_Vec inFaceDir;
8009       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
8010       {
8011         _CentralCurveOnEdge& ceCurve = centerCurves[ iE ];
8012         if ( ceCurve._adjFace.IsNull() )
8013           continue;
8014         for ( size_t iLE = 0; iLE < ceCurve._ledges.size(); ++iLE )
8015         {
8016           const SMDS_MeshNode* node = ceCurve._ledges[ iLE ]->_nodes[0];
8017           inFaceDir = getFaceDir( ceCurve._adjFace, ceCurve._edge, node, helper, isOK );
8018           if ( isOK )
8019           {
8020             double angle = inFaceDir.Angle( avgNormal ); // [0,PI]
8021             ceCurve._ledges[ iLE ]->_cosin = Cos( angle );
8022             avgCosin += ceCurve._ledges[ iLE ]->_cosin;
8023             nbCosin++;
8024           }
8025         }
8026       }
8027       if ( nbCosin > 0 )
8028         avgCosin /= nbCosin;
8029
8030       // set _LayerEdge::_normal = avgNormal
8031       id2eos = convFace._subIdToEOS.begin();
8032       for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
8033       {
8034         _EdgesOnShape& eos = *(id2eos->second);
8035         if ( eos.ShapeType() != TopAbs_EDGE )
8036           for ( size_t i = 0; i < eos._edges.size(); ++i )
8037             eos._edges[ i ]->_cosin = avgCosin;
8038
8039         for ( size_t i = 0; i < eos._edges.size(); ++i )
8040         {
8041           eos._edges[ i ]->SetNormal( avgNormal );
8042           eos._edges[ i ]->Set( _LayerEdge::NORMAL_UPDATED );
8043         }
8044       }
8045     }
8046     else // if ( isSpherical )
8047     {
8048       // We suppose that centers of curvature at all points of the FACE
8049       // lie on some curve, let's call it "central curve". For all _LayerEdge's
8050       // having a common center of curvature we define the same new normal
8051       // as a sum of normals of _LayerEdge's on EDGEs among them.
8052
8053       // get all centers of curvature for each EDGE
8054
8055       helper.SetSubShape( convFace._face );
8056       _LayerEdge* vertexLEdges[2], **edgeLEdge, **edgeLEdgeEnd;
8057
8058       TopExp_Explorer edgeExp( convFace._face, TopAbs_EDGE );
8059       for ( int iE = 0; edgeExp.More(); edgeExp.Next(), ++iE )
8060       {
8061         const TopoDS_Edge& edge = TopoDS::Edge( edgeExp.Current() );
8062
8063         // set adjacent FACE
8064         centerCurves[ iE ].SetShapes( edge, convFace, data, helper );
8065
8066         // get _LayerEdge's of the EDGE
8067         TGeomID edgeID = meshDS->ShapeToIndex( edge );
8068         _EdgesOnShape* eos = data.GetShapeEdges( edgeID );
8069         if ( !eos || eos->_edges.empty() )
8070         {
8071           // no _LayerEdge's on EDGE, use _LayerEdge's on VERTEXes
8072           for ( int iV = 0; iV < 2; ++iV )
8073           {
8074             TopoDS_Vertex v = helper.IthVertex( iV, edge );
8075             TGeomID     vID = meshDS->ShapeToIndex( v );
8076             eos = data.GetShapeEdges( vID );
8077             vertexLEdges[ iV ] = eos->_edges[ 0 ];
8078           }
8079           edgeLEdge    = &vertexLEdges[0];
8080           edgeLEdgeEnd = edgeLEdge + 2;
8081
8082           centerCurves[ iE ]._adjFace.Nullify();
8083         }
8084         else
8085         {
8086           if ( ! eos->_toSmooth )
8087             data.SortOnEdge( edge, eos->_edges );
8088           edgeLEdge    = &eos->_edges[ 0 ];
8089           edgeLEdgeEnd = edgeLEdge + eos->_edges.size();
8090           vertexLEdges[0] = eos->_edges.front()->_2neibors->_edges[0];
8091           vertexLEdges[1] = eos->_edges.back() ->_2neibors->_edges[1];
8092
8093           if ( ! eos->_sWOL.IsNull() )
8094             centerCurves[ iE ]._adjFace.Nullify();
8095         }
8096
8097         // Get curvature centers
8098
8099         centersBox.Clear();
8100
8101         if ( edgeLEdge[0]->IsOnEdge() &&
8102              convFace.GetCenterOfCurvature( vertexLEdges[0], surfProp, helper, center ))
8103         { // 1st VERTEX
8104           centerCurves[ iE ].Append( center, vertexLEdges[0] );
8105           centersBox.Add( center );
8106         }
8107         for ( ; edgeLEdge < edgeLEdgeEnd; ++edgeLEdge )
8108           if ( convFace.GetCenterOfCurvature( *edgeLEdge, surfProp, helper, center ))
8109           { // EDGE or VERTEXes
8110             centerCurves[ iE ].Append( center, *edgeLEdge );
8111             centersBox.Add( center );
8112           }
8113         if ( edgeLEdge[-1]->IsOnEdge() &&
8114              convFace.GetCenterOfCurvature( vertexLEdges[1], surfProp, helper, center ))
8115         { // 2nd VERTEX
8116           centerCurves[ iE ].Append( center, vertexLEdges[1] );
8117           centersBox.Add( center );
8118         }
8119         centerCurves[ iE ]._isDegenerated =
8120           ( centersBox.IsVoid() || centersBox.SquareExtent() < 1e-6 * nodesBox.SquareExtent() );
8121
8122       } // loop on EDGES of convFace._face to set up data of centerCurves
8123
8124       // Compute new normals for _LayerEdge's on EDGEs
8125
8126       double avgCosin = 0;
8127       int     nbCosin = 0;
8128       gp_Vec inFaceDir;
8129       for ( size_t iE1 = 0; iE1 < centerCurves.size(); ++iE1 )
8130       {
8131         _CentralCurveOnEdge& ceCurve = centerCurves[ iE1 ];
8132         if ( ceCurve._isDegenerated )
8133           continue;
8134         const vector< gp_Pnt >& centers = ceCurve._curvaCenters;
8135         vector< gp_XYZ > &   newNormals = ceCurve._normals;
8136         for ( size_t iC1 = 0; iC1 < centers.size(); ++iC1 )
8137         {
8138           isOK = false;
8139           for ( size_t iE2 = 0; iE2 < centerCurves.size() && !isOK; ++iE2 )
8140           {
8141             if ( iE1 != iE2 )
8142               isOK = centerCurves[ iE2 ].FindNewNormal( centers[ iC1 ], newNormals[ iC1 ]);
8143           }
8144           if ( isOK && !ceCurve._adjFace.IsNull() )
8145           {
8146             // compute new _LayerEdge::_cosin
8147             const SMDS_MeshNode* node = ceCurve._ledges[ iC1 ]->_nodes[0];
8148             inFaceDir = getFaceDir( ceCurve._adjFace, ceCurve._edge, node, helper, isOK );
8149             if ( isOK )
8150             {
8151               double angle = inFaceDir.Angle( newNormals[ iC1 ] ); // [0,PI]
8152               ceCurve._ledges[ iC1 ]->_cosin = Cos( angle );
8153               avgCosin += ceCurve._ledges[ iC1 ]->_cosin;
8154               nbCosin++;
8155             }
8156           }
8157         }
8158       }
8159       // set new normals to _LayerEdge's of NOT degenerated central curves
8160       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
8161       {
8162         if ( centerCurves[ iE ]._isDegenerated )
8163           continue;
8164         for ( size_t iLE = 0; iLE < centerCurves[ iE ]._ledges.size(); ++iLE )
8165         {
8166           centerCurves[ iE ]._ledges[ iLE ]->SetNormal( centerCurves[ iE ]._normals[ iLE ]);
8167           centerCurves[ iE ]._ledges[ iLE ]->Set( _LayerEdge::NORMAL_UPDATED );
8168         }
8169       }
8170       // set new normals to _LayerEdge's of     degenerated central curves
8171       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
8172       {
8173         if ( !centerCurves[ iE ]._isDegenerated ||
8174              centerCurves[ iE ]._ledges.size() < 3 )
8175           continue;
8176         // new normal is an average of new normals at VERTEXes that
8177         // was computed on non-degenerated _CentralCurveOnEdge's
8178         gp_XYZ newNorm = ( centerCurves[ iE ]._ledges.front()->_normal +
8179                            centerCurves[ iE ]._ledges.back ()->_normal );
8180         double sz = newNorm.Modulus();
8181         if ( sz < 1e-200 )
8182           continue;
8183         newNorm /= sz;
8184         double newCosin = ( 0.5 * centerCurves[ iE ]._ledges.front()->_cosin +
8185                             0.5 * centerCurves[ iE ]._ledges.back ()->_cosin );
8186         for ( size_t iLE = 1, nb = centerCurves[ iE ]._ledges.size() - 1; iLE < nb; ++iLE )
8187         {
8188           centerCurves[ iE ]._ledges[ iLE ]->SetNormal( newNorm );
8189           centerCurves[ iE ]._ledges[ iLE ]->_cosin   = newCosin;
8190           centerCurves[ iE ]._ledges[ iLE ]->Set( _LayerEdge::NORMAL_UPDATED );
8191         }
8192       }
8193
8194       // Find new normals for _LayerEdge's based on FACE
8195
8196       if ( nbCosin > 0 )
8197         avgCosin /= nbCosin;
8198       const TGeomID faceID = meshDS->ShapeToIndex( convFace._face );
8199       map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.find( faceID );
8200       if ( id2eos != convFace._subIdToEOS.end() )
8201       {
8202         int iE = 0;
8203         gp_XYZ newNorm;
8204         _EdgesOnShape& eos = * ( id2eos->second );
8205         for ( size_t i = 0; i < eos._edges.size(); ++i )
8206         {
8207           _LayerEdge* ledge = eos._edges[ i ];
8208           if ( !convFace.GetCenterOfCurvature( ledge, surfProp, helper, center ))
8209             continue;
8210           for ( size_t i = 0; i < centerCurves.size(); ++i, ++iE )
8211           {
8212             iE = iE % centerCurves.size();
8213             if ( centerCurves[ iE ]._isDegenerated )
8214               continue;
8215             newNorm.SetCoord( 0,0,0 );
8216             if ( centerCurves[ iE ].FindNewNormal( center, newNorm ))
8217             {
8218               ledge->SetNormal( newNorm );
8219               ledge->_cosin  = avgCosin;
8220               ledge->Set( _LayerEdge::NORMAL_UPDATED );
8221               break;
8222             }
8223           }
8224         }
8225       }
8226
8227     } // not a quasi-spherical FACE
8228
8229     // Update _LayerEdge's data according to a new normal
8230
8231     dumpFunction(SMESH_Comment("updateNormalsOfConvexFaces")<<data._index
8232                  <<"_F"<<meshDS->ShapeToIndex( convFace._face ));
8233
8234     id2eos = convFace._subIdToEOS.begin();
8235     for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
8236     {
8237       _EdgesOnShape& eos = * ( id2eos->second );
8238       for ( size_t i = 0; i < eos._edges.size(); ++i )
8239       {
8240         _LayerEdge* & ledge = eos._edges[ i ];
8241         double len = ledge->_len;
8242         ledge->InvalidateStep( stepNb + 1, eos, /*restoreLength=*/true );
8243         ledge->SetCosin( ledge->_cosin );
8244         ledge->SetNewLength( len, eos, helper );
8245       }
8246       if ( eos.ShapeType() != TopAbs_FACE )
8247         for ( size_t i = 0; i < eos._edges.size(); ++i )
8248         {
8249           _LayerEdge* ledge = eos._edges[ i ];
8250           for ( size_t iN = 0; iN < ledge->_neibors.size(); ++iN )
8251           {
8252             _LayerEdge* neibor = ledge->_neibors[iN];
8253             if ( neibor->_nodes[0]->GetPosition()->GetDim() == 2 )
8254             {
8255               neibor->Set( _LayerEdge::NEAR_BOUNDARY );
8256               neibor->Set( _LayerEdge::MOVED );
8257               neibor->SetSmooLen( neibor->_len );
8258             }
8259           }
8260         }
8261     } // loop on sub-shapes of convFace._face
8262
8263     // Find FACEs adjacent to convFace._face that got necessity to smooth
8264     // as a result of normals modification
8265
8266     set< _EdgesOnShape* > adjFacesToSmooth;
8267     for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
8268     {
8269       if ( centerCurves[ iE ]._adjFace.IsNull() ||
8270            centerCurves[ iE ]._adjFaceToSmooth )
8271         continue;
8272       for ( size_t iLE = 0; iLE < centerCurves[ iE ]._ledges.size(); ++iLE )
8273       {
8274         if ( centerCurves[ iE ]._ledges[ iLE ]->_cosin > theMinSmoothCosin )
8275         {
8276           adjFacesToSmooth.insert( data.GetShapeEdges( centerCurves[ iE ]._adjFace ));
8277           break;
8278         }
8279       }
8280     }
8281     data.AddShapesToSmooth( adjFacesToSmooth );
8282
8283     dumpFunctionEnd();
8284
8285
8286   } // loop on data._convexFaces
8287
8288   return true;
8289 }
8290
8291 //================================================================================
8292 /*!
8293  * \brief Return max curvature of a FACE
8294  */
8295 //================================================================================
8296
8297 double _ConvexFace::GetMaxCurvature( _SolidData&         data,
8298                                      _EdgesOnShape&      eof,
8299                                      BRepLProp_SLProps&  surfProp,
8300                                      SMESH_MesherHelper& helper)
8301 {
8302   double maxCurvature = 0;
8303
8304   TopoDS_Face F = TopoDS::Face( eof._shape );
8305
8306   const int           nbTestPnt = 5;
8307   const double        oriFactor = ( F.Orientation() == TopAbs_REVERSED ? +1. : -1. );
8308   SMESH_subMeshIteratorPtr smIt = eof._subMesh->getDependsOnIterator(/*includeSelf=*/true);
8309   while ( smIt->more() )
8310   {
8311     SMESH_subMesh* sm = smIt->next();
8312     const TGeomID subID = sm->GetId();
8313
8314     // find _LayerEdge's of a sub-shape
8315     _EdgesOnShape* eos;
8316     if (( eos = data.GetShapeEdges( subID )))
8317       this->_subIdToEOS.insert( make_pair( subID, eos ));
8318     else
8319       continue;
8320
8321     // check concavity and curvature and limit data._stepSize
8322     const double minCurvature =
8323       1. / ( eos->_hyp.GetTotalThickness() * ( 1 + theThickToIntersection ));
8324     size_t iStep = Max( 1, eos->_edges.size() / nbTestPnt );
8325     for ( size_t i = 0; i < eos->_edges.size(); i += iStep )
8326     {
8327       gp_XY uv = helper.GetNodeUV( F, eos->_edges[ i ]->_nodes[0] );
8328       surfProp.SetParameters( uv.X(), uv.Y() );
8329       if ( surfProp.IsCurvatureDefined() )
8330       {
8331         double curvature = Max( surfProp.MaxCurvature() * oriFactor,
8332                                 surfProp.MinCurvature() * oriFactor );
8333         maxCurvature = Max( maxCurvature, curvature );
8334
8335         if ( curvature > minCurvature )
8336           this->_isTooCurved = true;
8337       }
8338     }
8339   } // loop on sub-shapes of the FACE
8340
8341   return maxCurvature;
8342 }
8343
8344 //================================================================================
8345 /*!
8346  * \brief Finds a center of curvature of a surface at a _LayerEdge
8347  */
8348 //================================================================================
8349
8350 bool _ConvexFace::GetCenterOfCurvature( _LayerEdge*         ledge,
8351                                         BRepLProp_SLProps&  surfProp,
8352                                         SMESH_MesherHelper& helper,
8353                                         gp_Pnt &            center ) const
8354 {
8355   gp_XY uv = helper.GetNodeUV( _face, ledge->_nodes[0] );
8356   surfProp.SetParameters( uv.X(), uv.Y() );
8357   if ( !surfProp.IsCurvatureDefined() )
8358     return false;
8359
8360   const double oriFactor = ( _face.Orientation() == TopAbs_REVERSED ? +1. : -1. );
8361   double surfCurvatureMax = surfProp.MaxCurvature() * oriFactor;
8362   double surfCurvatureMin = surfProp.MinCurvature() * oriFactor;
8363   if ( surfCurvatureMin > surfCurvatureMax )
8364     center = surfProp.Value().Translated( surfProp.Normal().XYZ() / surfCurvatureMin * oriFactor );
8365   else
8366     center = surfProp.Value().Translated( surfProp.Normal().XYZ() / surfCurvatureMax * oriFactor );
8367
8368   return true;
8369 }
8370
8371 //================================================================================
8372 /*!
8373  * \brief Check that prisms are not distorted
8374  */
8375 //================================================================================
8376
8377 bool _ConvexFace::CheckPrisms() const
8378 {
8379   double vol = 0;
8380   for ( size_t i = 0; i < _simplexTestEdges.size(); ++i )
8381   {
8382     const _LayerEdge* edge = _simplexTestEdges[i];
8383     SMESH_TNodeXYZ tgtXYZ( edge->_nodes.back() );
8384     for ( size_t j = 0; j < edge->_simplices.size(); ++j )
8385       if ( !edge->_simplices[j].IsForward( edge->_nodes[0], tgtXYZ, vol ))
8386       {
8387         debugMsg( "Bad simplex of _simplexTestEdges ("
8388                   << " "<< edge->_nodes[0]->GetID()<< " "<< tgtXYZ._node->GetID()
8389                   << " "<< edge->_simplices[j]._nPrev->GetID()
8390                   << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
8391         return false;
8392       }
8393   }
8394   return true;
8395 }
8396
8397 //================================================================================
8398 /*!
8399  * \brief Try to compute a new normal by interpolating normals of _LayerEdge's
8400  *        stored in this _CentralCurveOnEdge.
8401  *  \param [in] center - curvature center of a point of another _CentralCurveOnEdge.
8402  *  \param [in,out] newNormal - current normal at this point, to be redefined
8403  *  \return bool - true if succeeded.
8404  */
8405 //================================================================================
8406
8407 bool _CentralCurveOnEdge::FindNewNormal( const gp_Pnt& center, gp_XYZ& newNormal )
8408 {
8409   if ( this->_isDegenerated )
8410     return false;
8411
8412   // find two centers the given one lies between
8413
8414   for ( size_t i = 0, nb = _curvaCenters.size()-1;  i < nb;  ++i )
8415   {
8416     double sl2 = 1.001 * _segLength2[ i ];
8417
8418     double d1 = center.SquareDistance( _curvaCenters[ i ]);
8419     if ( d1 > sl2 )
8420       continue;
8421     
8422     double d2 = center.SquareDistance( _curvaCenters[ i+1 ]);
8423     if ( d2 > sl2 || d2 + d1 < 1e-100 )
8424       continue;
8425
8426     d1 = Sqrt( d1 );
8427     d2 = Sqrt( d2 );
8428     double r = d1 / ( d1 + d2 );
8429     gp_XYZ norm = (( 1. - r ) * _ledges[ i   ]->_normal +
8430                    (      r ) * _ledges[ i+1 ]->_normal );
8431     norm.Normalize();
8432
8433     newNormal += norm;
8434     double sz = newNormal.Modulus();
8435     if ( sz < 1e-200 )
8436       break;
8437     newNormal /= sz;
8438     return true;
8439   }
8440   return false;
8441 }
8442
8443 //================================================================================
8444 /*!
8445  * \brief Set shape members
8446  */
8447 //================================================================================
8448
8449 void _CentralCurveOnEdge::SetShapes( const TopoDS_Edge&  edge,
8450                                      const _ConvexFace&  convFace,
8451                                      _SolidData&         data,
8452                                      SMESH_MesherHelper& helper)
8453 {
8454   _edge = edge;
8455
8456   PShapeIteratorPtr fIt = helper.GetAncestors( edge, *helper.GetMesh(), TopAbs_FACE );
8457   while ( const TopoDS_Shape* F = fIt->next())
8458     if ( !convFace._face.IsSame( *F ))
8459     {
8460       _adjFace = TopoDS::Face( *F );
8461       _adjFaceToSmooth = false;
8462       // _adjFace already in a smoothing queue ?
8463       if ( _EdgesOnShape* eos = data.GetShapeEdges( _adjFace ))
8464         _adjFaceToSmooth = eos->_toSmooth;
8465       break;
8466     }
8467 }
8468
8469 //================================================================================
8470 /*!
8471  * \brief Looks for intersection of it's last segment with faces
8472  *  \param distance - returns shortest distance from the last node to intersection
8473  */
8474 //================================================================================
8475
8476 bool _LayerEdge::FindIntersection( SMESH_ElementSearcher&   searcher,
8477                                    double &                 distance,
8478                                    const double&            epsilon,
8479                                    _EdgesOnShape&           eos,
8480                                    const SMDS_MeshElement** intFace)
8481 {
8482   vector< const SMDS_MeshElement* > suspectFaces;
8483   double segLen;
8484   gp_Ax1 lastSegment = LastSegment( segLen, eos );
8485   searcher.GetElementsNearLine( lastSegment, SMDSAbs_Face, suspectFaces );
8486
8487   bool segmentIntersected = false;
8488   distance = Precision::Infinite();
8489   int iFace = -1; // intersected face
8490   for ( size_t j = 0 ; j < suspectFaces.size() /*&& !segmentIntersected*/; ++j )
8491   {
8492     const SMDS_MeshElement* face = suspectFaces[j];
8493     if ( face->GetNodeIndex( _nodes.back() ) >= 0 ||
8494          face->GetNodeIndex( _nodes[0]     ) >= 0 )
8495       continue; // face sharing _LayerEdge node
8496     const int nbNodes = face->NbCornerNodes();
8497     bool intFound = false;
8498     double dist;
8499     SMDS_MeshElement::iterator nIt = face->begin_nodes();
8500     if ( nbNodes == 3 )
8501     {
8502       intFound = SegTriaInter( lastSegment, *nIt++, *nIt++, *nIt++, dist, epsilon );
8503     }
8504     else
8505     {
8506       const SMDS_MeshNode* tria[3];
8507       tria[0] = *nIt++;
8508       tria[1] = *nIt++;
8509       for ( int n2 = 2; n2 < nbNodes && !intFound; ++n2 )
8510       {
8511         tria[2] = *nIt++;
8512         intFound = SegTriaInter(lastSegment, tria[0], tria[1], tria[2], dist, epsilon );
8513         tria[1] = tria[2];
8514       }
8515     }
8516     if ( intFound )
8517     {
8518       if ( dist < segLen*(1.01) && dist > -(_len*_lenFactor-segLen) )
8519         segmentIntersected = true;
8520       if ( distance > dist )
8521         distance = dist, iFace = j;
8522     }
8523   }
8524   if ( intFace ) *intFace = ( iFace != -1 ) ? suspectFaces[iFace] : 0;
8525
8526   distance -= segLen;
8527
8528   if ( segmentIntersected )
8529   {
8530 #ifdef __myDEBUG
8531     SMDS_MeshElement::iterator nIt = suspectFaces[iFace]->begin_nodes();
8532     gp_XYZ intP( lastSegment.Location().XYZ() + lastSegment.Direction().XYZ() * ( distance+segLen ));
8533     cout << "nodes: tgt " << _nodes.back()->GetID() << " src " << _nodes[0]->GetID()
8534          << ", intersection with face ("
8535          << (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()
8536          << ") at point (" << intP.X() << ", " << intP.Y() << ", " << intP.Z()
8537          << ") distance = " << distance << endl;
8538 #endif
8539   }
8540
8541   return segmentIntersected;
8542 }
8543
8544 //================================================================================
8545 /*!
8546  * \brief Returns a point used to check orientation of _simplices
8547  */
8548 //================================================================================
8549
8550 gp_XYZ _LayerEdge::PrevCheckPos( _EdgesOnShape* eos ) const
8551 {
8552   size_t i = Is( NORMAL_UPDATED ) && IsOnFace() ? _pos.size()-2 : 0;
8553
8554   if ( !eos || eos->_sWOL.IsNull() )
8555     return _pos[ i ];
8556
8557   if ( eos->SWOLType() == TopAbs_EDGE )
8558   {
8559     return BRepAdaptor_Curve( TopoDS::Edge( eos->_sWOL )).Value( _pos[i].X() ).XYZ();
8560   }
8561   //else //  TopAbs_FACE
8562
8563   return BRepAdaptor_Surface( TopoDS::Face( eos->_sWOL )).Value(_pos[i].X(), _pos[i].Y() ).XYZ();
8564 }
8565
8566 //================================================================================
8567 /*!
8568  * \brief Returns size and direction of the last segment
8569  */
8570 //================================================================================
8571
8572 gp_Ax1 _LayerEdge::LastSegment(double& segLen, _EdgesOnShape& eos) const
8573 {
8574   // find two non-coincident positions
8575   gp_XYZ orig = _pos.back();
8576   gp_XYZ vec;
8577   int iPrev = _pos.size() - 2;
8578   //const double tol = ( _len > 0 ) ? 0.3*_len : 1e-100; // adjusted for IPAL52478 + PAL22576
8579   const double tol = ( _len > 0 ) ? ( 1e-6 * _len ) : 1e-100;
8580   while ( iPrev >= 0 )
8581   {
8582     vec = orig - _pos[iPrev];
8583     if ( vec.SquareModulus() > tol*tol )
8584       break;
8585     else
8586       iPrev--;
8587   }
8588
8589   // make gp_Ax1
8590   gp_Ax1 segDir;
8591   if ( iPrev < 0 )
8592   {
8593     segDir.SetLocation( SMESH_TNodeXYZ( _nodes[0] ));
8594     segDir.SetDirection( _normal );
8595     segLen = 0;
8596   }
8597   else
8598   {
8599     gp_Pnt pPrev = _pos[ iPrev ];
8600     if ( !eos._sWOL.IsNull() )
8601     {
8602       TopLoc_Location loc;
8603       if ( eos.SWOLType() == TopAbs_EDGE )
8604       {
8605         double f,l;
8606         Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( eos._sWOL ), loc, f,l);
8607         pPrev = curve->Value( pPrev.X() ).Transformed( loc );
8608       }
8609       else
8610       {
8611         Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face( eos._sWOL ), loc );
8612         pPrev = surface->Value( pPrev.X(), pPrev.Y() ).Transformed( loc );
8613       }
8614       vec = SMESH_TNodeXYZ( _nodes.back() ) - pPrev.XYZ();
8615     }
8616     segDir.SetLocation( pPrev );
8617     segDir.SetDirection( vec );
8618     segLen = vec.Modulus();
8619   }
8620
8621   return segDir;
8622 }
8623
8624 //================================================================================
8625 /*!
8626  * \brief Return the last (or \a which) position of the target node on a FACE. 
8627  *  \param [in] F - the FACE this _LayerEdge is inflated along
8628  *  \param [in] which - index of position
8629  *  \return gp_XY - result UV
8630  */
8631 //================================================================================
8632
8633 gp_XY _LayerEdge::LastUV( const TopoDS_Face& F, _EdgesOnShape& eos, int which ) const
8634 {
8635   if ( F.IsSame( eos._sWOL )) // F is my FACE
8636     return gp_XY( _pos.back().X(), _pos.back().Y() );
8637
8638   if ( eos.SWOLType() != TopAbs_EDGE ) // wrong call
8639     return gp_XY( 1e100, 1e100 );
8640
8641   // _sWOL is EDGE of F; _pos.back().X() is the last U on the EDGE
8642   double f, l, u = _pos[ which < 0 ? _pos.size()-1 : which ].X();
8643   Handle(Geom2d_Curve) C2d = BRep_Tool::CurveOnSurface( TopoDS::Edge(eos._sWOL), F, f,l);
8644   if ( !C2d.IsNull() && f <= u && u <= l )
8645     return C2d->Value( u ).XY();
8646
8647   return gp_XY( 1e100, 1e100 );
8648 }
8649
8650 //================================================================================
8651 /*!
8652  * \brief Test intersection of the last segment with a given triangle
8653  *   using Moller-Trumbore algorithm
8654  * Intersection is detected if distance to intersection is less than _LayerEdge._len
8655  */
8656 //================================================================================
8657
8658 bool _LayerEdge::SegTriaInter( const gp_Ax1& lastSegment,
8659                                const gp_XYZ& vert0,
8660                                const gp_XYZ& vert1,
8661                                const gp_XYZ& vert2,
8662                                double&       t,
8663                                const double& EPSILON) const
8664 {
8665   const gp_Pnt& orig = lastSegment.Location();
8666   const gp_Dir& dir  = lastSegment.Direction();
8667
8668   /* calculate distance from vert0 to ray origin */
8669   //gp_XYZ tvec = orig.XYZ() - vert0;
8670
8671   //if ( tvec * dir > EPSILON )
8672     // intersected face is at back side of the temporary face this _LayerEdge belongs to
8673     //return false;
8674
8675   gp_XYZ edge1 = vert1 - vert0;
8676   gp_XYZ edge2 = vert2 - vert0;
8677
8678   /* begin calculating determinant - also used to calculate U parameter */
8679   gp_XYZ pvec = dir.XYZ() ^ edge2;
8680
8681   /* if determinant is near zero, ray lies in plane of triangle */
8682   double det = edge1 * pvec;
8683
8684   const double ANGL_EPSILON = 1e-12;
8685   if ( det > -ANGL_EPSILON && det < ANGL_EPSILON )
8686     return false;
8687
8688   /* calculate distance from vert0 to ray origin */
8689   gp_XYZ tvec = orig.XYZ() - vert0;
8690
8691   /* calculate U parameter and test bounds */
8692   double u = ( tvec * pvec ) / det;
8693   //if (u < 0.0 || u > 1.0)
8694   if ( u < -EPSILON || u > 1.0 + EPSILON )
8695     return false;
8696
8697   /* prepare to test V parameter */
8698   gp_XYZ qvec = tvec ^ edge1;
8699
8700   /* calculate V parameter and test bounds */
8701   double v = (dir.XYZ() * qvec) / det;
8702   //if ( v < 0.0 || u + v > 1.0 )
8703   if ( v < -EPSILON || u + v > 1.0 + EPSILON )
8704     return false;
8705
8706   /* calculate t, ray intersects triangle */
8707   t = (edge2 * qvec) / det;
8708
8709   //return true;
8710   return t > 0.;
8711 }
8712
8713 //================================================================================
8714 /*!
8715  * \brief _LayerEdge, located at a concave VERTEX of a FACE, moves target nodes of
8716  *        neighbor _LayerEdge's by it's own inflation vector.
8717  *  \param [in] eov - EOS of the VERTEX
8718  *  \param [in] eos - EOS of the FACE
8719  *  \param [in] step - inflation step
8720  *  \param [in,out] badSmooEdges - tangled _LayerEdge's
8721  */
8722 //================================================================================
8723
8724 void _LayerEdge::MoveNearConcaVer( const _EdgesOnShape*    eov,
8725                                    const _EdgesOnShape*    eos,
8726                                    const int               step,
8727                                    vector< _LayerEdge* > & badSmooEdges )
8728 {
8729   // check if any of _neibors is in badSmooEdges
8730   if ( std::find_first_of( _neibors.begin(), _neibors.end(),
8731                            badSmooEdges.begin(), badSmooEdges.end() ) == _neibors.end() )
8732     return;
8733
8734   // get all edges to move
8735
8736   set< _LayerEdge* > edges;
8737
8738   // find a distance between _LayerEdge on VERTEX and its neighbors
8739   gp_XYZ  curPosV = SMESH_TNodeXYZ( _nodes.back() );
8740   double dist2 = 0;
8741   for ( size_t i = 0; i < _neibors.size(); ++i )
8742   {
8743     _LayerEdge* nEdge = _neibors[i];
8744     if ( nEdge->_nodes[0]->getshapeId() == eos->_shapeID )
8745     {
8746       edges.insert( nEdge );
8747       dist2 = Max( dist2, ( curPosV - nEdge->_pos.back() ).SquareModulus() );
8748     }
8749   }
8750   // add _LayerEdge's close to curPosV
8751   size_t nbE;
8752   do {
8753     nbE = edges.size();
8754     for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8755     {
8756       _LayerEdge* edgeF = *e;
8757       for ( size_t i = 0; i < edgeF->_neibors.size(); ++i )
8758       {
8759         _LayerEdge* nEdge = edgeF->_neibors[i];
8760         if ( nEdge->_nodes[0]->getshapeId() == eos->_shapeID &&
8761              dist2 > ( curPosV - nEdge->_pos.back() ).SquareModulus() )
8762           edges.insert( nEdge );
8763       }
8764     }
8765   }
8766   while ( nbE < edges.size() );
8767
8768   // move the target node of the got edges
8769
8770   gp_XYZ prevPosV = PrevPos();
8771   if ( eov->SWOLType() == TopAbs_EDGE )
8772   {
8773     BRepAdaptor_Curve curve ( TopoDS::Edge( eov->_sWOL ));
8774     prevPosV = curve.Value( prevPosV.X() ).XYZ();
8775   }
8776   else if ( eov->SWOLType() == TopAbs_FACE )
8777   {
8778     BRepAdaptor_Surface surface( TopoDS::Face( eov->_sWOL ));
8779     prevPosV = surface.Value( prevPosV.X(), prevPosV.Y() ).XYZ();
8780   }
8781
8782   SMDS_FacePositionPtr fPos;
8783   //double r = 1. - Min( 0.9, step / 10. );
8784   for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8785   {
8786     _LayerEdge*       edgeF = *e;
8787     const gp_XYZ     prevVF = edgeF->PrevPos() - prevPosV;
8788     const gp_XYZ    newPosF = curPosV + prevVF;
8789     SMDS_MeshNode* tgtNodeF = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
8790     tgtNodeF->setXYZ( newPosF.X(), newPosF.Y(), newPosF.Z() );
8791     edgeF->_pos.back() = newPosF;
8792     dumpMoveComm( tgtNodeF, "MoveNearConcaVer" ); // debug
8793
8794     // set _curvature to make edgeF updated by putOnOffsetSurface()
8795     if ( !edgeF->_curvature )
8796       if (( fPos = edgeF->_nodes[0]->GetPosition() ))
8797       {
8798         edgeF->_curvature = _Factory::NewCurvature();
8799         edgeF->_curvature->_r = 0;
8800         edgeF->_curvature->_k = 0;
8801         edgeF->_curvature->_h2lenRatio = 0;
8802         edgeF->_curvature->_uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
8803       }
8804   }
8805   // gp_XYZ inflationVec( SMESH_TNodeXYZ( _nodes.back() ) -
8806   //                      SMESH_TNodeXYZ( _nodes[0]    ));
8807   // for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8808   // {
8809   //   _LayerEdge*      edgeF = *e;
8810   //   gp_XYZ          newPos = SMESH_TNodeXYZ( edgeF->_nodes[0] ) + inflationVec;
8811   //   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
8812   //   tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8813   //   edgeF->_pos.back() = newPosF;
8814   //   dumpMoveComm( tgtNode, "MoveNearConcaVer" ); // debug
8815   // }
8816
8817   // smooth _LayerEdge's around moved nodes
8818   //size_t nbBadBefore = badSmooEdges.size();
8819   for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8820   {
8821     _LayerEdge* edgeF = *e;
8822     for ( size_t j = 0; j < edgeF->_neibors.size(); ++j )
8823       if ( edgeF->_neibors[j]->_nodes[0]->getshapeId() == eos->_shapeID )
8824         //&& !edges.count( edgeF->_neibors[j] ))
8825       {
8826         _LayerEdge* edgeFN = edgeF->_neibors[j];
8827         edgeFN->Unset( SMOOTHED );
8828         int nbBad = edgeFN->Smooth( step, /*isConcaFace=*/true, /*findBest=*/true );
8829         // if ( nbBad > 0 )
8830         // {
8831         //   gp_XYZ         newPos = SMESH_TNodeXYZ( edgeFN->_nodes[0] ) + inflationVec;
8832         //   const gp_XYZ& prevPos = edgeFN->_pos[ edgeFN->_pos.size()-2 ];
8833         //   int        nbBadAfter = edgeFN->_simplices.size();
8834         //   double vol;
8835         //   for ( size_t iS = 0; iS < edgeFN->_simplices.size(); ++iS )
8836         //   {
8837         //     nbBadAfter -= edgeFN->_simplices[iS].IsForward( &prevPos, &newPos, vol );
8838         //   }
8839         //   if ( nbBadAfter <= nbBad )
8840         //   {
8841         //     SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeFN->_nodes.back() );
8842         //     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8843         //     edgeF->_pos.back() = newPosF;
8844         //     dumpMoveComm( tgtNode, "MoveNearConcaVer 2" ); // debug
8845         //     nbBad = nbBadAfter;
8846         //   }
8847         // }
8848         if ( nbBad > 0 )
8849           badSmooEdges.push_back( edgeFN );
8850       }
8851   }
8852     // move a bit not smoothed around moved nodes
8853   //   for ( size_t i = nbBadBefore; i < badSmooEdges.size(); ++i )
8854   //   {
8855   //   _LayerEdge*      edgeF = badSmooEdges[i];
8856   //   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
8857   //   gp_XYZ         newPos1 = SMESH_TNodeXYZ( edgeF->_nodes[0] ) + inflationVec;
8858   //   gp_XYZ         newPos2 = 0.5 * ( newPos1 + SMESH_TNodeXYZ( tgtNode ));
8859   //   tgtNode->setXYZ( newPos2.X(), newPos2.Y(), newPos2.Z() );
8860   //   edgeF->_pos.back() = newPosF;
8861   //   dumpMoveComm( tgtNode, "MoveNearConcaVer 2" ); // debug
8862   // }
8863 }
8864
8865 //================================================================================
8866 /*!
8867  * \brief Perform smooth of _LayerEdge's based on EDGE's
8868  *  \retval bool - true if node has been moved
8869  */
8870 //================================================================================
8871
8872 bool _LayerEdge::SmoothOnEdge(Handle(ShapeAnalysis_Surface)& surface,
8873                               const TopoDS_Face&             F,
8874                               SMESH_MesherHelper&            helper)
8875 {
8876   ASSERT( IsOnEdge() );
8877
8878   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _nodes.back() );
8879   SMESH_TNodeXYZ oldPos( tgtNode );
8880   double dist01, distNewOld;
8881   
8882   SMESH_TNodeXYZ p0( _2neibors->tgtNode(0));
8883   SMESH_TNodeXYZ p1( _2neibors->tgtNode(1));
8884   dist01 = p0.Distance( _2neibors->tgtNode(1) );
8885
8886   gp_Pnt newPos = p0 * _2neibors->_wgt[0] + p1 * _2neibors->_wgt[1];
8887   double lenDelta = 0;
8888   if ( _curvature )
8889   {
8890     //lenDelta = _curvature->lenDelta( _len );
8891     lenDelta = _curvature->lenDeltaByDist( dist01 );
8892     newPos.ChangeCoord() += _normal * lenDelta;
8893   }
8894
8895   distNewOld = newPos.Distance( oldPos );
8896
8897   if ( F.IsNull() )
8898   {
8899     if ( _2neibors->_plnNorm )
8900     {
8901       // put newPos on the plane defined by source node and _plnNorm
8902       gp_XYZ new2src = SMESH_TNodeXYZ( _nodes[0] ) - newPos.XYZ();
8903       double new2srcProj = (*_2neibors->_plnNorm) * new2src;
8904       newPos.ChangeCoord() += (*_2neibors->_plnNorm) * new2srcProj;
8905     }
8906     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8907     _pos.back() = newPos.XYZ();
8908   }
8909   else
8910   {
8911     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8912     gp_XY uv( Precision::Infinite(), 0 );
8913     helper.CheckNodeUV( F, tgtNode, uv, 1e-10, /*force=*/true );
8914     _pos.back().SetCoord( uv.X(), uv.Y(), 0 );
8915
8916     newPos = surface->Value( uv );
8917     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8918   }
8919
8920   // commented for IPAL0052478
8921   // if ( _curvature && lenDelta < 0 )
8922   // {
8923   //   gp_Pnt prevPos( _pos[ _pos.size()-2 ]);
8924   //   _len -= prevPos.Distance( oldPos );
8925   //   _len += prevPos.Distance( newPos );
8926   // }
8927   bool moved = distNewOld > dist01/50;
8928   //if ( moved )
8929   dumpMove( tgtNode ); // debug
8930
8931   return moved;
8932 }
8933
8934 //================================================================================
8935 /*!
8936  * \brief Perform 3D smooth of nodes inflated from FACE. No check of validity
8937  */
8938 //================================================================================
8939
8940 void _LayerEdge::SmoothWoCheck()
8941 {
8942   if ( Is( DIFFICULT ))
8943     return;
8944
8945   bool moved = Is( SMOOTHED );
8946   for ( size_t i = 0; i < _neibors.size()  &&  !moved; ++i )
8947     moved = _neibors[i]->Is( SMOOTHED );
8948   if ( !moved )
8949     return;
8950
8951   gp_XYZ newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
8952
8953   SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
8954   n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
8955   _pos.back() = newPos;
8956
8957   dumpMoveComm( n, SMESH_Comment("No check - ") << _funNames[ smooFunID() ]);
8958 }
8959
8960 //================================================================================
8961 /*!
8962  * \brief Checks validity of _neibors on EDGEs and VERTEXes
8963  */
8964 //================================================================================
8965
8966 int _LayerEdge::CheckNeiborsOnBoundary( vector< _LayerEdge* >* badNeibors, bool * needSmooth )
8967 {
8968   if ( ! Is( NEAR_BOUNDARY ))
8969     return 0;
8970
8971   int nbBad = 0;
8972   double vol;
8973   for ( size_t iN = 0; iN < _neibors.size(); ++iN )
8974   {
8975     _LayerEdge* eN = _neibors[iN];
8976     if ( eN->_nodes[0]->getshapeId() == _nodes[0]->getshapeId() )
8977       continue;
8978     if ( needSmooth )
8979       *needSmooth |= ( eN->Is( _LayerEdge::BLOCKED ) ||
8980                        eN->Is( _LayerEdge::NORMAL_UPDATED ) ||
8981                        eN->_pos.size() != _pos.size() );
8982
8983     SMESH_TNodeXYZ curPosN ( eN->_nodes.back() );
8984     SMESH_TNodeXYZ prevPosN( eN->_nodes[0] );
8985     for ( size_t i = 0; i < eN->_simplices.size(); ++i )
8986       if ( eN->_nodes.size() > 1 &&
8987            eN->_simplices[i].Includes( _nodes.back() ) &&
8988            !eN->_simplices[i].IsForward( &prevPosN, &curPosN, vol ))
8989       {
8990         ++nbBad;
8991         if ( badNeibors )
8992         {
8993           badNeibors->push_back( eN );
8994           debugMsg("Bad boundary simplex ( "
8995                    << " "<< eN->_nodes[0]->GetID()
8996                    << " "<< eN->_nodes.back()->GetID()
8997                    << " "<< eN->_simplices[i]._nPrev->GetID()
8998                    << " "<< eN->_simplices[i]._nNext->GetID() << " )" );
8999         }
9000         else
9001         {
9002           break;
9003         }
9004       }
9005   }
9006   return nbBad;
9007 }
9008
9009 //================================================================================
9010 /*!
9011  * \brief Perform 'smart' 3D smooth of nodes inflated from FACE
9012  *  \retval int - nb of bad simplices around this _LayerEdge
9013  */
9014 //================================================================================
9015
9016 int _LayerEdge::Smooth(const int step, bool findBest, vector< _LayerEdge* >& toSmooth )
9017 {
9018   if ( !Is( MOVED ) || Is( SMOOTHED ) || Is( BLOCKED ))
9019     return 0; // shape of simplices not changed
9020   if ( _simplices.size() < 2 )
9021     return 0; // _LayerEdge inflated along EDGE or FACE
9022
9023   if ( Is( DIFFICULT )) // || Is( ON_CONCAVE_FACE )
9024     findBest = true;
9025
9026   const gp_XYZ& curPos  = _pos.back();
9027   const gp_XYZ& prevPos = _pos[0]; //PrevPos();
9028
9029   // quality metrics (orientation) of tetras around _tgtNode
9030   int nbOkBefore = 0;
9031   double vol, minVolBefore = 1e100;
9032   for ( size_t i = 0; i < _simplices.size(); ++i )
9033   {
9034     nbOkBefore += _simplices[i].IsForward( &prevPos, &curPos, vol );
9035     minVolBefore = Min( minVolBefore, vol );
9036   }
9037   int nbBad = _simplices.size() - nbOkBefore;
9038
9039   bool bndNeedSmooth = false;
9040   if ( nbBad == 0 )
9041     nbBad = CheckNeiborsOnBoundary( 0, & bndNeedSmooth );
9042   if ( nbBad > 0 )
9043     Set( DISTORTED );
9044
9045   // evaluate min angle
9046   if ( nbBad == 0 && !findBest && !bndNeedSmooth )
9047   {
9048     size_t nbGoodAngles = _simplices.size();
9049     double angle;
9050     for ( size_t i = 0; i < _simplices.size(); ++i )
9051     {
9052       if ( !_simplices[i].IsMinAngleOK( curPos, angle ) && angle > _minAngle )
9053         --nbGoodAngles;
9054     }
9055     if ( nbGoodAngles == _simplices.size() )
9056     {
9057       Unset( MOVED );
9058       return 0;
9059     }
9060   }
9061   if ( Is( ON_CONCAVE_FACE ))
9062     findBest = true;
9063
9064   if ( step % 2 == 0 )
9065     findBest = false;
9066
9067   if ( Is( ON_CONCAVE_FACE ) && !findBest ) // alternate FUN_CENTROIDAL and FUN_LAPLACIAN
9068   {
9069     if ( _smooFunction == _funs[ FUN_LAPLACIAN ] )
9070       _smooFunction = _funs[ FUN_CENTROIDAL ];
9071     else
9072       _smooFunction = _funs[ FUN_LAPLACIAN ];
9073   }
9074
9075   // compute new position for the last _pos using different _funs
9076   gp_XYZ newPos;
9077   bool moved = false;
9078   for ( int iFun = -1; iFun < theNbSmooFuns; ++iFun )
9079   {
9080     if ( iFun < 0 )
9081       newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
9082     else if ( _funs[ iFun ] == _smooFunction )
9083       continue; // _smooFunction again
9084     else if ( step > 1 )
9085       newPos = (this->*_funs[ iFun ])(); // try other smoothing fun
9086     else
9087       break; // let "easy" functions improve elements around distorted ones
9088
9089     if ( _curvature )
9090     {
9091       double delta  = _curvature->lenDelta( _len );
9092       if ( delta > 0 )
9093         newPos += _normal * delta;
9094       else
9095       {
9096         double segLen = _normal * ( newPos - prevPos );
9097         if ( segLen + delta > 0 )
9098           newPos += _normal * delta;
9099       }
9100       // double segLenChange = _normal * ( curPos - newPos );
9101       // newPos += 0.5 * _normal * segLenChange;
9102     }
9103
9104     int nbOkAfter = 0;
9105     double minVolAfter = 1e100;
9106     for ( size_t i = 0; i < _simplices.size(); ++i )
9107     {
9108       nbOkAfter += _simplices[i].IsForward( &prevPos, &newPos, vol );
9109       minVolAfter = Min( minVolAfter, vol );
9110     }
9111     // get worse?
9112     if ( nbOkAfter < nbOkBefore )
9113       continue;
9114
9115     if (( findBest ) &&
9116         ( nbOkAfter == nbOkBefore ) &&
9117         ( minVolAfter <= minVolBefore ))
9118       continue;
9119
9120     nbBad        = _simplices.size() - nbOkAfter;
9121     minVolBefore = minVolAfter;
9122     nbOkBefore   = nbOkAfter;
9123     moved        = true;
9124
9125     SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
9126     n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
9127     _pos.back() = newPos;
9128
9129     dumpMoveComm( n, SMESH_Comment( _funNames[ iFun < 0 ? smooFunID() : iFun ] )
9130                   << (nbBad ? " --BAD" : ""));
9131
9132     if ( iFun > -1 )
9133     {
9134       continue; // look for a better function
9135     }
9136
9137     if ( !findBest )
9138       break;
9139
9140   } // loop on smoothing functions
9141
9142   if ( moved ) // notify _neibors
9143   {
9144     Set( SMOOTHED );
9145     for ( size_t i = 0; i < _neibors.size(); ++i )
9146       if ( !_neibors[i]->Is( MOVED ))
9147       {
9148         _neibors[i]->Set( MOVED );
9149         toSmooth.push_back( _neibors[i] );
9150       }
9151   }
9152
9153   return nbBad;
9154 }
9155
9156 //================================================================================
9157 /*!
9158  * \brief Perform 'smart' 3D smooth of nodes inflated from FACE
9159  *  \retval int - nb of bad simplices around this _LayerEdge
9160  */
9161 //================================================================================
9162
9163 int _LayerEdge::Smooth(const int step, const bool isConcaveFace, bool findBest )
9164 {
9165   if ( !_smooFunction )
9166     return 0; // _LayerEdge inflated along EDGE or FACE
9167   if ( Is( BLOCKED ))
9168     return 0; // not inflated
9169
9170   const gp_XYZ& curPos  = _pos.back();
9171   const gp_XYZ& prevPos = _pos[0]; //PrevCheckPos();
9172
9173   // quality metrics (orientation) of tetras around _tgtNode
9174   int nbOkBefore = 0;
9175   double vol, minVolBefore = 1e100;
9176   for ( size_t i = 0; i < _simplices.size(); ++i )
9177   {
9178     nbOkBefore += _simplices[i].IsForward( &prevPos, &curPos, vol );
9179     minVolBefore = Min( minVolBefore, vol );
9180   }
9181   int nbBad = _simplices.size() - nbOkBefore;
9182
9183   if ( isConcaveFace ) // alternate FUN_CENTROIDAL and FUN_LAPLACIAN
9184   {
9185     if      ( _smooFunction == _funs[ FUN_CENTROIDAL ] && step % 2 )
9186       _smooFunction = _funs[ FUN_LAPLACIAN ];
9187     else if ( _smooFunction == _funs[ FUN_LAPLACIAN ] && !( step % 2 ))
9188       _smooFunction = _funs[ FUN_CENTROIDAL ];
9189   }
9190
9191   // compute new position for the last _pos using different _funs
9192   gp_XYZ newPos;
9193   for ( int iFun = -1; iFun < theNbSmooFuns; ++iFun )
9194   {
9195     if ( iFun < 0 )
9196       newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
9197     else if ( _funs[ iFun ] == _smooFunction )
9198       continue; // _smooFunction again
9199     else if ( step > 1 )
9200       newPos = (this->*_funs[ iFun ])(); // try other smoothing fun
9201     else
9202       break; // let "easy" functions improve elements around distorted ones
9203
9204     if ( _curvature )
9205     {
9206       double delta  = _curvature->lenDelta( _len );
9207       if ( delta > 0 )
9208         newPos += _normal * delta;
9209       else
9210       {
9211         double segLen = _normal * ( newPos - prevPos );
9212         if ( segLen + delta > 0 )
9213           newPos += _normal * delta;
9214       }
9215       // double segLenChange = _normal * ( curPos - newPos );
9216       // newPos += 0.5 * _normal * segLenChange;
9217     }
9218
9219     int nbOkAfter = 0;
9220     double minVolAfter = 1e100;
9221     for ( size_t i = 0; i < _simplices.size(); ++i )
9222     {
9223       nbOkAfter += _simplices[i].IsForward( &prevPos, &newPos, vol );
9224       minVolAfter = Min( minVolAfter, vol );
9225     }
9226     // get worse?
9227     if ( nbOkAfter < nbOkBefore )
9228       continue;
9229     if (( isConcaveFace || findBest ) &&
9230         ( nbOkAfter == nbOkBefore ) &&
9231         ( minVolAfter <= minVolBefore )
9232         )
9233       continue;
9234
9235     nbBad        = _simplices.size() - nbOkAfter;
9236     minVolBefore = minVolAfter;
9237     nbOkBefore   = nbOkAfter;
9238
9239     SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
9240     n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
9241     _pos.back() = newPos;
9242
9243     dumpMoveComm( n, SMESH_Comment( _funNames[ iFun < 0 ? smooFunID() : iFun ] )
9244                   << ( nbBad ? "--BAD" : ""));
9245
9246     // commented for IPAL0052478
9247     // _len -= prevPos.Distance(SMESH_TNodeXYZ( n ));
9248     // _len += prevPos.Distance(newPos);
9249
9250     if ( iFun > -1 ) // findBest || the chosen _fun makes worse
9251     {
9252       //_smooFunction = _funs[ iFun ];
9253       // cout << "# " << _funNames[ iFun ] << "\t N:" << _nodes.back()->GetID()
9254       // << "\t nbBad: " << _simplices.size() - nbOkAfter
9255       // << " minVol: " << minVolAfter
9256       // << " " << newPos.X() << " " << newPos.Y() << " " << newPos.Z()
9257       // << endl;
9258       continue; // look for a better function
9259     }
9260
9261     if ( !findBest )
9262       break;
9263
9264   } // loop on smoothing functions
9265
9266   return nbBad;
9267 }
9268
9269 //================================================================================
9270 /*!
9271  * \brief Chooses a smoothing technique giving a position most close to an initial one.
9272  *        For a correct result, _simplices must contain nodes lying on geometry.
9273  */
9274 //================================================================================
9275
9276 void _LayerEdge::ChooseSmooFunction( const set< TGeomID >& concaveVertices,
9277                                      const TNode2Edge&     /*n2eMap*/)
9278 {
9279   if ( _smooFunction ) return;
9280
9281   // use smoothNefPolygon() near concaveVertices
9282   if ( !concaveVertices.empty() )
9283   {
9284     _smooFunction = _funs[ FUN_CENTROIDAL ];
9285
9286     Set( ON_CONCAVE_FACE );
9287
9288     for ( size_t i = 0; i < _simplices.size(); ++i )
9289     {
9290       if ( concaveVertices.count( _simplices[i]._nPrev->getshapeId() ))
9291       {
9292         _smooFunction = _funs[ FUN_NEFPOLY ];
9293
9294         // set FUN_CENTROIDAL to neighbor edges
9295         for ( i = 0; i < _neibors.size(); ++i )
9296         {
9297           if ( _neibors[i]->_nodes[0]->GetPosition()->GetDim() == 2 )
9298           {
9299             _neibors[i]->_smooFunction = _funs[ FUN_CENTROIDAL ];
9300           }
9301         }
9302         return;
9303       }
9304     }
9305
9306     // // this choice is done only if ( !concaveVertices.empty() ) for Grids/smesh/bugs_19/X1
9307     // // where the nodes are smoothed too far along a sphere thus creating
9308     // // inverted _simplices
9309     // double dist[theNbSmooFuns];
9310     // //double coef[theNbSmooFuns] = { 1., 1.2, 1.4, 1.4 };
9311     // double coef[theNbSmooFuns] = { 1., 1., 1., 1. };
9312
9313     // double minDist = Precision::Infinite();
9314     // gp_Pnt p = SMESH_TNodeXYZ( _nodes[0] );
9315     // for ( int i = 0; i < FUN_NEFPOLY; ++i )
9316     // {
9317     //   gp_Pnt newP = (this->*_funs[i])();
9318     //   dist[i] = p.SquareDistance( newP );
9319     //   if ( dist[i]*coef[i] < minDist )
9320     //   {
9321     //     _smooFunction = _funs[i];
9322     //     minDist = dist[i]*coef[i];
9323     //   }
9324     // }
9325   }
9326   else
9327   {
9328     _smooFunction = _funs[ FUN_LAPLACIAN ];
9329   }
9330   // int minDim = 3;
9331   // for ( size_t i = 0; i < _simplices.size(); ++i )
9332   //   minDim = Min( minDim, _simplices[i]._nPrev->GetPosition()->GetDim() );
9333   // if ( minDim == 0 )
9334   //   _smooFunction = _funs[ FUN_CENTROIDAL ];
9335   // else if ( minDim == 1 )
9336   //   _smooFunction = _funs[ FUN_CENTROIDAL ];
9337
9338
9339   // int iMin;
9340   // for ( int i = 0; i < FUN_NB; ++i )
9341   // {
9342   //   //cout << dist[i] << " ";
9343   //   if ( _smooFunction == _funs[i] ) {
9344   //     iMin = i;
9345   //     //debugMsg( fNames[i] );
9346   //     break;
9347   //   }
9348   // }
9349   // cout << _funNames[ iMin ] << "\t N:" << _nodes.back()->GetID() << endl;
9350 }
9351
9352 //================================================================================
9353 /*!
9354  * \brief Returns a name of _SmooFunction
9355  */
9356 //================================================================================
9357
9358 int _LayerEdge::smooFunID( _LayerEdge::PSmooFun fun) const
9359 {
9360   if ( !fun )
9361     fun = _smooFunction;
9362   for ( int i = 0; i < theNbSmooFuns; ++i )
9363     if ( fun == _funs[i] )
9364       return i;
9365
9366   return theNbSmooFuns;
9367 }
9368
9369 //================================================================================
9370 /*!
9371  * \brief Computes a new node position using Laplacian smoothing
9372  */
9373 //================================================================================
9374
9375 gp_XYZ _LayerEdge::smoothLaplacian()
9376 {
9377   gp_XYZ newPos (0,0,0);
9378   for ( size_t i = 0; i < _simplices.size(); ++i )
9379     newPos += SMESH_TNodeXYZ( _simplices[i]._nPrev );
9380   newPos /= _simplices.size();
9381
9382   return newPos;
9383 }
9384
9385 //================================================================================
9386 /*!
9387  * \brief Computes a new node position using angular-based smoothing
9388  */
9389 //================================================================================
9390
9391 gp_XYZ _LayerEdge::smoothAngular()
9392 {
9393   vector< gp_Vec > edgeDir;  edgeDir. reserve( _simplices.size() + 1 );
9394   vector< double > edgeSize; edgeSize.reserve( _simplices.size()     );
9395   vector< gp_XYZ > points;   points.  reserve( _simplices.size() + 1 );
9396
9397   gp_XYZ pPrev = SMESH_TNodeXYZ( _simplices.back()._nPrev );
9398   gp_XYZ pN( 0,0,0 );
9399   for ( size_t i = 0; i < _simplices.size(); ++i )
9400   {
9401     gp_XYZ p = SMESH_TNodeXYZ( _simplices[i]._nPrev );
9402     edgeDir.push_back( p - pPrev );
9403     edgeSize.push_back( edgeDir.back().Magnitude() );
9404     if ( edgeSize.back() < numeric_limits<double>::min() )
9405     {
9406       edgeDir.pop_back();
9407       edgeSize.pop_back();
9408     }
9409     else
9410     {
9411       edgeDir.back() /= edgeSize.back();
9412       points.push_back( p );
9413       pN += p;
9414     }
9415     pPrev = p;
9416   }
9417   edgeDir.push_back ( edgeDir[0] );
9418   edgeSize.push_back( edgeSize[0] );
9419   pN /= points.size();
9420
9421   gp_XYZ newPos(0,0,0);
9422   double sumSize = 0;
9423   for ( size_t i = 0; i < points.size(); ++i )
9424   {
9425     gp_Vec toN    = pN - points[i];
9426     double toNLen = toN.Magnitude();
9427     if ( toNLen < numeric_limits<double>::min() )
9428     {
9429       newPos += pN;
9430       continue;
9431     }
9432     gp_Vec bisec    = edgeDir[i] + edgeDir[i+1];
9433     double bisecLen = bisec.SquareMagnitude();
9434     if ( bisecLen < numeric_limits<double>::min() )
9435     {
9436       gp_Vec norm = edgeDir[i] ^ toN;
9437       bisec = norm ^ edgeDir[i];
9438       bisecLen = bisec.SquareMagnitude();
9439     }
9440     bisecLen = Sqrt( bisecLen );
9441     bisec /= bisecLen;
9442
9443 #if 1
9444     gp_XYZ pNew = ( points[i] + bisec.XYZ() * toNLen ) * bisecLen;
9445     sumSize += bisecLen;
9446 #else
9447     gp_XYZ pNew = ( points[i] + bisec.XYZ() * toNLen ) * ( edgeSize[i] + edgeSize[i+1] );
9448     sumSize += ( edgeSize[i] + edgeSize[i+1] );
9449 #endif
9450     newPos += pNew;
9451   }
9452   newPos /= sumSize;
9453
9454   // project newPos to an average plane
9455
9456   gp_XYZ norm(0,0,0); // plane normal
9457   points.push_back( points[0] );
9458   for ( size_t i = 1; i < points.size(); ++i )
9459   {
9460     gp_XYZ vec1 = points[ i-1 ] - pN;
9461     gp_XYZ vec2 = points[ i   ] - pN;
9462     gp_XYZ cross = vec1 ^ vec2;
9463     try {
9464       cross.Normalize();
9465       if ( cross * norm < numeric_limits<double>::min() )
9466         norm += cross.Reversed();
9467       else
9468         norm += cross;
9469     }
9470     catch (Standard_Failure&) { // if |cross| == 0.
9471     }
9472   }
9473   gp_XYZ vec = newPos - pN;
9474   double r   = ( norm * vec ) / norm.SquareModulus();  // param [0,1] on norm
9475   newPos     = newPos - r * norm;
9476
9477   return newPos;
9478 }
9479
9480 //================================================================================
9481 /*!
9482  * \brief Computes a new node position using weighted node positions
9483  */
9484 //================================================================================
9485
9486 gp_XYZ _LayerEdge::smoothLengthWeighted()
9487 {
9488   vector< double > edgeSize; edgeSize.reserve( _simplices.size() + 1);
9489   vector< gp_XYZ > points;   points.  reserve( _simplices.size() );
9490
9491   gp_XYZ pPrev = SMESH_TNodeXYZ( _simplices.back()._nPrev );
9492   for ( size_t i = 0; i < _simplices.size(); ++i )
9493   {
9494     gp_XYZ p = SMESH_TNodeXYZ( _simplices[i]._nPrev );
9495     edgeSize.push_back( ( p - pPrev ).Modulus() );
9496     if ( edgeSize.back() < numeric_limits<double>::min() )
9497     {
9498       edgeSize.pop_back();
9499     }
9500     else
9501     {
9502       points.push_back( p );
9503     }
9504     pPrev = p;
9505   }
9506   edgeSize.push_back( edgeSize[0] );
9507
9508   gp_XYZ newPos(0,0,0);
9509   double sumSize = 0;
9510   for ( size_t i = 0; i < points.size(); ++i )
9511   {
9512     newPos += points[i] * ( edgeSize[i] + edgeSize[i+1] );
9513     sumSize += edgeSize[i] + edgeSize[i+1];
9514   }
9515   newPos /= sumSize;
9516   return newPos;
9517 }
9518
9519 //================================================================================
9520 /*!
9521  * \brief Computes a new node position using angular-based smoothing
9522  */
9523 //================================================================================
9524
9525 gp_XYZ _LayerEdge::smoothCentroidal()
9526 {
9527   gp_XYZ newPos(0,0,0);
9528   gp_XYZ pN = SMESH_TNodeXYZ( _nodes.back() );
9529   double sumSize = 0;
9530   for ( size_t i = 0; i < _simplices.size(); ++i )
9531   {
9532     gp_XYZ p1 = SMESH_TNodeXYZ( _simplices[i]._nPrev );
9533     gp_XYZ p2 = SMESH_TNodeXYZ( _simplices[i]._nNext );
9534     gp_XYZ gc = ( pN + p1 + p2 ) / 3.;
9535     double size = (( p1 - pN ) ^ ( p2 - pN )).Modulus();
9536
9537     sumSize += size;
9538     newPos += gc * size;
9539   }
9540   newPos /= sumSize;
9541
9542   return newPos;
9543 }
9544
9545 //================================================================================
9546 /*!
9547  * \brief Computes a new node position located inside a Nef polygon
9548  */
9549 //================================================================================
9550
9551 gp_XYZ _LayerEdge::smoothNefPolygon()
9552 #ifdef OLD_NEF_POLYGON
9553 {
9554   gp_XYZ newPos(0,0,0);
9555
9556   // get a plane to search a solution on
9557
9558   vector< gp_XYZ > vecs( _simplices.size() + 1 );
9559   size_t i;
9560   const double tol = numeric_limits<double>::min();
9561   gp_XYZ center(0,0,0);
9562   for ( i = 0; i < _simplices.size(); ++i )
9563   {
9564     vecs[i] = ( SMESH_TNodeXYZ( _simplices[i]._nNext ) -
9565                 SMESH_TNodeXYZ( _simplices[i]._nPrev ));
9566     center += SMESH_TNodeXYZ( _simplices[i]._nPrev );
9567   }
9568   vecs.back() = vecs[0];
9569   center /= _simplices.size();
9570
9571   gp_XYZ zAxis(0,0,0);
9572   for ( i = 0; i < _simplices.size(); ++i )
9573     zAxis += vecs[i] ^ vecs[i+1];
9574
9575   gp_XYZ yAxis;
9576   for ( i = 0; i < _simplices.size(); ++i )
9577   {
9578     yAxis = vecs[i];
9579     if ( yAxis.SquareModulus() > tol )
9580       break;
9581   }
9582   gp_XYZ xAxis = yAxis ^ zAxis;
9583   // SMESH_TNodeXYZ p0( _simplices[0]._nPrev );
9584   // const double tol = 1e-6 * ( p0.Distance( _simplices[1]._nPrev ) +
9585   //                             p0.Distance( _simplices[2]._nPrev ));
9586   // gp_XYZ center = smoothLaplacian();
9587   // gp_XYZ xAxis, yAxis, zAxis;
9588   // for ( i = 0; i < _simplices.size(); ++i )
9589   // {
9590   //   xAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9591   //   if ( xAxis.SquareModulus() > tol*tol )
9592   //     break;
9593   // }
9594   // for ( i = 1; i < _simplices.size(); ++i )
9595   // {
9596   //   yAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9597   //   zAxis = xAxis ^ yAxis;
9598   //   if ( zAxis.SquareModulus() > tol*tol )
9599   //     break;
9600   // }
9601   // if ( i == _simplices.size() ) return newPos;
9602
9603   yAxis = zAxis ^ xAxis;
9604   xAxis /= xAxis.Modulus();
9605   yAxis /= yAxis.Modulus();
9606
9607   // get half-planes of _simplices
9608
9609   vector< _halfPlane > halfPlns( _simplices.size() );
9610   int nbHP = 0;
9611   for ( size_t i = 0; i < _simplices.size(); ++i )
9612   {
9613     gp_XYZ OP1 = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9614     gp_XYZ OP2 = SMESH_TNodeXYZ( _simplices[i]._nNext ) - center;
9615     gp_XY  p1( OP1 * xAxis, OP1 * yAxis );
9616     gp_XY  p2( OP2 * xAxis, OP2 * yAxis );
9617     gp_XY  vec12 = p2 - p1;
9618     double dist12 = vec12.Modulus();
9619     if ( dist12 < tol )
9620       continue;
9621     vec12 /= dist12;
9622     halfPlns[ nbHP ]._pos = p1;
9623     halfPlns[ nbHP ]._dir = vec12;
9624     halfPlns[ nbHP ]._inNorm.SetCoord( -vec12.Y(), vec12.X() );
9625     ++nbHP;
9626   }
9627
9628   // intersect boundaries of half-planes, define state of intersection points
9629   // in relation to all half-planes and calculate internal point of a 2D polygon
9630
9631   double sumLen = 0;
9632   gp_XY newPos2D (0,0);
9633
9634   enum { UNDEF = -1, NOT_OUT, IS_OUT, NO_INT };
9635   typedef std::pair< gp_XY, int > TIntPntState; // coord and isOut state
9636   TIntPntState undefIPS( gp_XY(1e100,1e100), UNDEF );
9637
9638   vector< vector< TIntPntState > > allIntPnts( nbHP );
9639   for ( int iHP1 = 0; iHP1 < nbHP; ++iHP1 )
9640   {
9641     vector< TIntPntState > & intPnts1 = allIntPnts[ iHP1 ];
9642     if ( intPnts1.empty() ) intPnts1.resize( nbHP, undefIPS );
9643
9644     int iPrev = SMESH_MesherHelper::WrapIndex( iHP1 - 1, nbHP );
9645     int iNext = SMESH_MesherHelper::WrapIndex( iHP1 + 1, nbHP );
9646
9647     int nbNotOut = 0;
9648     const gp_XY* segEnds[2] = { 0, 0 }; // NOT_OUT points
9649
9650     for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9651     {
9652       if ( iHP1 == iHP2 ) continue;
9653
9654       TIntPntState & ips1 = intPnts1[ iHP2 ];
9655       if ( ips1.second == UNDEF )
9656       {
9657         // find an intersection point of boundaries of iHP1 and iHP2
9658
9659         if ( iHP2 == iPrev ) // intersection with neighbors is known
9660           ips1.first = halfPlns[ iHP1 ]._pos;
9661         else if ( iHP2 == iNext )
9662           ips1.first = halfPlns[ iHP2 ]._pos;
9663         else if ( !halfPlns[ iHP1 ].FindIntersection( halfPlns[ iHP2 ], ips1.first ))
9664           ips1.second = NO_INT;
9665
9666         // classify the found intersection point
9667         if ( ips1.second != NO_INT )
9668         {
9669           ips1.second = NOT_OUT;
9670           for ( int i = 0; i < nbHP && ips1.second == NOT_OUT; ++i )
9671             if ( i != iHP1 && i != iHP2 &&
9672                  halfPlns[ i ].IsOut( ips1.first, tol ))
9673               ips1.second = IS_OUT;
9674         }
9675         vector< TIntPntState > & intPnts2 = allIntPnts[ iHP2 ];
9676         if ( intPnts2.empty() ) intPnts2.resize( nbHP, undefIPS );
9677         TIntPntState & ips2 = intPnts2[ iHP1 ];
9678         ips2 = ips1;
9679       }
9680       if ( ips1.second == NOT_OUT )
9681       {
9682         ++nbNotOut;
9683         segEnds[ bool(segEnds[0]) ] = & ips1.first;
9684       }
9685     }
9686
9687     // find a NOT_OUT segment of boundary which is located between
9688     // two NOT_OUT int points
9689
9690     if ( nbNotOut < 2 )
9691       continue; // no such a segment
9692
9693     if ( nbNotOut > 2 )
9694     {
9695       // sort points along the boundary
9696       map< double, TIntPntState* > ipsByParam;
9697       for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9698       {
9699         TIntPntState & ips1 = intPnts1[ iHP2 ];
9700         if ( ips1.second != NO_INT )
9701         {
9702           gp_XY     op = ips1.first - halfPlns[ iHP1 ]._pos;
9703           double param = op * halfPlns[ iHP1 ]._dir;
9704           ipsByParam.insert( make_pair( param, & ips1 ));
9705         }
9706       }
9707       // look for two neighboring NOT_OUT points
9708       nbNotOut = 0;
9709       map< double, TIntPntState* >::iterator u2ips = ipsByParam.begin();
9710       for ( ; u2ips != ipsByParam.end(); ++u2ips )
9711       {
9712         TIntPntState & ips1 = *(u2ips->second);
9713         if ( ips1.second == NOT_OUT )
9714           segEnds[ bool( nbNotOut++ ) ] = & ips1.first;
9715         else if ( nbNotOut >= 2 )
9716           break;
9717         else
9718           nbNotOut = 0;
9719       }
9720     }
9721
9722     if ( nbNotOut >= 2 )
9723     {
9724       double len = ( *segEnds[0] - *segEnds[1] ).Modulus();
9725       sumLen += len;
9726
9727       newPos2D += 0.5 * len * ( *segEnds[0] + *segEnds[1] );
9728     }
9729   }
9730
9731   if ( sumLen > 0 )
9732   {
9733     newPos2D /= sumLen;
9734     newPos = center + xAxis * newPos2D.X() + yAxis * newPos2D.Y();
9735   }
9736   else
9737   {
9738     newPos = center;
9739   }
9740
9741   return newPos;
9742 }
9743 #else // OLD_NEF_POLYGON
9744 { ////////////////////////////////// NEW
9745   gp_XYZ newPos(0,0,0);
9746
9747   // get a plane to search a solution on
9748
9749   size_t i;
9750   gp_XYZ center(0,0,0);
9751   for ( i = 0; i < _simplices.size(); ++i )
9752     center += SMESH_TNodeXYZ( _simplices[i]._nPrev );
9753   center /= _simplices.size();
9754
9755   vector< gp_XYZ > vecs( _simplices.size() + 1 );
9756   for ( i = 0; i < _simplices.size(); ++i )
9757     vecs[i] = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9758   vecs.back() = vecs[0];
9759
9760   const double tol = numeric_limits<double>::min();
9761   gp_XYZ zAxis(0,0,0);
9762   for ( i = 0; i < _simplices.size(); ++i )
9763   {
9764     gp_XYZ cross = vecs[i] ^ vecs[i+1];
9765     try {
9766       cross.Normalize();
9767       if ( cross * zAxis < tol )
9768         zAxis += cross.Reversed();
9769       else
9770         zAxis += cross;
9771     }
9772     catch (Standard_Failure) { // if |cross| == 0.
9773     }
9774   }
9775
9776   gp_XYZ yAxis;
9777   for ( i = 0; i < _simplices.size(); ++i )
9778   {
9779     yAxis = vecs[i];
9780     if ( yAxis.SquareModulus() > tol )
9781       break;
9782   }
9783   gp_XYZ xAxis = yAxis ^ zAxis;
9784   // SMESH_TNodeXYZ p0( _simplices[0]._nPrev );
9785   // const double tol = 1e-6 * ( p0.Distance( _simplices[1]._nPrev ) +
9786   //                             p0.Distance( _simplices[2]._nPrev ));
9787   // gp_XYZ center = smoothLaplacian();
9788   // gp_XYZ xAxis, yAxis, zAxis;
9789   // for ( i = 0; i < _simplices.size(); ++i )
9790   // {
9791   //   xAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9792   //   if ( xAxis.SquareModulus() > tol*tol )
9793   //     break;
9794   // }
9795   // for ( i = 1; i < _simplices.size(); ++i )
9796   // {
9797   //   yAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9798   //   zAxis = xAxis ^ yAxis;
9799   //   if ( zAxis.SquareModulus() > tol*tol )
9800   //     break;
9801   // }
9802   // if ( i == _simplices.size() ) return newPos;
9803
9804   yAxis = zAxis ^ xAxis;
9805   xAxis /= xAxis.Modulus();
9806   yAxis /= yAxis.Modulus();
9807
9808   // get half-planes of _simplices
9809
9810   vector< _halfPlane > halfPlns( _simplices.size() );
9811   int nbHP = 0;
9812   for ( size_t i = 0; i < _simplices.size(); ++i )
9813   {
9814     const gp_XYZ& OP1 = vecs[ i   ];
9815     const gp_XYZ& OP2 = vecs[ i+1 ];
9816     gp_XY  p1( OP1 * xAxis, OP1 * yAxis );
9817     gp_XY  p2( OP2 * xAxis, OP2 * yAxis );
9818     gp_XY  vec12 = p2 - p1;
9819     double dist12 = vec12.Modulus();
9820     if ( dist12 < tol )
9821       continue;
9822     vec12 /= dist12;
9823     halfPlns[ nbHP ]._pos = p1;
9824     halfPlns[ nbHP ]._dir = vec12;
9825     halfPlns[ nbHP ]._inNorm.SetCoord( -vec12.Y(), vec12.X() );
9826     ++nbHP;
9827   }
9828
9829   // intersect boundaries of half-planes, define state of intersection points
9830   // in relation to all half-planes and calculate internal point of a 2D polygon
9831
9832   double sumLen = 0;
9833   gp_XY newPos2D (0,0);
9834
9835   enum { UNDEF = -1, NOT_OUT, IS_OUT, NO_INT };
9836   typedef std::pair< gp_XY, int > TIntPntState; // coord and isOut state
9837   TIntPntState undefIPS( gp_XY(1e100,1e100), UNDEF );
9838
9839   vector< vector< TIntPntState > > allIntPnts( nbHP );
9840   for ( int iHP1 = 0; iHP1 < nbHP; ++iHP1 )
9841   {
9842     vector< TIntPntState > & intPnts1 = allIntPnts[ iHP1 ];
9843     if ( intPnts1.empty() ) intPnts1.resize( nbHP, undefIPS );
9844
9845     int iPrev = SMESH_MesherHelper::WrapIndex( iHP1 - 1, nbHP );
9846     int iNext = SMESH_MesherHelper::WrapIndex( iHP1 + 1, nbHP );
9847
9848     int nbNotOut = 0;
9849     const gp_XY* segEnds[2] = { 0, 0 }; // NOT_OUT points
9850
9851     for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9852     {
9853       if ( iHP1 == iHP2 ) continue;
9854
9855       TIntPntState & ips1 = intPnts1[ iHP2 ];
9856       if ( ips1.second == UNDEF )
9857       {
9858         // find an intersection point of boundaries of iHP1 and iHP2
9859
9860         if ( iHP2 == iPrev ) // intersection with neighbors is known
9861           ips1.first = halfPlns[ iHP1 ]._pos;
9862         else if ( iHP2 == iNext )
9863           ips1.first = halfPlns[ iHP2 ]._pos;
9864         else if ( !halfPlns[ iHP1 ].FindIntersection( halfPlns[ iHP2 ], ips1.first ))
9865           ips1.second = NO_INT;
9866
9867         // classify the found intersection point
9868         if ( ips1.second != NO_INT )
9869         {
9870           ips1.second = NOT_OUT;
9871           for ( int i = 0; i < nbHP && ips1.second == NOT_OUT; ++i )
9872             if ( i != iHP1 && i != iHP2 &&
9873                  halfPlns[ i ].IsOut( ips1.first, tol ))
9874               ips1.second = IS_OUT;
9875         }
9876         vector< TIntPntState > & intPnts2 = allIntPnts[ iHP2 ];
9877         if ( intPnts2.empty() ) intPnts2.resize( nbHP, undefIPS );
9878         TIntPntState & ips2 = intPnts2[ iHP1 ];
9879         ips2 = ips1;
9880       }
9881       if ( ips1.second == NOT_OUT )
9882       {
9883         ++nbNotOut;
9884         segEnds[ bool(segEnds[0]) ] = & ips1.first;
9885       }
9886     }
9887
9888     // find a NOT_OUT segment of boundary which is located between
9889     // two NOT_OUT int points
9890
9891     if ( nbNotOut < 2 )
9892       continue; // no such a segment
9893
9894     if ( nbNotOut > 2 )
9895     {
9896       // sort points along the boundary
9897       map< double, TIntPntState* > ipsByParam;
9898       for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9899       {
9900         TIntPntState & ips1 = intPnts1[ iHP2 ];
9901         if ( ips1.second != NO_INT )
9902         {
9903           gp_XY     op = ips1.first - halfPlns[ iHP1 ]._pos;
9904           double param = op * halfPlns[ iHP1 ]._dir;
9905           ipsByParam.insert( make_pair( param, & ips1 ));
9906         }
9907       }
9908       // look for two neighboring NOT_OUT points
9909       nbNotOut = 0;
9910       map< double, TIntPntState* >::iterator u2ips = ipsByParam.begin();
9911       for ( ; u2ips != ipsByParam.end(); ++u2ips )
9912       {
9913         TIntPntState & ips1 = *(u2ips->second);
9914         if ( ips1.second == NOT_OUT )
9915           segEnds[ bool( nbNotOut++ ) ] = & ips1.first;
9916         else if ( nbNotOut >= 2 )
9917           break;
9918         else
9919           nbNotOut = 0;
9920       }
9921     }
9922
9923     if ( nbNotOut >= 2 )
9924     {
9925       double len = ( *segEnds[0] - *segEnds[1] ).Modulus();
9926       sumLen += len;
9927
9928       newPos2D += 0.5 * len * ( *segEnds[0] + *segEnds[1] );
9929     }
9930   }
9931
9932   if ( sumLen > 0 )
9933   {
9934     newPos2D /= sumLen;
9935     newPos = center + xAxis * newPos2D.X() + yAxis * newPos2D.Y();
9936   }
9937   else
9938   {
9939     newPos = center;
9940   }
9941
9942   return newPos;
9943 }
9944 #endif // OLD_NEF_POLYGON
9945
9946 //================================================================================
9947 /*!
9948  * \brief Add a new segment to _LayerEdge during inflation
9949  */
9950 //================================================================================
9951
9952 void _LayerEdge::SetNewLength( double len, _EdgesOnShape& eos, SMESH_MesherHelper& helper )
9953 {
9954   if ( Is( BLOCKED ))
9955     return;
9956
9957   if ( len > _maxLen )
9958   {
9959     len = _maxLen;
9960     Block( eos.GetData() );
9961   }
9962   const double lenDelta = len - _len;
9963   // if ( lenDelta < 0 )
9964   //   return;
9965   if ( lenDelta < len * 1e-3  )
9966   {
9967     Block( eos.GetData() );
9968     return;
9969   }
9970
9971   SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() );
9972   gp_XYZ oldXYZ = SMESH_TNodeXYZ( n );
9973   gp_XYZ newXYZ;
9974   if ( eos._hyp.IsOffsetMethod() )
9975   {
9976     newXYZ = oldXYZ;
9977     gp_Vec faceNorm;
9978     SMDS_ElemIteratorPtr faceIt = _nodes[0]->GetInverseElementIterator( SMDSAbs_Face );
9979     while ( faceIt->more() )
9980     {
9981       const SMDS_MeshElement* face = faceIt->next();
9982       if ( !eos.GetNormal( face, faceNorm ))
9983         continue;
9984
9985       // translate plane of a face
9986       gp_XYZ baryCenter = oldXYZ + faceNorm.XYZ() * lenDelta;
9987
9988       // find point of intersection of the face plane located at baryCenter
9989       // and _normal located at newXYZ
9990       double d   = -( faceNorm.XYZ() * baryCenter ); // d of plane equation ax+by+cz+d=0
9991       double dot =  ( faceNorm.XYZ() * _normal );
9992       if ( dot < std::numeric_limits<double>::min() )
9993         dot = lenDelta * 1e-3;
9994       double step = -( faceNorm.XYZ() * newXYZ + d ) / dot;
9995       newXYZ += step * _normal;
9996     }
9997     _lenFactor = _normal * ( newXYZ - oldXYZ ) / lenDelta; // _lenFactor is used in InvalidateStep()
9998   }
9999   else
10000   {
10001     newXYZ = oldXYZ + _normal * lenDelta * _lenFactor;
10002   }
10003
10004   n->setXYZ( newXYZ.X(), newXYZ.Y(), newXYZ.Z() );
10005   _pos.push_back( newXYZ );
10006
10007   if ( !eos._sWOL.IsNull() )
10008     if ( !UpdatePositionOnSWOL( n, 2*lenDelta, eos, helper ))
10009     {
10010       n->setXYZ( oldXYZ.X(), oldXYZ.Y(), oldXYZ.Z() );
10011       _pos.pop_back();
10012       Block( eos.GetData() );
10013       return;
10014     }
10015
10016   _len = len;
10017
10018   // notify _neibors
10019   if ( eos.ShapeType() != TopAbs_FACE )
10020   {
10021     for ( size_t i = 0; i < _neibors.size(); ++i )
10022       //if (  _len > _neibors[i]->GetSmooLen() )
10023       _neibors[i]->Set( MOVED );
10024
10025     Set( MOVED );
10026   }
10027   dumpMove( n ); //debug
10028 }
10029
10030
10031 //================================================================================
10032 /*!
10033  * \brief Update last position on SWOL by projecting node on SWOL
10034 */
10035 //================================================================================
10036
10037 bool _LayerEdge::UpdatePositionOnSWOL( SMDS_MeshNode*      n,
10038                                        double              tol,
10039                                        _EdgesOnShape&      eos,
10040                                        SMESH_MesherHelper& helper )
10041 {
10042   double distXYZ[4];
10043   bool uvOK = false;
10044   if ( eos.SWOLType() == TopAbs_EDGE )
10045   {
10046     double u = Precision::Infinite(); // to force projection w/o distance check
10047     uvOK = helper.CheckNodeU( TopoDS::Edge( eos._sWOL ), n, u, tol, /*force=*/true, distXYZ );
10048     _pos.back().SetCoord( u, 0, 0 );
10049     if ( _nodes.size() > 1 && uvOK )
10050     {
10051       SMDS_EdgePositionPtr pos = n->GetPosition();
10052       pos->SetUParameter( u );
10053     }
10054   }
10055   else //  TopAbs_FACE
10056   {
10057     gp_XY uv( Precision::Infinite(), 0 );
10058     uvOK = helper.CheckNodeUV( TopoDS::Face( eos._sWOL ), n, uv, tol, /*force=*/true, distXYZ );
10059     _pos.back().SetCoord( uv.X(), uv.Y(), 0 );
10060     if ( _nodes.size() > 1 && uvOK )
10061     {
10062       SMDS_FacePositionPtr pos = n->GetPosition();
10063       pos->SetUParameter( uv.X() );
10064       pos->SetVParameter( uv.Y() );
10065     }
10066   }
10067   if ( uvOK )
10068   {
10069     n->setXYZ( distXYZ[1], distXYZ[2], distXYZ[3]);
10070   }
10071   return uvOK;
10072 }
10073
10074 //================================================================================
10075 /*!
10076  * \brief Set BLOCKED flag and propagate limited _maxLen to _neibors
10077  */
10078 //================================================================================
10079
10080 void _LayerEdge::Block( _SolidData& data )
10081 {
10082   //if ( Is( BLOCKED )) return;
10083   Set( BLOCKED );
10084
10085   SMESH_Comment msg( "#BLOCK shape=");
10086   msg << data.GetShapeEdges( this )->_shapeID
10087       << ", nodes " << _nodes[0]->GetID() << ", " << _nodes.back()->GetID();
10088   dumpCmd( msg + " -- BEGIN");
10089
10090   SetMaxLen( _len );
10091   std::queue<_LayerEdge*> queue;
10092   queue.push( this );
10093
10094   gp_Pnt pSrc, pTgt, pSrcN, pTgtN;
10095   while ( !queue.empty() )
10096   {
10097     _LayerEdge* edge = queue.front(); queue.pop();
10098     pSrc = SMESH_TNodeXYZ( edge->_nodes[0] );
10099     pTgt = SMESH_TNodeXYZ( edge->_nodes.back() );
10100     for ( size_t iN = 0; iN < edge->_neibors.size(); ++iN )
10101     {
10102       _LayerEdge* neibor = edge->_neibors[iN];
10103       if ( neibor->_maxLen < edge->_maxLen * 1.01 )
10104         continue;
10105       pSrcN = SMESH_TNodeXYZ( neibor->_nodes[0] );
10106       pTgtN = SMESH_TNodeXYZ( neibor->_nodes.back() );
10107       double minDist = pSrc.SquareDistance( pSrcN );
10108       minDist   = Min( pTgt.SquareDistance( pTgtN ), minDist );
10109       minDist   = Min( pSrc.SquareDistance( pTgtN ), minDist );
10110       minDist   = Min( pTgt.SquareDistance( pSrcN ), minDist );
10111       double newMaxLen = edge->_maxLen + 0.5 * Sqrt( minDist );
10112       //if ( edge->_nodes[0]->getshapeId() == neibor->_nodes[0]->getshapeId() ) viscous_layers_00/A3
10113       {
10114         //newMaxLen *= edge->_lenFactor / neibor->_lenFactor;
10115         // newMaxLen *= Min( edge->_lenFactor / neibor->_lenFactor,
10116         //                   neibor->_lenFactor / edge->_lenFactor );
10117       }
10118       if ( neibor->_maxLen > newMaxLen )
10119       {
10120         neibor->SetMaxLen( newMaxLen );
10121         if ( neibor->_maxLen < neibor->_len )
10122         {
10123           _EdgesOnShape* eos = data.GetShapeEdges( neibor );
10124           int       lastStep = neibor->Is( BLOCKED ) ? 1 : 0;
10125           while ( neibor->_len > neibor->_maxLen &&
10126                   neibor->NbSteps() > lastStep )
10127             neibor->InvalidateStep( neibor->NbSteps(), *eos, /*restoreLength=*/true );
10128           neibor->SetNewLength( neibor->_maxLen, *eos, data.GetHelper() );
10129           //neibor->Block( data );
10130         }
10131         queue.push( neibor );
10132       }
10133     }
10134   }
10135   dumpCmd( msg + " -- END");
10136 }
10137
10138 //================================================================================
10139 /*!
10140  * \brief Remove last inflation step
10141  */
10142 //================================================================================
10143
10144 void _LayerEdge::InvalidateStep( size_t curStep, const _EdgesOnShape& eos, bool restoreLength )
10145 {
10146   if ( _pos.size() > curStep && _nodes.size() > 1 )
10147   {
10148     _pos.resize( curStep );
10149
10150     gp_Pnt      nXYZ = _pos.back();
10151     SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() );
10152     SMESH_TNodeXYZ curXYZ( n );
10153     if ( !eos._sWOL.IsNull() )
10154     {
10155       TopLoc_Location loc;
10156       if ( eos.SWOLType() == TopAbs_EDGE )
10157       {
10158         SMDS_EdgePositionPtr pos = n->GetPosition();
10159         pos->SetUParameter( nXYZ.X() );
10160         double f,l;
10161         Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( eos._sWOL ), loc, f,l);
10162         nXYZ = curve->Value( nXYZ.X() ).Transformed( loc );
10163       }
10164       else
10165       {
10166         SMDS_FacePositionPtr pos = n->GetPosition();
10167         pos->SetUParameter( nXYZ.X() );
10168         pos->SetVParameter( nXYZ.Y() );
10169         Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face(eos._sWOL), loc );
10170         nXYZ = surface->Value( nXYZ.X(), nXYZ.Y() ).Transformed( loc );
10171       }
10172     }
10173     n->setXYZ( nXYZ.X(), nXYZ.Y(), nXYZ.Z() );
10174     dumpMove( n );
10175
10176     if ( restoreLength )
10177     {
10178       if ( NbSteps() == 0 )
10179         _len = 0.;
10180       else if ( IsOnFace() && Is( MOVED ))
10181         _len = ( nXYZ.XYZ() - SMESH_NodeXYZ( _nodes[0] )) * _normal;
10182       else
10183         _len -= ( nXYZ.XYZ() - curXYZ ).Modulus() / _lenFactor;
10184     }
10185   }
10186   return;
10187 }
10188
10189 //================================================================================
10190 /*!
10191  * \brief Return index of a _pos distant from _normal
10192  */
10193 //================================================================================
10194
10195 int _LayerEdge::GetSmoothedPos( const double tol )
10196 {
10197   int iSmoothed = 0;
10198   for ( size_t i = 1; i < _pos.size() && !iSmoothed; ++i )
10199   {
10200     double normDist = ( _pos[i] - _pos[0] ).Crossed( _normal ).SquareModulus();
10201     if ( normDist > tol * tol )
10202       iSmoothed = i;
10203   }
10204   return iSmoothed;
10205 }
10206
10207 //================================================================================
10208 /*!
10209  * \brief Smooth a path formed by _pos of a _LayerEdge smoothed on FACE
10210  */
10211 //================================================================================
10212
10213 void _LayerEdge::SmoothPos( const vector< double >& segLen, const double tol )
10214 {
10215   if ( /*Is( NORMAL_UPDATED ) ||*/ _pos.size() <= 2 )
10216     return;
10217
10218   // find the 1st smoothed _pos
10219   int iSmoothed = GetSmoothedPos( tol );
10220   if ( !iSmoothed ) return;
10221
10222   gp_XYZ normal = _normal;
10223   if ( Is( NORMAL_UPDATED ))
10224   {
10225     double minDot = 1;
10226     for ( size_t i = 0; i < _neibors.size(); ++i )
10227     {
10228       if ( _neibors[i]->IsOnFace() )
10229       {
10230         double dot = _normal * _neibors[i]->_normal;
10231         if ( dot < minDot )
10232         {
10233           normal = _neibors[i]->_normal;
10234           minDot = dot;
10235         }
10236       }
10237     }
10238     if ( minDot == 1. )
10239       for ( size_t i = 1; i < _pos.size(); ++i )
10240       {
10241         normal = _pos[i] - _pos[0];
10242         double size = normal.Modulus();
10243         if ( size > RealSmall() )
10244         {
10245           normal /= size;
10246           break;
10247         }
10248       }
10249   }
10250   const double r = 0.2;
10251   for ( int iter = 0; iter < 50; ++iter )
10252   {
10253     double minDot = 1;
10254     for ( size_t i = Max( 1, iSmoothed-1-iter ); i < _pos.size()-1; ++i )
10255     {
10256       gp_XYZ midPos = 0.5 * ( _pos[i-1] + _pos[i+1] );
10257       gp_XYZ newPos = ( 1-r ) * midPos + r * _pos[i];
10258       _pos[i] = newPos;
10259       double midLen = 0.5 * ( segLen[i-1] + segLen[i+1] );
10260       double newLen = ( 1-r ) * midLen + r * segLen[i];
10261       const_cast< double& >( segLen[i] ) = newLen;
10262       // check angle between normal and (_pos[i+1], _pos[i] )
10263       gp_XYZ posDir = _pos[i+1] - _pos[i];
10264       double size   = posDir.SquareModulus();
10265       if ( size > RealSmall() )
10266         minDot = Min( minDot, ( normal * posDir ) * ( normal * posDir ) / size );
10267     }
10268     if ( minDot > 0.5 * 0.5 )
10269       break;
10270   }
10271   return;
10272 }
10273
10274 //================================================================================
10275 /*!
10276  * \brief Print flags
10277  */
10278 //================================================================================
10279
10280 std::string _LayerEdge::DumpFlags() const
10281 {
10282   SMESH_Comment dump;
10283   for ( int flag = 1; flag < 0x1000000; flag *= 2 )
10284     if ( _flags & flag )
10285     {
10286       EFlags f = (EFlags) flag;
10287       switch ( f ) {
10288       case TO_SMOOTH:       dump << "TO_SMOOTH";       break;
10289       case MOVED:           dump << "MOVED";           break;
10290       case SMOOTHED:        dump << "SMOOTHED";        break;
10291       case DIFFICULT:       dump << "DIFFICULT";       break;
10292       case ON_CONCAVE_FACE: dump << "ON_CONCAVE_FACE"; break;
10293       case BLOCKED:         dump << "BLOCKED";         break;
10294       case INTERSECTED:     dump << "INTERSECTED";     break;
10295       case NORMAL_UPDATED:  dump << "NORMAL_UPDATED";  break;
10296       case UPD_NORMAL_CONV: dump << "UPD_NORMAL_CONV"; break;
10297       case MARKED:          dump << "MARKED";          break;
10298       case MULTI_NORMAL:    dump << "MULTI_NORMAL";    break;
10299       case NEAR_BOUNDARY:   dump << "NEAR_BOUNDARY";   break;
10300       case SMOOTHED_C1:     dump << "SMOOTHED_C1";     break;
10301       case DISTORTED:       dump << "DISTORTED";       break;
10302       case RISKY_SWOL:      dump << "RISKY_SWOL";      break;
10303       case SHRUNK:          dump << "SHRUNK";          break;
10304       case UNUSED_FLAG:     dump << "UNUSED_FLAG";     break;
10305       }
10306       dump << " ";
10307     }
10308   cout << dump << endl;
10309   return dump;
10310 }
10311
10312
10313 //================================================================================
10314 /*!
10315  * \brief Create layers of prisms
10316  */
10317 //================================================================================
10318
10319 bool _ViscousBuilder::refine(_SolidData& data)
10320 {
10321   SMESH_MesherHelper& helper = data.GetHelper();
10322   helper.SetElementsOnShape(false);
10323
10324   Handle(Geom_Curve) curve;
10325   Handle(ShapeAnalysis_Surface) surface;
10326   TopoDS_Edge geomEdge;
10327   TopoDS_Face geomFace;
10328   TopLoc_Location loc;
10329   double f,l, u = 0;
10330   gp_XY uv;
10331   vector< gp_XYZ > pos3D;
10332   bool isOnEdge, isTooConvexFace = false;
10333   TGeomID prevBaseId = -1;
10334   TNode2Edge* n2eMap = 0;
10335   TNode2Edge::iterator n2e;
10336
10337   // Create intermediate nodes on each _LayerEdge
10338
10339   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
10340   {
10341     _EdgesOnShape& eos = data._edgesOnShape[iS];
10342     if ( eos._edges.empty() ) continue;
10343
10344     if ( eos._edges[0]->_nodes.size() < 2 )
10345       continue; // on _noShrinkShapes
10346
10347     // get data of a shrink shape
10348     isOnEdge = false;
10349     geomEdge.Nullify(); geomFace.Nullify();
10350     curve.Nullify(); surface.Nullify();
10351     if ( !eos._sWOL.IsNull() )
10352     {
10353       isOnEdge = ( eos.SWOLType() == TopAbs_EDGE );
10354       if ( isOnEdge )
10355       {
10356         geomEdge = TopoDS::Edge( eos._sWOL );
10357         curve    = BRep_Tool::Curve( geomEdge, loc, f,l);
10358       }
10359       else
10360       {
10361         geomFace = TopoDS::Face( eos._sWOL );
10362         surface  = helper.GetSurface( geomFace );
10363       }
10364     }
10365     else if ( eos.ShapeType() == TopAbs_FACE && eos._toSmooth )
10366     {
10367       geomFace = TopoDS::Face( eos._shape );
10368       surface  = helper.GetSurface( geomFace );
10369       // propagate _toSmooth back to _eosC1, which was unset in findShapesToSmooth()
10370       for ( size_t i = 0; i < eos._eosC1.size(); ++i )
10371         eos._eosC1[ i ]->_toSmooth = true;
10372
10373       isTooConvexFace = false;
10374       if ( _ConvexFace* cf = data.GetConvexFace( eos._shapeID ))
10375         isTooConvexFace = cf->_isTooCurved;
10376     }
10377
10378     vector< double > segLen;
10379     for ( size_t i = 0; i < eos._edges.size(); ++i )
10380     {
10381       _LayerEdge& edge = *eos._edges[i];
10382       if ( edge._pos.size() < 2 )
10383         continue;
10384
10385       // get accumulated length of segments
10386       segLen.resize( edge._pos.size() );
10387       segLen[0] = 0.0;
10388       if ( eos._sWOL.IsNull() )
10389       {
10390         bool useNormal = true;
10391         bool    usePos = false;
10392         bool  smoothed = false;
10393         double   preci = 0.1 * edge._len;
10394         if ( eos._toSmooth && edge._pos.size() > 2 )
10395         {
10396           smoothed = edge.GetSmoothedPos( preci );
10397         }
10398         if ( smoothed )
10399         {
10400           if ( !surface.IsNull() && !isTooConvexFace ) // edge smoothed on FACE
10401           {
10402             useNormal = usePos = false;
10403             gp_Pnt2d uv = helper.GetNodeUV( geomFace, edge._nodes[0] );
10404             for ( size_t j = 1; j < edge._pos.size() && !useNormal; ++j )
10405             {
10406               uv = surface->NextValueOfUV( uv, edge._pos[j], preci );
10407               if ( surface->Gap() < 2. * edge._len )
10408                 segLen[j] = surface->Gap();
10409               else
10410                 useNormal = true;
10411             }
10412           }
10413         }
10414         else if ( !edge.Is( _LayerEdge::NORMAL_UPDATED ))
10415         {
10416 #ifndef __NODES_AT_POS
10417           useNormal = usePos = false;
10418           edge._pos[1] = edge._pos.back();
10419           edge._pos.resize( 2 );
10420           segLen.resize( 2 );
10421           segLen[ 1 ] = edge._len;
10422 #endif
10423         }
10424         if ( useNormal && edge.Is( _LayerEdge::NORMAL_UPDATED ))
10425         {
10426           useNormal = usePos = false;
10427           _LayerEdge tmpEdge; // get original _normal
10428           tmpEdge._nodes.push_back( edge._nodes[0] );
10429           if ( !setEdgeData( tmpEdge, eos, helper, data ))
10430             usePos = true;
10431           else
10432             for ( size_t j = 1; j < edge._pos.size(); ++j )
10433               segLen[j] = ( edge._pos[j] - edge._pos[0] ) * tmpEdge._normal;
10434         }
10435         if ( useNormal )
10436         {
10437           for ( size_t j = 1; j < edge._pos.size(); ++j )
10438             segLen[j] = ( edge._pos[j] - edge._pos[0] ) * edge._normal;
10439         }
10440         if ( usePos )
10441         {
10442           for ( size_t j = 1; j < edge._pos.size(); ++j )
10443             segLen[j] = segLen[j-1] + ( edge._pos[j-1] - edge._pos[j] ).Modulus();
10444         }
10445         else
10446         {
10447           bool swapped = ( edge._pos.size() > 2 );
10448           while ( swapped )
10449           {
10450             swapped = false;
10451             for ( size_t j = 1; j < edge._pos.size()-1; ++j )
10452               if ( segLen[j] > segLen.back() )
10453               {
10454                 segLen.erase( segLen.begin() + j );
10455                 edge._pos.erase( edge._pos.begin() + j );
10456                 --j;
10457               }
10458               else if ( segLen[j] < segLen[j-1] )
10459               {
10460                 std::swap( segLen[j], segLen[j-1] );
10461                 std::swap( edge._pos[j], edge._pos[j-1] );
10462                 swapped = true;
10463               }
10464           }
10465         }
10466         // smooth a path formed by edge._pos
10467 #ifndef __NODES_AT_POS
10468         if (( smoothed ) /*&&
10469             ( eos.ShapeType() == TopAbs_FACE || edge.Is( _LayerEdge::SMOOTHED_C1 ))*/)
10470           edge.SmoothPos( segLen, preci );
10471 #endif
10472       }
10473       else if ( eos._isRegularSWOL ) // usual SWOL
10474       {
10475         if ( edge.Is( _LayerEdge::SMOOTHED ))
10476         {
10477           SMESH_NodeXYZ p0( edge._nodes[0] );
10478           for ( size_t j = 1; j < edge._pos.size(); ++j )
10479           {
10480             gp_XYZ pj = surface->Value( edge._pos[j].X(), edge._pos[j].Y() ).XYZ();
10481             segLen[j] = ( pj - p0 ) * edge._normal;
10482           }
10483         }
10484         else
10485         {
10486           for ( size_t j = 1; j < edge._pos.size(); ++j )
10487             segLen[j] = segLen[j-1] + (edge._pos[j-1] - edge._pos[j] ).Modulus();
10488         }
10489       }
10490       else // SWOL is surface with singularities or irregularly parametrized curve
10491       {
10492         pos3D.resize( edge._pos.size() );
10493
10494         if ( !surface.IsNull() )
10495           for ( size_t j = 0; j < edge._pos.size(); ++j )
10496             pos3D[j] = surface->Value( edge._pos[j].X(), edge._pos[j].Y() ).XYZ();
10497         else if ( !curve.IsNull() )
10498           for ( size_t j = 0; j < edge._pos.size(); ++j )
10499             pos3D[j] = curve->Value( edge._pos[j].X() ).XYZ();
10500
10501         for ( size_t j = 1; j < edge._pos.size(); ++j )
10502           segLen[j] = segLen[j-1] + ( pos3D[j-1] - pos3D[j] ).Modulus();
10503       }
10504
10505       // allocate memory for new nodes if it is not yet refined
10506       const SMDS_MeshNode* tgtNode = edge._nodes.back();
10507       if ( edge._nodes.size() == 2 )
10508       {
10509 #ifdef __NODES_AT_POS
10510         int nbNodes = edge._pos.size();
10511 #else
10512         int nbNodes = eos._hyp.GetNumberLayers() + 1;
10513 #endif
10514         edge._nodes.resize( nbNodes, 0 );
10515         edge._nodes[1] = 0;
10516         edge._nodes.back() = tgtNode;
10517       }
10518       // restore shapePos of the last node by already treated _LayerEdge of another _SolidData
10519       const TGeomID baseShapeId = edge._nodes[0]->getshapeId();
10520       if ( baseShapeId != prevBaseId )
10521       {
10522         map< TGeomID, TNode2Edge* >::iterator s2ne = data._s2neMap.find( baseShapeId );
10523         n2eMap = ( s2ne == data._s2neMap.end() ) ? 0 : s2ne->second;
10524         prevBaseId = baseShapeId;
10525       }
10526       _LayerEdge* edgeOnSameNode = 0;
10527       bool        useExistingPos = false;
10528       if ( n2eMap && (( n2e = n2eMap->find( edge._nodes[0] )) != n2eMap->end() ))
10529       {
10530         edgeOnSameNode = n2e->second;
10531         useExistingPos = ( edgeOnSameNode->_len < edge._len ||
10532                            segLen[0] == segLen.back() ); // too short inflation step (bos #20643)
10533         const gp_XYZ& otherTgtPos = edgeOnSameNode->_pos.back();
10534         SMDS_PositionPtr  lastPos = tgtNode->GetPosition();
10535         if ( isOnEdge )
10536         {
10537           SMDS_EdgePositionPtr epos = lastPos;
10538           epos->SetUParameter( otherTgtPos.X() );
10539         }
10540         else
10541         {
10542           SMDS_FacePositionPtr fpos = lastPos;
10543           fpos->SetUParameter( otherTgtPos.X() );
10544           fpos->SetVParameter( otherTgtPos.Y() );
10545         }
10546       }
10547
10548       // create intermediate nodes
10549       const double      h0 = eos._hyp.Get1stLayerThickness( segLen.back() );
10550       const double zeroLen = std::numeric_limits<double>::min();
10551       double hSum = 0, hi = h0/eos._hyp.GetStretchFactor();
10552       size_t iSeg = 1;
10553       for ( size_t iStep = 1; iStep < edge._nodes.size(); ++iStep )
10554       {
10555         // compute an intermediate position
10556         hi *= eos._hyp.GetStretchFactor();
10557         hSum += hi;
10558         while ( hSum > segLen[iSeg] && iSeg < segLen.size()-1 )
10559           ++iSeg;
10560         int iPrevSeg = iSeg-1;
10561         while ( fabs( segLen[iPrevSeg] - segLen[iSeg]) <= zeroLen && iPrevSeg > 0 )
10562           --iPrevSeg;
10563         double   r = ( segLen[iSeg] - hSum ) / ( segLen[iSeg] - segLen[iPrevSeg] );
10564         gp_Pnt pos = r * edge._pos[iPrevSeg] + (1-r) * edge._pos[iSeg];
10565 #ifdef __NODES_AT_POS
10566         pos = edge._pos[ iStep ];
10567 #endif
10568         SMDS_MeshNode*& node = const_cast< SMDS_MeshNode*& >( edge._nodes[ iStep ]);
10569         if ( !eos._sWOL.IsNull() )
10570         {
10571           // compute XYZ by parameters <pos>
10572           if ( isOnEdge )
10573           {
10574             u = pos.X();
10575             if ( !node )
10576               pos = curve->Value( u ).Transformed(loc);
10577           }
10578           else if ( eos._isRegularSWOL )
10579           {
10580             uv.SetCoord( pos.X(), pos.Y() );
10581             if ( !node )
10582               pos = surface->Value( pos.X(), pos.Y() );
10583           }
10584           else
10585           {
10586             uv.SetCoord( pos.X(), pos.Y() );
10587             gp_Pnt p = r * pos3D[ iPrevSeg ] + (1-r) * pos3D[ iSeg ];
10588             uv = surface->NextValueOfUV( uv, p, BRep_Tool::Tolerance( geomFace )).XY();
10589             if ( !node )
10590               pos = surface->Value( uv );
10591           }
10592         }
10593         // create or update the node
10594         if ( !node )
10595         {
10596           node = helper.AddNode( pos.X(), pos.Y(), pos.Z());
10597           if ( !eos._sWOL.IsNull() )
10598           {
10599             if ( isOnEdge )
10600               getMeshDS()->SetNodeOnEdge( node, geomEdge, u );
10601             else
10602               getMeshDS()->SetNodeOnFace( node, geomFace, uv.X(), uv.Y() );
10603           }
10604           else
10605           {
10606             getMeshDS()->SetNodeInVolume( node, helper.GetSubShapeID() );
10607           }
10608         }
10609         else
10610         {
10611           if ( !eos._sWOL.IsNull() )
10612           {
10613             // make average pos from new and current parameters
10614             if ( isOnEdge )
10615             {
10616               //u = 0.5 * ( u + helper.GetNodeU( geomEdge, node ));
10617               if ( useExistingPos )
10618                 u = helper.GetNodeU( geomEdge, node );
10619               pos = curve->Value( u ).Transformed(loc);
10620
10621               SMDS_EdgePositionPtr epos = node->GetPosition();
10622               epos->SetUParameter( u );
10623             }
10624             else
10625             {
10626               //uv = 0.5 * ( uv + helper.GetNodeUV( geomFace, node ));
10627               if ( useExistingPos )
10628                 uv = helper.GetNodeUV( geomFace, node );
10629               pos = surface->Value( uv );
10630
10631               SMDS_FacePositionPtr fpos = node->GetPosition();
10632               fpos->SetUParameter( uv.X() );
10633               fpos->SetVParameter( uv.Y() );
10634             }
10635           }
10636           node->setXYZ( pos.X(), pos.Y(), pos.Z() );
10637         }
10638       } // loop on edge._nodes
10639
10640       if ( !eos._sWOL.IsNull() ) // prepare for shrink()
10641       {
10642         if ( isOnEdge )
10643           edge._pos.back().SetCoord( u, 0,0);
10644         else
10645           edge._pos.back().SetCoord( uv.X(), uv.Y() ,0);
10646
10647         if ( edgeOnSameNode )
10648           edgeOnSameNode->_pos.back() = edge._pos.back();
10649       }
10650
10651     } // loop on eos._edges to create nodes
10652
10653
10654     if ( !getMeshDS()->IsEmbeddedMode() )
10655       // Log node movement
10656       for ( size_t i = 0; i < eos._edges.size(); ++i )
10657       {
10658         SMESH_TNodeXYZ p ( eos._edges[i]->_nodes.back() );
10659         getMeshDS()->MoveNode( p._node, p.X(), p.Y(), p.Z() );
10660       }
10661   }
10662
10663
10664   // Create volumes
10665
10666   helper.SetElementsOnShape(true);
10667
10668   vector< vector<const SMDS_MeshNode*>* > nnVec;
10669   set< vector<const SMDS_MeshNode*>* >    nnSet;
10670   set< int >                       degenEdgeInd;
10671   vector<const SMDS_MeshElement*>     degenVols;
10672
10673   TopExp_Explorer exp( data._solid, TopAbs_FACE );
10674   for ( ; exp.More(); exp.Next() )
10675   {
10676     const TGeomID faceID = getMeshDS()->ShapeToIndex( exp.Current() );
10677     if ( data._ignoreFaceIds.count( faceID ))
10678       continue;
10679     _EdgesOnShape*    eos = data.GetShapeEdges( faceID );
10680     SMDS_MeshGroup* group = StdMeshers_ViscousLayers::CreateGroup( eos->_hyp.GetGroupName(),
10681                                                                    *helper.GetMesh(),
10682                                                                    SMDSAbs_Volume );
10683     std::vector< const SMDS_MeshElement* > vols;
10684     const bool isReversedFace = data._reversedFaceIds.count( faceID );
10685     SMESHDS_SubMesh*    fSubM = getMeshDS()->MeshElements( exp.Current() );
10686     SMDS_ElemIteratorPtr  fIt = fSubM->GetElements();
10687     while ( fIt->more() )
10688     {
10689       const SMDS_MeshElement* face = fIt->next();
10690       const int            nbNodes = face->NbCornerNodes();
10691       nnVec.resize( nbNodes );
10692       nnSet.clear();
10693       degenEdgeInd.clear();
10694       size_t maxZ = 0, minZ = std::numeric_limits<size_t>::max();
10695       SMDS_NodeIteratorPtr nIt = face->nodeIterator();
10696       for ( int iN = 0; iN < nbNodes; ++iN )
10697       {
10698         const SMDS_MeshNode* n = nIt->next();
10699         _LayerEdge*       edge = data._n2eMap[ n ];
10700         const int i = isReversedFace ? nbNodes-1-iN : iN;
10701         nnVec[ i ] = & edge->_nodes;
10702         maxZ = std::max( maxZ, nnVec[ i ]->size() );
10703         minZ = std::min( minZ, nnVec[ i ]->size() );
10704
10705         if ( helper.HasDegeneratedEdges() )
10706           nnSet.insert( nnVec[ i ]);
10707       }
10708
10709       if ( maxZ == 0 )
10710         continue;
10711       if ( 0 < nnSet.size() && nnSet.size() < 3 )
10712         continue;
10713
10714       vols.clear();
10715       const SMDS_MeshElement* vol;
10716
10717       switch ( nbNodes )
10718       {
10719       case 3: // TRIA
10720       {
10721         // PENTA
10722         for ( size_t iZ = 1; iZ < minZ; ++iZ )
10723         {
10724           vol = helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1], (*nnVec[2])[iZ-1],
10725                                   (*nnVec[0])[iZ],   (*nnVec[1])[iZ],   (*nnVec[2])[iZ]);
10726           vols.push_back( vol );
10727         }
10728
10729         for ( size_t iZ = minZ; iZ < maxZ; ++iZ )
10730         {
10731           for ( int iN = 0; iN < nbNodes; ++iN )
10732             if ( nnVec[ iN ]->size() < iZ+1 )
10733               degenEdgeInd.insert( iN );
10734
10735           if ( degenEdgeInd.size() == 1 )  // PYRAM
10736           {
10737             int i2 = *degenEdgeInd.begin();
10738             int i0 = helper.WrapIndex( i2 - 1, nbNodes );
10739             int i1 = helper.WrapIndex( i2 + 1, nbNodes );
10740             vol = helper.AddVolume( (*nnVec[i0])[iZ-1], (*nnVec[i1])[iZ-1],
10741                                     (*nnVec[i1])[iZ  ], (*nnVec[i0])[iZ  ], (*nnVec[i2]).back());
10742             vols.push_back( vol );
10743           }
10744           else  // TETRA
10745           {
10746             int i3 = !degenEdgeInd.count(0) ? 0 : !degenEdgeInd.count(1) ? 1 : 2;
10747             vol = helper.AddVolume( (*nnVec[  0 ])[ i3 == 0 ? iZ-1 : nnVec[0]->size()-1 ],
10748                                     (*nnVec[  1 ])[ i3 == 1 ? iZ-1 : nnVec[1]->size()-1 ],
10749                                     (*nnVec[  2 ])[ i3 == 2 ? iZ-1 : nnVec[2]->size()-1 ],
10750                                     (*nnVec[ i3 ])[ iZ ]);
10751             vols.push_back( vol );
10752           }
10753         }
10754         break; // TRIA
10755       }
10756       case 4: // QUAD
10757       {
10758         // HEX
10759         for ( size_t iZ = 1; iZ < minZ; ++iZ )
10760         {
10761           vol = helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1],
10762                                   (*nnVec[2])[iZ-1], (*nnVec[3])[iZ-1],
10763                                   (*nnVec[0])[iZ],   (*nnVec[1])[iZ],
10764                                   (*nnVec[2])[iZ],   (*nnVec[3])[iZ]);
10765           vols.push_back( vol );
10766         }
10767
10768         for ( size_t iZ = minZ; iZ < maxZ; ++iZ )
10769         {
10770           for ( int iN = 0; iN < nbNodes; ++iN )
10771             if ( nnVec[ iN ]->size() < iZ+1 )
10772               degenEdgeInd.insert( iN );
10773
10774           switch ( degenEdgeInd.size() )
10775           {
10776           case 2: // PENTA
10777           {
10778             int i2 = *degenEdgeInd.begin();
10779             int i3 = *degenEdgeInd.rbegin();
10780             bool ok = ( i3 - i2 == 1 );
10781             if ( i2 == 0 && i3 == 3 ) { i2 = 3; i3 = 0; ok = true; }
10782             int i0 = helper.WrapIndex( i3 + 1, nbNodes );
10783             int i1 = helper.WrapIndex( i0 + 1, nbNodes );
10784
10785             vol = helper.AddVolume( nnVec[i3]->back(), (*nnVec[i0])[iZ], (*nnVec[i0])[iZ-1],
10786                                     nnVec[i2]->back(), (*nnVec[i1])[iZ], (*nnVec[i1])[iZ-1]);
10787             vols.push_back( vol );
10788             if ( !ok && vol )
10789               degenVols.push_back( vol );
10790           }
10791           break;
10792
10793           default: // degen HEX
10794           {
10795             vol = helper.AddVolume( nnVec[0]->size() > iZ-1 ? (*nnVec[0])[iZ-1] : nnVec[0]->back(),
10796                                     nnVec[1]->size() > iZ-1 ? (*nnVec[1])[iZ-1] : nnVec[1]->back(),
10797                                     nnVec[2]->size() > iZ-1 ? (*nnVec[2])[iZ-1] : nnVec[2]->back(),
10798                                     nnVec[3]->size() > iZ-1 ? (*nnVec[3])[iZ-1] : nnVec[3]->back(),
10799                                     nnVec[0]->size() > iZ   ? (*nnVec[0])[iZ]   : nnVec[0]->back(),
10800                                     nnVec[1]->size() > iZ   ? (*nnVec[1])[iZ]   : nnVec[1]->back(),
10801                                     nnVec[2]->size() > iZ   ? (*nnVec[2])[iZ]   : nnVec[2]->back(),
10802                                     nnVec[3]->size() > iZ   ? (*nnVec[3])[iZ]   : nnVec[3]->back());
10803             vols.push_back( vol );
10804             degenVols.push_back( vol );
10805           }
10806           }
10807         }
10808         break; // HEX
10809       }
10810       default:
10811         return error("Not supported type of element", data._index);
10812
10813       } // switch ( nbNodes )
10814
10815       if ( group )
10816         for ( size_t i = 0; i < vols.size(); ++i )
10817           group->Add( vols[ i ]);
10818
10819     } // while ( fIt->more() )
10820   } // loop on FACEs
10821
10822   if ( !degenVols.empty() )
10823   {
10824     SMESH_ComputeErrorPtr& err = _mesh->GetSubMesh( data._solid )->GetComputeError();
10825     if ( !err || err->IsOK() )
10826     {
10827       SMESH_BadInputElements* badElems =
10828         new SMESH_BadInputElements( getMeshDS(), COMPERR_WARNING, "Bad quality volumes created" );
10829       badElems->myBadElements.insert( badElems->myBadElements.end(),
10830                                       degenVols.begin(),degenVols.end() );
10831       err.reset( badElems );
10832     }
10833   }
10834
10835   return true;
10836 }
10837
10838 namespace VISCOUS_3D
10839 {
10840   struct ShrinkFace;
10841   //--------------------------------------------------------------------------------
10842   /*!
10843    * \brief Pair of periodic FACEs
10844    */
10845   struct PeriodicFaces
10846   {
10847     typedef StdMeshers_ProjectionUtils::TrsfFinder3D Trsf;
10848
10849     ShrinkFace*  _shriFace[2];
10850     TNodeNodeMap _nnMap;
10851     Trsf         _trsf;
10852
10853     PeriodicFaces( ShrinkFace* sf1, ShrinkFace* sf2 ): _shriFace{ sf1, sf2 } {}
10854     bool IncludeShrunk( const TopoDS_Face& face, const TopTools_MapOfShape& shrunkFaces ) const;
10855     bool MoveNodes( const TopoDS_Face& tgtFace );
10856     void Clear() { _nnMap.clear(); }
10857     bool IsEmpty() const { return _nnMap.empty(); }
10858   };
10859
10860   //--------------------------------------------------------------------------------
10861   /*!
10862    * \brief Shrink FACE data used to find periodic FACEs
10863    */
10864   struct ShrinkFace
10865   {
10866     // ................................................................................
10867     struct BndPart //!< part of FACE boundary, either shrink or no-shrink
10868     {
10869       bool                         _isShrink, _isReverse;
10870       int                          _nbSegments;
10871       AverageHyp*                  _hyp;
10872       std::vector< SMESH_NodeXYZ > _nodes;
10873       TopAbs_ShapeEnum             _vertSWOLType[2]; // shrink part includes VERTEXes
10874       AverageHyp*                  _vertHyp[2];
10875       double                       _edgeWOLLen[2]; // length of wol EDGE
10876       double                       _tol; // to compare _edgeWOLLen's
10877
10878       BndPart():
10879         _isShrink(0), _isReverse(0), _nbSegments(0), _hyp(0),
10880         _vertSWOLType{ TopAbs_WIRE, TopAbs_WIRE }, _vertHyp{ 0, 0 }, _edgeWOLLen{ 0., 0.}
10881       {}
10882
10883       bool IsEqualLengthEWOL( const BndPart& other ) const
10884       {
10885         return ( std::abs( _edgeWOLLen[0] - other._edgeWOLLen[0] ) < _tol &&
10886                  std::abs( _edgeWOLLen[1] - other._edgeWOLLen[1] ) < _tol );
10887       }
10888
10889       bool operator==( const BndPart& other ) const
10890       {
10891         return ( _isShrink       == other._isShrink &&
10892                  _nbSegments     == other._nbSegments &&
10893                  _nodes.size()   == other._nodes.size() &&
10894                  vertSWOLType1() == other.vertSWOLType1() &&
10895                  vertSWOLType2() == other.vertSWOLType2() &&
10896                  (( !_isShrink ) ||
10897                   ( *_hyp        == *other._hyp &&
10898                     vertHyp1()   == other.vertHyp1() &&
10899                     vertHyp2()   == other.vertHyp2() &&
10900                     IsEqualLengthEWOL( other )))
10901                  );
10902       }
10903       bool CanAppend( const BndPart& other )
10904       {
10905         return ( _isShrink  == other._isShrink  &&
10906                  (( !_isShrink ) ||
10907                   ( *_hyp        == *other._hyp &&
10908                     *_hyp        == vertHyp2()  &&
10909                     vertHyp2()   == other.vertHyp1() ))
10910                  );
10911       }
10912       void Append( const BndPart& other )
10913       {
10914         _nbSegments += other._nbSegments;
10915         bool hasCommonNode = ( _nodes.back()->GetID() == other._nodes.front()->GetID() );
10916         _nodes.insert( _nodes.end(), other._nodes.begin() + hasCommonNode, other._nodes.end() );
10917         _vertSWOLType[1] = other._vertSWOLType[1];
10918         if ( _isShrink ) {
10919           _vertHyp[1]    = other._vertHyp[1];
10920           _edgeWOLLen[1] = other._edgeWOLLen[1];
10921         }
10922       }
10923       const SMDS_MeshNode* Node(size_t i)  const
10924       {
10925         return _nodes[ _isReverse ? ( _nodes.size() - 1 - i ) : i ]._node;
10926       }
10927       void Reverse() { _isReverse = !_isReverse; }
10928       const TopAbs_ShapeEnum& vertSWOLType1() const { return _vertSWOLType[ _isReverse  ]; }
10929       const TopAbs_ShapeEnum& vertSWOLType2() const { return _vertSWOLType[ !_isReverse ]; }
10930       const AverageHyp&       vertHyp1()      const { return *(_vertHyp[ _isReverse  ]); }
10931       const AverageHyp&       vertHyp2()      const { return *(_vertHyp[ !_isReverse ]); }
10932     };
10933     // ................................................................................
10934
10935     SMESH_subMesh*       _subMesh;
10936     _SolidData*          _data1;
10937     _SolidData*          _data2;
10938
10939     std::list< BndPart > _boundary;
10940     int                  _boundarySize, _nbBoundaryParts;
10941
10942     void Init( SMESH_subMesh* sm, _SolidData* sd1, _SolidData* sd2 )
10943     {
10944       _subMesh = sm; _data1 = sd1; _data2 = sd2;
10945     }
10946     bool IsSame( const TopoDS_Face& face ) const
10947     {
10948       return _subMesh->GetSubShape().IsSame( face );
10949     }
10950     bool IsShrunk( const TopTools_MapOfShape& shrunkFaces ) const
10951     {
10952       return shrunkFaces.Contains( _subMesh->GetSubShape() );
10953     }
10954
10955     //================================================================================
10956     /*!
10957      * Check if meshes on two FACEs are equal
10958      */
10959     bool IsPeriodic( ShrinkFace& other, PeriodicFaces& periodic )
10960     {
10961       if ( !IsSameNbElements( other ))
10962         return false;
10963
10964       this->SetBoundary();
10965       other.SetBoundary();
10966       if ( this->_boundarySize    != other._boundarySize ||
10967            this->_nbBoundaryParts != other._nbBoundaryParts )
10968         return false;
10969
10970       for ( int isReverse = 0; isReverse < 2; ++isReverse )
10971       {
10972         if ( isReverse )
10973           Reverse( _boundary );
10974
10975         // check boundaries
10976         bool equalBoundary = false;
10977         for ( int iP = 0; iP < _nbBoundaryParts &&  !equalBoundary; ++iP )
10978         {
10979           if ( ! ( equalBoundary = ( this->_boundary == other._boundary )))
10980             // set first part at end
10981             _boundary.splice( _boundary.end(), _boundary, _boundary.begin() );
10982         }
10983         if ( !equalBoundary )
10984           continue;
10985
10986         // check connectivity
10987         std::set<const SMDS_MeshElement*> elemsThis, elemsOther;
10988         this->GetElements( elemsThis  );
10989         other.GetElements( elemsOther );
10990         SMESH_MeshEditor::Sew_Error err =
10991           SMESH_MeshEditor::FindMatchingNodes( elemsThis, elemsOther,
10992                                                this->_boundary.front().Node(0),
10993                                                other._boundary.front().Node(0),
10994                                                this->_boundary.front().Node(1),
10995                                                other._boundary.front().Node(1),
10996                                                periodic._nnMap );
10997         if ( err != SMESH_MeshEditor::SEW_OK )
10998           continue;
10999
11000         // check node positions
11001         std::vector< gp_XYZ > srcPnts, tgtPnts;
11002         this->GetBoundaryPoints( srcPnts );
11003         other.GetBoundaryPoints( tgtPnts );
11004         if ( !periodic._trsf.Solve( srcPnts, tgtPnts )) {
11005           continue;
11006         }
11007         double tol = std::numeric_limits<double>::max(); // tolerance by segment size
11008         for ( size_t i = 1; i < srcPnts.size(); ++i ) {
11009           tol = Min( tol, ( srcPnts[i-1] - srcPnts[i] ).SquareModulus() );
11010         }
11011         tol = 0.01 * Sqrt( tol );
11012         for ( BndPart& boundary : _boundary ) { // tolerance by VL thickness
11013           if ( boundary._isShrink )
11014             tol = Min( tol, boundary._hyp->Get1stLayerThickness() / 50. );
11015         }
11016         bool nodeCoincide = true;
11017         TNodeNodeMap::iterator n2n = periodic._nnMap.begin();
11018         for ( ; n2n != periodic._nnMap.end() &&  nodeCoincide; ++n2n )
11019         {
11020           SMESH_NodeXYZ nSrc = n2n->first;
11021           SMESH_NodeXYZ nTgt = n2n->second;
11022           gp_XYZ pTgt = periodic._trsf.Transform( nSrc );
11023           nodeCoincide = (( pTgt - nTgt ).SquareModulus() < tol * tol );
11024         }
11025         if ( nodeCoincide )
11026           return true;
11027       }
11028       return false;
11029     }
11030
11031     bool IsSameNbElements( ShrinkFace& other ) // check number of mesh faces
11032     {
11033       SMESHDS_SubMesh* sm1 = this->_subMesh->GetSubMeshDS();
11034       SMESHDS_SubMesh* sm2 = other._subMesh->GetSubMeshDS();
11035       return ( sm1->NbElements() == sm2->NbElements() &&
11036                sm1->NbNodes()    == sm2->NbNodes() );
11037     }
11038
11039     void Reverse( std::list< BndPart >& boundary )
11040     {
11041       boundary.reverse();
11042       for ( std::list< BndPart >::iterator part = boundary.begin(); part != boundary.end(); ++part )
11043         part->Reverse();
11044     }
11045
11046     void SetBoundary()
11047     {
11048       if ( !_boundary.empty() )
11049         return;
11050
11051       TopoDS_Face F = TopoDS::Face( _subMesh->GetSubShape() );
11052       if ( F.Orientation() >= TopAbs_INTERNAL ) F.Orientation( TopAbs_FORWARD );
11053       std::list< TopoDS_Edge > edges;
11054       std::list< int > nbEdgesInWire;
11055       /*int nbWires =*/ SMESH_Block::GetOrderedEdges (F, edges, nbEdgesInWire);
11056
11057       // std::list< TopoDS_Edge >::iterator edgesEnd = edges.end();
11058       // if ( nbWires > 1 ) {
11059       //   edgesEnd = edges.begin();
11060       //   std::advance( edgesEnd, nbEdgesInWire.front() );
11061       // }
11062       StdMeshers_FaceSide fSide( F, edges, _subMesh->GetFather(),
11063                                  /*fwd=*/true, /*skipMedium=*/true );
11064       _boundarySize = fSide.NbSegments();
11065
11066       //TopoDS_Vertex vv[2];
11067       //std::list< TopoDS_Edge >::iterator edgeIt = edges.begin();
11068       for ( int iE = 0; iE < nbEdgesInWire.front(); ++iE )
11069       {
11070         BndPart bndPart;
11071
11072         std::vector<const SMDS_MeshNode*> nodes = fSide.GetOrderedNodes( iE );
11073         bndPart._nodes.assign( nodes.begin(), nodes.end() );
11074         bndPart._nbSegments = bndPart._nodes.size() - 1;
11075
11076         _EdgesOnShape*  eos = _data1->GetShapeEdges( fSide.EdgeID( iE ));
11077
11078         bndPart._isShrink = ( eos->SWOLType() == TopAbs_FACE );
11079         if ( bndPart._isShrink )
11080           if ((           _data1->_noShrinkShapes.count( eos->_shapeID )) ||
11081               ( _data2 && _data2->_noShrinkShapes.count( eos->_shapeID )))
11082             bndPart._isShrink = false;
11083
11084         if ( bndPart._isShrink )
11085         {
11086           bndPart._hyp = & eos->_hyp;
11087           _EdgesOnShape* eov[2] = { _data1->GetShapeEdges( fSide.FirstVertex( iE )),
11088                                     _data1->GetShapeEdges( fSide.LastVertex ( iE )) };
11089           for ( int iV = 0; iV < 2; ++iV )
11090           {
11091             bndPart._vertHyp     [iV] = & eov[iV]->_hyp;
11092             bndPart._vertSWOLType[iV] = eov[iV]->SWOLType();
11093             if ( _data1->_noShrinkShapes.count( eov[iV]->_shapeID ))
11094               bndPart._vertSWOLType[iV] = TopAbs_SHAPE;
11095             if ( _data2 && bndPart._vertSWOLType[iV] != TopAbs_SHAPE )
11096             {
11097               eov[iV] = _data2->GetShapeEdges( iV ? fSide.LastVertex(iE) : fSide.FirstVertex(iE ));
11098               if ( _data2->_noShrinkShapes.count( eov[iV]->_shapeID ))
11099                 bndPart._vertSWOLType[iV] = TopAbs_SHAPE;
11100               else if ( eov[iV]->SWOLType() > bndPart._vertSWOLType[iV] )
11101                 bndPart._vertSWOLType[iV] = eov[iV]->SWOLType();
11102             }
11103           }
11104           bndPart._edgeWOLLen[0] = fSide.EdgeLength( iE - 1 );
11105           bndPart._edgeWOLLen[1] = fSide.EdgeLength( iE + 1 );
11106
11107           bndPart._tol = std::numeric_limits<double>::max(); // tolerance by segment size
11108           for ( size_t i = 1; i < bndPart._nodes.size(); ++i )
11109             bndPart._tol = Min( bndPart._tol,
11110                                 ( bndPart._nodes[i-1] - bndPart._nodes[i] ).SquareModulus() );
11111         }
11112
11113         if ( _boundary.empty() || ! _boundary.back().CanAppend( bndPart ))
11114           _boundary.push_back( bndPart );
11115         else
11116           _boundary.back().Append( bndPart );
11117       }
11118
11119       _nbBoundaryParts = _boundary.size();
11120       if ( _nbBoundaryParts > 1 && _boundary.front()._isShrink == _boundary.back()._isShrink )
11121       {
11122         _boundary.back().Append( _boundary.front() );
11123         _boundary.pop_front();
11124         --_nbBoundaryParts;
11125       }
11126     }
11127
11128     void GetElements( std::set<const SMDS_MeshElement*>& theElems)
11129     {
11130       if ( SMESHDS_SubMesh* sm = _subMesh->GetSubMeshDS() )
11131         for ( SMDS_ElemIteratorPtr fIt = sm->GetElements(); fIt->more(); )
11132           theElems.insert( theElems.end(), fIt->next() );
11133
11134       return ;
11135     }
11136
11137     void GetBoundaryPoints( std::vector< gp_XYZ >& points )
11138     {
11139       points.reserve( _boundarySize );
11140       size_t  nb = _boundary.rbegin()->_nodes.size();
11141       smIdType lastID = _boundary.rbegin()->Node( nb - 1 )->GetID();
11142       std::list< BndPart >::const_iterator part = _boundary.begin();
11143       for ( ; part != _boundary.end(); ++part )
11144       {
11145         size_t nb = part->_nodes.size();
11146         size_t iF = 0;
11147         size_t iR = nb - 1;
11148         size_t* i = part->_isReverse ? &iR : &iF;
11149         if ( part->_nodes[ *i ]->GetID() == lastID )
11150           ++iF, --iR;
11151         for ( ; iF < nb; ++iF, --iR )
11152           points.push_back( part->_nodes[ *i ]);
11153         --iF, ++iR;
11154         lastID = part->_nodes[ *i ]->GetID();
11155       }
11156     }
11157   }; // struct ShrinkFace
11158
11159   //--------------------------------------------------------------------------------
11160   /*!
11161    * \brief Periodic FACEs
11162    */
11163   struct Periodicity
11164   {
11165     std::vector< ShrinkFace >    _shrinkFaces;
11166     std::vector< PeriodicFaces > _periodicFaces;
11167
11168     PeriodicFaces* GetPeriodic( const TopoDS_Face& face, const TopTools_MapOfShape& shrunkFaces )
11169     {
11170       for ( size_t i = 0; i < _periodicFaces.size(); ++i )
11171         if ( _periodicFaces[ i ].IncludeShrunk( face, shrunkFaces ))
11172           return & _periodicFaces[ i ];
11173       return 0;
11174     }
11175     void ClearPeriodic( const TopoDS_Face& face )
11176     {
11177       for ( size_t i = 0; i < _periodicFaces.size(); ++i )
11178         if ( _periodicFaces[ i ]._shriFace[0]->IsSame( face ) ||
11179              _periodicFaces[ i ]._shriFace[1]->IsSame( face ))
11180           _periodicFaces[ i ].Clear();
11181     }
11182   };
11183
11184   //================================================================================
11185   /*!
11186    * Check if a pair includes the given FACE and the other FACE is already shrunk
11187    */
11188   bool PeriodicFaces::IncludeShrunk( const TopoDS_Face&         face,
11189                                      const TopTools_MapOfShape& shrunkFaces ) const
11190   {
11191     if ( IsEmpty() ) return false;
11192     return (( _shriFace[0]->IsSame( face ) && _shriFace[1]->IsShrunk( shrunkFaces )) ||
11193             ( _shriFace[1]->IsSame( face ) && _shriFace[0]->IsShrunk( shrunkFaces )));
11194   }
11195
11196   //================================================================================
11197   /*!
11198    * Make equal meshes on periodic faces by moving corresponding nodes
11199    */
11200   bool PeriodicFaces::MoveNodes( const TopoDS_Face& tgtFace )
11201   {
11202     int iTgt = _shriFace[1]->IsSame( tgtFace );
11203     int iSrc = 1 - iTgt;
11204
11205     _SolidData* dataSrc = _shriFace[iSrc]->_data1;
11206     _SolidData* dataTgt = _shriFace[iTgt]->_data1;
11207
11208     Trsf * trsf = & _trsf, trsfInverse;
11209     if ( iSrc != 0 )
11210     {
11211       trsfInverse = _trsf;
11212       if ( !trsfInverse.Invert())
11213         return false;
11214       trsf = &trsfInverse;
11215     }
11216     SMESHDS_Mesh* meshDS = dataSrc->GetHelper().GetMeshDS();
11217
11218     dumpFunction(SMESH_Comment("periodicMoveNodes_F")
11219                                << _shriFace[iSrc]->_subMesh->GetId() << "_F"
11220                                << _shriFace[iTgt]->_subMesh->GetId() );
11221     TNode2Edge::iterator n2e;
11222     TNodeNodeMap::iterator n2n = _nnMap.begin();
11223     for ( ; n2n != _nnMap.end(); ++n2n )
11224     {
11225       const SMDS_MeshNode* const* nn = & n2n->first;
11226       const SMDS_MeshNode*      nSrc = nn[ iSrc ];
11227       const SMDS_MeshNode*      nTgt = nn[ iTgt ];
11228
11229       if (( nSrc->GetPosition()->GetDim() == 2 ) ||
11230           (( n2e = dataSrc->_n2eMap.find( nSrc )) == dataSrc->_n2eMap.end() ))
11231       {
11232         SMESH_NodeXYZ pSrc = nSrc;
11233         gp_XYZ pTgt = trsf->Transform( pSrc );
11234         meshDS->MoveNode( nTgt, pTgt.X(), pTgt.Y(), pTgt.Z() );
11235       }
11236       else
11237       {
11238         _LayerEdge* leSrc = n2e->second;
11239         n2e = dataTgt->_n2eMap.find( nTgt );
11240         if ( n2e == dataTgt->_n2eMap.end() )
11241           break;
11242         _LayerEdge* leTgt = n2e->second;
11243         if ( leSrc->_nodes.size() != leTgt->_nodes.size() )
11244           break;
11245         for ( size_t iN = 1; iN < leSrc->_nodes.size(); ++iN )
11246         {
11247           SMESH_NodeXYZ pSrc = leSrc->_nodes[ iN ];
11248           gp_XYZ pTgt = trsf->Transform( pSrc );
11249           meshDS->MoveNode( leTgt->_nodes[ iN ], pTgt.X(), pTgt.Y(), pTgt.Z() );
11250
11251           dumpMove( leTgt->_nodes[ iN ]);
11252         }
11253       }
11254     }
11255     bool done = ( n2n == _nnMap.end() );
11256     debugMsg( "PeriodicFaces::MoveNodes "
11257               << _shriFace[iSrc]->_subMesh->GetId() << " -> "
11258               << _shriFace[iTgt]->_subMesh->GetId() << " -- "
11259               << ( done ? "DONE" : "FAIL"));
11260     dumpFunctionEnd();
11261
11262     return done;
11263   }
11264 } // namespace VISCOUS_3D; Periodicity part
11265
11266
11267 //================================================================================
11268 /*!
11269  * \brief Find FACEs to shrink, that are equally meshed before shrink (i.e. periodic)
11270  *        and should remain equal after shrink
11271  */
11272 //================================================================================
11273
11274 void _ViscousBuilder::findPeriodicFaces()
11275 {
11276   // make map of (ids of FACEs to shrink mesh on) to (list of _SolidData containing
11277   // _LayerEdge's inflated along FACE or EDGE)
11278   std::map< TGeomID, std::list< _SolidData* > > id2sdMap;
11279   for ( size_t i = 0 ; i < _sdVec.size(); ++i )
11280   {
11281     _SolidData& data = _sdVec[i];
11282     std::map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
11283     for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
11284       if ( s2s->second.ShapeType() == TopAbs_FACE )
11285         id2sdMap[ getMeshDS()->ShapeToIndex( s2s->second )].push_back( &data );
11286   }
11287
11288   _periodicity.reset( new Periodicity );
11289   _periodicity->_shrinkFaces.resize( id2sdMap.size() );
11290
11291   std::map< TGeomID, std::list< _SolidData* > >::iterator id2sdIt = id2sdMap.begin();
11292   for ( size_t i = 0; i < id2sdMap.size(); ++i, ++id2sdIt )
11293   {
11294     _SolidData* sd1 = id2sdIt->second.front();
11295     _SolidData* sd2 = id2sdIt->second.back();
11296     _periodicity->_shrinkFaces[ i ].Init( _mesh->GetSubMeshContaining( id2sdIt->first ), sd1, sd2 );
11297   }
11298
11299   for (   size_t i1 = 0;      i1 < _periodicity->_shrinkFaces.size(); ++i1 )
11300     for ( size_t i2 = i1 + 1; i2 < _periodicity->_shrinkFaces.size(); ++i2 )
11301     {
11302       PeriodicFaces pf( & _periodicity->_shrinkFaces[ i1 ],
11303                         & _periodicity->_shrinkFaces[ i2 ]);
11304       if ( pf._shriFace[0]->IsPeriodic( *pf._shriFace[1], pf ))
11305       {
11306         _periodicity->_periodicFaces.push_back( pf );
11307       }
11308     }
11309   return;
11310 }
11311
11312 //================================================================================
11313 /*!
11314  * \brief Shrink 2D mesh on faces to let space for inflated layers
11315  */
11316 //================================================================================
11317
11318 bool _ViscousBuilder::shrink(_SolidData& theData)
11319 {
11320   // make map of (ids of FACEs to shrink mesh on) to (list of _SolidData containing
11321   // _LayerEdge's inflated along FACE or EDGE)
11322   map< TGeomID, list< _SolidData* > > f2sdMap;
11323   for ( size_t i = 0 ; i < _sdVec.size(); ++i )
11324   {
11325     _SolidData& data = _sdVec[i];
11326     map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
11327     for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
11328       if ( s2s->second.ShapeType() == TopAbs_FACE && !_shrunkFaces.Contains( s2s->second ))
11329       {
11330         f2sdMap[ getMeshDS()->ShapeToIndex( s2s->second )].push_back( &data );
11331
11332         // Put mesh faces on the shrunk FACE to the proxy sub-mesh to avoid
11333         // usage of mesh faces made in addBoundaryElements() by the 3D algo or
11334         // by StdMeshers_QuadToTriaAdaptor
11335         if ( SMESHDS_SubMesh* smDS = getMeshDS()->MeshElements( s2s->second ))
11336         {
11337           SMESH_ProxyMesh::SubMesh* proxySub =
11338             data._proxyMesh->getFaceSubM( TopoDS::Face( s2s->second ), /*create=*/true);
11339           if ( proxySub->NbElements() == 0 )
11340           {
11341             SMDS_ElemIteratorPtr fIt = smDS->GetElements();
11342             while ( fIt->more() )
11343             {
11344               const SMDS_MeshElement* f = fIt->next();
11345               // as a result 3D algo will use elements from proxySub and not from smDS
11346               proxySub->AddElement( f );
11347               f->setIsMarked( true );
11348
11349               // Mark nodes on the FACE to discriminate them from nodes
11350               // added by addBoundaryElements(); marked nodes are to be smoothed while shrink()
11351               for ( int iN = 0, nbN = f->NbNodes(); iN < nbN; ++iN )
11352               {
11353                 const SMDS_MeshNode* n = f->GetNode( iN );
11354                 if ( n->GetPosition()->GetDim() == 2 )
11355                   n->setIsMarked( true );
11356               }
11357             }
11358           }
11359         }
11360       }
11361   }
11362
11363   SMESH_MesherHelper helper( *_mesh );
11364   helper.ToFixNodeParameters( true );
11365
11366   // EDGEs to shrink
11367   map< TGeomID, _Shrinker1D > e2shrMap;
11368   vector< _EdgesOnShape* > subEOS;
11369   vector< _LayerEdge* > lEdges;
11370
11371   // loop on FACEs to shrink mesh on
11372   map< TGeomID, list< _SolidData* > >::iterator f2sd = f2sdMap.begin();
11373   for ( ; f2sd != f2sdMap.end(); ++f2sd )
11374   {
11375     list< _SolidData* > & dataList = f2sd->second;
11376     if ( dataList.front()->_n2eMap.empty() ||
11377          dataList.back() ->_n2eMap.empty() )
11378       continue; // not yet computed
11379     if ( dataList.front() != &theData &&
11380          dataList.back()  != &theData )
11381       continue;
11382
11383     _SolidData&      data = *dataList.front();
11384     _SolidData*     data2 = dataList.size() > 1 ? dataList.back() : 0;
11385     const TopoDS_Face&  F = TopoDS::Face( getMeshDS()->IndexToShape( f2sd->first ));
11386     SMESH_subMesh*     sm = _mesh->GetSubMesh( F );
11387     SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
11388
11389     Handle(Geom_Surface) surface = BRep_Tool::Surface( F );
11390
11391     _shrunkFaces.Add( F );
11392     helper.SetSubShape( F );
11393
11394     // ==============================
11395     // Use periodicity to move nodes
11396     // ==============================
11397
11398     PeriodicFaces* periodic = _periodicity->GetPeriodic( F, _shrunkFaces );
11399     bool movedByPeriod = ( periodic && periodic->MoveNodes( F ));
11400
11401     // ===========================
11402     // Prepare data for shrinking
11403     // ===========================
11404
11405     // Collect nodes to smooth (they are marked at the beginning of this method)
11406     vector < const SMDS_MeshNode* > smoothNodes;
11407
11408     if ( !movedByPeriod )
11409     {
11410       SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
11411       while ( nIt->more() )
11412       {
11413         const SMDS_MeshNode* n = nIt->next();
11414         if ( n->isMarked() )
11415           smoothNodes.push_back( n );
11416       }
11417     }
11418     // Find out face orientation
11419     double refSign = 1;
11420     const set<TGeomID> ignoreShapes;
11421     bool isOkUV;
11422     if ( !smoothNodes.empty() )
11423     {
11424       vector<_Simplex> simplices;
11425       _Simplex::GetSimplices( smoothNodes[0], simplices, ignoreShapes );
11426       helper.GetNodeUV( F, simplices[0]._nPrev, 0, &isOkUV ); // fix UV of simplex nodes
11427       helper.GetNodeUV( F, simplices[0]._nNext, 0, &isOkUV );
11428       gp_XY uv = helper.GetNodeUV( F, smoothNodes[0], 0, &isOkUV );
11429       if ( !simplices[0].IsForward(uv, smoothNodes[0], F, helper, refSign ))
11430         refSign = -1;
11431     }
11432
11433     // Find _LayerEdge's inflated along F
11434     subEOS.clear();
11435     lEdges.clear();
11436     {
11437       SMESH_subMeshIteratorPtr subIt = sm->getDependsOnIterator(/*includeSelf=*/false,
11438                                                                 /*complexFirst=*/true); //!!!
11439       while ( subIt->more() )
11440       {
11441         const TGeomID subID = subIt->next()->GetId();
11442         if ( data._noShrinkShapes.count( subID ))
11443           continue;
11444         _EdgesOnShape* eos = data.GetShapeEdges( subID );
11445         if ( !eos || eos->_sWOL.IsNull() )
11446           if ( data2 ) // check in adjacent SOLID
11447           {
11448             eos = data2->GetShapeEdges( subID );
11449             if ( !eos || eos->_sWOL.IsNull() )
11450               continue;
11451           }
11452         subEOS.push_back( eos );
11453
11454         if ( !movedByPeriod )
11455           for ( size_t i = 0; i < eos->_edges.size(); ++i )
11456           {
11457             lEdges.push_back( eos->_edges[ i ] );
11458             prepareEdgeToShrink( *eos->_edges[ i ], *eos, helper, smDS );
11459           }
11460       }
11461     }
11462
11463     dumpFunction(SMESH_Comment("beforeShrinkFace")<<f2sd->first); // debug
11464     SMDS_ElemIteratorPtr fIt = smDS->GetElements();
11465     while ( fIt->more() )
11466       if ( const SMDS_MeshElement* f = fIt->next() )
11467         dumpChangeNodes( f );
11468     dumpFunctionEnd();
11469
11470     // Replace source nodes by target nodes in mesh faces to shrink
11471     dumpFunction(SMESH_Comment("replNodesOnFace")<<f2sd->first); // debug
11472     const SMDS_MeshNode* nodes[20];
11473     for ( size_t iS = 0; iS < subEOS.size(); ++iS )
11474     {
11475       _EdgesOnShape& eos = * subEOS[ iS ];
11476       for ( size_t i = 0; i < eos._edges.size(); ++i )
11477       {
11478         _LayerEdge& edge = *eos._edges[i];
11479         const SMDS_MeshNode* srcNode = edge._nodes[0];
11480         const SMDS_MeshNode* tgtNode = edge._nodes.back();
11481         SMDS_ElemIteratorPtr fIt = srcNode->GetInverseElementIterator(SMDSAbs_Face);
11482         while ( fIt->more() )
11483         {
11484           const SMDS_MeshElement* f = fIt->next();
11485           if ( !smDS->Contains( f ) || !f->isMarked() )
11486             continue;
11487           SMDS_NodeIteratorPtr nIt = f->nodeIterator();
11488           for ( int iN = 0; nIt->more(); ++iN )
11489           {
11490             const SMDS_MeshNode* n = nIt->next();
11491             nodes[iN] = ( n == srcNode ? tgtNode : n );
11492           }
11493           helper.GetMeshDS()->ChangeElementNodes( f, nodes, f->NbNodes() );
11494           dumpChangeNodes( f );
11495         }
11496       }
11497     }
11498     dumpFunctionEnd();
11499
11500     // find out if a FACE is concave
11501     const bool isConcaveFace = isConcave( F, helper );
11502
11503     // Create _SmoothNode's on face F
11504     vector< _SmoothNode > nodesToSmooth( smoothNodes.size() );
11505     {
11506       dumpFunction(SMESH_Comment("fixUVOnFace")<<f2sd->first); // debug
11507       const bool sortSimplices = isConcaveFace;
11508       for ( size_t i = 0; i < smoothNodes.size(); ++i )
11509       {
11510         const SMDS_MeshNode* n = smoothNodes[i];
11511         nodesToSmooth[ i ]._node = n;
11512         // src nodes must be already replaced by tgt nodes to have tgt nodes in _simplices
11513         _Simplex::GetSimplices( n, nodesToSmooth[ i ]._simplices, ignoreShapes, 0, sortSimplices);
11514         // fix up incorrect uv of nodes on the FACE
11515         helper.GetNodeUV( F, n, 0, &isOkUV);
11516         dumpMove( n );
11517       }
11518       dumpFunctionEnd();
11519     }
11520     //if ( nodesToSmooth.empty() ) continue;
11521
11522     // Find EDGE's to shrink and set simpices to LayerEdge's
11523     set< _Shrinker1D* > eShri1D;
11524     {
11525       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
11526       {
11527         _EdgesOnShape& eos = * subEOS[ iS ];
11528         if ( eos.SWOLType() == TopAbs_EDGE )
11529         {
11530           SMESH_subMesh* edgeSM = _mesh->GetSubMesh( eos._sWOL );
11531           VISCOUS_3D::ToClearSubWithMain( edgeSM, data._solid );
11532           if ( !movedByPeriod )
11533           {
11534             _Shrinker1D& shrinker = e2shrMap[ edgeSM->GetId() ];
11535             eShri1D.insert( & shrinker );
11536             shrinker.AddEdge( eos._edges[0], eos, helper );
11537             // restore params of nodes on EDGE if the EDGE has been already
11538             // shrunk while shrinking other FACE
11539             shrinker.RestoreParams();
11540           }
11541         }
11542         for ( size_t i = 0; i < eos._edges.size(); ++i )
11543         {
11544           _LayerEdge& edge = * eos._edges[i];
11545           _Simplex::GetSimplices( /*tgtNode=*/edge._nodes.back(), edge._simplices, ignoreShapes );
11546
11547           // additionally mark tgt node; only marked nodes will be used in SetNewLength2d()
11548           // not-marked nodes are those added by refine()
11549           edge._nodes.back()->setIsMarked( true );
11550         }
11551       }
11552     }
11553
11554     bool toFixTria = false; // to improve quality of trias by diagonal swap
11555     if ( isConcaveFace && !movedByPeriod )
11556     {
11557       const bool hasTria = _mesh->NbTriangles(), hasQuad = _mesh->NbQuadrangles();
11558       if ( hasTria != hasQuad ) {
11559         toFixTria = hasTria;
11560       }
11561       else {
11562         set<int> nbNodesSet;
11563         SMDS_ElemIteratorPtr fIt = smDS->GetElements();
11564         while ( fIt->more() && nbNodesSet.size() < 2 )
11565           nbNodesSet.insert( fIt->next()->NbCornerNodes() );
11566         toFixTria = ( *nbNodesSet.begin() == 3 );
11567       }
11568     }
11569
11570     // ==================
11571     // Perform shrinking
11572     // ==================
11573
11574     bool shrunk = !movedByPeriod;
11575     int nbBad, shriStep=0, smooStep=0;
11576     _SmoothNode::SmoothType smoothType
11577       = isConcaveFace ? _SmoothNode::ANGULAR : _SmoothNode::LAPLACIAN;
11578     SMESH_Comment errMsg;
11579     while ( shrunk )
11580     {
11581       shriStep++;
11582       // Move boundary nodes (actually just set new UV)
11583       // -----------------------------------------------
11584       dumpFunction(SMESH_Comment("moveBoundaryOnF")<<f2sd->first<<"_st"<<shriStep ); // debug
11585       shrunk = false;
11586       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
11587       {
11588         _EdgesOnShape& eos = * subEOS[ iS ];
11589         for ( size_t i = 0; i < eos._edges.size(); ++i )
11590         {
11591           shrunk |= eos._edges[i]->SetNewLength2d( surface, F, eos, helper );
11592         }
11593       }
11594       dumpFunctionEnd();
11595
11596       // Move nodes on EDGE's
11597       // (XYZ is set as soon as a needed length reached in SetNewLength2d())
11598       set< _Shrinker1D* >::iterator shr = eShri1D.begin();
11599       for ( ; shr != eShri1D.end(); ++shr )
11600         (*shr)->Compute( /*set3D=*/false, helper );
11601
11602       // Smoothing in 2D
11603       // -----------------
11604       int nbNoImpSteps = 0;
11605       bool       moved = true;
11606       nbBad = 1;
11607       while (( nbNoImpSteps < 5 && nbBad > 0) && moved)
11608       {
11609         dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
11610
11611         int oldBadNb = nbBad;
11612         nbBad = 0;
11613         moved = false;
11614         // '% 5' minimizes NB FUNCTIONS on viscous_layers_00/B2 case
11615         _SmoothNode::SmoothType smooTy = ( smooStep % 5 ) ? smoothType : _SmoothNode::LAPLACIAN;
11616         for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
11617         {
11618           moved |= nodesToSmooth[i].Smooth( nbBad, surface, helper, refSign,
11619                                             smooTy, /*set3D=*/isConcaveFace);
11620         }
11621         if ( nbBad < oldBadNb )
11622           nbNoImpSteps = 0;
11623         else
11624           nbNoImpSteps++;
11625
11626         dumpFunctionEnd();
11627       }
11628
11629       errMsg.clear();
11630       if ( nbBad > 0 )
11631         errMsg << "Can't shrink 2D mesh on face " << f2sd->first;
11632       if ( shriStep > 200 )
11633         errMsg << "Infinite loop at shrinking 2D mesh on face " << f2sd->first;
11634       if ( !errMsg.empty() )
11635         break;
11636
11637       // Fix narrow triangles by swapping diagonals
11638       // ---------------------------------------
11639       if ( toFixTria )
11640       {
11641         set<const SMDS_MeshNode*> usedNodes;
11642         fixBadFaces( F, helper, /*is2D=*/true, shriStep, & usedNodes); // swap diagonals
11643
11644         // update working data
11645         set<const SMDS_MeshNode*>::iterator n;
11646         for ( size_t i = 0; i < nodesToSmooth.size() && !usedNodes.empty(); ++i )
11647         {
11648           n = usedNodes.find( nodesToSmooth[ i ]._node );
11649           if ( n != usedNodes.end())
11650           {
11651             _Simplex::GetSimplices( nodesToSmooth[ i ]._node,
11652                                     nodesToSmooth[ i ]._simplices,
11653                                     ignoreShapes, NULL,
11654                                     /*sortSimplices=*/ smoothType == _SmoothNode::ANGULAR );
11655             usedNodes.erase( n );
11656           }
11657         }
11658         for ( size_t i = 0; i < lEdges.size() && !usedNodes.empty(); ++i )
11659         {
11660           n = usedNodes.find( /*tgtNode=*/ lEdges[i]->_nodes.back() );
11661           if ( n != usedNodes.end())
11662           {
11663             _Simplex::GetSimplices( lEdges[i]->_nodes.back(),
11664                                     lEdges[i]->_simplices,
11665                                     ignoreShapes );
11666             usedNodes.erase( n );
11667           }
11668         }
11669       }
11670       // TODO: check effect of this additional smooth
11671       // additional laplacian smooth to increase allowed shrink step
11672       // for ( int st = 1; st; --st )
11673       // {
11674       //   dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
11675       //   for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
11676       //   {
11677       //     nodesToSmooth[i].Smooth( nbBad,surface,helper,refSign,
11678       //                              _SmoothNode::LAPLACIAN,/*set3D=*/false);
11679       //   }
11680       // }
11681
11682     } // while ( shrunk )
11683
11684     if ( !errMsg.empty() ) // Try to re-compute the shrink FACE
11685     {
11686       debugMsg( "Re-compute FACE " << f2sd->first << " because " << errMsg );
11687
11688       // remove faces
11689       SMESHDS_SubMesh* psm = data._proxyMesh->getFaceSubM( F );
11690       {
11691         vector< const SMDS_MeshElement* > facesToRm;
11692         if ( psm )
11693         {
11694           facesToRm.reserve( psm->NbElements() );
11695           for ( SMDS_ElemIteratorPtr ite = psm->GetElements(); ite->more(); )
11696             facesToRm.push_back( ite->next() );
11697
11698           for ( size_t i = 0 ; i < _sdVec.size(); ++i )
11699             if (( psm = _sdVec[i]._proxyMesh->getFaceSubM( F )))
11700               psm->Clear();
11701         }
11702         for ( size_t i = 0; i < facesToRm.size(); ++i )
11703           getMeshDS()->RemoveFreeElement( facesToRm[i], smDS, /*fromGroups=*/false );
11704       }
11705       // remove nodes
11706       {
11707         TIDSortedNodeSet nodesToKeep; // nodes of _LayerEdge to keep
11708         for ( size_t iS = 0; iS < subEOS.size(); ++iS ) {
11709           for ( size_t i = 0; i < subEOS[iS]->_edges.size(); ++i )
11710             nodesToKeep.insert( ++( subEOS[iS]->_edges[i]->_nodes.begin() ),
11711                                 subEOS[iS]->_edges[i]->_nodes.end() );
11712         }
11713         SMDS_NodeIteratorPtr itn = smDS->GetNodes();
11714         while ( itn->more() ) {
11715           const SMDS_MeshNode* n = itn->next();
11716           if ( !nodesToKeep.count( n ))
11717             getMeshDS()->RemoveFreeNode( n, smDS, /*fromGroups=*/false );
11718         }
11719       }
11720       _periodicity->ClearPeriodic( F );
11721
11722       // restore position and UV of target nodes
11723       gp_Pnt p;
11724       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
11725         for ( size_t i = 0; i < subEOS[iS]->_edges.size(); ++i )
11726         {
11727           _LayerEdge*       edge = subEOS[iS]->_edges[i];
11728           SMDS_MeshNode* tgtNode = const_cast< SMDS_MeshNode*& >( edge->_nodes.back() );
11729           if ( edge->_pos.empty() ||
11730                edge->Is( _LayerEdge::SHRUNK )) continue;
11731           if ( subEOS[iS]->SWOLType() == TopAbs_FACE )
11732           {
11733             SMDS_FacePositionPtr pos = tgtNode->GetPosition();
11734             pos->SetUParameter( edge->_pos[0].X() );
11735             pos->SetVParameter( edge->_pos[0].Y() );
11736             p = surface->Value( edge->_pos[0].X(), edge->_pos[0].Y() );
11737           }
11738           else
11739           {
11740             SMDS_EdgePositionPtr pos = tgtNode->GetPosition();
11741             pos->SetUParameter( edge->_pos[0].Coord( U_TGT ));
11742             p = BRepAdaptor_Curve( TopoDS::Edge( subEOS[iS]->_sWOL )).Value( pos->GetUParameter() );
11743           }
11744           tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
11745           dumpMove( tgtNode );
11746         }
11747       // shrink EDGE sub-meshes and set proxy sub-meshes
11748       UVPtStructVec uvPtVec;
11749       set< _Shrinker1D* >::iterator shrIt = eShri1D.begin();
11750       for ( shrIt = eShri1D.begin(); shrIt != eShri1D.end(); ++shrIt )
11751       {
11752         _Shrinker1D* shr = (*shrIt);
11753         shr->Compute( /*set3D=*/true, helper );
11754
11755         // set proxy mesh of EDGEs w/o layers
11756         map< double, const SMDS_MeshNode* > nodes;
11757         SMESH_Algo::GetSortedNodesOnEdge( getMeshDS(), shr->GeomEdge(),/*skipMedium=*/true, nodes);
11758         // remove refinement nodes
11759         const SMDS_MeshNode* sn0 = shr->SrcNode(0), *sn1 = shr->SrcNode(1);
11760         const SMDS_MeshNode* tn0 = shr->TgtNode(0), *tn1 = shr->TgtNode(1);
11761         map< double, const SMDS_MeshNode* >::iterator u2n = nodes.begin();
11762         if ( u2n->second == sn0 || u2n->second == sn1 )
11763         {
11764           while ( u2n->second != tn0 && u2n->second != tn1 )
11765             ++u2n;
11766           nodes.erase( nodes.begin(), u2n );
11767         }
11768         u2n = --nodes.end();
11769         if ( u2n->second == sn0 || u2n->second == sn1 )
11770         {
11771           while ( u2n->second != tn0 && u2n->second != tn1 )
11772             --u2n;
11773           nodes.erase( ++u2n, nodes.end() );
11774         }
11775         // set proxy sub-mesh
11776         uvPtVec.resize( nodes.size() );
11777         u2n = nodes.begin();
11778         BRepAdaptor_Curve2d curve( shr->GeomEdge(), F );
11779         for ( size_t i = 0; i < nodes.size(); ++i, ++u2n )
11780         {
11781           uvPtVec[ i ].node = u2n->second;
11782           uvPtVec[ i ].param = u2n->first;
11783           uvPtVec[ i ].SetUV( curve.Value( u2n->first ).XY() );
11784         }
11785         StdMeshers_FaceSide fSide( uvPtVec, F, shr->GeomEdge(), _mesh );
11786         StdMeshers_ViscousLayers2D::SetProxyMeshOfEdge( fSide );
11787       }
11788
11789       // set proxy mesh of EDGEs with layers
11790       vector< _LayerEdge* > edges;
11791       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
11792       {
11793         _EdgesOnShape& eos = * subEOS[ iS ];
11794         if ( eos.ShapeType() != TopAbs_EDGE ) continue;
11795         if ( eos.size() == 0 )
11796           continue;
11797
11798         const TopoDS_Edge& E = TopoDS::Edge( eos._shape );
11799         data.SortOnEdge( E, eos._edges );
11800
11801         edges.clear();
11802         if ( _EdgesOnShape* eov = data.GetShapeEdges( helper.IthVertex( 0, E, /*CumOri=*/false )))
11803           if ( !eov->_edges.empty() )
11804             edges.push_back( eov->_edges[0] ); // on 1st VERTEX
11805
11806         edges.insert( edges.end(), eos._edges.begin(), eos._edges.end() );
11807
11808         if ( _EdgesOnShape* eov = data.GetShapeEdges( helper.IthVertex( 1, E, /*CumOri=*/false )))
11809           if ( !eov->_edges.empty() )
11810             edges.push_back( eov->_edges[0] ); // on last VERTEX
11811
11812         uvPtVec.resize( edges.size() );
11813         for ( size_t i = 0; i < edges.size(); ++i )
11814         {
11815           uvPtVec[ i ].node = edges[i]->_nodes.back();
11816           uvPtVec[ i ].param = helper.GetNodeU( E, edges[i]->_nodes[0] );
11817           uvPtVec[ i ].SetUV( helper.GetNodeUV( F, edges[i]->_nodes.back() ));
11818         }
11819         if ( uvPtVec[ 0 ].node == uvPtVec.back().node &&            // closed
11820              helper.IsSeamShape( uvPtVec[ 0 ].node->GetShapeID() ))
11821         {
11822           uvPtVec[ 0 ].SetUV( helper.GetNodeUV( F,
11823                                                 edges[0]->_nodes.back(),
11824                                                 edges[1]->_nodes.back() ));
11825           size_t i = edges.size() - 1;
11826           uvPtVec[ i ].SetUV( helper.GetNodeUV( F,
11827                                                 edges[i  ]->_nodes.back(),
11828                                                 edges[i-1]->_nodes.back() ));
11829         }
11830         // if ( edges.empty() )
11831         //   continue;
11832         BRep_Tool::Range( E, uvPtVec[0].param, uvPtVec.back().param );
11833         StdMeshers_FaceSide fSide( uvPtVec, F, E, _mesh );
11834         StdMeshers_ViscousLayers2D::SetProxyMeshOfEdge( fSide );
11835       }
11836       // temporary clear the FACE sub-mesh from faces made by refine()
11837       vector< const SMDS_MeshElement* > elems;
11838       elems.reserve( smDS->NbElements() + smDS->NbNodes() );
11839       for ( SMDS_ElemIteratorPtr ite = smDS->GetElements(); ite->more(); )
11840         elems.push_back( ite->next() );
11841       for ( SMDS_NodeIteratorPtr ite = smDS->GetNodes(); ite->more(); )
11842         elems.push_back( ite->next() );
11843       smDS->Clear();
11844
11845       // compute the mesh on the FACE
11846       TopTools_IndexedMapOfShape allowed(1);
11847       allowed.Add( sm->GetSubShape() );
11848       sm->SetAllowedSubShapes( &allowed );
11849       sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
11850       sm->ComputeStateEngine( SMESH_subMesh::COMPUTE_SUBMESH );
11851       sm->SetAllowedSubShapes( nullptr );
11852
11853       // re-fill proxy sub-meshes of the FACE
11854       for ( size_t i = 0 ; i < _sdVec.size(); ++i )
11855         if (( psm = _sdVec[i]._proxyMesh->getFaceSubM( F )))
11856           for ( SMDS_ElemIteratorPtr ite = smDS->GetElements(); ite->more(); )
11857             psm->AddElement( ite->next() );
11858
11859       // re-fill smDS
11860       for ( size_t i = 0; i < elems.size(); ++i )
11861         smDS->AddElement( elems[i] );
11862
11863       if ( sm->GetComputeState() != SMESH_subMesh::COMPUTE_OK )
11864         return error( errMsg );
11865
11866     } // end of re-meshing in case of failed smoothing
11867     else if ( !movedByPeriod )
11868     {
11869       // No wrongly shaped faces remain; final smooth. Set node XYZ.
11870       bool isStructuredFixed = false;
11871       if ( SMESH_2D_Algo* algo = dynamic_cast<SMESH_2D_Algo*>( sm->GetAlgo() ))
11872         isStructuredFixed = algo->FixInternalNodes( *data._proxyMesh, F );
11873       if ( !isStructuredFixed )
11874       {
11875         if ( isConcaveFace ) // fix narrow faces by swapping diagonals
11876           fixBadFaces( F, helper, /*is2D=*/false, ++shriStep );
11877
11878         for ( int st = 3; st; --st )
11879         {
11880           switch( st ) {
11881           case 1: smoothType = _SmoothNode::LAPLACIAN; break;
11882           case 2: smoothType = _SmoothNode::LAPLACIAN; break;
11883           case 3: smoothType = _SmoothNode::ANGULAR; break;
11884           }
11885           dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
11886           for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
11887           {
11888             nodesToSmooth[i].Smooth( nbBad,surface,helper,refSign,
11889                                      smoothType,/*set3D=*/st==1 );
11890           }
11891           dumpFunctionEnd();
11892         }
11893       }
11894       if ( !getMeshDS()->IsEmbeddedMode() )
11895         // Log node movement
11896         for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
11897         {
11898           SMESH_TNodeXYZ p ( nodesToSmooth[i]._node );
11899           getMeshDS()->MoveNode( nodesToSmooth[i]._node, p.X(), p.Y(), p.Z() );
11900         }
11901     }
11902
11903     // Set an event listener to clear FACE sub-mesh together with SOLID sub-mesh
11904     VISCOUS_3D::ToClearSubWithMain( sm, data._solid );
11905     if ( data2 )
11906       VISCOUS_3D::ToClearSubWithMain( sm, data2->_solid );
11907
11908   } // loop on FACES to shrink mesh on
11909
11910
11911   // Replace source nodes by target nodes in shrunk mesh edges
11912
11913   map< int, _Shrinker1D >::iterator e2shr = e2shrMap.begin();
11914   for ( ; e2shr != e2shrMap.end(); ++e2shr )
11915     e2shr->second.SwapSrcTgtNodes( getMeshDS() );
11916
11917   return true;
11918 }
11919
11920 //================================================================================
11921 /*!
11922  * \brief Computes 2d shrink direction and finds nodes limiting shrinking
11923  */
11924 //================================================================================
11925
11926 bool _ViscousBuilder::prepareEdgeToShrink( _LayerEdge&            edge,
11927                                            _EdgesOnShape&         eos,
11928                                            SMESH_MesherHelper&    helper,
11929                                            const SMESHDS_SubMesh* /*faceSubMesh*/)
11930 {
11931   const SMDS_MeshNode* srcNode = edge._nodes[0];
11932   const SMDS_MeshNode* tgtNode = edge._nodes.back();
11933
11934   if ( eos.SWOLType() == TopAbs_FACE )
11935   {
11936     if ( tgtNode->GetPosition()->GetDim() != 2 ) // not inflated edge
11937     {
11938       edge._pos.clear();
11939       edge.Set( _LayerEdge::SHRUNK );
11940       return srcNode == tgtNode;
11941     }
11942     gp_XY srcUV ( edge._pos[0].X(), edge._pos[0].Y() );          //helper.GetNodeUV( F, srcNode );
11943     gp_XY tgtUV = edge.LastUV( TopoDS::Face( eos._sWOL ), eos ); //helper.GetNodeUV( F, tgtNode );
11944     gp_Vec2d uvDir( srcUV, tgtUV );
11945     double uvLen = uvDir.Magnitude();
11946     uvDir /= uvLen;
11947     edge._normal.SetCoord( uvDir.X(),uvDir.Y(), 0 );
11948     edge._len = uvLen;
11949
11950     //edge._pos.resize(1);
11951     edge._pos[0].SetCoord( tgtUV.X(), tgtUV.Y(), 0 );
11952
11953     // set UV of source node to target node
11954     SMDS_FacePositionPtr pos = tgtNode->GetPosition();
11955     pos->SetUParameter( srcUV.X() );
11956     pos->SetVParameter( srcUV.Y() );
11957   }
11958   else // _sWOL is TopAbs_EDGE
11959   {
11960     if ( tgtNode->GetPosition()->GetDim() != 1 ) // not inflated edge
11961     {
11962       edge._pos.clear();
11963       edge.Set( _LayerEdge::SHRUNK );
11964       return srcNode == tgtNode;
11965     }
11966     const TopoDS_Edge&    E = TopoDS::Edge( eos._sWOL );
11967     SMESHDS_SubMesh* edgeSM = getMeshDS()->MeshElements( E );
11968     if ( !edgeSM || edgeSM->NbElements() == 0 )
11969       return error(SMESH_Comment("Not meshed EDGE ") << getMeshDS()->ShapeToIndex( E ));
11970
11971     const SMDS_MeshNode* n2 = 0;
11972     SMDS_ElemIteratorPtr eIt = srcNode->GetInverseElementIterator(SMDSAbs_Edge);
11973     while ( eIt->more() && !n2 )
11974     {
11975       const SMDS_MeshElement* e = eIt->next();
11976       if ( !edgeSM->Contains(e)) continue;
11977       n2 = e->GetNode( 0 );
11978       if ( n2 == srcNode ) n2 = e->GetNode( 1 );
11979     }
11980     if ( !n2 )
11981       return error(SMESH_Comment("Wrongly meshed EDGE ") << getMeshDS()->ShapeToIndex( E ));
11982
11983     if ( n2 == tgtNode       || // for 3D_mesh_GHS3D_01/B1
11984          n2 == edge._nodes[1] ) // bos #20643
11985     {
11986       // shrunk by other SOLID
11987       edge.Set( _LayerEdge::SHRUNK ); // ???
11988       return true;
11989     }
11990
11991     double uSrc = helper.GetNodeU( E, srcNode, n2 );
11992     double uTgt = helper.GetNodeU( E, tgtNode, srcNode );
11993     double u2   = helper.GetNodeU( E, n2,      srcNode );
11994
11995     //edge._pos.clear();
11996
11997     if ( fabs( uSrc-uTgt ) < 0.99 * fabs( uSrc-u2 ))
11998     {
11999       // tgtNode is located so that it does not make faces with wrong orientation
12000       edge.Set( _LayerEdge::SHRUNK );
12001       return true;
12002     }
12003     //edge._pos.resize(1);
12004     edge._pos[0].SetCoord( U_TGT, uTgt );
12005     edge._pos[0].SetCoord( U_SRC, uSrc );
12006     edge._pos[0].SetCoord( LEN_TGT, fabs( uSrc-uTgt ));
12007
12008     edge._simplices.resize( 1 );
12009     edge._simplices[0]._nPrev = n2;
12010
12011     // set U of source node to the target node
12012     SMDS_EdgePositionPtr pos = tgtNode->GetPosition();
12013     pos->SetUParameter( uSrc );
12014   }
12015   return true;
12016 }
12017
12018 //================================================================================
12019 /*!
12020  * \brief Restore position of a sole node of a _LayerEdge based on _noShrinkShapes
12021  */
12022 //================================================================================
12023
12024 void _ViscousBuilder::restoreNoShrink( _LayerEdge& edge ) const
12025 {
12026   if ( edge._nodes.size() == 1 )
12027   {
12028     edge._pos.clear();
12029     edge._len = 0;
12030
12031     const SMDS_MeshNode* srcNode = edge._nodes[0];
12032     TopoDS_Shape S = SMESH_MesherHelper::GetSubShapeByNode( srcNode, getMeshDS() );
12033     if ( S.IsNull() ) return;
12034
12035     gp_Pnt p;
12036
12037     switch ( S.ShapeType() )
12038     {
12039     case TopAbs_EDGE:
12040     {
12041       double f,l;
12042       TopLoc_Location loc;
12043       Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( S ), loc, f, l );
12044       if ( curve.IsNull() ) return;
12045       SMDS_EdgePositionPtr ePos = srcNode->GetPosition();
12046       p = curve->Value( ePos->GetUParameter() );
12047       break;
12048     }
12049     case TopAbs_VERTEX:
12050     {
12051       p = BRep_Tool::Pnt( TopoDS::Vertex( S ));
12052       break;
12053     }
12054     default: return;
12055     }
12056     getMeshDS()->MoveNode( srcNode, p.X(), p.Y(), p.Z() );
12057     dumpMove( srcNode );
12058   }
12059 }
12060
12061 //================================================================================
12062 /*!
12063  * \brief Try to fix triangles with high aspect ratio by swapping diagonals
12064  */
12065 //================================================================================
12066
12067 void _ViscousBuilder::fixBadFaces(const TopoDS_Face&          F,
12068                                   SMESH_MesherHelper&         helper,
12069                                   const bool                  is2D,
12070                                   const int                   step,
12071                                   set<const SMDS_MeshNode*> * involvedNodes)
12072 {
12073   SMESH::Controls::AspectRatio qualifier;
12074   SMESH::Controls::TSequenceOfXYZ points(3), points1(3), points2(3);
12075   const double maxAspectRatio = is2D ? 4. : 2;
12076   _NodeCoordHelper xyz( F, helper, is2D );
12077
12078   // find bad triangles
12079
12080   vector< const SMDS_MeshElement* > badTrias;
12081   vector< double >                  badAspects;
12082   SMESHDS_SubMesh*      sm = helper.GetMeshDS()->MeshElements( F );
12083   SMDS_ElemIteratorPtr fIt = sm->GetElements();
12084   while ( fIt->more() )
12085   {
12086     const SMDS_MeshElement * f = fIt->next();
12087     if ( f->NbCornerNodes() != 3 ) continue;
12088     for ( int iP = 0; iP < 3; ++iP ) points(iP+1) = xyz( f->GetNode(iP));
12089     double aspect = qualifier.GetValue( points );
12090     if ( aspect > maxAspectRatio )
12091     {
12092       badTrias.push_back( f );
12093       badAspects.push_back( aspect );
12094     }
12095   }
12096   if ( step == 1 )
12097   {
12098     dumpFunction(SMESH_Comment("beforeSwapDiagonals_F")<<helper.GetSubShapeID());
12099     SMDS_ElemIteratorPtr fIt = sm->GetElements();
12100     while ( fIt->more() )
12101     {
12102       const SMDS_MeshElement * f = fIt->next();
12103       if ( f->NbCornerNodes() == 3 )
12104         dumpChangeNodes( f );
12105     }
12106     dumpFunctionEnd();
12107   }
12108   if ( badTrias.empty() )
12109     return;
12110
12111   // find couples of faces to swap diagonal
12112
12113   typedef pair < const SMDS_MeshElement* , const SMDS_MeshElement* > T2Trias;
12114   vector< T2Trias > triaCouples; 
12115
12116   TIDSortedElemSet involvedFaces, emptySet;
12117   for ( size_t iTia = 0; iTia < badTrias.size(); ++iTia )
12118   {
12119     T2Trias trias    [3];
12120     double  aspRatio [3];
12121     int i1, i2, i3;
12122
12123     if ( !involvedFaces.insert( badTrias[iTia] ).second )
12124       continue;
12125     for ( int iP = 0; iP < 3; ++iP )
12126       points(iP+1) = xyz( badTrias[iTia]->GetNode(iP));
12127
12128     // find triangles adjacent to badTrias[iTia] with better aspect ratio after diag-swaping
12129     int bestCouple = -1;
12130     for ( int iSide = 0; iSide < 3; ++iSide )
12131     {
12132       const SMDS_MeshNode* n1 = badTrias[iTia]->GetNode( iSide );
12133       const SMDS_MeshNode* n2 = badTrias[iTia]->GetNode(( iSide+1 ) % 3 );
12134       trias [iSide].first  = badTrias[iTia];
12135       trias [iSide].second = SMESH_MeshAlgos::FindFaceInSet( n1, n2, emptySet, involvedFaces,
12136                                                              & i1, & i2 );
12137       if (( ! trias[iSide].second ) ||
12138           ( trias[iSide].second->NbCornerNodes() != 3 ) ||
12139           ( ! sm->Contains( trias[iSide].second )))
12140         continue;
12141
12142       // aspect ratio of an adjacent tria
12143       for ( int iP = 0; iP < 3; ++iP )
12144         points2(iP+1) = xyz( trias[iSide].second->GetNode(iP));
12145       double aspectInit = qualifier.GetValue( points2 );
12146
12147       // arrange nodes as after diag-swaping
12148       if ( helper.WrapIndex( i1+1, 3 ) == i2 )
12149         i3 = helper.WrapIndex( i1-1, 3 );
12150       else
12151         i3 = helper.WrapIndex( i1+1, 3 );
12152       points1 = points;
12153       points1( 1+ iSide ) = points2( 1+ i3 );
12154       points2( 1+ i2    ) = points1( 1+ ( iSide+2 ) % 3 );
12155
12156       // aspect ratio after diag-swaping
12157       aspRatio[ iSide ] = qualifier.GetValue( points1 ) + qualifier.GetValue( points2 );
12158       if ( aspRatio[ iSide ] > aspectInit + badAspects[ iTia ] )
12159         continue;
12160
12161       // prevent inversion of a triangle
12162       gp_Vec norm1 = gp_Vec( points1(1), points1(3) ) ^ gp_Vec( points1(1), points1(2) );
12163       gp_Vec norm2 = gp_Vec( points2(1), points2(3) ) ^ gp_Vec( points2(1), points2(2) );
12164       if ( norm1 * norm2 < 0. && norm1.Angle( norm2 ) > 70./180.*M_PI )
12165         continue;
12166
12167       if ( bestCouple < 0 || aspRatio[ bestCouple ] > aspRatio[ iSide ] )
12168         bestCouple = iSide;
12169     }
12170
12171     if ( bestCouple >= 0 )
12172     {
12173       triaCouples.push_back( trias[bestCouple] );
12174       involvedFaces.insert ( trias[bestCouple].second );
12175     }
12176     else
12177     {
12178       involvedFaces.erase( badTrias[iTia] );
12179     }
12180   }
12181   if ( triaCouples.empty() )
12182     return;
12183
12184   // swap diagonals
12185
12186   SMESH_MeshEditor editor( helper.GetMesh() );
12187   dumpFunction(SMESH_Comment("beforeSwapDiagonals_F")<<helper.GetSubShapeID()<<"_"<<step);
12188   for ( size_t i = 0; i < triaCouples.size(); ++i )
12189   {
12190     dumpChangeNodes( triaCouples[i].first );
12191     dumpChangeNodes( triaCouples[i].second );
12192     editor.InverseDiag( triaCouples[i].first, triaCouples[i].second );
12193   }
12194
12195   if ( involvedNodes )
12196     for ( size_t i = 0; i < triaCouples.size(); ++i )
12197     {
12198       involvedNodes->insert( triaCouples[i].first->begin_nodes(),
12199                              triaCouples[i].first->end_nodes() );
12200       involvedNodes->insert( triaCouples[i].second->begin_nodes(),
12201                              triaCouples[i].second->end_nodes() );
12202     }
12203
12204   // just for debug dump resulting triangles
12205   dumpFunction(SMESH_Comment("swapDiagonals_F")<<helper.GetSubShapeID()<<"_"<<step);
12206   for ( size_t i = 0; i < triaCouples.size(); ++i )
12207   {
12208     dumpChangeNodes( triaCouples[i].first );
12209     dumpChangeNodes( triaCouples[i].second );
12210   }
12211 }
12212
12213 //================================================================================
12214 /*!
12215  * \brief Move target node to it's final position on the FACE during shrinking
12216  */
12217 //================================================================================
12218
12219 bool _LayerEdge::SetNewLength2d( Handle(Geom_Surface)& surface,
12220                                  const TopoDS_Face&    F,
12221                                  _EdgesOnShape&        eos,
12222                                  SMESH_MesherHelper&   helper )
12223 {
12224   if ( Is( SHRUNK ))
12225     return false; // already at the target position
12226
12227   SMDS_MeshNode* tgtNode = const_cast< SMDS_MeshNode*& >( _nodes.back() );
12228
12229   if ( eos.SWOLType() == TopAbs_FACE )
12230   {
12231     gp_XY    curUV = helper.GetNodeUV( F, tgtNode );
12232     gp_Pnt2d tgtUV( _pos[0].X(), _pos[0].Y() );
12233     gp_Vec2d uvDir( _normal.X(), _normal.Y() );
12234     const double uvLen = tgtUV.Distance( curUV );
12235     const double kSafe = Max( 0.5, 1. - 0.1 * _simplices.size() );
12236
12237     // Select shrinking step such that not to make faces with wrong orientation.
12238     double stepSize = 1e100;
12239     for ( size_t i = 0; i < _simplices.size(); ++i )
12240     {
12241       if ( !_simplices[i]._nPrev->isMarked() ||
12242            !_simplices[i]._nNext->isMarked() )
12243         continue; // simplex of quadrangle created by addBoundaryElements()
12244
12245       // find intersection of 2 lines: curUV-tgtUV and that connecting simplex nodes
12246       gp_XY uvN1 = helper.GetNodeUV( F, _simplices[i]._nPrev, tgtNode );
12247       gp_XY uvN2 = helper.GetNodeUV( F, _simplices[i]._nNext, tgtNode );
12248       gp_XY dirN = uvN2 - uvN1;
12249       double det = uvDir.Crossed( dirN );
12250       if ( Abs( det )  < std::numeric_limits<double>::min() ) continue;
12251       gp_XY dirN2Cur = curUV - uvN1;
12252       double step = dirN.Crossed( dirN2Cur ) / det;
12253       if ( step > 0 )
12254         stepSize = Min( step, stepSize );
12255     }
12256     gp_Pnt2d newUV;
12257     if ( uvLen <= stepSize )
12258     {
12259       newUV = tgtUV;
12260       Set( SHRUNK );
12261       //_pos.clear();
12262     }
12263     else if ( stepSize > 0 )
12264     {
12265       newUV = curUV + uvDir.XY() * stepSize * kSafe;
12266     }
12267     else
12268     {
12269       return true;
12270     }
12271     SMDS_FacePositionPtr pos = tgtNode->GetPosition();
12272     pos->SetUParameter( newUV.X() );
12273     pos->SetVParameter( newUV.Y() );
12274
12275 #ifdef __myDEBUG
12276     gp_Pnt p = surface->Value( newUV.X(), newUV.Y() );
12277     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
12278     dumpMove( tgtNode );
12279 #else
12280     if ( surface.IsNull() ) {}
12281 #endif
12282   }
12283   else // _sWOL is TopAbs_EDGE
12284   {
12285     const TopoDS_Edge&      E = TopoDS::Edge( eos._sWOL );
12286     const SMDS_MeshNode*   n2 = _simplices[0]._nPrev;
12287     SMDS_EdgePositionPtr tgtPos = tgtNode->GetPosition();
12288
12289     const double u2     = helper.GetNodeU( E, n2, tgtNode );
12290     const double uSrc   = _pos[0].Coord( U_SRC );
12291     const double lenTgt = _pos[0].Coord( LEN_TGT );
12292
12293     double newU = _pos[0].Coord( U_TGT );
12294     if ( lenTgt < 0.99 * fabs( uSrc-u2 )) // n2 got out of src-tgt range
12295     {
12296       Set( _LayerEdge::SHRUNK );
12297       //_pos.clear();
12298     }
12299     else
12300     {
12301       newU = 0.1 * tgtPos->GetUParameter() + 0.9 * u2;
12302     }
12303     tgtPos->SetUParameter( newU );
12304 #ifdef __myDEBUG
12305     gp_XY newUV = helper.GetNodeUV( F, tgtNode, _nodes[0]);
12306     gp_Pnt p = surface->Value( newUV.X(), newUV.Y() );
12307     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
12308     dumpMove( tgtNode );
12309 #endif
12310   }
12311
12312   return true;
12313 }
12314
12315 //================================================================================
12316 /*!
12317  * \brief Perform smooth on the FACE
12318  *  \retval bool - true if the node has been moved
12319  */
12320 //================================================================================
12321
12322 bool _SmoothNode::Smooth(int&                  nbBad,
12323                          Handle(Geom_Surface)& surface,
12324                          SMESH_MesherHelper&   helper,
12325                          const double          refSign,
12326                          SmoothType            how,
12327                          bool                  set3D)
12328 {
12329   const TopoDS_Face& face = TopoDS::Face( helper.GetSubShape() );
12330
12331   // get uv of surrounding nodes
12332   vector<gp_XY> uv( _simplices.size() );
12333   for ( size_t i = 0; i < _simplices.size(); ++i )
12334     uv[i] = helper.GetNodeUV( face, _simplices[i]._nPrev, _node );
12335
12336   // compute new UV for the node
12337   gp_XY newPos (0,0);
12338   if ( how == TFI && _simplices.size() == 4 )
12339   {
12340     gp_XY corners[4];
12341     for ( size_t i = 0; i < _simplices.size(); ++i )
12342       if ( _simplices[i]._nOpp )
12343         corners[i] = helper.GetNodeUV( face, _simplices[i]._nOpp, _node );
12344       else
12345         throw SALOME_Exception(LOCALIZED("TFI smoothing: _Simplex::_nOpp not set!"));
12346
12347     newPos = helper.calcTFI ( 0.5, 0.5,
12348                               corners[0], corners[1], corners[2], corners[3],
12349                               uv[1], uv[2], uv[3], uv[0] );
12350   }
12351   else if ( how == ANGULAR )
12352   {
12353     newPos = computeAngularPos( uv, helper.GetNodeUV( face, _node ), refSign );
12354   }
12355   else if ( how == CENTROIDAL && _simplices.size() > 3 )
12356   {
12357     // average centers of diagonals wieghted with their reciprocal lengths
12358     if ( _simplices.size() == 4 )
12359     {
12360       double w1 = 1. / ( uv[2]-uv[0] ).SquareModulus();
12361       double w2 = 1. / ( uv[3]-uv[1] ).SquareModulus();
12362       newPos = ( w1 * ( uv[2]+uv[0] ) + w2 * ( uv[3]+uv[1] )) / ( w1+w2 ) / 2;
12363     }
12364     else
12365     {
12366       double sumWeight = 0;
12367       int nb = _simplices.size() == 4 ? 2 : _simplices.size();
12368       for ( int i = 0; i < nb; ++i )
12369       {
12370         int iFrom = i + 2;
12371         int iTo   = i + _simplices.size() - 1;
12372         for ( int j = iFrom; j < iTo; ++j )
12373         {
12374           int i2 = SMESH_MesherHelper::WrapIndex( j, _simplices.size() );
12375           double w = 1. / ( uv[i]-uv[i2] ).SquareModulus();
12376           sumWeight += w;
12377           newPos += w * ( uv[i]+uv[i2] );
12378         }
12379       }
12380       newPos /= 2 * sumWeight; // 2 is to get a middle between uv's
12381     }
12382   }
12383   else
12384   {
12385     // Laplacian smooth
12386     for ( size_t i = 0; i < _simplices.size(); ++i )
12387       newPos += uv[i];
12388     newPos /= _simplices.size();
12389   }
12390
12391   // count quality metrics (orientation) of triangles around the node
12392   int nbOkBefore = 0;
12393   gp_XY tgtUV = helper.GetNodeUV( face, _node );
12394   for ( size_t i = 0; i < _simplices.size(); ++i )
12395     nbOkBefore += _simplices[i].IsForward( tgtUV, _node, face, helper, refSign );
12396
12397   int nbOkAfter = 0;
12398   for ( size_t i = 0; i < _simplices.size(); ++i )
12399     nbOkAfter += _simplices[i].IsForward( newPos, _node, face, helper, refSign );
12400
12401   if ( nbOkAfter < nbOkBefore )
12402   {
12403     nbBad += _simplices.size() - nbOkBefore;
12404     return false;
12405   }
12406
12407   SMDS_FacePositionPtr pos = _node->GetPosition();
12408   pos->SetUParameter( newPos.X() );
12409   pos->SetVParameter( newPos.Y() );
12410
12411 #ifdef __myDEBUG
12412   set3D = true;
12413 #endif
12414   if ( set3D )
12415   {
12416     gp_Pnt p = surface->Value( newPos.X(), newPos.Y() );
12417     const_cast< SMDS_MeshNode* >( _node )->setXYZ( p.X(), p.Y(), p.Z() );
12418     dumpMove( _node );
12419   }
12420
12421   nbBad += _simplices.size() - nbOkAfter;
12422   return ( (tgtUV-newPos).SquareModulus() > 1e-10 );
12423 }
12424
12425 //================================================================================
12426 /*!
12427  * \brief Computes new UV using angle based smoothing technique
12428  */
12429 //================================================================================
12430
12431 gp_XY _SmoothNode::computeAngularPos(vector<gp_XY>& uv,
12432                                      const gp_XY&   uvToFix,
12433                                      const double   refSign)
12434 {
12435   uv.push_back( uv.front() );
12436
12437   vector< gp_XY >  edgeDir ( uv.size() );
12438   vector< double > edgeSize( uv.size() );
12439   for ( size_t i = 1; i < edgeDir.size(); ++i )
12440   {
12441     edgeDir [i-1] = uv[i] - uv[i-1];
12442     edgeSize[i-1] = edgeDir[i-1].Modulus();
12443     if ( edgeSize[i-1] < numeric_limits<double>::min() )
12444       edgeDir[i-1].SetX( 100 );
12445     else
12446       edgeDir[i-1] /= edgeSize[i-1] * refSign;
12447   }
12448   edgeDir.back()  = edgeDir.front();
12449   edgeSize.back() = edgeSize.front();
12450
12451   gp_XY  newPos(0,0);
12452   double sumWgt = 0;
12453   for ( size_t i = 1; i < edgeDir.size(); ++i )
12454   {
12455     const int i1 = i-1;
12456     if ( edgeDir[i1].X() > 1. ) continue;
12457     while ( edgeDir[i].X() > 1. && ++i < edgeDir.size() );
12458     if ( i == edgeDir.size() ) break;
12459     gp_XY p = uv[i];
12460     gp_XY norm1( -edgeDir[i1].Y(), edgeDir[i1].X() );
12461     gp_XY norm2( -edgeDir[i ].Y(), edgeDir[i ].X() );
12462     gp_XY bisec = norm1 + norm2;
12463     double bisecSize = bisec.Modulus();
12464     if ( bisecSize < numeric_limits<double>::min() )
12465     {
12466       bisec = -edgeDir[i1] + edgeDir[i];
12467       bisecSize = bisec.Modulus();
12468     }
12469     bisec /= bisecSize;
12470
12471     gp_XY   dirToN = uvToFix - p;
12472     double distToN = bisec * dirToN;
12473     if ( bisec * dirToN < 0 )
12474       distToN = -distToN;
12475
12476     double wgt = edgeSize[i1] + edgeSize[i];
12477     newPos += ( p + bisec * distToN ) * wgt;
12478     sumWgt += wgt;
12479   }
12480   newPos /= sumWgt;
12481   return newPos;
12482 }
12483
12484 //================================================================================
12485 /*!
12486  * \brief Keep a _LayerEdge inflated along the EDGE
12487  */
12488 //================================================================================
12489
12490 void _Shrinker1D::AddEdge( const _LayerEdge*   e,
12491                            _EdgesOnShape&      eos,
12492                            SMESH_MesherHelper& helper )
12493 {
12494   // init
12495   if ( _nodes.empty() )
12496   {
12497     _edges[0] = _edges[1] = 0;
12498     _done = false;
12499   }
12500   // check _LayerEdge
12501   if ( e == _edges[0] || e == _edges[1] || e->_nodes.size() < 2 )
12502     return;
12503   if ( eos.SWOLType() != TopAbs_EDGE )
12504     throw SALOME_Exception(LOCALIZED("Wrong _LayerEdge is added"));
12505   if ( _edges[0] && !_geomEdge.IsSame( eos._sWOL ))
12506     throw SALOME_Exception(LOCALIZED("Wrong _LayerEdge is added"));
12507
12508   // store _LayerEdge
12509   _geomEdge = TopoDS::Edge( eos._sWOL );
12510   double f,l;
12511   BRep_Tool::Range( _geomEdge, f,l );
12512   double u = helper.GetNodeU( _geomEdge, e->_nodes[0], e->_nodes.back());
12513   _edges[ u < 0.5*(f+l) ? 0 : 1 ] = e;
12514
12515   // Check if the nodes are already shrunk by another SOLID
12516
12517   const SMDS_MeshNode* tgtNode0 = TgtNode( 0 );
12518   const SMDS_MeshNode* tgtNode1 = TgtNode( 1 );
12519
12520   _done = (( tgtNode0 && tgtNode0->NbInverseElements( SMDSAbs_Edge ) == 2 ) ||
12521            ( tgtNode1 && tgtNode1->NbInverseElements( SMDSAbs_Edge ) == 2 ));
12522   if ( _done )
12523     _nodes.resize( 1, nullptr );
12524
12525   // Update _nodes
12526
12527   if ( _nodes.empty() )
12528   {
12529     SMESHDS_SubMesh * eSubMesh = helper.GetMeshDS()->MeshElements( _geomEdge );
12530     if ( !eSubMesh || eSubMesh->NbNodes() < 1 )
12531       return;
12532     TopLoc_Location loc;
12533     Handle(Geom_Curve) C = BRep_Tool::Curve( _geomEdge, loc, f,l );
12534     GeomAdaptor_Curve aCurve(C, f,l);
12535     const double totLen = GCPnts_AbscissaPoint::Length(aCurve, f, l);
12536
12537     smIdType nbExpectNodes = eSubMesh->NbNodes();
12538     _initU  .reserve( nbExpectNodes );
12539     _normPar.reserve( nbExpectNodes );
12540     _nodes  .reserve( nbExpectNodes );
12541     SMDS_NodeIteratorPtr nIt = eSubMesh->GetNodes();
12542     while ( nIt->more() )
12543     {
12544       const SMDS_MeshNode* node = nIt->next();
12545
12546       // skip refinement nodes
12547       if ( node->NbInverseElements(SMDSAbs_Edge) == 0 ||
12548            node == tgtNode0 || node == tgtNode1 )
12549         continue;
12550       bool hasMarkedFace = false;
12551       SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
12552       while ( fIt->more() && !hasMarkedFace )
12553         hasMarkedFace = fIt->next()->isMarked();
12554       if ( !hasMarkedFace )
12555         continue;
12556
12557       _nodes.push_back( node );
12558       _initU.push_back( helper.GetNodeU( _geomEdge, node ));
12559       double len = GCPnts_AbscissaPoint::Length(aCurve, f, _initU.back());
12560       _normPar.push_back(  len / totLen );
12561     }
12562   }
12563   else
12564   {
12565     // remove target node of the _LayerEdge from _nodes
12566     size_t nbFound = 0;
12567     for ( size_t i = 0; i < _nodes.size(); ++i )
12568       if ( !_nodes[i] || _nodes[i] == tgtNode0 || _nodes[i] == tgtNode1 )
12569         _nodes[i] = 0, nbFound++;
12570     if ( nbFound == _nodes.size() )
12571       _nodes.clear();
12572   }
12573 }
12574
12575 //================================================================================
12576 /*!
12577  * \brief Move nodes on EDGE from ends where _LayerEdge's are inflated
12578  */
12579 //================================================================================
12580
12581 void _Shrinker1D::Compute(bool set3D, SMESH_MesherHelper& helper)
12582 {
12583   if ( _done || _nodes.empty())
12584     return;
12585   const _LayerEdge* e = _edges[0];
12586   if ( !e ) e = _edges[1];
12587   if ( !e ) return;
12588
12589   _done =  (( !_edges[0] || _edges[0]->Is( _LayerEdge::SHRUNK )) &&
12590             ( !_edges[1] || _edges[1]->Is( _LayerEdge::SHRUNK )));
12591
12592   double f,l;
12593   if ( set3D || _done )
12594   {
12595     dumpFunction(SMESH_Comment("shrink1D_E") << helper.GetMeshDS()->ShapeToIndex( _geomEdge )<<
12596                  "_F" << helper.GetSubShapeID() );
12597     Handle(Geom_Curve) C = BRep_Tool::Curve(_geomEdge, f,l);
12598     GeomAdaptor_Curve aCurve(C, f,l);
12599
12600     if ( _edges[0] )
12601       f = helper.GetNodeU( _geomEdge, _edges[0]->_nodes.back(), _nodes[0] );
12602     if ( _edges[1] )
12603       l = helper.GetNodeU( _geomEdge, _edges[1]->_nodes.back(), _nodes.back() );
12604     double totLen = GCPnts_AbscissaPoint::Length( aCurve, f, l );
12605
12606     for ( size_t i = 0; i < _nodes.size(); ++i )
12607     {
12608       if ( !_nodes[i] ) continue;
12609       double len = totLen * _normPar[i];
12610       GCPnts_AbscissaPoint discret( aCurve, len, f );
12611       if ( !discret.IsDone() )
12612         return throw SALOME_Exception(LOCALIZED("GCPnts_AbscissaPoint failed"));
12613       double u = discret.Parameter();
12614       SMDS_EdgePositionPtr pos = _nodes[i]->GetPosition();
12615       pos->SetUParameter( u );
12616       gp_Pnt p = C->Value( u );
12617       const_cast< SMDS_MeshNode*>( _nodes[i] )->setXYZ( p.X(), p.Y(), p.Z() );
12618       dumpMove( _nodes[i] );
12619     }
12620     dumpFunctionEnd();
12621   }
12622   else
12623   {
12624     BRep_Tool::Range( _geomEdge, f,l );
12625     if ( _edges[0] )
12626       f = helper.GetNodeU( _geomEdge, _edges[0]->_nodes.back(), _nodes[0] );
12627     if ( _edges[1] )
12628       l = helper.GetNodeU( _geomEdge, _edges[1]->_nodes.back(), _nodes.back() );
12629     
12630     for ( size_t i = 0; i < _nodes.size(); ++i )
12631     {
12632       if ( !_nodes[i] ) continue;
12633       double u = f * ( 1-_normPar[i] ) + l * _normPar[i];
12634       SMDS_EdgePositionPtr pos = _nodes[i]->GetPosition();
12635       pos->SetUParameter( u );
12636     }
12637   }
12638 }
12639
12640 //================================================================================
12641 /*!
12642  * \brief Restore initial parameters of nodes on EDGE
12643  */
12644 //================================================================================
12645
12646 void _Shrinker1D::RestoreParams()
12647 {
12648   if ( _done )
12649     for ( size_t i = 0; i < _nodes.size(); ++i )
12650     {
12651       if ( !_nodes[i] ) continue;
12652       SMDS_EdgePositionPtr pos = _nodes[i]->GetPosition();
12653       pos->SetUParameter( _initU[i] );
12654     }
12655   _done = false;
12656 }
12657
12658 //================================================================================
12659 /*!
12660  * \brief Replace source nodes by target nodes in shrunk mesh edges
12661  */
12662 //================================================================================
12663
12664 void _Shrinker1D::SwapSrcTgtNodes( SMESHDS_Mesh* mesh )
12665 {
12666   const SMDS_MeshNode* nodes[3];
12667   for ( int i = 0; i < 2; ++i )
12668   {
12669     if ( !_edges[i] ) continue;
12670
12671     SMESHDS_SubMesh * eSubMesh = mesh->MeshElements( _geomEdge );
12672     if ( !eSubMesh ) return;
12673     const SMDS_MeshNode* srcNode = _edges[i]->_nodes[0];
12674     const SMDS_MeshNode* tgtNode = _edges[i]->_nodes.back();
12675     const SMDS_MeshNode* scdNode = _edges[i]->_nodes[1];
12676     SMDS_ElemIteratorPtr eIt = srcNode->GetInverseElementIterator(SMDSAbs_Edge);
12677     while ( eIt->more() )
12678     {
12679       const SMDS_MeshElement* e = eIt->next();
12680       if ( !eSubMesh->Contains( e ) || e->GetNodeIndex( scdNode ) >= 0 )
12681           continue;
12682       SMDS_ElemIteratorPtr nIt = e->nodesIterator();
12683       for ( int iN = 0; iN < e->NbNodes(); ++iN )
12684       {
12685         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
12686         nodes[iN] = ( n == srcNode ? tgtNode : n );
12687       }
12688       mesh->ChangeElementNodes( e, nodes, e->NbNodes() );
12689     }
12690   }
12691 }
12692
12693 //================================================================================
12694 /*!
12695  * \brief Setup quadPoints
12696  */
12697 //================================================================================
12698
12699 _Mapper2D::_Mapper2D( const TParam2ColumnMap & param2ColumnMap, const TNode2Edge& n2eMap )
12700 {
12701   size_t i, iSize = _quadPoints.iSize = param2ColumnMap.size();
12702   size_t j, jSize = _quadPoints.jSize = param2ColumnMap.begin()->second.size();
12703   if ( _quadPoints.iSize < 3 ||
12704        _quadPoints.jSize < 3 )
12705     return;
12706   _quadPoints.uv_grid.resize( iSize * jSize );
12707
12708   // set nodes
12709   i = 0;
12710   for ( auto & u_columnNodes : param2ColumnMap )
12711   {
12712     for ( j = 0; j < u_columnNodes.second.size(); ++j )
12713       _quadPoints.UVPt( i, j ).node = u_columnNodes.second[ j ];
12714     ++i;
12715   }
12716
12717   // compute x parameter on borders
12718   uvPnt( 0, 0       ).x = 0;
12719   uvPnt( 0, jSize-1 ).x = 0;
12720   gp_Pnt p0, pPrev0 = SMESH_NodeXYZ( uvPnt( 0, 0       ).node );
12721   gp_Pnt p1, pPrev1 = SMESH_NodeXYZ( uvPnt( 0, jSize-1 ).node );
12722   for ( i = 1; i < iSize; ++i )
12723   {
12724     p0 = SMESH_NodeXYZ( uvPnt( i, 0       ).node );
12725     p1 = SMESH_NodeXYZ( uvPnt( i, jSize-1 ).node );
12726     uvPnt( i, 0       ).x = uvPnt( i-1, 0       ).x + p0.Distance( pPrev0 );
12727     uvPnt( i, jSize-1 ).x = uvPnt( i-1, jSize-1 ).x + p1.Distance( pPrev1 );
12728     pPrev0 = p0;
12729     pPrev1 = p1;
12730   }
12731   for ( i = 1; i < iSize-1; ++i )
12732   {
12733     uvPnt( i, 0       ).x /= uvPnt( iSize-1, 0       ).x;
12734     uvPnt( i, jSize-1 ).x /= uvPnt( iSize-1, jSize-1 ).x;
12735     uvPnt( i, 0       ).y = 0;
12736     uvPnt( i, jSize-1 ).y = 1;
12737   }
12738
12739   // compute y parameter on borders
12740   uvPnt( 0,       0 ).y = 0;
12741   uvPnt( iSize-1, 0 ).y = 0;
12742   pPrev0 = SMESH_NodeXYZ( uvPnt( 0,       0 ).node );
12743   pPrev1 = SMESH_NodeXYZ( uvPnt( iSize-1, 0 ).node );
12744   for ( j = 1; j < jSize; ++j )
12745   {
12746     p0 = SMESH_NodeXYZ( uvPnt( 0,       j ).node );
12747     p1 = SMESH_NodeXYZ( uvPnt( iSize-1, j ).node );
12748     uvPnt( 0,       j ).y = uvPnt( 0,       j-1 ).y + p0.Distance( pPrev0 );
12749     uvPnt( iSize-1, j ).y = uvPnt( iSize-1, j-1 ).y + p1.Distance( pPrev1 );
12750     pPrev0 = p0;
12751     pPrev1 = p1;
12752   }
12753   for ( j = 1; j < jSize-1; ++j )
12754   {
12755     uvPnt( 0,       j ).y /= uvPnt( 0,       jSize-1 ).y;
12756     uvPnt( iSize-1, j ).y /= uvPnt( iSize-1, jSize-1 ).y;
12757     uvPnt( 0,       j ).x = 0;
12758     uvPnt( iSize-1, j ).x = 1;
12759   }
12760
12761   // compute xy of internal nodes
12762   for ( i = 1; i < iSize-1; ++i )
12763   {
12764     const double x0 = uvPnt( i, 0       ).x;
12765     const double x1 = uvPnt( i, jSize-1 ).x;
12766     for ( j = 1; j < jSize-1; ++j )
12767     {
12768       const double y0 = uvPnt( 0,       j ).y;
12769       const double y1 = uvPnt( iSize-1, j ).y;
12770       double x = (x0 + y0 * (x1 - x0)) / (1 - (y1 - y0) * (x1 - x0));
12771       double y = y0 + x * (y1 - y0);
12772       uvPnt( i, j ).x = x;
12773       uvPnt( i, j ).y = y;
12774     }
12775   }
12776
12777   // replace base nodes with target ones
12778   for ( i = 0; i < iSize; ++i )
12779     for ( j = 0; j < jSize; ++j )
12780     {
12781       auto n2e = n2eMap.find( uvPnt( i, j ).node );
12782       uvPnt( i, j ).node = n2e->second->_nodes.back();
12783     }
12784
12785   return;
12786 }
12787
12788 //================================================================================
12789 /*!
12790  * \brief Compute positions of nodes of 2D structured mesh using TFI
12791  */
12792 //================================================================================
12793
12794 bool _Mapper2D::ComputeNodePositions()
12795 {
12796   if ( _quadPoints.uv_grid.empty() )
12797     return true;
12798
12799   size_t i, iSize = _quadPoints.iSize;
12800   size_t j, jSize = _quadPoints.jSize;
12801
12802   SMESH_NodeXYZ a0 ( uvPnt( 0,       0       ).node );
12803   SMESH_NodeXYZ a1 ( uvPnt( iSize-1, 0       ).node );
12804   SMESH_NodeXYZ a2 ( uvPnt( iSize-1, jSize-1 ).node );
12805   SMESH_NodeXYZ a3 ( uvPnt( 0,       jSize-1 ).node );
12806
12807   for ( i = 1; i < iSize-1; ++i )
12808   {
12809     SMESH_NodeXYZ p0 ( uvPnt( i, 0       ).node );
12810     SMESH_NodeXYZ p2 ( uvPnt( i, jSize-1 ).node );
12811     for ( j = 1; j < jSize-1; ++j )
12812     {
12813       SMESH_NodeXYZ p1 ( uvPnt( iSize-1, j ).node );
12814       SMESH_NodeXYZ p3 ( uvPnt( 0,       j ).node );
12815       double x = uvPnt( i, j ).x;
12816       double y = uvPnt( i, j ).y;
12817
12818       gp_XYZ p = SMESH_MesherHelper::calcTFI( x, y, a0,a1,a2,a3, p0,p1,p2,p3 );
12819       const_cast< SMDS_MeshNode* >( uvPnt( i, j ).node )->setXYZ( p.X(), p.Y(), p.Z() );
12820
12821       dumpMove( uvPnt( i, j ).node );
12822     }
12823   }
12824   return true;
12825 }
12826
12827 //================================================================================
12828 /*!
12829  * \brief Creates 2D and 1D elements on boundaries of new prisms
12830  */
12831 //================================================================================
12832
12833 bool _ViscousBuilder::addBoundaryElements(_SolidData& data)
12834 {
12835   SMESH_MesherHelper helper( *_mesh );
12836
12837   vector< const SMDS_MeshNode* > faceNodes;
12838
12839   //for ( size_t i = 0; i < _sdVec.size(); ++i )
12840   {
12841     //_SolidData& data = _sdVec[i];
12842     TopTools_IndexedMapOfShape geomEdges;
12843     TopExp::MapShapes( data._solid, TopAbs_EDGE, geomEdges );
12844     for ( int iE = 1; iE <= geomEdges.Extent(); ++iE )
12845     {
12846       const TopoDS_Edge& E = TopoDS::Edge( geomEdges(iE));
12847       const TGeomID edgeID = getMeshDS()->ShapeToIndex( E );
12848       if ( data._noShrinkShapes.count( edgeID ))
12849         continue;
12850
12851       // Get _LayerEdge's based on E
12852
12853       map< double, const SMDS_MeshNode* > u2nodes;
12854       if ( !SMESH_Algo::GetSortedNodesOnEdge( getMeshDS(), E, /*ignoreMedium=*/false, u2nodes))
12855         continue;
12856
12857       vector< _LayerEdge* > ledges; ledges.reserve( u2nodes.size() );
12858       TNode2Edge & n2eMap = data._n2eMap;
12859       map< double, const SMDS_MeshNode* >::iterator u2n = u2nodes.begin();
12860       {
12861         //check if 2D elements are needed on E
12862         TNode2Edge::iterator n2e = n2eMap.find( u2n->second );
12863         if ( n2e == n2eMap.end() ) continue; // no layers on vertex
12864         ledges.push_back( n2e->second );
12865         u2n++;
12866         if (( n2e = n2eMap.find( u2n->second )) == n2eMap.end() )
12867           continue; // no layers on E
12868         ledges.push_back( n2eMap[ u2n->second ]);
12869
12870         const SMDS_MeshNode* tgtN0 = ledges[0]->_nodes.back();
12871         const SMDS_MeshNode* tgtN1 = ledges[1]->_nodes.back();
12872         int nbSharedPyram = 0;
12873         SMDS_ElemIteratorPtr vIt = tgtN1->GetInverseElementIterator(SMDSAbs_Volume);
12874         while ( vIt->more() )
12875         {
12876           const SMDS_MeshElement* v = vIt->next();
12877           nbSharedPyram += int( v->GetNodeIndex( tgtN0 ) >= 0 );
12878         }
12879         if ( nbSharedPyram > 1 )
12880           continue; // not free border of the pyramid
12881
12882         faceNodes.clear();
12883         faceNodes.push_back( ledges[0]->_nodes[0] );
12884         faceNodes.push_back( ledges[1]->_nodes[0] );
12885         if ( ledges[0]->_nodes.size() > 1 ) faceNodes.push_back( ledges[0]->_nodes[1] );
12886         if ( ledges[1]->_nodes.size() > 1 ) faceNodes.push_back( ledges[1]->_nodes[1] );
12887
12888         if ( getMeshDS()->FindElement( faceNodes, SMDSAbs_Face, /*noMedium=*/true))
12889           continue; // faces already created
12890       }
12891       for ( ++u2n; u2n != u2nodes.end(); ++u2n )
12892         ledges.push_back( n2eMap[ u2n->second ]);
12893
12894       // Find out orientation and type of face to create
12895
12896       bool reverse = false, isOnFace;
12897       TopoDS_Shape F;
12898
12899       map< TGeomID, TopoDS_Shape >::iterator e2f = data._shrinkShape2Shape.find( edgeID );
12900       if (( isOnFace = ( e2f != data._shrinkShape2Shape.end() )))
12901       {
12902         F = e2f->second.Oriented( TopAbs_FORWARD );
12903         reverse = ( helper.GetSubShapeOri( F, E ) == TopAbs_REVERSED );
12904         if ( helper.GetSubShapeOri( data._solid, F ) == TopAbs_REVERSED )
12905           reverse = !reverse, F.Reverse();
12906         if ( helper.IsReversedSubMesh( TopoDS::Face(F) ))
12907           reverse = !reverse;
12908       }
12909       else if ( !data._ignoreFaceIds.count( e2f->first ))
12910       {
12911         // find FACE with layers sharing E
12912         PShapeIteratorPtr fIt = helper.GetAncestors( E, *_mesh, TopAbs_FACE, &data._solid );
12913         if ( fIt->more() )
12914           F = *( fIt->next() );
12915       }
12916       // Find the sub-mesh to add new faces
12917       SMESHDS_SubMesh* sm = 0;
12918       if ( isOnFace )
12919         sm = getMeshDS()->MeshElements( F );
12920       else
12921         sm = data._proxyMesh->getFaceSubM( TopoDS::Face(F), /*create=*/true );
12922       if ( !sm )
12923         return error("error in addBoundaryElements()", data._index);
12924
12925       // Find a proxy sub-mesh of the FACE of an adjacent SOLID, which will use the new boundary
12926       // faces for 3D meshing (PAL23414)
12927       SMESHDS_SubMesh* adjSM = 0;
12928       if ( isOnFace )
12929       {
12930         const TGeomID   faceID = sm->GetID();
12931         PShapeIteratorPtr soIt = helper.GetAncestors( F, *_mesh, TopAbs_SOLID );
12932         while ( const TopoDS_Shape* solid = soIt->next() )
12933           if ( !solid->IsSame( data._solid ))
12934           {
12935             size_t iData = _solids.FindIndex( *solid ) - 1;
12936             if ( iData < _sdVec.size() &&
12937                  _sdVec[ iData ]._ignoreFaceIds.count( faceID ) &&
12938                  _sdVec[ iData ]._shrinkShape2Shape.count( edgeID ) == 0 )
12939             {
12940               SMESH_ProxyMesh::SubMesh* proxySub =
12941                 _sdVec[ iData ]._proxyMesh->getFaceSubM( TopoDS::Face( F ), /*create=*/false);
12942               if ( proxySub && proxySub->NbElements() > 0 )
12943                 adjSM = proxySub;
12944             }
12945           }
12946       }
12947
12948       // Make faces
12949       const int dj1 = reverse ? 0 : 1;
12950       const int dj2 = reverse ? 1 : 0;
12951       vector< const SMDS_MeshElement*> ff; // new faces row
12952       SMESHDS_Mesh* m = getMeshDS();
12953       for ( size_t j = 1; j < ledges.size(); ++j )
12954       {
12955         vector< const SMDS_MeshNode*>&  nn1 = ledges[j-dj1]->_nodes;
12956         vector< const SMDS_MeshNode*>&  nn2 = ledges[j-dj2]->_nodes;
12957         ff.resize( std::max( nn1.size(), nn2.size() ), NULL );
12958         if ( nn1.size() == nn2.size() )
12959         {
12960           if ( isOnFace )
12961             for ( size_t z = 1; z < nn1.size(); ++z )
12962               sm->AddElement( ff[z-1] = m->AddFace( nn1[z-1], nn2[z-1], nn2[z], nn1[z] ));
12963           else
12964             for ( size_t z = 1; z < nn1.size(); ++z )
12965               sm->AddElement( new SMDS_FaceOfNodes( nn1[z-1], nn2[z-1], nn2[z], nn1[z] ));
12966         }
12967         else if ( nn1.size() == 1 )
12968         {
12969           if ( isOnFace )
12970             for ( size_t z = 1; z < nn2.size(); ++z )
12971               sm->AddElement( ff[z-1] = m->AddFace( nn1[0], nn2[z-1], nn2[z] ));
12972           else
12973             for ( size_t z = 1; z < nn2.size(); ++z )
12974               sm->AddElement( new SMDS_FaceOfNodes( nn1[0], nn2[z-1], nn2[z] ));
12975         }
12976         else
12977         {
12978           if ( isOnFace )
12979             for ( size_t z = 1; z < nn1.size(); ++z )
12980               sm->AddElement( ff[z-1] = m->AddFace( nn1[z-1], nn2[0], nn1[z] ));
12981           else
12982             for ( size_t z = 1; z < nn1.size(); ++z )
12983               sm->AddElement( new SMDS_FaceOfNodes( nn1[z-1], nn2[0], nn2[z] ));
12984         }
12985
12986         if ( adjSM ) // add faces to a proxy SM of the adjacent SOLID
12987         {
12988           for ( size_t z = 0; z < ff.size(); ++z )
12989             if ( ff[ z ])
12990               adjSM->AddElement( ff[ z ]);
12991           ff.clear();
12992         }
12993       }
12994
12995       // Make edges
12996       for ( int isFirst = 0; isFirst < 2; ++isFirst )
12997       {
12998         _LayerEdge* edge = isFirst ? ledges.front() : ledges.back();
12999         _EdgesOnShape* eos = data.GetShapeEdges( edge );
13000         if ( eos && eos->SWOLType() == TopAbs_EDGE )
13001         {
13002           vector< const SMDS_MeshNode*>&  nn = edge->_nodes;
13003           if ( nn.size() < 2 || nn[1]->NbInverseElements( SMDSAbs_Edge ) >= 2 )
13004             continue;
13005           helper.SetSubShape( eos->_sWOL );
13006           helper.SetElementsOnShape( true );
13007           for ( size_t z = 1; z < nn.size(); ++z )
13008             helper.AddEdge( nn[z-1], nn[z] );
13009         }
13010       }
13011
13012     } // loop on EDGE's
13013   } // loop on _SolidData's
13014
13015   return true;
13016 }