Salome HOME
Merge V9_dev branch into master
[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_PolygonalFaceOfNodes.hxx"
31 #include "SMDS_SetIterator.hxx"
32 #include "SMESHDS_Group.hxx"
33 #include "SMESHDS_Hypothesis.hxx"
34 #include "SMESHDS_Mesh.hxx"
35 #include "SMESH_Algo.hxx"
36 #include "SMESH_ComputeError.hxx"
37 #include "SMESH_ControlsDef.hxx"
38 #include "SMESH_Gen.hxx"
39 #include "SMESH_Group.hxx"
40 #include "SMESH_HypoFilter.hxx"
41 #include "SMESH_Mesh.hxx"
42 #include "SMESH_MeshAlgos.hxx"
43 #include "SMESH_MesherHelper.hxx"
44 #include "SMESH_ProxyMesh.hxx"
45 #include "SMESH_subMesh.hxx"
46 #include "SMESH_subMeshEventListener.hxx"
47 #include "StdMeshers_FaceSide.hxx"
48 #include "StdMeshers_ViscousLayers2D.hxx"
49
50 #include <Adaptor3d_HSurface.hxx>
51 #include <BRepAdaptor_Curve.hxx>
52 #include <BRepAdaptor_Curve2d.hxx>
53 #include <BRepAdaptor_Surface.hxx>
54 //#include <BRepLProp_CLProps.hxx>
55 #include <BRepLProp_SLProps.hxx>
56 #include <BRepOffsetAPI_MakeOffsetShape.hxx>
57 #include <BRep_Tool.hxx>
58 #include <Bnd_B2d.hxx>
59 #include <Bnd_B3d.hxx>
60 #include <ElCLib.hxx>
61 #include <GCPnts_AbscissaPoint.hxx>
62 #include <GCPnts_TangentialDeflection.hxx>
63 #include <Geom2d_Circle.hxx>
64 #include <Geom2d_Line.hxx>
65 #include <Geom2d_TrimmedCurve.hxx>
66 #include <GeomAdaptor_Curve.hxx>
67 #include <GeomLib.hxx>
68 #include <Geom_Circle.hxx>
69 #include <Geom_Curve.hxx>
70 #include <Geom_Line.hxx>
71 #include <Geom_TrimmedCurve.hxx>
72 #include <Precision.hxx>
73 #include <Standard_ErrorHandler.hxx>
74 #include <Standard_Failure.hxx>
75 #include <TColStd_Array1OfReal.hxx>
76 #include <TopExp.hxx>
77 #include <TopExp_Explorer.hxx>
78 #include <TopTools_IndexedMapOfShape.hxx>
79 #include <TopTools_ListOfShape.hxx>
80 #include <TopTools_MapIteratorOfMapOfShape.hxx>
81 #include <TopTools_MapOfShape.hxx>
82 #include <TopoDS.hxx>
83 #include <TopoDS_Edge.hxx>
84 #include <TopoDS_Face.hxx>
85 #include <TopoDS_Vertex.hxx>
86 #include <gp_Ax1.hxx>
87 #include <gp_Cone.hxx>
88 #include <gp_Sphere.hxx>
89 #include <gp_Vec.hxx>
90 #include <gp_XY.hxx>
91
92 #include <cmath>
93 #include <limits>
94 #include <list>
95 #include <queue>
96 #include <string>
97 #include <unordered_map>
98
99 #ifdef _DEBUG_
100 #define __myDEBUG
101 //#define __NOT_INVALIDATE_BAD_SMOOTH
102 //#define __NODES_AT_POS
103 #endif
104
105 #define INCREMENTAL_SMOOTH // smooth only if min angle is too small
106 #define BLOCK_INFLATION // of individual _LayerEdge's
107 #define OLD_NEF_POLYGON
108
109 using namespace std;
110
111 //================================================================================
112 namespace VISCOUS_3D
113 {
114   typedef int TGeomID;
115
116   enum UIndex { U_TGT = 1, U_SRC, LEN_TGT };
117
118   const double theMinSmoothCosin = 0.1;
119   const double theSmoothThickToElemSizeRatio = 0.6;
120   const double theMinSmoothTriaAngle = 30;
121   const double theMinSmoothQuadAngle = 45;
122
123   // what part of thickness is allowed till intersection
124   // (defined by SALOME_TESTS/Grids/smesh/viscous_layers_00/A5)
125   const double theThickToIntersection = 1.5;
126
127   bool needSmoothing( double cosin, double tgtThick, double elemSize )
128   {
129     return cosin * tgtThick > theSmoothThickToElemSizeRatio * elemSize;
130   }
131   double getSmoothingThickness( double cosin, double elemSize )
132   {
133     return theSmoothThickToElemSizeRatio * elemSize / cosin;
134   }
135
136   /*!
137    * \brief SMESH_ProxyMesh computed by _ViscousBuilder for a SOLID.
138    * It is stored in a SMESH_subMesh of the SOLID as SMESH_subMeshEventListenerData
139    */
140   struct _MeshOfSolid : public SMESH_ProxyMesh,
141                         public SMESH_subMeshEventListenerData
142   {
143     bool                  _n2nMapComputed;
144     SMESH_ComputeErrorPtr _warning;
145
146     _MeshOfSolid( SMESH_Mesh* mesh)
147       :SMESH_subMeshEventListenerData( /*isDeletable=*/true),_n2nMapComputed(false)
148     {
149       SMESH_ProxyMesh::setMesh( *mesh );
150     }
151
152     // returns submesh for a geom face
153     SMESH_ProxyMesh::SubMesh* getFaceSubM(const TopoDS_Face& F, bool create=false)
154     {
155       TGeomID i = SMESH_ProxyMesh::shapeIndex(F);
156       return create ? SMESH_ProxyMesh::getProxySubMesh(i) : findProxySubMesh(i);
157     }
158     void setNode2Node(const SMDS_MeshNode*                 srcNode,
159                       const SMDS_MeshNode*                 proxyNode,
160                       const SMESH_ProxyMesh::SubMesh* subMesh)
161     {
162       SMESH_ProxyMesh::setNode2Node( srcNode,proxyNode,subMesh);
163     }
164   };
165   //--------------------------------------------------------------------------------
166   /*!
167    * \brief Listener of events of 3D sub-meshes computed with viscous layers.
168    * It is used to clear an inferior dim sub-meshes modified by viscous layers
169    */
170   class _ShrinkShapeListener : SMESH_subMeshEventListener
171   {
172     _ShrinkShapeListener()
173       : SMESH_subMeshEventListener(/*isDeletable=*/false,
174                                    "StdMeshers_ViscousLayers::_ShrinkShapeListener") {}
175   public:
176     static SMESH_subMeshEventListener* Get() { static _ShrinkShapeListener l; return &l; }
177     virtual void ProcessEvent(const int                       event,
178                               const int                       eventType,
179                               SMESH_subMesh*                  solidSM,
180                               SMESH_subMeshEventListenerData* data,
181                               const SMESH_Hypothesis*         hyp)
182     {
183       if ( SMESH_subMesh::COMPUTE_EVENT == eventType && solidSM->IsEmpty() && data )
184       {
185         SMESH_subMeshEventListener::ProcessEvent(event,eventType,solidSM,data,hyp);
186       }
187     }
188   };
189   //--------------------------------------------------------------------------------
190   /*!
191    * \brief Listener of events of 3D sub-meshes computed with viscous layers.
192    * It is used to store data computed by _ViscousBuilder for a sub-mesh and to
193    * delete the data as soon as it has been used
194    */
195   class _ViscousListener : SMESH_subMeshEventListener
196   {
197     _ViscousListener():
198       SMESH_subMeshEventListener(/*isDeletable=*/false,
199                                  "StdMeshers_ViscousLayers::_ViscousListener") {}
200     static SMESH_subMeshEventListener* Get() { static _ViscousListener l; return &l; }
201   public:
202     virtual void ProcessEvent(const int                       event,
203                               const int                       eventType,
204                               SMESH_subMesh*                  subMesh,
205                               SMESH_subMeshEventListenerData* data,
206                               const SMESH_Hypothesis*         hyp)
207     {
208       if (( SMESH_subMesh::COMPUTE_EVENT       == eventType ) &&
209           ( SMESH_subMesh::CHECK_COMPUTE_STATE != event &&
210             SMESH_subMesh::SUBMESH_COMPUTED    != event ))
211       {
212         // delete SMESH_ProxyMesh containing temporary faces
213         subMesh->DeleteEventListener( this );
214       }
215     }
216     // Finds or creates proxy mesh of the solid
217     static _MeshOfSolid* GetSolidMesh(SMESH_Mesh*         mesh,
218                                       const TopoDS_Shape& solid,
219                                       bool                toCreate=false)
220     {
221       if ( !mesh ) return 0;
222       SMESH_subMesh* sm = mesh->GetSubMesh(solid);
223       _MeshOfSolid* data = (_MeshOfSolid*) sm->GetEventListenerData( Get() );
224       if ( !data && toCreate )
225       {
226         data = new _MeshOfSolid(mesh);
227         data->mySubMeshes.push_back( sm ); // to find SOLID by _MeshOfSolid
228         sm->SetEventListener( Get(), data, sm );
229       }
230       return data;
231     }
232     // Removes proxy mesh of the solid
233     static void RemoveSolidMesh(SMESH_Mesh* mesh, const TopoDS_Shape& solid)
234     {
235       mesh->GetSubMesh(solid)->DeleteEventListener( _ViscousListener::Get() );
236     }
237   };
238   
239   //================================================================================
240   /*!
241    * \brief sets a sub-mesh event listener to clear sub-meshes of sub-shapes of
242    * the main shape when sub-mesh of the main shape is cleared,
243    * for example to clear sub-meshes of FACEs when sub-mesh of a SOLID
244    * is cleared
245    */
246   //================================================================================
247
248   void ToClearSubWithMain( SMESH_subMesh* sub, const TopoDS_Shape& main)
249   {
250     SMESH_subMesh* mainSM = sub->GetFather()->GetSubMesh( main );
251     SMESH_subMeshEventListenerData* data =
252       mainSM->GetEventListenerData( _ShrinkShapeListener::Get());
253     if ( data )
254     {
255       if ( find( data->mySubMeshes.begin(), data->mySubMeshes.end(), sub ) ==
256            data->mySubMeshes.end())
257         data->mySubMeshes.push_back( sub );
258     }
259     else
260     {
261       data = SMESH_subMeshEventListenerData::MakeData( /*dependent=*/sub );
262       sub->SetEventListener( _ShrinkShapeListener::Get(), data, /*whereToListenTo=*/mainSM );
263     }
264   }
265   struct _SolidData;
266   //--------------------------------------------------------------------------------
267   /*!
268    * \brief Simplex (triangle or tetrahedron) based on 1 (tria) or 2 (tet) nodes of
269    * _LayerEdge and 2 nodes of the mesh surface beening smoothed.
270    * The class is used to check validity of face or volumes around a smoothed node;
271    * it stores only 2 nodes as the other nodes are stored by _LayerEdge.
272    */
273   struct _Simplex
274   {
275     const SMDS_MeshNode *_nPrev, *_nNext; // nodes on a smoothed mesh surface
276     const SMDS_MeshNode *_nOpp; // in 2D case, a node opposite to a smoothed node in QUAD
277     _Simplex(const SMDS_MeshNode* nPrev=0,
278              const SMDS_MeshNode* nNext=0,
279              const SMDS_MeshNode* nOpp=0)
280       : _nPrev(nPrev), _nNext(nNext), _nOpp(nOpp) {}
281     bool IsForward(const gp_XYZ* pntSrc, const gp_XYZ* pntTgt, double& vol) const
282     {
283       const double M[3][3] =
284         {{ _nNext->X() - pntSrc->X(), _nNext->Y() - pntSrc->Y(), _nNext->Z() - pntSrc->Z() },
285          { pntTgt->X() - pntSrc->X(), pntTgt->Y() - pntSrc->Y(), pntTgt->Z() - pntSrc->Z() },
286          { _nPrev->X() - pntSrc->X(), _nPrev->Y() - pntSrc->Y(), _nPrev->Z() - pntSrc->Z() }};
287       vol = ( + M[0][0] * M[1][1] * M[2][2]
288               + M[0][1] * M[1][2] * M[2][0]
289               + M[0][2] * M[1][0] * M[2][1]
290               - M[0][0] * M[1][2] * M[2][1]
291               - M[0][1] * M[1][0] * M[2][2]
292               - M[0][2] * M[1][1] * M[2][0]);
293       return vol > 1e-100;
294     }
295     bool IsForward(const SMDS_MeshNode* nSrc, const gp_XYZ& pTgt, double& vol) const
296     {
297       SMESH_TNodeXYZ pSrc( nSrc );
298       return IsForward( &pSrc, &pTgt, vol );
299     }
300     bool IsForward(const gp_XY&         tgtUV,
301                    const SMDS_MeshNode* smoothedNode,
302                    const TopoDS_Face&   face,
303                    SMESH_MesherHelper&  helper,
304                    const double         refSign) const
305     {
306       gp_XY prevUV = helper.GetNodeUV( face, _nPrev, smoothedNode );
307       gp_XY nextUV = helper.GetNodeUV( face, _nNext, smoothedNode );
308       gp_Vec2d v1( tgtUV, prevUV ), v2( tgtUV, nextUV );
309       double d = v1 ^ v2;
310       return d*refSign > 1e-100;
311     }
312     bool IsMinAngleOK( const gp_XYZ& pTgt, double& minAngle ) const
313     {
314       SMESH_TNodeXYZ pPrev( _nPrev ), pNext( _nNext );
315       if ( !_nOpp ) // triangle
316       {
317         gp_Vec tp( pPrev - pTgt ), pn( pNext - pPrev ), nt( pTgt - pNext );
318         double tp2 = tp.SquareMagnitude();
319         double pn2 = pn.SquareMagnitude();
320         double nt2 = nt.SquareMagnitude();
321
322         if ( tp2 < pn2 && tp2 < nt2 )
323           minAngle = ( nt * -pn ) * ( nt * -pn ) / nt2 / pn2;
324         else if ( pn2 < nt2 )
325           minAngle = ( tp * -nt ) * ( tp * -nt ) / tp2 / nt2;
326         else
327           minAngle = ( pn * -tp ) * ( pn * -tp ) / pn2 / tp2;
328
329         static double theMaxCos2 = ( Cos( theMinSmoothTriaAngle * M_PI / 180. ) *
330                                      Cos( theMinSmoothTriaAngle * M_PI / 180. ));
331         return minAngle < theMaxCos2;
332       }
333       else // quadrangle
334       {
335         SMESH_TNodeXYZ pOpp( _nOpp );
336         gp_Vec tp( pPrev - pTgt ), po( pOpp - pPrev ), on( pNext - pOpp), nt( pTgt - pNext );
337         double tp2 = tp.SquareMagnitude();
338         double po2 = po.SquareMagnitude();
339         double on2 = on.SquareMagnitude();
340         double nt2 = nt.SquareMagnitude();
341         minAngle = Max( Max((( tp * -nt ) * ( tp * -nt ) / tp2 / nt2 ),
342                             (( po * -tp ) * ( po * -tp ) / po2 / tp2 )),
343                         Max((( on * -po ) * ( on * -po ) / on2 / po2 ),
344                             (( nt * -on ) * ( nt * -on ) / nt2 / on2 )));
345
346         static double theMaxCos2 = ( Cos( theMinSmoothQuadAngle * M_PI / 180. ) *
347                                      Cos( theMinSmoothQuadAngle * M_PI / 180. ));
348         return minAngle < theMaxCos2;
349       }
350     }
351     bool IsNeighbour(const _Simplex& other) const
352     {
353       return _nPrev == other._nNext || _nNext == other._nPrev;
354     }
355     bool Includes( const SMDS_MeshNode* node ) const { return _nPrev == node || _nNext == node; }
356     static void GetSimplices( const SMDS_MeshNode* node,
357                               vector<_Simplex>&   simplices,
358                               const set<TGeomID>& ingnoreShapes,
359                               const _SolidData*   dataToCheckOri = 0,
360                               const bool          toSort = false);
361     static void SortSimplices(vector<_Simplex>& simplices);
362   };
363   //--------------------------------------------------------------------------------
364   /*!
365    * Structure used to take into account surface curvature while smoothing
366    */
367   struct _Curvature
368   {
369     double   _r; // radius
370     double   _k; // factor to correct node smoothed position
371     double   _h2lenRatio; // avgNormProj / (2*avgDist)
372     gp_Pnt2d _uv; // UV used in putOnOffsetSurface()
373   public:
374     static _Curvature* New( double avgNormProj, double avgDist )
375     {
376       _Curvature* c = 0;
377       if ( fabs( avgNormProj / avgDist ) > 1./200 )
378       {
379         c = new _Curvature;
380         c->_r = avgDist * avgDist / avgNormProj;
381         c->_k = avgDist * avgDist / c->_r / c->_r;
382         //c->_k = avgNormProj / c->_r;
383         c->_k *= ( c->_r < 0 ? 1/1.1 : 1.1 ); // not to be too restrictive
384         c->_h2lenRatio = avgNormProj / ( avgDist + avgDist );
385
386         c->_uv.SetCoord( 0., 0. );
387       }
388       return c;
389     }
390     double lenDelta(double len) const { return _k * ( _r + len ); }
391     double lenDeltaByDist(double dist) const { return dist * _h2lenRatio; }
392   };
393   //--------------------------------------------------------------------------------
394
395   struct _2NearEdges;
396   struct _LayerEdge;
397   struct _EdgesOnShape;
398   struct _Smoother1D;
399   typedef map< const SMDS_MeshNode*, _LayerEdge*, TIDCompare > TNode2Edge;
400
401   //--------------------------------------------------------------------------------
402   /*!
403    * \brief Edge normal to surface, connecting a node on solid surface (_nodes[0])
404    * and a node of the most internal layer (_nodes.back())
405    */
406   struct _LayerEdge
407   {
408     typedef gp_XYZ (_LayerEdge::*PSmooFun)();
409
410     vector< const SMDS_MeshNode*> _nodes;
411
412     gp_XYZ              _normal;    // to boundary of solid
413     vector<gp_XYZ>      _pos;       // points computed during inflation
414     double              _len;       // length achieved with the last inflation step
415     double              _maxLen;    // maximal possible length
416     double              _cosin;     // of angle (_normal ^ surface)
417     double              _minAngle;  // of _simplices
418     double              _lenFactor; // to compute _len taking _cosin into account
419     int                 _flags;
420
421     // simplices connected to the source node (_nodes[0]);
422     // used for smoothing and quality check of _LayerEdge's based on the FACE
423     vector<_Simplex>    _simplices;
424     vector<_LayerEdge*> _neibors; // all surrounding _LayerEdge's
425     PSmooFun            _smooFunction; // smoothing function
426     _Curvature*         _curvature;
427     // data for smoothing of _LayerEdge's based on the EDGE
428     _2NearEdges*        _2neibors;
429
430     enum EFlags { TO_SMOOTH       = 0x0000001,
431                   MOVED           = 0x0000002, // set by _neibors[i]->SetNewLength()
432                   SMOOTHED        = 0x0000004, // set by _LayerEdge::Smooth()
433                   DIFFICULT       = 0x0000008, // near concave VERTEX
434                   ON_CONCAVE_FACE = 0x0000010,
435                   BLOCKED         = 0x0000020, // not to inflate any more
436                   INTERSECTED     = 0x0000040, // close intersection with a face found
437                   NORMAL_UPDATED  = 0x0000080,
438                   UPD_NORMAL_CONV = 0x0000100, // to update normal on boundary of concave FACE
439                   MARKED          = 0x0000200, // local usage
440                   MULTI_NORMAL    = 0x0000400, // a normal is invisible by some of surrounding faces
441                   NEAR_BOUNDARY   = 0x0000800, // is near FACE boundary forcing smooth
442                   SMOOTHED_C1     = 0x0001000, // is on _eosC1
443                   DISTORTED       = 0x0002000, // was bad before smoothing
444                   RISKY_SWOL      = 0x0004000, // SWOL is parallel to a source FACE
445                   SHRUNK          = 0x0008000, // target node reached a tgt position while shrink()
446                   UNUSED_FLAG     = 0x0100000  // to add user flags after
447     };
448     bool Is   ( int flag ) const { return _flags & flag; }
449     void Set  ( int flag ) { _flags |= flag; }
450     void Unset( int flag ) { _flags &= ~flag; }
451     std::string DumpFlags() const; // debug
452
453     void SetNewLength( double len, _EdgesOnShape& eos, SMESH_MesherHelper& helper );
454     bool SetNewLength2d( Handle(Geom_Surface)& surface,
455                          const TopoDS_Face&    F,
456                          _EdgesOnShape&        eos,
457                          SMESH_MesherHelper&   helper );
458     void SetDataByNeighbors( const SMDS_MeshNode* n1,
459                              const SMDS_MeshNode* n2,
460                              const _EdgesOnShape& eos,
461                              SMESH_MesherHelper&  helper);
462     void Block( _SolidData& data );
463     void InvalidateStep( size_t curStep, const _EdgesOnShape& eos, bool restoreLength=false );
464     void ChooseSmooFunction(const set< TGeomID >& concaveVertices,
465                             const TNode2Edge&     n2eMap);
466     void SmoothPos( const vector< double >& segLen, const double tol );
467     int  GetSmoothedPos( const double tol );
468     int  Smooth(const int step, const bool isConcaveFace, bool findBest);
469     int  Smooth(const int step, bool findBest, vector< _LayerEdge* >& toSmooth );
470     int  CheckNeiborsOnBoundary(vector< _LayerEdge* >* badNeibors = 0, bool * needSmooth = 0 );
471     void SmoothWoCheck();
472     bool SmoothOnEdge(Handle(ShapeAnalysis_Surface)& surface,
473                       const TopoDS_Face&             F,
474                       SMESH_MesherHelper&            helper);
475     void MoveNearConcaVer( const _EdgesOnShape*    eov,
476                            const _EdgesOnShape*    eos,
477                            const int               step,
478                            vector< _LayerEdge* > & badSmooEdges);
479     bool FindIntersection( SMESH_ElementSearcher&   searcher,
480                            double &                 distance,
481                            const double&            epsilon,
482                            _EdgesOnShape&           eos,
483                            const SMDS_MeshElement** face = 0);
484     bool SegTriaInter( const gp_Ax1&        lastSegment,
485                        const gp_XYZ&        p0,
486                        const gp_XYZ&        p1,
487                        const gp_XYZ&        p2,
488                        double&              dist,
489                        const double&        epsilon) const;
490     bool SegTriaInter( const gp_Ax1&        lastSegment,
491                        const SMDS_MeshNode* n0,
492                        const SMDS_MeshNode* n1,
493                        const SMDS_MeshNode* n2,
494                        double&              dist,
495                        const double&        epsilon) const
496     { return SegTriaInter( lastSegment,
497                            SMESH_TNodeXYZ( n0 ), SMESH_TNodeXYZ( n1 ), SMESH_TNodeXYZ( n2 ),
498                            dist, epsilon );
499     }
500     const gp_XYZ& PrevPos() const { return _pos[ _pos.size() - 2 ]; }
501     gp_XYZ PrevCheckPos( _EdgesOnShape* eos=0 ) const;
502     gp_Ax1 LastSegment(double& segLen, _EdgesOnShape& eos) const;
503     gp_XY  LastUV( const TopoDS_Face& F, _EdgesOnShape& eos, int which=-1 ) const;
504     bool   IsOnEdge() const { return _2neibors; }
505     bool   IsOnFace() const { return ( _nodes[0]->GetPosition()->GetDim() == 2 ); }
506     int    BaseShapeDim() const { return _nodes[0]->GetPosition()->GetDim(); }
507     gp_XYZ Copy( _LayerEdge& other, _EdgesOnShape& eos, SMESH_MesherHelper& helper );
508     void   SetCosin( double cosin );
509     void   SetNormal( const gp_XYZ& n ) { _normal = n; }
510     void   SetMaxLen( double l ) { _maxLen = l; }
511     int    NbSteps() const { return _pos.size() - 1; } // nb inlation steps
512     bool   IsNeiborOnEdge( const _LayerEdge* edge ) const;
513     void   SetSmooLen( double len ) { // set _len at which smoothing is needed
514       _cosin = len; // as for _LayerEdge's on FACE _cosin is not used
515     }
516     double GetSmooLen() { return _cosin; } // for _LayerEdge's on FACE _cosin is not used
517
518     gp_XYZ smoothLaplacian();
519     gp_XYZ smoothAngular();
520     gp_XYZ smoothLengthWeighted();
521     gp_XYZ smoothCentroidal();
522     gp_XYZ smoothNefPolygon();
523
524     enum { FUN_LAPLACIAN, FUN_LENWEIGHTED, FUN_CENTROIDAL, FUN_NEFPOLY, FUN_ANGULAR, FUN_NB };
525     static const int theNbSmooFuns = FUN_NB;
526     static PSmooFun _funs[theNbSmooFuns];
527     static const char* _funNames[theNbSmooFuns+1];
528     int smooFunID( PSmooFun fun=0) const;
529   };
530   _LayerEdge::PSmooFun _LayerEdge::_funs[theNbSmooFuns] = { &_LayerEdge::smoothLaplacian,
531                                                             &_LayerEdge::smoothLengthWeighted,
532                                                             &_LayerEdge::smoothCentroidal,
533                                                             &_LayerEdge::smoothNefPolygon,
534                                                             &_LayerEdge::smoothAngular };
535   const char* _LayerEdge::_funNames[theNbSmooFuns+1] = { "Laplacian",
536                                                          "LengthWeighted",
537                                                          "Centroidal",
538                                                          "NefPolygon",
539                                                          "Angular",
540                                                          "None"};
541   struct _LayerEdgeCmp
542   {
543     bool operator () (const _LayerEdge* e1, const _LayerEdge* e2) const
544     {
545       const bool cmpNodes = ( e1 && e2 && e1->_nodes.size() && e2->_nodes.size() );
546       return cmpNodes ? ( e1->_nodes[0]->GetID() < e2->_nodes[0]->GetID()) : ( e1 < e2 );
547     }
548   };
549   //--------------------------------------------------------------------------------
550   /*!
551    * A 2D half plane used by _LayerEdge::smoothNefPolygon()
552    */
553   struct _halfPlane
554   {
555     gp_XY _pos, _dir, _inNorm;
556     bool IsOut( const gp_XY p, const double tol ) const
557     {
558       return _inNorm * ( p - _pos ) < -tol;
559     }
560     bool FindIntersection( const _halfPlane& hp, gp_XY & intPnt )
561     {
562       //const double eps = 1e-10;
563       double D = _dir.Crossed( hp._dir );
564       if ( fabs(D) < std::numeric_limits<double>::min())
565         return false;
566       gp_XY vec21 = _pos - hp._pos; 
567       double u = hp._dir.Crossed( vec21 ) / D; 
568       intPnt = _pos + _dir * u;
569       return true;
570     }
571   };
572   //--------------------------------------------------------------------------------
573   /*!
574    * Structure used to smooth a _LayerEdge based on an EDGE.
575    */
576   struct _2NearEdges
577   {
578     double               _wgt  [2]; // weights of _nodes
579     _LayerEdge*          _edges[2];
580
581      // normal to plane passing through _LayerEdge._normal and tangent of EDGE
582     gp_XYZ*              _plnNorm;
583
584     _2NearEdges() { _edges[0]=_edges[1]=0; _plnNorm = 0; }
585     const SMDS_MeshNode* tgtNode(bool is2nd) {
586       return _edges[is2nd] ? _edges[is2nd]->_nodes.back() : 0;
587     }
588     const SMDS_MeshNode* srcNode(bool is2nd) {
589       return _edges[is2nd] ? _edges[is2nd]->_nodes[0] : 0;
590     }
591     void reverse() {
592       std::swap( _wgt  [0], _wgt  [1] );
593       std::swap( _edges[0], _edges[1] );
594     }
595     void set( _LayerEdge* e1, _LayerEdge* e2, double w1, double w2 ) {
596       _edges[0] = e1; _edges[1] = e2; _wgt[0] = w1; _wgt[1] = w2;
597     }
598     bool include( const _LayerEdge* e ) {
599       return ( _edges[0] == e || _edges[1] == e );
600     }
601   };
602
603
604   //--------------------------------------------------------------------------------
605   /*!
606    * \brief Layers parameters got by averaging several hypotheses
607    */
608   struct AverageHyp
609   {
610     AverageHyp( const StdMeshers_ViscousLayers* hyp = 0 )
611       :_nbLayers(0), _nbHyps(0), _method(0), _thickness(0), _stretchFactor(0)
612     {
613       Add( hyp );
614     }
615     void Add( const StdMeshers_ViscousLayers* hyp )
616     {
617       if ( hyp )
618       {
619         _nbHyps++;
620         _nbLayers       = hyp->GetNumberLayers();
621         //_thickness     += hyp->GetTotalThickness();
622         _thickness      = Max( _thickness, hyp->GetTotalThickness() );
623         _stretchFactor += hyp->GetStretchFactor();
624         _method         = hyp->GetMethod();
625       }
626     }
627     double GetTotalThickness() const { return _thickness; /*_nbHyps ? _thickness / _nbHyps : 0;*/ }
628     double GetStretchFactor()  const { return _nbHyps ? _stretchFactor / _nbHyps : 0; }
629     int    GetNumberLayers()   const { return _nbLayers; }
630     int    GetMethod()         const { return _method; }
631
632     bool   UseSurfaceNormal()  const
633     { return _method == StdMeshers_ViscousLayers::SURF_OFFSET_SMOOTH; }
634     bool   ToSmooth()          const
635     { return _method == StdMeshers_ViscousLayers::SURF_OFFSET_SMOOTH; }
636     bool   IsOffsetMethod()    const
637     { return _method == StdMeshers_ViscousLayers::FACE_OFFSET; }
638
639   private:
640     int     _nbLayers, _nbHyps, _method;
641     double  _thickness, _stretchFactor;
642   };
643
644   //--------------------------------------------------------------------------------
645   /*!
646    * \brief _LayerEdge's on a shape and other shape data
647    */
648   struct _EdgesOnShape
649   {
650     vector< _LayerEdge* > _edges;
651
652     TopoDS_Shape          _shape;
653     TGeomID               _shapeID;
654     SMESH_subMesh *       _subMesh;
655     // face or edge w/o layer along or near which _edges are inflated
656     TopoDS_Shape          _sWOL;
657     bool                  _isRegularSWOL; // w/o singularities
658     // averaged StdMeshers_ViscousLayers parameters
659     AverageHyp            _hyp;
660     bool                  _toSmooth;
661     _Smoother1D*          _edgeSmoother;
662     vector< _EdgesOnShape* > _eosConcaVer; // edges at concave VERTEXes of a FACE
663     vector< _EdgesOnShape* > _eosC1; // to smooth together several C1 continues shapes
664
665     typedef std::unordered_map< const SMDS_MeshElement*, gp_XYZ > TFace2NormMap;
666     TFace2NormMap            _faceNormals; // if _shape is FACE
667     vector< _EdgesOnShape* > _faceEOS; // to get _faceNormals of adjacent FACEs
668
669     Handle(ShapeAnalysis_Surface) _offsetSurf;
670     _LayerEdge*                   _edgeForOffset;
671
672     _SolidData*            _data; // parent SOLID
673
674     _LayerEdge*      operator[](size_t i) const { return (_LayerEdge*) _edges[i]; }
675     size_t           size() const { return _edges.size(); }
676     TopAbs_ShapeEnum ShapeType() const
677     { return _shape.IsNull() ? TopAbs_SHAPE : _shape.ShapeType(); }
678     TopAbs_ShapeEnum SWOLType() const
679     { return _sWOL.IsNull() ? TopAbs_SHAPE : _sWOL.ShapeType(); }
680     bool             HasC1( const _EdgesOnShape* other ) const
681     { return std::find( _eosC1.begin(), _eosC1.end(), other ) != _eosC1.end(); }
682     bool             GetNormal( const SMDS_MeshElement* face, gp_Vec& norm );
683     _SolidData&      GetData() const { return *_data; }
684
685     _EdgesOnShape(): _shapeID(-1), _subMesh(0), _toSmooth(false), _edgeSmoother(0) {}
686   };
687
688   //--------------------------------------------------------------------------------
689   /*!
690    * \brief Convex FACE whose radius of curvature is less than the thickness of
691    *        layers. It is used to detect distortion of prisms based on a convex
692    *        FACE and to update normals to enable further increasing the thickness
693    */
694   struct _ConvexFace
695   {
696     TopoDS_Face                     _face;
697
698     // edges whose _simplices are used to detect prism distortion
699     vector< _LayerEdge* >           _simplexTestEdges;
700
701     // map a sub-shape to _SolidData::_edgesOnShape
702     map< TGeomID, _EdgesOnShape* >  _subIdToEOS;
703
704     bool                            _isTooCurved;
705     bool                            _normalsFixed;
706     bool                            _normalsFixedOnBorders; // used in putOnOffsetSurface()
707
708     double GetMaxCurvature( _SolidData&         data,
709                             _EdgesOnShape&      eof,
710                             BRepLProp_SLProps&  surfProp,
711                             SMESH_MesherHelper& helper);
712
713     bool GetCenterOfCurvature( _LayerEdge*         ledge,
714                                BRepLProp_SLProps&  surfProp,
715                                SMESH_MesherHelper& helper,
716                                gp_Pnt &            center ) const;
717     bool CheckPrisms() const;
718   };
719
720   //--------------------------------------------------------------------------------
721   /*!
722    * \brief Structure holding _LayerEdge's based on EDGEs that will collide
723    *        at inflation up to the full thickness. A detected collision
724    *        is fixed in updateNormals()
725    */
726   struct _CollisionEdges
727   {
728     _LayerEdge*           _edge;
729     vector< _LayerEdge* > _intEdges; // each pair forms an intersected quadrangle
730     const SMDS_MeshNode* nSrc(int i) const { return _intEdges[i]->_nodes[0]; }
731     const SMDS_MeshNode* nTgt(int i) const { return _intEdges[i]->_nodes.back(); }
732   };
733
734   //--------------------------------------------------------------------------------
735   /*!
736    * \brief Data of a SOLID
737    */
738   struct _SolidData
739   {
740     typedef const StdMeshers_ViscousLayers* THyp;
741     TopoDS_Shape                    _solid;
742     TopTools_MapOfShape             _before; // SOLIDs to be computed before _solid
743     TGeomID                         _index; // SOLID id
744     _MeshOfSolid*                   _proxyMesh;
745     list< THyp >                    _hyps;
746     list< TopoDS_Shape >            _hypShapes;
747     map< TGeomID, THyp >            _face2hyp; // filled if _hyps.size() > 1
748     set< TGeomID >                  _reversedFaceIds;
749     set< TGeomID >                  _ignoreFaceIds; // WOL FACEs and FACEs of other SOLIDs
750
751     double                          _stepSize, _stepSizeCoeff, _geomSize;
752     const SMDS_MeshNode*            _stepSizeNodes[2];
753
754     TNode2Edge                      _n2eMap; // nodes and _LayerEdge's based on them
755
756     // map to find _n2eMap of another _SolidData by a shrink shape shared by two _SolidData's
757     map< TGeomID, TNode2Edge* >     _s2neMap;
758     // _LayerEdge's with underlying shapes
759     vector< _EdgesOnShape >         _edgesOnShape;
760
761     // key:   an id of shape (EDGE or VERTEX) shared by a FACE with
762     //        layers and a FACE w/o layers
763     // value: the shape (FACE or EDGE) to shrink mesh on.
764     //       _LayerEdge's basing on nodes on key shape are inflated along the value shape
765     map< TGeomID, TopoDS_Shape >     _shrinkShape2Shape;
766
767     // Convex FACEs whose radius of curvature is less than the thickness of layers
768     map< TGeomID, _ConvexFace >      _convexFaces;
769
770     // shapes (EDGEs and VERTEXes) shrink from which is forbidden due to collisions with
771     // the adjacent SOLID
772     set< TGeomID >                   _noShrinkShapes;
773
774     int                              _nbShapesToSmooth;
775
776     vector< _CollisionEdges >        _collisionEdges;
777     set< TGeomID >                   _concaveFaces;
778
779     double                           _maxThickness; // of all _hyps
780     double                           _minThickness; // of all _hyps
781
782     double                           _epsilon; // precision for SegTriaInter()
783
784     SMESH_MesherHelper*              _helper;
785
786     _SolidData(const TopoDS_Shape& s=TopoDS_Shape(),
787                _MeshOfSolid*       m=0)
788       :_solid(s), _proxyMesh(m), _helper(0) {}
789     ~_SolidData();
790
791     void SortOnEdge( const TopoDS_Edge& E, vector< _LayerEdge* >& edges);
792     void Sort2NeiborsOnEdge( vector< _LayerEdge* >& edges );
793
794     _ConvexFace* GetConvexFace( const TGeomID faceID ) {
795       map< TGeomID, _ConvexFace >::iterator id2face = _convexFaces.find( faceID );
796       return id2face == _convexFaces.end() ? 0 : & id2face->second;
797     }
798     _EdgesOnShape* GetShapeEdges(const TGeomID       shapeID );
799     _EdgesOnShape* GetShapeEdges(const TopoDS_Shape& shape );
800     _EdgesOnShape* GetShapeEdges(const _LayerEdge*   edge )
801     { return GetShapeEdges( edge->_nodes[0]->getshapeId() ); }
802
803     SMESH_MesherHelper& GetHelper() const { return *_helper; }
804
805     void UnmarkEdges( int flag = _LayerEdge::MARKED ) {
806       for ( size_t i = 0; i < _edgesOnShape.size(); ++i )
807         for ( size_t j = 0; j < _edgesOnShape[i]._edges.size(); ++j )
808           _edgesOnShape[i]._edges[j]->Unset( flag );
809     }
810     void AddShapesToSmooth( const set< _EdgesOnShape* >& shape,
811                             const set< _EdgesOnShape* >* edgesNoAnaSmooth=0 );
812
813     void PrepareEdgesToSmoothOnFace( _EdgesOnShape* eof, bool substituteSrcNodes );
814   };
815   //--------------------------------------------------------------------------------
816   /*!
817    * \brief Offset plane used in getNormalByOffset()
818    */
819   struct _OffsetPlane
820   {
821     gp_Pln _plane;
822     int    _faceIndex;
823     int    _faceIndexNext[2];
824     gp_Lin _lines[2]; // line of intersection with neighbor _OffsetPlane's
825     bool   _isLineOK[2];
826     _OffsetPlane() {
827       _isLineOK[0] = _isLineOK[1] = false; _faceIndexNext[0] = _faceIndexNext[1] = -1;
828     }
829     void   ComputeIntersectionLine( _OffsetPlane&        pln, 
830                                     const TopoDS_Edge&   E,
831                                     const TopoDS_Vertex& V );
832     gp_XYZ GetCommonPoint(bool& isFound, const TopoDS_Vertex& V) const;
833     int    NbLines() const { return _isLineOK[0] + _isLineOK[1]; }
834   };
835   //--------------------------------------------------------------------------------
836   /*!
837    * \brief Container of centers of curvature at nodes on an EDGE bounding _ConvexFace
838    */
839   struct _CentralCurveOnEdge
840   {
841     bool                  _isDegenerated;
842     vector< gp_Pnt >      _curvaCenters;
843     vector< _LayerEdge* > _ledges;
844     vector< gp_XYZ >      _normals; // new normal for each of _ledges
845     vector< double >      _segLength2;
846
847     TopoDS_Edge           _edge;
848     TopoDS_Face           _adjFace;
849     bool                  _adjFaceToSmooth;
850
851     void Append( const gp_Pnt& center, _LayerEdge* ledge )
852     {
853       if ( ledge->Is( _LayerEdge::MULTI_NORMAL ))
854         return;
855       if ( _curvaCenters.size() > 0 )
856         _segLength2.push_back( center.SquareDistance( _curvaCenters.back() ));
857       _curvaCenters.push_back( center );
858       _ledges.push_back( ledge );
859       _normals.push_back( ledge->_normal );
860     }
861     bool FindNewNormal( const gp_Pnt& center, gp_XYZ& newNormal );
862     void SetShapes( const TopoDS_Edge&  edge,
863                     const _ConvexFace&  convFace,
864                     _SolidData&         data,
865                     SMESH_MesherHelper& helper);
866   };
867   //--------------------------------------------------------------------------------
868   /*!
869    * \brief Data of node on a shrinked FACE
870    */
871   struct _SmoothNode
872   {
873     const SMDS_MeshNode*         _node;
874     vector<_Simplex>             _simplices; // for quality check
875
876     enum SmoothType { LAPLACIAN, CENTROIDAL, ANGULAR, TFI };
877
878     bool Smooth(int&                  badNb,
879                 Handle(Geom_Surface)& surface,
880                 SMESH_MesherHelper&   helper,
881                 const double          refSign,
882                 SmoothType            how,
883                 bool                  set3D);
884
885     gp_XY computeAngularPos(vector<gp_XY>& uv,
886                             const gp_XY&   uvToFix,
887                             const double   refSign );
888   };
889   struct PyDump;
890   //--------------------------------------------------------------------------------
891   /*!
892    * \brief Builder of viscous layers
893    */
894   class _ViscousBuilder
895   {
896   public:
897     _ViscousBuilder();
898     // does it's job
899     SMESH_ComputeErrorPtr Compute(SMESH_Mesh&         mesh,
900                                   const TopoDS_Shape& shape);
901     // check validity of hypotheses
902     SMESH_ComputeErrorPtr CheckHypotheses( SMESH_Mesh&         mesh,
903                                            const TopoDS_Shape& shape );
904
905     // restore event listeners used to clear an inferior dim sub-mesh modified by viscous layers
906     void RestoreListeners();
907
908     // computes SMESH_ProxyMesh::SubMesh::_n2n;
909     bool MakeN2NMap( _MeshOfSolid* pm );
910
911   private:
912
913     bool findSolidsWithLayers();
914     bool setBefore( _SolidData& solidBefore, _SolidData& solidAfter );
915     bool findFacesWithLayers(const bool onlyWith=false);
916     void getIgnoreFaces(const TopoDS_Shape&             solid,
917                         const StdMeshers_ViscousLayers* hyp,
918                         const TopoDS_Shape&             hypShape,
919                         set<TGeomID>&                   ignoreFaces);
920     bool makeLayer(_SolidData& data);
921     void setShapeData( _EdgesOnShape& eos, SMESH_subMesh* sm, _SolidData& data );
922     bool setEdgeData( _LayerEdge& edge, _EdgesOnShape& eos,
923                       SMESH_MesherHelper& helper, _SolidData& data);
924     gp_XYZ getFaceNormal(const SMDS_MeshNode* n,
925                          const TopoDS_Face&   face,
926                          SMESH_MesherHelper&  helper,
927                          bool&                isOK,
928                          bool                 shiftInside=false);
929     bool getFaceNormalAtSingularity(const gp_XY&        uv,
930                                     const TopoDS_Face&  face,
931                                     SMESH_MesherHelper& helper,
932                                     gp_Dir&             normal );
933     gp_XYZ getWeigthedNormal( const _LayerEdge*                edge );
934     gp_XYZ getNormalByOffset( _LayerEdge*                      edge,
935                               std::pair< TopoDS_Face, gp_XYZ > fId2Normal[],
936                               int                              nbFaces,
937                               bool                             lastNoOffset = false);
938     bool findNeiborsOnEdge(const _LayerEdge*     edge,
939                            const SMDS_MeshNode*& n1,
940                            const SMDS_MeshNode*& n2,
941                            _EdgesOnShape&        eos,
942                            _SolidData&           data);
943     void findSimplexTestEdges( _SolidData&                    data,
944                                vector< vector<_LayerEdge*> >& edgesByGeom);
945     void computeGeomSize( _SolidData& data );
946     bool findShapesToSmooth( _SolidData& data);
947     void limitStepSizeByCurvature( _SolidData&  data );
948     void limitStepSize( _SolidData&             data,
949                         const SMDS_MeshElement* face,
950                         const _LayerEdge*       maxCosinEdge );
951     void limitStepSize( _SolidData& data, const double minSize);
952     bool inflate(_SolidData& data);
953     bool smoothAndCheck(_SolidData& data, const int nbSteps, double & distToIntersection);
954     int  invalidateBadSmooth( _SolidData&               data,
955                               SMESH_MesherHelper&       helper,
956                               vector< _LayerEdge* >&    badSmooEdges,
957                               vector< _EdgesOnShape* >& eosC1,
958                               const int                 infStep );
959     void makeOffsetSurface( _EdgesOnShape& eos, SMESH_MesherHelper& );
960     void putOnOffsetSurface( _EdgesOnShape& eos, int infStep,
961                              vector< _EdgesOnShape* >& eosC1,
962                              int smooStep=0, int moveAll=false );
963     void findCollisionEdges( _SolidData& data, SMESH_MesherHelper& helper );
964     void findEdgesToUpdateNormalNearConvexFace( _ConvexFace &       convFace,
965                                                 _SolidData&         data,
966                                                 SMESH_MesherHelper& helper );
967     void limitMaxLenByCurvature( _SolidData& data, SMESH_MesherHelper& helper );
968     void limitMaxLenByCurvature( _LayerEdge* e1, _LayerEdge* e2,
969                                  _EdgesOnShape& eos1, _EdgesOnShape& eos2,
970                                  const bool isSmoothable );
971     bool updateNormals( _SolidData& data, SMESH_MesherHelper& helper, int stepNb, double stepSize );
972     bool updateNormalsOfConvexFaces( _SolidData&         data,
973                                      SMESH_MesherHelper& helper,
974                                      int                 stepNb );
975     void updateNormalsOfC1Vertices( _SolidData& data );
976     bool updateNormalsOfSmoothed( _SolidData&         data,
977                                   SMESH_MesherHelper& helper,
978                                   const int           nbSteps,
979                                   const double        stepSize );
980     bool isNewNormalOk( _SolidData&   data,
981                         _LayerEdge&   edge,
982                         const gp_XYZ& newNormal);
983     bool refine(_SolidData& data);
984     bool shrink(_SolidData& data);
985     bool prepareEdgeToShrink( _LayerEdge& edge, _EdgesOnShape& eos,
986                               SMESH_MesherHelper& helper,
987                               const SMESHDS_SubMesh* faceSubMesh );
988     void restoreNoShrink( _LayerEdge& edge ) const;
989     void fixBadFaces(const TopoDS_Face&          F,
990                      SMESH_MesherHelper&         helper,
991                      const bool                  is2D,
992                      const int                   step,
993                      set<const SMDS_MeshNode*> * involvedNodes=NULL);
994     bool addBoundaryElements(_SolidData& data);
995
996     bool error( const string& text, int solidID=-1 );
997     SMESHDS_Mesh* getMeshDS() const { return _mesh->GetMeshDS(); }
998
999     // debug
1000     void makeGroupOfLE();
1001
1002     SMESH_Mesh*                _mesh;
1003     SMESH_ComputeErrorPtr      _error;
1004
1005     vector<                    _SolidData >  _sdVec;
1006     TopTools_IndexedMapOfShape _solids; // to find _SolidData by a solid
1007     TopTools_MapOfShape        _shrinkedFaces;
1008
1009     int                        _tmpFaceID;
1010     PyDump*                    _pyDump;
1011   };
1012   //--------------------------------------------------------------------------------
1013   /*!
1014    * \brief Shrinker of nodes on the EDGE
1015    */
1016   class _Shrinker1D
1017   {
1018     TopoDS_Edge                   _geomEdge;
1019     vector<double>                _initU;
1020     vector<double>                _normPar;
1021     vector<const SMDS_MeshNode*>  _nodes;
1022     const _LayerEdge*             _edges[2];
1023     bool                          _done;
1024   public:
1025     void AddEdge( const _LayerEdge* e, _EdgesOnShape& eos, SMESH_MesherHelper& helper );
1026     void Compute(bool set3D, SMESH_MesherHelper& helper);
1027     void RestoreParams();
1028     void SwapSrcTgtNodes(SMESHDS_Mesh* mesh);
1029     const TopoDS_Edge& GeomEdge() const { return _geomEdge; }
1030     const SMDS_MeshNode* TgtNode( bool is2nd ) const
1031     { return _edges[is2nd] ? _edges[is2nd]->_nodes.back() : 0; }
1032     const SMDS_MeshNode* SrcNode( bool is2nd ) const
1033     { return _edges[is2nd] ? _edges[is2nd]->_nodes[0] : 0; }
1034   };
1035   //--------------------------------------------------------------------------------
1036   /*!
1037    * \brief Smoother of _LayerEdge's on EDGE.
1038    */
1039   struct _Smoother1D
1040   {
1041     struct OffPnt // point of the offsetted EDGE
1042     {
1043       gp_XYZ      _xyz;    // coord of a point inflated from EDGE w/o smooth
1044       double      _len;    // length reached at previous inflation step
1045       double      _param;  // on EDGE
1046       _2NearEdges _2edges; // 2 neighbor _LayerEdge's
1047       gp_XYZ      _edgeDir;// EDGE tangent at _param
1048       double Distance( const OffPnt& p ) const { return ( _xyz - p._xyz ).Modulus(); }
1049     };
1050     vector< OffPnt >   _offPoints;
1051     vector< double >   _leParams; // normalized param of _eos._edges on EDGE
1052     Handle(Geom_Curve) _anaCurve; // for analytic smooth
1053     _LayerEdge         _leOnV[2]; // _LayerEdge's holding normal to the EDGE at VERTEXes
1054     gp_XYZ             _edgeDir[2]; // tangent at VERTEXes
1055     size_t             _iSeg[2];  // index of segment where extreme tgt node is projected
1056     _EdgesOnShape&     _eos;
1057     double             _curveLen; // length of the EDGE
1058     std::pair<int,int> _eToSmooth[2]; // <from,to> indices of _LayerEdge's in _eos
1059
1060     static Handle(Geom_Curve) CurveForSmooth( const TopoDS_Edge&  E,
1061                                               _EdgesOnShape&      eos,
1062                                               SMESH_MesherHelper& helper);
1063
1064     _Smoother1D( Handle(Geom_Curve) curveForSmooth,
1065                  _EdgesOnShape&     eos )
1066       : _anaCurve( curveForSmooth ), _eos( eos )
1067     {
1068     }
1069     bool Perform(_SolidData&                    data,
1070                  Handle(ShapeAnalysis_Surface)& surface,
1071                  const TopoDS_Face&             F,
1072                  SMESH_MesherHelper&            helper );
1073
1074     void prepare(_SolidData& data );
1075
1076     void findEdgesToSmooth();
1077
1078     bool isToSmooth( int iE );
1079
1080     bool smoothAnalyticEdge( _SolidData&                    data,
1081                              Handle(ShapeAnalysis_Surface)& surface,
1082                              const TopoDS_Face&             F,
1083                              SMESH_MesherHelper&            helper);
1084     bool smoothComplexEdge( _SolidData&                    data,
1085                             Handle(ShapeAnalysis_Surface)& surface,
1086                             const TopoDS_Face&             F,
1087                             SMESH_MesherHelper&            helper);
1088     gp_XYZ getNormalNormal( const gp_XYZ & normal,
1089                             const gp_XYZ&  edgeDir);
1090     _LayerEdge* getLEdgeOnV( bool is2nd )
1091     {
1092       return _eos._edges[ is2nd ? _eos._edges.size()-1 : 0 ]->_2neibors->_edges[ is2nd ];
1093     }
1094     bool isAnalytic() const { return !_anaCurve.IsNull(); }
1095
1096     void offPointsToPython() const; // debug
1097   };
1098   //--------------------------------------------------------------------------------
1099   /*!
1100    * \brief Class of temporary mesh face.
1101    * We can't use SMDS_FaceOfNodes since it's impossible to set it's ID which is
1102    * needed because SMESH_ElementSearcher internaly uses set of elements sorted by ID
1103    */
1104   struct _TmpMeshFace : public SMDS_PolygonalFaceOfNodes
1105   {
1106     const SMDS_MeshElement* _srcFace;
1107
1108     _TmpMeshFace( const vector<const SMDS_MeshNode*>& nodes,
1109                   int                                 ID,
1110                   int                                 faceID=-1,
1111                   const SMDS_MeshElement*             srcFace=0 ):
1112       SMDS_PolygonalFaceOfNodes(nodes), _srcFace( srcFace ) { setID( ID ); setShapeID( faceID ); }
1113     virtual SMDSAbs_EntityType  GetEntityType() const
1114     { return _srcFace ? _srcFace->GetEntityType() : SMDSEntity_Quadrangle; }
1115     virtual SMDSAbs_GeometryType GetGeomType()  const
1116     { return _srcFace ? _srcFace->GetGeomType() : SMDSGeom_QUADRANGLE; }
1117   };
1118   //--------------------------------------------------------------------------------
1119   /*!
1120    * \brief Class of temporary mesh quadrangle face storing _LayerEdge it's based on
1121    */
1122   struct _TmpMeshFaceOnEdge : public _TmpMeshFace
1123   {
1124     _LayerEdge *_le1, *_le2;
1125     _TmpMeshFaceOnEdge( _LayerEdge* le1, _LayerEdge* le2, int ID ):
1126       _TmpMeshFace( vector<const SMDS_MeshNode*>(4), ID ), _le1(le1), _le2(le2)
1127     {
1128       myNodes[0]=_le1->_nodes[0];
1129       myNodes[1]=_le1->_nodes.back();
1130       myNodes[2]=_le2->_nodes.back();
1131       myNodes[3]=_le2->_nodes[0];
1132     }
1133     const SMDS_MeshNode* n( size_t i ) const
1134     {
1135       return myNodes[ i ];
1136     }
1137     gp_XYZ GetDir() const // return average direction of _LayerEdge's, normal to EDGE
1138     {
1139       SMESH_TNodeXYZ p0s( myNodes[0] );
1140       SMESH_TNodeXYZ p0t( myNodes[1] );
1141       SMESH_TNodeXYZ p1t( myNodes[2] );
1142       SMESH_TNodeXYZ p1s( myNodes[3] );
1143       gp_XYZ  v0 = p0t - p0s;
1144       gp_XYZ  v1 = p1t - p1s;
1145       gp_XYZ v01 = p1s - p0s;
1146       gp_XYZ   n = ( v0 ^ v01 ) + ( v1 ^ v01 );
1147       gp_XYZ   d = v01 ^ n;
1148       d.Normalize();
1149       return d;
1150     }
1151     gp_XYZ GetDir(_LayerEdge* le1, _LayerEdge* le2) // return average direction of _LayerEdge's
1152     {
1153       myNodes[0]=le1->_nodes[0];
1154       myNodes[1]=le1->_nodes.back();
1155       myNodes[2]=le2->_nodes.back();
1156       myNodes[3]=le2->_nodes[0];
1157       return GetDir();
1158     }
1159   };
1160   //--------------------------------------------------------------------------------
1161   /*!
1162    * \brief Retriever of node coordinates either directly or from a surface by node UV.
1163    * \warning Location of a surface is ignored
1164    */
1165   struct _NodeCoordHelper
1166   {
1167     SMESH_MesherHelper&        _helper;
1168     const TopoDS_Face&         _face;
1169     Handle(Geom_Surface)       _surface;
1170     gp_XYZ (_NodeCoordHelper::* _fun)(const SMDS_MeshNode* n) const;
1171
1172     _NodeCoordHelper(const TopoDS_Face& F, SMESH_MesherHelper& helper, bool is2D)
1173       : _helper( helper ), _face( F )
1174     {
1175       if ( is2D )
1176       {
1177         TopLoc_Location loc;
1178         _surface = BRep_Tool::Surface( _face, loc );
1179       }
1180       if ( _surface.IsNull() )
1181         _fun = & _NodeCoordHelper::direct;
1182       else
1183         _fun = & _NodeCoordHelper::byUV;
1184     }
1185     gp_XYZ operator()(const SMDS_MeshNode* n) const { return (this->*_fun)( n ); }
1186
1187   private:
1188     gp_XYZ direct(const SMDS_MeshNode* n) const
1189     {
1190       return SMESH_TNodeXYZ( n );
1191     }
1192     gp_XYZ byUV  (const SMDS_MeshNode* n) const
1193     {
1194       gp_XY uv = _helper.GetNodeUV( _face, n );
1195       return _surface->Value( uv.X(), uv.Y() ).XYZ();
1196     }
1197   };
1198
1199   //================================================================================
1200   /*!
1201    * \brief Check angle between vectors 
1202    */
1203   //================================================================================
1204
1205   inline bool isLessAngle( const gp_Vec& v1, const gp_Vec& v2, const double cos )
1206   {
1207     double dot = v1 * v2; // cos * |v1| * |v2|
1208     double l1  = v1.SquareMagnitude();
1209     double l2  = v2.SquareMagnitude();
1210     return (( dot * cos >= 0 ) && 
1211             ( dot * dot ) / l1 / l2 >= ( cos * cos ));
1212   }
1213
1214 } // namespace VISCOUS_3D
1215
1216
1217
1218 //================================================================================
1219 // StdMeshers_ViscousLayers hypothesis
1220 //
1221 StdMeshers_ViscousLayers::StdMeshers_ViscousLayers(int hypId, SMESH_Gen* gen)
1222   :SMESH_Hypothesis(hypId, gen),
1223    _isToIgnoreShapes(1), _nbLayers(1), _thickness(1), _stretchFactor(1),
1224    _method( SURF_OFFSET_SMOOTH )
1225 {
1226   _name = StdMeshers_ViscousLayers::GetHypType();
1227   _param_algo_dim = -3; // auxiliary hyp used by 3D algos
1228 } // --------------------------------------------------------------------------------
1229 void StdMeshers_ViscousLayers::SetBndShapes(const std::vector<int>& faceIds, bool toIgnore)
1230 {
1231   if ( faceIds != _shapeIds )
1232     _shapeIds = faceIds, NotifySubMeshesHypothesisModification();
1233   if ( _isToIgnoreShapes != toIgnore )
1234     _isToIgnoreShapes = toIgnore, NotifySubMeshesHypothesisModification();
1235 } // --------------------------------------------------------------------------------
1236 void StdMeshers_ViscousLayers::SetTotalThickness(double thickness)
1237 {
1238   if ( thickness != _thickness )
1239     _thickness = thickness, NotifySubMeshesHypothesisModification();
1240 } // --------------------------------------------------------------------------------
1241 void StdMeshers_ViscousLayers::SetNumberLayers(int nb)
1242 {
1243   if ( _nbLayers != nb )
1244     _nbLayers = nb, NotifySubMeshesHypothesisModification();
1245 } // --------------------------------------------------------------------------------
1246 void StdMeshers_ViscousLayers::SetStretchFactor(double factor)
1247 {
1248   if ( _stretchFactor != factor )
1249     _stretchFactor = factor, NotifySubMeshesHypothesisModification();
1250 } // --------------------------------------------------------------------------------
1251 void StdMeshers_ViscousLayers::SetMethod( ExtrusionMethod method )
1252 {
1253   if ( _method != method )
1254     _method = method, NotifySubMeshesHypothesisModification();
1255 } // --------------------------------------------------------------------------------
1256 SMESH_ProxyMesh::Ptr
1257 StdMeshers_ViscousLayers::Compute(SMESH_Mesh&         theMesh,
1258                                   const TopoDS_Shape& theShape,
1259                                   const bool          toMakeN2NMap) const
1260 {
1261   using namespace VISCOUS_3D;
1262   _ViscousBuilder builder;
1263   SMESH_ComputeErrorPtr err = builder.Compute( theMesh, theShape );
1264   if ( err && !err->IsOK() )
1265     return SMESH_ProxyMesh::Ptr();
1266
1267   vector<SMESH_ProxyMesh::Ptr> components;
1268   TopExp_Explorer exp( theShape, TopAbs_SOLID );
1269   for ( ; exp.More(); exp.Next() )
1270   {
1271     if ( _MeshOfSolid* pm =
1272          _ViscousListener::GetSolidMesh( &theMesh, exp.Current(), /*toCreate=*/false))
1273     {
1274       if ( toMakeN2NMap && !pm->_n2nMapComputed )
1275         if ( !builder.MakeN2NMap( pm ))
1276           return SMESH_ProxyMesh::Ptr();
1277       components.push_back( SMESH_ProxyMesh::Ptr( pm ));
1278       pm->myIsDeletable = false; // it will de deleted by boost::shared_ptr
1279
1280       if ( pm->_warning && !pm->_warning->IsOK() )
1281       {
1282         SMESH_subMesh* sm = theMesh.GetSubMesh( exp.Current() );
1283         SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1284         if ( !smError || smError->IsOK() )
1285           smError = pm->_warning;
1286       }
1287     }
1288     _ViscousListener::RemoveSolidMesh ( &theMesh, exp.Current() );
1289   }
1290   switch ( components.size() )
1291   {
1292   case 0: break;
1293
1294   case 1: return components[0];
1295
1296   default: return SMESH_ProxyMesh::Ptr( new SMESH_ProxyMesh( components ));
1297   }
1298   return SMESH_ProxyMesh::Ptr();
1299 } // --------------------------------------------------------------------------------
1300 std::ostream & StdMeshers_ViscousLayers::SaveTo(std::ostream & save)
1301 {
1302   save << " " << _nbLayers
1303        << " " << _thickness
1304        << " " << _stretchFactor
1305        << " " << _shapeIds.size();
1306   for ( size_t i = 0; i < _shapeIds.size(); ++i )
1307     save << " " << _shapeIds[i];
1308   save << " " << !_isToIgnoreShapes; // negate to keep the behavior in old studies.
1309   save << " " << _method;
1310   return save;
1311 } // --------------------------------------------------------------------------------
1312 std::istream & StdMeshers_ViscousLayers::LoadFrom(std::istream & load)
1313 {
1314   int nbFaces, faceID, shapeToTreat, method;
1315   load >> _nbLayers >> _thickness >> _stretchFactor >> nbFaces;
1316   while ( (int) _shapeIds.size() < nbFaces && load >> faceID )
1317     _shapeIds.push_back( faceID );
1318   if ( load >> shapeToTreat ) {
1319     _isToIgnoreShapes = !shapeToTreat;
1320     if ( load >> method )
1321       _method = (ExtrusionMethod) method;
1322   }
1323   else {
1324     _isToIgnoreShapes = true; // old behavior
1325   }
1326   return load;
1327 } // --------------------------------------------------------------------------------
1328 bool StdMeshers_ViscousLayers::SetParametersByMesh(const SMESH_Mesh*   theMesh,
1329                                                    const TopoDS_Shape& theShape)
1330 {
1331   // TODO
1332   return false;
1333 } // --------------------------------------------------------------------------------
1334 SMESH_ComputeErrorPtr
1335 StdMeshers_ViscousLayers::CheckHypothesis(SMESH_Mesh&                          theMesh,
1336                                           const TopoDS_Shape&                  theShape,
1337                                           SMESH_Hypothesis::Hypothesis_Status& theStatus)
1338 {
1339   VISCOUS_3D::_ViscousBuilder builder;
1340   SMESH_ComputeErrorPtr err = builder.CheckHypotheses( theMesh, theShape );
1341   if ( err && !err->IsOK() )
1342     theStatus = SMESH_Hypothesis::HYP_INCOMPAT_HYPS;
1343   else
1344     theStatus = SMESH_Hypothesis::HYP_OK;
1345
1346   return err;
1347 }
1348 // --------------------------------------------------------------------------------
1349 bool StdMeshers_ViscousLayers::IsShapeWithLayers(int shapeIndex) const
1350 {
1351   bool isIn =
1352     ( std::find( _shapeIds.begin(), _shapeIds.end(), shapeIndex ) != _shapeIds.end() );
1353   return IsToIgnoreShapes() ? !isIn : isIn;
1354 }
1355 // END StdMeshers_ViscousLayers hypothesis
1356 //================================================================================
1357
1358 namespace VISCOUS_3D
1359 {
1360   gp_XYZ getEdgeDir( const TopoDS_Edge& E, const TopoDS_Vertex& fromV )
1361   {
1362     gp_Vec dir;
1363     double f,l;
1364     Handle(Geom_Curve) c = BRep_Tool::Curve( E, f, l );
1365     if ( c.IsNull() ) return gp_XYZ( Precision::Infinite(), 1e100, 1e100 );
1366     gp_Pnt p = BRep_Tool::Pnt( fromV );
1367     double distF = p.SquareDistance( c->Value( f ));
1368     double distL = p.SquareDistance( c->Value( l ));
1369     c->D1(( distF < distL ? f : l), p, dir );
1370     if ( distL < distF ) dir.Reverse();
1371     return dir.XYZ();
1372   }
1373   //--------------------------------------------------------------------------------
1374   gp_XYZ getEdgeDir( const TopoDS_Edge& E, const SMDS_MeshNode* atNode,
1375                      SMESH_MesherHelper& helper)
1376   {
1377     gp_Vec dir;
1378     double f,l; gp_Pnt p;
1379     Handle(Geom_Curve) c = BRep_Tool::Curve( E, f, l );
1380     if ( c.IsNull() ) return gp_XYZ( Precision::Infinite(), 1e100, 1e100 );
1381     double u = helper.GetNodeU( E, atNode );
1382     c->D1( u, p, dir );
1383     return dir.XYZ();
1384   }
1385   //--------------------------------------------------------------------------------
1386   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Vertex& fromV,
1387                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper, bool& ok,
1388                      double* cosin=0);
1389   //--------------------------------------------------------------------------------
1390   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Edge& fromE,
1391                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper, bool& ok)
1392   {
1393     double f,l;
1394     Handle(Geom_Curve) c = BRep_Tool::Curve( fromE, f, l );
1395     if ( c.IsNull() )
1396     {
1397       TopoDS_Vertex v = helper.IthVertex( 0, fromE );
1398       return getFaceDir( F, v, node, helper, ok );
1399     }
1400     gp_XY uv = helper.GetNodeUV( F, node, 0, &ok );
1401     Handle(Geom_Surface) surface = BRep_Tool::Surface( F );
1402     gp_Pnt p; gp_Vec du, dv, norm;
1403     surface->D1( uv.X(),uv.Y(), p, du,dv );
1404     norm = du ^ dv;
1405
1406     double u = helper.GetNodeU( fromE, node, 0, &ok );
1407     c->D1( u, p, du );
1408     TopAbs_Orientation o = helper.GetSubShapeOri( F.Oriented(TopAbs_FORWARD), fromE);
1409     if ( o == TopAbs_REVERSED )
1410       du.Reverse();
1411
1412     gp_Vec dir = norm ^ du;
1413
1414     if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX &&
1415          helper.IsClosedEdge( fromE ))
1416     {
1417       if ( fabs(u-f) < fabs(u-l)) c->D1( l, p, dv );
1418       else                        c->D1( f, p, dv );
1419       if ( o == TopAbs_REVERSED )
1420         dv.Reverse();
1421       gp_Vec dir2 = norm ^ dv;
1422       dir = dir.Normalized() + dir2.Normalized();
1423     }
1424     return dir.XYZ();
1425   }
1426   //--------------------------------------------------------------------------------
1427   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Vertex& fromV,
1428                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper,
1429                      bool& ok, double* cosin)
1430   {
1431     TopoDS_Face faceFrw = F;
1432     faceFrw.Orientation( TopAbs_FORWARD );
1433     //double f,l; TopLoc_Location loc;
1434     TopoDS_Edge edges[2]; // sharing a vertex
1435     size_t nbEdges = 0;
1436     {
1437       TopoDS_Vertex VV[2];
1438       TopExp_Explorer exp( faceFrw, TopAbs_EDGE );
1439       for ( ; exp.More() && nbEdges < 2; exp.Next() )
1440       {
1441         const TopoDS_Edge& e = TopoDS::Edge( exp.Current() );
1442         if ( SMESH_Algo::isDegenerated( e )) continue;
1443         TopExp::Vertices( e, VV[0], VV[1], /*CumOri=*/true );
1444         if ( VV[1].IsSame( fromV )) {
1445           nbEdges += edges[ 0 ].IsNull();
1446           edges[ 0 ] = e;
1447         }
1448         else if ( VV[0].IsSame( fromV )) {
1449           nbEdges += edges[ 1 ].IsNull();
1450           edges[ 1 ] = e;
1451         }
1452       }
1453     }
1454     gp_XYZ dir(0,0,0), edgeDir[2];
1455     if ( nbEdges == 2 )
1456     {
1457       // get dirs of edges going fromV
1458       ok = true;
1459       for ( size_t i = 0; i < nbEdges && ok; ++i )
1460       {
1461         edgeDir[i] = getEdgeDir( edges[i], fromV );
1462         double size2 = edgeDir[i].SquareModulus();
1463         if (( ok = size2 > numeric_limits<double>::min() ))
1464           edgeDir[i] /= sqrt( size2 );
1465       }
1466       if ( !ok ) return dir;
1467
1468       // get angle between the 2 edges
1469       gp_Vec faceNormal;
1470       double angle = helper.GetAngle( edges[0], edges[1], faceFrw, fromV, &faceNormal );
1471       if ( Abs( angle ) < 5 * M_PI/180 )
1472       {
1473         dir = ( faceNormal.XYZ() ^ edgeDir[0].Reversed()) + ( faceNormal.XYZ() ^ edgeDir[1] );
1474       }
1475       else
1476       {
1477         dir = edgeDir[0] + edgeDir[1];
1478         if ( angle < 0 )
1479           dir.Reverse();
1480       }
1481       if ( cosin ) {
1482         double angle = gp_Vec( edgeDir[0] ).Angle( dir );
1483         *cosin = Cos( angle );
1484       }
1485     }
1486     else if ( nbEdges == 1 )
1487     {
1488       dir = getFaceDir( faceFrw, edges[ edges[0].IsNull() ], node, helper, ok );
1489       if ( cosin ) *cosin = 1.;
1490     }
1491     else
1492     {
1493       ok = false;
1494     }
1495
1496     return dir;
1497   }
1498
1499   //================================================================================
1500   /*!
1501    * \brief Finds concave VERTEXes of a FACE
1502    */
1503   //================================================================================
1504
1505   bool getConcaveVertices( const TopoDS_Face&  F,
1506                            SMESH_MesherHelper& helper,
1507                            set< TGeomID >*     vertices = 0)
1508   {
1509     // check angles at VERTEXes
1510     TError error;
1511     TSideVector wires = StdMeshers_FaceSide::GetFaceWires( F, *helper.GetMesh(), 0, error );
1512     for ( size_t iW = 0; iW < wires.size(); ++iW )
1513     {
1514       const int nbEdges = wires[iW]->NbEdges();
1515       if ( nbEdges < 2 && SMESH_Algo::isDegenerated( wires[iW]->Edge(0)))
1516         continue;
1517       for ( int iE1 = 0; iE1 < nbEdges; ++iE1 )
1518       {
1519         if ( SMESH_Algo::isDegenerated( wires[iW]->Edge( iE1 ))) continue;
1520         int iE2 = ( iE1 + 1 ) % nbEdges;
1521         while ( SMESH_Algo::isDegenerated( wires[iW]->Edge( iE2 )))
1522           iE2 = ( iE2 + 1 ) % nbEdges;
1523         TopoDS_Vertex V = wires[iW]->FirstVertex( iE2 );
1524         double angle = helper.GetAngle( wires[iW]->Edge( iE1 ),
1525                                         wires[iW]->Edge( iE2 ), F, V );
1526         if ( angle < -5. * M_PI / 180. )
1527         {
1528           if ( !vertices )
1529             return true;
1530           vertices->insert( helper.GetMeshDS()->ShapeToIndex( V ));
1531         }
1532       }
1533     }
1534     return vertices ? !vertices->empty() : false;
1535   }
1536
1537   //================================================================================
1538   /*!
1539    * \brief Returns true if a FACE is bound by a concave EDGE
1540    */
1541   //================================================================================
1542
1543   bool isConcave( const TopoDS_Face&  F,
1544                   SMESH_MesherHelper& helper,
1545                   set< TGeomID >*     vertices = 0 )
1546   {
1547     bool isConcv = false;
1548     // if ( helper.Count( F, TopAbs_WIRE, /*useMap=*/false) > 1 )
1549     //   return true;
1550     gp_Vec2d drv1, drv2;
1551     gp_Pnt2d p;
1552     TopExp_Explorer eExp( F.Oriented( TopAbs_FORWARD ), TopAbs_EDGE );
1553     for ( ; eExp.More(); eExp.Next() )
1554     {
1555       const TopoDS_Edge& E = TopoDS::Edge( eExp.Current() );
1556       if ( SMESH_Algo::isDegenerated( E )) continue;
1557       // check if 2D curve is concave
1558       BRepAdaptor_Curve2d curve( E, F );
1559       const int nbIntervals = curve.NbIntervals( GeomAbs_C2 );
1560       TColStd_Array1OfReal intervals(1, nbIntervals + 1 );
1561       curve.Intervals( intervals, GeomAbs_C2 );
1562       bool isConvex = true;
1563       for ( int i = 1; i <= nbIntervals && isConvex; ++i )
1564       {
1565         double u1 = intervals( i );
1566         double u2 = intervals( i+1 );
1567         curve.D2( 0.5*( u1+u2 ), p, drv1, drv2 );
1568         double cross = drv1 ^ drv2;
1569         if ( E.Orientation() == TopAbs_REVERSED )
1570           cross = -cross;
1571         isConvex = ( cross > -1e-9 ); // 0.1 );
1572       }
1573       if ( !isConvex )
1574       {
1575         //cout << "Concave FACE " << helper.GetMeshDS()->ShapeToIndex( F ) << endl;
1576         isConcv = true;
1577         if ( vertices )
1578           break;
1579         else
1580           return true;
1581       }
1582     }
1583
1584     // check angles at VERTEXes
1585     if ( getConcaveVertices( F, helper, vertices ))
1586       isConcv = true;
1587
1588     return isConcv;
1589   }
1590
1591   //================================================================================
1592   /*!
1593    * \brief Computes mimimal distance of face in-FACE nodes from an EDGE
1594    *  \param [in] face - the mesh face to treat
1595    *  \param [in] nodeOnEdge - a node on the EDGE
1596    *  \param [out] faceSize - the computed distance
1597    *  \return bool - true if faceSize computed
1598    */
1599   //================================================================================
1600
1601   bool getDistFromEdge( const SMDS_MeshElement* face,
1602                         const SMDS_MeshNode*    nodeOnEdge,
1603                         double &                faceSize )
1604   {
1605     faceSize = Precision::Infinite();
1606     bool done = false;
1607
1608     int nbN  = face->NbCornerNodes();
1609     int iOnE = face->GetNodeIndex( nodeOnEdge );
1610     int iNext[2] = { SMESH_MesherHelper::WrapIndex( iOnE+1, nbN ),
1611                      SMESH_MesherHelper::WrapIndex( iOnE-1, nbN ) };
1612     const SMDS_MeshNode* nNext[2] = { face->GetNode( iNext[0] ),
1613                                       face->GetNode( iNext[1] ) };
1614     gp_XYZ segVec, segEnd = SMESH_TNodeXYZ( nodeOnEdge ); // segment on EDGE
1615     double segLen = -1.;
1616     // look for two neighbor not in-FACE nodes of face
1617     for ( int i = 0; i < 2; ++i )
1618     {
1619       if (( nNext[i]->GetPosition()->GetDim() != 2 ) &&
1620           ( nodeOnEdge->GetPosition()->GetDim() == 0 || nNext[i]->GetID() < nodeOnEdge->GetID() ))
1621       {
1622         // look for an in-FACE node
1623         for ( int iN = 0; iN < nbN; ++iN )
1624         {
1625           if ( iN == iOnE || iN == iNext[i] )
1626             continue;
1627           SMESH_TNodeXYZ pInFace = face->GetNode( iN );
1628           gp_XYZ v = pInFace - segEnd;
1629           if ( segLen < 0 )
1630           {
1631             segVec = SMESH_TNodeXYZ( nNext[i] ) - segEnd;
1632             segLen = segVec.Modulus();
1633           }
1634           double distToSeg = v.Crossed( segVec ).Modulus() / segLen;
1635           faceSize = Min( faceSize, distToSeg );
1636           done = true;
1637         }
1638         segLen = -1;
1639       }
1640     }
1641     return done;
1642   }
1643   //================================================================================
1644   /*!
1645    * \brief Return direction of axis or revolution of a surface
1646    */
1647   //================================================================================
1648
1649   bool getRovolutionAxis( const Adaptor3d_Surface& surface,
1650                           gp_Dir &                 axis )
1651   {
1652     switch ( surface.GetType() ) {
1653     case GeomAbs_Cone:
1654     {
1655       gp_Cone cone = surface.Cone();
1656       axis = cone.Axis().Direction();
1657       break;
1658     }
1659     case GeomAbs_Sphere:
1660     {
1661       gp_Sphere sphere = surface.Sphere();
1662       axis = sphere.Position().Direction();
1663       break;
1664     }
1665     case GeomAbs_SurfaceOfRevolution:
1666     {
1667       axis = surface.AxeOfRevolution().Direction();
1668       break;
1669     }
1670     //case GeomAbs_SurfaceOfExtrusion:
1671     case GeomAbs_OffsetSurface:
1672     {
1673       Handle(Adaptor3d_HSurface) base = surface.BasisSurface();
1674       return getRovolutionAxis( base->Surface(), axis );
1675     }
1676     default: return false;
1677     }
1678     return true;
1679   }
1680
1681   //--------------------------------------------------------------------------------
1682   // DEBUG. Dump intermediate node positions into a python script
1683   // HOWTO use: run python commands written in a console to see
1684   //  construction steps of viscous layers
1685 #ifdef __myDEBUG
1686   ostream* py;
1687   int      theNbPyFunc;
1688   struct PyDump
1689   {
1690     PyDump(SMESH_Mesh& m) {
1691       int tag = 3 + m.GetId();
1692       const char* fname = "/tmp/viscous.py";
1693       cout << "execfile('"<<fname<<"')"<<endl;
1694       py = _pyStream = new ofstream(fname);
1695       *py << "import SMESH" << endl
1696           << "from salome.smesh import smeshBuilder" << endl
1697           << "smesh  = smeshBuilder.New()" << endl
1698           << "meshSO = salome.myStudy.FindObjectID('0:1:2:" << tag <<"')" << endl
1699           << "mesh   = smesh.Mesh( meshSO.GetObject() )"<<endl;
1700       theNbPyFunc = 0;
1701     }
1702     void Finish() {
1703       if (py) {
1704         *py << "mesh.GroupOnFilter(SMESH.VOLUME,'Viscous Prisms',"
1705           "smesh.GetFilter(SMESH.VOLUME,SMESH.FT_ElemGeomType,'=',SMESH.Geom_PENTA))"<<endl;
1706         *py << "mesh.GroupOnFilter(SMESH.VOLUME,'Neg Volumes',"
1707           "smesh.GetFilter(SMESH.VOLUME,SMESH.FT_Volume3D,'<',0))"<<endl;
1708       }
1709       delete py; py=0;
1710     }
1711     ~PyDump() { Finish(); cout << "NB FUNCTIONS: " << theNbPyFunc << endl; }
1712     struct MyStream : public ostream
1713     {
1714       template <class T> ostream & operator<<( const T &anything ) { return *this ; }
1715     };
1716     void Pause() { py = &_mystream; }
1717     void Resume() { py = _pyStream; }
1718     MyStream _mystream;
1719     ostream* _pyStream;
1720   };
1721 #define dumpFunction(f) { _dumpFunction(f, __LINE__);}
1722 #define dumpMove(n)     { _dumpMove(n, __LINE__);}
1723 #define dumpMoveComm(n,txt) { _dumpMove(n, __LINE__, txt);}
1724 #define dumpCmd(txt)    { _dumpCmd(txt, __LINE__);}
1725   void _dumpFunction(const string& fun, int ln)
1726   { if (py) *py<< "def "<<fun<<"(): # "<< ln <<endl; cout<<fun<<"()"<<endl; ++theNbPyFunc; }
1727   void _dumpMove(const SMDS_MeshNode* n, int ln, const char* txt="")
1728   { if (py) *py<< "  mesh.MoveNode( "<<n->GetID()<< ", "<< n->X()
1729                << ", "<<n->Y()<<", "<< n->Z()<< ")\t\t # "<< ln <<" "<< txt << endl; }
1730   void _dumpCmd(const string& txt, int ln)
1731   { if (py) *py<< "  "<<txt<<" # "<< ln <<endl; }
1732   void dumpFunctionEnd()
1733   { if (py) *py<< "  return"<< endl; }
1734   void dumpChangeNodes( const SMDS_MeshElement* f )
1735   { if (py) { *py<< "  mesh.ChangeElemNodes( " << f->GetID()<<", [";
1736       for ( int i=1; i < f->NbNodes(); ++i ) *py << f->GetNode(i-1)->GetID()<<", ";
1737       *py << f->GetNode( f->NbNodes()-1 )->GetID() << " ])"<< endl; }}
1738 #define debugMsg( txt ) { cout << "# "<< txt << " (line: " << __LINE__ << ")" << endl; }
1739
1740 #else
1741
1742   struct PyDump { PyDump(SMESH_Mesh&) {} void Finish() {} void Pause() {} void Resume() {} };
1743 #define dumpFunction(f) f
1744 #define dumpMove(n)
1745 #define dumpMoveComm(n,txt)
1746 #define dumpCmd(txt)
1747 #define dumpFunctionEnd()
1748 #define dumpChangeNodes(f) { if(f) {} } // prevent "unused variable 'f'" warning
1749 #define debugMsg( txt ) {}
1750
1751 #endif
1752 }
1753
1754 using namespace VISCOUS_3D;
1755
1756 //================================================================================
1757 /*!
1758  * \brief Constructor of _ViscousBuilder
1759  */
1760 //================================================================================
1761
1762 _ViscousBuilder::_ViscousBuilder()
1763 {
1764   _error = SMESH_ComputeError::New(COMPERR_OK);
1765   _tmpFaceID = 0;
1766 }
1767
1768 //================================================================================
1769 /*!
1770  * \brief Stores error description and returns false
1771  */
1772 //================================================================================
1773
1774 bool _ViscousBuilder::error(const string& text, int solidId )
1775 {
1776   const string prefix = string("Viscous layers builder: ");
1777   _error->myName    = COMPERR_ALGO_FAILED;
1778   _error->myComment = prefix + text;
1779   if ( _mesh )
1780   {
1781     SMESH_subMesh* sm = _mesh->GetSubMeshContaining( solidId );
1782     if ( !sm && !_sdVec.empty() )
1783       sm = _mesh->GetSubMeshContaining( solidId = _sdVec[0]._index );
1784     if ( sm && sm->GetSubShape().ShapeType() == TopAbs_SOLID )
1785     {
1786       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1787       if ( smError && smError->myAlgo )
1788         _error->myAlgo = smError->myAlgo;
1789       smError = _error;
1790       sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
1791     }
1792     // set KO to all solids
1793     for ( size_t i = 0; i < _sdVec.size(); ++i )
1794     {
1795       if ( _sdVec[i]._index == solidId )
1796         continue;
1797       sm = _mesh->GetSubMesh( _sdVec[i]._solid );
1798       if ( !sm->IsEmpty() )
1799         continue;
1800       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1801       if ( !smError || smError->IsOK() )
1802       {
1803         smError = SMESH_ComputeError::New( COMPERR_ALGO_FAILED, prefix + "failed");
1804         sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
1805       }
1806     }
1807   }
1808   makeGroupOfLE(); // debug
1809
1810   return false;
1811 }
1812
1813 //================================================================================
1814 /*!
1815  * \brief At study restoration, restore event listeners used to clear an inferior
1816  *  dim sub-mesh modified by viscous layers
1817  */
1818 //================================================================================
1819
1820 void _ViscousBuilder::RestoreListeners()
1821 {
1822   // TODO
1823 }
1824
1825 //================================================================================
1826 /*!
1827  * \brief computes SMESH_ProxyMesh::SubMesh::_n2n
1828  */
1829 //================================================================================
1830
1831 bool _ViscousBuilder::MakeN2NMap( _MeshOfSolid* pm )
1832 {
1833   SMESH_subMesh* solidSM = pm->mySubMeshes.front();
1834   TopExp_Explorer fExp( solidSM->GetSubShape(), TopAbs_FACE );
1835   for ( ; fExp.More(); fExp.Next() )
1836   {
1837     SMESHDS_SubMesh* srcSmDS = pm->GetMeshDS()->MeshElements( fExp.Current() );
1838     const SMESH_ProxyMesh::SubMesh* prxSmDS = pm->GetProxySubMesh( fExp.Current() );
1839
1840     if ( !srcSmDS || !prxSmDS || !srcSmDS->NbElements() || !prxSmDS->NbElements() )
1841       continue;
1842     if ( srcSmDS->GetElements()->next() == prxSmDS->GetElements()->next())
1843       continue;
1844
1845     if ( srcSmDS->NbElements() != prxSmDS->NbElements() )
1846       return error( "Different nb elements in a source and a proxy sub-mesh", solidSM->GetId());
1847
1848     SMDS_ElemIteratorPtr srcIt = srcSmDS->GetElements();
1849     SMDS_ElemIteratorPtr prxIt = prxSmDS->GetElements();
1850     while( prxIt->more() )
1851     {
1852       const SMDS_MeshElement* fSrc = srcIt->next();
1853       const SMDS_MeshElement* fPrx = prxIt->next();
1854       if ( fSrc->NbNodes() != fPrx->NbNodes())
1855         return error( "Different elements in a source and a proxy sub-mesh", solidSM->GetId());
1856       for ( int i = 0 ; i < fPrx->NbNodes(); ++i )
1857         pm->setNode2Node( fSrc->GetNode(i), fPrx->GetNode(i), prxSmDS );
1858     }
1859   }
1860   pm->_n2nMapComputed = true;
1861   return true;
1862 }
1863
1864 //================================================================================
1865 /*!
1866  * \brief Does its job
1867  */
1868 //================================================================================
1869
1870 SMESH_ComputeErrorPtr _ViscousBuilder::Compute(SMESH_Mesh&         theMesh,
1871                                                const TopoDS_Shape& theShape)
1872 {
1873   _mesh = & theMesh;
1874
1875   // check if proxy mesh already computed
1876   TopExp_Explorer exp( theShape, TopAbs_SOLID );
1877   if ( !exp.More() )
1878     return error("No SOLID's in theShape"), _error;
1879
1880   if ( _ViscousListener::GetSolidMesh( _mesh, exp.Current(), /*toCreate=*/false))
1881     return SMESH_ComputeErrorPtr(); // everything already computed
1882
1883   PyDump debugDump( theMesh );
1884   _pyDump = &debugDump;
1885
1886   // TODO: ignore already computed SOLIDs 
1887   if ( !findSolidsWithLayers())
1888     return _error;
1889
1890   if ( !findFacesWithLayers() )
1891     return _error;
1892
1893   for ( size_t i = 0; i < _sdVec.size(); ++i )
1894   {
1895     size_t iSD = 0;
1896     for ( iSD = 0; iSD < _sdVec.size(); ++iSD ) // find next SOLID to compute
1897       if ( _sdVec[iSD]._before.IsEmpty() &&
1898            !_sdVec[iSD]._solid.IsNull() &&
1899            _sdVec[iSD]._n2eMap.empty() )
1900         break;
1901
1902     if ( ! makeLayer(_sdVec[iSD]) )   // create _LayerEdge's
1903       return _error;
1904
1905     if ( _sdVec[iSD]._n2eMap.size() == 0 ) // no layers in a SOLID
1906     {
1907       _sdVec[iSD]._solid.Nullify();
1908       continue;
1909     }
1910
1911     if ( ! inflate(_sdVec[iSD]) )     // increase length of _LayerEdge's
1912       return _error;
1913
1914     if ( ! refine(_sdVec[iSD]) )      // create nodes and prisms
1915       return _error;
1916
1917     if ( ! shrink(_sdVec[iSD]) )      // shrink 2D mesh on FACEs w/o layer
1918       return _error;
1919
1920     addBoundaryElements(_sdVec[iSD]); // create quadrangles on prism bare sides
1921
1922     const TopoDS_Shape& solid = _sdVec[iSD]._solid;
1923     for ( iSD = 0; iSD < _sdVec.size(); ++iSD )
1924       _sdVec[iSD]._before.Remove( solid );
1925   }
1926
1927   makeGroupOfLE(); // debug
1928   debugDump.Finish();
1929
1930   return _error;
1931 }
1932
1933 //================================================================================
1934 /*!
1935  * \brief Check validity of hypotheses
1936  */
1937 //================================================================================
1938
1939 SMESH_ComputeErrorPtr _ViscousBuilder::CheckHypotheses( SMESH_Mesh&         mesh,
1940                                                         const TopoDS_Shape& shape )
1941 {
1942   _mesh = & mesh;
1943
1944   if ( _ViscousListener::GetSolidMesh( _mesh, shape, /*toCreate=*/false))
1945     return SMESH_ComputeErrorPtr(); // everything already computed
1946
1947
1948   findSolidsWithLayers();
1949   bool ok = findFacesWithLayers( true );
1950
1951   // remove _MeshOfSolid's of _SolidData's
1952   for ( size_t i = 0; i < _sdVec.size(); ++i )
1953     _ViscousListener::RemoveSolidMesh( _mesh, _sdVec[i]._solid );
1954
1955   if ( !ok )
1956     return _error;
1957
1958   return SMESH_ComputeErrorPtr();
1959 }
1960
1961 //================================================================================
1962 /*!
1963  * \brief Finds SOLIDs to compute using viscous layers. Fills _sdVec
1964  */
1965 //================================================================================
1966
1967 bool _ViscousBuilder::findSolidsWithLayers()
1968 {
1969   // get all solids
1970   TopTools_IndexedMapOfShape allSolids;
1971   TopExp::MapShapes( _mesh->GetShapeToMesh(), TopAbs_SOLID, allSolids );
1972   _sdVec.reserve( allSolids.Extent());
1973
1974   SMESH_HypoFilter filter;
1975   for ( int i = 1; i <= allSolids.Extent(); ++i )
1976   {
1977     // find StdMeshers_ViscousLayers hyp assigned to the i-th solid
1978     SMESH_subMesh* sm = _mesh->GetSubMesh( allSolids(i) );
1979     if ( sm->GetSubMeshDS() && sm->GetSubMeshDS()->NbElements() > 0 )
1980       continue; // solid is already meshed
1981     SMESH_Algo* algo = sm->GetAlgo();
1982     if ( !algo ) continue;
1983     // TODO: check if algo is hidden
1984     const list <const SMESHDS_Hypothesis *> & allHyps =
1985       algo->GetUsedHypothesis(*_mesh, allSolids(i), /*ignoreAuxiliary=*/false);
1986     _SolidData* soData = 0;
1987     list< const SMESHDS_Hypothesis *>::const_iterator hyp = allHyps.begin();
1988     const StdMeshers_ViscousLayers* viscHyp = 0;
1989     for ( ; hyp != allHyps.end(); ++hyp )
1990       if (( viscHyp = dynamic_cast<const StdMeshers_ViscousLayers*>( *hyp )))
1991       {
1992         TopoDS_Shape hypShape;
1993         filter.Init( filter.Is( viscHyp ));
1994         _mesh->GetHypothesis( allSolids(i), filter, true, &hypShape );
1995
1996         if ( !soData )
1997         {
1998           _MeshOfSolid* proxyMesh = _ViscousListener::GetSolidMesh( _mesh,
1999                                                                     allSolids(i),
2000                                                                     /*toCreate=*/true);
2001           _sdVec.push_back( _SolidData( allSolids(i), proxyMesh ));
2002           soData = & _sdVec.back();
2003           soData->_index = getMeshDS()->ShapeToIndex( allSolids(i));
2004           soData->_helper = new SMESH_MesherHelper( *_mesh );
2005           soData->_helper->SetSubShape( allSolids(i) );
2006           _solids.Add( allSolids(i) );
2007         }
2008         soData->_hyps.push_back( viscHyp );
2009         soData->_hypShapes.push_back( hypShape );
2010       }
2011   }
2012   if ( _sdVec.empty() )
2013     return error
2014       ( SMESH_Comment(StdMeshers_ViscousLayers::GetHypType()) << " hypothesis not found",0);
2015
2016   return true;
2017 }
2018
2019 //================================================================================
2020 /*!
2021  * \brief Set a _SolidData to be computed before another
2022  */
2023 //================================================================================
2024
2025 bool _ViscousBuilder::setBefore( _SolidData& solidBefore, _SolidData& solidAfter )
2026 {
2027   // check possibility to set this order; get all solids before solidBefore
2028   TopTools_IndexedMapOfShape allSolidsBefore;
2029   allSolidsBefore.Add( solidBefore._solid );
2030   for ( int i = 1; i <= allSolidsBefore.Extent(); ++i )
2031   {
2032     int iSD = _solids.FindIndex( allSolidsBefore(i) );
2033     if ( iSD )
2034     {
2035       TopTools_MapIteratorOfMapOfShape soIt( _sdVec[ iSD-1 ]._before );
2036       for ( ; soIt.More(); soIt.Next() )
2037         allSolidsBefore.Add( soIt.Value() );
2038     }
2039   }
2040   if ( allSolidsBefore.Contains( solidAfter._solid ))
2041     return false;
2042
2043   for ( int i = 1; i <= allSolidsBefore.Extent(); ++i )
2044     solidAfter._before.Add( allSolidsBefore(i) );
2045
2046   return true;
2047 }
2048
2049 //================================================================================
2050 /*!
2051  * \brief
2052  */
2053 //================================================================================
2054
2055 bool _ViscousBuilder::findFacesWithLayers(const bool onlyWith)
2056 {
2057   SMESH_MesherHelper helper( *_mesh );
2058   TopExp_Explorer exp;
2059
2060   // collect all faces-to-ignore defined by hyp
2061   for ( size_t i = 0; i < _sdVec.size(); ++i )
2062   {
2063     // get faces-to-ignore defined by each hyp
2064     typedef const StdMeshers_ViscousLayers* THyp;
2065     typedef std::pair< set<TGeomID>, THyp > TFacesOfHyp;
2066     list< TFacesOfHyp > ignoreFacesOfHyps;
2067     list< THyp >::iterator              hyp = _sdVec[i]._hyps.begin();
2068     list< TopoDS_Shape >::iterator hypShape = _sdVec[i]._hypShapes.begin();
2069     for ( ; hyp != _sdVec[i]._hyps.end(); ++hyp, ++hypShape )
2070     {
2071       ignoreFacesOfHyps.push_back( TFacesOfHyp( set<TGeomID>(), *hyp ));
2072       getIgnoreFaces( _sdVec[i]._solid, *hyp, *hypShape, ignoreFacesOfHyps.back().first );
2073     }
2074
2075     // fill _SolidData::_face2hyp and check compatibility of hypotheses
2076     const int nbHyps = _sdVec[i]._hyps.size();
2077     if ( nbHyps > 1 )
2078     {
2079       // check if two hypotheses define different parameters for the same FACE
2080       list< TFacesOfHyp >::iterator igFacesOfHyp;
2081       for ( exp.Init( _sdVec[i]._solid, TopAbs_FACE ); exp.More(); exp.Next() )
2082       {
2083         const TGeomID faceID = getMeshDS()->ShapeToIndex( exp.Current() );
2084         THyp hyp = 0;
2085         igFacesOfHyp = ignoreFacesOfHyps.begin();
2086         for ( ; igFacesOfHyp != ignoreFacesOfHyps.end(); ++igFacesOfHyp )
2087           if ( ! igFacesOfHyp->first.count( faceID ))
2088           {
2089             if ( hyp )
2090               return error(SMESH_Comment("Several hypotheses define "
2091                                          "Viscous Layers on the face #") << faceID );
2092             hyp = igFacesOfHyp->second;
2093           }
2094         if ( hyp )
2095           _sdVec[i]._face2hyp.insert( make_pair( faceID, hyp ));
2096         else
2097           _sdVec[i]._ignoreFaceIds.insert( faceID );
2098       }
2099
2100       // check if two hypotheses define different number of viscous layers for
2101       // adjacent faces of a solid
2102       set< int > nbLayersSet;
2103       igFacesOfHyp = ignoreFacesOfHyps.begin();
2104       for ( ; igFacesOfHyp != ignoreFacesOfHyps.end(); ++igFacesOfHyp )
2105       {
2106         nbLayersSet.insert( igFacesOfHyp->second->GetNumberLayers() );
2107       }
2108       if ( nbLayersSet.size() > 1 )
2109       {
2110         for ( exp.Init( _sdVec[i]._solid, TopAbs_EDGE ); exp.More(); exp.Next() )
2111         {
2112           PShapeIteratorPtr fIt = helper.GetAncestors( exp.Current(), *_mesh, TopAbs_FACE );
2113           THyp hyp1 = 0, hyp2 = 0;
2114           while( const TopoDS_Shape* face = fIt->next() )
2115           {
2116             const TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
2117             map< TGeomID, THyp >::iterator f2h = _sdVec[i]._face2hyp.find( faceID );
2118             if ( f2h != _sdVec[i]._face2hyp.end() )
2119             {
2120               ( hyp1 ? hyp2 : hyp1 ) = f2h->second;
2121             }
2122           }
2123           if ( hyp1 && hyp2 &&
2124                hyp1->GetNumberLayers() != hyp2->GetNumberLayers() )
2125           {
2126             return error("Two hypotheses define different number of "
2127                          "viscous layers on adjacent faces");
2128           }
2129         }
2130       }
2131     } // if ( nbHyps > 1 )
2132     else
2133     {
2134       _sdVec[i]._ignoreFaceIds.swap( ignoreFacesOfHyps.back().first );
2135     }
2136   } // loop on _sdVec
2137
2138   if ( onlyWith ) // is called to check hypotheses compatibility only
2139     return true;
2140
2141   // fill _SolidData::_reversedFaceIds
2142   for ( size_t i = 0; i < _sdVec.size(); ++i )
2143   {
2144     exp.Init( _sdVec[i]._solid.Oriented( TopAbs_FORWARD ), TopAbs_FACE );
2145     for ( ; exp.More(); exp.Next() )
2146     {
2147       const TopoDS_Face& face = TopoDS::Face( exp.Current() );
2148       const TGeomID faceID = getMeshDS()->ShapeToIndex( face );
2149       if ( //!sdVec[i]._ignoreFaceIds.count( faceID ) &&
2150           helper.NbAncestors( face, *_mesh, TopAbs_SOLID ) > 1 &&
2151           helper.IsReversedSubMesh( face ))
2152       {
2153         _sdVec[i]._reversedFaceIds.insert( faceID );
2154       }
2155     }
2156   }
2157
2158   // Find FACEs to shrink mesh on (solution 2 in issue 0020832): fill in _shrinkShape2Shape
2159   TopTools_IndexedMapOfShape shapes;
2160   std::string structAlgoName = "Hexa_3D";
2161   for ( size_t i = 0; i < _sdVec.size(); ++i )
2162   {
2163     shapes.Clear();
2164     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_EDGE, shapes);
2165     for ( int iE = 1; iE <= shapes.Extent(); ++iE )
2166     {
2167       const TopoDS_Shape& edge = shapes(iE);
2168       // find 2 FACEs sharing an EDGE
2169       TopoDS_Shape FF[2];
2170       PShapeIteratorPtr fIt = helper.GetAncestors(edge, *_mesh, TopAbs_FACE, &_sdVec[i]._solid);
2171       while ( fIt->more())
2172       {
2173         const TopoDS_Shape* f = fIt->next();
2174         FF[ int( !FF[0].IsNull()) ] = *f;
2175       }
2176       if( FF[1].IsNull() ) continue; // seam edge can be shared by 1 FACE only
2177
2178       // check presence of layers on them
2179       int ignore[2];
2180       for ( int j = 0; j < 2; ++j )
2181         ignore[j] = _sdVec[i]._ignoreFaceIds.count( getMeshDS()->ShapeToIndex( FF[j] ));
2182       if ( ignore[0] == ignore[1] )
2183         continue; // nothing interesting
2184       TopoDS_Shape fWOL = FF[ ignore[0] ? 0 : 1 ];
2185
2186       // add EDGE to maps
2187       if ( !fWOL.IsNull())
2188       {
2189         TGeomID edgeInd = getMeshDS()->ShapeToIndex( edge );
2190         _sdVec[i]._shrinkShape2Shape.insert( make_pair( edgeInd, fWOL ));
2191       }
2192     }
2193   }
2194
2195   // Find the SHAPE along which to inflate _LayerEdge based on VERTEX
2196
2197   for ( size_t i = 0; i < _sdVec.size(); ++i )
2198   {
2199     shapes.Clear();
2200     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_VERTEX, shapes);
2201     for ( int iV = 1; iV <= shapes.Extent(); ++iV )
2202     {
2203       const TopoDS_Shape& vertex = shapes(iV);
2204       // find faces WOL sharing the vertex
2205       vector< TopoDS_Shape > facesWOL;
2206       size_t totalNbFaces = 0;
2207       PShapeIteratorPtr fIt = helper.GetAncestors(vertex, *_mesh, TopAbs_FACE, &_sdVec[i]._solid );
2208       while ( fIt->more())
2209       {
2210         const TopoDS_Shape* f = fIt->next();
2211         totalNbFaces++;
2212         const int fID = getMeshDS()->ShapeToIndex( *f );
2213         if ( _sdVec[i]._ignoreFaceIds.count ( fID ) /*&& !_sdVec[i]._noShrinkShapes.count( fID )*/)
2214           facesWOL.push_back( *f );
2215       }
2216       if ( facesWOL.size() == totalNbFaces || facesWOL.empty() )
2217         continue; // no layers at this vertex or no WOL
2218       TGeomID vInd = getMeshDS()->ShapeToIndex( vertex );
2219       switch ( facesWOL.size() )
2220       {
2221       case 1:
2222       {
2223         helper.SetSubShape( facesWOL[0] );
2224         if ( helper.IsRealSeam( vInd )) // inflate along a seam edge?
2225         {
2226           TopoDS_Shape seamEdge;
2227           PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE);
2228           while ( eIt->more() && seamEdge.IsNull() )
2229           {
2230             const TopoDS_Shape* e = eIt->next();
2231             if ( helper.IsRealSeam( *e ) )
2232               seamEdge = *e;
2233           }
2234           if ( !seamEdge.IsNull() )
2235           {
2236             _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, seamEdge ));
2237             break;
2238           }
2239         }
2240         _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, facesWOL[0] ));
2241         break;
2242       }
2243       case 2:
2244       {
2245         // find an edge shared by 2 faces
2246         PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE);
2247         while ( eIt->more())
2248         {
2249           const TopoDS_Shape* e = eIt->next();
2250           if ( helper.IsSubShape( *e, facesWOL[0]) &&
2251                helper.IsSubShape( *e, facesWOL[1]))
2252           {
2253             _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, *e )); break;
2254           }
2255         }
2256         break;
2257       }
2258       default:
2259         return error("Not yet supported case", _sdVec[i]._index);
2260       }
2261     }
2262   }
2263
2264   // Add to _noShrinkShapes sub-shapes of FACE's that can't be shrinked since
2265   // the algo of the SOLID sharing the FACE does not support it or for other reasons
2266   set< string > notSupportAlgos; notSupportAlgos.insert( structAlgoName );
2267   for ( size_t i = 0; i < _sdVec.size(); ++i )
2268   {
2269     map< TGeomID, TopoDS_Shape >::iterator e2f = _sdVec[i]._shrinkShape2Shape.begin();
2270     for ( ; e2f != _sdVec[i]._shrinkShape2Shape.end(); ++e2f )
2271     {
2272       const TopoDS_Shape& fWOL = e2f->second;
2273       const TGeomID     edgeID = e2f->first;
2274       TGeomID           faceID = getMeshDS()->ShapeToIndex( fWOL );
2275       TopoDS_Shape        edge = getMeshDS()->IndexToShape( edgeID );
2276       if ( edge.ShapeType() != TopAbs_EDGE )
2277         continue; // shrink shape is VERTEX
2278
2279       TopoDS_Shape solid;
2280       PShapeIteratorPtr soIt = helper.GetAncestors(fWOL, *_mesh, TopAbs_SOLID);
2281       while ( soIt->more() && solid.IsNull() )
2282       {
2283         const TopoDS_Shape* so = soIt->next();
2284         if ( !so->IsSame( _sdVec[i]._solid ))
2285           solid = *so;
2286       }
2287       if ( solid.IsNull() )
2288         continue;
2289
2290       bool noShrinkE = false;
2291       SMESH_Algo*  algo = _mesh->GetSubMesh( solid )->GetAlgo();
2292       bool isStructured = ( algo && algo->GetName() == structAlgoName );
2293       size_t     iSolid = _solids.FindIndex( solid ) - 1;
2294       if ( iSolid < _sdVec.size() && _sdVec[ iSolid ]._ignoreFaceIds.count( faceID ))
2295       {
2296         // the adjacent SOLID has NO layers on fWOL;
2297         // shrink allowed if
2298         // - there are layers on the EDGE in the adjacent SOLID
2299         // - there are NO layers in the adjacent SOLID && algo is unstructured and computed later
2300         bool hasWLAdj = (_sdVec[iSolid]._shrinkShape2Shape.count( edgeID ));
2301         bool shrinkAllowed = (( hasWLAdj ) ||
2302                               ( !isStructured && setBefore( _sdVec[ i ], _sdVec[ iSolid ] )));
2303         noShrinkE = !shrinkAllowed;
2304       }
2305       else if ( iSolid < _sdVec.size() )
2306       {
2307         // the adjacent SOLID has layers on fWOL;
2308         // check if SOLID's mesh is unstructured and then try to set it
2309         // to be computed after the i-th solid
2310         if ( isStructured || !setBefore( _sdVec[ i ], _sdVec[ iSolid ] ))
2311           noShrinkE = true; // don't shrink fWOL
2312       }
2313       else
2314       {
2315         // the adjacent SOLID has NO layers at all
2316         noShrinkE = isStructured;
2317       }
2318
2319       if ( noShrinkE )
2320       {
2321         _sdVec[i]._noShrinkShapes.insert( edgeID );
2322
2323         // check if there is a collision with to-shrink-from EDGEs in iSolid
2324         // if ( iSolid < _sdVec.size() )
2325         // {
2326         //   shapes.Clear();
2327         //   TopExp::MapShapes( fWOL, TopAbs_EDGE, shapes);
2328         //   for ( int iE = 1; iE <= shapes.Extent(); ++iE )
2329         //   {
2330         //     const TopoDS_Edge& E = TopoDS::Edge( shapes( iE ));
2331         //     const TGeomID    eID = getMeshDS()->ShapeToIndex( E );
2332         //     if ( eID == edgeID ||
2333         //          !_sdVec[iSolid]._shrinkShape2Shape.count( eID ) ||
2334         //          _sdVec[i]._noShrinkShapes.count( eID ))
2335         //       continue;
2336         //     for ( int is1st = 0; is1st < 2; ++is1st )
2337         //     {
2338         //       TopoDS_Vertex V = helper.IthVertex( is1st, E );
2339         //       if ( _sdVec[i]._noShrinkShapes.count( getMeshDS()->ShapeToIndex( V ) ))
2340         //       {
2341         //         return error("No way to make a conformal mesh with "
2342         //                      "the given set of faces with layers", _sdVec[i]._index);
2343         //       }
2344         //     }
2345         //   }
2346         // }
2347       }
2348
2349       // add VERTEXes of the edge in _noShrinkShapes, which is necessary if
2350       // _shrinkShape2Shape is different in the adjacent SOLID
2351       for ( TopoDS_Iterator vIt( edge ); vIt.More(); vIt.Next() )
2352       {
2353         TGeomID vID = getMeshDS()->ShapeToIndex( vIt.Value() );
2354         bool noShrinkV = false, noShrinkIfAdjMeshed = false;
2355
2356         if ( iSolid < _sdVec.size() )
2357         {
2358           if ( _sdVec[ iSolid ]._ignoreFaceIds.count( faceID ))
2359           {
2360             map< TGeomID, TopoDS_Shape >::iterator i2S, i2SAdj;
2361             i2S    = _sdVec[i     ]._shrinkShape2Shape.find( vID );
2362             i2SAdj = _sdVec[iSolid]._shrinkShape2Shape.find( vID );
2363             if ( i2SAdj == _sdVec[iSolid]._shrinkShape2Shape.end() )
2364               noShrinkV = (( isStructured ) ||
2365                            ( noShrinkIfAdjMeshed = i2S->second.ShapeType() == TopAbs_EDGE ));
2366             else
2367               noShrinkV = ( ! i2S->second.IsSame( i2SAdj->second ));
2368           }
2369           else
2370           {
2371             noShrinkV = noShrinkE;
2372           }
2373         }
2374         else
2375         {
2376           // the adjacent SOLID has NO layers at all
2377           if ( isStructured )
2378           {
2379             noShrinkV = true;
2380           }
2381           else
2382           {
2383             noShrinkV = noShrinkIfAdjMeshed =
2384               ( _sdVec[i]._shrinkShape2Shape[ vID ].ShapeType() == TopAbs_EDGE );
2385           }
2386         }
2387
2388         if ( noShrinkV && noShrinkIfAdjMeshed )
2389         {
2390           // noShrinkV if FACEs in the adjacent SOLID are meshed
2391           PShapeIteratorPtr fIt = helper.GetAncestors( _sdVec[i]._shrinkShape2Shape[ vID ],
2392                                                        *_mesh, TopAbs_FACE, &solid );
2393           while ( fIt->more() )
2394           {
2395             const TopoDS_Shape* f = fIt->next();
2396             if ( !f->IsSame( fWOL ))
2397             {
2398               noShrinkV = ! _mesh->GetSubMesh( *f )->IsEmpty();
2399               break;
2400             }
2401           }
2402         }
2403         if ( noShrinkV )
2404           _sdVec[i]._noShrinkShapes.insert( vID );
2405       }
2406
2407     } // loop on _sdVec[i]._shrinkShape2Shape
2408   } // loop on _sdVec to fill in _SolidData::_noShrinkShapes
2409
2410
2411     // add FACEs of other SOLIDs to _ignoreFaceIds
2412   for ( size_t i = 0; i < _sdVec.size(); ++i )
2413   {
2414     shapes.Clear();
2415     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_FACE, shapes);
2416
2417     for ( exp.Init( _mesh->GetShapeToMesh(), TopAbs_FACE ); exp.More(); exp.Next() )
2418     {
2419       if ( !shapes.Contains( exp.Current() ))
2420         _sdVec[i]._ignoreFaceIds.insert( getMeshDS()->ShapeToIndex( exp.Current() ));
2421     }
2422   }
2423
2424   return true;
2425 }
2426
2427 //================================================================================
2428 /*!
2429  * \brief Finds FACEs w/o layers for a given SOLID by an hypothesis
2430  */
2431 //================================================================================
2432
2433 void _ViscousBuilder::getIgnoreFaces(const TopoDS_Shape&             solid,
2434                                      const StdMeshers_ViscousLayers* hyp,
2435                                      const TopoDS_Shape&             hypShape,
2436                                      set<TGeomID>&                   ignoreFaceIds)
2437 {
2438   TopExp_Explorer exp;
2439
2440   vector<TGeomID> ids = hyp->GetBndShapes();
2441   if ( hyp->IsToIgnoreShapes() ) // FACEs to ignore are given
2442   {
2443     for ( size_t ii = 0; ii < ids.size(); ++ii )
2444     {
2445       const TopoDS_Shape& s = getMeshDS()->IndexToShape( ids[ii] );
2446       if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2447         ignoreFaceIds.insert( ids[ii] );
2448     }
2449   }
2450   else // FACEs with layers are given
2451   {
2452     exp.Init( solid, TopAbs_FACE );
2453     for ( ; exp.More(); exp.Next() )
2454     {
2455       TGeomID faceInd = getMeshDS()->ShapeToIndex( exp.Current() );
2456       if ( find( ids.begin(), ids.end(), faceInd ) == ids.end() )
2457         ignoreFaceIds.insert( faceInd );
2458     }
2459   }
2460
2461   // ignore internal FACEs if inlets and outlets are specified
2462   if ( hyp->IsToIgnoreShapes() )
2463   {
2464     TopTools_IndexedDataMapOfShapeListOfShape solidsOfFace;
2465     TopExp::MapShapesAndAncestors( hypShape,
2466                                    TopAbs_FACE, TopAbs_SOLID, solidsOfFace);
2467
2468     for ( exp.Init( solid, TopAbs_FACE ); exp.More(); exp.Next() )
2469     {
2470       const TopoDS_Face& face = TopoDS::Face( exp.Current() );
2471       if ( SMESH_MesherHelper::NbAncestors( face, *_mesh, TopAbs_SOLID ) < 2 )
2472         continue;
2473
2474       int nbSolids = solidsOfFace.FindFromKey( face ).Extent();
2475       if ( nbSolids > 1 )
2476         ignoreFaceIds.insert( getMeshDS()->ShapeToIndex( face ));
2477     }
2478   }
2479 }
2480
2481 //================================================================================
2482 /*!
2483  * \brief Create the inner surface of the viscous layer and prepare data for infation
2484  */
2485 //================================================================================
2486
2487 bool _ViscousBuilder::makeLayer(_SolidData& data)
2488 {
2489   // get all sub-shapes to make layers on
2490   set<TGeomID> subIds, faceIds;
2491   subIds = data._noShrinkShapes;
2492   TopExp_Explorer exp( data._solid, TopAbs_FACE );
2493   for ( ; exp.More(); exp.Next() )
2494   {
2495     SMESH_subMesh* fSubM = _mesh->GetSubMesh( exp.Current() );
2496     if ( ! data._ignoreFaceIds.count( fSubM->GetId() ))
2497       faceIds.insert( fSubM->GetId() );
2498   }
2499
2500   // make a map to find new nodes on sub-shapes shared with other SOLID
2501   map< TGeomID, TNode2Edge* >::iterator s2ne;
2502   map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
2503   for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
2504   {
2505     TGeomID shapeInd = s2s->first;
2506     for ( size_t i = 0; i < _sdVec.size(); ++i )
2507     {
2508       if ( _sdVec[i]._index == data._index ) continue;
2509       map< TGeomID, TopoDS_Shape >::iterator s2s2 = _sdVec[i]._shrinkShape2Shape.find( shapeInd );
2510       if ( s2s2 != _sdVec[i]._shrinkShape2Shape.end() &&
2511            *s2s == *s2s2 && !_sdVec[i]._n2eMap.empty() )
2512       {
2513         data._s2neMap.insert( make_pair( shapeInd, &_sdVec[i]._n2eMap ));
2514         break;
2515       }
2516     }
2517   }
2518
2519   // Create temporary faces and _LayerEdge's
2520
2521   dumpFunction(SMESH_Comment("makeLayers_")<<data._index);
2522
2523   data._stepSize = Precision::Infinite();
2524   data._stepSizeNodes[0] = 0;
2525
2526   SMESH_MesherHelper helper( *_mesh );
2527   helper.SetSubShape( data._solid );
2528   helper.SetElementsOnShape( true );
2529
2530   vector< const SMDS_MeshNode*> newNodes; // of a mesh face
2531   TNode2Edge::iterator n2e2;
2532
2533   // collect _LayerEdge's of shapes they are based on
2534   vector< _EdgesOnShape >& edgesByGeom = data._edgesOnShape;
2535   const int nbShapes = getMeshDS()->MaxShapeIndex();
2536   edgesByGeom.resize( nbShapes+1 );
2537
2538   // set data of _EdgesOnShape's
2539   if ( SMESH_subMesh* sm = _mesh->GetSubMesh( data._solid ))
2540   {
2541     SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(/*includeSelf=*/false);
2542     while ( smIt->more() )
2543     {
2544       sm = smIt->next();
2545       if ( sm->GetSubShape().ShapeType() == TopAbs_FACE &&
2546            !faceIds.count( sm->GetId() ))
2547         continue;
2548       setShapeData( edgesByGeom[ sm->GetId() ], sm, data );
2549     }
2550   }
2551   // make _LayerEdge's
2552   for ( set<TGeomID>::iterator id = faceIds.begin(); id != faceIds.end(); ++id )
2553   {
2554     const TopoDS_Face& F = TopoDS::Face( getMeshDS()->IndexToShape( *id ));
2555     SMESH_subMesh* sm = _mesh->GetSubMesh( F );
2556     SMESH_ProxyMesh::SubMesh* proxySub =
2557       data._proxyMesh->getFaceSubM( F, /*create=*/true);
2558
2559     SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
2560     if ( !smDS ) return error(SMESH_Comment("Not meshed face ") << *id, data._index );
2561
2562     SMDS_ElemIteratorPtr eIt = smDS->GetElements();
2563     while ( eIt->more() )
2564     {
2565       const SMDS_MeshElement* face = eIt->next();
2566       double          faceMaxCosin = -1;
2567       _LayerEdge*     maxCosinEdge = 0;
2568       int             nbDegenNodes = 0;
2569
2570       newNodes.resize( face->NbCornerNodes() );
2571       for ( size_t i = 0 ; i < newNodes.size(); ++i )
2572       {
2573         const SMDS_MeshNode* n = face->GetNode( i );
2574         const int      shapeID = n->getshapeId();
2575         const bool onDegenShap = helper.IsDegenShape( shapeID );
2576         const bool onDegenEdge = ( onDegenShap && n->GetPosition()->GetDim() == 1 );
2577         if ( onDegenShap )
2578         {
2579           if ( onDegenEdge )
2580           {
2581             // substitute n on a degenerated EDGE with a node on a corresponding VERTEX
2582             const TopoDS_Shape& E = getMeshDS()->IndexToShape( shapeID );
2583             TopoDS_Vertex       V = helper.IthVertex( 0, TopoDS::Edge( E ));
2584             if ( const SMDS_MeshNode* vN = SMESH_Algo::VertexNode( V, getMeshDS() )) {
2585               n = vN;
2586               nbDegenNodes++;
2587             }
2588           }
2589           else
2590           {
2591             nbDegenNodes++;
2592           }
2593         }
2594         TNode2Edge::iterator n2e = data._n2eMap.insert( make_pair( n, (_LayerEdge*)0 )).first;
2595         if ( !(*n2e).second )
2596         {
2597           // add a _LayerEdge
2598           _LayerEdge* edge = new _LayerEdge();
2599           edge->_nodes.push_back( n );
2600           n2e->second = edge;
2601           edgesByGeom[ shapeID ]._edges.push_back( edge );
2602           const bool noShrink = data._noShrinkShapes.count( shapeID );
2603
2604           SMESH_TNodeXYZ xyz( n );
2605
2606           // set edge data or find already refined _LayerEdge and get data from it
2607           if (( !noShrink                                                     ) &&
2608               ( n->GetPosition()->GetTypeOfPosition() != SMDS_TOP_FACE        ) &&
2609               (( s2ne = data._s2neMap.find( shapeID )) != data._s2neMap.end() ) &&
2610               (( n2e2 = (*s2ne).second->find( n )) != s2ne->second->end()     ))
2611           {
2612             _LayerEdge* foundEdge = (*n2e2).second;
2613             gp_XYZ        lastPos = edge->Copy( *foundEdge, edgesByGeom[ shapeID ], helper );
2614             foundEdge->_pos.push_back( lastPos );
2615             // location of the last node is modified and we restore it by foundEdge->_pos.back()
2616             const_cast< SMDS_MeshNode* >
2617               ( edge->_nodes.back() )->setXYZ( xyz.X(), xyz.Y(), xyz.Z() );
2618           }
2619           else
2620           {
2621             if ( !noShrink )
2622             {
2623               edge->_nodes.push_back( helper.AddNode( xyz.X(), xyz.Y(), xyz.Z() ));
2624             }
2625             if ( !setEdgeData( *edge, edgesByGeom[ shapeID ], helper, data ))
2626               return false;
2627
2628             if ( edge->_nodes.size() < 2 )
2629               edge->Block( data );
2630               //data._noShrinkShapes.insert( shapeID );
2631           }
2632           dumpMove(edge->_nodes.back());
2633
2634           if ( edge->_cosin > faceMaxCosin )
2635           {
2636             faceMaxCosin = edge->_cosin;
2637             maxCosinEdge = edge;
2638           }
2639         }
2640         newNodes[ i ] = n2e->second->_nodes.back();
2641
2642         if ( onDegenEdge )
2643           data._n2eMap.insert( make_pair( face->GetNode( i ), n2e->second ));
2644       }
2645       if ( newNodes.size() - nbDegenNodes < 2 )
2646         continue;
2647
2648       // create a temporary face
2649       const SMDS_MeshElement* newFace =
2650         new _TmpMeshFace( newNodes, --_tmpFaceID, face->GetShapeID(), face );
2651       proxySub->AddElement( newFace );
2652
2653       // compute inflation step size by min size of element on a convex surface
2654       if ( faceMaxCosin > theMinSmoothCosin )
2655         limitStepSize( data, face, maxCosinEdge );
2656
2657     } // loop on 2D elements on a FACE
2658   } // loop on FACEs of a SOLID to create _LayerEdge's
2659
2660
2661   // Set _LayerEdge::_neibors
2662   TNode2Edge::iterator n2e;
2663   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
2664   {
2665     _EdgesOnShape& eos = data._edgesOnShape[iS];
2666     for ( size_t i = 0; i < eos._edges.size(); ++i )
2667     {
2668       _LayerEdge* edge = eos._edges[i];
2669       TIDSortedNodeSet nearNodes;
2670       SMDS_ElemIteratorPtr fIt = edge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
2671       while ( fIt->more() )
2672       {
2673         const SMDS_MeshElement* f = fIt->next();
2674         if ( !data._ignoreFaceIds.count( f->getshapeId() ))
2675           nearNodes.insert( f->begin_nodes(), f->end_nodes() );
2676       }
2677       nearNodes.erase( edge->_nodes[0] );
2678       edge->_neibors.reserve( nearNodes.size() );
2679       TIDSortedNodeSet::iterator node = nearNodes.begin();
2680       for ( ; node != nearNodes.end(); ++node )
2681         if (( n2e = data._n2eMap.find( *node )) != data._n2eMap.end() )
2682           edge->_neibors.push_back( n2e->second );
2683     }
2684   }
2685
2686   data._epsilon = 1e-7;
2687   if ( data._stepSize < 1. )
2688     data._epsilon *= data._stepSize;
2689
2690   if ( !findShapesToSmooth( data )) // _LayerEdge::_maxLen is computed here
2691     return false;
2692
2693   // limit data._stepSize depending on surface curvature and fill data._convexFaces
2694   limitStepSizeByCurvature( data ); // !!! it must be before node substitution in _Simplex
2695
2696   // Set target nodes into _Simplex and _LayerEdge's to _2NearEdges
2697   const SMDS_MeshNode* nn[2];
2698   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
2699   {
2700     _EdgesOnShape& eos = data._edgesOnShape[iS];
2701     for ( size_t i = 0; i < eos._edges.size(); ++i )
2702     {
2703       _LayerEdge* edge = eos._edges[i];
2704       if ( edge->IsOnEdge() )
2705       {
2706         // get neighbor nodes
2707         bool hasData = ( edge->_2neibors->_edges[0] );
2708         if ( hasData ) // _LayerEdge is a copy of another one
2709         {
2710           nn[0] = edge->_2neibors->srcNode(0);
2711           nn[1] = edge->_2neibors->srcNode(1);
2712         }
2713         else if ( !findNeiborsOnEdge( edge, nn[0],nn[1], eos, data ))
2714         {
2715           return false;
2716         }
2717         // set neighbor _LayerEdge's
2718         for ( int j = 0; j < 2; ++j )
2719         {
2720           if (( n2e = data._n2eMap.find( nn[j] )) == data._n2eMap.end() )
2721             return error("_LayerEdge not found by src node", data._index);
2722           edge->_2neibors->_edges[j] = n2e->second;
2723         }
2724         if ( !hasData )
2725           edge->SetDataByNeighbors( nn[0], nn[1], eos, helper );
2726       }
2727
2728       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
2729       {
2730         _Simplex& s = edge->_simplices[j];
2731         s._nNext = data._n2eMap[ s._nNext ]->_nodes.back();
2732         s._nPrev = data._n2eMap[ s._nPrev ]->_nodes.back();
2733       }
2734
2735       // For an _LayerEdge on a degenerated EDGE, copy some data from
2736       // a corresponding _LayerEdge on a VERTEX
2737       // (issue 52453, pb on a downloaded SampleCase2-Tet-netgen-mephisto.hdf)
2738       if ( helper.IsDegenShape( edge->_nodes[0]->getshapeId() ))
2739       {
2740         // Generally we should not get here
2741         if ( eos.ShapeType() != TopAbs_EDGE )
2742           continue;
2743         TopoDS_Vertex V = helper.IthVertex( 0, TopoDS::Edge( eos._shape ));
2744         const SMDS_MeshNode* vN = SMESH_Algo::VertexNode( V, getMeshDS() );
2745         if (( n2e = data._n2eMap.find( vN )) == data._n2eMap.end() )
2746           continue;
2747         const _LayerEdge* vEdge = n2e->second;
2748         edge->_normal    = vEdge->_normal;
2749         edge->_lenFactor = vEdge->_lenFactor;
2750         edge->_cosin     = vEdge->_cosin;
2751       }
2752
2753     } // loop on data._edgesOnShape._edges
2754   } // loop on data._edgesOnShape
2755
2756   // fix _LayerEdge::_2neibors on EDGEs to smooth
2757   // map< TGeomID,Handle(Geom_Curve)>::iterator e2c = data._edge2curve.begin();
2758   // for ( ; e2c != data._edge2curve.end(); ++e2c )
2759   //   if ( !e2c->second.IsNull() )
2760   //   {
2761   //     if ( _EdgesOnShape* eos = data.GetShapeEdges( e2c->first ))
2762   //       data.Sort2NeiborsOnEdge( eos->_edges );
2763   //   }
2764
2765   dumpFunctionEnd();
2766   return true;
2767 }
2768
2769 //================================================================================
2770 /*!
2771  * \brief Compute inflation step size by min size of element on a convex surface
2772  */
2773 //================================================================================
2774
2775 void _ViscousBuilder::limitStepSize( _SolidData&             data,
2776                                      const SMDS_MeshElement* face,
2777                                      const _LayerEdge*       maxCosinEdge )
2778 {
2779   int iN = 0;
2780   double minSize = 10 * data._stepSize;
2781   const int nbNodes = face->NbCornerNodes();
2782   for ( int i = 0; i < nbNodes; ++i )
2783   {
2784     const SMDS_MeshNode* nextN = face->GetNode( SMESH_MesherHelper::WrapIndex( i+1, nbNodes ));
2785     const SMDS_MeshNode*  curN = face->GetNode( i );
2786     if ( nextN->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ||
2787          curN-> GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2788     {
2789       double dist = SMESH_TNodeXYZ( curN ).Distance( nextN );
2790       if ( dist < minSize )
2791         minSize = dist, iN = i;
2792     }
2793   }
2794   double newStep = 0.8 * minSize / maxCosinEdge->_lenFactor;
2795   if ( newStep < data._stepSize )
2796   {
2797     data._stepSize = newStep;
2798     data._stepSizeCoeff = 0.8 / maxCosinEdge->_lenFactor;
2799     data._stepSizeNodes[0] = face->GetNode( iN );
2800     data._stepSizeNodes[1] = face->GetNode( SMESH_MesherHelper::WrapIndex( iN+1, nbNodes ));
2801   }
2802 }
2803
2804 //================================================================================
2805 /*!
2806  * \brief Compute inflation step size by min size of element on a convex surface
2807  */
2808 //================================================================================
2809
2810 void _ViscousBuilder::limitStepSize( _SolidData& data, const double minSize )
2811 {
2812   if ( minSize < data._stepSize )
2813   {
2814     data._stepSize = minSize;
2815     if ( data._stepSizeNodes[0] )
2816     {
2817       double dist =
2818         SMESH_TNodeXYZ(data._stepSizeNodes[0]).Distance(data._stepSizeNodes[1]);
2819       data._stepSizeCoeff = data._stepSize / dist;
2820     }
2821   }
2822 }
2823
2824 //================================================================================
2825 /*!
2826  * \brief Limit data._stepSize by evaluating curvature of shapes and fill data._convexFaces
2827  */
2828 //================================================================================
2829
2830 void _ViscousBuilder::limitStepSizeByCurvature( _SolidData& data )
2831 {
2832   SMESH_MesherHelper helper( *_mesh );
2833
2834   BRepLProp_SLProps surfProp( 2, 1e-6 );
2835   data._convexFaces.clear();
2836
2837   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
2838   {
2839     _EdgesOnShape& eof = data._edgesOnShape[iS];
2840     if ( eof.ShapeType() != TopAbs_FACE ||
2841          data._ignoreFaceIds.count( eof._shapeID ))
2842       continue;
2843
2844     TopoDS_Face        F = TopoDS::Face( eof._shape );
2845     const TGeomID faceID = eof._shapeID;
2846
2847     BRepAdaptor_Surface surface( F, false );
2848     surfProp.SetSurface( surface );
2849
2850     _ConvexFace cnvFace;
2851     cnvFace._face = F;
2852     cnvFace._normalsFixed = false;
2853     cnvFace._isTooCurved = false;
2854
2855     double maxCurvature = cnvFace.GetMaxCurvature( data, eof, surfProp, helper );
2856     if ( maxCurvature > 0 )
2857     {
2858       limitStepSize( data, 0.9 / maxCurvature );
2859       findEdgesToUpdateNormalNearConvexFace( cnvFace, data, helper );
2860     }
2861     if ( !cnvFace._isTooCurved ) continue;
2862
2863     _ConvexFace & convFace =
2864       data._convexFaces.insert( make_pair( faceID, cnvFace )).first->second;
2865
2866     // skip a closed surface (data._convexFaces is useful anyway)
2867     bool isClosedF = false;
2868     helper.SetSubShape( F );
2869     if ( helper.HasRealSeam() )
2870     {
2871       // in the closed surface there must be a closed EDGE
2872       for ( TopExp_Explorer eIt( F, TopAbs_EDGE ); eIt.More() && !isClosedF; eIt.Next() )
2873         isClosedF = helper.IsClosedEdge( TopoDS::Edge( eIt.Current() ));
2874     }
2875     if ( isClosedF )
2876     {
2877       // limit _LayerEdge::_maxLen on the FACE
2878       const double oriFactor    = ( F.Orientation() == TopAbs_REVERSED ? +1. : -1. );
2879       const double minCurvature =
2880         1. / ( eof._hyp.GetTotalThickness() * ( 1 + theThickToIntersection ));
2881       map< TGeomID, _EdgesOnShape* >::iterator id2eos = cnvFace._subIdToEOS.find( faceID );
2882       if ( id2eos != cnvFace._subIdToEOS.end() )
2883       {
2884         _EdgesOnShape& eos = * id2eos->second;
2885         for ( size_t i = 0; i < eos._edges.size(); ++i )
2886         {
2887           _LayerEdge* ledge = eos._edges[ i ];
2888           gp_XY uv = helper.GetNodeUV( F, ledge->_nodes[0] );
2889           surfProp.SetParameters( uv.X(), uv.Y() );
2890           if ( surfProp.IsCurvatureDefined() )
2891           {
2892             double curvature = Max( surfProp.MaxCurvature() * oriFactor,
2893                                     surfProp.MinCurvature() * oriFactor );
2894             if ( curvature > minCurvature )
2895               ledge->SetMaxLen( Min( ledge->_maxLen, 1. / curvature ));
2896           }
2897         }
2898       }
2899       continue;
2900     }
2901
2902     // Fill _ConvexFace::_simplexTestEdges. These _LayerEdge's are used to detect
2903     // prism distortion.
2904     map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.find( faceID );
2905     if ( id2eos != convFace._subIdToEOS.end() && !id2eos->second->_edges.empty() )
2906     {
2907       // there are _LayerEdge's on the FACE it-self;
2908       // select _LayerEdge's near EDGEs
2909       _EdgesOnShape& eos = * id2eos->second;
2910       for ( size_t i = 0; i < eos._edges.size(); ++i )
2911       {
2912         _LayerEdge* ledge = eos._edges[ i ];
2913         for ( size_t j = 0; j < ledge->_simplices.size(); ++j )
2914           if ( ledge->_simplices[j]._nNext->GetPosition()->GetDim() < 2 )
2915           {
2916             // do not select _LayerEdge's neighboring sharp EDGEs
2917             bool sharpNbr = false;
2918             for ( size_t iN = 0; iN < ledge->_neibors.size()  && !sharpNbr; ++iN )
2919               sharpNbr = ( ledge->_neibors[iN]->_cosin > theMinSmoothCosin );
2920             if ( !sharpNbr )
2921               convFace._simplexTestEdges.push_back( ledge );
2922             break;
2923           }
2924       }
2925     }
2926     else
2927     {
2928       // where there are no _LayerEdge's on a _ConvexFace,
2929       // as e.g. on a fillet surface with no internal nodes - issue 22580,
2930       // so that collision of viscous internal faces is not detected by check of
2931       // intersection of _LayerEdge's with the viscous internal faces.
2932
2933       set< const SMDS_MeshNode* > usedNodes;
2934
2935       // look for _LayerEdge's with null _sWOL
2936       id2eos = convFace._subIdToEOS.begin();
2937       for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
2938       {
2939         _EdgesOnShape& eos = * id2eos->second;
2940         if ( !eos._sWOL.IsNull() )
2941           continue;
2942         for ( size_t i = 0; i < eos._edges.size(); ++i )
2943         {
2944           _LayerEdge* ledge = eos._edges[ i ];
2945           const SMDS_MeshNode* srcNode = ledge->_nodes[0];
2946           if ( !usedNodes.insert( srcNode ).second ) continue;
2947
2948           for ( size_t i = 0; i < ledge->_simplices.size(); ++i )
2949           {
2950             usedNodes.insert( ledge->_simplices[i]._nPrev );
2951             usedNodes.insert( ledge->_simplices[i]._nNext );
2952           }
2953           convFace._simplexTestEdges.push_back( ledge );
2954         }
2955       }
2956     }
2957   } // loop on FACEs of data._solid
2958 }
2959
2960 //================================================================================
2961 /*!
2962  * \brief Detect shapes (and _LayerEdge's on them) to smooth
2963  */
2964 //================================================================================
2965
2966 bool _ViscousBuilder::findShapesToSmooth( _SolidData& data )
2967 {
2968   // define allowed thickness
2969   computeGeomSize( data ); // compute data._geomSize and _LayerEdge::_maxLen
2970
2971
2972   // Find shapes needing smoothing; such a shape has _LayerEdge._normal on it's
2973   // boundary inclined to the shape at a sharp angle
2974
2975   TopTools_MapOfShape edgesOfSmooFaces;
2976   SMESH_MesherHelper helper( *_mesh );
2977   bool ok = true;
2978
2979   vector< _EdgesOnShape >& edgesByGeom = data._edgesOnShape;
2980   data._nbShapesToSmooth = 0;
2981
2982   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check FACEs
2983   {
2984     _EdgesOnShape& eos = edgesByGeom[iS];
2985     eos._toSmooth = false;
2986     if ( eos._edges.empty() || eos.ShapeType() != TopAbs_FACE )
2987       continue;
2988
2989     double tgtThick = eos._hyp.GetTotalThickness();
2990     SMESH_subMeshIteratorPtr subIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false );
2991     while ( subIt->more() && !eos._toSmooth )
2992     {
2993       TGeomID iSub = subIt->next()->GetId();
2994       const vector<_LayerEdge*>& eSub = edgesByGeom[ iSub ]._edges;
2995       if ( eSub.empty() ) continue;
2996
2997       double faceSize;
2998       for ( size_t i = 0; i < eSub.size() && !eos._toSmooth; ++i )
2999         if ( eSub[i]->_cosin > theMinSmoothCosin )
3000         {
3001           SMDS_ElemIteratorPtr fIt = eSub[i]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
3002           while ( fIt->more() && !eos._toSmooth )
3003           {
3004             const SMDS_MeshElement* face = fIt->next();
3005             if ( face->getshapeId() == eos._shapeID &&
3006                  getDistFromEdge( face, eSub[i]->_nodes[0], faceSize ))
3007             {
3008               eos._toSmooth = needSmoothing( eSub[i]->_cosin,
3009                                              tgtThick * eSub[i]->_lenFactor,
3010                                              faceSize);
3011             }
3012           }
3013         }
3014     }
3015     if ( eos._toSmooth )
3016     {
3017       for ( TopExp_Explorer eExp( edgesByGeom[iS]._shape, TopAbs_EDGE ); eExp.More(); eExp.Next() )
3018         edgesOfSmooFaces.Add( eExp.Current() );
3019
3020       data.PrepareEdgesToSmoothOnFace( &edgesByGeom[iS], /*substituteSrcNodes=*/false );
3021     }
3022     data._nbShapesToSmooth += eos._toSmooth;
3023
3024   }  // check FACEs
3025
3026   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check EDGEs
3027   {
3028     _EdgesOnShape& eos = edgesByGeom[iS];
3029     eos._edgeSmoother = NULL;
3030     if ( eos._edges.empty() || eos.ShapeType() != TopAbs_EDGE ) continue;
3031     if ( !eos._hyp.ToSmooth() ) continue;
3032
3033     const TopoDS_Edge& E = TopoDS::Edge( edgesByGeom[iS]._shape );
3034     if ( SMESH_Algo::isDegenerated( E ) || !edgesOfSmooFaces.Contains( E ))
3035       continue;
3036
3037     double tgtThick = eos._hyp.GetTotalThickness();
3038     for ( TopoDS_Iterator vIt( E ); vIt.More() && !eos._toSmooth; vIt.Next() )
3039     {
3040       TGeomID iV = getMeshDS()->ShapeToIndex( vIt.Value() );
3041       vector<_LayerEdge*>& eV = edgesByGeom[ iV ]._edges;
3042       if ( eV.empty() || eV[0]->Is( _LayerEdge::MULTI_NORMAL )) continue;
3043       gp_Vec  eDir    = getEdgeDir( E, TopoDS::Vertex( vIt.Value() ));
3044       double angle    = eDir.Angle( eV[0]->_normal );
3045       double cosin    = Cos( angle );
3046       double cosinAbs = Abs( cosin );
3047       if ( cosinAbs > theMinSmoothCosin )
3048       {
3049         // always smooth analytic EDGEs
3050         Handle(Geom_Curve) curve = _Smoother1D::CurveForSmooth( E, eos, helper );
3051         eos._toSmooth = ! curve.IsNull();
3052
3053         // compare tgtThick with the length of an end segment
3054         SMDS_ElemIteratorPtr eIt = eV[0]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Edge);
3055         while ( eIt->more() && !eos._toSmooth )
3056         {
3057           const SMDS_MeshElement* endSeg = eIt->next();
3058           if ( endSeg->getshapeId() == (int) iS )
3059           {
3060             double segLen =
3061               SMESH_TNodeXYZ( endSeg->GetNode( 0 )).Distance( endSeg->GetNode( 1 ));
3062             eos._toSmooth = needSmoothing( cosinAbs, tgtThick * eV[0]->_lenFactor, segLen );
3063           }
3064         }
3065         if ( eos._toSmooth )
3066         {
3067           eos._edgeSmoother = new _Smoother1D( curve, eos );
3068
3069           // for ( size_t i = 0; i < eos._edges.size(); ++i )
3070           //   eos._edges[i]->Set( _LayerEdge::TO_SMOOTH );
3071         }
3072       }
3073     }
3074     data._nbShapesToSmooth += eos._toSmooth;
3075
3076   } // check EDGEs
3077
3078   // Reset _cosin if no smooth is allowed by the user
3079   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS )
3080   {
3081     _EdgesOnShape& eos = edgesByGeom[iS];
3082     if ( eos._edges.empty() ) continue;
3083
3084     if ( !eos._hyp.ToSmooth() )
3085       for ( size_t i = 0; i < eos._edges.size(); ++i )
3086         //eos._edges[i]->SetCosin( 0 ); // keep _cosin to use in limitMaxLenByCurvature()
3087         eos._edges[i]->_lenFactor = 1;
3088   }
3089
3090
3091   // Fill _eosC1 to make that C1 FACEs and EDGEs between them to be smoothed as a whole
3092
3093   TopTools_MapOfShape c1VV;
3094
3095   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check FACEs
3096   {
3097     _EdgesOnShape& eos = edgesByGeom[iS];
3098     if ( eos._edges.empty() ||
3099          eos.ShapeType() != TopAbs_FACE ||
3100          !eos._toSmooth )
3101       continue;
3102
3103     // check EDGEs of a FACE
3104     TopTools_MapOfShape checkedEE, allVV;
3105     list< SMESH_subMesh* > smQueue( 1, eos._subMesh ); // sm of FACEs
3106     while ( !smQueue.empty() )
3107     {
3108       SMESH_subMesh* sm = smQueue.front();
3109       smQueue.pop_front();
3110       SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(/*includeSelf=*/false);
3111       while ( smIt->more() )
3112       {
3113         sm = smIt->next();
3114         if ( sm->GetSubShape().ShapeType() == TopAbs_VERTEX )
3115           allVV.Add( sm->GetSubShape() );
3116         if ( sm->GetSubShape().ShapeType() != TopAbs_EDGE ||
3117              !checkedEE.Add( sm->GetSubShape() ))
3118           continue;
3119
3120         _EdgesOnShape*      eoe = data.GetShapeEdges( sm->GetId() );
3121         vector<_LayerEdge*>& eE = eoe->_edges;
3122         if ( eE.empty() || !eoe->_sWOL.IsNull() )
3123           continue;
3124
3125         bool isC1 = true; // check continuity along an EDGE
3126         for ( size_t i = 0; i < eE.size() && isC1; ++i )
3127           isC1 = ( Abs( eE[i]->_cosin ) < theMinSmoothCosin );
3128         if ( !isC1 )
3129           continue;
3130
3131         // check that mesh faces are C1 as well
3132         {
3133           gp_XYZ norm1, norm2;
3134           const SMDS_MeshNode*   n = eE[ eE.size() / 2 ]->_nodes[0];
3135           SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
3136           if ( !SMESH_MeshAlgos::FaceNormal( fIt->next(), norm1, /*normalized=*/true ))
3137             continue;
3138           while ( fIt->more() && isC1 )
3139             isC1 = ( SMESH_MeshAlgos::FaceNormal( fIt->next(), norm2, /*normalized=*/true ) &&
3140                      Abs( norm1 * norm2 ) >= ( 1. - theMinSmoothCosin ));
3141           if ( !isC1 )
3142             continue;
3143         }
3144
3145         // add the EDGE and an adjacent FACE to _eosC1
3146         PShapeIteratorPtr fIt = helper.GetAncestors( sm->GetSubShape(), *_mesh, TopAbs_FACE );
3147         while ( const TopoDS_Shape* face = fIt->next() )
3148         {
3149           _EdgesOnShape* eof = data.GetShapeEdges( *face );
3150           if ( !eof ) continue; // other solid
3151           if ( eos._shapeID == eof->_shapeID ) continue;
3152           if ( !eos.HasC1( eof ))
3153           {
3154             // check the FACEs
3155             eos._eosC1.push_back( eof );
3156             eof->_toSmooth = false;
3157             data.PrepareEdgesToSmoothOnFace( eof, /*substituteSrcNodes=*/false );
3158             smQueue.push_back( eof->_subMesh );
3159           }
3160           if ( !eos.HasC1( eoe ))
3161           {
3162             eos._eosC1.push_back( eoe );
3163             eoe->_toSmooth = false;
3164             data.PrepareEdgesToSmoothOnFace( eoe, /*substituteSrcNodes=*/false );
3165           }
3166         }
3167       }
3168     }
3169     if ( eos._eosC1.empty() )
3170       continue;
3171
3172     // check VERTEXes of C1 FACEs
3173     TopTools_MapIteratorOfMapOfShape vIt( allVV );
3174     for ( ; vIt.More(); vIt.Next() )
3175     {
3176       _EdgesOnShape* eov = data.GetShapeEdges( vIt.Key() );
3177       if ( !eov || eov->_edges.empty() || !eov->_sWOL.IsNull() )
3178         continue;
3179
3180       bool isC1 = true; // check if all adjacent FACEs are in eos._eosC1
3181       PShapeIteratorPtr fIt = helper.GetAncestors( vIt.Key(), *_mesh, TopAbs_FACE );
3182       while ( const TopoDS_Shape* face = fIt->next() )
3183       {
3184         _EdgesOnShape* eof = data.GetShapeEdges( *face );
3185         if ( !eof ) continue; // other solid
3186         isC1 = ( face->IsSame( eos._shape ) || eos.HasC1( eof ));
3187         if ( !isC1 )
3188           break;
3189       }
3190       if ( isC1 )
3191       {
3192         eos._eosC1.push_back( eov );
3193         data.PrepareEdgesToSmoothOnFace( eov, /*substituteSrcNodes=*/false );
3194         c1VV.Add( eov->_shape );
3195       }
3196     }
3197
3198   } // fill _eosC1 of FACEs
3199
3200
3201   // Find C1 EDGEs
3202
3203   vector< pair< _EdgesOnShape*, gp_XYZ > > dirOfEdges;
3204
3205   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS ) // check VERTEXes
3206   {
3207     _EdgesOnShape& eov = edgesByGeom[iS];
3208     if ( eov._edges.empty() ||
3209          eov.ShapeType() != TopAbs_VERTEX ||
3210          c1VV.Contains( eov._shape ))
3211       continue;
3212     const TopoDS_Vertex& V = TopoDS::Vertex( eov._shape );
3213
3214     // get directions of surrounding EDGEs
3215     dirOfEdges.clear();
3216     PShapeIteratorPtr fIt = helper.GetAncestors( eov._shape, *_mesh, TopAbs_EDGE );
3217     while ( const TopoDS_Shape* e = fIt->next() )
3218     {
3219       _EdgesOnShape* eoe = data.GetShapeEdges( *e );
3220       if ( !eoe ) continue; // other solid
3221       gp_XYZ eDir = getEdgeDir( TopoDS::Edge( *e ), V );
3222       if ( !Precision::IsInfinite( eDir.X() ))
3223         dirOfEdges.push_back( make_pair( eoe, eDir.Normalized() ));
3224     }
3225
3226     // find EDGEs with C1 directions
3227     for ( size_t i = 0; i < dirOfEdges.size(); ++i )
3228       for ( size_t j = i+1; j < dirOfEdges.size(); ++j )
3229         if ( dirOfEdges[i].first && dirOfEdges[j].first )
3230         {
3231           double dot = dirOfEdges[i].second * dirOfEdges[j].second;
3232           bool isC1 = ( dot < - ( 1. - theMinSmoothCosin ));
3233           if ( isC1 )
3234           {
3235             double maxEdgeLen = 3 * Min( eov._edges[0]->_maxLen, eov._hyp.GetTotalThickness() );
3236             double eLen1 = SMESH_Algo::EdgeLength( TopoDS::Edge( dirOfEdges[i].first->_shape ));
3237             double eLen2 = SMESH_Algo::EdgeLength( TopoDS::Edge( dirOfEdges[j].first->_shape ));
3238             if ( eLen1 < maxEdgeLen ) eov._eosC1.push_back( dirOfEdges[i].first );
3239             if ( eLen2 < maxEdgeLen ) eov._eosC1.push_back( dirOfEdges[j].first );
3240             dirOfEdges[i].first = 0;
3241             dirOfEdges[j].first = 0;
3242           }
3243         }
3244   } // fill _eosC1 of VERTEXes
3245
3246
3247
3248   return ok;
3249 }
3250
3251 //================================================================================
3252 /*!
3253  * \brief initialize data of _EdgesOnShape
3254  */
3255 //================================================================================
3256
3257 void _ViscousBuilder::setShapeData( _EdgesOnShape& eos,
3258                                     SMESH_subMesh* sm,
3259                                     _SolidData&    data )
3260 {
3261   if ( !eos._shape.IsNull() ||
3262        sm->GetSubShape().ShapeType() == TopAbs_WIRE )
3263     return;
3264
3265   SMESH_MesherHelper helper( *_mesh );
3266
3267   eos._subMesh = sm;
3268   eos._shapeID = sm->GetId();
3269   eos._shape   = sm->GetSubShape();
3270   if ( eos.ShapeType() == TopAbs_FACE )
3271     eos._shape.Orientation( helper.GetSubShapeOri( data._solid, eos._shape ));
3272   eos._toSmooth = false;
3273   eos._data = &data;
3274
3275   // set _SWOL
3276   map< TGeomID, TopoDS_Shape >::const_iterator s2s =
3277     data._shrinkShape2Shape.find( eos._shapeID );
3278   if ( s2s != data._shrinkShape2Shape.end() )
3279     eos._sWOL = s2s->second;
3280
3281   eos._isRegularSWOL = true;
3282   if ( eos.SWOLType() == TopAbs_FACE )
3283   {
3284     const TopoDS_Face& F = TopoDS::Face( eos._sWOL );
3285     Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( F );
3286     eos._isRegularSWOL = ( ! surface->HasSingularities( 1e-7 ));
3287   }
3288
3289   // set _hyp
3290   if ( data._hyps.size() == 1 )
3291   {
3292     eos._hyp = data._hyps.back();
3293   }
3294   else
3295   {
3296     // compute average StdMeshers_ViscousLayers parameters
3297     map< TGeomID, const StdMeshers_ViscousLayers* >::iterator f2hyp;
3298     if ( eos.ShapeType() == TopAbs_FACE )
3299     {
3300       if (( f2hyp = data._face2hyp.find( eos._shapeID )) != data._face2hyp.end() )
3301         eos._hyp = f2hyp->second;
3302     }
3303     else
3304     {
3305       PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
3306       while ( const TopoDS_Shape* face = fIt->next() )
3307       {
3308         TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
3309         if (( f2hyp = data._face2hyp.find( faceID )) != data._face2hyp.end() )
3310           eos._hyp.Add( f2hyp->second );
3311       }
3312     }
3313   }
3314
3315   // set _faceNormals
3316   if ( ! eos._hyp.UseSurfaceNormal() )
3317   {
3318     if ( eos.ShapeType() == TopAbs_FACE ) // get normals to elements on a FACE
3319     {
3320       SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
3321       if ( !smDS ) return;
3322       eos._faceNormals.reserve( smDS->NbElements() );
3323
3324       double oriFactor = helper.IsReversedSubMesh( TopoDS::Face( eos._shape )) ? 1.: -1.;
3325       SMDS_ElemIteratorPtr eIt = smDS->GetElements();
3326       for ( ; eIt->more(); )
3327       {
3328         const SMDS_MeshElement* face = eIt->next();
3329         gp_XYZ&                 norm = eos._faceNormals[face];
3330         if ( !SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
3331           norm.SetCoord( 0,0,0 );
3332         norm *= oriFactor;
3333       }
3334     }
3335     else // find EOS of adjacent FACEs
3336     {
3337       PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
3338       while ( const TopoDS_Shape* face = fIt->next() )
3339       {
3340         TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
3341         eos._faceEOS.push_back( & data._edgesOnShape[ faceID ]);
3342         if ( eos._faceEOS.back()->_shape.IsNull() )
3343           // avoid using uninitialised _shapeID in GetNormal()
3344           eos._faceEOS.back()->_shapeID = faceID;
3345       }
3346     }
3347   }
3348 }
3349
3350 //================================================================================
3351 /*!
3352  * \brief Returns normal of a face
3353  */
3354 //================================================================================
3355
3356 bool _EdgesOnShape::GetNormal( const SMDS_MeshElement* face, gp_Vec& norm )
3357 {
3358   bool ok = false;
3359   _EdgesOnShape* eos = 0;
3360
3361   if ( face->getshapeId() == _shapeID )
3362   {
3363     eos = this;
3364   }
3365   else
3366   {
3367     for ( size_t iF = 0; iF < _faceEOS.size() && !eos; ++iF )
3368       if ( face->getshapeId() == _faceEOS[ iF ]->_shapeID )
3369         eos = _faceEOS[ iF ];
3370   }
3371
3372   if (( eos ) &&
3373       ( ok = ( eos->_faceNormals.count( face ) )))
3374   {
3375     norm = eos->_faceNormals[ face ];
3376   }
3377   else if ( !eos )
3378   {
3379     debugMsg( "_EdgesOnShape::Normal() failed for face "<<face->GetID()
3380               << " on _shape #" << _shapeID );
3381   }
3382   return ok;
3383 }
3384
3385
3386 //================================================================================
3387 /*!
3388  * \brief Set data of _LayerEdge needed for smoothing
3389  */
3390 //================================================================================
3391
3392 bool _ViscousBuilder::setEdgeData(_LayerEdge&         edge,
3393                                   _EdgesOnShape&      eos,
3394                                   SMESH_MesherHelper& helper,
3395                                   _SolidData&         data)
3396 {
3397   const SMDS_MeshNode* node = edge._nodes[0]; // source node
3398
3399   edge._len       = 0;
3400   edge._maxLen    = Precision::Infinite();
3401   edge._minAngle  = 0;
3402   edge._2neibors  = 0;
3403   edge._curvature = 0;
3404   edge._flags     = 0;
3405
3406   // --------------------------
3407   // Compute _normal and _cosin
3408   // --------------------------
3409
3410   edge._cosin     = 0;
3411   edge._lenFactor = 1.;
3412   edge._normal.SetCoord(0,0,0);
3413   _Simplex::GetSimplices( node, edge._simplices, data._ignoreFaceIds, &data );
3414
3415   int totalNbFaces = 0;
3416   TopoDS_Face F;
3417   std::pair< TopoDS_Face, gp_XYZ > face2Norm[20];
3418   gp_Vec geomNorm;
3419   bool normOK = true;
3420
3421   const bool onShrinkShape = !eos._sWOL.IsNull();
3422   const bool useGeometry   = (( eos._hyp.UseSurfaceNormal() ) ||
3423                               ( eos.ShapeType() != TopAbs_FACE /*&& !onShrinkShape*/ ));
3424
3425   // get geom FACEs the node lies on
3426   //if ( useGeometry )
3427   {
3428     set<TGeomID> faceIds;
3429     if  ( eos.ShapeType() == TopAbs_FACE )
3430     {
3431       faceIds.insert( eos._shapeID );
3432     }
3433     else
3434     {
3435       SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
3436       while ( fIt->more() )
3437         faceIds.insert( fIt->next()->getshapeId() );
3438     }
3439     set<TGeomID>::iterator id = faceIds.begin();
3440     for ( ; id != faceIds.end(); ++id )
3441     {
3442       const TopoDS_Shape& s = getMeshDS()->IndexToShape( *id );
3443       if ( s.IsNull() || s.ShapeType() != TopAbs_FACE || data._ignoreFaceIds.count( *id ))
3444         continue;
3445       F = TopoDS::Face( s );
3446       face2Norm[ totalNbFaces ].first = F;
3447       totalNbFaces++;
3448     }
3449   }
3450
3451   // find _normal
3452   bool fromVonF = false;
3453   if ( useGeometry )
3454   {
3455     fromVonF = ( eos.ShapeType() == TopAbs_VERTEX &&
3456                  eos.SWOLType()  == TopAbs_FACE  &&
3457                  totalNbFaces > 1 );
3458
3459     if ( onShrinkShape && !fromVonF ) // one of faces the node is on has no layers
3460     {
3461       if ( eos.SWOLType() == TopAbs_EDGE )
3462       {
3463         // inflate from VERTEX along EDGE
3464         edge._normal = getEdgeDir( TopoDS::Edge( eos._sWOL ), TopoDS::Vertex( eos._shape ));
3465       }
3466       else if ( eos.ShapeType() == TopAbs_VERTEX )
3467       {
3468         // inflate from VERTEX along FACE
3469         edge._normal = getFaceDir( TopoDS::Face( eos._sWOL ), TopoDS::Vertex( eos._shape ),
3470                                    node, helper, normOK, &edge._cosin);
3471       }
3472       else
3473       {
3474         // inflate from EDGE along FACE
3475         edge._normal = getFaceDir( TopoDS::Face( eos._sWOL ), TopoDS::Edge( eos._shape ),
3476                                    node, helper, normOK);
3477       }
3478     }
3479     else // layers are on all FACEs of SOLID the node is on (or fromVonF)
3480     {
3481       if ( fromVonF )
3482         face2Norm[ totalNbFaces++ ].first = TopoDS::Face( eos._sWOL );
3483
3484       int nbOkNorms = 0;
3485       for ( int iF = totalNbFaces - 1; iF >= 0; --iF )
3486       {
3487         F = face2Norm[ iF ].first;
3488         geomNorm = getFaceNormal( node, F, helper, normOK );
3489         if ( !normOK ) continue;
3490         nbOkNorms++;
3491
3492         if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3493           geomNorm.Reverse();
3494         face2Norm[ iF ].second = geomNorm.XYZ();
3495         edge._normal += geomNorm.XYZ();
3496       }
3497       if ( nbOkNorms == 0 )
3498         return error(SMESH_Comment("Can't get normal to node ") << node->GetID(), data._index);
3499
3500       if ( totalNbFaces >= 3 )
3501       {
3502         edge._normal = getNormalByOffset( &edge, face2Norm, totalNbFaces, fromVonF );
3503       }
3504
3505       if ( edge._normal.Modulus() < 1e-3 && nbOkNorms > 1 )
3506       {
3507         // opposite normals, re-get normals at shifted positions (IPAL 52426)
3508         edge._normal.SetCoord( 0,0,0 );
3509         for ( int iF = 0; iF < totalNbFaces - fromVonF; ++iF )
3510         {
3511           const TopoDS_Face& F = face2Norm[iF].first;
3512           geomNorm = getFaceNormal( node, F, helper, normOK, /*shiftInside=*/true );
3513           if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3514             geomNorm.Reverse();
3515           if ( normOK )
3516             face2Norm[ iF ].second = geomNorm.XYZ();
3517           edge._normal += face2Norm[ iF ].second;
3518         }
3519       }
3520     }
3521   }
3522   else // !useGeometry - get _normal using surrounding mesh faces
3523   {
3524     edge._normal = getWeigthedNormal( &edge );
3525
3526     // set<TGeomID> faceIds;
3527     //
3528     // SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
3529     // while ( fIt->more() )
3530     // {
3531     //   const SMDS_MeshElement* face = fIt->next();
3532     //   if ( eos.GetNormal( face, geomNorm ))
3533     //   {
3534     //     if ( onShrinkShape && !faceIds.insert( face->getshapeId() ).second )
3535     //       continue; // use only one mesh face on FACE
3536     //     edge._normal += geomNorm.XYZ();
3537     //     totalNbFaces++;
3538     //   }
3539     // }
3540   }
3541
3542   // compute _cosin
3543   //if ( eos._hyp.UseSurfaceNormal() )
3544   {
3545     switch ( eos.ShapeType() )
3546     {
3547     case TopAbs_FACE: {
3548       edge._cosin = 0;
3549       break;
3550     }
3551     case TopAbs_EDGE: {
3552       TopoDS_Edge E    = TopoDS::Edge( eos._shape );
3553       gp_Vec inFaceDir = getFaceDir( F, E, node, helper, normOK );
3554       double angle     = inFaceDir.Angle( edge._normal ); // [0,PI]
3555       edge._cosin      = Cos( angle );
3556       break;
3557     }
3558     case TopAbs_VERTEX: {
3559       if ( fromVonF )
3560       {
3561         getFaceDir( TopoDS::Face( eos._sWOL ), TopoDS::Vertex( eos._shape ),
3562                     node, helper, normOK, &edge._cosin );
3563       }
3564       else if ( eos.SWOLType() != TopAbs_FACE ) // else _cosin is set by getFaceDir()
3565       {
3566         TopoDS_Vertex V  = TopoDS::Vertex( eos._shape );
3567         gp_Vec inFaceDir = getFaceDir( F, V, node, helper, normOK );
3568         double angle     = inFaceDir.Angle( edge._normal ); // [0,PI]
3569         edge._cosin      = Cos( angle );
3570         if ( totalNbFaces > 2 || helper.IsSeamShape( node->getshapeId() ))
3571           for ( int iF = 1; iF < totalNbFaces; ++iF )
3572           {
3573             F = face2Norm[ iF ].first;
3574             inFaceDir = getFaceDir( F, V, node, helper, normOK=true );
3575             if ( normOK ) {
3576               double angle = inFaceDir.Angle( edge._normal );
3577               double cosin = Cos( angle );
3578               if ( Abs( cosin ) > Abs( edge._cosin ))
3579                 edge._cosin = cosin;
3580             }
3581           }
3582       }
3583       break;
3584     }
3585     default:
3586       return error(SMESH_Comment("Invalid shape position of node ")<<node, data._index);
3587     }
3588   }
3589
3590   double normSize = edge._normal.SquareModulus();
3591   if ( normSize < numeric_limits<double>::min() )
3592     return error(SMESH_Comment("Bad normal at node ")<< node->GetID(), data._index );
3593
3594   edge._normal /= sqrt( normSize );
3595
3596   if ( edge.Is( _LayerEdge::MULTI_NORMAL ) && edge._nodes.size() == 2 )
3597   {
3598     getMeshDS()->RemoveFreeNode( edge._nodes.back(), 0, /*fromGroups=*/false );
3599     edge._nodes.resize( 1 );
3600     edge._normal.SetCoord( 0,0,0 );
3601     edge.SetMaxLen( 0 );
3602   }
3603
3604   // Set the rest data
3605   // --------------------
3606
3607   edge.SetCosin( edge._cosin ); // to update edge._lenFactor
3608
3609   if ( onShrinkShape )
3610   {
3611     const SMDS_MeshNode* tgtNode = edge._nodes.back();
3612     if ( SMESHDS_SubMesh* sm = getMeshDS()->MeshElements( data._solid ))
3613       sm->RemoveNode( tgtNode );
3614
3615     // set initial position which is parameters on _sWOL in this case
3616     if ( eos.SWOLType() == TopAbs_EDGE )
3617     {
3618       double u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), node, 0, &normOK );
3619       edge._pos.push_back( gp_XYZ( u, 0, 0 ));
3620       if ( edge._nodes.size() > 1 )
3621         getMeshDS()->SetNodeOnEdge( tgtNode, TopoDS::Edge( eos._sWOL ), u );
3622     }
3623     else // eos.SWOLType() == TopAbs_FACE
3624     {
3625       gp_XY uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), node, 0, &normOK );
3626       edge._pos.push_back( gp_XYZ( uv.X(), uv.Y(), 0));
3627       if ( edge._nodes.size() > 1 )
3628         getMeshDS()->SetNodeOnFace( tgtNode, TopoDS::Face( eos._sWOL ), uv.X(), uv.Y() );
3629     }
3630
3631     if ( edge._nodes.size() > 1 )
3632     {
3633       // check if an angle between a FACE with layers and SWOL is sharp,
3634       // else the edge should not inflate
3635       F.Nullify();
3636       for ( int iF = 0; iF < totalNbFaces  &&  F.IsNull();  ++iF ) // find a FACE with VL
3637         if ( ! helper.IsSubShape( eos._sWOL, face2Norm[iF].first ))
3638           F = face2Norm[iF].first;
3639       if ( !F.IsNull())
3640       {
3641         geomNorm = getFaceNormal( node, F, helper, normOK );
3642         if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
3643           geomNorm.Reverse(); // inside the SOLID
3644         if ( geomNorm * edge._normal < -0.001 )
3645         {
3646           getMeshDS()->RemoveFreeNode( tgtNode, 0, /*fromGroups=*/false );
3647           edge._nodes.resize( 1 );
3648         }
3649         else if ( edge._lenFactor > 3 )
3650         {
3651           edge._lenFactor = 2;
3652           edge.Set( _LayerEdge::RISKY_SWOL );
3653         }
3654       }
3655     }
3656   }
3657   else
3658   {
3659     edge._pos.push_back( SMESH_TNodeXYZ( node ));
3660
3661     if ( eos.ShapeType() == TopAbs_FACE )
3662     {
3663       double angle;
3664       for ( size_t i = 0; i < edge._simplices.size(); ++i )
3665       {
3666         edge._simplices[i].IsMinAngleOK( edge._pos.back(), angle );
3667         edge._minAngle = Max( edge._minAngle, angle ); // "angle" is actually cosine
3668       }
3669     }
3670   }
3671
3672   // Set neighbor nodes for a _LayerEdge based on EDGE
3673
3674   if ( eos.ShapeType() == TopAbs_EDGE /*||
3675        ( onShrinkShape && posType == SMDS_TOP_VERTEX && fabs( edge._cosin ) < 1e-10 )*/)
3676   {
3677     edge._2neibors = new _2NearEdges;
3678     // target nodes instead of source ones will be set later
3679   }
3680
3681   return true;
3682 }
3683
3684 //================================================================================
3685 /*!
3686  * \brief Return normal to a FACE at a node
3687  *  \param [in] n - node
3688  *  \param [in] face - FACE
3689  *  \param [in] helper - helper
3690  *  \param [out] isOK - true or false
3691  *  \param [in] shiftInside - to find normal at a position shifted inside the face
3692  *  \return gp_XYZ - normal
3693  */
3694 //================================================================================
3695
3696 gp_XYZ _ViscousBuilder::getFaceNormal(const SMDS_MeshNode* node,
3697                                       const TopoDS_Face&   face,
3698                                       SMESH_MesherHelper&  helper,
3699                                       bool&                isOK,
3700                                       bool                 shiftInside)
3701 {
3702   gp_XY uv;
3703   if ( shiftInside )
3704   {
3705     // get a shifted position
3706     gp_Pnt p = SMESH_TNodeXYZ( node );
3707     gp_XYZ shift( 0,0,0 );
3708     TopoDS_Shape S = helper.GetSubShapeByNode( node, helper.GetMeshDS() );
3709     switch ( S.ShapeType() ) {
3710     case TopAbs_VERTEX:
3711     {
3712       shift = getFaceDir( face, TopoDS::Vertex( S ), node, helper, isOK );
3713       break;
3714     }
3715     case TopAbs_EDGE:
3716     {
3717       shift = getFaceDir( face, TopoDS::Edge( S ), node, helper, isOK );
3718       break;
3719     }
3720     default:
3721       isOK = false;
3722     }
3723     if ( isOK )
3724       shift.Normalize();
3725     p.Translate( shift * 1e-5 );
3726
3727     TopLoc_Location loc;
3728     GeomAPI_ProjectPointOnSurf& projector = helper.GetProjector( face, loc, 1e-7 );
3729
3730     if ( !loc.IsIdentity() ) p.Transform( loc.Transformation().Inverted() );
3731     
3732     projector.Perform( p );
3733     if ( !projector.IsDone() || projector.NbPoints() < 1 )
3734     {
3735       isOK = false;
3736       return p.XYZ();
3737     }
3738     Standard_Real U,V;
3739     projector.LowerDistanceParameters(U,V);
3740     uv.SetCoord( U,V );
3741   }
3742   else
3743   {
3744     uv = helper.GetNodeUV( face, node, 0, &isOK );
3745   }
3746
3747   gp_Dir normal;
3748   isOK = false;
3749
3750   Handle(Geom_Surface) surface = BRep_Tool::Surface( face );
3751
3752   if ( !shiftInside &&
3753        helper.IsDegenShape( node->getshapeId() ) &&
3754        getFaceNormalAtSingularity( uv, face, helper, normal ))
3755   {
3756     isOK = true;
3757     return normal.XYZ();
3758   }
3759
3760   int pointKind = GeomLib::NormEstim( surface, uv, 1e-5, normal );
3761   enum { REGULAR = 0, QUASYSINGULAR, CONICAL, IMPOSSIBLE };
3762
3763   if ( pointKind == IMPOSSIBLE &&
3764        node->GetPosition()->GetDim() == 2 ) // node inside the FACE
3765   {
3766     // probably NormEstim() failed due to a too high tolerance
3767     pointKind = GeomLib::NormEstim( surface, uv, 1e-20, normal );
3768     isOK = ( pointKind < IMPOSSIBLE );
3769   }
3770   if ( pointKind < IMPOSSIBLE )
3771   {
3772     if ( pointKind != REGULAR &&
3773          !shiftInside &&
3774          node->GetPosition()->GetDim() < 2 ) // FACE boundary
3775     {
3776       gp_XYZ normShift = getFaceNormal( node, face, helper, isOK, /*shiftInside=*/true );
3777       if ( normShift * normal.XYZ() < 0. )
3778         normal = normShift;
3779     }
3780     isOK = true;
3781   }
3782
3783   if ( !isOK ) // hard singularity, to call with shiftInside=true ?
3784   {
3785     const TGeomID faceID = helper.GetMeshDS()->ShapeToIndex( face );
3786
3787     SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
3788     while ( fIt->more() )
3789     {
3790       const SMDS_MeshElement* f = fIt->next();
3791       if ( f->getshapeId() == faceID )
3792       {
3793         isOK = SMESH_MeshAlgos::FaceNormal( f, (gp_XYZ&) normal.XYZ(), /*normalized=*/true );
3794         if ( isOK )
3795         {
3796           TopoDS_Face ff = face;
3797           ff.Orientation( TopAbs_FORWARD );
3798           if ( helper.IsReversedSubMesh( ff ))
3799             normal.Reverse();
3800           break;
3801         }
3802       }
3803     }
3804   }
3805   return normal.XYZ();
3806 }
3807
3808 //================================================================================
3809 /*!
3810  * \brief Try to get normal at a singularity of a surface basing on it's nature
3811  */
3812 //================================================================================
3813
3814 bool _ViscousBuilder::getFaceNormalAtSingularity( const gp_XY&        uv,
3815                                                   const TopoDS_Face&  face,
3816                                                   SMESH_MesherHelper& helper,
3817                                                   gp_Dir&             normal )
3818 {
3819   BRepAdaptor_Surface surface( face );
3820   gp_Dir axis;
3821   if ( !getRovolutionAxis( surface, axis ))
3822     return false;
3823
3824   double f,l, d, du, dv;
3825   f = surface.FirstUParameter();
3826   l = surface.LastUParameter();
3827   d = ( uv.X() - f ) / ( l - f );
3828   du = ( d < 0.5 ? +1. : -1 ) * 1e-5 * ( l - f );
3829   f = surface.FirstVParameter();
3830   l = surface.LastVParameter();
3831   d = ( uv.Y() - f ) / ( l - f );
3832   dv = ( d < 0.5 ? +1. : -1 ) * 1e-5 * ( l - f );
3833
3834   gp_Dir refDir;
3835   gp_Pnt2d testUV = uv;
3836   enum { REGULAR = 0, QUASYSINGULAR, CONICAL, IMPOSSIBLE };
3837   double tol = 1e-5;
3838   Handle(Geom_Surface) geomsurf = surface.Surface().Surface();
3839   for ( int iLoop = 0; true ; ++iLoop )
3840   {
3841     testUV.SetCoord( testUV.X() + du, testUV.Y() + dv );
3842     if ( GeomLib::NormEstim( geomsurf, testUV, tol, refDir ) == REGULAR )
3843       break;
3844     if ( iLoop > 20 )
3845       return false;
3846     tol /= 10.;
3847   }
3848
3849   if ( axis * refDir < 0. )
3850     axis.Reverse();
3851
3852   normal = axis;
3853
3854   return true;
3855 }
3856
3857 //================================================================================
3858 /*!
3859  * \brief Return a normal at a node weighted with angles taken by faces
3860  */
3861 //================================================================================
3862
3863 gp_XYZ _ViscousBuilder::getWeigthedNormal( const _LayerEdge* edge )
3864 {
3865   const SMDS_MeshNode* n = edge->_nodes[0];
3866
3867   gp_XYZ resNorm(0,0,0);
3868   SMESH_TNodeXYZ p0( n ), pP, pN;
3869   for ( size_t i = 0; i < edge->_simplices.size(); ++i )
3870   {
3871     pP.Set( edge->_simplices[i]._nPrev );
3872     pN.Set( edge->_simplices[i]._nNext );
3873     gp_Vec v0P( p0, pP ), v0N( p0, pN ), vPN( pP, pN ), norm = v0P ^ v0N;
3874     double l0P = v0P.SquareMagnitude();
3875     double l0N = v0N.SquareMagnitude();
3876     double lPN = vPN.SquareMagnitude();
3877     if ( l0P < std::numeric_limits<double>::min() ||
3878          l0N < std::numeric_limits<double>::min() ||
3879          lPN < std::numeric_limits<double>::min() )
3880       continue;
3881     double lNorm = norm.SquareMagnitude();
3882     double  sin2 = lNorm / l0P / l0N;
3883     double angle = ACos(( v0P * v0N ) / Sqrt( l0P ) / Sqrt( l0N ));
3884
3885     double weight = sin2 * angle / lPN;
3886     resNorm += weight * norm.XYZ() / Sqrt( lNorm );
3887   }
3888
3889   return resNorm;
3890 }
3891
3892 //================================================================================
3893 /*!
3894  * \brief Return a normal at a node by getting a common point of offset planes
3895  *        defined by the FACE normals
3896  */
3897 //================================================================================
3898
3899 gp_XYZ _ViscousBuilder::getNormalByOffset( _LayerEdge*                      edge,
3900                                            std::pair< TopoDS_Face, gp_XYZ > f2Normal[],
3901                                            int                              nbFaces,
3902                                            bool                             lastNoOffset)
3903 {
3904   SMESH_TNodeXYZ p0 = edge->_nodes[0];
3905
3906   gp_XYZ resNorm(0,0,0);
3907   TopoDS_Shape V = SMESH_MesherHelper::GetSubShapeByNode( p0._node, getMeshDS() );
3908   if ( V.ShapeType() != TopAbs_VERTEX || nbFaces < 3 )
3909   {
3910     for ( int i = 0; i < nbFaces; ++i )
3911       resNorm += f2Normal[i].second;
3912     return resNorm;
3913   }
3914
3915   // prepare _OffsetPlane's
3916   vector< _OffsetPlane > pln( nbFaces );
3917   for ( int i = 0; i < nbFaces - lastNoOffset; ++i )
3918   {
3919     pln[i]._faceIndex = i;
3920     pln[i]._plane = gp_Pln( p0 + f2Normal[i].second, f2Normal[i].second );
3921   }
3922   if ( lastNoOffset )
3923   {
3924     pln[ nbFaces - 1 ]._faceIndex = nbFaces - 1;
3925     pln[ nbFaces - 1 ]._plane = gp_Pln( p0, f2Normal[ nbFaces - 1 ].second );
3926   }
3927
3928   // intersect neighboring OffsetPlane's
3929   PShapeIteratorPtr edgeIt = SMESH_MesherHelper::GetAncestors( V, *_mesh, TopAbs_EDGE );
3930   while ( const TopoDS_Shape* edge = edgeIt->next() )
3931   {
3932     int f1 = -1, f2 = -1;
3933     for ( int i = 0; i < nbFaces &&  f2 < 0;  ++i )
3934       if ( SMESH_MesherHelper::IsSubShape( *edge, f2Normal[i].first ))
3935         (( f1 < 0 ) ? f1 : f2 ) = i;
3936
3937     if ( f2 >= 0 )
3938       pln[ f1 ].ComputeIntersectionLine( pln[ f2 ], TopoDS::Edge( *edge ), TopoDS::Vertex( V ));
3939   }
3940
3941   // get a common point
3942   gp_XYZ commonPnt( 0, 0, 0 );
3943   int nbPoints = 0;
3944   bool isPointFound;
3945   for ( int i = 0; i < nbFaces; ++i )
3946   {
3947     commonPnt += pln[ i ].GetCommonPoint( isPointFound, TopoDS::Vertex( V ));
3948     nbPoints  += isPointFound;
3949   }
3950   gp_XYZ wgtNorm = getWeigthedNormal( edge );
3951   if ( nbPoints == 0 )
3952     return wgtNorm;
3953
3954   commonPnt /= nbPoints;
3955   resNorm = commonPnt - p0;
3956   if ( lastNoOffset )
3957     return resNorm;
3958
3959   // choose the best among resNorm and wgtNorm
3960   resNorm.Normalize();
3961   wgtNorm.Normalize();
3962   double resMinDot = std::numeric_limits<double>::max();
3963   double wgtMinDot = std::numeric_limits<double>::max();
3964   for ( int i = 0; i < nbFaces - lastNoOffset; ++i )
3965   {
3966     resMinDot = Min( resMinDot, resNorm * f2Normal[i].second );
3967     wgtMinDot = Min( wgtMinDot, wgtNorm * f2Normal[i].second );
3968   }
3969
3970   if ( Max( resMinDot, wgtMinDot ) < theMinSmoothCosin )
3971   {
3972     edge->Set( _LayerEdge::MULTI_NORMAL );
3973   }
3974
3975   return ( resMinDot > wgtMinDot ) ? resNorm : wgtNorm;
3976 }
3977
3978 //================================================================================
3979 /*!
3980  * \brief Compute line of intersection of 2 planes
3981  */
3982 //================================================================================
3983
3984 void _OffsetPlane::ComputeIntersectionLine( _OffsetPlane&        pln,
3985                                             const TopoDS_Edge&   E,
3986                                             const TopoDS_Vertex& V )
3987 {
3988   int iNext = bool( _faceIndexNext[0] >= 0 );
3989   _faceIndexNext[ iNext ] = pln._faceIndex;
3990
3991   gp_XYZ n1 = _plane.Axis().Direction().XYZ();
3992   gp_XYZ n2 = pln._plane.Axis().Direction().XYZ();
3993
3994   gp_XYZ lineDir = n1 ^ n2;
3995
3996   double x = Abs( lineDir.X() );
3997   double y = Abs( lineDir.Y() );
3998   double z = Abs( lineDir.Z() );
3999
4000   int cooMax; // max coordinate
4001   if (x > y) {
4002     if (x > z) cooMax = 1;
4003     else       cooMax = 3;
4004   }
4005   else {
4006     if (y > z) cooMax = 2;
4007     else       cooMax = 3;
4008   }
4009
4010   gp_Pnt linePos;
4011   if ( Abs( lineDir.Coord( cooMax )) < 0.05 )
4012   {
4013     // parallel planes - intersection is an offset of the common EDGE
4014     gp_Pnt p = BRep_Tool::Pnt( V );
4015     linePos  = 0.5 * (( p.XYZ() + n1 ) + ( p.XYZ() + n2 ));
4016     lineDir  = getEdgeDir( E, V );
4017   }
4018   else
4019   {
4020     // the constants in the 2 plane equations
4021     double d1 = - ( _plane.Axis().Direction().XYZ()     * _plane.Location().XYZ() );
4022     double d2 = - ( pln._plane.Axis().Direction().XYZ() * pln._plane.Location().XYZ() );
4023
4024     switch ( cooMax ) {
4025     case 1:
4026       linePos.SetX(  0 );
4027       linePos.SetY(( d2*n1.Z() - d1*n2.Z()) / lineDir.X() );
4028       linePos.SetZ(( d1*n2.Y() - d2*n1.Y()) / lineDir.X() );
4029       break;
4030     case 2:
4031       linePos.SetX(( d1*n2.Z() - d2*n1.Z()) / lineDir.Y() );
4032       linePos.SetY(  0 );
4033       linePos.SetZ(( d2*n1.X() - d1*n2.X()) / lineDir.Y() );
4034       break;
4035     case 3:
4036       linePos.SetX(( d2*n1.Y() - d1*n2.Y()) / lineDir.Z() );
4037       linePos.SetY(( d1*n2.X() - d2*n1.X()) / lineDir.Z() );
4038       linePos.SetZ(  0 );
4039     }
4040   }
4041   gp_Lin& line = _lines[ iNext ];
4042   line.SetDirection( lineDir );
4043   line.SetLocation ( linePos );
4044
4045   _isLineOK[ iNext ] = true;
4046
4047
4048   iNext = bool( pln._faceIndexNext[0] >= 0 );
4049   pln._lines        [ iNext ] = line;
4050   pln._faceIndexNext[ iNext ] = this->_faceIndex;
4051   pln._isLineOK     [ iNext ] = true;
4052 }
4053
4054 //================================================================================
4055 /*!
4056  * \brief Computes intersection point of two _lines
4057  */
4058 //================================================================================
4059
4060 gp_XYZ _OffsetPlane::GetCommonPoint(bool&                 isFound,
4061                                     const TopoDS_Vertex & V) const
4062 {
4063   gp_XYZ p( 0,0,0 );
4064   isFound = false;
4065
4066   if ( NbLines() == 2 )
4067   {
4068     gp_Vec lPerp0 = _lines[0].Direction().XYZ() ^ _plane.Axis().Direction().XYZ();
4069     double  dot01 = lPerp0 * _lines[1].Direction().XYZ();
4070     if ( Abs( dot01 ) > 0.05 )
4071     {
4072       gp_Vec l0l1 = _lines[1].Location().XYZ() - _lines[0].Location().XYZ();
4073       double   u1 = - ( lPerp0 * l0l1 ) / dot01;
4074       p = ( _lines[1].Location().XYZ() + _lines[1].Direction().XYZ() * u1 );
4075       isFound = true;
4076     }
4077     else
4078     {
4079       gp_Pnt  pV ( BRep_Tool::Pnt( V ));
4080       gp_Vec  lv0( _lines[0].Location(), pV    ),  lv1(_lines[1].Location(), pV     );
4081       double dot0( lv0 * _lines[0].Direction() ), dot1( lv1 * _lines[1].Direction() );
4082       p += 0.5 * ( _lines[0].Location().XYZ() + _lines[0].Direction().XYZ() * dot0 );
4083       p += 0.5 * ( _lines[1].Location().XYZ() + _lines[1].Direction().XYZ() * dot1 );
4084       isFound = true;
4085     }
4086   }
4087
4088   return p;
4089 }
4090
4091 //================================================================================
4092 /*!
4093  * \brief Find 2 neigbor nodes of a node on EDGE
4094  */
4095 //================================================================================
4096
4097 bool _ViscousBuilder::findNeiborsOnEdge(const _LayerEdge*     edge,
4098                                         const SMDS_MeshNode*& n1,
4099                                         const SMDS_MeshNode*& n2,
4100                                         _EdgesOnShape&        eos,
4101                                         _SolidData&           data)
4102 {
4103   const SMDS_MeshNode* node = edge->_nodes[0];
4104   const int        shapeInd = eos._shapeID;
4105   SMESHDS_SubMesh*   edgeSM = 0;
4106   if ( eos.ShapeType() == TopAbs_EDGE )
4107   {
4108     edgeSM = eos._subMesh->GetSubMeshDS();
4109     if ( !edgeSM || edgeSM->NbElements() == 0 )
4110       return error(SMESH_Comment("Not meshed EDGE ") << shapeInd, data._index);
4111   }
4112   int iN = 0;
4113   n2 = 0;
4114   SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Edge);
4115   while ( eIt->more() && !n2 )
4116   {
4117     const SMDS_MeshElement* e = eIt->next();
4118     const SMDS_MeshNode*   nNeibor = e->GetNode( 0 );
4119     if ( nNeibor == node ) nNeibor = e->GetNode( 1 );
4120     if ( edgeSM )
4121     {
4122       if (!edgeSM->Contains(e)) continue;
4123     }
4124     else
4125     {
4126       TopoDS_Shape s = SMESH_MesherHelper::GetSubShapeByNode( nNeibor, getMeshDS() );
4127       if ( !SMESH_MesherHelper::IsSubShape( s, eos._sWOL )) continue;
4128     }
4129     ( iN++ ? n2 : n1 ) = nNeibor;
4130   }
4131   if ( !n2 )
4132     return error(SMESH_Comment("Wrongly meshed EDGE ") << shapeInd, data._index);
4133   return true;
4134 }
4135
4136 //================================================================================
4137 /*!
4138  * \brief Set _curvature and _2neibors->_plnNorm by 2 neigbor nodes residing the same EDGE
4139  */
4140 //================================================================================
4141
4142 void _LayerEdge::SetDataByNeighbors( const SMDS_MeshNode* n1,
4143                                      const SMDS_MeshNode* n2,
4144                                      const _EdgesOnShape& eos,
4145                                      SMESH_MesherHelper&  helper)
4146 {
4147   if ( eos.ShapeType() != TopAbs_EDGE )
4148     return;
4149   if ( _curvature && Is( SMOOTHED_C1 ))
4150     return;
4151
4152   gp_XYZ  pos = SMESH_TNodeXYZ( _nodes[0] );
4153   gp_XYZ vec1 = pos - SMESH_TNodeXYZ( n1 );
4154   gp_XYZ vec2 = pos - SMESH_TNodeXYZ( n2 );
4155
4156   // Set _curvature
4157
4158   double      sumLen = vec1.Modulus() + vec2.Modulus();
4159   _2neibors->_wgt[0] = 1 - vec1.Modulus() / sumLen;
4160   _2neibors->_wgt[1] = 1 - vec2.Modulus() / sumLen;
4161   double avgNormProj = 0.5 * ( _normal * vec1 + _normal * vec2 );
4162   double      avgLen = 0.5 * ( vec1.Modulus() + vec2.Modulus() );
4163   if ( _curvature ) delete _curvature;
4164   _curvature = _Curvature::New( avgNormProj, avgLen );
4165   // if ( _curvature )
4166   //   debugMsg( _nodes[0]->GetID()
4167   //             << " CURV r,k: " << _curvature->_r<<","<<_curvature->_k
4168   //             << " proj = "<<avgNormProj<< " len = " << avgLen << "| lenDelta(0) = "
4169   //             << _curvature->lenDelta(0) );
4170
4171   // Set _plnNorm
4172
4173   if ( eos._sWOL.IsNull() )
4174   {
4175     TopoDS_Edge  E = TopoDS::Edge( eos._shape );
4176     // if ( SMESH_Algo::isDegenerated( E ))
4177     //   return;
4178     gp_XYZ dirE    = getEdgeDir( E, _nodes[0], helper );
4179     gp_XYZ plnNorm = dirE ^ _normal;
4180     double proj0   = plnNorm * vec1;
4181     double proj1   = plnNorm * vec2;
4182     if ( fabs( proj0 ) > 1e-10 || fabs( proj1 ) > 1e-10 )
4183     {
4184       if ( _2neibors->_plnNorm ) delete _2neibors->_plnNorm;
4185       _2neibors->_plnNorm = new gp_XYZ( plnNorm.Normalized() );
4186     }
4187   }
4188 }
4189
4190 //================================================================================
4191 /*!
4192  * \brief Copy data from a _LayerEdge of other SOLID and based on the same node;
4193  * this and the other _LayerEdge are inflated along a FACE or an EDGE
4194  */
4195 //================================================================================
4196
4197 gp_XYZ _LayerEdge::Copy( _LayerEdge&         other,
4198                          _EdgesOnShape&      eos,
4199                          SMESH_MesherHelper& helper )
4200 {
4201   _nodes     = other._nodes;
4202   _normal    = other._normal;
4203   _len       = 0;
4204   _lenFactor = other._lenFactor;
4205   _cosin     = other._cosin;
4206   _2neibors  = other._2neibors;
4207   _curvature = 0; std::swap( _curvature, other._curvature );
4208   _2neibors  = 0; std::swap( _2neibors,  other._2neibors );
4209
4210   gp_XYZ lastPos( 0,0,0 );
4211   if ( eos.SWOLType() == TopAbs_EDGE )
4212   {
4213     double u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), _nodes[0] );
4214     _pos.push_back( gp_XYZ( u, 0, 0));
4215
4216     u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), _nodes.back() );
4217     lastPos.SetX( u );
4218   }
4219   else // TopAbs_FACE
4220   {
4221     gp_XY uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), _nodes[0]);
4222     _pos.push_back( gp_XYZ( uv.X(), uv.Y(), 0));
4223
4224     uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), _nodes.back() );
4225     lastPos.SetX( uv.X() );
4226     lastPos.SetY( uv.Y() );
4227   }
4228   return lastPos;
4229 }
4230
4231 //================================================================================
4232 /*!
4233  * \brief Set _cosin and _lenFactor
4234  */
4235 //================================================================================
4236
4237 void _LayerEdge::SetCosin( double cosin )
4238 {
4239   _cosin = cosin;
4240   cosin = Abs( _cosin );
4241   //_lenFactor = ( cosin < 1.-1e-12 ) ?  Min( 2., 1./sqrt(1-cosin*cosin )) : 1.0;
4242   _lenFactor = ( cosin < 1.-1e-12 ) ?  1./sqrt(1-cosin*cosin ) : 1.0;
4243 }
4244
4245 //================================================================================
4246 /*!
4247  * \brief Check if another _LayerEdge is a neighbor on EDGE
4248  */
4249 //================================================================================
4250
4251 bool _LayerEdge::IsNeiborOnEdge( const _LayerEdge* edge ) const
4252 {
4253   return (( this->_2neibors && this->_2neibors->include( edge )) ||
4254           ( edge->_2neibors && edge->_2neibors->include( this )));
4255 }
4256
4257 //================================================================================
4258 /*!
4259  * \brief Fills a vector<_Simplex > 
4260  */
4261 //================================================================================
4262
4263 void _Simplex::GetSimplices( const SMDS_MeshNode* node,
4264                              vector<_Simplex>&    simplices,
4265                              const set<TGeomID>&  ingnoreShapes,
4266                              const _SolidData*    dataToCheckOri,
4267                              const bool           toSort)
4268 {
4269   simplices.clear();
4270   SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
4271   while ( fIt->more() )
4272   {
4273     const SMDS_MeshElement* f = fIt->next();
4274     const TGeomID    shapeInd = f->getshapeId();
4275     if ( ingnoreShapes.count( shapeInd )) continue;
4276     const int nbNodes = f->NbCornerNodes();
4277     const int  srcInd = f->GetNodeIndex( node );
4278     const SMDS_MeshNode* nPrev = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd-1, nbNodes ));
4279     const SMDS_MeshNode* nNext = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd+1, nbNodes ));
4280     const SMDS_MeshNode* nOpp  = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd+2, nbNodes ));
4281     if ( dataToCheckOri && dataToCheckOri->_reversedFaceIds.count( shapeInd ))
4282       std::swap( nPrev, nNext );
4283     simplices.push_back( _Simplex( nPrev, nNext, ( nbNodes == 3 ? 0 : nOpp )));
4284   }
4285
4286   if ( toSort )
4287     SortSimplices( simplices );
4288 }
4289
4290 //================================================================================
4291 /*!
4292  * \brief Set neighbor simplices side by side
4293  */
4294 //================================================================================
4295
4296 void _Simplex::SortSimplices(vector<_Simplex>& simplices)
4297 {
4298   vector<_Simplex> sortedSimplices( simplices.size() );
4299   sortedSimplices[0] = simplices[0];
4300   size_t nbFound = 0;
4301   for ( size_t i = 1; i < simplices.size(); ++i )
4302   {
4303     for ( size_t j = 1; j < simplices.size(); ++j )
4304       if ( sortedSimplices[i-1]._nNext == simplices[j]._nPrev )
4305       {
4306         sortedSimplices[i] = simplices[j];
4307         nbFound++;
4308         break;
4309       }
4310   }
4311   if ( nbFound == simplices.size() - 1 )
4312     simplices.swap( sortedSimplices );
4313 }
4314
4315 //================================================================================
4316 /*!
4317  * \brief DEBUG. Create groups containing temporary data of _LayerEdge's
4318  */
4319 //================================================================================
4320
4321 void _ViscousBuilder::makeGroupOfLE()
4322 {
4323 #ifdef _DEBUG_
4324   for ( size_t i = 0 ; i < _sdVec.size(); ++i )
4325   {
4326     if ( _sdVec[i]._n2eMap.empty() ) continue;
4327
4328     dumpFunction( SMESH_Comment("make_LayerEdge_") << i );
4329     TNode2Edge::iterator n2e;
4330     for ( n2e = _sdVec[i]._n2eMap.begin(); n2e != _sdVec[i]._n2eMap.end(); ++n2e )
4331     {
4332       _LayerEdge* le = n2e->second;
4333       // for ( size_t iN = 1; iN < le->_nodes.size(); ++iN )
4334       //   dumpCmd(SMESH_Comment("mesh.AddEdge([ ") <<le->_nodes[iN-1]->GetID()
4335       //           << ", " << le->_nodes[iN]->GetID() <<"])");
4336       if ( le ) {
4337         dumpCmd(SMESH_Comment("mesh.AddEdge([ ") <<le->_nodes[0]->GetID()
4338                 << ", " << le->_nodes.back()->GetID() <<"]) # " << le->_flags );
4339       }
4340     }
4341     dumpFunctionEnd();
4342
4343     dumpFunction( SMESH_Comment("makeNormals") << i );
4344     for ( n2e = _sdVec[i]._n2eMap.begin(); n2e != _sdVec[i]._n2eMap.end(); ++n2e )
4345     {
4346       _LayerEdge* edge = n2e->second;
4347       SMESH_TNodeXYZ nXYZ( edge->_nodes[0] );
4348       nXYZ += edge->_normal * _sdVec[i]._stepSize;
4349       dumpCmd(SMESH_Comment("mesh.AddEdge([ ") << edge->_nodes[0]->GetID()
4350               << ", mesh.AddNode( "<< nXYZ.X()<<","<< nXYZ.Y()<<","<< nXYZ.Z()<<")])");
4351     }
4352     dumpFunctionEnd();
4353
4354     dumpFunction( SMESH_Comment("makeTmpFaces_") << i );
4355     dumpCmd( "faceId1 = mesh.NbElements()" );
4356     TopExp_Explorer fExp( _sdVec[i]._solid, TopAbs_FACE );
4357     for ( ; fExp.More(); fExp.Next() )
4358     {
4359       if ( const SMESHDS_SubMesh* sm = _sdVec[i]._proxyMesh->GetProxySubMesh( fExp.Current() ))
4360       {
4361         if ( sm->NbElements() == 0 ) continue;
4362         SMDS_ElemIteratorPtr fIt = sm->GetElements();
4363         while ( fIt->more())
4364         {
4365           const SMDS_MeshElement* e = fIt->next();
4366           SMESH_Comment cmd("mesh.AddFace([");
4367           for ( int j = 0; j < e->NbCornerNodes(); ++j )
4368             cmd << e->GetNode(j)->GetID() << (j+1 < e->NbCornerNodes() ? ",": "])");
4369           dumpCmd( cmd );
4370         }
4371       }
4372     }
4373     dumpCmd( "faceId2 = mesh.NbElements()" );
4374     dumpCmd( SMESH_Comment( "mesh.MakeGroup( 'tmpFaces_" ) << i << "',"
4375              << "SMESH.FACE, SMESH.FT_RangeOfIds,'=',"
4376              << "'%s-%s' % (faceId1+1, faceId2))");
4377     dumpFunctionEnd();
4378   }
4379 #endif
4380 }
4381
4382 //================================================================================
4383 /*!
4384  * \brief Find maximal _LayerEdge length (layer thickness) limited by geometry
4385  */
4386 //================================================================================
4387
4388 void _ViscousBuilder::computeGeomSize( _SolidData& data )
4389 {
4390   data._geomSize = Precision::Infinite();
4391   double intersecDist;
4392   const SMDS_MeshElement* face;
4393   SMESH_MesherHelper helper( *_mesh );
4394
4395   SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
4396     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
4397                                            data._proxyMesh->GetFaces( data._solid )));
4398
4399   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4400   {
4401     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4402     if ( eos._edges.empty() )
4403       continue;
4404     // get neighbor faces, intersection with which should not be considered since
4405     // collisions are avoided by means of smoothing
4406     set< TGeomID > neighborFaces;
4407     if ( eos._hyp.ToSmooth() )
4408     {
4409       SMESH_subMeshIteratorPtr subIt =
4410         eos._subMesh->getDependsOnIterator(/*includeSelf=*/eos.ShapeType() != TopAbs_FACE );
4411       while ( subIt->more() )
4412       {
4413         SMESH_subMesh* sm = subIt->next();
4414         PShapeIteratorPtr fIt = helper.GetAncestors( sm->GetSubShape(), *_mesh, TopAbs_FACE );
4415         while ( const TopoDS_Shape* face = fIt->next() )
4416           neighborFaces.insert( getMeshDS()->ShapeToIndex( *face ));
4417       }
4418     }
4419     // find intersections
4420     double thinkness = eos._hyp.GetTotalThickness();
4421     for ( size_t i = 0; i < eos._edges.size(); ++i )
4422     {
4423       if ( eos._edges[i]->Is( _LayerEdge::BLOCKED )) continue;
4424       eos._edges[i]->SetMaxLen( thinkness );
4425       eos._edges[i]->FindIntersection( *searcher, intersecDist, data._epsilon, eos, &face );
4426       if ( intersecDist > 0 && face )
4427       {
4428         data._geomSize = Min( data._geomSize, intersecDist );
4429         if ( !neighborFaces.count( face->getshapeId() ))
4430           eos[i]->SetMaxLen( Min( thinkness, intersecDist / ( face->GetID() < 0 ? 3. : 2. )));
4431       }
4432     }
4433   }
4434
4435   data._maxThickness = 0;
4436   data._minThickness = 1e100;
4437   list< const StdMeshers_ViscousLayers* >::iterator hyp = data._hyps.begin();
4438   for ( ; hyp != data._hyps.end(); ++hyp )
4439   {
4440     data._maxThickness = Max( data._maxThickness, (*hyp)->GetTotalThickness() );
4441     data._minThickness = Min( data._minThickness, (*hyp)->GetTotalThickness() );
4442   }
4443
4444   // Limit inflation step size by geometry size found by intersecting
4445   // normals of _LayerEdge's with mesh faces
4446   if ( data._stepSize > 0.3 * data._geomSize )
4447     limitStepSize( data, 0.3 * data._geomSize );
4448
4449   if ( data._stepSize > data._minThickness )
4450     limitStepSize( data, data._minThickness );
4451
4452
4453   // -------------------------------------------------------------------------
4454   // Detect _LayerEdge which can't intersect with opposite or neighbor layer,
4455   // so no need in detecting intersection at each inflation step
4456   // -------------------------------------------------------------------------
4457
4458   int nbSteps = data._maxThickness / data._stepSize;
4459   if ( nbSteps < 3 || nbSteps * data._n2eMap.size() < 100000 )
4460     return;
4461
4462   vector< const SMDS_MeshElement* > closeFaces;
4463   int nbDetected = 0;
4464
4465   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4466   {
4467     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4468     if ( eos._edges.empty() || eos.ShapeType() != TopAbs_FACE )
4469       continue;
4470
4471     for ( size_t i = 0; i < eos.size(); ++i )
4472     {
4473       SMESH_NodeXYZ p( eos[i]->_nodes[0] );
4474       double radius = data._maxThickness + 2 * eos[i]->_maxLen;
4475       closeFaces.clear();
4476       searcher->GetElementsInSphere( p, radius, SMDSAbs_Face, closeFaces );
4477
4478       bool toIgnore = true;
4479       for ( size_t iF = 0; iF < closeFaces.size()  && toIgnore; ++iF )
4480         if ( !( toIgnore = ( closeFaces[ iF ]->getshapeId() == eos._shapeID ||
4481                              data._ignoreFaceIds.count( closeFaces[ iF ]->getshapeId() ))))
4482         {
4483           // check if a _LayerEdge will inflate in a direction opposite to a direction
4484           // toward a close face
4485           bool allBehind = true;
4486           for ( int iN = 0; iN < closeFaces[ iF ]->NbCornerNodes()  && allBehind; ++iN )
4487           {
4488             SMESH_NodeXYZ pi( closeFaces[ iF ]->GetNode( iN ));
4489             allBehind = (( pi - p ) * eos[i]->_normal < 0.1 * data._stepSize );
4490           }
4491           toIgnore = allBehind;
4492         }
4493
4494
4495       if ( toIgnore ) // no need to detect intersection
4496       {
4497         eos[i]->Set( _LayerEdge::INTERSECTED );
4498         ++nbDetected;
4499       }
4500     }
4501   }
4502
4503   debugMsg( "Nb LE to intersect " << data._n2eMap.size()-nbDetected << ", ignore " << nbDetected );
4504
4505   return;
4506 }
4507
4508 //================================================================================
4509 /*!
4510  * \brief Increase length of _LayerEdge's to reach the required thickness of layers
4511  */
4512 //================================================================================
4513
4514 bool _ViscousBuilder::inflate(_SolidData& data)
4515 {
4516   SMESH_MesherHelper helper( *_mesh );
4517
4518   const double tgtThick = data._maxThickness;
4519
4520   if ( data._stepSize < 1. )
4521     data._epsilon = data._stepSize * 1e-7;
4522
4523   debugMsg( "-- geomSize = " << data._geomSize << ", stepSize = " << data._stepSize );
4524   _pyDump->Pause();
4525
4526   findCollisionEdges( data, helper );
4527
4528   limitMaxLenByCurvature( data, helper );
4529
4530   _pyDump->Resume();
4531
4532   // limit length of _LayerEdge's around MULTI_NORMAL _LayerEdge's
4533   for ( size_t i = 0; i < data._edgesOnShape.size(); ++i )
4534     if ( data._edgesOnShape[i].ShapeType() == TopAbs_VERTEX &&
4535          data._edgesOnShape[i]._edges.size() > 0 &&
4536          data._edgesOnShape[i]._edges[0]->Is( _LayerEdge::MULTI_NORMAL ))
4537     {
4538       data._edgesOnShape[i]._edges[0]->Unset( _LayerEdge::BLOCKED );
4539       data._edgesOnShape[i]._edges[0]->Block( data );
4540     }
4541
4542   const double safeFactor = ( 2*data._maxThickness < data._geomSize ) ? 1 : theThickToIntersection;
4543
4544   double avgThick = 0, curThick = 0, distToIntersection = Precision::Infinite();
4545   int nbSteps = 0, nbRepeats = 0;
4546   while ( avgThick < 0.99 )
4547   {
4548     // new target length
4549     double prevThick = curThick;
4550     curThick += data._stepSize;
4551     if ( curThick > tgtThick )
4552     {
4553       curThick = tgtThick + tgtThick*( 1.-avgThick ) * nbRepeats;
4554       nbRepeats++;
4555     }
4556
4557     double stepSize = curThick - prevThick;
4558     updateNormalsOfSmoothed( data, helper, nbSteps, stepSize ); // to ease smoothing
4559
4560     // Elongate _LayerEdge's
4561     dumpFunction(SMESH_Comment("inflate")<<data._index<<"_step"<<nbSteps); // debug
4562     for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4563     {
4564       _EdgesOnShape& eos = data._edgesOnShape[iS];
4565       if ( eos._edges.empty() ) continue;
4566
4567       const double shapeCurThick = Min( curThick, eos._hyp.GetTotalThickness() );
4568       for ( size_t i = 0; i < eos._edges.size(); ++i )
4569       {
4570         eos._edges[i]->SetNewLength( shapeCurThick, eos, helper );
4571       }
4572     }
4573     dumpFunctionEnd();
4574
4575     if ( !updateNormals( data, helper, nbSteps, stepSize )) // to avoid collisions
4576       return false;
4577
4578     // Improve and check quality
4579     if ( !smoothAndCheck( data, nbSteps, distToIntersection ))
4580     {
4581       if ( nbSteps > 0 )
4582       {
4583 #ifdef __NOT_INVALIDATE_BAD_SMOOTH
4584         debugMsg("NOT INVALIDATED STEP!");
4585         return error("Smoothing failed", data._index);
4586 #endif
4587         dumpFunction(SMESH_Comment("invalidate")<<data._index<<"_step"<<nbSteps); // debug
4588         for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4589         {
4590           _EdgesOnShape& eos = data._edgesOnShape[iS];
4591           for ( size_t i = 0; i < eos._edges.size(); ++i )
4592             eos._edges[i]->InvalidateStep( nbSteps+1, eos );
4593         }
4594         dumpFunctionEnd();
4595       }
4596       break; // no more inflating possible
4597     }
4598     nbSteps++;
4599
4600     // Evaluate achieved thickness
4601     avgThick = 0;
4602     int nbActiveEdges = 0;
4603     for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4604     {
4605       _EdgesOnShape& eos = data._edgesOnShape[iS];
4606       if ( eos._edges.empty() ) continue;
4607
4608       const double shapeTgtThick = eos._hyp.GetTotalThickness();
4609       for ( size_t i = 0; i < eos._edges.size(); ++i )
4610       {
4611         if ( eos._edges[i]->_nodes.size() > 1 )
4612           avgThick    += Min( 1., eos._edges[i]->_len / shapeTgtThick );
4613         else
4614           avgThick    += shapeTgtThick;
4615         nbActiveEdges += ( ! eos._edges[i]->Is( _LayerEdge::BLOCKED ));
4616       }
4617     }
4618     avgThick /= data._n2eMap.size();
4619     debugMsg( "-- Thickness " << curThick << " ("<< avgThick*100 << "%) reached" );
4620
4621 #ifdef BLOCK_INFLATION
4622     if ( nbActiveEdges == 0 )
4623     {
4624       debugMsg( "-- Stop inflation since all _LayerEdge's BLOCKED " );
4625       break;
4626     }
4627 #else
4628     if ( distToIntersection < tgtThick * avgThick * safeFactor && avgThick < 0.9 )
4629     {
4630       debugMsg( "-- Stop inflation since "
4631                 << " distToIntersection( "<<distToIntersection<<" ) < avgThick( "
4632                 << tgtThick * avgThick << " ) * " << safeFactor );
4633       break;
4634     }
4635 #endif
4636
4637     // new step size
4638     limitStepSize( data, 0.25 * distToIntersection );
4639     if ( data._stepSizeNodes[0] )
4640       data._stepSize = data._stepSizeCoeff *
4641         SMESH_TNodeXYZ(data._stepSizeNodes[0]).Distance(data._stepSizeNodes[1]);
4642
4643   } // while ( avgThick < 0.99 )
4644
4645   if ( nbSteps == 0 )
4646     return error("failed at the very first inflation step", data._index);
4647
4648   if ( avgThick < 0.99 )
4649   {
4650     if ( !data._proxyMesh->_warning || data._proxyMesh->_warning->IsOK() )
4651     {
4652       data._proxyMesh->_warning.reset
4653         ( new SMESH_ComputeError (COMPERR_WARNING,
4654                                   SMESH_Comment("Thickness ") << tgtThick <<
4655                                   " of viscous layers not reached,"
4656                                   " average reached thickness is " << avgThick*tgtThick));
4657     }
4658   }
4659
4660   // Restore position of src nodes moved by inflation on _noShrinkShapes
4661   dumpFunction(SMESH_Comment("restoNoShrink_So")<<data._index); // debug
4662   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4663   {
4664     _EdgesOnShape& eos = data._edgesOnShape[iS];
4665     if ( !eos._edges.empty() && eos._edges[0]->_nodes.size() == 1 )
4666       for ( size_t i = 0; i < eos._edges.size(); ++i )
4667       {
4668         restoreNoShrink( *eos._edges[ i ] );
4669       }
4670   }
4671   dumpFunctionEnd();
4672
4673   return safeFactor > 0; // == true (avoid warning: unused variable 'safeFactor')
4674 }
4675
4676 //================================================================================
4677 /*!
4678  * \brief Improve quality of layer inner surface and check intersection
4679  */
4680 //================================================================================
4681
4682 bool _ViscousBuilder::smoothAndCheck(_SolidData& data,
4683                                      const int   infStep,
4684                                      double &    distToIntersection)
4685 {
4686   if ( data._nbShapesToSmooth == 0 )
4687     return true; // no shapes needing smoothing
4688
4689   bool moved, improved;
4690   double vol;
4691   vector< _LayerEdge* >    movedEdges, badEdges;
4692   vector< _EdgesOnShape* > eosC1; // C1 continues shapes
4693   vector< bool >           isConcaveFace;
4694
4695   SMESH_MesherHelper helper(*_mesh);
4696   Handle(ShapeAnalysis_Surface) surface;
4697   TopoDS_Face F;
4698
4699   for ( int isFace = 0; isFace < 2; ++isFace ) // smooth on [ EDGEs, FACEs ]
4700   {
4701     const TopAbs_ShapeEnum shapeType = isFace ? TopAbs_FACE : TopAbs_EDGE;
4702
4703     for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4704     {
4705       _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4706       if ( !eos._toSmooth ||
4707            eos.ShapeType() != shapeType ||
4708            eos._edges.empty() )
4709         continue;
4710
4711       // already smoothed?
4712       // bool toSmooth = ( eos._edges[ 0 ]->NbSteps() >= infStep+1 );
4713       // if ( !toSmooth ) continue;
4714
4715       if ( !eos._hyp.ToSmooth() )
4716       {
4717         // smooth disabled by the user; check validy only
4718         if ( !isFace ) continue;
4719         badEdges.clear();
4720         for ( size_t i = 0; i < eos._edges.size(); ++i )
4721         {
4722           _LayerEdge* edge = eos._edges[i];
4723           for ( size_t iF = 0; iF < edge->_simplices.size(); ++iF )
4724             if ( !edge->_simplices[iF].IsForward( edge->_nodes[0], edge->_pos.back(), vol ))
4725             {
4726               // debugMsg( "-- Stop inflation. Bad simplex ("
4727               //           << " "<< edge->_nodes[0]->GetID()
4728               //           << " "<< edge->_nodes.back()->GetID()
4729               //           << " "<< edge->_simplices[iF]._nPrev->GetID()
4730               //           << " "<< edge->_simplices[iF]._nNext->GetID() << " ) ");
4731               // return false;
4732               badEdges.push_back( edge );
4733             }
4734         }
4735         if ( !badEdges.empty() )
4736         {
4737           eosC1.resize(1);
4738           eosC1[0] = &eos;
4739           int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
4740           if ( nbBad > 0 )
4741             return false;
4742         }
4743         continue; // goto the next EDGE or FACE
4744       }
4745
4746       // prepare data
4747       if ( eos.SWOLType() == TopAbs_FACE )
4748       {
4749         if ( !F.IsSame( eos._sWOL )) {
4750           F = TopoDS::Face( eos._sWOL );
4751           helper.SetSubShape( F );
4752           surface = helper.GetSurface( F );
4753         }
4754       }
4755       else
4756       {
4757         F.Nullify(); surface.Nullify();
4758       }
4759       const TGeomID sInd = eos._shapeID;
4760
4761       // perform smoothing
4762
4763       if ( eos.ShapeType() == TopAbs_EDGE )
4764       {
4765         dumpFunction(SMESH_Comment("smooth")<<data._index << "_Ed"<<sInd <<"_InfStep"<<infStep);
4766
4767         if ( !eos._edgeSmoother->Perform( data, surface, F, helper ))
4768         {
4769           // smooth on EDGE's (normally we should not get here)
4770           int step = 0;
4771           do {
4772             moved = false;
4773             for ( size_t i = 0; i < eos._edges.size(); ++i )
4774             {
4775               moved |= eos._edges[i]->SmoothOnEdge( surface, F, helper );
4776             }
4777             dumpCmd( SMESH_Comment("# end step ")<<step);
4778           }
4779           while ( moved && step++ < 5 );
4780         }
4781         dumpFunctionEnd();
4782       }
4783
4784       else // smooth on FACE
4785       {
4786         eosC1.clear();
4787         eosC1.push_back( & eos );
4788         eosC1.insert( eosC1.end(), eos._eosC1.begin(), eos._eosC1.end() );
4789
4790         movedEdges.clear();
4791         isConcaveFace.resize( eosC1.size() );
4792         for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
4793         {
4794           isConcaveFace[ iEOS ] = data._concaveFaces.count( eosC1[ iEOS ]->_shapeID  );
4795           vector< _LayerEdge* > & edges = eosC1[ iEOS ]->_edges;
4796           for ( size_t i = 0; i < edges.size(); ++i )
4797             if ( edges[i]->Is( _LayerEdge::MOVED ) ||
4798                  edges[i]->Is( _LayerEdge::NEAR_BOUNDARY ))
4799               movedEdges.push_back( edges[i] );
4800
4801           makeOffsetSurface( *eosC1[ iEOS ], helper );
4802         }
4803
4804         int step = 0, stepLimit = 5, nbBad = 0;
4805         while (( ++step <= stepLimit ) || improved )
4806         {
4807           dumpFunction(SMESH_Comment("smooth")<<data._index<<"_Fa"<<sInd
4808                        <<"_InfStep"<<infStep<<"_"<<step); // debug
4809           int oldBadNb = nbBad;
4810           badEdges.clear();
4811
4812 #ifdef INCREMENTAL_SMOOTH
4813           bool findBest = false; // ( step == stepLimit );
4814           for ( size_t i = 0; i < movedEdges.size(); ++i )
4815           {
4816             movedEdges[i]->Unset( _LayerEdge::SMOOTHED );
4817             if ( movedEdges[i]->Smooth( step, findBest, movedEdges ) > 0 )
4818               badEdges.push_back( movedEdges[i] );
4819           }
4820 #else
4821           bool findBest = ( step == stepLimit || isConcaveFace[ iEOS ]);
4822           for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
4823           {
4824             vector< _LayerEdge* > & edges = eosC1[ iEOS ]->_edges;
4825             for ( size_t i = 0; i < edges.size(); ++i )
4826             {
4827               edges[i]->Unset( _LayerEdge::SMOOTHED );
4828               if ( edges[i]->Smooth( step, findBest, false ) > 0 )
4829                 badEdges.push_back( eos._edges[i] );
4830             }
4831           }
4832 #endif
4833           nbBad = badEdges.size();
4834
4835           if ( nbBad > 0 )
4836             debugMsg(SMESH_Comment("nbBad = ") << nbBad );
4837
4838           if ( !badEdges.empty() && step >= stepLimit / 2 )
4839           {
4840             if ( badEdges[0]->Is( _LayerEdge::ON_CONCAVE_FACE ))
4841               stepLimit = 9;
4842
4843             // resolve hard smoothing situation around concave VERTEXes
4844             for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
4845             {
4846               vector< _EdgesOnShape* > & eosCoVe = eosC1[ iEOS ]->_eosConcaVer;
4847               for ( size_t i = 0; i < eosCoVe.size(); ++i )
4848                 eosCoVe[i]->_edges[0]->MoveNearConcaVer( eosCoVe[i], eosC1[ iEOS ],
4849                                                          step, badEdges );
4850             }
4851             // look for the best smooth of _LayerEdge's neighboring badEdges
4852             nbBad = 0;
4853             for ( size_t i = 0; i < badEdges.size(); ++i )
4854             {
4855               _LayerEdge* ledge = badEdges[i];
4856               for ( size_t iN = 0; iN < ledge->_neibors.size(); ++iN )
4857               {
4858                 ledge->_neibors[iN]->Unset( _LayerEdge::SMOOTHED );
4859                 nbBad += ledge->_neibors[iN]->Smooth( step, true, /*findBest=*/true );
4860               }
4861               ledge->Unset( _LayerEdge::SMOOTHED );
4862               nbBad += ledge->Smooth( step, true, /*findBest=*/true );
4863             }
4864             debugMsg(SMESH_Comment("nbBad = ") << nbBad );
4865           }
4866
4867           if ( nbBad == oldBadNb  &&
4868                nbBad > 0 &&
4869                step < stepLimit ) // smooth w/o chech of validity
4870           {
4871             dumpFunctionEnd();
4872             dumpFunction(SMESH_Comment("smoothWoCheck")<<data._index<<"_Fa"<<sInd
4873                          <<"_InfStep"<<infStep<<"_"<<step); // debug
4874             for ( size_t i = 0; i < movedEdges.size(); ++i )
4875             {
4876               movedEdges[i]->SmoothWoCheck();
4877             }
4878             if ( stepLimit < 9 )
4879               stepLimit++;
4880           }
4881
4882           improved = ( nbBad < oldBadNb );
4883
4884           dumpFunctionEnd();
4885
4886           if (( step % 3 == 1 ) || ( nbBad > 0 && step >= stepLimit / 2 ))
4887             for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
4888             {
4889               putOnOffsetSurface( *eosC1[ iEOS ], infStep, eosC1, step, /*moveAll=*/step == 1 );
4890             }
4891
4892         } // smoothing steps
4893
4894         // project -- to prevent intersections or fix bad simplices
4895         for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
4896         {
4897           if ( ! eosC1[ iEOS ]->_eosConcaVer.empty() || nbBad > 0 )
4898             putOnOffsetSurface( *eosC1[ iEOS ], infStep, eosC1 );
4899         }
4900
4901         //if ( !badEdges.empty() )
4902         {
4903           badEdges.clear();
4904           for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
4905           {
4906             for ( size_t i = 0; i < eosC1[ iEOS ]->_edges.size(); ++i )
4907             {
4908               if ( !eosC1[ iEOS ]->_sWOL.IsNull() ) continue;
4909
4910               _LayerEdge* edge = eosC1[ iEOS ]->_edges[i];
4911               edge->CheckNeiborsOnBoundary( & badEdges );
4912               if (( nbBad > 0 ) ||
4913                   ( edge->Is( _LayerEdge::BLOCKED ) && edge->Is( _LayerEdge::NEAR_BOUNDARY )))
4914               {
4915                 SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
4916                 gp_XYZ        prevXYZ = edge->PrevCheckPos();
4917                 for ( size_t j = 0; j < edge->_simplices.size(); ++j )
4918                   if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
4919                   {
4920                     debugMsg("Bad simplex ( " << edge->_nodes[0]->GetID()
4921                              << " "<< tgtXYZ._node->GetID()
4922                              << " "<< edge->_simplices[j]._nPrev->GetID()
4923                              << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
4924                     badEdges.push_back( edge );
4925                     break;
4926                   }
4927               }
4928             }
4929           }
4930
4931           // try to fix bad simplices by removing the last inflation step of some _LayerEdge's
4932           nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
4933
4934           if ( nbBad > 0 )
4935             return false;
4936         }
4937
4938       } // // smooth on FACE's
4939     } // loop on shapes
4940   } // smooth on [ EDGEs, FACEs ]
4941
4942   // Check orientation of simplices of _LayerEdge's on EDGEs and VERTEXes
4943   eosC1.resize(1);
4944   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
4945   {
4946     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
4947     if ( eos.ShapeType() == TopAbs_FACE ||
4948          eos._edges.empty() ||
4949          !eos._sWOL.IsNull() )
4950       continue;
4951
4952     badEdges.clear();
4953     for ( size_t i = 0; i < eos._edges.size(); ++i )
4954     {
4955       _LayerEdge*      edge = eos._edges[i];
4956       if ( edge->_nodes.size() < 2 ) continue;
4957       SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
4958       //SMESH_TNodeXYZ prevXYZ = edge->_nodes[0];
4959       gp_XYZ        prevXYZ = edge->PrevCheckPos( &eos );
4960       //const gp_XYZ& prevXYZ = edge->PrevPos();
4961       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
4962         if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
4963         {
4964           debugMsg("Bad simplex on bnd ( " << edge->_nodes[0]->GetID()
4965                    << " "<< tgtXYZ._node->GetID()
4966                    << " "<< edge->_simplices[j]._nPrev->GetID()
4967                    << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
4968           badEdges.push_back( edge );
4969           break;
4970         }
4971     }
4972
4973     // try to fix bad simplices by removing the last inflation step of some _LayerEdge's
4974     eosC1[0] = &eos;
4975     int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
4976     if ( nbBad > 0 )
4977       return false;
4978   }
4979
4980
4981   // Check if the last segments of _LayerEdge intersects 2D elements;
4982   // checked elements are either temporary faces or faces on surfaces w/o the layers
4983
4984   SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
4985     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
4986                                            data._proxyMesh->GetFaces( data._solid )) );
4987
4988 #ifdef BLOCK_INFLATION
4989   const bool toBlockInfaltion = true;
4990 #else
4991   const bool toBlockInfaltion = false;
4992 #endif
4993   distToIntersection = Precision::Infinite();
4994   double dist;
4995   const SMDS_MeshElement* intFace = 0;
4996   const SMDS_MeshElement* closestFace = 0;
4997   _LayerEdge* le = 0;
4998   bool is1stBlocked = true; // dbg
4999   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
5000   {
5001     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
5002     if ( eos._edges.empty() || !eos._sWOL.IsNull() )
5003       continue;
5004     for ( size_t i = 0; i < eos._edges.size(); ++i )
5005     {
5006       if ( eos._edges[i]->Is( _LayerEdge::INTERSECTED ) ||
5007            eos._edges[i]->Is( _LayerEdge::MULTI_NORMAL ))
5008         continue;
5009       if ( eos._edges[i]->FindIntersection( *searcher, dist, data._epsilon, eos, &intFace ))
5010       {
5011         return false;
5012         // commented due to "Illegal hash-positionPosition" error in NETGEN
5013         // on Debian60 on viscous_layers_01/B2 case
5014         // Collision; try to deflate _LayerEdge's causing it
5015         // badEdges.clear();
5016         // badEdges.push_back( eos._edges[i] );
5017         // eosC1[0] = & eos;
5018         // int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5019         // if ( nbBad > 0 )
5020         //   return false;
5021
5022         // badEdges.clear();
5023         // if ( _EdgesOnShape* eof = data.GetShapeEdges( intFace->getshapeId() ))
5024         // {
5025         //   if ( const _TmpMeshFace* f = dynamic_cast< const _TmpMeshFace*>( intFace ))
5026         //   {
5027         //     const SMDS_MeshElement* srcFace =
5028         //       eof->_subMesh->GetSubMeshDS()->GetElement( f->getIdInShape() );
5029         //     SMDS_ElemIteratorPtr nIt = srcFace->nodesIterator();
5030         //     while ( nIt->more() )
5031         //     {
5032         //       const SMDS_MeshNode* srcNode = static_cast<const SMDS_MeshNode*>( nIt->next() );
5033         //       TNode2Edge::iterator n2e = data._n2eMap.find( srcNode );
5034         //       if ( n2e != data._n2eMap.end() )
5035         //         badEdges.push_back( n2e->second );
5036         //     }
5037         //     eosC1[0] = eof;
5038         //     nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
5039         //     if ( nbBad > 0 )
5040         //       return false;
5041         //   }
5042         // }
5043         // if ( eos._edges[i]->FindIntersection( *searcher, dist, data._epsilon, eos, &intFace ))
5044         //   return false;
5045         // else
5046         //   continue;
5047       }
5048       if ( !intFace )
5049       {
5050         SMESH_Comment msg("Invalid? normal at node "); msg << eos._edges[i]->_nodes[0]->GetID();
5051         debugMsg( msg );
5052         continue;
5053       }
5054
5055       const bool isShorterDist = ( distToIntersection > dist );
5056       if ( toBlockInfaltion || isShorterDist )
5057       {
5058         // ignore intersection of a _LayerEdge based on a _ConvexFace with a face
5059         // lying on this _ConvexFace
5060         if ( _ConvexFace* convFace = data.GetConvexFace( intFace->getshapeId() ))
5061           if ( convFace->_isTooCurved && convFace->_subIdToEOS.count ( eos._shapeID ))
5062             continue;
5063
5064         // ignore intersection of a _LayerEdge based on a FACE with an element on this FACE
5065         // ( avoid limiting the thickness on the case of issue 22576)
5066         if ( intFace->getshapeId() == eos._shapeID  )
5067           continue;
5068
5069         // ignore intersection with intFace of an adjacent FACE
5070         if ( dist > 0.1 * eos._edges[i]->_len )
5071         {
5072           bool toIgnore = false;
5073           if (  eos._toSmooth )
5074           {
5075             const TopoDS_Shape& S = getMeshDS()->IndexToShape( intFace->getshapeId() );
5076             if ( !S.IsNull() && S.ShapeType() == TopAbs_FACE )
5077             {
5078               TopExp_Explorer sub( eos._shape,
5079                                    eos.ShapeType() == TopAbs_FACE ? TopAbs_EDGE : TopAbs_VERTEX );
5080               for ( ; !toIgnore && sub.More(); sub.Next() )
5081                 // is adjacent - has a common EDGE or VERTEX
5082                 toIgnore = ( helper.IsSubShape( sub.Current(), S ));
5083
5084               if ( toIgnore ) // check angle between normals
5085               {
5086                 gp_XYZ normal;
5087                 if ( SMESH_MeshAlgos::FaceNormal( intFace, normal, /*normalized=*/true ))
5088                   toIgnore  = ( normal * eos._edges[i]->_normal > -0.5 );
5089               }
5090             }
5091           }
5092           if ( !toIgnore ) // check if the edge is a neighbor of intFace
5093           {
5094             for ( size_t iN = 0; !toIgnore &&  iN < eos._edges[i]->_neibors.size(); ++iN )
5095             {
5096               int nInd = intFace->GetNodeIndex( eos._edges[i]->_neibors[ iN ]->_nodes.back() );
5097               toIgnore = ( nInd >= 0 );
5098             }
5099           }
5100           if ( toIgnore )
5101             continue;
5102         }
5103
5104         // intersection not ignored
5105
5106         if ( toBlockInfaltion &&
5107              dist < ( eos._edges[i]->_len * theThickToIntersection ))
5108         {
5109           if ( is1stBlocked ) { is1stBlocked = false; // debug
5110             dumpFunction(SMESH_Comment("blockIntersected") <<data._index<<"_InfStep"<<infStep);
5111           }
5112           eos._edges[i]->Set( _LayerEdge::INTERSECTED ); // not to intersect
5113           eos._edges[i]->Block( data );                  // not to inflate
5114
5115           //if ( _EdgesOnShape* eof = data.GetShapeEdges( intFace->getshapeId() ))
5116           {
5117             // block _LayerEdge's, on top of which intFace is
5118             if ( const _TmpMeshFace* f = dynamic_cast< const _TmpMeshFace*>( intFace ))
5119             {
5120               const SMDS_MeshElement* srcFace = f->_srcFace;
5121               SMDS_ElemIteratorPtr        nIt = srcFace->nodesIterator();
5122               while ( nIt->more() )
5123               {
5124                 const SMDS_MeshNode* srcNode = static_cast<const SMDS_MeshNode*>( nIt->next() );
5125                 TNode2Edge::iterator n2e = data._n2eMap.find( srcNode );
5126                 if ( n2e != data._n2eMap.end() )
5127                   n2e->second->Block( data );
5128               }
5129             }
5130           }
5131         }
5132
5133         if ( isShorterDist )
5134         {
5135           distToIntersection = dist;
5136           le = eos._edges[i];
5137           closestFace = intFace;
5138         }
5139
5140       } // if ( toBlockInfaltion || isShorterDist )
5141     } // loop on eos._edges
5142   } // loop on data._edgesOnShape
5143
5144   if ( !is1stBlocked )
5145     dumpFunctionEnd();
5146
5147   if ( closestFace && le )
5148   {
5149 #ifdef __myDEBUG
5150     SMDS_MeshElement::iterator nIt = closestFace->begin_nodes();
5151     cout << "#Shortest distance: _LayerEdge nodes: tgt " << le->_nodes.back()->GetID()
5152          << " src " << le->_nodes[0]->GetID()<< ", intersection with face ("
5153          << (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()
5154          << ") distance = " << distToIntersection<< endl;
5155 #endif
5156   }
5157
5158   return true;
5159 }
5160
5161 //================================================================================
5162 /*!
5163  * \brief try to fix bad simplices by removing the last inflation step of some _LayerEdge's
5164  *  \param [in,out] badSmooEdges - _LayerEdge's to fix
5165  *  \return int - resulting nb of bad _LayerEdge's
5166  */
5167 //================================================================================
5168
5169 int _ViscousBuilder::invalidateBadSmooth( _SolidData&               data,
5170                                           SMESH_MesherHelper&       helper,
5171                                           vector< _LayerEdge* >&    badSmooEdges,
5172                                           vector< _EdgesOnShape* >& eosC1,
5173                                           const int                 infStep )
5174 {
5175   if ( badSmooEdges.empty() || infStep == 0 ) return 0;
5176
5177   dumpFunction(SMESH_Comment("invalidateBadSmooth")<<"_S"<<eosC1[0]->_shapeID<<"_InfStep"<<infStep);
5178
5179   enum {
5180     INVALIDATED   = _LayerEdge::UNUSED_FLAG,
5181     TO_INVALIDATE = _LayerEdge::UNUSED_FLAG * 2,
5182     ADDED         = _LayerEdge::UNUSED_FLAG * 4
5183   };
5184   data.UnmarkEdges( TO_INVALIDATE & INVALIDATED & ADDED );
5185
5186   double vol;
5187   bool haveInvalidated = true;
5188   while ( haveInvalidated )
5189   {
5190     haveInvalidated = false;
5191     for ( size_t i = 0; i < badSmooEdges.size(); ++i )
5192     {
5193       _LayerEdge*   edge = badSmooEdges[i];
5194       _EdgesOnShape* eos = data.GetShapeEdges( edge );
5195       edge->Set( ADDED );
5196       bool invalidated = false;
5197       if ( edge->Is( TO_INVALIDATE ) && edge->NbSteps() > 1 )
5198       {
5199         edge->InvalidateStep( edge->NbSteps(), *eos, /*restoreLength=*/true );
5200         edge->Block( data );
5201         edge->Set( INVALIDATED );
5202         edge->Unset( TO_INVALIDATE );
5203         invalidated = true;
5204         haveInvalidated = true;
5205       }
5206
5207       // look for _LayerEdge's of bad _simplices
5208       int nbBad = 0;
5209       SMESH_TNodeXYZ tgtXYZ  = edge->_nodes.back();
5210       gp_XYZ        prevXYZ1 = edge->PrevCheckPos( eos );
5211       //const gp_XYZ& prevXYZ2 = edge->PrevPos();
5212       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5213       {
5214         if (( edge->_simplices[j].IsForward( &prevXYZ1, &tgtXYZ, vol ))/* &&
5215             ( &prevXYZ1 == &prevXYZ2 || edge->_simplices[j].IsForward( &prevXYZ2, &tgtXYZ, vol ))*/)
5216           continue;
5217
5218         bool isBad = true;
5219         _LayerEdge* ee[2] = { 0,0 };
5220         for ( size_t iN = 0; iN < edge->_neibors.size() &&   !ee[1]  ; ++iN )
5221           if ( edge->_simplices[j].Includes( edge->_neibors[iN]->_nodes.back() ))
5222             ee[ ee[0] != 0 ] = edge->_neibors[iN];
5223
5224         int maxNbSteps = Max( ee[0]->NbSteps(), ee[1]->NbSteps() );
5225         while ( maxNbSteps > edge->NbSteps() && isBad )
5226         {
5227           --maxNbSteps;
5228           for ( int iE = 0; iE < 2; ++iE )
5229           {
5230             if ( ee[ iE ]->NbSteps() > maxNbSteps &&
5231                  ee[ iE ]->NbSteps() > 1 )
5232             {
5233               _EdgesOnShape* eos = data.GetShapeEdges( ee[ iE ] );
5234               ee[ iE ]->InvalidateStep( ee[ iE ]->NbSteps(), *eos, /*restoreLength=*/true );
5235               ee[ iE ]->Block( data );
5236               ee[ iE ]->Set( INVALIDATED );
5237               haveInvalidated = true;
5238             }
5239           }
5240           if (( edge->_simplices[j].IsForward( &prevXYZ1, &tgtXYZ, vol )) /*&&
5241               ( &prevXYZ1 == &prevXYZ2 || edge->_simplices[j].IsForward( &prevXYZ2, &tgtXYZ, vol ))*/)
5242             isBad = false;
5243         }
5244         nbBad += isBad;
5245         if ( !ee[0]->Is( ADDED )) badSmooEdges.push_back( ee[0] );
5246         if ( !ee[1]->Is( ADDED )) badSmooEdges.push_back( ee[1] );
5247         ee[0]->Set( ADDED );
5248         ee[1]->Set( ADDED );
5249         if ( isBad )
5250         {
5251           ee[0]->Set( TO_INVALIDATE );
5252           ee[1]->Set( TO_INVALIDATE );
5253         }
5254       }
5255
5256       if ( !invalidated &&  nbBad > 0  &&  edge->NbSteps() > 1 )
5257       {
5258         _EdgesOnShape* eos = data.GetShapeEdges( edge );
5259         edge->InvalidateStep( edge->NbSteps(), *eos, /*restoreLength=*/true );
5260         edge->Block( data );
5261         edge->Set( INVALIDATED );
5262         edge->Unset( TO_INVALIDATE );
5263         haveInvalidated = true;
5264       }
5265     } // loop on badSmooEdges
5266   } // while ( haveInvalidated )
5267
5268   // re-smooth on analytical EDGEs
5269   for ( size_t i = 0; i < badSmooEdges.size(); ++i )
5270   {
5271     _LayerEdge* edge = badSmooEdges[i];
5272     if ( !edge->Is( INVALIDATED )) continue;
5273
5274     _EdgesOnShape* eos = data.GetShapeEdges( edge );
5275     if ( eos->ShapeType() == TopAbs_VERTEX )
5276     {
5277       PShapeIteratorPtr eIt = helper.GetAncestors( eos->_shape, *_mesh, TopAbs_EDGE );
5278       while ( const TopoDS_Shape* e = eIt->next() )
5279         if ( _EdgesOnShape* eoe = data.GetShapeEdges( *e ))
5280           if ( eoe->_edgeSmoother && eoe->_edgeSmoother->isAnalytic() )
5281           {
5282             // TopoDS_Face F; Handle(ShapeAnalysis_Surface) surface;
5283             // if ( eoe->SWOLType() == TopAbs_FACE ) {
5284             //   F       = TopoDS::Face( eoe->_sWOL );
5285             //   surface = helper.GetSurface( F );
5286             // }
5287             // eoe->_edgeSmoother->Perform( data, surface, F, helper );
5288             eoe->_edgeSmoother->_anaCurve.Nullify();
5289           }
5290     }
5291   }
5292
5293
5294   // check result of invalidation
5295
5296   int nbBad = 0;
5297   for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
5298   {
5299     for ( size_t i = 0; i < eosC1[ iEOS ]->_edges.size(); ++i )
5300     {
5301       if ( !eosC1[ iEOS ]->_sWOL.IsNull() ) continue;
5302       _LayerEdge*      edge = eosC1[ iEOS ]->_edges[i];
5303       SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
5304       gp_XYZ        prevXYZ = edge->PrevCheckPos( eosC1[ iEOS ]);
5305       for ( size_t j = 0; j < edge->_simplices.size(); ++j )
5306         if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
5307         {
5308           ++nbBad;
5309           debugMsg("Bad simplex remains ( " << edge->_nodes[0]->GetID()
5310                    << " "<< tgtXYZ._node->GetID()
5311                    << " "<< edge->_simplices[j]._nPrev->GetID()
5312                    << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
5313         }
5314     }
5315   }
5316   dumpFunctionEnd();
5317
5318   return nbBad;
5319 }
5320
5321 //================================================================================
5322 /*!
5323  * \brief Create an offset surface
5324  */
5325 //================================================================================
5326
5327 void _ViscousBuilder::makeOffsetSurface( _EdgesOnShape& eos, SMESH_MesherHelper& helper )
5328 {
5329   if ( eos._offsetSurf.IsNull() ||
5330        eos._edgeForOffset == 0 ||
5331        eos._edgeForOffset->Is( _LayerEdge::BLOCKED ))
5332     return;
5333
5334   Handle(ShapeAnalysis_Surface) baseSurface = helper.GetSurface( TopoDS::Face( eos._shape ));
5335
5336   // find offset
5337   gp_Pnt   tgtP = SMESH_TNodeXYZ( eos._edgeForOffset->_nodes.back() );
5338   /*gp_Pnt2d uv=*/baseSurface->ValueOfUV( tgtP, Precision::Confusion() );
5339   double offset = baseSurface->Gap();
5340
5341   eos._offsetSurf.Nullify();
5342
5343   try
5344   {
5345     BRepOffsetAPI_MakeOffsetShape offsetMaker;
5346     offsetMaker.PerformByJoin( eos._shape, -offset, Precision::Confusion() );
5347     if ( !offsetMaker.IsDone() ) return;
5348
5349     TopExp_Explorer fExp( offsetMaker.Shape(), TopAbs_FACE );
5350     if ( !fExp.More() ) return;
5351
5352     TopoDS_Face F = TopoDS::Face( fExp.Current() );
5353     Handle(Geom_Surface) surf = BRep_Tool::Surface( F );
5354     if ( surf.IsNull() ) return;
5355
5356     eos._offsetSurf = new ShapeAnalysis_Surface( surf );
5357   }
5358   catch ( Standard_Failure )
5359   {
5360   }
5361 }
5362
5363 //================================================================================
5364 /*!
5365  * \brief Put nodes of a curved FACE to its offset surface
5366  */
5367 //================================================================================
5368
5369 void _ViscousBuilder::putOnOffsetSurface( _EdgesOnShape&            eos,
5370                                           int                       infStep,
5371                                           vector< _EdgesOnShape* >& eosC1,
5372                                           int                       smooStep,
5373                                           int                       moveAll )
5374 {
5375   _EdgesOnShape * eof = & eos;
5376   if ( eos.ShapeType() != TopAbs_FACE ) // eos is a boundary of C1 FACE, look for the FACE eos
5377   {
5378     eof = 0;
5379     for ( size_t i = 0; i < eosC1.size() && !eof; ++i )
5380     {
5381       if ( eosC1[i]->_offsetSurf.IsNull() ||
5382            eosC1[i]->ShapeType() != TopAbs_FACE ||
5383            eosC1[i]->_edgeForOffset == 0 ||
5384            eosC1[i]->_edgeForOffset->Is( _LayerEdge::BLOCKED ))
5385         continue;
5386       if ( SMESH_MesherHelper::IsSubShape( eos._shape, eosC1[i]->_shape ))
5387         eof = eosC1[i];
5388     }
5389   }
5390   if ( !eof ||
5391        eof->_offsetSurf.IsNull() ||
5392        eof->ShapeType() != TopAbs_FACE ||
5393        eof->_edgeForOffset == 0 ||
5394        eof->_edgeForOffset->Is( _LayerEdge::BLOCKED ))
5395     return;
5396
5397   double preci = BRep_Tool::Tolerance( TopoDS::Face( eof->_shape )), vol;
5398   for ( size_t i = 0; i < eos._edges.size(); ++i )
5399   {
5400     _LayerEdge* edge = eos._edges[i];
5401     edge->Unset( _LayerEdge::MARKED );
5402     if ( edge->Is( _LayerEdge::BLOCKED ) || !edge->_curvature )
5403       continue;
5404     if ( moveAll == _LayerEdge::UPD_NORMAL_CONV )
5405     {
5406       if ( !edge->Is( _LayerEdge::UPD_NORMAL_CONV ))
5407         continue;
5408     }
5409     else if ( !moveAll && !edge->Is( _LayerEdge::MOVED ))
5410       continue;
5411
5412     int nbBlockedAround = 0;
5413     for ( size_t iN = 0; iN < edge->_neibors.size(); ++iN )
5414       nbBlockedAround += edge->_neibors[iN]->Is( _LayerEdge::BLOCKED );
5415     if ( nbBlockedAround > 1 )
5416       continue;
5417
5418     gp_Pnt tgtP = SMESH_TNodeXYZ( edge->_nodes.back() );
5419     gp_Pnt2d uv = eof->_offsetSurf->NextValueOfUV( edge->_curvature->_uv, tgtP, preci );
5420     if ( eof->_offsetSurf->Gap() > edge->_len ) continue; // NextValueOfUV() bug
5421     edge->_curvature->_uv = uv;
5422     if ( eof->_offsetSurf->Gap() < 10 * preci ) continue; // same pos
5423
5424     gp_XYZ  newP = eof->_offsetSurf->Value( uv ).XYZ();
5425     gp_XYZ prevP = edge->PrevCheckPos();
5426     bool      ok = true;
5427     if ( !moveAll )
5428       for ( size_t iS = 0; iS < edge->_simplices.size() && ok; ++iS )
5429       {
5430         ok = edge->_simplices[iS].IsForward( &prevP, &newP, vol );
5431       }
5432     if ( ok )
5433     {
5434       SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( edge->_nodes.back() );
5435       n->setXYZ( newP.X(), newP.Y(), newP.Z());
5436       edge->_pos.back() = newP;
5437
5438       edge->Set( _LayerEdge::MARKED );
5439       if ( moveAll == _LayerEdge::UPD_NORMAL_CONV )
5440       {
5441         edge->_normal = ( newP - prevP ).Normalized();
5442       }
5443     }
5444   }
5445
5446
5447
5448 #ifdef _DEBUG_
5449   // dumpMove() for debug
5450   size_t i = 0;
5451   for ( ; i < eos._edges.size(); ++i )
5452     if ( eos._edges[i]->Is( _LayerEdge::MARKED ))
5453       break;
5454   if ( i < eos._edges.size() )
5455   {
5456     dumpFunction(SMESH_Comment("putOnOffsetSurface_S") << eos._shapeID
5457                  << "_InfStep" << infStep << "_" << smooStep );
5458     for ( ; i < eos._edges.size(); ++i )
5459     {
5460       if ( eos._edges[i]->Is( _LayerEdge::MARKED ))
5461         dumpMove( eos._edges[i]->_nodes.back() );
5462     }
5463     dumpFunctionEnd();
5464   }
5465 #endif
5466
5467   _ConvexFace* cnvFace;
5468   if ( moveAll != _LayerEdge::UPD_NORMAL_CONV &&
5469        eos.ShapeType() == TopAbs_FACE &&
5470        (cnvFace = eos.GetData().GetConvexFace( eos._shapeID )) &&
5471        !cnvFace->_normalsFixedOnBorders )
5472   {
5473     // put on the surface nodes built on FACE boundaries
5474     SMESH_subMeshIteratorPtr smIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false);
5475     while ( smIt->more() )
5476     {
5477       SMESH_subMesh* sm = smIt->next();
5478       _EdgesOnShape* subEOS = eos.GetData().GetShapeEdges( sm->GetId() );
5479       if ( !subEOS->_sWOL.IsNull() ) continue;
5480       if ( std::find( eosC1.begin(), eosC1.end(), subEOS ) != eosC1.end() ) continue;
5481
5482       putOnOffsetSurface( *subEOS, infStep, eosC1, smooStep, _LayerEdge::UPD_NORMAL_CONV );
5483     }
5484     cnvFace->_normalsFixedOnBorders = true;
5485   }
5486 }
5487
5488 //================================================================================
5489 /*!
5490  * \brief Return a curve of the EDGE to be used for smoothing and arrange
5491  *        _LayerEdge's to be in a consequent order
5492  */
5493 //================================================================================
5494
5495 Handle(Geom_Curve) _Smoother1D::CurveForSmooth( const TopoDS_Edge&  E,
5496                                                 _EdgesOnShape&      eos,
5497                                                 SMESH_MesherHelper& helper)
5498 {
5499   SMESHDS_SubMesh* smDS = eos._subMesh->GetSubMeshDS();
5500
5501   TopLoc_Location loc; double f,l;
5502
5503   Handle(Geom_Line)   line;
5504   Handle(Geom_Circle) circle;
5505   bool isLine, isCirc;
5506   if ( eos._sWOL.IsNull() ) /////////////////////////////////////////// 3D case
5507   {
5508     // check if the EDGE is a line
5509     Handle(Geom_Curve) curve = BRep_Tool::Curve( E, f, l);
5510     if ( curve->IsKind( STANDARD_TYPE( Geom_TrimmedCurve )))
5511       curve = Handle(Geom_TrimmedCurve)::DownCast( curve )->BasisCurve();
5512
5513     line   = Handle(Geom_Line)::DownCast( curve );
5514     circle = Handle(Geom_Circle)::DownCast( curve );
5515     isLine = (!line.IsNull());
5516     isCirc = (!circle.IsNull());
5517
5518     if ( !isLine && !isCirc ) // Check if the EDGE is close to a line
5519     {
5520       isLine = SMESH_Algo::IsStraight( E );
5521
5522       if ( isLine )
5523         line = new Geom_Line( gp::OX() ); // only type does matter
5524     }
5525     if ( !isLine && !isCirc && eos._edges.size() > 2) // Check if the EDGE is close to a circle
5526     {
5527       // TODO
5528     }
5529   }
5530   else //////////////////////////////////////////////////////////////////////// 2D case
5531   {
5532     if ( !eos._isRegularSWOL ) // 23190
5533       return NULL;
5534
5535     const TopoDS_Face& F = TopoDS::Face( eos._sWOL );
5536
5537     // check if the EDGE is a line
5538     Handle(Geom2d_Curve) curve = BRep_Tool::CurveOnSurface( E, F, f, l );
5539     if ( curve->IsKind( STANDARD_TYPE( Geom2d_TrimmedCurve )))
5540       curve = Handle(Geom2d_TrimmedCurve)::DownCast( curve )->BasisCurve();
5541
5542     Handle(Geom2d_Line)   line2d   = Handle(Geom2d_Line)::DownCast( curve );
5543     Handle(Geom2d_Circle) circle2d = Handle(Geom2d_Circle)::DownCast( curve );
5544     isLine = (!line2d.IsNull());
5545     isCirc = (!circle2d.IsNull());
5546
5547     if ( !isLine && !isCirc ) // Check if the EDGE is close to a line
5548     {
5549       Bnd_B2d bndBox;
5550       SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
5551       while ( nIt->more() )
5552         bndBox.Add( helper.GetNodeUV( F, nIt->next() ));
5553       gp_XY size = bndBox.CornerMax() - bndBox.CornerMin();
5554
5555       const double lineTol = 1e-2 * sqrt( bndBox.SquareExtent() );
5556       for ( int i = 0; i < 2 && !isLine; ++i )
5557         isLine = ( size.Coord( i+1 ) <= lineTol );
5558     }
5559     if ( !isLine && !isCirc && eos._edges.size() > 2 ) // Check if the EDGE is close to a circle
5560     {
5561       // TODO
5562     }
5563     if ( isLine )
5564     {
5565       line = new Geom_Line( gp::OX() ); // only type does matter
5566     }
5567     else if ( isCirc )
5568     {
5569       gp_Pnt2d p = circle2d->Location();
5570       gp_Ax2 ax( gp_Pnt( p.X(), p.Y(), 0), gp::DX());
5571       circle = new Geom_Circle( ax, 1.); // only center position does matter
5572     }
5573   }
5574
5575   if ( isLine )
5576     return line;
5577   if ( isCirc )
5578     return circle;
5579
5580   return Handle(Geom_Curve)();
5581 }
5582
5583 //================================================================================
5584 /*!
5585  * \brief Smooth edges on EDGE
5586  */
5587 //================================================================================
5588
5589 bool _Smoother1D::Perform(_SolidData&                    data,
5590                           Handle(ShapeAnalysis_Surface)& surface,
5591                           const TopoDS_Face&             F,
5592                           SMESH_MesherHelper&            helper )
5593 {
5594   if ( _leParams.empty() || ( !isAnalytic() && _offPoints.empty() ))
5595     prepare( data );
5596
5597   findEdgesToSmooth();
5598   if ( isAnalytic() )
5599     return smoothAnalyticEdge( data, surface, F, helper );
5600   else
5601     return smoothComplexEdge ( data, surface, F, helper );
5602 }
5603
5604 //================================================================================
5605 /*!
5606  * \brief Find edges to smooth
5607  */
5608 //================================================================================
5609
5610 void _Smoother1D::findEdgesToSmooth()
5611 {
5612   _LayerEdge* leOnV[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
5613   for ( int iEnd = 0; iEnd < 2; ++iEnd )
5614     if ( leOnV[iEnd]->Is( _LayerEdge::NORMAL_UPDATED ))
5615       _leOnV[iEnd]._cosin = Abs( _edgeDir[iEnd].Normalized() * leOnV[iEnd]->_normal );
5616
5617   _eToSmooth[0].first = _eToSmooth[0].second = 0;
5618
5619   for ( size_t i = 0; i < _eos.size(); ++i )
5620   {
5621     if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH ))
5622     {
5623       if ( needSmoothing( _leOnV[0]._cosin,
5624                           _eos[i]->_len * leOnV[0]->_lenFactor, _curveLen * _leParams[i] ) ||
5625            isToSmooth( i )
5626            )
5627         _eos[i]->Set( _LayerEdge::TO_SMOOTH );
5628       else
5629         break;
5630     }
5631     _eToSmooth[0].second = i+1;
5632   }
5633
5634   _eToSmooth[1].first = _eToSmooth[1].second = _eos.size();
5635
5636   for ( int i = _eos.size() - 1; i >= _eToSmooth[0].second; --i )
5637   {
5638     if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH ))
5639     {
5640       if ( needSmoothing( _leOnV[1]._cosin,
5641                           _eos[i]->_len * leOnV[1]->_lenFactor, _curveLen * ( 1.-_leParams[i] )) ||
5642            isToSmooth( i ))
5643         _eos[i]->Set( _LayerEdge::TO_SMOOTH );
5644       else
5645         break;
5646     }
5647     _eToSmooth[1].first = i;
5648   }
5649 }
5650
5651 //================================================================================
5652 /*!
5653  * \brief Check if iE-th _LayerEdge needs smoothing
5654  */
5655 //================================================================================
5656
5657 bool _Smoother1D::isToSmooth( int iE )
5658 {
5659   SMESH_NodeXYZ pi( _eos[iE]->_nodes[0] );
5660   SMESH_NodeXYZ p0( _eos[iE]->_2neibors->srcNode(0) );
5661   SMESH_NodeXYZ p1( _eos[iE]->_2neibors->srcNode(1) );
5662   gp_XYZ       seg0 = pi - p0;
5663   gp_XYZ       seg1 = p1 - pi;
5664   gp_XYZ    tangent =  seg0 + seg1;
5665   double tangentLen = tangent.Modulus();
5666   double  segMinLen = Min( seg0.Modulus(), seg1.Modulus() );
5667   if ( tangentLen < std::numeric_limits<double>::min() )
5668     return false;
5669   tangent /= tangentLen;
5670
5671   for ( size_t i = 0; i < _eos[iE]->_neibors.size(); ++i )
5672   {
5673     _LayerEdge* ne = _eos[iE]->_neibors[i];
5674     if ( !ne->Is( _LayerEdge::TO_SMOOTH ) ||
5675          ne->_nodes.size() < 2 ||
5676          ne->_nodes[0]->GetPosition()->GetDim() != 2 )
5677       continue;
5678     gp_XYZ edgeVec = SMESH_NodeXYZ( ne->_nodes.back() ) - SMESH_NodeXYZ( ne->_nodes[0] );
5679     double    proj = edgeVec * tangent;
5680     if ( needSmoothing( 1., proj, segMinLen ))
5681       return true;
5682   }
5683   return false;
5684 }
5685
5686 //================================================================================
5687 /*!
5688  * \brief smooth _LayerEdge's on a staight EDGE or circular EDGE
5689  */
5690 //================================================================================
5691
5692 bool _Smoother1D::smoothAnalyticEdge( _SolidData&                    data,
5693                                       Handle(ShapeAnalysis_Surface)& surface,
5694                                       const TopoDS_Face&             F,
5695                                       SMESH_MesherHelper&            helper)
5696 {
5697   if ( !isAnalytic() ) return false;
5698
5699   size_t iFrom = 0, iTo = _eos._edges.size();
5700
5701   if ( _anaCurve->IsKind( STANDARD_TYPE( Geom_Line )))
5702   {
5703     if ( F.IsNull() ) // 3D
5704     {
5705       SMESH_TNodeXYZ pSrc0( _eos._edges[iFrom]->_2neibors->srcNode(0) );
5706       SMESH_TNodeXYZ pSrc1( _eos._edges[iTo-1]->_2neibors->srcNode(1) );
5707       //const   gp_XYZ lineDir = pSrc1 - pSrc0;
5708       //_LayerEdge* vLE0 = getLEdgeOnV( 0 );
5709       //_LayerEdge* vLE1 = getLEdgeOnV( 1 );
5710       // bool shiftOnly = ( vLE0->Is( _LayerEdge::NORMAL_UPDATED ) ||
5711       //                    vLE0->Is( _LayerEdge::BLOCKED ) ||
5712       //                    vLE1->Is( _LayerEdge::NORMAL_UPDATED ) ||
5713       //                    vLE1->Is( _LayerEdge::BLOCKED ));
5714       for ( int iEnd = 0; iEnd < 2; ++iEnd )
5715       {
5716         iFrom = _eToSmooth[ iEnd ].first, iTo = _eToSmooth[ iEnd ].second;
5717         if ( iFrom >= iTo ) continue;
5718         SMESH_TNodeXYZ p0( _eos[iFrom]->_2neibors->tgtNode(0) );
5719         SMESH_TNodeXYZ p1( _eos[iTo-1]->_2neibors->tgtNode(1) );
5720         double param0 = ( iFrom == 0 ) ? 0. : _leParams[ iFrom-1 ];
5721         double param1 = _leParams[ iTo ];
5722         for ( size_t i = iFrom; i < iTo; ++i )
5723         {
5724           _LayerEdge*       edge = _eos[i];
5725           SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edge->_nodes.back() );
5726           double           param = ( _leParams[i] - param0 ) / ( param1 - param0 );
5727           gp_XYZ          newPos = p0 * ( 1. - param ) + p1 * param;
5728
5729           // if ( shiftOnly || edge->Is( _LayerEdge::NORMAL_UPDATED ))
5730           // {
5731           //   gp_XYZ curPos = SMESH_TNodeXYZ ( tgtNode );
5732           //   double  shift = ( lineDir * ( newPos - pSrc0 ) -
5733           //                     lineDir * ( curPos - pSrc0 ));
5734           //   newPos = curPos + lineDir * shift / lineDir.SquareModulus();
5735           // }
5736           if ( edge->Is( _LayerEdge::BLOCKED ))
5737           {
5738             SMESH_TNodeXYZ pSrc( edge->_nodes[0] );
5739             double curThick = pSrc.SquareDistance( tgtNode );
5740             double newThink = ( pSrc - newPos ).SquareModulus();
5741             if ( newThink > curThick )
5742               continue;
5743           }
5744           edge->_pos.back() = newPos;
5745           tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
5746           dumpMove( tgtNode );
5747         }
5748       }
5749     }
5750     else // 2D
5751     {
5752       _LayerEdge* eV0 = getLEdgeOnV( 0 );
5753       _LayerEdge* eV1 = getLEdgeOnV( 1 );
5754       gp_XY      uvV0 = eV0->LastUV( F, *data.GetShapeEdges( eV0 ));
5755       gp_XY      uvV1 = eV1->LastUV( F, *data.GetShapeEdges( eV1 ));
5756       if ( eV0->_nodes.back() == eV1->_nodes.back() ) // closed edge
5757       {
5758         int iPeriodic = helper.GetPeriodicIndex();
5759         if ( iPeriodic == 1 || iPeriodic == 2 )
5760         {
5761           uvV1.SetCoord( iPeriodic, helper.GetOtherParam( uvV1.Coord( iPeriodic )));
5762           if ( uvV0.Coord( iPeriodic ) > uvV1.Coord( iPeriodic ))
5763             std::swap( uvV0, uvV1 );
5764         }
5765       }
5766       for ( int iEnd = 0; iEnd < 2; ++iEnd )
5767       {
5768         iFrom = _eToSmooth[ iEnd ].first, iTo = _eToSmooth[ iEnd ].second;
5769         if ( iFrom >= iTo ) continue;
5770         _LayerEdge* e0 = _eos[iFrom]->_2neibors->_edges[0];
5771         _LayerEdge* e1 = _eos[iTo-1]->_2neibors->_edges[1];
5772         gp_XY      uv0 = ( e0 == eV0 ) ? uvV0 : e0->LastUV( F, _eos );
5773         gp_XY      uv1 = ( e1 == eV1 ) ? uvV1 : e1->LastUV( F, _eos );
5774         double  param0 = ( iFrom == 0 ) ? 0. : _leParams[ iFrom-1 ];
5775         double  param1 = _leParams[ iTo ];
5776         gp_XY  rangeUV = uv1 - uv0;
5777         for ( size_t i = iFrom; i < iTo; ++i )
5778         {
5779           if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
5780           double param = ( _leParams[i] - param0 ) / ( param1 - param0 );
5781           gp_XY newUV = uv0 + param * rangeUV;
5782
5783           gp_Pnt newPos = surface->Value( newUV.X(), newUV.Y() );
5784           SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos[i]->_nodes.back() );
5785           tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
5786           dumpMove( tgtNode );
5787
5788           SMDS_FacePositionPtr pos = tgtNode->GetPosition();
5789           pos->SetUParameter( newUV.X() );
5790           pos->SetVParameter( newUV.Y() );
5791
5792           gp_XYZ newUV0( newUV.X(), newUV.Y(), 0 );
5793
5794           if ( !_eos[i]->Is( _LayerEdge::SMOOTHED ))
5795           {
5796             _eos[i]->Set( _LayerEdge::SMOOTHED ); // to check in refine() (IPAL54237)
5797             if ( _eos[i]->_pos.size() > 2 )
5798             {
5799               // modify previous positions to make _LayerEdge less sharply bent
5800               vector<gp_XYZ>& uvVec = _eos[i]->_pos;
5801               const gp_XYZ  uvShift = newUV0 - uvVec.back();
5802               const double     len2 = ( uvVec.back() - uvVec[ 0 ] ).SquareModulus();
5803               int iPrev = uvVec.size() - 2;
5804               while ( iPrev > 0 )
5805               {
5806                 double r = ( uvVec[ iPrev ] - uvVec[0] ).SquareModulus() / len2;
5807                 uvVec[ iPrev ] += uvShift * r;
5808                 --iPrev;
5809               }
5810             }
5811           }
5812           _eos[i]->_pos.back() = newUV0;
5813         }
5814       }
5815     }
5816     return true;
5817   }
5818
5819   if ( _anaCurve->IsKind( STANDARD_TYPE( Geom_Circle )))
5820   {
5821     Handle(Geom_Circle) circle = Handle(Geom_Circle)::DownCast( _anaCurve );
5822     gp_Pnt center3D = circle->Location();
5823
5824     if ( F.IsNull() ) // 3D
5825     {
5826       if ( getLEdgeOnV( 0 )->_nodes.back() == getLEdgeOnV( 1 )->_nodes.back() )
5827         return true; // closed EDGE - nothing to do
5828
5829       // circle is a real curve of EDGE
5830       gp_Circ circ = circle->Circ();
5831
5832       // new center is shifted along its axis
5833       const gp_Dir& axis = circ.Axis().Direction();
5834       _LayerEdge*     e0 = getLEdgeOnV(0);
5835       _LayerEdge*     e1 = getLEdgeOnV(1);
5836       SMESH_TNodeXYZ  p0 = e0->_nodes.back();
5837       SMESH_TNodeXYZ  p1 = e1->_nodes.back();
5838       double      shift1 = axis.XYZ() * ( p0 - center3D.XYZ() );
5839       double      shift2 = axis.XYZ() * ( p1 - center3D.XYZ() );
5840       gp_Pnt   newCenter = center3D.XYZ() + axis.XYZ() * 0.5 * ( shift1 + shift2 );
5841
5842       double newRadius = 0.5 * ( newCenter.Distance( p0 ) + newCenter.Distance( p1 ));
5843
5844       gp_Ax2  newAxis( newCenter, axis, gp_Vec( newCenter, p0 ));
5845       gp_Circ newCirc( newAxis, newRadius );
5846       gp_Vec  vecC1  ( newCenter, p1 );
5847
5848       double uLast = newAxis.XDirection().AngleWithRef( vecC1, newAxis.Direction() ); // -PI - +PI
5849       if ( uLast < 0 )
5850         uLast += 2 * M_PI;
5851       
5852       for ( size_t i = 0; i < _eos.size(); ++i )
5853       {
5854         if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
5855         //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
5856         double u = uLast * _leParams[i];
5857         gp_Pnt p = ElCLib::Value( u, newCirc );
5858         _eos._edges[i]->_pos.back() = p.XYZ();
5859
5860         SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos._edges[i]->_nodes.back() );
5861         tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
5862         dumpMove( tgtNode );
5863       }
5864       return true;
5865     }
5866     else // 2D
5867     {
5868       const gp_XY center( center3D.X(), center3D.Y() );
5869
5870       _LayerEdge* e0 = getLEdgeOnV(0);
5871       _LayerEdge* eM = _eos._edges[ 0 ];
5872       _LayerEdge* e1 = getLEdgeOnV(1);
5873       gp_XY      uv0 = e0->LastUV( F, *data.GetShapeEdges( e0 ) );
5874       gp_XY      uvM = eM->LastUV( F, *data.GetShapeEdges( eM ) );
5875       gp_XY      uv1 = e1->LastUV( F, *data.GetShapeEdges( e1 ) );
5876       gp_Vec2d vec0( center, uv0 );
5877       gp_Vec2d vecM( center, uvM );
5878       gp_Vec2d vec1( center, uv1 );
5879       double uLast = vec0.Angle( vec1 ); // -PI - +PI
5880       double uMidl = vec0.Angle( vecM );
5881       if ( uLast * uMidl <= 0. )
5882         uLast += ( uMidl > 0 ? +2. : -2. ) * M_PI;
5883       const double radius = 0.5 * ( vec0.Magnitude() + vec1.Magnitude() );
5884
5885       gp_Ax2d   axis( center, vec0 );
5886       gp_Circ2d circ( axis, radius );
5887       for ( size_t i = 0; i < _eos.size(); ++i )
5888       {
5889         if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
5890         //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
5891         double    newU = uLast * _leParams[i];
5892         gp_Pnt2d newUV = ElCLib::Value( newU, circ );
5893         _eos._edges[i]->_pos.back().SetCoord( newUV.X(), newUV.Y(), 0 );
5894
5895         gp_Pnt newPos = surface->Value( newUV.X(), newUV.Y() );
5896         SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos._edges[i]->_nodes.back() );
5897         tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
5898         dumpMove( tgtNode );
5899
5900         SMDS_FacePositionPtr pos = tgtNode->GetPosition();
5901         pos->SetUParameter( newUV.X() );
5902         pos->SetVParameter( newUV.Y() );
5903
5904         _eos[i]->Set( _LayerEdge::SMOOTHED ); // to check in refine() (IPAL54237)
5905       }
5906     }
5907     return true;
5908   }
5909
5910   return false;
5911 }
5912
5913 //================================================================================
5914 /*!
5915  * \brief smooth _LayerEdge's on a an EDGE
5916  */
5917 //================================================================================
5918
5919 bool _Smoother1D::smoothComplexEdge( _SolidData&                    data,
5920                                      Handle(ShapeAnalysis_Surface)& surface,
5921                                      const TopoDS_Face&             F,
5922                                      SMESH_MesherHelper&            helper)
5923 {
5924   if ( _offPoints.empty() )
5925     return false;
5926
5927   // ----------------------------------------------
5928   // move _offPoints along normals of _LayerEdge's
5929   // ----------------------------------------------
5930
5931   _LayerEdge* e[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
5932   if ( e[0]->Is( _LayerEdge::NORMAL_UPDATED ))
5933     _leOnV[0]._normal = getNormalNormal( e[0]->_normal, _edgeDir[0] );
5934   if ( e[1]->Is( _LayerEdge::NORMAL_UPDATED )) 
5935     _leOnV[1]._normal = getNormalNormal( e[1]->_normal, _edgeDir[1] );
5936   _leOnV[0]._len = e[0]->_len;
5937   _leOnV[1]._len = e[1]->_len;
5938   for ( size_t i = 0; i < _offPoints.size(); i++ )
5939   {
5940     _LayerEdge*  e0 = _offPoints[i]._2edges._edges[0];
5941     _LayerEdge*  e1 = _offPoints[i]._2edges._edges[1];
5942     const double w0 = _offPoints[i]._2edges._wgt[0];
5943     const double w1 = _offPoints[i]._2edges._wgt[1];
5944     gp_XYZ  avgNorm = ( e0->_normal    * w0 + e1->_normal    * w1 ).Normalized();
5945     double  avgLen  = ( e0->_len       * w0 + e1->_len       * w1 );
5946     double  avgFact = ( e0->_lenFactor * w0 + e1->_lenFactor * w1 );
5947     if ( e0->Is( _LayerEdge::NORMAL_UPDATED ) ||
5948          e1->Is( _LayerEdge::NORMAL_UPDATED ))
5949       avgNorm = getNormalNormal( avgNorm, _offPoints[i]._edgeDir );
5950
5951     _offPoints[i]._xyz += avgNorm * ( avgLen - _offPoints[i]._len ) * avgFact;
5952     _offPoints[i]._len  = avgLen;
5953   }
5954
5955   double fTol = 0;
5956   if ( !surface.IsNull() ) // project _offPoints to the FACE
5957   {
5958     fTol = 100 * BRep_Tool::Tolerance( F );
5959     //const double segLen = _offPoints[0].Distance( _offPoints[1] );
5960
5961     gp_Pnt2d uv = surface->ValueOfUV( _offPoints[0]._xyz, fTol );
5962     //if ( surface->Gap() < 0.5 * segLen )
5963       _offPoints[0]._xyz = surface->Value( uv ).XYZ();
5964
5965     for ( size_t i = 1; i < _offPoints.size(); ++i )
5966     {
5967       uv = surface->NextValueOfUV( uv, _offPoints[i]._xyz, fTol );
5968       //if ( surface->Gap() < 0.5 * segLen )
5969         _offPoints[i]._xyz = surface->Value( uv ).XYZ();
5970     }
5971   }
5972
5973   // -----------------------------------------------------------------
5974   // project tgt nodes of extreme _LayerEdge's to the offset segments
5975   // -----------------------------------------------------------------
5976
5977   const int updatedOrBlocked = _LayerEdge::NORMAL_UPDATED | _LayerEdge::BLOCKED;
5978   if ( e[0]->Is( updatedOrBlocked )) _iSeg[0] = 0;
5979   if ( e[1]->Is( updatedOrBlocked )) _iSeg[1] = _offPoints.size()-2;
5980
5981   gp_Pnt pExtreme[2], pProj[2];
5982   bool isProjected[2];
5983   for ( int is2nd = 0; is2nd < 2; ++is2nd )
5984   {
5985     pExtreme[ is2nd ] = SMESH_TNodeXYZ( e[is2nd]->_nodes.back() );
5986     int  i = _iSeg[ is2nd ];
5987     int di = is2nd ? -1 : +1;
5988     bool & projected = isProjected[ is2nd ];
5989     projected = false;
5990     double uOnSeg, distMin = Precision::Infinite(), dist, distPrev = 0;
5991     int nbWorse = 0;
5992     do {
5993       gp_Vec v0p( _offPoints[i]._xyz, pExtreme[ is2nd ]    );
5994       gp_Vec v01( _offPoints[i]._xyz, _offPoints[i+1]._xyz );
5995       uOnSeg     = ( v0p * v01 ) / v01.SquareMagnitude();  // param [0,1] along v01
5996       projected  = ( Abs( uOnSeg - 0.5 ) <= 0.5 );
5997       dist       =  pExtreme[ is2nd ].SquareDistance( _offPoints[ i + ( uOnSeg > 0.5 )]._xyz );
5998       if ( dist < distMin || projected )
5999       {
6000         _iSeg[ is2nd ] = i;
6001         pProj[ is2nd ] = _offPoints[i]._xyz + ( v01 * uOnSeg ).XYZ();
6002         distMin = dist;
6003       }
6004       else if ( dist > distPrev )
6005       {
6006         if ( ++nbWorse > 3 ) // avoid projection to the middle of a closed EDGE
6007           break;
6008       }
6009       distPrev = dist;
6010       i += di;
6011     }
6012     while ( !projected &&
6013             i >= 0 && i+1 < (int)_offPoints.size() );
6014
6015     if ( !projected )
6016     {
6017       if (( is2nd && _iSeg[1] != _offPoints.size()-2 ) || ( !is2nd && _iSeg[0] != 0 ))
6018       {
6019         _iSeg[0] = 0;
6020         _iSeg[1] = _offPoints.size()-2;
6021         debugMsg( "smoothComplexEdge() failed to project nodes of extreme _LayerEdge's" );
6022         return false;
6023       }
6024     }
6025   }
6026   if ( _iSeg[0] > _iSeg[1] )
6027   {
6028     debugMsg( "smoothComplexEdge() incorrectly projected nodes of extreme _LayerEdge's" );
6029     return false;
6030   }
6031
6032   // adjust length of extreme LE (test viscous_layers_01/B7)
6033   gp_Vec vDiv0( pExtreme[0], pProj[0] );
6034   gp_Vec vDiv1( pExtreme[1], pProj[1] );
6035   double d0 = vDiv0.Magnitude();
6036   double d1 = isProjected[1] ? vDiv1.Magnitude() : 0;
6037   if ( e[0]->Is( _LayerEdge::BLOCKED )) {
6038     if ( e[0]->_normal * vDiv0.XYZ() < 0 ) e[0]->_len += d0;
6039     else                                   e[0]->_len -= d0;
6040   }
6041   if ( e[1]->Is( _LayerEdge::BLOCKED )) {
6042     if ( e[1]->_normal * vDiv1.XYZ() < 0 ) e[1]->_len += d1;
6043     else                                   e[1]->_len -= d1;
6044   }
6045
6046   // ---------------------------------------------------------------------------------
6047   // compute normalized length of the offset segments located between the projections
6048   // ---------------------------------------------------------------------------------
6049
6050   // temporary replace extreme _offPoints by pExtreme
6051   gp_XYZ opXYZ[2] = { _offPoints[ _iSeg[0]   ]._xyz,
6052                       _offPoints[ _iSeg[1]+1 ]._xyz };
6053   _offPoints[ _iSeg[0]   ]._xyz = pExtreme[0].XYZ();
6054   _offPoints[ _iSeg[1]+ 1]._xyz = pExtreme[1].XYZ();
6055
6056   size_t iSeg = 0, nbSeg = _iSeg[1] - _iSeg[0] + 1;
6057   vector< double > len( nbSeg + 1 );
6058   len[ iSeg++ ] = 0;
6059   len[ iSeg++ ] = pProj[ 0 ].Distance( _offPoints[ _iSeg[0]+1 ]._xyz );
6060   for ( size_t i = _iSeg[0]+1; i <= _iSeg[1]; ++i, ++iSeg )
6061   {
6062     len[ iSeg ] = len[ iSeg-1 ] + _offPoints[i].Distance( _offPoints[i+1] );
6063   }
6064   // if ( isProjected[ 1 ])
6065   //   len[ nbSeg ] -= pProj[ 1 ].Distance( _offPoints[ _iSeg[1]+1 ]._xyz );
6066   // else
6067   //   len[ nbSeg ] += pExtreme[ 1 ].Distance( _offPoints[ _iSeg[1]+1 ]._xyz );
6068
6069   double fullLen = len.back() - d0 - d1;
6070   for ( iSeg = 0; iSeg < len.size(); ++iSeg )
6071     len[iSeg] = ( len[iSeg] - d0 ) / fullLen;
6072
6073   // -------------------------------------------------------------
6074   // distribute tgt nodes of _LayerEdge's between the projections
6075   // -------------------------------------------------------------
6076
6077   iSeg = 0;
6078   for ( size_t i = 0; i < _eos.size(); ++i )
6079   {
6080     if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
6081     //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
6082     while ( iSeg+2 < len.size() && _leParams[i] > len[ iSeg+1 ] )
6083       iSeg++;
6084     double r = ( _leParams[i] - len[ iSeg ]) / ( len[ iSeg+1 ] - len[ iSeg ]);
6085     gp_XYZ p = ( _offPoints[ iSeg + _iSeg[0]     ]._xyz * ( 1 - r ) +
6086                  _offPoints[ iSeg + _iSeg[0] + 1 ]._xyz * r );
6087
6088     if ( surface.IsNull() )
6089     {
6090       _eos[i]->_pos.back() = p;
6091     }
6092     else // project a new node position to a FACE
6093     {
6094       gp_Pnt2d uv ( _eos[i]->_pos.back().X(), _eos[i]->_pos.back().Y() );
6095       gp_Pnt2d uv2( surface->NextValueOfUV( uv, p, fTol ));
6096
6097       p = surface->Value( uv2 ).XYZ();
6098       _eos[i]->_pos.back().SetCoord( uv2.X(), uv2.Y(), 0 );
6099     }
6100     SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos[i]->_nodes.back() );
6101     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
6102     dumpMove( tgtNode );
6103   }
6104
6105   _offPoints[ _iSeg[0]   ]._xyz = opXYZ[0];
6106   _offPoints[ _iSeg[1]+1 ]._xyz = opXYZ[1];
6107
6108   return true;
6109 }
6110
6111 //================================================================================
6112 /*!
6113  * \brief Prepare for smoothing
6114  */
6115 //================================================================================
6116
6117 void _Smoother1D::prepare(_SolidData& data)
6118 {
6119   const TopoDS_Edge& E = TopoDS::Edge( _eos._shape );
6120   _curveLen = SMESH_Algo::EdgeLength( E );
6121
6122   // sort _LayerEdge's by position on the EDGE
6123   data.SortOnEdge( E, _eos._edges );
6124
6125   // compute normalized param of _eos._edges on EDGE
6126   _leParams.resize( _eos._edges.size() + 1 );
6127   {
6128     double curLen;
6129     gp_Pnt pPrev = SMESH_TNodeXYZ( getLEdgeOnV( 0 )->_nodes[0] );
6130     _leParams[0] = 0;
6131     for ( size_t i = 0; i < _eos._edges.size(); ++i )
6132     {
6133       gp_Pnt p       = SMESH_TNodeXYZ( _eos._edges[i]->_nodes[0] );
6134       curLen         = p.Distance( pPrev );
6135       _leParams[i+1] = _leParams[i] + curLen;
6136       pPrev          = p;
6137     }
6138     double fullLen = _leParams.back() + pPrev.Distance( SMESH_TNodeXYZ( getLEdgeOnV(1)->_nodes[0]));
6139     for ( size_t i = 0; i < _leParams.size()-1; ++i )
6140       _leParams[i] = _leParams[i+1] / fullLen;
6141     _leParams.back() = 1.;
6142   }
6143
6144   _LayerEdge* leOnV[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
6145
6146   // get cosin to use in findEdgesToSmooth()
6147   _edgeDir[0] = getEdgeDir( E, leOnV[0]->_nodes[0], data.GetHelper() );
6148   _edgeDir[1] = getEdgeDir( E, leOnV[1]->_nodes[0], data.GetHelper() );
6149   _leOnV[0]._cosin = Abs( leOnV[0]->_cosin );
6150   _leOnV[1]._cosin = Abs( leOnV[1]->_cosin );
6151   if ( _eos._sWOL.IsNull() ) // 3D
6152     for ( int iEnd = 0; iEnd < 2; ++iEnd )
6153       _leOnV[iEnd]._cosin = Abs( _edgeDir[iEnd].Normalized() * leOnV[iEnd]->_normal );
6154
6155   if ( isAnalytic() )
6156     return;
6157
6158   // divide E to have offset segments with low deflection
6159   BRepAdaptor_Curve c3dAdaptor( E );
6160   const double curDeflect = 0.1; //0.01; // Curvature deflection == |p1p2]*sin(p1p2,p1pM)
6161   const double angDeflect = 0.1; //0.09; // Angular deflection == sin(p1pM,pMp2)
6162   GCPnts_TangentialDeflection discret(c3dAdaptor, angDeflect, curDeflect);
6163   if ( discret.NbPoints() <= 2 )
6164   {
6165     _anaCurve = new Geom_Line( gp::OX() ); // only type does matter
6166     return;
6167   }
6168
6169   const double u0 = c3dAdaptor.FirstParameter();
6170   gp_Pnt p; gp_Vec tangent;
6171   if ( discret.NbPoints() >= (int) _eos.size() + 2 )
6172   {
6173     _offPoints.resize( discret.NbPoints() );
6174     for ( size_t i = 0; i < _offPoints.size(); i++ )
6175     {
6176       double u = discret.Parameter( i+1 );
6177       c3dAdaptor.D1( u, p, tangent );
6178       _offPoints[i]._xyz     = p.XYZ();
6179       _offPoints[i]._edgeDir = tangent.XYZ();
6180       _offPoints[i]._param = GCPnts_AbscissaPoint::Length( c3dAdaptor, u0, u ) / _curveLen;
6181     }
6182   }
6183   else
6184   {
6185     std::vector< double > params( _eos.size() + 2 );
6186
6187     params[0]     = data.GetHelper().GetNodeU( E, leOnV[0]->_nodes[0] );
6188     params.back() = data.GetHelper().GetNodeU( E, leOnV[1]->_nodes[0] );
6189     for ( size_t i = 0; i < _eos.size(); i++ )
6190       params[i+1] = data.GetHelper().GetNodeU( E, _eos[i]->_nodes[0] );
6191
6192     if ( params[1] > params[ _eos.size() ] )
6193       std::reverse( params.begin() + 1, params.end() - 1 );
6194
6195     _offPoints.resize( _eos.size() + 2 );
6196     for ( size_t i = 0; i < _offPoints.size(); i++ )
6197     {
6198       const double u = params[i];
6199       c3dAdaptor.D1( u, p, tangent );
6200       _offPoints[i]._xyz     = p.XYZ();
6201       _offPoints[i]._edgeDir = tangent.XYZ();
6202       _offPoints[i]._param = GCPnts_AbscissaPoint::Length( c3dAdaptor, u0, u ) / _curveLen;
6203     }
6204   }
6205
6206   // set _2edges
6207   _offPoints    [0]._2edges.set( &_leOnV[0], &_leOnV[0], 0.5, 0.5 );
6208   _offPoints.back()._2edges.set( &_leOnV[1], &_leOnV[1], 0.5, 0.5 );
6209   _2NearEdges tmp2edges;
6210   tmp2edges._edges[1] = _eos._edges[0];
6211   _leOnV[0]._2neibors = & tmp2edges;
6212   _leOnV[0]._nodes    = leOnV[0]->_nodes;
6213   _leOnV[1]._nodes    = leOnV[1]->_nodes;
6214   _LayerEdge* eNext, *ePrev = & _leOnV[0];
6215   for ( size_t iLE = 0, i = 1; i < _offPoints.size()-1; i++ )
6216   {
6217     // find _LayerEdge's located before and after an offset point
6218     // (_eos._edges[ iLE ] is next after ePrev)
6219     while ( iLE < _eos._edges.size() && _offPoints[i]._param > _leParams[ iLE ] )
6220       ePrev = _eos._edges[ iLE++ ];
6221     eNext = ePrev->_2neibors->_edges[1];
6222
6223     gp_Pnt p0 = SMESH_TNodeXYZ( ePrev->_nodes[0] );
6224     gp_Pnt p1 = SMESH_TNodeXYZ( eNext->_nodes[0] );
6225     double  r = p0.Distance( _offPoints[i]._xyz ) / p0.Distance( p1 );
6226     _offPoints[i]._2edges.set( ePrev, eNext, 1-r, r );
6227   }
6228
6229   // replace _LayerEdge's on VERTEX by _leOnV in _offPoints._2edges
6230   for ( size_t i = 0; i < _offPoints.size(); i++ )
6231     if ( _offPoints[i]._2edges._edges[0] == leOnV[0] )
6232       _offPoints[i]._2edges._edges[0] = & _leOnV[0];
6233     else break;
6234   for ( size_t i = _offPoints.size()-1; i > 0; i-- )
6235     if ( _offPoints[i]._2edges._edges[1] == leOnV[1] )
6236       _offPoints[i]._2edges._edges[1] = & _leOnV[1];
6237     else break;
6238
6239   // set _normal of _leOnV[0] and _leOnV[1] to be normal to the EDGE
6240
6241   int iLBO = _offPoints.size() - 2; // last but one
6242
6243   if ( leOnV[ 0 ]->Is( _LayerEdge::MULTI_NORMAL ))
6244     _leOnV[ 0 ]._normal = getNormalNormal( _eos._edges[1]->_normal, _edgeDir[0] );
6245   else
6246     _leOnV[ 0 ]._normal = getNormalNormal( leOnV[0]->_normal,       _edgeDir[0] );
6247   if ( leOnV[ 1 ]->Is( _LayerEdge::MULTI_NORMAL ))
6248     _leOnV[ 1 ]._normal = getNormalNormal( _eos._edges.back()->_normal, _edgeDir[1] );
6249   else
6250     _leOnV[ 1 ]._normal = getNormalNormal( leOnV[1]->_normal,           _edgeDir[1] );
6251   _leOnV[ 0 ]._len = 0;
6252   _leOnV[ 1 ]._len = 0;
6253   _leOnV[ 0 ]._lenFactor = _offPoints[1   ]._2edges._edges[1]->_lenFactor;
6254   _leOnV[ 1 ]._lenFactor = _offPoints[iLBO]._2edges._edges[0]->_lenFactor;
6255
6256   _iSeg[0] = 0;
6257   _iSeg[1] = _offPoints.size()-2;
6258
6259   // initialize OffPnt::_len
6260   for ( size_t i = 0; i < _offPoints.size(); ++i )
6261     _offPoints[i]._len = 0;
6262
6263   if ( _eos._edges[0]->NbSteps() > 1 ) // already inflated several times, init _xyz
6264   {
6265     _leOnV[0]._len = leOnV[0]->_len;
6266     _leOnV[1]._len = leOnV[1]->_len;
6267     for ( size_t i = 0; i < _offPoints.size(); i++ )
6268     {
6269       _LayerEdge*  e0 = _offPoints[i]._2edges._edges[0];
6270       _LayerEdge*  e1 = _offPoints[i]._2edges._edges[1];
6271       const double w0 = _offPoints[i]._2edges._wgt[0];
6272       const double w1 = _offPoints[i]._2edges._wgt[1];
6273       double  avgLen  = ( e0->_len * w0 + e1->_len * w1 );
6274       gp_XYZ  avgXYZ  = ( SMESH_TNodeXYZ( e0->_nodes.back() ) * w0 +
6275                           SMESH_TNodeXYZ( e1->_nodes.back() ) * w1 );
6276       _offPoints[i]._xyz = avgXYZ;
6277       _offPoints[i]._len = avgLen;
6278     }
6279   }
6280 }
6281
6282 //================================================================================
6283 /*!
6284  * \brief return _normal of _leOnV[is2nd] normal to the EDGE
6285  */
6286 //================================================================================
6287
6288 gp_XYZ _Smoother1D::getNormalNormal( const gp_XYZ & normal,
6289                                      const gp_XYZ&  edgeDir)
6290 {
6291   gp_XYZ cross = normal ^ edgeDir;
6292   gp_XYZ  norm = edgeDir ^ cross;
6293   double  size = norm.Modulus();
6294
6295   // if ( size == 0 ) // MULTI_NORMAL _LayerEdge
6296   //   return gp_XYZ( 1e-100, 1e-100, 1e-100 );
6297
6298   return norm / size;
6299 }
6300
6301 //================================================================================
6302 /*!
6303  * \brief Writes a script creating a mesh composed of _offPoints
6304  */
6305 //================================================================================
6306
6307 void _Smoother1D::offPointsToPython() const
6308 {
6309   const char* fname = "/tmp/offPoints.py";
6310   cout << "execfile('"<<fname<<"')"<<endl;
6311   ofstream py(fname);
6312   py << "import SMESH" << endl
6313      << "from salome.smesh import smeshBuilder" << endl
6314      << "smesh  = smeshBuilder.New(salome.myStudy)" << endl
6315      << "mesh   = smesh.Mesh( 'offPoints' )"<<endl;
6316   for ( size_t i = 0; i < _offPoints.size(); i++ )
6317   {
6318     py << "mesh.AddNode( "
6319        << _offPoints[i]._xyz.X() << ", "
6320        << _offPoints[i]._xyz.Y() << ", "
6321        << _offPoints[i]._xyz.Z() << " )" << endl;
6322   }
6323 }
6324
6325 //================================================================================
6326 /*!
6327  * \brief Sort _LayerEdge's by a parameter on a given EDGE
6328  */
6329 //================================================================================
6330
6331 void _SolidData::SortOnEdge( const TopoDS_Edge&     E,
6332                              vector< _LayerEdge* >& edges)
6333 {
6334   map< double, _LayerEdge* > u2edge;
6335   for ( size_t i = 0; i < edges.size(); ++i )
6336     u2edge.insert( u2edge.end(),
6337                    make_pair( _helper->GetNodeU( E, edges[i]->_nodes[0] ), edges[i] ));
6338
6339   ASSERT( u2edge.size() == edges.size() );
6340   map< double, _LayerEdge* >::iterator u2e = u2edge.begin();
6341   for ( size_t i = 0; i < edges.size(); ++i, ++u2e )
6342     edges[i] = u2e->second;
6343
6344   Sort2NeiborsOnEdge( edges );
6345 }
6346
6347 //================================================================================
6348 /*!
6349  * \brief Set _2neibors according to the order of _LayerEdge on EDGE
6350  */
6351 //================================================================================
6352
6353 void _SolidData::Sort2NeiborsOnEdge( vector< _LayerEdge* >& edges )
6354 {
6355   if ( edges.size() < 2 || !edges[0]->_2neibors ) return;
6356
6357   for ( size_t i = 0; i < edges.size()-1; ++i )
6358     if ( edges[i]->_2neibors->tgtNode(1) != edges[i+1]->_nodes.back() )
6359       edges[i]->_2neibors->reverse();
6360
6361   const size_t iLast = edges.size() - 1;
6362   if ( edges.size() > 1 &&
6363        edges[iLast]->_2neibors->tgtNode(0) != edges[iLast-1]->_nodes.back() )
6364     edges[iLast]->_2neibors->reverse();
6365 }
6366
6367 //================================================================================
6368 /*!
6369  * \brief Return _EdgesOnShape* corresponding to the shape
6370  */
6371 //================================================================================
6372
6373 _EdgesOnShape* _SolidData::GetShapeEdges(const TGeomID shapeID )
6374 {
6375   if ( shapeID < (int)_edgesOnShape.size() &&
6376        _edgesOnShape[ shapeID ]._shapeID == shapeID )
6377     return _edgesOnShape[ shapeID ]._subMesh ? & _edgesOnShape[ shapeID ] : 0;
6378
6379   for ( size_t i = 0; i < _edgesOnShape.size(); ++i )
6380     if ( _edgesOnShape[i]._shapeID == shapeID )
6381       return _edgesOnShape[i]._subMesh ? & _edgesOnShape[i] : 0;
6382
6383   return 0;
6384 }
6385
6386 //================================================================================
6387 /*!
6388  * \brief Return _EdgesOnShape* corresponding to the shape
6389  */
6390 //================================================================================
6391
6392 _EdgesOnShape* _SolidData::GetShapeEdges(const TopoDS_Shape& shape )
6393 {
6394   SMESHDS_Mesh* meshDS = _proxyMesh->GetMesh()->GetMeshDS();
6395   return GetShapeEdges( meshDS->ShapeToIndex( shape ));
6396 }
6397
6398 //================================================================================
6399 /*!
6400  * \brief Prepare data of the _LayerEdge for smoothing on FACE
6401  */
6402 //================================================================================
6403
6404 void _SolidData::PrepareEdgesToSmoothOnFace( _EdgesOnShape* eos, bool substituteSrcNodes )
6405 {
6406   SMESH_MesherHelper helper( *_proxyMesh->GetMesh() );
6407
6408   set< TGeomID > vertices;
6409   TopoDS_Face F;
6410   if ( eos->ShapeType() == TopAbs_FACE )
6411   {
6412     // check FACE concavity and get concave VERTEXes
6413     F = TopoDS::Face( eos->_shape );
6414     if ( isConcave( F, helper, &vertices ))
6415       _concaveFaces.insert( eos->_shapeID );
6416
6417     // set eos._eosConcaVer
6418     eos->_eosConcaVer.clear();
6419     eos->_eosConcaVer.reserve( vertices.size() );
6420     for ( set< TGeomID >::iterator v = vertices.begin(); v != vertices.end(); ++v )
6421     {
6422       _EdgesOnShape* eov = GetShapeEdges( *v );
6423       if ( eov && eov->_edges.size() == 1 )
6424       {
6425         eos->_eosConcaVer.push_back( eov );
6426         for ( size_t i = 0; i < eov->_edges[0]->_neibors.size(); ++i )
6427           eov->_edges[0]->_neibors[i]->Set( _LayerEdge::DIFFICULT );
6428       }
6429     }
6430
6431     // SetSmooLen() to _LayerEdge's on FACE
6432     // for ( size_t i = 0; i < eos->_edges.size(); ++i )
6433     // {
6434     //   eos->_edges[i]->SetSmooLen( Precision::Infinite() );
6435     // }
6436     // SMESH_subMeshIteratorPtr smIt = eos->_subMesh->getDependsOnIterator(/*includeSelf=*/false);
6437     // while ( smIt->more() ) // loop on sub-shapes of the FACE
6438     // {
6439     //   _EdgesOnShape* eoe = GetShapeEdges( smIt->next()->GetId() );
6440     //   if ( !eoe ) continue;
6441
6442     //   vector<_LayerEdge*>& eE = eoe->_edges;
6443     //   for ( size_t iE = 0; iE < eE.size(); ++iE ) // loop on _LayerEdge's on EDGE or VERTEX
6444     //   {
6445     //     if ( eE[iE]->_cosin <= theMinSmoothCosin )
6446     //       continue;
6447
6448     //     SMDS_ElemIteratorPtr segIt = eE[iE]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Edge);
6449     //     while ( segIt->more() )
6450     //     {
6451     //       const SMDS_MeshElement* seg = segIt->next();
6452     //       if ( !eos->_subMesh->DependsOn( seg->getshapeId() ))
6453     //         continue;
6454     //       if ( seg->GetNode(0) != eE[iE]->_nodes[0] )
6455     //         continue; // not to check a seg twice
6456     //       for ( size_t iN = 0; iN < eE[iE]->_neibors.size(); ++iN )
6457     //       {
6458     //         _LayerEdge* eN = eE[iE]->_neibors[iN];
6459     //         if ( eN->_nodes[0]->getshapeId() != eos->_shapeID )
6460     //           continue;
6461     //         double dist    = SMESH_MeshAlgos::GetDistance( seg, SMESH_TNodeXYZ( eN->_nodes[0] ));
6462     //         double smooLen = getSmoothingThickness( eE[iE]->_cosin, dist );
6463     //         eN->SetSmooLen( Min( smooLen, eN->GetSmooLen() ));
6464     //         eN->Set( _LayerEdge::NEAR_BOUNDARY );
6465     //       }
6466     //     }
6467     //   }
6468     // }
6469   } // if ( eos->ShapeType() == TopAbs_FACE )
6470
6471   for ( size_t i = 0; i < eos->_edges.size(); ++i )
6472   {
6473     eos->_edges[i]->_smooFunction = 0;
6474     eos->_edges[i]->Set( _LayerEdge::TO_SMOOTH );
6475   }
6476   bool isCurved = false;
6477   for ( size_t i = 0; i < eos->_edges.size(); ++i )
6478   {
6479     _LayerEdge* edge = eos->_edges[i];
6480
6481     // get simplices sorted
6482     _Simplex::SortSimplices( edge->_simplices );
6483
6484     // smoothing function
6485     edge->ChooseSmooFunction( vertices, _n2eMap );
6486
6487     // set _curvature
6488     double avgNormProj = 0, avgLen = 0;
6489     for ( size_t iS = 0; iS < edge->_simplices.size(); ++iS )
6490     {
6491       _Simplex& s = edge->_simplices[iS];
6492
6493       gp_XYZ  vec = edge->_pos.back() - SMESH_TNodeXYZ( s._nPrev );
6494       avgNormProj += edge->_normal * vec;
6495       avgLen      += vec.Modulus();
6496       if ( substituteSrcNodes )
6497       {
6498         s._nNext = _n2eMap[ s._nNext ]->_nodes.back();
6499         s._nPrev = _n2eMap[ s._nPrev ]->_nodes.back();
6500       }
6501     }
6502     avgNormProj /= edge->_simplices.size();
6503     avgLen      /= edge->_simplices.size();
6504     if (( edge->_curvature = _Curvature::New( avgNormProj, avgLen )))
6505     {
6506       edge->Set( _LayerEdge::SMOOTHED_C1 );
6507       isCurved = true;
6508       SMDS_FacePositionPtr fPos = edge->_nodes[0]->GetPosition();
6509       if ( !fPos )
6510         for ( size_t iS = 0; iS < edge->_simplices.size()  &&  !fPos; ++iS )
6511           fPos = edge->_simplices[iS]._nPrev->GetPosition();
6512       if ( fPos )
6513         edge->_curvature->_uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
6514     }
6515   }
6516
6517   // prepare for putOnOffsetSurface()
6518   if (( eos->ShapeType() == TopAbs_FACE ) &&
6519       ( isCurved || !eos->_eosConcaVer.empty() ))
6520   {
6521     eos->_offsetSurf = helper.GetSurface( TopoDS::Face( eos->_shape ));
6522     eos->_edgeForOffset = 0;
6523
6524     double maxCosin = -1;
6525     for ( TopExp_Explorer eExp( eos->_shape, TopAbs_EDGE ); eExp.More(); eExp.Next() )
6526     {
6527       _EdgesOnShape* eoe = GetShapeEdges( eExp.Current() );
6528       if ( !eoe || eoe->_edges.empty() ) continue;
6529
6530       vector<_LayerEdge*>& eE = eoe->_edges;
6531       _LayerEdge* e = eE[ eE.size() / 2 ];
6532       if ( e->_cosin > maxCosin )
6533       {
6534         eos->_edgeForOffset = e;
6535         maxCosin = e->_cosin;
6536       }
6537     }
6538   }
6539 }
6540
6541 //================================================================================
6542 /*!
6543  * \brief Add faces for smoothing
6544  */
6545 //================================================================================
6546
6547 void _SolidData::AddShapesToSmooth( const set< _EdgesOnShape* >& eosToSmooth,
6548                                     const set< _EdgesOnShape* >* edgesNoAnaSmooth )
6549 {
6550   set< _EdgesOnShape * >::const_iterator eos = eosToSmooth.begin();
6551   for ( ; eos != eosToSmooth.end(); ++eos )
6552   {
6553     if ( !*eos || (*eos)->_toSmooth ) continue;
6554
6555     (*eos)->_toSmooth = true;
6556
6557     if ( (*eos)->ShapeType() == TopAbs_FACE )
6558     {
6559       PrepareEdgesToSmoothOnFace( *eos, /*substituteSrcNodes=*/false );
6560       (*eos)->_toSmooth = true;
6561     }
6562   }
6563
6564   // avoid _Smoother1D::smoothAnalyticEdge() of edgesNoAnaSmooth
6565   if ( edgesNoAnaSmooth )
6566     for ( eos = edgesNoAnaSmooth->begin(); eos != edgesNoAnaSmooth->end(); ++eos )
6567     {
6568       if ( (*eos)->_edgeSmoother )
6569         (*eos)->_edgeSmoother->_anaCurve.Nullify();
6570     }
6571 }
6572
6573 //================================================================================
6574 /*!
6575  * \brief Limit _LayerEdge::_maxLen according to local curvature
6576  */
6577 //================================================================================
6578
6579 void _ViscousBuilder::limitMaxLenByCurvature( _SolidData& data, SMESH_MesherHelper& helper )
6580 {
6581   // find intersection of neighbor _LayerEdge's to limit _maxLen
6582   // according to local curvature (IPAL52648)
6583
6584   // This method must be called after findCollisionEdges() where _LayerEdge's
6585   // get _lenFactor initialized in the case of eos._hyp.IsOffsetMethod()
6586
6587   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
6588   {
6589     _EdgesOnShape& eosI = data._edgesOnShape[iS];
6590     if ( eosI._edges.empty() ) continue;
6591     if ( !eosI._hyp.ToSmooth() )
6592     {
6593       for ( size_t i = 0; i < eosI._edges.size(); ++i )
6594       {
6595         _LayerEdge* eI = eosI._edges[i];
6596         for ( size_t iN = 0; iN < eI->_neibors.size(); ++iN )
6597         {
6598           _LayerEdge* eN = eI->_neibors[iN];
6599           if ( eI->_nodes[0]->GetID() < eN->_nodes[0]->GetID() ) // treat this pair once
6600           {
6601             _EdgesOnShape* eosN = data.GetShapeEdges( eN );
6602             limitMaxLenByCurvature( eI, eN, eosI, *eosN, eosI._hyp.ToSmooth() );
6603           }
6604         }
6605       }
6606     }
6607     else if ( eosI.ShapeType() == TopAbs_EDGE )
6608     {
6609       const TopoDS_Edge& E = TopoDS::Edge( eosI._shape );
6610       if ( SMESH_Algo::IsStraight( E, /*degenResult=*/true )) continue;
6611
6612       _LayerEdge* e0 = eosI._edges[0];
6613       for ( size_t i = 1; i < eosI._edges.size(); ++i )
6614       {
6615         _LayerEdge* eI = eosI._edges[i];
6616         limitMaxLenByCurvature( eI, e0, eosI, eosI, eosI._hyp.ToSmooth() );
6617         e0 = eI;
6618       }
6619     }
6620   }
6621 }
6622
6623 //================================================================================
6624 /*!
6625  * \brief Limit _LayerEdge::_maxLen according to local curvature
6626  */
6627 //================================================================================
6628
6629 void _ViscousBuilder::limitMaxLenByCurvature( _LayerEdge*    e1,
6630                                               _LayerEdge*    e2,
6631                                               _EdgesOnShape& eos1,
6632                                               _EdgesOnShape& eos2,
6633                                               const bool     isSmoothable )
6634 {
6635   if (( e1->_nodes[0]->GetPosition()->GetDim() !=
6636         e2->_nodes[0]->GetPosition()->GetDim() ) &&
6637       ( e1->_cosin < 0.75 ))
6638     return; // angle > 90 deg at e1
6639
6640   gp_XYZ plnNorm = e1->_normal ^ e2->_normal;
6641   double norSize = plnNorm.SquareModulus();
6642   if ( norSize < std::numeric_limits<double>::min() )
6643     return; // parallel normals
6644
6645   // find closest points of skew _LayerEdge's
6646   SMESH_TNodeXYZ src1( e1->_nodes[0] ), src2( e2->_nodes[0] );
6647   gp_XYZ dir12 = src2 - src1;
6648   gp_XYZ perp1 = e1->_normal ^ plnNorm;
6649   gp_XYZ perp2 = e2->_normal ^ plnNorm;
6650   double  dot1 = perp2 * e1->_normal;
6651   double  dot2 = perp1 * e2->_normal;
6652   double    u1 =   ( perp2 * dir12 ) / dot1;
6653   double    u2 = - ( perp1 * dir12 ) / dot2;
6654   if ( u1 > 0 && u2 > 0 )
6655   {
6656     double ovl = ( u1 * e1->_normal * dir12 -
6657                    u2 * e2->_normal * dir12 ) / dir12.SquareModulus();
6658     if ( ovl > theSmoothThickToElemSizeRatio )
6659     {
6660       const double coef = 0.75;
6661       e1->SetMaxLen( Min( e1->_maxLen, coef * u1 / e1->_lenFactor ));
6662       e2->SetMaxLen( Min( e2->_maxLen, coef * u2 / e2->_lenFactor ));
6663     }
6664   }
6665 }
6666
6667 //================================================================================
6668 /*!
6669  * \brief Fill data._collisionEdges
6670  */
6671 //================================================================================
6672
6673 void _ViscousBuilder::findCollisionEdges( _SolidData& data, SMESH_MesherHelper& helper )
6674 {
6675   data._collisionEdges.clear();
6676
6677   // set the full thickness of the layers to LEs
6678   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
6679   {
6680     _EdgesOnShape& eos = data._edgesOnShape[iS];
6681     if ( eos._edges.empty() ) continue;
6682     if ( eos.ShapeType() != TopAbs_EDGE && eos.ShapeType() != TopAbs_VERTEX ) continue;
6683
6684     for ( size_t i = 0; i < eos._edges.size(); ++i )
6685     {
6686       if ( eos._edges[i]->Is( _LayerEdge::BLOCKED )) continue;
6687       double maxLen = eos._edges[i]->_maxLen;
6688       eos._edges[i]->_maxLen = Precision::Infinite(); // avoid blocking
6689       eos._edges[i]->SetNewLength( 1.5 * maxLen, eos, helper );
6690       eos._edges[i]->_maxLen = maxLen;
6691     }
6692   }
6693
6694   // make temporary quadrangles got by extrusion of
6695   // mesh edges along _LayerEdge._normal's
6696
6697   vector< const SMDS_MeshElement* > tmpFaces;
6698
6699   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
6700   {
6701     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
6702     if ( eos.ShapeType() != TopAbs_EDGE )
6703       continue;
6704     if ( eos._edges.empty() )
6705     {
6706       _LayerEdge* edge[2] = { 0, 0 }; // LE of 2 VERTEX'es
6707       SMESH_subMeshIteratorPtr smIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false);
6708       while ( smIt->more() )
6709         if ( _EdgesOnShape* eov = data.GetShapeEdges( smIt->next()->GetId() ))
6710           if ( eov->_edges.size() == 1 )
6711             edge[ bool( edge[0]) ] = eov->_edges[0];
6712
6713       if ( edge[1] )
6714       {
6715         _TmpMeshFaceOnEdge* f = new _TmpMeshFaceOnEdge( edge[0], edge[1], --_tmpFaceID );
6716         tmpFaces.push_back( f );
6717       }
6718     }
6719     for ( size_t i = 0; i < eos._edges.size(); ++i )
6720     {
6721       _LayerEdge* edge = eos._edges[i];
6722       for ( int j = 0; j < 2; ++j ) // loop on _2NearEdges
6723       {
6724         const SMDS_MeshNode* src2 = edge->_2neibors->srcNode(j);
6725         if ( src2->GetPosition()->GetDim() > 0 &&
6726              src2->GetID() < edge->_nodes[0]->GetID() )
6727           continue; // avoid using same segment twice
6728
6729         // a _LayerEdge containing tgt2
6730         _LayerEdge* neiborEdge = edge->_2neibors->_edges[j];
6731
6732         _TmpMeshFaceOnEdge* f = new _TmpMeshFaceOnEdge( edge, neiborEdge, --_tmpFaceID );
6733         tmpFaces.push_back( f );
6734       }
6735     }
6736   }
6737
6738   // Find _LayerEdge's intersecting tmpFaces.
6739
6740   SMDS_ElemIteratorPtr fIt( new SMDS_ElementVectorIterator( tmpFaces.begin(),
6741                                                             tmpFaces.end()));
6742   SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
6743     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(), fIt ));
6744
6745   double dist1, dist2, segLen, eps = 0.5;
6746   _CollisionEdges collEdges;
6747   vector< const SMDS_MeshElement* > suspectFaces;
6748   const double angle45 = Cos( 45. * M_PI / 180. );
6749
6750   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
6751   {
6752     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
6753     if ( eos.ShapeType() == TopAbs_FACE || !eos._sWOL.IsNull() )
6754       continue;
6755     // find sub-shapes whose VL can influence VL on eos
6756     set< TGeomID > neighborShapes;
6757     PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
6758     while ( const TopoDS_Shape* face = fIt->next() )
6759     {
6760       TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
6761       if ( _EdgesOnShape* eof = data.GetShapeEdges( faceID ))
6762       {
6763         SMESH_subMeshIteratorPtr subIt = eof->_subMesh->getDependsOnIterator(/*includeSelf=*/false);
6764         while ( subIt->more() )
6765           neighborShapes.insert( subIt->next()->GetId() );
6766       }
6767     }
6768     if ( eos.ShapeType() == TopAbs_VERTEX )
6769     {
6770       PShapeIteratorPtr eIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_EDGE );
6771       while ( const TopoDS_Shape* edge = eIt->next() )
6772         neighborShapes.erase( getMeshDS()->ShapeToIndex( *edge ));
6773     }
6774     // find intersecting _LayerEdge's
6775     for ( size_t i = 0; i < eos._edges.size(); ++i )
6776     {
6777       if ( eos._edges[i]->Is( _LayerEdge::MULTI_NORMAL )) continue;
6778       _LayerEdge*   edge = eos._edges[i];
6779       gp_Ax1 lastSegment = edge->LastSegment( segLen, eos );
6780       segLen *= 1.2;
6781
6782       gp_Vec eSegDir0, eSegDir1;
6783       if ( edge->IsOnEdge() )
6784       {
6785         SMESH_TNodeXYZ eP( edge->_nodes[0] );
6786         eSegDir0 = SMESH_TNodeXYZ( edge->_2neibors->srcNode(0) ) - eP;
6787         eSegDir1 = SMESH_TNodeXYZ( edge->_2neibors->srcNode(1) ) - eP;
6788       }
6789       suspectFaces.clear();
6790       searcher->GetElementsInSphere( SMESH_TNodeXYZ( edge->_nodes.back()), edge->_len * 2,
6791                                      SMDSAbs_Face, suspectFaces );
6792       collEdges._intEdges.clear();
6793       for ( size_t j = 0 ; j < suspectFaces.size(); ++j )
6794       {
6795         const _TmpMeshFaceOnEdge* f = (const _TmpMeshFaceOnEdge*) suspectFaces[j];
6796         if ( f->_le1 == edge || f->_le2 == edge ) continue;
6797         if ( !neighborShapes.count( f->_le1->_nodes[0]->getshapeId() )) continue;
6798         if ( !neighborShapes.count( f->_le2->_nodes[0]->getshapeId() )) continue;
6799         if ( edge->IsOnEdge() ) {
6800           if ( edge->_2neibors->include( f->_le1 ) ||
6801                edge->_2neibors->include( f->_le2 )) continue;
6802         }
6803         else {
6804           if (( f->_le1->IsOnEdge() && f->_le1->_2neibors->include( edge )) ||
6805               ( f->_le2->IsOnEdge() && f->_le2->_2neibors->include( edge )))  continue;
6806         }
6807         dist1 = dist2 = Precision::Infinite();
6808         if ( !edge->SegTriaInter( lastSegment, f->n(0), f->n(1), f->n(2), dist1, eps ))
6809           dist1 = Precision::Infinite();
6810         if ( !edge->SegTriaInter( lastSegment, f->n(3), f->n(2), f->n(0), dist2, eps ))
6811           dist2 = Precision::Infinite();
6812         if (( dist1 > segLen ) && ( dist2 > segLen ))
6813           continue;
6814
6815         if ( edge->IsOnEdge() )
6816         {
6817           // skip perpendicular EDGEs
6818           gp_Vec fSegDir  = SMESH_TNodeXYZ( f->n(0) ) - SMESH_TNodeXYZ( f->n(3) );
6819           bool isParallel = ( isLessAngle( eSegDir0, fSegDir, angle45 ) ||
6820                               isLessAngle( eSegDir1, fSegDir, angle45 ) ||
6821                               isLessAngle( eSegDir0, fSegDir.Reversed(), angle45 ) ||
6822                               isLessAngle( eSegDir1, fSegDir.Reversed(), angle45 ));
6823           if ( !isParallel )
6824             continue;
6825         }
6826
6827         // either limit inflation of edges or remember them for updating _normal
6828         // double dot = edge->_normal * f->GetDir();
6829         // if ( dot > 0.1 )
6830         {
6831           collEdges._intEdges.push_back( f->_le1 );
6832           collEdges._intEdges.push_back( f->_le2 );
6833         }
6834         // else
6835         // {
6836         //   double shortLen = 0.75 * ( Min( dist1, dist2 ) / edge->_lenFactor );
6837         //   edge->SetMaxLen( Min( shortLen, edge->_maxLen ));
6838         // }
6839       }
6840
6841       if ( !collEdges._intEdges.empty() )
6842       {
6843         collEdges._edge = edge;
6844         data._collisionEdges.push_back( collEdges );
6845       }
6846     }
6847   }
6848
6849   for ( size_t i = 0 ; i < tmpFaces.size(); ++i )
6850     delete tmpFaces[i];
6851
6852   // restore the zero thickness
6853   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
6854   {
6855     _EdgesOnShape& eos = data._edgesOnShape[iS];
6856     if ( eos._edges.empty() ) continue;
6857     if ( eos.ShapeType() != TopAbs_EDGE && eos.ShapeType() != TopAbs_VERTEX ) continue;
6858
6859     for ( size_t i = 0; i < eos._edges.size(); ++i )
6860     {
6861       eos._edges[i]->InvalidateStep( 1, eos );
6862       eos._edges[i]->_len = 0;
6863     }
6864   }
6865 }
6866
6867 //================================================================================
6868 /*!
6869  * \brief Find _LayerEdge's located on boundary of a convex FACE whose normal
6870  *        will be updated at each inflation step
6871  */
6872 //================================================================================
6873
6874 void _ViscousBuilder::findEdgesToUpdateNormalNearConvexFace( _ConvexFace &       convFace,
6875                                                              _SolidData&         data,
6876                                                              SMESH_MesherHelper& helper )
6877 {
6878   const TGeomID convFaceID = getMeshDS()->ShapeToIndex( convFace._face );
6879   const double       preci = BRep_Tool::Tolerance( convFace._face );
6880   Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( convFace._face );
6881
6882   bool edgesToUpdateFound = false;
6883
6884   map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.begin();
6885   for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
6886   {
6887     _EdgesOnShape& eos = * id2eos->second;
6888     if ( !eos._sWOL.IsNull() ) continue;
6889     if ( !eos._hyp.ToSmooth() ) continue;
6890     for ( size_t i = 0; i < eos._edges.size(); ++i )
6891     {
6892       _LayerEdge* ledge = eos._edges[ i ];
6893       if ( ledge->Is( _LayerEdge::UPD_NORMAL_CONV )) continue; // already checked
6894       if ( ledge->Is( _LayerEdge::MULTI_NORMAL )) continue; // not inflatable
6895
6896       gp_XYZ tgtPos = ( SMESH_NodeXYZ( ledge->_nodes[0] ) +
6897                         ledge->_normal * ledge->_lenFactor * ledge->_maxLen );
6898
6899       // the normal must be updated if distance from tgtPos to surface is less than
6900       // target thickness
6901
6902       // find an initial UV for search of a projection of tgtPos to surface
6903       const SMDS_MeshNode* nodeInFace = 0;
6904       SMDS_ElemIteratorPtr fIt = ledge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
6905       while ( fIt->more() && !nodeInFace )
6906       {
6907         const SMDS_MeshElement* f = fIt->next();
6908         if ( convFaceID != f->getshapeId() ) continue;
6909
6910         SMDS_ElemIteratorPtr nIt = f->nodesIterator();
6911         while ( nIt->more() && !nodeInFace )
6912         {
6913           const SMDS_MeshElement* n = nIt->next();
6914           if ( n->getshapeId() == convFaceID )
6915             nodeInFace = static_cast< const SMDS_MeshNode* >( n );
6916         }
6917       }
6918       if ( !nodeInFace )
6919         continue;
6920       gp_XY uv = helper.GetNodeUV( convFace._face, nodeInFace );
6921
6922       // projection
6923       surface->NextValueOfUV( uv, tgtPos, preci );
6924       double  dist = surface->Gap();
6925       if ( dist < 0.95 * ledge->_maxLen )
6926       {
6927         ledge->Set( _LayerEdge::UPD_NORMAL_CONV );
6928         if ( !ledge->_curvature ) ledge->_curvature = new _Curvature;
6929         ledge->_curvature->_uv.SetCoord( uv.X(), uv.Y() );
6930         edgesToUpdateFound = true;
6931       }
6932     }
6933   }
6934
6935   if ( !convFace._isTooCurved && edgesToUpdateFound )
6936   {
6937     data._convexFaces.insert( make_pair( convFaceID, convFace )).first->second;
6938   }
6939 }
6940
6941 //================================================================================
6942 /*!
6943  * \brief Modify normals of _LayerEdge's on EDGE's to avoid intersection with
6944  * _LayerEdge's on neighbor EDGE's
6945  */
6946 //================================================================================
6947
6948 bool _ViscousBuilder::updateNormals( _SolidData&         data,
6949                                      SMESH_MesherHelper& helper,
6950                                      int                 stepNb,
6951                                      double              stepSize)
6952 {
6953   updateNormalsOfC1Vertices( data );
6954
6955   if ( stepNb > 0 && !updateNormalsOfConvexFaces( data, helper, stepNb ))
6956     return false;
6957
6958   // map to store new _normal and _cosin for each intersected edge
6959   map< _LayerEdge*, _LayerEdge, _LayerEdgeCmp >           edge2newEdge;
6960   map< _LayerEdge*, _LayerEdge, _LayerEdgeCmp >::iterator e2neIt;
6961   _LayerEdge zeroEdge;
6962   zeroEdge._normal.SetCoord( 0,0,0 );
6963   zeroEdge._maxLen = Precision::Infinite();
6964   zeroEdge._nodes.resize(1); // to init _TmpMeshFaceOnEdge
6965
6966   set< _EdgesOnShape* > shapesToSmooth, edgesNoAnaSmooth;
6967
6968   double segLen, dist1, dist2, dist;
6969   vector< pair< _LayerEdge*, double > > intEdgesDist;
6970   _TmpMeshFaceOnEdge quad( &zeroEdge, &zeroEdge, 0 );
6971
6972   for ( int iter = 0; iter < 5; ++iter )
6973   {
6974     edge2newEdge.clear();
6975
6976     for ( size_t iE = 0; iE < data._collisionEdges.size(); ++iE )
6977     {
6978       _CollisionEdges& ce = data._collisionEdges[iE];
6979       _LayerEdge*   edge1 = ce._edge;
6980       if ( !edge1 /*|| edge1->Is( _LayerEdge::BLOCKED )*/) continue;
6981       _EdgesOnShape* eos1 = data.GetShapeEdges( edge1 );
6982       if ( !eos1 ) continue;
6983
6984       // detect intersections
6985       gp_Ax1 lastSeg = edge1->LastSegment( segLen, *eos1 );
6986       double testLen = 1.5 * edge1->_maxLen * edge1->_lenFactor;
6987       double     eps = 0.5;
6988       intEdgesDist.clear();
6989       double minIntDist = Precision::Infinite();
6990       for ( size_t i = 0; i < ce._intEdges.size(); i += 2 )
6991       {
6992         if ( edge1->Is( _LayerEdge::BLOCKED ) &&
6993              ce._intEdges[i  ]->Is( _LayerEdge::BLOCKED ) &&
6994              ce._intEdges[i+1]->Is( _LayerEdge::BLOCKED ))
6995           continue;
6996         double dot  = edge1->_normal * quad.GetDir( ce._intEdges[i], ce._intEdges[i+1] );
6997         double fact = ( 1.1 + dot * dot );
6998         SMESH_TNodeXYZ pSrc0( ce.nSrc(i) ), pSrc1( ce.nSrc(i+1) );
6999         SMESH_TNodeXYZ pTgt0( ce.nTgt(i) ), pTgt1( ce.nTgt(i+1) );
7000         gp_XYZ pLast0 = pSrc0 + ( pTgt0 - pSrc0 ) * fact;
7001         gp_XYZ pLast1 = pSrc1 + ( pTgt1 - pSrc1 ) * fact;
7002         dist1 = dist2 = Precision::Infinite();
7003         if ( !edge1->SegTriaInter( lastSeg, pSrc0, pLast0, pSrc1,  dist1, eps ) &&
7004              !edge1->SegTriaInter( lastSeg, pSrc1, pLast1, pLast0, dist2, eps ))
7005           continue;
7006         dist = dist1;
7007         if ( dist > testLen || dist <= 0 )
7008         {
7009           dist = dist2;
7010           if ( dist > testLen || dist <= 0 )
7011             continue;
7012         }
7013         // choose a closest edge
7014         gp_Pnt intP( lastSeg.Location().XYZ() + lastSeg.Direction().XYZ() * ( dist + segLen ));
7015         double d1 = intP.SquareDistance( pSrc0 );
7016         double d2 = intP.SquareDistance( pSrc1 );
7017         int iClose = i + ( d2 < d1 );
7018         _LayerEdge* edge2 = ce._intEdges[iClose];
7019         edge2->Unset( _LayerEdge::MARKED );
7020
7021         // choose a closest edge among neighbors
7022         gp_Pnt srcP( SMESH_TNodeXYZ( edge1->_nodes[0] ));
7023         d1 = srcP.SquareDistance( SMESH_TNodeXYZ( edge2->_nodes[0] ));
7024         for ( size_t j = 0; j < intEdgesDist.size(); ++j )
7025         {
7026           _LayerEdge * edgeJ = intEdgesDist[j].first;
7027           if ( edge2->IsNeiborOnEdge( edgeJ ))
7028           {
7029             d2 = srcP.SquareDistance( SMESH_TNodeXYZ( edgeJ->_nodes[0] ));
7030             ( d1 < d2 ? edgeJ : edge2 )->Set( _LayerEdge::MARKED );
7031           }
7032         }
7033         intEdgesDist.push_back( make_pair( edge2, dist ));
7034         // if ( Abs( d2 - d1 ) / Max( d2, d1 ) < 0.5 )
7035         // {
7036         //   iClose = i + !( d2 < d1 );
7037         //   intEdges.push_back( ce._intEdges[iClose] );
7038         //   ce._intEdges[iClose]->Unset( _LayerEdge::MARKED );
7039         // }
7040         minIntDist = Min( edge1->_len * edge1->_lenFactor - segLen + dist, minIntDist );
7041       }
7042
7043       //ce._edge = 0;
7044
7045       // compute new _normals
7046       for ( size_t i = 0; i < intEdgesDist.size(); ++i )
7047       {
7048         _LayerEdge* edge2   = intEdgesDist[i].first;
7049         double      distWgt = edge1->_len / intEdgesDist[i].second;
7050         // if ( edge1->Is( _LayerEdge::BLOCKED ) &&
7051         //      edge2->Is( _LayerEdge::BLOCKED )) continue;        
7052         if ( edge2->Is( _LayerEdge::MARKED )) continue;
7053         edge2->Set( _LayerEdge::MARKED );
7054
7055         // get a new normal
7056         gp_XYZ dir1 = edge1->_normal, dir2 = edge2->_normal;
7057
7058         double cos1 = Abs( edge1->_cosin ), cos2 = Abs( edge2->_cosin );
7059         double wgt1 = ( cos1 + 0.001 ) / ( cos1 + cos2 + 0.002 );
7060         double wgt2 = ( cos2 + 0.001 ) / ( cos1 + cos2 + 0.002 );
7061         // double cos1 = Abs( edge1->_cosin ),        cos2 = Abs( edge2->_cosin );
7062         // double sgn1 = 0.1 * ( 1 + edge1->_cosin ), sgn2 = 0.1 * ( 1 + edge2->_cosin );
7063         // double wgt1 = ( cos1 + sgn1 ) / ( cos1 + cos2 + sgn1 + sgn2 );
7064         // double wgt2 = ( cos2 + sgn2 ) / ( cos1 + cos2 + sgn1 + sgn2 );
7065         gp_XYZ newNormal = wgt1 * dir1 + wgt2 * dir2;
7066         newNormal.Normalize();
7067
7068         // get new cosin
7069         double newCos;
7070         double sgn1 = edge1->_cosin / cos1, sgn2 = edge2->_cosin / cos2;
7071         if ( cos1 < theMinSmoothCosin )
7072         {
7073           newCos = cos2 * sgn1;
7074         }
7075         else if ( cos2 > theMinSmoothCosin ) // both cos1 and cos2 > theMinSmoothCosin
7076         {
7077           newCos = ( wgt1 * cos1 + wgt2 * cos2 ) * edge1->_cosin / cos1;
7078         }
7079         else
7080         {
7081           newCos = edge1->_cosin;
7082         }
7083
7084         e2neIt = edge2newEdge.insert( make_pair( edge1, zeroEdge )).first;
7085         e2neIt->second._normal += distWgt * newNormal;
7086         e2neIt->second._cosin   = newCos;
7087         e2neIt->second.SetMaxLen( 0.7 * minIntDist / edge1->_lenFactor );
7088         if ( iter > 0 && sgn1 * sgn2 < 0 && edge1->_cosin < 0 )
7089           e2neIt->second._normal += dir2;
7090
7091         e2neIt = edge2newEdge.insert( make_pair( edge2, zeroEdge )).first;
7092         e2neIt->second._normal += distWgt * newNormal;
7093         if ( Precision::IsInfinite( zeroEdge._maxLen ))
7094         {
7095           e2neIt->second._cosin  = edge2->_cosin;
7096           e2neIt->second.SetMaxLen( 1.3 * minIntDist / edge1->_lenFactor );
7097         }
7098         if ( iter > 0 && sgn1 * sgn2 < 0 && edge2->_cosin < 0 )
7099           e2neIt->second._normal += dir1;
7100       }
7101     }
7102
7103     if ( edge2newEdge.empty() )
7104       break; //return true;
7105
7106     dumpFunction(SMESH_Comment("updateNormals")<< data._index << "_" << stepNb << "_it" << iter);
7107
7108     // Update data of edges depending on a new _normal
7109
7110     data.UnmarkEdges();
7111     for ( e2neIt = edge2newEdge.begin(); e2neIt != edge2newEdge.end(); ++e2neIt )
7112     {
7113       _LayerEdge*    edge = e2neIt->first;
7114       _LayerEdge& newEdge = e2neIt->second;
7115       _EdgesOnShape*  eos = data.GetShapeEdges( edge );
7116       if ( edge->Is( _LayerEdge::BLOCKED && newEdge._maxLen > edge->_len ))
7117         continue;
7118
7119       // Check if a new _normal is OK:
7120       newEdge._normal.Normalize();
7121       if ( !isNewNormalOk( data, *edge, newEdge._normal ))
7122       {
7123         if ( newEdge._maxLen < edge->_len && iter > 0 ) // limit _maxLen
7124         {
7125           edge->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true  );
7126           edge->SetMaxLen( newEdge._maxLen );
7127           edge->SetNewLength( newEdge._maxLen, *eos, helper );
7128         }
7129         continue; // the new _normal is bad
7130       }
7131       // the new _normal is OK
7132
7133       // find shapes that need smoothing due to change of _normal
7134       if ( edge->_cosin   < theMinSmoothCosin &&
7135            newEdge._cosin > theMinSmoothCosin )
7136       {
7137         if ( eos->_sWOL.IsNull() )
7138         {
7139           SMDS_ElemIteratorPtr fIt = edge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
7140           while ( fIt->more() )
7141             shapesToSmooth.insert( data.GetShapeEdges( fIt->next()->getshapeId() ));
7142         }
7143         else // edge inflates along a FACE
7144         {
7145           TopoDS_Shape V = helper.GetSubShapeByNode( edge->_nodes[0], getMeshDS() );
7146           PShapeIteratorPtr eIt = helper.GetAncestors( V, *_mesh, TopAbs_EDGE, &eos->_sWOL );
7147           while ( const TopoDS_Shape* E = eIt->next() )
7148           {
7149             gp_Vec edgeDir = getEdgeDir( TopoDS::Edge( *E ), TopoDS::Vertex( V ));
7150             double   angle = edgeDir.Angle( newEdge._normal ); // [0,PI]
7151             if ( angle < M_PI / 2 )
7152               shapesToSmooth.insert( data.GetShapeEdges( *E ));
7153           }
7154         }
7155       }
7156
7157       double len = edge->_len;
7158       edge->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true  );
7159       edge->SetNormal( newEdge._normal );
7160       edge->SetCosin( newEdge._cosin );
7161       edge->SetNewLength( len, *eos, helper );
7162       edge->Set( _LayerEdge::MARKED );
7163       edge->Set( _LayerEdge::NORMAL_UPDATED );
7164       edgesNoAnaSmooth.insert( eos );
7165     }
7166
7167     // Update normals and other dependent data of not intersecting _LayerEdge's
7168     // neighboring the intersecting ones
7169
7170     for ( e2neIt = edge2newEdge.begin(); e2neIt != edge2newEdge.end(); ++e2neIt )
7171     {
7172       _LayerEdge*   edge1 = e2neIt->first;
7173       _EdgesOnShape* eos1 = data.GetShapeEdges( edge1 );
7174       if ( !edge1->Is( _LayerEdge::MARKED ))
7175         continue;
7176
7177       if ( edge1->IsOnEdge() )
7178       {
7179         const SMDS_MeshNode * n1 = edge1->_2neibors->srcNode(0);
7180         const SMDS_MeshNode * n2 = edge1->_2neibors->srcNode(1);
7181         edge1->SetDataByNeighbors( n1, n2, *eos1, helper );
7182       }
7183
7184       if ( !edge1->_2neibors || !eos1->_sWOL.IsNull() )
7185         continue;
7186       for ( int j = 0; j < 2; ++j ) // loop on 2 neighbors
7187       {
7188         _LayerEdge* neighbor = edge1->_2neibors->_edges[j];
7189         if ( neighbor->Is( _LayerEdge::MARKED ) /*edge2newEdge.count ( neighbor )*/)
7190           continue; // j-th neighbor is also intersected
7191         _LayerEdge* prevEdge = edge1;
7192         const int nbSteps = 10;
7193         for ( int step = nbSteps; step; --step ) // step from edge1 in j-th direction
7194         {
7195           if ( neighbor->Is( _LayerEdge::BLOCKED ) ||
7196                neighbor->Is( _LayerEdge::MARKED ))
7197             break;
7198           _EdgesOnShape* eos = data.GetShapeEdges( neighbor );
7199           if ( !eos ) continue;
7200           _LayerEdge* nextEdge = neighbor;
7201           if ( neighbor->_2neibors )
7202           {
7203             int iNext = 0;
7204             nextEdge = neighbor->_2neibors->_edges[iNext];
7205             if ( nextEdge == prevEdge )
7206               nextEdge = neighbor->_2neibors->_edges[ ++iNext ];
7207           }
7208           double r = double(step-1)/nbSteps/(iter+1);
7209           if ( !nextEdge->_2neibors )
7210             r = Min( r, 0.5 );
7211
7212           gp_XYZ newNorm = prevEdge->_normal * r + nextEdge->_normal * (1-r);
7213           newNorm.Normalize();
7214           if ( !isNewNormalOk( data, *neighbor, newNorm ))
7215             break;
7216
7217           double len = neighbor->_len;
7218           neighbor->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true  );
7219           neighbor->SetNormal( newNorm );
7220           neighbor->SetCosin( prevEdge->_cosin * r + nextEdge->_cosin * (1-r) );
7221           if ( neighbor->_2neibors )
7222             neighbor->SetDataByNeighbors( prevEdge->_nodes[0], nextEdge->_nodes[0], *eos, helper );
7223           neighbor->SetNewLength( len, *eos, helper );
7224           neighbor->Set( _LayerEdge::MARKED );
7225           neighbor->Set( _LayerEdge::NORMAL_UPDATED );
7226           edgesNoAnaSmooth.insert( eos );
7227
7228           if ( !neighbor->_2neibors )
7229             break; // neighbor is on VERTEX
7230
7231           // goto the next neighbor
7232           prevEdge = neighbor;
7233           neighbor = nextEdge;
7234         }
7235       }
7236     }
7237     dumpFunctionEnd();
7238   } // iterations
7239
7240   data.AddShapesToSmooth( shapesToSmooth, &edgesNoAnaSmooth );
7241
7242   return true;
7243 }
7244
7245 //================================================================================
7246 /*!
7247  * \brief Check if a new normal is OK
7248  */
7249 //================================================================================
7250
7251 bool _ViscousBuilder::isNewNormalOk( _SolidData&   data,
7252                                      _LayerEdge&   edge,
7253                                      const gp_XYZ& newNormal)
7254 {
7255   // check a min angle between the newNormal and surrounding faces
7256   vector<_Simplex> simplices;
7257   SMESH_TNodeXYZ n0( edge._nodes[0] ), n1, n2;
7258   _Simplex::GetSimplices( n0._node, simplices, data._ignoreFaceIds, &data );
7259   double newMinDot = 1, curMinDot = 1;
7260   for ( size_t i = 0; i < simplices.size(); ++i )
7261   {
7262     n1.Set( simplices[i]._nPrev );
7263     n2.Set( simplices[i]._nNext );
7264     gp_XYZ normFace = ( n1 - n0 ) ^ ( n2 - n0 );
7265     double normLen2 = normFace.SquareModulus();
7266     if ( normLen2 < std::numeric_limits<double>::min() )
7267       continue;
7268     normFace /= Sqrt( normLen2 );
7269     newMinDot = Min( newNormal    * normFace, newMinDot );
7270     curMinDot = Min( edge._normal * normFace, curMinDot );
7271   }
7272   bool ok = true;
7273   if ( newMinDot < 0.5 )
7274   {
7275     ok = ( newMinDot >= curMinDot * 0.9 );
7276     //return ( newMinDot >= ( curMinDot * ( 0.8 + 0.1 * edge.NbSteps() )));
7277     // double initMinDot2 = 1. - edge._cosin * edge._cosin;
7278     // return ( newMinDot * newMinDot ) >= ( 0.8 * initMinDot2 );
7279   }
7280
7281   return ok;
7282 }
7283
7284 //================================================================================
7285 /*!
7286  * \brief Modify normals of _LayerEdge's on FACE to reflex smoothing
7287  */
7288 //================================================================================
7289
7290 bool _ViscousBuilder::updateNormalsOfSmoothed( _SolidData&         data,
7291                                                SMESH_MesherHelper& helper,
7292                                                const int           nbSteps,
7293                                                const double        stepSize )
7294 {
7295   if ( data._nbShapesToSmooth == 0 || nbSteps == 0 )
7296     return true; // no shapes needing smoothing
7297
7298   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7299   {
7300     _EdgesOnShape& eos = data._edgesOnShape[ iS ];
7301     if ( //!eos._toSmooth ||  _eosC1 have _toSmooth == false
7302          !eos._hyp.ToSmooth() ||
7303          eos.ShapeType() != TopAbs_FACE ||
7304          eos._edges.empty() )
7305       continue;
7306
7307     bool toSmooth = ( eos._edges[ 0 ]->NbSteps() >= nbSteps+1 );
7308     if ( !toSmooth ) continue;
7309
7310     for ( size_t i = 0; i < eos._edges.size(); ++i )
7311     {
7312       _LayerEdge* edge = eos._edges[i];
7313       if ( !edge->Is( _LayerEdge::SMOOTHED ))
7314         continue;
7315       if ( edge->Is( _LayerEdge::DIFFICULT ) && nbSteps != 1 )
7316         continue;
7317
7318       const gp_XYZ& pPrev = edge->PrevPos();
7319       const gp_XYZ& pLast = edge->_pos.back();
7320       gp_XYZ      stepVec = pLast - pPrev;
7321       double realStepSize = stepVec.Modulus();
7322       if ( realStepSize < numeric_limits<double>::min() )
7323         continue;
7324
7325       edge->_lenFactor = realStepSize / stepSize;
7326       edge->_normal    = stepVec / realStepSize;
7327       edge->Set( _LayerEdge::NORMAL_UPDATED );
7328     }
7329   }
7330
7331   return true;
7332 }
7333
7334 //================================================================================
7335 /*!
7336  * \brief Modify normals of _LayerEdge's on C1 VERTEXes
7337  */
7338 //================================================================================
7339
7340 void _ViscousBuilder::updateNormalsOfC1Vertices( _SolidData& data )
7341 {
7342   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
7343   {
7344     _EdgesOnShape& eov = data._edgesOnShape[ iS ];
7345     if ( eov._eosC1.empty() ||
7346          eov.ShapeType() != TopAbs_VERTEX ||
7347          eov._edges.empty() )
7348       continue;
7349
7350     gp_XYZ newNorm   = eov._edges[0]->_normal;
7351     double curThick  = eov._edges[0]->_len * eov._edges[0]->_lenFactor;
7352     bool normChanged = false;
7353
7354     for ( size_t i = 0; i < eov._eosC1.size(); ++i )
7355     {
7356       _EdgesOnShape*   eoe = eov._eosC1[i];
7357       const TopoDS_Edge& e = TopoDS::Edge( eoe->_shape );
7358       const double    eLen = SMESH_Algo::EdgeLength( e );
7359       TopoDS_Shape    oppV = SMESH_MesherHelper::IthVertex( 0, e );
7360       if ( oppV.IsSame( eov._shape ))
7361         oppV = SMESH_MesherHelper::IthVertex( 1, e );
7362       _EdgesOnShape* eovOpp = data.GetShapeEdges( oppV );
7363       if ( !eovOpp || eovOpp->_edges.empty() ) continue;
7364       if ( eov._edges[0]->Is( _LayerEdge::BLOCKED )) continue;
7365
7366       double curThickOpp = eovOpp->_edges[0]->_len * eovOpp->_edges[0]->_lenFactor;
7367       if ( curThickOpp + curThick < eLen )
7368         continue;
7369
7370       double wgt = 2. * curThick / eLen;
7371       newNorm += wgt * eovOpp->_edges[0]->_normal;
7372       normChanged = true;
7373     }
7374     if ( normChanged )
7375     {
7376       eov._edges[0]->SetNormal( newNorm.Normalized() );
7377       eov._edges[0]->Set( _LayerEdge::NORMAL_UPDATED );
7378     }
7379   }
7380 }
7381
7382 //================================================================================
7383 /*!
7384  * \brief Modify normals of _LayerEdge's on _ConvexFace's
7385  */
7386 //================================================================================
7387
7388 bool _ViscousBuilder::updateNormalsOfConvexFaces( _SolidData&         data,
7389                                                   SMESH_MesherHelper& helper,
7390                                                   int                 stepNb )
7391 {
7392   SMESHDS_Mesh* meshDS = helper.GetMeshDS();
7393   bool isOK;
7394
7395   map< TGeomID, _ConvexFace >::iterator id2face = data._convexFaces.begin();
7396   for ( ; id2face != data._convexFaces.end(); ++id2face )
7397   {
7398     _ConvexFace & convFace = (*id2face).second;
7399     convFace._normalsFixedOnBorders = false; // to update at each inflation step
7400
7401     if ( convFace._normalsFixed )
7402       continue; // already fixed
7403     if ( convFace.CheckPrisms() )
7404       continue; // nothing to fix
7405
7406     convFace._normalsFixed = true;
7407
7408     BRepAdaptor_Surface surface ( convFace._face, false );
7409     BRepLProp_SLProps   surfProp( surface, 2, 1e-6 );
7410
7411     // check if the convex FACE is of spherical shape
7412
7413     Bnd_B3d centersBox; // bbox of centers of curvature of _LayerEdge's on VERTEXes
7414     Bnd_B3d nodesBox;
7415     gp_Pnt  center;
7416
7417     map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.begin();
7418     for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7419     {
7420       _EdgesOnShape& eos = *(id2eos->second);
7421       if ( eos.ShapeType() == TopAbs_VERTEX )
7422       {
7423         _LayerEdge* ledge = eos._edges[ 0 ];
7424         if ( convFace.GetCenterOfCurvature( ledge, surfProp, helper, center ))
7425           centersBox.Add( center );
7426       }
7427       for ( size_t i = 0; i < eos._edges.size(); ++i )
7428         nodesBox.Add( SMESH_TNodeXYZ( eos._edges[ i ]->_nodes[0] ));
7429     }
7430     if ( centersBox.IsVoid() )
7431     {
7432       debugMsg( "Error: centersBox.IsVoid()" );
7433       return false;
7434     }
7435     const bool isSpherical =
7436       ( centersBox.SquareExtent() < 1e-6 * nodesBox.SquareExtent() );
7437
7438     int nbEdges = helper.Count( convFace._face, TopAbs_EDGE, /*ignoreSame=*/false );
7439     vector < _CentralCurveOnEdge > centerCurves( nbEdges );
7440
7441     if ( isSpherical )
7442     {
7443       // set _LayerEdge::_normal as average of all normals
7444
7445       // WARNING: different density of nodes on EDGEs is not taken into account that
7446       // can lead to an improper new normal
7447
7448       gp_XYZ avgNormal( 0,0,0 );
7449       nbEdges = 0;
7450       id2eos = convFace._subIdToEOS.begin();
7451       for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7452       {
7453         _EdgesOnShape& eos = *(id2eos->second);
7454         // set data of _CentralCurveOnEdge
7455         if ( eos.ShapeType() == TopAbs_EDGE )
7456         {
7457           _CentralCurveOnEdge& ceCurve = centerCurves[ nbEdges++ ];
7458           ceCurve.SetShapes( TopoDS::Edge( eos._shape ), convFace, data, helper );
7459           if ( !eos._sWOL.IsNull() )
7460             ceCurve._adjFace.Nullify();
7461           else
7462             ceCurve._ledges.insert( ceCurve._ledges.end(),
7463                                     eos._edges.begin(), eos._edges.end());
7464         }
7465         // summarize normals
7466         for ( size_t i = 0; i < eos._edges.size(); ++i )
7467           avgNormal += eos._edges[ i ]->_normal;
7468       }
7469       double normSize = avgNormal.SquareModulus();
7470       if ( normSize < 1e-200 )
7471       {
7472         debugMsg( "updateNormalsOfConvexFaces(): zero avgNormal" );
7473         return false;
7474       }
7475       avgNormal /= Sqrt( normSize );
7476
7477       // compute new _LayerEdge::_cosin on EDGEs
7478       double avgCosin = 0;
7479       int     nbCosin = 0;
7480       gp_Vec inFaceDir;
7481       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
7482       {
7483         _CentralCurveOnEdge& ceCurve = centerCurves[ iE ];
7484         if ( ceCurve._adjFace.IsNull() )
7485           continue;
7486         for ( size_t iLE = 0; iLE < ceCurve._ledges.size(); ++iLE )
7487         {
7488           const SMDS_MeshNode* node = ceCurve._ledges[ iLE ]->_nodes[0];
7489           inFaceDir = getFaceDir( ceCurve._adjFace, ceCurve._edge, node, helper, isOK );
7490           if ( isOK )
7491           {
7492             double angle = inFaceDir.Angle( avgNormal ); // [0,PI]
7493             ceCurve._ledges[ iLE ]->_cosin = Cos( angle );
7494             avgCosin += ceCurve._ledges[ iLE ]->_cosin;
7495             nbCosin++;
7496           }
7497         }
7498       }
7499       if ( nbCosin > 0 )
7500         avgCosin /= nbCosin;
7501
7502       // set _LayerEdge::_normal = avgNormal
7503       id2eos = convFace._subIdToEOS.begin();
7504       for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7505       {
7506         _EdgesOnShape& eos = *(id2eos->second);
7507         if ( eos.ShapeType() != TopAbs_EDGE )
7508           for ( size_t i = 0; i < eos._edges.size(); ++i )
7509             eos._edges[ i ]->_cosin = avgCosin;
7510
7511         for ( size_t i = 0; i < eos._edges.size(); ++i )
7512         {
7513           eos._edges[ i ]->SetNormal( avgNormal );
7514           eos._edges[ i ]->Set( _LayerEdge::NORMAL_UPDATED );
7515         }
7516       }
7517     }
7518     else // if ( isSpherical )
7519     {
7520       // We suppose that centers of curvature at all points of the FACE
7521       // lie on some curve, let's call it "central curve". For all _LayerEdge's
7522       // having a common center of curvature we define the same new normal
7523       // as a sum of normals of _LayerEdge's on EDGEs among them.
7524
7525       // get all centers of curvature for each EDGE
7526
7527       helper.SetSubShape( convFace._face );
7528       _LayerEdge* vertexLEdges[2], **edgeLEdge, **edgeLEdgeEnd;
7529
7530       TopExp_Explorer edgeExp( convFace._face, TopAbs_EDGE );
7531       for ( int iE = 0; edgeExp.More(); edgeExp.Next(), ++iE )
7532       {
7533         const TopoDS_Edge& edge = TopoDS::Edge( edgeExp.Current() );
7534
7535         // set adjacent FACE
7536         centerCurves[ iE ].SetShapes( edge, convFace, data, helper );
7537
7538         // get _LayerEdge's of the EDGE
7539         TGeomID edgeID = meshDS->ShapeToIndex( edge );
7540         _EdgesOnShape* eos = data.GetShapeEdges( edgeID );
7541         if ( !eos || eos->_edges.empty() )
7542         {
7543           // no _LayerEdge's on EDGE, use _LayerEdge's on VERTEXes
7544           for ( int iV = 0; iV < 2; ++iV )
7545           {
7546             TopoDS_Vertex v = helper.IthVertex( iV, edge );
7547             TGeomID     vID = meshDS->ShapeToIndex( v );
7548             eos = data.GetShapeEdges( vID );
7549             vertexLEdges[ iV ] = eos->_edges[ 0 ];
7550           }
7551           edgeLEdge    = &vertexLEdges[0];
7552           edgeLEdgeEnd = edgeLEdge + 2;
7553
7554           centerCurves[ iE ]._adjFace.Nullify();
7555         }
7556         else
7557         {
7558           if ( ! eos->_toSmooth )
7559             data.SortOnEdge( edge, eos->_edges );
7560           edgeLEdge    = &eos->_edges[ 0 ];
7561           edgeLEdgeEnd = edgeLEdge + eos->_edges.size();
7562           vertexLEdges[0] = eos->_edges.front()->_2neibors->_edges[0];
7563           vertexLEdges[1] = eos->_edges.back() ->_2neibors->_edges[1];
7564
7565           if ( ! eos->_sWOL.IsNull() )
7566             centerCurves[ iE ]._adjFace.Nullify();
7567         }
7568
7569         // Get curvature centers
7570
7571         centersBox.Clear();
7572
7573         if ( edgeLEdge[0]->IsOnEdge() &&
7574              convFace.GetCenterOfCurvature( vertexLEdges[0], surfProp, helper, center ))
7575         { // 1st VERTEX
7576           centerCurves[ iE ].Append( center, vertexLEdges[0] );
7577           centersBox.Add( center );
7578         }
7579         for ( ; edgeLEdge < edgeLEdgeEnd; ++edgeLEdge )
7580           if ( convFace.GetCenterOfCurvature( *edgeLEdge, surfProp, helper, center ))
7581           { // EDGE or VERTEXes
7582             centerCurves[ iE ].Append( center, *edgeLEdge );
7583             centersBox.Add( center );
7584           }
7585         if ( edgeLEdge[-1]->IsOnEdge() &&
7586              convFace.GetCenterOfCurvature( vertexLEdges[1], surfProp, helper, center ))
7587         { // 2nd VERTEX
7588           centerCurves[ iE ].Append( center, vertexLEdges[1] );
7589           centersBox.Add( center );
7590         }
7591         centerCurves[ iE ]._isDegenerated =
7592           ( centersBox.IsVoid() || centersBox.SquareExtent() < 1e-6 * nodesBox.SquareExtent() );
7593
7594       } // loop on EDGES of convFace._face to set up data of centerCurves
7595
7596       // Compute new normals for _LayerEdge's on EDGEs
7597
7598       double avgCosin = 0;
7599       int     nbCosin = 0;
7600       gp_Vec inFaceDir;
7601       for ( size_t iE1 = 0; iE1 < centerCurves.size(); ++iE1 )
7602       {
7603         _CentralCurveOnEdge& ceCurve = centerCurves[ iE1 ];
7604         if ( ceCurve._isDegenerated )
7605           continue;
7606         const vector< gp_Pnt >& centers = ceCurve._curvaCenters;
7607         vector< gp_XYZ > &   newNormals = ceCurve._normals;
7608         for ( size_t iC1 = 0; iC1 < centers.size(); ++iC1 )
7609         {
7610           isOK = false;
7611           for ( size_t iE2 = 0; iE2 < centerCurves.size() && !isOK; ++iE2 )
7612           {
7613             if ( iE1 != iE2 )
7614               isOK = centerCurves[ iE2 ].FindNewNormal( centers[ iC1 ], newNormals[ iC1 ]);
7615           }
7616           if ( isOK && !ceCurve._adjFace.IsNull() )
7617           {
7618             // compute new _LayerEdge::_cosin
7619             const SMDS_MeshNode* node = ceCurve._ledges[ iC1 ]->_nodes[0];
7620             inFaceDir = getFaceDir( ceCurve._adjFace, ceCurve._edge, node, helper, isOK );
7621             if ( isOK )
7622             {
7623               double angle = inFaceDir.Angle( newNormals[ iC1 ] ); // [0,PI]
7624               ceCurve._ledges[ iC1 ]->_cosin = Cos( angle );
7625               avgCosin += ceCurve._ledges[ iC1 ]->_cosin;
7626               nbCosin++;
7627             }
7628           }
7629         }
7630       }
7631       // set new normals to _LayerEdge's of NOT degenerated central curves
7632       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
7633       {
7634         if ( centerCurves[ iE ]._isDegenerated )
7635           continue;
7636         for ( size_t iLE = 0; iLE < centerCurves[ iE ]._ledges.size(); ++iLE )
7637         {
7638           centerCurves[ iE ]._ledges[ iLE ]->SetNormal( centerCurves[ iE ]._normals[ iLE ]);
7639           centerCurves[ iE ]._ledges[ iLE ]->Set( _LayerEdge::NORMAL_UPDATED );
7640         }
7641       }
7642       // set new normals to _LayerEdge's of     degenerated central curves
7643       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
7644       {
7645         if ( !centerCurves[ iE ]._isDegenerated ||
7646              centerCurves[ iE ]._ledges.size() < 3 )
7647           continue;
7648         // new normal is an average of new normals at VERTEXes that
7649         // was computed on non-degenerated _CentralCurveOnEdge's
7650         gp_XYZ newNorm = ( centerCurves[ iE ]._ledges.front()->_normal +
7651                            centerCurves[ iE ]._ledges.back ()->_normal );
7652         double sz = newNorm.Modulus();
7653         if ( sz < 1e-200 )
7654           continue;
7655         newNorm /= sz;
7656         double newCosin = ( 0.5 * centerCurves[ iE ]._ledges.front()->_cosin +
7657                             0.5 * centerCurves[ iE ]._ledges.back ()->_cosin );
7658         for ( size_t iLE = 1, nb = centerCurves[ iE ]._ledges.size() - 1; iLE < nb; ++iLE )
7659         {
7660           centerCurves[ iE ]._ledges[ iLE ]->SetNormal( newNorm );
7661           centerCurves[ iE ]._ledges[ iLE ]->_cosin   = newCosin;
7662           centerCurves[ iE ]._ledges[ iLE ]->Set( _LayerEdge::NORMAL_UPDATED );
7663         }
7664       }
7665
7666       // Find new normals for _LayerEdge's based on FACE
7667
7668       if ( nbCosin > 0 )
7669         avgCosin /= nbCosin;
7670       const TGeomID faceID = meshDS->ShapeToIndex( convFace._face );
7671       map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.find( faceID );
7672       if ( id2eos != convFace._subIdToEOS.end() )
7673       {
7674         int iE = 0;
7675         gp_XYZ newNorm;
7676         _EdgesOnShape& eos = * ( id2eos->second );
7677         for ( size_t i = 0; i < eos._edges.size(); ++i )
7678         {
7679           _LayerEdge* ledge = eos._edges[ i ];
7680           if ( !convFace.GetCenterOfCurvature( ledge, surfProp, helper, center ))
7681             continue;
7682           for ( size_t i = 0; i < centerCurves.size(); ++i, ++iE )
7683           {
7684             iE = iE % centerCurves.size();
7685             if ( centerCurves[ iE ]._isDegenerated )
7686               continue;
7687             newNorm.SetCoord( 0,0,0 );
7688             if ( centerCurves[ iE ].FindNewNormal( center, newNorm ))
7689             {
7690               ledge->SetNormal( newNorm );
7691               ledge->_cosin  = avgCosin;
7692               ledge->Set( _LayerEdge::NORMAL_UPDATED );
7693               break;
7694             }
7695           }
7696         }
7697       }
7698
7699     } // not a quasi-spherical FACE
7700
7701     // Update _LayerEdge's data according to a new normal
7702
7703     dumpFunction(SMESH_Comment("updateNormalsOfConvexFaces")<<data._index
7704                  <<"_F"<<meshDS->ShapeToIndex( convFace._face ));
7705
7706     id2eos = convFace._subIdToEOS.begin();
7707     for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
7708     {
7709       _EdgesOnShape& eos = * ( id2eos->second );
7710       for ( size_t i = 0; i < eos._edges.size(); ++i )
7711       {
7712         _LayerEdge* & ledge = eos._edges[ i ];
7713         double len = ledge->_len;
7714         ledge->InvalidateStep( stepNb + 1, eos, /*restoreLength=*/true );
7715         ledge->SetCosin( ledge->_cosin );
7716         ledge->SetNewLength( len, eos, helper );
7717       }
7718       if ( eos.ShapeType() != TopAbs_FACE )
7719         for ( size_t i = 0; i < eos._edges.size(); ++i )
7720         {
7721           _LayerEdge* ledge = eos._edges[ i ];
7722           for ( size_t iN = 0; iN < ledge->_neibors.size(); ++iN )
7723           {
7724             _LayerEdge* neibor = ledge->_neibors[iN];
7725             if ( neibor->_nodes[0]->GetPosition()->GetDim() == 2 )
7726             {
7727               neibor->Set( _LayerEdge::NEAR_BOUNDARY );
7728               neibor->Set( _LayerEdge::MOVED );
7729               neibor->SetSmooLen( neibor->_len );
7730             }
7731           }
7732         }
7733     } // loop on sub-shapes of convFace._face
7734
7735     // Find FACEs adjacent to convFace._face that got necessity to smooth
7736     // as a result of normals modification
7737
7738     set< _EdgesOnShape* > adjFacesToSmooth;
7739     for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
7740     {
7741       if ( centerCurves[ iE ]._adjFace.IsNull() ||
7742            centerCurves[ iE ]._adjFaceToSmooth )
7743         continue;
7744       for ( size_t iLE = 0; iLE < centerCurves[ iE ]._ledges.size(); ++iLE )
7745       {
7746         if ( centerCurves[ iE ]._ledges[ iLE ]->_cosin > theMinSmoothCosin )
7747         {
7748           adjFacesToSmooth.insert( data.GetShapeEdges( centerCurves[ iE ]._adjFace ));
7749           break;
7750         }
7751       }
7752     }
7753     data.AddShapesToSmooth( adjFacesToSmooth );
7754
7755     dumpFunctionEnd();
7756
7757
7758   } // loop on data._convexFaces
7759
7760   return true;
7761 }
7762
7763 //================================================================================
7764 /*!
7765  * \brief Return max curvature of a FACE
7766  */
7767 //================================================================================
7768
7769 double _ConvexFace::GetMaxCurvature( _SolidData&         data,
7770                                      _EdgesOnShape&      eof,
7771                                      BRepLProp_SLProps&  surfProp,
7772                                      SMESH_MesherHelper& helper)
7773 {
7774   double maxCurvature = 0;
7775
7776   TopoDS_Face F = TopoDS::Face( eof._shape );
7777
7778   const int           nbTestPnt = 5;
7779   const double        oriFactor = ( F.Orientation() == TopAbs_REVERSED ? +1. : -1. );
7780   SMESH_subMeshIteratorPtr smIt = eof._subMesh->getDependsOnIterator(/*includeSelf=*/true);
7781   while ( smIt->more() )
7782   {
7783     SMESH_subMesh* sm = smIt->next();
7784     const TGeomID subID = sm->GetId();
7785
7786     // find _LayerEdge's of a sub-shape
7787     _EdgesOnShape* eos;
7788     if (( eos = data.GetShapeEdges( subID )))
7789       this->_subIdToEOS.insert( make_pair( subID, eos ));
7790     else
7791       continue;
7792
7793     // check concavity and curvature and limit data._stepSize
7794     const double minCurvature =
7795       1. / ( eos->_hyp.GetTotalThickness() * ( 1 + theThickToIntersection ));
7796     size_t iStep = Max( 1, eos->_edges.size() / nbTestPnt );
7797     for ( size_t i = 0; i < eos->_edges.size(); i += iStep )
7798     {
7799       gp_XY uv = helper.GetNodeUV( F, eos->_edges[ i ]->_nodes[0] );
7800       surfProp.SetParameters( uv.X(), uv.Y() );
7801       if ( surfProp.IsCurvatureDefined() )
7802       {
7803         double curvature = Max( surfProp.MaxCurvature() * oriFactor,
7804                                 surfProp.MinCurvature() * oriFactor );
7805         maxCurvature = Max( maxCurvature, curvature );
7806
7807         if ( curvature > minCurvature )
7808           this->_isTooCurved = true;
7809       }
7810     }
7811   } // loop on sub-shapes of the FACE
7812
7813   return maxCurvature;
7814 }
7815
7816 //================================================================================
7817 /*!
7818  * \brief Finds a center of curvature of a surface at a _LayerEdge
7819  */
7820 //================================================================================
7821
7822 bool _ConvexFace::GetCenterOfCurvature( _LayerEdge*         ledge,
7823                                         BRepLProp_SLProps&  surfProp,
7824                                         SMESH_MesherHelper& helper,
7825                                         gp_Pnt &            center ) const
7826 {
7827   gp_XY uv = helper.GetNodeUV( _face, ledge->_nodes[0] );
7828   surfProp.SetParameters( uv.X(), uv.Y() );
7829   if ( !surfProp.IsCurvatureDefined() )
7830     return false;
7831
7832   const double oriFactor = ( _face.Orientation() == TopAbs_REVERSED ? +1. : -1. );
7833   double surfCurvatureMax = surfProp.MaxCurvature() * oriFactor;
7834   double surfCurvatureMin = surfProp.MinCurvature() * oriFactor;
7835   if ( surfCurvatureMin > surfCurvatureMax )
7836     center = surfProp.Value().Translated( surfProp.Normal().XYZ() / surfCurvatureMin * oriFactor );
7837   else
7838     center = surfProp.Value().Translated( surfProp.Normal().XYZ() / surfCurvatureMax * oriFactor );
7839
7840   return true;
7841 }
7842
7843 //================================================================================
7844 /*!
7845  * \brief Check that prisms are not distorted
7846  */
7847 //================================================================================
7848
7849 bool _ConvexFace::CheckPrisms() const
7850 {
7851   double vol = 0;
7852   for ( size_t i = 0; i < _simplexTestEdges.size(); ++i )
7853   {
7854     const _LayerEdge* edge = _simplexTestEdges[i];
7855     SMESH_TNodeXYZ tgtXYZ( edge->_nodes.back() );
7856     for ( size_t j = 0; j < edge->_simplices.size(); ++j )
7857       if ( !edge->_simplices[j].IsForward( edge->_nodes[0], tgtXYZ, vol ))
7858       {
7859         debugMsg( "Bad simplex of _simplexTestEdges ("
7860                   << " "<< edge->_nodes[0]->GetID()<< " "<< tgtXYZ._node->GetID()
7861                   << " "<< edge->_simplices[j]._nPrev->GetID()
7862                   << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
7863         return false;
7864       }
7865   }
7866   return true;
7867 }
7868
7869 //================================================================================
7870 /*!
7871  * \brief Try to compute a new normal by interpolating normals of _LayerEdge's
7872  *        stored in this _CentralCurveOnEdge.
7873  *  \param [in] center - curvature center of a point of another _CentralCurveOnEdge.
7874  *  \param [in,out] newNormal - current normal at this point, to be redefined
7875  *  \return bool - true if succeeded.
7876  */
7877 //================================================================================
7878
7879 bool _CentralCurveOnEdge::FindNewNormal( const gp_Pnt& center, gp_XYZ& newNormal )
7880 {
7881   if ( this->_isDegenerated )
7882     return false;
7883
7884   // find two centers the given one lies between
7885
7886   for ( size_t i = 0, nb = _curvaCenters.size()-1;  i < nb;  ++i )
7887   {
7888     double sl2 = 1.001 * _segLength2[ i ];
7889
7890     double d1 = center.SquareDistance( _curvaCenters[ i ]);
7891     if ( d1 > sl2 )
7892       continue;
7893     
7894     double d2 = center.SquareDistance( _curvaCenters[ i+1 ]);
7895     if ( d2 > sl2 || d2 + d1 < 1e-100 )
7896       continue;
7897
7898     d1 = Sqrt( d1 );
7899     d2 = Sqrt( d2 );
7900     double r = d1 / ( d1 + d2 );
7901     gp_XYZ norm = (( 1. - r ) * _ledges[ i   ]->_normal +
7902                    (      r ) * _ledges[ i+1 ]->_normal );
7903     norm.Normalize();
7904
7905     newNormal += norm;
7906     double sz = newNormal.Modulus();
7907     if ( sz < 1e-200 )
7908       break;
7909     newNormal /= sz;
7910     return true;
7911   }
7912   return false;
7913 }
7914
7915 //================================================================================
7916 /*!
7917  * \brief Set shape members
7918  */
7919 //================================================================================
7920
7921 void _CentralCurveOnEdge::SetShapes( const TopoDS_Edge&  edge,
7922                                      const _ConvexFace&  convFace,
7923                                      _SolidData&         data,
7924                                      SMESH_MesherHelper& helper)
7925 {
7926   _edge = edge;
7927
7928   PShapeIteratorPtr fIt = helper.GetAncestors( edge, *helper.GetMesh(), TopAbs_FACE );
7929   while ( const TopoDS_Shape* F = fIt->next())
7930     if ( !convFace._face.IsSame( *F ))
7931     {
7932       _adjFace = TopoDS::Face( *F );
7933       _adjFaceToSmooth = false;
7934       // _adjFace already in a smoothing queue ?
7935       if ( _EdgesOnShape* eos = data.GetShapeEdges( _adjFace ))
7936         _adjFaceToSmooth = eos->_toSmooth;
7937       break;
7938     }
7939 }
7940
7941 //================================================================================
7942 /*!
7943  * \brief Looks for intersection of it's last segment with faces
7944  *  \param distance - returns shortest distance from the last node to intersection
7945  */
7946 //================================================================================
7947
7948 bool _LayerEdge::FindIntersection( SMESH_ElementSearcher&   searcher,
7949                                    double &                 distance,
7950                                    const double&            epsilon,
7951                                    _EdgesOnShape&           eos,
7952                                    const SMDS_MeshElement** intFace)
7953 {
7954   vector< const SMDS_MeshElement* > suspectFaces;
7955   double segLen;
7956   gp_Ax1 lastSegment = LastSegment( segLen, eos );
7957   searcher.GetElementsNearLine( lastSegment, SMDSAbs_Face, suspectFaces );
7958
7959   bool segmentIntersected = false;
7960   distance = Precision::Infinite();
7961   int iFace = -1; // intersected face
7962   for ( size_t j = 0 ; j < suspectFaces.size() /*&& !segmentIntersected*/; ++j )
7963   {
7964     const SMDS_MeshElement* face = suspectFaces[j];
7965     if ( face->GetNodeIndex( _nodes.back() ) >= 0 ||
7966          face->GetNodeIndex( _nodes[0]     ) >= 0 )
7967       continue; // face sharing _LayerEdge node
7968     const int nbNodes = face->NbCornerNodes();
7969     bool intFound = false;
7970     double dist;
7971     SMDS_MeshElement::iterator nIt = face->begin_nodes();
7972     if ( nbNodes == 3 )
7973     {
7974       intFound = SegTriaInter( lastSegment, *nIt++, *nIt++, *nIt++, dist, epsilon );
7975     }
7976     else
7977     {
7978       const SMDS_MeshNode* tria[3];
7979       tria[0] = *nIt++;
7980       tria[1] = *nIt++;
7981       for ( int n2 = 2; n2 < nbNodes && !intFound; ++n2 )
7982       {
7983         tria[2] = *nIt++;
7984         intFound = SegTriaInter(lastSegment, tria[0], tria[1], tria[2], dist, epsilon );
7985         tria[1] = tria[2];
7986       }
7987     }
7988     if ( intFound )
7989     {
7990       if ( dist < segLen*(1.01) && dist > -(_len*_lenFactor-segLen) )
7991         segmentIntersected = true;
7992       if ( distance > dist )
7993         distance = dist, iFace = j;
7994     }
7995   }
7996   if ( intFace ) *intFace = ( iFace != -1 ) ? suspectFaces[iFace] : 0;
7997
7998   distance -= segLen;
7999
8000   if ( segmentIntersected )
8001   {
8002 #ifdef __myDEBUG
8003     SMDS_MeshElement::iterator nIt = suspectFaces[iFace]->begin_nodes();
8004     gp_XYZ intP( lastSegment.Location().XYZ() + lastSegment.Direction().XYZ() * ( distance+segLen ));
8005     cout << "nodes: tgt " << _nodes.back()->GetID() << " src " << _nodes[0]->GetID()
8006          << ", intersection with face ("
8007          << (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()
8008          << ") at point (" << intP.X() << ", " << intP.Y() << ", " << intP.Z()
8009          << ") distance = " << distance << endl;
8010 #endif
8011   }
8012
8013   return segmentIntersected;
8014 }
8015
8016 //================================================================================
8017 /*!
8018  * \brief Returns a point used to check orientation of _simplices
8019  */
8020 //================================================================================
8021
8022 gp_XYZ _LayerEdge::PrevCheckPos( _EdgesOnShape* eos ) const
8023 {
8024   size_t i = Is( NORMAL_UPDATED ) && IsOnFace() ? _pos.size()-2 : 0;
8025
8026   if ( !eos || eos->_sWOL.IsNull() )
8027     return _pos[ i ];
8028
8029   if ( eos->SWOLType() == TopAbs_EDGE )
8030   {
8031     return BRepAdaptor_Curve( TopoDS::Edge( eos->_sWOL )).Value( _pos[i].X() ).XYZ();
8032   }
8033   //else //  TopAbs_FACE
8034
8035   return BRepAdaptor_Surface( TopoDS::Face( eos->_sWOL )).Value(_pos[i].X(), _pos[i].Y() ).XYZ();
8036 }
8037
8038 //================================================================================
8039 /*!
8040  * \brief Returns size and direction of the last segment
8041  */
8042 //================================================================================
8043
8044 gp_Ax1 _LayerEdge::LastSegment(double& segLen, _EdgesOnShape& eos) const
8045 {
8046   // find two non-coincident positions
8047   gp_XYZ orig = _pos.back();
8048   gp_XYZ vec;
8049   int iPrev = _pos.size() - 2;
8050   //const double tol = ( _len > 0 ) ? 0.3*_len : 1e-100; // adjusted for IPAL52478 + PAL22576
8051   const double tol = ( _len > 0 ) ? ( 1e-6 * _len ) : 1e-100;
8052   while ( iPrev >= 0 )
8053   {
8054     vec = orig - _pos[iPrev];
8055     if ( vec.SquareModulus() > tol*tol )
8056       break;
8057     else
8058       iPrev--;
8059   }
8060
8061   // make gp_Ax1
8062   gp_Ax1 segDir;
8063   if ( iPrev < 0 )
8064   {
8065     segDir.SetLocation( SMESH_TNodeXYZ( _nodes[0] ));
8066     segDir.SetDirection( _normal );
8067     segLen = 0;
8068   }
8069   else
8070   {
8071     gp_Pnt pPrev = _pos[ iPrev ];
8072     if ( !eos._sWOL.IsNull() )
8073     {
8074       TopLoc_Location loc;
8075       if ( eos.SWOLType() == TopAbs_EDGE )
8076       {
8077         double f,l;
8078         Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( eos._sWOL ), loc, f,l);
8079         pPrev = curve->Value( pPrev.X() ).Transformed( loc );
8080       }
8081       else
8082       {
8083         Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face( eos._sWOL ), loc );
8084         pPrev = surface->Value( pPrev.X(), pPrev.Y() ).Transformed( loc );
8085       }
8086       vec = SMESH_TNodeXYZ( _nodes.back() ) - pPrev.XYZ();
8087     }
8088     segDir.SetLocation( pPrev );
8089     segDir.SetDirection( vec );
8090     segLen = vec.Modulus();
8091   }
8092
8093   return segDir;
8094 }
8095
8096 //================================================================================
8097 /*!
8098  * \brief Return the last (or \a which) position of the target node on a FACE. 
8099  *  \param [in] F - the FACE this _LayerEdge is inflated along
8100  *  \param [in] which - index of position
8101  *  \return gp_XY - result UV
8102  */
8103 //================================================================================
8104
8105 gp_XY _LayerEdge::LastUV( const TopoDS_Face& F, _EdgesOnShape& eos, int which ) const
8106 {
8107   if ( F.IsSame( eos._sWOL )) // F is my FACE
8108     return gp_XY( _pos.back().X(), _pos.back().Y() );
8109
8110   if ( eos.SWOLType() != TopAbs_EDGE ) // wrong call
8111     return gp_XY( 1e100, 1e100 );
8112
8113   // _sWOL is EDGE of F; _pos.back().X() is the last U on the EDGE
8114   double f, l, u = _pos[ which < 0 ? _pos.size()-1 : which ].X();
8115   Handle(Geom2d_Curve) C2d = BRep_Tool::CurveOnSurface( TopoDS::Edge(eos._sWOL), F, f,l);
8116   if ( !C2d.IsNull() && f <= u && u <= l )
8117     return C2d->Value( u ).XY();
8118
8119   return gp_XY( 1e100, 1e100 );
8120 }
8121
8122 //================================================================================
8123 /*!
8124  * \brief Test intersection of the last segment with a given triangle
8125  *   using Moller-Trumbore algorithm
8126  * Intersection is detected if distance to intersection is less than _LayerEdge._len
8127  */
8128 //================================================================================
8129
8130 bool _LayerEdge::SegTriaInter( const gp_Ax1& lastSegment,
8131                                const gp_XYZ& vert0,
8132                                const gp_XYZ& vert1,
8133                                const gp_XYZ& vert2,
8134                                double&       t,
8135                                const double& EPSILON) const
8136 {
8137   const gp_Pnt& orig = lastSegment.Location();
8138   const gp_Dir& dir  = lastSegment.Direction();
8139
8140   /* calculate distance from vert0 to ray origin */
8141   //gp_XYZ tvec = orig.XYZ() - vert0;
8142
8143   //if ( tvec * dir > EPSILON )
8144     // intersected face is at back side of the temporary face this _LayerEdge belongs to
8145     //return false;
8146
8147   gp_XYZ edge1 = vert1 - vert0;
8148   gp_XYZ edge2 = vert2 - vert0;
8149
8150   /* begin calculating determinant - also used to calculate U parameter */
8151   gp_XYZ pvec = dir.XYZ() ^ edge2;
8152
8153   /* if determinant is near zero, ray lies in plane of triangle */
8154   double det = edge1 * pvec;
8155
8156   const double ANGL_EPSILON = 1e-12;
8157   if ( det > -ANGL_EPSILON && det < ANGL_EPSILON )
8158     return false;
8159
8160   /* calculate distance from vert0 to ray origin */
8161   gp_XYZ tvec = orig.XYZ() - vert0;
8162
8163   /* calculate U parameter and test bounds */
8164   double u = ( tvec * pvec ) / det;
8165   //if (u < 0.0 || u > 1.0)
8166   if ( u < -EPSILON || u > 1.0 + EPSILON )
8167     return false;
8168
8169   /* prepare to test V parameter */
8170   gp_XYZ qvec = tvec ^ edge1;
8171
8172   /* calculate V parameter and test bounds */
8173   double v = (dir.XYZ() * qvec) / det;
8174   //if ( v < 0.0 || u + v > 1.0 )
8175   if ( v < -EPSILON || u + v > 1.0 + EPSILON )
8176     return false;
8177
8178   /* calculate t, ray intersects triangle */
8179   t = (edge2 * qvec) / det;
8180
8181   //return true;
8182   return t > 0.;
8183 }
8184
8185 //================================================================================
8186 /*!
8187  * \brief _LayerEdge, located at a concave VERTEX of a FACE, moves target nodes of
8188  *        neighbor _LayerEdge's by it's own inflation vector.
8189  *  \param [in] eov - EOS of the VERTEX
8190  *  \param [in] eos - EOS of the FACE
8191  *  \param [in] step - inflation step
8192  *  \param [in,out] badSmooEdges - tangled _LayerEdge's
8193  */
8194 //================================================================================
8195
8196 void _LayerEdge::MoveNearConcaVer( const _EdgesOnShape*    eov,
8197                                    const _EdgesOnShape*    eos,
8198                                    const int               step,
8199                                    vector< _LayerEdge* > & badSmooEdges )
8200 {
8201   // check if any of _neibors is in badSmooEdges
8202   if ( std::find_first_of( _neibors.begin(), _neibors.end(),
8203                            badSmooEdges.begin(), badSmooEdges.end() ) == _neibors.end() )
8204     return;
8205
8206   // get all edges to move
8207
8208   set< _LayerEdge* > edges;
8209
8210   // find a distance between _LayerEdge on VERTEX and its neighbors
8211   gp_XYZ  curPosV = SMESH_TNodeXYZ( _nodes.back() );
8212   double dist2 = 0;
8213   for ( size_t i = 0; i < _neibors.size(); ++i )
8214   {
8215     _LayerEdge* nEdge = _neibors[i];
8216     if ( nEdge->_nodes[0]->getshapeId() == eos->_shapeID )
8217     {
8218       edges.insert( nEdge );
8219       dist2 = Max( dist2, ( curPosV - nEdge->_pos.back() ).SquareModulus() );
8220     }
8221   }
8222   // add _LayerEdge's close to curPosV
8223   size_t nbE;
8224   do {
8225     nbE = edges.size();
8226     for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8227     {
8228       _LayerEdge* edgeF = *e;
8229       for ( size_t i = 0; i < edgeF->_neibors.size(); ++i )
8230       {
8231         _LayerEdge* nEdge = edgeF->_neibors[i];
8232         if ( nEdge->_nodes[0]->getshapeId() == eos->_shapeID &&
8233              dist2 > ( curPosV - nEdge->_pos.back() ).SquareModulus() )
8234           edges.insert( nEdge );
8235       }
8236     }
8237   }
8238   while ( nbE < edges.size() );
8239
8240   // move the target node of the got edges
8241
8242   gp_XYZ prevPosV = PrevPos();
8243   if ( eov->SWOLType() == TopAbs_EDGE )
8244   {
8245     BRepAdaptor_Curve curve ( TopoDS::Edge( eov->_sWOL ));
8246     prevPosV = curve.Value( prevPosV.X() ).XYZ();
8247   }
8248   else if ( eov->SWOLType() == TopAbs_FACE )
8249   {
8250     BRepAdaptor_Surface surface( TopoDS::Face( eov->_sWOL ));
8251     prevPosV = surface.Value( prevPosV.X(), prevPosV.Y() ).XYZ();
8252   }
8253
8254   SMDS_FacePositionPtr fPos;
8255   //double r = 1. - Min( 0.9, step / 10. );
8256   for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8257   {
8258     _LayerEdge*       edgeF = *e;
8259     const gp_XYZ     prevVF = edgeF->PrevPos() - prevPosV;
8260     const gp_XYZ    newPosF = curPosV + prevVF;
8261     SMDS_MeshNode* tgtNodeF = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
8262     tgtNodeF->setXYZ( newPosF.X(), newPosF.Y(), newPosF.Z() );
8263     edgeF->_pos.back() = newPosF;
8264     dumpMoveComm( tgtNodeF, "MoveNearConcaVer" ); // debug
8265
8266     // set _curvature to make edgeF updated by putOnOffsetSurface()
8267     if ( !edgeF->_curvature )
8268       if (( fPos = edgeF->_nodes[0]->GetPosition() ))
8269       {
8270         edgeF->_curvature = new _Curvature;
8271         edgeF->_curvature->_r = 0;
8272         edgeF->_curvature->_k = 0;
8273         edgeF->_curvature->_h2lenRatio = 0;
8274         edgeF->_curvature->_uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
8275       }
8276   }
8277   // gp_XYZ inflationVec( SMESH_TNodeXYZ( _nodes.back() ) -
8278   //                      SMESH_TNodeXYZ( _nodes[0]    ));
8279   // for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8280   // {
8281   //   _LayerEdge*      edgeF = *e;
8282   //   gp_XYZ          newPos = SMESH_TNodeXYZ( edgeF->_nodes[0] ) + inflationVec;
8283   //   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
8284   //   tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8285   //   edgeF->_pos.back() = newPosF;
8286   //   dumpMoveComm( tgtNode, "MoveNearConcaVer" ); // debug
8287   // }
8288
8289   // smooth _LayerEdge's around moved nodes
8290   //size_t nbBadBefore = badSmooEdges.size();
8291   for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
8292   {
8293     _LayerEdge* edgeF = *e;
8294     for ( size_t j = 0; j < edgeF->_neibors.size(); ++j )
8295       if ( edgeF->_neibors[j]->_nodes[0]->getshapeId() == eos->_shapeID )
8296         //&& !edges.count( edgeF->_neibors[j] ))
8297       {
8298         _LayerEdge* edgeFN = edgeF->_neibors[j];
8299         edgeFN->Unset( SMOOTHED );
8300         int nbBad = edgeFN->Smooth( step, /*isConcaFace=*/true, /*findBest=*/true );
8301         // if ( nbBad > 0 )
8302         // {
8303         //   gp_XYZ         newPos = SMESH_TNodeXYZ( edgeFN->_nodes[0] ) + inflationVec;
8304         //   const gp_XYZ& prevPos = edgeFN->_pos[ edgeFN->_pos.size()-2 ];
8305         //   int        nbBadAfter = edgeFN->_simplices.size();
8306         //   double vol;
8307         //   for ( size_t iS = 0; iS < edgeFN->_simplices.size(); ++iS )
8308         //   {
8309         //     nbBadAfter -= edgeFN->_simplices[iS].IsForward( &prevPos, &newPos, vol );
8310         //   }
8311         //   if ( nbBadAfter <= nbBad )
8312         //   {
8313         //     SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeFN->_nodes.back() );
8314         //     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8315         //     edgeF->_pos.back() = newPosF;
8316         //     dumpMoveComm( tgtNode, "MoveNearConcaVer 2" ); // debug
8317         //     nbBad = nbBadAfter;
8318         //   }
8319         // }
8320         if ( nbBad > 0 )
8321           badSmooEdges.push_back( edgeFN );
8322       }
8323   }
8324     // move a bit not smoothed around moved nodes
8325   //   for ( size_t i = nbBadBefore; i < badSmooEdges.size(); ++i )
8326   //   {
8327   //   _LayerEdge*      edgeF = badSmooEdges[i];
8328   //   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
8329   //   gp_XYZ         newPos1 = SMESH_TNodeXYZ( edgeF->_nodes[0] ) + inflationVec;
8330   //   gp_XYZ         newPos2 = 0.5 * ( newPos1 + SMESH_TNodeXYZ( tgtNode ));
8331   //   tgtNode->setXYZ( newPos2.X(), newPos2.Y(), newPos2.Z() );
8332   //   edgeF->_pos.back() = newPosF;
8333   //   dumpMoveComm( tgtNode, "MoveNearConcaVer 2" ); // debug
8334   // }
8335 }
8336
8337 //================================================================================
8338 /*!
8339  * \brief Perform smooth of _LayerEdge's based on EDGE's
8340  *  \retval bool - true if node has been moved
8341  */
8342 //================================================================================
8343
8344 bool _LayerEdge::SmoothOnEdge(Handle(ShapeAnalysis_Surface)& surface,
8345                               const TopoDS_Face&             F,
8346                               SMESH_MesherHelper&            helper)
8347 {
8348   ASSERT( IsOnEdge() );
8349
8350   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _nodes.back() );
8351   SMESH_TNodeXYZ oldPos( tgtNode );
8352   double dist01, distNewOld;
8353   
8354   SMESH_TNodeXYZ p0( _2neibors->tgtNode(0));
8355   SMESH_TNodeXYZ p1( _2neibors->tgtNode(1));
8356   dist01 = p0.Distance( _2neibors->tgtNode(1) );
8357
8358   gp_Pnt newPos = p0 * _2neibors->_wgt[0] + p1 * _2neibors->_wgt[1];
8359   double lenDelta = 0;
8360   if ( _curvature )
8361   {
8362     //lenDelta = _curvature->lenDelta( _len );
8363     lenDelta = _curvature->lenDeltaByDist( dist01 );
8364     newPos.ChangeCoord() += _normal * lenDelta;
8365   }
8366
8367   distNewOld = newPos.Distance( oldPos );
8368
8369   if ( F.IsNull() )
8370   {
8371     if ( _2neibors->_plnNorm )
8372     {
8373       // put newPos on the plane defined by source node and _plnNorm
8374       gp_XYZ new2src = SMESH_TNodeXYZ( _nodes[0] ) - newPos.XYZ();
8375       double new2srcProj = (*_2neibors->_plnNorm) * new2src;
8376       newPos.ChangeCoord() += (*_2neibors->_plnNorm) * new2srcProj;
8377     }
8378     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8379     _pos.back() = newPos.XYZ();
8380   }
8381   else
8382   {
8383     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8384     gp_XY uv( Precision::Infinite(), 0 );
8385     helper.CheckNodeUV( F, tgtNode, uv, 1e-10, /*force=*/true );
8386     _pos.back().SetCoord( uv.X(), uv.Y(), 0 );
8387
8388     newPos = surface->Value( uv );
8389     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
8390   }
8391
8392   // commented for IPAL0052478
8393   // if ( _curvature && lenDelta < 0 )
8394   // {
8395   //   gp_Pnt prevPos( _pos[ _pos.size()-2 ]);
8396   //   _len -= prevPos.Distance( oldPos );
8397   //   _len += prevPos.Distance( newPos );
8398   // }
8399   bool moved = distNewOld > dist01/50;
8400   //if ( moved )
8401   dumpMove( tgtNode ); // debug
8402
8403   return moved;
8404 }
8405
8406 //================================================================================
8407 /*!
8408  * \brief Perform 3D smooth of nodes inflated from FACE. No check of validity
8409  */
8410 //================================================================================
8411
8412 void _LayerEdge::SmoothWoCheck()
8413 {
8414   if ( Is( DIFFICULT ))
8415     return;
8416
8417   bool moved = Is( SMOOTHED );
8418   for ( size_t i = 0; i < _neibors.size()  &&  !moved; ++i )
8419     moved = _neibors[i]->Is( SMOOTHED );
8420   if ( !moved )
8421     return;
8422
8423   gp_XYZ newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
8424
8425   SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
8426   n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
8427   _pos.back() = newPos;
8428
8429   dumpMoveComm( n, SMESH_Comment("No check - ") << _funNames[ smooFunID() ]);
8430 }
8431
8432 //================================================================================
8433 /*!
8434  * \brief Checks validity of _neibors on EDGEs and VERTEXes
8435  */
8436 //================================================================================
8437
8438 int _LayerEdge::CheckNeiborsOnBoundary( vector< _LayerEdge* >* badNeibors, bool * needSmooth )
8439 {
8440   if ( ! Is( NEAR_BOUNDARY ))
8441     return 0;
8442
8443   int nbBad = 0;
8444   double vol;
8445   for ( size_t iN = 0; iN < _neibors.size(); ++iN )
8446   {
8447     _LayerEdge* eN = _neibors[iN];
8448     if ( eN->_nodes[0]->getshapeId() == _nodes[0]->getshapeId() )
8449       continue;
8450     if ( needSmooth )
8451       *needSmooth |= ( eN->Is( _LayerEdge::BLOCKED ) ||
8452                        eN->Is( _LayerEdge::NORMAL_UPDATED ) ||
8453                        eN->_pos.size() != _pos.size() );
8454
8455     SMESH_TNodeXYZ curPosN ( eN->_nodes.back() );
8456     SMESH_TNodeXYZ prevPosN( eN->_nodes[0] );
8457     for ( size_t i = 0; i < eN->_simplices.size(); ++i )
8458       if ( eN->_nodes.size() > 1 &&
8459            eN->_simplices[i].Includes( _nodes.back() ) &&
8460            !eN->_simplices[i].IsForward( &prevPosN, &curPosN, vol ))
8461       {
8462         ++nbBad;
8463         if ( badNeibors )
8464         {
8465           badNeibors->push_back( eN );
8466           debugMsg("Bad boundary simplex ( "
8467                    << " "<< eN->_nodes[0]->GetID()
8468                    << " "<< eN->_nodes.back()->GetID()
8469                    << " "<< eN->_simplices[i]._nPrev->GetID()
8470                    << " "<< eN->_simplices[i]._nNext->GetID() << " )" );
8471         }
8472         else
8473         {
8474           break;
8475         }
8476       }
8477   }
8478   return nbBad;
8479 }
8480
8481 //================================================================================
8482 /*!
8483  * \brief Perform 'smart' 3D smooth of nodes inflated from FACE
8484  *  \retval int - nb of bad simplices around this _LayerEdge
8485  */
8486 //================================================================================
8487
8488 int _LayerEdge::Smooth(const int step, bool findBest, vector< _LayerEdge* >& toSmooth )
8489 {
8490   if ( !Is( MOVED ) || Is( SMOOTHED ) || Is( BLOCKED ))
8491     return 0; // shape of simplices not changed
8492   if ( _simplices.size() < 2 )
8493     return 0; // _LayerEdge inflated along EDGE or FACE
8494
8495   if ( Is( DIFFICULT )) // || Is( ON_CONCAVE_FACE )
8496     findBest = true;
8497
8498   const gp_XYZ& curPos  = _pos.back();
8499   const gp_XYZ& prevPos = _pos[0]; //PrevPos();
8500
8501   // quality metrics (orientation) of tetras around _tgtNode
8502   int nbOkBefore = 0;
8503   double vol, minVolBefore = 1e100;
8504   for ( size_t i = 0; i < _simplices.size(); ++i )
8505   {
8506     nbOkBefore += _simplices[i].IsForward( &prevPos, &curPos, vol );
8507     minVolBefore = Min( minVolBefore, vol );
8508   }
8509   int nbBad = _simplices.size() - nbOkBefore;
8510
8511   bool bndNeedSmooth = false;
8512   if ( nbBad == 0 )
8513     nbBad = CheckNeiborsOnBoundary( 0, & bndNeedSmooth );
8514   if ( nbBad > 0 )
8515     Set( DISTORTED );
8516
8517   // evaluate min angle
8518   if ( nbBad == 0 && !findBest && !bndNeedSmooth )
8519   {
8520     size_t nbGoodAngles = _simplices.size();
8521     double angle;
8522     for ( size_t i = 0; i < _simplices.size(); ++i )
8523     {
8524       if ( !_simplices[i].IsMinAngleOK( curPos, angle ) && angle > _minAngle )
8525         --nbGoodAngles;
8526     }
8527     if ( nbGoodAngles == _simplices.size() )
8528     {
8529       Unset( MOVED );
8530       return 0;
8531     }
8532   }
8533   if ( Is( ON_CONCAVE_FACE ))
8534     findBest = true;
8535
8536   if ( step % 2 == 0 )
8537     findBest = false;
8538
8539   if ( Is( ON_CONCAVE_FACE ) && !findBest ) // alternate FUN_CENTROIDAL and FUN_LAPLACIAN
8540   {
8541     if ( _smooFunction == _funs[ FUN_LAPLACIAN ] )
8542       _smooFunction = _funs[ FUN_CENTROIDAL ];
8543     else
8544       _smooFunction = _funs[ FUN_LAPLACIAN ];
8545   }
8546
8547   // compute new position for the last _pos using different _funs
8548   gp_XYZ newPos;
8549   bool moved = false;
8550   for ( int iFun = -1; iFun < theNbSmooFuns; ++iFun )
8551   {
8552     if ( iFun < 0 )
8553       newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
8554     else if ( _funs[ iFun ] == _smooFunction )
8555       continue; // _smooFunction again
8556     else if ( step > 1 )
8557       newPos = (this->*_funs[ iFun ])(); // try other smoothing fun
8558     else
8559       break; // let "easy" functions improve elements around distorted ones
8560
8561     if ( _curvature )
8562     {
8563       double delta  = _curvature->lenDelta( _len );
8564       if ( delta > 0 )
8565         newPos += _normal * delta;
8566       else
8567       {
8568         double segLen = _normal * ( newPos - prevPos );
8569         if ( segLen + delta > 0 )
8570           newPos += _normal * delta;
8571       }
8572       // double segLenChange = _normal * ( curPos - newPos );
8573       // newPos += 0.5 * _normal * segLenChange;
8574     }
8575
8576     int nbOkAfter = 0;
8577     double minVolAfter = 1e100;
8578     for ( size_t i = 0; i < _simplices.size(); ++i )
8579     {
8580       nbOkAfter += _simplices[i].IsForward( &prevPos, &newPos, vol );
8581       minVolAfter = Min( minVolAfter, vol );
8582     }
8583     // get worse?
8584     if ( nbOkAfter < nbOkBefore )
8585       continue;
8586
8587     if (( findBest ) &&
8588         ( nbOkAfter == nbOkBefore ) &&
8589         ( minVolAfter <= minVolBefore ))
8590       continue;
8591
8592     nbBad        = _simplices.size() - nbOkAfter;
8593     minVolBefore = minVolAfter;
8594     nbOkBefore   = nbOkAfter;
8595     moved        = true;
8596
8597     SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
8598     n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
8599     _pos.back() = newPos;
8600
8601     dumpMoveComm( n, SMESH_Comment( _funNames[ iFun < 0 ? smooFunID() : iFun ] )
8602                   << (nbBad ? " --BAD" : ""));
8603
8604     if ( iFun > -1 )
8605     {
8606       continue; // look for a better function
8607     }
8608
8609     if ( !findBest )
8610       break;
8611
8612   } // loop on smoothing functions
8613
8614   if ( moved ) // notify _neibors
8615   {
8616     Set( SMOOTHED );
8617     for ( size_t i = 0; i < _neibors.size(); ++i )
8618       if ( !_neibors[i]->Is( MOVED ))
8619       {
8620         _neibors[i]->Set( MOVED );
8621         toSmooth.push_back( _neibors[i] );
8622       }
8623   }
8624
8625   return nbBad;
8626 }
8627
8628 //================================================================================
8629 /*!
8630  * \brief Perform 'smart' 3D smooth of nodes inflated from FACE
8631  *  \retval int - nb of bad simplices around this _LayerEdge
8632  */
8633 //================================================================================
8634
8635 int _LayerEdge::Smooth(const int step, const bool isConcaveFace, bool findBest )
8636 {
8637   if ( !_smooFunction )
8638     return 0; // _LayerEdge inflated along EDGE or FACE
8639   if ( Is( BLOCKED ))
8640     return 0; // not inflated
8641
8642   const gp_XYZ& curPos  = _pos.back();
8643   const gp_XYZ& prevPos = _pos[0]; //PrevCheckPos();
8644
8645   // quality metrics (orientation) of tetras around _tgtNode
8646   int nbOkBefore = 0;
8647   double vol, minVolBefore = 1e100;
8648   for ( size_t i = 0; i < _simplices.size(); ++i )
8649   {
8650     nbOkBefore += _simplices[i].IsForward( &prevPos, &curPos, vol );
8651     minVolBefore = Min( minVolBefore, vol );
8652   }
8653   int nbBad = _simplices.size() - nbOkBefore;
8654
8655   if ( isConcaveFace ) // alternate FUN_CENTROIDAL and FUN_LAPLACIAN
8656   {
8657     if      ( _smooFunction == _funs[ FUN_CENTROIDAL ] && step % 2 )
8658       _smooFunction = _funs[ FUN_LAPLACIAN ];
8659     else if ( _smooFunction == _funs[ FUN_LAPLACIAN ] && !( step % 2 ))
8660       _smooFunction = _funs[ FUN_CENTROIDAL ];
8661   }
8662
8663   // compute new position for the last _pos using different _funs
8664   gp_XYZ newPos;
8665   for ( int iFun = -1; iFun < theNbSmooFuns; ++iFun )
8666   {
8667     if ( iFun < 0 )
8668       newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
8669     else if ( _funs[ iFun ] == _smooFunction )
8670       continue; // _smooFunction again
8671     else if ( step > 1 )
8672       newPos = (this->*_funs[ iFun ])(); // try other smoothing fun
8673     else
8674       break; // let "easy" functions improve elements around distorted ones
8675
8676     if ( _curvature )
8677     {
8678       double delta  = _curvature->lenDelta( _len );
8679       if ( delta > 0 )
8680         newPos += _normal * delta;
8681       else
8682       {
8683         double segLen = _normal * ( newPos - prevPos );
8684         if ( segLen + delta > 0 )
8685           newPos += _normal * delta;
8686       }
8687       // double segLenChange = _normal * ( curPos - newPos );
8688       // newPos += 0.5 * _normal * segLenChange;
8689     }
8690
8691     int nbOkAfter = 0;
8692     double minVolAfter = 1e100;
8693     for ( size_t i = 0; i < _simplices.size(); ++i )
8694     {
8695       nbOkAfter += _simplices[i].IsForward( &prevPos, &newPos, vol );
8696       minVolAfter = Min( minVolAfter, vol );
8697     }
8698     // get worse?
8699     if ( nbOkAfter < nbOkBefore )
8700       continue;
8701     if (( isConcaveFace || findBest ) &&
8702         ( nbOkAfter == nbOkBefore ) &&
8703         ( minVolAfter <= minVolBefore )
8704         )
8705       continue;
8706
8707     nbBad        = _simplices.size() - nbOkAfter;
8708     minVolBefore = minVolAfter;
8709     nbOkBefore   = nbOkAfter;
8710
8711     SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
8712     n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
8713     _pos.back() = newPos;
8714
8715     dumpMoveComm( n, SMESH_Comment( _funNames[ iFun < 0 ? smooFunID() : iFun ] )
8716                   << ( nbBad ? "--BAD" : ""));
8717
8718     // commented for IPAL0052478
8719     // _len -= prevPos.Distance(SMESH_TNodeXYZ( n ));
8720     // _len += prevPos.Distance(newPos);
8721
8722     if ( iFun > -1 ) // findBest || the chosen _fun makes worse
8723     {
8724       //_smooFunction = _funs[ iFun ];
8725       // cout << "# " << _funNames[ iFun ] << "\t N:" << _nodes.back()->GetID()
8726       // << "\t nbBad: " << _simplices.size() - nbOkAfter
8727       // << " minVol: " << minVolAfter
8728       // << " " << newPos.X() << " " << newPos.Y() << " " << newPos.Z()
8729       // << endl;
8730       continue; // look for a better function
8731     }
8732
8733     if ( !findBest )
8734       break;
8735
8736   } // loop on smoothing functions
8737
8738   return nbBad;
8739 }
8740
8741 //================================================================================
8742 /*!
8743  * \brief Chooses a smoothing technic giving a position most close to an initial one.
8744  *        For a correct result, _simplices must contain nodes lying on geometry.
8745  */
8746 //================================================================================
8747
8748 void _LayerEdge::ChooseSmooFunction( const set< TGeomID >& concaveVertices,
8749                                      const TNode2Edge&     n2eMap)
8750 {
8751   if ( _smooFunction ) return;
8752
8753   // use smoothNefPolygon() near concaveVertices
8754   if ( !concaveVertices.empty() )
8755   {
8756     _smooFunction = _funs[ FUN_CENTROIDAL ];
8757
8758     Set( ON_CONCAVE_FACE );
8759
8760     for ( size_t i = 0; i < _simplices.size(); ++i )
8761     {
8762       if ( concaveVertices.count( _simplices[i]._nPrev->getshapeId() ))
8763       {
8764         _smooFunction = _funs[ FUN_NEFPOLY ];
8765
8766         // set FUN_CENTROIDAL to neighbor edges
8767         for ( i = 0; i < _neibors.size(); ++i )
8768         {
8769           if ( _neibors[i]->_nodes[0]->GetPosition()->GetDim() == 2 )
8770           {
8771             _neibors[i]->_smooFunction = _funs[ FUN_CENTROIDAL ];
8772           }
8773         }
8774         return;
8775       }
8776     }
8777
8778     // // this choice is done only if ( !concaveVertices.empty() ) for Grids/smesh/bugs_19/X1
8779     // // where the nodes are smoothed too far along a sphere thus creating
8780     // // inverted _simplices
8781     // double dist[theNbSmooFuns];
8782     // //double coef[theNbSmooFuns] = { 1., 1.2, 1.4, 1.4 };
8783     // double coef[theNbSmooFuns] = { 1., 1., 1., 1. };
8784
8785     // double minDist = Precision::Infinite();
8786     // gp_Pnt p = SMESH_TNodeXYZ( _nodes[0] );
8787     // for ( int i = 0; i < FUN_NEFPOLY; ++i )
8788     // {
8789     //   gp_Pnt newP = (this->*_funs[i])();
8790     //   dist[i] = p.SquareDistance( newP );
8791     //   if ( dist[i]*coef[i] < minDist )
8792     //   {
8793     //     _smooFunction = _funs[i];
8794     //     minDist = dist[i]*coef[i];
8795     //   }
8796     // }
8797   }
8798   else
8799   {
8800     _smooFunction = _funs[ FUN_LAPLACIAN ];
8801   }
8802   // int minDim = 3;
8803   // for ( size_t i = 0; i < _simplices.size(); ++i )
8804   //   minDim = Min( minDim, _simplices[i]._nPrev->GetPosition()->GetDim() );
8805   // if ( minDim == 0 )
8806   //   _smooFunction = _funs[ FUN_CENTROIDAL ];
8807   // else if ( minDim == 1 )
8808   //   _smooFunction = _funs[ FUN_CENTROIDAL ];
8809
8810
8811   // int iMin;
8812   // for ( int i = 0; i < FUN_NB; ++i )
8813   // {
8814   //   //cout << dist[i] << " ";
8815   //   if ( _smooFunction == _funs[i] ) {
8816   //     iMin = i;
8817   //     //debugMsg( fNames[i] );
8818   //     break;
8819   //   }
8820   // }
8821   // cout << _funNames[ iMin ] << "\t N:" << _nodes.back()->GetID() << endl;
8822 }
8823
8824 //================================================================================
8825 /*!
8826  * \brief Returns a name of _SmooFunction
8827  */
8828 //================================================================================
8829
8830 int _LayerEdge::smooFunID( _LayerEdge::PSmooFun fun) const
8831 {
8832   if ( !fun )
8833     fun = _smooFunction;
8834   for ( int i = 0; i < theNbSmooFuns; ++i )
8835     if ( fun == _funs[i] )
8836       return i;
8837
8838   return theNbSmooFuns;
8839 }
8840
8841 //================================================================================
8842 /*!
8843  * \brief Computes a new node position using Laplacian smoothing
8844  */
8845 //================================================================================
8846
8847 gp_XYZ _LayerEdge::smoothLaplacian()
8848 {
8849   gp_XYZ newPos (0,0,0);
8850   for ( size_t i = 0; i < _simplices.size(); ++i )
8851     newPos += SMESH_TNodeXYZ( _simplices[i]._nPrev );
8852   newPos /= _simplices.size();
8853
8854   return newPos;
8855 }
8856
8857 //================================================================================
8858 /*!
8859  * \brief Computes a new node position using angular-based smoothing
8860  */
8861 //================================================================================
8862
8863 gp_XYZ _LayerEdge::smoothAngular()
8864 {
8865   vector< gp_Vec > edgeDir;  edgeDir. reserve( _simplices.size() + 1 );
8866   vector< double > edgeSize; edgeSize.reserve( _simplices.size()     );
8867   vector< gp_XYZ > points;   points.  reserve( _simplices.size() + 1 );
8868
8869   gp_XYZ pPrev = SMESH_TNodeXYZ( _simplices.back()._nPrev );
8870   gp_XYZ pN( 0,0,0 );
8871   for ( size_t i = 0; i < _simplices.size(); ++i )
8872   {
8873     gp_XYZ p = SMESH_TNodeXYZ( _simplices[i]._nPrev );
8874     edgeDir.push_back( p - pPrev );
8875     edgeSize.push_back( edgeDir.back().Magnitude() );
8876     if ( edgeSize.back() < numeric_limits<double>::min() )
8877     {
8878       edgeDir.pop_back();
8879       edgeSize.pop_back();
8880     }
8881     else
8882     {
8883       edgeDir.back() /= edgeSize.back();
8884       points.push_back( p );
8885       pN += p;
8886     }
8887     pPrev = p;
8888   }
8889   edgeDir.push_back ( edgeDir[0] );
8890   edgeSize.push_back( edgeSize[0] );
8891   pN /= points.size();
8892
8893   gp_XYZ newPos(0,0,0);
8894   double sumSize = 0;
8895   for ( size_t i = 0; i < points.size(); ++i )
8896   {
8897     gp_Vec toN    = pN - points[i];
8898     double toNLen = toN.Magnitude();
8899     if ( toNLen < numeric_limits<double>::min() )
8900     {
8901       newPos += pN;
8902       continue;
8903     }
8904     gp_Vec bisec    = edgeDir[i] + edgeDir[i+1];
8905     double bisecLen = bisec.SquareMagnitude();
8906     if ( bisecLen < numeric_limits<double>::min() )
8907     {
8908       gp_Vec norm = edgeDir[i] ^ toN;
8909       bisec = norm ^ edgeDir[i];
8910       bisecLen = bisec.SquareMagnitude();
8911     }
8912     bisecLen = Sqrt( bisecLen );
8913     bisec /= bisecLen;
8914
8915 #if 1
8916     gp_XYZ pNew = ( points[i] + bisec.XYZ() * toNLen ) * bisecLen;
8917     sumSize += bisecLen;
8918 #else
8919     gp_XYZ pNew = ( points[i] + bisec.XYZ() * toNLen ) * ( edgeSize[i] + edgeSize[i+1] );
8920     sumSize += ( edgeSize[i] + edgeSize[i+1] );
8921 #endif
8922     newPos += pNew;
8923   }
8924   newPos /= sumSize;
8925
8926   // project newPos to an average plane
8927
8928   gp_XYZ norm(0,0,0); // plane normal
8929   points.push_back( points[0] );
8930   for ( size_t i = 1; i < points.size(); ++i )
8931   {
8932     gp_XYZ vec1 = points[ i-1 ] - pN;
8933     gp_XYZ vec2 = points[ i   ] - pN;
8934     gp_XYZ cross = vec1 ^ vec2;
8935     try {
8936       cross.Normalize();
8937       if ( cross * norm < numeric_limits<double>::min() )
8938         norm += cross.Reversed();
8939       else
8940         norm += cross;
8941     }
8942     catch (Standard_Failure) { // if |cross| == 0.
8943     }
8944   }
8945   gp_XYZ vec = newPos - pN;
8946   double r   = ( norm * vec ) / norm.SquareModulus();  // param [0,1] on norm
8947   newPos     = newPos - r * norm;
8948
8949   return newPos;
8950 }
8951
8952 //================================================================================
8953 /*!
8954  * \brief Computes a new node position using weigthed node positions
8955  */
8956 //================================================================================
8957
8958 gp_XYZ _LayerEdge::smoothLengthWeighted()
8959 {
8960   vector< double > edgeSize; edgeSize.reserve( _simplices.size() + 1);
8961   vector< gp_XYZ > points;   points.  reserve( _simplices.size() );
8962
8963   gp_XYZ pPrev = SMESH_TNodeXYZ( _simplices.back()._nPrev );
8964   for ( size_t i = 0; i < _simplices.size(); ++i )
8965   {
8966     gp_XYZ p = SMESH_TNodeXYZ( _simplices[i]._nPrev );
8967     edgeSize.push_back( ( p - pPrev ).Modulus() );
8968     if ( edgeSize.back() < numeric_limits<double>::min() )
8969     {
8970       edgeSize.pop_back();
8971     }
8972     else
8973     {
8974       points.push_back( p );
8975     }
8976     pPrev = p;
8977   }
8978   edgeSize.push_back( edgeSize[0] );
8979
8980   gp_XYZ newPos(0,0,0);
8981   double sumSize = 0;
8982   for ( size_t i = 0; i < points.size(); ++i )
8983   {
8984     newPos += points[i] * ( edgeSize[i] + edgeSize[i+1] );
8985     sumSize += edgeSize[i] + edgeSize[i+1];
8986   }
8987   newPos /= sumSize;
8988   return newPos;
8989 }
8990
8991 //================================================================================
8992 /*!
8993  * \brief Computes a new node position using angular-based smoothing
8994  */
8995 //================================================================================
8996
8997 gp_XYZ _LayerEdge::smoothCentroidal()
8998 {
8999   gp_XYZ newPos(0,0,0);
9000   gp_XYZ pN = SMESH_TNodeXYZ( _nodes.back() );
9001   double sumSize = 0;
9002   for ( size_t i = 0; i < _simplices.size(); ++i )
9003   {
9004     gp_XYZ p1 = SMESH_TNodeXYZ( _simplices[i]._nPrev );
9005     gp_XYZ p2 = SMESH_TNodeXYZ( _simplices[i]._nNext );
9006     gp_XYZ gc = ( pN + p1 + p2 ) / 3.;
9007     double size = (( p1 - pN ) ^ ( p2 - pN )).Modulus();
9008
9009     sumSize += size;
9010     newPos += gc * size;
9011   }
9012   newPos /= sumSize;
9013
9014   return newPos;
9015 }
9016
9017 //================================================================================
9018 /*!
9019  * \brief Computes a new node position located inside a Nef polygon
9020  */
9021 //================================================================================
9022
9023 gp_XYZ _LayerEdge::smoothNefPolygon()
9024 #ifdef OLD_NEF_POLYGON
9025 {
9026   gp_XYZ newPos(0,0,0);
9027
9028   // get a plane to search a solution on
9029
9030   vector< gp_XYZ > vecs( _simplices.size() + 1 );
9031   size_t i;
9032   const double tol = numeric_limits<double>::min();
9033   gp_XYZ center(0,0,0);
9034   for ( i = 0; i < _simplices.size(); ++i )
9035   {
9036     vecs[i] = ( SMESH_TNodeXYZ( _simplices[i]._nNext ) -
9037                 SMESH_TNodeXYZ( _simplices[i]._nPrev ));
9038     center += SMESH_TNodeXYZ( _simplices[i]._nPrev );
9039   }
9040   vecs.back() = vecs[0];
9041   center /= _simplices.size();
9042
9043   gp_XYZ zAxis(0,0,0);
9044   for ( i = 0; i < _simplices.size(); ++i )
9045     zAxis += vecs[i] ^ vecs[i+1];
9046
9047   gp_XYZ yAxis;
9048   for ( i = 0; i < _simplices.size(); ++i )
9049   {
9050     yAxis = vecs[i];
9051     if ( yAxis.SquareModulus() > tol )
9052       break;
9053   }
9054   gp_XYZ xAxis = yAxis ^ zAxis;
9055   // SMESH_TNodeXYZ p0( _simplices[0]._nPrev );
9056   // const double tol = 1e-6 * ( p0.Distance( _simplices[1]._nPrev ) +
9057   //                             p0.Distance( _simplices[2]._nPrev ));
9058   // gp_XYZ center = smoothLaplacian();
9059   // gp_XYZ xAxis, yAxis, zAxis;
9060   // for ( i = 0; i < _simplices.size(); ++i )
9061   // {
9062   //   xAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9063   //   if ( xAxis.SquareModulus() > tol*tol )
9064   //     break;
9065   // }
9066   // for ( i = 1; i < _simplices.size(); ++i )
9067   // {
9068   //   yAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9069   //   zAxis = xAxis ^ yAxis;
9070   //   if ( zAxis.SquareModulus() > tol*tol )
9071   //     break;
9072   // }
9073   // if ( i == _simplices.size() ) return newPos;
9074
9075   yAxis = zAxis ^ xAxis;
9076   xAxis /= xAxis.Modulus();
9077   yAxis /= yAxis.Modulus();
9078
9079   // get half-planes of _simplices
9080
9081   vector< _halfPlane > halfPlns( _simplices.size() );
9082   int nbHP = 0;
9083   for ( size_t i = 0; i < _simplices.size(); ++i )
9084   {
9085     gp_XYZ OP1 = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9086     gp_XYZ OP2 = SMESH_TNodeXYZ( _simplices[i]._nNext ) - center;
9087     gp_XY  p1( OP1 * xAxis, OP1 * yAxis );
9088     gp_XY  p2( OP2 * xAxis, OP2 * yAxis );
9089     gp_XY  vec12 = p2 - p1;
9090     double dist12 = vec12.Modulus();
9091     if ( dist12 < tol )
9092       continue;
9093     vec12 /= dist12;
9094     halfPlns[ nbHP ]._pos = p1;
9095     halfPlns[ nbHP ]._dir = vec12;
9096     halfPlns[ nbHP ]._inNorm.SetCoord( -vec12.Y(), vec12.X() );
9097     ++nbHP;
9098   }
9099
9100   // intersect boundaries of half-planes, define state of intersection points
9101   // in relation to all half-planes and calculate internal point of a 2D polygon
9102
9103   double sumLen = 0;
9104   gp_XY newPos2D (0,0);
9105
9106   enum { UNDEF = -1, NOT_OUT, IS_OUT, NO_INT };
9107   typedef std::pair< gp_XY, int > TIntPntState; // coord and isOut state
9108   TIntPntState undefIPS( gp_XY(1e100,1e100), UNDEF );
9109
9110   vector< vector< TIntPntState > > allIntPnts( nbHP );
9111   for ( int iHP1 = 0; iHP1 < nbHP; ++iHP1 )
9112   {
9113     vector< TIntPntState > & intPnts1 = allIntPnts[ iHP1 ];
9114     if ( intPnts1.empty() ) intPnts1.resize( nbHP, undefIPS );
9115
9116     int iPrev = SMESH_MesherHelper::WrapIndex( iHP1 - 1, nbHP );
9117     int iNext = SMESH_MesherHelper::WrapIndex( iHP1 + 1, nbHP );
9118
9119     int nbNotOut = 0;
9120     const gp_XY* segEnds[2] = { 0, 0 }; // NOT_OUT points
9121
9122     for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9123     {
9124       if ( iHP1 == iHP2 ) continue;
9125
9126       TIntPntState & ips1 = intPnts1[ iHP2 ];
9127       if ( ips1.second == UNDEF )
9128       {
9129         // find an intersection point of boundaries of iHP1 and iHP2
9130
9131         if ( iHP2 == iPrev ) // intersection with neighbors is known
9132           ips1.first = halfPlns[ iHP1 ]._pos;
9133         else if ( iHP2 == iNext )
9134           ips1.first = halfPlns[ iHP2 ]._pos;
9135         else if ( !halfPlns[ iHP1 ].FindIntersection( halfPlns[ iHP2 ], ips1.first ))
9136           ips1.second = NO_INT;
9137
9138         // classify the found intersection point
9139         if ( ips1.second != NO_INT )
9140         {
9141           ips1.second = NOT_OUT;
9142           for ( int i = 0; i < nbHP && ips1.second == NOT_OUT; ++i )
9143             if ( i != iHP1 && i != iHP2 &&
9144                  halfPlns[ i ].IsOut( ips1.first, tol ))
9145               ips1.second = IS_OUT;
9146         }
9147         vector< TIntPntState > & intPnts2 = allIntPnts[ iHP2 ];
9148         if ( intPnts2.empty() ) intPnts2.resize( nbHP, undefIPS );
9149         TIntPntState & ips2 = intPnts2[ iHP1 ];
9150         ips2 = ips1;
9151       }
9152       if ( ips1.second == NOT_OUT )
9153       {
9154         ++nbNotOut;
9155         segEnds[ bool(segEnds[0]) ] = & ips1.first;
9156       }
9157     }
9158
9159     // find a NOT_OUT segment of boundary which is located between
9160     // two NOT_OUT int points
9161
9162     if ( nbNotOut < 2 )
9163       continue; // no such a segment
9164
9165     if ( nbNotOut > 2 )
9166     {
9167       // sort points along the boundary
9168       map< double, TIntPntState* > ipsByParam;
9169       for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9170       {
9171         TIntPntState & ips1 = intPnts1[ iHP2 ];
9172         if ( ips1.second != NO_INT )
9173         {
9174           gp_XY     op = ips1.first - halfPlns[ iHP1 ]._pos;
9175           double param = op * halfPlns[ iHP1 ]._dir;
9176           ipsByParam.insert( make_pair( param, & ips1 ));
9177         }
9178       }
9179       // look for two neighboring NOT_OUT points
9180       nbNotOut = 0;
9181       map< double, TIntPntState* >::iterator u2ips = ipsByParam.begin();
9182       for ( ; u2ips != ipsByParam.end(); ++u2ips )
9183       {
9184         TIntPntState & ips1 = *(u2ips->second);
9185         if ( ips1.second == NOT_OUT )
9186           segEnds[ bool( nbNotOut++ ) ] = & ips1.first;
9187         else if ( nbNotOut >= 2 )
9188           break;
9189         else
9190           nbNotOut = 0;
9191       }
9192     }
9193
9194     if ( nbNotOut >= 2 )
9195     {
9196       double len = ( *segEnds[0] - *segEnds[1] ).Modulus();
9197       sumLen += len;
9198
9199       newPos2D += 0.5 * len * ( *segEnds[0] + *segEnds[1] );
9200     }
9201   }
9202
9203   if ( sumLen > 0 )
9204   {
9205     newPos2D /= sumLen;
9206     newPos = center + xAxis * newPos2D.X() + yAxis * newPos2D.Y();
9207   }
9208   else
9209   {
9210     newPos = center;
9211   }
9212
9213   return newPos;
9214 }
9215 #else // OLD_NEF_POLYGON
9216 { ////////////////////////////////// NEW
9217   gp_XYZ newPos(0,0,0);
9218
9219   // get a plane to search a solution on
9220
9221   size_t i;
9222   gp_XYZ center(0,0,0);
9223   for ( i = 0; i < _simplices.size(); ++i )
9224     center += SMESH_TNodeXYZ( _simplices[i]._nPrev );
9225   center /= _simplices.size();
9226
9227   vector< gp_XYZ > vecs( _simplices.size() + 1 );
9228   for ( i = 0; i < _simplices.size(); ++i )
9229     vecs[i] = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9230   vecs.back() = vecs[0];
9231
9232   const double tol = numeric_limits<double>::min();
9233   gp_XYZ zAxis(0,0,0);
9234   for ( i = 0; i < _simplices.size(); ++i )
9235   {
9236     gp_XYZ cross = vecs[i] ^ vecs[i+1];
9237     try {
9238       cross.Normalize();
9239       if ( cross * zAxis < tol )
9240         zAxis += cross.Reversed();
9241       else
9242         zAxis += cross;
9243     }
9244     catch (Standard_Failure) { // if |cross| == 0.
9245     }
9246   }
9247
9248   gp_XYZ yAxis;
9249   for ( i = 0; i < _simplices.size(); ++i )
9250   {
9251     yAxis = vecs[i];
9252     if ( yAxis.SquareModulus() > tol )
9253       break;
9254   }
9255   gp_XYZ xAxis = yAxis ^ zAxis;
9256   // SMESH_TNodeXYZ p0( _simplices[0]._nPrev );
9257   // const double tol = 1e-6 * ( p0.Distance( _simplices[1]._nPrev ) +
9258   //                             p0.Distance( _simplices[2]._nPrev ));
9259   // gp_XYZ center = smoothLaplacian();
9260   // gp_XYZ xAxis, yAxis, zAxis;
9261   // for ( i = 0; i < _simplices.size(); ++i )
9262   // {
9263   //   xAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9264   //   if ( xAxis.SquareModulus() > tol*tol )
9265   //     break;
9266   // }
9267   // for ( i = 1; i < _simplices.size(); ++i )
9268   // {
9269   //   yAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
9270   //   zAxis = xAxis ^ yAxis;
9271   //   if ( zAxis.SquareModulus() > tol*tol )
9272   //     break;
9273   // }
9274   // if ( i == _simplices.size() ) return newPos;
9275
9276   yAxis = zAxis ^ xAxis;
9277   xAxis /= xAxis.Modulus();
9278   yAxis /= yAxis.Modulus();
9279
9280   // get half-planes of _simplices
9281
9282   vector< _halfPlane > halfPlns( _simplices.size() );
9283   int nbHP = 0;
9284   for ( size_t i = 0; i < _simplices.size(); ++i )
9285   {
9286     const gp_XYZ& OP1 = vecs[ i   ];
9287     const gp_XYZ& OP2 = vecs[ i+1 ];
9288     gp_XY  p1( OP1 * xAxis, OP1 * yAxis );
9289     gp_XY  p2( OP2 * xAxis, OP2 * yAxis );
9290     gp_XY  vec12 = p2 - p1;
9291     double dist12 = vec12.Modulus();
9292     if ( dist12 < tol )
9293       continue;
9294     vec12 /= dist12;
9295     halfPlns[ nbHP ]._pos = p1;
9296     halfPlns[ nbHP ]._dir = vec12;
9297     halfPlns[ nbHP ]._inNorm.SetCoord( -vec12.Y(), vec12.X() );
9298     ++nbHP;
9299   }
9300
9301   // intersect boundaries of half-planes, define state of intersection points
9302   // in relation to all half-planes and calculate internal point of a 2D polygon
9303
9304   double sumLen = 0;
9305   gp_XY newPos2D (0,0);
9306
9307   enum { UNDEF = -1, NOT_OUT, IS_OUT, NO_INT };
9308   typedef std::pair< gp_XY, int > TIntPntState; // coord and isOut state
9309   TIntPntState undefIPS( gp_XY(1e100,1e100), UNDEF );
9310
9311   vector< vector< TIntPntState > > allIntPnts( nbHP );
9312   for ( int iHP1 = 0; iHP1 < nbHP; ++iHP1 )
9313   {
9314     vector< TIntPntState > & intPnts1 = allIntPnts[ iHP1 ];
9315     if ( intPnts1.empty() ) intPnts1.resize( nbHP, undefIPS );
9316
9317     int iPrev = SMESH_MesherHelper::WrapIndex( iHP1 - 1, nbHP );
9318     int iNext = SMESH_MesherHelper::WrapIndex( iHP1 + 1, nbHP );
9319
9320     int nbNotOut = 0;
9321     const gp_XY* segEnds[2] = { 0, 0 }; // NOT_OUT points
9322
9323     for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9324     {
9325       if ( iHP1 == iHP2 ) continue;
9326
9327       TIntPntState & ips1 = intPnts1[ iHP2 ];
9328       if ( ips1.second == UNDEF )
9329       {
9330         // find an intersection point of boundaries of iHP1 and iHP2
9331
9332         if ( iHP2 == iPrev ) // intersection with neighbors is known
9333           ips1.first = halfPlns[ iHP1 ]._pos;
9334         else if ( iHP2 == iNext )
9335           ips1.first = halfPlns[ iHP2 ]._pos;
9336         else if ( !halfPlns[ iHP1 ].FindIntersection( halfPlns[ iHP2 ], ips1.first ))
9337           ips1.second = NO_INT;
9338
9339         // classify the found intersection point
9340         if ( ips1.second != NO_INT )
9341         {
9342           ips1.second = NOT_OUT;
9343           for ( int i = 0; i < nbHP && ips1.second == NOT_OUT; ++i )
9344             if ( i != iHP1 && i != iHP2 &&
9345                  halfPlns[ i ].IsOut( ips1.first, tol ))
9346               ips1.second = IS_OUT;
9347         }
9348         vector< TIntPntState > & intPnts2 = allIntPnts[ iHP2 ];
9349         if ( intPnts2.empty() ) intPnts2.resize( nbHP, undefIPS );
9350         TIntPntState & ips2 = intPnts2[ iHP1 ];
9351         ips2 = ips1;
9352       }
9353       if ( ips1.second == NOT_OUT )
9354       {
9355         ++nbNotOut;
9356         segEnds[ bool(segEnds[0]) ] = & ips1.first;
9357       }
9358     }
9359
9360     // find a NOT_OUT segment of boundary which is located between
9361     // two NOT_OUT int points
9362
9363     if ( nbNotOut < 2 )
9364       continue; // no such a segment
9365
9366     if ( nbNotOut > 2 )
9367     {
9368       // sort points along the boundary
9369       map< double, TIntPntState* > ipsByParam;
9370       for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
9371       {
9372         TIntPntState & ips1 = intPnts1[ iHP2 ];
9373         if ( ips1.second != NO_INT )
9374         {
9375           gp_XY     op = ips1.first - halfPlns[ iHP1 ]._pos;
9376           double param = op * halfPlns[ iHP1 ]._dir;
9377           ipsByParam.insert( make_pair( param, & ips1 ));
9378         }
9379       }
9380       // look for two neighboring NOT_OUT points
9381       nbNotOut = 0;
9382       map< double, TIntPntState* >::iterator u2ips = ipsByParam.begin();
9383       for ( ; u2ips != ipsByParam.end(); ++u2ips )
9384       {
9385         TIntPntState & ips1 = *(u2ips->second);
9386         if ( ips1.second == NOT_OUT )
9387           segEnds[ bool( nbNotOut++ ) ] = & ips1.first;
9388         else if ( nbNotOut >= 2 )
9389           break;
9390         else
9391           nbNotOut = 0;
9392       }
9393     }
9394
9395     if ( nbNotOut >= 2 )
9396     {
9397       double len = ( *segEnds[0] - *segEnds[1] ).Modulus();
9398       sumLen += len;
9399
9400       newPos2D += 0.5 * len * ( *segEnds[0] + *segEnds[1] );
9401     }
9402   }
9403
9404   if ( sumLen > 0 )
9405   {
9406     newPos2D /= sumLen;
9407     newPos = center + xAxis * newPos2D.X() + yAxis * newPos2D.Y();
9408   }
9409   else
9410   {
9411     newPos = center;
9412   }
9413
9414   return newPos;
9415 }
9416 #endif // OLD_NEF_POLYGON
9417
9418 //================================================================================
9419 /*!
9420  * \brief Add a new segment to _LayerEdge during inflation
9421  */
9422 //================================================================================
9423
9424 void _LayerEdge::SetNewLength( double len, _EdgesOnShape& eos, SMESH_MesherHelper& helper )
9425 {
9426   if ( Is( BLOCKED ))
9427     return;
9428
9429   if ( len > _maxLen )
9430   {
9431     len = _maxLen;
9432     Block( eos.GetData() );
9433   }
9434   const double lenDelta = len - _len;
9435   if ( lenDelta < len * 1e-3  )
9436   {
9437     Block( eos.GetData() );
9438     return;
9439   }
9440
9441   SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() );
9442   gp_XYZ oldXYZ = SMESH_TNodeXYZ( n );
9443   gp_XYZ newXYZ;
9444   if ( eos._hyp.IsOffsetMethod() )
9445   {
9446     newXYZ = oldXYZ;
9447     gp_Vec faceNorm;
9448     SMDS_ElemIteratorPtr faceIt = _nodes[0]->GetInverseElementIterator( SMDSAbs_Face );
9449     while ( faceIt->more() )
9450     {
9451       const SMDS_MeshElement* face = faceIt->next();
9452       if ( !eos.GetNormal( face, faceNorm ))
9453         continue;
9454
9455       // translate plane of a face
9456       gp_XYZ baryCenter = oldXYZ + faceNorm.XYZ() * lenDelta;
9457
9458       // find point of intersection of the face plane located at baryCenter
9459       // and _normal located at newXYZ
9460       double d   = -( faceNorm.XYZ() * baryCenter ); // d of plane equation ax+by+cz+d=0
9461       double dot =  ( faceNorm.XYZ() * _normal );
9462       if ( dot < std::numeric_limits<double>::min() )
9463         dot = lenDelta * 1e-3;
9464       double step = -( faceNorm.XYZ() * newXYZ + d ) / dot;
9465       newXYZ += step * _normal;
9466     }
9467     _lenFactor = _normal * ( newXYZ - oldXYZ ) / lenDelta; // _lenFactor is used in InvalidateStep()
9468   }
9469   else
9470   {
9471     newXYZ = oldXYZ + _normal * lenDelta * _lenFactor;
9472   }
9473
9474   n->setXYZ( newXYZ.X(), newXYZ.Y(), newXYZ.Z() );
9475   _pos.push_back( newXYZ );
9476
9477   if ( !eos._sWOL.IsNull() )
9478   {
9479     double distXYZ[4];
9480     bool uvOK = false;
9481     if ( eos.SWOLType() == TopAbs_EDGE )
9482     {
9483       double u = Precision::Infinite(); // to force projection w/o distance check
9484       uvOK = helper.CheckNodeU( TopoDS::Edge( eos._sWOL ), n, u,
9485                                 /*tol=*/2*lenDelta, /*force=*/true, distXYZ );
9486       _pos.back().SetCoord( u, 0, 0 );
9487       if ( _nodes.size() > 1 && uvOK )
9488       {
9489         SMDS_EdgePositionPtr pos = n->GetPosition();
9490         pos->SetUParameter( u );
9491       }
9492     }
9493     else //  TopAbs_FACE
9494     {
9495       gp_XY uv( Precision::Infinite(), 0 );
9496       uvOK = helper.CheckNodeUV( TopoDS::Face( eos._sWOL ), n, uv,
9497                                  /*tol=*/2*lenDelta, /*force=*/true, distXYZ );
9498       _pos.back().SetCoord( uv.X(), uv.Y(), 0 );
9499       if ( _nodes.size() > 1 && uvOK )
9500       {
9501         SMDS_FacePositionPtr pos = n->GetPosition();
9502         pos->SetUParameter( uv.X() );
9503         pos->SetVParameter( uv.Y() );
9504       }
9505     }
9506     if ( uvOK )
9507     {
9508       n->setXYZ( distXYZ[1], distXYZ[2], distXYZ[3]);
9509     }
9510     else
9511     {
9512       n->setXYZ( oldXYZ.X(), oldXYZ.Y(), oldXYZ.Z() );
9513       _pos.pop_back();
9514       Block( eos.GetData() );
9515       return;
9516     }
9517   }
9518
9519   _len = len;
9520
9521   // notify _neibors
9522   if ( eos.ShapeType() != TopAbs_FACE )
9523   {
9524     for ( size_t i = 0; i < _neibors.size(); ++i )
9525       //if (  _len > _neibors[i]->GetSmooLen() )
9526         _neibors[i]->Set( MOVED );
9527
9528     Set( MOVED );
9529   }
9530   dumpMove( n ); //debug
9531 }
9532
9533 //================================================================================
9534 /*!
9535  * \brief Set BLOCKED flag and propagate limited _maxLen to _neibors
9536  */
9537 //================================================================================
9538
9539 void _LayerEdge::Block( _SolidData& data )
9540 {
9541   //if ( Is( BLOCKED )) return;
9542   Set( BLOCKED );
9543
9544   SMESH_Comment msg( "#BLOCK shape=");
9545   msg << data.GetShapeEdges( this )->_shapeID
9546       << ", nodes " << _nodes[0]->GetID() << ", " << _nodes.back()->GetID();
9547   dumpCmd( msg + " -- BEGIN");
9548
9549   SetMaxLen( _len );
9550   std::queue<_LayerEdge*> queue;
9551   queue.push( this );
9552
9553   gp_Pnt pSrc, pTgt, pSrcN, pTgtN;
9554   while ( !queue.empty() )
9555   {
9556     _LayerEdge* edge = queue.front(); queue.pop();
9557     pSrc = SMESH_TNodeXYZ( edge->_nodes[0] );
9558     pTgt = SMESH_TNodeXYZ( edge->_nodes.back() );
9559     for ( size_t iN = 0; iN < edge->_neibors.size(); ++iN )
9560     {
9561       _LayerEdge* neibor = edge->_neibors[iN];
9562       if ( neibor->_maxLen < edge->_maxLen * 1.01 )
9563         continue;
9564       pSrcN = SMESH_TNodeXYZ( neibor->_nodes[0] );
9565       pTgtN = SMESH_TNodeXYZ( neibor->_nodes.back() );
9566       double minDist = pSrc.SquareDistance( pSrcN );
9567       minDist   = Min( pTgt.SquareDistance( pTgtN ), minDist );
9568       minDist   = Min( pSrc.SquareDistance( pTgtN ), minDist );
9569       minDist   = Min( pTgt.SquareDistance( pSrcN ), minDist );
9570       double newMaxLen = edge->_maxLen + 0.5 * Sqrt( minDist );
9571       //if ( edge->_nodes[0]->getshapeId() == neibor->_nodes[0]->getshapeId() ) viscous_layers_00/A3
9572       {
9573         //newMaxLen *= edge->_lenFactor / neibor->_lenFactor;
9574         // newMaxLen *= Min( edge->_lenFactor / neibor->_lenFactor,
9575         //                   neibor->_lenFactor / edge->_lenFactor );
9576       }
9577       if ( neibor->_maxLen > newMaxLen )
9578       {
9579         neibor->SetMaxLen( newMaxLen );
9580         if ( neibor->_maxLen < neibor->_len )
9581         {
9582           _EdgesOnShape* eos = data.GetShapeEdges( neibor );
9583           int       lastStep = neibor->Is( BLOCKED ) ? 1 : 0;
9584           while ( neibor->_len > neibor->_maxLen &&
9585                   neibor->NbSteps() > lastStep )
9586             neibor->InvalidateStep( neibor->NbSteps(), *eos, /*restoreLength=*/true );
9587           neibor->SetNewLength( neibor->_maxLen, *eos, data.GetHelper() );
9588           //neibor->Block( data );
9589         }
9590         queue.push( neibor );
9591       }
9592     }
9593   }
9594   dumpCmd( msg + " -- END");
9595 }
9596
9597 //================================================================================
9598 /*!
9599  * \brief Remove last inflation step
9600  */
9601 //================================================================================
9602
9603 void _LayerEdge::InvalidateStep( size_t curStep, const _EdgesOnShape& eos, bool restoreLength )
9604 {
9605   if ( _pos.size() > curStep && _nodes.size() > 1 )
9606   {
9607     _pos.resize( curStep );
9608
9609     gp_Pnt      nXYZ = _pos.back();
9610     SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() );
9611     SMESH_TNodeXYZ curXYZ( n );
9612     if ( !eos._sWOL.IsNull() )
9613     {
9614       TopLoc_Location loc;
9615       if ( eos.SWOLType() == TopAbs_EDGE )
9616       {
9617         SMDS_EdgePositionPtr pos = n->GetPosition();
9618         pos->SetUParameter( nXYZ.X() );
9619         double f,l;
9620         Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( eos._sWOL ), loc, f,l);
9621         nXYZ = curve->Value( nXYZ.X() ).Transformed( loc );
9622       }
9623       else
9624       {
9625         SMDS_FacePositionPtr pos = n->GetPosition();
9626         pos->SetUParameter( nXYZ.X() );
9627         pos->SetVParameter( nXYZ.Y() );
9628         Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face(eos._sWOL), loc );
9629         nXYZ = surface->Value( nXYZ.X(), nXYZ.Y() ).Transformed( loc );
9630       }
9631     }
9632     n->setXYZ( nXYZ.X(), nXYZ.Y(), nXYZ.Z() );
9633     dumpMove( n );
9634
9635     if ( restoreLength )
9636     {
9637       if ( NbSteps() == 0 )
9638         _len = 0.;
9639       else if ( IsOnFace() && Is( MOVED ))
9640         _len = ( nXYZ.XYZ() - SMESH_NodeXYZ( _nodes[0] )) * _normal;
9641       else
9642         _len -= ( nXYZ.XYZ() - curXYZ ).Modulus() / _lenFactor;
9643     }
9644   }
9645   return;
9646 }
9647
9648 //================================================================================
9649 /*!
9650  * \brief Return index of a _pos distant from _normal
9651  */
9652 //================================================================================
9653
9654 int _LayerEdge::GetSmoothedPos( const double tol )
9655 {
9656   int iSmoothed = 0;
9657   for ( size_t i = 1; i < _pos.size() && !iSmoothed; ++i )
9658   {
9659     double normDist = ( _pos[i] - _pos[0] ).Crossed( _normal ).SquareModulus();
9660     if ( normDist > tol * tol )
9661       iSmoothed = i;
9662   }
9663   return iSmoothed;
9664 }
9665
9666 //================================================================================
9667 /*!
9668  * \brief Smooth a path formed by _pos of a _LayerEdge smoothed on FACE
9669  */
9670 //================================================================================
9671
9672 void _LayerEdge::SmoothPos( const vector< double >& segLen, const double tol )
9673 {
9674   if ( /*Is( NORMAL_UPDATED ) ||*/ _pos.size() <= 2 )
9675     return;
9676
9677   // find the 1st smoothed _pos
9678   int iSmoothed = GetSmoothedPos( tol );
9679   if ( !iSmoothed ) return;
9680
9681   gp_XYZ normal = _normal;
9682   if ( Is( NORMAL_UPDATED ))
9683   {
9684     double minDot = 1;
9685     for ( size_t i = 0; i < _neibors.size(); ++i )
9686     {
9687       if ( _neibors[i]->IsOnFace() )
9688       {
9689         double dot = _normal * _neibors[i]->_normal;
9690         if ( dot < minDot )
9691         {
9692           normal = _neibors[i]->_normal;
9693           minDot = dot;
9694         }
9695       }
9696     }
9697     if ( minDot == 1. )
9698       for ( size_t i = 1; i < _pos.size(); ++i )
9699       {
9700         normal = _pos[i] - _pos[0];
9701         double size = normal.Modulus();
9702         if ( size > RealSmall() )
9703         {
9704           normal /= size;
9705           break;
9706         }
9707       }
9708   }
9709   const double r = 0.2;
9710   for ( int iter = 0; iter < 50; ++iter )
9711   {
9712     double minDot = 1;
9713     for ( size_t i = Max( 1, iSmoothed-1-iter ); i < _pos.size()-1; ++i )
9714     {
9715       gp_XYZ midPos = 0.5 * ( _pos[i-1] + _pos[i+1] );
9716       gp_XYZ newPos = ( 1-r ) * midPos + r * _pos[i];
9717       _pos[i] = newPos;
9718       double midLen = 0.5 * ( segLen[i-1] + segLen[i+1] );
9719       double newLen = ( 1-r ) * midLen + r * segLen[i];
9720       const_cast< double& >( segLen[i] ) = newLen;
9721       // check angle between normal and (_pos[i+1], _pos[i] )
9722       gp_XYZ posDir = _pos[i+1] - _pos[i];
9723       double size   = posDir.SquareModulus();
9724       if ( size > RealSmall() )
9725         minDot = Min( minDot, ( normal * posDir ) * ( normal * posDir ) / size );
9726     }
9727     if ( minDot > 0.5 * 0.5 )
9728       break;
9729   }
9730   return;
9731 }
9732
9733 //================================================================================
9734 /*!
9735  * \brief Print flags
9736  */
9737 //================================================================================
9738
9739 std::string _LayerEdge::DumpFlags() const
9740 {
9741   SMESH_Comment dump;
9742   for ( int flag = 1; flag < 0x1000000; flag *= 2 )
9743     if ( _flags & flag )
9744     {
9745       EFlags f = (EFlags) flag;
9746       switch ( f ) {
9747       case TO_SMOOTH:       dump << "TO_SMOOTH";       break;
9748       case MOVED:           dump << "MOVED";           break;
9749       case SMOOTHED:        dump << "SMOOTHED";        break;
9750       case DIFFICULT:       dump << "DIFFICULT";       break;
9751       case ON_CONCAVE_FACE: dump << "ON_CONCAVE_FACE"; break;
9752       case BLOCKED:         dump << "BLOCKED";         break;
9753       case INTERSECTED:     dump << "INTERSECTED";     break;
9754       case NORMAL_UPDATED:  dump << "NORMAL_UPDATED";  break;
9755       case UPD_NORMAL_CONV: dump << "UPD_NORMAL_CONV"; break;
9756       case MARKED:          dump << "MARKED";          break;
9757       case MULTI_NORMAL:    dump << "MULTI_NORMAL";    break;
9758       case NEAR_BOUNDARY:   dump << "NEAR_BOUNDARY";   break;
9759       case SMOOTHED_C1:     dump << "SMOOTHED_C1";     break;
9760       case DISTORTED:       dump << "DISTORTED";       break;
9761       case RISKY_SWOL:      dump << "RISKY_SWOL";      break;
9762       case SHRUNK:          dump << "SHRUNK";          break;
9763       case UNUSED_FLAG:     dump << "UNUSED_FLAG";     break;
9764       }
9765       dump << " ";
9766     }
9767   cout << dump << endl;
9768   return dump;
9769 }
9770
9771
9772 //================================================================================
9773 /*!
9774  * \brief Create layers of prisms
9775  */
9776 //================================================================================
9777
9778 bool _ViscousBuilder::refine(_SolidData& data)
9779 {
9780   SMESH_MesherHelper& helper = data.GetHelper();
9781   helper.SetElementsOnShape(false);
9782
9783   Handle(Geom_Curve) curve;
9784   Handle(ShapeAnalysis_Surface) surface;
9785   TopoDS_Edge geomEdge;
9786   TopoDS_Face geomFace;
9787   TopLoc_Location loc;
9788   double f,l, u = 0;
9789   gp_XY uv;
9790   vector< gp_XYZ > pos3D;
9791   bool isOnEdge, isTooConvexFace = false;
9792   TGeomID prevBaseId = -1;
9793   TNode2Edge* n2eMap = 0;
9794   TNode2Edge::iterator n2e;
9795
9796   // Create intermediate nodes on each _LayerEdge
9797
9798   for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
9799   {
9800     _EdgesOnShape& eos = data._edgesOnShape[iS];
9801     if ( eos._edges.empty() ) continue;
9802
9803     if ( eos._edges[0]->_nodes.size() < 2 )
9804       continue; // on _noShrinkShapes
9805
9806     // get data of a shrink shape
9807     isOnEdge = false;
9808     geomEdge.Nullify(); geomFace.Nullify();
9809     curve.Nullify(); surface.Nullify();
9810     if ( !eos._sWOL.IsNull() )
9811     {
9812       isOnEdge = ( eos.SWOLType() == TopAbs_EDGE );
9813       if ( isOnEdge )
9814       {
9815         geomEdge = TopoDS::Edge( eos._sWOL );
9816         curve    = BRep_Tool::Curve( geomEdge, loc, f,l);
9817       }
9818       else
9819       {
9820         geomFace = TopoDS::Face( eos._sWOL );
9821         surface  = helper.GetSurface( geomFace );
9822       }
9823     }
9824     else if ( eos.ShapeType() == TopAbs_FACE && eos._toSmooth )
9825     {
9826       geomFace = TopoDS::Face( eos._shape );
9827       surface  = helper.GetSurface( geomFace );
9828       // propagate _toSmooth back to _eosC1, which was unset in findShapesToSmooth()
9829       for ( size_t i = 0; i < eos._eosC1.size(); ++i )
9830         eos._eosC1[ i ]->_toSmooth = true;
9831
9832       isTooConvexFace = false;
9833       if ( _ConvexFace* cf = data.GetConvexFace( eos._shapeID ))
9834         isTooConvexFace = cf->_isTooCurved;
9835     }
9836
9837     vector< double > segLen;
9838     for ( size_t i = 0; i < eos._edges.size(); ++i )
9839     {
9840       _LayerEdge& edge = *eos._edges[i];
9841       if ( edge._pos.size() < 2 )
9842         continue;
9843
9844       // get accumulated length of segments
9845       segLen.resize( edge._pos.size() );
9846       segLen[0] = 0.0;
9847       if ( eos._sWOL.IsNull() )
9848       {
9849         bool useNormal = true;
9850         bool    usePos = false;
9851         bool  smoothed = false;
9852         double   preci = 0.1 * edge._len;
9853         if ( eos._toSmooth && edge._pos.size() > 2 )
9854         {
9855           smoothed = edge.GetSmoothedPos( preci );
9856         }
9857         if ( smoothed )
9858         {
9859           if ( !surface.IsNull() && !isTooConvexFace ) // edge smoothed on FACE
9860           {
9861             useNormal = usePos = false;
9862             gp_Pnt2d uv = helper.GetNodeUV( geomFace, edge._nodes[0] );
9863             for ( size_t j = 1; j < edge._pos.size() && !useNormal; ++j )
9864             {
9865               uv = surface->NextValueOfUV( uv, edge._pos[j], preci );
9866               if ( surface->Gap() < 2. * edge._len )
9867                 segLen[j] = surface->Gap();
9868               else
9869                 useNormal = true;
9870             }
9871           }
9872         }
9873         else if ( !edge.Is( _LayerEdge::NORMAL_UPDATED ))
9874         {
9875 #ifndef __NODES_AT_POS
9876           useNormal = usePos = false;
9877           edge._pos[1] = edge._pos.back();
9878           edge._pos.resize( 2 );
9879           segLen.resize( 2 );
9880           segLen[ 1 ] = edge._len;
9881 #endif
9882         }
9883         if ( useNormal && edge.Is( _LayerEdge::NORMAL_UPDATED ))
9884         {
9885           useNormal = usePos = false;
9886           _LayerEdge tmpEdge; // get original _normal
9887           tmpEdge._nodes.push_back( edge._nodes[0] );
9888           if ( !setEdgeData( tmpEdge, eos, helper, data ))
9889             usePos = true;
9890           else
9891             for ( size_t j = 1; j < edge._pos.size(); ++j )
9892               segLen[j] = ( edge._pos[j] - edge._pos[0] ) * tmpEdge._normal;
9893         }
9894         if ( useNormal )
9895         {
9896           for ( size_t j = 1; j < edge._pos.size(); ++j )
9897             segLen[j] = ( edge._pos[j] - edge._pos[0] ) * edge._normal;
9898         }
9899         if ( usePos )
9900         {
9901           for ( size_t j = 1; j < edge._pos.size(); ++j )
9902             segLen[j] = segLen[j-1] + ( edge._pos[j-1] - edge._pos[j] ).Modulus();
9903         }
9904         else
9905         {
9906           bool swapped = ( edge._pos.size() > 2 );
9907           while ( swapped )
9908           {
9909             swapped = false;
9910             for ( size_t j = 1; j < edge._pos.size()-1; ++j )
9911               if ( segLen[j] > segLen.back() )
9912               {
9913                 segLen.erase( segLen.begin() + j );
9914                 edge._pos.erase( edge._pos.begin() + j );
9915                 --j;
9916               }
9917               else if ( segLen[j] < segLen[j-1] )
9918               {
9919                 std::swap( segLen[j], segLen[j-1] );
9920                 std::swap( edge._pos[j], edge._pos[j-1] );
9921                 swapped = true;
9922               }
9923           }
9924         }
9925         // smooth a path formed by edge._pos
9926 #ifndef __NODES_AT_POS
9927         if (( smoothed ) /*&&
9928             ( eos.ShapeType() == TopAbs_FACE || edge.Is( _LayerEdge::SMOOTHED_C1 ))*/)
9929           edge.SmoothPos( segLen, preci );
9930 #endif
9931       }
9932       else if ( eos._isRegularSWOL ) // usual SWOL
9933       {
9934         if ( edge.Is( _LayerEdge::SMOOTHED ))
9935         {
9936           SMESH_NodeXYZ p0( edge._nodes[0] );
9937           for ( size_t j = 1; j < edge._pos.size(); ++j )
9938           {
9939             gp_XYZ pj = surface->Value( edge._pos[j].X(), edge._pos[j].Y() ).XYZ();
9940             segLen[j] = ( pj - p0 ) * edge._normal;
9941           }
9942         }
9943         else
9944         {
9945           for ( size_t j = 1; j < edge._pos.size(); ++j )
9946             segLen[j] = segLen[j-1] + (edge._pos[j-1] - edge._pos[j] ).Modulus();
9947         }
9948       }
9949       else if ( !surface.IsNull() ) // SWOL surface with singularities
9950       {
9951         pos3D.resize( edge._pos.size() );
9952         for ( size_t j = 0; j < edge._pos.size(); ++j )
9953           pos3D[j] = surface->Value( edge._pos[j].X(), edge._pos[j].Y() ).XYZ();
9954
9955         for ( size_t j = 1; j < edge._pos.size(); ++j )
9956           segLen[j] = segLen[j-1] + ( pos3D[j-1] - pos3D[j] ).Modulus();
9957       }
9958
9959       // allocate memory for new nodes if it is not yet refined
9960       const SMDS_MeshNode* tgtNode = edge._nodes.back();
9961       if ( edge._nodes.size() == 2 )
9962       {
9963 #ifdef __NODES_AT_POS
9964         int nbNodes = edge._pos.size();
9965 #else
9966         int nbNodes = eos._hyp.GetNumberLayers() + 1;
9967 #endif
9968         edge._nodes.resize( nbNodes, 0 );
9969         edge._nodes[1] = 0;
9970         edge._nodes.back() = tgtNode;
9971       }
9972       // restore shapePos of the last node by already treated _LayerEdge of another _SolidData
9973       const TGeomID baseShapeId = edge._nodes[0]->getshapeId();
9974       if ( baseShapeId != prevBaseId )
9975       {
9976         map< TGeomID, TNode2Edge* >::iterator s2ne = data._s2neMap.find( baseShapeId );
9977         n2eMap = ( s2ne == data._s2neMap.end() ) ? 0 : s2ne->second;
9978         prevBaseId = baseShapeId;
9979       }
9980       _LayerEdge* edgeOnSameNode = 0;
9981       bool        useExistingPos = false;
9982       if ( n2eMap && (( n2e = n2eMap->find( edge._nodes[0] )) != n2eMap->end() ))
9983       {
9984         edgeOnSameNode = n2e->second;
9985         useExistingPos = ( edgeOnSameNode->_len < edge._len );
9986         const gp_XYZ& otherTgtPos = edgeOnSameNode->_pos.back();
9987         SMDS_PositionPtr  lastPos = tgtNode->GetPosition();
9988         if ( isOnEdge )
9989         {
9990           SMDS_EdgePositionPtr epos = lastPos;
9991           epos->SetUParameter( otherTgtPos.X() );
9992         }
9993         else
9994         {
9995           SMDS_FacePositionPtr fpos = lastPos;
9996           fpos->SetUParameter( otherTgtPos.X() );
9997           fpos->SetVParameter( otherTgtPos.Y() );
9998         }
9999       }
10000       // calculate height of the first layer
10001       double h0;
10002       const double T = segLen.back(); //data._hyp.GetTotalThickness();
10003       const double f = eos._hyp.GetStretchFactor();
10004       const int    N = eos._hyp.GetNumberLayers();
10005       const double fPowN = pow( f, N );
10006       if ( fPowN - 1 <= numeric_limits<double>::min() )
10007         h0 = T / N;
10008       else
10009         h0 = T * ( f - 1 )/( fPowN - 1 );
10010
10011       const double zeroLen = std::numeric_limits<double>::min();
10012
10013       // create intermediate nodes
10014       double hSum = 0, hi = h0/f;
10015       size_t iSeg = 1;
10016       for ( size_t iStep = 1; iStep < edge._nodes.size(); ++iStep )
10017       {
10018         // compute an intermediate position
10019         hi *= f;
10020         hSum += hi;
10021         while ( hSum > segLen[iSeg] && iSeg < segLen.size()-1 )
10022           ++iSeg;
10023         int iPrevSeg = iSeg-1;
10024         while ( fabs( segLen[iPrevSeg] - segLen[iSeg]) <= zeroLen && iPrevSeg > 0 )
10025           --iPrevSeg;
10026         double   r = ( segLen[iSeg] - hSum ) / ( segLen[iSeg] - segLen[iPrevSeg] );
10027         gp_Pnt pos = r * edge._pos[iPrevSeg] + (1-r) * edge._pos[iSeg];
10028 #ifdef __NODES_AT_POS
10029         pos = edge._pos[ iStep ];
10030 #endif
10031         SMDS_MeshNode*& node = const_cast< SMDS_MeshNode*& >( edge._nodes[ iStep ]);
10032         if ( !eos._sWOL.IsNull() )
10033         {
10034           // compute XYZ by parameters <pos>
10035           if ( isOnEdge )
10036           {
10037             u = pos.X();
10038             if ( !node )
10039               pos = curve->Value( u ).Transformed(loc);
10040           }
10041           else if ( eos._isRegularSWOL )
10042           {
10043             uv.SetCoord( pos.X(), pos.Y() );
10044             if ( !node )
10045               pos = surface->Value( pos.X(), pos.Y() );
10046           }
10047           else
10048           {
10049             uv.SetCoord( pos.X(), pos.Y() );
10050             gp_Pnt p = r * pos3D[ iPrevSeg ] + (1-r) * pos3D[ iSeg ];
10051             uv = surface->NextValueOfUV( uv, p, BRep_Tool::Tolerance( geomFace )).XY();
10052             if ( !node )
10053               pos = surface->Value( uv );
10054           }
10055         }
10056         // create or update the node
10057         if ( !node )
10058         {
10059           node = helper.AddNode( pos.X(), pos.Y(), pos.Z());
10060           if ( !eos._sWOL.IsNull() )
10061           {
10062             if ( isOnEdge )
10063               getMeshDS()->SetNodeOnEdge( node, geomEdge, u );
10064             else
10065               getMeshDS()->SetNodeOnFace( node, geomFace, uv.X(), uv.Y() );
10066           }
10067           else
10068           {
10069             getMeshDS()->SetNodeInVolume( node, helper.GetSubShapeID() );
10070           }
10071         }
10072         else
10073         {
10074           if ( !eos._sWOL.IsNull() )
10075           {
10076             // make average pos from new and current parameters
10077             if ( isOnEdge )
10078             {
10079               //u = 0.5 * ( u + helper.GetNodeU( geomEdge, node ));
10080               if ( useExistingPos )
10081                 u = helper.GetNodeU( geomEdge, node );
10082               pos = curve->Value( u ).Transformed(loc);
10083
10084               SMDS_EdgePositionPtr epos = node->GetPosition();
10085               epos->SetUParameter( u );
10086             }
10087             else
10088             {
10089               //uv = 0.5 * ( uv + helper.GetNodeUV( geomFace, node ));
10090               if ( useExistingPos )
10091                 uv = helper.GetNodeUV( geomFace, node );
10092               pos = surface->Value( uv );
10093
10094               SMDS_FacePositionPtr fpos = node->GetPosition();
10095               fpos->SetUParameter( uv.X() );
10096               fpos->SetVParameter( uv.Y() );
10097             }
10098           }
10099           node->setXYZ( pos.X(), pos.Y(), pos.Z() );
10100         }
10101       } // loop on edge._nodes
10102
10103       if ( !eos._sWOL.IsNull() ) // prepare for shrink()
10104       {
10105         if ( isOnEdge )
10106           edge._pos.back().SetCoord( u, 0,0);
10107         else
10108           edge._pos.back().SetCoord( uv.X(), uv.Y() ,0);
10109
10110         if ( edgeOnSameNode )
10111           edgeOnSameNode->_pos.back() = edge._pos.back();
10112       }
10113
10114     } // loop on eos._edges to create nodes
10115
10116
10117     if ( !getMeshDS()->IsEmbeddedMode() )
10118       // Log node movement
10119       for ( size_t i = 0; i < eos._edges.size(); ++i )
10120       {
10121         SMESH_TNodeXYZ p ( eos._edges[i]->_nodes.back() );
10122         getMeshDS()->MoveNode( p._node, p.X(), p.Y(), p.Z() );
10123       }
10124   }
10125
10126
10127   // Create volumes
10128
10129   helper.SetElementsOnShape(true);
10130
10131   vector< vector<const SMDS_MeshNode*>* > nnVec;
10132   set< vector<const SMDS_MeshNode*>* >    nnSet;
10133   set< int >                       degenEdgeInd;
10134   vector<const SMDS_MeshElement*>     degenVols;
10135
10136   TopExp_Explorer exp( data._solid, TopAbs_FACE );
10137   for ( ; exp.More(); exp.Next() )
10138   {
10139     const TGeomID faceID = getMeshDS()->ShapeToIndex( exp.Current() );
10140     if ( data._ignoreFaceIds.count( faceID ))
10141       continue;
10142     const bool isReversedFace = data._reversedFaceIds.count( faceID );
10143     SMESHDS_SubMesh*    fSubM = getMeshDS()->MeshElements( exp.Current() );
10144     SMDS_ElemIteratorPtr  fIt = fSubM->GetElements();
10145     while ( fIt->more() )
10146     {
10147       const SMDS_MeshElement* face = fIt->next();
10148       const int            nbNodes = face->NbCornerNodes();
10149       nnVec.resize( nbNodes );
10150       nnSet.clear();
10151       degenEdgeInd.clear();
10152       size_t maxZ = 0, minZ = std::numeric_limits<size_t>::max();
10153       SMDS_NodeIteratorPtr nIt = face->nodeIterator();
10154       for ( int iN = 0; iN < nbNodes; ++iN )
10155       {
10156         const SMDS_MeshNode* n = nIt->next();
10157         _LayerEdge*       edge = data._n2eMap[ n ];
10158         const int i = isReversedFace ? nbNodes-1-iN : iN;
10159         nnVec[ i ] = & edge->_nodes;
10160         maxZ = std::max( maxZ, nnVec[ i ]->size() );
10161         minZ = std::min( minZ, nnVec[ i ]->size() );
10162
10163         if ( helper.HasDegeneratedEdges() )
10164           nnSet.insert( nnVec[ i ]);
10165       }
10166
10167       if ( maxZ == 0 )
10168         continue;
10169       if ( 0 < nnSet.size() && nnSet.size() < 3 )
10170         continue;
10171
10172       switch ( nbNodes )
10173       {
10174       case 3: // TRIA
10175       {
10176         // PENTA
10177         for ( size_t iZ = 1; iZ < minZ; ++iZ )
10178           helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1], (*nnVec[2])[iZ-1],
10179                             (*nnVec[0])[iZ],   (*nnVec[1])[iZ],   (*nnVec[2])[iZ]);
10180
10181         for ( size_t iZ = minZ; iZ < maxZ; ++iZ )
10182         {
10183           for ( int iN = 0; iN < nbNodes; ++iN )
10184             if ( nnVec[ iN ]->size() < iZ+1 )
10185               degenEdgeInd.insert( iN );
10186
10187           if ( degenEdgeInd.size() == 1 )  // PYRAM
10188           {
10189             int i2 = *degenEdgeInd.begin();
10190             int i0 = helper.WrapIndex( i2 - 1, nbNodes );
10191             int i1 = helper.WrapIndex( i2 + 1, nbNodes );
10192             helper.AddVolume( (*nnVec[i0])[iZ-1], (*nnVec[i1])[iZ-1],
10193                               (*nnVec[i1])[iZ  ], (*nnVec[i0])[iZ  ], (*nnVec[i2]).back());
10194           }
10195           else  // TETRA
10196           {
10197             int i3 = !degenEdgeInd.count(0) ? 0 : !degenEdgeInd.count(1) ? 1 : 2;
10198             helper.AddVolume( (*nnVec[  0 ])[ i3 == 0 ? iZ-1 : nnVec[0]->size()-1 ],
10199                               (*nnVec[  1 ])[ i3 == 1 ? iZ-1 : nnVec[1]->size()-1 ],
10200                               (*nnVec[  2 ])[ i3 == 2 ? iZ-1 : nnVec[2]->size()-1 ],
10201                               (*nnVec[ i3 ])[ iZ ]);
10202           }
10203         }
10204         break; // TRIA
10205       }
10206       case 4: // QUAD
10207       {
10208         // HEX
10209         for ( size_t iZ = 1; iZ < minZ; ++iZ )
10210           helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1],
10211                             (*nnVec[2])[iZ-1], (*nnVec[3])[iZ-1],
10212                             (*nnVec[0])[iZ],   (*nnVec[1])[iZ],
10213                             (*nnVec[2])[iZ],   (*nnVec[3])[iZ]);
10214
10215         for ( size_t iZ = minZ; iZ < maxZ; ++iZ )
10216         {
10217           for ( int iN = 0; iN < nbNodes; ++iN )
10218             if ( nnVec[ iN ]->size() < iZ+1 )
10219               degenEdgeInd.insert( iN );
10220
10221           switch ( degenEdgeInd.size() )
10222           {
10223           case 2: // PENTA
10224           {
10225             int i2 = *degenEdgeInd.begin();
10226             int i3 = *degenEdgeInd.rbegin();
10227             bool ok = ( i3 - i2 == 1 );
10228             if ( i2 == 0 && i3 == 3 ) { i2 = 3; i3 = 0; ok = true; }
10229             int i0 = helper.WrapIndex( i3 + 1, nbNodes );
10230             int i1 = helper.WrapIndex( i0 + 1, nbNodes );
10231
10232             const SMDS_MeshElement* vol =
10233               helper.AddVolume( nnVec[i3]->back(), (*nnVec[i0])[iZ], (*nnVec[i0])[iZ-1],
10234                                 nnVec[i2]->back(), (*nnVec[i1])[iZ], (*nnVec[i1])[iZ-1]);
10235             if ( !ok && vol )
10236               degenVols.push_back( vol );
10237           }
10238           break;
10239
10240           default: // degen HEX
10241           {
10242             const SMDS_MeshElement* vol =
10243               helper.AddVolume( nnVec[0]->size() > iZ-1 ? (*nnVec[0])[iZ-1] : nnVec[0]->back(),
10244                                 nnVec[1]->size() > iZ-1 ? (*nnVec[1])[iZ-1] : nnVec[1]->back(),
10245                                 nnVec[2]->size() > iZ-1 ? (*nnVec[2])[iZ-1] : nnVec[2]->back(),
10246                                 nnVec[3]->size() > iZ-1 ? (*nnVec[3])[iZ-1] : nnVec[3]->back(),
10247                                 nnVec[0]->size() > iZ   ? (*nnVec[0])[iZ]   : nnVec[0]->back(),
10248                                 nnVec[1]->size() > iZ   ? (*nnVec[1])[iZ]   : nnVec[1]->back(),
10249                                 nnVec[2]->size() > iZ   ? (*nnVec[2])[iZ]   : nnVec[2]->back(),
10250                                 nnVec[3]->size() > iZ   ? (*nnVec[3])[iZ]   : nnVec[3]->back());
10251             degenVols.push_back( vol );
10252           }
10253           }
10254         }
10255         break; // HEX
10256       }
10257       default:
10258         return error("Not supported type of element", data._index);
10259
10260       } // switch ( nbNodes )
10261     } // while ( fIt->more() )
10262   } // loop on FACEs
10263
10264   if ( !degenVols.empty() )
10265   {
10266     SMESH_ComputeErrorPtr& err = _mesh->GetSubMesh( data._solid )->GetComputeError();
10267     if ( !err || err->IsOK() )
10268     {
10269       SMESH_BadInputElements* badElems =
10270         new SMESH_BadInputElements( getMeshDS(), COMPERR_WARNING, "Bad quality volumes created" );
10271       badElems->myBadElements.insert( badElems->myBadElements.end(),
10272                                       degenVols.begin(),degenVols.end() );
10273       err.reset( badElems );
10274     }
10275   }
10276
10277   return true;
10278 }
10279
10280 //================================================================================
10281 /*!
10282  * \brief Shrink 2D mesh on faces to let space for inflated layers
10283  */
10284 //================================================================================
10285
10286 bool _ViscousBuilder::shrink(_SolidData& theData)
10287 {
10288   // make map of (ids of FACEs to shrink mesh on) to (list of _SolidData containing
10289   // _LayerEdge's inflated along FACE or EDGE)
10290   map< TGeomID, list< _SolidData* > > f2sdMap;
10291   for ( size_t i = 0 ; i < _sdVec.size(); ++i )
10292   {
10293     _SolidData& data = _sdVec[i];
10294     map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
10295     for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
10296       if ( s2s->second.ShapeType() == TopAbs_FACE && !_shrinkedFaces.Contains( s2s->second ))
10297       {
10298         f2sdMap[ getMeshDS()->ShapeToIndex( s2s->second )].push_back( &data );
10299
10300         // Put mesh faces on the shrinked FACE to the proxy sub-mesh to avoid
10301         // usage of mesh faces made in addBoundaryElements() by the 3D algo or
10302         // by StdMeshers_QuadToTriaAdaptor
10303         if ( SMESHDS_SubMesh* smDS = getMeshDS()->MeshElements( s2s->second ))
10304         {
10305           SMESH_ProxyMesh::SubMesh* proxySub =
10306             data._proxyMesh->getFaceSubM( TopoDS::Face( s2s->second ), /*create=*/true);
10307           if ( proxySub->NbElements() == 0 )
10308           {
10309             SMDS_ElemIteratorPtr fIt = smDS->GetElements();
10310             while ( fIt->more() )
10311             {
10312               const SMDS_MeshElement* f = fIt->next();
10313               // as a result 3D algo will use elements from proxySub and not from smDS
10314               proxySub->AddElement( f );
10315               f->setIsMarked( true );
10316
10317               // Mark nodes on the FACE to discriminate them from nodes
10318               // added by addBoundaryElements(); marked nodes are to be smoothed while shrink()
10319               for ( int iN = 0, nbN = f->NbNodes(); iN < nbN; ++iN )
10320               {
10321                 const SMDS_MeshNode* n = f->GetNode( iN );
10322                 if ( n->GetPosition()->GetDim() == 2 )
10323                   n->setIsMarked( true );
10324               }
10325             }
10326           }
10327         }
10328       }
10329   }
10330
10331   SMESH_MesherHelper helper( *_mesh );
10332   helper.ToFixNodeParameters( true );
10333
10334   // EDGEs to shrink
10335   map< TGeomID, _Shrinker1D > e2shrMap;
10336   vector< _EdgesOnShape* > subEOS;
10337   vector< _LayerEdge* > lEdges;
10338
10339   // loop on FACEs to shrink mesh on
10340   map< TGeomID, list< _SolidData* > >::iterator f2sd = f2sdMap.begin();
10341   for ( ; f2sd != f2sdMap.end(); ++f2sd )
10342   {
10343     list< _SolidData* > & dataList = f2sd->second;
10344     if ( dataList.front()->_n2eMap.empty() ||
10345          dataList.back() ->_n2eMap.empty() )
10346       continue; // not yet computed
10347     if ( dataList.front() != &theData &&
10348          dataList.back()  != &theData )
10349       continue;
10350
10351     _SolidData&      data = *dataList.front();
10352     _SolidData*     data2 = dataList.size() > 1 ? dataList.back() : 0;
10353     const TopoDS_Face&  F = TopoDS::Face( getMeshDS()->IndexToShape( f2sd->first ));
10354     SMESH_subMesh*     sm = _mesh->GetSubMesh( F );
10355     SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
10356
10357     Handle(Geom_Surface) surface = BRep_Tool::Surface( F );
10358
10359     _shrinkedFaces.Add( F );
10360     helper.SetSubShape( F );
10361
10362     // ===========================
10363     // Prepare data for shrinking
10364     // ===========================
10365
10366     // Collect nodes to smooth (they are marked at the beginning of this method)
10367     vector < const SMDS_MeshNode* > smoothNodes;
10368     {
10369       SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
10370       while ( nIt->more() )
10371       {
10372         const SMDS_MeshNode* n = nIt->next();
10373         if ( n->isMarked() )
10374           smoothNodes.push_back( n );
10375       }
10376     }
10377     // Find out face orientation
10378     double refSign = 1;
10379     const set<TGeomID> ignoreShapes;
10380     bool isOkUV;
10381     if ( !smoothNodes.empty() )
10382     {
10383       vector<_Simplex> simplices;
10384       _Simplex::GetSimplices( smoothNodes[0], simplices, ignoreShapes );
10385       helper.GetNodeUV( F, simplices[0]._nPrev, 0, &isOkUV ); // fix UV of simplex nodes
10386       helper.GetNodeUV( F, simplices[0]._nNext, 0, &isOkUV );
10387       gp_XY uv = helper.GetNodeUV( F, smoothNodes[0], 0, &isOkUV );
10388       if ( !simplices[0].IsForward(uv, smoothNodes[0], F, helper, refSign ))
10389         refSign = -1;
10390     }
10391
10392     // Find _LayerEdge's inflated along F
10393     subEOS.clear();
10394     lEdges.clear();
10395     {
10396       SMESH_subMeshIteratorPtr subIt = sm->getDependsOnIterator(/*includeSelf=*/false,
10397                                                                 /*complexFirst=*/true); //!!!
10398       while ( subIt->more() )
10399       {
10400         const TGeomID subID = subIt->next()->GetId();
10401         if ( data._noShrinkShapes.count( subID ))
10402           continue;
10403         _EdgesOnShape* eos = data.GetShapeEdges( subID );
10404         if ( !eos || eos->_sWOL.IsNull() )
10405           if ( data2 ) // check in adjacent SOLID
10406           {
10407             eos = data2->GetShapeEdges( subID );
10408             if ( !eos || eos->_sWOL.IsNull() )
10409               continue;
10410           }
10411         subEOS.push_back( eos );
10412
10413         for ( size_t i = 0; i < eos->_edges.size(); ++i )
10414         {
10415           lEdges.push_back( eos->_edges[ i ] );
10416           prepareEdgeToShrink( *eos->_edges[ i ], *eos, helper, smDS );
10417         }
10418       }
10419     }
10420
10421     dumpFunction(SMESH_Comment("beforeShrinkFace")<<f2sd->first); // debug
10422     SMDS_ElemIteratorPtr fIt = smDS->GetElements();
10423     while ( fIt->more() )
10424       if ( const SMDS_MeshElement* f = fIt->next() )
10425         dumpChangeNodes( f );
10426     dumpFunctionEnd();
10427
10428     // Replace source nodes by target nodes in mesh faces to shrink
10429     dumpFunction(SMESH_Comment("replNodesOnFace")<<f2sd->first); // debug
10430     const SMDS_MeshNode* nodes[20];
10431     for ( size_t iS = 0; iS < subEOS.size(); ++iS )
10432     {
10433       _EdgesOnShape& eos = * subEOS[ iS ];
10434       for ( size_t i = 0; i < eos._edges.size(); ++i )
10435       {
10436         _LayerEdge& edge = *eos._edges[i];
10437         const SMDS_MeshNode* srcNode = edge._nodes[0];
10438         const SMDS_MeshNode* tgtNode = edge._nodes.back();
10439         SMDS_ElemIteratorPtr fIt = srcNode->GetInverseElementIterator(SMDSAbs_Face);
10440         while ( fIt->more() )
10441         {
10442           const SMDS_MeshElement* f = fIt->next();
10443           if ( !smDS->Contains( f ) || !f->isMarked() )
10444             continue;
10445           SMDS_NodeIteratorPtr nIt = f->nodeIterator();
10446           for ( int iN = 0; nIt->more(); ++iN )
10447           {
10448             const SMDS_MeshNode* n = nIt->next();
10449             nodes[iN] = ( n == srcNode ? tgtNode : n );
10450           }
10451           helper.GetMeshDS()->ChangeElementNodes( f, nodes, f->NbNodes() );
10452           dumpChangeNodes( f );
10453         }
10454       }
10455     }
10456     dumpFunctionEnd();
10457
10458     // find out if a FACE is concave
10459     const bool isConcaveFace = isConcave( F, helper );
10460
10461     // Create _SmoothNode's on face F
10462     vector< _SmoothNode > nodesToSmooth( smoothNodes.size() );
10463     {
10464       dumpFunction(SMESH_Comment("fixUVOnFace")<<f2sd->first); // debug
10465       const bool sortSimplices = isConcaveFace;
10466       for ( size_t i = 0; i < smoothNodes.size(); ++i )
10467       {
10468         const SMDS_MeshNode* n = smoothNodes[i];
10469         nodesToSmooth[ i ]._node = n;
10470         // src nodes must be already replaced by tgt nodes to have tgt nodes in _simplices
10471         _Simplex::GetSimplices( n, nodesToSmooth[ i ]._simplices, ignoreShapes, 0, sortSimplices);
10472         // fix up incorrect uv of nodes on the FACE
10473         helper.GetNodeUV( F, n, 0, &isOkUV);
10474         dumpMove( n );
10475       }
10476       dumpFunctionEnd();
10477     }
10478     //if ( nodesToSmooth.empty() ) continue;
10479
10480     // Find EDGE's to shrink and set simpices to LayerEdge's
10481     set< _Shrinker1D* > eShri1D;
10482     {
10483       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
10484       {
10485         _EdgesOnShape& eos = * subEOS[ iS ];
10486         if ( eos.SWOLType() == TopAbs_EDGE )
10487         {
10488           SMESH_subMesh* edgeSM = _mesh->GetSubMesh( eos._sWOL );
10489           _Shrinker1D& shrinker  = e2shrMap[ edgeSM->GetId() ];
10490           eShri1D.insert( & shrinker );
10491           shrinker.AddEdge( eos._edges[0], eos, helper );
10492           VISCOUS_3D::ToClearSubWithMain( edgeSM, data._solid );
10493           // restore params of nodes on EDGE if the EDGE has been already
10494           // shrinked while shrinking other FACE
10495           shrinker.RestoreParams();
10496         }
10497         for ( size_t i = 0; i < eos._edges.size(); ++i )
10498         {
10499           _LayerEdge& edge = * eos._edges[i];
10500           _Simplex::GetSimplices( /*tgtNode=*/edge._nodes.back(), edge._simplices, ignoreShapes );
10501
10502           // additionally mark tgt node; only marked nodes will be used in SetNewLength2d()
10503           // not-marked nodes are those added by refine()
10504           edge._nodes.back()->setIsMarked( true );
10505         }
10506       }
10507     }
10508
10509     bool toFixTria = false; // to improve quality of trias by diagonal swap
10510     if ( isConcaveFace )
10511     {
10512       const bool hasTria = _mesh->NbTriangles(), hasQuad = _mesh->NbQuadrangles();
10513       if ( hasTria != hasQuad ) {
10514         toFixTria = hasTria;
10515       }
10516       else {
10517         set<int> nbNodesSet;
10518         SMDS_ElemIteratorPtr fIt = smDS->GetElements();
10519         while ( fIt->more() && nbNodesSet.size() < 2 )
10520           nbNodesSet.insert( fIt->next()->NbCornerNodes() );
10521         toFixTria = ( *nbNodesSet.begin() == 3 );
10522       }
10523     }
10524
10525     // ==================
10526     // Perform shrinking
10527     // ==================
10528
10529     bool shrinked = true;
10530     int nbBad, shriStep=0, smooStep=0;
10531     _SmoothNode::SmoothType smoothType
10532       = isConcaveFace ? _SmoothNode::ANGULAR : _SmoothNode::LAPLACIAN;
10533     SMESH_Comment errMsg;
10534     while ( shrinked )
10535     {
10536       shriStep++;
10537       // Move boundary nodes (actually just set new UV)
10538       // -----------------------------------------------
10539       dumpFunction(SMESH_Comment("moveBoundaryOnF")<<f2sd->first<<"_st"<<shriStep ); // debug
10540       shrinked = false;
10541       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
10542       {
10543         _EdgesOnShape& eos = * subEOS[ iS ];
10544         for ( size_t i = 0; i < eos._edges.size(); ++i )
10545         {
10546           shrinked |= eos._edges[i]->SetNewLength2d( surface, F, eos, helper );
10547         }
10548       }
10549       dumpFunctionEnd();
10550
10551       // Move nodes on EDGE's
10552       // (XYZ is set as soon as a needed length reached in SetNewLength2d())
10553       set< _Shrinker1D* >::iterator shr = eShri1D.begin();
10554       for ( ; shr != eShri1D.end(); ++shr )
10555         (*shr)->Compute( /*set3D=*/false, helper );
10556
10557       // Smoothing in 2D
10558       // -----------------
10559       int nbNoImpSteps = 0;
10560       bool       moved = true;
10561       nbBad = 1;
10562       while (( nbNoImpSteps < 5 && nbBad > 0) && moved)
10563       {
10564         dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
10565
10566         int oldBadNb = nbBad;
10567         nbBad = 0;
10568         moved = false;
10569         // '% 5' minimizes NB FUNCTIONS on viscous_layers_00/B2 case
10570         _SmoothNode::SmoothType smooTy = ( smooStep % 5 ) ? smoothType : _SmoothNode::LAPLACIAN;
10571         for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
10572         {
10573           moved |= nodesToSmooth[i].Smooth( nbBad, surface, helper, refSign,
10574                                             smooTy, /*set3D=*/isConcaveFace);
10575         }
10576         if ( nbBad < oldBadNb )
10577           nbNoImpSteps = 0;
10578         else
10579           nbNoImpSteps++;
10580
10581         dumpFunctionEnd();
10582       }
10583
10584       errMsg.clear();
10585       if ( nbBad > 0 )
10586         errMsg << "Can't shrink 2D mesh on face " << f2sd->first;
10587       if ( shriStep > 200 )
10588         errMsg << "Infinite loop at shrinking 2D mesh on face " << f2sd->first;
10589       if ( !errMsg.empty() )
10590         break;
10591
10592       // Fix narrow triangles by swapping diagonals
10593       // ---------------------------------------
10594       if ( toFixTria )
10595       {
10596         set<const SMDS_MeshNode*> usedNodes;
10597         fixBadFaces( F, helper, /*is2D=*/true, shriStep, & usedNodes); // swap diagonals
10598
10599         // update working data
10600         set<const SMDS_MeshNode*>::iterator n;
10601         for ( size_t i = 0; i < nodesToSmooth.size() && !usedNodes.empty(); ++i )
10602         {
10603           n = usedNodes.find( nodesToSmooth[ i ]._node );
10604           if ( n != usedNodes.end())
10605           {
10606             _Simplex::GetSimplices( nodesToSmooth[ i ]._node,
10607                                     nodesToSmooth[ i ]._simplices,
10608                                     ignoreShapes, NULL,
10609                                     /*sortSimplices=*/ smoothType == _SmoothNode::ANGULAR );
10610             usedNodes.erase( n );
10611           }
10612         }
10613         for ( size_t i = 0; i < lEdges.size() && !usedNodes.empty(); ++i )
10614         {
10615           n = usedNodes.find( /*tgtNode=*/ lEdges[i]->_nodes.back() );
10616           if ( n != usedNodes.end())
10617           {
10618             _Simplex::GetSimplices( lEdges[i]->_nodes.back(),
10619                                     lEdges[i]->_simplices,
10620                                     ignoreShapes );
10621             usedNodes.erase( n );
10622           }
10623         }
10624       }
10625       // TODO: check effect of this additional smooth
10626       // additional laplacian smooth to increase allowed shrink step
10627       // for ( int st = 1; st; --st )
10628       // {
10629       //   dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
10630       //   for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
10631       //   {
10632       //     nodesToSmooth[i].Smooth( nbBad,surface,helper,refSign,
10633       //                              _SmoothNode::LAPLACIAN,/*set3D=*/false);
10634       //   }
10635       // }
10636
10637     } // while ( shrinked )
10638
10639     if ( !errMsg.empty() ) // Try to re-compute the shrink FACE
10640     {
10641       debugMsg( "Re-compute FACE " << f2sd->first << " because " << errMsg );
10642
10643       // remove faces
10644       SMESHDS_SubMesh* psm = data._proxyMesh->getFaceSubM( F );
10645       {
10646         vector< const SMDS_MeshElement* > facesToRm;
10647         if ( psm )
10648         {
10649           facesToRm.reserve( psm->NbElements() );
10650           for ( SMDS_ElemIteratorPtr ite = psm->GetElements(); ite->more(); )
10651             facesToRm.push_back( ite->next() );
10652
10653           for ( size_t i = 0 ; i < _sdVec.size(); ++i )
10654             if (( psm = _sdVec[i]._proxyMesh->getFaceSubM( F )))
10655               psm->Clear();
10656         }
10657         for ( size_t i = 0; i < facesToRm.size(); ++i )
10658           getMeshDS()->RemoveFreeElement( facesToRm[i], smDS, /*fromGroups=*/false );
10659       }
10660       // remove nodes
10661       {
10662         TIDSortedNodeSet nodesToKeep; // nodes of _LayerEdge to keep
10663         for ( size_t iS = 0; iS < subEOS.size(); ++iS ) {
10664           for ( size_t i = 0; i < subEOS[iS]->_edges.size(); ++i )
10665             nodesToKeep.insert( ++( subEOS[iS]->_edges[i]->_nodes.begin() ),
10666                                 subEOS[iS]->_edges[i]->_nodes.end() );
10667         }
10668         SMDS_NodeIteratorPtr itn = smDS->GetNodes();
10669         while ( itn->more() ) {
10670           const SMDS_MeshNode* n = itn->next();
10671           if ( !nodesToKeep.count( n ))
10672             getMeshDS()->RemoveFreeNode( n, smDS, /*fromGroups=*/false );
10673         }
10674       }
10675       // restore position and UV of target nodes
10676       gp_Pnt p;
10677       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
10678         for ( size_t i = 0; i < subEOS[iS]->_edges.size(); ++i )
10679         {
10680           _LayerEdge*       edge = subEOS[iS]->_edges[i];
10681           SMDS_MeshNode* tgtNode = const_cast< SMDS_MeshNode*& >( edge->_nodes.back() );
10682           if ( edge->_pos.empty() ||
10683                edge->Is( _LayerEdge::SHRUNK )) continue;
10684           if ( subEOS[iS]->SWOLType() == TopAbs_FACE )
10685           {
10686             SMDS_FacePositionPtr pos = tgtNode->GetPosition();
10687             pos->SetUParameter( edge->_pos[0].X() );
10688             pos->SetVParameter( edge->_pos[0].Y() );
10689             p = surface->Value( edge->_pos[0].X(), edge->_pos[0].Y() );
10690           }
10691           else
10692           {
10693             SMDS_EdgePositionPtr pos = tgtNode->GetPosition();
10694             pos->SetUParameter( edge->_pos[0].Coord( U_TGT ));
10695             p = BRepAdaptor_Curve( TopoDS::Edge( subEOS[iS]->_sWOL )).Value( pos->GetUParameter() );
10696           }
10697           tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
10698           dumpMove( tgtNode );
10699         }
10700       // shrink EDGE sub-meshes and set proxy sub-meshes
10701       UVPtStructVec uvPtVec;
10702       set< _Shrinker1D* >::iterator shrIt = eShri1D.begin();
10703       for ( shrIt = eShri1D.begin(); shrIt != eShri1D.end(); ++shrIt )
10704       {
10705         _Shrinker1D* shr = (*shrIt);
10706         shr->Compute( /*set3D=*/true, helper );
10707
10708         // set proxy mesh of EDGEs w/o layers
10709         map< double, const SMDS_MeshNode* > nodes;
10710         SMESH_Algo::GetSortedNodesOnEdge( getMeshDS(), shr->GeomEdge(),/*skipMedium=*/true, nodes);
10711         // remove refinement nodes
10712         const SMDS_MeshNode* sn0 = shr->SrcNode(0), *sn1 = shr->SrcNode(1);
10713         const SMDS_MeshNode* tn0 = shr->TgtNode(0), *tn1 = shr->TgtNode(1);
10714         map< double, const SMDS_MeshNode* >::iterator u2n = nodes.begin();
10715         if ( u2n->second == sn0 || u2n->second == sn1 )
10716         {
10717           while ( u2n->second != tn0 && u2n->second != tn1 )
10718             ++u2n;
10719           nodes.erase( nodes.begin(), u2n );
10720         }
10721         u2n = --nodes.end();
10722         if ( u2n->second == sn0 || u2n->second == sn1 )
10723         {
10724           while ( u2n->second != tn0 && u2n->second != tn1 )
10725             --u2n;
10726           nodes.erase( ++u2n, nodes.end() );
10727         }
10728         // set proxy sub-mesh
10729         uvPtVec.resize( nodes.size() );
10730         u2n = nodes.begin();
10731         BRepAdaptor_Curve2d curve( shr->GeomEdge(), F );
10732         for ( size_t i = 0; i < nodes.size(); ++i, ++u2n )
10733         {
10734           uvPtVec[ i ].node = u2n->second;
10735           uvPtVec[ i ].param = u2n->first;
10736           uvPtVec[ i ].SetUV( curve.Value( u2n->first ).XY() );
10737         }
10738         StdMeshers_FaceSide fSide( uvPtVec, F, shr->GeomEdge(), _mesh );
10739         StdMeshers_ViscousLayers2D::SetProxyMeshOfEdge( fSide );
10740       }
10741
10742       // set proxy mesh of EDGEs with layers
10743       vector< _LayerEdge* > edges;
10744       for ( size_t iS = 0; iS < subEOS.size(); ++iS )
10745       {
10746         _EdgesOnShape& eos = * subEOS[ iS ];
10747         if ( eos.ShapeType() != TopAbs_EDGE ) continue;
10748
10749         const TopoDS_Edge& E = TopoDS::Edge( eos._shape );
10750         data.SortOnEdge( E, eos._edges );
10751
10752         edges.clear();
10753         if ( _EdgesOnShape* eov = data.GetShapeEdges( helper.IthVertex( 0, E, /*CumOri=*/false )))
10754           if ( !eov->_edges.empty() )
10755             edges.push_back( eov->_edges[0] ); // on 1st VERTEX
10756
10757         edges.insert( edges.end(), eos._edges.begin(), eos._edges.end() );
10758
10759         if ( _EdgesOnShape* eov = data.GetShapeEdges( helper.IthVertex( 1, E, /*CumOri=*/false )))
10760           if ( !eov->_edges.empty() )
10761             edges.push_back( eov->_edges[0] ); // on last VERTEX
10762
10763         uvPtVec.resize( edges.size() );
10764         for ( size_t i = 0; i < edges.size(); ++i )
10765         {
10766           uvPtVec[ i ].node = edges[i]->_nodes.back();
10767           uvPtVec[ i ].param = helper.GetNodeU( E, edges[i]->_nodes[0] );
10768           uvPtVec[ i ].SetUV( helper.GetNodeUV( F, edges[i]->_nodes.back() ));
10769         }
10770         BRep_Tool::Range( E, uvPtVec[0].param, uvPtVec.back().param );
10771         StdMeshers_FaceSide fSide( uvPtVec, F, E, _mesh );
10772         StdMeshers_ViscousLayers2D::SetProxyMeshOfEdge( fSide );
10773       }
10774       // temporary clear the FACE sub-mesh from faces made by refine()
10775       vector< const SMDS_MeshElement* > elems;
10776       elems.reserve( smDS->NbElements() + smDS->NbNodes() );
10777       for ( SMDS_ElemIteratorPtr ite = smDS->GetElements(); ite->more(); )
10778         elems.push_back( ite->next() );
10779       for ( SMDS_NodeIteratorPtr ite = smDS->GetNodes(); ite->more(); )
10780         elems.push_back( ite->next() );
10781       smDS->Clear();
10782
10783       // compute the mesh on the FACE
10784       sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
10785       sm->ComputeStateEngine( SMESH_subMesh::COMPUTE_SUBMESH );
10786
10787       // re-fill proxy sub-meshes of the FACE
10788       for ( size_t i = 0 ; i < _sdVec.size(); ++i )
10789         if (( psm = _sdVec[i]._proxyMesh->getFaceSubM( F )))
10790           for ( SMDS_ElemIteratorPtr ite = smDS->GetElements(); ite->more(); )
10791             psm->AddElement( ite->next() );
10792
10793       // re-fill smDS
10794       for ( size_t i = 0; i < elems.size(); ++i )
10795         smDS->AddElement( elems[i] );
10796
10797       if ( sm->GetComputeState() != SMESH_subMesh::COMPUTE_OK )
10798         return error( errMsg );
10799
10800     } // end of re-meshing in case of failed smoothing
10801     else
10802     {
10803       // No wrongly shaped faces remain; final smooth. Set node XYZ.
10804       bool isStructuredFixed = false;
10805       if ( SMESH_2D_Algo* algo = dynamic_cast<SMESH_2D_Algo*>( sm->GetAlgo() ))
10806         isStructuredFixed = algo->FixInternalNodes( *data._proxyMesh, F );
10807       if ( !isStructuredFixed )
10808       {
10809         if ( isConcaveFace ) // fix narrow faces by swapping diagonals
10810           fixBadFaces( F, helper, /*is2D=*/false, ++shriStep );
10811
10812         for ( int st = 3; st; --st )
10813         {
10814           switch( st ) {
10815           case 1: smoothType = _SmoothNode::LAPLACIAN; break;
10816           case 2: smoothType = _SmoothNode::LAPLACIAN; break;
10817           case 3: smoothType = _SmoothNode::ANGULAR; break;
10818           }
10819           dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
10820           for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
10821           {
10822             nodesToSmooth[i].Smooth( nbBad,surface,helper,refSign,
10823                                      smoothType,/*set3D=*/st==1 );
10824           }
10825           dumpFunctionEnd();
10826         }
10827       }
10828       if ( !getMeshDS()->IsEmbeddedMode() )
10829         // Log node movement
10830         for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
10831         {
10832           SMESH_TNodeXYZ p ( nodesToSmooth[i]._node );
10833           getMeshDS()->MoveNode( nodesToSmooth[i]._node, p.X(), p.Y(), p.Z() );
10834         }
10835     }
10836
10837     // Set an event listener to clear FACE sub-mesh together with SOLID sub-mesh
10838     VISCOUS_3D::ToClearSubWithMain( sm, data._solid );
10839     if ( data2 )
10840       VISCOUS_3D::ToClearSubWithMain( sm, data2->_solid );
10841
10842   } // loop on FACES to shrink mesh on
10843
10844
10845   // Replace source nodes by target nodes in shrinked mesh edges
10846
10847   map< int, _Shrinker1D >::iterator e2shr = e2shrMap.begin();
10848   for ( ; e2shr != e2shrMap.end(); ++e2shr )
10849     e2shr->second.SwapSrcTgtNodes( getMeshDS() );
10850
10851   return true;
10852 }
10853
10854 //================================================================================
10855 /*!
10856  * \brief Computes 2d shrink direction and finds nodes limiting shrinking
10857  */
10858 //================================================================================
10859
10860 bool _ViscousBuilder::prepareEdgeToShrink( _LayerEdge&            edge,
10861                                            _EdgesOnShape&         eos,
10862                                            SMESH_MesherHelper&    helper,
10863                                            const SMESHDS_SubMesh* faceSubMesh)
10864 {
10865   const SMDS_MeshNode* srcNode = edge._nodes[0];
10866   const SMDS_MeshNode* tgtNode = edge._nodes.back();
10867
10868   if ( eos.SWOLType() == TopAbs_FACE )
10869   {
10870     if ( tgtNode->GetPosition()->GetDim() != 2 ) // not inflated edge
10871     {
10872       edge._pos.clear();
10873       edge.Set( _LayerEdge::SHRUNK );
10874       return srcNode == tgtNode;
10875     }
10876     gp_XY srcUV ( edge._pos[0].X(), edge._pos[0].Y() );          //helper.GetNodeUV( F, srcNode );
10877     gp_XY tgtUV = edge.LastUV( TopoDS::Face( eos._sWOL ), eos ); //helper.GetNodeUV( F, tgtNode );
10878     gp_Vec2d uvDir( srcUV, tgtUV );
10879     double uvLen = uvDir.Magnitude();
10880     uvDir /= uvLen;
10881     edge._normal.SetCoord( uvDir.X(),uvDir.Y(), 0 );
10882     edge._len = uvLen;
10883
10884     //edge._pos.resize(1);
10885     edge._pos[0].SetCoord( tgtUV.X(), tgtUV.Y(), 0 );
10886
10887     // set UV of source node to target node
10888     SMDS_FacePositionPtr pos = tgtNode->GetPosition();
10889     pos->SetUParameter( srcUV.X() );
10890     pos->SetVParameter( srcUV.Y() );
10891   }
10892   else // _sWOL is TopAbs_EDGE
10893   {
10894     if ( tgtNode->GetPosition()->GetDim() != 1 ) // not inflated edge
10895     {
10896       edge._pos.clear();
10897       edge.Set( _LayerEdge::SHRUNK );
10898       return srcNode == tgtNode;
10899     }
10900     const TopoDS_Edge&    E = TopoDS::Edge( eos._sWOL );
10901     SMESHDS_SubMesh* edgeSM = getMeshDS()->MeshElements( E );
10902     if ( !edgeSM || edgeSM->NbElements() == 0 )
10903       return error(SMESH_Comment("Not meshed EDGE ") << getMeshDS()->ShapeToIndex( E ));
10904
10905     const SMDS_MeshNode* n2 = 0;
10906     SMDS_ElemIteratorPtr eIt = srcNode->GetInverseElementIterator(SMDSAbs_Edge);
10907     while ( eIt->more() && !n2 )
10908     {
10909       const SMDS_MeshElement* e = eIt->next();
10910       if ( !edgeSM->Contains(e)) continue;
10911       n2 = e->GetNode( 0 );
10912       if ( n2 == srcNode ) n2 = e->GetNode( 1 );
10913     }
10914     if ( !n2 )
10915       return error(SMESH_Comment("Wrongly meshed EDGE ") << getMeshDS()->ShapeToIndex( E ));
10916
10917     double uSrc = helper.GetNodeU( E, srcNode, n2 );
10918     double uTgt = helper.GetNodeU( E, tgtNode, srcNode );
10919     double u2   = helper.GetNodeU( E, n2,      srcNode );
10920
10921     //edge._pos.clear();
10922
10923     if ( fabs( uSrc-uTgt ) < 0.99 * fabs( uSrc-u2 ))
10924     {
10925       // tgtNode is located so that it does not make faces with wrong orientation
10926       edge.Set( _LayerEdge::SHRUNK );
10927       return true;
10928     }
10929     //edge._pos.resize(1);
10930     edge._pos[0].SetCoord( U_TGT, uTgt );
10931     edge._pos[0].SetCoord( U_SRC, uSrc );
10932     edge._pos[0].SetCoord( LEN_TGT, fabs( uSrc-uTgt ));
10933
10934     edge._simplices.resize( 1 );
10935     edge._simplices[0]._nPrev = n2;
10936
10937     // set U of source node to the target node
10938     SMDS_EdgePositionPtr pos = tgtNode->GetPosition();
10939     pos->SetUParameter( uSrc );
10940   }
10941   return true;
10942 }
10943
10944 //================================================================================
10945 /*!
10946  * \brief Restore position of a sole node of a _LayerEdge based on _noShrinkShapes
10947  */
10948 //================================================================================
10949
10950 void _ViscousBuilder::restoreNoShrink( _LayerEdge& edge ) const
10951 {
10952   if ( edge._nodes.size() == 1 )
10953   {
10954     edge._pos.clear();
10955     edge._len = 0;
10956
10957     const SMDS_MeshNode* srcNode = edge._nodes[0];
10958     TopoDS_Shape S = SMESH_MesherHelper::GetSubShapeByNode( srcNode, getMeshDS() );
10959     if ( S.IsNull() ) return;
10960
10961     gp_Pnt p;
10962
10963     switch ( S.ShapeType() )
10964     {
10965     case TopAbs_EDGE:
10966     {
10967       double f,l;
10968       TopLoc_Location loc;
10969       Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( S ), loc, f, l );
10970       if ( curve.IsNull() ) return;
10971       SMDS_EdgePositionPtr ePos = srcNode->GetPosition();
10972       p = curve->Value( ePos->GetUParameter() );
10973       break;
10974     }
10975     case TopAbs_VERTEX:
10976     {
10977       p = BRep_Tool::Pnt( TopoDS::Vertex( S ));
10978       break;
10979     }
10980     default: return;
10981     }
10982     getMeshDS()->MoveNode( srcNode, p.X(), p.Y(), p.Z() );
10983     dumpMove( srcNode );
10984   }
10985 }
10986
10987 //================================================================================
10988 /*!
10989  * \brief Try to fix triangles with high aspect ratio by swapping diagonals
10990  */
10991 //================================================================================
10992
10993 void _ViscousBuilder::fixBadFaces(const TopoDS_Face&          F,
10994                                   SMESH_MesherHelper&         helper,
10995                                   const bool                  is2D,
10996                                   const int                   step,
10997                                   set<const SMDS_MeshNode*> * involvedNodes)
10998 {
10999   SMESH::Controls::AspectRatio qualifier;
11000   SMESH::Controls::TSequenceOfXYZ points(3), points1(3), points2(3);
11001   const double maxAspectRatio = is2D ? 4. : 2;
11002   _NodeCoordHelper xyz( F, helper, is2D );
11003
11004   // find bad triangles
11005
11006   vector< const SMDS_MeshElement* > badTrias;
11007   vector< double >                  badAspects;
11008   SMESHDS_SubMesh*      sm = helper.GetMeshDS()->MeshElements( F );
11009   SMDS_ElemIteratorPtr fIt = sm->GetElements();
11010   while ( fIt->more() )
11011   {
11012     const SMDS_MeshElement * f = fIt->next();
11013     if ( f->NbCornerNodes() != 3 ) continue;
11014     for ( int iP = 0; iP < 3; ++iP ) points(iP+1) = xyz( f->GetNode(iP));
11015     double aspect = qualifier.GetValue( points );
11016     if ( aspect > maxAspectRatio )
11017     {
11018       badTrias.push_back( f );
11019       badAspects.push_back( aspect );
11020     }
11021   }
11022   if ( step == 1 )
11023   {
11024     dumpFunction(SMESH_Comment("beforeSwapDiagonals_F")<<helper.GetSubShapeID());
11025     SMDS_ElemIteratorPtr fIt = sm->GetElements();
11026     while ( fIt->more() )
11027     {
11028       const SMDS_MeshElement * f = fIt->next();
11029       if ( f->NbCornerNodes() == 3 )
11030         dumpChangeNodes( f );
11031     }
11032     dumpFunctionEnd();
11033   }
11034   if ( badTrias.empty() )
11035     return;
11036
11037   // find couples of faces to swap diagonal
11038
11039   typedef pair < const SMDS_MeshElement* , const SMDS_MeshElement* > T2Trias;
11040   vector< T2Trias > triaCouples; 
11041
11042   TIDSortedElemSet involvedFaces, emptySet;
11043   for ( size_t iTia = 0; iTia < badTrias.size(); ++iTia )
11044   {
11045     T2Trias trias    [3];
11046     double  aspRatio [3];
11047     int i1, i2, i3;
11048
11049     if ( !involvedFaces.insert( badTrias[iTia] ).second )
11050       continue;
11051     for ( int iP = 0; iP < 3; ++iP )
11052       points(iP+1) = xyz( badTrias[iTia]->GetNode(iP));
11053
11054     // find triangles adjacent to badTrias[iTia] with better aspect ratio after diag-swaping
11055     int bestCouple = -1;
11056     for ( int iSide = 0; iSide < 3; ++iSide )
11057     {
11058       const SMDS_MeshNode* n1 = badTrias[iTia]->GetNode( iSide );
11059       const SMDS_MeshNode* n2 = badTrias[iTia]->GetNode(( iSide+1 ) % 3 );
11060       trias [iSide].first  = badTrias[iTia];
11061       trias [iSide].second = SMESH_MeshAlgos::FindFaceInSet( n1, n2, emptySet, involvedFaces,
11062                                                              & i1, & i2 );
11063       if (( ! trias[iSide].second ) ||
11064           ( trias[iSide].second->NbCornerNodes() != 3 ) ||
11065           ( ! sm->Contains( trias[iSide].second )))
11066         continue;
11067
11068       // aspect ratio of an adjacent tria
11069       for ( int iP = 0; iP < 3; ++iP )
11070         points2(iP+1) = xyz( trias[iSide].second->GetNode(iP));
11071       double aspectInit = qualifier.GetValue( points2 );
11072
11073       // arrange nodes as after diag-swaping
11074       if ( helper.WrapIndex( i1+1, 3 ) == i2 )
11075         i3 = helper.WrapIndex( i1-1, 3 );
11076       else
11077         i3 = helper.WrapIndex( i1+1, 3 );
11078       points1 = points;
11079       points1( 1+ iSide ) = points2( 1+ i3 );
11080       points2( 1+ i2    ) = points1( 1+ ( iSide+2 ) % 3 );
11081
11082       // aspect ratio after diag-swaping
11083       aspRatio[ iSide ] = qualifier.GetValue( points1 ) + qualifier.GetValue( points2 );
11084       if ( aspRatio[ iSide ] > aspectInit + badAspects[ iTia ] )
11085         continue;
11086
11087       // prevent inversion of a triangle
11088       gp_Vec norm1 = gp_Vec( points1(1), points1(3) ) ^ gp_Vec( points1(1), points1(2) );
11089       gp_Vec norm2 = gp_Vec( points2(1), points2(3) ) ^ gp_Vec( points2(1), points2(2) );
11090       if ( norm1 * norm2 < 0. && norm1.Angle( norm2 ) > 70./180.*M_PI )
11091         continue;
11092
11093       if ( bestCouple < 0 || aspRatio[ bestCouple ] > aspRatio[ iSide ] )
11094         bestCouple = iSide;
11095     }
11096
11097     if ( bestCouple >= 0 )
11098     {
11099       triaCouples.push_back( trias[bestCouple] );
11100       involvedFaces.insert ( trias[bestCouple].second );
11101     }
11102     else
11103     {
11104       involvedFaces.erase( badTrias[iTia] );
11105     }
11106   }
11107   if ( triaCouples.empty() )
11108     return;
11109
11110   // swap diagonals
11111
11112   SMESH_MeshEditor editor( helper.GetMesh() );
11113   dumpFunction(SMESH_Comment("beforeSwapDiagonals_F")<<helper.GetSubShapeID()<<"_"<<step);
11114   for ( size_t i = 0; i < triaCouples.size(); ++i )
11115   {
11116     dumpChangeNodes( triaCouples[i].first );
11117     dumpChangeNodes( triaCouples[i].second );
11118     editor.InverseDiag( triaCouples[i].first, triaCouples[i].second );
11119   }
11120
11121   if ( involvedNodes )
11122     for ( size_t i = 0; i < triaCouples.size(); ++i )
11123     {
11124       involvedNodes->insert( triaCouples[i].first->begin_nodes(),
11125                              triaCouples[i].first->end_nodes() );
11126       involvedNodes->insert( triaCouples[i].second->begin_nodes(),
11127                              triaCouples[i].second->end_nodes() );
11128     }
11129
11130   // just for debug dump resulting triangles
11131   dumpFunction(SMESH_Comment("swapDiagonals_F")<<helper.GetSubShapeID()<<"_"<<step);
11132   for ( size_t i = 0; i < triaCouples.size(); ++i )
11133   {
11134     dumpChangeNodes( triaCouples[i].first );
11135     dumpChangeNodes( triaCouples[i].second );
11136   }
11137 }
11138
11139 //================================================================================
11140 /*!
11141  * \brief Move target node to it's final position on the FACE during shrinking
11142  */
11143 //================================================================================
11144
11145 bool _LayerEdge::SetNewLength2d( Handle(Geom_Surface)& surface,
11146                                  const TopoDS_Face&    F,
11147                                  _EdgesOnShape&        eos,
11148                                  SMESH_MesherHelper&   helper )
11149 {
11150   if ( Is( SHRUNK ))
11151     return false; // already at the target position
11152
11153   SMDS_MeshNode* tgtNode = const_cast< SMDS_MeshNode*& >( _nodes.back() );
11154
11155   if ( eos.SWOLType() == TopAbs_FACE )
11156   {
11157     gp_XY    curUV = helper.GetNodeUV( F, tgtNode );
11158     gp_Pnt2d tgtUV( _pos[0].X(), _pos[0].Y() );
11159     gp_Vec2d uvDir( _normal.X(), _normal.Y() );
11160     const double uvLen = tgtUV.Distance( curUV );
11161     const double kSafe = Max( 0.5, 1. - 0.1 * _simplices.size() );
11162
11163     // Select shrinking step such that not to make faces with wrong orientation.
11164     double stepSize = 1e100;
11165     for ( size_t i = 0; i < _simplices.size(); ++i )
11166     {
11167       if ( !_simplices[i]._nPrev->isMarked() ||
11168            !_simplices[i]._nNext->isMarked() )
11169         continue; // simplex of quadrangle created by addBoundaryElements()
11170
11171       // find intersection of 2 lines: curUV-tgtUV and that connecting simplex nodes
11172       gp_XY uvN1 = helper.GetNodeUV( F, _simplices[i]._nPrev );
11173       gp_XY uvN2 = helper.GetNodeUV( F, _simplices[i]._nNext );
11174       gp_XY dirN = uvN2 - uvN1;
11175       double det = uvDir.Crossed( dirN );
11176       if ( Abs( det )  < std::numeric_limits<double>::min() ) continue;
11177       gp_XY dirN2Cur = curUV - uvN1;
11178       double step = dirN.Crossed( dirN2Cur ) / det;
11179       if ( step > 0 )
11180         stepSize = Min( step, stepSize );
11181     }
11182     gp_Pnt2d newUV;
11183     if ( uvLen <= stepSize )
11184     {
11185       newUV = tgtUV;
11186       Set( SHRUNK );
11187       //_pos.clear();
11188     }
11189     else if ( stepSize > 0 )
11190     {
11191       newUV = curUV + uvDir.XY() * stepSize * kSafe;
11192     }
11193     else
11194     {
11195       return true;
11196     }
11197     SMDS_FacePositionPtr pos = tgtNode->GetPosition();
11198     pos->SetUParameter( newUV.X() );
11199     pos->SetVParameter( newUV.Y() );
11200
11201 #ifdef __myDEBUG
11202     gp_Pnt p = surface->Value( newUV.X(), newUV.Y() );
11203     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
11204     dumpMove( tgtNode );
11205 #endif
11206   }
11207   else // _sWOL is TopAbs_EDGE
11208   {
11209     const TopoDS_Edge&      E = TopoDS::Edge( eos._sWOL );
11210     const SMDS_MeshNode*   n2 = _simplices[0]._nPrev;
11211     SMDS_EdgePositionPtr tgtPos = tgtNode->GetPosition();
11212
11213     const double u2     = helper.GetNodeU( E, n2, tgtNode );
11214     const double uSrc   = _pos[0].Coord( U_SRC );
11215     const double lenTgt = _pos[0].Coord( LEN_TGT );
11216
11217     double newU = _pos[0].Coord( U_TGT );
11218     if ( lenTgt < 0.99 * fabs( uSrc-u2 )) // n2 got out of src-tgt range
11219     {
11220       Set( _LayerEdge::SHRUNK );
11221       //_pos.clear();
11222     }
11223     else
11224     {
11225       newU = 0.1 * tgtPos->GetUParameter() + 0.9 * u2;
11226     }
11227     tgtPos->SetUParameter( newU );
11228 #ifdef __myDEBUG
11229     gp_XY newUV = helper.GetNodeUV( F, tgtNode, _nodes[0]);
11230     gp_Pnt p = surface->Value( newUV.X(), newUV.Y() );
11231     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
11232     dumpMove( tgtNode );
11233 #endif
11234   }
11235
11236   return true;
11237 }
11238
11239 //================================================================================
11240 /*!
11241  * \brief Perform smooth on the FACE
11242  *  \retval bool - true if the node has been moved
11243  */
11244 //================================================================================
11245
11246 bool _SmoothNode::Smooth(int&                  nbBad,
11247                          Handle(Geom_Surface)& surface,
11248                          SMESH_MesherHelper&   helper,
11249                          const double          refSign,
11250                          SmoothType            how,
11251                          bool                  set3D)
11252 {
11253   const TopoDS_Face& face = TopoDS::Face( helper.GetSubShape() );
11254
11255   // get uv of surrounding nodes
11256   vector<gp_XY> uv( _simplices.size() );
11257   for ( size_t i = 0; i < _simplices.size(); ++i )
11258     uv[i] = helper.GetNodeUV( face, _simplices[i]._nPrev, _node );
11259
11260   // compute new UV for the node
11261   gp_XY newPos (0,0);
11262   if ( how == TFI && _simplices.size() == 4 )
11263   {
11264     gp_XY corners[4];
11265     for ( size_t i = 0; i < _simplices.size(); ++i )
11266       if ( _simplices[i]._nOpp )
11267         corners[i] = helper.GetNodeUV( face, _simplices[i]._nOpp, _node );
11268       else
11269         throw SALOME_Exception(LOCALIZED("TFI smoothing: _Simplex::_nOpp not set!"));
11270
11271     newPos = helper.calcTFI ( 0.5, 0.5,
11272                               corners[0], corners[1], corners[2], corners[3],
11273                               uv[1], uv[2], uv[3], uv[0] );
11274   }
11275   else if ( how == ANGULAR )
11276   {
11277     newPos = computeAngularPos( uv, helper.GetNodeUV( face, _node ), refSign );
11278   }
11279   else if ( how == CENTROIDAL && _simplices.size() > 3 )
11280   {
11281     // average centers of diagonals wieghted with their reciprocal lengths
11282     if ( _simplices.size() == 4 )
11283     {
11284       double w1 = 1. / ( uv[2]-uv[0] ).SquareModulus();
11285       double w2 = 1. / ( uv[3]-uv[1] ).SquareModulus();
11286       newPos = ( w1 * ( uv[2]+uv[0] ) + w2 * ( uv[3]+uv[1] )) / ( w1+w2 ) / 2;
11287     }
11288     else
11289     {
11290       double sumWeight = 0;
11291       int nb = _simplices.size() == 4 ? 2 : _simplices.size();
11292       for ( int i = 0; i < nb; ++i )
11293       {
11294         int iFrom = i + 2;
11295         int iTo   = i + _simplices.size() - 1;
11296         for ( int j = iFrom; j < iTo; ++j )
11297         {
11298           int i2 = SMESH_MesherHelper::WrapIndex( j, _simplices.size() );
11299           double w = 1. / ( uv[i]-uv[i2] ).SquareModulus();
11300           sumWeight += w;
11301           newPos += w * ( uv[i]+uv[i2] );
11302         }
11303       }
11304       newPos /= 2 * sumWeight; // 2 is to get a middle between uv's
11305     }
11306   }
11307   else
11308   {
11309     // Laplacian smooth
11310     for ( size_t i = 0; i < _simplices.size(); ++i )
11311       newPos += uv[i];
11312     newPos /= _simplices.size();
11313   }
11314
11315   // count quality metrics (orientation) of triangles around the node
11316   int nbOkBefore = 0;
11317   gp_XY tgtUV = helper.GetNodeUV( face, _node );
11318   for ( size_t i = 0; i < _simplices.size(); ++i )
11319     nbOkBefore += _simplices[i].IsForward( tgtUV, _node, face, helper, refSign );
11320
11321   int nbOkAfter = 0;
11322   for ( size_t i = 0; i < _simplices.size(); ++i )
11323     nbOkAfter += _simplices[i].IsForward( newPos, _node, face, helper, refSign );
11324
11325   if ( nbOkAfter < nbOkBefore )
11326   {
11327     nbBad += _simplices.size() - nbOkBefore;
11328     return false;
11329   }
11330
11331   SMDS_FacePositionPtr pos = _node->GetPosition();
11332   pos->SetUParameter( newPos.X() );
11333   pos->SetVParameter( newPos.Y() );
11334
11335 #ifdef __myDEBUG
11336   set3D = true;
11337 #endif
11338   if ( set3D )
11339   {
11340     gp_Pnt p = surface->Value( newPos.X(), newPos.Y() );
11341     const_cast< SMDS_MeshNode* >( _node )->setXYZ( p.X(), p.Y(), p.Z() );
11342     dumpMove( _node );
11343   }
11344
11345   nbBad += _simplices.size() - nbOkAfter;
11346   return ( (tgtUV-newPos).SquareModulus() > 1e-10 );
11347 }
11348
11349 //================================================================================
11350 /*!
11351  * \brief Computes new UV using angle based smoothing technic
11352  */
11353 //================================================================================
11354
11355 gp_XY _SmoothNode::computeAngularPos(vector<gp_XY>& uv,
11356                                      const gp_XY&   uvToFix,
11357                                      const double   refSign)
11358 {
11359   uv.push_back( uv.front() );
11360
11361   vector< gp_XY >  edgeDir ( uv.size() );
11362   vector< double > edgeSize( uv.size() );
11363   for ( size_t i = 1; i < edgeDir.size(); ++i )
11364   {
11365     edgeDir [i-1] = uv[i] - uv[i-1];
11366     edgeSize[i-1] = edgeDir[i-1].Modulus();
11367     if ( edgeSize[i-1] < numeric_limits<double>::min() )
11368       edgeDir[i-1].SetX( 100 );
11369     else
11370       edgeDir[i-1] /= edgeSize[i-1] * refSign;
11371   }
11372   edgeDir.back()  = edgeDir.front();
11373   edgeSize.back() = edgeSize.front();
11374
11375   gp_XY  newPos(0,0);
11376   //int    nbEdges = 0;
11377   double sumSize = 0;
11378   for ( size_t i = 1; i < edgeDir.size(); ++i )
11379   {
11380     if ( edgeDir[i-1].X() > 1. ) continue;
11381     int i1 = i-1;
11382     while ( edgeDir[i].X() > 1. && ++i < edgeDir.size() );
11383     if ( i == edgeDir.size() ) break;
11384     gp_XY p = uv[i];
11385     gp_XY norm1( -edgeDir[i1].Y(), edgeDir[i1].X() );
11386     gp_XY norm2( -edgeDir[i].Y(),  edgeDir[i].X() );
11387     gp_XY bisec = norm1 + norm2;
11388     double bisecSize = bisec.Modulus();
11389     if ( bisecSize < numeric_limits<double>::min() )
11390     {
11391       bisec = -edgeDir[i1] + edgeDir[i];
11392       bisecSize = bisec.Modulus();
11393     }
11394     bisec /= bisecSize;
11395
11396     gp_XY  dirToN  = uvToFix - p;
11397     double distToN = dirToN.Modulus();
11398     if ( bisec * dirToN < 0 )
11399       distToN = -distToN;
11400
11401     newPos += ( p + bisec * distToN ) * ( edgeSize[i1] + edgeSize[i] );
11402     //++nbEdges;
11403     sumSize += edgeSize[i1] + edgeSize[i];
11404   }
11405   newPos /= /*nbEdges * */sumSize;
11406   return newPos;
11407 }
11408
11409 //================================================================================
11410 /*!
11411  * \brief Delete _SolidData
11412  */
11413 //================================================================================
11414
11415 _SolidData::~_SolidData()
11416 {
11417   TNode2Edge::iterator n2e = _n2eMap.begin();
11418   for ( ; n2e != _n2eMap.end(); ++n2e )
11419   {
11420     _LayerEdge* & e = n2e->second;
11421     if ( e )
11422     {
11423       delete e->_curvature;
11424       if ( e->_2neibors )
11425         delete e->_2neibors->_plnNorm;
11426       delete e->_2neibors;
11427     }
11428     delete e;
11429     e = 0;
11430   }
11431   _n2eMap.clear();
11432
11433   delete _helper;
11434   _helper = 0;
11435 }
11436
11437 //================================================================================
11438 /*!
11439  * \brief Keep a _LayerEdge inflated along the EDGE
11440  */
11441 //================================================================================
11442
11443 void _Shrinker1D::AddEdge( const _LayerEdge*   e,
11444                            _EdgesOnShape&      eos,
11445                            SMESH_MesherHelper& helper )
11446 {
11447   // init
11448   if ( _nodes.empty() )
11449   {
11450     _edges[0] = _edges[1] = 0;
11451     _done = false;
11452   }
11453   // check _LayerEdge
11454   if ( e == _edges[0] || e == _edges[1] || e->_nodes.size() < 2 )
11455     return;
11456   if ( eos.SWOLType() != TopAbs_EDGE )
11457     throw SALOME_Exception(LOCALIZED("Wrong _LayerEdge is added"));
11458   if ( _edges[0] && !_geomEdge.IsSame( eos._sWOL ))
11459     throw SALOME_Exception(LOCALIZED("Wrong _LayerEdge is added"));
11460
11461   // store _LayerEdge
11462   _geomEdge = TopoDS::Edge( eos._sWOL );
11463   double f,l;
11464   BRep_Tool::Range( _geomEdge, f,l );
11465   double u = helper.GetNodeU( _geomEdge, e->_nodes[0], e->_nodes.back());
11466   _edges[ u < 0.5*(f+l) ? 0 : 1 ] = e;
11467
11468   // Update _nodes
11469
11470   const SMDS_MeshNode* tgtNode0 = TgtNode( 0 );
11471   const SMDS_MeshNode* tgtNode1 = TgtNode( 1 );
11472
11473   if ( _nodes.empty() )
11474   {
11475     SMESHDS_SubMesh * eSubMesh = helper.GetMeshDS()->MeshElements( _geomEdge );
11476     if ( !eSubMesh || eSubMesh->NbNodes() < 1 )
11477       return;
11478     TopLoc_Location loc;
11479     Handle(Geom_Curve) C = BRep_Tool::Curve( _geomEdge, loc, f,l );
11480     GeomAdaptor_Curve aCurve(C, f,l);
11481     const double totLen = GCPnts_AbscissaPoint::Length(aCurve, f, l);
11482
11483     int nbExpectNodes = eSubMesh->NbNodes();
11484     _initU  .reserve( nbExpectNodes );
11485     _normPar.reserve( nbExpectNodes );
11486     _nodes  .reserve( nbExpectNodes );
11487     SMDS_NodeIteratorPtr nIt = eSubMesh->GetNodes();
11488     while ( nIt->more() )
11489     {
11490       const SMDS_MeshNode* node = nIt->next();
11491
11492       // skip refinement nodes
11493       if ( node->NbInverseElements(SMDSAbs_Edge) == 0 ||
11494            node == tgtNode0 || node == tgtNode1 )
11495         continue;
11496       bool hasMarkedFace = false;
11497       SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
11498       while ( fIt->more() && !hasMarkedFace )
11499         hasMarkedFace = fIt->next()->isMarked();
11500       if ( !hasMarkedFace )
11501         continue;
11502
11503       _nodes.push_back( node );
11504       _initU.push_back( helper.GetNodeU( _geomEdge, node ));
11505       double len = GCPnts_AbscissaPoint::Length(aCurve, f, _initU.back());
11506       _normPar.push_back(  len / totLen );
11507     }
11508   }
11509   else
11510   {
11511     // remove target node of the _LayerEdge from _nodes
11512     size_t nbFound = 0;
11513     for ( size_t i = 0; i < _nodes.size(); ++i )
11514       if ( !_nodes[i] || _nodes[i] == tgtNode0 || _nodes[i] == tgtNode1 )
11515         _nodes[i] = 0, nbFound++;
11516     if ( nbFound == _nodes.size() )
11517       _nodes.clear();
11518   }
11519 }
11520
11521 //================================================================================
11522 /*!
11523  * \brief Move nodes on EDGE from ends where _LayerEdge's are inflated
11524  */
11525 //================================================================================
11526
11527 void _Shrinker1D::Compute(bool set3D, SMESH_MesherHelper& helper)
11528 {
11529   if ( _done || _nodes.empty())
11530     return;
11531   const _LayerEdge* e = _edges[0];
11532   if ( !e ) e = _edges[1];
11533   if ( !e ) return;
11534
11535   _done =  (( !_edges[0] || _edges[0]->Is( _LayerEdge::SHRUNK )) &&
11536             ( !_edges[1] || _edges[1]->Is( _LayerEdge::SHRUNK )));
11537
11538   double f,l;
11539   if ( set3D || _done )
11540   {
11541     Handle(Geom_Curve) C = BRep_Tool::Curve(_geomEdge, f,l);
11542     GeomAdaptor_Curve aCurve(C, f,l);
11543
11544     if ( _edges[0] )
11545       f = helper.GetNodeU( _geomEdge, _edges[0]->_nodes.back(), _nodes[0] );
11546     if ( _edges[1] )
11547       l = helper.GetNodeU( _geomEdge, _edges[1]->_nodes.back(), _nodes.back() );
11548     double totLen = GCPnts_AbscissaPoint::Length( aCurve, f, l );
11549
11550     for ( size_t i = 0; i < _nodes.size(); ++i )
11551     {
11552       if ( !_nodes[i] ) continue;
11553       double len = totLen * _normPar[i];
11554       GCPnts_AbscissaPoint discret( aCurve, len, f );
11555       if ( !discret.IsDone() )
11556         return throw SALOME_Exception(LOCALIZED("GCPnts_AbscissaPoint failed"));
11557       double u = discret.Parameter();
11558       SMDS_EdgePositionPtr pos = _nodes[i]->GetPosition();
11559       pos->SetUParameter( u );
11560       gp_Pnt p = C->Value( u );
11561       const_cast< SMDS_MeshNode*>( _nodes[i] )->setXYZ( p.X(), p.Y(), p.Z() );
11562     }
11563   }
11564   else
11565   {
11566     BRep_Tool::Range( _geomEdge, f,l );
11567     if ( _edges[0] )
11568       f = helper.GetNodeU( _geomEdge, _edges[0]->_nodes.back(), _nodes[0] );
11569     if ( _edges[1] )
11570       l = helper.GetNodeU( _geomEdge, _edges[1]->_nodes.back(), _nodes.back() );
11571     
11572     for ( size_t i = 0; i < _nodes.size(); ++i )
11573     {
11574       if ( !_nodes[i] ) continue;
11575       double u = f * ( 1-_normPar[i] ) + l * _normPar[i];
11576       SMDS_EdgePositionPtr pos = _nodes[i]->GetPosition();
11577       pos->SetUParameter( u );
11578     }
11579   }
11580 }
11581
11582 //================================================================================
11583 /*!
11584  * \brief Restore initial parameters of nodes on EDGE
11585  */
11586 //================================================================================
11587
11588 void _Shrinker1D::RestoreParams()
11589 {
11590   if ( _done )
11591     for ( size_t i = 0; i < _nodes.size(); ++i )
11592     {
11593       if ( !_nodes[i] ) continue;
11594       SMDS_EdgePositionPtr pos = _nodes[i]->GetPosition();
11595       pos->SetUParameter( _initU[i] );
11596     }
11597   _done = false;
11598 }
11599
11600 //================================================================================
11601 /*!
11602  * \brief Replace source nodes by target nodes in shrinked mesh edges
11603  */
11604 //================================================================================
11605
11606 void _Shrinker1D::SwapSrcTgtNodes( SMESHDS_Mesh* mesh )
11607 {
11608   const SMDS_MeshNode* nodes[3];
11609   for ( int i = 0; i < 2; ++i )
11610   {
11611     if ( !_edges[i] ) continue;
11612
11613     SMESHDS_SubMesh * eSubMesh = mesh->MeshElements( _geomEdge );
11614     if ( !eSubMesh ) return;
11615     const SMDS_MeshNode* srcNode = _edges[i]->_nodes[0];
11616     const SMDS_MeshNode* tgtNode = _edges[i]->_nodes.back();
11617     const SMDS_MeshNode* scdNode = _edges[i]->_nodes[1];
11618     SMDS_ElemIteratorPtr eIt = srcNode->GetInverseElementIterator(SMDSAbs_Edge);
11619     while ( eIt->more() )
11620     {
11621       const SMDS_MeshElement* e = eIt->next();
11622       if ( !eSubMesh->Contains( e ) || e->GetNodeIndex( scdNode ) >= 0 )
11623           continue;
11624       SMDS_ElemIteratorPtr nIt = e->nodesIterator();
11625       for ( int iN = 0; iN < e->NbNodes(); ++iN )
11626       {
11627         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
11628         nodes[iN] = ( n == srcNode ? tgtNode : n );
11629       }
11630       mesh->ChangeElementNodes( e, nodes, e->NbNodes() );
11631     }
11632   }
11633 }
11634
11635 //================================================================================
11636 /*!
11637  * \brief Creates 2D and 1D elements on boundaries of new prisms
11638  */
11639 //================================================================================
11640
11641 bool _ViscousBuilder::addBoundaryElements(_SolidData& data)
11642 {
11643   SMESH_MesherHelper helper( *_mesh );
11644
11645   vector< const SMDS_MeshNode* > faceNodes;
11646
11647   //for ( size_t i = 0; i < _sdVec.size(); ++i )
11648   {
11649     //_SolidData& data = _sdVec[i];
11650     TopTools_IndexedMapOfShape geomEdges;
11651     TopExp::MapShapes( data._solid, TopAbs_EDGE, geomEdges );
11652     for ( int iE = 1; iE <= geomEdges.Extent(); ++iE )
11653     {
11654       const TopoDS_Edge& E = TopoDS::Edge( geomEdges(iE));
11655       const TGeomID edgeID = getMeshDS()->ShapeToIndex( E );
11656       if ( data._noShrinkShapes.count( edgeID ))
11657         continue;
11658
11659       // Get _LayerEdge's based on E
11660
11661       map< double, const SMDS_MeshNode* > u2nodes;
11662       if ( !SMESH_Algo::GetSortedNodesOnEdge( getMeshDS(), E, /*ignoreMedium=*/false, u2nodes))
11663         continue;
11664
11665       vector< _LayerEdge* > ledges; ledges.reserve( u2nodes.size() );
11666       TNode2Edge & n2eMap = data._n2eMap;
11667       map< double, const SMDS_MeshNode* >::iterator u2n = u2nodes.begin();
11668       {
11669         //check if 2D elements are needed on E
11670         TNode2Edge::iterator n2e = n2eMap.find( u2n->second );
11671         if ( n2e == n2eMap.end() ) continue; // no layers on vertex
11672         ledges.push_back( n2e->second );
11673         u2n++;
11674         if (( n2e = n2eMap.find( u2n->second )) == n2eMap.end() )
11675           continue; // no layers on E
11676         ledges.push_back( n2eMap[ u2n->second ]);
11677
11678         const SMDS_MeshNode* tgtN0 = ledges[0]->_nodes.back();
11679         const SMDS_MeshNode* tgtN1 = ledges[1]->_nodes.back();
11680         int nbSharedPyram = 0;
11681         SMDS_ElemIteratorPtr vIt = tgtN1->GetInverseElementIterator(SMDSAbs_Volume);
11682         while ( vIt->more() )
11683         {
11684           const SMDS_MeshElement* v = vIt->next();
11685           nbSharedPyram += int( v->GetNodeIndex( tgtN0 ) >= 0 );
11686         }
11687         if ( nbSharedPyram > 1 )
11688           continue; // not free border of the pyramid
11689
11690         faceNodes.clear();
11691         faceNodes.push_back( ledges[0]->_nodes[0] );
11692         faceNodes.push_back( ledges[1]->_nodes[0] );
11693         if ( ledges[0]->_nodes.size() > 1 ) faceNodes.push_back( ledges[0]->_nodes[1] );
11694         if ( ledges[1]->_nodes.size() > 1 ) faceNodes.push_back( ledges[1]->_nodes[1] );
11695
11696         if ( getMeshDS()->FindElement( faceNodes, SMDSAbs_Face, /*noMedium=*/true))
11697           continue; // faces already created
11698       }
11699       for ( ++u2n; u2n != u2nodes.end(); ++u2n )
11700         ledges.push_back( n2eMap[ u2n->second ]);
11701
11702       // Find out orientation and type of face to create
11703
11704       bool reverse = false, isOnFace;
11705       TopoDS_Shape F;
11706
11707       map< TGeomID, TopoDS_Shape >::iterator e2f = data._shrinkShape2Shape.find( edgeID );
11708       if (( isOnFace = ( e2f != data._shrinkShape2Shape.end() )))
11709       {
11710         F = e2f->second.Oriented( TopAbs_FORWARD );
11711         reverse = ( helper.GetSubShapeOri( F, E ) == TopAbs_REVERSED );
11712         if ( helper.GetSubShapeOri( data._solid, F ) == TopAbs_REVERSED )
11713           reverse = !reverse, F.Reverse();
11714         if ( helper.IsReversedSubMesh( TopoDS::Face(F) ))
11715           reverse = !reverse;
11716       }
11717       else if ( !data._ignoreFaceIds.count( e2f->first ))
11718       {
11719         // find FACE with layers sharing E
11720         PShapeIteratorPtr fIt = helper.GetAncestors( E, *_mesh, TopAbs_FACE, &data._solid );
11721         if ( fIt->more() )
11722           F = *( fIt->next() );
11723       }
11724       // Find the sub-mesh to add new faces
11725       SMESHDS_SubMesh* sm = 0;
11726       if ( isOnFace )
11727         sm = getMeshDS()->MeshElements( F );
11728       else
11729         sm = data._proxyMesh->getFaceSubM( TopoDS::Face(F), /*create=*/true );
11730       if ( !sm )
11731         return error("error in addBoundaryElements()", data._index);
11732
11733       // Find a proxy sub-mesh of the FACE of an adjacent SOLID, which will use the new boundary
11734       // faces for 3D meshing (PAL23414)
11735       SMESHDS_SubMesh* adjSM = 0;
11736       if ( isOnFace )
11737       {
11738         const TGeomID   faceID = sm->GetID();
11739         PShapeIteratorPtr soIt = helper.GetAncestors( F, *_mesh, TopAbs_SOLID );
11740         while ( const TopoDS_Shape* solid = soIt->next() )
11741           if ( !solid->IsSame( data._solid ))
11742           {
11743             size_t iData = _solids.FindIndex( *solid ) - 1;
11744             if ( iData < _sdVec.size() &&
11745                  _sdVec[ iData ]._ignoreFaceIds.count( faceID ) &&
11746                  _sdVec[ iData ]._shrinkShape2Shape.count( edgeID ) == 0 )
11747             {
11748               SMESH_ProxyMesh::SubMesh* proxySub =
11749                 _sdVec[ iData ]._proxyMesh->getFaceSubM( TopoDS::Face( F ), /*create=*/false);
11750               if ( proxySub && proxySub->NbElements() > 0 )
11751                 adjSM = proxySub;
11752             }
11753           }
11754       }
11755
11756       // Make faces
11757       const int dj1 = reverse ? 0 : 1;
11758       const int dj2 = reverse ? 1 : 0;
11759       vector< const SMDS_MeshElement*> ff; // new faces row
11760       SMESHDS_Mesh* m = getMeshDS();
11761       for ( size_t j = 1; j < ledges.size(); ++j )
11762       {
11763         vector< const SMDS_MeshNode*>&  nn1 = ledges[j-dj1]->_nodes;
11764         vector< const SMDS_MeshNode*>&  nn2 = ledges[j-dj2]->_nodes;
11765         ff.resize( std::max( nn1.size(), nn2.size() ), NULL );
11766         if ( nn1.size() == nn2.size() )
11767         {
11768           if ( isOnFace )
11769             for ( size_t z = 1; z < nn1.size(); ++z )
11770               sm->AddElement( ff[z-1] = m->AddFace( nn1[z-1], nn2[z-1], nn2[z], nn1[z] ));
11771           else
11772             for ( size_t z = 1; z < nn1.size(); ++z )
11773               sm->AddElement( new SMDS_FaceOfNodes( nn1[z-1], nn2[z-1], nn2[z], nn1[z] ));
11774         }
11775         else if ( nn1.size() == 1 )
11776         {
11777           if ( isOnFace )
11778             for ( size_t z = 1; z < nn2.size(); ++z )
11779               sm->AddElement( ff[z-1] = m->AddFace( nn1[0], nn2[z-1], nn2[z] ));
11780           else
11781             for ( size_t z = 1; z < nn2.size(); ++z )
11782               sm->AddElement( new SMDS_FaceOfNodes( nn1[0], nn2[z-1], nn2[z] ));
11783         }
11784         else
11785         {
11786           if ( isOnFace )
11787             for ( size_t z = 1; z < nn1.size(); ++z )
11788               sm->AddElement( ff[z-1] = m->AddFace( nn1[z-1], nn2[0], nn1[z] ));
11789           else
11790             for ( size_t z = 1; z < nn1.size(); ++z )
11791               sm->AddElement( new SMDS_FaceOfNodes( nn1[z-1], nn2[0], nn2[z] ));
11792         }
11793
11794         if ( adjSM ) // add faces to a proxy SM of the adjacent SOLID
11795         {
11796           for ( size_t z = 0; z < ff.size(); ++z )
11797             if ( ff[ z ])
11798               adjSM->AddElement( ff[ z ]);
11799           ff.clear();
11800         }
11801       }
11802
11803       // Make edges
11804       for ( int isFirst = 0; isFirst < 2; ++isFirst )
11805       {
11806         _LayerEdge* edge = isFirst ? ledges.front() : ledges.back();
11807         _EdgesOnShape* eos = data.GetShapeEdges( edge );
11808         if ( eos && eos->SWOLType() == TopAbs_EDGE )
11809         {
11810           vector< const SMDS_MeshNode*>&  nn = edge->_nodes;
11811           if ( nn.size() < 2 || nn[1]->NbInverseElements( SMDSAbs_Edge ) >= 2 )
11812             continue;
11813           helper.SetSubShape( eos->_sWOL );
11814           helper.SetElementsOnShape( true );
11815           for ( size_t z = 1; z < nn.size(); ++z )
11816             helper.AddEdge( nn[z-1], nn[z] );
11817         }
11818       }
11819
11820     } // loop on EDGE's
11821   } // loop on _SolidData's
11822
11823   return true;
11824 }