Salome HOME
Improve error message
[modules/smesh.git] / src / StdMeshers / StdMeshers_ViscousLayers.cxx
1 // Copyright (C) 2007-2022  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 // File      : StdMeshers_ViscousLayers.cxx
21 // Created   : Wed Dec  1 15:15:34 2010
22 // Author    : Edward AGAPOV (eap)
23
24 #include "StdMeshers_ViscousLayers.hxx"
25
26 #include "ObjectPool.hxx"
27 #include "SMDS_EdgePosition.hxx"
28 #include "SMDS_FaceOfNodes.hxx"
29 #include "SMDS_FacePosition.hxx"
30 #include "SMDS_MeshNode.hxx"
31 #include "SMDS_PolygonalFaceOfNodes.hxx"
32 #include "SMDS_SetIterator.hxx"
33 #include "SMESHDS_Group.hxx"
34 #include "SMESHDS_Hypothesis.hxx"
35 #include "SMESHDS_Mesh.hxx"
36 #include "SMESH_Algo.hxx"
37 #include "SMESH_Block.hxx"
38 #include "SMESH_ComputeError.hxx"
39 #include "SMESH_ControlsDef.hxx"
40 #include "SMESH_Gen.hxx"
41 #include "SMESH_Group.hxx"
42 #include "SMESH_HypoFilter.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MeshEditor.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_ProxyMesh.hxx"
48 #include "SMESH_subMesh.hxx"
49 #include "SMESH_subMeshEventListener.hxx"
50 #include "StdMeshers_FaceSide.hxx"
51 #include "StdMeshers_ProjectionUtils.hxx"
52 #include "StdMeshers_Quadrangle_2D.hxx"
53 #include "StdMeshers_ViscousLayers2D.hxx"
54
55 #include <Adaptor3d_HSurface.hxx>
56 #include <BRepAdaptor_Curve.hxx>
57 #include <BRepAdaptor_Curve2d.hxx>
58 #include <BRepAdaptor_Surface.hxx>
59 #include <BRepLProp_SLProps.hxx>
60 #include <BRepOffsetAPI_MakeOffsetShape.hxx>
61 #include <BRep_Tool.hxx>
62 #include <Bnd_B2d.hxx>
63 #include <Bnd_B3d.hxx>
64 #include <ElCLib.hxx>
65 #include <GCPnts_AbscissaPoint.hxx>
66 #include <GCPnts_TangentialDeflection.hxx>
67 #include <Geom2d_Circle.hxx>
68 #include <Geom2d_Line.hxx>
69 #include <Geom2d_TrimmedCurve.hxx>
70 #include <GeomAdaptor_Curve.hxx>
71 #include <GeomLib.hxx>
72 #include <Geom_Circle.hxx>
73 #include <Geom_Curve.hxx>
74 #include <Geom_Line.hxx>
75 #include <Geom_TrimmedCurve.hxx>
76 #include <Precision.hxx>
77 #include <Standard_ErrorHandler.hxx>
78 #include <Standard_Failure.hxx>
79 #include <TColStd_Array1OfReal.hxx>
80 #include <TopExp.hxx>
81 #include <TopExp_Explorer.hxx>
82 #include <TopTools_IndexedMapOfShape.hxx>
83 #include <TopTools_ListOfShape.hxx>
84 #include <TopTools_MapIteratorOfMapOfShape.hxx>
85 #include <TopTools_MapOfShape.hxx>
86 #include <TopoDS.hxx>
87 #include <TopoDS_Edge.hxx>
88 #include <TopoDS_Face.hxx>
89 #include <TopoDS_Vertex.hxx>
90 #include <gp_Ax1.hxx>
91 #include <gp_Cone.hxx>
92 #include <gp_Sphere.hxx>
93 #include <gp_Vec.hxx>
94 #include <gp_XY.hxx>
95
96 #include <cmath>
97 #include <limits>
98 #include <list>
99 #include <queue>
100 #include <string>
101 #include <unordered_map>
102
103 #ifdef _DEBUG_
104 #ifndef WIN32
105 #define __myDEBUG
106 #endif
107 //#define __NOT_INVALIDATE_BAD_SMOOTH
108 //#define __NODES_AT_POS
109 #endif
110
111 #define INCREMENTAL_SMOOTH // smooth only if min angle is too small
112 #define BLOCK_INFLATION // of individual _LayerEdge's
113 #define OLD_NEF_POLYGON
114
115 using namespace std;
116
117 //================================================================================
118 namespace VISCOUS_3D
119 {
120   typedef int TGeomID;
121
122   enum UIndex { U_TGT = 1, U_SRC, LEN_TGT };
123
124   const double theMinSmoothCosin = 0.1;
125   const double theSmoothThickToElemSizeRatio = 0.6;
126   const double theMinSmoothTriaAngle = 30;
127   const double theMinSmoothQuadAngle = 45;
128
129   // what part of thickness is allowed till intersection
130   // (defined by SALOME_TESTS/Grids/smesh/viscous_layers_00/A5)
131   const double theThickToIntersection = 1.5;
132
133   bool needSmoothing( double cosin, double tgtThick, double elemSize )
134   {
135     return cosin * tgtThick > theSmoothThickToElemSizeRatio * elemSize;
136   }
137   double getSmoothingThickness( double cosin, double elemSize )
138   {
139     return theSmoothThickToElemSizeRatio * elemSize / cosin;
140   }
141
142   /*!
143    * \brief SMESH_ProxyMesh computed by _ViscousBuilder for a SOLID.
144    * It is stored in a SMESH_subMesh of the SOLID as SMESH_subMeshEventListenerData
145    */
146   struct _MeshOfSolid : public SMESH_ProxyMesh,
147                         public SMESH_subMeshEventListenerData
148   {
149     bool                  _n2nMapComputed;
150     SMESH_ComputeErrorPtr _warning;
151
152     _MeshOfSolid( SMESH_Mesh* mesh)
153       :SMESH_subMeshEventListenerData( /*isDeletable=*/true),_n2nMapComputed(false)
154     {
155       SMESH_ProxyMesh::setMesh( *mesh );
156     }
157
158     // returns submesh for a geom face
159     SMESH_ProxyMesh::SubMesh* getFaceSubM(const TopoDS_Face& F, bool create=false)
160     {
161       TGeomID i = SMESH_ProxyMesh::shapeIndex(F);
162       return create ? SMESH_ProxyMesh::getProxySubMesh(i) : findProxySubMesh(i);
163     }
164     void setNode2Node(const SMDS_MeshNode*                 srcNode,
165                       const SMDS_MeshNode*                 proxyNode,
166                       const SMESH_ProxyMesh::SubMesh* subMesh)
167     {
168       SMESH_ProxyMesh::setNode2Node( srcNode,proxyNode,subMesh);
169     }
170   };
171   //--------------------------------------------------------------------------------
172   /*!
173    * \brief Listener of events of 3D sub-meshes computed with viscous layers.
174    * It is used to clear an inferior dim sub-meshes modified by viscous layers
175    */
176   class _ShrinkShapeListener : SMESH_subMeshEventListener
177   {
178     _ShrinkShapeListener()
179       : SMESH_subMeshEventListener(/*isDeletable=*/false,
180                                    "StdMeshers_ViscousLayers::_ShrinkShapeListener") {}
181   public:
182     static SMESH_subMeshEventListener* Get() { static _ShrinkShapeListener l; return &l; }
183     virtual void ProcessEvent(const int                       event,
184                               const int                       eventType,
185                               SMESH_subMesh*                  solidSM,
186                               SMESH_subMeshEventListenerData* data,
187                               const SMESH_Hypothesis*         hyp)
188     {
189       if ( SMESH_subMesh::COMPUTE_EVENT == eventType && solidSM->IsEmpty() && data )
190       {
191         SMESH_subMeshEventListener::ProcessEvent(event,eventType,solidSM,data,hyp);
192       }
193     }
194   };
195   //--------------------------------------------------------------------------------
196   /*!
197    * \brief Listener of events of 3D sub-meshes computed with viscous layers.
198    * It is used to store data computed by _ViscousBuilder for a sub-mesh and to
199    * delete the data as soon as it has been used
200    */
201   class _ViscousListener : SMESH_subMeshEventListener
202   {
203     _ViscousListener():
204       SMESH_subMeshEventListener(/*isDeletable=*/false,
205                                  "StdMeshers_ViscousLayers::_ViscousListener") {}
206     static SMESH_subMeshEventListener* Get() { static _ViscousListener l; return &l; }
207   public:
208     virtual void ProcessEvent(const int                       event,
209                               const int                       eventType,
210                               SMESH_subMesh*                  subMesh,
211                               SMESH_subMeshEventListenerData* /*data*/,
212                               const SMESH_Hypothesis*         /*hyp*/)
213     {
214       if (( SMESH_subMesh::COMPUTE_EVENT       == eventType ) &&
215           ( SMESH_subMesh::CHECK_COMPUTE_STATE != event &&
216             SMESH_subMesh::SUBMESH_COMPUTED    != event ))
217       {
218         // delete SMESH_ProxyMesh containing temporary faces
219         subMesh->DeleteEventListener( this );
220       }
221     }
222     // Finds or creates proxy mesh of the solid
223     static _MeshOfSolid* GetSolidMesh(SMESH_Mesh*         mesh,
224                                       const TopoDS_Shape& solid,
225                                       bool                toCreate=false)
226     {
227       if ( !mesh ) return 0;
228       SMESH_subMesh* sm = mesh->GetSubMesh(solid);
229       _MeshOfSolid* data = (_MeshOfSolid*) sm->GetEventListenerData( Get() );
230       if ( !data && toCreate )
231       {
232         data = new _MeshOfSolid(mesh);
233         data->mySubMeshes.push_back( sm ); // to find SOLID by _MeshOfSolid
234         sm->SetEventListener( Get(), data, sm );
235       }
236       return data;
237     }
238     // Removes proxy mesh of the solid
239     static void RemoveSolidMesh(SMESH_Mesh* mesh, const TopoDS_Shape& solid)
240     {
241       mesh->GetSubMesh(solid)->DeleteEventListener( _ViscousListener::Get() );
242     }
243   };
244   
245   //================================================================================
246   /*!
247    * \brief sets a sub-mesh event listener to clear sub-meshes of sub-shapes of
248    * the main shape when sub-mesh of the main shape is cleared,
249    * for example to clear sub-meshes of FACEs when sub-mesh of a SOLID
250    * is cleared
251    */
252   //================================================================================
253
254   void ToClearSubWithMain( SMESH_subMesh* sub, const TopoDS_Shape& main)
255   {
256     SMESH_subMesh* mainSM = sub->GetFather()->GetSubMesh( main );
257     SMESH_subMeshEventListenerData* data =
258       mainSM->GetEventListenerData( _ShrinkShapeListener::Get());
259     if ( data )
260     {
261       if ( find( data->mySubMeshes.begin(), data->mySubMeshes.end(), sub ) ==
262            data->mySubMeshes.end())
263         data->mySubMeshes.push_back( sub );
264     }
265     else
266     {
267       data = SMESH_subMeshEventListenerData::MakeData( /*dependent=*/sub );
268       sub->SetEventListener( _ShrinkShapeListener::Get(), data, /*whereToListenTo=*/mainSM );
269     }
270   }
271   struct _SolidData;
272   //--------------------------------------------------------------------------------
273   /*!
274    * \brief Simplex (triangle or tetrahedron) based on 1 (tria) or 2 (tet) nodes of
275    * _LayerEdge and 2 nodes of the mesh surface beening smoothed.
276    * The class is used to check validity of face or volumes around a smoothed node;
277    * it stores only 2 nodes as the other nodes are stored by _LayerEdge.
278    */
279   struct _Simplex
280   {
281     const SMDS_MeshNode *_nPrev, *_nNext; // nodes on a smoothed mesh surface
282     const SMDS_MeshNode *_nOpp; // in 2D case, a node opposite to a smoothed node in QUAD
283     _Simplex(const SMDS_MeshNode* nPrev=0,
284              const SMDS_MeshNode* nNext=0,
285              const SMDS_MeshNode* nOpp=0)
286       : _nPrev(nPrev), _nNext(nNext), _nOpp(nOpp) {}
287     bool IsForward(const gp_XYZ* pntSrc, const gp_XYZ* pntTgt, double& vol) const
288     {
289       const double M[3][3] =
290         {{ _nNext->X() - pntSrc->X(), _nNext->Y() - pntSrc->Y(), _nNext->Z() - pntSrc->Z() },
291          { pntTgt->X() - pntSrc->X(), pntTgt->Y() - pntSrc->Y(), pntTgt->Z() - pntSrc->Z() },
292          { _nPrev->X() - pntSrc->X(), _nPrev->Y() - pntSrc->Y(), _nPrev->Z() - pntSrc->Z() }};
293       vol = ( + M[0][0] * M[1][1] * M[2][2]
294               + M[0][1] * M[1][2] * M[2][0]
295               + M[0][2] * M[1][0] * M[2][1]
296               - M[0][0] * M[1][2] * M[2][1]
297               - M[0][1] * M[1][0] * M[2][2]
298               - M[0][2] * M[1][1] * M[2][0]);
299       return vol > 1e-100;
300     }
301     bool IsForward(const SMDS_MeshNode* nSrc, const gp_XYZ& pTgt, double& vol) const
302     {
303       SMESH_TNodeXYZ pSrc( nSrc );
304       return IsForward( &pSrc, &pTgt, vol );
305     }
306     bool IsForward(const gp_XY&         tgtUV,
307                    const SMDS_MeshNode* smoothedNode,
308                    const TopoDS_Face&   face,
309                    SMESH_MesherHelper&  helper,
310                    const double         refSign) const
311     {
312       gp_XY prevUV = helper.GetNodeUV( face, _nPrev, smoothedNode );
313       gp_XY nextUV = helper.GetNodeUV( face, _nNext, smoothedNode );
314       gp_Vec2d v1( tgtUV, prevUV ), v2( tgtUV, nextUV );
315       double d = v1 ^ v2;
316       return d*refSign > 1e-100;
317     }
318     bool IsMinAngleOK( const gp_XYZ& pTgt, double& minAngle ) const
319     {
320       SMESH_TNodeXYZ pPrev( _nPrev ), pNext( _nNext );
321       if ( !_nOpp ) // triangle
322       {
323         gp_Vec tp( pPrev - pTgt ), pn( pNext - pPrev ), nt( pTgt - pNext );
324         double tp2 = tp.SquareMagnitude();
325         double pn2 = pn.SquareMagnitude();
326         double nt2 = nt.SquareMagnitude();
327
328         if ( tp2 < pn2 && tp2 < nt2 )
329           minAngle = ( nt * -pn ) * ( nt * -pn ) / nt2 / pn2;
330         else if ( pn2 < nt2 )
331           minAngle = ( tp * -nt ) * ( tp * -nt ) / tp2 / nt2;
332         else
333           minAngle = ( pn * -tp ) * ( pn * -tp ) / pn2 / tp2;
334
335         static double theMaxCos2 = ( Cos( theMinSmoothTriaAngle * M_PI / 180. ) *
336                                      Cos( theMinSmoothTriaAngle * M_PI / 180. ));
337         return minAngle < theMaxCos2;
338       }
339       else // quadrangle
340       {
341         SMESH_TNodeXYZ pOpp( _nOpp );
342         gp_Vec tp( pPrev - pTgt ), po( pOpp - pPrev ), on( pNext - pOpp), nt( pTgt - pNext );
343         double tp2 = tp.SquareMagnitude();
344         double po2 = po.SquareMagnitude();
345         double on2 = on.SquareMagnitude();
346         double nt2 = nt.SquareMagnitude();
347         minAngle = Max( Max((( tp * -nt ) * ( tp * -nt ) / tp2 / nt2 ),
348                             (( po * -tp ) * ( po * -tp ) / po2 / tp2 )),
349                         Max((( on * -po ) * ( on * -po ) / on2 / po2 ),
350                             (( nt * -on ) * ( nt * -on ) / nt2 / on2 )));
351
352         static double theMaxCos2 = ( Cos( theMinSmoothQuadAngle * M_PI / 180. ) *
353                                      Cos( theMinSmoothQuadAngle * M_PI / 180. ));
354         return minAngle < theMaxCos2;
355       }
356     }
357     bool IsNeighbour(const _Simplex& other) const
358     {
359       return _nPrev == other._nNext || _nNext == other._nPrev;
360     }
361     bool Includes( const SMDS_MeshNode* node ) const { return _nPrev == node || _nNext == node; }
362     static void GetSimplices( const SMDS_MeshNode* node,
363                               vector<_Simplex>&   simplices,
364                               const set<TGeomID>& ingnoreShapes,
365                               const _SolidData*   dataToCheckOri = 0,
366                               const bool          toSort = false);
367     static void SortSimplices(vector<_Simplex>& simplices);
368   };
369   //--------------------------------------------------------------------------------
370   /*!
371    * Structure used to take into account surface curvature while smoothing
372    */
373   struct _Curvature
374   {
375     double   _r; // radius
376     double   _k; // factor to correct node smoothed position
377     double   _h2lenRatio; // avgNormProj / (2*avgDist)
378     gp_Pnt2d _uv; // UV used in putOnOffsetSurface()
379   public:
380     static _Curvature* New( double avgNormProj, double avgDist );
381     double lenDelta(double len) const { return _k * ( _r + len ); }
382     double lenDeltaByDist(double dist) const { return dist * _h2lenRatio; }
383   };
384   //--------------------------------------------------------------------------------
385
386   struct _2NearEdges;
387   struct _LayerEdge;
388   struct _EdgesOnShape;
389   struct _Smoother1D;
390   struct _Mapper2D;
391   typedef map< const SMDS_MeshNode*, _LayerEdge*, TIDCompare > TNode2Edge;
392
393   //--------------------------------------------------------------------------------
394   /*!
395    * \brief Edge normal to surface, connecting a node on solid surface (_nodes[0])
396    * and a node of the most internal layer (_nodes.back())
397    */
398   struct _LayerEdge
399   {
400     typedef gp_XYZ (_LayerEdge::*PSmooFun)();
401
402     vector< const SMDS_MeshNode*> _nodes;
403
404     gp_XYZ              _normal;    // to boundary of solid
405     vector<gp_XYZ>      _pos;       // points computed during inflation
406     double              _len;       // length achieved with the last inflation step
407     double              _maxLen;    // maximal possible length
408     double              _cosin;     // of angle (_normal ^ surface)
409     double              _minAngle;  // of _simplices
410     double              _lenFactor; // to compute _len taking _cosin into account
411     int                 _flags;
412
413     // simplices connected to the source node (_nodes[0]);
414     // used for smoothing and quality check of _LayerEdge's based on the FACE
415     vector<_Simplex>    _simplices;
416     vector<_LayerEdge*> _neibors; // all surrounding _LayerEdge's
417     PSmooFun            _smooFunction; // smoothing function
418     _Curvature*         _curvature;
419     // data for smoothing of _LayerEdge's based on the EDGE
420     _2NearEdges*        _2neibors;
421
422     enum EFlags { TO_SMOOTH       = 0x0000001,
423                   MOVED           = 0x0000002, // set by _neibors[i]->SetNewLength()
424                   SMOOTHED        = 0x0000004, // set by _LayerEdge::Smooth()
425                   DIFFICULT       = 0x0000008, // near concave VERTEX
426                   ON_CONCAVE_FACE = 0x0000010,
427                   BLOCKED         = 0x0000020, // not to inflate any more
428                   INTERSECTED     = 0x0000040, // close intersection with a face found
429                   NORMAL_UPDATED  = 0x0000080,
430                   UPD_NORMAL_CONV = 0x0000100, // to update normal on boundary of concave FACE
431                   MARKED          = 0x0000200, // local usage
432                   MULTI_NORMAL    = 0x0000400, // a normal is invisible by some of surrounding faces
433                   NEAR_BOUNDARY   = 0x0000800, // is near FACE boundary forcing smooth
434                   SMOOTHED_C1     = 0x0001000, // is on _eosC1
435                   DISTORTED       = 0x0002000, // was bad before smoothing
436                   RISKY_SWOL      = 0x0004000, // SWOL is parallel to a source FACE
437                   SHRUNK          = 0x0008000, // target node reached a tgt position while shrink()
438                   UNUSED_FLAG     = 0x0100000  // to add user flags after
439     };
440     bool Is   ( int flag ) const { return _flags & flag; }
441     void Set  ( int flag ) { _flags |= flag; }
442     void Unset( int flag ) { _flags &= ~flag; }
443     std::string DumpFlags() const; // debug
444
445     void SetNewLength( double len, _EdgesOnShape& eos, SMESH_MesherHelper& helper );
446     bool SetNewLength2d( Handle(Geom_Surface)& surface,
447                          const TopoDS_Face&    F,
448                          _EdgesOnShape&        eos,
449                          SMESH_MesherHelper&   helper );
450     bool UpdatePositionOnSWOL( SMDS_MeshNode*      n,
451                                double              tol,
452                                _EdgesOnShape&      eos,
453                                SMESH_MesherHelper& helper );
454     void SetDataByNeighbors( const SMDS_MeshNode* n1,
455                              const SMDS_MeshNode* n2,
456                              const _EdgesOnShape& eos,
457                              SMESH_MesherHelper&  helper);
458     void Block( _SolidData& data );
459     void InvalidateStep( size_t curStep, const _EdgesOnShape& eos, bool restoreLength=false );
460     void ChooseSmooFunction(const set< TGeomID >& concaveVertices,
461                             const TNode2Edge&     n2eMap);
462     void SmoothPos( const vector< double >& segLen, const double tol );
463     int  GetSmoothedPos( const double tol );
464     int  Smooth(const int step, const bool isConcaveFace, bool findBest);
465     int  Smooth(const int step, bool findBest, vector< _LayerEdge* >& toSmooth );
466     int  CheckNeiborsOnBoundary(vector< _LayerEdge* >* badNeibors = 0, bool * needSmooth = 0 );
467     void SmoothWoCheck();
468     bool SmoothOnEdge(Handle(ShapeAnalysis_Surface)& surface,
469                       const TopoDS_Face&             F,
470                       SMESH_MesherHelper&            helper);
471     void MoveNearConcaVer( const _EdgesOnShape*    eov,
472                            const _EdgesOnShape*    eos,
473                            const int               step,
474                            vector< _LayerEdge* > & badSmooEdges);
475     bool FindIntersection( SMESH_ElementSearcher&   searcher,
476                            double &                 distance,
477                            const double&            epsilon,
478                            _EdgesOnShape&           eos,
479                            const SMDS_MeshElement** face = 0);
480     bool SegTriaInter( const gp_Ax1&        lastSegment,
481                        const gp_XYZ&        p0,
482                        const gp_XYZ&        p1,
483                        const gp_XYZ&        p2,
484                        double&              dist,
485                        const double&        epsilon) const;
486     bool SegTriaInter( const gp_Ax1&        lastSegment,
487                        const SMDS_MeshNode* n0,
488                        const SMDS_MeshNode* n1,
489                        const SMDS_MeshNode* n2,
490                        double&              dist,
491                        const double&        epsilon) const
492     { return SegTriaInter( lastSegment,
493                            SMESH_TNodeXYZ( n0 ), SMESH_TNodeXYZ( n1 ), SMESH_TNodeXYZ( n2 ),
494                            dist, epsilon );
495     }
496     const gp_XYZ& PrevPos() const { return _pos[ _pos.size() - 2 ]; }
497     gp_XYZ PrevCheckPos( _EdgesOnShape* eos=0 ) const;
498     gp_Ax1 LastSegment(double& segLen, _EdgesOnShape& eos) const;
499     gp_XY  LastUV( const TopoDS_Face& F, _EdgesOnShape& eos, int which=-1 ) const;
500     bool   IsOnEdge() const { return _2neibors; }
501     bool   IsOnFace() const { return ( _nodes[0]->GetPosition()->GetDim() == 2 ); }
502     int    BaseShapeDim() const { return _nodes[0]->GetPosition()->GetDim(); }
503     gp_XYZ Copy( _LayerEdge& other, _EdgesOnShape& eos, SMESH_MesherHelper& helper );
504     double SetCosin( double cosin );
505     void   SetNormal( const gp_XYZ& n ) { _normal = n; }
506     void   SetMaxLen( double l ) { _maxLen = l; }
507     int    NbSteps() const { return _pos.size() - 1; } // nb inlation steps
508     bool   IsNeiborOnEdge( const _LayerEdge* edge ) const;
509     void   SetSmooLen( double len ) { // set _len at which smoothing is needed
510       _cosin = len; // as for _LayerEdge's on FACE _cosin is not used
511     }
512     double GetSmooLen() { return _cosin; } // for _LayerEdge's on FACE _cosin is not used
513
514     gp_XYZ smoothLaplacian();
515     gp_XYZ smoothAngular();
516     gp_XYZ smoothLengthWeighted();
517     gp_XYZ smoothCentroidal();
518     gp_XYZ smoothNefPolygon();
519
520     enum { FUN_LAPLACIAN, FUN_LENWEIGHTED, FUN_CENTROIDAL, FUN_NEFPOLY, FUN_ANGULAR, FUN_NB };
521     static const int theNbSmooFuns = FUN_NB;
522     static PSmooFun _funs[theNbSmooFuns];
523     static const char* _funNames[theNbSmooFuns+1];
524     int smooFunID( PSmooFun fun=0) const;
525   };
526   _LayerEdge::PSmooFun _LayerEdge::_funs[theNbSmooFuns] = { &_LayerEdge::smoothLaplacian,
527                                                             &_LayerEdge::smoothLengthWeighted,
528                                                             &_LayerEdge::smoothCentroidal,
529                                                             &_LayerEdge::smoothNefPolygon,
530                                                             &_LayerEdge::smoothAngular };
531   const char* _LayerEdge::_funNames[theNbSmooFuns+1] = { "Laplacian",
532                                                          "LengthWeighted",
533                                                          "Centroidal",
534                                                          "NefPolygon",
535                                                          "Angular",
536                                                          "None"};
537   struct _LayerEdgeCmp
538   {
539     bool operator () (const _LayerEdge* e1, const _LayerEdge* e2) const
540     {
541       const bool cmpNodes = ( e1 && e2 && e1->_nodes.size() && e2->_nodes.size() );
542       return cmpNodes ? ( e1->_nodes[0]->GetID() < e2->_nodes[0]->GetID()) : ( e1 < e2 );
543     }
544   };
545   //--------------------------------------------------------------------------------
546   /*!
547    * A 2D half plane used by _LayerEdge::smoothNefPolygon()
548    */
549   struct _halfPlane
550   {
551     gp_XY _pos, _dir, _inNorm;
552     bool IsOut( const gp_XY p, const double tol ) const
553     {
554       return _inNorm * ( p - _pos ) < -tol;
555     }
556     bool FindIntersection( const _halfPlane& hp, gp_XY & intPnt )
557     {
558       //const double eps = 1e-10;
559       double D = _dir.Crossed( hp._dir );
560       if ( fabs(D) < std::numeric_limits<double>::min())
561         return false;
562       gp_XY vec21 = _pos - hp._pos; 
563       double u = hp._dir.Crossed( vec21 ) / D; 
564       intPnt = _pos + _dir * u;
565       return true;
566     }
567   };
568   //--------------------------------------------------------------------------------
569   /*!
570    * Structure used to smooth a _LayerEdge based on an EDGE.
571    */
572   struct _2NearEdges
573   {
574     double               _wgt  [2]; // weights of _nodes
575     _LayerEdge*          _edges[2];
576
577      // normal to plane passing through _LayerEdge._normal and tangent of EDGE
578     gp_XYZ*              _plnNorm;
579
580     _2NearEdges() { _edges[0]=_edges[1]=0; _plnNorm = 0; }
581     ~_2NearEdges(){ delete _plnNorm; }
582     const SMDS_MeshNode* tgtNode(bool is2nd) {
583       return _edges[is2nd] ? _edges[is2nd]->_nodes.back() : 0;
584     }
585     const SMDS_MeshNode* srcNode(bool is2nd) {
586       return _edges[is2nd] ? _edges[is2nd]->_nodes[0] : 0;
587     }
588     void reverse() {
589       std::swap( _wgt  [0], _wgt  [1] );
590       std::swap( _edges[0], _edges[1] );
591     }
592     void set( _LayerEdge* e1, _LayerEdge* e2, double w1, double w2 ) {
593       _edges[0] = e1; _edges[1] = e2; _wgt[0] = w1; _wgt[1] = w2;
594     }
595     bool include( const _LayerEdge* e ) {
596       return ( _edges[0] == e || _edges[1] == e );
597     }
598   };
599
600
601   //--------------------------------------------------------------------------------
602   /*!
603    * \brief Layers parameters got by averaging several hypotheses
604    */
605   struct AverageHyp
606   {
607     AverageHyp( const StdMeshers_ViscousLayers* hyp = 0 )
608       :_nbLayers(0), _nbHyps(0), _method(0), _thickness(0), _stretchFactor(0)
609     {
610       Add( hyp );
611     }
612     void Add( const StdMeshers_ViscousLayers* hyp )
613     {
614       if ( hyp )
615       {
616         _nbHyps++;
617         _nbLayers       = hyp->GetNumberLayers();
618         //_thickness     += hyp->GetTotalThickness();
619         _thickness      = Max( _thickness, hyp->GetTotalThickness() );
620         _stretchFactor += hyp->GetStretchFactor();
621         _method         = hyp->GetMethod();
622         if ( _groupName.empty() )
623           _groupName = hyp->GetGroupName();
624       }
625     }
626     double GetTotalThickness() const { return _thickness; /*_nbHyps ? _thickness / _nbHyps : 0;*/ }
627     double GetStretchFactor()  const { return _nbHyps ? _stretchFactor / _nbHyps : 0; }
628     int    GetNumberLayers()   const { return _nbLayers; }
629     int    GetMethod()         const { return _method; }
630     bool   ToCreateGroup()     const { return !_groupName.empty(); }
631     const std::string& GetGroupName() const { return _groupName; }
632
633     double Get1stLayerThickness( double realThickness = 0.) const
634     {
635       const double T = ( realThickness > 0 ) ? realThickness : GetTotalThickness();
636       const double f = GetStretchFactor();
637       const int    N = GetNumberLayers();
638       const double fPowN = pow( f, N );
639       double h0;
640       if ( fPowN - 1 <= numeric_limits<double>::min() )
641         h0 = T / N;
642       else
643         h0 = T * ( f - 1 )/( fPowN - 1 );
644       return h0;
645     }
646
647     bool   UseSurfaceNormal()  const
648     { return _method == StdMeshers_ViscousLayers::SURF_OFFSET_SMOOTH; }
649     bool   ToSmooth()          const
650     { return _method == StdMeshers_ViscousLayers::SURF_OFFSET_SMOOTH; }
651     bool   IsOffsetMethod()    const
652     { return _method == StdMeshers_ViscousLayers::FACE_OFFSET; }
653
654     bool operator==( const AverageHyp& other ) const
655     {
656       return ( _nbLayers == other._nbLayers &&
657                _method   == other._method   &&
658                Equals( GetTotalThickness(), other.GetTotalThickness() ) &&
659                Equals( GetStretchFactor(), other.GetStretchFactor() ));
660     }
661     static bool Equals( double v1, double v2 ) { return Abs( v1 - v2 ) < 0.01 * ( v1 + v2 ); }
662
663   private:
664     int         _nbLayers, _nbHyps, _method;
665     double      _thickness, _stretchFactor;
666     std::string _groupName;
667   };
668
669   //--------------------------------------------------------------------------------
670   /*!
671    * \brief _LayerEdge's on a shape and other shape data
672    */
673   struct _EdgesOnShape
674   {
675     vector< _LayerEdge* > _edges;
676
677     TopoDS_Shape          _shape;
678     TGeomID               _shapeID;
679     SMESH_subMesh *       _subMesh;
680     // face or edge w/o layer along or near which _edges are inflated
681     TopoDS_Shape          _sWOL;
682     bool                  _isRegularSWOL; // w/o singularities
683     // averaged StdMeshers_ViscousLayers parameters
684     AverageHyp            _hyp;
685     bool                  _toSmooth;
686     _Smoother1D*          _edgeSmoother;
687     vector< _EdgesOnShape* > _eosConcaVer; // edges at concave VERTEXes of a FACE
688     vector< _EdgesOnShape* > _eosC1; // to smooth together several C1 continues shapes
689
690     typedef std::unordered_map< const SMDS_MeshElement*, gp_XYZ > TFace2NormMap;
691     TFace2NormMap            _faceNormals; // if _shape is FACE
692     vector< _EdgesOnShape* > _faceEOS; // to get _faceNormals of adjacent FACEs
693
694     Handle(ShapeAnalysis_Surface) _offsetSurf;
695     _LayerEdge*                   _edgeForOffset;
696     double                        _offsetValue;
697     _Mapper2D*                    _mapper2D;
698
699     _SolidData*            _data; // parent SOLID
700
701     _LayerEdge*      operator[](size_t i) const { return (_LayerEdge*) _edges[i]; }
702     size_t           size() const { return _edges.size(); }
703     TopAbs_ShapeEnum ShapeType() const
704     { return _shape.IsNull() ? TopAbs_SHAPE : _shape.ShapeType(); }
705     TopAbs_ShapeEnum SWOLType() const
706     { return _sWOL.IsNull() ? TopAbs_SHAPE : _sWOL.ShapeType(); }
707     bool             HasC1( const _EdgesOnShape* other ) const
708     { return std::find( _eosC1.begin(), _eosC1.end(), other ) != _eosC1.end(); }
709     bool             GetNormal( const SMDS_MeshElement* face, gp_Vec& norm );
710     _SolidData&      GetData() const { return *_data; }
711     char             ShapeTypeLetter() const
712     { switch ( ShapeType() ) { case TopAbs_FACE: return 'F'; case TopAbs_EDGE: return 'E';
713       case TopAbs_VERTEX: return 'V'; default: return 'S'; }}
714
715     _EdgesOnShape(): _shapeID(-1), _subMesh(0), _toSmooth(false), _edgeSmoother(0), _mapper2D(0) {}
716     ~_EdgesOnShape();
717   };
718
719   //--------------------------------------------------------------------------------
720   /*!
721    * \brief Convex FACE whose radius of curvature is less than the thickness of
722    *        layers. It is used to detect distortion of prisms based on a convex
723    *        FACE and to update normals to enable further increasing the thickness
724    */
725   struct _ConvexFace
726   {
727     TopoDS_Face                     _face;
728
729     // edges whose _simplices are used to detect prism distortion
730     vector< _LayerEdge* >           _simplexTestEdges;
731
732     // map a sub-shape to _SolidData::_edgesOnShape
733     map< TGeomID, _EdgesOnShape* >  _subIdToEOS;
734
735     bool                            _isTooCurved;
736     bool                            _normalsFixed;
737     bool                            _normalsFixedOnBorders; // used in putOnOffsetSurface()
738
739     double GetMaxCurvature( _SolidData&         data,
740                             _EdgesOnShape&      eof,
741                             BRepLProp_SLProps&  surfProp,
742                             SMESH_MesherHelper& helper);
743
744     bool GetCenterOfCurvature( _LayerEdge*         ledge,
745                                BRepLProp_SLProps&  surfProp,
746                                SMESH_MesherHelper& helper,
747                                gp_Pnt &            center ) const;
748     bool CheckPrisms() const;
749   };
750
751   //--------------------------------------------------------------------------------
752   /*!
753    * \brief Structure holding _LayerEdge's based on EDGEs that will collide
754    *        at inflation up to the full thickness. A detected collision
755    *        is fixed in updateNormals()
756    */
757   struct _CollisionEdges
758   {
759     _LayerEdge*           _edge;
760     vector< _LayerEdge* > _intEdges; // each pair forms an intersected quadrangle
761     const SMDS_MeshNode* nSrc(int i) const { return _intEdges[i]->_nodes[0]; }
762     const SMDS_MeshNode* nTgt(int i) const { return _intEdges[i]->_nodes.back(); }
763   };
764
765   //--------------------------------------------------------------------------------
766   /*!
767    * \brief Data of a SOLID
768    */
769   struct _SolidData
770   {
771     typedef const StdMeshers_ViscousLayers* THyp;
772     TopoDS_Shape                    _solid;
773     TopTools_MapOfShape             _before; // SOLIDs to be computed before _solid
774     TGeomID                         _index; // SOLID id
775     _MeshOfSolid*                   _proxyMesh;
776     bool                            _done;
777     list< THyp >                    _hyps;
778     list< TopoDS_Shape >            _hypShapes;
779     map< TGeomID, THyp >            _face2hyp; // filled if _hyps.size() > 1
780     set< TGeomID >                  _reversedFaceIds;
781     set< TGeomID >                  _ignoreFaceIds; // WOL FACEs and FACEs of other SOLIDs
782
783     double                          _stepSize, _stepSizeCoeff, _geomSize;
784     const SMDS_MeshNode*            _stepSizeNodes[2];
785
786     TNode2Edge                      _n2eMap; // nodes and _LayerEdge's based on them
787
788     // map to find _n2eMap of another _SolidData by a shrink shape shared by two _SolidData's
789     map< TGeomID, TNode2Edge* >     _s2neMap;
790     // _LayerEdge's with underlying shapes
791     vector< _EdgesOnShape >         _edgesOnShape;
792
793     // key:   an ID of shape (EDGE or VERTEX) shared by a FACE with
794     //        layers and a FACE w/o layers
795     // value: the shape (FACE or EDGE) to shrink mesh on.
796     //       _LayerEdge's basing on nodes on key shape are inflated along the value shape
797     map< TGeomID, TopoDS_Shape >     _shrinkShape2Shape;
798
799     // Convex FACEs whose radius of curvature is less than the thickness of layers
800     map< TGeomID, _ConvexFace >      _convexFaces;
801
802     // shapes (EDGEs and VERTEXes) shrink from which is forbidden due to collisions with
803     // the adjacent SOLID
804     set< TGeomID >                   _noShrinkShapes;
805
806     int                              _nbShapesToSmooth;
807
808     vector< _CollisionEdges >        _collisionEdges;
809     set< TGeomID >                   _concaveFaces;
810
811     double                           _maxThickness; // of all _hyps
812     double                           _minThickness; // of all _hyps
813
814     double                           _epsilon; // precision for SegTriaInter()
815
816     SMESH_MesherHelper*              _helper;
817
818     _SolidData(const TopoDS_Shape& s=TopoDS_Shape(),
819                _MeshOfSolid*       m=0)
820       :_solid(s), _proxyMesh(m), _done(false),_helper(0) {}
821     ~_SolidData() { delete _helper; _helper = 0; }
822
823     void SortOnEdge( const TopoDS_Edge& E, vector< _LayerEdge* >& edges);
824     void Sort2NeiborsOnEdge( vector< _LayerEdge* >& edges );
825
826     _ConvexFace* GetConvexFace( const TGeomID faceID ) {
827       map< TGeomID, _ConvexFace >::iterator id2face = _convexFaces.find( faceID );
828       return id2face == _convexFaces.end() ? 0 : & id2face->second;
829     }
830     _EdgesOnShape* GetShapeEdges(const TGeomID       shapeID );
831     _EdgesOnShape* GetShapeEdges(const TopoDS_Shape& shape );
832     _EdgesOnShape* GetShapeEdges(const _LayerEdge*   edge )
833     { return GetShapeEdges( edge->_nodes[0]->getshapeId() ); }
834
835     SMESH_MesherHelper& GetHelper() const { return *_helper; }
836
837     void UnmarkEdges( int flag = _LayerEdge::MARKED ) {
838       for ( size_t i = 0; i < _edgesOnShape.size(); ++i )
839         for ( size_t j = 0; j < _edgesOnShape[i]._edges.size(); ++j )
840           _edgesOnShape[i]._edges[j]->Unset( flag );
841     }
842     void AddShapesToSmooth( const set< _EdgesOnShape* >& shape,
843                             const set< _EdgesOnShape* >* edgesNoAnaSmooth=0 );
844
845     void PrepareEdgesToSmoothOnFace( _EdgesOnShape* eof, bool substituteSrcNodes );
846   };
847   //--------------------------------------------------------------------------------
848   /*!
849    * \brief Offset plane used in getNormalByOffset()
850    */
851   struct _OffsetPlane
852   {
853     gp_Pln _plane;
854     int    _faceIndex;
855     int    _faceIndexNext[2];
856     gp_Lin _lines[2]; // line of intersection with neighbor _OffsetPlane's
857     bool   _isLineOK[2];
858     _OffsetPlane() {
859       _isLineOK[0] = _isLineOK[1] = false; _faceIndexNext[0] = _faceIndexNext[1] = -1;
860     }
861     void   ComputeIntersectionLine( _OffsetPlane&        pln, 
862                                     const TopoDS_Edge&   E,
863                                     const TopoDS_Vertex& V );
864     gp_XYZ GetCommonPoint(bool& isFound, const TopoDS_Vertex& V) const;
865     int    NbLines() const { return _isLineOK[0] + _isLineOK[1]; }
866   };
867   //--------------------------------------------------------------------------------
868   /*!
869    * \brief Container of centers of curvature at nodes on an EDGE bounding _ConvexFace
870    */
871   struct _CentralCurveOnEdge
872   {
873     bool                  _isDegenerated;
874     vector< gp_Pnt >      _curvaCenters;
875     vector< _LayerEdge* > _ledges;
876     vector< gp_XYZ >      _normals; // new normal for each of _ledges
877     vector< double >      _segLength2;
878
879     TopoDS_Edge           _edge;
880     TopoDS_Face           _adjFace;
881     bool                  _adjFaceToSmooth;
882
883     void Append( const gp_Pnt& center, _LayerEdge* ledge )
884     {
885       if ( ledge->Is( _LayerEdge::MULTI_NORMAL ))
886         return;
887       if ( _curvaCenters.size() > 0 )
888         _segLength2.push_back( center.SquareDistance( _curvaCenters.back() ));
889       _curvaCenters.push_back( center );
890       _ledges.push_back( ledge );
891       _normals.push_back( ledge->_normal );
892     }
893     bool FindNewNormal( const gp_Pnt& center, gp_XYZ& newNormal );
894     void SetShapes( const TopoDS_Edge&  edge,
895                     const _ConvexFace&  convFace,
896                     _SolidData&         data,
897                     SMESH_MesherHelper& helper);
898   };
899   //--------------------------------------------------------------------------------
900   /*!
901    * \brief Data of node on a shrinked FACE
902    */
903   struct _SmoothNode
904   {
905     const SMDS_MeshNode*         _node;
906     vector<_Simplex>             _simplices; // for quality check
907
908     enum SmoothType { LAPLACIAN, CENTROIDAL, ANGULAR, TFI };
909
910     bool Smooth(int&                  badNb,
911                 Handle(Geom_Surface)& surface,
912                 SMESH_MesherHelper&   helper,
913                 const double          refSign,
914                 SmoothType            how,
915                 bool                  set3D);
916
917     gp_XY computeAngularPos(vector<gp_XY>& uv,
918                             const gp_XY&   uvToFix,
919                             const double   refSign );
920   };
921   struct PyDump;
922   struct Periodicity;
923   //--------------------------------------------------------------------------------
924   /*!
925    * \brief Builder of viscous layers
926    */
927   class _ViscousBuilder
928   {
929   public:
930     _ViscousBuilder();
931     // does it's job
932     SMESH_ComputeErrorPtr Compute(SMESH_Mesh&         mesh,
933                                   const TopoDS_Shape& shape);
934     // check validity of hypotheses
935     SMESH_ComputeErrorPtr CheckHypotheses( SMESH_Mesh&         mesh,
936                                            const TopoDS_Shape& shape );
937
938     // restore event listeners used to clear an inferior dim sub-mesh modified by viscous layers
939     void RestoreListeners();
940
941     // computes SMESH_ProxyMesh::SubMesh::_n2n;
942     bool MakeN2NMap( _MeshOfSolid* pm );
943
944   private:
945
946     bool findSolidsWithLayers(const bool checkFaceMesh=true);
947     bool setBefore( _SolidData& solidBefore, _SolidData& solidAfter );
948     bool findFacesWithLayers(const bool onlyWith=false);
949     void findPeriodicFaces();
950     void getIgnoreFaces(const TopoDS_Shape&             solid,
951                         const StdMeshers_ViscousLayers* hyp,
952                         const TopoDS_Shape&             hypShape,
953                         set<TGeomID>&                   ignoreFaces);
954     int makeEdgesOnShape();
955     bool makeLayer(_SolidData& data);
956     void setShapeData( _EdgesOnShape& eos, SMESH_subMesh* sm, _SolidData& data );
957     bool setEdgeData( _LayerEdge& edge, _EdgesOnShape& eos,
958                       SMESH_MesherHelper& helper, _SolidData& data);
959     gp_XYZ getFaceNormal(const SMDS_MeshNode* n,
960                          const TopoDS_Face&   face,
961                          SMESH_MesherHelper&  helper,
962                          bool&                isOK,
963                          bool                 shiftInside=false);
964     bool getFaceNormalAtSingularity(const gp_XY&        uv,
965                                     const TopoDS_Face&  face,
966                                     SMESH_MesherHelper& helper,
967                                     gp_Dir&             normal );
968     gp_XYZ getWeigthedNormal( const _LayerEdge*                edge );
969     gp_XYZ getNormalByOffset( _LayerEdge*                      edge,
970                               std::pair< TopoDS_Face, gp_XYZ > fId2Normal[],
971                               int                              nbFaces,
972                               bool                             lastNoOffset = false);
973     bool findNeiborsOnEdge(const _LayerEdge*     edge,
974                            const SMDS_MeshNode*& n1,
975                            const SMDS_MeshNode*& n2,
976                            _EdgesOnShape&        eos,
977                            _SolidData&           data);
978     void findSimplexTestEdges( _SolidData&                    data,
979                                vector< vector<_LayerEdge*> >& edgesByGeom);
980     void computeGeomSize( _SolidData& data );
981     bool findShapesToSmooth( _SolidData& data);
982     void limitStepSizeByCurvature( _SolidData&  data );
983     void limitStepSize( _SolidData&             data,
984                         const SMDS_MeshElement* face,
985                         const _LayerEdge*       maxCosinEdge );
986     void limitStepSize( _SolidData& data, const double minSize);
987     bool inflate(_SolidData& data);
988     bool smoothAndCheck(_SolidData& data, const int nbSteps, double & distToIntersection);
989     int  invalidateBadSmooth( _SolidData&               data,
990                               SMESH_MesherHelper&       helper,
991                               vector< _LayerEdge* >&    badSmooEdges,
992                               vector< _EdgesOnShape* >& eosC1,
993                               const int                 infStep );
994     void makeOffsetSurface( _EdgesOnShape& eos, SMESH_MesherHelper& );
995     void putOnOffsetSurface( _EdgesOnShape& eos, int infStep,
996                              vector< _EdgesOnShape* >& eosC1,
997                              int smooStep=0, int moveAll=false );
998     void findCollisionEdges( _SolidData& data, SMESH_MesherHelper& helper );
999     void findEdgesToUpdateNormalNearConvexFace( _ConvexFace &       convFace,
1000                                                 _SolidData&         data,
1001                                                 SMESH_MesherHelper& helper );
1002     void limitMaxLenByCurvature( _SolidData& data, SMESH_MesherHelper& helper );
1003     void limitMaxLenByCurvature( _LayerEdge* e1, _LayerEdge* e2,
1004                                  _EdgesOnShape& eos1, _EdgesOnShape& eos2,
1005                                  const bool isSmoothable );
1006     bool updateNormals( _SolidData& data, SMESH_MesherHelper& helper, int stepNb, double stepSize );
1007     bool updateNormalsOfConvexFaces( _SolidData&         data,
1008                                      SMESH_MesherHelper& helper,
1009                                      int                 stepNb );
1010     void updateNormalsOfC1Vertices( _SolidData& data );
1011     bool updateNormalsOfSmoothed( _SolidData&         data,
1012                                   SMESH_MesherHelper& helper,
1013                                   const int           nbSteps,
1014                                   const double        stepSize );
1015     bool isNewNormalOk( _SolidData&   data,
1016                         _LayerEdge&   edge,
1017                         const gp_XYZ& newNormal);
1018     bool refine(_SolidData& data);
1019     bool shrink(_SolidData& data);
1020     bool prepareEdgeToShrink( _LayerEdge& edge, _EdgesOnShape& eos,
1021                               SMESH_MesherHelper& helper,
1022                               const SMESHDS_SubMesh* faceSubMesh );
1023     void restoreNoShrink( _LayerEdge& edge ) const;
1024     void fixBadFaces(const TopoDS_Face&          F,
1025                      SMESH_MesherHelper&         helper,
1026                      const bool                  is2D,
1027                      const int                   step,
1028                      set<const SMDS_MeshNode*> * involvedNodes=NULL);
1029     bool addBoundaryElements(_SolidData& data);
1030
1031     bool error( const string& text, int solidID=-1 );
1032     SMESHDS_Mesh* getMeshDS() const { return _mesh->GetMeshDS(); }
1033
1034     // debug
1035     void makeGroupOfLE();
1036
1037     SMESH_Mesh*                  _mesh;
1038     SMESH_ComputeErrorPtr        _error;
1039
1040     vector<                      _SolidData >  _sdVec;
1041     TopTools_IndexedMapOfShape   _solids; // to find _SolidData by a solid
1042     TopTools_MapOfShape          _shrunkFaces;
1043     std::unique_ptr<Periodicity> _periodicity;
1044
1045     int                          _tmpFaceID;
1046     PyDump*                      _pyDump;
1047   };
1048   //--------------------------------------------------------------------------------
1049   /*!
1050    * \brief Shrinker of nodes on the EDGE
1051    */
1052   class _Shrinker1D
1053   {
1054     TopoDS_Edge                   _geomEdge;
1055     vector<double>                _initU;
1056     vector<double>                _normPar;
1057     vector<const SMDS_MeshNode*>  _nodes;
1058     const _LayerEdge*             _edges[2];
1059     bool                          _done;
1060   public:
1061     void AddEdge( const _LayerEdge* e, _EdgesOnShape& eos, SMESH_MesherHelper& helper );
1062     void Compute(bool set3D, SMESH_MesherHelper& helper);
1063     void RestoreParams();
1064     void SwapSrcTgtNodes(SMESHDS_Mesh* mesh);
1065     const TopoDS_Edge& GeomEdge() const { return _geomEdge; }
1066     const SMDS_MeshNode* TgtNode( bool is2nd ) const
1067     { return _edges[is2nd] ? _edges[is2nd]->_nodes.back() : 0; }
1068     const SMDS_MeshNode* SrcNode( bool is2nd ) const
1069     { return _edges[is2nd] ? _edges[is2nd]->_nodes[0] : 0; }
1070   };
1071   //--------------------------------------------------------------------------------
1072   /*!
1073    * \brief Smoother of _LayerEdge's on EDGE.
1074    */
1075   struct _Smoother1D
1076   {
1077     struct OffPnt // point of the offsetted EDGE
1078     {
1079       gp_XYZ      _xyz;    // coord of a point inflated from EDGE w/o smooth
1080       double      _len;    // length reached at previous inflation step
1081       double      _param;  // on EDGE
1082       _2NearEdges _2edges; // 2 neighbor _LayerEdge's
1083       gp_XYZ      _edgeDir;// EDGE tangent at _param
1084       double Distance( const OffPnt& p ) const { return ( _xyz - p._xyz ).Modulus(); }
1085     };
1086     vector< OffPnt >   _offPoints;
1087     vector< double >   _leParams; // normalized param of _eos._edges on EDGE
1088     Handle(Geom_Curve) _anaCurve; // for analytic smooth
1089     _LayerEdge         _leOnV[2]; // _LayerEdge's holding normal to the EDGE at VERTEXes
1090     gp_XYZ             _edgeDir[2]; // tangent at VERTEXes
1091     size_t             _iSeg[2];  // index of segment where extreme tgt node is projected
1092     _EdgesOnShape&     _eos;
1093     double             _curveLen; // length of the EDGE
1094     std::pair<int,int> _eToSmooth[2]; // <from,to> indices of _LayerEdge's in _eos
1095
1096     static Handle(Geom_Curve) CurveForSmooth( const TopoDS_Edge&  E,
1097                                               _EdgesOnShape&      eos,
1098                                               SMESH_MesherHelper& helper);
1099
1100     _Smoother1D( Handle(Geom_Curve) curveForSmooth,
1101                  _EdgesOnShape&     eos )
1102       : _anaCurve( curveForSmooth ), _eos( eos )
1103     {
1104     }
1105     bool Perform(_SolidData&                    data,
1106                  Handle(ShapeAnalysis_Surface)& surface,
1107                  const TopoDS_Face&             F,
1108                  SMESH_MesherHelper&            helper );
1109
1110     void prepare(_SolidData& data );
1111
1112     void findEdgesToSmooth();
1113
1114     bool isToSmooth( int iE );
1115
1116     bool smoothAnalyticEdge( _SolidData&                    data,
1117                              Handle(ShapeAnalysis_Surface)& surface,
1118                              const TopoDS_Face&             F,
1119                              SMESH_MesherHelper&            helper);
1120     bool smoothComplexEdge( _SolidData&                     data,
1121                             Handle(ShapeAnalysis_Surface)& surface,
1122                             const TopoDS_Face&             F,
1123                             SMESH_MesherHelper&            helper);
1124     gp_XYZ getNormalNormal( const gp_XYZ & normal,
1125                             const gp_XYZ&  edgeDir);
1126     _LayerEdge* getLEdgeOnV( bool is2nd )
1127     {
1128       return _eos._edges[ is2nd ? _eos._edges.size()-1 : 0 ]->_2neibors->_edges[ is2nd ];
1129     }
1130     bool isAnalytic() const { return !_anaCurve.IsNull(); }
1131
1132     void offPointsToPython() const; // debug
1133   };
1134
1135   //--------------------------------------------------------------------------------
1136   /*!
1137    * \brief Compute positions of nodes of 2D structured mesh using TFI
1138    */
1139   class _Mapper2D
1140   {
1141     FaceQuadStruct _quadPoints;
1142
1143     UVPtStruct& uvPnt( size_t i, size_t j ) { return _quadPoints.UVPt( i, j ); }
1144
1145   public:
1146     _Mapper2D( const TParam2ColumnMap & param2ColumnMap, const TNode2Edge& n2eMap );
1147     bool ComputeNodePositions();
1148   };
1149
1150   //--------------------------------------------------------------------------------
1151   /*!
1152    * \brief Class of temporary mesh face.
1153    * We can't use SMDS_FaceOfNodes since it's impossible to set it's ID which is
1154    * needed because SMESH_ElementSearcher internally uses set of elements sorted by ID
1155    */
1156   struct _TmpMeshFace : public SMDS_PolygonalFaceOfNodes
1157   {
1158     const SMDS_MeshElement* _srcFace;
1159
1160     _TmpMeshFace( const vector<const SMDS_MeshNode*>& nodes,
1161                   int                                 ID,
1162                   int                                 faceID=-1,
1163                   const SMDS_MeshElement*             srcFace=0 ):
1164       SMDS_PolygonalFaceOfNodes(nodes), _srcFace( srcFace ) { setID( ID ); setShapeID( faceID ); }
1165     virtual SMDSAbs_EntityType  GetEntityType() const
1166     { return _srcFace ? _srcFace->GetEntityType() : SMDSEntity_Quadrangle; }
1167     virtual SMDSAbs_GeometryType GetGeomType()  const
1168     { return _srcFace ? _srcFace->GetGeomType() : SMDSGeom_QUADRANGLE; }
1169   };
1170   //--------------------------------------------------------------------------------
1171   /*!
1172    * \brief Class of temporary mesh quadrangle face storing _LayerEdge it's based on
1173    */
1174   struct _TmpMeshFaceOnEdge : public _TmpMeshFace
1175   {
1176     _LayerEdge *_le1, *_le2;
1177     _TmpMeshFaceOnEdge( _LayerEdge* le1, _LayerEdge* le2, int ID ):
1178       _TmpMeshFace( vector<const SMDS_MeshNode*>(4), ID ), _le1(le1), _le2(le2)
1179     {
1180       myNodes[0]=_le1->_nodes[0];
1181       myNodes[1]=_le1->_nodes.back();
1182       myNodes[2]=_le2->_nodes.back();
1183       myNodes[3]=_le2->_nodes[0];
1184     }
1185     const SMDS_MeshNode* n( size_t i ) const
1186     {
1187       return myNodes[ i ];
1188     }
1189     gp_XYZ GetDir() const // return average direction of _LayerEdge's, normal to EDGE
1190     {
1191       SMESH_TNodeXYZ p0s( myNodes[0] );
1192       SMESH_TNodeXYZ p0t( myNodes[1] );
1193       SMESH_TNodeXYZ p1t( myNodes[2] );
1194       SMESH_TNodeXYZ p1s( myNodes[3] );
1195       gp_XYZ  v0 = p0t - p0s;
1196       gp_XYZ  v1 = p1t - p1s;
1197       gp_XYZ v01 = p1s - p0s;
1198       gp_XYZ   n = ( v0 ^ v01 ) + ( v1 ^ v01 );
1199       gp_XYZ   d = v01 ^ n;
1200       d.Normalize();
1201       return d;
1202     }
1203     gp_XYZ GetDir(_LayerEdge* le1, _LayerEdge* le2) // return average direction of _LayerEdge's
1204     {
1205       myNodes[0]=le1->_nodes[0];
1206       myNodes[1]=le1->_nodes.back();
1207       myNodes[2]=le2->_nodes.back();
1208       myNodes[3]=le2->_nodes[0];
1209       return GetDir();
1210     }
1211   };
1212   //--------------------------------------------------------------------------------
1213   /*!
1214    * \brief Retriever of node coordinates either directly or from a surface by node UV.
1215    * \warning Location of a surface is ignored
1216    */
1217   struct _NodeCoordHelper
1218   {
1219     SMESH_MesherHelper&        _helper;
1220     const TopoDS_Face&         _face;
1221     Handle(Geom_Surface)       _surface;
1222     gp_XYZ (_NodeCoordHelper::* _fun)(const SMDS_MeshNode* n) const;
1223
1224     _NodeCoordHelper(const TopoDS_Face& F, SMESH_MesherHelper& helper, bool is2D)
1225       : _helper( helper ), _face( F )
1226     {
1227       if ( is2D )
1228       {
1229         TopLoc_Location loc;
1230         _surface = BRep_Tool::Surface( _face, loc );
1231       }
1232       if ( _surface.IsNull() )
1233         _fun = & _NodeCoordHelper::direct;
1234       else
1235         _fun = & _NodeCoordHelper::byUV;
1236     }
1237     gp_XYZ operator()(const SMDS_MeshNode* n) const { return (this->*_fun)( n ); }
1238
1239   private:
1240     gp_XYZ direct(const SMDS_MeshNode* n) const
1241     {
1242       return SMESH_TNodeXYZ( n );
1243     }
1244     gp_XYZ byUV  (const SMDS_MeshNode* n) const
1245     {
1246       gp_XY uv = _helper.GetNodeUV( _face, n );
1247       return _surface->Value( uv.X(), uv.Y() ).XYZ();
1248     }
1249   };
1250
1251   //================================================================================
1252   /*!
1253    * \brief Check angle between vectors 
1254    */
1255   //================================================================================
1256
1257   inline bool isLessAngle( const gp_Vec& v1, const gp_Vec& v2, const double cos )
1258   {
1259     double dot = v1 * v2; // cos * |v1| * |v2|
1260     double l1  = v1.SquareMagnitude();
1261     double l2  = v2.SquareMagnitude();
1262     return (( dot * cos >= 0 ) && 
1263             ( dot * dot ) / l1 / l2 >= ( cos * cos ));
1264   }
1265
1266   class _Factory
1267   {
1268     ObjectPool< _LayerEdge >  _edgePool;
1269     ObjectPool< _Curvature >  _curvaturePool;
1270     ObjectPool< _2NearEdges > _nearEdgesPool;
1271
1272     static _Factory* & me()
1273     {
1274       static _Factory* theFactory = 0;
1275       return theFactory;
1276     }
1277   public:
1278
1279     _Factory()  { me() = this; }
1280     ~_Factory() { me() = 0; }
1281
1282     static _LayerEdge*  NewLayerEdge() { return me()->_edgePool.getNew(); }
1283     static _Curvature * NewCurvature() { return me()->_curvaturePool.getNew(); }
1284     static _2NearEdges* NewNearEdges() { return me()->_nearEdgesPool.getNew(); }
1285   };
1286
1287 } // namespace VISCOUS_3D
1288
1289
1290
1291 //================================================================================
1292 // StdMeshers_ViscousLayers hypothesis
1293 //
1294 StdMeshers_ViscousLayers::StdMeshers_ViscousLayers(int hypId, SMESH_Gen* gen)
1295   :SMESH_Hypothesis(hypId, gen),
1296    _isToIgnoreShapes(1), _nbLayers(1), _thickness(1), _stretchFactor(1),
1297    _method( SURF_OFFSET_SMOOTH ),
1298    _groupName("")
1299 {
1300   _name = StdMeshers_ViscousLayers::GetHypType();
1301   _param_algo_dim = -3; // auxiliary hyp used by 3D algos
1302 } // --------------------------------------------------------------------------------
1303 void StdMeshers_ViscousLayers::SetBndShapes(const std::vector<int>& faceIds, bool toIgnore)
1304 {
1305   if ( faceIds != _shapeIds )
1306     _shapeIds = faceIds, NotifySubMeshesHypothesisModification();
1307   if ( _isToIgnoreShapes != toIgnore )
1308     _isToIgnoreShapes = toIgnore, NotifySubMeshesHypothesisModification();
1309 } // --------------------------------------------------------------------------------
1310 void StdMeshers_ViscousLayers::SetTotalThickness(double thickness)
1311 {
1312   if ( thickness != _thickness )
1313     _thickness = thickness, NotifySubMeshesHypothesisModification();
1314 } // --------------------------------------------------------------------------------
1315 void StdMeshers_ViscousLayers::SetNumberLayers(int nb)
1316 {
1317   if ( _nbLayers != nb )
1318     _nbLayers = nb, NotifySubMeshesHypothesisModification();
1319 } // --------------------------------------------------------------------------------
1320 void StdMeshers_ViscousLayers::SetStretchFactor(double factor)
1321 {
1322   if ( _stretchFactor != factor )
1323     _stretchFactor = factor, NotifySubMeshesHypothesisModification();
1324 } // --------------------------------------------------------------------------------
1325 void StdMeshers_ViscousLayers::SetMethod( ExtrusionMethod method )
1326 {
1327   if ( _method != method )
1328     _method = method, NotifySubMeshesHypothesisModification();
1329 } // --------------------------------------------------------------------------------
1330 void StdMeshers_ViscousLayers::SetGroupName(const std::string& name)
1331 {
1332   if ( _groupName != name )
1333   {
1334     _groupName = name;
1335     if ( !_groupName.empty() )
1336       NotifySubMeshesHypothesisModification();
1337   }
1338 } // --------------------------------------------------------------------------------
1339 SMESH_ProxyMesh::Ptr
1340 StdMeshers_ViscousLayers::Compute(SMESH_Mesh&         theMesh,
1341                                   const TopoDS_Shape& theShape,
1342                                   const bool          toMakeN2NMap) const
1343 {
1344   using namespace VISCOUS_3D;
1345   _ViscousBuilder builder;
1346   SMESH_ComputeErrorPtr err = builder.Compute( theMesh, theShape );
1347   if ( err && !err->IsOK() )
1348     return SMESH_ProxyMesh::Ptr();
1349
1350   vector<SMESH_ProxyMesh::Ptr> components;
1351   TopExp_Explorer exp( theShape, TopAbs_SOLID );
1352   for ( ; exp.More(); exp.Next() )
1353   {
1354     if ( _MeshOfSolid* pm =
1355          _ViscousListener::GetSolidMesh( &theMesh, exp.Current(), /*toCreate=*/false))
1356     {
1357       if ( toMakeN2NMap && !pm->_n2nMapComputed )
1358         if ( !builder.MakeN2NMap( pm ))
1359           return SMESH_ProxyMesh::Ptr();
1360       components.push_back( SMESH_ProxyMesh::Ptr( pm ));
1361       pm->myIsDeletable = false; // it will de deleted by boost::shared_ptr
1362
1363       if ( pm->_warning && !pm->_warning->IsOK() )
1364       {
1365         SMESH_subMesh* sm = theMesh.GetSubMesh( exp.Current() );
1366         SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1367         if ( !smError || smError->IsOK() )
1368           smError = pm->_warning;
1369       }
1370     }
1371     _ViscousListener::RemoveSolidMesh ( &theMesh, exp.Current() );
1372   }
1373   switch ( components.size() )
1374   {
1375   case 0: break;
1376
1377   case 1: return components[0];
1378
1379   default: return SMESH_ProxyMesh::Ptr( new SMESH_ProxyMesh( components ));
1380   }
1381   return SMESH_ProxyMesh::Ptr();
1382 } // --------------------------------------------------------------------------------
1383 std::ostream & StdMeshers_ViscousLayers::SaveTo(std::ostream & save)
1384 {
1385   save << " " << _nbLayers
1386        << " " << _thickness
1387        << " " << _stretchFactor
1388        << " " << _shapeIds.size();
1389   for ( size_t i = 0; i < _shapeIds.size(); ++i )
1390     save << " " << _shapeIds[i];
1391   save << " " << !_isToIgnoreShapes; // negate to keep the behavior in old studies.
1392   save << " " << _method;
1393   save << " " << _groupName.size();
1394   if ( !_groupName.empty() )
1395     save << " " << _groupName;
1396   return save;
1397 } // --------------------------------------------------------------------------------
1398 std::istream & StdMeshers_ViscousLayers::LoadFrom(std::istream & load)
1399 {
1400   int nbFaces, faceID, shapeToTreat, method;
1401   load >> _nbLayers >> _thickness >> _stretchFactor >> nbFaces;
1402   while ( (int) _shapeIds.size() < nbFaces && load >> faceID )
1403     _shapeIds.push_back( faceID );
1404   if ( load >> shapeToTreat ) {
1405     _isToIgnoreShapes = !shapeToTreat;
1406     if ( load >> method )
1407       _method = (ExtrusionMethod) method;
1408     int nameSize = 0;
1409     if ( load >> nameSize && nameSize > 0 )
1410     {
1411       _groupName.resize( nameSize );
1412       load.get( _groupName[0] ); // remove a white-space
1413       load.getline( &_groupName[0], nameSize + 1 );
1414     }
1415   }
1416   else {
1417     _isToIgnoreShapes = true; // old behavior
1418   }
1419   return load;
1420 } // --------------------------------------------------------------------------------
1421 bool StdMeshers_ViscousLayers::SetParametersByMesh(const SMESH_Mesh*   /*theMesh*/,
1422                                                    const TopoDS_Shape& /*theShape*/)
1423 {
1424   // TODO
1425   return false;
1426 } // --------------------------------------------------------------------------------
1427 SMESH_ComputeErrorPtr
1428 StdMeshers_ViscousLayers::CheckHypothesis(SMESH_Mesh&                          theMesh,
1429                                           const TopoDS_Shape&                  theShape,
1430                                           SMESH_Hypothesis::Hypothesis_Status& theStatus)
1431 {
1432   VISCOUS_3D::_ViscousBuilder builder;
1433   SMESH_ComputeErrorPtr err = builder.CheckHypotheses( theMesh, theShape );
1434   if ( err && !err->IsOK() )
1435     theStatus = SMESH_Hypothesis::HYP_INCOMPAT_HYPS;
1436   else
1437     theStatus = SMESH_Hypothesis::HYP_OK;
1438
1439   return err;
1440 }
1441 // --------------------------------------------------------------------------------
1442 bool StdMeshers_ViscousLayers::IsShapeWithLayers(int shapeIndex) const
1443 {
1444   bool isIn =
1445     ( std::find( _shapeIds.begin(), _shapeIds.end(), shapeIndex ) != _shapeIds.end() );
1446   return IsToIgnoreShapes() ? !isIn : isIn;
1447 }
1448
1449 // --------------------------------------------------------------------------------
1450 SMDS_MeshGroup* StdMeshers_ViscousLayers::CreateGroup( const std::string&  theName,
1451                                                        SMESH_Mesh&         theMesh,
1452                                                        SMDSAbs_ElementType theType)
1453 {
1454   SMESH_Group*      group = 0;
1455   SMDS_MeshGroup* groupDS = 0;
1456
1457   if ( theName.empty() )
1458     return groupDS;
1459        
1460   if ( SMESH_Mesh::GroupIteratorPtr grIt = theMesh.GetGroups() )
1461     while( grIt->more() && !group )
1462     {
1463       group = grIt->next();
1464       if ( !group ||
1465            group->GetGroupDS()->GetType() != theType ||
1466            group->GetName()               != theName ||
1467            !dynamic_cast< SMESHDS_Group* >( group->GetGroupDS() ))
1468         group = 0;
1469     }
1470   if ( !group )
1471     group = theMesh.AddGroup( theType, theName.c_str() );
1472
1473   groupDS = & dynamic_cast< SMESHDS_Group* >( group->GetGroupDS() )->SMDSGroup();
1474
1475   return groupDS;
1476 }
1477
1478 // END StdMeshers_ViscousLayers hypothesis
1479 //================================================================================
1480
1481 namespace VISCOUS_3D
1482 {
1483   gp_XYZ getEdgeDir( const TopoDS_Edge& E, const TopoDS_Vertex& fromV,
1484                      const double h0, bool* isRegularEdge = nullptr )
1485   {
1486     gp_Vec dir;
1487     double f,l;
1488     Handle(Geom_Curve) c = BRep_Tool::Curve( E, f, l );
1489     if ( c.IsNull() ) return gp_XYZ( Precision::Infinite(), 1e100, 1e100 );
1490     gp_Pnt  p = BRep_Tool::Pnt( fromV );
1491     gp_Pnt pf = c->Value( f ), pl = c->Value( l );
1492     double distF = p.SquareDistance( pf );
1493     double distL = p.SquareDistance( pl );
1494     c->D1(( distF < distL ? f : l), p, dir );
1495     if ( distL < distF ) dir.Reverse();
1496     bool isDifficult = false;
1497     if ( dir.SquareMagnitude() < h0 * h0 ) // check dir orientation
1498     {
1499       gp_Pnt& pClose = distF < distL ? pf : pl;
1500       gp_Pnt&   pFar = distF < distL ? pl : pf;
1501       gp_Pnt    pMid = 0.9 * pClose.XYZ() + 0.1 * pFar.XYZ();
1502       gp_Vec vMid( p, pMid );
1503       double     dot = vMid * dir;
1504       double    cos2 = dot * dot / dir.SquareMagnitude() / vMid.SquareMagnitude();
1505       if ( cos2 < 0.7 * 0.7 || dot < 0 ) // large angle between dir and vMid
1506       {
1507         double uClose = distF < distL ? f : l;
1508         double   uFar = distF < distL ? l : f;
1509         double      r = h0 / SMESH_Algo::EdgeLength( E );
1510         double   uMid = ( 1 - r ) * uClose + r * uFar;
1511         pMid = c->Value( uMid );
1512         dir = gp_Vec( p, pMid );
1513         isDifficult = true;
1514       }
1515     }
1516     if ( isRegularEdge )
1517       *isRegularEdge = !isDifficult;
1518
1519     return dir.XYZ();
1520   }
1521   //--------------------------------------------------------------------------------
1522   gp_XYZ getEdgeDir( const TopoDS_Edge& E, const SMDS_MeshNode* atNode,
1523                      SMESH_MesherHelper& helper)
1524   {
1525     gp_Vec dir;
1526     double f,l; gp_Pnt p;
1527     Handle(Geom_Curve) c = BRep_Tool::Curve( E, f, l );
1528     if ( c.IsNull() ) return gp_XYZ( Precision::Infinite(), 1e100, 1e100 );
1529     double u = helper.GetNodeU( E, atNode );
1530     c->D1( u, p, dir );
1531     return dir.XYZ();
1532   }
1533   //--------------------------------------------------------------------------------
1534   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Vertex& fromV,
1535                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper, bool& ok/*,
1536                      double* cosin=0*/);
1537   //--------------------------------------------------------------------------------
1538   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Edge& fromE,
1539                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper, bool& ok)
1540   {
1541     double f,l;
1542     Handle(Geom_Curve) c = BRep_Tool::Curve( fromE, f, l );
1543     if ( c.IsNull() )
1544     {
1545       TopoDS_Vertex v = helper.IthVertex( 0, fromE );
1546       return getFaceDir( F, v, node, helper, ok );
1547     }
1548     gp_XY uv = helper.GetNodeUV( F, node, 0, &ok );
1549     Handle(Geom_Surface) surface = BRep_Tool::Surface( F );
1550     gp_Pnt p; gp_Vec du, dv, norm;
1551     surface->D1( uv.X(),uv.Y(), p, du,dv );
1552     norm = du ^ dv;
1553
1554     double u = helper.GetNodeU( fromE, node, 0, &ok );
1555     c->D1( u, p, du );
1556     TopAbs_Orientation o = helper.GetSubShapeOri( F.Oriented(TopAbs_FORWARD), fromE);
1557     if ( o == TopAbs_REVERSED )
1558       du.Reverse();
1559
1560     gp_Vec dir = norm ^ du;
1561
1562     if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX &&
1563          helper.IsClosedEdge( fromE ))
1564     {
1565       if ( fabs(u-f) < fabs(u-l)) c->D1( l, p, dv );
1566       else                        c->D1( f, p, dv );
1567       if ( o == TopAbs_REVERSED )
1568         dv.Reverse();
1569       gp_Vec dir2 = norm ^ dv;
1570       dir = dir.Normalized() + dir2.Normalized();
1571     }
1572     return dir.XYZ();
1573   }
1574   //--------------------------------------------------------------------------------
1575   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Vertex& fromV,
1576                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper,
1577                      bool& ok/*, double* cosin*/)
1578   {
1579     TopoDS_Face faceFrw = F;
1580     faceFrw.Orientation( TopAbs_FORWARD );
1581     //double f,l; TopLoc_Location loc;
1582     TopoDS_Edge edges[2]; // sharing a vertex
1583     size_t nbEdges = 0;
1584     {
1585       TopoDS_Vertex VV[2];
1586       TopExp_Explorer exp( faceFrw, TopAbs_EDGE );
1587       for ( ; exp.More() && nbEdges < 2; exp.Next() )
1588       {
1589         const TopoDS_Edge& e = TopoDS::Edge( exp.Current() );
1590         if ( SMESH_Algo::isDegenerated( e )) continue;
1591         TopExp::Vertices( e, VV[0], VV[1], /*CumOri=*/true );
1592         if ( VV[1].IsSame( fromV )) {
1593           nbEdges += edges[ 0 ].IsNull();
1594           edges[ 0 ] = e;
1595         }
1596         else if ( VV[0].IsSame( fromV )) {
1597           nbEdges += edges[ 1 ].IsNull();
1598           edges[ 1 ] = e;
1599         }
1600       }
1601     }
1602     gp_XYZ dir(0,0,0), edgeDir[2];
1603     if ( nbEdges == 2 )
1604     {
1605       // get dirs of edges going fromV
1606       ok = true;
1607       for ( size_t i = 0; i < nbEdges && ok; ++i )
1608       {
1609         edgeDir[i] = getEdgeDir( edges[i], fromV, 0.1 * SMESH_Algo::EdgeLength( edges[i] ));
1610         double size2 = edgeDir[i].SquareModulus();
1611         if (( ok = size2 > numeric_limits<double>::min() ))
1612           edgeDir[i] /= sqrt( size2 );
1613       }
1614       if ( !ok ) return dir;
1615
1616       // get angle between the 2 edges
1617       gp_Vec faceNormal;
1618       double angle = helper.GetAngle( edges[0], edges[1], faceFrw, fromV, &faceNormal );
1619       if ( Abs( angle ) < 5 * M_PI/180 )
1620       {
1621         dir = ( faceNormal.XYZ() ^ edgeDir[0].Reversed()) + ( faceNormal.XYZ() ^ edgeDir[1] );
1622       }
1623       else
1624       {
1625         dir = edgeDir[0] + edgeDir[1];
1626         if ( angle < 0 )
1627           dir.Reverse();
1628       }
1629       // if ( cosin ) {
1630       //   double angle = faceNormal.Angle( dir );
1631       //   *cosin = Cos( angle );
1632       // }
1633     }
1634     else if ( nbEdges == 1 )
1635     {
1636       dir = getFaceDir( faceFrw, edges[ edges[0].IsNull() ], node, helper, ok );
1637       //if ( cosin ) *cosin = 1.;
1638     }
1639     else
1640     {
1641       ok = false;
1642     }
1643
1644     return dir;
1645   }
1646
1647   //================================================================================
1648   /*!
1649    * \brief Finds concave VERTEXes of a FACE
1650    */
1651   //================================================================================
1652
1653   bool getConcaveVertices( const TopoDS_Face&  F,
1654                            SMESH_MesherHelper& helper,
1655                            set< TGeomID >*     vertices = 0)
1656   {
1657     // check angles at VERTEXes
1658     TError error;
1659     TSideVector wires = StdMeshers_FaceSide::GetFaceWires( F, *helper.GetMesh(), 0, error );
1660     for ( size_t iW = 0; iW < wires.size(); ++iW )
1661     {
1662       const int nbEdges = wires[iW]->NbEdges();
1663       if ( nbEdges < 2 && SMESH_Algo::isDegenerated( wires[iW]->Edge(0)))
1664         continue;
1665       for ( int iE1 = 0; iE1 < nbEdges; ++iE1 )
1666       {
1667         if ( SMESH_Algo::isDegenerated( wires[iW]->Edge( iE1 ))) continue;
1668         int iE2 = ( iE1 + 1 ) % nbEdges;
1669         while ( SMESH_Algo::isDegenerated( wires[iW]->Edge( iE2 )))
1670           iE2 = ( iE2 + 1 ) % nbEdges;
1671         TopoDS_Vertex V = wires[iW]->FirstVertex( iE2 );
1672         double angle = helper.GetAngle( wires[iW]->Edge( iE1 ),
1673                                         wires[iW]->Edge( iE2 ), F, V );
1674         if ( angle < -5. * M_PI / 180. )
1675         {
1676           if ( !vertices )
1677             return true;
1678           vertices->insert( helper.GetMeshDS()->ShapeToIndex( V ));
1679         }
1680       }
1681     }
1682     return vertices ? !vertices->empty() : false;
1683   }
1684
1685   //================================================================================
1686   /*!
1687    * \brief Returns true if a FACE is bound by a concave EDGE
1688    */
1689   //================================================================================
1690
1691   bool isConcave( const TopoDS_Face&  F,
1692                   SMESH_MesherHelper& helper,
1693                   set< TGeomID >*     vertices = 0 )
1694   {
1695     bool isConcv = false;
1696     // if ( helper.Count( F, TopAbs_WIRE, /*useMap=*/false) > 1 )
1697     //   return true;
1698     gp_Vec2d drv1, drv2;
1699     gp_Pnt2d p;
1700     TopExp_Explorer eExp( F.Oriented( TopAbs_FORWARD ), TopAbs_EDGE );
1701     for ( ; eExp.More(); eExp.Next() )
1702     {
1703       const TopoDS_Edge& E = TopoDS::Edge( eExp.Current() );
1704       if ( SMESH_Algo::isDegenerated( E )) continue;
1705       // check if 2D curve is concave
1706       BRepAdaptor_Curve2d curve( E, F );
1707       const int nbIntervals = curve.NbIntervals( GeomAbs_C2 );
1708       TColStd_Array1OfReal intervals(1, nbIntervals + 1 );
1709       curve.Intervals( intervals, GeomAbs_C2 );
1710       bool isConvex = true;
1711       for ( int i = 1; i <= nbIntervals && isConvex; ++i )
1712       {
1713         double u1 = intervals( i );
1714         double u2 = intervals( i+1 );
1715         curve.D2( 0.5*( u1+u2 ), p, drv1, drv2 );
1716         double cross = drv1 ^ drv2;
1717         if ( E.Orientation() == TopAbs_REVERSED )
1718           cross = -cross;
1719         isConvex = ( cross > -1e-9 ); // 0.1 );
1720       }
1721       if ( !isConvex )
1722       {
1723         //cout << "Concave FACE " << helper.GetMeshDS()->ShapeToIndex( F ) << endl;
1724         isConcv = true;
1725         if ( vertices )
1726           break;
1727         else
1728           return true;
1729       }
1730     }
1731
1732     // check angles at VERTEXes
1733     if ( getConcaveVertices( F, helper, vertices ))
1734       isConcv = true;
1735
1736     return isConcv;
1737   }
1738
1739   //================================================================================
1740   /*!
1741    * \brief Computes minimal distance of face in-FACE nodes from an EDGE
1742    *  \param [in] face - the mesh face to treat
1743    *  \param [in] nodeOnEdge - a node on the EDGE
1744    *  \param [out] faceSize - the computed distance
1745    *  \return bool - true if faceSize computed
1746    */
1747   //================================================================================
1748
1749   bool getDistFromEdge( const SMDS_MeshElement* face,
1750                         const SMDS_MeshNode*    nodeOnEdge,
1751                         double &                faceSize )
1752   {
1753     faceSize = Precision::Infinite();
1754     bool done = false;
1755
1756     int nbN  = face->NbCornerNodes();
1757     int iOnE = face->GetNodeIndex( nodeOnEdge );
1758     int iNext[2] = { SMESH_MesherHelper::WrapIndex( iOnE+1, nbN ),
1759                      SMESH_MesherHelper::WrapIndex( iOnE-1, nbN ) };
1760     const SMDS_MeshNode* nNext[2] = { face->GetNode( iNext[0] ),
1761                                       face->GetNode( iNext[1] ) };
1762     gp_XYZ segVec, segEnd = SMESH_TNodeXYZ( nodeOnEdge ); // segment on EDGE
1763     double segLen = -1.;
1764     // look for two neighbor not in-FACE nodes of face
1765     for ( int i = 0; i < 2; ++i )
1766     {
1767       if (( nNext[i]->GetPosition()->GetDim() != 2 ) &&
1768           ( nodeOnEdge->GetPosition()->GetDim() == 0 || nNext[i]->GetID() < nodeOnEdge->GetID() ))
1769       {
1770         // look for an in-FACE node
1771         for ( int iN = 0; iN < nbN; ++iN )
1772         {
1773           if ( iN == iOnE || iN == iNext[i] )
1774             continue;
1775           SMESH_TNodeXYZ pInFace = face->GetNode( iN );
1776           gp_XYZ v = pInFace - segEnd;
1777           if ( segLen < 0 )
1778           {
1779             segVec = SMESH_TNodeXYZ( nNext[i] ) - segEnd;
1780             segLen = segVec.Modulus();
1781           }
1782           double distToSeg = v.Crossed( segVec ).Modulus() / segLen;
1783           faceSize = Min( faceSize, distToSeg );
1784           done = true;
1785         }
1786         segLen = -1;
1787       }
1788     }
1789     return done;
1790   }
1791   //================================================================================
1792   /*!
1793    * \brief Return direction of axis or revolution of a surface
1794    */
1795   //================================================================================
1796
1797   bool getRovolutionAxis( const Adaptor3d_Surface& surface,
1798                           gp_Dir &                 axis )
1799   {
1800     switch ( surface.GetType() ) {
1801     case GeomAbs_Cone:
1802     {
1803       gp_Cone cone = surface.Cone();
1804       axis = cone.Axis().Direction();
1805       break;
1806     }
1807     case GeomAbs_Sphere:
1808     {
1809       gp_Sphere sphere = surface.Sphere();
1810       axis = sphere.Position().Direction();
1811       break;
1812     }
1813     case GeomAbs_SurfaceOfRevolution:
1814     {
1815       axis = surface.AxeOfRevolution().Direction();
1816       break;
1817     }
1818     //case GeomAbs_SurfaceOfExtrusion:
1819     case GeomAbs_OffsetSurface:
1820     {
1821       Handle(Adaptor3d_HSurface) base = surface.BasisSurface();
1822       return getRovolutionAxis( base->Surface(), axis );
1823     }
1824     default: return false;
1825     }
1826     return true;
1827   }
1828
1829   //--------------------------------------------------------------------------------
1830   // DEBUG. Dump intermediate node positions into a python script
1831   // HOWTO use: run python commands written in a console and defined in /tmp/viscous.py
1832   // to see construction steps of viscous layers
1833 #ifdef __myDEBUG
1834   ostream* py;
1835   int      theNbPyFunc;
1836   struct PyDump
1837   {
1838     PyDump(SMESH_Mesh& m) {
1839       int tag = 3 + m.GetId();
1840       const char* fname = "/tmp/viscous.py";
1841       cout << "exec(open('"<<fname<<"','rb').read() )"<<endl;
1842       py = _pyStream = new ofstream(fname);
1843       *py << "import SMESH" << endl
1844           << "from salome.smesh import smeshBuilder" << endl
1845           << "smesh  = smeshBuilder.New()" << endl
1846           << "meshSO = salome.myStudy.FindObjectID('0:1:2:" << tag <<"')" << endl
1847           << "mesh   = smesh.Mesh( meshSO.GetObject() )"<<endl;
1848       theNbPyFunc = 0;
1849     }
1850     void Finish() {
1851       if (py) {
1852         *py << "mesh.GroupOnFilter(SMESH.VOLUME,'Viscous Prisms',"
1853           "smesh.GetFilter(SMESH.VOLUME,SMESH.FT_ElemGeomType,'=',SMESH.Geom_PENTA))"<<endl;
1854         *py << "mesh.GroupOnFilter(SMESH.VOLUME,'Neg Volumes',"
1855           "smesh.GetFilter(SMESH.VOLUME,SMESH.FT_Volume3D,'<',0))"<<endl;
1856       }
1857       delete py; py=0;
1858     }
1859     ~PyDump() { Finish(); cout << "NB FUNCTIONS: " << theNbPyFunc << endl; }
1860     struct MyStream : public ostream
1861     {
1862       template <class T> ostream & operator<<( const T &anything ) { return *this ; }
1863     };
1864     void Pause() { py = &_mystream; }
1865     void Resume() { py = _pyStream; }
1866     MyStream _mystream;
1867     ostream* _pyStream;
1868   };
1869 #define dumpFunction(f) { _dumpFunction(f, __LINE__);}
1870 #define dumpMove(n)     { _dumpMove(n, __LINE__);}
1871 #define dumpMoveComm(n,txt) { _dumpMove(n, __LINE__, txt);}
1872 #define dumpCmd(txt)    { _dumpCmd(txt, __LINE__);}
1873   void _dumpFunction(const string& fun, int ln)
1874   { if (py) *py<< "def "<<fun<<"(): # "<< ln <<endl; cout<<fun<<"()"<<endl; ++theNbPyFunc; }
1875   void _dumpMove(const SMDS_MeshNode* n, int ln, const char* txt="")
1876   { if (py) *py<< "  mesh.MoveNode( "<<n->GetID()<< ", "<< n->X()
1877                << ", "<<n->Y()<<", "<< n->Z()<< ")\t\t # "<< ln <<" "<< txt << endl; }
1878   void _dumpCmd(const string& txt, int ln)
1879   { if (py) *py<< "  "<<txt<<" # "<< ln <<endl; }
1880   void dumpFunctionEnd()
1881   { if (py) *py<< "  return"<< endl; }
1882   void dumpChangeNodes( const SMDS_MeshElement* f )
1883   { if (py) { *py<< "  mesh.ChangeElemNodes( " << f->GetID()<<", [";
1884       for ( int i=1; i < f->NbNodes(); ++i ) *py << f->GetNode(i-1)->GetID()<<", ";
1885       *py << f->GetNode( f->NbNodes()-1 )->GetID() << " ])"<< endl; }}
1886 #define debugMsg( txt ) { cout << "# "<< txt << " (line: " << __LINE__ << ")" << endl; }
1887
1888 #else
1889
1890   struct PyDump { PyDump(SMESH_Mesh&) {} void Finish() {} void Pause() {} void Resume() {} };
1891 #define dumpFunction(f) f
1892 #define dumpMove(n)
1893 #define dumpMoveComm(n,txt)
1894 #define dumpCmd(txt)
1895 #define dumpFunctionEnd()
1896 #define dumpChangeNodes(f) { if(f) {} } // prevent "unused variable 'f'" warning
1897 #define debugMsg( txt ) {}
1898
1899 #endif
1900 }
1901
1902 using namespace VISCOUS_3D;
1903
1904 //================================================================================
1905 /*!
1906  * \brief Constructor of _ViscousBuilder
1907  */
1908 //================================================================================
1909
1910 _ViscousBuilder::_ViscousBuilder()
1911 {
1912   _error = SMESH_ComputeError::New(COMPERR_OK);
1913   _tmpFaceID = 0;
1914 }
1915
1916 //================================================================================
1917 /*!
1918  * \brief Stores error description and returns false
1919  */
1920 //================================================================================
1921
1922 bool _ViscousBuilder::error(const string& text, int solidId )
1923 {
1924   const string prefix = string("Viscous layers builder: ");
1925   _error->myName    = COMPERR_ALGO_FAILED;
1926   _error->myComment = prefix + text;
1927   if ( _mesh )
1928   {
1929     SMESH_subMesh* sm = _mesh->GetSubMeshContaining( solidId );
1930     if ( !sm && !_sdVec.empty() )
1931       sm = _mesh->GetSubMeshContaining( solidId = _sdVec[0]._index );
1932     if ( sm && sm->GetSubShape().ShapeType() == TopAbs_SOLID )
1933     {
1934       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1935       if ( smError && smError->myAlgo )
1936         _error->myAlgo = smError->myAlgo;
1937       smError = _error;
1938       sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
1939     }
1940     // set KO to all solids
1941     for ( size_t i = 0; i < _sdVec.size(); ++i )
1942     {
1943       if ( _sdVec[i]._index == solidId )
1944         continue;
1945       sm = _mesh->GetSubMesh( _sdVec[i]._solid );
1946       if ( !sm->IsEmpty() )
1947         continue;
1948       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1949       if ( !smError || smError->IsOK() )
1950       {
1951         smError = SMESH_ComputeError::New( COMPERR_ALGO_FAILED, prefix + "failed");
1952         sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
1953       }
1954     }
1955   }
1956   makeGroupOfLE(); // debug
1957
1958   return false;
1959 }
1960
1961 //================================================================================
1962 /*!
1963  * \brief At study restoration, restore event listeners used to clear an inferior
1964  *  dim sub-mesh modified by viscous layers
1965  */
1966 //================================================================================
1967
1968 void _ViscousBuilder::RestoreListeners()
1969 {
1970   // TODO
1971 }
1972
1973 //================================================================================
1974 /*!
1975  * \brief computes SMESH_ProxyMesh::SubMesh::_n2n
1976  */
1977 //================================================================================
1978
1979 bool _ViscousBuilder::MakeN2NMap( _MeshOfSolid* pm )
1980 {
1981   SMESH_subMesh* solidSM = pm->mySubMeshes.front();
1982   TopExp_Explorer fExp( solidSM->GetSubShape(), TopAbs_FACE );
1983   for ( ; fExp.More(); fExp.Next() )
1984   {
1985     SMESHDS_SubMesh* srcSmDS = pm->GetMeshDS()->MeshElements( fExp.Current() );
1986     const SMESH_ProxyMesh::SubMesh* prxSmDS = pm->GetProxySubMesh( fExp.Current() );
1987
1988     if ( !srcSmDS || !prxSmDS || !srcSmDS->NbElements() || !prxSmDS->NbElements() )
1989       continue;
1990     if ( srcSmDS->GetElements()->next() == prxSmDS->GetElements()->next())
1991       continue;
1992
1993     if ( srcSmDS->NbElements() != prxSmDS->NbElements() )
1994       return error( "Different nb elements in a source and a proxy sub-mesh", solidSM->GetId());
1995
1996     SMDS_ElemIteratorPtr srcIt = srcSmDS->GetElements();
1997     SMDS_ElemIteratorPtr prxIt = prxSmDS->GetElements();
1998     while( prxIt->more() )
1999     {
2000       const SMDS_MeshElement* fSrc = srcIt->next();
2001       const SMDS_MeshElement* fPrx = prxIt->next();
2002       if ( fSrc->NbNodes() != fPrx->NbNodes())
2003         return error( "Different elements in a source and a proxy sub-mesh", solidSM->GetId());
2004       for ( int i = 0 ; i < fPrx->NbNodes(); ++i )
2005         pm->setNode2Node( fSrc->GetNode(i), fPrx->GetNode(i), prxSmDS );
2006     }
2007   }
2008   pm->_n2nMapComputed = true;
2009   return true;
2010 }
2011
2012 //================================================================================
2013 /*!
2014  * \brief Does its job
2015  */
2016 //================================================================================
2017
2018 SMESH_ComputeErrorPtr _ViscousBuilder::Compute(SMESH_Mesh&         theMesh,
2019                                                const TopoDS_Shape& theShape)
2020 {
2021   _mesh = & theMesh;
2022
2023   _Factory factory;
2024
2025   // check if proxy mesh already computed
2026   TopExp_Explorer exp( theShape, TopAbs_SOLID );
2027   if ( !exp.More() )
2028     return error("No SOLID's in theShape"), _error;
2029
2030   if ( _ViscousListener::GetSolidMesh( _mesh, exp.Current(), /*toCreate=*/false))
2031     return SMESH_ComputeErrorPtr(); // everything already computed
2032
2033   // TODO: ignore already computed SOLIDs
2034   if ( !findSolidsWithLayers())
2035     return _error;
2036
2037   if ( !findFacesWithLayers() )
2038     return _error;
2039
2040   if ( !makeEdgesOnShape() )
2041     return _error;
2042
2043   findPeriodicFaces();
2044
2045   PyDump debugDump( theMesh );
2046   _pyDump = &debugDump;
2047
2048
2049   for ( size_t i = 0; i < _sdVec.size(); ++i )
2050   {
2051     size_t iSD = 0;
2052     for ( iSD = 0; iSD < _sdVec.size(); ++iSD ) // find next SOLID to compute
2053       if ( _sdVec[iSD]._before.IsEmpty() &&
2054            !_sdVec[iSD]._solid.IsNull() &&
2055            !_sdVec[iSD]._done )
2056         break;
2057     if ( iSD == _sdVec.size() )
2058       break; // all done
2059
2060     if ( ! makeLayer(_sdVec[iSD]) )   // create _LayerEdge's
2061       return _error;
2062
2063     if ( _sdVec[iSD]._n2eMap.size() == 0 ) // no layers in a SOLID
2064     {
2065       _sdVec[iSD]._solid.Nullify();
2066       continue;
2067     }
2068
2069     if ( ! inflate(_sdVec[iSD]) )     // increase length of _LayerEdge's
2070       return _error;
2071
2072     if ( ! refine(_sdVec[iSD]) )      // create nodes and prisms
2073       return _error;
2074
2075     if ( ! shrink(_sdVec[iSD]) )      // shrink 2D mesh on FACEs w/o layer
2076       return _error;
2077
2078     addBoundaryElements(_sdVec[iSD]); // create quadrangles on prism bare sides
2079
2080     _sdVec[iSD]._done = true;
2081
2082     const TopoDS_Shape& solid = _sdVec[iSD]._solid;
2083     for ( iSD = 0; iSD < _sdVec.size(); ++iSD )
2084       _sdVec[iSD]._before.Remove( solid );
2085   }
2086
2087   makeGroupOfLE(); // debug
2088   debugDump.Finish();
2089
2090   return _error;
2091 }
2092
2093 //================================================================================
2094 /*!
2095  * \brief Check validity of hypotheses
2096  */
2097 //================================================================================
2098
2099 SMESH_ComputeErrorPtr _ViscousBuilder::CheckHypotheses( SMESH_Mesh&         mesh,
2100                                                         const TopoDS_Shape& shape )
2101 {
2102   _mesh = & mesh;
2103
2104   if ( _ViscousListener::GetSolidMesh( _mesh, shape, /*toCreate=*/false))
2105     return SMESH_ComputeErrorPtr(); // everything already computed
2106
2107
2108   findSolidsWithLayers( /*checkFaceMesh=*/false );
2109   bool ok = findFacesWithLayers( true );
2110
2111   // remove _MeshOfSolid's of _SolidData's
2112   for ( size_t i = 0; i < _sdVec.size(); ++i )
2113     _ViscousListener::RemoveSolidMesh( _mesh, _sdVec[i]._solid );
2114
2115   if ( !ok )
2116     return _error;
2117
2118   return SMESH_ComputeErrorPtr();
2119 }
2120
2121 //================================================================================
2122 /*!
2123  * \brief Finds SOLIDs to compute using viscous layers. Fills _sdVec
2124  */
2125 //================================================================================
2126
2127 bool _ViscousBuilder::findSolidsWithLayers(const bool checkFaceMesh)
2128 {
2129   // get all solids
2130   TopTools_IndexedMapOfShape allSolids;
2131   TopExp::MapShapes( _mesh->GetShapeToMesh(), TopAbs_SOLID, allSolids );
2132   _sdVec.reserve( allSolids.Extent());
2133
2134   SMESH_HypoFilter filter;
2135   for ( int i = 1; i <= allSolids.Extent(); ++i )
2136   {
2137     SMESH_subMesh* sm = _mesh->GetSubMesh( allSolids(i) );
2138     if ( sm->GetSubMeshDS() && sm->GetSubMeshDS()->NbElements() > 0 )
2139       continue; // solid is already meshed
2140     // TODO: check if algo is hidden
2141     SMESH_Algo* algo = sm->GetAlgo();
2142     if ( !algo ) continue;
2143     // check if all FACEs are meshed, which can be false if Compute() a sub-shape
2144     if ( checkFaceMesh )
2145     {
2146       bool facesMeshed = true;
2147       SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(false,true);
2148       while ( smIt->more() && facesMeshed )
2149       {
2150         SMESH_subMesh * faceSM = smIt->next();
2151         if ( faceSM->GetSubShape().ShapeType() != TopAbs_FACE )
2152           break;
2153         facesMeshed = faceSM->IsMeshComputed();
2154       }
2155       if ( !facesMeshed )
2156         continue;
2157     }
2158     // find StdMeshers_ViscousLayers hyp assigned to the i-th solid
2159     const list <const SMESHDS_Hypothesis *> & allHyps =
2160       algo->GetUsedHypothesis(*_mesh, allSolids(i), /*ignoreAuxiliary=*/false);
2161     _SolidData* soData = 0;
2162     list< const SMESHDS_Hypothesis *>::const_iterator hyp = allHyps.begin();
2163     const StdMeshers_ViscousLayers* viscHyp = 0;
2164     for ( ; hyp != allHyps.end(); ++hyp )
2165       if (( viscHyp = dynamic_cast<const StdMeshers_ViscousLayers*>( *hyp )))
2166       {
2167         TopoDS_Shape hypShape;
2168         filter.Init( filter.Is( viscHyp ));
2169         _mesh->GetHypothesis( allSolids(i), filter, true, &hypShape );
2170
2171         if ( !soData )
2172         {
2173           _MeshOfSolid* proxyMesh = _ViscousListener::GetSolidMesh( _mesh,
2174                                                                     allSolids(i),
2175                                                                     /*toCreate=*/true);
2176           _sdVec.push_back( _SolidData( allSolids(i), proxyMesh ));
2177           soData = & _sdVec.back();
2178           soData->_index = getMeshDS()->ShapeToIndex( allSolids(i));
2179           soData->_helper = new SMESH_MesherHelper( *_mesh );
2180           soData->_helper->SetSubShape( allSolids(i) );
2181           _solids.Add( allSolids(i) );
2182         }
2183         soData->_hyps.push_back( viscHyp );
2184         soData->_hypShapes.push_back( hypShape );
2185       }
2186   }
2187   if ( _sdVec.empty() )
2188     return error
2189       ( SMESH_Comment(StdMeshers_ViscousLayers::GetHypType()) << " hypothesis not found",0);
2190
2191   return true;
2192 }
2193
2194 //================================================================================
2195 /*!
2196  * \brief Set a _SolidData to be computed before another
2197  */
2198 //================================================================================
2199
2200 bool _ViscousBuilder::setBefore( _SolidData& solidBefore, _SolidData& solidAfter )
2201 {
2202   // check possibility to set this order; get all solids before solidBefore
2203   TopTools_IndexedMapOfShape allSolidsBefore;
2204   allSolidsBefore.Add( solidBefore._solid );
2205   for ( int i = 1; i <= allSolidsBefore.Extent(); ++i )
2206   {
2207     int iSD = _solids.FindIndex( allSolidsBefore(i) );
2208     if ( iSD )
2209     {
2210       TopTools_MapIteratorOfMapOfShape soIt( _sdVec[ iSD-1 ]._before );
2211       for ( ; soIt.More(); soIt.Next() )
2212         allSolidsBefore.Add( soIt.Value() );
2213     }
2214   }
2215   if ( allSolidsBefore.Contains( solidAfter._solid ))
2216     return false;
2217
2218   for ( int i = 1; i <= allSolidsBefore.Extent(); ++i )
2219     solidAfter._before.Add( allSolidsBefore(i) );
2220
2221   return true;
2222 }
2223
2224 //================================================================================
2225 /*!
2226  * \brief
2227  */
2228 //================================================================================
2229
2230 bool _ViscousBuilder::findFacesWithLayers(const bool onlyWith)
2231 {
2232   SMESH_MesherHelper helper( *_mesh );
2233   TopExp_Explorer exp;
2234
2235   // collect all faces-to-ignore defined by hyp
2236   for ( size_t i = 0; i < _sdVec.size(); ++i )
2237   {
2238     // get faces-to-ignore defined by each hyp
2239     typedef const StdMeshers_ViscousLayers* THyp;
2240     typedef std::pair< set<TGeomID>, THyp > TFacesOfHyp;
2241     list< TFacesOfHyp > ignoreFacesOfHyps;
2242     list< THyp >::iterator              hyp = _sdVec[i]._hyps.begin();
2243     list< TopoDS_Shape >::iterator hypShape = _sdVec[i]._hypShapes.begin();
2244     for ( ; hyp != _sdVec[i]._hyps.end(); ++hyp, ++hypShape )
2245     {
2246       ignoreFacesOfHyps.push_back( TFacesOfHyp( set<TGeomID>(), *hyp ));
2247       getIgnoreFaces( _sdVec[i]._solid, *hyp, *hypShape, ignoreFacesOfHyps.back().first );
2248     }
2249
2250     // fill _SolidData::_face2hyp and check compatibility of hypotheses
2251     const int nbHyps = _sdVec[i]._hyps.size();
2252     if ( nbHyps > 1 )
2253     {
2254       // check if two hypotheses define different parameters for the same FACE
2255       list< TFacesOfHyp >::iterator igFacesOfHyp;
2256       for ( exp.Init( _sdVec[i]._solid, TopAbs_FACE ); exp.More(); exp.Next() )
2257       {
2258         const TGeomID faceID = getMeshDS()->ShapeToIndex( exp.Current() );
2259         THyp hyp = 0;
2260         igFacesOfHyp = ignoreFacesOfHyps.begin();
2261         for ( ; igFacesOfHyp != ignoreFacesOfHyps.end(); ++igFacesOfHyp )
2262           if ( ! igFacesOfHyp->first.count( faceID ))
2263           {
2264             if ( hyp )
2265               return error(SMESH_Comment("Several hypotheses define "
2266                                          "Viscous Layers on the face #") << faceID );
2267             hyp = igFacesOfHyp->second;
2268           }
2269         if ( hyp )
2270           _sdVec[i]._face2hyp.insert( make_pair( faceID, hyp ));
2271         else
2272           _sdVec[i]._ignoreFaceIds.insert( faceID );
2273       }
2274
2275       // check if two hypotheses define different number of viscous layers for
2276       // adjacent faces of a solid
2277       set< int > nbLayersSet;
2278       igFacesOfHyp = ignoreFacesOfHyps.begin();
2279       for ( ; igFacesOfHyp != ignoreFacesOfHyps.end(); ++igFacesOfHyp )
2280       {
2281         nbLayersSet.insert( igFacesOfHyp->second->GetNumberLayers() );
2282       }
2283       if ( nbLayersSet.size() > 1 )
2284       {
2285         for ( exp.Init( _sdVec[i]._solid, TopAbs_EDGE ); exp.More(); exp.Next() )
2286         {
2287           PShapeIteratorPtr fIt = helper.GetAncestors( exp.Current(), *_mesh, TopAbs_FACE );
2288           THyp hyp1 = 0, hyp2 = 0;
2289           while( const TopoDS_Shape* face = fIt->next() )
2290           {
2291             const TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
2292             map< TGeomID, THyp >::iterator f2h = _sdVec[i]._face2hyp.find( faceID );
2293             if ( f2h != _sdVec[i]._face2hyp.end() )
2294             {
2295               ( hyp1 ? hyp2 : hyp1 ) = f2h->second;
2296             }
2297           }
2298           if ( hyp1 && hyp2 &&
2299                hyp1->GetNumberLayers() != hyp2->GetNumberLayers() )
2300           {
2301             return error("Two hypotheses define different number of "
2302                          "viscous layers on adjacent faces");
2303           }
2304         }
2305       }
2306     } // if ( nbHyps > 1 )
2307     else
2308     {
2309       _sdVec[i]._ignoreFaceIds.swap( ignoreFacesOfHyps.back().first );
2310     }
2311   } // loop on _sdVec
2312
2313   if ( onlyWith ) // is called to check hypotheses compatibility only
2314     return true;
2315
2316   // fill _SolidData::_reversedFaceIds
2317   for ( size_t i = 0; i < _sdVec.size(); ++i )
2318   {
2319     exp.Init( _sdVec[i]._solid.Oriented( TopAbs_FORWARD ), TopAbs_FACE );
2320     for ( ; exp.More(); exp.Next() )
2321     {
2322       const TopoDS_Face& face = TopoDS::Face( exp.Current() );
2323       const TGeomID    faceID = getMeshDS()->ShapeToIndex( face );
2324       if ( //!sdVec[i]._ignoreFaceIds.count( faceID ) &&
2325           helper.NbAncestors( face, *_mesh, TopAbs_SOLID ) > 1 &&
2326           helper.IsReversedSubMesh( face ))
2327       {
2328         _sdVec[i]._reversedFaceIds.insert( faceID );
2329       }
2330     }
2331   }
2332
2333   // Find FACEs to shrink mesh on (solution 2 in issue 0020832): fill in _shrinkShape2Shape
2334   TopTools_IndexedMapOfShape shapes;
2335   std::string structAlgoName = "Hexa_3D";
2336   for ( size_t i = 0; i < _sdVec.size(); ++i )
2337   {
2338     shapes.Clear();
2339     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_EDGE, shapes);
2340     for ( int iE = 1; iE <= shapes.Extent(); ++iE )
2341     {
2342       const TopoDS_Shape& edge = shapes(iE);
2343       // find 2 FACEs sharing an EDGE
2344       TopoDS_Shape FF[2];
2345       PShapeIteratorPtr fIt = helper.GetAncestors(edge, *_mesh, TopAbs_FACE, &_sdVec[i]._solid);
2346       while ( fIt->more())
2347       {
2348         const TopoDS_Shape* f = fIt->next();
2349         FF[ int( !FF[0].IsNull()) ] = *f;
2350       }
2351       if( FF[1].IsNull() ) continue; // seam edge can be shared by 1 FACE only
2352
2353       // check presence of layers on them
2354       int ignore[2];
2355       for ( int j = 0; j < 2; ++j )
2356         ignore[j] = _sdVec[i]._ignoreFaceIds.count( getMeshDS()->ShapeToIndex( FF[j] ));
2357       if ( ignore[0] == ignore[1] )
2358         continue; // nothing interesting
2359       TopoDS_Shape fWOL = FF[ ignore[0] ? 0 : 1 ]; // FACE w/o layers
2360
2361       // add EDGE to maps
2362       if ( !fWOL.IsNull())
2363       {
2364         TGeomID edgeInd = getMeshDS()->ShapeToIndex( edge );
2365         _sdVec[i]._shrinkShape2Shape.insert( make_pair( edgeInd, fWOL ));
2366       }
2367     }
2368   }
2369
2370   // Find the SHAPE along which to inflate _LayerEdge based on VERTEX
2371
2372   for ( size_t i = 0; i < _sdVec.size(); ++i )
2373   {
2374     shapes.Clear();
2375     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_VERTEX, shapes);
2376     for ( int iV = 1; iV <= shapes.Extent(); ++iV )
2377     {
2378       const TopoDS_Shape& vertex = shapes(iV);
2379       // find faces WOL sharing the vertex
2380       vector< TopoDS_Shape > facesWOL;
2381       size_t totalNbFaces = 0;
2382       PShapeIteratorPtr fIt = helper.GetAncestors(vertex, *_mesh, TopAbs_FACE, &_sdVec[i]._solid );
2383       while ( fIt->more())
2384       {
2385         const TopoDS_Shape* f = fIt->next();
2386         totalNbFaces++;
2387         const int fID = getMeshDS()->ShapeToIndex( *f );
2388         if ( _sdVec[i]._ignoreFaceIds.count ( fID ) /*&& !_sdVec[i]._noShrinkShapes.count( fID )*/)
2389           facesWOL.push_back( *f );
2390       }
2391       if ( facesWOL.size() == totalNbFaces || facesWOL.empty() )
2392         continue; // no layers at this vertex or no WOL
2393       TGeomID vInd = getMeshDS()->ShapeToIndex( vertex );
2394       switch ( facesWOL.size() )
2395       {
2396       case 1:
2397       {
2398         helper.SetSubShape( facesWOL[0] );
2399         if ( helper.IsRealSeam( vInd )) // inflate along a seam edge?
2400         {
2401           TopoDS_Shape seamEdge;
2402           PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE);
2403           while ( eIt->more() && seamEdge.IsNull() )
2404           {
2405             const TopoDS_Shape* e = eIt->next();
2406             if ( helper.IsRealSeam( *e ) )
2407               seamEdge = *e;
2408           }
2409           if ( !seamEdge.IsNull() )
2410           {
2411             _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, seamEdge ));
2412             break;
2413           }
2414         }
2415         _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, facesWOL[0] ));
2416         break;
2417       }
2418       case 2:
2419       {
2420         // find an edge shared by 2 faces
2421         PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE);
2422         while ( eIt->more())
2423         {
2424           const TopoDS_Shape* e = eIt->next();
2425           if ( helper.IsSubShape( *e, facesWOL[0]) &&
2426                helper.IsSubShape( *e, facesWOL[1]))
2427           {
2428             _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, *e )); break;
2429           }
2430         }
2431         break;
2432       }
2433       default:
2434         std::ostringstream msg;
2435         msg << "Not yet supported case: vertex bounded by ";
2436         msg << facesWOL.size();
2437         msg << " faces without layer at coordinates (";
2438         TopoDS_Vertex v = TopoDS::Vertex(vertex);
2439         gp_Pnt p = BRep_Tool::Pnt(v);
2440         msg << p.X() << ", " << p.Y() << ", " << p.Z() << ")";
2441         return error(msg.str().c_str(), _sdVec[i]._index);
2442       }
2443     }
2444   }
2445
2446   // Add to _noShrinkShapes sub-shapes of FACE's that can't be shrunk since
2447   // the algo of the SOLID sharing the FACE does not support it or for other reasons
2448   set< string > notSupportAlgos; notSupportAlgos.insert( structAlgoName );
2449   for ( size_t i = 0; i < _sdVec.size(); ++i )
2450   {
2451     map< TGeomID, TopoDS_Shape >::iterator e2f = _sdVec[i]._shrinkShape2Shape.begin();
2452     for ( ; e2f != _sdVec[i]._shrinkShape2Shape.end(); ++e2f )
2453     {
2454       const TopoDS_Shape& fWOL = e2f->second;
2455       const TGeomID     edgeID = e2f->first;
2456       TGeomID           faceID = getMeshDS()->ShapeToIndex( fWOL );
2457       TopoDS_Shape        edge = getMeshDS()->IndexToShape( edgeID );
2458       if ( edge.ShapeType() != TopAbs_EDGE )
2459         continue; // shrink shape is VERTEX
2460
2461       TopoDS_Shape solid;
2462       PShapeIteratorPtr soIt = helper.GetAncestors(fWOL, *_mesh, TopAbs_SOLID);
2463       while ( soIt->more() && solid.IsNull() )
2464       {
2465         const TopoDS_Shape* so = soIt->next();
2466         if ( !so->IsSame( _sdVec[i]._solid ))
2467           solid = *so;
2468       }
2469       if ( solid.IsNull() )
2470         continue;
2471
2472       bool noShrinkE = false;
2473       SMESH_Algo*  algo = _mesh->GetSubMesh( solid )->GetAlgo();
2474       bool isStructured = ( algo && algo->GetName() == structAlgoName );
2475       size_t     iSolid = _solids.FindIndex( solid ) - 1;
2476       if ( iSolid < _sdVec.size() && _sdVec[ iSolid ]._ignoreFaceIds.count( faceID ))
2477       {
2478         // the adjacent SOLID has NO layers on fWOL;
2479         // shrink allowed if
2480         // - there are layers on the EDGE in the adjacent SOLID
2481         // - there are NO layers in the adjacent SOLID && algo is unstructured and computed later
2482         bool hasWLAdj = (_sdVec[iSolid]._shrinkShape2Shape.count( edgeID ));
2483         bool shrinkAllowed = (( hasWLAdj ) ||
2484                               ( !isStructured && setBefore( _sdVec[ i ], _sdVec[ iSolid ] )));
2485         noShrinkE = !shrinkAllowed;
2486       }
2487       else if ( iSolid < _sdVec.size() )
2488       {
2489         // the adjacent SOLID has layers on fWOL;
2490         // check if SOLID's mesh is unstructured and then try to set it
2491         // to be computed after the i-th solid
2492         if ( isStructured || !setBefore( _sdVec[ i ], _sdVec[ iSolid ] ))
2493           noShrinkE = true; // don't shrink fWOL
2494       }
2495       else
2496       {
2497         // the adjacent SOLID has NO layers at all
2498         noShrinkE = isStructured;
2499       }
2500
2501       if ( noShrinkE )
2502       {
2503         _sdVec[i]._noShrinkShapes.insert( edgeID );
2504
2505         // check if there is a collision with to-shrink-from EDGEs in iSolid
2506         // if ( iSolid < _sdVec.size() )
2507         // {
2508         //   shapes.Clear();
2509         //   TopExp::MapShapes( fWOL, TopAbs_EDGE, shapes);
2510         //   for ( int iE = 1; iE <= shapes.Extent(); ++iE )
2511         //   {
2512         //     const TopoDS_Edge& E = TopoDS::Edge( shapes( iE ));
2513         //     const TGeomID    eID = getMeshDS()->ShapeToIndex( E );
2514         //     if ( eID == edgeID ||
2515         //          !_sdVec[iSolid]._shrinkShape2Shape.count( eID ) ||
2516         //          _sdVec[i]._noShrinkShapes.count( eID ))
2517         //       continue;
2518         //     for ( int is1st = 0; is1st < 2; ++is1st )
2519         //     {
2520         //       TopoDS_Vertex V = helper.IthVertex( is1st, E );
2521         //       if ( _sdVec[i]._noShrinkShapes.count( getMeshDS()->ShapeToIndex( V ) ))
2522         //       {
2523         //         return error("No way to make a conformal mesh with "
2524         //                      "the given set of faces with layers", _sdVec[i]._index);
2525         //       }
2526         //     }
2527         //   }
2528         // }
2529       }
2530
2531       // add VERTEXes of the edge in _noShrinkShapes, which is necessary if
2532       // _shrinkShape2Shape is different in the adjacent SOLID
2533       for ( TopoDS_Iterator vIt( edge ); vIt.More(); vIt.Next() )
2534       {
2535         TGeomID vID = getMeshDS()->ShapeToIndex( vIt.Value() );
2536         bool noShrinkV = false, noShrinkIfAdjMeshed = false;
2537
2538         if ( iSolid < _sdVec.size() )
2539         {
2540           if ( _sdVec[ iSolid ]._ignoreFaceIds.count( faceID ))
2541           {
2542             map< TGeomID, TopoDS_Shape >::iterator i2S, i2SAdj;
2543             i2S    = _sdVec[i     ]._shrinkShape2Shape.find( vID );
2544             i2SAdj = _sdVec[iSolid]._shrinkShape2Shape.find( vID );
2545             if ( i2SAdj == _sdVec[iSolid]._shrinkShape2Shape.end() )
2546               noShrinkV = (( isStructured ) ||
2547                            ( noShrinkIfAdjMeshed = i2S->second.ShapeType() == TopAbs_EDGE ));
2548             else
2549               noShrinkV = ( ! i2S->second.IsSame( i2SAdj->second ));
2550           }
2551           else
2552           {
2553             noShrinkV = noShrinkE;
2554           }
2555         }
2556         else
2557         {
2558           // the adjacent SOLID has NO layers at all
2559           if ( isStructured )
2560           {
2561             noShrinkV = true;
2562           }
2563           else
2564           {
2565             noShrinkV = noShrinkIfAdjMeshed =
2566               ( _sdVec[i]._shrinkShape2Shape[ vID ].ShapeType() == TopAbs_EDGE );
2567           }
2568         }
2569
2570         if ( noShrinkV && noShrinkIfAdjMeshed )
2571         {
2572           // noShrinkV if FACEs in the adjacent SOLID are meshed
2573           PShapeIteratorPtr fIt = helper.GetAncestors( _sdVec[i]._shrinkShape2Shape[ vID ],
2574                                                        *_mesh, TopAbs_FACE, &solid );
2575           while ( fIt->more() )
2576           {
2577             const TopoDS_Shape* f = fIt->next();
2578             if ( !f->IsSame( fWOL ))
2579             {
2580               noShrinkV = ! _mesh->GetSubMesh( *f )->IsEmpty();
2581               break;
2582             }
2583           }
2584         }
2585         if ( noShrinkV )
2586           _sdVec[i]._noShrinkShapes.insert( vID );
2587       }
2588
2589     } // loop on _sdVec[i]._shrinkShape2Shape
2590   } // loop on _sdVec to fill in _SolidData::_noShrinkShapes
2591
2592
2593     // add FACEs of other SOLIDs to _ignoreFaceIds
2594   for ( size_t i = 0; i < _sdVec.size(); ++i )
2595   {
2596     shapes.Clear();
2597     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_FACE, shapes);
2598
2599     for ( exp.Init( _mesh->GetShapeToMesh(), TopAbs_FACE ); exp.More(); exp.Next() )
2600     {
2601       if ( !shapes.Contains( exp.Current() ))
2602         _sdVec[i]._ignoreFaceIds.insert( getMeshDS()->ShapeToIndex( exp.Current() ));
2603     }
2604   }
2605
2606   return true;
2607 }
2608
2609 //================================================================================
2610 /*!
2611  * \brief Finds FACEs w/o layers for a given SOLID by an hypothesis
2612  */
2613 //================================================================================
2614
2615 void _ViscousBuilder::getIgnoreFaces(const TopoDS_Shape&             solid,
2616                                      const StdMeshers_ViscousLayers* hyp,
2617                                      const TopoDS_Shape&             hypShape,
2618                                      set<TGeomID>&                   ignoreFaceIds)
2619 {
2620   TopExp_Explorer exp;
2621
2622   vector<TGeomID> ids = hyp->GetBndShapes();
2623   if ( hyp->IsToIgnoreShapes() ) // FACEs to ignore are given
2624   {
2625     for ( size_t ii = 0; ii < ids.size(); ++ii )
2626     {
2627       const TopoDS_Shape& s = getMeshDS()->IndexToShape( ids[ii] );
2628       if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2629         ignoreFaceIds.insert( ids[ii] );
2630     }
2631   }
2632   else // FACEs with layers are given
2633   {
2634     exp.Init( solid, TopAbs_FACE );
2635     for ( ; exp.More(); exp.Next() )
2636     {
2637       TGeomID faceInd = getMeshDS()->ShapeToIndex( exp.Current() );
2638       if ( find( ids.begin(), ids.end(), faceInd ) == ids.end() )
2639         ignoreFaceIds.insert( faceInd );
2640     }
2641   }
2642
2643   // ignore internal FACEs if inlets and outlets are specified
2644   if ( hyp->IsToIgnoreShapes() )
2645   {
2646     TopTools_IndexedDataMapOfShapeListOfShape solidsOfFace;
2647     TopExp::MapShapesAndAncestors( hypShape,
2648                                    TopAbs_FACE, TopAbs_SOLID, solidsOfFace);
2649
2650     for ( exp.Init( solid, TopAbs_FACE ); exp.More(); exp.Next() )
2651     {
2652       const TopoDS_Face& face = TopoDS::Face( exp.Current() );
2653       if ( SMESH_MesherHelper::NbAncestors( face, *_mesh, TopAbs_SOLID ) < 2 )
2654         continue;
2655
2656       int nbSolids = solidsOfFace.FindFromKey( face ).Extent();
2657       if ( nbSolids > 1 )
2658         ignoreFaceIds.insert( getMeshDS()->ShapeToIndex( face ));
2659     }
2660   }
2661 }
2662
2663 //================================================================================
2664 /*!
2665  * \brief Create the inner surface of the viscous layer and prepare data for infation
2666  */
2667 //================================================================================
2668
2669 bool _ViscousBuilder::makeLayer(_SolidData& data)
2670 {
2671   // make a map to find new nodes on sub-shapes shared with other SOLID
2672   map< TGeomID, TNode2Edge* >::iterator s2ne;
2673   map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
2674   for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
2675   {
2676     TGeomID shapeInd = s2s->first;
2677     for ( size_t i = 0; i < _sdVec.size(); ++i )
2678     {
2679       if ( _sdVec[i]._index == data._index ) continue;
2680       map< TGeomID, TopoDS_Shape >::iterator s2s2 = _sdVec[i]._shrinkShape2Shape.find( shapeInd );
2681       if ( s2s2 != _sdVec[i]._shrinkShape2Shape.end() &&
2682            *s2s == *s2s2 && !_sdVec[i]._n2eMap.empty() )
2683       {
2684         data._s2neMap.insert( make_pair( shapeInd, &_sdVec[i]._n2eMap ));
2685         break;
2686       }
2687     }
2688   }
2689
2690   // Create temporary faces and _LayerEdge's
2691
2692   debugMsg( "######################" );
2693   dumpFunction(SMESH_Comment("makeLayers_")<<data._index);
2694
2695   vector< _EdgesOnShape >& edgesByGeom = data._edgesOnShape;
2696
2697   data._stepSize = Precision::Infinite();
2698   data._stepSizeNodes[0] = 0;
2699
2700   SMESH_MesherHelper helper( *_mesh );
2701   helper.SetSubShape( data._solid );
2702   helper.SetElementsOnShape( true );
2703
2704   vector< const SMDS_MeshNode*> newNodes; // of a mesh face
2705   TNode2Edge::iterator n2e2;
2706
2707   // make _LayerEdge's
2708   for ( TopExp_Explorer exp( data._solid, TopAbs_FACE ); exp.More(); exp.Next() )
2709   {
2710     const TopoDS_Face& F = TopoDS::Face( exp.Current() );
2711     SMESH_subMesh*    sm = _mesh->GetSubMesh( F );
2712     const TGeomID     id = sm->GetId();
2713     if ( edgesByGeom[ id ]._shape.IsNull() )
2714       continue; // no layers
2715     SMESH_ProxyMesh::SubMesh* proxySub =
2716       data._proxyMesh->getFaceSubM( F, /*create=*/true);
2717
2718     SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
2719     if ( !smDS ) return error(SMESH_Comment("Not meshed face ") << id, data._index );
2720
2721     SMDS_ElemIteratorPtr eIt = smDS->GetElements();
2722     while ( eIt->more() )
2723     {
2724       const SMDS_MeshElement* face = eIt->next();
2725       double          faceMaxCosin = -1;
2726       _LayerEdge*     maxCosinEdge = 0;
2727       int             nbDegenNodes = 0;
2728
2729       newNodes.resize( face->NbCornerNodes() );
2730       for ( size_t i = 0 ; i < newNodes.size(); ++i )
2731       {
2732         const SMDS_MeshNode* n = face->GetNode( i );
2733         const int      shapeID = n->getshapeId();
2734         const bool onDegenShap = helper.IsDegenShape( shapeID );
2735         const bool onDegenEdge = ( onDegenShap && n->GetPosition()->GetDim() == 1 );
2736         if ( onDegenShap )
2737         {
2738           if ( onDegenEdge )
2739           {
2740             // substitute n on a degenerated EDGE with a node on a corresponding VERTEX
2741             const TopoDS_Shape& E = getMeshDS()->IndexToShape( shapeID );
2742             TopoDS_Vertex       V = helper.IthVertex( 0, TopoDS::Edge( E ));
2743             if ( const SMDS_MeshNode* vN = SMESH_Algo::VertexNode( V, getMeshDS() )) {
2744               n = vN;
2745               nbDegenNodes++;
2746             }
2747           }
2748           else
2749           {
2750             nbDegenNodes++;
2751           }
2752         }
2753         TNode2Edge::iterator n2e = data._n2eMap.insert({ n, nullptr }).first;
2754         if ( !(*n2e).second )
2755         {
2756           // add a _LayerEdge
2757           _LayerEdge* edge = _Factory::NewLayerEdge();
2758           edge->_nodes.push_back( n );
2759           n2e->second = edge;
2760           edgesByGeom[ shapeID ]._edges.push_back( edge );
2761           const bool noShrink = data._noShrinkShapes.count( shapeID );
2762
2763           SMESH_TNodeXYZ xyz( n );
2764
2765           // set edge data or find already refined _LayerEdge and get data from it
2766           if (( !noShrink                                                     ) &&
2767               ( n->GetPosition()->GetTypeOfPosition() != SMDS_TOP_FACE        ) &&
2768               (( s2ne = data._s2neMap.find( shapeID )) != data._s2neMap.end() ) &&
2769               (( n2e2 = (*s2ne).second->find( n )) != s2ne->second->end()     ))
2770           {
2771             _LayerEdge* foundEdge = (*n2e2).second;
2772             gp_XYZ        lastPos = edge->Copy( *foundEdge, edgesByGeom[ shapeID ], helper );
2773             foundEdge->_pos.push_back( lastPos );
2774             // location of the last node is modified and we restore it by foundEdge->_pos.back()
2775             const_cast< SMDS_MeshNode* >
2776               ( edge->_nodes.back() )->setXYZ( xyz.X(), xyz.Y(), xyz.Z() );
2777           }
2778           else
2779           {
2780             if ( !noShrink )
2781             {
2782               edge->_nodes.push_back( helper.AddNode( xyz.X(), xyz.Y(), xyz.Z() ));
2783             }
2784             if ( !setEdgeData( *edge, edgesByGeom[ shapeID ], helper, data ))
2785               return false;
2786
2787             if ( edge->_nodes.size() < 2 && !noShrink )
2788               edge->Block( data ); // a sole node is moved only if noShrink
2789           }
2790           dumpMove(edge->_nodes.back());
2791
2792           if ( edge->_cosin > faceMaxCosin && edge->_nodes.size() > 1 )
2793           {
2794             faceMaxCosin = edge->_cosin;
2795             maxCosinEdge = edge;
2796           }
2797         }
2798         newNodes[ i ] = n2e->second->_nodes.back();
2799
2800         if ( onDegenEdge )
2801           data._n2eMap.insert( make_pair( face->GetNode( i ), n2e->second ));
2802       }
2803       if ( newNodes.size() - nbDegenNodes < 2 )
2804         continue;
2805
2806       // create a temporary face
2807       const SMDS_MeshElement* newFace =
2808         new _TmpMeshFace( newNodes, --_tmpFaceID, face->GetShapeID(), face );
2809       proxySub->AddElement( newFace );
2810
2811       // compute inflation step size by min size of element on a convex surface
2812       if ( faceMaxCosin > theMinSmoothCosin )
2813         limitStepSize( data, face, maxCosinEdge );
2814
2815     } // loop on 2D elements on a FACE
2816   } // loop on FACEs of a SOLID to create _LayerEdge's
2817
2818
2819   // Set _LayerEdge::_neibors
2820   TNode2Edge::iterator n2e;
2821   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
2822   {
2823     _EdgesOnShape& eos = data._edgesOnShape[iS];
2824     for ( size_t i = 0; i < eos._edges.size(); ++i )
2825     {
2826       _LayerEdge* edge = eos._edges[i];
2827       TIDSortedNodeSet nearNodes;
2828       SMDS_ElemIteratorPtr fIt = edge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
2829       while ( fIt->more() )
2830       {
2831         const SMDS_MeshElement* f = fIt->next();
2832         if ( !data._ignoreFaceIds.count( f->getshapeId() ))
2833           nearNodes.insert( f->begin_nodes(), f->end_nodes() );
2834       }
2835       nearNodes.erase( edge->_nodes[0] );
2836       edge->_neibors.reserve( nearNodes.size() );
2837       TIDSortedNodeSet::iterator node = nearNodes.begin();
2838       for ( ; node != nearNodes.end(); ++node )
2839         if (( n2e = data._n2eMap.find( *node )) != data._n2eMap.end() )
2840           edge->_neibors.push_back( n2e->second );
2841     }
2842
2843     // Fix uv of nodes on periodic FACEs (bos #20643)
2844
2845     if ( eos.ShapeType() != TopAbs_EDGE ||
2846          eos.SWOLType()  != TopAbs_FACE ||
2847          eos.size() == 0 )
2848       continue;
2849
2850     const TopoDS_Face& F = TopoDS::Face( eos._sWOL );
2851     SMESH_MesherHelper faceHelper( *_mesh );
2852     faceHelper.SetSubShape( F );
2853     faceHelper.ToFixNodeParameters( true );
2854     if ( faceHelper.GetPeriodicIndex() == 0 )
2855       continue;
2856
2857     SMESHDS_SubMesh* smDS = getMeshDS()->MeshElements( F );
2858     if ( !smDS || smDS->GetNodes() == 0 )
2859       continue;
2860
2861     bool toCheck = true;
2862     const double tol = 2 * helper.MaxTolerance( F );
2863     for ( SMDS_NodeIteratorPtr nIt = smDS->GetNodes(); nIt->more(); )
2864     {
2865       const SMDS_MeshNode* node = nIt->next();
2866       gp_XY uvNew( Precision::Infinite(), 0 );
2867       if ( toCheck )
2868       {
2869         toCheck = false;
2870         gp_XY uv = faceHelper.GetNodeUV( F, node );
2871         if ( ! faceHelper.CheckNodeUV( F, node, uvNew, tol, /*force=*/true ))
2872           break; // projection on F failed
2873         if (( uv - uvNew ).Modulus() < Precision::Confusion() )
2874           break; // current uv is OK
2875       }
2876       faceHelper.CheckNodeUV( F, node, uvNew, tol, /*force=*/true );
2877     }
2878   }
2879
2880   data._epsilon = 1e-7;
2881   if ( data._stepSize < 1. )
2882     data._epsilon *= data._stepSize;
2883
2884   if ( !findShapesToSmooth( data )) // _LayerEdge::_maxLen is computed here
2885     return false;
2886
2887   // limit data._stepSize depending on surface curvature and fill data._convexFaces
2888   limitStepSizeByCurvature( data ); // !!! it must be before node substitution in _Simplex
2889
2890   // Set target nodes into _Simplex and _LayerEdge's to _2NearEdges
2891   const SMDS_MeshNode* nn[2];
2892   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
2893   {
2894     _EdgesOnShape& eos = data._edgesOnShape[iS];
2895     for ( size_t i = 0; i < eos._edges.size(); ++i )
2896     {
2897       _LayerEdge* edge = eos._edges[i];
2898       if ( edge->IsOnEdge() )
2899       {
2900         // get neighbor nodes
2901         bool hasData = ( edge->_2neibors->_edges[0] );
2902         if ( hasData ) // _LayerEdge is a copy of another one
2903         {
2904           nn[0] = edge->_2neibors->srcNode(0);
2905           nn[1] = edge->_2neibors->srcNode(1);
2906         }
2907         else if ( !findNeiborsOnEdge( edge, nn[0],nn[1], eos, data ))
2908         {
2909           return false;
2910         }
2911         // set neighbor _LayerEdge's
2912         for ( int j = 0; j < 2; ++j )
2913         {
2914           if (( n2e = data._n2eMap.find( nn[j] )) == data._n2eMap.end() )
2915             return error("_LayerEdge not found by src node", data._index);
2916           edge->_2neibors->_edges[j] = n2e->second;
2917         }
2918         if ( !hasData )
2919           edge->SetDataByNeighbors( nn[0], nn[1], eos, helper );
2920       }
2921
2922       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
2923       {
2924         _Simplex& s = edge->_simplices[j];
2925         s._nNext = data._n2eMap[ s._nNext ]->_nodes.back();
2926         s._nPrev = data._n2eMap[ s._nPrev ]->_nodes.back();
2927       }
2928
2929       // For an _LayerEdge on a degenerated EDGE, copy some data from
2930       // a corresponding _LayerEdge on a VERTEX
2931       // (issue 52453, pb on a downloaded SampleCase2-Tet-netgen-mephisto.hdf)
2932       if ( helper.IsDegenShape( edge->_nodes[0]->getshapeId() ))
2933       {
2934         // Generally we should not get here
2935         if ( eos.ShapeType() != TopAbs_EDGE )
2936           continue;
2937         TopoDS_Vertex V = helper.IthVertex( 0, TopoDS::Edge( eos._shape ));
2938         const SMDS_MeshNode* vN = SMESH_Algo::VertexNode( V, getMeshDS() );
2939         if (( n2e = data._n2eMap.find( vN )) == data._n2eMap.end() )
2940           continue;
2941         const _LayerEdge* vEdge = n2e->second;
2942         edge->_normal    = vEdge->_normal;
2943         edge->_lenFactor = vEdge->_lenFactor;
2944         edge->_cosin     = vEdge->_cosin;
2945       }
2946
2947     } // loop on data._edgesOnShape._edges
2948   } // loop on data._edgesOnShape
2949
2950   // fix _LayerEdge::_2neibors on EDGEs to smooth
2951   // map< TGeomID,Handle(Geom_Curve)>::iterator e2c = data._edge2curve.begin();
2952   // for ( ; e2c != data._edge2curve.end(); ++e2c )
2953   //   if ( !e2c->second.IsNull() )
2954   //   {
2955   //     if ( _EdgesOnShape* eos = data.GetShapeEdges( e2c->first ))
2956   //       data.Sort2NeiborsOnEdge( eos->_edges );
2957   //   }
2958
2959   dumpFunctionEnd();
2960   return true;
2961 }
2962
2963 //================================================================================
2964 /*!
2965  * \brief Compute inflation step size by min size of element on a convex surface
2966  */
2967 //================================================================================
2968
2969 void _ViscousBuilder::limitStepSize( _SolidData&             data,
2970                                      const SMDS_MeshElement* face,
2971                                      const _LayerEdge*       maxCosinEdge )
2972 {
2973   int iN = 0;
2974   double minSize = 10 * data._stepSize;
2975   const int nbNodes = face->NbCornerNodes();
2976   for ( int i = 0; i < nbNodes; ++i )
2977   {
2978     const SMDS_MeshNode* nextN = face->GetNode( SMESH_MesherHelper::WrapIndex( i+1, nbNodes ));
2979     const SMDS_MeshNode*  curN = face->GetNode( i );
2980     if ( nextN->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ||
2981          curN-> GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2982     {
2983       double dist = SMESH_TNodeXYZ( curN ).Distance( nextN );
2984       if ( dist < minSize )
2985         minSize = dist, iN = i;
2986     }
2987   }
2988   double newStep = 0.8 * minSize / maxCosinEdge->_lenFactor;
2989   if ( newStep < data._stepSize )
2990   {
2991     data._stepSize = newStep;
2992     data._stepSizeCoeff = 0.8 / maxCosinEdge->_lenFactor;
2993     data._stepSizeNodes[0] = face->GetNode( iN );
2994     data._stepSizeNodes[1] = face->GetNode( SMESH_MesherHelper::WrapIndex( iN+1, nbNodes ));
2995   }
2996 }
2997
2998 //================================================================================
2999 /*!
3000  * \brief Compute inflation step size by min size of element on a convex surface
3001  */
3002 //================================================================================
3003
3004 void _ViscousBuilder::limitStepSize( _SolidData& data, const double minSize )
3005 {
3006   if ( minSize < data._stepSize )
3007   {
3008     data._stepSize = minSize;
3009     if ( data._stepSizeNodes[0] )
3010     {
3011       double dist =
3012         SMESH_TNodeXYZ(data._stepSizeNodes[0]).Distance(data._stepSizeNodes[1]);
3013       data._stepSizeCoeff = data._stepSize / dist;
3014     }
3015   }
3016 }
3017
3018 //================================================================================
3019 /*!
3020  * \brief Limit data._stepSize by evaluating curvature of shapes and fill data._convexFaces
3021  */
3022 //================================================================================
3023
3024 void _ViscousBuilder::limitStepSizeByCurvature( _SolidData& data )
3025 {
3026   SMESH_MesherHelper helper( *_mesh );
3027
3028   BRepLProp_SLProps surfProp( 2, 1e-6 );
3029   data._convexFaces.clear();
3030
3031   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
3032   {
3033     _EdgesOnShape& eof = data._edgesOnShape[iS];
3034     if ( eof.ShapeType() != TopAbs_FACE ||
3035          data._ignoreFaceIds.count( eof._shapeID ))
3036       continue;
3037
3038     TopoDS_Face        F = TopoDS::Face( eof._shape );
3039     const TGeomID faceID = eof._shapeID;
3040
3041     BRepAdaptor_Surface surface( F, false );
3042     surfProp.SetSurface( surface );
3043
3044     _ConvexFace cnvFace;
3045     cnvFace._face = F;
3046     cnvFace._normalsFixed = false;
3047     cnvFace._isTooCurved = false;
3048     cnvFace._normalsFixedOnBorders = false;
3049
3050     double maxCurvature = cnvFace.GetMaxCurvature( data, eof, surfProp, helper );
3051     if ( maxCurvature > 0 )
3052     {
3053       limitStepSize( data, 0.9 / maxCurvature );
3054       findEdgesToUpdateNormalNearConvexFace( cnvFace, data, helper );
3055     }
3056     if ( !cnvFace._isTooCurved ) continue;
3057
3058     _ConvexFace & convFace =
3059       data._convexFaces.insert( make_pair( faceID, cnvFace )).first->second;
3060
3061     // skip a closed surface (data._convexFaces is useful anyway)
3062     bool isClosedF = false;
3063     helper.SetSubShape( F );
3064     if ( helper.HasRealSeam() )
3065     {
3066       // in the closed surface there must be a closed EDGE
3067       for ( TopExp_Explorer eIt( F, TopAbs_EDGE ); eIt.More() && !isClosedF; eIt.Next() )
3068         isClosedF = helper.IsClosedEdge( TopoDS::Edge( eIt.Current() ));
3069     }
3070     if ( isClosedF )
3071     {
3072       // limit _LayerEdge::_maxLen on the FACE
3073       const double oriFactor    = ( F.Orientation() == TopAbs_REVERSED ? +1. : -1. );
3074       const double minCurvature =
3075         1. / ( eof._hyp.GetTotalThickness() * ( 1 + theThickToIntersection ));
3076       map< TGeomID, _EdgesOnShape* >::iterator id2eos = cnvFace._subIdToEOS.find( faceID );
3077       if ( id2eos != cnvFace._subIdToEOS.end() )
3078       {
3079         _EdgesOnShape& eos = * id2eos->second;
3080         for ( size_t i = 0; i < eos._edges.size(); ++i )
3081         {
3082           _LayerEdge* ledge = eos._edges[ i ];
3083           gp_XY uv = helper.GetNodeUV( F, ledge->_nodes[0] );
3084           surfProp.SetParameters( uv.X(), uv.Y() );
3085           if ( surfProp.IsCurvatureDefined() )
3086           {
3087             double curvature = Max( surfProp.MaxCurvature() * oriFactor,
3088                                     surfProp.MinCurvature() * oriFactor );
3089             if ( curvature > minCurvature )
3090               ledge->SetMaxLen( Min( ledge->_maxLen, 1. / curvature ));
3091           }
3092         }
3093       }
3094       continue;
3095     }
3096
3097     // Fill _ConvexFace::_simplexTestEdges. These _LayerEdge's are used to detect
3098     // prism distortion.
3099     map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.find( faceID );
3100     if ( id2eos != convFace._subIdToEOS.end() && !id2eos->second->_edges.empty() )
3101     {
3102       // there are _LayerEdge's on the FACE it-self;
3103       // select _LayerEdge's near EDGEs
3104       _EdgesOnShape& eos = * id2eos->second;
3105       for ( size_t i = 0; i < eos._edges.size(); ++i )
3106       {
3107         _LayerEdge* ledge = eos._edges[ i ];
3108         for ( size_t j = 0; j < ledge->_simplices.size(); ++j )
3109           if ( ledge->_simplices[j]._nNext->GetPosition()->GetDim() < 2 )
3110           {
3111             // do not select _LayerEdge's neighboring sharp EDGEs
3112             bool sharpNbr = false;
3113             for ( size_t iN = 0; iN < ledge->_neibors.size()  && !sharpNbr; ++iN )
3114               sharpNbr = ( ledge->_neibors[iN]->_cosin > theMinSmoothCosin );
3115             if ( !sharpNbr )
3116               convFace._simplexTestEdges.push_back( ledge );
3117             break;
3118           }
3119       }
3120     }
3121     else
3122     {
3123       // where there are no _LayerEdge's on a _ConvexFace,
3124       // as e.g. on a fillet surface with no internal nodes - issue 22580,
3125       // so that collision of viscous internal faces is not detected by check of
3126       // intersection of _LayerEdge's with the viscous internal faces.
3127
3128       set< const SMDS_MeshNode* > usedNodes;
3129
3130       // look for _LayerEdge's with null _sWOL
3131       id2eos = convFace._subIdToEOS.begin();
3132       for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
3133       {
3134         _EdgesOnShape& eos = * id2eos->second;
3135         if ( !eos._sWOL.IsNull() )
3136           continue;
3137         for ( size_t i = 0; i < eos._edges.size(); ++i )
3138         {
3139           _LayerEdge* ledge = eos._edges[ i ];
3140           const SMDS_MeshNode* srcNode = ledge->_nodes[0];
3141           if ( !usedNodes.insert( srcNode ).second ) continue;
3142
3143           for ( size_t i = 0; i < ledge->_simplices.size(); ++i )
3144           {
3145             usedNodes.insert( ledge->_simplices[i]._nPrev );
3146             usedNodes.insert( ledge->_simplices[i]._nNext );
3147           }
3148           convFace._simplexTestEdges.push_back( ledge );
3149         }
3150       }
3151     }
3152   } // loop on FACEs of data._solid
3153 }
3154
3155 //================================================================================
3156 /*!
3157  * \brief Detect shapes (and _LayerEdge's on them) to smooth
3158  */
3159 //================================================================================
3160
3161 bool _ViscousBuilder::findShapesToSmooth( _SolidData& data )
3162 {
3163   // define allowed thickness
3164   computeGeomSize( data ); // compute data._geomSize and _LayerEdge::_maxLen
3165
3166
3167   // Find shapes needing smoothing; such a shape has _LayerEdge._normal on it's
3168   // boundary inclined to the shape at a sharp angle
3169
3170   TopTools_MapOfShape edgesOfSmooFaces;
3171   SMESH_MesherHelper helper( *_mesh );
3172   bool ok = true;
3173
3174   vector< _EdgesOnShape >& edgesByGeom = data._edgesOnShape;
3175   data._nbShapesToSmooth = 0;
3176
3177   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check FACEs
3178   {
3179     _EdgesOnShape& eos = edgesByGeom[iS];
3180     eos._toSmooth = false;
3181     if ( eos._edges.empty() || eos.ShapeType() != TopAbs_FACE )
3182       continue;
3183
3184     double tgtThick = eos._hyp.GetTotalThickness();
3185     SMESH_subMeshIteratorPtr subIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false );
3186     while ( subIt->more() && !eos._toSmooth )
3187     {
3188       TGeomID iSub = subIt->next()->GetId();
3189       const vector<_LayerEdge*>& eSub = edgesByGeom[ iSub ]._edges;
3190       if ( eSub.empty() ) continue;
3191
3192       double faceSize;
3193       for ( size_t i = 0; i < eSub.size() && !eos._toSmooth; ++i )
3194         if ( eSub[i]->_cosin > theMinSmoothCosin )
3195         {
3196           SMDS_ElemIteratorPtr fIt = eSub[i]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
3197           while ( fIt->more() && !eos._toSmooth )
3198           {
3199             const SMDS_MeshElement* face = fIt->next();
3200             if ( face->getshapeId() == eos._shapeID &&
3201                  getDistFromEdge( face, eSub[i]->_nodes[0], faceSize ))
3202             {
3203               eos._toSmooth = needSmoothing( eSub[i]->_cosin,
3204                                              tgtThick * eSub[i]->_lenFactor,
3205                                              faceSize);
3206             }
3207           }
3208         }
3209     }
3210     if ( eos._toSmooth )
3211     {
3212       for ( TopExp_Explorer eExp( edgesByGeom[iS]._shape, TopAbs_EDGE ); eExp.More(); eExp.Next() )
3213         edgesOfSmooFaces.Add( eExp.Current() );
3214
3215       data.PrepareEdgesToSmoothOnFace( &edgesByGeom[iS], /*substituteSrcNodes=*/false );
3216     }
3217     data._nbShapesToSmooth += eos._toSmooth;
3218
3219   }  // check FACEs
3220
3221   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check EDGEs
3222   {
3223     _EdgesOnShape& eos = edgesByGeom[iS];
3224     eos._edgeSmoother = NULL;
3225     if ( eos._edges.empty() || eos.ShapeType() != TopAbs_EDGE ) continue;
3226     if ( !eos._hyp.ToSmooth() ) continue;
3227
3228     const TopoDS_Edge& E = TopoDS::Edge( edgesByGeom[iS]._shape );
3229     if ( SMESH_Algo::isDegenerated( E ) || !edgesOfSmooFaces.Contains( E ))
3230       continue;
3231
3232     double tgtThick = eos._hyp.GetTotalThickness(), h0 = eos._hyp.Get1stLayerThickness();
3233     for ( TopoDS_Iterator vIt( E ); vIt.More() && !eos._toSmooth; vIt.Next() )
3234     {
3235       TGeomID iV = getMeshDS()->ShapeToIndex( vIt.Value() );
3236       vector<_LayerEdge*>& eV = edgesByGeom[ iV ]._edges;
3237       if ( eV.empty() || eV[0]->Is( _LayerEdge::MULTI_NORMAL )) continue;
3238       gp_Vec  eDir    = getEdgeDir( E, TopoDS::Vertex( vIt.Value() ), h0 );
3239       double angle    = eDir.Angle( eV[0]->_normal );
3240       double cosin    = Cos( angle );
3241       double cosinAbs = Abs( cosin );
3242       if ( cosinAbs > theMinSmoothCosin )
3243       {
3244         // always smooth analytic EDGEs
3245         Handle(Geom_Curve) curve = _Smoother1D::CurveForSmooth( E, eos, helper );
3246         eos._toSmooth = ! curve.IsNull();
3247
3248         // compare tgtThick with the length of an end segment
3249         SMDS_ElemIteratorPtr eIt = eV[0]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Edge);
3250         while ( eIt->more() && !eos._toSmooth )
3251         {
3252           const SMDS_MeshElement* endSeg = eIt->next();
3253           if ( endSeg->getshapeId() == (int) iS )
3254           {
3255             double segLen =
3256               SMESH_TNodeXYZ( endSeg->GetNode( 0 )).Distance( endSeg->GetNode( 1 ));
3257             eos._toSmooth = needSmoothing( cosinAbs, tgtThick * eV[0]->_lenFactor, segLen );
3258           }
3259         }
3260         if ( eos._toSmooth )
3261         {
3262           eos._edgeSmoother = new _Smoother1D( curve, eos );
3263
3264           // for ( size_t i = 0; i < eos._edges.size(); ++i )
3265           //   eos._edges[i]->Set( _LayerEdge::TO_SMOOTH );
3266         }
3267       }
3268     }
3269     data._nbShapesToSmooth += eos._toSmooth;
3270
3271   } // check EDGEs
3272
3273   // Reset _cosin if no smooth is allowed by the user
3274   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS )
3275   {
3276     _EdgesOnShape& eos = edgesByGeom[iS];
3277     if ( eos._edges.empty() ) continue;
3278
3279     if ( !eos._hyp.ToSmooth() )
3280       for ( size_t i = 0; i < eos._edges.size(); ++i )
3281         //eos._edges[i]->SetCosin( 0 ); // keep _cosin to use in limitMaxLenByCurvature()
3282         eos._edges[i]->_lenFactor = 1;
3283   }
3284
3285
3286   // Fill _eosC1 to make that C1 FACEs and EDGEs between them to be smoothed as a whole
3287
3288   TopTools_MapOfShape c1VV;
3289
3290   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check FACEs
3291   {
3292     _EdgesOnShape& eos = edgesByGeom[iS];
3293     if ( eos._edges.empty() ||
3294          eos.ShapeType() != TopAbs_FACE ||
3295          !eos._toSmooth )
3296       continue;
3297
3298     // check EDGEs of a FACE
3299     TopTools_MapOfShape checkedEE, allVV;
3300     list< SMESH_subMesh* > smQueue( 1, eos._subMesh ); // sm of FACEs
3301     while ( !smQueue.empty() )
3302     {
3303       SMESH_subMesh* sm = smQueue.front();
3304       smQueue.pop_front();
3305       SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(/*includeSelf=*/false);
3306       while ( smIt->more() )
3307       {
3308         sm = smIt->next();
3309         if ( sm->GetSubShape().ShapeType() == TopAbs_VERTEX )
3310           allVV.Add( sm->GetSubShape() );
3311         if ( sm->GetSubShape().ShapeType() != TopAbs_EDGE ||
3312              !checkedEE.Add( sm->GetSubShape() ))
3313           continue;
3314
3315         _EdgesOnShape*      eoe = data.GetShapeEdges( sm->GetId() );
3316         vector<_LayerEdge*>& eE = eoe->_edges;
3317         if ( eE.empty() || !eoe->_sWOL.IsNull() )
3318           continue;
3319
3320         bool isC1 = true; // check continuity along an EDGE
3321         for ( size_t i = 0; i < eE.size() && isC1; ++i )
3322           isC1 = ( Abs( eE[i]->_cosin ) < theMinSmoothCosin );
3323         if ( !isC1 )
3324           continue;
3325
3326         // check that mesh faces are C1 as well
3327         {
3328           gp_XYZ norm1, norm2;
3329           const SMDS_MeshNode*   n = eE[ eE.size() / 2 ]->_nodes[0];
3330           SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
3331           if ( !SMESH_MeshAlgos::FaceNormal( fIt->next(), norm1, /*normalized=*/true ))
3332             continue;
3333           while ( fIt->more() && isC1 )
3334             isC1 = ( SMESH_MeshAlgos::FaceNormal( fIt->next(), norm2, /*normalized=*/true ) &&
3335                      Abs( norm1 * norm2 ) >= ( 1. - theMinSmoothCosin ));
3336           if ( !isC1 )
3337             continue;
3338         }
3339
3340         // add the EDGE and an adjacent FACE to _eosC1
3341         PShapeIteratorPtr fIt = helper.GetAncestors( sm->GetSubShape(), *_mesh, TopAbs_FACE );
3342         while ( const TopoDS_Shape* face = fIt->next() )
3343         {
3344           _EdgesOnShape* eof = data.GetShapeEdges( *face );
3345           if ( !eof ) continue; // other solid
3346           if ( eos._shapeID == eof->_shapeID ) continue;
3347           if ( !eos.HasC1( eof ))
3348           {
3349             // check the FACEs
3350             eos._eosC1.push_back( eof );
3351             eof->_toSmooth = false;
3352             data.PrepareEdgesToSmoothOnFace( eof, /*substituteSrcNodes=*/false );
3353             smQueue.push_back( eof->_subMesh );
3354           }
3355           if ( !eos.HasC1( eoe ))
3356           {
3357             eos._eosC1.push_back( eoe );
3358             eoe->_toSmooth = false;
3359             data.PrepareEdgesToSmoothOnFace( eoe, /*substituteSrcNodes=*/false );
3360           }
3361         }
3362       }
3363     }
3364     if ( eos._eosC1.empty() )
3365       continue;
3366
3367     // check VERTEXes of C1 FACEs
3368     TopTools_MapIteratorOfMapOfShape vIt( allVV );
3369     for ( ; vIt.More(); vIt.Next() )
3370     {
3371       _EdgesOnShape* eov = data.GetShapeEdges( vIt.Key() );
3372       if ( !eov || eov->_edges.empty() || !eov->_sWOL.IsNull() )
3373         continue;
3374
3375       bool isC1 = true; // check if all adjacent FACEs are in eos._eosC1
3376       PShapeIteratorPtr fIt = helper.GetAncestors( vIt.Key(), *_mesh, TopAbs_FACE );
3377       while ( const TopoDS_Shape* face = fIt->next() )
3378       {
3379         _EdgesOnShape* eof = data.GetShapeEdges( *face );
3380         if ( !eof ) continue; // other solid
3381         isC1 = ( face->IsSame( eos._shape ) || eos.HasC1( eof ));
3382         if ( !isC1 )
3383           break;
3384       }
3385       if ( isC1 )
3386       {
3387         eos._eosC1.push_back( eov );
3388         data.PrepareEdgesToSmoothOnFace( eov, /*substituteSrcNodes=*/false );
3389         c1VV.Add( eov->_shape );
3390       }
3391     }
3392
3393   } // fill _eosC1 of FACEs
3394
3395
3396   // Find C1 EDGEs
3397
3398   vector< pair< _EdgesOnShape*, gp_XYZ > > dirOfEdges;
3399
3400   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check VERTEXes
3401   {
3402     _EdgesOnShape& eov = edgesByGeom[iS];
3403     if ( eov._edges.empty() ||
3404          eov.ShapeType() != TopAbs_VERTEX ||
3405          c1VV.Contains( eov._shape ))
3406       continue;
3407     const TopoDS_Vertex& V = TopoDS::Vertex( eov._shape );
3408
3409     // get directions of surrounding EDGEs
3410     dirOfEdges.clear();
3411     PShapeIteratorPtr fIt = helper.GetAncestors( eov._shape, *_mesh, TopAbs_EDGE );
3412     while ( const TopoDS_Shape* e = fIt->next() )
3413     {
3414       _EdgesOnShape* eoe = data.GetShapeEdges( *e );
3415       if ( !eoe ) continue; // other solid
3416       gp_XYZ eDir = getEdgeDir( TopoDS::Edge( *e ), V, eoe->_hyp.Get1stLayerThickness() );
3417       if ( !Precision::IsInfinite( eDir.X() ))
3418         dirOfEdges.push_back( make_pair( eoe, eDir.Normalized() ));
3419     }
3420
3421     // find EDGEs with C1 directions
3422     for ( size_t i = 0; i < dirOfEdges.size(); ++i )
3423       for ( size_t j = i+1; j < dirOfEdges.size(); ++j )
3424         if ( dirOfEdges[i].first && dirOfEdges[j].first )
3425         {
3426           double dot = dirOfEdges[i].second * dirOfEdges[j].second;
3427           bool isC1 = ( dot < - ( 1. - theMinSmoothCosin ));
3428           if ( isC1 )
3429           {
3430             double maxEdgeLen = 3 * Min( eov._edges[0]->_maxLen, eov._hyp.GetTotalThickness() );
3431             for ( int isJ = 0; isJ < 2; ++isJ ) // loop on [i,j]
3432             {
3433               size_t k = isJ ? j : i;
3434               const TopoDS_Edge& e = TopoDS::Edge( dirOfEdges[k].first->_shape );
3435               double eLen = SMESH_Algo::EdgeLength( e );
3436               if ( eLen < maxEdgeLen )
3437               {
3438                 TopoDS_Shape oppV = SMESH_MesherHelper::IthVertex( 0, e );
3439                 if ( oppV.IsSame( V ))
3440                   oppV = SMESH_MesherHelper::IthVertex( 1, e );
3441                 _EdgesOnShape* eovOpp = data.GetShapeEdges( oppV );
3442                 if ( dirOfEdges[k].second * eovOpp->_edges[0]->_normal < 0 )
3443                   eov._eosC1.push_back( dirOfEdges[k].first );
3444               }
3445               dirOfEdges[k].first = 0;
3446             }
3447           }
3448         }
3449   } // fill _eosC1 of VERTEXes
3450
3451
3452
3453   return ok;
3454 }
3455
3456 //================================================================================
3457 /*!
3458  * \brief Set up _SolidData::_edgesOnShape
3459  */
3460 //================================================================================
3461
3462 int _ViscousBuilder::makeEdgesOnShape()
3463 {
3464   const int nbShapes = getMeshDS()->MaxShapeIndex();
3465   int nbSolidsWL = 0;
3466
3467   for ( size_t i = 0; i < _sdVec.size(); ++i )
3468   {
3469     _SolidData& data = _sdVec[ i ];
3470     vector< _EdgesOnShape >& edgesByGeom = data._edgesOnShape;
3471     edgesByGeom.resize( nbShapes+1 );
3472
3473     // set data of _EdgesOnShape's
3474     int nbShapesWL = 0;
3475     if ( SMESH_subMesh* sm = _mesh->GetSubMesh( data._solid ))
3476     {
3477       SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(/*includeSelf=*/false);
3478       while ( smIt->more() )
3479       {
3480         sm = smIt->next();
3481         if ( sm->GetSubShape().ShapeType() == TopAbs_FACE &&
3482              data._ignoreFaceIds.count( sm->GetId() ))
3483           continue;
3484
3485         setShapeData( edgesByGeom[ sm->GetId() ], sm, data );
3486
3487         nbShapesWL += ( sm->GetSubShape().ShapeType() == TopAbs_FACE );
3488       }
3489     }
3490     if ( nbShapesWL == 0 ) // no shapes with layers in a SOLID
3491     {
3492       data._done = true;
3493       SMESHUtils::FreeVector( edgesByGeom );
3494     }
3495     else
3496     {
3497       ++nbSolidsWL;
3498     }
3499   }
3500   return nbSolidsWL;
3501 }
3502
3503 //================================================================================
3504 /*!
3505  * \brief initialize data of _EdgesOnShape
3506  */
3507 //================================================================================
3508
3509 void _ViscousBuilder::setShapeData( _EdgesOnShape& eos,
3510                                     SMESH_subMesh* sm,
3511                                     _SolidData&    data )
3512 {
3513   if ( !eos._shape.IsNull() ||
3514        sm->GetSubShape().ShapeType() == TopAbs_WIRE )
3515     return;
3516
3517   SMESH_MesherHelper helper( *_mesh );
3518
3519   eos._subMesh = sm;
3520   eos._shapeID = sm->GetId();
3521   eos._shape   = sm->GetSubShape();
3522   if ( eos.ShapeType() == TopAbs_FACE )
3523     eos._shape.Orientation( helper.GetSubShapeOri( data._solid, eos._shape ));
3524   eos._toSmooth = false;
3525   eos._data = &data;
3526   eos._mapper2D = nullptr;
3527
3528   // set _SWOL
3529   map< TGeomID, TopoDS_Shape >::const_iterator s2s =
3530     data._shrinkShape2Shape.find( eos._shapeID );
3531   if ( s2s != data._shrinkShape2Shape.end() )
3532     eos._sWOL = s2s->second;
3533
3534   eos._isRegularSWOL = true;
3535   if ( eos.SWOLType() == TopAbs_FACE )
3536   {
3537     const TopoDS_Face& F = TopoDS::Face( eos._sWOL );
3538     Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( F );
3539     eos._isRegularSWOL = ( ! surface->HasSingularities( 1e-7 ));
3540   }
3541
3542   // set _hyp
3543   if ( data._hyps.size() == 1 )
3544   {
3545     eos._hyp = data._hyps.back();
3546   }
3547   else
3548   {
3549     // compute average StdMeshers_ViscousLayers parameters
3550     map< TGeomID, const StdMeshers_ViscousLayers* >::iterator f2hyp;
3551     if ( eos.ShapeType() == TopAbs_FACE )
3552     {
3553       if (( f2hyp = data._face2hyp.find( eos._shapeID )) != data._face2hyp.end() )
3554         eos._hyp = f2hyp->second;
3555     }
3556     else
3557     {
3558       PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
3559       while ( const TopoDS_Shape* face = fIt->next() )
3560       {
3561         TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
3562         if (( f2hyp = data._face2hyp.find( faceID )) != data._face2hyp.end() )
3563           eos._hyp.Add( f2hyp->second );
3564       }
3565     }
3566   }
3567
3568   // set _faceNormals
3569   if ( ! eos._hyp.UseSurfaceNormal() )
3570   {
3571     if ( eos.ShapeType() == TopAbs_FACE ) // get normals to elements on a FACE
3572     {
3573       SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
3574       if ( !smDS ) return;
3575       eos._faceNormals.reserve( smDS->NbElements() );
3576
3577       double oriFactor = helper.IsReversedSubMesh( TopoDS::Face( eos._shape )) ? 1.: -1.;
3578       SMDS_ElemIteratorPtr eIt = smDS->GetElements();
3579       for ( ; eIt->more(); )
3580       {
3581         const SMDS_MeshElement* face = eIt->next();
3582         gp_XYZ&                 norm = eos._faceNormals[face];
3583         if ( !SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
3584           norm.SetCoord( 0,0,0 );
3585         norm *= oriFactor;
3586       }
3587     }
3588     else // find EOS of adjacent FACEs
3589     {
3590       PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
3591       while ( const TopoDS_Shape* face = fIt->next() )
3592       {
3593         TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
3594         eos._faceEOS.push_back( & data._edgesOnShape[ faceID ]);
3595         if ( eos._faceEOS.back()->_shape.IsNull() )
3596           // avoid using uninitialised _shapeID in GetNormal()
3597           eos._faceEOS.back()->_shapeID = faceID;
3598       }
3599     }
3600   }
3601 }
3602
3603 //================================================================================
3604 /*!
3605  * \brief Returns normal of a face
3606  */
3607 //================================================================================
3608
3609 bool _EdgesOnShape::GetNormal( const SMDS_MeshElement* face, gp_Vec& norm )
3610 {
3611   bool ok = false;
3612   _EdgesOnShape* eos = 0;
3613
3614   if ( face->getshapeId() == _shapeID )
3615   {
3616     eos = this;
3617   }
3618   else
3619   {
3620     for ( size_t iF = 0; iF < _faceEOS.size() && !eos; ++iF )
3621       if ( face->getshapeId() == _faceEOS[ iF ]->_shapeID )
3622         eos = _faceEOS[ iF ];
3623   }
3624
3625   if (( eos ) &&
3626       ( ok = ( eos->_faceNormals.count( face ) )))
3627   {
3628     norm = eos->_faceNormals[ face ];
3629   }
3630   else if ( !eos )
3631   {
3632     debugMsg( "_EdgesOnShape::Normal() failed for face "<<face->GetID()
3633               << " on _shape #" << _shapeID );
3634   }
3635   return ok;
3636 }
3637
3638 //================================================================================
3639 /*!
3640  * \brief EdgesOnShape destructor
3641  */
3642 //================================================================================
3643
3644 _EdgesOnShape::~_EdgesOnShape()
3645 {
3646   delete _edgeSmoother;
3647   delete _mapper2D;
3648 }
3649
3650 //================================================================================
3651 /*!
3652  * \brief Set data of _LayerEdge needed for smoothing
3653  */
3654 //================================================================================
3655
3656 bool _ViscousBuilder::setEdgeData(_LayerEdge&         edge,
3657                                   _EdgesOnShape&      eos,
3658                                   SMESH_MesherHelper& helper,
3659                                   _SolidData&         data)
3660 {
3661   const SMDS_MeshNode* node = edge._nodes[0]; // source node
3662
3663   edge._len          = 0;
3664   edge._maxLen       = Precision::Infinite();
3665   edge._minAngle     = 0;
3666   edge._2neibors     = 0;
3667   edge._curvature    = 0;
3668   edge._flags        = 0;
3669   edge._smooFunction = 0;
3670
3671   // --------------------------
3672   // Compute _normal and _cosin
3673   // --------------------------
3674
3675   edge._cosin     = 0;
3676   edge._lenFactor = 1.;
3677   edge._normal.SetCoord(0,0,0);
3678   _Simplex::GetSimplices( node, edge._simplices, data._ignoreFaceIds, &data );
3679
3680   int totalNbFaces = 0;
3681   TopoDS_Face F;
3682   std::pair< TopoDS_Face, gp_XYZ > face2Norm[20];
3683   gp_Vec geomNorm;
3684   bool normOK = true;
3685
3686   const bool onShrinkShape = !eos._sWOL.IsNull();
3687   const bool useGeometry   = (( eos._hyp.UseSurfaceNormal() ) ||
3688                               ( eos.ShapeType() != TopAbs_FACE /*&& !onShrinkShape*/ ));
3689
3690   // get geom FACEs the node lies on
3691   //if ( useGeometry )
3692   {
3693     set<TGeomID> faceIds;
3694     if  ( eos.ShapeType() == TopAbs_FACE )
3695     {
3696       faceIds.insert( eos._shapeID );
3697     }
3698     else
3699     {
3700       SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
3701       while ( fIt->more() )
3702         faceIds.insert( fIt->next()->getshapeId() );
3703     }
3704     set<TGeomID>::iterator id = faceIds.begin();
3705     for ( ; id != faceIds.end(); ++id )
3706     {
3707       const TopoDS_Shape& s = getMeshDS()->IndexToShape( *id );
3708       if ( s.IsNull() || s.ShapeType() != TopAbs_FACE || data._ignoreFaceIds.count( *id ))
3709         continue;
3710       F = TopoDS::Face( s );
3711       face2Norm[ totalNbFaces ].first = F;
3712       totalNbFaces++;
3713     }
3714   }
3715
3716   // find _normal
3717   bool fromVonF = false;
3718   if ( useGeometry )
3719   {
3720     fromVonF = ( eos.ShapeType() == TopAbs_VERTEX &&
3721                  eos.SWOLType()  == TopAbs_FACE  &&
3722                  totalNbFaces > 1 );
3723
3724     if ( onShrinkShape && !fromVonF ) // one of faces the node is on has no layers
3725     {
3726       if ( eos.SWOLType() == TopAbs_EDGE )
3727       {
3728         // inflate from VERTEX along EDGE
3729         edge._normal = getEdgeDir( TopoDS::Edge( eos._sWOL ), TopoDS::Vertex( eos._shape ),
3730                                    eos._hyp.Get1stLayerThickness(), &eos._isRegularSWOL );
3731       }
3732       else if ( eos.ShapeType() == TopAbs_VERTEX )
3733       {
3734         // inflate from VERTEX along FACE
3735         edge._normal = getFaceDir( TopoDS::Face( eos._sWOL ), TopoDS::Vertex( eos._shape ),
3736                                    node, helper, normOK/*, &edge._cosin*/);
3737       }
3738       else
3739       {
3740         // inflate from EDGE along FACE
3741         edge._normal = getFaceDir( TopoDS::Face( eos._sWOL ), TopoDS::Edge( eos._shape ),
3742                                    node, helper, normOK);
3743       }
3744     }
3745     else // layers are on all FACEs of SOLID the node is on (or fromVonF)
3746     {
3747       if ( fromVonF )
3748         face2Norm[ totalNbFaces++ ].first = TopoDS::Face( eos._sWOL );
3749
3750       int nbOkNorms = 0;
3751       for ( int iF = totalNbFaces - 1; iF >= 0; --iF )
3752       {
3753         F = face2Norm[ iF ].first;
3754         geomNorm = getFaceNormal( node, F, helper, normOK );
3755         if ( !normOK ) continue;
3756         nbOkNorms++;
3757
3758         if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3759           geomNorm.Reverse();
3760         face2Norm[ iF ].second = geomNorm.XYZ();
3761         edge._normal += geomNorm.XYZ();
3762       }
3763       if ( nbOkNorms == 0 )
3764         return error(SMESH_Comment("Can't get normal to node ") << node->GetID(), data._index);
3765
3766       if ( totalNbFaces >= 3 )
3767       {
3768         edge._normal = getNormalByOffset( &edge, face2Norm, totalNbFaces, fromVonF );
3769       }
3770
3771       if ( edge._normal.Modulus() < 1e-3 && nbOkNorms > 1 )
3772       {
3773         // opposite normals, re-get normals at shifted positions (IPAL 52426)
3774         edge._normal.SetCoord( 0,0,0 );
3775         for ( int iF = 0; iF < totalNbFaces - fromVonF; ++iF )
3776         {
3777           const TopoDS_Face& F = face2Norm[iF].first;
3778           geomNorm = getFaceNormal( node, F, helper, normOK, /*shiftInside=*/true );
3779           if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3780             geomNorm.Reverse();
3781           if ( normOK )
3782             face2Norm[ iF ].second = geomNorm.XYZ();
3783           edge._normal += face2Norm[ iF ].second;
3784         }
3785       }
3786     }
3787   }
3788   else // !useGeometry - get _normal using surrounding mesh faces
3789   {
3790     edge._normal = getWeigthedNormal( &edge );
3791
3792     // set<TGeomID> faceIds;
3793     //
3794     // SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
3795     // while ( fIt->more() )
3796     // {
3797     //   const SMDS_MeshElement* face = fIt->next();
3798     //   if ( eos.GetNormal( face, geomNorm ))
3799     //   {
3800     //     if ( onShrinkShape && !faceIds.insert( face->getshapeId() ).second )
3801     //       continue; // use only one mesh face on FACE
3802     //     edge._normal += geomNorm.XYZ();
3803     //     totalNbFaces++;
3804     //   }
3805     // }
3806   }
3807
3808   // compute _cosin
3809   //if ( eos._hyp.UseSurfaceNormal() )
3810   {
3811     switch ( eos.ShapeType() )
3812     {
3813     case TopAbs_FACE: {
3814       edge._cosin = 0;
3815       break;
3816     }
3817     case TopAbs_EDGE: {
3818       TopoDS_Edge E    = TopoDS::Edge( eos._shape );
3819       gp_Vec inFaceDir = getFaceDir( F, E, node, helper, normOK );
3820       double angle     = inFaceDir.Angle( edge._normal ); // [0,PI]
3821       edge._cosin      = Cos( angle );
3822       break;
3823     }
3824     case TopAbs_VERTEX: {
3825       TopoDS_Vertex V  = TopoDS::Vertex( eos._shape );
3826       gp_Vec inFaceDir = getFaceDir( face2Norm[0].first , V, node, helper, normOK );
3827       double angle     = inFaceDir.Angle( edge._normal ); // [0,PI]
3828       edge._cosin      = Cos( angle );
3829       if ( fromVonF )
3830         totalNbFaces--;
3831       if ( totalNbFaces > 1 || helper.IsSeamShape( node->getshapeId() ))
3832         for ( int iF = 1; iF < totalNbFaces; ++iF )
3833         {
3834           F = face2Norm[ iF ].first;
3835           inFaceDir = getFaceDir( F, V, node, helper, normOK=true );
3836           if ( normOK ) {
3837             if ( onShrinkShape )
3838             {
3839               gp_Vec faceNorm = getFaceNormal( node, F, helper, normOK );
3840               if ( !normOK ) continue;
3841               if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3842                 faceNorm.Reverse();
3843               angle = 0.5 * M_PI - faceNorm.Angle( edge._normal );
3844               if ( inFaceDir * edge._normal < 0 )
3845                 angle = M_PI - angle;
3846             }
3847             else
3848             {
3849               angle = inFaceDir.Angle( edge._normal );
3850             }
3851             double cosin = Cos( angle );
3852             if ( Abs( cosin ) > Abs( edge._cosin ))
3853               edge._cosin = cosin;
3854           }
3855         }
3856       break;
3857     }
3858     default:
3859       return error(SMESH_Comment("Invalid shape position of node ")<<node, data._index);
3860     }
3861   }
3862
3863   double normSize = edge._normal.SquareModulus();
3864   if ( normSize < numeric_limits<double>::min() )
3865     return error(SMESH_Comment("Bad normal at node ")<< node->GetID(), data._index );
3866
3867   edge._normal /= sqrt( normSize );
3868
3869   if ( edge.Is( _LayerEdge::MULTI_NORMAL ) && edge._nodes.size() == 2 )
3870   {
3871     getMeshDS()->RemoveFreeNode( edge._nodes.back(), 0, /*fromGroups=*/false );
3872     edge._nodes.resize( 1 );
3873     edge._normal.SetCoord( 0,0,0 );
3874     edge.SetMaxLen( 0 );
3875   }
3876
3877   // Set the rest data
3878   // --------------------
3879
3880   double realLenFactor = edge.SetCosin( edge._cosin ); // to update edge._lenFactor
3881   // if ( realLenFactor > 3 )
3882   // {
3883   //   edge._cosin = 1;
3884   //   if ( onShrinkShape )
3885   //   {
3886   //     edge.Set( _LayerEdge::RISKY_SWOL );
3887   //     edge._lenFactor = 2;
3888   //   }
3889   //   else
3890   //   {
3891   //     edge._lenFactor = 1;
3892   //   }
3893   // }
3894
3895   if ( onShrinkShape )
3896   {
3897     const SMDS_MeshNode* tgtNode = edge._nodes.back();
3898     if ( SMESHDS_SubMesh* sm = getMeshDS()->MeshElements( data._solid ))
3899       sm->RemoveNode( tgtNode );
3900
3901     // set initial position which is parameters on _sWOL in this case
3902     if ( eos.SWOLType() == TopAbs_EDGE )
3903     {
3904       double u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), node, 0, &normOK );
3905       edge._pos.push_back( gp_XYZ( u, 0, 0 ));
3906       if ( edge._nodes.size() > 1 )
3907         getMeshDS()->SetNodeOnEdge( tgtNode, TopoDS::Edge( eos._sWOL ), u );
3908     }
3909     else // eos.SWOLType() == TopAbs_FACE
3910     {
3911       gp_XY uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), node, 0, &normOK );
3912       edge._pos.push_back( gp_XYZ( uv.X(), uv.Y(), 0));
3913       if ( edge._nodes.size() > 1 )
3914         getMeshDS()->SetNodeOnFace( tgtNode, TopoDS::Face( eos._sWOL ), uv.X(), uv.Y() );
3915     }
3916
3917     //if ( edge._nodes.size() > 1 ) -- allow RISKY_SWOL on noShrink shape
3918     {
3919       // check if an angle between a FACE with layers and SWOL is sharp,
3920       // else the edge should not inflate
3921       F.Nullify();
3922       for ( int iF = 0; iF < totalNbFaces  &&  F.IsNull();  ++iF ) // find a FACE with VL
3923         if ( ! helper.IsSubShape( eos._sWOL, face2Norm[iF].first ))
3924           F = face2Norm[iF].first;
3925       if ( !F.IsNull())
3926       {
3927         geomNorm = getFaceNormal( node, F, helper, normOK );
3928         if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3929           geomNorm.Reverse(); // inside the SOLID
3930         if ( geomNorm * edge._normal < -0.001 )
3931         {
3932           if ( edge._nodes.size() > 1 )
3933           {
3934             getMeshDS()->RemoveFreeNode( tgtNode, 0, /*fromGroups=*/false );
3935             edge._nodes.resize( 1 );
3936           }
3937         }
3938         else if ( realLenFactor > 3 ) ///  -- moved to SetCosin()
3939           //else if ( edge._lenFactor > 3 )
3940         {
3941           edge._lenFactor = 2;
3942           edge.Set( _LayerEdge::RISKY_SWOL );
3943         }
3944       }
3945     }
3946   }
3947   else
3948   {
3949     edge._pos.push_back( SMESH_TNodeXYZ( node ));
3950
3951     if ( eos.ShapeType() == TopAbs_FACE )
3952     {
3953       double angle;
3954       for ( size_t i = 0; i < edge._simplices.size(); ++i )
3955       {
3956         edge._simplices[i].IsMinAngleOK( edge._pos.back(), angle );
3957         edge._minAngle = Max( edge._minAngle, angle ); // "angle" is actually cosine
3958       }
3959     }
3960   }
3961
3962   // Set neighbor nodes for a _LayerEdge based on EDGE
3963
3964   if ( eos.ShapeType() == TopAbs_EDGE /*||
3965        ( onShrinkShape && posType == SMDS_TOP_VERTEX && fabs( edge._cosin ) < 1e-10 )*/)
3966   {
3967     edge._2neibors = _Factory::NewNearEdges();
3968     // target nodes instead of source ones will be set later
3969   }
3970
3971   return true;
3972 }
3973
3974 //================================================================================
3975 /*!
3976  * \brief Return normal to a FACE at a node
3977  *  \param [in] n - node
3978  *  \param [in] face - FACE
3979  *  \param [in] helper - helper
3980  *  \param [out] isOK - true or false
3981  *  \param [in] shiftInside - to find normal at a position shifted inside the face
3982  *  \return gp_XYZ - normal
3983  */
3984 //================================================================================
3985
3986 gp_XYZ _ViscousBuilder::getFaceNormal(const SMDS_MeshNode* node,
3987                                       const TopoDS_Face&   face,
3988                                       SMESH_MesherHelper&  helper,
3989                                       bool&                isOK,
3990                                       bool                 shiftInside)
3991 {
3992   gp_XY uv;
3993   if ( shiftInside )
3994   {
3995     // get a shifted position
3996     gp_Pnt p = SMESH_TNodeXYZ( node );
3997     gp_XYZ shift( 0,0,0 );
3998     TopoDS_Shape S = helper.GetSubShapeByNode( node, helper.GetMeshDS() );
3999     switch ( S.ShapeType() ) {
4000     case TopAbs_VERTEX:
4001     {
4002       shift = getFaceDir( face, TopoDS::Vertex( S ), node, helper, isOK );
4003       break;
4004     }
4005     case TopAbs_EDGE:
4006     {
4007       shift = getFaceDir( face, TopoDS::Edge( S ), node, helper, isOK );
4008       break;
4009     }
4010     default:
4011       isOK = false;
4012     }
4013     if ( isOK )
4014       shift.Normalize();
4015     p.Translate( shift * 1e-5 );
4016
4017     TopLoc_Location loc;
4018     GeomAPI_ProjectPointOnSurf& projector = helper.GetProjector( face, loc, 1e-7 );
4019
4020     if ( !loc.IsIdentity() ) p.Transform( loc.Transformation().Inverted() );
4021     
4022     projector.Perform( p );
4023     if ( !projector.IsDone() || projector.NbPoints() < 1 )
4024     {
4025       isOK = false;
4026       return p.XYZ();
4027     }
4028     Standard_Real U,V;
4029     projector.LowerDistanceParameters(U,V);
4030     uv.SetCoord( U,V );
4031   }
4032   else
4033   {
4034     uv = helper.GetNodeUV( face, node, 0, &isOK );
4035   }
4036
4037   gp_Dir normal;
4038   isOK = false;
4039
4040   Handle(Geom_Surface) surface = BRep_Tool::Surface( face );
4041
4042   if ( !shiftInside &&
4043        helper.IsDegenShape( node->getshapeId() ) &&
4044        getFaceNormalAtSingularity( uv, face, helper, normal ))
4045   {
4046     isOK = true;
4047     return normal.XYZ();
4048   }
4049
4050   int pointKind = GeomLib::NormEstim( surface, uv, 1e-5, normal );
4051   enum { REGULAR = 0, QUASYSINGULAR, CONICAL, IMPOSSIBLE };
4052
4053   if ( pointKind == IMPOSSIBLE &&
4054        node->GetPosition()->GetDim() == 2 ) // node inside the FACE
4055   {
4056     // probably NormEstim() failed due to a too high tolerance
4057     pointKind = GeomLib::NormEstim( surface, uv, 1e-20, normal );
4058     isOK = ( pointKind < IMPOSSIBLE );
4059   }
4060   if ( pointKind < IMPOSSIBLE )
4061   {
4062     if ( pointKind != REGULAR &&
4063          !shiftInside &&
4064          node->GetPosition()->GetDim() < 2 ) // FACE boundary
4065     {
4066       gp_XYZ normShift = getFaceNormal( node, face, helper, isOK, /*shiftInside=*/true );
4067       if ( normShift * normal.XYZ() < 0. )
4068         normal = normShift;
4069     }
4070     isOK = true;
4071   }
4072
4073   if ( !isOK ) // hard singularity, to call with shiftInside=true ?
4074   {
4075     const TGeomID faceID = helper.GetMeshDS()->ShapeToIndex( face );
4076
4077     SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
4078     while ( fIt->more() )
4079     {
4080       const SMDS_MeshElement* f = fIt->next();
4081       if ( f->getshapeId() == faceID )
4082       {
4083         isOK = SMESH_MeshAlgos::FaceNormal( f, (gp_XYZ&) normal.XYZ(), /*normalized=*/true );
4084         if ( isOK )
4085         {
4086           TopoDS_Face ff = face;
4087           ff.Orientation( TopAbs_FORWARD );
4088           if ( helper.IsReversedSubMesh( ff ))
4089             normal.Reverse();
4090           break;
4091         }
4092       }
4093     }
4094   }
4095   return normal.XYZ();
4096 }
4097
4098 //================================================================================
4099 /*!
4100  * \brief Try to get normal at a singularity of a surface basing on it's nature
4101  */
4102 //================================================================================
4103
4104 bool _ViscousBuilder::getFaceNormalAtSingularity( const gp_XY&        uv,
4105                                                   const TopoDS_Face&  face,
4106                                                   SMESH_MesherHelper& /*helper*/,
4107                                                   gp_Dir&             normal )
4108 {
4109   BRepAdaptor_Surface surface( face );
4110   gp_Dir axis;
4111   if ( !getRovolutionAxis( surface, axis ))
4112     return false;
4113
4114   double f,l, d, du, dv;
4115   f = surface.FirstUParameter();
4116   l = surface.LastUParameter();
4117   d = ( uv.X() - f ) / ( l - f );
4118   du = ( d < 0.5 ? +1. : -1 ) * 1e-5 * ( l - f );
4119   f = surface.FirstVParameter();
4120   l = surface.LastVParameter();
4121   d = ( uv.Y() - f ) / ( l - f );
4122   dv = ( d < 0.5 ? +1. : -1 ) * 1e-5 * ( l - f );
4123
4124   gp_Dir refDir;
4125   gp_Pnt2d testUV = uv;
4126   enum { REGULAR = 0, QUASYSINGULAR, CONICAL, IMPOSSIBLE };
4127   double tol = 1e-5;
4128   Handle(Geom_Surface) geomsurf = surface.Surface().Surface();
4129   for ( int iLoop = 0; true ; ++iLoop )
4130   {
4131     testUV.SetCoord( testUV.X() + du, testUV.Y() + dv );
4132     if ( GeomLib::NormEstim( geomsurf, testUV, tol, refDir ) == REGULAR )
4133       break;
4134     if ( iLoop > 20 )
4135       return false;
4136     tol /= 10.;
4137   }
4138
4139   if ( axis * refDir < 0. )
4140     axis.Reverse();
4141
4142   normal = axis;
4143
4144   return true;
4145 }
4146
4147 //================================================================================
4148 /*!
4149  * \brief Return a normal at a node weighted with angles taken by faces
4150  */
4151 //================================================================================
4152
4153 gp_XYZ _ViscousBuilder::getWeigthedNormal( const _LayerEdge* edge )
4154 {
4155   const SMDS_MeshNode* n = edge->_nodes[0];
4156
4157   gp_XYZ resNorm(0,0,0);
4158   SMESH_TNodeXYZ p0( n ), pP, pN;
4159   for ( size_t i = 0; i < edge->_simplices.size(); ++i )
4160   {
4161     pP.Set( edge->_simplices[i]._nPrev );
4162     pN.Set( edge->_simplices[i]._nNext );
4163     gp_Vec v0P( p0, pP ), v0N( p0, pN ), vPN( pP, pN ), norm = v0P ^ v0N;
4164     double l0P = v0P.SquareMagnitude();
4165     double l0N = v0N.SquareMagnitude();
4166     double lPN = vPN.SquareMagnitude();
4167     if ( l0P < std::numeric_limits<double>::min() ||
4168          l0N < std::numeric_limits<double>::min() ||
4169          lPN < std::numeric_limits<double>::min() )
4170       continue;
4171     double lNorm = norm.SquareMagnitude();
4172     double  sin2 = lNorm / l0P / l0N;
4173     double angle = ACos(( v0P * v0N ) / Sqrt( l0P ) / Sqrt( l0N ));
4174
4175     double weight = sin2 * angle / lPN;
4176     resNorm += weight * norm.XYZ() / Sqrt( lNorm );
4177   }
4178
4179   return resNorm;
4180 }
4181
4182 //================================================================================
4183 /*!
4184  * \brief Return a normal at a node by getting a common point of offset planes
4185  *        defined by the FACE normals
4186  */
4187 //================================================================================
4188
4189 gp_XYZ _ViscousBuilder::getNormalByOffset( _LayerEdge*                      edge,
4190                                            std::pair< TopoDS_Face, gp_XYZ > f2Normal[],
4191                                            int                              nbFaces,
4192                                            bool                             lastNoOffset)
4193 {
4194   SMESH_TNodeXYZ p0 = edge->_nodes[0];
4195
4196   gp_XYZ resNorm(0,0,0);
4197   TopoDS_Shape V = SMESH_MesherHelper::GetSubShapeByNode( p0._node, getMeshDS() );
4198   if ( V.ShapeType() != TopAbs_VERTEX || nbFaces < 3 )
4199   {
4200     for ( int i = 0; i < nbFaces; ++i )
4201       resNorm += f2Normal[i].second;
4202     return resNorm;
4203   }
4204
4205   // prepare _OffsetPlane's
4206   vector< _OffsetPlane > pln( nbFaces );
4207   for ( int i = 0; i < nbFaces - lastNoOffset; ++i )
4208   {
4209     pln[i]._faceIndex = i;
4210     pln[i]._plane = gp_Pln( p0 + f2Normal[i].second, f2Normal[i].second );
4211   }
4212   if ( lastNoOffset )
4213   {
4214     pln[ nbFaces - 1 ]._faceIndex = nbFaces - 1;
4215     pln[ nbFaces - 1 ]._plane = gp_Pln( p0, f2Normal[ nbFaces - 1 ].second );
4216   }
4217
4218   // intersect neighboring OffsetPlane's
4219   PShapeIteratorPtr edgeIt = SMESH_MesherHelper::GetAncestors( V, *_mesh, TopAbs_EDGE );
4220   while ( const TopoDS_Shape* edge = edgeIt->next() )
4221   {
4222     int f1 = -1, f2 = -1;
4223     for ( int i = 0; i < nbFaces &&  f2 < 0;  ++i )
4224       if ( SMESH_MesherHelper::IsSubShape( *edge, f2Normal[i].first ))
4225         (( f1 < 0 ) ? f1 : f2 ) = i;
4226
4227     if ( f2 >= 0 )
4228       pln[ f1 ].ComputeIntersectionLine( pln[ f2 ], TopoDS::Edge( *edge ), TopoDS::Vertex( V ));
4229   }
4230
4231   // get a common point
4232   gp_XYZ commonPnt( 0, 0, 0 );
4233   int nbPoints = 0;
4234   bool isPointFound;
4235   for ( int i = 0; i < nbFaces; ++i )
4236   {
4237     commonPnt += pln[ i ].GetCommonPoint( isPointFound, TopoDS::Vertex( V ));
4238     nbPoints  += isPointFound;
4239   }
4240   gp_XYZ wgtNorm = getWeigthedNormal( edge );
4241   if ( nbPoints == 0 )
4242     return wgtNorm;
4243
4244   commonPnt /= nbPoints;
4245   resNorm = commonPnt - p0;
4246   if ( lastNoOffset )
4247     return resNorm;
4248
4249   // choose the best among resNorm and wgtNorm
4250   resNorm.Normalize();
4251   wgtNorm.Normalize();
4252   double resMinDot = std::numeric_limits<double>::max();
4253   double wgtMinDot = std::numeric_limits<double>::max();
4254   for ( int i = 0; i < nbFaces - lastNoOffset; ++i )
4255   {
4256     resMinDot = Min( resMinDot, resNorm * f2Normal[i].second );
4257     wgtMinDot = Min( wgtMinDot, wgtNorm * f2Normal[i].second );
4258   }
4259
4260   if ( Max( resMinDot, wgtMinDot ) < theMinSmoothCosin )
4261   {
4262     edge->Set( _LayerEdge::MULTI_NORMAL );
4263   }
4264
4265   return ( resMinDot > wgtMinDot ) ? resNorm : wgtNorm;
4266 }
4267
4268 //================================================================================
4269 /*!
4270  * \brief Compute line of intersection of 2 planes
4271  */
4272 //================================================================================
4273
4274 void _OffsetPlane::ComputeIntersectionLine( _OffsetPlane&        pln,
4275                                             const TopoDS_Edge&   E,
4276                                             const TopoDS_Vertex& V )
4277 {
4278   int iNext = bool( _faceIndexNext[0] >= 0 );
4279   _faceIndexNext[ iNext ] = pln._faceIndex;
4280
4281   gp_XYZ n1 = _plane.Axis().Direction().XYZ();
4282   gp_XYZ n2 = pln._plane.Axis().Direction().XYZ();
4283
4284   gp_XYZ lineDir = n1 ^ n2;
4285
4286   double x = Abs( lineDir.X() );
4287   double y = Abs( lineDir.Y() );
4288   double z = Abs( lineDir.Z() );
4289
4290   int cooMax; // max coordinate
4291   if (x > y) {
4292     if (x > z) cooMax = 1;
4293     else       cooMax = 3;
4294   }
4295   else {
4296     if (y > z) cooMax = 2;
4297     else       cooMax = 3;
4298   }
4299
4300   gp_Pnt linePos;
4301   if ( Abs( lineDir.Coord( cooMax )) < 0.05 )
4302   {
4303     // parallel planes - intersection is an offset of the common EDGE
4304     gp_Pnt p = BRep_Tool::Pnt( V );
4305     linePos  = 0.5 * (( p.XYZ() + n1 ) + ( p.XYZ() + n2 ));
4306     lineDir  = getEdgeDir( E, V, 0.1 * SMESH_Algo::EdgeLength( E ));
4307   }
4308   else
4309   {
4310     // the constants in the 2 plane equations
4311     double d1 = - ( _plane.Axis().Direction().XYZ()     * _plane.Location().XYZ() );
4312     double d2 = - ( pln._plane.Axis().Direction().XYZ() * pln._plane.Location().XYZ() );
4313
4314     switch ( cooMax ) {
4315     case 1:
4316       linePos.SetX(  0 );
4317       linePos.SetY(( d2*n1.Z() - d1*n2.Z()) / lineDir.X() );
4318       linePos.SetZ(( d1*n2.Y() - d2*n1.Y()) / lineDir.X() );
4319       break;
4320     case 2:
4321       linePos.SetX(( d1*n2.Z() - d2*n1.Z()) / lineDir.Y() );
4322       linePos.SetY(  0 );
4323       linePos.SetZ(( d2*n1.X() - d1*n2.X()) / lineDir.Y() );
4324       break;
4325     case 3:
4326       linePos.SetX(( d2*n1.Y() - d1*n2.Y()) / lineDir.Z() );
4327       linePos.SetY(( d1*n2.X() - d2*n1.X()) / lineDir.Z() );
4328       linePos.SetZ(  0 );
4329     }
4330   }
4331   gp_Lin& line = _lines[ iNext ];
4332   line.SetDirection( lineDir );
4333   line.SetLocation ( linePos );
4334
4335   _isLineOK[ iNext ] = true;
4336
4337
4338   iNext = bool( pln._faceIndexNext[0] >= 0 );
4339   pln._lines        [ iNext ] = line;
4340   pln._faceIndexNext[ iNext ] = this->_faceIndex;
4341   pln._isLineOK     [ iNext ] = true;
4342 }
4343
4344 //================================================================================
4345 /*!
4346  * \brief Computes intersection point of two _lines
4347  */
4348 //================================================================================
4349
4350 gp_XYZ _OffsetPlane::GetCommonPoint(bool&                 isFound,
4351                                     const TopoDS_Vertex & V) const
4352 {
4353   gp_XYZ p( 0,0,0 );
4354   isFound = false;
4355
4356   if ( NbLines() == 2 )
4357   {
4358     gp_Vec lPerp0 = _lines[0].Direction().XYZ() ^ _plane.Axis().Direction().XYZ();
4359     double  dot01 = lPerp0 * _lines[1].Direction().XYZ();
4360     if ( Abs( dot01 ) > 0.05 )
4361     {
4362       gp_Vec l0l1 = _lines[1].Location().XYZ() - _lines[0].Location().XYZ();
4363       double   u1 = - ( lPerp0 * l0l1 ) / dot01;
4364       p = ( _lines[1].Location().XYZ() + _lines[1].Direction().XYZ() * u1 );
4365       isFound = true;
4366     }
4367     else
4368     {
4369       gp_Pnt  pV ( BRep_Tool::Pnt( V ));
4370       gp_Vec  lv0( _lines[0].Location(), pV    ),  lv1(_lines[1].Location(), pV     );
4371       double dot0( lv0 * _lines[0].Direction() ), dot1( lv1 * _lines[1].Direction() );
4372       p += 0.5 * ( _lines[0].Location().XYZ() + _lines[0].Direction().XYZ() * dot0 );
4373       p += 0.5 * ( _lines[1].Location().XYZ() + _lines[1].Direction().XYZ() * dot1 );
4374       isFound = true;
4375     }
4376   }
4377
4378   return p;
4379 }
4380
4381 //================================================================================
4382 /*!
4383  * \brief Find 2 neighbor nodes of a node on EDGE
4384  */
4385 //================================================================================
4386
4387 bool _ViscousBuilder::findNeiborsOnEdge(const _LayerEdge*     edge,
4388                                         const SMDS_MeshNode*& n1,
4389                                         const SMDS_MeshNode*& n2,
4390                                         _EdgesOnShape&        eos,
4391                                         _SolidData&           data)
4392 {
4393   const SMDS_MeshNode* node = edge->_nodes[0];
4394   const int        shapeInd = eos._shapeID;
4395   SMESHDS_SubMesh*   edgeSM = 0;
4396   if ( eos.ShapeType() == TopAbs_EDGE )
4397   {
4398     edgeSM = eos._subMesh->GetSubMeshDS();
4399     if ( !edgeSM || edgeSM->NbElements() == 0 )
4400       return error(SMESH_Comment("Not meshed EDGE ") << shapeInd, data._index);
4401   }
4402   int iN = 0;
4403   n2 = 0;
4404   SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Edge);
4405   while ( eIt->more() && !n2 )
4406   {
4407     const SMDS_MeshElement* e = eIt->next();
4408     const SMDS_MeshNode*   nNeibor = e->GetNode( 0 );
4409     if ( nNeibor == node ) nNeibor = e->GetNode( 1 );
4410     if ( edgeSM )
4411     {
4412       if (!edgeSM->Contains(e)) continue;
4413     }
4414     else
4415     {
4416       TopoDS_Shape s = SMESH_MesherHelper::GetSubShapeByNode( nNeibor, getMeshDS() );
4417       if ( !SMESH_MesherHelper::IsSubShape( s, eos._sWOL )) continue;
4418     }
4419     ( iN++ ? n2 : n1 ) = nNeibor;
4420   }
4421   if ( !n2 )
4422     return error(SMESH_Comment("Wrongly meshed EDGE ") << shapeInd, data._index);
4423   return true;
4424 }
4425
4426 //================================================================================
4427 /*!
4428  * \brief Create _Curvature
4429  */
4430 //================================================================================
4431
4432 _Curvature* _Curvature::New( double avgNormProj, double avgDist )
4433 {
4434   // double   _r; // radius
4435   // double   _k; // factor to correct node smoothed position
4436   // double   _h2lenRatio; // avgNormProj / (2*avgDist)
4437   // gp_Pnt2d _uv; // UV used in putOnOffsetSurface()
4438
4439   _Curvature* c = 0;
4440   if ( fabs( avgNormProj / avgDist ) > 1./200 )
4441   {
4442     c = _Factory::NewCurvature();
4443     c->_r = avgDist * avgDist / avgNormProj;
4444     c->_k = avgDist * avgDist / c->_r / c->_r;
4445     //c->_k = avgNormProj / c->_r;
4446     c->_k *= ( c->_r < 0 ? 1/1.1 : 1.1 ); // not to be too restrictive
4447     c->_h2lenRatio = avgNormProj / ( avgDist + avgDist );
4448
4449     c->_uv.SetCoord( 0., 0. );
4450   }
4451   return c;
4452 }
4453
4454 //================================================================================
4455 /*!
4456  * \brief Set _curvature and _2neibors->_plnNorm by 2 neighbor nodes residing the same EDGE
4457  */
4458 //================================================================================
4459
4460 void _LayerEdge::SetDataByNeighbors( const SMDS_MeshNode* n1,
4461                                      const SMDS_MeshNode* n2,
4462                                      const _EdgesOnShape& eos,
4463                                      SMESH_MesherHelper&  helper)
4464 {
4465   if ( eos.ShapeType() != TopAbs_EDGE )
4466     return;
4467   if ( _curvature && Is( SMOOTHED_C1 ))
4468     return;
4469
4470   gp_XYZ  pos = SMESH_TNodeXYZ( _nodes[0] );
4471   gp_XYZ vec1 = pos - SMESH_TNodeXYZ( n1 );
4472   gp_XYZ vec2 = pos - SMESH_TNodeXYZ( n2 );
4473
4474   // Set _curvature
4475
4476   double      sumLen = vec1.Modulus() + vec2.Modulus();
4477   _2neibors->_wgt[0] = 1 - vec1.Modulus() / sumLen;
4478   _2neibors->_wgt[1] = 1 - vec2.Modulus() / sumLen;
4479   double avgNormProj = 0.5 * ( _normal * vec1 + _normal * vec2 );
4480   double      avgLen = 0.5 * ( vec1.Modulus() + vec2.Modulus() );
4481   _curvature = _Curvature::New( avgNormProj, avgLen );
4482   // if ( _curvature )
4483   //   debugMsg( _nodes[0]->GetID()
4484   //             << " CURV r,k: " << _curvature->_r<<","<<_curvature->_k
4485   //             << " proj = "<<avgNormProj<< " len = " << avgLen << "| lenDelta(0) = "
4486   //             << _curvature->lenDelta(0) );
4487
4488   // Set _plnNorm
4489
4490   if ( eos._sWOL.IsNull() )
4491   {
4492     TopoDS_Edge  E = TopoDS::Edge( eos._shape );
4493     // if ( SMESH_Algo::isDegenerated( E ))
4494     //   return;
4495     gp_XYZ dirE    = getEdgeDir( E, _nodes[0], helper );
4496     gp_XYZ plnNorm = dirE ^ _normal;
4497     double proj0   = plnNorm * vec1;
4498     double proj1   = plnNorm * vec2;
4499     if ( fabs( proj0 ) > 1e-10 || fabs( proj1 ) > 1e-10 )
4500     {
4501       if ( _2neibors->_plnNorm ) delete _2neibors->_plnNorm;
4502       _2neibors->_plnNorm = new gp_XYZ( plnNorm.Normalized() );
4503     }
4504   }
4505 }
4506
4507 //================================================================================
4508 /*!
4509  * \brief Copy data from a _LayerEdge of other SOLID and based on the same node;
4510  * this and the other _LayerEdge are inflated along a FACE or an EDGE
4511  */
4512 //================================================================================
4513
4514 gp_XYZ _LayerEdge::Copy( _LayerEdge&         other,
4515                          _EdgesOnShape&      eos,
4516                          SMESH_MesherHelper& helper )
4517 {
4518   _nodes     = other._nodes;
4519   _normal    = other._normal;
4520   _len       = 0;
4521   _lenFactor = other._lenFactor;
4522   _cosin     = other._cosin;
4523   _2neibors  = other._2neibors;
4524   _curvature = other._curvature;
4525   _2neibors  = other._2neibors;
4526   _maxLen    = Precision::Infinite();//other._maxLen;
4527   _flags     = 0;
4528   _smooFunction = 0;
4529
4530   gp_XYZ lastPos( 0,0,0 );
4531   if ( eos.SWOLType() == TopAbs_EDGE )
4532   {
4533     double u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), _nodes[0] );
4534     _pos.push_back( gp_XYZ( u, 0, 0));
4535
4536     u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), _nodes.back() );
4537     lastPos.SetX( u );
4538   }
4539   else // TopAbs_FACE
4540   {
4541     gp_XY uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), _nodes[0]);
4542     _pos.push_back( gp_XYZ( uv.X(), uv.Y(), 0));
4543
4544     uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), _nodes.back() );
4545     lastPos.SetX( uv.X() );
4546     lastPos.SetY( uv.Y() );
4547   }
4548   return lastPos;
4549 }
4550
4551 //================================================================================
4552 /*!
4553  * \brief Set _cosin and _lenFactor
4554  */
4555 //================================================================================
4556
4557 double _LayerEdge::SetCosin( double cosin )
4558 {
4559   _cosin = cosin;
4560   cosin = Abs( _cosin );
4561   //_lenFactor = ( cosin < 1.-1e-12 ) ?  1./sqrt(1-cosin*cosin ) : 1.0;
4562   double realLenFactor;
4563   if ( cosin < 1.-1e-12 )
4564   {
4565     _lenFactor = realLenFactor = 1./sqrt(1-cosin*cosin );
4566   }
4567   else
4568   {
4569     _lenFactor = 1;
4570     realLenFactor = Precision::Infinite();
4571   }
4572
4573   return realLenFactor;
4574 }
4575
4576 //================================================================================
4577 /*!
4578  * \brief Check if another _LayerEdge is a neighbor on EDGE
4579  */
4580 //================================================================================
4581
4582 bool _LayerEdge::IsNeiborOnEdge( const _LayerEdge* edge ) const
4583 {
4584   return (( this->_2neibors && this->_2neibors->include( edge )) ||
4585           ( edge->_2neibors && edge->_2neibors->include( this )));
4586 }
4587
4588 //================================================================================
4589 /*!
4590  * \brief Fills a vector<_Simplex > 
4591  */
4592 //================================================================================
4593
4594 void _Simplex::GetSimplices( const SMDS_MeshNode* node,
4595                              vector<_Simplex>&    simplices,
4596                              const set<TGeomID>&  ingnoreShapes,
4597                              const _SolidData*    dataToCheckOri,
4598                              const bool           toSort)
4599 {
4600   simplices.clear();
4601   SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
4602   while ( fIt->more() )
4603   {
4604     const SMDS_MeshElement* f = fIt->next();
4605     const TGeomID    shapeInd = f->getshapeId();
4606     if ( ingnoreShapes.count( shapeInd )) continue;
4607     const int nbNodes = f->NbCornerNodes();
4608     const int  srcInd = f->GetNodeIndex( node );
4609     const SMDS_MeshNode* nPrev = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd-1, nbNodes ));
4610     const SMDS_MeshNode* nNext = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd+1, nbNodes ));
4611     const SMDS_MeshNode* nOpp  = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd+2, nbNodes ));
4612     if ( dataToCheckOri && dataToCheckOri->_reversedFaceIds.count( shapeInd ))
4613       std::swap( nPrev, nNext );
4614     simplices.push_back( _Simplex( nPrev, nNext, ( nbNodes == 3 ? 0 : nOpp )));
4615   }
4616
4617   if ( toSort )
4618     SortSimplices( simplices );
4619 }
4620
4621 //================================================================================
4622 /*!
4623  * \brief Set neighbor simplices side by side
4624  */
4625 //================================================================================
4626
4627 void _Simplex::SortSimplices(vector<_Simplex>& simplices)
4628 {
4629   vector<_Simplex> sortedSimplices( simplices.size() );
4630   sortedSimplices[0] = simplices[0];
4631   size_t nbFound = 0;
4632   for ( size_t i = 1; i < simplices.size(); ++i )
4633   {
4634     for ( size_t j = 1; j < simplices.size(); ++j )
4635       if ( sortedSimplices[i-1]._nNext == simplices[j]._nPrev )
4636       {
4637         sortedSimplices[i] = simplices[j];
4638         nbFound++;
4639         break;
4640       }
4641   }
4642   if ( nbFound == simplices.size() - 1 )
4643     simplices.swap( sortedSimplices );
4644 }
4645
4646 //================================================================================
4647 /*!
4648  * \brief DEBUG. Create groups containing temporary data of _LayerEdge's
4649  */
4650 //================================================================================
4651
4652 void _ViscousBuilder::makeGroupOfLE()
4653 {
4654   if (!SALOME::VerbosityActivated())
4655     return;
4656
4657   for ( size_t i = 0 ; i < _sdVec.size(); ++i )
4658   {
4659     if ( _sdVec[i]._n2eMap.empty() ) continue;
4660
4661     dumpFunction( SMESH_Comment("make_LayerEdge_") << i );
4662     TNode2Edge::iterator n2e;
4663     for ( n2e = _sdVec[i]._n2eMap.begin(); n2e != _sdVec[i]._n2eMap.end(); ++n2e )
4664     {
4665       _LayerEdge* le = n2e->second;
4666       // for ( size_t iN = 1; iN < le->_nodes.size(); ++iN )
4667       //   dumpCmd(SMESH_Comment("mesh.AddEdge([ ") <<le->_nodes[iN-1]->GetID()
4668       //           << ", " << le->_nodes[iN]->GetID() <<"])");
4669       if ( le ) {
4670         dumpCmd(SMESH_Comment("mesh.AddEdge([ ") <<le->_nodes[0]->GetID()
4671                 << ", " << le->_nodes.back()->GetID() <<"]) # " << le->_flags );
4672       }
4673     }
4674     dumpFunctionEnd();
4675
4676     dumpFunction( SMESH_Comment("makeNormals") << i );
4677     for ( n2e = _sdVec[i]._n2eMap.begin(); n2e != _sdVec[i]._n2eMap.end(); ++n2e )
4678     {
4679       _LayerEdge* edge = n2e->second;
4680       SMESH_TNodeXYZ nXYZ( edge->_nodes[0] );
4681       nXYZ += edge->_normal * _sdVec[i]._stepSize;
4682       dumpCmd(SMESH_Comment("mesh.AddEdge([ ") << edge->_nodes[0]->GetID()
4683               << ", mesh.AddNode( "<< nXYZ.X()<<","<< nXYZ.Y()<<","<< nXYZ.Z()<<")])");
4684     }
4685     dumpFunctionEnd();
4686
4687     dumpFunction( SMESH_Comment("makeTmpFaces_") << i );
4688     dumpCmd( "faceId1 = mesh.NbElements()" );
4689     TopExp_Explorer fExp( _sdVec[i]._solid, TopAbs_FACE );
4690     for ( ; fExp.More(); fExp.Next() )
4691     {
4692       if ( const SMESHDS_SubMesh* sm = _sdVec[i]._proxyMesh->GetProxySubMesh( fExp.Current() ))
4693       {
4694         if ( sm->NbElements() == 0 ) continue;
4695         SMDS_ElemIteratorPtr fIt = sm->GetElements();
4696         while ( fIt->more())
4697         {
4698           const SMDS_MeshElement* e = fIt->next();
4699           SMESH_Comment cmd("mesh.AddFace([");
4700           for ( int j = 0; j < e->NbCornerNodes(); ++j )
4701             cmd << e->GetNode(j)->GetID() << (j+1 < e->NbCornerNodes() ? ",": "])");
4702           dumpCmd( cmd );
4703         }
4704       }
4705     }
4706     dumpCmd( "faceId2 = mesh.NbElements()" );
4707     dumpCmd( SMESH_Comment( "mesh.MakeGroup( 'tmpFaces_" ) << i << "',"
4708              << "SMESH.FACE, SMESH.FT_RangeOfIds,'=',"
4709              << "'%s-%s' % (faceId1+1, faceId2))");
4710     dumpFunctionEnd();
4711   }
4712 }
4713
4714 //================================================================================
4715 /*!
4716  * \brief Find maximal _LayerEdge length (layer thickness) limited by geometry
4717  */
4718 //================================================================================
4719
4720 void _ViscousBuilder::computeGeomSize( _SolidData& data )
4721 {
4722   data._geomSize = Precision::Infinite();
4723   double intersecDist;
4724   const SMDS_MeshElement* face;
4725   SMESH_MesherHelper helper( *_mesh );
4726
4727   SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
4728     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
4729                                            data._proxyMesh->GetFaces( data._solid )));
4730
4731   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4732   {
4733     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4734     if ( eos._edges.empty() )
4735       continue;
4736     // get neighbor faces, intersection with which should not be considered since
4737     // collisions are avoided by means of smoothing
4738     set< TGeomID > neighborFaces;
4739     if ( eos._hyp.ToSmooth() )
4740     {
4741       SMESH_subMeshIteratorPtr subIt =
4742         eos._subMesh->getDependsOnIterator(/*includeSelf=*/eos.ShapeType() != TopAbs_FACE );
4743       while ( subIt->more() )
4744       {
4745         SMESH_subMesh* sm = subIt->next();
4746         PShapeIteratorPtr fIt = helper.GetAncestors( sm->GetSubShape(), *_mesh, TopAbs_FACE );
4747         while ( const TopoDS_Shape* face = fIt->next() )
4748           neighborFaces.insert( getMeshDS()->ShapeToIndex( *face ));
4749       }
4750     }
4751     // find intersections
4752     double thinkness = eos._hyp.GetTotalThickness();
4753     for ( size_t i = 0; i < eos._edges.size(); ++i )
4754     {
4755       if ( eos._edges[i]->_nodes.size() < 2 ) continue;
4756       eos._edges[i]->SetMaxLen( thinkness );
4757       eos._edges[i]->FindIntersection( *searcher, intersecDist, data._epsilon, eos, &face );
4758       if ( intersecDist > 0 && face )
4759       {
4760         data._geomSize = Min( data._geomSize, intersecDist );
4761         if ( !neighborFaces.count( face->getshapeId() ))
4762           eos[i]->SetMaxLen( Min( thinkness, intersecDist / ( face->GetID() < 0 ? 3. : 2. )));
4763       }
4764     }
4765   }
4766
4767   data._maxThickness = 0;
4768   data._minThickness = 1e100;
4769   list< const StdMeshers_ViscousLayers* >::iterator hyp = data._hyps.begin();
4770   for ( ; hyp != data._hyps.end(); ++hyp )
4771   {
4772     data._maxThickness = Max( data._maxThickness, (*hyp)->GetTotalThickness() );
4773     data._minThickness = Min( data._minThickness, (*hyp)->GetTotalThickness() );
4774   }
4775
4776   // Limit inflation step size by geometry size found by intersecting
4777   // normals of _LayerEdge's with mesh faces
4778   if ( data._stepSize > 0.3 * data._geomSize )
4779     limitStepSize( data, 0.3 * data._geomSize );
4780
4781   if ( data._stepSize > data._minThickness )
4782     limitStepSize( data, data._minThickness );
4783
4784
4785   // -------------------------------------------------------------------------
4786   // Detect _LayerEdge which can't intersect with opposite or neighbor layer,
4787   // so no need in detecting intersection at each inflation step
4788   // -------------------------------------------------------------------------
4789
4790   int nbSteps = data._maxThickness / data._stepSize;
4791   if ( nbSteps < 3 || nbSteps * data._n2eMap.size() < 100000 )
4792     return;
4793
4794   vector< const SMDS_MeshElement* > closeFaces;
4795   int nbDetected = 0;
4796
4797   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4798   {
4799     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4800     if ( eos._edges.empty() || eos.ShapeType() != TopAbs_FACE )
4801       continue;
4802
4803     for ( size_t i = 0; i < eos.size(); ++i )
4804     {
4805       SMESH_NodeXYZ p( eos[i]->_nodes[0] );
4806       double radius = data._maxThickness + 2 * eos[i]->_maxLen;
4807       closeFaces.clear();
4808       searcher->GetElementsInSphere( p, radius, SMDSAbs_Face, closeFaces );
4809
4810       bool toIgnore = true;
4811       for ( size_t iF = 0; iF < closeFaces.size()  && toIgnore; ++iF )
4812         if ( !( toIgnore = ( closeFaces[ iF ]->getshapeId() == eos._shapeID ||
4813                              data._ignoreFaceIds.count( closeFaces[ iF ]->getshapeId() ))))
4814         {
4815           // check if a _LayerEdge will inflate in a direction opposite to a direction
4816           // toward a close face
4817           bool allBehind = true;
4818           for ( int iN = 0; iN < closeFaces[ iF ]->NbCornerNodes()  && allBehind; ++iN )
4819           {
4820             SMESH_NodeXYZ pi( closeFaces[ iF ]->GetNode( iN ));
4821             allBehind = (( pi - p ) * eos[i]->_normal < 0.1 * data._stepSize );
4822           }
4823           toIgnore = allBehind;
4824         }
4825
4826
4827       if ( toIgnore ) // no need to detect intersection
4828       {
4829         eos[i]->Set( _LayerEdge::INTERSECTED );
4830         ++nbDetected;
4831       }
4832     }
4833   }
4834
4835   debugMsg( "Nb LE to intersect " << data._n2eMap.size()-nbDetected << ", ignore " << nbDetected );
4836
4837   return;
4838 }
4839
4840 //================================================================================
4841 /*!
4842  * \brief Increase length of _LayerEdge's to reach the required thickness of layers
4843  */
4844 //================================================================================
4845
4846 bool _ViscousBuilder::inflate(_SolidData& data)
4847 {
4848   SMESH_MesherHelper helper( *_mesh );
4849
4850   const double tgtThick = data._maxThickness;
4851
4852   if ( data._stepSize < 1. )
4853     data._epsilon = data._stepSize * 1e-7;
4854
4855   debugMsg( "-- geomSize = " << data._geomSize << ", stepSize = " << data._stepSize );
4856   _pyDump->Pause();
4857
4858   findCollisionEdges( data, helper );
4859
4860   limitMaxLenByCurvature( data, helper );
4861
4862   _pyDump->Resume();
4863
4864   // limit length of _LayerEdge's around MULTI_NORMAL _LayerEdge's
4865   for ( size_t i = 0; i < data._edgesOnShape.size(); ++i )
4866     if ( data._edgesOnShape[i].ShapeType() == TopAbs_VERTEX &&
4867          data._edgesOnShape[i]._edges.size() > 0 &&
4868          data._edgesOnShape[i]._edges[0]->Is( _LayerEdge::MULTI_NORMAL ))
4869     {
4870       data._edgesOnShape[i]._edges[0]->Unset( _LayerEdge::BLOCKED );
4871       data._edgesOnShape[i]._edges[0]->Block( data );
4872     }
4873
4874   const double safeFactor = ( 2*data._maxThickness < data._geomSize ) ? 1 : theThickToIntersection;
4875
4876   double avgThick = 0, curThick = 0, distToIntersection = Precision::Infinite();
4877   int nbSteps = 0, nbRepeats = 0;
4878   while ( avgThick < 0.99 )
4879   {
4880     // new target length
4881     double prevThick = curThick;
4882     curThick += data._stepSize;
4883     if ( curThick > tgtThick )
4884     {
4885       curThick = tgtThick + tgtThick*( 1.-avgThick ) * nbRepeats;
4886       nbRepeats++;
4887     }
4888
4889     double stepSize = curThick - prevThick;
4890     updateNormalsOfSmoothed( data, helper, nbSteps, stepSize ); // to ease smoothing
4891
4892     // Elongate _LayerEdge's
4893     dumpFunction(SMESH_Comment("inflate")<<data._index<<"_step"<<nbSteps); // debug
4894     for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4895     {
4896       _EdgesOnShape& eos = data._edgesOnShape[iS];
4897       if ( eos._edges.empty() ) continue;
4898
4899       const double shapeCurThick = Min( curThick, eos._hyp.GetTotalThickness() );
4900       for ( size_t i = 0; i < eos._edges.size(); ++i )
4901       {
4902         eos._edges[i]->SetNewLength( shapeCurThick, eos, helper );
4903       }
4904     }
4905     dumpFunctionEnd();
4906
4907     if ( !updateNormals( data, helper, nbSteps, stepSize )) // to avoid collisions
4908       return false;
4909
4910     // Improve and check quality
4911     if ( !smoothAndCheck( data, nbSteps, distToIntersection ))
4912     {
4913       if ( nbSteps > 0 )
4914       {
4915 #ifdef __NOT_INVALIDATE_BAD_SMOOTH
4916         debugMsg("NOT INVALIDATED STEP!");
4917         return error("Smoothing failed", data._index);
4918 #endif
4919         dumpFunction(SMESH_Comment("invalidate")<<data._index<<"_step"<<nbSteps); // debug
4920         for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4921         {
4922           _EdgesOnShape& eos = data._edgesOnShape[iS];
4923           for ( size_t i = 0; i < eos._edges.size(); ++i )
4924             eos._edges[i]->InvalidateStep( nbSteps+1, eos );
4925         }
4926         dumpFunctionEnd();
4927       }
4928       break; // no more inflating possible
4929     }
4930     nbSteps++;
4931
4932     // Evaluate achieved thickness
4933     avgThick = 0;
4934     int nbActiveEdges = 0;
4935     for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4936     {
4937       _EdgesOnShape& eos = data._edgesOnShape[iS];
4938       if ( eos._edges.empty() ) continue;
4939
4940       const double shapeTgtThick = eos._hyp.GetTotalThickness();
4941       for ( size_t i = 0; i < eos._edges.size(); ++i )
4942       {
4943         if ( eos._edges[i]->_nodes.size() > 1 )
4944           avgThick    += Min( 1., eos._edges[i]->_len / shapeTgtThick );
4945         else
4946           avgThick    += 1;
4947         nbActiveEdges += ( ! eos._edges[i]->Is( _LayerEdge::BLOCKED ));
4948       }
4949     }
4950     avgThick /= data._n2eMap.size();
4951     debugMsg( "-- Thickness " << curThick << " ("<< avgThick*100 << "%) reached" );
4952
4953 #ifdef BLOCK_INFLATION
4954     if ( nbActiveEdges == 0 )
4955     {
4956       debugMsg( "-- Stop inflation since all _LayerEdge's BLOCKED " );
4957       break;
4958     }
4959 #else
4960     if ( distToIntersection < tgtThick * avgThick * safeFactor && avgThick < 0.9 )
4961     {
4962       debugMsg( "-- Stop inflation since "
4963                 << " distToIntersection( "<<distToIntersection<<" ) < avgThick( "
4964                 << tgtThick * avgThick << " ) * " << safeFactor );
4965       break;
4966     }
4967 #endif
4968
4969     // new step size
4970     limitStepSize( data, 0.25 * distToIntersection );
4971     if ( data._stepSizeNodes[0] )
4972       data._stepSize = data._stepSizeCoeff *
4973         SMESH_TNodeXYZ(data._stepSizeNodes[0]).Distance(data._stepSizeNodes[1]);
4974
4975   } // while ( avgThick < 0.99 )
4976
4977   if ( nbSteps == 0 )
4978     return error("failed at the very first inflation step", data._index);
4979
4980   if ( avgThick < 0.99 )
4981   {
4982     if ( !data._proxyMesh->_warning || data._proxyMesh->_warning->IsOK() )
4983     {
4984       data._proxyMesh->_warning.reset
4985         ( new SMESH_ComputeError (COMPERR_WARNING,
4986                                   SMESH_Comment("Thickness ") << tgtThick <<
4987                                   " of viscous layers not reached,"
4988                                   " average reached thickness is " << avgThick*tgtThick));
4989     }
4990   }
4991
4992   // Restore position of src nodes moved by inflation on _noShrinkShapes
4993   dumpFunction(SMESH_Comment("restoNoShrink_So")<<data._index); // debug
4994   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4995   {
4996     _EdgesOnShape& eos = data._edgesOnShape[iS];
4997     if ( !eos._edges.empty() && eos._edges[0]->_nodes.size() == 1 )
4998       for ( size_t i = 0; i < eos._edges.size(); ++i )
4999       {
5000         restoreNoShrink( *eos._edges[ i ] );
5001       }
5002   }
5003   dumpFunctionEnd();
5004
5005   return safeFactor > 0; // == true (avoid warning: unused variable 'safeFactor')
5006 }
5007
5008 //================================================================================
5009 /*!
5010  * \brief Improve quality of layer inner surface and check intersection
5011  */
5012 //================================================================================
5013
5014 bool _ViscousBuilder::smoothAndCheck(_SolidData& data,
5015                                      const int   infStep,
5016                                      double &    distToIntersection)
5017 {
5018   if ( data._nbShapesToSmooth == 0 )
5019     return true; // no shapes needing smoothing
5020
5021   bool moved, improved;
5022   double vol;
5023   vector< _LayerEdge* >    movedEdges, badEdges;
5024   vector< _EdgesOnShape* > eosC1; // C1 continues shapes
5025   vector< bool >           isConcaveFace;
5026
5027   SMESH_MesherHelper helper(*_mesh);
5028   Handle(ShapeAnalysis_Surface) surface;
5029   TopoDS_Face F;
5030
5031   for ( int isFace = 0; isFace < 2; ++isFace ) // smooth on [ EDGEs, FACEs ]
5032   {
5033     const TopAbs_ShapeEnum shapeType = isFace ? TopAbs_FACE : TopAbs_EDGE;
5034
5035     for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
5036     {
5037       _EdgesOnShape& eos = data._edgesOnShape[ iS ];
5038       if ( !eos._toSmooth ||
5039            eos.ShapeType() != shapeType ||
5040            eos._edges.empty() )
5041         continue;
5042
5043       // already smoothed?
5044       // bool toSmooth = ( eos._edges[ 0 ]->NbSteps() >= infStep+1 );
5045       // if ( !toSmooth ) continue;
5046
5047       if ( !eos._hyp.ToSmooth() )
5048       {
5049         // smooth disabled by the user; check validy only
5050         if ( !isFace ) continue;
5051         badEdges.clear();
5052         for ( size_t i = 0; i < eos._edges.size(); ++i )
5053         {
5054           _LayerEdge* edge = eos._edges[i];
5055           for ( size_t iF = 0; iF < edge->_simplices.size(); ++iF )
5056             if ( !edge->_simplices[iF].IsForward( edge->_nodes[0], edge->_pos.back(), vol ))
5057             {
5058               // debugMsg( "-- Stop inflation. Bad simplex ("
5059               //           << " "<< edge->_nodes[0]->GetID()
5060               //           << " "<< edge->_nodes.back()->GetID()
5061               //           << " "<< edge->_simplices[iF]._nPrev->GetID()
5062               //           << " "<< edge->_simplices[iF]._nNext->GetID() << " ) ");
5063               // return false;
5064               badEdges.push_back( edge );
5065             }
5066         }
5067         if ( !badEdges.empty() )
5068         {
5069           eosC1.resize(1);
5070           eosC1[0] = &eos;
5071           int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5072           if ( nbBad > 0 )
5073             return false;
5074         }
5075         continue; // goto the next EDGE or FACE
5076       }
5077
5078       // prepare data
5079       if ( eos.SWOLType() == TopAbs_FACE )
5080       {
5081         if ( !F.IsSame( eos._sWOL )) {
5082           F = TopoDS::Face( eos._sWOL );
5083           helper.SetSubShape( F );
5084           surface = helper.GetSurface( F );
5085         }
5086       }
5087       else
5088       {
5089         F.Nullify(); surface.Nullify();
5090       }
5091       const TGeomID sInd = eos._shapeID;
5092
5093       // perform smoothing
5094
5095       if ( eos.ShapeType() == TopAbs_EDGE )
5096       {
5097         dumpFunction(SMESH_Comment("smooth")<<data._index << "_Ed"<<sInd <<"_InfStep"<<infStep);
5098
5099         if ( !eos._edgeSmoother->Perform( data, surface, F, helper ))
5100         {
5101           // smooth on EDGE's (normally we should not get here)
5102           int step = 0;
5103           do {
5104             moved = false;
5105             for ( size_t i = 0; i < eos._edges.size(); ++i )
5106             {
5107               moved |= eos._edges[i]->SmoothOnEdge( surface, F, helper );
5108             }
5109             dumpCmd( SMESH_Comment("# end step ")<<step);
5110           }
5111           while ( moved && step++ < 5 );
5112         }
5113         dumpFunctionEnd();
5114       }
5115
5116       else // smooth on FACE
5117       {
5118         eosC1.clear();
5119         eosC1.push_back( & eos );
5120         eosC1.insert( eosC1.end(), eos._eosC1.begin(), eos._eosC1.end() );
5121
5122         movedEdges.clear();
5123         isConcaveFace.resize( eosC1.size() );
5124         for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5125         {
5126           isConcaveFace[ iEOS ] = data._concaveFaces.count( eosC1[ iEOS ]->_shapeID  );
5127
5128           if ( eosC1[ iEOS ]->_mapper2D )
5129           {
5130             // compute node position by boundary node position in structured mesh
5131             dumpFunction(SMESH_Comment("map2dS")<<data._index<<"_Fa"<<eos._shapeID
5132                          <<"_InfStep"<<infStep);
5133
5134             eosC1[ iEOS ]->_mapper2D->ComputeNodePositions();
5135
5136             for ( _LayerEdge* le : eosC1[ iEOS ]->_edges )
5137               le->_pos.back() = SMESH_NodeXYZ( le->_nodes.back() );
5138
5139             dumpFunctionEnd();
5140           }
5141           else
5142           {
5143             for ( _LayerEdge* le : eosC1[ iEOS ]->_edges )
5144               if ( le->Is( _LayerEdge::MOVED ) ||
5145                    le->Is( _LayerEdge::NEAR_BOUNDARY ))
5146                 movedEdges.push_back( le );
5147           }
5148           makeOffsetSurface( *eosC1[ iEOS ], helper );
5149         }
5150
5151         int step = 0, stepLimit = 5, nbBad = 0;
5152         while (( ++step <= stepLimit ) || improved )
5153         {
5154           int oldBadNb = nbBad;
5155           badEdges.clear();
5156
5157 #ifdef INCREMENTAL_SMOOTH
5158           // smooth moved only
5159           if ( !movedEdges.empty() )
5160             dumpFunction(SMESH_Comment("smooth")<<data._index<<"_Fa"<<sInd
5161                          <<"_InfStep"<<infStep<<"_"<<step); // debug
5162           bool findBest = false; // ( step == stepLimit );
5163           for ( size_t i = 0; i < movedEdges.size(); ++i )
5164           {
5165             movedEdges[i]->Unset( _LayerEdge::SMOOTHED );
5166             if ( movedEdges[i]->Smooth( step, findBest, movedEdges ) > 0 )
5167               badEdges.push_back( movedEdges[i] );
5168           }
5169 #else
5170           // smooth all
5171           dumpFunction(SMESH_Comment("smooth")<<data._index<<"_Fa"<<sInd
5172                        <<"_InfStep"<<infStep<<"_"<<step); // debug
5173           bool findBest = ( step == stepLimit || isConcaveFace[ iEOS ]);
5174           for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5175           {
5176             if ( eosC1[ iEOS ]->_mapper2D )
5177               continue;
5178             vector< _LayerEdge* > & edges = eosC1[ iEOS ]->_edges;
5179             for ( size_t i = 0; i < edges.size(); ++i )
5180             {
5181               edges[i]->Unset( _LayerEdge::SMOOTHED );
5182               if ( edges[i]->Smooth( step, findBest, false ) > 0 )
5183                 badEdges.push_back( eos._edges[i] );
5184             }
5185           }
5186 #endif
5187           nbBad = badEdges.size();
5188
5189           if ( nbBad > 0 )
5190             debugMsg(SMESH_Comment("nbBad = ") << nbBad );
5191
5192           if ( !badEdges.empty() && step >= stepLimit / 2 )
5193           {
5194             if ( badEdges[0]->Is( _LayerEdge::ON_CONCAVE_FACE ))
5195               stepLimit = 9;
5196
5197             // resolve hard smoothing situation around concave VERTEXes
5198             for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5199             {
5200               vector< _EdgesOnShape* > & eosCoVe = eosC1[ iEOS ]->_eosConcaVer;
5201               for ( size_t i = 0; i < eosCoVe.size(); ++i )
5202                 eosCoVe[i]->_edges[0]->MoveNearConcaVer( eosCoVe[i], eosC1[ iEOS ],
5203                                                          step, badEdges );
5204             }
5205             // look for the best smooth of _LayerEdge's neighboring badEdges
5206             nbBad = 0;
5207             for ( size_t i = 0; i < badEdges.size(); ++i )
5208             {
5209               _LayerEdge* ledge = badEdges[i];
5210               for ( size_t iN = 0; iN < ledge->_neibors.size(); ++iN )
5211               {
5212                 ledge->_neibors[iN]->Unset( _LayerEdge::SMOOTHED );
5213                 nbBad += ledge->_neibors[iN]->Smooth( step, true, /*findBest=*/true );
5214               }
5215               ledge->Unset( _LayerEdge::SMOOTHED );
5216               nbBad += ledge->Smooth( step, true, /*findBest=*/true );
5217             }
5218             debugMsg(SMESH_Comment("nbBad = ") << nbBad );
5219           }
5220
5221           if ( nbBad == oldBadNb  &&
5222                nbBad > 0 &&
5223                step < stepLimit ) // smooth w/o check of validity
5224           {
5225             dumpFunctionEnd();
5226             dumpFunction(SMESH_Comment("smoothWoCheck")<<data._index<<"_Fa"<<sInd
5227                          <<"_InfStep"<<infStep<<"_"<<step); // debug
5228             for ( size_t i = 0; i < movedEdges.size(); ++i )
5229             {
5230               movedEdges[i]->SmoothWoCheck();
5231             }
5232             if ( stepLimit < 9 )
5233               stepLimit++;
5234           }
5235
5236           improved = ( nbBad < oldBadNb );
5237
5238           dumpFunctionEnd();
5239
5240           if (( step % 3 == 1 ) || ( nbBad > 0 && step >= stepLimit / 2 ))
5241             for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5242             {
5243               putOnOffsetSurface( *eosC1[ iEOS ], infStep, eosC1, step, /*moveAll=*/step == 1 );
5244             }
5245
5246         } // smoothing steps
5247
5248         // project -- to prevent intersections or to fix bad simplices
5249         for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5250         {
5251           if ( ! eosC1[ iEOS ]->_eosConcaVer.empty() || nbBad > 0 )
5252             putOnOffsetSurface( *eosC1[ iEOS ], -infStep, eosC1 );
5253         }
5254
5255         //if ( !badEdges.empty() )
5256         {
5257           badEdges.clear();
5258           for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5259           {
5260             for ( size_t i = 0; i < eosC1[ iEOS ]->_edges.size(); ++i )
5261             {
5262               if ( !eosC1[ iEOS ]->_sWOL.IsNull() ) continue;
5263
5264               _LayerEdge* edge = eosC1[ iEOS ]->_edges[i];
5265               edge->CheckNeiborsOnBoundary( & badEdges );
5266               if (( nbBad > 0 ) ||
5267                   ( edge->Is( _LayerEdge::BLOCKED ) && edge->Is( _LayerEdge::NEAR_BOUNDARY )))
5268               {
5269                 SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
5270                 gp_XYZ        prevXYZ = edge->PrevCheckPos();
5271                 for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5272                   if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
5273                   {
5274                     debugMsg("Bad simplex ( " << edge->_nodes[0]->GetID()
5275                              << " "<< tgtXYZ._node->GetID()
5276                              << " "<< edge->_simplices[j]._nPrev->GetID()
5277                              << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
5278                     badEdges.push_back( edge );
5279                     break;
5280                   }
5281               }
5282             }
5283           }
5284
5285           // try to fix bad simplices by removing the last inflation step of some _LayerEdge's
5286           nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5287
5288           if ( nbBad > 0 )
5289             return false;
5290         }
5291
5292       } // // smooth on FACE's
5293     } // loop on shapes
5294   } // smooth on [ EDGEs, FACEs ]
5295
5296   // Check orientation of simplices of _LayerEdge's on EDGEs and VERTEXes
5297   eosC1.resize(1);
5298   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
5299   {
5300     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
5301     if ( eos.ShapeType() == TopAbs_FACE ||
5302          eos._edges.empty() ||
5303          !eos._sWOL.IsNull() )
5304       continue;
5305
5306     badEdges.clear();
5307     for ( size_t i = 0; i < eos._edges.size(); ++i )
5308     {
5309       _LayerEdge*      edge = eos._edges[i];
5310       if ( edge->_nodes.size() < 2 ) continue;
5311       SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
5312       //SMESH_TNodeXYZ prevXYZ = edge->_nodes[0];
5313       gp_XYZ        prevXYZ = edge->PrevCheckPos( &eos );
5314       //const gp_XYZ& prevXYZ = edge->PrevPos();
5315       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5316         if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
5317         {
5318           debugMsg("Bad simplex on bnd ( " << edge->_nodes[0]->GetID()
5319                    << " "<< tgtXYZ._node->GetID()
5320                    << " "<< edge->_simplices[j]._nPrev->GetID()
5321                    << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
5322           badEdges.push_back( edge );
5323           break;
5324         }
5325     }
5326
5327     // try to fix bad simplices by removing the last inflation step of some _LayerEdge's
5328     eosC1[0] = &eos;
5329     int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5330     if ( nbBad > 0 )
5331       return false;
5332   }
5333
5334
5335   // Check if the last segments of _LayerEdge intersects 2D elements;
5336   // checked elements are either temporary faces or faces on surfaces w/o the layers
5337
5338   SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
5339     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
5340                                            data._proxyMesh->GetFaces( data._solid )) );
5341
5342 #ifdef BLOCK_INFLATION
5343   const bool toBlockInfaltion = true;
5344 #else
5345   const bool toBlockInfaltion = false;
5346 #endif
5347   distToIntersection = Precision::Infinite();
5348   double dist;
5349   const SMDS_MeshElement* intFace = 0;
5350   const SMDS_MeshElement* closestFace = 0;
5351   _LayerEdge* le = 0;
5352   bool is1stBlocked = true; // dbg
5353   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
5354   {
5355     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
5356     if ( eos._edges.empty() || !eos._sWOL.IsNull() )
5357       continue;
5358     for ( size_t i = 0; i < eos._edges.size(); ++i )
5359     {
5360       if ( eos._edges[i]->Is( _LayerEdge::INTERSECTED ) ||
5361            eos._edges[i]->Is( _LayerEdge::MULTI_NORMAL ))
5362         continue;
5363       if ( eos._edges[i]->FindIntersection( *searcher, dist, data._epsilon, eos, &intFace ))
5364       {
5365         return false;
5366         // commented due to "Illegal hash-positionPosition" error in NETGEN
5367         // on Debian60 on viscous_layers_01/B2 case
5368         // Collision; try to deflate _LayerEdge's causing it
5369         // badEdges.clear();
5370         // badEdges.push_back( eos._edges[i] );
5371         // eosC1[0] = & eos;
5372         // int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5373         // if ( nbBad > 0 )
5374         //   return false;
5375
5376         // badEdges.clear();
5377         // if ( _EdgesOnShape* eof = data.GetShapeEdges( intFace->getshapeId() ))
5378         // {
5379         //   if ( const _TmpMeshFace* f = dynamic_cast< const _TmpMeshFace*>( intFace ))
5380         //   {
5381         //     const SMDS_MeshElement* srcFace =
5382         //       eof->_subMesh->GetSubMeshDS()->GetElement( f->getIdInShape() );
5383         //     SMDS_ElemIteratorPtr nIt = srcFace->nodesIterator();
5384         //     while ( nIt->more() )
5385         //     {
5386         //       const SMDS_MeshNode* srcNode = static_cast<const SMDS_MeshNode*>( nIt->next() );
5387         //       TNode2Edge::iterator n2e = data._n2eMap.find( srcNode );
5388         //       if ( n2e != data._n2eMap.end() )
5389         //         badEdges.push_back( n2e->second );
5390         //     }
5391         //     eosC1[0] = eof;
5392         //     nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5393         //     if ( nbBad > 0 )
5394         //       return false;
5395         //   }
5396         // }
5397         // if ( eos._edges[i]->FindIntersection( *searcher, dist, data._epsilon, eos, &intFace ))
5398         //   return false;
5399         // else
5400         //   continue;
5401       }
5402       if ( !intFace )
5403       {
5404         SMESH_Comment msg("Invalid? normal at node "); msg << eos._edges[i]->_nodes[0]->GetID();
5405         debugMsg( msg );
5406         continue;
5407       }
5408
5409       const bool isShorterDist = ( distToIntersection > dist );
5410       if ( toBlockInfaltion || isShorterDist )
5411       {
5412         // ignore intersection of a _LayerEdge based on a _ConvexFace with a face
5413         // lying on this _ConvexFace
5414         if ( _ConvexFace* convFace = data.GetConvexFace( intFace->getshapeId() ))
5415           if ( convFace->_isTooCurved && convFace->_subIdToEOS.count ( eos._shapeID ))
5416             continue;
5417
5418         // ignore intersection of a _LayerEdge based on a FACE with an element on this FACE
5419         // ( avoid limiting the thickness on the case of issue 22576)
5420         if ( intFace->getshapeId() == eos._shapeID  )
5421           continue;
5422
5423         // ignore intersection with intFace of an adjacent FACE
5424         if ( dist > 0.01 * eos._edges[i]->_len )
5425         {
5426           bool toIgnore = false;
5427           if (  eos._toSmooth )
5428           {
5429             const TopoDS_Shape& S = getMeshDS()->IndexToShape( intFace->getshapeId() );
5430             if ( !S.IsNull() && S.ShapeType() == TopAbs_FACE )
5431             {
5432               TopExp_Explorer sub( eos._shape,
5433                                    eos.ShapeType() == TopAbs_FACE ? TopAbs_EDGE : TopAbs_VERTEX );
5434               for ( ; !toIgnore && sub.More(); sub.Next() )
5435                 // is adjacent - has a common EDGE or VERTEX
5436                 toIgnore = ( helper.IsSubShape( sub.Current(), S ));
5437
5438               if ( toIgnore ) // check angle between normals
5439               {
5440                 gp_XYZ normal;
5441                 if ( SMESH_MeshAlgos::FaceNormal( intFace, normal, /*normalized=*/true ))
5442                   toIgnore  = ( normal * eos._edges[i]->_normal > -0.5 );
5443               }
5444             }
5445           }
5446           if ( !toIgnore ) // check if the edge is a neighbor of intFace
5447           {
5448             for ( size_t iN = 0; !toIgnore &&  iN < eos._edges[i]->_neibors.size(); ++iN )
5449             {
5450               int nInd = intFace->GetNodeIndex( eos._edges[i]->_neibors[ iN ]->_nodes.back() );
5451               toIgnore = ( nInd >= 0 );
5452             }
5453           }
5454           if ( toIgnore )
5455             continue;
5456         }
5457
5458         // intersection not ignored
5459
5460         double minDist = 0;
5461         if ( eos._edges[i]->_maxLen < 0.99 * eos._hyp.GetTotalThickness() ) // limited length
5462           minDist = eos._edges[i]->_len * theThickToIntersection;
5463
5464         if ( toBlockInfaltion && dist < minDist  )
5465         {
5466           if ( is1stBlocked ) { is1stBlocked = false; // debug
5467             dumpFunction(SMESH_Comment("blockIntersected") <<data._index<<"_InfStep"<<infStep);
5468           }
5469           eos._edges[i]->Set( _LayerEdge::INTERSECTED ); // not to intersect
5470           eos._edges[i]->Block( data );                  // not to inflate
5471
5472           //if ( _EdgesOnShape* eof = data.GetShapeEdges( intFace->getshapeId() ))
5473           {
5474             // block _LayerEdge's, on top of which intFace is
5475             if ( const _TmpMeshFace* f = dynamic_cast< const _TmpMeshFace*>( intFace ))
5476             {
5477               const SMDS_MeshElement* srcFace = f->_srcFace;
5478               SMDS_ElemIteratorPtr        nIt = srcFace->nodesIterator();
5479               while ( nIt->more() )
5480               {
5481                 const SMDS_MeshNode* srcNode = static_cast<const SMDS_MeshNode*>( nIt->next() );
5482                 TNode2Edge::iterator n2e = data._n2eMap.find( srcNode );
5483                 if ( n2e != data._n2eMap.end() )
5484                   n2e->second->Block( data );
5485               }
5486             }
5487           }
5488         }
5489
5490         if ( isShorterDist )
5491         {
5492           distToIntersection = dist;
5493           le = eos._edges[i];
5494           closestFace = intFace;
5495         }
5496
5497       } // if ( toBlockInfaltion || isShorterDist )
5498     } // loop on eos._edges
5499   } // loop on data._edgesOnShape
5500
5501   if ( !is1stBlocked )
5502   {
5503     dumpFunctionEnd();
5504   }
5505
5506   if ( closestFace && le )
5507   {
5508 #ifdef __myDEBUG
5509     SMDS_MeshElement::iterator nIt = closestFace->begin_nodes();
5510     cout << "#Shortest distance: _LayerEdge nodes: tgt " << le->_nodes.back()->GetID()
5511          << " src " << le->_nodes[0]->GetID()<< ", intersection with face ("
5512          << (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()
5513          << ") distance = " << distToIntersection<< endl;
5514 #endif
5515   }
5516
5517   return true;
5518 }
5519
5520 //================================================================================
5521 /*!
5522  * \brief try to fix bad simplices by removing the last inflation step of some _LayerEdge's
5523  *  \param [in,out] badSmooEdges - _LayerEdge's to fix
5524  *  \return int - resulting nb of bad _LayerEdge's
5525  */
5526 //================================================================================
5527
5528 int _ViscousBuilder::invalidateBadSmooth( _SolidData&               data,
5529                                           SMESH_MesherHelper&       helper,
5530                                           vector< _LayerEdge* >&    badSmooEdges,
5531                                           vector< _EdgesOnShape* >& eosC1,
5532                                           const int                 infStep )
5533 {
5534   if ( badSmooEdges.empty() || infStep == 0 ) return 0;
5535
5536   dumpFunction(SMESH_Comment("invalidateBadSmooth")<<"_S"<<eosC1[0]->_shapeID<<"_InfStep"<<infStep);
5537
5538   enum {
5539     INVALIDATED   = _LayerEdge::UNUSED_FLAG,
5540     TO_INVALIDATE = _LayerEdge::UNUSED_FLAG * 2,
5541     ADDED         = _LayerEdge::UNUSED_FLAG * 4
5542   };
5543   data.UnmarkEdges( TO_INVALIDATE & INVALIDATED & ADDED );
5544
5545   double vol;
5546   bool haveInvalidated = true;
5547   while ( haveInvalidated )
5548   {
5549     haveInvalidated = false;
5550     for ( size_t i = 0; i < badSmooEdges.size(); ++i )
5551     {
5552       _LayerEdge*   edge = badSmooEdges[i];
5553       _EdgesOnShape* eos = data.GetShapeEdges( edge );
5554       edge->Set( ADDED );
5555       bool invalidated = false;
5556       if ( edge->Is( TO_INVALIDATE ) && edge->NbSteps() > 1 )
5557       {
5558         edge->InvalidateStep( edge->NbSteps(), *eos, /*restoreLength=*/true );
5559         edge->Block( data );
5560         edge->Set( INVALIDATED );
5561         edge->Unset( TO_INVALIDATE );
5562         invalidated = true;
5563         haveInvalidated = true;
5564       }
5565
5566       // look for _LayerEdge's of bad _simplices
5567       int nbBad = 0;
5568       SMESH_TNodeXYZ tgtXYZ  = edge->_nodes.back();
5569       gp_XYZ        prevXYZ1 = edge->PrevCheckPos( eos );
5570       //const gp_XYZ& prevXYZ2 = edge->PrevPos();
5571       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5572       {
5573         if (( edge->_simplices[j].IsForward( &prevXYZ1, &tgtXYZ, vol ))/* &&
5574             ( &prevXYZ1 == &prevXYZ2 || edge->_simplices[j].IsForward( &prevXYZ2, &tgtXYZ, vol ))*/)
5575           continue;
5576
5577         bool isBad = true;
5578         _LayerEdge* ee[2] = { 0,0 };
5579         for ( size_t iN = 0; iN < edge->_neibors.size() &&   !ee[1]  ; ++iN )
5580           if ( edge->_simplices[j].Includes( edge->_neibors[iN]->_nodes.back() ))
5581             ee[ ee[0] != 0 ] = edge->_neibors[iN];
5582
5583         int maxNbSteps = Max( ee[0]->NbSteps(), ee[1]->NbSteps() );
5584         while ( maxNbSteps > edge->NbSteps() && isBad )
5585         {
5586           --maxNbSteps;
5587           for ( int iE = 0; iE < 2; ++iE )
5588           {
5589             if ( ee[ iE ]->NbSteps() > maxNbSteps &&
5590                  ee[ iE ]->NbSteps() > 1 )
5591             {
5592               _EdgesOnShape* eos = data.GetShapeEdges( ee[ iE ] );
5593               ee[ iE ]->InvalidateStep( ee[ iE ]->NbSteps(), *eos, /*restoreLength=*/true );
5594               ee[ iE ]->Block( data );
5595               ee[ iE ]->Set( INVALIDATED );
5596               haveInvalidated = true;
5597             }
5598           }
5599           if (( edge->_simplices[j].IsForward( &prevXYZ1, &tgtXYZ, vol )) /*&&
5600               ( &prevXYZ1 == &prevXYZ2 || edge->_simplices[j].IsForward( &prevXYZ2, &tgtXYZ, vol ))*/)
5601             isBad = false;
5602         }
5603         nbBad += isBad;
5604         if ( !ee[0]->Is( ADDED )) badSmooEdges.push_back( ee[0] );
5605         if ( !ee[1]->Is( ADDED )) badSmooEdges.push_back( ee[1] );
5606         ee[0]->Set( ADDED );
5607         ee[1]->Set( ADDED );
5608         if ( isBad )
5609         {
5610           ee[0]->Set( TO_INVALIDATE );
5611           ee[1]->Set( TO_INVALIDATE );
5612         }
5613       }
5614
5615       if ( !invalidated &&  nbBad > 0  &&  edge->NbSteps() > 1 )
5616       {
5617         _EdgesOnShape* eos = data.GetShapeEdges( edge );
5618         edge->InvalidateStep( edge->NbSteps(), *eos, /*restoreLength=*/true );
5619         edge->Block( data );
5620         edge->Set( INVALIDATED );
5621         edge->Unset( TO_INVALIDATE );
5622         haveInvalidated = true;
5623       }
5624     } // loop on badSmooEdges
5625   } // while ( haveInvalidated )
5626
5627   // re-smooth on analytical EDGEs
5628   for ( size_t i = 0; i < badSmooEdges.size(); ++i )
5629   {
5630     _LayerEdge* edge = badSmooEdges[i];
5631     if ( !edge->Is( INVALIDATED )) continue;
5632
5633     _EdgesOnShape* eos = data.GetShapeEdges( edge );
5634     if ( eos->ShapeType() == TopAbs_VERTEX )
5635     {
5636       PShapeIteratorPtr eIt = helper.GetAncestors( eos->_shape, *_mesh, TopAbs_EDGE );
5637       while ( const TopoDS_Shape* e = eIt->next() )
5638         if ( _EdgesOnShape* eoe = data.GetShapeEdges( *e ))
5639           if ( eoe->_edgeSmoother && eoe->_edgeSmoother->isAnalytic() )
5640           {
5641             // TopoDS_Face F; Handle(ShapeAnalysis_Surface) surface;
5642             // if ( eoe->SWOLType() == TopAbs_FACE ) {
5643             //   F       = TopoDS::Face( eoe->_sWOL );
5644             //   surface = helper.GetSurface( F );
5645             // }
5646             // eoe->_edgeSmoother->Perform( data, surface, F, helper );
5647             eoe->_edgeSmoother->_anaCurve.Nullify();
5648           }
5649     }
5650   }
5651
5652
5653   // check result of invalidation
5654
5655   int nbBad = 0;
5656   for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5657   {
5658     for ( size_t i = 0; i < eosC1[ iEOS ]->_edges.size(); ++i )
5659     {
5660       if ( !eosC1[ iEOS ]->_sWOL.IsNull() ) continue;
5661       _LayerEdge*      edge = eosC1[ iEOS ]->_edges[i];
5662       SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
5663       gp_XYZ        prevXYZ = edge->PrevCheckPos( eosC1[ iEOS ]);
5664       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5665         if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
5666         {
5667           ++nbBad;
5668           debugMsg("Bad simplex remains ( " << edge->_nodes[0]->GetID()
5669                    << " "<< tgtXYZ._node->GetID()
5670                    << " "<< edge->_simplices[j]._nPrev->GetID()
5671                    << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
5672         }
5673     }
5674   }
5675   dumpFunctionEnd();
5676
5677   return nbBad;
5678 }
5679
5680 //================================================================================
5681 /*!
5682  * \brief Create an offset surface
5683  */
5684 //================================================================================
5685
5686 void _ViscousBuilder::makeOffsetSurface( _EdgesOnShape& eos, SMESH_MesherHelper& helper )
5687 {
5688   if ( eos._offsetSurf.IsNull() ||
5689        eos._edgeForOffset == 0 ||
5690        eos._edgeForOffset->Is( _LayerEdge::BLOCKED ))
5691     return;
5692
5693   Handle(ShapeAnalysis_Surface) baseSurface = helper.GetSurface( TopoDS::Face( eos._shape ));
5694
5695   // find offset
5696   gp_Pnt   tgtP = SMESH_TNodeXYZ( eos._edgeForOffset->_nodes.back() );
5697   /*gp_Pnt2d uv=*/baseSurface->ValueOfUV( tgtP, Precision::Confusion() );
5698   eos._offsetValue = baseSurface->Gap();
5699
5700   eos._offsetSurf.Nullify();
5701
5702   try
5703   {
5704     BRepOffsetAPI_MakeOffsetShape offsetMaker;
5705     offsetMaker.PerformByJoin( eos._shape, -eos._offsetValue, Precision::Confusion() );
5706     if ( !offsetMaker.IsDone() ) return;
5707
5708     TopExp_Explorer fExp( offsetMaker.Shape(), TopAbs_FACE );
5709     if ( !fExp.More() ) return;
5710
5711     TopoDS_Face F = TopoDS::Face( fExp.Current() );
5712     Handle(Geom_Surface) surf = BRep_Tool::Surface( F );
5713     if ( surf.IsNull() ) return;
5714
5715     eos._offsetSurf = new ShapeAnalysis_Surface( surf );
5716   }
5717   catch ( Standard_Failure& )
5718   {
5719   }
5720 }
5721
5722 //================================================================================
5723 /*!
5724  * \brief Put nodes of a curved FACE to its offset surface
5725  */
5726 //================================================================================
5727
5728 void _ViscousBuilder::putOnOffsetSurface( _EdgesOnShape&            eos,
5729                                           int                       infStep,
5730                                           vector< _EdgesOnShape* >& eosC1,
5731                                           int                       smooStep,
5732                                           int                       moveAll )
5733 {
5734   _EdgesOnShape * eof = & eos;
5735   if ( eos.ShapeType() != TopAbs_FACE ) // eos is a boundary of C1 FACE, look for the FACE eos
5736   {
5737     eof = 0;
5738     for ( size_t i = 0; i < eosC1.size() && !eof; ++i )
5739     {
5740       if ( eosC1[i]->_offsetSurf.IsNull() ||
5741            eosC1[i]->ShapeType() != TopAbs_FACE ||
5742            eosC1[i]->_edgeForOffset == 0 ||
5743            eosC1[i]->_edgeForOffset->Is( _LayerEdge::BLOCKED ))
5744         continue;
5745       if ( SMESH_MesherHelper::IsSubShape( eos._shape, eosC1[i]->_shape ))
5746         eof = eosC1[i];
5747     }
5748   }
5749   if ( !eof ||
5750        eof->_offsetSurf.IsNull() ||
5751        eof->ShapeType() != TopAbs_FACE ||
5752        eof->_edgeForOffset == 0 ||
5753        eof->_edgeForOffset->Is( _LayerEdge::BLOCKED ))
5754     return;
5755
5756   double preci = BRep_Tool::Tolerance( TopoDS::Face( eof->_shape )), vol;
5757   bool neighborHasRiskySWOL = false;
5758   for ( size_t i = 0; i < eos._edges.size(); ++i )
5759   {
5760     _LayerEdge* edge = eos._edges[i];
5761     edge->Unset( _LayerEdge::MARKED );
5762     if ( edge->Is( _LayerEdge::BLOCKED ) || !edge->_curvature )
5763       continue;
5764     if ( moveAll == _LayerEdge::UPD_NORMAL_CONV )
5765     {
5766       if ( !edge->Is( _LayerEdge::UPD_NORMAL_CONV ))
5767         continue;
5768     }
5769     else if ( moveAll == _LayerEdge::RISKY_SWOL )
5770     {
5771       if ( !edge->Is( _LayerEdge::RISKY_SWOL ) ||
5772            edge->_cosin < 0 )
5773         continue;
5774     }
5775     else if ( !moveAll && !edge->Is( _LayerEdge::MOVED ))
5776       continue;
5777
5778     int nbBlockedAround = 0;
5779     for ( size_t iN = 0; iN < edge->_neibors.size(); ++iN )
5780     {
5781       nbBlockedAround += edge->_neibors[iN]->Is( _LayerEdge::BLOCKED );
5782       if ( edge->_neibors[iN]->Is( _LayerEdge::RISKY_SWOL ) &&
5783            edge->_neibors[iN]->_cosin > 0 )
5784         neighborHasRiskySWOL = true;
5785     }
5786     if ( nbBlockedAround > 1 )
5787       continue;
5788
5789     gp_Pnt tgtP = SMESH_TNodeXYZ( edge->_nodes.back() );
5790     gp_Pnt2d uv = eof->_offsetSurf->NextValueOfUV( edge->_curvature->_uv, tgtP, preci );
5791     if ( eof->_offsetSurf->Gap() > edge->_len ) continue; // NextValueOfUV() bug
5792     edge->_curvature->_uv = uv;
5793     if ( eof->_offsetSurf->Gap() < 10 * preci ) continue; // same pos
5794
5795     gp_XYZ  newP = eof->_offsetSurf->Value( uv ).XYZ();
5796     gp_XYZ prevP = edge->PrevCheckPos();
5797     bool      ok = true;
5798     if ( !moveAll )
5799       for ( size_t iS = 0; iS < edge->_simplices.size() && ok; ++iS )
5800       {
5801         ok = edge->_simplices[iS].IsForward( &prevP, &newP, vol );
5802       }
5803     if ( ok )
5804     {
5805       SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( edge->_nodes.back() );
5806       n->setXYZ( newP.X(), newP.Y(), newP.Z());
5807       edge->_pos.back() = newP;
5808
5809       edge->Set( _LayerEdge::MARKED );
5810       if ( moveAll == _LayerEdge::UPD_NORMAL_CONV )
5811       {
5812         edge->_normal = ( newP - prevP ).Normalized();
5813       }
5814       // if ( edge->_len < eof->_offsetValue )
5815       //   edge->_len = eof->_offsetValue;
5816
5817       if ( !eos._sWOL.IsNull() ) // RISKY_SWOL
5818       {
5819         double change = eof->_offsetSurf->Gap() / eof->_offsetValue;
5820         if (( newP - tgtP.XYZ() ) * edge->_normal < 0 )
5821           change = 1 - change;
5822         else
5823           change = 1 + change;
5824         gp_XYZ shitfVec    = tgtP.XYZ() - SMESH_NodeXYZ( edge->_nodes[0] );
5825         gp_XYZ newShiftVec = shitfVec * change;
5826         double shift       = edge->_normal * shitfVec;
5827         double newShift    = edge->_normal * newShiftVec;
5828         newP = tgtP.XYZ() + edge->_normal * ( newShift - shift );
5829
5830         uv = eof->_offsetSurf->NextValueOfUV( edge->_curvature->_uv, newP, preci );
5831         if ( eof->_offsetSurf->Gap() < edge->_len )
5832         {
5833           edge->_curvature->_uv = uv;
5834           newP = eof->_offsetSurf->Value( uv ).XYZ();
5835         }
5836         n->setXYZ( newP.X(), newP.Y(), newP.Z());
5837         if ( !edge->UpdatePositionOnSWOL( n, /*tol=*/10 * edge->_len / ( edge->NbSteps() + 1 ),
5838                                           eos, eos.GetData().GetHelper() ))
5839         {
5840           debugMsg("UpdatePositionOnSWOL fails in putOnOffsetSurface()" );
5841         }
5842       }
5843     }
5844   }
5845
5846   if (SALOME::VerbosityActivated())
5847   {
5848     // dumpMove() for debug
5849     size_t i = 0;
5850     for ( ; i < eos._edges.size(); ++i )
5851       if ( eos._edges[i]->Is( _LayerEdge::MARKED ))
5852         break;
5853     if ( i < eos._edges.size() )
5854     {
5855       dumpFunction(SMESH_Comment("putOnOffsetSurface_") << eos.ShapeTypeLetter() << eos._shapeID
5856                   << "_InfStep" << infStep << "_" << Abs( smooStep ));
5857       for ( ; i < eos._edges.size(); ++i )
5858       {
5859         if ( eos._edges[i]->Is( _LayerEdge::MARKED )) {
5860           dumpMove( eos._edges[i]->_nodes.back() );
5861         }
5862       }
5863       dumpFunctionEnd();
5864     }
5865   }
5866
5867   _ConvexFace* cnvFace;
5868   if ( moveAll != _LayerEdge::UPD_NORMAL_CONV &&
5869        eos.ShapeType() == TopAbs_FACE &&
5870        (cnvFace = eos.GetData().GetConvexFace( eos._shapeID )) &&
5871        !cnvFace->_normalsFixedOnBorders )
5872   {
5873     // put on the surface nodes built on FACE boundaries
5874     SMESH_subMeshIteratorPtr smIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false);
5875     while ( smIt->more() )
5876     {
5877       SMESH_subMesh*     sm = smIt->next();
5878       _EdgesOnShape* subEOS = eos.GetData().GetShapeEdges( sm->GetId() );
5879       if ( !subEOS->_sWOL.IsNull() ) continue;
5880       if ( std::find( eosC1.begin(), eosC1.end(), subEOS ) != eosC1.end() ) continue;
5881
5882       putOnOffsetSurface( *subEOS, infStep, eosC1, smooStep, _LayerEdge::UPD_NORMAL_CONV );
5883     }
5884     cnvFace->_normalsFixedOnBorders = true;
5885   }
5886
5887
5888   // bos #20643
5889   // negative smooStep means "final step", where we don't treat RISKY_SWOL edges
5890   // as edges based on FACE are a bit late comparing with them
5891   if ( smooStep >= 0 &&
5892        neighborHasRiskySWOL &&
5893        moveAll != _LayerEdge::RISKY_SWOL &&
5894        eos.ShapeType() == TopAbs_FACE )
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::RISKY_SWOL );
5906     }
5907   }
5908 }
5909
5910 //================================================================================
5911 /*!
5912  * \brief Return a curve of the EDGE to be used for smoothing and arrange
5913  *        _LayerEdge's to be in a consequent order
5914  */
5915 //================================================================================
5916
5917 Handle(Geom_Curve) _Smoother1D::CurveForSmooth( const TopoDS_Edge&  E,
5918                                                 _EdgesOnShape&      eos,
5919                                                 SMESH_MesherHelper& helper)
5920 {
5921   SMESHDS_SubMesh* smDS = eos._subMesh->GetSubMeshDS();
5922
5923   TopLoc_Location loc; double f,l;
5924
5925   Handle(Geom_Line)   line;
5926   Handle(Geom_Circle) circle;
5927   bool isLine, isCirc;
5928   if ( eos._sWOL.IsNull() ) /////////////////////////////////////////// 3D case
5929   {
5930     // check if the EDGE is a line
5931     Handle(Geom_Curve) curve = BRep_Tool::Curve( E, f, l);
5932     if ( curve->IsKind( STANDARD_TYPE( Geom_TrimmedCurve )))
5933       curve = Handle(Geom_TrimmedCurve)::DownCast( curve )->BasisCurve();
5934
5935     line   = Handle(Geom_Line)::DownCast( curve );
5936     circle = Handle(Geom_Circle)::DownCast( curve );
5937     isLine = (!line.IsNull());
5938     isCirc = (!circle.IsNull());
5939
5940     if ( !isLine && !isCirc ) // Check if the EDGE is close to a line
5941     {
5942       isLine = SMESH_Algo::IsStraight( E );
5943
5944       if ( isLine )
5945         line = new Geom_Line( gp::OX() ); // only type does matter
5946     }
5947     if ( !isLine && !isCirc && eos._edges.size() > 2) // Check if the EDGE is close to a circle
5948     {
5949       // TODO
5950     }
5951   }
5952   else //////////////////////////////////////////////////////////////////////// 2D case
5953   {
5954     if ( !eos._isRegularSWOL ) // 23190
5955       return NULL;
5956
5957     const TopoDS_Face& F = TopoDS::Face( eos._sWOL );
5958
5959     // check if the EDGE is a line
5960     Handle(Geom2d_Curve) curve = BRep_Tool::CurveOnSurface( E, F, f, l );
5961     if ( curve->IsKind( STANDARD_TYPE( Geom2d_TrimmedCurve )))
5962       curve = Handle(Geom2d_TrimmedCurve)::DownCast( curve )->BasisCurve();
5963
5964     Handle(Geom2d_Line)   line2d   = Handle(Geom2d_Line)::DownCast( curve );
5965     Handle(Geom2d_Circle) circle2d = Handle(Geom2d_Circle)::DownCast( curve );
5966     isLine = (!line2d.IsNull());
5967     isCirc = (!circle2d.IsNull());
5968
5969     if ( !isLine && !isCirc ) // Check if the EDGE is close to a line
5970     {
5971       Bnd_B2d bndBox;
5972       SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
5973       while ( nIt->more() )
5974         bndBox.Add( helper.GetNodeUV( F, nIt->next() ));
5975       gp_XY size = bndBox.CornerMax() - bndBox.CornerMin();
5976
5977       const double lineTol = 1e-2 * sqrt( bndBox.SquareExtent() );
5978       for ( int i = 0; i < 2 && !isLine; ++i )
5979         isLine = ( size.Coord( i+1 ) <= lineTol );
5980     }
5981     if ( !isLine && !isCirc && eos._edges.size() > 2 ) // Check if the EDGE is close to a circle
5982     {
5983       // TODO
5984     }
5985     if ( isLine )
5986     {
5987       line = new Geom_Line( gp::OX() ); // only type does matter
5988     }
5989     else if ( isCirc )
5990     {
5991       gp_Pnt2d p = circle2d->Location();
5992       gp_Ax2 ax( gp_Pnt( p.X(), p.Y(), 0), gp::DX());
5993       circle = new Geom_Circle( ax, 1.); // only center position does matter
5994     }
5995   }
5996
5997   if ( isLine )
5998     return line;
5999   if ( isCirc )
6000     return circle;
6001
6002   return Handle(Geom_Curve)();
6003 }
6004
6005 //================================================================================
6006 /*!
6007  * \brief Smooth edges on EDGE
6008  */
6009 //================================================================================
6010
6011 bool _Smoother1D::Perform(_SolidData&                    data,
6012                           Handle(ShapeAnalysis_Surface)& surface,
6013                           const TopoDS_Face&             F,
6014                           SMESH_MesherHelper&            helper )
6015 {
6016   if ( _leParams.empty() || ( !isAnalytic() && _offPoints.empty() ))
6017     prepare( data );
6018
6019   findEdgesToSmooth();
6020   if ( isAnalytic() )
6021     return smoothAnalyticEdge( data, surface, F, helper );
6022   else
6023     return smoothComplexEdge ( data, surface, F, helper );
6024 }
6025
6026 //================================================================================
6027 /*!
6028  * \brief Find edges to smooth
6029  */
6030 //================================================================================
6031
6032 void _Smoother1D::findEdgesToSmooth()
6033 {
6034   _LayerEdge* leOnV[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
6035   for ( int iEnd = 0; iEnd < 2; ++iEnd )
6036     if ( leOnV[iEnd]->Is( _LayerEdge::NORMAL_UPDATED ))
6037       _leOnV[iEnd]._cosin = Abs( _edgeDir[iEnd].Normalized() * leOnV[iEnd]->_normal );
6038
6039   _eToSmooth[0].first = _eToSmooth[0].second = 0;
6040
6041   for ( size_t i = 0; i < _eos.size(); ++i )
6042   {
6043     if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH ))
6044     {
6045       if ( needSmoothing( _leOnV[0]._cosin,
6046                           _eos[i]->_len * leOnV[0]->_lenFactor, _curveLen * _leParams[i] ) ||
6047            isToSmooth( i )
6048            )
6049         _eos[i]->Set( _LayerEdge::TO_SMOOTH );
6050       else
6051         break;
6052     }
6053     _eToSmooth[0].second = i+1;
6054   }
6055
6056   _eToSmooth[1].first = _eToSmooth[1].second = _eos.size();
6057
6058   for ( int i = _eos.size() - 1; i >= _eToSmooth[0].second; --i )
6059   {
6060     if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH ))
6061     {
6062       if ( needSmoothing( _leOnV[1]._cosin,
6063                           _eos[i]->_len * leOnV[1]->_lenFactor, _curveLen * ( 1.-_leParams[i] )) ||
6064            isToSmooth( i ))
6065         _eos[i]->Set( _LayerEdge::TO_SMOOTH );
6066       else
6067         break;
6068     }
6069     _eToSmooth[1].first = i;
6070   }
6071 }
6072
6073 //================================================================================
6074 /*!
6075  * \brief Check if iE-th _LayerEdge needs smoothing
6076  */
6077 //================================================================================
6078
6079 bool _Smoother1D::isToSmooth( int iE )
6080 {
6081   SMESH_NodeXYZ pi( _eos[iE]->_nodes[0] );
6082   SMESH_NodeXYZ p0( _eos[iE]->_2neibors->srcNode(0) );
6083   SMESH_NodeXYZ p1( _eos[iE]->_2neibors->srcNode(1) );
6084   gp_XYZ       seg0 = pi - p0;
6085   gp_XYZ       seg1 = p1 - pi;
6086   gp_XYZ    tangent =  seg0 + seg1;
6087   double tangentLen = tangent.Modulus();
6088   double  segMinLen = Min( seg0.Modulus(), seg1.Modulus() );
6089   if ( tangentLen < std::numeric_limits<double>::min() )
6090     return false;
6091   tangent /= tangentLen;
6092
6093   for ( size_t i = 0; i < _eos[iE]->_neibors.size(); ++i )
6094   {
6095     _LayerEdge* ne = _eos[iE]->_neibors[i];
6096     if ( !ne->Is( _LayerEdge::TO_SMOOTH ) ||
6097          ne->_nodes.size() < 2 ||
6098          ne->_nodes[0]->GetPosition()->GetDim() != 2 )
6099       continue;
6100     gp_XYZ edgeVec = SMESH_NodeXYZ( ne->_nodes.back() ) - SMESH_NodeXYZ( ne->_nodes[0] );
6101     double    proj = edgeVec * tangent;
6102     if ( needSmoothing( 1., proj, segMinLen ))
6103       return true;
6104   }
6105   return false;
6106 }
6107
6108 //================================================================================
6109 /*!
6110  * \brief smooth _LayerEdge's on a staight EDGE or circular EDGE
6111  */
6112 //================================================================================
6113
6114 bool _Smoother1D::smoothAnalyticEdge( _SolidData&                    data,
6115                                       Handle(ShapeAnalysis_Surface)& surface,
6116                                       const TopoDS_Face&             F,
6117                                       SMESH_MesherHelper&            helper)
6118 {
6119   if ( !isAnalytic() ) return false;
6120
6121   size_t iFrom = 0, iTo = _eos._edges.size();
6122
6123   if ( _anaCurve->IsKind( STANDARD_TYPE( Geom_Line )))
6124   {
6125     if ( F.IsNull() ) // 3D
6126     {
6127       SMESH_TNodeXYZ pSrc0( _eos._edges[iFrom]->_2neibors->srcNode(0) );
6128       SMESH_TNodeXYZ pSrc1( _eos._edges[iTo-1]->_2neibors->srcNode(1) );
6129       //const   gp_XYZ lineDir = pSrc1 - pSrc0;
6130       //_LayerEdge* vLE0 = getLEdgeOnV( 0 );
6131       //_LayerEdge* vLE1 = getLEdgeOnV( 1 );
6132       // bool shiftOnly = ( vLE0->Is( _LayerEdge::NORMAL_UPDATED ) ||
6133       //                    vLE0->Is( _LayerEdge::BLOCKED ) ||
6134       //                    vLE1->Is( _LayerEdge::NORMAL_UPDATED ) ||
6135       //                    vLE1->Is( _LayerEdge::BLOCKED ));
6136       for ( int iEnd = 0; iEnd < 2; ++iEnd )
6137       {
6138         iFrom = _eToSmooth[ iEnd ].first, iTo = _eToSmooth[ iEnd ].second;
6139         if ( iFrom >= iTo ) continue;
6140         SMESH_TNodeXYZ p0( _eos[iFrom]->_2neibors->tgtNode(0) );
6141         SMESH_TNodeXYZ p1( _eos[iTo-1]->_2neibors->tgtNode(1) );
6142         double param0 = ( iFrom == 0 ) ? 0. : _leParams[ iFrom-1 ];
6143         double param1 = _leParams[ iTo ];
6144         for ( size_t i = iFrom; i < iTo; ++i )
6145         {
6146           _LayerEdge*       edge = _eos[i];
6147           SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edge->_nodes.back() );
6148           double           param = ( _leParams[i] - param0 ) / ( param1 - param0 );
6149           gp_XYZ          newPos = p0 * ( 1. - param ) + p1 * param;
6150
6151           // if ( shiftOnly || edge->Is( _LayerEdge::NORMAL_UPDATED ))
6152           // {
6153           //   gp_XYZ curPos = SMESH_TNodeXYZ ( tgtNode );
6154           //   double  shift = ( lineDir * ( newPos - pSrc0 ) -
6155           //                     lineDir * ( curPos - pSrc0 ));
6156           //   newPos = curPos + lineDir * shift / lineDir.SquareModulus();
6157           // }
6158           if ( edge->Is( _LayerEdge::BLOCKED ))
6159           {
6160             SMESH_TNodeXYZ pSrc( edge->_nodes[0] );
6161             double curThick = pSrc.SquareDistance( tgtNode );
6162             double newThink = ( pSrc - newPos ).SquareModulus();
6163             if ( newThink > curThick )
6164               continue;
6165           }
6166           edge->_pos.back() = newPos;
6167           tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
6168           dumpMove( tgtNode );
6169         }
6170       }
6171     }
6172     else // 2D
6173     {
6174       _LayerEdge* eV0 = getLEdgeOnV( 0 );
6175       _LayerEdge* eV1 = getLEdgeOnV( 1 );
6176       gp_XY      uvV0 = eV0->LastUV( F, *data.GetShapeEdges( eV0 ));
6177       gp_XY      uvV1 = eV1->LastUV( F, *data.GetShapeEdges( eV1 ));
6178       if ( eV0->_nodes.back() == eV1->_nodes.back() ) // closed edge
6179       {
6180         int iPeriodic = helper.GetPeriodicIndex();
6181         if ( iPeriodic == 1 || iPeriodic == 2 )
6182         {
6183           uvV1.SetCoord( iPeriodic, helper.GetOtherParam( uvV1.Coord( iPeriodic )));
6184           if ( uvV0.Coord( iPeriodic ) > uvV1.Coord( iPeriodic ))
6185             std::swap( uvV0, uvV1 );
6186         }
6187       }
6188       for ( int iEnd = 0; iEnd < 2; ++iEnd )
6189       {
6190         iFrom = _eToSmooth[ iEnd ].first, iTo = _eToSmooth[ iEnd ].second;
6191         if ( iFrom >= iTo ) continue;
6192         _LayerEdge* e0 = _eos[iFrom]->_2neibors->_edges[0];
6193         _LayerEdge* e1 = _eos[iTo-1]->_2neibors->_edges[1];
6194         gp_XY      uv0 = ( e0 == eV0 ) ? uvV0 : e0->LastUV( F, _eos );
6195         gp_XY      uv1 = ( e1 == eV1 ) ? uvV1 : e1->LastUV( F, _eos );
6196         double  param0 = ( iFrom == 0 ) ? 0. : _leParams[ iFrom-1 ];
6197         double  param1 = _leParams[ iTo ];
6198         gp_XY  rangeUV = uv1 - uv0;
6199         for ( size_t i = iFrom; i < iTo; ++i )
6200         {
6201           if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
6202           double param = ( _leParams[i] - param0 ) / ( param1 - param0 );
6203           gp_XY newUV = uv0 + param * rangeUV;
6204
6205           gp_Pnt newPos = surface->Value( newUV.X(), newUV.Y() );
6206           SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos[i]->_nodes.back() );
6207           tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
6208           dumpMove( tgtNode );
6209
6210           if ( SMDS_FacePositionPtr pos = tgtNode->GetPosition() ) // NULL if F is noShrink
6211           {
6212             pos->SetUParameter( newUV.X() );
6213             pos->SetVParameter( newUV.Y() );
6214           }
6215
6216           gp_XYZ newUV0( newUV.X(), newUV.Y(), 0 );
6217
6218           if ( !_eos[i]->Is( _LayerEdge::SMOOTHED ))
6219           {
6220             _eos[i]->Set( _LayerEdge::SMOOTHED ); // to check in refine() (IPAL54237)
6221             if ( _eos[i]->_pos.size() > 2 )
6222             {
6223               // modify previous positions to make _LayerEdge less sharply bent
6224               vector<gp_XYZ>& uvVec = _eos[i]->_pos;
6225               const gp_XYZ  uvShift = newUV0 - uvVec.back();
6226               const double     len2 = ( uvVec.back() - uvVec[ 0 ] ).SquareModulus();
6227               int iPrev = uvVec.size() - 2;
6228               while ( iPrev > 0 )
6229               {
6230                 double r = ( uvVec[ iPrev ] - uvVec[0] ).SquareModulus() / len2;
6231                 uvVec[ iPrev ] += uvShift * r;
6232                 --iPrev;
6233               }
6234             }
6235           }
6236           _eos[i]->_pos.back() = newUV0;
6237         }
6238       }
6239     }
6240     return true;
6241   }
6242
6243   if ( _anaCurve->IsKind( STANDARD_TYPE( Geom_Circle )))
6244   {
6245     Handle(Geom_Circle) circle = Handle(Geom_Circle)::DownCast( _anaCurve );
6246     gp_Pnt center3D = circle->Location();
6247
6248     if ( F.IsNull() ) // 3D
6249     {
6250       if ( getLEdgeOnV( 0 )->_nodes.back() == getLEdgeOnV( 1 )->_nodes.back() )
6251         return true; // closed EDGE - nothing to do
6252
6253       // circle is a real curve of EDGE
6254       gp_Circ circ = circle->Circ();
6255
6256       // new center is shifted along its axis
6257       const gp_Dir& axis = circ.Axis().Direction();
6258       _LayerEdge*     e0 = getLEdgeOnV(0);
6259       _LayerEdge*     e1 = getLEdgeOnV(1);
6260       SMESH_TNodeXYZ  p0 = e0->_nodes.back();
6261       SMESH_TNodeXYZ  p1 = e1->_nodes.back();
6262       double      shift1 = axis.XYZ() * ( p0 - center3D.XYZ() );
6263       double      shift2 = axis.XYZ() * ( p1 - center3D.XYZ() );
6264       gp_Pnt   newCenter = center3D.XYZ() + axis.XYZ() * 0.5 * ( shift1 + shift2 );
6265
6266       double newRadius = 0.5 * ( newCenter.Distance( p0 ) + newCenter.Distance( p1 ));
6267
6268       gp_Ax2  newAxis( newCenter, axis, gp_Vec( newCenter, p0 ));
6269       gp_Circ newCirc( newAxis, newRadius );
6270       gp_Vec  vecC1  ( newCenter, p1 );
6271
6272       double uLast = newAxis.XDirection().AngleWithRef( vecC1, newAxis.Direction() ); // -PI - +PI
6273       if ( uLast < 0 )
6274         uLast += 2 * M_PI;
6275       
6276       for ( size_t i = 0; i < _eos.size(); ++i )
6277       {
6278         if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
6279         //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
6280         double u = uLast * _leParams[i];
6281         gp_Pnt p = ElCLib::Value( u, newCirc );
6282         _eos._edges[i]->_pos.back() = p.XYZ();
6283
6284         SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos._edges[i]->_nodes.back() );
6285         tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
6286         dumpMove( tgtNode );
6287       }
6288       return true;
6289     }
6290     else // 2D
6291     {
6292       const gp_XY center( center3D.X(), center3D.Y() );
6293
6294       _LayerEdge* e0 = getLEdgeOnV(0);
6295       _LayerEdge* eM = _eos._edges[ 0 ];
6296       _LayerEdge* e1 = getLEdgeOnV(1);
6297       gp_XY      uv0 = e0->LastUV( F, *data.GetShapeEdges( e0 ) );
6298       gp_XY      uvM = eM->LastUV( F, *data.GetShapeEdges( eM ) );
6299       gp_XY      uv1 = e1->LastUV( F, *data.GetShapeEdges( e1 ) );
6300       gp_Vec2d vec0( center, uv0 );
6301       gp_Vec2d vecM( center, uvM );
6302       gp_Vec2d vec1( center, uv1 );
6303       double uLast = vec0.Angle( vec1 ); // -PI - +PI
6304       double uMidl = vec0.Angle( vecM );
6305       if ( uLast * uMidl <= 0. )
6306         uLast += ( uMidl > 0 ? +2. : -2. ) * M_PI;
6307       const double radius = 0.5 * ( vec0.Magnitude() + vec1.Magnitude() );
6308
6309       gp_Ax2d   axis( center, vec0 );
6310       gp_Circ2d circ( axis, radius );
6311       for ( size_t i = 0; i < _eos.size(); ++i )
6312       {
6313         if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
6314         //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
6315         double    newU = uLast * _leParams[i];
6316         gp_Pnt2d newUV = ElCLib::Value( newU, circ );
6317         _eos._edges[i]->_pos.back().SetCoord( newUV.X(), newUV.Y(), 0 );
6318
6319         gp_Pnt newPos = surface->Value( newUV.X(), newUV.Y() );
6320         SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos._edges[i]->_nodes.back() );
6321         tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
6322         dumpMove( tgtNode );
6323
6324         if ( SMDS_FacePositionPtr pos = tgtNode->GetPosition() ) // NULL if F is noShrink
6325         {
6326           pos->SetUParameter( newUV.X() );
6327           pos->SetVParameter( newUV.Y() );
6328         }
6329         _eos[i]->Set( _LayerEdge::SMOOTHED ); // to check in refine() (IPAL54237)
6330       }
6331     }
6332     return true;
6333   }
6334
6335   return false;
6336 }
6337
6338 //================================================================================
6339 /*!
6340  * \brief smooth _LayerEdge's on a an EDGE
6341  */
6342 //================================================================================
6343
6344 bool _Smoother1D::smoothComplexEdge( _SolidData&                    /*data*/,
6345                                      Handle(ShapeAnalysis_Surface)& surface,
6346                                      const TopoDS_Face&             F,
6347                                      SMESH_MesherHelper&            /*helper*/)
6348 {
6349   if ( _offPoints.empty() )
6350     return false;
6351
6352   // ----------------------------------------------
6353   // move _offPoints along normals of _LayerEdge's
6354   // ----------------------------------------------
6355
6356   _LayerEdge* e[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
6357   if ( e[0]->Is( _LayerEdge::NORMAL_UPDATED ))
6358     _leOnV[0]._normal = getNormalNormal( e[0]->_normal, _edgeDir[0] );
6359   if ( e[1]->Is( _LayerEdge::NORMAL_UPDATED )) 
6360     _leOnV[1]._normal = getNormalNormal( e[1]->_normal, _edgeDir[1] );
6361   _leOnV[0]._len = e[0]->_len;
6362   _leOnV[1]._len = e[1]->_len;
6363   for ( size_t i = 0; i < _offPoints.size(); i++ )
6364   {
6365     _LayerEdge*  e0 = _offPoints[i]._2edges._edges[0];
6366     _LayerEdge*  e1 = _offPoints[i]._2edges._edges[1];
6367     const double w0 = _offPoints[i]._2edges._wgt[0];
6368     const double w1 = _offPoints[i]._2edges._wgt[1];
6369     gp_XYZ  avgNorm = ( e0->_normal    * w0 + e1->_normal    * w1 ).Normalized();
6370     double  avgLen  = ( e0->_len       * w0 + e1->_len       * w1 );
6371     double  avgFact = ( e0->_lenFactor * w0 + e1->_lenFactor * w1 );
6372     if ( e0->Is( _LayerEdge::NORMAL_UPDATED ) ||
6373          e1->Is( _LayerEdge::NORMAL_UPDATED ))
6374       avgNorm = getNormalNormal( avgNorm, _offPoints[i]._edgeDir );
6375
6376     _offPoints[i]._xyz += avgNorm * ( avgLen - _offPoints[i]._len ) * avgFact;
6377     _offPoints[i]._len  = avgLen;
6378   }
6379
6380   double fTol = 0;
6381   if ( !surface.IsNull() ) // project _offPoints to the FACE
6382   {
6383     fTol = 100 * BRep_Tool::Tolerance( F );
6384     //const double segLen = _offPoints[0].Distance( _offPoints[1] );
6385
6386     gp_Pnt2d uv = surface->ValueOfUV( _offPoints[0]._xyz, fTol );
6387     //if ( surface->Gap() < 0.5 * segLen )
6388       _offPoints[0]._xyz = surface->Value( uv ).XYZ();
6389
6390     for ( size_t i = 1; i < _offPoints.size(); ++i )
6391     {
6392       uv = surface->NextValueOfUV( uv, _offPoints[i]._xyz, fTol );
6393       //if ( surface->Gap() < 0.5 * segLen )
6394         _offPoints[i]._xyz = surface->Value( uv ).XYZ();
6395     }
6396   }
6397
6398   // -----------------------------------------------------------------
6399   // project tgt nodes of extreme _LayerEdge's to the offset segments
6400   // -----------------------------------------------------------------
6401
6402   const int updatedOrBlocked = _LayerEdge::NORMAL_UPDATED | _LayerEdge::BLOCKED;
6403   if ( e[0]->Is( updatedOrBlocked )) _iSeg[0] = 0;
6404   if ( e[1]->Is( updatedOrBlocked )) _iSeg[1] = _offPoints.size()-2;
6405
6406   gp_Pnt pExtreme[2], pProj[2];
6407   bool isProjected[2];
6408   for ( int is2nd = 0; is2nd < 2; ++is2nd )
6409   {
6410     pExtreme[ is2nd ] = SMESH_TNodeXYZ( e[is2nd]->_nodes.back() );
6411     int  i = _iSeg[ is2nd ];
6412     int di = is2nd ? -1 : +1;
6413     bool & projected = isProjected[ is2nd ];
6414     projected = false;
6415     double uOnSeg, distMin = Precision::Infinite(), dist, distPrev = 0;
6416     int nbWorse = 0;
6417     do {
6418       gp_Vec v0p( _offPoints[i]._xyz, pExtreme[ is2nd ]    );
6419       gp_Vec v01( _offPoints[i]._xyz, _offPoints[i+1]._xyz );
6420       uOnSeg     = ( v0p * v01 ) / v01.SquareMagnitude();  // param [0,1] along v01
6421       projected  = ( Abs( uOnSeg - 0.5 ) <= 0.5 );
6422       dist       =  pExtreme[ is2nd ].SquareDistance( _offPoints[ i + ( uOnSeg > 0.5 )]._xyz );
6423       if ( dist < distMin || projected )
6424       {
6425         _iSeg[ is2nd ] = i;
6426         pProj[ is2nd ] = _offPoints[i]._xyz + ( v01 * uOnSeg ).XYZ();
6427         distMin = dist;
6428       }
6429       else if ( dist > distPrev )
6430       {
6431         if ( ++nbWorse > 3 ) // avoid projection to the middle of a closed EDGE
6432           break;
6433       }
6434       distPrev = dist;
6435       i += di;
6436     }
6437     while ( !projected &&
6438             i >= 0 && i+1 < (int)_offPoints.size() );
6439
6440     if ( !projected )
6441     {
6442       if (( is2nd && _iSeg[1] != _offPoints.size()-2 ) || ( !is2nd && _iSeg[0] != 0 ))
6443       {
6444         _iSeg[0] = 0;
6445         _iSeg[1] = _offPoints.size()-2;
6446         debugMsg( "smoothComplexEdge() failed to project nodes of extreme _LayerEdge's" );
6447         return false;
6448       }
6449     }
6450   }
6451   if ( _iSeg[0] > _iSeg[1] )
6452   {
6453     debugMsg( "smoothComplexEdge() incorrectly projected nodes of extreme _LayerEdge's" );
6454     return false;
6455   }
6456
6457   // adjust length of extreme LE (test viscous_layers_01/B7)
6458   gp_Vec vDiv0( pExtreme[0], pProj[0] );
6459   gp_Vec vDiv1( pExtreme[1], pProj[1] );
6460   double d0 = vDiv0.Magnitude();
6461   double d1 = isProjected[1] ? vDiv1.Magnitude() : 0;
6462   if ( e[0]->Is( _LayerEdge::BLOCKED )) {
6463     if ( e[0]->_normal * vDiv0.XYZ() < 0 ) e[0]->_len += d0;
6464     else                                   e[0]->_len -= d0;
6465   }
6466   if ( e[1]->Is( _LayerEdge::BLOCKED )) {
6467     if ( e[1]->_normal * vDiv1.XYZ() < 0 ) e[1]->_len += d1;
6468     else                                   e[1]->_len -= d1;
6469   }
6470
6471   // ---------------------------------------------------------------------------------
6472   // compute normalized length of the offset segments located between the projections
6473   // ---------------------------------------------------------------------------------
6474
6475   // temporary replace extreme _offPoints by pExtreme
6476   gp_XYZ opXYZ[2] = { _offPoints[ _iSeg[0]   ]._xyz,
6477                       _offPoints[ _iSeg[1]+1 ]._xyz };
6478   _offPoints[ _iSeg[0]   ]._xyz = pExtreme[0].XYZ();
6479   _offPoints[ _iSeg[1]+ 1]._xyz = pExtreme[1].XYZ();
6480
6481   size_t iSeg = 0, nbSeg = _iSeg[1] - _iSeg[0] + 1;
6482   vector< double > len( nbSeg + 1 );
6483   len[ iSeg++ ] = 0;
6484   len[ iSeg++ ] = pProj[ 0 ].Distance( _offPoints[ _iSeg[0]+1 ]._xyz );
6485   for ( size_t i = _iSeg[0]+1; i <= _iSeg[1]; ++i, ++iSeg )
6486   {
6487     len[ iSeg ] = len[ iSeg-1 ] + _offPoints[i].Distance( _offPoints[i+1] );
6488   }
6489   // if ( isProjected[ 1 ])
6490   //   len[ nbSeg ] -= pProj[ 1 ].Distance( _offPoints[ _iSeg[1]+1 ]._xyz );
6491   // else
6492   //   len[ nbSeg ] += pExtreme[ 1 ].Distance( _offPoints[ _iSeg[1]+1 ]._xyz );
6493
6494   double fullLen = len.back() - d0 - d1;
6495   for ( iSeg = 0; iSeg < len.size(); ++iSeg )
6496     len[iSeg] = ( len[iSeg] - d0 ) / fullLen;
6497
6498   // -------------------------------------------------------------
6499   // distribute tgt nodes of _LayerEdge's between the projections
6500   // -------------------------------------------------------------
6501
6502   iSeg = 0;
6503   for ( size_t i = 0; i < _eos.size(); ++i )
6504   {
6505     if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
6506     //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
6507     while ( iSeg+2 < len.size() && _leParams[i] > len[ iSeg+1 ] )
6508       iSeg++;
6509     double r = ( _leParams[i] - len[ iSeg ]) / ( len[ iSeg+1 ] - len[ iSeg ]);
6510     gp_XYZ p = ( _offPoints[ iSeg + _iSeg[0]     ]._xyz * ( 1 - r ) +
6511                  _offPoints[ iSeg + _iSeg[0] + 1 ]._xyz * r );
6512
6513     if ( surface.IsNull() )
6514     {
6515       _eos[i]->_pos.back() = p;
6516     }
6517     else // project a new node position to a FACE
6518     {
6519       gp_Pnt2d uv ( _eos[i]->_pos.back().X(), _eos[i]->_pos.back().Y() );
6520       gp_Pnt2d uv2( surface->NextValueOfUV( uv, p, fTol ));
6521
6522       p = surface->Value( uv2 ).XYZ();
6523       _eos[i]->_pos.back().SetCoord( uv2.X(), uv2.Y(), 0 );
6524     }
6525     SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos[i]->_nodes.back() );
6526     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
6527     dumpMove( tgtNode );
6528   }
6529
6530   _offPoints[ _iSeg[0]   ]._xyz = opXYZ[0];
6531   _offPoints[ _iSeg[1]+1 ]._xyz = opXYZ[1];
6532
6533   return true;
6534 }
6535
6536 //================================================================================
6537 /*!
6538  * \brief Prepare for smoothing
6539  */
6540 //================================================================================
6541
6542 void _Smoother1D::prepare(_SolidData& data)
6543 {
6544   const TopoDS_Edge& E = TopoDS::Edge( _eos._shape );
6545   _curveLen = SMESH_Algo::EdgeLength( E );
6546
6547   // sort _LayerEdge's by position on the EDGE
6548   data.SortOnEdge( E, _eos._edges );
6549
6550   // compute normalized param of _eos._edges on EDGE
6551   _leParams.resize( _eos._edges.size() + 1 );
6552   {
6553     double curLen;
6554     gp_Pnt pPrev = SMESH_TNodeXYZ( getLEdgeOnV( 0 )->_nodes[0] );
6555     _leParams[0] = 0;
6556     for ( size_t i = 0; i < _eos._edges.size(); ++i )
6557     {
6558       gp_Pnt p       = SMESH_TNodeXYZ( _eos._edges[i]->_nodes[0] );
6559       curLen         = p.Distance( pPrev );
6560       _leParams[i+1] = _leParams[i] + curLen;
6561       pPrev          = p;
6562     }
6563     double fullLen = _leParams.back() + pPrev.Distance( SMESH_TNodeXYZ( getLEdgeOnV(1)->_nodes[0]));
6564     for ( size_t i = 0; i < _leParams.size()-1; ++i )
6565       _leParams[i] = _leParams[i+1] / fullLen;
6566     _leParams.back() = 1.;
6567   }
6568
6569   _LayerEdge* leOnV[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
6570
6571   // get cosin to use in findEdgesToSmooth()
6572   _edgeDir[0] = getEdgeDir( E, leOnV[0]->_nodes[0], data.GetHelper() );
6573   _edgeDir[1] = getEdgeDir( E, leOnV[1]->_nodes[0], data.GetHelper() );
6574   _leOnV[0]._cosin = Abs( leOnV[0]->_cosin );
6575   _leOnV[1]._cosin = Abs( leOnV[1]->_cosin );
6576   _leOnV[0]._flags = _leOnV[1]._flags = 0;
6577   if ( _eos._sWOL.IsNull() ) // 3D
6578     for ( int iEnd = 0; iEnd < 2; ++iEnd )
6579       _leOnV[iEnd]._cosin = Abs( _edgeDir[iEnd].Normalized() * leOnV[iEnd]->_normal );
6580
6581   if ( isAnalytic() )
6582     return;
6583
6584   // divide E to have offset segments with low deflection
6585   BRepAdaptor_Curve c3dAdaptor( E );
6586   const double curDeflect = 0.1; //0.01; // Curvature deflection == |p1p2|*sin(p1p2,p1pM)
6587   const double angDeflect = 0.1; //0.09; // Angular deflection == sin(p1pM,pMp2)
6588   GCPnts_TangentialDeflection discret(c3dAdaptor, angDeflect, curDeflect);
6589   if ( discret.NbPoints() <= 2 )
6590   {
6591     _anaCurve = new Geom_Line( gp::OX() ); // only type does matter
6592     return;
6593   }
6594
6595   const double u0 = c3dAdaptor.FirstParameter();
6596   gp_Pnt p; gp_Vec tangent;
6597   if ( discret.NbPoints() >= (int) _eos.size() + 2 )
6598   {
6599     _offPoints.resize( discret.NbPoints() );
6600     for ( size_t i = 0; i < _offPoints.size(); i++ )
6601     {
6602       double u = discret.Parameter( i+1 );
6603       c3dAdaptor.D1( u, p, tangent );
6604       _offPoints[i]._xyz     = p.XYZ();
6605       _offPoints[i]._edgeDir = tangent.XYZ();
6606       _offPoints[i]._param = GCPnts_AbscissaPoint::Length( c3dAdaptor, u0, u ) / _curveLen;
6607     }
6608   }
6609   else
6610   {
6611     std::vector< double > params( _eos.size() + 2 );
6612
6613     params[0]     = data.GetHelper().GetNodeU( E, leOnV[0]->_nodes[0] );
6614     params.back() = data.GetHelper().GetNodeU( E, leOnV[1]->_nodes[0] );
6615     for ( size_t i = 0; i < _eos.size(); i++ )
6616       params[i+1] = data.GetHelper().GetNodeU( E, _eos[i]->_nodes[0] );
6617
6618     if ( params[1] > params[ _eos.size() ] )
6619       std::reverse( params.begin() + 1, params.end() - 1 );
6620
6621     _offPoints.resize( _eos.size() + 2 );
6622     for ( size_t i = 0; i < _offPoints.size(); i++ )
6623     {
6624       const double u = params[i];
6625       c3dAdaptor.D1( u, p, tangent );
6626       _offPoints[i]._xyz     = p.XYZ();
6627       _offPoints[i]._edgeDir = tangent.XYZ();
6628       _offPoints[i]._param = GCPnts_AbscissaPoint::Length( c3dAdaptor, u0, u ) / _curveLen;
6629     }
6630   }
6631
6632   // set _2edges
6633   _offPoints    [0]._2edges.set( &_leOnV[0], &_leOnV[0], 0.5, 0.5 );
6634   _offPoints.back()._2edges.set( &_leOnV[1], &_leOnV[1], 0.5, 0.5 );
6635   _2NearEdges tmp2edges;
6636   tmp2edges._edges[1] = _eos._edges[0];
6637   _leOnV[0]._2neibors = & tmp2edges;
6638   _leOnV[0]._nodes    = leOnV[0]->_nodes;
6639   _leOnV[1]._nodes    = leOnV[1]->_nodes;
6640   _LayerEdge* eNext, *ePrev = & _leOnV[0];
6641   for ( size_t iLE = 0, i = 1; i < _offPoints.size()-1; i++ )
6642   {
6643     // find _LayerEdge's located before and after an offset point
6644     // (_eos._edges[ iLE ] is next after ePrev)
6645     while ( iLE < _eos._edges.size() && _offPoints[i]._param > _leParams[ iLE ] )
6646       ePrev = _eos._edges[ iLE++ ];
6647     eNext = ePrev->_2neibors->_edges[1];
6648
6649     gp_Pnt p0 = SMESH_TNodeXYZ( ePrev->_nodes[0] );
6650     gp_Pnt p1 = SMESH_TNodeXYZ( eNext->_nodes[0] );
6651     double  r = p0.Distance( _offPoints[i]._xyz ) / p0.Distance( p1 );
6652     _offPoints[i]._2edges.set( ePrev, eNext, 1-r, r );
6653   }
6654
6655   // replace _LayerEdge's on VERTEX by _leOnV in _offPoints._2edges
6656   for ( size_t i = 0; i < _offPoints.size(); i++ )
6657     if ( _offPoints[i]._2edges._edges[0] == leOnV[0] )
6658       _offPoints[i]._2edges._edges[0] = & _leOnV[0];
6659     else break;
6660   for ( size_t i = _offPoints.size()-1; i > 0; i-- )
6661     if ( _offPoints[i]._2edges._edges[1] == leOnV[1] )
6662       _offPoints[i]._2edges._edges[1] = & _leOnV[1];
6663     else break;
6664
6665   // set _normal of _leOnV[0] and _leOnV[1] to be normal to the EDGE
6666
6667   int iLBO = _offPoints.size() - 2; // last but one
6668
6669   if ( leOnV[ 0 ]->Is( _LayerEdge::MULTI_NORMAL ))
6670     _leOnV[ 0 ]._normal = getNormalNormal( _eos._edges[1]->_normal, _edgeDir[0] );
6671   else
6672     _leOnV[ 0 ]._normal = getNormalNormal( leOnV[0]->_normal,       _edgeDir[0] );
6673   if ( leOnV[ 1 ]->Is( _LayerEdge::MULTI_NORMAL ))
6674     _leOnV[ 1 ]._normal = getNormalNormal( _eos._edges.back()->_normal, _edgeDir[1] );
6675   else
6676     _leOnV[ 1 ]._normal = getNormalNormal( leOnV[1]->_normal,           _edgeDir[1] );
6677   _leOnV[ 0 ]._len = 0;
6678   _leOnV[ 1 ]._len = 0;
6679   _leOnV[ 0 ]._lenFactor = _offPoints[1   ]._2edges._edges[1]->_lenFactor;
6680   _leOnV[ 1 ]._lenFactor = _offPoints[iLBO]._2edges._edges[0]->_lenFactor;
6681
6682   _iSeg[0] = 0;
6683   _iSeg[1] = _offPoints.size()-2;
6684
6685   // initialize OffPnt::_len
6686   for ( size_t i = 0; i < _offPoints.size(); ++i )
6687     _offPoints[i]._len = 0;
6688
6689   if ( _eos._edges[0]->NbSteps() > 1 ) // already inflated several times, init _xyz
6690   {
6691     _leOnV[0]._len = leOnV[0]->_len;
6692     _leOnV[1]._len = leOnV[1]->_len;
6693     for ( size_t i = 0; i < _offPoints.size(); i++ )
6694     {
6695       _LayerEdge*  e0 = _offPoints[i]._2edges._edges[0];
6696       _LayerEdge*  e1 = _offPoints[i]._2edges._edges[1];
6697       const double w0 = _offPoints[i]._2edges._wgt[0];
6698       const double w1 = _offPoints[i]._2edges._wgt[1];
6699       double  avgLen  = ( e0->_len * w0 + e1->_len * w1 );
6700       gp_XYZ  avgXYZ  = ( SMESH_TNodeXYZ( e0->_nodes.back() ) * w0 +
6701                           SMESH_TNodeXYZ( e1->_nodes.back() ) * w1 );
6702       _offPoints[i]._xyz = avgXYZ;
6703       _offPoints[i]._len = avgLen;
6704     }
6705   }
6706 }
6707
6708 //================================================================================
6709 /*!
6710  * \brief return _normal of _leOnV[is2nd] normal to the EDGE
6711  */
6712 //================================================================================
6713
6714 gp_XYZ _Smoother1D::getNormalNormal( const gp_XYZ & normal,
6715                                      const gp_XYZ&  edgeDir)
6716 {
6717   gp_XYZ cross = normal ^ edgeDir;
6718   gp_XYZ  norm = edgeDir ^ cross;
6719   double  size = norm.Modulus();
6720
6721   // if ( size == 0 ) // MULTI_NORMAL _LayerEdge
6722   //   return gp_XYZ( 1e-100, 1e-100, 1e-100 );
6723
6724   if ( size < 1e-5 ) // normal || edgeDir (almost) at inflation along EDGE (bos #20643)
6725   {
6726     const _LayerEdge* le = _eos._edges[ _eos._edges.size() / 2 ];
6727     const gp_XYZ& leNorm = le->_normal;
6728
6729     cross = leNorm ^ edgeDir;
6730     norm = edgeDir ^ cross;
6731     size = norm.Modulus();
6732   }
6733
6734   return norm / size;
6735 }
6736
6737 //================================================================================
6738 /*!
6739  * \brief Writes a script creating a mesh composed of _offPoints
6740  */
6741 //================================================================================
6742
6743 void _Smoother1D::offPointsToPython() const
6744 {
6745   const char* fname = "/tmp/offPoints.py";
6746   cout << "exec(open('"<<fname<<"','rb').read() )"<<endl;
6747   ofstream py(fname);
6748   py << "import SMESH" << endl
6749      << "from salome.smesh import smeshBuilder" << endl
6750      << "smesh  = smeshBuilder.New(salome.myStudy)" << endl
6751      << "mesh   = smesh.Mesh( 'offPoints' )"<<endl;
6752   for ( size_t i = 0; i < _offPoints.size(); i++ )
6753   {
6754     py << "mesh.AddNode( "
6755        << _offPoints[i]._xyz.X() << ", "
6756        << _offPoints[i]._xyz.Y() << ", "
6757        << _offPoints[i]._xyz.Z() << " )" << endl;
6758   }
6759 }
6760
6761 //================================================================================
6762 /*!
6763  * \brief Sort _LayerEdge's by a parameter on a given EDGE
6764  */
6765 //================================================================================
6766
6767 void _SolidData::SortOnEdge( const TopoDS_Edge&     E,
6768                              vector< _LayerEdge* >& edges)
6769 {
6770   map< double, _LayerEdge* > u2edge;
6771   for ( size_t i = 0; i < edges.size(); ++i )
6772     u2edge.insert( u2edge.end(),
6773                    make_pair( _helper->GetNodeU( E, edges[i]->_nodes[0] ), edges[i] ));
6774
6775   ASSERT( u2edge.size() == edges.size() );
6776   map< double, _LayerEdge* >::iterator u2e = u2edge.begin();
6777   for ( size_t i = 0; i < edges.size(); ++i, ++u2e )
6778     edges[i] = u2e->second;
6779
6780   Sort2NeiborsOnEdge( edges );
6781 }
6782
6783 //================================================================================
6784 /*!
6785  * \brief Set _2neibors according to the order of _LayerEdge on EDGE
6786  */
6787 //================================================================================
6788
6789 void _SolidData::Sort2NeiborsOnEdge( vector< _LayerEdge* >& edges )
6790 {
6791   if ( edges.size() < 2 || !edges[0]->_2neibors ) return;
6792
6793   for ( size_t i = 0; i < edges.size()-1; ++i )
6794     if ( edges[i]->_2neibors->tgtNode(1) != edges[i+1]->_nodes.back() )
6795       edges[i]->_2neibors->reverse();
6796
6797   const size_t iLast = edges.size() - 1;
6798   if ( edges.size() > 1 &&
6799        edges[iLast]->_2neibors->tgtNode(0) != edges[iLast-1]->_nodes.back() )
6800     edges[iLast]->_2neibors->reverse();
6801 }
6802
6803 //================================================================================
6804 /*!
6805  * \brief Return _EdgesOnShape* corresponding to the shape
6806  */
6807 //================================================================================
6808
6809 _EdgesOnShape* _SolidData::GetShapeEdges(const TGeomID shapeID )
6810 {
6811   if ( shapeID < (int)_edgesOnShape.size() &&
6812        _edgesOnShape[ shapeID ]._shapeID == shapeID )
6813     return _edgesOnShape[ shapeID ]._subMesh ? & _edgesOnShape[ shapeID ] : 0;
6814
6815   for ( size_t i = 0; i < _edgesOnShape.size(); ++i )
6816     if ( _edgesOnShape[i]._shapeID == shapeID )
6817       return _edgesOnShape[i]._subMesh ? & _edgesOnShape[i] : 0;
6818
6819   return 0;
6820 }
6821
6822 //================================================================================
6823 /*!
6824  * \brief Return _EdgesOnShape* corresponding to the shape
6825  */
6826 //================================================================================
6827
6828 _EdgesOnShape* _SolidData::GetShapeEdges(const TopoDS_Shape& shape )
6829 {
6830   SMESHDS_Mesh* meshDS = _proxyMesh->GetMesh()->GetMeshDS();
6831   return GetShapeEdges( meshDS->ShapeToIndex( shape ));
6832 }
6833
6834 //================================================================================
6835 /*!
6836  * \brief Prepare data of the _LayerEdge for smoothing on FACE
6837  */
6838 //================================================================================
6839
6840 void _SolidData::PrepareEdgesToSmoothOnFace( _EdgesOnShape* eos, bool substituteSrcNodes )
6841 {
6842   SMESH_MesherHelper helper( *_proxyMesh->GetMesh() );
6843
6844   set< TGeomID > vertices;
6845   TopoDS_Face F;
6846   if ( eos->ShapeType() == TopAbs_FACE )
6847   {
6848     // check FACE concavity and get concave VERTEXes
6849     F = TopoDS::Face( eos->_shape );
6850     if ( isConcave( F, helper, &vertices ))
6851       _concaveFaces.insert( eos->_shapeID );
6852
6853     // set eos._eosConcaVer
6854     eos->_eosConcaVer.clear();
6855     eos->_eosConcaVer.reserve( vertices.size() );
6856     for ( set< TGeomID >::iterator v = vertices.begin(); v != vertices.end(); ++v )
6857     {
6858       _EdgesOnShape* eov = GetShapeEdges( *v );
6859       if ( eov && eov->_edges.size() == 1 )
6860       {
6861         eos->_eosConcaVer.push_back( eov );
6862         for ( size_t i = 0; i < eov->_edges[0]->_neibors.size(); ++i )
6863           eov->_edges[0]->_neibors[i]->Set( _LayerEdge::DIFFICULT );
6864       }
6865     }
6866
6867     // SetSmooLen() to _LayerEdge's on FACE
6868     // for ( size_t i = 0; i < eos->_edges.size(); ++i )
6869     // {
6870     //   eos->_edges[i]->SetSmooLen( Precision::Infinite() );
6871     // }
6872     // SMESH_subMeshIteratorPtr smIt = eos->_subMesh->getDependsOnIterator(/*includeSelf=*/false);
6873     // while ( smIt->more() ) // loop on sub-shapes of the FACE
6874     // {
6875     //   _EdgesOnShape* eoe = GetShapeEdges( smIt->next()->GetId() );
6876     //   if ( !eoe ) continue;
6877
6878     //   vector<_LayerEdge*>& eE = eoe->_edges;
6879     //   for ( size_t iE = 0; iE < eE.size(); ++iE ) // loop on _LayerEdge's on EDGE or VERTEX
6880     //   {
6881     //     if ( eE[iE]->_cosin <= theMinSmoothCosin )
6882     //       continue;
6883
6884     //     SMDS_ElemIteratorPtr segIt = eE[iE]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Edge);
6885     //     while ( segIt->more() )
6886     //     {
6887     //       const SMDS_MeshElement* seg = segIt->next();
6888     //       if ( !eos->_subMesh->DependsOn( seg->getshapeId() ))
6889     //         continue;
6890     //       if ( seg->GetNode(0) != eE[iE]->_nodes[0] )
6891     //         continue; // not to check a seg twice
6892     //       for ( size_t iN = 0; iN < eE[iE]->_neibors.size(); ++iN )
6893     //       {
6894     //         _LayerEdge* eN = eE[iE]->_neibors[iN];
6895     //         if ( eN->_nodes[0]->getshapeId() != eos->_shapeID )
6896     //           continue;
6897     //         double dist    = SMESH_MeshAlgos::GetDistance( seg, SMESH_TNodeXYZ( eN->_nodes[0] ));
6898     //         double smooLen = getSmoothingThickness( eE[iE]->_cosin, dist );
6899     //         eN->SetSmooLen( Min( smooLen, eN->GetSmooLen() ));
6900     //         eN->Set( _LayerEdge::NEAR_BOUNDARY );
6901     //       }
6902     //     }
6903     //   }
6904     // }
6905   } // if ( eos->ShapeType() == TopAbs_FACE )
6906
6907   for ( size_t i = 0; i < eos->_edges.size(); ++i )
6908   {
6909     eos->_edges[i]->_smooFunction = 0;
6910     eos->_edges[i]->Set( _LayerEdge::TO_SMOOTH );
6911   }
6912   bool isCurved = false;
6913   for ( size_t i = 0; i < eos->_edges.size(); ++i )
6914   {
6915     _LayerEdge* edge = eos->_edges[i];
6916
6917     // get simplices sorted
6918     _Simplex::SortSimplices( edge->_simplices );
6919
6920     // smoothing function
6921     edge->ChooseSmooFunction( vertices, _n2eMap );
6922
6923     // set _curvature
6924     double avgNormProj = 0, avgLen = 0;
6925     for ( size_t iS = 0; iS < edge->_simplices.size(); ++iS )
6926     {
6927       _Simplex& s = edge->_simplices[iS];
6928
6929       gp_XYZ  vec = edge->_pos.back() - SMESH_TNodeXYZ( s._nPrev );
6930       avgNormProj += edge->_normal * vec;
6931       avgLen      += vec.Modulus();
6932       if ( substituteSrcNodes )
6933       {
6934         s._nNext = _n2eMap[ s._nNext ]->_nodes.back();
6935         s._nPrev = _n2eMap[ s._nPrev ]->_nodes.back();
6936       }
6937     }
6938     avgNormProj /= edge->_simplices.size();
6939     avgLen      /= edge->_simplices.size();
6940     if (( edge->_curvature = _Curvature::New( avgNormProj, avgLen )))
6941     {
6942       edge->Set( _LayerEdge::SMOOTHED_C1 );
6943       isCurved = true;
6944       SMDS_FacePositionPtr fPos = edge->_nodes[0]->GetPosition();
6945       if ( !fPos )
6946         for ( size_t iS = 0; iS < edge->_simplices.size()  &&  !fPos; ++iS )
6947           fPos = edge->_simplices[iS]._nPrev->GetPosition();
6948       if ( fPos )
6949         edge->_curvature->_uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
6950     }
6951   }
6952
6953   // prepare for putOnOffsetSurface()
6954   if (( eos->ShapeType() == TopAbs_FACE ) &&
6955       ( isCurved || !eos->_eosConcaVer.empty() ))
6956   {
6957     eos->_offsetSurf = helper.GetSurface( TopoDS::Face( eos->_shape ));
6958     eos->_edgeForOffset = 0;
6959
6960     double maxCosin = -1;
6961     //bool hasNoShrink = false;
6962     for ( TopExp_Explorer eExp( eos->_shape, TopAbs_EDGE ); eExp.More(); eExp.Next() )
6963     {
6964       _EdgesOnShape* eoe = GetShapeEdges( eExp.Current() );
6965       if ( !eoe || eoe->_edges.empty() ) continue;
6966
6967       // if ( eos->GetData()._noShrinkShapes.count( eoe->_shapeID ))
6968       //   hasNoShrink = true;
6969
6970       vector<_LayerEdge*>& eE = eoe->_edges;
6971       _LayerEdge* e = eE[ eE.size() / 2 ];
6972       if ( !e->Is( _LayerEdge::RISKY_SWOL ) && e->_cosin > maxCosin )
6973       {
6974         eos->_edgeForOffset = e;
6975         maxCosin = e->_cosin;
6976       }
6977
6978       if ( !eoe->_sWOL.IsNull() )
6979         for ( _LayerEdge* le : eoe->_edges )
6980           if ( le->Is( _LayerEdge::RISKY_SWOL ) && e->_cosin > 0 )
6981           {
6982             // make _neibors on FACE be smoothed after le->Is( BLOCKED )
6983             for ( _LayerEdge* neibor : le->_neibors )
6984             {
6985               int shapeDim =  neibor->BaseShapeDim();
6986               if ( shapeDim == 2 )
6987                 neibor->Set( _LayerEdge::NEAR_BOUNDARY ); // on FACE
6988               else if ( shapeDim == 0 )
6989                 neibor->Set( _LayerEdge::RISKY_SWOL );    // on VERTEX
6990
6991               if ( !neibor->_curvature )
6992               {
6993                 gp_XY uv = helper.GetNodeUV( F, neibor->_nodes[0] );
6994                 neibor->_curvature = _Factory::NewCurvature();
6995                 neibor->_curvature->_r = 0;
6996                 neibor->_curvature->_k = 0;
6997                 neibor->_curvature->_h2lenRatio = 0;
6998                 neibor->_curvature->_uv = uv;
6999               }
7000             }
7001           }
7002     } // loop on EDGEs
7003
7004     // Try to initialize _Mapper2D
7005
7006     // if ( hasNoShrink )
7007     //   return;
7008
7009     SMDS_ElemIteratorPtr fIt = eos->_subMesh->GetSubMeshDS()->GetElements();
7010     if ( !fIt->more() || fIt->next()->NbCornerNodes() != 4 )
7011       return;
7012
7013     // get EDGEs of quadrangle bottom
7014     std::list< TopoDS_Edge > edges;
7015     std::list< int > nbEdgesInWire;
7016     int nbWire = SMESH_Block::GetOrderedEdges( F, edges, nbEdgesInWire );
7017     if ( nbWire != 1 || nbEdgesInWire.front() < 4 )
7018       return;
7019     const SMDS_MeshNode* node;
7020     while ( true ) // make edges start at a corner VERTEX
7021     {
7022       node = SMESH_Algo::VertexNode( helper.IthVertex( 0, edges.front() ), helper.GetMeshDS() );
7023       if ( node && helper.IsCornerOfStructure( node, eos->_subMesh->GetSubMeshDS(), helper ))
7024         break;
7025       edges.pop_front();
7026       if ( edges.empty() )
7027         return;
7028     }
7029     std::list< TopoDS_Edge >::iterator edgeIt = edges.begin();
7030     while ( true ) // make edges finish at a corner VERTEX
7031     {
7032       node = SMESH_Algo::VertexNode( helper.IthVertex( 1, *edgeIt ), helper.GetMeshDS() );
7033       ++edgeIt;
7034       if ( node && helper.IsCornerOfStructure( node, eos->_subMesh->GetSubMeshDS(), helper ))
7035       {
7036         edges.erase( edgeIt, edges.end() );
7037         break;
7038       }
7039       if ( edgeIt == edges.end() )
7040         return;
7041     }
7042
7043     // get structure of nodes
7044     TParam2ColumnMap param2ColumnMap;
7045     if ( !helper.LoadNodeColumns( param2ColumnMap, F, edges, helper.GetMeshDS() ))
7046       return;
7047
7048     eos->_mapper2D = new _Mapper2D( param2ColumnMap, eos->GetData()._n2eMap );
7049
7050   } // if eos is of curved FACE
7051
7052   return;
7053 }
7054
7055 //================================================================================
7056 /*!
7057  * \brief Add faces for smoothing
7058  */
7059 //================================================================================
7060
7061 void _SolidData::AddShapesToSmooth( const set< _EdgesOnShape* >& eosToSmooth,
7062                                     const set< _EdgesOnShape* >* edgesNoAnaSmooth )
7063 {
7064   set< _EdgesOnShape * >::const_iterator eos = eosToSmooth.begin();
7065   for ( ; eos != eosToSmooth.end(); ++eos )
7066   {
7067     if ( !*eos || (*eos)->_toSmooth ) continue;
7068
7069     (*eos)->_toSmooth = true;
7070
7071     if ( (*eos)->ShapeType() == TopAbs_FACE )
7072     {
7073       PrepareEdgesToSmoothOnFace( *eos, /*substituteSrcNodes=*/false );
7074       (*eos)->_toSmooth = true;
7075     }
7076   }
7077
7078   // avoid _Smoother1D::smoothAnalyticEdge() of edgesNoAnaSmooth
7079   if ( edgesNoAnaSmooth )
7080     for ( eos = edgesNoAnaSmooth->begin(); eos != edgesNoAnaSmooth->end(); ++eos )
7081     {
7082       if ( (*eos)->_edgeSmoother )
7083         (*eos)->_edgeSmoother->_anaCurve.Nullify();
7084     }
7085 }
7086
7087 //================================================================================
7088 /*!
7089  * \brief Limit _LayerEdge::_maxLen according to local curvature
7090  */
7091 //================================================================================
7092
7093 void _ViscousBuilder::limitMaxLenByCurvature( _SolidData& data, SMESH_MesherHelper& /*helper*/ )
7094 {
7095   // find intersection of neighbor _LayerEdge's to limit _maxLen
7096   // according to local curvature (IPAL52648)
7097
7098   // This method must be called after findCollisionEdges() where _LayerEdge's
7099   // get _lenFactor initialized in the case of eos._hyp.IsOffsetMethod()
7100
7101   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7102   {
7103     _EdgesOnShape& eosI = data._edgesOnShape[iS];
7104     if ( eosI._edges.empty() ) continue;
7105     if ( !eosI._hyp.ToSmooth() )
7106     {
7107       for ( size_t i = 0; i < eosI._edges.size(); ++i )
7108       {
7109         _LayerEdge* eI = eosI._edges[i];
7110         for ( size_t iN = 0; iN < eI->_neibors.size(); ++iN )
7111         {
7112           _LayerEdge* eN = eI->_neibors[iN];
7113           if ( eI->_nodes[0]->GetID() < eN->_nodes[0]->GetID() ) // treat this pair once
7114           {
7115             _EdgesOnShape* eosN = data.GetShapeEdges( eN );
7116             limitMaxLenByCurvature( eI, eN, eosI, *eosN, eosI._hyp.ToSmooth() );
7117           }
7118         }
7119       }
7120     }
7121     else if ( eosI.ShapeType() == TopAbs_EDGE )
7122     {
7123       const TopoDS_Edge& E = TopoDS::Edge( eosI._shape );
7124       if ( SMESH_Algo::IsStraight( E, /*degenResult=*/true )) continue;
7125
7126       _LayerEdge* e0 = eosI._edges[0];
7127       for ( size_t i = 1; i < eosI._edges.size(); ++i )
7128       {
7129         _LayerEdge* eI = eosI._edges[i];
7130         limitMaxLenByCurvature( eI, e0, eosI, eosI, eosI._hyp.ToSmooth() );
7131         e0 = eI;
7132       }
7133     }
7134   }
7135 }
7136
7137 //================================================================================
7138 /*!
7139  * \brief Limit _LayerEdge::_maxLen according to local curvature
7140  */
7141 //================================================================================
7142
7143 void _ViscousBuilder::limitMaxLenByCurvature( _LayerEdge*    e1,
7144                                               _LayerEdge*    e2,
7145                                               _EdgesOnShape& /*eos1*/,
7146                                               _EdgesOnShape& /*eos2*/,
7147                                               const bool     /*isSmoothable*/ )
7148 {
7149   if (( e1->_nodes[0]->GetPosition()->GetDim() !=
7150         e2->_nodes[0]->GetPosition()->GetDim() ) &&
7151       ( e1->_cosin < 0.75 ))
7152     return; // angle > 90 deg at e1
7153
7154   gp_XYZ plnNorm = e1->_normal ^ e2->_normal;
7155   double norSize = plnNorm.SquareModulus();
7156   if ( norSize < std::numeric_limits<double>::min() )
7157     return; // parallel normals
7158
7159   // find closest points of skew _LayerEdge's
7160   SMESH_TNodeXYZ src1( e1->_nodes[0] ), src2( e2->_nodes[0] );
7161   gp_XYZ dir12 = src2 - src1;
7162   gp_XYZ perp1 = e1->_normal ^ plnNorm;
7163   gp_XYZ perp2 = e2->_normal ^ plnNorm;
7164   double  dot1 = perp2 * e1->_normal;
7165   double  dot2 = perp1 * e2->_normal;
7166   double    u1 =   ( perp2 * dir12 ) / dot1;
7167   double    u2 = - ( perp1 * dir12 ) / dot2;
7168   if ( u1 > 0 && u2 > 0 )
7169   {
7170     double ovl = ( u1 * e1->_normal * dir12 -
7171                    u2 * e2->_normal * dir12 ) / dir12.SquareModulus();
7172     if ( ovl > theSmoothThickToElemSizeRatio )
7173     {
7174       const double coef = 0.75;
7175       e1->SetMaxLen( Min( e1->_maxLen, coef * u1 / e1->_lenFactor ));
7176       e2->SetMaxLen( Min( e2->_maxLen, coef * u2 / e2->_lenFactor ));
7177     }
7178   }
7179 }
7180
7181 //================================================================================
7182 /*!
7183  * \brief Fill data._collisionEdges
7184  */
7185 //================================================================================
7186
7187 void _ViscousBuilder::findCollisionEdges( _SolidData& data, SMESH_MesherHelper& helper )
7188 {
7189   data._collisionEdges.clear();
7190
7191   // set the full thickness of the layers to LEs
7192   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7193   {
7194     _EdgesOnShape& eos = data._edgesOnShape[iS];
7195     if ( eos._edges.empty() ) continue;
7196     if ( eos.ShapeType() != TopAbs_EDGE && eos.ShapeType() != TopAbs_VERTEX ) continue;
7197     if ( !eos._sWOL.IsNull() ) continue; // PAL23566
7198
7199     for ( size_t i = 0; i < eos._edges.size(); ++i )
7200     {
7201       if ( eos._edges[i]->Is( _LayerEdge::BLOCKED )) continue;
7202       double maxLen = eos._edges[i]->_maxLen;
7203       eos._edges[i]->_maxLen = Precision::Infinite(); // avoid blocking
7204       eos._edges[i]->SetNewLength( 1.5 * maxLen, eos, helper );
7205       eos._edges[i]->_maxLen = maxLen;
7206     }
7207   }
7208
7209   // make temporary quadrangles got by extrusion of
7210   // mesh edges along _LayerEdge._normal's
7211
7212   vector< const SMDS_MeshElement* > tmpFaces;
7213
7214   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7215   {
7216     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
7217     if ( eos.ShapeType() != TopAbs_EDGE )
7218       continue;
7219     if ( eos._edges.empty() )
7220     {
7221       _LayerEdge* edge[2] = { 0, 0 }; // LE of 2 VERTEX'es
7222       SMESH_subMeshIteratorPtr smIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false);
7223       while ( smIt->more() )
7224         if ( _EdgesOnShape* eov = data.GetShapeEdges( smIt->next()->GetId() ))
7225           if ( eov->_edges.size() == 1 )
7226             edge[ bool( edge[0]) ] = eov->_edges[0];
7227
7228       if ( edge[1] )
7229       {
7230         _TmpMeshFaceOnEdge* f = new _TmpMeshFaceOnEdge( edge[0], edge[1], --_tmpFaceID );
7231         tmpFaces.push_back( f );
7232       }
7233     }
7234     for ( size_t i = 0; i < eos._edges.size(); ++i )
7235     {
7236       _LayerEdge* edge = eos._edges[i];
7237       for ( int j = 0; j < 2; ++j ) // loop on _2NearEdges
7238       {
7239         const SMDS_MeshNode* src2 = edge->_2neibors->srcNode(j);
7240         if ( src2->GetPosition()->GetDim() > 0 &&
7241              src2->GetID() < edge->_nodes[0]->GetID() )
7242           continue; // avoid using same segment twice
7243
7244         // a _LayerEdge containing tgt2
7245         _LayerEdge* neiborEdge = edge->_2neibors->_edges[j];
7246
7247         _TmpMeshFaceOnEdge* f = new _TmpMeshFaceOnEdge( edge, neiborEdge, --_tmpFaceID );
7248         tmpFaces.push_back( f );
7249       }
7250     }
7251   }
7252
7253   // Find _LayerEdge's intersecting tmpFaces.
7254
7255   SMDS_ElemIteratorPtr fIt( new SMDS_ElementVectorIterator( tmpFaces.begin(),
7256                                                             tmpFaces.end()));
7257   SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
7258     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(), fIt ));
7259
7260   double dist1, dist2, segLen, eps = 0.5;
7261   _CollisionEdges collEdges;
7262   vector< const SMDS_MeshElement* > suspectFaces;
7263   const double angle45 = Cos( 45. * M_PI / 180. );
7264
7265   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7266   {
7267     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
7268     if ( eos.ShapeType() == TopAbs_FACE || !eos._sWOL.IsNull() )
7269       continue;
7270     // find sub-shapes whose VL can influence VL on eos
7271     set< TGeomID > neighborShapes;
7272     PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
7273     while ( const TopoDS_Shape* face = fIt->next() )
7274     {
7275       TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
7276       if ( _EdgesOnShape* eof = data.GetShapeEdges( faceID ))
7277       {
7278         SMESH_subMeshIteratorPtr subIt = eof->_subMesh->getDependsOnIterator(/*includeSelf=*/false);
7279         while ( subIt->more() )
7280           neighborShapes.insert( subIt->next()->GetId() );
7281       }
7282     }
7283     if ( eos.ShapeType() == TopAbs_VERTEX )
7284     {
7285       PShapeIteratorPtr eIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_EDGE );
7286       while ( const TopoDS_Shape* edge = eIt->next() )
7287         neighborShapes.erase( getMeshDS()->ShapeToIndex( *edge ));
7288     }
7289     // find intersecting _LayerEdge's
7290     for ( size_t i = 0; i < eos._edges.size(); ++i )
7291     {
7292       if ( eos._edges[i]->Is( _LayerEdge::MULTI_NORMAL )) continue;
7293       _LayerEdge*   edge = eos._edges[i];
7294       gp_Ax1 lastSegment = edge->LastSegment( segLen, eos );
7295       segLen *= 1.2;
7296
7297       gp_Vec eSegDir0, eSegDir1;
7298       if ( edge->IsOnEdge() )
7299       {
7300         SMESH_TNodeXYZ eP( edge->_nodes[0] );
7301         eSegDir0 = SMESH_TNodeXYZ( edge->_2neibors->srcNode(0) ) - eP;
7302         eSegDir1 = SMESH_TNodeXYZ( edge->_2neibors->srcNode(1) ) - eP;
7303       }
7304       suspectFaces.clear();
7305       searcher->GetElementsInSphere( SMESH_TNodeXYZ( edge->_nodes.back()), edge->_len * 2,
7306                                      SMDSAbs_Face, suspectFaces );
7307       collEdges._intEdges.clear();
7308       for ( size_t j = 0 ; j < suspectFaces.size(); ++j )
7309       {
7310         const _TmpMeshFaceOnEdge* f = (const _TmpMeshFaceOnEdge*) suspectFaces[j];
7311         if ( f->_le1 == edge || f->_le2 == edge ) continue;
7312         if ( !neighborShapes.count( f->_le1->_nodes[0]->getshapeId() )) continue;
7313         if ( !neighborShapes.count( f->_le2->_nodes[0]->getshapeId() )) continue;
7314         if ( edge->IsOnEdge() ) {
7315           if ( edge->_2neibors->include( f->_le1 ) ||
7316                edge->_2neibors->include( f->_le2 )) continue;
7317         }
7318         else {
7319           if (( f->_le1->IsOnEdge() && f->_le1->_2neibors->include( edge )) ||
7320               ( f->_le2->IsOnEdge() && f->_le2->_2neibors->include( edge )))  continue;
7321         }
7322         dist1 = dist2 = Precision::Infinite();
7323         if ( !edge->SegTriaInter( lastSegment, f->n(0), f->n(1), f->n(2), dist1, eps ))
7324           dist1 = Precision::Infinite();
7325         if ( !edge->SegTriaInter( lastSegment, f->n(3), f->n(2), f->n(0), dist2, eps ))
7326           dist2 = Precision::Infinite();
7327         if (( dist1 > segLen ) && ( dist2 > segLen ))
7328           continue;
7329
7330         if ( edge->IsOnEdge() )
7331         {
7332           // skip perpendicular EDGEs
7333           gp_Vec fSegDir  = SMESH_TNodeXYZ( f->n(0) ) - SMESH_TNodeXYZ( f->n(3) );
7334           bool isParallel = ( isLessAngle( eSegDir0, fSegDir, angle45 ) ||
7335                               isLessAngle( eSegDir1, fSegDir, angle45 ) ||
7336                               isLessAngle( eSegDir0, fSegDir.Reversed(), angle45 ) ||
7337                               isLessAngle( eSegDir1, fSegDir.Reversed(), angle45 ));
7338           if ( !isParallel )
7339             continue;
7340         }
7341
7342         // either limit inflation of edges or remember them for updating _normal
7343         // double dot = edge->_normal * f->GetDir();
7344         // if ( dot > 0.1 )
7345         {
7346           collEdges._intEdges.push_back( f->_le1 );
7347           collEdges._intEdges.push_back( f->_le2 );
7348         }
7349         // else
7350         // {
7351         //   double shortLen = 0.75 * ( Min( dist1, dist2 ) / edge->_lenFactor );
7352         //   edge->SetMaxLen( Min( shortLen, edge->_maxLen ));
7353         // }
7354       }
7355
7356       if ( !collEdges._intEdges.empty() )
7357       {
7358         collEdges._edge = edge;
7359         data._collisionEdges.push_back( collEdges );
7360       }
7361     }
7362   }
7363
7364   for ( size_t i = 0 ; i < tmpFaces.size(); ++i )
7365     delete tmpFaces[i];
7366
7367   // restore the zero thickness
7368   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7369   {
7370     _EdgesOnShape& eos = data._edgesOnShape[iS];
7371     if ( eos._edges.empty() ) continue;
7372     if ( eos.ShapeType() != TopAbs_EDGE && eos.ShapeType() != TopAbs_VERTEX ) continue;
7373
7374     for ( size_t i = 0; i < eos._edges.size(); ++i )
7375     {
7376       eos._edges[i]->InvalidateStep( 1, eos );
7377       eos._edges[i]->_len = 0;
7378     }
7379   }
7380 }
7381
7382 //================================================================================
7383 /*!
7384  * \brief Find _LayerEdge's located on boundary of a convex FACE whose normal
7385  *        will be updated at each inflation step
7386  */
7387 //================================================================================
7388
7389 void _ViscousBuilder::findEdgesToUpdateNormalNearConvexFace( _ConvexFace &       convFace,
7390                                                              _SolidData&         data,
7391                                                              SMESH_MesherHelper& helper )
7392 {
7393   const TGeomID convFaceID = getMeshDS()->ShapeToIndex( convFace._face );
7394   const double       preci = BRep_Tool::Tolerance( convFace._face );
7395   Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( convFace._face );
7396
7397   bool edgesToUpdateFound = false;
7398
7399   map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.begin();
7400   for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7401   {
7402     _EdgesOnShape& eos = * id2eos->second;
7403     if ( !eos._sWOL.IsNull() ) continue;
7404     if ( !eos._hyp.ToSmooth() ) continue;
7405     for ( size_t i = 0; i < eos._edges.size(); ++i )
7406     {
7407       _LayerEdge* ledge = eos._edges[ i ];
7408       if ( ledge->Is( _LayerEdge::UPD_NORMAL_CONV )) continue; // already checked
7409       if ( ledge->Is( _LayerEdge::MULTI_NORMAL )) continue; // not inflatable
7410
7411       gp_XYZ tgtPos = ( SMESH_NodeXYZ( ledge->_nodes[0] ) +
7412                         ledge->_normal * ledge->_lenFactor * ledge->_maxLen );
7413
7414       // the normal must be updated if distance from tgtPos to surface is less than
7415       // target thickness
7416
7417       // find an initial UV for search of a projection of tgtPos to surface
7418       const SMDS_MeshNode* nodeInFace = 0;
7419       SMDS_ElemIteratorPtr fIt = ledge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
7420       while ( fIt->more() && !nodeInFace )
7421       {
7422         const SMDS_MeshElement* f = fIt->next();
7423         if ( convFaceID != f->getshapeId() ) continue;
7424
7425         SMDS_ElemIteratorPtr nIt = f->nodesIterator();
7426         while ( nIt->more() && !nodeInFace )
7427         {
7428           const SMDS_MeshElement* n = nIt->next();
7429           if ( n->getshapeId() == convFaceID )
7430             nodeInFace = static_cast< const SMDS_MeshNode* >( n );
7431         }
7432       }
7433       if ( !nodeInFace )
7434         continue;
7435       gp_XY uv = helper.GetNodeUV( convFace._face, nodeInFace );
7436
7437       // projection
7438       surface->NextValueOfUV( uv, tgtPos, preci );
7439       double  dist = surface->Gap();
7440       if ( dist < 0.95 * ledge->_maxLen )
7441       {
7442         ledge->Set( _LayerEdge::UPD_NORMAL_CONV );
7443         if ( !ledge->_curvature ) ledge->_curvature = _Factory::NewCurvature();
7444         ledge->_curvature->_uv.SetCoord( uv.X(), uv.Y() );
7445         edgesToUpdateFound = true;
7446       }
7447     }
7448   }
7449
7450   if ( !convFace._isTooCurved && edgesToUpdateFound )
7451   {
7452     data._convexFaces.insert( make_pair( convFaceID, convFace )).first->second;
7453   }
7454 }
7455
7456 //================================================================================
7457 /*!
7458  * \brief Modify normals of _LayerEdge's on EDGE's to avoid intersection with
7459  * _LayerEdge's on neighbor EDGE's
7460  */
7461 //================================================================================
7462
7463 bool _ViscousBuilder::updateNormals( _SolidData&         data,
7464                                      SMESH_MesherHelper& helper,
7465                                      int                 stepNb,
7466                                      double              /*stepSize*/)
7467 {
7468   updateNormalsOfC1Vertices( data );
7469
7470   if ( stepNb > 0 && !updateNormalsOfConvexFaces( data, helper, stepNb ))
7471     return false;
7472
7473   // map to store new _normal and _cosin for each intersected edge
7474   map< _LayerEdge*, _LayerEdge, _LayerEdgeCmp >           edge2newEdge;
7475   map< _LayerEdge*, _LayerEdge, _LayerEdgeCmp >::iterator e2neIt;
7476   _LayerEdge zeroEdge;
7477   zeroEdge._normal.SetCoord( 0,0,0 );
7478   zeroEdge._maxLen = Precision::Infinite();
7479   zeroEdge._nodes.resize(1); // to init _TmpMeshFaceOnEdge
7480
7481   set< _EdgesOnShape* > shapesToSmooth, edgesNoAnaSmooth;
7482
7483   double segLen, dist1, dist2, dist;
7484   vector< pair< _LayerEdge*, double > > intEdgesDist;
7485   _TmpMeshFaceOnEdge quad( &zeroEdge, &zeroEdge, 0 );
7486
7487   for ( int iter = 0; iter < 5; ++iter )
7488   {
7489     edge2newEdge.clear();
7490
7491     for ( size_t iE = 0; iE < data._collisionEdges.size(); ++iE )
7492     {
7493       _CollisionEdges& ce = data._collisionEdges[iE];
7494       _LayerEdge*   edge1 = ce._edge;
7495       if ( !edge1 /*|| edge1->Is( _LayerEdge::BLOCKED )*/) continue;
7496       _EdgesOnShape* eos1 = data.GetShapeEdges( edge1 );
7497       if ( !eos1 ) continue;
7498
7499       // detect intersections
7500       gp_Ax1 lastSeg = edge1->LastSegment( segLen, *eos1 );
7501       double testLen = 1.5 * edge1->_maxLen * edge1->_lenFactor;
7502       double     eps = 0.5;
7503       intEdgesDist.clear();
7504       double minIntDist = Precision::Infinite();
7505       for ( size_t i = 0; i < ce._intEdges.size(); i += 2 )
7506       {
7507         if ( edge1->Is( _LayerEdge::BLOCKED ) &&
7508              ce._intEdges[i  ]->Is( _LayerEdge::BLOCKED ) &&
7509              ce._intEdges[i+1]->Is( _LayerEdge::BLOCKED ))
7510           continue;
7511         double dot  = edge1->_normal * quad.GetDir( ce._intEdges[i], ce._intEdges[i+1] );
7512         double fact = ( 1.1 + dot * dot );
7513         SMESH_TNodeXYZ pSrc0( ce.nSrc(i) ), pSrc1( ce.nSrc(i+1) );
7514         SMESH_TNodeXYZ pTgt0( ce.nTgt(i) ), pTgt1( ce.nTgt(i+1) );
7515         gp_XYZ pLast0 = pSrc0 + ( pTgt0 - pSrc0 ) * fact;
7516         gp_XYZ pLast1 = pSrc1 + ( pTgt1 - pSrc1 ) * fact;
7517         dist1 = dist2 = Precision::Infinite();
7518         if ( !edge1->SegTriaInter( lastSeg, pSrc0, pLast0, pSrc1,  dist1, eps ) &&
7519              !edge1->SegTriaInter( lastSeg, pSrc1, pLast1, pLast0, dist2, eps ))
7520           continue;
7521         dist = dist1;
7522         if ( dist > testLen || dist <= 0 )
7523         {
7524           dist = dist2;
7525           if ( dist > testLen || dist <= 0 )
7526             continue;
7527         }
7528         // choose a closest edge
7529         gp_Pnt intP( lastSeg.Location().XYZ() + lastSeg.Direction().XYZ() * ( dist + segLen ));
7530         double d1 = intP.SquareDistance( pSrc0 );
7531         double d2 = intP.SquareDistance( pSrc1 );
7532         int iClose = i + ( d2 < d1 );
7533         _LayerEdge* edge2 = ce._intEdges[iClose];
7534         edge2->Unset( _LayerEdge::MARKED );
7535
7536         // choose a closest edge among neighbors
7537         gp_Pnt srcP( SMESH_TNodeXYZ( edge1->_nodes[0] ));
7538         d1 = srcP.SquareDistance( SMESH_TNodeXYZ( edge2->_nodes[0] ));
7539         for ( size_t j = 0; j < intEdgesDist.size(); ++j )
7540         {
7541           _LayerEdge * edgeJ = intEdgesDist[j].first;
7542           if ( edge2->IsNeiborOnEdge( edgeJ ))
7543           {
7544             d2 = srcP.SquareDistance( SMESH_TNodeXYZ( edgeJ->_nodes[0] ));
7545             ( d1 < d2 ? edgeJ : edge2 )->Set( _LayerEdge::MARKED );
7546           }
7547         }
7548         intEdgesDist.push_back( make_pair( edge2, dist ));
7549         // if ( Abs( d2 - d1 ) / Max( d2, d1 ) < 0.5 )
7550         // {
7551         //   iClose = i + !( d2 < d1 );
7552         //   intEdges.push_back( ce._intEdges[iClose] );
7553         //   ce._intEdges[iClose]->Unset( _LayerEdge::MARKED );
7554         // }
7555         minIntDist = Min( edge1->_len * edge1->_lenFactor - segLen + dist, minIntDist );
7556       }
7557
7558       //ce._edge = 0;
7559
7560       // compute new _normals
7561       for ( size_t i = 0; i < intEdgesDist.size(); ++i )
7562       {
7563         _LayerEdge* edge2   = intEdgesDist[i].first;
7564         double      distWgt = edge1->_len / intEdgesDist[i].second;
7565         // if ( edge1->Is( _LayerEdge::BLOCKED ) &&
7566         //      edge2->Is( _LayerEdge::BLOCKED )) continue;        
7567         if ( edge2->Is( _LayerEdge::MARKED )) continue;
7568         edge2->Set( _LayerEdge::MARKED );
7569
7570         // get a new normal
7571         gp_XYZ dir1 = edge1->_normal, dir2 = edge2->_normal;
7572
7573         double cos1 = Abs( edge1->_cosin ), cos2 = Abs( edge2->_cosin );
7574         double wgt1 = ( cos1 + 0.001 ) / ( cos1 + cos2 + 0.002 );
7575         double wgt2 = ( cos2 + 0.001 ) / ( cos1 + cos2 + 0.002 );
7576         // double cos1 = Abs( edge1->_cosin ),        cos2 = Abs( edge2->_cosin );
7577         // double sgn1 = 0.1 * ( 1 + edge1->_cosin ), sgn2 = 0.1 * ( 1 + edge2->_cosin );
7578         // double wgt1 = ( cos1 + sgn1 ) / ( cos1 + cos2 + sgn1 + sgn2 );
7579         // double wgt2 = ( cos2 + sgn2 ) / ( cos1 + cos2 + sgn1 + sgn2 );
7580         gp_XYZ newNormal = wgt1 * dir1 + wgt2 * dir2;
7581         newNormal.Normalize();
7582
7583         // get new cosin
7584         double newCos;
7585         double sgn1 = edge1->_cosin / cos1, sgn2 = edge2->_cosin / cos2;
7586         if ( cos1 < theMinSmoothCosin )
7587         {
7588           newCos = cos2 * sgn1;
7589         }
7590         else if ( cos2 > theMinSmoothCosin ) // both cos1 and cos2 > theMinSmoothCosin
7591         {
7592           newCos = ( wgt1 * cos1 + wgt2 * cos2 ) * edge1->_cosin / cos1;
7593         }
7594         else
7595         {
7596           newCos = edge1->_cosin;
7597         }
7598
7599         e2neIt = edge2newEdge.insert( make_pair( edge1, zeroEdge )).first;
7600         e2neIt->second._normal += distWgt * newNormal;
7601         e2neIt->second._cosin   = newCos;
7602         e2neIt->second.SetMaxLen( 0.7 * minIntDist / edge1->_lenFactor );
7603         if ( iter > 0 && sgn1 * sgn2 < 0 && edge1->_cosin < 0 )
7604           e2neIt->second._normal += dir2;
7605
7606         e2neIt = edge2newEdge.insert( make_pair( edge2, zeroEdge )).first;
7607         e2neIt->second._normal += distWgt * newNormal;
7608         if ( Precision::IsInfinite( zeroEdge._maxLen ))
7609         {
7610           e2neIt->second._cosin  = edge2->_cosin;
7611           e2neIt->second.SetMaxLen( 1.3 * minIntDist / edge1->_lenFactor );
7612         }
7613         if ( iter > 0 && sgn1 * sgn2 < 0 && edge2->_cosin < 0 )
7614           e2neIt->second._normal += dir1;
7615       }
7616     }
7617
7618     if ( edge2newEdge.empty() )
7619       break; //return true;
7620
7621     dumpFunction(SMESH_Comment("updateNormals")<< data._index << "_" << stepNb << "_it" << iter);
7622
7623     // Update data of edges depending on a new _normal
7624
7625     data.UnmarkEdges();
7626     for ( e2neIt = edge2newEdge.begin(); e2neIt != edge2newEdge.end(); ++e2neIt )
7627     {
7628       _LayerEdge*    edge = e2neIt->first;
7629       _LayerEdge& newEdge = e2neIt->second;
7630       _EdgesOnShape*  eos = data.GetShapeEdges( edge );
7631       if ( edge->Is( _LayerEdge::BLOCKED ) && newEdge._maxLen > edge->_len )
7632         continue;
7633
7634       // Check if a new _normal is OK:
7635       newEdge._normal.Normalize();
7636       if ( !isNewNormalOk( data, *edge, newEdge._normal ))
7637       {
7638         if ( newEdge._maxLen < edge->_len && iter > 0 ) // limit _maxLen
7639         {
7640           edge->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true  );
7641           edge->SetMaxLen( newEdge._maxLen );
7642           edge->SetNewLength( newEdge._maxLen, *eos, helper );
7643         }
7644         continue; // the new _normal is bad
7645       }
7646       // the new _normal is OK
7647
7648       // find shapes that need smoothing due to change of _normal
7649       if ( edge->_cosin   < theMinSmoothCosin &&
7650            newEdge._cosin > theMinSmoothCosin )
7651       {
7652         if ( eos->_sWOL.IsNull() )
7653         {
7654           SMDS_ElemIteratorPtr fIt = edge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
7655           while ( fIt->more() )
7656             shapesToSmooth.insert( data.GetShapeEdges( fIt->next()->getshapeId() ));
7657         }
7658         else // edge inflates along a FACE
7659         {
7660           TopoDS_Shape V = helper.GetSubShapeByNode( edge->_nodes[0], getMeshDS() );
7661           PShapeIteratorPtr eIt = helper.GetAncestors( V, *_mesh, TopAbs_EDGE, &eos->_sWOL );
7662           while ( const TopoDS_Shape* E = eIt->next() )
7663           {
7664             gp_Vec edgeDir = getEdgeDir( TopoDS::Edge( *E ), TopoDS::Vertex( V ),
7665                                          eos->_hyp.Get1stLayerThickness() );
7666             double   angle = edgeDir.Angle( newEdge._normal ); // [0,PI]
7667             if ( angle < M_PI / 2 )
7668               shapesToSmooth.insert( data.GetShapeEdges( *E ));
7669           }
7670         }
7671       }
7672
7673       double len = edge->_len;
7674       edge->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true  );
7675       edge->SetNormal( newEdge._normal );
7676       edge->SetCosin( newEdge._cosin );
7677       edge->SetNewLength( len, *eos, helper );
7678       edge->Set( _LayerEdge::MARKED );
7679       edge->Set( _LayerEdge::NORMAL_UPDATED );
7680       edgesNoAnaSmooth.insert( eos );
7681     }
7682
7683     // Update normals and other dependent data of not intersecting _LayerEdge's
7684     // neighboring the intersecting ones
7685
7686     for ( e2neIt = edge2newEdge.begin(); e2neIt != edge2newEdge.end(); ++e2neIt )
7687     {
7688       _LayerEdge*   edge1 = e2neIt->first;
7689       _EdgesOnShape* eos1 = data.GetShapeEdges( edge1 );
7690       if ( !edge1->Is( _LayerEdge::MARKED ))
7691         continue;
7692
7693       if ( edge1->IsOnEdge() )
7694       {
7695         const SMDS_MeshNode * n1 = edge1->_2neibors->srcNode(0);
7696         const SMDS_MeshNode * n2 = edge1->_2neibors->srcNode(1);
7697         edge1->SetDataByNeighbors( n1, n2, *eos1, helper );
7698       }
7699
7700       if ( !edge1->_2neibors || !eos1->_sWOL.IsNull() )
7701         continue;
7702       for ( int j = 0; j < 2; ++j ) // loop on 2 neighbors
7703       {
7704         _LayerEdge* neighbor = edge1->_2neibors->_edges[j];
7705         if ( neighbor->Is( _LayerEdge::MARKED ) /*edge2newEdge.count ( neighbor )*/)
7706           continue; // j-th neighbor is also intersected
7707         _LayerEdge* prevEdge = edge1;
7708         const int nbSteps = 10;
7709         for ( int step = nbSteps; step; --step ) // step from edge1 in j-th direction
7710         {
7711           if ( neighbor->Is( _LayerEdge::BLOCKED ) ||
7712                neighbor->Is( _LayerEdge::MARKED ))
7713             break;
7714           _EdgesOnShape* eos = data.GetShapeEdges( neighbor );
7715           if ( !eos ) continue;
7716           _LayerEdge* nextEdge = neighbor;
7717           if ( neighbor->_2neibors )
7718           {
7719             int iNext = 0;
7720             nextEdge = neighbor->_2neibors->_edges[iNext];
7721             if ( nextEdge == prevEdge )
7722               nextEdge = neighbor->_2neibors->_edges[ ++iNext ];
7723           }
7724           double r = double(step-1)/nbSteps/(iter+1);
7725           if ( !nextEdge->_2neibors )
7726             r = Min( r, 0.5 );
7727
7728           gp_XYZ newNorm = prevEdge->_normal * r + nextEdge->_normal * (1-r);
7729           newNorm.Normalize();
7730           if ( !isNewNormalOk( data, *neighbor, newNorm ))
7731             break;
7732
7733           double len = neighbor->_len;
7734           neighbor->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true  );
7735           neighbor->SetNormal( newNorm );
7736           neighbor->SetCosin( prevEdge->_cosin * r + nextEdge->_cosin * (1-r) );
7737           if ( neighbor->_2neibors )
7738             neighbor->SetDataByNeighbors( prevEdge->_nodes[0], nextEdge->_nodes[0], *eos, helper );
7739           neighbor->SetNewLength( len, *eos, helper );
7740           neighbor->Set( _LayerEdge::MARKED );
7741           neighbor->Set( _LayerEdge::NORMAL_UPDATED );
7742           edgesNoAnaSmooth.insert( eos );
7743
7744           if ( !neighbor->_2neibors )
7745             break; // neighbor is on VERTEX
7746
7747           // goto the next neighbor
7748           prevEdge = neighbor;
7749           neighbor = nextEdge;
7750         }
7751       }
7752     }
7753     dumpFunctionEnd();
7754   } // iterations
7755
7756   data.AddShapesToSmooth( shapesToSmooth, &edgesNoAnaSmooth );
7757
7758   return true;
7759 }
7760
7761 //================================================================================
7762 /*!
7763  * \brief Check if a new normal is OK
7764  */
7765 //================================================================================
7766
7767 bool _ViscousBuilder::isNewNormalOk( _SolidData&   data,
7768                                      _LayerEdge&   edge,
7769                                      const gp_XYZ& newNormal)
7770 {
7771   // check a min angle between the newNormal and surrounding faces
7772   vector<_Simplex> simplices;
7773   SMESH_TNodeXYZ n0( edge._nodes[0] ), n1, n2;
7774   _Simplex::GetSimplices( n0._node, simplices, data._ignoreFaceIds, &data );
7775   double newMinDot = 1, curMinDot = 1;
7776   for ( size_t i = 0; i < simplices.size(); ++i )
7777   {
7778     n1.Set( simplices[i]._nPrev );
7779     n2.Set( simplices[i]._nNext );
7780     gp_XYZ normFace = ( n1 - n0 ) ^ ( n2 - n0 );
7781     double normLen2 = normFace.SquareModulus();
7782     if ( normLen2 < std::numeric_limits<double>::min() )
7783       continue;
7784     normFace /= Sqrt( normLen2 );
7785     newMinDot = Min( newNormal    * normFace, newMinDot );
7786     curMinDot = Min( edge._normal * normFace, curMinDot );
7787   }
7788   bool ok = true;
7789   if ( newMinDot < 0.5 )
7790   {
7791     ok = ( newMinDot >= curMinDot * 0.9 );
7792     //return ( newMinDot >= ( curMinDot * ( 0.8 + 0.1 * edge.NbSteps() )));
7793     // double initMinDot2 = 1. - edge._cosin * edge._cosin;
7794     // return ( newMinDot * newMinDot ) >= ( 0.8 * initMinDot2 );
7795   }
7796
7797   return ok;
7798 }
7799
7800 //================================================================================
7801 /*!
7802  * \brief Modify normals of _LayerEdge's on FACE to reflex smoothing
7803  */
7804 //================================================================================
7805
7806 bool _ViscousBuilder::updateNormalsOfSmoothed( _SolidData&         data,
7807                                                SMESH_MesherHelper& /*helper*/,
7808                                                const int           nbSteps,
7809                                                const double        stepSize )
7810 {
7811   if ( data._nbShapesToSmooth == 0 || nbSteps == 0 )
7812     return true; // no shapes needing smoothing
7813
7814   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7815   {
7816     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
7817     if ( //!eos._toSmooth ||  _eosC1 have _toSmooth == false
7818          !eos._hyp.ToSmooth() ||
7819          eos.ShapeType() != TopAbs_FACE ||
7820          eos._edges.empty() )
7821       continue;
7822
7823     bool toSmooth = ( eos._edges[ 0 ]->NbSteps() >= nbSteps+1 );
7824     if ( !toSmooth ) continue;
7825
7826     for ( size_t i = 0; i < eos._edges.size(); ++i )
7827     {
7828       _LayerEdge* edge = eos._edges[i];
7829       if ( !edge->Is( _LayerEdge::SMOOTHED ))
7830         continue;
7831       if ( edge->Is( _LayerEdge::DIFFICULT ) && nbSteps != 1 )
7832         continue;
7833
7834       const gp_XYZ& pPrev = edge->PrevPos();
7835       const gp_XYZ& pLast = edge->_pos.back();
7836       gp_XYZ      stepVec = pLast - pPrev;
7837       double realStepSize = stepVec.Modulus();
7838       if ( realStepSize < numeric_limits<double>::min() )
7839         continue;
7840
7841       edge->_lenFactor = realStepSize / stepSize;
7842       edge->_normal    = stepVec / realStepSize;
7843       edge->Set( _LayerEdge::NORMAL_UPDATED );
7844     }
7845   }
7846
7847   return true;
7848 }
7849
7850 //================================================================================
7851 /*!
7852  * \brief Modify normals of _LayerEdge's on C1 VERTEXes
7853  */
7854 //================================================================================
7855
7856 void _ViscousBuilder::updateNormalsOfC1Vertices( _SolidData& data )
7857 {
7858   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7859   {
7860     _EdgesOnShape& eov = data._edgesOnShape[ iS ];
7861     if ( eov._eosC1.empty() ||
7862          eov.ShapeType() != TopAbs_VERTEX ||
7863          eov._edges.empty() )
7864       continue;
7865
7866     gp_XYZ newNorm   = eov._edges[0]->_normal;
7867     double curThick  = eov._edges[0]->_len * eov._edges[0]->_lenFactor;
7868     bool normChanged = false;
7869
7870     for ( size_t i = 0; i < eov._eosC1.size(); ++i )
7871     {
7872       _EdgesOnShape*   eoe = eov._eosC1[i];
7873       const TopoDS_Edge& e = TopoDS::Edge( eoe->_shape );
7874       const double    eLen = SMESH_Algo::EdgeLength( e );
7875       TopoDS_Shape    oppV = SMESH_MesherHelper::IthVertex( 0, e );
7876       if ( oppV.IsSame( eov._shape ))
7877         oppV = SMESH_MesherHelper::IthVertex( 1, e );
7878       _EdgesOnShape* eovOpp = data.GetShapeEdges( oppV );
7879       if ( !eovOpp || eovOpp->_edges.empty() ) continue;
7880       if ( eov._edges[0]->Is( _LayerEdge::BLOCKED )) continue;
7881
7882       double curThickOpp = eovOpp->_edges[0]->_len * eovOpp->_edges[0]->_lenFactor;
7883       if ( curThickOpp + curThick < eLen )
7884         continue;
7885
7886       double wgt = 2. * curThick / eLen;
7887       newNorm += wgt * eovOpp->_edges[0]->_normal;
7888       normChanged = true;
7889     }
7890     if ( normChanged )
7891     {
7892       eov._edges[0]->SetNormal( newNorm.Normalized() );
7893       eov._edges[0]->Set( _LayerEdge::NORMAL_UPDATED );
7894     }
7895   }
7896 }
7897
7898 //================================================================================
7899 /*!
7900  * \brief Modify normals of _LayerEdge's on _ConvexFace's
7901  */
7902 //================================================================================
7903
7904 bool _ViscousBuilder::updateNormalsOfConvexFaces( _SolidData&         data,
7905                                                   SMESH_MesherHelper& helper,
7906                                                   int                 stepNb )
7907 {
7908   SMESHDS_Mesh* meshDS = helper.GetMeshDS();
7909   bool isOK;
7910
7911   map< TGeomID, _ConvexFace >::iterator id2face = data._convexFaces.begin();
7912   for ( ; id2face != data._convexFaces.end(); ++id2face )
7913   {
7914     _ConvexFace & convFace = (*id2face).second;
7915     convFace._normalsFixedOnBorders = false; // to update at each inflation step
7916
7917     if ( convFace._normalsFixed )
7918       continue; // already fixed
7919     if ( convFace.CheckPrisms() )
7920       continue; // nothing to fix
7921
7922     convFace._normalsFixed = true;
7923
7924     BRepAdaptor_Surface surface ( convFace._face, false );
7925     BRepLProp_SLProps   surfProp( surface, 2, 1e-6 );
7926
7927     // check if the convex FACE is of spherical shape
7928
7929     Bnd_B3d centersBox; // bbox of centers of curvature of _LayerEdge's on VERTEXes
7930     Bnd_B3d nodesBox;
7931     gp_Pnt  center;
7932
7933     map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.begin();
7934     for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7935     {
7936       _EdgesOnShape& eos = *(id2eos->second);
7937       if ( eos.ShapeType() == TopAbs_VERTEX )
7938       {
7939         _LayerEdge* ledge = eos._edges[ 0 ];
7940         if ( convFace.GetCenterOfCurvature( ledge, surfProp, helper, center ))
7941           centersBox.Add( center );
7942       }
7943       for ( size_t i = 0; i < eos._edges.size(); ++i )
7944         nodesBox.Add( SMESH_TNodeXYZ( eos._edges[ i ]->_nodes[0] ));
7945     }
7946     if ( centersBox.IsVoid() )
7947     {
7948       debugMsg( "Error: centersBox.IsVoid()" );
7949       return false;
7950     }
7951     const bool isSpherical =
7952       ( centersBox.SquareExtent() < 1e-6 * nodesBox.SquareExtent() );
7953
7954     int nbEdges = helper.Count( convFace._face, TopAbs_EDGE, /*ignoreSame=*/false );
7955     vector < _CentralCurveOnEdge > centerCurves( nbEdges );
7956
7957     if ( isSpherical )
7958     {
7959       // set _LayerEdge::_normal as average of all normals
7960
7961       // WARNING: different density of nodes on EDGEs is not taken into account that
7962       // can lead to an improper new normal
7963
7964       gp_XYZ avgNormal( 0,0,0 );
7965       nbEdges = 0;
7966       id2eos = convFace._subIdToEOS.begin();
7967       for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7968       {
7969         _EdgesOnShape& eos = *(id2eos->second);
7970         // set data of _CentralCurveOnEdge
7971         if ( eos.ShapeType() == TopAbs_EDGE )
7972         {
7973           _CentralCurveOnEdge& ceCurve = centerCurves[ nbEdges++ ];
7974           ceCurve.SetShapes( TopoDS::Edge( eos._shape ), convFace, data, helper );
7975           if ( !eos._sWOL.IsNull() )
7976             ceCurve._adjFace.Nullify();
7977           else
7978             ceCurve._ledges.insert( ceCurve._ledges.end(),
7979                                     eos._edges.begin(), eos._edges.end());
7980         }
7981         // summarize normals
7982         for ( size_t i = 0; i < eos._edges.size(); ++i )
7983           avgNormal += eos._edges[ i ]->_normal;
7984       }
7985       double normSize = avgNormal.SquareModulus();
7986       if ( normSize < 1e-200 )
7987       {
7988         debugMsg( "updateNormalsOfConvexFaces(): zero avgNormal" );
7989         return false;
7990       }
7991       avgNormal /= Sqrt( normSize );
7992
7993       // compute new _LayerEdge::_cosin on EDGEs
7994       double avgCosin = 0;
7995       int     nbCosin = 0;
7996       gp_Vec inFaceDir;
7997       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
7998       {
7999         _CentralCurveOnEdge& ceCurve = centerCurves[ iE ];
8000         if ( ceCurve._adjFace.IsNull() )
8001           continue;
8002         for ( size_t iLE = 0; iLE < ceCurve._ledges.size(); ++iLE )
8003         {
8004           const SMDS_MeshNode* node = ceCurve._ledges[ iLE ]->_nodes[0];
8005           inFaceDir = getFaceDir( ceCurve._adjFace, ceCurve._edge, node, helper, isOK );
8006           if ( isOK )
8007           {
8008             double angle = inFaceDir.Angle( avgNormal ); // [0,PI]
8009             ceCurve._ledges[ iLE ]->_cosin = Cos( angle );
8010             avgCosin += ceCurve._ledges[ iLE ]->_cosin;
8011             nbCosin++;
8012           }
8013         }
8014       }
8015       if ( nbCosin > 0 )
8016         avgCosin /= nbCosin;
8017
8018       // set _LayerEdge::_normal = avgNormal
8019       id2eos = convFace._subIdToEOS.begin();
8020       for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
8021       {
8022         _EdgesOnShape& eos = *(id2eos->second);
8023         if ( eos.ShapeType() != TopAbs_EDGE )
8024           for ( size_t i = 0; i < eos._edges.size(); ++i )
8025             eos._edges[ i ]->_cosin = avgCosin;
8026
8027         for ( size_t i = 0; i < eos._edges.size(); ++i )
8028         {
8029           eos._edges[ i ]->SetNormal( avgNormal );
8030           eos._edges[ i ]->Set( _LayerEdge::NORMAL_UPDATED );
8031         }
8032       }
8033     }
8034     else // if ( isSpherical )
8035     {
8036       // We suppose that centers of curvature at all points of the FACE
8037       // lie on some curve, let's call it "central curve". For all _LayerEdge's
8038       // having a common center of curvature we define the same new normal
8039       // as a sum of normals of _LayerEdge's on EDGEs among them.
8040
8041       // get all centers of curvature for each EDGE
8042
8043       helper.SetSubShape( convFace._face );
8044       _LayerEdge* vertexLEdges[2], **edgeLEdge, **edgeLEdgeEnd;
8045
8046       TopExp_Explorer edgeExp( convFace._face, TopAbs_EDGE );
8047       for ( int iE = 0; edgeExp.More(); edgeExp.Next(), ++iE )
8048       {
8049         const TopoDS_Edge& edge = TopoDS::Edge( edgeExp.Current() );
8050
8051         // set adjacent FACE
8052         centerCurves[ iE ].SetShapes( edge, convFace, data, helper );
8053
8054         // get _LayerEdge's of the EDGE
8055         TGeomID edgeID = meshDS->ShapeToIndex( edge );
8056         _EdgesOnShape* eos = data.GetShapeEdges( edgeID );
8057         if ( !eos || eos->_edges.empty() )
8058         {
8059           // no _LayerEdge's on EDGE, use _LayerEdge's on VERTEXes
8060           for ( int iV = 0; iV < 2; ++iV )
8061           {
8062             TopoDS_Vertex v = helper.IthVertex( iV, edge );
8063             TGeomID     vID = meshDS->ShapeToIndex( v );
8064             eos = data.GetShapeEdges( vID );
8065             vertexLEdges[ iV ] = eos->_edges[ 0 ];
8066           }
8067           edgeLEdge    = &vertexLEdges[0];
8068           edgeLEdgeEnd = edgeLEdge + 2;
8069
8070           centerCurves[ iE ]._adjFace.Nullify();
8071         }
8072         else
8073         {
8074           if ( ! eos->_toSmooth )
8075             data.SortOnEdge( edge, eos->_edges );
8076           edgeLEdge    = &eos->_edges[ 0 ];
8077           edgeLEdgeEnd = edgeLEdge + eos->_edges.size();
8078           vertexLEdges[0] = eos->_edges.front()->_2neibors->_edges[0];
8079           vertexLEdges[1] = eos->_edges.back() ->_2neibors->_edges[1];
8080
8081           if ( ! eos->_sWOL.IsNull() )
8082             centerCurves[ iE ]._adjFace.Nullify();
8083         }
8084
8085         // Get curvature centers
8086
8087         centersBox.Clear();
8088
8089         if ( edgeLEdge[0]->IsOnEdge() &&
8090              convFace.GetCenterOfCurvature( vertexLEdges[0], surfProp, helper, center ))
8091         { // 1st VERTEX
8092           centerCurves[ iE ].Append( center, vertexLEdges[0] );
8093           centersBox.Add( center );
8094         }
8095         for ( ; edgeLEdge < edgeLEdgeEnd; ++edgeLEdge )
8096           if ( convFace.GetCenterOfCurvature( *edgeLEdge, surfProp, helper, center ))
8097           { // EDGE or VERTEXes
8098             centerCurves[ iE ].Append( center, *edgeLEdge );
8099             centersBox.Add( center );
8100           }
8101         if ( edgeLEdge[-1]->IsOnEdge() &&
8102              convFace.GetCenterOfCurvature( vertexLEdges[1], surfProp, helper, center ))
8103         { // 2nd VERTEX
8104           centerCurves[ iE ].Append( center, vertexLEdges[1] );
8105           centersBox.Add( center );
8106         }
8107         centerCurves[ iE ]._isDegenerated =
8108           ( centersBox.IsVoid() || centersBox.SquareExtent() < 1e-6 * nodesBox.SquareExtent() );
8109
8110       } // loop on EDGES of convFace._face to set up data of centerCurves
8111
8112       // Compute new normals for _LayerEdge's on EDGEs
8113
8114       double avgCosin = 0;
8115       int     nbCosin = 0;
8116       gp_Vec inFaceDir;
8117       for ( size_t iE1 = 0; iE1 < centerCurves.size(); ++iE1 )
8118       {
8119         _CentralCurveOnEdge& ceCurve = centerCurves[ iE1 ];
8120         if ( ceCurve._isDegenerated )
8121           continue;
8122         const vector< gp_Pnt >& centers = ceCurve._curvaCenters;
8123         vector< gp_XYZ > &   newNormals = ceCurve._normals;
8124         for ( size_t iC1 = 0; iC1 < centers.size(); ++iC1 )
8125         {
8126           isOK = false;
8127           for ( size_t iE2 = 0; iE2 < centerCurves.size() && !isOK; ++iE2 )
8128           {
8129             if ( iE1 != iE2 )
8130               isOK = centerCurves[ iE2 ].FindNewNormal( centers[ iC1 ], newNormals[ iC1 ]);
8131           }
8132           if ( isOK && !ceCurve._adjFace.IsNull() )
8133           {
8134             // compute new _LayerEdge::_cosin
8135             const SMDS_MeshNode* node = ceCurve._ledges[ iC1 ]->_nodes[0];
8136             inFaceDir = getFaceDir( ceCurve._adjFace, ceCurve._edge, node, helper, isOK );
8137             if ( isOK )
8138             {
8139               double angle = inFaceDir.Angle( newNormals[ iC1 ] ); // [0,PI]
8140               ceCurve._ledges[ iC1 ]->_cosin = Cos( angle );
8141               avgCosin += ceCurve._ledges[ iC1 ]->_cosin;
8142               nbCosin++;
8143             }
8144           }
8145         }
8146       }
8147       // set new normals to _LayerEdge's of NOT degenerated central curves
8148       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
8149       {
8150         if ( centerCurves[ iE ]._isDegenerated )
8151           continue;
8152         for ( size_t iLE = 0; iLE < centerCurves[ iE ]._ledges.size(); ++iLE )
8153         {
8154           centerCurves[ iE ]._ledges[ iLE ]->SetNormal( centerCurves[ iE ]._normals[ iLE ]);
8155           centerCurves[ iE ]._ledges[ iLE ]->Set( _LayerEdge::NORMAL_UPDATED );
8156         }
8157       }
8158       // set new normals to _LayerEdge's of     degenerated central curves
8159       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
8160       {
8161         if ( !centerCurves[ iE ]._isDegenerated ||
8162              centerCurves[ iE ]._ledges.size() < 3 )
8163           continue;
8164         // new normal is an average of new normals at VERTEXes that
8165         // was computed on non-degenerated _CentralCurveOnEdge's
8166         gp_XYZ newNorm = ( centerCurves[ iE ]._ledges.front()->_normal +
8167                            centerCurves[ iE ]._ledges.back ()->_normal );
8168         double sz = newNorm.Modulus();
8169         if ( sz < 1e-200 )
8170           continue;
8171         newNorm /= sz;
8172         double newCosin = ( 0.5 * centerCurves[ iE ]._ledges.front()->_cosin +
8173                             0.5 * centerCurves[ iE ]._ledges.back ()->_cosin );
8174         for ( size_t iLE = 1, nb = centerCurves[ iE ]._ledges.size() - 1; iLE < nb; ++iLE )
8175         {
8176           centerCurves[ iE ]._ledges[ iLE ]->SetNormal( newNorm );
8177           centerCurves[ iE ]._ledges[ iLE ]->_cosin   = newCosin;
8178           centerCurves[ iE ]._ledges[ iLE ]->Set( _LayerEdge::NORMAL_UPDATED );
8179         }
8180       }
8181
8182       // Find new normals for _LayerEdge's based on FACE
8183
8184       if ( nbCosin > 0 )
8185         avgCosin /= nbCosin;
8186       const TGeomID faceID = meshDS->ShapeToIndex( convFace._face );
8187       map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.find( faceID );
8188       if ( id2eos != convFace._subIdToEOS.end() )
8189       {
8190         int iE = 0;
8191         gp_XYZ newNorm;
8192         _EdgesOnShape& eos = * ( id2eos->second );
8193         for ( size_t i = 0; i < eos._edges.size(); ++i )
8194         {
8195           _LayerEdge* ledge = eos._edges[ i ];
8196           if ( !convFace.GetCenterOfCurvature( ledge, surfProp, helper, center ))
8197             continue;
8198           for ( size_t i = 0; i < centerCurves.size(); ++i, ++iE )
8199           {
8200             iE = iE % centerCurves.size();
8201             if ( centerCurves[ iE ]._isDegenerated )
8202               continue;
8203             newNorm.SetCoord( 0,0,0 );
8204             if ( centerCurves[ iE ].FindNewNormal( center, newNorm ))
8205             {
8206               ledge->SetNormal( newNorm );
8207               ledge->_cosin  = avgCosin;
8208               ledge->Set( _LayerEdge::NORMAL_UPDATED );
8209               break;
8210             }
8211           }
8212         }
8213       }
8214
8215     } // not a quasi-spherical FACE
8216
8217     // Update _LayerEdge's data according to a new normal
8218
8219     dumpFunction(SMESH_Comment("updateNormalsOfConvexFaces")<<data._index
8220                  <<"_F"<<meshDS->ShapeToIndex( convFace._face ));
8221
8222     id2eos = convFace._subIdToEOS.begin();
8223     for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
8224     {
8225       _EdgesOnShape& eos = * ( id2eos->second );
8226       for ( size_t i = 0; i < eos._edges.size(); ++i )
8227       {
8228         _LayerEdge* & ledge = eos._edges[ i ];
8229         double len = ledge->_len;
8230         ledge->InvalidateStep( stepNb + 1, eos, /*restoreLength=*/true );
8231         ledge->SetCosin( ledge->_cosin );
8232         ledge->SetNewLength( len, eos, helper );
8233       }
8234       if ( eos.ShapeType() != TopAbs_FACE )
8235         for ( size_t i = 0; i < eos._edges.size(); ++i )
8236         {
8237           _LayerEdge* ledge = eos._edges[ i ];
8238           for ( size_t iN = 0; iN < ledge->_neibors.size(); ++iN )
8239           {
8240             _LayerEdge* neibor = ledge->_neibors[iN];
8241             if ( neibor->_nodes[0]->GetPosition()->GetDim() == 2 )
8242             {
8243               neibor->Set( _LayerEdge::NEAR_BOUNDARY );
8244               neibor->Set( _LayerEdge::MOVED );
8245               neibor->SetSmooLen( neibor->_len );
8246             }
8247           }
8248         }
8249     } // loop on sub-shapes of convFace._face
8250
8251     // Find FACEs adjacent to convFace._face that got necessity to smooth
8252     // as a result of normals modification
8253
8254     set< _EdgesOnShape* > adjFacesToSmooth;
8255     for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
8256     {
8257       if ( centerCurves[ iE ]._adjFace.IsNull() ||
8258            centerCurves[ iE ]._adjFaceToSmooth )
8259         continue;
8260       for ( size_t iLE = 0; iLE < centerCurves[ iE ]._ledges.size(); ++iLE )
8261       {
8262         if ( centerCurves[ iE ]._ledges[ iLE ]->_cosin > theMinSmoothCosin )
8263         {
8264           adjFacesToSmooth.insert( data.GetShapeEdges( centerCurves[ iE ]._adjFace ));
8265           break;
8266         }
8267       }
8268     }
8269     data.AddShapesToSmooth( adjFacesToSmooth );
8270
8271     dumpFunctionEnd();
8272
8273
8274   } // loop on data._convexFaces
8275
8276   return true;
8277 }
8278
8279 //================================================================================
8280 /*!
8281  * \brief Return max curvature of a FACE
8282  */
8283 //================================================================================
8284
8285 double _ConvexFace::GetMaxCurvature( _SolidData&         data,
8286                                      _EdgesOnShape&      eof,
8287                                      BRepLProp_SLProps&  surfProp,
8288                                      SMESH_MesherHelper& helper)
8289 {
8290   double maxCurvature = 0;
8291
8292   TopoDS_Face F = TopoDS::Face( eof._shape );
8293
8294   const int           nbTestPnt = 5;
8295   const double        oriFactor = ( F.Orientation() == TopAbs_REVERSED ? +1. : -1. );
8296   SMESH_subMeshIteratorPtr smIt = eof._subMesh->getDependsOnIterator(/*includeSelf=*/true);
8297   while ( smIt->more() )
8298   {
8299     SMESH_subMesh* sm = smIt->next();
8300     const TGeomID subID = sm->GetId();
8301
8302     // find _LayerEdge's of a sub-shape
8303     _EdgesOnShape* eos;
8304     if (( eos = data.GetShapeEdges( subID )))
8305       this->_subIdToEOS.insert( make_pair( subID, eos ));
8306     else
8307       continue;
8308
8309     // check concavity and curvature and limit data._stepSize
8310     const double minCurvature =
8311       1. / ( eos->_hyp.GetTotalThickness() * ( 1 + theThickToIntersection ));
8312     size_t iStep = Max( 1, eos->_edges.size() / nbTestPnt );
8313     for ( size_t i = 0; i < eos->_edges.size(); i += iStep )
8314     {
8315       gp_XY uv = helper.GetNodeUV( F, eos->_edges[ i ]->_nodes[0] );
8316       surfProp.SetParameters( uv.X(), uv.Y() );
8317       if ( surfProp.IsCurvatureDefined() )
8318       {
8319         double curvature = Max( surfProp.MaxCurvature() * oriFactor,
8320                                 surfProp.MinCurvature() * oriFactor );
8321         maxCurvature = Max( maxCurvature, curvature );
8322
8323         if ( curvature > minCurvature )
8324           this->_isTooCurved = true;
8325       }
8326     }
8327   } // loop on sub-shapes of the FACE
8328
8329   return maxCurvature;
8330 }
8331
8332 //================================================================================
8333 /*!
8334  * \brief Finds a center of curvature of a surface at a _LayerEdge
8335  */
8336 //================================================================================
8337
8338 bool _ConvexFace::GetCenterOfCurvature( _LayerEdge*         ledge,
8339                                         BRepLProp_SLProps&  surfProp,
8340                                         SMESH_MesherHelper& helper,
8341                                         gp_Pnt &            center ) const
8342 {
8343   gp_XY uv = helper.GetNodeUV( _face, ledge->_nodes[0] );
8344   surfProp.SetParameters( uv.X(), uv.Y() );
8345   if ( !surfProp.IsCurvatureDefined() )
8346     return false;
8347
8348   const double oriFactor = ( _face.Orientation() == TopAbs_REVERSED ? +1. : -1. );
8349   double surfCurvatureMax = surfProp.MaxCurvature() * oriFactor;
8350   double surfCurvatureMin = surfProp.MinCurvature() * oriFactor;
8351   if ( surfCurvatureMin > surfCurvatureMax )
8352     center = surfProp.Value().Translated( surfProp.Normal().XYZ() / surfCurvatureMin * oriFactor );
8353   else
8354     center = surfProp.Value().Translated( surfProp.Normal().XYZ() / surfCurvatureMax * oriFactor );
8355
8356   return true;
8357 }
8358
8359 //================================================================================
8360 /*!
8361  * \brief Check that prisms are not distorted
8362  */
8363 //================================================================================
8364
8365 bool _ConvexFace::CheckPrisms() const
8366 {
8367   double vol = 0;
8368   for ( size_t i = 0; i < _simplexTestEdges.size(); ++i )
8369   {
8370     const _LayerEdge* edge = _simplexTestEdges[i];
8371     SMESH_TNodeXYZ tgtXYZ( edge->_nodes.back() );
8372     for ( size_t j = 0; j < edge->_simplices.size(); ++j )
8373       if ( !edge->_simplices[j].IsForward( edge->_nodes[0], tgtXYZ, vol ))
8374       {
8375         debugMsg( "Bad simplex of _simplexTestEdges ("
8376                   << " "<< edge->_nodes[0]->GetID()<< " "<< tgtXYZ._node->GetID()
8377                   << " "<< edge->_simplices[j]._nPrev->GetID()
8378                   << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
8379         return false;
8380       }
8381   }
8382   return true;
8383 }
8384
8385 //================================================================================
8386 /*!
8387  * \brief Try to compute a new normal by interpolating normals of _LayerEdge's
8388  *        stored in this _CentralCurveOnEdge.
8389  *  \param [in] center - curvature center of a point of another _CentralCurveOnEdge.
8390  *  \param [in,out] newNormal - current normal at this point, to be redefined
8391  *  \return bool - true if succeeded.
8392  */
8393 //================================================================================
8394
8395 bool _CentralCurveOnEdge::FindNewNormal( const gp_Pnt& center, gp_XYZ& newNormal )
8396 {
8397   if ( this->_isDegenerated )
8398     return false;
8399
8400   // find two centers the given one lies between
8401
8402   for ( size_t i = 0, nb = _curvaCenters.size()-1;  i < nb;  ++i )
8403   {
8404     double sl2 = 1.001 * _segLength2[ i ];
8405
8406     double d1 = center.SquareDistance( _curvaCenters[ i ]);
8407     if ( d1 > sl2 )
8408       continue;
8409     
8410     double d2 = center.SquareDistance( _curvaCenters[ i+1 ]);
8411     if ( d2 > sl2 || d2 + d1 < 1e-100 )
8412       continue;
8413
8414     d1 = Sqrt( d1 );
8415     d2 = Sqrt( d2 );
8416     double r = d1 / ( d1 + d2 );
8417     gp_XYZ norm = (( 1. - r ) * _ledges[ i   ]->_normal +
8418                    (      r ) * _ledges[ i+1 ]->_normal );
8419     norm.Normalize();
8420
8421     newNormal += norm;
8422     double sz = newNormal.Modulus();
8423     if ( sz < 1e-200 )
8424       break;
8425     newNormal /= sz;
8426     return true;
8427   }
8428   return false;
8429 }
8430
8431 //================================================================================
8432 /*!
8433  * \brief Set shape members
8434  */
8435 //================================================================================
8436
8437 void _CentralCurveOnEdge::SetShapes( const TopoDS_Edge&  edge,
8438                                      const _ConvexFace&  convFace,
8439                                      _SolidData&         data,
8440                                      SMESH_MesherHelper& helper)
8441 {
8442   _edge = edge;
8443
8444   PShapeIteratorPtr fIt = helper.GetAncestors( edge, *helper.GetMesh(), TopAbs_FACE );
8445   while ( const TopoDS_Shape* F = fIt->next())
8446     if ( !convFace._face.IsSame( *F ))
8447     {
8448       _adjFace = TopoDS::Face( *F );
8449       _adjFaceToSmooth = false;
8450       // _adjFace already in a smoothing queue ?
8451       if ( _EdgesOnShape* eos = data.GetShapeEdges( _adjFace ))
8452         _adjFaceToSmooth = eos->_toSmooth;
8453       break;
8454     }
8455 }
8456
8457 //================================================================================
8458 /*!
8459  * \brief Looks for intersection of it's last segment with faces
8460  *  \param distance - returns shortest distance from the last node to intersection
8461  */
8462 //================================================================================
8463
8464 bool _LayerEdge::FindIntersection( SMESH_ElementSearcher&   searcher,
8465                                    double &                 distance,
8466                                    const double&            epsilon,
8467                                    _EdgesOnShape&           eos,
8468                                    const SMDS_MeshElement** intFace)
8469 {
8470   vector< const SMDS_MeshElement* > suspectFaces;
8471   double segLen;
8472   gp_Ax1 lastSegment = LastSegment( segLen, eos );
8473   searcher.GetElementsNearLine( lastSegment, SMDSAbs_Face, suspectFaces );
8474
8475   bool segmentIntersected = false;
8476   distance = Precision::Infinite();
8477   int iFace = -1; // intersected face
8478   for ( size_t j = 0 ; j < suspectFaces.size() /*&& !segmentIntersected*/; ++j )
8479   {
8480     const SMDS_MeshElement* face = suspectFaces[j];
8481     if ( face->GetNodeIndex( _nodes.back() ) >= 0 ||
8482          face->GetNodeIndex( _nodes[0]     ) >= 0 )
8483       continue; // face sharing _LayerEdge node
8484     const int nbNodes = face->NbCornerNodes();
8485     bool intFound = false;
8486     double dist;
8487     SMDS_MeshElement::iterator nIt = face->begin_nodes();
8488     if ( nbNodes == 3 )
8489     {
8490       intFound = SegTriaInter( lastSegment, *nIt++, *nIt++, *nIt++, dist, epsilon );
8491     }
8492     else
8493     {
8494       const SMDS_MeshNode* tria[3];
8495       tria[0] = *nIt++;
8496       tria[1] = *nIt++;
8497       for ( int n2 = 2; n2 < nbNodes && !intFound; ++n2 )
8498       {
8499         tria[2] = *nIt++;
8500         intFound = SegTriaInter(lastSegment, tria[0], tria[1], tria[2], dist, epsilon );
8501         tria[1] = tria[2];
8502       }
8503     }
8504     if ( intFound )
8505     {
8506       if ( dist < segLen*(1.01) && dist > -(_len*_lenFactor-segLen) )
8507         segmentIntersected = true;
8508       if ( distance > dist )
8509         distance = dist, iFace = j;
8510     }
8511   }
8512   if ( intFace ) *intFace = ( iFace != -1 ) ? suspectFaces[iFace] : 0;
8513
8514   distance -= segLen;
8515
8516   if ( segmentIntersected )
8517   {
8518 #ifdef __myDEBUG
8519     SMDS_MeshElement::iterator nIt = suspectFaces[iFace]->begin_nodes();
8520     gp_XYZ intP( lastSegment.Location().XYZ() + lastSegment.Direction().XYZ() * ( distance+segLen ));
8521     cout << "nodes: tgt " << _nodes.back()->GetID() << " src " << _nodes[0]->GetID()
8522          << ", intersection with face ("
8523          << (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()
8524          << ") at point (" << intP.X() << ", " << intP.Y() << ", " << intP.Z()
8525          << ") distance = " << distance << endl;
8526 #endif
8527   }
8528
8529   return segmentIntersected;
8530 }
8531
8532 //================================================================================
8533 /*!
8534  * \brief Returns a point used to check orientation of _simplices
8535  */
8536 //================================================================================
8537
8538 gp_XYZ _LayerEdge::PrevCheckPos( _EdgesOnShape* eos ) const
8539 {
8540   size_t i = Is( NORMAL_UPDATED ) && IsOnFace() ? _pos.size()-2 : 0;
8541
8542   if ( !eos || eos->_sWOL.IsNull() )
8543     return _pos[ i ];
8544
8545   if ( eos->SWOLType() == TopAbs_EDGE )
8546   {
8547     return BRepAdaptor_Curve( TopoDS::Edge( eos->_sWOL )).Value( _pos[i].X() ).XYZ();
8548   }
8549   //else //  TopAbs_FACE
8550
8551   return BRepAdaptor_Surface( TopoDS::Face( eos->_sWOL )).Value(_pos[i].X(), _pos[i].Y() ).XYZ();
8552 }
8553
8554 //================================================================================
8555 /*!
8556  * \brief Returns size and direction of the last segment
8557  */
8558 //================================================================================
8559
8560 gp_Ax1 _LayerEdge::LastSegment(double& segLen, _EdgesOnShape& eos) const
8561 {
8562   // find two non-coincident positions
8563   gp_XYZ orig = _pos.back();
8564   gp_XYZ vec;
8565   int iPrev = _pos.size() - 2;
8566   //const double tol = ( _len > 0 ) ? 0.3*_len : 1e-100; // adjusted for IPAL52478 + PAL22576
8567   const double tol = ( _len > 0 ) ? ( 1e-6 * _len ) : 1e-100;
8568   while ( iPrev >= 0 )
8569   {
8570     vec = orig - _pos[iPrev];
8571     if ( vec.SquareModulus() > tol*tol )
8572       break;
8573     else
8574       iPrev--;
8575   }
8576
8577   // make gp_Ax1
8578   gp_Ax1 segDir;
8579   if ( iPrev < 0 )
8580   {
8581     segDir.SetLocation( SMESH_TNodeXYZ( _nodes[0] ));
8582     segDir.SetDirection( _normal );
8583     segLen = 0;
8584   }
8585   else
8586   {
8587     gp_Pnt pPrev = _pos[ iPrev ];
8588     if ( !eos._sWOL.IsNull() )
8589     {
8590       TopLoc_Location loc;
8591       if ( eos.SWOLType() == TopAbs_EDGE )
8592       {
8593         double f,l;
8594         Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( eos._sWOL ), loc, f,l);
8595         pPrev = curve->Value( pPrev.X() ).Transformed( loc );
8596       }
8597       else
8598       {
8599         Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face( eos._sWOL ), loc );
8600         pPrev = surface->Value( pPrev.X(), pPrev.Y() ).Transformed( loc );
8601       }
8602       vec = SMESH_TNodeXYZ( _nodes.back() ) - pPrev.XYZ();
8603     }
8604     segDir.SetLocation( pPrev );
8605     segDir.SetDirection( vec );
8606     segLen = vec.Modulus();
8607   }
8608
8609   return segDir;
8610 }
8611
8612 //================================================================================
8613 /*!
8614  * \brief Return the last (or \a which) position of the target node on a FACE. 
8615  *  \param [in] F - the FACE this _LayerEdge is inflated along
8616  *  \param [in] which - index of position
8617  *  \return gp_XY - result UV
8618  */
8619 //================================================================================
8620
8621 gp_XY _LayerEdge::LastUV( const TopoDS_Face& F, _EdgesOnShape& eos, int which ) const
8622 {
8623   if ( F.IsSame( eos._sWOL )) // F is my FACE
8624     return gp_XY( _pos.back().X(), _pos.back().Y() );
8625
8626   if ( eos.SWOLType() != TopAbs_EDGE ) // wrong call
8627     return gp_XY( 1e100, 1e100 );
8628
8629   // _sWOL is EDGE of F; _pos.back().X() is the last U on the EDGE
8630   double f, l, u = _pos[ which < 0 ? _pos.size()-1 : which ].X();
8631   Handle(Geom2d_Curve) C2d = BRep_Tool::CurveOnSurface( TopoDS::Edge(eos._sWOL), F, f,l);
8632   if ( !C2d.IsNull() && f <= u && u <= l )
8633     return C2d->Value( u ).XY();
8634
8635   return gp_XY( 1e100, 1e100 );
8636 }
8637
8638 //================================================================================
8639 /*!
8640  * \brief Test intersection of the last segment with a given triangle
8641  *   using Moller-Trumbore algorithm
8642  * Intersection is detected if distance to intersection is less than _LayerEdge._len
8643  */
8644 //================================================================================
8645
8646 bool _LayerEdge::SegTriaInter( const gp_Ax1& lastSegment,
8647                                const gp_XYZ& vert0,
8648                                const gp_XYZ& vert1,
8649                                const gp_XYZ& vert2,
8650                                double&       t,
8651                                const double& EPSILON) const
8652 {
8653   const gp_Pnt& orig = lastSegment.Location();
8654   const gp_Dir& dir  = lastSegment.Direction();
8655
8656   /* calculate distance from vert0 to ray origin */
8657   //gp_XYZ tvec = orig.XYZ() - vert0;
8658
8659   //if ( tvec * dir > EPSILON )
8660     // intersected face is at back side of the temporary face this _LayerEdge belongs to
8661     //return false;
8662
8663   gp_XYZ edge1 = vert1 - vert0;
8664   gp_XYZ edge2 = vert2 - vert0;
8665
8666   /* begin calculating determinant - also used to calculate U parameter */
8667   gp_XYZ pvec = dir.XYZ() ^ edge2;
8668
8669   /* if determinant is near zero, ray lies in plane of triangle */
8670   double det = edge1 * pvec;
8671
8672   const double ANGL_EPSILON = 1e-12;
8673   if ( det > -ANGL_EPSILON && det < ANGL_EPSILON )
8674     return false;
8675
8676   /* calculate distance from vert0 to ray origin */
8677   gp_XYZ tvec = orig.XYZ() - vert0;
8678
8679   /* calculate U parameter and test bounds */
8680   double u = ( tvec * pvec ) / det;
8681   //if (u < 0.0 || u > 1.0)
8682   if ( u < -EPSILON || u > 1.0 + EPSILON )
8683     return false;
8684
8685   /* prepare to test V parameter */
8686   gp_XYZ qvec = tvec ^ edge1;
8687
8688   /* calculate V parameter and test bounds */
8689   double v = (dir.XYZ() * qvec) / det;
8690   //if ( v < 0.0 || u + v > 1.0 )
8691   if ( v < -EPSILON || u + v > 1.0 + EPSILON )
8692     return false;
8693
8694   /* calculate t, ray intersects triangle */
8695   t = (edge2 * qvec) / det;
8696
8697   //return true;
8698   return t > 0.;
8699 }
8700
8701 //================================================================================
8702 /*!
8703  * \brief _LayerEdge, located at a concave VERTEX of a FACE, moves target nodes of
8704  *        neighbor _LayerEdge's by it's own inflation vector.
8705  *  \param [in] eov - EOS of the VERTEX
8706  *  \param [in] eos - EOS of the FACE
8707  *  \param [in] step - inflation step
8708  *  \param [in,out] badSmooEdges - tangled _LayerEdge's
8709  */
8710 //================================================================================
8711
8712 void _LayerEdge::MoveNearConcaVer( const _EdgesOnShape*    eov,
8713                                    const _EdgesOnShape*    eos,
8714                                    const int               step,
8715                                    vector< _LayerEdge* > & badSmooEdges )
8716 {
8717   // check if any of _neibors is in badSmooEdges
8718   if ( std::find_first_of( _neibors.begin(), _neibors.end(),
8719                            badSmooEdges.begin(), badSmooEdges.end() ) == _neibors.end() )
8720     return;
8721
8722   // get all edges to move
8723
8724   set< _LayerEdge* > edges;
8725
8726   // find a distance between _LayerEdge on VERTEX and its neighbors
8727   gp_XYZ  curPosV = SMESH_TNodeXYZ( _nodes.back() );
8728   double dist2 = 0;
8729   for ( size_t i = 0; i < _neibors.size(); ++i )
8730   {
8731     _LayerEdge* nEdge = _neibors[i];
8732     if ( nEdge->_nodes[0]->getshapeId() == eos->_shapeID )
8733     {
8734       edges.insert( nEdge );
8735       dist2 = Max( dist2, ( curPosV - nEdge->_pos.back() ).SquareModulus() );
8736     }
8737   }
8738   // add _LayerEdge's close to curPosV
8739   size_t nbE;
8740   do {
8741     nbE = edges.size();
8742     for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8743     {
8744       _LayerEdge* edgeF = *e;
8745       for ( size_t i = 0; i < edgeF->_neibors.size(); ++i )
8746       {
8747         _LayerEdge* nEdge = edgeF->_neibors[i];
8748         if ( nEdge->_nodes[0]->getshapeId() == eos->_shapeID &&
8749              dist2 > ( curPosV - nEdge->_pos.back() ).SquareModulus() )
8750           edges.insert( nEdge );
8751       }
8752     }
8753   }
8754   while ( nbE < edges.size() );
8755
8756   // move the target node of the got edges
8757
8758   gp_XYZ prevPosV = PrevPos();
8759   if ( eov->SWOLType() == TopAbs_EDGE )
8760   {
8761     BRepAdaptor_Curve curve ( TopoDS::Edge( eov->_sWOL ));
8762     prevPosV = curve.Value( prevPosV.X() ).XYZ();
8763   }
8764   else if ( eov->SWOLType() == TopAbs_FACE )
8765   {
8766     BRepAdaptor_Surface surface( TopoDS::Face( eov->_sWOL ));
8767     prevPosV = surface.Value( prevPosV.X(), prevPosV.Y() ).XYZ();
8768   }
8769
8770   SMDS_FacePositionPtr fPos;
8771   //double r = 1. - Min( 0.9, step / 10. );
8772   for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8773   {
8774     _LayerEdge*       edgeF = *e;
8775     const gp_XYZ     prevVF = edgeF->PrevPos() - prevPosV;
8776     const gp_XYZ    newPosF = curPosV + prevVF;
8777     SMDS_MeshNode* tgtNodeF = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
8778     tgtNodeF->setXYZ( newPosF.X(), newPosF.Y(), newPosF.Z() );
8779     edgeF->_pos.back() = newPosF;
8780     dumpMoveComm( tgtNodeF, "MoveNearConcaVer" ); // debug
8781
8782     // set _curvature to make edgeF updated by putOnOffsetSurface()
8783     if ( !edgeF->_curvature )
8784       if (( fPos = edgeF->_nodes[0]->GetPosition() ))
8785       {
8786         edgeF->_curvature = _Factory::NewCurvature();
8787         edgeF->_curvature->_r = 0;
8788         edgeF->_curvature->_k = 0;
8789         edgeF->_curvature->_h2lenRatio = 0;
8790         edgeF->_curvature->_uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
8791       }
8792   }
8793   // gp_XYZ inflationVec( SMESH_TNodeXYZ( _nodes.back() ) -
8794   //                      SMESH_TNodeXYZ( _nodes[0]    ));
8795   // for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8796   // {
8797   //   _LayerEdge*      edgeF = *e;
8798   //   gp_XYZ          newPos = SMESH_TNodeXYZ( edgeF->_nodes[0] ) + inflationVec;
8799   //   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
8800   //   tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8801   //   edgeF->_pos.back() = newPosF;
8802   //   dumpMoveComm( tgtNode, "MoveNearConcaVer" ); // debug
8803   // }
8804
8805   // smooth _LayerEdge's around moved nodes
8806   //size_t nbBadBefore = badSmooEdges.size();
8807   for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8808   {
8809     _LayerEdge* edgeF = *e;
8810     for ( size_t j = 0; j < edgeF->_neibors.size(); ++j )
8811       if ( edgeF->_neibors[j]->_nodes[0]->getshapeId() == eos->_shapeID )
8812         //&& !edges.count( edgeF->_neibors[j] ))
8813       {
8814         _LayerEdge* edgeFN = edgeF->_neibors[j];
8815         edgeFN->Unset( SMOOTHED );
8816         int nbBad = edgeFN->Smooth( step, /*isConcaFace=*/true, /*findBest=*/true );
8817         // if ( nbBad > 0 )
8818         // {
8819         //   gp_XYZ         newPos = SMESH_TNodeXYZ( edgeFN->_nodes[0] ) + inflationVec;
8820         //   const gp_XYZ& prevPos = edgeFN->_pos[ edgeFN->_pos.size()-2 ];
8821         //   int        nbBadAfter = edgeFN->_simplices.size();
8822         //   double vol;
8823         //   for ( size_t iS = 0; iS < edgeFN->_simplices.size(); ++iS )
8824         //   {
8825         //     nbBadAfter -= edgeFN->_simplices[iS].IsForward( &prevPos, &newPos, vol );
8826         //   }
8827         //   if ( nbBadAfter <= nbBad )
8828         //   {
8829         //     SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeFN->_nodes.back() );
8830         //     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8831         //     edgeF->_pos.back() = newPosF;
8832         //     dumpMoveComm( tgtNode, "MoveNearConcaVer 2" ); // debug
8833         //     nbBad = nbBadAfter;
8834         //   }
8835         // }
8836         if ( nbBad > 0 )
8837           badSmooEdges.push_back( edgeFN );
8838       }
8839   }
8840     // move a bit not smoothed around moved nodes
8841   //   for ( size_t i = nbBadBefore; i < badSmooEdges.size(); ++i )
8842   //   {
8843   //   _LayerEdge*      edgeF = badSmooEdges[i];
8844   //   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
8845   //   gp_XYZ         newPos1 = SMESH_TNodeXYZ( edgeF->_nodes[0] ) + inflationVec;
8846   //   gp_XYZ         newPos2 = 0.5 * ( newPos1 + SMESH_TNodeXYZ( tgtNode ));
8847   //   tgtNode->setXYZ( newPos2.X(), newPos2.Y(), newPos2.Z() );
8848   //   edgeF->_pos.back() = newPosF;
8849   //   dumpMoveComm( tgtNode, "MoveNearConcaVer 2" ); // debug
8850   // }
8851 }
8852
8853 //================================================================================
8854 /*!
8855  * \brief Perform smooth of _LayerEdge's based on EDGE's
8856  *  \retval bool - true if node has been moved
8857  */
8858 //================================================================================
8859
8860 bool _LayerEdge::SmoothOnEdge(Handle(ShapeAnalysis_Surface)& surface,
8861                               const TopoDS_Face&             F,
8862                               SMESH_MesherHelper&            helper)
8863 {
8864   ASSERT( IsOnEdge() );
8865
8866   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _nodes.back() );
8867   SMESH_TNodeXYZ oldPos( tgtNode );
8868   double dist01, distNewOld;
8869   
8870   SMESH_TNodeXYZ p0( _2neibors->tgtNode(0));
8871   SMESH_TNodeXYZ p1( _2neibors->tgtNode(1));
8872   dist01 = p0.Distance( _2neibors->tgtNode(1) );
8873
8874   gp_Pnt newPos = p0 * _2neibors->_wgt[0] + p1 * _2neibors->_wgt[1];
8875   double lenDelta = 0;
8876   if ( _curvature )
8877   {
8878     //lenDelta = _curvature->lenDelta( _len );
8879     lenDelta = _curvature->lenDeltaByDist( dist01 );
8880     newPos.ChangeCoord() += _normal * lenDelta;
8881   }
8882
8883   distNewOld = newPos.Distance( oldPos );
8884
8885   if ( F.IsNull() )
8886   {
8887     if ( _2neibors->_plnNorm )
8888     {
8889       // put newPos on the plane defined by source node and _plnNorm
8890       gp_XYZ new2src = SMESH_TNodeXYZ( _nodes[0] ) - newPos.XYZ();
8891       double new2srcProj = (*_2neibors->_plnNorm) * new2src;
8892       newPos.ChangeCoord() += (*_2neibors->_plnNorm) * new2srcProj;
8893     }
8894     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8895     _pos.back() = newPos.XYZ();
8896   }
8897   else
8898   {
8899     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8900     gp_XY uv( Precision::Infinite(), 0 );
8901     helper.CheckNodeUV( F, tgtNode, uv, 1e-10, /*force=*/true );
8902     _pos.back().SetCoord( uv.X(), uv.Y(), 0 );
8903
8904     newPos = surface->Value( uv );
8905     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8906   }
8907
8908   // commented for IPAL0052478
8909   // if ( _curvature && lenDelta < 0 )
8910   // {
8911   //   gp_Pnt prevPos( _pos[ _pos.size()-2 ]);
8912   //   _len -= prevPos.Distance( oldPos );
8913   //   _len += prevPos.Distance( newPos );
8914   // }
8915   bool moved = distNewOld > dist01/50;
8916   //if ( moved )
8917   dumpMove( tgtNode ); // debug
8918
8919   return moved;
8920 }
8921
8922 //================================================================================
8923 /*!
8924  * \brief Perform 3D smooth of nodes inflated from FACE. No check of validity
8925  */
8926 //================================================================================
8927
8928 void _LayerEdge::SmoothWoCheck()
8929 {
8930   if ( Is( DIFFICULT ))
8931     return;
8932
8933   bool moved = Is( SMOOTHED );
8934   for ( size_t i = 0; i < _neibors.size()  &&  !moved; ++i )
8935     moved = _neibors[i]->Is( SMOOTHED );
8936   if ( !moved )
8937     return;
8938
8939   gp_XYZ newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
8940
8941   SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
8942   n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
8943   _pos.back() = newPos;
8944
8945   dumpMoveComm( n, SMESH_Comment("No check - ") << _funNames[ smooFunID() ]);
8946 }
8947
8948 //================================================================================
8949 /*!
8950  * \brief Checks validity of _neibors on EDGEs and VERTEXes
8951  */
8952 //================================================================================
8953
8954 int _LayerEdge::CheckNeiborsOnBoundary( vector< _LayerEdge* >* badNeibors, bool * needSmooth )
8955 {
8956   if ( ! Is( NEAR_BOUNDARY ))
8957     return 0;
8958
8959   int nbBad = 0;
8960   double vol;
8961   for ( size_t iN = 0; iN < _neibors.size(); ++iN )
8962   {
8963     _LayerEdge* eN = _neibors[iN];
8964     if ( eN->_nodes[0]->getshapeId() == _nodes[0]->getshapeId() )
8965       continue;
8966     if ( needSmooth )
8967       *needSmooth |= ( eN->Is( _LayerEdge::BLOCKED ) ||
8968                        eN->Is( _LayerEdge::NORMAL_UPDATED ) ||
8969                        eN->_pos.size() != _pos.size() );
8970
8971     SMESH_TNodeXYZ curPosN ( eN->_nodes.back() );
8972     SMESH_TNodeXYZ prevPosN( eN->_nodes[0] );
8973     for ( size_t i = 0; i < eN->_simplices.size(); ++i )
8974       if ( eN->_nodes.size() > 1 &&
8975            eN->_simplices[i].Includes( _nodes.back() ) &&
8976            !eN->_simplices[i].IsForward( &prevPosN, &curPosN, vol ))
8977       {
8978         ++nbBad;
8979         if ( badNeibors )
8980         {
8981           badNeibors->push_back( eN );
8982           debugMsg("Bad boundary simplex ( "
8983                    << " "<< eN->_nodes[0]->GetID()
8984                    << " "<< eN->_nodes.back()->GetID()
8985                    << " "<< eN->_simplices[i]._nPrev->GetID()
8986                    << " "<< eN->_simplices[i]._nNext->GetID() << " )" );
8987         }
8988         else
8989         {
8990           break;
8991         }
8992       }
8993   }
8994   return nbBad;
8995 }
8996
8997 //================================================================================
8998 /*!
8999  * \brief Perform 'smart' 3D smooth of nodes inflated from FACE
9000  *  \retval int - nb of bad simplices around this _LayerEdge
9001  */
9002 //================================================================================
9003
9004 int _LayerEdge::Smooth(const int step, bool findBest, vector< _LayerEdge* >& toSmooth )
9005 {
9006   if ( !Is( MOVED ) || Is( SMOOTHED ) || Is( BLOCKED ))
9007     return 0; // shape of simplices not changed
9008   if ( _simplices.size() < 2 )
9009     return 0; // _LayerEdge inflated along EDGE or FACE
9010
9011   if ( Is( DIFFICULT )) // || Is( ON_CONCAVE_FACE )
9012     findBest = true;
9013
9014   const gp_XYZ& curPos  = _pos.back();
9015   const gp_XYZ& prevPos = _pos[0]; //PrevPos();
9016
9017   // quality metrics (orientation) of tetras around _tgtNode
9018   int nbOkBefore = 0;
9019   double vol, minVolBefore = 1e100;
9020   for ( size_t i = 0; i < _simplices.size(); ++i )
9021   {
9022     nbOkBefore += _simplices[i].IsForward( &prevPos, &curPos, vol );
9023     minVolBefore = Min( minVolBefore, vol );
9024   }
9025   int nbBad = _simplices.size() - nbOkBefore;
9026
9027   bool bndNeedSmooth = false;
9028   if ( nbBad == 0 )
9029     nbBad = CheckNeiborsOnBoundary( 0, & bndNeedSmooth );
9030   if ( nbBad > 0 )
9031     Set( DISTORTED );
9032
9033   // evaluate min angle
9034   if ( nbBad == 0 && !findBest && !bndNeedSmooth )
9035   {
9036     size_t nbGoodAngles = _simplices.size();
9037     double angle;
9038     for ( size_t i = 0; i < _simplices.size(); ++i )
9039     {
9040       if ( !_simplices[i].IsMinAngleOK( curPos, angle ) && angle > _minAngle )
9041         --nbGoodAngles;
9042     }
9043     if ( nbGoodAngles == _simplices.size() )
9044     {
9045       Unset( MOVED );
9046       return 0;
9047     }
9048   }
9049   if ( Is( ON_CONCAVE_FACE ))
9050     findBest = true;
9051
9052   if ( step % 2 == 0 )
9053     findBest = false;
9054
9055   if ( Is( ON_CONCAVE_FACE ) && !findBest ) // alternate FUN_CENTROIDAL and FUN_LAPLACIAN
9056   {
9057     if ( _smooFunction == _funs[ FUN_LAPLACIAN ] )
9058       _smooFunction = _funs[ FUN_CENTROIDAL ];
9059     else
9060       _smooFunction = _funs[ FUN_LAPLACIAN ];
9061   }
9062
9063   // compute new position for the last _pos using different _funs
9064   gp_XYZ newPos;
9065   bool moved = false;
9066   for ( int iFun = -1; iFun < theNbSmooFuns; ++iFun )
9067   {
9068     if ( iFun < 0 )
9069       newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
9070     else if ( _funs[ iFun ] == _smooFunction )
9071       continue; // _smooFunction again
9072     else if ( step > 1 )
9073       newPos = (this->*_funs[ iFun ])(); // try other smoothing fun
9074     else
9075       break; // let "easy" functions improve elements around distorted ones
9076
9077     if ( _curvature )
9078     {
9079       double delta  = _curvature->lenDelta( _len );
9080       if ( delta > 0 )
9081         newPos += _normal * delta;
9082       else
9083       {
9084         double segLen = _normal * ( newPos - prevPos );
9085         if ( segLen + delta > 0 )
9086           newPos += _normal * delta;
9087       }
9088       // double segLenChange = _normal * ( curPos - newPos );
9089       // newPos += 0.5 * _normal * segLenChange;
9090     }
9091
9092     int nbOkAfter = 0;
9093     double minVolAfter = 1e100;
9094     for ( size_t i = 0; i < _simplices.size(); ++i )
9095     {
9096       nbOkAfter += _simplices[i].IsForward( &prevPos, &newPos, vol );
9097       minVolAfter = Min( minVolAfter, vol );
9098     }
9099     // get worse?
9100     if ( nbOkAfter < nbOkBefore )
9101       continue;
9102
9103     if (( findBest ) &&
9104         ( nbOkAfter == nbOkBefore ) &&
9105         ( minVolAfter <= minVolBefore ))
9106       continue;
9107
9108     nbBad        = _simplices.size() - nbOkAfter;
9109     minVolBefore = minVolAfter;
9110     nbOkBefore   = nbOkAfter;
9111     moved        = true;
9112
9113     SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
9114     n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
9115     _pos.back() = newPos;
9116
9117     dumpMoveComm( n, SMESH_Comment( _funNames[ iFun < 0 ? smooFunID() : iFun ] )
9118                   << (nbBad ? " --BAD" : ""));
9119
9120     if ( iFun > -1 )
9121     {
9122       continue; // look for a better function
9123     }
9124
9125     if ( !findBest )
9126       break;
9127
9128   } // loop on smoothing functions
9129
9130   if ( moved ) // notify _neibors
9131   {
9132     Set( SMOOTHED );
9133     for ( size_t i = 0; i < _neibors.size(); ++i )
9134       if ( !_neibors[i]->Is( MOVED ))
9135       {
9136         _neibors[i]->Set( MOVED );
9137         toSmooth.push_back( _neibors[i] );
9138       }
9139   }
9140
9141   return nbBad;
9142 }
9143
9144 //================================================================================
9145 /*!
9146  * \brief Perform 'smart' 3D smooth of nodes inflated from FACE
9147  *  \retval int - nb of bad simplices around this _LayerEdge
9148  */
9149 //================================================================================
9150
9151 int _LayerEdge::Smooth(const int step, const bool isConcaveFace, bool findBest )
9152 {
9153   if ( !_smooFunction )
9154     return 0; // _LayerEdge inflated along EDGE or FACE
9155   if ( Is( BLOCKED ))
9156     return 0; // not inflated
9157
9158   const gp_XYZ& curPos  = _pos.back();
9159   const gp_XYZ& prevPos = _pos[0]; //PrevCheckPos();
9160
9161   // quality metrics (orientation) of tetras around _tgtNode
9162   int nbOkBefore = 0;
9163   double vol, minVolBefore = 1e100;
9164   for ( size_t i = 0; i < _simplices.size(); ++i )
9165   {
9166     nbOkBefore += _simplices[i].IsForward( &prevPos, &curPos, vol );
9167     minVolBefore = Min( minVolBefore, vol );
9168   }
9169   int nbBad = _simplices.size() - nbOkBefore;
9170
9171   if ( isConcaveFace ) // alternate FUN_CENTROIDAL and FUN_LAPLACIAN
9172   {
9173     if      ( _smooFunction == _funs[ FUN_CENTROIDAL ] && step % 2 )
9174       _smooFunction = _funs[ FUN_LAPLACIAN ];
9175     else if ( _smooFunction == _funs[ FUN_LAPLACIAN ] && !( step % 2 ))
9176       _smooFunction = _funs[ FUN_CENTROIDAL ];
9177   }
9178
9179   // compute new position for the last _pos using different _funs
9180   gp_XYZ newPos;
9181   for ( int iFun = -1; iFun < theNbSmooFuns; ++iFun )
9182   {
9183     if ( iFun < 0 )
9184       newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
9185     else if ( _funs[ iFun ] == _smooFunction )
9186       continue; // _smooFunction again
9187     else if ( step > 1 )
9188       newPos = (this->*_funs[ iFun ])(); // try other smoothing fun
9189     else
9190       break; // let "easy" functions improve elements around distorted ones
9191
9192     if ( _curvature )
9193     {
9194       double delta  = _curvature->lenDelta( _len );
9195       if ( delta > 0 )
9196         newPos += _normal * delta;
9197       else
9198       {
9199         double segLen = _normal * ( newPos - prevPos );
9200         if ( segLen + delta > 0 )
9201           newPos += _normal * delta;
9202       }
9203       // double segLenChange = _normal * ( curPos - newPos );
9204       // newPos += 0.5 * _normal * segLenChange;
9205     }
9206
9207     int nbOkAfter = 0;
9208     double minVolAfter = 1e100;
9209     for ( size_t i = 0; i < _simplices.size(); ++i )
9210     {
9211       nbOkAfter += _simplices[i].IsForward( &prevPos, &newPos, vol );
9212       minVolAfter = Min( minVolAfter, vol );
9213     }
9214     // get worse?
9215     if ( nbOkAfter < nbOkBefore )
9216       continue;
9217     if (( isConcaveFace || findBest ) &&
9218         ( nbOkAfter == nbOkBefore ) &&
9219         ( minVolAfter <= minVolBefore )
9220         )
9221       continue;
9222
9223     nbBad        = _simplices.size() - nbOkAfter;
9224     minVolBefore = minVolAfter;
9225     nbOkBefore   = nbOkAfter;
9226
9227     SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
9228     n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
9229     _pos.back() = newPos;
9230
9231     dumpMoveComm( n, SMESH_Comment( _funNames[ iFun < 0 ? smooFunID() : iFun ] )
9232                   << ( nbBad ? "--BAD" : ""));
9233
9234     // commented for IPAL0052478
9235     // _len -= prevPos.Distance(SMESH_TNodeXYZ( n ));
9236     // _len += prevPos.Distance(newPos);
9237
9238     if ( iFun > -1 ) // findBest || the chosen _fun makes worse
9239     {
9240       //_smooFunction = _funs[ iFun ];
9241       // cout << "# " << _funNames[ iFun ] << "\t N:" << _nodes.back()->GetID()
9242       // << "\t nbBad: " << _simplices.size() - nbOkAfter
9243       // << " minVol: " << minVolAfter
9244       // << " " << newPos.X() << " " << newPos.Y() << " " << newPos.Z()
9245       // << endl;
9246       continue; // look for a better function
9247     }
9248
9249     if ( !findBest )
9250       break;
9251
9252   } // loop on smoothing functions
9253
9254   return nbBad;
9255 }
9256
9257 //================================================================================
9258 /*!
9259  * \brief Chooses a smoothing technique giving a position most close to an initial one.
9260  *        For a correct result, _simplices must contain nodes lying on geometry.
9261  */
9262 //================================================================================
9263
9264 void _LayerEdge::ChooseSmooFunction( const set< TGeomID >& concaveVertices,
9265                                      const TNode2Edge&     /*n2eMap*/)
9266 {
9267   if ( _smooFunction ) return;
9268
9269   // use smoothNefPolygon() near concaveVertices
9270   if ( !concaveVertices.empty() )
9271   {
9272     _smooFunction = _funs[ FUN_CENTROIDAL ];
9273
9274     Set( ON_CONCAVE_FACE );
9275
9276     for ( size_t i = 0; i < _simplices.size(); ++i )
9277     {
9278       if ( concaveVertices.count( _simplices[i]._nPrev->getshapeId() ))
9279       {
9280         _smooFunction = _funs[ FUN_NEFPOLY ];
9281
9282         // set FUN_CENTROIDAL to neighbor edges
9283         for ( i = 0; i < _neibors.size(); ++i )
9284         {
9285           if ( _neibors[i]->_nodes[0]->GetPosition()->GetDim() == 2 )
9286           {
9287             _neibors[i]->_smooFunction = _funs[ FUN_CENTROIDAL ];
9288           }
9289         }
9290         return;
9291       }
9292     }
9293
9294     // // this choice is done only if ( !concaveVertices.empty() ) for Grids/smesh/bugs_19/X1
9295     // // where the nodes are smoothed too far along a sphere thus creating
9296     // // inverted _simplices
9297     // double dist[theNbSmooFuns];
9298     // //double coef[theNbSmooFuns] = { 1., 1.2, 1.4, 1.4 };
9299     // double coef[theNbSmooFuns] = { 1., 1., 1., 1. };
9300
9301     // double minDist = Precision::Infinite();
9302     // gp_Pnt p = SMESH_TNodeXYZ( _nodes[0] );
9303     // for ( int i = 0; i < FUN_NEFPOLY; ++i )
9304     // {
9305     //   gp_Pnt newP = (this->*_funs[i])();
9306     //   dist[i] = p.SquareDistance( newP );
9307     //   if ( dist[i]*coef[i] < minDist )
9308     //   {
9309     //     _smooFunction = _funs[i];
9310     //     minDist = dist[i]*coef[i];
9311     //   }
9312     // }
9313   }
9314   else
9315   {
9316     _smooFunction = _funs[ FUN_LAPLACIAN ];
9317   }
9318   // int minDim = 3;
9319   // for ( size_t i = 0; i < _simplices.size(); ++i )
9320   //   minDim = Min( minDim, _simplices[i]._nPrev->GetPosition()->GetDim() );
9321   // if ( minDim == 0 )
9322   //   _smooFunction = _funs[ FUN_CENTROIDAL ];
9323   // else if ( minDim == 1 )
9324   //   _smooFunction = _funs[ FUN_CENTROIDAL ];
9325
9326
9327   // int iMin;
9328   // for ( int i = 0; i < FUN_NB; ++i )
9329   // {
9330   //   //cout << dist[i] << " ";
9331   //   if ( _smooFunction == _funs[i] ) {
9332   //     iMin = i;
9333   //     //debugMsg( fNames[i] );
9334   //     break;
9335   //   }
9336   // }
9337   // cout << _funNames[ iMin ] << "\t N:" << _nodes.back()->GetID() << endl;
9338 }
9339
9340 //================================================================================
9341 /*!
9342  * \brief Returns a name of _SmooFunction
9343  */
9344 //================================================================================
9345
9346 int _LayerEdge::smooFunID( _LayerEdge::PSmooFun fun) const
9347 {
9348   if ( !fun )
9349     fun = _smooFunction;
9350   for ( int i = 0; i < theNbSmooFuns; ++i )
9351     if ( fun == _funs[i] )
9352       return i;
9353
9354   return theNbSmooFuns;
9355 }
9356
9357 //================================================================================
9358 /*!
9359  * \brief Computes a new node position using Laplacian smoothing
9360  */
9361 //================================================================================
9362
9363 gp_XYZ _LayerEdge::smoothLaplacian()
9364 {
9365   gp_XYZ newPos (0,0,0);
9366   for ( size_t i = 0; i < _simplices.size(); ++i )
9367     newPos += SMESH_TNodeXYZ( _simplices[i]._nPrev );
9368   newPos /= _simplices.size();
9369
9370   return newPos;
9371 }
9372
9373 //================================================================================
9374 /*!
9375  * \brief Computes a new node position using angular-based smoothing
9376  */
9377 //================================================================================
9378
9379 gp_XYZ _LayerEdge::smoothAngular()
9380 {
9381   vector< gp_Vec > edgeDir;  edgeDir. reserve( _simplices.size() + 1 );
9382   vector< double > edgeSize; edgeSize.reserve( _simplices.size()     );
9383   vector< gp_XYZ > points;   points.  reserve( _simplices.size() + 1 );
9384
9385   gp_XYZ pPrev = SMESH_TNodeXYZ( _simplices.back()._nPrev );
9386   gp_XYZ pN( 0,0,0 );
9387   for ( size_t i = 0; i < _simplices.size(); ++i )
9388   {
9389     gp_XYZ p = SMESH_TNodeXYZ( _simplices[i]._nPrev );
9390     edgeDir.push_back( p - pPrev );
9391     edgeSize.push_back( edgeDir.back().Magnitude() );
9392     if ( edgeSize.back() < numeric_limits<double>::min() )
9393     {
9394       edgeDir.pop_back();
9395       edgeSize.pop_back();
9396     }
9397     else
9398     {
9399       edgeDir.back() /= edgeSize.back();
9400       points.push_back( p );
9401       pN += p;
9402     }
9403     pPrev = p;
9404   }
9405   edgeDir.push_back ( edgeDir[0] );
9406   edgeSize.push_back( edgeSize[0] );
9407   pN /= points.size();
9408
9409   gp_XYZ newPos(0,0,0);
9410   double sumSize = 0;
9411   for ( size_t i = 0; i < points.size(); ++i )
9412   {
9413     gp_Vec toN    = pN - points[i];
9414     double toNLen = toN.Magnitude();
9415     if ( toNLen < numeric_limits<double>::min() )
9416     {
9417       newPos += pN;
9418       continue;
9419     }
9420     gp_Vec bisec    = edgeDir[i] + edgeDir[i+1];
9421     double bisecLen = bisec.SquareMagnitude();
9422     if ( bisecLen < numeric_limits<double>::min() )
9423     {
9424       gp_Vec norm = edgeDir[i] ^ toN;
9425       bisec = norm ^ edgeDir[i];
9426       bisecLen = bisec.SquareMagnitude();
9427     }
9428     bisecLen = Sqrt( bisecLen );
9429     bisec /= bisecLen;
9430
9431 #if 1
9432     gp_XYZ pNew = ( points[i] + bisec.XYZ() * toNLen ) * bisecLen;
9433     sumSize += bisecLen;
9434 #else
9435     gp_XYZ pNew = ( points[i] + bisec.XYZ() * toNLen ) * ( edgeSize[i] + edgeSize[i+1] );
9436     sumSize += ( edgeSize[i] + edgeSize[i+1] );
9437 #endif
9438     newPos += pNew;
9439   }
9440   newPos /= sumSize;
9441
9442   // project newPos to an average plane
9443
9444   gp_XYZ norm(0,0,0); // plane normal
9445   points.push_back( points[0] );
9446   for ( size_t i = 1; i < points.size(); ++i )
9447   {
9448     gp_XYZ vec1 = points[ i-1 ] - pN;
9449     gp_XYZ vec2 = points[ i   ] - pN;
9450     gp_XYZ cross = vec1 ^ vec2;
9451     try {
9452       cross.Normalize();
9453       if ( cross * norm < numeric_limits<double>::min() )
9454         norm += cross.Reversed();
9455       else
9456         norm += cross;
9457     }
9458     catch (Standard_Failure&) { // if |cross| == 0.
9459     }
9460   }
9461   gp_XYZ vec = newPos - pN;
9462   double r   = ( norm * vec ) / norm.SquareModulus();  // param [0,1] on norm
9463   newPos     = newPos - r * norm;
9464
9465   return newPos;
9466 }
9467
9468 //================================================================================
9469 /*!
9470  * \brief Computes a new node position using weighted node positions
9471  */
9472 //================================================================================
9473
9474 gp_XYZ _LayerEdge::smoothLengthWeighted()
9475 {
9476   vector< double > edgeSize; edgeSize.reserve( _simplices.size() + 1);
9477   vector< gp_XYZ > points;   points.  reserve( _simplices.size() );
9478
9479   gp_XYZ pPrev = SMESH_TNodeXYZ( _simplices.back()._nPrev );
9480   for ( size_t i = 0; i < _simplices.size(); ++i )
9481   {
9482     gp_XYZ p = SMESH_TNodeXYZ( _simplices[i]._nPrev );
9483     edgeSize.push_back( ( p - pPrev ).Modulus() );
9484     if ( edgeSize.back() < numeric_limits<double>::min() )
9485     {
9486       edgeSize.pop_back();
9487     }
9488     else
9489     {
9490       points.push_back( p );
9491     }
9492     pPrev = p;
9493   }
9494   edgeSize.push_back( edgeSize[0] );
9495
9496   gp_XYZ newPos(0,0,0);
9497   double sumSize = 0;
9498   for ( size_t i = 0; i < points.size(); ++i )
9499   {
9500     newPos += points[i] * ( edgeSize[i] + edgeSize[i+1] );
9501     sumSize += edgeSize[i] + edgeSize[i+1];
9502   }
9503   newPos /= sumSize;
9504   return newPos;
9505 }
9506
9507 //================================================================================
9508 /*!
9509  * \brief Computes a new node position using angular-based smoothing
9510  */
9511 //================================================================================
9512
9513 gp_XYZ _LayerEdge::smoothCentroidal()
9514 {
9515   gp_XYZ newPos(0,0,0);
9516   gp_XYZ pN = SMESH_TNodeXYZ( _nodes.back() );
9517   double sumSize = 0;
9518   for ( size_t i = 0; i < _simplices.size(); ++i )
9519   {
9520     gp_XYZ p1 = SMESH_TNodeXYZ( _simplices[i]._nPrev );
9521     gp_XYZ p2 = SMESH_TNodeXYZ( _simplices[i]._nNext );
9522     gp_XYZ gc = ( pN + p1 + p2 ) / 3.;
9523     double size = (( p1 - pN ) ^ ( p2 - pN )).Modulus();
9524
9525     sumSize += size;
9526     newPos += gc * size;
9527   }
9528   newPos /= sumSize;
9529
9530   return newPos;
9531 }
9532
9533 //================================================================================
9534 /*!
9535  * \brief Computes a new node position located inside a Nef polygon
9536  */
9537 //================================================================================
9538
9539 gp_XYZ _LayerEdge::smoothNefPolygon()
9540 #ifdef OLD_NEF_POLYGON
9541 {
9542   gp_XYZ newPos(0,0,0);
9543
9544   // get a plane to search a solution on
9545
9546   vector< gp_XYZ > vecs( _simplices.size() + 1 );
9547   size_t i;
9548   const double tol = numeric_limits<double>::min();
9549   gp_XYZ center(0,0,0);
9550   for ( i = 0; i < _simplices.size(); ++i )
9551   {
9552     vecs[i] = ( SMESH_TNodeXYZ( _simplices[i]._nNext ) -
9553                 SMESH_TNodeXYZ( _simplices[i]._nPrev ));
9554     center += SMESH_TNodeXYZ( _simplices[i]._nPrev );
9555   }
9556   vecs.back() = vecs[0];
9557   center /= _simplices.size();
9558
9559   gp_XYZ zAxis(0,0,0);
9560   for ( i = 0; i < _simplices.size(); ++i )
9561     zAxis += vecs[i] ^ vecs[i+1];
9562
9563   gp_XYZ yAxis;
9564   for ( i = 0; i < _simplices.size(); ++i )
9565   {
9566     yAxis = vecs[i];
9567     if ( yAxis.SquareModulus() > tol )
9568       break;
9569   }
9570   gp_XYZ xAxis = yAxis ^ zAxis;
9571   // SMESH_TNodeXYZ p0( _simplices[0]._nPrev );
9572   // const double tol = 1e-6 * ( p0.Distance( _simplices[1]._nPrev ) +
9573   //                             p0.Distance( _simplices[2]._nPrev ));
9574   // gp_XYZ center = smoothLaplacian();
9575   // gp_XYZ xAxis, yAxis, zAxis;
9576   // for ( i = 0; i < _simplices.size(); ++i )
9577   // {
9578   //   xAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9579   //   if ( xAxis.SquareModulus() > tol*tol )
9580   //     break;
9581   // }
9582   // for ( i = 1; i < _simplices.size(); ++i )
9583   // {
9584   //   yAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9585   //   zAxis = xAxis ^ yAxis;
9586   //   if ( zAxis.SquareModulus() > tol*tol )
9587   //     break;
9588   // }
9589   // if ( i == _simplices.size() ) return newPos;
9590
9591   yAxis = zAxis ^ xAxis;
9592   xAxis /= xAxis.Modulus();
9593   yAxis /= yAxis.Modulus();
9594
9595   // get half-planes of _simplices
9596
9597   vector< _halfPlane > halfPlns( _simplices.size() );
9598   int nbHP = 0;
9599   for ( size_t i = 0; i < _simplices.size(); ++i )
9600   {
9601     gp_XYZ OP1 = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9602     gp_XYZ OP2 = SMESH_TNodeXYZ( _simplices[i]._nNext ) - center;
9603     gp_XY  p1( OP1 * xAxis, OP1 * yAxis );
9604     gp_XY  p2( OP2 * xAxis, OP2 * yAxis );
9605     gp_XY  vec12 = p2 - p1;
9606     double dist12 = vec12.Modulus();
9607     if ( dist12 < tol )
9608       continue;
9609     vec12 /= dist12;
9610     halfPlns[ nbHP ]._pos = p1;
9611     halfPlns[ nbHP ]._dir = vec12;
9612     halfPlns[ nbHP ]._inNorm.SetCoord( -vec12.Y(), vec12.X() );
9613     ++nbHP;
9614   }
9615
9616   // intersect boundaries of half-planes, define state of intersection points
9617   // in relation to all half-planes and calculate internal point of a 2D polygon
9618
9619   double sumLen = 0;
9620   gp_XY newPos2D (0,0);
9621
9622   enum { UNDEF = -1, NOT_OUT, IS_OUT, NO_INT };
9623   typedef std::pair< gp_XY, int > TIntPntState; // coord and isOut state
9624   TIntPntState undefIPS( gp_XY(1e100,1e100), UNDEF );
9625
9626   vector< vector< TIntPntState > > allIntPnts( nbHP );
9627   for ( int iHP1 = 0; iHP1 < nbHP; ++iHP1 )
9628   {
9629     vector< TIntPntState > & intPnts1 = allIntPnts[ iHP1 ];
9630     if ( intPnts1.empty() ) intPnts1.resize( nbHP, undefIPS );
9631
9632     int iPrev = SMESH_MesherHelper::WrapIndex( iHP1 - 1, nbHP );
9633     int iNext = SMESH_MesherHelper::WrapIndex( iHP1 + 1, nbHP );
9634
9635     int nbNotOut = 0;
9636     const gp_XY* segEnds[2] = { 0, 0 }; // NOT_OUT points
9637
9638     for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9639     {
9640       if ( iHP1 == iHP2 ) continue;
9641
9642       TIntPntState & ips1 = intPnts1[ iHP2 ];
9643       if ( ips1.second == UNDEF )
9644       {
9645         // find an intersection point of boundaries of iHP1 and iHP2
9646
9647         if ( iHP2 == iPrev ) // intersection with neighbors is known
9648           ips1.first = halfPlns[ iHP1 ]._pos;
9649         else if ( iHP2 == iNext )
9650           ips1.first = halfPlns[ iHP2 ]._pos;
9651         else if ( !halfPlns[ iHP1 ].FindIntersection( halfPlns[ iHP2 ], ips1.first ))
9652           ips1.second = NO_INT;
9653
9654         // classify the found intersection point
9655         if ( ips1.second != NO_INT )
9656         {
9657           ips1.second = NOT_OUT;
9658           for ( int i = 0; i < nbHP && ips1.second == NOT_OUT; ++i )
9659             if ( i != iHP1 && i != iHP2 &&
9660                  halfPlns[ i ].IsOut( ips1.first, tol ))
9661               ips1.second = IS_OUT;
9662         }
9663         vector< TIntPntState > & intPnts2 = allIntPnts[ iHP2 ];
9664         if ( intPnts2.empty() ) intPnts2.resize( nbHP, undefIPS );
9665         TIntPntState & ips2 = intPnts2[ iHP1 ];
9666         ips2 = ips1;
9667       }
9668       if ( ips1.second == NOT_OUT )
9669       {
9670         ++nbNotOut;
9671         segEnds[ bool(segEnds[0]) ] = & ips1.first;
9672       }
9673     }
9674
9675     // find a NOT_OUT segment of boundary which is located between
9676     // two NOT_OUT int points
9677
9678     if ( nbNotOut < 2 )
9679       continue; // no such a segment
9680
9681     if ( nbNotOut > 2 )
9682     {
9683       // sort points along the boundary
9684       map< double, TIntPntState* > ipsByParam;
9685       for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9686       {
9687         TIntPntState & ips1 = intPnts1[ iHP2 ];
9688         if ( ips1.second != NO_INT )
9689         {
9690           gp_XY     op = ips1.first - halfPlns[ iHP1 ]._pos;
9691           double param = op * halfPlns[ iHP1 ]._dir;
9692           ipsByParam.insert( make_pair( param, & ips1 ));
9693         }
9694       }
9695       // look for two neighboring NOT_OUT points
9696       nbNotOut = 0;
9697       map< double, TIntPntState* >::iterator u2ips = ipsByParam.begin();
9698       for ( ; u2ips != ipsByParam.end(); ++u2ips )
9699       {
9700         TIntPntState & ips1 = *(u2ips->second);
9701         if ( ips1.second == NOT_OUT )
9702           segEnds[ bool( nbNotOut++ ) ] = & ips1.first;
9703         else if ( nbNotOut >= 2 )
9704           break;
9705         else
9706           nbNotOut = 0;
9707       }
9708     }
9709
9710     if ( nbNotOut >= 2 )
9711     {
9712       double len = ( *segEnds[0] - *segEnds[1] ).Modulus();
9713       sumLen += len;
9714
9715       newPos2D += 0.5 * len * ( *segEnds[0] + *segEnds[1] );
9716     }
9717   }
9718
9719   if ( sumLen > 0 )
9720   {
9721     newPos2D /= sumLen;
9722     newPos = center + xAxis * newPos2D.X() + yAxis * newPos2D.Y();
9723   }
9724   else
9725   {
9726     newPos = center;
9727   }
9728
9729   return newPos;
9730 }
9731 #else // OLD_NEF_POLYGON
9732 { ////////////////////////////////// NEW
9733   gp_XYZ newPos(0,0,0);
9734
9735   // get a plane to search a solution on
9736
9737   size_t i;
9738   gp_XYZ center(0,0,0);
9739   for ( i = 0; i < _simplices.size(); ++i )
9740     center += SMESH_TNodeXYZ( _simplices[i]._nPrev );
9741   center /= _simplices.size();
9742
9743   vector< gp_XYZ > vecs( _simplices.size() + 1 );
9744   for ( i = 0; i < _simplices.size(); ++i )
9745     vecs[i] = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9746   vecs.back() = vecs[0];
9747
9748   const double tol = numeric_limits<double>::min();
9749   gp_XYZ zAxis(0,0,0);
9750   for ( i = 0; i < _simplices.size(); ++i )
9751   {
9752     gp_XYZ cross = vecs[i] ^ vecs[i+1];
9753     try {
9754       cross.Normalize();
9755       if ( cross * zAxis < tol )
9756         zAxis += cross.Reversed();
9757       else
9758         zAxis += cross;
9759     }
9760     catch (Standard_Failure) { // if |cross| == 0.
9761     }
9762   }
9763
9764   gp_XYZ yAxis;
9765   for ( i = 0; i < _simplices.size(); ++i )
9766   {
9767     yAxis = vecs[i];
9768     if ( yAxis.SquareModulus() > tol )
9769       break;
9770   }
9771   gp_XYZ xAxis = yAxis ^ zAxis;
9772   // SMESH_TNodeXYZ p0( _simplices[0]._nPrev );
9773   // const double tol = 1e-6 * ( p0.Distance( _simplices[1]._nPrev ) +
9774   //                             p0.Distance( _simplices[2]._nPrev ));
9775   // gp_XYZ center = smoothLaplacian();
9776   // gp_XYZ xAxis, yAxis, zAxis;
9777   // for ( i = 0; i < _simplices.size(); ++i )
9778   // {
9779   //   xAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9780   //   if ( xAxis.SquareModulus() > tol*tol )
9781   //     break;
9782   // }
9783   // for ( i = 1; i < _simplices.size(); ++i )
9784   // {
9785   //   yAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9786   //   zAxis = xAxis ^ yAxis;
9787   //   if ( zAxis.SquareModulus() > tol*tol )
9788   //     break;
9789   // }
9790   // if ( i == _simplices.size() ) return newPos;
9791
9792   yAxis = zAxis ^ xAxis;
9793   xAxis /= xAxis.Modulus();
9794   yAxis /= yAxis.Modulus();
9795
9796   // get half-planes of _simplices
9797
9798   vector< _halfPlane > halfPlns( _simplices.size() );
9799   int nbHP = 0;
9800   for ( size_t i = 0; i < _simplices.size(); ++i )
9801   {
9802     const gp_XYZ& OP1 = vecs[ i   ];
9803     const gp_XYZ& OP2 = vecs[ i+1 ];
9804     gp_XY  p1( OP1 * xAxis, OP1 * yAxis );
9805     gp_XY  p2( OP2 * xAxis, OP2 * yAxis );
9806     gp_XY  vec12 = p2 - p1;
9807     double dist12 = vec12.Modulus();
9808     if ( dist12 < tol )
9809       continue;
9810     vec12 /= dist12;
9811     halfPlns[ nbHP ]._pos = p1;
9812     halfPlns[ nbHP ]._dir = vec12;
9813     halfPlns[ nbHP ]._inNorm.SetCoord( -vec12.Y(), vec12.X() );
9814     ++nbHP;
9815   }
9816
9817   // intersect boundaries of half-planes, define state of intersection points
9818   // in relation to all half-planes and calculate internal point of a 2D polygon
9819
9820   double sumLen = 0;
9821   gp_XY newPos2D (0,0);
9822
9823   enum { UNDEF = -1, NOT_OUT, IS_OUT, NO_INT };
9824   typedef std::pair< gp_XY, int > TIntPntState; // coord and isOut state
9825   TIntPntState undefIPS( gp_XY(1e100,1e100), UNDEF );
9826
9827   vector< vector< TIntPntState > > allIntPnts( nbHP );
9828   for ( int iHP1 = 0; iHP1 < nbHP; ++iHP1 )
9829   {
9830     vector< TIntPntState > & intPnts1 = allIntPnts[ iHP1 ];
9831     if ( intPnts1.empty() ) intPnts1.resize( nbHP, undefIPS );
9832
9833     int iPrev = SMESH_MesherHelper::WrapIndex( iHP1 - 1, nbHP );
9834     int iNext = SMESH_MesherHelper::WrapIndex( iHP1 + 1, nbHP );
9835
9836     int nbNotOut = 0;
9837     const gp_XY* segEnds[2] = { 0, 0 }; // NOT_OUT points
9838
9839     for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9840     {
9841       if ( iHP1 == iHP2 ) continue;
9842
9843       TIntPntState & ips1 = intPnts1[ iHP2 ];
9844       if ( ips1.second == UNDEF )
9845       {
9846         // find an intersection point of boundaries of iHP1 and iHP2
9847
9848         if ( iHP2 == iPrev ) // intersection with neighbors is known
9849           ips1.first = halfPlns[ iHP1 ]._pos;
9850         else if ( iHP2 == iNext )
9851           ips1.first = halfPlns[ iHP2 ]._pos;
9852         else if ( !halfPlns[ iHP1 ].FindIntersection( halfPlns[ iHP2 ], ips1.first ))
9853           ips1.second = NO_INT;
9854
9855         // classify the found intersection point
9856         if ( ips1.second != NO_INT )
9857         {
9858           ips1.second = NOT_OUT;
9859           for ( int i = 0; i < nbHP && ips1.second == NOT_OUT; ++i )
9860             if ( i != iHP1 && i != iHP2 &&
9861                  halfPlns[ i ].IsOut( ips1.first, tol ))
9862               ips1.second = IS_OUT;
9863         }
9864         vector< TIntPntState > & intPnts2 = allIntPnts[ iHP2 ];
9865         if ( intPnts2.empty() ) intPnts2.resize( nbHP, undefIPS );
9866         TIntPntState & ips2 = intPnts2[ iHP1 ];
9867         ips2 = ips1;
9868       }
9869       if ( ips1.second == NOT_OUT )
9870       {
9871         ++nbNotOut;
9872         segEnds[ bool(segEnds[0]) ] = & ips1.first;
9873       }
9874     }
9875
9876     // find a NOT_OUT segment of boundary which is located between
9877     // two NOT_OUT int points
9878
9879     if ( nbNotOut < 2 )
9880       continue; // no such a segment
9881
9882     if ( nbNotOut > 2 )
9883     {
9884       // sort points along the boundary
9885       map< double, TIntPntState* > ipsByParam;
9886       for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9887       {
9888         TIntPntState & ips1 = intPnts1[ iHP2 ];
9889         if ( ips1.second != NO_INT )
9890         {
9891           gp_XY     op = ips1.first - halfPlns[ iHP1 ]._pos;
9892           double param = op * halfPlns[ iHP1 ]._dir;
9893           ipsByParam.insert( make_pair( param, & ips1 ));
9894         }
9895       }
9896       // look for two neighboring NOT_OUT points
9897       nbNotOut = 0;
9898       map< double, TIntPntState* >::iterator u2ips = ipsByParam.begin();
9899       for ( ; u2ips != ipsByParam.end(); ++u2ips )
9900       {
9901         TIntPntState & ips1 = *(u2ips->second);
9902         if ( ips1.second == NOT_OUT )
9903           segEnds[ bool( nbNotOut++ ) ] = & ips1.first;
9904         else if ( nbNotOut >= 2 )
9905           break;
9906         else
9907           nbNotOut = 0;
9908       }
9909     }
9910
9911     if ( nbNotOut >= 2 )
9912     {
9913       double len = ( *segEnds[0] - *segEnds[1] ).Modulus();
9914       sumLen += len;
9915
9916       newPos2D += 0.5 * len * ( *segEnds[0] + *segEnds[1] );
9917     }
9918   }
9919
9920   if ( sumLen > 0 )
9921   {
9922     newPos2D /= sumLen;
9923     newPos = center + xAxis * newPos2D.X() + yAxis * newPos2D.Y();
9924   }
9925   else
9926   {
9927     newPos = center;
9928   }
9929
9930   return newPos;
9931 }
9932 #endif // OLD_NEF_POLYGON
9933
9934 //================================================================================
9935 /*!
9936  * \brief Add a new segment to _LayerEdge during inflation
9937  */
9938 //================================================================================
9939
9940 void _LayerEdge::SetNewLength( double len, _EdgesOnShape& eos, SMESH_MesherHelper& helper )
9941 {
9942   if ( Is( BLOCKED ))
9943     return;
9944
9945   if ( len > _maxLen )
9946   {
9947     len = _maxLen;
9948     Block( eos.GetData() );
9949   }
9950   const double lenDelta = len - _len;
9951   // if ( lenDelta < 0 )
9952   //   return;
9953   if ( lenDelta < len * 1e-3  )
9954   {
9955     Block( eos.GetData() );
9956     return;
9957   }
9958
9959   SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() );
9960   gp_XYZ oldXYZ = SMESH_TNodeXYZ( n );
9961   gp_XYZ newXYZ;
9962   if ( eos._hyp.IsOffsetMethod() )
9963   {
9964     newXYZ = oldXYZ;
9965     gp_Vec faceNorm;
9966     SMDS_ElemIteratorPtr faceIt = _nodes[0]->GetInverseElementIterator( SMDSAbs_Face );
9967     while ( faceIt->more() )
9968     {
9969       const SMDS_MeshElement* face = faceIt->next();
9970       if ( !eos.GetNormal( face, faceNorm ))
9971         continue;
9972
9973       // translate plane of a face
9974       gp_XYZ baryCenter = oldXYZ + faceNorm.XYZ() * lenDelta;
9975
9976       // find point of intersection of the face plane located at baryCenter
9977       // and _normal located at newXYZ
9978       double d   = -( faceNorm.XYZ() * baryCenter ); // d of plane equation ax+by+cz+d=0
9979       double dot =  ( faceNorm.XYZ() * _normal );
9980       if ( dot < std::numeric_limits<double>::min() )
9981         dot = lenDelta * 1e-3;
9982       double step = -( faceNorm.XYZ() * newXYZ + d ) / dot;
9983       newXYZ += step * _normal;
9984     }
9985     _lenFactor = _normal * ( newXYZ - oldXYZ ) / lenDelta; // _lenFactor is used in InvalidateStep()
9986   }
9987   else
9988   {
9989     newXYZ = oldXYZ + _normal * lenDelta * _lenFactor;
9990   }
9991
9992   n->setXYZ( newXYZ.X(), newXYZ.Y(), newXYZ.Z() );
9993   _pos.push_back( newXYZ );
9994
9995   if ( !eos._sWOL.IsNull() )
9996     if ( !UpdatePositionOnSWOL( n, 2*lenDelta, eos, helper ))
9997     {
9998       n->setXYZ( oldXYZ.X(), oldXYZ.Y(), oldXYZ.Z() );
9999       _pos.pop_back();
10000       Block( eos.GetData() );
10001       return;
10002     }
10003
10004   _len = len;
10005
10006   // notify _neibors
10007   if ( eos.ShapeType() != TopAbs_FACE )
10008   {
10009     for ( size_t i = 0; i < _neibors.size(); ++i )
10010       //if (  _len > _neibors[i]->GetSmooLen() )
10011       _neibors[i]->Set( MOVED );
10012
10013     Set( MOVED );
10014   }
10015   dumpMove( n ); //debug
10016 }
10017
10018
10019 //================================================================================
10020 /*!
10021  * \brief Update last position on SWOL by projecting node on SWOL
10022 */
10023 //================================================================================
10024
10025 bool _LayerEdge::UpdatePositionOnSWOL( SMDS_MeshNode*      n,
10026                                        double              tol,
10027                                        _EdgesOnShape&      eos,
10028                                        SMESH_MesherHelper& helper )
10029 {
10030   double distXYZ[4];
10031   bool uvOK = false;
10032   if ( eos.SWOLType() == TopAbs_EDGE )
10033   {
10034     double u = Precision::Infinite(); // to force projection w/o distance check
10035     uvOK = helper.CheckNodeU( TopoDS::Edge( eos._sWOL ), n, u, tol, /*force=*/true, distXYZ );
10036     _pos.back().SetCoord( u, 0, 0 );
10037     if ( _nodes.size() > 1 && uvOK )
10038     {
10039       SMDS_EdgePositionPtr pos = n->GetPosition();
10040       pos->SetUParameter( u );
10041     }
10042   }
10043   else //  TopAbs_FACE
10044   {
10045     gp_XY uv( Precision::Infinite(), 0 );
10046     uvOK = helper.CheckNodeUV( TopoDS::Face( eos._sWOL ), n, uv, tol, /*force=*/true, distXYZ );
10047     _pos.back().SetCoord( uv.X(), uv.Y(), 0 );
10048     if ( _nodes.size() > 1 && uvOK )
10049     {
10050       SMDS_FacePositionPtr pos = n->GetPosition();
10051       pos->SetUParameter( uv.X() );
10052       pos->SetVParameter( uv.Y() );
10053     }
10054   }
10055   if ( uvOK )
10056   {
10057     n->setXYZ( distXYZ[1], distXYZ[2], distXYZ[3]);
10058   }
10059   return uvOK;
10060 }
10061
10062 //================================================================================
10063 /*!
10064  * \brief Set BLOCKED flag and propagate limited _maxLen to _neibors
10065  */
10066 //================================================================================
10067
10068 void _LayerEdge::Block( _SolidData& data )
10069 {
10070   //if ( Is( BLOCKED )) return;
10071   Set( BLOCKED );
10072
10073   SMESH_Comment msg( "#BLOCK shape=");
10074   msg << data.GetShapeEdges( this )->_shapeID
10075       << ", nodes " << _nodes[0]->GetID() << ", " << _nodes.back()->GetID();
10076   dumpCmd( msg + " -- BEGIN");
10077
10078   SetMaxLen( _len );
10079   std::queue<_LayerEdge*> queue;
10080   queue.push( this );
10081
10082   gp_Pnt pSrc, pTgt, pSrcN, pTgtN;
10083   while ( !queue.empty() )
10084   {
10085     _LayerEdge* edge = queue.front(); queue.pop();
10086     pSrc = SMESH_TNodeXYZ( edge->_nodes[0] );
10087     pTgt = SMESH_TNodeXYZ( edge->_nodes.back() );
10088     for ( size_t iN = 0; iN < edge->_neibors.size(); ++iN )
10089     {
10090       _LayerEdge* neibor = edge->_neibors[iN];
10091       if ( neibor->_maxLen < edge->_maxLen * 1.01 )
10092         continue;
10093       pSrcN = SMESH_TNodeXYZ( neibor->_nodes[0] );
10094       pTgtN = SMESH_TNodeXYZ( neibor->_nodes.back() );
10095       double minDist = pSrc.SquareDistance( pSrcN );
10096       minDist   = Min( pTgt.SquareDistance( pTgtN ), minDist );
10097       minDist   = Min( pSrc.SquareDistance( pTgtN ), minDist );
10098       minDist   = Min( pTgt.SquareDistance( pSrcN ), minDist );
10099       double newMaxLen = edge->_maxLen + 0.5 * Sqrt( minDist );
10100       //if ( edge->_nodes[0]->getshapeId() == neibor->_nodes[0]->getshapeId() ) viscous_layers_00/A3
10101       {
10102         //newMaxLen *= edge->_lenFactor / neibor->_lenFactor;
10103         // newMaxLen *= Min( edge->_lenFactor / neibor->_lenFactor,
10104         //                   neibor->_lenFactor / edge->_lenFactor );
10105       }
10106       if ( neibor->_maxLen > newMaxLen )
10107       {
10108         neibor->SetMaxLen( newMaxLen );
10109         if ( neibor->_maxLen < neibor->_len )
10110         {
10111           _EdgesOnShape* eos = data.GetShapeEdges( neibor );
10112           int       lastStep = neibor->Is( BLOCKED ) ? 1 : 0;
10113           while ( neibor->_len > neibor->_maxLen &&
10114                   neibor->NbSteps() > lastStep )
10115             neibor->InvalidateStep( neibor->NbSteps(), *eos, /*restoreLength=*/true );
10116           neibor->SetNewLength( neibor->_maxLen, *eos, data.GetHelper() );
10117           //neibor->Block( data );
10118         }
10119         queue.push( neibor );
10120       }
10121     }
10122   }
10123   dumpCmd( msg + " -- END");
10124 }
10125
10126 //================================================================================
10127 /*!
10128  * \brief Remove last inflation step
10129  */
10130 //================================================================================
10131
10132 void _LayerEdge::InvalidateStep( size_t curStep, const _EdgesOnShape& eos, bool restoreLength )
10133 {
10134   if ( _pos.size() > curStep && _nodes.size() > 1 )
10135   {
10136     _pos.resize( curStep );
10137
10138     gp_Pnt      nXYZ = _pos.back();
10139     SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() );
10140     SMESH_TNodeXYZ curXYZ( n );
10141     if ( !eos._sWOL.IsNull() )
10142     {
10143       TopLoc_Location loc;
10144       if ( eos.SWOLType() == TopAbs_EDGE )
10145       {
10146         SMDS_EdgePositionPtr pos = n->GetPosition();
10147         pos->SetUParameter( nXYZ.X() );
10148         double f,l;
10149         Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( eos._sWOL ), loc, f,l);
10150         nXYZ = curve->Value( nXYZ.X() ).Transformed( loc );
10151       }
10152       else
10153       {
10154         SMDS_FacePositionPtr pos = n->GetPosition();
10155         pos->SetUParameter( nXYZ.X() );
10156         pos->SetVParameter( nXYZ.Y() );
10157         Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face(eos._sWOL), loc );
10158         nXYZ = surface->Value( nXYZ.X(), nXYZ.Y() ).Transformed( loc );
10159       }
10160     }
10161     n->setXYZ( nXYZ.X(), nXYZ.Y(), nXYZ.Z() );
10162     dumpMove( n );
10163
10164     if ( restoreLength )
10165     {
10166       if ( NbSteps() == 0 )
10167         _len = 0.;
10168       else if ( IsOnFace() && Is( MOVED ))
10169         _len = ( nXYZ.XYZ() - SMESH_NodeXYZ( _nodes[0] )) * _normal;
10170       else
10171         _len -= ( nXYZ.XYZ() - curXYZ ).Modulus() / _lenFactor;
10172     }
10173   }
10174   return;
10175 }
10176
10177 //================================================================================
10178 /*!
10179  * \brief Return index of a _pos distant from _normal
10180  */
10181 //================================================================================
10182
10183 int _LayerEdge::GetSmoothedPos( const double tol )
10184 {
10185   int iSmoothed = 0;
10186   for ( size_t i = 1; i < _pos.size() && !iSmoothed; ++i )
10187   {
10188     double normDist = ( _pos[i] - _pos[0] ).Crossed( _normal ).SquareModulus();
10189     if ( normDist > tol * tol )
10190       iSmoothed = i;
10191   }
10192   return iSmoothed;
10193 }
10194
10195 //================================================================================
10196 /*!
10197  * \brief Smooth a path formed by _pos of a _LayerEdge smoothed on FACE
10198  */
10199 //================================================================================
10200
10201 void _LayerEdge::SmoothPos( const vector< double >& segLen, const double tol )
10202 {
10203   if ( /*Is( NORMAL_UPDATED ) ||*/ _pos.size() <= 2 )
10204     return;
10205
10206   // find the 1st smoothed _pos
10207   int iSmoothed = GetSmoothedPos( tol );
10208   if ( !iSmoothed ) return;
10209
10210   gp_XYZ normal = _normal;
10211   if ( Is( NORMAL_UPDATED ))
10212   {
10213     double minDot = 1;
10214     for ( size_t i = 0; i < _neibors.size(); ++i )
10215     {
10216       if ( _neibors[i]->IsOnFace() )
10217       {
10218         double dot = _normal * _neibors[i]->_normal;
10219         if ( dot < minDot )
10220         {
10221           normal = _neibors[i]->_normal;
10222           minDot = dot;
10223         }
10224       }
10225     }
10226     if ( minDot == 1. )
10227       for ( size_t i = 1; i < _pos.size(); ++i )
10228       {
10229         normal = _pos[i] - _pos[0];
10230         double size = normal.Modulus();
10231         if ( size > RealSmall() )
10232         {
10233           normal /= size;
10234           break;
10235         }
10236       }
10237   }
10238   const double r = 0.2;
10239   for ( int iter = 0; iter < 50; ++iter )
10240   {
10241     double minDot = 1;
10242     for ( size_t i = Max( 1, iSmoothed-1-iter ); i < _pos.size()-1; ++i )
10243     {
10244       gp_XYZ midPos = 0.5 * ( _pos[i-1] + _pos[i+1] );
10245       gp_XYZ newPos = ( 1-r ) * midPos + r * _pos[i];
10246       _pos[i] = newPos;
10247       double midLen = 0.5 * ( segLen[i-1] + segLen[i+1] );
10248       double newLen = ( 1-r ) * midLen + r * segLen[i];
10249       const_cast< double& >( segLen[i] ) = newLen;
10250       // check angle between normal and (_pos[i+1], _pos[i] )
10251       gp_XYZ posDir = _pos[i+1] - _pos[i];
10252       double size   = posDir.SquareModulus();
10253       if ( size > RealSmall() )
10254         minDot = Min( minDot, ( normal * posDir ) * ( normal * posDir ) / size );
10255     }
10256     if ( minDot > 0.5 * 0.5 )
10257       break;
10258   }
10259   return;
10260 }
10261
10262 //================================================================================
10263 /*!
10264  * \brief Print flags
10265  */
10266 //================================================================================
10267
10268 std::string _LayerEdge::DumpFlags() const
10269 {
10270   SMESH_Comment dump;
10271   for ( int flag = 1; flag < 0x1000000; flag *= 2 )
10272     if ( _flags & flag )
10273     {
10274       EFlags f = (EFlags) flag;
10275       switch ( f ) {
10276       case TO_SMOOTH:       dump << "TO_SMOOTH";       break;
10277       case MOVED:           dump << "MOVED";           break;
10278       case SMOOTHED:        dump << "SMOOTHED";        break;
10279       case DIFFICULT:       dump << "DIFFICULT";       break;
10280       case ON_CONCAVE_FACE: dump << "ON_CONCAVE_FACE"; break;
10281       case BLOCKED:         dump << "BLOCKED";         break;
10282       case INTERSECTED:     dump << "INTERSECTED";     break;
10283       case NORMAL_UPDATED:  dump << "NORMAL_UPDATED";  break;
10284       case UPD_NORMAL_CONV: dump << "UPD_NORMAL_CONV"; break;
10285       case MARKED:          dump << "MARKED";          break;
10286       case MULTI_NORMAL:    dump << "MULTI_NORMAL";    break;
10287       case NEAR_BOUNDARY:   dump << "NEAR_BOUNDARY";   break;
10288       case SMOOTHED_C1:     dump << "SMOOTHED_C1";     break;
10289       case DISTORTED:       dump << "DISTORTED";       break;
10290       case RISKY_SWOL:      dump << "RISKY_SWOL";      break;
10291       case SHRUNK:          dump << "SHRUNK";          break;
10292       case UNUSED_FLAG:     dump << "UNUSED_FLAG";     break;
10293       }
10294       dump << " ";
10295     }
10296   cout << dump << endl;
10297   return dump;
10298 }
10299
10300
10301 //================================================================================
10302 /*!
10303  * \brief Create layers of prisms
10304  */
10305 //================================================================================
10306
10307 bool _ViscousBuilder::refine(_SolidData& data)
10308 {
10309   SMESH_MesherHelper& helper = data.GetHelper();
10310   helper.SetElementsOnShape(false);
10311
10312   Handle(Geom_Curve) curve;
10313   Handle(ShapeAnalysis_Surface) surface;
10314   TopoDS_Edge geomEdge;
10315   TopoDS_Face geomFace;
10316   TopLoc_Location loc;
10317   double f,l, u = 0;
10318   gp_XY uv;
10319   vector< gp_XYZ > pos3D;
10320   bool isOnEdge, isTooConvexFace = false;
10321   TGeomID prevBaseId = -1;
10322   TNode2Edge* n2eMap = 0;
10323   TNode2Edge::iterator n2e;
10324
10325   // Create intermediate nodes on each _LayerEdge
10326
10327   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
10328   {
10329     _EdgesOnShape& eos = data._edgesOnShape[iS];
10330     if ( eos._edges.empty() ) continue;
10331
10332     if ( eos._edges[0]->_nodes.size() < 2 )
10333       continue; // on _noShrinkShapes
10334
10335     // get data of a shrink shape
10336     isOnEdge = false;
10337     geomEdge.Nullify(); geomFace.Nullify();
10338     curve.Nullify(); surface.Nullify();
10339     if ( !eos._sWOL.IsNull() )
10340     {
10341       isOnEdge = ( eos.SWOLType() == TopAbs_EDGE );
10342       if ( isOnEdge )
10343       {
10344         geomEdge = TopoDS::Edge( eos._sWOL );
10345         curve    = BRep_Tool::Curve( geomEdge, loc, f,l);
10346       }
10347       else
10348       {
10349         geomFace = TopoDS::Face( eos._sWOL );
10350         surface  = helper.GetSurface( geomFace );
10351       }
10352     }
10353     else if ( eos.ShapeType() == TopAbs_FACE && eos._toSmooth )
10354     {
10355       geomFace = TopoDS::Face( eos._shape );
10356       surface  = helper.GetSurface( geomFace );
10357       // propagate _toSmooth back to _eosC1, which was unset in findShapesToSmooth()
10358       for ( size_t i = 0; i < eos._eosC1.size(); ++i )
10359         eos._eosC1[ i ]->_toSmooth = true;
10360
10361       isTooConvexFace = false;
10362       if ( _ConvexFace* cf = data.GetConvexFace( eos._shapeID ))
10363         isTooConvexFace = cf->_isTooCurved;
10364     }
10365
10366     vector< double > segLen;
10367     for ( size_t i = 0; i < eos._edges.size(); ++i )
10368     {
10369       _LayerEdge& edge = *eos._edges[i];
10370       if ( edge._pos.size() < 2 )
10371         continue;
10372
10373       // get accumulated length of segments
10374       segLen.resize( edge._pos.size() );
10375       segLen[0] = 0.0;
10376       if ( eos._sWOL.IsNull() )
10377       {
10378         bool useNormal = true;
10379         bool    usePos = false;
10380         bool  smoothed = false;
10381         double   preci = 0.1 * edge._len;
10382         if ( eos._toSmooth && edge._pos.size() > 2 )
10383         {
10384           smoothed = edge.GetSmoothedPos( preci );
10385         }
10386         if ( smoothed )
10387         {
10388           if ( !surface.IsNull() && !isTooConvexFace ) // edge smoothed on FACE
10389           {
10390             useNormal = usePos = false;
10391             gp_Pnt2d uv = helper.GetNodeUV( geomFace, edge._nodes[0] );
10392             for ( size_t j = 1; j < edge._pos.size() && !useNormal; ++j )
10393             {
10394               uv = surface->NextValueOfUV( uv, edge._pos[j], preci );
10395               if ( surface->Gap() < 2. * edge._len )
10396                 segLen[j] = surface->Gap();
10397               else
10398                 useNormal = true;
10399             }
10400           }
10401         }
10402         else if ( !edge.Is( _LayerEdge::NORMAL_UPDATED ))
10403         {
10404 #ifndef __NODES_AT_POS
10405           useNormal = usePos = false;
10406           edge._pos[1] = edge._pos.back();
10407           edge._pos.resize( 2 );
10408           segLen.resize( 2 );
10409           segLen[ 1 ] = edge._len;
10410 #endif
10411         }
10412         if ( useNormal && edge.Is( _LayerEdge::NORMAL_UPDATED ))
10413         {
10414           useNormal = usePos = false;
10415           _LayerEdge tmpEdge; // get original _normal
10416           tmpEdge._nodes.push_back( edge._nodes[0] );
10417           if ( !setEdgeData( tmpEdge, eos, helper, data ))
10418             usePos = true;
10419           else
10420             for ( size_t j = 1; j < edge._pos.size(); ++j )
10421               segLen[j] = ( edge._pos[j] - edge._pos[0] ) * tmpEdge._normal;
10422         }
10423         if ( useNormal )
10424         {
10425           for ( size_t j = 1; j < edge._pos.size(); ++j )
10426             segLen[j] = ( edge._pos[j] - edge._pos[0] ) * edge._normal;
10427         }
10428         if ( usePos )
10429         {
10430           for ( size_t j = 1; j < edge._pos.size(); ++j )
10431             segLen[j] = segLen[j-1] + ( edge._pos[j-1] - edge._pos[j] ).Modulus();
10432         }
10433         else
10434         {
10435           bool swapped = ( edge._pos.size() > 2 );
10436           while ( swapped )
10437           {
10438             swapped = false;
10439             for ( size_t j = 1; j < edge._pos.size()-1; ++j )
10440               if ( segLen[j] > segLen.back() )
10441               {
10442                 segLen.erase( segLen.begin() + j );
10443                 edge._pos.erase( edge._pos.begin() + j );
10444                 --j;
10445               }
10446               else if ( segLen[j] < segLen[j-1] )
10447               {
10448                 std::swap( segLen[j], segLen[j-1] );
10449                 std::swap( edge._pos[j], edge._pos[j-1] );
10450                 swapped = true;
10451               }
10452           }
10453         }
10454         // smooth a path formed by edge._pos
10455 #ifndef __NODES_AT_POS
10456         if (( smoothed ) /*&&
10457             ( eos.ShapeType() == TopAbs_FACE || edge.Is( _LayerEdge::SMOOTHED_C1 ))*/)
10458           edge.SmoothPos( segLen, preci );
10459 #endif
10460       }
10461       else if ( eos._isRegularSWOL ) // usual SWOL
10462       {
10463         if ( edge.Is( _LayerEdge::SMOOTHED ))
10464         {
10465           SMESH_NodeXYZ p0( edge._nodes[0] );
10466           for ( size_t j = 1; j < edge._pos.size(); ++j )
10467           {
10468             gp_XYZ pj = surface->Value( edge._pos[j].X(), edge._pos[j].Y() ).XYZ();
10469             segLen[j] = ( pj - p0 ) * edge._normal;
10470           }
10471         }
10472         else
10473         {
10474           for ( size_t j = 1; j < edge._pos.size(); ++j )
10475             segLen[j] = segLen[j-1] + (edge._pos[j-1] - edge._pos[j] ).Modulus();
10476         }
10477       }
10478       else // SWOL is surface with singularities or irregularly parametrized curve
10479       {
10480         pos3D.resize( edge._pos.size() );
10481
10482         if ( !surface.IsNull() )
10483           for ( size_t j = 0; j < edge._pos.size(); ++j )
10484             pos3D[j] = surface->Value( edge._pos[j].X(), edge._pos[j].Y() ).XYZ();
10485         else if ( !curve.IsNull() )
10486           for ( size_t j = 0; j < edge._pos.size(); ++j )
10487             pos3D[j] = curve->Value( edge._pos[j].X() ).XYZ();
10488
10489         for ( size_t j = 1; j < edge._pos.size(); ++j )
10490           segLen[j] = segLen[j-1] + ( pos3D[j-1] - pos3D[j] ).Modulus();
10491       }
10492
10493       // allocate memory for new nodes if it is not yet refined
10494       const SMDS_MeshNode* tgtNode = edge._nodes.back();
10495       if ( edge._nodes.size() == 2 )
10496       {
10497 #ifdef __NODES_AT_POS
10498         int nbNodes = edge._pos.size();
10499 #else
10500         int nbNodes = eos._hyp.GetNumberLayers() + 1;
10501 #endif
10502         edge._nodes.resize( nbNodes, 0 );
10503         edge._nodes[1] = 0;
10504         edge._nodes.back() = tgtNode;
10505       }
10506       // restore shapePos of the last node by already treated _LayerEdge of another _SolidData
10507       const TGeomID baseShapeId = edge._nodes[0]->getshapeId();
10508       if ( baseShapeId != prevBaseId )
10509       {
10510         map< TGeomID, TNode2Edge* >::iterator s2ne = data._s2neMap.find( baseShapeId );
10511         n2eMap = ( s2ne == data._s2neMap.end() ) ? 0 : s2ne->second;
10512         prevBaseId = baseShapeId;
10513       }
10514       _LayerEdge* edgeOnSameNode = 0;
10515       bool        useExistingPos = false;
10516       if ( n2eMap && (( n2e = n2eMap->find( edge._nodes[0] )) != n2eMap->end() ))
10517       {
10518         edgeOnSameNode = n2e->second;
10519         useExistingPos = ( edgeOnSameNode->_len < edge._len ||
10520                            segLen[0] == segLen.back() ); // too short inflation step (bos #20643)
10521         const gp_XYZ& otherTgtPos = edgeOnSameNode->_pos.back();
10522         SMDS_PositionPtr  lastPos = tgtNode->GetPosition();
10523         if ( isOnEdge )
10524         {
10525           SMDS_EdgePositionPtr epos = lastPos;
10526           epos->SetUParameter( otherTgtPos.X() );
10527         }
10528         else
10529         {
10530           SMDS_FacePositionPtr fpos = lastPos;
10531           fpos->SetUParameter( otherTgtPos.X() );
10532           fpos->SetVParameter( otherTgtPos.Y() );
10533         }
10534       }
10535
10536       // create intermediate nodes
10537       const double      h0 = eos._hyp.Get1stLayerThickness( segLen.back() );
10538       const double zeroLen = std::numeric_limits<double>::min();
10539       double hSum = 0, hi = h0/eos._hyp.GetStretchFactor();
10540       size_t iSeg = 1;
10541       for ( size_t iStep = 1; iStep < edge._nodes.size(); ++iStep )
10542       {
10543         // compute an intermediate position
10544         hi *= eos._hyp.GetStretchFactor();
10545         hSum += hi;
10546         while ( hSum > segLen[iSeg] && iSeg < segLen.size()-1 )
10547           ++iSeg;
10548         int iPrevSeg = iSeg-1;
10549         while ( fabs( segLen[iPrevSeg] - segLen[iSeg]) <= zeroLen && iPrevSeg > 0 )
10550           --iPrevSeg;
10551         double   r = ( segLen[iSeg] - hSum ) / ( segLen[iSeg] - segLen[iPrevSeg] );
10552         gp_Pnt pos = r * edge._pos[iPrevSeg] + (1-r) * edge._pos[iSeg];
10553 #ifdef __NODES_AT_POS
10554         pos = edge._pos[ iStep ];
10555 #endif
10556         SMDS_MeshNode*& node = const_cast< SMDS_MeshNode*& >( edge._nodes[ iStep ]);
10557         if ( !eos._sWOL.IsNull() )
10558         {
10559           // compute XYZ by parameters <pos>
10560           if ( isOnEdge )
10561           {
10562             u = pos.X();
10563             if ( !node )
10564               pos = curve->Value( u ).Transformed(loc);
10565           }
10566           else if ( eos._isRegularSWOL )
10567           {
10568             uv.SetCoord( pos.X(), pos.Y() );
10569             if ( !node )
10570               pos = surface->Value( pos.X(), pos.Y() );
10571           }
10572           else
10573           {
10574             uv.SetCoord( pos.X(), pos.Y() );
10575             gp_Pnt p = r * pos3D[ iPrevSeg ] + (1-r) * pos3D[ iSeg ];
10576             uv = surface->NextValueOfUV( uv, p, BRep_Tool::Tolerance( geomFace )).XY();
10577             if ( !node )
10578               pos = surface->Value( uv );
10579           }
10580         }
10581         // create or update the node
10582         if ( !node )
10583         {
10584           node = helper.AddNode( pos.X(), pos.Y(), pos.Z());
10585           if ( !eos._sWOL.IsNull() )
10586           {
10587             if ( isOnEdge )
10588               getMeshDS()->SetNodeOnEdge( node, geomEdge, u );
10589             else
10590               getMeshDS()->SetNodeOnFace( node, geomFace, uv.X(), uv.Y() );
10591           }
10592           else
10593           {
10594             getMeshDS()->SetNodeInVolume( node, helper.GetSubShapeID() );
10595           }
10596         }
10597         else
10598         {
10599           if ( !eos._sWOL.IsNull() )
10600           {
10601             // make average pos from new and current parameters
10602             if ( isOnEdge )
10603             {
10604               //u = 0.5 * ( u + helper.GetNodeU( geomEdge, node ));
10605               if ( useExistingPos )
10606                 u = helper.GetNodeU( geomEdge, node );
10607               pos = curve->Value( u ).Transformed(loc);
10608
10609               SMDS_EdgePositionPtr epos = node->GetPosition();
10610               epos->SetUParameter( u );
10611             }
10612             else
10613             {
10614               //uv = 0.5 * ( uv + helper.GetNodeUV( geomFace, node ));
10615               if ( useExistingPos )
10616                 uv = helper.GetNodeUV( geomFace, node );
10617               pos = surface->Value( uv );
10618
10619               SMDS_FacePositionPtr fpos = node->GetPosition();
10620               fpos->SetUParameter( uv.X() );
10621               fpos->SetVParameter( uv.Y() );
10622             }
10623           }
10624           node->setXYZ( pos.X(), pos.Y(), pos.Z() );
10625         }
10626       } // loop on edge._nodes
10627
10628       if ( !eos._sWOL.IsNull() ) // prepare for shrink()
10629       {
10630         if ( isOnEdge )
10631           edge._pos.back().SetCoord( u, 0,0);
10632         else
10633           edge._pos.back().SetCoord( uv.X(), uv.Y() ,0);
10634
10635         if ( edgeOnSameNode )
10636           edgeOnSameNode->_pos.back() = edge._pos.back();
10637       }
10638
10639     } // loop on eos._edges to create nodes
10640
10641
10642     if ( !getMeshDS()->IsEmbeddedMode() )
10643       // Log node movement
10644       for ( size_t i = 0; i < eos._edges.size(); ++i )
10645       {
10646         SMESH_TNodeXYZ p ( eos._edges[i]->_nodes.back() );
10647         getMeshDS()->MoveNode( p._node, p.X(), p.Y(), p.Z() );
10648       }
10649   }
10650
10651
10652   // Create volumes
10653
10654   helper.SetElementsOnShape(true);
10655
10656   vector< vector<const SMDS_MeshNode*>* > nnVec;
10657   set< vector<const SMDS_MeshNode*>* >    nnSet;
10658   set< int >                       degenEdgeInd;
10659   vector<const SMDS_MeshElement*>     degenVols;
10660
10661   TopExp_Explorer exp( data._solid, TopAbs_FACE );
10662   for ( ; exp.More(); exp.Next() )
10663   {
10664     const TGeomID faceID = getMeshDS()->ShapeToIndex( exp.Current() );
10665     if ( data._ignoreFaceIds.count( faceID ))
10666       continue;
10667     _EdgesOnShape*    eos = data.GetShapeEdges( faceID );
10668     SMDS_MeshGroup* group = StdMeshers_ViscousLayers::CreateGroup( eos->_hyp.GetGroupName(),
10669                                                                    *helper.GetMesh(),
10670                                                                    SMDSAbs_Volume );
10671     std::vector< const SMDS_MeshElement* > vols;
10672     const bool isReversedFace = data._reversedFaceIds.count( faceID );
10673     SMESHDS_SubMesh*    fSubM = getMeshDS()->MeshElements( exp.Current() );
10674     SMDS_ElemIteratorPtr  fIt = fSubM->GetElements();
10675     while ( fIt->more() )
10676     {
10677       const SMDS_MeshElement* face = fIt->next();
10678       const int            nbNodes = face->NbCornerNodes();
10679       nnVec.resize( nbNodes );
10680       nnSet.clear();
10681       degenEdgeInd.clear();
10682       size_t maxZ = 0, minZ = std::numeric_limits<size_t>::max();
10683       SMDS_NodeIteratorPtr nIt = face->nodeIterator();
10684       for ( int iN = 0; iN < nbNodes; ++iN )
10685       {
10686         const SMDS_MeshNode* n = nIt->next();
10687         _LayerEdge*       edge = data._n2eMap[ n ];
10688         const int i = isReversedFace ? nbNodes-1-iN : iN;
10689         nnVec[ i ] = & edge->_nodes;
10690         maxZ = std::max( maxZ, nnVec[ i ]->size() );
10691         minZ = std::min( minZ, nnVec[ i ]->size() );
10692
10693         if ( helper.HasDegeneratedEdges() )
10694           nnSet.insert( nnVec[ i ]);
10695       }
10696
10697       if ( maxZ == 0 )
10698         continue;
10699       if ( 0 < nnSet.size() && nnSet.size() < 3 )
10700         continue;
10701
10702       vols.clear();
10703       const SMDS_MeshElement* vol;
10704
10705       switch ( nbNodes )
10706       {
10707       case 3: // TRIA
10708       {
10709         // PENTA
10710         for ( size_t iZ = 1; iZ < minZ; ++iZ )
10711         {
10712           vol = helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1], (*nnVec[2])[iZ-1],
10713                                   (*nnVec[0])[iZ],   (*nnVec[1])[iZ],   (*nnVec[2])[iZ]);
10714           vols.push_back( vol );
10715         }
10716
10717         for ( size_t iZ = minZ; iZ < maxZ; ++iZ )
10718         {
10719           for ( int iN = 0; iN < nbNodes; ++iN )
10720             if ( nnVec[ iN ]->size() < iZ+1 )
10721               degenEdgeInd.insert( iN );
10722
10723           if ( degenEdgeInd.size() == 1 )  // PYRAM
10724           {
10725             int i2 = *degenEdgeInd.begin();
10726             int i0 = helper.WrapIndex( i2 - 1, nbNodes );
10727             int i1 = helper.WrapIndex( i2 + 1, nbNodes );
10728             vol = helper.AddVolume( (*nnVec[i0])[iZ-1], (*nnVec[i1])[iZ-1],
10729                                     (*nnVec[i1])[iZ  ], (*nnVec[i0])[iZ  ], (*nnVec[i2]).back());
10730             vols.push_back( vol );
10731           }
10732           else  // TETRA
10733           {
10734             int i3 = !degenEdgeInd.count(0) ? 0 : !degenEdgeInd.count(1) ? 1 : 2;
10735             vol = helper.AddVolume( (*nnVec[  0 ])[ i3 == 0 ? iZ-1 : nnVec[0]->size()-1 ],
10736                                     (*nnVec[  1 ])[ i3 == 1 ? iZ-1 : nnVec[1]->size()-1 ],
10737                                     (*nnVec[  2 ])[ i3 == 2 ? iZ-1 : nnVec[2]->size()-1 ],
10738                                     (*nnVec[ i3 ])[ iZ ]);
10739             vols.push_back( vol );
10740           }
10741         }
10742         break; // TRIA
10743       }
10744       case 4: // QUAD
10745       {
10746         // HEX
10747         for ( size_t iZ = 1; iZ < minZ; ++iZ )
10748         {
10749           vol = helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1],
10750                                   (*nnVec[2])[iZ-1], (*nnVec[3])[iZ-1],
10751                                   (*nnVec[0])[iZ],   (*nnVec[1])[iZ],
10752                                   (*nnVec[2])[iZ],   (*nnVec[3])[iZ]);
10753           vols.push_back( vol );
10754         }
10755
10756         for ( size_t iZ = minZ; iZ < maxZ; ++iZ )
10757         {
10758           for ( int iN = 0; iN < nbNodes; ++iN )
10759             if ( nnVec[ iN ]->size() < iZ+1 )
10760               degenEdgeInd.insert( iN );
10761
10762           switch ( degenEdgeInd.size() )
10763           {
10764           case 2: // PENTA
10765           {
10766             int i2 = *degenEdgeInd.begin();
10767             int i3 = *degenEdgeInd.rbegin();
10768             bool ok = ( i3 - i2 == 1 );
10769             if ( i2 == 0 && i3 == 3 ) { i2 = 3; i3 = 0; ok = true; }
10770             int i0 = helper.WrapIndex( i3 + 1, nbNodes );
10771             int i1 = helper.WrapIndex( i0 + 1, nbNodes );
10772
10773             vol = helper.AddVolume( nnVec[i3]->back(), (*nnVec[i0])[iZ], (*nnVec[i0])[iZ-1],
10774                                     nnVec[i2]->back(), (*nnVec[i1])[iZ], (*nnVec[i1])[iZ-1]);
10775             vols.push_back( vol );
10776             if ( !ok && vol )
10777               degenVols.push_back( vol );
10778           }
10779           break;
10780
10781           default: // degen HEX
10782           {
10783             vol = helper.AddVolume( nnVec[0]->size() > iZ-1 ? (*nnVec[0])[iZ-1] : nnVec[0]->back(),
10784                                     nnVec[1]->size() > iZ-1 ? (*nnVec[1])[iZ-1] : nnVec[1]->back(),
10785                                     nnVec[2]->size() > iZ-1 ? (*nnVec[2])[iZ-1] : nnVec[2]->back(),
10786                                     nnVec[3]->size() > iZ-1 ? (*nnVec[3])[iZ-1] : nnVec[3]->back(),
10787                                     nnVec[0]->size() > iZ   ? (*nnVec[0])[iZ]   : nnVec[0]->back(),
10788                                     nnVec[1]->size() > iZ   ? (*nnVec[1])[iZ]   : nnVec[1]->back(),
10789                                     nnVec[2]->size() > iZ   ? (*nnVec[2])[iZ]   : nnVec[2]->back(),
10790                                     nnVec[3]->size() > iZ   ? (*nnVec[3])[iZ]   : nnVec[3]->back());
10791             vols.push_back( vol );
10792             degenVols.push_back( vol );
10793           }
10794           }
10795         }
10796         break; // HEX
10797       }
10798       default:
10799         return error("Not supported type of element", data._index);
10800
10801       } // switch ( nbNodes )
10802
10803       if ( group )
10804         for ( size_t i = 0; i < vols.size(); ++i )
10805           group->Add( vols[ i ]);
10806
10807     } // while ( fIt->more() )
10808   } // loop on FACEs
10809
10810   if ( !degenVols.empty() )
10811   {
10812     SMESH_ComputeErrorPtr& err = _mesh->GetSubMesh( data._solid )->GetComputeError();
10813     if ( !err || err->IsOK() )
10814     {
10815       SMESH_BadInputElements* badElems =
10816         new SMESH_BadInputElements( getMeshDS(), COMPERR_WARNING, "Bad quality volumes created" );
10817       badElems->myBadElements.insert( badElems->myBadElements.end(),
10818                                       degenVols.begin(),degenVols.end() );
10819       err.reset( badElems );
10820     }
10821   }
10822
10823   return true;
10824 }
10825
10826 namespace VISCOUS_3D
10827 {
10828   struct ShrinkFace;
10829   //--------------------------------------------------------------------------------
10830   /*!
10831    * \brief Pair of periodic FACEs
10832    */
10833   struct PeriodicFaces
10834   {
10835     typedef StdMeshers_ProjectionUtils::TrsfFinder3D Trsf;
10836
10837     ShrinkFace*  _shriFace[2];
10838     TNodeNodeMap _nnMap;
10839     Trsf         _trsf;
10840
10841     PeriodicFaces( ShrinkFace* sf1, ShrinkFace* sf2 ): _shriFace{ sf1, sf2 } {}
10842     bool IncludeShrunk( const TopoDS_Face& face, const TopTools_MapOfShape& shrunkFaces ) const;
10843     bool MoveNodes( const TopoDS_Face& tgtFace );
10844     void Clear() { _nnMap.clear(); }
10845     bool IsEmpty() const { return _nnMap.empty(); }
10846   };
10847
10848   //--------------------------------------------------------------------------------
10849   /*!
10850    * \brief Shrink FACE data used to find periodic FACEs
10851    */
10852   struct ShrinkFace
10853   {
10854     // ................................................................................
10855     struct BndPart //!< part of FACE boundary, either shrink or no-shrink
10856     {
10857       bool                         _isShrink, _isReverse;
10858       int                          _nbSegments;
10859       AverageHyp*                  _hyp;
10860       std::vector< SMESH_NodeXYZ > _nodes;
10861       TopAbs_ShapeEnum             _vertSWOLType[2]; // shrink part includes VERTEXes
10862       AverageHyp*                  _vertHyp[2];
10863       double                       _edgeWOLLen[2]; // length of wol EDGE
10864       double                       _tol; // to compare _edgeWOLLen's
10865
10866       BndPart():
10867         _isShrink(0), _isReverse(0), _nbSegments(0), _hyp(0),
10868         _vertSWOLType{ TopAbs_WIRE, TopAbs_WIRE }, _vertHyp{ 0, 0 }, _edgeWOLLen{ 0., 0.}
10869       {}
10870
10871       bool IsEqualLengthEWOL( const BndPart& other ) const
10872       {
10873         return ( std::abs( _edgeWOLLen[0] - other._edgeWOLLen[0] ) < _tol &&
10874                  std::abs( _edgeWOLLen[1] - other._edgeWOLLen[1] ) < _tol );
10875       }
10876
10877       bool operator==( const BndPart& other ) const
10878       {
10879         return ( _isShrink       == other._isShrink &&
10880                  _nbSegments     == other._nbSegments &&
10881                  _nodes.size()   == other._nodes.size() &&
10882                  vertSWOLType1() == other.vertSWOLType1() &&
10883                  vertSWOLType2() == other.vertSWOLType2() &&
10884                  (( !_isShrink ) ||
10885                   ( *_hyp        == *other._hyp &&
10886                     vertHyp1()   == other.vertHyp1() &&
10887                     vertHyp2()   == other.vertHyp2() &&
10888                     IsEqualLengthEWOL( other )))
10889                  );
10890       }
10891       bool CanAppend( const BndPart& other )
10892       {
10893         return ( _isShrink  == other._isShrink  &&
10894                  (( !_isShrink ) ||
10895                   ( *_hyp        == *other._hyp &&
10896                     *_hyp        == vertHyp2()  &&
10897                     vertHyp2()   == other.vertHyp1() ))
10898                  );
10899       }
10900       void Append( const BndPart& other )
10901       {
10902         _nbSegments += other._nbSegments;
10903         bool hasCommonNode = ( _nodes.back()->GetID() == other._nodes.front()->GetID() );
10904         _nodes.insert( _nodes.end(), other._nodes.begin() + hasCommonNode, other._nodes.end() );
10905         _vertSWOLType[1] = other._vertSWOLType[1];
10906         if ( _isShrink ) {
10907           _vertHyp[1]    = other._vertHyp[1];
10908           _edgeWOLLen[1] = other._edgeWOLLen[1];
10909         }
10910       }
10911       const SMDS_MeshNode* Node(size_t i)  const
10912       {
10913         return _nodes[ _isReverse ? ( _nodes.size() - 1 - i ) : i ]._node;
10914       }
10915       void Reverse() { _isReverse = !_isReverse; }
10916       const TopAbs_ShapeEnum& vertSWOLType1() const { return _vertSWOLType[ _isReverse  ]; }
10917       const TopAbs_ShapeEnum& vertSWOLType2() const { return _vertSWOLType[ !_isReverse ]; }
10918       const AverageHyp&       vertHyp1()      const { return *(_vertHyp[ _isReverse  ]); }
10919       const AverageHyp&       vertHyp2()      const { return *(_vertHyp[ !_isReverse ]); }
10920     };
10921     // ................................................................................
10922
10923     SMESH_subMesh*       _subMesh;
10924     _SolidData*          _data1;
10925     _SolidData*          _data2;
10926
10927     std::list< BndPart > _boundary;
10928     int                  _boundarySize, _nbBoundaryParts;
10929
10930     void Init( SMESH_subMesh* sm, _SolidData* sd1, _SolidData* sd2 )
10931     {
10932       _subMesh = sm; _data1 = sd1; _data2 = sd2;
10933     }
10934     bool IsSame( const TopoDS_Face& face ) const
10935     {
10936       return _subMesh->GetSubShape().IsSame( face );
10937     }
10938     bool IsShrunk( const TopTools_MapOfShape& shrunkFaces ) const
10939     {
10940       return shrunkFaces.Contains( _subMesh->GetSubShape() );
10941     }
10942
10943     //================================================================================
10944     /*!
10945      * Check if meshes on two FACEs are equal
10946      */
10947     bool IsPeriodic( ShrinkFace& other, PeriodicFaces& periodic )
10948     {
10949       if ( !IsSameNbElements( other ))
10950         return false;
10951
10952       this->SetBoundary();
10953       other.SetBoundary();
10954       if ( this->_boundarySize    != other._boundarySize ||
10955            this->_nbBoundaryParts != other._nbBoundaryParts )
10956         return false;
10957
10958       for ( int isReverse = 0; isReverse < 2; ++isReverse )
10959       {
10960         if ( isReverse )
10961           Reverse( _boundary );
10962
10963         // check boundaries
10964         bool equalBoundary = false;
10965         for ( int iP = 0; iP < _nbBoundaryParts &&  !equalBoundary; ++iP )
10966         {
10967           if ( ! ( equalBoundary = ( this->_boundary == other._boundary )))
10968             // set first part at end
10969             _boundary.splice( _boundary.end(), _boundary, _boundary.begin() );
10970         }
10971         if ( !equalBoundary )
10972           continue;
10973
10974         // check connectivity
10975         std::set<const SMDS_MeshElement*> elemsThis, elemsOther;
10976         this->GetElements( elemsThis  );
10977         other.GetElements( elemsOther );
10978         SMESH_MeshEditor::Sew_Error err =
10979           SMESH_MeshEditor::FindMatchingNodes( elemsThis, elemsOther,
10980                                                this->_boundary.front().Node(0),
10981                                                other._boundary.front().Node(0),
10982                                                this->_boundary.front().Node(1),
10983                                                other._boundary.front().Node(1),
10984                                                periodic._nnMap );
10985         if ( err != SMESH_MeshEditor::SEW_OK )
10986           continue;
10987
10988         // check node positions
10989         std::vector< gp_XYZ > srcPnts, tgtPnts;
10990         this->GetBoundaryPoints( srcPnts );
10991         other.GetBoundaryPoints( tgtPnts );
10992         if ( !periodic._trsf.Solve( srcPnts, tgtPnts )) {
10993           continue;
10994         }
10995         double tol = std::numeric_limits<double>::max(); // tolerance by segment size
10996         for ( size_t i = 1; i < srcPnts.size(); ++i ) {
10997           tol = Min( tol, ( srcPnts[i-1] - srcPnts[i] ).SquareModulus() );
10998         }
10999         tol = 0.01 * Sqrt( tol );
11000         for ( BndPart& boundary : _boundary ) { // tolerance by VL thickness
11001           if ( boundary._isShrink )
11002             tol = Min( tol, boundary._hyp->Get1stLayerThickness() / 50. );
11003         }
11004         bool nodeCoincide = true;
11005         TNodeNodeMap::iterator n2n = periodic._nnMap.begin();
11006         for ( ; n2n != periodic._nnMap.end() &&  nodeCoincide; ++n2n )
11007         {
11008           SMESH_NodeXYZ nSrc = n2n->first;
11009           SMESH_NodeXYZ nTgt = n2n->second;
11010           gp_XYZ pTgt = periodic._trsf.Transform( nSrc );
11011           nodeCoincide = (( pTgt - nTgt ).SquareModulus() < tol * tol );
11012         }
11013         if ( nodeCoincide )
11014           return true;
11015       }
11016       return false;
11017     }
11018
11019     bool IsSameNbElements( ShrinkFace& other ) // check number of mesh faces
11020     {
11021       SMESHDS_SubMesh* sm1 = this->_subMesh->GetSubMeshDS();
11022       SMESHDS_SubMesh* sm2 = other._subMesh->GetSubMeshDS();
11023       return ( sm1->NbElements() == sm2->NbElements() &&
11024                sm1->NbNodes()    == sm2->NbNodes() );
11025     }
11026
11027     void Reverse( std::list< BndPart >& boundary )
11028     {
11029       boundary.reverse();
11030       for ( std::list< BndPart >::iterator part = boundary.begin(); part != boundary.end(); ++part )
11031         part->Reverse();
11032     }
11033
11034     void SetBoundary()
11035     {
11036       if ( !_boundary.empty() )
11037         return;
11038
11039       TopoDS_Face F = TopoDS::Face( _subMesh->GetSubShape() );
11040       if ( F.Orientation() >= TopAbs_INTERNAL ) F.Orientation( TopAbs_FORWARD );
11041       std::list< TopoDS_Edge > edges;
11042       std::list< int > nbEdgesInWire;
11043       /*int nbWires =*/ SMESH_Block::GetOrderedEdges (F, edges, nbEdgesInWire);
11044
11045       // std::list< TopoDS_Edge >::iterator edgesEnd = edges.end();
11046       // if ( nbWires > 1 ) {
11047       //   edgesEnd = edges.begin();
11048       //   std::advance( edgesEnd, nbEdgesInWire.front() );
11049       // }
11050       StdMeshers_FaceSide fSide( F, edges, _subMesh->GetFather(),
11051                                  /*fwd=*/true, /*skipMedium=*/true );
11052       _boundarySize = fSide.NbSegments();
11053
11054       //TopoDS_Vertex vv[2];
11055       //std::list< TopoDS_Edge >::iterator edgeIt = edges.begin();
11056       for ( int iE = 0; iE < nbEdgesInWire.front(); ++iE )
11057       {
11058         BndPart bndPart;
11059
11060         std::vector<const SMDS_MeshNode*> nodes = fSide.GetOrderedNodes( iE );
11061         bndPart._nodes.assign( nodes.begin(), nodes.end() );
11062         bndPart._nbSegments = bndPart._nodes.size() - 1;
11063
11064         _EdgesOnShape*  eos = _data1->GetShapeEdges( fSide.EdgeID( iE ));
11065
11066         bndPart._isShrink = ( eos->SWOLType() == TopAbs_FACE );
11067         if ( bndPart._isShrink )
11068           if ((           _data1->_noShrinkShapes.count( eos->_shapeID )) ||
11069               ( _data2 && _data2->_noShrinkShapes.count( eos->_shapeID )))
11070             bndPart._isShrink = false;
11071
11072         if ( bndPart._isShrink )
11073         {
11074           bndPart._hyp = & eos->_hyp;
11075           _EdgesOnShape* eov[2] = { _data1->GetShapeEdges( fSide.FirstVertex( iE )),
11076                                     _data1->GetShapeEdges( fSide.LastVertex ( iE )) };
11077           for ( int iV = 0; iV < 2; ++iV )
11078           {
11079             bndPart._vertHyp     [iV] = & eov[iV]->_hyp;
11080             bndPart._vertSWOLType[iV] = eov[iV]->SWOLType();
11081             if ( _data1->_noShrinkShapes.count( eov[iV]->_shapeID ))
11082               bndPart._vertSWOLType[iV] = TopAbs_SHAPE;
11083             if ( _data2 && bndPart._vertSWOLType[iV] != TopAbs_SHAPE )
11084             {
11085               eov[iV] = _data2->GetShapeEdges( iV ? fSide.LastVertex(iE) : fSide.FirstVertex(iE ));
11086               if ( _data2->_noShrinkShapes.count( eov[iV]->_shapeID ))
11087                 bndPart._vertSWOLType[iV] = TopAbs_SHAPE;
11088               else if ( eov[iV]->SWOLType() > bndPart._vertSWOLType[iV] )
11089                 bndPart._vertSWOLType[iV] = eov[iV]->SWOLType();
11090             }
11091           }
11092           bndPart._edgeWOLLen[0] = fSide.EdgeLength( iE - 1 );
11093           bndPart._edgeWOLLen[1] = fSide.EdgeLength( iE + 1 );
11094
11095           bndPart._tol = std::numeric_limits<double>::max(); // tolerance by segment size
11096           for ( size_t i = 1; i < bndPart._nodes.size(); ++i )
11097             bndPart._tol = Min( bndPart._tol,
11098                                 ( bndPart._nodes[i-1] - bndPart._nodes[i] ).SquareModulus() );
11099         }
11100
11101         if ( _boundary.empty() || ! _boundary.back().CanAppend( bndPart ))
11102           _boundary.push_back( bndPart );
11103         else
11104           _boundary.back().Append( bndPart );
11105       }
11106
11107       _nbBoundaryParts = _boundary.size();
11108       if ( _nbBoundaryParts > 1 && _boundary.front()._isShrink == _boundary.back()._isShrink )
11109       {
11110         _boundary.back().Append( _boundary.front() );
11111         _boundary.pop_front();
11112         --_nbBoundaryParts;
11113       }
11114     }
11115
11116     void GetElements( std::set<const SMDS_MeshElement*>& theElems)
11117     {
11118       if ( SMESHDS_SubMesh* sm = _subMesh->GetSubMeshDS() )
11119         for ( SMDS_ElemIteratorPtr fIt = sm->GetElements(); fIt->more(); )
11120           theElems.insert( theElems.end(), fIt->next() );
11121
11122       return ;
11123     }
11124
11125     void GetBoundaryPoints( std::vector< gp_XYZ >& points )
11126     {
11127       points.reserve( _boundarySize );
11128       size_t  nb = _boundary.rbegin()->_nodes.size();
11129       smIdType lastID = _boundary.rbegin()->Node( nb - 1 )->GetID();
11130       std::list< BndPart >::const_iterator part = _boundary.begin();
11131       for ( ; part != _boundary.end(); ++part )
11132       {
11133         size_t nb = part->_nodes.size();
11134         size_t iF = 0;
11135         size_t iR = nb - 1;
11136         size_t* i = part->_isReverse ? &iR : &iF;
11137         if ( part->_nodes[ *i ]->GetID() == lastID )
11138           ++iF, --iR;
11139         for ( ; iF < nb; ++iF, --iR )
11140           points.push_back( part->_nodes[ *i ]);
11141         --iF, ++iR;
11142         lastID = part->_nodes[ *i ]->GetID();
11143       }
11144     }
11145   }; // struct ShrinkFace
11146
11147   //--------------------------------------------------------------------------------
11148   /*!
11149    * \brief Periodic FACEs
11150    */
11151   struct Periodicity
11152   {
11153     std::vector< ShrinkFace >    _shrinkFaces;
11154     std::vector< PeriodicFaces > _periodicFaces;
11155
11156     PeriodicFaces* GetPeriodic( const TopoDS_Face& face, const TopTools_MapOfShape& shrunkFaces )
11157     {
11158       for ( size_t i = 0; i < _periodicFaces.size(); ++i )
11159         if ( _periodicFaces[ i ].IncludeShrunk( face, shrunkFaces ))
11160           return & _periodicFaces[ i ];
11161       return 0;
11162     }
11163     void ClearPeriodic( const TopoDS_Face& face )
11164     {
11165       for ( size_t i = 0; i < _periodicFaces.size(); ++i )
11166         if ( _periodicFaces[ i ]._shriFace[0]->IsSame( face ) ||
11167              _periodicFaces[ i ]._shriFace[1]->IsSame( face ))
11168           _periodicFaces[ i ].Clear();
11169     }
11170   };
11171
11172   //================================================================================
11173   /*!
11174    * Check if a pair includes the given FACE and the other FACE is already shrunk
11175    */
11176   bool PeriodicFaces::IncludeShrunk( const TopoDS_Face&         face,
11177                                      const TopTools_MapOfShape& shrunkFaces ) const
11178   {
11179     if ( IsEmpty() ) return false;
11180     return (( _shriFace[0]->IsSame( face ) && _shriFace[1]->IsShrunk( shrunkFaces )) ||
11181             ( _shriFace[1]->IsSame( face ) && _shriFace[0]->IsShrunk( shrunkFaces )));
11182   }
11183
11184   //================================================================================
11185   /*!
11186    * Make equal meshes on periodic faces by moving corresponding nodes
11187    */
11188   bool PeriodicFaces::MoveNodes( const TopoDS_Face& tgtFace )
11189   {
11190     int iTgt = _shriFace[1]->IsSame( tgtFace );
11191     int iSrc = 1 - iTgt;
11192
11193     _SolidData* dataSrc = _shriFace[iSrc]->_data1;
11194     _SolidData* dataTgt = _shriFace[iTgt]->_data1;
11195
11196     Trsf * trsf = & _trsf, trsfInverse;
11197     if ( iSrc != 0 )
11198     {
11199       trsfInverse = _trsf;
11200       if ( !trsfInverse.Invert())
11201         return false;
11202       trsf = &trsfInverse;
11203     }
11204     SMESHDS_Mesh* meshDS = dataSrc->GetHelper().GetMeshDS();
11205
11206     dumpFunction(SMESH_Comment("periodicMoveNodes_F")
11207                                << _shriFace[iSrc]->_subMesh->GetId() << "_F"
11208                                << _shriFace[iTgt]->_subMesh->GetId() );
11209     TNode2Edge::iterator n2e;
11210     TNodeNodeMap::iterator n2n = _nnMap.begin();
11211     for ( ; n2n != _nnMap.end(); ++n2n )
11212     {
11213       const SMDS_MeshNode* const* nn = & n2n->first;
11214       const SMDS_MeshNode*      nSrc = nn[ iSrc ];
11215       const SMDS_MeshNode*      nTgt = nn[ iTgt ];
11216
11217       if (( nSrc->GetPosition()->GetDim() == 2 ) ||
11218           (( n2e = dataSrc->_n2eMap.find( nSrc )) == dataSrc->_n2eMap.end() ))
11219       {
11220         SMESH_NodeXYZ pSrc = nSrc;
11221         gp_XYZ pTgt = trsf->Transform( pSrc );
11222         meshDS->MoveNode( nTgt, pTgt.X(), pTgt.Y(), pTgt.Z() );
11223       }
11224       else
11225       {
11226         _LayerEdge* leSrc = n2e->second;
11227         n2e = dataTgt->_n2eMap.find( nTgt );
11228         if ( n2e == dataTgt->_n2eMap.end() )
11229           break;
11230         _LayerEdge* leTgt = n2e->second;
11231         if ( leSrc->_nodes.size() != leTgt->_nodes.size() )
11232           break;
11233         for ( size_t iN = 1; iN < leSrc->_nodes.size(); ++iN )
11234         {
11235           SMESH_NodeXYZ pSrc = leSrc->_nodes[ iN ];
11236           gp_XYZ pTgt = trsf->Transform( pSrc );
11237           meshDS->MoveNode( leTgt->_nodes[ iN ], pTgt.X(), pTgt.Y(), pTgt.Z() );
11238
11239           dumpMove( leTgt->_nodes[ iN ]);
11240         }
11241       }
11242     }
11243     bool done = ( n2n == _nnMap.end() );
11244     debugMsg( "PeriodicFaces::MoveNodes "
11245               << _shriFace[iSrc]->_subMesh->GetId() << " -> "
11246               << _shriFace[iTgt]->_subMesh->GetId() << " -- "
11247               << ( done ? "DONE" : "FAIL"));
11248     dumpFunctionEnd();
11249
11250     return done;
11251   }
11252 } // namespace VISCOUS_3D; Periodicity part
11253
11254
11255 //================================================================================
11256 /*!
11257  * \brief Find FACEs to shrink, that are equally meshed before shrink (i.e. periodic)
11258  *        and should remain equal after shrink
11259  */
11260 //================================================================================
11261
11262 void _ViscousBuilder::findPeriodicFaces()
11263 {
11264   // make map of (ids of FACEs to shrink mesh on) to (list of _SolidData containing
11265   // _LayerEdge's inflated along FACE or EDGE)
11266   std::map< TGeomID, std::list< _SolidData* > > id2sdMap;
11267   for ( size_t i = 0 ; i < _sdVec.size(); ++i )
11268   {
11269     _SolidData& data = _sdVec[i];
11270     std::map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
11271     for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
11272       if ( s2s->second.ShapeType() == TopAbs_FACE )
11273         id2sdMap[ getMeshDS()->ShapeToIndex( s2s->second )].push_back( &data );
11274   }
11275
11276   _periodicity.reset( new Periodicity );
11277   _periodicity->_shrinkFaces.resize( id2sdMap.size() );
11278
11279   std::map< TGeomID, std::list< _SolidData* > >::iterator id2sdIt = id2sdMap.begin();
11280   for ( size_t i = 0; i < id2sdMap.size(); ++i, ++id2sdIt )
11281   {
11282     _SolidData* sd1 = id2sdIt->second.front();
11283     _SolidData* sd2 = id2sdIt->second.back();
11284     _periodicity->_shrinkFaces[ i ].Init( _mesh->GetSubMeshContaining( id2sdIt->first ), sd1, sd2 );
11285   }
11286
11287   for (   size_t i1 = 0;      i1 < _periodicity->_shrinkFaces.size(); ++i1 )
11288     for ( size_t i2 = i1 + 1; i2 < _periodicity->_shrinkFaces.size(); ++i2 )
11289     {
11290       PeriodicFaces pf( & _periodicity->_shrinkFaces[ i1 ],
11291                         & _periodicity->_shrinkFaces[ i2 ]);
11292       if ( pf._shriFace[0]->IsPeriodic( *pf._shriFace[1], pf ))
11293       {
11294         _periodicity->_periodicFaces.push_back( pf );
11295       }
11296     }
11297   return;
11298 }
11299
11300 //================================================================================
11301 /*!
11302  * \brief Shrink 2D mesh on faces to let space for inflated layers
11303  */
11304 //================================================================================
11305
11306 bool _ViscousBuilder::shrink(_SolidData& theData)
11307 {
11308   // make map of (ids of FACEs to shrink mesh on) to (list of _SolidData containing
11309   // _LayerEdge's inflated along FACE or EDGE)
11310   map< TGeomID, list< _SolidData* > > f2sdMap;
11311   for ( size_t i = 0 ; i < _sdVec.size(); ++i )
11312   {
11313     _SolidData& data = _sdVec[i];
11314     map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
11315     for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
11316       if ( s2s->second.ShapeType() == TopAbs_FACE && !_shrunkFaces.Contains( s2s->second ))
11317       {
11318         f2sdMap[ getMeshDS()->ShapeToIndex( s2s->second )].push_back( &data );
11319
11320         // Put mesh faces on the shrunk FACE to the proxy sub-mesh to avoid
11321         // usage of mesh faces made in addBoundaryElements() by the 3D algo or
11322         // by StdMeshers_QuadToTriaAdaptor
11323         if ( SMESHDS_SubMesh* smDS = getMeshDS()->MeshElements( s2s->second ))
11324         {
11325           SMESH_ProxyMesh::SubMesh* proxySub =
11326             data._proxyMesh->getFaceSubM( TopoDS::Face( s2s->second ), /*create=*/true);
11327           if ( proxySub->NbElements() == 0 )
11328           {
11329             SMDS_ElemIteratorPtr fIt = smDS->GetElements();
11330             while ( fIt->more() )
11331             {
11332               const SMDS_MeshElement* f = fIt->next();
11333               // as a result 3D algo will use elements from proxySub and not from smDS
11334               proxySub->AddElement( f );
11335               f->setIsMarked( true );
11336
11337               // Mark nodes on the FACE to discriminate them from nodes
11338               // added by addBoundaryElements(); marked nodes are to be smoothed while shrink()
11339               for ( int iN = 0, nbN = f->NbNodes(); iN < nbN; ++iN )
11340               {
11341                 const SMDS_MeshNode* n = f->GetNode( iN );
11342                 if ( n->GetPosition()->GetDim() == 2 )
11343                   n->setIsMarked( true );
11344               }
11345             }
11346           }
11347         }
11348       }
11349   }
11350
11351   SMESH_MesherHelper helper( *_mesh );
11352   helper.ToFixNodeParameters( true );
11353
11354   // EDGEs to shrink
11355   map< TGeomID, _Shrinker1D > e2shrMap;
11356   vector< _EdgesOnShape* > subEOS;
11357   vector< _LayerEdge* > lEdges;
11358
11359   // loop on FACEs to shrink mesh on
11360   map< TGeomID, list< _SolidData* > >::iterator f2sd = f2sdMap.begin();
11361   for ( ; f2sd != f2sdMap.end(); ++f2sd )
11362   {
11363     list< _SolidData* > & dataList = f2sd->second;
11364     if ( dataList.front()->_n2eMap.empty() ||
11365          dataList.back() ->_n2eMap.empty() )
11366       continue; // not yet computed
11367     if ( dataList.front() != &theData &&
11368          dataList.back()  != &theData )
11369       continue;
11370
11371     _SolidData&      data = *dataList.front();
11372     _SolidData*     data2 = dataList.size() > 1 ? dataList.back() : 0;
11373     const TopoDS_Face&  F = TopoDS::Face( getMeshDS()->IndexToShape( f2sd->first ));
11374     SMESH_subMesh*     sm = _mesh->GetSubMesh( F );
11375     SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
11376
11377     Handle(Geom_Surface) surface = BRep_Tool::Surface( F );
11378
11379     _shrunkFaces.Add( F );
11380     helper.SetSubShape( F );
11381
11382     // ==============================
11383     // Use periodicity to move nodes
11384     // ==============================
11385
11386     PeriodicFaces* periodic = _periodicity->GetPeriodic( F, _shrunkFaces );
11387     bool movedByPeriod = ( periodic && periodic->MoveNodes( F ));
11388
11389     // ===========================
11390     // Prepare data for shrinking
11391     // ===========================
11392
11393     // Collect nodes to smooth (they are marked at the beginning of this method)
11394     vector < const SMDS_MeshNode* > smoothNodes;
11395
11396     if ( !movedByPeriod )
11397     {
11398       SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
11399       while ( nIt->more() )
11400       {
11401         const SMDS_MeshNode* n = nIt->next();
11402         if ( n->isMarked() )
11403           smoothNodes.push_back( n );
11404       }
11405     }
11406     // Find out face orientation
11407     double refSign = 1;
11408     const set<TGeomID> ignoreShapes;
11409     bool isOkUV;
11410     if ( !smoothNodes.empty() )
11411     {
11412       vector<_Simplex> simplices;
11413       _Simplex::GetSimplices( smoothNodes[0], simplices, ignoreShapes );
11414       helper.GetNodeUV( F, simplices[0]._nPrev, 0, &isOkUV ); // fix UV of simplex nodes
11415       helper.GetNodeUV( F, simplices[0]._nNext, 0, &isOkUV );
11416       gp_XY uv = helper.GetNodeUV( F, smoothNodes[0], 0, &isOkUV );
11417       if ( !simplices[0].IsForward(uv, smoothNodes[0], F, helper, refSign ))
11418         refSign = -1;
11419     }
11420
11421     // Find _LayerEdge's inflated along F
11422     subEOS.clear();
11423     lEdges.clear();
11424     {
11425       SMESH_subMeshIteratorPtr subIt = sm->getDependsOnIterator(/*includeSelf=*/false,
11426                                                                 /*complexFirst=*/true); //!!!
11427       while ( subIt->more() )
11428       {
11429         const TGeomID subID = subIt->next()->GetId();
11430         if ( data._noShrinkShapes.count( subID ))
11431           continue;
11432         _EdgesOnShape* eos = data.GetShapeEdges( subID );
11433         if ( !eos || eos->_sWOL.IsNull() )
11434           if ( data2 ) // check in adjacent SOLID
11435           {
11436             eos = data2->GetShapeEdges( subID );
11437             if ( !eos || eos->_sWOL.IsNull() )
11438               continue;
11439           }
11440         subEOS.push_back( eos );
11441
11442         if ( !movedByPeriod )
11443           for ( size_t i = 0; i < eos->_edges.size(); ++i )
11444           {
11445             lEdges.push_back( eos->_edges[ i ] );
11446             prepareEdgeToShrink( *eos->_edges[ i ], *eos, helper, smDS );
11447           }
11448       }
11449     }
11450
11451     dumpFunction(SMESH_Comment("beforeShrinkFace")<<f2sd->first); // debug
11452     SMDS_ElemIteratorPtr fIt = smDS->GetElements();
11453     while ( fIt->more() )
11454       if ( const SMDS_MeshElement* f = fIt->next() )
11455         dumpChangeNodes( f );
11456     dumpFunctionEnd();
11457
11458     // Replace source nodes by target nodes in mesh faces to shrink
11459     dumpFunction(SMESH_Comment("replNodesOnFace")<<f2sd->first); // debug
11460     const SMDS_MeshNode* nodes[20];
11461     for ( size_t iS = 0; iS < subEOS.size(); ++iS )
11462     {
11463       _EdgesOnShape& eos = * subEOS[ iS ];
11464       for ( size_t i = 0; i < eos._edges.size(); ++i )
11465       {
11466         _LayerEdge& edge = *eos._edges[i];
11467         const SMDS_MeshNode* srcNode = edge._nodes[0];
11468         const SMDS_MeshNode* tgtNode = edge._nodes.back();
11469         SMDS_ElemIteratorPtr fIt = srcNode->GetInverseElementIterator(SMDSAbs_Face);
11470         while ( fIt->more() )
11471         {
11472           const SMDS_MeshElement* f = fIt->next();
11473           if ( !smDS->Contains( f ) || !f->isMarked() )
11474             continue;
11475           SMDS_NodeIteratorPtr nIt = f->nodeIterator();
11476           for ( int iN = 0; nIt->more(); ++iN )
11477           {
11478             const SMDS_MeshNode* n = nIt->next();
11479             nodes[iN] = ( n == srcNode ? tgtNode : n );
11480           }
11481           helper.GetMeshDS()->ChangeElementNodes( f, nodes, f->NbNodes() );
11482           dumpChangeNodes( f );
11483         }
11484       }
11485     }
11486     dumpFunctionEnd();
11487
11488     // find out if a FACE is concave
11489     const bool isConcaveFace = isConcave( F, helper );
11490
11491     // Create _SmoothNode's on face F
11492     vector< _SmoothNode > nodesToSmooth( smoothNodes.size() );
11493     {
11494       dumpFunction(SMESH_Comment("fixUVOnFace")<<f2sd->first); // debug
11495       const bool sortSimplices = isConcaveFace;
11496       for ( size_t i = 0; i < smoothNodes.size(); ++i )
11497       {
11498         const SMDS_MeshNode* n = smoothNodes[i];
11499         nodesToSmooth[ i ]._node = n;
11500         // src nodes must be already replaced by tgt nodes to have tgt nodes in _simplices
11501         _Simplex::GetSimplices( n, nodesToSmooth[ i ]._simplices, ignoreShapes, 0, sortSimplices);
11502         // fix up incorrect uv of nodes on the FACE
11503         helper.GetNodeUV( F, n, 0, &isOkUV);
11504         dumpMove( n );
11505       }
11506       dumpFunctionEnd();
11507     }
11508     //if ( nodesToSmooth.empty() ) continue;
11509
11510     // Find EDGE's to shrink and set simpices to LayerEdge's
11511     set< _Shrinker1D* > eShri1D;
11512     {
11513       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
11514       {
11515         _EdgesOnShape& eos = * subEOS[ iS ];
11516         if ( eos.SWOLType() == TopAbs_EDGE )
11517         {
11518           SMESH_subMesh* edgeSM = _mesh->GetSubMesh( eos._sWOL );
11519           VISCOUS_3D::ToClearSubWithMain( edgeSM, data._solid );
11520           if ( !movedByPeriod )
11521           {
11522             _Shrinker1D& shrinker = e2shrMap[ edgeSM->GetId() ];
11523             eShri1D.insert( & shrinker );
11524             shrinker.AddEdge( eos._edges[0], eos, helper );
11525             // restore params of nodes on EDGE if the EDGE has been already
11526             // shrunk while shrinking other FACE
11527             shrinker.RestoreParams();
11528           }
11529         }
11530         for ( size_t i = 0; i < eos._edges.size(); ++i )
11531         {
11532           _LayerEdge& edge = * eos._edges[i];
11533           _Simplex::GetSimplices( /*tgtNode=*/edge._nodes.back(), edge._simplices, ignoreShapes );
11534
11535           // additionally mark tgt node; only marked nodes will be used in SetNewLength2d()
11536           // not-marked nodes are those added by refine()
11537           edge._nodes.back()->setIsMarked( true );
11538         }
11539       }
11540     }
11541
11542     bool toFixTria = false; // to improve quality of trias by diagonal swap
11543     if ( isConcaveFace && !movedByPeriod )
11544     {
11545       const bool hasTria = _mesh->NbTriangles(), hasQuad = _mesh->NbQuadrangles();
11546       if ( hasTria != hasQuad ) {
11547         toFixTria = hasTria;
11548       }
11549       else {
11550         set<int> nbNodesSet;
11551         SMDS_ElemIteratorPtr fIt = smDS->GetElements();
11552         while ( fIt->more() && nbNodesSet.size() < 2 )
11553           nbNodesSet.insert( fIt->next()->NbCornerNodes() );
11554         toFixTria = ( *nbNodesSet.begin() == 3 );
11555       }
11556     }
11557
11558     // ==================
11559     // Perform shrinking
11560     // ==================
11561
11562     bool shrunk = !movedByPeriod;
11563     int nbBad, shriStep=0, smooStep=0;
11564     _SmoothNode::SmoothType smoothType
11565       = isConcaveFace ? _SmoothNode::ANGULAR : _SmoothNode::LAPLACIAN;
11566     SMESH_Comment errMsg;
11567     while ( shrunk )
11568     {
11569       shriStep++;
11570       // Move boundary nodes (actually just set new UV)
11571       // -----------------------------------------------
11572       dumpFunction(SMESH_Comment("moveBoundaryOnF")<<f2sd->first<<"_st"<<shriStep ); // debug
11573       shrunk = false;
11574       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
11575       {
11576         _EdgesOnShape& eos = * subEOS[ iS ];
11577         for ( size_t i = 0; i < eos._edges.size(); ++i )
11578         {
11579           shrunk |= eos._edges[i]->SetNewLength2d( surface, F, eos, helper );
11580         }
11581       }
11582       dumpFunctionEnd();
11583
11584       // Move nodes on EDGE's
11585       // (XYZ is set as soon as a needed length reached in SetNewLength2d())
11586       set< _Shrinker1D* >::iterator shr = eShri1D.begin();
11587       for ( ; shr != eShri1D.end(); ++shr )
11588         (*shr)->Compute( /*set3D=*/false, helper );
11589
11590       // Smoothing in 2D
11591       // -----------------
11592       int nbNoImpSteps = 0;
11593       bool       moved = true;
11594       nbBad = 1;
11595       while (( nbNoImpSteps < 5 && nbBad > 0) && moved)
11596       {
11597         dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
11598
11599         int oldBadNb = nbBad;
11600         nbBad = 0;
11601         moved = false;
11602         // '% 5' minimizes NB FUNCTIONS on viscous_layers_00/B2 case
11603         _SmoothNode::SmoothType smooTy = ( smooStep % 5 ) ? smoothType : _SmoothNode::LAPLACIAN;
11604         for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
11605         {
11606           moved |= nodesToSmooth[i].Smooth( nbBad, surface, helper, refSign,
11607                                             smooTy, /*set3D=*/isConcaveFace);
11608         }
11609         if ( nbBad < oldBadNb )
11610           nbNoImpSteps = 0;
11611         else
11612           nbNoImpSteps++;
11613
11614         dumpFunctionEnd();
11615       }
11616
11617       errMsg.clear();
11618       if ( nbBad > 0 )
11619         errMsg << "Can't shrink 2D mesh on face " << f2sd->first;
11620       if ( shriStep > 200 )
11621         errMsg << "Infinite loop at shrinking 2D mesh on face " << f2sd->first;
11622       if ( !errMsg.empty() )
11623         break;
11624
11625       // Fix narrow triangles by swapping diagonals
11626       // ---------------------------------------
11627       if ( toFixTria )
11628       {
11629         set<const SMDS_MeshNode*> usedNodes;
11630         fixBadFaces( F, helper, /*is2D=*/true, shriStep, & usedNodes); // swap diagonals
11631
11632         // update working data
11633         set<const SMDS_MeshNode*>::iterator n;
11634         for ( size_t i = 0; i < nodesToSmooth.size() && !usedNodes.empty(); ++i )
11635         {
11636           n = usedNodes.find( nodesToSmooth[ i ]._node );
11637           if ( n != usedNodes.end())
11638           {
11639             _Simplex::GetSimplices( nodesToSmooth[ i ]._node,
11640                                     nodesToSmooth[ i ]._simplices,
11641                                     ignoreShapes, NULL,
11642                                     /*sortSimplices=*/ smoothType == _SmoothNode::ANGULAR );
11643             usedNodes.erase( n );
11644           }
11645         }
11646         for ( size_t i = 0; i < lEdges.size() && !usedNodes.empty(); ++i )
11647         {
11648           n = usedNodes.find( /*tgtNode=*/ lEdges[i]->_nodes.back() );
11649           if ( n != usedNodes.end())
11650           {
11651             _Simplex::GetSimplices( lEdges[i]->_nodes.back(),
11652                                     lEdges[i]->_simplices,
11653                                     ignoreShapes );
11654             usedNodes.erase( n );
11655           }
11656         }
11657       }
11658       // TODO: check effect of this additional smooth
11659       // additional laplacian smooth to increase allowed shrink step
11660       // for ( int st = 1; st; --st )
11661       // {
11662       //   dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
11663       //   for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
11664       //   {
11665       //     nodesToSmooth[i].Smooth( nbBad,surface,helper,refSign,
11666       //                              _SmoothNode::LAPLACIAN,/*set3D=*/false);
11667       //   }
11668       // }
11669
11670     } // while ( shrunk )
11671
11672     if ( !errMsg.empty() ) // Try to re-compute the shrink FACE
11673     {
11674       debugMsg( "Re-compute FACE " << f2sd->first << " because " << errMsg );
11675
11676       // remove faces
11677       SMESHDS_SubMesh* psm = data._proxyMesh->getFaceSubM( F );
11678       {
11679         vector< const SMDS_MeshElement* > facesToRm;
11680         if ( psm )
11681         {
11682           facesToRm.reserve( psm->NbElements() );
11683           for ( SMDS_ElemIteratorPtr ite = psm->GetElements(); ite->more(); )
11684             facesToRm.push_back( ite->next() );
11685
11686           for ( size_t i = 0 ; i < _sdVec.size(); ++i )
11687             if (( psm = _sdVec[i]._proxyMesh->getFaceSubM( F )))
11688               psm->Clear();
11689         }
11690         for ( size_t i = 0; i < facesToRm.size(); ++i )
11691           getMeshDS()->RemoveFreeElement( facesToRm[i], smDS, /*fromGroups=*/false );
11692       }
11693       // remove nodes
11694       {
11695         TIDSortedNodeSet nodesToKeep; // nodes of _LayerEdge to keep
11696         for ( size_t iS = 0; iS < subEOS.size(); ++iS ) {
11697           for ( size_t i = 0; i < subEOS[iS]->_edges.size(); ++i )
11698             nodesToKeep.insert( ++( subEOS[iS]->_edges[i]->_nodes.begin() ),
11699                                 subEOS[iS]->_edges[i]->_nodes.end() );
11700         }
11701         SMDS_NodeIteratorPtr itn = smDS->GetNodes();
11702         while ( itn->more() ) {
11703           const SMDS_MeshNode* n = itn->next();
11704           if ( !nodesToKeep.count( n ))
11705             getMeshDS()->RemoveFreeNode( n, smDS, /*fromGroups=*/false );
11706         }
11707       }
11708       _periodicity->ClearPeriodic( F );
11709
11710       // restore position and UV of target nodes
11711       gp_Pnt p;
11712       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
11713         for ( size_t i = 0; i < subEOS[iS]->_edges.size(); ++i )
11714         {
11715           _LayerEdge*       edge = subEOS[iS]->_edges[i];
11716           SMDS_MeshNode* tgtNode = const_cast< SMDS_MeshNode*& >( edge->_nodes.back() );
11717           if ( edge->_pos.empty() ||
11718                edge->Is( _LayerEdge::SHRUNK )) continue;
11719           if ( subEOS[iS]->SWOLType() == TopAbs_FACE )
11720           {
11721             SMDS_FacePositionPtr pos = tgtNode->GetPosition();
11722             pos->SetUParameter( edge->_pos[0].X() );
11723             pos->SetVParameter( edge->_pos[0].Y() );
11724             p = surface->Value( edge->_pos[0].X(), edge->_pos[0].Y() );
11725           }
11726           else
11727           {
11728             SMDS_EdgePositionPtr pos = tgtNode->GetPosition();
11729             pos->SetUParameter( edge->_pos[0].Coord( U_TGT ));
11730             p = BRepAdaptor_Curve( TopoDS::Edge( subEOS[iS]->_sWOL )).Value( pos->GetUParameter() );
11731           }
11732           tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
11733           dumpMove( tgtNode );
11734         }
11735       // shrink EDGE sub-meshes and set proxy sub-meshes
11736       UVPtStructVec uvPtVec;
11737       set< _Shrinker1D* >::iterator shrIt = eShri1D.begin();
11738       for ( shrIt = eShri1D.begin(); shrIt != eShri1D.end(); ++shrIt )
11739       {
11740         _Shrinker1D* shr = (*shrIt);
11741         shr->Compute( /*set3D=*/true, helper );
11742
11743         // set proxy mesh of EDGEs w/o layers
11744         map< double, const SMDS_MeshNode* > nodes;
11745         SMESH_Algo::GetSortedNodesOnEdge( getMeshDS(), shr->GeomEdge(),/*skipMedium=*/true, nodes);
11746         // remove refinement nodes
11747         const SMDS_MeshNode* sn0 = shr->SrcNode(0), *sn1 = shr->SrcNode(1);
11748         const SMDS_MeshNode* tn0 = shr->TgtNode(0), *tn1 = shr->TgtNode(1);
11749         map< double, const SMDS_MeshNode* >::iterator u2n = nodes.begin();
11750         if ( u2n->second == sn0 || u2n->second == sn1 )
11751         {
11752           while ( u2n->second != tn0 && u2n->second != tn1 )
11753             ++u2n;
11754           nodes.erase( nodes.begin(), u2n );
11755         }
11756         u2n = --nodes.end();
11757         if ( u2n->second == sn0 || u2n->second == sn1 )
11758         {
11759           while ( u2n->second != tn0 && u2n->second != tn1 )
11760             --u2n;
11761           nodes.erase( ++u2n, nodes.end() );
11762         }
11763         // set proxy sub-mesh
11764         uvPtVec.resize( nodes.size() );
11765         u2n = nodes.begin();
11766         BRepAdaptor_Curve2d curve( shr->GeomEdge(), F );
11767         for ( size_t i = 0; i < nodes.size(); ++i, ++u2n )
11768         {
11769           uvPtVec[ i ].node = u2n->second;
11770           uvPtVec[ i ].param = u2n->first;
11771           uvPtVec[ i ].SetUV( curve.Value( u2n->first ).XY() );
11772         }
11773         StdMeshers_FaceSide fSide( uvPtVec, F, shr->GeomEdge(), _mesh );
11774         StdMeshers_ViscousLayers2D::SetProxyMeshOfEdge( fSide );
11775       }
11776
11777       // set proxy mesh of EDGEs with layers
11778       vector< _LayerEdge* > edges;
11779       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
11780       {
11781         _EdgesOnShape& eos = * subEOS[ iS ];
11782         if ( eos.ShapeType() != TopAbs_EDGE ) continue;
11783         if ( eos.size() == 0 )
11784           continue;
11785
11786         const TopoDS_Edge& E = TopoDS::Edge( eos._shape );
11787         data.SortOnEdge( E, eos._edges );
11788
11789         edges.clear();
11790         if ( _EdgesOnShape* eov = data.GetShapeEdges( helper.IthVertex( 0, E, /*CumOri=*/false )))
11791           if ( !eov->_edges.empty() )
11792             edges.push_back( eov->_edges[0] ); // on 1st VERTEX
11793
11794         edges.insert( edges.end(), eos._edges.begin(), eos._edges.end() );
11795
11796         if ( _EdgesOnShape* eov = data.GetShapeEdges( helper.IthVertex( 1, E, /*CumOri=*/false )))
11797           if ( !eov->_edges.empty() )
11798             edges.push_back( eov->_edges[0] ); // on last VERTEX
11799
11800         uvPtVec.resize( edges.size() );
11801         for ( size_t i = 0; i < edges.size(); ++i )
11802         {
11803           uvPtVec[ i ].node = edges[i]->_nodes.back();
11804           uvPtVec[ i ].param = helper.GetNodeU( E, edges[i]->_nodes[0] );
11805           uvPtVec[ i ].SetUV( helper.GetNodeUV( F, edges[i]->_nodes.back() ));
11806         }
11807         if ( uvPtVec[ 0 ].node == uvPtVec.back().node &&            // closed
11808              helper.IsSeamShape( uvPtVec[ 0 ].node->GetShapeID() ))
11809         {
11810           uvPtVec[ 0 ].SetUV( helper.GetNodeUV( F,
11811                                                 edges[0]->_nodes.back(),
11812                                                 edges[1]->_nodes.back() ));
11813           size_t i = edges.size() - 1;
11814           uvPtVec[ i ].SetUV( helper.GetNodeUV( F,
11815                                                 edges[i  ]->_nodes.back(),
11816                                                 edges[i-1]->_nodes.back() ));
11817         }
11818         // if ( edges.empty() )
11819         //   continue;
11820         BRep_Tool::Range( E, uvPtVec[0].param, uvPtVec.back().param );
11821         StdMeshers_FaceSide fSide( uvPtVec, F, E, _mesh );
11822         StdMeshers_ViscousLayers2D::SetProxyMeshOfEdge( fSide );
11823       }
11824       // temporary clear the FACE sub-mesh from faces made by refine()
11825       vector< const SMDS_MeshElement* > elems;
11826       elems.reserve( smDS->NbElements() + smDS->NbNodes() );
11827       for ( SMDS_ElemIteratorPtr ite = smDS->GetElements(); ite->more(); )
11828         elems.push_back( ite->next() );
11829       for ( SMDS_NodeIteratorPtr ite = smDS->GetNodes(); ite->more(); )
11830         elems.push_back( ite->next() );
11831       smDS->Clear();
11832
11833       // compute the mesh on the FACE
11834       TopTools_IndexedMapOfShape allowed(1);
11835       allowed.Add( sm->GetSubShape() );
11836       sm->SetAllowedSubShapes( &allowed );
11837       sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
11838       sm->ComputeStateEngine( SMESH_subMesh::COMPUTE_SUBMESH );
11839       sm->SetAllowedSubShapes( nullptr );
11840
11841       // re-fill proxy sub-meshes of the FACE
11842       for ( size_t i = 0 ; i < _sdVec.size(); ++i )
11843         if (( psm = _sdVec[i]._proxyMesh->getFaceSubM( F )))
11844           for ( SMDS_ElemIteratorPtr ite = smDS->GetElements(); ite->more(); )
11845             psm->AddElement( ite->next() );
11846
11847       // re-fill smDS
11848       for ( size_t i = 0; i < elems.size(); ++i )
11849         smDS->AddElement( elems[i] );
11850
11851       if ( sm->GetComputeState() != SMESH_subMesh::COMPUTE_OK )
11852         return error( errMsg );
11853
11854     } // end of re-meshing in case of failed smoothing
11855     else if ( !movedByPeriod )
11856     {
11857       // No wrongly shaped faces remain; final smooth. Set node XYZ.
11858       bool isStructuredFixed = false;
11859       if ( SMESH_2D_Algo* algo = dynamic_cast<SMESH_2D_Algo*>( sm->GetAlgo() ))
11860         isStructuredFixed = algo->FixInternalNodes( *data._proxyMesh, F );
11861       if ( !isStructuredFixed )
11862       {
11863         if ( isConcaveFace ) // fix narrow faces by swapping diagonals
11864           fixBadFaces( F, helper, /*is2D=*/false, ++shriStep );
11865
11866         for ( int st = 3; st; --st )
11867         {
11868           switch( st ) {
11869           case 1: smoothType = _SmoothNode::LAPLACIAN; break;
11870           case 2: smoothType = _SmoothNode::LAPLACIAN; break;
11871           case 3: smoothType = _SmoothNode::ANGULAR; break;
11872           }
11873           dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
11874           for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
11875           {
11876             nodesToSmooth[i].Smooth( nbBad,surface,helper,refSign,
11877                                      smoothType,/*set3D=*/st==1 );
11878           }
11879           dumpFunctionEnd();
11880         }
11881       }
11882       if ( !getMeshDS()->IsEmbeddedMode() )
11883         // Log node movement
11884         for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
11885         {
11886           SMESH_TNodeXYZ p ( nodesToSmooth[i]._node );
11887           getMeshDS()->MoveNode( nodesToSmooth[i]._node, p.X(), p.Y(), p.Z() );
11888         }
11889     }
11890
11891     // Set an event listener to clear FACE sub-mesh together with SOLID sub-mesh
11892     VISCOUS_3D::ToClearSubWithMain( sm, data._solid );
11893     if ( data2 )
11894       VISCOUS_3D::ToClearSubWithMain( sm, data2->_solid );
11895
11896   } // loop on FACES to shrink mesh on
11897
11898
11899   // Replace source nodes by target nodes in shrunk mesh edges
11900
11901   map< int, _Shrinker1D >::iterator e2shr = e2shrMap.begin();
11902   for ( ; e2shr != e2shrMap.end(); ++e2shr )
11903     e2shr->second.SwapSrcTgtNodes( getMeshDS() );
11904
11905   return true;
11906 }
11907
11908 //================================================================================
11909 /*!
11910  * \brief Computes 2d shrink direction and finds nodes limiting shrinking
11911  */
11912 //================================================================================
11913
11914 bool _ViscousBuilder::prepareEdgeToShrink( _LayerEdge&            edge,
11915                                            _EdgesOnShape&         eos,
11916                                            SMESH_MesherHelper&    helper,
11917                                            const SMESHDS_SubMesh* /*faceSubMesh*/)
11918 {
11919   const SMDS_MeshNode* srcNode = edge._nodes[0];
11920   const SMDS_MeshNode* tgtNode = edge._nodes.back();
11921
11922   if ( eos.SWOLType() == TopAbs_FACE )
11923   {
11924     if ( tgtNode->GetPosition()->GetDim() != 2 ) // not inflated edge
11925     {
11926       edge._pos.clear();
11927       edge.Set( _LayerEdge::SHRUNK );
11928       return srcNode == tgtNode;
11929     }
11930     gp_XY srcUV ( edge._pos[0].X(), edge._pos[0].Y() );          //helper.GetNodeUV( F, srcNode );
11931     gp_XY tgtUV = edge.LastUV( TopoDS::Face( eos._sWOL ), eos ); //helper.GetNodeUV( F, tgtNode );
11932     gp_Vec2d uvDir( srcUV, tgtUV );
11933     double uvLen = uvDir.Magnitude();
11934     uvDir /= uvLen;
11935     edge._normal.SetCoord( uvDir.X(),uvDir.Y(), 0 );
11936     edge._len = uvLen;
11937
11938     //edge._pos.resize(1);
11939     edge._pos[0].SetCoord( tgtUV.X(), tgtUV.Y(), 0 );
11940
11941     // set UV of source node to target node
11942     SMDS_FacePositionPtr pos = tgtNode->GetPosition();
11943     pos->SetUParameter( srcUV.X() );
11944     pos->SetVParameter( srcUV.Y() );
11945   }
11946   else // _sWOL is TopAbs_EDGE
11947   {
11948     if ( tgtNode->GetPosition()->GetDim() != 1 ) // not inflated edge
11949     {
11950       edge._pos.clear();
11951       edge.Set( _LayerEdge::SHRUNK );
11952       return srcNode == tgtNode;
11953     }
11954     const TopoDS_Edge&    E = TopoDS::Edge( eos._sWOL );
11955     SMESHDS_SubMesh* edgeSM = getMeshDS()->MeshElements( E );
11956     if ( !edgeSM || edgeSM->NbElements() == 0 )
11957       return error(SMESH_Comment("Not meshed EDGE ") << getMeshDS()->ShapeToIndex( E ));
11958
11959     const SMDS_MeshNode* n2 = 0;
11960     SMDS_ElemIteratorPtr eIt = srcNode->GetInverseElementIterator(SMDSAbs_Edge);
11961     while ( eIt->more() && !n2 )
11962     {
11963       const SMDS_MeshElement* e = eIt->next();
11964       if ( !edgeSM->Contains(e)) continue;
11965       n2 = e->GetNode( 0 );
11966       if ( n2 == srcNode ) n2 = e->GetNode( 1 );
11967     }
11968     if ( !n2 )
11969       return error(SMESH_Comment("Wrongly meshed EDGE ") << getMeshDS()->ShapeToIndex( E ));
11970
11971     if ( n2 == tgtNode       || // for 3D_mesh_GHS3D_01/B1
11972          n2 == edge._nodes[1] ) // bos #20643
11973     {
11974       // shrunk by other SOLID
11975       edge.Set( _LayerEdge::SHRUNK ); // ???
11976       return true;
11977     }
11978
11979     double uSrc = helper.GetNodeU( E, srcNode, n2 );
11980     double uTgt = helper.GetNodeU( E, tgtNode, srcNode );
11981     double u2   = helper.GetNodeU( E, n2,      srcNode );
11982
11983     //edge._pos.clear();
11984
11985     if ( fabs( uSrc-uTgt ) < 0.99 * fabs( uSrc-u2 ))
11986     {
11987       // tgtNode is located so that it does not make faces with wrong orientation
11988       edge.Set( _LayerEdge::SHRUNK );
11989       return true;
11990     }
11991     //edge._pos.resize(1);
11992     edge._pos[0].SetCoord( U_TGT, uTgt );
11993     edge._pos[0].SetCoord( U_SRC, uSrc );
11994     edge._pos[0].SetCoord( LEN_TGT, fabs( uSrc-uTgt ));
11995
11996     edge._simplices.resize( 1 );
11997     edge._simplices[0]._nPrev = n2;
11998
11999     // set U of source node to the target node
12000     SMDS_EdgePositionPtr pos = tgtNode->GetPosition();
12001     pos->SetUParameter( uSrc );
12002   }
12003   return true;
12004 }
12005
12006 //================================================================================
12007 /*!
12008  * \brief Restore position of a sole node of a _LayerEdge based on _noShrinkShapes
12009  */
12010 //================================================================================
12011
12012 void _ViscousBuilder::restoreNoShrink( _LayerEdge& edge ) const
12013 {
12014   if ( edge._nodes.size() == 1 )
12015   {
12016     edge._pos.clear();
12017     edge._len = 0;
12018
12019     const SMDS_MeshNode* srcNode = edge._nodes[0];
12020     TopoDS_Shape S = SMESH_MesherHelper::GetSubShapeByNode( srcNode, getMeshDS() );
12021     if ( S.IsNull() ) return;
12022
12023     gp_Pnt p;
12024
12025     switch ( S.ShapeType() )
12026     {
12027     case TopAbs_EDGE:
12028     {
12029       double f,l;
12030       TopLoc_Location loc;
12031       Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( S ), loc, f, l );
12032       if ( curve.IsNull() ) return;
12033       SMDS_EdgePositionPtr ePos = srcNode->GetPosition();
12034       p = curve->Value( ePos->GetUParameter() );
12035       break;
12036     }
12037     case TopAbs_VERTEX:
12038     {
12039       p = BRep_Tool::Pnt( TopoDS::Vertex( S ));
12040       break;
12041     }
12042     default: return;
12043     }
12044     getMeshDS()->MoveNode( srcNode, p.X(), p.Y(), p.Z() );
12045     dumpMove( srcNode );
12046   }
12047 }
12048
12049 //================================================================================
12050 /*!
12051  * \brief Try to fix triangles with high aspect ratio by swapping diagonals
12052  */
12053 //================================================================================
12054
12055 void _ViscousBuilder::fixBadFaces(const TopoDS_Face&          F,
12056                                   SMESH_MesherHelper&         helper,
12057                                   const bool                  is2D,
12058                                   const int                   step,
12059                                   set<const SMDS_MeshNode*> * involvedNodes)
12060 {
12061   SMESH::Controls::AspectRatio qualifier;
12062   SMESH::Controls::TSequenceOfXYZ points(3), points1(3), points2(3);
12063   const double maxAspectRatio = is2D ? 4. : 2;
12064   _NodeCoordHelper xyz( F, helper, is2D );
12065
12066   // find bad triangles
12067
12068   vector< const SMDS_MeshElement* > badTrias;
12069   vector< double >                  badAspects;
12070   SMESHDS_SubMesh*      sm = helper.GetMeshDS()->MeshElements( F );
12071   SMDS_ElemIteratorPtr fIt = sm->GetElements();
12072   while ( fIt->more() )
12073   {
12074     const SMDS_MeshElement * f = fIt->next();
12075     if ( f->NbCornerNodes() != 3 ) continue;
12076     for ( int iP = 0; iP < 3; ++iP ) points(iP+1) = xyz( f->GetNode(iP));
12077     double aspect = qualifier.GetValue( points );
12078     if ( aspect > maxAspectRatio )
12079     {
12080       badTrias.push_back( f );
12081       badAspects.push_back( aspect );
12082     }
12083   }
12084   if ( step == 1 )
12085   {
12086     dumpFunction(SMESH_Comment("beforeSwapDiagonals_F")<<helper.GetSubShapeID());
12087     SMDS_ElemIteratorPtr fIt = sm->GetElements();
12088     while ( fIt->more() )
12089     {
12090       const SMDS_MeshElement * f = fIt->next();
12091       if ( f->NbCornerNodes() == 3 )
12092         dumpChangeNodes( f );
12093     }
12094     dumpFunctionEnd();
12095   }
12096   if ( badTrias.empty() )
12097     return;
12098
12099   // find couples of faces to swap diagonal
12100
12101   typedef pair < const SMDS_MeshElement* , const SMDS_MeshElement* > T2Trias;
12102   vector< T2Trias > triaCouples; 
12103
12104   TIDSortedElemSet involvedFaces, emptySet;
12105   for ( size_t iTia = 0; iTia < badTrias.size(); ++iTia )
12106   {
12107     T2Trias trias    [3];
12108     double  aspRatio [3];
12109     int i1, i2, i3;
12110
12111     if ( !involvedFaces.insert( badTrias[iTia] ).second )
12112       continue;
12113     for ( int iP = 0; iP < 3; ++iP )
12114       points(iP+1) = xyz( badTrias[iTia]->GetNode(iP));
12115
12116     // find triangles adjacent to badTrias[iTia] with better aspect ratio after diag-swaping
12117     int bestCouple = -1;
12118     for ( int iSide = 0; iSide < 3; ++iSide )
12119     {
12120       const SMDS_MeshNode* n1 = badTrias[iTia]->GetNode( iSide );
12121       const SMDS_MeshNode* n2 = badTrias[iTia]->GetNode(( iSide+1 ) % 3 );
12122       trias [iSide].first  = badTrias[iTia];
12123       trias [iSide].second = SMESH_MeshAlgos::FindFaceInSet( n1, n2, emptySet, involvedFaces,
12124                                                              & i1, & i2 );
12125       if (( ! trias[iSide].second ) ||
12126           ( trias[iSide].second->NbCornerNodes() != 3 ) ||
12127           ( ! sm->Contains( trias[iSide].second )))
12128         continue;
12129
12130       // aspect ratio of an adjacent tria
12131       for ( int iP = 0; iP < 3; ++iP )
12132         points2(iP+1) = xyz( trias[iSide].second->GetNode(iP));
12133       double aspectInit = qualifier.GetValue( points2 );
12134
12135       // arrange nodes as after diag-swaping
12136       if ( helper.WrapIndex( i1+1, 3 ) == i2 )
12137         i3 = helper.WrapIndex( i1-1, 3 );
12138       else
12139         i3 = helper.WrapIndex( i1+1, 3 );
12140       points1 = points;
12141       points1( 1+ iSide ) = points2( 1+ i3 );
12142       points2( 1+ i2    ) = points1( 1+ ( iSide+2 ) % 3 );
12143
12144       // aspect ratio after diag-swaping
12145       aspRatio[ iSide ] = qualifier.GetValue( points1 ) + qualifier.GetValue( points2 );
12146       if ( aspRatio[ iSide ] > aspectInit + badAspects[ iTia ] )
12147         continue;
12148
12149       // prevent inversion of a triangle
12150       gp_Vec norm1 = gp_Vec( points1(1), points1(3) ) ^ gp_Vec( points1(1), points1(2) );
12151       gp_Vec norm2 = gp_Vec( points2(1), points2(3) ) ^ gp_Vec( points2(1), points2(2) );
12152       if ( norm1 * norm2 < 0. && norm1.Angle( norm2 ) > 70./180.*M_PI )
12153         continue;
12154
12155       if ( bestCouple < 0 || aspRatio[ bestCouple ] > aspRatio[ iSide ] )
12156         bestCouple = iSide;
12157     }
12158
12159     if ( bestCouple >= 0 )
12160     {
12161       triaCouples.push_back( trias[bestCouple] );
12162       involvedFaces.insert ( trias[bestCouple].second );
12163     }
12164     else
12165     {
12166       involvedFaces.erase( badTrias[iTia] );
12167     }
12168   }
12169   if ( triaCouples.empty() )
12170     return;
12171
12172   // swap diagonals
12173
12174   SMESH_MeshEditor editor( helper.GetMesh() );
12175   dumpFunction(SMESH_Comment("beforeSwapDiagonals_F")<<helper.GetSubShapeID()<<"_"<<step);
12176   for ( size_t i = 0; i < triaCouples.size(); ++i )
12177   {
12178     dumpChangeNodes( triaCouples[i].first );
12179     dumpChangeNodes( triaCouples[i].second );
12180     editor.InverseDiag( triaCouples[i].first, triaCouples[i].second );
12181   }
12182
12183   if ( involvedNodes )
12184     for ( size_t i = 0; i < triaCouples.size(); ++i )
12185     {
12186       involvedNodes->insert( triaCouples[i].first->begin_nodes(),
12187                              triaCouples[i].first->end_nodes() );
12188       involvedNodes->insert( triaCouples[i].second->begin_nodes(),
12189                              triaCouples[i].second->end_nodes() );
12190     }
12191
12192   // just for debug dump resulting triangles
12193   dumpFunction(SMESH_Comment("swapDiagonals_F")<<helper.GetSubShapeID()<<"_"<<step);
12194   for ( size_t i = 0; i < triaCouples.size(); ++i )
12195   {
12196     dumpChangeNodes( triaCouples[i].first );
12197     dumpChangeNodes( triaCouples[i].second );
12198   }
12199 }
12200
12201 //================================================================================
12202 /*!
12203  * \brief Move target node to it's final position on the FACE during shrinking
12204  */
12205 //================================================================================
12206
12207 bool _LayerEdge::SetNewLength2d( Handle(Geom_Surface)& surface,
12208                                  const TopoDS_Face&    F,
12209                                  _EdgesOnShape&        eos,
12210                                  SMESH_MesherHelper&   helper )
12211 {
12212   if ( Is( SHRUNK ))
12213     return false; // already at the target position
12214
12215   SMDS_MeshNode* tgtNode = const_cast< SMDS_MeshNode*& >( _nodes.back() );
12216
12217   if ( eos.SWOLType() == TopAbs_FACE )
12218   {
12219     gp_XY    curUV = helper.GetNodeUV( F, tgtNode );
12220     gp_Pnt2d tgtUV( _pos[0].X(), _pos[0].Y() );
12221     gp_Vec2d uvDir( _normal.X(), _normal.Y() );
12222     const double uvLen = tgtUV.Distance( curUV );
12223     const double kSafe = Max( 0.5, 1. - 0.1 * _simplices.size() );
12224
12225     // Select shrinking step such that not to make faces with wrong orientation.
12226     double stepSize = 1e100;
12227     for ( size_t i = 0; i < _simplices.size(); ++i )
12228     {
12229       if ( !_simplices[i]._nPrev->isMarked() ||
12230            !_simplices[i]._nNext->isMarked() )
12231         continue; // simplex of quadrangle created by addBoundaryElements()
12232
12233       // find intersection of 2 lines: curUV-tgtUV and that connecting simplex nodes
12234       gp_XY uvN1 = helper.GetNodeUV( F, _simplices[i]._nPrev, tgtNode );
12235       gp_XY uvN2 = helper.GetNodeUV( F, _simplices[i]._nNext, tgtNode );
12236       gp_XY dirN = uvN2 - uvN1;
12237       double det = uvDir.Crossed( dirN );
12238       if ( Abs( det )  < std::numeric_limits<double>::min() ) continue;
12239       gp_XY dirN2Cur = curUV - uvN1;
12240       double step = dirN.Crossed( dirN2Cur ) / det;
12241       if ( step > 0 )
12242         stepSize = Min( step, stepSize );
12243     }
12244     gp_Pnt2d newUV;
12245     if ( uvLen <= stepSize )
12246     {
12247       newUV = tgtUV;
12248       Set( SHRUNK );
12249       //_pos.clear();
12250     }
12251     else if ( stepSize > 0 )
12252     {
12253       newUV = curUV + uvDir.XY() * stepSize * kSafe;
12254     }
12255     else
12256     {
12257       return true;
12258     }
12259     SMDS_FacePositionPtr pos = tgtNode->GetPosition();
12260     pos->SetUParameter( newUV.X() );
12261     pos->SetVParameter( newUV.Y() );
12262
12263 #ifdef __myDEBUG
12264     gp_Pnt p = surface->Value( newUV.X(), newUV.Y() );
12265     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
12266     dumpMove( tgtNode );
12267 #else
12268     if ( surface.IsNull() ) {}
12269 #endif
12270   }
12271   else // _sWOL is TopAbs_EDGE
12272   {
12273     const TopoDS_Edge&      E = TopoDS::Edge( eos._sWOL );
12274     const SMDS_MeshNode*   n2 = _simplices[0]._nPrev;
12275     SMDS_EdgePositionPtr tgtPos = tgtNode->GetPosition();
12276
12277     const double u2     = helper.GetNodeU( E, n2, tgtNode );
12278     const double uSrc   = _pos[0].Coord( U_SRC );
12279     const double lenTgt = _pos[0].Coord( LEN_TGT );
12280
12281     double newU = _pos[0].Coord( U_TGT );
12282     if ( lenTgt < 0.99 * fabs( uSrc-u2 )) // n2 got out of src-tgt range
12283     {
12284       Set( _LayerEdge::SHRUNK );
12285       //_pos.clear();
12286     }
12287     else
12288     {
12289       newU = 0.1 * tgtPos->GetUParameter() + 0.9 * u2;
12290     }
12291     tgtPos->SetUParameter( newU );
12292 #ifdef __myDEBUG
12293     gp_XY newUV = helper.GetNodeUV( F, tgtNode, _nodes[0]);
12294     gp_Pnt p = surface->Value( newUV.X(), newUV.Y() );
12295     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
12296     dumpMove( tgtNode );
12297 #endif
12298   }
12299
12300   return true;
12301 }
12302
12303 //================================================================================
12304 /*!
12305  * \brief Perform smooth on the FACE
12306  *  \retval bool - true if the node has been moved
12307  */
12308 //================================================================================
12309
12310 bool _SmoothNode::Smooth(int&                  nbBad,
12311                          Handle(Geom_Surface)& surface,
12312                          SMESH_MesherHelper&   helper,
12313                          const double          refSign,
12314                          SmoothType            how,
12315                          bool                  set3D)
12316 {
12317   const TopoDS_Face& face = TopoDS::Face( helper.GetSubShape() );
12318
12319   // get uv of surrounding nodes
12320   vector<gp_XY> uv( _simplices.size() );
12321   for ( size_t i = 0; i < _simplices.size(); ++i )
12322     uv[i] = helper.GetNodeUV( face, _simplices[i]._nPrev, _node );
12323
12324   // compute new UV for the node
12325   gp_XY newPos (0,0);
12326   if ( how == TFI && _simplices.size() == 4 )
12327   {
12328     gp_XY corners[4];
12329     for ( size_t i = 0; i < _simplices.size(); ++i )
12330       if ( _simplices[i]._nOpp )
12331         corners[i] = helper.GetNodeUV( face, _simplices[i]._nOpp, _node );
12332       else
12333         throw SALOME_Exception(LOCALIZED("TFI smoothing: _Simplex::_nOpp not set!"));
12334
12335     newPos = helper.calcTFI ( 0.5, 0.5,
12336                               corners[0], corners[1], corners[2], corners[3],
12337                               uv[1], uv[2], uv[3], uv[0] );
12338   }
12339   else if ( how == ANGULAR )
12340   {
12341     newPos = computeAngularPos( uv, helper.GetNodeUV( face, _node ), refSign );
12342   }
12343   else if ( how == CENTROIDAL && _simplices.size() > 3 )
12344   {
12345     // average centers of diagonals wieghted with their reciprocal lengths
12346     if ( _simplices.size() == 4 )
12347     {
12348       double w1 = 1. / ( uv[2]-uv[0] ).SquareModulus();
12349       double w2 = 1. / ( uv[3]-uv[1] ).SquareModulus();
12350       newPos = ( w1 * ( uv[2]+uv[0] ) + w2 * ( uv[3]+uv[1] )) / ( w1+w2 ) / 2;
12351     }
12352     else
12353     {
12354       double sumWeight = 0;
12355       int nb = _simplices.size() == 4 ? 2 : _simplices.size();
12356       for ( int i = 0; i < nb; ++i )
12357       {
12358         int iFrom = i + 2;
12359         int iTo   = i + _simplices.size() - 1;
12360         for ( int j = iFrom; j < iTo; ++j )
12361         {
12362           int i2 = SMESH_MesherHelper::WrapIndex( j, _simplices.size() );
12363           double w = 1. / ( uv[i]-uv[i2] ).SquareModulus();
12364           sumWeight += w;
12365           newPos += w * ( uv[i]+uv[i2] );
12366         }
12367       }
12368       newPos /= 2 * sumWeight; // 2 is to get a middle between uv's
12369     }
12370   }
12371   else
12372   {
12373     // Laplacian smooth
12374     for ( size_t i = 0; i < _simplices.size(); ++i )
12375       newPos += uv[i];
12376     newPos /= _simplices.size();
12377   }
12378
12379   // count quality metrics (orientation) of triangles around the node
12380   int nbOkBefore = 0;
12381   gp_XY tgtUV = helper.GetNodeUV( face, _node );
12382   for ( size_t i = 0; i < _simplices.size(); ++i )
12383     nbOkBefore += _simplices[i].IsForward( tgtUV, _node, face, helper, refSign );
12384
12385   int nbOkAfter = 0;
12386   for ( size_t i = 0; i < _simplices.size(); ++i )
12387     nbOkAfter += _simplices[i].IsForward( newPos, _node, face, helper, refSign );
12388
12389   if ( nbOkAfter < nbOkBefore )
12390   {
12391     nbBad += _simplices.size() - nbOkBefore;
12392     return false;
12393   }
12394
12395   SMDS_FacePositionPtr pos = _node->GetPosition();
12396   pos->SetUParameter( newPos.X() );
12397   pos->SetVParameter( newPos.Y() );
12398
12399 #ifdef __myDEBUG
12400   set3D = true;
12401 #endif
12402   if ( set3D )
12403   {
12404     gp_Pnt p = surface->Value( newPos.X(), newPos.Y() );
12405     const_cast< SMDS_MeshNode* >( _node )->setXYZ( p.X(), p.Y(), p.Z() );
12406     dumpMove( _node );
12407   }
12408
12409   nbBad += _simplices.size() - nbOkAfter;
12410   return ( (tgtUV-newPos).SquareModulus() > 1e-10 );
12411 }
12412
12413 //================================================================================
12414 /*!
12415  * \brief Computes new UV using angle based smoothing technique
12416  */
12417 //================================================================================
12418
12419 gp_XY _SmoothNode::computeAngularPos(vector<gp_XY>& uv,
12420                                      const gp_XY&   uvToFix,
12421                                      const double   refSign)
12422 {
12423   uv.push_back( uv.front() );
12424
12425   vector< gp_XY >  edgeDir ( uv.size() );
12426   vector< double > edgeSize( uv.size() );
12427   for ( size_t i = 1; i < edgeDir.size(); ++i )
12428   {
12429     edgeDir [i-1] = uv[i] - uv[i-1];
12430     edgeSize[i-1] = edgeDir[i-1].Modulus();
12431     if ( edgeSize[i-1] < numeric_limits<double>::min() )
12432       edgeDir[i-1].SetX( 100 );
12433     else
12434       edgeDir[i-1] /= edgeSize[i-1] * refSign;
12435   }
12436   edgeDir.back()  = edgeDir.front();
12437   edgeSize.back() = edgeSize.front();
12438
12439   gp_XY  newPos(0,0);
12440   double sumWgt = 0;
12441   for ( size_t i = 1; i < edgeDir.size(); ++i )
12442   {
12443     const int i1 = i-1;
12444     if ( edgeDir[i1].X() > 1. ) continue;
12445     while ( edgeDir[i].X() > 1. && ++i < edgeDir.size() );
12446     if ( i == edgeDir.size() ) break;
12447     gp_XY p = uv[i];
12448     gp_XY norm1( -edgeDir[i1].Y(), edgeDir[i1].X() );
12449     gp_XY norm2( -edgeDir[i ].Y(), edgeDir[i ].X() );
12450     gp_XY bisec = norm1 + norm2;
12451     double bisecSize = bisec.Modulus();
12452     if ( bisecSize < numeric_limits<double>::min() )
12453     {
12454       bisec = -edgeDir[i1] + edgeDir[i];
12455       bisecSize = bisec.Modulus();
12456     }
12457     bisec /= bisecSize;
12458
12459     gp_XY   dirToN = uvToFix - p;
12460     double distToN = bisec * dirToN;
12461     if ( bisec * dirToN < 0 )
12462       distToN = -distToN;
12463
12464     double wgt = edgeSize[i1] + edgeSize[i];
12465     newPos += ( p + bisec * distToN ) * wgt;
12466     sumWgt += wgt;
12467   }
12468   newPos /= sumWgt;
12469   return newPos;
12470 }
12471
12472 //================================================================================
12473 /*!
12474  * \brief Keep a _LayerEdge inflated along the EDGE
12475  */
12476 //================================================================================
12477
12478 void _Shrinker1D::AddEdge( const _LayerEdge*   e,
12479                            _EdgesOnShape&      eos,
12480                            SMESH_MesherHelper& helper )
12481 {
12482   // init
12483   if ( _nodes.empty() )
12484   {
12485     _edges[0] = _edges[1] = 0;
12486     _done = false;
12487   }
12488   // check _LayerEdge
12489   if ( e == _edges[0] || e == _edges[1] || e->_nodes.size() < 2 )
12490     return;
12491   if ( eos.SWOLType() != TopAbs_EDGE )
12492     throw SALOME_Exception(LOCALIZED("Wrong _LayerEdge is added"));
12493   if ( _edges[0] && !_geomEdge.IsSame( eos._sWOL ))
12494     throw SALOME_Exception(LOCALIZED("Wrong _LayerEdge is added"));
12495
12496   // store _LayerEdge
12497   _geomEdge = TopoDS::Edge( eos._sWOL );
12498   double f,l;
12499   BRep_Tool::Range( _geomEdge, f,l );
12500   double u = helper.GetNodeU( _geomEdge, e->_nodes[0], e->_nodes.back());
12501   _edges[ u < 0.5*(f+l) ? 0 : 1 ] = e;
12502
12503   // Check if the nodes are already shrunk by another SOLID
12504
12505   const SMDS_MeshNode* tgtNode0 = TgtNode( 0 );
12506   const SMDS_MeshNode* tgtNode1 = TgtNode( 1 );
12507
12508   _done = (( tgtNode0 && tgtNode0->NbInverseElements( SMDSAbs_Edge ) == 2 ) ||
12509            ( tgtNode1 && tgtNode1->NbInverseElements( SMDSAbs_Edge ) == 2 ));
12510   if ( _done )
12511     _nodes.resize( 1, nullptr );
12512
12513   // Update _nodes
12514
12515   if ( _nodes.empty() )
12516   {
12517     SMESHDS_SubMesh * eSubMesh = helper.GetMeshDS()->MeshElements( _geomEdge );
12518     if ( !eSubMesh || eSubMesh->NbNodes() < 1 )
12519       return;
12520     TopLoc_Location loc;
12521     Handle(Geom_Curve) C = BRep_Tool::Curve( _geomEdge, loc, f,l );
12522     GeomAdaptor_Curve aCurve(C, f,l);
12523     const double totLen = GCPnts_AbscissaPoint::Length(aCurve, f, l);
12524
12525     smIdType nbExpectNodes = eSubMesh->NbNodes();
12526     _initU  .reserve( nbExpectNodes );
12527     _normPar.reserve( nbExpectNodes );
12528     _nodes  .reserve( nbExpectNodes );
12529     SMDS_NodeIteratorPtr nIt = eSubMesh->GetNodes();
12530     while ( nIt->more() )
12531     {
12532       const SMDS_MeshNode* node = nIt->next();
12533
12534       // skip refinement nodes
12535       if ( node->NbInverseElements(SMDSAbs_Edge) == 0 ||
12536            node == tgtNode0 || node == tgtNode1 )
12537         continue;
12538       bool hasMarkedFace = false;
12539       SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
12540       while ( fIt->more() && !hasMarkedFace )
12541         hasMarkedFace = fIt->next()->isMarked();
12542       if ( !hasMarkedFace )
12543         continue;
12544
12545       _nodes.push_back( node );
12546       _initU.push_back( helper.GetNodeU( _geomEdge, node ));
12547       double len = GCPnts_AbscissaPoint::Length(aCurve, f, _initU.back());
12548       _normPar.push_back(  len / totLen );
12549     }
12550   }
12551   else
12552   {
12553     // remove target node of the _LayerEdge from _nodes
12554     size_t nbFound = 0;
12555     for ( size_t i = 0; i < _nodes.size(); ++i )
12556       if ( !_nodes[i] || _nodes[i] == tgtNode0 || _nodes[i] == tgtNode1 )
12557         _nodes[i] = 0, nbFound++;
12558     if ( nbFound == _nodes.size() )
12559       _nodes.clear();
12560   }
12561 }
12562
12563 //================================================================================
12564 /*!
12565  * \brief Move nodes on EDGE from ends where _LayerEdge's are inflated
12566  */
12567 //================================================================================
12568
12569 void _Shrinker1D::Compute(bool set3D, SMESH_MesherHelper& helper)
12570 {
12571   if ( _done || _nodes.empty())
12572     return;
12573   const _LayerEdge* e = _edges[0];
12574   if ( !e ) e = _edges[1];
12575   if ( !e ) return;
12576
12577   _done =  (( !_edges[0] || _edges[0]->Is( _LayerEdge::SHRUNK )) &&
12578             ( !_edges[1] || _edges[1]->Is( _LayerEdge::SHRUNK )));
12579
12580   double f,l;
12581   if ( set3D || _done )
12582   {
12583     dumpFunction(SMESH_Comment("shrink1D_E") << helper.GetMeshDS()->ShapeToIndex( _geomEdge )<<
12584                  "_F" << helper.GetSubShapeID() );
12585     Handle(Geom_Curve) C = BRep_Tool::Curve(_geomEdge, f,l);
12586     GeomAdaptor_Curve aCurve(C, f,l);
12587
12588     if ( _edges[0] )
12589       f = helper.GetNodeU( _geomEdge, _edges[0]->_nodes.back(), _nodes[0] );
12590     if ( _edges[1] )
12591       l = helper.GetNodeU( _geomEdge, _edges[1]->_nodes.back(), _nodes.back() );
12592     double totLen = GCPnts_AbscissaPoint::Length( aCurve, f, l );
12593
12594     for ( size_t i = 0; i < _nodes.size(); ++i )
12595     {
12596       if ( !_nodes[i] ) continue;
12597       double len = totLen * _normPar[i];
12598       GCPnts_AbscissaPoint discret( aCurve, len, f );
12599       if ( !discret.IsDone() )
12600         return throw SALOME_Exception(LOCALIZED("GCPnts_AbscissaPoint failed"));
12601       double u = discret.Parameter();
12602       SMDS_EdgePositionPtr pos = _nodes[i]->GetPosition();
12603       pos->SetUParameter( u );
12604       gp_Pnt p = C->Value( u );
12605       const_cast< SMDS_MeshNode*>( _nodes[i] )->setXYZ( p.X(), p.Y(), p.Z() );
12606       dumpMove( _nodes[i] );
12607     }
12608     dumpFunctionEnd();
12609   }
12610   else
12611   {
12612     BRep_Tool::Range( _geomEdge, f,l );
12613     if ( _edges[0] )
12614       f = helper.GetNodeU( _geomEdge, _edges[0]->_nodes.back(), _nodes[0] );
12615     if ( _edges[1] )
12616       l = helper.GetNodeU( _geomEdge, _edges[1]->_nodes.back(), _nodes.back() );
12617     
12618     for ( size_t i = 0; i < _nodes.size(); ++i )
12619     {
12620       if ( !_nodes[i] ) continue;
12621       double u = f * ( 1-_normPar[i] ) + l * _normPar[i];
12622       SMDS_EdgePositionPtr pos = _nodes[i]->GetPosition();
12623       pos->SetUParameter( u );
12624     }
12625   }
12626 }
12627
12628 //================================================================================
12629 /*!
12630  * \brief Restore initial parameters of nodes on EDGE
12631  */
12632 //================================================================================
12633
12634 void _Shrinker1D::RestoreParams()
12635 {
12636   if ( _done )
12637     for ( size_t i = 0; i < _nodes.size(); ++i )
12638     {
12639       if ( !_nodes[i] ) continue;
12640       SMDS_EdgePositionPtr pos = _nodes[i]->GetPosition();
12641       pos->SetUParameter( _initU[i] );
12642     }
12643   _done = false;
12644 }
12645
12646 //================================================================================
12647 /*!
12648  * \brief Replace source nodes by target nodes in shrunk mesh edges
12649  */
12650 //================================================================================
12651
12652 void _Shrinker1D::SwapSrcTgtNodes( SMESHDS_Mesh* mesh )
12653 {
12654   const SMDS_MeshNode* nodes[3];
12655   for ( int i = 0; i < 2; ++i )
12656   {
12657     if ( !_edges[i] ) continue;
12658
12659     SMESHDS_SubMesh * eSubMesh = mesh->MeshElements( _geomEdge );
12660     if ( !eSubMesh ) return;
12661     const SMDS_MeshNode* srcNode = _edges[i]->_nodes[0];
12662     const SMDS_MeshNode* tgtNode = _edges[i]->_nodes.back();
12663     const SMDS_MeshNode* scdNode = _edges[i]->_nodes[1];
12664     SMDS_ElemIteratorPtr eIt = srcNode->GetInverseElementIterator(SMDSAbs_Edge);
12665     while ( eIt->more() )
12666     {
12667       const SMDS_MeshElement* e = eIt->next();
12668       if ( !eSubMesh->Contains( e ) || e->GetNodeIndex( scdNode ) >= 0 )
12669           continue;
12670       SMDS_ElemIteratorPtr nIt = e->nodesIterator();
12671       for ( int iN = 0; iN < e->NbNodes(); ++iN )
12672       {
12673         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
12674         nodes[iN] = ( n == srcNode ? tgtNode : n );
12675       }
12676       mesh->ChangeElementNodes( e, nodes, e->NbNodes() );
12677     }
12678   }
12679 }
12680
12681 //================================================================================
12682 /*!
12683  * \brief Setup quadPoints
12684  */
12685 //================================================================================
12686
12687 _Mapper2D::_Mapper2D( const TParam2ColumnMap & param2ColumnMap, const TNode2Edge& n2eMap )
12688 {
12689   size_t i, iSize = _quadPoints.iSize = param2ColumnMap.size();
12690   size_t j, jSize = _quadPoints.jSize = param2ColumnMap.begin()->second.size();
12691   if ( _quadPoints.iSize < 3 ||
12692        _quadPoints.jSize < 3 )
12693     return;
12694   _quadPoints.uv_grid.resize( iSize * jSize );
12695
12696   // set nodes
12697   i = 0;
12698   for ( auto & u_columnNodes : param2ColumnMap )
12699   {
12700     for ( j = 0; j < u_columnNodes.second.size(); ++j )
12701       _quadPoints.UVPt( i, j ).node = u_columnNodes.second[ j ];
12702     ++i;
12703   }
12704
12705   // compute x parameter on borders
12706   uvPnt( 0, 0       ).x = 0;
12707   uvPnt( 0, jSize-1 ).x = 0;
12708   gp_Pnt p0, pPrev0 = SMESH_NodeXYZ( uvPnt( 0, 0       ).node );
12709   gp_Pnt p1, pPrev1 = SMESH_NodeXYZ( uvPnt( 0, jSize-1 ).node );
12710   for ( i = 1; i < iSize; ++i )
12711   {
12712     p0 = SMESH_NodeXYZ( uvPnt( i, 0       ).node );
12713     p1 = SMESH_NodeXYZ( uvPnt( i, jSize-1 ).node );
12714     uvPnt( i, 0       ).x = uvPnt( i-1, 0       ).x + p0.Distance( pPrev0 );
12715     uvPnt( i, jSize-1 ).x = uvPnt( i-1, jSize-1 ).x + p1.Distance( pPrev1 );
12716     pPrev0 = p0;
12717     pPrev1 = p1;
12718   }
12719   for ( i = 1; i < iSize-1; ++i )
12720   {
12721     uvPnt( i, 0       ).x /= uvPnt( iSize-1, 0       ).x;
12722     uvPnt( i, jSize-1 ).x /= uvPnt( iSize-1, jSize-1 ).x;
12723     uvPnt( i, 0       ).y = 0;
12724     uvPnt( i, jSize-1 ).y = 1;
12725   }
12726
12727   // compute y parameter on borders
12728   uvPnt( 0,       0 ).y = 0;
12729   uvPnt( iSize-1, 0 ).y = 0;
12730   pPrev0 = SMESH_NodeXYZ( uvPnt( 0,       0 ).node );
12731   pPrev1 = SMESH_NodeXYZ( uvPnt( iSize-1, 0 ).node );
12732   for ( j = 1; j < jSize; ++j )
12733   {
12734     p0 = SMESH_NodeXYZ( uvPnt( 0,       j ).node );
12735     p1 = SMESH_NodeXYZ( uvPnt( iSize-1, j ).node );
12736     uvPnt( 0,       j ).y = uvPnt( 0,       j-1 ).y + p0.Distance( pPrev0 );
12737     uvPnt( iSize-1, j ).y = uvPnt( iSize-1, j-1 ).y + p1.Distance( pPrev1 );
12738     pPrev0 = p0;
12739     pPrev1 = p1;
12740   }
12741   for ( j = 1; j < jSize-1; ++j )
12742   {
12743     uvPnt( 0,       j ).y /= uvPnt( 0,       jSize-1 ).y;
12744     uvPnt( iSize-1, j ).y /= uvPnt( iSize-1, jSize-1 ).y;
12745     uvPnt( 0,       j ).x = 0;
12746     uvPnt( iSize-1, j ).x = 1;
12747   }
12748
12749   // compute xy of internal nodes
12750   for ( i = 1; i < iSize-1; ++i )
12751   {
12752     const double x0 = uvPnt( i, 0       ).x;
12753     const double x1 = uvPnt( i, jSize-1 ).x;
12754     for ( j = 1; j < jSize-1; ++j )
12755     {
12756       const double y0 = uvPnt( 0,       j ).y;
12757       const double y1 = uvPnt( iSize-1, j ).y;
12758       double x = (x0 + y0 * (x1 - x0)) / (1 - (y1 - y0) * (x1 - x0));
12759       double y = y0 + x * (y1 - y0);
12760       uvPnt( i, j ).x = x;
12761       uvPnt( i, j ).y = y;
12762     }
12763   }
12764
12765   // replace base nodes with target ones
12766   for ( i = 0; i < iSize; ++i )
12767     for ( j = 0; j < jSize; ++j )
12768     {
12769       auto n2e = n2eMap.find( uvPnt( i, j ).node );
12770       uvPnt( i, j ).node = n2e->second->_nodes.back();
12771     }
12772
12773   return;
12774 }
12775
12776 //================================================================================
12777 /*!
12778  * \brief Compute positions of nodes of 2D structured mesh using TFI
12779  */
12780 //================================================================================
12781
12782 bool _Mapper2D::ComputeNodePositions()
12783 {
12784   if ( _quadPoints.uv_grid.empty() )
12785     return true;
12786
12787   size_t i, iSize = _quadPoints.iSize;
12788   size_t j, jSize = _quadPoints.jSize;
12789
12790   SMESH_NodeXYZ a0 ( uvPnt( 0,       0       ).node );
12791   SMESH_NodeXYZ a1 ( uvPnt( iSize-1, 0       ).node );
12792   SMESH_NodeXYZ a2 ( uvPnt( iSize-1, jSize-1 ).node );
12793   SMESH_NodeXYZ a3 ( uvPnt( 0,       jSize-1 ).node );
12794
12795   for ( i = 1; i < iSize-1; ++i )
12796   {
12797     SMESH_NodeXYZ p0 ( uvPnt( i, 0       ).node );
12798     SMESH_NodeXYZ p2 ( uvPnt( i, jSize-1 ).node );
12799     for ( j = 1; j < jSize-1; ++j )
12800     {
12801       SMESH_NodeXYZ p1 ( uvPnt( iSize-1, j ).node );
12802       SMESH_NodeXYZ p3 ( uvPnt( 0,       j ).node );
12803       double x = uvPnt( i, j ).x;
12804       double y = uvPnt( i, j ).y;
12805
12806       gp_XYZ p = SMESH_MesherHelper::calcTFI( x, y, a0,a1,a2,a3, p0,p1,p2,p3 );
12807       const_cast< SMDS_MeshNode* >( uvPnt( i, j ).node )->setXYZ( p.X(), p.Y(), p.Z() );
12808
12809       dumpMove( uvPnt( i, j ).node );
12810     }
12811   }
12812   return true;
12813 }
12814
12815 //================================================================================
12816 /*!
12817  * \brief Creates 2D and 1D elements on boundaries of new prisms
12818  */
12819 //================================================================================
12820
12821 bool _ViscousBuilder::addBoundaryElements(_SolidData& data)
12822 {
12823   SMESH_MesherHelper helper( *_mesh );
12824
12825   vector< const SMDS_MeshNode* > faceNodes;
12826
12827   //for ( size_t i = 0; i < _sdVec.size(); ++i )
12828   {
12829     //_SolidData& data = _sdVec[i];
12830     TopTools_IndexedMapOfShape geomEdges;
12831     TopExp::MapShapes( data._solid, TopAbs_EDGE, geomEdges );
12832     for ( int iE = 1; iE <= geomEdges.Extent(); ++iE )
12833     {
12834       const TopoDS_Edge& E = TopoDS::Edge( geomEdges(iE));
12835       const TGeomID edgeID = getMeshDS()->ShapeToIndex( E );
12836       if ( data._noShrinkShapes.count( edgeID ))
12837         continue;
12838
12839       // Get _LayerEdge's based on E
12840
12841       map< double, const SMDS_MeshNode* > u2nodes;
12842       if ( !SMESH_Algo::GetSortedNodesOnEdge( getMeshDS(), E, /*ignoreMedium=*/false, u2nodes))
12843         continue;
12844
12845       vector< _LayerEdge* > ledges; ledges.reserve( u2nodes.size() );
12846       TNode2Edge & n2eMap = data._n2eMap;
12847       map< double, const SMDS_MeshNode* >::iterator u2n = u2nodes.begin();
12848       {
12849         //check if 2D elements are needed on E
12850         TNode2Edge::iterator n2e = n2eMap.find( u2n->second );
12851         if ( n2e == n2eMap.end() ) continue; // no layers on vertex
12852         ledges.push_back( n2e->second );
12853         u2n++;
12854         if (( n2e = n2eMap.find( u2n->second )) == n2eMap.end() )
12855           continue; // no layers on E
12856         ledges.push_back( n2eMap[ u2n->second ]);
12857
12858         const SMDS_MeshNode* tgtN0 = ledges[0]->_nodes.back();
12859         const SMDS_MeshNode* tgtN1 = ledges[1]->_nodes.back();
12860         int nbSharedPyram = 0;
12861         SMDS_ElemIteratorPtr vIt = tgtN1->GetInverseElementIterator(SMDSAbs_Volume);
12862         while ( vIt->more() )
12863         {
12864           const SMDS_MeshElement* v = vIt->next();
12865           nbSharedPyram += int( v->GetNodeIndex( tgtN0 ) >= 0 );
12866         }
12867         if ( nbSharedPyram > 1 )
12868           continue; // not free border of the pyramid
12869
12870         faceNodes.clear();
12871         faceNodes.push_back( ledges[0]->_nodes[0] );
12872         faceNodes.push_back( ledges[1]->_nodes[0] );
12873         if ( ledges[0]->_nodes.size() > 1 ) faceNodes.push_back( ledges[0]->_nodes[1] );
12874         if ( ledges[1]->_nodes.size() > 1 ) faceNodes.push_back( ledges[1]->_nodes[1] );
12875
12876         if ( getMeshDS()->FindElement( faceNodes, SMDSAbs_Face, /*noMedium=*/true))
12877           continue; // faces already created
12878       }
12879       for ( ++u2n; u2n != u2nodes.end(); ++u2n )
12880         ledges.push_back( n2eMap[ u2n->second ]);
12881
12882       // Find out orientation and type of face to create
12883
12884       bool reverse = false, isOnFace;
12885       TopoDS_Shape F;
12886
12887       map< TGeomID, TopoDS_Shape >::iterator e2f = data._shrinkShape2Shape.find( edgeID );
12888       if (( isOnFace = ( e2f != data._shrinkShape2Shape.end() )))
12889       {
12890         F = e2f->second.Oriented( TopAbs_FORWARD );
12891         reverse = ( helper.GetSubShapeOri( F, E ) == TopAbs_REVERSED );
12892         if ( helper.GetSubShapeOri( data._solid, F ) == TopAbs_REVERSED )
12893           reverse = !reverse, F.Reverse();
12894         if ( helper.IsReversedSubMesh( TopoDS::Face(F) ))
12895           reverse = !reverse;
12896       }
12897       else if ( !data._ignoreFaceIds.count( e2f->first ))
12898       {
12899         // find FACE with layers sharing E
12900         PShapeIteratorPtr fIt = helper.GetAncestors( E, *_mesh, TopAbs_FACE, &data._solid );
12901         if ( fIt->more() )
12902           F = *( fIt->next() );
12903       }
12904       // Find the sub-mesh to add new faces
12905       SMESHDS_SubMesh* sm = 0;
12906       if ( isOnFace )
12907         sm = getMeshDS()->MeshElements( F );
12908       else
12909         sm = data._proxyMesh->getFaceSubM( TopoDS::Face(F), /*create=*/true );
12910       if ( !sm )
12911         return error("error in addBoundaryElements()", data._index);
12912
12913       // Find a proxy sub-mesh of the FACE of an adjacent SOLID, which will use the new boundary
12914       // faces for 3D meshing (PAL23414)
12915       SMESHDS_SubMesh* adjSM = 0;
12916       if ( isOnFace )
12917       {
12918         const TGeomID   faceID = sm->GetID();
12919         PShapeIteratorPtr soIt = helper.GetAncestors( F, *_mesh, TopAbs_SOLID );
12920         while ( const TopoDS_Shape* solid = soIt->next() )
12921           if ( !solid->IsSame( data._solid ))
12922           {
12923             size_t iData = _solids.FindIndex( *solid ) - 1;
12924             if ( iData < _sdVec.size() &&
12925                  _sdVec[ iData ]._ignoreFaceIds.count( faceID ) &&
12926                  _sdVec[ iData ]._shrinkShape2Shape.count( edgeID ) == 0 )
12927             {
12928               SMESH_ProxyMesh::SubMesh* proxySub =
12929                 _sdVec[ iData ]._proxyMesh->getFaceSubM( TopoDS::Face( F ), /*create=*/false);
12930               if ( proxySub && proxySub->NbElements() > 0 )
12931                 adjSM = proxySub;
12932             }
12933           }
12934       }
12935
12936       // Make faces
12937       const int dj1 = reverse ? 0 : 1;
12938       const int dj2 = reverse ? 1 : 0;
12939       vector< const SMDS_MeshElement*> ff; // new faces row
12940       SMESHDS_Mesh* m = getMeshDS();
12941       for ( size_t j = 1; j < ledges.size(); ++j )
12942       {
12943         vector< const SMDS_MeshNode*>&  nn1 = ledges[j-dj1]->_nodes;
12944         vector< const SMDS_MeshNode*>&  nn2 = ledges[j-dj2]->_nodes;
12945         ff.resize( std::max( nn1.size(), nn2.size() ), NULL );
12946         if ( nn1.size() == nn2.size() )
12947         {
12948           if ( isOnFace )
12949             for ( size_t z = 1; z < nn1.size(); ++z )
12950               sm->AddElement( ff[z-1] = m->AddFace( nn1[z-1], nn2[z-1], nn2[z], nn1[z] ));
12951           else
12952             for ( size_t z = 1; z < nn1.size(); ++z )
12953               sm->AddElement( new SMDS_FaceOfNodes( nn1[z-1], nn2[z-1], nn2[z], nn1[z] ));
12954         }
12955         else if ( nn1.size() == 1 )
12956         {
12957           if ( isOnFace )
12958             for ( size_t z = 1; z < nn2.size(); ++z )
12959               sm->AddElement( ff[z-1] = m->AddFace( nn1[0], nn2[z-1], nn2[z] ));
12960           else
12961             for ( size_t z = 1; z < nn2.size(); ++z )
12962               sm->AddElement( new SMDS_FaceOfNodes( nn1[0], nn2[z-1], nn2[z] ));
12963         }
12964         else
12965         {
12966           if ( isOnFace )
12967             for ( size_t z = 1; z < nn1.size(); ++z )
12968               sm->AddElement( ff[z-1] = m->AddFace( nn1[z-1], nn2[0], nn1[z] ));
12969           else
12970             for ( size_t z = 1; z < nn1.size(); ++z )
12971               sm->AddElement( new SMDS_FaceOfNodes( nn1[z-1], nn2[0], nn2[z] ));
12972         }
12973
12974         if ( adjSM ) // add faces to a proxy SM of the adjacent SOLID
12975         {
12976           for ( size_t z = 0; z < ff.size(); ++z )
12977             if ( ff[ z ])
12978               adjSM->AddElement( ff[ z ]);
12979           ff.clear();
12980         }
12981       }
12982
12983       // Make edges
12984       for ( int isFirst = 0; isFirst < 2; ++isFirst )
12985       {
12986         _LayerEdge* edge = isFirst ? ledges.front() : ledges.back();
12987         _EdgesOnShape* eos = data.GetShapeEdges( edge );
12988         if ( eos && eos->SWOLType() == TopAbs_EDGE )
12989         {
12990           vector< const SMDS_MeshNode*>&  nn = edge->_nodes;
12991           if ( nn.size() < 2 || nn[1]->NbInverseElements( SMDSAbs_Edge ) >= 2 )
12992             continue;
12993           helper.SetSubShape( eos->_sWOL );
12994           helper.SetElementsOnShape( true );
12995           for ( size_t z = 1; z < nn.size(); ++z )
12996             helper.AddEdge( nn[z-1], nn[z] );
12997         }
12998       }
12999
13000     } // loop on EDGE's
13001   } // loop on _SolidData's
13002
13003   return true;
13004 }