]> SALOME platform Git repositories - modules/smesh.git/blob - src/StdMeshers/StdMeshers_ViscousLayers.cxx
Salome HOME
[bos #41867][CEA] Handle properly 3D VL when certain edges are not meshed.
[modules/smesh.git] / src / StdMeshers / StdMeshers_ViscousLayers.cxx
1 // Copyright (C) 2007-2024  CEA, EDF, 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       return StdMeshers_ViscousLayers::Get1stLayerThickness( T, f, N );
646     }
647
648     bool   UseSurfaceNormal()  const
649     { return _method == StdMeshers_ViscousLayers::SURF_OFFSET_SMOOTH; }
650     bool   ToSmooth()          const
651     { return _method == StdMeshers_ViscousLayers::SURF_OFFSET_SMOOTH; }
652     bool   IsOffsetMethod()    const
653     { return _method == StdMeshers_ViscousLayers::FACE_OFFSET; }
654
655     bool operator==( const AverageHyp& other ) const
656     {
657       return ( _nbLayers == other._nbLayers &&
658                _method   == other._method   &&
659                Equals( GetTotalThickness(), other.GetTotalThickness() ) &&
660                Equals( GetStretchFactor(), other.GetStretchFactor() ));
661     }
662     static bool Equals( double v1, double v2 ) { return Abs( v1 - v2 ) < 0.01 * ( v1 + v2 ); }
663
664   private:
665     int         _nbLayers, _nbHyps, _method;
666     double      _thickness, _stretchFactor;
667     std::string _groupName;
668   };
669
670   //--------------------------------------------------------------------------------
671   /*!
672    * \brief _LayerEdge's on a shape and other shape data
673    */
674   struct _EdgesOnShape
675   {
676     vector< _LayerEdge* > _edges;
677
678     TopoDS_Shape          _shape;
679     TGeomID               _shapeID;
680     SMESH_subMesh *       _subMesh;
681     // face or edge w/o layer along or near which _edges are inflated
682     TopoDS_Shape          _sWOL;
683     bool                  _isRegularSWOL; // w/o singularities
684     // averaged StdMeshers_ViscousLayers parameters
685     AverageHyp            _hyp;
686     bool                  _toSmooth;
687     _Smoother1D*          _edgeSmoother;
688     vector< _EdgesOnShape* > _eosConcaVer; // edges at concave VERTEXes of a FACE
689     vector< _EdgesOnShape* > _eosC1; // to smooth together several C1 continues shapes
690
691     typedef std::unordered_map< const SMDS_MeshElement*, gp_XYZ > TFace2NormMap;
692     TFace2NormMap            _faceNormals; // if _shape is FACE
693     vector< _EdgesOnShape* > _faceEOS; // to get _faceNormals of adjacent FACEs
694
695     Handle(ShapeAnalysis_Surface) _offsetSurf;
696     _LayerEdge*                   _edgeForOffset;
697     double                        _offsetValue;
698     _Mapper2D*                    _mapper2D;
699
700     _SolidData*            _data; // parent SOLID
701
702     _LayerEdge*      operator[](size_t i) const { return (_LayerEdge*) _edges[i]; }
703     size_t           size() const { return _edges.size(); }
704     TopAbs_ShapeEnum ShapeType() const
705     { return _shape.IsNull() ? TopAbs_SHAPE : _shape.ShapeType(); }
706     TopAbs_ShapeEnum SWOLType() const
707     { return _sWOL.IsNull() ? TopAbs_SHAPE : _sWOL.ShapeType(); }
708     bool             HasC1( const _EdgesOnShape* other ) const
709     { return std::find( _eosC1.begin(), _eosC1.end(), other ) != _eosC1.end(); }
710     bool             GetNormal( const SMDS_MeshElement* face, gp_Vec& norm );
711     _SolidData&      GetData() const { return *_data; }
712     char             ShapeTypeLetter() const
713     { switch ( ShapeType() ) { case TopAbs_FACE: return 'F'; case TopAbs_EDGE: return 'E';
714       case TopAbs_VERTEX: return 'V'; default: return 'S'; }}
715
716     _EdgesOnShape(): _shapeID(-1), _subMesh(0), _toSmooth(false), _edgeSmoother(0), _mapper2D(0) {}
717     ~_EdgesOnShape();
718   };
719
720   //--------------------------------------------------------------------------------
721   /*!
722    * \brief Convex FACE whose radius of curvature is less than the thickness of
723    *        layers. It is used to detect distortion of prisms based on a convex
724    *        FACE and to update normals to enable further increasing the thickness
725    */
726   struct _ConvexFace
727   {
728     TopoDS_Face                     _face;
729
730     // edges whose _simplices are used to detect prism distortion
731     vector< _LayerEdge* >           _simplexTestEdges;
732
733     // map a sub-shape to _SolidData::_edgesOnShape
734     map< TGeomID, _EdgesOnShape* >  _subIdToEOS;
735
736     bool                            _isTooCurved;
737     bool                            _normalsFixed;
738     bool                            _normalsFixedOnBorders; // used in putOnOffsetSurface()
739
740     double GetMaxCurvature( _SolidData&         data,
741                             _EdgesOnShape&      eof,
742                             BRepLProp_SLProps&  surfProp,
743                             SMESH_MesherHelper& helper);
744
745     bool GetCenterOfCurvature( _LayerEdge*         ledge,
746                                BRepLProp_SLProps&  surfProp,
747                                SMESH_MesherHelper& helper,
748                                gp_Pnt &            center ) const;
749     bool CheckPrisms() const;
750   };
751
752   //--------------------------------------------------------------------------------
753   /*!
754    * \brief Structure holding _LayerEdge's based on EDGEs that will collide
755    *        at inflation up to the full thickness. A detected collision
756    *        is fixed in updateNormals()
757    */
758   struct _CollisionEdges
759   {
760     _LayerEdge*           _edge;
761     vector< _LayerEdge* > _intEdges; // each pair forms an intersected quadrangle
762     const SMDS_MeshNode* nSrc(int i) const { return _intEdges[i]->_nodes[0]; }
763     const SMDS_MeshNode* nTgt(int i) const { return _intEdges[i]->_nodes.back(); }
764   };
765
766   //--------------------------------------------------------------------------------
767   /*!
768    * \brief Data of a SOLID
769    */
770   struct _SolidData
771   {
772     typedef const StdMeshers_ViscousLayers* THyp;
773     TopoDS_Shape                    _solid;
774     TopTools_MapOfShape             _before; // SOLIDs to be computed before _solid
775     TGeomID                         _index; // SOLID id
776     _MeshOfSolid*                   _proxyMesh;
777     bool                            _done;
778     list< THyp >                    _hyps;
779     list< TopoDS_Shape >            _hypShapes;
780     map< TGeomID, THyp >            _face2hyp; // filled if _hyps.size() > 1
781     set< TGeomID >                  _reversedFaceIds;
782     set< TGeomID >                  _ignoreFaceIds; // WOL FACEs and FACEs of other SOLIDs
783
784     double                          _stepSize, _stepSizeCoeff, _geomSize;
785     const SMDS_MeshNode*            _stepSizeNodes[2];
786
787     TNode2Edge                      _n2eMap; // nodes and _LayerEdge's based on them
788
789     // map to find _n2eMap of another _SolidData by a shrink shape shared by two _SolidData's
790     map< TGeomID, TNode2Edge* >     _s2neMap;
791     // _LayerEdge's with underlying shapes
792     vector< _EdgesOnShape >         _edgesOnShape;
793
794     // key:   an ID of shape (EDGE or VERTEX) shared by a FACE with
795     //        layers and a FACE w/o layers
796     // value: the shape (FACE or EDGE) to shrink mesh on.
797     //       _LayerEdge's basing on nodes on key shape are inflated along the value shape
798     map< TGeomID, TopoDS_Shape >     _shrinkShape2Shape;
799
800     // Convex FACEs whose radius of curvature is less than the thickness of layers
801     map< TGeomID, _ConvexFace >      _convexFaces;
802
803     // shapes (EDGEs and VERTEXes) shrink from which is forbidden due to collisions with
804     // the adjacent SOLID
805     set< TGeomID >                   _noShrinkShapes;
806
807     int                              _nbShapesToSmooth;
808
809     vector< _CollisionEdges >        _collisionEdges;
810     set< TGeomID >                   _concaveFaces;
811
812     double                           _maxThickness; // of all _hyps
813     double                           _minThickness; // of all _hyps
814
815     double                           _epsilon; // precision for SegTriaInter()
816
817     SMESH_MesherHelper*              _helper;
818
819     _SolidData(const TopoDS_Shape& s=TopoDS_Shape(),
820                _MeshOfSolid*       m=0)
821       :_solid(s), _proxyMesh(m), _done(false),_helper(0) {}
822     ~_SolidData() { delete _helper; _helper = 0; }
823
824     void SortOnEdge( const TopoDS_Edge& E, vector< _LayerEdge* >& edges);
825     void Sort2NeiborsOnEdge( vector< _LayerEdge* >& edges );
826
827     _ConvexFace* GetConvexFace( const TGeomID faceID ) {
828       map< TGeomID, _ConvexFace >::iterator id2face = _convexFaces.find( faceID );
829       return id2face == _convexFaces.end() ? 0 : & id2face->second;
830     }
831     _EdgesOnShape* GetShapeEdges(const TGeomID       shapeID );
832     _EdgesOnShape* GetShapeEdges(const TopoDS_Shape& shape );
833     _EdgesOnShape* GetShapeEdges(const _LayerEdge*   edge )
834     { return GetShapeEdges( edge->_nodes[0]->getshapeId() ); }
835
836     SMESH_MesherHelper& GetHelper() const { return *_helper; }
837
838     void UnmarkEdges( int flag = _LayerEdge::MARKED ) {
839       for ( size_t i = 0; i < _edgesOnShape.size(); ++i )
840         for ( size_t j = 0; j < _edgesOnShape[i]._edges.size(); ++j )
841           _edgesOnShape[i]._edges[j]->Unset( flag );
842     }
843     void AddShapesToSmooth( const set< _EdgesOnShape* >& shape,
844                             const set< _EdgesOnShape* >* edgesNoAnaSmooth=0 );
845
846     void PrepareEdgesToSmoothOnFace( _EdgesOnShape* eof, bool substituteSrcNodes );
847   };
848   //--------------------------------------------------------------------------------
849   /*!
850    * \brief Offset plane used in getNormalByOffset()
851    */
852   struct _OffsetPlane
853   {
854     gp_Pln _plane;
855     int    _faceIndex;
856     int    _faceIndexNext[2];
857     gp_Lin _lines[2]; // line of intersection with neighbor _OffsetPlane's
858     bool   _isLineOK[2];
859     _OffsetPlane() {
860       _isLineOK[0] = _isLineOK[1] = false; _faceIndexNext[0] = _faceIndexNext[1] = -1;
861     }
862     void   ComputeIntersectionLine( _OffsetPlane&        pln, 
863                                     const TopoDS_Edge&   E,
864                                     const TopoDS_Vertex& V );
865     gp_XYZ GetCommonPoint(bool& isFound, const TopoDS_Vertex& V) const;
866     int    NbLines() const { return _isLineOK[0] + _isLineOK[1]; }
867   };
868   //--------------------------------------------------------------------------------
869   /*!
870    * \brief Container of centers of curvature at nodes on an EDGE bounding _ConvexFace
871    */
872   struct _CentralCurveOnEdge
873   {
874     bool                  _isDegenerated;
875     vector< gp_Pnt >      _curvaCenters;
876     vector< _LayerEdge* > _ledges;
877     vector< gp_XYZ >      _normals; // new normal for each of _ledges
878     vector< double >      _segLength2;
879
880     TopoDS_Edge           _edge;
881     TopoDS_Face           _adjFace;
882     bool                  _adjFaceToSmooth;
883
884     void Append( const gp_Pnt& center, _LayerEdge* ledge )
885     {
886       if ( ledge->Is( _LayerEdge::MULTI_NORMAL ))
887         return;
888       if ( _curvaCenters.size() > 0 )
889         _segLength2.push_back( center.SquareDistance( _curvaCenters.back() ));
890       _curvaCenters.push_back( center );
891       _ledges.push_back( ledge );
892       _normals.push_back( ledge->_normal );
893     }
894     bool FindNewNormal( const gp_Pnt& center, gp_XYZ& newNormal );
895     void SetShapes( const TopoDS_Edge&  edge,
896                     const _ConvexFace&  convFace,
897                     _SolidData&         data,
898                     SMESH_MesherHelper& helper);
899   };
900   //--------------------------------------------------------------------------------
901   /*!
902    * \brief Data of node on a shrinked FACE
903    */
904   struct _SmoothNode
905   {
906     const SMDS_MeshNode*         _node;
907     vector<_Simplex>             _simplices; // for quality check
908
909     enum SmoothType { LAPLACIAN, CENTROIDAL, ANGULAR, TFI };
910
911     bool Smooth(int&                  badNb,
912                 Handle(Geom_Surface)& surface,
913                 SMESH_MesherHelper&   helper,
914                 const double          refSign,
915                 SmoothType            how,
916                 bool                  set3D);
917
918     gp_XY computeAngularPos(vector<gp_XY>& uv,
919                             const gp_XY&   uvToFix,
920                             const double   refSign );
921   };
922   struct PyDump;
923   struct Periodicity;
924   //--------------------------------------------------------------------------------
925   /*!
926    * \brief Builder of viscous layers
927    */
928   class _ViscousBuilder
929   {
930   public:
931     _ViscousBuilder();
932     // does it's job
933     SMESH_ComputeErrorPtr Compute(SMESH_Mesh&         mesh,
934                                   const TopoDS_Shape& shape);
935     // check validity of hypotheses
936     SMESH_ComputeErrorPtr CheckHypotheses( SMESH_Mesh&         mesh,
937                                            const TopoDS_Shape& shape );
938
939     // restore event listeners used to clear an inferior dim sub-mesh modified by viscous layers
940     void RestoreListeners();
941
942     // computes SMESH_ProxyMesh::SubMesh::_n2n;
943     bool MakeN2NMap( _MeshOfSolid* pm );
944
945   private:
946
947     bool findSolidsWithLayers(const bool checkFaceMesh=true);
948     bool setBefore( _SolidData& solidBefore, _SolidData& solidAfter );
949     bool findFacesWithLayers(const bool onlyWith=false);
950     void findPeriodicFaces();
951     void getIgnoreFaces(const TopoDS_Shape&             solid,
952                         const StdMeshers_ViscousLayers* hyp,
953                         const TopoDS_Shape&             hypShape,
954                         set<TGeomID>&                   ignoreFaces);
955     int makeEdgesOnShape();
956     bool makeLayer(_SolidData& data);
957     void setShapeData( _EdgesOnShape& eos, SMESH_subMesh* sm, _SolidData& data );
958     bool setEdgeData( _LayerEdge& edge, _EdgesOnShape& eos,
959                       SMESH_MesherHelper& helper, _SolidData& data);
960     gp_XYZ getFaceNormal(const SMDS_MeshNode* n,
961                          const TopoDS_Face&   face,
962                          SMESH_MesherHelper&  helper,
963                          bool&                isOK,
964                          bool                 shiftInside=false);
965     bool getFaceNormalAtSingularity(const gp_XY&        uv,
966                                     const TopoDS_Face&  face,
967                                     SMESH_MesherHelper& helper,
968                                     gp_Dir&             normal );
969     gp_XYZ getWeigthedNormal( const _LayerEdge*                edge );
970     gp_XYZ getNormalByOffset( _LayerEdge*                      edge,
971                               std::pair< TopoDS_Face, gp_XYZ > fId2Normal[],
972                               int                              nbFaces,
973                               bool                             lastNoOffset = false);
974     bool findNeiborsOnEdge(const _LayerEdge*     edge,
975                            const SMDS_MeshNode*& n1,
976                            const SMDS_MeshNode*& n2,
977                            _EdgesOnShape&        eos,
978                            _SolidData&           data);
979     void findSimplexTestEdges( _SolidData&                    data,
980                                vector< vector<_LayerEdge*> >& edgesByGeom);
981     void computeGeomSize( _SolidData& data );
982     bool findShapesToSmooth( _SolidData& data);
983     void limitStepSizeByCurvature( _SolidData&  data );
984     void limitStepSize( _SolidData&             data,
985                         const SMDS_MeshElement* face,
986                         const _LayerEdge*       maxCosinEdge );
987     void limitStepSize( _SolidData& data, const double minSize);
988     bool inflate(_SolidData& data);
989     bool smoothAndCheck(_SolidData& data, const int nbSteps, double & distToIntersection);
990     int  invalidateBadSmooth( _SolidData&               data,
991                               SMESH_MesherHelper&       helper,
992                               vector< _LayerEdge* >&    badSmooEdges,
993                               vector< _EdgesOnShape* >& eosC1,
994                               const int                 infStep );
995     void makeOffsetSurface( _EdgesOnShape& eos, SMESH_MesherHelper& );
996     void putOnOffsetSurface( _EdgesOnShape& eos, int infStep,
997                              vector< _EdgesOnShape* >& eosC1,
998                              int smooStep=0, int moveAll=false );
999     void findCollisionEdges( _SolidData& data, SMESH_MesherHelper& helper );
1000     void findEdgesToUpdateNormalNearConvexFace( _ConvexFace &       convFace,
1001                                                 _SolidData&         data,
1002                                                 SMESH_MesherHelper& helper );
1003     void limitMaxLenByCurvature( _SolidData& data, SMESH_MesherHelper& helper );
1004     void limitMaxLenByCurvature( _LayerEdge* e1, _LayerEdge* e2,
1005                                  _EdgesOnShape& eos1, _EdgesOnShape& eos2,
1006                                  const bool isSmoothable );
1007     bool updateNormals( _SolidData& data, SMESH_MesherHelper& helper, int stepNb, double stepSize );
1008     bool updateNormalsOfConvexFaces( _SolidData&         data,
1009                                      SMESH_MesherHelper& helper,
1010                                      int                 stepNb );
1011     void updateNormalsOfC1Vertices( _SolidData& data );
1012     bool updateNormalsOfSmoothed( _SolidData&         data,
1013                                   SMESH_MesherHelper& helper,
1014                                   const int           nbSteps,
1015                                   const double        stepSize );
1016     bool isNewNormalOk( _SolidData&   data,
1017                         _LayerEdge&   edge,
1018                         const gp_XYZ& newNormal);
1019     bool refine(_SolidData& data);
1020     bool shrink(_SolidData& data);
1021     bool prepareEdgeToShrink( _LayerEdge& edge, _EdgesOnShape& eos,
1022                               SMESH_MesherHelper& helper,
1023                               const SMESHDS_SubMesh* faceSubMesh );
1024     void restoreNoShrink( _LayerEdge& edge ) const;
1025     void fixBadFaces(const TopoDS_Face&          F,
1026                      SMESH_MesherHelper&         helper,
1027                      const bool                  is2D,
1028                      const int                   step,
1029                      set<const SMDS_MeshNode*> * involvedNodes=NULL);
1030     bool addBoundaryElements(_SolidData& data);
1031
1032     bool error( const string& text, int solidID=-1 );
1033     SMESHDS_Mesh* getMeshDS() const { return _mesh->GetMeshDS(); }
1034
1035     // debug
1036     void makeGroupOfLE();
1037
1038     SMESH_Mesh*                  _mesh;
1039     SMESH_ComputeErrorPtr        _error;
1040
1041     vector<                      _SolidData >  _sdVec;
1042     TopTools_IndexedMapOfShape   _solids; // to find _SolidData by a solid
1043     TopTools_MapOfShape          _shrunkFaces;
1044     std::unique_ptr<Periodicity> _periodicity;
1045
1046     int                          _tmpFaceID;
1047     PyDump*                      _pyDump;
1048   };
1049   //--------------------------------------------------------------------------------
1050   /*!
1051    * \brief Shrinker of nodes on the EDGE
1052    */
1053   class _Shrinker1D
1054   {
1055     TopoDS_Edge                   _geomEdge;
1056     vector<double>                _initU;
1057     vector<double>                _normPar;
1058     vector<const SMDS_MeshNode*>  _nodes;
1059     const _LayerEdge*             _edges[2];
1060     bool                          _done;
1061   public:
1062     void AddEdge( const _LayerEdge* e, _EdgesOnShape& eos, SMESH_MesherHelper& helper );
1063     void Compute(bool set3D, SMESH_MesherHelper& helper);
1064     void RestoreParams();
1065     void SwapSrcTgtNodes(SMESHDS_Mesh* mesh);
1066     const TopoDS_Edge& GeomEdge() const { return _geomEdge; }
1067     const SMDS_MeshNode* TgtNode( bool is2nd ) const
1068     { return _edges[is2nd] ? _edges[is2nd]->_nodes.back() : 0; }
1069     const SMDS_MeshNode* SrcNode( bool is2nd ) const
1070     { return _edges[is2nd] ? _edges[is2nd]->_nodes[0] : 0; }
1071   };
1072   //--------------------------------------------------------------------------------
1073   /*!
1074    * \brief Smoother of _LayerEdge's on EDGE.
1075    */
1076   struct _Smoother1D
1077   {
1078     struct OffPnt // point of the offsetted EDGE
1079     {
1080       gp_XYZ      _xyz;    // coord of a point inflated from EDGE w/o smooth
1081       double      _len;    // length reached at previous inflation step
1082       double      _param;  // on EDGE
1083       _2NearEdges _2edges; // 2 neighbor _LayerEdge's
1084       gp_XYZ      _edgeDir;// EDGE tangent at _param
1085       double Distance( const OffPnt& p ) const { return ( _xyz - p._xyz ).Modulus(); }
1086     };
1087     vector< OffPnt >   _offPoints;
1088     vector< double >   _leParams; // normalized param of _eos._edges on EDGE
1089     Handle(Geom_Curve) _anaCurve; // for analytic smooth
1090     _LayerEdge         _leOnV[2]; // _LayerEdge's holding normal to the EDGE at VERTEXes
1091     gp_XYZ             _edgeDir[2]; // tangent at VERTEXes
1092     size_t             _iSeg[2];  // index of segment where extreme tgt node is projected
1093     _EdgesOnShape&     _eos;
1094     double             _curveLen; // length of the EDGE
1095     std::pair<int,int> _eToSmooth[2]; // <from,to> indices of _LayerEdge's in _eos
1096
1097     static Handle(Geom_Curve) CurveForSmooth( const TopoDS_Edge&  E,
1098                                               _EdgesOnShape&      eos,
1099                                               SMESH_MesherHelper& helper);
1100
1101     _Smoother1D( Handle(Geom_Curve) curveForSmooth,
1102                  _EdgesOnShape&     eos )
1103       : _anaCurve( curveForSmooth ), _eos( eos )
1104     {
1105     }
1106     bool Perform(_SolidData&                    data,
1107                  Handle(ShapeAnalysis_Surface)& surface,
1108                  const TopoDS_Face&             F,
1109                  SMESH_MesherHelper&            helper );
1110
1111     void prepare(_SolidData& data );
1112
1113     void findEdgesToSmooth();
1114
1115     bool isToSmooth( int iE );
1116
1117     bool smoothAnalyticEdge( _SolidData&                    data,
1118                              Handle(ShapeAnalysis_Surface)& surface,
1119                              const TopoDS_Face&             F,
1120                              SMESH_MesherHelper&            helper);
1121     bool smoothComplexEdge( _SolidData&                     data,
1122                             Handle(ShapeAnalysis_Surface)& surface,
1123                             const TopoDS_Face&             F,
1124                             SMESH_MesherHelper&            helper);
1125     gp_XYZ getNormalNormal( const gp_XYZ & normal,
1126                             const gp_XYZ&  edgeDir);
1127     _LayerEdge* getLEdgeOnV( bool is2nd )
1128     {
1129       return _eos._edges[ is2nd ? _eos._edges.size()-1 : 0 ]->_2neibors->_edges[ is2nd ];
1130     }
1131     bool isAnalytic() const { return !_anaCurve.IsNull(); }
1132
1133     void offPointsToPython() const; // debug
1134   };
1135
1136   //--------------------------------------------------------------------------------
1137   /*!
1138    * \brief Compute positions of nodes of 2D structured mesh using TFI
1139    */
1140   class _Mapper2D
1141   {
1142     FaceQuadStruct _quadPoints;
1143
1144     UVPtStruct& uvPnt( size_t i, size_t j ) { return _quadPoints.UVPt( i, j ); }
1145
1146   public:
1147     _Mapper2D( const TParam2ColumnMap & param2ColumnMap, const TNode2Edge& n2eMap );
1148     bool ComputeNodePositions();
1149   };
1150
1151   //--------------------------------------------------------------------------------
1152   /*!
1153    * \brief Class of temporary mesh face.
1154    * We can't use SMDS_FaceOfNodes since it's impossible to set it's ID which is
1155    * needed because SMESH_ElementSearcher internally uses set of elements sorted by ID
1156    */
1157   struct _TmpMeshFace : public SMDS_PolygonalFaceOfNodes
1158   {
1159     const SMDS_MeshElement* _srcFace;
1160
1161     _TmpMeshFace( const vector<const SMDS_MeshNode*>& nodes,
1162                   int                                 ID,
1163                   int                                 faceID=-1,
1164                   const SMDS_MeshElement*             srcFace=0 ):
1165       SMDS_PolygonalFaceOfNodes(nodes), _srcFace( srcFace ) { setID( ID ); setShapeID( faceID ); }
1166     virtual SMDSAbs_EntityType  GetEntityType() const
1167     { return _srcFace ? _srcFace->GetEntityType() : SMDSEntity_Quadrangle; }
1168     virtual SMDSAbs_GeometryType GetGeomType()  const
1169     { return _srcFace ? _srcFace->GetGeomType() : SMDSGeom_QUADRANGLE; }
1170   };
1171   //--------------------------------------------------------------------------------
1172   /*!
1173    * \brief Class of temporary mesh quadrangle face storing _LayerEdge it's based on
1174    */
1175   struct _TmpMeshFaceOnEdge : public _TmpMeshFace
1176   {
1177     _LayerEdge *_le1, *_le2;
1178     _TmpMeshFaceOnEdge( _LayerEdge* le1, _LayerEdge* le2, int ID ):
1179       _TmpMeshFace( vector<const SMDS_MeshNode*>(4), ID ), _le1(le1), _le2(le2)
1180     {
1181       myNodes[0]=_le1->_nodes[0];
1182       myNodes[1]=_le1->_nodes.back();
1183       myNodes[2]=_le2->_nodes.back();
1184       myNodes[3]=_le2->_nodes[0];
1185     }
1186     const SMDS_MeshNode* n( size_t i ) const
1187     {
1188       return myNodes[ i ];
1189     }
1190     gp_XYZ GetDir() const // return average direction of _LayerEdge's, normal to EDGE
1191     {
1192       SMESH_TNodeXYZ p0s( myNodes[0] );
1193       SMESH_TNodeXYZ p0t( myNodes[1] );
1194       SMESH_TNodeXYZ p1t( myNodes[2] );
1195       SMESH_TNodeXYZ p1s( myNodes[3] );
1196       gp_XYZ  v0 = p0t - p0s;
1197       gp_XYZ  v1 = p1t - p1s;
1198       gp_XYZ v01 = p1s - p0s;
1199       gp_XYZ   n = ( v0 ^ v01 ) + ( v1 ^ v01 );
1200       gp_XYZ   d = v01 ^ n;
1201       d.Normalize();
1202       return d;
1203     }
1204     gp_XYZ GetDir(_LayerEdge* le1, _LayerEdge* le2) // return average direction of _LayerEdge's
1205     {
1206       myNodes[0]=le1->_nodes[0];
1207       myNodes[1]=le1->_nodes.back();
1208       myNodes[2]=le2->_nodes.back();
1209       myNodes[3]=le2->_nodes[0];
1210       return GetDir();
1211     }
1212   };
1213   //--------------------------------------------------------------------------------
1214   /*!
1215    * \brief Retriever of node coordinates either directly or from a surface by node UV.
1216    * \warning Location of a surface is ignored
1217    */
1218   struct _NodeCoordHelper
1219   {
1220     SMESH_MesherHelper&        _helper;
1221     const TopoDS_Face&         _face;
1222     Handle(Geom_Surface)       _surface;
1223     gp_XYZ (_NodeCoordHelper::* _fun)(const SMDS_MeshNode* n) const;
1224
1225     _NodeCoordHelper(const TopoDS_Face& F, SMESH_MesherHelper& helper, bool is2D)
1226       : _helper( helper ), _face( F )
1227     {
1228       if ( is2D )
1229       {
1230         TopLoc_Location loc;
1231         _surface = BRep_Tool::Surface( _face, loc );
1232       }
1233       if ( _surface.IsNull() )
1234         _fun = & _NodeCoordHelper::direct;
1235       else
1236         _fun = & _NodeCoordHelper::byUV;
1237     }
1238     gp_XYZ operator()(const SMDS_MeshNode* n) const { return (this->*_fun)( n ); }
1239
1240   private:
1241     gp_XYZ direct(const SMDS_MeshNode* n) const
1242     {
1243       return SMESH_TNodeXYZ( n );
1244     }
1245     gp_XYZ byUV  (const SMDS_MeshNode* n) const
1246     {
1247       gp_XY uv = _helper.GetNodeUV( _face, n );
1248       return _surface->Value( uv.X(), uv.Y() ).XYZ();
1249     }
1250   };
1251
1252   //================================================================================
1253   /*!
1254    * \brief Check angle between vectors 
1255    */
1256   //================================================================================
1257
1258   inline bool isLessAngle( const gp_Vec& v1, const gp_Vec& v2, const double cos )
1259   {
1260     double dot = v1 * v2; // cos * |v1| * |v2|
1261     double l1  = v1.SquareMagnitude();
1262     double l2  = v2.SquareMagnitude();
1263     return (( dot * cos >= 0 ) && 
1264             ( dot * dot ) / l1 / l2 >= ( cos * cos ));
1265   }
1266
1267   class _Factory
1268   {
1269     ObjectPool< _LayerEdge >  _edgePool;
1270     ObjectPool< _Curvature >  _curvaturePool;
1271     ObjectPool< _2NearEdges > _nearEdgesPool;
1272
1273     static _Factory* & me()
1274     {
1275       static _Factory* theFactory = 0;
1276       return theFactory;
1277     }
1278   public:
1279
1280     _Factory()  { me() = this; }
1281     ~_Factory() { me() = 0; }
1282
1283     static _LayerEdge*  NewLayerEdge() { return me()->_edgePool.getNew(); }
1284     static _Curvature * NewCurvature() { return me()->_curvaturePool.getNew(); }
1285     static _2NearEdges* NewNearEdges() { return me()->_nearEdgesPool.getNew(); }
1286   };
1287
1288 } // namespace VISCOUS_3D
1289
1290
1291
1292 //================================================================================
1293 // StdMeshers_ViscousLayers hypothesis
1294 //
1295 StdMeshers_ViscousLayers::StdMeshers_ViscousLayers(int hypId, SMESH_Gen* gen)
1296   :SMESH_Hypothesis(hypId, gen),
1297    _isToIgnoreShapes(1), _nbLayers(1), _thickness(1), _stretchFactor(1),
1298    _method( SURF_OFFSET_SMOOTH ),
1299    _groupName("")
1300 {
1301   _name = StdMeshers_ViscousLayers::GetHypType();
1302   _param_algo_dim = -3; // auxiliary hyp used by 3D algos
1303 } // --------------------------------------------------------------------------------
1304 void StdMeshers_ViscousLayers::SetBndShapes(const std::vector<int>& faceIds, bool toIgnore)
1305 {
1306   if ( faceIds != _shapeIds )
1307     _shapeIds = faceIds, NotifySubMeshesHypothesisModification();
1308   if ( _isToIgnoreShapes != toIgnore )
1309     _isToIgnoreShapes = toIgnore, NotifySubMeshesHypothesisModification();
1310 } // --------------------------------------------------------------------------------
1311 void StdMeshers_ViscousLayers::SetTotalThickness(double thickness)
1312 {
1313   if ( thickness != _thickness )
1314     _thickness = thickness, NotifySubMeshesHypothesisModification();
1315 } // --------------------------------------------------------------------------------
1316 void StdMeshers_ViscousLayers::SetNumberLayers(int nb)
1317 {
1318   if ( _nbLayers != nb )
1319     _nbLayers = nb, NotifySubMeshesHypothesisModification();
1320 } // --------------------------------------------------------------------------------
1321 void StdMeshers_ViscousLayers::SetStretchFactor(double factor)
1322 {
1323   if ( _stretchFactor != factor )
1324     _stretchFactor = factor, NotifySubMeshesHypothesisModification();
1325 } // --------------------------------------------------------------------------------
1326 void StdMeshers_ViscousLayers::SetMethod( ExtrusionMethod method )
1327 {
1328   if ( _method != method )
1329     _method = method, NotifySubMeshesHypothesisModification();
1330 } // --------------------------------------------------------------------------------
1331 void StdMeshers_ViscousLayers::SetGroupName(const std::string& name)
1332 {
1333   if ( _groupName != name )
1334   {
1335     _groupName = name;
1336     if ( !_groupName.empty() )
1337       NotifySubMeshesHypothesisModification();
1338   }
1339 } // --------------------------------------------------------------------------------
1340 SMESH_ProxyMesh::Ptr
1341 StdMeshers_ViscousLayers::Compute(SMESH_Mesh&         theMesh,
1342                                   const TopoDS_Shape& theShape,
1343                                   const bool          toMakeN2NMap) const
1344 {
1345   using namespace VISCOUS_3D;
1346   _ViscousBuilder builder;
1347   SMESH_ComputeErrorPtr err = builder.Compute( theMesh, theShape );
1348   if ( err && !err->IsOK() )
1349     return SMESH_ProxyMesh::Ptr();
1350
1351   vector<SMESH_ProxyMesh::Ptr> components;
1352   TopExp_Explorer exp( theShape, TopAbs_SOLID );
1353   for ( ; exp.More(); exp.Next() )
1354   {
1355     if ( _MeshOfSolid* pm =
1356          _ViscousListener::GetSolidMesh( &theMesh, exp.Current(), /*toCreate=*/false))
1357     {
1358       if ( toMakeN2NMap && !pm->_n2nMapComputed )
1359         if ( !builder.MakeN2NMap( pm ))
1360           return SMESH_ProxyMesh::Ptr();
1361       components.push_back( SMESH_ProxyMesh::Ptr( pm ));
1362       pm->myIsDeletable = false; // it will de deleted by boost::shared_ptr
1363
1364       if ( pm->_warning && !pm->_warning->IsOK() )
1365       {
1366         SMESH_subMesh* sm = theMesh.GetSubMesh( exp.Current() );
1367         SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1368         if ( !smError || smError->IsOK() )
1369           smError = pm->_warning;
1370       }
1371     }
1372     _ViscousListener::RemoveSolidMesh ( &theMesh, exp.Current() );
1373   }
1374   switch ( components.size() )
1375   {
1376   case 0: break;
1377
1378   case 1: return components[0];
1379
1380   default: return SMESH_ProxyMesh::Ptr( new SMESH_ProxyMesh( components ));
1381   }
1382   return SMESH_ProxyMesh::Ptr();
1383 } // --------------------------------------------------------------------------------
1384 std::ostream & StdMeshers_ViscousLayers::SaveTo(std::ostream & save)
1385 {
1386   save << " " << _nbLayers
1387        << " " << _thickness
1388        << " " << _stretchFactor
1389        << " " << _shapeIds.size();
1390   for ( size_t i = 0; i < _shapeIds.size(); ++i )
1391     save << " " << _shapeIds[i];
1392   save << " " << !_isToIgnoreShapes; // negate to keep the behavior in old studies.
1393   save << " " << _method;
1394   save << " " << _groupName.size();
1395   if ( !_groupName.empty() )
1396     save << " " << _groupName;
1397   return save;
1398 } // --------------------------------------------------------------------------------
1399 std::istream & StdMeshers_ViscousLayers::LoadFrom(std::istream & load)
1400 {
1401   int nbFaces, faceID, shapeToTreat, method;
1402   load >> _nbLayers >> _thickness >> _stretchFactor >> nbFaces;
1403   while ( (int) _shapeIds.size() < nbFaces && load >> faceID )
1404     _shapeIds.push_back( faceID );
1405   if ( load >> shapeToTreat ) {
1406     _isToIgnoreShapes = !shapeToTreat;
1407     if ( load >> method )
1408       _method = (ExtrusionMethod) method;
1409     int nameSize = 0;
1410     if ( load >> nameSize && nameSize > 0 )
1411     {
1412       _groupName.resize( nameSize );
1413       load.get( _groupName[0] ); // remove a white-space
1414       load.getline( &_groupName[0], nameSize + 1 );
1415     }
1416   }
1417   else {
1418     _isToIgnoreShapes = true; // old behavior
1419   }
1420   return load;
1421 } // --------------------------------------------------------------------------------
1422 bool StdMeshers_ViscousLayers::SetParametersByMesh(const SMESH_Mesh*   /*theMesh*/,
1423                                                    const TopoDS_Shape& /*theShape*/)
1424 {
1425   // TODO
1426   return false;
1427 } // --------------------------------------------------------------------------------
1428 SMESH_ComputeErrorPtr
1429 StdMeshers_ViscousLayers::CheckHypothesis(SMESH_Mesh&                          theMesh,
1430                                           const TopoDS_Shape&                  theShape,
1431                                           SMESH_Hypothesis::Hypothesis_Status& theStatus)
1432 {
1433   VISCOUS_3D::_ViscousBuilder builder;
1434   SMESH_ComputeErrorPtr err = builder.CheckHypotheses( theMesh, theShape );
1435   if ( err && !err->IsOK() )
1436     theStatus = SMESH_Hypothesis::HYP_INCOMPAT_HYPS;
1437   else
1438     theStatus = SMESH_Hypothesis::HYP_OK;
1439
1440   return err;
1441 }
1442 // --------------------------------------------------------------------------------
1443 bool StdMeshers_ViscousLayers::IsShapeWithLayers(int shapeIndex) const
1444 {
1445   bool isIn =
1446     ( std::find( _shapeIds.begin(), _shapeIds.end(), shapeIndex ) != _shapeIds.end() );
1447   return IsToIgnoreShapes() ? !isIn : isIn;
1448 }
1449 // --------------------------------------------------------------------------------
1450 double StdMeshers_ViscousLayers::Get1stLayerThickness( double T, double f, int N )
1451 {
1452   const double fPowN = pow( f, N );
1453   double h0;
1454   if ( fPowN - 1 <= numeric_limits<double>::min() )
1455     h0 = T / N;
1456   else
1457     h0 = T * ( f - 1 )/( fPowN - 1 );
1458   return h0;
1459 }
1460 // --------------------------------------------------------------------------------
1461 SMDS_MeshGroup* StdMeshers_ViscousLayers::CreateGroup( const std::string&  theName,
1462                                                        SMESH_Mesh&         theMesh,
1463                                                        SMDSAbs_ElementType theType)
1464 {
1465   SMESH_Group*      group = 0;
1466   SMDS_MeshGroup* groupDS = 0;
1467
1468   if ( theName.empty() )
1469     return groupDS;
1470        
1471   if ( SMESH_Mesh::GroupIteratorPtr grIt = theMesh.GetGroups() )
1472     while( grIt->more() && !group )
1473     {
1474       group = grIt->next();
1475       if ( !group ||
1476            group->GetGroupDS()->GetType() != theType ||
1477            group->GetName()               != theName ||
1478            !dynamic_cast< SMESHDS_Group* >( group->GetGroupDS() ))
1479         group = 0;
1480     }
1481   if ( !group )
1482     group = theMesh.AddGroup( theType, theName.c_str() );
1483
1484   groupDS = & dynamic_cast< SMESHDS_Group* >( group->GetGroupDS() )->SMDSGroup();
1485
1486   return groupDS;
1487 }
1488
1489 // END StdMeshers_ViscousLayers hypothesis
1490 //================================================================================
1491
1492 namespace VISCOUS_3D
1493 {
1494   gp_XYZ getEdgeDir( const TopoDS_Edge& E, const TopoDS_Vertex& fromV,
1495                      const double h0, bool* isRegularEdge = nullptr )
1496   {
1497     gp_Vec dir;
1498     double f,l;
1499     Handle(Geom_Curve) c = BRep_Tool::Curve( E, f, l );
1500     if ( c.IsNull() ) return gp_XYZ( Precision::Infinite(), 1e100, 1e100 );
1501     gp_Pnt  p = BRep_Tool::Pnt( fromV );
1502     gp_Pnt pf = c->Value( f ), pl = c->Value( l );
1503     double distF = p.SquareDistance( pf );
1504     double distL = p.SquareDistance( pl );
1505     c->D1(( distF < distL ? f : l), p, dir );
1506     if ( distL < distF ) dir.Reverse();
1507     bool isDifficult = false;
1508     if ( dir.SquareMagnitude() < h0 * h0 ) // check dir orientation
1509     {
1510       gp_Pnt& pClose = distF < distL ? pf : pl;
1511       gp_Pnt&   pFar = distF < distL ? pl : pf;
1512       gp_Pnt    pMid = 0.9 * pClose.XYZ() + 0.1 * pFar.XYZ();
1513       gp_Vec vMid( p, pMid );
1514       double     dot = vMid * dir;
1515       double    cos2 = dot * dot / dir.SquareMagnitude() / vMid.SquareMagnitude();
1516       if ( cos2 < 0.7 * 0.7 || dot < 0 ) // large angle between dir and vMid
1517       {
1518         double uClose = distF < distL ? f : l;
1519         double   uFar = distF < distL ? l : f;
1520         double      r = h0 / SMESH_Algo::EdgeLength( E );
1521         double   uMid = ( 1 - r ) * uClose + r * uFar;
1522         pMid = c->Value( uMid );
1523         dir = gp_Vec( p, pMid );
1524         isDifficult = true;
1525       }
1526     }
1527     if ( isRegularEdge )
1528       *isRegularEdge = !isDifficult;
1529
1530     return dir.XYZ();
1531   }
1532   //--------------------------------------------------------------------------------
1533   gp_XYZ getEdgeDir( const TopoDS_Edge& E, const SMDS_MeshNode* atNode,
1534                      SMESH_MesherHelper& helper)
1535   {
1536     gp_Vec dir;
1537     double f,l; gp_Pnt p;
1538     Handle(Geom_Curve) c = BRep_Tool::Curve( E, f, l );
1539     if ( c.IsNull() ) return gp_XYZ( Precision::Infinite(), 1e100, 1e100 );
1540     double u = helper.GetNodeU( E, atNode );
1541     c->D1( u, p, dir );
1542     return dir.XYZ();
1543   }
1544   //--------------------------------------------------------------------------------
1545   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Vertex& fromV,
1546                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper, bool& ok/*,
1547                      double* cosin=0*/);
1548   //--------------------------------------------------------------------------------
1549   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Edge& fromE,
1550                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper, bool& ok)
1551   {
1552     double f,l;
1553     Handle(Geom_Curve) c = BRep_Tool::Curve( fromE, f, l );
1554     if ( c.IsNull() )
1555     {
1556       TopoDS_Vertex v = helper.IthVertex( 0, fromE );
1557       return getFaceDir( F, v, node, helper, ok );
1558     }
1559     gp_XY uv = helper.GetNodeUV( F, node, 0, &ok );
1560     Handle(Geom_Surface) surface = BRep_Tool::Surface( F );
1561     gp_Pnt p; gp_Vec du, dv, norm;
1562     surface->D1( uv.X(),uv.Y(), p, du,dv );
1563     norm = du ^ dv;
1564
1565     double u = helper.GetNodeU( fromE, node, 0, &ok );
1566     c->D1( u, p, du );
1567     TopAbs_Orientation o = helper.GetSubShapeOri( F.Oriented(TopAbs_FORWARD), fromE);
1568     if ( o == TopAbs_REVERSED )
1569       du.Reverse();
1570
1571     gp_Vec dir = norm ^ du;
1572
1573     if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX &&
1574          helper.IsClosedEdge( fromE ))
1575     {
1576       if ( fabs(u-f) < fabs(u-l)) c->D1( l, p, dv );
1577       else                        c->D1( f, p, dv );
1578       if ( o == TopAbs_REVERSED )
1579         dv.Reverse();
1580       gp_Vec dir2 = norm ^ dv;
1581       dir = dir.Normalized() + dir2.Normalized();
1582     }
1583     return dir.XYZ();
1584   }
1585   //--------------------------------------------------------------------------------
1586   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Vertex& fromV,
1587                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper,
1588                      bool& ok/*, double* cosin*/)
1589   {
1590     TopoDS_Face faceFrw = F;
1591     faceFrw.Orientation( TopAbs_FORWARD );
1592     //double f,l; TopLoc_Location loc;
1593     TopoDS_Edge edges[2]; // sharing a vertex
1594     size_t nbEdges = 0;
1595     {
1596       TopoDS_Vertex VV[2];
1597       TopExp_Explorer exp( faceFrw, TopAbs_EDGE );
1598       for ( ; exp.More() && nbEdges < 2; exp.Next() )
1599       {
1600         const TopoDS_Edge& e = TopoDS::Edge( exp.Current() );
1601         if ( SMESH_Algo::isDegenerated( e )) continue;
1602         TopExp::Vertices( e, VV[0], VV[1], /*CumOri=*/true );
1603         if ( VV[1].IsSame( fromV )) {
1604           nbEdges += edges[ 0 ].IsNull();
1605           edges[ 0 ] = e;
1606         }
1607         else if ( VV[0].IsSame( fromV )) {
1608           nbEdges += edges[ 1 ].IsNull();
1609           edges[ 1 ] = e;
1610         }
1611       }
1612     }
1613     gp_XYZ dir(0,0,0), edgeDir[2];
1614     if ( nbEdges == 2 )
1615     {
1616       // get dirs of edges going fromV
1617       ok = true;
1618       for ( size_t i = 0; i < nbEdges && ok; ++i )
1619       {
1620         edgeDir[i] = getEdgeDir( edges[i], fromV, 0.1 * SMESH_Algo::EdgeLength( edges[i] ));
1621         double size2 = edgeDir[i].SquareModulus();
1622         if (( ok = size2 > numeric_limits<double>::min() ))
1623           edgeDir[i] /= sqrt( size2 );
1624       }
1625       if ( !ok ) return dir;
1626
1627       // get angle between the 2 edges
1628       gp_Vec faceNormal;
1629       double angle = helper.GetAngle( edges[0], edges[1], faceFrw, fromV, &faceNormal );
1630       if ( Abs( angle ) < 5 * M_PI/180 )
1631       {
1632         dir = ( faceNormal.XYZ() ^ edgeDir[0].Reversed()) + ( faceNormal.XYZ() ^ edgeDir[1] );
1633       }
1634       else
1635       {
1636         dir = edgeDir[0] + edgeDir[1];
1637         if ( angle < 0 )
1638           dir.Reverse();
1639       }
1640       // if ( cosin ) {
1641       //   double angle = faceNormal.Angle( dir );
1642       //   *cosin = Cos( angle );
1643       // }
1644     }
1645     else if ( nbEdges == 1 )
1646     {
1647       dir = getFaceDir( faceFrw, edges[ edges[0].IsNull() ], node, helper, ok );
1648       //if ( cosin ) *cosin = 1.;
1649     }
1650     else
1651     {
1652       ok = false;
1653     }
1654
1655     return dir;
1656   }
1657
1658   //================================================================================
1659   /*!
1660    * \brief Finds concave VERTEXes of a FACE
1661    */
1662   //================================================================================
1663
1664   bool getConcaveVertices( const TopoDS_Face&  F,
1665                            SMESH_MesherHelper& helper,
1666                            set< TGeomID >*     vertices = 0)
1667   {
1668     // check angles at VERTEXes
1669     TError error;
1670     TSideVector wires = StdMeshers_FaceSide::GetFaceWires( F, *helper.GetMesh(), 0, error );
1671     for ( size_t iW = 0; iW < wires.size(); ++iW )
1672     {
1673       const int nbEdges = wires[iW]->NbEdges();
1674       if ( nbEdges < 2 && SMESH_Algo::isDegenerated( wires[iW]->Edge(0)))
1675         continue;
1676       for ( int iE1 = 0; iE1 < nbEdges; ++iE1 )
1677       {
1678         if ( SMESH_Algo::isDegenerated( wires[iW]->Edge( iE1 ))) continue;
1679         int iE2 = ( iE1 + 1 ) % nbEdges;
1680         while ( SMESH_Algo::isDegenerated( wires[iW]->Edge( iE2 )))
1681           iE2 = ( iE2 + 1 ) % nbEdges;
1682         TopoDS_Vertex V = wires[iW]->FirstVertex( iE2 );
1683         double angle = helper.GetAngle( wires[iW]->Edge( iE1 ),
1684                                         wires[iW]->Edge( iE2 ), F, V );
1685         if ( angle < -5. * M_PI / 180. )
1686         {
1687           if ( !vertices )
1688             return true;
1689           vertices->insert( helper.GetMeshDS()->ShapeToIndex( V ));
1690         }
1691       }
1692     }
1693     return vertices ? !vertices->empty() : false;
1694   }
1695
1696   //================================================================================
1697   /*!
1698    * \brief Returns true if a FACE is bound by a concave EDGE
1699    */
1700   //================================================================================
1701
1702   bool isConcave( const TopoDS_Face&  F,
1703                   SMESH_MesherHelper& helper,
1704                   set< TGeomID >*     vertices = 0 )
1705   {
1706     bool isConcv = false;
1707     // if ( helper.Count( F, TopAbs_WIRE, /*useMap=*/false) > 1 )
1708     //   return true;
1709     gp_Vec2d drv1, drv2;
1710     gp_Pnt2d p;
1711     TopExp_Explorer eExp( F.Oriented( TopAbs_FORWARD ), TopAbs_EDGE );
1712     for ( ; eExp.More(); eExp.Next() )
1713     {
1714       const TopoDS_Edge& E = TopoDS::Edge( eExp.Current() );
1715       if ( SMESH_Algo::isDegenerated( E )) continue;
1716       // check if 2D curve is concave
1717       BRepAdaptor_Curve2d curve( E, F );
1718       const int nbIntervals = curve.NbIntervals( GeomAbs_C2 );
1719       TColStd_Array1OfReal intervals(1, nbIntervals + 1 );
1720       curve.Intervals( intervals, GeomAbs_C2 );
1721       bool isConvex = true;
1722       for ( int i = 1; i <= nbIntervals && isConvex; ++i )
1723       {
1724         double u1 = intervals( i );
1725         double u2 = intervals( i+1 );
1726         curve.D2( 0.5*( u1+u2 ), p, drv1, drv2 );
1727         double cross = drv1 ^ drv2;
1728         if ( E.Orientation() == TopAbs_REVERSED )
1729           cross = -cross;
1730         isConvex = ( cross > -1e-9 ); // 0.1 );
1731       }
1732       if ( !isConvex )
1733       {
1734         //cout << "Concave FACE " << helper.GetMeshDS()->ShapeToIndex( F ) << endl;
1735         isConcv = true;
1736         if ( vertices )
1737           break;
1738         else
1739           return true;
1740       }
1741     }
1742
1743     // check angles at VERTEXes
1744     if ( getConcaveVertices( F, helper, vertices ))
1745       isConcv = true;
1746
1747     return isConcv;
1748   }
1749
1750   //================================================================================
1751   /*!
1752    * \brief Computes minimal distance of face in-FACE nodes from an EDGE
1753    *  \param [in] face - the mesh face to treat
1754    *  \param [in] nodeOnEdge - a node on the EDGE
1755    *  \param [out] faceSize - the computed distance
1756    *  \return bool - true if faceSize computed
1757    */
1758   //================================================================================
1759
1760   bool getDistFromEdge( const SMDS_MeshElement* face,
1761                         const SMDS_MeshNode*    nodeOnEdge,
1762                         double &                faceSize )
1763   {
1764     faceSize = Precision::Infinite();
1765     bool done = false;
1766
1767     int nbN  = face->NbCornerNodes();
1768     int iOnE = face->GetNodeIndex( nodeOnEdge );
1769     int iNext[2] = { SMESH_MesherHelper::WrapIndex( iOnE+1, nbN ),
1770                      SMESH_MesherHelper::WrapIndex( iOnE-1, nbN ) };
1771     const SMDS_MeshNode* nNext[2] = { face->GetNode( iNext[0] ),
1772                                       face->GetNode( iNext[1] ) };
1773     gp_XYZ segVec, segEnd = SMESH_TNodeXYZ( nodeOnEdge ); // segment on EDGE
1774     double segLen = -1.;
1775     // look for two neighbor not in-FACE nodes of face
1776     for ( int i = 0; i < 2; ++i )
1777     {
1778       if (( nNext[i]->GetPosition()->GetDim() != 2 ) &&
1779           ( nodeOnEdge->GetPosition()->GetDim() == 0 || nNext[i]->GetID() < nodeOnEdge->GetID() ))
1780       {
1781         // look for an in-FACE node
1782         for ( int iN = 0; iN < nbN; ++iN )
1783         {
1784           if ( iN == iOnE || iN == iNext[i] )
1785             continue;
1786           SMESH_TNodeXYZ pInFace = face->GetNode( iN );
1787           gp_XYZ v = pInFace - segEnd;
1788           if ( segLen < 0 )
1789           {
1790             segVec = SMESH_TNodeXYZ( nNext[i] ) - segEnd;
1791             segLen = segVec.Modulus();
1792           }
1793           double distToSeg = v.Crossed( segVec ).Modulus() / segLen;
1794           faceSize = Min( faceSize, distToSeg );
1795           done = true;
1796         }
1797         segLen = -1;
1798       }
1799     }
1800     return done;
1801   }
1802   //================================================================================
1803   /*!
1804    * \brief Return direction of axis or revolution of a surface
1805    */
1806   //================================================================================
1807
1808   bool getRovolutionAxis( const Adaptor3d_Surface& surface,
1809                           gp_Dir &                 axis )
1810   {
1811     switch ( surface.GetType() ) {
1812     case GeomAbs_Cone:
1813     {
1814       gp_Cone cone = surface.Cone();
1815       axis = cone.Axis().Direction();
1816       break;
1817     }
1818     case GeomAbs_Sphere:
1819     {
1820       gp_Sphere sphere = surface.Sphere();
1821       axis = sphere.Position().Direction();
1822       break;
1823     }
1824     case GeomAbs_SurfaceOfRevolution:
1825     {
1826       axis = surface.AxeOfRevolution().Direction();
1827       break;
1828     }
1829     //case GeomAbs_SurfaceOfExtrusion:
1830     case GeomAbs_OffsetSurface:
1831     {
1832 #if OCC_VERSION_LARGE < 0x07070000
1833       Handle(Adaptor3d_HSurface) base = surface.BasisSurface();
1834       return getRovolutionAxis( base->Surface(), axis );
1835 #else
1836       Handle(Adaptor3d_Surface) base = surface.BasisSurface();
1837       return getRovolutionAxis( *base, axis );
1838 #endif
1839     }
1840     default: return false;
1841     }
1842     return true;
1843   }
1844
1845   //--------------------------------------------------------------------------------
1846   // DEBUG. Dump intermediate node positions into a python script
1847   // HOWTO use: run python commands written in a console and defined in /tmp/viscous.py
1848   // to see construction steps of viscous layers
1849 #ifdef __myDEBUG
1850   ostream* py;
1851   int      theNbPyFunc;
1852   struct PyDump
1853   {
1854     PyDump(SMESH_Mesh& m) {
1855       int tag = 3 + m.GetId();
1856       const char* fname = "/tmp/viscous.py";
1857       cout << "exec(open('"<<fname<<"','rb').read() )"<<endl;
1858       py = _pyStream = new ofstream(fname);
1859       *py << "import SMESH" << endl
1860           << "from salome.smesh import smeshBuilder" << endl
1861           << "smesh  = smeshBuilder.New()" << endl
1862           << "meshSO = salome.myStudy.FindObjectID('0:1:2:" << tag <<"')" << endl
1863           << "mesh   = smesh.Mesh( meshSO.GetObject() )"<<endl;
1864       theNbPyFunc = 0;
1865     }
1866     void Finish() {
1867       if (py) {
1868         *py << "mesh.GroupOnFilter(SMESH.VOLUME,'Viscous Prisms',"
1869           "smesh.GetFilter(SMESH.VOLUME,SMESH.FT_ElemGeomType,'=',SMESH.Geom_PENTA))"<<endl;
1870         *py << "mesh.GroupOnFilter(SMESH.VOLUME,'Neg Volumes',"
1871           "smesh.GetFilter(SMESH.VOLUME,SMESH.FT_Volume3D,'<',0))"<<endl;
1872       }
1873       delete py; py=0;
1874     }
1875     ~PyDump() { Finish(); cout << "NB FUNCTIONS: " << theNbPyFunc << endl; }
1876     struct MyStream : public ostream
1877     {
1878       template <class T> ostream & operator<<( const T &anything ) { return *this ; }
1879     };
1880     void Pause() { py = &_mystream; }
1881     void Resume() { py = _pyStream; }
1882     MyStream _mystream;
1883     ostream* _pyStream;
1884   };
1885 #define dumpFunction(f) { _dumpFunction(f, __LINE__);}
1886 #define dumpMove(n)     { _dumpMove(n, __LINE__);}
1887 #define dumpMoveComm(n,txt) { _dumpMove(n, __LINE__, txt);}
1888 #define dumpCmd(txt)    { _dumpCmd(txt, __LINE__);}
1889   void _dumpFunction(const string& fun, int ln)
1890   { if (py) *py<< "def "<<fun<<"(): # "<< ln <<endl; cout<<fun<<"()"<<endl; ++theNbPyFunc; }
1891   void _dumpMove(const SMDS_MeshNode* n, int ln, const char* txt="")
1892   { if (py) *py<< "  mesh.MoveNode( "<<n->GetID()<< ", "<< n->X()
1893                << ", "<<n->Y()<<", "<< n->Z()<< ")\t\t # "<< ln <<" "<< txt << endl; }
1894   void _dumpCmd(const string& txt, int ln)
1895   { if (py) *py<< "  "<<txt<<" # "<< ln <<endl; }
1896   void dumpFunctionEnd()
1897   { if (py) *py<< "  return"<< endl; }
1898   void dumpChangeNodes( const SMDS_MeshElement* f )
1899   { if (py) { *py<< "  mesh.ChangeElemNodes( " << f->GetID()<<", [";
1900       for ( int i=1; i < f->NbNodes(); ++i ) *py << f->GetNode(i-1)->GetID()<<", ";
1901       *py << f->GetNode( f->NbNodes()-1 )->GetID() << " ])"<< endl; }}
1902 #define debugMsg( txt ) { cout << "# "<< txt << " (line: " << __LINE__ << ")" << endl; }
1903
1904 #else
1905
1906   struct PyDump { PyDump(SMESH_Mesh&) {} void Finish() {} void Pause() {} void Resume() {} };
1907 #define dumpFunction(f) f
1908 #define dumpMove(n)
1909 #define dumpMoveComm(n,txt)
1910 #define dumpCmd(txt)
1911 #define dumpFunctionEnd()
1912 #define dumpChangeNodes(f) { if(f) {} } // prevent "unused variable 'f'" warning
1913 #define debugMsg( txt ) {}
1914
1915 #endif
1916 }
1917
1918 using namespace VISCOUS_3D;
1919
1920 //================================================================================
1921 /*!
1922  * \brief Constructor of _ViscousBuilder
1923  */
1924 //================================================================================
1925
1926 _ViscousBuilder::_ViscousBuilder()
1927 {
1928   _error = SMESH_ComputeError::New(COMPERR_OK);
1929   _tmpFaceID = 0;
1930 }
1931
1932 //================================================================================
1933 /*!
1934  * \brief Stores error description and returns false
1935  */
1936 //================================================================================
1937
1938 bool _ViscousBuilder::error(const string& text, int solidId )
1939 {
1940   const string prefix = string("Viscous layers builder: ");
1941   _error->myName    = COMPERR_ALGO_FAILED;
1942   _error->myComment = prefix + text;
1943   if ( _mesh )
1944   {
1945     SMESH_subMesh* sm = _mesh->GetSubMeshContaining( solidId );
1946     if ( !sm && !_sdVec.empty() )
1947       sm = _mesh->GetSubMeshContaining( solidId = _sdVec[0]._index );
1948     if ( sm && sm->GetSubShape().ShapeType() == TopAbs_SOLID )
1949     {
1950       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1951       if ( smError && smError->myAlgo )
1952         _error->myAlgo = smError->myAlgo;
1953       smError = _error;
1954       sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
1955     }
1956     // set KO to all solids
1957     for ( size_t i = 0; i < _sdVec.size(); ++i )
1958     {
1959       if ( _sdVec[i]._index == solidId )
1960         continue;
1961       sm = _mesh->GetSubMesh( _sdVec[i]._solid );
1962       if ( !sm->IsEmpty() )
1963         continue;
1964       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1965       if ( !smError || smError->IsOK() )
1966       {
1967         smError = SMESH_ComputeError::New( COMPERR_ALGO_FAILED, prefix + "failed");
1968         sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
1969       }
1970     }
1971   }
1972   makeGroupOfLE(); // debug
1973
1974   return false;
1975 }
1976
1977 //================================================================================
1978 /*!
1979  * \brief At study restoration, restore event listeners used to clear an inferior
1980  *  dim sub-mesh modified by viscous layers
1981  */
1982 //================================================================================
1983
1984 void _ViscousBuilder::RestoreListeners()
1985 {
1986   // TODO
1987 }
1988
1989 //================================================================================
1990 /*!
1991  * \brief computes SMESH_ProxyMesh::SubMesh::_n2n
1992  */
1993 //================================================================================
1994
1995 bool _ViscousBuilder::MakeN2NMap( _MeshOfSolid* pm )
1996 {
1997   SMESH_subMesh* solidSM = pm->mySubMeshes.front();
1998   TopExp_Explorer fExp( solidSM->GetSubShape(), TopAbs_FACE );
1999   for ( ; fExp.More(); fExp.Next() )
2000   {
2001     SMESHDS_SubMesh* srcSmDS = pm->GetMeshDS()->MeshElements( fExp.Current() );
2002     const SMESH_ProxyMesh::SubMesh* prxSmDS = pm->GetProxySubMesh( fExp.Current() );
2003
2004     if ( !srcSmDS || !prxSmDS || !srcSmDS->NbElements() || !prxSmDS->NbElements() )
2005       continue;
2006     if ( srcSmDS->GetElements()->next() == prxSmDS->GetElements()->next())
2007       continue;
2008
2009     if ( srcSmDS->NbElements() != prxSmDS->NbElements() )
2010       return error( "Different nb elements in a source and a proxy sub-mesh", solidSM->GetId());
2011
2012     SMDS_ElemIteratorPtr srcIt = srcSmDS->GetElements();
2013     SMDS_ElemIteratorPtr prxIt = prxSmDS->GetElements();
2014     while( prxIt->more() )
2015     {
2016       const SMDS_MeshElement* fSrc = srcIt->next();
2017       const SMDS_MeshElement* fPrx = prxIt->next();
2018       if ( fSrc->NbNodes() != fPrx->NbNodes())
2019         return error( "Different elements in a source and a proxy sub-mesh", solidSM->GetId());
2020       for ( int i = 0 ; i < fPrx->NbNodes(); ++i )
2021         pm->setNode2Node( fSrc->GetNode(i), fPrx->GetNode(i), prxSmDS );
2022     }
2023   }
2024   pm->_n2nMapComputed = true;
2025   return true;
2026 }
2027
2028 //================================================================================
2029 /*!
2030  * \brief Does its job
2031  */
2032 //================================================================================
2033
2034 SMESH_ComputeErrorPtr _ViscousBuilder::Compute(SMESH_Mesh&         theMesh,
2035                                                const TopoDS_Shape& theShape)
2036 {
2037   _mesh = & theMesh;
2038
2039   _Factory factory;
2040
2041   // check if proxy mesh already computed
2042   TopExp_Explorer exp( theShape, TopAbs_SOLID );
2043   if ( !exp.More() )
2044     return error("No SOLID's in theShape"), _error;
2045
2046   if ( _ViscousListener::GetSolidMesh( _mesh, exp.Current(), /*toCreate=*/false))
2047     return SMESH_ComputeErrorPtr(); // everything already computed
2048
2049   // TODO: ignore already computed SOLIDs
2050   if ( !findSolidsWithLayers())
2051     return _error;
2052
2053   if ( !findFacesWithLayers() )
2054     return _error;
2055
2056   if ( !makeEdgesOnShape() )
2057     return _error;
2058
2059   findPeriodicFaces();
2060
2061   PyDump debugDump( theMesh );
2062   _pyDump = &debugDump;
2063
2064
2065   for ( size_t i = 0; i < _sdVec.size(); ++i )
2066   {
2067     size_t iSD = 0;
2068     for ( iSD = 0; iSD < _sdVec.size(); ++iSD ) // find next SOLID to compute
2069       if ( _sdVec[iSD]._before.IsEmpty() &&
2070            !_sdVec[iSD]._solid.IsNull() &&
2071            !_sdVec[iSD]._done )
2072         break;
2073     if ( iSD == _sdVec.size() )
2074       break; // all done
2075
2076     if ( ! makeLayer(_sdVec[iSD]) )   // create _LayerEdge's
2077       return _error;
2078
2079     if ( _sdVec[iSD]._n2eMap.size() == 0 ) // no layers in a SOLID
2080     {
2081       _sdVec[iSD]._solid.Nullify();
2082       continue;
2083     }
2084
2085     if ( ! inflate(_sdVec[iSD]) )     // increase length of _LayerEdge's
2086       return _error;
2087
2088     if ( ! refine(_sdVec[iSD]) )      // create nodes and prisms
2089       return _error;
2090
2091     if ( ! shrink(_sdVec[iSD]) )      // shrink 2D mesh on FACEs w/o layer
2092       return _error;
2093
2094     bool notMissingFaces = addBoundaryElements(_sdVec[iSD]); // create quadrangles on prism bare sides
2095
2096     if ( !notMissingFaces )
2097     {
2098       SMESH_MeshEditor editor( &theMesh );
2099       TIDSortedElemSet elements;
2100       editor.MakeBoundaryMesh( elements, SMESH_MeshEditor::BND_2DFROM3D );
2101     }
2102     
2103     _sdVec[iSD]._done = true;
2104
2105     const TopoDS_Shape& solid = _sdVec[iSD]._solid;
2106     for ( iSD = 0; iSD < _sdVec.size(); ++iSD )
2107       _sdVec[iSD]._before.Remove( solid );
2108   }
2109
2110   makeGroupOfLE(); // debug
2111   debugDump.Finish();
2112
2113   return _error;
2114 }
2115
2116 //================================================================================
2117 /*!
2118  * \brief Check validity of hypotheses
2119  */
2120 //================================================================================
2121
2122 SMESH_ComputeErrorPtr _ViscousBuilder::CheckHypotheses( SMESH_Mesh&         mesh,
2123                                                         const TopoDS_Shape& shape )
2124 {
2125   _mesh = & mesh;
2126
2127   if ( _ViscousListener::GetSolidMesh( _mesh, shape, /*toCreate=*/false))
2128     return SMESH_ComputeErrorPtr(); // everything already computed
2129
2130
2131   findSolidsWithLayers( /*checkFaceMesh=*/false );
2132   bool ok = findFacesWithLayers( true );
2133
2134   // remove _MeshOfSolid's of _SolidData's
2135   for ( size_t i = 0; i < _sdVec.size(); ++i )
2136     _ViscousListener::RemoveSolidMesh( _mesh, _sdVec[i]._solid );
2137
2138   if ( !ok )
2139     return _error;
2140
2141   return SMESH_ComputeErrorPtr();
2142 }
2143
2144 //================================================================================
2145 /*!
2146  * \brief Finds SOLIDs to compute using viscous layers. Fills _sdVec
2147  */
2148 //================================================================================
2149
2150 bool _ViscousBuilder::findSolidsWithLayers(const bool checkFaceMesh)
2151 {
2152   // get all solids
2153   TopTools_IndexedMapOfShape allSolids;
2154   TopExp::MapShapes( _mesh->GetShapeToMesh(), TopAbs_SOLID, allSolids );
2155   _sdVec.reserve( allSolids.Extent());
2156
2157   SMESH_HypoFilter filter;
2158   for ( int i = 1; i <= allSolids.Extent(); ++i )
2159   {
2160     SMESH_subMesh* sm = _mesh->GetSubMesh( allSolids(i) );
2161     if ( sm->GetSubMeshDS() && sm->GetSubMeshDS()->NbElements() > 0 )
2162       continue; // solid is already meshed
2163     // TODO: check if algo is hidden
2164     SMESH_Algo* algo = sm->GetAlgo();
2165     if ( !algo ) continue;
2166     // check if all FACEs are meshed, which can be false if Compute() a sub-shape
2167     if ( checkFaceMesh )
2168     {
2169       bool facesMeshed = true;
2170       SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(false,true);
2171       while ( smIt->more() && facesMeshed )
2172       {
2173         SMESH_subMesh * faceSM = smIt->next();
2174         if ( faceSM->GetSubShape().ShapeType() != TopAbs_FACE )
2175           break;
2176         facesMeshed = faceSM->IsMeshComputed();
2177       }
2178       if ( !facesMeshed )
2179         continue;
2180     }
2181     // find StdMeshers_ViscousLayers hyp assigned to the i-th solid
2182     const list <const SMESHDS_Hypothesis *> & allHyps =
2183       algo->GetUsedHypothesis(*_mesh, allSolids(i), /*ignoreAuxiliary=*/false);
2184     _SolidData* soData = 0;
2185     list< const SMESHDS_Hypothesis *>::const_iterator hyp = allHyps.begin();
2186     const StdMeshers_ViscousLayers* viscHyp = 0;
2187     for ( ; hyp != allHyps.end(); ++hyp )
2188       if (( viscHyp = dynamic_cast<const StdMeshers_ViscousLayers*>( *hyp )))
2189       {
2190         TopoDS_Shape hypShape;
2191         filter.Init( filter.Is( viscHyp ));
2192         _mesh->GetHypothesis( allSolids(i), filter, true, &hypShape );
2193
2194         if ( !soData )
2195         {
2196           _MeshOfSolid* proxyMesh = _ViscousListener::GetSolidMesh( _mesh,
2197                                                                     allSolids(i),
2198                                                                     /*toCreate=*/true);
2199           _sdVec.push_back( _SolidData( allSolids(i), proxyMesh ));
2200           soData = & _sdVec.back();
2201           soData->_index = getMeshDS()->ShapeToIndex( allSolids(i));
2202           soData->_helper = new SMESH_MesherHelper( *_mesh );
2203           soData->_helper->SetSubShape( allSolids(i) );
2204           _solids.Add( allSolids(i) );
2205         }
2206         soData->_hyps.push_back( viscHyp );
2207         soData->_hypShapes.push_back( hypShape );
2208       }
2209   }
2210   if ( _sdVec.empty() )
2211     return error
2212       ( SMESH_Comment(StdMeshers_ViscousLayers::GetHypType()) << " hypothesis not found",0);
2213
2214   return true;
2215 }
2216
2217 //================================================================================
2218 /*!
2219  * \brief Set a _SolidData to be computed before another
2220  */
2221 //================================================================================
2222
2223 bool _ViscousBuilder::setBefore( _SolidData& solidBefore, _SolidData& solidAfter )
2224 {
2225   // check possibility to set this order; get all solids before solidBefore
2226   TopTools_IndexedMapOfShape allSolidsBefore;
2227   allSolidsBefore.Add( solidBefore._solid );
2228   for ( int i = 1; i <= allSolidsBefore.Extent(); ++i )
2229   {
2230     int iSD = _solids.FindIndex( allSolidsBefore(i) );
2231     if ( iSD )
2232     {
2233       TopTools_MapIteratorOfMapOfShape soIt( _sdVec[ iSD-1 ]._before );
2234       for ( ; soIt.More(); soIt.Next() )
2235         allSolidsBefore.Add( soIt.Value() );
2236     }
2237   }
2238   if ( allSolidsBefore.Contains( solidAfter._solid ))
2239     return false;
2240
2241   for ( int i = 1; i <= allSolidsBefore.Extent(); ++i )
2242     solidAfter._before.Add( allSolidsBefore(i) );
2243
2244   return true;
2245 }
2246
2247 //================================================================================
2248 /*!
2249  * \brief
2250  */
2251 //================================================================================
2252
2253 bool _ViscousBuilder::findFacesWithLayers(const bool onlyWith)
2254 {
2255   SMESH_MesherHelper helper( *_mesh );
2256   TopExp_Explorer exp;
2257
2258   // collect all faces-to-ignore defined by hyp
2259   for ( size_t i = 0; i < _sdVec.size(); ++i )
2260   {
2261     // get faces-to-ignore defined by each hyp
2262     typedef const StdMeshers_ViscousLayers* THyp;
2263     typedef std::pair< set<TGeomID>, THyp > TFacesOfHyp;
2264     list< TFacesOfHyp > ignoreFacesOfHyps;
2265     list< THyp >::iterator              hyp = _sdVec[i]._hyps.begin();
2266     list< TopoDS_Shape >::iterator hypShape = _sdVec[i]._hypShapes.begin();
2267     for ( ; hyp != _sdVec[i]._hyps.end(); ++hyp, ++hypShape )
2268     {
2269       ignoreFacesOfHyps.push_back( TFacesOfHyp( set<TGeomID>(), *hyp ));
2270       getIgnoreFaces( _sdVec[i]._solid, *hyp, *hypShape, ignoreFacesOfHyps.back().first );
2271     }
2272
2273     // fill _SolidData::_face2hyp and check compatibility of hypotheses
2274     const int nbHyps = _sdVec[i]._hyps.size();
2275     if ( nbHyps > 1 )
2276     {
2277       // check if two hypotheses define different parameters for the same FACE
2278       list< TFacesOfHyp >::iterator igFacesOfHyp;
2279       for ( exp.Init( _sdVec[i]._solid, TopAbs_FACE ); exp.More(); exp.Next() )
2280       {
2281         const TGeomID faceID = getMeshDS()->ShapeToIndex( exp.Current() );
2282         THyp hyp = 0;
2283         igFacesOfHyp = ignoreFacesOfHyps.begin();
2284         for ( ; igFacesOfHyp != ignoreFacesOfHyps.end(); ++igFacesOfHyp )
2285           if ( ! igFacesOfHyp->first.count( faceID ))
2286           {
2287             if ( hyp )
2288               return error(SMESH_Comment("Several hypotheses define "
2289                                          "Viscous Layers on the face #") << faceID );
2290             hyp = igFacesOfHyp->second;
2291           }
2292         if ( hyp )
2293           _sdVec[i]._face2hyp.insert( make_pair( faceID, hyp ));
2294         else
2295           _sdVec[i]._ignoreFaceIds.insert( faceID );
2296       }
2297
2298       // check if two hypotheses define different number of viscous layers for
2299       // adjacent faces of a solid
2300       set< int > nbLayersSet;
2301       igFacesOfHyp = ignoreFacesOfHyps.begin();
2302       for ( ; igFacesOfHyp != ignoreFacesOfHyps.end(); ++igFacesOfHyp )
2303       {
2304         nbLayersSet.insert( igFacesOfHyp->second->GetNumberLayers() );
2305       }
2306       if ( nbLayersSet.size() > 1 )
2307       {
2308         for ( exp.Init( _sdVec[i]._solid, TopAbs_EDGE ); exp.More(); exp.Next() )
2309         {
2310           PShapeIteratorPtr fIt = helper.GetAncestors( exp.Current(), *_mesh, TopAbs_FACE );
2311           THyp hyp1 = 0, hyp2 = 0;
2312           while( const TopoDS_Shape* face = fIt->next() )
2313           {
2314             const TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
2315             map< TGeomID, THyp >::iterator f2h = _sdVec[i]._face2hyp.find( faceID );
2316             if ( f2h != _sdVec[i]._face2hyp.end() )
2317             {
2318               ( hyp1 ? hyp2 : hyp1 ) = f2h->second;
2319             }
2320           }
2321           if ( hyp1 && hyp2 &&
2322                hyp1->GetNumberLayers() != hyp2->GetNumberLayers() )
2323           {
2324             return error("Two hypotheses define different number of "
2325                          "viscous layers on adjacent faces");
2326           }
2327         }
2328       }
2329     } // if ( nbHyps > 1 )
2330     else
2331     {
2332       _sdVec[i]._ignoreFaceIds.swap( ignoreFacesOfHyps.back().first );
2333     }
2334   } // loop on _sdVec
2335
2336   if ( onlyWith ) // is called to check hypotheses compatibility only
2337     return true;
2338
2339   // fill _SolidData::_reversedFaceIds
2340   for ( size_t i = 0; i < _sdVec.size(); ++i )
2341   {
2342     exp.Init( _sdVec[i]._solid.Oriented( TopAbs_FORWARD ), TopAbs_FACE );
2343     for ( ; exp.More(); exp.Next() )
2344     {
2345       const TopoDS_Face& face = TopoDS::Face( exp.Current() );
2346       const TGeomID    faceID = getMeshDS()->ShapeToIndex( face );
2347       if ( //!sdVec[i]._ignoreFaceIds.count( faceID ) &&
2348           helper.NbAncestors( face, *_mesh, TopAbs_SOLID ) > 1 &&
2349           helper.IsReversedSubMesh( face ))
2350       {
2351         _sdVec[i]._reversedFaceIds.insert( faceID );
2352       }
2353     }
2354   }
2355
2356   // Find FACEs to shrink mesh on (solution 2 in issue 0020832): fill in _shrinkShape2Shape
2357   TopTools_IndexedMapOfShape shapes;
2358   std::string structAlgoName = "Hexa_3D";
2359   for ( size_t i = 0; i < _sdVec.size(); ++i )
2360   {
2361     shapes.Clear();
2362     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_EDGE, shapes);
2363     for ( int iE = 1; iE <= shapes.Extent(); ++iE )
2364     {
2365       const TopoDS_Shape& edge = shapes(iE);
2366       // find 2 FACEs sharing an EDGE
2367       TopoDS_Shape FF[2];
2368       PShapeIteratorPtr fIt = helper.GetAncestors(edge, *_mesh, TopAbs_FACE, &_sdVec[i]._solid);
2369       while ( fIt->more())
2370       {
2371         const TopoDS_Shape* f = fIt->next();
2372         FF[ int( !FF[0].IsNull()) ] = *f;
2373       }
2374       if( FF[1].IsNull() ) continue; // seam edge can be shared by 1 FACE only
2375
2376       // check presence of layers on them
2377       int ignore[2];
2378       for ( int j = 0; j < 2; ++j )
2379         ignore[j] = _sdVec[i]._ignoreFaceIds.count( getMeshDS()->ShapeToIndex( FF[j] ));
2380       if ( ignore[0] == ignore[1] )
2381         continue; // nothing interesting
2382       TopoDS_Shape fWOL = FF[ ignore[0] ? 0 : 1 ]; // FACE w/o layers
2383
2384       // add EDGE to maps
2385       if ( !fWOL.IsNull())
2386       {
2387         TGeomID edgeInd = getMeshDS()->ShapeToIndex( edge );
2388         _sdVec[i]._shrinkShape2Shape.insert( make_pair( edgeInd, fWOL ));
2389       }
2390     }
2391   }
2392
2393   // Find the SHAPE along which to inflate _LayerEdge based on VERTEX
2394
2395   for ( size_t i = 0; i < _sdVec.size(); ++i )
2396   {
2397     shapes.Clear();
2398     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_VERTEX, shapes);
2399     for ( int iV = 1; iV <= shapes.Extent(); ++iV )
2400     {
2401       const TopoDS_Shape& vertex = shapes(iV);
2402       // find faces WOL sharing the vertex
2403       vector< TopoDS_Shape > facesWOL;
2404       size_t totalNbFaces = 0;
2405       PShapeIteratorPtr fIt = helper.GetAncestors(vertex, *_mesh, TopAbs_FACE, &_sdVec[i]._solid );
2406       while ( fIt->more())
2407       {
2408         const TopoDS_Shape* f = fIt->next();
2409         totalNbFaces++;
2410         const int fID = getMeshDS()->ShapeToIndex( *f );
2411         if ( _sdVec[i]._ignoreFaceIds.count ( fID ) /*&& !_sdVec[i]._noShrinkShapes.count( fID )*/)
2412           facesWOL.push_back( *f );
2413       }
2414       if ( facesWOL.size() == totalNbFaces || facesWOL.empty() )
2415         continue; // no layers at this vertex or no WOL
2416       TGeomID vInd = getMeshDS()->ShapeToIndex( vertex );
2417       switch ( facesWOL.size() )
2418       {
2419       case 1:
2420       {
2421         helper.SetSubShape( facesWOL[0] );
2422         if ( helper.IsRealSeam( vInd )) // inflate along a seam edge?
2423         {
2424           TopoDS_Shape seamEdge;
2425           PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE);
2426           while ( eIt->more() && seamEdge.IsNull() )
2427           {
2428             const TopoDS_Shape* e = eIt->next();
2429             if ( helper.IsRealSeam( *e ) )
2430               seamEdge = *e;
2431           }
2432           if ( !seamEdge.IsNull() )
2433           {
2434             _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, seamEdge ));
2435             break;
2436           }
2437         }
2438         _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, facesWOL[0] ));
2439         break;
2440       }
2441       case 2:
2442       {
2443         // find an edge shared by 2 faces
2444         PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE);
2445         while ( eIt->more())
2446         {
2447           const TopoDS_Shape* e = eIt->next();
2448           if ( helper.IsSubShape( *e, facesWOL[0]) &&
2449                helper.IsSubShape( *e, facesWOL[1]))
2450           {
2451             _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, *e )); break;
2452           }
2453         }
2454         break;
2455       }
2456       default:
2457         std::ostringstream msg;
2458         msg << "Not yet supported case: vertex bounded by ";
2459         msg << facesWOL.size();
2460         msg << " faces without layer at coordinates (";
2461         TopoDS_Vertex v = TopoDS::Vertex(vertex);
2462         gp_Pnt p = BRep_Tool::Pnt(v);
2463         msg << p.X() << ", " << p.Y() << ", " << p.Z() << ")";
2464         return error(msg.str().c_str(), _sdVec[i]._index);
2465       }
2466     }
2467   }
2468
2469   // Add to _noShrinkShapes sub-shapes of FACE's that can't be shrunk since
2470   // the algo of the SOLID sharing the FACE does not support it or for other reasons
2471   set< string > notSupportAlgos; notSupportAlgos.insert( structAlgoName );
2472   for ( size_t i = 0; i < _sdVec.size(); ++i )
2473   {
2474     map< TGeomID, TopoDS_Shape >::iterator e2f = _sdVec[i]._shrinkShape2Shape.begin();
2475     for ( ; e2f != _sdVec[i]._shrinkShape2Shape.end(); ++e2f )
2476     {
2477       const TopoDS_Shape& fWOL = e2f->second;
2478       const TGeomID     edgeID = e2f->first;
2479       TGeomID           faceID = getMeshDS()->ShapeToIndex( fWOL );
2480       TopoDS_Shape        edge = getMeshDS()->IndexToShape( edgeID );
2481       if ( edge.ShapeType() != TopAbs_EDGE )
2482         continue; // shrink shape is VERTEX
2483
2484       TopoDS_Shape solid;
2485       PShapeIteratorPtr soIt = helper.GetAncestors(fWOL, *_mesh, TopAbs_SOLID);
2486       while ( soIt->more() && solid.IsNull() )
2487       {
2488         const TopoDS_Shape* so = soIt->next();
2489         if ( !so->IsSame( _sdVec[i]._solid ))
2490           solid = *so;
2491       }
2492       if ( solid.IsNull() )
2493         continue;
2494
2495       bool noShrinkE = false;
2496       SMESH_Algo*  algo = _mesh->GetSubMesh( solid )->GetAlgo();
2497       bool isStructured = ( algo && algo->GetName() == structAlgoName );
2498       size_t     iSolid = _solids.FindIndex( solid ) - 1;
2499       if ( iSolid < _sdVec.size() && _sdVec[ iSolid ]._ignoreFaceIds.count( faceID ))
2500       {
2501         // the adjacent SOLID has NO layers on fWOL;
2502         // shrink allowed if
2503         // - there are layers on the EDGE in the adjacent SOLID
2504         // - there are NO layers in the adjacent SOLID && algo is unstructured and computed later
2505         bool hasWLAdj = (_sdVec[iSolid]._shrinkShape2Shape.count( edgeID ));
2506         bool shrinkAllowed = (( hasWLAdj ) ||
2507                               ( !isStructured && setBefore( _sdVec[ i ], _sdVec[ iSolid ] )));
2508         noShrinkE = !shrinkAllowed;
2509       }
2510       else if ( iSolid < _sdVec.size() )
2511       {
2512         // the adjacent SOLID has layers on fWOL;
2513         // check if SOLID's mesh is unstructured and then try to set it
2514         // to be computed after the i-th solid
2515         if ( isStructured || !setBefore( _sdVec[ i ], _sdVec[ iSolid ] ))
2516           noShrinkE = true; // don't shrink fWOL
2517       }
2518       else
2519       {
2520         // the adjacent SOLID has NO layers at all
2521         noShrinkE = isStructured;
2522       }
2523
2524       if ( noShrinkE )
2525       {
2526         _sdVec[i]._noShrinkShapes.insert( edgeID );
2527
2528         // check if there is a collision with to-shrink-from EDGEs in iSolid
2529         // if ( iSolid < _sdVec.size() )
2530         // {
2531         //   shapes.Clear();
2532         //   TopExp::MapShapes( fWOL, TopAbs_EDGE, shapes);
2533         //   for ( int iE = 1; iE <= shapes.Extent(); ++iE )
2534         //   {
2535         //     const TopoDS_Edge& E = TopoDS::Edge( shapes( iE ));
2536         //     const TGeomID    eID = getMeshDS()->ShapeToIndex( E );
2537         //     if ( eID == edgeID ||
2538         //          !_sdVec[iSolid]._shrinkShape2Shape.count( eID ) ||
2539         //          _sdVec[i]._noShrinkShapes.count( eID ))
2540         //       continue;
2541         //     for ( int is1st = 0; is1st < 2; ++is1st )
2542         //     {
2543         //       TopoDS_Vertex V = helper.IthVertex( is1st, E );
2544         //       if ( _sdVec[i]._noShrinkShapes.count( getMeshDS()->ShapeToIndex( V ) ))
2545         //       {
2546         //         return error("No way to make a conformal mesh with "
2547         //                      "the given set of faces with layers", _sdVec[i]._index);
2548         //       }
2549         //     }
2550         //   }
2551         // }
2552       }
2553
2554       // add VERTEXes of the edge in _noShrinkShapes, which is necessary if
2555       // _shrinkShape2Shape is different in the adjacent SOLID
2556       for ( TopoDS_Iterator vIt( edge ); vIt.More(); vIt.Next() )
2557       {
2558         TGeomID vID = getMeshDS()->ShapeToIndex( vIt.Value() );
2559         bool noShrinkV = false, noShrinkIfAdjMeshed = false;
2560
2561         if ( iSolid < _sdVec.size() )
2562         {
2563           if ( _sdVec[ iSolid ]._ignoreFaceIds.count( faceID ))
2564           {
2565             map< TGeomID, TopoDS_Shape >::iterator i2S, i2SAdj;
2566             i2S    = _sdVec[i     ]._shrinkShape2Shape.find( vID );
2567             i2SAdj = _sdVec[iSolid]._shrinkShape2Shape.find( vID );
2568             if ( i2SAdj == _sdVec[iSolid]._shrinkShape2Shape.end() )
2569               noShrinkV = (( isStructured ) ||
2570                            ( noShrinkIfAdjMeshed = i2S->second.ShapeType() == TopAbs_EDGE ));
2571             else
2572               noShrinkV = ( ! i2S->second.IsSame( i2SAdj->second ));
2573           }
2574           else
2575           {
2576             noShrinkV = noShrinkE;
2577           }
2578         }
2579         else
2580         {
2581           // the adjacent SOLID has NO layers at all
2582           if ( isStructured )
2583           {
2584             noShrinkV = true;
2585           }
2586           else
2587           {
2588             noShrinkV = noShrinkIfAdjMeshed =
2589               ( _sdVec[i]._shrinkShape2Shape[ vID ].ShapeType() == TopAbs_EDGE );
2590           }
2591         }
2592
2593         if ( noShrinkV && noShrinkIfAdjMeshed )
2594         {
2595           // noShrinkV if FACEs in the adjacent SOLID are meshed
2596           PShapeIteratorPtr fIt = helper.GetAncestors( _sdVec[i]._shrinkShape2Shape[ vID ],
2597                                                        *_mesh, TopAbs_FACE, &solid );
2598           while ( fIt->more() )
2599           {
2600             const TopoDS_Shape* f = fIt->next();
2601             if ( !f->IsSame( fWOL ))
2602             {
2603               noShrinkV = ! _mesh->GetSubMesh( *f )->IsEmpty();
2604               break;
2605             }
2606           }
2607         }
2608         if ( noShrinkV )
2609           _sdVec[i]._noShrinkShapes.insert( vID );
2610       }
2611
2612     } // loop on _sdVec[i]._shrinkShape2Shape
2613   } // loop on _sdVec to fill in _SolidData::_noShrinkShapes
2614
2615
2616     // add FACEs of other SOLIDs to _ignoreFaceIds
2617   for ( size_t i = 0; i < _sdVec.size(); ++i )
2618   {
2619     shapes.Clear();
2620     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_FACE, shapes);
2621
2622     for ( exp.Init( _mesh->GetShapeToMesh(), TopAbs_FACE ); exp.More(); exp.Next() )
2623     {
2624       if ( !shapes.Contains( exp.Current() ))
2625         _sdVec[i]._ignoreFaceIds.insert( getMeshDS()->ShapeToIndex( exp.Current() ));
2626     }
2627   }
2628
2629   return true;
2630 }
2631
2632 //================================================================================
2633 /*!
2634  * \brief Finds FACEs w/o layers for a given SOLID by an hypothesis
2635  */
2636 //================================================================================
2637
2638 void _ViscousBuilder::getIgnoreFaces(const TopoDS_Shape&             solid,
2639                                      const StdMeshers_ViscousLayers* hyp,
2640                                      const TopoDS_Shape&             hypShape,
2641                                      set<TGeomID>&                   ignoreFaceIds)
2642 {
2643   TopExp_Explorer exp;
2644
2645   vector<TGeomID> ids = hyp->GetBndShapes();
2646   if ( hyp->IsToIgnoreShapes() ) // FACEs to ignore are given
2647   {
2648     for ( size_t ii = 0; ii < ids.size(); ++ii )
2649     {
2650       const TopoDS_Shape& s = getMeshDS()->IndexToShape( ids[ii] );
2651       if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2652         ignoreFaceIds.insert( ids[ii] );
2653     }
2654   }
2655   else // FACEs with layers are given
2656   {
2657     exp.Init( solid, TopAbs_FACE );
2658     for ( ; exp.More(); exp.Next() )
2659     {
2660       TGeomID faceInd = getMeshDS()->ShapeToIndex( exp.Current() );
2661       if ( find( ids.begin(), ids.end(), faceInd ) == ids.end() )
2662         ignoreFaceIds.insert( faceInd );
2663     }
2664   }
2665
2666   // ignore internal FACEs if inlets and outlets are specified
2667   if ( hyp->IsToIgnoreShapes() )
2668   {
2669     TopTools_IndexedDataMapOfShapeListOfShape solidsOfFace;
2670     TopExp::MapShapesAndAncestors( hypShape,
2671                                    TopAbs_FACE, TopAbs_SOLID, solidsOfFace);
2672
2673     for ( exp.Init( solid, TopAbs_FACE ); exp.More(); exp.Next() )
2674     {
2675       const TopoDS_Face& face = TopoDS::Face( exp.Current() );
2676       if ( SMESH_MesherHelper::NbAncestors( face, *_mesh, TopAbs_SOLID ) < 2 )
2677         continue;
2678
2679       int nbSolids = solidsOfFace.FindFromKey( face ).Extent();
2680       if ( nbSolids > 1 )
2681         ignoreFaceIds.insert( getMeshDS()->ShapeToIndex( face ));
2682     }
2683   }
2684 }
2685
2686 //================================================================================
2687 /*!
2688  * \brief Create the inner surface of the viscous layer and prepare data for infation
2689  */
2690 //================================================================================
2691
2692 bool _ViscousBuilder::makeLayer(_SolidData& data)
2693 {
2694   // make a map to find new nodes on sub-shapes shared with other SOLID
2695   map< TGeomID, TNode2Edge* >::iterator s2ne;
2696   map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
2697   for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
2698   {
2699     TGeomID shapeInd = s2s->first;
2700     for ( size_t i = 0; i < _sdVec.size(); ++i )
2701     {
2702       if ( _sdVec[i]._index == data._index ) continue;
2703       map< TGeomID, TopoDS_Shape >::iterator s2s2 = _sdVec[i]._shrinkShape2Shape.find( shapeInd );
2704       if ( s2s2 != _sdVec[i]._shrinkShape2Shape.end() &&
2705            *s2s == *s2s2 && !_sdVec[i]._n2eMap.empty() )
2706       {
2707         data._s2neMap.insert( make_pair( shapeInd, &_sdVec[i]._n2eMap ));
2708         break;
2709       }
2710     }
2711   }
2712
2713   // Create temporary faces and _LayerEdge's
2714
2715   debugMsg( "######################" );
2716   dumpFunction(SMESH_Comment("makeLayers_")<<data._index);
2717
2718   vector< _EdgesOnShape >& edgesByGeom = data._edgesOnShape;
2719
2720   data._stepSize = Precision::Infinite();
2721   data._stepSizeNodes[0] = 0;
2722
2723   SMESH_MesherHelper helper( *_mesh );
2724   helper.SetSubShape( data._solid );
2725   helper.SetElementsOnShape( true );
2726
2727   vector< const SMDS_MeshNode*> newNodes; // of a mesh face
2728   TNode2Edge::iterator n2e2;
2729
2730   // make _LayerEdge's
2731   for ( TopExp_Explorer exp( data._solid, TopAbs_FACE ); exp.More(); exp.Next() )
2732   {
2733     const TopoDS_Face& F = TopoDS::Face( exp.Current() );
2734     SMESH_subMesh*    sm = _mesh->GetSubMesh( F );
2735     const TGeomID     id = sm->GetId();
2736     if ( edgesByGeom[ id ]._shape.IsNull() )
2737       continue; // no layers
2738     SMESH_ProxyMesh::SubMesh* proxySub =
2739       data._proxyMesh->getFaceSubM( F, /*create=*/true);
2740
2741     SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
2742     if ( !smDS ) return error(SMESH_Comment("Not meshed face ") << id, data._index );
2743
2744     SMDS_ElemIteratorPtr eIt = smDS->GetElements();
2745     while ( eIt->more() )
2746     {
2747       const SMDS_MeshElement* face = eIt->next();
2748       double          faceMaxCosin = -1;
2749       _LayerEdge*     maxCosinEdge = 0;
2750       int             nbDegenNodes = 0;
2751
2752       newNodes.resize( face->NbCornerNodes() );
2753       for ( size_t i = 0 ; i < newNodes.size(); ++i )
2754       {
2755         const SMDS_MeshNode* n = face->GetNode( i );
2756         const int      shapeID = n->getshapeId();
2757         const bool onDegenShap = helper.IsDegenShape( shapeID );
2758         const bool onDegenEdge = ( onDegenShap && n->GetPosition()->GetDim() == 1 );
2759         if ( onDegenShap )
2760         {
2761           if ( onDegenEdge )
2762           {
2763             // substitute n on a degenerated EDGE with a node on a corresponding VERTEX
2764             const TopoDS_Shape& E = getMeshDS()->IndexToShape( shapeID );
2765             TopoDS_Vertex       V = helper.IthVertex( 0, TopoDS::Edge( E ));
2766             if ( const SMDS_MeshNode* vN = SMESH_Algo::VertexNode( V, getMeshDS() )) {
2767               n = vN;
2768               nbDegenNodes++;
2769             }
2770           }
2771           else
2772           {
2773             nbDegenNodes++;
2774           }
2775         }
2776         TNode2Edge::iterator n2e = data._n2eMap.insert({ n, nullptr }).first;
2777         if ( !(*n2e).second )
2778         {
2779           // add a _LayerEdge
2780           _LayerEdge* edge = _Factory::NewLayerEdge();
2781           edge->_nodes.push_back( n );
2782           n2e->second = edge;
2783           edgesByGeom[ shapeID ]._edges.push_back( edge );
2784           const bool noShrink = data._noShrinkShapes.count( shapeID );
2785
2786           SMESH_TNodeXYZ xyz( n );
2787
2788           // set edge data or find already refined _LayerEdge and get data from it
2789           if (( !noShrink                                                     ) &&
2790               ( n->GetPosition()->GetTypeOfPosition() != SMDS_TOP_FACE        ) &&
2791               (( s2ne = data._s2neMap.find( shapeID )) != data._s2neMap.end() ) &&
2792               (( n2e2 = (*s2ne).second->find( n )) != s2ne->second->end()     ))
2793           {
2794             _LayerEdge* foundEdge = (*n2e2).second;
2795             gp_XYZ        lastPos = edge->Copy( *foundEdge, edgesByGeom[ shapeID ], helper );
2796             foundEdge->_pos.push_back( lastPos );
2797             // location of the last node is modified and we restore it by foundEdge->_pos.back()
2798             const_cast< SMDS_MeshNode* >
2799               ( edge->_nodes.back() )->setXYZ( xyz.X(), xyz.Y(), xyz.Z() );
2800           }
2801           else
2802           {
2803             if ( !noShrink )
2804             {
2805               edge->_nodes.push_back( helper.AddNode( xyz.X(), xyz.Y(), xyz.Z() ));
2806             }
2807             if ( !setEdgeData( *edge, edgesByGeom[ shapeID ], helper, data ))
2808               return false;
2809
2810             if ( edge->_nodes.size() < 2 && !noShrink )
2811               edge->Block( data ); // a sole node is moved only if noShrink
2812           }
2813           dumpMove(edge->_nodes.back());
2814
2815           if ( edge->_cosin > faceMaxCosin && edge->_nodes.size() > 1 )
2816           {
2817             faceMaxCosin = edge->_cosin;
2818             maxCosinEdge = edge;
2819           }
2820         }
2821         newNodes[ i ] = n2e->second->_nodes.back();
2822
2823         if ( onDegenEdge )
2824           data._n2eMap.insert( make_pair( face->GetNode( i ), n2e->second ));
2825       }
2826       if ( newNodes.size() - nbDegenNodes < 2 )
2827         continue;
2828
2829       // create a temporary face
2830       const SMDS_MeshElement* newFace =
2831         new _TmpMeshFace( newNodes, --_tmpFaceID, face->GetShapeID(), face );
2832       proxySub->AddElement( newFace );
2833
2834       // compute inflation step size by min size of element on a convex surface
2835       if ( faceMaxCosin > theMinSmoothCosin )
2836         limitStepSize( data, face, maxCosinEdge );
2837
2838     } // loop on 2D elements on a FACE
2839   } // loop on FACEs of a SOLID to create _LayerEdge's
2840
2841
2842   // Set _LayerEdge::_neibors
2843   TNode2Edge::iterator n2e;
2844   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
2845   {
2846     _EdgesOnShape& eos = data._edgesOnShape[iS];
2847     for ( size_t i = 0; i < eos._edges.size(); ++i )
2848     {
2849       _LayerEdge* edge = eos._edges[i];
2850       TIDSortedNodeSet nearNodes;
2851       SMDS_ElemIteratorPtr fIt = edge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
2852       while ( fIt->more() )
2853       {
2854         const SMDS_MeshElement* f = fIt->next();
2855         if ( !data._ignoreFaceIds.count( f->getshapeId() ))
2856           nearNodes.insert( f->begin_nodes(), f->end_nodes() );
2857       }
2858       nearNodes.erase( edge->_nodes[0] );
2859       edge->_neibors.reserve( nearNodes.size() );
2860       TIDSortedNodeSet::iterator node = nearNodes.begin();
2861       for ( ; node != nearNodes.end(); ++node )
2862         if (( n2e = data._n2eMap.find( *node )) != data._n2eMap.end() )
2863           edge->_neibors.push_back( n2e->second );
2864     }
2865
2866     // Fix uv of nodes on periodic FACEs (bos #20643)
2867
2868     if ( eos.ShapeType() != TopAbs_EDGE ||
2869          eos.SWOLType()  != TopAbs_FACE ||
2870          eos.size() == 0 )
2871       continue;
2872
2873     const TopoDS_Face& F = TopoDS::Face( eos._sWOL );
2874     SMESH_MesherHelper faceHelper( *_mesh );
2875     faceHelper.SetSubShape( F );
2876     faceHelper.ToFixNodeParameters( true );
2877     if ( faceHelper.GetPeriodicIndex() == 0 )
2878       continue;
2879
2880     SMESHDS_SubMesh* smDS = getMeshDS()->MeshElements( F );
2881     if ( !smDS || smDS->GetNodes() == 0 )
2882       continue;
2883
2884     bool toCheck = true;
2885     const double tol = 2 * helper.MaxTolerance( F );
2886     for ( SMDS_NodeIteratorPtr nIt = smDS->GetNodes(); nIt->more(); )
2887     {
2888       const SMDS_MeshNode* node = nIt->next();
2889       gp_XY uvNew( Precision::Infinite(), 0 );
2890       if ( toCheck )
2891       {
2892         toCheck = false;
2893         gp_XY uv = faceHelper.GetNodeUV( F, node );
2894         if ( ! faceHelper.CheckNodeUV( F, node, uvNew, tol, /*force=*/true ))
2895           break; // projection on F failed
2896         if (( uv - uvNew ).Modulus() < Precision::Confusion() )
2897           break; // current uv is OK
2898       }
2899       faceHelper.CheckNodeUV( F, node, uvNew, tol, /*force=*/true );
2900     }
2901   }
2902
2903   data._epsilon = 1e-7;
2904   if ( data._stepSize < 1. )
2905     data._epsilon *= data._stepSize;
2906
2907   if ( !findShapesToSmooth( data )) // _LayerEdge::_maxLen is computed here
2908     return false;
2909
2910   // limit data._stepSize depending on surface curvature and fill data._convexFaces
2911   limitStepSizeByCurvature( data ); // !!! it must be before node substitution in _Simplex
2912
2913   // Set target nodes into _Simplex and _LayerEdge's to _2NearEdges
2914   const SMDS_MeshNode* nn[2];
2915   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
2916   {
2917     _EdgesOnShape& eos = data._edgesOnShape[iS];
2918     for ( size_t i = 0; i < eos._edges.size(); ++i )
2919     {
2920       _LayerEdge* edge = eos._edges[i];
2921       if ( edge->IsOnEdge() )
2922       {
2923         // get neighbor nodes
2924         bool hasData = ( edge->_2neibors->_edges[0] );
2925         if ( hasData ) // _LayerEdge is a copy of another one
2926         {
2927           nn[0] = edge->_2neibors->srcNode(0);
2928           nn[1] = edge->_2neibors->srcNode(1);
2929         }
2930         else if ( !findNeiborsOnEdge( edge, nn[0],nn[1], eos, data ))
2931         {
2932           return false;
2933         }
2934         // set neighbor _LayerEdge's
2935         for ( int j = 0; j < 2; ++j )
2936         {
2937           if (( n2e = data._n2eMap.find( nn[j] )) == data._n2eMap.end() )
2938             return error("_LayerEdge not found by src node", data._index);
2939           edge->_2neibors->_edges[j] = n2e->second;
2940         }
2941         if ( !hasData )
2942           edge->SetDataByNeighbors( nn[0], nn[1], eos, helper );
2943       }
2944
2945       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
2946       {
2947         _Simplex& s = edge->_simplices[j];
2948         s._nNext = data._n2eMap[ s._nNext ]->_nodes.back();
2949         s._nPrev = data._n2eMap[ s._nPrev ]->_nodes.back();
2950       }
2951
2952       // For an _LayerEdge on a degenerated EDGE, copy some data from
2953       // a corresponding _LayerEdge on a VERTEX
2954       // (issue 52453, pb on a downloaded SampleCase2-Tet-netgen-mephisto.hdf)
2955       if ( helper.IsDegenShape( edge->_nodes[0]->getshapeId() ))
2956       {
2957         // Generally we should not get here
2958         if ( eos.ShapeType() != TopAbs_EDGE )
2959           continue;
2960         TopoDS_Vertex V = helper.IthVertex( 0, TopoDS::Edge( eos._shape ));
2961         const SMDS_MeshNode* vN = SMESH_Algo::VertexNode( V, getMeshDS() );
2962         if (( n2e = data._n2eMap.find( vN )) == data._n2eMap.end() )
2963           continue;
2964         const _LayerEdge* vEdge = n2e->second;
2965         edge->_normal    = vEdge->_normal;
2966         edge->_lenFactor = vEdge->_lenFactor;
2967         edge->_cosin     = vEdge->_cosin;
2968       }
2969
2970     } // loop on data._edgesOnShape._edges
2971   } // loop on data._edgesOnShape
2972
2973   // fix _LayerEdge::_2neibors on EDGEs to smooth
2974   // map< TGeomID,Handle(Geom_Curve)>::iterator e2c = data._edge2curve.begin();
2975   // for ( ; e2c != data._edge2curve.end(); ++e2c )
2976   //   if ( !e2c->second.IsNull() )
2977   //   {
2978   //     if ( _EdgesOnShape* eos = data.GetShapeEdges( e2c->first ))
2979   //       data.Sort2NeiborsOnEdge( eos->_edges );
2980   //   }
2981
2982   dumpFunctionEnd();
2983   return true;
2984 }
2985
2986 //================================================================================
2987 /*!
2988  * \brief Compute inflation step size by min size of element on a convex surface
2989  */
2990 //================================================================================
2991
2992 void _ViscousBuilder::limitStepSize( _SolidData&             data,
2993                                      const SMDS_MeshElement* face,
2994                                      const _LayerEdge*       maxCosinEdge )
2995 {
2996   int iN = 0;
2997   double minSize = 10 * data._stepSize;
2998   const int nbNodes = face->NbCornerNodes();
2999   for ( int i = 0; i < nbNodes; ++i )
3000   {
3001     const SMDS_MeshNode* nextN = face->GetNode( SMESH_MesherHelper::WrapIndex( i+1, nbNodes ));
3002     const SMDS_MeshNode*  curN = face->GetNode( i );
3003     if ( nextN->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ||
3004          curN-> GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
3005     {
3006       double dist = SMESH_TNodeXYZ( curN ).Distance( nextN );
3007       if ( dist < minSize )
3008         minSize = dist, iN = i;
3009     }
3010   }
3011   double newStep = 0.8 * minSize / maxCosinEdge->_lenFactor;
3012   if ( newStep < data._stepSize )
3013   {
3014     data._stepSize = newStep;
3015     data._stepSizeCoeff = 0.8 / maxCosinEdge->_lenFactor;
3016     data._stepSizeNodes[0] = face->GetNode( iN );
3017     data._stepSizeNodes[1] = face->GetNode( SMESH_MesherHelper::WrapIndex( iN+1, nbNodes ));
3018   }
3019 }
3020
3021 //================================================================================
3022 /*!
3023  * \brief Compute inflation step size by min size of element on a convex surface
3024  */
3025 //================================================================================
3026
3027 void _ViscousBuilder::limitStepSize( _SolidData& data, const double minSize )
3028 {
3029   if ( minSize < data._stepSize )
3030   {
3031     data._stepSize = minSize;
3032     if ( data._stepSizeNodes[0] )
3033     {
3034       double dist =
3035         SMESH_TNodeXYZ(data._stepSizeNodes[0]).Distance(data._stepSizeNodes[1]);
3036       data._stepSizeCoeff = data._stepSize / dist;
3037     }
3038   }
3039 }
3040
3041 //================================================================================
3042 /*!
3043  * \brief Limit data._stepSize by evaluating curvature of shapes and fill data._convexFaces
3044  */
3045 //================================================================================
3046
3047 void _ViscousBuilder::limitStepSizeByCurvature( _SolidData& data )
3048 {
3049   SMESH_MesherHelper helper( *_mesh );
3050
3051   BRepLProp_SLProps surfProp( 2, 1e-6 );
3052   data._convexFaces.clear();
3053
3054   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
3055   {
3056     _EdgesOnShape& eof = data._edgesOnShape[iS];
3057     if ( eof.ShapeType() != TopAbs_FACE ||
3058          data._ignoreFaceIds.count( eof._shapeID ))
3059       continue;
3060
3061     TopoDS_Face        F = TopoDS::Face( eof._shape );
3062     const TGeomID faceID = eof._shapeID;
3063
3064     BRepAdaptor_Surface surface( F, false );
3065     surfProp.SetSurface( surface );
3066
3067     _ConvexFace cnvFace;
3068     cnvFace._face = F;
3069     cnvFace._normalsFixed = false;
3070     cnvFace._isTooCurved = false;
3071     cnvFace._normalsFixedOnBorders = false;
3072
3073     double maxCurvature = cnvFace.GetMaxCurvature( data, eof, surfProp, helper );
3074     if ( maxCurvature > 0 )
3075     {
3076       limitStepSize( data, 0.9 / maxCurvature );
3077       findEdgesToUpdateNormalNearConvexFace( cnvFace, data, helper );
3078     }
3079     if ( !cnvFace._isTooCurved ) continue;
3080
3081     _ConvexFace & convFace =
3082       data._convexFaces.insert( make_pair( faceID, cnvFace )).first->second;
3083
3084     // skip a closed surface (data._convexFaces is useful anyway)
3085     bool isClosedF = false;
3086     helper.SetSubShape( F );
3087     if ( helper.HasRealSeam() )
3088     {
3089       // in the closed surface there must be a closed EDGE
3090       for ( TopExp_Explorer eIt( F, TopAbs_EDGE ); eIt.More() && !isClosedF; eIt.Next() )
3091         isClosedF = helper.IsClosedEdge( TopoDS::Edge( eIt.Current() ));
3092     }
3093     if ( isClosedF )
3094     {
3095       // limit _LayerEdge::_maxLen on the FACE
3096       const double oriFactor    = ( F.Orientation() == TopAbs_REVERSED ? +1. : -1. );
3097       const double minCurvature =
3098         1. / ( eof._hyp.GetTotalThickness() * ( 1 + theThickToIntersection ));
3099       map< TGeomID, _EdgesOnShape* >::iterator id2eos = cnvFace._subIdToEOS.find( faceID );
3100       if ( id2eos != cnvFace._subIdToEOS.end() )
3101       {
3102         _EdgesOnShape& eos = * id2eos->second;
3103         for ( size_t i = 0; i < eos._edges.size(); ++i )
3104         {
3105           _LayerEdge* ledge = eos._edges[ i ];
3106           gp_XY uv = helper.GetNodeUV( F, ledge->_nodes[0] );
3107           surfProp.SetParameters( uv.X(), uv.Y() );
3108           if ( surfProp.IsCurvatureDefined() )
3109           {
3110             double curvature = Max( surfProp.MaxCurvature() * oriFactor,
3111                                     surfProp.MinCurvature() * oriFactor );
3112             if ( curvature > minCurvature )
3113               ledge->SetMaxLen( Min( ledge->_maxLen, 1. / curvature ));
3114           }
3115         }
3116       }
3117       continue;
3118     }
3119
3120     // Fill _ConvexFace::_simplexTestEdges. These _LayerEdge's are used to detect
3121     // prism distortion.
3122     map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.find( faceID );
3123     if ( id2eos != convFace._subIdToEOS.end() && !id2eos->second->_edges.empty() )
3124     {
3125       // there are _LayerEdge's on the FACE it-self;
3126       // select _LayerEdge's near EDGEs
3127       _EdgesOnShape& eos = * id2eos->second;
3128       for ( size_t i = 0; i < eos._edges.size(); ++i )
3129       {
3130         _LayerEdge* ledge = eos._edges[ i ];
3131         for ( size_t j = 0; j < ledge->_simplices.size(); ++j )
3132           if ( ledge->_simplices[j]._nNext->GetPosition()->GetDim() < 2 )
3133           {
3134             // do not select _LayerEdge's neighboring sharp EDGEs
3135             bool sharpNbr = false;
3136             for ( size_t iN = 0; iN < ledge->_neibors.size()  && !sharpNbr; ++iN )
3137               sharpNbr = ( ledge->_neibors[iN]->_cosin > theMinSmoothCosin );
3138             if ( !sharpNbr )
3139               convFace._simplexTestEdges.push_back( ledge );
3140             break;
3141           }
3142       }
3143     }
3144     else
3145     {
3146       // where there are no _LayerEdge's on a _ConvexFace,
3147       // as e.g. on a fillet surface with no internal nodes - issue 22580,
3148       // so that collision of viscous internal faces is not detected by check of
3149       // intersection of _LayerEdge's with the viscous internal faces.
3150
3151       set< const SMDS_MeshNode* > usedNodes;
3152
3153       // look for _LayerEdge's with null _sWOL
3154       id2eos = convFace._subIdToEOS.begin();
3155       for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
3156       {
3157         _EdgesOnShape& eos = * id2eos->second;
3158         if ( !eos._sWOL.IsNull() )
3159           continue;
3160         for ( size_t i = 0; i < eos._edges.size(); ++i )
3161         {
3162           _LayerEdge* ledge = eos._edges[ i ];
3163           const SMDS_MeshNode* srcNode = ledge->_nodes[0];
3164           if ( !usedNodes.insert( srcNode ).second ) continue;
3165
3166           for ( size_t i = 0; i < ledge->_simplices.size(); ++i )
3167           {
3168             usedNodes.insert( ledge->_simplices[i]._nPrev );
3169             usedNodes.insert( ledge->_simplices[i]._nNext );
3170           }
3171           convFace._simplexTestEdges.push_back( ledge );
3172         }
3173       }
3174     }
3175   } // loop on FACEs of data._solid
3176 }
3177
3178 //================================================================================
3179 /*!
3180  * \brief Detect shapes (and _LayerEdge's on them) to smooth
3181  */
3182 //================================================================================
3183
3184 bool _ViscousBuilder::findShapesToSmooth( _SolidData& data )
3185 {
3186   // define allowed thickness
3187   computeGeomSize( data ); // compute data._geomSize and _LayerEdge::_maxLen
3188
3189
3190   // Find shapes needing smoothing; such a shape has _LayerEdge._normal on it's
3191   // boundary inclined to the shape at a sharp angle
3192
3193   TopTools_MapOfShape edgesOfSmooFaces;
3194   SMESH_MesherHelper helper( *_mesh );
3195   bool ok = true;
3196
3197   vector< _EdgesOnShape >& edgesByGeom = data._edgesOnShape;
3198   data._nbShapesToSmooth = 0;
3199
3200   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check FACEs
3201   {
3202     _EdgesOnShape& eos = edgesByGeom[iS];
3203     eos._toSmooth = false;
3204     if ( eos._edges.empty() || eos.ShapeType() != TopAbs_FACE )
3205       continue;
3206
3207     double tgtThick = eos._hyp.GetTotalThickness();
3208     SMESH_subMeshIteratorPtr subIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false );
3209     while ( subIt->more() && !eos._toSmooth )
3210     {
3211       TGeomID iSub = subIt->next()->GetId();
3212       const vector<_LayerEdge*>& eSub = edgesByGeom[ iSub ]._edges;
3213       if ( eSub.empty() ) continue;
3214
3215       double faceSize;
3216       for ( size_t i = 0; i < eSub.size() && !eos._toSmooth; ++i )
3217         if ( eSub[i]->_cosin > theMinSmoothCosin )
3218         {
3219           SMDS_ElemIteratorPtr fIt = eSub[i]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
3220           while ( fIt->more() && !eos._toSmooth )
3221           {
3222             const SMDS_MeshElement* face = fIt->next();
3223             if ( face->getshapeId() == eos._shapeID &&
3224                  getDistFromEdge( face, eSub[i]->_nodes[0], faceSize ))
3225             {
3226               eos._toSmooth = needSmoothing( eSub[i]->_cosin,
3227                                              tgtThick * eSub[i]->_lenFactor,
3228                                              faceSize);
3229             }
3230           }
3231         }
3232     }
3233     if ( eos._toSmooth )
3234     {
3235       for ( TopExp_Explorer eExp( edgesByGeom[iS]._shape, TopAbs_EDGE ); eExp.More(); eExp.Next() )
3236         edgesOfSmooFaces.Add( eExp.Current() );
3237
3238       data.PrepareEdgesToSmoothOnFace( &edgesByGeom[iS], /*substituteSrcNodes=*/false );
3239     }
3240     data._nbShapesToSmooth += eos._toSmooth;
3241
3242   }  // check FACEs
3243
3244   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check EDGEs
3245   {
3246     _EdgesOnShape& eos = edgesByGeom[iS];
3247     eos._edgeSmoother = NULL;
3248     if ( eos._edges.empty() || eos.ShapeType() != TopAbs_EDGE ) continue;
3249     if ( !eos._hyp.ToSmooth() ) continue;
3250
3251     const TopoDS_Edge& E = TopoDS::Edge( edgesByGeom[iS]._shape );
3252     if ( SMESH_Algo::isDegenerated( E ) || !edgesOfSmooFaces.Contains( E ))
3253       continue;
3254
3255     double tgtThick = eos._hyp.GetTotalThickness(), h0 = eos._hyp.Get1stLayerThickness();
3256     for ( TopoDS_Iterator vIt( E ); vIt.More() && !eos._toSmooth; vIt.Next() )
3257     {
3258       TGeomID iV = getMeshDS()->ShapeToIndex( vIt.Value() );
3259       vector<_LayerEdge*>& eV = edgesByGeom[ iV ]._edges;
3260       if ( eV.empty() || eV[0]->Is( _LayerEdge::MULTI_NORMAL )) continue;
3261       gp_Vec  eDir    = getEdgeDir( E, TopoDS::Vertex( vIt.Value() ), h0 );
3262       double angle    = eDir.Angle( eV[0]->_normal );
3263       double cosin    = Cos( angle );
3264       double cosinAbs = Abs( cosin );
3265       if ( cosinAbs > theMinSmoothCosin )
3266       {
3267         // always smooth analytic EDGEs
3268         Handle(Geom_Curve) curve = _Smoother1D::CurveForSmooth( E, eos, helper );
3269         eos._toSmooth = ! curve.IsNull();
3270
3271         // compare tgtThick with the length of an end segment
3272         SMDS_ElemIteratorPtr eIt = eV[0]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Edge);
3273         while ( eIt->more() && !eos._toSmooth )
3274         {
3275           const SMDS_MeshElement* endSeg = eIt->next();
3276           if ( endSeg->getshapeId() == (int) iS )
3277           {
3278             double segLen =
3279               SMESH_TNodeXYZ( endSeg->GetNode( 0 )).Distance( endSeg->GetNode( 1 ));
3280             eos._toSmooth = needSmoothing( cosinAbs, tgtThick * eV[0]->_lenFactor, segLen );
3281           }
3282         }
3283         if ( eos._toSmooth )
3284         {
3285           eos._edgeSmoother = new _Smoother1D( curve, eos );
3286
3287           // for ( size_t i = 0; i < eos._edges.size(); ++i )
3288           //   eos._edges[i]->Set( _LayerEdge::TO_SMOOTH );
3289         }
3290       }
3291     }
3292     data._nbShapesToSmooth += eos._toSmooth;
3293
3294   } // check EDGEs
3295
3296   // Reset _cosin if no smooth is allowed by the user
3297   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS )
3298   {
3299     _EdgesOnShape& eos = edgesByGeom[iS];
3300     if ( eos._edges.empty() ) continue;
3301
3302     if ( !eos._hyp.ToSmooth() )
3303       for ( size_t i = 0; i < eos._edges.size(); ++i )
3304         //eos._edges[i]->SetCosin( 0 ); // keep _cosin to use in limitMaxLenByCurvature()
3305         eos._edges[i]->_lenFactor = 1;
3306   }
3307
3308
3309   // Fill _eosC1 to make that C1 FACEs and EDGEs between them to be smoothed as a whole
3310
3311   TopTools_MapOfShape c1VV;
3312
3313   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check FACEs
3314   {
3315     _EdgesOnShape& eos = edgesByGeom[iS];
3316     if ( eos._edges.empty() ||
3317          eos.ShapeType() != TopAbs_FACE ||
3318          !eos._toSmooth )
3319       continue;
3320
3321     // check EDGEs of a FACE
3322     TopTools_MapOfShape checkedEE, allVV;
3323     list< SMESH_subMesh* > smQueue( 1, eos._subMesh ); // sm of FACEs
3324     while ( !smQueue.empty() )
3325     {
3326       SMESH_subMesh* sm = smQueue.front();
3327       smQueue.pop_front();
3328       SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(/*includeSelf=*/false);
3329       while ( smIt->more() )
3330       {
3331         sm = smIt->next();
3332         if ( sm->GetSubShape().ShapeType() == TopAbs_VERTEX )
3333           allVV.Add( sm->GetSubShape() );
3334         if ( sm->GetSubShape().ShapeType() != TopAbs_EDGE ||
3335              !checkedEE.Add( sm->GetSubShape() ))
3336           continue;
3337
3338         _EdgesOnShape*      eoe = data.GetShapeEdges( sm->GetId() );
3339         vector<_LayerEdge*>& eE = eoe->_edges;
3340         if ( eE.empty() || !eoe->_sWOL.IsNull() )
3341           continue;
3342
3343         bool isC1 = true; // check continuity along an EDGE
3344         for ( size_t i = 0; i < eE.size() && isC1; ++i )
3345           isC1 = ( Abs( eE[i]->_cosin ) < theMinSmoothCosin );
3346         if ( !isC1 )
3347           continue;
3348
3349         // check that mesh faces are C1 as well
3350         {
3351           gp_XYZ norm1, norm2;
3352           const SMDS_MeshNode*   n = eE[ eE.size() / 2 ]->_nodes[0];
3353           SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
3354           if ( !SMESH_MeshAlgos::FaceNormal( fIt->next(), norm1, /*normalized=*/true ))
3355             continue;
3356           while ( fIt->more() && isC1 )
3357             isC1 = ( SMESH_MeshAlgos::FaceNormal( fIt->next(), norm2, /*normalized=*/true ) &&
3358                      Abs( norm1 * norm2 ) >= ( 1. - theMinSmoothCosin ));
3359           if ( !isC1 )
3360             continue;
3361         }
3362
3363         // add the EDGE and an adjacent FACE to _eosC1
3364         PShapeIteratorPtr fIt = helper.GetAncestors( sm->GetSubShape(), *_mesh, TopAbs_FACE );
3365         while ( const TopoDS_Shape* face = fIt->next() )
3366         {
3367           _EdgesOnShape* eof = data.GetShapeEdges( *face );
3368           if ( !eof ) continue; // other solid
3369           if ( eos._shapeID == eof->_shapeID ) continue;
3370           if ( !eos.HasC1( eof ))
3371           {
3372             // check the FACEs
3373             eos._eosC1.push_back( eof );
3374             eof->_toSmooth = false;
3375             data.PrepareEdgesToSmoothOnFace( eof, /*substituteSrcNodes=*/false );
3376             smQueue.push_back( eof->_subMesh );
3377           }
3378           if ( !eos.HasC1( eoe ))
3379           {
3380             eos._eosC1.push_back( eoe );
3381             eoe->_toSmooth = false;
3382             data.PrepareEdgesToSmoothOnFace( eoe, /*substituteSrcNodes=*/false );
3383           }
3384         }
3385       }
3386     }
3387     if ( eos._eosC1.empty() )
3388       continue;
3389
3390     // check VERTEXes of C1 FACEs
3391     TopTools_MapIteratorOfMapOfShape vIt( allVV );
3392     for ( ; vIt.More(); vIt.Next() )
3393     {
3394       _EdgesOnShape* eov = data.GetShapeEdges( vIt.Key() );
3395       if ( !eov || eov->_edges.empty() || !eov->_sWOL.IsNull() )
3396         continue;
3397
3398       bool isC1 = true; // check if all adjacent FACEs are in eos._eosC1
3399       PShapeIteratorPtr fIt = helper.GetAncestors( vIt.Key(), *_mesh, TopAbs_FACE );
3400       while ( const TopoDS_Shape* face = fIt->next() )
3401       {
3402         _EdgesOnShape* eof = data.GetShapeEdges( *face );
3403         if ( !eof ) continue; // other solid
3404         isC1 = ( face->IsSame( eos._shape ) || eos.HasC1( eof ));
3405         if ( !isC1 )
3406           break;
3407       }
3408       if ( isC1 )
3409       {
3410         eos._eosC1.push_back( eov );
3411         data.PrepareEdgesToSmoothOnFace( eov, /*substituteSrcNodes=*/false );
3412         c1VV.Add( eov->_shape );
3413       }
3414     }
3415
3416   } // fill _eosC1 of FACEs
3417
3418
3419   // Find C1 EDGEs
3420
3421   vector< pair< _EdgesOnShape*, gp_XYZ > > dirOfEdges;
3422   
3423   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check VERTEXes
3424   {
3425     _EdgesOnShape& eov = edgesByGeom[iS];
3426     if ( eov._edges.empty() ||
3427          eov.ShapeType() != TopAbs_VERTEX ||
3428          c1VV.Contains( eov._shape ))
3429         continue;
3430     const TopoDS_Vertex& V = TopoDS::Vertex( eov._shape );
3431
3432     // get directions of surrounding EDGEs
3433     dirOfEdges.clear();
3434     PShapeIteratorPtr fIt = helper.GetAncestors( eov._shape, *_mesh, TopAbs_EDGE );
3435     while ( const TopoDS_Shape* e = fIt->next() )
3436     {
3437       _EdgesOnShape* eoe = data.GetShapeEdges( *e );
3438       if ( !eoe ) continue; // other solid
3439       gp_XYZ eDir = getEdgeDir( TopoDS::Edge( *e ), V, eoe->_hyp.Get1stLayerThickness() );
3440       if ( !Precision::IsInfinite( eDir.X() ))
3441         dirOfEdges.push_back( make_pair( eoe, eDir.Normalized() ));
3442     }
3443
3444     // find EDGEs with C1 directions
3445     for ( size_t i = 0; i < dirOfEdges.size(); ++i )
3446       for ( size_t j = i+1; j < dirOfEdges.size(); ++j )
3447         if ( dirOfEdges[i].first && dirOfEdges[j].first )
3448         {
3449           double dot = dirOfEdges[i].second * dirOfEdges[j].second;
3450           bool isC1 = ( dot < - ( 1. - theMinSmoothCosin ));
3451           if ( isC1 )
3452           {
3453             double maxEdgeLen = 3 * Min( eov._edges[0]->_maxLen, eov._hyp.GetTotalThickness() );
3454             for ( int isJ = 0; isJ < 2; ++isJ ) // loop on [i,j]
3455             {
3456               size_t k = isJ ? j : i;
3457               const TopoDS_Edge& e = TopoDS::Edge( dirOfEdges[k].first->_shape );
3458               double eLen = SMESH_Algo::EdgeLength( e );
3459               if ( eLen < maxEdgeLen )
3460               {
3461                 TopoDS_Shape oppV = SMESH_MesherHelper::IthVertex( 0, e );
3462                 if ( oppV.IsSame( V ))
3463                   oppV = SMESH_MesherHelper::IthVertex( 1, e );
3464                 _EdgesOnShape* eovOpp = data.GetShapeEdges( oppV );
3465                 if ( !eovOpp->_edges.empty() && dirOfEdges[k].second * eovOpp->_edges[0]->_normal < 0 )
3466                   eov._eosC1.push_back( dirOfEdges[k].first );
3467               }
3468               dirOfEdges[k].first = 0;
3469             }
3470           }
3471         }
3472   } // fill _eosC1 of VERTEXes
3473
3474
3475
3476   return ok;
3477 }
3478
3479 //================================================================================
3480 /*!
3481  * \brief Set up _SolidData::_edgesOnShape
3482  */
3483 //================================================================================
3484
3485 int _ViscousBuilder::makeEdgesOnShape()
3486 {
3487   const int nbShapes = getMeshDS()->MaxShapeIndex();
3488   int nbSolidsWL = 0;
3489
3490   for ( size_t i = 0; i < _sdVec.size(); ++i )
3491   {
3492     _SolidData& data = _sdVec[ i ];
3493     vector< _EdgesOnShape >& edgesByGeom = data._edgesOnShape;
3494     edgesByGeom.resize( nbShapes+1 );
3495
3496     // set data of _EdgesOnShape's
3497     int nbShapesWL = 0;
3498     if ( SMESH_subMesh* sm = _mesh->GetSubMesh( data._solid ))
3499     {
3500       SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(/*includeSelf=*/false);
3501       while ( smIt->more() )
3502       {
3503         sm = smIt->next();
3504         if ( sm->GetSubShape().ShapeType() == TopAbs_FACE &&
3505              data._ignoreFaceIds.count( sm->GetId() ))
3506           continue;
3507
3508         setShapeData( edgesByGeom[ sm->GetId() ], sm, data );
3509
3510         nbShapesWL += ( sm->GetSubShape().ShapeType() == TopAbs_FACE );
3511       }
3512     }
3513     if ( nbShapesWL == 0 ) // no shapes with layers in a SOLID
3514     {
3515       data._done = true;
3516       SMESHUtils::FreeVector( edgesByGeom );
3517     }
3518     else
3519     {
3520       ++nbSolidsWL;
3521     }
3522   }
3523   return nbSolidsWL;
3524 }
3525
3526 //================================================================================
3527 /*!
3528  * \brief initialize data of _EdgesOnShape
3529  */
3530 //================================================================================
3531
3532 void _ViscousBuilder::setShapeData( _EdgesOnShape& eos,
3533                                     SMESH_subMesh* sm,
3534                                     _SolidData&    data )
3535 {
3536   if ( !eos._shape.IsNull() ||
3537        sm->GetSubShape().ShapeType() == TopAbs_WIRE )
3538     return;
3539
3540   SMESH_MesherHelper helper( *_mesh );
3541
3542   eos._subMesh = sm;
3543   eos._shapeID = sm->GetId();
3544   eos._shape   = sm->GetSubShape();
3545   if ( eos.ShapeType() == TopAbs_FACE )
3546     eos._shape.Orientation( helper.GetSubShapeOri( data._solid, eos._shape ));
3547   eos._toSmooth = false;
3548   eos._data = &data;
3549   eos._mapper2D = nullptr;
3550
3551   // set _SWOL
3552   map< TGeomID, TopoDS_Shape >::const_iterator s2s =
3553     data._shrinkShape2Shape.find( eos._shapeID );
3554   if ( s2s != data._shrinkShape2Shape.end() )
3555     eos._sWOL = s2s->second;
3556
3557   eos._isRegularSWOL = true;
3558   if ( eos.SWOLType() == TopAbs_FACE )
3559   {
3560     const TopoDS_Face& F = TopoDS::Face( eos._sWOL );
3561     Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( F );
3562     eos._isRegularSWOL = ( ! surface->HasSingularities( 1e-7 ));
3563   }
3564
3565   // set _hyp
3566   if ( data._hyps.size() == 1 )
3567   {
3568     eos._hyp = data._hyps.back();
3569   }
3570   else
3571   {
3572     // compute average StdMeshers_ViscousLayers parameters
3573     map< TGeomID, const StdMeshers_ViscousLayers* >::iterator f2hyp;
3574     if ( eos.ShapeType() == TopAbs_FACE )
3575     {
3576       if (( f2hyp = data._face2hyp.find( eos._shapeID )) != data._face2hyp.end() )
3577         eos._hyp = f2hyp->second;
3578     }
3579     else
3580     {
3581       PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
3582       while ( const TopoDS_Shape* face = fIt->next() )
3583       {
3584         TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
3585         if (( f2hyp = data._face2hyp.find( faceID )) != data._face2hyp.end() )
3586           eos._hyp.Add( f2hyp->second );
3587       }
3588     }
3589   }
3590
3591   // set _faceNormals
3592   if ( ! eos._hyp.UseSurfaceNormal() )
3593   {
3594     if ( eos.ShapeType() == TopAbs_FACE ) // get normals to elements on a FACE
3595     {
3596       SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
3597       if ( !smDS ) return;
3598       eos._faceNormals.reserve( smDS->NbElements() );
3599
3600       double oriFactor = helper.IsReversedSubMesh( TopoDS::Face( eos._shape )) ? 1.: -1.;
3601       SMDS_ElemIteratorPtr eIt = smDS->GetElements();
3602       for ( ; eIt->more(); )
3603       {
3604         const SMDS_MeshElement* face = eIt->next();
3605         gp_XYZ&                 norm = eos._faceNormals[face];
3606         if ( !SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
3607           norm.SetCoord( 0,0,0 );
3608         norm *= oriFactor;
3609       }
3610     }
3611     else // find EOS of adjacent FACEs
3612     {
3613       PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
3614       while ( const TopoDS_Shape* face = fIt->next() )
3615       {
3616         TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
3617         eos._faceEOS.push_back( & data._edgesOnShape[ faceID ]);
3618         if ( eos._faceEOS.back()->_shape.IsNull() )
3619           // avoid using uninitialised _shapeID in GetNormal()
3620           eos._faceEOS.back()->_shapeID = faceID;
3621       }
3622     }
3623   }
3624 }
3625
3626 //================================================================================
3627 /*!
3628  * \brief Returns normal of a face
3629  */
3630 //================================================================================
3631
3632 bool _EdgesOnShape::GetNormal( const SMDS_MeshElement* face, gp_Vec& norm )
3633 {
3634   bool ok = false;
3635   _EdgesOnShape* eos = 0;
3636
3637   if ( face->getshapeId() == _shapeID )
3638   {
3639     eos = this;
3640   }
3641   else
3642   {
3643     for ( size_t iF = 0; iF < _faceEOS.size() && !eos; ++iF )
3644       if ( face->getshapeId() == _faceEOS[ iF ]->_shapeID )
3645         eos = _faceEOS[ iF ];
3646   }
3647
3648   if (( eos ) &&
3649       ( ok = ( eos->_faceNormals.count( face ) )))
3650   {
3651     norm = eos->_faceNormals[ face ];
3652   }
3653   else if ( !eos )
3654   {
3655     debugMsg( "_EdgesOnShape::Normal() failed for face "<<face->GetID()
3656               << " on _shape #" << _shapeID );
3657   }
3658   return ok;
3659 }
3660
3661 //================================================================================
3662 /*!
3663  * \brief EdgesOnShape destructor
3664  */
3665 //================================================================================
3666
3667 _EdgesOnShape::~_EdgesOnShape()
3668 {
3669   delete _edgeSmoother;
3670   delete _mapper2D;
3671 }
3672
3673 //================================================================================
3674 /*!
3675  * \brief Set data of _LayerEdge needed for smoothing
3676  */
3677 //================================================================================
3678
3679 bool _ViscousBuilder::setEdgeData(_LayerEdge&         edge,
3680                                   _EdgesOnShape&      eos,
3681                                   SMESH_MesherHelper& helper,
3682                                   _SolidData&         data)
3683 {
3684   const SMDS_MeshNode* node = edge._nodes[0]; // source node
3685
3686   edge._len          = 0;
3687   edge._maxLen       = Precision::Infinite();
3688   edge._minAngle     = 0;
3689   edge._2neibors     = 0;
3690   edge._curvature    = 0;
3691   edge._flags        = 0;
3692   edge._smooFunction = 0;
3693
3694   // --------------------------
3695   // Compute _normal and _cosin
3696   // --------------------------
3697
3698   edge._cosin     = 0;
3699   edge._lenFactor = 1.;
3700   edge._normal.SetCoord(0,0,0);
3701   _Simplex::GetSimplices( node, edge._simplices, data._ignoreFaceIds, &data );
3702
3703   int totalNbFaces = 0;
3704   TopoDS_Face F;
3705   std::pair< TopoDS_Face, gp_XYZ > face2Norm[20];
3706   gp_Vec geomNorm;
3707   bool normOK = true;
3708
3709   const bool onShrinkShape = !eos._sWOL.IsNull();
3710   const bool useGeometry   = (( eos._hyp.UseSurfaceNormal() ) ||
3711                               ( eos.ShapeType() != TopAbs_FACE /*&& !onShrinkShape*/ ));
3712
3713   // get geom FACEs the node lies on
3714   //if ( useGeometry )
3715   {
3716     set<TGeomID> faceIds;
3717     if  ( eos.ShapeType() == TopAbs_FACE )
3718     {
3719       faceIds.insert( eos._shapeID );
3720     }
3721     else
3722     {
3723       SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
3724       while ( fIt->more() )
3725         faceIds.insert( fIt->next()->getshapeId() );
3726     }
3727     set<TGeomID>::iterator id = faceIds.begin();
3728     for ( ; id != faceIds.end(); ++id )
3729     {
3730       const TopoDS_Shape& s = getMeshDS()->IndexToShape( *id );
3731       if ( s.IsNull() || s.ShapeType() != TopAbs_FACE || data._ignoreFaceIds.count( *id ))
3732         continue;
3733       F = TopoDS::Face( s );
3734       face2Norm[ totalNbFaces ].first = F;
3735       totalNbFaces++;
3736     }
3737   }
3738
3739   // find _normal
3740   bool fromVonF = false;
3741   if ( useGeometry )
3742   {
3743     fromVonF = ( eos.ShapeType() == TopAbs_VERTEX &&
3744                  eos.SWOLType()  == TopAbs_FACE  &&
3745                  totalNbFaces > 1 );
3746
3747     if ( onShrinkShape && !fromVonF ) // one of faces the node is on has no layers
3748     {
3749       if ( eos.SWOLType() == TopAbs_EDGE )
3750       {
3751         // inflate from VERTEX along EDGE
3752         edge._normal = getEdgeDir( TopoDS::Edge( eos._sWOL ), TopoDS::Vertex( eos._shape ),
3753                                    eos._hyp.Get1stLayerThickness(), &eos._isRegularSWOL );
3754       }
3755       else if ( eos.ShapeType() == TopAbs_VERTEX )
3756       {
3757         // inflate from VERTEX along FACE
3758         edge._normal = getFaceDir( TopoDS::Face( eos._sWOL ), TopoDS::Vertex( eos._shape ),
3759                                    node, helper, normOK/*, &edge._cosin*/);
3760       }
3761       else
3762       {
3763         // inflate from EDGE along FACE
3764         edge._normal = getFaceDir( TopoDS::Face( eos._sWOL ), TopoDS::Edge( eos._shape ),
3765                                    node, helper, normOK);
3766       }
3767     }
3768     else // layers are on all FACEs of SOLID the node is on (or fromVonF)
3769     {
3770       if ( fromVonF )
3771         face2Norm[ totalNbFaces++ ].first = TopoDS::Face( eos._sWOL );
3772
3773       int nbOkNorms = 0;
3774       for ( int iF = totalNbFaces - 1; iF >= 0; --iF )
3775       {
3776         F = face2Norm[ iF ].first;
3777         geomNorm = getFaceNormal( node, F, helper, normOK );
3778         if ( !normOK ) continue;
3779         nbOkNorms++;
3780
3781         if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3782           geomNorm.Reverse();
3783         face2Norm[ iF ].second = geomNorm.XYZ();
3784         edge._normal += geomNorm.XYZ();
3785       }
3786       if ( nbOkNorms == 0 )
3787         return error(SMESH_Comment("Can't get normal to node ") << node->GetID(), data._index);
3788
3789       if ( totalNbFaces >= 3 )
3790       {
3791         edge._normal = getNormalByOffset( &edge, face2Norm, totalNbFaces, fromVonF );
3792       }
3793
3794       if ( edge._normal.Modulus() < 1e-3 && nbOkNorms > 1 )
3795       {
3796         // opposite normals, re-get normals at shifted positions (IPAL 52426)
3797         edge._normal.SetCoord( 0,0,0 );
3798         for ( int iF = 0; iF < totalNbFaces - fromVonF; ++iF )
3799         {
3800           const TopoDS_Face& F = face2Norm[iF].first;
3801           geomNorm = getFaceNormal( node, F, helper, normOK, /*shiftInside=*/true );
3802           if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3803             geomNorm.Reverse();
3804           if ( normOK )
3805             face2Norm[ iF ].second = geomNorm.XYZ();
3806           edge._normal += face2Norm[ iF ].second;
3807         }
3808       }
3809     }
3810   }
3811   else // !useGeometry - get _normal using surrounding mesh faces
3812   {
3813     edge._normal = getWeigthedNormal( &edge );
3814
3815     // set<TGeomID> faceIds;
3816     //
3817     // SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
3818     // while ( fIt->more() )
3819     // {
3820     //   const SMDS_MeshElement* face = fIt->next();
3821     //   if ( eos.GetNormal( face, geomNorm ))
3822     //   {
3823     //     if ( onShrinkShape && !faceIds.insert( face->getshapeId() ).second )
3824     //       continue; // use only one mesh face on FACE
3825     //     edge._normal += geomNorm.XYZ();
3826     //     totalNbFaces++;
3827     //   }
3828     // }
3829   }
3830
3831   // compute _cosin
3832   //if ( eos._hyp.UseSurfaceNormal() )
3833   {
3834     switch ( eos.ShapeType() )
3835     {
3836     case TopAbs_FACE: {
3837       edge._cosin = 0;
3838       break;
3839     }
3840     case TopAbs_EDGE: {
3841       TopoDS_Edge E    = TopoDS::Edge( eos._shape );
3842       gp_Vec inFaceDir = getFaceDir( F, E, node, helper, normOK );
3843       double angle     = inFaceDir.Angle( edge._normal ); // [0,PI]
3844       edge._cosin      = Cos( angle );
3845       break;
3846     }
3847     case TopAbs_VERTEX: {
3848       TopoDS_Vertex V  = TopoDS::Vertex( eos._shape );
3849       gp_Vec inFaceDir = getFaceDir( face2Norm[0].first , V, node, helper, normOK );
3850       double angle     = inFaceDir.Angle( edge._normal ); // [0,PI]
3851       edge._cosin      = Cos( angle );
3852       if ( fromVonF )
3853         totalNbFaces--;
3854       if ( totalNbFaces > 1 || helper.IsSeamShape( node->getshapeId() ))
3855         for ( int iF = 1; iF < totalNbFaces; ++iF )
3856         {
3857           F = face2Norm[ iF ].first;
3858           inFaceDir = getFaceDir( F, V, node, helper, normOK=true );
3859           if ( normOK ) {
3860             if ( onShrinkShape )
3861             {
3862               gp_Vec faceNorm = getFaceNormal( node, F, helper, normOK );
3863               if ( !normOK ) continue;
3864               if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3865                 faceNorm.Reverse();
3866               angle = 0.5 * M_PI - faceNorm.Angle( edge._normal );
3867               if ( inFaceDir * edge._normal < 0 )
3868                 angle = M_PI - angle;
3869             }
3870             else
3871             {
3872               angle = inFaceDir.Angle( edge._normal );
3873             }
3874             double cosin = Cos( angle );
3875             if ( Abs( cosin ) > Abs( edge._cosin ))
3876               edge._cosin = cosin;
3877           }
3878         }
3879       break;
3880     }
3881     default:
3882       return error(SMESH_Comment("Invalid shape position of node ")<<node, data._index);
3883     }
3884   }
3885
3886   double normSize = edge._normal.SquareModulus();
3887   if ( normSize < numeric_limits<double>::min() )
3888     return error(SMESH_Comment("Bad normal at node ")<< node->GetID(), data._index );
3889
3890   edge._normal /= sqrt( normSize );
3891
3892   if ( edge.Is( _LayerEdge::MULTI_NORMAL ) && edge._nodes.size() == 2 )
3893   {
3894     getMeshDS()->RemoveFreeNode( edge._nodes.back(), 0, /*fromGroups=*/false );
3895     edge._nodes.resize( 1 );
3896     edge._normal.SetCoord( 0,0,0 );
3897     edge.SetMaxLen( 0 );
3898   }
3899
3900   // Set the rest data
3901   // --------------------
3902
3903   double realLenFactor = edge.SetCosin( edge._cosin ); // to update edge._lenFactor
3904   // if ( realLenFactor > 3 )
3905   // {
3906   //   edge._cosin = 1;
3907   //   if ( onShrinkShape )
3908   //   {
3909   //     edge.Set( _LayerEdge::RISKY_SWOL );
3910   //     edge._lenFactor = 2;
3911   //   }
3912   //   else
3913   //   {
3914   //     edge._lenFactor = 1;
3915   //   }
3916   // }
3917
3918   if ( onShrinkShape )
3919   {
3920     const SMDS_MeshNode* tgtNode = edge._nodes.back();
3921     if ( SMESHDS_SubMesh* sm = getMeshDS()->MeshElements( data._solid ))
3922       sm->RemoveNode( tgtNode );
3923
3924     // set initial position which is parameters on _sWOL in this case
3925     if ( eos.SWOLType() == TopAbs_EDGE )
3926     {
3927       double u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), node, 0, &normOK );
3928       edge._pos.push_back( gp_XYZ( u, 0, 0 ));
3929       if ( edge._nodes.size() > 1 )
3930         getMeshDS()->SetNodeOnEdge( tgtNode, TopoDS::Edge( eos._sWOL ), u );
3931     }
3932     else // eos.SWOLType() == TopAbs_FACE
3933     {
3934       gp_XY uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), node, 0, &normOK );
3935       edge._pos.push_back( gp_XYZ( uv.X(), uv.Y(), 0));
3936       if ( edge._nodes.size() > 1 )
3937         getMeshDS()->SetNodeOnFace( tgtNode, TopoDS::Face( eos._sWOL ), uv.X(), uv.Y() );
3938     }
3939
3940     //if ( edge._nodes.size() > 1 ) -- allow RISKY_SWOL on noShrink shape
3941     {
3942       // check if an angle between a FACE with layers and SWOL is sharp,
3943       // else the edge should not inflate
3944       F.Nullify();
3945       for ( int iF = 0; iF < totalNbFaces  &&  F.IsNull();  ++iF ) // find a FACE with VL
3946         if ( ! helper.IsSubShape( eos._sWOL, face2Norm[iF].first ))
3947           F = face2Norm[iF].first;
3948       if ( !F.IsNull())
3949       {
3950         geomNorm = getFaceNormal( node, F, helper, normOK );
3951         if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3952           geomNorm.Reverse(); // inside the SOLID
3953         if ( geomNorm * edge._normal < -0.001 )
3954         {
3955           if ( edge._nodes.size() > 1 )
3956           {
3957             getMeshDS()->RemoveFreeNode( tgtNode, 0, /*fromGroups=*/false );
3958             edge._nodes.resize( 1 );
3959           }
3960         }
3961         else if ( realLenFactor > 3 ) ///  -- moved to SetCosin()
3962           //else if ( edge._lenFactor > 3 )
3963         {
3964           edge._lenFactor = 2;
3965           edge.Set( _LayerEdge::RISKY_SWOL );
3966         }
3967       }
3968     }
3969   }
3970   else
3971   {
3972     edge._pos.push_back( SMESH_TNodeXYZ( node ));
3973
3974     if ( eos.ShapeType() == TopAbs_FACE )
3975     {
3976       double angle;
3977       for ( size_t i = 0; i < edge._simplices.size(); ++i )
3978       {
3979         edge._simplices[i].IsMinAngleOK( edge._pos.back(), angle );
3980         edge._minAngle = Max( edge._minAngle, angle ); // "angle" is actually cosine
3981       }
3982     }
3983   }
3984
3985   // Set neighbor nodes for a _LayerEdge based on EDGE
3986
3987   if ( eos.ShapeType() == TopAbs_EDGE /*||
3988        ( onShrinkShape && posType == SMDS_TOP_VERTEX && fabs( edge._cosin ) < 1e-10 )*/)
3989   {
3990     edge._2neibors = _Factory::NewNearEdges();
3991     // target nodes instead of source ones will be set later
3992   }
3993
3994   return true;
3995 }
3996
3997 //================================================================================
3998 /*!
3999  * \brief Return normal to a FACE at a node
4000  *  \param [in] n - node
4001  *  \param [in] face - FACE
4002  *  \param [in] helper - helper
4003  *  \param [out] isOK - true or false
4004  *  \param [in] shiftInside - to find normal at a position shifted inside the face
4005  *  \return gp_XYZ - normal
4006  */
4007 //================================================================================
4008
4009 gp_XYZ _ViscousBuilder::getFaceNormal(const SMDS_MeshNode* node,
4010                                       const TopoDS_Face&   face,
4011                                       SMESH_MesherHelper&  helper,
4012                                       bool&                isOK,
4013                                       bool                 shiftInside)
4014 {
4015   gp_XY uv;
4016   if ( shiftInside )
4017   {
4018     // get a shifted position
4019     gp_Pnt p = SMESH_TNodeXYZ( node );
4020     gp_XYZ shift( 0,0,0 );
4021     TopoDS_Shape S = helper.GetSubShapeByNode( node, helper.GetMeshDS() );
4022     switch ( S.ShapeType() ) {
4023     case TopAbs_VERTEX:
4024     {
4025       shift = getFaceDir( face, TopoDS::Vertex( S ), node, helper, isOK );
4026       break;
4027     }
4028     case TopAbs_EDGE:
4029     {
4030       shift = getFaceDir( face, TopoDS::Edge( S ), node, helper, isOK );
4031       break;
4032     }
4033     default:
4034       isOK = false;
4035     }
4036     if ( isOK )
4037       shift.Normalize();
4038     p.Translate( shift * 1e-5 );
4039
4040     TopLoc_Location loc;
4041     GeomAPI_ProjectPointOnSurf& projector = helper.GetProjector( face, loc, 1e-7 );
4042
4043     if ( !loc.IsIdentity() ) p.Transform( loc.Transformation().Inverted() );
4044     
4045     projector.Perform( p );
4046     if ( !projector.IsDone() || projector.NbPoints() < 1 )
4047     {
4048       isOK = false;
4049       return p.XYZ();
4050     }
4051     Standard_Real U,V;
4052     projector.LowerDistanceParameters(U,V);
4053     uv.SetCoord( U,V );
4054   }
4055   else
4056   {
4057     uv = helper.GetNodeUV( face, node, 0, &isOK );
4058   }
4059
4060   gp_Dir normal;
4061   isOK = false;
4062
4063   Handle(Geom_Surface) surface = BRep_Tool::Surface( face );
4064
4065   if ( !shiftInside &&
4066        helper.IsDegenShape( node->getshapeId() ) &&
4067        getFaceNormalAtSingularity( uv, face, helper, normal ))
4068   {
4069     isOK = true;
4070     return normal.XYZ();
4071   }
4072
4073   int pointKind = GeomLib::NormEstim( surface, uv, 1e-5, normal );
4074   enum { REGULAR = 0, QUASYSINGULAR, CONICAL, IMPOSSIBLE };
4075
4076   if ( pointKind == IMPOSSIBLE &&
4077        node->GetPosition()->GetDim() == 2 ) // node inside the FACE
4078   {
4079     // probably NormEstim() failed due to a too high tolerance
4080     pointKind = GeomLib::NormEstim( surface, uv, 1e-20, normal );
4081     isOK = ( pointKind < IMPOSSIBLE );
4082   }
4083   if ( pointKind < IMPOSSIBLE )
4084   {
4085     if ( pointKind != REGULAR &&
4086          !shiftInside &&
4087          node->GetPosition()->GetDim() < 2 ) // FACE boundary
4088     {
4089       gp_XYZ normShift = getFaceNormal( node, face, helper, isOK, /*shiftInside=*/true );
4090       if ( normShift * normal.XYZ() < 0. )
4091         normal = normShift;
4092     }
4093     isOK = true;
4094   }
4095
4096   if ( !isOK ) // hard singularity, to call with shiftInside=true ?
4097   {
4098     const TGeomID faceID = helper.GetMeshDS()->ShapeToIndex( face );
4099
4100     SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
4101     while ( fIt->more() )
4102     {
4103       const SMDS_MeshElement* f = fIt->next();
4104       if ( f->getshapeId() == faceID )
4105       {
4106         isOK = SMESH_MeshAlgos::FaceNormal( f, (gp_XYZ&) normal.XYZ(), /*normalized=*/true );
4107         if ( isOK )
4108         {
4109           TopoDS_Face ff = face;
4110           ff.Orientation( TopAbs_FORWARD );
4111           if ( helper.IsReversedSubMesh( ff ))
4112             normal.Reverse();
4113           break;
4114         }
4115       }
4116     }
4117   }
4118   return normal.XYZ();
4119 }
4120
4121 //================================================================================
4122 /*!
4123  * \brief Try to get normal at a singularity of a surface basing on it's nature
4124  */
4125 //================================================================================
4126
4127 bool _ViscousBuilder::getFaceNormalAtSingularity( const gp_XY&        uv,
4128                                                   const TopoDS_Face&  face,
4129                                                   SMESH_MesherHelper& /*helper*/,
4130                                                   gp_Dir&             normal )
4131 {
4132   BRepAdaptor_Surface surface( face );
4133   gp_Dir axis;
4134   if ( !getRovolutionAxis( surface, axis ))
4135     return false;
4136
4137   double f,l, d, du, dv;
4138   f = surface.FirstUParameter();
4139   l = surface.LastUParameter();
4140   d = ( uv.X() - f ) / ( l - f );
4141   du = ( d < 0.5 ? +1. : -1 ) * 1e-5 * ( l - f );
4142   f = surface.FirstVParameter();
4143   l = surface.LastVParameter();
4144   d = ( uv.Y() - f ) / ( l - f );
4145   dv = ( d < 0.5 ? +1. : -1 ) * 1e-5 * ( l - f );
4146
4147   gp_Dir refDir;
4148   gp_Pnt2d testUV = uv;
4149   enum { REGULAR = 0, QUASYSINGULAR, CONICAL, IMPOSSIBLE };
4150   double tol = 1e-5;
4151   Handle(Geom_Surface) geomsurf = surface.Surface().Surface();
4152   for ( int iLoop = 0; true ; ++iLoop )
4153   {
4154     testUV.SetCoord( testUV.X() + du, testUV.Y() + dv );
4155     if ( GeomLib::NormEstim( geomsurf, testUV, tol, refDir ) == REGULAR )
4156       break;
4157     if ( iLoop > 20 )
4158       return false;
4159     tol /= 10.;
4160   }
4161
4162   if ( axis * refDir < 0. )
4163     axis.Reverse();
4164
4165   normal = axis;
4166
4167   return true;
4168 }
4169
4170 //================================================================================
4171 /*!
4172  * \brief Return a normal at a node weighted with angles taken by faces
4173  */
4174 //================================================================================
4175
4176 gp_XYZ _ViscousBuilder::getWeigthedNormal( const _LayerEdge* edge )
4177 {
4178   const SMDS_MeshNode* n = edge->_nodes[0];
4179
4180   gp_XYZ resNorm(0,0,0);
4181   SMESH_TNodeXYZ p0( n ), pP, pN;
4182   for ( size_t i = 0; i < edge->_simplices.size(); ++i )
4183   {
4184     pP.Set( edge->_simplices[i]._nPrev );
4185     pN.Set( edge->_simplices[i]._nNext );
4186     gp_Vec v0P( p0, pP ), v0N( p0, pN ), vPN( pP, pN ), norm = v0P ^ v0N;
4187     double l0P = v0P.SquareMagnitude();
4188     double l0N = v0N.SquareMagnitude();
4189     double lPN = vPN.SquareMagnitude();
4190     if ( l0P < std::numeric_limits<double>::min() ||
4191          l0N < std::numeric_limits<double>::min() ||
4192          lPN < std::numeric_limits<double>::min() )
4193       continue;
4194     double lNorm = norm.SquareMagnitude();
4195     double  sin2 = lNorm / l0P / l0N;
4196     double angle = ACos(( v0P * v0N ) / Sqrt( l0P ) / Sqrt( l0N ));
4197
4198     double weight = sin2 * angle / lPN;
4199     resNorm += weight * norm.XYZ() / Sqrt( lNorm );
4200   }
4201
4202   return resNorm;
4203 }
4204
4205 //================================================================================
4206 /*!
4207  * \brief Return a normal at a node by getting a common point of offset planes
4208  *        defined by the FACE normals
4209  */
4210 //================================================================================
4211
4212 gp_XYZ _ViscousBuilder::getNormalByOffset( _LayerEdge*                      edge,
4213                                            std::pair< TopoDS_Face, gp_XYZ > f2Normal[],
4214                                            int                              nbFaces,
4215                                            bool                             lastNoOffset)
4216 {
4217   SMESH_TNodeXYZ p0 = edge->_nodes[0];
4218
4219   gp_XYZ resNorm(0,0,0);
4220   TopoDS_Shape V = SMESH_MesherHelper::GetSubShapeByNode( p0._node, getMeshDS() );
4221   if ( V.ShapeType() != TopAbs_VERTEX || nbFaces < 3 )
4222   {
4223     for ( int i = 0; i < nbFaces; ++i )
4224       resNorm += f2Normal[i].second;
4225     return resNorm;
4226   }
4227
4228   // prepare _OffsetPlane's
4229   vector< _OffsetPlane > pln( nbFaces );
4230   for ( int i = 0; i < nbFaces - lastNoOffset; ++i )
4231   {
4232     pln[i]._faceIndex = i;
4233     pln[i]._plane = gp_Pln( p0 + f2Normal[i].second, f2Normal[i].second );
4234   }
4235   if ( lastNoOffset )
4236   {
4237     pln[ nbFaces - 1 ]._faceIndex = nbFaces - 1;
4238     pln[ nbFaces - 1 ]._plane = gp_Pln( p0, f2Normal[ nbFaces - 1 ].second );
4239   }
4240
4241   // intersect neighboring OffsetPlane's
4242   PShapeIteratorPtr edgeIt = SMESH_MesherHelper::GetAncestors( V, *_mesh, TopAbs_EDGE );
4243   while ( const TopoDS_Shape* edge = edgeIt->next() )
4244   {
4245     int f1 = -1, f2 = -1;
4246     for ( int i = 0; i < nbFaces &&  f2 < 0;  ++i )
4247       if ( SMESH_MesherHelper::IsSubShape( *edge, f2Normal[i].first ))
4248         (( f1 < 0 ) ? f1 : f2 ) = i;
4249
4250     if ( f2 >= 0 )
4251       pln[ f1 ].ComputeIntersectionLine( pln[ f2 ], TopoDS::Edge( *edge ), TopoDS::Vertex( V ));
4252   }
4253
4254   // get a common point
4255   gp_XYZ commonPnt( 0, 0, 0 );
4256   int nbPoints = 0;
4257   bool isPointFound;
4258   for ( int i = 0; i < nbFaces; ++i )
4259   {
4260     commonPnt += pln[ i ].GetCommonPoint( isPointFound, TopoDS::Vertex( V ));
4261     nbPoints  += isPointFound;
4262   }
4263   gp_XYZ wgtNorm = getWeigthedNormal( edge );
4264   if ( nbPoints == 0 )
4265     return wgtNorm;
4266
4267   commonPnt /= nbPoints;
4268   resNorm = commonPnt - p0;
4269   if ( lastNoOffset )
4270     return resNorm;
4271
4272   // choose the best among resNorm and wgtNorm
4273   resNorm.Normalize();
4274   wgtNorm.Normalize();
4275   double resMinDot = std::numeric_limits<double>::max();
4276   double wgtMinDot = std::numeric_limits<double>::max();
4277   for ( int i = 0; i < nbFaces - lastNoOffset; ++i )
4278   {
4279     resMinDot = Min( resMinDot, resNorm * f2Normal[i].second );
4280     wgtMinDot = Min( wgtMinDot, wgtNorm * f2Normal[i].second );
4281   }
4282
4283   if ( Max( resMinDot, wgtMinDot ) < theMinSmoothCosin )
4284   {
4285     edge->Set( _LayerEdge::MULTI_NORMAL );
4286   }
4287
4288   return ( resMinDot > wgtMinDot ) ? resNorm : wgtNorm;
4289 }
4290
4291 //================================================================================
4292 /*!
4293  * \brief Compute line of intersection of 2 planes
4294  */
4295 //================================================================================
4296
4297 void _OffsetPlane::ComputeIntersectionLine( _OffsetPlane&        pln,
4298                                             const TopoDS_Edge&   E,
4299                                             const TopoDS_Vertex& V )
4300 {
4301   int iNext = bool( _faceIndexNext[0] >= 0 );
4302   _faceIndexNext[ iNext ] = pln._faceIndex;
4303
4304   gp_XYZ n1 = _plane.Axis().Direction().XYZ();
4305   gp_XYZ n2 = pln._plane.Axis().Direction().XYZ();
4306
4307   gp_XYZ lineDir = n1 ^ n2;
4308
4309   double x = Abs( lineDir.X() );
4310   double y = Abs( lineDir.Y() );
4311   double z = Abs( lineDir.Z() );
4312
4313   int cooMax; // max coordinate
4314   if (x > y) {
4315     if (x > z) cooMax = 1;
4316     else       cooMax = 3;
4317   }
4318   else {
4319     if (y > z) cooMax = 2;
4320     else       cooMax = 3;
4321   }
4322
4323   gp_Pnt linePos;
4324   if ( Abs( lineDir.Coord( cooMax )) < 0.05 )
4325   {
4326     // parallel planes - intersection is an offset of the common EDGE
4327     gp_Pnt p = BRep_Tool::Pnt( V );
4328     linePos  = 0.5 * (( p.XYZ() + n1 ) + ( p.XYZ() + n2 ));
4329     lineDir  = getEdgeDir( E, V, 0.1 * SMESH_Algo::EdgeLength( E ));
4330   }
4331   else
4332   {
4333     // the constants in the 2 plane equations
4334     double d1 = - ( _plane.Axis().Direction().XYZ()     * _plane.Location().XYZ() );
4335     double d2 = - ( pln._plane.Axis().Direction().XYZ() * pln._plane.Location().XYZ() );
4336
4337     switch ( cooMax ) {
4338     case 1:
4339       linePos.SetX(  0 );
4340       linePos.SetY(( d2*n1.Z() - d1*n2.Z()) / lineDir.X() );
4341       linePos.SetZ(( d1*n2.Y() - d2*n1.Y()) / lineDir.X() );
4342       break;
4343     case 2:
4344       linePos.SetX(( d1*n2.Z() - d2*n1.Z()) / lineDir.Y() );
4345       linePos.SetY(  0 );
4346       linePos.SetZ(( d2*n1.X() - d1*n2.X()) / lineDir.Y() );
4347       break;
4348     case 3:
4349       linePos.SetX(( d2*n1.Y() - d1*n2.Y()) / lineDir.Z() );
4350       linePos.SetY(( d1*n2.X() - d2*n1.X()) / lineDir.Z() );
4351       linePos.SetZ(  0 );
4352     }
4353   }
4354   gp_Lin& line = _lines[ iNext ];
4355   line.SetDirection( lineDir );
4356   line.SetLocation ( linePos );
4357
4358   _isLineOK[ iNext ] = true;
4359
4360
4361   iNext = bool( pln._faceIndexNext[0] >= 0 );
4362   pln._lines        [ iNext ] = line;
4363   pln._faceIndexNext[ iNext ] = this->_faceIndex;
4364   pln._isLineOK     [ iNext ] = true;
4365 }
4366
4367 //================================================================================
4368 /*!
4369  * \brief Computes intersection point of two _lines
4370  */
4371 //================================================================================
4372
4373 gp_XYZ _OffsetPlane::GetCommonPoint(bool&                 isFound,
4374                                     const TopoDS_Vertex & V) const
4375 {
4376   gp_XYZ p( 0,0,0 );
4377   isFound = false;
4378
4379   if ( NbLines() == 2 )
4380   {
4381     gp_Vec lPerp0 = _lines[0].Direction().XYZ() ^ _plane.Axis().Direction().XYZ();
4382     double  dot01 = lPerp0 * _lines[1].Direction().XYZ();
4383     if ( Abs( dot01 ) > 0.05 )
4384     {
4385       gp_Vec l0l1 = _lines[1].Location().XYZ() - _lines[0].Location().XYZ();
4386       double   u1 = - ( lPerp0 * l0l1 ) / dot01;
4387       p = ( _lines[1].Location().XYZ() + _lines[1].Direction().XYZ() * u1 );
4388       isFound = true;
4389     }
4390     else
4391     {
4392       gp_Pnt  pV ( BRep_Tool::Pnt( V ));
4393       gp_Vec  lv0( _lines[0].Location(), pV    ),  lv1(_lines[1].Location(), pV     );
4394       double dot0( lv0 * _lines[0].Direction() ), dot1( lv1 * _lines[1].Direction() );
4395       p += 0.5 * ( _lines[0].Location().XYZ() + _lines[0].Direction().XYZ() * dot0 );
4396       p += 0.5 * ( _lines[1].Location().XYZ() + _lines[1].Direction().XYZ() * dot1 );
4397       isFound = true;
4398     }
4399   }
4400
4401   return p;
4402 }
4403
4404 //================================================================================
4405 /*!
4406  * \brief Find 2 neighbor nodes of a node on EDGE
4407  */
4408 //================================================================================
4409
4410 bool _ViscousBuilder::findNeiborsOnEdge(const _LayerEdge*     edge,
4411                                         const SMDS_MeshNode*& n1,
4412                                         const SMDS_MeshNode*& n2,
4413                                         _EdgesOnShape&        eos,
4414                                         _SolidData&           data)
4415 {
4416   const SMDS_MeshNode* node = edge->_nodes[0];
4417   const int        shapeInd = eos._shapeID;
4418   SMESHDS_SubMesh*   edgeSM = 0;
4419   if ( eos.ShapeType() == TopAbs_EDGE )
4420   {
4421     edgeSM = eos._subMesh->GetSubMeshDS();
4422     if ( !edgeSM || edgeSM->NbElements() == 0 )
4423       return error(SMESH_Comment("Not meshed EDGE ") << shapeInd, data._index);
4424   }
4425   int iN = 0;
4426   n2 = 0;
4427   SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Edge);
4428   while ( eIt->more() && !n2 )
4429   {
4430     const SMDS_MeshElement* e = eIt->next();
4431     const SMDS_MeshNode*   nNeibor = e->GetNode( 0 );
4432     if ( nNeibor == node ) nNeibor = e->GetNode( 1 );
4433     if ( edgeSM )
4434     {
4435       if (!edgeSM->Contains(e)) continue;
4436     }
4437     else
4438     {
4439       TopoDS_Shape s = SMESH_MesherHelper::GetSubShapeByNode( nNeibor, getMeshDS() );
4440       if ( !SMESH_MesherHelper::IsSubShape( s, eos._sWOL )) continue;
4441     }
4442     ( iN++ ? n2 : n1 ) = nNeibor;
4443   }
4444   if ( !n2 )
4445     return error(SMESH_Comment("Wrongly meshed EDGE ") << shapeInd, data._index);
4446   return true;
4447 }
4448
4449 //================================================================================
4450 /*!
4451  * \brief Create _Curvature
4452  */
4453 //================================================================================
4454
4455 _Curvature* _Curvature::New( double avgNormProj, double avgDist )
4456 {
4457   // double   _r; // radius
4458   // double   _k; // factor to correct node smoothed position
4459   // double   _h2lenRatio; // avgNormProj / (2*avgDist)
4460   // gp_Pnt2d _uv; // UV used in putOnOffsetSurface()
4461
4462   _Curvature* c = 0;
4463   if ( fabs( avgNormProj / avgDist ) > 1./200 )
4464   {
4465     c = _Factory::NewCurvature();
4466     c->_r = avgDist * avgDist / avgNormProj;
4467     c->_k = avgDist * avgDist / c->_r / c->_r;
4468     //c->_k = avgNormProj / c->_r;
4469     c->_k *= ( c->_r < 0 ? 1/1.1 : 1.1 ); // not to be too restrictive
4470     c->_h2lenRatio = avgNormProj / ( avgDist + avgDist );
4471
4472     c->_uv.SetCoord( 0., 0. );
4473   }
4474   return c;
4475 }
4476
4477 //================================================================================
4478 /*!
4479  * \brief Set _curvature and _2neibors->_plnNorm by 2 neighbor nodes residing the same EDGE
4480  */
4481 //================================================================================
4482
4483 void _LayerEdge::SetDataByNeighbors( const SMDS_MeshNode* n1,
4484                                      const SMDS_MeshNode* n2,
4485                                      const _EdgesOnShape& eos,
4486                                      SMESH_MesherHelper&  helper)
4487 {
4488   if ( eos.ShapeType() != TopAbs_EDGE )
4489     return;
4490   if ( _curvature && Is( SMOOTHED_C1 ))
4491     return;
4492
4493   gp_XYZ  pos = SMESH_TNodeXYZ( _nodes[0] );
4494   gp_XYZ vec1 = pos - SMESH_TNodeXYZ( n1 );
4495   gp_XYZ vec2 = pos - SMESH_TNodeXYZ( n2 );
4496
4497   // Set _curvature
4498
4499   double      sumLen = vec1.Modulus() + vec2.Modulus();
4500   _2neibors->_wgt[0] = 1 - vec1.Modulus() / sumLen;
4501   _2neibors->_wgt[1] = 1 - vec2.Modulus() / sumLen;
4502   double avgNormProj = 0.5 * ( _normal * vec1 + _normal * vec2 );
4503   double      avgLen = 0.5 * ( vec1.Modulus() + vec2.Modulus() );
4504   _curvature = _Curvature::New( avgNormProj, avgLen );
4505   // if ( _curvature )
4506   //   debugMsg( _nodes[0]->GetID()
4507   //             << " CURV r,k: " << _curvature->_r<<","<<_curvature->_k
4508   //             << " proj = "<<avgNormProj<< " len = " << avgLen << "| lenDelta(0) = "
4509   //             << _curvature->lenDelta(0) );
4510
4511   // Set _plnNorm
4512
4513   if ( eos._sWOL.IsNull() )
4514   {
4515     TopoDS_Edge  E = TopoDS::Edge( eos._shape );
4516     // if ( SMESH_Algo::isDegenerated( E ))
4517     //   return;
4518     gp_XYZ dirE    = getEdgeDir( E, _nodes[0], helper );
4519     gp_XYZ plnNorm = dirE ^ _normal;
4520     double proj0   = plnNorm * vec1;
4521     double proj1   = plnNorm * vec2;
4522     if ( fabs( proj0 ) > 1e-10 || fabs( proj1 ) > 1e-10 )
4523     {
4524       if ( _2neibors->_plnNorm ) delete _2neibors->_plnNorm;
4525       _2neibors->_plnNorm = new gp_XYZ( plnNorm.Normalized() );
4526     }
4527   }
4528 }
4529
4530 //================================================================================
4531 /*!
4532  * \brief Copy data from a _LayerEdge of other SOLID and based on the same node;
4533  * this and the other _LayerEdge are inflated along a FACE or an EDGE
4534  */
4535 //================================================================================
4536
4537 gp_XYZ _LayerEdge::Copy( _LayerEdge&         other,
4538                          _EdgesOnShape&      eos,
4539                          SMESH_MesherHelper& helper )
4540 {
4541   _nodes     = other._nodes;
4542   _normal    = other._normal;
4543   _len       = 0;
4544   _lenFactor = other._lenFactor;
4545   _cosin     = other._cosin;
4546   _2neibors  = other._2neibors;
4547   _curvature = other._curvature;
4548   _2neibors  = other._2neibors;
4549   _maxLen    = Precision::Infinite();//other._maxLen;
4550   _flags     = 0;
4551   _smooFunction = 0;
4552
4553   gp_XYZ lastPos( 0,0,0 );
4554   if ( eos.SWOLType() == TopAbs_EDGE )
4555   {
4556     double u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), _nodes[0] );
4557     _pos.push_back( gp_XYZ( u, 0, 0));
4558
4559     u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), _nodes.back() );
4560     lastPos.SetX( u );
4561   }
4562   else // TopAbs_FACE
4563   {
4564     gp_XY uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), _nodes[0]);
4565     _pos.push_back( gp_XYZ( uv.X(), uv.Y(), 0));
4566
4567     uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), _nodes.back() );
4568     lastPos.SetX( uv.X() );
4569     lastPos.SetY( uv.Y() );
4570   }
4571   return lastPos;
4572 }
4573
4574 //================================================================================
4575 /*!
4576  * \brief Set _cosin and _lenFactor
4577  */
4578 //================================================================================
4579
4580 double _LayerEdge::SetCosin( double cosin )
4581 {
4582   _cosin = cosin;
4583   cosin = Abs( _cosin );
4584   //_lenFactor = ( cosin < 1.-1e-12 ) ?  1./sqrt(1-cosin*cosin ) : 1.0;
4585   double realLenFactor;
4586   if ( cosin < 1.-1e-12 )
4587   {
4588     _lenFactor = realLenFactor = 1./sqrt(1-cosin*cosin );
4589   }
4590   else
4591   {
4592     _lenFactor = 1;
4593     realLenFactor = Precision::Infinite();
4594   }
4595
4596   return realLenFactor;
4597 }
4598
4599 //================================================================================
4600 /*!
4601  * \brief Check if another _LayerEdge is a neighbor on EDGE
4602  */
4603 //================================================================================
4604
4605 bool _LayerEdge::IsNeiborOnEdge( const _LayerEdge* edge ) const
4606 {
4607   return (( this->_2neibors && this->_2neibors->include( edge )) ||
4608           ( edge->_2neibors && edge->_2neibors->include( this )));
4609 }
4610
4611 //================================================================================
4612 /*!
4613  * \brief Fills a vector<_Simplex > 
4614  */
4615 //================================================================================
4616
4617 void _Simplex::GetSimplices( const SMDS_MeshNode* node,
4618                              vector<_Simplex>&    simplices,
4619                              const set<TGeomID>&  ingnoreShapes,
4620                              const _SolidData*    dataToCheckOri,
4621                              const bool           toSort)
4622 {
4623   simplices.clear();
4624   SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
4625   while ( fIt->more() )
4626   {
4627     const SMDS_MeshElement* f = fIt->next();
4628     const TGeomID    shapeInd = f->getshapeId();
4629     if ( ingnoreShapes.count( shapeInd )) continue;
4630     const int nbNodes = f->NbCornerNodes();
4631     const int  srcInd = f->GetNodeIndex( node );
4632     const SMDS_MeshNode* nPrev = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd-1, nbNodes ));
4633     const SMDS_MeshNode* nNext = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd+1, nbNodes ));
4634     const SMDS_MeshNode* nOpp  = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd+2, nbNodes ));
4635     if ( dataToCheckOri && dataToCheckOri->_reversedFaceIds.count( shapeInd ))
4636       std::swap( nPrev, nNext );
4637     simplices.push_back( _Simplex( nPrev, nNext, ( nbNodes == 3 ? 0 : nOpp )));
4638   }
4639
4640   if ( toSort )
4641     SortSimplices( simplices );
4642 }
4643
4644 //================================================================================
4645 /*!
4646  * \brief Set neighbor simplices side by side
4647  */
4648 //================================================================================
4649
4650 void _Simplex::SortSimplices(vector<_Simplex>& simplices)
4651 {
4652   vector<_Simplex> sortedSimplices( simplices.size() );
4653   sortedSimplices[0] = simplices[0];
4654   size_t nbFound = 0;
4655   for ( size_t i = 1; i < simplices.size(); ++i )
4656   {
4657     for ( size_t j = 1; j < simplices.size(); ++j )
4658       if ( sortedSimplices[i-1]._nNext == simplices[j]._nPrev )
4659       {
4660         sortedSimplices[i] = simplices[j];
4661         nbFound++;
4662         break;
4663       }
4664   }
4665   if ( nbFound == simplices.size() - 1 )
4666     simplices.swap( sortedSimplices );
4667 }
4668
4669 //================================================================================
4670 /*!
4671  * \brief DEBUG. Create groups containing temporary data of _LayerEdge's
4672  */
4673 //================================================================================
4674
4675 void _ViscousBuilder::makeGroupOfLE()
4676 {
4677   if (!SALOME::VerbosityActivated())
4678     return;
4679
4680   for ( size_t i = 0 ; i < _sdVec.size(); ++i )
4681   {
4682     if ( _sdVec[i]._n2eMap.empty() ) continue;
4683
4684     dumpFunction( SMESH_Comment("make_LayerEdge_") << i );
4685     TNode2Edge::iterator n2e;
4686     for ( n2e = _sdVec[i]._n2eMap.begin(); n2e != _sdVec[i]._n2eMap.end(); ++n2e )
4687     {
4688       _LayerEdge* le = n2e->second;
4689       // for ( size_t iN = 1; iN < le->_nodes.size(); ++iN )
4690       //   dumpCmd(SMESH_Comment("mesh.AddEdge([ ") <<le->_nodes[iN-1]->GetID()
4691       //           << ", " << le->_nodes[iN]->GetID() <<"])");
4692       if ( le ) {
4693         dumpCmd(SMESH_Comment("mesh.AddEdge([ ") <<le->_nodes[0]->GetID()
4694                 << ", " << le->_nodes.back()->GetID() <<"]) # " << le->_flags );
4695       }
4696     }
4697     dumpFunctionEnd();
4698
4699     dumpFunction( SMESH_Comment("makeNormals") << i );
4700     for ( n2e = _sdVec[i]._n2eMap.begin(); n2e != _sdVec[i]._n2eMap.end(); ++n2e )
4701     {
4702       _LayerEdge* edge = n2e->second;
4703       SMESH_TNodeXYZ nXYZ( edge->_nodes[0] );
4704       nXYZ += edge->_normal * _sdVec[i]._stepSize;
4705       dumpCmd(SMESH_Comment("mesh.AddEdge([ ") << edge->_nodes[0]->GetID()
4706               << ", mesh.AddNode( "<< nXYZ.X()<<","<< nXYZ.Y()<<","<< nXYZ.Z()<<")])");
4707     }
4708     dumpFunctionEnd();
4709
4710     dumpFunction( SMESH_Comment("makeTmpFaces_") << i );
4711     dumpCmd( "faceId1 = mesh.NbElements()" );
4712     TopExp_Explorer fExp( _sdVec[i]._solid, TopAbs_FACE );
4713     for ( ; fExp.More(); fExp.Next() )
4714     {
4715       if ( const SMESHDS_SubMesh* sm = _sdVec[i]._proxyMesh->GetProxySubMesh( fExp.Current() ))
4716       {
4717         if ( sm->NbElements() == 0 ) continue;
4718         SMDS_ElemIteratorPtr fIt = sm->GetElements();
4719         while ( fIt->more())
4720         {
4721           const SMDS_MeshElement* e = fIt->next();
4722           SMESH_Comment cmd("mesh.AddFace([");
4723           for ( int j = 0; j < e->NbCornerNodes(); ++j )
4724             cmd << e->GetNode(j)->GetID() << (j+1 < e->NbCornerNodes() ? ",": "])");
4725           dumpCmd( cmd );
4726         }
4727       }
4728     }
4729     dumpCmd( "faceId2 = mesh.NbElements()" );
4730     dumpCmd( SMESH_Comment( "mesh.MakeGroup( 'tmpFaces_" ) << i << "',"
4731              << "SMESH.FACE, SMESH.FT_RangeOfIds,'=',"
4732              << "'%s-%s' % (faceId1+1, faceId2))");
4733     dumpFunctionEnd();
4734   }
4735 }
4736
4737 //================================================================================
4738 /*!
4739  * \brief Find maximal _LayerEdge length (layer thickness) limited by geometry
4740  */
4741 //================================================================================
4742
4743 void _ViscousBuilder::computeGeomSize( _SolidData& data )
4744 {
4745   data._geomSize = Precision::Infinite();
4746   double intersecDist;
4747   const SMDS_MeshElement* face;
4748   SMESH_MesherHelper helper( *_mesh );
4749
4750   SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
4751     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
4752                                            data._proxyMesh->GetFaces( data._solid )));
4753
4754   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4755   {
4756     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4757     if ( eos._edges.empty() )
4758       continue;
4759     // get neighbor faces, intersection with which should not be considered since
4760     // collisions are avoided by means of smoothing
4761     set< TGeomID > neighborFaces;
4762     if ( eos._hyp.ToSmooth() )
4763     {
4764       SMESH_subMeshIteratorPtr subIt =
4765         eos._subMesh->getDependsOnIterator(/*includeSelf=*/eos.ShapeType() != TopAbs_FACE );
4766       while ( subIt->more() )
4767       {
4768         SMESH_subMesh* sm = subIt->next();
4769         PShapeIteratorPtr fIt = helper.GetAncestors( sm->GetSubShape(), *_mesh, TopAbs_FACE );
4770         while ( const TopoDS_Shape* face = fIt->next() )
4771           neighborFaces.insert( getMeshDS()->ShapeToIndex( *face ));
4772       }
4773     }
4774     // find intersections
4775     double thinkness = eos._hyp.GetTotalThickness();
4776     for ( size_t i = 0; i < eos._edges.size(); ++i )
4777     {
4778       if ( eos._edges[i]->_nodes.size() < 2 ) continue;
4779       eos._edges[i]->SetMaxLen( thinkness );
4780       eos._edges[i]->FindIntersection( *searcher, intersecDist, data._epsilon, eos, &face );
4781       if ( intersecDist > 0 && face )
4782       {
4783         data._geomSize = Min( data._geomSize, intersecDist );
4784         if ( !neighborFaces.count( face->getshapeId() ))
4785           eos[i]->SetMaxLen( Min( thinkness, intersecDist / ( face->GetID() < 0 ? 3. : 2. )));
4786       }
4787     }
4788   }
4789
4790   data._maxThickness = 0;
4791   data._minThickness = 1e100;
4792   list< const StdMeshers_ViscousLayers* >::iterator hyp = data._hyps.begin();
4793   for ( ; hyp != data._hyps.end(); ++hyp )
4794   {
4795     data._maxThickness = Max( data._maxThickness, (*hyp)->GetTotalThickness() );
4796     data._minThickness = Min( data._minThickness, (*hyp)->GetTotalThickness() );
4797   }
4798
4799   // Limit inflation step size by geometry size found by intersecting
4800   // normals of _LayerEdge's with mesh faces
4801   if ( data._stepSize > 0.3 * data._geomSize )
4802     limitStepSize( data, 0.3 * data._geomSize );
4803
4804   if ( data._stepSize > data._minThickness )
4805     limitStepSize( data, data._minThickness );
4806
4807
4808   // -------------------------------------------------------------------------
4809   // Detect _LayerEdge which can't intersect with opposite or neighbor layer,
4810   // so no need in detecting intersection at each inflation step
4811   // -------------------------------------------------------------------------
4812
4813   int nbSteps = data._maxThickness / data._stepSize;
4814   if ( nbSteps < 3 || nbSteps * data._n2eMap.size() < 100000 )
4815     return;
4816
4817   vector< const SMDS_MeshElement* > closeFaces;
4818   int nbDetected = 0;
4819
4820   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4821   {
4822     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4823     if ( eos._edges.empty() || eos.ShapeType() != TopAbs_FACE )
4824       continue;
4825
4826     for ( size_t i = 0; i < eos.size(); ++i )
4827     {
4828       SMESH_NodeXYZ p( eos[i]->_nodes[0] );
4829       double radius = data._maxThickness + 2 * eos[i]->_maxLen;
4830       closeFaces.clear();
4831       searcher->GetElementsInSphere( p, radius, SMDSAbs_Face, closeFaces );
4832
4833       bool toIgnore = true;
4834       for ( size_t iF = 0; iF < closeFaces.size()  && toIgnore; ++iF )
4835         if ( !( toIgnore = ( closeFaces[ iF ]->getshapeId() == eos._shapeID ||
4836                              data._ignoreFaceIds.count( closeFaces[ iF ]->getshapeId() ))))
4837         {
4838           // check if a _LayerEdge will inflate in a direction opposite to a direction
4839           // toward a close face
4840           bool allBehind = true;
4841           for ( int iN = 0; iN < closeFaces[ iF ]->NbCornerNodes()  && allBehind; ++iN )
4842           {
4843             SMESH_NodeXYZ pi( closeFaces[ iF ]->GetNode( iN ));
4844             allBehind = (( pi - p ) * eos[i]->_normal < 0.1 * data._stepSize );
4845           }
4846           toIgnore = allBehind;
4847         }
4848
4849
4850       if ( toIgnore ) // no need to detect intersection
4851       {
4852         eos[i]->Set( _LayerEdge::INTERSECTED );
4853         ++nbDetected;
4854       }
4855     }
4856   }
4857
4858   debugMsg( "Nb LE to intersect " << data._n2eMap.size()-nbDetected << ", ignore " << nbDetected );
4859
4860   return;
4861 }
4862
4863 //================================================================================
4864 /*!
4865  * \brief Increase length of _LayerEdge's to reach the required thickness of layers
4866  */
4867 //================================================================================
4868
4869 bool _ViscousBuilder::inflate(_SolidData& data)
4870 {
4871   SMESH_MesherHelper helper( *_mesh );
4872
4873   const double tgtThick = data._maxThickness;
4874
4875   if ( data._stepSize < 1. )
4876     data._epsilon = data._stepSize * 1e-7;
4877
4878   debugMsg( "-- geomSize = " << data._geomSize << ", stepSize = " << data._stepSize );
4879   _pyDump->Pause();
4880
4881   findCollisionEdges( data, helper );
4882
4883   limitMaxLenByCurvature( data, helper );
4884
4885   _pyDump->Resume();
4886
4887   // limit length of _LayerEdge's around MULTI_NORMAL _LayerEdge's
4888   for ( size_t i = 0; i < data._edgesOnShape.size(); ++i )
4889     if ( data._edgesOnShape[i].ShapeType() == TopAbs_VERTEX &&
4890          data._edgesOnShape[i]._edges.size() > 0 &&
4891          data._edgesOnShape[i]._edges[0]->Is( _LayerEdge::MULTI_NORMAL ))
4892     {
4893       data._edgesOnShape[i]._edges[0]->Unset( _LayerEdge::BLOCKED );
4894       data._edgesOnShape[i]._edges[0]->Block( data );
4895     }
4896
4897   const double safeFactor = ( 2*data._maxThickness < data._geomSize ) ? 1 : theThickToIntersection;
4898
4899   double avgThick = 0, curThick = 0, distToIntersection = Precision::Infinite();
4900   int nbSteps = 0, nbRepeats = 0;
4901   while ( avgThick < 0.99 )
4902   {
4903     // new target length
4904     double prevThick = curThick;
4905     curThick += data._stepSize;
4906     if ( curThick > tgtThick )
4907     {
4908       curThick = tgtThick + tgtThick*( 1.-avgThick ) * nbRepeats;
4909       nbRepeats++;
4910     }
4911
4912     double stepSize = curThick - prevThick;
4913     updateNormalsOfSmoothed( data, helper, nbSteps, stepSize ); // to ease smoothing
4914
4915     // Elongate _LayerEdge's
4916     dumpFunction(SMESH_Comment("inflate")<<data._index<<"_step"<<nbSteps); // debug
4917     for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4918     {
4919       _EdgesOnShape& eos = data._edgesOnShape[iS];
4920       if ( eos._edges.empty() ) continue;
4921
4922       const double shapeCurThick = Min( curThick, eos._hyp.GetTotalThickness() );
4923       for ( size_t i = 0; i < eos._edges.size(); ++i )
4924       {
4925         eos._edges[i]->SetNewLength( shapeCurThick, eos, helper );
4926       }
4927     }
4928     dumpFunctionEnd();
4929
4930     if ( !updateNormals( data, helper, nbSteps, stepSize )) // to avoid collisions
4931       return false;
4932
4933     // Improve and check quality
4934     if ( !smoothAndCheck( data, nbSteps, distToIntersection ))
4935     {
4936       if ( nbSteps > 0 )
4937       {
4938 #ifdef __NOT_INVALIDATE_BAD_SMOOTH
4939         debugMsg("NOT INVALIDATED STEP!");
4940         return error("Smoothing failed", data._index);
4941 #endif
4942         dumpFunction(SMESH_Comment("invalidate")<<data._index<<"_step"<<nbSteps); // debug
4943         for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4944         {
4945           _EdgesOnShape& eos = data._edgesOnShape[iS];
4946           for ( size_t i = 0; i < eos._edges.size(); ++i )
4947             eos._edges[i]->InvalidateStep( nbSteps+1, eos );
4948         }
4949         dumpFunctionEnd();
4950       }
4951       break; // no more inflating possible
4952     }
4953     nbSteps++;
4954
4955     // Evaluate achieved thickness
4956     avgThick = 0;
4957     int nbActiveEdges = 0;
4958     for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4959     {
4960       _EdgesOnShape& eos = data._edgesOnShape[iS];
4961       if ( eos._edges.empty() ) continue;
4962
4963       const double shapeTgtThick = eos._hyp.GetTotalThickness();
4964       for ( size_t i = 0; i < eos._edges.size(); ++i )
4965       {
4966         if ( eos._edges[i]->_nodes.size() > 1 )
4967           avgThick    += Min( 1., eos._edges[i]->_len / shapeTgtThick );
4968         else
4969           avgThick    += 1;
4970         nbActiveEdges += ( ! eos._edges[i]->Is( _LayerEdge::BLOCKED ));
4971       }
4972     }
4973     avgThick /= data._n2eMap.size();
4974     debugMsg( "-- Thickness " << curThick << " ("<< avgThick*100 << "%) reached" );
4975
4976 #ifdef BLOCK_INFLATION
4977     if ( nbActiveEdges == 0 )
4978     {
4979       debugMsg( "-- Stop inflation since all _LayerEdge's BLOCKED " );
4980       break;
4981     }
4982 #else
4983     if ( distToIntersection < tgtThick * avgThick * safeFactor && avgThick < 0.9 )
4984     {
4985       debugMsg( "-- Stop inflation since "
4986                 << " distToIntersection( "<<distToIntersection<<" ) < avgThick( "
4987                 << tgtThick * avgThick << " ) * " << safeFactor );
4988       break;
4989     }
4990 #endif
4991
4992     // new step size
4993     limitStepSize( data, 0.25 * distToIntersection );
4994     if ( data._stepSizeNodes[0] )
4995       data._stepSize = data._stepSizeCoeff *
4996         SMESH_TNodeXYZ(data._stepSizeNodes[0]).Distance(data._stepSizeNodes[1]);
4997
4998   } // while ( avgThick < 0.99 )
4999
5000   if ( nbSteps == 0 )
5001     return error("failed at the very first inflation step", data._index);
5002
5003   if ( avgThick < 0.99 )
5004   {
5005     if ( !data._proxyMesh->_warning || data._proxyMesh->_warning->IsOK() )
5006     {
5007       data._proxyMesh->_warning.reset
5008         ( new SMESH_ComputeError (COMPERR_WARNING,
5009                                   SMESH_Comment("Thickness ") << tgtThick <<
5010                                   " of viscous layers not reached,"
5011                                   " average reached thickness is " << avgThick*tgtThick));
5012     }
5013   }
5014
5015   // Restore position of src nodes moved by inflation on _noShrinkShapes
5016   dumpFunction(SMESH_Comment("restoNoShrink_So")<<data._index); // debug
5017   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
5018   {
5019     _EdgesOnShape& eos = data._edgesOnShape[iS];
5020     if ( !eos._edges.empty() && eos._edges[0]->_nodes.size() == 1 )
5021       for ( size_t i = 0; i < eos._edges.size(); ++i )
5022       {
5023         restoreNoShrink( *eos._edges[ i ] );
5024       }
5025   }
5026   dumpFunctionEnd();
5027
5028   return safeFactor > 0; // == true (avoid warning: unused variable 'safeFactor')
5029 }
5030
5031 //================================================================================
5032 /*!
5033  * \brief Improve quality of layer inner surface and check intersection
5034  */
5035 //================================================================================
5036
5037 bool _ViscousBuilder::smoothAndCheck(_SolidData& data,
5038                                      const int   infStep,
5039                                      double &    distToIntersection)
5040 {
5041   if ( data._nbShapesToSmooth == 0 )
5042     return true; // no shapes needing smoothing
5043
5044   bool moved, improved;
5045   double vol;
5046   vector< _LayerEdge* >    movedEdges, badEdges;
5047   vector< _EdgesOnShape* > eosC1; // C1 continues shapes
5048   vector< bool >           isConcaveFace;
5049
5050   SMESH_MesherHelper helper(*_mesh);
5051   Handle(ShapeAnalysis_Surface) surface;
5052   TopoDS_Face F;
5053
5054   for ( int isFace = 0; isFace < 2; ++isFace ) // smooth on [ EDGEs, FACEs ]
5055   {
5056     const TopAbs_ShapeEnum shapeType = isFace ? TopAbs_FACE : TopAbs_EDGE;
5057
5058     for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
5059     {
5060       _EdgesOnShape& eos = data._edgesOnShape[ iS ];
5061       if ( !eos._toSmooth ||
5062            eos.ShapeType() != shapeType ||
5063            eos._edges.empty() )
5064         continue;
5065
5066       // already smoothed?
5067       // bool toSmooth = ( eos._edges[ 0 ]->NbSteps() >= infStep+1 );
5068       // if ( !toSmooth ) continue;
5069
5070       if ( !eos._hyp.ToSmooth() )
5071       {
5072         // smooth disabled by the user; check validy only
5073         if ( !isFace ) continue;
5074         badEdges.clear();
5075         for ( size_t i = 0; i < eos._edges.size(); ++i )
5076         {
5077           _LayerEdge* edge = eos._edges[i];
5078           for ( size_t iF = 0; iF < edge->_simplices.size(); ++iF )
5079             if ( !edge->_simplices[iF].IsForward( edge->_nodes[0], edge->_pos.back(), vol ))
5080             {
5081               // debugMsg( "-- Stop inflation. Bad simplex ("
5082               //           << " "<< edge->_nodes[0]->GetID()
5083               //           << " "<< edge->_nodes.back()->GetID()
5084               //           << " "<< edge->_simplices[iF]._nPrev->GetID()
5085               //           << " "<< edge->_simplices[iF]._nNext->GetID() << " ) ");
5086               // return false;
5087               badEdges.push_back( edge );
5088             }
5089         }
5090         if ( !badEdges.empty() )
5091         {
5092           eosC1.resize(1);
5093           eosC1[0] = &eos;
5094           int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5095           if ( nbBad > 0 )
5096             return false;
5097         }
5098         continue; // goto the next EDGE or FACE
5099       }
5100
5101       // prepare data
5102       if ( eos.SWOLType() == TopAbs_FACE )
5103       {
5104         if ( !F.IsSame( eos._sWOL )) {
5105           F = TopoDS::Face( eos._sWOL );
5106           helper.SetSubShape( F );
5107           surface = helper.GetSurface( F );
5108         }
5109       }
5110       else
5111       {
5112         F.Nullify(); surface.Nullify();
5113       }
5114       const TGeomID sInd = eos._shapeID;
5115
5116       // perform smoothing
5117
5118       if ( eos.ShapeType() == TopAbs_EDGE )
5119       {
5120         dumpFunction(SMESH_Comment("smooth")<<data._index << "_Ed"<<sInd <<"_InfStep"<<infStep);
5121
5122         if ( !eos._edgeSmoother->Perform( data, surface, F, helper ))
5123         {
5124           // smooth on EDGE's (normally we should not get here)
5125           int step = 0;
5126           do {
5127             moved = false;
5128             for ( size_t i = 0; i < eos._edges.size(); ++i )
5129             {
5130               moved |= eos._edges[i]->SmoothOnEdge( surface, F, helper );
5131             }
5132             dumpCmd( SMESH_Comment("# end step ")<<step);
5133           }
5134           while ( moved && step++ < 5 );
5135         }
5136         dumpFunctionEnd();
5137       }
5138
5139       else // smooth on FACE
5140       {
5141         eosC1.clear();
5142         eosC1.push_back( & eos );
5143         eosC1.insert( eosC1.end(), eos._eosC1.begin(), eos._eosC1.end() );
5144
5145         movedEdges.clear();
5146         isConcaveFace.resize( eosC1.size() );
5147         for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5148         {
5149           isConcaveFace[ iEOS ] = data._concaveFaces.count( eosC1[ iEOS ]->_shapeID  );
5150
5151           if ( eosC1[ iEOS ]->_mapper2D )
5152           {
5153             // compute node position by boundary node position in structured mesh
5154             dumpFunction(SMESH_Comment("map2dS")<<data._index<<"_Fa"<<eos._shapeID
5155                          <<"_InfStep"<<infStep);
5156
5157             eosC1[ iEOS ]->_mapper2D->ComputeNodePositions();
5158
5159             for ( _LayerEdge* le : eosC1[ iEOS ]->_edges )
5160               le->_pos.back() = SMESH_NodeXYZ( le->_nodes.back() );
5161
5162             dumpFunctionEnd();
5163           }
5164           else
5165           {
5166             for ( _LayerEdge* le : eosC1[ iEOS ]->_edges )
5167               if ( le->Is( _LayerEdge::MOVED ) ||
5168                    le->Is( _LayerEdge::NEAR_BOUNDARY ))
5169                 movedEdges.push_back( le );
5170           }
5171           makeOffsetSurface( *eosC1[ iEOS ], helper );
5172         }
5173
5174         int step = 0, stepLimit = 5, nbBad = 0;
5175         while (( ++step <= stepLimit ) || improved )
5176         {
5177           int oldBadNb = nbBad;
5178           badEdges.clear();
5179
5180 #ifdef INCREMENTAL_SMOOTH
5181           // smooth moved only
5182           if ( !movedEdges.empty() )
5183             dumpFunction(SMESH_Comment("smooth")<<data._index<<"_Fa"<<sInd
5184                          <<"_InfStep"<<infStep<<"_"<<step); // debug
5185           bool findBest = false; // ( step == stepLimit );
5186           for ( size_t i = 0; i < movedEdges.size(); ++i )
5187           {
5188             movedEdges[i]->Unset( _LayerEdge::SMOOTHED );
5189             if ( movedEdges[i]->Smooth( step, findBest, movedEdges ) > 0 )
5190               badEdges.push_back( movedEdges[i] );
5191           }
5192 #else
5193           // smooth all
5194           dumpFunction(SMESH_Comment("smooth")<<data._index<<"_Fa"<<sInd
5195                        <<"_InfStep"<<infStep<<"_"<<step); // debug
5196           bool findBest = ( step == stepLimit || isConcaveFace[ iEOS ]);
5197           for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5198           {
5199             if ( eosC1[ iEOS ]->_mapper2D )
5200               continue;
5201             vector< _LayerEdge* > & edges = eosC1[ iEOS ]->_edges;
5202             for ( size_t i = 0; i < edges.size(); ++i )
5203             {
5204               edges[i]->Unset( _LayerEdge::SMOOTHED );
5205               if ( edges[i]->Smooth( step, findBest, false ) > 0 )
5206                 badEdges.push_back( eos._edges[i] );
5207             }
5208           }
5209 #endif
5210           nbBad = badEdges.size();
5211
5212           if ( nbBad > 0 )
5213             debugMsg(SMESH_Comment("nbBad = ") << nbBad );
5214
5215           if ( !badEdges.empty() && step >= stepLimit / 2 )
5216           {
5217             if ( badEdges[0]->Is( _LayerEdge::ON_CONCAVE_FACE ))
5218               stepLimit = 9;
5219
5220             // resolve hard smoothing situation around concave VERTEXes
5221             for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5222             {
5223               vector< _EdgesOnShape* > & eosCoVe = eosC1[ iEOS ]->_eosConcaVer;
5224               for ( size_t i = 0; i < eosCoVe.size(); ++i )
5225                 eosCoVe[i]->_edges[0]->MoveNearConcaVer( eosCoVe[i], eosC1[ iEOS ],
5226                                                          step, badEdges );
5227             }
5228             // look for the best smooth of _LayerEdge's neighboring badEdges
5229             nbBad = 0;
5230             for ( size_t i = 0; i < badEdges.size(); ++i )
5231             {
5232               _LayerEdge* ledge = badEdges[i];
5233               for ( size_t iN = 0; iN < ledge->_neibors.size(); ++iN )
5234               {
5235                 ledge->_neibors[iN]->Unset( _LayerEdge::SMOOTHED );
5236                 nbBad += ledge->_neibors[iN]->Smooth( step, true, /*findBest=*/true );
5237               }
5238               ledge->Unset( _LayerEdge::SMOOTHED );
5239               nbBad += ledge->Smooth( step, true, /*findBest=*/true );
5240             }
5241             debugMsg(SMESH_Comment("nbBad = ") << nbBad );
5242           }
5243
5244           if ( nbBad == oldBadNb  &&
5245                nbBad > 0 &&
5246                step < stepLimit ) // smooth w/o check of validity
5247           {
5248             dumpFunctionEnd();
5249             dumpFunction(SMESH_Comment("smoothWoCheck")<<data._index<<"_Fa"<<sInd
5250                          <<"_InfStep"<<infStep<<"_"<<step); // debug
5251             for ( size_t i = 0; i < movedEdges.size(); ++i )
5252             {
5253               movedEdges[i]->SmoothWoCheck();
5254             }
5255             if ( stepLimit < 9 )
5256               stepLimit++;
5257           }
5258
5259           improved = ( nbBad < oldBadNb );
5260
5261           dumpFunctionEnd();
5262
5263           if (( step % 3 == 1 ) || ( nbBad > 0 && step >= stepLimit / 2 ))
5264             for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5265             {
5266               putOnOffsetSurface( *eosC1[ iEOS ], infStep, eosC1, step, /*moveAll=*/step == 1 );
5267             }
5268
5269         } // smoothing steps
5270
5271         // project -- to prevent intersections or to fix bad simplices
5272         for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5273         {
5274           if ( ! eosC1[ iEOS ]->_eosConcaVer.empty() || nbBad > 0 )
5275             putOnOffsetSurface( *eosC1[ iEOS ], -infStep, eosC1 );
5276         }
5277
5278         //if ( !badEdges.empty() )
5279         {
5280           badEdges.clear();
5281           for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5282           {
5283             for ( size_t i = 0; i < eosC1[ iEOS ]->_edges.size(); ++i )
5284             {
5285               if ( !eosC1[ iEOS ]->_sWOL.IsNull() ) continue;
5286
5287               _LayerEdge* edge = eosC1[ iEOS ]->_edges[i];
5288               edge->CheckNeiborsOnBoundary( & badEdges );
5289               if (( nbBad > 0 ) ||
5290                   ( edge->Is( _LayerEdge::BLOCKED ) && edge->Is( _LayerEdge::NEAR_BOUNDARY )))
5291               {
5292                 SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
5293                 gp_XYZ        prevXYZ = edge->PrevCheckPos();
5294                 for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5295                   if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
5296                   {
5297                     debugMsg("Bad simplex ( " << edge->_nodes[0]->GetID()
5298                              << " "<< tgtXYZ._node->GetID()
5299                              << " "<< edge->_simplices[j]._nPrev->GetID()
5300                              << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
5301                     badEdges.push_back( edge );
5302                     break;
5303                   }
5304               }
5305             }
5306           }
5307
5308           // try to fix bad simplices by removing the last inflation step of some _LayerEdge's
5309           nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5310
5311           if ( nbBad > 0 )
5312             return false;
5313         }
5314
5315       } // // smooth on FACE's
5316     } // loop on shapes
5317   } // smooth on [ EDGEs, FACEs ]
5318
5319   // Check orientation of simplices of _LayerEdge's on EDGEs and VERTEXes
5320   eosC1.resize(1);
5321   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
5322   {
5323     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
5324     if ( eos.ShapeType() == TopAbs_FACE ||
5325          eos._edges.empty() ||
5326          !eos._sWOL.IsNull() )
5327       continue;
5328
5329     badEdges.clear();
5330     for ( size_t i = 0; i < eos._edges.size(); ++i )
5331     {
5332       _LayerEdge*      edge = eos._edges[i];
5333       if ( edge->_nodes.size() < 2 ) continue;
5334       SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
5335       //SMESH_TNodeXYZ prevXYZ = edge->_nodes[0];
5336       gp_XYZ        prevXYZ = edge->PrevCheckPos( &eos );
5337       //const gp_XYZ& prevXYZ = edge->PrevPos();
5338       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5339         if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
5340         {
5341           debugMsg("Bad simplex on bnd ( " << edge->_nodes[0]->GetID()
5342                    << " "<< tgtXYZ._node->GetID()
5343                    << " "<< edge->_simplices[j]._nPrev->GetID()
5344                    << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
5345           badEdges.push_back( edge );
5346           break;
5347         }
5348     }
5349
5350     // try to fix bad simplices by removing the last inflation step of some _LayerEdge's
5351     eosC1[0] = &eos;
5352     int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5353     if ( nbBad > 0 )
5354       return false;
5355   }
5356
5357
5358   // Check if the last segments of _LayerEdge intersects 2D elements;
5359   // checked elements are either temporary faces or faces on surfaces w/o the layers
5360
5361   SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
5362     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
5363                                            data._proxyMesh->GetFaces( data._solid )) );
5364
5365 #ifdef BLOCK_INFLATION
5366   const bool toBlockInfaltion = true;
5367 #else
5368   const bool toBlockInfaltion = false;
5369 #endif
5370   distToIntersection = Precision::Infinite();
5371   double dist;
5372   const SMDS_MeshElement* intFace = 0;
5373   const SMDS_MeshElement* closestFace = 0;
5374   _LayerEdge* le = 0;
5375   bool is1stBlocked = true; // dbg
5376   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
5377   {
5378     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
5379     if ( eos._edges.empty() || !eos._sWOL.IsNull() )
5380       continue;
5381     for ( size_t i = 0; i < eos._edges.size(); ++i )
5382     {
5383       if ( eos._edges[i]->Is( _LayerEdge::INTERSECTED ) ||
5384            eos._edges[i]->Is( _LayerEdge::MULTI_NORMAL ))
5385         continue;
5386       if ( eos._edges[i]->FindIntersection( *searcher, dist, data._epsilon, eos, &intFace ))
5387       {
5388         return false;
5389         // commented due to "Illegal hash-positionPosition" error in NETGEN
5390         // on Debian60 on viscous_layers_01/B2 case
5391         // Collision; try to deflate _LayerEdge's causing it
5392         // badEdges.clear();
5393         // badEdges.push_back( eos._edges[i] );
5394         // eosC1[0] = & eos;
5395         // int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5396         // if ( nbBad > 0 )
5397         //   return false;
5398
5399         // badEdges.clear();
5400         // if ( _EdgesOnShape* eof = data.GetShapeEdges( intFace->getshapeId() ))
5401         // {
5402         //   if ( const _TmpMeshFace* f = dynamic_cast< const _TmpMeshFace*>( intFace ))
5403         //   {
5404         //     const SMDS_MeshElement* srcFace =
5405         //       eof->_subMesh->GetSubMeshDS()->GetElement( f->getIdInShape() );
5406         //     SMDS_ElemIteratorPtr nIt = srcFace->nodesIterator();
5407         //     while ( nIt->more() )
5408         //     {
5409         //       const SMDS_MeshNode* srcNode = static_cast<const SMDS_MeshNode*>( nIt->next() );
5410         //       TNode2Edge::iterator n2e = data._n2eMap.find( srcNode );
5411         //       if ( n2e != data._n2eMap.end() )
5412         //         badEdges.push_back( n2e->second );
5413         //     }
5414         //     eosC1[0] = eof;
5415         //     nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5416         //     if ( nbBad > 0 )
5417         //       return false;
5418         //   }
5419         // }
5420         // if ( eos._edges[i]->FindIntersection( *searcher, dist, data._epsilon, eos, &intFace ))
5421         //   return false;
5422         // else
5423         //   continue;
5424       }
5425       if ( !intFace )
5426       {
5427         SMESH_Comment msg("Invalid? normal at node "); msg << eos._edges[i]->_nodes[0]->GetID();
5428         debugMsg( msg );
5429         continue;
5430       }
5431
5432       const bool isShorterDist = ( distToIntersection > dist );
5433       if ( toBlockInfaltion || isShorterDist )
5434       {
5435         // ignore intersection of a _LayerEdge based on a _ConvexFace with a face
5436         // lying on this _ConvexFace
5437         if ( _ConvexFace* convFace = data.GetConvexFace( intFace->getshapeId() ))
5438           if ( convFace->_isTooCurved && convFace->_subIdToEOS.count ( eos._shapeID ))
5439             continue;
5440
5441         // ignore intersection of a _LayerEdge based on a FACE with an element on this FACE
5442         // ( avoid limiting the thickness on the case of issue 22576)
5443         if ( intFace->getshapeId() == eos._shapeID  )
5444           continue;
5445
5446         // ignore intersection with intFace of an adjacent FACE
5447         if ( dist > 0.01 * eos._edges[i]->_len )
5448         {
5449           bool toIgnore = false;
5450           if (  eos._toSmooth )
5451           {
5452             const TopoDS_Shape& S = getMeshDS()->IndexToShape( intFace->getshapeId() );
5453             if ( !S.IsNull() && S.ShapeType() == TopAbs_FACE )
5454             {
5455               TopExp_Explorer sub( eos._shape,
5456                                    eos.ShapeType() == TopAbs_FACE ? TopAbs_EDGE : TopAbs_VERTEX );
5457               for ( ; !toIgnore && sub.More(); sub.Next() )
5458                 // is adjacent - has a common EDGE or VERTEX
5459                 toIgnore = ( helper.IsSubShape( sub.Current(), S ));
5460
5461               if ( toIgnore ) // check angle between normals
5462               {
5463                 gp_XYZ normal;
5464                 if ( SMESH_MeshAlgos::FaceNormal( intFace, normal, /*normalized=*/true ))
5465                   toIgnore  = ( normal * eos._edges[i]->_normal > -0.5 );
5466               }
5467             }
5468           }
5469           if ( !toIgnore ) // check if the edge is a neighbor of intFace
5470           {
5471             for ( size_t iN = 0; !toIgnore &&  iN < eos._edges[i]->_neibors.size(); ++iN )
5472             {
5473               int nInd = intFace->GetNodeIndex( eos._edges[i]->_neibors[ iN ]->_nodes.back() );
5474               toIgnore = ( nInd >= 0 );
5475             }
5476           }
5477           if ( toIgnore )
5478             continue;
5479         }
5480
5481         // intersection not ignored
5482
5483         double minDist = 0;
5484         if ( eos._edges[i]->_maxLen < 0.99 * eos._hyp.GetTotalThickness() ) // limited length
5485           minDist = eos._edges[i]->_len * theThickToIntersection;
5486
5487         if ( toBlockInfaltion && dist < minDist  )
5488         {
5489           if ( is1stBlocked ) { is1stBlocked = false; // debug
5490             dumpFunction(SMESH_Comment("blockIntersected") <<data._index<<"_InfStep"<<infStep);
5491           }
5492           eos._edges[i]->Set( _LayerEdge::INTERSECTED ); // not to intersect
5493           eos._edges[i]->Block( data );                  // not to inflate
5494
5495           //if ( _EdgesOnShape* eof = data.GetShapeEdges( intFace->getshapeId() ))
5496           {
5497             // block _LayerEdge's, on top of which intFace is
5498             if ( const _TmpMeshFace* f = dynamic_cast< const _TmpMeshFace*>( intFace ))
5499             {
5500               const SMDS_MeshElement* srcFace = f->_srcFace;
5501               SMDS_ElemIteratorPtr        nIt = srcFace->nodesIterator();
5502               while ( nIt->more() )
5503               {
5504                 const SMDS_MeshNode* srcNode = static_cast<const SMDS_MeshNode*>( nIt->next() );
5505                 TNode2Edge::iterator n2e = data._n2eMap.find( srcNode );
5506                 if ( n2e != data._n2eMap.end() )
5507                   n2e->second->Block( data );
5508               }
5509             }
5510           }
5511         }
5512
5513         if ( isShorterDist )
5514         {
5515           distToIntersection = dist;
5516           le = eos._edges[i];
5517           closestFace = intFace;
5518         }
5519
5520       } // if ( toBlockInfaltion || isShorterDist )
5521     } // loop on eos._edges
5522   } // loop on data._edgesOnShape
5523
5524   if ( !is1stBlocked )
5525   {
5526     dumpFunctionEnd();
5527   }
5528
5529   if ( closestFace && le )
5530   {
5531 #ifdef __myDEBUG
5532     SMDS_MeshElement::iterator nIt = closestFace->begin_nodes();
5533     cout << "#Shortest distance: _LayerEdge nodes: tgt " << le->_nodes.back()->GetID()
5534          << " src " << le->_nodes[0]->GetID()<< ", intersection with face ("
5535          << (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()
5536          << ") distance = " << distToIntersection<< endl;
5537 #endif
5538   }
5539
5540   return true;
5541 }
5542
5543 //================================================================================
5544 /*!
5545  * \brief try to fix bad simplices by removing the last inflation step of some _LayerEdge's
5546  *  \param [in,out] badSmooEdges - _LayerEdge's to fix
5547  *  \return int - resulting nb of bad _LayerEdge's
5548  */
5549 //================================================================================
5550
5551 int _ViscousBuilder::invalidateBadSmooth( _SolidData&               data,
5552                                           SMESH_MesherHelper&       helper,
5553                                           vector< _LayerEdge* >&    badSmooEdges,
5554                                           vector< _EdgesOnShape* >& eosC1,
5555                                           const int                 infStep )
5556 {
5557   if ( badSmooEdges.empty() || infStep == 0 ) return 0;
5558
5559   dumpFunction(SMESH_Comment("invalidateBadSmooth")<<"_S"<<eosC1[0]->_shapeID<<"_InfStep"<<infStep);
5560
5561   enum {
5562     INVALIDATED   = _LayerEdge::UNUSED_FLAG,
5563     TO_INVALIDATE = _LayerEdge::UNUSED_FLAG * 2,
5564     ADDED         = _LayerEdge::UNUSED_FLAG * 4
5565   };
5566   data.UnmarkEdges( TO_INVALIDATE & INVALIDATED & ADDED );
5567
5568   double vol;
5569   bool haveInvalidated = true;
5570   while ( haveInvalidated )
5571   {
5572     haveInvalidated = false;
5573     for ( size_t i = 0; i < badSmooEdges.size(); ++i )
5574     {
5575       _LayerEdge*   edge = badSmooEdges[i];
5576       _EdgesOnShape* eos = data.GetShapeEdges( edge );
5577       edge->Set( ADDED );
5578       bool invalidated = false;
5579       if ( edge->Is( TO_INVALIDATE ) && edge->NbSteps() > 1 )
5580       {
5581         edge->InvalidateStep( edge->NbSteps(), *eos, /*restoreLength=*/true );
5582         edge->Block( data );
5583         edge->Set( INVALIDATED );
5584         edge->Unset( TO_INVALIDATE );
5585         invalidated = true;
5586         haveInvalidated = true;
5587       }
5588
5589       // look for _LayerEdge's of bad _simplices
5590       int nbBad = 0;
5591       SMESH_TNodeXYZ tgtXYZ  = edge->_nodes.back();
5592       gp_XYZ        prevXYZ1 = edge->PrevCheckPos( eos );
5593       //const gp_XYZ& prevXYZ2 = edge->PrevPos();
5594       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5595       {
5596         if (( edge->_simplices[j].IsForward( &prevXYZ1, &tgtXYZ, vol ))/* &&
5597             ( &prevXYZ1 == &prevXYZ2 || edge->_simplices[j].IsForward( &prevXYZ2, &tgtXYZ, vol ))*/)
5598           continue;
5599
5600         bool isBad = true;
5601         _LayerEdge* ee[2] = { 0,0 };
5602         for ( size_t iN = 0; iN < edge->_neibors.size() &&   !ee[1]  ; ++iN )
5603           if ( edge->_simplices[j].Includes( edge->_neibors[iN]->_nodes.back() ))
5604             ee[ ee[0] != 0 ] = edge->_neibors[iN];
5605
5606         int maxNbSteps = Max( ee[0]->NbSteps(), ee[1]->NbSteps() );
5607         while ( maxNbSteps > edge->NbSteps() && isBad )
5608         {
5609           --maxNbSteps;
5610           for ( int iE = 0; iE < 2; ++iE )
5611           {
5612             if ( ee[ iE ]->NbSteps() > maxNbSteps &&
5613                  ee[ iE ]->NbSteps() > 1 )
5614             {
5615               _EdgesOnShape* eos = data.GetShapeEdges( ee[ iE ] );
5616               ee[ iE ]->InvalidateStep( ee[ iE ]->NbSteps(), *eos, /*restoreLength=*/true );
5617               ee[ iE ]->Block( data );
5618               ee[ iE ]->Set( INVALIDATED );
5619               haveInvalidated = true;
5620             }
5621           }
5622           if (( edge->_simplices[j].IsForward( &prevXYZ1, &tgtXYZ, vol )) /*&&
5623               ( &prevXYZ1 == &prevXYZ2 || edge->_simplices[j].IsForward( &prevXYZ2, &tgtXYZ, vol ))*/)
5624             isBad = false;
5625         }
5626         nbBad += isBad;
5627         if ( !ee[0]->Is( ADDED )) badSmooEdges.push_back( ee[0] );
5628         if ( !ee[1]->Is( ADDED )) badSmooEdges.push_back( ee[1] );
5629         ee[0]->Set( ADDED );
5630         ee[1]->Set( ADDED );
5631         if ( isBad )
5632         {
5633           ee[0]->Set( TO_INVALIDATE );
5634           ee[1]->Set( TO_INVALIDATE );
5635         }
5636       }
5637
5638       if ( !invalidated &&  nbBad > 0  &&  edge->NbSteps() > 1 )
5639       {
5640         _EdgesOnShape* eos = data.GetShapeEdges( edge );
5641         edge->InvalidateStep( edge->NbSteps(), *eos, /*restoreLength=*/true );
5642         edge->Block( data );
5643         edge->Set( INVALIDATED );
5644         edge->Unset( TO_INVALIDATE );
5645         haveInvalidated = true;
5646       }
5647     } // loop on badSmooEdges
5648   } // while ( haveInvalidated )
5649
5650   // re-smooth on analytical EDGEs
5651   for ( size_t i = 0; i < badSmooEdges.size(); ++i )
5652   {
5653     _LayerEdge* edge = badSmooEdges[i];
5654     if ( !edge->Is( INVALIDATED )) continue;
5655
5656     _EdgesOnShape* eos = data.GetShapeEdges( edge );
5657     if ( eos->ShapeType() == TopAbs_VERTEX )
5658     {
5659       PShapeIteratorPtr eIt = helper.GetAncestors( eos->_shape, *_mesh, TopAbs_EDGE );
5660       while ( const TopoDS_Shape* e = eIt->next() )
5661         if ( _EdgesOnShape* eoe = data.GetShapeEdges( *e ))
5662           if ( eoe->_edgeSmoother && eoe->_edgeSmoother->isAnalytic() )
5663           {
5664             // TopoDS_Face F; Handle(ShapeAnalysis_Surface) surface;
5665             // if ( eoe->SWOLType() == TopAbs_FACE ) {
5666             //   F       = TopoDS::Face( eoe->_sWOL );
5667             //   surface = helper.GetSurface( F );
5668             // }
5669             // eoe->_edgeSmoother->Perform( data, surface, F, helper );
5670             eoe->_edgeSmoother->_anaCurve.Nullify();
5671           }
5672     }
5673   }
5674
5675
5676   // check result of invalidation
5677
5678   int nbBad = 0;
5679   for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5680   {
5681     for ( size_t i = 0; i < eosC1[ iEOS ]->_edges.size(); ++i )
5682     {
5683       if ( !eosC1[ iEOS ]->_sWOL.IsNull() ) continue;
5684       _LayerEdge*      edge = eosC1[ iEOS ]->_edges[i];
5685       SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
5686       gp_XYZ        prevXYZ = edge->PrevCheckPos( eosC1[ iEOS ]);
5687       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5688         if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
5689         {
5690           ++nbBad;
5691           debugMsg("Bad simplex remains ( " << edge->_nodes[0]->GetID()
5692                    << " "<< tgtXYZ._node->GetID()
5693                    << " "<< edge->_simplices[j]._nPrev->GetID()
5694                    << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
5695         }
5696     }
5697   }
5698   dumpFunctionEnd();
5699
5700   return nbBad;
5701 }
5702
5703 //================================================================================
5704 /*!
5705  * \brief Create an offset surface
5706  */
5707 //================================================================================
5708
5709 void _ViscousBuilder::makeOffsetSurface( _EdgesOnShape& eos, SMESH_MesherHelper& helper )
5710 {
5711   if ( eos._offsetSurf.IsNull() ||
5712        eos._edgeForOffset == 0 ||
5713        eos._edgeForOffset->Is( _LayerEdge::BLOCKED ))
5714     return;
5715
5716   Handle(ShapeAnalysis_Surface) baseSurface = helper.GetSurface( TopoDS::Face( eos._shape ));
5717
5718   // find offset
5719   gp_Pnt   tgtP = SMESH_TNodeXYZ( eos._edgeForOffset->_nodes.back() );
5720   /*gp_Pnt2d uv=*/baseSurface->ValueOfUV( tgtP, Precision::Confusion() );
5721   eos._offsetValue = baseSurface->Gap();
5722
5723   eos._offsetSurf.Nullify();
5724
5725   try
5726   {
5727     BRepOffsetAPI_MakeOffsetShape offsetMaker;
5728     offsetMaker.PerformByJoin( eos._shape, -eos._offsetValue, Precision::Confusion() );
5729     if ( !offsetMaker.IsDone() ) return;
5730
5731     TopExp_Explorer fExp( offsetMaker.Shape(), TopAbs_FACE );
5732     if ( !fExp.More() ) return;
5733
5734     TopoDS_Face F = TopoDS::Face( fExp.Current() );
5735     Handle(Geom_Surface) surf = BRep_Tool::Surface( F );
5736     if ( surf.IsNull() ) return;
5737
5738     eos._offsetSurf = new ShapeAnalysis_Surface( surf );
5739   }
5740   catch ( Standard_Failure& )
5741   {
5742   }
5743 }
5744
5745 //================================================================================
5746 /*!
5747  * \brief Put nodes of a curved FACE to its offset surface
5748  */
5749 //================================================================================
5750
5751 void _ViscousBuilder::putOnOffsetSurface( _EdgesOnShape&            eos,
5752                                           int                       infStep,
5753                                           vector< _EdgesOnShape* >& eosC1,
5754                                           int                       smooStep,
5755                                           int                       moveAll )
5756 {
5757   _EdgesOnShape * eof = & eos;
5758   if ( eos.ShapeType() != TopAbs_FACE ) // eos is a boundary of C1 FACE, look for the FACE eos
5759   {
5760     eof = 0;
5761     for ( size_t i = 0; i < eosC1.size() && !eof; ++i )
5762     {
5763       if ( eosC1[i]->_offsetSurf.IsNull() ||
5764            eosC1[i]->ShapeType() != TopAbs_FACE ||
5765            eosC1[i]->_edgeForOffset == 0 ||
5766            eosC1[i]->_edgeForOffset->Is( _LayerEdge::BLOCKED ))
5767         continue;
5768       if ( SMESH_MesherHelper::IsSubShape( eos._shape, eosC1[i]->_shape ))
5769         eof = eosC1[i];
5770     }
5771   }
5772   if ( !eof ||
5773        eof->_offsetSurf.IsNull() ||
5774        eof->ShapeType() != TopAbs_FACE ||
5775        eof->_edgeForOffset == 0 ||
5776        eof->_edgeForOffset->Is( _LayerEdge::BLOCKED ))
5777     return;
5778
5779   double preci = BRep_Tool::Tolerance( TopoDS::Face( eof->_shape )), vol;
5780   bool neighborHasRiskySWOL = false;
5781   for ( size_t i = 0; i < eos._edges.size(); ++i )
5782   {
5783     _LayerEdge* edge = eos._edges[i];
5784     edge->Unset( _LayerEdge::MARKED );
5785     if ( edge->Is( _LayerEdge::BLOCKED ) || !edge->_curvature )
5786       continue;
5787     if ( moveAll == _LayerEdge::UPD_NORMAL_CONV )
5788     {
5789       if ( !edge->Is( _LayerEdge::UPD_NORMAL_CONV ))
5790         continue;
5791     }
5792     else if ( moveAll == _LayerEdge::RISKY_SWOL )
5793     {
5794       if ( !edge->Is( _LayerEdge::RISKY_SWOL ) ||
5795            edge->_cosin < 0 )
5796         continue;
5797     }
5798     else if ( !moveAll && !edge->Is( _LayerEdge::MOVED ))
5799       continue;
5800
5801     int nbBlockedAround = 0;
5802     for ( size_t iN = 0; iN < edge->_neibors.size(); ++iN )
5803     {
5804       nbBlockedAround += edge->_neibors[iN]->Is( _LayerEdge::BLOCKED );
5805       if ( edge->_neibors[iN]->Is( _LayerEdge::RISKY_SWOL ) &&
5806            edge->_neibors[iN]->_cosin > 0 )
5807         neighborHasRiskySWOL = true;
5808     }
5809     if ( nbBlockedAround > 1 )
5810       continue;
5811
5812     gp_Pnt tgtP = SMESH_TNodeXYZ( edge->_nodes.back() );
5813     gp_Pnt2d uv = eof->_offsetSurf->NextValueOfUV( edge->_curvature->_uv, tgtP, preci );
5814     if ( eof->_offsetSurf->Gap() > edge->_len ) continue; // NextValueOfUV() bug
5815     edge->_curvature->_uv = uv;
5816     if ( eof->_offsetSurf->Gap() < 10 * preci ) continue; // same pos
5817
5818     gp_XYZ  newP = eof->_offsetSurf->Value( uv ).XYZ();
5819     gp_XYZ prevP = edge->PrevCheckPos();
5820     bool      ok = true;
5821     if ( !moveAll )
5822       for ( size_t iS = 0; iS < edge->_simplices.size() && ok; ++iS )
5823       {
5824         ok = edge->_simplices[iS].IsForward( &prevP, &newP, vol );
5825       }
5826     if ( ok )
5827     {
5828       SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( edge->_nodes.back() );
5829       n->setXYZ( newP.X(), newP.Y(), newP.Z());
5830       edge->_pos.back() = newP;
5831
5832       edge->Set( _LayerEdge::MARKED );
5833       if ( moveAll == _LayerEdge::UPD_NORMAL_CONV )
5834       {
5835         edge->_normal = ( newP - prevP ).Normalized();
5836       }
5837       // if ( edge->_len < eof->_offsetValue )
5838       //   edge->_len = eof->_offsetValue;
5839
5840       if ( !eos._sWOL.IsNull() ) // RISKY_SWOL
5841       {
5842         double change = eof->_offsetSurf->Gap() / eof->_offsetValue;
5843         if (( newP - tgtP.XYZ() ) * edge->_normal < 0 )
5844           change = 1 - change;
5845         else
5846           change = 1 + change;
5847         gp_XYZ shitfVec    = tgtP.XYZ() - SMESH_NodeXYZ( edge->_nodes[0] );
5848         gp_XYZ newShiftVec = shitfVec * change;
5849         double shift       = edge->_normal * shitfVec;
5850         double newShift    = edge->_normal * newShiftVec;
5851         newP = tgtP.XYZ() + edge->_normal * ( newShift - shift );
5852
5853         uv = eof->_offsetSurf->NextValueOfUV( edge->_curvature->_uv, newP, preci );
5854         if ( eof->_offsetSurf->Gap() < edge->_len )
5855         {
5856           edge->_curvature->_uv = uv;
5857           newP = eof->_offsetSurf->Value( uv ).XYZ();
5858         }
5859         n->setXYZ( newP.X(), newP.Y(), newP.Z());
5860         if ( !edge->UpdatePositionOnSWOL( n, /*tol=*/10 * edge->_len / ( edge->NbSteps() + 1 ),
5861                                           eos, eos.GetData().GetHelper() ))
5862         {
5863           debugMsg("UpdatePositionOnSWOL fails in putOnOffsetSurface()" );
5864         }
5865       }
5866     }
5867   }
5868
5869   if (SALOME::VerbosityActivated())
5870   {
5871     // dumpMove() for debug
5872     size_t i = 0;
5873     for ( ; i < eos._edges.size(); ++i )
5874       if ( eos._edges[i]->Is( _LayerEdge::MARKED ))
5875         break;
5876     if ( i < eos._edges.size() )
5877     {
5878       dumpFunction(SMESH_Comment("putOnOffsetSurface_") << eos.ShapeTypeLetter() << eos._shapeID
5879                   << "_InfStep" << infStep << "_" << Abs( smooStep ));
5880       for ( ; i < eos._edges.size(); ++i )
5881       {
5882         if ( eos._edges[i]->Is( _LayerEdge::MARKED )) {
5883           dumpMove( eos._edges[i]->_nodes.back() );
5884         }
5885       }
5886       dumpFunctionEnd();
5887     }
5888   }
5889
5890   _ConvexFace* cnvFace;
5891   if ( moveAll != _LayerEdge::UPD_NORMAL_CONV &&
5892        eos.ShapeType() == TopAbs_FACE &&
5893        (cnvFace = eos.GetData().GetConvexFace( eos._shapeID )) &&
5894        !cnvFace->_normalsFixedOnBorders )
5895   {
5896     // put on the surface nodes built on FACE boundaries
5897     SMESH_subMeshIteratorPtr smIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false);
5898     while ( smIt->more() )
5899     {
5900       SMESH_subMesh*     sm = smIt->next();
5901       _EdgesOnShape* subEOS = eos.GetData().GetShapeEdges( sm->GetId() );
5902       if ( !subEOS->_sWOL.IsNull() ) continue;
5903       if ( std::find( eosC1.begin(), eosC1.end(), subEOS ) != eosC1.end() ) continue;
5904
5905       putOnOffsetSurface( *subEOS, infStep, eosC1, smooStep, _LayerEdge::UPD_NORMAL_CONV );
5906     }
5907     cnvFace->_normalsFixedOnBorders = true;
5908   }
5909
5910
5911   // bos #20643
5912   // negative smooStep means "final step", where we don't treat RISKY_SWOL edges
5913   // as edges based on FACE are a bit late comparing with them
5914   if ( smooStep >= 0 &&
5915        neighborHasRiskySWOL &&
5916        moveAll != _LayerEdge::RISKY_SWOL &&
5917        eos.ShapeType() == TopAbs_FACE )
5918   {
5919     // put on the surface nodes built on FACE boundaries
5920     SMESH_subMeshIteratorPtr smIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false);
5921     while ( smIt->more() )
5922     {
5923       SMESH_subMesh*     sm = smIt->next();
5924       _EdgesOnShape* subEOS = eos.GetData().GetShapeEdges( sm->GetId() );
5925       if ( subEOS->_sWOL.IsNull() ) continue;
5926       if ( std::find( eosC1.begin(), eosC1.end(), subEOS ) != eosC1.end() ) continue;
5927
5928       putOnOffsetSurface( *subEOS, infStep, eosC1, smooStep, _LayerEdge::RISKY_SWOL );
5929     }
5930   }
5931 }
5932
5933 //================================================================================
5934 /*!
5935  * \brief Return a curve of the EDGE to be used for smoothing and arrange
5936  *        _LayerEdge's to be in a consequent order
5937  */
5938 //================================================================================
5939
5940 Handle(Geom_Curve) _Smoother1D::CurveForSmooth( const TopoDS_Edge&  E,
5941                                                 _EdgesOnShape&      eos,
5942                                                 SMESH_MesherHelper& helper)
5943 {
5944   SMESHDS_SubMesh* smDS = eos._subMesh->GetSubMeshDS();
5945
5946   TopLoc_Location loc; double f,l;
5947
5948   Handle(Geom_Line)   line;
5949   Handle(Geom_Circle) circle;
5950   bool isLine, isCirc;
5951   if ( eos._sWOL.IsNull() ) /////////////////////////////////////////// 3D case
5952   {
5953     // check if the EDGE is a line
5954     Handle(Geom_Curve) curve = BRep_Tool::Curve( E, f, l);
5955     if ( curve->IsKind( STANDARD_TYPE( Geom_TrimmedCurve )))
5956       curve = Handle(Geom_TrimmedCurve)::DownCast( curve )->BasisCurve();
5957
5958     line   = Handle(Geom_Line)::DownCast( curve );
5959     circle = Handle(Geom_Circle)::DownCast( curve );
5960     isLine = (!line.IsNull());
5961     isCirc = (!circle.IsNull());
5962
5963     if ( !isLine && !isCirc ) // Check if the EDGE is close to a line
5964     {
5965       isLine = SMESH_Algo::IsStraight( E );
5966
5967       if ( isLine )
5968         line = new Geom_Line( gp::OX() ); // only type does matter
5969     }
5970     if ( !isLine && !isCirc && eos._edges.size() > 2) // Check if the EDGE is close to a circle
5971     {
5972       // TODO
5973     }
5974   }
5975   else //////////////////////////////////////////////////////////////////////// 2D case
5976   {
5977     if ( !eos._isRegularSWOL ) // 23190
5978       return NULL;
5979
5980     const TopoDS_Face& F = TopoDS::Face( eos._sWOL );
5981
5982     // check if the EDGE is a line
5983     Handle(Geom2d_Curve) curve = BRep_Tool::CurveOnSurface( E, F, f, l );
5984     if ( curve->IsKind( STANDARD_TYPE( Geom2d_TrimmedCurve )))
5985       curve = Handle(Geom2d_TrimmedCurve)::DownCast( curve )->BasisCurve();
5986
5987     Handle(Geom2d_Line)   line2d   = Handle(Geom2d_Line)::DownCast( curve );
5988     Handle(Geom2d_Circle) circle2d = Handle(Geom2d_Circle)::DownCast( curve );
5989     isLine = (!line2d.IsNull());
5990     isCirc = (!circle2d.IsNull());
5991
5992     if ( !isLine && !isCirc ) // Check if the EDGE is close to a line
5993     {
5994       Bnd_B2d bndBox;
5995       SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
5996       while ( nIt->more() )
5997         bndBox.Add( helper.GetNodeUV( F, nIt->next() ));
5998       gp_XY size = bndBox.CornerMax() - bndBox.CornerMin();
5999
6000       const double lineTol = 1e-2 * sqrt( bndBox.SquareExtent() );
6001       for ( int i = 0; i < 2 && !isLine; ++i )
6002         isLine = ( size.Coord( i+1 ) <= lineTol );
6003     }
6004     if ( !isLine && !isCirc && eos._edges.size() > 2 ) // Check if the EDGE is close to a circle
6005     {
6006       // TODO
6007     }
6008     if ( isLine )
6009     {
6010       line = new Geom_Line( gp::OX() ); // only type does matter
6011     }
6012     else if ( isCirc )
6013     {
6014       gp_Pnt2d p = circle2d->Location();
6015       gp_Ax2 ax( gp_Pnt( p.X(), p.Y(), 0), gp::DX());
6016       circle = new Geom_Circle( ax, 1.); // only center position does matter
6017     }
6018   }
6019
6020   if ( isLine )
6021     return line;
6022   if ( isCirc )
6023     return circle;
6024
6025   return Handle(Geom_Curve)();
6026 }
6027
6028 //================================================================================
6029 /*!
6030  * \brief Smooth edges on EDGE
6031  */
6032 //================================================================================
6033
6034 bool _Smoother1D::Perform(_SolidData&                    data,
6035                           Handle(ShapeAnalysis_Surface)& surface,
6036                           const TopoDS_Face&             F,
6037                           SMESH_MesherHelper&            helper )
6038 {
6039   if ( _leParams.empty() || ( !isAnalytic() && _offPoints.empty() ))
6040     prepare( data );
6041
6042   findEdgesToSmooth();
6043   if ( isAnalytic() )
6044     return smoothAnalyticEdge( data, surface, F, helper );
6045   else
6046     return smoothComplexEdge ( data, surface, F, helper );
6047 }
6048
6049 //================================================================================
6050 /*!
6051  * \brief Find edges to smooth
6052  */
6053 //================================================================================
6054
6055 void _Smoother1D::findEdgesToSmooth()
6056 {
6057   _LayerEdge* leOnV[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
6058   for ( int iEnd = 0; iEnd < 2; ++iEnd )
6059     if ( leOnV[iEnd]->Is( _LayerEdge::NORMAL_UPDATED ))
6060       _leOnV[iEnd]._cosin = Abs( _edgeDir[iEnd].Normalized() * leOnV[iEnd]->_normal );
6061
6062   _eToSmooth[0].first = _eToSmooth[0].second = 0;
6063
6064   for ( size_t i = 0; i < _eos.size(); ++i )
6065   {
6066     if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH ))
6067     {
6068       if ( needSmoothing( _leOnV[0]._cosin,
6069                           _eos[i]->_len * leOnV[0]->_lenFactor, _curveLen * _leParams[i] ) ||
6070            isToSmooth( i )
6071            )
6072         _eos[i]->Set( _LayerEdge::TO_SMOOTH );
6073       else
6074         break;
6075     }
6076     _eToSmooth[0].second = i+1;
6077   }
6078
6079   _eToSmooth[1].first = _eToSmooth[1].second = _eos.size();
6080
6081   for ( int i = _eos.size() - 1; i >= _eToSmooth[0].second; --i )
6082   {
6083     if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH ))
6084     {
6085       if ( needSmoothing( _leOnV[1]._cosin,
6086                           _eos[i]->_len * leOnV[1]->_lenFactor, _curveLen * ( 1.-_leParams[i] )) ||
6087            isToSmooth( i ))
6088         _eos[i]->Set( _LayerEdge::TO_SMOOTH );
6089       else
6090         break;
6091     }
6092     _eToSmooth[1].first = i;
6093   }
6094 }
6095
6096 //================================================================================
6097 /*!
6098  * \brief Check if iE-th _LayerEdge needs smoothing
6099  */
6100 //================================================================================
6101
6102 bool _Smoother1D::isToSmooth( int iE )
6103 {
6104   SMESH_NodeXYZ pi( _eos[iE]->_nodes[0] );
6105   SMESH_NodeXYZ p0( _eos[iE]->_2neibors->srcNode(0) );
6106   SMESH_NodeXYZ p1( _eos[iE]->_2neibors->srcNode(1) );
6107   gp_XYZ       seg0 = pi - p0;
6108   gp_XYZ       seg1 = p1 - pi;
6109   gp_XYZ    tangent =  seg0 + seg1;
6110   double tangentLen = tangent.Modulus();
6111   double  segMinLen = Min( seg0.Modulus(), seg1.Modulus() );
6112   if ( tangentLen < std::numeric_limits<double>::min() )
6113     return false;
6114   tangent /= tangentLen;
6115
6116   for ( size_t i = 0; i < _eos[iE]->_neibors.size(); ++i )
6117   {
6118     _LayerEdge* ne = _eos[iE]->_neibors[i];
6119     if ( !ne->Is( _LayerEdge::TO_SMOOTH ) ||
6120          ne->_nodes.size() < 2 ||
6121          ne->_nodes[0]->GetPosition()->GetDim() != 2 )
6122       continue;
6123     gp_XYZ edgeVec = SMESH_NodeXYZ( ne->_nodes.back() ) - SMESH_NodeXYZ( ne->_nodes[0] );
6124     double    proj = edgeVec * tangent;
6125     if ( needSmoothing( 1., proj, segMinLen ))
6126       return true;
6127   }
6128   return false;
6129 }
6130
6131 //================================================================================
6132 /*!
6133  * \brief smooth _LayerEdge's on a staight EDGE or circular EDGE
6134  */
6135 //================================================================================
6136
6137 bool _Smoother1D::smoothAnalyticEdge( _SolidData&                    data,
6138                                       Handle(ShapeAnalysis_Surface)& surface,
6139                                       const TopoDS_Face&             F,
6140                                       SMESH_MesherHelper&            helper)
6141 {
6142   if ( !isAnalytic() ) return false;
6143
6144   size_t iFrom = 0, iTo = _eos._edges.size();
6145
6146   if ( _anaCurve->IsKind( STANDARD_TYPE( Geom_Line )))
6147   {
6148     if ( F.IsNull() ) // 3D
6149     {
6150       SMESH_TNodeXYZ pSrc0( _eos._edges[iFrom]->_2neibors->srcNode(0) );
6151       SMESH_TNodeXYZ pSrc1( _eos._edges[iTo-1]->_2neibors->srcNode(1) );
6152       //const   gp_XYZ lineDir = pSrc1 - pSrc0;
6153       //_LayerEdge* vLE0 = getLEdgeOnV( 0 );
6154       //_LayerEdge* vLE1 = getLEdgeOnV( 1 );
6155       // bool shiftOnly = ( vLE0->Is( _LayerEdge::NORMAL_UPDATED ) ||
6156       //                    vLE0->Is( _LayerEdge::BLOCKED ) ||
6157       //                    vLE1->Is( _LayerEdge::NORMAL_UPDATED ) ||
6158       //                    vLE1->Is( _LayerEdge::BLOCKED ));
6159       for ( int iEnd = 0; iEnd < 2; ++iEnd )
6160       {
6161         iFrom = _eToSmooth[ iEnd ].first, iTo = _eToSmooth[ iEnd ].second;
6162         if ( iFrom >= iTo ) continue;
6163         SMESH_TNodeXYZ p0( _eos[iFrom]->_2neibors->tgtNode(0) );
6164         SMESH_TNodeXYZ p1( _eos[iTo-1]->_2neibors->tgtNode(1) );
6165         double param0 = ( iFrom == 0 ) ? 0. : _leParams[ iFrom-1 ];
6166         double param1 = _leParams[ iTo ];
6167         for ( size_t i = iFrom; i < iTo; ++i )
6168         {
6169           _LayerEdge*       edge = _eos[i];
6170           SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edge->_nodes.back() );
6171           double           param = ( _leParams[i] - param0 ) / ( param1 - param0 );
6172           gp_XYZ          newPos = p0 * ( 1. - param ) + p1 * param;
6173
6174           // if ( shiftOnly || edge->Is( _LayerEdge::NORMAL_UPDATED ))
6175           // {
6176           //   gp_XYZ curPos = SMESH_TNodeXYZ ( tgtNode );
6177           //   double  shift = ( lineDir * ( newPos - pSrc0 ) -
6178           //                     lineDir * ( curPos - pSrc0 ));
6179           //   newPos = curPos + lineDir * shift / lineDir.SquareModulus();
6180           // }
6181           if ( edge->Is( _LayerEdge::BLOCKED ))
6182           {
6183             SMESH_TNodeXYZ pSrc( edge->_nodes[0] );
6184             double curThick = pSrc.SquareDistance( tgtNode );
6185             double newThink = ( pSrc - newPos ).SquareModulus();
6186             if ( newThink > curThick )
6187               continue;
6188           }
6189           edge->_pos.back() = newPos;
6190           tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
6191           dumpMove( tgtNode );
6192         }
6193       }
6194     }
6195     else // 2D
6196     {
6197       _LayerEdge* eV0 = getLEdgeOnV( 0 );
6198       _LayerEdge* eV1 = getLEdgeOnV( 1 );
6199       gp_XY      uvV0 = eV0->LastUV( F, *data.GetShapeEdges( eV0 ));
6200       gp_XY      uvV1 = eV1->LastUV( F, *data.GetShapeEdges( eV1 ));
6201       if ( eV0->_nodes.back() == eV1->_nodes.back() ) // closed edge
6202       {
6203         int iPeriodic = helper.GetPeriodicIndex();
6204         if ( iPeriodic == 1 || iPeriodic == 2 )
6205         {
6206           uvV1.SetCoord( iPeriodic, helper.GetOtherParam( uvV1.Coord( iPeriodic )));
6207           if ( uvV0.Coord( iPeriodic ) > uvV1.Coord( iPeriodic ))
6208             std::swap( uvV0, uvV1 );
6209         }
6210       }
6211       for ( int iEnd = 0; iEnd < 2; ++iEnd )
6212       {
6213         iFrom = _eToSmooth[ iEnd ].first, iTo = _eToSmooth[ iEnd ].second;
6214         if ( iFrom >= iTo ) continue;
6215         _LayerEdge* e0 = _eos[iFrom]->_2neibors->_edges[0];
6216         _LayerEdge* e1 = _eos[iTo-1]->_2neibors->_edges[1];
6217         gp_XY      uv0 = ( e0 == eV0 ) ? uvV0 : e0->LastUV( F, _eos );
6218         gp_XY      uv1 = ( e1 == eV1 ) ? uvV1 : e1->LastUV( F, _eos );
6219         double  param0 = ( iFrom == 0 ) ? 0. : _leParams[ iFrom-1 ];
6220         double  param1 = _leParams[ iTo ];
6221         gp_XY  rangeUV = uv1 - uv0;
6222         for ( size_t i = iFrom; i < iTo; ++i )
6223         {
6224           if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
6225           double param = ( _leParams[i] - param0 ) / ( param1 - param0 );
6226           gp_XY newUV = uv0 + param * rangeUV;
6227
6228           gp_Pnt newPos = surface->Value( newUV.X(), newUV.Y() );
6229           SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos[i]->_nodes.back() );
6230           tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
6231           dumpMove( tgtNode );
6232
6233           if ( SMDS_FacePositionPtr pos = tgtNode->GetPosition() ) // NULL if F is noShrink
6234           {
6235             pos->SetUParameter( newUV.X() );
6236             pos->SetVParameter( newUV.Y() );
6237           }
6238
6239           gp_XYZ newUV0( newUV.X(), newUV.Y(), 0 );
6240
6241           if ( !_eos[i]->Is( _LayerEdge::SMOOTHED ))
6242           {
6243             _eos[i]->Set( _LayerEdge::SMOOTHED ); // to check in refine() (IPAL54237)
6244             if ( _eos[i]->_pos.size() > 2 )
6245             {
6246               // modify previous positions to make _LayerEdge less sharply bent
6247               vector<gp_XYZ>& uvVec = _eos[i]->_pos;
6248               const gp_XYZ  uvShift = newUV0 - uvVec.back();
6249               const double     len2 = ( uvVec.back() - uvVec[ 0 ] ).SquareModulus();
6250               int iPrev = uvVec.size() - 2;
6251               while ( iPrev > 0 )
6252               {
6253                 double r = ( uvVec[ iPrev ] - uvVec[0] ).SquareModulus() / len2;
6254                 uvVec[ iPrev ] += uvShift * r;
6255                 --iPrev;
6256               }
6257             }
6258           }
6259           _eos[i]->_pos.back() = newUV0;
6260         }
6261       }
6262     }
6263     return true;
6264   }
6265
6266   if ( _anaCurve->IsKind( STANDARD_TYPE( Geom_Circle )))
6267   {
6268     Handle(Geom_Circle) circle = Handle(Geom_Circle)::DownCast( _anaCurve );
6269     gp_Pnt center3D = circle->Location();
6270
6271     if ( F.IsNull() ) // 3D
6272     {
6273       if ( getLEdgeOnV( 0 )->_nodes.back() == getLEdgeOnV( 1 )->_nodes.back() )
6274         return true; // closed EDGE - nothing to do
6275
6276       // circle is a real curve of EDGE
6277       gp_Circ circ = circle->Circ();
6278
6279       // new center is shifted along its axis
6280       const gp_Dir& axis = circ.Axis().Direction();
6281       _LayerEdge*     e0 = getLEdgeOnV(0);
6282       _LayerEdge*     e1 = getLEdgeOnV(1);
6283       SMESH_TNodeXYZ  p0 = e0->_nodes.back();
6284       SMESH_TNodeXYZ  p1 = e1->_nodes.back();
6285       double      shift1 = axis.XYZ() * ( p0 - center3D.XYZ() );
6286       double      shift2 = axis.XYZ() * ( p1 - center3D.XYZ() );
6287       gp_Pnt   newCenter = center3D.XYZ() + axis.XYZ() * 0.5 * ( shift1 + shift2 );
6288
6289       double newRadius = 0.5 * ( newCenter.Distance( p0 ) + newCenter.Distance( p1 ));
6290
6291       gp_Ax2  newAxis( newCenter, axis, gp_Vec( newCenter, p0 ));
6292       gp_Circ newCirc( newAxis, newRadius );
6293       gp_Vec  vecC1  ( newCenter, p1 );
6294
6295       double uLast = newAxis.XDirection().AngleWithRef( vecC1, newAxis.Direction() ); // -PI - +PI
6296       if ( uLast < 0 )
6297         uLast += 2 * M_PI;
6298       
6299       for ( size_t i = 0; i < _eos.size(); ++i )
6300       {
6301         if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
6302         //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
6303         double u = uLast * _leParams[i];
6304         gp_Pnt p = ElCLib::Value( u, newCirc );
6305         _eos._edges[i]->_pos.back() = p.XYZ();
6306
6307         SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos._edges[i]->_nodes.back() );
6308         tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
6309         dumpMove( tgtNode );
6310       }
6311       return true;
6312     }
6313     else // 2D
6314     {
6315       const gp_XY center( center3D.X(), center3D.Y() );
6316
6317       _LayerEdge* e0 = getLEdgeOnV(0);
6318       _LayerEdge* eM = _eos._edges[ 0 ];
6319       _LayerEdge* e1 = getLEdgeOnV(1);
6320       gp_XY      uv0 = e0->LastUV( F, *data.GetShapeEdges( e0 ) );
6321       gp_XY      uvM = eM->LastUV( F, *data.GetShapeEdges( eM ) );
6322       gp_XY      uv1 = e1->LastUV( F, *data.GetShapeEdges( e1 ) );
6323       gp_Vec2d vec0( center, uv0 );
6324       gp_Vec2d vecM( center, uvM );
6325       gp_Vec2d vec1( center, uv1 );
6326       double uLast = vec0.Angle( vec1 ); // -PI - +PI
6327       double uMidl = vec0.Angle( vecM );
6328       if ( uLast * uMidl <= 0. )
6329         uLast += ( uMidl > 0 ? +2. : -2. ) * M_PI;
6330       const double radius = 0.5 * ( vec0.Magnitude() + vec1.Magnitude() );
6331
6332       gp_Ax2d   axis( center, vec0 );
6333       gp_Circ2d circ( axis, radius );
6334       for ( size_t i = 0; i < _eos.size(); ++i )
6335       {
6336         if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
6337         //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
6338         double    newU = uLast * _leParams[i];
6339         gp_Pnt2d newUV = ElCLib::Value( newU, circ );
6340         _eos._edges[i]->_pos.back().SetCoord( newUV.X(), newUV.Y(), 0 );
6341
6342         gp_Pnt newPos = surface->Value( newUV.X(), newUV.Y() );
6343         SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos._edges[i]->_nodes.back() );
6344         tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
6345         dumpMove( tgtNode );
6346
6347         if ( SMDS_FacePositionPtr pos = tgtNode->GetPosition() ) // NULL if F is noShrink
6348         {
6349           pos->SetUParameter( newUV.X() );
6350           pos->SetVParameter( newUV.Y() );
6351         }
6352         _eos[i]->Set( _LayerEdge::SMOOTHED ); // to check in refine() (IPAL54237)
6353       }
6354     }
6355     return true;
6356   }
6357
6358   return false;
6359 }
6360
6361 //================================================================================
6362 /*!
6363  * \brief smooth _LayerEdge's on a an EDGE
6364  */
6365 //================================================================================
6366
6367 bool _Smoother1D::smoothComplexEdge( _SolidData&                    /*data*/,
6368                                      Handle(ShapeAnalysis_Surface)& surface,
6369                                      const TopoDS_Face&             F,
6370                                      SMESH_MesherHelper&            /*helper*/)
6371 {
6372   if ( _offPoints.empty() )
6373     return false;
6374
6375   // ----------------------------------------------
6376   // move _offPoints along normals of _LayerEdge's
6377   // ----------------------------------------------
6378
6379   _LayerEdge* e[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
6380   if ( e[0]->Is( _LayerEdge::NORMAL_UPDATED ))
6381     _leOnV[0]._normal = getNormalNormal( e[0]->_normal, _edgeDir[0] );
6382   if ( e[1]->Is( _LayerEdge::NORMAL_UPDATED )) 
6383     _leOnV[1]._normal = getNormalNormal( e[1]->_normal, _edgeDir[1] );
6384   _leOnV[0]._len = e[0]->_len;
6385   _leOnV[1]._len = e[1]->_len;
6386   for ( size_t i = 0; i < _offPoints.size(); i++ )
6387   {
6388     _LayerEdge*  e0 = _offPoints[i]._2edges._edges[0];
6389     _LayerEdge*  e1 = _offPoints[i]._2edges._edges[1];
6390     const double w0 = _offPoints[i]._2edges._wgt[0];
6391     const double w1 = _offPoints[i]._2edges._wgt[1];
6392     gp_XYZ  avgNorm = ( e0->_normal    * w0 + e1->_normal    * w1 ).Normalized();
6393     double  avgLen  = ( e0->_len       * w0 + e1->_len       * w1 );
6394     double  avgFact = ( e0->_lenFactor * w0 + e1->_lenFactor * w1 );
6395     if ( e0->Is( _LayerEdge::NORMAL_UPDATED ) ||
6396          e1->Is( _LayerEdge::NORMAL_UPDATED ))
6397       avgNorm = getNormalNormal( avgNorm, _offPoints[i]._edgeDir );
6398
6399     _offPoints[i]._xyz += avgNorm * ( avgLen - _offPoints[i]._len ) * avgFact;
6400     _offPoints[i]._len  = avgLen;
6401   }
6402
6403   double fTol = 0;
6404   if ( !surface.IsNull() ) // project _offPoints to the FACE
6405   {
6406     fTol = 100 * BRep_Tool::Tolerance( F );
6407     //const double segLen = _offPoints[0].Distance( _offPoints[1] );
6408
6409     gp_Pnt2d uv = surface->ValueOfUV( _offPoints[0]._xyz, fTol );
6410     //if ( surface->Gap() < 0.5 * segLen )
6411       _offPoints[0]._xyz = surface->Value( uv ).XYZ();
6412
6413     for ( size_t i = 1; i < _offPoints.size(); ++i )
6414     {
6415       uv = surface->NextValueOfUV( uv, _offPoints[i]._xyz, fTol );
6416       //if ( surface->Gap() < 0.5 * segLen )
6417         _offPoints[i]._xyz = surface->Value( uv ).XYZ();
6418     }
6419   }
6420
6421   // -----------------------------------------------------------------
6422   // project tgt nodes of extreme _LayerEdge's to the offset segments
6423   // -----------------------------------------------------------------
6424
6425   const int updatedOrBlocked = _LayerEdge::NORMAL_UPDATED | _LayerEdge::BLOCKED;
6426   if ( e[0]->Is( updatedOrBlocked )) _iSeg[0] = 0;
6427   if ( e[1]->Is( updatedOrBlocked )) _iSeg[1] = _offPoints.size()-2;
6428
6429   gp_Pnt pExtreme[2], pProj[2];
6430   bool isProjected[2];
6431   for ( int is2nd = 0; is2nd < 2; ++is2nd )
6432   {
6433     pExtreme[ is2nd ] = SMESH_TNodeXYZ( e[is2nd]->_nodes.back() );
6434     int  i = _iSeg[ is2nd ];
6435     int di = is2nd ? -1 : +1;
6436     bool & projected = isProjected[ is2nd ];
6437     projected = false;
6438     double uOnSeg, distMin = Precision::Infinite(), dist, distPrev = 0;
6439     int nbWorse = 0;
6440     do {
6441       gp_Vec v0p( _offPoints[i]._xyz, pExtreme[ is2nd ]    );
6442       gp_Vec v01( _offPoints[i]._xyz, _offPoints[i+1]._xyz );
6443       uOnSeg     = ( v0p * v01 ) / v01.SquareMagnitude();  // param [0,1] along v01
6444       projected  = ( Abs( uOnSeg - 0.5 ) <= 0.5 );
6445       dist       =  pExtreme[ is2nd ].SquareDistance( _offPoints[ i + ( uOnSeg > 0.5 )]._xyz );
6446       if ( dist < distMin || projected )
6447       {
6448         _iSeg[ is2nd ] = i;
6449         pProj[ is2nd ] = _offPoints[i]._xyz + ( v01 * uOnSeg ).XYZ();
6450         distMin = dist;
6451       }
6452       else if ( dist > distPrev )
6453       {
6454         if ( ++nbWorse > 3 ) // avoid projection to the middle of a closed EDGE
6455           break;
6456       }
6457       distPrev = dist;
6458       i += di;
6459     }
6460     while ( !projected &&
6461             i >= 0 && i+1 < (int)_offPoints.size() );
6462
6463     if ( !projected )
6464     {
6465       if (( is2nd && _iSeg[1] != _offPoints.size()-2 ) || ( !is2nd && _iSeg[0] != 0 ))
6466       {
6467         _iSeg[0] = 0;
6468         _iSeg[1] = _offPoints.size()-2;
6469         debugMsg( "smoothComplexEdge() failed to project nodes of extreme _LayerEdge's" );
6470         return false;
6471       }
6472     }
6473   }
6474   if ( _iSeg[0] > _iSeg[1] )
6475   {
6476     debugMsg( "smoothComplexEdge() incorrectly projected nodes of extreme _LayerEdge's" );
6477     return false;
6478   }
6479
6480   // adjust length of extreme LE (test viscous_layers_01/B7)
6481   gp_Vec vDiv0( pExtreme[0], pProj[0] );
6482   gp_Vec vDiv1( pExtreme[1], pProj[1] );
6483   double d0 = vDiv0.Magnitude();
6484   double d1 = isProjected[1] ? vDiv1.Magnitude() : 0;
6485   if ( e[0]->Is( _LayerEdge::BLOCKED )) {
6486     if ( e[0]->_normal * vDiv0.XYZ() < 0 ) e[0]->_len += d0;
6487     else                                   e[0]->_len -= d0;
6488   }
6489   if ( e[1]->Is( _LayerEdge::BLOCKED )) {
6490     if ( e[1]->_normal * vDiv1.XYZ() < 0 ) e[1]->_len += d1;
6491     else                                   e[1]->_len -= d1;
6492   }
6493
6494   // ---------------------------------------------------------------------------------
6495   // compute normalized length of the offset segments located between the projections
6496   // ---------------------------------------------------------------------------------
6497
6498   // temporary replace extreme _offPoints by pExtreme
6499   gp_XYZ opXYZ[2] = { _offPoints[ _iSeg[0]   ]._xyz,
6500                       _offPoints[ _iSeg[1]+1 ]._xyz };
6501   _offPoints[ _iSeg[0]   ]._xyz = pExtreme[0].XYZ();
6502   _offPoints[ _iSeg[1]+ 1]._xyz = pExtreme[1].XYZ();
6503
6504   size_t iSeg = 0, nbSeg = _iSeg[1] - _iSeg[0] + 1;
6505   vector< double > len( nbSeg + 1 );
6506   len[ iSeg++ ] = 0;
6507   len[ iSeg++ ] = pProj[ 0 ].Distance( _offPoints[ _iSeg[0]+1 ]._xyz );
6508   for ( size_t i = _iSeg[0]+1; i <= _iSeg[1]; ++i, ++iSeg )
6509   {
6510     len[ iSeg ] = len[ iSeg-1 ] + _offPoints[i].Distance( _offPoints[i+1] );
6511   }
6512   // if ( isProjected[ 1 ])
6513   //   len[ nbSeg ] -= pProj[ 1 ].Distance( _offPoints[ _iSeg[1]+1 ]._xyz );
6514   // else
6515   //   len[ nbSeg ] += pExtreme[ 1 ].Distance( _offPoints[ _iSeg[1]+1 ]._xyz );
6516
6517   double fullLen = len.back() - d0 - d1;
6518   for ( iSeg = 0; iSeg < len.size(); ++iSeg )
6519     len[iSeg] = ( len[iSeg] - d0 ) / fullLen;
6520
6521   // -------------------------------------------------------------
6522   // distribute tgt nodes of _LayerEdge's between the projections
6523   // -------------------------------------------------------------
6524
6525   iSeg = 0;
6526   for ( size_t i = 0; i < _eos.size(); ++i )
6527   {
6528     if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
6529     //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
6530     while ( iSeg+2 < len.size() && _leParams[i] > len[ iSeg+1 ] )
6531       iSeg++;
6532     double r = ( _leParams[i] - len[ iSeg ]) / ( len[ iSeg+1 ] - len[ iSeg ]);
6533     gp_XYZ p = ( _offPoints[ iSeg + _iSeg[0]     ]._xyz * ( 1 - r ) +
6534                  _offPoints[ iSeg + _iSeg[0] + 1 ]._xyz * r );
6535
6536     if ( surface.IsNull() )
6537     {
6538       _eos[i]->_pos.back() = p;
6539     }
6540     else // project a new node position to a FACE
6541     {
6542       gp_Pnt2d uv ( _eos[i]->_pos.back().X(), _eos[i]->_pos.back().Y() );
6543       gp_Pnt2d uv2( surface->NextValueOfUV( uv, p, fTol ));
6544
6545       p = surface->Value( uv2 ).XYZ();
6546       _eos[i]->_pos.back().SetCoord( uv2.X(), uv2.Y(), 0 );
6547     }
6548     SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos[i]->_nodes.back() );
6549     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
6550     dumpMove( tgtNode );
6551   }
6552
6553   _offPoints[ _iSeg[0]   ]._xyz = opXYZ[0];
6554   _offPoints[ _iSeg[1]+1 ]._xyz = opXYZ[1];
6555
6556   return true;
6557 }
6558
6559 //================================================================================
6560 /*!
6561  * \brief Prepare for smoothing
6562  */
6563 //================================================================================
6564
6565 void _Smoother1D::prepare(_SolidData& data)
6566 {
6567   const TopoDS_Edge& E = TopoDS::Edge( _eos._shape );
6568   _curveLen = SMESH_Algo::EdgeLength( E );
6569
6570   // sort _LayerEdge's by position on the EDGE
6571   data.SortOnEdge( E, _eos._edges );
6572
6573   // compute normalized param of _eos._edges on EDGE
6574   _leParams.resize( _eos._edges.size() + 1 );
6575   {
6576     double curLen;
6577     gp_Pnt pPrev = SMESH_TNodeXYZ( getLEdgeOnV( 0 )->_nodes[0] );
6578     _leParams[0] = 0;
6579     for ( size_t i = 0; i < _eos._edges.size(); ++i )
6580     {
6581       gp_Pnt p       = SMESH_TNodeXYZ( _eos._edges[i]->_nodes[0] );
6582       curLen         = p.Distance( pPrev );
6583       _leParams[i+1] = _leParams[i] + curLen;
6584       pPrev          = p;
6585     }
6586     double fullLen = _leParams.back() + pPrev.Distance( SMESH_TNodeXYZ( getLEdgeOnV(1)->_nodes[0]));
6587     for ( size_t i = 0; i < _leParams.size()-1; ++i )
6588       _leParams[i] = _leParams[i+1] / fullLen;
6589     _leParams.back() = 1.;
6590   }
6591
6592   _LayerEdge* leOnV[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
6593
6594   // get cosin to use in findEdgesToSmooth()
6595   _edgeDir[0] = getEdgeDir( E, leOnV[0]->_nodes[0], data.GetHelper() );
6596   _edgeDir[1] = getEdgeDir( E, leOnV[1]->_nodes[0], data.GetHelper() );
6597   _leOnV[0]._cosin = Abs( leOnV[0]->_cosin );
6598   _leOnV[1]._cosin = Abs( leOnV[1]->_cosin );
6599   _leOnV[0]._flags = _leOnV[1]._flags = 0;
6600   if ( _eos._sWOL.IsNull() ) // 3D
6601     for ( int iEnd = 0; iEnd < 2; ++iEnd )
6602       _leOnV[iEnd]._cosin = Abs( _edgeDir[iEnd].Normalized() * leOnV[iEnd]->_normal );
6603
6604   if ( isAnalytic() )
6605     return;
6606
6607   // divide E to have offset segments with low deflection
6608   BRepAdaptor_Curve c3dAdaptor( E );
6609   const double curDeflect = 0.1; //0.01; // Curvature deflection == |p1p2|*sin(p1p2,p1pM)
6610   const double angDeflect = 0.1; //0.09; // Angular deflection == sin(p1pM,pMp2)
6611   GCPnts_TangentialDeflection discret(c3dAdaptor, angDeflect, curDeflect);
6612   if ( discret.NbPoints() <= 2 )
6613   {
6614     _anaCurve = new Geom_Line( gp::OX() ); // only type does matter
6615     return;
6616   }
6617
6618   const double u0 = c3dAdaptor.FirstParameter();
6619   gp_Pnt p; gp_Vec tangent;
6620   if ( discret.NbPoints() >= (int) _eos.size() + 2 )
6621   {
6622     _offPoints.resize( discret.NbPoints() );
6623     for ( size_t i = 0; i < _offPoints.size(); i++ )
6624     {
6625       double u = discret.Parameter( i+1 );
6626       c3dAdaptor.D1( u, p, tangent );
6627       _offPoints[i]._xyz     = p.XYZ();
6628       _offPoints[i]._edgeDir = tangent.XYZ();
6629       _offPoints[i]._param = GCPnts_AbscissaPoint::Length( c3dAdaptor, u0, u ) / _curveLen;
6630     }
6631   }
6632   else
6633   {
6634     std::vector< double > params( _eos.size() + 2 );
6635
6636     params[0]     = data.GetHelper().GetNodeU( E, leOnV[0]->_nodes[0] );
6637     params.back() = data.GetHelper().GetNodeU( E, leOnV[1]->_nodes[0] );
6638     for ( size_t i = 0; i < _eos.size(); i++ )
6639       params[i+1] = data.GetHelper().GetNodeU( E, _eos[i]->_nodes[0] );
6640
6641     if ( params[1] > params[ _eos.size() ] )
6642       std::reverse( params.begin() + 1, params.end() - 1 );
6643
6644     _offPoints.resize( _eos.size() + 2 );
6645     for ( size_t i = 0; i < _offPoints.size(); i++ )
6646     {
6647       const double u = params[i];
6648       c3dAdaptor.D1( u, p, tangent );
6649       _offPoints[i]._xyz     = p.XYZ();
6650       _offPoints[i]._edgeDir = tangent.XYZ();
6651       _offPoints[i]._param = GCPnts_AbscissaPoint::Length( c3dAdaptor, u0, u ) / _curveLen;
6652     }
6653   }
6654
6655   // set _2edges
6656   _offPoints    [0]._2edges.set( &_leOnV[0], &_leOnV[0], 0.5, 0.5 );
6657   _offPoints.back()._2edges.set( &_leOnV[1], &_leOnV[1], 0.5, 0.5 );
6658   _2NearEdges tmp2edges;
6659   tmp2edges._edges[1] = _eos._edges[0];
6660   _leOnV[0]._2neibors = & tmp2edges;
6661   _leOnV[0]._nodes    = leOnV[0]->_nodes;
6662   _leOnV[1]._nodes    = leOnV[1]->_nodes;
6663   _LayerEdge* eNext, *ePrev = & _leOnV[0];
6664   for ( size_t iLE = 0, i = 1; i < _offPoints.size()-1; i++ )
6665   {
6666     // find _LayerEdge's located before and after an offset point
6667     // (_eos._edges[ iLE ] is next after ePrev)
6668     while ( iLE < _eos._edges.size() && _offPoints[i]._param > _leParams[ iLE ] )
6669       ePrev = _eos._edges[ iLE++ ];
6670     eNext = ePrev->_2neibors->_edges[1];
6671
6672     gp_Pnt p0 = SMESH_TNodeXYZ( ePrev->_nodes[0] );
6673     gp_Pnt p1 = SMESH_TNodeXYZ( eNext->_nodes[0] );
6674     double  r = p0.Distance( _offPoints[i]._xyz ) / p0.Distance( p1 );
6675     _offPoints[i]._2edges.set( ePrev, eNext, 1-r, r );
6676   }
6677
6678   // replace _LayerEdge's on VERTEX by _leOnV in _offPoints._2edges
6679   for ( size_t i = 0; i < _offPoints.size(); i++ )
6680     if ( _offPoints[i]._2edges._edges[0] == leOnV[0] )
6681       _offPoints[i]._2edges._edges[0] = & _leOnV[0];
6682     else break;
6683   for ( size_t i = _offPoints.size()-1; i > 0; i-- )
6684     if ( _offPoints[i]._2edges._edges[1] == leOnV[1] )
6685       _offPoints[i]._2edges._edges[1] = & _leOnV[1];
6686     else break;
6687
6688   // set _normal of _leOnV[0] and _leOnV[1] to be normal to the EDGE
6689
6690   int iLBO = _offPoints.size() - 2; // last but one
6691
6692   if ( leOnV[ 0 ]->Is( _LayerEdge::MULTI_NORMAL ))
6693     _leOnV[ 0 ]._normal = getNormalNormal( _eos._edges[1]->_normal, _edgeDir[0] );
6694   else
6695     _leOnV[ 0 ]._normal = getNormalNormal( leOnV[0]->_normal,       _edgeDir[0] );
6696   if ( leOnV[ 1 ]->Is( _LayerEdge::MULTI_NORMAL ))
6697     _leOnV[ 1 ]._normal = getNormalNormal( _eos._edges.back()->_normal, _edgeDir[1] );
6698   else
6699     _leOnV[ 1 ]._normal = getNormalNormal( leOnV[1]->_normal,           _edgeDir[1] );
6700   _leOnV[ 0 ]._len = 0;
6701   _leOnV[ 1 ]._len = 0;
6702   _leOnV[ 0 ]._lenFactor = _offPoints[1   ]._2edges._edges[1]->_lenFactor;
6703   _leOnV[ 1 ]._lenFactor = _offPoints[iLBO]._2edges._edges[0]->_lenFactor;
6704
6705   _iSeg[0] = 0;
6706   _iSeg[1] = _offPoints.size()-2;
6707
6708   // initialize OffPnt::_len
6709   for ( size_t i = 0; i < _offPoints.size(); ++i )
6710     _offPoints[i]._len = 0;
6711
6712   if ( _eos._edges[0]->NbSteps() > 1 ) // already inflated several times, init _xyz
6713   {
6714     _leOnV[0]._len = leOnV[0]->_len;
6715     _leOnV[1]._len = leOnV[1]->_len;
6716     for ( size_t i = 0; i < _offPoints.size(); i++ )
6717     {
6718       _LayerEdge*  e0 = _offPoints[i]._2edges._edges[0];
6719       _LayerEdge*  e1 = _offPoints[i]._2edges._edges[1];
6720       const double w0 = _offPoints[i]._2edges._wgt[0];
6721       const double w1 = _offPoints[i]._2edges._wgt[1];
6722       double  avgLen  = ( e0->_len * w0 + e1->_len * w1 );
6723       gp_XYZ  avgXYZ  = ( SMESH_TNodeXYZ( e0->_nodes.back() ) * w0 +
6724                           SMESH_TNodeXYZ( e1->_nodes.back() ) * w1 );
6725       _offPoints[i]._xyz = avgXYZ;
6726       _offPoints[i]._len = avgLen;
6727     }
6728   }
6729 }
6730
6731 //================================================================================
6732 /*!
6733  * \brief return _normal of _leOnV[is2nd] normal to the EDGE
6734  */
6735 //================================================================================
6736
6737 gp_XYZ _Smoother1D::getNormalNormal( const gp_XYZ & normal,
6738                                      const gp_XYZ&  edgeDir)
6739 {
6740   gp_XYZ cross = normal ^ edgeDir;
6741   gp_XYZ  norm = edgeDir ^ cross;
6742   double  size = norm.Modulus();
6743
6744   // if ( size == 0 ) // MULTI_NORMAL _LayerEdge
6745   //   return gp_XYZ( 1e-100, 1e-100, 1e-100 );
6746
6747   if ( size < 1e-5 ) // normal || edgeDir (almost) at inflation along EDGE (bos #20643)
6748   {
6749     const _LayerEdge* le = _eos._edges[ _eos._edges.size() / 2 ];
6750     const gp_XYZ& leNorm = le->_normal;
6751
6752     cross = leNorm ^ edgeDir;
6753     norm = edgeDir ^ cross;
6754     size = norm.Modulus();
6755   }
6756
6757   return norm / size;
6758 }
6759
6760 //================================================================================
6761 /*!
6762  * \brief Writes a script creating a mesh composed of _offPoints
6763  */
6764 //================================================================================
6765
6766 void _Smoother1D::offPointsToPython() const
6767 {
6768   const char* fname = "/tmp/offPoints.py";
6769   cout << "exec(open('"<<fname<<"','rb').read() )"<<endl;
6770   ofstream py(fname);
6771   py << "import SMESH" << endl
6772      << "from salome.smesh import smeshBuilder" << endl
6773      << "smesh  = smeshBuilder.New(salome.myStudy)" << endl
6774      << "mesh   = smesh.Mesh( 'offPoints' )"<<endl;
6775   for ( size_t i = 0; i < _offPoints.size(); i++ )
6776   {
6777     py << "mesh.AddNode( "
6778        << _offPoints[i]._xyz.X() << ", "
6779        << _offPoints[i]._xyz.Y() << ", "
6780        << _offPoints[i]._xyz.Z() << " )" << endl;
6781   }
6782 }
6783
6784 //================================================================================
6785 /*!
6786  * \brief Sort _LayerEdge's by a parameter on a given EDGE
6787  */
6788 //================================================================================
6789
6790 void _SolidData::SortOnEdge( const TopoDS_Edge&     E,
6791                              vector< _LayerEdge* >& edges)
6792 {
6793   map< double, _LayerEdge* > u2edge;
6794   for ( size_t i = 0; i < edges.size(); ++i )
6795     u2edge.insert( u2edge.end(),
6796                    make_pair( _helper->GetNodeU( E, edges[i]->_nodes[0] ), edges[i] ));
6797
6798   ASSERT( u2edge.size() == edges.size() );
6799   map< double, _LayerEdge* >::iterator u2e = u2edge.begin();
6800   for ( size_t i = 0; i < edges.size(); ++i, ++u2e )
6801     edges[i] = u2e->second;
6802
6803   Sort2NeiborsOnEdge( edges );
6804 }
6805
6806 //================================================================================
6807 /*!
6808  * \brief Set _2neibors according to the order of _LayerEdge on EDGE
6809  */
6810 //================================================================================
6811
6812 void _SolidData::Sort2NeiborsOnEdge( vector< _LayerEdge* >& edges )
6813 {
6814   if ( edges.size() < 2 || !edges[0]->_2neibors ) return;
6815
6816   for ( size_t i = 0; i < edges.size()-1; ++i )
6817     if ( edges[i]->_2neibors->tgtNode(1) != edges[i+1]->_nodes.back() )
6818       edges[i]->_2neibors->reverse();
6819
6820   const size_t iLast = edges.size() - 1;
6821   if ( edges.size() > 1 &&
6822        edges[iLast]->_2neibors->tgtNode(0) != edges[iLast-1]->_nodes.back() )
6823     edges[iLast]->_2neibors->reverse();
6824 }
6825
6826 //================================================================================
6827 /*!
6828  * \brief Return _EdgesOnShape* corresponding to the shape
6829  */
6830 //================================================================================
6831
6832 _EdgesOnShape* _SolidData::GetShapeEdges(const TGeomID shapeID )
6833 {
6834   if ( shapeID < (int)_edgesOnShape.size() &&
6835        _edgesOnShape[ shapeID ]._shapeID == shapeID )
6836     return _edgesOnShape[ shapeID ]._subMesh ? & _edgesOnShape[ shapeID ] : 0;
6837
6838   for ( size_t i = 0; i < _edgesOnShape.size(); ++i )
6839     if ( _edgesOnShape[i]._shapeID == shapeID )
6840       return _edgesOnShape[i]._subMesh ? & _edgesOnShape[i] : 0;
6841
6842   return 0;
6843 }
6844
6845 //================================================================================
6846 /*!
6847  * \brief Return _EdgesOnShape* corresponding to the shape
6848  */
6849 //================================================================================
6850
6851 _EdgesOnShape* _SolidData::GetShapeEdges(const TopoDS_Shape& shape )
6852 {
6853   SMESHDS_Mesh* meshDS = _proxyMesh->GetMesh()->GetMeshDS();
6854   return GetShapeEdges( meshDS->ShapeToIndex( shape ));
6855 }
6856
6857 //================================================================================
6858 /*!
6859  * \brief Prepare data of the _LayerEdge for smoothing on FACE
6860  */
6861 //================================================================================
6862
6863 void _SolidData::PrepareEdgesToSmoothOnFace( _EdgesOnShape* eos, bool substituteSrcNodes )
6864 {
6865   SMESH_MesherHelper helper( *_proxyMesh->GetMesh() );
6866
6867   set< TGeomID > vertices;
6868   TopoDS_Face F;
6869   if ( eos->ShapeType() == TopAbs_FACE )
6870   {
6871     // check FACE concavity and get concave VERTEXes
6872     F = TopoDS::Face( eos->_shape );
6873     if ( isConcave( F, helper, &vertices ))
6874       _concaveFaces.insert( eos->_shapeID );
6875
6876     // set eos._eosConcaVer
6877     eos->_eosConcaVer.clear();
6878     eos->_eosConcaVer.reserve( vertices.size() );
6879     for ( set< TGeomID >::iterator v = vertices.begin(); v != vertices.end(); ++v )
6880     {
6881       _EdgesOnShape* eov = GetShapeEdges( *v );
6882       if ( eov && eov->_edges.size() == 1 )
6883       {
6884         eos->_eosConcaVer.push_back( eov );
6885         for ( size_t i = 0; i < eov->_edges[0]->_neibors.size(); ++i )
6886           eov->_edges[0]->_neibors[i]->Set( _LayerEdge::DIFFICULT );
6887       }
6888     }
6889
6890     // SetSmooLen() to _LayerEdge's on FACE
6891     // for ( size_t i = 0; i < eos->_edges.size(); ++i )
6892     // {
6893     //   eos->_edges[i]->SetSmooLen( Precision::Infinite() );
6894     // }
6895     // SMESH_subMeshIteratorPtr smIt = eos->_subMesh->getDependsOnIterator(/*includeSelf=*/false);
6896     // while ( smIt->more() ) // loop on sub-shapes of the FACE
6897     // {
6898     //   _EdgesOnShape* eoe = GetShapeEdges( smIt->next()->GetId() );
6899     //   if ( !eoe ) continue;
6900
6901     //   vector<_LayerEdge*>& eE = eoe->_edges;
6902     //   for ( size_t iE = 0; iE < eE.size(); ++iE ) // loop on _LayerEdge's on EDGE or VERTEX
6903     //   {
6904     //     if ( eE[iE]->_cosin <= theMinSmoothCosin )
6905     //       continue;
6906
6907     //     SMDS_ElemIteratorPtr segIt = eE[iE]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Edge);
6908     //     while ( segIt->more() )
6909     //     {
6910     //       const SMDS_MeshElement* seg = segIt->next();
6911     //       if ( !eos->_subMesh->DependsOn( seg->getshapeId() ))
6912     //         continue;
6913     //       if ( seg->GetNode(0) != eE[iE]->_nodes[0] )
6914     //         continue; // not to check a seg twice
6915     //       for ( size_t iN = 0; iN < eE[iE]->_neibors.size(); ++iN )
6916     //       {
6917     //         _LayerEdge* eN = eE[iE]->_neibors[iN];
6918     //         if ( eN->_nodes[0]->getshapeId() != eos->_shapeID )
6919     //           continue;
6920     //         double dist    = SMESH_MeshAlgos::GetDistance( seg, SMESH_TNodeXYZ( eN->_nodes[0] ));
6921     //         double smooLen = getSmoothingThickness( eE[iE]->_cosin, dist );
6922     //         eN->SetSmooLen( Min( smooLen, eN->GetSmooLen() ));
6923     //         eN->Set( _LayerEdge::NEAR_BOUNDARY );
6924     //       }
6925     //     }
6926     //   }
6927     // }
6928   } // if ( eos->ShapeType() == TopAbs_FACE )
6929
6930   for ( size_t i = 0; i < eos->_edges.size(); ++i )
6931   {
6932     eos->_edges[i]->_smooFunction = 0;
6933     eos->_edges[i]->Set( _LayerEdge::TO_SMOOTH );
6934   }
6935   bool isCurved = false;
6936   for ( size_t i = 0; i < eos->_edges.size(); ++i )
6937   {
6938     _LayerEdge* edge = eos->_edges[i];
6939
6940     // get simplices sorted
6941     _Simplex::SortSimplices( edge->_simplices );
6942
6943     // smoothing function
6944     edge->ChooseSmooFunction( vertices, _n2eMap );
6945
6946     // set _curvature
6947     double avgNormProj = 0, avgLen = 0;
6948     for ( size_t iS = 0; iS < edge->_simplices.size(); ++iS )
6949     {
6950       _Simplex& s = edge->_simplices[iS];
6951
6952       gp_XYZ  vec = edge->_pos.back() - SMESH_TNodeXYZ( s._nPrev );
6953       avgNormProj += edge->_normal * vec;
6954       avgLen      += vec.Modulus();
6955       if ( substituteSrcNodes )
6956       {
6957         s._nNext = _n2eMap[ s._nNext ]->_nodes.back();
6958         s._nPrev = _n2eMap[ s._nPrev ]->_nodes.back();
6959       }
6960     }
6961     avgNormProj /= edge->_simplices.size();
6962     avgLen      /= edge->_simplices.size();
6963     if (( edge->_curvature = _Curvature::New( avgNormProj, avgLen )))
6964     {
6965       edge->Set( _LayerEdge::SMOOTHED_C1 );
6966       isCurved = true;
6967       SMDS_FacePositionPtr fPos = edge->_nodes[0]->GetPosition();
6968       if ( !fPos )
6969         for ( size_t iS = 0; iS < edge->_simplices.size()  &&  !fPos; ++iS )
6970           fPos = edge->_simplices[iS]._nPrev->GetPosition();
6971       if ( fPos )
6972         edge->_curvature->_uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
6973     }
6974   }
6975
6976   // prepare for putOnOffsetSurface()
6977   if (( eos->ShapeType() == TopAbs_FACE ) &&
6978       ( isCurved || !eos->_eosConcaVer.empty() ))
6979   {
6980     eos->_offsetSurf = helper.GetSurface( TopoDS::Face( eos->_shape ));
6981     eos->_edgeForOffset = 0;
6982
6983     double maxCosin = -1;
6984     //bool hasNoShrink = false;
6985     for ( TopExp_Explorer eExp( eos->_shape, TopAbs_EDGE ); eExp.More(); eExp.Next() )
6986     {
6987       _EdgesOnShape* eoe = GetShapeEdges( eExp.Current() );
6988       if ( !eoe || eoe->_edges.empty() ) continue;
6989
6990       // if ( eos->GetData()._noShrinkShapes.count( eoe->_shapeID ))
6991       //   hasNoShrink = true;
6992
6993       vector<_LayerEdge*>& eE = eoe->_edges;
6994       _LayerEdge* e = eE[ eE.size() / 2 ];
6995       if ( !e->Is( _LayerEdge::RISKY_SWOL ) && e->_cosin > maxCosin )
6996       {
6997         eos->_edgeForOffset = e;
6998         maxCosin = e->_cosin;
6999       }
7000
7001       if ( !eoe->_sWOL.IsNull() )
7002         for ( _LayerEdge* le : eoe->_edges )
7003           if ( le->Is( _LayerEdge::RISKY_SWOL ) && e->_cosin > 0 )
7004           {
7005             // make _neibors on FACE be smoothed after le->Is( BLOCKED )
7006             for ( _LayerEdge* neibor : le->_neibors )
7007             {
7008               int shapeDim =  neibor->BaseShapeDim();
7009               if ( shapeDim == 2 )
7010                 neibor->Set( _LayerEdge::NEAR_BOUNDARY ); // on FACE
7011               else if ( shapeDim == 0 )
7012                 neibor->Set( _LayerEdge::RISKY_SWOL );    // on VERTEX
7013
7014               if ( !neibor->_curvature )
7015               {
7016                 gp_XY uv = helper.GetNodeUV( F, neibor->_nodes[0] );
7017                 neibor->_curvature = _Factory::NewCurvature();
7018                 neibor->_curvature->_r = 0;
7019                 neibor->_curvature->_k = 0;
7020                 neibor->_curvature->_h2lenRatio = 0;
7021                 neibor->_curvature->_uv = uv;
7022               }
7023             }
7024           }
7025     } // loop on EDGEs
7026
7027     // Try to initialize _Mapper2D
7028
7029     // if ( hasNoShrink )
7030     //   return;
7031
7032     SMDS_ElemIteratorPtr fIt = eos->_subMesh->GetSubMeshDS()->GetElements();
7033     if ( !fIt->more() || fIt->next()->NbCornerNodes() != 4 )
7034       return;
7035
7036     // get EDGEs of quadrangle bottom
7037     std::list< TopoDS_Edge > edges;
7038     std::list< int > nbEdgesInWire;
7039     int nbWire = SMESH_Block::GetOrderedEdges( F, edges, nbEdgesInWire );
7040     if ( nbWire != 1 || nbEdgesInWire.front() < 4 )
7041       return;
7042     const SMDS_MeshNode* node;
7043     while ( true ) // make edges start at a corner VERTEX
7044     {
7045       node = SMESH_Algo::VertexNode( helper.IthVertex( 0, edges.front() ), helper.GetMeshDS() );
7046       if ( node && helper.IsCornerOfStructure( node, eos->_subMesh->GetSubMeshDS(), helper ))
7047         break;
7048       edges.pop_front();
7049       if ( edges.empty() )
7050         return;
7051     }
7052     std::list< TopoDS_Edge >::iterator edgeIt = edges.begin();
7053     while ( true ) // make edges finish at a corner VERTEX
7054     {
7055       node = SMESH_Algo::VertexNode( helper.IthVertex( 1, *edgeIt ), helper.GetMeshDS() );
7056       ++edgeIt;
7057       if ( node && helper.IsCornerOfStructure( node, eos->_subMesh->GetSubMeshDS(), helper ))
7058       {
7059         edges.erase( edgeIt, edges.end() );
7060         break;
7061       }
7062       if ( edgeIt == edges.end() )
7063         return;
7064     }
7065
7066     // get structure of nodes
7067     TParam2ColumnMap param2ColumnMap;
7068     if ( !helper.LoadNodeColumns( param2ColumnMap, F, edges, helper.GetMeshDS() ))
7069       return;
7070
7071     eos->_mapper2D = new _Mapper2D( param2ColumnMap, eos->GetData()._n2eMap );
7072
7073   } // if eos is of curved FACE
7074
7075   return;
7076 }
7077
7078 //================================================================================
7079 /*!
7080  * \brief Add faces for smoothing
7081  */
7082 //================================================================================
7083
7084 void _SolidData::AddShapesToSmooth( const set< _EdgesOnShape* >& eosToSmooth,
7085                                     const set< _EdgesOnShape* >* edgesNoAnaSmooth )
7086 {
7087   set< _EdgesOnShape * >::const_iterator eos = eosToSmooth.begin();
7088   for ( ; eos != eosToSmooth.end(); ++eos )
7089   {
7090     if ( !*eos || (*eos)->_toSmooth ) continue;
7091
7092     (*eos)->_toSmooth = true;
7093
7094     if ( (*eos)->ShapeType() == TopAbs_FACE )
7095     {
7096       PrepareEdgesToSmoothOnFace( *eos, /*substituteSrcNodes=*/false );
7097       (*eos)->_toSmooth = true;
7098     }
7099   }
7100
7101   // avoid _Smoother1D::smoothAnalyticEdge() of edgesNoAnaSmooth
7102   if ( edgesNoAnaSmooth )
7103     for ( eos = edgesNoAnaSmooth->begin(); eos != edgesNoAnaSmooth->end(); ++eos )
7104     {
7105       if ( (*eos)->_edgeSmoother )
7106         (*eos)->_edgeSmoother->_anaCurve.Nullify();
7107     }
7108 }
7109
7110 //================================================================================
7111 /*!
7112  * \brief Limit _LayerEdge::_maxLen according to local curvature
7113  */
7114 //================================================================================
7115
7116 void _ViscousBuilder::limitMaxLenByCurvature( _SolidData& data, SMESH_MesherHelper& /*helper*/ )
7117 {
7118   // find intersection of neighbor _LayerEdge's to limit _maxLen
7119   // according to local curvature (IPAL52648)
7120
7121   // This method must be called after findCollisionEdges() where _LayerEdge's
7122   // get _lenFactor initialized in the case of eos._hyp.IsOffsetMethod()
7123
7124   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7125   {
7126     _EdgesOnShape& eosI = data._edgesOnShape[iS];
7127     if ( eosI._edges.empty() ) continue;
7128     if ( !eosI._hyp.ToSmooth() )
7129     {
7130       for ( size_t i = 0; i < eosI._edges.size(); ++i )
7131       {
7132         _LayerEdge* eI = eosI._edges[i];
7133         for ( size_t iN = 0; iN < eI->_neibors.size(); ++iN )
7134         {
7135           _LayerEdge* eN = eI->_neibors[iN];
7136           if ( eI->_nodes[0]->GetID() < eN->_nodes[0]->GetID() ) // treat this pair once
7137           {
7138             _EdgesOnShape* eosN = data.GetShapeEdges( eN );
7139             limitMaxLenByCurvature( eI, eN, eosI, *eosN, eosI._hyp.ToSmooth() );
7140           }
7141         }
7142       }
7143     }
7144     else if ( eosI.ShapeType() == TopAbs_EDGE )
7145     {
7146       const TopoDS_Edge& E = TopoDS::Edge( eosI._shape );
7147       if ( SMESH_Algo::IsStraight( E, /*degenResult=*/true )) continue;
7148
7149       _LayerEdge* e0 = eosI._edges[0];
7150       for ( size_t i = 1; i < eosI._edges.size(); ++i )
7151       {
7152         _LayerEdge* eI = eosI._edges[i];
7153         limitMaxLenByCurvature( eI, e0, eosI, eosI, eosI._hyp.ToSmooth() );
7154         e0 = eI;
7155       }
7156     }
7157   }
7158 }
7159
7160 //================================================================================
7161 /*!
7162  * \brief Limit _LayerEdge::_maxLen according to local curvature
7163  */
7164 //================================================================================
7165
7166 void _ViscousBuilder::limitMaxLenByCurvature( _LayerEdge*    e1,
7167                                               _LayerEdge*    e2,
7168                                               _EdgesOnShape& /*eos1*/,
7169                                               _EdgesOnShape& /*eos2*/,
7170                                               const bool     /*isSmoothable*/ )
7171 {
7172   if (( e1->_nodes[0]->GetPosition()->GetDim() !=
7173         e2->_nodes[0]->GetPosition()->GetDim() ) &&
7174       ( e1->_cosin < 0.75 ))
7175     return; // angle > 90 deg at e1
7176
7177   gp_XYZ plnNorm = e1->_normal ^ e2->_normal;
7178   double norSize = plnNorm.SquareModulus();
7179   if ( norSize < std::numeric_limits<double>::min() )
7180     return; // parallel normals
7181
7182   // find closest points of skew _LayerEdge's
7183   SMESH_TNodeXYZ src1( e1->_nodes[0] ), src2( e2->_nodes[0] );
7184   gp_XYZ dir12 = src2 - src1;
7185   gp_XYZ perp1 = e1->_normal ^ plnNorm;
7186   gp_XYZ perp2 = e2->_normal ^ plnNorm;
7187   double  dot1 = perp2 * e1->_normal;
7188   double  dot2 = perp1 * e2->_normal;
7189   double    u1 =   ( perp2 * dir12 ) / dot1;
7190   double    u2 = - ( perp1 * dir12 ) / dot2;
7191   if ( u1 > 0 && u2 > 0 )
7192   {
7193     double ovl = ( u1 * e1->_normal * dir12 -
7194                    u2 * e2->_normal * dir12 ) / dir12.SquareModulus();
7195     if ( ovl > theSmoothThickToElemSizeRatio )
7196     {
7197       const double coef = 0.75;
7198       e1->SetMaxLen( Min( e1->_maxLen, coef * u1 / e1->_lenFactor ));
7199       e2->SetMaxLen( Min( e2->_maxLen, coef * u2 / e2->_lenFactor ));
7200     }
7201   }
7202 }
7203
7204 //================================================================================
7205 /*!
7206  * \brief Fill data._collisionEdges
7207  */
7208 //================================================================================
7209
7210 void _ViscousBuilder::findCollisionEdges( _SolidData& data, SMESH_MesherHelper& helper )
7211 {
7212   data._collisionEdges.clear();
7213
7214   // set the full thickness of the layers to LEs
7215   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7216   {
7217     _EdgesOnShape& eos = data._edgesOnShape[iS];
7218     if ( eos._edges.empty() ) continue;
7219     if ( eos.ShapeType() != TopAbs_EDGE && eos.ShapeType() != TopAbs_VERTEX ) continue;
7220     if ( !eos._sWOL.IsNull() ) continue; // PAL23566
7221
7222     for ( size_t i = 0; i < eos._edges.size(); ++i )
7223     {
7224       if ( eos._edges[i]->Is( _LayerEdge::BLOCKED )) continue;
7225       double maxLen = eos._edges[i]->_maxLen;
7226       eos._edges[i]->_maxLen = Precision::Infinite(); // avoid blocking
7227       eos._edges[i]->SetNewLength( 1.5 * maxLen, eos, helper );
7228       eos._edges[i]->_maxLen = maxLen;
7229     }
7230   }
7231
7232   // make temporary quadrangles got by extrusion of
7233   // mesh edges along _LayerEdge._normal's
7234
7235   vector< const SMDS_MeshElement* > tmpFaces;
7236
7237   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7238   {
7239     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
7240     if ( eos.ShapeType() != TopAbs_EDGE )
7241       continue;
7242     if ( eos._edges.empty() )
7243     {
7244       _LayerEdge* edge[2] = { 0, 0 }; // LE of 2 VERTEX'es
7245       SMESH_subMeshIteratorPtr smIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false);
7246       while ( smIt->more() )
7247         if ( _EdgesOnShape* eov = data.GetShapeEdges( smIt->next()->GetId() ))
7248           if ( eov->_edges.size() == 1 )
7249             edge[ bool( edge[0]) ] = eov->_edges[0];
7250
7251       if ( edge[1] )
7252       {
7253         _TmpMeshFaceOnEdge* f = new _TmpMeshFaceOnEdge( edge[0], edge[1], --_tmpFaceID );
7254         tmpFaces.push_back( f );
7255       }
7256     }
7257     for ( size_t i = 0; i < eos._edges.size(); ++i )
7258     {
7259       _LayerEdge* edge = eos._edges[i];
7260       for ( int j = 0; j < 2; ++j ) // loop on _2NearEdges
7261       {
7262         const SMDS_MeshNode* src2 = edge->_2neibors->srcNode(j);
7263         if ( src2->GetPosition()->GetDim() > 0 &&
7264              src2->GetID() < edge->_nodes[0]->GetID() )
7265           continue; // avoid using same segment twice
7266
7267         // a _LayerEdge containing tgt2
7268         _LayerEdge* neiborEdge = edge->_2neibors->_edges[j];
7269
7270         _TmpMeshFaceOnEdge* f = new _TmpMeshFaceOnEdge( edge, neiborEdge, --_tmpFaceID );
7271         tmpFaces.push_back( f );
7272       }
7273     }
7274   }
7275
7276   // Find _LayerEdge's intersecting tmpFaces.
7277
7278   SMDS_ElemIteratorPtr fIt( new SMDS_ElementVectorIterator( tmpFaces.begin(),
7279                                                             tmpFaces.end()));
7280   SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
7281     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(), fIt ));
7282
7283   double dist1, dist2, segLen, eps = 0.5;
7284   _CollisionEdges collEdges;
7285   vector< const SMDS_MeshElement* > suspectFaces;
7286   const double angle45 = Cos( 45. * M_PI / 180. );
7287
7288   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7289   {
7290     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
7291     if ( eos.ShapeType() == TopAbs_FACE || !eos._sWOL.IsNull() )
7292       continue;
7293     // find sub-shapes whose VL can influence VL on eos
7294     set< TGeomID > neighborShapes;
7295     PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
7296     while ( const TopoDS_Shape* face = fIt->next() )
7297     {
7298       TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
7299       if ( _EdgesOnShape* eof = data.GetShapeEdges( faceID ))
7300       {
7301         SMESH_subMeshIteratorPtr subIt = eof->_subMesh->getDependsOnIterator(/*includeSelf=*/false);
7302         while ( subIt->more() )
7303           neighborShapes.insert( subIt->next()->GetId() );
7304       }
7305     }
7306     if ( eos.ShapeType() == TopAbs_VERTEX )
7307     {
7308       PShapeIteratorPtr eIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_EDGE );
7309       while ( const TopoDS_Shape* edge = eIt->next() )
7310         neighborShapes.erase( getMeshDS()->ShapeToIndex( *edge ));
7311     }
7312     // find intersecting _LayerEdge's
7313     for ( size_t i = 0; i < eos._edges.size(); ++i )
7314     {
7315       if ( eos._edges[i]->Is( _LayerEdge::MULTI_NORMAL )) continue;
7316       _LayerEdge*   edge = eos._edges[i];
7317       gp_Ax1 lastSegment = edge->LastSegment( segLen, eos );
7318       segLen *= 1.2;
7319
7320       gp_Vec eSegDir0, eSegDir1;
7321       if ( edge->IsOnEdge() )
7322       {
7323         SMESH_TNodeXYZ eP( edge->_nodes[0] );
7324         eSegDir0 = SMESH_TNodeXYZ( edge->_2neibors->srcNode(0) ) - eP;
7325         eSegDir1 = SMESH_TNodeXYZ( edge->_2neibors->srcNode(1) ) - eP;
7326       }
7327       suspectFaces.clear();
7328       searcher->GetElementsInSphere( SMESH_TNodeXYZ( edge->_nodes.back()), edge->_len * 2,
7329                                      SMDSAbs_Face, suspectFaces );
7330       collEdges._intEdges.clear();
7331       for ( size_t j = 0 ; j < suspectFaces.size(); ++j )
7332       {
7333         const _TmpMeshFaceOnEdge* f = (const _TmpMeshFaceOnEdge*) suspectFaces[j];
7334         if ( f->_le1 == edge || f->_le2 == edge ) continue;
7335         if ( !neighborShapes.count( f->_le1->_nodes[0]->getshapeId() )) continue;
7336         if ( !neighborShapes.count( f->_le2->_nodes[0]->getshapeId() )) continue;
7337         if ( edge->IsOnEdge() ) {
7338           if ( edge->_2neibors->include( f->_le1 ) ||
7339                edge->_2neibors->include( f->_le2 )) continue;
7340         }
7341         else {
7342           if (( f->_le1->IsOnEdge() && f->_le1->_2neibors->include( edge )) ||
7343               ( f->_le2->IsOnEdge() && f->_le2->_2neibors->include( edge )))  continue;
7344         }
7345         dist1 = dist2 = Precision::Infinite();
7346         if ( !edge->SegTriaInter( lastSegment, f->n(0), f->n(1), f->n(2), dist1, eps ))
7347           dist1 = Precision::Infinite();
7348         if ( !edge->SegTriaInter( lastSegment, f->n(3), f->n(2), f->n(0), dist2, eps ))
7349           dist2 = Precision::Infinite();
7350         if (( dist1 > segLen ) && ( dist2 > segLen ))
7351           continue;
7352
7353         if ( edge->IsOnEdge() )
7354         {
7355           // skip perpendicular EDGEs
7356           gp_Vec fSegDir  = SMESH_TNodeXYZ( f->n(0) ) - SMESH_TNodeXYZ( f->n(3) );
7357           bool isParallel = ( isLessAngle( eSegDir0, fSegDir, angle45 ) ||
7358                               isLessAngle( eSegDir1, fSegDir, angle45 ) ||
7359                               isLessAngle( eSegDir0, fSegDir.Reversed(), angle45 ) ||
7360                               isLessAngle( eSegDir1, fSegDir.Reversed(), angle45 ));
7361           if ( !isParallel )
7362             continue;
7363         }
7364
7365         // either limit inflation of edges or remember them for updating _normal
7366         // double dot = edge->_normal * f->GetDir();
7367         // if ( dot > 0.1 )
7368         {
7369           collEdges._intEdges.push_back( f->_le1 );
7370           collEdges._intEdges.push_back( f->_le2 );
7371         }
7372         // else
7373         // {
7374         //   double shortLen = 0.75 * ( Min( dist1, dist2 ) / edge->_lenFactor );
7375         //   edge->SetMaxLen( Min( shortLen, edge->_maxLen ));
7376         // }
7377       }
7378
7379       if ( !collEdges._intEdges.empty() )
7380       {
7381         collEdges._edge = edge;
7382         data._collisionEdges.push_back( collEdges );
7383       }
7384     }
7385   }
7386
7387   for ( size_t i = 0 ; i < tmpFaces.size(); ++i )
7388     delete tmpFaces[i];
7389
7390   // restore the zero thickness
7391   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7392   {
7393     _EdgesOnShape& eos = data._edgesOnShape[iS];
7394     if ( eos._edges.empty() ) continue;
7395     if ( eos.ShapeType() != TopAbs_EDGE && eos.ShapeType() != TopAbs_VERTEX ) continue;
7396
7397     for ( size_t i = 0; i < eos._edges.size(); ++i )
7398     {
7399       eos._edges[i]->InvalidateStep( 1, eos );
7400       eos._edges[i]->_len = 0;
7401     }
7402   }
7403 }
7404
7405 //================================================================================
7406 /*!
7407  * \brief Find _LayerEdge's located on boundary of a convex FACE whose normal
7408  *        will be updated at each inflation step
7409  */
7410 //================================================================================
7411
7412 void _ViscousBuilder::findEdgesToUpdateNormalNearConvexFace( _ConvexFace &       convFace,
7413                                                              _SolidData&         data,
7414                                                              SMESH_MesherHelper& helper )
7415 {
7416   const TGeomID convFaceID = getMeshDS()->ShapeToIndex( convFace._face );
7417   const double       preci = BRep_Tool::Tolerance( convFace._face );
7418   Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( convFace._face );
7419
7420   bool edgesToUpdateFound = false;
7421
7422   map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.begin();
7423   for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7424   {
7425     _EdgesOnShape& eos = * id2eos->second;
7426     if ( !eos._sWOL.IsNull() ) continue;
7427     if ( !eos._hyp.ToSmooth() ) continue;
7428     for ( size_t i = 0; i < eos._edges.size(); ++i )
7429     {
7430       _LayerEdge* ledge = eos._edges[ i ];
7431       if ( ledge->Is( _LayerEdge::UPD_NORMAL_CONV )) continue; // already checked
7432       if ( ledge->Is( _LayerEdge::MULTI_NORMAL )) continue; // not inflatable
7433
7434       gp_XYZ tgtPos = ( SMESH_NodeXYZ( ledge->_nodes[0] ) +
7435                         ledge->_normal * ledge->_lenFactor * ledge->_maxLen );
7436
7437       // the normal must be updated if distance from tgtPos to surface is less than
7438       // target thickness
7439
7440       // find an initial UV for search of a projection of tgtPos to surface
7441       const SMDS_MeshNode* nodeInFace = 0;
7442       SMDS_ElemIteratorPtr fIt = ledge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
7443       while ( fIt->more() && !nodeInFace )
7444       {
7445         const SMDS_MeshElement* f = fIt->next();
7446         if ( convFaceID != f->getshapeId() ) continue;
7447
7448         SMDS_ElemIteratorPtr nIt = f->nodesIterator();
7449         while ( nIt->more() && !nodeInFace )
7450         {
7451           const SMDS_MeshElement* n = nIt->next();
7452           if ( n->getshapeId() == convFaceID )
7453             nodeInFace = static_cast< const SMDS_MeshNode* >( n );
7454         }
7455       }
7456       if ( !nodeInFace )
7457         continue;
7458       gp_XY uv = helper.GetNodeUV( convFace._face, nodeInFace );
7459
7460       // projection
7461       surface->NextValueOfUV( uv, tgtPos, preci );
7462       double  dist = surface->Gap();
7463       if ( dist < 0.95 * ledge->_maxLen )
7464       {
7465         ledge->Set( _LayerEdge::UPD_NORMAL_CONV );
7466         if ( !ledge->_curvature ) ledge->_curvature = _Factory::NewCurvature();
7467         ledge->_curvature->_uv.SetCoord( uv.X(), uv.Y() );
7468         edgesToUpdateFound = true;
7469       }
7470     }
7471   }
7472
7473   if ( !convFace._isTooCurved && edgesToUpdateFound )
7474   {
7475     data._convexFaces.insert( make_pair( convFaceID, convFace )).first->second;
7476   }
7477 }
7478
7479 //================================================================================
7480 /*!
7481  * \brief Modify normals of _LayerEdge's on EDGE's to avoid intersection with
7482  * _LayerEdge's on neighbor EDGE's
7483  */
7484 //================================================================================
7485
7486 bool _ViscousBuilder::updateNormals( _SolidData&         data,
7487                                      SMESH_MesherHelper& helper,
7488                                      int                 stepNb,
7489                                      double              /*stepSize*/)
7490 {
7491   updateNormalsOfC1Vertices( data );
7492
7493   if ( stepNb > 0 && !updateNormalsOfConvexFaces( data, helper, stepNb ))
7494     return false;
7495
7496   // map to store new _normal and _cosin for each intersected edge
7497   map< _LayerEdge*, _LayerEdge, _LayerEdgeCmp >           edge2newEdge;
7498   map< _LayerEdge*, _LayerEdge, _LayerEdgeCmp >::iterator e2neIt;
7499   _LayerEdge zeroEdge;
7500   zeroEdge._normal.SetCoord( 0,0,0 );
7501   zeroEdge._maxLen = Precision::Infinite();
7502   zeroEdge._nodes.resize(1); // to init _TmpMeshFaceOnEdge
7503
7504   set< _EdgesOnShape* > shapesToSmooth, edgesNoAnaSmooth;
7505
7506   double segLen, dist1, dist2, dist;
7507   vector< pair< _LayerEdge*, double > > intEdgesDist;
7508   _TmpMeshFaceOnEdge quad( &zeroEdge, &zeroEdge, 0 );
7509
7510   for ( int iter = 0; iter < 5; ++iter )
7511   {
7512     edge2newEdge.clear();
7513
7514     for ( size_t iE = 0; iE < data._collisionEdges.size(); ++iE )
7515     {
7516       _CollisionEdges& ce = data._collisionEdges[iE];
7517       _LayerEdge*   edge1 = ce._edge;
7518       if ( !edge1 /*|| edge1->Is( _LayerEdge::BLOCKED )*/) continue;
7519       _EdgesOnShape* eos1 = data.GetShapeEdges( edge1 );
7520       if ( !eos1 ) continue;
7521
7522       // detect intersections
7523       gp_Ax1 lastSeg = edge1->LastSegment( segLen, *eos1 );
7524       double testLen = 1.5 * edge1->_maxLen * edge1->_lenFactor;
7525       double     eps = 0.5;
7526       intEdgesDist.clear();
7527       double minIntDist = Precision::Infinite();
7528       for ( size_t i = 0; i < ce._intEdges.size(); i += 2 )
7529       {
7530         if ( edge1->Is( _LayerEdge::BLOCKED ) &&
7531              ce._intEdges[i  ]->Is( _LayerEdge::BLOCKED ) &&
7532              ce._intEdges[i+1]->Is( _LayerEdge::BLOCKED ))
7533           continue;
7534         double dot  = edge1->_normal * quad.GetDir( ce._intEdges[i], ce._intEdges[i+1] );
7535         double fact = ( 1.1 + dot * dot );
7536         SMESH_TNodeXYZ pSrc0( ce.nSrc(i) ), pSrc1( ce.nSrc(i+1) );
7537         SMESH_TNodeXYZ pTgt0( ce.nTgt(i) ), pTgt1( ce.nTgt(i+1) );
7538         gp_XYZ pLast0 = pSrc0 + ( pTgt0 - pSrc0 ) * fact;
7539         gp_XYZ pLast1 = pSrc1 + ( pTgt1 - pSrc1 ) * fact;
7540         dist1 = dist2 = Precision::Infinite();
7541         if ( !edge1->SegTriaInter( lastSeg, pSrc0, pLast0, pSrc1,  dist1, eps ) &&
7542              !edge1->SegTriaInter( lastSeg, pSrc1, pLast1, pLast0, dist2, eps ))
7543           continue;
7544         dist = dist1;
7545         if ( dist > testLen || dist <= 0 )
7546         {
7547           dist = dist2;
7548           if ( dist > testLen || dist <= 0 )
7549             continue;
7550         }
7551         // choose a closest edge
7552         gp_Pnt intP( lastSeg.Location().XYZ() + lastSeg.Direction().XYZ() * ( dist + segLen ));
7553         double d1 = intP.SquareDistance( pSrc0 );
7554         double d2 = intP.SquareDistance( pSrc1 );
7555         int iClose = i + ( d2 < d1 );
7556         _LayerEdge* edge2 = ce._intEdges[iClose];
7557         edge2->Unset( _LayerEdge::MARKED );
7558
7559         // choose a closest edge among neighbors
7560         gp_Pnt srcP( SMESH_TNodeXYZ( edge1->_nodes[0] ));
7561         d1 = srcP.SquareDistance( SMESH_TNodeXYZ( edge2->_nodes[0] ));
7562         for ( size_t j = 0; j < intEdgesDist.size(); ++j )
7563         {
7564           _LayerEdge * edgeJ = intEdgesDist[j].first;
7565           if ( edge2->IsNeiborOnEdge( edgeJ ))
7566           {
7567             d2 = srcP.SquareDistance( SMESH_TNodeXYZ( edgeJ->_nodes[0] ));
7568             ( d1 < d2 ? edgeJ : edge2 )->Set( _LayerEdge::MARKED );
7569           }
7570         }
7571         intEdgesDist.push_back( make_pair( edge2, dist ));
7572         // if ( Abs( d2 - d1 ) / Max( d2, d1 ) < 0.5 )
7573         // {
7574         //   iClose = i + !( d2 < d1 );
7575         //   intEdges.push_back( ce._intEdges[iClose] );
7576         //   ce._intEdges[iClose]->Unset( _LayerEdge::MARKED );
7577         // }
7578         minIntDist = Min( edge1->_len * edge1->_lenFactor - segLen + dist, minIntDist );
7579       }
7580
7581       //ce._edge = 0;
7582
7583       // compute new _normals
7584       for ( size_t i = 0; i < intEdgesDist.size(); ++i )
7585       {
7586         _LayerEdge* edge2   = intEdgesDist[i].first;
7587         double      distWgt = edge1->_len / intEdgesDist[i].second;
7588         // if ( edge1->Is( _LayerEdge::BLOCKED ) &&
7589         //      edge2->Is( _LayerEdge::BLOCKED )) continue;        
7590         if ( edge2->Is( _LayerEdge::MARKED )) continue;
7591         edge2->Set( _LayerEdge::MARKED );
7592
7593         // get a new normal
7594         gp_XYZ dir1 = edge1->_normal, dir2 = edge2->_normal;
7595
7596         double cos1 = Abs( edge1->_cosin ), cos2 = Abs( edge2->_cosin );
7597         double wgt1 = ( cos1 + 0.001 ) / ( cos1 + cos2 + 0.002 );
7598         double wgt2 = ( cos2 + 0.001 ) / ( cos1 + cos2 + 0.002 );
7599         // double cos1 = Abs( edge1->_cosin ),        cos2 = Abs( edge2->_cosin );
7600         // double sgn1 = 0.1 * ( 1 + edge1->_cosin ), sgn2 = 0.1 * ( 1 + edge2->_cosin );
7601         // double wgt1 = ( cos1 + sgn1 ) / ( cos1 + cos2 + sgn1 + sgn2 );
7602         // double wgt2 = ( cos2 + sgn2 ) / ( cos1 + cos2 + sgn1 + sgn2 );
7603         gp_XYZ newNormal = wgt1 * dir1 + wgt2 * dir2;
7604         newNormal.Normalize();
7605
7606         // get new cosin
7607         double newCos;
7608         double sgn1 = edge1->_cosin / cos1, sgn2 = edge2->_cosin / cos2;
7609         if ( cos1 < theMinSmoothCosin )
7610         {
7611           newCos = cos2 * sgn1;
7612         }
7613         else if ( cos2 > theMinSmoothCosin ) // both cos1 and cos2 > theMinSmoothCosin
7614         {
7615           newCos = ( wgt1 * cos1 + wgt2 * cos2 ) * edge1->_cosin / cos1;
7616         }
7617         else
7618         {
7619           newCos = edge1->_cosin;
7620         }
7621
7622         e2neIt = edge2newEdge.insert( make_pair( edge1, zeroEdge )).first;
7623         e2neIt->second._normal += distWgt * newNormal;
7624         e2neIt->second._cosin   = newCos;
7625         e2neIt->second.SetMaxLen( 0.7 * minIntDist / edge1->_lenFactor );
7626         if ( iter > 0 && sgn1 * sgn2 < 0 && edge1->_cosin < 0 )
7627           e2neIt->second._normal += dir2;
7628
7629         e2neIt = edge2newEdge.insert( make_pair( edge2, zeroEdge )).first;
7630         e2neIt->second._normal += distWgt * newNormal;
7631         if ( Precision::IsInfinite( zeroEdge._maxLen ))
7632         {
7633           e2neIt->second._cosin  = edge2->_cosin;
7634           e2neIt->second.SetMaxLen( 1.3 * minIntDist / edge1->_lenFactor );
7635         }
7636         if ( iter > 0 && sgn1 * sgn2 < 0 && edge2->_cosin < 0 )
7637           e2neIt->second._normal += dir1;
7638       }
7639     }
7640
7641     if ( edge2newEdge.empty() )
7642       break; //return true;
7643
7644     dumpFunction(SMESH_Comment("updateNormals")<< data._index << "_" << stepNb << "_it" << iter);
7645
7646     // Update data of edges depending on a new _normal
7647
7648     data.UnmarkEdges();
7649     for ( e2neIt = edge2newEdge.begin(); e2neIt != edge2newEdge.end(); ++e2neIt )
7650     {
7651       _LayerEdge*    edge = e2neIt->first;
7652       _LayerEdge& newEdge = e2neIt->second;
7653       _EdgesOnShape*  eos = data.GetShapeEdges( edge );
7654       if ( edge->Is( _LayerEdge::BLOCKED ) && newEdge._maxLen > edge->_len )
7655         continue;
7656
7657       // Check if a new _normal is OK:
7658       newEdge._normal.Normalize();
7659       if ( !isNewNormalOk( data, *edge, newEdge._normal ))
7660       {
7661         if ( newEdge._maxLen < edge->_len && iter > 0 ) // limit _maxLen
7662         {
7663           edge->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true  );
7664           edge->SetMaxLen( newEdge._maxLen );
7665           edge->SetNewLength( newEdge._maxLen, *eos, helper );
7666         }
7667         continue; // the new _normal is bad
7668       }
7669       // the new _normal is OK
7670
7671       // find shapes that need smoothing due to change of _normal
7672       if ( edge->_cosin   < theMinSmoothCosin &&
7673            newEdge._cosin > theMinSmoothCosin )
7674       {
7675         if ( eos->_sWOL.IsNull() )
7676         {
7677           SMDS_ElemIteratorPtr fIt = edge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
7678           while ( fIt->more() )
7679             shapesToSmooth.insert( data.GetShapeEdges( fIt->next()->getshapeId() ));
7680         }
7681         else // edge inflates along a FACE
7682         {
7683           TopoDS_Shape V = helper.GetSubShapeByNode( edge->_nodes[0], getMeshDS() );
7684           PShapeIteratorPtr eIt = helper.GetAncestors( V, *_mesh, TopAbs_EDGE, &eos->_sWOL );
7685           while ( const TopoDS_Shape* E = eIt->next() )
7686           {
7687             gp_Vec edgeDir = getEdgeDir( TopoDS::Edge( *E ), TopoDS::Vertex( V ),
7688                                          eos->_hyp.Get1stLayerThickness() );
7689             double   angle = edgeDir.Angle( newEdge._normal ); // [0,PI]
7690             if ( angle < M_PI / 2 )
7691               shapesToSmooth.insert( data.GetShapeEdges( *E ));
7692           }
7693         }
7694       }
7695
7696       double len = edge->_len;
7697       edge->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true  );
7698       edge->SetNormal( newEdge._normal );
7699       edge->SetCosin( newEdge._cosin );
7700       edge->SetNewLength( len, *eos, helper );
7701       edge->Set( _LayerEdge::MARKED );
7702       edge->Set( _LayerEdge::NORMAL_UPDATED );
7703       edgesNoAnaSmooth.insert( eos );
7704     }
7705
7706     // Update normals and other dependent data of not intersecting _LayerEdge's
7707     // neighboring the intersecting ones
7708
7709     for ( e2neIt = edge2newEdge.begin(); e2neIt != edge2newEdge.end(); ++e2neIt )
7710     {
7711       _LayerEdge*   edge1 = e2neIt->first;
7712       _EdgesOnShape* eos1 = data.GetShapeEdges( edge1 );
7713       if ( !edge1->Is( _LayerEdge::MARKED ))
7714         continue;
7715
7716       if ( edge1->IsOnEdge() )
7717       {
7718         const SMDS_MeshNode * n1 = edge1->_2neibors->srcNode(0);
7719         const SMDS_MeshNode * n2 = edge1->_2neibors->srcNode(1);
7720         edge1->SetDataByNeighbors( n1, n2, *eos1, helper );
7721       }
7722
7723       if ( !edge1->_2neibors || !eos1->_sWOL.IsNull() )
7724         continue;
7725       for ( int j = 0; j < 2; ++j ) // loop on 2 neighbors
7726       {
7727         _LayerEdge* neighbor = edge1->_2neibors->_edges[j];
7728         if ( neighbor->Is( _LayerEdge::MARKED ) /*edge2newEdge.count ( neighbor )*/)
7729           continue; // j-th neighbor is also intersected
7730         _LayerEdge* prevEdge = edge1;
7731         const int nbSteps = 10;
7732         for ( int step = nbSteps; step; --step ) // step from edge1 in j-th direction
7733         {
7734           if ( neighbor->Is( _LayerEdge::BLOCKED ) ||
7735                neighbor->Is( _LayerEdge::MARKED ))
7736             break;
7737           _EdgesOnShape* eos = data.GetShapeEdges( neighbor );
7738           if ( !eos ) continue;
7739           _LayerEdge* nextEdge = neighbor;
7740           if ( neighbor->_2neibors )
7741           {
7742             int iNext = 0;
7743             nextEdge = neighbor->_2neibors->_edges[iNext];
7744             if ( nextEdge == prevEdge )
7745               nextEdge = neighbor->_2neibors->_edges[ ++iNext ];
7746           }
7747           double r = double(step-1)/nbSteps/(iter+1);
7748           if ( !nextEdge->_2neibors )
7749             r = Min( r, 0.5 );
7750
7751           gp_XYZ newNorm = prevEdge->_normal * r + nextEdge->_normal * (1-r);
7752           newNorm.Normalize();
7753           if ( !isNewNormalOk( data, *neighbor, newNorm ))
7754             break;
7755
7756           double len = neighbor->_len;
7757           neighbor->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true  );
7758           neighbor->SetNormal( newNorm );
7759           neighbor->SetCosin( prevEdge->_cosin * r + nextEdge->_cosin * (1-r) );
7760           if ( neighbor->_2neibors )
7761             neighbor->SetDataByNeighbors( prevEdge->_nodes[0], nextEdge->_nodes[0], *eos, helper );
7762           neighbor->SetNewLength( len, *eos, helper );
7763           neighbor->Set( _LayerEdge::MARKED );
7764           neighbor->Set( _LayerEdge::NORMAL_UPDATED );
7765           edgesNoAnaSmooth.insert( eos );
7766
7767           if ( !neighbor->_2neibors )
7768             break; // neighbor is on VERTEX
7769
7770           // goto the next neighbor
7771           prevEdge = neighbor;
7772           neighbor = nextEdge;
7773         }
7774       }
7775     }
7776     dumpFunctionEnd();
7777   } // iterations
7778
7779   data.AddShapesToSmooth( shapesToSmooth, &edgesNoAnaSmooth );
7780
7781   return true;
7782 }
7783
7784 //================================================================================
7785 /*!
7786  * \brief Check if a new normal is OK
7787  */
7788 //================================================================================
7789
7790 bool _ViscousBuilder::isNewNormalOk( _SolidData&   data,
7791                                      _LayerEdge&   edge,
7792                                      const gp_XYZ& newNormal)
7793 {
7794   // check a min angle between the newNormal and surrounding faces
7795   vector<_Simplex> simplices;
7796   SMESH_TNodeXYZ n0( edge._nodes[0] ), n1, n2;
7797   _Simplex::GetSimplices( n0._node, simplices, data._ignoreFaceIds, &data );
7798   double newMinDot = 1, curMinDot = 1;
7799   for ( size_t i = 0; i < simplices.size(); ++i )
7800   {
7801     n1.Set( simplices[i]._nPrev );
7802     n2.Set( simplices[i]._nNext );
7803     gp_XYZ normFace = ( n1 - n0 ) ^ ( n2 - n0 );
7804     double normLen2 = normFace.SquareModulus();
7805     if ( normLen2 < std::numeric_limits<double>::min() )
7806       continue;
7807     normFace /= Sqrt( normLen2 );
7808     newMinDot = Min( newNormal    * normFace, newMinDot );
7809     curMinDot = Min( edge._normal * normFace, curMinDot );
7810   }
7811   bool ok = true;
7812   if ( newMinDot < 0.5 )
7813   {
7814     ok = ( newMinDot >= curMinDot * 0.9 );
7815     //return ( newMinDot >= ( curMinDot * ( 0.8 + 0.1 * edge.NbSteps() )));
7816     // double initMinDot2 = 1. - edge._cosin * edge._cosin;
7817     // return ( newMinDot * newMinDot ) >= ( 0.8 * initMinDot2 );
7818   }
7819
7820   return ok;
7821 }
7822
7823 //================================================================================
7824 /*!
7825  * \brief Modify normals of _LayerEdge's on FACE to reflex smoothing
7826  */
7827 //================================================================================
7828
7829 bool _ViscousBuilder::updateNormalsOfSmoothed( _SolidData&         data,
7830                                                SMESH_MesherHelper& /*helper*/,
7831                                                const int           nbSteps,
7832                                                const double        stepSize )
7833 {
7834   if ( data._nbShapesToSmooth == 0 || nbSteps == 0 )
7835     return true; // no shapes needing smoothing
7836
7837   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7838   {
7839     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
7840     if ( //!eos._toSmooth ||  _eosC1 have _toSmooth == false
7841          !eos._hyp.ToSmooth() ||
7842          eos.ShapeType() != TopAbs_FACE ||
7843          eos._edges.empty() )
7844       continue;
7845
7846     bool toSmooth = ( eos._edges[ 0 ]->NbSteps() >= nbSteps+1 );
7847     if ( !toSmooth ) continue;
7848
7849     for ( size_t i = 0; i < eos._edges.size(); ++i )
7850     {
7851       _LayerEdge* edge = eos._edges[i];
7852       if ( !edge->Is( _LayerEdge::SMOOTHED ))
7853         continue;
7854       if ( edge->Is( _LayerEdge::DIFFICULT ) && nbSteps != 1 )
7855         continue;
7856
7857       const gp_XYZ& pPrev = edge->PrevPos();
7858       const gp_XYZ& pLast = edge->_pos.back();
7859       gp_XYZ      stepVec = pLast - pPrev;
7860       double realStepSize = stepVec.Modulus();
7861       if ( realStepSize < numeric_limits<double>::min() )
7862         continue;
7863
7864       edge->_lenFactor = realStepSize / stepSize;
7865       edge->_normal    = stepVec / realStepSize;
7866       edge->Set( _LayerEdge::NORMAL_UPDATED );
7867     }
7868   }
7869
7870   return true;
7871 }
7872
7873 //================================================================================
7874 /*!
7875  * \brief Modify normals of _LayerEdge's on C1 VERTEXes
7876  */
7877 //================================================================================
7878
7879 void _ViscousBuilder::updateNormalsOfC1Vertices( _SolidData& data )
7880 {
7881   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7882   {
7883     _EdgesOnShape& eov = data._edgesOnShape[ iS ];
7884     if ( eov._eosC1.empty() ||
7885          eov.ShapeType() != TopAbs_VERTEX ||
7886          eov._edges.empty() )
7887       continue;
7888
7889     gp_XYZ newNorm   = eov._edges[0]->_normal;
7890     double curThick  = eov._edges[0]->_len * eov._edges[0]->_lenFactor;
7891     bool normChanged = false;
7892
7893     for ( size_t i = 0; i < eov._eosC1.size(); ++i )
7894     {
7895       _EdgesOnShape*   eoe = eov._eosC1[i];
7896       const TopoDS_Edge& e = TopoDS::Edge( eoe->_shape );
7897       const double    eLen = SMESH_Algo::EdgeLength( e );
7898       TopoDS_Shape    oppV = SMESH_MesherHelper::IthVertex( 0, e );
7899       if ( oppV.IsSame( eov._shape ))
7900         oppV = SMESH_MesherHelper::IthVertex( 1, e );
7901       _EdgesOnShape* eovOpp = data.GetShapeEdges( oppV );
7902       if ( !eovOpp || eovOpp->_edges.empty() ) continue;
7903       if ( eov._edges[0]->Is( _LayerEdge::BLOCKED )) continue;
7904
7905       double curThickOpp = eovOpp->_edges[0]->_len * eovOpp->_edges[0]->_lenFactor;
7906       if ( curThickOpp + curThick < eLen )
7907         continue;
7908
7909       double wgt = 2. * curThick / eLen;
7910       newNorm += wgt * eovOpp->_edges[0]->_normal;
7911       normChanged = true;
7912     }
7913     if ( normChanged )
7914     {
7915       eov._edges[0]->SetNormal( newNorm.Normalized() );
7916       eov._edges[0]->Set( _LayerEdge::NORMAL_UPDATED );
7917     }
7918   }
7919 }
7920
7921 //================================================================================
7922 /*!
7923  * \brief Modify normals of _LayerEdge's on _ConvexFace's
7924  */
7925 //================================================================================
7926
7927 bool _ViscousBuilder::updateNormalsOfConvexFaces( _SolidData&         data,
7928                                                   SMESH_MesherHelper& helper,
7929                                                   int                 stepNb )
7930 {
7931   SMESHDS_Mesh* meshDS = helper.GetMeshDS();
7932   bool isOK;
7933
7934   map< TGeomID, _ConvexFace >::iterator id2face = data._convexFaces.begin();
7935   for ( ; id2face != data._convexFaces.end(); ++id2face )
7936   {
7937     _ConvexFace & convFace = (*id2face).second;
7938     convFace._normalsFixedOnBorders = false; // to update at each inflation step
7939
7940     if ( convFace._normalsFixed )
7941       continue; // already fixed
7942     if ( convFace.CheckPrisms() )
7943       continue; // nothing to fix
7944
7945     convFace._normalsFixed = true;
7946
7947     BRepAdaptor_Surface surface ( convFace._face, false );
7948     BRepLProp_SLProps   surfProp( surface, 2, 1e-6 );
7949
7950     // check if the convex FACE is of spherical shape
7951
7952     Bnd_B3d centersBox; // bbox of centers of curvature of _LayerEdge's on VERTEXes
7953     Bnd_B3d nodesBox;
7954     gp_Pnt  center;
7955
7956     map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.begin();
7957     for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7958     {
7959       _EdgesOnShape& eos = *(id2eos->second);
7960       if ( eos.ShapeType() == TopAbs_VERTEX )
7961       {
7962         _LayerEdge* ledge = eos._edges[ 0 ];
7963         if ( convFace.GetCenterOfCurvature( ledge, surfProp, helper, center ))
7964           centersBox.Add( center );
7965       }
7966       for ( size_t i = 0; i < eos._edges.size(); ++i )
7967         nodesBox.Add( SMESH_TNodeXYZ( eos._edges[ i ]->_nodes[0] ));
7968     }
7969     if ( centersBox.IsVoid() )
7970     {
7971       debugMsg( "Error: centersBox.IsVoid()" );
7972       return false;
7973     }
7974     const bool isSpherical =
7975       ( centersBox.SquareExtent() < 1e-6 * nodesBox.SquareExtent() );
7976
7977     int nbEdges = helper.Count( convFace._face, TopAbs_EDGE, /*ignoreSame=*/false );
7978     vector < _CentralCurveOnEdge > centerCurves( nbEdges );
7979
7980     if ( isSpherical )
7981     {
7982       // set _LayerEdge::_normal as average of all normals
7983
7984       // WARNING: different density of nodes on EDGEs is not taken into account that
7985       // can lead to an improper new normal
7986
7987       gp_XYZ avgNormal( 0,0,0 );
7988       nbEdges = 0;
7989       id2eos = convFace._subIdToEOS.begin();
7990       for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7991       {
7992         _EdgesOnShape& eos = *(id2eos->second);
7993         // set data of _CentralCurveOnEdge
7994         if ( eos.ShapeType() == TopAbs_EDGE )
7995         {
7996           _CentralCurveOnEdge& ceCurve = centerCurves[ nbEdges++ ];
7997           ceCurve.SetShapes( TopoDS::Edge( eos._shape ), convFace, data, helper );
7998           if ( !eos._sWOL.IsNull() )
7999             ceCurve._adjFace.Nullify();
8000           else
8001             ceCurve._ledges.insert( ceCurve._ledges.end(),
8002                                     eos._edges.begin(), eos._edges.end());
8003         }
8004         // summarize normals
8005         for ( size_t i = 0; i < eos._edges.size(); ++i )
8006           avgNormal += eos._edges[ i ]->_normal;
8007       }
8008       double normSize = avgNormal.SquareModulus();
8009       if ( normSize < 1e-200 )
8010       {
8011         debugMsg( "updateNormalsOfConvexFaces(): zero avgNormal" );
8012         return false;
8013       }
8014       avgNormal /= Sqrt( normSize );
8015
8016       // compute new _LayerEdge::_cosin on EDGEs
8017       double avgCosin = 0;
8018       int     nbCosin = 0;
8019       gp_Vec inFaceDir;
8020       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
8021       {
8022         _CentralCurveOnEdge& ceCurve = centerCurves[ iE ];
8023         if ( ceCurve._adjFace.IsNull() )
8024           continue;
8025         for ( size_t iLE = 0; iLE < ceCurve._ledges.size(); ++iLE )
8026         {
8027           const SMDS_MeshNode* node = ceCurve._ledges[ iLE ]->_nodes[0];
8028           inFaceDir = getFaceDir( ceCurve._adjFace, ceCurve._edge, node, helper, isOK );
8029           if ( isOK )
8030           {
8031             double angle = inFaceDir.Angle( avgNormal ); // [0,PI]
8032             ceCurve._ledges[ iLE ]->_cosin = Cos( angle );
8033             avgCosin += ceCurve._ledges[ iLE ]->_cosin;
8034             nbCosin++;
8035           }
8036         }
8037       }
8038       if ( nbCosin > 0 )
8039         avgCosin /= nbCosin;
8040
8041       // set _LayerEdge::_normal = avgNormal
8042       id2eos = convFace._subIdToEOS.begin();
8043       for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
8044       {
8045         _EdgesOnShape& eos = *(id2eos->second);
8046         if ( eos.ShapeType() != TopAbs_EDGE )
8047           for ( size_t i = 0; i < eos._edges.size(); ++i )
8048             eos._edges[ i ]->_cosin = avgCosin;
8049
8050         for ( size_t i = 0; i < eos._edges.size(); ++i )
8051         {
8052           eos._edges[ i ]->SetNormal( avgNormal );
8053           eos._edges[ i ]->Set( _LayerEdge::NORMAL_UPDATED );
8054         }
8055       }
8056     }
8057     else // if ( isSpherical )
8058     {
8059       // We suppose that centers of curvature at all points of the FACE
8060       // lie on some curve, let's call it "central curve". For all _LayerEdge's
8061       // having a common center of curvature we define the same new normal
8062       // as a sum of normals of _LayerEdge's on EDGEs among them.
8063
8064       // get all centers of curvature for each EDGE
8065
8066       helper.SetSubShape( convFace._face );
8067       _LayerEdge* vertexLEdges[2], **edgeLEdge, **edgeLEdgeEnd;
8068
8069       TopExp_Explorer edgeExp( convFace._face, TopAbs_EDGE );
8070       for ( int iE = 0; edgeExp.More(); edgeExp.Next(), ++iE )
8071       {
8072         const TopoDS_Edge& edge = TopoDS::Edge( edgeExp.Current() );
8073
8074         // set adjacent FACE
8075         centerCurves[ iE ].SetShapes( edge, convFace, data, helper );
8076
8077         // get _LayerEdge's of the EDGE
8078         TGeomID edgeID = meshDS->ShapeToIndex( edge );
8079         _EdgesOnShape* eos = data.GetShapeEdges( edgeID );
8080         if ( !eos || eos->_edges.empty() )
8081         {
8082           // no _LayerEdge's on EDGE, use _LayerEdge's on VERTEXes
8083           for ( int iV = 0; iV < 2; ++iV )
8084           {
8085             TopoDS_Vertex v = helper.IthVertex( iV, edge );
8086             TGeomID     vID = meshDS->ShapeToIndex( v );
8087             eos = data.GetShapeEdges( vID );
8088             vertexLEdges[ iV ] = eos->_edges[ 0 ];
8089           }
8090           edgeLEdge    = &vertexLEdges[0];
8091           edgeLEdgeEnd = edgeLEdge + 2;
8092
8093           centerCurves[ iE ]._adjFace.Nullify();
8094         }
8095         else
8096         {
8097           if ( ! eos->_toSmooth )
8098             data.SortOnEdge( edge, eos->_edges );
8099           edgeLEdge    = &eos->_edges[ 0 ];
8100           edgeLEdgeEnd = edgeLEdge + eos->_edges.size();
8101           vertexLEdges[0] = eos->_edges.front()->_2neibors->_edges[0];
8102           vertexLEdges[1] = eos->_edges.back() ->_2neibors->_edges[1];
8103
8104           if ( ! eos->_sWOL.IsNull() )
8105             centerCurves[ iE ]._adjFace.Nullify();
8106         }
8107
8108         // Get curvature centers
8109
8110         centersBox.Clear();
8111
8112         if ( edgeLEdge[0]->IsOnEdge() &&
8113              convFace.GetCenterOfCurvature( vertexLEdges[0], surfProp, helper, center ))
8114         { // 1st VERTEX
8115           centerCurves[ iE ].Append( center, vertexLEdges[0] );
8116           centersBox.Add( center );
8117         }
8118         for ( ; edgeLEdge < edgeLEdgeEnd; ++edgeLEdge )
8119           if ( convFace.GetCenterOfCurvature( *edgeLEdge, surfProp, helper, center ))
8120           { // EDGE or VERTEXes
8121             centerCurves[ iE ].Append( center, *edgeLEdge );
8122             centersBox.Add( center );
8123           }
8124         if ( edgeLEdge[-1]->IsOnEdge() &&
8125              convFace.GetCenterOfCurvature( vertexLEdges[1], surfProp, helper, center ))
8126         { // 2nd VERTEX
8127           centerCurves[ iE ].Append( center, vertexLEdges[1] );
8128           centersBox.Add( center );
8129         }
8130         centerCurves[ iE ]._isDegenerated =
8131           ( centersBox.IsVoid() || centersBox.SquareExtent() < 1e-6 * nodesBox.SquareExtent() );
8132
8133       } // loop on EDGES of convFace._face to set up data of centerCurves
8134
8135       // Compute new normals for _LayerEdge's on EDGEs
8136
8137       double avgCosin = 0;
8138       int     nbCosin = 0;
8139       gp_Vec inFaceDir;
8140       for ( size_t iE1 = 0; iE1 < centerCurves.size(); ++iE1 )
8141       {
8142         _CentralCurveOnEdge& ceCurve = centerCurves[ iE1 ];
8143         if ( ceCurve._isDegenerated )
8144           continue;
8145         const vector< gp_Pnt >& centers = ceCurve._curvaCenters;
8146         vector< gp_XYZ > &   newNormals = ceCurve._normals;
8147         for ( size_t iC1 = 0; iC1 < centers.size(); ++iC1 )
8148         {
8149           isOK = false;
8150           for ( size_t iE2 = 0; iE2 < centerCurves.size() && !isOK; ++iE2 )
8151           {
8152             if ( iE1 != iE2 )
8153               isOK = centerCurves[ iE2 ].FindNewNormal( centers[ iC1 ], newNormals[ iC1 ]);
8154           }
8155           if ( isOK && !ceCurve._adjFace.IsNull() )
8156           {
8157             // compute new _LayerEdge::_cosin
8158             const SMDS_MeshNode* node = ceCurve._ledges[ iC1 ]->_nodes[0];
8159             inFaceDir = getFaceDir( ceCurve._adjFace, ceCurve._edge, node, helper, isOK );
8160             if ( isOK )
8161             {
8162               double angle = inFaceDir.Angle( newNormals[ iC1 ] ); // [0,PI]
8163               ceCurve._ledges[ iC1 ]->_cosin = Cos( angle );
8164               avgCosin += ceCurve._ledges[ iC1 ]->_cosin;
8165               nbCosin++;
8166             }
8167           }
8168         }
8169       }
8170       // set new normals to _LayerEdge's of NOT degenerated central curves
8171       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
8172       {
8173         if ( centerCurves[ iE ]._isDegenerated )
8174           continue;
8175         for ( size_t iLE = 0; iLE < centerCurves[ iE ]._ledges.size(); ++iLE )
8176         {
8177           centerCurves[ iE ]._ledges[ iLE ]->SetNormal( centerCurves[ iE ]._normals[ iLE ]);
8178           centerCurves[ iE ]._ledges[ iLE ]->Set( _LayerEdge::NORMAL_UPDATED );
8179         }
8180       }
8181       // set new normals to _LayerEdge's of     degenerated central curves
8182       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
8183       {
8184         if ( !centerCurves[ iE ]._isDegenerated ||
8185              centerCurves[ iE ]._ledges.size() < 3 )
8186           continue;
8187         // new normal is an average of new normals at VERTEXes that
8188         // was computed on non-degenerated _CentralCurveOnEdge's
8189         gp_XYZ newNorm = ( centerCurves[ iE ]._ledges.front()->_normal +
8190                            centerCurves[ iE ]._ledges.back ()->_normal );
8191         double sz = newNorm.Modulus();
8192         if ( sz < 1e-200 )
8193           continue;
8194         newNorm /= sz;
8195         double newCosin = ( 0.5 * centerCurves[ iE ]._ledges.front()->_cosin +
8196                             0.5 * centerCurves[ iE ]._ledges.back ()->_cosin );
8197         for ( size_t iLE = 1, nb = centerCurves[ iE ]._ledges.size() - 1; iLE < nb; ++iLE )
8198         {
8199           centerCurves[ iE ]._ledges[ iLE ]->SetNormal( newNorm );
8200           centerCurves[ iE ]._ledges[ iLE ]->_cosin   = newCosin;
8201           centerCurves[ iE ]._ledges[ iLE ]->Set( _LayerEdge::NORMAL_UPDATED );
8202         }
8203       }
8204
8205       // Find new normals for _LayerEdge's based on FACE
8206
8207       if ( nbCosin > 0 )
8208         avgCosin /= nbCosin;
8209       const TGeomID faceID = meshDS->ShapeToIndex( convFace._face );
8210       map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.find( faceID );
8211       if ( id2eos != convFace._subIdToEOS.end() )
8212       {
8213         int iE = 0;
8214         gp_XYZ newNorm;
8215         _EdgesOnShape& eos = * ( id2eos->second );
8216         for ( size_t i = 0; i < eos._edges.size(); ++i )
8217         {
8218           _LayerEdge* ledge = eos._edges[ i ];
8219           if ( !convFace.GetCenterOfCurvature( ledge, surfProp, helper, center ))
8220             continue;
8221           for ( size_t i = 0; i < centerCurves.size(); ++i, ++iE )
8222           {
8223             iE = iE % centerCurves.size();
8224             if ( centerCurves[ iE ]._isDegenerated )
8225               continue;
8226             newNorm.SetCoord( 0,0,0 );
8227             if ( centerCurves[ iE ].FindNewNormal( center, newNorm ))
8228             {
8229               ledge->SetNormal( newNorm );
8230               ledge->_cosin  = avgCosin;
8231               ledge->Set( _LayerEdge::NORMAL_UPDATED );
8232               break;
8233             }
8234           }
8235         }
8236       }
8237
8238     } // not a quasi-spherical FACE
8239
8240     // Update _LayerEdge's data according to a new normal
8241
8242     dumpFunction(SMESH_Comment("updateNormalsOfConvexFaces")<<data._index
8243                  <<"_F"<<meshDS->ShapeToIndex( convFace._face ));
8244
8245     id2eos = convFace._subIdToEOS.begin();
8246     for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
8247     {
8248       _EdgesOnShape& eos = * ( id2eos->second );
8249       for ( size_t i = 0; i < eos._edges.size(); ++i )
8250       {
8251         _LayerEdge* & ledge = eos._edges[ i ];
8252         double len = ledge->_len;
8253         ledge->InvalidateStep( stepNb + 1, eos, /*restoreLength=*/true );
8254         ledge->SetCosin( ledge->_cosin );
8255         ledge->SetNewLength( len, eos, helper );
8256       }
8257       if ( eos.ShapeType() != TopAbs_FACE )
8258         for ( size_t i = 0; i < eos._edges.size(); ++i )
8259         {
8260           _LayerEdge* ledge = eos._edges[ i ];
8261           for ( size_t iN = 0; iN < ledge->_neibors.size(); ++iN )
8262           {
8263             _LayerEdge* neibor = ledge->_neibors[iN];
8264             if ( neibor->_nodes[0]->GetPosition()->GetDim() == 2 )
8265             {
8266               neibor->Set( _LayerEdge::NEAR_BOUNDARY );
8267               neibor->Set( _LayerEdge::MOVED );
8268               neibor->SetSmooLen( neibor->_len );
8269             }
8270           }
8271         }
8272     } // loop on sub-shapes of convFace._face
8273
8274     // Find FACEs adjacent to convFace._face that got necessity to smooth
8275     // as a result of normals modification
8276
8277     set< _EdgesOnShape* > adjFacesToSmooth;
8278     for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
8279     {
8280       if ( centerCurves[ iE ]._adjFace.IsNull() ||
8281            centerCurves[ iE ]._adjFaceToSmooth )
8282         continue;
8283       for ( size_t iLE = 0; iLE < centerCurves[ iE ]._ledges.size(); ++iLE )
8284       {
8285         if ( centerCurves[ iE ]._ledges[ iLE ]->_cosin > theMinSmoothCosin )
8286         {
8287           adjFacesToSmooth.insert( data.GetShapeEdges( centerCurves[ iE ]._adjFace ));
8288           break;
8289         }
8290       }
8291     }
8292     data.AddShapesToSmooth( adjFacesToSmooth );
8293
8294     dumpFunctionEnd();
8295
8296
8297   } // loop on data._convexFaces
8298
8299   return true;
8300 }
8301
8302 //================================================================================
8303 /*!
8304  * \brief Return max curvature of a FACE
8305  */
8306 //================================================================================
8307
8308 double _ConvexFace::GetMaxCurvature( _SolidData&         data,
8309                                      _EdgesOnShape&      eof,
8310                                      BRepLProp_SLProps&  surfProp,
8311                                      SMESH_MesherHelper& helper)
8312 {
8313   double maxCurvature = 0;
8314
8315   TopoDS_Face F = TopoDS::Face( eof._shape );
8316
8317   const int           nbTestPnt = 5;
8318   const double        oriFactor = ( F.Orientation() == TopAbs_REVERSED ? +1. : -1. );
8319   SMESH_subMeshIteratorPtr smIt = eof._subMesh->getDependsOnIterator(/*includeSelf=*/true);
8320   while ( smIt->more() )
8321   {
8322     SMESH_subMesh* sm = smIt->next();
8323     const TGeomID subID = sm->GetId();
8324
8325     // find _LayerEdge's of a sub-shape
8326     _EdgesOnShape* eos;
8327     if (( eos = data.GetShapeEdges( subID )))
8328       this->_subIdToEOS.insert( make_pair( subID, eos ));
8329     else
8330       continue;
8331
8332     // check concavity and curvature and limit data._stepSize
8333     const double minCurvature =
8334       1. / ( eos->_hyp.GetTotalThickness() * ( 1 + theThickToIntersection ));
8335     size_t iStep = Max( 1, eos->_edges.size() / nbTestPnt );
8336     for ( size_t i = 0; i < eos->_edges.size(); i += iStep )
8337     {
8338       gp_XY uv = helper.GetNodeUV( F, eos->_edges[ i ]->_nodes[0] );
8339       surfProp.SetParameters( uv.X(), uv.Y() );
8340       if ( surfProp.IsCurvatureDefined() )
8341       {
8342         double curvature = Max( surfProp.MaxCurvature() * oriFactor,
8343                                 surfProp.MinCurvature() * oriFactor );
8344         maxCurvature = Max( maxCurvature, curvature );
8345
8346         if ( curvature > minCurvature )
8347           this->_isTooCurved = true;
8348       }
8349     }
8350   } // loop on sub-shapes of the FACE
8351
8352   return maxCurvature;
8353 }
8354
8355 //================================================================================
8356 /*!
8357  * \brief Finds a center of curvature of a surface at a _LayerEdge
8358  */
8359 //================================================================================
8360
8361 bool _ConvexFace::GetCenterOfCurvature( _LayerEdge*         ledge,
8362                                         BRepLProp_SLProps&  surfProp,
8363                                         SMESH_MesherHelper& helper,
8364                                         gp_Pnt &            center ) const
8365 {
8366   gp_XY uv = helper.GetNodeUV( _face, ledge->_nodes[0] );
8367   surfProp.SetParameters( uv.X(), uv.Y() );
8368   if ( !surfProp.IsCurvatureDefined() )
8369     return false;
8370
8371   const double oriFactor = ( _face.Orientation() == TopAbs_REVERSED ? +1. : -1. );
8372   double surfCurvatureMax = surfProp.MaxCurvature() * oriFactor;
8373   double surfCurvatureMin = surfProp.MinCurvature() * oriFactor;
8374   if ( surfCurvatureMin > surfCurvatureMax )
8375     center = surfProp.Value().Translated( surfProp.Normal().XYZ() / surfCurvatureMin * oriFactor );
8376   else
8377     center = surfProp.Value().Translated( surfProp.Normal().XYZ() / surfCurvatureMax * oriFactor );
8378
8379   return true;
8380 }
8381
8382 //================================================================================
8383 /*!
8384  * \brief Check that prisms are not distorted
8385  */
8386 //================================================================================
8387
8388 bool _ConvexFace::CheckPrisms() const
8389 {
8390   double vol = 0;
8391   for ( size_t i = 0; i < _simplexTestEdges.size(); ++i )
8392   {
8393     const _LayerEdge* edge = _simplexTestEdges[i];
8394     SMESH_TNodeXYZ tgtXYZ( edge->_nodes.back() );
8395     for ( size_t j = 0; j < edge->_simplices.size(); ++j )
8396       if ( !edge->_simplices[j].IsForward( edge->_nodes[0], tgtXYZ, vol ))
8397       {
8398         debugMsg( "Bad simplex of _simplexTestEdges ("
8399                   << " "<< edge->_nodes[0]->GetID()<< " "<< tgtXYZ._node->GetID()
8400                   << " "<< edge->_simplices[j]._nPrev->GetID()
8401                   << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
8402         return false;
8403       }
8404   }
8405   return true;
8406 }
8407
8408 //================================================================================
8409 /*!
8410  * \brief Try to compute a new normal by interpolating normals of _LayerEdge's
8411  *        stored in this _CentralCurveOnEdge.
8412  *  \param [in] center - curvature center of a point of another _CentralCurveOnEdge.
8413  *  \param [in,out] newNormal - current normal at this point, to be redefined
8414  *  \return bool - true if succeeded.
8415  */
8416 //================================================================================
8417
8418 bool _CentralCurveOnEdge::FindNewNormal( const gp_Pnt& center, gp_XYZ& newNormal )
8419 {
8420   if ( this->_isDegenerated )
8421     return false;
8422
8423   // find two centers the given one lies between
8424
8425   for ( size_t i = 0, nb = _curvaCenters.size()-1;  i < nb;  ++i )
8426   {
8427     double sl2 = 1.001 * _segLength2[ i ];
8428
8429     double d1 = center.SquareDistance( _curvaCenters[ i ]);
8430     if ( d1 > sl2 )
8431       continue;
8432     
8433     double d2 = center.SquareDistance( _curvaCenters[ i+1 ]);
8434     if ( d2 > sl2 || d2 + d1 < 1e-100 )
8435       continue;
8436
8437     d1 = Sqrt( d1 );
8438     d2 = Sqrt( d2 );
8439     double r = d1 / ( d1 + d2 );
8440     gp_XYZ norm = (( 1. - r ) * _ledges[ i   ]->_normal +
8441                    (      r ) * _ledges[ i+1 ]->_normal );
8442     norm.Normalize();
8443
8444     newNormal += norm;
8445     double sz = newNormal.Modulus();
8446     if ( sz < 1e-200 )
8447       break;
8448     newNormal /= sz;
8449     return true;
8450   }
8451   return false;
8452 }
8453
8454 //================================================================================
8455 /*!
8456  * \brief Set shape members
8457  */
8458 //================================================================================
8459
8460 void _CentralCurveOnEdge::SetShapes( const TopoDS_Edge&  edge,
8461                                      const _ConvexFace&  convFace,
8462                                      _SolidData&         data,
8463                                      SMESH_MesherHelper& helper)
8464 {
8465   _edge = edge;
8466
8467   PShapeIteratorPtr fIt = helper.GetAncestors( edge, *helper.GetMesh(), TopAbs_FACE );
8468   while ( const TopoDS_Shape* F = fIt->next())
8469     if ( !convFace._face.IsSame( *F ))
8470     {
8471       _adjFace = TopoDS::Face( *F );
8472       _adjFaceToSmooth = false;
8473       // _adjFace already in a smoothing queue ?
8474       if ( _EdgesOnShape* eos = data.GetShapeEdges( _adjFace ))
8475         _adjFaceToSmooth = eos->_toSmooth;
8476       break;
8477     }
8478 }
8479
8480 //================================================================================
8481 /*!
8482  * \brief Looks for intersection of it's last segment with faces
8483  *  \param distance - returns shortest distance from the last node to intersection
8484  */
8485 //================================================================================
8486
8487 bool _LayerEdge::FindIntersection( SMESH_ElementSearcher&   searcher,
8488                                    double &                 distance,
8489                                    const double&            epsilon,
8490                                    _EdgesOnShape&           eos,
8491                                    const SMDS_MeshElement** intFace)
8492 {
8493   vector< const SMDS_MeshElement* > suspectFaces;
8494   double segLen;
8495   gp_Ax1 lastSegment = LastSegment( segLen, eos );
8496   searcher.GetElementsNearLine( lastSegment, SMDSAbs_Face, suspectFaces );
8497
8498   bool segmentIntersected = false;
8499   distance = Precision::Infinite();
8500   int iFace = -1; // intersected face
8501   for ( size_t j = 0 ; j < suspectFaces.size() /*&& !segmentIntersected*/; ++j )
8502   {
8503     const SMDS_MeshElement* face = suspectFaces[j];
8504     if ( face->GetNodeIndex( _nodes.back() ) >= 0 ||
8505          face->GetNodeIndex( _nodes[0]     ) >= 0 )
8506       continue; // face sharing _LayerEdge node
8507     const int nbNodes = face->NbCornerNodes();
8508     bool intFound = false;
8509     double dist;
8510     SMDS_MeshElement::iterator nIt = face->begin_nodes();
8511     if ( nbNodes == 3 )
8512     {
8513       intFound = SegTriaInter( lastSegment, *nIt++, *nIt++, *nIt++, dist, epsilon );
8514     }
8515     else
8516     {
8517       const SMDS_MeshNode* tria[3];
8518       tria[0] = *nIt++;
8519       tria[1] = *nIt++;
8520       for ( int n2 = 2; n2 < nbNodes && !intFound; ++n2 )
8521       {
8522         tria[2] = *nIt++;
8523         intFound = SegTriaInter(lastSegment, tria[0], tria[1], tria[2], dist, epsilon );
8524         tria[1] = tria[2];
8525       }
8526     }
8527     if ( intFound )
8528     {
8529       if ( dist < segLen*(1.01) && dist > -(_len*_lenFactor-segLen) )
8530         segmentIntersected = true;
8531       if ( distance > dist )
8532         distance = dist, iFace = j;
8533     }
8534   }
8535   if ( intFace ) *intFace = ( iFace != -1 ) ? suspectFaces[iFace] : 0;
8536
8537   distance -= segLen;
8538
8539   if ( segmentIntersected )
8540   {
8541 #ifdef __myDEBUG
8542     SMDS_MeshElement::iterator nIt = suspectFaces[iFace]->begin_nodes();
8543     gp_XYZ intP( lastSegment.Location().XYZ() + lastSegment.Direction().XYZ() * ( distance+segLen ));
8544     cout << "nodes: tgt " << _nodes.back()->GetID() << " src " << _nodes[0]->GetID()
8545          << ", intersection with face ("
8546          << (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()
8547          << ") at point (" << intP.X() << ", " << intP.Y() << ", " << intP.Z()
8548          << ") distance = " << distance << endl;
8549 #endif
8550   }
8551
8552   return segmentIntersected;
8553 }
8554
8555 //================================================================================
8556 /*!
8557  * \brief Returns a point used to check orientation of _simplices
8558  */
8559 //================================================================================
8560
8561 gp_XYZ _LayerEdge::PrevCheckPos( _EdgesOnShape* eos ) const
8562 {
8563   size_t i = Is( NORMAL_UPDATED ) && IsOnFace() ? _pos.size()-2 : 0;
8564
8565   if ( !eos || eos->_sWOL.IsNull() )
8566     return _pos[ i ];
8567
8568   if ( eos->SWOLType() == TopAbs_EDGE )
8569   {
8570     return BRepAdaptor_Curve( TopoDS::Edge( eos->_sWOL )).Value( _pos[i].X() ).XYZ();
8571   }
8572   //else //  TopAbs_FACE
8573
8574   return BRepAdaptor_Surface( TopoDS::Face( eos->_sWOL )).Value(_pos[i].X(), _pos[i].Y() ).XYZ();
8575 }
8576
8577 //================================================================================
8578 /*!
8579  * \brief Returns size and direction of the last segment
8580  */
8581 //================================================================================
8582
8583 gp_Ax1 _LayerEdge::LastSegment(double& segLen, _EdgesOnShape& eos) const
8584 {
8585   // find two non-coincident positions
8586   gp_XYZ orig = _pos.back();
8587   gp_XYZ vec;
8588   int iPrev = _pos.size() - 2;
8589   //const double tol = ( _len > 0 ) ? 0.3*_len : 1e-100; // adjusted for IPAL52478 + PAL22576
8590   const double tol = ( _len > 0 ) ? ( 1e-6 * _len ) : 1e-100;
8591   while ( iPrev >= 0 )
8592   {
8593     vec = orig - _pos[iPrev];
8594     if ( vec.SquareModulus() > tol*tol )
8595       break;
8596     else
8597       iPrev--;
8598   }
8599
8600   // make gp_Ax1
8601   gp_Ax1 segDir;
8602   if ( iPrev < 0 )
8603   {
8604     segDir.SetLocation( SMESH_TNodeXYZ( _nodes[0] ));
8605     segDir.SetDirection( _normal );
8606     segLen = 0;
8607   }
8608   else
8609   {
8610     gp_Pnt pPrev = _pos[ iPrev ];
8611     if ( !eos._sWOL.IsNull() )
8612     {
8613       TopLoc_Location loc;
8614       if ( eos.SWOLType() == TopAbs_EDGE )
8615       {
8616         double f,l;
8617         Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( eos._sWOL ), loc, f,l);
8618         pPrev = curve->Value( pPrev.X() ).Transformed( loc );
8619       }
8620       else
8621       {
8622         Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face( eos._sWOL ), loc );
8623         pPrev = surface->Value( pPrev.X(), pPrev.Y() ).Transformed( loc );
8624       }
8625       vec = SMESH_TNodeXYZ( _nodes.back() ) - pPrev.XYZ();
8626     }
8627     segDir.SetLocation( pPrev );
8628     segDir.SetDirection( vec );
8629     segLen = vec.Modulus();
8630   }
8631
8632   return segDir;
8633 }
8634
8635 //================================================================================
8636 /*!
8637  * \brief Return the last (or \a which) position of the target node on a FACE. 
8638  *  \param [in] F - the FACE this _LayerEdge is inflated along
8639  *  \param [in] which - index of position
8640  *  \return gp_XY - result UV
8641  */
8642 //================================================================================
8643
8644 gp_XY _LayerEdge::LastUV( const TopoDS_Face& F, _EdgesOnShape& eos, int which ) const
8645 {
8646   if ( F.IsSame( eos._sWOL )) // F is my FACE
8647     return gp_XY( _pos.back().X(), _pos.back().Y() );
8648
8649   if ( eos.SWOLType() != TopAbs_EDGE ) // wrong call
8650     return gp_XY( 1e100, 1e100 );
8651
8652   // _sWOL is EDGE of F; _pos.back().X() is the last U on the EDGE
8653   double f, l, u = _pos[ which < 0 ? _pos.size()-1 : which ].X();
8654   Handle(Geom2d_Curve) C2d = BRep_Tool::CurveOnSurface( TopoDS::Edge(eos._sWOL), F, f,l);
8655   if ( !C2d.IsNull() && f <= u && u <= l )
8656     return C2d->Value( u ).XY();
8657
8658   return gp_XY( 1e100, 1e100 );
8659 }
8660
8661 //================================================================================
8662 /*!
8663  * \brief Test intersection of the last segment with a given triangle
8664  *   using Moller-Trumbore algorithm
8665  * Intersection is detected if distance to intersection is less than _LayerEdge._len
8666  */
8667 //================================================================================
8668
8669 bool _LayerEdge::SegTriaInter( const gp_Ax1& lastSegment,
8670                                const gp_XYZ& vert0,
8671                                const gp_XYZ& vert1,
8672                                const gp_XYZ& vert2,
8673                                double&       t,
8674                                const double& EPSILON) const
8675 {
8676   const gp_Pnt& orig = lastSegment.Location();
8677   const gp_Dir& dir  = lastSegment.Direction();
8678
8679   /* calculate distance from vert0 to ray origin */
8680   //gp_XYZ tvec = orig.XYZ() - vert0;
8681
8682   //if ( tvec * dir > EPSILON )
8683     // intersected face is at back side of the temporary face this _LayerEdge belongs to
8684     //return false;
8685
8686   gp_XYZ edge1 = vert1 - vert0;
8687   gp_XYZ edge2 = vert2 - vert0;
8688
8689   /* begin calculating determinant - also used to calculate U parameter */
8690   gp_XYZ pvec = dir.XYZ() ^ edge2;
8691
8692   /* if determinant is near zero, ray lies in plane of triangle */
8693   double det = edge1 * pvec;
8694
8695   const double ANGL_EPSILON = 1e-12;
8696   if ( det > -ANGL_EPSILON && det < ANGL_EPSILON )
8697     return false;
8698
8699   /* calculate distance from vert0 to ray origin */
8700   gp_XYZ tvec = orig.XYZ() - vert0;
8701
8702   /* calculate U parameter and test bounds */
8703   double u = ( tvec * pvec ) / det;
8704   //if (u < 0.0 || u > 1.0)
8705   if ( u < -EPSILON || u > 1.0 + EPSILON )
8706     return false;
8707
8708   /* prepare to test V parameter */
8709   gp_XYZ qvec = tvec ^ edge1;
8710
8711   /* calculate V parameter and test bounds */
8712   double v = (dir.XYZ() * qvec) / det;
8713   //if ( v < 0.0 || u + v > 1.0 )
8714   if ( v < -EPSILON || u + v > 1.0 + EPSILON )
8715     return false;
8716
8717   /* calculate t, ray intersects triangle */
8718   t = (edge2 * qvec) / det;
8719
8720   //return true;
8721   return t > 0.;
8722 }
8723
8724 //================================================================================
8725 /*!
8726  * \brief _LayerEdge, located at a concave VERTEX of a FACE, moves target nodes of
8727  *        neighbor _LayerEdge's by it's own inflation vector.
8728  *  \param [in] eov - EOS of the VERTEX
8729  *  \param [in] eos - EOS of the FACE
8730  *  \param [in] step - inflation step
8731  *  \param [in,out] badSmooEdges - tangled _LayerEdge's
8732  */
8733 //================================================================================
8734
8735 void _LayerEdge::MoveNearConcaVer( const _EdgesOnShape*    eov,
8736                                    const _EdgesOnShape*    eos,
8737                                    const int               step,
8738                                    vector< _LayerEdge* > & badSmooEdges )
8739 {
8740   // check if any of _neibors is in badSmooEdges
8741   if ( std::find_first_of( _neibors.begin(), _neibors.end(),
8742                            badSmooEdges.begin(), badSmooEdges.end() ) == _neibors.end() )
8743     return;
8744
8745   // get all edges to move
8746
8747   set< _LayerEdge* > edges;
8748
8749   // find a distance between _LayerEdge on VERTEX and its neighbors
8750   gp_XYZ  curPosV = SMESH_TNodeXYZ( _nodes.back() );
8751   double dist2 = 0;
8752   for ( size_t i = 0; i < _neibors.size(); ++i )
8753   {
8754     _LayerEdge* nEdge = _neibors[i];
8755     if ( nEdge->_nodes[0]->getshapeId() == eos->_shapeID )
8756     {
8757       edges.insert( nEdge );
8758       dist2 = Max( dist2, ( curPosV - nEdge->_pos.back() ).SquareModulus() );
8759     }
8760   }
8761   // add _LayerEdge's close to curPosV
8762   size_t nbE;
8763   do {
8764     nbE = edges.size();
8765     for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8766     {
8767       _LayerEdge* edgeF = *e;
8768       for ( size_t i = 0; i < edgeF->_neibors.size(); ++i )
8769       {
8770         _LayerEdge* nEdge = edgeF->_neibors[i];
8771         if ( nEdge->_nodes[0]->getshapeId() == eos->_shapeID &&
8772              dist2 > ( curPosV - nEdge->_pos.back() ).SquareModulus() )
8773           edges.insert( nEdge );
8774       }
8775     }
8776   }
8777   while ( nbE < edges.size() );
8778
8779   // move the target node of the got edges
8780
8781   gp_XYZ prevPosV = PrevPos();
8782   if ( eov->SWOLType() == TopAbs_EDGE )
8783   {
8784     BRepAdaptor_Curve curve ( TopoDS::Edge( eov->_sWOL ));
8785     prevPosV = curve.Value( prevPosV.X() ).XYZ();
8786   }
8787   else if ( eov->SWOLType() == TopAbs_FACE )
8788   {
8789     BRepAdaptor_Surface surface( TopoDS::Face( eov->_sWOL ));
8790     prevPosV = surface.Value( prevPosV.X(), prevPosV.Y() ).XYZ();
8791   }
8792
8793   SMDS_FacePositionPtr fPos;
8794   //double r = 1. - Min( 0.9, step / 10. );
8795   for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8796   {
8797     _LayerEdge*       edgeF = *e;
8798     const gp_XYZ     prevVF = edgeF->PrevPos() - prevPosV;
8799     const gp_XYZ    newPosF = curPosV + prevVF;
8800     SMDS_MeshNode* tgtNodeF = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
8801     tgtNodeF->setXYZ( newPosF.X(), newPosF.Y(), newPosF.Z() );
8802     edgeF->_pos.back() = newPosF;
8803     dumpMoveComm( tgtNodeF, "MoveNearConcaVer" ); // debug
8804
8805     // set _curvature to make edgeF updated by putOnOffsetSurface()
8806     if ( !edgeF->_curvature )
8807       if (( fPos = edgeF->_nodes[0]->GetPosition() ))
8808       {
8809         edgeF->_curvature = _Factory::NewCurvature();
8810         edgeF->_curvature->_r = 0;
8811         edgeF->_curvature->_k = 0;
8812         edgeF->_curvature->_h2lenRatio = 0;
8813         edgeF->_curvature->_uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
8814       }
8815   }
8816   // gp_XYZ inflationVec( SMESH_TNodeXYZ( _nodes.back() ) -
8817   //                      SMESH_TNodeXYZ( _nodes[0]    ));
8818   // for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8819   // {
8820   //   _LayerEdge*      edgeF = *e;
8821   //   gp_XYZ          newPos = SMESH_TNodeXYZ( edgeF->_nodes[0] ) + inflationVec;
8822   //   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
8823   //   tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8824   //   edgeF->_pos.back() = newPosF;
8825   //   dumpMoveComm( tgtNode, "MoveNearConcaVer" ); // debug
8826   // }
8827
8828   // smooth _LayerEdge's around moved nodes
8829   //size_t nbBadBefore = badSmooEdges.size();
8830   for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8831   {
8832     _LayerEdge* edgeF = *e;
8833     for ( size_t j = 0; j < edgeF->_neibors.size(); ++j )
8834       if ( edgeF->_neibors[j]->_nodes[0]->getshapeId() == eos->_shapeID )
8835         //&& !edges.count( edgeF->_neibors[j] ))
8836       {
8837         _LayerEdge* edgeFN = edgeF->_neibors[j];
8838         edgeFN->Unset( SMOOTHED );
8839         int nbBad = edgeFN->Smooth( step, /*isConcaFace=*/true, /*findBest=*/true );
8840         // if ( nbBad > 0 )
8841         // {
8842         //   gp_XYZ         newPos = SMESH_TNodeXYZ( edgeFN->_nodes[0] ) + inflationVec;
8843         //   const gp_XYZ& prevPos = edgeFN->_pos[ edgeFN->_pos.size()-2 ];
8844         //   int        nbBadAfter = edgeFN->_simplices.size();
8845         //   double vol;
8846         //   for ( size_t iS = 0; iS < edgeFN->_simplices.size(); ++iS )
8847         //   {
8848         //     nbBadAfter -= edgeFN->_simplices[iS].IsForward( &prevPos, &newPos, vol );
8849         //   }
8850         //   if ( nbBadAfter <= nbBad )
8851         //   {
8852         //     SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeFN->_nodes.back() );
8853         //     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8854         //     edgeF->_pos.back() = newPosF;
8855         //     dumpMoveComm( tgtNode, "MoveNearConcaVer 2" ); // debug
8856         //     nbBad = nbBadAfter;
8857         //   }
8858         // }
8859         if ( nbBad > 0 )
8860           badSmooEdges.push_back( edgeFN );
8861       }
8862   }
8863     // move a bit not smoothed around moved nodes
8864   //   for ( size_t i = nbBadBefore; i < badSmooEdges.size(); ++i )
8865   //   {
8866   //   _LayerEdge*      edgeF = badSmooEdges[i];
8867   //   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
8868   //   gp_XYZ         newPos1 = SMESH_TNodeXYZ( edgeF->_nodes[0] ) + inflationVec;
8869   //   gp_XYZ         newPos2 = 0.5 * ( newPos1 + SMESH_TNodeXYZ( tgtNode ));
8870   //   tgtNode->setXYZ( newPos2.X(), newPos2.Y(), newPos2.Z() );
8871   //   edgeF->_pos.back() = newPosF;
8872   //   dumpMoveComm( tgtNode, "MoveNearConcaVer 2" ); // debug
8873   // }
8874 }
8875
8876 //================================================================================
8877 /*!
8878  * \brief Perform smooth of _LayerEdge's based on EDGE's
8879  *  \retval bool - true if node has been moved
8880  */
8881 //================================================================================
8882
8883 bool _LayerEdge::SmoothOnEdge(Handle(ShapeAnalysis_Surface)& surface,
8884                               const TopoDS_Face&             F,
8885                               SMESH_MesherHelper&            helper)
8886 {
8887   ASSERT( IsOnEdge() );
8888
8889   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _nodes.back() );
8890   SMESH_TNodeXYZ oldPos( tgtNode );
8891   double dist01, distNewOld;
8892   
8893   SMESH_TNodeXYZ p0( _2neibors->tgtNode(0));
8894   SMESH_TNodeXYZ p1( _2neibors->tgtNode(1));
8895   dist01 = p0.Distance( _2neibors->tgtNode(1) );
8896
8897   gp_Pnt newPos = p0 * _2neibors->_wgt[0] + p1 * _2neibors->_wgt[1];
8898   double lenDelta = 0;
8899   if ( _curvature )
8900   {
8901     //lenDelta = _curvature->lenDelta( _len );
8902     lenDelta = _curvature->lenDeltaByDist( dist01 );
8903     newPos.ChangeCoord() += _normal * lenDelta;
8904   }
8905
8906   distNewOld = newPos.Distance( oldPos );
8907
8908   if ( F.IsNull() )
8909   {
8910     if ( _2neibors->_plnNorm )
8911     {
8912       // put newPos on the plane defined by source node and _plnNorm
8913       gp_XYZ new2src = SMESH_TNodeXYZ( _nodes[0] ) - newPos.XYZ();
8914       double new2srcProj = (*_2neibors->_plnNorm) * new2src;
8915       newPos.ChangeCoord() += (*_2neibors->_plnNorm) * new2srcProj;
8916     }
8917     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8918     _pos.back() = newPos.XYZ();
8919   }
8920   else
8921   {
8922     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8923     gp_XY uv( Precision::Infinite(), 0 );
8924     helper.CheckNodeUV( F, tgtNode, uv, 1e-10, /*force=*/true );
8925     _pos.back().SetCoord( uv.X(), uv.Y(), 0 );
8926
8927     newPos = surface->Value( uv );
8928     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8929   }
8930
8931   // commented for IPAL0052478
8932   // if ( _curvature && lenDelta < 0 )
8933   // {
8934   //   gp_Pnt prevPos( _pos[ _pos.size()-2 ]);
8935   //   _len -= prevPos.Distance( oldPos );
8936   //   _len += prevPos.Distance( newPos );
8937   // }
8938   bool moved = distNewOld > dist01/50;
8939   //if ( moved )
8940   dumpMove( tgtNode ); // debug
8941
8942   return moved;
8943 }
8944
8945 //================================================================================
8946 /*!
8947  * \brief Perform 3D smooth of nodes inflated from FACE. No check of validity
8948  */
8949 //================================================================================
8950
8951 void _LayerEdge::SmoothWoCheck()
8952 {
8953   if ( Is( DIFFICULT ))
8954     return;
8955
8956   bool moved = Is( SMOOTHED );
8957   for ( size_t i = 0; i < _neibors.size()  &&  !moved; ++i )
8958     moved = _neibors[i]->Is( SMOOTHED );
8959   if ( !moved )
8960     return;
8961
8962   gp_XYZ newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
8963
8964   SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
8965   n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
8966   _pos.back() = newPos;
8967
8968   dumpMoveComm( n, SMESH_Comment("No check - ") << _funNames[ smooFunID() ]);
8969 }
8970
8971 //================================================================================
8972 /*!
8973  * \brief Checks validity of _neibors on EDGEs and VERTEXes
8974  */
8975 //================================================================================
8976
8977 int _LayerEdge::CheckNeiborsOnBoundary( vector< _LayerEdge* >* badNeibors, bool * needSmooth )
8978 {
8979   if ( ! Is( NEAR_BOUNDARY ))
8980     return 0;
8981
8982   int nbBad = 0;
8983   double vol;
8984   for ( size_t iN = 0; iN < _neibors.size(); ++iN )
8985   {
8986     _LayerEdge* eN = _neibors[iN];
8987     if ( eN->_nodes[0]->getshapeId() == _nodes[0]->getshapeId() )
8988       continue;
8989     if ( needSmooth )
8990       *needSmooth |= ( eN->Is( _LayerEdge::BLOCKED ) ||
8991                        eN->Is( _LayerEdge::NORMAL_UPDATED ) ||
8992                        eN->_pos.size() != _pos.size() );
8993
8994     SMESH_TNodeXYZ curPosN ( eN->_nodes.back() );
8995     SMESH_TNodeXYZ prevPosN( eN->_nodes[0] );
8996     for ( size_t i = 0; i < eN->_simplices.size(); ++i )
8997       if ( eN->_nodes.size() > 1 &&
8998            eN->_simplices[i].Includes( _nodes.back() ) &&
8999            !eN->_simplices[i].IsForward( &prevPosN, &curPosN, vol ))
9000       {
9001         ++nbBad;
9002         if ( badNeibors )
9003         {
9004           badNeibors->push_back( eN );
9005           debugMsg("Bad boundary simplex ( "
9006                    << " "<< eN->_nodes[0]->GetID()
9007                    << " "<< eN->_nodes.back()->GetID()
9008                    << " "<< eN->_simplices[i]._nPrev->GetID()
9009                    << " "<< eN->_simplices[i]._nNext->GetID() << " )" );
9010         }
9011         else
9012         {
9013           break;
9014         }
9015       }
9016   }
9017   return nbBad;
9018 }
9019
9020 //================================================================================
9021 /*!
9022  * \brief Perform 'smart' 3D smooth of nodes inflated from FACE
9023  *  \retval int - nb of bad simplices around this _LayerEdge
9024  */
9025 //================================================================================
9026
9027 int _LayerEdge::Smooth(const int step, bool findBest, vector< _LayerEdge* >& toSmooth )
9028 {
9029   if ( !Is( MOVED ) || Is( SMOOTHED ) || Is( BLOCKED ))
9030     return 0; // shape of simplices not changed
9031   if ( _simplices.size() < 2 )
9032     return 0; // _LayerEdge inflated along EDGE or FACE
9033
9034   if ( Is( DIFFICULT )) // || Is( ON_CONCAVE_FACE )
9035     findBest = true;
9036
9037   const gp_XYZ& curPos  = _pos.back();
9038   const gp_XYZ& prevPos = _pos[0]; //PrevPos();
9039
9040   // quality metrics (orientation) of tetras around _tgtNode
9041   int nbOkBefore = 0;
9042   double vol, minVolBefore = 1e100;
9043   for ( size_t i = 0; i < _simplices.size(); ++i )
9044   {
9045     nbOkBefore += _simplices[i].IsForward( &prevPos, &curPos, vol );
9046     minVolBefore = Min( minVolBefore, vol );
9047   }
9048   int nbBad = _simplices.size() - nbOkBefore;
9049
9050   bool bndNeedSmooth = false;
9051   if ( nbBad == 0 )
9052     nbBad = CheckNeiborsOnBoundary( 0, & bndNeedSmooth );
9053   if ( nbBad > 0 )
9054     Set( DISTORTED );
9055
9056   // evaluate min angle
9057   if ( nbBad == 0 && !findBest && !bndNeedSmooth )
9058   {
9059     size_t nbGoodAngles = _simplices.size();
9060     double angle;
9061     for ( size_t i = 0; i < _simplices.size(); ++i )
9062     {
9063       if ( !_simplices[i].IsMinAngleOK( curPos, angle ) && angle > _minAngle )
9064         --nbGoodAngles;
9065     }
9066     if ( nbGoodAngles == _simplices.size() )
9067     {
9068       Unset( MOVED );
9069       return 0;
9070     }
9071   }
9072   if ( Is( ON_CONCAVE_FACE ))
9073     findBest = true;
9074
9075   if ( step % 2 == 0 )
9076     findBest = false;
9077
9078   if ( Is( ON_CONCAVE_FACE ) && !findBest ) // alternate FUN_CENTROIDAL and FUN_LAPLACIAN
9079   {
9080     if ( _smooFunction == _funs[ FUN_LAPLACIAN ] )
9081       _smooFunction = _funs[ FUN_CENTROIDAL ];
9082     else
9083       _smooFunction = _funs[ FUN_LAPLACIAN ];
9084   }
9085
9086   // compute new position for the last _pos using different _funs
9087   gp_XYZ newPos;
9088   bool moved = false;
9089   for ( int iFun = -1; iFun < theNbSmooFuns; ++iFun )
9090   {
9091     if ( iFun < 0 )
9092       newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
9093     else if ( _funs[ iFun ] == _smooFunction )
9094       continue; // _smooFunction again
9095     else if ( step > 1 )
9096       newPos = (this->*_funs[ iFun ])(); // try other smoothing fun
9097     else
9098       break; // let "easy" functions improve elements around distorted ones
9099
9100     if ( _curvature )
9101     {
9102       double delta  = _curvature->lenDelta( _len );
9103       if ( delta > 0 )
9104         newPos += _normal * delta;
9105       else
9106       {
9107         double segLen = _normal * ( newPos - prevPos );
9108         if ( segLen + delta > 0 )
9109           newPos += _normal * delta;
9110       }
9111       // double segLenChange = _normal * ( curPos - newPos );
9112       // newPos += 0.5 * _normal * segLenChange;
9113     }
9114
9115     int nbOkAfter = 0;
9116     double minVolAfter = 1e100;
9117     for ( size_t i = 0; i < _simplices.size(); ++i )
9118     {
9119       nbOkAfter += _simplices[i].IsForward( &prevPos, &newPos, vol );
9120       minVolAfter = Min( minVolAfter, vol );
9121     }
9122     // get worse?
9123     if ( nbOkAfter < nbOkBefore )
9124       continue;
9125
9126     if (( findBest ) &&
9127         ( nbOkAfter == nbOkBefore ) &&
9128         ( minVolAfter <= minVolBefore ))
9129       continue;
9130
9131     nbBad        = _simplices.size() - nbOkAfter;
9132     minVolBefore = minVolAfter;
9133     nbOkBefore   = nbOkAfter;
9134     moved        = true;
9135
9136     SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
9137     n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
9138     _pos.back() = newPos;
9139
9140     dumpMoveComm( n, SMESH_Comment( _funNames[ iFun < 0 ? smooFunID() : iFun ] )
9141                   << (nbBad ? " --BAD" : ""));
9142
9143     if ( iFun > -1 )
9144     {
9145       continue; // look for a better function
9146     }
9147
9148     if ( !findBest )
9149       break;
9150
9151   } // loop on smoothing functions
9152
9153   if ( moved ) // notify _neibors
9154   {
9155     Set( SMOOTHED );
9156     for ( size_t i = 0; i < _neibors.size(); ++i )
9157       if ( !_neibors[i]->Is( MOVED ))
9158       {
9159         _neibors[i]->Set( MOVED );
9160         toSmooth.push_back( _neibors[i] );
9161       }
9162   }
9163
9164   return nbBad;
9165 }
9166
9167 //================================================================================
9168 /*!
9169  * \brief Perform 'smart' 3D smooth of nodes inflated from FACE
9170  *  \retval int - nb of bad simplices around this _LayerEdge
9171  */
9172 //================================================================================
9173
9174 int _LayerEdge::Smooth(const int step, const bool isConcaveFace, bool findBest )
9175 {
9176   if ( !_smooFunction )
9177     return 0; // _LayerEdge inflated along EDGE or FACE
9178   if ( Is( BLOCKED ))
9179     return 0; // not inflated
9180
9181   const gp_XYZ& curPos  = _pos.back();
9182   const gp_XYZ& prevPos = _pos[0]; //PrevCheckPos();
9183
9184   // quality metrics (orientation) of tetras around _tgtNode
9185   int nbOkBefore = 0;
9186   double vol, minVolBefore = 1e100;
9187   for ( size_t i = 0; i < _simplices.size(); ++i )
9188   {
9189     nbOkBefore += _simplices[i].IsForward( &prevPos, &curPos, vol );
9190     minVolBefore = Min( minVolBefore, vol );
9191   }
9192   int nbBad = _simplices.size() - nbOkBefore;
9193
9194   if ( isConcaveFace ) // alternate FUN_CENTROIDAL and FUN_LAPLACIAN
9195   {
9196     if      ( _smooFunction == _funs[ FUN_CENTROIDAL ] && step % 2 )
9197       _smooFunction = _funs[ FUN_LAPLACIAN ];
9198     else if ( _smooFunction == _funs[ FUN_LAPLACIAN ] && !( step % 2 ))
9199       _smooFunction = _funs[ FUN_CENTROIDAL ];
9200   }
9201
9202   // compute new position for the last _pos using different _funs
9203   gp_XYZ newPos;
9204   for ( int iFun = -1; iFun < theNbSmooFuns; ++iFun )
9205   {
9206     if ( iFun < 0 )
9207       newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
9208     else if ( _funs[ iFun ] == _smooFunction )
9209       continue; // _smooFunction again
9210     else if ( step > 1 )
9211       newPos = (this->*_funs[ iFun ])(); // try other smoothing fun
9212     else
9213       break; // let "easy" functions improve elements around distorted ones
9214
9215     if ( _curvature )
9216     {
9217       double delta  = _curvature->lenDelta( _len );
9218       if ( delta > 0 )
9219         newPos += _normal * delta;
9220       else
9221       {
9222         double segLen = _normal * ( newPos - prevPos );
9223         if ( segLen + delta > 0 )
9224           newPos += _normal * delta;
9225       }
9226       // double segLenChange = _normal * ( curPos - newPos );
9227       // newPos += 0.5 * _normal * segLenChange;
9228     }
9229
9230     int nbOkAfter = 0;
9231     double minVolAfter = 1e100;
9232     for ( size_t i = 0; i < _simplices.size(); ++i )
9233     {
9234       nbOkAfter += _simplices[i].IsForward( &prevPos, &newPos, vol );
9235       minVolAfter = Min( minVolAfter, vol );
9236     }
9237     // get worse?
9238     if ( nbOkAfter < nbOkBefore )
9239       continue;
9240     if (( isConcaveFace || findBest ) &&
9241         ( nbOkAfter == nbOkBefore ) &&
9242         ( minVolAfter <= minVolBefore )
9243         )
9244       continue;
9245
9246     nbBad        = _simplices.size() - nbOkAfter;
9247     minVolBefore = minVolAfter;
9248     nbOkBefore   = nbOkAfter;
9249
9250     SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
9251     n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
9252     _pos.back() = newPos;
9253
9254     dumpMoveComm( n, SMESH_Comment( _funNames[ iFun < 0 ? smooFunID() : iFun ] )
9255                   << ( nbBad ? "--BAD" : ""));
9256
9257     // commented for IPAL0052478
9258     // _len -= prevPos.Distance(SMESH_TNodeXYZ( n ));
9259     // _len += prevPos.Distance(newPos);
9260
9261     if ( iFun > -1 ) // findBest || the chosen _fun makes worse
9262     {
9263       //_smooFunction = _funs[ iFun ];
9264       // cout << "# " << _funNames[ iFun ] << "\t N:" << _nodes.back()->GetID()
9265       // << "\t nbBad: " << _simplices.size() - nbOkAfter
9266       // << " minVol: " << minVolAfter
9267       // << " " << newPos.X() << " " << newPos.Y() << " " << newPos.Z()
9268       // << endl;
9269       continue; // look for a better function
9270     }
9271
9272     if ( !findBest )
9273       break;
9274
9275   } // loop on smoothing functions
9276
9277   return nbBad;
9278 }
9279
9280 //================================================================================
9281 /*!
9282  * \brief Chooses a smoothing technique giving a position most close to an initial one.
9283  *        For a correct result, _simplices must contain nodes lying on geometry.
9284  */
9285 //================================================================================
9286
9287 void _LayerEdge::ChooseSmooFunction( const set< TGeomID >& concaveVertices,
9288                                      const TNode2Edge&     /*n2eMap*/)
9289 {
9290   if ( _smooFunction ) return;
9291
9292   // use smoothNefPolygon() near concaveVertices
9293   if ( !concaveVertices.empty() )
9294   {
9295     _smooFunction = _funs[ FUN_CENTROIDAL ];
9296
9297     Set( ON_CONCAVE_FACE );
9298
9299     for ( size_t i = 0; i < _simplices.size(); ++i )
9300     {
9301       if ( concaveVertices.count( _simplices[i]._nPrev->getshapeId() ))
9302       {
9303         _smooFunction = _funs[ FUN_NEFPOLY ];
9304
9305         // set FUN_CENTROIDAL to neighbor edges
9306         for ( i = 0; i < _neibors.size(); ++i )
9307         {
9308           if ( _neibors[i]->_nodes[0]->GetPosition()->GetDim() == 2 )
9309           {
9310             _neibors[i]->_smooFunction = _funs[ FUN_CENTROIDAL ];
9311           }
9312         }
9313         return;
9314       }
9315     }
9316
9317     // // this choice is done only if ( !concaveVertices.empty() ) for Grids/smesh/bugs_19/X1
9318     // // where the nodes are smoothed too far along a sphere thus creating
9319     // // inverted _simplices
9320     // double dist[theNbSmooFuns];
9321     // //double coef[theNbSmooFuns] = { 1., 1.2, 1.4, 1.4 };
9322     // double coef[theNbSmooFuns] = { 1., 1., 1., 1. };
9323
9324     // double minDist = Precision::Infinite();
9325     // gp_Pnt p = SMESH_TNodeXYZ( _nodes[0] );
9326     // for ( int i = 0; i < FUN_NEFPOLY; ++i )
9327     // {
9328     //   gp_Pnt newP = (this->*_funs[i])();
9329     //   dist[i] = p.SquareDistance( newP );
9330     //   if ( dist[i]*coef[i] < minDist )
9331     //   {
9332     //     _smooFunction = _funs[i];
9333     //     minDist = dist[i]*coef[i];
9334     //   }
9335     // }
9336   }
9337   else
9338   {
9339     _smooFunction = _funs[ FUN_LAPLACIAN ];
9340   }
9341   // int minDim = 3;
9342   // for ( size_t i = 0; i < _simplices.size(); ++i )
9343   //   minDim = Min( minDim, _simplices[i]._nPrev->GetPosition()->GetDim() );
9344   // if ( minDim == 0 )
9345   //   _smooFunction = _funs[ FUN_CENTROIDAL ];
9346   // else if ( minDim == 1 )
9347   //   _smooFunction = _funs[ FUN_CENTROIDAL ];
9348
9349
9350   // int iMin;
9351   // for ( int i = 0; i < FUN_NB; ++i )
9352   // {
9353   //   //cout << dist[i] << " ";
9354   //   if ( _smooFunction == _funs[i] ) {
9355   //     iMin = i;
9356   //     //debugMsg( fNames[i] );
9357   //     break;
9358   //   }
9359   // }
9360   // cout << _funNames[ iMin ] << "\t N:" << _nodes.back()->GetID() << endl;
9361 }
9362
9363 //================================================================================
9364 /*!
9365  * \brief Returns a name of _SmooFunction
9366  */
9367 //================================================================================
9368
9369 int _LayerEdge::smooFunID( _LayerEdge::PSmooFun fun) const
9370 {
9371   if ( !fun )
9372     fun = _smooFunction;
9373   for ( int i = 0; i < theNbSmooFuns; ++i )
9374     if ( fun == _funs[i] )
9375       return i;
9376
9377   return theNbSmooFuns;
9378 }
9379
9380 //================================================================================
9381 /*!
9382  * \brief Computes a new node position using Laplacian smoothing
9383  */
9384 //================================================================================
9385
9386 gp_XYZ _LayerEdge::smoothLaplacian()
9387 {
9388   gp_XYZ newPos (0,0,0);
9389   for ( size_t i = 0; i < _simplices.size(); ++i )
9390     newPos += SMESH_TNodeXYZ( _simplices[i]._nPrev );
9391   newPos /= _simplices.size();
9392
9393   return newPos;
9394 }
9395
9396 //================================================================================
9397 /*!
9398  * \brief Computes a new node position using angular-based smoothing
9399  */
9400 //================================================================================
9401
9402 gp_XYZ _LayerEdge::smoothAngular()
9403 {
9404   vector< gp_Vec > edgeDir;  edgeDir. reserve( _simplices.size() + 1 );
9405   vector< double > edgeSize; edgeSize.reserve( _simplices.size()     );
9406   vector< gp_XYZ > points;   points.  reserve( _simplices.size() + 1 );
9407
9408   gp_XYZ pPrev = SMESH_TNodeXYZ( _simplices.back()._nPrev );
9409   gp_XYZ pN( 0,0,0 );
9410   for ( size_t i = 0; i < _simplices.size(); ++i )
9411   {
9412     gp_XYZ p = SMESH_TNodeXYZ( _simplices[i]._nPrev );
9413     edgeDir.push_back( p - pPrev );
9414     edgeSize.push_back( edgeDir.back().Magnitude() );
9415     if ( edgeSize.back() < numeric_limits<double>::min() )
9416     {
9417       edgeDir.pop_back();
9418       edgeSize.pop_back();
9419     }
9420     else
9421     {
9422       edgeDir.back() /= edgeSize.back();
9423       points.push_back( p );
9424       pN += p;
9425     }
9426     pPrev = p;
9427   }
9428   edgeDir.push_back ( edgeDir[0] );
9429   edgeSize.push_back( edgeSize[0] );
9430   pN /= points.size();
9431
9432   gp_XYZ newPos(0,0,0);
9433   double sumSize = 0;
9434   for ( size_t i = 0; i < points.size(); ++i )
9435   {
9436     gp_Vec toN    = pN - points[i];
9437     double toNLen = toN.Magnitude();
9438     if ( toNLen < numeric_limits<double>::min() )
9439     {
9440       newPos += pN;
9441       continue;
9442     }
9443     gp_Vec bisec    = edgeDir[i] + edgeDir[i+1];
9444     double bisecLen = bisec.SquareMagnitude();
9445     if ( bisecLen < numeric_limits<double>::min() )
9446     {
9447       gp_Vec norm = edgeDir[i] ^ toN;
9448       bisec = norm ^ edgeDir[i];
9449       bisecLen = bisec.SquareMagnitude();
9450     }
9451     bisecLen = Sqrt( bisecLen );
9452     bisec /= bisecLen;
9453
9454 #if 1
9455     gp_XYZ pNew = ( points[i] + bisec.XYZ() * toNLen ) * bisecLen;
9456     sumSize += bisecLen;
9457 #else
9458     gp_XYZ pNew = ( points[i] + bisec.XYZ() * toNLen ) * ( edgeSize[i] + edgeSize[i+1] );
9459     sumSize += ( edgeSize[i] + edgeSize[i+1] );
9460 #endif
9461     newPos += pNew;
9462   }
9463   newPos /= sumSize;
9464
9465   // project newPos to an average plane
9466
9467   gp_XYZ norm(0,0,0); // plane normal
9468   points.push_back( points[0] );
9469   for ( size_t i = 1; i < points.size(); ++i )
9470   {
9471     gp_XYZ vec1 = points[ i-1 ] - pN;
9472     gp_XYZ vec2 = points[ i   ] - pN;
9473     gp_XYZ cross = vec1 ^ vec2;
9474     try {
9475       cross.Normalize();
9476       if ( cross * norm < numeric_limits<double>::min() )
9477         norm += cross.Reversed();
9478       else
9479         norm += cross;
9480     }
9481     catch (Standard_Failure&) { // if |cross| == 0.
9482     }
9483   }
9484   gp_XYZ vec = newPos - pN;
9485   double r   = ( norm * vec ) / norm.SquareModulus();  // param [0,1] on norm
9486   newPos     = newPos - r * norm;
9487
9488   return newPos;
9489 }
9490
9491 //================================================================================
9492 /*!
9493  * \brief Computes a new node position using weighted node positions
9494  */
9495 //================================================================================
9496
9497 gp_XYZ _LayerEdge::smoothLengthWeighted()
9498 {
9499   vector< double > edgeSize; edgeSize.reserve( _simplices.size() + 1);
9500   vector< gp_XYZ > points;   points.  reserve( _simplices.size() );
9501
9502   gp_XYZ pPrev = SMESH_TNodeXYZ( _simplices.back()._nPrev );
9503   for ( size_t i = 0; i < _simplices.size(); ++i )
9504   {
9505     gp_XYZ p = SMESH_TNodeXYZ( _simplices[i]._nPrev );
9506     edgeSize.push_back( ( p - pPrev ).Modulus() );
9507     if ( edgeSize.back() < numeric_limits<double>::min() )
9508     {
9509       edgeSize.pop_back();
9510     }
9511     else
9512     {
9513       points.push_back( p );
9514     }
9515     pPrev = p;
9516   }
9517   edgeSize.push_back( edgeSize[0] );
9518
9519   gp_XYZ newPos(0,0,0);
9520   double sumSize = 0;
9521   for ( size_t i = 0; i < points.size(); ++i )
9522   {
9523     newPos += points[i] * ( edgeSize[i] + edgeSize[i+1] );
9524     sumSize += edgeSize[i] + edgeSize[i+1];
9525   }
9526   newPos /= sumSize;
9527   return newPos;
9528 }
9529
9530 //================================================================================
9531 /*!
9532  * \brief Computes a new node position using angular-based smoothing
9533  */
9534 //================================================================================
9535
9536 gp_XYZ _LayerEdge::smoothCentroidal()
9537 {
9538   gp_XYZ newPos(0,0,0);
9539   gp_XYZ pN = SMESH_TNodeXYZ( _nodes.back() );
9540   double sumSize = 0;
9541   for ( size_t i = 0; i < _simplices.size(); ++i )
9542   {
9543     gp_XYZ p1 = SMESH_TNodeXYZ( _simplices[i]._nPrev );
9544     gp_XYZ p2 = SMESH_TNodeXYZ( _simplices[i]._nNext );
9545     gp_XYZ gc = ( pN + p1 + p2 ) / 3.;
9546     double size = (( p1 - pN ) ^ ( p2 - pN )).Modulus();
9547
9548     sumSize += size;
9549     newPos += gc * size;
9550   }
9551   newPos /= sumSize;
9552
9553   return newPos;
9554 }
9555
9556 //================================================================================
9557 /*!
9558  * \brief Computes a new node position located inside a Nef polygon
9559  */
9560 //================================================================================
9561
9562 gp_XYZ _LayerEdge::smoothNefPolygon()
9563 #ifdef OLD_NEF_POLYGON
9564 {
9565   gp_XYZ newPos(0,0,0);
9566
9567   // get a plane to search a solution on
9568
9569   vector< gp_XYZ > vecs( _simplices.size() + 1 );
9570   size_t i;
9571   const double tol = numeric_limits<double>::min();
9572   gp_XYZ center(0,0,0);
9573   for ( i = 0; i < _simplices.size(); ++i )
9574   {
9575     vecs[i] = ( SMESH_TNodeXYZ( _simplices[i]._nNext ) -
9576                 SMESH_TNodeXYZ( _simplices[i]._nPrev ));
9577     center += SMESH_TNodeXYZ( _simplices[i]._nPrev );
9578   }
9579   vecs.back() = vecs[0];
9580   center /= _simplices.size();
9581
9582   gp_XYZ zAxis(0,0,0);
9583   for ( i = 0; i < _simplices.size(); ++i )
9584     zAxis += vecs[i] ^ vecs[i+1];
9585
9586   gp_XYZ yAxis;
9587   for ( i = 0; i < _simplices.size(); ++i )
9588   {
9589     yAxis = vecs[i];
9590     if ( yAxis.SquareModulus() > tol )
9591       break;
9592   }
9593   gp_XYZ xAxis = yAxis ^ zAxis;
9594   // SMESH_TNodeXYZ p0( _simplices[0]._nPrev );
9595   // const double tol = 1e-6 * ( p0.Distance( _simplices[1]._nPrev ) +
9596   //                             p0.Distance( _simplices[2]._nPrev ));
9597   // gp_XYZ center = smoothLaplacian();
9598   // gp_XYZ xAxis, yAxis, zAxis;
9599   // for ( i = 0; i < _simplices.size(); ++i )
9600   // {
9601   //   xAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9602   //   if ( xAxis.SquareModulus() > tol*tol )
9603   //     break;
9604   // }
9605   // for ( i = 1; i < _simplices.size(); ++i )
9606   // {
9607   //   yAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9608   //   zAxis = xAxis ^ yAxis;
9609   //   if ( zAxis.SquareModulus() > tol*tol )
9610   //     break;
9611   // }
9612   // if ( i == _simplices.size() ) return newPos;
9613
9614   yAxis = zAxis ^ xAxis;
9615   xAxis /= xAxis.Modulus();
9616   yAxis /= yAxis.Modulus();
9617
9618   // get half-planes of _simplices
9619
9620   vector< _halfPlane > halfPlns( _simplices.size() );
9621   int nbHP = 0;
9622   for ( size_t i = 0; i < _simplices.size(); ++i )
9623   {
9624     gp_XYZ OP1 = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9625     gp_XYZ OP2 = SMESH_TNodeXYZ( _simplices[i]._nNext ) - center;
9626     gp_XY  p1( OP1 * xAxis, OP1 * yAxis );
9627     gp_XY  p2( OP2 * xAxis, OP2 * yAxis );
9628     gp_XY  vec12 = p2 - p1;
9629     double dist12 = vec12.Modulus();
9630     if ( dist12 < tol )
9631       continue;
9632     vec12 /= dist12;
9633     halfPlns[ nbHP ]._pos = p1;
9634     halfPlns[ nbHP ]._dir = vec12;
9635     halfPlns[ nbHP ]._inNorm.SetCoord( -vec12.Y(), vec12.X() );
9636     ++nbHP;
9637   }
9638
9639   // intersect boundaries of half-planes, define state of intersection points
9640   // in relation to all half-planes and calculate internal point of a 2D polygon
9641
9642   double sumLen = 0;
9643   gp_XY newPos2D (0,0);
9644
9645   enum { UNDEF = -1, NOT_OUT, IS_OUT, NO_INT };
9646   typedef std::pair< gp_XY, int > TIntPntState; // coord and isOut state
9647   TIntPntState undefIPS( gp_XY(1e100,1e100), UNDEF );
9648
9649   vector< vector< TIntPntState > > allIntPnts( nbHP );
9650   for ( int iHP1 = 0; iHP1 < nbHP; ++iHP1 )
9651   {
9652     vector< TIntPntState > & intPnts1 = allIntPnts[ iHP1 ];
9653     if ( intPnts1.empty() ) intPnts1.resize( nbHP, undefIPS );
9654
9655     int iPrev = SMESH_MesherHelper::WrapIndex( iHP1 - 1, nbHP );
9656     int iNext = SMESH_MesherHelper::WrapIndex( iHP1 + 1, nbHP );
9657
9658     int nbNotOut = 0;
9659     const gp_XY* segEnds[2] = { 0, 0 }; // NOT_OUT points
9660
9661     for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9662     {
9663       if ( iHP1 == iHP2 ) continue;
9664
9665       TIntPntState & ips1 = intPnts1[ iHP2 ];
9666       if ( ips1.second == UNDEF )
9667       {
9668         // find an intersection point of boundaries of iHP1 and iHP2
9669
9670         if ( iHP2 == iPrev ) // intersection with neighbors is known
9671           ips1.first = halfPlns[ iHP1 ]._pos;
9672         else if ( iHP2 == iNext )
9673           ips1.first = halfPlns[ iHP2 ]._pos;
9674         else if ( !halfPlns[ iHP1 ].FindIntersection( halfPlns[ iHP2 ], ips1.first ))
9675           ips1.second = NO_INT;
9676
9677         // classify the found intersection point
9678         if ( ips1.second != NO_INT )
9679         {
9680           ips1.second = NOT_OUT;
9681           for ( int i = 0; i < nbHP && ips1.second == NOT_OUT; ++i )
9682             if ( i != iHP1 && i != iHP2 &&
9683                  halfPlns[ i ].IsOut( ips1.first, tol ))
9684               ips1.second = IS_OUT;
9685         }
9686         vector< TIntPntState > & intPnts2 = allIntPnts[ iHP2 ];
9687         if ( intPnts2.empty() ) intPnts2.resize( nbHP, undefIPS );
9688         TIntPntState & ips2 = intPnts2[ iHP1 ];
9689         ips2 = ips1;
9690       }
9691       if ( ips1.second == NOT_OUT )
9692       {
9693         ++nbNotOut;
9694         segEnds[ bool(segEnds[0]) ] = & ips1.first;
9695       }
9696     }
9697
9698     // find a NOT_OUT segment of boundary which is located between
9699     // two NOT_OUT int points
9700
9701     if ( nbNotOut < 2 )
9702       continue; // no such a segment
9703
9704     if ( nbNotOut > 2 )
9705     {
9706       // sort points along the boundary
9707       map< double, TIntPntState* > ipsByParam;
9708       for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9709       {
9710         TIntPntState & ips1 = intPnts1[ iHP2 ];
9711         if ( ips1.second != NO_INT )
9712         {
9713           gp_XY     op = ips1.first - halfPlns[ iHP1 ]._pos;
9714           double param = op * halfPlns[ iHP1 ]._dir;
9715           ipsByParam.insert( make_pair( param, & ips1 ));
9716         }
9717       }
9718       // look for two neighboring NOT_OUT points
9719       nbNotOut = 0;
9720       map< double, TIntPntState* >::iterator u2ips = ipsByParam.begin();
9721       for ( ; u2ips != ipsByParam.end(); ++u2ips )
9722       {
9723         TIntPntState & ips1 = *(u2ips->second);
9724         if ( ips1.second == NOT_OUT )
9725           segEnds[ bool( nbNotOut++ ) ] = & ips1.first;
9726         else if ( nbNotOut >= 2 )
9727           break;
9728         else
9729           nbNotOut = 0;
9730       }
9731     }
9732
9733     if ( nbNotOut >= 2 )
9734     {
9735       double len = ( *segEnds[0] - *segEnds[1] ).Modulus();
9736       sumLen += len;
9737
9738       newPos2D += 0.5 * len * ( *segEnds[0] + *segEnds[1] );
9739     }
9740   }
9741
9742   if ( sumLen > 0 )
9743   {
9744     newPos2D /= sumLen;
9745     newPos = center + xAxis * newPos2D.X() + yAxis * newPos2D.Y();
9746   }
9747   else
9748   {
9749     newPos = center;
9750   }
9751
9752   return newPos;
9753 }
9754 #else // OLD_NEF_POLYGON
9755 { ////////////////////////////////// NEW
9756   gp_XYZ newPos(0,0,0);
9757
9758   // get a plane to search a solution on
9759
9760   size_t i;
9761   gp_XYZ center(0,0,0);
9762   for ( i = 0; i < _simplices.size(); ++i )
9763     center += SMESH_TNodeXYZ( _simplices[i]._nPrev );
9764   center /= _simplices.size();
9765
9766   vector< gp_XYZ > vecs( _simplices.size() + 1 );
9767   for ( i = 0; i < _simplices.size(); ++i )
9768     vecs[i] = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9769   vecs.back() = vecs[0];
9770
9771   const double tol = numeric_limits<double>::min();
9772   gp_XYZ zAxis(0,0,0);
9773   for ( i = 0; i < _simplices.size(); ++i )
9774   {
9775     gp_XYZ cross = vecs[i] ^ vecs[i+1];
9776     try {
9777       cross.Normalize();
9778       if ( cross * zAxis < tol )
9779         zAxis += cross.Reversed();
9780       else
9781         zAxis += cross;
9782     }
9783     catch (Standard_Failure) { // if |cross| == 0.
9784     }
9785   }
9786
9787   gp_XYZ yAxis;
9788   for ( i = 0; i < _simplices.size(); ++i )
9789   {
9790     yAxis = vecs[i];
9791     if ( yAxis.SquareModulus() > tol )
9792       break;
9793   }
9794   gp_XYZ xAxis = yAxis ^ zAxis;
9795   // SMESH_TNodeXYZ p0( _simplices[0]._nPrev );
9796   // const double tol = 1e-6 * ( p0.Distance( _simplices[1]._nPrev ) +
9797   //                             p0.Distance( _simplices[2]._nPrev ));
9798   // gp_XYZ center = smoothLaplacian();
9799   // gp_XYZ xAxis, yAxis, zAxis;
9800   // for ( i = 0; i < _simplices.size(); ++i )
9801   // {
9802   //   xAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9803   //   if ( xAxis.SquareModulus() > tol*tol )
9804   //     break;
9805   // }
9806   // for ( i = 1; i < _simplices.size(); ++i )
9807   // {
9808   //   yAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9809   //   zAxis = xAxis ^ yAxis;
9810   //   if ( zAxis.SquareModulus() > tol*tol )
9811   //     break;
9812   // }
9813   // if ( i == _simplices.size() ) return newPos;
9814
9815   yAxis = zAxis ^ xAxis;
9816   xAxis /= xAxis.Modulus();
9817   yAxis /= yAxis.Modulus();
9818
9819   // get half-planes of _simplices
9820
9821   vector< _halfPlane > halfPlns( _simplices.size() );
9822   int nbHP = 0;
9823   for ( size_t i = 0; i < _simplices.size(); ++i )
9824   {
9825     const gp_XYZ& OP1 = vecs[ i   ];
9826     const gp_XYZ& OP2 = vecs[ i+1 ];
9827     gp_XY  p1( OP1 * xAxis, OP1 * yAxis );
9828     gp_XY  p2( OP2 * xAxis, OP2 * yAxis );
9829     gp_XY  vec12 = p2 - p1;
9830     double dist12 = vec12.Modulus();
9831     if ( dist12 < tol )
9832       continue;
9833     vec12 /= dist12;
9834     halfPlns[ nbHP ]._pos = p1;
9835     halfPlns[ nbHP ]._dir = vec12;
9836     halfPlns[ nbHP ]._inNorm.SetCoord( -vec12.Y(), vec12.X() );
9837     ++nbHP;
9838   }
9839
9840   // intersect boundaries of half-planes, define state of intersection points
9841   // in relation to all half-planes and calculate internal point of a 2D polygon
9842
9843   double sumLen = 0;
9844   gp_XY newPos2D (0,0);
9845
9846   enum { UNDEF = -1, NOT_OUT, IS_OUT, NO_INT };
9847   typedef std::pair< gp_XY, int > TIntPntState; // coord and isOut state
9848   TIntPntState undefIPS( gp_XY(1e100,1e100), UNDEF );
9849
9850   vector< vector< TIntPntState > > allIntPnts( nbHP );
9851   for ( int iHP1 = 0; iHP1 < nbHP; ++iHP1 )
9852   {
9853     vector< TIntPntState > & intPnts1 = allIntPnts[ iHP1 ];
9854     if ( intPnts1.empty() ) intPnts1.resize( nbHP, undefIPS );
9855
9856     int iPrev = SMESH_MesherHelper::WrapIndex( iHP1 - 1, nbHP );
9857     int iNext = SMESH_MesherHelper::WrapIndex( iHP1 + 1, nbHP );
9858
9859     int nbNotOut = 0;
9860     const gp_XY* segEnds[2] = { 0, 0 }; // NOT_OUT points
9861
9862     for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9863     {
9864       if ( iHP1 == iHP2 ) continue;
9865
9866       TIntPntState & ips1 = intPnts1[ iHP2 ];
9867       if ( ips1.second == UNDEF )
9868       {
9869         // find an intersection point of boundaries of iHP1 and iHP2
9870
9871         if ( iHP2 == iPrev ) // intersection with neighbors is known
9872           ips1.first = halfPlns[ iHP1 ]._pos;
9873         else if ( iHP2 == iNext )
9874           ips1.first = halfPlns[ iHP2 ]._pos;
9875         else if ( !halfPlns[ iHP1 ].FindIntersection( halfPlns[ iHP2 ], ips1.first ))
9876           ips1.second = NO_INT;
9877
9878         // classify the found intersection point
9879         if ( ips1.second != NO_INT )
9880         {
9881           ips1.second = NOT_OUT;
9882           for ( int i = 0; i < nbHP && ips1.second == NOT_OUT; ++i )
9883             if ( i != iHP1 && i != iHP2 &&
9884                  halfPlns[ i ].IsOut( ips1.first, tol ))
9885               ips1.second = IS_OUT;
9886         }
9887         vector< TIntPntState > & intPnts2 = allIntPnts[ iHP2 ];
9888         if ( intPnts2.empty() ) intPnts2.resize( nbHP, undefIPS );
9889         TIntPntState & ips2 = intPnts2[ iHP1 ];
9890         ips2 = ips1;
9891       }
9892       if ( ips1.second == NOT_OUT )
9893       {
9894         ++nbNotOut;
9895         segEnds[ bool(segEnds[0]) ] = & ips1.first;
9896       }
9897     }
9898
9899     // find a NOT_OUT segment of boundary which is located between
9900     // two NOT_OUT int points
9901
9902     if ( nbNotOut < 2 )
9903       continue; // no such a segment
9904
9905     if ( nbNotOut > 2 )
9906     {
9907       // sort points along the boundary
9908       map< double, TIntPntState* > ipsByParam;
9909       for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9910       {
9911         TIntPntState & ips1 = intPnts1[ iHP2 ];
9912         if ( ips1.second != NO_INT )
9913         {
9914           gp_XY     op = ips1.first - halfPlns[ iHP1 ]._pos;
9915           double param = op * halfPlns[ iHP1 ]._dir;
9916           ipsByParam.insert( make_pair( param, & ips1 ));
9917         }
9918       }
9919       // look for two neighboring NOT_OUT points
9920       nbNotOut = 0;
9921       map< double, TIntPntState* >::iterator u2ips = ipsByParam.begin();
9922       for ( ; u2ips != ipsByParam.end(); ++u2ips )
9923       {
9924         TIntPntState & ips1 = *(u2ips->second);
9925         if ( ips1.second == NOT_OUT )
9926           segEnds[ bool( nbNotOut++ ) ] = & ips1.first;
9927         else if ( nbNotOut >= 2 )
9928           break;
9929         else
9930           nbNotOut = 0;
9931       }
9932     }
9933
9934     if ( nbNotOut >= 2 )
9935     {
9936       double len = ( *segEnds[0] - *segEnds[1] ).Modulus();
9937       sumLen += len;
9938
9939       newPos2D += 0.5 * len * ( *segEnds[0] + *segEnds[1] );
9940     }
9941   }
9942
9943   if ( sumLen > 0 )
9944   {
9945     newPos2D /= sumLen;
9946     newPos = center + xAxis * newPos2D.X() + yAxis * newPos2D.Y();
9947   }
9948   else
9949   {
9950     newPos = center;
9951   }
9952
9953   return newPos;
9954 }
9955 #endif // OLD_NEF_POLYGON
9956
9957 //================================================================================
9958 /*!
9959  * \brief Add a new segment to _LayerEdge during inflation
9960  */
9961 //================================================================================
9962
9963 void _LayerEdge::SetNewLength( double len, _EdgesOnShape& eos, SMESH_MesherHelper& helper )
9964 {
9965   if ( Is( BLOCKED ))
9966     return;
9967
9968   if ( len > _maxLen )
9969   {
9970     len = _maxLen;
9971     Block( eos.GetData() );
9972   }
9973   const double lenDelta = len - _len;
9974   // if ( lenDelta < 0 )
9975   //   return;
9976   if ( lenDelta < len * 1e-3  )
9977   {
9978     Block( eos.GetData() );
9979     return;
9980   }
9981
9982   SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() );
9983   gp_XYZ oldXYZ = SMESH_TNodeXYZ( n );
9984   gp_XYZ newXYZ;
9985   if ( eos._hyp.IsOffsetMethod() )
9986   {
9987     newXYZ = oldXYZ;
9988     gp_Vec faceNorm;
9989     SMDS_ElemIteratorPtr faceIt = _nodes[0]->GetInverseElementIterator( SMDSAbs_Face );
9990     while ( faceIt->more() )
9991     {
9992       const SMDS_MeshElement* face = faceIt->next();
9993       if ( !eos.GetNormal( face, faceNorm ))
9994         continue;
9995
9996       // translate plane of a face
9997       gp_XYZ baryCenter = oldXYZ + faceNorm.XYZ() * lenDelta;
9998
9999       // find point of intersection of the face plane located at baryCenter
10000       // and _normal located at newXYZ
10001       double d   = -( faceNorm.XYZ() * baryCenter ); // d of plane equation ax+by+cz+d=0
10002       double dot =  ( faceNorm.XYZ() * _normal );
10003       if ( dot < std::numeric_limits<double>::min() )
10004         dot = lenDelta * 1e-3;
10005       double step = -( faceNorm.XYZ() * newXYZ + d ) / dot;
10006       newXYZ += step * _normal;
10007     }
10008     _lenFactor = _normal * ( newXYZ - oldXYZ ) / lenDelta; // _lenFactor is used in InvalidateStep()
10009   }
10010   else
10011   {
10012     newXYZ = oldXYZ + _normal * lenDelta * _lenFactor;
10013   }
10014
10015   n->setXYZ( newXYZ.X(), newXYZ.Y(), newXYZ.Z() );
10016   _pos.push_back( newXYZ );
10017
10018   if ( !eos._sWOL.IsNull() )
10019     if ( !UpdatePositionOnSWOL( n, 2*lenDelta, eos, helper ))
10020     {
10021       n->setXYZ( oldXYZ.X(), oldXYZ.Y(), oldXYZ.Z() );
10022       _pos.pop_back();
10023       Block( eos.GetData() );
10024       return;
10025     }
10026
10027   _len = len;
10028
10029   // notify _neibors
10030   if ( eos.ShapeType() != TopAbs_FACE )
10031   {
10032     for ( size_t i = 0; i < _neibors.size(); ++i )
10033       //if (  _len > _neibors[i]->GetSmooLen() )
10034       _neibors[i]->Set( MOVED );
10035
10036     Set( MOVED );
10037   }
10038   dumpMove( n ); //debug
10039 }
10040
10041
10042 //================================================================================
10043 /*!
10044  * \brief Update last position on SWOL by projecting node on SWOL
10045 */
10046 //================================================================================
10047
10048 bool _LayerEdge::UpdatePositionOnSWOL( SMDS_MeshNode*      n,
10049                                        double              tol,
10050                                        _EdgesOnShape&      eos,
10051                                        SMESH_MesherHelper& helper )
10052 {
10053   double distXYZ[4];
10054   bool uvOK = false;
10055   if ( eos.SWOLType() == TopAbs_EDGE )
10056   {
10057     double u = Precision::Infinite(); // to force projection w/o distance check
10058     uvOK = helper.CheckNodeU( TopoDS::Edge( eos._sWOL ), n, u, tol, /*force=*/true, distXYZ );
10059     _pos.back().SetCoord( u, 0, 0 );
10060     if ( _nodes.size() > 1 && uvOK )
10061     {
10062       SMDS_EdgePositionPtr pos = n->GetPosition();
10063       pos->SetUParameter( u );
10064     }
10065   }
10066   else //  TopAbs_FACE
10067   {
10068     gp_XY uv( Precision::Infinite(), 0 );
10069     uvOK = helper.CheckNodeUV( TopoDS::Face( eos._sWOL ), n, uv, tol, /*force=*/true, distXYZ );
10070     _pos.back().SetCoord( uv.X(), uv.Y(), 0 );
10071     if ( _nodes.size() > 1 && uvOK )
10072     {
10073       SMDS_FacePositionPtr pos = n->GetPosition();
10074       pos->SetUParameter( uv.X() );
10075       pos->SetVParameter( uv.Y() );
10076     }
10077   }
10078   if ( uvOK )
10079   {
10080     n->setXYZ( distXYZ[1], distXYZ[2], distXYZ[3]);
10081   }
10082   return uvOK;
10083 }
10084
10085 //================================================================================
10086 /*!
10087  * \brief Set BLOCKED flag and propagate limited _maxLen to _neibors
10088  */
10089 //================================================================================
10090
10091 void _LayerEdge::Block( _SolidData& data )
10092 {
10093   //if ( Is( BLOCKED )) return;
10094   Set( BLOCKED );
10095
10096   SMESH_Comment msg( "#BLOCK shape=");
10097   msg << data.GetShapeEdges( this )->_shapeID
10098       << ", nodes " << _nodes[0]->GetID() << ", " << _nodes.back()->GetID();
10099   dumpCmd( msg + " -- BEGIN");
10100
10101   SetMaxLen( _len );
10102   std::queue<_LayerEdge*> queue;
10103   queue.push( this );
10104
10105   gp_Pnt pSrc, pTgt, pSrcN, pTgtN;
10106   while ( !queue.empty() )
10107   {
10108     _LayerEdge* edge = queue.front(); queue.pop();
10109     pSrc = SMESH_TNodeXYZ( edge->_nodes[0] );
10110     pTgt = SMESH_TNodeXYZ( edge->_nodes.back() );
10111     for ( size_t iN = 0; iN < edge->_neibors.size(); ++iN )
10112     {
10113       _LayerEdge* neibor = edge->_neibors[iN];
10114       if ( neibor->_maxLen < edge->_maxLen * 1.01 )
10115         continue;
10116       pSrcN = SMESH_TNodeXYZ( neibor->_nodes[0] );
10117       pTgtN = SMESH_TNodeXYZ( neibor->_nodes.back() );
10118       double minDist = pSrc.SquareDistance( pSrcN );
10119       minDist   = Min( pTgt.SquareDistance( pTgtN ), minDist );
10120       minDist   = Min( pSrc.SquareDistance( pTgtN ), minDist );
10121       minDist   = Min( pTgt.SquareDistance( pSrcN ), minDist );
10122       double newMaxLen = edge->_maxLen + 0.5 * Sqrt( minDist );
10123       //if ( edge->_nodes[0]->getshapeId() == neibor->_nodes[0]->getshapeId() ) viscous_layers_00/A3
10124       {
10125         //newMaxLen *= edge->_lenFactor / neibor->_lenFactor;
10126         // newMaxLen *= Min( edge->_lenFactor / neibor->_lenFactor,
10127         //                   neibor->_lenFactor / edge->_lenFactor );
10128       }
10129       if ( neibor->_maxLen > newMaxLen )
10130       {
10131         neibor->SetMaxLen( newMaxLen );
10132         if ( neibor->_maxLen < neibor->_len )
10133         {
10134           _EdgesOnShape* eos = data.GetShapeEdges( neibor );
10135           int       lastStep = neibor->Is( BLOCKED ) ? 1 : 0;
10136           while ( neibor->_len > neibor->_maxLen &&
10137                   neibor->NbSteps() > lastStep )
10138             neibor->InvalidateStep( neibor->NbSteps(), *eos, /*restoreLength=*/true );
10139           neibor->SetNewLength( neibor->_maxLen, *eos, data.GetHelper() );
10140           //neibor->Block( data );
10141         }
10142         queue.push( neibor );
10143       }
10144     }
10145   }
10146   dumpCmd( msg + " -- END");
10147 }
10148
10149 //================================================================================
10150 /*!
10151  * \brief Remove last inflation step
10152  */
10153 //================================================================================
10154
10155 void _LayerEdge::InvalidateStep( size_t curStep, const _EdgesOnShape& eos, bool restoreLength )
10156 {
10157   if ( _pos.size() > curStep && _nodes.size() > 1 )
10158   {
10159     _pos.resize( curStep );
10160
10161     gp_Pnt      nXYZ = _pos.back();
10162     SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() );
10163     SMESH_TNodeXYZ curXYZ( n );
10164     if ( !eos._sWOL.IsNull() )
10165     {
10166       TopLoc_Location loc;
10167       if ( eos.SWOLType() == TopAbs_EDGE )
10168       {
10169         SMDS_EdgePositionPtr pos = n->GetPosition();
10170         pos->SetUParameter( nXYZ.X() );
10171         double f,l;
10172         Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( eos._sWOL ), loc, f,l);
10173         nXYZ = curve->Value( nXYZ.X() ).Transformed( loc );
10174       }
10175       else
10176       {
10177         SMDS_FacePositionPtr pos = n->GetPosition();
10178         pos->SetUParameter( nXYZ.X() );
10179         pos->SetVParameter( nXYZ.Y() );
10180         Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face(eos._sWOL), loc );
10181         nXYZ = surface->Value( nXYZ.X(), nXYZ.Y() ).Transformed( loc );
10182       }
10183     }
10184     n->setXYZ( nXYZ.X(), nXYZ.Y(), nXYZ.Z() );
10185     dumpMove( n );
10186
10187     if ( restoreLength )
10188     {
10189       if ( NbSteps() == 0 )
10190         _len = 0.;
10191       else if ( IsOnFace() && Is( MOVED ))
10192         _len = ( nXYZ.XYZ() - SMESH_NodeXYZ( _nodes[0] )) * _normal;
10193       else
10194         _len -= ( nXYZ.XYZ() - curXYZ ).Modulus() / _lenFactor;
10195     }
10196   }
10197   return;
10198 }
10199
10200 //================================================================================
10201 /*!
10202  * \brief Return index of a _pos distant from _normal
10203  */
10204 //================================================================================
10205
10206 int _LayerEdge::GetSmoothedPos( const double tol )
10207 {
10208   int iSmoothed = 0;
10209   for ( size_t i = 1; i < _pos.size() && !iSmoothed; ++i )
10210   {
10211     double normDist = ( _pos[i] - _pos[0] ).Crossed( _normal ).SquareModulus();
10212     if ( normDist > tol * tol )
10213       iSmoothed = i;
10214   }
10215   return iSmoothed;
10216 }
10217
10218 //================================================================================
10219 /*!
10220  * \brief Smooth a path formed by _pos of a _LayerEdge smoothed on FACE
10221  */
10222 //================================================================================
10223
10224 void _LayerEdge::SmoothPos( const vector< double >& segLen, const double tol )
10225 {
10226   if ( /*Is( NORMAL_UPDATED ) ||*/ _pos.size() <= 2 )
10227     return;
10228
10229   // find the 1st smoothed _pos
10230   int iSmoothed = GetSmoothedPos( tol );
10231   if ( !iSmoothed ) return;
10232
10233   gp_XYZ normal = _normal;
10234   if ( Is( NORMAL_UPDATED ))
10235   {
10236     double minDot = 1;
10237     for ( size_t i = 0; i < _neibors.size(); ++i )
10238     {
10239       if ( _neibors[i]->IsOnFace() )
10240       {
10241         double dot = _normal * _neibors[i]->_normal;
10242         if ( dot < minDot )
10243         {
10244           normal = _neibors[i]->_normal;
10245           minDot = dot;
10246         }
10247       }
10248     }
10249     if ( minDot == 1. )
10250       for ( size_t i = 1; i < _pos.size(); ++i )
10251       {
10252         normal = _pos[i] - _pos[0];
10253         double size = normal.Modulus();
10254         if ( size > RealSmall() )
10255         {
10256           normal /= size;
10257           break;
10258         }
10259       }
10260   }
10261   const double r = 0.2;
10262   for ( int iter = 0; iter < 50; ++iter )
10263   {
10264     double minDot = 1;
10265     for ( size_t i = Max( 1, iSmoothed-1-iter ); i < _pos.size()-1; ++i )
10266     {
10267       gp_XYZ midPos = 0.5 * ( _pos[i-1] + _pos[i+1] );
10268       gp_XYZ newPos = ( 1-r ) * midPos + r * _pos[i];
10269       _pos[i] = newPos;
10270       double midLen = 0.5 * ( segLen[i-1] + segLen[i+1] );
10271       double newLen = ( 1-r ) * midLen + r * segLen[i];
10272       const_cast< double& >( segLen[i] ) = newLen;
10273       // check angle between normal and (_pos[i+1], _pos[i] )
10274       gp_XYZ posDir = _pos[i+1] - _pos[i];
10275       double size   = posDir.SquareModulus();
10276       if ( size > RealSmall() )
10277         minDot = Min( minDot, ( normal * posDir ) * ( normal * posDir ) / size );
10278     }
10279     if ( minDot > 0.5 * 0.5 )
10280       break;
10281   }
10282   return;
10283 }
10284
10285 //================================================================================
10286 /*!
10287  * \brief Print flags
10288  */
10289 //================================================================================
10290
10291 std::string _LayerEdge::DumpFlags() const
10292 {
10293   SMESH_Comment dump;
10294   for ( int flag = 1; flag < 0x1000000; flag *= 2 )
10295     if ( _flags & flag )
10296     {
10297       EFlags f = (EFlags) flag;
10298       switch ( f ) {
10299       case TO_SMOOTH:       dump << "TO_SMOOTH";       break;
10300       case MOVED:           dump << "MOVED";           break;
10301       case SMOOTHED:        dump << "SMOOTHED";        break;
10302       case DIFFICULT:       dump << "DIFFICULT";       break;
10303       case ON_CONCAVE_FACE: dump << "ON_CONCAVE_FACE"; break;
10304       case BLOCKED:         dump << "BLOCKED";         break;
10305       case INTERSECTED:     dump << "INTERSECTED";     break;
10306       case NORMAL_UPDATED:  dump << "NORMAL_UPDATED";  break;
10307       case UPD_NORMAL_CONV: dump << "UPD_NORMAL_CONV"; break;
10308       case MARKED:          dump << "MARKED";          break;
10309       case MULTI_NORMAL:    dump << "MULTI_NORMAL";    break;
10310       case NEAR_BOUNDARY:   dump << "NEAR_BOUNDARY";   break;
10311       case SMOOTHED_C1:     dump << "SMOOTHED_C1";     break;
10312       case DISTORTED:       dump << "DISTORTED";       break;
10313       case RISKY_SWOL:      dump << "RISKY_SWOL";      break;
10314       case SHRUNK:          dump << "SHRUNK";          break;
10315       case UNUSED_FLAG:     dump << "UNUSED_FLAG";     break;
10316       }
10317       dump << " ";
10318     }
10319   cout << dump << endl;
10320   return dump;
10321 }
10322
10323
10324 //================================================================================
10325 /*!
10326  * \brief Create layers of prisms
10327  */
10328 //================================================================================
10329
10330 bool _ViscousBuilder::refine(_SolidData& data)
10331 {
10332   SMESH_MesherHelper& helper = data.GetHelper();
10333   helper.SetElementsOnShape(false);
10334
10335   Handle(Geom_Curve) curve;
10336   Handle(ShapeAnalysis_Surface) surface;
10337   TopoDS_Edge geomEdge;
10338   TopoDS_Face geomFace;
10339   TopLoc_Location loc;
10340   double f,l, u = 0;
10341   gp_XY uv;
10342   vector< gp_XYZ > pos3D;
10343   bool isOnEdge, isTooConvexFace = false;
10344   TGeomID prevBaseId = -1;
10345   TNode2Edge* n2eMap = 0;
10346   TNode2Edge::iterator n2e;
10347
10348   // Create intermediate nodes on each _LayerEdge
10349
10350   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
10351   {
10352     _EdgesOnShape& eos = data._edgesOnShape[iS];
10353     if ( eos._edges.empty() ) continue;
10354
10355     if ( eos._edges[0]->_nodes.size() < 2 )
10356       continue; // on _noShrinkShapes
10357
10358     // get data of a shrink shape
10359     isOnEdge = false;
10360     geomEdge.Nullify(); geomFace.Nullify();
10361     curve.Nullify(); surface.Nullify();
10362     if ( !eos._sWOL.IsNull() )
10363     {
10364       isOnEdge = ( eos.SWOLType() == TopAbs_EDGE );
10365       if ( isOnEdge )
10366       {
10367         geomEdge = TopoDS::Edge( eos._sWOL );
10368         curve    = BRep_Tool::Curve( geomEdge, loc, f,l);
10369       }
10370       else
10371       {
10372         geomFace = TopoDS::Face( eos._sWOL );
10373         surface  = helper.GetSurface( geomFace );
10374       }
10375     }
10376     else if ( eos.ShapeType() == TopAbs_FACE && eos._toSmooth )
10377     {
10378       geomFace = TopoDS::Face( eos._shape );
10379       surface  = helper.GetSurface( geomFace );
10380       // propagate _toSmooth back to _eosC1, which was unset in findShapesToSmooth()
10381       for ( size_t i = 0; i < eos._eosC1.size(); ++i )
10382         eos._eosC1[ i ]->_toSmooth = true;
10383
10384       isTooConvexFace = false;
10385       if ( _ConvexFace* cf = data.GetConvexFace( eos._shapeID ))
10386         isTooConvexFace = cf->_isTooCurved;
10387     }
10388
10389     vector< double > segLen;
10390     for ( size_t i = 0; i < eos._edges.size(); ++i )
10391     {
10392       _LayerEdge& edge = *eos._edges[i];
10393       if ( edge._pos.size() < 2 )
10394         continue;
10395
10396       // get accumulated length of segments
10397       segLen.resize( edge._pos.size() );
10398       segLen[0] = 0.0;
10399       if ( eos._sWOL.IsNull() )
10400       {
10401         bool useNormal = true;
10402         bool    usePos = false;
10403         bool  smoothed = false;
10404         double   preci = 0.1 * edge._len;
10405         if ( eos._toSmooth && edge._pos.size() > 2 )
10406         {
10407           smoothed = edge.GetSmoothedPos( preci );
10408         }
10409         if ( smoothed )
10410         {
10411           if ( !surface.IsNull() && !isTooConvexFace ) // edge smoothed on FACE
10412           {
10413             useNormal = usePos = false;
10414             gp_Pnt2d uv = helper.GetNodeUV( geomFace, edge._nodes[0] );
10415             for ( size_t j = 1; j < edge._pos.size() && !useNormal; ++j )
10416             {
10417               uv = surface->NextValueOfUV( uv, edge._pos[j], preci );
10418               if ( surface->Gap() < 2. * edge._len )
10419                 segLen[j] = surface->Gap();
10420               else
10421                 useNormal = true;
10422             }
10423           }
10424         }
10425         else if ( !edge.Is( _LayerEdge::NORMAL_UPDATED ))
10426         {
10427 #ifndef __NODES_AT_POS
10428           useNormal = usePos = false;
10429           edge._pos[1] = edge._pos.back();
10430           edge._pos.resize( 2 );
10431           segLen.resize( 2 );
10432           segLen[ 1 ] = edge._len;
10433 #endif
10434         }
10435         if ( useNormal && edge.Is( _LayerEdge::NORMAL_UPDATED ))
10436         {
10437           useNormal = usePos = false;
10438           _LayerEdge tmpEdge; // get original _normal
10439           tmpEdge._nodes.push_back( edge._nodes[0] );
10440           if ( !setEdgeData( tmpEdge, eos, helper, data ))
10441             usePos = true;
10442           else
10443             for ( size_t j = 1; j < edge._pos.size(); ++j )
10444               segLen[j] = ( edge._pos[j] - edge._pos[0] ) * tmpEdge._normal;
10445         }
10446         if ( useNormal )
10447         {
10448           for ( size_t j = 1; j < edge._pos.size(); ++j )
10449             segLen[j] = ( edge._pos[j] - edge._pos[0] ) * edge._normal;
10450         }
10451         if ( usePos )
10452         {
10453           for ( size_t j = 1; j < edge._pos.size(); ++j )
10454             segLen[j] = segLen[j-1] + ( edge._pos[j-1] - edge._pos[j] ).Modulus();
10455         }
10456         else
10457         {
10458           bool swapped = ( edge._pos.size() > 2 );
10459           while ( swapped )
10460           {
10461             swapped = false;
10462             for ( size_t j = 1; j < edge._pos.size()-1; ++j )
10463               if ( segLen[j] > segLen.back() )
10464               {
10465                 segLen.erase( segLen.begin() + j );
10466                 edge._pos.erase( edge._pos.begin() + j );
10467                 --j;
10468               }
10469               else if ( segLen[j] < segLen[j-1] )
10470               {
10471                 std::swap( segLen[j], segLen[j-1] );
10472                 std::swap( edge._pos[j], edge._pos[j-1] );
10473                 swapped = true;
10474               }
10475           }
10476         }
10477         // smooth a path formed by edge._pos
10478 #ifndef __NODES_AT_POS
10479         if (( smoothed ) /*&&
10480             ( eos.ShapeType() == TopAbs_FACE || edge.Is( _LayerEdge::SMOOTHED_C1 ))*/)
10481           edge.SmoothPos( segLen, preci );
10482 #endif
10483       }
10484       else if ( eos._isRegularSWOL ) // usual SWOL
10485       {
10486         if ( edge.Is( _LayerEdge::SMOOTHED ))
10487         {
10488           SMESH_NodeXYZ p0( edge._nodes[0] );
10489           for ( size_t j = 1; j < edge._pos.size(); ++j )
10490           {
10491             gp_XYZ pj = surface->Value( edge._pos[j].X(), edge._pos[j].Y() ).XYZ();
10492             segLen[j] = ( pj - p0 ) * edge._normal;
10493           }
10494         }
10495         else
10496         {
10497           for ( size_t j = 1; j < edge._pos.size(); ++j )
10498             segLen[j] = segLen[j-1] + (edge._pos[j-1] - edge._pos[j] ).Modulus();
10499         }
10500       }
10501       else // SWOL is surface with singularities or irregularly parametrized curve
10502       {
10503         pos3D.resize( edge._pos.size() );
10504
10505         if ( !surface.IsNull() )
10506           for ( size_t j = 0; j < edge._pos.size(); ++j )
10507             pos3D[j] = surface->Value( edge._pos[j].X(), edge._pos[j].Y() ).XYZ();
10508         else if ( !curve.IsNull() )
10509           for ( size_t j = 0; j < edge._pos.size(); ++j )
10510             pos3D[j] = curve->Value( edge._pos[j].X() ).XYZ();
10511
10512         for ( size_t j = 1; j < edge._pos.size(); ++j )
10513           segLen[j] = segLen[j-1] + ( pos3D[j-1] - pos3D[j] ).Modulus();
10514       }
10515
10516       // allocate memory for new nodes if it is not yet refined
10517       const SMDS_MeshNode* tgtNode = edge._nodes.back();
10518       if ( edge._nodes.size() == 2 )
10519       {
10520 #ifdef __NODES_AT_POS
10521         int nbNodes = edge._pos.size();
10522 #else
10523         int nbNodes = eos._hyp.GetNumberLayers() + 1;
10524 #endif
10525         edge._nodes.resize( nbNodes, 0 );
10526         edge._nodes[1] = 0;
10527         edge._nodes.back() = tgtNode;
10528       }
10529       // restore shapePos of the last node by already treated _LayerEdge of another _SolidData
10530       const TGeomID baseShapeId = edge._nodes[0]->getshapeId();
10531       if ( baseShapeId != prevBaseId )
10532       {
10533         map< TGeomID, TNode2Edge* >::iterator s2ne = data._s2neMap.find( baseShapeId );
10534         n2eMap = ( s2ne == data._s2neMap.end() ) ? 0 : s2ne->second;
10535         prevBaseId = baseShapeId;
10536       }
10537       _LayerEdge* edgeOnSameNode = 0;
10538       bool        useExistingPos = false;
10539       if ( n2eMap && (( n2e = n2eMap->find( edge._nodes[0] )) != n2eMap->end() ))
10540       {
10541         edgeOnSameNode = n2e->second;
10542         useExistingPos = ( edgeOnSameNode->_len < edge._len ||
10543                            segLen[0] == segLen.back() ); // too short inflation step (bos #20643)
10544         const gp_XYZ& otherTgtPos = edgeOnSameNode->_pos.back();
10545         SMDS_PositionPtr  lastPos = tgtNode->GetPosition();
10546         if ( isOnEdge )
10547         {
10548           SMDS_EdgePositionPtr epos = lastPos;
10549           epos->SetUParameter( otherTgtPos.X() );
10550         }
10551         else
10552         {
10553           SMDS_FacePositionPtr fpos = lastPos;
10554           fpos->SetUParameter( otherTgtPos.X() );
10555           fpos->SetVParameter( otherTgtPos.Y() );
10556         }
10557       }
10558
10559       // create intermediate nodes
10560       const double      h0 = eos._hyp.Get1stLayerThickness( segLen.back() );
10561       const double zeroLen = std::numeric_limits<double>::min();
10562       double hSum = 0, hi = h0/eos._hyp.GetStretchFactor();
10563       size_t iSeg = 1;
10564       for ( size_t iStep = 1; iStep < edge._nodes.size(); ++iStep )
10565       {
10566         // compute an intermediate position
10567         hi *= eos._hyp.GetStretchFactor();
10568         hSum += hi;
10569         while ( hSum > segLen[iSeg] && iSeg < segLen.size()-1 )
10570           ++iSeg;
10571         int iPrevSeg = iSeg-1;
10572         while ( fabs( segLen[iPrevSeg] - segLen[iSeg]) <= zeroLen && iPrevSeg > 0 )
10573           --iPrevSeg;
10574         double   r = ( segLen[iSeg] - hSum ) / ( segLen[iSeg] - segLen[iPrevSeg] );
10575         gp_Pnt pos = r * edge._pos[iPrevSeg] + (1-r) * edge._pos[iSeg];
10576 #ifdef __NODES_AT_POS
10577         pos = edge._pos[ iStep ];
10578 #endif
10579         SMDS_MeshNode*& node = const_cast< SMDS_MeshNode*& >( edge._nodes[ iStep ]);
10580         if ( !eos._sWOL.IsNull() )
10581         {
10582           // compute XYZ by parameters <pos>
10583           if ( isOnEdge )
10584           {
10585             u = pos.X();
10586             if ( !node )
10587               pos = curve->Value( u ).Transformed(loc);
10588           }
10589           else if ( eos._isRegularSWOL )
10590           {
10591             uv.SetCoord( pos.X(), pos.Y() );
10592             if ( !node )
10593               pos = surface->Value( pos.X(), pos.Y() );
10594           }
10595           else
10596           {
10597             uv.SetCoord( pos.X(), pos.Y() );
10598             gp_Pnt p = r * pos3D[ iPrevSeg ] + (1-r) * pos3D[ iSeg ];
10599             uv = surface->NextValueOfUV( uv, p, BRep_Tool::Tolerance( geomFace )).XY();
10600             if ( !node )
10601               pos = surface->Value( uv );
10602           }
10603         }
10604         // create or update the node
10605         if ( !node )
10606         {
10607           node = helper.AddNode( pos.X(), pos.Y(), pos.Z());
10608           if ( !eos._sWOL.IsNull() )
10609           {
10610             if ( isOnEdge )
10611               getMeshDS()->SetNodeOnEdge( node, geomEdge, u );
10612             else
10613               getMeshDS()->SetNodeOnFace( node, geomFace, uv.X(), uv.Y() );
10614           }
10615           else
10616           {
10617             getMeshDS()->SetNodeInVolume( node, helper.GetSubShapeID() );
10618           }
10619         }
10620         else
10621         {
10622           if ( !eos._sWOL.IsNull() )
10623           {
10624             // make average pos from new and current parameters
10625             if ( isOnEdge )
10626             {
10627               //u = 0.5 * ( u + helper.GetNodeU( geomEdge, node ));
10628               if ( useExistingPos )
10629                 u = helper.GetNodeU( geomEdge, node );
10630               pos = curve->Value( u ).Transformed(loc);
10631
10632               SMDS_EdgePositionPtr epos = node->GetPosition();
10633               epos->SetUParameter( u );
10634             }
10635             else
10636             {
10637               //uv = 0.5 * ( uv + helper.GetNodeUV( geomFace, node ));
10638               if ( useExistingPos )
10639                 uv = helper.GetNodeUV( geomFace, node );
10640               pos = surface->Value( uv );
10641
10642               SMDS_FacePositionPtr fpos = node->GetPosition();
10643               fpos->SetUParameter( uv.X() );
10644               fpos->SetVParameter( uv.Y() );
10645             }
10646           }
10647           node->setXYZ( pos.X(), pos.Y(), pos.Z() );
10648         }
10649       } // loop on edge._nodes
10650
10651       if ( !eos._sWOL.IsNull() ) // prepare for shrink()
10652       {
10653         if ( isOnEdge )
10654           edge._pos.back().SetCoord( u, 0,0);
10655         else
10656           edge._pos.back().SetCoord( uv.X(), uv.Y() ,0);
10657
10658         if ( edgeOnSameNode )
10659           edgeOnSameNode->_pos.back() = edge._pos.back();
10660       }
10661
10662     } // loop on eos._edges to create nodes
10663
10664
10665     if ( !getMeshDS()->IsEmbeddedMode() )
10666       // Log node movement
10667       for ( size_t i = 0; i < eos._edges.size(); ++i )
10668       {
10669         SMESH_TNodeXYZ p ( eos._edges[i]->_nodes.back() );
10670         getMeshDS()->MoveNode( p._node, p.X(), p.Y(), p.Z() );
10671       }
10672   }
10673
10674
10675   // Create volumes
10676
10677   helper.SetElementsOnShape(true);
10678
10679   vector< vector<const SMDS_MeshNode*>* > nnVec;
10680   set< vector<const SMDS_MeshNode*>* >    nnSet;
10681   set< int >                       degenEdgeInd;
10682   vector<const SMDS_MeshElement*>     degenVols;
10683
10684   TopExp_Explorer exp( data._solid, TopAbs_FACE );
10685   for ( ; exp.More(); exp.Next() )
10686   {
10687     const TGeomID faceID = getMeshDS()->ShapeToIndex( exp.Current() );
10688     if ( data._ignoreFaceIds.count( faceID ))
10689       continue;
10690     _EdgesOnShape*    eos = data.GetShapeEdges( faceID );
10691     SMDS_MeshGroup* group = StdMeshers_ViscousLayers::CreateGroup( eos->_hyp.GetGroupName(),
10692                                                                    *helper.GetMesh(),
10693                                                                    SMDSAbs_Volume );
10694     std::vector< const SMDS_MeshElement* > vols;
10695     const bool isReversedFace = data._reversedFaceIds.count( faceID );
10696     SMESHDS_SubMesh*    fSubM = getMeshDS()->MeshElements( exp.Current() );
10697     SMDS_ElemIteratorPtr  fIt = fSubM->GetElements();
10698     while ( fIt->more() )
10699     {
10700       const SMDS_MeshElement* face = fIt->next();
10701       const int            nbNodes = face->NbCornerNodes();
10702       nnVec.resize( nbNodes );
10703       nnSet.clear();
10704       degenEdgeInd.clear();
10705       size_t maxZ = 0, minZ = std::numeric_limits<size_t>::max();
10706       SMDS_NodeIteratorPtr nIt = face->nodeIterator();
10707       for ( int iN = 0; iN < nbNodes; ++iN )
10708       {
10709         const SMDS_MeshNode* n = nIt->next();
10710         _LayerEdge*       edge = data._n2eMap[ n ];
10711         const int i = isReversedFace ? nbNodes-1-iN : iN;
10712         nnVec[ i ] = & edge->_nodes;
10713         maxZ = std::max( maxZ, nnVec[ i ]->size() );
10714         minZ = std::min( minZ, nnVec[ i ]->size() );
10715
10716         if ( helper.HasDegeneratedEdges() )
10717           nnSet.insert( nnVec[ i ]);
10718       }
10719
10720       if ( maxZ == 0 )
10721         continue;
10722       if ( 0 < nnSet.size() && nnSet.size() < 3 )
10723         continue;
10724
10725       vols.clear();
10726       const SMDS_MeshElement* vol;
10727
10728       switch ( nbNodes )
10729       {
10730       case 3: // TRIA
10731       {
10732         // PENTA
10733         for ( size_t iZ = 1; iZ < minZ; ++iZ )
10734         {
10735           vol = helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1], (*nnVec[2])[iZ-1],
10736                                   (*nnVec[0])[iZ],   (*nnVec[1])[iZ],   (*nnVec[2])[iZ]);
10737           vols.push_back( vol );
10738         }
10739
10740         for ( size_t iZ = minZ; iZ < maxZ; ++iZ )
10741         {
10742           for ( int iN = 0; iN < nbNodes; ++iN )
10743             if ( nnVec[ iN ]->size() < iZ+1 )
10744               degenEdgeInd.insert( iN );
10745
10746           if ( degenEdgeInd.size() == 1 )  // PYRAM
10747           {
10748             int i2 = *degenEdgeInd.begin();
10749             int i0 = helper.WrapIndex( i2 - 1, nbNodes );
10750             int i1 = helper.WrapIndex( i2 + 1, nbNodes );
10751             vol = helper.AddVolume( (*nnVec[i0])[iZ-1], (*nnVec[i1])[iZ-1],
10752                                     (*nnVec[i1])[iZ  ], (*nnVec[i0])[iZ  ], (*nnVec[i2]).back());
10753             vols.push_back( vol );
10754           }
10755           else  // TETRA
10756           {
10757             int i3 = !degenEdgeInd.count(0) ? 0 : !degenEdgeInd.count(1) ? 1 : 2;
10758             vol = helper.AddVolume( (*nnVec[  0 ])[ i3 == 0 ? iZ-1 : nnVec[0]->size()-1 ],
10759                                     (*nnVec[  1 ])[ i3 == 1 ? iZ-1 : nnVec[1]->size()-1 ],
10760                                     (*nnVec[  2 ])[ i3 == 2 ? iZ-1 : nnVec[2]->size()-1 ],
10761                                     (*nnVec[ i3 ])[ iZ ]);
10762             vols.push_back( vol );
10763           }
10764         }
10765         break; // TRIA
10766       }
10767       case 4: // QUAD
10768       {
10769         // HEX
10770         for ( size_t iZ = 1; iZ < minZ; ++iZ )
10771         {
10772           vol = helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1],
10773                                   (*nnVec[2])[iZ-1], (*nnVec[3])[iZ-1],
10774                                   (*nnVec[0])[iZ],   (*nnVec[1])[iZ],
10775                                   (*nnVec[2])[iZ],   (*nnVec[3])[iZ]);
10776           vols.push_back( vol );
10777         }
10778
10779         for ( size_t iZ = minZ; iZ < maxZ; ++iZ )
10780         {
10781           for ( int iN = 0; iN < nbNodes; ++iN )
10782             if ( nnVec[ iN ]->size() < iZ+1 )
10783               degenEdgeInd.insert( iN );
10784
10785           switch ( degenEdgeInd.size() )
10786           {
10787           case 2: // PENTA
10788           {
10789             int i2 = *degenEdgeInd.begin();
10790             int i3 = *degenEdgeInd.rbegin();
10791             bool ok = ( i3 - i2 == 1 );
10792             if ( i2 == 0 && i3 == 3 ) { i2 = 3; i3 = 0; ok = true; }
10793             int i0 = helper.WrapIndex( i3 + 1, nbNodes );
10794             int i1 = helper.WrapIndex( i0 + 1, nbNodes );
10795
10796             vol = helper.AddVolume( nnVec[i3]->back(), (*nnVec[i0])[iZ], (*nnVec[i0])[iZ-1],
10797                                     nnVec[i2]->back(), (*nnVec[i1])[iZ], (*nnVec[i1])[iZ-1]);
10798             vols.push_back( vol );
10799             if ( !ok && vol )
10800               degenVols.push_back( vol );
10801           }
10802           break;
10803
10804           default: // degen HEX
10805           {
10806             vol = helper.AddVolume( nnVec[0]->size() > iZ-1 ? (*nnVec[0])[iZ-1] : nnVec[0]->back(),
10807                                     nnVec[1]->size() > iZ-1 ? (*nnVec[1])[iZ-1] : nnVec[1]->back(),
10808                                     nnVec[2]->size() > iZ-1 ? (*nnVec[2])[iZ-1] : nnVec[2]->back(),
10809                                     nnVec[3]->size() > iZ-1 ? (*nnVec[3])[iZ-1] : nnVec[3]->back(),
10810                                     nnVec[0]->size() > iZ   ? (*nnVec[0])[iZ]   : nnVec[0]->back(),
10811                                     nnVec[1]->size() > iZ   ? (*nnVec[1])[iZ]   : nnVec[1]->back(),
10812                                     nnVec[2]->size() > iZ   ? (*nnVec[2])[iZ]   : nnVec[2]->back(),
10813                                     nnVec[3]->size() > iZ   ? (*nnVec[3])[iZ]   : nnVec[3]->back());
10814             vols.push_back( vol );
10815             degenVols.push_back( vol );
10816           }
10817           }
10818         }
10819         break; // HEX
10820       }
10821       default:
10822         return error("Not supported type of element", data._index);
10823
10824       } // switch ( nbNodes )
10825
10826       if ( group )
10827         for ( size_t i = 0; i < vols.size(); ++i )
10828           group->Add( vols[ i ]);
10829
10830     } // while ( fIt->more() )
10831   } // loop on FACEs
10832
10833   if ( !degenVols.empty() )
10834   {
10835     SMESH_ComputeErrorPtr& err = _mesh->GetSubMesh( data._solid )->GetComputeError();
10836     if ( !err || err->IsOK() )
10837     {
10838       SMESH_BadInputElements* badElems =
10839         new SMESH_BadInputElements( getMeshDS(), COMPERR_WARNING, "Bad quality volumes created" );
10840       badElems->myBadElements.insert( badElems->myBadElements.end(),
10841                                       degenVols.begin(),degenVols.end() );
10842       err.reset( badElems );
10843     }
10844   }
10845
10846   return true;
10847 }
10848
10849 namespace VISCOUS_3D
10850 {
10851   struct ShrinkFace;
10852   //--------------------------------------------------------------------------------
10853   /*!
10854    * \brief Pair of periodic FACEs
10855    */
10856   struct PeriodicFaces
10857   {
10858     typedef StdMeshers_ProjectionUtils::TrsfFinder3D Trsf;
10859
10860     ShrinkFace*  _shriFace[2];
10861     TNodeNodeMap _nnMap;
10862     Trsf         _trsf;
10863
10864     PeriodicFaces( ShrinkFace* sf1, ShrinkFace* sf2 ): _shriFace{ sf1, sf2 } {}
10865     bool IncludeShrunk( const TopoDS_Face& face, const TopTools_MapOfShape& shrunkFaces ) const;
10866     bool MoveNodes( const TopoDS_Face& tgtFace );
10867     void Clear() { _nnMap.clear(); }
10868     bool IsEmpty() const { return _nnMap.empty(); }
10869   };
10870
10871   //--------------------------------------------------------------------------------
10872   /*!
10873    * \brief Shrink FACE data used to find periodic FACEs
10874    */
10875   struct ShrinkFace
10876   {
10877     // ................................................................................
10878     struct BndPart //!< part of FACE boundary, either shrink or no-shrink
10879     {
10880       bool                         _isShrink, _isReverse;
10881       int                          _nbSegments;
10882       AverageHyp*                  _hyp;
10883       std::vector< SMESH_NodeXYZ > _nodes;
10884       TopAbs_ShapeEnum             _vertSWOLType[2]; // shrink part includes VERTEXes
10885       AverageHyp*                  _vertHyp[2];
10886       double                       _edgeWOLLen[2]; // length of wol EDGE
10887       double                       _tol; // to compare _edgeWOLLen's
10888
10889       BndPart():
10890         _isShrink(0), _isReverse(0), _nbSegments(0), _hyp(0),
10891         _vertSWOLType{ TopAbs_WIRE, TopAbs_WIRE }, _vertHyp{ 0, 0 }, _edgeWOLLen{ 0., 0.}
10892       {}
10893
10894       bool IsEqualLengthEWOL( const BndPart& other ) const
10895       {
10896         return ( std::abs( _edgeWOLLen[0] - other._edgeWOLLen[0] ) < _tol &&
10897                  std::abs( _edgeWOLLen[1] - other._edgeWOLLen[1] ) < _tol );
10898       }
10899
10900       bool operator==( const BndPart& other ) const
10901       {
10902         return ( _isShrink       == other._isShrink &&
10903                  _nbSegments     == other._nbSegments &&
10904                  _nodes.size()   == other._nodes.size() &&
10905                  vertSWOLType1() == other.vertSWOLType1() &&
10906                  vertSWOLType2() == other.vertSWOLType2() &&
10907                  (( !_isShrink ) ||
10908                   ( *_hyp        == *other._hyp &&
10909                     vertHyp1()   == other.vertHyp1() &&
10910                     vertHyp2()   == other.vertHyp2() &&
10911                     IsEqualLengthEWOL( other )))
10912                  );
10913       }
10914       bool CanAppend( const BndPart& other )
10915       {
10916         return ( _isShrink  == other._isShrink  &&
10917                  (( !_isShrink ) ||
10918                   ( *_hyp        == *other._hyp &&
10919                     *_hyp        == vertHyp2()  &&
10920                     vertHyp2()   == other.vertHyp1() ))
10921                  );
10922       }
10923       void Append( const BndPart& other )
10924       {
10925         _nbSegments += other._nbSegments;
10926         bool hasCommonNode = ( _nodes.back()->GetID() == other._nodes.front()->GetID() );
10927         _nodes.insert( _nodes.end(), other._nodes.begin() + hasCommonNode, other._nodes.end() );
10928         _vertSWOLType[1] = other._vertSWOLType[1];
10929         if ( _isShrink ) {
10930           _vertHyp[1]    = other._vertHyp[1];
10931           _edgeWOLLen[1] = other._edgeWOLLen[1];
10932         }
10933       }
10934       const SMDS_MeshNode* Node(size_t i)  const
10935       {
10936         return _nodes[ _isReverse ? ( _nodes.size() - 1 - i ) : i ]._node;
10937       }
10938       void Reverse() { _isReverse = !_isReverse; }
10939       const TopAbs_ShapeEnum& vertSWOLType1() const { return _vertSWOLType[ _isReverse  ]; }
10940       const TopAbs_ShapeEnum& vertSWOLType2() const { return _vertSWOLType[ !_isReverse ]; }
10941       const AverageHyp&       vertHyp1()      const { return *(_vertHyp[ _isReverse  ]); }
10942       const AverageHyp&       vertHyp2()      const { return *(_vertHyp[ !_isReverse ]); }
10943     };
10944     // ................................................................................
10945
10946     SMESH_subMesh*       _subMesh;
10947     _SolidData*          _data1;
10948     _SolidData*          _data2;
10949
10950     std::list< BndPart > _boundary;
10951     int                  _boundarySize, _nbBoundaryParts;
10952
10953     void Init( SMESH_subMesh* sm, _SolidData* sd1, _SolidData* sd2 )
10954     {
10955       _subMesh = sm; _data1 = sd1; _data2 = sd2;
10956     }
10957     bool IsSame( const TopoDS_Face& face ) const
10958     {
10959       return _subMesh->GetSubShape().IsSame( face );
10960     }
10961     bool IsShrunk( const TopTools_MapOfShape& shrunkFaces ) const
10962     {
10963       return shrunkFaces.Contains( _subMesh->GetSubShape() );
10964     }
10965
10966     //================================================================================
10967     /*!
10968      * Check if meshes on two FACEs are equal
10969      */
10970     bool IsPeriodic( ShrinkFace& other, PeriodicFaces& periodic )
10971     {
10972       if ( !IsSameNbElements( other ))
10973         return false;
10974
10975       this->SetBoundary();
10976       other.SetBoundary();
10977       if ( this->_boundarySize    != other._boundarySize ||
10978            this->_nbBoundaryParts != other._nbBoundaryParts )
10979         return false;
10980
10981       for ( int isReverse = 0; isReverse < 2; ++isReverse )
10982       {
10983         if ( isReverse )
10984           Reverse( _boundary );
10985
10986         // check boundaries
10987         bool equalBoundary = false;
10988         for ( int iP = 0; iP < _nbBoundaryParts &&  !equalBoundary; ++iP )
10989         {
10990           if ( ! ( equalBoundary = ( this->_boundary == other._boundary )))
10991             // set first part at end
10992             _boundary.splice( _boundary.end(), _boundary, _boundary.begin() );
10993         }
10994         if ( !equalBoundary )
10995           continue;
10996
10997         // check connectivity
10998         std::set<const SMDS_MeshElement*> elemsThis, elemsOther;
10999         this->GetElements( elemsThis  );
11000         other.GetElements( elemsOther );
11001         SMESH_MeshEditor::Sew_Error err =
11002           SMESH_MeshEditor::FindMatchingNodes( elemsThis, elemsOther,
11003                                                this->_boundary.front().Node(0),
11004                                                other._boundary.front().Node(0),
11005                                                this->_boundary.front().Node(1),
11006                                                other._boundary.front().Node(1),
11007                                                periodic._nnMap );
11008         if ( err != SMESH_MeshEditor::SEW_OK )
11009           continue;
11010
11011         // check node positions
11012         std::vector< gp_XYZ > srcPnts, tgtPnts;
11013         this->GetBoundaryPoints( srcPnts );
11014         other.GetBoundaryPoints( tgtPnts );
11015         if ( !periodic._trsf.Solve( srcPnts, tgtPnts )) {
11016           continue;
11017         }
11018         double tol = std::numeric_limits<double>::max(); // tolerance by segment size
11019         for ( size_t i = 1; i < srcPnts.size(); ++i ) {
11020           tol = Min( tol, ( srcPnts[i-1] - srcPnts[i] ).SquareModulus() );
11021         }
11022         tol = 0.01 * Sqrt( tol );
11023         for ( BndPart& boundary : _boundary ) { // tolerance by VL thickness
11024           if ( boundary._isShrink )
11025             tol = Min( tol, boundary._hyp->Get1stLayerThickness() / 50. );
11026         }
11027         bool nodeCoincide = true;
11028         TNodeNodeMap::iterator n2n = periodic._nnMap.begin();
11029         for ( ; n2n != periodic._nnMap.end() &&  nodeCoincide; ++n2n )
11030         {
11031           SMESH_NodeXYZ nSrc = n2n->first;
11032           SMESH_NodeXYZ nTgt = n2n->second;
11033           gp_XYZ pTgt = periodic._trsf.Transform( nSrc );
11034           nodeCoincide = (( pTgt - nTgt ).SquareModulus() < tol * tol );
11035         }
11036         if ( nodeCoincide )
11037           return true;
11038       }
11039       return false;
11040     }
11041
11042     bool IsSameNbElements( ShrinkFace& other ) // check number of mesh faces
11043     {
11044       SMESHDS_SubMesh* sm1 = this->_subMesh->GetSubMeshDS();
11045       SMESHDS_SubMesh* sm2 = other._subMesh->GetSubMeshDS();
11046       return ( sm1->NbElements() == sm2->NbElements() &&
11047                sm1->NbNodes()    == sm2->NbNodes() );
11048     }
11049
11050     void Reverse( std::list< BndPart >& boundary )
11051     {
11052       boundary.reverse();
11053       for ( std::list< BndPart >::iterator part = boundary.begin(); part != boundary.end(); ++part )
11054         part->Reverse();
11055     }
11056
11057     void SetBoundary()
11058     {
11059       if ( !_boundary.empty() )
11060         return;
11061
11062       TopoDS_Face F = TopoDS::Face( _subMesh->GetSubShape() );
11063       if ( F.Orientation() >= TopAbs_INTERNAL ) F.Orientation( TopAbs_FORWARD );
11064       std::list< TopoDS_Edge > edges;
11065       std::list< int > nbEdgesInWire;
11066       /*int nbWires =*/ SMESH_Block::GetOrderedEdges (F, edges, nbEdgesInWire);
11067
11068       // std::list< TopoDS_Edge >::iterator edgesEnd = edges.end();
11069       // if ( nbWires > 1 ) {
11070       //   edgesEnd = edges.begin();
11071       //   std::advance( edgesEnd, nbEdgesInWire.front() );
11072       // }
11073       StdMeshers_FaceSide fSide( F, edges, _subMesh->GetFather(),
11074                                  /*fwd=*/true, /*skipMedium=*/true );
11075       _boundarySize = fSide.NbSegments();
11076
11077       //TopoDS_Vertex vv[2];
11078       //std::list< TopoDS_Edge >::iterator edgeIt = edges.begin();
11079       for ( int iE = 0; iE < nbEdgesInWire.front(); ++iE )
11080       {
11081         BndPart bndPart;
11082
11083         std::vector<const SMDS_MeshNode*> nodes = fSide.GetOrderedNodes( iE );
11084         bndPart._nodes.assign( nodes.begin(), nodes.end() );
11085         bndPart._nbSegments = bndPart._nodes.size() - 1;
11086
11087         _EdgesOnShape*  eos = _data1->GetShapeEdges( fSide.EdgeID( iE ));
11088
11089         bndPart._isShrink = ( eos->SWOLType() == TopAbs_FACE );
11090         if ( bndPart._isShrink )
11091           if ((           _data1->_noShrinkShapes.count( eos->_shapeID )) ||
11092               ( _data2 && _data2->_noShrinkShapes.count( eos->_shapeID )))
11093             bndPart._isShrink = false;
11094
11095         if ( bndPart._isShrink )
11096         {
11097           bndPart._hyp = & eos->_hyp;
11098           _EdgesOnShape* eov[2] = { _data1->GetShapeEdges( fSide.FirstVertex( iE )),
11099                                     _data1->GetShapeEdges( fSide.LastVertex ( iE )) };
11100           for ( int iV = 0; iV < 2; ++iV )
11101           {
11102             bndPart._vertHyp     [iV] = & eov[iV]->_hyp;
11103             bndPart._vertSWOLType[iV] = eov[iV]->SWOLType();
11104             if ( _data1->_noShrinkShapes.count( eov[iV]->_shapeID ))
11105               bndPart._vertSWOLType[iV] = TopAbs_SHAPE;
11106             if ( _data2 && bndPart._vertSWOLType[iV] != TopAbs_SHAPE )
11107             {
11108               eov[iV] = _data2->GetShapeEdges( iV ? fSide.LastVertex(iE) : fSide.FirstVertex(iE ));
11109               if ( _data2->_noShrinkShapes.count( eov[iV]->_shapeID ))
11110                 bndPart._vertSWOLType[iV] = TopAbs_SHAPE;
11111               else if ( eov[iV]->SWOLType() > bndPart._vertSWOLType[iV] )
11112                 bndPart._vertSWOLType[iV] = eov[iV]->SWOLType();
11113             }
11114           }
11115           bndPart._edgeWOLLen[0] = fSide.EdgeLength( iE - 1 );
11116           bndPart._edgeWOLLen[1] = fSide.EdgeLength( iE + 1 );
11117
11118           bndPart._tol = std::numeric_limits<double>::max(); // tolerance by segment size
11119           for ( size_t i = 1; i < bndPart._nodes.size(); ++i )
11120             bndPart._tol = Min( bndPart._tol,
11121                                 ( bndPart._nodes[i-1] - bndPart._nodes[i] ).SquareModulus() );
11122         }
11123
11124         if ( _boundary.empty() || ! _boundary.back().CanAppend( bndPart ))
11125           _boundary.push_back( bndPart );
11126         else
11127           _boundary.back().Append( bndPart );
11128       }
11129
11130       _nbBoundaryParts = _boundary.size();
11131       if ( _nbBoundaryParts > 1 && _boundary.front()._isShrink == _boundary.back()._isShrink )
11132       {
11133         _boundary.back().Append( _boundary.front() );
11134         _boundary.pop_front();
11135         --_nbBoundaryParts;
11136       }
11137     }
11138
11139     void GetElements( std::set<const SMDS_MeshElement*>& theElems)
11140     {
11141       if ( SMESHDS_SubMesh* sm = _subMesh->GetSubMeshDS() )
11142         for ( SMDS_ElemIteratorPtr fIt = sm->GetElements(); fIt->more(); )
11143           theElems.insert( theElems.end(), fIt->next() );
11144
11145       return ;
11146     }
11147
11148     void GetBoundaryPoints( std::vector< gp_XYZ >& points )
11149     {
11150       points.reserve( _boundarySize );
11151       size_t  nb = _boundary.rbegin()->_nodes.size();
11152       smIdType lastID = _boundary.rbegin()->Node( nb - 1 )->GetID();
11153       std::list< BndPart >::const_iterator part = _boundary.begin();
11154       for ( ; part != _boundary.end(); ++part )
11155       {
11156         size_t nb = part->_nodes.size();
11157         size_t iF = 0;
11158         size_t iR = nb - 1;
11159         size_t* i = part->_isReverse ? &iR : &iF;
11160         if ( part->_nodes[ *i ]->GetID() == lastID )
11161           ++iF, --iR;
11162         for ( ; iF < nb; ++iF, --iR )
11163           points.push_back( part->_nodes[ *i ]);
11164         --iF, ++iR;
11165         lastID = part->_nodes[ *i ]->GetID();
11166       }
11167     }
11168   }; // struct ShrinkFace
11169
11170   //--------------------------------------------------------------------------------
11171   /*!
11172    * \brief Periodic FACEs
11173    */
11174   struct Periodicity
11175   {
11176     std::vector< ShrinkFace >    _shrinkFaces;
11177     std::vector< PeriodicFaces > _periodicFaces;
11178
11179     PeriodicFaces* GetPeriodic( const TopoDS_Face& face, const TopTools_MapOfShape& shrunkFaces )
11180     {
11181       for ( size_t i = 0; i < _periodicFaces.size(); ++i )
11182         if ( _periodicFaces[ i ].IncludeShrunk( face, shrunkFaces ))
11183           return & _periodicFaces[ i ];
11184       return 0;
11185     }
11186     void ClearPeriodic( const TopoDS_Face& face )
11187     {
11188       for ( size_t i = 0; i < _periodicFaces.size(); ++i )
11189         if ( _periodicFaces[ i ]._shriFace[0]->IsSame( face ) ||
11190              _periodicFaces[ i ]._shriFace[1]->IsSame( face ))
11191           _periodicFaces[ i ].Clear();
11192     }
11193   };
11194
11195   //================================================================================
11196   /*!
11197    * Check if a pair includes the given FACE and the other FACE is already shrunk
11198    */
11199   bool PeriodicFaces::IncludeShrunk( const TopoDS_Face&         face,
11200                                      const TopTools_MapOfShape& shrunkFaces ) const
11201   {
11202     if ( IsEmpty() ) return false;
11203     return (( _shriFace[0]->IsSame( face ) && _shriFace[1]->IsShrunk( shrunkFaces )) ||
11204             ( _shriFace[1]->IsSame( face ) && _shriFace[0]->IsShrunk( shrunkFaces )));
11205   }
11206
11207   //================================================================================
11208   /*!
11209    * Make equal meshes on periodic faces by moving corresponding nodes
11210    */
11211   bool PeriodicFaces::MoveNodes( const TopoDS_Face& tgtFace )
11212   {
11213     int iTgt = _shriFace[1]->IsSame( tgtFace );
11214     int iSrc = 1 - iTgt;
11215
11216     _SolidData* dataSrc = _shriFace[iSrc]->_data1;
11217     _SolidData* dataTgt = _shriFace[iTgt]->_data1;
11218
11219     Trsf * trsf = & _trsf, trsfInverse;
11220     if ( iSrc != 0 )
11221     {
11222       trsfInverse = _trsf;
11223       if ( !trsfInverse.Invert())
11224         return false;
11225       trsf = &trsfInverse;
11226     }
11227     SMESHDS_Mesh* meshDS = dataSrc->GetHelper().GetMeshDS();
11228
11229     dumpFunction(SMESH_Comment("periodicMoveNodes_F")
11230                                << _shriFace[iSrc]->_subMesh->GetId() << "_F"
11231                                << _shriFace[iTgt]->_subMesh->GetId() );
11232     TNode2Edge::iterator n2e;
11233     TNodeNodeMap::iterator n2n = _nnMap.begin();
11234     for ( ; n2n != _nnMap.end(); ++n2n )
11235     {
11236       const SMDS_MeshNode* const* nn = & n2n->first;
11237       const SMDS_MeshNode*      nSrc = nn[ iSrc ];
11238       const SMDS_MeshNode*      nTgt = nn[ iTgt ];
11239
11240       if (( nSrc->GetPosition()->GetDim() == 2 ) ||
11241           (( n2e = dataSrc->_n2eMap.find( nSrc )) == dataSrc->_n2eMap.end() ))
11242       {
11243         SMESH_NodeXYZ pSrc = nSrc;
11244         gp_XYZ pTgt = trsf->Transform( pSrc );
11245         meshDS->MoveNode( nTgt, pTgt.X(), pTgt.Y(), pTgt.Z() );
11246       }
11247       else
11248       {
11249         _LayerEdge* leSrc = n2e->second;
11250         n2e = dataTgt->_n2eMap.find( nTgt );
11251         if ( n2e == dataTgt->_n2eMap.end() )
11252           break;
11253         _LayerEdge* leTgt = n2e->second;
11254         if ( leSrc->_nodes.size() != leTgt->_nodes.size() )
11255           break;
11256         for ( size_t iN = 1; iN < leSrc->_nodes.size(); ++iN )
11257         {
11258           SMESH_NodeXYZ pSrc = leSrc->_nodes[ iN ];
11259           gp_XYZ pTgt = trsf->Transform( pSrc );
11260           meshDS->MoveNode( leTgt->_nodes[ iN ], pTgt.X(), pTgt.Y(), pTgt.Z() );
11261
11262           dumpMove( leTgt->_nodes[ iN ]);
11263         }
11264       }
11265     }
11266     bool done = ( n2n == _nnMap.end() );
11267     debugMsg( "PeriodicFaces::MoveNodes "
11268               << _shriFace[iSrc]->_subMesh->GetId() << " -> "
11269               << _shriFace[iTgt]->_subMesh->GetId() << " -- "
11270               << ( done ? "DONE" : "FAIL"));
11271     dumpFunctionEnd();
11272
11273     return done;
11274   }
11275 } // namespace VISCOUS_3D; Periodicity part
11276
11277
11278 //================================================================================
11279 /*!
11280  * \brief Find FACEs to shrink, that are equally meshed before shrink (i.e. periodic)
11281  *        and should remain equal after shrink
11282  */
11283 //================================================================================
11284
11285 void _ViscousBuilder::findPeriodicFaces()
11286 {
11287   // make map of (ids of FACEs to shrink mesh on) to (list of _SolidData containing
11288   // _LayerEdge's inflated along FACE or EDGE)
11289   std::map< TGeomID, std::list< _SolidData* > > id2sdMap;
11290   for ( size_t i = 0 ; i < _sdVec.size(); ++i )
11291   {
11292     _SolidData& data = _sdVec[i];
11293     std::map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
11294     for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
11295       if ( s2s->second.ShapeType() == TopAbs_FACE )
11296         id2sdMap[ getMeshDS()->ShapeToIndex( s2s->second )].push_back( &data );
11297   }
11298
11299   _periodicity.reset( new Periodicity );
11300   _periodicity->_shrinkFaces.resize( id2sdMap.size() );
11301
11302   std::map< TGeomID, std::list< _SolidData* > >::iterator id2sdIt = id2sdMap.begin();
11303   for ( size_t i = 0; i < id2sdMap.size(); ++i, ++id2sdIt )
11304   {
11305     _SolidData* sd1 = id2sdIt->second.front();
11306     _SolidData* sd2 = id2sdIt->second.back();
11307     _periodicity->_shrinkFaces[ i ].Init( _mesh->GetSubMeshContaining( id2sdIt->first ), sd1, sd2 );
11308   }
11309
11310   for (   size_t i1 = 0;      i1 < _periodicity->_shrinkFaces.size(); ++i1 )
11311     for ( size_t i2 = i1 + 1; i2 < _periodicity->_shrinkFaces.size(); ++i2 )
11312     {
11313       PeriodicFaces pf( & _periodicity->_shrinkFaces[ i1 ],
11314                         & _periodicity->_shrinkFaces[ i2 ]);
11315       if ( pf._shriFace[0]->IsPeriodic( *pf._shriFace[1], pf ))
11316       {
11317         _periodicity->_periodicFaces.push_back( pf );
11318       }
11319     }
11320   return;
11321 }
11322
11323 //================================================================================
11324 /*!
11325  * \brief Shrink 2D mesh on faces to let space for inflated layers
11326  */
11327 //================================================================================
11328
11329 bool _ViscousBuilder::shrink(_SolidData& theData)
11330 {
11331   // make map of (ids of FACEs to shrink mesh on) to (list of _SolidData containing
11332   // _LayerEdge's inflated along FACE or EDGE)
11333   map< TGeomID, list< _SolidData* > > f2sdMap;
11334   for ( size_t i = 0 ; i < _sdVec.size(); ++i )
11335   {
11336     _SolidData& data = _sdVec[i];
11337     map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
11338     for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
11339       if ( s2s->second.ShapeType() == TopAbs_FACE && !_shrunkFaces.Contains( s2s->second ))
11340       {
11341         f2sdMap[ getMeshDS()->ShapeToIndex( s2s->second )].push_back( &data );
11342
11343         // Put mesh faces on the shrunk FACE to the proxy sub-mesh to avoid
11344         // usage of mesh faces made in addBoundaryElements() by the 3D algo or
11345         // by StdMeshers_QuadToTriaAdaptor
11346         if ( SMESHDS_SubMesh* smDS = getMeshDS()->MeshElements( s2s->second ))
11347         {
11348           SMESH_ProxyMesh::SubMesh* proxySub =
11349             data._proxyMesh->getFaceSubM( TopoDS::Face( s2s->second ), /*create=*/true);
11350           if ( proxySub->NbElements() == 0 )
11351           {
11352             SMDS_ElemIteratorPtr fIt = smDS->GetElements();
11353             while ( fIt->more() )
11354             {
11355               const SMDS_MeshElement* f = fIt->next();
11356               // as a result 3D algo will use elements from proxySub and not from smDS
11357               proxySub->AddElement( f );
11358               f->setIsMarked( true );
11359
11360               // Mark nodes on the FACE to discriminate them from nodes
11361               // added by addBoundaryElements(); marked nodes are to be smoothed while shrink()
11362               for ( int iN = 0, nbN = f->NbNodes(); iN < nbN; ++iN )
11363               {
11364                 const SMDS_MeshNode* n = f->GetNode( iN );
11365                 if ( n->GetPosition()->GetDim() == 2 )
11366                   n->setIsMarked( true );
11367               }
11368             }
11369           }
11370         }
11371       }
11372   }
11373
11374   SMESH_MesherHelper helper( *_mesh );
11375   helper.ToFixNodeParameters( true );
11376
11377   // EDGEs to shrink
11378   map< TGeomID, _Shrinker1D > e2shrMap;
11379   vector< _EdgesOnShape* > subEOS;
11380   vector< _LayerEdge* > lEdges;
11381
11382   // loop on FACEs to shrink mesh on
11383   map< TGeomID, list< _SolidData* > >::iterator f2sd = f2sdMap.begin();
11384   for ( ; f2sd != f2sdMap.end(); ++f2sd )
11385   {
11386     list< _SolidData* > & dataList = f2sd->second;
11387     if ( dataList.front()->_n2eMap.empty() ||
11388          dataList.back() ->_n2eMap.empty() )
11389       continue; // not yet computed
11390     if ( dataList.front() != &theData &&
11391          dataList.back()  != &theData )
11392       continue;
11393
11394     _SolidData&      data = *dataList.front();
11395     _SolidData*     data2 = dataList.size() > 1 ? dataList.back() : 0;
11396     const TopoDS_Face&  F = TopoDS::Face( getMeshDS()->IndexToShape( f2sd->first ));
11397     SMESH_subMesh*     sm = _mesh->GetSubMesh( F );
11398     SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
11399
11400     Handle(Geom_Surface) surface = BRep_Tool::Surface( F );
11401
11402     _shrunkFaces.Add( F );
11403     helper.SetSubShape( F );
11404
11405     // ==============================
11406     // Use periodicity to move nodes
11407     // ==============================
11408
11409     PeriodicFaces* periodic = _periodicity->GetPeriodic( F, _shrunkFaces );
11410     bool movedByPeriod = ( periodic && periodic->MoveNodes( F ));
11411
11412     // ===========================
11413     // Prepare data for shrinking
11414     // ===========================
11415
11416     // Collect nodes to smooth (they are marked at the beginning of this method)
11417     vector < const SMDS_MeshNode* > smoothNodes;
11418
11419     if ( !movedByPeriod )
11420     {
11421       SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
11422       while ( nIt->more() )
11423       {
11424         const SMDS_MeshNode* n = nIt->next();
11425         if ( n->isMarked() )
11426           smoothNodes.push_back( n );
11427       }
11428     }
11429     // Find out face orientation
11430     double refSign = 1;
11431     const set<TGeomID> ignoreShapes;
11432     bool isOkUV;
11433     if ( !smoothNodes.empty() )
11434     {
11435       vector<_Simplex> simplices;
11436       _Simplex::GetSimplices( smoothNodes[0], simplices, ignoreShapes );
11437       helper.GetNodeUV( F, simplices[0]._nPrev, 0, &isOkUV ); // fix UV of simplex nodes
11438       helper.GetNodeUV( F, simplices[0]._nNext, 0, &isOkUV );
11439       gp_XY uv = helper.GetNodeUV( F, smoothNodes[0], 0, &isOkUV );
11440       if ( !simplices[0].IsForward(uv, smoothNodes[0], F, helper, refSign ))
11441         refSign = -1;
11442     }
11443
11444     // Find _LayerEdge's inflated along F
11445     subEOS.clear();
11446     lEdges.clear();
11447     {
11448       SMESH_subMeshIteratorPtr subIt = sm->getDependsOnIterator(/*includeSelf=*/false,
11449                                                                 /*complexFirst=*/true); //!!!
11450       while ( subIt->more() )
11451       {
11452         const TGeomID subID = subIt->next()->GetId();
11453         if ( data._noShrinkShapes.count( subID ))
11454           continue;
11455         _EdgesOnShape* eos = data.GetShapeEdges( subID );
11456         if ( !eos || eos->_sWOL.IsNull() )
11457           if ( data2 ) // check in adjacent SOLID
11458           {
11459             eos = data2->GetShapeEdges( subID );
11460             if ( !eos || eos->_sWOL.IsNull() )
11461               continue;
11462           }
11463         subEOS.push_back( eos );
11464
11465         if ( !movedByPeriod )
11466           for ( size_t i = 0; i < eos->_edges.size(); ++i )
11467           {
11468             lEdges.push_back( eos->_edges[ i ] );
11469             prepareEdgeToShrink( *eos->_edges[ i ], *eos, helper, smDS );
11470           }
11471       }
11472     }
11473
11474     dumpFunction(SMESH_Comment("beforeShrinkFace")<<f2sd->first); // debug
11475     SMDS_ElemIteratorPtr fIt = smDS->GetElements();
11476     while ( fIt->more() )
11477       if ( const SMDS_MeshElement* f = fIt->next() )
11478         dumpChangeNodes( f );
11479     dumpFunctionEnd();
11480
11481     // Replace source nodes by target nodes in mesh faces to shrink
11482     dumpFunction(SMESH_Comment("replNodesOnFace")<<f2sd->first); // debug
11483     const SMDS_MeshNode* nodes[20];
11484     for ( size_t iS = 0; iS < subEOS.size(); ++iS )
11485     {
11486       _EdgesOnShape& eos = * subEOS[ iS ];
11487       for ( size_t i = 0; i < eos._edges.size(); ++i )
11488       {
11489         _LayerEdge& edge = *eos._edges[i];
11490         const SMDS_MeshNode* srcNode = edge._nodes[0];
11491         const SMDS_MeshNode* tgtNode = edge._nodes.back();
11492         SMDS_ElemIteratorPtr fIt = srcNode->GetInverseElementIterator(SMDSAbs_Face);
11493         while ( fIt->more() )
11494         {
11495           const SMDS_MeshElement* f = fIt->next();
11496           if ( !smDS->Contains( f ) || !f->isMarked() )
11497             continue;
11498           SMDS_NodeIteratorPtr nIt = f->nodeIterator();
11499           for ( int iN = 0; nIt->more(); ++iN )
11500           {
11501             const SMDS_MeshNode* n = nIt->next();
11502             nodes[iN] = ( n == srcNode ? tgtNode : n );
11503           }
11504           helper.GetMeshDS()->ChangeElementNodes( f, nodes, f->NbNodes() );
11505           dumpChangeNodes( f );
11506         }
11507       }
11508     }
11509     dumpFunctionEnd();
11510
11511     // find out if a FACE is concave
11512     const bool isConcaveFace = isConcave( F, helper );
11513
11514     // Create _SmoothNode's on face F
11515     vector< _SmoothNode > nodesToSmooth( smoothNodes.size() );
11516     {
11517       dumpFunction(SMESH_Comment("fixUVOnFace")<<f2sd->first); // debug
11518       const bool sortSimplices = isConcaveFace;
11519       for ( size_t i = 0; i < smoothNodes.size(); ++i )
11520       {
11521         const SMDS_MeshNode* n = smoothNodes[i];
11522         nodesToSmooth[ i ]._node = n;
11523         // src nodes must be already replaced by tgt nodes to have tgt nodes in _simplices
11524         _Simplex::GetSimplices( n, nodesToSmooth[ i ]._simplices, ignoreShapes, 0, sortSimplices);
11525         // fix up incorrect uv of nodes on the FACE
11526         helper.GetNodeUV( F, n, 0, &isOkUV);
11527         dumpMove( n );
11528       }
11529       dumpFunctionEnd();
11530     }
11531     //if ( nodesToSmooth.empty() ) continue;
11532
11533     // Find EDGE's to shrink and set simpices to LayerEdge's
11534     set< _Shrinker1D* > eShri1D;
11535     {
11536       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
11537       {
11538         _EdgesOnShape& eos = * subEOS[ iS ];
11539         if ( eos.SWOLType() == TopAbs_EDGE )
11540         {
11541           SMESH_subMesh* edgeSM = _mesh->GetSubMesh( eos._sWOL );
11542           VISCOUS_3D::ToClearSubWithMain( edgeSM, data._solid );
11543           if ( !movedByPeriod )
11544           {
11545             _Shrinker1D& shrinker = e2shrMap[ edgeSM->GetId() ];
11546             eShri1D.insert( & shrinker );
11547             shrinker.AddEdge( eos._edges[0], eos, helper );
11548             // restore params of nodes on EDGE if the EDGE has been already
11549             // shrunk while shrinking other FACE
11550             shrinker.RestoreParams();
11551           }
11552         }
11553         for ( size_t i = 0; i < eos._edges.size(); ++i )
11554         {
11555           _LayerEdge& edge = * eos._edges[i];
11556           _Simplex::GetSimplices( /*tgtNode=*/edge._nodes.back(), edge._simplices, ignoreShapes );
11557
11558           // additionally mark tgt node; only marked nodes will be used in SetNewLength2d()
11559           // not-marked nodes are those added by refine()
11560           edge._nodes.back()->setIsMarked( true );
11561         }
11562       }
11563     }
11564
11565     bool toFixTria = false; // to improve quality of trias by diagonal swap
11566     if ( isConcaveFace && !movedByPeriod )
11567     {
11568       const bool hasTria = _mesh->NbTriangles(), hasQuad = _mesh->NbQuadrangles();
11569       if ( hasTria != hasQuad ) {
11570         toFixTria = hasTria;
11571       }
11572       else {
11573         set<int> nbNodesSet;
11574         SMDS_ElemIteratorPtr fIt = smDS->GetElements();
11575         while ( fIt->more() && nbNodesSet.size() < 2 )
11576           nbNodesSet.insert( fIt->next()->NbCornerNodes() );
11577         toFixTria = ( *nbNodesSet.begin() == 3 );
11578       }
11579     }
11580
11581     // ==================
11582     // Perform shrinking
11583     // ==================
11584
11585     bool shrunk = !movedByPeriod;
11586     int nbBad, shriStep=0, smooStep=0;
11587     _SmoothNode::SmoothType smoothType
11588       = isConcaveFace ? _SmoothNode::ANGULAR : _SmoothNode::LAPLACIAN;
11589     SMESH_Comment errMsg;
11590     while ( shrunk )
11591     {
11592       shriStep++;
11593       // Move boundary nodes (actually just set new UV)
11594       // -----------------------------------------------
11595       dumpFunction(SMESH_Comment("moveBoundaryOnF")<<f2sd->first<<"_st"<<shriStep ); // debug
11596       shrunk = false;
11597       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
11598       {
11599         _EdgesOnShape& eos = * subEOS[ iS ];
11600         for ( size_t i = 0; i < eos._edges.size(); ++i )
11601         {
11602           shrunk |= eos._edges[i]->SetNewLength2d( surface, F, eos, helper );
11603         }
11604       }
11605       dumpFunctionEnd();
11606
11607       // Move nodes on EDGE's
11608       // (XYZ is set as soon as a needed length reached in SetNewLength2d())
11609       set< _Shrinker1D* >::iterator shr = eShri1D.begin();
11610       for ( ; shr != eShri1D.end(); ++shr )
11611         (*shr)->Compute( /*set3D=*/false, helper );
11612
11613       // Smoothing in 2D
11614       // -----------------
11615       int nbNoImpSteps = 0;
11616       bool       moved = true;
11617       nbBad = 1;
11618       while (( nbNoImpSteps < 5 && nbBad > 0) && moved)
11619       {
11620         dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
11621
11622         int oldBadNb = nbBad;
11623         nbBad = 0;
11624         moved = false;
11625         // '% 5' minimizes NB FUNCTIONS on viscous_layers_00/B2 case
11626         _SmoothNode::SmoothType smooTy = ( smooStep % 5 ) ? smoothType : _SmoothNode::LAPLACIAN;
11627         for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
11628         {
11629           moved |= nodesToSmooth[i].Smooth( nbBad, surface, helper, refSign,
11630                                             smooTy, /*set3D=*/isConcaveFace);
11631         }
11632         if ( nbBad < oldBadNb )
11633           nbNoImpSteps = 0;
11634         else
11635           nbNoImpSteps++;
11636
11637         dumpFunctionEnd();
11638       }
11639
11640       errMsg.clear();
11641       if ( nbBad > 0 )
11642         errMsg << "Can't shrink 2D mesh on face " << f2sd->first;
11643       if ( shriStep > 200 )
11644         errMsg << "Infinite loop at shrinking 2D mesh on face " << f2sd->first;
11645       if ( !errMsg.empty() )
11646         break;
11647
11648       // Fix narrow triangles by swapping diagonals
11649       // ---------------------------------------
11650       if ( toFixTria )
11651       {
11652         set<const SMDS_MeshNode*> usedNodes;
11653         fixBadFaces( F, helper, /*is2D=*/true, shriStep, & usedNodes); // swap diagonals
11654
11655         // update working data
11656         set<const SMDS_MeshNode*>::iterator n;
11657         for ( size_t i = 0; i < nodesToSmooth.size() && !usedNodes.empty(); ++i )
11658         {
11659           n = usedNodes.find( nodesToSmooth[ i ]._node );
11660           if ( n != usedNodes.end())
11661           {
11662             _Simplex::GetSimplices( nodesToSmooth[ i ]._node,
11663                                     nodesToSmooth[ i ]._simplices,
11664                                     ignoreShapes, NULL,
11665                                     /*sortSimplices=*/ smoothType == _SmoothNode::ANGULAR );
11666             usedNodes.erase( n );
11667           }
11668         }
11669         for ( size_t i = 0; i < lEdges.size() && !usedNodes.empty(); ++i )
11670         {
11671           n = usedNodes.find( /*tgtNode=*/ lEdges[i]->_nodes.back() );
11672           if ( n != usedNodes.end())
11673           {
11674             _Simplex::GetSimplices( lEdges[i]->_nodes.back(),
11675                                     lEdges[i]->_simplices,
11676                                     ignoreShapes );
11677             usedNodes.erase( n );
11678           }
11679         }
11680       }
11681       // TODO: check effect of this additional smooth
11682       // additional laplacian smooth to increase allowed shrink step
11683       // for ( int st = 1; st; --st )
11684       // {
11685       //   dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
11686       //   for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
11687       //   {
11688       //     nodesToSmooth[i].Smooth( nbBad,surface,helper,refSign,
11689       //                              _SmoothNode::LAPLACIAN,/*set3D=*/false);
11690       //   }
11691       // }
11692
11693     } // while ( shrunk )
11694
11695     if ( !errMsg.empty() ) // Try to re-compute the shrink FACE
11696     {
11697       debugMsg( "Re-compute FACE " << f2sd->first << " because " << errMsg );
11698
11699       // remove faces
11700       SMESHDS_SubMesh* psm = data._proxyMesh->getFaceSubM( F );
11701       {
11702         vector< const SMDS_MeshElement* > facesToRm;
11703         if ( psm )
11704         {
11705           facesToRm.reserve( psm->NbElements() );
11706           for ( SMDS_ElemIteratorPtr ite = psm->GetElements(); ite->more(); )
11707             facesToRm.push_back( ite->next() );
11708
11709           for ( size_t i = 0 ; i < _sdVec.size(); ++i )
11710             if (( psm = _sdVec[i]._proxyMesh->getFaceSubM( F )))
11711               psm->Clear();
11712         }
11713         for ( size_t i = 0; i < facesToRm.size(); ++i )
11714           getMeshDS()->RemoveFreeElement( facesToRm[i], smDS, /*fromGroups=*/false );
11715       }
11716       // remove nodes
11717       {
11718         TIDSortedNodeSet nodesToKeep; // nodes of _LayerEdge to keep
11719         for ( size_t iS = 0; iS < subEOS.size(); ++iS ) {
11720           for ( size_t i = 0; i < subEOS[iS]->_edges.size(); ++i )
11721             nodesToKeep.insert( ++( subEOS[iS]->_edges[i]->_nodes.begin() ),
11722                                 subEOS[iS]->_edges[i]->_nodes.end() );
11723         }
11724         SMDS_NodeIteratorPtr itn = smDS->GetNodes();
11725         while ( itn->more() ) {
11726           const SMDS_MeshNode* n = itn->next();
11727           if ( !nodesToKeep.count( n ))
11728             getMeshDS()->RemoveFreeNode( n, smDS, /*fromGroups=*/false );
11729         }
11730       }
11731       _periodicity->ClearPeriodic( F );
11732
11733       // restore position and UV of target nodes
11734       gp_Pnt p;
11735       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
11736         for ( size_t i = 0; i < subEOS[iS]->_edges.size(); ++i )
11737         {
11738           _LayerEdge*       edge = subEOS[iS]->_edges[i];
11739           SMDS_MeshNode* tgtNode = const_cast< SMDS_MeshNode*& >( edge->_nodes.back() );
11740           if ( edge->_pos.empty() ||
11741                edge->Is( _LayerEdge::SHRUNK )) continue;
11742           if ( subEOS[iS]->SWOLType() == TopAbs_FACE )
11743           {
11744             SMDS_FacePositionPtr pos = tgtNode->GetPosition();
11745             pos->SetUParameter( edge->_pos[0].X() );
11746             pos->SetVParameter( edge->_pos[0].Y() );
11747             p = surface->Value( edge->_pos[0].X(), edge->_pos[0].Y() );
11748           }
11749           else
11750           {
11751             SMDS_EdgePositionPtr pos = tgtNode->GetPosition();
11752             pos->SetUParameter( edge->_pos[0].Coord( U_TGT ));
11753             p = BRepAdaptor_Curve( TopoDS::Edge( subEOS[iS]->_sWOL )).Value( pos->GetUParameter() );
11754           }
11755           tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
11756           dumpMove( tgtNode );
11757         }
11758       // shrink EDGE sub-meshes and set proxy sub-meshes
11759       UVPtStructVec uvPtVec;
11760       set< _Shrinker1D* >::iterator shrIt = eShri1D.begin();
11761       for ( shrIt = eShri1D.begin(); shrIt != eShri1D.end(); ++shrIt )
11762       {
11763         _Shrinker1D* shr = (*shrIt);
11764         shr->Compute( /*set3D=*/true, helper );
11765
11766         // set proxy mesh of EDGEs w/o layers
11767         map< double, const SMDS_MeshNode* > nodes;
11768         SMESH_Algo::GetSortedNodesOnEdge( getMeshDS(), shr->GeomEdge(),/*skipMedium=*/true, nodes);
11769         // remove refinement nodes
11770         const SMDS_MeshNode* sn0 = shr->SrcNode(0), *sn1 = shr->SrcNode(1);
11771         const SMDS_MeshNode* tn0 = shr->TgtNode(0), *tn1 = shr->TgtNode(1);
11772         map< double, const SMDS_MeshNode* >::iterator u2n = nodes.begin();
11773         if ( u2n->second == sn0 || u2n->second == sn1 )
11774         {
11775           while ( u2n->second != tn0 && u2n->second != tn1 )
11776             ++u2n;
11777           nodes.erase( nodes.begin(), u2n );
11778         }
11779         u2n = --nodes.end();
11780         if ( u2n->second == sn0 || u2n->second == sn1 )
11781         {
11782           while ( u2n->second != tn0 && u2n->second != tn1 )
11783             --u2n;
11784           nodes.erase( ++u2n, nodes.end() );
11785         }
11786         // set proxy sub-mesh
11787         uvPtVec.resize( nodes.size() );
11788         u2n = nodes.begin();
11789         BRepAdaptor_Curve2d curve( shr->GeomEdge(), F );
11790         for ( size_t i = 0; i < nodes.size(); ++i, ++u2n )
11791         {
11792           uvPtVec[ i ].node = u2n->second;
11793           uvPtVec[ i ].param = u2n->first;
11794           uvPtVec[ i ].SetUV( curve.Value( u2n->first ).XY() );
11795         }
11796         StdMeshers_FaceSide fSide( uvPtVec, F, shr->GeomEdge(), _mesh );
11797         StdMeshers_ViscousLayers2D::SetProxyMeshOfEdge( fSide );
11798       }
11799
11800       // set proxy mesh of EDGEs with layers
11801       vector< _LayerEdge* > edges;
11802       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
11803       {
11804         _EdgesOnShape& eos = * subEOS[ iS ];
11805         if ( eos.ShapeType() != TopAbs_EDGE ) continue;
11806         if ( eos.size() == 0 )
11807           continue;
11808
11809         const TopoDS_Edge& E = TopoDS::Edge( eos._shape );
11810         data.SortOnEdge( E, eos._edges );
11811
11812         edges.clear();
11813         if ( _EdgesOnShape* eov = data.GetShapeEdges( helper.IthVertex( 0, E, /*CumOri=*/false )))
11814           if ( !eov->_edges.empty() )
11815             edges.push_back( eov->_edges[0] ); // on 1st VERTEX
11816
11817         edges.insert( edges.end(), eos._edges.begin(), eos._edges.end() );
11818
11819         if ( _EdgesOnShape* eov = data.GetShapeEdges( helper.IthVertex( 1, E, /*CumOri=*/false )))
11820           if ( !eov->_edges.empty() )
11821             edges.push_back( eov->_edges[0] ); // on last VERTEX
11822
11823         uvPtVec.resize( edges.size() );
11824         for ( size_t i = 0; i < edges.size(); ++i )
11825         {
11826           uvPtVec[ i ].node = edges[i]->_nodes.back();
11827           uvPtVec[ i ].param = helper.GetNodeU( E, edges[i]->_nodes[0] );
11828           uvPtVec[ i ].SetUV( helper.GetNodeUV( F, edges[i]->_nodes.back() ));
11829         }
11830         if ( uvPtVec[ 0 ].node == uvPtVec.back().node &&            // closed
11831              helper.IsSeamShape( uvPtVec[ 0 ].node->GetShapeID() ))
11832         {
11833           uvPtVec[ 0 ].SetUV( helper.GetNodeUV( F,
11834                                                 edges[0]->_nodes.back(),
11835                                                 edges[1]->_nodes.back() ));
11836           size_t i = edges.size() - 1;
11837           uvPtVec[ i ].SetUV( helper.GetNodeUV( F,
11838                                                 edges[i  ]->_nodes.back(),
11839                                                 edges[i-1]->_nodes.back() ));
11840         }
11841         // if ( edges.empty() )
11842         //   continue;
11843         BRep_Tool::Range( E, uvPtVec[0].param, uvPtVec.back().param );
11844         StdMeshers_FaceSide fSide( uvPtVec, F, E, _mesh );
11845         StdMeshers_ViscousLayers2D::SetProxyMeshOfEdge( fSide );
11846       }
11847       // temporary clear the FACE sub-mesh from faces made by refine()
11848       vector< const SMDS_MeshElement* > elems;
11849       elems.reserve( smDS->NbElements() + smDS->NbNodes() );
11850       for ( SMDS_ElemIteratorPtr ite = smDS->GetElements(); ite->more(); )
11851         elems.push_back( ite->next() );
11852       for ( SMDS_NodeIteratorPtr ite = smDS->GetNodes(); ite->more(); )
11853         elems.push_back( ite->next() );
11854       smDS->Clear();
11855
11856       // compute the mesh on the FACE
11857       TopTools_IndexedMapOfShape allowed(1);
11858       allowed.Add( sm->GetSubShape() );
11859       sm->SetAllowedSubShapes( &allowed );
11860       sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
11861       sm->ComputeStateEngine( SMESH_subMesh::COMPUTE_SUBMESH );
11862       sm->SetAllowedSubShapes( nullptr );
11863
11864       // re-fill proxy sub-meshes of the FACE
11865       for ( size_t i = 0 ; i < _sdVec.size(); ++i )
11866         if (( psm = _sdVec[i]._proxyMesh->getFaceSubM( F )))
11867           for ( SMDS_ElemIteratorPtr ite = smDS->GetElements(); ite->more(); )
11868             psm->AddElement( ite->next() );
11869
11870       // re-fill smDS
11871       for ( size_t i = 0; i < elems.size(); ++i )
11872         smDS->AddElement( elems[i] );
11873
11874       if ( sm->GetComputeState() != SMESH_subMesh::COMPUTE_OK )
11875         return error( errMsg );
11876
11877     } // end of re-meshing in case of failed smoothing
11878     else if ( !movedByPeriod )
11879     {
11880       // No wrongly shaped faces remain; final smooth. Set node XYZ.
11881       bool isStructuredFixed = false;
11882       if ( SMESH_2D_Algo* algo = dynamic_cast<SMESH_2D_Algo*>( sm->GetAlgo() ))
11883         isStructuredFixed = algo->FixInternalNodes( *data._proxyMesh, F );
11884       if ( !isStructuredFixed )
11885       {
11886         if ( isConcaveFace ) // fix narrow faces by swapping diagonals
11887           fixBadFaces( F, helper, /*is2D=*/false, ++shriStep );
11888
11889         for ( int st = 3; st; --st )
11890         {
11891           switch( st ) {
11892           case 1: smoothType = _SmoothNode::LAPLACIAN; break;
11893           case 2: smoothType = _SmoothNode::LAPLACIAN; break;
11894           case 3: smoothType = _SmoothNode::ANGULAR; break;
11895           }
11896           dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
11897           for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
11898           {
11899             nodesToSmooth[i].Smooth( nbBad,surface,helper,refSign,
11900                                      smoothType,/*set3D=*/st==1 );
11901           }
11902           dumpFunctionEnd();
11903         }
11904       }
11905       if ( !getMeshDS()->IsEmbeddedMode() )
11906         // Log node movement
11907         for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
11908         {
11909           SMESH_TNodeXYZ p ( nodesToSmooth[i]._node );
11910           getMeshDS()->MoveNode( nodesToSmooth[i]._node, p.X(), p.Y(), p.Z() );
11911         }
11912     }
11913
11914     // Set an event listener to clear FACE sub-mesh together with SOLID sub-mesh
11915     VISCOUS_3D::ToClearSubWithMain( sm, data._solid );
11916     if ( data2 )
11917       VISCOUS_3D::ToClearSubWithMain( sm, data2->_solid );
11918
11919   } // loop on FACES to shrink mesh on
11920
11921
11922   // Replace source nodes by target nodes in shrunk mesh edges
11923
11924   map< int, _Shrinker1D >::iterator e2shr = e2shrMap.begin();
11925   for ( ; e2shr != e2shrMap.end(); ++e2shr )
11926     e2shr->second.SwapSrcTgtNodes( getMeshDS() );
11927
11928   return true;
11929 }
11930
11931 //================================================================================
11932 /*!
11933  * \brief Computes 2d shrink direction and finds nodes limiting shrinking
11934  */
11935 //================================================================================
11936
11937 bool _ViscousBuilder::prepareEdgeToShrink( _LayerEdge&            edge,
11938                                            _EdgesOnShape&         eos,
11939                                            SMESH_MesherHelper&    helper,
11940                                            const SMESHDS_SubMesh* /*faceSubMesh*/)
11941 {
11942   const SMDS_MeshNode* srcNode = edge._nodes[0];
11943   const SMDS_MeshNode* tgtNode = edge._nodes.back();
11944
11945   if ( eos.SWOLType() == TopAbs_FACE )
11946   {
11947     if ( tgtNode->GetPosition()->GetDim() != 2 ) // not inflated edge
11948     {
11949       edge._pos.clear();
11950       edge.Set( _LayerEdge::SHRUNK );
11951       return srcNode == tgtNode;
11952     }
11953     gp_XY srcUV ( edge._pos[0].X(), edge._pos[0].Y() );          //helper.GetNodeUV( F, srcNode );
11954     gp_XY tgtUV = edge.LastUV( TopoDS::Face( eos._sWOL ), eos ); //helper.GetNodeUV( F, tgtNode );
11955     gp_Vec2d uvDir( srcUV, tgtUV );
11956     double uvLen = uvDir.Magnitude();
11957     uvDir /= uvLen;
11958     edge._normal.SetCoord( uvDir.X(),uvDir.Y(), 0 );
11959     edge._len = uvLen;
11960
11961     //edge._pos.resize(1);
11962     edge._pos[0].SetCoord( tgtUV.X(), tgtUV.Y(), 0 );
11963
11964     // set UV of source node to target node
11965     SMDS_FacePositionPtr pos = tgtNode->GetPosition();
11966     pos->SetUParameter( srcUV.X() );
11967     pos->SetVParameter( srcUV.Y() );
11968   }
11969   else // _sWOL is TopAbs_EDGE
11970   {
11971     if ( tgtNode->GetPosition()->GetDim() != 1 ) // not inflated edge
11972     {
11973       edge._pos.clear();
11974       edge.Set( _LayerEdge::SHRUNK );
11975       return srcNode == tgtNode;
11976     }
11977     const TopoDS_Edge&    E = TopoDS::Edge( eos._sWOL );
11978     SMESHDS_SubMesh* edgeSM = getMeshDS()->MeshElements( E );
11979     if ( !edgeSM || edgeSM->NbElements() == 0 )
11980       return error(SMESH_Comment("Not meshed EDGE ") << getMeshDS()->ShapeToIndex( E ));
11981
11982     const SMDS_MeshNode* n2 = 0;
11983     SMDS_ElemIteratorPtr eIt = srcNode->GetInverseElementIterator(SMDSAbs_Edge);
11984     while ( eIt->more() && !n2 )
11985     {
11986       const SMDS_MeshElement* e = eIt->next();
11987       if ( !edgeSM->Contains(e)) continue;
11988       n2 = e->GetNode( 0 );
11989       if ( n2 == srcNode ) n2 = e->GetNode( 1 );
11990     }
11991     if ( !n2 )
11992       return error(SMESH_Comment("Wrongly meshed EDGE ") << getMeshDS()->ShapeToIndex( E ));
11993
11994     if ( n2 == tgtNode       || // for 3D_mesh_GHS3D_01/B1
11995          n2 == edge._nodes[1] ) // bos #20643
11996     {
11997       // shrunk by other SOLID
11998       edge.Set( _LayerEdge::SHRUNK ); // ???
11999       return true;
12000     }
12001
12002     double uSrc = helper.GetNodeU( E, srcNode, n2 );
12003     double uTgt = helper.GetNodeU( E, tgtNode, srcNode );
12004     double u2   = helper.GetNodeU( E, n2,      srcNode );
12005
12006     //edge._pos.clear();
12007
12008     if ( fabs( uSrc-uTgt ) < 0.99 * fabs( uSrc-u2 ))
12009     {
12010       // tgtNode is located so that it does not make faces with wrong orientation
12011       edge.Set( _LayerEdge::SHRUNK );
12012       return true;
12013     }
12014     //edge._pos.resize(1);
12015     edge._pos[0].SetCoord( U_TGT, uTgt );
12016     edge._pos[0].SetCoord( U_SRC, uSrc );
12017     edge._pos[0].SetCoord( LEN_TGT, fabs( uSrc-uTgt ));
12018
12019     edge._simplices.resize( 1 );
12020     edge._simplices[0]._nPrev = n2;
12021
12022     // set U of source node to the target node
12023     SMDS_EdgePositionPtr pos = tgtNode->GetPosition();
12024     pos->SetUParameter( uSrc );
12025   }
12026   return true;
12027 }
12028
12029 //================================================================================
12030 /*!
12031  * \brief Restore position of a sole node of a _LayerEdge based on _noShrinkShapes
12032  */
12033 //================================================================================
12034
12035 void _ViscousBuilder::restoreNoShrink( _LayerEdge& edge ) const
12036 {
12037   if ( edge._nodes.size() == 1 )
12038   {
12039     edge._pos.clear();
12040     edge._len = 0;
12041
12042     const SMDS_MeshNode* srcNode = edge._nodes[0];
12043     TopoDS_Shape S = SMESH_MesherHelper::GetSubShapeByNode( srcNode, getMeshDS() );
12044     if ( S.IsNull() ) return;
12045
12046     gp_Pnt p;
12047
12048     switch ( S.ShapeType() )
12049     {
12050     case TopAbs_EDGE:
12051     {
12052       double f,l;
12053       TopLoc_Location loc;
12054       Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( S ), loc, f, l );
12055       if ( curve.IsNull() ) return;
12056       SMDS_EdgePositionPtr ePos = srcNode->GetPosition();
12057       p = curve->Value( ePos->GetUParameter() );
12058       break;
12059     }
12060     case TopAbs_VERTEX:
12061     {
12062       p = BRep_Tool::Pnt( TopoDS::Vertex( S ));
12063       break;
12064     }
12065     default: return;
12066     }
12067     getMeshDS()->MoveNode( srcNode, p.X(), p.Y(), p.Z() );
12068     dumpMove( srcNode );
12069   }
12070 }
12071
12072 //================================================================================
12073 /*!
12074  * \brief Try to fix triangles with high aspect ratio by swapping diagonals
12075  */
12076 //================================================================================
12077
12078 void _ViscousBuilder::fixBadFaces(const TopoDS_Face&          F,
12079                                   SMESH_MesherHelper&         helper,
12080                                   const bool                  is2D,
12081                                   const int                   step,
12082                                   set<const SMDS_MeshNode*> * involvedNodes)
12083 {
12084   SMESH::Controls::AspectRatio qualifier;
12085   SMESH::Controls::TSequenceOfXYZ points(3), points1(3), points2(3);
12086   const double maxAspectRatio = is2D ? 4. : 2;
12087   _NodeCoordHelper xyz( F, helper, is2D );
12088
12089   // find bad triangles
12090
12091   vector< const SMDS_MeshElement* > badTrias;
12092   vector< double >                  badAspects;
12093   SMESHDS_SubMesh*      sm = helper.GetMeshDS()->MeshElements( F );
12094   SMDS_ElemIteratorPtr fIt = sm->GetElements();
12095   while ( fIt->more() )
12096   {
12097     const SMDS_MeshElement * f = fIt->next();
12098     if ( f->NbCornerNodes() != 3 ) continue;
12099     for ( int iP = 0; iP < 3; ++iP ) points(iP+1) = xyz( f->GetNode(iP));
12100     double aspect = qualifier.GetValue( points );
12101     if ( aspect > maxAspectRatio )
12102     {
12103       badTrias.push_back( f );
12104       badAspects.push_back( aspect );
12105     }
12106   }
12107   if ( step == 1 )
12108   {
12109     dumpFunction(SMESH_Comment("beforeSwapDiagonals_F")<<helper.GetSubShapeID());
12110     SMDS_ElemIteratorPtr fIt = sm->GetElements();
12111     while ( fIt->more() )
12112     {
12113       const SMDS_MeshElement * f = fIt->next();
12114       if ( f->NbCornerNodes() == 3 )
12115         dumpChangeNodes( f );
12116     }
12117     dumpFunctionEnd();
12118   }
12119   if ( badTrias.empty() )
12120     return;
12121
12122   // find couples of faces to swap diagonal
12123
12124   typedef pair < const SMDS_MeshElement* , const SMDS_MeshElement* > T2Trias;
12125   vector< T2Trias > triaCouples; 
12126
12127   TIDSortedElemSet involvedFaces, emptySet;
12128   for ( size_t iTia = 0; iTia < badTrias.size(); ++iTia )
12129   {
12130     T2Trias trias    [3];
12131     double  aspRatio [3];
12132     int i1, i2, i3;
12133
12134     if ( !involvedFaces.insert( badTrias[iTia] ).second )
12135       continue;
12136     for ( int iP = 0; iP < 3; ++iP )
12137       points(iP+1) = xyz( badTrias[iTia]->GetNode(iP));
12138
12139     // find triangles adjacent to badTrias[iTia] with better aspect ratio after diag-swaping
12140     int bestCouple = -1;
12141     for ( int iSide = 0; iSide < 3; ++iSide )
12142     {
12143       const SMDS_MeshNode* n1 = badTrias[iTia]->GetNode( iSide );
12144       const SMDS_MeshNode* n2 = badTrias[iTia]->GetNode(( iSide+1 ) % 3 );
12145       trias [iSide].first  = badTrias[iTia];
12146       trias [iSide].second = SMESH_MeshAlgos::FindFaceInSet( n1, n2, emptySet, involvedFaces,
12147                                                              & i1, & i2 );
12148       if (( ! trias[iSide].second ) ||
12149           ( trias[iSide].second->NbCornerNodes() != 3 ) ||
12150           ( ! sm->Contains( trias[iSide].second )))
12151         continue;
12152
12153       // aspect ratio of an adjacent tria
12154       for ( int iP = 0; iP < 3; ++iP )
12155         points2(iP+1) = xyz( trias[iSide].second->GetNode(iP));
12156       double aspectInit = qualifier.GetValue( points2 );
12157
12158       // arrange nodes as after diag-swaping
12159       if ( helper.WrapIndex( i1+1, 3 ) == i2 )
12160         i3 = helper.WrapIndex( i1-1, 3 );
12161       else
12162         i3 = helper.WrapIndex( i1+1, 3 );
12163       points1 = points;
12164       points1( 1+ iSide ) = points2( 1+ i3 );
12165       points2( 1+ i2    ) = points1( 1+ ( iSide+2 ) % 3 );
12166
12167       // aspect ratio after diag-swaping
12168       aspRatio[ iSide ] = qualifier.GetValue( points1 ) + qualifier.GetValue( points2 );
12169       if ( aspRatio[ iSide ] > aspectInit + badAspects[ iTia ] )
12170         continue;
12171
12172       // prevent inversion of a triangle
12173       gp_Vec norm1 = gp_Vec( points1(1), points1(3) ) ^ gp_Vec( points1(1), points1(2) );
12174       gp_Vec norm2 = gp_Vec( points2(1), points2(3) ) ^ gp_Vec( points2(1), points2(2) );
12175       if ( norm1 * norm2 < 0. && norm1.Angle( norm2 ) > 70./180.*M_PI )
12176         continue;
12177
12178       if ( bestCouple < 0 || aspRatio[ bestCouple ] > aspRatio[ iSide ] )
12179         bestCouple = iSide;
12180     }
12181
12182     if ( bestCouple >= 0 )
12183     {
12184       triaCouples.push_back( trias[bestCouple] );
12185       involvedFaces.insert ( trias[bestCouple].second );
12186     }
12187     else
12188     {
12189       involvedFaces.erase( badTrias[iTia] );
12190     }
12191   }
12192   if ( triaCouples.empty() )
12193     return;
12194
12195   // swap diagonals
12196
12197   SMESH_MeshEditor editor( helper.GetMesh() );
12198   dumpFunction(SMESH_Comment("beforeSwapDiagonals_F")<<helper.GetSubShapeID()<<"_"<<step);
12199   for ( size_t i = 0; i < triaCouples.size(); ++i )
12200   {
12201     dumpChangeNodes( triaCouples[i].first );
12202     dumpChangeNodes( triaCouples[i].second );
12203     editor.InverseDiag( triaCouples[i].first, triaCouples[i].second );
12204   }
12205
12206   if ( involvedNodes )
12207     for ( size_t i = 0; i < triaCouples.size(); ++i )
12208     {
12209       involvedNodes->insert( triaCouples[i].first->begin_nodes(),
12210                              triaCouples[i].first->end_nodes() );
12211       involvedNodes->insert( triaCouples[i].second->begin_nodes(),
12212                              triaCouples[i].second->end_nodes() );
12213     }
12214
12215   // just for debug dump resulting triangles
12216   dumpFunction(SMESH_Comment("swapDiagonals_F")<<helper.GetSubShapeID()<<"_"<<step);
12217   for ( size_t i = 0; i < triaCouples.size(); ++i )
12218   {
12219     dumpChangeNodes( triaCouples[i].first );
12220     dumpChangeNodes( triaCouples[i].second );
12221   }
12222 }
12223
12224 //================================================================================
12225 /*!
12226  * \brief Move target node to it's final position on the FACE during shrinking
12227  */
12228 //================================================================================
12229
12230 bool _LayerEdge::SetNewLength2d( Handle(Geom_Surface)& surface,
12231                                  const TopoDS_Face&    F,
12232                                  _EdgesOnShape&        eos,
12233                                  SMESH_MesherHelper&   helper )
12234 {
12235   if ( Is( SHRUNK ))
12236     return false; // already at the target position
12237
12238   SMDS_MeshNode* tgtNode = const_cast< SMDS_MeshNode*& >( _nodes.back() );
12239
12240   if ( eos.SWOLType() == TopAbs_FACE )
12241   {
12242     gp_XY    curUV = helper.GetNodeUV( F, tgtNode );
12243     gp_Pnt2d tgtUV( _pos[0].X(), _pos[0].Y() );
12244     gp_Vec2d uvDir( _normal.X(), _normal.Y() );
12245     const double uvLen = tgtUV.Distance( curUV );
12246     const double kSafe = Max( 0.5, 1. - 0.1 * _simplices.size() );
12247
12248     // Select shrinking step such that not to make faces with wrong orientation.
12249     double stepSize = 1e100;
12250     for ( size_t i = 0; i < _simplices.size(); ++i )
12251     {
12252       if ( !_simplices[i]._nPrev->isMarked() ||
12253            !_simplices[i]._nNext->isMarked() )
12254         continue; // simplex of quadrangle created by addBoundaryElements()
12255
12256       // find intersection of 2 lines: curUV-tgtUV and that connecting simplex nodes
12257       gp_XY uvN1 = helper.GetNodeUV( F, _simplices[i]._nPrev, tgtNode );
12258       gp_XY uvN2 = helper.GetNodeUV( F, _simplices[i]._nNext, tgtNode );
12259       gp_XY dirN = uvN2 - uvN1;
12260       double det = uvDir.Crossed( dirN );
12261       if ( Abs( det )  < std::numeric_limits<double>::min() ) continue;
12262       gp_XY dirN2Cur = curUV - uvN1;
12263       double step = dirN.Crossed( dirN2Cur ) / det;
12264       if ( step > 0 )
12265         stepSize = Min( step, stepSize );
12266     }
12267     gp_Pnt2d newUV;
12268     if ( uvLen <= stepSize )
12269     {
12270       newUV = tgtUV;
12271       Set( SHRUNK );
12272       //_pos.clear();
12273     }
12274     else if ( stepSize > 0 )
12275     {
12276       newUV = curUV + uvDir.XY() * stepSize * kSafe;
12277     }
12278     else
12279     {
12280       return true;
12281     }
12282     SMDS_FacePositionPtr pos = tgtNode->GetPosition();
12283     pos->SetUParameter( newUV.X() );
12284     pos->SetVParameter( newUV.Y() );
12285
12286 #ifdef __myDEBUG
12287     gp_Pnt p = surface->Value( newUV.X(), newUV.Y() );
12288     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
12289     dumpMove( tgtNode );
12290 #else
12291     if ( surface.IsNull() ) {}
12292 #endif
12293   }
12294   else // _sWOL is TopAbs_EDGE
12295   {
12296     const TopoDS_Edge&      E = TopoDS::Edge( eos._sWOL );
12297     const SMDS_MeshNode*   n2 = _simplices[0]._nPrev;
12298     SMDS_EdgePositionPtr tgtPos = tgtNode->GetPosition();
12299
12300     const double u2     = helper.GetNodeU( E, n2, tgtNode );
12301     const double uSrc   = _pos[0].Coord( U_SRC );
12302     const double lenTgt = _pos[0].Coord( LEN_TGT );
12303
12304     double newU = _pos[0].Coord( U_TGT );
12305     if ( lenTgt < 0.99 * fabs( uSrc-u2 )) // n2 got out of src-tgt range
12306     {
12307       Set( _LayerEdge::SHRUNK );
12308       //_pos.clear();
12309     }
12310     else
12311     {
12312       newU = 0.1 * tgtPos->GetUParameter() + 0.9 * u2;
12313     }
12314     tgtPos->SetUParameter( newU );
12315 #ifdef __myDEBUG
12316     gp_XY newUV = helper.GetNodeUV( F, tgtNode, _nodes[0]);
12317     gp_Pnt p = surface->Value( newUV.X(), newUV.Y() );
12318     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
12319     dumpMove( tgtNode );
12320 #endif
12321   }
12322
12323   return true;
12324 }
12325
12326 //================================================================================
12327 /*!
12328  * \brief Perform smooth on the FACE
12329  *  \retval bool - true if the node has been moved
12330  */
12331 //================================================================================
12332
12333 bool _SmoothNode::Smooth(int&                  nbBad,
12334                          Handle(Geom_Surface)& surface,
12335                          SMESH_MesherHelper&   helper,
12336                          const double          refSign,
12337                          SmoothType            how,
12338                          bool                  set3D)
12339 {
12340   const TopoDS_Face& face = TopoDS::Face( helper.GetSubShape() );
12341
12342   // get uv of surrounding nodes
12343   vector<gp_XY> uv( _simplices.size() );
12344   for ( size_t i = 0; i < _simplices.size(); ++i )
12345     uv[i] = helper.GetNodeUV( face, _simplices[i]._nPrev, _node );
12346
12347   // compute new UV for the node
12348   gp_XY newPos (0,0);
12349   if ( how == TFI && _simplices.size() == 4 )
12350   {
12351     gp_XY corners[4];
12352     for ( size_t i = 0; i < _simplices.size(); ++i )
12353       if ( _simplices[i]._nOpp )
12354         corners[i] = helper.GetNodeUV( face, _simplices[i]._nOpp, _node );
12355       else
12356         throw SALOME_Exception(LOCALIZED("TFI smoothing: _Simplex::_nOpp not set!"));
12357
12358     newPos = helper.calcTFI ( 0.5, 0.5,
12359                               corners[0], corners[1], corners[2], corners[3],
12360                               uv[1], uv[2], uv[3], uv[0] );
12361   }
12362   else if ( how == ANGULAR )
12363   {
12364     newPos = computeAngularPos( uv, helper.GetNodeUV( face, _node ), refSign );
12365   }
12366   else if ( how == CENTROIDAL && _simplices.size() > 3 )
12367   {
12368     // average centers of diagonals wieghted with their reciprocal lengths
12369     if ( _simplices.size() == 4 )
12370     {
12371       double w1 = 1. / ( uv[2]-uv[0] ).SquareModulus();
12372       double w2 = 1. / ( uv[3]-uv[1] ).SquareModulus();
12373       newPos = ( w1 * ( uv[2]+uv[0] ) + w2 * ( uv[3]+uv[1] )) / ( w1+w2 ) / 2;
12374     }
12375     else
12376     {
12377       double sumWeight = 0;
12378       int nb = _simplices.size() == 4 ? 2 : _simplices.size();
12379       for ( int i = 0; i < nb; ++i )
12380       {
12381         int iFrom = i + 2;
12382         int iTo   = i + _simplices.size() - 1;
12383         for ( int j = iFrom; j < iTo; ++j )
12384         {
12385           int i2 = SMESH_MesherHelper::WrapIndex( j, _simplices.size() );
12386           double w = 1. / ( uv[i]-uv[i2] ).SquareModulus();
12387           sumWeight += w;
12388           newPos += w * ( uv[i]+uv[i2] );
12389         }
12390       }
12391       newPos /= 2 * sumWeight; // 2 is to get a middle between uv's
12392     }
12393   }
12394   else
12395   {
12396     // Laplacian smooth
12397     for ( size_t i = 0; i < _simplices.size(); ++i )
12398       newPos += uv[i];
12399     newPos /= _simplices.size();
12400   }
12401
12402   // count quality metrics (orientation) of triangles around the node
12403   int nbOkBefore = 0;
12404   gp_XY tgtUV = helper.GetNodeUV( face, _node );
12405   for ( size_t i = 0; i < _simplices.size(); ++i )
12406     nbOkBefore += _simplices[i].IsForward( tgtUV, _node, face, helper, refSign );
12407
12408   int nbOkAfter = 0;
12409   for ( size_t i = 0; i < _simplices.size(); ++i )
12410     nbOkAfter += _simplices[i].IsForward( newPos, _node, face, helper, refSign );
12411
12412   if ( nbOkAfter < nbOkBefore )
12413   {
12414     nbBad += _simplices.size() - nbOkBefore;
12415     return false;
12416   }
12417
12418   SMDS_FacePositionPtr pos = _node->GetPosition();
12419   pos->SetUParameter( newPos.X() );
12420   pos->SetVParameter( newPos.Y() );
12421
12422 #ifdef __myDEBUG
12423   set3D = true;
12424 #endif
12425   if ( set3D )
12426   {
12427     gp_Pnt p = surface->Value( newPos.X(), newPos.Y() );
12428     const_cast< SMDS_MeshNode* >( _node )->setXYZ( p.X(), p.Y(), p.Z() );
12429     dumpMove( _node );
12430   }
12431
12432   nbBad += _simplices.size() - nbOkAfter;
12433   return ( (tgtUV-newPos).SquareModulus() > 1e-10 );
12434 }
12435
12436 //================================================================================
12437 /*!
12438  * \brief Computes new UV using angle based smoothing technique
12439  */
12440 //================================================================================
12441
12442 gp_XY _SmoothNode::computeAngularPos(vector<gp_XY>& uv,
12443                                      const gp_XY&   uvToFix,
12444                                      const double   refSign)
12445 {
12446   uv.push_back( uv.front() );
12447
12448   vector< gp_XY >  edgeDir ( uv.size() );
12449   vector< double > edgeSize( uv.size() );
12450   for ( size_t i = 1; i < edgeDir.size(); ++i )
12451   {
12452     edgeDir [i-1] = uv[i] - uv[i-1];
12453     edgeSize[i-1] = edgeDir[i-1].Modulus();
12454     if ( edgeSize[i-1] < numeric_limits<double>::min() )
12455       edgeDir[i-1].SetX( 100 );
12456     else
12457       edgeDir[i-1] /= edgeSize[i-1] * refSign;
12458   }
12459   edgeDir.back()  = edgeDir.front();
12460   edgeSize.back() = edgeSize.front();
12461
12462   gp_XY  newPos(0,0);
12463   double sumWgt = 0;
12464   for ( size_t i = 1; i < edgeDir.size(); ++i )
12465   {
12466     const int i1 = i-1;
12467     if ( edgeDir[i1].X() > 1. ) continue;
12468     while ( edgeDir[i].X() > 1. && ++i < edgeDir.size() );
12469     if ( i == edgeDir.size() ) break;
12470     gp_XY p = uv[i];
12471     gp_XY norm1( -edgeDir[i1].Y(), edgeDir[i1].X() );
12472     gp_XY norm2( -edgeDir[i ].Y(), edgeDir[i ].X() );
12473     gp_XY bisec = norm1 + norm2;
12474     double bisecSize = bisec.Modulus();
12475     if ( bisecSize < numeric_limits<double>::min() )
12476     {
12477       bisec = -edgeDir[i1] + edgeDir[i];
12478       bisecSize = bisec.Modulus();
12479     }
12480     bisec /= bisecSize;
12481
12482     gp_XY   dirToN = uvToFix - p;
12483     double distToN = bisec * dirToN;
12484     if ( bisec * dirToN < 0 )
12485       distToN = -distToN;
12486
12487     double wgt = edgeSize[i1] + edgeSize[i];
12488     newPos += ( p + bisec * distToN ) * wgt;
12489     sumWgt += wgt;
12490   }
12491   newPos /= sumWgt;
12492   return newPos;
12493 }
12494
12495 //================================================================================
12496 /*!
12497  * \brief Keep a _LayerEdge inflated along the EDGE
12498  */
12499 //================================================================================
12500
12501 void _Shrinker1D::AddEdge( const _LayerEdge*   e,
12502                            _EdgesOnShape&      eos,
12503                            SMESH_MesherHelper& helper )
12504 {
12505   // init
12506   if ( _nodes.empty() )
12507   {
12508     _edges[0] = _edges[1] = 0;
12509     _done = false;
12510   }
12511   // check _LayerEdge
12512   if ( e == _edges[0] || e == _edges[1] || e->_nodes.size() < 2 )
12513     return;
12514   if ( eos.SWOLType() != TopAbs_EDGE )
12515     throw SALOME_Exception(LOCALIZED("Wrong _LayerEdge is added"));
12516   if ( _edges[0] && !_geomEdge.IsSame( eos._sWOL ))
12517     throw SALOME_Exception(LOCALIZED("Wrong _LayerEdge is added"));
12518
12519   // store _LayerEdge
12520   _geomEdge = TopoDS::Edge( eos._sWOL );
12521   double f,l;
12522   BRep_Tool::Range( _geomEdge, f,l );
12523   double u = helper.GetNodeU( _geomEdge, e->_nodes[0], e->_nodes.back());
12524   _edges[ u < 0.5*(f+l) ? 0 : 1 ] = e;
12525
12526   // Check if the nodes are already shrunk by another SOLID
12527
12528   const SMDS_MeshNode* tgtNode0 = TgtNode( 0 );
12529   const SMDS_MeshNode* tgtNode1 = TgtNode( 1 );
12530
12531   _done = (( tgtNode0 && tgtNode0->NbInverseElements( SMDSAbs_Edge ) == 2 ) ||
12532            ( tgtNode1 && tgtNode1->NbInverseElements( SMDSAbs_Edge ) == 2 ));
12533   if ( _done )
12534     _nodes.resize( 1, nullptr );
12535
12536   // Update _nodes
12537
12538   if ( _nodes.empty() )
12539   {
12540     SMESHDS_SubMesh * eSubMesh = helper.GetMeshDS()->MeshElements( _geomEdge );
12541     if ( !eSubMesh || eSubMesh->NbNodes() < 1 )
12542       return;
12543     TopLoc_Location loc;
12544     Handle(Geom_Curve) C = BRep_Tool::Curve( _geomEdge, loc, f,l );
12545     GeomAdaptor_Curve aCurve(C, f,l);
12546     const double totLen = GCPnts_AbscissaPoint::Length(aCurve, f, l);
12547
12548     smIdType nbExpectNodes = eSubMesh->NbNodes();
12549     _initU  .reserve( nbExpectNodes );
12550     _normPar.reserve( nbExpectNodes );
12551     _nodes  .reserve( nbExpectNodes );
12552     SMDS_NodeIteratorPtr nIt = eSubMesh->GetNodes();
12553     while ( nIt->more() )
12554     {
12555       const SMDS_MeshNode* node = nIt->next();
12556
12557       // skip refinement nodes
12558       if ( node->NbInverseElements(SMDSAbs_Edge) == 0 ||
12559            node == tgtNode0 || node == tgtNode1 )
12560         continue;
12561       bool hasMarkedFace = false;
12562       SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
12563       while ( fIt->more() && !hasMarkedFace )
12564         hasMarkedFace = fIt->next()->isMarked();
12565       if ( !hasMarkedFace )
12566         continue;
12567
12568       _nodes.push_back( node );
12569       _initU.push_back( helper.GetNodeU( _geomEdge, node ));
12570       double len = GCPnts_AbscissaPoint::Length(aCurve, f, _initU.back());
12571       _normPar.push_back(  len / totLen );
12572     }
12573   }
12574   else
12575   {
12576     // remove target node of the _LayerEdge from _nodes
12577     size_t nbFound = 0;
12578     for ( size_t i = 0; i < _nodes.size(); ++i )
12579       if ( !_nodes[i] || _nodes[i] == tgtNode0 || _nodes[i] == tgtNode1 )
12580         _nodes[i] = 0, nbFound++;
12581     if ( nbFound == _nodes.size() )
12582       _nodes.clear();
12583   }
12584 }
12585
12586 //================================================================================
12587 /*!
12588  * \brief Move nodes on EDGE from ends where _LayerEdge's are inflated
12589  */
12590 //================================================================================
12591
12592 void _Shrinker1D::Compute(bool set3D, SMESH_MesherHelper& helper)
12593 {
12594   if ( _done || _nodes.empty())
12595     return;
12596   const _LayerEdge* e = _edges[0];
12597   if ( !e ) e = _edges[1];
12598   if ( !e ) return;
12599
12600   _done =  (( !_edges[0] || _edges[0]->Is( _LayerEdge::SHRUNK )) &&
12601             ( !_edges[1] || _edges[1]->Is( _LayerEdge::SHRUNK )));
12602
12603   double f,l;
12604   if ( set3D || _done )
12605   {
12606     dumpFunction(SMESH_Comment("shrink1D_E") << helper.GetMeshDS()->ShapeToIndex( _geomEdge )<<
12607                  "_F" << helper.GetSubShapeID() );
12608     Handle(Geom_Curve) C = BRep_Tool::Curve(_geomEdge, f,l);
12609     GeomAdaptor_Curve aCurve(C, f,l);
12610
12611     if ( _edges[0] )
12612       f = helper.GetNodeU( _geomEdge, _edges[0]->_nodes.back(), _nodes[0] );
12613     if ( _edges[1] )
12614       l = helper.GetNodeU( _geomEdge, _edges[1]->_nodes.back(), _nodes.back() );
12615     double totLen = GCPnts_AbscissaPoint::Length( aCurve, f, l );
12616
12617     for ( size_t i = 0; i < _nodes.size(); ++i )
12618     {
12619       if ( !_nodes[i] ) continue;
12620       double len = totLen * _normPar[i];
12621       GCPnts_AbscissaPoint discret( aCurve, len, f );
12622       if ( !discret.IsDone() )
12623         return throw SALOME_Exception(LOCALIZED("GCPnts_AbscissaPoint failed"));
12624       double u = discret.Parameter();
12625       SMDS_EdgePositionPtr pos = _nodes[i]->GetPosition();
12626       pos->SetUParameter( u );
12627       gp_Pnt p = C->Value( u );
12628       const_cast< SMDS_MeshNode*>( _nodes[i] )->setXYZ( p.X(), p.Y(), p.Z() );
12629       dumpMove( _nodes[i] );
12630     }
12631     dumpFunctionEnd();
12632   }
12633   else
12634   {
12635     BRep_Tool::Range( _geomEdge, f,l );
12636     if ( _edges[0] )
12637       f = helper.GetNodeU( _geomEdge, _edges[0]->_nodes.back(), _nodes[0] );
12638     if ( _edges[1] )
12639       l = helper.GetNodeU( _geomEdge, _edges[1]->_nodes.back(), _nodes.back() );
12640     
12641     for ( size_t i = 0; i < _nodes.size(); ++i )
12642     {
12643       if ( !_nodes[i] ) continue;
12644       double u = f * ( 1-_normPar[i] ) + l * _normPar[i];
12645       SMDS_EdgePositionPtr pos = _nodes[i]->GetPosition();
12646       pos->SetUParameter( u );
12647     }
12648   }
12649 }
12650
12651 //================================================================================
12652 /*!
12653  * \brief Restore initial parameters of nodes on EDGE
12654  */
12655 //================================================================================
12656
12657 void _Shrinker1D::RestoreParams()
12658 {
12659   if ( _done )
12660     for ( size_t i = 0; i < _nodes.size(); ++i )
12661     {
12662       if ( !_nodes[i] ) continue;
12663       SMDS_EdgePositionPtr pos = _nodes[i]->GetPosition();
12664       pos->SetUParameter( _initU[i] );
12665     }
12666   _done = false;
12667 }
12668
12669 //================================================================================
12670 /*!
12671  * \brief Replace source nodes by target nodes in shrunk mesh edges
12672  */
12673 //================================================================================
12674
12675 void _Shrinker1D::SwapSrcTgtNodes( SMESHDS_Mesh* mesh )
12676 {
12677   const SMDS_MeshNode* nodes[3];
12678   for ( int i = 0; i < 2; ++i )
12679   {
12680     if ( !_edges[i] ) continue;
12681
12682     SMESHDS_SubMesh * eSubMesh = mesh->MeshElements( _geomEdge );
12683     if ( !eSubMesh ) return;
12684     const SMDS_MeshNode* srcNode = _edges[i]->_nodes[0];
12685     const SMDS_MeshNode* tgtNode = _edges[i]->_nodes.back();
12686     const SMDS_MeshNode* scdNode = _edges[i]->_nodes[1];
12687     SMDS_ElemIteratorPtr eIt = srcNode->GetInverseElementIterator(SMDSAbs_Edge);
12688     while ( eIt->more() )
12689     {
12690       const SMDS_MeshElement* e = eIt->next();
12691       if ( !eSubMesh->Contains( e ) || e->GetNodeIndex( scdNode ) >= 0 )
12692           continue;
12693       SMDS_ElemIteratorPtr nIt = e->nodesIterator();
12694       for ( int iN = 0; iN < e->NbNodes(); ++iN )
12695       {
12696         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
12697         nodes[iN] = ( n == srcNode ? tgtNode : n );
12698       }
12699       mesh->ChangeElementNodes( e, nodes, e->NbNodes() );
12700     }
12701   }
12702 }
12703
12704 //================================================================================
12705 /*!
12706  * \brief Setup quadPoints
12707  */
12708 //================================================================================
12709
12710 _Mapper2D::_Mapper2D( const TParam2ColumnMap & param2ColumnMap, const TNode2Edge& n2eMap )
12711 {
12712   size_t i, iSize = _quadPoints.iSize = param2ColumnMap.size();
12713   size_t j, jSize = _quadPoints.jSize = param2ColumnMap.begin()->second.size();
12714   if ( _quadPoints.iSize < 3 ||
12715        _quadPoints.jSize < 3 )
12716     return;
12717   _quadPoints.uv_grid.resize( iSize * jSize );
12718
12719   // set nodes
12720   i = 0;
12721   for ( auto & u_columnNodes : param2ColumnMap )
12722   {
12723     for ( j = 0; j < u_columnNodes.second.size(); ++j )
12724       _quadPoints.UVPt( i, j ).node = u_columnNodes.second[ j ];
12725     ++i;
12726   }
12727
12728   // compute x parameter on borders
12729   uvPnt( 0, 0       ).x = 0;
12730   uvPnt( 0, jSize-1 ).x = 0;
12731   gp_Pnt p0, pPrev0 = SMESH_NodeXYZ( uvPnt( 0, 0       ).node );
12732   gp_Pnt p1, pPrev1 = SMESH_NodeXYZ( uvPnt( 0, jSize-1 ).node );
12733   for ( i = 1; i < iSize; ++i )
12734   {
12735     p0 = SMESH_NodeXYZ( uvPnt( i, 0       ).node );
12736     p1 = SMESH_NodeXYZ( uvPnt( i, jSize-1 ).node );
12737     uvPnt( i, 0       ).x = uvPnt( i-1, 0       ).x + p0.Distance( pPrev0 );
12738     uvPnt( i, jSize-1 ).x = uvPnt( i-1, jSize-1 ).x + p1.Distance( pPrev1 );
12739     pPrev0 = p0;
12740     pPrev1 = p1;
12741   }
12742   for ( i = 1; i < iSize-1; ++i )
12743   {
12744     uvPnt( i, 0       ).x /= uvPnt( iSize-1, 0       ).x;
12745     uvPnt( i, jSize-1 ).x /= uvPnt( iSize-1, jSize-1 ).x;
12746     uvPnt( i, 0       ).y = 0;
12747     uvPnt( i, jSize-1 ).y = 1;
12748   }
12749
12750   // compute y parameter on borders
12751   uvPnt( 0,       0 ).y = 0;
12752   uvPnt( iSize-1, 0 ).y = 0;
12753   pPrev0 = SMESH_NodeXYZ( uvPnt( 0,       0 ).node );
12754   pPrev1 = SMESH_NodeXYZ( uvPnt( iSize-1, 0 ).node );
12755   for ( j = 1; j < jSize; ++j )
12756   {
12757     p0 = SMESH_NodeXYZ( uvPnt( 0,       j ).node );
12758     p1 = SMESH_NodeXYZ( uvPnt( iSize-1, j ).node );
12759     uvPnt( 0,       j ).y = uvPnt( 0,       j-1 ).y + p0.Distance( pPrev0 );
12760     uvPnt( iSize-1, j ).y = uvPnt( iSize-1, j-1 ).y + p1.Distance( pPrev1 );
12761     pPrev0 = p0;
12762     pPrev1 = p1;
12763   }
12764   for ( j = 1; j < jSize-1; ++j )
12765   {
12766     uvPnt( 0,       j ).y /= uvPnt( 0,       jSize-1 ).y;
12767     uvPnt( iSize-1, j ).y /= uvPnt( iSize-1, jSize-1 ).y;
12768     uvPnt( 0,       j ).x = 0;
12769     uvPnt( iSize-1, j ).x = 1;
12770   }
12771
12772   // compute xy of internal nodes
12773   for ( i = 1; i < iSize-1; ++i )
12774   {
12775     const double x0 = uvPnt( i, 0       ).x;
12776     const double x1 = uvPnt( i, jSize-1 ).x;
12777     for ( j = 1; j < jSize-1; ++j )
12778     {
12779       const double y0 = uvPnt( 0,       j ).y;
12780       const double y1 = uvPnt( iSize-1, j ).y;
12781       double x = (x0 + y0 * (x1 - x0)) / (1 - (y1 - y0) * (x1 - x0));
12782       double y = y0 + x * (y1 - y0);
12783       uvPnt( i, j ).x = x;
12784       uvPnt( i, j ).y = y;
12785     }
12786   }
12787
12788   // replace base nodes with target ones
12789   for ( i = 0; i < iSize; ++i )
12790     for ( j = 0; j < jSize; ++j )
12791     {
12792       auto n2e = n2eMap.find( uvPnt( i, j ).node );
12793       uvPnt( i, j ).node = n2e->second->_nodes.back();
12794     }
12795
12796   return;
12797 }
12798
12799 //================================================================================
12800 /*!
12801  * \brief Compute positions of nodes of 2D structured mesh using TFI
12802  */
12803 //================================================================================
12804
12805 bool _Mapper2D::ComputeNodePositions()
12806 {
12807   if ( _quadPoints.uv_grid.empty() )
12808     return true;
12809
12810   size_t i, iSize = _quadPoints.iSize;
12811   size_t j, jSize = _quadPoints.jSize;
12812
12813   SMESH_NodeXYZ a0 ( uvPnt( 0,       0       ).node );
12814   SMESH_NodeXYZ a1 ( uvPnt( iSize-1, 0       ).node );
12815   SMESH_NodeXYZ a2 ( uvPnt( iSize-1, jSize-1 ).node );
12816   SMESH_NodeXYZ a3 ( uvPnt( 0,       jSize-1 ).node );
12817
12818   for ( i = 1; i < iSize-1; ++i )
12819   {
12820     SMESH_NodeXYZ p0 ( uvPnt( i, 0       ).node );
12821     SMESH_NodeXYZ p2 ( uvPnt( i, jSize-1 ).node );
12822     for ( j = 1; j < jSize-1; ++j )
12823     {
12824       SMESH_NodeXYZ p1 ( uvPnt( iSize-1, j ).node );
12825       SMESH_NodeXYZ p3 ( uvPnt( 0,       j ).node );
12826       double x = uvPnt( i, j ).x;
12827       double y = uvPnt( i, j ).y;
12828
12829       gp_XYZ p = SMESH_MesherHelper::calcTFI( x, y, a0,a1,a2,a3, p0,p1,p2,p3 );
12830       const_cast< SMDS_MeshNode* >( uvPnt( i, j ).node )->setXYZ( p.X(), p.Y(), p.Z() );
12831
12832       dumpMove( uvPnt( i, j ).node );
12833     }
12834   }
12835   return true;
12836 }
12837
12838 //================================================================================
12839 /*!
12840  * \brief Creates 2D and 1D elements on boundaries of new prisms
12841  */
12842 //================================================================================
12843
12844 bool _ViscousBuilder::addBoundaryElements(_SolidData& data)
12845 {
12846   bool addAllBoundaryElements = true;
12847   SMESH_MesherHelper helper( *_mesh );
12848
12849   vector< const SMDS_MeshNode* > faceNodes;
12850
12851   //for ( size_t i = 0; i < _sdVec.size(); ++i )
12852   {
12853     //_SolidData& data = _sdVec[i];
12854     TopTools_IndexedMapOfShape geomEdges;
12855     TopExp::MapShapes( data._solid, TopAbs_EDGE, geomEdges );
12856     for ( int iE = 1; iE <= geomEdges.Extent(); ++iE )
12857     {
12858       const TopoDS_Edge& E = TopoDS::Edge( geomEdges(iE));
12859       const TGeomID edgeID = getMeshDS()->ShapeToIndex( E );
12860       if ( data._noShrinkShapes.count( edgeID ))
12861         continue;
12862
12863       // Get _LayerEdge's based on E
12864
12865       map< double, const SMDS_MeshNode* > u2nodes;
12866       if ( !SMESH_Algo::GetSortedNodesOnEdge( getMeshDS(), E, /*ignoreMedium=*/false, u2nodes))
12867         continue;
12868       
12869       vector< _LayerEdge* > ledges; ledges.reserve( u2nodes.size() );
12870       TNode2Edge & n2eMap = data._n2eMap;
12871       map< double, const SMDS_MeshNode* >::iterator u2n = u2nodes.begin();
12872       {
12873         //check if 2D elements are needed on E
12874         TNode2Edge::iterator n2e = n2eMap.find( u2n->second );
12875         if ( n2e == n2eMap.end() ) continue; // no layers on vertex
12876         ledges.push_back( n2e->second );
12877         u2n++;
12878         if (( n2e = n2eMap.find( u2n->second )) == n2eMap.end() )
12879           continue; // no layers on E
12880         ledges.push_back( n2eMap[ u2n->second ]);
12881
12882         const SMDS_MeshNode* tgtN0 = ledges[0]->_nodes.back();
12883         const SMDS_MeshNode* tgtN1 = ledges[1]->_nodes.back();
12884         int nbSharedPyram = 0;
12885         SMDS_ElemIteratorPtr vIt = tgtN1->GetInverseElementIterator(SMDSAbs_Volume);
12886         while ( vIt->more() )
12887         {
12888           const SMDS_MeshElement* v = vIt->next();
12889           nbSharedPyram += int( v->GetNodeIndex( tgtN0 ) >= 0 );
12890         }
12891         if ( nbSharedPyram > 1 )
12892           continue; // not free border of the pyramid
12893
12894         faceNodes.clear();
12895         faceNodes.push_back( ledges[0]->_nodes[0] );
12896         faceNodes.push_back( ledges[1]->_nodes[0] );
12897         if ( ledges[0]->_nodes.size() > 1 ) faceNodes.push_back( ledges[0]->_nodes[1] );
12898         if ( ledges[1]->_nodes.size() > 1 ) faceNodes.push_back( ledges[1]->_nodes[1] );
12899
12900         if ( getMeshDS()->FindElement( faceNodes, SMDSAbs_Face, /*noMedium=*/true))
12901           continue; // faces already created
12902       }      
12903       for ( ++u2n; u2n != u2nodes.end(); ++u2n )
12904         if ( n2eMap[ u2n->second ] != nullptr )
12905           ledges.push_back( n2eMap[ u2n->second ]);
12906         else /*some boundary elements might be lost because the connectivity of the face is not entirely defined on this edge*/
12907           addAllBoundaryElements = false;
12908
12909       // Find out orientation and type of face to create
12910       bool reverse = false, isOnFace;
12911       TopoDS_Shape F;
12912
12913       map< TGeomID, TopoDS_Shape >::iterator e2f = data._shrinkShape2Shape.find( edgeID );
12914       if (( isOnFace = ( e2f != data._shrinkShape2Shape.end() )))
12915       {
12916         F = e2f->second.Oriented( TopAbs_FORWARD );
12917         reverse = ( helper.GetSubShapeOri( F, E ) == TopAbs_REVERSED );
12918         if ( helper.GetSubShapeOri( data._solid, F ) == TopAbs_REVERSED )
12919           reverse = !reverse, F.Reverse();
12920         if ( helper.IsReversedSubMesh( TopoDS::Face(F) ))
12921           reverse = !reverse;
12922       }
12923       else if ( !data._ignoreFaceIds.count( e2f->first ))
12924       {
12925         // find FACE with layers sharing E
12926         PShapeIteratorPtr fIt = helper.GetAncestors( E, *_mesh, TopAbs_FACE, &data._solid );
12927         if ( fIt->more() )
12928           F = *( fIt->next() );
12929       }
12930       // Find the sub-mesh to add new faces
12931       SMESHDS_SubMesh* sm = 0;
12932       if ( isOnFace )
12933         sm = getMeshDS()->MeshElements( F );
12934       else
12935         sm = data._proxyMesh->getFaceSubM( TopoDS::Face(F), /*create=*/true );
12936       if ( !sm )
12937         return error("error in addBoundaryElements()", data._index);
12938
12939       // Find a proxy sub-mesh of the FACE of an adjacent SOLID, which will use the new boundary
12940       // faces for 3D meshing (PAL23414)
12941       SMESHDS_SubMesh* adjSM = 0;
12942       if ( isOnFace )
12943       {
12944         const TGeomID   faceID = sm->GetID();
12945         PShapeIteratorPtr soIt = helper.GetAncestors( F, *_mesh, TopAbs_SOLID );
12946         while ( const TopoDS_Shape* solid = soIt->next() )
12947           if ( !solid->IsSame( data._solid ))
12948           {
12949             size_t iData = _solids.FindIndex( *solid ) - 1;
12950             if ( iData < _sdVec.size() &&
12951                  _sdVec[ iData ]._ignoreFaceIds.count( faceID ) &&
12952                  _sdVec[ iData ]._shrinkShape2Shape.count( edgeID ) == 0 )
12953             {
12954               SMESH_ProxyMesh::SubMesh* proxySub =
12955                 _sdVec[ iData ]._proxyMesh->getFaceSubM( TopoDS::Face( F ), /*create=*/false);
12956               if ( proxySub && proxySub->NbElements() > 0 )
12957                 adjSM = proxySub;
12958             }
12959           }
12960       }
12961
12962       // Make faces
12963       const int dj1 = reverse ? 0 : 1;
12964       const int dj2 = reverse ? 1 : 0;
12965       vector< const SMDS_MeshElement*> ff; // new faces row
12966       SMESHDS_Mesh* m = getMeshDS();
12967       for ( size_t j = 1; j < ledges.size(); ++j )
12968       {
12969         vector< const SMDS_MeshNode*>&  nn1 = ledges[j-dj1]->_nodes;
12970         vector< const SMDS_MeshNode*>&  nn2 = ledges[j-dj2]->_nodes;
12971         ff.resize( std::max( nn1.size(), nn2.size() ), NULL );
12972         if ( nn1.size() == nn2.size() )
12973         {
12974           if ( isOnFace )
12975             for ( size_t z = 1; z < nn1.size(); ++z )
12976               sm->AddElement( ff[z-1] = m->AddFace( nn1[z-1], nn2[z-1], nn2[z], nn1[z] ));
12977           else
12978             for ( size_t z = 1; z < nn1.size(); ++z )
12979               sm->AddElement( new SMDS_FaceOfNodes( nn1[z-1], nn2[z-1], nn2[z], nn1[z] ));
12980         }
12981         else if ( nn1.size() == 1 )
12982         {
12983           if ( isOnFace )
12984             for ( size_t z = 1; z < nn2.size(); ++z )
12985               sm->AddElement( ff[z-1] = m->AddFace( nn1[0], nn2[z-1], nn2[z] ));
12986           else
12987             for ( size_t z = 1; z < nn2.size(); ++z )
12988               sm->AddElement( new SMDS_FaceOfNodes( nn1[0], nn2[z-1], nn2[z] ));
12989         }
12990         else
12991         {
12992           if ( isOnFace )
12993             for ( size_t z = 1; z < nn1.size(); ++z )
12994               sm->AddElement( ff[z-1] = m->AddFace( nn1[z-1], nn2[0], nn1[z] ));
12995           else
12996             for ( size_t z = 1; z < nn1.size(); ++z )
12997               sm->AddElement( new SMDS_FaceOfNodes( nn1[z-1], nn2[0], nn2[z] ));
12998         }
12999
13000         if ( adjSM ) // add faces to a proxy SM of the adjacent SOLID
13001         {
13002           for ( size_t z = 0; z < ff.size(); ++z )
13003             if ( ff[ z ])
13004               adjSM->AddElement( ff[ z ]);
13005           ff.clear();
13006         }
13007       }
13008
13009       // Make edges
13010       for ( int isFirst = 0; isFirst < 2; ++isFirst )
13011       {
13012         _LayerEdge* edge = isFirst ? ledges.front() : ledges.back();
13013         _EdgesOnShape* eos = data.GetShapeEdges( edge );
13014         if ( eos && eos->SWOLType() == TopAbs_EDGE )
13015         {
13016           vector< const SMDS_MeshNode*>&  nn = edge->_nodes;
13017           if ( nn.size() < 2 || nn[1]->NbInverseElements( SMDSAbs_Edge ) >= 2 )
13018             continue;
13019           helper.SetSubShape( eos->_sWOL );
13020           helper.SetElementsOnShape( true );
13021           for ( size_t z = 1; z < nn.size(); ++z )
13022             helper.AddEdge( nn[z-1], nn[z] );
13023         }
13024       }
13025
13026     } // loop on EDGE's
13027   } // loop on _SolidData's
13028
13029   return addAllBoundaryElements;
13030 }