Salome HOME
bos #20643 EDF 22805 - Pb Viscous Layer
[modules/smesh.git] / src / StdMeshers / StdMeshers_ViscousLayers.cxx
1 // Copyright (C) 2007-2021  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 #define __myDEBUG
105 //#define __NOT_INVALIDATE_BAD_SMOOTH
106 //#define __NODES_AT_POS
107 #endif
108
109 #define INCREMENTAL_SMOOTH // smooth only if min angle is too small
110 #define BLOCK_INFLATION // of individual _LayerEdge's
111 #define OLD_NEF_POLYGON
112
113 using namespace std;
114
115 //================================================================================
116 namespace VISCOUS_3D
117 {
118   typedef int TGeomID;
119
120   enum UIndex { U_TGT = 1, U_SRC, LEN_TGT };
121
122   const double theMinSmoothCosin = 0.1;
123   const double theSmoothThickToElemSizeRatio = 0.6;
124   const double theMinSmoothTriaAngle = 30;
125   const double theMinSmoothQuadAngle = 45;
126
127   // what part of thickness is allowed till intersection
128   // (defined by SALOME_TESTS/Grids/smesh/viscous_layers_00/A5)
129   const double theThickToIntersection = 1.5;
130
131   bool needSmoothing( double cosin, double tgtThick, double elemSize )
132   {
133     return cosin * tgtThick > theSmoothThickToElemSizeRatio * elemSize;
134   }
135   double getSmoothingThickness( double cosin, double elemSize )
136   {
137     return theSmoothThickToElemSizeRatio * elemSize / cosin;
138   }
139
140   /*!
141    * \brief SMESH_ProxyMesh computed by _ViscousBuilder for a SOLID.
142    * It is stored in a SMESH_subMesh of the SOLID as SMESH_subMeshEventListenerData
143    */
144   struct _MeshOfSolid : public SMESH_ProxyMesh,
145                         public SMESH_subMeshEventListenerData
146   {
147     bool                  _n2nMapComputed;
148     SMESH_ComputeErrorPtr _warning;
149
150     _MeshOfSolid( SMESH_Mesh* mesh)
151       :SMESH_subMeshEventListenerData( /*isDeletable=*/true),_n2nMapComputed(false)
152     {
153       SMESH_ProxyMesh::setMesh( *mesh );
154     }
155
156     // returns submesh for a geom face
157     SMESH_ProxyMesh::SubMesh* getFaceSubM(const TopoDS_Face& F, bool create=false)
158     {
159       TGeomID i = SMESH_ProxyMesh::shapeIndex(F);
160       return create ? SMESH_ProxyMesh::getProxySubMesh(i) : findProxySubMesh(i);
161     }
162     void setNode2Node(const SMDS_MeshNode*                 srcNode,
163                       const SMDS_MeshNode*                 proxyNode,
164                       const SMESH_ProxyMesh::SubMesh* subMesh)
165     {
166       SMESH_ProxyMesh::setNode2Node( srcNode,proxyNode,subMesh);
167     }
168   };
169   //--------------------------------------------------------------------------------
170   /*!
171    * \brief Listener of events of 3D sub-meshes computed with viscous layers.
172    * It is used to clear an inferior dim sub-meshes modified by viscous layers
173    */
174   class _ShrinkShapeListener : SMESH_subMeshEventListener
175   {
176     _ShrinkShapeListener()
177       : SMESH_subMeshEventListener(/*isDeletable=*/false,
178                                    "StdMeshers_ViscousLayers::_ShrinkShapeListener") {}
179   public:
180     static SMESH_subMeshEventListener* Get() { static _ShrinkShapeListener l; return &l; }
181     virtual void ProcessEvent(const int                       event,
182                               const int                       eventType,
183                               SMESH_subMesh*                  solidSM,
184                               SMESH_subMeshEventListenerData* data,
185                               const SMESH_Hypothesis*         hyp)
186     {
187       if ( SMESH_subMesh::COMPUTE_EVENT == eventType && solidSM->IsEmpty() && data )
188       {
189         SMESH_subMeshEventListener::ProcessEvent(event,eventType,solidSM,data,hyp);
190       }
191     }
192   };
193   //--------------------------------------------------------------------------------
194   /*!
195    * \brief Listener of events of 3D sub-meshes computed with viscous layers.
196    * It is used to store data computed by _ViscousBuilder for a sub-mesh and to
197    * delete the data as soon as it has been used
198    */
199   class _ViscousListener : SMESH_subMeshEventListener
200   {
201     _ViscousListener():
202       SMESH_subMeshEventListener(/*isDeletable=*/false,
203                                  "StdMeshers_ViscousLayers::_ViscousListener") {}
204     static SMESH_subMeshEventListener* Get() { static _ViscousListener l; return &l; }
205   public:
206     virtual void ProcessEvent(const int                       event,
207                               const int                       eventType,
208                               SMESH_subMesh*                  subMesh,
209                               SMESH_subMeshEventListenerData* /*data*/,
210                               const SMESH_Hypothesis*         /*hyp*/)
211     {
212       if (( SMESH_subMesh::COMPUTE_EVENT       == eventType ) &&
213           ( SMESH_subMesh::CHECK_COMPUTE_STATE != event &&
214             SMESH_subMesh::SUBMESH_COMPUTED    != event ))
215       {
216         // delete SMESH_ProxyMesh containing temporary faces
217         subMesh->DeleteEventListener( this );
218       }
219     }
220     // Finds or creates proxy mesh of the solid
221     static _MeshOfSolid* GetSolidMesh(SMESH_Mesh*         mesh,
222                                       const TopoDS_Shape& solid,
223                                       bool                toCreate=false)
224     {
225       if ( !mesh ) return 0;
226       SMESH_subMesh* sm = mesh->GetSubMesh(solid);
227       _MeshOfSolid* data = (_MeshOfSolid*) sm->GetEventListenerData( Get() );
228       if ( !data && toCreate )
229       {
230         data = new _MeshOfSolid(mesh);
231         data->mySubMeshes.push_back( sm ); // to find SOLID by _MeshOfSolid
232         sm->SetEventListener( Get(), data, sm );
233       }
234       return data;
235     }
236     // Removes proxy mesh of the solid
237     static void RemoveSolidMesh(SMESH_Mesh* mesh, const TopoDS_Shape& solid)
238     {
239       mesh->GetSubMesh(solid)->DeleteEventListener( _ViscousListener::Get() );
240     }
241   };
242   
243   //================================================================================
244   /*!
245    * \brief sets a sub-mesh event listener to clear sub-meshes of sub-shapes of
246    * the main shape when sub-mesh of the main shape is cleared,
247    * for example to clear sub-meshes of FACEs when sub-mesh of a SOLID
248    * is cleared
249    */
250   //================================================================================
251
252   void ToClearSubWithMain( SMESH_subMesh* sub, const TopoDS_Shape& main)
253   {
254     SMESH_subMesh* mainSM = sub->GetFather()->GetSubMesh( main );
255     SMESH_subMeshEventListenerData* data =
256       mainSM->GetEventListenerData( _ShrinkShapeListener::Get());
257     if ( data )
258     {
259       if ( find( data->mySubMeshes.begin(), data->mySubMeshes.end(), sub ) ==
260            data->mySubMeshes.end())
261         data->mySubMeshes.push_back( sub );
262     }
263     else
264     {
265       data = SMESH_subMeshEventListenerData::MakeData( /*dependent=*/sub );
266       sub->SetEventListener( _ShrinkShapeListener::Get(), data, /*whereToListenTo=*/mainSM );
267     }
268   }
269   struct _SolidData;
270   //--------------------------------------------------------------------------------
271   /*!
272    * \brief Simplex (triangle or tetrahedron) based on 1 (tria) or 2 (tet) nodes of
273    * _LayerEdge and 2 nodes of the mesh surface beening smoothed.
274    * The class is used to check validity of face or volumes around a smoothed node;
275    * it stores only 2 nodes as the other nodes are stored by _LayerEdge.
276    */
277   struct _Simplex
278   {
279     const SMDS_MeshNode *_nPrev, *_nNext; // nodes on a smoothed mesh surface
280     const SMDS_MeshNode *_nOpp; // in 2D case, a node opposite to a smoothed node in QUAD
281     _Simplex(const SMDS_MeshNode* nPrev=0,
282              const SMDS_MeshNode* nNext=0,
283              const SMDS_MeshNode* nOpp=0)
284       : _nPrev(nPrev), _nNext(nNext), _nOpp(nOpp) {}
285     bool IsForward(const gp_XYZ* pntSrc, const gp_XYZ* pntTgt, double& vol) const
286     {
287       const double M[3][3] =
288         {{ _nNext->X() - pntSrc->X(), _nNext->Y() - pntSrc->Y(), _nNext->Z() - pntSrc->Z() },
289          { pntTgt->X() - pntSrc->X(), pntTgt->Y() - pntSrc->Y(), pntTgt->Z() - pntSrc->Z() },
290          { _nPrev->X() - pntSrc->X(), _nPrev->Y() - pntSrc->Y(), _nPrev->Z() - pntSrc->Z() }};
291       vol = ( + M[0][0] * M[1][1] * M[2][2]
292               + M[0][1] * M[1][2] * M[2][0]
293               + M[0][2] * M[1][0] * M[2][1]
294               - M[0][0] * M[1][2] * M[2][1]
295               - M[0][1] * M[1][0] * M[2][2]
296               - M[0][2] * M[1][1] * M[2][0]);
297       return vol > 1e-100;
298     }
299     bool IsForward(const SMDS_MeshNode* nSrc, const gp_XYZ& pTgt, double& vol) const
300     {
301       SMESH_TNodeXYZ pSrc( nSrc );
302       return IsForward( &pSrc, &pTgt, vol );
303     }
304     bool IsForward(const gp_XY&         tgtUV,
305                    const SMDS_MeshNode* smoothedNode,
306                    const TopoDS_Face&   face,
307                    SMESH_MesherHelper&  helper,
308                    const double         refSign) const
309     {
310       gp_XY prevUV = helper.GetNodeUV( face, _nPrev, smoothedNode );
311       gp_XY nextUV = helper.GetNodeUV( face, _nNext, smoothedNode );
312       gp_Vec2d v1( tgtUV, prevUV ), v2( tgtUV, nextUV );
313       double d = v1 ^ v2;
314       return d*refSign > 1e-100;
315     }
316     bool IsMinAngleOK( const gp_XYZ& pTgt, double& minAngle ) const
317     {
318       SMESH_TNodeXYZ pPrev( _nPrev ), pNext( _nNext );
319       if ( !_nOpp ) // triangle
320       {
321         gp_Vec tp( pPrev - pTgt ), pn( pNext - pPrev ), nt( pTgt - pNext );
322         double tp2 = tp.SquareMagnitude();
323         double pn2 = pn.SquareMagnitude();
324         double nt2 = nt.SquareMagnitude();
325
326         if ( tp2 < pn2 && tp2 < nt2 )
327           minAngle = ( nt * -pn ) * ( nt * -pn ) / nt2 / pn2;
328         else if ( pn2 < nt2 )
329           minAngle = ( tp * -nt ) * ( tp * -nt ) / tp2 / nt2;
330         else
331           minAngle = ( pn * -tp ) * ( pn * -tp ) / pn2 / tp2;
332
333         static double theMaxCos2 = ( Cos( theMinSmoothTriaAngle * M_PI / 180. ) *
334                                      Cos( theMinSmoothTriaAngle * M_PI / 180. ));
335         return minAngle < theMaxCos2;
336       }
337       else // quadrangle
338       {
339         SMESH_TNodeXYZ pOpp( _nOpp );
340         gp_Vec tp( pPrev - pTgt ), po( pOpp - pPrev ), on( pNext - pOpp), nt( pTgt - pNext );
341         double tp2 = tp.SquareMagnitude();
342         double po2 = po.SquareMagnitude();
343         double on2 = on.SquareMagnitude();
344         double nt2 = nt.SquareMagnitude();
345         minAngle = Max( Max((( tp * -nt ) * ( tp * -nt ) / tp2 / nt2 ),
346                             (( po * -tp ) * ( po * -tp ) / po2 / tp2 )),
347                         Max((( on * -po ) * ( on * -po ) / on2 / po2 ),
348                             (( nt * -on ) * ( nt * -on ) / nt2 / on2 )));
349
350         static double theMaxCos2 = ( Cos( theMinSmoothQuadAngle * M_PI / 180. ) *
351                                      Cos( theMinSmoothQuadAngle * M_PI / 180. ));
352         return minAngle < theMaxCos2;
353       }
354     }
355     bool IsNeighbour(const _Simplex& other) const
356     {
357       return _nPrev == other._nNext || _nNext == other._nPrev;
358     }
359     bool Includes( const SMDS_MeshNode* node ) const { return _nPrev == node || _nNext == node; }
360     static void GetSimplices( const SMDS_MeshNode* node,
361                               vector<_Simplex>&   simplices,
362                               const set<TGeomID>& ingnoreShapes,
363                               const _SolidData*   dataToCheckOri = 0,
364                               const bool          toSort = false);
365     static void SortSimplices(vector<_Simplex>& simplices);
366   };
367   //--------------------------------------------------------------------------------
368   /*!
369    * Structure used to take into account surface curvature while smoothing
370    */
371   struct _Curvature
372   {
373     double   _r; // radius
374     double   _k; // factor to correct node smoothed position
375     double   _h2lenRatio; // avgNormProj / (2*avgDist)
376     gp_Pnt2d _uv; // UV used in putOnOffsetSurface()
377   public:
378     static _Curvature* New( double avgNormProj, double avgDist );
379     double lenDelta(double len) const { return _k * ( _r + len ); }
380     double lenDeltaByDist(double dist) const { return dist * _h2lenRatio; }
381   };
382   //--------------------------------------------------------------------------------
383
384   struct _2NearEdges;
385   struct _LayerEdge;
386   struct _EdgesOnShape;
387   struct _Smoother1D;
388   struct _Mapper2D;
389   typedef map< const SMDS_MeshNode*, _LayerEdge*, TIDCompare > TNode2Edge;
390
391   //--------------------------------------------------------------------------------
392   /*!
393    * \brief Edge normal to surface, connecting a node on solid surface (_nodes[0])
394    * and a node of the most internal layer (_nodes.back())
395    */
396   struct _LayerEdge
397   {
398     typedef gp_XYZ (_LayerEdge::*PSmooFun)();
399
400     vector< const SMDS_MeshNode*> _nodes;
401
402     gp_XYZ              _normal;    // to boundary of solid
403     vector<gp_XYZ>      _pos;       // points computed during inflation
404     double              _len;       // length achieved with the last inflation step
405     double              _maxLen;    // maximal possible length
406     double              _cosin;     // of angle (_normal ^ surface)
407     double              _minAngle;  // of _simplices
408     double              _lenFactor; // to compute _len taking _cosin into account
409     int                 _flags;
410
411     // simplices connected to the source node (_nodes[0]);
412     // used for smoothing and quality check of _LayerEdge's based on the FACE
413     vector<_Simplex>    _simplices;
414     vector<_LayerEdge*> _neibors; // all surrounding _LayerEdge's
415     PSmooFun            _smooFunction; // smoothing function
416     _Curvature*         _curvature;
417     // data for smoothing of _LayerEdge's based on the EDGE
418     _2NearEdges*        _2neibors;
419
420     enum EFlags { TO_SMOOTH       = 0x0000001,
421                   MOVED           = 0x0000002, // set by _neibors[i]->SetNewLength()
422                   SMOOTHED        = 0x0000004, // set by _LayerEdge::Smooth()
423                   DIFFICULT       = 0x0000008, // near concave VERTEX
424                   ON_CONCAVE_FACE = 0x0000010,
425                   BLOCKED         = 0x0000020, // not to inflate any more
426                   INTERSECTED     = 0x0000040, // close intersection with a face found
427                   NORMAL_UPDATED  = 0x0000080,
428                   UPD_NORMAL_CONV = 0x0000100, // to update normal on boundary of concave FACE
429                   MARKED          = 0x0000200, // local usage
430                   MULTI_NORMAL    = 0x0000400, // a normal is invisible by some of surrounding faces
431                   NEAR_BOUNDARY   = 0x0000800, // is near FACE boundary forcing smooth
432                   SMOOTHED_C1     = 0x0001000, // is on _eosC1
433                   DISTORTED       = 0x0002000, // was bad before smoothing
434                   RISKY_SWOL      = 0x0004000, // SWOL is parallel to a source FACE
435                   SHRUNK          = 0x0008000, // target node reached a tgt position while shrink()
436                   UNUSED_FLAG     = 0x0100000  // to add user flags after
437     };
438     bool Is   ( int flag ) const { return _flags & flag; }
439     void Set  ( int flag ) { _flags |= flag; }
440     void Unset( int flag ) { _flags &= ~flag; }
441     std::string DumpFlags() const; // debug
442
443     void SetNewLength( double len, _EdgesOnShape& eos, SMESH_MesherHelper& helper );
444     bool SetNewLength2d( Handle(Geom_Surface)& surface,
445                          const TopoDS_Face&    F,
446                          _EdgesOnShape&        eos,
447                          SMESH_MesherHelper&   helper );
448     bool UpdatePositionOnSWOL( SMDS_MeshNode*      n,
449                                double              tol,
450                                _EdgesOnShape&      eos,
451                                SMESH_MesherHelper& helper );
452     void SetDataByNeighbors( const SMDS_MeshNode* n1,
453                              const SMDS_MeshNode* n2,
454                              const _EdgesOnShape& eos,
455                              SMESH_MesherHelper&  helper);
456     void Block( _SolidData& data );
457     void InvalidateStep( size_t curStep, const _EdgesOnShape& eos, bool restoreLength=false );
458     void ChooseSmooFunction(const set< TGeomID >& concaveVertices,
459                             const TNode2Edge&     n2eMap);
460     void SmoothPos( const vector< double >& segLen, const double tol );
461     int  GetSmoothedPos( const double tol );
462     int  Smooth(const int step, const bool isConcaveFace, bool findBest);
463     int  Smooth(const int step, bool findBest, vector< _LayerEdge* >& toSmooth );
464     int  CheckNeiborsOnBoundary(vector< _LayerEdge* >* badNeibors = 0, bool * needSmooth = 0 );
465     void SmoothWoCheck();
466     bool SmoothOnEdge(Handle(ShapeAnalysis_Surface)& surface,
467                       const TopoDS_Face&             F,
468                       SMESH_MesherHelper&            helper);
469     void MoveNearConcaVer( const _EdgesOnShape*    eov,
470                            const _EdgesOnShape*    eos,
471                            const int               step,
472                            vector< _LayerEdge* > & badSmooEdges);
473     bool FindIntersection( SMESH_ElementSearcher&   searcher,
474                            double &                 distance,
475                            const double&            epsilon,
476                            _EdgesOnShape&           eos,
477                            const SMDS_MeshElement** face = 0);
478     bool SegTriaInter( const gp_Ax1&        lastSegment,
479                        const gp_XYZ&        p0,
480                        const gp_XYZ&        p1,
481                        const gp_XYZ&        p2,
482                        double&              dist,
483                        const double&        epsilon) const;
484     bool SegTriaInter( const gp_Ax1&        lastSegment,
485                        const SMDS_MeshNode* n0,
486                        const SMDS_MeshNode* n1,
487                        const SMDS_MeshNode* n2,
488                        double&              dist,
489                        const double&        epsilon) const
490     { return SegTriaInter( lastSegment,
491                            SMESH_TNodeXYZ( n0 ), SMESH_TNodeXYZ( n1 ), SMESH_TNodeXYZ( n2 ),
492                            dist, epsilon );
493     }
494     const gp_XYZ& PrevPos() const { return _pos[ _pos.size() - 2 ]; }
495     gp_XYZ PrevCheckPos( _EdgesOnShape* eos=0 ) const;
496     gp_Ax1 LastSegment(double& segLen, _EdgesOnShape& eos) const;
497     gp_XY  LastUV( const TopoDS_Face& F, _EdgesOnShape& eos, int which=-1 ) const;
498     bool   IsOnEdge() const { return _2neibors; }
499     bool   IsOnFace() const { return ( _nodes[0]->GetPosition()->GetDim() == 2 ); }
500     int    BaseShapeDim() const { return _nodes[0]->GetPosition()->GetDim(); }
501     gp_XYZ Copy( _LayerEdge& other, _EdgesOnShape& eos, SMESH_MesherHelper& helper );
502     double SetCosin( double cosin );
503     void   SetNormal( const gp_XYZ& n ) { _normal = n; }
504     void   SetMaxLen( double l ) { _maxLen = l; }
505     int    NbSteps() const { return _pos.size() - 1; } // nb inlation steps
506     bool   IsNeiborOnEdge( const _LayerEdge* edge ) const;
507     void   SetSmooLen( double len ) { // set _len at which smoothing is needed
508       _cosin = len; // as for _LayerEdge's on FACE _cosin is not used
509     }
510     double GetSmooLen() { return _cosin; } // for _LayerEdge's on FACE _cosin is not used
511
512     gp_XYZ smoothLaplacian();
513     gp_XYZ smoothAngular();
514     gp_XYZ smoothLengthWeighted();
515     gp_XYZ smoothCentroidal();
516     gp_XYZ smoothNefPolygon();
517
518     enum { FUN_LAPLACIAN, FUN_LENWEIGHTED, FUN_CENTROIDAL, FUN_NEFPOLY, FUN_ANGULAR, FUN_NB };
519     static const int theNbSmooFuns = FUN_NB;
520     static PSmooFun _funs[theNbSmooFuns];
521     static const char* _funNames[theNbSmooFuns+1];
522     int smooFunID( PSmooFun fun=0) const;
523   };
524   _LayerEdge::PSmooFun _LayerEdge::_funs[theNbSmooFuns] = { &_LayerEdge::smoothLaplacian,
525                                                             &_LayerEdge::smoothLengthWeighted,
526                                                             &_LayerEdge::smoothCentroidal,
527                                                             &_LayerEdge::smoothNefPolygon,
528                                                             &_LayerEdge::smoothAngular };
529   const char* _LayerEdge::_funNames[theNbSmooFuns+1] = { "Laplacian",
530                                                          "LengthWeighted",
531                                                          "Centroidal",
532                                                          "NefPolygon",
533                                                          "Angular",
534                                                          "None"};
535   struct _LayerEdgeCmp
536   {
537     bool operator () (const _LayerEdge* e1, const _LayerEdge* e2) const
538     {
539       const bool cmpNodes = ( e1 && e2 && e1->_nodes.size() && e2->_nodes.size() );
540       return cmpNodes ? ( e1->_nodes[0]->GetID() < e2->_nodes[0]->GetID()) : ( e1 < e2 );
541     }
542   };
543   //--------------------------------------------------------------------------------
544   /*!
545    * A 2D half plane used by _LayerEdge::smoothNefPolygon()
546    */
547   struct _halfPlane
548   {
549     gp_XY _pos, _dir, _inNorm;
550     bool IsOut( const gp_XY p, const double tol ) const
551     {
552       return _inNorm * ( p - _pos ) < -tol;
553     }
554     bool FindIntersection( const _halfPlane& hp, gp_XY & intPnt )
555     {
556       //const double eps = 1e-10;
557       double D = _dir.Crossed( hp._dir );
558       if ( fabs(D) < std::numeric_limits<double>::min())
559         return false;
560       gp_XY vec21 = _pos - hp._pos; 
561       double u = hp._dir.Crossed( vec21 ) / D; 
562       intPnt = _pos + _dir * u;
563       return true;
564     }
565   };
566   //--------------------------------------------------------------------------------
567   /*!
568    * Structure used to smooth a _LayerEdge based on an EDGE.
569    */
570   struct _2NearEdges
571   {
572     double               _wgt  [2]; // weights of _nodes
573     _LayerEdge*          _edges[2];
574
575      // normal to plane passing through _LayerEdge._normal and tangent of EDGE
576     gp_XYZ*              _plnNorm;
577
578     _2NearEdges() { _edges[0]=_edges[1]=0; _plnNorm = 0; }
579     ~_2NearEdges(){ delete _plnNorm; }
580     const SMDS_MeshNode* tgtNode(bool is2nd) {
581       return _edges[is2nd] ? _edges[is2nd]->_nodes.back() : 0;
582     }
583     const SMDS_MeshNode* srcNode(bool is2nd) {
584       return _edges[is2nd] ? _edges[is2nd]->_nodes[0] : 0;
585     }
586     void reverse() {
587       std::swap( _wgt  [0], _wgt  [1] );
588       std::swap( _edges[0], _edges[1] );
589     }
590     void set( _LayerEdge* e1, _LayerEdge* e2, double w1, double w2 ) {
591       _edges[0] = e1; _edges[1] = e2; _wgt[0] = w1; _wgt[1] = w2;
592     }
593     bool include( const _LayerEdge* e ) {
594       return ( _edges[0] == e || _edges[1] == e );
595     }
596   };
597
598
599   //--------------------------------------------------------------------------------
600   /*!
601    * \brief Layers parameters got by averaging several hypotheses
602    */
603   struct AverageHyp
604   {
605     AverageHyp( const StdMeshers_ViscousLayers* hyp = 0 )
606       :_nbLayers(0), _nbHyps(0), _method(0), _thickness(0), _stretchFactor(0)
607     {
608       Add( hyp );
609     }
610     void Add( const StdMeshers_ViscousLayers* hyp )
611     {
612       if ( hyp )
613       {
614         _nbHyps++;
615         _nbLayers       = hyp->GetNumberLayers();
616         //_thickness     += hyp->GetTotalThickness();
617         _thickness      = Max( _thickness, hyp->GetTotalThickness() );
618         _stretchFactor += hyp->GetStretchFactor();
619         _method         = hyp->GetMethod();
620         if ( _groupName.empty() )
621           _groupName = hyp->GetGroupName();
622       }
623     }
624     double GetTotalThickness() const { return _thickness; /*_nbHyps ? _thickness / _nbHyps : 0;*/ }
625     double GetStretchFactor()  const { return _nbHyps ? _stretchFactor / _nbHyps : 0; }
626     int    GetNumberLayers()   const { return _nbLayers; }
627     int    GetMethod()         const { return _method; }
628     bool   ToCreateGroup()     const { return !_groupName.empty(); }
629     const std::string& GetGroupName() const { return _groupName; }
630
631     double Get1stLayerThickness( double realThickness = 0.) const
632     {
633       const double T = ( realThickness > 0 ) ? realThickness : GetTotalThickness();
634       const double f = GetStretchFactor();
635       const int    N = GetNumberLayers();
636       const double fPowN = pow( f, N );
637       double h0;
638       if ( fPowN - 1 <= numeric_limits<double>::min() )
639         h0 = T / N;
640       else
641         h0 = T * ( f - 1 )/( fPowN - 1 );
642       return h0;
643     }
644
645     bool   UseSurfaceNormal()  const
646     { return _method == StdMeshers_ViscousLayers::SURF_OFFSET_SMOOTH; }
647     bool   ToSmooth()          const
648     { return _method == StdMeshers_ViscousLayers::SURF_OFFSET_SMOOTH; }
649     bool   IsOffsetMethod()    const
650     { return _method == StdMeshers_ViscousLayers::FACE_OFFSET; }
651
652     bool operator==( const AverageHyp& other ) const
653     {
654       return ( _nbLayers == other._nbLayers &&
655                _method   == other._method   &&
656                Equals( GetTotalThickness(), other.GetTotalThickness() ) &&
657                Equals( GetStretchFactor(), other.GetStretchFactor() ));
658     }
659     static bool Equals( double v1, double v2 ) { return Abs( v1 - v2 ) < 0.01 * ( v1 + v2 ); }
660
661   private:
662     int         _nbLayers, _nbHyps, _method;
663     double      _thickness, _stretchFactor;
664     std::string _groupName;
665   };
666
667   //--------------------------------------------------------------------------------
668   /*!
669    * \brief _LayerEdge's on a shape and other shape data
670    */
671   struct _EdgesOnShape
672   {
673     vector< _LayerEdge* > _edges;
674
675     TopoDS_Shape          _shape;
676     TGeomID               _shapeID;
677     SMESH_subMesh *       _subMesh;
678     // face or edge w/o layer along or near which _edges are inflated
679     TopoDS_Shape          _sWOL;
680     bool                  _isRegularSWOL; // w/o singularities
681     // averaged StdMeshers_ViscousLayers parameters
682     AverageHyp            _hyp;
683     bool                  _toSmooth;
684     _Smoother1D*          _edgeSmoother;
685     vector< _EdgesOnShape* > _eosConcaVer; // edges at concave VERTEXes of a FACE
686     vector< _EdgesOnShape* > _eosC1; // to smooth together several C1 continues shapes
687
688     typedef std::unordered_map< const SMDS_MeshElement*, gp_XYZ > TFace2NormMap;
689     TFace2NormMap            _faceNormals; // if _shape is FACE
690     vector< _EdgesOnShape* > _faceEOS; // to get _faceNormals of adjacent FACEs
691
692     Handle(ShapeAnalysis_Surface) _offsetSurf;
693     _LayerEdge*                   _edgeForOffset;
694     double                        _offsetValue;
695     _Mapper2D*                    _mapper2D;
696
697     _SolidData*            _data; // parent SOLID
698
699     _LayerEdge*      operator[](size_t i) const { return (_LayerEdge*) _edges[i]; }
700     size_t           size() const { return _edges.size(); }
701     TopAbs_ShapeEnum ShapeType() const
702     { return _shape.IsNull() ? TopAbs_SHAPE : _shape.ShapeType(); }
703     TopAbs_ShapeEnum SWOLType() const
704     { return _sWOL.IsNull() ? TopAbs_SHAPE : _sWOL.ShapeType(); }
705     bool             HasC1( const _EdgesOnShape* other ) const
706     { return std::find( _eosC1.begin(), _eosC1.end(), other ) != _eosC1.end(); }
707     bool             GetNormal( const SMDS_MeshElement* face, gp_Vec& norm );
708     _SolidData&      GetData() const { return *_data; }
709     char             ShapeTypeLetter() const
710     { switch ( ShapeType() ) { case TopAbs_FACE: return 'F'; case TopAbs_EDGE: return 'E';
711       case TopAbs_VERTEX: return 'V'; default: return 'S'; }}
712
713     _EdgesOnShape(): _shapeID(-1), _subMesh(0), _toSmooth(false), _edgeSmoother(0), _mapper2D(0) {}
714     ~_EdgesOnShape();
715   };
716
717   //--------------------------------------------------------------------------------
718   /*!
719    * \brief Convex FACE whose radius of curvature is less than the thickness of
720    *        layers. It is used to detect distortion of prisms based on a convex
721    *        FACE and to update normals to enable further increasing the thickness
722    */
723   struct _ConvexFace
724   {
725     TopoDS_Face                     _face;
726
727     // edges whose _simplices are used to detect prism distortion
728     vector< _LayerEdge* >           _simplexTestEdges;
729
730     // map a sub-shape to _SolidData::_edgesOnShape
731     map< TGeomID, _EdgesOnShape* >  _subIdToEOS;
732
733     bool                            _isTooCurved;
734     bool                            _normalsFixed;
735     bool                            _normalsFixedOnBorders; // used in putOnOffsetSurface()
736
737     double GetMaxCurvature( _SolidData&         data,
738                             _EdgesOnShape&      eof,
739                             BRepLProp_SLProps&  surfProp,
740                             SMESH_MesherHelper& helper);
741
742     bool GetCenterOfCurvature( _LayerEdge*         ledge,
743                                BRepLProp_SLProps&  surfProp,
744                                SMESH_MesherHelper& helper,
745                                gp_Pnt &            center ) const;
746     bool CheckPrisms() const;
747   };
748
749   //--------------------------------------------------------------------------------
750   /*!
751    * \brief Structure holding _LayerEdge's based on EDGEs that will collide
752    *        at inflation up to the full thickness. A detected collision
753    *        is fixed in updateNormals()
754    */
755   struct _CollisionEdges
756   {
757     _LayerEdge*           _edge;
758     vector< _LayerEdge* > _intEdges; // each pair forms an intersected quadrangle
759     const SMDS_MeshNode* nSrc(int i) const { return _intEdges[i]->_nodes[0]; }
760     const SMDS_MeshNode* nTgt(int i) const { return _intEdges[i]->_nodes.back(); }
761   };
762
763   //--------------------------------------------------------------------------------
764   /*!
765    * \brief Data of a SOLID
766    */
767   struct _SolidData
768   {
769     typedef const StdMeshers_ViscousLayers* THyp;
770     TopoDS_Shape                    _solid;
771     TopTools_MapOfShape             _before; // SOLIDs to be computed before _solid
772     TGeomID                         _index; // SOLID id
773     _MeshOfSolid*                   _proxyMesh;
774     bool                            _done;
775     list< THyp >                    _hyps;
776     list< TopoDS_Shape >            _hypShapes;
777     map< TGeomID, THyp >            _face2hyp; // filled if _hyps.size() > 1
778     set< TGeomID >                  _reversedFaceIds;
779     set< TGeomID >                  _ignoreFaceIds; // WOL FACEs and FACEs of other SOLIDs
780
781     double                          _stepSize, _stepSizeCoeff, _geomSize;
782     const SMDS_MeshNode*            _stepSizeNodes[2];
783
784     TNode2Edge                      _n2eMap; // nodes and _LayerEdge's based on them
785
786     // map to find _n2eMap of another _SolidData by a shrink shape shared by two _SolidData's
787     map< TGeomID, TNode2Edge* >     _s2neMap;
788     // _LayerEdge's with underlying shapes
789     vector< _EdgesOnShape >         _edgesOnShape;
790
791     // key:   an ID of shape (EDGE or VERTEX) shared by a FACE with
792     //        layers and a FACE w/o layers
793     // value: the shape (FACE or EDGE) to shrink mesh on.
794     //       _LayerEdge's basing on nodes on key shape are inflated along the value shape
795     map< TGeomID, TopoDS_Shape >     _shrinkShape2Shape;
796
797     // Convex FACEs whose radius of curvature is less than the thickness of layers
798     map< TGeomID, _ConvexFace >      _convexFaces;
799
800     // shapes (EDGEs and VERTEXes) shrink from which is forbidden due to collisions with
801     // the adjacent SOLID
802     set< TGeomID >                   _noShrinkShapes;
803
804     int                              _nbShapesToSmooth;
805
806     vector< _CollisionEdges >        _collisionEdges;
807     set< TGeomID >                   _concaveFaces;
808
809     double                           _maxThickness; // of all _hyps
810     double                           _minThickness; // of all _hyps
811
812     double                           _epsilon; // precision for SegTriaInter()
813
814     SMESH_MesherHelper*              _helper;
815
816     _SolidData(const TopoDS_Shape& s=TopoDS_Shape(),
817                _MeshOfSolid*       m=0)
818       :_solid(s), _proxyMesh(m), _done(false),_helper(0) {}
819     ~_SolidData() { delete _helper; _helper = 0; }
820
821     void SortOnEdge( const TopoDS_Edge& E, vector< _LayerEdge* >& edges);
822     void Sort2NeiborsOnEdge( vector< _LayerEdge* >& edges );
823
824     _ConvexFace* GetConvexFace( const TGeomID faceID ) {
825       map< TGeomID, _ConvexFace >::iterator id2face = _convexFaces.find( faceID );
826       return id2face == _convexFaces.end() ? 0 : & id2face->second;
827     }
828     _EdgesOnShape* GetShapeEdges(const TGeomID       shapeID );
829     _EdgesOnShape* GetShapeEdges(const TopoDS_Shape& shape );
830     _EdgesOnShape* GetShapeEdges(const _LayerEdge*   edge )
831     { return GetShapeEdges( edge->_nodes[0]->getshapeId() ); }
832
833     SMESH_MesherHelper& GetHelper() const { return *_helper; }
834
835     void UnmarkEdges( int flag = _LayerEdge::MARKED ) {
836       for ( size_t i = 0; i < _edgesOnShape.size(); ++i )
837         for ( size_t j = 0; j < _edgesOnShape[i]._edges.size(); ++j )
838           _edgesOnShape[i]._edges[j]->Unset( flag );
839     }
840     void AddShapesToSmooth( const set< _EdgesOnShape* >& shape,
841                             const set< _EdgesOnShape* >* edgesNoAnaSmooth=0 );
842
843     void PrepareEdgesToSmoothOnFace( _EdgesOnShape* eof, bool substituteSrcNodes );
844   };
845   //--------------------------------------------------------------------------------
846   /*!
847    * \brief Offset plane used in getNormalByOffset()
848    */
849   struct _OffsetPlane
850   {
851     gp_Pln _plane;
852     int    _faceIndex;
853     int    _faceIndexNext[2];
854     gp_Lin _lines[2]; // line of intersection with neighbor _OffsetPlane's
855     bool   _isLineOK[2];
856     _OffsetPlane() {
857       _isLineOK[0] = _isLineOK[1] = false; _faceIndexNext[0] = _faceIndexNext[1] = -1;
858     }
859     void   ComputeIntersectionLine( _OffsetPlane&        pln, 
860                                     const TopoDS_Edge&   E,
861                                     const TopoDS_Vertex& V );
862     gp_XYZ GetCommonPoint(bool& isFound, const TopoDS_Vertex& V) const;
863     int    NbLines() const { return _isLineOK[0] + _isLineOK[1]; }
864   };
865   //--------------------------------------------------------------------------------
866   /*!
867    * \brief Container of centers of curvature at nodes on an EDGE bounding _ConvexFace
868    */
869   struct _CentralCurveOnEdge
870   {
871     bool                  _isDegenerated;
872     vector< gp_Pnt >      _curvaCenters;
873     vector< _LayerEdge* > _ledges;
874     vector< gp_XYZ >      _normals; // new normal for each of _ledges
875     vector< double >      _segLength2;
876
877     TopoDS_Edge           _edge;
878     TopoDS_Face           _adjFace;
879     bool                  _adjFaceToSmooth;
880
881     void Append( const gp_Pnt& center, _LayerEdge* ledge )
882     {
883       if ( ledge->Is( _LayerEdge::MULTI_NORMAL ))
884         return;
885       if ( _curvaCenters.size() > 0 )
886         _segLength2.push_back( center.SquareDistance( _curvaCenters.back() ));
887       _curvaCenters.push_back( center );
888       _ledges.push_back( ledge );
889       _normals.push_back( ledge->_normal );
890     }
891     bool FindNewNormal( const gp_Pnt& center, gp_XYZ& newNormal );
892     void SetShapes( const TopoDS_Edge&  edge,
893                     const _ConvexFace&  convFace,
894                     _SolidData&         data,
895                     SMESH_MesherHelper& helper);
896   };
897   //--------------------------------------------------------------------------------
898   /*!
899    * \brief Data of node on a shrinked FACE
900    */
901   struct _SmoothNode
902   {
903     const SMDS_MeshNode*         _node;
904     vector<_Simplex>             _simplices; // for quality check
905
906     enum SmoothType { LAPLACIAN, CENTROIDAL, ANGULAR, TFI };
907
908     bool Smooth(int&                  badNb,
909                 Handle(Geom_Surface)& surface,
910                 SMESH_MesherHelper&   helper,
911                 const double          refSign,
912                 SmoothType            how,
913                 bool                  set3D);
914
915     gp_XY computeAngularPos(vector<gp_XY>& uv,
916                             const gp_XY&   uvToFix,
917                             const double   refSign );
918   };
919   struct PyDump;
920   struct Periodicity;
921   //--------------------------------------------------------------------------------
922   /*!
923    * \brief Builder of viscous layers
924    */
925   class _ViscousBuilder
926   {
927   public:
928     _ViscousBuilder();
929     // does it's job
930     SMESH_ComputeErrorPtr Compute(SMESH_Mesh&         mesh,
931                                   const TopoDS_Shape& shape);
932     // check validity of hypotheses
933     SMESH_ComputeErrorPtr CheckHypotheses( SMESH_Mesh&         mesh,
934                                            const TopoDS_Shape& shape );
935
936     // restore event listeners used to clear an inferior dim sub-mesh modified by viscous layers
937     void RestoreListeners();
938
939     // computes SMESH_ProxyMesh::SubMesh::_n2n;
940     bool MakeN2NMap( _MeshOfSolid* pm );
941
942   private:
943
944     bool findSolidsWithLayers(const bool checkFaceMesh=true);
945     bool setBefore( _SolidData& solidBefore, _SolidData& solidAfter );
946     bool findFacesWithLayers(const bool onlyWith=false);
947     void findPeriodicFaces();
948     void getIgnoreFaces(const TopoDS_Shape&             solid,
949                         const StdMeshers_ViscousLayers* hyp,
950                         const TopoDS_Shape&             hypShape,
951                         set<TGeomID>&                   ignoreFaces);
952     int makeEdgesOnShape();
953     bool makeLayer(_SolidData& data);
954     void setShapeData( _EdgesOnShape& eos, SMESH_subMesh* sm, _SolidData& data );
955     bool setEdgeData( _LayerEdge& edge, _EdgesOnShape& eos,
956                       SMESH_MesherHelper& helper, _SolidData& data);
957     gp_XYZ getFaceNormal(const SMDS_MeshNode* n,
958                          const TopoDS_Face&   face,
959                          SMESH_MesherHelper&  helper,
960                          bool&                isOK,
961                          bool                 shiftInside=false);
962     bool getFaceNormalAtSingularity(const gp_XY&        uv,
963                                     const TopoDS_Face&  face,
964                                     SMESH_MesherHelper& helper,
965                                     gp_Dir&             normal );
966     gp_XYZ getWeigthedNormal( const _LayerEdge*                edge );
967     gp_XYZ getNormalByOffset( _LayerEdge*                      edge,
968                               std::pair< TopoDS_Face, gp_XYZ > fId2Normal[],
969                               int                              nbFaces,
970                               bool                             lastNoOffset = false);
971     bool findNeiborsOnEdge(const _LayerEdge*     edge,
972                            const SMDS_MeshNode*& n1,
973                            const SMDS_MeshNode*& n2,
974                            _EdgesOnShape&        eos,
975                            _SolidData&           data);
976     void findSimplexTestEdges( _SolidData&                    data,
977                                vector< vector<_LayerEdge*> >& edgesByGeom);
978     void computeGeomSize( _SolidData& data );
979     bool findShapesToSmooth( _SolidData& data);
980     void limitStepSizeByCurvature( _SolidData&  data );
981     void limitStepSize( _SolidData&             data,
982                         const SMDS_MeshElement* face,
983                         const _LayerEdge*       maxCosinEdge );
984     void limitStepSize( _SolidData& data, const double minSize);
985     bool inflate(_SolidData& data);
986     bool smoothAndCheck(_SolidData& data, const int nbSteps, double & distToIntersection);
987     int  invalidateBadSmooth( _SolidData&               data,
988                               SMESH_MesherHelper&       helper,
989                               vector< _LayerEdge* >&    badSmooEdges,
990                               vector< _EdgesOnShape* >& eosC1,
991                               const int                 infStep );
992     void makeOffsetSurface( _EdgesOnShape& eos, SMESH_MesherHelper& );
993     void putOnOffsetSurface( _EdgesOnShape& eos, int infStep,
994                              vector< _EdgesOnShape* >& eosC1,
995                              int smooStep=0, int moveAll=false );
996     void findCollisionEdges( _SolidData& data, SMESH_MesherHelper& helper );
997     void findEdgesToUpdateNormalNearConvexFace( _ConvexFace &       convFace,
998                                                 _SolidData&         data,
999                                                 SMESH_MesherHelper& helper );
1000     void limitMaxLenByCurvature( _SolidData& data, SMESH_MesherHelper& helper );
1001     void limitMaxLenByCurvature( _LayerEdge* e1, _LayerEdge* e2,
1002                                  _EdgesOnShape& eos1, _EdgesOnShape& eos2,
1003                                  const bool isSmoothable );
1004     bool updateNormals( _SolidData& data, SMESH_MesherHelper& helper, int stepNb, double stepSize );
1005     bool updateNormalsOfConvexFaces( _SolidData&         data,
1006                                      SMESH_MesherHelper& helper,
1007                                      int                 stepNb );
1008     void updateNormalsOfC1Vertices( _SolidData& data );
1009     bool updateNormalsOfSmoothed( _SolidData&         data,
1010                                   SMESH_MesherHelper& helper,
1011                                   const int           nbSteps,
1012                                   const double        stepSize );
1013     bool isNewNormalOk( _SolidData&   data,
1014                         _LayerEdge&   edge,
1015                         const gp_XYZ& newNormal);
1016     bool refine(_SolidData& data);
1017     bool shrink(_SolidData& data);
1018     bool prepareEdgeToShrink( _LayerEdge& edge, _EdgesOnShape& eos,
1019                               SMESH_MesherHelper& helper,
1020                               const SMESHDS_SubMesh* faceSubMesh );
1021     void restoreNoShrink( _LayerEdge& edge ) const;
1022     void fixBadFaces(const TopoDS_Face&          F,
1023                      SMESH_MesherHelper&         helper,
1024                      const bool                  is2D,
1025                      const int                   step,
1026                      set<const SMDS_MeshNode*> * involvedNodes=NULL);
1027     bool addBoundaryElements(_SolidData& data);
1028
1029     bool error( const string& text, int solidID=-1 );
1030     SMESHDS_Mesh* getMeshDS() const { return _mesh->GetMeshDS(); }
1031
1032     // debug
1033     void makeGroupOfLE();
1034
1035     SMESH_Mesh*                  _mesh;
1036     SMESH_ComputeErrorPtr        _error;
1037
1038     vector<                      _SolidData >  _sdVec;
1039     TopTools_IndexedMapOfShape   _solids; // to find _SolidData by a solid
1040     TopTools_MapOfShape          _shrunkFaces;
1041     std::unique_ptr<Periodicity> _periodicity;
1042
1043     int                          _tmpFaceID;
1044     PyDump*                      _pyDump;
1045   };
1046   //--------------------------------------------------------------------------------
1047   /*!
1048    * \brief Shrinker of nodes on the EDGE
1049    */
1050   class _Shrinker1D
1051   {
1052     TopoDS_Edge                   _geomEdge;
1053     vector<double>                _initU;
1054     vector<double>                _normPar;
1055     vector<const SMDS_MeshNode*>  _nodes;
1056     const _LayerEdge*             _edges[2];
1057     bool                          _done;
1058   public:
1059     void AddEdge( const _LayerEdge* e, _EdgesOnShape& eos, SMESH_MesherHelper& helper );
1060     void Compute(bool set3D, SMESH_MesherHelper& helper);
1061     void RestoreParams();
1062     void SwapSrcTgtNodes(SMESHDS_Mesh* mesh);
1063     const TopoDS_Edge& GeomEdge() const { return _geomEdge; }
1064     const SMDS_MeshNode* TgtNode( bool is2nd ) const
1065     { return _edges[is2nd] ? _edges[is2nd]->_nodes.back() : 0; }
1066     const SMDS_MeshNode* SrcNode( bool is2nd ) const
1067     { return _edges[is2nd] ? _edges[is2nd]->_nodes[0] : 0; }
1068   };
1069   //--------------------------------------------------------------------------------
1070   /*!
1071    * \brief Smoother of _LayerEdge's on EDGE.
1072    */
1073   struct _Smoother1D
1074   {
1075     struct OffPnt // point of the offsetted EDGE
1076     {
1077       gp_XYZ      _xyz;    // coord of a point inflated from EDGE w/o smooth
1078       double      _len;    // length reached at previous inflation step
1079       double      _param;  // on EDGE
1080       _2NearEdges _2edges; // 2 neighbor _LayerEdge's
1081       gp_XYZ      _edgeDir;// EDGE tangent at _param
1082       double Distance( const OffPnt& p ) const { return ( _xyz - p._xyz ).Modulus(); }
1083     };
1084     vector< OffPnt >   _offPoints;
1085     vector< double >   _leParams; // normalized param of _eos._edges on EDGE
1086     Handle(Geom_Curve) _anaCurve; // for analytic smooth
1087     _LayerEdge         _leOnV[2]; // _LayerEdge's holding normal to the EDGE at VERTEXes
1088     gp_XYZ             _edgeDir[2]; // tangent at VERTEXes
1089     size_t             _iSeg[2];  // index of segment where extreme tgt node is projected
1090     _EdgesOnShape&     _eos;
1091     double             _curveLen; // length of the EDGE
1092     std::pair<int,int> _eToSmooth[2]; // <from,to> indices of _LayerEdge's in _eos
1093
1094     static Handle(Geom_Curve) CurveForSmooth( const TopoDS_Edge&  E,
1095                                               _EdgesOnShape&      eos,
1096                                               SMESH_MesherHelper& helper);
1097
1098     _Smoother1D( Handle(Geom_Curve) curveForSmooth,
1099                  _EdgesOnShape&     eos )
1100       : _anaCurve( curveForSmooth ), _eos( eos )
1101     {
1102     }
1103     bool Perform(_SolidData&                    data,
1104                  Handle(ShapeAnalysis_Surface)& surface,
1105                  const TopoDS_Face&             F,
1106                  SMESH_MesherHelper&            helper );
1107
1108     void prepare(_SolidData& data );
1109
1110     void findEdgesToSmooth();
1111
1112     bool isToSmooth( int iE );
1113
1114     bool smoothAnalyticEdge( _SolidData&                    data,
1115                              Handle(ShapeAnalysis_Surface)& surface,
1116                              const TopoDS_Face&             F,
1117                              SMESH_MesherHelper&            helper);
1118     bool smoothComplexEdge( _SolidData&                     data,
1119                             Handle(ShapeAnalysis_Surface)& surface,
1120                             const TopoDS_Face&             F,
1121                             SMESH_MesherHelper&            helper);
1122     gp_XYZ getNormalNormal( const gp_XYZ & normal,
1123                             const gp_XYZ&  edgeDir);
1124     _LayerEdge* getLEdgeOnV( bool is2nd )
1125     {
1126       return _eos._edges[ is2nd ? _eos._edges.size()-1 : 0 ]->_2neibors->_edges[ is2nd ];
1127     }
1128     bool isAnalytic() const { return !_anaCurve.IsNull(); }
1129
1130     void offPointsToPython() const; // debug
1131   };
1132
1133   //--------------------------------------------------------------------------------
1134   /*!
1135    * \brief Compute positions of nodes of 2D structured mesh using TFI
1136    */
1137   class _Mapper2D
1138   {
1139     FaceQuadStruct _quadPoints;
1140
1141     UVPtStruct& uvPnt( size_t i, size_t j ) { return _quadPoints.UVPt( i, j ); }
1142
1143   public:
1144     _Mapper2D( const TParam2ColumnMap & param2ColumnMap, const TNode2Edge& n2eMap );
1145     bool ComputeNodePositions();
1146   };
1147
1148   //--------------------------------------------------------------------------------
1149   /*!
1150    * \brief Class of temporary mesh face.
1151    * We can't use SMDS_FaceOfNodes since it's impossible to set it's ID which is
1152    * needed because SMESH_ElementSearcher internally uses set of elements sorted by ID
1153    */
1154   struct _TmpMeshFace : public SMDS_PolygonalFaceOfNodes
1155   {
1156     const SMDS_MeshElement* _srcFace;
1157
1158     _TmpMeshFace( const vector<const SMDS_MeshNode*>& nodes,
1159                   int                                 ID,
1160                   int                                 faceID=-1,
1161                   const SMDS_MeshElement*             srcFace=0 ):
1162       SMDS_PolygonalFaceOfNodes(nodes), _srcFace( srcFace ) { setID( ID ); setShapeID( faceID ); }
1163     virtual SMDSAbs_EntityType  GetEntityType() const
1164     { return _srcFace ? _srcFace->GetEntityType() : SMDSEntity_Quadrangle; }
1165     virtual SMDSAbs_GeometryType GetGeomType()  const
1166     { return _srcFace ? _srcFace->GetGeomType() : SMDSGeom_QUADRANGLE; }
1167   };
1168   //--------------------------------------------------------------------------------
1169   /*!
1170    * \brief Class of temporary mesh quadrangle face storing _LayerEdge it's based on
1171    */
1172   struct _TmpMeshFaceOnEdge : public _TmpMeshFace
1173   {
1174     _LayerEdge *_le1, *_le2;
1175     _TmpMeshFaceOnEdge( _LayerEdge* le1, _LayerEdge* le2, int ID ):
1176       _TmpMeshFace( vector<const SMDS_MeshNode*>(4), ID ), _le1(le1), _le2(le2)
1177     {
1178       myNodes[0]=_le1->_nodes[0];
1179       myNodes[1]=_le1->_nodes.back();
1180       myNodes[2]=_le2->_nodes.back();
1181       myNodes[3]=_le2->_nodes[0];
1182     }
1183     const SMDS_MeshNode* n( size_t i ) const
1184     {
1185       return myNodes[ i ];
1186     }
1187     gp_XYZ GetDir() const // return average direction of _LayerEdge's, normal to EDGE
1188     {
1189       SMESH_TNodeXYZ p0s( myNodes[0] );
1190       SMESH_TNodeXYZ p0t( myNodes[1] );
1191       SMESH_TNodeXYZ p1t( myNodes[2] );
1192       SMESH_TNodeXYZ p1s( myNodes[3] );
1193       gp_XYZ  v0 = p0t - p0s;
1194       gp_XYZ  v1 = p1t - p1s;
1195       gp_XYZ v01 = p1s - p0s;
1196       gp_XYZ   n = ( v0 ^ v01 ) + ( v1 ^ v01 );
1197       gp_XYZ   d = v01 ^ n;
1198       d.Normalize();
1199       return d;
1200     }
1201     gp_XYZ GetDir(_LayerEdge* le1, _LayerEdge* le2) // return average direction of _LayerEdge's
1202     {
1203       myNodes[0]=le1->_nodes[0];
1204       myNodes[1]=le1->_nodes.back();
1205       myNodes[2]=le2->_nodes.back();
1206       myNodes[3]=le2->_nodes[0];
1207       return GetDir();
1208     }
1209   };
1210   //--------------------------------------------------------------------------------
1211   /*!
1212    * \brief Retriever of node coordinates either directly or from a surface by node UV.
1213    * \warning Location of a surface is ignored
1214    */
1215   struct _NodeCoordHelper
1216   {
1217     SMESH_MesherHelper&        _helper;
1218     const TopoDS_Face&         _face;
1219     Handle(Geom_Surface)       _surface;
1220     gp_XYZ (_NodeCoordHelper::* _fun)(const SMDS_MeshNode* n) const;
1221
1222     _NodeCoordHelper(const TopoDS_Face& F, SMESH_MesherHelper& helper, bool is2D)
1223       : _helper( helper ), _face( F )
1224     {
1225       if ( is2D )
1226       {
1227         TopLoc_Location loc;
1228         _surface = BRep_Tool::Surface( _face, loc );
1229       }
1230       if ( _surface.IsNull() )
1231         _fun = & _NodeCoordHelper::direct;
1232       else
1233         _fun = & _NodeCoordHelper::byUV;
1234     }
1235     gp_XYZ operator()(const SMDS_MeshNode* n) const { return (this->*_fun)( n ); }
1236
1237   private:
1238     gp_XYZ direct(const SMDS_MeshNode* n) const
1239     {
1240       return SMESH_TNodeXYZ( n );
1241     }
1242     gp_XYZ byUV  (const SMDS_MeshNode* n) const
1243     {
1244       gp_XY uv = _helper.GetNodeUV( _face, n );
1245       return _surface->Value( uv.X(), uv.Y() ).XYZ();
1246     }
1247   };
1248
1249   //================================================================================
1250   /*!
1251    * \brief Check angle between vectors 
1252    */
1253   //================================================================================
1254
1255   inline bool isLessAngle( const gp_Vec& v1, const gp_Vec& v2, const double cos )
1256   {
1257     double dot = v1 * v2; // cos * |v1| * |v2|
1258     double l1  = v1.SquareMagnitude();
1259     double l2  = v2.SquareMagnitude();
1260     return (( dot * cos >= 0 ) && 
1261             ( dot * dot ) / l1 / l2 >= ( cos * cos ));
1262   }
1263
1264   class _Factory
1265   {
1266     ObjectPool< _LayerEdge >  _edgePool;
1267     ObjectPool< _Curvature >  _curvaturePool;
1268     ObjectPool< _2NearEdges > _nearEdgesPool;
1269
1270     static _Factory* & me()
1271     {
1272       static _Factory* theFactory = 0;
1273       return theFactory;
1274     }
1275   public:
1276
1277     _Factory()  { me() = this; }
1278     ~_Factory() { me() = 0; }
1279
1280     static _LayerEdge*  NewLayerEdge() { return me()->_edgePool.getNew(); }
1281     static _Curvature * NewCurvature() { return me()->_curvaturePool.getNew(); }
1282     static _2NearEdges* NewNearEdges() { return me()->_nearEdgesPool.getNew(); }
1283   };
1284
1285 } // namespace VISCOUS_3D
1286
1287
1288
1289 //================================================================================
1290 // StdMeshers_ViscousLayers hypothesis
1291 //
1292 StdMeshers_ViscousLayers::StdMeshers_ViscousLayers(int hypId, SMESH_Gen* gen)
1293   :SMESH_Hypothesis(hypId, gen),
1294    _isToIgnoreShapes(1), _nbLayers(1), _thickness(1), _stretchFactor(1),
1295    _method( SURF_OFFSET_SMOOTH ),
1296    _groupName("")
1297 {
1298   _name = StdMeshers_ViscousLayers::GetHypType();
1299   _param_algo_dim = -3; // auxiliary hyp used by 3D algos
1300 } // --------------------------------------------------------------------------------
1301 void StdMeshers_ViscousLayers::SetBndShapes(const std::vector<int>& faceIds, bool toIgnore)
1302 {
1303   if ( faceIds != _shapeIds )
1304     _shapeIds = faceIds, NotifySubMeshesHypothesisModification();
1305   if ( _isToIgnoreShapes != toIgnore )
1306     _isToIgnoreShapes = toIgnore, NotifySubMeshesHypothesisModification();
1307 } // --------------------------------------------------------------------------------
1308 void StdMeshers_ViscousLayers::SetTotalThickness(double thickness)
1309 {
1310   if ( thickness != _thickness )
1311     _thickness = thickness, NotifySubMeshesHypothesisModification();
1312 } // --------------------------------------------------------------------------------
1313 void StdMeshers_ViscousLayers::SetNumberLayers(int nb)
1314 {
1315   if ( _nbLayers != nb )
1316     _nbLayers = nb, NotifySubMeshesHypothesisModification();
1317 } // --------------------------------------------------------------------------------
1318 void StdMeshers_ViscousLayers::SetStretchFactor(double factor)
1319 {
1320   if ( _stretchFactor != factor )
1321     _stretchFactor = factor, NotifySubMeshesHypothesisModification();
1322 } // --------------------------------------------------------------------------------
1323 void StdMeshers_ViscousLayers::SetMethod( ExtrusionMethod method )
1324 {
1325   if ( _method != method )
1326     _method = method, NotifySubMeshesHypothesisModification();
1327 } // --------------------------------------------------------------------------------
1328 void StdMeshers_ViscousLayers::SetGroupName(const std::string& name)
1329 {
1330   if ( _groupName != name )
1331   {
1332     _groupName = name;
1333     if ( !_groupName.empty() )
1334       NotifySubMeshesHypothesisModification();
1335   }
1336 } // --------------------------------------------------------------------------------
1337 SMESH_ProxyMesh::Ptr
1338 StdMeshers_ViscousLayers::Compute(SMESH_Mesh&         theMesh,
1339                                   const TopoDS_Shape& theShape,
1340                                   const bool          toMakeN2NMap) const
1341 {
1342   using namespace VISCOUS_3D;
1343   _ViscousBuilder builder;
1344   SMESH_ComputeErrorPtr err = builder.Compute( theMesh, theShape );
1345   if ( err && !err->IsOK() )
1346     return SMESH_ProxyMesh::Ptr();
1347
1348   vector<SMESH_ProxyMesh::Ptr> components;
1349   TopExp_Explorer exp( theShape, TopAbs_SOLID );
1350   for ( ; exp.More(); exp.Next() )
1351   {
1352     if ( _MeshOfSolid* pm =
1353          _ViscousListener::GetSolidMesh( &theMesh, exp.Current(), /*toCreate=*/false))
1354     {
1355       if ( toMakeN2NMap && !pm->_n2nMapComputed )
1356         if ( !builder.MakeN2NMap( pm ))
1357           return SMESH_ProxyMesh::Ptr();
1358       components.push_back( SMESH_ProxyMesh::Ptr( pm ));
1359       pm->myIsDeletable = false; // it will de deleted by boost::shared_ptr
1360
1361       if ( pm->_warning && !pm->_warning->IsOK() )
1362       {
1363         SMESH_subMesh* sm = theMesh.GetSubMesh( exp.Current() );
1364         SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1365         if ( !smError || smError->IsOK() )
1366           smError = pm->_warning;
1367       }
1368     }
1369     _ViscousListener::RemoveSolidMesh ( &theMesh, exp.Current() );
1370   }
1371   switch ( components.size() )
1372   {
1373   case 0: break;
1374
1375   case 1: return components[0];
1376
1377   default: return SMESH_ProxyMesh::Ptr( new SMESH_ProxyMesh( components ));
1378   }
1379   return SMESH_ProxyMesh::Ptr();
1380 } // --------------------------------------------------------------------------------
1381 std::ostream & StdMeshers_ViscousLayers::SaveTo(std::ostream & save)
1382 {
1383   save << " " << _nbLayers
1384        << " " << _thickness
1385        << " " << _stretchFactor
1386        << " " << _shapeIds.size();
1387   for ( size_t i = 0; i < _shapeIds.size(); ++i )
1388     save << " " << _shapeIds[i];
1389   save << " " << !_isToIgnoreShapes; // negate to keep the behavior in old studies.
1390   save << " " << _method;
1391   save << " " << _groupName.size();
1392   if ( !_groupName.empty() )
1393     save << " " << _groupName;
1394   return save;
1395 } // --------------------------------------------------------------------------------
1396 std::istream & StdMeshers_ViscousLayers::LoadFrom(std::istream & load)
1397 {
1398   int nbFaces, faceID, shapeToTreat, method;
1399   load >> _nbLayers >> _thickness >> _stretchFactor >> nbFaces;
1400   while ( (int) _shapeIds.size() < nbFaces && load >> faceID )
1401     _shapeIds.push_back( faceID );
1402   if ( load >> shapeToTreat ) {
1403     _isToIgnoreShapes = !shapeToTreat;
1404     if ( load >> method )
1405       _method = (ExtrusionMethod) method;
1406     int nameSize = 0;
1407     if ( load >> nameSize && nameSize > 0 )
1408     {
1409       _groupName.resize( nameSize );
1410       load.get( _groupName[0] ); // remove a white-space
1411       load.getline( &_groupName[0], nameSize + 1 );
1412     }
1413   }
1414   else {
1415     _isToIgnoreShapes = true; // old behavior
1416   }
1417   return load;
1418 } // --------------------------------------------------------------------------------
1419 bool StdMeshers_ViscousLayers::SetParametersByMesh(const SMESH_Mesh*   /*theMesh*/,
1420                                                    const TopoDS_Shape& /*theShape*/)
1421 {
1422   // TODO
1423   return false;
1424 } // --------------------------------------------------------------------------------
1425 SMESH_ComputeErrorPtr
1426 StdMeshers_ViscousLayers::CheckHypothesis(SMESH_Mesh&                          theMesh,
1427                                           const TopoDS_Shape&                  theShape,
1428                                           SMESH_Hypothesis::Hypothesis_Status& theStatus)
1429 {
1430   VISCOUS_3D::_ViscousBuilder builder;
1431   SMESH_ComputeErrorPtr err = builder.CheckHypotheses( theMesh, theShape );
1432   if ( err && !err->IsOK() )
1433     theStatus = SMESH_Hypothesis::HYP_INCOMPAT_HYPS;
1434   else
1435     theStatus = SMESH_Hypothesis::HYP_OK;
1436
1437   return err;
1438 }
1439 // --------------------------------------------------------------------------------
1440 bool StdMeshers_ViscousLayers::IsShapeWithLayers(int shapeIndex) const
1441 {
1442   bool isIn =
1443     ( std::find( _shapeIds.begin(), _shapeIds.end(), shapeIndex ) != _shapeIds.end() );
1444   return IsToIgnoreShapes() ? !isIn : isIn;
1445 }
1446
1447 // --------------------------------------------------------------------------------
1448 SMDS_MeshGroup* StdMeshers_ViscousLayers::CreateGroup( const std::string&  theName,
1449                                                        SMESH_Mesh&         theMesh,
1450                                                        SMDSAbs_ElementType theType)
1451 {
1452   SMESH_Group*      group = 0;
1453   SMDS_MeshGroup* groupDS = 0;
1454
1455   if ( theName.empty() )
1456     return groupDS;
1457        
1458   if ( SMESH_Mesh::GroupIteratorPtr grIt = theMesh.GetGroups() )
1459     while( grIt->more() && !group )
1460     {
1461       group = grIt->next();
1462       if ( !group ||
1463            group->GetGroupDS()->GetType() != theType ||
1464            group->GetName()               != theName ||
1465            !dynamic_cast< SMESHDS_Group* >( group->GetGroupDS() ))
1466         group = 0;
1467     }
1468   if ( !group )
1469     group = theMesh.AddGroup( theType, theName.c_str() );
1470
1471   groupDS = & dynamic_cast< SMESHDS_Group* >( group->GetGroupDS() )->SMDSGroup();
1472
1473   return groupDS;
1474 }
1475
1476 // END StdMeshers_ViscousLayers hypothesis
1477 //================================================================================
1478
1479 namespace VISCOUS_3D
1480 {
1481   gp_XYZ getEdgeDir( const TopoDS_Edge& E, const TopoDS_Vertex& fromV,
1482                      const double h0, bool* isRegularEdge = nullptr )
1483   {
1484     gp_Vec dir;
1485     double f,l;
1486     Handle(Geom_Curve) c = BRep_Tool::Curve( E, f, l );
1487     if ( c.IsNull() ) return gp_XYZ( Precision::Infinite(), 1e100, 1e100 );
1488     gp_Pnt  p = BRep_Tool::Pnt( fromV );
1489     gp_Pnt pf = c->Value( f ), pl = c->Value( l );
1490     double distF = p.SquareDistance( pf );
1491     double distL = p.SquareDistance( pl );
1492     c->D1(( distF < distL ? f : l), p, dir );
1493     if ( distL < distF ) dir.Reverse();
1494     bool isDifficult = false;
1495     if ( dir.SquareMagnitude() < h0 * h0 ) // check dir orientation
1496     {
1497       gp_Pnt& pClose = distF < distL ? pf : pl;
1498       gp_Pnt&   pFar = distF < distL ? pl : pf;
1499       gp_Pnt    pMid = 0.9 * pClose.XYZ() + 0.1 * pFar.XYZ();
1500       gp_Vec vMid( p, pMid );
1501       double     dot = vMid * dir;
1502       double    cos2 = dot * dot / dir.SquareMagnitude() / vMid.SquareMagnitude();
1503       if ( cos2 < 0.7 * 0.7 || dot < 0 ) // large angle between dir and vMid
1504       {
1505         double uClose = distF < distL ? f : l;
1506         double   uFar = distF < distL ? l : f;
1507         double      r = h0 / SMESH_Algo::EdgeLength( E );
1508         double   uMid = ( 1 - r ) * uClose + r * uFar;
1509         pMid = c->Value( uMid );
1510         dir = gp_Vec( p, pMid );
1511         isDifficult = true;
1512       }
1513     }
1514     if ( isRegularEdge )
1515       *isRegularEdge = !isDifficult;
1516
1517     return dir.XYZ();
1518   }
1519   //--------------------------------------------------------------------------------
1520   gp_XYZ getEdgeDir( const TopoDS_Edge& E, const SMDS_MeshNode* atNode,
1521                      SMESH_MesherHelper& helper)
1522   {
1523     gp_Vec dir;
1524     double f,l; gp_Pnt p;
1525     Handle(Geom_Curve) c = BRep_Tool::Curve( E, f, l );
1526     if ( c.IsNull() ) return gp_XYZ( Precision::Infinite(), 1e100, 1e100 );
1527     double u = helper.GetNodeU( E, atNode );
1528     c->D1( u, p, dir );
1529     return dir.XYZ();
1530   }
1531   //--------------------------------------------------------------------------------
1532   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Vertex& fromV,
1533                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper, bool& ok/*,
1534                      double* cosin=0*/);
1535   //--------------------------------------------------------------------------------
1536   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Edge& fromE,
1537                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper, bool& ok)
1538   {
1539     double f,l;
1540     Handle(Geom_Curve) c = BRep_Tool::Curve( fromE, f, l );
1541     if ( c.IsNull() )
1542     {
1543       TopoDS_Vertex v = helper.IthVertex( 0, fromE );
1544       return getFaceDir( F, v, node, helper, ok );
1545     }
1546     gp_XY uv = helper.GetNodeUV( F, node, 0, &ok );
1547     Handle(Geom_Surface) surface = BRep_Tool::Surface( F );
1548     gp_Pnt p; gp_Vec du, dv, norm;
1549     surface->D1( uv.X(),uv.Y(), p, du,dv );
1550     norm = du ^ dv;
1551
1552     double u = helper.GetNodeU( fromE, node, 0, &ok );
1553     c->D1( u, p, du );
1554     TopAbs_Orientation o = helper.GetSubShapeOri( F.Oriented(TopAbs_FORWARD), fromE);
1555     if ( o == TopAbs_REVERSED )
1556       du.Reverse();
1557
1558     gp_Vec dir = norm ^ du;
1559
1560     if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX &&
1561          helper.IsClosedEdge( fromE ))
1562     {
1563       if ( fabs(u-f) < fabs(u-l)) c->D1( l, p, dv );
1564       else                        c->D1( f, p, dv );
1565       if ( o == TopAbs_REVERSED )
1566         dv.Reverse();
1567       gp_Vec dir2 = norm ^ dv;
1568       dir = dir.Normalized() + dir2.Normalized();
1569     }
1570     return dir.XYZ();
1571   }
1572   //--------------------------------------------------------------------------------
1573   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Vertex& fromV,
1574                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper,
1575                      bool& ok/*, double* cosin*/)
1576   {
1577     TopoDS_Face faceFrw = F;
1578     faceFrw.Orientation( TopAbs_FORWARD );
1579     //double f,l; TopLoc_Location loc;
1580     TopoDS_Edge edges[2]; // sharing a vertex
1581     size_t nbEdges = 0;
1582     {
1583       TopoDS_Vertex VV[2];
1584       TopExp_Explorer exp( faceFrw, TopAbs_EDGE );
1585       for ( ; exp.More() && nbEdges < 2; exp.Next() )
1586       {
1587         const TopoDS_Edge& e = TopoDS::Edge( exp.Current() );
1588         if ( SMESH_Algo::isDegenerated( e )) continue;
1589         TopExp::Vertices( e, VV[0], VV[1], /*CumOri=*/true );
1590         if ( VV[1].IsSame( fromV )) {
1591           nbEdges += edges[ 0 ].IsNull();
1592           edges[ 0 ] = e;
1593         }
1594         else if ( VV[0].IsSame( fromV )) {
1595           nbEdges += edges[ 1 ].IsNull();
1596           edges[ 1 ] = e;
1597         }
1598       }
1599     }
1600     gp_XYZ dir(0,0,0), edgeDir[2];
1601     if ( nbEdges == 2 )
1602     {
1603       // get dirs of edges going fromV
1604       ok = true;
1605       for ( size_t i = 0; i < nbEdges && ok; ++i )
1606       {
1607         edgeDir[i] = getEdgeDir( edges[i], fromV, 0.1 * SMESH_Algo::EdgeLength( edges[i] ));
1608         double size2 = edgeDir[i].SquareModulus();
1609         if (( ok = size2 > numeric_limits<double>::min() ))
1610           edgeDir[i] /= sqrt( size2 );
1611       }
1612       if ( !ok ) return dir;
1613
1614       // get angle between the 2 edges
1615       gp_Vec faceNormal;
1616       double angle = helper.GetAngle( edges[0], edges[1], faceFrw, fromV, &faceNormal );
1617       if ( Abs( angle ) < 5 * M_PI/180 )
1618       {
1619         dir = ( faceNormal.XYZ() ^ edgeDir[0].Reversed()) + ( faceNormal.XYZ() ^ edgeDir[1] );
1620       }
1621       else
1622       {
1623         dir = edgeDir[0] + edgeDir[1];
1624         if ( angle < 0 )
1625           dir.Reverse();
1626       }
1627       // if ( cosin ) {
1628       //   double angle = faceNormal.Angle( dir );
1629       //   *cosin = Cos( angle );
1630       // }
1631     }
1632     else if ( nbEdges == 1 )
1633     {
1634       dir = getFaceDir( faceFrw, edges[ edges[0].IsNull() ], node, helper, ok );
1635       //if ( cosin ) *cosin = 1.;
1636     }
1637     else
1638     {
1639       ok = false;
1640     }
1641
1642     return dir;
1643   }
1644
1645   //================================================================================
1646   /*!
1647    * \brief Finds concave VERTEXes of a FACE
1648    */
1649   //================================================================================
1650
1651   bool getConcaveVertices( const TopoDS_Face&  F,
1652                            SMESH_MesherHelper& helper,
1653                            set< TGeomID >*     vertices = 0)
1654   {
1655     // check angles at VERTEXes
1656     TError error;
1657     TSideVector wires = StdMeshers_FaceSide::GetFaceWires( F, *helper.GetMesh(), 0, error );
1658     for ( size_t iW = 0; iW < wires.size(); ++iW )
1659     {
1660       const int nbEdges = wires[iW]->NbEdges();
1661       if ( nbEdges < 2 && SMESH_Algo::isDegenerated( wires[iW]->Edge(0)))
1662         continue;
1663       for ( int iE1 = 0; iE1 < nbEdges; ++iE1 )
1664       {
1665         if ( SMESH_Algo::isDegenerated( wires[iW]->Edge( iE1 ))) continue;
1666         int iE2 = ( iE1 + 1 ) % nbEdges;
1667         while ( SMESH_Algo::isDegenerated( wires[iW]->Edge( iE2 )))
1668           iE2 = ( iE2 + 1 ) % nbEdges;
1669         TopoDS_Vertex V = wires[iW]->FirstVertex( iE2 );
1670         double angle = helper.GetAngle( wires[iW]->Edge( iE1 ),
1671                                         wires[iW]->Edge( iE2 ), F, V );
1672         if ( angle < -5. * M_PI / 180. )
1673         {
1674           if ( !vertices )
1675             return true;
1676           vertices->insert( helper.GetMeshDS()->ShapeToIndex( V ));
1677         }
1678       }
1679     }
1680     return vertices ? !vertices->empty() : false;
1681   }
1682
1683   //================================================================================
1684   /*!
1685    * \brief Returns true if a FACE is bound by a concave EDGE
1686    */
1687   //================================================================================
1688
1689   bool isConcave( const TopoDS_Face&  F,
1690                   SMESH_MesherHelper& helper,
1691                   set< TGeomID >*     vertices = 0 )
1692   {
1693     bool isConcv = false;
1694     // if ( helper.Count( F, TopAbs_WIRE, /*useMap=*/false) > 1 )
1695     //   return true;
1696     gp_Vec2d drv1, drv2;
1697     gp_Pnt2d p;
1698     TopExp_Explorer eExp( F.Oriented( TopAbs_FORWARD ), TopAbs_EDGE );
1699     for ( ; eExp.More(); eExp.Next() )
1700     {
1701       const TopoDS_Edge& E = TopoDS::Edge( eExp.Current() );
1702       if ( SMESH_Algo::isDegenerated( E )) continue;
1703       // check if 2D curve is concave
1704       BRepAdaptor_Curve2d curve( E, F );
1705       const int nbIntervals = curve.NbIntervals( GeomAbs_C2 );
1706       TColStd_Array1OfReal intervals(1, nbIntervals + 1 );
1707       curve.Intervals( intervals, GeomAbs_C2 );
1708       bool isConvex = true;
1709       for ( int i = 1; i <= nbIntervals && isConvex; ++i )
1710       {
1711         double u1 = intervals( i );
1712         double u2 = intervals( i+1 );
1713         curve.D2( 0.5*( u1+u2 ), p, drv1, drv2 );
1714         double cross = drv1 ^ drv2;
1715         if ( E.Orientation() == TopAbs_REVERSED )
1716           cross = -cross;
1717         isConvex = ( cross > -1e-9 ); // 0.1 );
1718       }
1719       if ( !isConvex )
1720       {
1721         //cout << "Concave FACE " << helper.GetMeshDS()->ShapeToIndex( F ) << endl;
1722         isConcv = true;
1723         if ( vertices )
1724           break;
1725         else
1726           return true;
1727       }
1728     }
1729
1730     // check angles at VERTEXes
1731     if ( getConcaveVertices( F, helper, vertices ))
1732       isConcv = true;
1733
1734     return isConcv;
1735   }
1736
1737   //================================================================================
1738   /*!
1739    * \brief Computes minimal distance of face in-FACE nodes from an EDGE
1740    *  \param [in] face - the mesh face to treat
1741    *  \param [in] nodeOnEdge - a node on the EDGE
1742    *  \param [out] faceSize - the computed distance
1743    *  \return bool - true if faceSize computed
1744    */
1745   //================================================================================
1746
1747   bool getDistFromEdge( const SMDS_MeshElement* face,
1748                         const SMDS_MeshNode*    nodeOnEdge,
1749                         double &                faceSize )
1750   {
1751     faceSize = Precision::Infinite();
1752     bool done = false;
1753
1754     int nbN  = face->NbCornerNodes();
1755     int iOnE = face->GetNodeIndex( nodeOnEdge );
1756     int iNext[2] = { SMESH_MesherHelper::WrapIndex( iOnE+1, nbN ),
1757                      SMESH_MesherHelper::WrapIndex( iOnE-1, nbN ) };
1758     const SMDS_MeshNode* nNext[2] = { face->GetNode( iNext[0] ),
1759                                       face->GetNode( iNext[1] ) };
1760     gp_XYZ segVec, segEnd = SMESH_TNodeXYZ( nodeOnEdge ); // segment on EDGE
1761     double segLen = -1.;
1762     // look for two neighbor not in-FACE nodes of face
1763     for ( int i = 0; i < 2; ++i )
1764     {
1765       if (( nNext[i]->GetPosition()->GetDim() != 2 ) &&
1766           ( nodeOnEdge->GetPosition()->GetDim() == 0 || nNext[i]->GetID() < nodeOnEdge->GetID() ))
1767       {
1768         // look for an in-FACE node
1769         for ( int iN = 0; iN < nbN; ++iN )
1770         {
1771           if ( iN == iOnE || iN == iNext[i] )
1772             continue;
1773           SMESH_TNodeXYZ pInFace = face->GetNode( iN );
1774           gp_XYZ v = pInFace - segEnd;
1775           if ( segLen < 0 )
1776           {
1777             segVec = SMESH_TNodeXYZ( nNext[i] ) - segEnd;
1778             segLen = segVec.Modulus();
1779           }
1780           double distToSeg = v.Crossed( segVec ).Modulus() / segLen;
1781           faceSize = Min( faceSize, distToSeg );
1782           done = true;
1783         }
1784         segLen = -1;
1785       }
1786     }
1787     return done;
1788   }
1789   //================================================================================
1790   /*!
1791    * \brief Return direction of axis or revolution of a surface
1792    */
1793   //================================================================================
1794
1795   bool getRovolutionAxis( const Adaptor3d_Surface& surface,
1796                           gp_Dir &                 axis )
1797   {
1798     switch ( surface.GetType() ) {
1799     case GeomAbs_Cone:
1800     {
1801       gp_Cone cone = surface.Cone();
1802       axis = cone.Axis().Direction();
1803       break;
1804     }
1805     case GeomAbs_Sphere:
1806     {
1807       gp_Sphere sphere = surface.Sphere();
1808       axis = sphere.Position().Direction();
1809       break;
1810     }
1811     case GeomAbs_SurfaceOfRevolution:
1812     {
1813       axis = surface.AxeOfRevolution().Direction();
1814       break;
1815     }
1816     //case GeomAbs_SurfaceOfExtrusion:
1817     case GeomAbs_OffsetSurface:
1818     {
1819       Handle(Adaptor3d_HSurface) base = surface.BasisSurface();
1820       return getRovolutionAxis( base->Surface(), axis );
1821     }
1822     default: return false;
1823     }
1824     return true;
1825   }
1826
1827   //--------------------------------------------------------------------------------
1828   // DEBUG. Dump intermediate node positions into a python script
1829   // HOWTO use: run python commands written in a console and defined in /tmp/viscous.py
1830   // to see construction steps of viscous layers
1831 #ifdef __myDEBUG
1832   ostream* py;
1833   int      theNbPyFunc;
1834   struct PyDump
1835   {
1836     PyDump(SMESH_Mesh& m) {
1837       int tag = 3 + m.GetId();
1838       const char* fname = "/tmp/viscous.py";
1839       cout << "exec(open('"<<fname<<"','rb').read() )"<<endl;
1840       py = _pyStream = new ofstream(fname);
1841       *py << "import SMESH" << endl
1842           << "from salome.smesh import smeshBuilder" << endl
1843           << "smesh  = smeshBuilder.New()" << endl
1844           << "meshSO = salome.myStudy.FindObjectID('0:1:2:" << tag <<"')" << endl
1845           << "mesh   = smesh.Mesh( meshSO.GetObject() )"<<endl;
1846       theNbPyFunc = 0;
1847     }
1848     void Finish() {
1849       if (py) {
1850         *py << "mesh.GroupOnFilter(SMESH.VOLUME,'Viscous Prisms',"
1851           "smesh.GetFilter(SMESH.VOLUME,SMESH.FT_ElemGeomType,'=',SMESH.Geom_PENTA))"<<endl;
1852         *py << "mesh.GroupOnFilter(SMESH.VOLUME,'Neg Volumes',"
1853           "smesh.GetFilter(SMESH.VOLUME,SMESH.FT_Volume3D,'<',0))"<<endl;
1854       }
1855       delete py; py=0;
1856     }
1857     ~PyDump() { Finish(); cout << "NB FUNCTIONS: " << theNbPyFunc << endl; }
1858     struct MyStream : public ostream
1859     {
1860       template <class T> ostream & operator<<( const T &anything ) { return *this ; }
1861     };
1862     void Pause() { py = &_mystream; }
1863     void Resume() { py = _pyStream; }
1864     MyStream _mystream;
1865     ostream* _pyStream;
1866   };
1867 #define dumpFunction(f) { _dumpFunction(f, __LINE__);}
1868 #define dumpMove(n)     { _dumpMove(n, __LINE__);}
1869 #define dumpMoveComm(n,txt) { _dumpMove(n, __LINE__, txt);}
1870 #define dumpCmd(txt)    { _dumpCmd(txt, __LINE__);}
1871   void _dumpFunction(const string& fun, int ln)
1872   { if (py) *py<< "def "<<fun<<"(): # "<< ln <<endl; cout<<fun<<"()"<<endl; ++theNbPyFunc; }
1873   void _dumpMove(const SMDS_MeshNode* n, int ln, const char* txt="")
1874   { if (py) *py<< "  mesh.MoveNode( "<<n->GetID()<< ", "<< n->X()
1875                << ", "<<n->Y()<<", "<< n->Z()<< ")\t\t # "<< ln <<" "<< txt << endl; }
1876   void _dumpCmd(const string& txt, int ln)
1877   { if (py) *py<< "  "<<txt<<" # "<< ln <<endl; }
1878   void dumpFunctionEnd()
1879   { if (py) *py<< "  return"<< endl; }
1880   void dumpChangeNodes( const SMDS_MeshElement* f )
1881   { if (py) { *py<< "  mesh.ChangeElemNodes( " << f->GetID()<<", [";
1882       for ( int i=1; i < f->NbNodes(); ++i ) *py << f->GetNode(i-1)->GetID()<<", ";
1883       *py << f->GetNode( f->NbNodes()-1 )->GetID() << " ])"<< endl; }}
1884 #define debugMsg( txt ) { cout << "# "<< txt << " (line: " << __LINE__ << ")" << endl; }
1885
1886 #else
1887
1888   struct PyDump { PyDump(SMESH_Mesh&) {} void Finish() {} void Pause() {} void Resume() {} };
1889 #define dumpFunction(f) f
1890 #define dumpMove(n)
1891 #define dumpMoveComm(n,txt)
1892 #define dumpCmd(txt)
1893 #define dumpFunctionEnd()
1894 #define dumpChangeNodes(f) { if(f) {} } // prevent "unused variable 'f'" warning
1895 #define debugMsg( txt ) {}
1896
1897 #endif
1898 }
1899
1900 using namespace VISCOUS_3D;
1901
1902 //================================================================================
1903 /*!
1904  * \brief Constructor of _ViscousBuilder
1905  */
1906 //================================================================================
1907
1908 _ViscousBuilder::_ViscousBuilder()
1909 {
1910   _error = SMESH_ComputeError::New(COMPERR_OK);
1911   _tmpFaceID = 0;
1912 }
1913
1914 //================================================================================
1915 /*!
1916  * \brief Stores error description and returns false
1917  */
1918 //================================================================================
1919
1920 bool _ViscousBuilder::error(const string& text, int solidId )
1921 {
1922   const string prefix = string("Viscous layers builder: ");
1923   _error->myName    = COMPERR_ALGO_FAILED;
1924   _error->myComment = prefix + text;
1925   if ( _mesh )
1926   {
1927     SMESH_subMesh* sm = _mesh->GetSubMeshContaining( solidId );
1928     if ( !sm && !_sdVec.empty() )
1929       sm = _mesh->GetSubMeshContaining( solidId = _sdVec[0]._index );
1930     if ( sm && sm->GetSubShape().ShapeType() == TopAbs_SOLID )
1931     {
1932       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1933       if ( smError && smError->myAlgo )
1934         _error->myAlgo = smError->myAlgo;
1935       smError = _error;
1936       sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
1937     }
1938     // set KO to all solids
1939     for ( size_t i = 0; i < _sdVec.size(); ++i )
1940     {
1941       if ( _sdVec[i]._index == solidId )
1942         continue;
1943       sm = _mesh->GetSubMesh( _sdVec[i]._solid );
1944       if ( !sm->IsEmpty() )
1945         continue;
1946       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1947       if ( !smError || smError->IsOK() )
1948       {
1949         smError = SMESH_ComputeError::New( COMPERR_ALGO_FAILED, prefix + "failed");
1950         sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
1951       }
1952     }
1953   }
1954   makeGroupOfLE(); // debug
1955
1956   return false;
1957 }
1958
1959 //================================================================================
1960 /*!
1961  * \brief At study restoration, restore event listeners used to clear an inferior
1962  *  dim sub-mesh modified by viscous layers
1963  */
1964 //================================================================================
1965
1966 void _ViscousBuilder::RestoreListeners()
1967 {
1968   // TODO
1969 }
1970
1971 //================================================================================
1972 /*!
1973  * \brief computes SMESH_ProxyMesh::SubMesh::_n2n
1974  */
1975 //================================================================================
1976
1977 bool _ViscousBuilder::MakeN2NMap( _MeshOfSolid* pm )
1978 {
1979   SMESH_subMesh* solidSM = pm->mySubMeshes.front();
1980   TopExp_Explorer fExp( solidSM->GetSubShape(), TopAbs_FACE );
1981   for ( ; fExp.More(); fExp.Next() )
1982   {
1983     SMESHDS_SubMesh* srcSmDS = pm->GetMeshDS()->MeshElements( fExp.Current() );
1984     const SMESH_ProxyMesh::SubMesh* prxSmDS = pm->GetProxySubMesh( fExp.Current() );
1985
1986     if ( !srcSmDS || !prxSmDS || !srcSmDS->NbElements() || !prxSmDS->NbElements() )
1987       continue;
1988     if ( srcSmDS->GetElements()->next() == prxSmDS->GetElements()->next())
1989       continue;
1990
1991     if ( srcSmDS->NbElements() != prxSmDS->NbElements() )
1992       return error( "Different nb elements in a source and a proxy sub-mesh", solidSM->GetId());
1993
1994     SMDS_ElemIteratorPtr srcIt = srcSmDS->GetElements();
1995     SMDS_ElemIteratorPtr prxIt = prxSmDS->GetElements();
1996     while( prxIt->more() )
1997     {
1998       const SMDS_MeshElement* fSrc = srcIt->next();
1999       const SMDS_MeshElement* fPrx = prxIt->next();
2000       if ( fSrc->NbNodes() != fPrx->NbNodes())
2001         return error( "Different elements in a source and a proxy sub-mesh", solidSM->GetId());
2002       for ( int i = 0 ; i < fPrx->NbNodes(); ++i )
2003         pm->setNode2Node( fSrc->GetNode(i), fPrx->GetNode(i), prxSmDS );
2004     }
2005   }
2006   pm->_n2nMapComputed = true;
2007   return true;
2008 }
2009
2010 //================================================================================
2011 /*!
2012  * \brief Does its job
2013  */
2014 //================================================================================
2015
2016 SMESH_ComputeErrorPtr _ViscousBuilder::Compute(SMESH_Mesh&         theMesh,
2017                                                const TopoDS_Shape& theShape)
2018 {
2019   _mesh = & theMesh;
2020
2021   _Factory factory;
2022
2023   // check if proxy mesh already computed
2024   TopExp_Explorer exp( theShape, TopAbs_SOLID );
2025   if ( !exp.More() )
2026     return error("No SOLID's in theShape"), _error;
2027
2028   if ( _ViscousListener::GetSolidMesh( _mesh, exp.Current(), /*toCreate=*/false))
2029     return SMESH_ComputeErrorPtr(); // everything already computed
2030
2031   // TODO: ignore already computed SOLIDs
2032   if ( !findSolidsWithLayers())
2033     return _error;
2034
2035   if ( !findFacesWithLayers() )
2036     return _error;
2037
2038   if ( !makeEdgesOnShape() )
2039     return _error;
2040
2041   findPeriodicFaces();
2042
2043   PyDump debugDump( theMesh );
2044   _pyDump = &debugDump;
2045
2046
2047   for ( size_t i = 0; i < _sdVec.size(); ++i )
2048   {
2049     size_t iSD = 0;
2050     for ( iSD = 0; iSD < _sdVec.size(); ++iSD ) // find next SOLID to compute
2051       if ( _sdVec[iSD]._before.IsEmpty() &&
2052            !_sdVec[iSD]._solid.IsNull() &&
2053            !_sdVec[iSD]._done )
2054         break;
2055     if ( iSD == _sdVec.size() )
2056       break; // all done
2057
2058     if ( ! makeLayer(_sdVec[iSD]) )   // create _LayerEdge's
2059       return _error;
2060
2061     if ( _sdVec[iSD]._n2eMap.size() == 0 ) // no layers in a SOLID
2062     {
2063       _sdVec[iSD]._solid.Nullify();
2064       continue;
2065     }
2066
2067     if ( ! inflate(_sdVec[iSD]) )     // increase length of _LayerEdge's
2068       return _error;
2069
2070     if ( ! refine(_sdVec[iSD]) )      // create nodes and prisms
2071       return _error;
2072
2073     if ( ! shrink(_sdVec[iSD]) )      // shrink 2D mesh on FACEs w/o layer
2074       return _error;
2075
2076     addBoundaryElements(_sdVec[iSD]); // create quadrangles on prism bare sides
2077
2078     _sdVec[iSD]._done = true;
2079
2080     const TopoDS_Shape& solid = _sdVec[iSD]._solid;
2081     for ( iSD = 0; iSD < _sdVec.size(); ++iSD )
2082       _sdVec[iSD]._before.Remove( solid );
2083   }
2084
2085   makeGroupOfLE(); // debug
2086   debugDump.Finish();
2087
2088   return _error;
2089 }
2090
2091 //================================================================================
2092 /*!
2093  * \brief Check validity of hypotheses
2094  */
2095 //================================================================================
2096
2097 SMESH_ComputeErrorPtr _ViscousBuilder::CheckHypotheses( SMESH_Mesh&         mesh,
2098                                                         const TopoDS_Shape& shape )
2099 {
2100   _mesh = & mesh;
2101
2102   if ( _ViscousListener::GetSolidMesh( _mesh, shape, /*toCreate=*/false))
2103     return SMESH_ComputeErrorPtr(); // everything already computed
2104
2105
2106   findSolidsWithLayers( /*checkFaceMesh=*/false );
2107   bool ok = findFacesWithLayers( true );
2108
2109   // remove _MeshOfSolid's of _SolidData's
2110   for ( size_t i = 0; i < _sdVec.size(); ++i )
2111     _ViscousListener::RemoveSolidMesh( _mesh, _sdVec[i]._solid );
2112
2113   if ( !ok )
2114     return _error;
2115
2116   return SMESH_ComputeErrorPtr();
2117 }
2118
2119 //================================================================================
2120 /*!
2121  * \brief Finds SOLIDs to compute using viscous layers. Fills _sdVec
2122  */
2123 //================================================================================
2124
2125 bool _ViscousBuilder::findSolidsWithLayers(const bool checkFaceMesh)
2126 {
2127   // get all solids
2128   TopTools_IndexedMapOfShape allSolids;
2129   TopExp::MapShapes( _mesh->GetShapeToMesh(), TopAbs_SOLID, allSolids );
2130   _sdVec.reserve( allSolids.Extent());
2131
2132   SMESH_HypoFilter filter;
2133   for ( int i = 1; i <= allSolids.Extent(); ++i )
2134   {
2135     SMESH_subMesh* sm = _mesh->GetSubMesh( allSolids(i) );
2136     if ( sm->GetSubMeshDS() && sm->GetSubMeshDS()->NbElements() > 0 )
2137       continue; // solid is already meshed
2138     // TODO: check if algo is hidden
2139     SMESH_Algo* algo = sm->GetAlgo();
2140     if ( !algo ) continue;
2141     // check if all FACEs are meshed, which can be false if Compute() a sub-shape
2142     if ( checkFaceMesh )
2143     {
2144       bool facesMeshed = true;
2145       SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(false,true);
2146       while ( smIt->more() && facesMeshed )
2147       {
2148         SMESH_subMesh * faceSM = smIt->next();
2149         if ( faceSM->GetSubShape().ShapeType() != TopAbs_FACE )
2150           break;
2151         facesMeshed = faceSM->IsMeshComputed();
2152       }
2153       if ( !facesMeshed )
2154         continue;
2155     }
2156     // find StdMeshers_ViscousLayers hyp assigned to the i-th solid
2157     const list <const SMESHDS_Hypothesis *> & allHyps =
2158       algo->GetUsedHypothesis(*_mesh, allSolids(i), /*ignoreAuxiliary=*/false);
2159     _SolidData* soData = 0;
2160     list< const SMESHDS_Hypothesis *>::const_iterator hyp = allHyps.begin();
2161     const StdMeshers_ViscousLayers* viscHyp = 0;
2162     for ( ; hyp != allHyps.end(); ++hyp )
2163       if (( viscHyp = dynamic_cast<const StdMeshers_ViscousLayers*>( *hyp )))
2164       {
2165         TopoDS_Shape hypShape;
2166         filter.Init( filter.Is( viscHyp ));
2167         _mesh->GetHypothesis( allSolids(i), filter, true, &hypShape );
2168
2169         if ( !soData )
2170         {
2171           _MeshOfSolid* proxyMesh = _ViscousListener::GetSolidMesh( _mesh,
2172                                                                     allSolids(i),
2173                                                                     /*toCreate=*/true);
2174           _sdVec.push_back( _SolidData( allSolids(i), proxyMesh ));
2175           soData = & _sdVec.back();
2176           soData->_index = getMeshDS()->ShapeToIndex( allSolids(i));
2177           soData->_helper = new SMESH_MesherHelper( *_mesh );
2178           soData->_helper->SetSubShape( allSolids(i) );
2179           _solids.Add( allSolids(i) );
2180         }
2181         soData->_hyps.push_back( viscHyp );
2182         soData->_hypShapes.push_back( hypShape );
2183       }
2184   }
2185   if ( _sdVec.empty() )
2186     return error
2187       ( SMESH_Comment(StdMeshers_ViscousLayers::GetHypType()) << " hypothesis not found",0);
2188
2189   return true;
2190 }
2191
2192 //================================================================================
2193 /*!
2194  * \brief Set a _SolidData to be computed before another
2195  */
2196 //================================================================================
2197
2198 bool _ViscousBuilder::setBefore( _SolidData& solidBefore, _SolidData& solidAfter )
2199 {
2200   // check possibility to set this order; get all solids before solidBefore
2201   TopTools_IndexedMapOfShape allSolidsBefore;
2202   allSolidsBefore.Add( solidBefore._solid );
2203   for ( int i = 1; i <= allSolidsBefore.Extent(); ++i )
2204   {
2205     int iSD = _solids.FindIndex( allSolidsBefore(i) );
2206     if ( iSD )
2207     {
2208       TopTools_MapIteratorOfMapOfShape soIt( _sdVec[ iSD-1 ]._before );
2209       for ( ; soIt.More(); soIt.Next() )
2210         allSolidsBefore.Add( soIt.Value() );
2211     }
2212   }
2213   if ( allSolidsBefore.Contains( solidAfter._solid ))
2214     return false;
2215
2216   for ( int i = 1; i <= allSolidsBefore.Extent(); ++i )
2217     solidAfter._before.Add( allSolidsBefore(i) );
2218
2219   return true;
2220 }
2221
2222 //================================================================================
2223 /*!
2224  * \brief
2225  */
2226 //================================================================================
2227
2228 bool _ViscousBuilder::findFacesWithLayers(const bool onlyWith)
2229 {
2230   SMESH_MesherHelper helper( *_mesh );
2231   TopExp_Explorer exp;
2232
2233   // collect all faces-to-ignore defined by hyp
2234   for ( size_t i = 0; i < _sdVec.size(); ++i )
2235   {
2236     // get faces-to-ignore defined by each hyp
2237     typedef const StdMeshers_ViscousLayers* THyp;
2238     typedef std::pair< set<TGeomID>, THyp > TFacesOfHyp;
2239     list< TFacesOfHyp > ignoreFacesOfHyps;
2240     list< THyp >::iterator              hyp = _sdVec[i]._hyps.begin();
2241     list< TopoDS_Shape >::iterator hypShape = _sdVec[i]._hypShapes.begin();
2242     for ( ; hyp != _sdVec[i]._hyps.end(); ++hyp, ++hypShape )
2243     {
2244       ignoreFacesOfHyps.push_back( TFacesOfHyp( set<TGeomID>(), *hyp ));
2245       getIgnoreFaces( _sdVec[i]._solid, *hyp, *hypShape, ignoreFacesOfHyps.back().first );
2246     }
2247
2248     // fill _SolidData::_face2hyp and check compatibility of hypotheses
2249     const int nbHyps = _sdVec[i]._hyps.size();
2250     if ( nbHyps > 1 )
2251     {
2252       // check if two hypotheses define different parameters for the same FACE
2253       list< TFacesOfHyp >::iterator igFacesOfHyp;
2254       for ( exp.Init( _sdVec[i]._solid, TopAbs_FACE ); exp.More(); exp.Next() )
2255       {
2256         const TGeomID faceID = getMeshDS()->ShapeToIndex( exp.Current() );
2257         THyp hyp = 0;
2258         igFacesOfHyp = ignoreFacesOfHyps.begin();
2259         for ( ; igFacesOfHyp != ignoreFacesOfHyps.end(); ++igFacesOfHyp )
2260           if ( ! igFacesOfHyp->first.count( faceID ))
2261           {
2262             if ( hyp )
2263               return error(SMESH_Comment("Several hypotheses define "
2264                                          "Viscous Layers on the face #") << faceID );
2265             hyp = igFacesOfHyp->second;
2266           }
2267         if ( hyp )
2268           _sdVec[i]._face2hyp.insert( make_pair( faceID, hyp ));
2269         else
2270           _sdVec[i]._ignoreFaceIds.insert( faceID );
2271       }
2272
2273       // check if two hypotheses define different number of viscous layers for
2274       // adjacent faces of a solid
2275       set< int > nbLayersSet;
2276       igFacesOfHyp = ignoreFacesOfHyps.begin();
2277       for ( ; igFacesOfHyp != ignoreFacesOfHyps.end(); ++igFacesOfHyp )
2278       {
2279         nbLayersSet.insert( igFacesOfHyp->second->GetNumberLayers() );
2280       }
2281       if ( nbLayersSet.size() > 1 )
2282       {
2283         for ( exp.Init( _sdVec[i]._solid, TopAbs_EDGE ); exp.More(); exp.Next() )
2284         {
2285           PShapeIteratorPtr fIt = helper.GetAncestors( exp.Current(), *_mesh, TopAbs_FACE );
2286           THyp hyp1 = 0, hyp2 = 0;
2287           while( const TopoDS_Shape* face = fIt->next() )
2288           {
2289             const TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
2290             map< TGeomID, THyp >::iterator f2h = _sdVec[i]._face2hyp.find( faceID );
2291             if ( f2h != _sdVec[i]._face2hyp.end() )
2292             {
2293               ( hyp1 ? hyp2 : hyp1 ) = f2h->second;
2294             }
2295           }
2296           if ( hyp1 && hyp2 &&
2297                hyp1->GetNumberLayers() != hyp2->GetNumberLayers() )
2298           {
2299             return error("Two hypotheses define different number of "
2300                          "viscous layers on adjacent faces");
2301           }
2302         }
2303       }
2304     } // if ( nbHyps > 1 )
2305     else
2306     {
2307       _sdVec[i]._ignoreFaceIds.swap( ignoreFacesOfHyps.back().first );
2308     }
2309   } // loop on _sdVec
2310
2311   if ( onlyWith ) // is called to check hypotheses compatibility only
2312     return true;
2313
2314   // fill _SolidData::_reversedFaceIds
2315   for ( size_t i = 0; i < _sdVec.size(); ++i )
2316   {
2317     exp.Init( _sdVec[i]._solid.Oriented( TopAbs_FORWARD ), TopAbs_FACE );
2318     for ( ; exp.More(); exp.Next() )
2319     {
2320       const TopoDS_Face& face = TopoDS::Face( exp.Current() );
2321       const TGeomID    faceID = getMeshDS()->ShapeToIndex( face );
2322       if ( //!sdVec[i]._ignoreFaceIds.count( faceID ) &&
2323           helper.NbAncestors( face, *_mesh, TopAbs_SOLID ) > 1 &&
2324           helper.IsReversedSubMesh( face ))
2325       {
2326         _sdVec[i]._reversedFaceIds.insert( faceID );
2327       }
2328     }
2329   }
2330
2331   // Find FACEs to shrink mesh on (solution 2 in issue 0020832): fill in _shrinkShape2Shape
2332   TopTools_IndexedMapOfShape shapes;
2333   std::string structAlgoName = "Hexa_3D";
2334   for ( size_t i = 0; i < _sdVec.size(); ++i )
2335   {
2336     shapes.Clear();
2337     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_EDGE, shapes);
2338     for ( int iE = 1; iE <= shapes.Extent(); ++iE )
2339     {
2340       const TopoDS_Shape& edge = shapes(iE);
2341       // find 2 FACEs sharing an EDGE
2342       TopoDS_Shape FF[2];
2343       PShapeIteratorPtr fIt = helper.GetAncestors(edge, *_mesh, TopAbs_FACE, &_sdVec[i]._solid);
2344       while ( fIt->more())
2345       {
2346         const TopoDS_Shape* f = fIt->next();
2347         FF[ int( !FF[0].IsNull()) ] = *f;
2348       }
2349       if( FF[1].IsNull() ) continue; // seam edge can be shared by 1 FACE only
2350
2351       // check presence of layers on them
2352       int ignore[2];
2353       for ( int j = 0; j < 2; ++j )
2354         ignore[j] = _sdVec[i]._ignoreFaceIds.count( getMeshDS()->ShapeToIndex( FF[j] ));
2355       if ( ignore[0] == ignore[1] )
2356         continue; // nothing interesting
2357       TopoDS_Shape fWOL = FF[ ignore[0] ? 0 : 1 ]; // FACE w/o layers
2358
2359       // add EDGE to maps
2360       if ( !fWOL.IsNull())
2361       {
2362         TGeomID edgeInd = getMeshDS()->ShapeToIndex( edge );
2363         _sdVec[i]._shrinkShape2Shape.insert( make_pair( edgeInd, fWOL ));
2364       }
2365     }
2366   }
2367
2368   // Find the SHAPE along which to inflate _LayerEdge based on VERTEX
2369
2370   for ( size_t i = 0; i < _sdVec.size(); ++i )
2371   {
2372     shapes.Clear();
2373     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_VERTEX, shapes);
2374     for ( int iV = 1; iV <= shapes.Extent(); ++iV )
2375     {
2376       const TopoDS_Shape& vertex = shapes(iV);
2377       // find faces WOL sharing the vertex
2378       vector< TopoDS_Shape > facesWOL;
2379       size_t totalNbFaces = 0;
2380       PShapeIteratorPtr fIt = helper.GetAncestors(vertex, *_mesh, TopAbs_FACE, &_sdVec[i]._solid );
2381       while ( fIt->more())
2382       {
2383         const TopoDS_Shape* f = fIt->next();
2384         totalNbFaces++;
2385         const int fID = getMeshDS()->ShapeToIndex( *f );
2386         if ( _sdVec[i]._ignoreFaceIds.count ( fID ) /*&& !_sdVec[i]._noShrinkShapes.count( fID )*/)
2387           facesWOL.push_back( *f );
2388       }
2389       if ( facesWOL.size() == totalNbFaces || facesWOL.empty() )
2390         continue; // no layers at this vertex or no WOL
2391       TGeomID vInd = getMeshDS()->ShapeToIndex( vertex );
2392       switch ( facesWOL.size() )
2393       {
2394       case 1:
2395       {
2396         helper.SetSubShape( facesWOL[0] );
2397         if ( helper.IsRealSeam( vInd )) // inflate along a seam edge?
2398         {
2399           TopoDS_Shape seamEdge;
2400           PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE);
2401           while ( eIt->more() && seamEdge.IsNull() )
2402           {
2403             const TopoDS_Shape* e = eIt->next();
2404             if ( helper.IsRealSeam( *e ) )
2405               seamEdge = *e;
2406           }
2407           if ( !seamEdge.IsNull() )
2408           {
2409             _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, seamEdge ));
2410             break;
2411           }
2412         }
2413         _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, facesWOL[0] ));
2414         break;
2415       }
2416       case 2:
2417       {
2418         // find an edge shared by 2 faces
2419         PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE);
2420         while ( eIt->more())
2421         {
2422           const TopoDS_Shape* e = eIt->next();
2423           if ( helper.IsSubShape( *e, facesWOL[0]) &&
2424                helper.IsSubShape( *e, facesWOL[1]))
2425           {
2426             _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, *e )); break;
2427           }
2428         }
2429         break;
2430       }
2431       default:
2432         return error("Not yet supported case", _sdVec[i]._index);
2433       }
2434     }
2435   }
2436
2437   // Add to _noShrinkShapes sub-shapes of FACE's that can't be shrunk since
2438   // the algo of the SOLID sharing the FACE does not support it or for other reasons
2439   set< string > notSupportAlgos; notSupportAlgos.insert( structAlgoName );
2440   for ( size_t i = 0; i < _sdVec.size(); ++i )
2441   {
2442     map< TGeomID, TopoDS_Shape >::iterator e2f = _sdVec[i]._shrinkShape2Shape.begin();
2443     for ( ; e2f != _sdVec[i]._shrinkShape2Shape.end(); ++e2f )
2444     {
2445       const TopoDS_Shape& fWOL = e2f->second;
2446       const TGeomID     edgeID = e2f->first;
2447       TGeomID           faceID = getMeshDS()->ShapeToIndex( fWOL );
2448       TopoDS_Shape        edge = getMeshDS()->IndexToShape( edgeID );
2449       if ( edge.ShapeType() != TopAbs_EDGE )
2450         continue; // shrink shape is VERTEX
2451
2452       TopoDS_Shape solid;
2453       PShapeIteratorPtr soIt = helper.GetAncestors(fWOL, *_mesh, TopAbs_SOLID);
2454       while ( soIt->more() && solid.IsNull() )
2455       {
2456         const TopoDS_Shape* so = soIt->next();
2457         if ( !so->IsSame( _sdVec[i]._solid ))
2458           solid = *so;
2459       }
2460       if ( solid.IsNull() )
2461         continue;
2462
2463       bool noShrinkE = false;
2464       SMESH_Algo*  algo = _mesh->GetSubMesh( solid )->GetAlgo();
2465       bool isStructured = ( algo && algo->GetName() == structAlgoName );
2466       size_t     iSolid = _solids.FindIndex( solid ) - 1;
2467       if ( iSolid < _sdVec.size() && _sdVec[ iSolid ]._ignoreFaceIds.count( faceID ))
2468       {
2469         // the adjacent SOLID has NO layers on fWOL;
2470         // shrink allowed if
2471         // - there are layers on the EDGE in the adjacent SOLID
2472         // - there are NO layers in the adjacent SOLID && algo is unstructured and computed later
2473         bool hasWLAdj = (_sdVec[iSolid]._shrinkShape2Shape.count( edgeID ));
2474         bool shrinkAllowed = (( hasWLAdj ) ||
2475                               ( !isStructured && setBefore( _sdVec[ i ], _sdVec[ iSolid ] )));
2476         noShrinkE = !shrinkAllowed;
2477       }
2478       else if ( iSolid < _sdVec.size() )
2479       {
2480         // the adjacent SOLID has layers on fWOL;
2481         // check if SOLID's mesh is unstructured and then try to set it
2482         // to be computed after the i-th solid
2483         if ( isStructured || !setBefore( _sdVec[ i ], _sdVec[ iSolid ] ))
2484           noShrinkE = true; // don't shrink fWOL
2485       }
2486       else
2487       {
2488         // the adjacent SOLID has NO layers at all
2489         noShrinkE = isStructured;
2490       }
2491
2492       if ( noShrinkE )
2493       {
2494         _sdVec[i]._noShrinkShapes.insert( edgeID );
2495
2496         // check if there is a collision with to-shrink-from EDGEs in iSolid
2497         // if ( iSolid < _sdVec.size() )
2498         // {
2499         //   shapes.Clear();
2500         //   TopExp::MapShapes( fWOL, TopAbs_EDGE, shapes);
2501         //   for ( int iE = 1; iE <= shapes.Extent(); ++iE )
2502         //   {
2503         //     const TopoDS_Edge& E = TopoDS::Edge( shapes( iE ));
2504         //     const TGeomID    eID = getMeshDS()->ShapeToIndex( E );
2505         //     if ( eID == edgeID ||
2506         //          !_sdVec[iSolid]._shrinkShape2Shape.count( eID ) ||
2507         //          _sdVec[i]._noShrinkShapes.count( eID ))
2508         //       continue;
2509         //     for ( int is1st = 0; is1st < 2; ++is1st )
2510         //     {
2511         //       TopoDS_Vertex V = helper.IthVertex( is1st, E );
2512         //       if ( _sdVec[i]._noShrinkShapes.count( getMeshDS()->ShapeToIndex( V ) ))
2513         //       {
2514         //         return error("No way to make a conformal mesh with "
2515         //                      "the given set of faces with layers", _sdVec[i]._index);
2516         //       }
2517         //     }
2518         //   }
2519         // }
2520       }
2521
2522       // add VERTEXes of the edge in _noShrinkShapes, which is necessary if
2523       // _shrinkShape2Shape is different in the adjacent SOLID
2524       for ( TopoDS_Iterator vIt( edge ); vIt.More(); vIt.Next() )
2525       {
2526         TGeomID vID = getMeshDS()->ShapeToIndex( vIt.Value() );
2527         bool noShrinkV = false, noShrinkIfAdjMeshed = false;
2528
2529         if ( iSolid < _sdVec.size() )
2530         {
2531           if ( _sdVec[ iSolid ]._ignoreFaceIds.count( faceID ))
2532           {
2533             map< TGeomID, TopoDS_Shape >::iterator i2S, i2SAdj;
2534             i2S    = _sdVec[i     ]._shrinkShape2Shape.find( vID );
2535             i2SAdj = _sdVec[iSolid]._shrinkShape2Shape.find( vID );
2536             if ( i2SAdj == _sdVec[iSolid]._shrinkShape2Shape.end() )
2537               noShrinkV = (( isStructured ) ||
2538                            ( noShrinkIfAdjMeshed = i2S->second.ShapeType() == TopAbs_EDGE ));
2539             else
2540               noShrinkV = ( ! i2S->second.IsSame( i2SAdj->second ));
2541           }
2542           else
2543           {
2544             noShrinkV = noShrinkE;
2545           }
2546         }
2547         else
2548         {
2549           // the adjacent SOLID has NO layers at all
2550           if ( isStructured )
2551           {
2552             noShrinkV = true;
2553           }
2554           else
2555           {
2556             noShrinkV = noShrinkIfAdjMeshed =
2557               ( _sdVec[i]._shrinkShape2Shape[ vID ].ShapeType() == TopAbs_EDGE );
2558           }
2559         }
2560
2561         if ( noShrinkV && noShrinkIfAdjMeshed )
2562         {
2563           // noShrinkV if FACEs in the adjacent SOLID are meshed
2564           PShapeIteratorPtr fIt = helper.GetAncestors( _sdVec[i]._shrinkShape2Shape[ vID ],
2565                                                        *_mesh, TopAbs_FACE, &solid );
2566           while ( fIt->more() )
2567           {
2568             const TopoDS_Shape* f = fIt->next();
2569             if ( !f->IsSame( fWOL ))
2570             {
2571               noShrinkV = ! _mesh->GetSubMesh( *f )->IsEmpty();
2572               break;
2573             }
2574           }
2575         }
2576         if ( noShrinkV )
2577           _sdVec[i]._noShrinkShapes.insert( vID );
2578       }
2579
2580     } // loop on _sdVec[i]._shrinkShape2Shape
2581   } // loop on _sdVec to fill in _SolidData::_noShrinkShapes
2582
2583
2584     // add FACEs of other SOLIDs to _ignoreFaceIds
2585   for ( size_t i = 0; i < _sdVec.size(); ++i )
2586   {
2587     shapes.Clear();
2588     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_FACE, shapes);
2589
2590     for ( exp.Init( _mesh->GetShapeToMesh(), TopAbs_FACE ); exp.More(); exp.Next() )
2591     {
2592       if ( !shapes.Contains( exp.Current() ))
2593         _sdVec[i]._ignoreFaceIds.insert( getMeshDS()->ShapeToIndex( exp.Current() ));
2594     }
2595   }
2596
2597   return true;
2598 }
2599
2600 //================================================================================
2601 /*!
2602  * \brief Finds FACEs w/o layers for a given SOLID by an hypothesis
2603  */
2604 //================================================================================
2605
2606 void _ViscousBuilder::getIgnoreFaces(const TopoDS_Shape&             solid,
2607                                      const StdMeshers_ViscousLayers* hyp,
2608                                      const TopoDS_Shape&             hypShape,
2609                                      set<TGeomID>&                   ignoreFaceIds)
2610 {
2611   TopExp_Explorer exp;
2612
2613   vector<TGeomID> ids = hyp->GetBndShapes();
2614   if ( hyp->IsToIgnoreShapes() ) // FACEs to ignore are given
2615   {
2616     for ( size_t ii = 0; ii < ids.size(); ++ii )
2617     {
2618       const TopoDS_Shape& s = getMeshDS()->IndexToShape( ids[ii] );
2619       if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2620         ignoreFaceIds.insert( ids[ii] );
2621     }
2622   }
2623   else // FACEs with layers are given
2624   {
2625     exp.Init( solid, TopAbs_FACE );
2626     for ( ; exp.More(); exp.Next() )
2627     {
2628       TGeomID faceInd = getMeshDS()->ShapeToIndex( exp.Current() );
2629       if ( find( ids.begin(), ids.end(), faceInd ) == ids.end() )
2630         ignoreFaceIds.insert( faceInd );
2631     }
2632   }
2633
2634   // ignore internal FACEs if inlets and outlets are specified
2635   if ( hyp->IsToIgnoreShapes() )
2636   {
2637     TopTools_IndexedDataMapOfShapeListOfShape solidsOfFace;
2638     TopExp::MapShapesAndAncestors( hypShape,
2639                                    TopAbs_FACE, TopAbs_SOLID, solidsOfFace);
2640
2641     for ( exp.Init( solid, TopAbs_FACE ); exp.More(); exp.Next() )
2642     {
2643       const TopoDS_Face& face = TopoDS::Face( exp.Current() );
2644       if ( SMESH_MesherHelper::NbAncestors( face, *_mesh, TopAbs_SOLID ) < 2 )
2645         continue;
2646
2647       int nbSolids = solidsOfFace.FindFromKey( face ).Extent();
2648       if ( nbSolids > 1 )
2649         ignoreFaceIds.insert( getMeshDS()->ShapeToIndex( face ));
2650     }
2651   }
2652 }
2653
2654 //================================================================================
2655 /*!
2656  * \brief Create the inner surface of the viscous layer and prepare data for infation
2657  */
2658 //================================================================================
2659
2660 bool _ViscousBuilder::makeLayer(_SolidData& data)
2661 {
2662   // make a map to find new nodes on sub-shapes shared with other SOLID
2663   map< TGeomID, TNode2Edge* >::iterator s2ne;
2664   map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
2665   for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
2666   {
2667     TGeomID shapeInd = s2s->first;
2668     for ( size_t i = 0; i < _sdVec.size(); ++i )
2669     {
2670       if ( _sdVec[i]._index == data._index ) continue;
2671       map< TGeomID, TopoDS_Shape >::iterator s2s2 = _sdVec[i]._shrinkShape2Shape.find( shapeInd );
2672       if ( s2s2 != _sdVec[i]._shrinkShape2Shape.end() &&
2673            *s2s == *s2s2 && !_sdVec[i]._n2eMap.empty() )
2674       {
2675         data._s2neMap.insert( make_pair( shapeInd, &_sdVec[i]._n2eMap ));
2676         break;
2677       }
2678     }
2679   }
2680
2681   // Create temporary faces and _LayerEdge's
2682
2683   debugMsg( "######################" );
2684   dumpFunction(SMESH_Comment("makeLayers_")<<data._index);
2685
2686   vector< _EdgesOnShape >& edgesByGeom = data._edgesOnShape;
2687
2688   data._stepSize = Precision::Infinite();
2689   data._stepSizeNodes[0] = 0;
2690
2691   SMESH_MesherHelper helper( *_mesh );
2692   helper.SetSubShape( data._solid );
2693   helper.SetElementsOnShape( true );
2694
2695   vector< const SMDS_MeshNode*> newNodes; // of a mesh face
2696   TNode2Edge::iterator n2e2;
2697
2698   // make _LayerEdge's
2699   for ( TopExp_Explorer exp( data._solid, TopAbs_FACE ); exp.More(); exp.Next() )
2700   {
2701     const TopoDS_Face& F = TopoDS::Face( exp.Current() );
2702     SMESH_subMesh*    sm = _mesh->GetSubMesh( F );
2703     const TGeomID     id = sm->GetId();
2704     if ( edgesByGeom[ id ]._shape.IsNull() )
2705       continue; // no layers
2706     SMESH_ProxyMesh::SubMesh* proxySub =
2707       data._proxyMesh->getFaceSubM( F, /*create=*/true);
2708
2709     SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
2710     if ( !smDS ) return error(SMESH_Comment("Not meshed face ") << id, data._index );
2711
2712     SMDS_ElemIteratorPtr eIt = smDS->GetElements();
2713     while ( eIt->more() )
2714     {
2715       const SMDS_MeshElement* face = eIt->next();
2716       double          faceMaxCosin = -1;
2717       _LayerEdge*     maxCosinEdge = 0;
2718       int             nbDegenNodes = 0;
2719
2720       newNodes.resize( face->NbCornerNodes() );
2721       for ( size_t i = 0 ; i < newNodes.size(); ++i )
2722       {
2723         const SMDS_MeshNode* n = face->GetNode( i );
2724         const int      shapeID = n->getshapeId();
2725         const bool onDegenShap = helper.IsDegenShape( shapeID );
2726         const bool onDegenEdge = ( onDegenShap && n->GetPosition()->GetDim() == 1 );
2727         if ( onDegenShap )
2728         {
2729           if ( onDegenEdge )
2730           {
2731             // substitute n on a degenerated EDGE with a node on a corresponding VERTEX
2732             const TopoDS_Shape& E = getMeshDS()->IndexToShape( shapeID );
2733             TopoDS_Vertex       V = helper.IthVertex( 0, TopoDS::Edge( E ));
2734             if ( const SMDS_MeshNode* vN = SMESH_Algo::VertexNode( V, getMeshDS() )) {
2735               n = vN;
2736               nbDegenNodes++;
2737             }
2738           }
2739           else
2740           {
2741             nbDegenNodes++;
2742           }
2743         }
2744         TNode2Edge::iterator n2e = data._n2eMap.insert({ n, nullptr }).first;
2745         if ( !(*n2e).second )
2746         {
2747           // add a _LayerEdge
2748           _LayerEdge* edge = _Factory::NewLayerEdge();
2749           edge->_nodes.push_back( n );
2750           n2e->second = edge;
2751           edgesByGeom[ shapeID ]._edges.push_back( edge );
2752           const bool noShrink = data._noShrinkShapes.count( shapeID );
2753
2754           SMESH_TNodeXYZ xyz( n );
2755
2756           // set edge data or find already refined _LayerEdge and get data from it
2757           if (( !noShrink                                                     ) &&
2758               ( n->GetPosition()->GetTypeOfPosition() != SMDS_TOP_FACE        ) &&
2759               (( s2ne = data._s2neMap.find( shapeID )) != data._s2neMap.end() ) &&
2760               (( n2e2 = (*s2ne).second->find( n )) != s2ne->second->end()     ))
2761           {
2762             _LayerEdge* foundEdge = (*n2e2).second;
2763             gp_XYZ        lastPos = edge->Copy( *foundEdge, edgesByGeom[ shapeID ], helper );
2764             foundEdge->_pos.push_back( lastPos );
2765             // location of the last node is modified and we restore it by foundEdge->_pos.back()
2766             const_cast< SMDS_MeshNode* >
2767               ( edge->_nodes.back() )->setXYZ( xyz.X(), xyz.Y(), xyz.Z() );
2768           }
2769           else
2770           {
2771             if ( !noShrink )
2772             {
2773               edge->_nodes.push_back( helper.AddNode( xyz.X(), xyz.Y(), xyz.Z() ));
2774             }
2775             if ( !setEdgeData( *edge, edgesByGeom[ shapeID ], helper, data ))
2776               return false;
2777
2778             if ( edge->_nodes.size() < 2 && !noShrink )
2779               edge->Block( data ); // a sole node is moved only if noShrink
2780           }
2781           dumpMove(edge->_nodes.back());
2782
2783           if ( edge->_cosin > faceMaxCosin && edge->_nodes.size() > 1 )
2784           {
2785             faceMaxCosin = edge->_cosin;
2786             maxCosinEdge = edge;
2787           }
2788         }
2789         newNodes[ i ] = n2e->second->_nodes.back();
2790
2791         if ( onDegenEdge )
2792           data._n2eMap.insert( make_pair( face->GetNode( i ), n2e->second ));
2793       }
2794       if ( newNodes.size() - nbDegenNodes < 2 )
2795         continue;
2796
2797       // create a temporary face
2798       const SMDS_MeshElement* newFace =
2799         new _TmpMeshFace( newNodes, --_tmpFaceID, face->GetShapeID(), face );
2800       proxySub->AddElement( newFace );
2801
2802       // compute inflation step size by min size of element on a convex surface
2803       if ( faceMaxCosin > theMinSmoothCosin )
2804         limitStepSize( data, face, maxCosinEdge );
2805
2806     } // loop on 2D elements on a FACE
2807   } // loop on FACEs of a SOLID to create _LayerEdge's
2808
2809
2810   // Set _LayerEdge::_neibors
2811   TNode2Edge::iterator n2e;
2812   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
2813   {
2814     _EdgesOnShape& eos = data._edgesOnShape[iS];
2815     for ( size_t i = 0; i < eos._edges.size(); ++i )
2816     {
2817       _LayerEdge* edge = eos._edges[i];
2818       TIDSortedNodeSet nearNodes;
2819       SMDS_ElemIteratorPtr fIt = edge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
2820       while ( fIt->more() )
2821       {
2822         const SMDS_MeshElement* f = fIt->next();
2823         if ( !data._ignoreFaceIds.count( f->getshapeId() ))
2824           nearNodes.insert( f->begin_nodes(), f->end_nodes() );
2825       }
2826       nearNodes.erase( edge->_nodes[0] );
2827       edge->_neibors.reserve( nearNodes.size() );
2828       TIDSortedNodeSet::iterator node = nearNodes.begin();
2829       for ( ; node != nearNodes.end(); ++node )
2830         if (( n2e = data._n2eMap.find( *node )) != data._n2eMap.end() )
2831           edge->_neibors.push_back( n2e->second );
2832     }
2833
2834     // Fix uv of nodes on periodic FACEs (bos #20643)
2835
2836     if ( eos.ShapeType() != TopAbs_EDGE ||
2837          eos.SWOLType()  != TopAbs_FACE ||
2838          eos.size() == 0 )
2839       continue;
2840
2841     const TopoDS_Face& F = TopoDS::Face( eos._sWOL );
2842     SMESH_MesherHelper faceHelper( *_mesh );
2843     faceHelper.SetSubShape( F );
2844     faceHelper.ToFixNodeParameters( true );
2845     if ( faceHelper.GetPeriodicIndex() == 0 )
2846       continue;
2847
2848     SMESHDS_SubMesh* smDS = getMeshDS()->MeshElements( F );
2849     if ( !smDS || smDS->GetNodes() == 0 )
2850       continue;
2851
2852     bool toCheck = true;
2853     const double tol = 2 * helper.MaxTolerance( F );
2854     for ( SMDS_NodeIteratorPtr nIt = smDS->GetNodes(); nIt->more(); )
2855     {
2856       const SMDS_MeshNode* node = nIt->next();
2857       gp_XY uvNew( Precision::Infinite(), 0 );
2858       if ( toCheck )
2859       {
2860         toCheck = false;
2861         gp_XY uv = faceHelper.GetNodeUV( F, node );
2862         if ( ! faceHelper.CheckNodeUV( F, node, uvNew, tol, /*force=*/true ))
2863           break; // projection on F failed
2864         if (( uv - uvNew ).Modulus() < Precision::Confusion() )
2865           break; // current uv is OK
2866       }
2867       faceHelper.CheckNodeUV( F, node, uvNew, tol, /*force=*/true );
2868     }
2869   }
2870
2871   data._epsilon = 1e-7;
2872   if ( data._stepSize < 1. )
2873     data._epsilon *= data._stepSize;
2874
2875   if ( !findShapesToSmooth( data )) // _LayerEdge::_maxLen is computed here
2876     return false;
2877
2878   // limit data._stepSize depending on surface curvature and fill data._convexFaces
2879   limitStepSizeByCurvature( data ); // !!! it must be before node substitution in _Simplex
2880
2881   // Set target nodes into _Simplex and _LayerEdge's to _2NearEdges
2882   const SMDS_MeshNode* nn[2];
2883   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
2884   {
2885     _EdgesOnShape& eos = data._edgesOnShape[iS];
2886     for ( size_t i = 0; i < eos._edges.size(); ++i )
2887     {
2888       _LayerEdge* edge = eos._edges[i];
2889       if ( edge->IsOnEdge() )
2890       {
2891         // get neighbor nodes
2892         bool hasData = ( edge->_2neibors->_edges[0] );
2893         if ( hasData ) // _LayerEdge is a copy of another one
2894         {
2895           nn[0] = edge->_2neibors->srcNode(0);
2896           nn[1] = edge->_2neibors->srcNode(1);
2897         }
2898         else if ( !findNeiborsOnEdge( edge, nn[0],nn[1], eos, data ))
2899         {
2900           return false;
2901         }
2902         // set neighbor _LayerEdge's
2903         for ( int j = 0; j < 2; ++j )
2904         {
2905           if (( n2e = data._n2eMap.find( nn[j] )) == data._n2eMap.end() )
2906             return error("_LayerEdge not found by src node", data._index);
2907           edge->_2neibors->_edges[j] = n2e->second;
2908         }
2909         if ( !hasData )
2910           edge->SetDataByNeighbors( nn[0], nn[1], eos, helper );
2911       }
2912
2913       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
2914       {
2915         _Simplex& s = edge->_simplices[j];
2916         s._nNext = data._n2eMap[ s._nNext ]->_nodes.back();
2917         s._nPrev = data._n2eMap[ s._nPrev ]->_nodes.back();
2918       }
2919
2920       // For an _LayerEdge on a degenerated EDGE, copy some data from
2921       // a corresponding _LayerEdge on a VERTEX
2922       // (issue 52453, pb on a downloaded SampleCase2-Tet-netgen-mephisto.hdf)
2923       if ( helper.IsDegenShape( edge->_nodes[0]->getshapeId() ))
2924       {
2925         // Generally we should not get here
2926         if ( eos.ShapeType() != TopAbs_EDGE )
2927           continue;
2928         TopoDS_Vertex V = helper.IthVertex( 0, TopoDS::Edge( eos._shape ));
2929         const SMDS_MeshNode* vN = SMESH_Algo::VertexNode( V, getMeshDS() );
2930         if (( n2e = data._n2eMap.find( vN )) == data._n2eMap.end() )
2931           continue;
2932         const _LayerEdge* vEdge = n2e->second;
2933         edge->_normal    = vEdge->_normal;
2934         edge->_lenFactor = vEdge->_lenFactor;
2935         edge->_cosin     = vEdge->_cosin;
2936       }
2937
2938     } // loop on data._edgesOnShape._edges
2939   } // loop on data._edgesOnShape
2940
2941   // fix _LayerEdge::_2neibors on EDGEs to smooth
2942   // map< TGeomID,Handle(Geom_Curve)>::iterator e2c = data._edge2curve.begin();
2943   // for ( ; e2c != data._edge2curve.end(); ++e2c )
2944   //   if ( !e2c->second.IsNull() )
2945   //   {
2946   //     if ( _EdgesOnShape* eos = data.GetShapeEdges( e2c->first ))
2947   //       data.Sort2NeiborsOnEdge( eos->_edges );
2948   //   }
2949
2950   dumpFunctionEnd();
2951   return true;
2952 }
2953
2954 //================================================================================
2955 /*!
2956  * \brief Compute inflation step size by min size of element on a convex surface
2957  */
2958 //================================================================================
2959
2960 void _ViscousBuilder::limitStepSize( _SolidData&             data,
2961                                      const SMDS_MeshElement* face,
2962                                      const _LayerEdge*       maxCosinEdge )
2963 {
2964   int iN = 0;
2965   double minSize = 10 * data._stepSize;
2966   const int nbNodes = face->NbCornerNodes();
2967   for ( int i = 0; i < nbNodes; ++i )
2968   {
2969     const SMDS_MeshNode* nextN = face->GetNode( SMESH_MesherHelper::WrapIndex( i+1, nbNodes ));
2970     const SMDS_MeshNode*  curN = face->GetNode( i );
2971     if ( nextN->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ||
2972          curN-> GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2973     {
2974       double dist = SMESH_TNodeXYZ( curN ).Distance( nextN );
2975       if ( dist < minSize )
2976         minSize = dist, iN = i;
2977     }
2978   }
2979   double newStep = 0.8 * minSize / maxCosinEdge->_lenFactor;
2980   if ( newStep < data._stepSize )
2981   {
2982     data._stepSize = newStep;
2983     data._stepSizeCoeff = 0.8 / maxCosinEdge->_lenFactor;
2984     data._stepSizeNodes[0] = face->GetNode( iN );
2985     data._stepSizeNodes[1] = face->GetNode( SMESH_MesherHelper::WrapIndex( iN+1, nbNodes ));
2986   }
2987 }
2988
2989 //================================================================================
2990 /*!
2991  * \brief Compute inflation step size by min size of element on a convex surface
2992  */
2993 //================================================================================
2994
2995 void _ViscousBuilder::limitStepSize( _SolidData& data, const double minSize )
2996 {
2997   if ( minSize < data._stepSize )
2998   {
2999     data._stepSize = minSize;
3000     if ( data._stepSizeNodes[0] )
3001     {
3002       double dist =
3003         SMESH_TNodeXYZ(data._stepSizeNodes[0]).Distance(data._stepSizeNodes[1]);
3004       data._stepSizeCoeff = data._stepSize / dist;
3005     }
3006   }
3007 }
3008
3009 //================================================================================
3010 /*!
3011  * \brief Limit data._stepSize by evaluating curvature of shapes and fill data._convexFaces
3012  */
3013 //================================================================================
3014
3015 void _ViscousBuilder::limitStepSizeByCurvature( _SolidData& data )
3016 {
3017   SMESH_MesherHelper helper( *_mesh );
3018
3019   BRepLProp_SLProps surfProp( 2, 1e-6 );
3020   data._convexFaces.clear();
3021
3022   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
3023   {
3024     _EdgesOnShape& eof = data._edgesOnShape[iS];
3025     if ( eof.ShapeType() != TopAbs_FACE ||
3026          data._ignoreFaceIds.count( eof._shapeID ))
3027       continue;
3028
3029     TopoDS_Face        F = TopoDS::Face( eof._shape );
3030     const TGeomID faceID = eof._shapeID;
3031
3032     BRepAdaptor_Surface surface( F, false );
3033     surfProp.SetSurface( surface );
3034
3035     _ConvexFace cnvFace;
3036     cnvFace._face = F;
3037     cnvFace._normalsFixed = false;
3038     cnvFace._isTooCurved = false;
3039
3040     double maxCurvature = cnvFace.GetMaxCurvature( data, eof, surfProp, helper );
3041     if ( maxCurvature > 0 )
3042     {
3043       limitStepSize( data, 0.9 / maxCurvature );
3044       findEdgesToUpdateNormalNearConvexFace( cnvFace, data, helper );
3045     }
3046     if ( !cnvFace._isTooCurved ) continue;
3047
3048     _ConvexFace & convFace =
3049       data._convexFaces.insert( make_pair( faceID, cnvFace )).first->second;
3050
3051     // skip a closed surface (data._convexFaces is useful anyway)
3052     bool isClosedF = false;
3053     helper.SetSubShape( F );
3054     if ( helper.HasRealSeam() )
3055     {
3056       // in the closed surface there must be a closed EDGE
3057       for ( TopExp_Explorer eIt( F, TopAbs_EDGE ); eIt.More() && !isClosedF; eIt.Next() )
3058         isClosedF = helper.IsClosedEdge( TopoDS::Edge( eIt.Current() ));
3059     }
3060     if ( isClosedF )
3061     {
3062       // limit _LayerEdge::_maxLen on the FACE
3063       const double oriFactor    = ( F.Orientation() == TopAbs_REVERSED ? +1. : -1. );
3064       const double minCurvature =
3065         1. / ( eof._hyp.GetTotalThickness() * ( 1 + theThickToIntersection ));
3066       map< TGeomID, _EdgesOnShape* >::iterator id2eos = cnvFace._subIdToEOS.find( faceID );
3067       if ( id2eos != cnvFace._subIdToEOS.end() )
3068       {
3069         _EdgesOnShape& eos = * id2eos->second;
3070         for ( size_t i = 0; i < eos._edges.size(); ++i )
3071         {
3072           _LayerEdge* ledge = eos._edges[ i ];
3073           gp_XY uv = helper.GetNodeUV( F, ledge->_nodes[0] );
3074           surfProp.SetParameters( uv.X(), uv.Y() );
3075           if ( surfProp.IsCurvatureDefined() )
3076           {
3077             double curvature = Max( surfProp.MaxCurvature() * oriFactor,
3078                                     surfProp.MinCurvature() * oriFactor );
3079             if ( curvature > minCurvature )
3080               ledge->SetMaxLen( Min( ledge->_maxLen, 1. / curvature ));
3081           }
3082         }
3083       }
3084       continue;
3085     }
3086
3087     // Fill _ConvexFace::_simplexTestEdges. These _LayerEdge's are used to detect
3088     // prism distortion.
3089     map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.find( faceID );
3090     if ( id2eos != convFace._subIdToEOS.end() && !id2eos->second->_edges.empty() )
3091     {
3092       // there are _LayerEdge's on the FACE it-self;
3093       // select _LayerEdge's near EDGEs
3094       _EdgesOnShape& eos = * id2eos->second;
3095       for ( size_t i = 0; i < eos._edges.size(); ++i )
3096       {
3097         _LayerEdge* ledge = eos._edges[ i ];
3098         for ( size_t j = 0; j < ledge->_simplices.size(); ++j )
3099           if ( ledge->_simplices[j]._nNext->GetPosition()->GetDim() < 2 )
3100           {
3101             // do not select _LayerEdge's neighboring sharp EDGEs
3102             bool sharpNbr = false;
3103             for ( size_t iN = 0; iN < ledge->_neibors.size()  && !sharpNbr; ++iN )
3104               sharpNbr = ( ledge->_neibors[iN]->_cosin > theMinSmoothCosin );
3105             if ( !sharpNbr )
3106               convFace._simplexTestEdges.push_back( ledge );
3107             break;
3108           }
3109       }
3110     }
3111     else
3112     {
3113       // where there are no _LayerEdge's on a _ConvexFace,
3114       // as e.g. on a fillet surface with no internal nodes - issue 22580,
3115       // so that collision of viscous internal faces is not detected by check of
3116       // intersection of _LayerEdge's with the viscous internal faces.
3117
3118       set< const SMDS_MeshNode* > usedNodes;
3119
3120       // look for _LayerEdge's with null _sWOL
3121       id2eos = convFace._subIdToEOS.begin();
3122       for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
3123       {
3124         _EdgesOnShape& eos = * id2eos->second;
3125         if ( !eos._sWOL.IsNull() )
3126           continue;
3127         for ( size_t i = 0; i < eos._edges.size(); ++i )
3128         {
3129           _LayerEdge* ledge = eos._edges[ i ];
3130           const SMDS_MeshNode* srcNode = ledge->_nodes[0];
3131           if ( !usedNodes.insert( srcNode ).second ) continue;
3132
3133           for ( size_t i = 0; i < ledge->_simplices.size(); ++i )
3134           {
3135             usedNodes.insert( ledge->_simplices[i]._nPrev );
3136             usedNodes.insert( ledge->_simplices[i]._nNext );
3137           }
3138           convFace._simplexTestEdges.push_back( ledge );
3139         }
3140       }
3141     }
3142   } // loop on FACEs of data._solid
3143 }
3144
3145 //================================================================================
3146 /*!
3147  * \brief Detect shapes (and _LayerEdge's on them) to smooth
3148  */
3149 //================================================================================
3150
3151 bool _ViscousBuilder::findShapesToSmooth( _SolidData& data )
3152 {
3153   // define allowed thickness
3154   computeGeomSize( data ); // compute data._geomSize and _LayerEdge::_maxLen
3155
3156
3157   // Find shapes needing smoothing; such a shape has _LayerEdge._normal on it's
3158   // boundary inclined to the shape at a sharp angle
3159
3160   TopTools_MapOfShape edgesOfSmooFaces;
3161   SMESH_MesherHelper helper( *_mesh );
3162   bool ok = true;
3163
3164   vector< _EdgesOnShape >& edgesByGeom = data._edgesOnShape;
3165   data._nbShapesToSmooth = 0;
3166
3167   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check FACEs
3168   {
3169     _EdgesOnShape& eos = edgesByGeom[iS];
3170     eos._toSmooth = false;
3171     if ( eos._edges.empty() || eos.ShapeType() != TopAbs_FACE )
3172       continue;
3173
3174     double tgtThick = eos._hyp.GetTotalThickness();
3175     SMESH_subMeshIteratorPtr subIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false );
3176     while ( subIt->more() && !eos._toSmooth )
3177     {
3178       TGeomID iSub = subIt->next()->GetId();
3179       const vector<_LayerEdge*>& eSub = edgesByGeom[ iSub ]._edges;
3180       if ( eSub.empty() ) continue;
3181
3182       double faceSize;
3183       for ( size_t i = 0; i < eSub.size() && !eos._toSmooth; ++i )
3184         if ( eSub[i]->_cosin > theMinSmoothCosin )
3185         {
3186           SMDS_ElemIteratorPtr fIt = eSub[i]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
3187           while ( fIt->more() && !eos._toSmooth )
3188           {
3189             const SMDS_MeshElement* face = fIt->next();
3190             if ( face->getshapeId() == eos._shapeID &&
3191                  getDistFromEdge( face, eSub[i]->_nodes[0], faceSize ))
3192             {
3193               eos._toSmooth = needSmoothing( eSub[i]->_cosin,
3194                                              tgtThick * eSub[i]->_lenFactor,
3195                                              faceSize);
3196             }
3197           }
3198         }
3199     }
3200     if ( eos._toSmooth )
3201     {
3202       for ( TopExp_Explorer eExp( edgesByGeom[iS]._shape, TopAbs_EDGE ); eExp.More(); eExp.Next() )
3203         edgesOfSmooFaces.Add( eExp.Current() );
3204
3205       data.PrepareEdgesToSmoothOnFace( &edgesByGeom[iS], /*substituteSrcNodes=*/false );
3206     }
3207     data._nbShapesToSmooth += eos._toSmooth;
3208
3209   }  // check FACEs
3210
3211   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check EDGEs
3212   {
3213     _EdgesOnShape& eos = edgesByGeom[iS];
3214     eos._edgeSmoother = NULL;
3215     if ( eos._edges.empty() || eos.ShapeType() != TopAbs_EDGE ) continue;
3216     if ( !eos._hyp.ToSmooth() ) continue;
3217
3218     const TopoDS_Edge& E = TopoDS::Edge( edgesByGeom[iS]._shape );
3219     if ( SMESH_Algo::isDegenerated( E ) || !edgesOfSmooFaces.Contains( E ))
3220       continue;
3221
3222     double tgtThick = eos._hyp.GetTotalThickness(), h0 = eos._hyp.Get1stLayerThickness();
3223     for ( TopoDS_Iterator vIt( E ); vIt.More() && !eos._toSmooth; vIt.Next() )
3224     {
3225       TGeomID iV = getMeshDS()->ShapeToIndex( vIt.Value() );
3226       vector<_LayerEdge*>& eV = edgesByGeom[ iV ]._edges;
3227       if ( eV.empty() || eV[0]->Is( _LayerEdge::MULTI_NORMAL )) continue;
3228       gp_Vec  eDir    = getEdgeDir( E, TopoDS::Vertex( vIt.Value() ), h0 );
3229       double angle    = eDir.Angle( eV[0]->_normal );
3230       double cosin    = Cos( angle );
3231       double cosinAbs = Abs( cosin );
3232       if ( cosinAbs > theMinSmoothCosin )
3233       {
3234         // always smooth analytic EDGEs
3235         Handle(Geom_Curve) curve = _Smoother1D::CurveForSmooth( E, eos, helper );
3236         eos._toSmooth = ! curve.IsNull();
3237
3238         // compare tgtThick with the length of an end segment
3239         SMDS_ElemIteratorPtr eIt = eV[0]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Edge);
3240         while ( eIt->more() && !eos._toSmooth )
3241         {
3242           const SMDS_MeshElement* endSeg = eIt->next();
3243           if ( endSeg->getshapeId() == (int) iS )
3244           {
3245             double segLen =
3246               SMESH_TNodeXYZ( endSeg->GetNode( 0 )).Distance( endSeg->GetNode( 1 ));
3247             eos._toSmooth = needSmoothing( cosinAbs, tgtThick * eV[0]->_lenFactor, segLen );
3248           }
3249         }
3250         if ( eos._toSmooth )
3251         {
3252           eos._edgeSmoother = new _Smoother1D( curve, eos );
3253
3254           // for ( size_t i = 0; i < eos._edges.size(); ++i )
3255           //   eos._edges[i]->Set( _LayerEdge::TO_SMOOTH );
3256         }
3257       }
3258     }
3259     data._nbShapesToSmooth += eos._toSmooth;
3260
3261   } // check EDGEs
3262
3263   // Reset _cosin if no smooth is allowed by the user
3264   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS )
3265   {
3266     _EdgesOnShape& eos = edgesByGeom[iS];
3267     if ( eos._edges.empty() ) continue;
3268
3269     if ( !eos._hyp.ToSmooth() )
3270       for ( size_t i = 0; i < eos._edges.size(); ++i )
3271         //eos._edges[i]->SetCosin( 0 ); // keep _cosin to use in limitMaxLenByCurvature()
3272         eos._edges[i]->_lenFactor = 1;
3273   }
3274
3275
3276   // Fill _eosC1 to make that C1 FACEs and EDGEs between them to be smoothed as a whole
3277
3278   TopTools_MapOfShape c1VV;
3279
3280   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check FACEs
3281   {
3282     _EdgesOnShape& eos = edgesByGeom[iS];
3283     if ( eos._edges.empty() ||
3284          eos.ShapeType() != TopAbs_FACE ||
3285          !eos._toSmooth )
3286       continue;
3287
3288     // check EDGEs of a FACE
3289     TopTools_MapOfShape checkedEE, allVV;
3290     list< SMESH_subMesh* > smQueue( 1, eos._subMesh ); // sm of FACEs
3291     while ( !smQueue.empty() )
3292     {
3293       SMESH_subMesh* sm = smQueue.front();
3294       smQueue.pop_front();
3295       SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(/*includeSelf=*/false);
3296       while ( smIt->more() )
3297       {
3298         sm = smIt->next();
3299         if ( sm->GetSubShape().ShapeType() == TopAbs_VERTEX )
3300           allVV.Add( sm->GetSubShape() );
3301         if ( sm->GetSubShape().ShapeType() != TopAbs_EDGE ||
3302              !checkedEE.Add( sm->GetSubShape() ))
3303           continue;
3304
3305         _EdgesOnShape*      eoe = data.GetShapeEdges( sm->GetId() );
3306         vector<_LayerEdge*>& eE = eoe->_edges;
3307         if ( eE.empty() || !eoe->_sWOL.IsNull() )
3308           continue;
3309
3310         bool isC1 = true; // check continuity along an EDGE
3311         for ( size_t i = 0; i < eE.size() && isC1; ++i )
3312           isC1 = ( Abs( eE[i]->_cosin ) < theMinSmoothCosin );
3313         if ( !isC1 )
3314           continue;
3315
3316         // check that mesh faces are C1 as well
3317         {
3318           gp_XYZ norm1, norm2;
3319           const SMDS_MeshNode*   n = eE[ eE.size() / 2 ]->_nodes[0];
3320           SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
3321           if ( !SMESH_MeshAlgos::FaceNormal( fIt->next(), norm1, /*normalized=*/true ))
3322             continue;
3323           while ( fIt->more() && isC1 )
3324             isC1 = ( SMESH_MeshAlgos::FaceNormal( fIt->next(), norm2, /*normalized=*/true ) &&
3325                      Abs( norm1 * norm2 ) >= ( 1. - theMinSmoothCosin ));
3326           if ( !isC1 )
3327             continue;
3328         }
3329
3330         // add the EDGE and an adjacent FACE to _eosC1
3331         PShapeIteratorPtr fIt = helper.GetAncestors( sm->GetSubShape(), *_mesh, TopAbs_FACE );
3332         while ( const TopoDS_Shape* face = fIt->next() )
3333         {
3334           _EdgesOnShape* eof = data.GetShapeEdges( *face );
3335           if ( !eof ) continue; // other solid
3336           if ( eos._shapeID == eof->_shapeID ) continue;
3337           if ( !eos.HasC1( eof ))
3338           {
3339             // check the FACEs
3340             eos._eosC1.push_back( eof );
3341             eof->_toSmooth = false;
3342             data.PrepareEdgesToSmoothOnFace( eof, /*substituteSrcNodes=*/false );
3343             smQueue.push_back( eof->_subMesh );
3344           }
3345           if ( !eos.HasC1( eoe ))
3346           {
3347             eos._eosC1.push_back( eoe );
3348             eoe->_toSmooth = false;
3349             data.PrepareEdgesToSmoothOnFace( eoe, /*substituteSrcNodes=*/false );
3350           }
3351         }
3352       }
3353     }
3354     if ( eos._eosC1.empty() )
3355       continue;
3356
3357     // check VERTEXes of C1 FACEs
3358     TopTools_MapIteratorOfMapOfShape vIt( allVV );
3359     for ( ; vIt.More(); vIt.Next() )
3360     {
3361       _EdgesOnShape* eov = data.GetShapeEdges( vIt.Key() );
3362       if ( !eov || eov->_edges.empty() || !eov->_sWOL.IsNull() )
3363         continue;
3364
3365       bool isC1 = true; // check if all adjacent FACEs are in eos._eosC1
3366       PShapeIteratorPtr fIt = helper.GetAncestors( vIt.Key(), *_mesh, TopAbs_FACE );
3367       while ( const TopoDS_Shape* face = fIt->next() )
3368       {
3369         _EdgesOnShape* eof = data.GetShapeEdges( *face );
3370         if ( !eof ) continue; // other solid
3371         isC1 = ( face->IsSame( eos._shape ) || eos.HasC1( eof ));
3372         if ( !isC1 )
3373           break;
3374       }
3375       if ( isC1 )
3376       {
3377         eos._eosC1.push_back( eov );
3378         data.PrepareEdgesToSmoothOnFace( eov, /*substituteSrcNodes=*/false );
3379         c1VV.Add( eov->_shape );
3380       }
3381     }
3382
3383   } // fill _eosC1 of FACEs
3384
3385
3386   // Find C1 EDGEs
3387
3388   vector< pair< _EdgesOnShape*, gp_XYZ > > dirOfEdges;
3389
3390   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check VERTEXes
3391   {
3392     _EdgesOnShape& eov = edgesByGeom[iS];
3393     if ( eov._edges.empty() ||
3394          eov.ShapeType() != TopAbs_VERTEX ||
3395          c1VV.Contains( eov._shape ))
3396       continue;
3397     const TopoDS_Vertex& V = TopoDS::Vertex( eov._shape );
3398
3399     // get directions of surrounding EDGEs
3400     dirOfEdges.clear();
3401     PShapeIteratorPtr fIt = helper.GetAncestors( eov._shape, *_mesh, TopAbs_EDGE );
3402     while ( const TopoDS_Shape* e = fIt->next() )
3403     {
3404       _EdgesOnShape* eoe = data.GetShapeEdges( *e );
3405       if ( !eoe ) continue; // other solid
3406       gp_XYZ eDir = getEdgeDir( TopoDS::Edge( *e ), V, eoe->_hyp.Get1stLayerThickness() );
3407       if ( !Precision::IsInfinite( eDir.X() ))
3408         dirOfEdges.push_back( make_pair( eoe, eDir.Normalized() ));
3409     }
3410
3411     // find EDGEs with C1 directions
3412     for ( size_t i = 0; i < dirOfEdges.size(); ++i )
3413       for ( size_t j = i+1; j < dirOfEdges.size(); ++j )
3414         if ( dirOfEdges[i].first && dirOfEdges[j].first )
3415         {
3416           double dot = dirOfEdges[i].second * dirOfEdges[j].second;
3417           bool isC1 = ( dot < - ( 1. - theMinSmoothCosin ));
3418           if ( isC1 )
3419           {
3420             double maxEdgeLen = 3 * Min( eov._edges[0]->_maxLen, eov._hyp.GetTotalThickness() );
3421             for ( int isJ = 0; isJ < 2; ++isJ ) // loop on [i,j]
3422             {
3423               size_t k = isJ ? j : i;
3424               const TopoDS_Edge& e = TopoDS::Edge( dirOfEdges[k].first->_shape );
3425               double eLen = SMESH_Algo::EdgeLength( e );
3426               if ( eLen < maxEdgeLen )
3427               {
3428                 TopoDS_Shape oppV = SMESH_MesherHelper::IthVertex( 0, e );
3429                 if ( oppV.IsSame( V ))
3430                   oppV = SMESH_MesherHelper::IthVertex( 1, e );
3431                 _EdgesOnShape* eovOpp = data.GetShapeEdges( oppV );
3432                 if ( dirOfEdges[k].second * eovOpp->_edges[0]->_normal < 0 )
3433                   eov._eosC1.push_back( dirOfEdges[k].first );
3434               }
3435               dirOfEdges[k].first = 0;
3436             }
3437           }
3438         }
3439   } // fill _eosC1 of VERTEXes
3440
3441
3442
3443   return ok;
3444 }
3445
3446 //================================================================================
3447 /*!
3448  * \brief Set up _SolidData::_edgesOnShape
3449  */
3450 //================================================================================
3451
3452 int _ViscousBuilder::makeEdgesOnShape()
3453 {
3454   const int nbShapes = getMeshDS()->MaxShapeIndex();
3455   int nbSolidsWL = 0;
3456
3457   for ( size_t i = 0; i < _sdVec.size(); ++i )
3458   {
3459     _SolidData& data = _sdVec[ i ];
3460     vector< _EdgesOnShape >& edgesByGeom = data._edgesOnShape;
3461     edgesByGeom.resize( nbShapes+1 );
3462
3463     // set data of _EdgesOnShape's
3464     int nbShapesWL = 0;
3465     if ( SMESH_subMesh* sm = _mesh->GetSubMesh( data._solid ))
3466     {
3467       SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(/*includeSelf=*/false);
3468       while ( smIt->more() )
3469       {
3470         sm = smIt->next();
3471         if ( sm->GetSubShape().ShapeType() == TopAbs_FACE &&
3472              data._ignoreFaceIds.count( sm->GetId() ))
3473           continue;
3474
3475         setShapeData( edgesByGeom[ sm->GetId() ], sm, data );
3476
3477         nbShapesWL += ( sm->GetSubShape().ShapeType() == TopAbs_FACE );
3478       }
3479     }
3480     if ( nbShapesWL == 0 ) // no shapes with layers in a SOLID
3481     {
3482       data._done = true;
3483       SMESHUtils::FreeVector( edgesByGeom );
3484     }
3485     else
3486     {
3487       ++nbSolidsWL;
3488     }
3489   }
3490   return nbSolidsWL;
3491 }
3492
3493 //================================================================================
3494 /*!
3495  * \brief initialize data of _EdgesOnShape
3496  */
3497 //================================================================================
3498
3499 void _ViscousBuilder::setShapeData( _EdgesOnShape& eos,
3500                                     SMESH_subMesh* sm,
3501                                     _SolidData&    data )
3502 {
3503   if ( !eos._shape.IsNull() ||
3504        sm->GetSubShape().ShapeType() == TopAbs_WIRE )
3505     return;
3506
3507   SMESH_MesherHelper helper( *_mesh );
3508
3509   eos._subMesh = sm;
3510   eos._shapeID = sm->GetId();
3511   eos._shape   = sm->GetSubShape();
3512   if ( eos.ShapeType() == TopAbs_FACE )
3513     eos._shape.Orientation( helper.GetSubShapeOri( data._solid, eos._shape ));
3514   eos._toSmooth = false;
3515   eos._data = &data;
3516   eos._mapper2D = nullptr;
3517
3518   // set _SWOL
3519   map< TGeomID, TopoDS_Shape >::const_iterator s2s =
3520     data._shrinkShape2Shape.find( eos._shapeID );
3521   if ( s2s != data._shrinkShape2Shape.end() )
3522     eos._sWOL = s2s->second;
3523
3524   eos._isRegularSWOL = true;
3525   if ( eos.SWOLType() == TopAbs_FACE )
3526   {
3527     const TopoDS_Face& F = TopoDS::Face( eos._sWOL );
3528     Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( F );
3529     eos._isRegularSWOL = ( ! surface->HasSingularities( 1e-7 ));
3530   }
3531
3532   // set _hyp
3533   if ( data._hyps.size() == 1 )
3534   {
3535     eos._hyp = data._hyps.back();
3536   }
3537   else
3538   {
3539     // compute average StdMeshers_ViscousLayers parameters
3540     map< TGeomID, const StdMeshers_ViscousLayers* >::iterator f2hyp;
3541     if ( eos.ShapeType() == TopAbs_FACE )
3542     {
3543       if (( f2hyp = data._face2hyp.find( eos._shapeID )) != data._face2hyp.end() )
3544         eos._hyp = f2hyp->second;
3545     }
3546     else
3547     {
3548       PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
3549       while ( const TopoDS_Shape* face = fIt->next() )
3550       {
3551         TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
3552         if (( f2hyp = data._face2hyp.find( faceID )) != data._face2hyp.end() )
3553           eos._hyp.Add( f2hyp->second );
3554       }
3555     }
3556   }
3557
3558   // set _faceNormals
3559   if ( ! eos._hyp.UseSurfaceNormal() )
3560   {
3561     if ( eos.ShapeType() == TopAbs_FACE ) // get normals to elements on a FACE
3562     {
3563       SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
3564       if ( !smDS ) return;
3565       eos._faceNormals.reserve( smDS->NbElements() );
3566
3567       double oriFactor = helper.IsReversedSubMesh( TopoDS::Face( eos._shape )) ? 1.: -1.;
3568       SMDS_ElemIteratorPtr eIt = smDS->GetElements();
3569       for ( ; eIt->more(); )
3570       {
3571         const SMDS_MeshElement* face = eIt->next();
3572         gp_XYZ&                 norm = eos._faceNormals[face];
3573         if ( !SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
3574           norm.SetCoord( 0,0,0 );
3575         norm *= oriFactor;
3576       }
3577     }
3578     else // find EOS of adjacent FACEs
3579     {
3580       PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
3581       while ( const TopoDS_Shape* face = fIt->next() )
3582       {
3583         TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
3584         eos._faceEOS.push_back( & data._edgesOnShape[ faceID ]);
3585         if ( eos._faceEOS.back()->_shape.IsNull() )
3586           // avoid using uninitialised _shapeID in GetNormal()
3587           eos._faceEOS.back()->_shapeID = faceID;
3588       }
3589     }
3590   }
3591 }
3592
3593 //================================================================================
3594 /*!
3595  * \brief Returns normal of a face
3596  */
3597 //================================================================================
3598
3599 bool _EdgesOnShape::GetNormal( const SMDS_MeshElement* face, gp_Vec& norm )
3600 {
3601   bool ok = false;
3602   _EdgesOnShape* eos = 0;
3603
3604   if ( face->getshapeId() == _shapeID )
3605   {
3606     eos = this;
3607   }
3608   else
3609   {
3610     for ( size_t iF = 0; iF < _faceEOS.size() && !eos; ++iF )
3611       if ( face->getshapeId() == _faceEOS[ iF ]->_shapeID )
3612         eos = _faceEOS[ iF ];
3613   }
3614
3615   if (( eos ) &&
3616       ( ok = ( eos->_faceNormals.count( face ) )))
3617   {
3618     norm = eos->_faceNormals[ face ];
3619   }
3620   else if ( !eos )
3621   {
3622     debugMsg( "_EdgesOnShape::Normal() failed for face "<<face->GetID()
3623               << " on _shape #" << _shapeID );
3624   }
3625   return ok;
3626 }
3627
3628 //================================================================================
3629 /*!
3630  * \brief EdgesOnShape destructor
3631  */
3632 //================================================================================
3633
3634 _EdgesOnShape::~_EdgesOnShape()
3635 {
3636   delete _edgeSmoother;
3637   delete _mapper2D;
3638 }
3639
3640 //================================================================================
3641 /*!
3642  * \brief Set data of _LayerEdge needed for smoothing
3643  */
3644 //================================================================================
3645
3646 bool _ViscousBuilder::setEdgeData(_LayerEdge&         edge,
3647                                   _EdgesOnShape&      eos,
3648                                   SMESH_MesherHelper& helper,
3649                                   _SolidData&         data)
3650 {
3651   const SMDS_MeshNode* node = edge._nodes[0]; // source node
3652
3653   edge._len          = 0;
3654   edge._maxLen       = Precision::Infinite();
3655   edge._minAngle     = 0;
3656   edge._2neibors     = 0;
3657   edge._curvature    = 0;
3658   edge._flags        = 0;
3659   edge._smooFunction = 0;
3660
3661   // --------------------------
3662   // Compute _normal and _cosin
3663   // --------------------------
3664
3665   edge._cosin     = 0;
3666   edge._lenFactor = 1.;
3667   edge._normal.SetCoord(0,0,0);
3668   _Simplex::GetSimplices( node, edge._simplices, data._ignoreFaceIds, &data );
3669
3670   int totalNbFaces = 0;
3671   TopoDS_Face F;
3672   std::pair< TopoDS_Face, gp_XYZ > face2Norm[20];
3673   gp_Vec geomNorm;
3674   bool normOK = true;
3675
3676   const bool onShrinkShape = !eos._sWOL.IsNull();
3677   const bool useGeometry   = (( eos._hyp.UseSurfaceNormal() ) ||
3678                               ( eos.ShapeType() != TopAbs_FACE /*&& !onShrinkShape*/ ));
3679
3680   // get geom FACEs the node lies on
3681   //if ( useGeometry )
3682   {
3683     set<TGeomID> faceIds;
3684     if  ( eos.ShapeType() == TopAbs_FACE )
3685     {
3686       faceIds.insert( eos._shapeID );
3687     }
3688     else
3689     {
3690       SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
3691       while ( fIt->more() )
3692         faceIds.insert( fIt->next()->getshapeId() );
3693     }
3694     set<TGeomID>::iterator id = faceIds.begin();
3695     for ( ; id != faceIds.end(); ++id )
3696     {
3697       const TopoDS_Shape& s = getMeshDS()->IndexToShape( *id );
3698       if ( s.IsNull() || s.ShapeType() != TopAbs_FACE || data._ignoreFaceIds.count( *id ))
3699         continue;
3700       F = TopoDS::Face( s );
3701       face2Norm[ totalNbFaces ].first = F;
3702       totalNbFaces++;
3703     }
3704   }
3705
3706   // find _normal
3707   bool fromVonF = false;
3708   if ( useGeometry )
3709   {
3710     fromVonF = ( eos.ShapeType() == TopAbs_VERTEX &&
3711                  eos.SWOLType()  == TopAbs_FACE  &&
3712                  totalNbFaces > 1 );
3713
3714     if ( onShrinkShape && !fromVonF ) // one of faces the node is on has no layers
3715     {
3716       if ( eos.SWOLType() == TopAbs_EDGE )
3717       {
3718         // inflate from VERTEX along EDGE
3719         edge._normal = getEdgeDir( TopoDS::Edge( eos._sWOL ), TopoDS::Vertex( eos._shape ),
3720                                    eos._hyp.Get1stLayerThickness(), &eos._isRegularSWOL );
3721       }
3722       else if ( eos.ShapeType() == TopAbs_VERTEX )
3723       {
3724         // inflate from VERTEX along FACE
3725         edge._normal = getFaceDir( TopoDS::Face( eos._sWOL ), TopoDS::Vertex( eos._shape ),
3726                                    node, helper, normOK/*, &edge._cosin*/);
3727       }
3728       else
3729       {
3730         // inflate from EDGE along FACE
3731         edge._normal = getFaceDir( TopoDS::Face( eos._sWOL ), TopoDS::Edge( eos._shape ),
3732                                    node, helper, normOK);
3733       }
3734     }
3735     else // layers are on all FACEs of SOLID the node is on (or fromVonF)
3736     {
3737       if ( fromVonF )
3738         face2Norm[ totalNbFaces++ ].first = TopoDS::Face( eos._sWOL );
3739
3740       int nbOkNorms = 0;
3741       for ( int iF = totalNbFaces - 1; iF >= 0; --iF )
3742       {
3743         F = face2Norm[ iF ].first;
3744         geomNorm = getFaceNormal( node, F, helper, normOK );
3745         if ( !normOK ) continue;
3746         nbOkNorms++;
3747
3748         if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3749           geomNorm.Reverse();
3750         face2Norm[ iF ].second = geomNorm.XYZ();
3751         edge._normal += geomNorm.XYZ();
3752       }
3753       if ( nbOkNorms == 0 )
3754         return error(SMESH_Comment("Can't get normal to node ") << node->GetID(), data._index);
3755
3756       if ( totalNbFaces >= 3 )
3757       {
3758         edge._normal = getNormalByOffset( &edge, face2Norm, totalNbFaces, fromVonF );
3759       }
3760
3761       if ( edge._normal.Modulus() < 1e-3 && nbOkNorms > 1 )
3762       {
3763         // opposite normals, re-get normals at shifted positions (IPAL 52426)
3764         edge._normal.SetCoord( 0,0,0 );
3765         for ( int iF = 0; iF < totalNbFaces - fromVonF; ++iF )
3766         {
3767           const TopoDS_Face& F = face2Norm[iF].first;
3768           geomNorm = getFaceNormal( node, F, helper, normOK, /*shiftInside=*/true );
3769           if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3770             geomNorm.Reverse();
3771           if ( normOK )
3772             face2Norm[ iF ].second = geomNorm.XYZ();
3773           edge._normal += face2Norm[ iF ].second;
3774         }
3775       }
3776     }
3777   }
3778   else // !useGeometry - get _normal using surrounding mesh faces
3779   {
3780     edge._normal = getWeigthedNormal( &edge );
3781
3782     // set<TGeomID> faceIds;
3783     //
3784     // SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
3785     // while ( fIt->more() )
3786     // {
3787     //   const SMDS_MeshElement* face = fIt->next();
3788     //   if ( eos.GetNormal( face, geomNorm ))
3789     //   {
3790     //     if ( onShrinkShape && !faceIds.insert( face->getshapeId() ).second )
3791     //       continue; // use only one mesh face on FACE
3792     //     edge._normal += geomNorm.XYZ();
3793     //     totalNbFaces++;
3794     //   }
3795     // }
3796   }
3797
3798   // compute _cosin
3799   //if ( eos._hyp.UseSurfaceNormal() )
3800   {
3801     switch ( eos.ShapeType() )
3802     {
3803     case TopAbs_FACE: {
3804       edge._cosin = 0;
3805       break;
3806     }
3807     case TopAbs_EDGE: {
3808       TopoDS_Edge E    = TopoDS::Edge( eos._shape );
3809       gp_Vec inFaceDir = getFaceDir( F, E, node, helper, normOK );
3810       double angle     = inFaceDir.Angle( edge._normal ); // [0,PI]
3811       edge._cosin      = Cos( angle );
3812       break;
3813     }
3814     case TopAbs_VERTEX: {
3815       TopoDS_Vertex V  = TopoDS::Vertex( eos._shape );
3816       gp_Vec inFaceDir = getFaceDir( face2Norm[0].first , V, node, helper, normOK );
3817       double angle     = inFaceDir.Angle( edge._normal ); // [0,PI]
3818       edge._cosin      = Cos( angle );
3819       if ( fromVonF )
3820         totalNbFaces--;
3821       if ( totalNbFaces > 1 || helper.IsSeamShape( node->getshapeId() ))
3822         for ( int iF = 1; iF < totalNbFaces; ++iF )
3823         {
3824           F = face2Norm[ iF ].first;
3825           inFaceDir = getFaceDir( F, V, node, helper, normOK=true );
3826           if ( normOK ) {
3827             if ( onShrinkShape )
3828             {
3829               gp_Vec faceNorm = getFaceNormal( node, F, helper, normOK );
3830               if ( !normOK ) continue;
3831               if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3832                 faceNorm.Reverse();
3833               angle = 0.5 * M_PI - faceNorm.Angle( edge._normal );
3834               if ( inFaceDir * edge._normal < 0 )
3835                 angle = M_PI - angle;
3836             }
3837             else
3838             {
3839               angle = inFaceDir.Angle( edge._normal );
3840             }
3841             double cosin = Cos( angle );
3842             if ( Abs( cosin ) > Abs( edge._cosin ))
3843               edge._cosin = cosin;
3844           }
3845         }
3846       break;
3847     }
3848     default:
3849       return error(SMESH_Comment("Invalid shape position of node ")<<node, data._index);
3850     }
3851   }
3852
3853   double normSize = edge._normal.SquareModulus();
3854   if ( normSize < numeric_limits<double>::min() )
3855     return error(SMESH_Comment("Bad normal at node ")<< node->GetID(), data._index );
3856
3857   edge._normal /= sqrt( normSize );
3858
3859   if ( edge.Is( _LayerEdge::MULTI_NORMAL ) && edge._nodes.size() == 2 )
3860   {
3861     getMeshDS()->RemoveFreeNode( edge._nodes.back(), 0, /*fromGroups=*/false );
3862     edge._nodes.resize( 1 );
3863     edge._normal.SetCoord( 0,0,0 );
3864     edge.SetMaxLen( 0 );
3865   }
3866
3867   // Set the rest data
3868   // --------------------
3869
3870   double realLenFactor = edge.SetCosin( edge._cosin ); // to update edge._lenFactor
3871   // if ( realLenFactor > 3 )
3872   // {
3873   //   edge._cosin = 1;
3874   //   if ( onShrinkShape )
3875   //   {
3876   //     edge.Set( _LayerEdge::RISKY_SWOL );
3877   //     edge._lenFactor = 2;
3878   //   }
3879   //   else
3880   //   {
3881   //     edge._lenFactor = 1;
3882   //   }
3883   // }
3884
3885   if ( onShrinkShape )
3886   {
3887     const SMDS_MeshNode* tgtNode = edge._nodes.back();
3888     if ( SMESHDS_SubMesh* sm = getMeshDS()->MeshElements( data._solid ))
3889       sm->RemoveNode( tgtNode );
3890
3891     // set initial position which is parameters on _sWOL in this case
3892     if ( eos.SWOLType() == TopAbs_EDGE )
3893     {
3894       double u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), node, 0, &normOK );
3895       edge._pos.push_back( gp_XYZ( u, 0, 0 ));
3896       if ( edge._nodes.size() > 1 )
3897         getMeshDS()->SetNodeOnEdge( tgtNode, TopoDS::Edge( eos._sWOL ), u );
3898     }
3899     else // eos.SWOLType() == TopAbs_FACE
3900     {
3901       gp_XY uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), node, 0, &normOK );
3902       edge._pos.push_back( gp_XYZ( uv.X(), uv.Y(), 0));
3903       if ( edge._nodes.size() > 1 )
3904         getMeshDS()->SetNodeOnFace( tgtNode, TopoDS::Face( eos._sWOL ), uv.X(), uv.Y() );
3905     }
3906
3907     //if ( edge._nodes.size() > 1 ) -- allow RISKY_SWOL on noShrink shape
3908     {
3909       // check if an angle between a FACE with layers and SWOL is sharp,
3910       // else the edge should not inflate
3911       F.Nullify();
3912       for ( int iF = 0; iF < totalNbFaces  &&  F.IsNull();  ++iF ) // find a FACE with VL
3913         if ( ! helper.IsSubShape( eos._sWOL, face2Norm[iF].first ))
3914           F = face2Norm[iF].first;
3915       if ( !F.IsNull())
3916       {
3917         geomNorm = getFaceNormal( node, F, helper, normOK );
3918         if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3919           geomNorm.Reverse(); // inside the SOLID
3920         if ( geomNorm * edge._normal < -0.001 )
3921         {
3922           if ( edge._nodes.size() > 1 )
3923           {
3924             getMeshDS()->RemoveFreeNode( tgtNode, 0, /*fromGroups=*/false );
3925             edge._nodes.resize( 1 );
3926           }
3927         }
3928         else if ( realLenFactor > 3 ) ///  -- moved to SetCosin()
3929           //else if ( edge._lenFactor > 3 )
3930         {
3931           edge._lenFactor = 2;
3932           edge.Set( _LayerEdge::RISKY_SWOL );
3933         }
3934       }
3935     }
3936   }
3937   else
3938   {
3939     edge._pos.push_back( SMESH_TNodeXYZ( node ));
3940
3941     if ( eos.ShapeType() == TopAbs_FACE )
3942     {
3943       double angle;
3944       for ( size_t i = 0; i < edge._simplices.size(); ++i )
3945       {
3946         edge._simplices[i].IsMinAngleOK( edge._pos.back(), angle );
3947         edge._minAngle = Max( edge._minAngle, angle ); // "angle" is actually cosine
3948       }
3949     }
3950   }
3951
3952   // Set neighbor nodes for a _LayerEdge based on EDGE
3953
3954   if ( eos.ShapeType() == TopAbs_EDGE /*||
3955        ( onShrinkShape && posType == SMDS_TOP_VERTEX && fabs( edge._cosin ) < 1e-10 )*/)
3956   {
3957     edge._2neibors = _Factory::NewNearEdges();
3958     // target nodes instead of source ones will be set later
3959   }
3960
3961   return true;
3962 }
3963
3964 //================================================================================
3965 /*!
3966  * \brief Return normal to a FACE at a node
3967  *  \param [in] n - node
3968  *  \param [in] face - FACE
3969  *  \param [in] helper - helper
3970  *  \param [out] isOK - true or false
3971  *  \param [in] shiftInside - to find normal at a position shifted inside the face
3972  *  \return gp_XYZ - normal
3973  */
3974 //================================================================================
3975
3976 gp_XYZ _ViscousBuilder::getFaceNormal(const SMDS_MeshNode* node,
3977                                       const TopoDS_Face&   face,
3978                                       SMESH_MesherHelper&  helper,
3979                                       bool&                isOK,
3980                                       bool                 shiftInside)
3981 {
3982   gp_XY uv;
3983   if ( shiftInside )
3984   {
3985     // get a shifted position
3986     gp_Pnt p = SMESH_TNodeXYZ( node );
3987     gp_XYZ shift( 0,0,0 );
3988     TopoDS_Shape S = helper.GetSubShapeByNode( node, helper.GetMeshDS() );
3989     switch ( S.ShapeType() ) {
3990     case TopAbs_VERTEX:
3991     {
3992       shift = getFaceDir( face, TopoDS::Vertex( S ), node, helper, isOK );
3993       break;
3994     }
3995     case TopAbs_EDGE:
3996     {
3997       shift = getFaceDir( face, TopoDS::Edge( S ), node, helper, isOK );
3998       break;
3999     }
4000     default:
4001       isOK = false;
4002     }
4003     if ( isOK )
4004       shift.Normalize();
4005     p.Translate( shift * 1e-5 );
4006
4007     TopLoc_Location loc;
4008     GeomAPI_ProjectPointOnSurf& projector = helper.GetProjector( face, loc, 1e-7 );
4009
4010     if ( !loc.IsIdentity() ) p.Transform( loc.Transformation().Inverted() );
4011     
4012     projector.Perform( p );
4013     if ( !projector.IsDone() || projector.NbPoints() < 1 )
4014     {
4015       isOK = false;
4016       return p.XYZ();
4017     }
4018     Standard_Real U,V;
4019     projector.LowerDistanceParameters(U,V);
4020     uv.SetCoord( U,V );
4021   }
4022   else
4023   {
4024     uv = helper.GetNodeUV( face, node, 0, &isOK );
4025   }
4026
4027   gp_Dir normal;
4028   isOK = false;
4029
4030   Handle(Geom_Surface) surface = BRep_Tool::Surface( face );
4031
4032   if ( !shiftInside &&
4033        helper.IsDegenShape( node->getshapeId() ) &&
4034        getFaceNormalAtSingularity( uv, face, helper, normal ))
4035   {
4036     isOK = true;
4037     return normal.XYZ();
4038   }
4039
4040   int pointKind = GeomLib::NormEstim( surface, uv, 1e-5, normal );
4041   enum { REGULAR = 0, QUASYSINGULAR, CONICAL, IMPOSSIBLE };
4042
4043   if ( pointKind == IMPOSSIBLE &&
4044        node->GetPosition()->GetDim() == 2 ) // node inside the FACE
4045   {
4046     // probably NormEstim() failed due to a too high tolerance
4047     pointKind = GeomLib::NormEstim( surface, uv, 1e-20, normal );
4048     isOK = ( pointKind < IMPOSSIBLE );
4049   }
4050   if ( pointKind < IMPOSSIBLE )
4051   {
4052     if ( pointKind != REGULAR &&
4053          !shiftInside &&
4054          node->GetPosition()->GetDim() < 2 ) // FACE boundary
4055     {
4056       gp_XYZ normShift = getFaceNormal( node, face, helper, isOK, /*shiftInside=*/true );
4057       if ( normShift * normal.XYZ() < 0. )
4058         normal = normShift;
4059     }
4060     isOK = true;
4061   }
4062
4063   if ( !isOK ) // hard singularity, to call with shiftInside=true ?
4064   {
4065     const TGeomID faceID = helper.GetMeshDS()->ShapeToIndex( face );
4066
4067     SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
4068     while ( fIt->more() )
4069     {
4070       const SMDS_MeshElement* f = fIt->next();
4071       if ( f->getshapeId() == faceID )
4072       {
4073         isOK = SMESH_MeshAlgos::FaceNormal( f, (gp_XYZ&) normal.XYZ(), /*normalized=*/true );
4074         if ( isOK )
4075         {
4076           TopoDS_Face ff = face;
4077           ff.Orientation( TopAbs_FORWARD );
4078           if ( helper.IsReversedSubMesh( ff ))
4079             normal.Reverse();
4080           break;
4081         }
4082       }
4083     }
4084   }
4085   return normal.XYZ();
4086 }
4087
4088 //================================================================================
4089 /*!
4090  * \brief Try to get normal at a singularity of a surface basing on it's nature
4091  */
4092 //================================================================================
4093
4094 bool _ViscousBuilder::getFaceNormalAtSingularity( const gp_XY&        uv,
4095                                                   const TopoDS_Face&  face,
4096                                                   SMESH_MesherHelper& /*helper*/,
4097                                                   gp_Dir&             normal )
4098 {
4099   BRepAdaptor_Surface surface( face );
4100   gp_Dir axis;
4101   if ( !getRovolutionAxis( surface, axis ))
4102     return false;
4103
4104   double f,l, d, du, dv;
4105   f = surface.FirstUParameter();
4106   l = surface.LastUParameter();
4107   d = ( uv.X() - f ) / ( l - f );
4108   du = ( d < 0.5 ? +1. : -1 ) * 1e-5 * ( l - f );
4109   f = surface.FirstVParameter();
4110   l = surface.LastVParameter();
4111   d = ( uv.Y() - f ) / ( l - f );
4112   dv = ( d < 0.5 ? +1. : -1 ) * 1e-5 * ( l - f );
4113
4114   gp_Dir refDir;
4115   gp_Pnt2d testUV = uv;
4116   enum { REGULAR = 0, QUASYSINGULAR, CONICAL, IMPOSSIBLE };
4117   double tol = 1e-5;
4118   Handle(Geom_Surface) geomsurf = surface.Surface().Surface();
4119   for ( int iLoop = 0; true ; ++iLoop )
4120   {
4121     testUV.SetCoord( testUV.X() + du, testUV.Y() + dv );
4122     if ( GeomLib::NormEstim( geomsurf, testUV, tol, refDir ) == REGULAR )
4123       break;
4124     if ( iLoop > 20 )
4125       return false;
4126     tol /= 10.;
4127   }
4128
4129   if ( axis * refDir < 0. )
4130     axis.Reverse();
4131
4132   normal = axis;
4133
4134   return true;
4135 }
4136
4137 //================================================================================
4138 /*!
4139  * \brief Return a normal at a node weighted with angles taken by faces
4140  */
4141 //================================================================================
4142
4143 gp_XYZ _ViscousBuilder::getWeigthedNormal( const _LayerEdge* edge )
4144 {
4145   const SMDS_MeshNode* n = edge->_nodes[0];
4146
4147   gp_XYZ resNorm(0,0,0);
4148   SMESH_TNodeXYZ p0( n ), pP, pN;
4149   for ( size_t i = 0; i < edge->_simplices.size(); ++i )
4150   {
4151     pP.Set( edge->_simplices[i]._nPrev );
4152     pN.Set( edge->_simplices[i]._nNext );
4153     gp_Vec v0P( p0, pP ), v0N( p0, pN ), vPN( pP, pN ), norm = v0P ^ v0N;
4154     double l0P = v0P.SquareMagnitude();
4155     double l0N = v0N.SquareMagnitude();
4156     double lPN = vPN.SquareMagnitude();
4157     if ( l0P < std::numeric_limits<double>::min() ||
4158          l0N < std::numeric_limits<double>::min() ||
4159          lPN < std::numeric_limits<double>::min() )
4160       continue;
4161     double lNorm = norm.SquareMagnitude();
4162     double  sin2 = lNorm / l0P / l0N;
4163     double angle = ACos(( v0P * v0N ) / Sqrt( l0P ) / Sqrt( l0N ));
4164
4165     double weight = sin2 * angle / lPN;
4166     resNorm += weight * norm.XYZ() / Sqrt( lNorm );
4167   }
4168
4169   return resNorm;
4170 }
4171
4172 //================================================================================
4173 /*!
4174  * \brief Return a normal at a node by getting a common point of offset planes
4175  *        defined by the FACE normals
4176  */
4177 //================================================================================
4178
4179 gp_XYZ _ViscousBuilder::getNormalByOffset( _LayerEdge*                      edge,
4180                                            std::pair< TopoDS_Face, gp_XYZ > f2Normal[],
4181                                            int                              nbFaces,
4182                                            bool                             lastNoOffset)
4183 {
4184   SMESH_TNodeXYZ p0 = edge->_nodes[0];
4185
4186   gp_XYZ resNorm(0,0,0);
4187   TopoDS_Shape V = SMESH_MesherHelper::GetSubShapeByNode( p0._node, getMeshDS() );
4188   if ( V.ShapeType() != TopAbs_VERTEX || nbFaces < 3 )
4189   {
4190     for ( int i = 0; i < nbFaces; ++i )
4191       resNorm += f2Normal[i].second;
4192     return resNorm;
4193   }
4194
4195   // prepare _OffsetPlane's
4196   vector< _OffsetPlane > pln( nbFaces );
4197   for ( int i = 0; i < nbFaces - lastNoOffset; ++i )
4198   {
4199     pln[i]._faceIndex = i;
4200     pln[i]._plane = gp_Pln( p0 + f2Normal[i].second, f2Normal[i].second );
4201   }
4202   if ( lastNoOffset )
4203   {
4204     pln[ nbFaces - 1 ]._faceIndex = nbFaces - 1;
4205     pln[ nbFaces - 1 ]._plane = gp_Pln( p0, f2Normal[ nbFaces - 1 ].second );
4206   }
4207
4208   // intersect neighboring OffsetPlane's
4209   PShapeIteratorPtr edgeIt = SMESH_MesherHelper::GetAncestors( V, *_mesh, TopAbs_EDGE );
4210   while ( const TopoDS_Shape* edge = edgeIt->next() )
4211   {
4212     int f1 = -1, f2 = -1;
4213     for ( int i = 0; i < nbFaces &&  f2 < 0;  ++i )
4214       if ( SMESH_MesherHelper::IsSubShape( *edge, f2Normal[i].first ))
4215         (( f1 < 0 ) ? f1 : f2 ) = i;
4216
4217     if ( f2 >= 0 )
4218       pln[ f1 ].ComputeIntersectionLine( pln[ f2 ], TopoDS::Edge( *edge ), TopoDS::Vertex( V ));
4219   }
4220
4221   // get a common point
4222   gp_XYZ commonPnt( 0, 0, 0 );
4223   int nbPoints = 0;
4224   bool isPointFound;
4225   for ( int i = 0; i < nbFaces; ++i )
4226   {
4227     commonPnt += pln[ i ].GetCommonPoint( isPointFound, TopoDS::Vertex( V ));
4228     nbPoints  += isPointFound;
4229   }
4230   gp_XYZ wgtNorm = getWeigthedNormal( edge );
4231   if ( nbPoints == 0 )
4232     return wgtNorm;
4233
4234   commonPnt /= nbPoints;
4235   resNorm = commonPnt - p0;
4236   if ( lastNoOffset )
4237     return resNorm;
4238
4239   // choose the best among resNorm and wgtNorm
4240   resNorm.Normalize();
4241   wgtNorm.Normalize();
4242   double resMinDot = std::numeric_limits<double>::max();
4243   double wgtMinDot = std::numeric_limits<double>::max();
4244   for ( int i = 0; i < nbFaces - lastNoOffset; ++i )
4245   {
4246     resMinDot = Min( resMinDot, resNorm * f2Normal[i].second );
4247     wgtMinDot = Min( wgtMinDot, wgtNorm * f2Normal[i].second );
4248   }
4249
4250   if ( Max( resMinDot, wgtMinDot ) < theMinSmoothCosin )
4251   {
4252     edge->Set( _LayerEdge::MULTI_NORMAL );
4253   }
4254
4255   return ( resMinDot > wgtMinDot ) ? resNorm : wgtNorm;
4256 }
4257
4258 //================================================================================
4259 /*!
4260  * \brief Compute line of intersection of 2 planes
4261  */
4262 //================================================================================
4263
4264 void _OffsetPlane::ComputeIntersectionLine( _OffsetPlane&        pln,
4265                                             const TopoDS_Edge&   E,
4266                                             const TopoDS_Vertex& V )
4267 {
4268   int iNext = bool( _faceIndexNext[0] >= 0 );
4269   _faceIndexNext[ iNext ] = pln._faceIndex;
4270
4271   gp_XYZ n1 = _plane.Axis().Direction().XYZ();
4272   gp_XYZ n2 = pln._plane.Axis().Direction().XYZ();
4273
4274   gp_XYZ lineDir = n1 ^ n2;
4275
4276   double x = Abs( lineDir.X() );
4277   double y = Abs( lineDir.Y() );
4278   double z = Abs( lineDir.Z() );
4279
4280   int cooMax; // max coordinate
4281   if (x > y) {
4282     if (x > z) cooMax = 1;
4283     else       cooMax = 3;
4284   }
4285   else {
4286     if (y > z) cooMax = 2;
4287     else       cooMax = 3;
4288   }
4289
4290   gp_Pnt linePos;
4291   if ( Abs( lineDir.Coord( cooMax )) < 0.05 )
4292   {
4293     // parallel planes - intersection is an offset of the common EDGE
4294     gp_Pnt p = BRep_Tool::Pnt( V );
4295     linePos  = 0.5 * (( p.XYZ() + n1 ) + ( p.XYZ() + n2 ));
4296     lineDir  = getEdgeDir( E, V, 0.1 * SMESH_Algo::EdgeLength( E ));
4297   }
4298   else
4299   {
4300     // the constants in the 2 plane equations
4301     double d1 = - ( _plane.Axis().Direction().XYZ()     * _plane.Location().XYZ() );
4302     double d2 = - ( pln._plane.Axis().Direction().XYZ() * pln._plane.Location().XYZ() );
4303
4304     switch ( cooMax ) {
4305     case 1:
4306       linePos.SetX(  0 );
4307       linePos.SetY(( d2*n1.Z() - d1*n2.Z()) / lineDir.X() );
4308       linePos.SetZ(( d1*n2.Y() - d2*n1.Y()) / lineDir.X() );
4309       break;
4310     case 2:
4311       linePos.SetX(( d1*n2.Z() - d2*n1.Z()) / lineDir.Y() );
4312       linePos.SetY(  0 );
4313       linePos.SetZ(( d2*n1.X() - d1*n2.X()) / lineDir.Y() );
4314       break;
4315     case 3:
4316       linePos.SetX(( d2*n1.Y() - d1*n2.Y()) / lineDir.Z() );
4317       linePos.SetY(( d1*n2.X() - d2*n1.X()) / lineDir.Z() );
4318       linePos.SetZ(  0 );
4319     }
4320   }
4321   gp_Lin& line = _lines[ iNext ];
4322   line.SetDirection( lineDir );
4323   line.SetLocation ( linePos );
4324
4325   _isLineOK[ iNext ] = true;
4326
4327
4328   iNext = bool( pln._faceIndexNext[0] >= 0 );
4329   pln._lines        [ iNext ] = line;
4330   pln._faceIndexNext[ iNext ] = this->_faceIndex;
4331   pln._isLineOK     [ iNext ] = true;
4332 }
4333
4334 //================================================================================
4335 /*!
4336  * \brief Computes intersection point of two _lines
4337  */
4338 //================================================================================
4339
4340 gp_XYZ _OffsetPlane::GetCommonPoint(bool&                 isFound,
4341                                     const TopoDS_Vertex & V) const
4342 {
4343   gp_XYZ p( 0,0,0 );
4344   isFound = false;
4345
4346   if ( NbLines() == 2 )
4347   {
4348     gp_Vec lPerp0 = _lines[0].Direction().XYZ() ^ _plane.Axis().Direction().XYZ();
4349     double  dot01 = lPerp0 * _lines[1].Direction().XYZ();
4350     if ( Abs( dot01 ) > 0.05 )
4351     {
4352       gp_Vec l0l1 = _lines[1].Location().XYZ() - _lines[0].Location().XYZ();
4353       double   u1 = - ( lPerp0 * l0l1 ) / dot01;
4354       p = ( _lines[1].Location().XYZ() + _lines[1].Direction().XYZ() * u1 );
4355       isFound = true;
4356     }
4357     else
4358     {
4359       gp_Pnt  pV ( BRep_Tool::Pnt( V ));
4360       gp_Vec  lv0( _lines[0].Location(), pV    ),  lv1(_lines[1].Location(), pV     );
4361       double dot0( lv0 * _lines[0].Direction() ), dot1( lv1 * _lines[1].Direction() );
4362       p += 0.5 * ( _lines[0].Location().XYZ() + _lines[0].Direction().XYZ() * dot0 );
4363       p += 0.5 * ( _lines[1].Location().XYZ() + _lines[1].Direction().XYZ() * dot1 );
4364       isFound = true;
4365     }
4366   }
4367
4368   return p;
4369 }
4370
4371 //================================================================================
4372 /*!
4373  * \brief Find 2 neighbor nodes of a node on EDGE
4374  */
4375 //================================================================================
4376
4377 bool _ViscousBuilder::findNeiborsOnEdge(const _LayerEdge*     edge,
4378                                         const SMDS_MeshNode*& n1,
4379                                         const SMDS_MeshNode*& n2,
4380                                         _EdgesOnShape&        eos,
4381                                         _SolidData&           data)
4382 {
4383   const SMDS_MeshNode* node = edge->_nodes[0];
4384   const int        shapeInd = eos._shapeID;
4385   SMESHDS_SubMesh*   edgeSM = 0;
4386   if ( eos.ShapeType() == TopAbs_EDGE )
4387   {
4388     edgeSM = eos._subMesh->GetSubMeshDS();
4389     if ( !edgeSM || edgeSM->NbElements() == 0 )
4390       return error(SMESH_Comment("Not meshed EDGE ") << shapeInd, data._index);
4391   }
4392   int iN = 0;
4393   n2 = 0;
4394   SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Edge);
4395   while ( eIt->more() && !n2 )
4396   {
4397     const SMDS_MeshElement* e = eIt->next();
4398     const SMDS_MeshNode*   nNeibor = e->GetNode( 0 );
4399     if ( nNeibor == node ) nNeibor = e->GetNode( 1 );
4400     if ( edgeSM )
4401     {
4402       if (!edgeSM->Contains(e)) continue;
4403     }
4404     else
4405     {
4406       TopoDS_Shape s = SMESH_MesherHelper::GetSubShapeByNode( nNeibor, getMeshDS() );
4407       if ( !SMESH_MesherHelper::IsSubShape( s, eos._sWOL )) continue;
4408     }
4409     ( iN++ ? n2 : n1 ) = nNeibor;
4410   }
4411   if ( !n2 )
4412     return error(SMESH_Comment("Wrongly meshed EDGE ") << shapeInd, data._index);
4413   return true;
4414 }
4415
4416 //================================================================================
4417 /*!
4418  * \brief Create _Curvature
4419  */
4420 //================================================================================
4421
4422 _Curvature* _Curvature::New( double avgNormProj, double avgDist )
4423 {
4424   // double   _r; // radius
4425   // double   _k; // factor to correct node smoothed position
4426   // double   _h2lenRatio; // avgNormProj / (2*avgDist)
4427   // gp_Pnt2d _uv; // UV used in putOnOffsetSurface()
4428
4429   _Curvature* c = 0;
4430   if ( fabs( avgNormProj / avgDist ) > 1./200 )
4431   {
4432     c = _Factory::NewCurvature();
4433     c->_r = avgDist * avgDist / avgNormProj;
4434     c->_k = avgDist * avgDist / c->_r / c->_r;
4435     //c->_k = avgNormProj / c->_r;
4436     c->_k *= ( c->_r < 0 ? 1/1.1 : 1.1 ); // not to be too restrictive
4437     c->_h2lenRatio = avgNormProj / ( avgDist + avgDist );
4438
4439     c->_uv.SetCoord( 0., 0. );
4440   }
4441   return c;
4442 }
4443
4444 //================================================================================
4445 /*!
4446  * \brief Set _curvature and _2neibors->_plnNorm by 2 neighbor nodes residing the same EDGE
4447  */
4448 //================================================================================
4449
4450 void _LayerEdge::SetDataByNeighbors( const SMDS_MeshNode* n1,
4451                                      const SMDS_MeshNode* n2,
4452                                      const _EdgesOnShape& eos,
4453                                      SMESH_MesherHelper&  helper)
4454 {
4455   if ( eos.ShapeType() != TopAbs_EDGE )
4456     return;
4457   if ( _curvature && Is( SMOOTHED_C1 ))
4458     return;
4459
4460   gp_XYZ  pos = SMESH_TNodeXYZ( _nodes[0] );
4461   gp_XYZ vec1 = pos - SMESH_TNodeXYZ( n1 );
4462   gp_XYZ vec2 = pos - SMESH_TNodeXYZ( n2 );
4463
4464   // Set _curvature
4465
4466   double      sumLen = vec1.Modulus() + vec2.Modulus();
4467   _2neibors->_wgt[0] = 1 - vec1.Modulus() / sumLen;
4468   _2neibors->_wgt[1] = 1 - vec2.Modulus() / sumLen;
4469   double avgNormProj = 0.5 * ( _normal * vec1 + _normal * vec2 );
4470   double      avgLen = 0.5 * ( vec1.Modulus() + vec2.Modulus() );
4471   _curvature = _Curvature::New( avgNormProj, avgLen );
4472   // if ( _curvature )
4473   //   debugMsg( _nodes[0]->GetID()
4474   //             << " CURV r,k: " << _curvature->_r<<","<<_curvature->_k
4475   //             << " proj = "<<avgNormProj<< " len = " << avgLen << "| lenDelta(0) = "
4476   //             << _curvature->lenDelta(0) );
4477
4478   // Set _plnNorm
4479
4480   if ( eos._sWOL.IsNull() )
4481   {
4482     TopoDS_Edge  E = TopoDS::Edge( eos._shape );
4483     // if ( SMESH_Algo::isDegenerated( E ))
4484     //   return;
4485     gp_XYZ dirE    = getEdgeDir( E, _nodes[0], helper );
4486     gp_XYZ plnNorm = dirE ^ _normal;
4487     double proj0   = plnNorm * vec1;
4488     double proj1   = plnNorm * vec2;
4489     if ( fabs( proj0 ) > 1e-10 || fabs( proj1 ) > 1e-10 )
4490     {
4491       if ( _2neibors->_plnNorm ) delete _2neibors->_plnNorm;
4492       _2neibors->_plnNorm = new gp_XYZ( plnNorm.Normalized() );
4493     }
4494   }
4495 }
4496
4497 //================================================================================
4498 /*!
4499  * \brief Copy data from a _LayerEdge of other SOLID and based on the same node;
4500  * this and the other _LayerEdge are inflated along a FACE or an EDGE
4501  */
4502 //================================================================================
4503
4504 gp_XYZ _LayerEdge::Copy( _LayerEdge&         other,
4505                          _EdgesOnShape&      eos,
4506                          SMESH_MesherHelper& helper )
4507 {
4508   _nodes     = other._nodes;
4509   _normal    = other._normal;
4510   _len       = 0;
4511   _lenFactor = other._lenFactor;
4512   _cosin     = other._cosin;
4513   _2neibors  = other._2neibors;
4514   _curvature = other._curvature;
4515   _2neibors  = other._2neibors;
4516   _maxLen    = Precision::Infinite();//other._maxLen;
4517   _flags     = 0;
4518   _smooFunction = 0;
4519
4520   gp_XYZ lastPos( 0,0,0 );
4521   if ( eos.SWOLType() == TopAbs_EDGE )
4522   {
4523     double u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), _nodes[0] );
4524     _pos.push_back( gp_XYZ( u, 0, 0));
4525
4526     u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), _nodes.back() );
4527     lastPos.SetX( u );
4528   }
4529   else // TopAbs_FACE
4530   {
4531     gp_XY uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), _nodes[0]);
4532     _pos.push_back( gp_XYZ( uv.X(), uv.Y(), 0));
4533
4534     uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), _nodes.back() );
4535     lastPos.SetX( uv.X() );
4536     lastPos.SetY( uv.Y() );
4537   }
4538   return lastPos;
4539 }
4540
4541 //================================================================================
4542 /*!
4543  * \brief Set _cosin and _lenFactor
4544  */
4545 //================================================================================
4546
4547 double _LayerEdge::SetCosin( double cosin )
4548 {
4549   _cosin = cosin;
4550   cosin = Abs( _cosin );
4551   //_lenFactor = ( cosin < 1.-1e-12 ) ?  1./sqrt(1-cosin*cosin ) : 1.0;
4552   double realLenFactor;
4553   if ( cosin < 1.-1e-12 )
4554   {
4555     _lenFactor = realLenFactor = 1./sqrt(1-cosin*cosin );
4556   }
4557   else
4558   {
4559     _lenFactor = 1;
4560     realLenFactor = Precision::Infinite();
4561   }
4562
4563   return realLenFactor;
4564 }
4565
4566 //================================================================================
4567 /*!
4568  * \brief Check if another _LayerEdge is a neighbor on EDGE
4569  */
4570 //================================================================================
4571
4572 bool _LayerEdge::IsNeiborOnEdge( const _LayerEdge* edge ) const
4573 {
4574   return (( this->_2neibors && this->_2neibors->include( edge )) ||
4575           ( edge->_2neibors && edge->_2neibors->include( this )));
4576 }
4577
4578 //================================================================================
4579 /*!
4580  * \brief Fills a vector<_Simplex > 
4581  */
4582 //================================================================================
4583
4584 void _Simplex::GetSimplices( const SMDS_MeshNode* node,
4585                              vector<_Simplex>&    simplices,
4586                              const set<TGeomID>&  ingnoreShapes,
4587                              const _SolidData*    dataToCheckOri,
4588                              const bool           toSort)
4589 {
4590   simplices.clear();
4591   SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
4592   while ( fIt->more() )
4593   {
4594     const SMDS_MeshElement* f = fIt->next();
4595     const TGeomID    shapeInd = f->getshapeId();
4596     if ( ingnoreShapes.count( shapeInd )) continue;
4597     const int nbNodes = f->NbCornerNodes();
4598     const int  srcInd = f->GetNodeIndex( node );
4599     const SMDS_MeshNode* nPrev = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd-1, nbNodes ));
4600     const SMDS_MeshNode* nNext = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd+1, nbNodes ));
4601     const SMDS_MeshNode* nOpp  = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd+2, nbNodes ));
4602     if ( dataToCheckOri && dataToCheckOri->_reversedFaceIds.count( shapeInd ))
4603       std::swap( nPrev, nNext );
4604     simplices.push_back( _Simplex( nPrev, nNext, ( nbNodes == 3 ? 0 : nOpp )));
4605   }
4606
4607   if ( toSort )
4608     SortSimplices( simplices );
4609 }
4610
4611 //================================================================================
4612 /*!
4613  * \brief Set neighbor simplices side by side
4614  */
4615 //================================================================================
4616
4617 void _Simplex::SortSimplices(vector<_Simplex>& simplices)
4618 {
4619   vector<_Simplex> sortedSimplices( simplices.size() );
4620   sortedSimplices[0] = simplices[0];
4621   size_t nbFound = 0;
4622   for ( size_t i = 1; i < simplices.size(); ++i )
4623   {
4624     for ( size_t j = 1; j < simplices.size(); ++j )
4625       if ( sortedSimplices[i-1]._nNext == simplices[j]._nPrev )
4626       {
4627         sortedSimplices[i] = simplices[j];
4628         nbFound++;
4629         break;
4630       }
4631   }
4632   if ( nbFound == simplices.size() - 1 )
4633     simplices.swap( sortedSimplices );
4634 }
4635
4636 //================================================================================
4637 /*!
4638  * \brief DEBUG. Create groups containing temporary data of _LayerEdge's
4639  */
4640 //================================================================================
4641
4642 void _ViscousBuilder::makeGroupOfLE()
4643 {
4644 #ifdef _DEBUG_
4645   for ( size_t i = 0 ; i < _sdVec.size(); ++i )
4646   {
4647     if ( _sdVec[i]._n2eMap.empty() ) continue;
4648
4649     dumpFunction( SMESH_Comment("make_LayerEdge_") << i );
4650     TNode2Edge::iterator n2e;
4651     for ( n2e = _sdVec[i]._n2eMap.begin(); n2e != _sdVec[i]._n2eMap.end(); ++n2e )
4652     {
4653       _LayerEdge* le = n2e->second;
4654       // for ( size_t iN = 1; iN < le->_nodes.size(); ++iN )
4655       //   dumpCmd(SMESH_Comment("mesh.AddEdge([ ") <<le->_nodes[iN-1]->GetID()
4656       //           << ", " << le->_nodes[iN]->GetID() <<"])");
4657       if ( le ) {
4658         dumpCmd(SMESH_Comment("mesh.AddEdge([ ") <<le->_nodes[0]->GetID()
4659                 << ", " << le->_nodes.back()->GetID() <<"]) # " << le->_flags );
4660       }
4661     }
4662     dumpFunctionEnd();
4663
4664     dumpFunction( SMESH_Comment("makeNormals") << i );
4665     for ( n2e = _sdVec[i]._n2eMap.begin(); n2e != _sdVec[i]._n2eMap.end(); ++n2e )
4666     {
4667       _LayerEdge* edge = n2e->second;
4668       SMESH_TNodeXYZ nXYZ( edge->_nodes[0] );
4669       nXYZ += edge->_normal * _sdVec[i]._stepSize;
4670       dumpCmd(SMESH_Comment("mesh.AddEdge([ ") << edge->_nodes[0]->GetID()
4671               << ", mesh.AddNode( "<< nXYZ.X()<<","<< nXYZ.Y()<<","<< nXYZ.Z()<<")])");
4672     }
4673     dumpFunctionEnd();
4674
4675     dumpFunction( SMESH_Comment("makeTmpFaces_") << i );
4676     dumpCmd( "faceId1 = mesh.NbElements()" );
4677     TopExp_Explorer fExp( _sdVec[i]._solid, TopAbs_FACE );
4678     for ( ; fExp.More(); fExp.Next() )
4679     {
4680       if ( const SMESHDS_SubMesh* sm = _sdVec[i]._proxyMesh->GetProxySubMesh( fExp.Current() ))
4681       {
4682         if ( sm->NbElements() == 0 ) continue;
4683         SMDS_ElemIteratorPtr fIt = sm->GetElements();
4684         while ( fIt->more())
4685         {
4686           const SMDS_MeshElement* e = fIt->next();
4687           SMESH_Comment cmd("mesh.AddFace([");
4688           for ( int j = 0; j < e->NbCornerNodes(); ++j )
4689             cmd << e->GetNode(j)->GetID() << (j+1 < e->NbCornerNodes() ? ",": "])");
4690           dumpCmd( cmd );
4691         }
4692       }
4693     }
4694     dumpCmd( "faceId2 = mesh.NbElements()" );
4695     dumpCmd( SMESH_Comment( "mesh.MakeGroup( 'tmpFaces_" ) << i << "',"
4696              << "SMESH.FACE, SMESH.FT_RangeOfIds,'=',"
4697              << "'%s-%s' % (faceId1+1, faceId2))");
4698     dumpFunctionEnd();
4699   }
4700 #endif
4701 }
4702
4703 //================================================================================
4704 /*!
4705  * \brief Find maximal _LayerEdge length (layer thickness) limited by geometry
4706  */
4707 //================================================================================
4708
4709 void _ViscousBuilder::computeGeomSize( _SolidData& data )
4710 {
4711   data._geomSize = Precision::Infinite();
4712   double intersecDist;
4713   const SMDS_MeshElement* face;
4714   SMESH_MesherHelper helper( *_mesh );
4715
4716   SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
4717     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
4718                                            data._proxyMesh->GetFaces( data._solid )));
4719
4720   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4721   {
4722     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4723     if ( eos._edges.empty() )
4724       continue;
4725     // get neighbor faces, intersection with which should not be considered since
4726     // collisions are avoided by means of smoothing
4727     set< TGeomID > neighborFaces;
4728     if ( eos._hyp.ToSmooth() )
4729     {
4730       SMESH_subMeshIteratorPtr subIt =
4731         eos._subMesh->getDependsOnIterator(/*includeSelf=*/eos.ShapeType() != TopAbs_FACE );
4732       while ( subIt->more() )
4733       {
4734         SMESH_subMesh* sm = subIt->next();
4735         PShapeIteratorPtr fIt = helper.GetAncestors( sm->GetSubShape(), *_mesh, TopAbs_FACE );
4736         while ( const TopoDS_Shape* face = fIt->next() )
4737           neighborFaces.insert( getMeshDS()->ShapeToIndex( *face ));
4738       }
4739     }
4740     // find intersections
4741     double thinkness = eos._hyp.GetTotalThickness();
4742     for ( size_t i = 0; i < eos._edges.size(); ++i )
4743     {
4744       if ( eos._edges[i]->_nodes.size() < 2 ) continue;
4745       eos._edges[i]->SetMaxLen( thinkness );
4746       eos._edges[i]->FindIntersection( *searcher, intersecDist, data._epsilon, eos, &face );
4747       if ( intersecDist > 0 && face )
4748       {
4749         data._geomSize = Min( data._geomSize, intersecDist );
4750         if ( !neighborFaces.count( face->getshapeId() ))
4751           eos[i]->SetMaxLen( Min( thinkness, intersecDist / ( face->GetID() < 0 ? 3. : 2. )));
4752       }
4753     }
4754   }
4755
4756   data._maxThickness = 0;
4757   data._minThickness = 1e100;
4758   list< const StdMeshers_ViscousLayers* >::iterator hyp = data._hyps.begin();
4759   for ( ; hyp != data._hyps.end(); ++hyp )
4760   {
4761     data._maxThickness = Max( data._maxThickness, (*hyp)->GetTotalThickness() );
4762     data._minThickness = Min( data._minThickness, (*hyp)->GetTotalThickness() );
4763   }
4764
4765   // Limit inflation step size by geometry size found by intersecting
4766   // normals of _LayerEdge's with mesh faces
4767   if ( data._stepSize > 0.3 * data._geomSize )
4768     limitStepSize( data, 0.3 * data._geomSize );
4769
4770   if ( data._stepSize > data._minThickness )
4771     limitStepSize( data, data._minThickness );
4772
4773
4774   // -------------------------------------------------------------------------
4775   // Detect _LayerEdge which can't intersect with opposite or neighbor layer,
4776   // so no need in detecting intersection at each inflation step
4777   // -------------------------------------------------------------------------
4778
4779   int nbSteps = data._maxThickness / data._stepSize;
4780   if ( nbSteps < 3 || nbSteps * data._n2eMap.size() < 100000 )
4781     return;
4782
4783   vector< const SMDS_MeshElement* > closeFaces;
4784   int nbDetected = 0;
4785
4786   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4787   {
4788     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4789     if ( eos._edges.empty() || eos.ShapeType() != TopAbs_FACE )
4790       continue;
4791
4792     for ( size_t i = 0; i < eos.size(); ++i )
4793     {
4794       SMESH_NodeXYZ p( eos[i]->_nodes[0] );
4795       double radius = data._maxThickness + 2 * eos[i]->_maxLen;
4796       closeFaces.clear();
4797       searcher->GetElementsInSphere( p, radius, SMDSAbs_Face, closeFaces );
4798
4799       bool toIgnore = true;
4800       for ( size_t iF = 0; iF < closeFaces.size()  && toIgnore; ++iF )
4801         if ( !( toIgnore = ( closeFaces[ iF ]->getshapeId() == eos._shapeID ||
4802                              data._ignoreFaceIds.count( closeFaces[ iF ]->getshapeId() ))))
4803         {
4804           // check if a _LayerEdge will inflate in a direction opposite to a direction
4805           // toward a close face
4806           bool allBehind = true;
4807           for ( int iN = 0; iN < closeFaces[ iF ]->NbCornerNodes()  && allBehind; ++iN )
4808           {
4809             SMESH_NodeXYZ pi( closeFaces[ iF ]->GetNode( iN ));
4810             allBehind = (( pi - p ) * eos[i]->_normal < 0.1 * data._stepSize );
4811           }
4812           toIgnore = allBehind;
4813         }
4814
4815
4816       if ( toIgnore ) // no need to detect intersection
4817       {
4818         eos[i]->Set( _LayerEdge::INTERSECTED );
4819         ++nbDetected;
4820       }
4821     }
4822   }
4823
4824   debugMsg( "Nb LE to intersect " << data._n2eMap.size()-nbDetected << ", ignore " << nbDetected );
4825
4826   return;
4827 }
4828
4829 //================================================================================
4830 /*!
4831  * \brief Increase length of _LayerEdge's to reach the required thickness of layers
4832  */
4833 //================================================================================
4834
4835 bool _ViscousBuilder::inflate(_SolidData& data)
4836 {
4837   SMESH_MesherHelper helper( *_mesh );
4838
4839   const double tgtThick = data._maxThickness;
4840
4841   if ( data._stepSize < 1. )
4842     data._epsilon = data._stepSize * 1e-7;
4843
4844   debugMsg( "-- geomSize = " << data._geomSize << ", stepSize = " << data._stepSize );
4845   _pyDump->Pause();
4846
4847   findCollisionEdges( data, helper );
4848
4849   limitMaxLenByCurvature( data, helper );
4850
4851   _pyDump->Resume();
4852
4853   // limit length of _LayerEdge's around MULTI_NORMAL _LayerEdge's
4854   for ( size_t i = 0; i < data._edgesOnShape.size(); ++i )
4855     if ( data._edgesOnShape[i].ShapeType() == TopAbs_VERTEX &&
4856          data._edgesOnShape[i]._edges.size() > 0 &&
4857          data._edgesOnShape[i]._edges[0]->Is( _LayerEdge::MULTI_NORMAL ))
4858     {
4859       data._edgesOnShape[i]._edges[0]->Unset( _LayerEdge::BLOCKED );
4860       data._edgesOnShape[i]._edges[0]->Block( data );
4861     }
4862
4863   const double safeFactor = ( 2*data._maxThickness < data._geomSize ) ? 1 : theThickToIntersection;
4864
4865   double avgThick = 0, curThick = 0, distToIntersection = Precision::Infinite();
4866   int nbSteps = 0, nbRepeats = 0;
4867   while ( avgThick < 0.99 )
4868   {
4869     // new target length
4870     double prevThick = curThick;
4871     curThick += data._stepSize;
4872     if ( curThick > tgtThick )
4873     {
4874       curThick = tgtThick + tgtThick*( 1.-avgThick ) * nbRepeats;
4875       nbRepeats++;
4876     }
4877
4878     double stepSize = curThick - prevThick;
4879     updateNormalsOfSmoothed( data, helper, nbSteps, stepSize ); // to ease smoothing
4880
4881     // Elongate _LayerEdge's
4882     dumpFunction(SMESH_Comment("inflate")<<data._index<<"_step"<<nbSteps); // debug
4883     for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4884     {
4885       _EdgesOnShape& eos = data._edgesOnShape[iS];
4886       if ( eos._edges.empty() ) continue;
4887
4888       const double shapeCurThick = Min( curThick, eos._hyp.GetTotalThickness() );
4889       for ( size_t i = 0; i < eos._edges.size(); ++i )
4890       {
4891         eos._edges[i]->SetNewLength( shapeCurThick, eos, helper );
4892       }
4893     }
4894     dumpFunctionEnd();
4895
4896     if ( !updateNormals( data, helper, nbSteps, stepSize )) // to avoid collisions
4897       return false;
4898
4899     // Improve and check quality
4900     if ( !smoothAndCheck( data, nbSteps, distToIntersection ))
4901     {
4902       if ( nbSteps > 0 )
4903       {
4904 #ifdef __NOT_INVALIDATE_BAD_SMOOTH
4905         debugMsg("NOT INVALIDATED STEP!");
4906         return error("Smoothing failed", data._index);
4907 #endif
4908         dumpFunction(SMESH_Comment("invalidate")<<data._index<<"_step"<<nbSteps); // debug
4909         for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4910         {
4911           _EdgesOnShape& eos = data._edgesOnShape[iS];
4912           for ( size_t i = 0; i < eos._edges.size(); ++i )
4913             eos._edges[i]->InvalidateStep( nbSteps+1, eos );
4914         }
4915         dumpFunctionEnd();
4916       }
4917       break; // no more inflating possible
4918     }
4919     nbSteps++;
4920
4921     // Evaluate achieved thickness
4922     avgThick = 0;
4923     int nbActiveEdges = 0;
4924     for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4925     {
4926       _EdgesOnShape& eos = data._edgesOnShape[iS];
4927       if ( eos._edges.empty() ) continue;
4928
4929       const double shapeTgtThick = eos._hyp.GetTotalThickness();
4930       for ( size_t i = 0; i < eos._edges.size(); ++i )
4931       {
4932         if ( eos._edges[i]->_nodes.size() > 1 )
4933           avgThick    += Min( 1., eos._edges[i]->_len / shapeTgtThick );
4934         else
4935           avgThick    += shapeTgtThick;
4936         nbActiveEdges += ( ! eos._edges[i]->Is( _LayerEdge::BLOCKED ));
4937       }
4938     }
4939     avgThick /= data._n2eMap.size();
4940     debugMsg( "-- Thickness " << curThick << " ("<< avgThick*100 << "%) reached" );
4941
4942 #ifdef BLOCK_INFLATION
4943     if ( nbActiveEdges == 0 )
4944     {
4945       debugMsg( "-- Stop inflation since all _LayerEdge's BLOCKED " );
4946       break;
4947     }
4948 #else
4949     if ( distToIntersection < tgtThick * avgThick * safeFactor && avgThick < 0.9 )
4950     {
4951       debugMsg( "-- Stop inflation since "
4952                 << " distToIntersection( "<<distToIntersection<<" ) < avgThick( "
4953                 << tgtThick * avgThick << " ) * " << safeFactor );
4954       break;
4955     }
4956 #endif
4957
4958     // new step size
4959     limitStepSize( data, 0.25 * distToIntersection );
4960     if ( data._stepSizeNodes[0] )
4961       data._stepSize = data._stepSizeCoeff *
4962         SMESH_TNodeXYZ(data._stepSizeNodes[0]).Distance(data._stepSizeNodes[1]);
4963
4964   } // while ( avgThick < 0.99 )
4965
4966   if ( nbSteps == 0 )
4967     return error("failed at the very first inflation step", data._index);
4968
4969   if ( avgThick < 0.99 )
4970   {
4971     if ( !data._proxyMesh->_warning || data._proxyMesh->_warning->IsOK() )
4972     {
4973       data._proxyMesh->_warning.reset
4974         ( new SMESH_ComputeError (COMPERR_WARNING,
4975                                   SMESH_Comment("Thickness ") << tgtThick <<
4976                                   " of viscous layers not reached,"
4977                                   " average reached thickness is " << avgThick*tgtThick));
4978     }
4979   }
4980
4981   // Restore position of src nodes moved by inflation on _noShrinkShapes
4982   dumpFunction(SMESH_Comment("restoNoShrink_So")<<data._index); // debug
4983   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4984   {
4985     _EdgesOnShape& eos = data._edgesOnShape[iS];
4986     if ( !eos._edges.empty() && eos._edges[0]->_nodes.size() == 1 )
4987       for ( size_t i = 0; i < eos._edges.size(); ++i )
4988       {
4989         restoreNoShrink( *eos._edges[ i ] );
4990       }
4991   }
4992   dumpFunctionEnd();
4993
4994   return safeFactor > 0; // == true (avoid warning: unused variable 'safeFactor')
4995 }
4996
4997 //================================================================================
4998 /*!
4999  * \brief Improve quality of layer inner surface and check intersection
5000  */
5001 //================================================================================
5002
5003 bool _ViscousBuilder::smoothAndCheck(_SolidData& data,
5004                                      const int   infStep,
5005                                      double &    distToIntersection)
5006 {
5007   if ( data._nbShapesToSmooth == 0 )
5008     return true; // no shapes needing smoothing
5009
5010   bool moved, improved;
5011   double vol;
5012   vector< _LayerEdge* >    movedEdges, badEdges;
5013   vector< _EdgesOnShape* > eosC1; // C1 continues shapes
5014   vector< bool >           isConcaveFace;
5015
5016   SMESH_MesherHelper helper(*_mesh);
5017   Handle(ShapeAnalysis_Surface) surface;
5018   TopoDS_Face F;
5019
5020   for ( int isFace = 0; isFace < 2; ++isFace ) // smooth on [ EDGEs, FACEs ]
5021   {
5022     const TopAbs_ShapeEnum shapeType = isFace ? TopAbs_FACE : TopAbs_EDGE;
5023
5024     for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
5025     {
5026       _EdgesOnShape& eos = data._edgesOnShape[ iS ];
5027       if ( !eos._toSmooth ||
5028            eos.ShapeType() != shapeType ||
5029            eos._edges.empty() )
5030         continue;
5031
5032       // already smoothed?
5033       // bool toSmooth = ( eos._edges[ 0 ]->NbSteps() >= infStep+1 );
5034       // if ( !toSmooth ) continue;
5035
5036       if ( !eos._hyp.ToSmooth() )
5037       {
5038         // smooth disabled by the user; check validy only
5039         if ( !isFace ) continue;
5040         badEdges.clear();
5041         for ( size_t i = 0; i < eos._edges.size(); ++i )
5042         {
5043           _LayerEdge* edge = eos._edges[i];
5044           for ( size_t iF = 0; iF < edge->_simplices.size(); ++iF )
5045             if ( !edge->_simplices[iF].IsForward( edge->_nodes[0], edge->_pos.back(), vol ))
5046             {
5047               // debugMsg( "-- Stop inflation. Bad simplex ("
5048               //           << " "<< edge->_nodes[0]->GetID()
5049               //           << " "<< edge->_nodes.back()->GetID()
5050               //           << " "<< edge->_simplices[iF]._nPrev->GetID()
5051               //           << " "<< edge->_simplices[iF]._nNext->GetID() << " ) ");
5052               // return false;
5053               badEdges.push_back( edge );
5054             }
5055         }
5056         if ( !badEdges.empty() )
5057         {
5058           eosC1.resize(1);
5059           eosC1[0] = &eos;
5060           int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5061           if ( nbBad > 0 )
5062             return false;
5063         }
5064         continue; // goto the next EDGE or FACE
5065       }
5066
5067       // prepare data
5068       if ( eos.SWOLType() == TopAbs_FACE )
5069       {
5070         if ( !F.IsSame( eos._sWOL )) {
5071           F = TopoDS::Face( eos._sWOL );
5072           helper.SetSubShape( F );
5073           surface = helper.GetSurface( F );
5074         }
5075       }
5076       else
5077       {
5078         F.Nullify(); surface.Nullify();
5079       }
5080       const TGeomID sInd = eos._shapeID;
5081
5082       // perform smoothing
5083
5084       if ( eos.ShapeType() == TopAbs_EDGE )
5085       {
5086         dumpFunction(SMESH_Comment("smooth")<<data._index << "_Ed"<<sInd <<"_InfStep"<<infStep);
5087
5088         if ( !eos._edgeSmoother->Perform( data, surface, F, helper ))
5089         {
5090           // smooth on EDGE's (normally we should not get here)
5091           int step = 0;
5092           do {
5093             moved = false;
5094             for ( size_t i = 0; i < eos._edges.size(); ++i )
5095             {
5096               moved |= eos._edges[i]->SmoothOnEdge( surface, F, helper );
5097             }
5098             dumpCmd( SMESH_Comment("# end step ")<<step);
5099           }
5100           while ( moved && step++ < 5 );
5101         }
5102         dumpFunctionEnd();
5103       }
5104
5105       else // smooth on FACE
5106       {
5107         eosC1.clear();
5108         eosC1.push_back( & eos );
5109         eosC1.insert( eosC1.end(), eos._eosC1.begin(), eos._eosC1.end() );
5110
5111         movedEdges.clear();
5112         isConcaveFace.resize( eosC1.size() );
5113         for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5114         {
5115           isConcaveFace[ iEOS ] = data._concaveFaces.count( eosC1[ iEOS ]->_shapeID  );
5116
5117           if ( eosC1[ iEOS ]->_mapper2D )
5118           {
5119             // compute node position by boundary node position in structured mesh
5120             dumpFunction(SMESH_Comment("map2dS")<<data._index<<"_Fa"<<eos._shapeID
5121                          <<"_InfStep"<<infStep);
5122
5123             eosC1[ iEOS ]->_mapper2D->ComputeNodePositions();
5124
5125             for ( _LayerEdge* le : eosC1[ iEOS ]->_edges )
5126               le->_pos.back() = SMESH_NodeXYZ( le->_nodes.back() );
5127
5128             dumpFunctionEnd();
5129           }
5130           else
5131           {
5132             for ( _LayerEdge* le : eosC1[ iEOS ]->_edges )
5133               if ( le->Is( _LayerEdge::MOVED ) ||
5134                    le->Is( _LayerEdge::NEAR_BOUNDARY ))
5135                 movedEdges.push_back( le );
5136           }
5137           makeOffsetSurface( *eosC1[ iEOS ], helper );
5138         }
5139
5140         int step = 0, stepLimit = 5, nbBad = 0;
5141         while (( ++step <= stepLimit ) || improved )
5142         {
5143           int oldBadNb = nbBad;
5144           badEdges.clear();
5145
5146 #ifdef INCREMENTAL_SMOOTH
5147           // smooth moved only
5148           if ( !movedEdges.empty() )
5149             dumpFunction(SMESH_Comment("smooth")<<data._index<<"_Fa"<<sInd
5150                          <<"_InfStep"<<infStep<<"_"<<step); // debug
5151           bool findBest = false; // ( step == stepLimit );
5152           for ( size_t i = 0; i < movedEdges.size(); ++i )
5153           {
5154             movedEdges[i]->Unset( _LayerEdge::SMOOTHED );
5155             if ( movedEdges[i]->Smooth( step, findBest, movedEdges ) > 0 )
5156               badEdges.push_back( movedEdges[i] );
5157           }
5158 #else
5159           // smooth all
5160           dumpFunction(SMESH_Comment("smooth")<<data._index<<"_Fa"<<sInd
5161                        <<"_InfStep"<<infStep<<"_"<<step); // debug
5162           bool findBest = ( step == stepLimit || isConcaveFace[ iEOS ]);
5163           for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5164           {
5165             if ( eosC1[ iEOS ]->_mapper2D )
5166               continue;
5167             vector< _LayerEdge* > & edges = eosC1[ iEOS ]->_edges;
5168             for ( size_t i = 0; i < edges.size(); ++i )
5169             {
5170               edges[i]->Unset( _LayerEdge::SMOOTHED );
5171               if ( edges[i]->Smooth( step, findBest, false ) > 0 )
5172                 badEdges.push_back( eos._edges[i] );
5173             }
5174           }
5175 #endif
5176           nbBad = badEdges.size();
5177
5178           if ( nbBad > 0 )
5179             debugMsg(SMESH_Comment("nbBad = ") << nbBad );
5180
5181           if ( !badEdges.empty() && step >= stepLimit / 2 )
5182           {
5183             if ( badEdges[0]->Is( _LayerEdge::ON_CONCAVE_FACE ))
5184               stepLimit = 9;
5185
5186             // resolve hard smoothing situation around concave VERTEXes
5187             for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5188             {
5189               vector< _EdgesOnShape* > & eosCoVe = eosC1[ iEOS ]->_eosConcaVer;
5190               for ( size_t i = 0; i < eosCoVe.size(); ++i )
5191                 eosCoVe[i]->_edges[0]->MoveNearConcaVer( eosCoVe[i], eosC1[ iEOS ],
5192                                                          step, badEdges );
5193             }
5194             // look for the best smooth of _LayerEdge's neighboring badEdges
5195             nbBad = 0;
5196             for ( size_t i = 0; i < badEdges.size(); ++i )
5197             {
5198               _LayerEdge* ledge = badEdges[i];
5199               for ( size_t iN = 0; iN < ledge->_neibors.size(); ++iN )
5200               {
5201                 ledge->_neibors[iN]->Unset( _LayerEdge::SMOOTHED );
5202                 nbBad += ledge->_neibors[iN]->Smooth( step, true, /*findBest=*/true );
5203               }
5204               ledge->Unset( _LayerEdge::SMOOTHED );
5205               nbBad += ledge->Smooth( step, true, /*findBest=*/true );
5206             }
5207             debugMsg(SMESH_Comment("nbBad = ") << nbBad );
5208           }
5209
5210           if ( nbBad == oldBadNb  &&
5211                nbBad > 0 &&
5212                step < stepLimit ) // smooth w/o check of validity
5213           {
5214             dumpFunctionEnd();
5215             dumpFunction(SMESH_Comment("smoothWoCheck")<<data._index<<"_Fa"<<sInd
5216                          <<"_InfStep"<<infStep<<"_"<<step); // debug
5217             for ( size_t i = 0; i < movedEdges.size(); ++i )
5218             {
5219               movedEdges[i]->SmoothWoCheck();
5220             }
5221             if ( stepLimit < 9 )
5222               stepLimit++;
5223           }
5224
5225           improved = ( nbBad < oldBadNb );
5226
5227           dumpFunctionEnd();
5228
5229           if (( step % 3 == 1 ) || ( nbBad > 0 && step >= stepLimit / 2 ))
5230             for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5231             {
5232               putOnOffsetSurface( *eosC1[ iEOS ], infStep, eosC1, step, /*moveAll=*/step == 1 );
5233             }
5234
5235         } // smoothing steps
5236
5237         // project -- to prevent intersections or to fix bad simplices
5238         for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5239         {
5240           if ( ! eosC1[ iEOS ]->_eosConcaVer.empty() || nbBad > 0 )
5241             putOnOffsetSurface( *eosC1[ iEOS ], -infStep, eosC1 );
5242         }
5243
5244         //if ( !badEdges.empty() )
5245         {
5246           badEdges.clear();
5247           for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5248           {
5249             for ( size_t i = 0; i < eosC1[ iEOS ]->_edges.size(); ++i )
5250             {
5251               if ( !eosC1[ iEOS ]->_sWOL.IsNull() ) continue;
5252
5253               _LayerEdge* edge = eosC1[ iEOS ]->_edges[i];
5254               edge->CheckNeiborsOnBoundary( & badEdges );
5255               if (( nbBad > 0 ) ||
5256                   ( edge->Is( _LayerEdge::BLOCKED ) && edge->Is( _LayerEdge::NEAR_BOUNDARY )))
5257               {
5258                 SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
5259                 gp_XYZ        prevXYZ = edge->PrevCheckPos();
5260                 for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5261                   if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
5262                   {
5263                     debugMsg("Bad simplex ( " << edge->_nodes[0]->GetID()
5264                              << " "<< tgtXYZ._node->GetID()
5265                              << " "<< edge->_simplices[j]._nPrev->GetID()
5266                              << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
5267                     badEdges.push_back( edge );
5268                     break;
5269                   }
5270               }
5271             }
5272           }
5273
5274           // try to fix bad simplices by removing the last inflation step of some _LayerEdge's
5275           nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5276
5277           if ( nbBad > 0 )
5278             return false;
5279         }
5280
5281       } // // smooth on FACE's
5282     } // loop on shapes
5283   } // smooth on [ EDGEs, FACEs ]
5284
5285   // Check orientation of simplices of _LayerEdge's on EDGEs and VERTEXes
5286   eosC1.resize(1);
5287   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
5288   {
5289     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
5290     if ( eos.ShapeType() == TopAbs_FACE ||
5291          eos._edges.empty() ||
5292          !eos._sWOL.IsNull() )
5293       continue;
5294
5295     badEdges.clear();
5296     for ( size_t i = 0; i < eos._edges.size(); ++i )
5297     {
5298       _LayerEdge*      edge = eos._edges[i];
5299       if ( edge->_nodes.size() < 2 ) continue;
5300       SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
5301       //SMESH_TNodeXYZ prevXYZ = edge->_nodes[0];
5302       gp_XYZ        prevXYZ = edge->PrevCheckPos( &eos );
5303       //const gp_XYZ& prevXYZ = edge->PrevPos();
5304       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5305         if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
5306         {
5307           debugMsg("Bad simplex on bnd ( " << edge->_nodes[0]->GetID()
5308                    << " "<< tgtXYZ._node->GetID()
5309                    << " "<< edge->_simplices[j]._nPrev->GetID()
5310                    << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
5311           badEdges.push_back( edge );
5312           break;
5313         }
5314     }
5315
5316     // try to fix bad simplices by removing the last inflation step of some _LayerEdge's
5317     eosC1[0] = &eos;
5318     int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5319     if ( nbBad > 0 )
5320       return false;
5321   }
5322
5323
5324   // Check if the last segments of _LayerEdge intersects 2D elements;
5325   // checked elements are either temporary faces or faces on surfaces w/o the layers
5326
5327   SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
5328     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
5329                                            data._proxyMesh->GetFaces( data._solid )) );
5330
5331 #ifdef BLOCK_INFLATION
5332   const bool toBlockInfaltion = true;
5333 #else
5334   const bool toBlockInfaltion = false;
5335 #endif
5336   distToIntersection = Precision::Infinite();
5337   double dist;
5338   const SMDS_MeshElement* intFace = 0;
5339   const SMDS_MeshElement* closestFace = 0;
5340   _LayerEdge* le = 0;
5341   bool is1stBlocked = true; // dbg
5342   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
5343   {
5344     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
5345     if ( eos._edges.empty() || !eos._sWOL.IsNull() )
5346       continue;
5347     for ( size_t i = 0; i < eos._edges.size(); ++i )
5348     {
5349       if ( eos._edges[i]->Is( _LayerEdge::INTERSECTED ) ||
5350            eos._edges[i]->Is( _LayerEdge::MULTI_NORMAL ))
5351         continue;
5352       if ( eos._edges[i]->FindIntersection( *searcher, dist, data._epsilon, eos, &intFace ))
5353       {
5354         return false;
5355         // commented due to "Illegal hash-positionPosition" error in NETGEN
5356         // on Debian60 on viscous_layers_01/B2 case
5357         // Collision; try to deflate _LayerEdge's causing it
5358         // badEdges.clear();
5359         // badEdges.push_back( eos._edges[i] );
5360         // eosC1[0] = & eos;
5361         // int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5362         // if ( nbBad > 0 )
5363         //   return false;
5364
5365         // badEdges.clear();
5366         // if ( _EdgesOnShape* eof = data.GetShapeEdges( intFace->getshapeId() ))
5367         // {
5368         //   if ( const _TmpMeshFace* f = dynamic_cast< const _TmpMeshFace*>( intFace ))
5369         //   {
5370         //     const SMDS_MeshElement* srcFace =
5371         //       eof->_subMesh->GetSubMeshDS()->GetElement( f->getIdInShape() );
5372         //     SMDS_ElemIteratorPtr nIt = srcFace->nodesIterator();
5373         //     while ( nIt->more() )
5374         //     {
5375         //       const SMDS_MeshNode* srcNode = static_cast<const SMDS_MeshNode*>( nIt->next() );
5376         //       TNode2Edge::iterator n2e = data._n2eMap.find( srcNode );
5377         //       if ( n2e != data._n2eMap.end() )
5378         //         badEdges.push_back( n2e->second );
5379         //     }
5380         //     eosC1[0] = eof;
5381         //     nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5382         //     if ( nbBad > 0 )
5383         //       return false;
5384         //   }
5385         // }
5386         // if ( eos._edges[i]->FindIntersection( *searcher, dist, data._epsilon, eos, &intFace ))
5387         //   return false;
5388         // else
5389         //   continue;
5390       }
5391       if ( !intFace )
5392       {
5393         SMESH_Comment msg("Invalid? normal at node "); msg << eos._edges[i]->_nodes[0]->GetID();
5394         debugMsg( msg );
5395         continue;
5396       }
5397
5398       const bool isShorterDist = ( distToIntersection > dist );
5399       if ( toBlockInfaltion || isShorterDist )
5400       {
5401         // ignore intersection of a _LayerEdge based on a _ConvexFace with a face
5402         // lying on this _ConvexFace
5403         if ( _ConvexFace* convFace = data.GetConvexFace( intFace->getshapeId() ))
5404           if ( convFace->_isTooCurved && convFace->_subIdToEOS.count ( eos._shapeID ))
5405             continue;
5406
5407         // ignore intersection of a _LayerEdge based on a FACE with an element on this FACE
5408         // ( avoid limiting the thickness on the case of issue 22576)
5409         if ( intFace->getshapeId() == eos._shapeID  )
5410           continue;
5411
5412         // ignore intersection with intFace of an adjacent FACE
5413         if ( dist > 0.01 * eos._edges[i]->_len )
5414         {
5415           bool toIgnore = false;
5416           if (  eos._toSmooth )
5417           {
5418             const TopoDS_Shape& S = getMeshDS()->IndexToShape( intFace->getshapeId() );
5419             if ( !S.IsNull() && S.ShapeType() == TopAbs_FACE )
5420             {
5421               TopExp_Explorer sub( eos._shape,
5422                                    eos.ShapeType() == TopAbs_FACE ? TopAbs_EDGE : TopAbs_VERTEX );
5423               for ( ; !toIgnore && sub.More(); sub.Next() )
5424                 // is adjacent - has a common EDGE or VERTEX
5425                 toIgnore = ( helper.IsSubShape( sub.Current(), S ));
5426
5427               if ( toIgnore ) // check angle between normals
5428               {
5429                 gp_XYZ normal;
5430                 if ( SMESH_MeshAlgos::FaceNormal( intFace, normal, /*normalized=*/true ))
5431                   toIgnore  = ( normal * eos._edges[i]->_normal > -0.5 );
5432               }
5433             }
5434           }
5435           if ( !toIgnore ) // check if the edge is a neighbor of intFace
5436           {
5437             for ( size_t iN = 0; !toIgnore &&  iN < eos._edges[i]->_neibors.size(); ++iN )
5438             {
5439               int nInd = intFace->GetNodeIndex( eos._edges[i]->_neibors[ iN ]->_nodes.back() );
5440               toIgnore = ( nInd >= 0 );
5441             }
5442           }
5443           if ( toIgnore )
5444             continue;
5445         }
5446
5447         // intersection not ignored
5448
5449         if ( toBlockInfaltion &&
5450              dist < ( eos._edges[i]->_len * theThickToIntersection ))
5451         {
5452           if ( is1stBlocked ) { is1stBlocked = false; // debug
5453             dumpFunction(SMESH_Comment("blockIntersected") <<data._index<<"_InfStep"<<infStep);
5454           }
5455           eos._edges[i]->Set( _LayerEdge::INTERSECTED ); // not to intersect
5456           eos._edges[i]->Block( data );                  // not to inflate
5457
5458           //if ( _EdgesOnShape* eof = data.GetShapeEdges( intFace->getshapeId() ))
5459           {
5460             // block _LayerEdge's, on top of which intFace is
5461             if ( const _TmpMeshFace* f = dynamic_cast< const _TmpMeshFace*>( intFace ))
5462             {
5463               const SMDS_MeshElement* srcFace = f->_srcFace;
5464               SMDS_ElemIteratorPtr        nIt = srcFace->nodesIterator();
5465               while ( nIt->more() )
5466               {
5467                 const SMDS_MeshNode* srcNode = static_cast<const SMDS_MeshNode*>( nIt->next() );
5468                 TNode2Edge::iterator n2e = data._n2eMap.find( srcNode );
5469                 if ( n2e != data._n2eMap.end() )
5470                   n2e->second->Block( data );
5471               }
5472             }
5473           }
5474         }
5475
5476         if ( isShorterDist )
5477         {
5478           distToIntersection = dist;
5479           le = eos._edges[i];
5480           closestFace = intFace;
5481         }
5482
5483       } // if ( toBlockInfaltion || isShorterDist )
5484     } // loop on eos._edges
5485   } // loop on data._edgesOnShape
5486
5487   if ( !is1stBlocked )
5488   {
5489     dumpFunctionEnd();
5490   }
5491
5492   if ( closestFace && le )
5493   {
5494 #ifdef __myDEBUG
5495     SMDS_MeshElement::iterator nIt = closestFace->begin_nodes();
5496     cout << "#Shortest distance: _LayerEdge nodes: tgt " << le->_nodes.back()->GetID()
5497          << " src " << le->_nodes[0]->GetID()<< ", intersection with face ("
5498          << (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()
5499          << ") distance = " << distToIntersection<< endl;
5500 #endif
5501   }
5502
5503   return true;
5504 }
5505
5506 //================================================================================
5507 /*!
5508  * \brief try to fix bad simplices by removing the last inflation step of some _LayerEdge's
5509  *  \param [in,out] badSmooEdges - _LayerEdge's to fix
5510  *  \return int - resulting nb of bad _LayerEdge's
5511  */
5512 //================================================================================
5513
5514 int _ViscousBuilder::invalidateBadSmooth( _SolidData&               data,
5515                                           SMESH_MesherHelper&       helper,
5516                                           vector< _LayerEdge* >&    badSmooEdges,
5517                                           vector< _EdgesOnShape* >& eosC1,
5518                                           const int                 infStep )
5519 {
5520   if ( badSmooEdges.empty() || infStep == 0 ) return 0;
5521
5522   dumpFunction(SMESH_Comment("invalidateBadSmooth")<<"_S"<<eosC1[0]->_shapeID<<"_InfStep"<<infStep);
5523
5524   enum {
5525     INVALIDATED   = _LayerEdge::UNUSED_FLAG,
5526     TO_INVALIDATE = _LayerEdge::UNUSED_FLAG * 2,
5527     ADDED         = _LayerEdge::UNUSED_FLAG * 4
5528   };
5529   data.UnmarkEdges( TO_INVALIDATE & INVALIDATED & ADDED );
5530
5531   double vol;
5532   bool haveInvalidated = true;
5533   while ( haveInvalidated )
5534   {
5535     haveInvalidated = false;
5536     for ( size_t i = 0; i < badSmooEdges.size(); ++i )
5537     {
5538       _LayerEdge*   edge = badSmooEdges[i];
5539       _EdgesOnShape* eos = data.GetShapeEdges( edge );
5540       edge->Set( ADDED );
5541       bool invalidated = false;
5542       if ( edge->Is( TO_INVALIDATE ) && edge->NbSteps() > 1 )
5543       {
5544         edge->InvalidateStep( edge->NbSteps(), *eos, /*restoreLength=*/true );
5545         edge->Block( data );
5546         edge->Set( INVALIDATED );
5547         edge->Unset( TO_INVALIDATE );
5548         invalidated = true;
5549         haveInvalidated = true;
5550       }
5551
5552       // look for _LayerEdge's of bad _simplices
5553       int nbBad = 0;
5554       SMESH_TNodeXYZ tgtXYZ  = edge->_nodes.back();
5555       gp_XYZ        prevXYZ1 = edge->PrevCheckPos( eos );
5556       //const gp_XYZ& prevXYZ2 = edge->PrevPos();
5557       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5558       {
5559         if (( edge->_simplices[j].IsForward( &prevXYZ1, &tgtXYZ, vol ))/* &&
5560             ( &prevXYZ1 == &prevXYZ2 || edge->_simplices[j].IsForward( &prevXYZ2, &tgtXYZ, vol ))*/)
5561           continue;
5562
5563         bool isBad = true;
5564         _LayerEdge* ee[2] = { 0,0 };
5565         for ( size_t iN = 0; iN < edge->_neibors.size() &&   !ee[1]  ; ++iN )
5566           if ( edge->_simplices[j].Includes( edge->_neibors[iN]->_nodes.back() ))
5567             ee[ ee[0] != 0 ] = edge->_neibors[iN];
5568
5569         int maxNbSteps = Max( ee[0]->NbSteps(), ee[1]->NbSteps() );
5570         while ( maxNbSteps > edge->NbSteps() && isBad )
5571         {
5572           --maxNbSteps;
5573           for ( int iE = 0; iE < 2; ++iE )
5574           {
5575             if ( ee[ iE ]->NbSteps() > maxNbSteps &&
5576                  ee[ iE ]->NbSteps() > 1 )
5577             {
5578               _EdgesOnShape* eos = data.GetShapeEdges( ee[ iE ] );
5579               ee[ iE ]->InvalidateStep( ee[ iE ]->NbSteps(), *eos, /*restoreLength=*/true );
5580               ee[ iE ]->Block( data );
5581               ee[ iE ]->Set( INVALIDATED );
5582               haveInvalidated = true;
5583             }
5584           }
5585           if (( edge->_simplices[j].IsForward( &prevXYZ1, &tgtXYZ, vol )) /*&&
5586               ( &prevXYZ1 == &prevXYZ2 || edge->_simplices[j].IsForward( &prevXYZ2, &tgtXYZ, vol ))*/)
5587             isBad = false;
5588         }
5589         nbBad += isBad;
5590         if ( !ee[0]->Is( ADDED )) badSmooEdges.push_back( ee[0] );
5591         if ( !ee[1]->Is( ADDED )) badSmooEdges.push_back( ee[1] );
5592         ee[0]->Set( ADDED );
5593         ee[1]->Set( ADDED );
5594         if ( isBad )
5595         {
5596           ee[0]->Set( TO_INVALIDATE );
5597           ee[1]->Set( TO_INVALIDATE );
5598         }
5599       }
5600
5601       if ( !invalidated &&  nbBad > 0  &&  edge->NbSteps() > 1 )
5602       {
5603         _EdgesOnShape* eos = data.GetShapeEdges( edge );
5604         edge->InvalidateStep( edge->NbSteps(), *eos, /*restoreLength=*/true );
5605         edge->Block( data );
5606         edge->Set( INVALIDATED );
5607         edge->Unset( TO_INVALIDATE );
5608         haveInvalidated = true;
5609       }
5610     } // loop on badSmooEdges
5611   } // while ( haveInvalidated )
5612
5613   // re-smooth on analytical EDGEs
5614   for ( size_t i = 0; i < badSmooEdges.size(); ++i )
5615   {
5616     _LayerEdge* edge = badSmooEdges[i];
5617     if ( !edge->Is( INVALIDATED )) continue;
5618
5619     _EdgesOnShape* eos = data.GetShapeEdges( edge );
5620     if ( eos->ShapeType() == TopAbs_VERTEX )
5621     {
5622       PShapeIteratorPtr eIt = helper.GetAncestors( eos->_shape, *_mesh, TopAbs_EDGE );
5623       while ( const TopoDS_Shape* e = eIt->next() )
5624         if ( _EdgesOnShape* eoe = data.GetShapeEdges( *e ))
5625           if ( eoe->_edgeSmoother && eoe->_edgeSmoother->isAnalytic() )
5626           {
5627             // TopoDS_Face F; Handle(ShapeAnalysis_Surface) surface;
5628             // if ( eoe->SWOLType() == TopAbs_FACE ) {
5629             //   F       = TopoDS::Face( eoe->_sWOL );
5630             //   surface = helper.GetSurface( F );
5631             // }
5632             // eoe->_edgeSmoother->Perform( data, surface, F, helper );
5633             eoe->_edgeSmoother->_anaCurve.Nullify();
5634           }
5635     }
5636   }
5637
5638
5639   // check result of invalidation
5640
5641   int nbBad = 0;
5642   for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5643   {
5644     for ( size_t i = 0; i < eosC1[ iEOS ]->_edges.size(); ++i )
5645     {
5646       if ( !eosC1[ iEOS ]->_sWOL.IsNull() ) continue;
5647       _LayerEdge*      edge = eosC1[ iEOS ]->_edges[i];
5648       SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
5649       gp_XYZ        prevXYZ = edge->PrevCheckPos( eosC1[ iEOS ]);
5650       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5651         if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
5652         {
5653           ++nbBad;
5654           debugMsg("Bad simplex remains ( " << edge->_nodes[0]->GetID()
5655                    << " "<< tgtXYZ._node->GetID()
5656                    << " "<< edge->_simplices[j]._nPrev->GetID()
5657                    << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
5658         }
5659     }
5660   }
5661   dumpFunctionEnd();
5662
5663   return nbBad;
5664 }
5665
5666 //================================================================================
5667 /*!
5668  * \brief Create an offset surface
5669  */
5670 //================================================================================
5671
5672 void _ViscousBuilder::makeOffsetSurface( _EdgesOnShape& eos, SMESH_MesherHelper& helper )
5673 {
5674   if ( eos._offsetSurf.IsNull() ||
5675        eos._edgeForOffset == 0 ||
5676        eos._edgeForOffset->Is( _LayerEdge::BLOCKED ))
5677     return;
5678
5679   Handle(ShapeAnalysis_Surface) baseSurface = helper.GetSurface( TopoDS::Face( eos._shape ));
5680
5681   // find offset
5682   gp_Pnt   tgtP = SMESH_TNodeXYZ( eos._edgeForOffset->_nodes.back() );
5683   /*gp_Pnt2d uv=*/baseSurface->ValueOfUV( tgtP, Precision::Confusion() );
5684   eos._offsetValue = baseSurface->Gap();
5685
5686   eos._offsetSurf.Nullify();
5687
5688   try
5689   {
5690     BRepOffsetAPI_MakeOffsetShape offsetMaker;
5691     offsetMaker.PerformByJoin( eos._shape, -eos._offsetValue, Precision::Confusion() );
5692     if ( !offsetMaker.IsDone() ) return;
5693
5694     TopExp_Explorer fExp( offsetMaker.Shape(), TopAbs_FACE );
5695     if ( !fExp.More() ) return;
5696
5697     TopoDS_Face F = TopoDS::Face( fExp.Current() );
5698     Handle(Geom_Surface) surf = BRep_Tool::Surface( F );
5699     if ( surf.IsNull() ) return;
5700
5701     eos._offsetSurf = new ShapeAnalysis_Surface( surf );
5702   }
5703   catch ( Standard_Failure& )
5704   {
5705   }
5706 }
5707
5708 //================================================================================
5709 /*!
5710  * \brief Put nodes of a curved FACE to its offset surface
5711  */
5712 //================================================================================
5713
5714 void _ViscousBuilder::putOnOffsetSurface( _EdgesOnShape&            eos,
5715                                           int                       infStep,
5716                                           vector< _EdgesOnShape* >& eosC1,
5717                                           int                       smooStep,
5718                                           int                       moveAll )
5719 {
5720   _EdgesOnShape * eof = & eos;
5721   if ( eos.ShapeType() != TopAbs_FACE ) // eos is a boundary of C1 FACE, look for the FACE eos
5722   {
5723     eof = 0;
5724     for ( size_t i = 0; i < eosC1.size() && !eof; ++i )
5725     {
5726       if ( eosC1[i]->_offsetSurf.IsNull() ||
5727            eosC1[i]->ShapeType() != TopAbs_FACE ||
5728            eosC1[i]->_edgeForOffset == 0 ||
5729            eosC1[i]->_edgeForOffset->Is( _LayerEdge::BLOCKED ))
5730         continue;
5731       if ( SMESH_MesherHelper::IsSubShape( eos._shape, eosC1[i]->_shape ))
5732         eof = eosC1[i];
5733     }
5734   }
5735   if ( !eof ||
5736        eof->_offsetSurf.IsNull() ||
5737        eof->ShapeType() != TopAbs_FACE ||
5738        eof->_edgeForOffset == 0 ||
5739        eof->_edgeForOffset->Is( _LayerEdge::BLOCKED ))
5740     return;
5741
5742   double preci = BRep_Tool::Tolerance( TopoDS::Face( eof->_shape )), vol;
5743   bool neighborHasRiskySWOL = false;
5744   for ( size_t i = 0; i < eos._edges.size(); ++i )
5745   {
5746     _LayerEdge* edge = eos._edges[i];
5747     edge->Unset( _LayerEdge::MARKED );
5748     if ( edge->Is( _LayerEdge::BLOCKED ) || !edge->_curvature )
5749       continue;
5750     if ( moveAll == _LayerEdge::UPD_NORMAL_CONV )
5751     {
5752       if ( !edge->Is( _LayerEdge::UPD_NORMAL_CONV ))
5753         continue;
5754     }
5755     else if ( moveAll == _LayerEdge::RISKY_SWOL )
5756     {
5757       if ( !edge->Is( _LayerEdge::RISKY_SWOL ) ||
5758            edge->_cosin < 0 )
5759         continue;
5760     }
5761     else if ( !moveAll && !edge->Is( _LayerEdge::MOVED ))
5762       continue;
5763
5764     int nbBlockedAround = 0;
5765     for ( size_t iN = 0; iN < edge->_neibors.size(); ++iN )
5766     {
5767       nbBlockedAround += edge->_neibors[iN]->Is( _LayerEdge::BLOCKED );
5768       if ( edge->_neibors[iN]->Is( _LayerEdge::RISKY_SWOL ) &&
5769            edge->_neibors[iN]->_cosin > 0 )
5770         neighborHasRiskySWOL = true;
5771     }
5772     if ( nbBlockedAround > 1 )
5773       continue;
5774
5775     gp_Pnt tgtP = SMESH_TNodeXYZ( edge->_nodes.back() );
5776     gp_Pnt2d uv = eof->_offsetSurf->NextValueOfUV( edge->_curvature->_uv, tgtP, preci );
5777     if ( eof->_offsetSurf->Gap() > edge->_len ) continue; // NextValueOfUV() bug
5778     edge->_curvature->_uv = uv;
5779     if ( eof->_offsetSurf->Gap() < 10 * preci ) continue; // same pos
5780
5781     gp_XYZ  newP = eof->_offsetSurf->Value( uv ).XYZ();
5782     gp_XYZ prevP = edge->PrevCheckPos();
5783     bool      ok = true;
5784     if ( !moveAll )
5785       for ( size_t iS = 0; iS < edge->_simplices.size() && ok; ++iS )
5786       {
5787         ok = edge->_simplices[iS].IsForward( &prevP, &newP, vol );
5788       }
5789     if ( ok )
5790     {
5791       SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( edge->_nodes.back() );
5792       n->setXYZ( newP.X(), newP.Y(), newP.Z());
5793       edge->_pos.back() = newP;
5794
5795       edge->Set( _LayerEdge::MARKED );
5796       if ( moveAll == _LayerEdge::UPD_NORMAL_CONV )
5797       {
5798         edge->_normal = ( newP - prevP ).Normalized();
5799       }
5800       // if ( edge->_len < eof->_offsetValue )
5801       //   edge->_len = eof->_offsetValue;
5802
5803       if ( !eos._sWOL.IsNull() ) // RISKY_SWOL
5804       {
5805         double change = eof->_offsetSurf->Gap() / eof->_offsetValue;
5806         if (( newP - tgtP.XYZ() ) * edge->_normal < 0 )
5807           change = 1 - change;
5808         else
5809           change = 1 + change;
5810         gp_XYZ shitfVec    = tgtP.XYZ() - SMESH_NodeXYZ( edge->_nodes[0] );
5811         gp_XYZ newShiftVec = shitfVec * change;
5812         double shift       = edge->_normal * shitfVec;
5813         double newShift    = edge->_normal * newShiftVec;
5814         newP = tgtP.XYZ() + edge->_normal * ( newShift - shift );
5815
5816         uv = eof->_offsetSurf->NextValueOfUV( edge->_curvature->_uv, newP, preci );
5817         if ( eof->_offsetSurf->Gap() < edge->_len )
5818         {
5819           edge->_curvature->_uv = uv;
5820           newP = eof->_offsetSurf->Value( uv ).XYZ();
5821         }
5822         n->setXYZ( newP.X(), newP.Y(), newP.Z());
5823         if ( !edge->UpdatePositionOnSWOL( n, /*tol=*/10 * edge->_len / ( edge->NbSteps() + 1 ),
5824                                           eos, eos.GetData().GetHelper() ))
5825         {
5826           debugMsg("UpdatePositionOnSWOL fails in putOnOffsetSurface()" );
5827         }
5828       }
5829     }
5830   }
5831
5832
5833
5834 #ifdef _DEBUG_
5835   // dumpMove() for debug
5836   size_t i = 0;
5837   for ( ; i < eos._edges.size(); ++i )
5838     if ( eos._edges[i]->Is( _LayerEdge::MARKED ))
5839       break;
5840   if ( i < eos._edges.size() )
5841   {
5842     dumpFunction(SMESH_Comment("putOnOffsetSurface_") << eos.ShapeTypeLetter() << eos._shapeID
5843                  << "_InfStep" << infStep << "_" << Abs( smooStep ));
5844     for ( ; i < eos._edges.size(); ++i )
5845     {
5846       if ( eos._edges[i]->Is( _LayerEdge::MARKED )) {
5847         dumpMove( eos._edges[i]->_nodes.back() );
5848       }
5849     }
5850     dumpFunctionEnd();
5851   }
5852 #endif
5853
5854   _ConvexFace* cnvFace;
5855   if ( moveAll != _LayerEdge::UPD_NORMAL_CONV &&
5856        eos.ShapeType() == TopAbs_FACE &&
5857        (cnvFace = eos.GetData().GetConvexFace( eos._shapeID )) &&
5858        !cnvFace->_normalsFixedOnBorders )
5859   {
5860     // put on the surface nodes built on FACE boundaries
5861     SMESH_subMeshIteratorPtr smIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false);
5862     while ( smIt->more() )
5863     {
5864       SMESH_subMesh*     sm = smIt->next();
5865       _EdgesOnShape* subEOS = eos.GetData().GetShapeEdges( sm->GetId() );
5866       if ( !subEOS->_sWOL.IsNull() ) continue;
5867       if ( std::find( eosC1.begin(), eosC1.end(), subEOS ) != eosC1.end() ) continue;
5868
5869       putOnOffsetSurface( *subEOS, infStep, eosC1, smooStep, _LayerEdge::UPD_NORMAL_CONV );
5870     }
5871     cnvFace->_normalsFixedOnBorders = true;
5872   }
5873
5874
5875   // bos #20643
5876   // negative smooStep means "final step", where we don't treat RISKY_SWOL edges
5877   // as edges based on FACE are a bit late comparing with them
5878   if ( smooStep >= 0 &&
5879        neighborHasRiskySWOL &&
5880        moveAll != _LayerEdge::RISKY_SWOL &&
5881        eos.ShapeType() == TopAbs_FACE )
5882   {
5883     // put on the surface nodes built on FACE boundaries
5884     SMESH_subMeshIteratorPtr smIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false);
5885     while ( smIt->more() )
5886     {
5887       SMESH_subMesh*     sm = smIt->next();
5888       _EdgesOnShape* subEOS = eos.GetData().GetShapeEdges( sm->GetId() );
5889       if ( subEOS->_sWOL.IsNull() ) continue;
5890       if ( std::find( eosC1.begin(), eosC1.end(), subEOS ) != eosC1.end() ) continue;
5891
5892       putOnOffsetSurface( *subEOS, infStep, eosC1, smooStep, _LayerEdge::RISKY_SWOL );
5893     }
5894   }
5895 }
5896
5897 //================================================================================
5898 /*!
5899  * \brief Return a curve of the EDGE to be used for smoothing and arrange
5900  *        _LayerEdge's to be in a consequent order
5901  */
5902 //================================================================================
5903
5904 Handle(Geom_Curve) _Smoother1D::CurveForSmooth( const TopoDS_Edge&  E,
5905                                                 _EdgesOnShape&      eos,
5906                                                 SMESH_MesherHelper& helper)
5907 {
5908   SMESHDS_SubMesh* smDS = eos._subMesh->GetSubMeshDS();
5909
5910   TopLoc_Location loc; double f,l;
5911
5912   Handle(Geom_Line)   line;
5913   Handle(Geom_Circle) circle;
5914   bool isLine, isCirc;
5915   if ( eos._sWOL.IsNull() ) /////////////////////////////////////////// 3D case
5916   {
5917     // check if the EDGE is a line
5918     Handle(Geom_Curve) curve = BRep_Tool::Curve( E, f, l);
5919     if ( curve->IsKind( STANDARD_TYPE( Geom_TrimmedCurve )))
5920       curve = Handle(Geom_TrimmedCurve)::DownCast( curve )->BasisCurve();
5921
5922     line   = Handle(Geom_Line)::DownCast( curve );
5923     circle = Handle(Geom_Circle)::DownCast( curve );
5924     isLine = (!line.IsNull());
5925     isCirc = (!circle.IsNull());
5926
5927     if ( !isLine && !isCirc ) // Check if the EDGE is close to a line
5928     {
5929       isLine = SMESH_Algo::IsStraight( E );
5930
5931       if ( isLine )
5932         line = new Geom_Line( gp::OX() ); // only type does matter
5933     }
5934     if ( !isLine && !isCirc && eos._edges.size() > 2) // Check if the EDGE is close to a circle
5935     {
5936       // TODO
5937     }
5938   }
5939   else //////////////////////////////////////////////////////////////////////// 2D case
5940   {
5941     if ( !eos._isRegularSWOL ) // 23190
5942       return NULL;
5943
5944     const TopoDS_Face& F = TopoDS::Face( eos._sWOL );
5945
5946     // check if the EDGE is a line
5947     Handle(Geom2d_Curve) curve = BRep_Tool::CurveOnSurface( E, F, f, l );
5948     if ( curve->IsKind( STANDARD_TYPE( Geom2d_TrimmedCurve )))
5949       curve = Handle(Geom2d_TrimmedCurve)::DownCast( curve )->BasisCurve();
5950
5951     Handle(Geom2d_Line)   line2d   = Handle(Geom2d_Line)::DownCast( curve );
5952     Handle(Geom2d_Circle) circle2d = Handle(Geom2d_Circle)::DownCast( curve );
5953     isLine = (!line2d.IsNull());
5954     isCirc = (!circle2d.IsNull());
5955
5956     if ( !isLine && !isCirc ) // Check if the EDGE is close to a line
5957     {
5958       Bnd_B2d bndBox;
5959       SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
5960       while ( nIt->more() )
5961         bndBox.Add( helper.GetNodeUV( F, nIt->next() ));
5962       gp_XY size = bndBox.CornerMax() - bndBox.CornerMin();
5963
5964       const double lineTol = 1e-2 * sqrt( bndBox.SquareExtent() );
5965       for ( int i = 0; i < 2 && !isLine; ++i )
5966         isLine = ( size.Coord( i+1 ) <= lineTol );
5967     }
5968     if ( !isLine && !isCirc && eos._edges.size() > 2 ) // Check if the EDGE is close to a circle
5969     {
5970       // TODO
5971     }
5972     if ( isLine )
5973     {
5974       line = new Geom_Line( gp::OX() ); // only type does matter
5975     }
5976     else if ( isCirc )
5977     {
5978       gp_Pnt2d p = circle2d->Location();
5979       gp_Ax2 ax( gp_Pnt( p.X(), p.Y(), 0), gp::DX());
5980       circle = new Geom_Circle( ax, 1.); // only center position does matter
5981     }
5982   }
5983
5984   if ( isLine )
5985     return line;
5986   if ( isCirc )
5987     return circle;
5988
5989   return Handle(Geom_Curve)();
5990 }
5991
5992 //================================================================================
5993 /*!
5994  * \brief Smooth edges on EDGE
5995  */
5996 //================================================================================
5997
5998 bool _Smoother1D::Perform(_SolidData&                    data,
5999                           Handle(ShapeAnalysis_Surface)& surface,
6000                           const TopoDS_Face&             F,
6001                           SMESH_MesherHelper&            helper )
6002 {
6003   if ( _leParams.empty() || ( !isAnalytic() && _offPoints.empty() ))
6004     prepare( data );
6005
6006   findEdgesToSmooth();
6007   if ( isAnalytic() )
6008     return smoothAnalyticEdge( data, surface, F, helper );
6009   else
6010     return smoothComplexEdge ( data, surface, F, helper );
6011 }
6012
6013 //================================================================================
6014 /*!
6015  * \brief Find edges to smooth
6016  */
6017 //================================================================================
6018
6019 void _Smoother1D::findEdgesToSmooth()
6020 {
6021   _LayerEdge* leOnV[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
6022   for ( int iEnd = 0; iEnd < 2; ++iEnd )
6023     if ( leOnV[iEnd]->Is( _LayerEdge::NORMAL_UPDATED ))
6024       _leOnV[iEnd]._cosin = Abs( _edgeDir[iEnd].Normalized() * leOnV[iEnd]->_normal );
6025
6026   _eToSmooth[0].first = _eToSmooth[0].second = 0;
6027
6028   for ( size_t i = 0; i < _eos.size(); ++i )
6029   {
6030     if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH ))
6031     {
6032       if ( needSmoothing( _leOnV[0]._cosin,
6033                           _eos[i]->_len * leOnV[0]->_lenFactor, _curveLen * _leParams[i] ) ||
6034            isToSmooth( i )
6035            )
6036         _eos[i]->Set( _LayerEdge::TO_SMOOTH );
6037       else
6038         break;
6039     }
6040     _eToSmooth[0].second = i+1;
6041   }
6042
6043   _eToSmooth[1].first = _eToSmooth[1].second = _eos.size();
6044
6045   for ( int i = _eos.size() - 1; i >= _eToSmooth[0].second; --i )
6046   {
6047     if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH ))
6048     {
6049       if ( needSmoothing( _leOnV[1]._cosin,
6050                           _eos[i]->_len * leOnV[1]->_lenFactor, _curveLen * ( 1.-_leParams[i] )) ||
6051            isToSmooth( i ))
6052         _eos[i]->Set( _LayerEdge::TO_SMOOTH );
6053       else
6054         break;
6055     }
6056     _eToSmooth[1].first = i;
6057   }
6058 }
6059
6060 //================================================================================
6061 /*!
6062  * \brief Check if iE-th _LayerEdge needs smoothing
6063  */
6064 //================================================================================
6065
6066 bool _Smoother1D::isToSmooth( int iE )
6067 {
6068   SMESH_NodeXYZ pi( _eos[iE]->_nodes[0] );
6069   SMESH_NodeXYZ p0( _eos[iE]->_2neibors->srcNode(0) );
6070   SMESH_NodeXYZ p1( _eos[iE]->_2neibors->srcNode(1) );
6071   gp_XYZ       seg0 = pi - p0;
6072   gp_XYZ       seg1 = p1 - pi;
6073   gp_XYZ    tangent =  seg0 + seg1;
6074   double tangentLen = tangent.Modulus();
6075   double  segMinLen = Min( seg0.Modulus(), seg1.Modulus() );
6076   if ( tangentLen < std::numeric_limits<double>::min() )
6077     return false;
6078   tangent /= tangentLen;
6079
6080   for ( size_t i = 0; i < _eos[iE]->_neibors.size(); ++i )
6081   {
6082     _LayerEdge* ne = _eos[iE]->_neibors[i];
6083     if ( !ne->Is( _LayerEdge::TO_SMOOTH ) ||
6084          ne->_nodes.size() < 2 ||
6085          ne->_nodes[0]->GetPosition()->GetDim() != 2 )
6086       continue;
6087     gp_XYZ edgeVec = SMESH_NodeXYZ( ne->_nodes.back() ) - SMESH_NodeXYZ( ne->_nodes[0] );
6088     double    proj = edgeVec * tangent;
6089     if ( needSmoothing( 1., proj, segMinLen ))
6090       return true;
6091   }
6092   return false;
6093 }
6094
6095 //================================================================================
6096 /*!
6097  * \brief smooth _LayerEdge's on a staight EDGE or circular EDGE
6098  */
6099 //================================================================================
6100
6101 bool _Smoother1D::smoothAnalyticEdge( _SolidData&                    data,
6102                                       Handle(ShapeAnalysis_Surface)& surface,
6103                                       const TopoDS_Face&             F,
6104                                       SMESH_MesherHelper&            helper)
6105 {
6106   if ( !isAnalytic() ) return false;
6107
6108   size_t iFrom = 0, iTo = _eos._edges.size();
6109
6110   if ( _anaCurve->IsKind( STANDARD_TYPE( Geom_Line )))
6111   {
6112     if ( F.IsNull() ) // 3D
6113     {
6114       SMESH_TNodeXYZ pSrc0( _eos._edges[iFrom]->_2neibors->srcNode(0) );
6115       SMESH_TNodeXYZ pSrc1( _eos._edges[iTo-1]->_2neibors->srcNode(1) );
6116       //const   gp_XYZ lineDir = pSrc1 - pSrc0;
6117       //_LayerEdge* vLE0 = getLEdgeOnV( 0 );
6118       //_LayerEdge* vLE1 = getLEdgeOnV( 1 );
6119       // bool shiftOnly = ( vLE0->Is( _LayerEdge::NORMAL_UPDATED ) ||
6120       //                    vLE0->Is( _LayerEdge::BLOCKED ) ||
6121       //                    vLE1->Is( _LayerEdge::NORMAL_UPDATED ) ||
6122       //                    vLE1->Is( _LayerEdge::BLOCKED ));
6123       for ( int iEnd = 0; iEnd < 2; ++iEnd )
6124       {
6125         iFrom = _eToSmooth[ iEnd ].first, iTo = _eToSmooth[ iEnd ].second;
6126         if ( iFrom >= iTo ) continue;
6127         SMESH_TNodeXYZ p0( _eos[iFrom]->_2neibors->tgtNode(0) );
6128         SMESH_TNodeXYZ p1( _eos[iTo-1]->_2neibors->tgtNode(1) );
6129         double param0 = ( iFrom == 0 ) ? 0. : _leParams[ iFrom-1 ];
6130         double param1 = _leParams[ iTo ];
6131         for ( size_t i = iFrom; i < iTo; ++i )
6132         {
6133           _LayerEdge*       edge = _eos[i];
6134           SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edge->_nodes.back() );
6135           double           param = ( _leParams[i] - param0 ) / ( param1 - param0 );
6136           gp_XYZ          newPos = p0 * ( 1. - param ) + p1 * param;
6137
6138           // if ( shiftOnly || edge->Is( _LayerEdge::NORMAL_UPDATED ))
6139           // {
6140           //   gp_XYZ curPos = SMESH_TNodeXYZ ( tgtNode );
6141           //   double  shift = ( lineDir * ( newPos - pSrc0 ) -
6142           //                     lineDir * ( curPos - pSrc0 ));
6143           //   newPos = curPos + lineDir * shift / lineDir.SquareModulus();
6144           // }
6145           if ( edge->Is( _LayerEdge::BLOCKED ))
6146           {
6147             SMESH_TNodeXYZ pSrc( edge->_nodes[0] );
6148             double curThick = pSrc.SquareDistance( tgtNode );
6149             double newThink = ( pSrc - newPos ).SquareModulus();
6150             if ( newThink > curThick )
6151               continue;
6152           }
6153           edge->_pos.back() = newPos;
6154           tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
6155           dumpMove( tgtNode );
6156         }
6157       }
6158     }
6159     else // 2D
6160     {
6161       _LayerEdge* eV0 = getLEdgeOnV( 0 );
6162       _LayerEdge* eV1 = getLEdgeOnV( 1 );
6163       gp_XY      uvV0 = eV0->LastUV( F, *data.GetShapeEdges( eV0 ));
6164       gp_XY      uvV1 = eV1->LastUV( F, *data.GetShapeEdges( eV1 ));
6165       if ( eV0->_nodes.back() == eV1->_nodes.back() ) // closed edge
6166       {
6167         int iPeriodic = helper.GetPeriodicIndex();
6168         if ( iPeriodic == 1 || iPeriodic == 2 )
6169         {
6170           uvV1.SetCoord( iPeriodic, helper.GetOtherParam( uvV1.Coord( iPeriodic )));
6171           if ( uvV0.Coord( iPeriodic ) > uvV1.Coord( iPeriodic ))
6172             std::swap( uvV0, uvV1 );
6173         }
6174       }
6175       for ( int iEnd = 0; iEnd < 2; ++iEnd )
6176       {
6177         iFrom = _eToSmooth[ iEnd ].first, iTo = _eToSmooth[ iEnd ].second;
6178         if ( iFrom >= iTo ) continue;
6179         _LayerEdge* e0 = _eos[iFrom]->_2neibors->_edges[0];
6180         _LayerEdge* e1 = _eos[iTo-1]->_2neibors->_edges[1];
6181         gp_XY      uv0 = ( e0 == eV0 ) ? uvV0 : e0->LastUV( F, _eos );
6182         gp_XY      uv1 = ( e1 == eV1 ) ? uvV1 : e1->LastUV( F, _eos );
6183         double  param0 = ( iFrom == 0 ) ? 0. : _leParams[ iFrom-1 ];
6184         double  param1 = _leParams[ iTo ];
6185         gp_XY  rangeUV = uv1 - uv0;
6186         for ( size_t i = iFrom; i < iTo; ++i )
6187         {
6188           if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
6189           double param = ( _leParams[i] - param0 ) / ( param1 - param0 );
6190           gp_XY newUV = uv0 + param * rangeUV;
6191
6192           gp_Pnt newPos = surface->Value( newUV.X(), newUV.Y() );
6193           SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos[i]->_nodes.back() );
6194           tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
6195           dumpMove( tgtNode );
6196
6197           if ( SMDS_FacePositionPtr pos = tgtNode->GetPosition() ) // NULL if F is noShrink
6198           {
6199             pos->SetUParameter( newUV.X() );
6200             pos->SetVParameter( newUV.Y() );
6201           }
6202
6203           gp_XYZ newUV0( newUV.X(), newUV.Y(), 0 );
6204
6205           if ( !_eos[i]->Is( _LayerEdge::SMOOTHED ))
6206           {
6207             _eos[i]->Set( _LayerEdge::SMOOTHED ); // to check in refine() (IPAL54237)
6208             if ( _eos[i]->_pos.size() > 2 )
6209             {
6210               // modify previous positions to make _LayerEdge less sharply bent
6211               vector<gp_XYZ>& uvVec = _eos[i]->_pos;
6212               const gp_XYZ  uvShift = newUV0 - uvVec.back();
6213               const double     len2 = ( uvVec.back() - uvVec[ 0 ] ).SquareModulus();
6214               int iPrev = uvVec.size() - 2;
6215               while ( iPrev > 0 )
6216               {
6217                 double r = ( uvVec[ iPrev ] - uvVec[0] ).SquareModulus() / len2;
6218                 uvVec[ iPrev ] += uvShift * r;
6219                 --iPrev;
6220               }
6221             }
6222           }
6223           _eos[i]->_pos.back() = newUV0;
6224         }
6225       }
6226     }
6227     return true;
6228   }
6229
6230   if ( _anaCurve->IsKind( STANDARD_TYPE( Geom_Circle )))
6231   {
6232     Handle(Geom_Circle) circle = Handle(Geom_Circle)::DownCast( _anaCurve );
6233     gp_Pnt center3D = circle->Location();
6234
6235     if ( F.IsNull() ) // 3D
6236     {
6237       if ( getLEdgeOnV( 0 )->_nodes.back() == getLEdgeOnV( 1 )->_nodes.back() )
6238         return true; // closed EDGE - nothing to do
6239
6240       // circle is a real curve of EDGE
6241       gp_Circ circ = circle->Circ();
6242
6243       // new center is shifted along its axis
6244       const gp_Dir& axis = circ.Axis().Direction();
6245       _LayerEdge*     e0 = getLEdgeOnV(0);
6246       _LayerEdge*     e1 = getLEdgeOnV(1);
6247       SMESH_TNodeXYZ  p0 = e0->_nodes.back();
6248       SMESH_TNodeXYZ  p1 = e1->_nodes.back();
6249       double      shift1 = axis.XYZ() * ( p0 - center3D.XYZ() );
6250       double      shift2 = axis.XYZ() * ( p1 - center3D.XYZ() );
6251       gp_Pnt   newCenter = center3D.XYZ() + axis.XYZ() * 0.5 * ( shift1 + shift2 );
6252
6253       double newRadius = 0.5 * ( newCenter.Distance( p0 ) + newCenter.Distance( p1 ));
6254
6255       gp_Ax2  newAxis( newCenter, axis, gp_Vec( newCenter, p0 ));
6256       gp_Circ newCirc( newAxis, newRadius );
6257       gp_Vec  vecC1  ( newCenter, p1 );
6258
6259       double uLast = newAxis.XDirection().AngleWithRef( vecC1, newAxis.Direction() ); // -PI - +PI
6260       if ( uLast < 0 )
6261         uLast += 2 * M_PI;
6262       
6263       for ( size_t i = 0; i < _eos.size(); ++i )
6264       {
6265         if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
6266         //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
6267         double u = uLast * _leParams[i];
6268         gp_Pnt p = ElCLib::Value( u, newCirc );
6269         _eos._edges[i]->_pos.back() = p.XYZ();
6270
6271         SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos._edges[i]->_nodes.back() );
6272         tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
6273         dumpMove( tgtNode );
6274       }
6275       return true;
6276     }
6277     else // 2D
6278     {
6279       const gp_XY center( center3D.X(), center3D.Y() );
6280
6281       _LayerEdge* e0 = getLEdgeOnV(0);
6282       _LayerEdge* eM = _eos._edges[ 0 ];
6283       _LayerEdge* e1 = getLEdgeOnV(1);
6284       gp_XY      uv0 = e0->LastUV( F, *data.GetShapeEdges( e0 ) );
6285       gp_XY      uvM = eM->LastUV( F, *data.GetShapeEdges( eM ) );
6286       gp_XY      uv1 = e1->LastUV( F, *data.GetShapeEdges( e1 ) );
6287       gp_Vec2d vec0( center, uv0 );
6288       gp_Vec2d vecM( center, uvM );
6289       gp_Vec2d vec1( center, uv1 );
6290       double uLast = vec0.Angle( vec1 ); // -PI - +PI
6291       double uMidl = vec0.Angle( vecM );
6292       if ( uLast * uMidl <= 0. )
6293         uLast += ( uMidl > 0 ? +2. : -2. ) * M_PI;
6294       const double radius = 0.5 * ( vec0.Magnitude() + vec1.Magnitude() );
6295
6296       gp_Ax2d   axis( center, vec0 );
6297       gp_Circ2d circ( axis, radius );
6298       for ( size_t i = 0; i < _eos.size(); ++i )
6299       {
6300         if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
6301         //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
6302         double    newU = uLast * _leParams[i];
6303         gp_Pnt2d newUV = ElCLib::Value( newU, circ );
6304         _eos._edges[i]->_pos.back().SetCoord( newUV.X(), newUV.Y(), 0 );
6305
6306         gp_Pnt newPos = surface->Value( newUV.X(), newUV.Y() );
6307         SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos._edges[i]->_nodes.back() );
6308         tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
6309         dumpMove( tgtNode );
6310
6311         if ( SMDS_FacePositionPtr pos = tgtNode->GetPosition() ) // NULL if F is noShrink
6312         {
6313           pos->SetUParameter( newUV.X() );
6314           pos->SetVParameter( newUV.Y() );
6315         }
6316         _eos[i]->Set( _LayerEdge::SMOOTHED ); // to check in refine() (IPAL54237)
6317       }
6318     }
6319     return true;
6320   }
6321
6322   return false;
6323 }
6324
6325 //================================================================================
6326 /*!
6327  * \brief smooth _LayerEdge's on a an EDGE
6328  */
6329 //================================================================================
6330
6331 bool _Smoother1D::smoothComplexEdge( _SolidData&                    /*data*/,
6332                                      Handle(ShapeAnalysis_Surface)& surface,
6333                                      const TopoDS_Face&             F,
6334                                      SMESH_MesherHelper&            /*helper*/)
6335 {
6336   if ( _offPoints.empty() )
6337     return false;
6338
6339   // ----------------------------------------------
6340   // move _offPoints along normals of _LayerEdge's
6341   // ----------------------------------------------
6342
6343   _LayerEdge* e[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
6344   if ( e[0]->Is( _LayerEdge::NORMAL_UPDATED ))
6345     _leOnV[0]._normal = getNormalNormal( e[0]->_normal, _edgeDir[0] );
6346   if ( e[1]->Is( _LayerEdge::NORMAL_UPDATED )) 
6347     _leOnV[1]._normal = getNormalNormal( e[1]->_normal, _edgeDir[1] );
6348   _leOnV[0]._len = e[0]->_len;
6349   _leOnV[1]._len = e[1]->_len;
6350   for ( size_t i = 0; i < _offPoints.size(); i++ )
6351   {
6352     _LayerEdge*  e0 = _offPoints[i]._2edges._edges[0];
6353     _LayerEdge*  e1 = _offPoints[i]._2edges._edges[1];
6354     const double w0 = _offPoints[i]._2edges._wgt[0];
6355     const double w1 = _offPoints[i]._2edges._wgt[1];
6356     gp_XYZ  avgNorm = ( e0->_normal    * w0 + e1->_normal    * w1 ).Normalized();
6357     double  avgLen  = ( e0->_len       * w0 + e1->_len       * w1 );
6358     double  avgFact = ( e0->_lenFactor * w0 + e1->_lenFactor * w1 );
6359     if ( e0->Is( _LayerEdge::NORMAL_UPDATED ) ||
6360          e1->Is( _LayerEdge::NORMAL_UPDATED ))
6361       avgNorm = getNormalNormal( avgNorm, _offPoints[i]._edgeDir );
6362
6363     _offPoints[i]._xyz += avgNorm * ( avgLen - _offPoints[i]._len ) * avgFact;
6364     _offPoints[i]._len  = avgLen;
6365   }
6366
6367   double fTol = 0;
6368   if ( !surface.IsNull() ) // project _offPoints to the FACE
6369   {
6370     fTol = 100 * BRep_Tool::Tolerance( F );
6371     //const double segLen = _offPoints[0].Distance( _offPoints[1] );
6372
6373     gp_Pnt2d uv = surface->ValueOfUV( _offPoints[0]._xyz, fTol );
6374     //if ( surface->Gap() < 0.5 * segLen )
6375       _offPoints[0]._xyz = surface->Value( uv ).XYZ();
6376
6377     for ( size_t i = 1; i < _offPoints.size(); ++i )
6378     {
6379       uv = surface->NextValueOfUV( uv, _offPoints[i]._xyz, fTol );
6380       //if ( surface->Gap() < 0.5 * segLen )
6381         _offPoints[i]._xyz = surface->Value( uv ).XYZ();
6382     }
6383   }
6384
6385   // -----------------------------------------------------------------
6386   // project tgt nodes of extreme _LayerEdge's to the offset segments
6387   // -----------------------------------------------------------------
6388
6389   const int updatedOrBlocked = _LayerEdge::NORMAL_UPDATED | _LayerEdge::BLOCKED;
6390   if ( e[0]->Is( updatedOrBlocked )) _iSeg[0] = 0;
6391   if ( e[1]->Is( updatedOrBlocked )) _iSeg[1] = _offPoints.size()-2;
6392
6393   gp_Pnt pExtreme[2], pProj[2];
6394   bool isProjected[2];
6395   for ( int is2nd = 0; is2nd < 2; ++is2nd )
6396   {
6397     pExtreme[ is2nd ] = SMESH_TNodeXYZ( e[is2nd]->_nodes.back() );
6398     int  i = _iSeg[ is2nd ];
6399     int di = is2nd ? -1 : +1;
6400     bool & projected = isProjected[ is2nd ];
6401     projected = false;
6402     double uOnSeg, distMin = Precision::Infinite(), dist, distPrev = 0;
6403     int nbWorse = 0;
6404     do {
6405       gp_Vec v0p( _offPoints[i]._xyz, pExtreme[ is2nd ]    );
6406       gp_Vec v01( _offPoints[i]._xyz, _offPoints[i+1]._xyz );
6407       uOnSeg     = ( v0p * v01 ) / v01.SquareMagnitude();  // param [0,1] along v01
6408       projected  = ( Abs( uOnSeg - 0.5 ) <= 0.5 );
6409       dist       =  pExtreme[ is2nd ].SquareDistance( _offPoints[ i + ( uOnSeg > 0.5 )]._xyz );
6410       if ( dist < distMin || projected )
6411       {
6412         _iSeg[ is2nd ] = i;
6413         pProj[ is2nd ] = _offPoints[i]._xyz + ( v01 * uOnSeg ).XYZ();
6414         distMin = dist;
6415       }
6416       else if ( dist > distPrev )
6417       {
6418         if ( ++nbWorse > 3 ) // avoid projection to the middle of a closed EDGE
6419           break;
6420       }
6421       distPrev = dist;
6422       i += di;
6423     }
6424     while ( !projected &&
6425             i >= 0 && i+1 < (int)_offPoints.size() );
6426
6427     if ( !projected )
6428     {
6429       if (( is2nd && _iSeg[1] != _offPoints.size()-2 ) || ( !is2nd && _iSeg[0] != 0 ))
6430       {
6431         _iSeg[0] = 0;
6432         _iSeg[1] = _offPoints.size()-2;
6433         debugMsg( "smoothComplexEdge() failed to project nodes of extreme _LayerEdge's" );
6434         return false;
6435       }
6436     }
6437   }
6438   if ( _iSeg[0] > _iSeg[1] )
6439   {
6440     debugMsg( "smoothComplexEdge() incorrectly projected nodes of extreme _LayerEdge's" );
6441     return false;
6442   }
6443
6444   // adjust length of extreme LE (test viscous_layers_01/B7)
6445   gp_Vec vDiv0( pExtreme[0], pProj[0] );
6446   gp_Vec vDiv1( pExtreme[1], pProj[1] );
6447   double d0 = vDiv0.Magnitude();
6448   double d1 = isProjected[1] ? vDiv1.Magnitude() : 0;
6449   if ( e[0]->Is( _LayerEdge::BLOCKED )) {
6450     if ( e[0]->_normal * vDiv0.XYZ() < 0 ) e[0]->_len += d0;
6451     else                                   e[0]->_len -= d0;
6452   }
6453   if ( e[1]->Is( _LayerEdge::BLOCKED )) {
6454     if ( e[1]->_normal * vDiv1.XYZ() < 0 ) e[1]->_len += d1;
6455     else                                   e[1]->_len -= d1;
6456   }
6457
6458   // ---------------------------------------------------------------------------------
6459   // compute normalized length of the offset segments located between the projections
6460   // ---------------------------------------------------------------------------------
6461
6462   // temporary replace extreme _offPoints by pExtreme
6463   gp_XYZ opXYZ[2] = { _offPoints[ _iSeg[0]   ]._xyz,
6464                       _offPoints[ _iSeg[1]+1 ]._xyz };
6465   _offPoints[ _iSeg[0]   ]._xyz = pExtreme[0].XYZ();
6466   _offPoints[ _iSeg[1]+ 1]._xyz = pExtreme[1].XYZ();
6467
6468   size_t iSeg = 0, nbSeg = _iSeg[1] - _iSeg[0] + 1;
6469   vector< double > len( nbSeg + 1 );
6470   len[ iSeg++ ] = 0;
6471   len[ iSeg++ ] = pProj[ 0 ].Distance( _offPoints[ _iSeg[0]+1 ]._xyz );
6472   for ( size_t i = _iSeg[0]+1; i <= _iSeg[1]; ++i, ++iSeg )
6473   {
6474     len[ iSeg ] = len[ iSeg-1 ] + _offPoints[i].Distance( _offPoints[i+1] );
6475   }
6476   // if ( isProjected[ 1 ])
6477   //   len[ nbSeg ] -= pProj[ 1 ].Distance( _offPoints[ _iSeg[1]+1 ]._xyz );
6478   // else
6479   //   len[ nbSeg ] += pExtreme[ 1 ].Distance( _offPoints[ _iSeg[1]+1 ]._xyz );
6480
6481   double fullLen = len.back() - d0 - d1;
6482   for ( iSeg = 0; iSeg < len.size(); ++iSeg )
6483     len[iSeg] = ( len[iSeg] - d0 ) / fullLen;
6484
6485   // -------------------------------------------------------------
6486   // distribute tgt nodes of _LayerEdge's between the projections
6487   // -------------------------------------------------------------
6488
6489   iSeg = 0;
6490   for ( size_t i = 0; i < _eos.size(); ++i )
6491   {
6492     if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
6493     //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
6494     while ( iSeg+2 < len.size() && _leParams[i] > len[ iSeg+1 ] )
6495       iSeg++;
6496     double r = ( _leParams[i] - len[ iSeg ]) / ( len[ iSeg+1 ] - len[ iSeg ]);
6497     gp_XYZ p = ( _offPoints[ iSeg + _iSeg[0]     ]._xyz * ( 1 - r ) +
6498                  _offPoints[ iSeg + _iSeg[0] + 1 ]._xyz * r );
6499
6500     if ( surface.IsNull() )
6501     {
6502       _eos[i]->_pos.back() = p;
6503     }
6504     else // project a new node position to a FACE
6505     {
6506       gp_Pnt2d uv ( _eos[i]->_pos.back().X(), _eos[i]->_pos.back().Y() );
6507       gp_Pnt2d uv2( surface->NextValueOfUV( uv, p, fTol ));
6508
6509       p = surface->Value( uv2 ).XYZ();
6510       _eos[i]->_pos.back().SetCoord( uv2.X(), uv2.Y(), 0 );
6511     }
6512     SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos[i]->_nodes.back() );
6513     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
6514     dumpMove( tgtNode );
6515   }
6516
6517   _offPoints[ _iSeg[0]   ]._xyz = opXYZ[0];
6518   _offPoints[ _iSeg[1]+1 ]._xyz = opXYZ[1];
6519
6520   return true;
6521 }
6522
6523 //================================================================================
6524 /*!
6525  * \brief Prepare for smoothing
6526  */
6527 //================================================================================
6528
6529 void _Smoother1D::prepare(_SolidData& data)
6530 {
6531   const TopoDS_Edge& E = TopoDS::Edge( _eos._shape );
6532   _curveLen = SMESH_Algo::EdgeLength( E );
6533
6534   // sort _LayerEdge's by position on the EDGE
6535   data.SortOnEdge( E, _eos._edges );
6536
6537   // compute normalized param of _eos._edges on EDGE
6538   _leParams.resize( _eos._edges.size() + 1 );
6539   {
6540     double curLen;
6541     gp_Pnt pPrev = SMESH_TNodeXYZ( getLEdgeOnV( 0 )->_nodes[0] );
6542     _leParams[0] = 0;
6543     for ( size_t i = 0; i < _eos._edges.size(); ++i )
6544     {
6545       gp_Pnt p       = SMESH_TNodeXYZ( _eos._edges[i]->_nodes[0] );
6546       curLen         = p.Distance( pPrev );
6547       _leParams[i+1] = _leParams[i] + curLen;
6548       pPrev          = p;
6549     }
6550     double fullLen = _leParams.back() + pPrev.Distance( SMESH_TNodeXYZ( getLEdgeOnV(1)->_nodes[0]));
6551     for ( size_t i = 0; i < _leParams.size()-1; ++i )
6552       _leParams[i] = _leParams[i+1] / fullLen;
6553     _leParams.back() = 1.;
6554   }
6555
6556   _LayerEdge* leOnV[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
6557
6558   // get cosin to use in findEdgesToSmooth()
6559   _edgeDir[0] = getEdgeDir( E, leOnV[0]->_nodes[0], data.GetHelper() );
6560   _edgeDir[1] = getEdgeDir( E, leOnV[1]->_nodes[0], data.GetHelper() );
6561   _leOnV[0]._cosin = Abs( leOnV[0]->_cosin );
6562   _leOnV[1]._cosin = Abs( leOnV[1]->_cosin );
6563   if ( _eos._sWOL.IsNull() ) // 3D
6564     for ( int iEnd = 0; iEnd < 2; ++iEnd )
6565       _leOnV[iEnd]._cosin = Abs( _edgeDir[iEnd].Normalized() * leOnV[iEnd]->_normal );
6566
6567   if ( isAnalytic() )
6568     return;
6569
6570   // divide E to have offset segments with low deflection
6571   BRepAdaptor_Curve c3dAdaptor( E );
6572   const double curDeflect = 0.1; //0.01; // Curvature deflection == |p1p2|*sin(p1p2,p1pM)
6573   const double angDeflect = 0.1; //0.09; // Angular deflection == sin(p1pM,pMp2)
6574   GCPnts_TangentialDeflection discret(c3dAdaptor, angDeflect, curDeflect);
6575   if ( discret.NbPoints() <= 2 )
6576   {
6577     _anaCurve = new Geom_Line( gp::OX() ); // only type does matter
6578     return;
6579   }
6580
6581   const double u0 = c3dAdaptor.FirstParameter();
6582   gp_Pnt p; gp_Vec tangent;
6583   if ( discret.NbPoints() >= (int) _eos.size() + 2 )
6584   {
6585     _offPoints.resize( discret.NbPoints() );
6586     for ( size_t i = 0; i < _offPoints.size(); i++ )
6587     {
6588       double u = discret.Parameter( i+1 );
6589       c3dAdaptor.D1( u, p, tangent );
6590       _offPoints[i]._xyz     = p.XYZ();
6591       _offPoints[i]._edgeDir = tangent.XYZ();
6592       _offPoints[i]._param = GCPnts_AbscissaPoint::Length( c3dAdaptor, u0, u ) / _curveLen;
6593     }
6594   }
6595   else
6596   {
6597     std::vector< double > params( _eos.size() + 2 );
6598
6599     params[0]     = data.GetHelper().GetNodeU( E, leOnV[0]->_nodes[0] );
6600     params.back() = data.GetHelper().GetNodeU( E, leOnV[1]->_nodes[0] );
6601     for ( size_t i = 0; i < _eos.size(); i++ )
6602       params[i+1] = data.GetHelper().GetNodeU( E, _eos[i]->_nodes[0] );
6603
6604     if ( params[1] > params[ _eos.size() ] )
6605       std::reverse( params.begin() + 1, params.end() - 1 );
6606
6607     _offPoints.resize( _eos.size() + 2 );
6608     for ( size_t i = 0; i < _offPoints.size(); i++ )
6609     {
6610       const double u = params[i];
6611       c3dAdaptor.D1( u, p, tangent );
6612       _offPoints[i]._xyz     = p.XYZ();
6613       _offPoints[i]._edgeDir = tangent.XYZ();
6614       _offPoints[i]._param = GCPnts_AbscissaPoint::Length( c3dAdaptor, u0, u ) / _curveLen;
6615     }
6616   }
6617
6618   // set _2edges
6619   _offPoints    [0]._2edges.set( &_leOnV[0], &_leOnV[0], 0.5, 0.5 );
6620   _offPoints.back()._2edges.set( &_leOnV[1], &_leOnV[1], 0.5, 0.5 );
6621   _2NearEdges tmp2edges;
6622   tmp2edges._edges[1] = _eos._edges[0];
6623   _leOnV[0]._2neibors = & tmp2edges;
6624   _leOnV[0]._nodes    = leOnV[0]->_nodes;
6625   _leOnV[1]._nodes    = leOnV[1]->_nodes;
6626   _LayerEdge* eNext, *ePrev = & _leOnV[0];
6627   for ( size_t iLE = 0, i = 1; i < _offPoints.size()-1; i++ )
6628   {
6629     // find _LayerEdge's located before and after an offset point
6630     // (_eos._edges[ iLE ] is next after ePrev)
6631     while ( iLE < _eos._edges.size() && _offPoints[i]._param > _leParams[ iLE ] )
6632       ePrev = _eos._edges[ iLE++ ];
6633     eNext = ePrev->_2neibors->_edges[1];
6634
6635     gp_Pnt p0 = SMESH_TNodeXYZ( ePrev->_nodes[0] );
6636     gp_Pnt p1 = SMESH_TNodeXYZ( eNext->_nodes[0] );
6637     double  r = p0.Distance( _offPoints[i]._xyz ) / p0.Distance( p1 );
6638     _offPoints[i]._2edges.set( ePrev, eNext, 1-r, r );
6639   }
6640
6641   // replace _LayerEdge's on VERTEX by _leOnV in _offPoints._2edges
6642   for ( size_t i = 0; i < _offPoints.size(); i++ )
6643     if ( _offPoints[i]._2edges._edges[0] == leOnV[0] )
6644       _offPoints[i]._2edges._edges[0] = & _leOnV[0];
6645     else break;
6646   for ( size_t i = _offPoints.size()-1; i > 0; i-- )
6647     if ( _offPoints[i]._2edges._edges[1] == leOnV[1] )
6648       _offPoints[i]._2edges._edges[1] = & _leOnV[1];
6649     else break;
6650
6651   // set _normal of _leOnV[0] and _leOnV[1] to be normal to the EDGE
6652
6653   int iLBO = _offPoints.size() - 2; // last but one
6654
6655   if ( leOnV[ 0 ]->Is( _LayerEdge::MULTI_NORMAL ))
6656     _leOnV[ 0 ]._normal = getNormalNormal( _eos._edges[1]->_normal, _edgeDir[0] );
6657   else
6658     _leOnV[ 0 ]._normal = getNormalNormal( leOnV[0]->_normal,       _edgeDir[0] );
6659   if ( leOnV[ 1 ]->Is( _LayerEdge::MULTI_NORMAL ))
6660     _leOnV[ 1 ]._normal = getNormalNormal( _eos._edges.back()->_normal, _edgeDir[1] );
6661   else
6662     _leOnV[ 1 ]._normal = getNormalNormal( leOnV[1]->_normal,           _edgeDir[1] );
6663   _leOnV[ 0 ]._len = 0;
6664   _leOnV[ 1 ]._len = 0;
6665   _leOnV[ 0 ]._lenFactor = _offPoints[1   ]._2edges._edges[1]->_lenFactor;
6666   _leOnV[ 1 ]._lenFactor = _offPoints[iLBO]._2edges._edges[0]->_lenFactor;
6667
6668   _iSeg[0] = 0;
6669   _iSeg[1] = _offPoints.size()-2;
6670
6671   // initialize OffPnt::_len
6672   for ( size_t i = 0; i < _offPoints.size(); ++i )
6673     _offPoints[i]._len = 0;
6674
6675   if ( _eos._edges[0]->NbSteps() > 1 ) // already inflated several times, init _xyz
6676   {
6677     _leOnV[0]._len = leOnV[0]->_len;
6678     _leOnV[1]._len = leOnV[1]->_len;
6679     for ( size_t i = 0; i < _offPoints.size(); i++ )
6680     {
6681       _LayerEdge*  e0 = _offPoints[i]._2edges._edges[0];
6682       _LayerEdge*  e1 = _offPoints[i]._2edges._edges[1];
6683       const double w0 = _offPoints[i]._2edges._wgt[0];
6684       const double w1 = _offPoints[i]._2edges._wgt[1];
6685       double  avgLen  = ( e0->_len * w0 + e1->_len * w1 );
6686       gp_XYZ  avgXYZ  = ( SMESH_TNodeXYZ( e0->_nodes.back() ) * w0 +
6687                           SMESH_TNodeXYZ( e1->_nodes.back() ) * w1 );
6688       _offPoints[i]._xyz = avgXYZ;
6689       _offPoints[i]._len = avgLen;
6690     }
6691   }
6692 }
6693
6694 //================================================================================
6695 /*!
6696  * \brief return _normal of _leOnV[is2nd] normal to the EDGE
6697  */
6698 //================================================================================
6699
6700 gp_XYZ _Smoother1D::getNormalNormal( const gp_XYZ & normal,
6701                                      const gp_XYZ&  edgeDir)
6702 {
6703   gp_XYZ cross = normal ^ edgeDir;
6704   gp_XYZ  norm = edgeDir ^ cross;
6705   double  size = norm.Modulus();
6706
6707   // if ( size == 0 ) // MULTI_NORMAL _LayerEdge
6708   //   return gp_XYZ( 1e-100, 1e-100, 1e-100 );
6709
6710   if ( size < 1e-5 ) // normal || edgeDir (almost) at inflation along EDGE (bos #20643)
6711   {
6712     const _LayerEdge* le = _eos._edges[ _eos._edges.size() / 2 ];
6713     const gp_XYZ& leNorm = le->_normal;
6714
6715     cross = leNorm ^ edgeDir;
6716     norm = edgeDir ^ cross;
6717     size = norm.Modulus();
6718   }
6719
6720   return norm / size;
6721 }
6722
6723 //================================================================================
6724 /*!
6725  * \brief Writes a script creating a mesh composed of _offPoints
6726  */
6727 //================================================================================
6728
6729 void _Smoother1D::offPointsToPython() const
6730 {
6731   const char* fname = "/tmp/offPoints.py";
6732   cout << "exec(open('"<<fname<<"','rb').read() )"<<endl;
6733   ofstream py(fname);
6734   py << "import SMESH" << endl
6735      << "from salome.smesh import smeshBuilder" << endl
6736      << "smesh  = smeshBuilder.New(salome.myStudy)" << endl
6737      << "mesh   = smesh.Mesh( 'offPoints' )"<<endl;
6738   for ( size_t i = 0; i < _offPoints.size(); i++ )
6739   {
6740     py << "mesh.AddNode( "
6741        << _offPoints[i]._xyz.X() << ", "
6742        << _offPoints[i]._xyz.Y() << ", "
6743        << _offPoints[i]._xyz.Z() << " )" << endl;
6744   }
6745 }
6746
6747 //================================================================================
6748 /*!
6749  * \brief Sort _LayerEdge's by a parameter on a given EDGE
6750  */
6751 //================================================================================
6752
6753 void _SolidData::SortOnEdge( const TopoDS_Edge&     E,
6754                              vector< _LayerEdge* >& edges)
6755 {
6756   map< double, _LayerEdge* > u2edge;
6757   for ( size_t i = 0; i < edges.size(); ++i )
6758     u2edge.insert( u2edge.end(),
6759                    make_pair( _helper->GetNodeU( E, edges[i]->_nodes[0] ), edges[i] ));
6760
6761   ASSERT( u2edge.size() == edges.size() );
6762   map< double, _LayerEdge* >::iterator u2e = u2edge.begin();
6763   for ( size_t i = 0; i < edges.size(); ++i, ++u2e )
6764     edges[i] = u2e->second;
6765
6766   Sort2NeiborsOnEdge( edges );
6767 }
6768
6769 //================================================================================
6770 /*!
6771  * \brief Set _2neibors according to the order of _LayerEdge on EDGE
6772  */
6773 //================================================================================
6774
6775 void _SolidData::Sort2NeiborsOnEdge( vector< _LayerEdge* >& edges )
6776 {
6777   if ( edges.size() < 2 || !edges[0]->_2neibors ) return;
6778
6779   for ( size_t i = 0; i < edges.size()-1; ++i )
6780     if ( edges[i]->_2neibors->tgtNode(1) != edges[i+1]->_nodes.back() )
6781       edges[i]->_2neibors->reverse();
6782
6783   const size_t iLast = edges.size() - 1;
6784   if ( edges.size() > 1 &&
6785        edges[iLast]->_2neibors->tgtNode(0) != edges[iLast-1]->_nodes.back() )
6786     edges[iLast]->_2neibors->reverse();
6787 }
6788
6789 //================================================================================
6790 /*!
6791  * \brief Return _EdgesOnShape* corresponding to the shape
6792  */
6793 //================================================================================
6794
6795 _EdgesOnShape* _SolidData::GetShapeEdges(const TGeomID shapeID )
6796 {
6797   if ( shapeID < (int)_edgesOnShape.size() &&
6798        _edgesOnShape[ shapeID ]._shapeID == shapeID )
6799     return _edgesOnShape[ shapeID ]._subMesh ? & _edgesOnShape[ shapeID ] : 0;
6800
6801   for ( size_t i = 0; i < _edgesOnShape.size(); ++i )
6802     if ( _edgesOnShape[i]._shapeID == shapeID )
6803       return _edgesOnShape[i]._subMesh ? & _edgesOnShape[i] : 0;
6804
6805   return 0;
6806 }
6807
6808 //================================================================================
6809 /*!
6810  * \brief Return _EdgesOnShape* corresponding to the shape
6811  */
6812 //================================================================================
6813
6814 _EdgesOnShape* _SolidData::GetShapeEdges(const TopoDS_Shape& shape )
6815 {
6816   SMESHDS_Mesh* meshDS = _proxyMesh->GetMesh()->GetMeshDS();
6817   return GetShapeEdges( meshDS->ShapeToIndex( shape ));
6818 }
6819
6820 //================================================================================
6821 /*!
6822  * \brief Prepare data of the _LayerEdge for smoothing on FACE
6823  */
6824 //================================================================================
6825
6826 void _SolidData::PrepareEdgesToSmoothOnFace( _EdgesOnShape* eos, bool substituteSrcNodes )
6827 {
6828   SMESH_MesherHelper helper( *_proxyMesh->GetMesh() );
6829
6830   set< TGeomID > vertices;
6831   TopoDS_Face F;
6832   if ( eos->ShapeType() == TopAbs_FACE )
6833   {
6834     // check FACE concavity and get concave VERTEXes
6835     F = TopoDS::Face( eos->_shape );
6836     if ( isConcave( F, helper, &vertices ))
6837       _concaveFaces.insert( eos->_shapeID );
6838
6839     // set eos._eosConcaVer
6840     eos->_eosConcaVer.clear();
6841     eos->_eosConcaVer.reserve( vertices.size() );
6842     for ( set< TGeomID >::iterator v = vertices.begin(); v != vertices.end(); ++v )
6843     {
6844       _EdgesOnShape* eov = GetShapeEdges( *v );
6845       if ( eov && eov->_edges.size() == 1 )
6846       {
6847         eos->_eosConcaVer.push_back( eov );
6848         for ( size_t i = 0; i < eov->_edges[0]->_neibors.size(); ++i )
6849           eov->_edges[0]->_neibors[i]->Set( _LayerEdge::DIFFICULT );
6850       }
6851     }
6852
6853     // SetSmooLen() to _LayerEdge's on FACE
6854     // for ( size_t i = 0; i < eos->_edges.size(); ++i )
6855     // {
6856     //   eos->_edges[i]->SetSmooLen( Precision::Infinite() );
6857     // }
6858     // SMESH_subMeshIteratorPtr smIt = eos->_subMesh->getDependsOnIterator(/*includeSelf=*/false);
6859     // while ( smIt->more() ) // loop on sub-shapes of the FACE
6860     // {
6861     //   _EdgesOnShape* eoe = GetShapeEdges( smIt->next()->GetId() );
6862     //   if ( !eoe ) continue;
6863
6864     //   vector<_LayerEdge*>& eE = eoe->_edges;
6865     //   for ( size_t iE = 0; iE < eE.size(); ++iE ) // loop on _LayerEdge's on EDGE or VERTEX
6866     //   {
6867     //     if ( eE[iE]->_cosin <= theMinSmoothCosin )
6868     //       continue;
6869
6870     //     SMDS_ElemIteratorPtr segIt = eE[iE]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Edge);
6871     //     while ( segIt->more() )
6872     //     {
6873     //       const SMDS_MeshElement* seg = segIt->next();
6874     //       if ( !eos->_subMesh->DependsOn( seg->getshapeId() ))
6875     //         continue;
6876     //       if ( seg->GetNode(0) != eE[iE]->_nodes[0] )
6877     //         continue; // not to check a seg twice
6878     //       for ( size_t iN = 0; iN < eE[iE]->_neibors.size(); ++iN )
6879     //       {
6880     //         _LayerEdge* eN = eE[iE]->_neibors[iN];
6881     //         if ( eN->_nodes[0]->getshapeId() != eos->_shapeID )
6882     //           continue;
6883     //         double dist    = SMESH_MeshAlgos::GetDistance( seg, SMESH_TNodeXYZ( eN->_nodes[0] ));
6884     //         double smooLen = getSmoothingThickness( eE[iE]->_cosin, dist );
6885     //         eN->SetSmooLen( Min( smooLen, eN->GetSmooLen() ));
6886     //         eN->Set( _LayerEdge::NEAR_BOUNDARY );
6887     //       }
6888     //     }
6889     //   }
6890     // }
6891   } // if ( eos->ShapeType() == TopAbs_FACE )
6892
6893   for ( size_t i = 0; i < eos->_edges.size(); ++i )
6894   {
6895     eos->_edges[i]->_smooFunction = 0;
6896     eos->_edges[i]->Set( _LayerEdge::TO_SMOOTH );
6897   }
6898   bool isCurved = false;
6899   for ( size_t i = 0; i < eos->_edges.size(); ++i )
6900   {
6901     _LayerEdge* edge = eos->_edges[i];
6902
6903     // get simplices sorted
6904     _Simplex::SortSimplices( edge->_simplices );
6905
6906     // smoothing function
6907     edge->ChooseSmooFunction( vertices, _n2eMap );
6908
6909     // set _curvature
6910     double avgNormProj = 0, avgLen = 0;
6911     for ( size_t iS = 0; iS < edge->_simplices.size(); ++iS )
6912     {
6913       _Simplex& s = edge->_simplices[iS];
6914
6915       gp_XYZ  vec = edge->_pos.back() - SMESH_TNodeXYZ( s._nPrev );
6916       avgNormProj += edge->_normal * vec;
6917       avgLen      += vec.Modulus();
6918       if ( substituteSrcNodes )
6919       {
6920         s._nNext = _n2eMap[ s._nNext ]->_nodes.back();
6921         s._nPrev = _n2eMap[ s._nPrev ]->_nodes.back();
6922       }
6923     }
6924     avgNormProj /= edge->_simplices.size();
6925     avgLen      /= edge->_simplices.size();
6926     if (( edge->_curvature = _Curvature::New( avgNormProj, avgLen )))
6927     {
6928       edge->Set( _LayerEdge::SMOOTHED_C1 );
6929       isCurved = true;
6930       SMDS_FacePositionPtr fPos = edge->_nodes[0]->GetPosition();
6931       if ( !fPos )
6932         for ( size_t iS = 0; iS < edge->_simplices.size()  &&  !fPos; ++iS )
6933           fPos = edge->_simplices[iS]._nPrev->GetPosition();
6934       if ( fPos )
6935         edge->_curvature->_uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
6936     }
6937   }
6938
6939   // prepare for putOnOffsetSurface()
6940   if (( eos->ShapeType() == TopAbs_FACE ) &&
6941       ( isCurved || !eos->_eosConcaVer.empty() ))
6942   {
6943     eos->_offsetSurf = helper.GetSurface( TopoDS::Face( eos->_shape ));
6944     eos->_edgeForOffset = 0;
6945
6946     double maxCosin = -1;
6947     //bool hasNoShrink = false;
6948     for ( TopExp_Explorer eExp( eos->_shape, TopAbs_EDGE ); eExp.More(); eExp.Next() )
6949     {
6950       _EdgesOnShape* eoe = GetShapeEdges( eExp.Current() );
6951       if ( !eoe || eoe->_edges.empty() ) continue;
6952
6953       // if ( eos->GetData()._noShrinkShapes.count( eoe->_shapeID ))
6954       //   hasNoShrink = true;
6955
6956       vector<_LayerEdge*>& eE = eoe->_edges;
6957       _LayerEdge* e = eE[ eE.size() / 2 ];
6958       if ( !e->Is( _LayerEdge::RISKY_SWOL ) && e->_cosin > maxCosin )
6959       {
6960         eos->_edgeForOffset = e;
6961         maxCosin = e->_cosin;
6962       }
6963
6964       if ( !eoe->_sWOL.IsNull() )
6965         for ( _LayerEdge* le : eoe->_edges )
6966           if ( le->Is( _LayerEdge::RISKY_SWOL ) && e->_cosin > 0 )
6967           {
6968             // make _neibors on FACE be smoothed after le->Is( BLOCKED )
6969             for ( _LayerEdge* neibor : le->_neibors )
6970             {
6971               int shapeDim =  neibor->BaseShapeDim();
6972               if ( shapeDim == 2 )
6973                 neibor->Set( _LayerEdge::NEAR_BOUNDARY ); // on FACE
6974               else if ( shapeDim == 0 )
6975                 neibor->Set( _LayerEdge::RISKY_SWOL );    // on VERTEX
6976
6977               if ( !neibor->_curvature )
6978               {
6979                 gp_XY uv = helper.GetNodeUV( F, neibor->_nodes[0] );
6980                 neibor->_curvature = _Factory::NewCurvature();
6981                 neibor->_curvature->_r = 0;
6982                 neibor->_curvature->_k = 0;
6983                 neibor->_curvature->_h2lenRatio = 0;
6984                 neibor->_curvature->_uv = uv;
6985               }
6986             }
6987           }
6988     } // loop on EDGEs
6989
6990     // Try to initialize _Mapper2D
6991
6992     // if ( hasNoShrink )
6993     //   return;
6994
6995     SMDS_ElemIteratorPtr fIt = eos->_subMesh->GetSubMeshDS()->GetElements();
6996     if ( !fIt->more() || fIt->next()->NbCornerNodes() != 4 )
6997       return;
6998
6999     // get EDGEs of quadrangle bottom
7000     std::list< TopoDS_Edge > edges;
7001     std::list< int > nbEdgesInWire;
7002     int nbWire = SMESH_Block::GetOrderedEdges( F, edges, nbEdgesInWire );
7003     if ( nbWire != 1 || nbEdgesInWire.front() < 4 )
7004       return;
7005     const SMDS_MeshNode* node;
7006     while ( true ) // make edges start at a corner VERTEX
7007     {
7008       node = SMESH_Algo::VertexNode( helper.IthVertex( 0, edges.front() ), helper.GetMeshDS() );
7009       if ( node && helper.IsCornerOfStructure( node, eos->_subMesh->GetSubMeshDS(), helper ))
7010         break;
7011       edges.pop_front();
7012       if ( edges.empty() )
7013         return;
7014     }
7015     std::list< TopoDS_Edge >::iterator edgeIt = edges.begin();
7016     while ( true ) // make edges finish at a corner VERTEX
7017     {
7018       node = SMESH_Algo::VertexNode( helper.IthVertex( 1, *edgeIt ), helper.GetMeshDS() );
7019       ++edgeIt;
7020       if ( node && helper.IsCornerOfStructure( node, eos->_subMesh->GetSubMeshDS(), helper ))
7021       {
7022         edges.erase( edgeIt, edges.end() );
7023         break;
7024       }
7025       if ( edgeIt == edges.end() )
7026         return;
7027     }
7028
7029     // get structure of nodes
7030     TParam2ColumnMap param2ColumnMap;
7031     if ( !helper.LoadNodeColumns( param2ColumnMap, F, edges, helper.GetMeshDS() ))
7032       return;
7033
7034     eos->_mapper2D = new _Mapper2D( param2ColumnMap, eos->GetData()._n2eMap );
7035
7036   } // if eos is of curved FACE
7037
7038   return;
7039 }
7040
7041 //================================================================================
7042 /*!
7043  * \brief Add faces for smoothing
7044  */
7045 //================================================================================
7046
7047 void _SolidData::AddShapesToSmooth( const set< _EdgesOnShape* >& eosToSmooth,
7048                                     const set< _EdgesOnShape* >* edgesNoAnaSmooth )
7049 {
7050   set< _EdgesOnShape * >::const_iterator eos = eosToSmooth.begin();
7051   for ( ; eos != eosToSmooth.end(); ++eos )
7052   {
7053     if ( !*eos || (*eos)->_toSmooth ) continue;
7054
7055     (*eos)->_toSmooth = true;
7056
7057     if ( (*eos)->ShapeType() == TopAbs_FACE )
7058     {
7059       PrepareEdgesToSmoothOnFace( *eos, /*substituteSrcNodes=*/false );
7060       (*eos)->_toSmooth = true;
7061     }
7062   }
7063
7064   // avoid _Smoother1D::smoothAnalyticEdge() of edgesNoAnaSmooth
7065   if ( edgesNoAnaSmooth )
7066     for ( eos = edgesNoAnaSmooth->begin(); eos != edgesNoAnaSmooth->end(); ++eos )
7067     {
7068       if ( (*eos)->_edgeSmoother )
7069         (*eos)->_edgeSmoother->_anaCurve.Nullify();
7070     }
7071 }
7072
7073 //================================================================================
7074 /*!
7075  * \brief Limit _LayerEdge::_maxLen according to local curvature
7076  */
7077 //================================================================================
7078
7079 void _ViscousBuilder::limitMaxLenByCurvature( _SolidData& data, SMESH_MesherHelper& /*helper*/ )
7080 {
7081   // find intersection of neighbor _LayerEdge's to limit _maxLen
7082   // according to local curvature (IPAL52648)
7083
7084   // This method must be called after findCollisionEdges() where _LayerEdge's
7085   // get _lenFactor initialized in the case of eos._hyp.IsOffsetMethod()
7086
7087   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7088   {
7089     _EdgesOnShape& eosI = data._edgesOnShape[iS];
7090     if ( eosI._edges.empty() ) continue;
7091     if ( !eosI._hyp.ToSmooth() )
7092     {
7093       for ( size_t i = 0; i < eosI._edges.size(); ++i )
7094       {
7095         _LayerEdge* eI = eosI._edges[i];
7096         for ( size_t iN = 0; iN < eI->_neibors.size(); ++iN )
7097         {
7098           _LayerEdge* eN = eI->_neibors[iN];
7099           if ( eI->_nodes[0]->GetID() < eN->_nodes[0]->GetID() ) // treat this pair once
7100           {
7101             _EdgesOnShape* eosN = data.GetShapeEdges( eN );
7102             limitMaxLenByCurvature( eI, eN, eosI, *eosN, eosI._hyp.ToSmooth() );
7103           }
7104         }
7105       }
7106     }
7107     else if ( eosI.ShapeType() == TopAbs_EDGE )
7108     {
7109       const TopoDS_Edge& E = TopoDS::Edge( eosI._shape );
7110       if ( SMESH_Algo::IsStraight( E, /*degenResult=*/true )) continue;
7111
7112       _LayerEdge* e0 = eosI._edges[0];
7113       for ( size_t i = 1; i < eosI._edges.size(); ++i )
7114       {
7115         _LayerEdge* eI = eosI._edges[i];
7116         limitMaxLenByCurvature( eI, e0, eosI, eosI, eosI._hyp.ToSmooth() );
7117         e0 = eI;
7118       }
7119     }
7120   }
7121 }
7122
7123 //================================================================================
7124 /*!
7125  * \brief Limit _LayerEdge::_maxLen according to local curvature
7126  */
7127 //================================================================================
7128
7129 void _ViscousBuilder::limitMaxLenByCurvature( _LayerEdge*    e1,
7130                                               _LayerEdge*    e2,
7131                                               _EdgesOnShape& /*eos1*/,
7132                                               _EdgesOnShape& /*eos2*/,
7133                                               const bool     /*isSmoothable*/ )
7134 {
7135   if (( e1->_nodes[0]->GetPosition()->GetDim() !=
7136         e2->_nodes[0]->GetPosition()->GetDim() ) &&
7137       ( e1->_cosin < 0.75 ))
7138     return; // angle > 90 deg at e1
7139
7140   gp_XYZ plnNorm = e1->_normal ^ e2->_normal;
7141   double norSize = plnNorm.SquareModulus();
7142   if ( norSize < std::numeric_limits<double>::min() )
7143     return; // parallel normals
7144
7145   // find closest points of skew _LayerEdge's
7146   SMESH_TNodeXYZ src1( e1->_nodes[0] ), src2( e2->_nodes[0] );
7147   gp_XYZ dir12 = src2 - src1;
7148   gp_XYZ perp1 = e1->_normal ^ plnNorm;
7149   gp_XYZ perp2 = e2->_normal ^ plnNorm;
7150   double  dot1 = perp2 * e1->_normal;
7151   double  dot2 = perp1 * e2->_normal;
7152   double    u1 =   ( perp2 * dir12 ) / dot1;
7153   double    u2 = - ( perp1 * dir12 ) / dot2;
7154   if ( u1 > 0 && u2 > 0 )
7155   {
7156     double ovl = ( u1 * e1->_normal * dir12 -
7157                    u2 * e2->_normal * dir12 ) / dir12.SquareModulus();
7158     if ( ovl > theSmoothThickToElemSizeRatio )
7159     {
7160       const double coef = 0.75;
7161       e1->SetMaxLen( Min( e1->_maxLen, coef * u1 / e1->_lenFactor ));
7162       e2->SetMaxLen( Min( e2->_maxLen, coef * u2 / e2->_lenFactor ));
7163     }
7164   }
7165 }
7166
7167 //================================================================================
7168 /*!
7169  * \brief Fill data._collisionEdges
7170  */
7171 //================================================================================
7172
7173 void _ViscousBuilder::findCollisionEdges( _SolidData& data, SMESH_MesherHelper& helper )
7174 {
7175   data._collisionEdges.clear();
7176
7177   // set the full thickness of the layers to LEs
7178   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7179   {
7180     _EdgesOnShape& eos = data._edgesOnShape[iS];
7181     if ( eos._edges.empty() ) continue;
7182     if ( eos.ShapeType() != TopAbs_EDGE && eos.ShapeType() != TopAbs_VERTEX ) continue;
7183     if ( !eos._sWOL.IsNull() ) continue; // PAL23566
7184
7185     for ( size_t i = 0; i < eos._edges.size(); ++i )
7186     {
7187       if ( eos._edges[i]->Is( _LayerEdge::BLOCKED )) continue;
7188       double maxLen = eos._edges[i]->_maxLen;
7189       eos._edges[i]->_maxLen = Precision::Infinite(); // avoid blocking
7190       eos._edges[i]->SetNewLength( 1.5 * maxLen, eos, helper );
7191       eos._edges[i]->_maxLen = maxLen;
7192     }
7193   }
7194
7195   // make temporary quadrangles got by extrusion of
7196   // mesh edges along _LayerEdge._normal's
7197
7198   vector< const SMDS_MeshElement* > tmpFaces;
7199
7200   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7201   {
7202     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
7203     if ( eos.ShapeType() != TopAbs_EDGE )
7204       continue;
7205     if ( eos._edges.empty() )
7206     {
7207       _LayerEdge* edge[2] = { 0, 0 }; // LE of 2 VERTEX'es
7208       SMESH_subMeshIteratorPtr smIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false);
7209       while ( smIt->more() )
7210         if ( _EdgesOnShape* eov = data.GetShapeEdges( smIt->next()->GetId() ))
7211           if ( eov->_edges.size() == 1 )
7212             edge[ bool( edge[0]) ] = eov->_edges[0];
7213
7214       if ( edge[1] )
7215       {
7216         _TmpMeshFaceOnEdge* f = new _TmpMeshFaceOnEdge( edge[0], edge[1], --_tmpFaceID );
7217         tmpFaces.push_back( f );
7218       }
7219     }
7220     for ( size_t i = 0; i < eos._edges.size(); ++i )
7221     {
7222       _LayerEdge* edge = eos._edges[i];
7223       for ( int j = 0; j < 2; ++j ) // loop on _2NearEdges
7224       {
7225         const SMDS_MeshNode* src2 = edge->_2neibors->srcNode(j);
7226         if ( src2->GetPosition()->GetDim() > 0 &&
7227              src2->GetID() < edge->_nodes[0]->GetID() )
7228           continue; // avoid using same segment twice
7229
7230         // a _LayerEdge containing tgt2
7231         _LayerEdge* neiborEdge = edge->_2neibors->_edges[j];
7232
7233         _TmpMeshFaceOnEdge* f = new _TmpMeshFaceOnEdge( edge, neiborEdge, --_tmpFaceID );
7234         tmpFaces.push_back( f );
7235       }
7236     }
7237   }
7238
7239   // Find _LayerEdge's intersecting tmpFaces.
7240
7241   SMDS_ElemIteratorPtr fIt( new SMDS_ElementVectorIterator( tmpFaces.begin(),
7242                                                             tmpFaces.end()));
7243   SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
7244     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(), fIt ));
7245
7246   double dist1, dist2, segLen, eps = 0.5;
7247   _CollisionEdges collEdges;
7248   vector< const SMDS_MeshElement* > suspectFaces;
7249   const double angle45 = Cos( 45. * M_PI / 180. );
7250
7251   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7252   {
7253     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
7254     if ( eos.ShapeType() == TopAbs_FACE || !eos._sWOL.IsNull() )
7255       continue;
7256     // find sub-shapes whose VL can influence VL on eos
7257     set< TGeomID > neighborShapes;
7258     PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
7259     while ( const TopoDS_Shape* face = fIt->next() )
7260     {
7261       TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
7262       if ( _EdgesOnShape* eof = data.GetShapeEdges( faceID ))
7263       {
7264         SMESH_subMeshIteratorPtr subIt = eof->_subMesh->getDependsOnIterator(/*includeSelf=*/false);
7265         while ( subIt->more() )
7266           neighborShapes.insert( subIt->next()->GetId() );
7267       }
7268     }
7269     if ( eos.ShapeType() == TopAbs_VERTEX )
7270     {
7271       PShapeIteratorPtr eIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_EDGE );
7272       while ( const TopoDS_Shape* edge = eIt->next() )
7273         neighborShapes.erase( getMeshDS()->ShapeToIndex( *edge ));
7274     }
7275     // find intersecting _LayerEdge's
7276     for ( size_t i = 0; i < eos._edges.size(); ++i )
7277     {
7278       if ( eos._edges[i]->Is( _LayerEdge::MULTI_NORMAL )) continue;
7279       _LayerEdge*   edge = eos._edges[i];
7280       gp_Ax1 lastSegment = edge->LastSegment( segLen, eos );
7281       segLen *= 1.2;
7282
7283       gp_Vec eSegDir0, eSegDir1;
7284       if ( edge->IsOnEdge() )
7285       {
7286         SMESH_TNodeXYZ eP( edge->_nodes[0] );
7287         eSegDir0 = SMESH_TNodeXYZ( edge->_2neibors->srcNode(0) ) - eP;
7288         eSegDir1 = SMESH_TNodeXYZ( edge->_2neibors->srcNode(1) ) - eP;
7289       }
7290       suspectFaces.clear();
7291       searcher->GetElementsInSphere( SMESH_TNodeXYZ( edge->_nodes.back()), edge->_len * 2,
7292                                      SMDSAbs_Face, suspectFaces );
7293       collEdges._intEdges.clear();
7294       for ( size_t j = 0 ; j < suspectFaces.size(); ++j )
7295       {
7296         const _TmpMeshFaceOnEdge* f = (const _TmpMeshFaceOnEdge*) suspectFaces[j];
7297         if ( f->_le1 == edge || f->_le2 == edge ) continue;
7298         if ( !neighborShapes.count( f->_le1->_nodes[0]->getshapeId() )) continue;
7299         if ( !neighborShapes.count( f->_le2->_nodes[0]->getshapeId() )) continue;
7300         if ( edge->IsOnEdge() ) {
7301           if ( edge->_2neibors->include( f->_le1 ) ||
7302                edge->_2neibors->include( f->_le2 )) continue;
7303         }
7304         else {
7305           if (( f->_le1->IsOnEdge() && f->_le1->_2neibors->include( edge )) ||
7306               ( f->_le2->IsOnEdge() && f->_le2->_2neibors->include( edge )))  continue;
7307         }
7308         dist1 = dist2 = Precision::Infinite();
7309         if ( !edge->SegTriaInter( lastSegment, f->n(0), f->n(1), f->n(2), dist1, eps ))
7310           dist1 = Precision::Infinite();
7311         if ( !edge->SegTriaInter( lastSegment, f->n(3), f->n(2), f->n(0), dist2, eps ))
7312           dist2 = Precision::Infinite();
7313         if (( dist1 > segLen ) && ( dist2 > segLen ))
7314           continue;
7315
7316         if ( edge->IsOnEdge() )
7317         {
7318           // skip perpendicular EDGEs
7319           gp_Vec fSegDir  = SMESH_TNodeXYZ( f->n(0) ) - SMESH_TNodeXYZ( f->n(3) );
7320           bool isParallel = ( isLessAngle( eSegDir0, fSegDir, angle45 ) ||
7321                               isLessAngle( eSegDir1, fSegDir, angle45 ) ||
7322                               isLessAngle( eSegDir0, fSegDir.Reversed(), angle45 ) ||
7323                               isLessAngle( eSegDir1, fSegDir.Reversed(), angle45 ));
7324           if ( !isParallel )
7325             continue;
7326         }
7327
7328         // either limit inflation of edges or remember them for updating _normal
7329         // double dot = edge->_normal * f->GetDir();
7330         // if ( dot > 0.1 )
7331         {
7332           collEdges._intEdges.push_back( f->_le1 );
7333           collEdges._intEdges.push_back( f->_le2 );
7334         }
7335         // else
7336         // {
7337         //   double shortLen = 0.75 * ( Min( dist1, dist2 ) / edge->_lenFactor );
7338         //   edge->SetMaxLen( Min( shortLen, edge->_maxLen ));
7339         // }
7340       }
7341
7342       if ( !collEdges._intEdges.empty() )
7343       {
7344         collEdges._edge = edge;
7345         data._collisionEdges.push_back( collEdges );
7346       }
7347     }
7348   }
7349
7350   for ( size_t i = 0 ; i < tmpFaces.size(); ++i )
7351     delete tmpFaces[i];
7352
7353   // restore the zero thickness
7354   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7355   {
7356     _EdgesOnShape& eos = data._edgesOnShape[iS];
7357     if ( eos._edges.empty() ) continue;
7358     if ( eos.ShapeType() != TopAbs_EDGE && eos.ShapeType() != TopAbs_VERTEX ) continue;
7359
7360     for ( size_t i = 0; i < eos._edges.size(); ++i )
7361     {
7362       eos._edges[i]->InvalidateStep( 1, eos );
7363       eos._edges[i]->_len = 0;
7364     }
7365   }
7366 }
7367
7368 //================================================================================
7369 /*!
7370  * \brief Find _LayerEdge's located on boundary of a convex FACE whose normal
7371  *        will be updated at each inflation step
7372  */
7373 //================================================================================
7374
7375 void _ViscousBuilder::findEdgesToUpdateNormalNearConvexFace( _ConvexFace &       convFace,
7376                                                              _SolidData&         data,
7377                                                              SMESH_MesherHelper& helper )
7378 {
7379   const TGeomID convFaceID = getMeshDS()->ShapeToIndex( convFace._face );
7380   const double       preci = BRep_Tool::Tolerance( convFace._face );
7381   Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( convFace._face );
7382
7383   bool edgesToUpdateFound = false;
7384
7385   map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.begin();
7386   for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7387   {
7388     _EdgesOnShape& eos = * id2eos->second;
7389     if ( !eos._sWOL.IsNull() ) continue;
7390     if ( !eos._hyp.ToSmooth() ) continue;
7391     for ( size_t i = 0; i < eos._edges.size(); ++i )
7392     {
7393       _LayerEdge* ledge = eos._edges[ i ];
7394       if ( ledge->Is( _LayerEdge::UPD_NORMAL_CONV )) continue; // already checked
7395       if ( ledge->Is( _LayerEdge::MULTI_NORMAL )) continue; // not inflatable
7396
7397       gp_XYZ tgtPos = ( SMESH_NodeXYZ( ledge->_nodes[0] ) +
7398                         ledge->_normal * ledge->_lenFactor * ledge->_maxLen );
7399
7400       // the normal must be updated if distance from tgtPos to surface is less than
7401       // target thickness
7402
7403       // find an initial UV for search of a projection of tgtPos to surface
7404       const SMDS_MeshNode* nodeInFace = 0;
7405       SMDS_ElemIteratorPtr fIt = ledge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
7406       while ( fIt->more() && !nodeInFace )
7407       {
7408         const SMDS_MeshElement* f = fIt->next();
7409         if ( convFaceID != f->getshapeId() ) continue;
7410
7411         SMDS_ElemIteratorPtr nIt = f->nodesIterator();
7412         while ( nIt->more() && !nodeInFace )
7413         {
7414           const SMDS_MeshElement* n = nIt->next();
7415           if ( n->getshapeId() == convFaceID )
7416             nodeInFace = static_cast< const SMDS_MeshNode* >( n );
7417         }
7418       }
7419       if ( !nodeInFace )
7420         continue;
7421       gp_XY uv = helper.GetNodeUV( convFace._face, nodeInFace );
7422
7423       // projection
7424       surface->NextValueOfUV( uv, tgtPos, preci );
7425       double  dist = surface->Gap();
7426       if ( dist < 0.95 * ledge->_maxLen )
7427       {
7428         ledge->Set( _LayerEdge::UPD_NORMAL_CONV );
7429         if ( !ledge->_curvature ) ledge->_curvature = _Factory::NewCurvature();
7430         ledge->_curvature->_uv.SetCoord( uv.X(), uv.Y() );
7431         edgesToUpdateFound = true;
7432       }
7433     }
7434   }
7435
7436   if ( !convFace._isTooCurved && edgesToUpdateFound )
7437   {
7438     data._convexFaces.insert( make_pair( convFaceID, convFace )).first->second;
7439   }
7440 }
7441
7442 //================================================================================
7443 /*!
7444  * \brief Modify normals of _LayerEdge's on EDGE's to avoid intersection with
7445  * _LayerEdge's on neighbor EDGE's
7446  */
7447 //================================================================================
7448
7449 bool _ViscousBuilder::updateNormals( _SolidData&         data,
7450                                      SMESH_MesherHelper& helper,
7451                                      int                 stepNb,
7452                                      double              /*stepSize*/)
7453 {
7454   updateNormalsOfC1Vertices( data );
7455
7456   if ( stepNb > 0 && !updateNormalsOfConvexFaces( data, helper, stepNb ))
7457     return false;
7458
7459   // map to store new _normal and _cosin for each intersected edge
7460   map< _LayerEdge*, _LayerEdge, _LayerEdgeCmp >           edge2newEdge;
7461   map< _LayerEdge*, _LayerEdge, _LayerEdgeCmp >::iterator e2neIt;
7462   _LayerEdge zeroEdge;
7463   zeroEdge._normal.SetCoord( 0,0,0 );
7464   zeroEdge._maxLen = Precision::Infinite();
7465   zeroEdge._nodes.resize(1); // to init _TmpMeshFaceOnEdge
7466
7467   set< _EdgesOnShape* > shapesToSmooth, edgesNoAnaSmooth;
7468
7469   double segLen, dist1, dist2, dist;
7470   vector< pair< _LayerEdge*, double > > intEdgesDist;
7471   _TmpMeshFaceOnEdge quad( &zeroEdge, &zeroEdge, 0 );
7472
7473   for ( int iter = 0; iter < 5; ++iter )
7474   {
7475     edge2newEdge.clear();
7476
7477     for ( size_t iE = 0; iE < data._collisionEdges.size(); ++iE )
7478     {
7479       _CollisionEdges& ce = data._collisionEdges[iE];
7480       _LayerEdge*   edge1 = ce._edge;
7481       if ( !edge1 /*|| edge1->Is( _LayerEdge::BLOCKED )*/) continue;
7482       _EdgesOnShape* eos1 = data.GetShapeEdges( edge1 );
7483       if ( !eos1 ) continue;
7484
7485       // detect intersections
7486       gp_Ax1 lastSeg = edge1->LastSegment( segLen, *eos1 );
7487       double testLen = 1.5 * edge1->_maxLen * edge1->_lenFactor;
7488       double     eps = 0.5;
7489       intEdgesDist.clear();
7490       double minIntDist = Precision::Infinite();
7491       for ( size_t i = 0; i < ce._intEdges.size(); i += 2 )
7492       {
7493         if ( edge1->Is( _LayerEdge::BLOCKED ) &&
7494              ce._intEdges[i  ]->Is( _LayerEdge::BLOCKED ) &&
7495              ce._intEdges[i+1]->Is( _LayerEdge::BLOCKED ))
7496           continue;
7497         double dot  = edge1->_normal * quad.GetDir( ce._intEdges[i], ce._intEdges[i+1] );
7498         double fact = ( 1.1 + dot * dot );
7499         SMESH_TNodeXYZ pSrc0( ce.nSrc(i) ), pSrc1( ce.nSrc(i+1) );
7500         SMESH_TNodeXYZ pTgt0( ce.nTgt(i) ), pTgt1( ce.nTgt(i+1) );
7501         gp_XYZ pLast0 = pSrc0 + ( pTgt0 - pSrc0 ) * fact;
7502         gp_XYZ pLast1 = pSrc1 + ( pTgt1 - pSrc1 ) * fact;
7503         dist1 = dist2 = Precision::Infinite();
7504         if ( !edge1->SegTriaInter( lastSeg, pSrc0, pLast0, pSrc1,  dist1, eps ) &&
7505              !edge1->SegTriaInter( lastSeg, pSrc1, pLast1, pLast0, dist2, eps ))
7506           continue;
7507         dist = dist1;
7508         if ( dist > testLen || dist <= 0 )
7509         {
7510           dist = dist2;
7511           if ( dist > testLen || dist <= 0 )
7512             continue;
7513         }
7514         // choose a closest edge
7515         gp_Pnt intP( lastSeg.Location().XYZ() + lastSeg.Direction().XYZ() * ( dist + segLen ));
7516         double d1 = intP.SquareDistance( pSrc0 );
7517         double d2 = intP.SquareDistance( pSrc1 );
7518         int iClose = i + ( d2 < d1 );
7519         _LayerEdge* edge2 = ce._intEdges[iClose];
7520         edge2->Unset( _LayerEdge::MARKED );
7521
7522         // choose a closest edge among neighbors
7523         gp_Pnt srcP( SMESH_TNodeXYZ( edge1->_nodes[0] ));
7524         d1 = srcP.SquareDistance( SMESH_TNodeXYZ( edge2->_nodes[0] ));
7525         for ( size_t j = 0; j < intEdgesDist.size(); ++j )
7526         {
7527           _LayerEdge * edgeJ = intEdgesDist[j].first;
7528           if ( edge2->IsNeiborOnEdge( edgeJ ))
7529           {
7530             d2 = srcP.SquareDistance( SMESH_TNodeXYZ( edgeJ->_nodes[0] ));
7531             ( d1 < d2 ? edgeJ : edge2 )->Set( _LayerEdge::MARKED );
7532           }
7533         }
7534         intEdgesDist.push_back( make_pair( edge2, dist ));
7535         // if ( Abs( d2 - d1 ) / Max( d2, d1 ) < 0.5 )
7536         // {
7537         //   iClose = i + !( d2 < d1 );
7538         //   intEdges.push_back( ce._intEdges[iClose] );
7539         //   ce._intEdges[iClose]->Unset( _LayerEdge::MARKED );
7540         // }
7541         minIntDist = Min( edge1->_len * edge1->_lenFactor - segLen + dist, minIntDist );
7542       }
7543
7544       //ce._edge = 0;
7545
7546       // compute new _normals
7547       for ( size_t i = 0; i < intEdgesDist.size(); ++i )
7548       {
7549         _LayerEdge* edge2   = intEdgesDist[i].first;
7550         double      distWgt = edge1->_len / intEdgesDist[i].second;
7551         // if ( edge1->Is( _LayerEdge::BLOCKED ) &&
7552         //      edge2->Is( _LayerEdge::BLOCKED )) continue;        
7553         if ( edge2->Is( _LayerEdge::MARKED )) continue;
7554         edge2->Set( _LayerEdge::MARKED );
7555
7556         // get a new normal
7557         gp_XYZ dir1 = edge1->_normal, dir2 = edge2->_normal;
7558
7559         double cos1 = Abs( edge1->_cosin ), cos2 = Abs( edge2->_cosin );
7560         double wgt1 = ( cos1 + 0.001 ) / ( cos1 + cos2 + 0.002 );
7561         double wgt2 = ( cos2 + 0.001 ) / ( cos1 + cos2 + 0.002 );
7562         // double cos1 = Abs( edge1->_cosin ),        cos2 = Abs( edge2->_cosin );
7563         // double sgn1 = 0.1 * ( 1 + edge1->_cosin ), sgn2 = 0.1 * ( 1 + edge2->_cosin );
7564         // double wgt1 = ( cos1 + sgn1 ) / ( cos1 + cos2 + sgn1 + sgn2 );
7565         // double wgt2 = ( cos2 + sgn2 ) / ( cos1 + cos2 + sgn1 + sgn2 );
7566         gp_XYZ newNormal = wgt1 * dir1 + wgt2 * dir2;
7567         newNormal.Normalize();
7568
7569         // get new cosin
7570         double newCos;
7571         double sgn1 = edge1->_cosin / cos1, sgn2 = edge2->_cosin / cos2;
7572         if ( cos1 < theMinSmoothCosin )
7573         {
7574           newCos = cos2 * sgn1;
7575         }
7576         else if ( cos2 > theMinSmoothCosin ) // both cos1 and cos2 > theMinSmoothCosin
7577         {
7578           newCos = ( wgt1 * cos1 + wgt2 * cos2 ) * edge1->_cosin / cos1;
7579         }
7580         else
7581         {
7582           newCos = edge1->_cosin;
7583         }
7584
7585         e2neIt = edge2newEdge.insert( make_pair( edge1, zeroEdge )).first;
7586         e2neIt->second._normal += distWgt * newNormal;
7587         e2neIt->second._cosin   = newCos;
7588         e2neIt->second.SetMaxLen( 0.7 * minIntDist / edge1->_lenFactor );
7589         if ( iter > 0 && sgn1 * sgn2 < 0 && edge1->_cosin < 0 )
7590           e2neIt->second._normal += dir2;
7591
7592         e2neIt = edge2newEdge.insert( make_pair( edge2, zeroEdge )).first;
7593         e2neIt->second._normal += distWgt * newNormal;
7594         if ( Precision::IsInfinite( zeroEdge._maxLen ))
7595         {
7596           e2neIt->second._cosin  = edge2->_cosin;
7597           e2neIt->second.SetMaxLen( 1.3 * minIntDist / edge1->_lenFactor );
7598         }
7599         if ( iter > 0 && sgn1 * sgn2 < 0 && edge2->_cosin < 0 )
7600           e2neIt->second._normal += dir1;
7601       }
7602     }
7603
7604     if ( edge2newEdge.empty() )
7605       break; //return true;
7606
7607     dumpFunction(SMESH_Comment("updateNormals")<< data._index << "_" << stepNb << "_it" << iter);
7608
7609     // Update data of edges depending on a new _normal
7610
7611     data.UnmarkEdges();
7612     for ( e2neIt = edge2newEdge.begin(); e2neIt != edge2newEdge.end(); ++e2neIt )
7613     {
7614       _LayerEdge*    edge = e2neIt->first;
7615       _LayerEdge& newEdge = e2neIt->second;
7616       _EdgesOnShape*  eos = data.GetShapeEdges( edge );
7617       if ( edge->Is( _LayerEdge::BLOCKED ) && newEdge._maxLen > edge->_len )
7618         continue;
7619
7620       // Check if a new _normal is OK:
7621       newEdge._normal.Normalize();
7622       if ( !isNewNormalOk( data, *edge, newEdge._normal ))
7623       {
7624         if ( newEdge._maxLen < edge->_len && iter > 0 ) // limit _maxLen
7625         {
7626           edge->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true  );
7627           edge->SetMaxLen( newEdge._maxLen );
7628           edge->SetNewLength( newEdge._maxLen, *eos, helper );
7629         }
7630         continue; // the new _normal is bad
7631       }
7632       // the new _normal is OK
7633
7634       // find shapes that need smoothing due to change of _normal
7635       if ( edge->_cosin   < theMinSmoothCosin &&
7636            newEdge._cosin > theMinSmoothCosin )
7637       {
7638         if ( eos->_sWOL.IsNull() )
7639         {
7640           SMDS_ElemIteratorPtr fIt = edge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
7641           while ( fIt->more() )
7642             shapesToSmooth.insert( data.GetShapeEdges( fIt->next()->getshapeId() ));
7643         }
7644         else // edge inflates along a FACE
7645         {
7646           TopoDS_Shape V = helper.GetSubShapeByNode( edge->_nodes[0], getMeshDS() );
7647           PShapeIteratorPtr eIt = helper.GetAncestors( V, *_mesh, TopAbs_EDGE, &eos->_sWOL );
7648           while ( const TopoDS_Shape* E = eIt->next() )
7649           {
7650             gp_Vec edgeDir = getEdgeDir( TopoDS::Edge( *E ), TopoDS::Vertex( V ),
7651                                          eos->_hyp.Get1stLayerThickness() );
7652             double   angle = edgeDir.Angle( newEdge._normal ); // [0,PI]
7653             if ( angle < M_PI / 2 )
7654               shapesToSmooth.insert( data.GetShapeEdges( *E ));
7655           }
7656         }
7657       }
7658
7659       double len = edge->_len;
7660       edge->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true  );
7661       edge->SetNormal( newEdge._normal );
7662       edge->SetCosin( newEdge._cosin );
7663       edge->SetNewLength( len, *eos, helper );
7664       edge->Set( _LayerEdge::MARKED );
7665       edge->Set( _LayerEdge::NORMAL_UPDATED );
7666       edgesNoAnaSmooth.insert( eos );
7667     }
7668
7669     // Update normals and other dependent data of not intersecting _LayerEdge's
7670     // neighboring the intersecting ones
7671
7672     for ( e2neIt = edge2newEdge.begin(); e2neIt != edge2newEdge.end(); ++e2neIt )
7673     {
7674       _LayerEdge*   edge1 = e2neIt->first;
7675       _EdgesOnShape* eos1 = data.GetShapeEdges( edge1 );
7676       if ( !edge1->Is( _LayerEdge::MARKED ))
7677         continue;
7678
7679       if ( edge1->IsOnEdge() )
7680       {
7681         const SMDS_MeshNode * n1 = edge1->_2neibors->srcNode(0);
7682         const SMDS_MeshNode * n2 = edge1->_2neibors->srcNode(1);
7683         edge1->SetDataByNeighbors( n1, n2, *eos1, helper );
7684       }
7685
7686       if ( !edge1->_2neibors || !eos1->_sWOL.IsNull() )
7687         continue;
7688       for ( int j = 0; j < 2; ++j ) // loop on 2 neighbors
7689       {
7690         _LayerEdge* neighbor = edge1->_2neibors->_edges[j];
7691         if ( neighbor->Is( _LayerEdge::MARKED ) /*edge2newEdge.count ( neighbor )*/)
7692           continue; // j-th neighbor is also intersected
7693         _LayerEdge* prevEdge = edge1;
7694         const int nbSteps = 10;
7695         for ( int step = nbSteps; step; --step ) // step from edge1 in j-th direction
7696         {
7697           if ( neighbor->Is( _LayerEdge::BLOCKED ) ||
7698                neighbor->Is( _LayerEdge::MARKED ))
7699             break;
7700           _EdgesOnShape* eos = data.GetShapeEdges( neighbor );
7701           if ( !eos ) continue;
7702           _LayerEdge* nextEdge = neighbor;
7703           if ( neighbor->_2neibors )
7704           {
7705             int iNext = 0;
7706             nextEdge = neighbor->_2neibors->_edges[iNext];
7707             if ( nextEdge == prevEdge )
7708               nextEdge = neighbor->_2neibors->_edges[ ++iNext ];
7709           }
7710           double r = double(step-1)/nbSteps/(iter+1);
7711           if ( !nextEdge->_2neibors )
7712             r = Min( r, 0.5 );
7713
7714           gp_XYZ newNorm = prevEdge->_normal * r + nextEdge->_normal * (1-r);
7715           newNorm.Normalize();
7716           if ( !isNewNormalOk( data, *neighbor, newNorm ))
7717             break;
7718
7719           double len = neighbor->_len;
7720           neighbor->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true  );
7721           neighbor->SetNormal( newNorm );
7722           neighbor->SetCosin( prevEdge->_cosin * r + nextEdge->_cosin * (1-r) );
7723           if ( neighbor->_2neibors )
7724             neighbor->SetDataByNeighbors( prevEdge->_nodes[0], nextEdge->_nodes[0], *eos, helper );
7725           neighbor->SetNewLength( len, *eos, helper );
7726           neighbor->Set( _LayerEdge::MARKED );
7727           neighbor->Set( _LayerEdge::NORMAL_UPDATED );
7728           edgesNoAnaSmooth.insert( eos );
7729
7730           if ( !neighbor->_2neibors )
7731             break; // neighbor is on VERTEX
7732
7733           // goto the next neighbor
7734           prevEdge = neighbor;
7735           neighbor = nextEdge;
7736         }
7737       }
7738     }
7739     dumpFunctionEnd();
7740   } // iterations
7741
7742   data.AddShapesToSmooth( shapesToSmooth, &edgesNoAnaSmooth );
7743
7744   return true;
7745 }
7746
7747 //================================================================================
7748 /*!
7749  * \brief Check if a new normal is OK
7750  */
7751 //================================================================================
7752
7753 bool _ViscousBuilder::isNewNormalOk( _SolidData&   data,
7754                                      _LayerEdge&   edge,
7755                                      const gp_XYZ& newNormal)
7756 {
7757   // check a min angle between the newNormal and surrounding faces
7758   vector<_Simplex> simplices;
7759   SMESH_TNodeXYZ n0( edge._nodes[0] ), n1, n2;
7760   _Simplex::GetSimplices( n0._node, simplices, data._ignoreFaceIds, &data );
7761   double newMinDot = 1, curMinDot = 1;
7762   for ( size_t i = 0; i < simplices.size(); ++i )
7763   {
7764     n1.Set( simplices[i]._nPrev );
7765     n2.Set( simplices[i]._nNext );
7766     gp_XYZ normFace = ( n1 - n0 ) ^ ( n2 - n0 );
7767     double normLen2 = normFace.SquareModulus();
7768     if ( normLen2 < std::numeric_limits<double>::min() )
7769       continue;
7770     normFace /= Sqrt( normLen2 );
7771     newMinDot = Min( newNormal    * normFace, newMinDot );
7772     curMinDot = Min( edge._normal * normFace, curMinDot );
7773   }
7774   bool ok = true;
7775   if ( newMinDot < 0.5 )
7776   {
7777     ok = ( newMinDot >= curMinDot * 0.9 );
7778     //return ( newMinDot >= ( curMinDot * ( 0.8 + 0.1 * edge.NbSteps() )));
7779     // double initMinDot2 = 1. - edge._cosin * edge._cosin;
7780     // return ( newMinDot * newMinDot ) >= ( 0.8 * initMinDot2 );
7781   }
7782
7783   return ok;
7784 }
7785
7786 //================================================================================
7787 /*!
7788  * \brief Modify normals of _LayerEdge's on FACE to reflex smoothing
7789  */
7790 //================================================================================
7791
7792 bool _ViscousBuilder::updateNormalsOfSmoothed( _SolidData&         data,
7793                                                SMESH_MesherHelper& /*helper*/,
7794                                                const int           nbSteps,
7795                                                const double        stepSize )
7796 {
7797   if ( data._nbShapesToSmooth == 0 || nbSteps == 0 )
7798     return true; // no shapes needing smoothing
7799
7800   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7801   {
7802     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
7803     if ( //!eos._toSmooth ||  _eosC1 have _toSmooth == false
7804          !eos._hyp.ToSmooth() ||
7805          eos.ShapeType() != TopAbs_FACE ||
7806          eos._edges.empty() )
7807       continue;
7808
7809     bool toSmooth = ( eos._edges[ 0 ]->NbSteps() >= nbSteps+1 );
7810     if ( !toSmooth ) continue;
7811
7812     for ( size_t i = 0; i < eos._edges.size(); ++i )
7813     {
7814       _LayerEdge* edge = eos._edges[i];
7815       if ( !edge->Is( _LayerEdge::SMOOTHED ))
7816         continue;
7817       if ( edge->Is( _LayerEdge::DIFFICULT ) && nbSteps != 1 )
7818         continue;
7819
7820       const gp_XYZ& pPrev = edge->PrevPos();
7821       const gp_XYZ& pLast = edge->_pos.back();
7822       gp_XYZ      stepVec = pLast - pPrev;
7823       double realStepSize = stepVec.Modulus();
7824       if ( realStepSize < numeric_limits<double>::min() )
7825         continue;
7826
7827       edge->_lenFactor = realStepSize / stepSize;
7828       edge->_normal    = stepVec / realStepSize;
7829       edge->Set( _LayerEdge::NORMAL_UPDATED );
7830     }
7831   }
7832
7833   return true;
7834 }
7835
7836 //================================================================================
7837 /*!
7838  * \brief Modify normals of _LayerEdge's on C1 VERTEXes
7839  */
7840 //================================================================================
7841
7842 void _ViscousBuilder::updateNormalsOfC1Vertices( _SolidData& data )
7843 {
7844   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7845   {
7846     _EdgesOnShape& eov = data._edgesOnShape[ iS ];
7847     if ( eov._eosC1.empty() ||
7848          eov.ShapeType() != TopAbs_VERTEX ||
7849          eov._edges.empty() )
7850       continue;
7851
7852     gp_XYZ newNorm   = eov._edges[0]->_normal;
7853     double curThick  = eov._edges[0]->_len * eov._edges[0]->_lenFactor;
7854     bool normChanged = false;
7855
7856     for ( size_t i = 0; i < eov._eosC1.size(); ++i )
7857     {
7858       _EdgesOnShape*   eoe = eov._eosC1[i];
7859       const TopoDS_Edge& e = TopoDS::Edge( eoe->_shape );
7860       const double    eLen = SMESH_Algo::EdgeLength( e );
7861       TopoDS_Shape    oppV = SMESH_MesherHelper::IthVertex( 0, e );
7862       if ( oppV.IsSame( eov._shape ))
7863         oppV = SMESH_MesherHelper::IthVertex( 1, e );
7864       _EdgesOnShape* eovOpp = data.GetShapeEdges( oppV );
7865       if ( !eovOpp || eovOpp->_edges.empty() ) continue;
7866       if ( eov._edges[0]->Is( _LayerEdge::BLOCKED )) continue;
7867
7868       double curThickOpp = eovOpp->_edges[0]->_len * eovOpp->_edges[0]->_lenFactor;
7869       if ( curThickOpp + curThick < eLen )
7870         continue;
7871
7872       double wgt = 2. * curThick / eLen;
7873       newNorm += wgt * eovOpp->_edges[0]->_normal;
7874       normChanged = true;
7875     }
7876     if ( normChanged )
7877     {
7878       eov._edges[0]->SetNormal( newNorm.Normalized() );
7879       eov._edges[0]->Set( _LayerEdge::NORMAL_UPDATED );
7880     }
7881   }
7882 }
7883
7884 //================================================================================
7885 /*!
7886  * \brief Modify normals of _LayerEdge's on _ConvexFace's
7887  */
7888 //================================================================================
7889
7890 bool _ViscousBuilder::updateNormalsOfConvexFaces( _SolidData&         data,
7891                                                   SMESH_MesherHelper& helper,
7892                                                   int                 stepNb )
7893 {
7894   SMESHDS_Mesh* meshDS = helper.GetMeshDS();
7895   bool isOK;
7896
7897   map< TGeomID, _ConvexFace >::iterator id2face = data._convexFaces.begin();
7898   for ( ; id2face != data._convexFaces.end(); ++id2face )
7899   {
7900     _ConvexFace & convFace = (*id2face).second;
7901     convFace._normalsFixedOnBorders = false; // to update at each inflation step
7902
7903     if ( convFace._normalsFixed )
7904       continue; // already fixed
7905     if ( convFace.CheckPrisms() )
7906       continue; // nothing to fix
7907
7908     convFace._normalsFixed = true;
7909
7910     BRepAdaptor_Surface surface ( convFace._face, false );
7911     BRepLProp_SLProps   surfProp( surface, 2, 1e-6 );
7912
7913     // check if the convex FACE is of spherical shape
7914
7915     Bnd_B3d centersBox; // bbox of centers of curvature of _LayerEdge's on VERTEXes
7916     Bnd_B3d nodesBox;
7917     gp_Pnt  center;
7918
7919     map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.begin();
7920     for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7921     {
7922       _EdgesOnShape& eos = *(id2eos->second);
7923       if ( eos.ShapeType() == TopAbs_VERTEX )
7924       {
7925         _LayerEdge* ledge = eos._edges[ 0 ];
7926         if ( convFace.GetCenterOfCurvature( ledge, surfProp, helper, center ))
7927           centersBox.Add( center );
7928       }
7929       for ( size_t i = 0; i < eos._edges.size(); ++i )
7930         nodesBox.Add( SMESH_TNodeXYZ( eos._edges[ i ]->_nodes[0] ));
7931     }
7932     if ( centersBox.IsVoid() )
7933     {
7934       debugMsg( "Error: centersBox.IsVoid()" );
7935       return false;
7936     }
7937     const bool isSpherical =
7938       ( centersBox.SquareExtent() < 1e-6 * nodesBox.SquareExtent() );
7939
7940     int nbEdges = helper.Count( convFace._face, TopAbs_EDGE, /*ignoreSame=*/false );
7941     vector < _CentralCurveOnEdge > centerCurves( nbEdges );
7942
7943     if ( isSpherical )
7944     {
7945       // set _LayerEdge::_normal as average of all normals
7946
7947       // WARNING: different density of nodes on EDGEs is not taken into account that
7948       // can lead to an improper new normal
7949
7950       gp_XYZ avgNormal( 0,0,0 );
7951       nbEdges = 0;
7952       id2eos = convFace._subIdToEOS.begin();
7953       for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7954       {
7955         _EdgesOnShape& eos = *(id2eos->second);
7956         // set data of _CentralCurveOnEdge
7957         if ( eos.ShapeType() == TopAbs_EDGE )
7958         {
7959           _CentralCurveOnEdge& ceCurve = centerCurves[ nbEdges++ ];
7960           ceCurve.SetShapes( TopoDS::Edge( eos._shape ), convFace, data, helper );
7961           if ( !eos._sWOL.IsNull() )
7962             ceCurve._adjFace.Nullify();
7963           else
7964             ceCurve._ledges.insert( ceCurve._ledges.end(),
7965                                     eos._edges.begin(), eos._edges.end());
7966         }
7967         // summarize normals
7968         for ( size_t i = 0; i < eos._edges.size(); ++i )
7969           avgNormal += eos._edges[ i ]->_normal;
7970       }
7971       double normSize = avgNormal.SquareModulus();
7972       if ( normSize < 1e-200 )
7973       {
7974         debugMsg( "updateNormalsOfConvexFaces(): zero avgNormal" );
7975         return false;
7976       }
7977       avgNormal /= Sqrt( normSize );
7978
7979       // compute new _LayerEdge::_cosin on EDGEs
7980       double avgCosin = 0;
7981       int     nbCosin = 0;
7982       gp_Vec inFaceDir;
7983       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
7984       {
7985         _CentralCurveOnEdge& ceCurve = centerCurves[ iE ];
7986         if ( ceCurve._adjFace.IsNull() )
7987           continue;
7988         for ( size_t iLE = 0; iLE < ceCurve._ledges.size(); ++iLE )
7989         {
7990           const SMDS_MeshNode* node = ceCurve._ledges[ iLE ]->_nodes[0];
7991           inFaceDir = getFaceDir( ceCurve._adjFace, ceCurve._edge, node, helper, isOK );
7992           if ( isOK )
7993           {
7994             double angle = inFaceDir.Angle( avgNormal ); // [0,PI]
7995             ceCurve._ledges[ iLE ]->_cosin = Cos( angle );
7996             avgCosin += ceCurve._ledges[ iLE ]->_cosin;
7997             nbCosin++;
7998           }
7999         }
8000       }
8001       if ( nbCosin > 0 )
8002         avgCosin /= nbCosin;
8003
8004       // set _LayerEdge::_normal = avgNormal
8005       id2eos = convFace._subIdToEOS.begin();
8006       for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
8007       {
8008         _EdgesOnShape& eos = *(id2eos->second);
8009         if ( eos.ShapeType() != TopAbs_EDGE )
8010           for ( size_t i = 0; i < eos._edges.size(); ++i )
8011             eos._edges[ i ]->_cosin = avgCosin;
8012
8013         for ( size_t i = 0; i < eos._edges.size(); ++i )
8014         {
8015           eos._edges[ i ]->SetNormal( avgNormal );
8016           eos._edges[ i ]->Set( _LayerEdge::NORMAL_UPDATED );
8017         }
8018       }
8019     }
8020     else // if ( isSpherical )
8021     {
8022       // We suppose that centers of curvature at all points of the FACE
8023       // lie on some curve, let's call it "central curve". For all _LayerEdge's
8024       // having a common center of curvature we define the same new normal
8025       // as a sum of normals of _LayerEdge's on EDGEs among them.
8026
8027       // get all centers of curvature for each EDGE
8028
8029       helper.SetSubShape( convFace._face );
8030       _LayerEdge* vertexLEdges[2], **edgeLEdge, **edgeLEdgeEnd;
8031
8032       TopExp_Explorer edgeExp( convFace._face, TopAbs_EDGE );
8033       for ( int iE = 0; edgeExp.More(); edgeExp.Next(), ++iE )
8034       {
8035         const TopoDS_Edge& edge = TopoDS::Edge( edgeExp.Current() );
8036
8037         // set adjacent FACE
8038         centerCurves[ iE ].SetShapes( edge, convFace, data, helper );
8039
8040         // get _LayerEdge's of the EDGE
8041         TGeomID edgeID = meshDS->ShapeToIndex( edge );
8042         _EdgesOnShape* eos = data.GetShapeEdges( edgeID );
8043         if ( !eos || eos->_edges.empty() )
8044         {
8045           // no _LayerEdge's on EDGE, use _LayerEdge's on VERTEXes
8046           for ( int iV = 0; iV < 2; ++iV )
8047           {
8048             TopoDS_Vertex v = helper.IthVertex( iV, edge );
8049             TGeomID     vID = meshDS->ShapeToIndex( v );
8050             eos = data.GetShapeEdges( vID );
8051             vertexLEdges[ iV ] = eos->_edges[ 0 ];
8052           }
8053           edgeLEdge    = &vertexLEdges[0];
8054           edgeLEdgeEnd = edgeLEdge + 2;
8055
8056           centerCurves[ iE ]._adjFace.Nullify();
8057         }
8058         else
8059         {
8060           if ( ! eos->_toSmooth )
8061             data.SortOnEdge( edge, eos->_edges );
8062           edgeLEdge    = &eos->_edges[ 0 ];
8063           edgeLEdgeEnd = edgeLEdge + eos->_edges.size();
8064           vertexLEdges[0] = eos->_edges.front()->_2neibors->_edges[0];
8065           vertexLEdges[1] = eos->_edges.back() ->_2neibors->_edges[1];
8066
8067           if ( ! eos->_sWOL.IsNull() )
8068             centerCurves[ iE ]._adjFace.Nullify();
8069         }
8070
8071         // Get curvature centers
8072
8073         centersBox.Clear();
8074
8075         if ( edgeLEdge[0]->IsOnEdge() &&
8076              convFace.GetCenterOfCurvature( vertexLEdges[0], surfProp, helper, center ))
8077         { // 1st VERTEX
8078           centerCurves[ iE ].Append( center, vertexLEdges[0] );
8079           centersBox.Add( center );
8080         }
8081         for ( ; edgeLEdge < edgeLEdgeEnd; ++edgeLEdge )
8082           if ( convFace.GetCenterOfCurvature( *edgeLEdge, surfProp, helper, center ))
8083           { // EDGE or VERTEXes
8084             centerCurves[ iE ].Append( center, *edgeLEdge );
8085             centersBox.Add( center );
8086           }
8087         if ( edgeLEdge[-1]->IsOnEdge() &&
8088              convFace.GetCenterOfCurvature( vertexLEdges[1], surfProp, helper, center ))
8089         { // 2nd VERTEX
8090           centerCurves[ iE ].Append( center, vertexLEdges[1] );
8091           centersBox.Add( center );
8092         }
8093         centerCurves[ iE ]._isDegenerated =
8094           ( centersBox.IsVoid() || centersBox.SquareExtent() < 1e-6 * nodesBox.SquareExtent() );
8095
8096       } // loop on EDGES of convFace._face to set up data of centerCurves
8097
8098       // Compute new normals for _LayerEdge's on EDGEs
8099
8100       double avgCosin = 0;
8101       int     nbCosin = 0;
8102       gp_Vec inFaceDir;
8103       for ( size_t iE1 = 0; iE1 < centerCurves.size(); ++iE1 )
8104       {
8105         _CentralCurveOnEdge& ceCurve = centerCurves[ iE1 ];
8106         if ( ceCurve._isDegenerated )
8107           continue;
8108         const vector< gp_Pnt >& centers = ceCurve._curvaCenters;
8109         vector< gp_XYZ > &   newNormals = ceCurve._normals;
8110         for ( size_t iC1 = 0; iC1 < centers.size(); ++iC1 )
8111         {
8112           isOK = false;
8113           for ( size_t iE2 = 0; iE2 < centerCurves.size() && !isOK; ++iE2 )
8114           {
8115             if ( iE1 != iE2 )
8116               isOK = centerCurves[ iE2 ].FindNewNormal( centers[ iC1 ], newNormals[ iC1 ]);
8117           }
8118           if ( isOK && !ceCurve._adjFace.IsNull() )
8119           {
8120             // compute new _LayerEdge::_cosin
8121             const SMDS_MeshNode* node = ceCurve._ledges[ iC1 ]->_nodes[0];
8122             inFaceDir = getFaceDir( ceCurve._adjFace, ceCurve._edge, node, helper, isOK );
8123             if ( isOK )
8124             {
8125               double angle = inFaceDir.Angle( newNormals[ iC1 ] ); // [0,PI]
8126               ceCurve._ledges[ iC1 ]->_cosin = Cos( angle );
8127               avgCosin += ceCurve._ledges[ iC1 ]->_cosin;
8128               nbCosin++;
8129             }
8130           }
8131         }
8132       }
8133       // set new normals to _LayerEdge's of NOT degenerated central curves
8134       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
8135       {
8136         if ( centerCurves[ iE ]._isDegenerated )
8137           continue;
8138         for ( size_t iLE = 0; iLE < centerCurves[ iE ]._ledges.size(); ++iLE )
8139         {
8140           centerCurves[ iE ]._ledges[ iLE ]->SetNormal( centerCurves[ iE ]._normals[ iLE ]);
8141           centerCurves[ iE ]._ledges[ iLE ]->Set( _LayerEdge::NORMAL_UPDATED );
8142         }
8143       }
8144       // set new normals to _LayerEdge's of     degenerated central curves
8145       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
8146       {
8147         if ( !centerCurves[ iE ]._isDegenerated ||
8148              centerCurves[ iE ]._ledges.size() < 3 )
8149           continue;
8150         // new normal is an average of new normals at VERTEXes that
8151         // was computed on non-degenerated _CentralCurveOnEdge's
8152         gp_XYZ newNorm = ( centerCurves[ iE ]._ledges.front()->_normal +
8153                            centerCurves[ iE ]._ledges.back ()->_normal );
8154         double sz = newNorm.Modulus();
8155         if ( sz < 1e-200 )
8156           continue;
8157         newNorm /= sz;
8158         double newCosin = ( 0.5 * centerCurves[ iE ]._ledges.front()->_cosin +
8159                             0.5 * centerCurves[ iE ]._ledges.back ()->_cosin );
8160         for ( size_t iLE = 1, nb = centerCurves[ iE ]._ledges.size() - 1; iLE < nb; ++iLE )
8161         {
8162           centerCurves[ iE ]._ledges[ iLE ]->SetNormal( newNorm );
8163           centerCurves[ iE ]._ledges[ iLE ]->_cosin   = newCosin;
8164           centerCurves[ iE ]._ledges[ iLE ]->Set( _LayerEdge::NORMAL_UPDATED );
8165         }
8166       }
8167
8168       // Find new normals for _LayerEdge's based on FACE
8169
8170       if ( nbCosin > 0 )
8171         avgCosin /= nbCosin;
8172       const TGeomID faceID = meshDS->ShapeToIndex( convFace._face );
8173       map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.find( faceID );
8174       if ( id2eos != convFace._subIdToEOS.end() )
8175       {
8176         int iE = 0;
8177         gp_XYZ newNorm;
8178         _EdgesOnShape& eos = * ( id2eos->second );
8179         for ( size_t i = 0; i < eos._edges.size(); ++i )
8180         {
8181           _LayerEdge* ledge = eos._edges[ i ];
8182           if ( !convFace.GetCenterOfCurvature( ledge, surfProp, helper, center ))
8183             continue;
8184           for ( size_t i = 0; i < centerCurves.size(); ++i, ++iE )
8185           {
8186             iE = iE % centerCurves.size();
8187             if ( centerCurves[ iE ]._isDegenerated )
8188               continue;
8189             newNorm.SetCoord( 0,0,0 );
8190             if ( centerCurves[ iE ].FindNewNormal( center, newNorm ))
8191             {
8192               ledge->SetNormal( newNorm );
8193               ledge->_cosin  = avgCosin;
8194               ledge->Set( _LayerEdge::NORMAL_UPDATED );
8195               break;
8196             }
8197           }
8198         }
8199       }
8200
8201     } // not a quasi-spherical FACE
8202
8203     // Update _LayerEdge's data according to a new normal
8204
8205     dumpFunction(SMESH_Comment("updateNormalsOfConvexFaces")<<data._index
8206                  <<"_F"<<meshDS->ShapeToIndex( convFace._face ));
8207
8208     id2eos = convFace._subIdToEOS.begin();
8209     for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
8210     {
8211       _EdgesOnShape& eos = * ( id2eos->second );
8212       for ( size_t i = 0; i < eos._edges.size(); ++i )
8213       {
8214         _LayerEdge* & ledge = eos._edges[ i ];
8215         double len = ledge->_len;
8216         ledge->InvalidateStep( stepNb + 1, eos, /*restoreLength=*/true );
8217         ledge->SetCosin( ledge->_cosin );
8218         ledge->SetNewLength( len, eos, helper );
8219       }
8220       if ( eos.ShapeType() != TopAbs_FACE )
8221         for ( size_t i = 0; i < eos._edges.size(); ++i )
8222         {
8223           _LayerEdge* ledge = eos._edges[ i ];
8224           for ( size_t iN = 0; iN < ledge->_neibors.size(); ++iN )
8225           {
8226             _LayerEdge* neibor = ledge->_neibors[iN];
8227             if ( neibor->_nodes[0]->GetPosition()->GetDim() == 2 )
8228             {
8229               neibor->Set( _LayerEdge::NEAR_BOUNDARY );
8230               neibor->Set( _LayerEdge::MOVED );
8231               neibor->SetSmooLen( neibor->_len );
8232             }
8233           }
8234         }
8235     } // loop on sub-shapes of convFace._face
8236
8237     // Find FACEs adjacent to convFace._face that got necessity to smooth
8238     // as a result of normals modification
8239
8240     set< _EdgesOnShape* > adjFacesToSmooth;
8241     for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
8242     {
8243       if ( centerCurves[ iE ]._adjFace.IsNull() ||
8244            centerCurves[ iE ]._adjFaceToSmooth )
8245         continue;
8246       for ( size_t iLE = 0; iLE < centerCurves[ iE ]._ledges.size(); ++iLE )
8247       {
8248         if ( centerCurves[ iE ]._ledges[ iLE ]->_cosin > theMinSmoothCosin )
8249         {
8250           adjFacesToSmooth.insert( data.GetShapeEdges( centerCurves[ iE ]._adjFace ));
8251           break;
8252         }
8253       }
8254     }
8255     data.AddShapesToSmooth( adjFacesToSmooth );
8256
8257     dumpFunctionEnd();
8258
8259
8260   } // loop on data._convexFaces
8261
8262   return true;
8263 }
8264
8265 //================================================================================
8266 /*!
8267  * \brief Return max curvature of a FACE
8268  */
8269 //================================================================================
8270
8271 double _ConvexFace::GetMaxCurvature( _SolidData&         data,
8272                                      _EdgesOnShape&      eof,
8273                                      BRepLProp_SLProps&  surfProp,
8274                                      SMESH_MesherHelper& helper)
8275 {
8276   double maxCurvature = 0;
8277
8278   TopoDS_Face F = TopoDS::Face( eof._shape );
8279
8280   const int           nbTestPnt = 5;
8281   const double        oriFactor = ( F.Orientation() == TopAbs_REVERSED ? +1. : -1. );
8282   SMESH_subMeshIteratorPtr smIt = eof._subMesh->getDependsOnIterator(/*includeSelf=*/true);
8283   while ( smIt->more() )
8284   {
8285     SMESH_subMesh* sm = smIt->next();
8286     const TGeomID subID = sm->GetId();
8287
8288     // find _LayerEdge's of a sub-shape
8289     _EdgesOnShape* eos;
8290     if (( eos = data.GetShapeEdges( subID )))
8291       this->_subIdToEOS.insert( make_pair( subID, eos ));
8292     else
8293       continue;
8294
8295     // check concavity and curvature and limit data._stepSize
8296     const double minCurvature =
8297       1. / ( eos->_hyp.GetTotalThickness() * ( 1 + theThickToIntersection ));
8298     size_t iStep = Max( 1, eos->_edges.size() / nbTestPnt );
8299     for ( size_t i = 0; i < eos->_edges.size(); i += iStep )
8300     {
8301       gp_XY uv = helper.GetNodeUV( F, eos->_edges[ i ]->_nodes[0] );
8302       surfProp.SetParameters( uv.X(), uv.Y() );
8303       if ( surfProp.IsCurvatureDefined() )
8304       {
8305         double curvature = Max( surfProp.MaxCurvature() * oriFactor,
8306                                 surfProp.MinCurvature() * oriFactor );
8307         maxCurvature = Max( maxCurvature, curvature );
8308
8309         if ( curvature > minCurvature )
8310           this->_isTooCurved = true;
8311       }
8312     }
8313   } // loop on sub-shapes of the FACE
8314
8315   return maxCurvature;
8316 }
8317
8318 //================================================================================
8319 /*!
8320  * \brief Finds a center of curvature of a surface at a _LayerEdge
8321  */
8322 //================================================================================
8323
8324 bool _ConvexFace::GetCenterOfCurvature( _LayerEdge*         ledge,
8325                                         BRepLProp_SLProps&  surfProp,
8326                                         SMESH_MesherHelper& helper,
8327                                         gp_Pnt &            center ) const
8328 {
8329   gp_XY uv = helper.GetNodeUV( _face, ledge->_nodes[0] );
8330   surfProp.SetParameters( uv.X(), uv.Y() );
8331   if ( !surfProp.IsCurvatureDefined() )
8332     return false;
8333
8334   const double oriFactor = ( _face.Orientation() == TopAbs_REVERSED ? +1. : -1. );
8335   double surfCurvatureMax = surfProp.MaxCurvature() * oriFactor;
8336   double surfCurvatureMin = surfProp.MinCurvature() * oriFactor;
8337   if ( surfCurvatureMin > surfCurvatureMax )
8338     center = surfProp.Value().Translated( surfProp.Normal().XYZ() / surfCurvatureMin * oriFactor );
8339   else
8340     center = surfProp.Value().Translated( surfProp.Normal().XYZ() / surfCurvatureMax * oriFactor );
8341
8342   return true;
8343 }
8344
8345 //================================================================================
8346 /*!
8347  * \brief Check that prisms are not distorted
8348  */
8349 //================================================================================
8350
8351 bool _ConvexFace::CheckPrisms() const
8352 {
8353   double vol = 0;
8354   for ( size_t i = 0; i < _simplexTestEdges.size(); ++i )
8355   {
8356     const _LayerEdge* edge = _simplexTestEdges[i];
8357     SMESH_TNodeXYZ tgtXYZ( edge->_nodes.back() );
8358     for ( size_t j = 0; j < edge->_simplices.size(); ++j )
8359       if ( !edge->_simplices[j].IsForward( edge->_nodes[0], tgtXYZ, vol ))
8360       {
8361         debugMsg( "Bad simplex of _simplexTestEdges ("
8362                   << " "<< edge->_nodes[0]->GetID()<< " "<< tgtXYZ._node->GetID()
8363                   << " "<< edge->_simplices[j]._nPrev->GetID()
8364                   << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
8365         return false;
8366       }
8367   }
8368   return true;
8369 }
8370
8371 //================================================================================
8372 /*!
8373  * \brief Try to compute a new normal by interpolating normals of _LayerEdge's
8374  *        stored in this _CentralCurveOnEdge.
8375  *  \param [in] center - curvature center of a point of another _CentralCurveOnEdge.
8376  *  \param [in,out] newNormal - current normal at this point, to be redefined
8377  *  \return bool - true if succeeded.
8378  */
8379 //================================================================================
8380
8381 bool _CentralCurveOnEdge::FindNewNormal( const gp_Pnt& center, gp_XYZ& newNormal )
8382 {
8383   if ( this->_isDegenerated )
8384     return false;
8385
8386   // find two centers the given one lies between
8387
8388   for ( size_t i = 0, nb = _curvaCenters.size()-1;  i < nb;  ++i )
8389   {
8390     double sl2 = 1.001 * _segLength2[ i ];
8391
8392     double d1 = center.SquareDistance( _curvaCenters[ i ]);
8393     if ( d1 > sl2 )
8394       continue;
8395     
8396     double d2 = center.SquareDistance( _curvaCenters[ i+1 ]);
8397     if ( d2 > sl2 || d2 + d1 < 1e-100 )
8398       continue;
8399
8400     d1 = Sqrt( d1 );
8401     d2 = Sqrt( d2 );
8402     double r = d1 / ( d1 + d2 );
8403     gp_XYZ norm = (( 1. - r ) * _ledges[ i   ]->_normal +
8404                    (      r ) * _ledges[ i+1 ]->_normal );
8405     norm.Normalize();
8406
8407     newNormal += norm;
8408     double sz = newNormal.Modulus();
8409     if ( sz < 1e-200 )
8410       break;
8411     newNormal /= sz;
8412     return true;
8413   }
8414   return false;
8415 }
8416
8417 //================================================================================
8418 /*!
8419  * \brief Set shape members
8420  */
8421 //================================================================================
8422
8423 void _CentralCurveOnEdge::SetShapes( const TopoDS_Edge&  edge,
8424                                      const _ConvexFace&  convFace,
8425                                      _SolidData&         data,
8426                                      SMESH_MesherHelper& helper)
8427 {
8428   _edge = edge;
8429
8430   PShapeIteratorPtr fIt = helper.GetAncestors( edge, *helper.GetMesh(), TopAbs_FACE );
8431   while ( const TopoDS_Shape* F = fIt->next())
8432     if ( !convFace._face.IsSame( *F ))
8433     {
8434       _adjFace = TopoDS::Face( *F );
8435       _adjFaceToSmooth = false;
8436       // _adjFace already in a smoothing queue ?
8437       if ( _EdgesOnShape* eos = data.GetShapeEdges( _adjFace ))
8438         _adjFaceToSmooth = eos->_toSmooth;
8439       break;
8440     }
8441 }
8442
8443 //================================================================================
8444 /*!
8445  * \brief Looks for intersection of it's last segment with faces
8446  *  \param distance - returns shortest distance from the last node to intersection
8447  */
8448 //================================================================================
8449
8450 bool _LayerEdge::FindIntersection( SMESH_ElementSearcher&   searcher,
8451                                    double &                 distance,
8452                                    const double&            epsilon,
8453                                    _EdgesOnShape&           eos,
8454                                    const SMDS_MeshElement** intFace)
8455 {
8456   vector< const SMDS_MeshElement* > suspectFaces;
8457   double segLen;
8458   gp_Ax1 lastSegment = LastSegment( segLen, eos );
8459   searcher.GetElementsNearLine( lastSegment, SMDSAbs_Face, suspectFaces );
8460
8461   bool segmentIntersected = false;
8462   distance = Precision::Infinite();
8463   int iFace = -1; // intersected face
8464   for ( size_t j = 0 ; j < suspectFaces.size() /*&& !segmentIntersected*/; ++j )
8465   {
8466     const SMDS_MeshElement* face = suspectFaces[j];
8467     if ( face->GetNodeIndex( _nodes.back() ) >= 0 ||
8468          face->GetNodeIndex( _nodes[0]     ) >= 0 )
8469       continue; // face sharing _LayerEdge node
8470     const int nbNodes = face->NbCornerNodes();
8471     bool intFound = false;
8472     double dist;
8473     SMDS_MeshElement::iterator nIt = face->begin_nodes();
8474     if ( nbNodes == 3 )
8475     {
8476       intFound = SegTriaInter( lastSegment, *nIt++, *nIt++, *nIt++, dist, epsilon );
8477     }
8478     else
8479     {
8480       const SMDS_MeshNode* tria[3];
8481       tria[0] = *nIt++;
8482       tria[1] = *nIt++;
8483       for ( int n2 = 2; n2 < nbNodes && !intFound; ++n2 )
8484       {
8485         tria[2] = *nIt++;
8486         intFound = SegTriaInter(lastSegment, tria[0], tria[1], tria[2], dist, epsilon );
8487         tria[1] = tria[2];
8488       }
8489     }
8490     if ( intFound )
8491     {
8492       if ( dist < segLen*(1.01) && dist > -(_len*_lenFactor-segLen) )
8493         segmentIntersected = true;
8494       if ( distance > dist )
8495         distance = dist, iFace = j;
8496     }
8497   }
8498   if ( intFace ) *intFace = ( iFace != -1 ) ? suspectFaces[iFace] : 0;
8499
8500   distance -= segLen;
8501
8502   if ( segmentIntersected )
8503   {
8504 #ifdef __myDEBUG
8505     SMDS_MeshElement::iterator nIt = suspectFaces[iFace]->begin_nodes();
8506     gp_XYZ intP( lastSegment.Location().XYZ() + lastSegment.Direction().XYZ() * ( distance+segLen ));
8507     cout << "nodes: tgt " << _nodes.back()->GetID() << " src " << _nodes[0]->GetID()
8508          << ", intersection with face ("
8509          << (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()
8510          << ") at point (" << intP.X() << ", " << intP.Y() << ", " << intP.Z()
8511          << ") distance = " << distance << endl;
8512 #endif
8513   }
8514
8515   return segmentIntersected;
8516 }
8517
8518 //================================================================================
8519 /*!
8520  * \brief Returns a point used to check orientation of _simplices
8521  */
8522 //================================================================================
8523
8524 gp_XYZ _LayerEdge::PrevCheckPos( _EdgesOnShape* eos ) const
8525 {
8526   size_t i = Is( NORMAL_UPDATED ) && IsOnFace() ? _pos.size()-2 : 0;
8527
8528   if ( !eos || eos->_sWOL.IsNull() )
8529     return _pos[ i ];
8530
8531   if ( eos->SWOLType() == TopAbs_EDGE )
8532   {
8533     return BRepAdaptor_Curve( TopoDS::Edge( eos->_sWOL )).Value( _pos[i].X() ).XYZ();
8534   }
8535   //else //  TopAbs_FACE
8536
8537   return BRepAdaptor_Surface( TopoDS::Face( eos->_sWOL )).Value(_pos[i].X(), _pos[i].Y() ).XYZ();
8538 }
8539
8540 //================================================================================
8541 /*!
8542  * \brief Returns size and direction of the last segment
8543  */
8544 //================================================================================
8545
8546 gp_Ax1 _LayerEdge::LastSegment(double& segLen, _EdgesOnShape& eos) const
8547 {
8548   // find two non-coincident positions
8549   gp_XYZ orig = _pos.back();
8550   gp_XYZ vec;
8551   int iPrev = _pos.size() - 2;
8552   //const double tol = ( _len > 0 ) ? 0.3*_len : 1e-100; // adjusted for IPAL52478 + PAL22576
8553   const double tol = ( _len > 0 ) ? ( 1e-6 * _len ) : 1e-100;
8554   while ( iPrev >= 0 )
8555   {
8556     vec = orig - _pos[iPrev];
8557     if ( vec.SquareModulus() > tol*tol )
8558       break;
8559     else
8560       iPrev--;
8561   }
8562
8563   // make gp_Ax1
8564   gp_Ax1 segDir;
8565   if ( iPrev < 0 )
8566   {
8567     segDir.SetLocation( SMESH_TNodeXYZ( _nodes[0] ));
8568     segDir.SetDirection( _normal );
8569     segLen = 0;
8570   }
8571   else
8572   {
8573     gp_Pnt pPrev = _pos[ iPrev ];
8574     if ( !eos._sWOL.IsNull() )
8575     {
8576       TopLoc_Location loc;
8577       if ( eos.SWOLType() == TopAbs_EDGE )
8578       {
8579         double f,l;
8580         Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( eos._sWOL ), loc, f,l);
8581         pPrev = curve->Value( pPrev.X() ).Transformed( loc );
8582       }
8583       else
8584       {
8585         Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face( eos._sWOL ), loc );
8586         pPrev = surface->Value( pPrev.X(), pPrev.Y() ).Transformed( loc );
8587       }
8588       vec = SMESH_TNodeXYZ( _nodes.back() ) - pPrev.XYZ();
8589     }
8590     segDir.SetLocation( pPrev );
8591     segDir.SetDirection( vec );
8592     segLen = vec.Modulus();
8593   }
8594
8595   return segDir;
8596 }
8597
8598 //================================================================================
8599 /*!
8600  * \brief Return the last (or \a which) position of the target node on a FACE. 
8601  *  \param [in] F - the FACE this _LayerEdge is inflated along
8602  *  \param [in] which - index of position
8603  *  \return gp_XY - result UV
8604  */
8605 //================================================================================
8606
8607 gp_XY _LayerEdge::LastUV( const TopoDS_Face& F, _EdgesOnShape& eos, int which ) const
8608 {
8609   if ( F.IsSame( eos._sWOL )) // F is my FACE
8610     return gp_XY( _pos.back().X(), _pos.back().Y() );
8611
8612   if ( eos.SWOLType() != TopAbs_EDGE ) // wrong call
8613     return gp_XY( 1e100, 1e100 );
8614
8615   // _sWOL is EDGE of F; _pos.back().X() is the last U on the EDGE
8616   double f, l, u = _pos[ which < 0 ? _pos.size()-1 : which ].X();
8617   Handle(Geom2d_Curve) C2d = BRep_Tool::CurveOnSurface( TopoDS::Edge(eos._sWOL), F, f,l);
8618   if ( !C2d.IsNull() && f <= u && u <= l )
8619     return C2d->Value( u ).XY();
8620
8621   return gp_XY( 1e100, 1e100 );
8622 }
8623
8624 //================================================================================
8625 /*!
8626  * \brief Test intersection of the last segment with a given triangle
8627  *   using Moller-Trumbore algorithm
8628  * Intersection is detected if distance to intersection is less than _LayerEdge._len
8629  */
8630 //================================================================================
8631
8632 bool _LayerEdge::SegTriaInter( const gp_Ax1& lastSegment,
8633                                const gp_XYZ& vert0,
8634                                const gp_XYZ& vert1,
8635                                const gp_XYZ& vert2,
8636                                double&       t,
8637                                const double& EPSILON) const
8638 {
8639   const gp_Pnt& orig = lastSegment.Location();
8640   const gp_Dir& dir  = lastSegment.Direction();
8641
8642   /* calculate distance from vert0 to ray origin */
8643   //gp_XYZ tvec = orig.XYZ() - vert0;
8644
8645   //if ( tvec * dir > EPSILON )
8646     // intersected face is at back side of the temporary face this _LayerEdge belongs to
8647     //return false;
8648
8649   gp_XYZ edge1 = vert1 - vert0;
8650   gp_XYZ edge2 = vert2 - vert0;
8651
8652   /* begin calculating determinant - also used to calculate U parameter */
8653   gp_XYZ pvec = dir.XYZ() ^ edge2;
8654
8655   /* if determinant is near zero, ray lies in plane of triangle */
8656   double det = edge1 * pvec;
8657
8658   const double ANGL_EPSILON = 1e-12;
8659   if ( det > -ANGL_EPSILON && det < ANGL_EPSILON )
8660     return false;
8661
8662   /* calculate distance from vert0 to ray origin */
8663   gp_XYZ tvec = orig.XYZ() - vert0;
8664
8665   /* calculate U parameter and test bounds */
8666   double u = ( tvec * pvec ) / det;
8667   //if (u < 0.0 || u > 1.0)
8668   if ( u < -EPSILON || u > 1.0 + EPSILON )
8669     return false;
8670
8671   /* prepare to test V parameter */
8672   gp_XYZ qvec = tvec ^ edge1;
8673
8674   /* calculate V parameter and test bounds */
8675   double v = (dir.XYZ() * qvec) / det;
8676   //if ( v < 0.0 || u + v > 1.0 )
8677   if ( v < -EPSILON || u + v > 1.0 + EPSILON )
8678     return false;
8679
8680   /* calculate t, ray intersects triangle */
8681   t = (edge2 * qvec) / det;
8682
8683   //return true;
8684   return t > 0.;
8685 }
8686
8687 //================================================================================
8688 /*!
8689  * \brief _LayerEdge, located at a concave VERTEX of a FACE, moves target nodes of
8690  *        neighbor _LayerEdge's by it's own inflation vector.
8691  *  \param [in] eov - EOS of the VERTEX
8692  *  \param [in] eos - EOS of the FACE
8693  *  \param [in] step - inflation step
8694  *  \param [in,out] badSmooEdges - tangled _LayerEdge's
8695  */
8696 //================================================================================
8697
8698 void _LayerEdge::MoveNearConcaVer( const _EdgesOnShape*    eov,
8699                                    const _EdgesOnShape*    eos,
8700                                    const int               step,
8701                                    vector< _LayerEdge* > & badSmooEdges )
8702 {
8703   // check if any of _neibors is in badSmooEdges
8704   if ( std::find_first_of( _neibors.begin(), _neibors.end(),
8705                            badSmooEdges.begin(), badSmooEdges.end() ) == _neibors.end() )
8706     return;
8707
8708   // get all edges to move
8709
8710   set< _LayerEdge* > edges;
8711
8712   // find a distance between _LayerEdge on VERTEX and its neighbors
8713   gp_XYZ  curPosV = SMESH_TNodeXYZ( _nodes.back() );
8714   double dist2 = 0;
8715   for ( size_t i = 0; i < _neibors.size(); ++i )
8716   {
8717     _LayerEdge* nEdge = _neibors[i];
8718     if ( nEdge->_nodes[0]->getshapeId() == eos->_shapeID )
8719     {
8720       edges.insert( nEdge );
8721       dist2 = Max( dist2, ( curPosV - nEdge->_pos.back() ).SquareModulus() );
8722     }
8723   }
8724   // add _LayerEdge's close to curPosV
8725   size_t nbE;
8726   do {
8727     nbE = edges.size();
8728     for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8729     {
8730       _LayerEdge* edgeF = *e;
8731       for ( size_t i = 0; i < edgeF->_neibors.size(); ++i )
8732       {
8733         _LayerEdge* nEdge = edgeF->_neibors[i];
8734         if ( nEdge->_nodes[0]->getshapeId() == eos->_shapeID &&
8735              dist2 > ( curPosV - nEdge->_pos.back() ).SquareModulus() )
8736           edges.insert( nEdge );
8737       }
8738     }
8739   }
8740   while ( nbE < edges.size() );
8741
8742   // move the target node of the got edges
8743
8744   gp_XYZ prevPosV = PrevPos();
8745   if ( eov->SWOLType() == TopAbs_EDGE )
8746   {
8747     BRepAdaptor_Curve curve ( TopoDS::Edge( eov->_sWOL ));
8748     prevPosV = curve.Value( prevPosV.X() ).XYZ();
8749   }
8750   else if ( eov->SWOLType() == TopAbs_FACE )
8751   {
8752     BRepAdaptor_Surface surface( TopoDS::Face( eov->_sWOL ));
8753     prevPosV = surface.Value( prevPosV.X(), prevPosV.Y() ).XYZ();
8754   }
8755
8756   SMDS_FacePositionPtr fPos;
8757   //double r = 1. - Min( 0.9, step / 10. );
8758   for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8759   {
8760     _LayerEdge*       edgeF = *e;
8761     const gp_XYZ     prevVF = edgeF->PrevPos() - prevPosV;
8762     const gp_XYZ    newPosF = curPosV + prevVF;
8763     SMDS_MeshNode* tgtNodeF = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
8764     tgtNodeF->setXYZ( newPosF.X(), newPosF.Y(), newPosF.Z() );
8765     edgeF->_pos.back() = newPosF;
8766     dumpMoveComm( tgtNodeF, "MoveNearConcaVer" ); // debug
8767
8768     // set _curvature to make edgeF updated by putOnOffsetSurface()
8769     if ( !edgeF->_curvature )
8770       if (( fPos = edgeF->_nodes[0]->GetPosition() ))
8771       {
8772         edgeF->_curvature = _Factory::NewCurvature();
8773         edgeF->_curvature->_r = 0;
8774         edgeF->_curvature->_k = 0;
8775         edgeF->_curvature->_h2lenRatio = 0;
8776         edgeF->_curvature->_uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
8777       }
8778   }
8779   // gp_XYZ inflationVec( SMESH_TNodeXYZ( _nodes.back() ) -
8780   //                      SMESH_TNodeXYZ( _nodes[0]    ));
8781   // for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8782   // {
8783   //   _LayerEdge*      edgeF = *e;
8784   //   gp_XYZ          newPos = SMESH_TNodeXYZ( edgeF->_nodes[0] ) + inflationVec;
8785   //   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
8786   //   tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8787   //   edgeF->_pos.back() = newPosF;
8788   //   dumpMoveComm( tgtNode, "MoveNearConcaVer" ); // debug
8789   // }
8790
8791   // smooth _LayerEdge's around moved nodes
8792   //size_t nbBadBefore = badSmooEdges.size();
8793   for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8794   {
8795     _LayerEdge* edgeF = *e;
8796     for ( size_t j = 0; j < edgeF->_neibors.size(); ++j )
8797       if ( edgeF->_neibors[j]->_nodes[0]->getshapeId() == eos->_shapeID )
8798         //&& !edges.count( edgeF->_neibors[j] ))
8799       {
8800         _LayerEdge* edgeFN = edgeF->_neibors[j];
8801         edgeFN->Unset( SMOOTHED );
8802         int nbBad = edgeFN->Smooth( step, /*isConcaFace=*/true, /*findBest=*/true );
8803         // if ( nbBad > 0 )
8804         // {
8805         //   gp_XYZ         newPos = SMESH_TNodeXYZ( edgeFN->_nodes[0] ) + inflationVec;
8806         //   const gp_XYZ& prevPos = edgeFN->_pos[ edgeFN->_pos.size()-2 ];
8807         //   int        nbBadAfter = edgeFN->_simplices.size();
8808         //   double vol;
8809         //   for ( size_t iS = 0; iS < edgeFN->_simplices.size(); ++iS )
8810         //   {
8811         //     nbBadAfter -= edgeFN->_simplices[iS].IsForward( &prevPos, &newPos, vol );
8812         //   }
8813         //   if ( nbBadAfter <= nbBad )
8814         //   {
8815         //     SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeFN->_nodes.back() );
8816         //     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8817         //     edgeF->_pos.back() = newPosF;
8818         //     dumpMoveComm( tgtNode, "MoveNearConcaVer 2" ); // debug
8819         //     nbBad = nbBadAfter;
8820         //   }
8821         // }
8822         if ( nbBad > 0 )
8823           badSmooEdges.push_back( edgeFN );
8824       }
8825   }
8826     // move a bit not smoothed around moved nodes
8827   //   for ( size_t i = nbBadBefore; i < badSmooEdges.size(); ++i )
8828   //   {
8829   //   _LayerEdge*      edgeF = badSmooEdges[i];
8830   //   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
8831   //   gp_XYZ         newPos1 = SMESH_TNodeXYZ( edgeF->_nodes[0] ) + inflationVec;
8832   //   gp_XYZ         newPos2 = 0.5 * ( newPos1 + SMESH_TNodeXYZ( tgtNode ));
8833   //   tgtNode->setXYZ( newPos2.X(), newPos2.Y(), newPos2.Z() );
8834   //   edgeF->_pos.back() = newPosF;
8835   //   dumpMoveComm( tgtNode, "MoveNearConcaVer 2" ); // debug
8836   // }
8837 }
8838
8839 //================================================================================
8840 /*!
8841  * \brief Perform smooth of _LayerEdge's based on EDGE's
8842  *  \retval bool - true if node has been moved
8843  */
8844 //================================================================================
8845
8846 bool _LayerEdge::SmoothOnEdge(Handle(ShapeAnalysis_Surface)& surface,
8847                               const TopoDS_Face&             F,
8848                               SMESH_MesherHelper&            helper)
8849 {
8850   ASSERT( IsOnEdge() );
8851
8852   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _nodes.back() );
8853   SMESH_TNodeXYZ oldPos( tgtNode );
8854   double dist01, distNewOld;
8855   
8856   SMESH_TNodeXYZ p0( _2neibors->tgtNode(0));
8857   SMESH_TNodeXYZ p1( _2neibors->tgtNode(1));
8858   dist01 = p0.Distance( _2neibors->tgtNode(1) );
8859
8860   gp_Pnt newPos = p0 * _2neibors->_wgt[0] + p1 * _2neibors->_wgt[1];
8861   double lenDelta = 0;
8862   if ( _curvature )
8863   {
8864     //lenDelta = _curvature->lenDelta( _len );
8865     lenDelta = _curvature->lenDeltaByDist( dist01 );
8866     newPos.ChangeCoord() += _normal * lenDelta;
8867   }
8868
8869   distNewOld = newPos.Distance( oldPos );
8870
8871   if ( F.IsNull() )
8872   {
8873     if ( _2neibors->_plnNorm )
8874     {
8875       // put newPos on the plane defined by source node and _plnNorm
8876       gp_XYZ new2src = SMESH_TNodeXYZ( _nodes[0] ) - newPos.XYZ();
8877       double new2srcProj = (*_2neibors->_plnNorm) * new2src;
8878       newPos.ChangeCoord() += (*_2neibors->_plnNorm) * new2srcProj;
8879     }
8880     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8881     _pos.back() = newPos.XYZ();
8882   }
8883   else
8884   {
8885     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8886     gp_XY uv( Precision::Infinite(), 0 );
8887     helper.CheckNodeUV( F, tgtNode, uv, 1e-10, /*force=*/true );
8888     _pos.back().SetCoord( uv.X(), uv.Y(), 0 );
8889
8890     newPos = surface->Value( uv );
8891     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8892   }
8893
8894   // commented for IPAL0052478
8895   // if ( _curvature && lenDelta < 0 )
8896   // {
8897   //   gp_Pnt prevPos( _pos[ _pos.size()-2 ]);
8898   //   _len -= prevPos.Distance( oldPos );
8899   //   _len += prevPos.Distance( newPos );
8900   // }
8901   bool moved = distNewOld > dist01/50;
8902   //if ( moved )
8903   dumpMove( tgtNode ); // debug
8904
8905   return moved;
8906 }
8907
8908 //================================================================================
8909 /*!
8910  * \brief Perform 3D smooth of nodes inflated from FACE. No check of validity
8911  */
8912 //================================================================================
8913
8914 void _LayerEdge::SmoothWoCheck()
8915 {
8916   if ( Is( DIFFICULT ))
8917     return;
8918
8919   bool moved = Is( SMOOTHED );
8920   for ( size_t i = 0; i < _neibors.size()  &&  !moved; ++i )
8921     moved = _neibors[i]->Is( SMOOTHED );
8922   if ( !moved )
8923     return;
8924
8925   gp_XYZ newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
8926
8927   SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
8928   n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
8929   _pos.back() = newPos;
8930
8931   dumpMoveComm( n, SMESH_Comment("No check - ") << _funNames[ smooFunID() ]);
8932 }
8933
8934 //================================================================================
8935 /*!
8936  * \brief Checks validity of _neibors on EDGEs and VERTEXes
8937  */
8938 //================================================================================
8939
8940 int _LayerEdge::CheckNeiborsOnBoundary( vector< _LayerEdge* >* badNeibors, bool * needSmooth )
8941 {
8942   if ( ! Is( NEAR_BOUNDARY ))
8943     return 0;
8944
8945   int nbBad = 0;
8946   double vol;
8947   for ( size_t iN = 0; iN < _neibors.size(); ++iN )
8948   {
8949     _LayerEdge* eN = _neibors[iN];
8950     if ( eN->_nodes[0]->getshapeId() == _nodes[0]->getshapeId() )
8951       continue;
8952     if ( needSmooth )
8953       *needSmooth |= ( eN->Is( _LayerEdge::BLOCKED ) ||
8954                        eN->Is( _LayerEdge::NORMAL_UPDATED ) ||
8955                        eN->_pos.size() != _pos.size() );
8956
8957     SMESH_TNodeXYZ curPosN ( eN->_nodes.back() );
8958     SMESH_TNodeXYZ prevPosN( eN->_nodes[0] );
8959     for ( size_t i = 0; i < eN->_simplices.size(); ++i )
8960       if ( eN->_nodes.size() > 1 &&
8961            eN->_simplices[i].Includes( _nodes.back() ) &&
8962            !eN->_simplices[i].IsForward( &prevPosN, &curPosN, vol ))
8963       {
8964         ++nbBad;
8965         if ( badNeibors )
8966         {
8967           badNeibors->push_back( eN );
8968           debugMsg("Bad boundary simplex ( "
8969                    << " "<< eN->_nodes[0]->GetID()
8970                    << " "<< eN->_nodes.back()->GetID()
8971                    << " "<< eN->_simplices[i]._nPrev->GetID()
8972                    << " "<< eN->_simplices[i]._nNext->GetID() << " )" );
8973         }
8974         else
8975         {
8976           break;
8977         }
8978       }
8979   }
8980   return nbBad;
8981 }
8982
8983 //================================================================================
8984 /*!
8985  * \brief Perform 'smart' 3D smooth of nodes inflated from FACE
8986  *  \retval int - nb of bad simplices around this _LayerEdge
8987  */
8988 //================================================================================
8989
8990 int _LayerEdge::Smooth(const int step, bool findBest, vector< _LayerEdge* >& toSmooth )
8991 {
8992   if ( !Is( MOVED ) || Is( SMOOTHED ) || Is( BLOCKED ))
8993     return 0; // shape of simplices not changed
8994   if ( _simplices.size() < 2 )
8995     return 0; // _LayerEdge inflated along EDGE or FACE
8996
8997   if ( Is( DIFFICULT )) // || Is( ON_CONCAVE_FACE )
8998     findBest = true;
8999
9000   const gp_XYZ& curPos  = _pos.back();
9001   const gp_XYZ& prevPos = _pos[0]; //PrevPos();
9002
9003   // quality metrics (orientation) of tetras around _tgtNode
9004   int nbOkBefore = 0;
9005   double vol, minVolBefore = 1e100;
9006   for ( size_t i = 0; i < _simplices.size(); ++i )
9007   {
9008     nbOkBefore += _simplices[i].IsForward( &prevPos, &curPos, vol );
9009     minVolBefore = Min( minVolBefore, vol );
9010   }
9011   int nbBad = _simplices.size() - nbOkBefore;
9012
9013   bool bndNeedSmooth = false;
9014   if ( nbBad == 0 )
9015     nbBad = CheckNeiborsOnBoundary( 0, & bndNeedSmooth );
9016   if ( nbBad > 0 )
9017     Set( DISTORTED );
9018
9019   // evaluate min angle
9020   if ( nbBad == 0 && !findBest && !bndNeedSmooth )
9021   {
9022     size_t nbGoodAngles = _simplices.size();
9023     double angle;
9024     for ( size_t i = 0; i < _simplices.size(); ++i )
9025     {
9026       if ( !_simplices[i].IsMinAngleOK( curPos, angle ) && angle > _minAngle )
9027         --nbGoodAngles;
9028     }
9029     if ( nbGoodAngles == _simplices.size() )
9030     {
9031       Unset( MOVED );
9032       return 0;
9033     }
9034   }
9035   if ( Is( ON_CONCAVE_FACE ))
9036     findBest = true;
9037
9038   if ( step % 2 == 0 )
9039     findBest = false;
9040
9041   if ( Is( ON_CONCAVE_FACE ) && !findBest ) // alternate FUN_CENTROIDAL and FUN_LAPLACIAN
9042   {
9043     if ( _smooFunction == _funs[ FUN_LAPLACIAN ] )
9044       _smooFunction = _funs[ FUN_CENTROIDAL ];
9045     else
9046       _smooFunction = _funs[ FUN_LAPLACIAN ];
9047   }
9048
9049   // compute new position for the last _pos using different _funs
9050   gp_XYZ newPos;
9051   bool moved = false;
9052   for ( int iFun = -1; iFun < theNbSmooFuns; ++iFun )
9053   {
9054     if ( iFun < 0 )
9055       newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
9056     else if ( _funs[ iFun ] == _smooFunction )
9057       continue; // _smooFunction again
9058     else if ( step > 1 )
9059       newPos = (this->*_funs[ iFun ])(); // try other smoothing fun
9060     else
9061       break; // let "easy" functions improve elements around distorted ones
9062
9063     if ( _curvature )
9064     {
9065       double delta  = _curvature->lenDelta( _len );
9066       if ( delta > 0 )
9067         newPos += _normal * delta;
9068       else
9069       {
9070         double segLen = _normal * ( newPos - prevPos );
9071         if ( segLen + delta > 0 )
9072           newPos += _normal * delta;
9073       }
9074       // double segLenChange = _normal * ( curPos - newPos );
9075       // newPos += 0.5 * _normal * segLenChange;
9076     }
9077
9078     int nbOkAfter = 0;
9079     double minVolAfter = 1e100;
9080     for ( size_t i = 0; i < _simplices.size(); ++i )
9081     {
9082       nbOkAfter += _simplices[i].IsForward( &prevPos, &newPos, vol );
9083       minVolAfter = Min( minVolAfter, vol );
9084     }
9085     // get worse?
9086     if ( nbOkAfter < nbOkBefore )
9087       continue;
9088
9089     if (( findBest ) &&
9090         ( nbOkAfter == nbOkBefore ) &&
9091         ( minVolAfter <= minVolBefore ))
9092       continue;
9093
9094     nbBad        = _simplices.size() - nbOkAfter;
9095     minVolBefore = minVolAfter;
9096     nbOkBefore   = nbOkAfter;
9097     moved        = true;
9098
9099     SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
9100     n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
9101     _pos.back() = newPos;
9102
9103     dumpMoveComm( n, SMESH_Comment( _funNames[ iFun < 0 ? smooFunID() : iFun ] )
9104                   << (nbBad ? " --BAD" : ""));
9105
9106     if ( iFun > -1 )
9107     {
9108       continue; // look for a better function
9109     }
9110
9111     if ( !findBest )
9112       break;
9113
9114   } // loop on smoothing functions
9115
9116   if ( moved ) // notify _neibors
9117   {
9118     Set( SMOOTHED );
9119     for ( size_t i = 0; i < _neibors.size(); ++i )
9120       if ( !_neibors[i]->Is( MOVED ))
9121       {
9122         _neibors[i]->Set( MOVED );
9123         toSmooth.push_back( _neibors[i] );
9124       }
9125   }
9126
9127   return nbBad;
9128 }
9129
9130 //================================================================================
9131 /*!
9132  * \brief Perform 'smart' 3D smooth of nodes inflated from FACE
9133  *  \retval int - nb of bad simplices around this _LayerEdge
9134  */
9135 //================================================================================
9136
9137 int _LayerEdge::Smooth(const int step, const bool isConcaveFace, bool findBest )
9138 {
9139   if ( !_smooFunction )
9140     return 0; // _LayerEdge inflated along EDGE or FACE
9141   if ( Is( BLOCKED ))
9142     return 0; // not inflated
9143
9144   const gp_XYZ& curPos  = _pos.back();
9145   const gp_XYZ& prevPos = _pos[0]; //PrevCheckPos();
9146
9147   // quality metrics (orientation) of tetras around _tgtNode
9148   int nbOkBefore = 0;
9149   double vol, minVolBefore = 1e100;
9150   for ( size_t i = 0; i < _simplices.size(); ++i )
9151   {
9152     nbOkBefore += _simplices[i].IsForward( &prevPos, &curPos, vol );
9153     minVolBefore = Min( minVolBefore, vol );
9154   }
9155   int nbBad = _simplices.size() - nbOkBefore;
9156
9157   if ( isConcaveFace ) // alternate FUN_CENTROIDAL and FUN_LAPLACIAN
9158   {
9159     if      ( _smooFunction == _funs[ FUN_CENTROIDAL ] && step % 2 )
9160       _smooFunction = _funs[ FUN_LAPLACIAN ];
9161     else if ( _smooFunction == _funs[ FUN_LAPLACIAN ] && !( step % 2 ))
9162       _smooFunction = _funs[ FUN_CENTROIDAL ];
9163   }
9164
9165   // compute new position for the last _pos using different _funs
9166   gp_XYZ newPos;
9167   for ( int iFun = -1; iFun < theNbSmooFuns; ++iFun )
9168   {
9169     if ( iFun < 0 )
9170       newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
9171     else if ( _funs[ iFun ] == _smooFunction )
9172       continue; // _smooFunction again
9173     else if ( step > 1 )
9174       newPos = (this->*_funs[ iFun ])(); // try other smoothing fun
9175     else
9176       break; // let "easy" functions improve elements around distorted ones
9177
9178     if ( _curvature )
9179     {
9180       double delta  = _curvature->lenDelta( _len );
9181       if ( delta > 0 )
9182         newPos += _normal * delta;
9183       else
9184       {
9185         double segLen = _normal * ( newPos - prevPos );
9186         if ( segLen + delta > 0 )
9187           newPos += _normal * delta;
9188       }
9189       // double segLenChange = _normal * ( curPos - newPos );
9190       // newPos += 0.5 * _normal * segLenChange;
9191     }
9192
9193     int nbOkAfter = 0;
9194     double minVolAfter = 1e100;
9195     for ( size_t i = 0; i < _simplices.size(); ++i )
9196     {
9197       nbOkAfter += _simplices[i].IsForward( &prevPos, &newPos, vol );
9198       minVolAfter = Min( minVolAfter, vol );
9199     }
9200     // get worse?
9201     if ( nbOkAfter < nbOkBefore )
9202       continue;
9203     if (( isConcaveFace || findBest ) &&
9204         ( nbOkAfter == nbOkBefore ) &&
9205         ( minVolAfter <= minVolBefore )
9206         )
9207       continue;
9208
9209     nbBad        = _simplices.size() - nbOkAfter;
9210     minVolBefore = minVolAfter;
9211     nbOkBefore   = nbOkAfter;
9212
9213     SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
9214     n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
9215     _pos.back() = newPos;
9216
9217     dumpMoveComm( n, SMESH_Comment( _funNames[ iFun < 0 ? smooFunID() : iFun ] )
9218                   << ( nbBad ? "--BAD" : ""));
9219
9220     // commented for IPAL0052478
9221     // _len -= prevPos.Distance(SMESH_TNodeXYZ( n ));
9222     // _len += prevPos.Distance(newPos);
9223
9224     if ( iFun > -1 ) // findBest || the chosen _fun makes worse
9225     {
9226       //_smooFunction = _funs[ iFun ];
9227       // cout << "# " << _funNames[ iFun ] << "\t N:" << _nodes.back()->GetID()
9228       // << "\t nbBad: " << _simplices.size() - nbOkAfter
9229       // << " minVol: " << minVolAfter
9230       // << " " << newPos.X() << " " << newPos.Y() << " " << newPos.Z()
9231       // << endl;
9232       continue; // look for a better function
9233     }
9234
9235     if ( !findBest )
9236       break;
9237
9238   } // loop on smoothing functions
9239
9240   return nbBad;
9241 }
9242
9243 //================================================================================
9244 /*!
9245  * \brief Chooses a smoothing technique giving a position most close to an initial one.
9246  *        For a correct result, _simplices must contain nodes lying on geometry.
9247  */
9248 //================================================================================
9249
9250 void _LayerEdge::ChooseSmooFunction( const set< TGeomID >& concaveVertices,
9251                                      const TNode2Edge&     /*n2eMap*/)
9252 {
9253   if ( _smooFunction ) return;
9254
9255   // use smoothNefPolygon() near concaveVertices
9256   if ( !concaveVertices.empty() )
9257   {
9258     _smooFunction = _funs[ FUN_CENTROIDAL ];
9259
9260     Set( ON_CONCAVE_FACE );
9261
9262     for ( size_t i = 0; i < _simplices.size(); ++i )
9263     {
9264       if ( concaveVertices.count( _simplices[i]._nPrev->getshapeId() ))
9265       {
9266         _smooFunction = _funs[ FUN_NEFPOLY ];
9267
9268         // set FUN_CENTROIDAL to neighbor edges
9269         for ( i = 0; i < _neibors.size(); ++i )
9270         {
9271           if ( _neibors[i]->_nodes[0]->GetPosition()->GetDim() == 2 )
9272           {
9273             _neibors[i]->_smooFunction = _funs[ FUN_CENTROIDAL ];
9274           }
9275         }
9276         return;
9277       }
9278     }
9279
9280     // // this choice is done only if ( !concaveVertices.empty() ) for Grids/smesh/bugs_19/X1
9281     // // where the nodes are smoothed too far along a sphere thus creating
9282     // // inverted _simplices
9283     // double dist[theNbSmooFuns];
9284     // //double coef[theNbSmooFuns] = { 1., 1.2, 1.4, 1.4 };
9285     // double coef[theNbSmooFuns] = { 1., 1., 1., 1. };
9286
9287     // double minDist = Precision::Infinite();
9288     // gp_Pnt p = SMESH_TNodeXYZ( _nodes[0] );
9289     // for ( int i = 0; i < FUN_NEFPOLY; ++i )
9290     // {
9291     //   gp_Pnt newP = (this->*_funs[i])();
9292     //   dist[i] = p.SquareDistance( newP );
9293     //   if ( dist[i]*coef[i] < minDist )
9294     //   {
9295     //     _smooFunction = _funs[i];
9296     //     minDist = dist[i]*coef[i];
9297     //   }
9298     // }
9299   }
9300   else
9301   {
9302     _smooFunction = _funs[ FUN_LAPLACIAN ];
9303   }
9304   // int minDim = 3;
9305   // for ( size_t i = 0; i < _simplices.size(); ++i )
9306   //   minDim = Min( minDim, _simplices[i]._nPrev->GetPosition()->GetDim() );
9307   // if ( minDim == 0 )
9308   //   _smooFunction = _funs[ FUN_CENTROIDAL ];
9309   // else if ( minDim == 1 )
9310   //   _smooFunction = _funs[ FUN_CENTROIDAL ];
9311
9312
9313   // int iMin;
9314   // for ( int i = 0; i < FUN_NB; ++i )
9315   // {
9316   //   //cout << dist[i] << " ";
9317   //   if ( _smooFunction == _funs[i] ) {
9318   //     iMin = i;
9319   //     //debugMsg( fNames[i] );
9320   //     break;
9321   //   }
9322   // }
9323   // cout << _funNames[ iMin ] << "\t N:" << _nodes.back()->GetID() << endl;
9324 }
9325
9326 //================================================================================
9327 /*!
9328  * \brief Returns a name of _SmooFunction
9329  */
9330 //================================================================================
9331
9332 int _LayerEdge::smooFunID( _LayerEdge::PSmooFun fun) const
9333 {
9334   if ( !fun )
9335     fun = _smooFunction;
9336   for ( int i = 0; i < theNbSmooFuns; ++i )
9337     if ( fun == _funs[i] )
9338       return i;
9339
9340   return theNbSmooFuns;
9341 }
9342
9343 //================================================================================
9344 /*!
9345  * \brief Computes a new node position using Laplacian smoothing
9346  */
9347 //================================================================================
9348
9349 gp_XYZ _LayerEdge::smoothLaplacian()
9350 {
9351   gp_XYZ newPos (0,0,0);
9352   for ( size_t i = 0; i < _simplices.size(); ++i )
9353     newPos += SMESH_TNodeXYZ( _simplices[i]._nPrev );
9354   newPos /= _simplices.size();
9355
9356   return newPos;
9357 }
9358
9359 //================================================================================
9360 /*!
9361  * \brief Computes a new node position using angular-based smoothing
9362  */
9363 //================================================================================
9364
9365 gp_XYZ _LayerEdge::smoothAngular()
9366 {
9367   vector< gp_Vec > edgeDir;  edgeDir. reserve( _simplices.size() + 1 );
9368   vector< double > edgeSize; edgeSize.reserve( _simplices.size()     );
9369   vector< gp_XYZ > points;   points.  reserve( _simplices.size() + 1 );
9370
9371   gp_XYZ pPrev = SMESH_TNodeXYZ( _simplices.back()._nPrev );
9372   gp_XYZ pN( 0,0,0 );
9373   for ( size_t i = 0; i < _simplices.size(); ++i )
9374   {
9375     gp_XYZ p = SMESH_TNodeXYZ( _simplices[i]._nPrev );
9376     edgeDir.push_back( p - pPrev );
9377     edgeSize.push_back( edgeDir.back().Magnitude() );
9378     if ( edgeSize.back() < numeric_limits<double>::min() )
9379     {
9380       edgeDir.pop_back();
9381       edgeSize.pop_back();
9382     }
9383     else
9384     {
9385       edgeDir.back() /= edgeSize.back();
9386       points.push_back( p );
9387       pN += p;
9388     }
9389     pPrev = p;
9390   }
9391   edgeDir.push_back ( edgeDir[0] );
9392   edgeSize.push_back( edgeSize[0] );
9393   pN /= points.size();
9394
9395   gp_XYZ newPos(0,0,0);
9396   double sumSize = 0;
9397   for ( size_t i = 0; i < points.size(); ++i )
9398   {
9399     gp_Vec toN    = pN - points[i];
9400     double toNLen = toN.Magnitude();
9401     if ( toNLen < numeric_limits<double>::min() )
9402     {
9403       newPos += pN;
9404       continue;
9405     }
9406     gp_Vec bisec    = edgeDir[i] + edgeDir[i+1];
9407     double bisecLen = bisec.SquareMagnitude();
9408     if ( bisecLen < numeric_limits<double>::min() )
9409     {
9410       gp_Vec norm = edgeDir[i] ^ toN;
9411       bisec = norm ^ edgeDir[i];
9412       bisecLen = bisec.SquareMagnitude();
9413     }
9414     bisecLen = Sqrt( bisecLen );
9415     bisec /= bisecLen;
9416
9417 #if 1
9418     gp_XYZ pNew = ( points[i] + bisec.XYZ() * toNLen ) * bisecLen;
9419     sumSize += bisecLen;
9420 #else
9421     gp_XYZ pNew = ( points[i] + bisec.XYZ() * toNLen ) * ( edgeSize[i] + edgeSize[i+1] );
9422     sumSize += ( edgeSize[i] + edgeSize[i+1] );
9423 #endif
9424     newPos += pNew;
9425   }
9426   newPos /= sumSize;
9427
9428   // project newPos to an average plane
9429
9430   gp_XYZ norm(0,0,0); // plane normal
9431   points.push_back( points[0] );
9432   for ( size_t i = 1; i < points.size(); ++i )
9433   {
9434     gp_XYZ vec1 = points[ i-1 ] - pN;
9435     gp_XYZ vec2 = points[ i   ] - pN;
9436     gp_XYZ cross = vec1 ^ vec2;
9437     try {
9438       cross.Normalize();
9439       if ( cross * norm < numeric_limits<double>::min() )
9440         norm += cross.Reversed();
9441       else
9442         norm += cross;
9443     }
9444     catch (Standard_Failure&) { // if |cross| == 0.
9445     }
9446   }
9447   gp_XYZ vec = newPos - pN;
9448   double r   = ( norm * vec ) / norm.SquareModulus();  // param [0,1] on norm
9449   newPos     = newPos - r * norm;
9450
9451   return newPos;
9452 }
9453
9454 //================================================================================
9455 /*!
9456  * \brief Computes a new node position using weighted node positions
9457  */
9458 //================================================================================
9459
9460 gp_XYZ _LayerEdge::smoothLengthWeighted()
9461 {
9462   vector< double > edgeSize; edgeSize.reserve( _simplices.size() + 1);
9463   vector< gp_XYZ > points;   points.  reserve( _simplices.size() );
9464
9465   gp_XYZ pPrev = SMESH_TNodeXYZ( _simplices.back()._nPrev );
9466   for ( size_t i = 0; i < _simplices.size(); ++i )
9467   {
9468     gp_XYZ p = SMESH_TNodeXYZ( _simplices[i]._nPrev );
9469     edgeSize.push_back( ( p - pPrev ).Modulus() );
9470     if ( edgeSize.back() < numeric_limits<double>::min() )
9471     {
9472       edgeSize.pop_back();
9473     }
9474     else
9475     {
9476       points.push_back( p );
9477     }
9478     pPrev = p;
9479   }
9480   edgeSize.push_back( edgeSize[0] );
9481
9482   gp_XYZ newPos(0,0,0);
9483   double sumSize = 0;
9484   for ( size_t i = 0; i < points.size(); ++i )
9485   {
9486     newPos += points[i] * ( edgeSize[i] + edgeSize[i+1] );
9487     sumSize += edgeSize[i] + edgeSize[i+1];
9488   }
9489   newPos /= sumSize;
9490   return newPos;
9491 }
9492
9493 //================================================================================
9494 /*!
9495  * \brief Computes a new node position using angular-based smoothing
9496  */
9497 //================================================================================
9498
9499 gp_XYZ _LayerEdge::smoothCentroidal()
9500 {
9501   gp_XYZ newPos(0,0,0);
9502   gp_XYZ pN = SMESH_TNodeXYZ( _nodes.back() );
9503   double sumSize = 0;
9504   for ( size_t i = 0; i < _simplices.size(); ++i )
9505   {
9506     gp_XYZ p1 = SMESH_TNodeXYZ( _simplices[i]._nPrev );
9507     gp_XYZ p2 = SMESH_TNodeXYZ( _simplices[i]._nNext );
9508     gp_XYZ gc = ( pN + p1 + p2 ) / 3.;
9509     double size = (( p1 - pN ) ^ ( p2 - pN )).Modulus();
9510
9511     sumSize += size;
9512     newPos += gc * size;
9513   }
9514   newPos /= sumSize;
9515
9516   return newPos;
9517 }
9518
9519 //================================================================================
9520 /*!
9521  * \brief Computes a new node position located inside a Nef polygon
9522  */
9523 //================================================================================
9524
9525 gp_XYZ _LayerEdge::smoothNefPolygon()
9526 #ifdef OLD_NEF_POLYGON
9527 {
9528   gp_XYZ newPos(0,0,0);
9529
9530   // get a plane to search a solution on
9531
9532   vector< gp_XYZ > vecs( _simplices.size() + 1 );
9533   size_t i;
9534   const double tol = numeric_limits<double>::min();
9535   gp_XYZ center(0,0,0);
9536   for ( i = 0; i < _simplices.size(); ++i )
9537   {
9538     vecs[i] = ( SMESH_TNodeXYZ( _simplices[i]._nNext ) -
9539                 SMESH_TNodeXYZ( _simplices[i]._nPrev ));
9540     center += SMESH_TNodeXYZ( _simplices[i]._nPrev );
9541   }
9542   vecs.back() = vecs[0];
9543   center /= _simplices.size();
9544
9545   gp_XYZ zAxis(0,0,0);
9546   for ( i = 0; i < _simplices.size(); ++i )
9547     zAxis += vecs[i] ^ vecs[i+1];
9548
9549   gp_XYZ yAxis;
9550   for ( i = 0; i < _simplices.size(); ++i )
9551   {
9552     yAxis = vecs[i];
9553     if ( yAxis.SquareModulus() > tol )
9554       break;
9555   }
9556   gp_XYZ xAxis = yAxis ^ zAxis;
9557   // SMESH_TNodeXYZ p0( _simplices[0]._nPrev );
9558   // const double tol = 1e-6 * ( p0.Distance( _simplices[1]._nPrev ) +
9559   //                             p0.Distance( _simplices[2]._nPrev ));
9560   // gp_XYZ center = smoothLaplacian();
9561   // gp_XYZ xAxis, yAxis, zAxis;
9562   // for ( i = 0; i < _simplices.size(); ++i )
9563   // {
9564   //   xAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9565   //   if ( xAxis.SquareModulus() > tol*tol )
9566   //     break;
9567   // }
9568   // for ( i = 1; i < _simplices.size(); ++i )
9569   // {
9570   //   yAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9571   //   zAxis = xAxis ^ yAxis;
9572   //   if ( zAxis.SquareModulus() > tol*tol )
9573   //     break;
9574   // }
9575   // if ( i == _simplices.size() ) return newPos;
9576
9577   yAxis = zAxis ^ xAxis;
9578   xAxis /= xAxis.Modulus();
9579   yAxis /= yAxis.Modulus();
9580
9581   // get half-planes of _simplices
9582
9583   vector< _halfPlane > halfPlns( _simplices.size() );
9584   int nbHP = 0;
9585   for ( size_t i = 0; i < _simplices.size(); ++i )
9586   {
9587     gp_XYZ OP1 = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9588     gp_XYZ OP2 = SMESH_TNodeXYZ( _simplices[i]._nNext ) - center;
9589     gp_XY  p1( OP1 * xAxis, OP1 * yAxis );
9590     gp_XY  p2( OP2 * xAxis, OP2 * yAxis );
9591     gp_XY  vec12 = p2 - p1;
9592     double dist12 = vec12.Modulus();
9593     if ( dist12 < tol )
9594       continue;
9595     vec12 /= dist12;
9596     halfPlns[ nbHP ]._pos = p1;
9597     halfPlns[ nbHP ]._dir = vec12;
9598     halfPlns[ nbHP ]._inNorm.SetCoord( -vec12.Y(), vec12.X() );
9599     ++nbHP;
9600   }
9601
9602   // intersect boundaries of half-planes, define state of intersection points
9603   // in relation to all half-planes and calculate internal point of a 2D polygon
9604
9605   double sumLen = 0;
9606   gp_XY newPos2D (0,0);
9607
9608   enum { UNDEF = -1, NOT_OUT, IS_OUT, NO_INT };
9609   typedef std::pair< gp_XY, int > TIntPntState; // coord and isOut state
9610   TIntPntState undefIPS( gp_XY(1e100,1e100), UNDEF );
9611
9612   vector< vector< TIntPntState > > allIntPnts( nbHP );
9613   for ( int iHP1 = 0; iHP1 < nbHP; ++iHP1 )
9614   {
9615     vector< TIntPntState > & intPnts1 = allIntPnts[ iHP1 ];
9616     if ( intPnts1.empty() ) intPnts1.resize( nbHP, undefIPS );
9617
9618     int iPrev = SMESH_MesherHelper::WrapIndex( iHP1 - 1, nbHP );
9619     int iNext = SMESH_MesherHelper::WrapIndex( iHP1 + 1, nbHP );
9620
9621     int nbNotOut = 0;
9622     const gp_XY* segEnds[2] = { 0, 0 }; // NOT_OUT points
9623
9624     for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9625     {
9626       if ( iHP1 == iHP2 ) continue;
9627
9628       TIntPntState & ips1 = intPnts1[ iHP2 ];
9629       if ( ips1.second == UNDEF )
9630       {
9631         // find an intersection point of boundaries of iHP1 and iHP2
9632
9633         if ( iHP2 == iPrev ) // intersection with neighbors is known
9634           ips1.first = halfPlns[ iHP1 ]._pos;
9635         else if ( iHP2 == iNext )
9636           ips1.first = halfPlns[ iHP2 ]._pos;
9637         else if ( !halfPlns[ iHP1 ].FindIntersection( halfPlns[ iHP2 ], ips1.first ))
9638           ips1.second = NO_INT;
9639
9640         // classify the found intersection point
9641         if ( ips1.second != NO_INT )
9642         {
9643           ips1.second = NOT_OUT;
9644           for ( int i = 0; i < nbHP && ips1.second == NOT_OUT; ++i )
9645             if ( i != iHP1 && i != iHP2 &&
9646                  halfPlns[ i ].IsOut( ips1.first, tol ))
9647               ips1.second = IS_OUT;
9648         }
9649         vector< TIntPntState > & intPnts2 = allIntPnts[ iHP2 ];
9650         if ( intPnts2.empty() ) intPnts2.resize( nbHP, undefIPS );
9651         TIntPntState & ips2 = intPnts2[ iHP1 ];
9652         ips2 = ips1;
9653       }
9654       if ( ips1.second == NOT_OUT )
9655       {
9656         ++nbNotOut;
9657         segEnds[ bool(segEnds[0]) ] = & ips1.first;
9658       }
9659     }
9660
9661     // find a NOT_OUT segment of boundary which is located between
9662     // two NOT_OUT int points
9663
9664     if ( nbNotOut < 2 )
9665       continue; // no such a segment
9666
9667     if ( nbNotOut > 2 )
9668     {
9669       // sort points along the boundary
9670       map< double, TIntPntState* > ipsByParam;
9671       for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9672       {
9673         TIntPntState & ips1 = intPnts1[ iHP2 ];
9674         if ( ips1.second != NO_INT )
9675         {
9676           gp_XY     op = ips1.first - halfPlns[ iHP1 ]._pos;
9677           double param = op * halfPlns[ iHP1 ]._dir;
9678           ipsByParam.insert( make_pair( param, & ips1 ));
9679         }
9680       }
9681       // look for two neighboring NOT_OUT points
9682       nbNotOut = 0;
9683       map< double, TIntPntState* >::iterator u2ips = ipsByParam.begin();
9684       for ( ; u2ips != ipsByParam.end(); ++u2ips )
9685       {
9686         TIntPntState & ips1 = *(u2ips->second);
9687         if ( ips1.second == NOT_OUT )
9688           segEnds[ bool( nbNotOut++ ) ] = & ips1.first;
9689         else if ( nbNotOut >= 2 )
9690           break;
9691         else
9692           nbNotOut = 0;
9693       }
9694     }
9695
9696     if ( nbNotOut >= 2 )
9697     {
9698       double len = ( *segEnds[0] - *segEnds[1] ).Modulus();
9699       sumLen += len;
9700
9701       newPos2D += 0.5 * len * ( *segEnds[0] + *segEnds[1] );
9702     }
9703   }
9704
9705   if ( sumLen > 0 )
9706   {
9707     newPos2D /= sumLen;
9708     newPos = center + xAxis * newPos2D.X() + yAxis * newPos2D.Y();
9709   }
9710   else
9711   {
9712     newPos = center;
9713   }
9714
9715   return newPos;
9716 }
9717 #else // OLD_NEF_POLYGON
9718 { ////////////////////////////////// NEW
9719   gp_XYZ newPos(0,0,0);
9720
9721   // get a plane to search a solution on
9722
9723   size_t i;
9724   gp_XYZ center(0,0,0);
9725   for ( i = 0; i < _simplices.size(); ++i )
9726     center += SMESH_TNodeXYZ( _simplices[i]._nPrev );
9727   center /= _simplices.size();
9728
9729   vector< gp_XYZ > vecs( _simplices.size() + 1 );
9730   for ( i = 0; i < _simplices.size(); ++i )
9731     vecs[i] = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9732   vecs.back() = vecs[0];
9733
9734   const double tol = numeric_limits<double>::min();
9735   gp_XYZ zAxis(0,0,0);
9736   for ( i = 0; i < _simplices.size(); ++i )
9737   {
9738     gp_XYZ cross = vecs[i] ^ vecs[i+1];
9739     try {
9740       cross.Normalize();
9741       if ( cross * zAxis < tol )
9742         zAxis += cross.Reversed();
9743       else
9744         zAxis += cross;
9745     }
9746     catch (Standard_Failure) { // if |cross| == 0.
9747     }
9748   }
9749
9750   gp_XYZ yAxis;
9751   for ( i = 0; i < _simplices.size(); ++i )
9752   {
9753     yAxis = vecs[i];
9754     if ( yAxis.SquareModulus() > tol )
9755       break;
9756   }
9757   gp_XYZ xAxis = yAxis ^ zAxis;
9758   // SMESH_TNodeXYZ p0( _simplices[0]._nPrev );
9759   // const double tol = 1e-6 * ( p0.Distance( _simplices[1]._nPrev ) +
9760   //                             p0.Distance( _simplices[2]._nPrev ));
9761   // gp_XYZ center = smoothLaplacian();
9762   // gp_XYZ xAxis, yAxis, zAxis;
9763   // for ( i = 0; i < _simplices.size(); ++i )
9764   // {
9765   //   xAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9766   //   if ( xAxis.SquareModulus() > tol*tol )
9767   //     break;
9768   // }
9769   // for ( i = 1; i < _simplices.size(); ++i )
9770   // {
9771   //   yAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9772   //   zAxis = xAxis ^ yAxis;
9773   //   if ( zAxis.SquareModulus() > tol*tol )
9774   //     break;
9775   // }
9776   // if ( i == _simplices.size() ) return newPos;
9777
9778   yAxis = zAxis ^ xAxis;
9779   xAxis /= xAxis.Modulus();
9780   yAxis /= yAxis.Modulus();
9781
9782   // get half-planes of _simplices
9783
9784   vector< _halfPlane > halfPlns( _simplices.size() );
9785   int nbHP = 0;
9786   for ( size_t i = 0; i < _simplices.size(); ++i )
9787   {
9788     const gp_XYZ& OP1 = vecs[ i   ];
9789     const gp_XYZ& OP2 = vecs[ i+1 ];
9790     gp_XY  p1( OP1 * xAxis, OP1 * yAxis );
9791     gp_XY  p2( OP2 * xAxis, OP2 * yAxis );
9792     gp_XY  vec12 = p2 - p1;
9793     double dist12 = vec12.Modulus();
9794     if ( dist12 < tol )
9795       continue;
9796     vec12 /= dist12;
9797     halfPlns[ nbHP ]._pos = p1;
9798     halfPlns[ nbHP ]._dir = vec12;
9799     halfPlns[ nbHP ]._inNorm.SetCoord( -vec12.Y(), vec12.X() );
9800     ++nbHP;
9801   }
9802
9803   // intersect boundaries of half-planes, define state of intersection points
9804   // in relation to all half-planes and calculate internal point of a 2D polygon
9805
9806   double sumLen = 0;
9807   gp_XY newPos2D (0,0);
9808
9809   enum { UNDEF = -1, NOT_OUT, IS_OUT, NO_INT };
9810   typedef std::pair< gp_XY, int > TIntPntState; // coord and isOut state
9811   TIntPntState undefIPS( gp_XY(1e100,1e100), UNDEF );
9812
9813   vector< vector< TIntPntState > > allIntPnts( nbHP );
9814   for ( int iHP1 = 0; iHP1 < nbHP; ++iHP1 )
9815   {
9816     vector< TIntPntState > & intPnts1 = allIntPnts[ iHP1 ];
9817     if ( intPnts1.empty() ) intPnts1.resize( nbHP, undefIPS );
9818
9819     int iPrev = SMESH_MesherHelper::WrapIndex( iHP1 - 1, nbHP );
9820     int iNext = SMESH_MesherHelper::WrapIndex( iHP1 + 1, nbHP );
9821
9822     int nbNotOut = 0;
9823     const gp_XY* segEnds[2] = { 0, 0 }; // NOT_OUT points
9824
9825     for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9826     {
9827       if ( iHP1 == iHP2 ) continue;
9828
9829       TIntPntState & ips1 = intPnts1[ iHP2 ];
9830       if ( ips1.second == UNDEF )
9831       {
9832         // find an intersection point of boundaries of iHP1 and iHP2
9833
9834         if ( iHP2 == iPrev ) // intersection with neighbors is known
9835           ips1.first = halfPlns[ iHP1 ]._pos;
9836         else if ( iHP2 == iNext )
9837           ips1.first = halfPlns[ iHP2 ]._pos;
9838         else if ( !halfPlns[ iHP1 ].FindIntersection( halfPlns[ iHP2 ], ips1.first ))
9839           ips1.second = NO_INT;
9840
9841         // classify the found intersection point
9842         if ( ips1.second != NO_INT )
9843         {
9844           ips1.second = NOT_OUT;
9845           for ( int i = 0; i < nbHP && ips1.second == NOT_OUT; ++i )
9846             if ( i != iHP1 && i != iHP2 &&
9847                  halfPlns[ i ].IsOut( ips1.first, tol ))
9848               ips1.second = IS_OUT;
9849         }
9850         vector< TIntPntState > & intPnts2 = allIntPnts[ iHP2 ];
9851         if ( intPnts2.empty() ) intPnts2.resize( nbHP, undefIPS );
9852         TIntPntState & ips2 = intPnts2[ iHP1 ];
9853         ips2 = ips1;
9854       }
9855       if ( ips1.second == NOT_OUT )
9856       {
9857         ++nbNotOut;
9858         segEnds[ bool(segEnds[0]) ] = & ips1.first;
9859       }
9860     }
9861
9862     // find a NOT_OUT segment of boundary which is located between
9863     // two NOT_OUT int points
9864
9865     if ( nbNotOut < 2 )
9866       continue; // no such a segment
9867
9868     if ( nbNotOut > 2 )
9869     {
9870       // sort points along the boundary
9871       map< double, TIntPntState* > ipsByParam;
9872       for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9873       {
9874         TIntPntState & ips1 = intPnts1[ iHP2 ];
9875         if ( ips1.second != NO_INT )
9876         {
9877           gp_XY     op = ips1.first - halfPlns[ iHP1 ]._pos;
9878           double param = op * halfPlns[ iHP1 ]._dir;
9879           ipsByParam.insert( make_pair( param, & ips1 ));
9880         }
9881       }
9882       // look for two neighboring NOT_OUT points
9883       nbNotOut = 0;
9884       map< double, TIntPntState* >::iterator u2ips = ipsByParam.begin();
9885       for ( ; u2ips != ipsByParam.end(); ++u2ips )
9886       {
9887         TIntPntState & ips1 = *(u2ips->second);
9888         if ( ips1.second == NOT_OUT )
9889           segEnds[ bool( nbNotOut++ ) ] = & ips1.first;
9890         else if ( nbNotOut >= 2 )
9891           break;
9892         else
9893           nbNotOut = 0;
9894       }
9895     }
9896
9897     if ( nbNotOut >= 2 )
9898     {
9899       double len = ( *segEnds[0] - *segEnds[1] ).Modulus();
9900       sumLen += len;
9901
9902       newPos2D += 0.5 * len * ( *segEnds[0] + *segEnds[1] );
9903     }
9904   }
9905
9906   if ( sumLen > 0 )
9907   {
9908     newPos2D /= sumLen;
9909     newPos = center + xAxis * newPos2D.X() + yAxis * newPos2D.Y();
9910   }
9911   else
9912   {
9913     newPos = center;
9914   }
9915
9916   return newPos;
9917 }
9918 #endif // OLD_NEF_POLYGON
9919
9920 //================================================================================
9921 /*!
9922  * \brief Add a new segment to _LayerEdge during inflation
9923  */
9924 //================================================================================
9925
9926 void _LayerEdge::SetNewLength( double len, _EdgesOnShape& eos, SMESH_MesherHelper& helper )
9927 {
9928   if ( Is( BLOCKED ))
9929     return;
9930
9931   if ( len > _maxLen )
9932   {
9933     len = _maxLen;
9934     Block( eos.GetData() );
9935   }
9936   const double lenDelta = len - _len;
9937   // if ( lenDelta < 0 )
9938   //   return;
9939   if ( lenDelta < len * 1e-3  )
9940   {
9941     Block( eos.GetData() );
9942     return;
9943   }
9944
9945   SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() );
9946   gp_XYZ oldXYZ = SMESH_TNodeXYZ( n );
9947   gp_XYZ newXYZ;
9948   if ( eos._hyp.IsOffsetMethod() )
9949   {
9950     newXYZ = oldXYZ;
9951     gp_Vec faceNorm;
9952     SMDS_ElemIteratorPtr faceIt = _nodes[0]->GetInverseElementIterator( SMDSAbs_Face );
9953     while ( faceIt->more() )
9954     {
9955       const SMDS_MeshElement* face = faceIt->next();
9956       if ( !eos.GetNormal( face, faceNorm ))
9957         continue;
9958
9959       // translate plane of a face
9960       gp_XYZ baryCenter = oldXYZ + faceNorm.XYZ() * lenDelta;
9961
9962       // find point of intersection of the face plane located at baryCenter
9963       // and _normal located at newXYZ
9964       double d   = -( faceNorm.XYZ() * baryCenter ); // d of plane equation ax+by+cz+d=0
9965       double dot =  ( faceNorm.XYZ() * _normal );
9966       if ( dot < std::numeric_limits<double>::min() )
9967         dot = lenDelta * 1e-3;
9968       double step = -( faceNorm.XYZ() * newXYZ + d ) / dot;
9969       newXYZ += step * _normal;
9970     }
9971     _lenFactor = _normal * ( newXYZ - oldXYZ ) / lenDelta; // _lenFactor is used in InvalidateStep()
9972   }
9973   else
9974   {
9975     newXYZ = oldXYZ + _normal * lenDelta * _lenFactor;
9976   }
9977
9978   n->setXYZ( newXYZ.X(), newXYZ.Y(), newXYZ.Z() );
9979   _pos.push_back( newXYZ );
9980
9981   if ( !eos._sWOL.IsNull() )
9982     if ( !UpdatePositionOnSWOL( n, 2*lenDelta, eos, helper ))
9983     {
9984       n->setXYZ( oldXYZ.X(), oldXYZ.Y(), oldXYZ.Z() );
9985       _pos.pop_back();
9986       Block( eos.GetData() );
9987       return;
9988     }
9989
9990   _len = len;
9991
9992   // notify _neibors
9993   if ( eos.ShapeType() != TopAbs_FACE )
9994   {
9995     for ( size_t i = 0; i < _neibors.size(); ++i )
9996       //if (  _len > _neibors[i]->GetSmooLen() )
9997       _neibors[i]->Set( MOVED );
9998
9999     Set( MOVED );
10000   }
10001   dumpMove( n ); //debug
10002 }
10003
10004
10005 //================================================================================
10006 /*!
10007  * \brief Update last position on SWOL by projecting node on SWOL
10008 */
10009 //================================================================================
10010
10011 bool _LayerEdge::UpdatePositionOnSWOL( SMDS_MeshNode*      n,
10012                                        double              tol,
10013                                        _EdgesOnShape&      eos,
10014                                        SMESH_MesherHelper& helper )
10015 {
10016   double distXYZ[4];
10017   bool uvOK = false;
10018   if ( eos.SWOLType() == TopAbs_EDGE )
10019   {
10020     double u = Precision::Infinite(); // to force projection w/o distance check
10021     uvOK = helper.CheckNodeU( TopoDS::Edge( eos._sWOL ), n, u, tol, /*force=*/true, distXYZ );
10022     _pos.back().SetCoord( u, 0, 0 );
10023     if ( _nodes.size() > 1 && uvOK )
10024     {
10025       SMDS_EdgePositionPtr pos = n->GetPosition();
10026       pos->SetUParameter( u );
10027     }
10028   }
10029   else //  TopAbs_FACE
10030   {
10031     gp_XY uv( Precision::Infinite(), 0 );
10032     uvOK = helper.CheckNodeUV( TopoDS::Face( eos._sWOL ), n, uv, tol, /*force=*/true, distXYZ );
10033     _pos.back().SetCoord( uv.X(), uv.Y(), 0 );
10034     if ( _nodes.size() > 1 && uvOK )
10035     {
10036       SMDS_FacePositionPtr pos = n->GetPosition();
10037       pos->SetUParameter( uv.X() );
10038       pos->SetVParameter( uv.Y() );
10039     }
10040   }
10041   if ( uvOK )
10042   {
10043     n->setXYZ( distXYZ[1], distXYZ[2], distXYZ[3]);
10044   }
10045   return uvOK;
10046 }
10047
10048 //================================================================================
10049 /*!
10050  * \brief Set BLOCKED flag and propagate limited _maxLen to _neibors
10051  */
10052 //================================================================================
10053
10054 void _LayerEdge::Block( _SolidData& data )
10055 {
10056   //if ( Is( BLOCKED )) return;
10057   Set( BLOCKED );
10058
10059   SMESH_Comment msg( "#BLOCK shape=");
10060   msg << data.GetShapeEdges( this )->_shapeID
10061       << ", nodes " << _nodes[0]->GetID() << ", " << _nodes.back()->GetID();
10062   dumpCmd( msg + " -- BEGIN");
10063
10064   SetMaxLen( _len );
10065   std::queue<_LayerEdge*> queue;
10066   queue.push( this );
10067
10068   gp_Pnt pSrc, pTgt, pSrcN, pTgtN;
10069   while ( !queue.empty() )
10070   {
10071     _LayerEdge* edge = queue.front(); queue.pop();
10072     pSrc = SMESH_TNodeXYZ( edge->_nodes[0] );
10073     pTgt = SMESH_TNodeXYZ( edge->_nodes.back() );
10074     for ( size_t iN = 0; iN < edge->_neibors.size(); ++iN )
10075     {
10076       _LayerEdge* neibor = edge->_neibors[iN];
10077       if ( neibor->_maxLen < edge->_maxLen * 1.01 )
10078         continue;
10079       pSrcN = SMESH_TNodeXYZ( neibor->_nodes[0] );
10080       pTgtN = SMESH_TNodeXYZ( neibor->_nodes.back() );
10081       double minDist = pSrc.SquareDistance( pSrcN );
10082       minDist   = Min( pTgt.SquareDistance( pTgtN ), minDist );
10083       minDist   = Min( pSrc.SquareDistance( pTgtN ), minDist );
10084       minDist   = Min( pTgt.SquareDistance( pSrcN ), minDist );
10085       double newMaxLen = edge->_maxLen + 0.5 * Sqrt( minDist );
10086       //if ( edge->_nodes[0]->getshapeId() == neibor->_nodes[0]->getshapeId() ) viscous_layers_00/A3
10087       {
10088         //newMaxLen *= edge->_lenFactor / neibor->_lenFactor;
10089         // newMaxLen *= Min( edge->_lenFactor / neibor->_lenFactor,
10090         //                   neibor->_lenFactor / edge->_lenFactor );
10091       }
10092       if ( neibor->_maxLen > newMaxLen )
10093       {
10094         neibor->SetMaxLen( newMaxLen );
10095         if ( neibor->_maxLen < neibor->_len )
10096         {
10097           _EdgesOnShape* eos = data.GetShapeEdges( neibor );
10098           int       lastStep = neibor->Is( BLOCKED ) ? 1 : 0;
10099           while ( neibor->_len > neibor->_maxLen &&
10100                   neibor->NbSteps() > lastStep )
10101             neibor->InvalidateStep( neibor->NbSteps(), *eos, /*restoreLength=*/true );
10102           neibor->SetNewLength( neibor->_maxLen, *eos, data.GetHelper() );
10103           //neibor->Block( data );
10104         }
10105         queue.push( neibor );
10106       }
10107     }
10108   }
10109   dumpCmd( msg + " -- END");
10110 }
10111
10112 //================================================================================
10113 /*!
10114  * \brief Remove last inflation step
10115  */
10116 //================================================================================
10117
10118 void _LayerEdge::InvalidateStep( size_t curStep, const _EdgesOnShape& eos, bool restoreLength )
10119 {
10120   if ( _pos.size() > curStep && _nodes.size() > 1 )
10121   {
10122     _pos.resize( curStep );
10123
10124     gp_Pnt      nXYZ = _pos.back();
10125     SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() );
10126     SMESH_TNodeXYZ curXYZ( n );
10127     if ( !eos._sWOL.IsNull() )
10128     {
10129       TopLoc_Location loc;
10130       if ( eos.SWOLType() == TopAbs_EDGE )
10131       {
10132         SMDS_EdgePositionPtr pos = n->GetPosition();
10133         pos->SetUParameter( nXYZ.X() );
10134         double f,l;
10135         Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( eos._sWOL ), loc, f,l);
10136         nXYZ = curve->Value( nXYZ.X() ).Transformed( loc );
10137       }
10138       else
10139       {
10140         SMDS_FacePositionPtr pos = n->GetPosition();
10141         pos->SetUParameter( nXYZ.X() );
10142         pos->SetVParameter( nXYZ.Y() );
10143         Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face(eos._sWOL), loc );
10144         nXYZ = surface->Value( nXYZ.X(), nXYZ.Y() ).Transformed( loc );
10145       }
10146     }
10147     n->setXYZ( nXYZ.X(), nXYZ.Y(), nXYZ.Z() );
10148     dumpMove( n );
10149
10150     if ( restoreLength )
10151     {
10152       if ( NbSteps() == 0 )
10153         _len = 0.;
10154       else if ( IsOnFace() && Is( MOVED ))
10155         _len = ( nXYZ.XYZ() - SMESH_NodeXYZ( _nodes[0] )) * _normal;
10156       else
10157         _len -= ( nXYZ.XYZ() - curXYZ ).Modulus() / _lenFactor;
10158     }
10159   }
10160   return;
10161 }
10162
10163 //================================================================================
10164 /*!
10165  * \brief Return index of a _pos distant from _normal
10166  */
10167 //================================================================================
10168
10169 int _LayerEdge::GetSmoothedPos( const double tol )
10170 {
10171   int iSmoothed = 0;
10172   for ( size_t i = 1; i < _pos.size() && !iSmoothed; ++i )
10173   {
10174     double normDist = ( _pos[i] - _pos[0] ).Crossed( _normal ).SquareModulus();
10175     if ( normDist > tol * tol )
10176       iSmoothed = i;
10177   }
10178   return iSmoothed;
10179 }
10180
10181 //================================================================================
10182 /*!
10183  * \brief Smooth a path formed by _pos of a _LayerEdge smoothed on FACE
10184  */
10185 //================================================================================
10186
10187 void _LayerEdge::SmoothPos( const vector< double >& segLen, const double tol )
10188 {
10189   if ( /*Is( NORMAL_UPDATED ) ||*/ _pos.size() <= 2 )
10190     return;
10191
10192   // find the 1st smoothed _pos
10193   int iSmoothed = GetSmoothedPos( tol );
10194   if ( !iSmoothed ) return;
10195
10196   gp_XYZ normal = _normal;
10197   if ( Is( NORMAL_UPDATED ))
10198   {
10199     double minDot = 1;
10200     for ( size_t i = 0; i < _neibors.size(); ++i )
10201     {
10202       if ( _neibors[i]->IsOnFace() )
10203       {
10204         double dot = _normal * _neibors[i]->_normal;
10205         if ( dot < minDot )
10206         {
10207           normal = _neibors[i]->_normal;
10208           minDot = dot;
10209         }
10210       }
10211     }
10212     if ( minDot == 1. )
10213       for ( size_t i = 1; i < _pos.size(); ++i )
10214       {
10215         normal = _pos[i] - _pos[0];
10216         double size = normal.Modulus();
10217         if ( size > RealSmall() )
10218         {
10219           normal /= size;
10220           break;
10221         }
10222       }
10223   }
10224   const double r = 0.2;
10225   for ( int iter = 0; iter < 50; ++iter )
10226   {
10227     double minDot = 1;
10228     for ( size_t i = Max( 1, iSmoothed-1-iter ); i < _pos.size()-1; ++i )
10229     {
10230       gp_XYZ midPos = 0.5 * ( _pos[i-1] + _pos[i+1] );
10231       gp_XYZ newPos = ( 1-r ) * midPos + r * _pos[i];
10232       _pos[i] = newPos;
10233       double midLen = 0.5 * ( segLen[i-1] + segLen[i+1] );
10234       double newLen = ( 1-r ) * midLen + r * segLen[i];
10235       const_cast< double& >( segLen[i] ) = newLen;
10236       // check angle between normal and (_pos[i+1], _pos[i] )
10237       gp_XYZ posDir = _pos[i+1] - _pos[i];
10238       double size   = posDir.SquareModulus();
10239       if ( size > RealSmall() )
10240         minDot = Min( minDot, ( normal * posDir ) * ( normal * posDir ) / size );
10241     }
10242     if ( minDot > 0.5 * 0.5 )
10243       break;
10244   }
10245   return;
10246 }
10247
10248 //================================================================================
10249 /*!
10250  * \brief Print flags
10251  */
10252 //================================================================================
10253
10254 std::string _LayerEdge::DumpFlags() const
10255 {
10256   SMESH_Comment dump;
10257   for ( int flag = 1; flag < 0x1000000; flag *= 2 )
10258     if ( _flags & flag )
10259     {
10260       EFlags f = (EFlags) flag;
10261       switch ( f ) {
10262       case TO_SMOOTH:       dump << "TO_SMOOTH";       break;
10263       case MOVED:           dump << "MOVED";           break;
10264       case SMOOTHED:        dump << "SMOOTHED";        break;
10265       case DIFFICULT:       dump << "DIFFICULT";       break;
10266       case ON_CONCAVE_FACE: dump << "ON_CONCAVE_FACE"; break;
10267       case BLOCKED:         dump << "BLOCKED";         break;
10268       case INTERSECTED:     dump << "INTERSECTED";     break;
10269       case NORMAL_UPDATED:  dump << "NORMAL_UPDATED";  break;
10270       case UPD_NORMAL_CONV: dump << "UPD_NORMAL_CONV"; break;
10271       case MARKED:          dump << "MARKED";          break;
10272       case MULTI_NORMAL:    dump << "MULTI_NORMAL";    break;
10273       case NEAR_BOUNDARY:   dump << "NEAR_BOUNDARY";   break;
10274       case SMOOTHED_C1:     dump << "SMOOTHED_C1";     break;
10275       case DISTORTED:       dump << "DISTORTED";       break;
10276       case RISKY_SWOL:      dump << "RISKY_SWOL";      break;
10277       case SHRUNK:          dump << "SHRUNK";          break;
10278       case UNUSED_FLAG:     dump << "UNUSED_FLAG";     break;
10279       }
10280       dump << " ";
10281     }
10282   cout << dump << endl;
10283   return dump;
10284 }
10285
10286
10287 //================================================================================
10288 /*!
10289  * \brief Create layers of prisms
10290  */
10291 //================================================================================
10292
10293 bool _ViscousBuilder::refine(_SolidData& data)
10294 {
10295   SMESH_MesherHelper& helper = data.GetHelper();
10296   helper.SetElementsOnShape(false);
10297
10298   Handle(Geom_Curve) curve;
10299   Handle(ShapeAnalysis_Surface) surface;
10300   TopoDS_Edge geomEdge;
10301   TopoDS_Face geomFace;
10302   TopLoc_Location loc;
10303   double f,l, u = 0;
10304   gp_XY uv;
10305   vector< gp_XYZ > pos3D;
10306   bool isOnEdge, isTooConvexFace = false;
10307   TGeomID prevBaseId = -1;
10308   TNode2Edge* n2eMap = 0;
10309   TNode2Edge::iterator n2e;
10310
10311   // Create intermediate nodes on each _LayerEdge
10312
10313   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
10314   {
10315     _EdgesOnShape& eos = data._edgesOnShape[iS];
10316     if ( eos._edges.empty() ) continue;
10317
10318     if ( eos._edges[0]->_nodes.size() < 2 )
10319       continue; // on _noShrinkShapes
10320
10321     // get data of a shrink shape
10322     isOnEdge = false;
10323     geomEdge.Nullify(); geomFace.Nullify();
10324     curve.Nullify(); surface.Nullify();
10325     if ( !eos._sWOL.IsNull() )
10326     {
10327       isOnEdge = ( eos.SWOLType() == TopAbs_EDGE );
10328       if ( isOnEdge )
10329       {
10330         geomEdge = TopoDS::Edge( eos._sWOL );
10331         curve    = BRep_Tool::Curve( geomEdge, loc, f,l);
10332       }
10333       else
10334       {
10335         geomFace = TopoDS::Face( eos._sWOL );
10336         surface  = helper.GetSurface( geomFace );
10337       }
10338     }
10339     else if ( eos.ShapeType() == TopAbs_FACE && eos._toSmooth )
10340     {
10341       geomFace = TopoDS::Face( eos._shape );
10342       surface  = helper.GetSurface( geomFace );
10343       // propagate _toSmooth back to _eosC1, which was unset in findShapesToSmooth()
10344       for ( size_t i = 0; i < eos._eosC1.size(); ++i )
10345         eos._eosC1[ i ]->_toSmooth = true;
10346
10347       isTooConvexFace = false;
10348       if ( _ConvexFace* cf = data.GetConvexFace( eos._shapeID ))
10349         isTooConvexFace = cf->_isTooCurved;
10350     }
10351
10352     vector< double > segLen;
10353     for ( size_t i = 0; i < eos._edges.size(); ++i )
10354     {
10355       _LayerEdge& edge = *eos._edges[i];
10356       if ( edge._pos.size() < 2 )
10357         continue;
10358
10359       // get accumulated length of segments
10360       segLen.resize( edge._pos.size() );
10361       segLen[0] = 0.0;
10362       if ( eos._sWOL.IsNull() )
10363       {
10364         bool useNormal = true;
10365         bool    usePos = false;
10366         bool  smoothed = false;
10367         double   preci = 0.1 * edge._len;
10368         if ( eos._toSmooth && edge._pos.size() > 2 )
10369         {
10370           smoothed = edge.GetSmoothedPos( preci );
10371         }
10372         if ( smoothed )
10373         {
10374           if ( !surface.IsNull() && !isTooConvexFace ) // edge smoothed on FACE
10375           {
10376             useNormal = usePos = false;
10377             gp_Pnt2d uv = helper.GetNodeUV( geomFace, edge._nodes[0] );
10378             for ( size_t j = 1; j < edge._pos.size() && !useNormal; ++j )
10379             {
10380               uv = surface->NextValueOfUV( uv, edge._pos[j], preci );
10381               if ( surface->Gap() < 2. * edge._len )
10382                 segLen[j] = surface->Gap();
10383               else
10384                 useNormal = true;
10385             }
10386           }
10387         }
10388         else if ( !edge.Is( _LayerEdge::NORMAL_UPDATED ))
10389         {
10390 #ifndef __NODES_AT_POS
10391           useNormal = usePos = false;
10392           edge._pos[1] = edge._pos.back();
10393           edge._pos.resize( 2 );
10394           segLen.resize( 2 );
10395           segLen[ 1 ] = edge._len;
10396 #endif
10397         }
10398         if ( useNormal && edge.Is( _LayerEdge::NORMAL_UPDATED ))
10399         {
10400           useNormal = usePos = false;
10401           _LayerEdge tmpEdge; // get original _normal
10402           tmpEdge._nodes.push_back( edge._nodes[0] );
10403           if ( !setEdgeData( tmpEdge, eos, helper, data ))
10404             usePos = true;
10405           else
10406             for ( size_t j = 1; j < edge._pos.size(); ++j )
10407               segLen[j] = ( edge._pos[j] - edge._pos[0] ) * tmpEdge._normal;
10408         }
10409         if ( useNormal )
10410         {
10411           for ( size_t j = 1; j < edge._pos.size(); ++j )
10412             segLen[j] = ( edge._pos[j] - edge._pos[0] ) * edge._normal;
10413         }
10414         if ( usePos )
10415         {
10416           for ( size_t j = 1; j < edge._pos.size(); ++j )
10417             segLen[j] = segLen[j-1] + ( edge._pos[j-1] - edge._pos[j] ).Modulus();
10418         }
10419         else
10420         {
10421           bool swapped = ( edge._pos.size() > 2 );
10422           while ( swapped )
10423           {
10424             swapped = false;
10425             for ( size_t j = 1; j < edge._pos.size()-1; ++j )
10426               if ( segLen[j] > segLen.back() )
10427               {
10428                 segLen.erase( segLen.begin() + j );
10429                 edge._pos.erase( edge._pos.begin() + j );
10430                 --j;
10431               }
10432               else if ( segLen[j] < segLen[j-1] )
10433               {
10434                 std::swap( segLen[j], segLen[j-1] );
10435                 std::swap( edge._pos[j], edge._pos[j-1] );
10436                 swapped = true;
10437               }
10438           }
10439         }
10440         // smooth a path formed by edge._pos
10441 #ifndef __NODES_AT_POS
10442         if (( smoothed ) /*&&
10443             ( eos.ShapeType() == TopAbs_FACE || edge.Is( _LayerEdge::SMOOTHED_C1 ))*/)
10444           edge.SmoothPos( segLen, preci );
10445 #endif
10446       }
10447       else if ( eos._isRegularSWOL ) // usual SWOL
10448       {
10449         if ( edge.Is( _LayerEdge::SMOOTHED ))
10450         {
10451           SMESH_NodeXYZ p0( edge._nodes[0] );
10452           for ( size_t j = 1; j < edge._pos.size(); ++j )
10453           {
10454             gp_XYZ pj = surface->Value( edge._pos[j].X(), edge._pos[j].Y() ).XYZ();
10455             segLen[j] = ( pj - p0 ) * edge._normal;
10456           }
10457         }
10458         else
10459         {
10460           for ( size_t j = 1; j < edge._pos.size(); ++j )
10461             segLen[j] = segLen[j-1] + (edge._pos[j-1] - edge._pos[j] ).Modulus();
10462         }
10463       }
10464       else // SWOL is surface with singularities or irregularly parametrized curve
10465       {
10466         pos3D.resize( edge._pos.size() );
10467
10468         if ( !surface.IsNull() )
10469           for ( size_t j = 0; j < edge._pos.size(); ++j )
10470             pos3D[j] = surface->Value( edge._pos[j].X(), edge._pos[j].Y() ).XYZ();
10471         else if ( !curve.IsNull() )
10472           for ( size_t j = 0; j < edge._pos.size(); ++j )
10473             pos3D[j] = curve->Value( edge._pos[j].X() ).XYZ();
10474
10475         for ( size_t j = 1; j < edge._pos.size(); ++j )
10476           segLen[j] = segLen[j-1] + ( pos3D[j-1] - pos3D[j] ).Modulus();
10477       }
10478
10479       // allocate memory for new nodes if it is not yet refined
10480       const SMDS_MeshNode* tgtNode = edge._nodes.back();
10481       if ( edge._nodes.size() == 2 )
10482       {
10483 #ifdef __NODES_AT_POS
10484         int nbNodes = edge._pos.size();
10485 #else
10486         int nbNodes = eos._hyp.GetNumberLayers() + 1;
10487 #endif
10488         edge._nodes.resize( nbNodes, 0 );
10489         edge._nodes[1] = 0;
10490         edge._nodes.back() = tgtNode;
10491       }
10492       // restore shapePos of the last node by already treated _LayerEdge of another _SolidData
10493       const TGeomID baseShapeId = edge._nodes[0]->getshapeId();
10494       if ( baseShapeId != prevBaseId )
10495       {
10496         map< TGeomID, TNode2Edge* >::iterator s2ne = data._s2neMap.find( baseShapeId );
10497         n2eMap = ( s2ne == data._s2neMap.end() ) ? 0 : s2ne->second;
10498         prevBaseId = baseShapeId;
10499       }
10500       _LayerEdge* edgeOnSameNode = 0;
10501       bool        useExistingPos = false;
10502       if ( n2eMap && (( n2e = n2eMap->find( edge._nodes[0] )) != n2eMap->end() ))
10503       {
10504         edgeOnSameNode = n2e->second;
10505         useExistingPos = ( edgeOnSameNode->_len < edge._len ||
10506                            segLen[0] == segLen.back() ); // too short inflation step (bos #20643)
10507         const gp_XYZ& otherTgtPos = edgeOnSameNode->_pos.back();
10508         SMDS_PositionPtr  lastPos = tgtNode->GetPosition();
10509         if ( isOnEdge )
10510         {
10511           SMDS_EdgePositionPtr epos = lastPos;
10512           epos->SetUParameter( otherTgtPos.X() );
10513         }
10514         else
10515         {
10516           SMDS_FacePositionPtr fpos = lastPos;
10517           fpos->SetUParameter( otherTgtPos.X() );
10518           fpos->SetVParameter( otherTgtPos.Y() );
10519         }
10520       }
10521
10522       // create intermediate nodes
10523       const double      h0 = eos._hyp.Get1stLayerThickness( segLen.back() );
10524       const double zeroLen = std::numeric_limits<double>::min();
10525       double hSum = 0, hi = h0/eos._hyp.GetStretchFactor();
10526       size_t iSeg = 1;
10527       for ( size_t iStep = 1; iStep < edge._nodes.size(); ++iStep )
10528       {
10529         // compute an intermediate position
10530         hi *= eos._hyp.GetStretchFactor();
10531         hSum += hi;
10532         while ( hSum > segLen[iSeg] && iSeg < segLen.size()-1 )
10533           ++iSeg;
10534         int iPrevSeg = iSeg-1;
10535         while ( fabs( segLen[iPrevSeg] - segLen[iSeg]) <= zeroLen && iPrevSeg > 0 )
10536           --iPrevSeg;
10537         double   r = ( segLen[iSeg] - hSum ) / ( segLen[iSeg] - segLen[iPrevSeg] );
10538         gp_Pnt pos = r * edge._pos[iPrevSeg] + (1-r) * edge._pos[iSeg];
10539 #ifdef __NODES_AT_POS
10540         pos = edge._pos[ iStep ];
10541 #endif
10542         SMDS_MeshNode*& node = const_cast< SMDS_MeshNode*& >( edge._nodes[ iStep ]);
10543         if ( !eos._sWOL.IsNull() )
10544         {
10545           // compute XYZ by parameters <pos>
10546           if ( isOnEdge )
10547           {
10548             u = pos.X();
10549             if ( !node )
10550               pos = curve->Value( u ).Transformed(loc);
10551           }
10552           else if ( eos._isRegularSWOL )
10553           {
10554             uv.SetCoord( pos.X(), pos.Y() );
10555             if ( !node )
10556               pos = surface->Value( pos.X(), pos.Y() );
10557           }
10558           else
10559           {
10560             uv.SetCoord( pos.X(), pos.Y() );
10561             gp_Pnt p = r * pos3D[ iPrevSeg ] + (1-r) * pos3D[ iSeg ];
10562             uv = surface->NextValueOfUV( uv, p, BRep_Tool::Tolerance( geomFace )).XY();
10563             if ( !node )
10564               pos = surface->Value( uv );
10565           }
10566         }
10567         // create or update the node
10568         if ( !node )
10569         {
10570           node = helper.AddNode( pos.X(), pos.Y(), pos.Z());
10571           if ( !eos._sWOL.IsNull() )
10572           {
10573             if ( isOnEdge )
10574               getMeshDS()->SetNodeOnEdge( node, geomEdge, u );
10575             else
10576               getMeshDS()->SetNodeOnFace( node, geomFace, uv.X(), uv.Y() );
10577           }
10578           else
10579           {
10580             getMeshDS()->SetNodeInVolume( node, helper.GetSubShapeID() );
10581           }
10582         }
10583         else
10584         {
10585           if ( !eos._sWOL.IsNull() )
10586           {
10587             // make average pos from new and current parameters
10588             if ( isOnEdge )
10589             {
10590               //u = 0.5 * ( u + helper.GetNodeU( geomEdge, node ));
10591               if ( useExistingPos )
10592                 u = helper.GetNodeU( geomEdge, node );
10593               pos = curve->Value( u ).Transformed(loc);
10594
10595               SMDS_EdgePositionPtr epos = node->GetPosition();
10596               epos->SetUParameter( u );
10597             }
10598             else
10599             {
10600               //uv = 0.5 * ( uv + helper.GetNodeUV( geomFace, node ));
10601               if ( useExistingPos )
10602                 uv = helper.GetNodeUV( geomFace, node );
10603               pos = surface->Value( uv );
10604
10605               SMDS_FacePositionPtr fpos = node->GetPosition();
10606               fpos->SetUParameter( uv.X() );
10607               fpos->SetVParameter( uv.Y() );
10608             }
10609           }
10610           node->setXYZ( pos.X(), pos.Y(), pos.Z() );
10611         }
10612       } // loop on edge._nodes
10613
10614       if ( !eos._sWOL.IsNull() ) // prepare for shrink()
10615       {
10616         if ( isOnEdge )
10617           edge._pos.back().SetCoord( u, 0,0);
10618         else
10619           edge._pos.back().SetCoord( uv.X(), uv.Y() ,0);
10620
10621         if ( edgeOnSameNode )
10622           edgeOnSameNode->_pos.back() = edge._pos.back();
10623       }
10624
10625     } // loop on eos._edges to create nodes
10626
10627
10628     if ( !getMeshDS()->IsEmbeddedMode() )
10629       // Log node movement
10630       for ( size_t i = 0; i < eos._edges.size(); ++i )
10631       {
10632         SMESH_TNodeXYZ p ( eos._edges[i]->_nodes.back() );
10633         getMeshDS()->MoveNode( p._node, p.X(), p.Y(), p.Z() );
10634       }
10635   }
10636
10637
10638   // Create volumes
10639
10640   helper.SetElementsOnShape(true);
10641
10642   vector< vector<const SMDS_MeshNode*>* > nnVec;
10643   set< vector<const SMDS_MeshNode*>* >    nnSet;
10644   set< int >                       degenEdgeInd;
10645   vector<const SMDS_MeshElement*>     degenVols;
10646
10647   TopExp_Explorer exp( data._solid, TopAbs_FACE );
10648   for ( ; exp.More(); exp.Next() )
10649   {
10650     const TGeomID faceID = getMeshDS()->ShapeToIndex( exp.Current() );
10651     if ( data._ignoreFaceIds.count( faceID ))
10652       continue;
10653     _EdgesOnShape*    eos = data.GetShapeEdges( faceID );
10654     SMDS_MeshGroup* group = StdMeshers_ViscousLayers::CreateGroup( eos->_hyp.GetGroupName(),
10655                                                                    *helper.GetMesh(),
10656                                                                    SMDSAbs_Volume );
10657     std::vector< const SMDS_MeshElement* > vols;
10658     const bool isReversedFace = data._reversedFaceIds.count( faceID );
10659     SMESHDS_SubMesh*    fSubM = getMeshDS()->MeshElements( exp.Current() );
10660     SMDS_ElemIteratorPtr  fIt = fSubM->GetElements();
10661     while ( fIt->more() )
10662     {
10663       const SMDS_MeshElement* face = fIt->next();
10664       const int            nbNodes = face->NbCornerNodes();
10665       nnVec.resize( nbNodes );
10666       nnSet.clear();
10667       degenEdgeInd.clear();
10668       size_t maxZ = 0, minZ = std::numeric_limits<size_t>::max();
10669       SMDS_NodeIteratorPtr nIt = face->nodeIterator();
10670       for ( int iN = 0; iN < nbNodes; ++iN )
10671       {
10672         const SMDS_MeshNode* n = nIt->next();
10673         _LayerEdge*       edge = data._n2eMap[ n ];
10674         const int i = isReversedFace ? nbNodes-1-iN : iN;
10675         nnVec[ i ] = & edge->_nodes;
10676         maxZ = std::max( maxZ, nnVec[ i ]->size() );
10677         minZ = std::min( minZ, nnVec[ i ]->size() );
10678
10679         if ( helper.HasDegeneratedEdges() )
10680           nnSet.insert( nnVec[ i ]);
10681       }
10682
10683       if ( maxZ == 0 )
10684         continue;
10685       if ( 0 < nnSet.size() && nnSet.size() < 3 )
10686         continue;
10687
10688       vols.clear();
10689       const SMDS_MeshElement* vol;
10690
10691       switch ( nbNodes )
10692       {
10693       case 3: // TRIA
10694       {
10695         // PENTA
10696         for ( size_t iZ = 1; iZ < minZ; ++iZ )
10697         {
10698           vol = helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1], (*nnVec[2])[iZ-1],
10699                                   (*nnVec[0])[iZ],   (*nnVec[1])[iZ],   (*nnVec[2])[iZ]);
10700           vols.push_back( vol );
10701         }
10702
10703         for ( size_t iZ = minZ; iZ < maxZ; ++iZ )
10704         {
10705           for ( int iN = 0; iN < nbNodes; ++iN )
10706             if ( nnVec[ iN ]->size() < iZ+1 )
10707               degenEdgeInd.insert( iN );
10708
10709           if ( degenEdgeInd.size() == 1 )  // PYRAM
10710           {
10711             int i2 = *degenEdgeInd.begin();
10712             int i0 = helper.WrapIndex( i2 - 1, nbNodes );
10713             int i1 = helper.WrapIndex( i2 + 1, nbNodes );
10714             vol = helper.AddVolume( (*nnVec[i0])[iZ-1], (*nnVec[i1])[iZ-1],
10715                                     (*nnVec[i1])[iZ  ], (*nnVec[i0])[iZ  ], (*nnVec[i2]).back());
10716             vols.push_back( vol );
10717           }
10718           else  // TETRA
10719           {
10720             int i3 = !degenEdgeInd.count(0) ? 0 : !degenEdgeInd.count(1) ? 1 : 2;
10721             vol = helper.AddVolume( (*nnVec[  0 ])[ i3 == 0 ? iZ-1 : nnVec[0]->size()-1 ],
10722                                     (*nnVec[  1 ])[ i3 == 1 ? iZ-1 : nnVec[1]->size()-1 ],
10723                                     (*nnVec[  2 ])[ i3 == 2 ? iZ-1 : nnVec[2]->size()-1 ],
10724                                     (*nnVec[ i3 ])[ iZ ]);
10725             vols.push_back( vol );
10726           }
10727         }
10728         break; // TRIA
10729       }
10730       case 4: // QUAD
10731       {
10732         // HEX
10733         for ( size_t iZ = 1; iZ < minZ; ++iZ )
10734         {
10735           vol = helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1],
10736                                   (*nnVec[2])[iZ-1], (*nnVec[3])[iZ-1],
10737                                   (*nnVec[0])[iZ],   (*nnVec[1])[iZ],
10738                                   (*nnVec[2])[iZ],   (*nnVec[3])[iZ]);
10739           vols.push_back( vol );
10740         }
10741
10742         for ( size_t iZ = minZ; iZ < maxZ; ++iZ )
10743         {
10744           for ( int iN = 0; iN < nbNodes; ++iN )
10745             if ( nnVec[ iN ]->size() < iZ+1 )
10746               degenEdgeInd.insert( iN );
10747
10748           switch ( degenEdgeInd.size() )
10749           {
10750           case 2: // PENTA
10751           {
10752             int i2 = *degenEdgeInd.begin();
10753             int i3 = *degenEdgeInd.rbegin();
10754             bool ok = ( i3 - i2 == 1 );
10755             if ( i2 == 0 && i3 == 3 ) { i2 = 3; i3 = 0; ok = true; }
10756             int i0 = helper.WrapIndex( i3 + 1, nbNodes );
10757             int i1 = helper.WrapIndex( i0 + 1, nbNodes );
10758
10759             vol = helper.AddVolume( nnVec[i3]->back(), (*nnVec[i0])[iZ], (*nnVec[i0])[iZ-1],
10760                                     nnVec[i2]->back(), (*nnVec[i1])[iZ], (*nnVec[i1])[iZ-1]);
10761             vols.push_back( vol );
10762             if ( !ok && vol )
10763               degenVols.push_back( vol );
10764           }
10765           break;
10766
10767           default: // degen HEX
10768           {
10769             vol = helper.AddVolume( nnVec[0]->size() > iZ-1 ? (*nnVec[0])[iZ-1] : nnVec[0]->back(),
10770                                     nnVec[1]->size() > iZ-1 ? (*nnVec[1])[iZ-1] : nnVec[1]->back(),
10771                                     nnVec[2]->size() > iZ-1 ? (*nnVec[2])[iZ-1] : nnVec[2]->back(),
10772                                     nnVec[3]->size() > iZ-1 ? (*nnVec[3])[iZ-1] : nnVec[3]->back(),
10773                                     nnVec[0]->size() > iZ   ? (*nnVec[0])[iZ]   : nnVec[0]->back(),
10774                                     nnVec[1]->size() > iZ   ? (*nnVec[1])[iZ]   : nnVec[1]->back(),
10775                                     nnVec[2]->size() > iZ   ? (*nnVec[2])[iZ]   : nnVec[2]->back(),
10776                                     nnVec[3]->size() > iZ   ? (*nnVec[3])[iZ]   : nnVec[3]->back());
10777             vols.push_back( vol );
10778             degenVols.push_back( vol );
10779           }
10780           }
10781         }
10782         break; // HEX
10783       }
10784       default:
10785         return error("Not supported type of element", data._index);
10786
10787       } // switch ( nbNodes )
10788
10789       if ( group )
10790         for ( size_t i = 0; i < vols.size(); ++i )
10791           group->Add( vols[ i ]);
10792
10793     } // while ( fIt->more() )
10794   } // loop on FACEs
10795
10796   if ( !degenVols.empty() )
10797   {
10798     SMESH_ComputeErrorPtr& err = _mesh->GetSubMesh( data._solid )->GetComputeError();
10799     if ( !err || err->IsOK() )
10800     {
10801       SMESH_BadInputElements* badElems =
10802         new SMESH_BadInputElements( getMeshDS(), COMPERR_WARNING, "Bad quality volumes created" );
10803       badElems->myBadElements.insert( badElems->myBadElements.end(),
10804                                       degenVols.begin(),degenVols.end() );
10805       err.reset( badElems );
10806     }
10807   }
10808
10809   return true;
10810 }
10811
10812 namespace VISCOUS_3D
10813 {
10814   struct ShrinkFace;
10815   //--------------------------------------------------------------------------------
10816   /*!
10817    * \brief Pair of periodic FACEs
10818    */
10819   struct PeriodicFaces
10820   {
10821     typedef StdMeshers_ProjectionUtils::TrsfFinder3D Trsf;
10822
10823     ShrinkFace*  _shriFace[2];
10824     TNodeNodeMap _nnMap;
10825     Trsf         _trsf;
10826
10827     PeriodicFaces( ShrinkFace* sf1, ShrinkFace* sf2 ): _shriFace{ sf1, sf2 } {}
10828     bool IncludeShrunk( const TopoDS_Face& face, const TopTools_MapOfShape& shrunkFaces ) const;
10829     bool MoveNodes( const TopoDS_Face& tgtFace );
10830     void Clear() { _nnMap.clear(); }
10831     bool IsEmpty() const { return _nnMap.empty(); }
10832   };
10833
10834   //--------------------------------------------------------------------------------
10835   /*!
10836    * \brief Shrink FACE data used to find periodic FACEs
10837    */
10838   struct ShrinkFace
10839   {
10840     // ................................................................................
10841     struct BndPart //!< part of FACE boundary, either shrink or no-shrink
10842     {
10843       bool                         _isShrink, _isReverse;
10844       int                          _nbSegments;
10845       AverageHyp*                  _hyp;
10846       std::vector< SMESH_NodeXYZ > _nodes;
10847       TopAbs_ShapeEnum             _vertSWOLType[2]; // shrink part includes VERTEXes
10848       AverageHyp*                  _vertHyp[2];
10849       double                       _edgeWOLLen[2]; // length of wol EDGE
10850       double                       _tol; // to compare _edgeWOLLen's
10851
10852       BndPart():
10853         _isShrink(0), _isReverse(0), _nbSegments(0), _hyp(0),
10854         _vertSWOLType{ TopAbs_WIRE, TopAbs_WIRE }, _vertHyp{ 0, 0 }, _edgeWOLLen{ 0., 0.}
10855       {}
10856
10857       bool IsEqualLengthEWOL( const BndPart& other ) const
10858       {
10859         return ( std::abs( _edgeWOLLen[0] - other._edgeWOLLen[0] ) < _tol &&
10860                  std::abs( _edgeWOLLen[1] - other._edgeWOLLen[1] ) < _tol );
10861       }
10862
10863       bool operator==( const BndPart& other ) const
10864       {
10865         return ( _isShrink       == other._isShrink &&
10866                  _nbSegments     == other._nbSegments &&
10867                  _nodes.size()   == other._nodes.size() &&
10868                  vertSWOLType1() == other.vertSWOLType1() &&
10869                  vertSWOLType2() == other.vertSWOLType2() &&
10870                  (( !_isShrink ) ||
10871                   ( *_hyp        == *other._hyp &&
10872                     vertHyp1()   == other.vertHyp1() &&
10873                     vertHyp2()   == other.vertHyp2() &&
10874                     IsEqualLengthEWOL( other )))
10875                  );
10876       }
10877       bool CanAppend( const BndPart& other )
10878       {
10879         return ( _isShrink  == other._isShrink  &&
10880                  (( !_isShrink ) ||
10881                   ( *_hyp        == *other._hyp &&
10882                     *_hyp        == vertHyp2()  &&
10883                     vertHyp2()   == other.vertHyp1() ))
10884                  );
10885       }
10886       void Append( const BndPart& other )
10887       {
10888         _nbSegments += other._nbSegments;
10889         bool hasCommonNode = ( _nodes.back()->GetID() == other._nodes.front()->GetID() );
10890         _nodes.insert( _nodes.end(), other._nodes.begin() + hasCommonNode, other._nodes.end() );
10891         _vertSWOLType[1] = other._vertSWOLType[1];
10892         if ( _isShrink ) {
10893           _vertHyp[1]    = other._vertHyp[1];
10894           _edgeWOLLen[1] = other._edgeWOLLen[1];
10895         }
10896       }
10897       const SMDS_MeshNode* Node(size_t i)  const
10898       {
10899         return _nodes[ _isReverse ? ( _nodes.size() - 1 - i ) : i ]._node;
10900       }
10901       void Reverse() { _isReverse = !_isReverse; }
10902       const TopAbs_ShapeEnum& vertSWOLType1() const { return _vertSWOLType[ _isReverse  ]; }
10903       const TopAbs_ShapeEnum& vertSWOLType2() const { return _vertSWOLType[ !_isReverse ]; }
10904       const AverageHyp&       vertHyp1()      const { return *(_vertHyp[ _isReverse  ]); }
10905       const AverageHyp&       vertHyp2()      const { return *(_vertHyp[ !_isReverse ]); }
10906     };
10907     // ................................................................................
10908
10909     SMESH_subMesh*       _subMesh;
10910     _SolidData*          _data1;
10911     _SolidData*          _data2;
10912
10913     std::list< BndPart > _boundary;
10914     int                  _boundarySize, _nbBoundaryParts;
10915
10916     void Init( SMESH_subMesh* sm, _SolidData* sd1, _SolidData* sd2 )
10917     {
10918       _subMesh = sm; _data1 = sd1; _data2 = sd2;
10919     }
10920     bool IsSame( const TopoDS_Face& face ) const
10921     {
10922       return _subMesh->GetSubShape().IsSame( face );
10923     }
10924     bool IsShrunk( const TopTools_MapOfShape& shrunkFaces ) const
10925     {
10926       return shrunkFaces.Contains( _subMesh->GetSubShape() );
10927     }
10928
10929     //================================================================================
10930     /*!
10931      * Check if meshes on two FACEs are equal
10932      */
10933     bool IsPeriodic( ShrinkFace& other, PeriodicFaces& periodic )
10934     {
10935       if ( !IsSameNbElements( other ))
10936         return false;
10937
10938       this->SetBoundary();
10939       other.SetBoundary();
10940       if ( this->_boundarySize    != other._boundarySize ||
10941            this->_nbBoundaryParts != other._nbBoundaryParts )
10942         return false;
10943
10944       for ( int isReverse = 0; isReverse < 2; ++isReverse )
10945       {
10946         if ( isReverse )
10947           Reverse( _boundary );
10948
10949         // check boundaries
10950         bool equalBoundary = false;
10951         for ( int iP = 0; iP < _nbBoundaryParts &&  !equalBoundary; ++iP )
10952         {
10953           if ( ! ( equalBoundary = ( this->_boundary == other._boundary )))
10954             // set first part at end
10955             _boundary.splice( _boundary.end(), _boundary, _boundary.begin() );
10956         }
10957         if ( !equalBoundary )
10958           continue;
10959
10960         // check connectivity
10961         std::set<const SMDS_MeshElement*> elemsThis, elemsOther;
10962         this->GetElements( elemsThis  );
10963         other.GetElements( elemsOther );
10964         SMESH_MeshEditor::Sew_Error err =
10965           SMESH_MeshEditor::FindMatchingNodes( elemsThis, elemsOther,
10966                                                this->_boundary.front().Node(0),
10967                                                other._boundary.front().Node(0),
10968                                                this->_boundary.front().Node(1),
10969                                                other._boundary.front().Node(1),
10970                                                periodic._nnMap );
10971         if ( err != SMESH_MeshEditor::SEW_OK )
10972           continue;
10973
10974         // check node positions
10975         std::vector< gp_XYZ > srcPnts, tgtPnts;
10976         this->GetBoundaryPoints( srcPnts );
10977         other.GetBoundaryPoints( tgtPnts );
10978         if ( !periodic._trsf.Solve( srcPnts, tgtPnts )) {
10979           continue;
10980         }
10981         double tol = std::numeric_limits<double>::max(); // tolerance by segment size
10982         for ( size_t i = 1; i < srcPnts.size(); ++i ) {
10983           tol = Min( tol, ( srcPnts[i-1] - srcPnts[i] ).SquareModulus() );
10984         }
10985         tol = 0.01 * Sqrt( tol );
10986         for ( BndPart& boundary : _boundary ) { // tolerance by VL thickness
10987           if ( boundary._isShrink )
10988             tol = Min( tol, boundary._hyp->Get1stLayerThickness() / 50. );
10989         }
10990         bool nodeCoincide = true;
10991         TNodeNodeMap::iterator n2n = periodic._nnMap.begin();
10992         for ( ; n2n != periodic._nnMap.end() &&  nodeCoincide; ++n2n )
10993         {
10994           SMESH_NodeXYZ nSrc = n2n->first;
10995           SMESH_NodeXYZ nTgt = n2n->second;
10996           gp_XYZ pTgt = periodic._trsf.Transform( nSrc );
10997           nodeCoincide = (( pTgt - nTgt ).SquareModulus() < tol * tol );
10998         }
10999         if ( nodeCoincide )
11000           return true;
11001       }
11002       return false;
11003     }
11004
11005     bool IsSameNbElements( ShrinkFace& other ) // check number of mesh faces
11006     {
11007       SMESHDS_SubMesh* sm1 = this->_subMesh->GetSubMeshDS();
11008       SMESHDS_SubMesh* sm2 = other._subMesh->GetSubMeshDS();
11009       return ( sm1->NbElements() == sm2->NbElements() &&
11010                sm1->NbNodes()    == sm2->NbNodes() );
11011     }
11012
11013     void Reverse( std::list< BndPart >& boundary )
11014     {
11015       boundary.reverse();
11016       for ( std::list< BndPart >::iterator part = boundary.begin(); part != boundary.end(); ++part )
11017         part->Reverse();
11018     }
11019
11020     void SetBoundary()
11021     {
11022       if ( !_boundary.empty() )
11023         return;
11024
11025       TopoDS_Face F = TopoDS::Face( _subMesh->GetSubShape() );
11026       if ( F.Orientation() >= TopAbs_INTERNAL ) F.Orientation( TopAbs_FORWARD );
11027       std::list< TopoDS_Edge > edges;
11028       std::list< int > nbEdgesInWire;
11029       /*int nbWires =*/ SMESH_Block::GetOrderedEdges (F, edges, nbEdgesInWire);
11030
11031       // std::list< TopoDS_Edge >::iterator edgesEnd = edges.end();
11032       // if ( nbWires > 1 ) {
11033       //   edgesEnd = edges.begin();
11034       //   std::advance( edgesEnd, nbEdgesInWire.front() );
11035       // }
11036       StdMeshers_FaceSide fSide( F, edges, _subMesh->GetFather(),
11037                                  /*fwd=*/true, /*skipMedium=*/true );
11038       _boundarySize = fSide.NbSegments();
11039
11040       //TopoDS_Vertex vv[2];
11041       //std::list< TopoDS_Edge >::iterator edgeIt = edges.begin();
11042       for ( int iE = 0; iE < nbEdgesInWire.front(); ++iE )
11043       {
11044         BndPart bndPart;
11045
11046         std::vector<const SMDS_MeshNode*> nodes = fSide.GetOrderedNodes( iE );
11047         bndPart._nodes.assign( nodes.begin(), nodes.end() );
11048         bndPart._nbSegments = bndPart._nodes.size() - 1;
11049
11050         _EdgesOnShape*  eos = _data1->GetShapeEdges( fSide.EdgeID( iE ));
11051
11052         bndPart._isShrink = ( eos->SWOLType() == TopAbs_FACE );
11053         if ( bndPart._isShrink )
11054           if ((           _data1->_noShrinkShapes.count( eos->_shapeID )) ||
11055               ( _data2 && _data2->_noShrinkShapes.count( eos->_shapeID )))
11056             bndPart._isShrink = false;
11057
11058         if ( bndPart._isShrink )
11059         {
11060           bndPart._hyp = & eos->_hyp;
11061           _EdgesOnShape* eov[2] = { _data1->GetShapeEdges( fSide.FirstVertex( iE )),
11062                                     _data1->GetShapeEdges( fSide.LastVertex ( iE )) };
11063           for ( int iV = 0; iV < 2; ++iV )
11064           {
11065             bndPart._vertHyp     [iV] = & eov[iV]->_hyp;
11066             bndPart._vertSWOLType[iV] = eov[iV]->SWOLType();
11067             if ( _data1->_noShrinkShapes.count( eov[iV]->_shapeID ))
11068               bndPart._vertSWOLType[iV] = TopAbs_SHAPE;
11069             if ( _data2 && bndPart._vertSWOLType[iV] != TopAbs_SHAPE )
11070             {
11071               eov[iV] = _data2->GetShapeEdges( iV ? fSide.LastVertex(iE) : fSide.FirstVertex(iE ));
11072               if ( _data2->_noShrinkShapes.count( eov[iV]->_shapeID ))
11073                 bndPart._vertSWOLType[iV] = TopAbs_SHAPE;
11074               else if ( eov[iV]->SWOLType() > bndPart._vertSWOLType[iV] )
11075                 bndPart._vertSWOLType[iV] = eov[iV]->SWOLType();
11076             }
11077           }
11078           bndPart._edgeWOLLen[0] = fSide.EdgeLength( iE - 1 );
11079           bndPart._edgeWOLLen[1] = fSide.EdgeLength( iE + 1 );
11080
11081           bndPart._tol = std::numeric_limits<double>::max(); // tolerance by segment size
11082           for ( size_t i = 1; i < bndPart._nodes.size(); ++i )
11083             bndPart._tol = Min( bndPart._tol,
11084                                 ( bndPart._nodes[i-1] - bndPart._nodes[i] ).SquareModulus() );
11085         }
11086
11087         if ( _boundary.empty() || ! _boundary.back().CanAppend( bndPart ))
11088           _boundary.push_back( bndPart );
11089         else
11090           _boundary.back().Append( bndPart );
11091       }
11092
11093       _nbBoundaryParts = _boundary.size();
11094       if ( _nbBoundaryParts > 1 && _boundary.front()._isShrink == _boundary.back()._isShrink )
11095       {
11096         _boundary.back().Append( _boundary.front() );
11097         _boundary.pop_front();
11098         --_nbBoundaryParts;
11099       }
11100     }
11101
11102     void GetElements( std::set<const SMDS_MeshElement*>& theElems)
11103     {
11104       if ( SMESHDS_SubMesh* sm = _subMesh->GetSubMeshDS() )
11105         for ( SMDS_ElemIteratorPtr fIt = sm->GetElements(); fIt->more(); )
11106           theElems.insert( theElems.end(), fIt->next() );
11107
11108       return ;
11109     }
11110
11111     void GetBoundaryPoints( std::vector< gp_XYZ >& points )
11112     {
11113       points.reserve( _boundarySize );
11114       size_t  nb = _boundary.rbegin()->_nodes.size();
11115       smIdType lastID = _boundary.rbegin()->Node( nb - 1 )->GetID();
11116       std::list< BndPart >::const_iterator part = _boundary.begin();
11117       for ( ; part != _boundary.end(); ++part )
11118       {
11119         size_t nb = part->_nodes.size();
11120         size_t iF = 0;
11121         size_t iR = nb - 1;
11122         size_t* i = part->_isReverse ? &iR : &iF;
11123         if ( part->_nodes[ *i ]->GetID() == lastID )
11124           ++iF, --iR;
11125         for ( ; iF < nb; ++iF, --iR )
11126           points.push_back( part->_nodes[ *i ]);
11127         --iF, ++iR;
11128         lastID = part->_nodes[ *i ]->GetID();
11129       }
11130     }
11131   }; // struct ShrinkFace
11132
11133   //--------------------------------------------------------------------------------
11134   /*!
11135    * \brief Periodic FACEs
11136    */
11137   struct Periodicity
11138   {
11139     std::vector< ShrinkFace >    _shrinkFaces;
11140     std::vector< PeriodicFaces > _periodicFaces;
11141
11142     PeriodicFaces* GetPeriodic( const TopoDS_Face& face, const TopTools_MapOfShape& shrunkFaces )
11143     {
11144       for ( size_t i = 0; i < _periodicFaces.size(); ++i )
11145         if ( _periodicFaces[ i ].IncludeShrunk( face, shrunkFaces ))
11146           return & _periodicFaces[ i ];
11147       return 0;
11148     }
11149     void ClearPeriodic( const TopoDS_Face& face )
11150     {
11151       for ( size_t i = 0; i < _periodicFaces.size(); ++i )
11152         if ( _periodicFaces[ i ]._shriFace[0]->IsSame( face ) ||
11153              _periodicFaces[ i ]._shriFace[1]->IsSame( face ))
11154           _periodicFaces[ i ].Clear();
11155     }
11156   };
11157
11158   //================================================================================
11159   /*!
11160    * Check if a pair includes the given FACE and the other FACE is already shrunk
11161    */
11162   bool PeriodicFaces::IncludeShrunk( const TopoDS_Face&         face,
11163                                      const TopTools_MapOfShape& shrunkFaces ) const
11164   {
11165     if ( IsEmpty() ) return false;
11166     return (( _shriFace[0]->IsSame( face ) && _shriFace[1]->IsShrunk( shrunkFaces )) ||
11167             ( _shriFace[1]->IsSame( face ) && _shriFace[0]->IsShrunk( shrunkFaces )));
11168   }
11169
11170   //================================================================================
11171   /*!
11172    * Make equal meshes on periodic faces by moving corresponding nodes
11173    */
11174   bool PeriodicFaces::MoveNodes( const TopoDS_Face& tgtFace )
11175   {
11176     int iTgt = _shriFace[1]->IsSame( tgtFace );
11177     int iSrc = 1 - iTgt;
11178
11179     _SolidData* dataSrc = _shriFace[iSrc]->_data1;
11180     _SolidData* dataTgt = _shriFace[iTgt]->_data1;
11181
11182     Trsf * trsf = & _trsf, trsfInverse;
11183     if ( iSrc != 0 )
11184     {
11185       trsfInverse = _trsf;
11186       if ( !trsfInverse.Invert())
11187         return false;
11188       trsf = &trsfInverse;
11189     }
11190     SMESHDS_Mesh* meshDS = dataSrc->GetHelper().GetMeshDS();
11191
11192     dumpFunction(SMESH_Comment("periodicMoveNodes_F")
11193                                << _shriFace[iSrc]->_subMesh->GetId() << "_F"
11194                                << _shriFace[iTgt]->_subMesh->GetId() );
11195     TNode2Edge::iterator n2e;
11196     TNodeNodeMap::iterator n2n = _nnMap.begin();
11197     for ( ; n2n != _nnMap.end(); ++n2n )
11198     {
11199       const SMDS_MeshNode* const* nn = & n2n->first;
11200       const SMDS_MeshNode*      nSrc = nn[ iSrc ];
11201       const SMDS_MeshNode*      nTgt = nn[ iTgt ];
11202
11203       if (( nSrc->GetPosition()->GetDim() == 2 ) ||
11204           (( n2e = dataSrc->_n2eMap.find( nSrc )) == dataSrc->_n2eMap.end() ))
11205       {
11206         SMESH_NodeXYZ pSrc = nSrc;
11207         gp_XYZ pTgt = trsf->Transform( pSrc );
11208         meshDS->MoveNode( nTgt, pTgt.X(), pTgt.Y(), pTgt.Z() );
11209       }
11210       else
11211       {
11212         _LayerEdge* leSrc = n2e->second;
11213         n2e = dataTgt->_n2eMap.find( nTgt );
11214         if ( n2e == dataTgt->_n2eMap.end() )
11215           break;
11216         _LayerEdge* leTgt = n2e->second;
11217         if ( leSrc->_nodes.size() != leTgt->_nodes.size() )
11218           break;
11219         for ( size_t iN = 1; iN < leSrc->_nodes.size(); ++iN )
11220         {
11221           SMESH_NodeXYZ pSrc = leSrc->_nodes[ iN ];
11222           gp_XYZ pTgt = trsf->Transform( pSrc );
11223           meshDS->MoveNode( leTgt->_nodes[ iN ], pTgt.X(), pTgt.Y(), pTgt.Z() );
11224
11225           dumpMove( leTgt->_nodes[ iN ]);
11226         }
11227       }
11228     }
11229     bool done = ( n2n == _nnMap.end() );
11230     debugMsg( "PeriodicFaces::MoveNodes "
11231               << _shriFace[iSrc]->_subMesh->GetId() << " -> "
11232               << _shriFace[iTgt]->_subMesh->GetId() << " -- "
11233               << ( done ? "DONE" : "FAIL"));
11234     dumpFunctionEnd();
11235
11236     return done;
11237   }
11238 } // namespace VISCOUS_3D; Periodicity part
11239
11240
11241 //================================================================================
11242 /*!
11243  * \brief Find FACEs to shrink, that are equally meshed before shrink (i.e. periodic)
11244  *        and should remain equal after shrink
11245  */
11246 //================================================================================
11247
11248 void _ViscousBuilder::findPeriodicFaces()
11249 {
11250   // make map of (ids of FACEs to shrink mesh on) to (list of _SolidData containing
11251   // _LayerEdge's inflated along FACE or EDGE)
11252   std::map< TGeomID, std::list< _SolidData* > > id2sdMap;
11253   for ( size_t i = 0 ; i < _sdVec.size(); ++i )
11254   {
11255     _SolidData& data = _sdVec[i];
11256     std::map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
11257     for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
11258       if ( s2s->second.ShapeType() == TopAbs_FACE )
11259         id2sdMap[ getMeshDS()->ShapeToIndex( s2s->second )].push_back( &data );
11260   }
11261
11262   _periodicity.reset( new Periodicity );
11263   _periodicity->_shrinkFaces.resize( id2sdMap.size() );
11264
11265   std::map< TGeomID, std::list< _SolidData* > >::iterator id2sdIt = id2sdMap.begin();
11266   for ( size_t i = 0; i < id2sdMap.size(); ++i, ++id2sdIt )
11267   {
11268     _SolidData* sd1 = id2sdIt->second.front();
11269     _SolidData* sd2 = id2sdIt->second.back();
11270     _periodicity->_shrinkFaces[ i ].Init( _mesh->GetSubMeshContaining( id2sdIt->first ), sd1, sd2 );
11271   }
11272
11273   for (   size_t i1 = 0;      i1 < _periodicity->_shrinkFaces.size(); ++i1 )
11274     for ( size_t i2 = i1 + 1; i2 < _periodicity->_shrinkFaces.size(); ++i2 )
11275     {
11276       PeriodicFaces pf( & _periodicity->_shrinkFaces[ i1 ],
11277                         & _periodicity->_shrinkFaces[ i2 ]);
11278       if ( pf._shriFace[0]->IsPeriodic( *pf._shriFace[1], pf ))
11279       {
11280         _periodicity->_periodicFaces.push_back( pf );
11281       }
11282     }
11283   return;
11284 }
11285
11286 //================================================================================
11287 /*!
11288  * \brief Shrink 2D mesh on faces to let space for inflated layers
11289  */
11290 //================================================================================
11291
11292 bool _ViscousBuilder::shrink(_SolidData& theData)
11293 {
11294   // make map of (ids of FACEs to shrink mesh on) to (list of _SolidData containing
11295   // _LayerEdge's inflated along FACE or EDGE)
11296   map< TGeomID, list< _SolidData* > > f2sdMap;
11297   for ( size_t i = 0 ; i < _sdVec.size(); ++i )
11298   {
11299     _SolidData& data = _sdVec[i];
11300     map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
11301     for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
11302       if ( s2s->second.ShapeType() == TopAbs_FACE && !_shrunkFaces.Contains( s2s->second ))
11303       {
11304         f2sdMap[ getMeshDS()->ShapeToIndex( s2s->second )].push_back( &data );
11305
11306         // Put mesh faces on the shrunk FACE to the proxy sub-mesh to avoid
11307         // usage of mesh faces made in addBoundaryElements() by the 3D algo or
11308         // by StdMeshers_QuadToTriaAdaptor
11309         if ( SMESHDS_SubMesh* smDS = getMeshDS()->MeshElements( s2s->second ))
11310         {
11311           SMESH_ProxyMesh::SubMesh* proxySub =
11312             data._proxyMesh->getFaceSubM( TopoDS::Face( s2s->second ), /*create=*/true);
11313           if ( proxySub->NbElements() == 0 )
11314           {
11315             SMDS_ElemIteratorPtr fIt = smDS->GetElements();
11316             while ( fIt->more() )
11317             {
11318               const SMDS_MeshElement* f = fIt->next();
11319               // as a result 3D algo will use elements from proxySub and not from smDS
11320               proxySub->AddElement( f );
11321               f->setIsMarked( true );
11322
11323               // Mark nodes on the FACE to discriminate them from nodes
11324               // added by addBoundaryElements(); marked nodes are to be smoothed while shrink()
11325               for ( int iN = 0, nbN = f->NbNodes(); iN < nbN; ++iN )
11326               {
11327                 const SMDS_MeshNode* n = f->GetNode( iN );
11328                 if ( n->GetPosition()->GetDim() == 2 )
11329                   n->setIsMarked( true );
11330               }
11331             }
11332           }
11333         }
11334       }
11335   }
11336
11337   SMESH_MesherHelper helper( *_mesh );
11338   helper.ToFixNodeParameters( true );
11339
11340   // EDGEs to shrink
11341   map< TGeomID, _Shrinker1D > e2shrMap;
11342   vector< _EdgesOnShape* > subEOS;
11343   vector< _LayerEdge* > lEdges;
11344
11345   // loop on FACEs to shrink mesh on
11346   map< TGeomID, list< _SolidData* > >::iterator f2sd = f2sdMap.begin();
11347   for ( ; f2sd != f2sdMap.end(); ++f2sd )
11348   {
11349     list< _SolidData* > & dataList = f2sd->second;
11350     if ( dataList.front()->_n2eMap.empty() ||
11351          dataList.back() ->_n2eMap.empty() )
11352       continue; // not yet computed
11353     if ( dataList.front() != &theData &&
11354          dataList.back()  != &theData )
11355       continue;
11356
11357     _SolidData&      data = *dataList.front();
11358     _SolidData*     data2 = dataList.size() > 1 ? dataList.back() : 0;
11359     const TopoDS_Face&  F = TopoDS::Face( getMeshDS()->IndexToShape( f2sd->first ));
11360     SMESH_subMesh*     sm = _mesh->GetSubMesh( F );
11361     SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
11362
11363     Handle(Geom_Surface) surface = BRep_Tool::Surface( F );
11364
11365     _shrunkFaces.Add( F );
11366     helper.SetSubShape( F );
11367
11368     // ==============================
11369     // Use periodicity to move nodes
11370     // ==============================
11371
11372     PeriodicFaces* periodic = _periodicity->GetPeriodic( F, _shrunkFaces );
11373     bool movedByPeriod = ( periodic && periodic->MoveNodes( F ));
11374
11375     // ===========================
11376     // Prepare data for shrinking
11377     // ===========================
11378
11379     // Collect nodes to smooth (they are marked at the beginning of this method)
11380     vector < const SMDS_MeshNode* > smoothNodes;
11381
11382     if ( !movedByPeriod )
11383     {
11384       SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
11385       while ( nIt->more() )
11386       {
11387         const SMDS_MeshNode* n = nIt->next();
11388         if ( n->isMarked() )
11389           smoothNodes.push_back( n );
11390       }
11391     }
11392     // Find out face orientation
11393     double refSign = 1;
11394     const set<TGeomID> ignoreShapes;
11395     bool isOkUV;
11396     if ( !smoothNodes.empty() )
11397     {
11398       vector<_Simplex> simplices;
11399       _Simplex::GetSimplices( smoothNodes[0], simplices, ignoreShapes );
11400       helper.GetNodeUV( F, simplices[0]._nPrev, 0, &isOkUV ); // fix UV of simplex nodes
11401       helper.GetNodeUV( F, simplices[0]._nNext, 0, &isOkUV );
11402       gp_XY uv = helper.GetNodeUV( F, smoothNodes[0], 0, &isOkUV );
11403       if ( !simplices[0].IsForward(uv, smoothNodes[0], F, helper, refSign ))
11404         refSign = -1;
11405     }
11406
11407     // Find _LayerEdge's inflated along F
11408     subEOS.clear();
11409     lEdges.clear();
11410     {
11411       SMESH_subMeshIteratorPtr subIt = sm->getDependsOnIterator(/*includeSelf=*/false,
11412                                                                 /*complexFirst=*/true); //!!!
11413       while ( subIt->more() )
11414       {
11415         const TGeomID subID = subIt->next()->GetId();
11416         if ( data._noShrinkShapes.count( subID ))
11417           continue;
11418         _EdgesOnShape* eos = data.GetShapeEdges( subID );
11419         if ( !eos || eos->_sWOL.IsNull() )
11420           if ( data2 ) // check in adjacent SOLID
11421           {
11422             eos = data2->GetShapeEdges( subID );
11423             if ( !eos || eos->_sWOL.IsNull() )
11424               continue;
11425           }
11426         subEOS.push_back( eos );
11427
11428         if ( !movedByPeriod )
11429           for ( size_t i = 0; i < eos->_edges.size(); ++i )
11430           {
11431             lEdges.push_back( eos->_edges[ i ] );
11432             prepareEdgeToShrink( *eos->_edges[ i ], *eos, helper, smDS );
11433           }
11434       }
11435     }
11436
11437     dumpFunction(SMESH_Comment("beforeShrinkFace")<<f2sd->first); // debug
11438     SMDS_ElemIteratorPtr fIt = smDS->GetElements();
11439     while ( fIt->more() )
11440       if ( const SMDS_MeshElement* f = fIt->next() )
11441         dumpChangeNodes( f );
11442     dumpFunctionEnd();
11443
11444     // Replace source nodes by target nodes in mesh faces to shrink
11445     dumpFunction(SMESH_Comment("replNodesOnFace")<<f2sd->first); // debug
11446     const SMDS_MeshNode* nodes[20];
11447     for ( size_t iS = 0; iS < subEOS.size(); ++iS )
11448     {
11449       _EdgesOnShape& eos = * subEOS[ iS ];
11450       for ( size_t i = 0; i < eos._edges.size(); ++i )
11451       {
11452         _LayerEdge& edge = *eos._edges[i];
11453         const SMDS_MeshNode* srcNode = edge._nodes[0];
11454         const SMDS_MeshNode* tgtNode = edge._nodes.back();
11455         SMDS_ElemIteratorPtr fIt = srcNode->GetInverseElementIterator(SMDSAbs_Face);
11456         while ( fIt->more() )
11457         {
11458           const SMDS_MeshElement* f = fIt->next();
11459           if ( !smDS->Contains( f ) || !f->isMarked() )
11460             continue;
11461           SMDS_NodeIteratorPtr nIt = f->nodeIterator();
11462           for ( int iN = 0; nIt->more(); ++iN )
11463           {
11464             const SMDS_MeshNode* n = nIt->next();
11465             nodes[iN] = ( n == srcNode ? tgtNode : n );
11466           }
11467           helper.GetMeshDS()->ChangeElementNodes( f, nodes, f->NbNodes() );
11468           dumpChangeNodes( f );
11469         }
11470       }
11471     }
11472     dumpFunctionEnd();
11473
11474     // find out if a FACE is concave
11475     const bool isConcaveFace = isConcave( F, helper );
11476
11477     // Create _SmoothNode's on face F
11478     vector< _SmoothNode > nodesToSmooth( smoothNodes.size() );
11479     {
11480       dumpFunction(SMESH_Comment("fixUVOnFace")<<f2sd->first); // debug
11481       const bool sortSimplices = isConcaveFace;
11482       for ( size_t i = 0; i < smoothNodes.size(); ++i )
11483       {
11484         const SMDS_MeshNode* n = smoothNodes[i];
11485         nodesToSmooth[ i ]._node = n;
11486         // src nodes must be already replaced by tgt nodes to have tgt nodes in _simplices
11487         _Simplex::GetSimplices( n, nodesToSmooth[ i ]._simplices, ignoreShapes, 0, sortSimplices);
11488         // fix up incorrect uv of nodes on the FACE
11489         helper.GetNodeUV( F, n, 0, &isOkUV);
11490         dumpMove( n );
11491       }
11492       dumpFunctionEnd();
11493     }
11494     //if ( nodesToSmooth.empty() ) continue;
11495
11496     // Find EDGE's to shrink and set simpices to LayerEdge's
11497     set< _Shrinker1D* > eShri1D;
11498     {
11499       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
11500       {
11501         _EdgesOnShape& eos = * subEOS[ iS ];
11502         if ( eos.SWOLType() == TopAbs_EDGE )
11503         {
11504           SMESH_subMesh* edgeSM = _mesh->GetSubMesh( eos._sWOL );
11505           VISCOUS_3D::ToClearSubWithMain( edgeSM, data._solid );
11506           if ( !movedByPeriod )
11507           {
11508             _Shrinker1D& shrinker = e2shrMap[ edgeSM->GetId() ];
11509             eShri1D.insert( & shrinker );
11510             shrinker.AddEdge( eos._edges[0], eos, helper );
11511             // restore params of nodes on EDGE if the EDGE has been already
11512             // shrunk while shrinking other FACE
11513             shrinker.RestoreParams();
11514           }
11515         }
11516         for ( size_t i = 0; i < eos._edges.size(); ++i )
11517         {
11518           _LayerEdge& edge = * eos._edges[i];
11519           _Simplex::GetSimplices( /*tgtNode=*/edge._nodes.back(), edge._simplices, ignoreShapes );
11520
11521           // additionally mark tgt node; only marked nodes will be used in SetNewLength2d()
11522           // not-marked nodes are those added by refine()
11523           edge._nodes.back()->setIsMarked( true );
11524         }
11525       }
11526     }
11527
11528     bool toFixTria = false; // to improve quality of trias by diagonal swap
11529     if ( isConcaveFace && !movedByPeriod )
11530     {
11531       const bool hasTria = _mesh->NbTriangles(), hasQuad = _mesh->NbQuadrangles();
11532       if ( hasTria != hasQuad ) {
11533         toFixTria = hasTria;
11534       }
11535       else {
11536         set<int> nbNodesSet;
11537         SMDS_ElemIteratorPtr fIt = smDS->GetElements();
11538         while ( fIt->more() && nbNodesSet.size() < 2 )
11539           nbNodesSet.insert( fIt->next()->NbCornerNodes() );
11540         toFixTria = ( *nbNodesSet.begin() == 3 );
11541       }
11542     }
11543
11544     // ==================
11545     // Perform shrinking
11546     // ==================
11547
11548     bool shrunk = !movedByPeriod;
11549     int nbBad, shriStep=0, smooStep=0;
11550     _SmoothNode::SmoothType smoothType
11551       = isConcaveFace ? _SmoothNode::ANGULAR : _SmoothNode::LAPLACIAN;
11552     SMESH_Comment errMsg;
11553     while ( shrunk )
11554     {
11555       shriStep++;
11556       // Move boundary nodes (actually just set new UV)
11557       // -----------------------------------------------
11558       dumpFunction(SMESH_Comment("moveBoundaryOnF")<<f2sd->first<<"_st"<<shriStep ); // debug
11559       shrunk = false;
11560       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
11561       {
11562         _EdgesOnShape& eos = * subEOS[ iS ];
11563         for ( size_t i = 0; i < eos._edges.size(); ++i )
11564         {
11565           shrunk |= eos._edges[i]->SetNewLength2d( surface, F, eos, helper );
11566         }
11567       }
11568       dumpFunctionEnd();
11569
11570       // Move nodes on EDGE's
11571       // (XYZ is set as soon as a needed length reached in SetNewLength2d())
11572       set< _Shrinker1D* >::iterator shr = eShri1D.begin();
11573       for ( ; shr != eShri1D.end(); ++shr )
11574         (*shr)->Compute( /*set3D=*/false, helper );
11575
11576       // Smoothing in 2D
11577       // -----------------
11578       int nbNoImpSteps = 0;
11579       bool       moved = true;
11580       nbBad = 1;
11581       while (( nbNoImpSteps < 5 && nbBad > 0) && moved)
11582       {
11583         dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
11584
11585         int oldBadNb = nbBad;
11586         nbBad = 0;
11587         moved = false;
11588         // '% 5' minimizes NB FUNCTIONS on viscous_layers_00/B2 case
11589         _SmoothNode::SmoothType smooTy = ( smooStep % 5 ) ? smoothType : _SmoothNode::LAPLACIAN;
11590         for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
11591         {
11592           moved |= nodesToSmooth[i].Smooth( nbBad, surface, helper, refSign,
11593                                             smooTy, /*set3D=*/isConcaveFace);
11594         }
11595         if ( nbBad < oldBadNb )
11596           nbNoImpSteps = 0;
11597         else
11598           nbNoImpSteps++;
11599
11600         dumpFunctionEnd();
11601       }
11602
11603       errMsg.clear();
11604       if ( nbBad > 0 )
11605         errMsg << "Can't shrink 2D mesh on face " << f2sd->first;
11606       if ( shriStep > 200 )
11607         errMsg << "Infinite loop at shrinking 2D mesh on face " << f2sd->first;
11608       if ( !errMsg.empty() )
11609         break;
11610
11611       // Fix narrow triangles by swapping diagonals
11612       // ---------------------------------------
11613       if ( toFixTria )
11614       {
11615         set<const SMDS_MeshNode*> usedNodes;
11616         fixBadFaces( F, helper, /*is2D=*/true, shriStep, & usedNodes); // swap diagonals
11617
11618         // update working data
11619         set<const SMDS_MeshNode*>::iterator n;
11620         for ( size_t i = 0; i < nodesToSmooth.size() && !usedNodes.empty(); ++i )
11621         {
11622           n = usedNodes.find( nodesToSmooth[ i ]._node );
11623           if ( n != usedNodes.end())
11624           {
11625             _Simplex::GetSimplices( nodesToSmooth[ i ]._node,
11626                                     nodesToSmooth[ i ]._simplices,
11627                                     ignoreShapes, NULL,
11628                                     /*sortSimplices=*/ smoothType == _SmoothNode::ANGULAR );
11629             usedNodes.erase( n );
11630           }
11631         }
11632         for ( size_t i = 0; i < lEdges.size() && !usedNodes.empty(); ++i )
11633         {
11634           n = usedNodes.find( /*tgtNode=*/ lEdges[i]->_nodes.back() );
11635           if ( n != usedNodes.end())
11636           {
11637             _Simplex::GetSimplices( lEdges[i]->_nodes.back(),
11638                                     lEdges[i]->_simplices,
11639                                     ignoreShapes );
11640             usedNodes.erase( n );
11641           }
11642         }
11643       }
11644       // TODO: check effect of this additional smooth
11645       // additional laplacian smooth to increase allowed shrink step
11646       // for ( int st = 1; st; --st )
11647       // {
11648       //   dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
11649       //   for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
11650       //   {
11651       //     nodesToSmooth[i].Smooth( nbBad,surface,helper,refSign,
11652       //                              _SmoothNode::LAPLACIAN,/*set3D=*/false);
11653       //   }
11654       // }
11655
11656     } // while ( shrunk )
11657
11658     if ( !errMsg.empty() ) // Try to re-compute the shrink FACE
11659     {
11660       debugMsg( "Re-compute FACE " << f2sd->first << " because " << errMsg );
11661
11662       // remove faces
11663       SMESHDS_SubMesh* psm = data._proxyMesh->getFaceSubM( F );
11664       {
11665         vector< const SMDS_MeshElement* > facesToRm;
11666         if ( psm )
11667         {
11668           facesToRm.reserve( psm->NbElements() );
11669           for ( SMDS_ElemIteratorPtr ite = psm->GetElements(); ite->more(); )
11670             facesToRm.push_back( ite->next() );
11671
11672           for ( size_t i = 0 ; i < _sdVec.size(); ++i )
11673             if (( psm = _sdVec[i]._proxyMesh->getFaceSubM( F )))
11674               psm->Clear();
11675         }
11676         for ( size_t i = 0; i < facesToRm.size(); ++i )
11677           getMeshDS()->RemoveFreeElement( facesToRm[i], smDS, /*fromGroups=*/false );
11678       }
11679       // remove nodes
11680       {
11681         TIDSortedNodeSet nodesToKeep; // nodes of _LayerEdge to keep
11682         for ( size_t iS = 0; iS < subEOS.size(); ++iS ) {
11683           for ( size_t i = 0; i < subEOS[iS]->_edges.size(); ++i )
11684             nodesToKeep.insert( ++( subEOS[iS]->_edges[i]->_nodes.begin() ),
11685                                 subEOS[iS]->_edges[i]->_nodes.end() );
11686         }
11687         SMDS_NodeIteratorPtr itn = smDS->GetNodes();
11688         while ( itn->more() ) {
11689           const SMDS_MeshNode* n = itn->next();
11690           if ( !nodesToKeep.count( n ))
11691             getMeshDS()->RemoveFreeNode( n, smDS, /*fromGroups=*/false );
11692         }
11693       }
11694       _periodicity->ClearPeriodic( F );
11695
11696       // restore position and UV of target nodes
11697       gp_Pnt p;
11698       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
11699         for ( size_t i = 0; i < subEOS[iS]->_edges.size(); ++i )
11700         {
11701           _LayerEdge*       edge = subEOS[iS]->_edges[i];
11702           SMDS_MeshNode* tgtNode = const_cast< SMDS_MeshNode*& >( edge->_nodes.back() );
11703           if ( edge->_pos.empty() ||
11704                edge->Is( _LayerEdge::SHRUNK )) continue;
11705           if ( subEOS[iS]->SWOLType() == TopAbs_FACE )
11706           {
11707             SMDS_FacePositionPtr pos = tgtNode->GetPosition();
11708             pos->SetUParameter( edge->_pos[0].X() );
11709             pos->SetVParameter( edge->_pos[0].Y() );
11710             p = surface->Value( edge->_pos[0].X(), edge->_pos[0].Y() );
11711           }
11712           else
11713           {
11714             SMDS_EdgePositionPtr pos = tgtNode->GetPosition();
11715             pos->SetUParameter( edge->_pos[0].Coord( U_TGT ));
11716             p = BRepAdaptor_Curve( TopoDS::Edge( subEOS[iS]->_sWOL )).Value( pos->GetUParameter() );
11717           }
11718           tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
11719           dumpMove( tgtNode );
11720         }
11721       // shrink EDGE sub-meshes and set proxy sub-meshes
11722       UVPtStructVec uvPtVec;
11723       set< _Shrinker1D* >::iterator shrIt = eShri1D.begin();
11724       for ( shrIt = eShri1D.begin(); shrIt != eShri1D.end(); ++shrIt )
11725       {
11726         _Shrinker1D* shr = (*shrIt);
11727         shr->Compute( /*set3D=*/true, helper );
11728
11729         // set proxy mesh of EDGEs w/o layers
11730         map< double, const SMDS_MeshNode* > nodes;
11731         SMESH_Algo::GetSortedNodesOnEdge( getMeshDS(), shr->GeomEdge(),/*skipMedium=*/true, nodes);
11732         // remove refinement nodes
11733         const SMDS_MeshNode* sn0 = shr->SrcNode(0), *sn1 = shr->SrcNode(1);
11734         const SMDS_MeshNode* tn0 = shr->TgtNode(0), *tn1 = shr->TgtNode(1);
11735         map< double, const SMDS_MeshNode* >::iterator u2n = nodes.begin();
11736         if ( u2n->second == sn0 || u2n->second == sn1 )
11737         {
11738           while ( u2n->second != tn0 && u2n->second != tn1 )
11739             ++u2n;
11740           nodes.erase( nodes.begin(), u2n );
11741         }
11742         u2n = --nodes.end();
11743         if ( u2n->second == sn0 || u2n->second == sn1 )
11744         {
11745           while ( u2n->second != tn0 && u2n->second != tn1 )
11746             --u2n;
11747           nodes.erase( ++u2n, nodes.end() );
11748         }
11749         // set proxy sub-mesh
11750         uvPtVec.resize( nodes.size() );
11751         u2n = nodes.begin();
11752         BRepAdaptor_Curve2d curve( shr->GeomEdge(), F );
11753         for ( size_t i = 0; i < nodes.size(); ++i, ++u2n )
11754         {
11755           uvPtVec[ i ].node = u2n->second;
11756           uvPtVec[ i ].param = u2n->first;
11757           uvPtVec[ i ].SetUV( curve.Value( u2n->first ).XY() );
11758         }
11759         StdMeshers_FaceSide fSide( uvPtVec, F, shr->GeomEdge(), _mesh );
11760         StdMeshers_ViscousLayers2D::SetProxyMeshOfEdge( fSide );
11761       }
11762
11763       // set proxy mesh of EDGEs with layers
11764       vector< _LayerEdge* > edges;
11765       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
11766       {
11767         _EdgesOnShape& eos = * subEOS[ iS ];
11768         if ( eos.ShapeType() != TopAbs_EDGE ) continue;
11769         if ( eos.size() == 0 )
11770           continue;
11771
11772         const TopoDS_Edge& E = TopoDS::Edge( eos._shape );
11773         data.SortOnEdge( E, eos._edges );
11774
11775         edges.clear();
11776         if ( _EdgesOnShape* eov = data.GetShapeEdges( helper.IthVertex( 0, E, /*CumOri=*/false )))
11777           if ( !eov->_edges.empty() )
11778             edges.push_back( eov->_edges[0] ); // on 1st VERTEX
11779
11780         edges.insert( edges.end(), eos._edges.begin(), eos._edges.end() );
11781
11782         if ( _EdgesOnShape* eov = data.GetShapeEdges( helper.IthVertex( 1, E, /*CumOri=*/false )))
11783           if ( !eov->_edges.empty() )
11784             edges.push_back( eov->_edges[0] ); // on last VERTEX
11785
11786         uvPtVec.resize( edges.size() );
11787         for ( size_t i = 0; i < edges.size(); ++i )
11788         {
11789           uvPtVec[ i ].node = edges[i]->_nodes.back();
11790           uvPtVec[ i ].param = helper.GetNodeU( E, edges[i]->_nodes[0] );
11791           uvPtVec[ i ].SetUV( helper.GetNodeUV( F, edges[i]->_nodes.back() ));
11792         }
11793         // if ( edges.empty() )
11794         //   continue;
11795         BRep_Tool::Range( E, uvPtVec[0].param, uvPtVec.back().param );
11796         StdMeshers_FaceSide fSide( uvPtVec, F, E, _mesh );
11797         StdMeshers_ViscousLayers2D::SetProxyMeshOfEdge( fSide );
11798       }
11799       // temporary clear the FACE sub-mesh from faces made by refine()
11800       vector< const SMDS_MeshElement* > elems;
11801       elems.reserve( smDS->NbElements() + smDS->NbNodes() );
11802       for ( SMDS_ElemIteratorPtr ite = smDS->GetElements(); ite->more(); )
11803         elems.push_back( ite->next() );
11804       for ( SMDS_NodeIteratorPtr ite = smDS->GetNodes(); ite->more(); )
11805         elems.push_back( ite->next() );
11806       smDS->Clear();
11807
11808       // compute the mesh on the FACE
11809       sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
11810       sm->ComputeStateEngine( SMESH_subMesh::COMPUTE_SUBMESH );
11811
11812       // re-fill proxy sub-meshes of the FACE
11813       for ( size_t i = 0 ; i < _sdVec.size(); ++i )
11814         if (( psm = _sdVec[i]._proxyMesh->getFaceSubM( F )))
11815           for ( SMDS_ElemIteratorPtr ite = smDS->GetElements(); ite->more(); )
11816             psm->AddElement( ite->next() );
11817
11818       // re-fill smDS
11819       for ( size_t i = 0; i < elems.size(); ++i )
11820         smDS->AddElement( elems[i] );
11821
11822       if ( sm->GetComputeState() != SMESH_subMesh::COMPUTE_OK )
11823         return error( errMsg );
11824
11825     } // end of re-meshing in case of failed smoothing
11826     else if ( !movedByPeriod )
11827     {
11828       // No wrongly shaped faces remain; final smooth. Set node XYZ.
11829       bool isStructuredFixed = false;
11830       if ( SMESH_2D_Algo* algo = dynamic_cast<SMESH_2D_Algo*>( sm->GetAlgo() ))
11831         isStructuredFixed = algo->FixInternalNodes( *data._proxyMesh, F );
11832       if ( !isStructuredFixed )
11833       {
11834         if ( isConcaveFace ) // fix narrow faces by swapping diagonals
11835           fixBadFaces( F, helper, /*is2D=*/false, ++shriStep );
11836
11837         for ( int st = 3; st; --st )
11838         {
11839           switch( st ) {
11840           case 1: smoothType = _SmoothNode::LAPLACIAN; break;
11841           case 2: smoothType = _SmoothNode::LAPLACIAN; break;
11842           case 3: smoothType = _SmoothNode::ANGULAR; break;
11843           }
11844           dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
11845           for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
11846           {
11847             nodesToSmooth[i].Smooth( nbBad,surface,helper,refSign,
11848                                      smoothType,/*set3D=*/st==1 );
11849           }
11850           dumpFunctionEnd();
11851         }
11852       }
11853       if ( !getMeshDS()->IsEmbeddedMode() )
11854         // Log node movement
11855         for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
11856         {
11857           SMESH_TNodeXYZ p ( nodesToSmooth[i]._node );
11858           getMeshDS()->MoveNode( nodesToSmooth[i]._node, p.X(), p.Y(), p.Z() );
11859         }
11860     }
11861
11862     // Set an event listener to clear FACE sub-mesh together with SOLID sub-mesh
11863     VISCOUS_3D::ToClearSubWithMain( sm, data._solid );
11864     if ( data2 )
11865       VISCOUS_3D::ToClearSubWithMain( sm, data2->_solid );
11866
11867   } // loop on FACES to shrink mesh on
11868
11869
11870   // Replace source nodes by target nodes in shrunk mesh edges
11871
11872   map< int, _Shrinker1D >::iterator e2shr = e2shrMap.begin();
11873   for ( ; e2shr != e2shrMap.end(); ++e2shr )
11874     e2shr->second.SwapSrcTgtNodes( getMeshDS() );
11875
11876   return true;
11877 }
11878
11879 //================================================================================
11880 /*!
11881  * \brief Computes 2d shrink direction and finds nodes limiting shrinking
11882  */
11883 //================================================================================
11884
11885 bool _ViscousBuilder::prepareEdgeToShrink( _LayerEdge&            edge,
11886                                            _EdgesOnShape&         eos,
11887                                            SMESH_MesherHelper&    helper,
11888                                            const SMESHDS_SubMesh* /*faceSubMesh*/)
11889 {
11890   const SMDS_MeshNode* srcNode = edge._nodes[0];
11891   const SMDS_MeshNode* tgtNode = edge._nodes.back();
11892
11893   if ( eos.SWOLType() == TopAbs_FACE )
11894   {
11895     if ( tgtNode->GetPosition()->GetDim() != 2 ) // not inflated edge
11896     {
11897       edge._pos.clear();
11898       edge.Set( _LayerEdge::SHRUNK );
11899       return srcNode == tgtNode;
11900     }
11901     gp_XY srcUV ( edge._pos[0].X(), edge._pos[0].Y() );          //helper.GetNodeUV( F, srcNode );
11902     gp_XY tgtUV = edge.LastUV( TopoDS::Face( eos._sWOL ), eos ); //helper.GetNodeUV( F, tgtNode );
11903     gp_Vec2d uvDir( srcUV, tgtUV );
11904     double uvLen = uvDir.Magnitude();
11905     uvDir /= uvLen;
11906     edge._normal.SetCoord( uvDir.X(),uvDir.Y(), 0 );
11907     edge._len = uvLen;
11908
11909     //edge._pos.resize(1);
11910     edge._pos[0].SetCoord( tgtUV.X(), tgtUV.Y(), 0 );
11911
11912     // set UV of source node to target node
11913     SMDS_FacePositionPtr pos = tgtNode->GetPosition();
11914     pos->SetUParameter( srcUV.X() );
11915     pos->SetVParameter( srcUV.Y() );
11916   }
11917   else // _sWOL is TopAbs_EDGE
11918   {
11919     if ( tgtNode->GetPosition()->GetDim() != 1 ) // not inflated edge
11920     {
11921       edge._pos.clear();
11922       edge.Set( _LayerEdge::SHRUNK );
11923       return srcNode == tgtNode;
11924     }
11925     const TopoDS_Edge&    E = TopoDS::Edge( eos._sWOL );
11926     SMESHDS_SubMesh* edgeSM = getMeshDS()->MeshElements( E );
11927     if ( !edgeSM || edgeSM->NbElements() == 0 )
11928       return error(SMESH_Comment("Not meshed EDGE ") << getMeshDS()->ShapeToIndex( E ));
11929
11930     const SMDS_MeshNode* n2 = 0;
11931     SMDS_ElemIteratorPtr eIt = srcNode->GetInverseElementIterator(SMDSAbs_Edge);
11932     while ( eIt->more() && !n2 )
11933     {
11934       const SMDS_MeshElement* e = eIt->next();
11935       if ( !edgeSM->Contains(e)) continue;
11936       n2 = e->GetNode( 0 );
11937       if ( n2 == srcNode ) n2 = e->GetNode( 1 );
11938     }
11939     if ( !n2 )
11940       return error(SMESH_Comment("Wrongly meshed EDGE ") << getMeshDS()->ShapeToIndex( E ));
11941
11942     if ( n2 == tgtNode       || // for 3D_mesh_GHS3D_01/B1
11943          n2 == edge._nodes[1] ) // bos #20643
11944     {
11945       // shrunk by other SOLID
11946       edge.Set( _LayerEdge::SHRUNK ); // ???
11947       return true;
11948     }
11949
11950     double uSrc = helper.GetNodeU( E, srcNode, n2 );
11951     double uTgt = helper.GetNodeU( E, tgtNode, srcNode );
11952     double u2   = helper.GetNodeU( E, n2,      srcNode );
11953
11954     //edge._pos.clear();
11955
11956     if ( fabs( uSrc-uTgt ) < 0.99 * fabs( uSrc-u2 ))
11957     {
11958       // tgtNode is located so that it does not make faces with wrong orientation
11959       edge.Set( _LayerEdge::SHRUNK );
11960       return true;
11961     }
11962     //edge._pos.resize(1);
11963     edge._pos[0].SetCoord( U_TGT, uTgt );
11964     edge._pos[0].SetCoord( U_SRC, uSrc );
11965     edge._pos[0].SetCoord( LEN_TGT, fabs( uSrc-uTgt ));
11966
11967     edge._simplices.resize( 1 );
11968     edge._simplices[0]._nPrev = n2;
11969
11970     // set U of source node to the target node
11971     SMDS_EdgePositionPtr pos = tgtNode->GetPosition();
11972     pos->SetUParameter( uSrc );
11973   }
11974   return true;
11975 }
11976
11977 //================================================================================
11978 /*!
11979  * \brief Restore position of a sole node of a _LayerEdge based on _noShrinkShapes
11980  */
11981 //================================================================================
11982
11983 void _ViscousBuilder::restoreNoShrink( _LayerEdge& edge ) const
11984 {
11985   if ( edge._nodes.size() == 1 )
11986   {
11987     edge._pos.clear();
11988     edge._len = 0;
11989
11990     const SMDS_MeshNode* srcNode = edge._nodes[0];
11991     TopoDS_Shape S = SMESH_MesherHelper::GetSubShapeByNode( srcNode, getMeshDS() );
11992     if ( S.IsNull() ) return;
11993
11994     gp_Pnt p;
11995
11996     switch ( S.ShapeType() )
11997     {
11998     case TopAbs_EDGE:
11999     {
12000       double f,l;
12001       TopLoc_Location loc;
12002       Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( S ), loc, f, l );
12003       if ( curve.IsNull() ) return;
12004       SMDS_EdgePositionPtr ePos = srcNode->GetPosition();
12005       p = curve->Value( ePos->GetUParameter() );
12006       break;
12007     }
12008     case TopAbs_VERTEX:
12009     {
12010       p = BRep_Tool::Pnt( TopoDS::Vertex( S ));
12011       break;
12012     }
12013     default: return;
12014     }
12015     getMeshDS()->MoveNode( srcNode, p.X(), p.Y(), p.Z() );
12016     dumpMove( srcNode );
12017   }
12018 }
12019
12020 //================================================================================
12021 /*!
12022  * \brief Try to fix triangles with high aspect ratio by swapping diagonals
12023  */
12024 //================================================================================
12025
12026 void _ViscousBuilder::fixBadFaces(const TopoDS_Face&          F,
12027                                   SMESH_MesherHelper&         helper,
12028                                   const bool                  is2D,
12029                                   const int                   step,
12030                                   set<const SMDS_MeshNode*> * involvedNodes)
12031 {
12032   SMESH::Controls::AspectRatio qualifier;
12033   SMESH::Controls::TSequenceOfXYZ points(3), points1(3), points2(3);
12034   const double maxAspectRatio = is2D ? 4. : 2;
12035   _NodeCoordHelper xyz( F, helper, is2D );
12036
12037   // find bad triangles
12038
12039   vector< const SMDS_MeshElement* > badTrias;
12040   vector< double >                  badAspects;
12041   SMESHDS_SubMesh*      sm = helper.GetMeshDS()->MeshElements( F );
12042   SMDS_ElemIteratorPtr fIt = sm->GetElements();
12043   while ( fIt->more() )
12044   {
12045     const SMDS_MeshElement * f = fIt->next();
12046     if ( f->NbCornerNodes() != 3 ) continue;
12047     for ( int iP = 0; iP < 3; ++iP ) points(iP+1) = xyz( f->GetNode(iP));
12048     double aspect = qualifier.GetValue( points );
12049     if ( aspect > maxAspectRatio )
12050     {
12051       badTrias.push_back( f );
12052       badAspects.push_back( aspect );
12053     }
12054   }
12055   if ( step == 1 )
12056   {
12057     dumpFunction(SMESH_Comment("beforeSwapDiagonals_F")<<helper.GetSubShapeID());
12058     SMDS_ElemIteratorPtr fIt = sm->GetElements();
12059     while ( fIt->more() )
12060     {
12061       const SMDS_MeshElement * f = fIt->next();
12062       if ( f->NbCornerNodes() == 3 )
12063         dumpChangeNodes( f );
12064     }
12065     dumpFunctionEnd();
12066   }
12067   if ( badTrias.empty() )
12068     return;
12069
12070   // find couples of faces to swap diagonal
12071
12072   typedef pair < const SMDS_MeshElement* , const SMDS_MeshElement* > T2Trias;
12073   vector< T2Trias > triaCouples; 
12074
12075   TIDSortedElemSet involvedFaces, emptySet;
12076   for ( size_t iTia = 0; iTia < badTrias.size(); ++iTia )
12077   {
12078     T2Trias trias    [3];
12079     double  aspRatio [3];
12080     int i1, i2, i3;
12081
12082     if ( !involvedFaces.insert( badTrias[iTia] ).second )
12083       continue;
12084     for ( int iP = 0; iP < 3; ++iP )
12085       points(iP+1) = xyz( badTrias[iTia]->GetNode(iP));
12086
12087     // find triangles adjacent to badTrias[iTia] with better aspect ratio after diag-swaping
12088     int bestCouple = -1;
12089     for ( int iSide = 0; iSide < 3; ++iSide )
12090     {
12091       const SMDS_MeshNode* n1 = badTrias[iTia]->GetNode( iSide );
12092       const SMDS_MeshNode* n2 = badTrias[iTia]->GetNode(( iSide+1 ) % 3 );
12093       trias [iSide].first  = badTrias[iTia];
12094       trias [iSide].second = SMESH_MeshAlgos::FindFaceInSet( n1, n2, emptySet, involvedFaces,
12095                                                              & i1, & i2 );
12096       if (( ! trias[iSide].second ) ||
12097           ( trias[iSide].second->NbCornerNodes() != 3 ) ||
12098           ( ! sm->Contains( trias[iSide].second )))
12099         continue;
12100
12101       // aspect ratio of an adjacent tria
12102       for ( int iP = 0; iP < 3; ++iP )
12103         points2(iP+1) = xyz( trias[iSide].second->GetNode(iP));
12104       double aspectInit = qualifier.GetValue( points2 );
12105
12106       // arrange nodes as after diag-swaping
12107       if ( helper.WrapIndex( i1+1, 3 ) == i2 )
12108         i3 = helper.WrapIndex( i1-1, 3 );
12109       else
12110         i3 = helper.WrapIndex( i1+1, 3 );
12111       points1 = points;
12112       points1( 1+ iSide ) = points2( 1+ i3 );
12113       points2( 1+ i2    ) = points1( 1+ ( iSide+2 ) % 3 );
12114
12115       // aspect ratio after diag-swaping
12116       aspRatio[ iSide ] = qualifier.GetValue( points1 ) + qualifier.GetValue( points2 );
12117       if ( aspRatio[ iSide ] > aspectInit + badAspects[ iTia ] )
12118         continue;
12119
12120       // prevent inversion of a triangle
12121       gp_Vec norm1 = gp_Vec( points1(1), points1(3) ) ^ gp_Vec( points1(1), points1(2) );
12122       gp_Vec norm2 = gp_Vec( points2(1), points2(3) ) ^ gp_Vec( points2(1), points2(2) );
12123       if ( norm1 * norm2 < 0. && norm1.Angle( norm2 ) > 70./180.*M_PI )
12124         continue;
12125
12126       if ( bestCouple < 0 || aspRatio[ bestCouple ] > aspRatio[ iSide ] )
12127         bestCouple = iSide;
12128     }
12129
12130     if ( bestCouple >= 0 )
12131     {
12132       triaCouples.push_back( trias[bestCouple] );
12133       involvedFaces.insert ( trias[bestCouple].second );
12134     }
12135     else
12136     {
12137       involvedFaces.erase( badTrias[iTia] );
12138     }
12139   }
12140   if ( triaCouples.empty() )
12141     return;
12142
12143   // swap diagonals
12144
12145   SMESH_MeshEditor editor( helper.GetMesh() );
12146   dumpFunction(SMESH_Comment("beforeSwapDiagonals_F")<<helper.GetSubShapeID()<<"_"<<step);
12147   for ( size_t i = 0; i < triaCouples.size(); ++i )
12148   {
12149     dumpChangeNodes( triaCouples[i].first );
12150     dumpChangeNodes( triaCouples[i].second );
12151     editor.InverseDiag( triaCouples[i].first, triaCouples[i].second );
12152   }
12153
12154   if ( involvedNodes )
12155     for ( size_t i = 0; i < triaCouples.size(); ++i )
12156     {
12157       involvedNodes->insert( triaCouples[i].first->begin_nodes(),
12158                              triaCouples[i].first->end_nodes() );
12159       involvedNodes->insert( triaCouples[i].second->begin_nodes(),
12160                              triaCouples[i].second->end_nodes() );
12161     }
12162
12163   // just for debug dump resulting triangles
12164   dumpFunction(SMESH_Comment("swapDiagonals_F")<<helper.GetSubShapeID()<<"_"<<step);
12165   for ( size_t i = 0; i < triaCouples.size(); ++i )
12166   {
12167     dumpChangeNodes( triaCouples[i].first );
12168     dumpChangeNodes( triaCouples[i].second );
12169   }
12170 }
12171
12172 //================================================================================
12173 /*!
12174  * \brief Move target node to it's final position on the FACE during shrinking
12175  */
12176 //================================================================================
12177
12178 bool _LayerEdge::SetNewLength2d( Handle(Geom_Surface)& surface,
12179                                  const TopoDS_Face&    F,
12180                                  _EdgesOnShape&        eos,
12181                                  SMESH_MesherHelper&   helper )
12182 {
12183   if ( Is( SHRUNK ))
12184     return false; // already at the target position
12185
12186   SMDS_MeshNode* tgtNode = const_cast< SMDS_MeshNode*& >( _nodes.back() );
12187
12188   if ( eos.SWOLType() == TopAbs_FACE )
12189   {
12190     gp_XY    curUV = helper.GetNodeUV( F, tgtNode );
12191     gp_Pnt2d tgtUV( _pos[0].X(), _pos[0].Y() );
12192     gp_Vec2d uvDir( _normal.X(), _normal.Y() );
12193     const double uvLen = tgtUV.Distance( curUV );
12194     const double kSafe = Max( 0.5, 1. - 0.1 * _simplices.size() );
12195
12196     // Select shrinking step such that not to make faces with wrong orientation.
12197     double stepSize = 1e100;
12198     for ( size_t i = 0; i < _simplices.size(); ++i )
12199     {
12200       if ( !_simplices[i]._nPrev->isMarked() ||
12201            !_simplices[i]._nNext->isMarked() )
12202         continue; // simplex of quadrangle created by addBoundaryElements()
12203
12204       // find intersection of 2 lines: curUV-tgtUV and that connecting simplex nodes
12205       gp_XY uvN1 = helper.GetNodeUV( F, _simplices[i]._nPrev, tgtNode );
12206       gp_XY uvN2 = helper.GetNodeUV( F, _simplices[i]._nNext, tgtNode );
12207       gp_XY dirN = uvN2 - uvN1;
12208       double det = uvDir.Crossed( dirN );
12209       if ( Abs( det )  < std::numeric_limits<double>::min() ) continue;
12210       gp_XY dirN2Cur = curUV - uvN1;
12211       double step = dirN.Crossed( dirN2Cur ) / det;
12212       if ( step > 0 )
12213         stepSize = Min( step, stepSize );
12214     }
12215     gp_Pnt2d newUV;
12216     if ( uvLen <= stepSize )
12217     {
12218       newUV = tgtUV;
12219       Set( SHRUNK );
12220       //_pos.clear();
12221     }
12222     else if ( stepSize > 0 )
12223     {
12224       newUV = curUV + uvDir.XY() * stepSize * kSafe;
12225     }
12226     else
12227     {
12228       return true;
12229     }
12230     SMDS_FacePositionPtr pos = tgtNode->GetPosition();
12231     pos->SetUParameter( newUV.X() );
12232     pos->SetVParameter( newUV.Y() );
12233
12234 #ifdef __myDEBUG
12235     gp_Pnt p = surface->Value( newUV.X(), newUV.Y() );
12236     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
12237     dumpMove( tgtNode );
12238 #else
12239     if ( surface.IsNull() ) {}
12240 #endif
12241   }
12242   else // _sWOL is TopAbs_EDGE
12243   {
12244     const TopoDS_Edge&      E = TopoDS::Edge( eos._sWOL );
12245     const SMDS_MeshNode*   n2 = _simplices[0]._nPrev;
12246     SMDS_EdgePositionPtr tgtPos = tgtNode->GetPosition();
12247
12248     const double u2     = helper.GetNodeU( E, n2, tgtNode );
12249     const double uSrc   = _pos[0].Coord( U_SRC );
12250     const double lenTgt = _pos[0].Coord( LEN_TGT );
12251
12252     double newU = _pos[0].Coord( U_TGT );
12253     if ( lenTgt < 0.99 * fabs( uSrc-u2 )) // n2 got out of src-tgt range
12254     {
12255       Set( _LayerEdge::SHRUNK );
12256       //_pos.clear();
12257     }
12258     else
12259     {
12260       newU = 0.1 * tgtPos->GetUParameter() + 0.9 * u2;
12261     }
12262     tgtPos->SetUParameter( newU );
12263 #ifdef __myDEBUG
12264     gp_XY newUV = helper.GetNodeUV( F, tgtNode, _nodes[0]);
12265     gp_Pnt p = surface->Value( newUV.X(), newUV.Y() );
12266     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
12267     dumpMove( tgtNode );
12268 #endif
12269   }
12270
12271   return true;
12272 }
12273
12274 //================================================================================
12275 /*!
12276  * \brief Perform smooth on the FACE
12277  *  \retval bool - true if the node has been moved
12278  */
12279 //================================================================================
12280
12281 bool _SmoothNode::Smooth(int&                  nbBad,
12282                          Handle(Geom_Surface)& surface,
12283                          SMESH_MesherHelper&   helper,
12284                          const double          refSign,
12285                          SmoothType            how,
12286                          bool                  set3D)
12287 {
12288   const TopoDS_Face& face = TopoDS::Face( helper.GetSubShape() );
12289
12290   // get uv of surrounding nodes
12291   vector<gp_XY> uv( _simplices.size() );
12292   for ( size_t i = 0; i < _simplices.size(); ++i )
12293     uv[i] = helper.GetNodeUV( face, _simplices[i]._nPrev, _node );
12294
12295   // compute new UV for the node
12296   gp_XY newPos (0,0);
12297   if ( how == TFI && _simplices.size() == 4 )
12298   {
12299     gp_XY corners[4];
12300     for ( size_t i = 0; i < _simplices.size(); ++i )
12301       if ( _simplices[i]._nOpp )
12302         corners[i] = helper.GetNodeUV( face, _simplices[i]._nOpp, _node );
12303       else
12304         throw SALOME_Exception(LOCALIZED("TFI smoothing: _Simplex::_nOpp not set!"));
12305
12306     newPos = helper.calcTFI ( 0.5, 0.5,
12307                               corners[0], corners[1], corners[2], corners[3],
12308                               uv[1], uv[2], uv[3], uv[0] );
12309   }
12310   else if ( how == ANGULAR )
12311   {
12312     newPos = computeAngularPos( uv, helper.GetNodeUV( face, _node ), refSign );
12313   }
12314   else if ( how == CENTROIDAL && _simplices.size() > 3 )
12315   {
12316     // average centers of diagonals wieghted with their reciprocal lengths
12317     if ( _simplices.size() == 4 )
12318     {
12319       double w1 = 1. / ( uv[2]-uv[0] ).SquareModulus();
12320       double w2 = 1. / ( uv[3]-uv[1] ).SquareModulus();
12321       newPos = ( w1 * ( uv[2]+uv[0] ) + w2 * ( uv[3]+uv[1] )) / ( w1+w2 ) / 2;
12322     }
12323     else
12324     {
12325       double sumWeight = 0;
12326       int nb = _simplices.size() == 4 ? 2 : _simplices.size();
12327       for ( int i = 0; i < nb; ++i )
12328       {
12329         int iFrom = i + 2;
12330         int iTo   = i + _simplices.size() - 1;
12331         for ( int j = iFrom; j < iTo; ++j )
12332         {
12333           int i2 = SMESH_MesherHelper::WrapIndex( j, _simplices.size() );
12334           double w = 1. / ( uv[i]-uv[i2] ).SquareModulus();
12335           sumWeight += w;
12336           newPos += w * ( uv[i]+uv[i2] );
12337         }
12338       }
12339       newPos /= 2 * sumWeight; // 2 is to get a middle between uv's
12340     }
12341   }
12342   else
12343   {
12344     // Laplacian smooth
12345     for ( size_t i = 0; i < _simplices.size(); ++i )
12346       newPos += uv[i];
12347     newPos /= _simplices.size();
12348   }
12349
12350   // count quality metrics (orientation) of triangles around the node
12351   int nbOkBefore = 0;
12352   gp_XY tgtUV = helper.GetNodeUV( face, _node );
12353   for ( size_t i = 0; i < _simplices.size(); ++i )
12354     nbOkBefore += _simplices[i].IsForward( tgtUV, _node, face, helper, refSign );
12355
12356   int nbOkAfter = 0;
12357   for ( size_t i = 0; i < _simplices.size(); ++i )
12358     nbOkAfter += _simplices[i].IsForward( newPos, _node, face, helper, refSign );
12359
12360   if ( nbOkAfter < nbOkBefore )
12361   {
12362     nbBad += _simplices.size() - nbOkBefore;
12363     return false;
12364   }
12365
12366   SMDS_FacePositionPtr pos = _node->GetPosition();
12367   pos->SetUParameter( newPos.X() );
12368   pos->SetVParameter( newPos.Y() );
12369
12370 #ifdef __myDEBUG
12371   set3D = true;
12372 #endif
12373   if ( set3D )
12374   {
12375     gp_Pnt p = surface->Value( newPos.X(), newPos.Y() );
12376     const_cast< SMDS_MeshNode* >( _node )->setXYZ( p.X(), p.Y(), p.Z() );
12377     dumpMove( _node );
12378   }
12379
12380   nbBad += _simplices.size() - nbOkAfter;
12381   return ( (tgtUV-newPos).SquareModulus() > 1e-10 );
12382 }
12383
12384 //================================================================================
12385 /*!
12386  * \brief Computes new UV using angle based smoothing technique
12387  */
12388 //================================================================================
12389
12390 gp_XY _SmoothNode::computeAngularPos(vector<gp_XY>& uv,
12391                                      const gp_XY&   uvToFix,
12392                                      const double   refSign)
12393 {
12394   uv.push_back( uv.front() );
12395
12396   vector< gp_XY >  edgeDir ( uv.size() );
12397   vector< double > edgeSize( uv.size() );
12398   for ( size_t i = 1; i < edgeDir.size(); ++i )
12399   {
12400     edgeDir [i-1] = uv[i] - uv[i-1];
12401     edgeSize[i-1] = edgeDir[i-1].Modulus();
12402     if ( edgeSize[i-1] < numeric_limits<double>::min() )
12403       edgeDir[i-1].SetX( 100 );
12404     else
12405       edgeDir[i-1] /= edgeSize[i-1] * refSign;
12406   }
12407   edgeDir.back()  = edgeDir.front();
12408   edgeSize.back() = edgeSize.front();
12409
12410   gp_XY  newPos(0,0);
12411   //int    nbEdges = 0;
12412   double sumSize = 0;
12413   for ( size_t i = 1; i < edgeDir.size(); ++i )
12414   {
12415     if ( edgeDir[i-1].X() > 1. ) continue;
12416     int i1 = i-1;
12417     while ( edgeDir[i].X() > 1. && ++i < edgeDir.size() );
12418     if ( i == edgeDir.size() ) break;
12419     gp_XY p = uv[i];
12420     gp_XY norm1( -edgeDir[i1].Y(), edgeDir[i1].X() );
12421     gp_XY norm2( -edgeDir[i].Y(),  edgeDir[i].X() );
12422     gp_XY bisec = norm1 + norm2;
12423     double bisecSize = bisec.Modulus();
12424     if ( bisecSize < numeric_limits<double>::min() )
12425     {
12426       bisec = -edgeDir[i1] + edgeDir[i];
12427       bisecSize = bisec.Modulus();
12428     }
12429     bisec /= bisecSize;
12430
12431     gp_XY  dirToN  = uvToFix - p;
12432     double distToN = dirToN.Modulus();
12433     if ( bisec * dirToN < 0 )
12434       distToN = -distToN;
12435
12436     newPos += ( p + bisec * distToN ) * ( edgeSize[i1] + edgeSize[i] );
12437     //++nbEdges;
12438     sumSize += edgeSize[i1] + edgeSize[i];
12439   }
12440   newPos /= /*nbEdges * */sumSize;
12441   return newPos;
12442 }
12443
12444 //================================================================================
12445 /*!
12446  * \brief Keep a _LayerEdge inflated along the EDGE
12447  */
12448 //================================================================================
12449
12450 void _Shrinker1D::AddEdge( const _LayerEdge*   e,
12451                            _EdgesOnShape&      eos,
12452                            SMESH_MesherHelper& helper )
12453 {
12454   // init
12455   if ( _nodes.empty() )
12456   {
12457     _edges[0] = _edges[1] = 0;
12458     _done = false;
12459   }
12460   // check _LayerEdge
12461   if ( e == _edges[0] || e == _edges[1] || e->_nodes.size() < 2 )
12462     return;
12463   if ( eos.SWOLType() != TopAbs_EDGE )
12464     throw SALOME_Exception(LOCALIZED("Wrong _LayerEdge is added"));
12465   if ( _edges[0] && !_geomEdge.IsSame( eos._sWOL ))
12466     throw SALOME_Exception(LOCALIZED("Wrong _LayerEdge is added"));
12467
12468   // store _LayerEdge
12469   _geomEdge = TopoDS::Edge( eos._sWOL );
12470   double f,l;
12471   BRep_Tool::Range( _geomEdge, f,l );
12472   double u = helper.GetNodeU( _geomEdge, e->_nodes[0], e->_nodes.back());
12473   _edges[ u < 0.5*(f+l) ? 0 : 1 ] = e;
12474
12475   // Check if the nodes are already shrunk by another SOLID
12476
12477   const SMDS_MeshNode* tgtNode0 = TgtNode( 0 );
12478   const SMDS_MeshNode* tgtNode1 = TgtNode( 1 );
12479
12480   _done = (( tgtNode0 && tgtNode0->NbInverseElements( SMDSAbs_Edge ) == 2 ) ||
12481            ( tgtNode1 && tgtNode1->NbInverseElements( SMDSAbs_Edge ) == 2 ));
12482   if ( _done )
12483     _nodes.resize( 1, nullptr );
12484
12485   // Update _nodes
12486
12487   if ( _nodes.empty() )
12488   {
12489     SMESHDS_SubMesh * eSubMesh = helper.GetMeshDS()->MeshElements( _geomEdge );
12490     if ( !eSubMesh || eSubMesh->NbNodes() < 1 )
12491       return;
12492     TopLoc_Location loc;
12493     Handle(Geom_Curve) C = BRep_Tool::Curve( _geomEdge, loc, f,l );
12494     GeomAdaptor_Curve aCurve(C, f,l);
12495     const double totLen = GCPnts_AbscissaPoint::Length(aCurve, f, l);
12496
12497     smIdType nbExpectNodes = eSubMesh->NbNodes();
12498     _initU  .reserve( nbExpectNodes );
12499     _normPar.reserve( nbExpectNodes );
12500     _nodes  .reserve( nbExpectNodes );
12501     SMDS_NodeIteratorPtr nIt = eSubMesh->GetNodes();
12502     while ( nIt->more() )
12503     {
12504       const SMDS_MeshNode* node = nIt->next();
12505
12506       // skip refinement nodes
12507       if ( node->NbInverseElements(SMDSAbs_Edge) == 0 ||
12508            node == tgtNode0 || node == tgtNode1 )
12509         continue;
12510       bool hasMarkedFace = false;
12511       SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
12512       while ( fIt->more() && !hasMarkedFace )
12513         hasMarkedFace = fIt->next()->isMarked();
12514       if ( !hasMarkedFace )
12515         continue;
12516
12517       _nodes.push_back( node );
12518       _initU.push_back( helper.GetNodeU( _geomEdge, node ));
12519       double len = GCPnts_AbscissaPoint::Length(aCurve, f, _initU.back());
12520       _normPar.push_back(  len / totLen );
12521     }
12522   }
12523   else
12524   {
12525     // remove target node of the _LayerEdge from _nodes
12526     size_t nbFound = 0;
12527     for ( size_t i = 0; i < _nodes.size(); ++i )
12528       if ( !_nodes[i] || _nodes[i] == tgtNode0 || _nodes[i] == tgtNode1 )
12529         _nodes[i] = 0, nbFound++;
12530     if ( nbFound == _nodes.size() )
12531       _nodes.clear();
12532   }
12533 }
12534
12535 //================================================================================
12536 /*!
12537  * \brief Move nodes on EDGE from ends where _LayerEdge's are inflated
12538  */
12539 //================================================================================
12540
12541 void _Shrinker1D::Compute(bool set3D, SMESH_MesherHelper& helper)
12542 {
12543   if ( _done || _nodes.empty())
12544     return;
12545   const _LayerEdge* e = _edges[0];
12546   if ( !e ) e = _edges[1];
12547   if ( !e ) return;
12548
12549   _done =  (( !_edges[0] || _edges[0]->Is( _LayerEdge::SHRUNK )) &&
12550             ( !_edges[1] || _edges[1]->Is( _LayerEdge::SHRUNK )));
12551
12552   double f,l;
12553   if ( set3D || _done )
12554   {
12555     dumpFunction(SMESH_Comment("shrink1D_E") << helper.GetMeshDS()->ShapeToIndex( _geomEdge )<<
12556                  "_F" << helper.GetSubShapeID() );
12557     Handle(Geom_Curve) C = BRep_Tool::Curve(_geomEdge, f,l);
12558     GeomAdaptor_Curve aCurve(C, f,l);
12559
12560     if ( _edges[0] )
12561       f = helper.GetNodeU( _geomEdge, _edges[0]->_nodes.back(), _nodes[0] );
12562     if ( _edges[1] )
12563       l = helper.GetNodeU( _geomEdge, _edges[1]->_nodes.back(), _nodes.back() );
12564     double totLen = GCPnts_AbscissaPoint::Length( aCurve, f, l );
12565
12566     for ( size_t i = 0; i < _nodes.size(); ++i )
12567     {
12568       if ( !_nodes[i] ) continue;
12569       double len = totLen * _normPar[i];
12570       GCPnts_AbscissaPoint discret( aCurve, len, f );
12571       if ( !discret.IsDone() )
12572         return throw SALOME_Exception(LOCALIZED("GCPnts_AbscissaPoint failed"));
12573       double u = discret.Parameter();
12574       SMDS_EdgePositionPtr pos = _nodes[i]->GetPosition();
12575       pos->SetUParameter( u );
12576       gp_Pnt p = C->Value( u );
12577       const_cast< SMDS_MeshNode*>( _nodes[i] )->setXYZ( p.X(), p.Y(), p.Z() );
12578       dumpMove( _nodes[i] );
12579     }
12580     dumpFunctionEnd();
12581   }
12582   else
12583   {
12584     BRep_Tool::Range( _geomEdge, f,l );
12585     if ( _edges[0] )
12586       f = helper.GetNodeU( _geomEdge, _edges[0]->_nodes.back(), _nodes[0] );
12587     if ( _edges[1] )
12588       l = helper.GetNodeU( _geomEdge, _edges[1]->_nodes.back(), _nodes.back() );
12589     
12590     for ( size_t i = 0; i < _nodes.size(); ++i )
12591     {
12592       if ( !_nodes[i] ) continue;
12593       double u = f * ( 1-_normPar[i] ) + l * _normPar[i];
12594       SMDS_EdgePositionPtr pos = _nodes[i]->GetPosition();
12595       pos->SetUParameter( u );
12596     }
12597   }
12598 }
12599
12600 //================================================================================
12601 /*!
12602  * \brief Restore initial parameters of nodes on EDGE
12603  */
12604 //================================================================================
12605
12606 void _Shrinker1D::RestoreParams()
12607 {
12608   if ( _done )
12609     for ( size_t i = 0; i < _nodes.size(); ++i )
12610     {
12611       if ( !_nodes[i] ) continue;
12612       SMDS_EdgePositionPtr pos = _nodes[i]->GetPosition();
12613       pos->SetUParameter( _initU[i] );
12614     }
12615   _done = false;
12616 }
12617
12618 //================================================================================
12619 /*!
12620  * \brief Replace source nodes by target nodes in shrunk mesh edges
12621  */
12622 //================================================================================
12623
12624 void _Shrinker1D::SwapSrcTgtNodes( SMESHDS_Mesh* mesh )
12625 {
12626   const SMDS_MeshNode* nodes[3];
12627   for ( int i = 0; i < 2; ++i )
12628   {
12629     if ( !_edges[i] ) continue;
12630
12631     SMESHDS_SubMesh * eSubMesh = mesh->MeshElements( _geomEdge );
12632     if ( !eSubMesh ) return;
12633     const SMDS_MeshNode* srcNode = _edges[i]->_nodes[0];
12634     const SMDS_MeshNode* tgtNode = _edges[i]->_nodes.back();
12635     const SMDS_MeshNode* scdNode = _edges[i]->_nodes[1];
12636     SMDS_ElemIteratorPtr eIt = srcNode->GetInverseElementIterator(SMDSAbs_Edge);
12637     while ( eIt->more() )
12638     {
12639       const SMDS_MeshElement* e = eIt->next();
12640       if ( !eSubMesh->Contains( e ) || e->GetNodeIndex( scdNode ) >= 0 )
12641           continue;
12642       SMDS_ElemIteratorPtr nIt = e->nodesIterator();
12643       for ( int iN = 0; iN < e->NbNodes(); ++iN )
12644       {
12645         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
12646         nodes[iN] = ( n == srcNode ? tgtNode : n );
12647       }
12648       mesh->ChangeElementNodes( e, nodes, e->NbNodes() );
12649     }
12650   }
12651 }
12652
12653 //================================================================================
12654 /*!
12655  * \brief Setup quadPoints
12656  */
12657 //================================================================================
12658
12659 _Mapper2D::_Mapper2D( const TParam2ColumnMap & param2ColumnMap, const TNode2Edge& n2eMap )
12660 {
12661   size_t i, iSize = _quadPoints.iSize = param2ColumnMap.size();
12662   size_t j, jSize = _quadPoints.jSize = param2ColumnMap.begin()->second.size();
12663   if ( _quadPoints.iSize < 3 ||
12664        _quadPoints.jSize < 3 )
12665     return;
12666   _quadPoints.uv_grid.resize( iSize * jSize );
12667
12668   // set nodes
12669   i = 0;
12670   for ( auto & u_columnNodes : param2ColumnMap )
12671   {
12672     for ( j = 0; j < u_columnNodes.second.size(); ++j )
12673       _quadPoints.UVPt( i, j ).node = u_columnNodes.second[ j ];
12674     ++i;
12675   }
12676
12677   // compute x parameter on borders
12678   uvPnt( 0, 0       ).x = 0;
12679   uvPnt( 0, jSize-1 ).x = 0;
12680   gp_Pnt p0, pPrev0 = SMESH_NodeXYZ( uvPnt( 0, 0       ).node );
12681   gp_Pnt p1, pPrev1 = SMESH_NodeXYZ( uvPnt( 0, jSize-1 ).node );
12682   for ( i = 1; i < iSize; ++i )
12683   {
12684     p0 = SMESH_NodeXYZ( uvPnt( i, 0       ).node );
12685     p1 = SMESH_NodeXYZ( uvPnt( i, jSize-1 ).node );
12686     uvPnt( i, 0       ).x = uvPnt( i-1, 0       ).x + p0.Distance( pPrev0 );
12687     uvPnt( i, jSize-1 ).x = uvPnt( i-1, jSize-1 ).x + p1.Distance( pPrev1 );
12688     pPrev0 = p0;
12689     pPrev1 = p1;
12690   }
12691   for ( i = 1; i < iSize-1; ++i )
12692   {
12693     uvPnt( i, 0       ).x /= uvPnt( iSize-1, 0       ).x;
12694     uvPnt( i, jSize-1 ).x /= uvPnt( iSize-1, jSize-1 ).x;
12695     uvPnt( i, 0       ).y = 0;
12696     uvPnt( i, jSize-1 ).y = 1;
12697   }
12698
12699   // compute y parameter on borders
12700   uvPnt( 0,       0 ).y = 0;
12701   uvPnt( iSize-1, 0 ).y = 0;
12702   pPrev0 = SMESH_NodeXYZ( uvPnt( 0,       0 ).node );
12703   pPrev1 = SMESH_NodeXYZ( uvPnt( iSize-1, 0 ).node );
12704   for ( j = 1; j < jSize; ++j )
12705   {
12706     p0 = SMESH_NodeXYZ( uvPnt( 0,       j ).node );
12707     p1 = SMESH_NodeXYZ( uvPnt( iSize-1, j ).node );
12708     uvPnt( 0,       j ).y = uvPnt( 0,       j-1 ).y + p0.Distance( pPrev0 );
12709     uvPnt( iSize-1, j ).y = uvPnt( iSize-1, j-1 ).y + p1.Distance( pPrev1 );
12710     pPrev0 = p0;
12711     pPrev1 = p1;
12712   }
12713   for ( j = 1; j < jSize-1; ++j )
12714   {
12715     uvPnt( 0,       j ).y /= uvPnt( 0,       jSize-1 ).y;
12716     uvPnt( iSize-1, j ).y /= uvPnt( iSize-1, jSize-1 ).y;
12717     uvPnt( 0,       j ).x = 0;
12718     uvPnt( iSize-1, j ).x = 1;
12719   }
12720
12721   // compute xy of internal nodes
12722   for ( i = 1; i < iSize-1; ++i )
12723   {
12724     const double x0 = uvPnt( i, 0       ).x;
12725     const double x1 = uvPnt( i, jSize-1 ).x;
12726     for ( j = 1; j < jSize-1; ++j )
12727     {
12728       const double y0 = uvPnt( 0,       j ).y;
12729       const double y1 = uvPnt( iSize-1, j ).y;
12730       double x = (x0 + y0 * (x1 - x0)) / (1 - (y1 - y0) * (x1 - x0));
12731       double y = y0 + x * (y1 - y0);
12732       uvPnt( i, j ).x = x;
12733       uvPnt( i, j ).y = y;
12734     }
12735   }
12736
12737   // replace base nodes with target ones
12738   for ( i = 0; i < iSize; ++i )
12739     for ( j = 0; j < jSize; ++j )
12740     {
12741       auto n2e = n2eMap.find( uvPnt( i, j ).node );
12742       uvPnt( i, j ).node = n2e->second->_nodes.back();
12743     }
12744
12745   return;
12746 }
12747
12748 //================================================================================
12749 /*!
12750  * \brief Compute positions of nodes of 2D structured mesh using TFI
12751  */
12752 //================================================================================
12753
12754 bool _Mapper2D::ComputeNodePositions()
12755 {
12756   if ( _quadPoints.uv_grid.empty() )
12757     return true;
12758
12759   size_t i, iSize = _quadPoints.iSize;
12760   size_t j, jSize = _quadPoints.jSize;
12761
12762   SMESH_NodeXYZ a0 ( uvPnt( 0,       0       ).node );
12763   SMESH_NodeXYZ a1 ( uvPnt( iSize-1, 0       ).node );
12764   SMESH_NodeXYZ a2 ( uvPnt( iSize-1, jSize-1 ).node );
12765   SMESH_NodeXYZ a3 ( uvPnt( 0,       jSize-1 ).node );
12766
12767   for ( i = 1; i < iSize-1; ++i )
12768   {
12769     SMESH_NodeXYZ p0 ( uvPnt( i, 0       ).node );
12770     SMESH_NodeXYZ p2 ( uvPnt( i, jSize-1 ).node );
12771     for ( j = 1; j < jSize-1; ++j )
12772     {
12773       SMESH_NodeXYZ p1 ( uvPnt( iSize-1, j ).node );
12774       SMESH_NodeXYZ p3 ( uvPnt( 0,       j ).node );
12775       double x = uvPnt( i, j ).x;
12776       double y = uvPnt( i, j ).y;
12777
12778       gp_XYZ p = SMESH_MesherHelper::calcTFI( x, y, a0,a1,a2,a3, p0,p1,p2,p3 );
12779       const_cast< SMDS_MeshNode* >( uvPnt( i, j ).node )->setXYZ( p.X(), p.Y(), p.Z() );
12780
12781       dumpMove( uvPnt( i, j ).node );
12782     }
12783   }
12784   return true;
12785 }
12786
12787 //================================================================================
12788 /*!
12789  * \brief Creates 2D and 1D elements on boundaries of new prisms
12790  */
12791 //================================================================================
12792
12793 bool _ViscousBuilder::addBoundaryElements(_SolidData& data)
12794 {
12795   SMESH_MesherHelper helper( *_mesh );
12796
12797   vector< const SMDS_MeshNode* > faceNodes;
12798
12799   //for ( size_t i = 0; i < _sdVec.size(); ++i )
12800   {
12801     //_SolidData& data = _sdVec[i];
12802     TopTools_IndexedMapOfShape geomEdges;
12803     TopExp::MapShapes( data._solid, TopAbs_EDGE, geomEdges );
12804     for ( int iE = 1; iE <= geomEdges.Extent(); ++iE )
12805     {
12806       const TopoDS_Edge& E = TopoDS::Edge( geomEdges(iE));
12807       const TGeomID edgeID = getMeshDS()->ShapeToIndex( E );
12808       if ( data._noShrinkShapes.count( edgeID ))
12809         continue;
12810
12811       // Get _LayerEdge's based on E
12812
12813       map< double, const SMDS_MeshNode* > u2nodes;
12814       if ( !SMESH_Algo::GetSortedNodesOnEdge( getMeshDS(), E, /*ignoreMedium=*/false, u2nodes))
12815         continue;
12816
12817       vector< _LayerEdge* > ledges; ledges.reserve( u2nodes.size() );
12818       TNode2Edge & n2eMap = data._n2eMap;
12819       map< double, const SMDS_MeshNode* >::iterator u2n = u2nodes.begin();
12820       {
12821         //check if 2D elements are needed on E
12822         TNode2Edge::iterator n2e = n2eMap.find( u2n->second );
12823         if ( n2e == n2eMap.end() ) continue; // no layers on vertex
12824         ledges.push_back( n2e->second );
12825         u2n++;
12826         if (( n2e = n2eMap.find( u2n->second )) == n2eMap.end() )
12827           continue; // no layers on E
12828         ledges.push_back( n2eMap[ u2n->second ]);
12829
12830         const SMDS_MeshNode* tgtN0 = ledges[0]->_nodes.back();
12831         const SMDS_MeshNode* tgtN1 = ledges[1]->_nodes.back();
12832         int nbSharedPyram = 0;
12833         SMDS_ElemIteratorPtr vIt = tgtN1->GetInverseElementIterator(SMDSAbs_Volume);
12834         while ( vIt->more() )
12835         {
12836           const SMDS_MeshElement* v = vIt->next();
12837           nbSharedPyram += int( v->GetNodeIndex( tgtN0 ) >= 0 );
12838         }
12839         if ( nbSharedPyram > 1 )
12840           continue; // not free border of the pyramid
12841
12842         faceNodes.clear();
12843         faceNodes.push_back( ledges[0]->_nodes[0] );
12844         faceNodes.push_back( ledges[1]->_nodes[0] );
12845         if ( ledges[0]->_nodes.size() > 1 ) faceNodes.push_back( ledges[0]->_nodes[1] );
12846         if ( ledges[1]->_nodes.size() > 1 ) faceNodes.push_back( ledges[1]->_nodes[1] );
12847
12848         if ( getMeshDS()->FindElement( faceNodes, SMDSAbs_Face, /*noMedium=*/true))
12849           continue; // faces already created
12850       }
12851       for ( ++u2n; u2n != u2nodes.end(); ++u2n )
12852         ledges.push_back( n2eMap[ u2n->second ]);
12853
12854       // Find out orientation and type of face to create
12855
12856       bool reverse = false, isOnFace;
12857       TopoDS_Shape F;
12858
12859       map< TGeomID, TopoDS_Shape >::iterator e2f = data._shrinkShape2Shape.find( edgeID );
12860       if (( isOnFace = ( e2f != data._shrinkShape2Shape.end() )))
12861       {
12862         F = e2f->second.Oriented( TopAbs_FORWARD );
12863         reverse = ( helper.GetSubShapeOri( F, E ) == TopAbs_REVERSED );
12864         if ( helper.GetSubShapeOri( data._solid, F ) == TopAbs_REVERSED )
12865           reverse = !reverse, F.Reverse();
12866         if ( helper.IsReversedSubMesh( TopoDS::Face(F) ))
12867           reverse = !reverse;
12868       }
12869       else if ( !data._ignoreFaceIds.count( e2f->first ))
12870       {
12871         // find FACE with layers sharing E
12872         PShapeIteratorPtr fIt = helper.GetAncestors( E, *_mesh, TopAbs_FACE, &data._solid );
12873         if ( fIt->more() )
12874           F = *( fIt->next() );
12875       }
12876       // Find the sub-mesh to add new faces
12877       SMESHDS_SubMesh* sm = 0;
12878       if ( isOnFace )
12879         sm = getMeshDS()->MeshElements( F );
12880       else
12881         sm = data._proxyMesh->getFaceSubM( TopoDS::Face(F), /*create=*/true );
12882       if ( !sm )
12883         return error("error in addBoundaryElements()", data._index);
12884
12885       // Find a proxy sub-mesh of the FACE of an adjacent SOLID, which will use the new boundary
12886       // faces for 3D meshing (PAL23414)
12887       SMESHDS_SubMesh* adjSM = 0;
12888       if ( isOnFace )
12889       {
12890         const TGeomID   faceID = sm->GetID();
12891         PShapeIteratorPtr soIt = helper.GetAncestors( F, *_mesh, TopAbs_SOLID );
12892         while ( const TopoDS_Shape* solid = soIt->next() )
12893           if ( !solid->IsSame( data._solid ))
12894           {
12895             size_t iData = _solids.FindIndex( *solid ) - 1;
12896             if ( iData < _sdVec.size() &&
12897                  _sdVec[ iData ]._ignoreFaceIds.count( faceID ) &&
12898                  _sdVec[ iData ]._shrinkShape2Shape.count( edgeID ) == 0 )
12899             {
12900               SMESH_ProxyMesh::SubMesh* proxySub =
12901                 _sdVec[ iData ]._proxyMesh->getFaceSubM( TopoDS::Face( F ), /*create=*/false);
12902               if ( proxySub && proxySub->NbElements() > 0 )
12903                 adjSM = proxySub;
12904             }
12905           }
12906       }
12907
12908       // Make faces
12909       const int dj1 = reverse ? 0 : 1;
12910       const int dj2 = reverse ? 1 : 0;
12911       vector< const SMDS_MeshElement*> ff; // new faces row
12912       SMESHDS_Mesh* m = getMeshDS();
12913       for ( size_t j = 1; j < ledges.size(); ++j )
12914       {
12915         vector< const SMDS_MeshNode*>&  nn1 = ledges[j-dj1]->_nodes;
12916         vector< const SMDS_MeshNode*>&  nn2 = ledges[j-dj2]->_nodes;
12917         ff.resize( std::max( nn1.size(), nn2.size() ), NULL );
12918         if ( nn1.size() == nn2.size() )
12919         {
12920           if ( isOnFace )
12921             for ( size_t z = 1; z < nn1.size(); ++z )
12922               sm->AddElement( ff[z-1] = m->AddFace( nn1[z-1], nn2[z-1], nn2[z], nn1[z] ));
12923           else
12924             for ( size_t z = 1; z < nn1.size(); ++z )
12925               sm->AddElement( new SMDS_FaceOfNodes( nn1[z-1], nn2[z-1], nn2[z], nn1[z] ));
12926         }
12927         else if ( nn1.size() == 1 )
12928         {
12929           if ( isOnFace )
12930             for ( size_t z = 1; z < nn2.size(); ++z )
12931               sm->AddElement( ff[z-1] = m->AddFace( nn1[0], nn2[z-1], nn2[z] ));
12932           else
12933             for ( size_t z = 1; z < nn2.size(); ++z )
12934               sm->AddElement( new SMDS_FaceOfNodes( nn1[0], nn2[z-1], nn2[z] ));
12935         }
12936         else
12937         {
12938           if ( isOnFace )
12939             for ( size_t z = 1; z < nn1.size(); ++z )
12940               sm->AddElement( ff[z-1] = m->AddFace( nn1[z-1], nn2[0], nn1[z] ));
12941           else
12942             for ( size_t z = 1; z < nn1.size(); ++z )
12943               sm->AddElement( new SMDS_FaceOfNodes( nn1[z-1], nn2[0], nn2[z] ));
12944         }
12945
12946         if ( adjSM ) // add faces to a proxy SM of the adjacent SOLID
12947         {
12948           for ( size_t z = 0; z < ff.size(); ++z )
12949             if ( ff[ z ])
12950               adjSM->AddElement( ff[ z ]);
12951           ff.clear();
12952         }
12953       }
12954
12955       // Make edges
12956       for ( int isFirst = 0; isFirst < 2; ++isFirst )
12957       {
12958         _LayerEdge* edge = isFirst ? ledges.front() : ledges.back();
12959         _EdgesOnShape* eos = data.GetShapeEdges( edge );
12960         if ( eos && eos->SWOLType() == TopAbs_EDGE )
12961         {
12962           vector< const SMDS_MeshNode*>&  nn = edge->_nodes;
12963           if ( nn.size() < 2 || nn[1]->NbInverseElements( SMDSAbs_Edge ) >= 2 )
12964             continue;
12965           helper.SetSubShape( eos->_sWOL );
12966           helper.SetElementsOnShape( true );
12967           for ( size_t z = 1; z < nn.size(); ++z )
12968             helper.AddEdge( nn[z-1], nn[z] );
12969         }
12970       }
12971
12972     } // loop on EDGE's
12973   } // loop on _SolidData's
12974
12975   return true;
12976 }