Salome HOME
Merge remote-tracking branch 'origin/master' into V9_dev
[modules/smesh.git] / src / StdMeshers / StdMeshers_ViscousLayers.cxx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 // File      : StdMeshers_ViscousLayers.cxx
21 // Created   : Wed Dec  1 15:15:34 2010
22 // Author    : Edward AGAPOV (eap)
23
24 #include "StdMeshers_ViscousLayers.hxx"
25
26 #include "SMDS_EdgePosition.hxx"
27 #include "SMDS_FaceOfNodes.hxx"
28 #include "SMDS_FacePosition.hxx"
29 #include "SMDS_MeshNode.hxx"
30 #include "SMDS_SetIterator.hxx"
31 #include "SMESHDS_Group.hxx"
32 #include "SMESHDS_Hypothesis.hxx"
33 #include "SMESHDS_Mesh.hxx"
34 #include "SMESH_Algo.hxx"
35 #include "SMESH_ComputeError.hxx"
36 #include "SMESH_ControlsDef.hxx"
37 #include "SMESH_Gen.hxx"
38 #include "SMESH_Group.hxx"
39 #include "SMESH_HypoFilter.hxx"
40 #include "SMESH_Mesh.hxx"
41 #include "SMESH_MeshAlgos.hxx"
42 #include "SMESH_MesherHelper.hxx"
43 #include "SMESH_ProxyMesh.hxx"
44 #include "SMESH_subMesh.hxx"
45 #include "SMESH_subMeshEventListener.hxx"
46 #include "StdMeshers_FaceSide.hxx"
47 #include "StdMeshers_ViscousLayers2D.hxx"
48
49 #include <Adaptor3d_HSurface.hxx>
50 #include <BRepAdaptor_Curve.hxx>
51 #include <BRepAdaptor_Curve2d.hxx>
52 #include <BRepAdaptor_Surface.hxx>
53 //#include <BRepLProp_CLProps.hxx>
54 #include <BRepLProp_SLProps.hxx>
55 #include <BRepOffsetAPI_MakeOffsetShape.hxx>
56 #include <BRep_Tool.hxx>
57 #include <Bnd_B2d.hxx>
58 #include <Bnd_B3d.hxx>
59 #include <ElCLib.hxx>
60 #include <GCPnts_AbscissaPoint.hxx>
61 #include <GCPnts_TangentialDeflection.hxx>
62 #include <Geom2d_Circle.hxx>
63 #include <Geom2d_Line.hxx>
64 #include <Geom2d_TrimmedCurve.hxx>
65 #include <GeomAdaptor_Curve.hxx>
66 #include <GeomLib.hxx>
67 #include <Geom_Circle.hxx>
68 #include <Geom_Curve.hxx>
69 #include <Geom_Line.hxx>
70 #include <Geom_TrimmedCurve.hxx>
71 #include <Precision.hxx>
72 #include <Standard_ErrorHandler.hxx>
73 #include <Standard_Failure.hxx>
74 #include <TColStd_Array1OfReal.hxx>
75 #include <TopExp.hxx>
76 #include <TopExp_Explorer.hxx>
77 #include <TopTools_IndexedMapOfShape.hxx>
78 #include <TopTools_ListOfShape.hxx>
79 #include <TopTools_MapIteratorOfMapOfShape.hxx>
80 #include <TopTools_MapOfShape.hxx>
81 #include <TopoDS.hxx>
82 #include <TopoDS_Edge.hxx>
83 #include <TopoDS_Face.hxx>
84 #include <TopoDS_Vertex.hxx>
85 #include <gp_Ax1.hxx>
86 #include <gp_Cone.hxx>
87 #include <gp_Sphere.hxx>
88 #include <gp_Vec.hxx>
89 #include <gp_XY.hxx>
90
91 #include <cmath>
92 #include <limits>
93 #include <list>
94 #include <queue>
95 #include <string>
96
97 #ifdef _DEBUG_
98 #define __myDEBUG
99 //#define __NOT_INVALIDATE_BAD_SMOOTH
100 //#define __NODES_AT_POS
101 #endif
102
103 #define INCREMENTAL_SMOOTH // smooth only if min angle is too small
104 #define BLOCK_INFLATION // of individual _LayerEdge's
105 #define OLD_NEF_POLYGON
106
107 using namespace std;
108
109 //================================================================================
110 namespace VISCOUS_3D
111 {
112   typedef int TGeomID;
113
114   enum UIndex { U_TGT = 1, U_SRC, LEN_TGT };
115
116   const double theMinSmoothCosin = 0.1;
117   const double theSmoothThickToElemSizeRatio = 0.3;
118   const double theMinSmoothTriaAngle = 30;
119   const double theMinSmoothQuadAngle = 45;
120
121   // what part of thickness is allowed till intersection
122   // (defined by SALOME_TESTS/Grids/smesh/viscous_layers_00/A5)
123   const double theThickToIntersection = 1.5;
124
125   bool needSmoothing( double cosin, double tgtThick, double elemSize )
126   {
127     return cosin * tgtThick > theSmoothThickToElemSizeRatio * elemSize;
128   }
129   double getSmoothingThickness( double cosin, double elemSize )
130   {
131     return theSmoothThickToElemSizeRatio * elemSize / cosin;
132   }
133
134   /*!
135    * \brief SMESH_ProxyMesh computed by _ViscousBuilder for a SOLID.
136    * It is stored in a SMESH_subMesh of the SOLID as SMESH_subMeshEventListenerData
137    */
138   struct _MeshOfSolid : public SMESH_ProxyMesh,
139                         public SMESH_subMeshEventListenerData
140   {
141     bool                  _n2nMapComputed;
142     SMESH_ComputeErrorPtr _warning;
143
144     _MeshOfSolid( SMESH_Mesh* mesh)
145       :SMESH_subMeshEventListenerData( /*isDeletable=*/true),_n2nMapComputed(false)
146     {
147       SMESH_ProxyMesh::setMesh( *mesh );
148     }
149
150     // returns submesh for a geom face
151     SMESH_ProxyMesh::SubMesh* getFaceSubM(const TopoDS_Face& F, bool create=false)
152     {
153       TGeomID i = SMESH_ProxyMesh::shapeIndex(F);
154       return create ? SMESH_ProxyMesh::getProxySubMesh(i) : findProxySubMesh(i);
155     }
156     void setNode2Node(const SMDS_MeshNode*                 srcNode,
157                       const SMDS_MeshNode*                 proxyNode,
158                       const SMESH_ProxyMesh::SubMesh* subMesh)
159     {
160       SMESH_ProxyMesh::setNode2Node( srcNode,proxyNode,subMesh);
161     }
162   };
163   //--------------------------------------------------------------------------------
164   /*!
165    * \brief Listener of events of 3D sub-meshes computed with viscous layers.
166    * It is used to clear an inferior dim sub-meshes modified by viscous layers
167    */
168   class _ShrinkShapeListener : SMESH_subMeshEventListener
169   {
170     _ShrinkShapeListener()
171       : SMESH_subMeshEventListener(/*isDeletable=*/false,
172                                    "StdMeshers_ViscousLayers::_ShrinkShapeListener") {}
173   public:
174     static SMESH_subMeshEventListener* Get() { static _ShrinkShapeListener l; return &l; }
175     virtual void ProcessEvent(const int                       event,
176                               const int                       eventType,
177                               SMESH_subMesh*                  solidSM,
178                               SMESH_subMeshEventListenerData* data,
179                               const SMESH_Hypothesis*         hyp)
180     {
181       if ( SMESH_subMesh::COMPUTE_EVENT == eventType && solidSM->IsEmpty() && data )
182       {
183         SMESH_subMeshEventListener::ProcessEvent(event,eventType,solidSM,data,hyp);
184       }
185     }
186   };
187   //--------------------------------------------------------------------------------
188   /*!
189    * \brief Listener of events of 3D sub-meshes computed with viscous layers.
190    * It is used to store data computed by _ViscousBuilder for a sub-mesh and to
191    * delete the data as soon as it has been used
192    */
193   class _ViscousListener : SMESH_subMeshEventListener
194   {
195     _ViscousListener():
196       SMESH_subMeshEventListener(/*isDeletable=*/false,
197                                  "StdMeshers_ViscousLayers::_ViscousListener") {}
198     static SMESH_subMeshEventListener* Get() { static _ViscousListener l; return &l; }
199   public:
200     virtual void ProcessEvent(const int                       event,
201                               const int                       eventType,
202                               SMESH_subMesh*                  subMesh,
203                               SMESH_subMeshEventListenerData* data,
204                               const SMESH_Hypothesis*         hyp)
205     {
206       if (( SMESH_subMesh::COMPUTE_EVENT       == eventType ) &&
207           ( SMESH_subMesh::CHECK_COMPUTE_STATE != event &&
208             SMESH_subMesh::SUBMESH_COMPUTED    != event ))
209       {
210         // delete SMESH_ProxyMesh containing temporary faces
211         subMesh->DeleteEventListener( this );
212       }
213     }
214     // Finds or creates proxy mesh of the solid
215     static _MeshOfSolid* GetSolidMesh(SMESH_Mesh*         mesh,
216                                       const TopoDS_Shape& solid,
217                                       bool                toCreate=false)
218     {
219       if ( !mesh ) return 0;
220       SMESH_subMesh* sm = mesh->GetSubMesh(solid);
221       _MeshOfSolid* data = (_MeshOfSolid*) sm->GetEventListenerData( Get() );
222       if ( !data && toCreate )
223       {
224         data = new _MeshOfSolid(mesh);
225         data->mySubMeshes.push_back( sm ); // to find SOLID by _MeshOfSolid
226         sm->SetEventListener( Get(), data, sm );
227       }
228       return data;
229     }
230     // Removes proxy mesh of the solid
231     static void RemoveSolidMesh(SMESH_Mesh* mesh, const TopoDS_Shape& solid)
232     {
233       mesh->GetSubMesh(solid)->DeleteEventListener( _ViscousListener::Get() );
234     }
235   };
236   
237   //================================================================================
238   /*!
239    * \brief sets a sub-mesh event listener to clear sub-meshes of sub-shapes of
240    * the main shape when sub-mesh of the main shape is cleared,
241    * for example to clear sub-meshes of FACEs when sub-mesh of a SOLID
242    * is cleared
243    */
244   //================================================================================
245
246   void ToClearSubWithMain( SMESH_subMesh* sub, const TopoDS_Shape& main)
247   {
248     SMESH_subMesh* mainSM = sub->GetFather()->GetSubMesh( main );
249     SMESH_subMeshEventListenerData* data =
250       mainSM->GetEventListenerData( _ShrinkShapeListener::Get());
251     if ( data )
252     {
253       if ( find( data->mySubMeshes.begin(), data->mySubMeshes.end(), sub ) ==
254            data->mySubMeshes.end())
255         data->mySubMeshes.push_back( sub );
256     }
257     else
258     {
259       data = SMESH_subMeshEventListenerData::MakeData( /*dependent=*/sub );
260       sub->SetEventListener( _ShrinkShapeListener::Get(), data, /*whereToListenTo=*/mainSM );
261     }
262   }
263   struct _SolidData;
264   //--------------------------------------------------------------------------------
265   /*!
266    * \brief Simplex (triangle or tetrahedron) based on 1 (tria) or 2 (tet) nodes of
267    * _LayerEdge and 2 nodes of the mesh surface beening smoothed.
268    * The class is used to check validity of face or volumes around a smoothed node;
269    * it stores only 2 nodes as the other nodes are stored by _LayerEdge.
270    */
271   struct _Simplex
272   {
273     const SMDS_MeshNode *_nPrev, *_nNext; // nodes on a smoothed mesh surface
274     const SMDS_MeshNode *_nOpp; // in 2D case, a node opposite to a smoothed node in QUAD
275     _Simplex(const SMDS_MeshNode* nPrev=0,
276              const SMDS_MeshNode* nNext=0,
277              const SMDS_MeshNode* nOpp=0)
278       : _nPrev(nPrev), _nNext(nNext), _nOpp(nOpp) {}
279     bool IsForward(const gp_XYZ* pntSrc, const gp_XYZ* pntTgt, double& vol) const
280     {
281       const double M[3][3] =
282         {{ _nNext->X() - pntSrc->X(), _nNext->Y() - pntSrc->Y(), _nNext->Z() - pntSrc->Z() },
283          { pntTgt->X() - pntSrc->X(), pntTgt->Y() - pntSrc->Y(), pntTgt->Z() - pntSrc->Z() },
284          { _nPrev->X() - pntSrc->X(), _nPrev->Y() - pntSrc->Y(), _nPrev->Z() - pntSrc->Z() }};
285       vol = ( + M[0][0] * M[1][1] * M[2][2]
286               + M[0][1] * M[1][2] * M[2][0]
287               + M[0][2] * M[1][0] * M[2][1]
288               - M[0][0] * M[1][2] * M[2][1]
289               - M[0][1] * M[1][0] * M[2][2]
290               - M[0][2] * M[1][1] * M[2][0]);
291       return vol > 1e-100;
292     }
293     bool IsForward(const SMDS_MeshNode* nSrc, const gp_XYZ& pTgt, double& vol) const
294     {
295       SMESH_TNodeXYZ pSrc( nSrc );
296       return IsForward( &pSrc, &pTgt, vol );
297     }
298     bool IsForward(const gp_XY&         tgtUV,
299                    const SMDS_MeshNode* smoothedNode,
300                    const TopoDS_Face&   face,
301                    SMESH_MesherHelper&  helper,
302                    const double         refSign) const
303     {
304       gp_XY prevUV = helper.GetNodeUV( face, _nPrev, smoothedNode );
305       gp_XY nextUV = helper.GetNodeUV( face, _nNext, smoothedNode );
306       gp_Vec2d v1( tgtUV, prevUV ), v2( tgtUV, nextUV );
307       double d = v1 ^ v2;
308       return d*refSign > 1e-100;
309     }
310     bool IsMinAngleOK( const gp_XYZ& pTgt, double& minAngle ) const
311     {
312       SMESH_TNodeXYZ pPrev( _nPrev ), pNext( _nNext );
313       if ( !_nOpp ) // triangle
314       {
315         gp_Vec tp( pPrev - pTgt ), pn( pNext - pPrev ), nt( pTgt - pNext );
316         double tp2 = tp.SquareMagnitude();
317         double pn2 = pn.SquareMagnitude();
318         double nt2 = nt.SquareMagnitude();
319
320         if ( tp2 < pn2 && tp2 < nt2 )
321           minAngle = ( nt * -pn ) * ( nt * -pn ) / nt2 / pn2;
322         else if ( pn2 < nt2 )
323           minAngle = ( tp * -nt ) * ( tp * -nt ) / tp2 / nt2;
324         else
325           minAngle = ( pn * -tp ) * ( pn * -tp ) / pn2 / tp2;
326
327         static double theMaxCos2 = ( Cos( theMinSmoothTriaAngle * M_PI / 180. ) *
328                                      Cos( theMinSmoothTriaAngle * M_PI / 180. ));
329         return minAngle < theMaxCos2;
330       }
331       else // quadrangle
332       {
333         SMESH_TNodeXYZ pOpp( _nOpp );
334         gp_Vec tp( pPrev - pTgt ), po( pOpp - pPrev ), on( pNext - pOpp), nt( pTgt - pNext );
335         double tp2 = tp.SquareMagnitude();
336         double po2 = po.SquareMagnitude();
337         double on2 = on.SquareMagnitude();
338         double nt2 = nt.SquareMagnitude();
339         minAngle = Max( Max((( tp * -nt ) * ( tp * -nt ) / tp2 / nt2 ),
340                             (( po * -tp ) * ( po * -tp ) / po2 / tp2 )),
341                         Max((( on * -po ) * ( on * -po ) / on2 / po2 ),
342                             (( nt * -on ) * ( nt * -on ) / nt2 / on2 )));
343
344         static double theMaxCos2 = ( Cos( theMinSmoothQuadAngle * M_PI / 180. ) *
345                                      Cos( theMinSmoothQuadAngle * M_PI / 180. ));
346         return minAngle < theMaxCos2;
347       }
348     }
349     bool IsNeighbour(const _Simplex& other) const
350     {
351       return _nPrev == other._nNext || _nNext == other._nPrev;
352     }
353     bool Includes( const SMDS_MeshNode* node ) const { return _nPrev == node || _nNext == node; }
354     static void GetSimplices( const SMDS_MeshNode* node,
355                               vector<_Simplex>&   simplices,
356                               const set<TGeomID>& ingnoreShapes,
357                               const _SolidData*   dataToCheckOri = 0,
358                               const bool          toSort = false);
359     static void SortSimplices(vector<_Simplex>& simplices);
360   };
361   //--------------------------------------------------------------------------------
362   /*!
363    * Structure used to take into account surface curvature while smoothing
364    */
365   struct _Curvature
366   {
367     double   _r; // radius
368     double   _k; // factor to correct node smoothed position
369     double   _h2lenRatio; // avgNormProj / (2*avgDist)
370     gp_Pnt2d _uv; // UV used in putOnOffsetSurface()
371   public:
372     static _Curvature* New( double avgNormProj, double avgDist )
373     {
374       _Curvature* c = 0;
375       if ( fabs( avgNormProj / avgDist ) > 1./200 )
376       {
377         c = new _Curvature;
378         c->_r = avgDist * avgDist / avgNormProj;
379         c->_k = avgDist * avgDist / c->_r / c->_r;
380         //c->_k = avgNormProj / c->_r;
381         c->_k *= ( c->_r < 0 ? 1/1.1 : 1.1 ); // not to be too restrictive
382         c->_h2lenRatio = avgNormProj / ( avgDist + avgDist );
383
384         c->_uv.SetCoord( 0., 0. );
385       }
386       return c;
387     }
388     double lenDelta(double len) const { return _k * ( _r + len ); }
389     double lenDeltaByDist(double dist) const { return dist * _h2lenRatio; }
390   };
391   //--------------------------------------------------------------------------------
392
393   struct _2NearEdges;
394   struct _LayerEdge;
395   struct _EdgesOnShape;
396   struct _Smoother1D;
397   typedef map< const SMDS_MeshNode*, _LayerEdge*, TIDCompare > TNode2Edge;
398
399   //--------------------------------------------------------------------------------
400   /*!
401    * \brief Edge normal to surface, connecting a node on solid surface (_nodes[0])
402    * and a node of the most internal layer (_nodes.back())
403    */
404   struct _LayerEdge
405   {
406     typedef gp_XYZ (_LayerEdge::*PSmooFun)();
407
408     vector< const SMDS_MeshNode*> _nodes;
409
410     gp_XYZ              _normal;    // to boundary of solid
411     vector<gp_XYZ>      _pos;       // points computed during inflation
412     double              _len;       // length achieved with the last inflation step
413     double              _maxLen;    // maximal possible length
414     double              _cosin;     // of angle (_normal ^ surface)
415     double              _minAngle;  // of _simplices
416     double              _lenFactor; // to compute _len taking _cosin into account
417     int                 _flags;
418
419     // simplices connected to the source node (_nodes[0]);
420     // used for smoothing and quality check of _LayerEdge's based on the FACE
421     vector<_Simplex>    _simplices;
422     vector<_LayerEdge*> _neibors; // all surrounding _LayerEdge's
423     PSmooFun            _smooFunction; // smoothing function
424     _Curvature*         _curvature;
425     // data for smoothing of _LayerEdge's based on the EDGE
426     _2NearEdges*        _2neibors;
427
428     enum EFlags { TO_SMOOTH       = 0x0000001,
429                   MOVED           = 0x0000002, // set by _neibors[i]->SetNewLength()
430                   SMOOTHED        = 0x0000004, // set by _LayerEdge::Smooth()
431                   DIFFICULT       = 0x0000008, // near concave VERTEX
432                   ON_CONCAVE_FACE = 0x0000010,
433                   BLOCKED         = 0x0000020, // not to inflate any more
434                   INTERSECTED     = 0x0000040, // close intersection with a face found
435                   NORMAL_UPDATED  = 0x0000080,
436                   UPD_NORMAL_CONV = 0x0000100, // to update normal on boundary of concave FACE
437                   MARKED          = 0x0000200, // local usage
438                   MULTI_NORMAL    = 0x0000400, // a normal is invisible by some of surrounding faces
439                   NEAR_BOUNDARY   = 0x0000800, // is near FACE boundary forcing smooth
440                   SMOOTHED_C1     = 0x0001000, // is on _eosC1
441                   DISTORTED       = 0x0002000, // was bad before smoothing
442                   RISKY_SWOL      = 0x0004000, // SWOL is parallel to a source FACE
443                   SHRUNK          = 0x0008000, // target node reached a tgt position while shrink()
444                   UNUSED_FLAG     = 0x0100000  // to add user flags after
445     };
446     bool Is   ( int flag ) const { return _flags & flag; }
447     void Set  ( int flag ) { _flags |= flag; }
448     void Unset( int flag ) { _flags &= ~flag; }
449     std::string DumpFlags() const; // debug
450
451     void SetNewLength( double len, _EdgesOnShape& eos, SMESH_MesherHelper& helper );
452     bool SetNewLength2d( Handle(Geom_Surface)& surface,
453                          const TopoDS_Face&    F,
454                          _EdgesOnShape&        eos,
455                          SMESH_MesherHelper&   helper );
456     void SetDataByNeighbors( const SMDS_MeshNode* n1,
457                              const SMDS_MeshNode* n2,
458                              const _EdgesOnShape& eos,
459                              SMESH_MesherHelper&  helper);
460     void Block( _SolidData& data );
461     void InvalidateStep( size_t curStep, const _EdgesOnShape& eos, bool restoreLength=false );
462     void ChooseSmooFunction(const set< TGeomID >& concaveVertices,
463                             const TNode2Edge&     n2eMap);
464     void SmoothPos( const vector< double >& segLen, const double tol );
465     int  GetSmoothedPos( const double tol );
466     int  Smooth(const int step, const bool isConcaveFace, bool findBest);
467     int  Smooth(const int step, bool findBest, vector< _LayerEdge* >& toSmooth );
468     int  CheckNeiborsOnBoundary(vector< _LayerEdge* >* badNeibors = 0, bool * needSmooth = 0 );
469     void SmoothWoCheck();
470     bool SmoothOnEdge(Handle(ShapeAnalysis_Surface)& surface,
471                       const TopoDS_Face&             F,
472                       SMESH_MesherHelper&            helper);
473     void MoveNearConcaVer( const _EdgesOnShape*    eov,
474                            const _EdgesOnShape*    eos,
475                            const int               step,
476                            vector< _LayerEdge* > & badSmooEdges);
477     bool FindIntersection( SMESH_ElementSearcher&   searcher,
478                            double &                 distance,
479                            const double&            epsilon,
480                            _EdgesOnShape&           eos,
481                            const SMDS_MeshElement** face = 0);
482     bool SegTriaInter( const gp_Ax1&        lastSegment,
483                        const gp_XYZ&        p0,
484                        const gp_XYZ&        p1,
485                        const gp_XYZ&        p2,
486                        double&              dist,
487                        const double&        epsilon) const;
488     bool SegTriaInter( const gp_Ax1&        lastSegment,
489                        const SMDS_MeshNode* n0,
490                        const SMDS_MeshNode* n1,
491                        const SMDS_MeshNode* n2,
492                        double&              dist,
493                        const double&        epsilon) const
494     { return SegTriaInter( lastSegment,
495                            SMESH_TNodeXYZ( n0 ), SMESH_TNodeXYZ( n1 ), SMESH_TNodeXYZ( n2 ),
496                            dist, epsilon );
497     }
498     const gp_XYZ& PrevPos() const { return _pos[ _pos.size() - 2 ]; }
499     gp_XYZ PrevCheckPos( _EdgesOnShape* eos=0 ) const;
500     gp_Ax1 LastSegment(double& segLen, _EdgesOnShape& eos) const;
501     gp_XY  LastUV( const TopoDS_Face& F, _EdgesOnShape& eos, int which=-1 ) const;
502     bool   IsOnEdge() const { return _2neibors; }
503     bool   IsOnFace() const { return ( _nodes[0]->GetPosition()->GetDim() == 2 ); }
504     gp_XYZ Copy( _LayerEdge& other, _EdgesOnShape& eos, SMESH_MesherHelper& helper );
505     void   SetCosin( double cosin );
506     void   SetNormal( const gp_XYZ& n ) { _normal = n; }
507     int    NbSteps() const { return _pos.size() - 1; } // nb inlation steps
508     bool   IsNeiborOnEdge( const _LayerEdge* edge ) const;
509     void   SetSmooLen( double len ) { // set _len at which smoothing is needed
510       _cosin = len; // as for _LayerEdge's on FACE _cosin is not used
511     }
512     double GetSmooLen() { return _cosin; } // for _LayerEdge's on FACE _cosin is not used
513
514     gp_XYZ smoothLaplacian();
515     gp_XYZ smoothAngular();
516     gp_XYZ smoothLengthWeighted();
517     gp_XYZ smoothCentroidal();
518     gp_XYZ smoothNefPolygon();
519
520     enum { FUN_LAPLACIAN, FUN_LENWEIGHTED, FUN_CENTROIDAL, FUN_NEFPOLY, FUN_ANGULAR, FUN_NB };
521     static const int theNbSmooFuns = FUN_NB;
522     static PSmooFun _funs[theNbSmooFuns];
523     static const char* _funNames[theNbSmooFuns+1];
524     int smooFunID( PSmooFun fun=0) const;
525   };
526   _LayerEdge::PSmooFun _LayerEdge::_funs[theNbSmooFuns] = { &_LayerEdge::smoothLaplacian,
527                                                             &_LayerEdge::smoothLengthWeighted,
528                                                             &_LayerEdge::smoothCentroidal,
529                                                             &_LayerEdge::smoothNefPolygon,
530                                                             &_LayerEdge::smoothAngular };
531   const char* _LayerEdge::_funNames[theNbSmooFuns+1] = { "Laplacian",
532                                                          "LengthWeighted",
533                                                          "Centroidal",
534                                                          "NefPolygon",
535                                                          "Angular",
536                                                          "None"};
537   struct _LayerEdgeCmp
538   {
539     bool operator () (const _LayerEdge* e1, const _LayerEdge* e2) const
540     {
541       const bool cmpNodes = ( e1 && e2 && e1->_nodes.size() && e2->_nodes.size() );
542       return cmpNodes ? ( e1->_nodes[0]->GetID() < e2->_nodes[0]->GetID()) : ( e1 < e2 );
543     }
544   };
545   //--------------------------------------------------------------------------------
546   /*!
547    * A 2D half plane used by _LayerEdge::smoothNefPolygon()
548    */
549   struct _halfPlane
550   {
551     gp_XY _pos, _dir, _inNorm;
552     bool IsOut( const gp_XY p, const double tol ) const
553     {
554       return _inNorm * ( p - _pos ) < -tol;
555     }
556     bool FindIntersection( const _halfPlane& hp, gp_XY & intPnt )
557     {
558       //const double eps = 1e-10;
559       double D = _dir.Crossed( hp._dir );
560       if ( fabs(D) < std::numeric_limits<double>::min())
561         return false;
562       gp_XY vec21 = _pos - hp._pos; 
563       double u = hp._dir.Crossed( vec21 ) / D; 
564       intPnt = _pos + _dir * u;
565       return true;
566     }
567   };
568   //--------------------------------------------------------------------------------
569   /*!
570    * Structure used to smooth a _LayerEdge based on an EDGE.
571    */
572   struct _2NearEdges
573   {
574     double               _wgt  [2]; // weights of _nodes
575     _LayerEdge*          _edges[2];
576
577      // normal to plane passing through _LayerEdge._normal and tangent of EDGE
578     gp_XYZ*              _plnNorm;
579
580     _2NearEdges() { _edges[0]=_edges[1]=0; _plnNorm = 0; }
581     const SMDS_MeshNode* tgtNode(bool is2nd) {
582       return _edges[is2nd] ? _edges[is2nd]->_nodes.back() : 0;
583     }
584     const SMDS_MeshNode* srcNode(bool is2nd) {
585       return _edges[is2nd] ? _edges[is2nd]->_nodes[0] : 0;
586     }
587     void reverse() {
588       std::swap( _wgt  [0], _wgt  [1] );
589       std::swap( _edges[0], _edges[1] );
590     }
591     void set( _LayerEdge* e1, _LayerEdge* e2, double w1, double w2 ) {
592       _edges[0] = e1; _edges[1] = e2; _wgt[0] = w1; _wgt[1] = w2;
593     }
594     bool include( const _LayerEdge* e ) {
595       return ( _edges[0] == e || _edges[1] == e );
596     }
597   };
598
599
600   //--------------------------------------------------------------------------------
601   /*!
602    * \brief Layers parameters got by averaging several hypotheses
603    */
604   struct AverageHyp
605   {
606     AverageHyp( const StdMeshers_ViscousLayers* hyp = 0 )
607       :_nbLayers(0), _nbHyps(0), _method(0), _thickness(0), _stretchFactor(0)
608     {
609       Add( hyp );
610     }
611     void Add( const StdMeshers_ViscousLayers* hyp )
612     {
613       if ( hyp )
614       {
615         _nbHyps++;
616         _nbLayers       = hyp->GetNumberLayers();
617         //_thickness     += hyp->GetTotalThickness();
618         _thickness      = Max( _thickness, hyp->GetTotalThickness() );
619         _stretchFactor += hyp->GetStretchFactor();
620         _method         = hyp->GetMethod();
621       }
622     }
623     double GetTotalThickness() const { return _thickness; /*_nbHyps ? _thickness / _nbHyps : 0;*/ }
624     double GetStretchFactor()  const { return _nbHyps ? _stretchFactor / _nbHyps : 0; }
625     int    GetNumberLayers()   const { return _nbLayers; }
626     int    GetMethod()         const { return _method; }
627
628     bool   UseSurfaceNormal()  const
629     { return _method == StdMeshers_ViscousLayers::SURF_OFFSET_SMOOTH; }
630     bool   ToSmooth()          const
631     { return _method == StdMeshers_ViscousLayers::SURF_OFFSET_SMOOTH; }
632     bool   IsOffsetMethod()    const
633     { return _method == StdMeshers_ViscousLayers::FACE_OFFSET; }
634
635   private:
636     int     _nbLayers, _nbHyps, _method;
637     double  _thickness, _stretchFactor;
638   };
639
640   //--------------------------------------------------------------------------------
641   /*!
642    * \brief _LayerEdge's on a shape and other shape data
643    */
644   struct _EdgesOnShape
645   {
646     vector< _LayerEdge* > _edges;
647
648     TopoDS_Shape          _shape;
649     TGeomID               _shapeID;
650     SMESH_subMesh *       _subMesh;
651     // face or edge w/o layer along or near which _edges are inflated
652     TopoDS_Shape          _sWOL;
653     bool                  _isRegularSWOL; // w/o singularities
654     // averaged StdMeshers_ViscousLayers parameters
655     AverageHyp            _hyp;
656     bool                  _toSmooth;
657     _Smoother1D*          _edgeSmoother;
658     vector< _EdgesOnShape* > _eosConcaVer; // edges at concave VERTEXes of a FACE
659     vector< _EdgesOnShape* > _eosC1; // to smooth together several C1 continues shapes
660
661     vector< gp_XYZ >         _faceNormals; // if _shape is FACE
662     vector< _EdgesOnShape* > _faceEOS; // to get _faceNormals of adjacent FACEs
663
664     Handle(ShapeAnalysis_Surface) _offsetSurf;
665     _LayerEdge*                   _edgeForOffset;
666
667     _SolidData*            _data; // parent SOLID
668
669     _LayerEdge*      operator[](size_t i) const { return (_LayerEdge*) _edges[i]; }
670     size_t           size() const { return _edges.size(); }
671     TopAbs_ShapeEnum ShapeType() const
672     { return _shape.IsNull() ? TopAbs_SHAPE : _shape.ShapeType(); }
673     TopAbs_ShapeEnum SWOLType() const
674     { return _sWOL.IsNull() ? TopAbs_SHAPE : _sWOL.ShapeType(); }
675     bool             HasC1( const _EdgesOnShape* other ) const
676     { return std::find( _eosC1.begin(), _eosC1.end(), other ) != _eosC1.end(); }
677     bool             GetNormal( const SMDS_MeshElement* face, gp_Vec& norm );
678     _SolidData&      GetData() const { return *_data; }
679
680     _EdgesOnShape(): _shapeID(-1), _subMesh(0), _toSmooth(false), _edgeSmoother(0) {}
681   };
682
683   //--------------------------------------------------------------------------------
684   /*!
685    * \brief Convex FACE whose radius of curvature is less than the thickness of
686    *        layers. It is used to detect distortion of prisms based on a convex
687    *        FACE and to update normals to enable further increasing the thickness
688    */
689   struct _ConvexFace
690   {
691     TopoDS_Face                     _face;
692
693     // edges whose _simplices are used to detect prism distortion
694     vector< _LayerEdge* >           _simplexTestEdges;
695
696     // map a sub-shape to _SolidData::_edgesOnShape
697     map< TGeomID, _EdgesOnShape* >  _subIdToEOS;
698
699     bool                            _isTooCurved;
700     bool                            _normalsFixed;
701     bool                            _normalsFixedOnBorders; // used in putOnOffsetSurface()
702
703     double GetMaxCurvature( _SolidData&         data,
704                             _EdgesOnShape&      eof,
705                             BRepLProp_SLProps&  surfProp,
706                             SMESH_MesherHelper& helper);
707
708     bool GetCenterOfCurvature( _LayerEdge*         ledge,
709                                BRepLProp_SLProps&  surfProp,
710                                SMESH_MesherHelper& helper,
711                                gp_Pnt &            center ) const;
712     bool CheckPrisms() const;
713   };
714
715   //--------------------------------------------------------------------------------
716   /*!
717    * \brief Structure holding _LayerEdge's based on EDGEs that will collide
718    *        at inflation up to the full thickness. A detected collision
719    *        is fixed in updateNormals()
720    */
721   struct _CollisionEdges
722   {
723     _LayerEdge*           _edge;
724     vector< _LayerEdge* > _intEdges; // each pair forms an intersected quadrangle
725     const SMDS_MeshNode* nSrc(int i) const { return _intEdges[i]->_nodes[0]; }
726     const SMDS_MeshNode* nTgt(int i) const { return _intEdges[i]->_nodes.back(); }
727   };
728
729   //--------------------------------------------------------------------------------
730   /*!
731    * \brief Data of a SOLID
732    */
733   struct _SolidData
734   {
735     typedef const StdMeshers_ViscousLayers* THyp;
736     TopoDS_Shape                    _solid;
737     TopTools_MapOfShape             _before; // SOLIDs to be computed before _solid
738     TGeomID                         _index; // SOLID id
739     _MeshOfSolid*                   _proxyMesh;
740     list< THyp >                    _hyps;
741     list< TopoDS_Shape >            _hypShapes;
742     map< TGeomID, THyp >            _face2hyp; // filled if _hyps.size() > 1
743     set< TGeomID >                  _reversedFaceIds;
744     set< TGeomID >                  _ignoreFaceIds; // WOL FACEs and FACEs of other SOLIDs
745
746     double                          _stepSize, _stepSizeCoeff, _geomSize;
747     const SMDS_MeshNode*            _stepSizeNodes[2];
748
749     TNode2Edge                      _n2eMap; // nodes and _LayerEdge's based on them
750
751     // map to find _n2eMap of another _SolidData by a shrink shape shared by two _SolidData's
752     map< TGeomID, TNode2Edge* >     _s2neMap;
753     // _LayerEdge's with underlying shapes
754     vector< _EdgesOnShape >         _edgesOnShape;
755
756     // key:   an id of shape (EDGE or VERTEX) shared by a FACE with
757     //        layers and a FACE w/o layers
758     // value: the shape (FACE or EDGE) to shrink mesh on.
759     //       _LayerEdge's basing on nodes on key shape are inflated along the value shape
760     map< TGeomID, TopoDS_Shape >     _shrinkShape2Shape;
761
762     // Convex FACEs whose radius of curvature is less than the thickness of layers
763     map< TGeomID, _ConvexFace >      _convexFaces;
764
765     // shapes (EDGEs and VERTEXes) srink from which is forbidden due to collisions with
766     // the adjacent SOLID
767     set< TGeomID >                   _noShrinkShapes;
768
769     int                              _nbShapesToSmooth;
770
771     vector< _CollisionEdges >        _collisionEdges;
772     set< TGeomID >                   _concaveFaces;
773
774     double                           _maxThickness; // of all _hyps
775     double                           _minThickness; // of all _hyps
776
777     double                           _epsilon; // precision for SegTriaInter()
778
779     SMESH_MesherHelper*              _helper;
780
781     _SolidData(const TopoDS_Shape& s=TopoDS_Shape(),
782                _MeshOfSolid*       m=0)
783       :_solid(s), _proxyMesh(m), _helper(0) {}
784     ~_SolidData();
785
786     void SortOnEdge( const TopoDS_Edge& E, vector< _LayerEdge* >& edges);
787     void Sort2NeiborsOnEdge( vector< _LayerEdge* >& edges );
788
789     _ConvexFace* GetConvexFace( const TGeomID faceID ) {
790       map< TGeomID, _ConvexFace >::iterator id2face = _convexFaces.find( faceID );
791       return id2face == _convexFaces.end() ? 0 : & id2face->second;
792     }
793     _EdgesOnShape* GetShapeEdges(const TGeomID       shapeID );
794     _EdgesOnShape* GetShapeEdges(const TopoDS_Shape& shape );
795     _EdgesOnShape* GetShapeEdges(const _LayerEdge*   edge )
796     { return GetShapeEdges( edge->_nodes[0]->getshapeId() ); }
797
798     SMESH_MesherHelper& GetHelper() const { return *_helper; }
799
800     void UnmarkEdges( int flag = _LayerEdge::MARKED ) {
801       for ( size_t i = 0; i < _edgesOnShape.size(); ++i )
802         for ( size_t j = 0; j < _edgesOnShape[i]._edges.size(); ++j )
803           _edgesOnShape[i]._edges[j]->Unset( flag );
804     }
805     void AddShapesToSmooth( const set< _EdgesOnShape* >& shape,
806                             const set< _EdgesOnShape* >* edgesNoAnaSmooth=0 );
807
808     void PrepareEdgesToSmoothOnFace( _EdgesOnShape* eof, bool substituteSrcNodes );
809   };
810   //--------------------------------------------------------------------------------
811   /*!
812    * \brief Offset plane used in getNormalByOffset()
813    */
814   struct _OffsetPlane
815   {
816     gp_Pln _plane;
817     int    _faceIndex;
818     int    _faceIndexNext[2];
819     gp_Lin _lines[2]; // line of intersection with neighbor _OffsetPlane's
820     bool   _isLineOK[2];
821     _OffsetPlane() {
822       _isLineOK[0] = _isLineOK[1] = false; _faceIndexNext[0] = _faceIndexNext[1] = -1;
823     }
824     void   ComputeIntersectionLine( _OffsetPlane&        pln, 
825                                     const TopoDS_Edge&   E,
826                                     const TopoDS_Vertex& V );
827     gp_XYZ GetCommonPoint(bool& isFound, const TopoDS_Vertex& V) const;
828     int    NbLines() const { return _isLineOK[0] + _isLineOK[1]; }
829   };
830   //--------------------------------------------------------------------------------
831   /*!
832    * \brief Container of centers of curvature at nodes on an EDGE bounding _ConvexFace
833    */
834   struct _CentralCurveOnEdge
835   {
836     bool                  _isDegenerated;
837     vector< gp_Pnt >      _curvaCenters;
838     vector< _LayerEdge* > _ledges;
839     vector< gp_XYZ >      _normals; // new normal for each of _ledges
840     vector< double >      _segLength2;
841
842     TopoDS_Edge           _edge;
843     TopoDS_Face           _adjFace;
844     bool                  _adjFaceToSmooth;
845
846     void Append( const gp_Pnt& center, _LayerEdge* ledge )
847     {
848       if ( _curvaCenters.size() > 0 )
849         _segLength2.push_back( center.SquareDistance( _curvaCenters.back() ));
850       _curvaCenters.push_back( center );
851       _ledges.push_back( ledge );
852       _normals.push_back( ledge->_normal );
853     }
854     bool FindNewNormal( const gp_Pnt& center, gp_XYZ& newNormal );
855     void SetShapes( const TopoDS_Edge&  edge,
856                     const _ConvexFace&  convFace,
857                     _SolidData&         data,
858                     SMESH_MesherHelper& helper);
859   };
860   //--------------------------------------------------------------------------------
861   /*!
862    * \brief Data of node on a shrinked FACE
863    */
864   struct _SmoothNode
865   {
866     const SMDS_MeshNode*         _node;
867     vector<_Simplex>             _simplices; // for quality check
868
869     enum SmoothType { LAPLACIAN, CENTROIDAL, ANGULAR, TFI };
870
871     bool Smooth(int&                  badNb,
872                 Handle(Geom_Surface)& surface,
873                 SMESH_MesherHelper&   helper,
874                 const double          refSign,
875                 SmoothType            how,
876                 bool                  set3D);
877
878     gp_XY computeAngularPos(vector<gp_XY>& uv,
879                             const gp_XY&   uvToFix,
880                             const double   refSign );
881   };
882   struct PyDump;
883   //--------------------------------------------------------------------------------
884   /*!
885    * \brief Builder of viscous layers
886    */
887   class _ViscousBuilder
888   {
889   public:
890     _ViscousBuilder();
891     // does it's job
892     SMESH_ComputeErrorPtr Compute(SMESH_Mesh&         mesh,
893                                   const TopoDS_Shape& shape);
894     // check validity of hypotheses
895     SMESH_ComputeErrorPtr CheckHypotheses( SMESH_Mesh&         mesh,
896                                            const TopoDS_Shape& shape );
897
898     // restore event listeners used to clear an inferior dim sub-mesh modified by viscous layers
899     void RestoreListeners();
900
901     // computes SMESH_ProxyMesh::SubMesh::_n2n;
902     bool MakeN2NMap( _MeshOfSolid* pm );
903
904   private:
905
906     bool findSolidsWithLayers();
907     bool setBefore( _SolidData& solidBefore, _SolidData& solidAfter );
908     bool findFacesWithLayers(const bool onlyWith=false);
909     void getIgnoreFaces(const TopoDS_Shape&             solid,
910                         const StdMeshers_ViscousLayers* hyp,
911                         const TopoDS_Shape&             hypShape,
912                         set<TGeomID>&                   ignoreFaces);
913     bool makeLayer(_SolidData& data);
914     void setShapeData( _EdgesOnShape& eos, SMESH_subMesh* sm, _SolidData& data );
915     bool setEdgeData( _LayerEdge& edge, _EdgesOnShape& eos,
916                       SMESH_MesherHelper& helper, _SolidData& data);
917     gp_XYZ getFaceNormal(const SMDS_MeshNode* n,
918                          const TopoDS_Face&   face,
919                          SMESH_MesherHelper&  helper,
920                          bool&                isOK,
921                          bool                 shiftInside=false);
922     bool getFaceNormalAtSingularity(const gp_XY&        uv,
923                                     const TopoDS_Face&  face,
924                                     SMESH_MesherHelper& helper,
925                                     gp_Dir&             normal );
926     gp_XYZ getWeigthedNormal( const _LayerEdge*                edge );
927     gp_XYZ getNormalByOffset( _LayerEdge*                      edge,
928                               std::pair< TopoDS_Face, gp_XYZ > fId2Normal[],
929                               int                              nbFaces,
930                               bool                             lastNoOffset = false);
931     bool findNeiborsOnEdge(const _LayerEdge*     edge,
932                            const SMDS_MeshNode*& n1,
933                            const SMDS_MeshNode*& n2,
934                            _EdgesOnShape&        eos,
935                            _SolidData&           data);
936     void findSimplexTestEdges( _SolidData&                    data,
937                                vector< vector<_LayerEdge*> >& edgesByGeom);
938     void computeGeomSize( _SolidData& data );
939     bool findShapesToSmooth( _SolidData& data);
940     void limitStepSizeByCurvature( _SolidData&  data );
941     void limitStepSize( _SolidData&             data,
942                         const SMDS_MeshElement* face,
943                         const _LayerEdge*       maxCosinEdge );
944     void limitStepSize( _SolidData& data, const double minSize);
945     bool inflate(_SolidData& data);
946     bool smoothAndCheck(_SolidData& data, const int nbSteps, double & distToIntersection);
947     int  invalidateBadSmooth( _SolidData&               data,
948                               SMESH_MesherHelper&       helper,
949                               vector< _LayerEdge* >&    badSmooEdges,
950                               vector< _EdgesOnShape* >& eosC1,
951                               const int                 infStep );
952     void makeOffsetSurface( _EdgesOnShape& eos, SMESH_MesherHelper& );
953     void putOnOffsetSurface( _EdgesOnShape& eos, int infStep,
954                              vector< _EdgesOnShape* >& eosC1,
955                              int smooStep=0, int moveAll=false );
956     void findCollisionEdges( _SolidData& data, SMESH_MesherHelper& helper );
957     void findEdgesToUpdateNormalNearConvexFace( _ConvexFace &       convFace,
958                                                 _SolidData&         data,
959                                                 SMESH_MesherHelper& helper );
960     void limitMaxLenByCurvature( _SolidData& data, SMESH_MesherHelper& helper );
961     void limitMaxLenByCurvature( _LayerEdge* e1, _LayerEdge* e2,
962                                  _EdgesOnShape& eos1, _EdgesOnShape& eos2,
963                                  SMESH_MesherHelper& helper );
964     bool updateNormals( _SolidData& data, SMESH_MesherHelper& helper, int stepNb, double stepSize );
965     bool updateNormalsOfConvexFaces( _SolidData&         data,
966                                      SMESH_MesherHelper& helper,
967                                      int                 stepNb );
968     void updateNormalsOfC1Vertices( _SolidData& data );
969     bool updateNormalsOfSmoothed( _SolidData&         data,
970                                   SMESH_MesherHelper& helper,
971                                   const int           nbSteps,
972                                   const double        stepSize );
973     bool isNewNormalOk( _SolidData&   data,
974                         _LayerEdge&   edge,
975                         const gp_XYZ& newNormal);
976     bool refine(_SolidData& data);
977     bool shrink(_SolidData& data);
978     bool prepareEdgeToShrink( _LayerEdge& edge, _EdgesOnShape& eos,
979                               SMESH_MesherHelper& helper,
980                               const SMESHDS_SubMesh* faceSubMesh );
981     void restoreNoShrink( _LayerEdge& edge ) const;
982     void fixBadFaces(const TopoDS_Face&          F,
983                      SMESH_MesherHelper&         helper,
984                      const bool                  is2D,
985                      const int                   step,
986                      set<const SMDS_MeshNode*> * involvedNodes=NULL);
987     bool addBoundaryElements(_SolidData& data);
988
989     bool error( const string& text, int solidID=-1 );
990     SMESHDS_Mesh* getMeshDS() const { return _mesh->GetMeshDS(); }
991
992     // debug
993     void makeGroupOfLE();
994
995     SMESH_Mesh*                _mesh;
996     SMESH_ComputeErrorPtr      _error;
997
998     vector<                    _SolidData >  _sdVec;
999     TopTools_IndexedMapOfShape _solids; // to find _SolidData by a solid
1000     TopTools_MapOfShape        _shrinkedFaces;
1001
1002     int                        _tmpFaceID;
1003     PyDump*                    _pyDump;
1004   };
1005   //--------------------------------------------------------------------------------
1006   /*!
1007    * \brief Shrinker of nodes on the EDGE
1008    */
1009   class _Shrinker1D
1010   {
1011     TopoDS_Edge                   _geomEdge;
1012     vector<double>                _initU;
1013     vector<double>                _normPar;
1014     vector<const SMDS_MeshNode*>  _nodes;
1015     const _LayerEdge*             _edges[2];
1016     bool                          _done;
1017   public:
1018     void AddEdge( const _LayerEdge* e, _EdgesOnShape& eos, SMESH_MesherHelper& helper );
1019     void Compute(bool set3D, SMESH_MesherHelper& helper);
1020     void RestoreParams();
1021     void SwapSrcTgtNodes(SMESHDS_Mesh* mesh);
1022     const TopoDS_Edge& GeomEdge() const { return _geomEdge; }
1023     const SMDS_MeshNode* TgtNode( bool is2nd ) const
1024     { return _edges[is2nd] ? _edges[is2nd]->_nodes.back() : 0; }
1025     const SMDS_MeshNode* SrcNode( bool is2nd ) const
1026     { return _edges[is2nd] ? _edges[is2nd]->_nodes[0] : 0; }
1027   };
1028   //--------------------------------------------------------------------------------
1029   /*!
1030    * \brief Smoother of _LayerEdge's on EDGE.
1031    */
1032   struct _Smoother1D
1033   {
1034     struct OffPnt // point of the offsetted EDGE
1035     {
1036       gp_XYZ      _xyz;    // coord of a point inflated from EDGE w/o smooth
1037       double      _len;    // length reached at previous inflation step
1038       double      _param;  // on EDGE
1039       _2NearEdges _2edges; // 2 neighbor _LayerEdge's
1040       gp_XYZ      _edgeDir;// EDGE tangent at _param
1041       double Distance( const OffPnt& p ) const { return ( _xyz - p._xyz ).Modulus(); }
1042     };
1043     vector< OffPnt >   _offPoints;
1044     vector< double >   _leParams; // normalized param of _eos._edges on EDGE
1045     Handle(Geom_Curve) _anaCurve; // for analytic smooth
1046     _LayerEdge         _leOnV[2]; // _LayerEdge's holding normal to the EDGE at VERTEXes
1047     gp_XYZ             _edgeDir[2]; // tangent at VERTEXes
1048     size_t             _iSeg[2];  // index of segment where extreme tgt node is projected
1049     _EdgesOnShape&     _eos;
1050     double             _curveLen; // length of the EDGE
1051     std::pair<int,int> _eToSmooth[2]; // <from,to> indices of _LayerEdge's in _eos
1052
1053     static Handle(Geom_Curve) CurveForSmooth( const TopoDS_Edge&  E,
1054                                               _EdgesOnShape&      eos,
1055                                               SMESH_MesherHelper& helper);
1056
1057     _Smoother1D( Handle(Geom_Curve) curveForSmooth,
1058                  _EdgesOnShape&     eos )
1059       : _anaCurve( curveForSmooth ), _eos( eos )
1060     {
1061     }
1062     bool Perform(_SolidData&                    data,
1063                  Handle(ShapeAnalysis_Surface)& surface,
1064                  const TopoDS_Face&             F,
1065                  SMESH_MesherHelper&            helper );
1066
1067     void prepare(_SolidData& data );
1068
1069     void findEdgesToSmooth();
1070
1071     bool isToSmooth( int iE );
1072
1073     bool smoothAnalyticEdge( _SolidData&                    data,
1074                              Handle(ShapeAnalysis_Surface)& surface,
1075                              const TopoDS_Face&             F,
1076                              SMESH_MesherHelper&            helper);
1077     bool smoothComplexEdge( _SolidData&                    data,
1078                             Handle(ShapeAnalysis_Surface)& surface,
1079                             const TopoDS_Face&             F,
1080                             SMESH_MesherHelper&            helper);
1081     gp_XYZ getNormalNormal( const gp_XYZ & normal,
1082                             const gp_XYZ&  edgeDir);
1083     _LayerEdge* getLEdgeOnV( bool is2nd )
1084     {
1085       return _eos._edges[ is2nd ? _eos._edges.size()-1 : 0 ]->_2neibors->_edges[ is2nd ];
1086     }
1087     bool isAnalytic() const { return !_anaCurve.IsNull(); }
1088   };
1089   //--------------------------------------------------------------------------------
1090   /*!
1091    * \brief Class of temporary mesh face.
1092    * We can't use SMDS_FaceOfNodes since it's impossible to set it's ID which is
1093    * needed because SMESH_ElementSearcher internaly uses set of elements sorted by ID
1094    */
1095   struct _TmpMeshFace : public SMDS_MeshElement
1096   {
1097     vector<const SMDS_MeshNode* > _nn;
1098     _TmpMeshFace( const vector<const SMDS_MeshNode*>& nodes,
1099                   int id, int faceID=-1, int idInFace=-1):
1100       SMDS_MeshElement(id), _nn(nodes) { setShapeId(faceID); setIdInShape(idInFace); }
1101     virtual const SMDS_MeshNode* GetNode(const int ind) const { return _nn[ind]; }
1102     virtual SMDSAbs_ElementType  GetType() const              { return SMDSAbs_Face; }
1103     virtual vtkIdType GetVtkType() const                      { return -1; }
1104     virtual SMDSAbs_EntityType   GetEntityType() const        { return SMDSEntity_Last; }
1105     virtual SMDSAbs_GeometryType GetGeomType() const
1106     { return _nn.size() == 3 ? SMDSGeom_TRIANGLE : SMDSGeom_QUADRANGLE; }
1107     virtual SMDS_ElemIteratorPtr elementsIterator(SMDSAbs_ElementType) const
1108     { return SMDS_ElemIteratorPtr( new SMDS_NodeVectorElemIterator( _nn.begin(), _nn.end()));}
1109   };
1110   //--------------------------------------------------------------------------------
1111   /*!
1112    * \brief Class of temporary mesh face storing _LayerEdge it's based on
1113    */
1114   struct _TmpMeshFaceOnEdge : public _TmpMeshFace
1115   {
1116     _LayerEdge *_le1, *_le2;
1117     _TmpMeshFaceOnEdge( _LayerEdge* le1, _LayerEdge* le2, int ID ):
1118       _TmpMeshFace( vector<const SMDS_MeshNode*>(4), ID ), _le1(le1), _le2(le2)
1119     {
1120       _nn[0]=_le1->_nodes[0];
1121       _nn[1]=_le1->_nodes.back();
1122       _nn[2]=_le2->_nodes.back();
1123       _nn[3]=_le2->_nodes[0];
1124     }
1125     gp_XYZ GetDir() const // return average direction of _LayerEdge's, normal to EDGE
1126     {
1127       SMESH_TNodeXYZ p0s( _nn[0] );
1128       SMESH_TNodeXYZ p0t( _nn[1] );
1129       SMESH_TNodeXYZ p1t( _nn[2] );
1130       SMESH_TNodeXYZ p1s( _nn[3] );
1131       gp_XYZ  v0 = p0t - p0s;
1132       gp_XYZ  v1 = p1t - p1s;
1133       gp_XYZ v01 = p1s - p0s;
1134       gp_XYZ   n = ( v0 ^ v01 ) + ( v1 ^ v01 );
1135       gp_XYZ   d = v01 ^ n;
1136       d.Normalize();
1137       return d;
1138     }
1139     gp_XYZ GetDir(_LayerEdge* le1, _LayerEdge* le2) // return average direction of _LayerEdge's
1140     {
1141       _nn[0]=le1->_nodes[0];
1142       _nn[1]=le1->_nodes.back();
1143       _nn[2]=le2->_nodes.back();
1144       _nn[3]=le2->_nodes[0];
1145       return GetDir();
1146     }
1147   };
1148   //--------------------------------------------------------------------------------
1149   /*!
1150    * \brief Retriever of node coordinates either directly or from a surface by node UV.
1151    * \warning Location of a surface is ignored
1152    */
1153   struct _NodeCoordHelper
1154   {
1155     SMESH_MesherHelper&        _helper;
1156     const TopoDS_Face&         _face;
1157     Handle(Geom_Surface)       _surface;
1158     gp_XYZ (_NodeCoordHelper::* _fun)(const SMDS_MeshNode* n) const;
1159
1160     _NodeCoordHelper(const TopoDS_Face& F, SMESH_MesherHelper& helper, bool is2D)
1161       : _helper( helper ), _face( F )
1162     {
1163       if ( is2D )
1164       {
1165         TopLoc_Location loc;
1166         _surface = BRep_Tool::Surface( _face, loc );
1167       }
1168       if ( _surface.IsNull() )
1169         _fun = & _NodeCoordHelper::direct;
1170       else
1171         _fun = & _NodeCoordHelper::byUV;
1172     }
1173     gp_XYZ operator()(const SMDS_MeshNode* n) const { return (this->*_fun)( n ); }
1174
1175   private:
1176     gp_XYZ direct(const SMDS_MeshNode* n) const
1177     {
1178       return SMESH_TNodeXYZ( n );
1179     }
1180     gp_XYZ byUV  (const SMDS_MeshNode* n) const
1181     {
1182       gp_XY uv = _helper.GetNodeUV( _face, n );
1183       return _surface->Value( uv.X(), uv.Y() ).XYZ();
1184     }
1185   };
1186
1187   //================================================================================
1188   /*!
1189    * \brief Check angle between vectors 
1190    */
1191   //================================================================================
1192
1193   inline bool isLessAngle( const gp_Vec& v1, const gp_Vec& v2, const double cos )
1194   {
1195     double dot = v1 * v2; // cos * |v1| * |v2|
1196     double l1  = v1.SquareMagnitude();
1197     double l2  = v2.SquareMagnitude();
1198     return (( dot * cos >= 0 ) && 
1199             ( dot * dot ) / l1 / l2 >= ( cos * cos ));
1200   }
1201
1202 } // namespace VISCOUS_3D
1203
1204
1205
1206 //================================================================================
1207 // StdMeshers_ViscousLayers hypothesis
1208 //
1209 StdMeshers_ViscousLayers::StdMeshers_ViscousLayers(int hypId, SMESH_Gen* gen)
1210   :SMESH_Hypothesis(hypId, gen),
1211    _isToIgnoreShapes(1), _nbLayers(1), _thickness(1), _stretchFactor(1),
1212    _method( SURF_OFFSET_SMOOTH )
1213 {
1214   _name = StdMeshers_ViscousLayers::GetHypType();
1215   _param_algo_dim = -3; // auxiliary hyp used by 3D algos
1216 } // --------------------------------------------------------------------------------
1217 void StdMeshers_ViscousLayers::SetBndShapes(const std::vector<int>& faceIds, bool toIgnore)
1218 {
1219   if ( faceIds != _shapeIds )
1220     _shapeIds = faceIds, NotifySubMeshesHypothesisModification();
1221   if ( _isToIgnoreShapes != toIgnore )
1222     _isToIgnoreShapes = toIgnore, NotifySubMeshesHypothesisModification();
1223 } // --------------------------------------------------------------------------------
1224 void StdMeshers_ViscousLayers::SetTotalThickness(double thickness)
1225 {
1226   if ( thickness != _thickness )
1227     _thickness = thickness, NotifySubMeshesHypothesisModification();
1228 } // --------------------------------------------------------------------------------
1229 void StdMeshers_ViscousLayers::SetNumberLayers(int nb)
1230 {
1231   if ( _nbLayers != nb )
1232     _nbLayers = nb, NotifySubMeshesHypothesisModification();
1233 } // --------------------------------------------------------------------------------
1234 void StdMeshers_ViscousLayers::SetStretchFactor(double factor)
1235 {
1236   if ( _stretchFactor != factor )
1237     _stretchFactor = factor, NotifySubMeshesHypothesisModification();
1238 } // --------------------------------------------------------------------------------
1239 void StdMeshers_ViscousLayers::SetMethod( ExtrusionMethod method )
1240 {
1241   if ( _method != method )
1242     _method = method, NotifySubMeshesHypothesisModification();
1243 } // --------------------------------------------------------------------------------
1244 SMESH_ProxyMesh::Ptr
1245 StdMeshers_ViscousLayers::Compute(SMESH_Mesh&         theMesh,
1246                                   const TopoDS_Shape& theShape,
1247                                   const bool          toMakeN2NMap) const
1248 {
1249   using namespace VISCOUS_3D;
1250   _ViscousBuilder builder;
1251   SMESH_ComputeErrorPtr err = builder.Compute( theMesh, theShape );
1252   if ( err && !err->IsOK() )
1253     return SMESH_ProxyMesh::Ptr();
1254
1255   vector<SMESH_ProxyMesh::Ptr> components;
1256   TopExp_Explorer exp( theShape, TopAbs_SOLID );
1257   for ( ; exp.More(); exp.Next() )
1258   {
1259     if ( _MeshOfSolid* pm =
1260          _ViscousListener::GetSolidMesh( &theMesh, exp.Current(), /*toCreate=*/false))
1261     {
1262       if ( toMakeN2NMap && !pm->_n2nMapComputed )
1263         if ( !builder.MakeN2NMap( pm ))
1264           return SMESH_ProxyMesh::Ptr();
1265       components.push_back( SMESH_ProxyMesh::Ptr( pm ));
1266       pm->myIsDeletable = false; // it will de deleted by boost::shared_ptr
1267
1268       if ( pm->_warning && !pm->_warning->IsOK() )
1269       {
1270         SMESH_subMesh* sm = theMesh.GetSubMesh( exp.Current() );
1271         SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1272         if ( !smError || smError->IsOK() )
1273           smError = pm->_warning;
1274       }
1275     }
1276     _ViscousListener::RemoveSolidMesh ( &theMesh, exp.Current() );
1277   }
1278   switch ( components.size() )
1279   {
1280   case 0: break;
1281
1282   case 1: return components[0];
1283
1284   default: return SMESH_ProxyMesh::Ptr( new SMESH_ProxyMesh( components ));
1285   }
1286   return SMESH_ProxyMesh::Ptr();
1287 } // --------------------------------------------------------------------------------
1288 std::ostream & StdMeshers_ViscousLayers::SaveTo(std::ostream & save)
1289 {
1290   save << " " << _nbLayers
1291        << " " << _thickness
1292        << " " << _stretchFactor
1293        << " " << _shapeIds.size();
1294   for ( size_t i = 0; i < _shapeIds.size(); ++i )
1295     save << " " << _shapeIds[i];
1296   save << " " << !_isToIgnoreShapes; // negate to keep the behavior in old studies.
1297   save << " " << _method;
1298   return save;
1299 } // --------------------------------------------------------------------------------
1300 std::istream & StdMeshers_ViscousLayers::LoadFrom(std::istream & load)
1301 {
1302   int nbFaces, faceID, shapeToTreat, method;
1303   load >> _nbLayers >> _thickness >> _stretchFactor >> nbFaces;
1304   while ( (int) _shapeIds.size() < nbFaces && load >> faceID )
1305     _shapeIds.push_back( faceID );
1306   if ( load >> shapeToTreat ) {
1307     _isToIgnoreShapes = !shapeToTreat;
1308     if ( load >> method )
1309       _method = (ExtrusionMethod) method;
1310   }
1311   else {
1312     _isToIgnoreShapes = true; // old behavior
1313   }
1314   return load;
1315 } // --------------------------------------------------------------------------------
1316 bool StdMeshers_ViscousLayers::SetParametersByMesh(const SMESH_Mesh*   theMesh,
1317                                                    const TopoDS_Shape& theShape)
1318 {
1319   // TODO
1320   return false;
1321 } // --------------------------------------------------------------------------------
1322 SMESH_ComputeErrorPtr
1323 StdMeshers_ViscousLayers::CheckHypothesis(SMESH_Mesh&                          theMesh,
1324                                           const TopoDS_Shape&                  theShape,
1325                                           SMESH_Hypothesis::Hypothesis_Status& theStatus)
1326 {
1327   VISCOUS_3D::_ViscousBuilder builder;
1328   SMESH_ComputeErrorPtr err = builder.CheckHypotheses( theMesh, theShape );
1329   if ( err && !err->IsOK() )
1330     theStatus = SMESH_Hypothesis::HYP_INCOMPAT_HYPS;
1331   else
1332     theStatus = SMESH_Hypothesis::HYP_OK;
1333
1334   return err;
1335 }
1336 // --------------------------------------------------------------------------------
1337 bool StdMeshers_ViscousLayers::IsShapeWithLayers(int shapeIndex) const
1338 {
1339   bool isIn =
1340     ( std::find( _shapeIds.begin(), _shapeIds.end(), shapeIndex ) != _shapeIds.end() );
1341   return IsToIgnoreShapes() ? !isIn : isIn;
1342 }
1343 // END StdMeshers_ViscousLayers hypothesis
1344 //================================================================================
1345
1346 namespace VISCOUS_3D
1347 {
1348   gp_XYZ getEdgeDir( const TopoDS_Edge& E, const TopoDS_Vertex& fromV )
1349   {
1350     gp_Vec dir;
1351     double f,l;
1352     Handle(Geom_Curve) c = BRep_Tool::Curve( E, f, l );
1353     if ( c.IsNull() ) return gp_XYZ( Precision::Infinite(), 1e100, 1e100 );
1354     gp_Pnt p = BRep_Tool::Pnt( fromV );
1355     double distF = p.SquareDistance( c->Value( f ));
1356     double distL = p.SquareDistance( c->Value( l ));
1357     c->D1(( distF < distL ? f : l), p, dir );
1358     if ( distL < distF ) dir.Reverse();
1359     return dir.XYZ();
1360   }
1361   //--------------------------------------------------------------------------------
1362   gp_XYZ getEdgeDir( const TopoDS_Edge& E, const SMDS_MeshNode* atNode,
1363                      SMESH_MesherHelper& helper)
1364   {
1365     gp_Vec dir;
1366     double f,l; gp_Pnt p;
1367     Handle(Geom_Curve) c = BRep_Tool::Curve( E, f, l );
1368     if ( c.IsNull() ) return gp_XYZ( Precision::Infinite(), 1e100, 1e100 );
1369     double u = helper.GetNodeU( E, atNode );
1370     c->D1( u, p, dir );
1371     return dir.XYZ();
1372   }
1373   //--------------------------------------------------------------------------------
1374   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Vertex& fromV,
1375                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper, bool& ok,
1376                      double* cosin=0);
1377   //--------------------------------------------------------------------------------
1378   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Edge& fromE,
1379                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper, bool& ok)
1380   {
1381     double f,l;
1382     Handle(Geom_Curve) c = BRep_Tool::Curve( fromE, f, l );
1383     if ( c.IsNull() )
1384     {
1385       TopoDS_Vertex v = helper.IthVertex( 0, fromE );
1386       return getFaceDir( F, v, node, helper, ok );
1387     }
1388     gp_XY uv = helper.GetNodeUV( F, node, 0, &ok );
1389     Handle(Geom_Surface) surface = BRep_Tool::Surface( F );
1390     gp_Pnt p; gp_Vec du, dv, norm;
1391     surface->D1( uv.X(),uv.Y(), p, du,dv );
1392     norm = du ^ dv;
1393
1394     double u = helper.GetNodeU( fromE, node, 0, &ok );
1395     c->D1( u, p, du );
1396     TopAbs_Orientation o = helper.GetSubShapeOri( F.Oriented(TopAbs_FORWARD), fromE);
1397     if ( o == TopAbs_REVERSED )
1398       du.Reverse();
1399
1400     gp_Vec dir = norm ^ du;
1401
1402     if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX &&
1403          helper.IsClosedEdge( fromE ))
1404     {
1405       if ( fabs(u-f) < fabs(u-l)) c->D1( l, p, dv );
1406       else                        c->D1( f, p, dv );
1407       if ( o == TopAbs_REVERSED )
1408         dv.Reverse();
1409       gp_Vec dir2 = norm ^ dv;
1410       dir = dir.Normalized() + dir2.Normalized();
1411     }
1412     return dir.XYZ();
1413   }
1414   //--------------------------------------------------------------------------------
1415   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Vertex& fromV,
1416                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper,
1417                      bool& ok, double* cosin)
1418   {
1419     TopoDS_Face faceFrw = F;
1420     faceFrw.Orientation( TopAbs_FORWARD );
1421     //double f,l; TopLoc_Location loc;
1422     TopoDS_Edge edges[2]; // sharing a vertex
1423     size_t nbEdges = 0;
1424     {
1425       TopoDS_Vertex VV[2];
1426       TopExp_Explorer exp( faceFrw, TopAbs_EDGE );
1427       for ( ; exp.More() && nbEdges < 2; exp.Next() )
1428       {
1429         const TopoDS_Edge& e = TopoDS::Edge( exp.Current() );
1430         if ( SMESH_Algo::isDegenerated( e )) continue;
1431         TopExp::Vertices( e, VV[0], VV[1], /*CumOri=*/true );
1432         if ( VV[1].IsSame( fromV )) {
1433           nbEdges += edges[ 0 ].IsNull();
1434           edges[ 0 ] = e;
1435         }
1436         else if ( VV[0].IsSame( fromV )) {
1437           nbEdges += edges[ 1 ].IsNull();
1438           edges[ 1 ] = e;
1439         }
1440       }
1441     }
1442     gp_XYZ dir(0,0,0), edgeDir[2];
1443     if ( nbEdges == 2 )
1444     {
1445       // get dirs of edges going fromV
1446       ok = true;
1447       for ( size_t i = 0; i < nbEdges && ok; ++i )
1448       {
1449         edgeDir[i] = getEdgeDir( edges[i], fromV );
1450         double size2 = edgeDir[i].SquareModulus();
1451         if (( ok = size2 > numeric_limits<double>::min() ))
1452           edgeDir[i] /= sqrt( size2 );
1453       }
1454       if ( !ok ) return dir;
1455
1456       // get angle between the 2 edges
1457       gp_Vec faceNormal;
1458       double angle = helper.GetAngle( edges[0], edges[1], faceFrw, fromV, &faceNormal );
1459       if ( Abs( angle ) < 5 * M_PI/180 )
1460       {
1461         dir = ( faceNormal.XYZ() ^ edgeDir[0].Reversed()) + ( faceNormal.XYZ() ^ edgeDir[1] );
1462       }
1463       else
1464       {
1465         dir = edgeDir[0] + edgeDir[1];
1466         if ( angle < 0 )
1467           dir.Reverse();
1468       }
1469       if ( cosin ) {
1470         double angle = gp_Vec( edgeDir[0] ).Angle( dir );
1471         *cosin = Cos( angle );
1472       }
1473     }
1474     else if ( nbEdges == 1 )
1475     {
1476       dir = getFaceDir( faceFrw, edges[ edges[0].IsNull() ], node, helper, ok );
1477       if ( cosin ) *cosin = 1.;
1478     }
1479     else
1480     {
1481       ok = false;
1482     }
1483
1484     return dir;
1485   }
1486
1487   //================================================================================
1488   /*!
1489    * \brief Finds concave VERTEXes of a FACE
1490    */
1491   //================================================================================
1492
1493   bool getConcaveVertices( const TopoDS_Face&  F,
1494                            SMESH_MesherHelper& helper,
1495                            set< TGeomID >*     vertices = 0)
1496   {
1497     // check angles at VERTEXes
1498     TError error;
1499     TSideVector wires = StdMeshers_FaceSide::GetFaceWires( F, *helper.GetMesh(), 0, error );
1500     for ( size_t iW = 0; iW < wires.size(); ++iW )
1501     {
1502       const int nbEdges = wires[iW]->NbEdges();
1503       if ( nbEdges < 2 && SMESH_Algo::isDegenerated( wires[iW]->Edge(0)))
1504         continue;
1505       for ( int iE1 = 0; iE1 < nbEdges; ++iE1 )
1506       {
1507         if ( SMESH_Algo::isDegenerated( wires[iW]->Edge( iE1 ))) continue;
1508         int iE2 = ( iE1 + 1 ) % nbEdges;
1509         while ( SMESH_Algo::isDegenerated( wires[iW]->Edge( iE2 )))
1510           iE2 = ( iE2 + 1 ) % nbEdges;
1511         TopoDS_Vertex V = wires[iW]->FirstVertex( iE2 );
1512         double angle = helper.GetAngle( wires[iW]->Edge( iE1 ),
1513                                         wires[iW]->Edge( iE2 ), F, V );
1514         if ( angle < -5. * M_PI / 180. )
1515         {
1516           if ( !vertices )
1517             return true;
1518           vertices->insert( helper.GetMeshDS()->ShapeToIndex( V ));
1519         }
1520       }
1521     }
1522     return vertices ? !vertices->empty() : false;
1523   }
1524
1525   //================================================================================
1526   /*!
1527    * \brief Returns true if a FACE is bound by a concave EDGE
1528    */
1529   //================================================================================
1530
1531   bool isConcave( const TopoDS_Face&  F,
1532                   SMESH_MesherHelper& helper,
1533                   set< TGeomID >*     vertices = 0 )
1534   {
1535     bool isConcv = false;
1536     // if ( helper.Count( F, TopAbs_WIRE, /*useMap=*/false) > 1 )
1537     //   return true;
1538     gp_Vec2d drv1, drv2;
1539     gp_Pnt2d p;
1540     TopExp_Explorer eExp( F.Oriented( TopAbs_FORWARD ), TopAbs_EDGE );
1541     for ( ; eExp.More(); eExp.Next() )
1542     {
1543       const TopoDS_Edge& E = TopoDS::Edge( eExp.Current() );
1544       if ( SMESH_Algo::isDegenerated( E )) continue;
1545       // check if 2D curve is concave
1546       BRepAdaptor_Curve2d curve( E, F );
1547       const int nbIntervals = curve.NbIntervals( GeomAbs_C2 );
1548       TColStd_Array1OfReal intervals(1, nbIntervals + 1 );
1549       curve.Intervals( intervals, GeomAbs_C2 );
1550       bool isConvex = true;
1551       for ( int i = 1; i <= nbIntervals && isConvex; ++i )
1552       {
1553         double u1 = intervals( i );
1554         double u2 = intervals( i+1 );
1555         curve.D2( 0.5*( u1+u2 ), p, drv1, drv2 );
1556         double cross = drv1 ^ drv2;
1557         if ( E.Orientation() == TopAbs_REVERSED )
1558           cross = -cross;
1559         isConvex = ( cross > -1e-9 ); // 0.1 );
1560       }
1561       if ( !isConvex )
1562       {
1563         //cout << "Concave FACE " << helper.GetMeshDS()->ShapeToIndex( F ) << endl;
1564         isConcv = true;
1565         if ( vertices )
1566           break;
1567         else
1568           return true;
1569       }
1570     }
1571
1572     // check angles at VERTEXes
1573     if ( getConcaveVertices( F, helper, vertices ))
1574       isConcv = true;
1575
1576     return isConcv;
1577   }
1578
1579   //================================================================================
1580   /*!
1581    * \brief Computes mimimal distance of face in-FACE nodes from an EDGE
1582    *  \param [in] face - the mesh face to treat
1583    *  \param [in] nodeOnEdge - a node on the EDGE
1584    *  \param [out] faceSize - the computed distance
1585    *  \return bool - true if faceSize computed
1586    */
1587   //================================================================================
1588
1589   bool getDistFromEdge( const SMDS_MeshElement* face,
1590                         const SMDS_MeshNode*    nodeOnEdge,
1591                         double &                faceSize )
1592   {
1593     faceSize = Precision::Infinite();
1594     bool done = false;
1595
1596     int nbN  = face->NbCornerNodes();
1597     int iOnE = face->GetNodeIndex( nodeOnEdge );
1598     int iNext[2] = { SMESH_MesherHelper::WrapIndex( iOnE+1, nbN ),
1599                      SMESH_MesherHelper::WrapIndex( iOnE-1, nbN ) };
1600     const SMDS_MeshNode* nNext[2] = { face->GetNode( iNext[0] ),
1601                                       face->GetNode( iNext[1] ) };
1602     gp_XYZ segVec, segEnd = SMESH_TNodeXYZ( nodeOnEdge ); // segment on EDGE
1603     double segLen = -1.;
1604     // look for two neighbor not in-FACE nodes of face
1605     for ( int i = 0; i < 2; ++i )
1606     {
1607       if ( nNext[i]->GetPosition()->GetDim() != 2 &&
1608            nNext[i]->GetID() < nodeOnEdge->GetID() )
1609       {
1610         // look for an in-FACE node
1611         for ( int iN = 0; iN < nbN; ++iN )
1612         {
1613           if ( iN == iOnE || iN == iNext[i] )
1614             continue;
1615           SMESH_TNodeXYZ pInFace = face->GetNode( iN );
1616           gp_XYZ v = pInFace - segEnd;
1617           if ( segLen < 0 )
1618           {
1619             segVec = SMESH_TNodeXYZ( nNext[i] ) - segEnd;
1620             segLen = segVec.Modulus();
1621           }
1622           double distToSeg = v.Crossed( segVec ).Modulus() / segLen;
1623           faceSize = Min( faceSize, distToSeg );
1624           done = true;
1625         }
1626         segLen = -1;
1627       }
1628     }
1629     return done;
1630   }
1631   //================================================================================
1632   /*!
1633    * \brief Return direction of axis or revolution of a surface
1634    */
1635   //================================================================================
1636
1637   bool getRovolutionAxis( const Adaptor3d_Surface& surface,
1638                           gp_Dir &                 axis )
1639   {
1640     switch ( surface.GetType() ) {
1641     case GeomAbs_Cone:
1642     {
1643       gp_Cone cone = surface.Cone();
1644       axis = cone.Axis().Direction();
1645       break;
1646     }
1647     case GeomAbs_Sphere:
1648     {
1649       gp_Sphere sphere = surface.Sphere();
1650       axis = sphere.Position().Direction();
1651       break;
1652     }
1653     case GeomAbs_SurfaceOfRevolution:
1654     {
1655       axis = surface.AxeOfRevolution().Direction();
1656       break;
1657     }
1658     //case GeomAbs_SurfaceOfExtrusion:
1659     case GeomAbs_OffsetSurface:
1660     {
1661       Handle(Adaptor3d_HSurface) base = surface.BasisSurface();
1662       return getRovolutionAxis( base->Surface(), axis );
1663     }
1664     default: return false;
1665     }
1666     return true;
1667   }
1668
1669   //--------------------------------------------------------------------------------
1670   // DEBUG. Dump intermediate node positions into a python script
1671   // HOWTO use: run python commands written in a console to see
1672   //  construction steps of viscous layers
1673 #ifdef __myDEBUG
1674   ostream* py;
1675   int      theNbPyFunc;
1676   struct PyDump
1677   {
1678     PyDump(SMESH_Mesh& m) {
1679       int tag = 3 + m.GetId();
1680       const char* fname = "/tmp/viscous.py";
1681       cout << "execfile('"<<fname<<"')"<<endl;
1682       py = _pyStream = new ofstream(fname);
1683       *py << "import SMESH" << endl
1684           << "from salome.smesh import smeshBuilder" << endl
1685           << "smesh  = smeshBuilder.New()" << endl
1686           << "meshSO = salome.myStudy.FindObjectID('0:1:2:" << tag <<"')" << endl
1687           << "mesh   = smesh.Mesh( meshSO.GetObject() )"<<endl;
1688       theNbPyFunc = 0;
1689     }
1690     void Finish() {
1691       if (py) {
1692         *py << "mesh.GroupOnFilter(SMESH.VOLUME,'Viscous Prisms',"
1693           "smesh.GetFilter(SMESH.VOLUME,SMESH.FT_ElemGeomType,'=',SMESH.Geom_PENTA))"<<endl;
1694         *py << "mesh.GroupOnFilter(SMESH.VOLUME,'Neg Volumes',"
1695           "smesh.GetFilter(SMESH.VOLUME,SMESH.FT_Volume3D,'<',0))"<<endl;
1696       }
1697       delete py; py=0;
1698     }
1699     ~PyDump() { Finish(); cout << "NB FUNCTIONS: " << theNbPyFunc << endl; }
1700     struct MyStream : public ostream
1701     {
1702       template <class T> ostream & operator<<( const T &anything ) { return *this ; }
1703     };
1704     void Pause() { py = &_mystream; }
1705     void Resume() { py = _pyStream; }
1706     MyStream _mystream;
1707     ostream* _pyStream;
1708   };
1709 #define dumpFunction(f) { _dumpFunction(f, __LINE__);}
1710 #define dumpMove(n)     { _dumpMove(n, __LINE__);}
1711 #define dumpMoveComm(n,txt) { _dumpMove(n, __LINE__, txt);}
1712 #define dumpCmd(txt)    { _dumpCmd(txt, __LINE__);}
1713   void _dumpFunction(const string& fun, int ln)
1714   { if (py) *py<< "def "<<fun<<"(): # "<< ln <<endl; cout<<fun<<"()"<<endl; ++theNbPyFunc; }
1715   void _dumpMove(const SMDS_MeshNode* n, int ln, const char* txt="")
1716   { if (py) *py<< "  mesh.MoveNode( "<<n->GetID()<< ", "<< n->X()
1717                << ", "<<n->Y()<<", "<< n->Z()<< ")\t\t # "<< ln <<" "<< txt << endl; }
1718   void _dumpCmd(const string& txt, int ln)
1719   { if (py) *py<< "  "<<txt<<" # "<< ln <<endl; }
1720   void dumpFunctionEnd()
1721   { if (py) *py<< "  return"<< endl; }
1722   void dumpChangeNodes( const SMDS_MeshElement* f )
1723   { if (py) { *py<< "  mesh.ChangeElemNodes( " << f->GetID()<<", [";
1724       for ( int i=1; i < f->NbNodes(); ++i ) *py << f->GetNode(i-1)->GetID()<<", ";
1725       *py << f->GetNode( f->NbNodes()-1 )->GetID() << " ])"<< endl; }}
1726 #define debugMsg( txt ) { cout << "# "<< txt << " (line: " << __LINE__ << ")" << endl; }
1727
1728 #else
1729
1730   struct PyDump { PyDump(SMESH_Mesh&) {} void Finish() {} void Pause() {} void Resume() {} };
1731 #define dumpFunction(f) f
1732 #define dumpMove(n)
1733 #define dumpMoveComm(n,txt)
1734 #define dumpCmd(txt)
1735 #define dumpFunctionEnd()
1736 #define dumpChangeNodes(f) { if(f) {} } // prevent "unused variable 'f'" warning
1737 #define debugMsg( txt ) {}
1738
1739 #endif
1740 }
1741
1742 using namespace VISCOUS_3D;
1743
1744 //================================================================================
1745 /*!
1746  * \brief Constructor of _ViscousBuilder
1747  */
1748 //================================================================================
1749
1750 _ViscousBuilder::_ViscousBuilder()
1751 {
1752   _error = SMESH_ComputeError::New(COMPERR_OK);
1753   _tmpFaceID = 0;
1754 }
1755
1756 //================================================================================
1757 /*!
1758  * \brief Stores error description and returns false
1759  */
1760 //================================================================================
1761
1762 bool _ViscousBuilder::error(const string& text, int solidId )
1763 {
1764   const string prefix = string("Viscous layers builder: ");
1765   _error->myName    = COMPERR_ALGO_FAILED;
1766   _error->myComment = prefix + text;
1767   if ( _mesh )
1768   {
1769     SMESH_subMesh* sm = _mesh->GetSubMeshContaining( solidId );
1770     if ( !sm && !_sdVec.empty() )
1771       sm = _mesh->GetSubMeshContaining( solidId = _sdVec[0]._index );
1772     if ( sm && sm->GetSubShape().ShapeType() == TopAbs_SOLID )
1773     {
1774       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1775       if ( smError && smError->myAlgo )
1776         _error->myAlgo = smError->myAlgo;
1777       smError = _error;
1778       sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
1779     }
1780     // set KO to all solids
1781     for ( size_t i = 0; i < _sdVec.size(); ++i )
1782     {
1783       if ( _sdVec[i]._index == solidId )
1784         continue;
1785       sm = _mesh->GetSubMesh( _sdVec[i]._solid );
1786       if ( !sm->IsEmpty() )
1787         continue;
1788       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1789       if ( !smError || smError->IsOK() )
1790       {
1791         smError = SMESH_ComputeError::New( COMPERR_ALGO_FAILED, prefix + "failed");
1792         sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
1793       }
1794     }
1795   }
1796   makeGroupOfLE(); // debug
1797
1798   return false;
1799 }
1800
1801 //================================================================================
1802 /*!
1803  * \brief At study restoration, restore event listeners used to clear an inferior
1804  *  dim sub-mesh modified by viscous layers
1805  */
1806 //================================================================================
1807
1808 void _ViscousBuilder::RestoreListeners()
1809 {
1810   // TODO
1811 }
1812
1813 //================================================================================
1814 /*!
1815  * \brief computes SMESH_ProxyMesh::SubMesh::_n2n
1816  */
1817 //================================================================================
1818
1819 bool _ViscousBuilder::MakeN2NMap( _MeshOfSolid* pm )
1820 {
1821   SMESH_subMesh* solidSM = pm->mySubMeshes.front();
1822   TopExp_Explorer fExp( solidSM->GetSubShape(), TopAbs_FACE );
1823   for ( ; fExp.More(); fExp.Next() )
1824   {
1825     SMESHDS_SubMesh* srcSmDS = pm->GetMeshDS()->MeshElements( fExp.Current() );
1826     const SMESH_ProxyMesh::SubMesh* prxSmDS = pm->GetProxySubMesh( fExp.Current() );
1827
1828     if ( !srcSmDS || !prxSmDS || !srcSmDS->NbElements() || !prxSmDS->NbElements() )
1829       continue;
1830     if ( srcSmDS->GetElements()->next() == prxSmDS->GetElements()->next())
1831       continue;
1832
1833     if ( srcSmDS->NbElements() != prxSmDS->NbElements() )
1834       return error( "Different nb elements in a source and a proxy sub-mesh", solidSM->GetId());
1835
1836     SMDS_ElemIteratorPtr srcIt = srcSmDS->GetElements();
1837     SMDS_ElemIteratorPtr prxIt = prxSmDS->GetElements();
1838     while( prxIt->more() )
1839     {
1840       const SMDS_MeshElement* fSrc = srcIt->next();
1841       const SMDS_MeshElement* fPrx = prxIt->next();
1842       if ( fSrc->NbNodes() != fPrx->NbNodes())
1843         return error( "Different elements in a source and a proxy sub-mesh", solidSM->GetId());
1844       for ( int i = 0 ; i < fPrx->NbNodes(); ++i )
1845         pm->setNode2Node( fSrc->GetNode(i), fPrx->GetNode(i), prxSmDS );
1846     }
1847   }
1848   pm->_n2nMapComputed = true;
1849   return true;
1850 }
1851
1852 //================================================================================
1853 /*!
1854  * \brief Does its job
1855  */
1856 //================================================================================
1857
1858 SMESH_ComputeErrorPtr _ViscousBuilder::Compute(SMESH_Mesh&         theMesh,
1859                                                const TopoDS_Shape& theShape)
1860 {
1861   _mesh = & theMesh;
1862
1863   // check if proxy mesh already computed
1864   TopExp_Explorer exp( theShape, TopAbs_SOLID );
1865   if ( !exp.More() )
1866     return error("No SOLID's in theShape"), _error;
1867
1868   if ( _ViscousListener::GetSolidMesh( _mesh, exp.Current(), /*toCreate=*/false))
1869     return SMESH_ComputeErrorPtr(); // everything already computed
1870
1871   PyDump debugDump( theMesh );
1872   _pyDump = &debugDump;
1873
1874   // TODO: ignore already computed SOLIDs 
1875   if ( !findSolidsWithLayers())
1876     return _error;
1877
1878   if ( !findFacesWithLayers() )
1879     return _error;
1880
1881   for ( size_t i = 0; i < _sdVec.size(); ++i )
1882   {
1883     size_t iSD = 0;
1884     for ( iSD = 0; iSD < _sdVec.size(); ++iSD ) // find next SOLID to compute
1885       if ( _sdVec[iSD]._before.IsEmpty() &&
1886            !_sdVec[iSD]._solid.IsNull() &&
1887            _sdVec[iSD]._n2eMap.empty() )
1888         break;
1889
1890     if ( ! makeLayer(_sdVec[iSD]) )   // create _LayerEdge's
1891       return _error;
1892
1893     if ( _sdVec[iSD]._n2eMap.size() == 0 ) // no layers in a SOLID
1894     {
1895       _sdVec[iSD]._solid.Nullify();
1896       continue;
1897     }
1898
1899     if ( ! inflate(_sdVec[iSD]) )     // increase length of _LayerEdge's
1900       return _error;
1901
1902     if ( ! refine(_sdVec[iSD]) )      // create nodes and prisms
1903       return _error;
1904
1905     if ( ! shrink(_sdVec[iSD]) )      // shrink 2D mesh on FACEs w/o layer
1906       return _error;
1907
1908     addBoundaryElements(_sdVec[iSD]); // create quadrangles on prism bare sides
1909
1910     const TopoDS_Shape& solid = _sdVec[iSD]._solid;
1911     for ( iSD = 0; iSD < _sdVec.size(); ++iSD )
1912       _sdVec[iSD]._before.Remove( solid );
1913   }
1914
1915   makeGroupOfLE(); // debug
1916   debugDump.Finish();
1917
1918   return _error;
1919 }
1920
1921 //================================================================================
1922 /*!
1923  * \brief Check validity of hypotheses
1924  */
1925 //================================================================================
1926
1927 SMESH_ComputeErrorPtr _ViscousBuilder::CheckHypotheses( SMESH_Mesh&         mesh,
1928                                                         const TopoDS_Shape& shape )
1929 {
1930   _mesh = & mesh;
1931
1932   if ( _ViscousListener::GetSolidMesh( _mesh, shape, /*toCreate=*/false))
1933     return SMESH_ComputeErrorPtr(); // everything already computed
1934
1935
1936   findSolidsWithLayers();
1937   bool ok = findFacesWithLayers( true );
1938
1939   // remove _MeshOfSolid's of _SolidData's
1940   for ( size_t i = 0; i < _sdVec.size(); ++i )
1941     _ViscousListener::RemoveSolidMesh( _mesh, _sdVec[i]._solid );
1942
1943   if ( !ok )
1944     return _error;
1945
1946   return SMESH_ComputeErrorPtr();
1947 }
1948
1949 //================================================================================
1950 /*!
1951  * \brief Finds SOLIDs to compute using viscous layers. Fills _sdVec
1952  */
1953 //================================================================================
1954
1955 bool _ViscousBuilder::findSolidsWithLayers()
1956 {
1957   // get all solids
1958   TopTools_IndexedMapOfShape allSolids;
1959   TopExp::MapShapes( _mesh->GetShapeToMesh(), TopAbs_SOLID, allSolids );
1960   _sdVec.reserve( allSolids.Extent());
1961
1962   SMESH_HypoFilter filter;
1963   for ( int i = 1; i <= allSolids.Extent(); ++i )
1964   {
1965     // find StdMeshers_ViscousLayers hyp assigned to the i-th solid
1966     SMESH_subMesh* sm = _mesh->GetSubMesh( allSolids(i) );
1967     if ( sm->GetSubMeshDS() && sm->GetSubMeshDS()->NbElements() > 0 )
1968       continue; // solid is already meshed
1969     SMESH_Algo* algo = sm->GetAlgo();
1970     if ( !algo ) continue;
1971     // TODO: check if algo is hidden
1972     const list <const SMESHDS_Hypothesis *> & allHyps =
1973       algo->GetUsedHypothesis(*_mesh, allSolids(i), /*ignoreAuxiliary=*/false);
1974     _SolidData* soData = 0;
1975     list< const SMESHDS_Hypothesis *>::const_iterator hyp = allHyps.begin();
1976     const StdMeshers_ViscousLayers* viscHyp = 0;
1977     for ( ; hyp != allHyps.end(); ++hyp )
1978       if (( viscHyp = dynamic_cast<const StdMeshers_ViscousLayers*>( *hyp )))
1979       {
1980         TopoDS_Shape hypShape;
1981         filter.Init( filter.Is( viscHyp ));
1982         _mesh->GetHypothesis( allSolids(i), filter, true, &hypShape );
1983
1984         if ( !soData )
1985         {
1986           _MeshOfSolid* proxyMesh = _ViscousListener::GetSolidMesh( _mesh,
1987                                                                     allSolids(i),
1988                                                                     /*toCreate=*/true);
1989           _sdVec.push_back( _SolidData( allSolids(i), proxyMesh ));
1990           soData = & _sdVec.back();
1991           soData->_index = getMeshDS()->ShapeToIndex( allSolids(i));
1992           soData->_helper = new SMESH_MesherHelper( *_mesh );
1993           soData->_helper->SetSubShape( allSolids(i) );
1994           _solids.Add( allSolids(i) );
1995         }
1996         soData->_hyps.push_back( viscHyp );
1997         soData->_hypShapes.push_back( hypShape );
1998       }
1999   }
2000   if ( _sdVec.empty() )
2001     return error
2002       ( SMESH_Comment(StdMeshers_ViscousLayers::GetHypType()) << " hypothesis not found",0);
2003
2004   return true;
2005 }
2006
2007 //================================================================================
2008 /*!
2009  * \brief Set a _SolidData to be computed before another
2010  */
2011 //================================================================================
2012
2013 bool _ViscousBuilder::setBefore( _SolidData& solidBefore, _SolidData& solidAfter )
2014 {
2015   // check possibility to set this order; get all solids before solidBefore
2016   TopTools_IndexedMapOfShape allSolidsBefore;
2017   allSolidsBefore.Add( solidBefore._solid );
2018   for ( int i = 1; i <= allSolidsBefore.Extent(); ++i )
2019   {
2020     int iSD = _solids.FindIndex( allSolidsBefore(i) );
2021     if ( iSD )
2022     {
2023       TopTools_MapIteratorOfMapOfShape soIt( _sdVec[ iSD-1 ]._before );
2024       for ( ; soIt.More(); soIt.Next() )
2025         allSolidsBefore.Add( soIt.Value() );
2026     }
2027   }
2028   if ( allSolidsBefore.Contains( solidAfter._solid ))
2029     return false;
2030
2031   for ( int i = 1; i <= allSolidsBefore.Extent(); ++i )
2032     solidAfter._before.Add( allSolidsBefore(i) );
2033
2034   return true;
2035 }
2036
2037 //================================================================================
2038 /*!
2039  * \brief
2040  */
2041 //================================================================================
2042
2043 bool _ViscousBuilder::findFacesWithLayers(const bool onlyWith)
2044 {
2045   SMESH_MesherHelper helper( *_mesh );
2046   TopExp_Explorer exp;
2047
2048   // collect all faces-to-ignore defined by hyp
2049   for ( size_t i = 0; i < _sdVec.size(); ++i )
2050   {
2051     // get faces-to-ignore defined by each hyp
2052     typedef const StdMeshers_ViscousLayers* THyp;
2053     typedef std::pair< set<TGeomID>, THyp > TFacesOfHyp;
2054     list< TFacesOfHyp > ignoreFacesOfHyps;
2055     list< THyp >::iterator              hyp = _sdVec[i]._hyps.begin();
2056     list< TopoDS_Shape >::iterator hypShape = _sdVec[i]._hypShapes.begin();
2057     for ( ; hyp != _sdVec[i]._hyps.end(); ++hyp, ++hypShape )
2058     {
2059       ignoreFacesOfHyps.push_back( TFacesOfHyp( set<TGeomID>(), *hyp ));
2060       getIgnoreFaces( _sdVec[i]._solid, *hyp, *hypShape, ignoreFacesOfHyps.back().first );
2061     }
2062
2063     // fill _SolidData::_face2hyp and check compatibility of hypotheses
2064     const int nbHyps = _sdVec[i]._hyps.size();
2065     if ( nbHyps > 1 )
2066     {
2067       // check if two hypotheses define different parameters for the same FACE
2068       list< TFacesOfHyp >::iterator igFacesOfHyp;
2069       for ( exp.Init( _sdVec[i]._solid, TopAbs_FACE ); exp.More(); exp.Next() )
2070       {
2071         const TGeomID faceID = getMeshDS()->ShapeToIndex( exp.Current() );
2072         THyp hyp = 0;
2073         igFacesOfHyp = ignoreFacesOfHyps.begin();
2074         for ( ; igFacesOfHyp != ignoreFacesOfHyps.end(); ++igFacesOfHyp )
2075           if ( ! igFacesOfHyp->first.count( faceID ))
2076           {
2077             if ( hyp )
2078               return error(SMESH_Comment("Several hypotheses define "
2079                                          "Viscous Layers on the face #") << faceID );
2080             hyp = igFacesOfHyp->second;
2081           }
2082         if ( hyp )
2083           _sdVec[i]._face2hyp.insert( make_pair( faceID, hyp ));
2084         else
2085           _sdVec[i]._ignoreFaceIds.insert( faceID );
2086       }
2087
2088       // check if two hypotheses define different number of viscous layers for
2089       // adjacent faces of a solid
2090       set< int > nbLayersSet;
2091       igFacesOfHyp = ignoreFacesOfHyps.begin();
2092       for ( ; igFacesOfHyp != ignoreFacesOfHyps.end(); ++igFacesOfHyp )
2093       {
2094         nbLayersSet.insert( igFacesOfHyp->second->GetNumberLayers() );
2095       }
2096       if ( nbLayersSet.size() > 1 )
2097       {
2098         for ( exp.Init( _sdVec[i]._solid, TopAbs_EDGE ); exp.More(); exp.Next() )
2099         {
2100           PShapeIteratorPtr fIt = helper.GetAncestors( exp.Current(), *_mesh, TopAbs_FACE );
2101           THyp hyp1 = 0, hyp2 = 0;
2102           while( const TopoDS_Shape* face = fIt->next() )
2103           {
2104             const TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
2105             map< TGeomID, THyp >::iterator f2h = _sdVec[i]._face2hyp.find( faceID );
2106             if ( f2h != _sdVec[i]._face2hyp.end() )
2107             {
2108               ( hyp1 ? hyp2 : hyp1 ) = f2h->second;
2109             }
2110           }
2111           if ( hyp1 && hyp2 &&
2112                hyp1->GetNumberLayers() != hyp2->GetNumberLayers() )
2113           {
2114             return error("Two hypotheses define different number of "
2115                          "viscous layers on adjacent faces");
2116           }
2117         }
2118       }
2119     } // if ( nbHyps > 1 )
2120     else
2121     {
2122       _sdVec[i]._ignoreFaceIds.swap( ignoreFacesOfHyps.back().first );
2123     }
2124   } // loop on _sdVec
2125
2126   if ( onlyWith ) // is called to check hypotheses compatibility only
2127     return true;
2128
2129   // fill _SolidData::_reversedFaceIds
2130   for ( size_t i = 0; i < _sdVec.size(); ++i )
2131   {
2132     exp.Init( _sdVec[i]._solid.Oriented( TopAbs_FORWARD ), TopAbs_FACE );
2133     for ( ; exp.More(); exp.Next() )
2134     {
2135       const TopoDS_Face& face = TopoDS::Face( exp.Current() );
2136       const TGeomID faceID = getMeshDS()->ShapeToIndex( face );
2137       if ( //!sdVec[i]._ignoreFaceIds.count( faceID ) &&
2138           helper.NbAncestors( face, *_mesh, TopAbs_SOLID ) > 1 &&
2139           helper.IsReversedSubMesh( face ))
2140       {
2141         _sdVec[i]._reversedFaceIds.insert( faceID );
2142       }
2143     }
2144   }
2145
2146   // Find FACEs to shrink mesh on (solution 2 in issue 0020832): fill in _shrinkShape2Shape
2147   TopTools_IndexedMapOfShape shapes;
2148   std::string structAlgoName = "Hexa_3D";
2149   for ( size_t i = 0; i < _sdVec.size(); ++i )
2150   {
2151     shapes.Clear();
2152     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_EDGE, shapes);
2153     for ( int iE = 1; iE <= shapes.Extent(); ++iE )
2154     {
2155       const TopoDS_Shape& edge = shapes(iE);
2156       // find 2 FACEs sharing an EDGE
2157       TopoDS_Shape FF[2];
2158       PShapeIteratorPtr fIt = helper.GetAncestors(edge, *_mesh, TopAbs_FACE, &_sdVec[i]._solid);
2159       while ( fIt->more())
2160       {
2161         const TopoDS_Shape* f = fIt->next();
2162         FF[ int( !FF[0].IsNull()) ] = *f;
2163       }
2164       if( FF[1].IsNull() ) continue; // seam edge can be shared by 1 FACE only
2165
2166       // check presence of layers on them
2167       int ignore[2];
2168       for ( int j = 0; j < 2; ++j )
2169         ignore[j] = _sdVec[i]._ignoreFaceIds.count( getMeshDS()->ShapeToIndex( FF[j] ));
2170       if ( ignore[0] == ignore[1] )
2171         continue; // nothing interesting
2172       TopoDS_Shape fWOL = FF[ ignore[0] ? 0 : 1 ];
2173
2174       // add EDGE to maps
2175       if ( !fWOL.IsNull())
2176       {
2177         TGeomID edgeInd = getMeshDS()->ShapeToIndex( edge );
2178         _sdVec[i]._shrinkShape2Shape.insert( make_pair( edgeInd, fWOL ));
2179       }
2180     }
2181   }
2182
2183   // Find the SHAPE along which to inflate _LayerEdge based on VERTEX
2184
2185   for ( size_t i = 0; i < _sdVec.size(); ++i )
2186   {
2187     shapes.Clear();
2188     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_VERTEX, shapes);
2189     for ( int iV = 1; iV <= shapes.Extent(); ++iV )
2190     {
2191       const TopoDS_Shape& vertex = shapes(iV);
2192       // find faces WOL sharing the vertex
2193       vector< TopoDS_Shape > facesWOL;
2194       size_t totalNbFaces = 0;
2195       PShapeIteratorPtr fIt = helper.GetAncestors(vertex, *_mesh, TopAbs_FACE, &_sdVec[i]._solid );
2196       while ( fIt->more())
2197       {
2198         const TopoDS_Shape* f = fIt->next();
2199         totalNbFaces++;
2200         const int fID = getMeshDS()->ShapeToIndex( *f );
2201         if ( _sdVec[i]._ignoreFaceIds.count ( fID ) /*&& !_sdVec[i]._noShrinkShapes.count( fID )*/)
2202           facesWOL.push_back( *f );
2203       }
2204       if ( facesWOL.size() == totalNbFaces || facesWOL.empty() )
2205         continue; // no layers at this vertex or no WOL
2206       TGeomID vInd = getMeshDS()->ShapeToIndex( vertex );
2207       switch ( facesWOL.size() )
2208       {
2209       case 1:
2210       {
2211         helper.SetSubShape( facesWOL[0] );
2212         if ( helper.IsRealSeam( vInd )) // inflate along a seam edge?
2213         {
2214           TopoDS_Shape seamEdge;
2215           PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE);
2216           while ( eIt->more() && seamEdge.IsNull() )
2217           {
2218             const TopoDS_Shape* e = eIt->next();
2219             if ( helper.IsRealSeam( *e ) )
2220               seamEdge = *e;
2221           }
2222           if ( !seamEdge.IsNull() )
2223           {
2224             _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, seamEdge ));
2225             break;
2226           }
2227         }
2228         _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, facesWOL[0] ));
2229         break;
2230       }
2231       case 2:
2232       {
2233         // find an edge shared by 2 faces
2234         PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE);
2235         while ( eIt->more())
2236         {
2237           const TopoDS_Shape* e = eIt->next();
2238           if ( helper.IsSubShape( *e, facesWOL[0]) &&
2239                helper.IsSubShape( *e, facesWOL[1]))
2240           {
2241             _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, *e )); break;
2242           }
2243         }
2244         break;
2245       }
2246       default:
2247         return error("Not yet supported case", _sdVec[i]._index);
2248       }
2249     }
2250   }
2251
2252   // Add to _noShrinkShapes sub-shapes of FACE's that can't be shrinked since
2253   // the algo of the SOLID sharing the FACE does not support it or for other reasons
2254   set< string > notSupportAlgos; notSupportAlgos.insert( structAlgoName );
2255   for ( size_t i = 0; i < _sdVec.size(); ++i )
2256   {
2257     map< TGeomID, TopoDS_Shape >::iterator e2f = _sdVec[i]._shrinkShape2Shape.begin();
2258     for ( ; e2f != _sdVec[i]._shrinkShape2Shape.end(); ++e2f )
2259     {
2260       const TopoDS_Shape& fWOL = e2f->second;
2261       const TGeomID     edgeID = e2f->first;
2262       TGeomID           faceID = getMeshDS()->ShapeToIndex( fWOL );
2263       TopoDS_Shape        edge = getMeshDS()->IndexToShape( edgeID );
2264       if ( edge.ShapeType() != TopAbs_EDGE )
2265         continue; // shrink shape is VERTEX
2266
2267       TopoDS_Shape solid;
2268       PShapeIteratorPtr soIt = helper.GetAncestors(fWOL, *_mesh, TopAbs_SOLID);
2269       while ( soIt->more() && solid.IsNull() )
2270       {
2271         const TopoDS_Shape* so = soIt->next();
2272         if ( !so->IsSame( _sdVec[i]._solid ))
2273           solid = *so;
2274       }
2275       if ( solid.IsNull() )
2276         continue;
2277
2278       bool noShrinkE = false;
2279       SMESH_Algo*  algo = _mesh->GetSubMesh( solid )->GetAlgo();
2280       bool isStructured = ( algo && algo->GetName() == structAlgoName );
2281       size_t     iSolid = _solids.FindIndex( solid ) - 1;
2282       if ( iSolid < _sdVec.size() && _sdVec[ iSolid ]._ignoreFaceIds.count( faceID ))
2283       {
2284         // the adjacent SOLID has NO layers on fWOL;
2285         // shrink allowed if
2286         // - there are layers on the EDGE in the adjacent SOLID
2287         // - there are NO layers in the adjacent SOLID && algo is unstructured and computed later
2288         bool hasWLAdj = (_sdVec[iSolid]._shrinkShape2Shape.count( edgeID ));
2289         bool shrinkAllowed = (( hasWLAdj ) ||
2290                               ( !isStructured && setBefore( _sdVec[ i ], _sdVec[ iSolid ] )));
2291         noShrinkE = !shrinkAllowed;
2292       }
2293       else if ( iSolid < _sdVec.size() )
2294       {
2295         // the adjacent SOLID has layers on fWOL;
2296         // check if SOLID's mesh is unstructured and then try to set it
2297         // to be computed after the i-th solid
2298         if ( isStructured || !setBefore( _sdVec[ i ], _sdVec[ iSolid ] ))
2299           noShrinkE = true; // don't shrink fWOL
2300       }
2301       else
2302       {
2303         // the adjacent SOLID has NO layers at all
2304         noShrinkE = isStructured;
2305       }
2306
2307       if ( noShrinkE )
2308       {
2309         _sdVec[i]._noShrinkShapes.insert( edgeID );
2310
2311         // check if there is a collision with to-shrink-from EDGEs in iSolid
2312         // if ( iSolid < _sdVec.size() )
2313         // {
2314         //   shapes.Clear();
2315         //   TopExp::MapShapes( fWOL, TopAbs_EDGE, shapes);
2316         //   for ( int iE = 1; iE <= shapes.Extent(); ++iE )
2317         //   {
2318         //     const TopoDS_Edge& E = TopoDS::Edge( shapes( iE ));
2319         //     const TGeomID    eID = getMeshDS()->ShapeToIndex( E );
2320         //     if ( eID == edgeID ||
2321         //          !_sdVec[iSolid]._shrinkShape2Shape.count( eID ) ||
2322         //          _sdVec[i]._noShrinkShapes.count( eID ))
2323         //       continue;
2324         //     for ( int is1st = 0; is1st < 2; ++is1st )
2325         //     {
2326         //       TopoDS_Vertex V = helper.IthVertex( is1st, E );
2327         //       if ( _sdVec[i]._noShrinkShapes.count( getMeshDS()->ShapeToIndex( V ) ))
2328         //       {
2329         //         return error("No way to make a conformal mesh with "
2330         //                      "the given set of faces with layers", _sdVec[i]._index);
2331         //       }
2332         //     }
2333         //   }
2334         // }
2335       }
2336
2337       // add VERTEXes of the edge in _noShrinkShapes, which is necessary if
2338       // _shrinkShape2Shape is different in the adjacent SOLID
2339       for ( TopoDS_Iterator vIt( edge ); vIt.More(); vIt.Next() )
2340       {
2341         TGeomID vID = getMeshDS()->ShapeToIndex( vIt.Value() );
2342         bool noShrinkV = false;
2343
2344         if ( iSolid < _sdVec.size() )
2345         {
2346           if ( _sdVec[ iSolid ]._ignoreFaceIds.count( faceID ))
2347           {
2348             map< TGeomID, TopoDS_Shape >::iterator i2S, i2SAdj;
2349             i2S    = _sdVec[i     ]._shrinkShape2Shape.find( vID );
2350             i2SAdj = _sdVec[iSolid]._shrinkShape2Shape.find( vID );
2351             if ( i2SAdj == _sdVec[iSolid]._shrinkShape2Shape.end() )
2352               noShrinkV = ( i2S->second.ShapeType() == TopAbs_EDGE || isStructured );
2353             else
2354               noShrinkV = ( ! i2S->second.IsSame( i2SAdj->second ));
2355           }
2356           else
2357           {
2358             noShrinkV = noShrinkE;
2359           }
2360         }
2361         else
2362         {
2363           // the adjacent SOLID has NO layers at all
2364           noShrinkV = ( isStructured ||
2365                         _sdVec[i]._shrinkShape2Shape[ vID ].ShapeType() == TopAbs_EDGE );
2366         }
2367         if ( noShrinkV )
2368           _sdVec[i]._noShrinkShapes.insert( vID );
2369       }
2370
2371     } // loop on _sdVec[i]._shrinkShape2Shape
2372   } // loop on _sdVec to fill in _SolidData::_noShrinkShapes
2373
2374
2375     // add FACEs of other SOLIDs to _ignoreFaceIds
2376   for ( size_t i = 0; i < _sdVec.size(); ++i )
2377   {
2378     shapes.Clear();
2379     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_FACE, shapes);
2380
2381     for ( exp.Init( _mesh->GetShapeToMesh(), TopAbs_FACE ); exp.More(); exp.Next() )
2382     {
2383       if ( !shapes.Contains( exp.Current() ))
2384         _sdVec[i]._ignoreFaceIds.insert( getMeshDS()->ShapeToIndex( exp.Current() ));
2385     }
2386   }
2387
2388   return true;
2389 }
2390
2391 //================================================================================
2392 /*!
2393  * \brief Finds FACEs w/o layers for a given SOLID by an hypothesis
2394  */
2395 //================================================================================
2396
2397 void _ViscousBuilder::getIgnoreFaces(const TopoDS_Shape&             solid,
2398                                      const StdMeshers_ViscousLayers* hyp,
2399                                      const TopoDS_Shape&             hypShape,
2400                                      set<TGeomID>&                   ignoreFaceIds)
2401 {
2402   TopExp_Explorer exp;
2403
2404   vector<TGeomID> ids = hyp->GetBndShapes();
2405   if ( hyp->IsToIgnoreShapes() ) // FACEs to ignore are given
2406   {
2407     for ( size_t ii = 0; ii < ids.size(); ++ii )
2408     {
2409       const TopoDS_Shape& s = getMeshDS()->IndexToShape( ids[ii] );
2410       if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2411         ignoreFaceIds.insert( ids[ii] );
2412     }
2413   }
2414   else // FACEs with layers are given
2415   {
2416     exp.Init( solid, TopAbs_FACE );
2417     for ( ; exp.More(); exp.Next() )
2418     {
2419       TGeomID faceInd = getMeshDS()->ShapeToIndex( exp.Current() );
2420       if ( find( ids.begin(), ids.end(), faceInd ) == ids.end() )
2421         ignoreFaceIds.insert( faceInd );
2422     }
2423   }
2424
2425   // ignore internal FACEs if inlets and outlets are specified
2426   if ( hyp->IsToIgnoreShapes() )
2427   {
2428     TopTools_IndexedDataMapOfShapeListOfShape solidsOfFace;
2429     TopExp::MapShapesAndAncestors( hypShape,
2430                                    TopAbs_FACE, TopAbs_SOLID, solidsOfFace);
2431
2432     for ( exp.Init( solid, TopAbs_FACE ); exp.More(); exp.Next() )
2433     {
2434       const TopoDS_Face& face = TopoDS::Face( exp.Current() );
2435       if ( SMESH_MesherHelper::NbAncestors( face, *_mesh, TopAbs_SOLID ) < 2 )
2436         continue;
2437
2438       int nbSolids = solidsOfFace.FindFromKey( face ).Extent();
2439       if ( nbSolids > 1 )
2440         ignoreFaceIds.insert( getMeshDS()->ShapeToIndex( face ));
2441     }
2442   }
2443 }
2444
2445 //================================================================================
2446 /*!
2447  * \brief Create the inner surface of the viscous layer and prepare data for infation
2448  */
2449 //================================================================================
2450
2451 bool _ViscousBuilder::makeLayer(_SolidData& data)
2452 {
2453   // get all sub-shapes to make layers on
2454   set<TGeomID> subIds, faceIds;
2455   subIds = data._noShrinkShapes;
2456   TopExp_Explorer exp( data._solid, TopAbs_FACE );
2457   for ( ; exp.More(); exp.Next() )
2458   {
2459     SMESH_subMesh* fSubM = _mesh->GetSubMesh( exp.Current() );
2460     if ( ! data._ignoreFaceIds.count( fSubM->GetId() ))
2461       faceIds.insert( fSubM->GetId() );
2462   }
2463
2464   // make a map to find new nodes on sub-shapes shared with other SOLID
2465   map< TGeomID, TNode2Edge* >::iterator s2ne;
2466   map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
2467   for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
2468   {
2469     TGeomID shapeInd = s2s->first;
2470     for ( size_t i = 0; i < _sdVec.size(); ++i )
2471     {
2472       if ( _sdVec[i]._index == data._index ) continue;
2473       map< TGeomID, TopoDS_Shape >::iterator s2s2 = _sdVec[i]._shrinkShape2Shape.find( shapeInd );
2474       if ( s2s2 != _sdVec[i]._shrinkShape2Shape.end() &&
2475            *s2s == *s2s2 && !_sdVec[i]._n2eMap.empty() )
2476       {
2477         data._s2neMap.insert( make_pair( shapeInd, &_sdVec[i]._n2eMap ));
2478         break;
2479       }
2480     }
2481   }
2482
2483   // Create temporary faces and _LayerEdge's
2484
2485   dumpFunction(SMESH_Comment("makeLayers_")<<data._index);
2486
2487   data._stepSize = Precision::Infinite();
2488   data._stepSizeNodes[0] = 0;
2489
2490   SMESH_MesherHelper helper( *_mesh );
2491   helper.SetSubShape( data._solid );
2492   helper.SetElementsOnShape( true );
2493
2494   vector< const SMDS_MeshNode*> newNodes; // of a mesh face
2495   TNode2Edge::iterator n2e2;
2496
2497   // collect _LayerEdge's of shapes they are based on
2498   vector< _EdgesOnShape >& edgesByGeom = data._edgesOnShape;
2499   const int nbShapes = getMeshDS()->MaxShapeIndex();
2500   edgesByGeom.resize( nbShapes+1 );
2501
2502   // set data of _EdgesOnShape's
2503   if ( SMESH_subMesh* sm = _mesh->GetSubMesh( data._solid ))
2504   {
2505     SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(/*includeSelf=*/false);
2506     while ( smIt->more() )
2507     {
2508       sm = smIt->next();
2509       if ( sm->GetSubShape().ShapeType() == TopAbs_FACE &&
2510            !faceIds.count( sm->GetId() ))
2511         continue;
2512       setShapeData( edgesByGeom[ sm->GetId() ], sm, data );
2513     }
2514   }
2515   // make _LayerEdge's
2516   for ( set<TGeomID>::iterator id = faceIds.begin(); id != faceIds.end(); ++id )
2517   {
2518     const TopoDS_Face& F = TopoDS::Face( getMeshDS()->IndexToShape( *id ));
2519     SMESH_subMesh* sm = _mesh->GetSubMesh( F );
2520     SMESH_ProxyMesh::SubMesh* proxySub =
2521       data._proxyMesh->getFaceSubM( F, /*create=*/true);
2522
2523     SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
2524     if ( !smDS ) return error(SMESH_Comment("Not meshed face ") << *id, data._index );
2525
2526     SMDS_ElemIteratorPtr eIt = smDS->GetElements();
2527     while ( eIt->more() )
2528     {
2529       const SMDS_MeshElement* face = eIt->next();
2530       double          faceMaxCosin = -1;
2531       _LayerEdge*     maxCosinEdge = 0;
2532       int             nbDegenNodes = 0;
2533
2534       newNodes.resize( face->NbCornerNodes() );
2535       for ( size_t i = 0 ; i < newNodes.size(); ++i )
2536       {
2537         const SMDS_MeshNode* n = face->GetNode( i );
2538         const int      shapeID = n->getshapeId();
2539         const bool onDegenShap = helper.IsDegenShape( shapeID );
2540         const bool onDegenEdge = ( onDegenShap && n->GetPosition()->GetDim() == 1 );
2541         if ( onDegenShap )
2542         {
2543           if ( onDegenEdge )
2544           {
2545             // substitute n on a degenerated EDGE with a node on a corresponding VERTEX
2546             const TopoDS_Shape& E = getMeshDS()->IndexToShape( shapeID );
2547             TopoDS_Vertex       V = helper.IthVertex( 0, TopoDS::Edge( E ));
2548             if ( const SMDS_MeshNode* vN = SMESH_Algo::VertexNode( V, getMeshDS() )) {
2549               n = vN;
2550               nbDegenNodes++;
2551             }
2552           }
2553           else
2554           {
2555             nbDegenNodes++;
2556           }
2557         }
2558         TNode2Edge::iterator n2e = data._n2eMap.insert( make_pair( n, (_LayerEdge*)0 )).first;
2559         if ( !(*n2e).second )
2560         {
2561           // add a _LayerEdge
2562           _LayerEdge* edge = new _LayerEdge();
2563           edge->_nodes.push_back( n );
2564           n2e->second = edge;
2565           edgesByGeom[ shapeID ]._edges.push_back( edge );
2566           const bool noShrink = data._noShrinkShapes.count( shapeID );
2567
2568           SMESH_TNodeXYZ xyz( n );
2569
2570           // set edge data or find already refined _LayerEdge and get data from it
2571           if (( !noShrink                                                     ) &&
2572               ( n->GetPosition()->GetTypeOfPosition() != SMDS_TOP_FACE        ) &&
2573               (( s2ne = data._s2neMap.find( shapeID )) != data._s2neMap.end() ) &&
2574               (( n2e2 = (*s2ne).second->find( n )) != s2ne->second->end()     ))
2575           {
2576             _LayerEdge* foundEdge = (*n2e2).second;
2577             gp_XYZ        lastPos = edge->Copy( *foundEdge, edgesByGeom[ shapeID ], helper );
2578             foundEdge->_pos.push_back( lastPos );
2579             // location of the last node is modified and we restore it by foundEdge->_pos.back()
2580             const_cast< SMDS_MeshNode* >
2581               ( edge->_nodes.back() )->setXYZ( xyz.X(), xyz.Y(), xyz.Z() );
2582           }
2583           else
2584           {
2585             if ( !noShrink )
2586             {
2587               edge->_nodes.push_back( helper.AddNode( xyz.X(), xyz.Y(), xyz.Z() ));
2588             }
2589             if ( !setEdgeData( *edge, edgesByGeom[ shapeID ], helper, data ))
2590               return false;
2591
2592             if ( edge->_nodes.size() < 2 )
2593               edge->Block( data );
2594               //data._noShrinkShapes.insert( shapeID );
2595           }
2596           dumpMove(edge->_nodes.back());
2597
2598           if ( edge->_cosin > faceMaxCosin )
2599           {
2600             faceMaxCosin = edge->_cosin;
2601             maxCosinEdge = edge;
2602           }
2603         }
2604         newNodes[ i ] = n2e->second->_nodes.back();
2605
2606         if ( onDegenEdge )
2607           data._n2eMap.insert( make_pair( face->GetNode( i ), n2e->second ));
2608       }
2609       if ( newNodes.size() - nbDegenNodes < 2 )
2610         continue;
2611
2612       // create a temporary face
2613       const SMDS_MeshElement* newFace =
2614         new _TmpMeshFace( newNodes, --_tmpFaceID, face->getshapeId(), face->getIdInShape() );
2615       proxySub->AddElement( newFace );
2616
2617       // compute inflation step size by min size of element on a convex surface
2618       if ( faceMaxCosin > theMinSmoothCosin )
2619         limitStepSize( data, face, maxCosinEdge );
2620
2621     } // loop on 2D elements on a FACE
2622   } // loop on FACEs of a SOLID to create _LayerEdge's
2623
2624
2625   // Set _LayerEdge::_neibors
2626   TNode2Edge::iterator n2e;
2627   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
2628   {
2629     _EdgesOnShape& eos = data._edgesOnShape[iS];
2630     for ( size_t i = 0; i < eos._edges.size(); ++i )
2631     {
2632       _LayerEdge* edge = eos._edges[i];
2633       TIDSortedNodeSet nearNodes;
2634       SMDS_ElemIteratorPtr fIt = edge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
2635       while ( fIt->more() )
2636       {
2637         const SMDS_MeshElement* f = fIt->next();
2638         if ( !data._ignoreFaceIds.count( f->getshapeId() ))
2639           nearNodes.insert( f->begin_nodes(), f->end_nodes() );
2640       }
2641       nearNodes.erase( edge->_nodes[0] );
2642       edge->_neibors.reserve( nearNodes.size() );
2643       TIDSortedNodeSet::iterator node = nearNodes.begin();
2644       for ( ; node != nearNodes.end(); ++node )
2645         if (( n2e = data._n2eMap.find( *node )) != data._n2eMap.end() )
2646           edge->_neibors.push_back( n2e->second );
2647     }
2648   }
2649
2650   data._epsilon = 1e-7;
2651   if ( data._stepSize < 1. )
2652     data._epsilon *= data._stepSize;
2653
2654   if ( !findShapesToSmooth( data )) // _LayerEdge::_maxLen is computed here
2655     return false;
2656
2657   // limit data._stepSize depending on surface curvature and fill data._convexFaces
2658   limitStepSizeByCurvature( data ); // !!! it must be before node substitution in _Simplex
2659
2660   // Set target nodes into _Simplex and _LayerEdge's to _2NearEdges
2661   const SMDS_MeshNode* nn[2];
2662   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
2663   {
2664     _EdgesOnShape& eos = data._edgesOnShape[iS];
2665     for ( size_t i = 0; i < eos._edges.size(); ++i )
2666     {
2667       _LayerEdge* edge = eos._edges[i];
2668       if ( edge->IsOnEdge() )
2669       {
2670         // get neighbor nodes
2671         bool hasData = ( edge->_2neibors->_edges[0] );
2672         if ( hasData ) // _LayerEdge is a copy of another one
2673         {
2674           nn[0] = edge->_2neibors->srcNode(0);
2675           nn[1] = edge->_2neibors->srcNode(1);
2676         }
2677         else if ( !findNeiborsOnEdge( edge, nn[0],nn[1], eos, data ))
2678         {
2679           return false;
2680         }
2681         // set neighbor _LayerEdge's
2682         for ( int j = 0; j < 2; ++j )
2683         {
2684           if (( n2e = data._n2eMap.find( nn[j] )) == data._n2eMap.end() )
2685             return error("_LayerEdge not found by src node", data._index);
2686           edge->_2neibors->_edges[j] = n2e->second;
2687         }
2688         if ( !hasData )
2689           edge->SetDataByNeighbors( nn[0], nn[1], eos, helper );
2690       }
2691
2692       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
2693       {
2694         _Simplex& s = edge->_simplices[j];
2695         s._nNext = data._n2eMap[ s._nNext ]->_nodes.back();
2696         s._nPrev = data._n2eMap[ s._nPrev ]->_nodes.back();
2697       }
2698
2699       // For an _LayerEdge on a degenerated EDGE, copy some data from
2700       // a corresponding _LayerEdge on a VERTEX
2701       // (issue 52453, pb on a downloaded SampleCase2-Tet-netgen-mephisto.hdf)
2702       if ( helper.IsDegenShape( edge->_nodes[0]->getshapeId() ))
2703       {
2704         // Generally we should not get here
2705         if ( eos.ShapeType() != TopAbs_EDGE )
2706           continue;
2707         TopoDS_Vertex V = helper.IthVertex( 0, TopoDS::Edge( eos._shape ));
2708         const SMDS_MeshNode* vN = SMESH_Algo::VertexNode( V, getMeshDS() );
2709         if (( n2e = data._n2eMap.find( vN )) == data._n2eMap.end() )
2710           continue;
2711         const _LayerEdge* vEdge = n2e->second;
2712         edge->_normal    = vEdge->_normal;
2713         edge->_lenFactor = vEdge->_lenFactor;
2714         edge->_cosin     = vEdge->_cosin;
2715       }
2716
2717     } // loop on data._edgesOnShape._edges
2718   } // loop on data._edgesOnShape
2719
2720   // fix _LayerEdge::_2neibors on EDGEs to smooth
2721   // map< TGeomID,Handle(Geom_Curve)>::iterator e2c = data._edge2curve.begin();
2722   // for ( ; e2c != data._edge2curve.end(); ++e2c )
2723   //   if ( !e2c->second.IsNull() )
2724   //   {
2725   //     if ( _EdgesOnShape* eos = data.GetShapeEdges( e2c->first ))
2726   //       data.Sort2NeiborsOnEdge( eos->_edges );
2727   //   }
2728
2729   dumpFunctionEnd();
2730   return true;
2731 }
2732
2733 //================================================================================
2734 /*!
2735  * \brief Compute inflation step size by min size of element on a convex surface
2736  */
2737 //================================================================================
2738
2739 void _ViscousBuilder::limitStepSize( _SolidData&             data,
2740                                      const SMDS_MeshElement* face,
2741                                      const _LayerEdge*       maxCosinEdge )
2742 {
2743   int iN = 0;
2744   double minSize = 10 * data._stepSize;
2745   const int nbNodes = face->NbCornerNodes();
2746   for ( int i = 0; i < nbNodes; ++i )
2747   {
2748     const SMDS_MeshNode* nextN = face->GetNode( SMESH_MesherHelper::WrapIndex( i+1, nbNodes ));
2749     const SMDS_MeshNode*  curN = face->GetNode( i );
2750     if ( nextN->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ||
2751          curN-> GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2752     {
2753       double dist = SMESH_TNodeXYZ( curN ).Distance( nextN );
2754       if ( dist < minSize )
2755         minSize = dist, iN = i;
2756     }
2757   }
2758   double newStep = 0.8 * minSize / maxCosinEdge->_lenFactor;
2759   if ( newStep < data._stepSize )
2760   {
2761     data._stepSize = newStep;
2762     data._stepSizeCoeff = 0.8 / maxCosinEdge->_lenFactor;
2763     data._stepSizeNodes[0] = face->GetNode( iN );
2764     data._stepSizeNodes[1] = face->GetNode( SMESH_MesherHelper::WrapIndex( iN+1, nbNodes ));
2765   }
2766 }
2767
2768 //================================================================================
2769 /*!
2770  * \brief Compute inflation step size by min size of element on a convex surface
2771  */
2772 //================================================================================
2773
2774 void _ViscousBuilder::limitStepSize( _SolidData& data, const double minSize )
2775 {
2776   if ( minSize < data._stepSize )
2777   {
2778     data._stepSize = minSize;
2779     if ( data._stepSizeNodes[0] )
2780     {
2781       double dist =
2782         SMESH_TNodeXYZ(data._stepSizeNodes[0]).Distance(data._stepSizeNodes[1]);
2783       data._stepSizeCoeff = data._stepSize / dist;
2784     }
2785   }
2786 }
2787
2788 //================================================================================
2789 /*!
2790  * \brief Limit data._stepSize by evaluating curvature of shapes and fill data._convexFaces
2791  */
2792 //================================================================================
2793
2794 void _ViscousBuilder::limitStepSizeByCurvature( _SolidData& data )
2795 {
2796   SMESH_MesherHelper helper( *_mesh );
2797
2798   BRepLProp_SLProps surfProp( 2, 1e-6 );
2799   data._convexFaces.clear();
2800
2801   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
2802   {
2803     _EdgesOnShape& eof = data._edgesOnShape[iS];
2804     if ( eof.ShapeType() != TopAbs_FACE ||
2805          data._ignoreFaceIds.count( eof._shapeID ))
2806       continue;
2807
2808     TopoDS_Face        F = TopoDS::Face( eof._shape );
2809     const TGeomID faceID = eof._shapeID;
2810
2811     BRepAdaptor_Surface surface( F, false );
2812     surfProp.SetSurface( surface );
2813
2814     _ConvexFace cnvFace;
2815     cnvFace._face = F;
2816     cnvFace._normalsFixed = false;
2817     cnvFace._isTooCurved = false;
2818
2819     double maxCurvature = cnvFace.GetMaxCurvature( data, eof, surfProp, helper );
2820     if ( maxCurvature > 0 )
2821     {
2822       limitStepSize( data, 0.9 / maxCurvature );
2823       findEdgesToUpdateNormalNearConvexFace( cnvFace, data, helper );
2824     }
2825     if ( !cnvFace._isTooCurved ) continue;
2826
2827     _ConvexFace & convFace =
2828       data._convexFaces.insert( make_pair( faceID, cnvFace )).first->second;
2829
2830     // skip a closed surface (data._convexFaces is useful anyway)
2831     bool isClosedF = false;
2832     helper.SetSubShape( F );
2833     if ( helper.HasRealSeam() )
2834     {
2835       // in the closed surface there must be a closed EDGE
2836       for ( TopExp_Explorer eIt( F, TopAbs_EDGE ); eIt.More() && !isClosedF; eIt.Next() )
2837         isClosedF = helper.IsClosedEdge( TopoDS::Edge( eIt.Current() ));
2838     }
2839     if ( isClosedF )
2840     {
2841       // limit _LayerEdge::_maxLen on the FACE
2842       const double oriFactor    = ( F.Orientation() == TopAbs_REVERSED ? +1. : -1. );
2843       const double minCurvature =
2844         1. / ( eof._hyp.GetTotalThickness() * ( 1 + theThickToIntersection ));
2845       map< TGeomID, _EdgesOnShape* >::iterator id2eos = cnvFace._subIdToEOS.find( faceID );
2846       if ( id2eos != cnvFace._subIdToEOS.end() )
2847       {
2848         _EdgesOnShape& eos = * id2eos->second;
2849         for ( size_t i = 0; i < eos._edges.size(); ++i )
2850         {
2851           _LayerEdge* ledge = eos._edges[ i ];
2852           gp_XY uv = helper.GetNodeUV( F, ledge->_nodes[0] );
2853           surfProp.SetParameters( uv.X(), uv.Y() );
2854           if ( surfProp.IsCurvatureDefined() )
2855           {
2856             double curvature = Max( surfProp.MaxCurvature() * oriFactor,
2857                                     surfProp.MinCurvature() * oriFactor );
2858             if ( curvature > minCurvature )
2859               ledge->_maxLen = Min( ledge->_maxLen, 1. / curvature );
2860           }
2861         }
2862       }
2863       continue;
2864     }
2865
2866     // Fill _ConvexFace::_simplexTestEdges. These _LayerEdge's are used to detect
2867     // prism distortion.
2868     map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.find( faceID );
2869     if ( id2eos != convFace._subIdToEOS.end() && !id2eos->second->_edges.empty() )
2870     {
2871       // there are _LayerEdge's on the FACE it-self;
2872       // select _LayerEdge's near EDGEs
2873       _EdgesOnShape& eos = * id2eos->second;
2874       for ( size_t i = 0; i < eos._edges.size(); ++i )
2875       {
2876         _LayerEdge* ledge = eos._edges[ i ];
2877         for ( size_t j = 0; j < ledge->_simplices.size(); ++j )
2878           if ( ledge->_simplices[j]._nNext->GetPosition()->GetDim() < 2 )
2879           {
2880             convFace._simplexTestEdges.push_back( ledge );
2881             break;
2882           }
2883       }
2884     }
2885     else
2886     {
2887       // where there are no _LayerEdge's on a _ConvexFace,
2888       // as e.g. on a fillet surface with no internal nodes - issue 22580,
2889       // so that collision of viscous internal faces is not detected by check of
2890       // intersection of _LayerEdge's with the viscous internal faces.
2891
2892       set< const SMDS_MeshNode* > usedNodes;
2893
2894       // look for _LayerEdge's with null _sWOL
2895       id2eos = convFace._subIdToEOS.begin();
2896       for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
2897       {
2898         _EdgesOnShape& eos = * id2eos->second;
2899         if ( !eos._sWOL.IsNull() )
2900           continue;
2901         for ( size_t i = 0; i < eos._edges.size(); ++i )
2902         {
2903           _LayerEdge* ledge = eos._edges[ i ];
2904           const SMDS_MeshNode* srcNode = ledge->_nodes[0];
2905           if ( !usedNodes.insert( srcNode ).second ) continue;
2906
2907           for ( size_t i = 0; i < ledge->_simplices.size(); ++i )
2908           {
2909             usedNodes.insert( ledge->_simplices[i]._nPrev );
2910             usedNodes.insert( ledge->_simplices[i]._nNext );
2911           }
2912           convFace._simplexTestEdges.push_back( ledge );
2913         }
2914       }
2915     }
2916   } // loop on FACEs of data._solid
2917 }
2918
2919 //================================================================================
2920 /*!
2921  * \brief Detect shapes (and _LayerEdge's on them) to smooth
2922  */
2923 //================================================================================
2924
2925 bool _ViscousBuilder::findShapesToSmooth( _SolidData& data )
2926 {
2927   // define allowed thickness
2928   computeGeomSize( data ); // compute data._geomSize and _LayerEdge::_maxLen
2929
2930
2931   // Find shapes needing smoothing; such a shape has _LayerEdge._normal on it's
2932   // boundary inclined to the shape at a sharp angle
2933
2934   //list< TGeomID > shapesToSmooth;
2935   TopTools_MapOfShape edgesOfSmooFaces;
2936
2937   SMESH_MesherHelper helper( *_mesh );
2938   bool ok = true;
2939
2940   vector< _EdgesOnShape >& edgesByGeom = data._edgesOnShape;
2941   data._nbShapesToSmooth = 0;
2942
2943   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check FACEs
2944   {
2945     _EdgesOnShape& eos = edgesByGeom[iS];
2946     eos._toSmooth = false;
2947     if ( eos._edges.empty() || eos.ShapeType() != TopAbs_FACE )
2948       continue;
2949
2950     double tgtThick = eos._hyp.GetTotalThickness();
2951     TopExp_Explorer eExp( edgesByGeom[iS]._shape, TopAbs_EDGE );
2952     for ( ; eExp.More() && !eos._toSmooth; eExp.Next() )
2953     {
2954       TGeomID iE = getMeshDS()->ShapeToIndex( eExp.Current() );
2955       vector<_LayerEdge*>& eE = edgesByGeom[ iE ]._edges;
2956       if ( eE.empty() ) continue;
2957
2958       double faceSize;
2959       for ( size_t i = 0; i < eE.size() && !eos._toSmooth; ++i )
2960         if ( eE[i]->_cosin > theMinSmoothCosin )
2961         {
2962           SMDS_ElemIteratorPtr fIt = eE[i]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
2963           while ( fIt->more() && !eos._toSmooth )
2964           {
2965             const SMDS_MeshElement* face = fIt->next();
2966             if ( face->getshapeId() == eos._shapeID &&
2967                  getDistFromEdge( face, eE[i]->_nodes[0], faceSize ))
2968             {
2969               eos._toSmooth = needSmoothing( eE[i]->_cosin, tgtThick, faceSize );
2970             }
2971           }
2972         }
2973     }
2974     if ( eos._toSmooth )
2975     {
2976       for ( eExp.ReInit(); eExp.More(); eExp.Next() )
2977         edgesOfSmooFaces.Add( eExp.Current() );
2978
2979       data.PrepareEdgesToSmoothOnFace( &edgesByGeom[iS], /*substituteSrcNodes=*/false );
2980     }
2981     data._nbShapesToSmooth += eos._toSmooth;
2982
2983   }  // check FACEs
2984
2985   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check EDGEs
2986   {
2987     _EdgesOnShape& eos = edgesByGeom[iS];
2988     eos._edgeSmoother = NULL;
2989     if ( eos._edges.empty() || eos.ShapeType() != TopAbs_EDGE ) continue;
2990     if ( !eos._hyp.ToSmooth() ) continue;
2991
2992     const TopoDS_Edge& E = TopoDS::Edge( edgesByGeom[iS]._shape );
2993     if ( SMESH_Algo::isDegenerated( E ) || !edgesOfSmooFaces.Contains( E ))
2994       continue;
2995
2996     double tgtThick = eos._hyp.GetTotalThickness();
2997     for ( TopoDS_Iterator vIt( E ); vIt.More() && !eos._toSmooth; vIt.Next() )
2998     {
2999       TGeomID iV = getMeshDS()->ShapeToIndex( vIt.Value() );
3000       vector<_LayerEdge*>& eV = edgesByGeom[ iV ]._edges;
3001       if ( eV.empty() || eV[0]->Is( _LayerEdge::MULTI_NORMAL )) continue;
3002       gp_Vec  eDir    = getEdgeDir( E, TopoDS::Vertex( vIt.Value() ));
3003       double angle    = eDir.Angle( eV[0]->_normal );
3004       double cosin    = Cos( angle );
3005       double cosinAbs = Abs( cosin );
3006       if ( cosinAbs > theMinSmoothCosin )
3007       {
3008         // always smooth analytic EDGEs
3009         Handle(Geom_Curve) curve = _Smoother1D::CurveForSmooth( E, eos, helper );
3010         eos._toSmooth = ! curve.IsNull();
3011
3012         // compare tgtThick with the length of an end segment
3013         SMDS_ElemIteratorPtr eIt = eV[0]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Edge);
3014         while ( eIt->more() && !eos._toSmooth )
3015         {
3016           const SMDS_MeshElement* endSeg = eIt->next();
3017           if ( endSeg->getshapeId() == (int) iS )
3018           {
3019             double segLen =
3020               SMESH_TNodeXYZ( endSeg->GetNode(0) ).Distance( endSeg->GetNode(1 ));
3021             eos._toSmooth = needSmoothing( cosinAbs, tgtThick, segLen );
3022           }
3023         }
3024         if ( eos._toSmooth )
3025         {
3026           eos._edgeSmoother = new _Smoother1D( curve, eos );
3027
3028           // for ( size_t i = 0; i < eos._edges.size(); ++i )
3029           //   eos._edges[i]->Set( _LayerEdge::TO_SMOOTH );
3030         }
3031       }
3032     }
3033     data._nbShapesToSmooth += eos._toSmooth;
3034
3035   } // check EDGEs
3036
3037   // Reset _cosin if no smooth is allowed by the user
3038   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS )
3039   {
3040     _EdgesOnShape& eos = edgesByGeom[iS];
3041     if ( eos._edges.empty() ) continue;
3042
3043     if ( !eos._hyp.ToSmooth() )
3044       for ( size_t i = 0; i < eos._edges.size(); ++i )
3045         eos._edges[i]->SetCosin( 0 );
3046   }
3047
3048
3049   // Fill _eosC1 to make that C1 FACEs and EGDEs between them to be smoothed as a whole
3050
3051   TopTools_MapOfShape c1VV;
3052
3053   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check FACEs
3054   {
3055     _EdgesOnShape& eos = edgesByGeom[iS];
3056     if ( eos._edges.empty() ||
3057          eos.ShapeType() != TopAbs_FACE ||
3058          !eos._toSmooth )
3059       continue;
3060
3061     // check EDGEs of a FACE
3062     TopTools_MapOfShape checkedEE, allVV;
3063     list< SMESH_subMesh* > smQueue( 1, eos._subMesh ); // sm of FACEs
3064     while ( !smQueue.empty() )
3065     {
3066       SMESH_subMesh* sm = smQueue.front();
3067       smQueue.pop_front();
3068       SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(/*includeSelf=*/false);
3069       while ( smIt->more() )
3070       {
3071         sm = smIt->next();
3072         if ( sm->GetSubShape().ShapeType() == TopAbs_VERTEX )
3073           allVV.Add( sm->GetSubShape() );
3074         if ( sm->GetSubShape().ShapeType() != TopAbs_EDGE ||
3075              !checkedEE.Add( sm->GetSubShape() ))
3076           continue;
3077
3078         _EdgesOnShape*      eoe = data.GetShapeEdges( sm->GetId() );
3079         vector<_LayerEdge*>& eE = eoe->_edges;
3080         if ( eE.empty() || !eoe->_sWOL.IsNull() )
3081           continue;
3082
3083         bool isC1 = true; // check continuity along an EDGE
3084         for ( size_t i = 0; i < eE.size() && isC1; ++i )
3085           isC1 = ( Abs( eE[i]->_cosin ) < theMinSmoothCosin );
3086         if ( !isC1 )
3087           continue;
3088
3089         // check that mesh faces are C1 as well
3090         {
3091           gp_XYZ norm1, norm2;
3092           const SMDS_MeshNode*   n = eE[ eE.size() / 2 ]->_nodes[0];
3093           SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
3094           if ( !SMESH_MeshAlgos::FaceNormal( fIt->next(), norm1, /*normalized=*/true ))
3095             continue;
3096           while ( fIt->more() && isC1 )
3097             isC1 = ( SMESH_MeshAlgos::FaceNormal( fIt->next(), norm2, /*normalized=*/true ) &&
3098                      Abs( norm1 * norm2 ) >= ( 1. - theMinSmoothCosin ));
3099           if ( !isC1 )
3100             continue;
3101         }
3102
3103         // add the EDGE and an adjacent FACE to _eosC1
3104         PShapeIteratorPtr fIt = helper.GetAncestors( sm->GetSubShape(), *_mesh, TopAbs_FACE );
3105         while ( const TopoDS_Shape* face = fIt->next() )
3106         {
3107           _EdgesOnShape* eof = data.GetShapeEdges( *face );
3108           if ( !eof ) continue; // other solid
3109           if ( !eos.HasC1( eoe ))
3110           {
3111             eos._eosC1.push_back( eoe );
3112             eoe->_toSmooth = false;
3113             data.PrepareEdgesToSmoothOnFace( eoe, /*substituteSrcNodes=*/false );
3114           }
3115           if ( eos._shapeID != eof->_shapeID && !eos.HasC1( eof )) 
3116           {
3117             eos._eosC1.push_back( eof );
3118             eof->_toSmooth = false;
3119             data.PrepareEdgesToSmoothOnFace( eof, /*substituteSrcNodes=*/false );
3120             smQueue.push_back( eof->_subMesh );
3121           }
3122         }
3123       }
3124     }
3125     if ( eos._eosC1.empty() )
3126       continue;
3127
3128     // check VERTEXes of C1 FACEs
3129     TopTools_MapIteratorOfMapOfShape vIt( allVV );
3130     for ( ; vIt.More(); vIt.Next() )
3131     {
3132       _EdgesOnShape* eov = data.GetShapeEdges( vIt.Key() );
3133       if ( !eov || eov->_edges.empty() || !eov->_sWOL.IsNull() )
3134         continue;
3135
3136       bool isC1 = true; // check if all adjacent FACEs are in eos._eosC1
3137       PShapeIteratorPtr fIt = helper.GetAncestors( vIt.Key(), *_mesh, TopAbs_FACE );
3138       while ( const TopoDS_Shape* face = fIt->next() )
3139       {
3140         _EdgesOnShape* eof = data.GetShapeEdges( *face );
3141         if ( !eof ) continue; // other solid
3142         isC1 = ( face->IsSame( eos._shape ) || eos.HasC1( eof ));
3143         if ( !isC1 )
3144           break;
3145       }
3146       if ( isC1 )
3147       {
3148         eos._eosC1.push_back( eov );
3149         data.PrepareEdgesToSmoothOnFace( eov, /*substituteSrcNodes=*/false );
3150         c1VV.Add( eov->_shape );
3151       }
3152     }
3153
3154   } // fill _eosC1 of FACEs
3155
3156
3157   // Find C1 EDGEs
3158
3159   vector< pair< _EdgesOnShape*, gp_XYZ > > dirOfEdges;
3160
3161   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check VERTEXes
3162   {
3163     _EdgesOnShape& eov = edgesByGeom[iS];
3164     if ( eov._edges.empty() ||
3165          eov.ShapeType() != TopAbs_VERTEX ||
3166          c1VV.Contains( eov._shape ))
3167       continue;
3168     const TopoDS_Vertex& V = TopoDS::Vertex( eov._shape );
3169
3170     // get directions of surrounding EDGEs
3171     dirOfEdges.clear();
3172     PShapeIteratorPtr fIt = helper.GetAncestors( eov._shape, *_mesh, TopAbs_EDGE );
3173     while ( const TopoDS_Shape* e = fIt->next() )
3174     {
3175       _EdgesOnShape* eoe = data.GetShapeEdges( *e );
3176       if ( !eoe ) continue; // other solid
3177       gp_XYZ eDir = getEdgeDir( TopoDS::Edge( *e ), V );
3178       if ( !Precision::IsInfinite( eDir.X() ))
3179         dirOfEdges.push_back( make_pair( eoe, eDir.Normalized() ));
3180     }
3181
3182     // find EDGEs with C1 directions
3183     for ( size_t i = 0; i < dirOfEdges.size(); ++i )
3184       for ( size_t j = i+1; j < dirOfEdges.size(); ++j )
3185         if ( dirOfEdges[i].first && dirOfEdges[j].first )
3186         {
3187           double dot = dirOfEdges[i].second * dirOfEdges[j].second;
3188           bool isC1 = ( dot < - ( 1. - theMinSmoothCosin ));
3189           if ( isC1 )
3190           {
3191             double maxEdgeLen = 3 * Min( eov._edges[0]->_maxLen, eov._hyp.GetTotalThickness() );
3192             double eLen1 = SMESH_Algo::EdgeLength( TopoDS::Edge( dirOfEdges[i].first->_shape ));
3193             double eLen2 = SMESH_Algo::EdgeLength( TopoDS::Edge( dirOfEdges[j].first->_shape ));
3194             if ( eLen1 < maxEdgeLen ) eov._eosC1.push_back( dirOfEdges[i].first );
3195             if ( eLen2 < maxEdgeLen ) eov._eosC1.push_back( dirOfEdges[j].first );
3196             dirOfEdges[i].first = 0;
3197             dirOfEdges[j].first = 0;
3198           }
3199         }
3200   } // fill _eosC1 of VERTEXes
3201
3202
3203
3204   return ok;
3205 }
3206
3207 //================================================================================
3208 /*!
3209  * \brief initialize data of _EdgesOnShape
3210  */
3211 //================================================================================
3212
3213 void _ViscousBuilder::setShapeData( _EdgesOnShape& eos,
3214                                     SMESH_subMesh* sm,
3215                                     _SolidData&    data )
3216 {
3217   if ( !eos._shape.IsNull() ||
3218        sm->GetSubShape().ShapeType() == TopAbs_WIRE )
3219     return;
3220
3221   SMESH_MesherHelper helper( *_mesh );
3222
3223   eos._subMesh = sm;
3224   eos._shapeID = sm->GetId();
3225   eos._shape   = sm->GetSubShape();
3226   if ( eos.ShapeType() == TopAbs_FACE )
3227     eos._shape.Orientation( helper.GetSubShapeOri( data._solid, eos._shape ));
3228   eos._toSmooth = false;
3229   eos._data = &data;
3230
3231   // set _SWOL
3232   map< TGeomID, TopoDS_Shape >::const_iterator s2s =
3233     data._shrinkShape2Shape.find( eos._shapeID );
3234   if ( s2s != data._shrinkShape2Shape.end() )
3235     eos._sWOL = s2s->second;
3236
3237   eos._isRegularSWOL = true;
3238   if ( eos.SWOLType() == TopAbs_FACE )
3239   {
3240     const TopoDS_Face& F = TopoDS::Face( eos._sWOL );
3241     Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( F );
3242     eos._isRegularSWOL = ( ! surface->HasSingularities( 1e-7 ));
3243   }
3244
3245   // set _hyp
3246   if ( data._hyps.size() == 1 )
3247   {
3248     eos._hyp = data._hyps.back();
3249   }
3250   else
3251   {
3252     // compute average StdMeshers_ViscousLayers parameters
3253     map< TGeomID, const StdMeshers_ViscousLayers* >::iterator f2hyp;
3254     if ( eos.ShapeType() == TopAbs_FACE )
3255     {
3256       if (( f2hyp = data._face2hyp.find( eos._shapeID )) != data._face2hyp.end() )
3257         eos._hyp = f2hyp->second;
3258     }
3259     else
3260     {
3261       PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
3262       while ( const TopoDS_Shape* face = fIt->next() )
3263       {
3264         TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
3265         if (( f2hyp = data._face2hyp.find( faceID )) != data._face2hyp.end() )
3266           eos._hyp.Add( f2hyp->second );
3267       }
3268     }
3269   }
3270
3271   // set _faceNormals
3272   if ( ! eos._hyp.UseSurfaceNormal() )
3273   {
3274     if ( eos.ShapeType() == TopAbs_FACE ) // get normals to elements on a FACE
3275     {
3276       SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
3277       eos._faceNormals.resize( smDS->NbElements() );
3278
3279       SMDS_ElemIteratorPtr eIt = smDS->GetElements();
3280       for ( int iF = 0; eIt->more(); ++iF )
3281       {
3282         const SMDS_MeshElement* face = eIt->next();
3283         if ( !SMESH_MeshAlgos::FaceNormal( face, eos._faceNormals[iF], /*normalized=*/true ))
3284           eos._faceNormals[iF].SetCoord( 0,0,0 );
3285       }
3286
3287       if ( !helper.IsReversedSubMesh( TopoDS::Face( eos._shape )))
3288         for ( size_t iF = 0; iF < eos._faceNormals.size(); ++iF )
3289           eos._faceNormals[iF].Reverse();
3290     }
3291     else // find EOS of adjacent FACEs
3292     {
3293       PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
3294       while ( const TopoDS_Shape* face = fIt->next() )
3295       {
3296         TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
3297         eos._faceEOS.push_back( & data._edgesOnShape[ faceID ]);
3298         if ( eos._faceEOS.back()->_shape.IsNull() )
3299           // avoid using uninitialised _shapeID in GetNormal()
3300           eos._faceEOS.back()->_shapeID = faceID;
3301       }
3302     }
3303   }
3304 }
3305
3306 //================================================================================
3307 /*!
3308  * \brief Returns normal of a face
3309  */
3310 //================================================================================
3311
3312 bool _EdgesOnShape::GetNormal( const SMDS_MeshElement* face, gp_Vec& norm )
3313 {
3314   bool ok = false;
3315   const _EdgesOnShape* eos = 0;
3316
3317   if ( face->getshapeId() == _shapeID )
3318   {
3319     eos = this;
3320   }
3321   else
3322   {
3323     for ( size_t iF = 0; iF < _faceEOS.size() && !eos; ++iF )
3324       if ( face->getshapeId() == _faceEOS[ iF ]->_shapeID )
3325         eos = _faceEOS[ iF ];
3326   }
3327
3328   if (( eos ) &&
3329       ( ok = ( face->getIdInShape() < (int) eos->_faceNormals.size() )))
3330   {
3331     norm = eos->_faceNormals[ face->getIdInShape() ];
3332   }
3333   else if ( !eos )
3334   {
3335     debugMsg( "_EdgesOnShape::Normal() failed for face "<<face->GetID()
3336               << " on _shape #" << _shapeID );
3337   }
3338   return ok;
3339 }
3340
3341
3342 //================================================================================
3343 /*!
3344  * \brief Set data of _LayerEdge needed for smoothing
3345  */
3346 //================================================================================
3347
3348 bool _ViscousBuilder::setEdgeData(_LayerEdge&         edge,
3349                                   _EdgesOnShape&      eos,
3350                                   SMESH_MesherHelper& helper,
3351                                   _SolidData&         data)
3352 {
3353   const SMDS_MeshNode* node = edge._nodes[0]; // source node
3354
3355   edge._len       = 0;
3356   edge._maxLen    = Precision::Infinite();
3357   edge._minAngle  = 0;
3358   edge._2neibors  = 0;
3359   edge._curvature = 0;
3360   edge._flags     = 0;
3361
3362   // --------------------------
3363   // Compute _normal and _cosin
3364   // --------------------------
3365
3366   edge._cosin     = 0;
3367   edge._lenFactor = 1.;
3368   edge._normal.SetCoord(0,0,0);
3369   _Simplex::GetSimplices( node, edge._simplices, data._ignoreFaceIds, &data );
3370
3371   int totalNbFaces = 0;
3372   TopoDS_Face F;
3373   std::pair< TopoDS_Face, gp_XYZ > face2Norm[20];
3374   gp_Vec geomNorm;
3375   bool normOK = true;
3376
3377   const bool onShrinkShape = !eos._sWOL.IsNull();
3378   const bool useGeometry   = (( eos._hyp.UseSurfaceNormal() ) ||
3379                               ( eos.ShapeType() != TopAbs_FACE /*&& !onShrinkShape*/ ));
3380
3381   // get geom FACEs the node lies on
3382   //if ( useGeometry )
3383   {
3384     set<TGeomID> faceIds;
3385     if  ( eos.ShapeType() == TopAbs_FACE )
3386     {
3387       faceIds.insert( eos._shapeID );
3388     }
3389     else
3390     {
3391       SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
3392       while ( fIt->more() )
3393         faceIds.insert( fIt->next()->getshapeId() );
3394     }
3395     set<TGeomID>::iterator id = faceIds.begin();
3396     for ( ; id != faceIds.end(); ++id )
3397     {
3398       const TopoDS_Shape& s = getMeshDS()->IndexToShape( *id );
3399       if ( s.IsNull() || s.ShapeType() != TopAbs_FACE || data._ignoreFaceIds.count( *id ))
3400         continue;
3401       F = TopoDS::Face( s );
3402       face2Norm[ totalNbFaces ].first = F;
3403       totalNbFaces++;
3404     }
3405   }
3406
3407   // find _normal
3408   bool fromVonF = false;
3409   if ( useGeometry )
3410   {
3411     fromVonF = ( eos.ShapeType() == TopAbs_VERTEX &&
3412                  eos.SWOLType()  == TopAbs_FACE  &&
3413                  totalNbFaces > 1 );
3414
3415     if ( onShrinkShape && !fromVonF ) // one of faces the node is on has no layers
3416     {
3417       if ( eos.SWOLType() == TopAbs_EDGE )
3418       {
3419         // inflate from VERTEX along EDGE
3420         edge._normal = getEdgeDir( TopoDS::Edge( eos._sWOL ), TopoDS::Vertex( eos._shape ));
3421       }
3422       else if ( eos.ShapeType() == TopAbs_VERTEX )
3423       {
3424         // inflate from VERTEX along FACE
3425         edge._normal = getFaceDir( TopoDS::Face( eos._sWOL ), TopoDS::Vertex( eos._shape ),
3426                                    node, helper, normOK, &edge._cosin);
3427       }
3428       else
3429       {
3430         // inflate from EDGE along FACE
3431         edge._normal = getFaceDir( TopoDS::Face( eos._sWOL ), TopoDS::Edge( eos._shape ),
3432                                    node, helper, normOK);
3433       }
3434     }
3435     else // layers are on all FACEs of SOLID the node is on (or fromVonF)
3436     {
3437       if ( fromVonF )
3438         face2Norm[ totalNbFaces++ ].first = TopoDS::Face( eos._sWOL );
3439
3440       int nbOkNorms = 0;
3441       for ( int iF = totalNbFaces - 1; iF >= 0; --iF )
3442       {
3443         F = face2Norm[ iF ].first;
3444         geomNorm = getFaceNormal( node, F, helper, normOK );
3445         if ( !normOK ) continue;
3446         nbOkNorms++;
3447
3448         if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3449           geomNorm.Reverse();
3450         face2Norm[ iF ].second = geomNorm.XYZ();
3451         edge._normal += geomNorm.XYZ();
3452       }
3453       if ( nbOkNorms == 0 )
3454         return error(SMESH_Comment("Can't get normal to node ") << node->GetID(), data._index);
3455
3456       if ( totalNbFaces >= 3 )
3457       {
3458         edge._normal = getNormalByOffset( &edge, face2Norm, totalNbFaces, fromVonF );
3459       }
3460
3461       if ( edge._normal.Modulus() < 1e-3 && nbOkNorms > 1 )
3462       {
3463         // opposite normals, re-get normals at shifted positions (IPAL 52426)
3464         edge._normal.SetCoord( 0,0,0 );
3465         for ( int iF = 0; iF < totalNbFaces - fromVonF; ++iF )
3466         {
3467           const TopoDS_Face& F = face2Norm[iF].first;
3468           geomNorm = getFaceNormal( node, F, helper, normOK, /*shiftInside=*/true );
3469           if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3470             geomNorm.Reverse();
3471           if ( normOK )
3472             face2Norm[ iF ].second = geomNorm.XYZ();
3473           edge._normal += face2Norm[ iF ].second;
3474         }
3475       }
3476     }
3477   }
3478   else // !useGeometry - get _normal using surrounding mesh faces
3479   {
3480     edge._normal = getWeigthedNormal( &edge );
3481
3482     // set<TGeomID> faceIds;
3483     //
3484     // SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
3485     // while ( fIt->more() )
3486     // {
3487     //   const SMDS_MeshElement* face = fIt->next();
3488     //   if ( eos.GetNormal( face, geomNorm ))
3489     //   {
3490     //     if ( onShrinkShape && !faceIds.insert( face->getshapeId() ).second )
3491     //       continue; // use only one mesh face on FACE
3492     //     edge._normal += geomNorm.XYZ();
3493     //     totalNbFaces++;
3494     //   }
3495     // }
3496   }
3497
3498   // compute _cosin
3499   //if ( eos._hyp.UseSurfaceNormal() )
3500   {
3501     switch ( eos.ShapeType() )
3502     {
3503     case TopAbs_FACE: {
3504       edge._cosin = 0;
3505       break;
3506     }
3507     case TopAbs_EDGE: {
3508       TopoDS_Edge E    = TopoDS::Edge( eos._shape );
3509       gp_Vec inFaceDir = getFaceDir( F, E, node, helper, normOK );
3510       double angle     = inFaceDir.Angle( edge._normal ); // [0,PI]
3511       edge._cosin      = Cos( angle );
3512       break;
3513     }
3514     case TopAbs_VERTEX: {
3515       if ( fromVonF )
3516       {
3517         getFaceDir( TopoDS::Face( eos._sWOL ), TopoDS::Vertex( eos._shape ),
3518                     node, helper, normOK, &edge._cosin );
3519       }
3520       else if ( eos.SWOLType() != TopAbs_FACE ) // else _cosin is set by getFaceDir()
3521       {
3522         TopoDS_Vertex V  = TopoDS::Vertex( eos._shape );
3523         gp_Vec inFaceDir = getFaceDir( F, V, node, helper, normOK );
3524         double angle     = inFaceDir.Angle( edge._normal ); // [0,PI]
3525         edge._cosin      = Cos( angle );
3526         if ( totalNbFaces > 2 || helper.IsSeamShape( node->getshapeId() ))
3527           for ( int iF = 1; iF < totalNbFaces; ++iF )
3528           {
3529             F = face2Norm[ iF ].first;
3530             inFaceDir = getFaceDir( F, V, node, helper, normOK=true );
3531             if ( normOK ) {
3532               double angle = inFaceDir.Angle( edge._normal );
3533               double cosin = Cos( angle );
3534               if ( Abs( cosin ) > Abs( edge._cosin ))
3535                 edge._cosin = cosin;
3536             }
3537           }
3538       }
3539       break;
3540     }
3541     default:
3542       return error(SMESH_Comment("Invalid shape position of node ")<<node, data._index);
3543     }
3544   }
3545
3546   double normSize = edge._normal.SquareModulus();
3547   if ( normSize < numeric_limits<double>::min() )
3548     return error(SMESH_Comment("Bad normal at node ")<< node->GetID(), data._index );
3549
3550   edge._normal /= sqrt( normSize );
3551
3552   if ( edge.Is( _LayerEdge::MULTI_NORMAL ) && edge._nodes.size() == 2 )
3553   {
3554     getMeshDS()->RemoveFreeNode( edge._nodes.back(), 0, /*fromGroups=*/false );
3555     edge._nodes.resize( 1 );
3556     edge._normal.SetCoord( 0,0,0 );
3557     edge._maxLen = 0;
3558   }
3559
3560   // Set the rest data
3561   // --------------------
3562
3563   edge.SetCosin( edge._cosin ); // to update edge._lenFactor
3564
3565   if ( onShrinkShape )
3566   {
3567     const SMDS_MeshNode* tgtNode = edge._nodes.back();
3568     if ( SMESHDS_SubMesh* sm = getMeshDS()->MeshElements( data._solid ))
3569       sm->RemoveNode( tgtNode , /*isNodeDeleted=*/false );
3570
3571     // set initial position which is parameters on _sWOL in this case
3572     if ( eos.SWOLType() == TopAbs_EDGE )
3573     {
3574       double u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), node, 0, &normOK );
3575       edge._pos.push_back( gp_XYZ( u, 0, 0 ));
3576       if ( edge._nodes.size() > 1 )
3577         getMeshDS()->SetNodeOnEdge( tgtNode, TopoDS::Edge( eos._sWOL ), u );
3578     }
3579     else // eos.SWOLType() == TopAbs_FACE
3580     {
3581       gp_XY uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), node, 0, &normOK );
3582       edge._pos.push_back( gp_XYZ( uv.X(), uv.Y(), 0));
3583       if ( edge._nodes.size() > 1 )
3584         getMeshDS()->SetNodeOnFace( tgtNode, TopoDS::Face( eos._sWOL ), uv.X(), uv.Y() );
3585     }
3586
3587     if ( edge._nodes.size() > 1 )
3588     {
3589       // check if an angle between a FACE with layers and SWOL is sharp,
3590       // else the edge should not inflate
3591       F.Nullify();
3592       for ( int iF = 0; iF < totalNbFaces  &&  F.IsNull();  ++iF ) // find a FACE with VL
3593         if ( ! helper.IsSubShape( eos._sWOL, face2Norm[iF].first ))
3594           F = face2Norm[iF].first;
3595       if ( !F.IsNull())
3596       {
3597         geomNorm = getFaceNormal( node, F, helper, normOK );
3598         if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3599           geomNorm.Reverse(); // inside the SOLID
3600         if ( geomNorm * edge._normal < -0.001 )
3601         {
3602           getMeshDS()->RemoveFreeNode( tgtNode, 0, /*fromGroups=*/false );
3603           edge._nodes.resize( 1 );
3604         }
3605         else if ( edge._lenFactor > 3 )
3606         {
3607           edge._lenFactor = 2;
3608           edge.Set( _LayerEdge::RISKY_SWOL );
3609         }
3610       }
3611     }
3612   }
3613   else
3614   {
3615     edge._pos.push_back( SMESH_TNodeXYZ( node ));
3616
3617     if ( eos.ShapeType() == TopAbs_FACE )
3618     {
3619       double angle;
3620       for ( size_t i = 0; i < edge._simplices.size(); ++i )
3621       {
3622         edge._simplices[i].IsMinAngleOK( edge._pos.back(), angle );
3623         edge._minAngle = Max( edge._minAngle, angle ); // "angle" is actually cosine
3624       }
3625     }
3626   }
3627
3628   // Set neighbor nodes for a _LayerEdge based on EDGE
3629
3630   if ( eos.ShapeType() == TopAbs_EDGE /*||
3631        ( onShrinkShape && posType == SMDS_TOP_VERTEX && fabs( edge._cosin ) < 1e-10 )*/)
3632   {
3633     edge._2neibors = new _2NearEdges;
3634     // target nodes instead of source ones will be set later
3635   }
3636
3637   return true;
3638 }
3639
3640 //================================================================================
3641 /*!
3642  * \brief Return normal to a FACE at a node
3643  *  \param [in] n - node
3644  *  \param [in] face - FACE
3645  *  \param [in] helper - helper
3646  *  \param [out] isOK - true or false
3647  *  \param [in] shiftInside - to find normal at a position shifted inside the face
3648  *  \return gp_XYZ - normal
3649  */
3650 //================================================================================
3651
3652 gp_XYZ _ViscousBuilder::getFaceNormal(const SMDS_MeshNode* node,
3653                                       const TopoDS_Face&   face,
3654                                       SMESH_MesherHelper&  helper,
3655                                       bool&                isOK,
3656                                       bool                 shiftInside)
3657 {
3658   gp_XY uv;
3659   if ( shiftInside )
3660   {
3661     // get a shifted position
3662     gp_Pnt p = SMESH_TNodeXYZ( node );
3663     gp_XYZ shift( 0,0,0 );
3664     TopoDS_Shape S = helper.GetSubShapeByNode( node, helper.GetMeshDS() );
3665     switch ( S.ShapeType() ) {
3666     case TopAbs_VERTEX:
3667     {
3668       shift = getFaceDir( face, TopoDS::Vertex( S ), node, helper, isOK );
3669       break;
3670     }
3671     case TopAbs_EDGE:
3672     {
3673       shift = getFaceDir( face, TopoDS::Edge( S ), node, helper, isOK );
3674       break;
3675     }
3676     default:
3677       isOK = false;
3678     }
3679     if ( isOK )
3680       shift.Normalize();
3681     p.Translate( shift * 1e-5 );
3682
3683     TopLoc_Location loc;
3684     GeomAPI_ProjectPointOnSurf& projector = helper.GetProjector( face, loc, 1e-7 );
3685
3686     if ( !loc.IsIdentity() ) p.Transform( loc.Transformation().Inverted() );
3687     
3688     projector.Perform( p );
3689     if ( !projector.IsDone() || projector.NbPoints() < 1 )
3690     {
3691       isOK = false;
3692       return p.XYZ();
3693     }
3694     Standard_Real U,V;
3695     projector.LowerDistanceParameters(U,V);
3696     uv.SetCoord( U,V );
3697   }
3698   else
3699   {
3700     uv = helper.GetNodeUV( face, node, 0, &isOK );
3701   }
3702
3703   gp_Dir normal;
3704   isOK = false;
3705
3706   Handle(Geom_Surface) surface = BRep_Tool::Surface( face );
3707
3708   if ( !shiftInside &&
3709        helper.IsDegenShape( node->getshapeId() ) &&
3710        getFaceNormalAtSingularity( uv, face, helper, normal ))
3711   {
3712     isOK = true;
3713     return normal.XYZ();
3714   }
3715
3716   int pointKind = GeomLib::NormEstim( surface, uv, 1e-5, normal );
3717   enum { REGULAR = 0, QUASYSINGULAR, CONICAL, IMPOSSIBLE };
3718
3719   if ( pointKind == IMPOSSIBLE &&
3720        node->GetPosition()->GetDim() == 2 ) // node inside the FACE
3721   {
3722     // probably NormEstim() failed due to a too high tolerance
3723     pointKind = GeomLib::NormEstim( surface, uv, 1e-20, normal );
3724     isOK = ( pointKind < IMPOSSIBLE );
3725   }
3726   if ( pointKind < IMPOSSIBLE )
3727   {
3728     if ( pointKind != REGULAR &&
3729          !shiftInside &&
3730          node->GetPosition()->GetDim() < 2 ) // FACE boundary
3731     {
3732       gp_XYZ normShift = getFaceNormal( node, face, helper, isOK, /*shiftInside=*/true );
3733       if ( normShift * normal.XYZ() < 0. )
3734         normal = normShift;
3735     }
3736     isOK = true;
3737   }
3738
3739   if ( !isOK ) // hard singularity, to call with shiftInside=true ?
3740   {
3741     const TGeomID faceID = helper.GetMeshDS()->ShapeToIndex( face );
3742
3743     SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
3744     while ( fIt->more() )
3745     {
3746       const SMDS_MeshElement* f = fIt->next();
3747       if ( f->getshapeId() == faceID )
3748       {
3749         isOK = SMESH_MeshAlgos::FaceNormal( f, (gp_XYZ&) normal.XYZ(), /*normalized=*/true );
3750         if ( isOK )
3751         {
3752           TopoDS_Face ff = face;
3753           ff.Orientation( TopAbs_FORWARD );
3754           if ( helper.IsReversedSubMesh( ff ))
3755             normal.Reverse();
3756           break;
3757         }
3758       }
3759     }
3760   }
3761   return normal.XYZ();
3762 }
3763
3764 //================================================================================
3765 /*!
3766  * \brief Try to get normal at a singularity of a surface basing on it's nature
3767  */
3768 //================================================================================
3769
3770 bool _ViscousBuilder::getFaceNormalAtSingularity( const gp_XY&        uv,
3771                                                   const TopoDS_Face&  face,
3772                                                   SMESH_MesherHelper& helper,
3773                                                   gp_Dir&             normal )
3774 {
3775   BRepAdaptor_Surface surface( face );
3776   gp_Dir axis;
3777   if ( !getRovolutionAxis( surface, axis ))
3778     return false;
3779
3780   double f,l, d, du, dv;
3781   f = surface.FirstUParameter();
3782   l = surface.LastUParameter();
3783   d = ( uv.X() - f ) / ( l - f );
3784   du = ( d < 0.5 ? +1. : -1 ) * 1e-5 * ( l - f );
3785   f = surface.FirstVParameter();
3786   l = surface.LastVParameter();
3787   d = ( uv.Y() - f ) / ( l - f );
3788   dv = ( d < 0.5 ? +1. : -1 ) * 1e-5 * ( l - f );
3789
3790   gp_Dir refDir;
3791   gp_Pnt2d testUV = uv;
3792   enum { REGULAR = 0, QUASYSINGULAR, CONICAL, IMPOSSIBLE };
3793   double tol = 1e-5;
3794   Handle(Geom_Surface) geomsurf = surface.Surface().Surface();
3795   for ( int iLoop = 0; true ; ++iLoop )
3796   {
3797     testUV.SetCoord( testUV.X() + du, testUV.Y() + dv );
3798     if ( GeomLib::NormEstim( geomsurf, testUV, tol, refDir ) == REGULAR )
3799       break;
3800     if ( iLoop > 20 )
3801       return false;
3802     tol /= 10.;
3803   }
3804
3805   if ( axis * refDir < 0. )
3806     axis.Reverse();
3807
3808   normal = axis;
3809
3810   return true;
3811 }
3812
3813 //================================================================================
3814 /*!
3815  * \brief Return a normal at a node weighted with angles taken by faces
3816  */
3817 //================================================================================
3818
3819 gp_XYZ _ViscousBuilder::getWeigthedNormal( const _LayerEdge* edge )
3820 {
3821   const SMDS_MeshNode* n = edge->_nodes[0];
3822
3823   gp_XYZ resNorm(0,0,0);
3824   SMESH_TNodeXYZ p0( n ), pP, pN;
3825   for ( size_t i = 0; i < edge->_simplices.size(); ++i )
3826   {
3827     pP.Set( edge->_simplices[i]._nPrev );
3828     pN.Set( edge->_simplices[i]._nNext );
3829     gp_Vec v0P( p0, pP ), v0N( p0, pN ), vPN( pP, pN ), norm = v0P ^ v0N;
3830     double l0P = v0P.SquareMagnitude();
3831     double l0N = v0N.SquareMagnitude();
3832     double lPN = vPN.SquareMagnitude();
3833     if ( l0P < std::numeric_limits<double>::min() ||
3834          l0N < std::numeric_limits<double>::min() ||
3835          lPN < std::numeric_limits<double>::min() )
3836       continue;
3837     double lNorm = norm.SquareMagnitude();
3838     double  sin2 = lNorm / l0P / l0N;
3839     double angle = ACos(( v0P * v0N ) / Sqrt( l0P ) / Sqrt( l0N ));
3840
3841     double weight = sin2 * angle / lPN;
3842     resNorm += weight * norm.XYZ() / Sqrt( lNorm );
3843   }
3844
3845   return resNorm;
3846 }
3847
3848 //================================================================================
3849 /*!
3850  * \brief Return a normal at a node by getting a common point of offset planes
3851  *        defined by the FACE normals
3852  */
3853 //================================================================================
3854
3855 gp_XYZ _ViscousBuilder::getNormalByOffset( _LayerEdge*                      edge,
3856                                            std::pair< TopoDS_Face, gp_XYZ > f2Normal[],
3857                                            int                              nbFaces,
3858                                            bool                             lastNoOffset)
3859 {
3860   SMESH_TNodeXYZ p0 = edge->_nodes[0];
3861
3862   gp_XYZ resNorm(0,0,0);
3863   TopoDS_Shape V = SMESH_MesherHelper::GetSubShapeByNode( p0._node, getMeshDS() );
3864   if ( V.ShapeType() != TopAbs_VERTEX || nbFaces < 3 )
3865   {
3866     for ( int i = 0; i < nbFaces; ++i )
3867       resNorm += f2Normal[i].second;
3868     return resNorm;
3869   }
3870
3871   // prepare _OffsetPlane's
3872   vector< _OffsetPlane > pln( nbFaces );
3873   for ( int i = 0; i < nbFaces - lastNoOffset; ++i )
3874   {
3875     pln[i]._faceIndex = i;
3876     pln[i]._plane = gp_Pln( p0 + f2Normal[i].second, f2Normal[i].second );
3877   }
3878   if ( lastNoOffset )
3879   {
3880     pln[ nbFaces - 1 ]._faceIndex = nbFaces - 1;
3881     pln[ nbFaces - 1 ]._plane = gp_Pln( p0, f2Normal[ nbFaces - 1 ].second );
3882   }
3883
3884   // intersect neighboring OffsetPlane's
3885   PShapeIteratorPtr edgeIt = SMESH_MesherHelper::GetAncestors( V, *_mesh, TopAbs_EDGE );
3886   while ( const TopoDS_Shape* edge = edgeIt->next() )
3887   {
3888     int f1 = -1, f2 = -1;
3889     for ( int i = 0; i < nbFaces &&  f2 < 0;  ++i )
3890       if ( SMESH_MesherHelper::IsSubShape( *edge, f2Normal[i].first ))
3891         (( f1 < 0 ) ? f1 : f2 ) = i;
3892
3893     if ( f2 >= 0 )
3894       pln[ f1 ].ComputeIntersectionLine( pln[ f2 ], TopoDS::Edge( *edge ), TopoDS::Vertex( V ));
3895   }
3896
3897   // get a common point
3898   gp_XYZ commonPnt( 0, 0, 0 );
3899   int nbPoints = 0;
3900   bool isPointFound;
3901   for ( int i = 0; i < nbFaces; ++i )
3902   {
3903     commonPnt += pln[ i ].GetCommonPoint( isPointFound, TopoDS::Vertex( V ));
3904     nbPoints  += isPointFound;
3905   }
3906   gp_XYZ wgtNorm = getWeigthedNormal( edge );
3907   if ( nbPoints == 0 )
3908     return wgtNorm;
3909
3910   commonPnt /= nbPoints;
3911   resNorm = commonPnt - p0;
3912   if ( lastNoOffset )
3913     return resNorm;
3914
3915   // choose the best among resNorm and wgtNorm
3916   resNorm.Normalize();
3917   wgtNorm.Normalize();
3918   double resMinDot = std::numeric_limits<double>::max();
3919   double wgtMinDot = std::numeric_limits<double>::max();
3920   for ( int i = 0; i < nbFaces - lastNoOffset; ++i )
3921   {
3922     resMinDot = Min( resMinDot, resNorm * f2Normal[i].second );
3923     wgtMinDot = Min( wgtMinDot, wgtNorm * f2Normal[i].second );
3924   }
3925
3926   if ( Max( resMinDot, wgtMinDot ) < theMinSmoothCosin )
3927   {
3928     edge->Set( _LayerEdge::MULTI_NORMAL );
3929   }
3930
3931   return ( resMinDot > wgtMinDot ) ? resNorm : wgtNorm;
3932 }
3933
3934 //================================================================================
3935 /*!
3936  * \brief Compute line of intersection of 2 planes
3937  */
3938 //================================================================================
3939
3940 void _OffsetPlane::ComputeIntersectionLine( _OffsetPlane&        pln,
3941                                             const TopoDS_Edge&   E,
3942                                             const TopoDS_Vertex& V )
3943 {
3944   int iNext = bool( _faceIndexNext[0] >= 0 );
3945   _faceIndexNext[ iNext ] = pln._faceIndex;
3946
3947   gp_XYZ n1 = _plane.Axis().Direction().XYZ();
3948   gp_XYZ n2 = pln._plane.Axis().Direction().XYZ();
3949
3950   gp_XYZ lineDir = n1 ^ n2;
3951
3952   double x = Abs( lineDir.X() );
3953   double y = Abs( lineDir.Y() );
3954   double z = Abs( lineDir.Z() );
3955
3956   int cooMax; // max coordinate
3957   if (x > y) {
3958     if (x > z) cooMax = 1;
3959     else       cooMax = 3;
3960   }
3961   else {
3962     if (y > z) cooMax = 2;
3963     else       cooMax = 3;
3964   }
3965
3966   gp_Pnt linePos;
3967   if ( Abs( lineDir.Coord( cooMax )) < 0.05 )
3968   {
3969     // parallel planes - intersection is an offset of the common EDGE
3970     gp_Pnt p = BRep_Tool::Pnt( V );
3971     linePos  = 0.5 * (( p.XYZ() + n1 ) + ( p.XYZ() + n2 ));
3972     lineDir  = getEdgeDir( E, V );
3973   }
3974   else
3975   {
3976     // the constants in the 2 plane equations
3977     double d1 = - ( _plane.Axis().Direction().XYZ()     * _plane.Location().XYZ() );
3978     double d2 = - ( pln._plane.Axis().Direction().XYZ() * pln._plane.Location().XYZ() );
3979
3980     switch ( cooMax ) {
3981     case 1:
3982       linePos.SetX(  0 );
3983       linePos.SetY(( d2*n1.Z() - d1*n2.Z()) / lineDir.X() );
3984       linePos.SetZ(( d1*n2.Y() - d2*n1.Y()) / lineDir.X() );
3985       break;
3986     case 2:
3987       linePos.SetX(( d1*n2.Z() - d2*n1.Z()) / lineDir.Y() );
3988       linePos.SetY(  0 );
3989       linePos.SetZ(( d2*n1.X() - d1*n2.X()) / lineDir.Y() );
3990       break;
3991     case 3:
3992       linePos.SetX(( d2*n1.Y() - d1*n2.Y()) / lineDir.Z() );
3993       linePos.SetY(( d1*n2.X() - d2*n1.X()) / lineDir.Z() );
3994       linePos.SetZ(  0 );
3995     }
3996   }
3997   gp_Lin& line = _lines[ iNext ];
3998   line.SetDirection( lineDir );
3999   line.SetLocation ( linePos );
4000
4001   _isLineOK[ iNext ] = true;
4002
4003
4004   iNext = bool( pln._faceIndexNext[0] >= 0 );
4005   pln._lines        [ iNext ] = line;
4006   pln._faceIndexNext[ iNext ] = this->_faceIndex;
4007   pln._isLineOK     [ iNext ] = true;
4008 }
4009
4010 //================================================================================
4011 /*!
4012  * \brief Computes intersection point of two _lines
4013  */
4014 //================================================================================
4015
4016 gp_XYZ _OffsetPlane::GetCommonPoint(bool&                 isFound,
4017                                     const TopoDS_Vertex & V) const
4018 {
4019   gp_XYZ p( 0,0,0 );
4020   isFound = false;
4021
4022   if ( NbLines() == 2 )
4023   {
4024     gp_Vec lPerp0 = _lines[0].Direction().XYZ() ^ _plane.Axis().Direction().XYZ();
4025     double  dot01 = lPerp0 * _lines[1].Direction().XYZ();
4026     if ( Abs( dot01 ) > 0.05 )
4027     {
4028       gp_Vec l0l1 = _lines[1].Location().XYZ() - _lines[0].Location().XYZ();
4029       double   u1 = - ( lPerp0 * l0l1 ) / dot01;
4030       p = ( _lines[1].Location().XYZ() + _lines[1].Direction().XYZ() * u1 );
4031       isFound = true;
4032     }
4033     else
4034     {
4035       gp_Pnt  pV ( BRep_Tool::Pnt( V ));
4036       gp_Vec  lv0( _lines[0].Location(), pV    ),  lv1(_lines[1].Location(), pV     );
4037       double dot0( lv0 * _lines[0].Direction() ), dot1( lv1 * _lines[1].Direction() );
4038       p += 0.5 * ( _lines[0].Location().XYZ() + _lines[0].Direction().XYZ() * dot0 );
4039       p += 0.5 * ( _lines[1].Location().XYZ() + _lines[1].Direction().XYZ() * dot1 );
4040       isFound = true;
4041     }
4042   }
4043
4044   return p;
4045 }
4046
4047 //================================================================================
4048 /*!
4049  * \brief Find 2 neigbor nodes of a node on EDGE
4050  */
4051 //================================================================================
4052
4053 bool _ViscousBuilder::findNeiborsOnEdge(const _LayerEdge*     edge,
4054                                         const SMDS_MeshNode*& n1,
4055                                         const SMDS_MeshNode*& n2,
4056                                         _EdgesOnShape&        eos,
4057                                         _SolidData&           data)
4058 {
4059   const SMDS_MeshNode* node = edge->_nodes[0];
4060   const int        shapeInd = eos._shapeID;
4061   SMESHDS_SubMesh*   edgeSM = 0;
4062   if ( eos.ShapeType() == TopAbs_EDGE )
4063   {
4064     edgeSM = eos._subMesh->GetSubMeshDS();
4065     if ( !edgeSM || edgeSM->NbElements() == 0 )
4066       return error(SMESH_Comment("Not meshed EDGE ") << shapeInd, data._index);
4067   }
4068   int iN = 0;
4069   n2 = 0;
4070   SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Edge);
4071   while ( eIt->more() && !n2 )
4072   {
4073     const SMDS_MeshElement* e = eIt->next();
4074     const SMDS_MeshNode*   nNeibor = e->GetNode( 0 );
4075     if ( nNeibor == node ) nNeibor = e->GetNode( 1 );
4076     if ( edgeSM )
4077     {
4078       if (!edgeSM->Contains(e)) continue;
4079     }
4080     else
4081     {
4082       TopoDS_Shape s = SMESH_MesherHelper::GetSubShapeByNode( nNeibor, getMeshDS() );
4083       if ( !SMESH_MesherHelper::IsSubShape( s, eos._sWOL )) continue;
4084     }
4085     ( iN++ ? n2 : n1 ) = nNeibor;
4086   }
4087   if ( !n2 )
4088     return error(SMESH_Comment("Wrongly meshed EDGE ") << shapeInd, data._index);
4089   return true;
4090 }
4091
4092 //================================================================================
4093 /*!
4094  * \brief Set _curvature and _2neibors->_plnNorm by 2 neigbor nodes residing the same EDGE
4095  */
4096 //================================================================================
4097
4098 void _LayerEdge::SetDataByNeighbors( const SMDS_MeshNode* n1,
4099                                      const SMDS_MeshNode* n2,
4100                                      const _EdgesOnShape& eos,
4101                                      SMESH_MesherHelper&  helper)
4102 {
4103   if ( eos.ShapeType() != TopAbs_EDGE )
4104     return;
4105
4106   gp_XYZ  pos = SMESH_TNodeXYZ( _nodes[0] );
4107   gp_XYZ vec1 = pos - SMESH_TNodeXYZ( n1 );
4108   gp_XYZ vec2 = pos - SMESH_TNodeXYZ( n2 );
4109
4110   // Set _curvature
4111
4112   double      sumLen = vec1.Modulus() + vec2.Modulus();
4113   _2neibors->_wgt[0] = 1 - vec1.Modulus() / sumLen;
4114   _2neibors->_wgt[1] = 1 - vec2.Modulus() / sumLen;
4115   double avgNormProj = 0.5 * ( _normal * vec1 + _normal * vec2 );
4116   double      avgLen = 0.5 * ( vec1.Modulus() + vec2.Modulus() );
4117   if ( _curvature ) delete _curvature;
4118   _curvature = _Curvature::New( avgNormProj, avgLen );
4119   // if ( _curvature )
4120   //   debugMsg( _nodes[0]->GetID()
4121   //             << " CURV r,k: " << _curvature->_r<<","<<_curvature->_k
4122   //             << " proj = "<<avgNormProj<< " len = " << avgLen << "| lenDelta(0) = "
4123   //             << _curvature->lenDelta(0) );
4124
4125   // Set _plnNorm
4126
4127   if ( eos._sWOL.IsNull() )
4128   {
4129     TopoDS_Edge  E = TopoDS::Edge( eos._shape );
4130     // if ( SMESH_Algo::isDegenerated( E ))
4131     //   return;
4132     gp_XYZ dirE    = getEdgeDir( E, _nodes[0], helper );
4133     gp_XYZ plnNorm = dirE ^ _normal;
4134     double proj0   = plnNorm * vec1;
4135     double proj1   = plnNorm * vec2;
4136     if ( fabs( proj0 ) > 1e-10 || fabs( proj1 ) > 1e-10 )
4137     {
4138       if ( _2neibors->_plnNorm ) delete _2neibors->_plnNorm;
4139       _2neibors->_plnNorm = new gp_XYZ( plnNorm.Normalized() );
4140     }
4141   }
4142 }
4143
4144 //================================================================================
4145 /*!
4146  * \brief Copy data from a _LayerEdge of other SOLID and based on the same node;
4147  * this and the other _LayerEdge are inflated along a FACE or an EDGE
4148  */
4149 //================================================================================
4150
4151 gp_XYZ _LayerEdge::Copy( _LayerEdge&         other,
4152                          _EdgesOnShape&      eos,
4153                          SMESH_MesherHelper& helper )
4154 {
4155   _nodes     = other._nodes;
4156   _normal    = other._normal;
4157   _len       = 0;
4158   _lenFactor = other._lenFactor;
4159   _cosin     = other._cosin;
4160   _2neibors  = other._2neibors;
4161   _curvature = 0; std::swap( _curvature, other._curvature );
4162   _2neibors  = 0; std::swap( _2neibors,  other._2neibors );
4163
4164   gp_XYZ lastPos( 0,0,0 );
4165   if ( eos.SWOLType() == TopAbs_EDGE )
4166   {
4167     double u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), _nodes[0] );
4168     _pos.push_back( gp_XYZ( u, 0, 0));
4169
4170     u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), _nodes.back() );
4171     lastPos.SetX( u );
4172   }
4173   else // TopAbs_FACE
4174   {
4175     gp_XY uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), _nodes[0]);
4176     _pos.push_back( gp_XYZ( uv.X(), uv.Y(), 0));
4177
4178     uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), _nodes.back() );
4179     lastPos.SetX( uv.X() );
4180     lastPos.SetY( uv.Y() );
4181   }
4182   return lastPos;
4183 }
4184
4185 //================================================================================
4186 /*!
4187  * \brief Set _cosin and _lenFactor
4188  */
4189 //================================================================================
4190
4191 void _LayerEdge::SetCosin( double cosin )
4192 {
4193   _cosin = cosin;
4194   cosin = Abs( _cosin );
4195   //_lenFactor = ( cosin < 1.-1e-12 ) ?  Min( 2., 1./sqrt(1-cosin*cosin )) : 1.0;
4196   _lenFactor = ( cosin < 1.-1e-12 ) ?  1./sqrt(1-cosin*cosin ) : 1.0;
4197 }
4198
4199 //================================================================================
4200 /*!
4201  * \brief Check if another _LayerEdge is a neighbor on EDGE
4202  */
4203 //================================================================================
4204
4205 bool _LayerEdge::IsNeiborOnEdge( const _LayerEdge* edge ) const
4206 {
4207   return (( this->_2neibors && this->_2neibors->include( edge )) ||
4208           ( edge->_2neibors && edge->_2neibors->include( this )));
4209 }
4210
4211 //================================================================================
4212 /*!
4213  * \brief Fills a vector<_Simplex > 
4214  */
4215 //================================================================================
4216
4217 void _Simplex::GetSimplices( const SMDS_MeshNode* node,
4218                              vector<_Simplex>&    simplices,
4219                              const set<TGeomID>&  ingnoreShapes,
4220                              const _SolidData*    dataToCheckOri,
4221                              const bool           toSort)
4222 {
4223   simplices.clear();
4224   SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
4225   while ( fIt->more() )
4226   {
4227     const SMDS_MeshElement* f = fIt->next();
4228     const TGeomID    shapeInd = f->getshapeId();
4229     if ( ingnoreShapes.count( shapeInd )) continue;
4230     const int nbNodes = f->NbCornerNodes();
4231     const int  srcInd = f->GetNodeIndex( node );
4232     const SMDS_MeshNode* nPrev = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd-1, nbNodes ));
4233     const SMDS_MeshNode* nNext = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd+1, nbNodes ));
4234     const SMDS_MeshNode* nOpp  = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd+2, nbNodes ));
4235     if ( dataToCheckOri && dataToCheckOri->_reversedFaceIds.count( shapeInd ))
4236       std::swap( nPrev, nNext );
4237     simplices.push_back( _Simplex( nPrev, nNext, ( nbNodes == 3 ? 0 : nOpp )));
4238   }
4239
4240   if ( toSort )
4241     SortSimplices( simplices );
4242 }
4243
4244 //================================================================================
4245 /*!
4246  * \brief Set neighbor simplices side by side
4247  */
4248 //================================================================================
4249
4250 void _Simplex::SortSimplices(vector<_Simplex>& simplices)
4251 {
4252   vector<_Simplex> sortedSimplices( simplices.size() );
4253   sortedSimplices[0] = simplices[0];
4254   size_t nbFound = 0;
4255   for ( size_t i = 1; i < simplices.size(); ++i )
4256   {
4257     for ( size_t j = 1; j < simplices.size(); ++j )
4258       if ( sortedSimplices[i-1]._nNext == simplices[j]._nPrev )
4259       {
4260         sortedSimplices[i] = simplices[j];
4261         nbFound++;
4262         break;
4263       }
4264   }
4265   if ( nbFound == simplices.size() - 1 )
4266     simplices.swap( sortedSimplices );
4267 }
4268
4269 //================================================================================
4270 /*!
4271  * \brief DEBUG. Create groups contating temorary data of _LayerEdge's
4272  */
4273 //================================================================================
4274
4275 void _ViscousBuilder::makeGroupOfLE()
4276 {
4277 #ifdef _DEBUG_
4278   for ( size_t i = 0 ; i < _sdVec.size(); ++i )
4279   {
4280     if ( _sdVec[i]._n2eMap.empty() ) continue;
4281
4282     dumpFunction( SMESH_Comment("make_LayerEdge_") << i );
4283     TNode2Edge::iterator n2e;
4284     for ( n2e = _sdVec[i]._n2eMap.begin(); n2e != _sdVec[i]._n2eMap.end(); ++n2e )
4285     {
4286       _LayerEdge* le = n2e->second;
4287       // for ( size_t iN = 1; iN < le->_nodes.size(); ++iN )
4288       //   dumpCmd(SMESH_Comment("mesh.AddEdge([ ") <<le->_nodes[iN-1]->GetID()
4289       //           << ", " << le->_nodes[iN]->GetID() <<"])");
4290       if ( le ) {
4291         dumpCmd(SMESH_Comment("mesh.AddEdge([ ") <<le->_nodes[0]->GetID()
4292                 << ", " << le->_nodes.back()->GetID() <<"]) # " << le->_flags );
4293       }
4294     }
4295     dumpFunctionEnd();
4296
4297     dumpFunction( SMESH_Comment("makeNormals") << i );
4298     for ( n2e = _sdVec[i]._n2eMap.begin(); n2e != _sdVec[i]._n2eMap.end(); ++n2e )
4299     {
4300       _LayerEdge* edge = n2e->second;
4301       SMESH_TNodeXYZ nXYZ( edge->_nodes[0] );
4302       nXYZ += edge->_normal * _sdVec[i]._stepSize;
4303       dumpCmd(SMESH_Comment("mesh.AddEdge([ ") << edge->_nodes[0]->GetID()
4304               << ", mesh.AddNode( "<< nXYZ.X()<<","<< nXYZ.Y()<<","<< nXYZ.Z()<<")])");
4305     }
4306     dumpFunctionEnd();
4307
4308     dumpFunction( SMESH_Comment("makeTmpFaces_") << i );
4309     dumpCmd( "faceId1 = mesh.NbElements()" );
4310     TopExp_Explorer fExp( _sdVec[i]._solid, TopAbs_FACE );
4311     for ( ; fExp.More(); fExp.Next() )
4312     {
4313       if ( const SMESHDS_SubMesh* sm = _sdVec[i]._proxyMesh->GetProxySubMesh( fExp.Current() ))
4314       {
4315         if ( sm->NbElements() == 0 ) continue;
4316         SMDS_ElemIteratorPtr fIt = sm->GetElements();
4317         while ( fIt->more())
4318         {
4319           const SMDS_MeshElement* e = fIt->next();
4320           SMESH_Comment cmd("mesh.AddFace([");
4321           for ( int j = 0; j < e->NbCornerNodes(); ++j )
4322             cmd << e->GetNode(j)->GetID() << (j+1 < e->NbCornerNodes() ? ",": "])");
4323           dumpCmd( cmd );
4324         }
4325       }
4326     }
4327     dumpCmd( "faceId2 = mesh.NbElements()" );
4328     dumpCmd( SMESH_Comment( "mesh.MakeGroup( 'tmpFaces_" ) << i << "',"
4329              << "SMESH.FACE, SMESH.FT_RangeOfIds,'=',"
4330              << "'%s-%s' % (faceId1+1, faceId2))");
4331     dumpFunctionEnd();
4332   }
4333 #endif
4334 }
4335
4336 //================================================================================
4337 /*!
4338  * \brief Find maximal _LayerEdge length (layer thickness) limited by geometry
4339  */
4340 //================================================================================
4341
4342 void _ViscousBuilder::computeGeomSize( _SolidData& data )
4343 {
4344   data._geomSize = Precision::Infinite();
4345   double intersecDist;
4346   const SMDS_MeshElement* face;
4347   SMESH_MesherHelper helper( *_mesh );
4348
4349   SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
4350     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
4351                                            data._proxyMesh->GetFaces( data._solid )));
4352
4353   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4354   {
4355     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4356     if ( eos._edges.empty() )
4357       continue;
4358     // get neighbor faces, intersection with which should not be considered since
4359     // collisions are avoided by means of smoothing
4360     set< TGeomID > neighborFaces;
4361     if ( eos._hyp.ToSmooth() )
4362     {
4363       SMESH_subMeshIteratorPtr subIt =
4364         eos._subMesh->getDependsOnIterator(/*includeSelf=*/eos.ShapeType() != TopAbs_FACE );
4365       while ( subIt->more() )
4366       {
4367         SMESH_subMesh* sm = subIt->next();
4368         PShapeIteratorPtr fIt = helper.GetAncestors( sm->GetSubShape(), *_mesh, TopAbs_FACE );
4369         while ( const TopoDS_Shape* face = fIt->next() )
4370           neighborFaces.insert( getMeshDS()->ShapeToIndex( *face ));
4371       }
4372     }
4373     // find intersections
4374     double thinkness = eos._hyp.GetTotalThickness();
4375     for ( size_t i = 0; i < eos._edges.size(); ++i )
4376     {
4377       if ( eos._edges[i]->Is( _LayerEdge::BLOCKED )) continue;
4378       eos._edges[i]->_maxLen = thinkness;
4379       eos._edges[i]->FindIntersection( *searcher, intersecDist, data._epsilon, eos, &face );
4380       if ( intersecDist > 0 && face )
4381       {
4382         data._geomSize = Min( data._geomSize, intersecDist );
4383         if ( !neighborFaces.count( face->getshapeId() ))
4384           eos._edges[i]->_maxLen = Min( thinkness, intersecDist / ( face->GetID() < 0 ? 3. : 2. ));
4385       }
4386     }
4387   }
4388
4389   data._maxThickness = 0;
4390   data._minThickness = 1e100;
4391   list< const StdMeshers_ViscousLayers* >::iterator hyp = data._hyps.begin();
4392   for ( ; hyp != data._hyps.end(); ++hyp )
4393   {
4394     data._maxThickness = Max( data._maxThickness, (*hyp)->GetTotalThickness() );
4395     data._minThickness = Min( data._minThickness, (*hyp)->GetTotalThickness() );
4396   }
4397
4398   // Limit inflation step size by geometry size found by intersecting
4399   // normals of _LayerEdge's with mesh faces
4400   if ( data._stepSize > 0.3 * data._geomSize )
4401     limitStepSize( data, 0.3 * data._geomSize );
4402
4403   if ( data._stepSize > data._minThickness )
4404     limitStepSize( data, data._minThickness );
4405
4406
4407   // -------------------------------------------------------------------------
4408   // Detect _LayerEdge which can't intersect with opposite or neighbor layer,
4409   // so no need in detecting intersection at each inflation step
4410   // -------------------------------------------------------------------------
4411
4412   int nbSteps = data._maxThickness / data._stepSize;
4413   if ( nbSteps < 3 || nbSteps * data._n2eMap.size() < 100000 )
4414     return;
4415
4416   vector< const SMDS_MeshElement* > closeFaces;
4417   int nbDetected = 0;
4418
4419   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4420   {
4421     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4422     if ( eos._edges.empty() || eos.ShapeType() != TopAbs_FACE )
4423       continue;
4424
4425     for ( size_t i = 0; i < eos.size(); ++i )
4426     {
4427       SMESH_NodeXYZ p( eos[i]->_nodes[0] );
4428       double radius = data._maxThickness + 2 * eos[i]->_maxLen;
4429       closeFaces.clear();
4430       searcher->GetElementsInSphere( p, radius, SMDSAbs_Face, closeFaces );
4431
4432       bool toIgnore = true;
4433       for ( size_t iF = 0; iF < closeFaces.size()  && toIgnore; ++iF )
4434         if ( !( toIgnore = ( closeFaces[ iF ]->getshapeId() == eos._shapeID ||
4435                              data._ignoreFaceIds.count( closeFaces[ iF ]->getshapeId() ))))
4436         {
4437           // check if a _LayerEdge will inflate in a direction opposite to a direction
4438           // toward a close face
4439           bool allBehind = true;
4440           for ( int iN = 0; iN < closeFaces[ iF ]->NbCornerNodes()  && allBehind; ++iN )
4441           {
4442             SMESH_NodeXYZ pi( closeFaces[ iF ]->GetNode( iN ));
4443             allBehind = (( pi - p ) * eos[i]->_normal < 0.1 * data._stepSize );
4444           }
4445           toIgnore = allBehind;
4446         }
4447
4448
4449       if ( toIgnore ) // no need to detect intersection
4450       {
4451         eos[i]->Set( _LayerEdge::INTERSECTED );
4452         ++nbDetected;
4453       }
4454     }
4455   }
4456
4457   debugMsg( "Nb LE to intersect " << data._n2eMap.size()-nbDetected << ", ignore " << nbDetected );
4458
4459   return;
4460 }
4461
4462 //================================================================================
4463 /*!
4464  * \brief Increase length of _LayerEdge's to reach the required thickness of layers
4465  */
4466 //================================================================================
4467
4468 bool _ViscousBuilder::inflate(_SolidData& data)
4469 {
4470   SMESH_MesherHelper helper( *_mesh );
4471
4472   const double tgtThick = data._maxThickness;
4473
4474   if ( data._stepSize < 1. )
4475     data._epsilon = data._stepSize * 1e-7;
4476
4477   debugMsg( "-- geomSize = " << data._geomSize << ", stepSize = " << data._stepSize );
4478   _pyDump->Pause();
4479
4480   findCollisionEdges( data, helper );
4481
4482   limitMaxLenByCurvature( data, helper );
4483
4484   _pyDump->Resume();
4485
4486   // limit length of _LayerEdge's around MULTI_NORMAL _LayerEdge's
4487   for ( size_t i = 0; i < data._edgesOnShape.size(); ++i )
4488     if ( data._edgesOnShape[i].ShapeType() == TopAbs_VERTEX &&
4489          data._edgesOnShape[i]._edges.size() > 0 &&
4490          data._edgesOnShape[i]._edges[0]->Is( _LayerEdge::MULTI_NORMAL ))
4491     {
4492       data._edgesOnShape[i]._edges[0]->Unset( _LayerEdge::BLOCKED );
4493       data._edgesOnShape[i]._edges[0]->Block( data );
4494     }
4495
4496   const double safeFactor = ( 2*data._maxThickness < data._geomSize ) ? 1 : theThickToIntersection;
4497
4498   double avgThick = 0, curThick = 0, distToIntersection = Precision::Infinite();
4499   int nbSteps = 0, nbRepeats = 0;
4500   while ( avgThick < 0.99 )
4501   {
4502     // new target length
4503     double prevThick = curThick;
4504     curThick += data._stepSize;
4505     if ( curThick > tgtThick )
4506     {
4507       curThick = tgtThick + tgtThick*( 1.-avgThick ) * nbRepeats;
4508       nbRepeats++;
4509     }
4510
4511     double stepSize = curThick - prevThick;
4512     updateNormalsOfSmoothed( data, helper, nbSteps, stepSize ); // to ease smoothing
4513
4514     // Elongate _LayerEdge's
4515     dumpFunction(SMESH_Comment("inflate")<<data._index<<"_step"<<nbSteps); // debug
4516     for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4517     {
4518       _EdgesOnShape& eos = data._edgesOnShape[iS];
4519       if ( eos._edges.empty() ) continue;
4520
4521       const double shapeCurThick = Min( curThick, eos._hyp.GetTotalThickness() );
4522       for ( size_t i = 0; i < eos._edges.size(); ++i )
4523       {
4524         eos._edges[i]->SetNewLength( shapeCurThick, eos, helper );
4525       }
4526     }
4527     dumpFunctionEnd();
4528
4529     if ( !updateNormals( data, helper, nbSteps, stepSize )) // to avoid collisions
4530       return false;
4531
4532     // Improve and check quality
4533     if ( !smoothAndCheck( data, nbSteps, distToIntersection ))
4534     {
4535       if ( nbSteps > 0 )
4536       {
4537 #ifdef __NOT_INVALIDATE_BAD_SMOOTH
4538         debugMsg("NOT INVALIDATED STEP!");
4539         return error("Smoothing failed", data._index);
4540 #endif
4541         dumpFunction(SMESH_Comment("invalidate")<<data._index<<"_step"<<nbSteps); // debug
4542         for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4543         {
4544           _EdgesOnShape& eos = data._edgesOnShape[iS];
4545           for ( size_t i = 0; i < eos._edges.size(); ++i )
4546             eos._edges[i]->InvalidateStep( nbSteps+1, eos );
4547         }
4548         dumpFunctionEnd();
4549       }
4550       break; // no more inflating possible
4551     }
4552     nbSteps++;
4553
4554     // Evaluate achieved thickness
4555     avgThick = 0;
4556     int nbActiveEdges = 0;
4557     for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4558     {
4559       _EdgesOnShape& eos = data._edgesOnShape[iS];
4560       if ( eos._edges.empty() ) continue;
4561
4562       const double shapeTgtThick = eos._hyp.GetTotalThickness();
4563       for ( size_t i = 0; i < eos._edges.size(); ++i )
4564       {
4565         if ( eos._edges[i]->_nodes.size() > 1 )
4566           avgThick    += Min( 1., eos._edges[i]->_len / shapeTgtThick );
4567         else
4568           avgThick    += shapeTgtThick;
4569         nbActiveEdges += ( ! eos._edges[i]->Is( _LayerEdge::BLOCKED ));
4570       }
4571     }
4572     avgThick /= data._n2eMap.size();
4573     debugMsg( "-- Thickness " << curThick << " ("<< avgThick*100 << "%) reached" );
4574
4575 #ifdef BLOCK_INFLATION
4576     if ( nbActiveEdges == 0 )
4577     {
4578       debugMsg( "-- Stop inflation since all _LayerEdge's BLOCKED " );
4579       break;
4580     }
4581 #else
4582     if ( distToIntersection < tgtThick * avgThick * safeFactor && avgThick < 0.9 )
4583     {
4584       debugMsg( "-- Stop inflation since "
4585                 << " distToIntersection( "<<distToIntersection<<" ) < avgThick( "
4586                 << tgtThick * avgThick << " ) * " << safeFactor );
4587       break;
4588     }
4589 #endif
4590
4591     // new step size
4592     limitStepSize( data, 0.25 * distToIntersection );
4593     if ( data._stepSizeNodes[0] )
4594       data._stepSize = data._stepSizeCoeff *
4595         SMESH_TNodeXYZ(data._stepSizeNodes[0]).Distance(data._stepSizeNodes[1]);
4596
4597   } // while ( avgThick < 0.99 )
4598
4599   if ( nbSteps == 0 )
4600     return error("failed at the very first inflation step", data._index);
4601
4602   if ( avgThick < 0.99 )
4603   {
4604     if ( !data._proxyMesh->_warning || data._proxyMesh->_warning->IsOK() )
4605     {
4606       data._proxyMesh->_warning.reset
4607         ( new SMESH_ComputeError (COMPERR_WARNING,
4608                                   SMESH_Comment("Thickness ") << tgtThick <<
4609                                   " of viscous layers not reached,"
4610                                   " average reached thickness is " << avgThick*tgtThick));
4611     }
4612   }
4613
4614   // Restore position of src nodes moved by inflation on _noShrinkShapes
4615   dumpFunction(SMESH_Comment("restoNoShrink_So")<<data._index); // debug
4616   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4617   {
4618     _EdgesOnShape& eos = data._edgesOnShape[iS];
4619     if ( !eos._edges.empty() && eos._edges[0]->_nodes.size() == 1 )
4620       for ( size_t i = 0; i < eos._edges.size(); ++i )
4621       {
4622         restoreNoShrink( *eos._edges[ i ] );
4623       }
4624   }
4625   dumpFunctionEnd();
4626
4627   return safeFactor > 0; // == true (avoid warning: unused variable 'safeFactor')
4628 }
4629
4630 //================================================================================
4631 /*!
4632  * \brief Improve quality of layer inner surface and check intersection
4633  */
4634 //================================================================================
4635
4636 bool _ViscousBuilder::smoothAndCheck(_SolidData& data,
4637                                      const int   infStep,
4638                                      double &    distToIntersection)
4639 {
4640   if ( data._nbShapesToSmooth == 0 )
4641     return true; // no shapes needing smoothing
4642
4643   bool moved, improved;
4644   double vol;
4645   vector< _LayerEdge* >    movedEdges, badEdges;
4646   vector< _EdgesOnShape* > eosC1; // C1 continues shapes
4647   vector< bool >           isConcaveFace;
4648
4649   SMESH_MesherHelper helper(*_mesh);
4650   Handle(ShapeAnalysis_Surface) surface;
4651   TopoDS_Face F;
4652
4653   for ( int isFace = 0; isFace < 2; ++isFace ) // smooth on [ EDGEs, FACEs ]
4654   {
4655     const TopAbs_ShapeEnum shapeType = isFace ? TopAbs_FACE : TopAbs_EDGE;
4656
4657     for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4658     {
4659       _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4660       if ( !eos._toSmooth ||
4661            eos.ShapeType() != shapeType ||
4662            eos._edges.empty() )
4663         continue;
4664
4665       // already smoothed?
4666       // bool toSmooth = ( eos._edges[ 0 ]->NbSteps() >= infStep+1 );
4667       // if ( !toSmooth ) continue;
4668
4669       if ( !eos._hyp.ToSmooth() )
4670       {
4671         // smooth disabled by the user; check validy only
4672         if ( !isFace ) continue;
4673         badEdges.clear();
4674         for ( size_t i = 0; i < eos._edges.size(); ++i )
4675         {
4676           _LayerEdge* edge = eos._edges[i];
4677           for ( size_t iF = 0; iF < edge->_simplices.size(); ++iF )
4678             if ( !edge->_simplices[iF].IsForward( edge->_nodes[0], edge->_pos.back(), vol ))
4679             {
4680               // debugMsg( "-- Stop inflation. Bad simplex ("
4681               //           << " "<< edge->_nodes[0]->GetID()
4682               //           << " "<< edge->_nodes.back()->GetID()
4683               //           << " "<< edge->_simplices[iF]._nPrev->GetID()
4684               //           << " "<< edge->_simplices[iF]._nNext->GetID() << " ) ");
4685               // return false;
4686               badEdges.push_back( edge );
4687             }
4688         }
4689         if ( !badEdges.empty() )
4690         {
4691           eosC1.resize(1);
4692           eosC1[0] = &eos;
4693           int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
4694           if ( nbBad > 0 )
4695             return false;
4696         }
4697         continue; // goto the next EDGE or FACE
4698       }
4699
4700       // prepare data
4701       if ( eos.SWOLType() == TopAbs_FACE )
4702       {
4703         if ( !F.IsSame( eos._sWOL )) {
4704           F = TopoDS::Face( eos._sWOL );
4705           helper.SetSubShape( F );
4706           surface = helper.GetSurface( F );
4707         }
4708       }
4709       else
4710       {
4711         F.Nullify(); surface.Nullify();
4712       }
4713       const TGeomID sInd = eos._shapeID;
4714
4715       // perform smoothing
4716
4717       if ( eos.ShapeType() == TopAbs_EDGE )
4718       {
4719         dumpFunction(SMESH_Comment("smooth")<<data._index << "_Ed"<<sInd <<"_InfStep"<<infStep);
4720
4721         if ( !eos._edgeSmoother->Perform( data, surface, F, helper ))
4722         {
4723           // smooth on EDGE's (normally we should not get here)
4724           int step = 0;
4725           do {
4726             moved = false;
4727             for ( size_t i = 0; i < eos._edges.size(); ++i )
4728             {
4729               moved |= eos._edges[i]->SmoothOnEdge( surface, F, helper );
4730             }
4731             dumpCmd( SMESH_Comment("# end step ")<<step);
4732           }
4733           while ( moved && step++ < 5 );
4734         }
4735         dumpFunctionEnd();
4736       }
4737
4738       else // smooth on FACE
4739       {
4740         eosC1.clear();
4741         eosC1.push_back( & eos );
4742         eosC1.insert( eosC1.end(), eos._eosC1.begin(), eos._eosC1.end() );
4743
4744         movedEdges.clear();
4745         isConcaveFace.resize( eosC1.size() );
4746         for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
4747         {
4748           isConcaveFace[ iEOS ] = data._concaveFaces.count( eosC1[ iEOS ]->_shapeID  );
4749           vector< _LayerEdge* > & edges = eosC1[ iEOS ]->_edges;
4750           for ( size_t i = 0; i < edges.size(); ++i )
4751             if ( edges[i]->Is( _LayerEdge::MOVED ) ||
4752                  edges[i]->Is( _LayerEdge::NEAR_BOUNDARY ))
4753               movedEdges.push_back( edges[i] );
4754
4755           makeOffsetSurface( *eosC1[ iEOS ], helper );
4756         }
4757
4758         int step = 0, stepLimit = 5, nbBad = 0;
4759         while (( ++step <= stepLimit ) || improved )
4760         {
4761           dumpFunction(SMESH_Comment("smooth")<<data._index<<"_Fa"<<sInd
4762                        <<"_InfStep"<<infStep<<"_"<<step); // debug
4763           int oldBadNb = nbBad;
4764           badEdges.clear();
4765
4766 #ifdef INCREMENTAL_SMOOTH
4767           bool findBest = false; // ( step == stepLimit );
4768           for ( size_t i = 0; i < movedEdges.size(); ++i )
4769           {
4770             movedEdges[i]->Unset( _LayerEdge::SMOOTHED );
4771             if ( movedEdges[i]->Smooth( step, findBest, movedEdges ) > 0 )
4772               badEdges.push_back( movedEdges[i] );
4773           }
4774 #else
4775           bool findBest = ( step == stepLimit || isConcaveFace[ iEOS ]);
4776           for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
4777           {
4778             vector< _LayerEdge* > & edges = eosC1[ iEOS ]->_edges;
4779             for ( size_t i = 0; i < edges.size(); ++i )
4780             {
4781               edges[i]->Unset( _LayerEdge::SMOOTHED );
4782               if ( edges[i]->Smooth( step, findBest, false ) > 0 )
4783                 badEdges.push_back( eos._edges[i] );
4784             }
4785           }
4786 #endif
4787           nbBad = badEdges.size();
4788
4789           if ( nbBad > 0 )
4790             debugMsg(SMESH_Comment("nbBad = ") << nbBad );
4791
4792           if ( !badEdges.empty() && step >= stepLimit / 2 )
4793           {
4794             if ( badEdges[0]->Is( _LayerEdge::ON_CONCAVE_FACE ))
4795               stepLimit = 9;
4796
4797             // resolve hard smoothing situation around concave VERTEXes
4798             for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
4799             {
4800               vector< _EdgesOnShape* > & eosCoVe = eosC1[ iEOS ]->_eosConcaVer;
4801               for ( size_t i = 0; i < eosCoVe.size(); ++i )
4802                 eosCoVe[i]->_edges[0]->MoveNearConcaVer( eosCoVe[i], eosC1[ iEOS ],
4803                                                          step, badEdges );
4804             }
4805             // look for the best smooth of _LayerEdge's neighboring badEdges
4806             nbBad = 0;
4807             for ( size_t i = 0; i < badEdges.size(); ++i )
4808             {
4809               _LayerEdge* ledge = badEdges[i];
4810               for ( size_t iN = 0; iN < ledge->_neibors.size(); ++iN )
4811               {
4812                 ledge->_neibors[iN]->Unset( _LayerEdge::SMOOTHED );
4813                 nbBad += ledge->_neibors[iN]->Smooth( step, true, /*findBest=*/true );
4814               }
4815               ledge->Unset( _LayerEdge::SMOOTHED );
4816               nbBad += ledge->Smooth( step, true, /*findBest=*/true );
4817             }
4818             debugMsg(SMESH_Comment("nbBad = ") << nbBad );
4819           }
4820
4821           if ( nbBad == oldBadNb  &&
4822                nbBad > 0 &&
4823                step < stepLimit ) // smooth w/o chech of validity
4824           {
4825             dumpFunctionEnd();
4826             dumpFunction(SMESH_Comment("smoothWoCheck")<<data._index<<"_Fa"<<sInd
4827                          <<"_InfStep"<<infStep<<"_"<<step); // debug
4828             for ( size_t i = 0; i < movedEdges.size(); ++i )
4829             {
4830               movedEdges[i]->SmoothWoCheck();
4831             }
4832             if ( stepLimit < 9 )
4833               stepLimit++;
4834           }
4835
4836           improved = ( nbBad < oldBadNb );
4837
4838           dumpFunctionEnd();
4839
4840           if (( step % 3 == 1 ) || ( nbBad > 0 && step >= stepLimit / 2 ))
4841             for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
4842             {
4843               putOnOffsetSurface( *eosC1[ iEOS ], infStep, eosC1, step, /*moveAll=*/step == 1 );
4844             }
4845
4846         } // smoothing steps
4847
4848         // project -- to prevent intersections or fix bad simplices
4849         for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
4850         {
4851           if ( ! eosC1[ iEOS ]->_eosConcaVer.empty() || nbBad > 0 )
4852             putOnOffsetSurface( *eosC1[ iEOS ], infStep, eosC1 );
4853         }
4854
4855         //if ( !badEdges.empty() )
4856         {
4857           badEdges.clear();
4858           for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
4859           {
4860             for ( size_t i = 0; i < eosC1[ iEOS ]->_edges.size(); ++i )
4861             {
4862               if ( !eosC1[ iEOS ]->_sWOL.IsNull() ) continue;
4863
4864               _LayerEdge* edge = eosC1[ iEOS ]->_edges[i];
4865               edge->CheckNeiborsOnBoundary( & badEdges );
4866               if (( nbBad > 0 ) ||
4867                   ( edge->Is( _LayerEdge::BLOCKED ) && edge->Is( _LayerEdge::NEAR_BOUNDARY )))
4868               {
4869                 SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
4870                 gp_XYZ        prevXYZ = edge->PrevCheckPos();
4871                 for ( size_t j = 0; j < edge->_simplices.size(); ++j )
4872                   if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
4873                   {
4874                     debugMsg("Bad simplex ( " << edge->_nodes[0]->GetID()
4875                              << " "<< tgtXYZ._node->GetID()
4876                              << " "<< edge->_simplices[j]._nPrev->GetID()
4877                              << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
4878                     badEdges.push_back( edge );
4879                     break;
4880                   }
4881               }
4882             }
4883           }
4884
4885           // try to fix bad simplices by removing the last inflation step of some _LayerEdge's
4886           nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
4887
4888           if ( nbBad > 0 )
4889             return false;
4890         }
4891
4892       } // // smooth on FACE's
4893     } // loop on shapes
4894   } // smooth on [ EDGEs, FACEs ]
4895
4896   // Check orientation of simplices of _LayerEdge's on EDGEs and VERTEXes
4897   eosC1.resize(1);
4898   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4899   {
4900     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4901     if ( eos.ShapeType() == TopAbs_FACE ||
4902          eos._edges.empty() ||
4903          !eos._sWOL.IsNull() )
4904       continue;
4905
4906     badEdges.clear();
4907     for ( size_t i = 0; i < eos._edges.size(); ++i )
4908     {
4909       _LayerEdge*      edge = eos._edges[i];
4910       if ( edge->_nodes.size() < 2 ) continue;
4911       SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
4912       //SMESH_TNodeXYZ prevXYZ = edge->_nodes[0];
4913       gp_XYZ        prevXYZ = edge->PrevCheckPos( &eos );
4914       //const gp_XYZ& prevXYZ = edge->PrevPos();
4915       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
4916         if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
4917         {
4918           debugMsg("Bad simplex on bnd ( " << edge->_nodes[0]->GetID()
4919                    << " "<< tgtXYZ._node->GetID()
4920                    << " "<< edge->_simplices[j]._nPrev->GetID()
4921                    << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
4922           badEdges.push_back( edge );
4923           break;
4924         }
4925     }
4926
4927     // try to fix bad simplices by removing the last inflation step of some _LayerEdge's
4928     eosC1[0] = &eos;
4929     int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
4930     if ( nbBad > 0 )
4931       return false;
4932   }
4933
4934
4935   // Check if the last segments of _LayerEdge intersects 2D elements;
4936   // checked elements are either temporary faces or faces on surfaces w/o the layers
4937
4938   SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
4939     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
4940                                            data._proxyMesh->GetFaces( data._solid )) );
4941
4942 #ifdef BLOCK_INFLATION
4943   const bool toBlockInfaltion = true;
4944 #else
4945   const bool toBlockInfaltion = false;
4946 #endif
4947   distToIntersection = Precision::Infinite();
4948   double dist;
4949   const SMDS_MeshElement* intFace = 0;
4950   const SMDS_MeshElement* closestFace = 0;
4951   _LayerEdge* le = 0;
4952   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4953   {
4954     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4955     if ( eos._edges.empty() || !eos._sWOL.IsNull() )
4956       continue;
4957     for ( size_t i = 0; i < eos._edges.size(); ++i )
4958     {
4959       if ( eos._edges[i]->Is( _LayerEdge::INTERSECTED ) ||
4960            eos._edges[i]->Is( _LayerEdge::MULTI_NORMAL ))
4961         continue;
4962       if ( eos._edges[i]->FindIntersection( *searcher, dist, data._epsilon, eos, &intFace ))
4963       {
4964         return false;
4965         // commented due to "Illegal hash-positionPosition" error in NETGEN
4966         // on Debian60 on viscous_layers_01/B2 case
4967         // Collision; try to deflate _LayerEdge's causing it
4968         // badEdges.clear();
4969         // badEdges.push_back( eos._edges[i] );
4970         // eosC1[0] = & eos;
4971         // int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
4972         // if ( nbBad > 0 )
4973         //   return false;
4974
4975         // badEdges.clear();
4976         // if ( _EdgesOnShape* eof = data.GetShapeEdges( intFace->getshapeId() ))
4977         // {
4978         //   if ( const _TmpMeshFace* f = dynamic_cast< const _TmpMeshFace*>( intFace ))
4979         //   {
4980         //     const SMDS_MeshElement* srcFace =
4981         //       eof->_subMesh->GetSubMeshDS()->GetElement( f->getIdInShape() );
4982         //     SMDS_ElemIteratorPtr nIt = srcFace->nodesIterator();
4983         //     while ( nIt->more() )
4984         //     {
4985         //       const SMDS_MeshNode* srcNode = static_cast<const SMDS_MeshNode*>( nIt->next() );
4986         //       TNode2Edge::iterator n2e = data._n2eMap.find( srcNode );
4987         //       if ( n2e != data._n2eMap.end() )
4988         //         badEdges.push_back( n2e->second );
4989         //     }
4990         //     eosC1[0] = eof;
4991         //     nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
4992         //     if ( nbBad > 0 )
4993         //       return false;
4994         //   }
4995         // }
4996         // if ( eos._edges[i]->FindIntersection( *searcher, dist, data._epsilon, eos, &intFace ))
4997         //   return false;
4998         // else
4999         //   continue;
5000       }
5001       if ( !intFace )
5002       {
5003         SMESH_Comment msg("Invalid? normal at node "); msg << eos._edges[i]->_nodes[0]->GetID();
5004         debugMsg( msg );
5005         continue;
5006       }
5007
5008       const bool isShorterDist = ( distToIntersection > dist );
5009       if ( toBlockInfaltion || isShorterDist )
5010       {
5011         // ignore intersection of a _LayerEdge based on a _ConvexFace with a face
5012         // lying on this _ConvexFace
5013         if ( _ConvexFace* convFace = data.GetConvexFace( intFace->getshapeId() ))
5014           if ( convFace->_isTooCurved && convFace->_subIdToEOS.count ( eos._shapeID ))
5015             continue;
5016
5017         // ignore intersection of a _LayerEdge based on a FACE with an element on this FACE
5018         // ( avoid limiting the thickness on the case of issue 22576)
5019         if ( intFace->getshapeId() == eos._shapeID  )
5020           continue;
5021
5022         // ignore intersection with intFace of an adjacent FACE
5023         if ( dist > 0 )
5024         {
5025           bool toIgnore = false;
5026           if (  eos._toSmooth )
5027           {
5028             const TopoDS_Shape& S = getMeshDS()->IndexToShape( intFace->getshapeId() );
5029             if ( !S.IsNull() && S.ShapeType() == TopAbs_FACE )
5030             {
5031               TopExp_Explorer sub( eos._shape,
5032                                    eos.ShapeType() == TopAbs_FACE ? TopAbs_EDGE : TopAbs_VERTEX );
5033               for ( ; !toIgnore && sub.More(); sub.Next() )
5034                 // is adjacent - has a common EDGE or VERTEX
5035                 toIgnore = ( helper.IsSubShape( sub.Current(), S ));
5036
5037               if ( toIgnore ) // check angle between normals
5038               {
5039                 gp_XYZ normal;
5040                 if ( SMESH_MeshAlgos::FaceNormal( intFace, normal, /*normalized=*/true ))
5041                   toIgnore  = ( normal * eos._edges[i]->_normal > -0.5 );
5042               }
5043             }
5044           }
5045           if ( !toIgnore ) // check if the edge is a neighbor of intFace
5046           {
5047             for ( size_t iN = 0; !toIgnore &&  iN < eos._edges[i]->_neibors.size(); ++iN )
5048             {
5049               int nInd = intFace->GetNodeIndex( eos._edges[i]->_neibors[ iN ]->_nodes.back() );
5050               toIgnore = ( nInd >= 0 );
5051             }
5052           }
5053           if ( toIgnore )
5054             continue;
5055         }
5056
5057         // intersection not ignored
5058
5059         if ( toBlockInfaltion &&
5060              dist < ( eos._edges[i]->_len * theThickToIntersection ))
5061         {
5062           eos._edges[i]->Set( _LayerEdge::INTERSECTED ); // not to intersect
5063           eos._edges[i]->Block( data );                  // not to inflate
5064
5065           if ( _EdgesOnShape* eof = data.GetShapeEdges( intFace->getshapeId() ))
5066           {
5067             // block _LayerEdge's, on top of which intFace is
5068             if ( const _TmpMeshFace* f = dynamic_cast< const _TmpMeshFace*>( intFace ))
5069             {
5070               const SMDS_MeshElement* srcFace =
5071                 eof->_subMesh->GetSubMeshDS()->GetElement( f->getIdInShape() );
5072               SMDS_ElemIteratorPtr nIt = srcFace->nodesIterator();
5073               while ( nIt->more() )
5074               {
5075                 const SMDS_MeshNode* srcNode = static_cast<const SMDS_MeshNode*>( nIt->next() );
5076                 TNode2Edge::iterator n2e = data._n2eMap.find( srcNode );
5077                 if ( n2e != data._n2eMap.end() )
5078                   n2e->second->Block( data );
5079               }
5080             }
5081           }
5082         }
5083
5084         if ( isShorterDist )
5085         {
5086           distToIntersection = dist;
5087           le = eos._edges[i];
5088           closestFace = intFace;
5089         }
5090
5091       } // if ( toBlockInfaltion || isShorterDist )
5092     } // loop on eos._edges
5093   } // loop on data._edgesOnShape
5094
5095   if ( closestFace && le )
5096   {
5097 #ifdef __myDEBUG
5098     SMDS_MeshElement::iterator nIt = closestFace->begin_nodes();
5099     cout << "Shortest distance: _LayerEdge nodes: tgt " << le->_nodes.back()->GetID()
5100          << " src " << le->_nodes[0]->GetID()<< ", intersection with face ("
5101          << (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()
5102          << ") distance = " << distToIntersection<< endl;
5103 #endif
5104   }
5105
5106   return true;
5107 }
5108
5109 //================================================================================
5110 /*!
5111  * \brief try to fix bad simplices by removing the last inflation step of some _LayerEdge's
5112  *  \param [in,out] badSmooEdges - _LayerEdge's to fix
5113  *  \return int - resulting nb of bad _LayerEdge's
5114  */
5115 //================================================================================
5116
5117 int _ViscousBuilder::invalidateBadSmooth( _SolidData&               data,
5118                                           SMESH_MesherHelper&       helper,
5119                                           vector< _LayerEdge* >&    badSmooEdges,
5120                                           vector< _EdgesOnShape* >& eosC1,
5121                                           const int                 infStep )
5122 {
5123   if ( badSmooEdges.empty() || infStep == 0 ) return 0;
5124
5125   dumpFunction(SMESH_Comment("invalidateBadSmooth")<<"_S"<<eosC1[0]->_shapeID<<"_InfStep"<<infStep);
5126
5127   enum {
5128     INVALIDATED   = _LayerEdge::UNUSED_FLAG,
5129     TO_INVALIDATE = _LayerEdge::UNUSED_FLAG * 2,
5130     ADDED         = _LayerEdge::UNUSED_FLAG * 4
5131   };
5132   data.UnmarkEdges( TO_INVALIDATE & INVALIDATED & ADDED );
5133
5134   double vol;
5135   bool haveInvalidated = true;
5136   while ( haveInvalidated )
5137   {
5138     haveInvalidated = false;
5139     for ( size_t i = 0; i < badSmooEdges.size(); ++i )
5140     {
5141       _LayerEdge*   edge = badSmooEdges[i];
5142       _EdgesOnShape* eos = data.GetShapeEdges( edge );
5143       edge->Set( ADDED );
5144       bool invalidated = false;
5145       if ( edge->Is( TO_INVALIDATE ) && edge->NbSteps() > 1 )
5146       {
5147         edge->InvalidateStep( edge->NbSteps(), *eos, /*restoreLength=*/true );
5148         edge->Block( data );
5149         edge->Set( INVALIDATED );
5150         edge->Unset( TO_INVALIDATE );
5151         invalidated = true;
5152         haveInvalidated = true;
5153       }
5154
5155       // look for _LayerEdge's of bad _simplices
5156       int nbBad = 0;
5157       SMESH_TNodeXYZ tgtXYZ  = edge->_nodes.back();
5158       gp_XYZ        prevXYZ1 = edge->PrevCheckPos( eos );
5159       //const gp_XYZ& prevXYZ2 = edge->PrevPos();
5160       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5161       {
5162         if (( edge->_simplices[j].IsForward( &prevXYZ1, &tgtXYZ, vol ))/* &&
5163             ( &prevXYZ1 == &prevXYZ2 || edge->_simplices[j].IsForward( &prevXYZ2, &tgtXYZ, vol ))*/)
5164           continue;
5165
5166         bool isBad = true;
5167         _LayerEdge* ee[2] = { 0,0 };
5168         for ( size_t iN = 0; iN < edge->_neibors.size() &&   !ee[1]  ; ++iN )
5169           if ( edge->_simplices[j].Includes( edge->_neibors[iN]->_nodes.back() ))
5170             ee[ ee[0] != 0 ] = edge->_neibors[iN];
5171
5172         int maxNbSteps = Max( ee[0]->NbSteps(), ee[1]->NbSteps() );
5173         while ( maxNbSteps > edge->NbSteps() && isBad )
5174         {
5175           --maxNbSteps;
5176           for ( int iE = 0; iE < 2; ++iE )
5177           {
5178             if ( ee[ iE ]->NbSteps() > maxNbSteps &&
5179                  ee[ iE ]->NbSteps() > 1 )
5180             {
5181               _EdgesOnShape* eos = data.GetShapeEdges( ee[ iE ] );
5182               ee[ iE ]->InvalidateStep( ee[ iE ]->NbSteps(), *eos, /*restoreLength=*/true );
5183               ee[ iE ]->Block( data );
5184               ee[ iE ]->Set( INVALIDATED );
5185               haveInvalidated = true;
5186             }
5187           }
5188           if (( edge->_simplices[j].IsForward( &prevXYZ1, &tgtXYZ, vol )) /*&&
5189               ( &prevXYZ1 == &prevXYZ2 || edge->_simplices[j].IsForward( &prevXYZ2, &tgtXYZ, vol ))*/)
5190             isBad = false;
5191         }
5192         nbBad += isBad;
5193         if ( !ee[0]->Is( ADDED )) badSmooEdges.push_back( ee[0] );
5194         if ( !ee[1]->Is( ADDED )) badSmooEdges.push_back( ee[1] );
5195         ee[0]->Set( ADDED );
5196         ee[1]->Set( ADDED );
5197         if ( isBad )
5198         {
5199           ee[0]->Set( TO_INVALIDATE );
5200           ee[1]->Set( TO_INVALIDATE );
5201         }
5202       }
5203
5204       if ( !invalidated &&  nbBad > 0  &&  edge->NbSteps() > 1 )
5205       {
5206         _EdgesOnShape* eos = data.GetShapeEdges( edge );
5207         edge->InvalidateStep( edge->NbSteps(), *eos, /*restoreLength=*/true );
5208         edge->Block( data );
5209         edge->Set( INVALIDATED );
5210         edge->Unset( TO_INVALIDATE );
5211         haveInvalidated = true;
5212       }
5213     } // loop on badSmooEdges
5214   } // while ( haveInvalidated )
5215
5216   // re-smooth on analytical EDGEs
5217   for ( size_t i = 0; i < badSmooEdges.size(); ++i )
5218   {
5219     _LayerEdge* edge = badSmooEdges[i];
5220     if ( !edge->Is( INVALIDATED )) continue;
5221
5222     _EdgesOnShape* eos = data.GetShapeEdges( edge );
5223     if ( eos->ShapeType() == TopAbs_VERTEX )
5224     {
5225       PShapeIteratorPtr eIt = helper.GetAncestors( eos->_shape, *_mesh, TopAbs_EDGE );
5226       while ( const TopoDS_Shape* e = eIt->next() )
5227         if ( _EdgesOnShape* eoe = data.GetShapeEdges( *e ))
5228           if ( eoe->_edgeSmoother && eoe->_edgeSmoother->isAnalytic() )
5229           {
5230             // TopoDS_Face F; Handle(ShapeAnalysis_Surface) surface;
5231             // if ( eoe->SWOLType() == TopAbs_FACE ) {
5232             //   F       = TopoDS::Face( eoe->_sWOL );
5233             //   surface = helper.GetSurface( F );
5234             // }
5235             // eoe->_edgeSmoother->Perform( data, surface, F, helper );
5236             eoe->_edgeSmoother->_anaCurve.Nullify();
5237           }
5238     }
5239   }
5240
5241
5242   // check result of invalidation
5243
5244   int nbBad = 0;
5245   for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5246   {
5247     for ( size_t i = 0; i < eosC1[ iEOS ]->_edges.size(); ++i )
5248     {
5249       if ( !eosC1[ iEOS ]->_sWOL.IsNull() ) continue;
5250       _LayerEdge*      edge = eosC1[ iEOS ]->_edges[i];
5251       SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
5252       gp_XYZ        prevXYZ = edge->PrevCheckPos( eosC1[ iEOS ]);
5253       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5254         if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
5255         {
5256           ++nbBad;
5257           debugMsg("Bad simplex remains ( " << edge->_nodes[0]->GetID()
5258                    << " "<< tgtXYZ._node->GetID()
5259                    << " "<< edge->_simplices[j]._nPrev->GetID()
5260                    << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
5261         }
5262     }
5263   }
5264   dumpFunctionEnd();
5265
5266   return nbBad;
5267 }
5268
5269 //================================================================================
5270 /*!
5271  * \brief Create an offset surface
5272  */
5273 //================================================================================
5274
5275 void _ViscousBuilder::makeOffsetSurface( _EdgesOnShape& eos, SMESH_MesherHelper& helper )
5276 {
5277   if ( eos._offsetSurf.IsNull() ||
5278        eos._edgeForOffset == 0 ||
5279        eos._edgeForOffset->Is( _LayerEdge::BLOCKED ))
5280     return;
5281
5282   Handle(ShapeAnalysis_Surface) baseSurface = helper.GetSurface( TopoDS::Face( eos._shape ));
5283
5284   // find offset
5285   gp_Pnt   tgtP = SMESH_TNodeXYZ( eos._edgeForOffset->_nodes.back() );
5286   /*gp_Pnt2d uv=*/baseSurface->ValueOfUV( tgtP, Precision::Confusion() );
5287   double offset = baseSurface->Gap();
5288
5289   eos._offsetSurf.Nullify();
5290
5291   try
5292   {
5293     BRepOffsetAPI_MakeOffsetShape offsetMaker( eos._shape, -offset, Precision::Confusion() );
5294     if ( !offsetMaker.IsDone() ) return;
5295
5296     TopExp_Explorer fExp( offsetMaker.Shape(), TopAbs_FACE );
5297     if ( !fExp.More() ) return;
5298
5299     TopoDS_Face F = TopoDS::Face( fExp.Current() );
5300     Handle(Geom_Surface) surf = BRep_Tool::Surface( F );
5301     if ( surf.IsNull() ) return;
5302
5303     eos._offsetSurf = new ShapeAnalysis_Surface( surf );
5304   }
5305   catch ( Standard_Failure )
5306   {
5307   }
5308 }
5309
5310 //================================================================================
5311 /*!
5312  * \brief Put nodes of a curved FACE to its offset surface
5313  */
5314 //================================================================================
5315
5316 void _ViscousBuilder::putOnOffsetSurface( _EdgesOnShape&            eos,
5317                                           int                       infStep,
5318                                           vector< _EdgesOnShape* >& eosC1,
5319                                           int                       smooStep,
5320                                           int                       moveAll )
5321 {
5322   _EdgesOnShape * eof = & eos;
5323   if ( eos.ShapeType() != TopAbs_FACE ) // eos is a boundary of C1 FACE, look for the FACE eos
5324   {
5325     eof = 0;
5326     for ( size_t i = 0; i < eosC1.size() && !eof; ++i )
5327     {
5328       if ( eosC1[i]->_offsetSurf.IsNull() ||
5329            eosC1[i]->ShapeType() != TopAbs_FACE ||
5330            eosC1[i]->_edgeForOffset == 0 ||
5331            eosC1[i]->_edgeForOffset->Is( _LayerEdge::BLOCKED ))
5332         continue;
5333       if ( SMESH_MesherHelper::IsSubShape( eos._shape, eosC1[i]->_shape ))
5334         eof = eosC1[i];
5335     }
5336   }
5337   if ( !eof ||
5338        eof->_offsetSurf.IsNull() ||
5339        eof->ShapeType() != TopAbs_FACE ||
5340        eof->_edgeForOffset == 0 ||
5341        eof->_edgeForOffset->Is( _LayerEdge::BLOCKED ))
5342     return;
5343
5344   double preci = BRep_Tool::Tolerance( TopoDS::Face( eof->_shape )), vol;
5345   for ( size_t i = 0; i < eos._edges.size(); ++i )
5346   {
5347     _LayerEdge* edge = eos._edges[i];
5348     edge->Unset( _LayerEdge::MARKED );
5349     if ( edge->Is( _LayerEdge::BLOCKED ) || !edge->_curvature )
5350       continue;
5351     if ( moveAll == _LayerEdge::UPD_NORMAL_CONV )
5352     {
5353       if ( !edge->Is( _LayerEdge::UPD_NORMAL_CONV ))
5354         continue;
5355     }
5356     else if ( !moveAll && !edge->Is( _LayerEdge::MOVED ))
5357       continue;
5358
5359     int nbBlockedAround = 0;
5360     for ( size_t iN = 0; iN < edge->_neibors.size(); ++iN )
5361       nbBlockedAround += edge->_neibors[iN]->Is( _LayerEdge::BLOCKED );
5362     if ( nbBlockedAround > 1 )
5363       continue;
5364
5365     gp_Pnt tgtP = SMESH_TNodeXYZ( edge->_nodes.back() );
5366     gp_Pnt2d uv = eof->_offsetSurf->NextValueOfUV( edge->_curvature->_uv, tgtP, preci );
5367     if ( eof->_offsetSurf->Gap() > edge->_len ) continue; // NextValueOfUV() bug
5368     edge->_curvature->_uv = uv;
5369     if ( eof->_offsetSurf->Gap() < 10 * preci ) continue; // same pos
5370
5371     gp_XYZ  newP = eof->_offsetSurf->Value( uv ).XYZ();
5372     gp_XYZ prevP = edge->PrevCheckPos();
5373     bool      ok = true;
5374     if ( !moveAll )
5375       for ( size_t iS = 0; iS < edge->_simplices.size() && ok; ++iS )
5376       {
5377         ok = edge->_simplices[iS].IsForward( &prevP, &newP, vol );
5378       }
5379     if ( ok )
5380     {
5381       SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( edge->_nodes.back() );
5382       n->setXYZ( newP.X(), newP.Y(), newP.Z());
5383       edge->_pos.back() = newP;
5384
5385       edge->Set( _LayerEdge::MARKED );
5386       if ( moveAll == _LayerEdge::UPD_NORMAL_CONV )
5387       {
5388         edge->_normal = ( newP - prevP ).Normalized();
5389       }
5390     }
5391   }
5392
5393
5394
5395 #ifdef _DEBUG_
5396   // dumpMove() for debug
5397   size_t i = 0;
5398   for ( ; i < eos._edges.size(); ++i )
5399     if ( eos._edges[i]->Is( _LayerEdge::MARKED ))
5400       break;
5401   if ( i < eos._edges.size() )
5402   {
5403     dumpFunction(SMESH_Comment("putOnOffsetSurface_S") << eos._shapeID
5404                  << "_InfStep" << infStep << "_" << smooStep );
5405     for ( ; i < eos._edges.size(); ++i )
5406     {
5407       if ( eos._edges[i]->Is( _LayerEdge::MARKED ))
5408         dumpMove( eos._edges[i]->_nodes.back() );
5409     }
5410     dumpFunctionEnd();
5411   }
5412 #endif
5413
5414   _ConvexFace* cnvFace;
5415   if ( moveAll != _LayerEdge::UPD_NORMAL_CONV &&
5416        eos.ShapeType() == TopAbs_FACE &&
5417        (cnvFace = eos.GetData().GetConvexFace( eos._shapeID )) &&
5418        !cnvFace->_normalsFixedOnBorders )
5419   {
5420     // put on the surface nodes built on FACE boundaries
5421     SMESH_subMeshIteratorPtr smIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false);
5422     while ( smIt->more() )
5423     {
5424       SMESH_subMesh* sm = smIt->next();
5425       _EdgesOnShape* subEOS = eos.GetData().GetShapeEdges( sm->GetId() );
5426       if ( !subEOS->_sWOL.IsNull() ) continue;
5427       if ( std::find( eosC1.begin(), eosC1.end(), subEOS ) != eosC1.end() ) continue;
5428
5429       putOnOffsetSurface( *subEOS, infStep, eosC1, smooStep, _LayerEdge::UPD_NORMAL_CONV );
5430     }
5431     cnvFace->_normalsFixedOnBorders = true;
5432   }
5433 }
5434
5435 //================================================================================
5436 /*!
5437  * \brief Return a curve of the EDGE to be used for smoothing and arrange
5438  *        _LayerEdge's to be in a consequent order
5439  */
5440 //================================================================================
5441
5442 Handle(Geom_Curve) _Smoother1D::CurveForSmooth( const TopoDS_Edge&  E,
5443                                                 _EdgesOnShape&      eos,
5444                                                 SMESH_MesherHelper& helper)
5445 {
5446   SMESHDS_SubMesh* smDS = eos._subMesh->GetSubMeshDS();
5447
5448   TopLoc_Location loc; double f,l;
5449
5450   Handle(Geom_Line)   line;
5451   Handle(Geom_Circle) circle;
5452   bool isLine, isCirc;
5453   if ( eos._sWOL.IsNull() ) /////////////////////////////////////////// 3D case
5454   {
5455     // check if the EDGE is a line
5456     Handle(Geom_Curve) curve = BRep_Tool::Curve( E, f, l);
5457     if ( curve->IsKind( STANDARD_TYPE( Geom_TrimmedCurve )))
5458       curve = Handle(Geom_TrimmedCurve)::DownCast( curve )->BasisCurve();
5459
5460     line   = Handle(Geom_Line)::DownCast( curve );
5461     circle = Handle(Geom_Circle)::DownCast( curve );
5462     isLine = (!line.IsNull());
5463     isCirc = (!circle.IsNull());
5464
5465     if ( !isLine && !isCirc ) // Check if the EDGE is close to a line
5466     {
5467       isLine = SMESH_Algo::IsStraight( E );
5468
5469       if ( isLine )
5470         line = new Geom_Line( gp::OX() ); // only type does matter
5471     }
5472     if ( !isLine && !isCirc && eos._edges.size() > 2) // Check if the EDGE is close to a circle
5473     {
5474       // TODO
5475     }
5476   }
5477   else //////////////////////////////////////////////////////////////////////// 2D case
5478   {
5479     if ( !eos._isRegularSWOL ) // 23190
5480       return NULL;
5481
5482     const TopoDS_Face& F = TopoDS::Face( eos._sWOL );
5483
5484     // check if the EDGE is a line
5485     Handle(Geom2d_Curve) curve = BRep_Tool::CurveOnSurface( E, F, f, l );
5486     if ( curve->IsKind( STANDARD_TYPE( Geom2d_TrimmedCurve )))
5487       curve = Handle(Geom2d_TrimmedCurve)::DownCast( curve )->BasisCurve();
5488
5489     Handle(Geom2d_Line)   line2d   = Handle(Geom2d_Line)::DownCast( curve );
5490     Handle(Geom2d_Circle) circle2d = Handle(Geom2d_Circle)::DownCast( curve );
5491     isLine = (!line2d.IsNull());
5492     isCirc = (!circle2d.IsNull());
5493
5494     if ( !isLine && !isCirc ) // Check if the EDGE is close to a line
5495     {
5496       Bnd_B2d bndBox;
5497       SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
5498       while ( nIt->more() )
5499         bndBox.Add( helper.GetNodeUV( F, nIt->next() ));
5500       gp_XY size = bndBox.CornerMax() - bndBox.CornerMin();
5501
5502       const double lineTol = 1e-2 * sqrt( bndBox.SquareExtent() );
5503       for ( int i = 0; i < 2 && !isLine; ++i )
5504         isLine = ( size.Coord( i+1 ) <= lineTol );
5505     }
5506     if ( !isLine && !isCirc && eos._edges.size() > 2 ) // Check if the EDGE is close to a circle
5507     {
5508       // TODO
5509     }
5510     if ( isLine )
5511     {
5512       line = new Geom_Line( gp::OX() ); // only type does matter
5513     }
5514     else if ( isCirc )
5515     {
5516       gp_Pnt2d p = circle2d->Location();
5517       gp_Ax2 ax( gp_Pnt( p.X(), p.Y(), 0), gp::DX());
5518       circle = new Geom_Circle( ax, 1.); // only center position does matter
5519     }
5520   }
5521
5522   if ( isLine )
5523     return line;
5524   if ( isCirc )
5525     return circle;
5526
5527   return Handle(Geom_Curve)();
5528 }
5529
5530 //================================================================================
5531 /*!
5532  * \brief Smooth edges on EDGE
5533  */
5534 //================================================================================
5535
5536 bool _Smoother1D::Perform(_SolidData&                    data,
5537                           Handle(ShapeAnalysis_Surface)& surface,
5538                           const TopoDS_Face&             F,
5539                           SMESH_MesherHelper&            helper )
5540 {
5541   if ( _leParams.empty() || ( !isAnalytic() && _offPoints.empty() ))
5542     prepare( data );
5543
5544   findEdgesToSmooth();
5545   if ( isAnalytic() )
5546     return smoothAnalyticEdge( data, surface, F, helper );
5547   else
5548     return smoothComplexEdge ( data, surface, F, helper );
5549 }
5550
5551 //================================================================================
5552 /*!
5553  * \brief Find edges to smooth
5554  */
5555 //================================================================================
5556
5557 void _Smoother1D::findEdgesToSmooth()
5558 {
5559   _LayerEdge* leOnV[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
5560   for ( int iEnd = 0; iEnd < 2; ++iEnd )
5561     if ( leOnV[iEnd]->Is( _LayerEdge::NORMAL_UPDATED ))
5562       _leOnV[iEnd]._cosin = Abs( _edgeDir[iEnd].Normalized() * leOnV[iEnd]->_normal );
5563
5564   _eToSmooth[0].first = _eToSmooth[0].second = 0;
5565
5566   for ( size_t i = 0; i < _eos.size(); ++i )
5567   {
5568     if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH ))
5569     {
5570       if ( needSmoothing( _leOnV[0]._cosin, _eos[i]->_len, _curveLen * _leParams[i] ) ||
5571            isToSmooth( i ))
5572         _eos[i]->Set( _LayerEdge::TO_SMOOTH );
5573       else
5574         break;
5575     }
5576     _eToSmooth[0].second = i+1;
5577   }
5578
5579   _eToSmooth[1].first = _eToSmooth[1].second = _eos.size();
5580
5581   for ( int i = _eos.size() - 1; i >= _eToSmooth[0].second; --i )
5582   {
5583     if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH ))
5584     {
5585       if ( needSmoothing( _leOnV[1]._cosin, _eos[i]->_len, _curveLen * ( 1.-_leParams[i] )) ||
5586            isToSmooth( i ))
5587         _eos[i]->Set( _LayerEdge::TO_SMOOTH );
5588       else
5589         break;
5590     }
5591     _eToSmooth[1].first = i;
5592   }
5593 }
5594
5595 //================================================================================
5596 /*!
5597  * \brief Check if iE-th _LayerEdge needs smoothing
5598  */
5599 //================================================================================
5600
5601 bool _Smoother1D::isToSmooth( int iE )
5602 {
5603   SMESH_NodeXYZ pi( _eos[iE]->_nodes[0] );
5604   SMESH_NodeXYZ p0( _eos[iE]->_2neibors->srcNode(0) );
5605   SMESH_NodeXYZ p1( _eos[iE]->_2neibors->srcNode(1) );
5606   gp_XYZ       seg0 = pi - p0;
5607   gp_XYZ       seg1 = p1 - pi;
5608   gp_XYZ    tangent =  seg0 + seg1;
5609   double tangentLen = tangent.Modulus();
5610   double  segMinLen = Min( seg0.Modulus(), seg1.Modulus() );
5611   if ( tangentLen < std::numeric_limits<double>::min() )
5612     return false;
5613   tangent /= tangentLen;
5614
5615   for ( size_t i = 0; i < _eos[iE]->_neibors.size(); ++i )
5616   {
5617     _LayerEdge* ne = _eos[iE]->_neibors[i];
5618     if ( !ne->Is( _LayerEdge::TO_SMOOTH ) ||
5619          ne->_nodes.size() < 2 ||
5620          ne->_nodes[0]->GetPosition()->GetDim() != 2 )
5621       continue;
5622     gp_XYZ edgeVec = SMESH_NodeXYZ( ne->_nodes.back() ) - SMESH_NodeXYZ( ne->_nodes[0] );
5623     double    proj = edgeVec * tangent;
5624     if ( needSmoothing( 1., proj, segMinLen ))
5625       return true;
5626   }
5627   return false;
5628 }
5629
5630 //================================================================================
5631 /*!
5632  * \brief smooth _LayerEdge's on a staight EDGE or circular EDGE
5633  */
5634 //================================================================================
5635
5636 bool _Smoother1D::smoothAnalyticEdge( _SolidData&                    data,
5637                                       Handle(ShapeAnalysis_Surface)& surface,
5638                                       const TopoDS_Face&             F,
5639                                       SMESH_MesherHelper&            helper)
5640 {
5641   if ( !isAnalytic() ) return false;
5642
5643   size_t iFrom = 0, iTo = _eos._edges.size();
5644
5645   if ( _anaCurve->IsKind( STANDARD_TYPE( Geom_Line )))
5646   {
5647     if ( F.IsNull() ) // 3D
5648     {
5649       SMESH_TNodeXYZ pSrc0( _eos._edges[iFrom]->_2neibors->srcNode(0) );
5650       SMESH_TNodeXYZ pSrc1( _eos._edges[iTo-1]->_2neibors->srcNode(1) );
5651       //const   gp_XYZ lineDir = pSrc1 - pSrc0;
5652       //_LayerEdge* vLE0 = getLEdgeOnV( 0 );
5653       //_LayerEdge* vLE1 = getLEdgeOnV( 1 );
5654       // bool shiftOnly = ( vLE0->Is( _LayerEdge::NORMAL_UPDATED ) ||
5655       //                    vLE0->Is( _LayerEdge::BLOCKED ) ||
5656       //                    vLE1->Is( _LayerEdge::NORMAL_UPDATED ) ||
5657       //                    vLE1->Is( _LayerEdge::BLOCKED ));
5658       for ( int iEnd = 0; iEnd < 2; ++iEnd )
5659       {
5660         iFrom = _eToSmooth[ iEnd ].first, iTo = _eToSmooth[ iEnd ].second;
5661         if ( iFrom >= iTo ) continue;
5662         SMESH_TNodeXYZ p0( _eos[iFrom]->_2neibors->tgtNode(0) );
5663         SMESH_TNodeXYZ p1( _eos[iTo-1]->_2neibors->tgtNode(1) );
5664         double param0 = ( iFrom == 0 ) ? 0. : _leParams[ iFrom-1 ];
5665         double param1 = _leParams[ iTo ];
5666         for ( size_t i = iFrom; i < iTo; ++i )
5667         {
5668           _LayerEdge*       edge = _eos[i];
5669           SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edge->_nodes.back() );
5670           double           param = ( _leParams[i] - param0 ) / ( param1 - param0 );
5671           gp_XYZ          newPos = p0 * ( 1. - param ) + p1 * param;
5672
5673           // if ( shiftOnly || edge->Is( _LayerEdge::NORMAL_UPDATED ))
5674           // {
5675           //   gp_XYZ curPos = SMESH_TNodeXYZ ( tgtNode );
5676           //   double  shift = ( lineDir * ( newPos - pSrc0 ) -
5677           //                     lineDir * ( curPos - pSrc0 ));
5678           //   newPos = curPos + lineDir * shift / lineDir.SquareModulus();
5679           // }
5680           if ( edge->Is( _LayerEdge::BLOCKED ))
5681           {
5682             SMESH_TNodeXYZ pSrc( edge->_nodes[0] );
5683             double curThick = pSrc.SquareDistance( tgtNode );
5684             double newThink = ( pSrc - newPos ).SquareModulus();
5685             if ( newThink > curThick )
5686               continue;
5687           }
5688           edge->_pos.back() = newPos;
5689           tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
5690           dumpMove( tgtNode );
5691         }
5692       }
5693     }
5694     else // 2D
5695     {
5696       _LayerEdge* eV0 = getLEdgeOnV( 0 );
5697       _LayerEdge* eV1 = getLEdgeOnV( 1 );
5698       gp_XY      uvV0 = eV0->LastUV( F, *data.GetShapeEdges( eV0 ));
5699       gp_XY      uvV1 = eV1->LastUV( F, *data.GetShapeEdges( eV1 ));
5700       if ( eV0->_nodes.back() == eV1->_nodes.back() ) // closed edge
5701       {
5702         int iPeriodic = helper.GetPeriodicIndex();
5703         if ( iPeriodic == 1 || iPeriodic == 2 )
5704         {
5705           uvV1.SetCoord( iPeriodic, helper.GetOtherParam( uvV1.Coord( iPeriodic )));
5706           if ( uvV0.Coord( iPeriodic ) > uvV1.Coord( iPeriodic ))
5707             std::swap( uvV0, uvV1 );
5708         }
5709       }
5710       for ( int iEnd = 0; iEnd < 2; ++iEnd )
5711       {
5712         iFrom = _eToSmooth[ iEnd ].first, iTo = _eToSmooth[ iEnd ].second;
5713         if ( iFrom >= iTo ) continue;
5714         _LayerEdge* e0 = _eos[iFrom]->_2neibors->_edges[0];
5715         _LayerEdge* e1 = _eos[iTo-1]->_2neibors->_edges[1];
5716         gp_XY      uv0 = ( e0 == eV0 ) ? uvV0 : e0->LastUV( F, _eos );
5717         gp_XY      uv1 = ( e1 == eV1 ) ? uvV1 : e1->LastUV( F, _eos );
5718         double  param0 = ( iFrom == 0 ) ? 0. : _leParams[ iFrom-1 ];
5719         double  param1 = _leParams[ iTo ];
5720         gp_XY  rangeUV = uv1 - uv0;
5721         for ( size_t i = iFrom; i < iTo; ++i )
5722         {
5723           if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
5724           double param = ( _leParams[i] - param0 ) / ( param1 - param0 );
5725           gp_XY newUV = uv0 + param * rangeUV;
5726
5727           gp_Pnt newPos = surface->Value( newUV.X(), newUV.Y() );
5728           SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos[i]->_nodes.back() );
5729           tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
5730           dumpMove( tgtNode );
5731
5732           SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( tgtNode->GetPosition() );
5733           pos->SetUParameter( newUV.X() );
5734           pos->SetVParameter( newUV.Y() );
5735
5736           gp_XYZ newUV0( newUV.X(), newUV.Y(), 0 );
5737
5738           if ( !_eos[i]->Is( _LayerEdge::SMOOTHED ))
5739           {
5740             _eos[i]->Set( _LayerEdge::SMOOTHED ); // to check in refine() (IPAL54237)
5741             if ( _eos[i]->_pos.size() > 2 )
5742             {
5743               // modify previous positions to make _LayerEdge less sharply bent
5744               vector<gp_XYZ>& uvVec = _eos[i]->_pos;
5745               const gp_XYZ  uvShift = newUV0 - uvVec.back();
5746               const double     len2 = ( uvVec.back() - uvVec[ 0 ] ).SquareModulus();
5747               int iPrev = uvVec.size() - 2;
5748               while ( iPrev > 0 )
5749               {
5750                 double r = ( uvVec[ iPrev ] - uvVec[0] ).SquareModulus() / len2;
5751                 uvVec[ iPrev ] += uvShift * r;
5752                 --iPrev;
5753               }
5754             }
5755           }
5756           _eos[i]->_pos.back() = newUV0;
5757         }
5758       }
5759     }
5760     return true;
5761   }
5762
5763   if ( _anaCurve->IsKind( STANDARD_TYPE( Geom_Circle )))
5764   {
5765     Handle(Geom_Circle) circle = Handle(Geom_Circle)::DownCast( _anaCurve );
5766     gp_Pnt center3D = circle->Location();
5767
5768     if ( F.IsNull() ) // 3D
5769     {
5770       if ( getLEdgeOnV( 0 )->_nodes.back() == getLEdgeOnV( 1 )->_nodes.back() )
5771         return true; // closed EDGE - nothing to do
5772
5773       // circle is a real curve of EDGE
5774       gp_Circ circ = circle->Circ();
5775
5776       // new center is shifted along its axis
5777       const gp_Dir& axis = circ.Axis().Direction();
5778       _LayerEdge*     e0 = getLEdgeOnV(0);
5779       _LayerEdge*     e1 = getLEdgeOnV(1);
5780       SMESH_TNodeXYZ  p0 = e0->_nodes.back();
5781       SMESH_TNodeXYZ  p1 = e1->_nodes.back();
5782       double      shift1 = axis.XYZ() * ( p0 - center3D.XYZ() );
5783       double      shift2 = axis.XYZ() * ( p1 - center3D.XYZ() );
5784       gp_Pnt   newCenter = center3D.XYZ() + axis.XYZ() * 0.5 * ( shift1 + shift2 );
5785
5786       double newRadius = 0.5 * ( newCenter.Distance( p0 ) + newCenter.Distance( p1 ));
5787
5788       gp_Ax2  newAxis( newCenter, axis, gp_Vec( newCenter, p0 ));
5789       gp_Circ newCirc( newAxis, newRadius );
5790       gp_Vec  vecC1  ( newCenter, p1 );
5791
5792       double uLast = newAxis.XDirection().AngleWithRef( vecC1, newAxis.Direction() ); // -PI - +PI
5793       if ( uLast < 0 )
5794         uLast += 2 * M_PI;
5795       
5796       for ( size_t i = 0; i < _eos.size(); ++i )
5797       {
5798         if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
5799         //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
5800         double u = uLast * _leParams[i];
5801         gp_Pnt p = ElCLib::Value( u, newCirc );
5802         _eos._edges[i]->_pos.back() = p.XYZ();
5803
5804         SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos._edges[i]->_nodes.back() );
5805         tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
5806         dumpMove( tgtNode );
5807       }
5808       return true;
5809     }
5810     else // 2D
5811     {
5812       const gp_XY center( center3D.X(), center3D.Y() );
5813
5814       _LayerEdge* e0 = getLEdgeOnV(0);
5815       _LayerEdge* eM = _eos._edges[ 0 ];
5816       _LayerEdge* e1 = getLEdgeOnV(1);
5817       gp_XY      uv0 = e0->LastUV( F, *data.GetShapeEdges( e0 ) );
5818       gp_XY      uvM = eM->LastUV( F, *data.GetShapeEdges( eM ) );
5819       gp_XY      uv1 = e1->LastUV( F, *data.GetShapeEdges( e1 ) );
5820       gp_Vec2d vec0( center, uv0 );
5821       gp_Vec2d vecM( center, uvM );
5822       gp_Vec2d vec1( center, uv1 );
5823       double uLast = vec0.Angle( vec1 ); // -PI - +PI
5824       double uMidl = vec0.Angle( vecM );
5825       if ( uLast * uMidl <= 0. )
5826         uLast += ( uMidl > 0 ? +2. : -2. ) * M_PI;
5827       const double radius = 0.5 * ( vec0.Magnitude() + vec1.Magnitude() );
5828
5829       gp_Ax2d   axis( center, vec0 );
5830       gp_Circ2d circ( axis, radius );
5831       for ( size_t i = 0; i < _eos.size(); ++i )
5832       {
5833         if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
5834         //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
5835         double    newU = uLast * _leParams[i];
5836         gp_Pnt2d newUV = ElCLib::Value( newU, circ );
5837         _eos._edges[i]->_pos.back().SetCoord( newUV.X(), newUV.Y(), 0 );
5838
5839         gp_Pnt newPos = surface->Value( newUV.X(), newUV.Y() );
5840         SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos._edges[i]->_nodes.back() );
5841         tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
5842         dumpMove( tgtNode );
5843
5844         SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( tgtNode->GetPosition() );
5845         pos->SetUParameter( newUV.X() );
5846         pos->SetVParameter( newUV.Y() );
5847
5848         _eos[i]->Set( _LayerEdge::SMOOTHED ); // to check in refine() (IPAL54237)
5849       }
5850     }
5851     return true;
5852   }
5853
5854   return false;
5855 }
5856
5857 //================================================================================
5858 /*!
5859  * \brief smooth _LayerEdge's on a an EDGE
5860  */
5861 //================================================================================
5862
5863 bool _Smoother1D::smoothComplexEdge( _SolidData&                    data,
5864                                      Handle(ShapeAnalysis_Surface)& surface,
5865                                      const TopoDS_Face&             F,
5866                                      SMESH_MesherHelper&            helper)
5867 {
5868   if ( _offPoints.empty() )
5869     return false;
5870
5871   // ----------------------------------------------
5872   // move _offPoints along normals of _LayerEdge's
5873   // ----------------------------------------------
5874
5875   _LayerEdge* e[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
5876   if ( e[0]->Is( _LayerEdge::NORMAL_UPDATED ))
5877     _leOnV[0]._normal = getNormalNormal( e[0]->_normal, _edgeDir[0] );
5878   if ( e[1]->Is( _LayerEdge::NORMAL_UPDATED )) 
5879     _leOnV[1]._normal = getNormalNormal( e[1]->_normal, _edgeDir[1] );
5880   _leOnV[0]._len = e[0]->_len;
5881   _leOnV[1]._len = e[1]->_len;
5882   for ( size_t i = 0; i < _offPoints.size(); i++ )
5883   {
5884     _LayerEdge*  e0 = _offPoints[i]._2edges._edges[0];
5885     _LayerEdge*  e1 = _offPoints[i]._2edges._edges[1];
5886     const double w0 = _offPoints[i]._2edges._wgt[0];
5887     const double w1 = _offPoints[i]._2edges._wgt[1];
5888     gp_XYZ  avgNorm = ( e0->_normal    * w0 + e1->_normal    * w1 ).Normalized();
5889     double  avgLen  = ( e0->_len       * w0 + e1->_len       * w1 );
5890     double  avgFact = ( e0->_lenFactor * w0 + e1->_lenFactor * w1 );
5891     if ( e0->Is( _LayerEdge::NORMAL_UPDATED ) ||
5892          e1->Is( _LayerEdge::NORMAL_UPDATED ))
5893       avgNorm = getNormalNormal( avgNorm, _offPoints[i]._edgeDir );
5894
5895     _offPoints[i]._xyz += avgNorm * ( avgLen - _offPoints[i]._len ) * avgFact;
5896     _offPoints[i]._len  = avgLen;
5897   }
5898
5899   double fTol = 0;
5900   if ( !surface.IsNull() ) // project _offPoints to the FACE
5901   {
5902     fTol = 100 * BRep_Tool::Tolerance( F );
5903     //const double segLen = _offPoints[0].Distance( _offPoints[1] );
5904
5905     gp_Pnt2d uv = surface->ValueOfUV( _offPoints[0]._xyz, fTol );
5906     //if ( surface->Gap() < 0.5 * segLen )
5907       _offPoints[0]._xyz = surface->Value( uv ).XYZ();
5908
5909     for ( size_t i = 1; i < _offPoints.size(); ++i )
5910     {
5911       uv = surface->NextValueOfUV( uv, _offPoints[i]._xyz, fTol );
5912       //if ( surface->Gap() < 0.5 * segLen )
5913         _offPoints[i]._xyz = surface->Value( uv ).XYZ();
5914     }
5915   }
5916
5917   // -----------------------------------------------------------------
5918   // project tgt nodes of extreme _LayerEdge's to the offset segments
5919   // -----------------------------------------------------------------
5920
5921   const int updatedOrBlocked = _LayerEdge::NORMAL_UPDATED | _LayerEdge::BLOCKED;
5922   if ( e[0]->Is( updatedOrBlocked )) _iSeg[0] = 0;
5923   if ( e[1]->Is( updatedOrBlocked )) _iSeg[1] = _offPoints.size()-2;
5924
5925   gp_Pnt pExtreme[2], pProj[2];
5926   for ( int is2nd = 0; is2nd < 2; ++is2nd )
5927   {
5928     pExtreme[ is2nd ] = SMESH_TNodeXYZ( e[is2nd]->_nodes.back() );
5929     int  i = _iSeg[ is2nd ];
5930     int di = is2nd ? -1 : +1;
5931     bool projected = false;
5932     double uOnSeg, distMin = Precision::Infinite(), dist, distPrev = 0;
5933     int nbWorse = 0;
5934     do {
5935       gp_Vec v0p( _offPoints[i]._xyz, pExtreme[ is2nd ]    );
5936       gp_Vec v01( _offPoints[i]._xyz, _offPoints[i+1]._xyz );
5937       uOnSeg     = ( v0p * v01 ) / v01.SquareMagnitude();  // param [0,1] along v01
5938       projected  = ( Abs( uOnSeg - 0.5 ) <= 0.5 );
5939       dist       =  pExtreme[ is2nd ].SquareDistance( _offPoints[ i + ( uOnSeg > 0.5 )]._xyz );
5940       if ( dist < distMin || projected )
5941       {
5942         _iSeg[ is2nd ] = i;
5943         pProj[ is2nd ] = _offPoints[i]._xyz + ( v01 * uOnSeg ).XYZ();
5944         distMin = dist;
5945       }
5946       else if ( dist > distPrev )
5947       {
5948         if ( ++nbWorse > 3 ) // avoid projection to the middle of a closed EDGE
5949           break;
5950       }
5951       distPrev = dist;
5952       i += di;
5953     }
5954     while ( !projected &&
5955             i >= 0 && i+1 < (int)_offPoints.size() );
5956
5957     if ( !projected )
5958     {
5959       if (( is2nd && _iSeg[1] != _offPoints.size()-2 ) || ( !is2nd && _iSeg[0] != 0 ))
5960       {
5961         _iSeg[0] = 0;
5962         _iSeg[1] = _offPoints.size()-2;
5963         debugMsg( "smoothComplexEdge() failed to project nodes of extreme _LayerEdge's" );
5964         return false;
5965       }
5966     }
5967   }
5968   if ( _iSeg[0] > _iSeg[1] )
5969   {
5970     debugMsg( "smoothComplexEdge() incorrectly projected nodes of extreme _LayerEdge's" );
5971     return false;
5972   }
5973
5974   // adjust length of extreme LE (test viscous_layers_01/B7)
5975   gp_Vec vDiv0( pExtreme[0], pProj[0] );
5976   gp_Vec vDiv1( pExtreme[1], pProj[1] );
5977   double d0 = vDiv0.Magnitude();
5978   double d1 = vDiv1.Magnitude();
5979   if ( e[0]->Is( _LayerEdge::BLOCKED )) {
5980     if ( e[0]->_normal * vDiv0.XYZ() < 0 ) e[0]->_len += d0;
5981     else                                   e[0]->_len -= d0;
5982   }
5983   if ( e[1]->Is( _LayerEdge::BLOCKED )) {
5984     if ( e[1]->_normal * vDiv1.XYZ() < 0 ) e[1]->_len += d1;
5985     else                                   e[1]->_len -= d1;
5986   }
5987
5988   // ---------------------------------------------------------------------------------
5989   // compute normalized length of the offset segments located between the projections
5990   // ---------------------------------------------------------------------------------
5991
5992   size_t iSeg = 0, nbSeg = _iSeg[1] - _iSeg[0] + 1;
5993   vector< double > len( nbSeg + 1 );
5994   len[ iSeg++ ] = 0;
5995   len[ iSeg++ ] = pProj[ 0 ].Distance( _offPoints[ _iSeg[0]+1 ]._xyz )/* * e[0]->_lenFactor*/;
5996   for ( size_t i = _iSeg[0]+1; i <= _iSeg[1]; ++i, ++iSeg )
5997   {
5998     len[ iSeg ] = len[ iSeg-1 ] + _offPoints[i].Distance( _offPoints[i+1] );
5999   }
6000   len[ nbSeg ] -= pProj[ 1 ].Distance( _offPoints[ _iSeg[1]+1 ]._xyz )/* * e[1]->_lenFactor*/;
6001
6002   // d0 *= e[0]->_lenFactor;
6003   // d1 *= e[1]->_lenFactor;
6004   double fullLen = len.back() - d0 - d1;
6005   for ( iSeg = 0; iSeg < len.size(); ++iSeg )
6006     len[iSeg] = ( len[iSeg] - d0 ) / fullLen;
6007
6008   // temporary replace extreme _offPoints by pExtreme
6009   gp_XYZ op[2] = { _offPoints[ _iSeg[0]   ]._xyz,
6010                    _offPoints[ _iSeg[1]+1 ]._xyz };
6011   _offPoints[ _iSeg[0]   ]._xyz = pExtreme[0].XYZ();
6012   _offPoints[ _iSeg[1]+ 1]._xyz = pExtreme[1].XYZ();
6013
6014   // -------------------------------------------------------------
6015   // distribute tgt nodes of _LayerEdge's between the projections
6016   // -------------------------------------------------------------
6017
6018   iSeg = 0;
6019   for ( size_t i = 0; i < _eos.size(); ++i )
6020   {
6021     if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
6022     //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
6023     while ( iSeg+2 < len.size() && _leParams[i] > len[ iSeg+1 ] )
6024       iSeg++;
6025     double r = ( _leParams[i] - len[ iSeg ]) / ( len[ iSeg+1 ] - len[ iSeg ]);
6026     gp_XYZ p = ( _offPoints[ iSeg + _iSeg[0]     ]._xyz * ( 1 - r ) +
6027                  _offPoints[ iSeg + _iSeg[0] + 1 ]._xyz * r );
6028
6029     if ( surface.IsNull() )
6030     {
6031       _eos[i]->_pos.back() = p;
6032     }
6033     else // project a new node position to a FACE
6034     {
6035       gp_Pnt2d uv ( _eos[i]->_pos.back().X(), _eos[i]->_pos.back().Y() );
6036       gp_Pnt2d uv2( surface->NextValueOfUV( uv, p, fTol ));
6037
6038       p = surface->Value( uv2 ).XYZ();
6039       _eos[i]->_pos.back().SetCoord( uv2.X(), uv2.Y(), 0 );
6040     }
6041     SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos[i]->_nodes.back() );
6042     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
6043     dumpMove( tgtNode );
6044   }
6045
6046   _offPoints[ _iSeg[0]   ]._xyz = op[0];
6047   _offPoints[ _iSeg[1]+1 ]._xyz = op[1];
6048
6049   return true;
6050 }
6051
6052 //================================================================================
6053 /*!
6054  * \brief Prepare for smoothing
6055  */
6056 //================================================================================
6057
6058 void _Smoother1D::prepare(_SolidData& data)
6059 {
6060   const TopoDS_Edge& E = TopoDS::Edge( _eos._shape );
6061   _curveLen = SMESH_Algo::EdgeLength( E );
6062
6063   // sort _LayerEdge's by position on the EDGE
6064   data.SortOnEdge( E, _eos._edges );
6065
6066   // compute normalized param of _eos._edges on EDGE
6067   _leParams.resize( _eos._edges.size() + 1 );
6068   {
6069     double curLen;
6070     gp_Pnt pPrev = SMESH_TNodeXYZ( getLEdgeOnV( 0 )->_nodes[0] );
6071     _leParams[0] = 0;
6072     for ( size_t i = 0; i < _eos._edges.size(); ++i )
6073     {
6074       gp_Pnt p       = SMESH_TNodeXYZ( _eos._edges[i]->_nodes[0] );
6075       curLen         = p.Distance( pPrev );
6076       _leParams[i+1] = _leParams[i] + curLen;
6077       pPrev          = p;
6078     }
6079     double fullLen = _leParams.back() + pPrev.Distance( SMESH_TNodeXYZ( getLEdgeOnV(1)->_nodes[0]));
6080     for ( size_t i = 0; i < _leParams.size()-1; ++i )
6081       _leParams[i] = _leParams[i+1] / fullLen;
6082     _leParams.back() = 1.;
6083   }
6084
6085   _LayerEdge* leOnV[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
6086
6087   // get cosin to use in findEdgesToSmooth()
6088   _edgeDir[0] = getEdgeDir( E, leOnV[0]->_nodes[0], data.GetHelper() );
6089   _edgeDir[1] = getEdgeDir( E, leOnV[1]->_nodes[0], data.GetHelper() );
6090   _leOnV[0]._cosin = Abs( leOnV[0]->_cosin );
6091   _leOnV[1]._cosin = Abs( leOnV[1]->_cosin );
6092   if ( _eos._sWOL.IsNull() ) // 3D
6093     for ( int iEnd = 0; iEnd < 2; ++iEnd )
6094       _leOnV[iEnd]._cosin = Abs( _edgeDir[iEnd].Normalized() * leOnV[iEnd]->_normal );
6095
6096   if ( isAnalytic() )
6097     return;
6098
6099   // divide E to have offset segments with low deflection
6100   BRepAdaptor_Curve c3dAdaptor( E );
6101   const double curDeflect = 0.1; //0.01; // Curvature deflection == |p1p2]*sin(p1p2,p1pM)
6102   const double angDeflect = 0.1; //0.09; // Angular deflection == sin(p1pM,pMp2)
6103   GCPnts_TangentialDeflection discret(c3dAdaptor, angDeflect, curDeflect);
6104   if ( discret.NbPoints() <= 2 )
6105   {
6106     _anaCurve = new Geom_Line( gp::OX() ); // only type does matter
6107     return;
6108   }
6109
6110   const double u0 = c3dAdaptor.FirstParameter();
6111   gp_Pnt p; gp_Vec tangent;
6112   if ( discret.NbPoints() >= (int) _eos.size() + 2 )
6113   {
6114     _offPoints.resize( discret.NbPoints() );
6115     for ( size_t i = 0; i < _offPoints.size(); i++ )
6116     {
6117       double u = discret.Parameter( i+1 );
6118       c3dAdaptor.D1( u, p, tangent );
6119       _offPoints[i]._xyz     = p.XYZ();
6120       _offPoints[i]._edgeDir = tangent.XYZ();
6121       _offPoints[i]._param = GCPnts_AbscissaPoint::Length( c3dAdaptor, u0, u ) / _curveLen;
6122     }
6123   }
6124   else
6125   {
6126     std::vector< double > params( _eos.size() + 2 );
6127
6128     params[0]     = data.GetHelper().GetNodeU( E, leOnV[0]->_nodes[0] );
6129     params.back() = data.GetHelper().GetNodeU( E, leOnV[1]->_nodes[0] );
6130     for ( size_t i = 0; i < _eos.size(); i++ )
6131       params[i+1] = data.GetHelper().GetNodeU( E, _eos[i]->_nodes[0] );
6132
6133     if ( params[1] > params[ _eos.size() ] )
6134       std::reverse( params.begin() + 1, params.end() - 1 );
6135
6136     _offPoints.resize( _eos.size() + 2 );
6137     for ( size_t i = 0; i < _offPoints.size(); i++ )
6138     {
6139       const double u = params[i];
6140       c3dAdaptor.D1( u, p, tangent );
6141       _offPoints[i]._xyz     = p.XYZ();
6142       _offPoints[i]._edgeDir = tangent.XYZ();
6143       _offPoints[i]._param = GCPnts_AbscissaPoint::Length( c3dAdaptor, u0, u ) / _curveLen;
6144     }
6145   }
6146
6147   // set _2edges
6148   _offPoints    [0]._2edges.set( &_leOnV[0], &_leOnV[0], 0.5, 0.5 );
6149   _offPoints.back()._2edges.set( &_leOnV[1], &_leOnV[1], 0.5, 0.5 );
6150   _2NearEdges tmp2edges;
6151   tmp2edges._edges[1] = _eos._edges[0];
6152   _leOnV[0]._2neibors = & tmp2edges;
6153   _leOnV[0]._nodes    = leOnV[0]->_nodes;
6154   _leOnV[1]._nodes    = leOnV[1]->_nodes;
6155   _LayerEdge* eNext, *ePrev = & _leOnV[0];
6156   for ( size_t iLE = 0, i = 1; i < _offPoints.size()-1; i++ )
6157   {
6158     // find _LayerEdge's located before and after an offset point
6159     // (_eos._edges[ iLE ] is next after ePrev)
6160     while ( iLE < _eos._edges.size() && _offPoints[i]._param > _leParams[ iLE ] )
6161       ePrev = _eos._edges[ iLE++ ];
6162     eNext = ePrev->_2neibors->_edges[1];
6163
6164     gp_Pnt p0 = SMESH_TNodeXYZ( ePrev->_nodes[0] );
6165     gp_Pnt p1 = SMESH_TNodeXYZ( eNext->_nodes[0] );
6166     double  r = p0.Distance( _offPoints[i]._xyz ) / p0.Distance( p1 );
6167     _offPoints[i]._2edges.set( ePrev, eNext, 1-r, r );
6168   }
6169
6170   // replace _LayerEdge's on VERTEX by _leOnV in _offPoints._2edges
6171   for ( size_t i = 0; i < _offPoints.size(); i++ )
6172     if ( _offPoints[i]._2edges._edges[0] == leOnV[0] )
6173       _offPoints[i]._2edges._edges[0] = & _leOnV[0];
6174     else break;
6175   for ( size_t i = _offPoints.size()-1; i > 0; i-- )
6176     if ( _offPoints[i]._2edges._edges[1] == leOnV[1] )
6177       _offPoints[i]._2edges._edges[1] = & _leOnV[1];
6178     else break;
6179
6180   // set _normal of _leOnV[0] and _leOnV[1] to be normal to the EDGE
6181
6182   int iLBO = _offPoints.size() - 2; // last but one
6183
6184   if ( leOnV[ 0 ]->Is( _LayerEdge::MULTI_NORMAL ))
6185     _leOnV[ 0 ]._normal = getNormalNormal( _eos._edges[1]->_normal, _edgeDir[0] );
6186   else
6187     _leOnV[ 0 ]._normal = getNormalNormal( leOnV[0]->_normal,       _edgeDir[0] );
6188   if ( leOnV[ 1 ]->Is( _LayerEdge::MULTI_NORMAL ))
6189     _leOnV[ 1 ]._normal = getNormalNormal( _eos._edges.back()->_normal, _edgeDir[1] );
6190   else
6191     _leOnV[ 1 ]._normal = getNormalNormal( leOnV[1]->_normal,           _edgeDir[1] );
6192   _leOnV[ 0 ]._len = 0;
6193   _leOnV[ 1 ]._len = 0;
6194   _leOnV[ 0 ]._lenFactor = _offPoints[1   ]._2edges._edges[1]->_lenFactor;
6195   _leOnV[ 1 ]._lenFactor = _offPoints[iLBO]._2edges._edges[0]->_lenFactor;
6196
6197   _iSeg[0] = 0;
6198   _iSeg[1] = _offPoints.size()-2;
6199
6200   // initialize OffPnt::_len
6201   for ( size_t i = 0; i < _offPoints.size(); ++i )
6202     _offPoints[i]._len = 0;
6203
6204   if ( _eos._edges[0]->NbSteps() > 1 ) // already inflated several times, init _xyz
6205   {
6206     _leOnV[0]._len = leOnV[0]->_len;
6207     _leOnV[1]._len = leOnV[1]->_len;
6208     for ( size_t i = 0; i < _offPoints.size(); i++ )
6209     {
6210       _LayerEdge*  e0 = _offPoints[i]._2edges._edges[0];
6211       _LayerEdge*  e1 = _offPoints[i]._2edges._edges[1];
6212       const double w0 = _offPoints[i]._2edges._wgt[0];
6213       const double w1 = _offPoints[i]._2edges._wgt[1];
6214       double  avgLen  = ( e0->_len * w0 + e1->_len * w1 );
6215       gp_XYZ  avgXYZ  = ( SMESH_TNodeXYZ( e0->_nodes.back() ) * w0 +
6216                           SMESH_TNodeXYZ( e1->_nodes.back() ) * w1 );
6217       _offPoints[i]._xyz = avgXYZ;
6218       _offPoints[i]._len = avgLen;
6219     }
6220   }
6221 }
6222
6223 //================================================================================
6224 /*!
6225  * \brief return _normal of _leOnV[is2nd] normal to the EDGE
6226  */
6227 //================================================================================
6228
6229 gp_XYZ _Smoother1D::getNormalNormal( const gp_XYZ & normal,
6230                                      const gp_XYZ&  edgeDir)
6231 {
6232   gp_XYZ cross = normal ^ edgeDir;
6233   gp_XYZ  norm = edgeDir ^ cross;
6234   double  size = norm.Modulus();
6235
6236   // if ( size == 0 ) // MULTI_NORMAL _LayerEdge
6237   //   return gp_XYZ( 1e-100, 1e-100, 1e-100 );
6238
6239   return norm / size;
6240 }
6241
6242 //================================================================================
6243 /*!
6244  * \brief Sort _LayerEdge's by a parameter on a given EDGE
6245  */
6246 //================================================================================
6247
6248 void _SolidData::SortOnEdge( const TopoDS_Edge&     E,
6249                              vector< _LayerEdge* >& edges)
6250 {
6251   map< double, _LayerEdge* > u2edge;
6252   for ( size_t i = 0; i < edges.size(); ++i )
6253     u2edge.insert( u2edge.end(),
6254                    make_pair( _helper->GetNodeU( E, edges[i]->_nodes[0] ), edges[i] ));
6255
6256   ASSERT( u2edge.size() == edges.size() );
6257   map< double, _LayerEdge* >::iterator u2e = u2edge.begin();
6258   for ( size_t i = 0; i < edges.size(); ++i, ++u2e )
6259     edges[i] = u2e->second;
6260
6261   Sort2NeiborsOnEdge( edges );
6262 }
6263
6264 //================================================================================
6265 /*!
6266  * \brief Set _2neibors according to the order of _LayerEdge on EDGE
6267  */
6268 //================================================================================
6269
6270 void _SolidData::Sort2NeiborsOnEdge( vector< _LayerEdge* >& edges )
6271 {
6272   if ( edges.size() < 2 || !edges[0]->_2neibors ) return;
6273
6274   for ( size_t i = 0; i < edges.size()-1; ++i )
6275     if ( edges[i]->_2neibors->tgtNode(1) != edges[i+1]->_nodes.back() )
6276       edges[i]->_2neibors->reverse();
6277
6278   const size_t iLast = edges.size() - 1;
6279   if ( edges.size() > 1 &&
6280        edges[iLast]->_2neibors->tgtNode(0) != edges[iLast-1]->_nodes.back() )
6281     edges[iLast]->_2neibors->reverse();
6282 }
6283
6284 //================================================================================
6285 /*!
6286  * \brief Return _EdgesOnShape* corresponding to the shape
6287  */
6288 //================================================================================
6289
6290 _EdgesOnShape* _SolidData::GetShapeEdges(const TGeomID shapeID )
6291 {
6292   if ( shapeID < (int)_edgesOnShape.size() &&
6293        _edgesOnShape[ shapeID ]._shapeID == shapeID )
6294     return _edgesOnShape[ shapeID ]._subMesh ? & _edgesOnShape[ shapeID ] : 0;
6295
6296   for ( size_t i = 0; i < _edgesOnShape.size(); ++i )
6297     if ( _edgesOnShape[i]._shapeID == shapeID )
6298       return _edgesOnShape[i]._subMesh ? & _edgesOnShape[i] : 0;
6299
6300   return 0;
6301 }
6302
6303 //================================================================================
6304 /*!
6305  * \brief Return _EdgesOnShape* corresponding to the shape
6306  */
6307 //================================================================================
6308
6309 _EdgesOnShape* _SolidData::GetShapeEdges(const TopoDS_Shape& shape )
6310 {
6311   SMESHDS_Mesh* meshDS = _proxyMesh->GetMesh()->GetMeshDS();
6312   return GetShapeEdges( meshDS->ShapeToIndex( shape ));
6313 }
6314
6315 //================================================================================
6316 /*!
6317  * \brief Prepare data of the _LayerEdge for smoothing on FACE
6318  */
6319 //================================================================================
6320
6321 void _SolidData::PrepareEdgesToSmoothOnFace( _EdgesOnShape* eos, bool substituteSrcNodes )
6322 {
6323   SMESH_MesherHelper helper( *_proxyMesh->GetMesh() );
6324
6325   set< TGeomID > vertices;
6326   TopoDS_Face F;
6327   if ( eos->ShapeType() == TopAbs_FACE )
6328   {
6329     // check FACE concavity and get concave VERTEXes
6330     F = TopoDS::Face( eos->_shape );
6331     if ( isConcave( F, helper, &vertices ))
6332       _concaveFaces.insert( eos->_shapeID );
6333
6334     // set eos._eosConcaVer
6335     eos->_eosConcaVer.clear();
6336     eos->_eosConcaVer.reserve( vertices.size() );
6337     for ( set< TGeomID >::iterator v = vertices.begin(); v != vertices.end(); ++v )
6338     {
6339       _EdgesOnShape* eov = GetShapeEdges( *v );
6340       if ( eov && eov->_edges.size() == 1 )
6341       {
6342         eos->_eosConcaVer.push_back( eov );
6343         for ( size_t i = 0; i < eov->_edges[0]->_neibors.size(); ++i )
6344           eov->_edges[0]->_neibors[i]->Set( _LayerEdge::DIFFICULT );
6345       }
6346     }
6347
6348     // SetSmooLen() to _LayerEdge's on FACE
6349     for ( size_t i = 0; i < eos->_edges.size(); ++i )
6350     {
6351       eos->_edges[i]->SetSmooLen( Precision::Infinite() );
6352     }
6353     SMESH_subMeshIteratorPtr smIt = eos->_subMesh->getDependsOnIterator(/*includeSelf=*/false);
6354     while ( smIt->more() ) // loop on sub-shapes of the FACE
6355     {
6356       _EdgesOnShape* eoe = GetShapeEdges( smIt->next()->GetId() );
6357       if ( !eoe ) continue;
6358
6359       vector<_LayerEdge*>& eE = eoe->_edges;
6360       for ( size_t iE = 0; iE < eE.size(); ++iE ) // loop on _LayerEdge's on EDGE or VERTEX
6361       {
6362         if ( eE[iE]->_cosin <= theMinSmoothCosin )
6363           continue;
6364
6365         SMDS_ElemIteratorPtr segIt = eE[iE]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Edge);
6366         while ( segIt->more() )
6367         {
6368           const SMDS_MeshElement* seg = segIt->next();
6369           if ( !eos->_subMesh->DependsOn( seg->getshapeId() ))
6370             continue;
6371           if ( seg->GetNode(0) != eE[iE]->_nodes[0] )
6372             continue; // not to check a seg twice
6373           for ( size_t iN = 0; iN < eE[iE]->_neibors.size(); ++iN )
6374           {
6375             _LayerEdge* eN = eE[iE]->_neibors[iN];
6376             if ( eN->_nodes[0]->getshapeId() != eos->_shapeID )
6377               continue;
6378             double dist    = SMESH_MeshAlgos::GetDistance( seg, SMESH_TNodeXYZ( eN->_nodes[0] ));
6379             double smooLen = getSmoothingThickness( eE[iE]->_cosin, dist );
6380             eN->SetSmooLen( Min( smooLen, eN->GetSmooLen() ));
6381             eN->Set( _LayerEdge::NEAR_BOUNDARY );
6382           }
6383         }
6384       }
6385     }
6386   } // if ( eos->ShapeType() == TopAbs_FACE )
6387
6388   for ( size_t i = 0; i < eos->_edges.size(); ++i )
6389   {
6390     eos->_edges[i]->_smooFunction = 0;
6391     eos->_edges[i]->Set( _LayerEdge::TO_SMOOTH );
6392   }
6393   bool isCurved = false;
6394   for ( size_t i = 0; i < eos->_edges.size(); ++i )
6395   {
6396     _LayerEdge* edge = eos->_edges[i];
6397
6398     // get simplices sorted
6399     _Simplex::SortSimplices( edge->_simplices );
6400
6401     // smoothing function
6402     edge->ChooseSmooFunction( vertices, _n2eMap );
6403
6404     // set _curvature
6405     double avgNormProj = 0, avgLen = 0;
6406     for ( size_t iS = 0; iS < edge->_simplices.size(); ++iS )
6407     {
6408       _Simplex& s = edge->_simplices[iS];
6409
6410       gp_XYZ  vec = edge->_pos.back() - SMESH_TNodeXYZ( s._nPrev );
6411       avgNormProj += edge->_normal * vec;
6412       avgLen      += vec.Modulus();
6413       if ( substituteSrcNodes )
6414       {
6415         s._nNext = _n2eMap[ s._nNext ]->_nodes.back();
6416         s._nPrev = _n2eMap[ s._nPrev ]->_nodes.back();
6417       }
6418     }
6419     avgNormProj /= edge->_simplices.size();
6420     avgLen      /= edge->_simplices.size();
6421     if (( edge->_curvature = _Curvature::New( avgNormProj, avgLen )))
6422     {
6423       isCurved = true;
6424       SMDS_FacePosition* fPos = dynamic_cast<SMDS_FacePosition*>( edge->_nodes[0]->GetPosition() );
6425       if ( !fPos )
6426         for ( size_t iS = 0; iS < edge->_simplices.size()  &&  !fPos; ++iS )
6427           fPos = dynamic_cast<SMDS_FacePosition*>( edge->_simplices[iS]._nPrev->GetPosition() );
6428       if ( fPos )
6429         edge->_curvature->_uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
6430     }
6431   }
6432
6433   // prepare for putOnOffsetSurface()
6434   if (( eos->ShapeType() == TopAbs_FACE ) &&
6435       ( isCurved || !eos->_eosConcaVer.empty() ))
6436   {
6437     eos->_offsetSurf = helper.GetSurface( TopoDS::Face( eos->_shape ));
6438     eos->_edgeForOffset = 0;
6439
6440     double maxCosin = -1;
6441     for ( TopExp_Explorer eExp( eos->_shape, TopAbs_EDGE ); eExp.More(); eExp.Next() )
6442     {
6443       _EdgesOnShape* eoe = GetShapeEdges( eExp.Current() );
6444       if ( !eoe || eoe->_edges.empty() ) continue;
6445
6446       vector<_LayerEdge*>& eE = eoe->_edges;
6447       _LayerEdge* e = eE[ eE.size() / 2 ];
6448       if ( e->_cosin > maxCosin )
6449       {
6450         eos->_edgeForOffset = e;
6451         maxCosin = e->_cosin;
6452       }
6453     }
6454   }
6455 }
6456
6457 //================================================================================
6458 /*!
6459  * \brief Add faces for smoothing
6460  */
6461 //================================================================================
6462
6463 void _SolidData::AddShapesToSmooth( const set< _EdgesOnShape* >& eosToSmooth,
6464                                     const set< _EdgesOnShape* >* edgesNoAnaSmooth )
6465 {
6466   set< _EdgesOnShape * >::const_iterator eos = eosToSmooth.begin();
6467   for ( ; eos != eosToSmooth.end(); ++eos )
6468   {
6469     if ( !*eos || (*eos)->_toSmooth ) continue;
6470
6471     (*eos)->_toSmooth = true;
6472
6473     if ( (*eos)->ShapeType() == TopAbs_FACE )
6474     {
6475       PrepareEdgesToSmoothOnFace( *eos, /*substituteSrcNodes=*/false );
6476       (*eos)->_toSmooth = true;
6477     }
6478   }
6479
6480   // avoid _Smoother1D::smoothAnalyticEdge() of edgesNoAnaSmooth
6481   if ( edgesNoAnaSmooth )
6482     for ( eos = edgesNoAnaSmooth->begin(); eos != edgesNoAnaSmooth->end(); ++eos )
6483     {
6484       if ( (*eos)->_edgeSmoother )
6485         (*eos)->_edgeSmoother->_anaCurve.Nullify();
6486     }
6487 }
6488
6489 //================================================================================
6490 /*!
6491  * \brief Limit _LayerEdge::_maxLen according to local curvature
6492  */
6493 //================================================================================
6494
6495 void _ViscousBuilder::limitMaxLenByCurvature( _SolidData& data, SMESH_MesherHelper& helper )
6496 {
6497   // find intersection of neighbor _LayerEdge's to limit _maxLen
6498   // according to local curvature (IPAL52648)
6499
6500   // This method must be called after findCollisionEdges() where _LayerEdge's
6501   // get _lenFactor initialized in the case of eos._hyp.IsOffsetMethod()
6502
6503   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
6504   {
6505     _EdgesOnShape& eosI = data._edgesOnShape[iS];
6506     if ( eosI._edges.empty() ) continue;
6507     if ( !eosI._hyp.ToSmooth() )
6508     {
6509       for ( size_t i = 0; i < eosI._edges.size(); ++i )
6510       {
6511         _LayerEdge* eI = eosI._edges[i];
6512         for ( size_t iN = 0; iN < eI->_neibors.size(); ++iN )
6513         {
6514           _LayerEdge* eN = eI->_neibors[iN];
6515           if ( eI->_nodes[0]->GetID() < eN->_nodes[0]->GetID() ) // treat this pair once
6516           {
6517             _EdgesOnShape* eosN = data.GetShapeEdges( eN );
6518             limitMaxLenByCurvature( eI, eN, eosI, *eosN, helper );
6519           }
6520         }
6521       }
6522     }
6523     else if ( eosI.ShapeType() == TopAbs_EDGE )
6524     {
6525       const TopoDS_Edge& E = TopoDS::Edge( eosI._shape );
6526       if ( SMESH_Algo::IsStraight( E, /*degenResult=*/true )) continue;
6527
6528       _LayerEdge* e0 = eosI._edges[0];
6529       for ( size_t i = 1; i < eosI._edges.size(); ++i )
6530       {
6531         _LayerEdge* eI = eosI._edges[i];
6532         limitMaxLenByCurvature( eI, e0, eosI, eosI, helper );
6533         e0 = eI;
6534       }
6535     }
6536   }
6537 }
6538
6539 //================================================================================
6540 /*!
6541  * \brief Limit _LayerEdge::_maxLen according to local curvature
6542  */
6543 //================================================================================
6544
6545 void _ViscousBuilder::limitMaxLenByCurvature( _LayerEdge*         e1,
6546                                               _LayerEdge*         e2,
6547                                               _EdgesOnShape&      eos1,
6548                                               _EdgesOnShape&      eos2,
6549                                               SMESH_MesherHelper& helper )
6550 {
6551   gp_XYZ plnNorm = e1->_normal ^ e2->_normal;
6552   double norSize = plnNorm.SquareModulus();
6553   if ( norSize < std::numeric_limits<double>::min() )
6554     return; // parallel normals
6555
6556   // find closest points of skew _LayerEdge's
6557   SMESH_TNodeXYZ src1( e1->_nodes[0] ), src2( e2->_nodes[0] );
6558   gp_XYZ dir12 = src2 - src1;
6559   gp_XYZ perp1 = e1->_normal ^ plnNorm;
6560   gp_XYZ perp2 = e2->_normal ^ plnNorm;
6561   double  dot1 = perp2 * e1->_normal;
6562   double  dot2 = perp1 * e2->_normal;
6563   double    u1 =   ( perp2 * dir12 ) / dot1;
6564   double    u2 = - ( perp1 * dir12 ) / dot2;
6565   if ( u1 > 0 && u2 > 0 )
6566   {
6567     double ovl = ( u1 * e1->_normal * dir12 -
6568                    u2 * e2->_normal * dir12 ) / dir12.SquareModulus();
6569     if ( ovl > theSmoothThickToElemSizeRatio )
6570     {    
6571       e1->_maxLen = Min( e1->_maxLen, 0.75 * u1 / e1->_lenFactor );
6572       e2->_maxLen = Min( e2->_maxLen, 0.75 * u2 / e2->_lenFactor );
6573     }
6574   }
6575 }
6576
6577 //================================================================================
6578 /*!
6579  * \brief Fill data._collisionEdges
6580  */
6581 //================================================================================
6582
6583 void _ViscousBuilder::findCollisionEdges( _SolidData& data, SMESH_MesherHelper& helper )
6584 {
6585   data._collisionEdges.clear();
6586
6587   // set the full thickness of the layers to LEs
6588   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
6589   {
6590     _EdgesOnShape& eos = data._edgesOnShape[iS];
6591     if ( eos._edges.empty() ) continue;
6592     if ( eos.ShapeType() != TopAbs_EDGE && eos.ShapeType() != TopAbs_VERTEX ) continue;
6593
6594     for ( size_t i = 0; i < eos._edges.size(); ++i )
6595     {
6596       if ( eos._edges[i]->Is( _LayerEdge::BLOCKED )) continue;
6597       double maxLen = eos._edges[i]->_maxLen;
6598       eos._edges[i]->_maxLen = Precision::Infinite(); // avoid blocking
6599       eos._edges[i]->SetNewLength( 1.5 * maxLen, eos, helper );
6600       eos._edges[i]->_maxLen = maxLen;
6601     }
6602   }
6603
6604   // make temporary quadrangles got by extrusion of
6605   // mesh edges along _LayerEdge._normal's
6606
6607   vector< const SMDS_MeshElement* > tmpFaces;
6608
6609   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
6610   {
6611     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
6612     if ( eos.ShapeType() != TopAbs_EDGE )
6613       continue;
6614     if ( eos._edges.empty() )
6615     {
6616       _LayerEdge* edge[2] = { 0, 0 }; // LE of 2 VERTEX'es
6617       SMESH_subMeshIteratorPtr smIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false);
6618       while ( smIt->more() )
6619         if ( _EdgesOnShape* eov = data.GetShapeEdges( smIt->next()->GetId() ))
6620           if ( eov->_edges.size() == 1 )
6621             edge[ bool( edge[0]) ] = eov->_edges[0];
6622
6623       if ( edge[1] )
6624       {
6625         _TmpMeshFaceOnEdge* f = new _TmpMeshFaceOnEdge( edge[0], edge[1], --_tmpFaceID );
6626         tmpFaces.push_back( f );
6627       }
6628     }
6629     for ( size_t i = 0; i < eos._edges.size(); ++i )
6630     {
6631       _LayerEdge* edge = eos._edges[i];
6632       for ( int j = 0; j < 2; ++j ) // loop on _2NearEdges
6633       {
6634         const SMDS_MeshNode* src2 = edge->_2neibors->srcNode(j);
6635         if ( src2->GetPosition()->GetDim() > 0 &&
6636              src2->GetID() < edge->_nodes[0]->GetID() )
6637           continue; // avoid using same segment twice
6638
6639         // a _LayerEdge containg tgt2
6640         _LayerEdge* neiborEdge = edge->_2neibors->_edges[j];
6641
6642         _TmpMeshFaceOnEdge* f = new _TmpMeshFaceOnEdge( edge, neiborEdge, --_tmpFaceID );
6643         tmpFaces.push_back( f );
6644       }
6645     }
6646   }
6647
6648   // Find _LayerEdge's intersecting tmpFaces.
6649
6650   SMDS_ElemIteratorPtr fIt( new SMDS_ElementVectorIterator( tmpFaces.begin(),
6651                                                             tmpFaces.end()));
6652   SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
6653     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(), fIt ));
6654
6655   double dist1, dist2, segLen, eps = 0.5;
6656   _CollisionEdges collEdges;
6657   vector< const SMDS_MeshElement* > suspectFaces;
6658   const double angle45 = Cos( 45. * M_PI / 180. );
6659
6660   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
6661   {
6662     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
6663     if ( eos.ShapeType() == TopAbs_FACE || !eos._sWOL.IsNull() )
6664       continue;
6665     // find sub-shapes whose VL can influence VL on eos
6666     set< TGeomID > neighborShapes;
6667     PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
6668     while ( const TopoDS_Shape* face = fIt->next() )
6669     {
6670       TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
6671       if ( _EdgesOnShape* eof = data.GetShapeEdges( faceID ))
6672       {
6673         SMESH_subMeshIteratorPtr subIt = eof->_subMesh->getDependsOnIterator(/*includeSelf=*/false);
6674         while ( subIt->more() )
6675           neighborShapes.insert( subIt->next()->GetId() );
6676       }
6677     }
6678     if ( eos.ShapeType() == TopAbs_VERTEX )
6679     {
6680       PShapeIteratorPtr eIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_EDGE );
6681       while ( const TopoDS_Shape* edge = eIt->next() )
6682         neighborShapes.erase( getMeshDS()->ShapeToIndex( *edge ));
6683     }
6684     // find intersecting _LayerEdge's
6685     for ( size_t i = 0; i < eos._edges.size(); ++i )
6686     {
6687       if ( eos._edges[i]->Is( _LayerEdge::MULTI_NORMAL )) continue;
6688       _LayerEdge*   edge = eos._edges[i];
6689       gp_Ax1 lastSegment = edge->LastSegment( segLen, eos );
6690       segLen *= 1.2;
6691
6692       gp_Vec eSegDir0, eSegDir1;
6693       if ( edge->IsOnEdge() )
6694       {
6695         SMESH_TNodeXYZ eP( edge->_nodes[0] );
6696         eSegDir0 = SMESH_TNodeXYZ( edge->_2neibors->srcNode(0) ) - eP;
6697         eSegDir1 = SMESH_TNodeXYZ( edge->_2neibors->srcNode(1) ) - eP;
6698       }
6699       suspectFaces.clear();
6700       searcher->GetElementsInSphere( SMESH_TNodeXYZ( edge->_nodes.back()), edge->_len * 2,
6701                                      SMDSAbs_Face, suspectFaces );
6702       collEdges._intEdges.clear();
6703       for ( size_t j = 0 ; j < suspectFaces.size(); ++j )
6704       {
6705         const _TmpMeshFaceOnEdge* f = (const _TmpMeshFaceOnEdge*) suspectFaces[j];
6706         if ( f->_le1 == edge || f->_le2 == edge ) continue;
6707         if ( !neighborShapes.count( f->_le1->_nodes[0]->getshapeId() )) continue;
6708         if ( !neighborShapes.count( f->_le2->_nodes[0]->getshapeId() )) continue;
6709         if ( edge->IsOnEdge() ) {
6710           if ( edge->_2neibors->include( f->_le1 ) ||
6711                edge->_2neibors->include( f->_le2 )) continue;
6712         }
6713         else {
6714           if (( f->_le1->IsOnEdge() && f->_le1->_2neibors->include( edge )) ||
6715               ( f->_le2->IsOnEdge() && f->_le2->_2neibors->include( edge )))  continue;
6716         }
6717         dist1 = dist2 = Precision::Infinite();
6718         if ( !edge->SegTriaInter( lastSegment, f->_nn[0], f->_nn[1], f->_nn[2], dist1, eps ))
6719           dist1 = Precision::Infinite();
6720         if ( !edge->SegTriaInter( lastSegment, f->_nn[3], f->_nn[2], f->_nn[0], dist2, eps ))
6721           dist2 = Precision::Infinite();
6722         if (( dist1 > segLen ) && ( dist2 > segLen ))
6723           continue;
6724
6725         if ( edge->IsOnEdge() )
6726         {
6727           // skip perpendicular EDGEs
6728           gp_Vec fSegDir  = SMESH_TNodeXYZ( f->_nn[0] ) - SMESH_TNodeXYZ( f->_nn[3] );
6729           bool isParallel = ( isLessAngle( eSegDir0, fSegDir, angle45 ) ||
6730                               isLessAngle( eSegDir1, fSegDir, angle45 ) ||
6731                               isLessAngle( eSegDir0, fSegDir.Reversed(), angle45 ) ||
6732                               isLessAngle( eSegDir1, fSegDir.Reversed(), angle45 ));
6733           if ( !isParallel )
6734             continue;
6735         }
6736
6737         // either limit inflation of edges or remember them for updating _normal
6738         // double dot = edge->_normal * f->GetDir();
6739         // if ( dot > 0.1 )
6740         {
6741           collEdges._intEdges.push_back( f->_le1 );
6742           collEdges._intEdges.push_back( f->_le2 );
6743         }
6744         // else
6745         // {
6746         //   double shortLen = 0.75 * ( Min( dist1, dist2 ) / edge->_lenFactor );
6747         //   edge->_maxLen = Min( shortLen, edge->_maxLen );
6748         // }
6749       }
6750
6751       if ( !collEdges._intEdges.empty() )
6752       {
6753         collEdges._edge = edge;
6754         data._collisionEdges.push_back( collEdges );
6755       }
6756     }
6757   }
6758
6759   for ( size_t i = 0 ; i < tmpFaces.size(); ++i )
6760     delete tmpFaces[i];
6761
6762   // restore the zero thickness
6763   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
6764   {
6765     _EdgesOnShape& eos = data._edgesOnShape[iS];
6766     if ( eos._edges.empty() ) continue;
6767     if ( eos.ShapeType() != TopAbs_EDGE && eos.ShapeType() != TopAbs_VERTEX ) continue;
6768
6769     for ( size_t i = 0; i < eos._edges.size(); ++i )
6770     {
6771       eos._edges[i]->InvalidateStep( 1, eos );
6772       eos._edges[i]->_len = 0;
6773     }
6774   }
6775 }
6776
6777 //================================================================================
6778 /*!
6779  * \brief Find _LayerEdge's located on boundary of a convex FACE whose normal
6780  *        will be updated at each inflation step
6781  */
6782 //================================================================================
6783
6784 void _ViscousBuilder::findEdgesToUpdateNormalNearConvexFace( _ConvexFace &       convFace,
6785                                                              _SolidData&         data,
6786                                                              SMESH_MesherHelper& helper )
6787 {
6788   const TGeomID convFaceID = getMeshDS()->ShapeToIndex( convFace._face );
6789   const double       preci = BRep_Tool::Tolerance( convFace._face );
6790   Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( convFace._face );
6791
6792   bool edgesToUpdateFound = false;
6793
6794   map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.begin();
6795   for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
6796   {
6797     _EdgesOnShape& eos = * id2eos->second;
6798     if ( !eos._sWOL.IsNull() ) continue;
6799     if ( !eos._hyp.ToSmooth() ) continue;
6800     for ( size_t i = 0; i < eos._edges.size(); ++i )
6801     {
6802       _LayerEdge* ledge = eos._edges[ i ];
6803       if ( ledge->Is( _LayerEdge::UPD_NORMAL_CONV )) continue; // already checked
6804       if ( ledge->Is( _LayerEdge::MULTI_NORMAL )) continue; // not inflatable
6805
6806       gp_XYZ tgtPos = ( SMESH_NodeXYZ( ledge->_nodes[0] ) +
6807                         ledge->_normal * ledge->_lenFactor * ledge->_maxLen );
6808
6809       // the normal must be updated if distance from tgtPos to surface is less than
6810       // target thickness
6811
6812       // find an initial UV for search of a projection of tgtPos to surface
6813       const SMDS_MeshNode* nodeInFace = 0;
6814       SMDS_ElemIteratorPtr fIt = ledge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
6815       while ( fIt->more() && !nodeInFace )
6816       {
6817         const SMDS_MeshElement* f = fIt->next();
6818         if ( convFaceID != f->getshapeId() ) continue;
6819
6820         SMDS_ElemIteratorPtr nIt = f->nodesIterator();
6821         while ( nIt->more() && !nodeInFace )
6822         {
6823           const SMDS_MeshElement* n = nIt->next();
6824           if ( n->getshapeId() == convFaceID )
6825             nodeInFace = static_cast< const SMDS_MeshNode* >( n );
6826         }
6827       }
6828       if ( !nodeInFace )
6829         continue;
6830       gp_XY uv = helper.GetNodeUV( convFace._face, nodeInFace );
6831
6832       // projection
6833       surface->NextValueOfUV( uv, tgtPos, preci );
6834       double  dist = surface->Gap();
6835       if ( dist < 0.95 * ledge->_maxLen )
6836       {
6837         ledge->Set( _LayerEdge::UPD_NORMAL_CONV );
6838         if ( !ledge->_curvature ) ledge->_curvature = new _Curvature;
6839         ledge->_curvature->_uv.SetCoord( uv.X(), uv.Y() );
6840         edgesToUpdateFound = true;
6841       }
6842     }
6843   }
6844
6845   if ( !convFace._isTooCurved && edgesToUpdateFound )
6846   {
6847     data._convexFaces.insert( make_pair( convFaceID, convFace )).first->second;
6848   }
6849 }
6850
6851 //================================================================================
6852 /*!
6853  * \brief Modify normals of _LayerEdge's on EDGE's to avoid intersection with
6854  * _LayerEdge's on neighbor EDGE's
6855  */
6856 //================================================================================
6857
6858 bool _ViscousBuilder::updateNormals( _SolidData&         data,
6859                                      SMESH_MesherHelper& helper,
6860                                      int                 stepNb,
6861                                      double              stepSize)
6862 {
6863   updateNormalsOfC1Vertices( data );
6864
6865   if ( stepNb > 0 && !updateNormalsOfConvexFaces( data, helper, stepNb ))
6866     return false;
6867
6868   // map to store new _normal and _cosin for each intersected edge
6869   map< _LayerEdge*, _LayerEdge, _LayerEdgeCmp >           edge2newEdge;
6870   map< _LayerEdge*, _LayerEdge, _LayerEdgeCmp >::iterator e2neIt;
6871   _LayerEdge zeroEdge;
6872   zeroEdge._normal.SetCoord( 0,0,0 );
6873   zeroEdge._maxLen = Precision::Infinite();
6874   zeroEdge._nodes.resize(1); // to init _TmpMeshFaceOnEdge
6875
6876   set< _EdgesOnShape* > shapesToSmooth, edgesNoAnaSmooth;
6877
6878   double segLen, dist1, dist2, dist;
6879   vector< pair< _LayerEdge*, double > > intEdgesDist;
6880   _TmpMeshFaceOnEdge quad( &zeroEdge, &zeroEdge, 0 );
6881
6882   for ( int iter = 0; iter < 5; ++iter )
6883   {
6884     edge2newEdge.clear();
6885
6886     for ( size_t iE = 0; iE < data._collisionEdges.size(); ++iE )
6887     {
6888       _CollisionEdges& ce = data._collisionEdges[iE];
6889       _LayerEdge*   edge1 = ce._edge;
6890       if ( !edge1 /*|| edge1->Is( _LayerEdge::BLOCKED )*/) continue;
6891       _EdgesOnShape* eos1 = data.GetShapeEdges( edge1 );
6892       if ( !eos1 ) continue;
6893
6894       // detect intersections
6895       gp_Ax1 lastSeg = edge1->LastSegment( segLen, *eos1 );
6896       double testLen = 1.5 * edge1->_maxLen * edge1->_lenFactor;
6897       double     eps = 0.5;
6898       intEdgesDist.clear();
6899       double minIntDist = Precision::Infinite();
6900       for ( size_t i = 0; i < ce._intEdges.size(); i += 2 )
6901       {
6902         if ( edge1->Is( _LayerEdge::BLOCKED ) &&
6903              ce._intEdges[i  ]->Is( _LayerEdge::BLOCKED ) &&
6904              ce._intEdges[i+1]->Is( _LayerEdge::BLOCKED ))
6905           continue;
6906         double dot  = edge1->_normal * quad.GetDir( ce._intEdges[i], ce._intEdges[i+1] );
6907         double fact = ( 1.1 + dot * dot );
6908         SMESH_TNodeXYZ pSrc0( ce.nSrc(i) ), pSrc1( ce.nSrc(i+1) );
6909         SMESH_TNodeXYZ pTgt0( ce.nTgt(i) ), pTgt1( ce.nTgt(i+1) );
6910         gp_XYZ pLast0 = pSrc0 + ( pTgt0 - pSrc0 ) * fact;
6911         gp_XYZ pLast1 = pSrc1 + ( pTgt1 - pSrc1 ) * fact;
6912         dist1 = dist2 = Precision::Infinite();
6913         if ( !edge1->SegTriaInter( lastSeg, pSrc0, pLast0, pSrc1,  dist1, eps ) &&
6914              !edge1->SegTriaInter( lastSeg, pSrc1, pLast1, pLast0, dist2, eps ))
6915           continue;
6916         dist = dist1;
6917         if ( dist > testLen || dist <= 0 )
6918         {
6919           dist = dist2;
6920           if ( dist > testLen || dist <= 0 )
6921             continue;
6922         }
6923         // choose a closest edge
6924         gp_Pnt intP( lastSeg.Location().XYZ() + lastSeg.Direction().XYZ() * ( dist + segLen ));
6925         double d1 = intP.SquareDistance( pSrc0 );
6926         double d2 = intP.SquareDistance( pSrc1 );
6927         int iClose = i + ( d2 < d1 );
6928         _LayerEdge* edge2 = ce._intEdges[iClose];
6929         edge2->Unset( _LayerEdge::MARKED );
6930
6931         // choose a closest edge among neighbors
6932         gp_Pnt srcP( SMESH_TNodeXYZ( edge1->_nodes[0] ));
6933         d1 = srcP.SquareDistance( SMESH_TNodeXYZ( edge2->_nodes[0] ));
6934         for ( size_t j = 0; j < intEdgesDist.size(); ++j )
6935         {
6936           _LayerEdge * edgeJ = intEdgesDist[j].first;
6937           if ( edge2->IsNeiborOnEdge( edgeJ ))
6938           {
6939             d2 = srcP.SquareDistance( SMESH_TNodeXYZ( edgeJ->_nodes[0] ));
6940             ( d1 < d2 ? edgeJ : edge2 )->Set( _LayerEdge::MARKED );
6941           }
6942         }
6943         intEdgesDist.push_back( make_pair( edge2, dist ));
6944         // if ( Abs( d2 - d1 ) / Max( d2, d1 ) < 0.5 )
6945         // {
6946         //   iClose = i + !( d2 < d1 );
6947         //   intEdges.push_back( ce._intEdges[iClose] );
6948         //   ce._intEdges[iClose]->Unset( _LayerEdge::MARKED );
6949         // }
6950         minIntDist = Min( edge1->_len * edge1->_lenFactor - segLen + dist, minIntDist );
6951       }
6952
6953       //ce._edge = 0;
6954
6955       // compute new _normals
6956       for ( size_t i = 0; i < intEdgesDist.size(); ++i )
6957       {
6958         _LayerEdge* edge2   = intEdgesDist[i].first;
6959         double      distWgt = edge1->_len / intEdgesDist[i].second;
6960         // if ( edge1->Is( _LayerEdge::BLOCKED ) &&
6961         //      edge2->Is( _LayerEdge::BLOCKED )) continue;        
6962         if ( edge2->Is( _LayerEdge::MARKED )) continue;
6963         edge2->Set( _LayerEdge::MARKED );
6964
6965         // get a new normal
6966         gp_XYZ dir1 = edge1->_normal, dir2 = edge2->_normal;
6967
6968         double cos1 = Abs( edge1->_cosin ), cos2 = Abs( edge2->_cosin );
6969         double wgt1 = ( cos1 + 0.001 ) / ( cos1 + cos2 + 0.002 );
6970         double wgt2 = ( cos2 + 0.001 ) / ( cos1 + cos2 + 0.002 );
6971         // double cos1 = Abs( edge1->_cosin ),        cos2 = Abs( edge2->_cosin );
6972         // double sgn1 = 0.1 * ( 1 + edge1->_cosin ), sgn2 = 0.1 * ( 1 + edge2->_cosin );
6973         // double wgt1 = ( cos1 + sgn1 ) / ( cos1 + cos2 + sgn1 + sgn2 );
6974         // double wgt2 = ( cos2 + sgn2 ) / ( cos1 + cos2 + sgn1 + sgn2 );
6975         gp_XYZ newNormal = wgt1 * dir1 + wgt2 * dir2;
6976         newNormal.Normalize();
6977
6978         // get new cosin
6979         double newCos;
6980         double sgn1 = edge1->_cosin / cos1, sgn2 = edge2->_cosin / cos2;
6981         if ( cos1 < theMinSmoothCosin )
6982         {
6983           newCos = cos2 * sgn1;
6984         }
6985         else if ( cos2 > theMinSmoothCosin ) // both cos1 and cos2 > theMinSmoothCosin
6986         {
6987           newCos = ( wgt1 * cos1 + wgt2 * cos2 ) * edge1->_cosin / cos1;
6988         }
6989         else
6990         {
6991           newCos = edge1->_cosin;
6992         }
6993
6994         e2neIt = edge2newEdge.insert( make_pair( edge1, zeroEdge )).first;
6995         e2neIt->second._normal += distWgt * newNormal;
6996         e2neIt->second._cosin   = newCos;
6997         e2neIt->second._maxLen  = 0.7 * minIntDist / edge1->_lenFactor;
6998         if ( iter > 0 && sgn1 * sgn2 < 0 && edge1->_cosin < 0 )
6999           e2neIt->second._normal += dir2;
7000
7001         e2neIt = edge2newEdge.insert( make_pair( edge2, zeroEdge )).first;
7002         e2neIt->second._normal += distWgt * newNormal;
7003         if ( Precision::IsInfinite( zeroEdge._maxLen ))
7004         {
7005           e2neIt->second._cosin  = edge2->_cosin;
7006           e2neIt->second._maxLen = 1.3 * minIntDist / edge1->_lenFactor;
7007         }
7008         if ( iter > 0 && sgn1 * sgn2 < 0 && edge2->_cosin < 0 )
7009           e2neIt->second._normal += dir1;
7010       }
7011     }
7012
7013     if ( edge2newEdge.empty() )
7014       break; //return true;
7015
7016     dumpFunction(SMESH_Comment("updateNormals")<< data._index << "_" << stepNb << "_it" << iter);
7017
7018     // Update data of edges depending on a new _normal
7019
7020     data.UnmarkEdges();
7021     for ( e2neIt = edge2newEdge.begin(); e2neIt != edge2newEdge.end(); ++e2neIt )
7022     {
7023       _LayerEdge*    edge = e2neIt->first;
7024       _LayerEdge& newEdge = e2neIt->second;
7025       _EdgesOnShape*  eos = data.GetShapeEdges( edge );
7026       if ( edge->Is( _LayerEdge::BLOCKED && newEdge._maxLen > edge->_len ))
7027         continue;
7028
7029       // Check if a new _normal is OK:
7030       newEdge._normal.Normalize();
7031       if ( !isNewNormalOk( data, *edge, newEdge._normal ))
7032       {
7033         if ( newEdge._maxLen < edge->_len && iter > 0 ) // limit _maxLen
7034         {
7035           edge->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true  );
7036           edge->_maxLen = newEdge._maxLen;
7037           edge->SetNewLength( newEdge._maxLen, *eos, helper );
7038         }
7039         continue; // the new _normal is bad
7040       }
7041       // the new _normal is OK
7042
7043       // find shapes that need smoothing due to change of _normal
7044       if ( edge->_cosin   < theMinSmoothCosin &&
7045            newEdge._cosin > theMinSmoothCosin )
7046       {
7047         if ( eos->_sWOL.IsNull() )
7048         {
7049           SMDS_ElemIteratorPtr fIt = edge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
7050           while ( fIt->more() )
7051             shapesToSmooth.insert( data.GetShapeEdges( fIt->next()->getshapeId() ));
7052         }
7053         else // edge inflates along a FACE
7054         {
7055           TopoDS_Shape V = helper.GetSubShapeByNode( edge->_nodes[0], getMeshDS() );
7056           PShapeIteratorPtr eIt = helper.GetAncestors( V, *_mesh, TopAbs_EDGE, &eos->_sWOL );
7057           while ( const TopoDS_Shape* E = eIt->next() )
7058           {
7059             gp_Vec edgeDir = getEdgeDir( TopoDS::Edge( *E ), TopoDS::Vertex( V ));
7060             double   angle = edgeDir.Angle( newEdge._normal ); // [0,PI]
7061             if ( angle < M_PI / 2 )
7062               shapesToSmooth.insert( data.GetShapeEdges( *E ));
7063           }
7064         }
7065       }
7066
7067       double len = edge->_len;
7068       edge->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true  );
7069       edge->SetNormal( newEdge._normal );
7070       edge->SetCosin( newEdge._cosin );
7071       edge->SetNewLength( len, *eos, helper );
7072       edge->Set( _LayerEdge::MARKED );
7073       edge->Set( _LayerEdge::NORMAL_UPDATED );
7074       edgesNoAnaSmooth.insert( eos );
7075     }
7076
7077     // Update normals and other dependent data of not intersecting _LayerEdge's
7078     // neighboring the intersecting ones
7079
7080     for ( e2neIt = edge2newEdge.begin(); e2neIt != edge2newEdge.end(); ++e2neIt )
7081     {
7082       _LayerEdge*   edge1 = e2neIt->first;
7083       _EdgesOnShape* eos1 = data.GetShapeEdges( edge1 );
7084       if ( !edge1->Is( _LayerEdge::MARKED ))
7085         continue;
7086
7087       if ( edge1->IsOnEdge() )
7088       {
7089         const SMDS_MeshNode * n1 = edge1->_2neibors->srcNode(0);
7090         const SMDS_MeshNode * n2 = edge1->_2neibors->srcNode(1);
7091         edge1->SetDataByNeighbors( n1, n2, *eos1, helper );
7092       }
7093
7094       if ( !edge1->_2neibors || !eos1->_sWOL.IsNull() )
7095         continue;
7096       for ( int j = 0; j < 2; ++j ) // loop on 2 neighbors
7097       {
7098         _LayerEdge* neighbor = edge1->_2neibors->_edges[j];
7099         if ( neighbor->Is( _LayerEdge::MARKED ) /*edge2newEdge.count ( neighbor )*/)
7100           continue; // j-th neighbor is also intersected
7101         _LayerEdge* prevEdge = edge1;
7102         const int nbSteps = 10;
7103         for ( int step = nbSteps; step; --step ) // step from edge1 in j-th direction
7104         {
7105           if ( neighbor->Is( _LayerEdge::BLOCKED ) ||
7106                neighbor->Is( _LayerEdge::MARKED ))
7107             break;
7108           _EdgesOnShape* eos = data.GetShapeEdges( neighbor );
7109           if ( !eos ) continue;
7110           _LayerEdge* nextEdge = neighbor;
7111           if ( neighbor->_2neibors )
7112           {
7113             int iNext = 0;
7114             nextEdge = neighbor->_2neibors->_edges[iNext];
7115             if ( nextEdge == prevEdge )
7116               nextEdge = neighbor->_2neibors->_edges[ ++iNext ];
7117           }
7118           double r = double(step-1)/nbSteps/(iter+1);
7119           if ( !nextEdge->_2neibors )
7120             r = Min( r, 0.5 );
7121
7122           gp_XYZ newNorm = prevEdge->_normal * r + nextEdge->_normal * (1-r);
7123           newNorm.Normalize();
7124           if ( !isNewNormalOk( data, *neighbor, newNorm ))
7125             break;
7126
7127           double len = neighbor->_len;
7128           neighbor->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true  );
7129           neighbor->SetNormal( newNorm );
7130           neighbor->SetCosin( prevEdge->_cosin * r + nextEdge->_cosin * (1-r) );
7131           if ( neighbor->_2neibors )
7132             neighbor->SetDataByNeighbors( prevEdge->_nodes[0], nextEdge->_nodes[0], *eos, helper );
7133           neighbor->SetNewLength( len, *eos, helper );
7134           neighbor->Set( _LayerEdge::MARKED );
7135           neighbor->Set( _LayerEdge::NORMAL_UPDATED );
7136           edgesNoAnaSmooth.insert( eos );
7137
7138           if ( !neighbor->_2neibors )
7139             break; // neighbor is on VERTEX
7140
7141           // goto the next neighbor
7142           prevEdge = neighbor;
7143           neighbor = nextEdge;
7144         }
7145       }
7146     }
7147     dumpFunctionEnd();
7148   } // iterations
7149
7150   data.AddShapesToSmooth( shapesToSmooth, &edgesNoAnaSmooth );
7151
7152   return true;
7153 }
7154
7155 //================================================================================
7156 /*!
7157  * \brief Check if a new normal is OK
7158  */
7159 //================================================================================
7160
7161 bool _ViscousBuilder::isNewNormalOk( _SolidData&   data,
7162                                      _LayerEdge&   edge,
7163                                      const gp_XYZ& newNormal)
7164 {
7165   // check a min angle between the newNormal and surrounding faces
7166   vector<_Simplex> simplices;
7167   SMESH_TNodeXYZ n0( edge._nodes[0] ), n1, n2;
7168   _Simplex::GetSimplices( n0._node, simplices, data._ignoreFaceIds, &data );
7169   double newMinDot = 1, curMinDot = 1;
7170   for ( size_t i = 0; i < simplices.size(); ++i )
7171   {
7172     n1.Set( simplices[i]._nPrev );
7173     n2.Set( simplices[i]._nNext );
7174     gp_XYZ normFace = ( n1 - n0 ) ^ ( n2 - n0 );
7175     double normLen2 = normFace.SquareModulus();
7176     if ( normLen2 < std::numeric_limits<double>::min() )
7177       continue;
7178     normFace /= Sqrt( normLen2 );
7179     newMinDot = Min( newNormal    * normFace, newMinDot );
7180     curMinDot = Min( edge._normal * normFace, curMinDot );
7181   }
7182   bool ok = true;
7183   if ( newMinDot < 0.5 )
7184   {
7185     ok = ( newMinDot >= curMinDot * 0.9 );
7186     //return ( newMinDot >= ( curMinDot * ( 0.8 + 0.1 * edge.NbSteps() )));
7187     // double initMinDot2 = 1. - edge._cosin * edge._cosin;
7188     // return ( newMinDot * newMinDot ) >= ( 0.8 * initMinDot2 );
7189   }
7190
7191   return ok;
7192 }
7193
7194 //================================================================================
7195 /*!
7196  * \brief Modify normals of _LayerEdge's on FACE to reflex smoothing
7197  */
7198 //================================================================================
7199
7200 bool _ViscousBuilder::updateNormalsOfSmoothed( _SolidData&         data,
7201                                                SMESH_MesherHelper& helper,
7202                                                const int           nbSteps,
7203                                                const double        stepSize )
7204 {
7205   if ( data._nbShapesToSmooth == 0 || nbSteps == 0 )
7206     return true; // no shapes needing smoothing
7207
7208   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7209   {
7210     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
7211     if ( //!eos._toSmooth ||  _eosC1 have _toSmooth == false
7212          !eos._hyp.ToSmooth() ||
7213          eos.ShapeType() != TopAbs_FACE ||
7214          eos._edges.empty() )
7215       continue;
7216
7217     bool toSmooth = ( eos._edges[ 0 ]->NbSteps() >= nbSteps+1 );
7218     if ( !toSmooth ) continue;
7219
7220     for ( size_t i = 0; i < eos._edges.size(); ++i )
7221     {
7222       _LayerEdge* edge = eos._edges[i];
7223       if ( !edge->Is( _LayerEdge::SMOOTHED ))
7224         continue;
7225       if ( edge->Is( _LayerEdge::DIFFICULT ) && nbSteps != 1 )
7226         continue;
7227
7228       const gp_XYZ& pPrev = edge->PrevPos();
7229       const gp_XYZ& pLast = edge->_pos.back();
7230       gp_XYZ      stepVec = pLast - pPrev;
7231       double realStepSize = stepVec.Modulus();
7232       if ( realStepSize < numeric_limits<double>::min() )
7233         continue;
7234
7235       edge->_lenFactor = realStepSize / stepSize;
7236       edge->_normal    = stepVec / realStepSize;
7237       edge->Set( _LayerEdge::NORMAL_UPDATED );
7238     }
7239   }
7240
7241   return true;
7242 }
7243
7244 //================================================================================
7245 /*!
7246  * \brief Modify normals of _LayerEdge's on C1 VERTEXes
7247  */
7248 //================================================================================
7249
7250 void _ViscousBuilder::updateNormalsOfC1Vertices( _SolidData& data )
7251 {
7252   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7253   {
7254     _EdgesOnShape& eov = data._edgesOnShape[ iS ];
7255     if ( eov._eosC1.empty() ||
7256          eov.ShapeType() != TopAbs_VERTEX ||
7257          eov._edges.empty() )
7258       continue;
7259
7260     gp_XYZ newNorm   = eov._edges[0]->_normal;
7261     double curThick  = eov._edges[0]->_len * eov._edges[0]->_lenFactor;
7262     bool normChanged = false;
7263
7264     for ( size_t i = 0; i < eov._eosC1.size(); ++i )
7265     {
7266       _EdgesOnShape*   eoe = eov._eosC1[i];
7267       const TopoDS_Edge& e = TopoDS::Edge( eoe->_shape );
7268       const double    eLen = SMESH_Algo::EdgeLength( e );
7269       TopoDS_Shape    oppV = SMESH_MesherHelper::IthVertex( 0, e );
7270       if ( oppV.IsSame( eov._shape ))
7271         oppV = SMESH_MesherHelper::IthVertex( 1, e );
7272       _EdgesOnShape* eovOpp = data.GetShapeEdges( oppV );
7273       if ( !eovOpp || eovOpp->_edges.empty() ) continue;
7274       if ( eov._edges[0]->Is( _LayerEdge::BLOCKED )) continue;
7275
7276       double curThickOpp = eovOpp->_edges[0]->_len * eovOpp->_edges[0]->_lenFactor;
7277       if ( curThickOpp + curThick < eLen )
7278         continue;
7279
7280       double wgt = 2. * curThick / eLen;
7281       newNorm += wgt * eovOpp->_edges[0]->_normal;
7282       normChanged = true;
7283     }
7284     if ( normChanged )
7285     {
7286       eov._edges[0]->SetNormal( newNorm.Normalized() );
7287       eov._edges[0]->Set( _LayerEdge::NORMAL_UPDATED );
7288     }
7289   }
7290 }
7291
7292 //================================================================================
7293 /*!
7294  * \brief Modify normals of _LayerEdge's on _ConvexFace's
7295  */
7296 //================================================================================
7297
7298 bool _ViscousBuilder::updateNormalsOfConvexFaces( _SolidData&         data,
7299                                                   SMESH_MesherHelper& helper,
7300                                                   int                 stepNb )
7301 {
7302   SMESHDS_Mesh* meshDS = helper.GetMeshDS();
7303   bool isOK;
7304
7305   map< TGeomID, _ConvexFace >::iterator id2face = data._convexFaces.begin();
7306   for ( ; id2face != data._convexFaces.end(); ++id2face )
7307   {
7308     _ConvexFace & convFace = (*id2face).second;
7309     convFace._normalsFixedOnBorders = false; // to update at each inflation step
7310
7311     if ( convFace._normalsFixed )
7312       continue; // already fixed
7313     if ( convFace.CheckPrisms() )
7314       continue; // nothing to fix
7315
7316     convFace._normalsFixed = true;
7317
7318     BRepAdaptor_Surface surface ( convFace._face, false );
7319     BRepLProp_SLProps   surfProp( surface, 2, 1e-6 );
7320
7321     // check if the convex FACE is of spherical shape
7322
7323     Bnd_B3d centersBox; // bbox of centers of curvature of _LayerEdge's on VERTEXes
7324     Bnd_B3d nodesBox;
7325     gp_Pnt  center;
7326
7327     map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.begin();
7328     for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7329     {
7330       _EdgesOnShape& eos = *(id2eos->second);
7331       if ( eos.ShapeType() == TopAbs_VERTEX )
7332       {
7333         _LayerEdge* ledge = eos._edges[ 0 ];
7334         if ( convFace.GetCenterOfCurvature( ledge, surfProp, helper, center ))
7335           centersBox.Add( center );
7336       }
7337       for ( size_t i = 0; i < eos._edges.size(); ++i )
7338         nodesBox.Add( SMESH_TNodeXYZ( eos._edges[ i ]->_nodes[0] ));
7339     }
7340     if ( centersBox.IsVoid() )
7341     {
7342       debugMsg( "Error: centersBox.IsVoid()" );
7343       return false;
7344     }
7345     const bool isSpherical =
7346       ( centersBox.SquareExtent() < 1e-6 * nodesBox.SquareExtent() );
7347
7348     int nbEdges = helper.Count( convFace._face, TopAbs_EDGE, /*ignoreSame=*/false );
7349     vector < _CentralCurveOnEdge > centerCurves( nbEdges );
7350
7351     if ( isSpherical )
7352     {
7353       // set _LayerEdge::_normal as average of all normals
7354
7355       // WARNING: different density of nodes on EDGEs is not taken into account that
7356       // can lead to an improper new normal
7357
7358       gp_XYZ avgNormal( 0,0,0 );
7359       nbEdges = 0;
7360       id2eos = convFace._subIdToEOS.begin();
7361       for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7362       {
7363         _EdgesOnShape& eos = *(id2eos->second);
7364         // set data of _CentralCurveOnEdge
7365         if ( eos.ShapeType() == TopAbs_EDGE )
7366         {
7367           _CentralCurveOnEdge& ceCurve = centerCurves[ nbEdges++ ];
7368           ceCurve.SetShapes( TopoDS::Edge( eos._shape ), convFace, data, helper );
7369           if ( !eos._sWOL.IsNull() )
7370             ceCurve._adjFace.Nullify();
7371           else
7372             ceCurve._ledges.insert( ceCurve._ledges.end(),
7373                                     eos._edges.begin(), eos._edges.end());
7374         }
7375         // summarize normals
7376         for ( size_t i = 0; i < eos._edges.size(); ++i )
7377           avgNormal += eos._edges[ i ]->_normal;
7378       }
7379       double normSize = avgNormal.SquareModulus();
7380       if ( normSize < 1e-200 )
7381       {
7382         debugMsg( "updateNormalsOfConvexFaces(): zero avgNormal" );
7383         return false;
7384       }
7385       avgNormal /= Sqrt( normSize );
7386
7387       // compute new _LayerEdge::_cosin on EDGEs
7388       double avgCosin = 0;
7389       int     nbCosin = 0;
7390       gp_Vec inFaceDir;
7391       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
7392       {
7393         _CentralCurveOnEdge& ceCurve = centerCurves[ iE ];
7394         if ( ceCurve._adjFace.IsNull() )
7395           continue;
7396         for ( size_t iLE = 0; iLE < ceCurve._ledges.size(); ++iLE )
7397         {
7398           const SMDS_MeshNode* node = ceCurve._ledges[ iLE ]->_nodes[0];
7399           inFaceDir = getFaceDir( ceCurve._adjFace, ceCurve._edge, node, helper, isOK );
7400           if ( isOK )
7401           {
7402             double angle = inFaceDir.Angle( avgNormal ); // [0,PI]
7403             ceCurve._ledges[ iLE ]->_cosin = Cos( angle );
7404             avgCosin += ceCurve._ledges[ iLE ]->_cosin;
7405             nbCosin++;
7406           }
7407         }
7408       }
7409       if ( nbCosin > 0 )
7410         avgCosin /= nbCosin;
7411
7412       // set _LayerEdge::_normal = avgNormal
7413       id2eos = convFace._subIdToEOS.begin();
7414       for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7415       {
7416         _EdgesOnShape& eos = *(id2eos->second);
7417         if ( eos.ShapeType() != TopAbs_EDGE )
7418           for ( size_t i = 0; i < eos._edges.size(); ++i )
7419             eos._edges[ i ]->_cosin = avgCosin;
7420
7421         for ( size_t i = 0; i < eos._edges.size(); ++i )
7422         {
7423           eos._edges[ i ]->SetNormal( avgNormal );
7424           eos._edges[ i ]->Set( _LayerEdge::NORMAL_UPDATED );
7425         }
7426       }
7427     }
7428     else // if ( isSpherical )
7429     {
7430       // We suppose that centers of curvature at all points of the FACE
7431       // lie on some curve, let's call it "central curve". For all _LayerEdge's
7432       // having a common center of curvature we define the same new normal
7433       // as a sum of normals of _LayerEdge's on EDGEs among them.
7434
7435       // get all centers of curvature for each EDGE
7436
7437       helper.SetSubShape( convFace._face );
7438       _LayerEdge* vertexLEdges[2], **edgeLEdge, **edgeLEdgeEnd;
7439
7440       TopExp_Explorer edgeExp( convFace._face, TopAbs_EDGE );
7441       for ( int iE = 0; edgeExp.More(); edgeExp.Next(), ++iE )
7442       {
7443         const TopoDS_Edge& edge = TopoDS::Edge( edgeExp.Current() );
7444
7445         // set adjacent FACE
7446         centerCurves[ iE ].SetShapes( edge, convFace, data, helper );
7447
7448         // get _LayerEdge's of the EDGE
7449         TGeomID edgeID = meshDS->ShapeToIndex( edge );
7450         _EdgesOnShape* eos = data.GetShapeEdges( edgeID );
7451         if ( !eos || eos->_edges.empty() )
7452         {
7453           // no _LayerEdge's on EDGE, use _LayerEdge's on VERTEXes
7454           for ( int iV = 0; iV < 2; ++iV )
7455           {
7456             TopoDS_Vertex v = helper.IthVertex( iV, edge );
7457             TGeomID     vID = meshDS->ShapeToIndex( v );
7458             eos = data.GetShapeEdges( vID );
7459             vertexLEdges[ iV ] = eos->_edges[ 0 ];
7460           }
7461           edgeLEdge    = &vertexLEdges[0];
7462           edgeLEdgeEnd = edgeLEdge + 2;
7463
7464           centerCurves[ iE ]._adjFace.Nullify();
7465         }
7466         else
7467         {
7468           if ( ! eos->_toSmooth )
7469             data.SortOnEdge( edge, eos->_edges );
7470           edgeLEdge    = &eos->_edges[ 0 ];
7471           edgeLEdgeEnd = edgeLEdge + eos->_edges.size();
7472           vertexLEdges[0] = eos->_edges.front()->_2neibors->_edges[0];
7473           vertexLEdges[1] = eos->_edges.back() ->_2neibors->_edges[1];
7474
7475           if ( ! eos->_sWOL.IsNull() )
7476             centerCurves[ iE ]._adjFace.Nullify();
7477         }
7478
7479         // Get curvature centers
7480
7481         centersBox.Clear();
7482
7483         if ( edgeLEdge[0]->IsOnEdge() &&
7484              convFace.GetCenterOfCurvature( vertexLEdges[0], surfProp, helper, center ))
7485         { // 1st VERTEX
7486           centerCurves[ iE ].Append( center, vertexLEdges[0] );
7487           centersBox.Add( center );
7488         }
7489         for ( ; edgeLEdge < edgeLEdgeEnd; ++edgeLEdge )
7490           if ( convFace.GetCenterOfCurvature( *edgeLEdge, surfProp, helper, center ))
7491           { // EDGE or VERTEXes
7492             centerCurves[ iE ].Append( center, *edgeLEdge );
7493             centersBox.Add( center );
7494           }
7495         if ( edgeLEdge[-1]->IsOnEdge() &&
7496              convFace.GetCenterOfCurvature( vertexLEdges[1], surfProp, helper, center ))
7497         { // 2nd VERTEX
7498           centerCurves[ iE ].Append( center, vertexLEdges[1] );
7499           centersBox.Add( center );
7500         }
7501         centerCurves[ iE ]._isDegenerated =
7502           ( centersBox.IsVoid() || centersBox.SquareExtent() < 1e-6 * nodesBox.SquareExtent() );
7503
7504       } // loop on EDGES of convFace._face to set up data of centerCurves
7505
7506       // Compute new normals for _LayerEdge's on EDGEs
7507
7508       double avgCosin = 0;
7509       int     nbCosin = 0;
7510       gp_Vec inFaceDir;
7511       for ( size_t iE1 = 0; iE1 < centerCurves.size(); ++iE1 )
7512       {
7513         _CentralCurveOnEdge& ceCurve = centerCurves[ iE1 ];
7514         if ( ceCurve._isDegenerated )
7515           continue;
7516         const vector< gp_Pnt >& centers = ceCurve._curvaCenters;
7517         vector< gp_XYZ > &   newNormals = ceCurve._normals;
7518         for ( size_t iC1 = 0; iC1 < centers.size(); ++iC1 )
7519         {
7520           isOK = false;
7521           for ( size_t iE2 = 0; iE2 < centerCurves.size() && !isOK; ++iE2 )
7522           {
7523             if ( iE1 != iE2 )
7524               isOK = centerCurves[ iE2 ].FindNewNormal( centers[ iC1 ], newNormals[ iC1 ]);
7525           }
7526           if ( isOK && !ceCurve._adjFace.IsNull() )
7527           {
7528             // compute new _LayerEdge::_cosin
7529             const SMDS_MeshNode* node = ceCurve._ledges[ iC1 ]->_nodes[0];
7530             inFaceDir = getFaceDir( ceCurve._adjFace, ceCurve._edge, node, helper, isOK );
7531             if ( isOK )
7532             {
7533               double angle = inFaceDir.Angle( newNormals[ iC1 ] ); // [0,PI]
7534               ceCurve._ledges[ iC1 ]->_cosin = Cos( angle );
7535               avgCosin += ceCurve._ledges[ iC1 ]->_cosin;
7536               nbCosin++;
7537             }
7538           }
7539         }
7540       }
7541       // set new normals to _LayerEdge's of NOT degenerated central curves
7542       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
7543       {
7544         if ( centerCurves[ iE ]._isDegenerated )
7545           continue;
7546         for ( size_t iLE = 0; iLE < centerCurves[ iE ]._ledges.size(); ++iLE )
7547         {
7548           centerCurves[ iE ]._ledges[ iLE ]->SetNormal( centerCurves[ iE ]._normals[ iLE ]);
7549           centerCurves[ iE ]._ledges[ iLE ]->Set( _LayerEdge::NORMAL_UPDATED );
7550         }
7551       }
7552       // set new normals to _LayerEdge's of     degenerated central curves
7553       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
7554       {
7555         if ( !centerCurves[ iE ]._isDegenerated ||
7556              centerCurves[ iE ]._ledges.size() < 3 )
7557           continue;
7558         // new normal is an average of new normals at VERTEXes that
7559         // was computed on non-degenerated _CentralCurveOnEdge's
7560         gp_XYZ newNorm = ( centerCurves[ iE ]._ledges.front()->_normal +
7561                            centerCurves[ iE ]._ledges.back ()->_normal );
7562         double sz = newNorm.Modulus();
7563         if ( sz < 1e-200 )
7564           continue;
7565         newNorm /= sz;
7566         double newCosin = ( 0.5 * centerCurves[ iE ]._ledges.front()->_cosin +
7567                             0.5 * centerCurves[ iE ]._ledges.back ()->_cosin );
7568         for ( size_t iLE = 1, nb = centerCurves[ iE ]._ledges.size() - 1; iLE < nb; ++iLE )
7569         {
7570           centerCurves[ iE ]._ledges[ iLE ]->SetNormal( newNorm );
7571           centerCurves[ iE ]._ledges[ iLE ]->_cosin   = newCosin;
7572           centerCurves[ iE ]._ledges[ iLE ]->Set( _LayerEdge::NORMAL_UPDATED );
7573         }
7574       }
7575
7576       // Find new normals for _LayerEdge's based on FACE
7577
7578       if ( nbCosin > 0 )
7579         avgCosin /= nbCosin;
7580       const TGeomID faceID = meshDS->ShapeToIndex( convFace._face );
7581       map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.find( faceID );
7582       if ( id2eos != convFace._subIdToEOS.end() )
7583       {
7584         int iE = 0;
7585         gp_XYZ newNorm;
7586         _EdgesOnShape& eos = * ( id2eos->second );
7587         for ( size_t i = 0; i < eos._edges.size(); ++i )
7588         {
7589           _LayerEdge* ledge = eos._edges[ i ];
7590           if ( !convFace.GetCenterOfCurvature( ledge, surfProp, helper, center ))
7591             continue;
7592           for ( size_t i = 0; i < centerCurves.size(); ++i, ++iE )
7593           {
7594             iE = iE % centerCurves.size();
7595             if ( centerCurves[ iE ]._isDegenerated )
7596               continue;
7597             newNorm.SetCoord( 0,0,0 );
7598             if ( centerCurves[ iE ].FindNewNormal( center, newNorm ))
7599             {
7600               ledge->SetNormal( newNorm );
7601               ledge->_cosin  = avgCosin;
7602               ledge->Set( _LayerEdge::NORMAL_UPDATED );
7603               break;
7604             }
7605           }
7606         }
7607       }
7608
7609     } // not a quasi-spherical FACE
7610
7611     // Update _LayerEdge's data according to a new normal
7612
7613     dumpFunction(SMESH_Comment("updateNormalsOfConvexFaces")<<data._index
7614                  <<"_F"<<meshDS->ShapeToIndex( convFace._face ));
7615
7616     id2eos = convFace._subIdToEOS.begin();
7617     for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7618     {
7619       _EdgesOnShape& eos = * ( id2eos->second );
7620       for ( size_t i = 0; i < eos._edges.size(); ++i )
7621       {
7622         _LayerEdge* & ledge = eos._edges[ i ];
7623         double len = ledge->_len;
7624         ledge->InvalidateStep( stepNb + 1, eos, /*restoreLength=*/true );
7625         ledge->SetCosin( ledge->_cosin );
7626         ledge->SetNewLength( len, eos, helper );
7627       }
7628       if ( eos.ShapeType() != TopAbs_FACE )
7629         for ( size_t i = 0; i < eos._edges.size(); ++i )
7630         {
7631           _LayerEdge* ledge = eos._edges[ i ];
7632           for ( size_t iN = 0; iN < ledge->_neibors.size(); ++iN )
7633           {
7634             _LayerEdge* neibor = ledge->_neibors[iN];
7635             if ( neibor->_nodes[0]->GetPosition()->GetDim() == 2 )
7636             {
7637               neibor->Set( _LayerEdge::NEAR_BOUNDARY );
7638               neibor->Set( _LayerEdge::MOVED );
7639               neibor->SetSmooLen( neibor->_len );
7640             }
7641           }
7642         }
7643     } // loop on sub-shapes of convFace._face
7644
7645     // Find FACEs adjacent to convFace._face that got necessity to smooth
7646     // as a result of normals modification
7647
7648     set< _EdgesOnShape* > adjFacesToSmooth;
7649     for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
7650     {
7651       if ( centerCurves[ iE ]._adjFace.IsNull() ||
7652            centerCurves[ iE ]._adjFaceToSmooth )
7653         continue;
7654       for ( size_t iLE = 0; iLE < centerCurves[ iE ]._ledges.size(); ++iLE )
7655       {
7656         if ( centerCurves[ iE ]._ledges[ iLE ]->_cosin > theMinSmoothCosin )
7657         {
7658           adjFacesToSmooth.insert( data.GetShapeEdges( centerCurves[ iE ]._adjFace ));
7659           break;
7660         }
7661       }
7662     }
7663     data.AddShapesToSmooth( adjFacesToSmooth );
7664
7665     dumpFunctionEnd();
7666
7667
7668   } // loop on data._convexFaces
7669
7670   return true;
7671 }
7672
7673 //================================================================================
7674 /*!
7675  * \brief Return max curvature of a FACE
7676  */
7677 //================================================================================
7678
7679 double _ConvexFace::GetMaxCurvature( _SolidData&         data,
7680                                      _EdgesOnShape&      eof,
7681                                      BRepLProp_SLProps&  surfProp,
7682                                      SMESH_MesherHelper& helper)
7683 {
7684   double maxCurvature = 0;
7685
7686   TopoDS_Face F = TopoDS::Face( eof._shape );
7687
7688   const int           nbTestPnt = 5;
7689   const double        oriFactor = ( F.Orientation() == TopAbs_REVERSED ? +1. : -1. );
7690   SMESH_subMeshIteratorPtr smIt = eof._subMesh->getDependsOnIterator(/*includeSelf=*/true);
7691   while ( smIt->more() )
7692   {
7693     SMESH_subMesh* sm = smIt->next();
7694     const TGeomID subID = sm->GetId();
7695
7696     // find _LayerEdge's of a sub-shape
7697     _EdgesOnShape* eos;
7698     if (( eos = data.GetShapeEdges( subID )))
7699       this->_subIdToEOS.insert( make_pair( subID, eos ));
7700     else
7701       continue;
7702
7703     // check concavity and curvature and limit data._stepSize
7704     const double minCurvature =
7705       1. / ( eos->_hyp.GetTotalThickness() * ( 1 + theThickToIntersection ));
7706     size_t iStep = Max( 1, eos->_edges.size() / nbTestPnt );
7707     for ( size_t i = 0; i < eos->_edges.size(); i += iStep )
7708     {
7709       gp_XY uv = helper.GetNodeUV( F, eos->_edges[ i ]->_nodes[0] );
7710       surfProp.SetParameters( uv.X(), uv.Y() );
7711       if ( surfProp.IsCurvatureDefined() )
7712       {
7713         double curvature = Max( surfProp.MaxCurvature() * oriFactor,
7714                                 surfProp.MinCurvature() * oriFactor );
7715         maxCurvature = Max( maxCurvature, curvature );
7716
7717         if ( curvature > minCurvature )
7718           this->_isTooCurved = true;
7719       }
7720     }
7721   } // loop on sub-shapes of the FACE
7722
7723   return maxCurvature;
7724 }
7725
7726 //================================================================================
7727 /*!
7728  * \brief Finds a center of curvature of a surface at a _LayerEdge
7729  */
7730 //================================================================================
7731
7732 bool _ConvexFace::GetCenterOfCurvature( _LayerEdge*         ledge,
7733                                         BRepLProp_SLProps&  surfProp,
7734                                         SMESH_MesherHelper& helper,
7735                                         gp_Pnt &            center ) const
7736 {
7737   gp_XY uv = helper.GetNodeUV( _face, ledge->_nodes[0] );
7738   surfProp.SetParameters( uv.X(), uv.Y() );
7739   if ( !surfProp.IsCurvatureDefined() )
7740     return false;
7741
7742   const double oriFactor = ( _face.Orientation() == TopAbs_REVERSED ? +1. : -1. );
7743   double surfCurvatureMax = surfProp.MaxCurvature() * oriFactor;
7744   double surfCurvatureMin = surfProp.MinCurvature() * oriFactor;
7745   if ( surfCurvatureMin > surfCurvatureMax )
7746     center = surfProp.Value().Translated( surfProp.Normal().XYZ() / surfCurvatureMin * oriFactor );
7747   else
7748     center = surfProp.Value().Translated( surfProp.Normal().XYZ() / surfCurvatureMax * oriFactor );
7749
7750   return true;
7751 }
7752
7753 //================================================================================
7754 /*!
7755  * \brief Check that prisms are not distorted
7756  */
7757 //================================================================================
7758
7759 bool _ConvexFace::CheckPrisms() const
7760 {
7761   double vol = 0;
7762   for ( size_t i = 0; i < _simplexTestEdges.size(); ++i )
7763   {
7764     const _LayerEdge* edge = _simplexTestEdges[i];
7765     SMESH_TNodeXYZ tgtXYZ( edge->_nodes.back() );
7766     for ( size_t j = 0; j < edge->_simplices.size(); ++j )
7767       if ( !edge->_simplices[j].IsForward( edge->_nodes[0], tgtXYZ, vol ))
7768       {
7769         debugMsg( "Bad simplex of _simplexTestEdges ("
7770                   << " "<< edge->_nodes[0]->GetID()<< " "<< tgtXYZ._node->GetID()
7771                   << " "<< edge->_simplices[j]._nPrev->GetID()
7772                   << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
7773         return false;
7774       }
7775   }
7776   return true;
7777 }
7778
7779 //================================================================================
7780 /*!
7781  * \brief Try to compute a new normal by interpolating normals of _LayerEdge's
7782  *        stored in this _CentralCurveOnEdge.
7783  *  \param [in] center - curvature center of a point of another _CentralCurveOnEdge.
7784  *  \param [in,out] newNormal - current normal at this point, to be redefined
7785  *  \return bool - true if succeeded.
7786  */
7787 //================================================================================
7788
7789 bool _CentralCurveOnEdge::FindNewNormal( const gp_Pnt& center, gp_XYZ& newNormal )
7790 {
7791   if ( this->_isDegenerated )
7792     return false;
7793
7794   // find two centers the given one lies between
7795
7796   for ( size_t i = 0, nb = _curvaCenters.size()-1;  i < nb;  ++i )
7797   {
7798     double sl2 = 1.001 * _segLength2[ i ];
7799
7800     double d1 = center.SquareDistance( _curvaCenters[ i ]);
7801     if ( d1 > sl2 )
7802       continue;
7803     
7804     double d2 = center.SquareDistance( _curvaCenters[ i+1 ]);
7805     if ( d2 > sl2 || d2 + d1 < 1e-100 )
7806       continue;
7807
7808     d1 = Sqrt( d1 );
7809     d2 = Sqrt( d2 );
7810     double r = d1 / ( d1 + d2 );
7811     gp_XYZ norm = (( 1. - r ) * _ledges[ i   ]->_normal +
7812                    (      r ) * _ledges[ i+1 ]->_normal );
7813     norm.Normalize();
7814
7815     newNormal += norm;
7816     double sz = newNormal.Modulus();
7817     if ( sz < 1e-200 )
7818       break;
7819     newNormal /= sz;
7820     return true;
7821   }
7822   return false;
7823 }
7824
7825 //================================================================================
7826 /*!
7827  * \brief Set shape members
7828  */
7829 //================================================================================
7830
7831 void _CentralCurveOnEdge::SetShapes( const TopoDS_Edge&  edge,
7832                                      const _ConvexFace&  convFace,
7833                                      _SolidData&         data,
7834                                      SMESH_MesherHelper& helper)
7835 {
7836   _edge = edge;
7837
7838   PShapeIteratorPtr fIt = helper.GetAncestors( edge, *helper.GetMesh(), TopAbs_FACE );
7839   while ( const TopoDS_Shape* F = fIt->next())
7840     if ( !convFace._face.IsSame( *F ))
7841     {
7842       _adjFace = TopoDS::Face( *F );
7843       _adjFaceToSmooth = false;
7844       // _adjFace already in a smoothing queue ?
7845       if ( _EdgesOnShape* eos = data.GetShapeEdges( _adjFace ))
7846         _adjFaceToSmooth = eos->_toSmooth;
7847       break;
7848     }
7849 }
7850
7851 //================================================================================
7852 /*!
7853  * \brief Looks for intersection of it's last segment with faces
7854  *  \param distance - returns shortest distance from the last node to intersection
7855  */
7856 //================================================================================
7857
7858 bool _LayerEdge::FindIntersection( SMESH_ElementSearcher&   searcher,
7859                                    double &                 distance,
7860                                    const double&            epsilon,
7861                                    _EdgesOnShape&           eos,
7862                                    const SMDS_MeshElement** intFace)
7863 {
7864   vector< const SMDS_MeshElement* > suspectFaces;
7865   double segLen;
7866   gp_Ax1 lastSegment = LastSegment( segLen, eos );
7867   searcher.GetElementsNearLine( lastSegment, SMDSAbs_Face, suspectFaces );
7868
7869   bool segmentIntersected = false;
7870   distance = Precision::Infinite();
7871   int iFace = -1; // intersected face
7872   for ( size_t j = 0 ; j < suspectFaces.size() /*&& !segmentIntersected*/; ++j )
7873   {
7874     const SMDS_MeshElement* face = suspectFaces[j];
7875     if ( face->GetNodeIndex( _nodes.back() ) >= 0 ||
7876          face->GetNodeIndex( _nodes[0]     ) >= 0 )
7877       continue; // face sharing _LayerEdge node
7878     const int nbNodes = face->NbCornerNodes();
7879     bool intFound = false;
7880     double dist;
7881     SMDS_MeshElement::iterator nIt = face->begin_nodes();
7882     if ( nbNodes == 3 )
7883     {
7884       intFound = SegTriaInter( lastSegment, *nIt++, *nIt++, *nIt++, dist, epsilon );
7885     }
7886     else
7887     {
7888       const SMDS_MeshNode* tria[3];
7889       tria[0] = *nIt++;
7890       tria[1] = *nIt++;
7891       for ( int n2 = 2; n2 < nbNodes && !intFound; ++n2 )
7892       {
7893         tria[2] = *nIt++;
7894         intFound = SegTriaInter(lastSegment, tria[0], tria[1], tria[2], dist, epsilon );
7895         tria[1] = tria[2];
7896       }
7897     }
7898     if ( intFound )
7899     {
7900       if ( dist < segLen*(1.01) && dist > -(_len*_lenFactor-segLen) )
7901         segmentIntersected = true;
7902       if ( distance > dist )
7903         distance = dist, iFace = j;
7904     }
7905   }
7906   if ( intFace ) *intFace = ( iFace != -1 ) ? suspectFaces[iFace] : 0;
7907
7908   distance -= segLen;
7909
7910   if ( segmentIntersected )
7911   {
7912 #ifdef __myDEBUG
7913     SMDS_MeshElement::iterator nIt = suspectFaces[iFace]->begin_nodes();
7914     gp_XYZ intP( lastSegment.Location().XYZ() + lastSegment.Direction().XYZ() * ( distance+segLen ));
7915     cout << "nodes: tgt " << _nodes.back()->GetID() << " src " << _nodes[0]->GetID()
7916          << ", intersection with face ("
7917          << (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()
7918          << ") at point (" << intP.X() << ", " << intP.Y() << ", " << intP.Z()
7919          << ") distance = " << distance << endl;
7920 #endif
7921   }
7922
7923   return segmentIntersected;
7924 }
7925
7926 //================================================================================
7927 /*!
7928  * \brief Returns a point used to check orientation of _simplices
7929  */
7930 //================================================================================
7931
7932 gp_XYZ _LayerEdge::PrevCheckPos( _EdgesOnShape* eos ) const
7933 {
7934   size_t i = Is( NORMAL_UPDATED ) && IsOnFace() ? _pos.size()-2 : 0;
7935
7936   if ( !eos || eos->_sWOL.IsNull() )
7937     return _pos[ i ];
7938
7939   if ( eos->SWOLType() == TopAbs_EDGE )
7940   {
7941     return BRepAdaptor_Curve( TopoDS::Edge( eos->_sWOL )).Value( _pos[i].X() ).XYZ();
7942   }
7943   //else //  TopAbs_FACE
7944
7945   return BRepAdaptor_Surface( TopoDS::Face( eos->_sWOL )).Value(_pos[i].X(), _pos[i].Y() ).XYZ();
7946 }
7947
7948 //================================================================================
7949 /*!
7950  * \brief Returns size and direction of the last segment
7951  */
7952 //================================================================================
7953
7954 gp_Ax1 _LayerEdge::LastSegment(double& segLen, _EdgesOnShape& eos) const
7955 {
7956   // find two non-coincident positions
7957   gp_XYZ orig = _pos.back();
7958   gp_XYZ vec;
7959   int iPrev = _pos.size() - 2;
7960   //const double tol = ( _len > 0 ) ? 0.3*_len : 1e-100; // adjusted for IPAL52478 + PAL22576
7961   const double tol = ( _len > 0 ) ? ( 1e-6 * _len ) : 1e-100;
7962   while ( iPrev >= 0 )
7963   {
7964     vec = orig - _pos[iPrev];
7965     if ( vec.SquareModulus() > tol*tol )
7966       break;
7967     else
7968       iPrev--;
7969   }
7970
7971   // make gp_Ax1
7972   gp_Ax1 segDir;
7973   if ( iPrev < 0 )
7974   {
7975     segDir.SetLocation( SMESH_TNodeXYZ( _nodes[0] ));
7976     segDir.SetDirection( _normal );
7977     segLen = 0;
7978   }
7979   else
7980   {
7981     gp_Pnt pPrev = _pos[ iPrev ];
7982     if ( !eos._sWOL.IsNull() )
7983     {
7984       TopLoc_Location loc;
7985       if ( eos.SWOLType() == TopAbs_EDGE )
7986       {
7987         double f,l;
7988         Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( eos._sWOL ), loc, f,l);
7989         pPrev = curve->Value( pPrev.X() ).Transformed( loc );
7990       }
7991       else
7992       {
7993         Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face( eos._sWOL ), loc );
7994         pPrev = surface->Value( pPrev.X(), pPrev.Y() ).Transformed( loc );
7995       }
7996       vec = SMESH_TNodeXYZ( _nodes.back() ) - pPrev.XYZ();
7997     }
7998     segDir.SetLocation( pPrev );
7999     segDir.SetDirection( vec );
8000     segLen = vec.Modulus();
8001   }
8002
8003   return segDir;
8004 }
8005
8006 //================================================================================
8007 /*!
8008  * \brief Return the last (or \a which) position of the target node on a FACE. 
8009  *  \param [in] F - the FACE this _LayerEdge is inflated along
8010  *  \param [in] which - index of position
8011  *  \return gp_XY - result UV
8012  */
8013 //================================================================================
8014
8015 gp_XY _LayerEdge::LastUV( const TopoDS_Face& F, _EdgesOnShape& eos, int which ) const
8016 {
8017   if ( F.IsSame( eos._sWOL )) // F is my FACE
8018     return gp_XY( _pos.back().X(), _pos.back().Y() );
8019
8020   if ( eos.SWOLType() != TopAbs_EDGE ) // wrong call
8021     return gp_XY( 1e100, 1e100 );
8022
8023   // _sWOL is EDGE of F; _pos.back().X() is the last U on the EDGE
8024   double f, l, u = _pos[ which < 0 ? _pos.size()-1 : which ].X();
8025   Handle(Geom2d_Curve) C2d = BRep_Tool::CurveOnSurface( TopoDS::Edge(eos._sWOL), F, f,l);
8026   if ( !C2d.IsNull() && f <= u && u <= l )
8027     return C2d->Value( u ).XY();
8028
8029   return gp_XY( 1e100, 1e100 );
8030 }
8031
8032 //================================================================================
8033 /*!
8034  * \brief Test intersection of the last segment with a given triangle
8035  *   using Moller-Trumbore algorithm
8036  * Intersection is detected if distance to intersection is less than _LayerEdge._len
8037  */
8038 //================================================================================
8039
8040 bool _LayerEdge::SegTriaInter( const gp_Ax1& lastSegment,
8041                                const gp_XYZ& vert0,
8042                                const gp_XYZ& vert1,
8043                                const gp_XYZ& vert2,
8044                                double&       t,
8045                                const double& EPSILON) const
8046 {
8047   const gp_Pnt& orig = lastSegment.Location();
8048   const gp_Dir& dir  = lastSegment.Direction();
8049
8050   /* calculate distance from vert0 to ray origin */
8051   //gp_XYZ tvec = orig.XYZ() - vert0;
8052
8053   //if ( tvec * dir > EPSILON )
8054     // intersected face is at back side of the temporary face this _LayerEdge belongs to
8055     //return false;
8056
8057   gp_XYZ edge1 = vert1 - vert0;
8058   gp_XYZ edge2 = vert2 - vert0;
8059
8060   /* begin calculating determinant - also used to calculate U parameter */
8061   gp_XYZ pvec = dir.XYZ() ^ edge2;
8062
8063   /* if determinant is near zero, ray lies in plane of triangle */
8064   double det = edge1 * pvec;
8065
8066   const double ANGL_EPSILON = 1e-12;
8067   if ( det > -ANGL_EPSILON && det < ANGL_EPSILON )
8068     return false;
8069
8070   /* calculate distance from vert0 to ray origin */
8071   gp_XYZ tvec = orig.XYZ() - vert0;
8072
8073   /* calculate U parameter and test bounds */
8074   double u = ( tvec * pvec ) / det;
8075   //if (u < 0.0 || u > 1.0)
8076   if ( u < -EPSILON || u > 1.0 + EPSILON )
8077     return false;
8078
8079   /* prepare to test V parameter */
8080   gp_XYZ qvec = tvec ^ edge1;
8081
8082   /* calculate V parameter and test bounds */
8083   double v = (dir.XYZ() * qvec) / det;
8084   //if ( v < 0.0 || u + v > 1.0 )
8085   if ( v < -EPSILON || u + v > 1.0 + EPSILON )
8086     return false;
8087
8088   /* calculate t, ray intersects triangle */
8089   t = (edge2 * qvec) / det;
8090
8091   //return true;
8092   return t > 0.;
8093 }
8094
8095 //================================================================================
8096 /*!
8097  * \brief _LayerEdge, located at a concave VERTEX of a FACE, moves target nodes of
8098  *        neighbor _LayerEdge's by it's own inflation vector.
8099  *  \param [in] eov - EOS of the VERTEX
8100  *  \param [in] eos - EOS of the FACE
8101  *  \param [in] step - inflation step
8102  *  \param [in,out] badSmooEdges - tangled _LayerEdge's
8103  */
8104 //================================================================================
8105
8106 void _LayerEdge::MoveNearConcaVer( const _EdgesOnShape*    eov,
8107                                    const _EdgesOnShape*    eos,
8108                                    const int               step,
8109                                    vector< _LayerEdge* > & badSmooEdges )
8110 {
8111   // check if any of _neibors is in badSmooEdges
8112   if ( std::find_first_of( _neibors.begin(), _neibors.end(),
8113                            badSmooEdges.begin(), badSmooEdges.end() ) == _neibors.end() )
8114     return;
8115
8116   // get all edges to move
8117
8118   set< _LayerEdge* > edges;
8119
8120   // find a distance between _LayerEdge on VERTEX and its neighbors
8121   gp_XYZ  curPosV = SMESH_TNodeXYZ( _nodes.back() );
8122   double dist2 = 0;
8123   for ( size_t i = 0; i < _neibors.size(); ++i )
8124   {
8125     _LayerEdge* nEdge = _neibors[i];
8126     if ( nEdge->_nodes[0]->getshapeId() == eos->_shapeID )
8127     {
8128       edges.insert( nEdge );
8129       dist2 = Max( dist2, ( curPosV - nEdge->_pos.back() ).SquareModulus() );
8130     }
8131   }
8132   // add _LayerEdge's close to curPosV
8133   size_t nbE;
8134   do {
8135     nbE = edges.size();
8136     for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8137     {
8138       _LayerEdge* edgeF = *e;
8139       for ( size_t i = 0; i < edgeF->_neibors.size(); ++i )
8140       {
8141         _LayerEdge* nEdge = edgeF->_neibors[i];
8142         if ( nEdge->_nodes[0]->getshapeId() == eos->_shapeID &&
8143              dist2 > ( curPosV - nEdge->_pos.back() ).SquareModulus() )
8144           edges.insert( nEdge );
8145       }
8146     }
8147   }
8148   while ( nbE < edges.size() );
8149
8150   // move the target node of the got edges
8151
8152   gp_XYZ prevPosV = PrevPos();
8153   if ( eov->SWOLType() == TopAbs_EDGE )
8154   {
8155     BRepAdaptor_Curve curve ( TopoDS::Edge( eov->_sWOL ));
8156     prevPosV = curve.Value( prevPosV.X() ).XYZ();
8157   }
8158   else if ( eov->SWOLType() == TopAbs_FACE )
8159   {
8160     BRepAdaptor_Surface surface( TopoDS::Face( eov->_sWOL ));
8161     prevPosV = surface.Value( prevPosV.X(), prevPosV.Y() ).XYZ();
8162   }
8163
8164   SMDS_FacePosition* fPos;
8165   //double r = 1. - Min( 0.9, step / 10. );
8166   for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8167   {
8168     _LayerEdge*       edgeF = *e;
8169     const gp_XYZ     prevVF = edgeF->PrevPos() - prevPosV;
8170     const gp_XYZ    newPosF = curPosV + prevVF;
8171     SMDS_MeshNode* tgtNodeF = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
8172     tgtNodeF->setXYZ( newPosF.X(), newPosF.Y(), newPosF.Z() );
8173     edgeF->_pos.back() = newPosF;
8174     dumpMoveComm( tgtNodeF, "MoveNearConcaVer" ); // debug
8175
8176     // set _curvature to make edgeF updated by putOnOffsetSurface()
8177     if ( !edgeF->_curvature )
8178       if (( fPos = dynamic_cast<SMDS_FacePosition*>( edgeF->_nodes[0]->GetPosition() )))
8179       {
8180         edgeF->_curvature = new _Curvature;
8181         edgeF->_curvature->_r = 0;
8182         edgeF->_curvature->_k = 0;
8183         edgeF->_curvature->_h2lenRatio = 0;
8184         edgeF->_curvature->_uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
8185       }
8186   }
8187   // gp_XYZ inflationVec( SMESH_TNodeXYZ( _nodes.back() ) -
8188   //                      SMESH_TNodeXYZ( _nodes[0]    ));
8189   // for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8190   // {
8191   //   _LayerEdge*      edgeF = *e;
8192   //   gp_XYZ          newPos = SMESH_TNodeXYZ( edgeF->_nodes[0] ) + inflationVec;
8193   //   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
8194   //   tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8195   //   edgeF->_pos.back() = newPosF;
8196   //   dumpMoveComm( tgtNode, "MoveNearConcaVer" ); // debug
8197   // }
8198
8199   // smooth _LayerEdge's around moved nodes
8200   //size_t nbBadBefore = badSmooEdges.size();
8201   for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8202   {
8203     _LayerEdge* edgeF = *e;
8204     for ( size_t j = 0; j < edgeF->_neibors.size(); ++j )
8205       if ( edgeF->_neibors[j]->_nodes[0]->getshapeId() == eos->_shapeID )
8206         //&& !edges.count( edgeF->_neibors[j] ))
8207       {
8208         _LayerEdge* edgeFN = edgeF->_neibors[j];
8209         edgeFN->Unset( SMOOTHED );
8210         int nbBad = edgeFN->Smooth( step, /*isConcaFace=*/true, /*findBest=*/true );
8211         // if ( nbBad > 0 )
8212         // {
8213         //   gp_XYZ         newPos = SMESH_TNodeXYZ( edgeFN->_nodes[0] ) + inflationVec;
8214         //   const gp_XYZ& prevPos = edgeFN->_pos[ edgeFN->_pos.size()-2 ];
8215         //   int        nbBadAfter = edgeFN->_simplices.size();
8216         //   double vol;
8217         //   for ( size_t iS = 0; iS < edgeFN->_simplices.size(); ++iS )
8218         //   {
8219         //     nbBadAfter -= edgeFN->_simplices[iS].IsForward( &prevPos, &newPos, vol );
8220         //   }
8221         //   if ( nbBadAfter <= nbBad )
8222         //   {
8223         //     SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeFN->_nodes.back() );
8224         //     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8225         //     edgeF->_pos.back() = newPosF;
8226         //     dumpMoveComm( tgtNode, "MoveNearConcaVer 2" ); // debug
8227         //     nbBad = nbBadAfter;
8228         //   }
8229         // }
8230         if ( nbBad > 0 )
8231           badSmooEdges.push_back( edgeFN );
8232       }
8233   }
8234     // move a bit not smoothed around moved nodes
8235   //   for ( size_t i = nbBadBefore; i < badSmooEdges.size(); ++i )
8236   //   {
8237   //   _LayerEdge*      edgeF = badSmooEdges[i];
8238   //   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
8239   //   gp_XYZ         newPos1 = SMESH_TNodeXYZ( edgeF->_nodes[0] ) + inflationVec;
8240   //   gp_XYZ         newPos2 = 0.5 * ( newPos1 + SMESH_TNodeXYZ( tgtNode ));
8241   //   tgtNode->setXYZ( newPos2.X(), newPos2.Y(), newPos2.Z() );
8242   //   edgeF->_pos.back() = newPosF;
8243   //   dumpMoveComm( tgtNode, "MoveNearConcaVer 2" ); // debug
8244   // }
8245 }
8246
8247 //================================================================================
8248 /*!
8249  * \brief Perform smooth of _LayerEdge's based on EDGE's
8250  *  \retval bool - true if node has been moved
8251  */
8252 //================================================================================
8253
8254 bool _LayerEdge::SmoothOnEdge(Handle(ShapeAnalysis_Surface)& surface,
8255                               const TopoDS_Face&             F,
8256                               SMESH_MesherHelper&            helper)
8257 {
8258   ASSERT( IsOnEdge() );
8259
8260   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _nodes.back() );
8261   SMESH_TNodeXYZ oldPos( tgtNode );
8262   double dist01, distNewOld;
8263   
8264   SMESH_TNodeXYZ p0( _2neibors->tgtNode(0));
8265   SMESH_TNodeXYZ p1( _2neibors->tgtNode(1));
8266   dist01 = p0.Distance( _2neibors->tgtNode(1) );
8267
8268   gp_Pnt newPos = p0 * _2neibors->_wgt[0] + p1 * _2neibors->_wgt[1];
8269   double lenDelta = 0;
8270   if ( _curvature )
8271   {
8272     //lenDelta = _curvature->lenDelta( _len );
8273     lenDelta = _curvature->lenDeltaByDist( dist01 );
8274     newPos.ChangeCoord() += _normal * lenDelta;
8275   }
8276
8277   distNewOld = newPos.Distance( oldPos );
8278
8279   if ( F.IsNull() )
8280   {
8281     if ( _2neibors->_plnNorm )
8282     {
8283       // put newPos on the plane defined by source node and _plnNorm
8284       gp_XYZ new2src = SMESH_TNodeXYZ( _nodes[0] ) - newPos.XYZ();
8285       double new2srcProj = (*_2neibors->_plnNorm) * new2src;
8286       newPos.ChangeCoord() += (*_2neibors->_plnNorm) * new2srcProj;
8287     }
8288     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8289     _pos.back() = newPos.XYZ();
8290   }
8291   else
8292   {
8293     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8294     gp_XY uv( Precision::Infinite(), 0 );
8295     helper.CheckNodeUV( F, tgtNode, uv, 1e-10, /*force=*/true );
8296     _pos.back().SetCoord( uv.X(), uv.Y(), 0 );
8297
8298     newPos = surface->Value( uv );
8299     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8300   }
8301
8302   // commented for IPAL0052478
8303   // if ( _curvature && lenDelta < 0 )
8304   // {
8305   //   gp_Pnt prevPos( _pos[ _pos.size()-2 ]);
8306   //   _len -= prevPos.Distance( oldPos );
8307   //   _len += prevPos.Distance( newPos );
8308   // }
8309   bool moved = distNewOld > dist01/50;
8310   //if ( moved )
8311   dumpMove( tgtNode ); // debug
8312
8313   return moved;
8314 }
8315
8316 //================================================================================
8317 /*!
8318  * \brief Perform 3D smooth of nodes inflated from FACE. No check of validity
8319  */
8320 //================================================================================
8321
8322 void _LayerEdge::SmoothWoCheck()
8323 {
8324   if ( Is( DIFFICULT ))
8325     return;
8326
8327   bool moved = Is( SMOOTHED );
8328   for ( size_t i = 0; i < _neibors.size()  &&  !moved; ++i )
8329     moved = _neibors[i]->Is( SMOOTHED );
8330   if ( !moved )
8331     return;
8332
8333   gp_XYZ newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
8334
8335   SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
8336   n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
8337   _pos.back() = newPos;
8338
8339   dumpMoveComm( n, SMESH_Comment("No check - ") << _funNames[ smooFunID() ]);
8340 }
8341
8342 //================================================================================
8343 /*!
8344  * \brief Checks validity of _neibors on EDGEs and VERTEXes
8345  */
8346 //================================================================================
8347
8348 int _LayerEdge::CheckNeiborsOnBoundary( vector< _LayerEdge* >* badNeibors, bool * needSmooth )
8349 {
8350   if ( ! Is( NEAR_BOUNDARY ))
8351     return 0;
8352
8353   int nbBad = 0;
8354   double vol;
8355   for ( size_t iN = 0; iN < _neibors.size(); ++iN )
8356   {
8357     _LayerEdge* eN = _neibors[iN];
8358     if ( eN->_nodes[0]->getshapeId() == _nodes[0]->getshapeId() )
8359       continue;
8360     if ( needSmooth )
8361       *needSmooth |= ( eN->Is( _LayerEdge::BLOCKED ) ||
8362                        eN->Is( _LayerEdge::NORMAL_UPDATED ) ||
8363                        eN->_pos.size() != _pos.size() );
8364
8365     SMESH_TNodeXYZ curPosN ( eN->_nodes.back() );
8366     SMESH_TNodeXYZ prevPosN( eN->_nodes[0] );
8367     for ( size_t i = 0; i < eN->_simplices.size(); ++i )
8368       if ( eN->_nodes.size() > 1 &&
8369            eN->_simplices[i].Includes( _nodes.back() ) &&
8370            !eN->_simplices[i].IsForward( &prevPosN, &curPosN, vol ))
8371       {
8372         ++nbBad;
8373         if ( badNeibors )
8374         {
8375           badNeibors->push_back( eN );
8376           debugMsg("Bad boundary simplex ( "
8377                    << " "<< eN->_nodes[0]->GetID()
8378                    << " "<< eN->_nodes.back()->GetID()
8379                    << " "<< eN->_simplices[i]._nPrev->GetID()
8380                    << " "<< eN->_simplices[i]._nNext->GetID() << " )" );
8381         }
8382         else
8383         {
8384           break;
8385         }
8386       }
8387   }
8388   return nbBad;
8389 }
8390
8391 //================================================================================
8392 /*!
8393  * \brief Perform 'smart' 3D smooth of nodes inflated from FACE
8394  *  \retval int - nb of bad simplices around this _LayerEdge
8395  */
8396 //================================================================================
8397
8398 int _LayerEdge::Smooth(const int step, bool findBest, vector< _LayerEdge* >& toSmooth )
8399 {
8400   if ( !Is( MOVED ) || Is( SMOOTHED ) || Is( BLOCKED ))
8401     return 0; // shape of simplices not changed
8402   if ( _simplices.size() < 2 )
8403     return 0; // _LayerEdge inflated along EDGE or FACE
8404
8405   if ( Is( DIFFICULT )) // || Is( ON_CONCAVE_FACE )
8406     findBest = true;
8407
8408   const gp_XYZ& curPos  = _pos.back();
8409   const gp_XYZ& prevPos = _pos[0]; //PrevPos();
8410
8411   // quality metrics (orientation) of tetras around _tgtNode
8412   int nbOkBefore = 0;
8413   double vol, minVolBefore = 1e100;
8414   for ( size_t i = 0; i < _simplices.size(); ++i )
8415   {
8416     nbOkBefore += _simplices[i].IsForward( &prevPos, &curPos, vol );
8417     minVolBefore = Min( minVolBefore, vol );
8418   }
8419   int nbBad = _simplices.size() - nbOkBefore;
8420
8421   bool bndNeedSmooth = false;
8422   if ( nbBad == 0 )
8423     nbBad = CheckNeiborsOnBoundary( 0, & bndNeedSmooth );
8424   if ( nbBad > 0 )
8425     Set( DISTORTED );
8426
8427   // evaluate min angle
8428   if ( nbBad == 0 && !findBest && !bndNeedSmooth )
8429   {
8430     size_t nbGoodAngles = _simplices.size();
8431     double angle;
8432     for ( size_t i = 0; i < _simplices.size(); ++i )
8433     {
8434       if ( !_simplices[i].IsMinAngleOK( curPos, angle ) && angle > _minAngle )
8435         --nbGoodAngles;
8436     }
8437     if ( nbGoodAngles == _simplices.size() )
8438     {
8439       Unset( MOVED );
8440       return 0;
8441     }
8442   }
8443   if ( Is( ON_CONCAVE_FACE ))
8444     findBest = true;
8445
8446   if ( step % 2 == 0 )
8447     findBest = false;
8448
8449   if ( Is( ON_CONCAVE_FACE ) && !findBest ) // alternate FUN_CENTROIDAL and FUN_LAPLACIAN
8450   {
8451     if ( _smooFunction == _funs[ FUN_LAPLACIAN ] )
8452       _smooFunction = _funs[ FUN_CENTROIDAL ];
8453     else
8454       _smooFunction = _funs[ FUN_LAPLACIAN ];
8455   }
8456
8457   // compute new position for the last _pos using different _funs
8458   gp_XYZ newPos;
8459   bool moved = false;
8460   for ( int iFun = -1; iFun < theNbSmooFuns; ++iFun )
8461   {
8462     if ( iFun < 0 )
8463       newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
8464     else if ( _funs[ iFun ] == _smooFunction )
8465       continue; // _smooFunction again
8466     else if ( step > 1 )
8467       newPos = (this->*_funs[ iFun ])(); // try other smoothing fun
8468     else
8469       break; // let "easy" functions improve elements around distorted ones
8470
8471     if ( _curvature )
8472     {
8473       double delta  = _curvature->lenDelta( _len );
8474       if ( delta > 0 )
8475         newPos += _normal * delta;
8476       else
8477       {
8478         double segLen = _normal * ( newPos - prevPos );
8479         if ( segLen + delta > 0 )
8480           newPos += _normal * delta;
8481       }
8482       // double segLenChange = _normal * ( curPos - newPos );
8483       // newPos += 0.5 * _normal * segLenChange;
8484     }
8485
8486     int nbOkAfter = 0;
8487     double minVolAfter = 1e100;
8488     for ( size_t i = 0; i < _simplices.size(); ++i )
8489     {
8490       nbOkAfter += _simplices[i].IsForward( &prevPos, &newPos, vol );
8491       minVolAfter = Min( minVolAfter, vol );
8492     }
8493     // get worse?
8494     if ( nbOkAfter < nbOkBefore )
8495       continue;
8496
8497     if (( findBest ) &&
8498         ( nbOkAfter == nbOkBefore ) &&
8499         ( minVolAfter <= minVolBefore ))
8500       continue;
8501
8502     nbBad        = _simplices.size() - nbOkAfter;
8503     minVolBefore = minVolAfter;
8504     nbOkBefore   = nbOkAfter;
8505     moved        = true;
8506
8507     SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
8508     n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
8509     _pos.back() = newPos;
8510
8511     dumpMoveComm( n, SMESH_Comment( _funNames[ iFun < 0 ? smooFunID() : iFun ] )
8512                   << (nbBad ? " --BAD" : ""));
8513
8514     if ( iFun > -1 )
8515     {
8516       continue; // look for a better function
8517     }
8518
8519     if ( !findBest )
8520       break;
8521
8522   } // loop on smoothing functions
8523
8524   if ( moved ) // notify _neibors
8525   {
8526     Set( SMOOTHED );
8527     for ( size_t i = 0; i < _neibors.size(); ++i )
8528       if ( !_neibors[i]->Is( MOVED ))
8529       {
8530         _neibors[i]->Set( MOVED );
8531         toSmooth.push_back( _neibors[i] );
8532       }
8533   }
8534
8535   return nbBad;
8536 }
8537
8538 //================================================================================
8539 /*!
8540  * \brief Perform 'smart' 3D smooth of nodes inflated from FACE
8541  *  \retval int - nb of bad simplices around this _LayerEdge
8542  */
8543 //================================================================================
8544
8545 int _LayerEdge::Smooth(const int step, const bool isConcaveFace, bool findBest )
8546 {
8547   if ( !_smooFunction )
8548     return 0; // _LayerEdge inflated along EDGE or FACE
8549   if ( Is( BLOCKED ))
8550     return 0; // not inflated
8551
8552   const gp_XYZ& curPos  = _pos.back();
8553   const gp_XYZ& prevPos = _pos[0]; //PrevCheckPos();
8554
8555   // quality metrics (orientation) of tetras around _tgtNode
8556   int nbOkBefore = 0;
8557   double vol, minVolBefore = 1e100;
8558   for ( size_t i = 0; i < _simplices.size(); ++i )
8559   {
8560     nbOkBefore += _simplices[i].IsForward( &prevPos, &curPos, vol );
8561     minVolBefore = Min( minVolBefore, vol );
8562   }
8563   int nbBad = _simplices.size() - nbOkBefore;
8564
8565   if ( isConcaveFace ) // alternate FUN_CENTROIDAL and FUN_LAPLACIAN
8566   {
8567     if      ( _smooFunction == _funs[ FUN_CENTROIDAL ] && step % 2 )
8568       _smooFunction = _funs[ FUN_LAPLACIAN ];
8569     else if ( _smooFunction == _funs[ FUN_LAPLACIAN ] && !( step % 2 ))
8570       _smooFunction = _funs[ FUN_CENTROIDAL ];
8571   }
8572
8573   // compute new position for the last _pos using different _funs
8574   gp_XYZ newPos;
8575   for ( int iFun = -1; iFun < theNbSmooFuns; ++iFun )
8576   {
8577     if ( iFun < 0 )
8578       newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
8579     else if ( _funs[ iFun ] == _smooFunction )
8580       continue; // _smooFunction again
8581     else if ( step > 1 )
8582       newPos = (this->*_funs[ iFun ])(); // try other smoothing fun
8583     else
8584       break; // let "easy" functions improve elements around distorted ones
8585
8586     if ( _curvature )
8587     {
8588       double delta  = _curvature->lenDelta( _len );
8589       if ( delta > 0 )
8590         newPos += _normal * delta;
8591       else
8592       {
8593         double segLen = _normal * ( newPos - prevPos );
8594         if ( segLen + delta > 0 )
8595           newPos += _normal * delta;
8596       }
8597       // double segLenChange = _normal * ( curPos - newPos );
8598       // newPos += 0.5 * _normal * segLenChange;
8599     }
8600
8601     int nbOkAfter = 0;
8602     double minVolAfter = 1e100;
8603     for ( size_t i = 0; i < _simplices.size(); ++i )
8604     {
8605       nbOkAfter += _simplices[i].IsForward( &prevPos, &newPos, vol );
8606       minVolAfter = Min( minVolAfter, vol );
8607     }
8608     // get worse?
8609     if ( nbOkAfter < nbOkBefore )
8610       continue;
8611     if (( isConcaveFace || findBest ) &&
8612         ( nbOkAfter == nbOkBefore ) &&
8613         ( minVolAfter <= minVolBefore )
8614         )
8615       continue;
8616
8617     nbBad        = _simplices.size() - nbOkAfter;
8618     minVolBefore = minVolAfter;
8619     nbOkBefore   = nbOkAfter;
8620
8621     SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
8622     n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
8623     _pos.back() = newPos;
8624
8625     dumpMoveComm( n, SMESH_Comment( _funNames[ iFun < 0 ? smooFunID() : iFun ] )
8626                   << ( nbBad ? "--BAD" : ""));
8627
8628     // commented for IPAL0052478
8629     // _len -= prevPos.Distance(SMESH_TNodeXYZ( n ));
8630     // _len += prevPos.Distance(newPos);
8631
8632     if ( iFun > -1 ) // findBest || the chosen _fun makes worse
8633     {
8634       //_smooFunction = _funs[ iFun ];
8635       // cout << "# " << _funNames[ iFun ] << "\t N:" << _nodes.back()->GetID()
8636       // << "\t nbBad: " << _simplices.size() - nbOkAfter
8637       // << " minVol: " << minVolAfter
8638       // << " " << newPos.X() << " " << newPos.Y() << " " << newPos.Z()
8639       // << endl;
8640       continue; // look for a better function
8641     }
8642
8643     if ( !findBest )
8644       break;
8645
8646   } // loop on smoothing functions
8647
8648   return nbBad;
8649 }
8650
8651 //================================================================================
8652 /*!
8653  * \brief Chooses a smoothing technic giving a position most close to an initial one.
8654  *        For a correct result, _simplices must contain nodes lying on geometry.
8655  */
8656 //================================================================================
8657
8658 void _LayerEdge::ChooseSmooFunction( const set< TGeomID >& concaveVertices,
8659                                      const TNode2Edge&     n2eMap)
8660 {
8661   if ( _smooFunction ) return;
8662
8663   // use smoothNefPolygon() near concaveVertices
8664   if ( !concaveVertices.empty() )
8665   {
8666     _smooFunction = _funs[ FUN_CENTROIDAL ];
8667
8668     Set( ON_CONCAVE_FACE );
8669
8670     for ( size_t i = 0; i < _simplices.size(); ++i )
8671     {
8672       if ( concaveVertices.count( _simplices[i]._nPrev->getshapeId() ))
8673       {
8674         _smooFunction = _funs[ FUN_NEFPOLY ];
8675
8676         // set FUN_CENTROIDAL to neighbor edges
8677         for ( i = 0; i < _neibors.size(); ++i )
8678         {
8679           if ( _neibors[i]->_nodes[0]->GetPosition()->GetDim() == 2 )
8680           {
8681             _neibors[i]->_smooFunction = _funs[ FUN_CENTROIDAL ];
8682           }
8683         }
8684         return;
8685       }
8686     }
8687
8688     // // this coice is done only if ( !concaveVertices.empty() ) for Grids/smesh/bugs_19/X1
8689     // // where the nodes are smoothed too far along a sphere thus creating
8690     // // inverted _simplices
8691     // double dist[theNbSmooFuns];
8692     // //double coef[theNbSmooFuns] = { 1., 1.2, 1.4, 1.4 };
8693     // double coef[theNbSmooFuns] = { 1., 1., 1., 1. };
8694
8695     // double minDist = Precision::Infinite();
8696     // gp_Pnt p = SMESH_TNodeXYZ( _nodes[0] );
8697     // for ( int i = 0; i < FUN_NEFPOLY; ++i )
8698     // {
8699     //   gp_Pnt newP = (this->*_funs[i])();
8700     //   dist[i] = p.SquareDistance( newP );
8701     //   if ( dist[i]*coef[i] < minDist )
8702     //   {
8703     //     _smooFunction = _funs[i];
8704     //     minDist = dist[i]*coef[i];
8705     //   }
8706     // }
8707   }
8708   else
8709   {
8710     _smooFunction = _funs[ FUN_LAPLACIAN ];
8711   }
8712   // int minDim = 3;
8713   // for ( size_t i = 0; i < _simplices.size(); ++i )
8714   //   minDim = Min( minDim, _simplices[i]._nPrev->GetPosition()->GetDim() );
8715   // if ( minDim == 0 )
8716   //   _smooFunction = _funs[ FUN_CENTROIDAL ];
8717   // else if ( minDim == 1 )
8718   //   _smooFunction = _funs[ FUN_CENTROIDAL ];
8719
8720
8721   // int iMin;
8722   // for ( int i = 0; i < FUN_NB; ++i )
8723   // {
8724   //   //cout << dist[i] << " ";
8725   //   if ( _smooFunction == _funs[i] ) {
8726   //     iMin = i;
8727   //     //debugMsg( fNames[i] );
8728   //     break;
8729   //   }
8730   // }
8731   // cout << _funNames[ iMin ] << "\t N:" << _nodes.back()->GetID() << endl;
8732 }
8733
8734 //================================================================================
8735 /*!
8736  * \brief Returns a name of _SmooFunction
8737  */
8738 //================================================================================
8739
8740 int _LayerEdge::smooFunID( _LayerEdge::PSmooFun fun) const
8741 {
8742   if ( !fun )
8743     fun = _smooFunction;
8744   for ( int i = 0; i < theNbSmooFuns; ++i )
8745     if ( fun == _funs[i] )
8746       return i;
8747
8748   return theNbSmooFuns;
8749 }
8750
8751 //================================================================================
8752 /*!
8753  * \brief Computes a new node position using Laplacian smoothing
8754  */
8755 //================================================================================
8756
8757 gp_XYZ _LayerEdge::smoothLaplacian()
8758 {
8759   gp_XYZ newPos (0,0,0);
8760   for ( size_t i = 0; i < _simplices.size(); ++i )
8761     newPos += SMESH_TNodeXYZ( _simplices[i]._nPrev );
8762   newPos /= _simplices.size();
8763
8764   return newPos;
8765 }
8766
8767 //================================================================================
8768 /*!
8769  * \brief Computes a new node position using angular-based smoothing
8770  */
8771 //================================================================================
8772
8773 gp_XYZ _LayerEdge::smoothAngular()
8774 {
8775   vector< gp_Vec > edgeDir;  edgeDir. reserve( _simplices.size() + 1 );
8776   vector< double > edgeSize; edgeSize.reserve( _simplices.size()     );
8777   vector< gp_XYZ > points;   points.  reserve( _simplices.size() + 1 );
8778
8779   gp_XYZ pPrev = SMESH_TNodeXYZ( _simplices.back()._nPrev );
8780   gp_XYZ pN( 0,0,0 );
8781   for ( size_t i = 0; i < _simplices.size(); ++i )
8782   {
8783     gp_XYZ p = SMESH_TNodeXYZ( _simplices[i]._nPrev );
8784     edgeDir.push_back( p - pPrev );
8785     edgeSize.push_back( edgeDir.back().Magnitude() );
8786     if ( edgeSize.back() < numeric_limits<double>::min() )
8787     {
8788       edgeDir.pop_back();
8789       edgeSize.pop_back();
8790     }
8791     else
8792     {
8793       edgeDir.back() /= edgeSize.back();
8794       points.push_back( p );
8795       pN += p;
8796     }
8797     pPrev = p;
8798   }
8799   edgeDir.push_back ( edgeDir[0] );
8800   edgeSize.push_back( edgeSize[0] );
8801   pN /= points.size();
8802
8803   gp_XYZ newPos(0,0,0);
8804   double sumSize = 0;
8805   for ( size_t i = 0; i < points.size(); ++i )
8806   {
8807     gp_Vec toN    = pN - points[i];
8808     double toNLen = toN.Magnitude();
8809     if ( toNLen < numeric_limits<double>::min() )
8810     {
8811       newPos += pN;
8812       continue;
8813     }
8814     gp_Vec bisec    = edgeDir[i] + edgeDir[i+1];
8815     double bisecLen = bisec.SquareMagnitude();
8816     if ( bisecLen < numeric_limits<double>::min() )
8817     {
8818       gp_Vec norm = edgeDir[i] ^ toN;
8819       bisec = norm ^ edgeDir[i];
8820       bisecLen = bisec.SquareMagnitude();
8821     }
8822     bisecLen = Sqrt( bisecLen );
8823     bisec /= bisecLen;
8824
8825 #if 1
8826     gp_XYZ pNew = ( points[i] + bisec.XYZ() * toNLen ) * bisecLen;
8827     sumSize += bisecLen;
8828 #else
8829     gp_XYZ pNew = ( points[i] + bisec.XYZ() * toNLen ) * ( edgeSize[i] + edgeSize[i+1] );
8830     sumSize += ( edgeSize[i] + edgeSize[i+1] );
8831 #endif
8832     newPos += pNew;
8833   }
8834   newPos /= sumSize;
8835
8836   // project newPos to an average plane
8837
8838   gp_XYZ norm(0,0,0); // plane normal
8839   points.push_back( points[0] );
8840   for ( size_t i = 1; i < points.size(); ++i )
8841   {
8842     gp_XYZ vec1 = points[ i-1 ] - pN;
8843     gp_XYZ vec2 = points[ i   ] - pN;
8844     gp_XYZ cross = vec1 ^ vec2;
8845     try {
8846       cross.Normalize();
8847       if ( cross * norm < numeric_limits<double>::min() )
8848         norm += cross.Reversed();
8849       else
8850         norm += cross;
8851     }
8852     catch (Standard_Failure) { // if |cross| == 0.
8853     }
8854   }
8855   gp_XYZ vec = newPos - pN;
8856   double r   = ( norm * vec ) / norm.SquareModulus();  // param [0,1] on norm
8857   newPos     = newPos - r * norm;
8858
8859   return newPos;
8860 }
8861
8862 //================================================================================
8863 /*!
8864  * \brief Computes a new node position using weigthed node positions
8865  */
8866 //================================================================================
8867
8868 gp_XYZ _LayerEdge::smoothLengthWeighted()
8869 {
8870   vector< double > edgeSize; edgeSize.reserve( _simplices.size() + 1);
8871   vector< gp_XYZ > points;   points.  reserve( _simplices.size() );
8872
8873   gp_XYZ pPrev = SMESH_TNodeXYZ( _simplices.back()._nPrev );
8874   for ( size_t i = 0; i < _simplices.size(); ++i )
8875   {
8876     gp_XYZ p = SMESH_TNodeXYZ( _simplices[i]._nPrev );
8877     edgeSize.push_back( ( p - pPrev ).Modulus() );
8878     if ( edgeSize.back() < numeric_limits<double>::min() )
8879     {
8880       edgeSize.pop_back();
8881     }
8882     else
8883     {
8884       points.push_back( p );
8885     }
8886     pPrev = p;
8887   }
8888   edgeSize.push_back( edgeSize[0] );
8889
8890   gp_XYZ newPos(0,0,0);
8891   double sumSize = 0;
8892   for ( size_t i = 0; i < points.size(); ++i )
8893   {
8894     newPos += points[i] * ( edgeSize[i] + edgeSize[i+1] );
8895     sumSize += edgeSize[i] + edgeSize[i+1];
8896   }
8897   newPos /= sumSize;
8898   return newPos;
8899 }
8900
8901 //================================================================================
8902 /*!
8903  * \brief Computes a new node position using angular-based smoothing
8904  */
8905 //================================================================================
8906
8907 gp_XYZ _LayerEdge::smoothCentroidal()
8908 {
8909   gp_XYZ newPos(0,0,0);
8910   gp_XYZ pN = SMESH_TNodeXYZ( _nodes.back() );
8911   double sumSize = 0;
8912   for ( size_t i = 0; i < _simplices.size(); ++i )
8913   {
8914     gp_XYZ p1 = SMESH_TNodeXYZ( _simplices[i]._nPrev );
8915     gp_XYZ p2 = SMESH_TNodeXYZ( _simplices[i]._nNext );
8916     gp_XYZ gc = ( pN + p1 + p2 ) / 3.;
8917     double size = (( p1 - pN ) ^ ( p2 - pN )).Modulus();
8918
8919     sumSize += size;
8920     newPos += gc * size;
8921   }
8922   newPos /= sumSize;
8923
8924   return newPos;
8925 }
8926
8927 //================================================================================
8928 /*!
8929  * \brief Computes a new node position located inside a Nef polygon
8930  */
8931 //================================================================================
8932
8933 gp_XYZ _LayerEdge::smoothNefPolygon()
8934 #ifdef OLD_NEF_POLYGON
8935 {
8936   gp_XYZ newPos(0,0,0);
8937
8938   // get a plane to search a solution on
8939
8940   vector< gp_XYZ > vecs( _simplices.size() + 1 );
8941   size_t i;
8942   const double tol = numeric_limits<double>::min();
8943   gp_XYZ center(0,0,0);
8944   for ( i = 0; i < _simplices.size(); ++i )
8945   {
8946     vecs[i] = ( SMESH_TNodeXYZ( _simplices[i]._nNext ) -
8947                 SMESH_TNodeXYZ( _simplices[i]._nPrev ));
8948     center += SMESH_TNodeXYZ( _simplices[i]._nPrev );
8949   }
8950   vecs.back() = vecs[0];
8951   center /= _simplices.size();
8952
8953   gp_XYZ zAxis(0,0,0);
8954   for ( i = 0; i < _simplices.size(); ++i )
8955     zAxis += vecs[i] ^ vecs[i+1];
8956
8957   gp_XYZ yAxis;
8958   for ( i = 0; i < _simplices.size(); ++i )
8959   {
8960     yAxis = vecs[i];
8961     if ( yAxis.SquareModulus() > tol )
8962       break;
8963   }
8964   gp_XYZ xAxis = yAxis ^ zAxis;
8965   // SMESH_TNodeXYZ p0( _simplices[0]._nPrev );
8966   // const double tol = 1e-6 * ( p0.Distance( _simplices[1]._nPrev ) +
8967   //                             p0.Distance( _simplices[2]._nPrev ));
8968   // gp_XYZ center = smoothLaplacian();
8969   // gp_XYZ xAxis, yAxis, zAxis;
8970   // for ( i = 0; i < _simplices.size(); ++i )
8971   // {
8972   //   xAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
8973   //   if ( xAxis.SquareModulus() > tol*tol )
8974   //     break;
8975   // }
8976   // for ( i = 1; i < _simplices.size(); ++i )
8977   // {
8978   //   yAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
8979   //   zAxis = xAxis ^ yAxis;
8980   //   if ( zAxis.SquareModulus() > tol*tol )
8981   //     break;
8982   // }
8983   // if ( i == _simplices.size() ) return newPos;
8984
8985   yAxis = zAxis ^ xAxis;
8986   xAxis /= xAxis.Modulus();
8987   yAxis /= yAxis.Modulus();
8988
8989   // get half-planes of _simplices
8990
8991   vector< _halfPlane > halfPlns( _simplices.size() );
8992   int nbHP = 0;
8993   for ( size_t i = 0; i < _simplices.size(); ++i )
8994   {
8995     gp_XYZ OP1 = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
8996     gp_XYZ OP2 = SMESH_TNodeXYZ( _simplices[i]._nNext ) - center;
8997     gp_XY  p1( OP1 * xAxis, OP1 * yAxis );
8998     gp_XY  p2( OP2 * xAxis, OP2 * yAxis );
8999     gp_XY  vec12 = p2 - p1;
9000     double dist12 = vec12.Modulus();
9001     if ( dist12 < tol )
9002       continue;
9003     vec12 /= dist12;
9004     halfPlns[ nbHP ]._pos = p1;
9005     halfPlns[ nbHP ]._dir = vec12;
9006     halfPlns[ nbHP ]._inNorm.SetCoord( -vec12.Y(), vec12.X() );
9007     ++nbHP;
9008   }
9009
9010   // intersect boundaries of half-planes, define state of intersection points
9011   // in relation to all half-planes and calculate internal point of a 2D polygon
9012
9013   double sumLen = 0;
9014   gp_XY newPos2D (0,0);
9015
9016   enum { UNDEF = -1, NOT_OUT, IS_OUT, NO_INT };
9017   typedef std::pair< gp_XY, int > TIntPntState; // coord and isOut state
9018   TIntPntState undefIPS( gp_XY(1e100,1e100), UNDEF );
9019
9020   vector< vector< TIntPntState > > allIntPnts( nbHP );
9021   for ( int iHP1 = 0; iHP1 < nbHP; ++iHP1 )
9022   {
9023     vector< TIntPntState > & intPnts1 = allIntPnts[ iHP1 ];
9024     if ( intPnts1.empty() ) intPnts1.resize( nbHP, undefIPS );
9025
9026     int iPrev = SMESH_MesherHelper::WrapIndex( iHP1 - 1, nbHP );
9027     int iNext = SMESH_MesherHelper::WrapIndex( iHP1 + 1, nbHP );
9028
9029     int nbNotOut = 0;
9030     const gp_XY* segEnds[2] = { 0, 0 }; // NOT_OUT points
9031
9032     for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9033     {
9034       if ( iHP1 == iHP2 ) continue;
9035
9036       TIntPntState & ips1 = intPnts1[ iHP2 ];
9037       if ( ips1.second == UNDEF )
9038       {
9039         // find an intersection point of boundaries of iHP1 and iHP2
9040
9041         if ( iHP2 == iPrev ) // intersection with neighbors is known
9042           ips1.first = halfPlns[ iHP1 ]._pos;
9043         else if ( iHP2 == iNext )
9044           ips1.first = halfPlns[ iHP2 ]._pos;
9045         else if ( !halfPlns[ iHP1 ].FindIntersection( halfPlns[ iHP2 ], ips1.first ))
9046           ips1.second = NO_INT;
9047
9048         // classify the found intersection point
9049         if ( ips1.second != NO_INT )
9050         {
9051           ips1.second = NOT_OUT;
9052           for ( int i = 0; i < nbHP && ips1.second == NOT_OUT; ++i )
9053             if ( i != iHP1 && i != iHP2 &&
9054                  halfPlns[ i ].IsOut( ips1.first, tol ))
9055               ips1.second = IS_OUT;
9056         }
9057         vector< TIntPntState > & intPnts2 = allIntPnts[ iHP2 ];
9058         if ( intPnts2.empty() ) intPnts2.resize( nbHP, undefIPS );
9059         TIntPntState & ips2 = intPnts2[ iHP1 ];
9060         ips2 = ips1;
9061       }
9062       if ( ips1.second == NOT_OUT )
9063       {
9064         ++nbNotOut;
9065         segEnds[ bool(segEnds[0]) ] = & ips1.first;
9066       }
9067     }
9068
9069     // find a NOT_OUT segment of boundary which is located between
9070     // two NOT_OUT int points
9071
9072     if ( nbNotOut < 2 )
9073       continue; // no such a segment
9074
9075     if ( nbNotOut > 2 )
9076     {
9077       // sort points along the boundary
9078       map< double, TIntPntState* > ipsByParam;
9079       for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9080       {
9081         TIntPntState & ips1 = intPnts1[ iHP2 ];
9082         if ( ips1.second != NO_INT )
9083         {
9084           gp_XY     op = ips1.first - halfPlns[ iHP1 ]._pos;
9085           double param = op * halfPlns[ iHP1 ]._dir;
9086           ipsByParam.insert( make_pair( param, & ips1 ));
9087         }
9088       }
9089       // look for two neighboring NOT_OUT points
9090       nbNotOut = 0;
9091       map< double, TIntPntState* >::iterator u2ips = ipsByParam.begin();
9092       for ( ; u2ips != ipsByParam.end(); ++u2ips )
9093       {
9094         TIntPntState & ips1 = *(u2ips->second);
9095         if ( ips1.second == NOT_OUT )
9096           segEnds[ bool( nbNotOut++ ) ] = & ips1.first;
9097         else if ( nbNotOut >= 2 )
9098           break;
9099         else
9100           nbNotOut = 0;
9101       }
9102     }
9103
9104     if ( nbNotOut >= 2 )
9105     {
9106       double len = ( *segEnds[0] - *segEnds[1] ).Modulus();
9107       sumLen += len;
9108
9109       newPos2D += 0.5 * len * ( *segEnds[0] + *segEnds[1] );
9110     }
9111   }
9112
9113   if ( sumLen > 0 )
9114   {
9115     newPos2D /= sumLen;
9116     newPos = center + xAxis * newPos2D.X() + yAxis * newPos2D.Y();
9117   }
9118   else
9119   {
9120     newPos = center;
9121   }
9122
9123   return newPos;
9124 }
9125 #else // OLD_NEF_POLYGON
9126 { ////////////////////////////////// NEW
9127   gp_XYZ newPos(0,0,0);
9128
9129   // get a plane to search a solution on
9130
9131   size_t i;
9132   gp_XYZ center(0,0,0);
9133   for ( i = 0; i < _simplices.size(); ++i )
9134     center += SMESH_TNodeXYZ( _simplices[i]._nPrev );
9135   center /= _simplices.size();
9136
9137   vector< gp_XYZ > vecs( _simplices.size() + 1 );
9138   for ( i = 0; i < _simplices.size(); ++i )
9139     vecs[i] = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9140   vecs.back() = vecs[0];
9141
9142   const double tol = numeric_limits<double>::min();
9143   gp_XYZ zAxis(0,0,0);
9144   for ( i = 0; i < _simplices.size(); ++i )
9145   {
9146     gp_XYZ cross = vecs[i] ^ vecs[i+1];
9147     try {
9148       cross.Normalize();
9149       if ( cross * zAxis < tol )
9150         zAxis += cross.Reversed();
9151       else
9152         zAxis += cross;
9153     }
9154     catch (Standard_Failure) { // if |cross| == 0.
9155     }
9156   }
9157
9158   gp_XYZ yAxis;
9159   for ( i = 0; i < _simplices.size(); ++i )
9160   {
9161     yAxis = vecs[i];
9162     if ( yAxis.SquareModulus() > tol )
9163       break;
9164   }
9165   gp_XYZ xAxis = yAxis ^ zAxis;
9166   // SMESH_TNodeXYZ p0( _simplices[0]._nPrev );
9167   // const double tol = 1e-6 * ( p0.Distance( _simplices[1]._nPrev ) +
9168   //                             p0.Distance( _simplices[2]._nPrev ));
9169   // gp_XYZ center = smoothLaplacian();
9170   // gp_XYZ xAxis, yAxis, zAxis;
9171   // for ( i = 0; i < _simplices.size(); ++i )
9172   // {
9173   //   xAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9174   //   if ( xAxis.SquareModulus() > tol*tol )
9175   //     break;
9176   // }
9177   // for ( i = 1; i < _simplices.size(); ++i )
9178   // {
9179   //   yAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9180   //   zAxis = xAxis ^ yAxis;
9181   //   if ( zAxis.SquareModulus() > tol*tol )
9182   //     break;
9183   // }
9184   // if ( i == _simplices.size() ) return newPos;
9185
9186   yAxis = zAxis ^ xAxis;
9187   xAxis /= xAxis.Modulus();
9188   yAxis /= yAxis.Modulus();
9189
9190   // get half-planes of _simplices
9191
9192   vector< _halfPlane > halfPlns( _simplices.size() );
9193   int nbHP = 0;
9194   for ( size_t i = 0; i < _simplices.size(); ++i )
9195   {
9196     const gp_XYZ& OP1 = vecs[ i   ];
9197     const gp_XYZ& OP2 = vecs[ i+1 ];
9198     gp_XY  p1( OP1 * xAxis, OP1 * yAxis );
9199     gp_XY  p2( OP2 * xAxis, OP2 * yAxis );
9200     gp_XY  vec12 = p2 - p1;
9201     double dist12 = vec12.Modulus();
9202     if ( dist12 < tol )
9203       continue;
9204     vec12 /= dist12;
9205     halfPlns[ nbHP ]._pos = p1;
9206     halfPlns[ nbHP ]._dir = vec12;
9207     halfPlns[ nbHP ]._inNorm.SetCoord( -vec12.Y(), vec12.X() );
9208     ++nbHP;
9209   }
9210
9211   // intersect boundaries of half-planes, define state of intersection points
9212   // in relation to all half-planes and calculate internal point of a 2D polygon
9213
9214   double sumLen = 0;
9215   gp_XY newPos2D (0,0);
9216
9217   enum { UNDEF = -1, NOT_OUT, IS_OUT, NO_INT };
9218   typedef std::pair< gp_XY, int > TIntPntState; // coord and isOut state
9219   TIntPntState undefIPS( gp_XY(1e100,1e100), UNDEF );
9220
9221   vector< vector< TIntPntState > > allIntPnts( nbHP );
9222   for ( int iHP1 = 0; iHP1 < nbHP; ++iHP1 )
9223   {
9224     vector< TIntPntState > & intPnts1 = allIntPnts[ iHP1 ];
9225     if ( intPnts1.empty() ) intPnts1.resize( nbHP, undefIPS );
9226
9227     int iPrev = SMESH_MesherHelper::WrapIndex( iHP1 - 1, nbHP );
9228     int iNext = SMESH_MesherHelper::WrapIndex( iHP1 + 1, nbHP );
9229
9230     int nbNotOut = 0;
9231     const gp_XY* segEnds[2] = { 0, 0 }; // NOT_OUT points
9232
9233     for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9234     {
9235       if ( iHP1 == iHP2 ) continue;
9236
9237       TIntPntState & ips1 = intPnts1[ iHP2 ];
9238       if ( ips1.second == UNDEF )
9239       {
9240         // find an intersection point of boundaries of iHP1 and iHP2
9241
9242         if ( iHP2 == iPrev ) // intersection with neighbors is known
9243           ips1.first = halfPlns[ iHP1 ]._pos;
9244         else if ( iHP2 == iNext )
9245           ips1.first = halfPlns[ iHP2 ]._pos;
9246         else if ( !halfPlns[ iHP1 ].FindIntersection( halfPlns[ iHP2 ], ips1.first ))
9247           ips1.second = NO_INT;
9248
9249         // classify the found intersection point
9250         if ( ips1.second != NO_INT )
9251         {
9252           ips1.second = NOT_OUT;
9253           for ( int i = 0; i < nbHP && ips1.second == NOT_OUT; ++i )
9254             if ( i != iHP1 && i != iHP2 &&
9255                  halfPlns[ i ].IsOut( ips1.first, tol ))
9256               ips1.second = IS_OUT;
9257         }
9258         vector< TIntPntState > & intPnts2 = allIntPnts[ iHP2 ];
9259         if ( intPnts2.empty() ) intPnts2.resize( nbHP, undefIPS );
9260         TIntPntState & ips2 = intPnts2[ iHP1 ];
9261         ips2 = ips1;
9262       }
9263       if ( ips1.second == NOT_OUT )
9264       {
9265         ++nbNotOut;
9266         segEnds[ bool(segEnds[0]) ] = & ips1.first;
9267       }
9268     }
9269
9270     // find a NOT_OUT segment of boundary which is located between
9271     // two NOT_OUT int points
9272
9273     if ( nbNotOut < 2 )
9274       continue; // no such a segment
9275
9276     if ( nbNotOut > 2 )
9277     {
9278       // sort points along the boundary
9279       map< double, TIntPntState* > ipsByParam;
9280       for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9281       {
9282         TIntPntState & ips1 = intPnts1[ iHP2 ];
9283         if ( ips1.second != NO_INT )
9284         {
9285           gp_XY     op = ips1.first - halfPlns[ iHP1 ]._pos;
9286           double param = op * halfPlns[ iHP1 ]._dir;
9287           ipsByParam.insert( make_pair( param, & ips1 ));
9288         }
9289       }
9290       // look for two neighboring NOT_OUT points
9291       nbNotOut = 0;
9292       map< double, TIntPntState* >::iterator u2ips = ipsByParam.begin();
9293       for ( ; u2ips != ipsByParam.end(); ++u2ips )
9294       {
9295         TIntPntState & ips1 = *(u2ips->second);
9296         if ( ips1.second == NOT_OUT )
9297           segEnds[ bool( nbNotOut++ ) ] = & ips1.first;
9298         else if ( nbNotOut >= 2 )
9299           break;
9300         else
9301           nbNotOut = 0;
9302       }
9303     }
9304
9305     if ( nbNotOut >= 2 )
9306     {
9307       double len = ( *segEnds[0] - *segEnds[1] ).Modulus();
9308       sumLen += len;
9309
9310       newPos2D += 0.5 * len * ( *segEnds[0] + *segEnds[1] );
9311     }
9312   }
9313
9314   if ( sumLen > 0 )
9315   {
9316     newPos2D /= sumLen;
9317     newPos = center + xAxis * newPos2D.X() + yAxis * newPos2D.Y();
9318   }
9319   else
9320   {
9321     newPos = center;
9322   }
9323
9324   return newPos;
9325 }
9326 #endif // OLD_NEF_POLYGON
9327
9328 //================================================================================
9329 /*!
9330  * \brief Add a new segment to _LayerEdge during inflation
9331  */
9332 //================================================================================
9333
9334 void _LayerEdge::SetNewLength( double len, _EdgesOnShape& eos, SMESH_MesherHelper& helper )
9335 {
9336   if ( Is( BLOCKED ))
9337     return;
9338
9339   if ( len > _maxLen )
9340   {
9341     len = _maxLen;
9342     Block( eos.GetData() );
9343   }
9344   const double lenDelta = len - _len;
9345   if ( lenDelta < len * 1e-3  )
9346   {
9347     Block( eos.GetData() );
9348     return;
9349   }
9350
9351   SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() );
9352   gp_XYZ oldXYZ = SMESH_TNodeXYZ( n );
9353   gp_XYZ newXYZ;
9354   if ( eos._hyp.IsOffsetMethod() )
9355   {
9356     newXYZ = oldXYZ;
9357     gp_Vec faceNorm;
9358     SMDS_ElemIteratorPtr faceIt = _nodes[0]->GetInverseElementIterator( SMDSAbs_Face );
9359     while ( faceIt->more() )
9360     {
9361       const SMDS_MeshElement* face = faceIt->next();
9362       if ( !eos.GetNormal( face, faceNorm ))
9363         continue;
9364
9365       // translate plane of a face
9366       gp_XYZ baryCenter = oldXYZ + faceNorm.XYZ() * lenDelta;
9367
9368       // find point of intersection of the face plane located at baryCenter
9369       // and _normal located at newXYZ
9370       double d   = -( faceNorm.XYZ() * baryCenter ); // d of plane equation ax+by+cz+d=0
9371       double dot =  ( faceNorm.XYZ() * _normal );
9372       if ( dot < std::numeric_limits<double>::min() )
9373         dot = lenDelta * 1e-3;
9374       double step = -( faceNorm.XYZ() * newXYZ + d ) / dot;
9375       newXYZ += step * _normal;
9376     }
9377     _lenFactor = _normal * ( newXYZ - oldXYZ ) / lenDelta; // _lenFactor is used in InvalidateStep()
9378   }
9379   else
9380   {
9381     newXYZ = oldXYZ + _normal * lenDelta * _lenFactor;
9382   }
9383
9384   n->setXYZ( newXYZ.X(), newXYZ.Y(), newXYZ.Z() );
9385   _pos.push_back( newXYZ );
9386
9387   if ( !eos._sWOL.IsNull() )
9388   {
9389     double distXYZ[4];
9390     bool uvOK = false;
9391     if ( eos.SWOLType() == TopAbs_EDGE )
9392     {
9393       double u = Precision::Infinite(); // to force projection w/o distance check
9394       uvOK = helper.CheckNodeU( TopoDS::Edge( eos._sWOL ), n, u,
9395                                 /*tol=*/2*lenDelta, /*force=*/true, distXYZ );
9396       _pos.back().SetCoord( u, 0, 0 );
9397       if ( _nodes.size() > 1 && uvOK )
9398       {
9399         SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( n->GetPosition() );
9400         pos->SetUParameter( u );
9401       }
9402     }
9403     else //  TopAbs_FACE
9404     {
9405       gp_XY uv( Precision::Infinite(), 0 );
9406       uvOK = helper.CheckNodeUV( TopoDS::Face( eos._sWOL ), n, uv,
9407                                  /*tol=*/2*lenDelta, /*force=*/true, distXYZ );
9408       _pos.back().SetCoord( uv.X(), uv.Y(), 0 );
9409       if ( _nodes.size() > 1 && uvOK )
9410       {
9411         SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( n->GetPosition() );
9412         pos->SetUParameter( uv.X() );
9413         pos->SetVParameter( uv.Y() );
9414       }
9415     }
9416     if ( uvOK )
9417     {
9418       n->setXYZ( distXYZ[1], distXYZ[2], distXYZ[3]);
9419     }
9420     else
9421     {
9422       n->setXYZ( oldXYZ.X(), oldXYZ.Y(), oldXYZ.Z() );
9423       _pos.pop_back();
9424       Block( eos.GetData() );
9425       return;
9426     }
9427   }
9428
9429   _len = len;
9430
9431   // notify _neibors
9432   if ( eos.ShapeType() != TopAbs_FACE )
9433   {
9434     for ( size_t i = 0; i < _neibors.size(); ++i )
9435       //if (  _len > _neibors[i]->GetSmooLen() )
9436         _neibors[i]->Set( MOVED );
9437
9438     Set( MOVED );
9439   }
9440   dumpMove( n ); //debug
9441 }
9442
9443 //================================================================================
9444 /*!
9445  * \brief Set BLOCKED flag and propagate limited _maxLen to _neibors
9446  */
9447 //================================================================================
9448
9449 void _LayerEdge::Block( _SolidData& data )
9450 {
9451   //if ( Is( BLOCKED )) return;
9452   Set( BLOCKED );
9453
9454   SMESH_Comment msg( "#BLOCK shape=");
9455   msg << data.GetShapeEdges( this )->_shapeID
9456       << ", nodes " << _nodes[0]->GetID() << ", " << _nodes.back()->GetID();
9457   dumpCmd( msg + " -- BEGIN")
9458
9459   _maxLen = _len;
9460   std::queue<_LayerEdge*> queue;
9461   queue.push( this );
9462
9463   gp_Pnt pSrc, pTgt, pSrcN, pTgtN;
9464   while ( !queue.empty() )
9465   {
9466     _LayerEdge* edge = queue.front(); queue.pop();
9467     pSrc = SMESH_TNodeXYZ( edge->_nodes[0] );
9468     pTgt = SMESH_TNodeXYZ( edge->_nodes.back() );
9469     for ( size_t iN = 0; iN < edge->_neibors.size(); ++iN )
9470     {
9471       _LayerEdge* neibor = edge->_neibors[iN];
9472       if ( neibor->_maxLen < edge->_maxLen * 1.01 )
9473         continue;
9474       pSrcN = SMESH_TNodeXYZ( neibor->_nodes[0] );
9475       pTgtN = SMESH_TNodeXYZ( neibor->_nodes.back() );
9476       double minDist = pSrc.SquareDistance( pSrcN );
9477       minDist   = Min( pTgt.SquareDistance( pTgtN ), minDist );
9478       minDist   = Min( pSrc.SquareDistance( pTgtN ), minDist );
9479       minDist   = Min( pTgt.SquareDistance( pSrcN ), minDist );
9480       double newMaxLen = edge->_maxLen + 0.5 * Sqrt( minDist );
9481       //if ( edge->_nodes[0]->getshapeId() == neibor->_nodes[0]->getshapeId() ) viscous_layers_00/A3
9482       {
9483         newMaxLen *= edge->_lenFactor / neibor->_lenFactor;
9484         // newMaxLen *= Min( edge->_lenFactor / neibor->_lenFactor,
9485         //                   neibor->_lenFactor / edge->_lenFactor );
9486       }
9487       if ( neibor->_maxLen > newMaxLen )
9488       {
9489         neibor->_maxLen = newMaxLen;
9490         if ( neibor->_maxLen < neibor->_len )
9491         {
9492           _EdgesOnShape* eos = data.GetShapeEdges( neibor );
9493           while ( neibor->_len > neibor->_maxLen &&
9494                   neibor->NbSteps() > 0 )
9495             neibor->InvalidateStep( neibor->NbSteps(), *eos, /*restoreLength=*/true );
9496           neibor->SetNewLength( neibor->_maxLen, *eos, data.GetHelper() );
9497           //neibor->Block( data );
9498         }
9499         queue.push( neibor );
9500       }
9501     }
9502   }
9503   dumpCmd( msg + " -- END")
9504 }
9505
9506 //================================================================================
9507 /*!
9508  * \brief Remove last inflation step
9509  */
9510 //================================================================================
9511
9512 void _LayerEdge::InvalidateStep( size_t curStep, const _EdgesOnShape& eos, bool restoreLength )
9513 {
9514   if ( _pos.size() > curStep && _nodes.size() > 1 )
9515   {
9516     _pos.resize( curStep );
9517
9518     gp_Pnt      nXYZ = _pos.back();
9519     SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() );
9520     SMESH_TNodeXYZ curXYZ( n );
9521     if ( !eos._sWOL.IsNull() )
9522     {
9523       TopLoc_Location loc;
9524       if ( eos.SWOLType() == TopAbs_EDGE )
9525       {
9526         SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( n->GetPosition() );
9527         pos->SetUParameter( nXYZ.X() );
9528         double f,l;
9529         Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( eos._sWOL ), loc, f,l);
9530         nXYZ = curve->Value( nXYZ.X() ).Transformed( loc );
9531       }
9532       else
9533       {
9534         SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( n->GetPosition() );
9535         pos->SetUParameter( nXYZ.X() );
9536         pos->SetVParameter( nXYZ.Y() );
9537         Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face(eos._sWOL), loc );
9538         nXYZ = surface->Value( nXYZ.X(), nXYZ.Y() ).Transformed( loc );
9539       }
9540     }
9541     n->setXYZ( nXYZ.X(), nXYZ.Y(), nXYZ.Z() );
9542     dumpMove( n );
9543
9544     if ( restoreLength )
9545     {
9546       if ( NbSteps() == 0 )
9547         _len = 0.;
9548       else
9549         _len -= ( nXYZ.XYZ() - curXYZ ).Modulus() / _lenFactor;
9550     }
9551   }
9552 }
9553
9554 //================================================================================
9555 /*!
9556  * \brief Return index of a _pos distant from _normal
9557  */
9558 //================================================================================
9559
9560 int _LayerEdge::GetSmoothedPos( const double tol )
9561 {
9562   int iSmoothed = 0;
9563   for ( size_t i = 1; i < _pos.size() && !iSmoothed; ++i )
9564   {
9565     double normDist = ( _pos[i] - _pos[0] ).Crossed( _normal ).SquareModulus();
9566     if ( normDist > tol * tol )
9567       iSmoothed = i;
9568   }
9569   return iSmoothed;
9570 }
9571
9572 //================================================================================
9573 /*!
9574  * \brief Smooth a path formed by _pos of a _LayerEdge smoothed on FACE
9575  */
9576 //================================================================================
9577
9578 void _LayerEdge::SmoothPos( const vector< double >& segLen, const double tol )
9579 {
9580   if ( /*Is( NORMAL_UPDATED ) ||*/ _pos.size() <= 2 )
9581     return;
9582
9583   // find the 1st smoothed _pos
9584   int iSmoothed = GetSmoothedPos( tol );
9585   if ( !iSmoothed ) return;
9586
9587   //if ( 1 || Is( DISTORTED ))
9588   {
9589     gp_XYZ normal = _normal;
9590     if ( Is( NORMAL_UPDATED ))
9591       for ( size_t i = 1; i < _pos.size(); ++i )
9592       {
9593         normal = _pos[i] - _pos[0];
9594         double size = normal.Modulus();
9595         if ( size > RealSmall() )
9596         {
9597           normal /= size;
9598           break;
9599         }
9600       }
9601     const double r = 0.2;
9602     for ( int iter = 0; iter < 50; ++iter )
9603     {
9604       double minDot = 1;
9605       for ( size_t i = Max( 1, iSmoothed-1-iter ); i < _pos.size()-1; ++i )
9606       {
9607         gp_XYZ midPos = 0.5 * ( _pos[i-1] + _pos[i+1] );
9608         gp_XYZ newPos = ( 1-r ) * midPos + r * _pos[i];
9609         _pos[i] = newPos;
9610         double midLen = 0.5 * ( segLen[i-1] + segLen[i+1] );
9611         double newLen = ( 1-r ) * midLen + r * segLen[i];
9612         const_cast< double& >( segLen[i] ) = newLen;
9613         // check angle between normal and (_pos[i+1], _pos[i] )
9614         gp_XYZ posDir = _pos[i+1] - _pos[i];
9615         double size   = posDir.SquareModulus();
9616         if ( size > RealSmall() )
9617           minDot = Min( minDot, ( normal * posDir ) * ( normal * posDir ) / size );
9618       }
9619       if ( minDot > 0.5 * 0.5 )
9620         break;
9621     }
9622   }
9623   // else
9624   // {
9625   //   for ( size_t i = 1; i < _pos.size()-1; ++i )
9626   //   {
9627   //     if ((int) i < iSmoothed  &&  ( segLen[i] / segLen.back() < 0.5 ))
9628   //       continue;
9629
9630   //     double     wgt = segLen[i] / segLen.back();
9631   //     gp_XYZ normPos = _pos[0] + _normal * wgt * _len;
9632   //     gp_XYZ tgtPos  = ( 1 - wgt ) * _pos[0] +  wgt * _pos.back();
9633   //     gp_XYZ newPos  = ( 1 - wgt ) * normPos +  wgt * tgtPos;
9634   //     _pos[i] = newPos;
9635   //   }
9636   // }
9637 }
9638
9639 //================================================================================
9640 /*!
9641  * \brief Print flags
9642  */
9643 //================================================================================
9644
9645 std::string _LayerEdge::DumpFlags() const
9646 {
9647   SMESH_Comment dump;
9648   for ( int flag = 1; flag < 0x1000000; flag *= 2 )
9649     if ( _flags & flag )
9650     {
9651       EFlags f = (EFlags) flag;
9652       switch ( f ) {
9653       case TO_SMOOTH:       dump << "TO_SMOOTH";       break;
9654       case MOVED:           dump << "MOVED";           break;
9655       case SMOOTHED:        dump << "SMOOTHED";        break;
9656       case DIFFICULT:       dump << "DIFFICULT";       break;
9657       case ON_CONCAVE_FACE: dump << "ON_CONCAVE_FACE"; break;
9658       case BLOCKED:         dump << "BLOCKED";         break;
9659       case INTERSECTED:     dump << "INTERSECTED";     break;
9660       case NORMAL_UPDATED:  dump << "NORMAL_UPDATED";  break;
9661       case UPD_NORMAL_CONV: dump << "UPD_NORMAL_CONV"; break;
9662       case MARKED:          dump << "MARKED";          break;
9663       case MULTI_NORMAL:    dump << "MULTI_NORMAL";    break;
9664       case NEAR_BOUNDARY:   dump << "NEAR_BOUNDARY";   break;
9665       case SMOOTHED_C1:     dump << "SMOOTHED_C1";     break;
9666       case DISTORTED:       dump << "DISTORTED";       break;
9667       case RISKY_SWOL:      dump << "RISKY_SWOL";      break;
9668       case SHRUNK:          dump << "SHRUNK";          break;
9669       case UNUSED_FLAG:     dump << "UNUSED_FLAG";     break;
9670       }
9671       dump << " ";
9672     }
9673   cout << dump << endl;
9674   return dump;
9675 }
9676
9677 //================================================================================
9678 /*!
9679   case brief:
9680   default:
9681 */
9682 //================================================================================
9683
9684 bool _ViscousBuilder::refine(_SolidData& data)
9685 {
9686   SMESH_MesherHelper& helper = data.GetHelper();
9687   helper.SetElementsOnShape(false);
9688
9689   Handle(Geom_Curve) curve;
9690   Handle(ShapeAnalysis_Surface) surface;
9691   TopoDS_Edge geomEdge;
9692   TopoDS_Face geomFace;
9693   TopLoc_Location loc;
9694   double f,l, u = 0;
9695   gp_XY uv;
9696   vector< gp_XYZ > pos3D;
9697   bool isOnEdge, isTooConvexFace = false;
9698   TGeomID prevBaseId = -1;
9699   TNode2Edge* n2eMap = 0;
9700   TNode2Edge::iterator n2e;
9701
9702   // Create intermediate nodes on each _LayerEdge
9703
9704   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
9705   {
9706     _EdgesOnShape& eos = data._edgesOnShape[iS];
9707     if ( eos._edges.empty() ) continue;
9708
9709     if ( eos._edges[0]->_nodes.size() < 2 )
9710       continue; // on _noShrinkShapes
9711
9712     // get data of a shrink shape
9713     isOnEdge = false;
9714     geomEdge.Nullify(); geomFace.Nullify();
9715     curve.Nullify(); surface.Nullify();
9716     if ( !eos._sWOL.IsNull() )
9717     {
9718       isOnEdge = ( eos.SWOLType() == TopAbs_EDGE );
9719       if ( isOnEdge )
9720       {
9721         geomEdge = TopoDS::Edge( eos._sWOL );
9722         curve    = BRep_Tool::Curve( geomEdge, loc, f,l);
9723       }
9724       else
9725       {
9726         geomFace = TopoDS::Face( eos._sWOL );
9727         surface  = helper.GetSurface( geomFace );
9728       }
9729     }
9730     else if ( eos.ShapeType() == TopAbs_FACE && eos._toSmooth )
9731     {
9732       geomFace = TopoDS::Face( eos._shape );
9733       surface  = helper.GetSurface( geomFace );
9734       // propagate _toSmooth back to _eosC1, which was unset in findShapesToSmooth()
9735       for ( size_t i = 0; i < eos._eosC1.size(); ++i )
9736       {
9737         eos._eosC1[ i ]->_toSmooth = true;
9738         for ( size_t j = 0; j < eos._eosC1[i]->_edges.size(); ++j )
9739           eos._eosC1[i]->_edges[j]->Set( _LayerEdge::SMOOTHED_C1 );
9740       }
9741       isTooConvexFace = false;
9742       if ( _ConvexFace* cf = data.GetConvexFace( eos._shapeID ))
9743         isTooConvexFace = cf->_isTooCurved;
9744     }
9745
9746     vector< double > segLen;
9747     for ( size_t i = 0; i < eos._edges.size(); ++i )
9748     {
9749       _LayerEdge& edge = *eos._edges[i];
9750       if ( edge._pos.size() < 2 )
9751         continue;
9752
9753       // get accumulated length of segments
9754       segLen.resize( edge._pos.size() );
9755       segLen[0] = 0.0;
9756       if ( eos._sWOL.IsNull() )
9757       {
9758         bool useNormal = true;
9759         bool    usePos = false;
9760         bool  smoothed = false;
9761         double   preci = 0.1 * edge._len;
9762         if ( eos._toSmooth && edge._pos.size() > 2 )
9763         {
9764           smoothed = edge.GetSmoothedPos( preci );
9765         }
9766         if ( smoothed )
9767         {
9768           if ( !surface.IsNull() && !isTooConvexFace ) // edge smoothed on FACE
9769           {
9770             useNormal = usePos = false;
9771             gp_Pnt2d uv = helper.GetNodeUV( geomFace, edge._nodes[0] );
9772             for ( size_t j = 1; j < edge._pos.size() && !useNormal; ++j )
9773             {
9774               uv = surface->NextValueOfUV( uv, edge._pos[j], preci );
9775               if ( surface->Gap() < 2. * edge._len )
9776                 segLen[j] = surface->Gap();
9777               else
9778                 useNormal = true;
9779             }
9780           }
9781         }
9782         else if ( !edge.Is( _LayerEdge::NORMAL_UPDATED ))
9783         {
9784 #ifndef __NODES_AT_POS
9785           useNormal = usePos = false;
9786           edge._pos[1] = edge._pos.back();
9787           edge._pos.resize( 2 );
9788           segLen.resize( 2 );
9789           segLen[ 1 ] = edge._len;
9790 #endif
9791         }
9792         if ( useNormal && edge.Is( _LayerEdge::NORMAL_UPDATED ))
9793         {
9794           useNormal = usePos = false;
9795           _LayerEdge tmpEdge; // get original _normal
9796           tmpEdge._nodes.push_back( edge._nodes[0] );
9797           if ( !setEdgeData( tmpEdge, eos, helper, data ))
9798             usePos = true;
9799           else
9800             for ( size_t j = 1; j < edge._pos.size(); ++j )
9801               segLen[j] = ( edge._pos[j] - edge._pos[0] ) * tmpEdge._normal;
9802         }
9803         if ( useNormal )
9804         {
9805           for ( size_t j = 1; j < edge._pos.size(); ++j )
9806             segLen[j] = ( edge._pos[j] - edge._pos[0] ) * edge._normal;
9807         }
9808         if ( usePos )
9809         {
9810           for ( size_t j = 1; j < edge._pos.size(); ++j )
9811             segLen[j] = segLen[j-1] + ( edge._pos[j-1] - edge._pos[j] ).Modulus();
9812         }
9813         else
9814         {
9815           bool swapped = ( edge._pos.size() > 2 );
9816           while ( swapped )
9817           {
9818             swapped = false;
9819             for ( size_t j = 1; j < edge._pos.size()-1; ++j )
9820               if ( segLen[j] > segLen.back() )
9821               {
9822                 segLen.erase( segLen.begin() + j );
9823                 edge._pos.erase( edge._pos.begin() + j );
9824                 --j;
9825               }
9826               else if ( segLen[j] < segLen[j-1] )
9827               {
9828                 std::swap( segLen[j], segLen[j-1] );
9829                 std::swap( edge._pos[j], edge._pos[j-1] );
9830                 swapped = true;
9831               }
9832           }
9833         }
9834         // smooth a path formed by edge._pos
9835 #ifndef __NODES_AT_POS
9836         if (( smoothed ) /*&&
9837             ( eos.ShapeType() == TopAbs_FACE || edge.Is( _LayerEdge::SMOOTHED_C1 ))*/)
9838           edge.SmoothPos( segLen, preci );
9839 #endif
9840       }
9841       else if ( eos._isRegularSWOL ) // usual SWOL
9842       {
9843         if ( edge.Is( _LayerEdge::SMOOTHED ))
9844         {
9845           SMESH_NodeXYZ p0( edge._nodes[0] );
9846           for ( size_t j = 1; j < edge._pos.size(); ++j )
9847           {
9848             gp_XYZ pj = surface->Value( edge._pos[j].X(), edge._pos[j].Y() ).XYZ();
9849             segLen[j] = ( pj - p0 ) * edge._normal;
9850           }
9851         }
9852         else
9853         {
9854           for ( size_t j = 1; j < edge._pos.size(); ++j )
9855             segLen[j] = segLen[j-1] + (edge._pos[j-1] - edge._pos[j] ).Modulus();
9856         }
9857       }
9858       else if ( !surface.IsNull() ) // SWOL surface with singularities
9859       {
9860         pos3D.resize( edge._pos.size() );
9861         for ( size_t j = 0; j < edge._pos.size(); ++j )
9862           pos3D[j] = surface->Value( edge._pos[j].X(), edge._pos[j].Y() ).XYZ();
9863
9864         for ( size_t j = 1; j < edge._pos.size(); ++j )
9865           segLen[j] = segLen[j-1] + ( pos3D[j-1] - pos3D[j] ).Modulus();
9866       }
9867
9868       // allocate memory for new nodes if it is not yet refined
9869       const SMDS_MeshNode* tgtNode = edge._nodes.back();
9870       if ( edge._nodes.size() == 2 )
9871       {
9872 #ifdef __NODES_AT_POS
9873         int nbNodes = edge._pos.size();
9874 #else
9875         int nbNodes = eos._hyp.GetNumberLayers() + 1;
9876 #endif
9877         edge._nodes.resize( nbNodes, 0 );
9878         edge._nodes[1] = 0;
9879         edge._nodes.back() = tgtNode;
9880       }
9881       // restore shapePos of the last node by already treated _LayerEdge of another _SolidData
9882       const TGeomID baseShapeId = edge._nodes[0]->getshapeId();
9883       if ( baseShapeId != prevBaseId )
9884       {
9885         map< TGeomID, TNode2Edge* >::iterator s2ne = data._s2neMap.find( baseShapeId );
9886         n2eMap = ( s2ne == data._s2neMap.end() ) ? 0 : s2ne->second;
9887         prevBaseId = baseShapeId;
9888       }
9889       _LayerEdge* edgeOnSameNode = 0;
9890       bool        useExistingPos = false;
9891       if ( n2eMap && (( n2e = n2eMap->find( edge._nodes[0] )) != n2eMap->end() ))
9892       {
9893         edgeOnSameNode = n2e->second;
9894         useExistingPos = ( edgeOnSameNode->_len < edge._len );
9895         const gp_XYZ& otherTgtPos = edgeOnSameNode->_pos.back();
9896         SMDS_PositionPtr  lastPos = tgtNode->GetPosition();
9897         if ( isOnEdge )
9898         {
9899           SMDS_EdgePosition* epos = static_cast<SMDS_EdgePosition*>( lastPos );
9900           epos->SetUParameter( otherTgtPos.X() );
9901         }
9902         else
9903         {
9904           SMDS_FacePosition* fpos = static_cast<SMDS_FacePosition*>( lastPos );
9905           fpos->SetUParameter( otherTgtPos.X() );
9906           fpos->SetVParameter( otherTgtPos.Y() );
9907         }
9908       }
9909       // calculate height of the first layer
9910       double h0;
9911       const double T = segLen.back(); //data._hyp.GetTotalThickness();
9912       const double f = eos._hyp.GetStretchFactor();
9913       const int    N = eos._hyp.GetNumberLayers();
9914       const double fPowN = pow( f, N );
9915       if ( fPowN - 1 <= numeric_limits<double>::min() )
9916         h0 = T / N;
9917       else
9918         h0 = T * ( f - 1 )/( fPowN - 1 );
9919
9920       const double zeroLen = std::numeric_limits<double>::min();
9921
9922       // create intermediate nodes
9923       double hSum = 0, hi = h0/f;
9924       size_t iSeg = 1;
9925       for ( size_t iStep = 1; iStep < edge._nodes.size(); ++iStep )
9926       {
9927         // compute an intermediate position
9928         hi *= f;
9929         hSum += hi;
9930         while ( hSum > segLen[iSeg] && iSeg < segLen.size()-1 )
9931           ++iSeg;
9932         int iPrevSeg = iSeg-1;
9933         while ( fabs( segLen[iPrevSeg] - segLen[iSeg]) <= zeroLen && iPrevSeg > 0 )
9934           --iPrevSeg;
9935         double   r = ( segLen[iSeg] - hSum ) / ( segLen[iSeg] - segLen[iPrevSeg] );
9936         gp_Pnt pos = r * edge._pos[iPrevSeg] + (1-r) * edge._pos[iSeg];
9937 #ifdef __NODES_AT_POS
9938         pos = edge._pos[ iStep ];
9939 #endif
9940         SMDS_MeshNode*& node = const_cast< SMDS_MeshNode*& >( edge._nodes[ iStep ]);
9941         if ( !eos._sWOL.IsNull() )
9942         {
9943           // compute XYZ by parameters <pos>
9944           if ( isOnEdge )
9945           {
9946             u = pos.X();
9947             if ( !node )
9948               pos = curve->Value( u ).Transformed(loc);
9949           }
9950           else if ( eos._isRegularSWOL )
9951           {
9952             uv.SetCoord( pos.X(), pos.Y() );
9953             if ( !node )
9954               pos = surface->Value( pos.X(), pos.Y() );
9955           }
9956           else
9957           {
9958             uv.SetCoord( pos.X(), pos.Y() );
9959             gp_Pnt p = r * pos3D[ iPrevSeg ] + (1-r) * pos3D[ iSeg ];
9960             uv = surface->NextValueOfUV( uv, p, BRep_Tool::Tolerance( geomFace )).XY();
9961             if ( !node )
9962               pos = surface->Value( uv );
9963           }
9964         }
9965         // create or update the node
9966         if ( !node )
9967         {
9968           node = helper.AddNode( pos.X(), pos.Y(), pos.Z());
9969           if ( !eos._sWOL.IsNull() )
9970           {
9971             if ( isOnEdge )
9972               getMeshDS()->SetNodeOnEdge( node, geomEdge, u );
9973             else
9974               getMeshDS()->SetNodeOnFace( node, geomFace, uv.X(), uv.Y() );
9975           }
9976           else
9977           {
9978             getMeshDS()->SetNodeInVolume( node, helper.GetSubShapeID() );
9979           }
9980         }
9981         else
9982         {
9983           if ( !eos._sWOL.IsNull() )
9984           {
9985             // make average pos from new and current parameters
9986             if ( isOnEdge )
9987             {
9988               //u = 0.5 * ( u + helper.GetNodeU( geomEdge, node ));
9989               if ( useExistingPos )
9990                 u = helper.GetNodeU( geomEdge, node );
9991               pos = curve->Value( u ).Transformed(loc);
9992
9993               SMDS_EdgePosition* epos = static_cast<SMDS_EdgePosition*>( node->GetPosition() );
9994               epos->SetUParameter( u );
9995             }
9996             else
9997             {
9998               //uv = 0.5 * ( uv + helper.GetNodeUV( geomFace, node ));
9999               if ( useExistingPos )
10000                 uv = helper.GetNodeUV( geomFace, node );
10001               pos = surface->Value( uv );
10002
10003               SMDS_FacePosition* fpos = static_cast<SMDS_FacePosition*>( node->GetPosition() );
10004               fpos->SetUParameter( uv.X() );
10005               fpos->SetVParameter( uv.Y() );
10006             }
10007           }
10008           node->setXYZ( pos.X(), pos.Y(), pos.Z() );
10009         }
10010       } // loop on edge._nodes
10011
10012       if ( !eos._sWOL.IsNull() ) // prepare for shrink()
10013       {
10014         if ( isOnEdge )
10015           edge._pos.back().SetCoord( u, 0,0);
10016         else
10017           edge._pos.back().SetCoord( uv.X(), uv.Y() ,0);
10018
10019         if ( edgeOnSameNode )
10020           edgeOnSameNode->_pos.back() = edge._pos.back();
10021       }
10022
10023     } // loop on eos._edges to create nodes
10024
10025
10026     if ( !getMeshDS()->IsEmbeddedMode() )
10027       // Log node movement
10028       for ( size_t i = 0; i < eos._edges.size(); ++i )
10029       {
10030         SMESH_TNodeXYZ p ( eos._edges[i]->_nodes.back() );
10031         getMeshDS()->MoveNode( p._node, p.X(), p.Y(), p.Z() );
10032       }
10033   }
10034
10035
10036   // Create volumes
10037
10038   helper.SetElementsOnShape(true);
10039
10040   vector< vector<const SMDS_MeshNode*>* > nnVec;
10041   set< vector<const SMDS_MeshNode*>* >    nnSet;
10042   set< int >                       degenEdgeInd;
10043   vector<const SMDS_MeshElement*>     degenVols;
10044
10045   TopExp_Explorer exp( data._solid, TopAbs_FACE );
10046   for ( ; exp.More(); exp.Next() )
10047   {
10048     const TGeomID faceID = getMeshDS()->ShapeToIndex( exp.Current() );
10049     if ( data._ignoreFaceIds.count( faceID ))
10050       continue;
10051     const bool isReversedFace = data._reversedFaceIds.count( faceID );
10052     SMESHDS_SubMesh*    fSubM = getMeshDS()->MeshElements( exp.Current() );
10053     SMDS_ElemIteratorPtr  fIt = fSubM->GetElements();
10054     while ( fIt->more() )
10055     {
10056       const SMDS_MeshElement* face = fIt->next();
10057       const int            nbNodes = face->NbCornerNodes();
10058       nnVec.resize( nbNodes );
10059       nnSet.clear();
10060       degenEdgeInd.clear();
10061       size_t maxZ = 0, minZ = std::numeric_limits<size_t>::max();
10062       SMDS_NodeIteratorPtr nIt = face->nodeIterator();
10063       for ( int iN = 0; iN < nbNodes; ++iN )
10064       {
10065         const SMDS_MeshNode* n = nIt->next();
10066         _LayerEdge*       edge = data._n2eMap[ n ];
10067         const int i = isReversedFace ? nbNodes-1-iN : iN;
10068         nnVec[ i ] = & edge->_nodes;
10069         maxZ = std::max( maxZ, nnVec[ i ]->size() );
10070         minZ = std::min( minZ, nnVec[ i ]->size() );
10071
10072         if ( helper.HasDegeneratedEdges() )
10073           nnSet.insert( nnVec[ i ]);
10074       }
10075
10076       if ( maxZ == 0 )
10077         continue;
10078       if ( 0 < nnSet.size() && nnSet.size() < 3 )
10079         continue;
10080
10081       switch ( nbNodes )
10082       {
10083       case 3: // TRIA
10084       {
10085         // PENTA
10086         for ( size_t iZ = 1; iZ < minZ; ++iZ )
10087           helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1], (*nnVec[2])[iZ-1],
10088                             (*nnVec[0])[iZ],   (*nnVec[1])[iZ],   (*nnVec[2])[iZ]);
10089
10090         for ( size_t iZ = minZ; iZ < maxZ; ++iZ )
10091         {
10092           for ( int iN = 0; iN < nbNodes; ++iN )
10093             if ( nnVec[ iN ]->size() < iZ+1 )
10094               degenEdgeInd.insert( iN );
10095
10096           if ( degenEdgeInd.size() == 1 )  // PYRAM
10097           {
10098             int i2 = *degenEdgeInd.begin();
10099             int i0 = helper.WrapIndex( i2 - 1, nbNodes );
10100             int i1 = helper.WrapIndex( i2 + 1, nbNodes );
10101             helper.AddVolume( (*nnVec[i0])[iZ-1], (*nnVec[i1])[iZ-1],
10102                               (*nnVec[i1])[iZ  ], (*nnVec[i0])[iZ  ], (*nnVec[i2]).back());
10103           }
10104           else  // TETRA
10105           {
10106             int i3 = !degenEdgeInd.count(0) ? 0 : !degenEdgeInd.count(1) ? 1 : 2;
10107             helper.AddVolume( (*nnVec[  0 ])[ i3 == 0 ? iZ-1 : nnVec[0]->size()-1 ],
10108                               (*nnVec[  1 ])[ i3 == 1 ? iZ-1 : nnVec[1]->size()-1 ],
10109                               (*nnVec[  2 ])[ i3 == 2 ? iZ-1 : nnVec[2]->size()-1 ],
10110                               (*nnVec[ i3 ])[ iZ ]);
10111           }
10112         }
10113         break; // TRIA
10114       }
10115       case 4: // QUAD
10116       {
10117         // HEX
10118         for ( size_t iZ = 1; iZ < minZ; ++iZ )
10119           helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1],
10120                             (*nnVec[2])[iZ-1], (*nnVec[3])[iZ-1],
10121                             (*nnVec[0])[iZ],   (*nnVec[1])[iZ],
10122                             (*nnVec[2])[iZ],   (*nnVec[3])[iZ]);
10123
10124         for ( size_t iZ = minZ; iZ < maxZ; ++iZ )
10125         {
10126           for ( int iN = 0; iN < nbNodes; ++iN )
10127             if ( nnVec[ iN ]->size() < iZ+1 )
10128               degenEdgeInd.insert( iN );
10129
10130           switch ( degenEdgeInd.size() )
10131           {
10132           case 2: // PENTA
10133           {
10134             int i2 = *degenEdgeInd.begin();
10135             int i3 = *degenEdgeInd.rbegin();
10136             bool ok = ( i3 - i2 == 1 );
10137             if ( i2 == 0 && i3 == 3 ) { i2 = 3; i3 = 0; ok = true; }
10138             int i0 = helper.WrapIndex( i3 + 1, nbNodes );
10139             int i1 = helper.WrapIndex( i0 + 1, nbNodes );
10140
10141             const SMDS_MeshElement* vol =
10142               helper.AddVolume( nnVec[i3]->back(), (*nnVec[i0])[iZ], (*nnVec[i0])[iZ-1],
10143                                 nnVec[i2]->back(), (*nnVec[i1])[iZ], (*nnVec[i1])[iZ-1]);
10144             if ( !ok && vol )
10145               degenVols.push_back( vol );
10146           }
10147           break;
10148
10149           default: // degen HEX
10150           {
10151             const SMDS_MeshElement* vol =
10152               helper.AddVolume( nnVec[0]->size() > iZ-1 ? (*nnVec[0])[iZ-1] : nnVec[0]->back(),
10153                                 nnVec[1]->size() > iZ-1 ? (*nnVec[1])[iZ-1] : nnVec[1]->back(),
10154                                 nnVec[2]->size() > iZ-1 ? (*nnVec[2])[iZ-1] : nnVec[2]->back(),
10155                                 nnVec[3]->size() > iZ-1 ? (*nnVec[3])[iZ-1] : nnVec[3]->back(),
10156                                 nnVec[0]->size() > iZ   ? (*nnVec[0])[iZ]   : nnVec[0]->back(),
10157                                 nnVec[1]->size() > iZ   ? (*nnVec[1])[iZ]   : nnVec[1]->back(),
10158                                 nnVec[2]->size() > iZ   ? (*nnVec[2])[iZ]   : nnVec[2]->back(),
10159                                 nnVec[3]->size() > iZ   ? (*nnVec[3])[iZ]   : nnVec[3]->back());
10160             degenVols.push_back( vol );
10161           }
10162           }
10163         }
10164         break; // HEX
10165       }
10166       default:
10167         return error("Not supported type of element", data._index);
10168
10169       } // switch ( nbNodes )
10170     } // while ( fIt->more() )
10171   } // loop on FACEs
10172
10173   if ( !degenVols.empty() )
10174   {
10175     SMESH_ComputeErrorPtr& err = _mesh->GetSubMesh( data._solid )->GetComputeError();
10176     if ( !err || err->IsOK() )
10177     {
10178       err.reset( new SMESH_ComputeError( COMPERR_WARNING,
10179                                          "Bad quality volumes created" ));
10180       err->myBadElements.insert( err->myBadElements.end(),
10181                                  degenVols.begin(),degenVols.end() );
10182     }
10183   }
10184
10185   return true;
10186 }
10187
10188 //================================================================================
10189 /*!
10190  * \brief Shrink 2D mesh on faces to let space for inflated layers
10191  */
10192 //================================================================================
10193
10194 bool _ViscousBuilder::shrink(_SolidData& theData)
10195 {
10196   // make map of (ids of FACEs to shrink mesh on) to (list of _SolidData containing
10197   // _LayerEdge's inflated along FACE or EDGE)
10198   map< TGeomID, list< _SolidData* > > f2sdMap;
10199   for ( size_t i = 0 ; i < _sdVec.size(); ++i )
10200   {
10201     _SolidData& data = _sdVec[i];
10202     map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
10203     for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
10204       if ( s2s->second.ShapeType() == TopAbs_FACE && !_shrinkedFaces.Contains( s2s->second ))
10205       {
10206         f2sdMap[ getMeshDS()->ShapeToIndex( s2s->second )].push_back( &data );
10207
10208         // Put mesh faces on the shrinked FACE to the proxy sub-mesh to avoid
10209         // usage of mesh faces made in addBoundaryElements() by the 3D algo or
10210         // by StdMeshers_QuadToTriaAdaptor
10211         if ( SMESHDS_SubMesh* smDS = getMeshDS()->MeshElements( s2s->second ))
10212         {
10213           SMESH_ProxyMesh::SubMesh* proxySub =
10214             data._proxyMesh->getFaceSubM( TopoDS::Face( s2s->second ), /*create=*/true);
10215           if ( proxySub->NbElements() == 0 )
10216           {
10217             SMDS_ElemIteratorPtr fIt = smDS->GetElements();
10218             while ( fIt->more() )
10219             {
10220               const SMDS_MeshElement* f = fIt->next();
10221               // as a result 3D algo will use elements from proxySub and not from smDS
10222               proxySub->AddElement( f );
10223               f->setIsMarked( true );
10224
10225               // Mark nodes on the FACE to discriminate them from nodes
10226               // added by addBoundaryElements(); marked nodes are to be smoothed while shrink()
10227               for ( int iN = 0, nbN = f->NbNodes(); iN < nbN; ++iN )
10228               {
10229                 const SMDS_MeshNode* n = f->GetNode( iN );
10230                 if ( n->GetPosition()->GetDim() == 2 )
10231                   n->setIsMarked( true );
10232               }
10233             }
10234           }
10235         }
10236       }
10237   }
10238
10239   SMESH_MesherHelper helper( *_mesh );
10240   helper.ToFixNodeParameters( true );
10241
10242   // EDGEs to shrink
10243   map< TGeomID, _Shrinker1D > e2shrMap;
10244   vector< _EdgesOnShape* > subEOS;
10245   vector< _LayerEdge* > lEdges;
10246
10247   // loop on FACEs to srink mesh on
10248   map< TGeomID, list< _SolidData* > >::iterator f2sd = f2sdMap.begin();
10249   for ( ; f2sd != f2sdMap.end(); ++f2sd )
10250   {
10251     list< _SolidData* > & dataList = f2sd->second;
10252     if ( dataList.front()->_n2eMap.empty() ||
10253          dataList.back() ->_n2eMap.empty() )
10254       continue; // not yet computed
10255     if ( dataList.front() != &theData &&
10256          dataList.back()  != &theData )
10257       continue;
10258
10259     _SolidData&      data = *dataList.front();
10260     _SolidData*     data2 = dataList.size() > 1 ? dataList.back() : 0;
10261     const TopoDS_Face&  F = TopoDS::Face( getMeshDS()->IndexToShape( f2sd->first ));
10262     SMESH_subMesh*     sm = _mesh->GetSubMesh( F );
10263     SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
10264
10265     Handle(Geom_Surface) surface = BRep_Tool::Surface( F );
10266
10267     _shrinkedFaces.Add( F );
10268     helper.SetSubShape( F );
10269
10270     // ===========================
10271     // Prepare data for shrinking
10272     // ===========================
10273
10274     // Collect nodes to smooth (they are marked at the beginning of this method)
10275     vector < const SMDS_MeshNode* > smoothNodes;
10276     {
10277       SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
10278       while ( nIt->more() )
10279       {
10280         const SMDS_MeshNode* n = nIt->next();
10281         if ( n->isMarked() )
10282           smoothNodes.push_back( n );
10283       }
10284     }
10285     // Find out face orientation
10286     double refSign = 1;
10287     const set<TGeomID> ignoreShapes;
10288     bool isOkUV;
10289     if ( !smoothNodes.empty() )
10290     {
10291       vector<_Simplex> simplices;
10292       _Simplex::GetSimplices( smoothNodes[0], simplices, ignoreShapes );
10293       helper.GetNodeUV( F, simplices[0]._nPrev, 0, &isOkUV ); // fix UV of simplex nodes
10294       helper.GetNodeUV( F, simplices[0]._nNext, 0, &isOkUV );
10295       gp_XY uv = helper.GetNodeUV( F, smoothNodes[0], 0, &isOkUV );
10296       if ( !simplices[0].IsForward(uv, smoothNodes[0], F, helper, refSign ))
10297         refSign = -1;
10298     }
10299
10300     // Find _LayerEdge's inflated along F
10301     subEOS.clear();
10302     lEdges.clear();
10303     {
10304       SMESH_subMeshIteratorPtr subIt = sm->getDependsOnIterator(/*includeSelf=*/false,
10305                                                                 /*complexFirst=*/true); //!!!
10306       while ( subIt->more() )
10307       {
10308         const TGeomID subID = subIt->next()->GetId();
10309         if ( data._noShrinkShapes.count( subID ))
10310           continue;
10311         _EdgesOnShape* eos = data.GetShapeEdges( subID );
10312         if ( !eos || eos->_sWOL.IsNull() )
10313           if ( data2 ) // check in adjacent SOLID
10314           {
10315             eos = data2->GetShapeEdges( subID );
10316             if ( !eos || eos->_sWOL.IsNull() )
10317               continue;
10318           }
10319         subEOS.push_back( eos );
10320
10321         for ( size_t i = 0; i < eos->_edges.size(); ++i )
10322         {
10323           lEdges.push_back( eos->_edges[ i ] );
10324           prepareEdgeToShrink( *eos->_edges[ i ], *eos, helper, smDS );
10325         }
10326       }
10327     }
10328
10329     dumpFunction(SMESH_Comment("beforeShrinkFace")<<f2sd->first); // debug
10330     SMDS_ElemIteratorPtr fIt = smDS->GetElements();
10331     while ( fIt->more() )
10332       if ( const SMDS_MeshElement* f = fIt->next() )
10333         dumpChangeNodes( f );
10334     dumpFunctionEnd();
10335
10336     // Replace source nodes by target nodes in mesh faces to shrink
10337     dumpFunction(SMESH_Comment("replNodesOnFace")<<f2sd->first); // debug
10338     const SMDS_MeshNode* nodes[20];
10339     for ( size_t iS = 0; iS < subEOS.size(); ++iS )
10340     {
10341       _EdgesOnShape& eos = * subEOS[ iS ];
10342       for ( size_t i = 0; i < eos._edges.size(); ++i )
10343       {
10344         _LayerEdge& edge = *eos._edges[i];
10345         const SMDS_MeshNode* srcNode = edge._nodes[0];
10346         const SMDS_MeshNode* tgtNode = edge._nodes.back();
10347         SMDS_ElemIteratorPtr fIt = srcNode->GetInverseElementIterator(SMDSAbs_Face);
10348         while ( fIt->more() )
10349         {
10350           const SMDS_MeshElement* f = fIt->next();
10351           if ( !smDS->Contains( f ) || !f->isMarked() )
10352             continue;
10353           SMDS_NodeIteratorPtr nIt = f->nodeIterator();
10354           for ( int iN = 0; nIt->more(); ++iN )
10355           {
10356             const SMDS_MeshNode* n = nIt->next();
10357             nodes[iN] = ( n == srcNode ? tgtNode : n );
10358           }
10359           helper.GetMeshDS()->ChangeElementNodes( f, nodes, f->NbNodes() );
10360           dumpChangeNodes( f );
10361         }
10362       }
10363     }
10364     dumpFunctionEnd();
10365
10366     // find out if a FACE is concave
10367     const bool isConcaveFace = isConcave( F, helper );
10368
10369     // Create _SmoothNode's on face F
10370     vector< _SmoothNode > nodesToSmooth( smoothNodes.size() );
10371     {
10372       dumpFunction(SMESH_Comment("fixUVOnFace")<<f2sd->first); // debug
10373       const bool sortSimplices = isConcaveFace;
10374       for ( size_t i = 0; i < smoothNodes.size(); ++i )
10375       {
10376         const SMDS_MeshNode* n = smoothNodes[i];
10377         nodesToSmooth[ i ]._node = n;
10378         // src nodes must be already replaced by tgt nodes to have tgt nodes in _simplices
10379         _Simplex::GetSimplices( n, nodesToSmooth[ i ]._simplices, ignoreShapes, 0, sortSimplices);
10380         // fix up incorrect uv of nodes on the FACE
10381         helper.GetNodeUV( F, n, 0, &isOkUV);
10382         dumpMove( n );
10383       }
10384       dumpFunctionEnd();
10385     }
10386     //if ( nodesToSmooth.empty() ) continue;
10387
10388     // Find EDGE's to shrink and set simpices to LayerEdge's
10389     set< _Shrinker1D* > eShri1D;
10390     {
10391       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
10392       {
10393         _EdgesOnShape& eos = * subEOS[ iS ];
10394         if ( eos.SWOLType() == TopAbs_EDGE )
10395         {
10396           SMESH_subMesh* edgeSM = _mesh->GetSubMesh( eos._sWOL );
10397           _Shrinker1D& srinker  = e2shrMap[ edgeSM->GetId() ];
10398           eShri1D.insert( & srinker );
10399           srinker.AddEdge( eos._edges[0], eos, helper );
10400           VISCOUS_3D::ToClearSubWithMain( edgeSM, data._solid );
10401           // restore params of nodes on EGDE if the EDGE has been already
10402           // srinked while srinking other FACE
10403           srinker.RestoreParams();
10404         }
10405         for ( size_t i = 0; i < eos._edges.size(); ++i )
10406         {
10407           _LayerEdge& edge = * eos._edges[i];
10408           _Simplex::GetSimplices( /*tgtNode=*/edge._nodes.back(), edge._simplices, ignoreShapes );
10409
10410           // additionally mark tgt node; only marked nodes will be used in SetNewLength2d()
10411           // not-marked nodes are those added by refine()
10412           edge._nodes.back()->setIsMarked( true );
10413         }
10414       }
10415     }
10416
10417     bool toFixTria = false; // to improve quality of trias by diagonal swap
10418     if ( isConcaveFace )
10419     {
10420       const bool hasTria = _mesh->NbTriangles(), hasQuad = _mesh->NbQuadrangles();
10421       if ( hasTria != hasQuad ) {
10422         toFixTria = hasTria;
10423       }
10424       else {
10425         set<int> nbNodesSet;
10426         SMDS_ElemIteratorPtr fIt = smDS->GetElements();
10427         while ( fIt->more() && nbNodesSet.size() < 2 )
10428           nbNodesSet.insert( fIt->next()->NbCornerNodes() );
10429         toFixTria = ( *nbNodesSet.begin() == 3 );
10430       }
10431     }
10432
10433     // ==================
10434     // Perform shrinking
10435     // ==================
10436
10437     bool shrinked = true;
10438     int nbBad, shriStep=0, smooStep=0;
10439     _SmoothNode::SmoothType smoothType
10440       = isConcaveFace ? _SmoothNode::ANGULAR : _SmoothNode::LAPLACIAN;
10441     SMESH_Comment errMsg;
10442     while ( shrinked )
10443     {
10444       shriStep++;
10445       // Move boundary nodes (actually just set new UV)
10446       // -----------------------------------------------
10447       dumpFunction(SMESH_Comment("moveBoundaryOnF")<<f2sd->first<<"_st"<<shriStep ); // debug
10448       shrinked = false;
10449       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
10450       {
10451         _EdgesOnShape& eos = * subEOS[ iS ];
10452         for ( size_t i = 0; i < eos._edges.size(); ++i )
10453         {
10454           shrinked |= eos._edges[i]->SetNewLength2d( surface, F, eos, helper );
10455         }
10456       }
10457       dumpFunctionEnd();
10458
10459       // Move nodes on EDGE's
10460       // (XYZ is set as soon as a needed length reached in SetNewLength2d())
10461       set< _Shrinker1D* >::iterator shr = eShri1D.begin();
10462       for ( ; shr != eShri1D.end(); ++shr )
10463         (*shr)->Compute( /*set3D=*/false, helper );
10464
10465       // Smoothing in 2D
10466       // -----------------
10467       int nbNoImpSteps = 0;
10468       bool       moved = true;
10469       nbBad = 1;
10470       while (( nbNoImpSteps < 5 && nbBad > 0) && moved)
10471       {
10472         dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
10473
10474         int oldBadNb = nbBad;
10475         nbBad = 0;
10476         moved = false;
10477         // '% 5' minimizes NB FUNCTIONS on viscous_layers_00/B2 case
10478         _SmoothNode::SmoothType smooTy = ( smooStep % 5 ) ? smoothType : _SmoothNode::LAPLACIAN;
10479         for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
10480         {
10481           moved |= nodesToSmooth[i].Smooth( nbBad, surface, helper, refSign,
10482                                             smooTy, /*set3D=*/isConcaveFace);
10483         }
10484         if ( nbBad < oldBadNb )
10485           nbNoImpSteps = 0;
10486         else
10487           nbNoImpSteps++;
10488
10489         dumpFunctionEnd();
10490       }
10491
10492       errMsg.clear();
10493       if ( nbBad > 0 )
10494         errMsg << "Can't shrink 2D mesh on face " << f2sd->first;
10495       if ( shriStep > 200 )
10496         errMsg << "Infinite loop at shrinking 2D mesh on face " << f2sd->first;
10497       if ( !errMsg.empty() )
10498         break;
10499
10500       // Fix narrow triangles by swapping diagonals
10501       // ---------------------------------------
10502       if ( toFixTria )
10503       {
10504         set<const SMDS_MeshNode*> usedNodes;
10505         fixBadFaces( F, helper, /*is2D=*/true, shriStep, & usedNodes); // swap diagonals
10506
10507         // update working data
10508         set<const SMDS_MeshNode*>::iterator n;
10509         for ( size_t i = 0; i < nodesToSmooth.size() && !usedNodes.empty(); ++i )
10510         {
10511           n = usedNodes.find( nodesToSmooth[ i ]._node );
10512           if ( n != usedNodes.end())
10513           {
10514             _Simplex::GetSimplices( nodesToSmooth[ i ]._node,
10515                                     nodesToSmooth[ i ]._simplices,
10516                                     ignoreShapes, NULL,
10517                                     /*sortSimplices=*/ smoothType == _SmoothNode::ANGULAR );
10518             usedNodes.erase( n );
10519           }
10520         }
10521         for ( size_t i = 0; i < lEdges.size() && !usedNodes.empty(); ++i )
10522         {
10523           n = usedNodes.find( /*tgtNode=*/ lEdges[i]->_nodes.back() );
10524           if ( n != usedNodes.end())
10525           {
10526             _Simplex::GetSimplices( lEdges[i]->_nodes.back(),
10527                                     lEdges[i]->_simplices,
10528                                     ignoreShapes );
10529             usedNodes.erase( n );
10530           }
10531         }
10532       }
10533       // TODO: check effect of this additional smooth
10534       // additional laplacian smooth to increase allowed shrink step
10535       // for ( int st = 1; st; --st )
10536       // {
10537       //   dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
10538       //   for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
10539       //   {
10540       //     nodesToSmooth[i].Smooth( nbBad,surface,helper,refSign,
10541       //                              _SmoothNode::LAPLACIAN,/*set3D=*/false);
10542       //   }
10543       // }
10544
10545     } // while ( shrinked )
10546
10547     if ( !errMsg.empty() ) // Try to re-compute the shrink FACE
10548     {
10549       debugMsg( "Re-compute FACE " << f2sd->first << " because " << errMsg );
10550
10551       // remove faces
10552       SMESHDS_SubMesh* psm = data._proxyMesh->getFaceSubM( F );
10553       {
10554         vector< const SMDS_MeshElement* > facesToRm;
10555         if ( psm )
10556         {
10557           facesToRm.reserve( psm->NbElements() );
10558           for ( SMDS_ElemIteratorPtr ite = psm->GetElements(); ite->more(); )
10559             facesToRm.push_back( ite->next() );
10560
10561           for ( size_t i = 0 ; i < _sdVec.size(); ++i )
10562             if (( psm = _sdVec[i]._proxyMesh->getFaceSubM( F )))
10563               psm->Clear();
10564         }
10565         for ( size_t i = 0; i < facesToRm.size(); ++i )
10566           getMeshDS()->RemoveFreeElement( facesToRm[i], smDS, /*fromGroups=*/false );
10567       }
10568       // remove nodes
10569       {
10570         TIDSortedNodeSet nodesToKeep; // nodes of _LayerEdge to keep
10571         for ( size_t iS = 0; iS < subEOS.size(); ++iS ) {
10572           for ( size_t i = 0; i < subEOS[iS]->_edges.size(); ++i )
10573             nodesToKeep.insert( ++( subEOS[iS]->_edges[i]->_nodes.begin() ),
10574                                 subEOS[iS]->_edges[i]->_nodes.end() );
10575         }
10576         SMDS_NodeIteratorPtr itn = smDS->GetNodes();
10577         while ( itn->more() ) {
10578           const SMDS_MeshNode* n = itn->next();
10579           if ( !nodesToKeep.count( n ))
10580             getMeshDS()->RemoveFreeNode( n, smDS, /*fromGroups=*/false );
10581         }
10582       }
10583       // restore position and UV of target nodes
10584       gp_Pnt p;
10585       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
10586         for ( size_t i = 0; i < subEOS[iS]->_edges.size(); ++i )
10587         {
10588           _LayerEdge*       edge = subEOS[iS]->_edges[i];
10589           SMDS_MeshNode* tgtNode = const_cast< SMDS_MeshNode*& >( edge->_nodes.back() );
10590           if ( edge->_pos.empty() ||
10591                edge->Is( _LayerEdge::SHRUNK )) continue;
10592           if ( subEOS[iS]->SWOLType() == TopAbs_FACE )
10593           {
10594             SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( tgtNode->GetPosition() );
10595             pos->SetUParameter( edge->_pos[0].X() );
10596             pos->SetVParameter( edge->_pos[0].Y() );
10597             p = surface->Value( edge->_pos[0].X(), edge->_pos[0].Y() );
10598           }
10599           else
10600           {
10601             SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( tgtNode->GetPosition() );
10602             pos->SetUParameter( edge->_pos[0].Coord( U_TGT ));
10603             p = BRepAdaptor_Curve( TopoDS::Edge( subEOS[iS]->_sWOL )).Value( pos->GetUParameter() );
10604           }
10605           tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
10606           dumpMove( tgtNode );
10607         }
10608       // shrink EDGE sub-meshes and set proxy sub-meshes
10609       UVPtStructVec uvPtVec;
10610       set< _Shrinker1D* >::iterator shrIt = eShri1D.begin();
10611       for ( shrIt = eShri1D.begin(); shrIt != eShri1D.end(); ++shrIt )
10612       {
10613         _Shrinker1D* shr = (*shrIt);
10614         shr->Compute( /*set3D=*/true, helper );
10615
10616         // set proxy mesh of EDGEs w/o layers
10617         map< double, const SMDS_MeshNode* > nodes;
10618         SMESH_Algo::GetSortedNodesOnEdge( getMeshDS(), shr->GeomEdge(),/*skipMedium=*/true, nodes);
10619         // remove refinement nodes
10620         const SMDS_MeshNode* sn0 = shr->SrcNode(0), *sn1 = shr->SrcNode(1);
10621         const SMDS_MeshNode* tn0 = shr->TgtNode(0), *tn1 = shr->TgtNode(1);
10622         map< double, const SMDS_MeshNode* >::iterator u2n = nodes.begin();
10623         if ( u2n->second == sn0 || u2n->second == sn1 )
10624         {
10625           while ( u2n->second != tn0 && u2n->second != tn1 )
10626             ++u2n;
10627           nodes.erase( nodes.begin(), u2n );
10628         }
10629         u2n = --nodes.end();
10630         if ( u2n->second == sn0 || u2n->second == sn1 )
10631         {
10632           while ( u2n->second != tn0 && u2n->second != tn1 )
10633             --u2n;
10634           nodes.erase( ++u2n, nodes.end() );
10635         }
10636         // set proxy sub-mesh
10637         uvPtVec.resize( nodes.size() );
10638         u2n = nodes.begin();
10639         BRepAdaptor_Curve2d curve( shr->GeomEdge(), F );
10640         for ( size_t i = 0; i < nodes.size(); ++i, ++u2n )
10641         {
10642           uvPtVec[ i ].node = u2n->second;
10643           uvPtVec[ i ].param = u2n->first;
10644           uvPtVec[ i ].SetUV( curve.Value( u2n->first ).XY() );
10645         }
10646         StdMeshers_FaceSide fSide( uvPtVec, F, shr->GeomEdge(), _mesh );
10647         StdMeshers_ViscousLayers2D::SetProxyMeshOfEdge( fSide );
10648       }
10649
10650       // set proxy mesh of EDGEs with layers
10651       vector< _LayerEdge* > edges;
10652       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
10653       {
10654         _EdgesOnShape& eos = * subEOS[ iS ];
10655         if ( eos.ShapeType() != TopAbs_EDGE ) continue;
10656
10657         const TopoDS_Edge& E = TopoDS::Edge( eos._shape );
10658         data.SortOnEdge( E, eos._edges );
10659
10660         edges.clear();
10661         if ( _EdgesOnShape* eov = data.GetShapeEdges( helper.IthVertex( 0, E, /*CumOri=*/false )))
10662           if ( !eov->_edges.empty() )
10663             edges.push_back( eov->_edges[0] ); // on 1st VERTEX
10664
10665         edges.insert( edges.end(), eos._edges.begin(), eos._edges.end() );
10666
10667         if ( _EdgesOnShape* eov = data.GetShapeEdges( helper.IthVertex( 1, E, /*CumOri=*/false )))
10668           if ( !eov->_edges.empty() )
10669             edges.push_back( eov->_edges[0] ); // on last VERTEX
10670
10671         uvPtVec.resize( edges.size() );
10672         for ( size_t i = 0; i < edges.size(); ++i )
10673         {
10674           uvPtVec[ i ].node = edges[i]->_nodes.back();
10675           uvPtVec[ i ].param = helper.GetNodeU( E, edges[i]->_nodes[0] );
10676           uvPtVec[ i ].SetUV( helper.GetNodeUV( F, edges[i]->_nodes.back() ));
10677         }
10678         BRep_Tool::Range( E, uvPtVec[0].param, uvPtVec.back().param );
10679         StdMeshers_FaceSide fSide( uvPtVec, F, E, _mesh );
10680         StdMeshers_ViscousLayers2D::SetProxyMeshOfEdge( fSide );
10681       }
10682       // temporary clear the FACE sub-mesh from faces made by refine()
10683       vector< const SMDS_MeshElement* > elems;
10684       elems.reserve( smDS->NbElements() + smDS->NbNodes() );
10685       for ( SMDS_ElemIteratorPtr ite = smDS->GetElements(); ite->more(); )
10686         elems.push_back( ite->next() );
10687       for ( SMDS_NodeIteratorPtr ite = smDS->GetNodes(); ite->more(); )
10688         elems.push_back( ite->next() );
10689       smDS->Clear();
10690
10691       // compute the mesh on the FACE
10692       sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
10693       sm->ComputeStateEngine( SMESH_subMesh::COMPUTE_SUBMESH );
10694
10695       // re-fill proxy sub-meshes of the FACE
10696       for ( size_t i = 0 ; i < _sdVec.size(); ++i )
10697         if (( psm = _sdVec[i]._proxyMesh->getFaceSubM( F )))
10698           for ( SMDS_ElemIteratorPtr ite = smDS->GetElements(); ite->more(); )
10699             psm->AddElement( ite->next() );
10700
10701       // re-fill smDS
10702       for ( size_t i = 0; i < elems.size(); ++i )
10703         smDS->AddElement( elems[i] );
10704
10705       if ( sm->GetComputeState() != SMESH_subMesh::COMPUTE_OK )
10706         return error( errMsg );
10707
10708     } // end of re-meshing in case of failed smoothing
10709     else
10710     {
10711       // No wrongly shaped faces remain; final smooth. Set node XYZ.
10712       bool isStructuredFixed = false;
10713       if ( SMESH_2D_Algo* algo = dynamic_cast<SMESH_2D_Algo*>( sm->GetAlgo() ))
10714         isStructuredFixed = algo->FixInternalNodes( *data._proxyMesh, F );
10715       if ( !isStructuredFixed )
10716       {
10717         if ( isConcaveFace ) // fix narrow faces by swapping diagonals
10718           fixBadFaces( F, helper, /*is2D=*/false, ++shriStep );
10719
10720         for ( int st = 3; st; --st )
10721         {
10722           switch( st ) {
10723           case 1: smoothType = _SmoothNode::LAPLACIAN; break;
10724           case 2: smoothType = _SmoothNode::LAPLACIAN; break;
10725           case 3: smoothType = _SmoothNode::ANGULAR; break;
10726           }
10727           dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
10728           for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
10729           {
10730             nodesToSmooth[i].Smooth( nbBad,surface,helper,refSign,
10731                                      smoothType,/*set3D=*/st==1 );
10732           }
10733           dumpFunctionEnd();
10734         }
10735       }
10736       if ( !getMeshDS()->IsEmbeddedMode() )
10737         // Log node movement
10738         for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
10739         {
10740           SMESH_TNodeXYZ p ( nodesToSmooth[i]._node );
10741           getMeshDS()->MoveNode( nodesToSmooth[i]._node, p.X(), p.Y(), p.Z() );
10742         }
10743     }
10744
10745     // Set an event listener to clear FACE sub-mesh together with SOLID sub-mesh
10746     VISCOUS_3D::ToClearSubWithMain( sm, data._solid );
10747     if ( data2 )
10748       VISCOUS_3D::ToClearSubWithMain( sm, data2->_solid );
10749
10750   } // loop on FACES to srink mesh on
10751
10752
10753   // Replace source nodes by target nodes in shrinked mesh edges
10754
10755   map< int, _Shrinker1D >::iterator e2shr = e2shrMap.begin();
10756   for ( ; e2shr != e2shrMap.end(); ++e2shr )
10757     e2shr->second.SwapSrcTgtNodes( getMeshDS() );
10758
10759   return true;
10760 }
10761
10762 //================================================================================
10763 /*!
10764  * \brief Computes 2d shrink direction and finds nodes limiting shrinking
10765  */
10766 //================================================================================
10767
10768 bool _ViscousBuilder::prepareEdgeToShrink( _LayerEdge&            edge,
10769                                            _EdgesOnShape&         eos,
10770                                            SMESH_MesherHelper&    helper,
10771                                            const SMESHDS_SubMesh* faceSubMesh)
10772 {
10773   const SMDS_MeshNode* srcNode = edge._nodes[0];
10774   const SMDS_MeshNode* tgtNode = edge._nodes.back();
10775
10776   if ( eos.SWOLType() == TopAbs_FACE )
10777   {
10778     if ( tgtNode->GetPosition()->GetDim() != 2 ) // not inflated edge
10779     {
10780       edge._pos.clear();
10781       edge.Set( _LayerEdge::SHRUNK );
10782       return srcNode == tgtNode;
10783     }
10784     gp_XY srcUV ( edge._pos[0].X(), edge._pos[0].Y() );          //helper.GetNodeUV( F, srcNode );
10785     gp_XY tgtUV = edge.LastUV( TopoDS::Face( eos._sWOL ), eos ); //helper.GetNodeUV( F, tgtNode );
10786     gp_Vec2d uvDir( srcUV, tgtUV );
10787     double uvLen = uvDir.Magnitude();
10788     uvDir /= uvLen;
10789     edge._normal.SetCoord( uvDir.X(),uvDir.Y(), 0 );
10790     edge._len = uvLen;
10791
10792     //edge._pos.resize(1);
10793     edge._pos[0].SetCoord( tgtUV.X(), tgtUV.Y(), 0 );
10794
10795     // set UV of source node to target node
10796     SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( tgtNode->GetPosition() );
10797     pos->SetUParameter( srcUV.X() );
10798     pos->SetVParameter( srcUV.Y() );
10799   }
10800   else // _sWOL is TopAbs_EDGE
10801   {
10802     if ( tgtNode->GetPosition()->GetDim() != 1 ) // not inflated edge
10803     {
10804       edge._pos.clear();
10805       edge.Set( _LayerEdge::SHRUNK );
10806       return srcNode == tgtNode;
10807     }
10808     const TopoDS_Edge&    E = TopoDS::Edge( eos._sWOL );
10809     SMESHDS_SubMesh* edgeSM = getMeshDS()->MeshElements( E );
10810     if ( !edgeSM || edgeSM->NbElements() == 0 )
10811       return error(SMESH_Comment("Not meshed EDGE ") << getMeshDS()->ShapeToIndex( E ));
10812
10813     const SMDS_MeshNode* n2 = 0;
10814     SMDS_ElemIteratorPtr eIt = srcNode->GetInverseElementIterator(SMDSAbs_Edge);
10815     while ( eIt->more() && !n2 )
10816     {
10817       const SMDS_MeshElement* e = eIt->next();
10818       if ( !edgeSM->Contains(e)) continue;
10819       n2 = e->GetNode( 0 );
10820       if ( n2 == srcNode ) n2 = e->GetNode( 1 );
10821     }
10822     if ( !n2 )
10823       return error(SMESH_Comment("Wrongly meshed EDGE ") << getMeshDS()->ShapeToIndex( E ));
10824
10825     double uSrc = helper.GetNodeU( E, srcNode, n2 );
10826     double uTgt = helper.GetNodeU( E, tgtNode, srcNode );
10827     double u2   = helper.GetNodeU( E, n2,      srcNode );
10828
10829     //edge._pos.clear();
10830
10831     if ( fabs( uSrc-uTgt ) < 0.99 * fabs( uSrc-u2 ))
10832     {
10833       // tgtNode is located so that it does not make faces with wrong orientation
10834       edge.Set( _LayerEdge::SHRUNK );
10835       return true;
10836     }
10837     //edge._pos.resize(1);
10838     edge._pos[0].SetCoord( U_TGT, uTgt );
10839     edge._pos[0].SetCoord( U_SRC, uSrc );
10840     edge._pos[0].SetCoord( LEN_TGT, fabs( uSrc-uTgt ));
10841
10842     edge._simplices.resize( 1 );
10843     edge._simplices[0]._nPrev = n2;
10844
10845     // set U of source node to the target node
10846     SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( tgtNode->GetPosition() );
10847     pos->SetUParameter( uSrc );
10848   }
10849   return true;
10850 }
10851
10852 //================================================================================
10853 /*!
10854  * \brief Restore position of a sole node of a _LayerEdge based on _noShrinkShapes
10855  */
10856 //================================================================================
10857
10858 void _ViscousBuilder::restoreNoShrink( _LayerEdge& edge ) const
10859 {
10860   if ( edge._nodes.size() == 1 )
10861   {
10862     edge._pos.clear();
10863     edge._len = 0;
10864
10865     const SMDS_MeshNode* srcNode = edge._nodes[0];
10866     TopoDS_Shape S = SMESH_MesherHelper::GetSubShapeByNode( srcNode, getMeshDS() );
10867     if ( S.IsNull() ) return;
10868
10869     gp_Pnt p;
10870
10871     switch ( S.ShapeType() )
10872     {
10873     case TopAbs_EDGE:
10874     {
10875       double f,l;
10876       TopLoc_Location loc;
10877       Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( S ), loc, f, l );
10878       if ( curve.IsNull() ) return;
10879       SMDS_EdgePosition* ePos = static_cast<SMDS_EdgePosition*>( srcNode->GetPosition() );
10880       p = curve->Value( ePos->GetUParameter() );
10881       break;
10882     }
10883     case TopAbs_VERTEX:
10884     {
10885       p = BRep_Tool::Pnt( TopoDS::Vertex( S ));
10886       break;
10887     }
10888     default: return;
10889     }
10890     getMeshDS()->MoveNode( srcNode, p.X(), p.Y(), p.Z() );
10891     dumpMove( srcNode );
10892   }
10893 }
10894
10895 //================================================================================
10896 /*!
10897  * \brief Try to fix triangles with high aspect ratio by swaping diagonals
10898  */
10899 //================================================================================
10900
10901 void _ViscousBuilder::fixBadFaces(const TopoDS_Face&          F,
10902                                   SMESH_MesherHelper&         helper,
10903                                   const bool                  is2D,
10904                                   const int                   step,
10905                                   set<const SMDS_MeshNode*> * involvedNodes)
10906 {
10907   SMESH::Controls::AspectRatio qualifier;
10908   SMESH::Controls::TSequenceOfXYZ points(3), points1(3), points2(3);
10909   const double maxAspectRatio = is2D ? 4. : 2;
10910   _NodeCoordHelper xyz( F, helper, is2D );
10911
10912   // find bad triangles
10913
10914   vector< const SMDS_MeshElement* > badTrias;
10915   vector< double >                  badAspects;
10916   SMESHDS_SubMesh*      sm = helper.GetMeshDS()->MeshElements( F );
10917   SMDS_ElemIteratorPtr fIt = sm->GetElements();
10918   while ( fIt->more() )
10919   {
10920     const SMDS_MeshElement * f = fIt->next();
10921     if ( f->NbCornerNodes() != 3 ) continue;
10922     for ( int iP = 0; iP < 3; ++iP ) points(iP+1) = xyz( f->GetNode(iP));
10923     double aspect = qualifier.GetValue( points );
10924     if ( aspect > maxAspectRatio )
10925     {
10926       badTrias.push_back( f );
10927       badAspects.push_back( aspect );
10928     }
10929   }
10930   if ( step == 1 )
10931   {
10932     dumpFunction(SMESH_Comment("beforeSwapDiagonals_F")<<helper.GetSubShapeID());
10933     SMDS_ElemIteratorPtr fIt = sm->GetElements();
10934     while ( fIt->more() )
10935     {
10936       const SMDS_MeshElement * f = fIt->next();
10937       if ( f->NbCornerNodes() == 3 )
10938         dumpChangeNodes( f );
10939     }
10940     dumpFunctionEnd();
10941   }
10942   if ( badTrias.empty() )
10943     return;
10944
10945   // find couples of faces to swap diagonal
10946
10947   typedef pair < const SMDS_MeshElement* , const SMDS_MeshElement* > T2Trias;
10948   vector< T2Trias > triaCouples; 
10949
10950   TIDSortedElemSet involvedFaces, emptySet;
10951   for ( size_t iTia = 0; iTia < badTrias.size(); ++iTia )
10952   {
10953     T2Trias trias    [3];
10954     double  aspRatio [3];
10955     int i1, i2, i3;
10956
10957     if ( !involvedFaces.insert( badTrias[iTia] ).second )
10958       continue;
10959     for ( int iP = 0; iP < 3; ++iP )
10960       points(iP+1) = xyz( badTrias[iTia]->GetNode(iP));
10961
10962     // find triangles adjacent to badTrias[iTia] with better aspect ratio after diag-swaping
10963     int bestCouple = -1;
10964     for ( int iSide = 0; iSide < 3; ++iSide )
10965     {
10966       const SMDS_MeshNode* n1 = badTrias[iTia]->GetNode( iSide );
10967       const SMDS_MeshNode* n2 = badTrias[iTia]->GetNode(( iSide+1 ) % 3 );
10968       trias [iSide].first  = badTrias[iTia];
10969       trias [iSide].second = SMESH_MeshAlgos::FindFaceInSet( n1, n2, emptySet, involvedFaces,
10970                                                              & i1, & i2 );
10971       if (( ! trias[iSide].second ) ||
10972           ( trias[iSide].second->NbCornerNodes() != 3 ) ||
10973           ( ! sm->Contains( trias[iSide].second )))
10974         continue;
10975
10976       // aspect ratio of an adjacent tria
10977       for ( int iP = 0; iP < 3; ++iP )
10978         points2(iP+1) = xyz( trias[iSide].second->GetNode(iP));
10979       double aspectInit = qualifier.GetValue( points2 );
10980
10981       // arrange nodes as after diag-swaping
10982       if ( helper.WrapIndex( i1+1, 3 ) == i2 )
10983         i3 = helper.WrapIndex( i1-1, 3 );
10984       else
10985         i3 = helper.WrapIndex( i1+1, 3 );
10986       points1 = points;
10987       points1( 1+ iSide ) = points2( 1+ i3 );
10988       points2( 1+ i2    ) = points1( 1+ ( iSide+2 ) % 3 );
10989
10990       // aspect ratio after diag-swaping
10991       aspRatio[ iSide ] = qualifier.GetValue( points1 ) + qualifier.GetValue( points2 );
10992       if ( aspRatio[ iSide ] > aspectInit + badAspects[ iTia ] )
10993         continue;
10994
10995       // prevent inversion of a triangle
10996       gp_Vec norm1 = gp_Vec( points1(1), points1(3) ) ^ gp_Vec( points1(1), points1(2) );
10997       gp_Vec norm2 = gp_Vec( points2(1), points2(3) ) ^ gp_Vec( points2(1), points2(2) );
10998       if ( norm1 * norm2 < 0. && norm1.Angle( norm2 ) > 70./180.*M_PI )
10999         continue;
11000
11001       if ( bestCouple < 0 || aspRatio[ bestCouple ] > aspRatio[ iSide ] )
11002         bestCouple = iSide;
11003     }
11004
11005     if ( bestCouple >= 0 )
11006     {
11007       triaCouples.push_back( trias[bestCouple] );
11008       involvedFaces.insert ( trias[bestCouple].second );
11009     }
11010     else
11011     {
11012       involvedFaces.erase( badTrias[iTia] );
11013     }
11014   }
11015   if ( triaCouples.empty() )
11016     return;
11017
11018   // swap diagonals
11019
11020   SMESH_MeshEditor editor( helper.GetMesh() );
11021   dumpFunction(SMESH_Comment("beforeSwapDiagonals_F")<<helper.GetSubShapeID()<<"_"<<step);
11022   for ( size_t i = 0; i < triaCouples.size(); ++i )
11023   {
11024     dumpChangeNodes( triaCouples[i].first );
11025     dumpChangeNodes( triaCouples[i].second );
11026     editor.InverseDiag( triaCouples[i].first, triaCouples[i].second );
11027   }
11028
11029   if ( involvedNodes )
11030     for ( size_t i = 0; i < triaCouples.size(); ++i )
11031     {
11032       involvedNodes->insert( triaCouples[i].first->begin_nodes(),
11033                              triaCouples[i].first->end_nodes() );
11034       involvedNodes->insert( triaCouples[i].second->begin_nodes(),
11035                              triaCouples[i].second->end_nodes() );
11036     }
11037
11038   // just for debug dump resulting triangles
11039   dumpFunction(SMESH_Comment("swapDiagonals_F")<<helper.GetSubShapeID()<<"_"<<step);
11040   for ( size_t i = 0; i < triaCouples.size(); ++i )
11041   {
11042     dumpChangeNodes( triaCouples[i].first );
11043     dumpChangeNodes( triaCouples[i].second );
11044   }
11045 }
11046
11047 //================================================================================
11048 /*!
11049  * \brief Move target node to it's final position on the FACE during shrinking
11050  */
11051 //================================================================================
11052
11053 bool _LayerEdge::SetNewLength2d( Handle(Geom_Surface)& surface,
11054                                  const TopoDS_Face&    F,
11055                                  _EdgesOnShape&        eos,
11056                                  SMESH_MesherHelper&   helper )
11057 {
11058   if ( Is( SHRUNK ))
11059     return false; // already at the target position
11060
11061   SMDS_MeshNode* tgtNode = const_cast< SMDS_MeshNode*& >( _nodes.back() );
11062
11063   if ( eos.SWOLType() == TopAbs_FACE )
11064   {
11065     gp_XY    curUV = helper.GetNodeUV( F, tgtNode );
11066     gp_Pnt2d tgtUV( _pos[0].X(), _pos[0].Y() );
11067     gp_Vec2d uvDir( _normal.X(), _normal.Y() );
11068     const double uvLen = tgtUV.Distance( curUV );
11069     const double kSafe = Max( 0.5, 1. - 0.1 * _simplices.size() );
11070
11071     // Select shrinking step such that not to make faces with wrong orientation.
11072     double stepSize = 1e100;
11073     for ( size_t i = 0; i < _simplices.size(); ++i )
11074     {
11075       if ( !_simplices[i]._nPrev->isMarked() ||
11076            !_simplices[i]._nNext->isMarked() )
11077         continue; // simplex of quadrangle created by addBoundaryElements()
11078
11079       // find intersection of 2 lines: curUV-tgtUV and that connecting simplex nodes
11080       gp_XY uvN1 = helper.GetNodeUV( F, _simplices[i]._nPrev );
11081       gp_XY uvN2 = helper.GetNodeUV( F, _simplices[i]._nNext );
11082       gp_XY dirN = uvN2 - uvN1;
11083       double det = uvDir.Crossed( dirN );
11084       if ( Abs( det )  < std::numeric_limits<double>::min() ) continue;
11085       gp_XY dirN2Cur = curUV - uvN1;
11086       double step = dirN.Crossed( dirN2Cur ) / det;
11087       if ( step > 0 )
11088         stepSize = Min( step, stepSize );
11089     }
11090     gp_Pnt2d newUV;
11091     if ( uvLen <= stepSize )
11092     {
11093       newUV = tgtUV;
11094       Set( SHRUNK );
11095       //_pos.clear();
11096     }
11097     else if ( stepSize > 0 )
11098     {
11099       newUV = curUV + uvDir.XY() * stepSize * kSafe;
11100     }
11101     else
11102     {
11103       return true;
11104     }
11105     SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( tgtNode->GetPosition() );
11106     pos->SetUParameter( newUV.X() );
11107     pos->SetVParameter( newUV.Y() );
11108
11109 #ifdef __myDEBUG
11110     gp_Pnt p = surface->Value( newUV.X(), newUV.Y() );
11111     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
11112     dumpMove( tgtNode );
11113 #endif
11114   }
11115   else // _sWOL is TopAbs_EDGE
11116   {
11117     const TopoDS_Edge&      E = TopoDS::Edge( eos._sWOL );
11118     const SMDS_MeshNode*   n2 = _simplices[0]._nPrev;
11119     SMDS_EdgePosition* tgtPos = static_cast<SMDS_EdgePosition*>( tgtNode->GetPosition() );
11120
11121     const double u2     = helper.GetNodeU( E, n2, tgtNode );
11122     const double uSrc   = _pos[0].Coord( U_SRC );
11123     const double lenTgt = _pos[0].Coord( LEN_TGT );
11124
11125     double newU = _pos[0].Coord( U_TGT );
11126     if ( lenTgt < 0.99 * fabs( uSrc-u2 )) // n2 got out of src-tgt range
11127     {
11128       Set( _LayerEdge::SHRUNK );
11129       //_pos.clear();
11130     }
11131     else
11132     {
11133       newU = 0.1 * tgtPos->GetUParameter() + 0.9 * u2;
11134     }
11135     tgtPos->SetUParameter( newU );
11136 #ifdef __myDEBUG
11137     gp_XY newUV = helper.GetNodeUV( F, tgtNode, _nodes[0]);
11138     gp_Pnt p = surface->Value( newUV.X(), newUV.Y() );
11139     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
11140     dumpMove( tgtNode );
11141 #endif
11142   }
11143
11144   return true;
11145 }
11146
11147 //================================================================================
11148 /*!
11149  * \brief Perform smooth on the FACE
11150  *  \retval bool - true if the node has been moved
11151  */
11152 //================================================================================
11153
11154 bool _SmoothNode::Smooth(int&                  nbBad,
11155                          Handle(Geom_Surface)& surface,
11156                          SMESH_MesherHelper&   helper,
11157                          const double          refSign,
11158                          SmoothType            how,
11159                          bool                  set3D)
11160 {
11161   const TopoDS_Face& face = TopoDS::Face( helper.GetSubShape() );
11162
11163   // get uv of surrounding nodes
11164   vector<gp_XY> uv( _simplices.size() );
11165   for ( size_t i = 0; i < _simplices.size(); ++i )
11166     uv[i] = helper.GetNodeUV( face, _simplices[i]._nPrev, _node );
11167
11168   // compute new UV for the node
11169   gp_XY newPos (0,0);
11170   if ( how == TFI && _simplices.size() == 4 )
11171   {
11172     gp_XY corners[4];
11173     for ( size_t i = 0; i < _simplices.size(); ++i )
11174       if ( _simplices[i]._nOpp )
11175         corners[i] = helper.GetNodeUV( face, _simplices[i]._nOpp, _node );
11176       else
11177         throw SALOME_Exception(LOCALIZED("TFI smoothing: _Simplex::_nOpp not set!"));
11178
11179     newPos = helper.calcTFI ( 0.5, 0.5,
11180                               corners[0], corners[1], corners[2], corners[3],
11181                               uv[1], uv[2], uv[3], uv[0] );
11182   }
11183   else if ( how == ANGULAR )
11184   {
11185     newPos = computeAngularPos( uv, helper.GetNodeUV( face, _node ), refSign );
11186   }
11187   else if ( how == CENTROIDAL && _simplices.size() > 3 )
11188   {
11189     // average centers of diagonals wieghted with their reciprocal lengths
11190     if ( _simplices.size() == 4 )
11191     {
11192       double w1 = 1. / ( uv[2]-uv[0] ).SquareModulus();
11193       double w2 = 1. / ( uv[3]-uv[1] ).SquareModulus();
11194       newPos = ( w1 * ( uv[2]+uv[0] ) + w2 * ( uv[3]+uv[1] )) / ( w1+w2 ) / 2;
11195     }
11196     else
11197     {
11198       double sumWeight = 0;
11199       int nb = _simplices.size() == 4 ? 2 : _simplices.size();
11200       for ( int i = 0; i < nb; ++i )
11201       {
11202         int iFrom = i + 2;
11203         int iTo   = i + _simplices.size() - 1;
11204         for ( int j = iFrom; j < iTo; ++j )
11205         {
11206           int i2 = SMESH_MesherHelper::WrapIndex( j, _simplices.size() );
11207           double w = 1. / ( uv[i]-uv[i2] ).SquareModulus();
11208           sumWeight += w;
11209           newPos += w * ( uv[i]+uv[i2] );
11210         }
11211       }
11212       newPos /= 2 * sumWeight; // 2 is to get a middle between uv's
11213     }
11214   }
11215   else
11216   {
11217     // Laplacian smooth
11218     for ( size_t i = 0; i < _simplices.size(); ++i )
11219       newPos += uv[i];
11220     newPos /= _simplices.size();
11221   }
11222
11223   // count quality metrics (orientation) of triangles around the node
11224   int nbOkBefore = 0;
11225   gp_XY tgtUV = helper.GetNodeUV( face, _node );
11226   for ( size_t i = 0; i < _simplices.size(); ++i )
11227     nbOkBefore += _simplices[i].IsForward( tgtUV, _node, face, helper, refSign );
11228
11229   int nbOkAfter = 0;
11230   for ( size_t i = 0; i < _simplices.size(); ++i )
11231     nbOkAfter += _simplices[i].IsForward( newPos, _node, face, helper, refSign );
11232
11233   if ( nbOkAfter < nbOkBefore )
11234   {
11235     nbBad += _simplices.size() - nbOkBefore;
11236     return false;
11237   }
11238
11239   SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( _node->GetPosition() );
11240   pos->SetUParameter( newPos.X() );
11241   pos->SetVParameter( newPos.Y() );
11242
11243 #ifdef __myDEBUG
11244   set3D = true;
11245 #endif
11246   if ( set3D )
11247   {
11248     gp_Pnt p = surface->Value( newPos.X(), newPos.Y() );
11249     const_cast< SMDS_MeshNode* >( _node )->setXYZ( p.X(), p.Y(), p.Z() );
11250     dumpMove( _node );
11251   }
11252
11253   nbBad += _simplices.size() - nbOkAfter;
11254   return ( (tgtUV-newPos).SquareModulus() > 1e-10 );
11255 }
11256
11257 //================================================================================
11258 /*!
11259  * \brief Computes new UV using angle based smoothing technic
11260  */
11261 //================================================================================
11262
11263 gp_XY _SmoothNode::computeAngularPos(vector<gp_XY>& uv,
11264                                      const gp_XY&   uvToFix,
11265                                      const double   refSign)
11266 {
11267   uv.push_back( uv.front() );
11268
11269   vector< gp_XY >  edgeDir ( uv.size() );
11270   vector< double > edgeSize( uv.size() );
11271   for ( size_t i = 1; i < edgeDir.size(); ++i )
11272   {
11273     edgeDir [i-1] = uv[i] - uv[i-1];
11274     edgeSize[i-1] = edgeDir[i-1].Modulus();
11275     if ( edgeSize[i-1] < numeric_limits<double>::min() )
11276       edgeDir[i-1].SetX( 100 );
11277     else
11278       edgeDir[i-1] /= edgeSize[i-1] * refSign;
11279   }
11280   edgeDir.back()  = edgeDir.front();
11281   edgeSize.back() = edgeSize.front();
11282
11283   gp_XY  newPos(0,0);
11284   //int    nbEdges = 0;
11285   double sumSize = 0;
11286   for ( size_t i = 1; i < edgeDir.size(); ++i )
11287   {
11288     if ( edgeDir[i-1].X() > 1. ) continue;
11289     int i1 = i-1;
11290     while ( edgeDir[i].X() > 1. && ++i < edgeDir.size() );
11291     if ( i == edgeDir.size() ) break;
11292     gp_XY p = uv[i];
11293     gp_XY norm1( -edgeDir[i1].Y(), edgeDir[i1].X() );
11294     gp_XY norm2( -edgeDir[i].Y(),  edgeDir[i].X() );
11295     gp_XY bisec = norm1 + norm2;
11296     double bisecSize = bisec.Modulus();
11297     if ( bisecSize < numeric_limits<double>::min() )
11298     {
11299       bisec = -edgeDir[i1] + edgeDir[i];
11300       bisecSize = bisec.Modulus();
11301     }
11302     bisec /= bisecSize;
11303
11304     gp_XY  dirToN  = uvToFix - p;
11305     double distToN = dirToN.Modulus();
11306     if ( bisec * dirToN < 0 )
11307       distToN = -distToN;
11308
11309     newPos += ( p + bisec * distToN ) * ( edgeSize[i1] + edgeSize[i] );
11310     //++nbEdges;
11311     sumSize += edgeSize[i1] + edgeSize[i];
11312   }
11313   newPos /= /*nbEdges * */sumSize;
11314   return newPos;
11315 }
11316
11317 //================================================================================
11318 /*!
11319  * \brief Delete _SolidData
11320  */
11321 //================================================================================
11322
11323 _SolidData::~_SolidData()
11324 {
11325   TNode2Edge::iterator n2e = _n2eMap.begin();
11326   for ( ; n2e != _n2eMap.end(); ++n2e )
11327   {
11328     _LayerEdge* & e = n2e->second;
11329     if ( e )
11330     {
11331       delete e->_curvature;
11332       if ( e->_2neibors )
11333         delete e->_2neibors->_plnNorm;
11334       delete e->_2neibors;
11335     }
11336     delete e;
11337     e = 0;
11338   }
11339   _n2eMap.clear();
11340
11341   delete _helper;
11342   _helper = 0;
11343 }
11344
11345 //================================================================================
11346 /*!
11347  * \brief Keep a _LayerEdge inflated along the EDGE
11348  */
11349 //================================================================================
11350
11351 void _Shrinker1D::AddEdge( const _LayerEdge*   e,
11352                            _EdgesOnShape&      eos,
11353                            SMESH_MesherHelper& helper )
11354 {
11355   // init
11356   if ( _nodes.empty() )
11357   {
11358     _edges[0] = _edges[1] = 0;
11359     _done = false;
11360   }
11361   // check _LayerEdge
11362   if ( e == _edges[0] || e == _edges[1] || e->_nodes.size() < 2 )
11363     return;
11364   if ( eos.SWOLType() != TopAbs_EDGE )
11365     throw SALOME_Exception(LOCALIZED("Wrong _LayerEdge is added"));
11366   if ( _edges[0] && !_geomEdge.IsSame( eos._sWOL ))
11367     throw SALOME_Exception(LOCALIZED("Wrong _LayerEdge is added"));
11368
11369   // store _LayerEdge
11370   _geomEdge = TopoDS::Edge( eos._sWOL );
11371   double f,l;
11372   BRep_Tool::Range( _geomEdge, f,l );
11373   double u = helper.GetNodeU( _geomEdge, e->_nodes[0], e->_nodes.back());
11374   _edges[ u < 0.5*(f+l) ? 0 : 1 ] = e;
11375
11376   // Update _nodes
11377
11378   const SMDS_MeshNode* tgtNode0 = TgtNode( 0 );
11379   const SMDS_MeshNode* tgtNode1 = TgtNode( 1 );
11380
11381   if ( _nodes.empty() )
11382   {
11383     SMESHDS_SubMesh * eSubMesh = helper.GetMeshDS()->MeshElements( _geomEdge );
11384     if ( !eSubMesh || eSubMesh->NbNodes() < 1 )
11385       return;
11386     TopLoc_Location loc;
11387     Handle(Geom_Curve) C = BRep_Tool::Curve( _geomEdge, loc, f,l );
11388     GeomAdaptor_Curve aCurve(C, f,l);
11389     const double totLen = GCPnts_AbscissaPoint::Length(aCurve, f, l);
11390
11391     int nbExpectNodes = eSubMesh->NbNodes();
11392     _initU  .reserve( nbExpectNodes );
11393     _normPar.reserve( nbExpectNodes );
11394     _nodes  .reserve( nbExpectNodes );
11395     SMDS_NodeIteratorPtr nIt = eSubMesh->GetNodes();
11396     while ( nIt->more() )
11397     {
11398       const SMDS_MeshNode* node = nIt->next();
11399
11400       // skip refinement nodes
11401       if ( node->NbInverseElements(SMDSAbs_Edge) == 0 ||
11402            node == tgtNode0 || node == tgtNode1 )
11403         continue;
11404       bool hasMarkedFace = false;
11405       SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
11406       while ( fIt->more() && !hasMarkedFace )
11407         hasMarkedFace = fIt->next()->isMarked();
11408       if ( !hasMarkedFace )
11409         continue;
11410
11411       _nodes.push_back( node );
11412       _initU.push_back( helper.GetNodeU( _geomEdge, node ));
11413       double len = GCPnts_AbscissaPoint::Length(aCurve, f, _initU.back());
11414       _normPar.push_back(  len / totLen );
11415     }
11416   }
11417   else
11418   {
11419     // remove target node of the _LayerEdge from _nodes
11420     size_t nbFound = 0;
11421     for ( size_t i = 0; i < _nodes.size(); ++i )
11422       if ( !_nodes[i] || _nodes[i] == tgtNode0 || _nodes[i] == tgtNode1 )
11423         _nodes[i] = 0, nbFound++;
11424     if ( nbFound == _nodes.size() )
11425       _nodes.clear();
11426   }
11427 }
11428
11429 //================================================================================
11430 /*!
11431  * \brief Move nodes on EDGE from ends where _LayerEdge's are inflated
11432  */
11433 //================================================================================
11434
11435 void _Shrinker1D::Compute(bool set3D, SMESH_MesherHelper& helper)
11436 {
11437   if ( _done || _nodes.empty())
11438     return;
11439   const _LayerEdge* e = _edges[0];
11440   if ( !e ) e = _edges[1];
11441   if ( !e ) return;
11442
11443   _done =  (( !_edges[0] || _edges[0]->Is( _LayerEdge::SHRUNK )) &&
11444             ( !_edges[1] || _edges[1]->Is( _LayerEdge::SHRUNK )));
11445
11446   double f,l;
11447   if ( set3D || _done )
11448   {
11449     Handle(Geom_Curve) C = BRep_Tool::Curve(_geomEdge, f,l);
11450     GeomAdaptor_Curve aCurve(C, f,l);
11451
11452     if ( _edges[0] )
11453       f = helper.GetNodeU( _geomEdge, _edges[0]->_nodes.back(), _nodes[0] );
11454     if ( _edges[1] )
11455       l = helper.GetNodeU( _geomEdge, _edges[1]->_nodes.back(), _nodes.back() );
11456     double totLen = GCPnts_AbscissaPoint::Length( aCurve, f, l );
11457
11458     for ( size_t i = 0; i < _nodes.size(); ++i )
11459     {
11460       if ( !_nodes[i] ) continue;
11461       double len = totLen * _normPar[i];
11462       GCPnts_AbscissaPoint discret( aCurve, len, f );
11463       if ( !discret.IsDone() )
11464         return throw SALOME_Exception(LOCALIZED("GCPnts_AbscissaPoint failed"));
11465       double u = discret.Parameter();
11466       SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( _nodes[i]->GetPosition() );
11467       pos->SetUParameter( u );
11468       gp_Pnt p = C->Value( u );
11469       const_cast< SMDS_MeshNode*>( _nodes[i] )->setXYZ( p.X(), p.Y(), p.Z() );
11470     }
11471   }
11472   else
11473   {
11474     BRep_Tool::Range( _geomEdge, f,l );
11475     if ( _edges[0] )
11476       f = helper.GetNodeU( _geomEdge, _edges[0]->_nodes.back(), _nodes[0] );
11477     if ( _edges[1] )
11478       l = helper.GetNodeU( _geomEdge, _edges[1]->_nodes.back(), _nodes.back() );
11479     
11480     for ( size_t i = 0; i < _nodes.size(); ++i )
11481     {
11482       if ( !_nodes[i] ) continue;
11483       double u = f * ( 1-_normPar[i] ) + l * _normPar[i];
11484       SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( _nodes[i]->GetPosition() );
11485       pos->SetUParameter( u );
11486     }
11487   }
11488 }
11489
11490 //================================================================================
11491 /*!
11492  * \brief Restore initial parameters of nodes on EDGE
11493  */
11494 //================================================================================
11495
11496 void _Shrinker1D::RestoreParams()
11497 {
11498   if ( _done )
11499     for ( size_t i = 0; i < _nodes.size(); ++i )
11500     {
11501       if ( !_nodes[i] ) continue;
11502       SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( _nodes[i]->GetPosition() );
11503       pos->SetUParameter( _initU[i] );
11504     }
11505   _done = false;
11506 }
11507
11508 //================================================================================
11509 /*!
11510  * \brief Replace source nodes by target nodes in shrinked mesh edges
11511  */
11512 //================================================================================
11513
11514 void _Shrinker1D::SwapSrcTgtNodes( SMESHDS_Mesh* mesh )
11515 {
11516   const SMDS_MeshNode* nodes[3];
11517   for ( int i = 0; i < 2; ++i )
11518   {
11519     if ( !_edges[i] ) continue;
11520
11521     SMESHDS_SubMesh * eSubMesh = mesh->MeshElements( _geomEdge );
11522     if ( !eSubMesh ) return;
11523     const SMDS_MeshNode* srcNode = _edges[i]->_nodes[0];
11524     const SMDS_MeshNode* tgtNode = _edges[i]->_nodes.back();
11525     const SMDS_MeshNode* scdNode = _edges[i]->_nodes[1];
11526     SMDS_ElemIteratorPtr eIt = srcNode->GetInverseElementIterator(SMDSAbs_Edge);
11527     while ( eIt->more() )
11528     {
11529       const SMDS_MeshElement* e = eIt->next();
11530       if ( !eSubMesh->Contains( e ) || e->GetNodeIndex( scdNode ) >= 0 )
11531           continue;
11532       SMDS_ElemIteratorPtr nIt = e->nodesIterator();
11533       for ( int iN = 0; iN < e->NbNodes(); ++iN )
11534       {
11535         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
11536         nodes[iN] = ( n == srcNode ? tgtNode : n );
11537       }
11538       mesh->ChangeElementNodes( e, nodes, e->NbNodes() );
11539     }
11540   }
11541 }
11542
11543 //================================================================================
11544 /*!
11545  * \brief Creates 2D and 1D elements on boundaries of new prisms
11546  */
11547 //================================================================================
11548
11549 bool _ViscousBuilder::addBoundaryElements(_SolidData& data)
11550 {
11551   SMESH_MesherHelper helper( *_mesh );
11552
11553   vector< const SMDS_MeshNode* > faceNodes;
11554
11555   //for ( size_t i = 0; i < _sdVec.size(); ++i )
11556   {
11557     //_SolidData& data = _sdVec[i];
11558     TopTools_IndexedMapOfShape geomEdges;
11559     TopExp::MapShapes( data._solid, TopAbs_EDGE, geomEdges );
11560     for ( int iE = 1; iE <= geomEdges.Extent(); ++iE )
11561     {
11562       const TopoDS_Edge& E = TopoDS::Edge( geomEdges(iE));
11563       const TGeomID edgeID = getMeshDS()->ShapeToIndex( E );
11564       if ( data._noShrinkShapes.count( edgeID ))
11565         continue;
11566
11567       // Get _LayerEdge's based on E
11568
11569       map< double, const SMDS_MeshNode* > u2nodes;
11570       if ( !SMESH_Algo::GetSortedNodesOnEdge( getMeshDS(), E, /*ignoreMedium=*/false, u2nodes))
11571         continue;
11572
11573       vector< _LayerEdge* > ledges; ledges.reserve( u2nodes.size() );
11574       TNode2Edge & n2eMap = data._n2eMap;
11575       map< double, const SMDS_MeshNode* >::iterator u2n = u2nodes.begin();
11576       {
11577         //check if 2D elements are needed on E
11578         TNode2Edge::iterator n2e = n2eMap.find( u2n->second );
11579         if ( n2e == n2eMap.end() ) continue; // no layers on vertex
11580         ledges.push_back( n2e->second );
11581         u2n++;
11582         if (( n2e = n2eMap.find( u2n->second )) == n2eMap.end() )
11583           continue; // no layers on E
11584         ledges.push_back( n2eMap[ u2n->second ]);
11585
11586         const SMDS_MeshNode* tgtN0 = ledges[0]->_nodes.back();
11587         const SMDS_MeshNode* tgtN1 = ledges[1]->_nodes.back();
11588         int nbSharedPyram = 0;
11589         SMDS_ElemIteratorPtr vIt = tgtN0->GetInverseElementIterator(SMDSAbs_Volume);
11590         while ( vIt->more() )
11591         {
11592           const SMDS_MeshElement* v = vIt->next();
11593           nbSharedPyram += int( v->GetNodeIndex( tgtN1 ) >= 0 );
11594         }
11595         if ( nbSharedPyram > 1 )
11596           continue; // not free border of the pyramid
11597
11598         faceNodes.clear();
11599         faceNodes.push_back( ledges[0]->_nodes[0] );
11600         faceNodes.push_back( ledges[1]->_nodes[0] );
11601         if ( ledges[0]->_nodes.size() > 1 ) faceNodes.push_back( ledges[0]->_nodes[1] );
11602         if ( ledges[1]->_nodes.size() > 1 ) faceNodes.push_back( ledges[1]->_nodes[1] );
11603
11604         if ( getMeshDS()->FindElement( faceNodes, SMDSAbs_Face, /*noMedium=*/true))
11605           continue; // faces already created
11606       }
11607       for ( ++u2n; u2n != u2nodes.end(); ++u2n )
11608         ledges.push_back( n2eMap[ u2n->second ]);
11609
11610       // Find out orientation and type of face to create
11611
11612       bool reverse = false, isOnFace;
11613       TopoDS_Shape F;
11614
11615       map< TGeomID, TopoDS_Shape >::iterator e2f = data._shrinkShape2Shape.find( edgeID );
11616       if (( isOnFace = ( e2f != data._shrinkShape2Shape.end() )))
11617       {
11618         F = e2f->second.Oriented( TopAbs_FORWARD );
11619         reverse = ( helper.GetSubShapeOri( F, E ) == TopAbs_REVERSED );
11620         if ( helper.GetSubShapeOri( data._solid, F ) == TopAbs_REVERSED )
11621           reverse = !reverse, F.Reverse();
11622         if ( helper.IsReversedSubMesh( TopoDS::Face(F) ))
11623           reverse = !reverse;
11624       }
11625       else if ( !data._ignoreFaceIds.count( e2f->first ))
11626       {
11627         // find FACE with layers sharing E
11628         PShapeIteratorPtr fIt = helper.GetAncestors( E, *_mesh, TopAbs_FACE, &data._solid );
11629         if ( fIt->more() )
11630           F = *( fIt->next() );
11631       }
11632       // Find the sub-mesh to add new faces
11633       SMESHDS_SubMesh* sm = 0;
11634       if ( isOnFace )
11635         sm = getMeshDS()->MeshElements( F );
11636       else
11637         sm = data._proxyMesh->getFaceSubM( TopoDS::Face(F), /*create=*/true );
11638       if ( !sm )
11639         return error("error in addBoundaryElements()", data._index);
11640
11641       // Find a proxy sub-mesh of the FACE of an adjacent SOLID, which will use the new boundary
11642       // faces for 3D meshing (PAL23414)
11643       SMESHDS_SubMesh* adjSM = 0;
11644       if ( isOnFace )
11645       {
11646         const TGeomID   faceID = sm->GetID();
11647         PShapeIteratorPtr soIt = helper.GetAncestors( F, *_mesh, TopAbs_SOLID );
11648         while ( const TopoDS_Shape* solid = soIt->next() )
11649           if ( !solid->IsSame( data._solid ))
11650           {
11651             size_t iData = _solids.FindIndex( *solid ) - 1;
11652             if ( iData < _sdVec.size() &&
11653                  _sdVec[ iData ]._ignoreFaceIds.count( faceID ) &&
11654                  _sdVec[ iData ]._shrinkShape2Shape.count( edgeID ) == 0 )
11655             {
11656               SMESH_ProxyMesh::SubMesh* proxySub =
11657                 _sdVec[ iData ]._proxyMesh->getFaceSubM( TopoDS::Face( F ), /*create=*/false);
11658               if ( proxySub && proxySub->NbElements() > 0 )
11659                 adjSM = proxySub;
11660             }
11661           }
11662       }
11663
11664       // Make faces
11665       const int dj1 = reverse ? 0 : 1;
11666       const int dj2 = reverse ? 1 : 0;
11667       vector< const SMDS_MeshElement*> ff; // new faces row
11668       SMESHDS_Mesh* m = getMeshDS();
11669       for ( size_t j = 1; j < ledges.size(); ++j )
11670       {
11671         vector< const SMDS_MeshNode*>&  nn1 = ledges[j-dj1]->_nodes;
11672         vector< const SMDS_MeshNode*>&  nn2 = ledges[j-dj2]->_nodes;
11673         ff.resize( std::max( nn1.size(), nn2.size() ), NULL );
11674         if ( nn1.size() == nn2.size() )
11675         {
11676           if ( isOnFace )
11677             for ( size_t z = 1; z < nn1.size(); ++z )
11678               sm->AddElement( ff[z-1] = m->AddFace( nn1[z-1], nn2[z-1], nn2[z], nn1[z] ));
11679           else
11680             for ( size_t z = 1; z < nn1.size(); ++z )
11681               sm->AddElement( new SMDS_FaceOfNodes( nn1[z-1], nn2[z-1], nn2[z], nn1[z] ));
11682         }
11683         else if ( nn1.size() == 1 )
11684         {
11685           if ( isOnFace )
11686             for ( size_t z = 1; z < nn2.size(); ++z )
11687               sm->AddElement( ff[z-1] = m->AddFace( nn1[0], nn2[z-1], nn2[z] ));
11688           else
11689             for ( size_t z = 1; z < nn2.size(); ++z )
11690               sm->AddElement( new SMDS_FaceOfNodes( nn1[0], nn2[z-1], nn2[z] ));
11691         }
11692         else
11693         {
11694           if ( isOnFace )
11695             for ( size_t z = 1; z < nn1.size(); ++z )
11696               sm->AddElement( ff[z-1] = m->AddFace( nn1[z-1], nn2[0], nn1[z] ));
11697           else
11698             for ( size_t z = 1; z < nn1.size(); ++z )
11699               sm->AddElement( new SMDS_FaceOfNodes( nn1[z-1], nn2[0], nn2[z] ));
11700         }
11701
11702         if ( adjSM ) // add faces to a proxy SM of the adjacent SOLID
11703         {
11704           for ( size_t z = 0; z < ff.size(); ++z )
11705             if ( ff[ z ])
11706               adjSM->AddElement( ff[ z ]);
11707           ff.clear();
11708         }
11709       }
11710
11711       // Make edges
11712       for ( int isFirst = 0; isFirst < 2; ++isFirst )
11713       {
11714         _LayerEdge* edge = isFirst ? ledges.front() : ledges.back();
11715         _EdgesOnShape* eos = data.GetShapeEdges( edge );
11716         if ( eos && eos->SWOLType() == TopAbs_EDGE )
11717         {
11718           vector< const SMDS_MeshNode*>&  nn = edge->_nodes;
11719           if ( nn.size() < 2 || nn[1]->NbInverseElements( SMDSAbs_Edge ) >= 2 )
11720             continue;
11721           helper.SetSubShape( eos->_sWOL );
11722           helper.SetElementsOnShape( true );
11723           for ( size_t z = 1; z < nn.size(); ++z )
11724             helper.AddEdge( nn[z-1], nn[z] );
11725         }
11726       }
11727
11728     } // loop on EDGE's
11729   } // loop on _SolidData's
11730
11731   return true;
11732 }