Salome HOME
Deleted Study parameter
[plugins/netgenplugin.git] / src / NETGENPlugin / NETGENPlugin_Mesher.cxx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 //  NETGENPlugin : C++ implementation
24 // File      : NETGENPlugin_Mesher.cxx
25 // Author    : Michael Sazonov (OCN)
26 // Date      : 31/03/2006
27 // Project   : SALOME
28 //=============================================================================
29
30 #include "NETGENPlugin_Mesher.hxx"
31 #include "NETGENPlugin_Hypothesis_2D.hxx"
32 #include "NETGENPlugin_SimpleHypothesis_3D.hxx"
33
34 #include <SMDS_FaceOfNodes.hxx>
35 #include <SMDS_LinearEdge.hxx>
36 #include <SMDS_MeshElement.hxx>
37 #include <SMDS_MeshNode.hxx>
38 #include <SMESHDS_Mesh.hxx>
39 #include <SMESH_Block.hxx>
40 #include <SMESH_Comment.hxx>
41 #include <SMESH_ComputeError.hxx>
42 #include <SMESH_ControlPnt.hxx>
43 #include <SMESH_File.hxx>
44 #include <SMESH_Gen_i.hxx>
45 #include <SMESH_Mesh.hxx>
46 #include <SMESH_MesherHelper.hxx>
47 #include <SMESH_subMesh.hxx>
48 #include <StdMeshers_QuadToTriaAdaptor.hxx>
49 #include <StdMeshers_ViscousLayers2D.hxx>
50
51 #include <SALOMEDS_Tool.hxx>
52
53 #include <utilities.h>
54
55 #include <BRepBuilderAPI_Copy.hxx>
56 #include <BRep_Tool.hxx>
57 #include <Bnd_B3d.hxx>
58 #include <NCollection_Map.hxx>
59 #include <Standard_ErrorHandler.hxx>
60 #include <Standard_ProgramError.hxx>
61 #include <TColStd_MapOfInteger.hxx>
62 #include <TopExp.hxx>
63 #include <TopExp_Explorer.hxx>
64 #include <TopTools_DataMapIteratorOfDataMapOfShapeInteger.hxx>
65 #include <TopTools_DataMapIteratorOfDataMapOfShapeShape.hxx>
66 #include <TopTools_DataMapOfShapeInteger.hxx>
67 #include <TopTools_DataMapOfShapeShape.hxx>
68 #include <TopTools_MapOfShape.hxx>
69 #include <TopoDS.hxx>
70
71 // Netgen include files
72 #ifndef OCCGEOMETRY
73 #define OCCGEOMETRY
74 #endif
75 #include <occgeom.hpp>
76 #include <meshing.hpp>
77 //#include <ngexception.hpp>
78 namespace netgen {
79 #ifdef NETGEN_V5
80   extern int OCCGenerateMesh (OCCGeometry&, Mesh*&, MeshingParameters&, int, int);
81 #else
82   extern int OCCGenerateMesh (OCCGeometry&, Mesh*&, int, int, char*);
83 #endif
84   //extern void OCCSetLocalMeshSize(OCCGeometry & geom, Mesh & mesh);
85   extern MeshingParameters mparam;
86   extern volatile multithreadt multithread;
87   extern bool merge_solids;
88
89   // values used for occgeo.facemeshstatus
90   enum EFaceMeshStatus { FACE_NOT_TREATED = 0,
91                          FACE_FAILED = -1,
92                          FACE_MESHED_OK = 1,
93   };
94 }
95
96 #include <vector>
97 #include <limits>
98
99 #ifdef WIN32
100 #include <process.h>
101 #endif
102 using namespace nglib;
103 using namespace std;
104
105 #ifdef _DEBUG_
106 #define nodeVec_ACCESS(index) ((SMDS_MeshNode*) nodeVec.at((index)))
107 #else
108 #define nodeVec_ACCESS(index) ((SMDS_MeshNode*) nodeVec[index])
109 #endif
110
111 #define NGPOINT_COORDS(p) p(0),p(1),p(2)
112
113 #ifdef _DEBUG_
114 // dump elements added to ng mesh
115 //#define DUMP_SEGMENTS
116 //#define DUMP_TRIANGLES
117 //#define DUMP_TRIANGLES_SCRIPT "/tmp/trias.py" //!< debug AddIntVerticesInSolids()
118 #endif
119
120 TopTools_IndexedMapOfShape ShapesWithLocalSize;
121 std::map<int,double> VertexId2LocalSize;
122 std::map<int,double> EdgeId2LocalSize;
123 std::map<int,double> FaceId2LocalSize;
124 std::map<int,double> SolidId2LocalSize;
125
126 std::vector<SMESHUtils::ControlPnt> ControlPoints;
127 std::set<int> ShapesWithControlPoints; // <-- allows calling SetLocalSize() several times w/o recomputing ControlPoints
128
129 //=============================================================================
130 /*!
131  *
132  */
133 //=============================================================================
134
135 NETGENPlugin_Mesher::NETGENPlugin_Mesher (SMESH_Mesh*         mesh,
136                                           const TopoDS_Shape& aShape,
137                                           const bool          isVolume)
138   : _mesh    (mesh),
139     _shape   (aShape),
140     _isVolume(isVolume),
141     _optimize(true),
142     _fineness(NETGENPlugin_Hypothesis::GetDefaultFineness()),
143     _isViscousLayers2D(false),
144     _ngMesh(NULL),
145     _occgeom(NULL),
146     _curShapeIndex(-1),
147     _progressTic(1),
148     _totalTime(1.0),
149     _simpleHyp(NULL),
150     _ptrToMe(NULL)
151 {
152   SetDefaultParameters();
153   ShapesWithLocalSize.Clear();
154   VertexId2LocalSize.clear();
155   EdgeId2LocalSize.clear();
156   FaceId2LocalSize.clear();
157   SolidId2LocalSize.clear();
158   ControlPoints.clear();
159   ShapesWithControlPoints.clear();
160 }
161
162 //================================================================================
163 /*!
164  * Destuctor
165  */
166 //================================================================================
167
168 NETGENPlugin_Mesher::~NETGENPlugin_Mesher()
169 {
170   if ( _ptrToMe )
171     *_ptrToMe = NULL;
172   _ptrToMe = 0;
173   _ngMesh = NULL;
174 }
175
176 //================================================================================
177 /*!
178  * Set pointer to NETGENPlugin_Mesher* field of the holder, that will be
179  * nullified at destruction of this
180  */
181 //================================================================================
182
183 void NETGENPlugin_Mesher::SetSelfPointer( NETGENPlugin_Mesher ** ptr )
184 {
185   if ( _ptrToMe )
186     *_ptrToMe = NULL;
187
188   _ptrToMe = ptr;
189
190   if ( _ptrToMe )
191     *_ptrToMe = this;
192 }
193
194 //================================================================================
195 /*!
196  * \brief Initialize global NETGEN parameters with default values
197  */
198 //================================================================================
199
200 void NETGENPlugin_Mesher::SetDefaultParameters()
201 {
202   netgen::MeshingParameters& mparams = netgen::mparam;
203   // maximal mesh edge size
204   mparams.maxh            = 0;//NETGENPlugin_Hypothesis::GetDefaultMaxSize();
205   mparams.minh            = 0;
206   // minimal number of segments per edge
207   mparams.segmentsperedge = NETGENPlugin_Hypothesis::GetDefaultNbSegPerEdge();
208   // rate of growth of size between elements
209   mparams.grading         = NETGENPlugin_Hypothesis::GetDefaultGrowthRate();
210   // safety factor for curvatures (elements per radius)
211   mparams.curvaturesafety = NETGENPlugin_Hypothesis::GetDefaultNbSegPerRadius();
212   // create elements of second order
213   mparams.secondorder     = NETGENPlugin_Hypothesis::GetDefaultSecondOrder();
214   // quad-dominated surface meshing
215   if (_isVolume)
216     mparams.quad          = 0;
217   else
218     mparams.quad          = NETGENPlugin_Hypothesis_2D::GetDefaultQuadAllowed();
219   _fineness               = NETGENPlugin_Hypothesis::GetDefaultFineness();
220   mparams.uselocalh       = NETGENPlugin_Hypothesis::GetDefaultSurfaceCurvature();
221   netgen::merge_solids    = NETGENPlugin_Hypothesis::GetDefaultFuseEdges();
222 }
223
224 //=============================================================================
225 /*!
226  *
227  */
228 //=============================================================================
229
230 void SetLocalSize(TopoDS_Shape GeomShape, double LocalSize)
231 {
232   if ( GeomShape.IsNull() ) return;
233   TopAbs_ShapeEnum GeomType = GeomShape.ShapeType();
234   if (GeomType == TopAbs_COMPOUND) {
235     for (TopoDS_Iterator it (GeomShape); it.More(); it.Next()) {
236       SetLocalSize(it.Value(), LocalSize);
237     }
238     return;
239   }
240   int key;
241   if (! ShapesWithLocalSize.Contains(GeomShape))
242     key = ShapesWithLocalSize.Add(GeomShape);
243   else
244     key = ShapesWithLocalSize.FindIndex(GeomShape);
245   if (GeomType == TopAbs_VERTEX) {
246     VertexId2LocalSize[key] = LocalSize;
247   } else if (GeomType == TopAbs_EDGE) {
248     EdgeId2LocalSize[key] = LocalSize;
249   } else if (GeomType == TopAbs_FACE) {
250     FaceId2LocalSize[key] = LocalSize;
251   } else if (GeomType == TopAbs_SOLID) {
252     SolidId2LocalSize[key] = LocalSize;
253   }
254 }
255
256 //=============================================================================
257 /*!
258  * Pass parameters to NETGEN
259  */
260 //=============================================================================
261 void NETGENPlugin_Mesher::SetParameters(const NETGENPlugin_Hypothesis* hyp)
262 {
263   if (hyp)
264   {
265     netgen::MeshingParameters& mparams = netgen::mparam;
266     // Initialize global NETGEN parameters:
267     // maximal mesh segment size
268     mparams.maxh            = hyp->GetMaxSize();
269     // maximal mesh element linear size
270     mparams.minh            = hyp->GetMinSize();
271     // minimal number of segments per edge
272     mparams.segmentsperedge = hyp->GetNbSegPerEdge();
273     // rate of growth of size between elements
274     mparams.grading         = hyp->GetGrowthRate();
275     // safety factor for curvatures (elements per radius)
276     mparams.curvaturesafety = hyp->GetNbSegPerRadius();
277     // create elements of second order
278     mparams.secondorder     = hyp->GetSecondOrder() ? 1 : 0;
279     // quad-dominated surface meshing
280     mparams.quad            = hyp->GetQuadAllowed() ? 1 : 0;
281     _optimize               = hyp->GetOptimize();
282     _fineness               = hyp->GetFineness();
283     mparams.uselocalh       = hyp->GetSurfaceCurvature();
284     netgen::merge_solids    = hyp->GetFuseEdges();
285     _simpleHyp              = NULL;
286     // mesh size file
287     mparams.meshsizefilename= hyp->GetMeshSizeFile().empty() ? 0 : hyp->GetMeshSizeFile().c_str();
288
289     SMESH_Gen_i*              smeshGen_i = SMESH_Gen_i::GetSMESHGen();
290
291     const NETGENPlugin_Hypothesis::TLocalSize   localSizes = hyp->GetLocalSizesAndEntries();
292     NETGENPlugin_Hypothesis::TLocalSize::const_iterator it = localSizes.begin();
293     for ( ; it != localSizes.end() ; it++)
294     {
295       std::string entry = (*it).first;
296       double        val = (*it).second;
297       // --
298       GEOM::GEOM_Object_var aGeomObj;
299       SALOMEDS::SObject_var aSObj = SMESH_Gen_i::getStudyServant()->FindObjectID( entry.c_str() );
300       if ( !aSObj->_is_nil() ) {
301         CORBA::Object_var obj = aSObj->GetObject();
302         aGeomObj = GEOM::GEOM_Object::_narrow(obj);
303         aSObj->UnRegister();
304       }
305       TopoDS_Shape S = smeshGen_i->GeomObjectToShape( aGeomObj.in() );
306       ::SetLocalSize(S, val);
307     }
308   }
309 }
310
311 //=============================================================================
312 /*!
313  * Pass simple parameters to NETGEN
314  */
315 //=============================================================================
316
317 void NETGENPlugin_Mesher::SetParameters(const NETGENPlugin_SimpleHypothesis_2D* hyp)
318 {
319   _simpleHyp = hyp;
320   if ( _simpleHyp )
321     SetDefaultParameters();
322 }
323
324 //=============================================================================
325 /*!
326  *  Link - a pair of integer numbers
327  */
328 //=============================================================================
329 struct Link
330 {
331   int n1, n2;
332   Link(int _n1, int _n2) : n1(_n1), n2(_n2) {}
333   Link() : n1(0), n2(0) {}
334   bool Contains( int n ) const { return n == n1 || n == n2; }
335   bool IsConnected( const Link& other ) const
336   {
337     return (( Contains( other.n1 ) || Contains( other.n2 )) && ( this != &other ));
338   }
339 };
340
341 int HashCode(const Link& aLink, int aLimit)
342 {
343   return HashCode(aLink.n1 + aLink.n2, aLimit);
344 }
345
346 Standard_Boolean IsEqual(const Link& aLink1, const Link& aLink2)
347 {
348   return (( aLink1.n1 == aLink2.n1 && aLink1.n2 == aLink2.n2 ) ||
349           ( aLink1.n1 == aLink2.n2 && aLink1.n2 == aLink2.n1 ));
350 }
351
352 namespace
353 {
354   //================================================================================
355   /*!
356    * \brief return id of netgen point corresponding to SMDS node
357    */
358   //================================================================================
359   typedef map< const SMDS_MeshNode*, int > TNode2IdMap;
360
361   int ngNodeId( const SMDS_MeshNode* node,
362                 netgen::Mesh&        ngMesh,
363                 TNode2IdMap&         nodeNgIdMap)
364   {
365     int newNgId = ngMesh.GetNP() + 1;
366
367     TNode2IdMap::iterator node_id = nodeNgIdMap.insert( make_pair( node, newNgId )).first;
368
369     if ( node_id->second == newNgId)
370     {
371 #if defined(DUMP_SEGMENTS) || defined(DUMP_TRIANGLES)
372       cout << "Ng " << newNgId << " - " << node;
373 #endif
374       netgen::MeshPoint p( netgen::Point<3> (node->X(), node->Y(), node->Z()) );
375       ngMesh.AddPoint( p );
376     }
377     return node_id->second;
378   }
379
380   //================================================================================
381   /*!
382    * \brief Return computed EDGEs connected to the given one
383    */
384   //================================================================================
385
386   list< TopoDS_Edge > getConnectedEdges( const TopoDS_Edge&                 edge,
387                                          const TopoDS_Face&                 face,
388                                          const set< SMESH_subMesh* > &      computedSM,
389                                          const SMESH_MesherHelper&          helper,
390                                          map< SMESH_subMesh*, set< int > >& addedEdgeSM2Faces)
391   {
392     // get ordered EDGEs
393     list< TopoDS_Edge > edges;
394     list< int > nbEdgesInWire;
395     /*int nbWires =*/ SMESH_Block::GetOrderedEdges( face, edges, nbEdgesInWire);
396
397     // find <edge> within <edges>
398     list< TopoDS_Edge >::iterator eItFwd = edges.begin();
399     for ( ; eItFwd != edges.end(); ++eItFwd )
400       if ( edge.IsSame( *eItFwd ))
401         break;
402     if ( eItFwd == edges.end()) return list< TopoDS_Edge>();
403
404     if ( eItFwd->Orientation() >= TopAbs_INTERNAL )
405     {
406       // connected INTERNAL edges returned from GetOrderedEdges() are wrongly oriented
407       // so treat each INTERNAL edge separately
408       TopoDS_Edge e = *eItFwd;
409       edges.clear();
410       edges.push_back( e );
411       return edges;
412     }
413
414     // get all computed EDGEs connected to <edge>
415
416     list< TopoDS_Edge >::iterator eItBack = eItFwd, ePrev;
417     TopoDS_Vertex vCommon;
418     TopTools_MapOfShape eAdded; // map used not to add a seam edge twice to <edges>
419     eAdded.Add( edge );
420
421     // put edges before <edge> to <edges> back
422     while ( edges.begin() != eItFwd )
423       edges.splice( edges.end(), edges, edges.begin() );
424
425     // search forward
426     ePrev = eItFwd;
427     while ( ++eItFwd != edges.end() )
428     {
429       SMESH_subMesh* sm = helper.GetMesh()->GetSubMesh( *eItFwd );
430
431       bool connected = TopExp::CommonVertex( *ePrev, *eItFwd, vCommon );
432       bool computed  = sm->IsMeshComputed();
433       bool added     = addedEdgeSM2Faces[ sm ].count( helper.GetSubShapeID() );
434       bool doubled   = !eAdded.Add( *eItFwd );
435       bool orientOK  = (( ePrev ->Orientation() < TopAbs_INTERNAL ) ==
436                         ( eItFwd->Orientation() < TopAbs_INTERNAL )    );
437       if ( !connected || !computed || !orientOK || added || doubled )
438       {
439         // stop advancement; move edges from tail to head
440         while ( edges.back() != *ePrev )
441           edges.splice( edges.begin(), edges, --edges.end() );
442         break;
443       }
444       ePrev = eItFwd;
445     }
446     // search backward
447     while ( eItBack != edges.begin() )
448     {
449       ePrev = eItBack;
450       --eItBack;
451       SMESH_subMesh* sm = helper.GetMesh()->GetSubMesh( *eItBack );
452
453       bool connected = TopExp::CommonVertex( *ePrev, *eItBack, vCommon );
454       bool computed  = sm->IsMeshComputed();
455       bool added     = addedEdgeSM2Faces[ sm ].count( helper.GetSubShapeID() );
456       bool doubled   = !eAdded.Add( *eItBack );
457       bool orientOK  = (( ePrev  ->Orientation() < TopAbs_INTERNAL ) ==
458                         ( eItBack->Orientation() < TopAbs_INTERNAL )    );
459       if ( !connected || !computed || !orientOK || added || doubled)
460       {
461         // stop advancement
462         edges.erase( edges.begin(), ePrev );
463         break;
464       }
465     }
466     if ( edges.front() != edges.back() )
467     {
468       // assure that the 1st vertex is meshed
469       TopoDS_Edge eLast = edges.back();
470       while ( !SMESH_Algo::VertexNode( SMESH_MesherHelper::IthVertex( 0, edges.front()), helper.GetMeshDS())
471               &&
472               edges.front() != eLast )
473         edges.splice( edges.end(), edges, edges.begin() );
474     }
475     return edges;
476   }
477
478   //================================================================================
479   /*!
480    * \brief Make triangulation of a shape precise enough
481    */
482   //================================================================================
483
484   void updateTriangulation( const TopoDS_Shape& shape )
485   {
486     // static set< Poly_Triangulation* > updated;
487
488     // TopLoc_Location loc;
489     // TopExp_Explorer fExp( shape, TopAbs_FACE );
490     // for ( ; fExp.More(); fExp.Next() )
491     // {
492     //   Handle(Poly_Triangulation) triangulation =
493     //     BRep_Tool::Triangulation ( TopoDS::Face( fExp.Current() ), loc);
494     //   if ( triangulation.IsNull() ||
495     //        updated.insert( triangulation.operator->() ).second )
496     //   {
497     //     BRepTools::Clean (shape);
498         try {
499           OCC_CATCH_SIGNALS;
500           BRepMesh_IncrementalMesh e(shape, 0.01, true);
501         }
502         catch (Standard_Failure)
503         {
504         }
505   //       updated.erase( triangulation.operator->() );
506   //       triangulation = BRep_Tool::Triangulation ( TopoDS::Face( fExp.Current() ), loc);
507   //       updated.insert( triangulation.operator->() );
508   //     }
509   //   }
510   }
511   //================================================================================
512   /*!
513    * \brief Returns a medium node either existing in SMESH of created by NETGEN
514    *  \param [in] corner1 - corner node 1
515    *  \param [in] corner2 - corner node 2
516    *  \param [in] defaultMedium - the node created by NETGEN
517    *  \param [in] helper - holder of medium nodes existing in SMESH
518    *  \return const SMDS_MeshNode* - the result node
519    */
520   //================================================================================
521
522   const SMDS_MeshNode* mediumNode( const SMDS_MeshNode*      corner1,
523                                    const SMDS_MeshNode*      corner2,
524                                    const SMDS_MeshNode*      defaultMedium,
525                                    const SMESH_MesherHelper* helper)
526   {
527     if ( helper )
528     {
529       TLinkNodeMap::const_iterator l2n =
530         helper->GetTLinkNodeMap().find( SMESH_TLink( corner1, corner2 ));
531       if ( l2n != helper->GetTLinkNodeMap().end() )
532         defaultMedium = l2n->second;
533     }
534     return defaultMedium;
535   }
536
537   //================================================================================
538   /*!
539    * \brief Assure that mesh on given shapes is quadratic
540    */
541   //================================================================================
542
543   // void makeQuadratic( const TopTools_IndexedMapOfShape& shapes,
544   //                     SMESH_Mesh*                       mesh )
545   // {
546   //   for ( int i = 1; i <= shapes.Extent(); ++i )
547   //   {
548   //     SMESHDS_SubMesh* smDS = mesh->GetMeshDS()->MeshElements( shapes(i) );
549   //     if ( !smDS ) continue;
550   //     SMDS_ElemIteratorPtr elemIt = smDS->GetElements();
551   //     if ( !elemIt->more() ) continue;
552   //     const SMDS_MeshElement* e = elemIt->next();
553   //     if ( !e || e->IsQuadratic() )
554   //       continue;
555
556   //     TIDSortedElemSet elems;
557   //     elems.insert( e );
558   //     while ( elemIt->more() )
559   //       elems.insert( elems.end(), elemIt->next() );
560
561   //     SMESH_MeshEditor( mesh ).ConvertToQuadratic( /*3d=*/false, elems, /*biQuad=*/false );
562   //   }
563   // }
564
565   //================================================================================
566   /*!
567    * \brief Restrict size of elements on the given edge 
568    */
569   //================================================================================
570
571   void setLocalSize(const TopoDS_Edge& edge,
572                     double             size,
573                     netgen::Mesh&      mesh)
574   {
575     if ( size <= std::numeric_limits<double>::min() )
576       return;
577     Standard_Real u1, u2;
578     Handle(Geom_Curve) curve = BRep_Tool::Curve(edge, u1, u2);
579     if ( curve.IsNull() )
580     {
581       TopoDS_Iterator vIt( edge );
582       if ( !vIt.More() ) return;
583       gp_Pnt p = BRep_Tool::Pnt( TopoDS::Vertex( vIt.Value() ));
584       NETGENPlugin_Mesher::RestrictLocalSize( mesh, p.XYZ(), size );
585     }
586     else
587     {
588       const int nb = (int)( 1.5 * SMESH_Algo::EdgeLength( edge ) / size );
589       Standard_Real delta = (u2-u1)/nb;
590       for(int i=0; i<nb; i++)
591       {
592         Standard_Real u = u1 + delta*i;
593         gp_Pnt p = curve->Value(u);
594         NETGENPlugin_Mesher::RestrictLocalSize( mesh, p.XYZ(), size );
595         netgen::Point3d pi(p.X(), p.Y(), p.Z());
596         double resultSize = mesh.GetH(pi);
597         if ( resultSize - size > 0.1*size )
598           // netgen does restriction iff oldH/newH > 1.2 (localh.cpp:136)
599           NETGENPlugin_Mesher::RestrictLocalSize( mesh, p.XYZ(), resultSize/1.201 );
600       }
601     }
602   }
603 } // namespace
604
605 //================================================================================
606 /*!
607  * \brief Set local size on shapes defined by SetParameters()
608  */
609 //================================================================================
610
611 void NETGENPlugin_Mesher::SetLocalSize( netgen::OCCGeometry& occgeo,
612                                         netgen::Mesh&        ngMesh )
613 {
614   for(std::map<int,double>::const_iterator it=EdgeId2LocalSize.begin(); it!=EdgeId2LocalSize.end(); it++)
615   {
616     int   key = (*it).first;
617     double hi = (*it).second;
618     const TopoDS_Shape& shape = ShapesWithLocalSize.FindKey(key);
619     setLocalSize( TopoDS::Edge(shape), hi, ngMesh );
620   }
621   for(std::map<int,double>::const_iterator it=VertexId2LocalSize.begin(); it!=VertexId2LocalSize.end(); it++)
622   {
623     int   key = (*it).first;
624     double hi = (*it).second;
625     const TopoDS_Shape& shape = ShapesWithLocalSize.FindKey(key);
626     gp_Pnt p = BRep_Tool::Pnt( TopoDS::Vertex(shape) );
627     NETGENPlugin_Mesher::RestrictLocalSize( ngMesh, p.XYZ(), hi );
628   }
629   for(map<int,double>::const_iterator it=FaceId2LocalSize.begin(); it!=FaceId2LocalSize.end(); it++)
630   {
631     int    key = (*it).first;
632     double val = (*it).second;
633     const TopoDS_Shape& shape = ShapesWithLocalSize.FindKey(key);
634     int faceNgID = occgeo.fmap.FindIndex(shape);
635     if ( faceNgID >= 1 )
636     {
637       occgeo.SetFaceMaxH(faceNgID, val);
638       for ( TopExp_Explorer edgeExp( shape, TopAbs_EDGE ); edgeExp.More(); edgeExp.Next() )
639         setLocalSize( TopoDS::Edge( edgeExp.Current() ), val, ngMesh );
640     }
641     else if ( !ShapesWithControlPoints.count( key ))
642     {
643       SMESHUtils::createPointsSampleFromFace( TopoDS::Face( shape ), val, ControlPoints );
644       ShapesWithControlPoints.insert( key );
645     }
646   }
647   for(map<int,double>::const_iterator it=SolidId2LocalSize.begin(); it!=SolidId2LocalSize.end(); it++)
648   {
649     int    key = (*it).first;
650     double val = (*it).second;
651     if ( !ShapesWithControlPoints.count( key ))
652     {
653       const TopoDS_Shape& shape = ShapesWithLocalSize.FindKey(key);
654       SMESHUtils::createPointsSampleFromSolid( TopoDS::Solid( shape ), val, ControlPoints );
655       ShapesWithControlPoints.insert( key );
656     }
657   }
658
659   if ( !ControlPoints.empty() )
660   {
661     for ( size_t i = 0; i < ControlPoints.size(); ++i )
662       NETGENPlugin_Mesher::RestrictLocalSize( ngMesh, ControlPoints[i].XYZ(), ControlPoints[i].Size() );
663   }
664 }
665
666 //================================================================================
667 /*!
668  * \brief Initialize netgen::OCCGeometry with OCCT shape
669  */
670 //================================================================================
671
672 void NETGENPlugin_Mesher::PrepareOCCgeometry(netgen::OCCGeometry&     occgeo,
673                                              const TopoDS_Shape&      shape,
674                                              SMESH_Mesh&              mesh,
675                                              list< SMESH_subMesh* > * meshedSM,
676                                              NETGENPlugin_Internals*  intern)
677 {
678   updateTriangulation( shape );
679
680   Bnd_Box bb;
681   BRepBndLib::Add (shape, bb);
682   double x1,y1,z1,x2,y2,z2;
683   bb.Get (x1,y1,z1,x2,y2,z2);
684   netgen::Point<3> p1 = netgen::Point<3> (x1,y1,z1);
685   netgen::Point<3> p2 = netgen::Point<3> (x2,y2,z2);
686   occgeo.boundingbox = netgen::Box<3> (p1,p2);
687
688   occgeo.shape = shape;
689   occgeo.changed = 1;
690
691   // fill maps of shapes of occgeo with not yet meshed subshapes
692
693   // get root submeshes
694   list< SMESH_subMesh* > rootSM;
695   const int shapeID = mesh.GetMeshDS()->ShapeToIndex( shape );
696   if ( shapeID > 0 ) { // SMESH_subMesh with ID 0 may exist, don't use it!
697     rootSM.push_back( mesh.GetSubMesh( shape ));
698   }
699   else {
700     for ( TopoDS_Iterator it( shape ); it.More(); it.Next() )
701       rootSM.push_back( mesh.GetSubMesh( it.Value() ));
702   }
703
704   int totNbFaces = 0;
705
706   // add subshapes of empty submeshes
707   list< SMESH_subMesh* >::iterator rootIt = rootSM.begin(), rootEnd = rootSM.end();
708   for ( ; rootIt != rootEnd; ++rootIt ) {
709     SMESH_subMesh * root = *rootIt;
710     SMESH_subMeshIteratorPtr smIt = root->getDependsOnIterator(/*includeSelf=*/true,
711                                                                /*complexShapeFirst=*/true);
712     // to find a right orientation of subshapes (PAL20462)
713     TopTools_IndexedMapOfShape subShapes;
714     TopExp::MapShapes(root->GetSubShape(), subShapes);
715     while ( smIt->more() )
716     {
717       SMESH_subMesh*  sm = smIt->next();
718       TopoDS_Shape shape = sm->GetSubShape();
719       totNbFaces += ( shape.ShapeType() == TopAbs_FACE );
720       if ( intern && intern->isShapeToPrecompute( shape ))
721         continue;
722       if ( !meshedSM || sm->IsEmpty() )
723       {
724         if ( shape.ShapeType() != TopAbs_VERTEX )
725           shape = subShapes( subShapes.FindIndex( shape ));// shape -> index -> oriented shape
726         if ( shape.Orientation() >= TopAbs_INTERNAL )
727           shape.Orientation( TopAbs_FORWARD ); // isuue 0020676
728         switch ( shape.ShapeType() ) {
729         case TopAbs_FACE  : occgeo.fmap.Add( shape ); break;
730         case TopAbs_EDGE  : occgeo.emap.Add( shape ); break;
731         case TopAbs_VERTEX: occgeo.vmap.Add( shape ); break;
732         case TopAbs_SOLID :occgeo.somap.Add( shape ); break;
733         default:;
734         }
735       }
736       // collect submeshes of meshed shapes
737       else if (meshedSM)
738       {
739         const int dim = SMESH_Gen::GetShapeDim( shape );
740         meshedSM[ dim ].push_back( sm );
741       }
742     }
743   }
744   occgeo.facemeshstatus.SetSize (totNbFaces);
745   occgeo.facemeshstatus = 0;
746   occgeo.face_maxh_modified.SetSize(totNbFaces);
747   occgeo.face_maxh_modified = 0;
748   occgeo.face_maxh.SetSize(totNbFaces);
749   occgeo.face_maxh = netgen::mparam.maxh;
750 }
751
752 //================================================================================
753 /*!
754  * \brief Return a default min size value suitable for the given geometry.
755  */
756 //================================================================================
757
758 double NETGENPlugin_Mesher::GetDefaultMinSize(const TopoDS_Shape& geom,
759                                               const double        maxSize)
760 {
761   updateTriangulation( geom );
762
763   TopLoc_Location loc;
764   int i1, i2, i3;
765   const int* pi[4] = { &i1, &i2, &i3, &i1 };
766   double minh = 1e100;
767   Bnd_B3d bb;
768   TopExp_Explorer fExp( geom, TopAbs_FACE );
769   for ( ; fExp.More(); fExp.Next() )
770   {
771     Handle(Poly_Triangulation) triangulation =
772       BRep_Tool::Triangulation ( TopoDS::Face( fExp.Current() ), loc);
773     if ( triangulation.IsNull() ) continue;
774     const double fTol = BRep_Tool::Tolerance( TopoDS::Face( fExp.Current() ));
775     const TColgp_Array1OfPnt&   points = triangulation->Nodes();
776     const Poly_Array1OfTriangle& trias = triangulation->Triangles();
777     for ( int iT = trias.Lower(); iT <= trias.Upper(); ++iT )
778     {
779       trias(iT).Get( i1, i2, i3 );
780       for ( int j = 0; j < 3; ++j )
781       {
782         double dist2 = points(*pi[j]).SquareDistance( points( *pi[j+1] ));
783         if ( dist2 < minh && fTol*fTol < dist2 )
784           minh = dist2;
785         bb.Add( points(*pi[j]));
786       }
787     }
788   }
789   if ( minh > 0.25 * bb.SquareExtent() ) // simple geometry, rough triangulation
790   {
791     minh = 1e-3 * sqrt( bb.SquareExtent());
792     //cout << "BND BOX minh = " <<minh << endl;
793   }
794   else
795   {
796     minh = 3 * sqrt( minh ); // triangulation for visualization is rather fine
797     //cout << "TRIANGULATION minh = " <<minh << endl;
798   }
799   if ( minh > 0.5 * maxSize )
800     minh = maxSize / 3.;
801
802   return minh;
803 }
804
805 //================================================================================
806 /*!
807  * \brief Restrict size of elements at a given point
808  */
809 //================================================================================
810
811 void NETGENPlugin_Mesher::RestrictLocalSize(netgen::Mesh& ngMesh,
812                                             const gp_XYZ& p,
813                                             double        size,
814                                             const bool    overrideMinH)
815 {
816   if ( size <= std::numeric_limits<double>::min() )
817     return;
818   if ( netgen::mparam.minh > size )
819   {
820     if ( overrideMinH )
821     {
822       ngMesh.SetMinimalH( size );
823       netgen::mparam.minh = size;
824     }
825     else
826     {
827       size = netgen::mparam.minh;
828     }
829   }
830   netgen::Point3d pi(p.X(), p.Y(), p.Z());
831   ngMesh.RestrictLocalH( pi, size );
832 }
833
834 //================================================================================
835 /*!
836  * \brief fill ngMesh with nodes and elements of computed submeshes
837  */
838 //================================================================================
839
840 bool NETGENPlugin_Mesher::FillNgMesh(netgen::OCCGeometry&           occgeom,
841                                      netgen::Mesh&                  ngMesh,
842                                      vector<const SMDS_MeshNode*>&  nodeVec,
843                                      const list< SMESH_subMesh* > & meshedSM,
844                                      SMESH_MesherHelper*            quadHelper,
845                                      SMESH_ProxyMesh::Ptr           proxyMesh)
846 {
847   TNode2IdMap nodeNgIdMap;
848   for ( size_t i = 1; i < nodeVec.size(); ++i )
849     nodeNgIdMap.insert( make_pair( nodeVec[i], i ));
850
851   TopTools_MapOfShape visitedShapes;
852   map< SMESH_subMesh*, set< int > > visitedEdgeSM2Faces;
853   set< SMESH_subMesh* > computedSM( meshedSM.begin(), meshedSM.end() );
854
855   SMESH_MesherHelper helper (*_mesh);
856
857   int faceNgID = ngMesh.GetNFD();
858
859   list< SMESH_subMesh* >::const_iterator smIt, smEnd = meshedSM.end();
860   for ( smIt = meshedSM.begin(); smIt != smEnd; ++smIt )
861   {
862     SMESH_subMesh* sm = *smIt;
863     if ( !visitedShapes.Add( sm->GetSubShape() ))
864       continue;
865
866     const SMESHDS_SubMesh * smDS = sm->GetSubMeshDS();
867     if ( !smDS ) continue;
868
869     switch ( sm->GetSubShape().ShapeType() )
870     {
871     case TopAbs_EDGE: { // EDGE
872       // ----------------------
873       TopoDS_Edge geomEdge  = TopoDS::Edge( sm->GetSubShape() );
874       if ( geomEdge.Orientation() >= TopAbs_INTERNAL )
875         geomEdge.Orientation( TopAbs_FORWARD ); // issue 0020676
876
877       // Add ng segments for each not meshed FACE the EDGE bounds
878       PShapeIteratorPtr fIt = helper.GetAncestors( geomEdge, *sm->GetFather(), TopAbs_FACE );
879       while ( const TopoDS_Shape * anc = fIt->next() )
880       {
881         faceNgID = occgeom.fmap.FindIndex( *anc );
882         if ( faceNgID < 1 )
883           continue; // meshed face
884
885         int faceSMDSId = helper.GetMeshDS()->ShapeToIndex( *anc );
886         if ( visitedEdgeSM2Faces[ sm ].count( faceSMDSId ))
887           continue; // already treated EDGE
888
889         TopoDS_Face face = TopoDS::Face( occgeom.fmap( faceNgID ));
890         if ( face.Orientation() >= TopAbs_INTERNAL )
891           face.Orientation( TopAbs_FORWARD ); // issue 0020676
892
893         // get all meshed EDGEs of the FACE connected to geomEdge (issue 0021140)
894         helper.SetSubShape( face );
895         list< TopoDS_Edge > edges = getConnectedEdges( geomEdge, face, computedSM, helper,
896                                                        visitedEdgeSM2Faces );
897         if ( edges.empty() )
898           continue; // wrong ancestor?
899
900         // find out orientation of <edges> within <face>
901         TopoDS_Edge eNotSeam = edges.front();
902         if ( helper.HasSeam() )
903         {
904           list< TopoDS_Edge >::iterator eIt = edges.begin();
905           while ( helper.IsRealSeam( *eIt )) ++eIt;
906           if ( eIt != edges.end() )
907             eNotSeam = *eIt;
908         }
909         TopAbs_Orientation fOri = helper.GetSubShapeOri( face, eNotSeam );
910         bool isForwad = ( fOri == eNotSeam.Orientation() || fOri >= TopAbs_INTERNAL );
911
912         // get all nodes from connected <edges>
913         const bool isQuad = smDS->IsQuadratic();
914         StdMeshers_FaceSide fSide( face, edges, _mesh, isForwad, isQuad );
915         const vector<UVPtStruct>& points = fSide.GetUVPtStruct();
916         if ( points.empty() )
917           return false; // invalid node params?
918         int i, nbSeg = fSide.NbSegments();
919
920         // remember EDGEs of fSide to treat only once
921         for ( int iE = 0; iE < fSide.NbEdges(); ++iE )
922           visitedEdgeSM2Faces[ helper.GetMesh()->GetSubMesh( fSide.Edge(iE )) ].insert(faceSMDSId);
923
924         double otherSeamParam = 0;
925         bool isSeam = false;
926
927         // add segments
928
929         int prevNgId = ngNodeId( points[0].node, ngMesh, nodeNgIdMap );
930
931         for ( i = 0; i < nbSeg; ++i )
932         {
933           const UVPtStruct& p1 = points[ i ];
934           const UVPtStruct& p2 = points[ i+1 ];
935
936           if ( p1.node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX ) //an EDGE begins
937           {
938             isSeam = false;
939             if ( helper.IsRealSeam( p1.node->getshapeId() ))
940             {
941               TopoDS_Edge e = fSide.Edge( fSide.EdgeIndex( 0.5 * ( p1.normParam + p2.normParam )));
942               isSeam = helper.IsRealSeam( e );
943               if ( isSeam )
944               {
945                 otherSeamParam = helper.GetOtherParam( helper.GetPeriodicIndex() & 1 ? p2.u : p2.v );
946               }
947             }
948           }
949           netgen::Segment seg;
950           // ng node ids
951           seg[0] = prevNgId;
952           seg[1] = prevNgId = ngNodeId( p2.node, ngMesh, nodeNgIdMap );
953           // node param on curve
954           seg.epgeominfo[ 0 ].dist = p1.param;
955           seg.epgeominfo[ 1 ].dist = p2.param;
956           // uv on face
957           seg.epgeominfo[ 0 ].u = p1.u;
958           seg.epgeominfo[ 0 ].v = p1.v;
959           seg.epgeominfo[ 1 ].u = p2.u;
960           seg.epgeominfo[ 1 ].v = p2.v;
961
962           //geomEdge = fSide.Edge( fSide.EdgeIndex( 0.5 * ( p1.normParam + p2.normParam )));
963           //seg.epgeominfo[ 0 ].edgenr = seg.epgeominfo[ 1 ].edgenr = occgeom.emap.FindIndex( geomEdge );
964
965           //seg.epgeominfo[ iEnd ].edgenr = edgeID; //  = geom.emap.FindIndex(edge);
966           seg.si = faceNgID;                   // = geom.fmap.FindIndex (face);
967           seg.edgenr = ngMesh.GetNSeg() + 1; // segment id
968           ngMesh.AddSegment (seg);
969
970           SMESH_TNodeXYZ np1( p1.node ), np2( p2.node );
971           RestrictLocalSize( ngMesh, 0.5*(np1+np2), (np1-np2).Modulus() );
972
973 #ifdef DUMP_SEGMENTS
974           cout << "Segment: " << seg.edgenr << " on SMESH face " << helper.GetMeshDS()->ShapeToIndex( face ) << endl
975                << "\tface index: " << seg.si << endl
976                << "\tp1: " << seg[0] << endl
977                << "\tp2: " << seg[1] << endl
978                << "\tp0 param: " << seg.epgeominfo[ 0 ].dist << endl
979                << "\tp0 uv: " << seg.epgeominfo[ 0 ].u <<", "<< seg.epgeominfo[ 0 ].v << endl
980             //<< "\tp0 edge: " << seg.epgeominfo[ 0 ].edgenr << endl
981                << "\tp1 param: " << seg.epgeominfo[ 1 ].dist << endl
982                << "\tp1 uv: " << seg.epgeominfo[ 1 ].u <<", "<< seg.epgeominfo[ 1 ].v << endl;
983             //<< "\tp1 edge: " << seg.epgeominfo[ 1 ].edgenr << endl;
984 #endif
985           if ( isSeam )
986           {
987             if ( helper.GetPeriodicIndex() && 1 ) {
988               seg.epgeominfo[ 0 ].u = otherSeamParam;
989               seg.epgeominfo[ 1 ].u = otherSeamParam;
990               swap (seg.epgeominfo[0].v, seg.epgeominfo[1].v);
991             } else {
992               seg.epgeominfo[ 0 ].v = otherSeamParam;
993               seg.epgeominfo[ 1 ].v = otherSeamParam;
994               swap (seg.epgeominfo[0].u, seg.epgeominfo[1].u);
995             }
996             swap( seg[0], seg[1] );
997             swap( seg.epgeominfo[0].dist, seg.epgeominfo[1].dist );
998             seg.edgenr = ngMesh.GetNSeg() + 1; // segment id
999             ngMesh.AddSegment( seg );
1000 #ifdef DUMP_SEGMENTS
1001             cout << "Segment: " << seg.edgenr << endl
1002                  << "\t is SEAM (reverse) of the previous. "
1003                  << " Other " << (helper.GetPeriodicIndex() && 1 ? "U" : "V")
1004                  << " = " << otherSeamParam << endl;
1005 #endif
1006           }
1007           else if ( fOri == TopAbs_INTERNAL )
1008           {
1009             swap( seg[0], seg[1] );
1010             swap( seg.epgeominfo[0], seg.epgeominfo[1] );
1011             seg.edgenr = ngMesh.GetNSeg() + 1; // segment id
1012             ngMesh.AddSegment( seg );
1013 #ifdef DUMP_SEGMENTS
1014             cout << "Segment: " << seg.edgenr << endl << "\t is REVERSE of the previous" << endl;
1015 #endif
1016           }
1017         }
1018       } // loop on geomEdge ancestors
1019
1020       if ( quadHelper ) // remember medium nodes of sub-meshes
1021       {
1022         SMDS_ElemIteratorPtr edges = smDS->GetElements();
1023         while ( edges->more() )
1024         {
1025           const SMDS_MeshElement* e = edges->next();
1026           if ( !quadHelper->AddTLinks( static_cast< const SMDS_MeshEdge*>( e )))
1027             break;
1028         }
1029       }
1030
1031       break;
1032     } // case TopAbs_EDGE
1033
1034     case TopAbs_FACE: { // FACE
1035       // ----------------------
1036       const TopoDS_Face& geomFace  = TopoDS::Face( sm->GetSubShape() );
1037       helper.SetSubShape( geomFace );
1038       bool isInternalFace = ( geomFace.Orientation() == TopAbs_INTERNAL );
1039
1040       // Find solids the geomFace bounds
1041       int solidID1 = 0, solidID2 = 0;
1042       StdMeshers_QuadToTriaAdaptor* quadAdaptor =
1043         dynamic_cast<StdMeshers_QuadToTriaAdaptor*>( proxyMesh.get() );
1044       if ( quadAdaptor )
1045       {
1046         solidID1 = occgeom.somap.FindIndex( quadAdaptor->GetShape() );
1047       }
1048       else
1049       {
1050         PShapeIteratorPtr solidIt = helper.GetAncestors( geomFace, *sm->GetFather(), TopAbs_SOLID);
1051         while ( const TopoDS_Shape * solid = solidIt->next() )
1052         {
1053           int id = occgeom.somap.FindIndex ( *solid );
1054           if ( solidID1 && id != solidID1 ) solidID2 = id;
1055           else                              solidID1 = id;
1056         }
1057       }
1058       // Add ng face descriptors of meshed faces
1059       faceNgID++;
1060       ngMesh.AddFaceDescriptor( netgen::FaceDescriptor( faceNgID, solidID1, solidID2, 0 ));
1061
1062       // if second oreder is required, even already meshed faces must be passed to NETGEN
1063       int fID = occgeom.fmap.Add( geomFace );
1064       if ( occgeom.facemeshstatus.Size() < fID ) occgeom.facemeshstatus.SetSize( fID );
1065       occgeom.facemeshstatus[ fID-1 ] = netgen::FACE_MESHED_OK;
1066       while ( fID < faceNgID ) // geomFace is already in occgeom.fmap, add a copy
1067       {
1068         fID = occgeom.fmap.Add( BRepBuilderAPI_Copy( geomFace, /*copyGeom=*/false ));
1069         if ( occgeom.facemeshstatus.Size() < fID ) occgeom.facemeshstatus.SetSize( fID );
1070         occgeom.facemeshstatus[ fID-1 ] = netgen::FACE_MESHED_OK;
1071       }
1072       // Problem with the second order in a quadrangular mesh remains.
1073       // 1) All quadrangles generated by NETGEN are moved to an inexistent face
1074       //    by FillSMesh() (find "AddFaceDescriptor")
1075       // 2) Temporary triangles generated by StdMeshers_QuadToTriaAdaptor
1076       //    are on faces where quadrangles were.
1077       // Due to these 2 points, wrong geom faces are used while conversion to quadratic
1078       // of the mentioned above quadrangles and triangles
1079
1080       // Orient the face correctly in solidID1 (issue 0020206)
1081       bool reverse = false;
1082       if ( solidID1 ) {
1083         TopoDS_Shape solid = occgeom.somap( solidID1 );
1084         TopAbs_Orientation faceOriInSolid = helper.GetSubShapeOri( solid, geomFace );
1085         if ( faceOriInSolid >= 0 )
1086           reverse =
1087             helper.IsReversedSubMesh( TopoDS::Face( geomFace.Oriented( faceOriInSolid )));
1088       }
1089
1090       // Add surface elements
1091
1092       netgen::Element2d tri(3);
1093       tri.SetIndex( faceNgID );
1094       SMESH_TNodeXYZ xyz[3];
1095
1096 #ifdef DUMP_TRIANGLES
1097       cout << "SMESH face " << helper.GetMeshDS()->ShapeToIndex( geomFace )
1098            << " internal="<<isInternalFace << endl;
1099 #endif
1100       if ( proxyMesh )
1101         smDS = proxyMesh->GetSubMesh( geomFace );
1102
1103       SMDS_ElemIteratorPtr faces = smDS->GetElements();
1104       while ( faces->more() )
1105       {
1106         const SMDS_MeshElement* f = faces->next();
1107         if ( f->NbNodes() % 3 != 0 ) // not triangle
1108         {
1109           PShapeIteratorPtr solidIt=helper.GetAncestors(geomFace,*sm->GetFather(),TopAbs_SOLID);
1110           if ( const TopoDS_Shape * solid = solidIt->next() )
1111             sm = _mesh->GetSubMesh( *solid );
1112           SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1113           smError.reset( new SMESH_ComputeError(COMPERR_BAD_INPUT_MESH,"Not triangle sub-mesh"));
1114           smError->myBadElements.push_back( f );
1115           return false;
1116         }
1117
1118         for ( int i = 0; i < 3; ++i )
1119         {
1120           const SMDS_MeshNode* node = f->GetNode( i ), * inFaceNode=0;
1121           xyz[i].Set( node );
1122
1123           // get node UV on face
1124           int shapeID = node->getshapeId();
1125           if ( helper.IsSeamShape( shapeID ))
1126           {
1127             if ( helper.IsSeamShape( f->GetNodeWrap( i+1 )->getshapeId() ))
1128               inFaceNode = f->GetNodeWrap( i-1 );
1129             else
1130               inFaceNode = f->GetNodeWrap( i+1 );
1131           }
1132           gp_XY uv = helper.GetNodeUV( geomFace, node, inFaceNode );
1133
1134           int ind = reverse ? 3-i : i+1;
1135           tri.GeomInfoPi(ind).u = uv.X();
1136           tri.GeomInfoPi(ind).v = uv.Y();
1137           tri.PNum      (ind) = ngNodeId( node, ngMesh, nodeNgIdMap );
1138         }
1139
1140         // pass a triangle size to NG size-map
1141         double size = ( ( xyz[0] - xyz[1] ).Modulus() +
1142                         ( xyz[1] - xyz[2] ).Modulus() +
1143                         ( xyz[2] - xyz[0] ).Modulus() ) / 3;
1144         gp_XYZ gc = ( xyz[0] + xyz[1] + xyz[2] ) / 3;
1145         RestrictLocalSize( ngMesh, gc, size, /*overrideMinH=*/false );
1146
1147         ngMesh.AddSurfaceElement (tri);
1148 #ifdef DUMP_TRIANGLES
1149         cout << tri << endl;
1150 #endif
1151
1152         if ( isInternalFace )
1153         {
1154           swap( tri[1], tri[2] );
1155           ngMesh.AddSurfaceElement (tri);
1156 #ifdef DUMP_TRIANGLES
1157           cout << tri << endl;
1158 #endif
1159         }
1160       }
1161
1162       if ( quadHelper ) // remember medium nodes of sub-meshes
1163       {
1164         SMDS_ElemIteratorPtr faces = smDS->GetElements();
1165         while ( faces->more() )
1166         {
1167           const SMDS_MeshElement* f = faces->next();
1168           if ( !quadHelper->AddTLinks( static_cast< const SMDS_MeshFace*>( f )))
1169             break;
1170         }
1171       }
1172
1173       break;
1174     } // case TopAbs_FACE
1175
1176     case TopAbs_VERTEX: { // VERTEX
1177       // --------------------------
1178       // issue 0021405. Add node only if a VERTEX is shared by a not meshed EDGE,
1179       // else netgen removes a free node and nodeVector becomes invalid
1180       PShapeIteratorPtr ansIt = helper.GetAncestors( sm->GetSubShape(),
1181                                                      *sm->GetFather(),
1182                                                      TopAbs_EDGE );
1183       bool toAdd = false;
1184       while ( const TopoDS_Shape* e = ansIt->next() )
1185       {
1186         SMESH_subMesh* eSub = helper.GetMesh()->GetSubMesh( *e );
1187         if (( toAdd = ( eSub->IsEmpty() && !SMESH_Algo::isDegenerated( TopoDS::Edge( *e )))))
1188           break;
1189       }
1190       if ( toAdd )
1191       {
1192         SMDS_NodeIteratorPtr nodeIt = smDS->GetNodes();
1193         if ( nodeIt->more() )
1194           ngNodeId( nodeIt->next(), ngMesh, nodeNgIdMap );
1195       }
1196       break;
1197     }
1198     default:;
1199     } // switch
1200   } // loop on submeshes
1201
1202   // fill nodeVec
1203   nodeVec.resize( ngMesh.GetNP() + 1 );
1204   TNode2IdMap::iterator node_NgId, nodeNgIdEnd = nodeNgIdMap.end();
1205   for ( node_NgId = nodeNgIdMap.begin(); node_NgId != nodeNgIdEnd; ++node_NgId)
1206     nodeVec[ node_NgId->second ] = node_NgId->first;
1207
1208   return true;
1209 }
1210
1211 //================================================================================
1212 /*!
1213  * \brief Duplicate mesh faces on internal geom faces
1214  */
1215 //================================================================================
1216
1217 void NETGENPlugin_Mesher::FixIntFaces(const netgen::OCCGeometry& occgeom,
1218                                       netgen::Mesh&              ngMesh,
1219                                       NETGENPlugin_Internals&    internalShapes)
1220 {
1221   SMESHDS_Mesh* meshDS = internalShapes.getMesh().GetMeshDS();
1222   
1223   // find ng indices of internal faces
1224   set<int> ngFaceIds;
1225   for ( int ngFaceID = 1; ngFaceID <= occgeom.fmap.Extent(); ++ngFaceID )
1226   {
1227     int smeshID = meshDS->ShapeToIndex( occgeom.fmap( ngFaceID ));
1228     if ( internalShapes.isInternalShape( smeshID ))
1229       ngFaceIds.insert( ngFaceID );
1230   }
1231   if ( !ngFaceIds.empty() )
1232   {
1233     // duplicate faces
1234     int i, nbFaces = ngMesh.GetNSE();
1235     for ( i = 1; i <= nbFaces; ++i)
1236     {
1237       netgen::Element2d elem = ngMesh.SurfaceElement(i);
1238       if ( ngFaceIds.count( elem.GetIndex() ))
1239       {
1240         swap( elem[1], elem[2] );
1241         ngMesh.AddSurfaceElement (elem);
1242       }
1243     }
1244   }
1245 }
1246
1247 //================================================================================
1248 /*!
1249  * \brief Tries to heal the mesh on a FACE. The FACE is supposed to be partially
1250  *        meshed due to NETGEN failure
1251  *  \param [in] occgeom - geometry
1252  *  \param [in,out] ngMesh - the mesh to fix
1253  *  \param [inout] faceID - ID of the FACE to fix the mesh on
1254  *  \return bool - is mesh is or becomes OK
1255  */
1256 //================================================================================
1257
1258 bool NETGENPlugin_Mesher::FixFaceMesh(const netgen::OCCGeometry& occgeom,
1259                                       netgen::Mesh&              ngMesh,
1260                                       const int                  faceID)
1261 {
1262   // we address a case where the FACE is almost fully meshed except small holes
1263   // of usually triangular shape at FACE boundary (IPAL52861)
1264
1265   // The case appeared to be not simple: holes only look triangular but
1266   // indeed are a self intersecting polygon. A reason of the bug was in coincident
1267   // NG points on a seam edge. But the code below is very nice, leave it for
1268   // another case.
1269   return false;
1270
1271
1272   if ( occgeom.fmap.Extent() < faceID )
1273     return false;
1274   //const TopoDS_Face& face = TopoDS::Face( occgeom.fmap( faceID ));
1275
1276   // find free links on the FACE
1277   NCollection_Map<Link> linkMap;
1278   for ( int iF = 1; iF <= ngMesh.GetNSE(); ++iF )
1279   {
1280     const netgen::Element2d& elem = ngMesh.SurfaceElement(iF);
1281     if ( faceID != elem.GetIndex() )
1282       continue;
1283     int n0 = elem[ elem.GetNP() - 1 ];
1284     for ( int i = 0; i < elem.GetNP(); ++i )
1285     {
1286       int n1 = elem[i];
1287       Link link( n0, n1 );
1288       if ( !linkMap.Add( link ))
1289         linkMap.Remove( link );
1290       n0 = n1;
1291     }
1292   }
1293   // add/remove boundary links
1294   for ( int iSeg = 1; iSeg <= ngMesh.GetNSeg(); ++iSeg )
1295   {
1296     const netgen::Segment& seg = ngMesh.LineSegment( iSeg );
1297     if ( seg.si != faceID ) // !edgeIDs.Contains( seg.edgenr ))
1298       continue;
1299     Link link( seg[1], seg[0] ); // reverse!!!
1300     if ( !linkMap.Add( link ))
1301       linkMap.Remove( link );
1302   }
1303   if ( linkMap.IsEmpty() )
1304     return true;
1305   if ( linkMap.Extent() < 3 )
1306     return false;
1307
1308   // make triangles of the links
1309
1310   netgen::Element2d tri(3);
1311   tri.SetIndex ( faceID );
1312
1313   NCollection_Map<Link>::Iterator linkIt( linkMap );
1314   Link link1 = linkIt.Value();
1315   // look for a link connected to link1
1316   NCollection_Map<Link>::Iterator linkIt2 = linkIt;
1317   for ( linkIt2.Next(); linkIt2.More(); linkIt2.Next() )
1318   {
1319     const Link& link2 = linkIt2.Value();
1320     if ( link2.IsConnected( link1 ))
1321     {
1322       // look for a link connected to both link1 and link2
1323       NCollection_Map<Link>::Iterator linkIt3 = linkIt2;
1324       for ( linkIt3.Next(); linkIt3.More(); linkIt3.Next() )
1325       {
1326         const Link& link3 = linkIt3.Value();
1327         if ( link3.IsConnected( link1 ) &&
1328              link3.IsConnected( link2 ) )
1329         {
1330           // add a triangle
1331           tri[0] = link1.n2;
1332           tri[1] = link1.n1;
1333           tri[2] = ( link2.Contains( link1.n1 ) ? link2.n1 : link3.n1 );
1334           if ( tri[0] == tri[2] || tri[1] == tri[2] )
1335             return false;
1336           ngMesh.AddSurfaceElement( tri );
1337
1338           // prepare for the next tria search
1339           if ( linkMap.Extent() == 3 )
1340             return true;
1341           linkMap.Remove( link3 );
1342           linkMap.Remove( link2 );
1343           linkIt.Next();
1344           linkMap.Remove( link1 );
1345           link1 = linkIt.Value();
1346           linkIt2 = linkIt;
1347           break;
1348         }
1349       }
1350     }
1351   }
1352   return false;
1353
1354 } // FixFaceMesh()
1355
1356 namespace
1357 {
1358   //================================================================================
1359   // define gp_XY_Subtracted pointer to function calling gp_XY::Subtracted(gp_XY)
1360   gp_XY_FunPtr(Subtracted);
1361   //gp_XY_FunPtr(Added);
1362
1363   //================================================================================
1364   /*!
1365    * \brief Evaluate distance between two 2d points along the surface
1366    */
1367   //================================================================================
1368
1369   double evalDist( const gp_XY&                uv1,
1370                    const gp_XY&                uv2,
1371                    const Handle(Geom_Surface)& surf,
1372                    const int                   stopHandler=-1)
1373   {
1374     if ( stopHandler > 0 ) // continue recursion
1375     {
1376       gp_XY mid = SMESH_MesherHelper::GetMiddleUV( surf, uv1, uv2 );
1377       return evalDist( uv1,mid, surf, stopHandler-1 ) + evalDist( mid,uv2, surf, stopHandler-1 );
1378     }
1379     double dist3D = surf->Value( uv1.X(), uv1.Y() ).Distance( surf->Value( uv2.X(), uv2.Y() ));
1380     if ( stopHandler == 0 ) // stop recursion
1381       return dist3D;
1382     
1383     // start recursion if necessary
1384     double dist2D = SMESH_MesherHelper::ApplyIn2D(surf, uv1, uv2, gp_XY_Subtracted, 0).Modulus();
1385     if ( fabs( dist3D - dist2D ) < dist2D * 1e-10 )
1386       return dist3D; // equal parametrization of a planar surface
1387
1388     return evalDist( uv1, uv2, surf, 3 ); // start recursion
1389   }
1390
1391   //================================================================================
1392   /*!
1393    * \brief Data of vertex internal in geom face
1394    */
1395   //================================================================================
1396
1397   struct TIntVData
1398   {
1399     gp_XY uv;        //!< UV in face parametric space
1400     int   ngId;      //!< ng id of corrsponding node
1401     gp_XY uvClose;   //!< UV of closest boundary node
1402     int   ngIdClose; //!< ng id of closest boundary node
1403   };
1404
1405   //================================================================================
1406   /*!
1407    * \brief Data of vertex internal in solid
1408    */
1409   //================================================================================
1410
1411   struct TIntVSoData
1412   {
1413     int   ngId;      //!< ng id of corresponding node
1414     int   ngIdClose; //!< ng id of closest 2d mesh element
1415     int   ngIdCloseN; //!< ng id of closest node of the closest 2d mesh element
1416   };
1417
1418   inline double dist2(const netgen::MeshPoint& p1, const netgen::MeshPoint& p2)
1419   {
1420     return gp_Pnt( NGPOINT_COORDS(p1)).SquareDistance( gp_Pnt( NGPOINT_COORDS(p2)));
1421   }
1422 }
1423
1424 //================================================================================
1425 /*!
1426  * \brief Make netgen take internal vertices in faces into account by adding
1427  *        segments including internal vertices
1428  *
1429  * This function works in supposition that 1D mesh is already computed in ngMesh
1430  */
1431 //================================================================================
1432
1433 void NETGENPlugin_Mesher::AddIntVerticesInFaces(const netgen::OCCGeometry&     occgeom,
1434                                                 netgen::Mesh&                  ngMesh,
1435                                                 vector<const SMDS_MeshNode*>&  nodeVec,
1436                                                 NETGENPlugin_Internals&        internalShapes)
1437 {
1438   if ((int) nodeVec.size() < ngMesh.GetNP() )
1439     nodeVec.resize( ngMesh.GetNP(), 0 );
1440
1441   SMESHDS_Mesh* meshDS = internalShapes.getMesh().GetMeshDS();
1442   SMESH_MesherHelper helper( internalShapes.getMesh() );
1443
1444   const map<int,list<int> >& face2Vert = internalShapes.getFacesWithVertices();
1445   map<int,list<int> >::const_iterator f2v = face2Vert.begin();
1446   for ( ; f2v != face2Vert.end(); ++f2v )
1447   {
1448     const TopoDS_Face& face = TopoDS::Face( meshDS->IndexToShape( f2v->first ));
1449     if ( face.IsNull() ) continue;
1450     int faceNgID = occgeom.fmap.FindIndex (face);
1451     if ( faceNgID < 0 ) continue;
1452
1453     TopLoc_Location loc;
1454     Handle(Geom_Surface) surf = BRep_Tool::Surface(face,loc);
1455
1456     helper.SetSubShape( face );
1457     helper.SetElementsOnShape( true );
1458
1459     // Get data of internal vertices and add them to ngMesh
1460
1461     multimap< double, TIntVData > dist2VData; // sort vertices by distance from boundary nodes
1462
1463     int i, nbSegInit = ngMesh.GetNSeg();
1464
1465     // boundary characteristics
1466     double totSegLen2D = 0;
1467     int totNbSeg = 0;
1468
1469     const list<int>& iVertices = f2v->second;
1470     list<int>::const_iterator iv = iVertices.begin();
1471     for ( int nbV = 0; iv != iVertices.end(); ++iv, nbV++ )
1472     {
1473       TIntVData vData;
1474       // get node on vertex
1475       const TopoDS_Vertex V = TopoDS::Vertex( meshDS->IndexToShape( *iv ));
1476       const SMDS_MeshNode * nV = SMESH_Algo::VertexNode( V, meshDS );
1477       if ( !nV )
1478       {
1479         SMESH_subMesh* sm = helper.GetMesh()->GetSubMesh( V );
1480         sm->ComputeStateEngine( SMESH_subMesh::COMPUTE );
1481         nV = SMESH_Algo::VertexNode( V, meshDS );
1482         if ( !nV ) continue;
1483       }
1484       // add ng node
1485       netgen::MeshPoint mp( netgen::Point<3> (nV->X(), nV->Y(), nV->Z()) );
1486       ngMesh.AddPoint ( mp, 1, netgen::EDGEPOINT );
1487       vData.ngId = ngMesh.GetNP();
1488       nodeVec.push_back( nV );
1489
1490       // get node UV
1491       bool uvOK = true;
1492       vData.uv = helper.GetNodeUV( face, nV, 0, &uvOK );
1493       if ( !uvOK ) helper.CheckNodeUV( face, nV, vData.uv, BRep_Tool::Tolerance(V),/*force=*/1);
1494
1495       // loop on all segments of the face to find the node closest to vertex and to count
1496       // average segment 2d length
1497       double closeDist2 = numeric_limits<double>::max(), dist2;
1498       int ngIdLast = 0;
1499       for (i = 1; i <= ngMesh.GetNSeg(); ++i)
1500       {
1501         netgen::Segment & seg = ngMesh.LineSegment(i);
1502         if ( seg.si != faceNgID ) continue;
1503         gp_XY uv[2];
1504         for ( int iEnd = 0; iEnd < 2; ++iEnd)
1505         {
1506           uv[iEnd].SetCoord( seg.epgeominfo[iEnd].u, seg.epgeominfo[iEnd].v );
1507           if ( ngIdLast == seg[ iEnd ] ) continue;
1508           dist2 = helper.ApplyIn2D(surf, uv[iEnd], vData.uv, gp_XY_Subtracted,0).SquareModulus();
1509           if ( dist2 < closeDist2 )
1510             vData.ngIdClose = seg[ iEnd ], vData.uvClose = uv[iEnd], closeDist2 = dist2;
1511           ngIdLast = seg[ iEnd ];
1512         }
1513         if ( !nbV )
1514         {
1515           totSegLen2D += helper.ApplyIn2D(surf, uv[0], uv[1], gp_XY_Subtracted, false).Modulus();
1516           totNbSeg++;
1517         }
1518       }
1519       dist2VData.insert( make_pair( closeDist2, vData ));
1520     }
1521
1522     if ( totNbSeg == 0 ) break;
1523     double avgSegLen2d = totSegLen2D / totNbSeg;
1524
1525     // Loop on vertices to add segments
1526
1527     multimap< double, TIntVData >::iterator dist_vData = dist2VData.begin();
1528     for ( ; dist_vData != dist2VData.end(); ++dist_vData )
1529     {
1530       double closeDist2 = dist_vData->first, dist2;
1531       TIntVData & vData = dist_vData->second;
1532
1533       // try to find more close node among segments added for internal vertices
1534       for (i = nbSegInit+1; i <= ngMesh.GetNSeg(); ++i)
1535       {
1536         netgen::Segment & seg = ngMesh.LineSegment(i);
1537         if ( seg.si != faceNgID ) continue;
1538         gp_XY uv[2];
1539         for ( int iEnd = 0; iEnd < 2; ++iEnd)
1540         {
1541           uv[iEnd].SetCoord( seg.epgeominfo[iEnd].u, seg.epgeominfo[iEnd].v );
1542           dist2 = helper.ApplyIn2D(surf, uv[iEnd], vData.uv, gp_XY_Subtracted,0).SquareModulus();
1543           if ( dist2 < closeDist2 )
1544             vData.ngIdClose = seg[ iEnd ], vData.uvClose = uv[iEnd], closeDist2 = dist2;
1545         }
1546       }
1547       // decide whether to use the closest node as the second end of segment or to
1548       // create a new point
1549       int segEnd1 = vData.ngId;
1550       int segEnd2 = vData.ngIdClose; // to use closest node
1551       gp_XY uvV = vData.uv, uvP = vData.uvClose;
1552       double segLenHint  = ngMesh.GetH( ngMesh.Point( vData.ngId ));
1553       double nodeDist2D  = sqrt( closeDist2 );
1554       double nodeDist3D  = evalDist( vData.uv, vData.uvClose, surf );
1555       bool avgLenOK  = ( avgSegLen2d < 0.75 * nodeDist2D );
1556       bool hintLenOK = ( segLenHint  < 0.75 * nodeDist3D );
1557       //cout << "uvV " << uvV.X() <<","<<uvV.Y() << " ";
1558       if ( hintLenOK || avgLenOK )
1559       {
1560         // create a point between the closest node and V
1561
1562         // how far from V
1563         double r = min( 0.5, ( hintLenOK ? segLenHint/nodeDist3D : avgSegLen2d/nodeDist2D ));
1564         // direction from V to closet node in 2D
1565         gp_Dir2d v2n( helper.ApplyIn2D(surf, uvP, uvV, gp_XY_Subtracted, false ));
1566         // new point
1567         uvP = vData.uv + r * nodeDist2D * v2n.XY();
1568         gp_Pnt P = surf->Value( uvP.X(), uvP.Y() ).Transformed( loc );
1569
1570         netgen::MeshPoint mp( netgen::Point<3> (P.X(), P.Y(), P.Z()));
1571         ngMesh.AddPoint ( mp, 1, netgen::EDGEPOINT );
1572         segEnd2 = ngMesh.GetNP();
1573         //cout << "Middle " << r << " uv " << uvP.X() << "," << uvP.Y() << "( " << ngMesh.Point(segEnd2).X()<<","<<ngMesh.Point(segEnd2).Y()<<","<<ngMesh.Point(segEnd2).Z()<<" )"<< endl;
1574         SMDS_MeshNode * nP = helper.AddNode(P.X(), P.Y(), P.Z());
1575         nodeVec.push_back( nP );
1576       }
1577       //else cout << "at Node " << " uv " << uvP.X() << "," << uvP.Y() << endl;
1578
1579       // Add the segment
1580       netgen::Segment seg;
1581
1582       if ( segEnd1 > segEnd2 ) swap( segEnd1, segEnd2 ), swap( uvV, uvP );
1583       seg[0] = segEnd1;  // ng node id
1584       seg[1] = segEnd2;  // ng node id
1585       seg.edgenr = ngMesh.GetNSeg() + 1;// segment id
1586       seg.si = faceNgID;
1587
1588       seg.epgeominfo[ 0 ].dist = 0; // param on curve
1589       seg.epgeominfo[ 0 ].u    = uvV.X();
1590       seg.epgeominfo[ 0 ].v    = uvV.Y();
1591       seg.epgeominfo[ 1 ].dist = 1; // param on curve
1592       seg.epgeominfo[ 1 ].u    = uvP.X();
1593       seg.epgeominfo[ 1 ].v    = uvP.Y();
1594
1595 //       seg.epgeominfo[ 0 ].edgenr = 10; //  = geom.emap.FindIndex(edge);
1596 //       seg.epgeominfo[ 1 ].edgenr = 10; //  = geom.emap.FindIndex(edge);
1597
1598       ngMesh.AddSegment (seg);
1599
1600       // add reverse segment
1601       swap( seg[0], seg[1] );
1602       swap( seg.epgeominfo[0], seg.epgeominfo[1] );
1603       seg.edgenr = ngMesh.GetNSeg() + 1; // segment id
1604       ngMesh.AddSegment (seg);
1605     }
1606
1607   }
1608 }
1609
1610 //================================================================================
1611 /*!
1612  * \brief Make netgen take internal vertices in solids into account by adding
1613  *        faces including internal vertices
1614  *
1615  * This function works in supposition that 2D mesh is already computed in ngMesh
1616  */
1617 //================================================================================
1618
1619 void NETGENPlugin_Mesher::AddIntVerticesInSolids(const netgen::OCCGeometry&     occgeom,
1620                                                  netgen::Mesh&                  ngMesh,
1621                                                  vector<const SMDS_MeshNode*>&  nodeVec,
1622                                                  NETGENPlugin_Internals&        internalShapes)
1623 {
1624 #ifdef DUMP_TRIANGLES_SCRIPT
1625   // create a python script making a mesh containing triangles added for internal vertices
1626   ofstream py(DUMP_TRIANGLES_SCRIPT);
1627   py << "import SMESH"<< endl
1628      << "from salome.smesh import smeshBuilder"<<endl
1629      << "smesh = smeshBuilder.New()"<<endl
1630      << "m = smesh.Mesh(name='triangles')" << endl;
1631 #endif
1632   if ((int) nodeVec.size() < ngMesh.GetNP() )
1633     nodeVec.resize( ngMesh.GetNP(), 0 );
1634
1635   SMESHDS_Mesh* meshDS = internalShapes.getMesh().GetMeshDS();
1636   SMESH_MesherHelper helper( internalShapes.getMesh() );
1637
1638   const map<int,list<int> >& so2Vert = internalShapes.getSolidsWithVertices();
1639   map<int,list<int> >::const_iterator s2v = so2Vert.begin();
1640   for ( ; s2v != so2Vert.end(); ++s2v )
1641   {
1642     const TopoDS_Shape& solid = meshDS->IndexToShape( s2v->first );
1643     if ( solid.IsNull() ) continue;
1644     int solidNgID = occgeom.somap.FindIndex (solid);
1645     if ( solidNgID < 0 && !occgeom.somap.IsEmpty() ) continue;
1646
1647     helper.SetSubShape( solid );
1648     helper.SetElementsOnShape( true );
1649
1650     // find ng indices of faces within the solid
1651     set<int> ngFaceIds;
1652     for (TopExp_Explorer fExp(solid, TopAbs_FACE); fExp.More(); fExp.Next() )
1653       ngFaceIds.insert( occgeom.fmap.FindIndex( fExp.Current() ));
1654     if ( ngFaceIds.size() == 1 && *ngFaceIds.begin() == 0 )
1655       ngFaceIds.insert( 1 );
1656
1657     // Get data of internal vertices and add them to ngMesh
1658
1659     multimap< double, TIntVSoData > dist2VData; // sort vertices by distance from ng faces
1660
1661     int i, nbFaceInit = ngMesh.GetNSE();
1662
1663     // boundary characteristics
1664     double totSegLen = 0;
1665     int totNbSeg = 0;
1666
1667     const list<int>& iVertices = s2v->second;
1668     list<int>::const_iterator iv = iVertices.begin();
1669     for ( int nbV = 0; iv != iVertices.end(); ++iv, nbV++ )
1670     {
1671       TIntVSoData vData;
1672       const TopoDS_Vertex V = TopoDS::Vertex( meshDS->IndexToShape( *iv ));
1673
1674       // get node on vertex
1675       const SMDS_MeshNode * nV = SMESH_Algo::VertexNode( V, meshDS );
1676       if ( !nV )
1677       {
1678         SMESH_subMesh* sm = helper.GetMesh()->GetSubMesh( V );
1679         sm->ComputeStateEngine( SMESH_subMesh::COMPUTE );
1680         nV = SMESH_Algo::VertexNode( V, meshDS );
1681         if ( !nV ) continue;
1682       }
1683       // add ng node
1684       netgen::MeshPoint mpV( netgen::Point<3> (nV->X(), nV->Y(), nV->Z()) );
1685       ngMesh.AddPoint ( mpV, 1, netgen::FIXEDPOINT );
1686       vData.ngId = ngMesh.GetNP();
1687       nodeVec.push_back( nV );
1688
1689       // loop on all 2d elements to find the one closest to vertex and to count
1690       // average segment length
1691       double closeDist2 = numeric_limits<double>::max(), avgDist2;
1692       for (i = 1; i <= ngMesh.GetNSE(); ++i)
1693       {
1694         const netgen::Element2d& elem = ngMesh.SurfaceElement(i);
1695         if ( !ngFaceIds.count( elem.GetIndex() )) continue;
1696         avgDist2 = 0;
1697         multimap< double, int> dist2nID; // sort nodes of element by distance from V
1698         for ( int j = 0; j < elem.GetNP(); ++j)
1699         {
1700           netgen::MeshPoint mp = ngMesh.Point( elem[j] );
1701           double d2 = dist2( mpV, mp );
1702           dist2nID.insert( make_pair( d2, elem[j] ));
1703           avgDist2 += d2 / elem.GetNP();
1704           if ( !nbV )
1705             totNbSeg++, totSegLen+= sqrt( dist2( mp, ngMesh.Point( elem[(j+1)%elem.GetNP()])));
1706         }
1707         double dist = dist2nID.begin()->first; //avgDist2;
1708         if ( dist < closeDist2 )
1709           vData.ngIdClose= i, vData.ngIdCloseN= dist2nID.begin()->second, closeDist2= dist;
1710       }
1711       dist2VData.insert( make_pair( closeDist2, vData ));
1712     }
1713
1714     if ( totNbSeg == 0 ) break;
1715     double avgSegLen = totSegLen / totNbSeg;
1716
1717     // Loop on vertices to add triangles
1718
1719     multimap< double, TIntVSoData >::iterator dist_vData = dist2VData.begin();
1720     for ( ; dist_vData != dist2VData.end(); ++dist_vData )
1721     {
1722       double closeDist2   = dist_vData->first;
1723       TIntVSoData & vData = dist_vData->second;
1724
1725       const netgen::MeshPoint& mpV = ngMesh.Point( vData.ngId );
1726
1727       // try to find more close face among ones added for internal vertices
1728       for (i = nbFaceInit+1; i <= ngMesh.GetNSE(); ++i)
1729       {
1730         double avgDist2 = 0;
1731         multimap< double, int> dist2nID;
1732         const netgen::Element2d& elem = ngMesh.SurfaceElement(i);
1733         for ( int j = 0; j < elem.GetNP(); ++j)
1734         {
1735           double d = dist2( mpV, ngMesh.Point( elem[j] ));
1736           dist2nID.insert( make_pair( d, elem[j] ));
1737           avgDist2 += d / elem.GetNP();
1738           if ( avgDist2 < closeDist2 )
1739             vData.ngIdClose= i, vData.ngIdCloseN= dist2nID.begin()->second, closeDist2= avgDist2;
1740         }
1741       }
1742       // sort nodes of the closest face by angle with vector from V to the closest node
1743       const double tol = numeric_limits<double>::min();
1744       map< double, int > angle2ID;
1745       const netgen::Element2d& closeFace = ngMesh.SurfaceElement( vData.ngIdClose );
1746       netgen::MeshPoint mp[2];
1747       mp[0] = ngMesh.Point( vData.ngIdCloseN );
1748       gp_XYZ p1( NGPOINT_COORDS( mp[0] ));
1749       gp_XYZ pV( NGPOINT_COORDS( mpV ));
1750       gp_Vec v2p1( pV, p1 );
1751       double distN1 = v2p1.Magnitude();
1752       if ( distN1 <= tol ) continue;
1753       v2p1 /= distN1;
1754       for ( int j = 0; j < closeFace.GetNP(); ++j)
1755       {
1756         mp[1] = ngMesh.Point( closeFace[j] );
1757         gp_Vec v2p( pV, gp_Pnt( NGPOINT_COORDS( mp[1] )) );
1758         angle2ID.insert( make_pair( v2p1.Angle( v2p ), closeFace[j]));
1759       }
1760       // get node with angle of 60 degrees or greater
1761       map< double, int >::iterator angle_id = angle2ID.lower_bound( 60. * M_PI / 180. );
1762       if ( angle_id == angle2ID.end() ) angle_id = --angle2ID.end();
1763       const double minAngle = 30. * M_PI / 180.;
1764       const double angle = angle_id->first;
1765       bool angleOK = ( angle > minAngle );
1766
1767       // find points to create a triangle
1768       netgen::Element2d tri(3);
1769       tri.SetIndex ( 1 );
1770       tri[0] = vData.ngId;
1771       tri[1] = vData.ngIdCloseN; // to use the closest nodes
1772       tri[2] = angle_id->second; // to use the node with best angle
1773
1774       // decide whether to use the closest node and the node with best angle or to create new ones
1775       for ( int isBestAngleN = 0; isBestAngleN < 2; ++isBestAngleN )
1776       {
1777         bool createNew = !angleOK; //, distOK = true;
1778         double distFromV;
1779         int triInd = isBestAngleN ? 2 : 1;
1780         mp[isBestAngleN] = ngMesh.Point( tri[triInd] );
1781         if ( isBestAngleN )
1782         {
1783           if ( angleOK )
1784           {
1785             double distN2 = sqrt( dist2( mpV, mp[isBestAngleN]));
1786             createNew = ( fabs( distN2 - distN1 ) > 0.25 * distN1 );
1787           }
1788           else if ( angle < tol )
1789           {
1790             v2p1.SetX( v2p1.X() + 1e-3 );
1791           }
1792           distFromV = distN1;
1793         }
1794         else
1795         {
1796           double segLenHint = ngMesh.GetH( ngMesh.Point( vData.ngId ));
1797           bool     avgLenOK = ( avgSegLen < 0.75 * distN1 );
1798           bool    hintLenOK = ( segLenHint  < 0.75 * distN1 );
1799           createNew = (createNew || avgLenOK || hintLenOK );
1800           // we create a new node not closer than 0.5 to the closest face
1801           // in order not to clash with other close face
1802           double r = min( 0.5, ( hintLenOK ? segLenHint : avgSegLen ) / distN1 );
1803           distFromV = r * distN1;
1804         }
1805         if ( createNew )
1806         {
1807           // create a new point, between the node and the vertex if angleOK
1808           gp_XYZ p( NGPOINT_COORDS( mp[isBestAngleN] ));
1809           gp_Vec v2p( pV, p ); v2p.Normalize();
1810           if ( isBestAngleN && !angleOK )
1811             p = p1 + gp_Dir( v2p.XYZ() - v2p1.XYZ()).XYZ() * distN1 * 0.95;
1812           else
1813             p = pV + v2p.XYZ() * distFromV;
1814
1815           if ( !isBestAngleN ) p1 = p, distN1 = distFromV;
1816
1817           mp[isBestAngleN].SetPoint( netgen::Point<3> (p.X(), p.Y(), p.Z()));
1818           ngMesh.AddPoint ( mp[isBestAngleN], 1, netgen::SURFACEPOINT );
1819           tri[triInd] = ngMesh.GetNP();
1820           nodeVec.push_back( helper.AddNode( p.X(), p.Y(), p.Z()) );
1821         }
1822       }
1823       ngMesh.AddSurfaceElement (tri);
1824       swap( tri[1], tri[2] );
1825       ngMesh.AddSurfaceElement (tri);
1826
1827 #ifdef DUMP_TRIANGLES_SCRIPT
1828       py << "n1 = m.AddNode( "<< mpV(0)<<", "<< mpV(1)<<", "<< mpV(2)<<") "<< endl
1829          << "n2 = m.AddNode( "<< mp[0](0)<<", "<< mp[0](1)<<", "<< mp[0](2)<<") "<< endl
1830          << "n3 = m.AddNode( "<< mp[1](0)<<", "<< mp[1](1)<<", "<< mp[1](2)<<" )" << endl
1831          << "m.AddFace([n1,n2,n3])" << endl;
1832 #endif
1833     } // loop on internal vertices of a solid
1834
1835   } // loop on solids with internal vertices
1836 }
1837
1838 //================================================================================
1839 /*!
1840  * \brief Fill netgen mesh with segments of a FACE
1841  *  \param ngMesh - netgen mesh
1842  *  \param geom - container of OCCT geometry to mesh
1843  *  \param wires - data of nodes on FACE boundary
1844  *  \param helper - mesher helper holding the FACE
1845  *  \param nodeVec - vector of nodes in which node index == netgen ID
1846  *  \retval SMESH_ComputeErrorPtr - error description 
1847  */
1848 //================================================================================
1849
1850 SMESH_ComputeErrorPtr
1851 NETGENPlugin_Mesher::AddSegmentsToMesh(netgen::Mesh&                    ngMesh,
1852                                        netgen::OCCGeometry&             geom,
1853                                        const TSideVector&               wires,
1854                                        SMESH_MesherHelper&              helper,
1855                                        vector< const SMDS_MeshNode* > & nodeVec,
1856                                        const bool                       overrideMinH)
1857 {
1858   // ----------------------------
1859   // Check wires and count nodes
1860   // ----------------------------
1861   int nbNodes = 0;
1862   for ( size_t iW = 0; iW < wires.size(); ++iW )
1863   {
1864     StdMeshers_FaceSidePtr wire = wires[ iW ];
1865     if ( wire->MissVertexNode() )
1866     {
1867       // Commented for issue 0020960. It worked for the case, let's wait for case where it doesn't.
1868       // It seems that there is no reason for this limitation
1869 //       return TError
1870 //         (new SMESH_ComputeError(COMPERR_BAD_INPUT_MESH, "Missing nodes on vertices"));
1871     }
1872     const vector<UVPtStruct>& uvPtVec = wire->GetUVPtStruct();
1873     if ((int) uvPtVec.size() != wire->NbPoints() )
1874       return SMESH_ComputeError::New(COMPERR_BAD_INPUT_MESH,
1875                                      SMESH_Comment("Unexpected nb of points on wire ") << iW
1876                                      << ": " << uvPtVec.size()<<" != "<<wire->NbPoints());
1877     nbNodes += wire->NbPoints();
1878   }
1879   nodeVec.reserve( nodeVec.size() + nbNodes + 1 );
1880   if ( nodeVec.empty() )
1881     nodeVec.push_back( 0 );
1882
1883   // -----------------
1884   // Fill netgen mesh
1885   // -----------------
1886
1887   const bool wasNgMeshEmpty = ( ngMesh.GetNP() < 1 ); /* true => this method is called by
1888                                                          NETGENPlugin_NETGEN_2D_ONLY */
1889
1890   // map for nodes on vertices since they can be shared between wires
1891   // ( issue 0020676, face_int_box.brep) and nodes built by NETGEN
1892   map<const SMDS_MeshNode*, int > node2ngID;
1893   if ( !wasNgMeshEmpty ) // fill node2ngID with nodes built by NETGEN
1894   {
1895     set< int > subIDs; // ids of sub-shapes of the FACE
1896     for ( size_t iW = 0; iW < wires.size(); ++iW )
1897     {
1898       StdMeshers_FaceSidePtr wire = wires[ iW ];
1899       for ( int iE = 0, nbE = wire->NbEdges(); iE < nbE; ++iE )
1900       {
1901         subIDs.insert( wire->EdgeID( iE ));
1902         subIDs.insert( helper.GetMeshDS()->ShapeToIndex( wire->FirstVertex( iE )));
1903       }
1904     }
1905     for ( size_t ngID = 1; ngID < nodeVec.size(); ++ngID )
1906       if ( subIDs.count( nodeVec[ngID]->getshapeId() ))
1907         node2ngID.insert( make_pair( nodeVec[ngID], ngID ));
1908   }
1909
1910   const int solidID = 0, faceID = geom.fmap.FindIndex( helper.GetSubShape() );
1911   if ( ngMesh.GetNFD() < 1 )
1912     ngMesh.AddFaceDescriptor( netgen::FaceDescriptor( faceID, solidID, solidID, 0 ));
1913
1914   for ( size_t iW = 0; iW < wires.size(); ++iW )
1915   {
1916     StdMeshers_FaceSidePtr       wire = wires[ iW ];
1917     const vector<UVPtStruct>& uvPtVec = wire->GetUVPtStruct();
1918     const int              nbSegments = wire->NbPoints() - 1;
1919
1920     // assure the 1st node to be in node2ngID, which is needed to correctly
1921     // "close chain of segments" (see below) in case if the 1st node is not
1922     // onVertex because it is on a Viscous layer
1923     node2ngID.insert( make_pair( uvPtVec[ 0 ].node, ngMesh.GetNP() + 1 ));
1924
1925     // compute length of every segment
1926     vector<double> segLen( nbSegments );
1927     for ( int i = 0; i < nbSegments; ++i )
1928       segLen[i] = SMESH_TNodeXYZ( uvPtVec[ i ].node ).Distance( uvPtVec[ i+1 ].node );
1929
1930     int edgeID = 1, posID = -2;
1931     bool isInternalWire = false;
1932     double vertexNormPar = 0;
1933     const int prevNbNGSeg = ngMesh.GetNSeg();
1934     for ( int i = 0; i < nbSegments; ++i ) // loop on segments
1935     {
1936       // Add the first point of a segment
1937
1938       const SMDS_MeshNode * n = uvPtVec[ i ].node;
1939       const int posShapeID = n->getshapeId();
1940       bool onVertex = ( n->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX );
1941       bool onEdge   = ( n->GetPosition()->GetTypeOfPosition() == SMDS_TOP_EDGE   );
1942
1943       // skip nodes on degenerated edges
1944       if ( helper.IsDegenShape( posShapeID ) &&
1945            helper.IsDegenShape( uvPtVec[ i+1 ].node->getshapeId() ))
1946         continue;
1947
1948       int ngID1 = ngMesh.GetNP() + 1, ngID2 = ngID1+1;
1949       if ( onVertex || ( !wasNgMeshEmpty && onEdge ) || helper.IsRealSeam( posShapeID ))
1950         ngID1 = node2ngID.insert( make_pair( n, ngID1 )).first->second;
1951       if ( ngID1 > ngMesh.GetNP() )
1952       {
1953         netgen::MeshPoint mp( netgen::Point<3> (n->X(), n->Y(), n->Z()) );
1954         ngMesh.AddPoint ( mp, 1, netgen::EDGEPOINT );
1955         nodeVec.push_back( n );
1956       }
1957       else // n is in ngMesh already, and ngID2 in prev segment is wrong
1958       {
1959         ngID2 = ngMesh.GetNP() + 1;
1960         if ( i > 0 ) // prev segment belongs to same wire
1961         {
1962           netgen::Segment& prevSeg = ngMesh.LineSegment( ngMesh.GetNSeg() );
1963           prevSeg[1] = ngID1;
1964         }
1965       }
1966
1967       // Add the segment
1968
1969       netgen::Segment seg;
1970
1971       seg[0]     = ngID1;                // ng node id
1972       seg[1]     = ngID2;                // ng node id
1973       seg.edgenr = ngMesh.GetNSeg() + 1; // ng segment id
1974       seg.si     = faceID;               // = geom.fmap.FindIndex (face);
1975
1976       for ( int iEnd = 0; iEnd < 2; ++iEnd)
1977       {
1978         const UVPtStruct& pnt = uvPtVec[ i + iEnd ];
1979
1980         seg.epgeominfo[ iEnd ].dist = pnt.param; // param on curve
1981         seg.epgeominfo[ iEnd ].u    = pnt.u;
1982         seg.epgeominfo[ iEnd ].v    = pnt.v;
1983
1984         // find out edge id and node parameter on edge
1985         onVertex = ( pnt.normParam + 1e-10 > vertexNormPar );
1986         if ( onVertex || posShapeID != posID )
1987         {
1988           // get edge id
1989           double normParam = pnt.normParam;
1990           if ( onVertex )
1991             normParam = 0.5 * ( uvPtVec[ i ].normParam + uvPtVec[ i+1 ].normParam );
1992           int edgeIndexInWire = wire->EdgeIndex( normParam );
1993           vertexNormPar = wire->LastParameter( edgeIndexInWire );
1994           const TopoDS_Edge& edge = wire->Edge( edgeIndexInWire );
1995           edgeID = geom.emap.FindIndex( edge );
1996           posID  = posShapeID;
1997           isInternalWire = ( edge.Orientation() == TopAbs_INTERNAL );
1998           // if ( onVertex ) // param on curve is different on each of two edges
1999           //   seg.epgeominfo[ iEnd ].dist = helper.GetNodeU( edge, pnt.node );
2000         }
2001         seg.epgeominfo[ iEnd ].edgenr = edgeID; //  = geom.emap.FindIndex(edge);
2002       }
2003
2004       ngMesh.AddSegment (seg);
2005       {
2006         // restrict size of elements near the segment
2007         SMESH_TNodeXYZ np1( n ), np2( uvPtVec[ i+1 ].node );
2008         // get an average size of adjacent segments to avoid sharp change of
2009         // element size (regression on issue 0020452, note 0010898)
2010         int   iPrev = SMESH_MesherHelper::WrapIndex( i-1, nbSegments );
2011         int   iNext = SMESH_MesherHelper::WrapIndex( i+1, nbSegments );
2012         double sumH = segLen[ iPrev ] + segLen[ i ] + segLen[ iNext ];
2013         int   nbSeg = ( int( segLen[ iPrev ] > sumH / 100.)  +
2014                         int( segLen[ i     ] > sumH / 100.)  +
2015                         int( segLen[ iNext ] > sumH / 100.));
2016         if ( nbSeg > 0 )
2017           RestrictLocalSize( ngMesh, 0.5*(np1+np2), sumH / nbSeg, overrideMinH );
2018       }
2019       if ( isInternalWire )
2020       {
2021         swap (seg[0], seg[1]);
2022         swap( seg.epgeominfo[0], seg.epgeominfo[1] );
2023         seg.edgenr = ngMesh.GetNSeg() + 1; // segment id
2024         ngMesh.AddSegment (seg);
2025       }
2026     } // loop on segments on a wire
2027
2028     // close chain of segments
2029     if ( nbSegments > 0 )
2030     {
2031       netgen::Segment& lastSeg = ngMesh.LineSegment( ngMesh.GetNSeg() - int( isInternalWire ));
2032       const SMDS_MeshNode * lastNode = uvPtVec.back().node;
2033       lastSeg[1] = node2ngID.insert( make_pair( lastNode, lastSeg[1] )).first->second;
2034       if ( lastSeg[1] > ngMesh.GetNP() )
2035       {
2036         netgen::MeshPoint mp( netgen::Point<3> (lastNode->X(), lastNode->Y(), lastNode->Z()) );
2037         ngMesh.AddPoint ( mp, 1, netgen::EDGEPOINT );
2038         nodeVec.push_back( lastNode );
2039       }
2040       if ( isInternalWire )
2041       {
2042         netgen::Segment& realLastSeg = ngMesh.LineSegment( ngMesh.GetNSeg() );
2043         realLastSeg[0] = lastSeg[1];
2044       }
2045     }
2046
2047 #ifdef DUMP_SEGMENTS
2048     cout << "BEGIN WIRE " << iW << endl;
2049     for ( int i = prevNbNGSeg+1; i <= ngMesh.GetNSeg(); ++i )
2050     {
2051       netgen::Segment& seg = ngMesh.LineSegment( i );
2052       if ( i > 1 ) {
2053         netgen::Segment& prevSeg = ngMesh.LineSegment( i-1 );
2054         if ( seg[0] == prevSeg[1] && seg[1] == prevSeg[0] )
2055         {
2056           cout << "Segment: " << seg.edgenr << endl << "\tis REVRESE of the previous one" << endl;
2057           continue;
2058         }
2059       }
2060       cout << "Segment: " << seg.edgenr << endl
2061            << "\tp1: " << seg[0] << "   n" << nodeVec[ seg[0]]->GetID() << endl
2062            << "\tp2: " << seg[1] << "   n" << nodeVec[ seg[1]]->GetID() <<  endl
2063            << "\tp0 param: " << seg.epgeominfo[ 0 ].dist << endl
2064            << "\tp0 uv: " << seg.epgeominfo[ 0 ].u <<", "<< seg.epgeominfo[ 0 ].v << endl
2065            << "\tp0 edge: " << seg.epgeominfo[ 0 ].edgenr << endl
2066            << "\tp1 param: " << seg.epgeominfo[ 1 ].dist << endl
2067            << "\tp1 uv: " << seg.epgeominfo[ 1 ].u <<", "<< seg.epgeominfo[ 1 ].v << endl
2068            << "\tp1 edge: " << seg.epgeominfo[ 1 ].edgenr << endl;
2069     }
2070     cout << "--END WIRE " << iW << endl;
2071 #else
2072     SMESH_Comment __not_unused_variable( prevNbNGSeg );
2073 #endif
2074
2075   } // loop on WIREs of a FACE
2076
2077   // add a segment instead of an internal vertex
2078   if ( wasNgMeshEmpty )
2079   {
2080     NETGENPlugin_Internals intShapes( *helper.GetMesh(), helper.GetSubShape(), /*is3D=*/false );
2081     AddIntVerticesInFaces( geom, ngMesh, nodeVec, intShapes );
2082   }
2083   ngMesh.CalcSurfacesOfNode();
2084
2085   return TError();
2086 }
2087
2088 //================================================================================
2089 /*!
2090  * \brief Fill SMESH mesh according to contents of netgen mesh
2091  *  \param occgeo - container of OCCT geometry to mesh
2092  *  \param ngMesh - netgen mesh
2093  *  \param initState - bn of entities in netgen mesh before computing
2094  *  \param sMesh - SMESH mesh to fill in
2095  *  \param nodeVec - vector of nodes in which node index == netgen ID
2096  *  \param comment - returns problem description
2097  *  \param quadHelper - holder of medium nodes of sub-meshes
2098  *  \retval int - error
2099  */
2100 //================================================================================
2101
2102 int NETGENPlugin_Mesher::FillSMesh(const netgen::OCCGeometry&          occgeo,
2103                                    netgen::Mesh&                       ngMesh,
2104                                    const NETGENPlugin_ngMeshInfo&      initState,
2105                                    SMESH_Mesh&                         sMesh,
2106                                    std::vector<const SMDS_MeshNode*>&  nodeVec,
2107                                    SMESH_Comment&                      comment,
2108                                    SMESH_MesherHelper*                 quadHelper)
2109 {
2110   int nbNod = ngMesh.GetNP();
2111   int nbSeg = ngMesh.GetNSeg();
2112   int nbFac = ngMesh.GetNSE();
2113   int nbVol = ngMesh.GetNE();
2114
2115   SMESHDS_Mesh* meshDS = sMesh.GetMeshDS();
2116
2117   // quadHelper is used for either
2118   // 1) making quadratic elements when a lower dimention mesh is loaded
2119   //    to SMESH before convertion to quadratic by NETGEN
2120   // 2) sewing of quadratic elements with quadratic elements of sub-meshes
2121   if ( quadHelper && !quadHelper->GetIsQuadratic() && quadHelper->GetTLinkNodeMap().empty() )
2122     quadHelper = 0;
2123
2124   // -------------------------------------
2125   // Create and insert nodes into nodeVec
2126   // -------------------------------------
2127
2128   nodeVec.resize( nbNod + 1 );
2129   int i, nbInitNod = initState._nbNodes;
2130   for (i = nbInitNod+1; i <= nbNod; ++i )
2131   {
2132     const netgen::MeshPoint& ngPoint = ngMesh.Point(i);
2133     SMDS_MeshNode* node = NULL;
2134     TopoDS_Vertex aVert;
2135     // First, netgen creates nodes on vertices in occgeo.vmap,
2136     // so node index corresponds to vertex index
2137     // but (issue 0020776) netgen does not create nodes with equal coordinates
2138     if ( i-nbInitNod <= occgeo.vmap.Extent() )
2139     {
2140       gp_Pnt p ( NGPOINT_COORDS(ngPoint) );
2141       for (int iV = i-nbInitNod; aVert.IsNull() && iV <= occgeo.vmap.Extent(); ++iV)
2142       {
2143         aVert = TopoDS::Vertex( occgeo.vmap( iV ));
2144         gp_Pnt pV = BRep_Tool::Pnt( aVert );
2145         if ( p.SquareDistance( pV ) > 1e-20 )
2146           aVert.Nullify();
2147         else
2148           node = const_cast<SMDS_MeshNode*>( SMESH_Algo::VertexNode( aVert, meshDS ));
2149       }
2150     }
2151     if (!node) // node not found on vertex
2152     {
2153       node = meshDS->AddNode( NGPOINT_COORDS( ngPoint ));
2154       if (!aVert.IsNull())
2155         meshDS->SetNodeOnVertex(node, aVert);
2156     }
2157     nodeVec[i] = node;
2158   }
2159
2160   // -------------------------------------------
2161   // Create mesh segments along geometric edges
2162   // -------------------------------------------
2163
2164   int nbInitSeg = initState._nbSegments;
2165   for (i = nbInitSeg+1; i <= nbSeg; ++i )
2166   {
2167     const netgen::Segment& seg = ngMesh.LineSegment(i);
2168     TopoDS_Edge aEdge;
2169     int pinds[3] = { seg.pnums[0], seg.pnums[1], seg.pnums[2] };
2170     int nbp = 0;
2171     double param2 = 0;
2172     for (int j=0; j < 3; ++j)
2173     {
2174       int pind = pinds[j];
2175       if (pind <= 0 || !nodeVec_ACCESS(pind))
2176         break;
2177       ++nbp;
2178       double param;
2179       if (j < 2)
2180       {
2181         if (aEdge.IsNull())
2182         {
2183           int aGeomEdgeInd = seg.epgeominfo[j].edgenr;
2184           if (aGeomEdgeInd > 0 && aGeomEdgeInd <= occgeo.emap.Extent())
2185             aEdge = TopoDS::Edge(occgeo.emap(aGeomEdgeInd));
2186         }
2187         param = seg.epgeominfo[j].dist;
2188         param2 += param;
2189       }
2190       else // middle point
2191       {
2192         param = param2 * 0.5;
2193       }
2194       if (!aEdge.IsNull() && nodeVec_ACCESS(pind)->getshapeId() < 1)
2195       {
2196         meshDS->SetNodeOnEdge(nodeVec_ACCESS(pind), aEdge, param);
2197       }
2198     }
2199     if ( nbp > 1 )
2200     {
2201       SMDS_MeshEdge* edge = 0;
2202       if (nbp == 2) // second order ?
2203       {
2204         if ( meshDS->FindEdge( nodeVec_ACCESS(pinds[0]), nodeVec_ACCESS(pinds[1])))
2205           continue;
2206         if ( quadHelper ) // final mesh must be quadratic
2207           edge = quadHelper->AddEdge(nodeVec_ACCESS(pinds[0]), nodeVec_ACCESS(pinds[1]));
2208         else
2209           edge = meshDS->AddEdge(nodeVec_ACCESS(pinds[0]), nodeVec_ACCESS(pinds[1]));
2210       }
2211       else
2212       {
2213         if ( meshDS->FindEdge( nodeVec_ACCESS(pinds[0]), nodeVec_ACCESS(pinds[1]),
2214                                nodeVec_ACCESS(pinds[2])))
2215           continue;
2216         edge = meshDS->AddEdge(nodeVec_ACCESS(pinds[0]), nodeVec_ACCESS(pinds[1]),
2217                                nodeVec_ACCESS(pinds[2]));
2218       }
2219       if (!edge)
2220       {
2221         if ( comment.empty() ) comment << "Cannot create a mesh edge";
2222         MESSAGE("Cannot create a mesh edge");
2223         nbSeg = nbFac = nbVol = 0;
2224         break;
2225       }
2226       if ( !aEdge.IsNull() && edge->getshapeId() < 1 )
2227         meshDS->SetMeshElementOnShape(edge, aEdge);
2228     }
2229     else if ( comment.empty() )
2230     {
2231       comment << "Invalid netgen segment #" << i;
2232     }
2233   }
2234
2235   // ----------------------------------------
2236   // Create mesh faces along geometric faces
2237   // ----------------------------------------
2238
2239   int nbInitFac = initState._nbFaces;
2240   int quadFaceID = ngMesh.GetNFD() + 1;
2241   if ( nbInitFac < nbFac )
2242     // add a faces descriptor to exclude qudrangle elements generated by NETGEN
2243     // from computation of 3D mesh
2244     ngMesh.AddFaceDescriptor (netgen::FaceDescriptor(quadFaceID, /*solid1=*/0, /*solid2=*/0, 0));
2245
2246   vector<const SMDS_MeshNode*> nodes;
2247   for (i = nbInitFac+1; i <= nbFac; ++i )
2248   {
2249     const netgen::Element2d& elem = ngMesh.SurfaceElement(i);
2250     const int        aGeomFaceInd = elem.GetIndex();
2251     TopoDS_Face aFace;
2252     if (aGeomFaceInd > 0 && aGeomFaceInd <= occgeo.fmap.Extent())
2253       aFace = TopoDS::Face(occgeo.fmap(aGeomFaceInd));
2254     nodes.clear();
2255     for ( int j = 1; j <= elem.GetNP(); ++j )
2256     {
2257       int pind = elem.PNum(j);
2258       if ( pind < 1 || pind >= (int) nodeVec.size() )
2259         break;
2260       if ( SMDS_MeshNode* node = nodeVec_ACCESS(pind))
2261       {
2262         nodes.push_back( node );
2263         if (!aFace.IsNull() && node->getshapeId() < 1)
2264         {
2265           const netgen::PointGeomInfo& pgi = elem.GeomInfoPi(j);
2266           meshDS->SetNodeOnFace(node, aFace, pgi.u, pgi.v);
2267         }
2268       }
2269     }
2270     if ((int) nodes.size() != elem.GetNP() )
2271     {
2272       if ( comment.empty() )
2273         comment << "Invalid netgen 2d element #" << i;
2274       continue; // bad node ids
2275     }
2276     SMDS_MeshFace* face = NULL;
2277     switch (elem.GetType())
2278     {
2279     case netgen::TRIG:
2280       if ( quadHelper ) // final mesh must be quadratic
2281         face = quadHelper->AddFace(nodes[0],nodes[1],nodes[2]);
2282       else
2283         face = meshDS->AddFace(nodes[0],nodes[1],nodes[2]);
2284       break;
2285     case netgen::QUAD:
2286       if ( quadHelper ) // final mesh must be quadratic
2287         face = quadHelper->AddFace(nodes[0],nodes[1],nodes[2],nodes[3]);
2288       else
2289         face = meshDS->AddFace(nodes[0],nodes[1],nodes[2],nodes[3]);
2290       // exclude qudrangle elements from computation of 3D mesh
2291       const_cast< netgen::Element2d& >( elem ).SetIndex( quadFaceID );
2292       break;
2293     case netgen::TRIG6:
2294       nodes[5] = mediumNode( nodes[0],nodes[1],nodes[5], quadHelper );
2295       nodes[3] = mediumNode( nodes[1],nodes[2],nodes[3], quadHelper );
2296       nodes[4] = mediumNode( nodes[2],nodes[0],nodes[4], quadHelper );
2297       face = meshDS->AddFace(nodes[0],nodes[1],nodes[2],nodes[5],nodes[3],nodes[4]);
2298       break;
2299     case netgen::QUAD8:
2300       nodes[4] = mediumNode( nodes[0],nodes[1],nodes[4], quadHelper );
2301       nodes[7] = mediumNode( nodes[1],nodes[2],nodes[7], quadHelper );
2302       nodes[5] = mediumNode( nodes[2],nodes[3],nodes[5], quadHelper );
2303       nodes[6] = mediumNode( nodes[3],nodes[0],nodes[6], quadHelper );
2304       face = meshDS->AddFace(nodes[0],nodes[1],nodes[2],nodes[3],
2305                              nodes[4],nodes[7],nodes[5],nodes[6]);
2306       // exclude qudrangle elements from computation of 3D mesh
2307       const_cast< netgen::Element2d& >( elem ).SetIndex( quadFaceID );
2308       break;
2309     default:
2310       MESSAGE("NETGEN created a face of unexpected type, ignoring");
2311       continue;
2312     }
2313     if ( !face )
2314     {
2315       if ( comment.empty() ) comment << "Cannot create a mesh face";
2316       MESSAGE("Cannot create a mesh face");
2317       nbSeg = nbFac = nbVol = 0;
2318       break;
2319     }
2320     if ( !aFace.IsNull() )
2321       meshDS->SetMeshElementOnShape( face, aFace );
2322   }
2323
2324   // ------------------
2325   // Create tetrahedra
2326   // ------------------
2327
2328   for ( i = 1; i <= nbVol; ++i )
2329   {
2330     const netgen::Element& elem = ngMesh.VolumeElement(i);      
2331     int aSolidInd = elem.GetIndex();
2332     TopoDS_Solid aSolid;
2333     if ( aSolidInd > 0 && aSolidInd <= occgeo.somap.Extent() )
2334       aSolid = TopoDS::Solid(occgeo.somap(aSolidInd));
2335     nodes.clear();
2336     for ( int j = 1; j <= elem.GetNP(); ++j )
2337     {
2338       int pind = elem.PNum(j);
2339       if ( pind < 1 || pind >= (int)nodeVec.size() )
2340         break;
2341       if ( SMDS_MeshNode* node = nodeVec_ACCESS(pind) )
2342       {
2343         nodes.push_back(node);
2344         if ( !aSolid.IsNull() && node->getshapeId() < 1 )
2345           meshDS->SetNodeInVolume(node, aSolid);
2346       }
2347     }
2348     if ((int) nodes.size() != elem.GetNP() )
2349     {
2350       if ( comment.empty() )
2351         comment << "Invalid netgen 3d element #" << i;
2352       continue;
2353     }
2354     SMDS_MeshVolume* vol = NULL;
2355     switch ( elem.GetType() )
2356     {
2357     case netgen::TET:
2358       vol = meshDS->AddVolume(nodes[0],nodes[1],nodes[2],nodes[3]);
2359       break;
2360     case netgen::TET10:
2361       nodes[4] = mediumNode( nodes[0],nodes[1],nodes[4], quadHelper );
2362       nodes[7] = mediumNode( nodes[1],nodes[2],nodes[7], quadHelper );
2363       nodes[5] = mediumNode( nodes[2],nodes[0],nodes[5], quadHelper );
2364       nodes[6] = mediumNode( nodes[0],nodes[3],nodes[6], quadHelper );
2365       nodes[8] = mediumNode( nodes[1],nodes[3],nodes[8], quadHelper );
2366       nodes[9] = mediumNode( nodes[2],nodes[3],nodes[9], quadHelper );
2367       vol = meshDS->AddVolume(nodes[0],nodes[1],nodes[2],nodes[3],
2368                               nodes[4],nodes[7],nodes[5],nodes[6],nodes[8],nodes[9]);
2369       break;
2370     default:
2371       MESSAGE("NETGEN created a volume of unexpected type, ignoring");
2372       continue;
2373     }
2374     if (!vol)
2375     {
2376       if ( comment.empty() ) comment << "Cannot create a mesh volume";
2377       MESSAGE("Cannot create a mesh volume");
2378       nbSeg = nbFac = nbVol = 0;
2379       break;
2380     }
2381     if (!aSolid.IsNull())
2382       meshDS->SetMeshElementOnShape(vol, aSolid);
2383   }
2384   return comment.empty() ? 0 : 1;
2385 }
2386
2387 namespace
2388 {
2389   //================================================================================
2390   /*!
2391    * \brief Convert error into text
2392    */
2393   //================================================================================
2394
2395   std::string text(int err)
2396   {
2397     if ( !err )
2398       return string("");
2399     return
2400       SMESH_Comment("Error in netgen::OCCGenerateMesh() at ") << netgen::multithread.task;
2401   }
2402
2403   //================================================================================
2404   /*!
2405    * \brief Convert exception into text
2406    */
2407   //================================================================================
2408
2409   std::string text(Standard_Failure& ex)
2410   {
2411     SMESH_Comment str("Exception in netgen::OCCGenerateMesh()");
2412     str << " at " << netgen::multithread.task
2413         << ": " << ex.DynamicType()->Name();
2414     if ( ex.GetMessageString() && strlen( ex.GetMessageString() ))
2415       str << ": " << ex.GetMessageString();
2416     return str;
2417   }
2418   //================================================================================
2419   /*!
2420    * \brief Convert exception into text
2421    */
2422   //================================================================================
2423
2424   std::string text(netgen::NgException& ex)
2425   {
2426     SMESH_Comment str("NgException");
2427     if ( strlen( netgen::multithread.task ) > 0 )
2428       str << " at " << netgen::multithread.task;
2429     str << ": " << ex.What();
2430     return str;
2431   }
2432
2433   //================================================================================
2434   /*!
2435    * \brief Looks for triangles lying on a SOLID
2436    */
2437   //================================================================================
2438
2439   bool hasBadElemOnSolid( const list<const SMDS_MeshElement*>& elems,
2440                           SMESH_subMesh*                       solidSM )
2441   {
2442     TopTools_IndexedMapOfShape solidSubs;
2443     TopExp::MapShapes( solidSM->GetSubShape(), solidSubs );
2444     SMESHDS_Mesh* mesh = solidSM->GetFather()->GetMeshDS();
2445
2446     list<const SMDS_MeshElement*>::const_iterator e = elems.begin();
2447     for ( ; e != elems.end(); ++e )
2448     {
2449       const SMDS_MeshElement* elem = *e;
2450       // if ( elem->GetType() != SMDSAbs_Face ) -- 23047
2451       //   continue;
2452       int nbNodesOnSolid = 0, nbNodes = elem->NbNodes();
2453       SMDS_NodeIteratorPtr nIt = elem->nodeIterator();
2454       while ( nIt->more() )
2455       {
2456         const SMDS_MeshNode* n = nIt->next();
2457         const TopoDS_Shape&  s = mesh->IndexToShape( n->getshapeId() );
2458         nbNodesOnSolid += ( !s.IsNull() && solidSubs.Contains( s ));
2459         if ( nbNodesOnSolid > 2 ||
2460              nbNodesOnSolid == nbNodes)
2461           return true;
2462       }
2463     }
2464     return false;
2465   }
2466
2467   const double edgeMeshingTime = 0.001;
2468   const double faceMeshingTime = 0.019;
2469   const double edgeFaceMeshingTime = edgeMeshingTime + faceMeshingTime;
2470   const double faceOptimizTime = 0.06;
2471   const double voluMeshingTime = 0.15;
2472   const double volOptimizeTime = 0.77;
2473 }
2474
2475 //=============================================================================
2476 /*!
2477  * Here we are going to use the NETGEN mesher
2478  */
2479 //=============================================================================
2480
2481 bool NETGENPlugin_Mesher::Compute()
2482 {
2483   NETGENPlugin_NetgenLibWrapper ngLib;
2484
2485   netgen::MeshingParameters& mparams = netgen::mparam;
2486
2487   SMESH_ComputeErrorPtr error = SMESH_ComputeError::New();
2488   SMESH_MesherHelper quadHelper( *_mesh );
2489   quadHelper.SetIsQuadratic( mparams.secondorder );
2490
2491   static string debugFile = "/tmp/ngMesh.py"; /* to call toPython( _ngMesh, debugFile )
2492                                                  while debugging netgen */
2493   // -------------------------
2494   // Prepare OCC geometry
2495   // -------------------------
2496
2497   netgen::OCCGeometry occgeo;
2498   list< SMESH_subMesh* > meshedSM[3]; // for 0-2 dimensions
2499   NETGENPlugin_Internals internals( *_mesh, _shape, _isVolume );
2500   PrepareOCCgeometry( occgeo, _shape, *_mesh, meshedSM, &internals );
2501   _occgeom = &occgeo;
2502
2503   _totalTime = edgeFaceMeshingTime;
2504   if ( _optimize )
2505     _totalTime += faceOptimizTime;
2506   if ( _isVolume )
2507     _totalTime += voluMeshingTime + ( _optimize ? volOptimizeTime : 0 );
2508   double doneTime = 0;
2509   _ticTime = -1;
2510   _progressTic = 1;
2511   _curShapeIndex = -1;
2512
2513   // -------------------------
2514   // Generate the mesh
2515   // -------------------------
2516
2517   _ngMesh = NULL;
2518   NETGENPlugin_ngMeshInfo initState; // it remembers size of ng mesh equal to size of Smesh
2519
2520   SMESH_Comment comment;
2521   int err = 0;
2522
2523   // vector of nodes in which node index == netgen ID
2524   vector< const SMDS_MeshNode* > nodeVec;
2525   
2526   {
2527     // ----------------
2528     // compute 1D mesh
2529     // ----------------
2530     if ( _simpleHyp )
2531     {
2532       // not to RestrictLocalH() according to curvature during MESHCONST_ANALYSE
2533       mparams.uselocalh = false;
2534       mparams.grading = 0.8; // not limitited size growth
2535
2536       if ( _simpleHyp->GetNumberOfSegments() )
2537         // nb of segments
2538         mparams.maxh = occgeo.boundingbox.Diam();
2539       else
2540         // segment length
2541         mparams.maxh = _simpleHyp->GetLocalLength();
2542     }
2543
2544     if ( mparams.maxh == 0.0 )
2545       mparams.maxh = occgeo.boundingbox.Diam();
2546     if ( _simpleHyp || ( mparams.minh == 0.0 && _fineness != NETGENPlugin_Hypothesis::UserDefined))
2547       mparams.minh = GetDefaultMinSize( _shape, mparams.maxh );
2548
2549     // Local size on faces
2550     occgeo.face_maxh = mparams.maxh;
2551
2552     // Let netgen create _ngMesh and calculate element size on not meshed shapes
2553 #ifndef NETGEN_V5
2554     char *optstr = 0;
2555 #endif
2556     int startWith = netgen::MESHCONST_ANALYSE;
2557     int endWith   = netgen::MESHCONST_ANALYSE;
2558     try
2559     {
2560       OCC_CATCH_SIGNALS;
2561 #ifdef NETGEN_V5
2562       err = netgen::OCCGenerateMesh(occgeo, _ngMesh, mparams, startWith, endWith);
2563 #else
2564       err = netgen::OCCGenerateMesh(occgeo, _ngMesh, startWith, endWith, optstr);
2565 #endif
2566       if(netgen::multithread.terminate)
2567         return false;
2568
2569       comment << text(err);
2570     }
2571     catch (Standard_Failure& ex)
2572     {
2573       comment << text(ex);
2574     }
2575     catch (netgen::NgException & ex)
2576     {
2577       comment << text(ex);
2578       if ( mparams.meshsizefilename )
2579         throw SMESH_ComputeError(COMPERR_BAD_PARMETERS, comment );
2580     }
2581     err = 0; //- MESHCONST_ANALYSE isn't so important step
2582     if ( !_ngMesh )
2583       return false;
2584     ngLib.setMesh(( Ng_Mesh*) _ngMesh );
2585
2586     _ngMesh->ClearFaceDescriptors(); // we make descriptors our-self
2587
2588     if ( !mparams.uselocalh ) // mparams.grading is not taken into account yet
2589       _ngMesh->LocalHFunction().SetGrading( mparams.grading );
2590
2591     if ( _simpleHyp )
2592     {
2593       // Pass 1D simple parameters to NETGEN
2594       // --------------------------------
2595       int      nbSeg = _simpleHyp->GetNumberOfSegments();
2596       double segSize = _simpleHyp->GetLocalLength();
2597       for ( int iE = 1; iE <= occgeo.emap.Extent(); ++iE )
2598       {
2599         const TopoDS_Edge& e = TopoDS::Edge( occgeo.emap(iE));
2600         if ( nbSeg )
2601           segSize = SMESH_Algo::EdgeLength( e ) / ( nbSeg - 0.4 );
2602         setLocalSize( e, segSize, *_ngMesh );
2603       }
2604     }
2605     else // if ( ! _simpleHyp )
2606     {
2607       // Local size on shapes
2608       SetLocalSize( occgeo, *_ngMesh );
2609     }
2610
2611     // Precompute internal edges (issue 0020676) in order to
2612     // add mesh on them correctly (twice) to netgen mesh
2613     if ( !err && internals.hasInternalEdges() )
2614     {
2615       // load internal shapes into OCCGeometry
2616       netgen::OCCGeometry intOccgeo;
2617       internals.getInternalEdges( intOccgeo.fmap, intOccgeo.emap, intOccgeo.vmap, meshedSM );
2618       intOccgeo.boundingbox = occgeo.boundingbox;
2619       intOccgeo.shape = occgeo.shape;
2620       intOccgeo.face_maxh.SetSize(intOccgeo.fmap.Extent());
2621       intOccgeo.face_maxh = netgen::mparam.maxh;
2622       netgen::Mesh *tmpNgMesh = NULL;
2623       try
2624       {
2625         OCC_CATCH_SIGNALS;
2626         // compute local H on internal shapes in the main mesh
2627         //OCCSetLocalMeshSize(intOccgeo, *_ngMesh); it deletes _ngMesh->localH
2628
2629         // let netgen create a temporary mesh
2630 #ifdef NETGEN_V5
2631         netgen::OCCGenerateMesh(intOccgeo, tmpNgMesh, mparams, startWith, endWith);
2632 #else
2633         netgen::OCCGenerateMesh(intOccgeo, tmpNgMesh, startWith, endWith, optstr);
2634 #endif
2635         if(netgen::multithread.terminate)
2636           return false;
2637
2638         // copy LocalH from the main to temporary mesh
2639         initState.transferLocalH( _ngMesh, tmpNgMesh );
2640
2641         // compute mesh on internal edges
2642         startWith = endWith = netgen::MESHCONST_MESHEDGES;
2643 #ifdef NETGEN_V5
2644         err = netgen::OCCGenerateMesh(intOccgeo, tmpNgMesh, mparams, startWith, endWith);
2645 #else
2646         err = netgen::OCCGenerateMesh(intOccgeo, tmpNgMesh, startWith, endWith, optstr);
2647 #endif
2648         comment << text(err);
2649       }
2650       catch (Standard_Failure& ex)
2651       {
2652         comment << text(ex);
2653         err = 1;
2654       }
2655       initState.restoreLocalH( tmpNgMesh );
2656
2657       // fill SMESH by netgen mesh
2658       vector< const SMDS_MeshNode* > tmpNodeVec;
2659       FillSMesh( intOccgeo, *tmpNgMesh, initState, *_mesh, tmpNodeVec, comment );
2660       err = ( err || !comment.empty() );
2661
2662       nglib::Ng_DeleteMesh((nglib::Ng_Mesh*)tmpNgMesh);
2663     }
2664
2665     // Fill _ngMesh with nodes and segments of computed submeshes
2666     if ( !err )
2667     {
2668       err = ! ( FillNgMesh(occgeo, *_ngMesh, nodeVec, meshedSM[ MeshDim_0D ]) &&
2669                 FillNgMesh(occgeo, *_ngMesh, nodeVec, meshedSM[ MeshDim_1D ], &quadHelper));
2670     }
2671     initState = NETGENPlugin_ngMeshInfo(_ngMesh);
2672
2673     // Compute 1d mesh
2674     if (!err)
2675     {
2676       startWith = endWith = netgen::MESHCONST_MESHEDGES;
2677       try
2678       {
2679         OCC_CATCH_SIGNALS;
2680 #ifdef NETGEN_V5
2681         err = netgen::OCCGenerateMesh(occgeo, _ngMesh, mparams, startWith, endWith);
2682 #else
2683         err = netgen::OCCGenerateMesh(occgeo, _ngMesh, startWith, endWith, optstr);
2684 #endif
2685         if(netgen::multithread.terminate)
2686           return false;
2687
2688         comment << text(err);
2689       }
2690       catch (Standard_Failure& ex)
2691       {
2692         comment << text(ex);
2693         err = 1;
2694       }
2695     }
2696     if ( _isVolume )
2697       _ticTime = ( doneTime += edgeMeshingTime ) / _totalTime / _progressTic;
2698
2699     mparams.uselocalh = true; // restore as it is used at surface optimization
2700
2701     // ---------------------
2702     // compute surface mesh
2703     // ---------------------
2704     if (!err)
2705     {
2706       // Pass 2D simple parameters to NETGEN
2707       if ( _simpleHyp ) {
2708         if ( double area = _simpleHyp->GetMaxElementArea() ) {
2709           // face area
2710           mparams.maxh = sqrt(2. * area/sqrt(3.0));
2711           mparams.grading = 0.4; // moderate size growth
2712         }
2713         else {
2714           // length from edges
2715           if ( _ngMesh->GetNSeg() ) {
2716             double edgeLength = 0;
2717             TopTools_MapOfShape visitedEdges;
2718             for ( TopExp_Explorer exp( _shape, TopAbs_EDGE ); exp.More(); exp.Next() )
2719               if( visitedEdges.Add(exp.Current()) )
2720                 edgeLength += SMESH_Algo::EdgeLength( TopoDS::Edge( exp.Current() ));
2721             // we have to multiply length by 2 since for each TopoDS_Edge there
2722             // are double set of NETGEN edges, in other words, we have to
2723             // divide _ngMesh->GetNSeg() by 2.
2724             mparams.maxh = 2*edgeLength / _ngMesh->GetNSeg();
2725           }
2726           else {
2727             mparams.maxh = 1000;
2728           }
2729           mparams.grading = 0.2; // slow size growth
2730         }
2731         mparams.quad = _simpleHyp->GetAllowQuadrangles();
2732         mparams.maxh = min( mparams.maxh, occgeo.boundingbox.Diam()/2 );
2733         _ngMesh->SetGlobalH (mparams.maxh);
2734         netgen::Box<3> bb = occgeo.GetBoundingBox();
2735         bb.Increase (bb.Diam()/20);
2736         _ngMesh->SetLocalH (bb.PMin(), bb.PMax(), mparams.grading);
2737       }
2738
2739       // Care of vertices internal in faces (issue 0020676)
2740       if ( internals.hasInternalVertexInFace() )
2741       {
2742         // store computed segments in SMESH in order not to create SMESH
2743         // edges for ng segments added by AddIntVerticesInFaces()
2744         FillSMesh( occgeo, *_ngMesh, initState, *_mesh, nodeVec, comment );
2745         // add segments to faces with internal vertices
2746         AddIntVerticesInFaces( occgeo, *_ngMesh, nodeVec, internals );
2747         initState = NETGENPlugin_ngMeshInfo(_ngMesh);
2748       }
2749
2750       // Build viscous layers
2751       if ( _isViscousLayers2D ||
2752            StdMeshers_ViscousLayers2D::HasProxyMesh( TopoDS::Face( occgeo.fmap(1) ), *_mesh ))
2753       {
2754         if ( !internals.hasInternalVertexInFace() ) {
2755           FillSMesh( occgeo, *_ngMesh, initState, *_mesh, nodeVec, comment );
2756           initState = NETGENPlugin_ngMeshInfo(_ngMesh);
2757         }
2758         SMESH_ProxyMesh::Ptr viscousMesh;
2759         SMESH_MesherHelper   helper( *_mesh );
2760         for ( int faceID = 1; faceID <= occgeo.fmap.Extent(); ++faceID )
2761         {
2762           const TopoDS_Face& F = TopoDS::Face( occgeo.fmap( faceID ));
2763           viscousMesh = StdMeshers_ViscousLayers2D::Compute( *_mesh, F );
2764           if ( !viscousMesh )
2765             return false;
2766           if ( viscousMesh->NbProxySubMeshes() == 0 )
2767             continue;
2768           // exclude from computation ng segments built on EDGEs of F
2769           for (int i = 1; i <= _ngMesh->GetNSeg(); i++)
2770           {
2771             netgen::Segment & seg = _ngMesh->LineSegment(i);
2772             if (seg.si == faceID)
2773               seg.si = 0;
2774           }
2775           // add new segments to _ngMesh instead of excluded ones
2776           helper.SetSubShape( F );
2777           TSideVector wires =
2778             StdMeshers_FaceSide::GetFaceWires( F, *_mesh, /*skipMediumNodes=*/true,
2779                                                error, viscousMesh );
2780           error = AddSegmentsToMesh( *_ngMesh, occgeo, wires, helper, nodeVec );
2781
2782           if ( !error ) error = SMESH_ComputeError::New();
2783         }
2784         initState = NETGENPlugin_ngMeshInfo(_ngMesh);
2785       }
2786
2787       // Let netgen compute 2D mesh
2788       startWith = netgen::MESHCONST_MESHSURFACE;
2789       endWith = _optimize ? netgen::MESHCONST_OPTSURFACE : netgen::MESHCONST_MESHSURFACE;
2790       try
2791       {
2792         OCC_CATCH_SIGNALS;
2793 #ifdef NETGEN_V5
2794         err = netgen::OCCGenerateMesh(occgeo, _ngMesh, mparams, startWith, endWith);
2795 #else
2796         err = netgen::OCCGenerateMesh(occgeo, _ngMesh, startWith, endWith, optstr);
2797 #endif
2798         if(netgen::multithread.terminate)
2799           return false;
2800
2801         comment << text (err);
2802       }
2803       catch (Standard_Failure& ex)
2804       {
2805         comment << text(ex);
2806         //err = 1; -- try to make volumes anyway
2807       }
2808       catch (netgen::NgException exc)
2809       {
2810         comment << text(exc);
2811         //err = 1; -- try to make volumes anyway
2812       }
2813     }
2814     if ( _isVolume )
2815     {
2816       doneTime += faceMeshingTime + ( _optimize ? faceOptimizTime : 0 );
2817       _ticTime = doneTime / _totalTime / _progressTic;
2818     }
2819     // ---------------------
2820     // generate volume mesh
2821     // ---------------------
2822     // Fill _ngMesh with nodes and faces of computed 2D submeshes
2823     if ( !err && _isVolume && ( !meshedSM[ MeshDim_2D ].empty() || mparams.quad ))
2824     {
2825       // load SMESH with computed segments and faces
2826       FillSMesh( occgeo, *_ngMesh, initState, *_mesh, nodeVec, comment, &quadHelper );
2827
2828       // compute pyramids on quadrangles
2829       SMESH_ProxyMesh::Ptr proxyMesh;
2830       if ( _mesh->NbQuadrangles() > 0 )
2831         for ( int iS = 1; iS <= occgeo.somap.Extent(); ++iS )
2832         {
2833           StdMeshers_QuadToTriaAdaptor* Adaptor = new StdMeshers_QuadToTriaAdaptor;
2834           proxyMesh.reset( Adaptor );
2835
2836           int nbPyrams = _mesh->NbPyramids();
2837           Adaptor->Compute( *_mesh, occgeo.somap(iS) );
2838           if ( nbPyrams != _mesh->NbPyramids() )
2839           {
2840             list< SMESH_subMesh* > quadFaceSM;
2841             for (TopExp_Explorer face(occgeo.somap(iS), TopAbs_FACE); face.More(); face.Next())
2842               if ( Adaptor->GetProxySubMesh( face.Current() ))
2843               {
2844                 quadFaceSM.push_back( _mesh->GetSubMesh( face.Current() ));
2845                 meshedSM[ MeshDim_2D ].remove( quadFaceSM.back() );
2846               }
2847             FillNgMesh(occgeo, *_ngMesh, nodeVec, quadFaceSM, &quadHelper, proxyMesh);
2848           }
2849         }
2850       // fill _ngMesh with faces of sub-meshes
2851       err = ! ( FillNgMesh(occgeo, *_ngMesh, nodeVec, meshedSM[ MeshDim_2D ], &quadHelper));
2852       initState = NETGENPlugin_ngMeshInfo(_ngMesh);
2853       //toPython( _ngMesh, "/tmp/ngPython.py");
2854     }
2855     if (!err && _isVolume)
2856     {
2857       // Pass 3D simple parameters to NETGEN
2858       const NETGENPlugin_SimpleHypothesis_3D* simple3d =
2859         dynamic_cast< const NETGENPlugin_SimpleHypothesis_3D* > ( _simpleHyp );
2860       if ( simple3d ) {
2861         if ( double vol = simple3d->GetMaxElementVolume() ) {
2862           // max volume
2863           mparams.maxh = pow( 72, 1/6. ) * pow( vol, 1/3. );
2864           mparams.maxh = min( mparams.maxh, occgeo.boundingbox.Diam()/2 );
2865         }
2866         else {
2867           // length from faces
2868           mparams.maxh = _ngMesh->AverageH();
2869         }
2870         _ngMesh->SetGlobalH (mparams.maxh);
2871         mparams.grading = 0.4;
2872 #ifdef NETGEN_V5
2873         _ngMesh->CalcLocalH(mparams.grading);
2874 #else
2875         _ngMesh->CalcLocalH();
2876 #endif
2877       }
2878       // Care of vertices internal in solids and internal faces (issue 0020676)
2879       if ( internals.hasInternalVertexInSolid() || internals.hasInternalFaces() )
2880       {
2881         // store computed faces in SMESH in order not to create SMESH
2882         // faces for ng faces added here
2883         FillSMesh( occgeo, *_ngMesh, initState, *_mesh, nodeVec, comment, &quadHelper );
2884         // add ng faces to solids with internal vertices
2885         AddIntVerticesInSolids( occgeo, *_ngMesh, nodeVec, internals );
2886         // duplicate mesh faces on internal faces
2887         FixIntFaces( occgeo, *_ngMesh, internals );
2888         initState = NETGENPlugin_ngMeshInfo(_ngMesh);
2889       }
2890       // Let netgen compute 3D mesh
2891       startWith = endWith = netgen::MESHCONST_MESHVOLUME;
2892       try
2893       {
2894         OCC_CATCH_SIGNALS;
2895 #ifdef NETGEN_V5
2896         err = netgen::OCCGenerateMesh(occgeo, _ngMesh, mparams, startWith, endWith);
2897 #else
2898         err = netgen::OCCGenerateMesh(occgeo, _ngMesh, startWith, endWith, optstr);
2899 #endif
2900         if(netgen::multithread.terminate)
2901           return false;
2902
2903         if ( comment.empty() ) // do not overwrite a previos error
2904           comment << text(err);
2905       }
2906       catch (Standard_Failure& ex)
2907       {
2908         if ( comment.empty() ) // do not overwrite a previos error
2909           comment << text(ex);
2910         err = 1;
2911       }
2912       catch (netgen::NgException exc)
2913       {
2914         if ( comment.empty() ) // do not overwrite a previos error
2915           comment << text(exc);
2916         err = 1;
2917       }
2918       _ticTime = ( doneTime += voluMeshingTime ) / _totalTime / _progressTic;
2919
2920       // Let netgen optimize 3D mesh
2921       if ( !err && _optimize )
2922       {
2923         startWith = endWith = netgen::MESHCONST_OPTVOLUME;
2924         try
2925         {
2926           OCC_CATCH_SIGNALS;
2927 #ifdef NETGEN_V5
2928           err = netgen::OCCGenerateMesh(occgeo, _ngMesh, mparams, startWith, endWith);
2929 #else
2930           err = netgen::OCCGenerateMesh(occgeo, _ngMesh, startWith, endWith, optstr);
2931 #endif
2932           if(netgen::multithread.terminate)
2933             return false;
2934
2935           if ( comment.empty() ) // do not overwrite a previos error
2936             comment << text(err);
2937         }
2938         catch (Standard_Failure& ex)
2939         {
2940           if ( comment.empty() ) // do not overwrite a previos error
2941             comment << text(ex);
2942         }
2943         catch (netgen::NgException exc)
2944         {
2945           if ( comment.empty() ) // do not overwrite a previos error
2946             comment << text(exc);
2947         }
2948       }
2949     }
2950     if (!err && mparams.secondorder > 0)
2951     {
2952       try
2953       {
2954         OCC_CATCH_SIGNALS;
2955         if ( !meshedSM[ MeshDim_1D ].empty() )
2956         {
2957           // remove segments not attached to geometry (IPAL0052479)
2958           for (int i = 1; i <= _ngMesh->GetNSeg(); ++i)
2959           {
2960             const netgen::Segment & seg = _ngMesh->LineSegment (i);
2961             if ( seg.epgeominfo[ 0 ].edgenr == 0 )
2962               _ngMesh->DeleteSegment( i );
2963           }
2964           _ngMesh->Compress();
2965         }
2966         // convert to quadratic
2967         netgen::OCCRefinementSurfaces ref (occgeo);
2968         ref.MakeSecondOrder (*_ngMesh);
2969
2970         // care of elements already loaded to SMESH
2971         // if ( initState._nbSegments > 0 )
2972         //   makeQuadratic( occgeo.emap, _mesh );
2973         // if ( initState._nbFaces > 0 )
2974         //   makeQuadratic( occgeo.fmap, _mesh );
2975       }
2976       catch (Standard_Failure& ex)
2977       {
2978         if ( comment.empty() ) // do not overwrite a previos error
2979           comment << "Exception in netgen at passing to 2nd order ";
2980       }
2981       catch (netgen::NgException exc)
2982       {
2983         if ( comment.empty() ) // do not overwrite a previos error
2984           comment << exc.What();
2985       }
2986     }
2987   }
2988
2989   _ticTime = 0.98 / _progressTic;
2990
2991   //int nbNod = _ngMesh->GetNP();
2992   //int nbSeg = _ngMesh->GetNSeg();
2993   int nbFac = _ngMesh->GetNSE();
2994   int nbVol = _ngMesh->GetNE();
2995   bool isOK = ( !err && (_isVolume ? (nbVol > 0) : (nbFac > 0)) );
2996
2997   // Feed back the SMESHDS with the generated Nodes and Elements
2998   if ( true /*isOK*/ ) // get whatever built
2999   {
3000     FillSMesh( occgeo, *_ngMesh, initState, *_mesh, nodeVec, comment, &quadHelper );
3001
3002     if ( quadHelper.GetIsQuadratic() ) // remove free nodes
3003       for ( size_t i = 0; i < nodeVec.size(); ++i )
3004         if ( nodeVec[i] && nodeVec[i]->NbInverseElements() == 0 )
3005           _mesh->GetMeshDS()->RemoveFreeNode( nodeVec[i], 0, /*fromGroups=*/false );
3006   }
3007   SMESH_ComputeErrorPtr readErr = ReadErrors(nodeVec);
3008   if ( readErr && !readErr->myBadElements.empty() )
3009   {
3010     error = readErr;
3011     if ( !comment.empty() && !readErr->myComment.empty() ) comment += "\n";
3012     comment += readErr->myComment;
3013   }
3014   if ( error->IsOK() && ( !isOK || comment.size() > 0 ))
3015     error->myName = COMPERR_ALGO_FAILED;
3016   if ( !comment.empty() )
3017     error->myComment = comment;
3018
3019   // SetIsAlwaysComputed( true ) to empty sub-meshes, which
3020   // appear if the geometry contains coincident sub-shape due
3021   // to bool merge_solids = 1; in netgen/libsrc/occ/occgenmesh.cpp
3022   const int nbMaps = 2;
3023   const TopTools_IndexedMapOfShape* geoMaps[nbMaps] =
3024     { & occgeo.vmap, & occgeo.emap/*, & occgeo.fmap*/ };
3025   for ( int iMap = 0; iMap < nbMaps; ++iMap )
3026     for (int i = 1; i <= geoMaps[iMap]->Extent(); i++)
3027       if ( SMESH_subMesh* sm = _mesh->GetSubMeshContaining( geoMaps[iMap]->FindKey(i)))
3028         if ( !sm->IsMeshComputed() )
3029           sm->SetIsAlwaysComputed( true );
3030
3031   // set bad compute error to subshapes of all failed sub-shapes
3032   if ( !error->IsOK() )
3033   {
3034     bool pb2D = false, pb3D = false;
3035     for (int i = 1; i <= occgeo.fmap.Extent(); i++) {
3036       int status = occgeo.facemeshstatus[i-1];
3037       if (status == netgen::FACE_MESHED_OK ) continue;
3038       if ( SMESH_subMesh* sm = _mesh->GetSubMeshContaining( occgeo.fmap( i ))) {
3039         SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
3040         if ( !smError || smError->IsOK() ) {
3041           if ( status == netgen::FACE_FAILED )
3042             smError.reset( new SMESH_ComputeError( *error ));
3043           else
3044             smError.reset( new SMESH_ComputeError( COMPERR_ALGO_FAILED, "Ignored" ));
3045           if ( SMESH_Algo::GetMeshError( sm ) == SMESH_Algo::MEr_OK )
3046             smError->myName = COMPERR_WARNING;
3047         }
3048         pb2D = pb2D || smError->IsKO();
3049       }
3050     }
3051     if ( !pb2D ) // all faces are OK
3052       for (int i = 1; i <= occgeo.somap.Extent(); i++)
3053         if ( SMESH_subMesh* sm = _mesh->GetSubMeshContaining( occgeo.somap( i )))
3054         {
3055           bool smComputed = nbVol && !sm->IsEmpty();
3056           if ( smComputed && internals.hasInternalVertexInSolid( sm->GetId() ))
3057           {
3058             int nbIntV = internals.getSolidsWithVertices().find( sm->GetId() )->second.size();
3059             SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
3060             smComputed = ( smDS->NbElements() > 0 || smDS->NbNodes() > nbIntV );
3061           }
3062           SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
3063           if ( !smComputed && ( !smError || smError->IsOK() ))
3064           {
3065             smError.reset( new SMESH_ComputeError( *error ));
3066             if ( nbVol && SMESH_Algo::GetMeshError( sm ) == SMESH_Algo::MEr_OK )
3067             {
3068               smError->myName = COMPERR_WARNING;
3069             }
3070             else if ( !smError->myBadElements.empty() ) // bad surface mesh
3071             {
3072               if ( !hasBadElemOnSolid( smError->myBadElements, sm ))
3073                 smError.reset();
3074             }
3075           }
3076           pb3D = pb3D || ( smError && smError->IsKO() );
3077         }
3078     if ( !pb2D && !pb3D )
3079       err = 0; // no fatal errors, only warnings
3080   }
3081
3082   ngLib._isComputeOk = !err;
3083
3084   return !err;
3085 }
3086
3087 //=============================================================================
3088 /*!
3089  * Evaluate
3090  */
3091 //=============================================================================
3092 bool NETGENPlugin_Mesher::Evaluate(MapShapeNbElems& aResMap)
3093 {
3094   netgen::MeshingParameters& mparams = netgen::mparam;
3095
3096
3097   // -------------------------
3098   // Prepare OCC geometry
3099   // -------------------------
3100   netgen::OCCGeometry occgeo;
3101   list< SMESH_subMesh* > meshedSM[4]; // for 0-3 dimensions
3102   NETGENPlugin_Internals internals( *_mesh, _shape, _isVolume );
3103   PrepareOCCgeometry( occgeo, _shape, *_mesh, meshedSM, &internals );
3104
3105   bool tooManyElems = false;
3106   const int hugeNb = std::numeric_limits<int>::max() / 100;
3107
3108   // ----------------
3109   // evaluate 1D 
3110   // ----------------
3111   // pass 1D simple parameters to NETGEN
3112   if ( _simpleHyp )
3113   {
3114     // not to RestrictLocalH() according to curvature during MESHCONST_ANALYSE
3115     mparams.uselocalh = false;
3116     mparams.grading = 0.8; // not limitited size growth
3117
3118     if ( _simpleHyp->GetNumberOfSegments() )
3119       // nb of segments
3120       mparams.maxh = occgeo.boundingbox.Diam();
3121     else
3122       // segment length
3123       mparams.maxh = _simpleHyp->GetLocalLength();
3124   }
3125
3126   if ( mparams.maxh == 0.0 )
3127     mparams.maxh = occgeo.boundingbox.Diam();
3128   if ( _simpleHyp || ( mparams.minh == 0.0 && _fineness != NETGENPlugin_Hypothesis::UserDefined))
3129     mparams.minh = GetDefaultMinSize( _shape, mparams.maxh );
3130
3131   // let netgen create _ngMesh and calculate element size on not meshed shapes
3132   NETGENPlugin_NetgenLibWrapper ngLib;
3133   netgen::Mesh *ngMesh = NULL;
3134 #ifndef NETGEN_V5
3135   char *optstr = 0;
3136 #endif
3137   int startWith = netgen::MESHCONST_ANALYSE;
3138   int endWith   = netgen::MESHCONST_MESHEDGES;
3139 #ifdef NETGEN_V5
3140   int err = netgen::OCCGenerateMesh(occgeo, ngMesh, mparams, startWith, endWith);
3141 #else
3142   int err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
3143 #endif
3144
3145   if(netgen::multithread.terminate)
3146     return false;
3147
3148   ngLib.setMesh(( Ng_Mesh*) ngMesh );
3149   if (err) {
3150     if ( SMESH_subMesh* sm = _mesh->GetSubMeshContaining( _shape ))
3151       sm->GetComputeError().reset( new SMESH_ComputeError( COMPERR_ALGO_FAILED ));
3152     return false;
3153   }
3154   if ( _simpleHyp )
3155   {
3156     // Pass 1D simple parameters to NETGEN
3157     // --------------------------------
3158     int      nbSeg = _simpleHyp->GetNumberOfSegments();
3159     double segSize = _simpleHyp->GetLocalLength();
3160     for ( int iE = 1; iE <= occgeo.emap.Extent(); ++iE )
3161     {
3162       const TopoDS_Edge& e = TopoDS::Edge( occgeo.emap(iE));
3163       if ( nbSeg )
3164         segSize = SMESH_Algo::EdgeLength( e ) / ( nbSeg - 0.4 );
3165       setLocalSize( e, segSize, *ngMesh );
3166     }
3167   }
3168   else // if ( ! _simpleHyp )
3169   {
3170     // Local size on shapes
3171     SetLocalSize( occgeo, *ngMesh );
3172   }
3173   // calculate total nb of segments and length of edges
3174   double fullLen = 0.0;
3175   int fullNbSeg = 0;
3176   int entity = mparams.secondorder > 0 ? SMDSEntity_Quad_Edge : SMDSEntity_Edge;
3177   TopTools_DataMapOfShapeInteger Edge2NbSeg;
3178   for (TopExp_Explorer exp(_shape, TopAbs_EDGE); exp.More(); exp.Next())
3179   {
3180     TopoDS_Edge E = TopoDS::Edge( exp.Current() );
3181     if( !Edge2NbSeg.Bind(E,0) )
3182       continue;
3183
3184     double aLen = SMESH_Algo::EdgeLength(E);
3185     fullLen += aLen;
3186
3187     vector<int>& aVec = aResMap[_mesh->GetSubMesh(E)];
3188     if ( aVec.empty() )
3189       aVec.resize( SMDSEntity_Last, 0);
3190     else
3191       fullNbSeg += aVec[ entity ];
3192   }
3193
3194   // store nb of segments computed by Netgen
3195   NCollection_Map<Link> linkMap;
3196   for (int i = 1; i <= ngMesh->GetNSeg(); ++i )
3197   {
3198     const netgen::Segment& seg = ngMesh->LineSegment(i);
3199     Link link(seg[0], seg[1]);
3200     if ( !linkMap.Add( link )) continue;
3201     int aGeomEdgeInd = seg.epgeominfo[0].edgenr;
3202     if (aGeomEdgeInd > 0 && aGeomEdgeInd <= occgeo.emap.Extent())
3203     {
3204       vector<int>& aVec = aResMap[_mesh->GetSubMesh(occgeo.emap(aGeomEdgeInd))];
3205       aVec[ entity ]++;
3206     }
3207   }
3208   // store nb of nodes on edges computed by Netgen
3209   TopTools_DataMapIteratorOfDataMapOfShapeInteger Edge2NbSegIt(Edge2NbSeg);
3210   for (; Edge2NbSegIt.More(); Edge2NbSegIt.Next())
3211   {
3212     vector<int>& aVec = aResMap[_mesh->GetSubMesh(Edge2NbSegIt.Key())];
3213     if ( aVec[ entity ] > 1 && aVec[ SMDSEntity_Node ] == 0 )
3214       aVec[SMDSEntity_Node] = mparams.secondorder > 0  ? 2*aVec[ entity ]-1 : aVec[ entity ]-1;
3215
3216     fullNbSeg += aVec[ entity ];
3217     Edge2NbSeg( Edge2NbSegIt.Key() ) = aVec[ entity ];
3218   }
3219   if ( fullNbSeg == 0 )
3220     return false;
3221
3222   // ----------------
3223   // evaluate 2D 
3224   // ----------------
3225   if ( _simpleHyp ) {
3226     if ( double area = _simpleHyp->GetMaxElementArea() ) {
3227       // face area
3228       mparams.maxh = sqrt(2. * area/sqrt(3.0));
3229       mparams.grading = 0.4; // moderate size growth
3230     }
3231     else {
3232       // length from edges
3233       mparams.maxh = fullLen/fullNbSeg;
3234       mparams.grading = 0.2; // slow size growth
3235     }
3236   }
3237   mparams.maxh = min( mparams.maxh, occgeo.boundingbox.Diam()/2 );
3238   mparams.maxh = min( mparams.maxh, fullLen/fullNbSeg * (1. + mparams.grading));
3239
3240   for (TopExp_Explorer exp(_shape, TopAbs_FACE); exp.More(); exp.Next())
3241   {
3242     TopoDS_Face F = TopoDS::Face( exp.Current() );
3243     SMESH_subMesh *sm = _mesh->GetSubMesh(F);
3244     GProp_GProps G;
3245     BRepGProp::SurfaceProperties(F,G);
3246     double anArea = G.Mass();
3247     tooManyElems = tooManyElems || ( anArea/hugeNb > mparams.maxh*mparams.maxh );
3248     int nb1d = 0;
3249     if ( !tooManyElems )
3250     {
3251       TopTools_MapOfShape egdes;
3252       for (TopExp_Explorer exp1(F,TopAbs_EDGE); exp1.More(); exp1.Next())
3253         if ( egdes.Add( exp1.Current() ))
3254           nb1d += Edge2NbSeg.Find(exp1.Current());
3255     }
3256     int nbFaces = tooManyElems ? hugeNb : int( 4*anArea / (mparams.maxh*mparams.maxh*sqrt(3.)));
3257     int nbNodes = tooManyElems ? hugeNb : (( nbFaces*3 - (nb1d-1)*2 ) / 6 + 1 );
3258
3259     vector<int> aVec(SMDSEntity_Last, 0);
3260     if( mparams.secondorder > 0 ) {
3261       int nb1d_in = (nbFaces*3 - nb1d) / 2;
3262       aVec[SMDSEntity_Node] = nbNodes + nb1d_in;
3263       aVec[SMDSEntity_Quad_Triangle] = nbFaces;
3264     }
3265     else {
3266       aVec[SMDSEntity_Node] = Max ( nbNodes, 0  );
3267       aVec[SMDSEntity_Triangle] = nbFaces;
3268     }
3269     aResMap[sm].swap(aVec);
3270   }
3271
3272   // ----------------
3273   // evaluate 3D
3274   // ----------------
3275   if(_isVolume) {
3276     // pass 3D simple parameters to NETGEN
3277     const NETGENPlugin_SimpleHypothesis_3D* simple3d =
3278       dynamic_cast< const NETGENPlugin_SimpleHypothesis_3D* > ( _simpleHyp );
3279     if ( simple3d ) {
3280       if ( double vol = simple3d->GetMaxElementVolume() ) {
3281         // max volume
3282         mparams.maxh = pow( 72, 1/6. ) * pow( vol, 1/3. );
3283         mparams.maxh = min( mparams.maxh, occgeo.boundingbox.Diam()/2 );
3284       }
3285       else {
3286         // using previous length from faces
3287       }
3288       mparams.grading = 0.4;
3289       mparams.maxh = min( mparams.maxh, fullLen/fullNbSeg * (1. + mparams.grading));
3290     }
3291     GProp_GProps G;
3292     BRepGProp::VolumeProperties(_shape,G);
3293     double aVolume = G.Mass();
3294     double tetrVol = 0.1179*mparams.maxh*mparams.maxh*mparams.maxh;
3295     tooManyElems = tooManyElems || ( aVolume/hugeNb > tetrVol );
3296     int nbVols = tooManyElems ? hugeNb : int(aVolume/tetrVol);
3297     int nb1d_in = int(( nbVols*6 - fullNbSeg ) / 6 );
3298     vector<int> aVec(SMDSEntity_Last, 0 );
3299     if ( tooManyElems ) // avoid FPE
3300     {
3301       aVec[SMDSEntity_Node] = hugeNb;
3302       aVec[ mparams.secondorder > 0 ? SMDSEntity_Quad_Tetra : SMDSEntity_Tetra] = hugeNb;
3303     }
3304     else
3305     {
3306       if( mparams.secondorder > 0 ) {
3307         aVec[SMDSEntity_Node] = nb1d_in/3 + 1 + nb1d_in;
3308         aVec[SMDSEntity_Quad_Tetra] = nbVols;
3309       }
3310       else {
3311         aVec[SMDSEntity_Node] = nb1d_in/3 + 1;
3312         aVec[SMDSEntity_Tetra] = nbVols;
3313       }
3314     }
3315     SMESH_subMesh *sm = _mesh->GetSubMesh(_shape);
3316     aResMap[sm].swap(aVec);
3317   }
3318
3319   return true;
3320 }
3321
3322 double NETGENPlugin_Mesher::GetProgress(const SMESH_Algo* holder,
3323                                         const int *       algoProgressTic,
3324                                         const double *    algoProgress) const
3325 {
3326   ((int&) _progressTic ) = *algoProgressTic + 1;
3327
3328   if ( !_occgeom ) return 0;
3329
3330   double progress = -1;
3331   if ( !_isVolume )
3332   {
3333     if ( _ticTime < 0 && netgen::multithread.task[0] == 'O'/*Optimizing surface*/ )
3334     {
3335       ((double&) _ticTime ) = edgeFaceMeshingTime / _totalTime / _progressTic;
3336     }
3337     else if ( !_optimize /*&& _occgeom->fmap.Extent() > 1*/ )
3338     {
3339       int doneShapeIndex = -1;
3340       while ( doneShapeIndex+1 < _occgeom->facemeshstatus.Size() &&
3341               _occgeom->facemeshstatus[ doneShapeIndex+1 ])
3342         doneShapeIndex++;
3343       if ( doneShapeIndex+1 != _curShapeIndex )
3344       {
3345         ((int&) _curShapeIndex) = doneShapeIndex+1;
3346         double    doneShapeRate = _curShapeIndex / double( _occgeom->fmap.Extent() );
3347         double         doneTime = edgeMeshingTime + doneShapeRate * faceMeshingTime;
3348         ((double&)    _ticTime) = doneTime / _totalTime / _progressTic;
3349         // cout << "shape " << _curShapeIndex << " _ticTime " << _ticTime
3350         //      << " " << doneTime / _totalTime / _progressTic << endl;
3351       }
3352     }
3353   }
3354   else if ( !_optimize && _occgeom->somap.Extent() > 1 )
3355   {
3356     int curShapeIndex = _curShapeIndex;
3357     if ( _ngMesh->GetNE() > 0 )
3358     {
3359       netgen::Element el = (*_ngMesh)[netgen::ElementIndex( _ngMesh->GetNE()-1 )];
3360       curShapeIndex = el.GetIndex();
3361     }
3362     if ( curShapeIndex != _curShapeIndex )
3363     {
3364       ((int&) _curShapeIndex) = curShapeIndex;
3365       double    doneShapeRate = _curShapeIndex / double( _occgeom->somap.Extent() );
3366       double         doneTime = edgeFaceMeshingTime + doneShapeRate * voluMeshingTime;
3367       ((double&)    _ticTime) = doneTime / _totalTime / _progressTic;
3368       // cout << "shape " << _curShapeIndex << " _ticTime " << _ticTime
3369       //      << " " << doneTime / _totalTime / _progressTic << endl;
3370     }
3371   }
3372
3373   if ( _ticTime > 0 )
3374     progress  = Max( *algoProgressTic * _ticTime, *algoProgress );
3375
3376   if ( progress > 0 )
3377   {
3378     if ( _isVolume &&
3379          netgen::multithread.task[0] == 'D'/*elaunay meshing*/ &&
3380          progress > voluMeshingTime )
3381     {
3382       progress = voluMeshingTime;
3383       ((double&) _ticTime) = voluMeshingTime / _totalTime / _progressTic;
3384     }
3385     ((int&) *algoProgressTic )++;
3386     ((double&) *algoProgress) = progress;
3387   }
3388   //cout << progress << " "  << *algoProgressTic << " " << netgen::multithread.task << " "<< _ticTime << endl;
3389
3390   return Min( progress, 0.99 );
3391 }
3392
3393 //================================================================================
3394 /*!
3395  * \brief Read mesh entities preventing successful computation from "test.out" file
3396  */
3397 //================================================================================
3398
3399 SMESH_ComputeErrorPtr
3400 NETGENPlugin_Mesher::ReadErrors(const vector<const SMDS_MeshNode* >& nodeVec)
3401 {
3402   SMESH_ComputeErrorPtr err = SMESH_ComputeError::New
3403     (COMPERR_BAD_INPUT_MESH, "Some edges multiple times in surface mesh");
3404   SMESH_File file("test.out");
3405   vector<int> two(2);
3406   vector<int> three1(3), three2(3);
3407   const char* badEdgeStr = " multiple times in surface mesh";
3408   const int   badEdgeStrLen = strlen( badEdgeStr );
3409   const int   nbNodes = nodeVec.size();
3410
3411   while( !file.eof() )
3412   {
3413     if ( strncmp( file, "Edge ", 5 ) == 0 &&
3414          file.getInts( two ) &&
3415          strncmp( file, badEdgeStr, badEdgeStrLen ) == 0 &&
3416          two[0] < nbNodes  &&  two[1] < nbNodes )
3417     {
3418       err->myBadElements.push_back( new SMDS_LinearEdge( nodeVec[ two[0]], nodeVec[ two[1]] ));
3419       file += badEdgeStrLen;
3420     }
3421     else if ( strncmp( file, "Intersecting: ", 14 ) == 0 )
3422     {
3423 // Intersecting: 
3424 // openelement 18 with open element 126
3425 // 41  36  38  
3426 // 69  70  72
3427       file.getLine();
3428       const char* pos = file;
3429       bool ok = ( strncmp( file, "openelement ", 12 ) == 0 );
3430       ok = ok && file.getInts( two );
3431       ok = ok && file.getInts( three1 );
3432       ok = ok && file.getInts( three2 );
3433       for ( int i = 0; ok && i < 3; ++i )
3434         ok = ( three1[i] < nbNodes && nodeVec[ three1[i]]);
3435       for ( int i = 0; ok && i < 3; ++i ) 
3436         ok = ( three2[i] < nbNodes && nodeVec[ three2[i]]);
3437       if ( ok )
3438       {
3439         err->myBadElements.push_back( new SMDS_FaceOfNodes( nodeVec[ three1[0]],
3440                                                             nodeVec[ three1[1]],
3441                                                             nodeVec[ three1[2]]));
3442         err->myBadElements.push_back( new SMDS_FaceOfNodes( nodeVec[ three2[0]],
3443                                                             nodeVec[ three2[1]],
3444                                                             nodeVec[ three2[2]]));
3445         err->myComment = "Intersecting triangles";
3446       }
3447       else
3448       {
3449         file.setPos( pos );
3450       }
3451     }
3452     else
3453     {
3454       ++file;
3455     }
3456   }
3457
3458 #ifdef _DEBUG_
3459   size_t nbBadElems = err->myBadElements.size();
3460   if ( nbBadElems ) nbBadElems++; // avoid warning: variable set but not used
3461 #endif
3462
3463   return err;
3464 }
3465
3466 //================================================================================
3467 /*!
3468  * \brief Write a python script creating an equivalent SALOME mesh.
3469  * This is useful to see what mesh is passed as input for the next step of mesh
3470  * generation (of mesh of higher dimension)
3471  */
3472 //================================================================================
3473
3474 void NETGENPlugin_Mesher::toPython( const netgen::Mesh* ngMesh )
3475 {
3476   const char*  pyFile = "/tmp/ngMesh.py";
3477   ofstream outfile( pyFile, ios::out );
3478   if ( !outfile ) return;
3479
3480   outfile << "import SMESH" << endl
3481           << "from salome.smesh import smeshBuilder" << endl
3482           << "smesh = smeshBuilder.New()" << endl
3483           << "mesh = smesh.Mesh()" << endl << endl;
3484
3485   using namespace netgen;
3486   PointIndex pi;
3487   for (pi = PointIndex::BASE; 
3488        pi < ngMesh->GetNP()+PointIndex::BASE; pi++)
3489   {
3490     outfile << "mesh.AddNode( ";
3491     outfile << (*ngMesh)[pi](0) << ", ";
3492     outfile << (*ngMesh)[pi](1) << ", ";
3493     outfile << (*ngMesh)[pi](2) << ") ## "<< pi << endl;
3494   }
3495
3496   int nbDom = ngMesh->GetNDomains();
3497   for ( int i = 0; i < nbDom; ++i )
3498     outfile<< "grp" << i+1 << " = mesh.CreateEmptyGroup( SMESH.FACE, 'domain"<< i+1 << "')"<< endl;
3499
3500   SurfaceElementIndex sei;
3501   for (sei = 0; sei < ngMesh->GetNSE(); sei++)
3502   {
3503     outfile << "mesh.AddFace([ ";
3504     Element2d sel = (*ngMesh)[sei];
3505     for (int j = 0; j < sel.GetNP(); j++)
3506       outfile << sel[j] << ( j+1 < sel.GetNP() ? ", " : " ])");
3507     if ( sel.IsDeleted() ) outfile << " ## IsDeleted ";
3508     outfile << endl;
3509
3510     if ((*ngMesh)[sei].GetIndex())
3511     {
3512       if ( int dom1 = ngMesh->GetFaceDescriptor((*ngMesh)[sei].GetIndex ()).DomainIn())
3513         outfile << "grp"<< dom1 <<".Add([ " << (int)sei+1 << " ])" << endl;
3514       if ( int dom2 = ngMesh->GetFaceDescriptor((*ngMesh)[sei].GetIndex ()).DomainOut())
3515         outfile << "grp"<< dom2 <<".Add([ " << (int)sei+1 << " ])" << endl;
3516     }
3517   }
3518
3519   for (ElementIndex ei = 0; ei < ngMesh->GetNE(); ei++)
3520   {
3521     Element el = (*ngMesh)[ei];
3522     outfile << "mesh.AddVolume([ ";
3523     for (int j = 0; j < el.GetNP(); j++)
3524       outfile << el[j] << ( j+1 < el.GetNP() ? ", " : " ])");
3525     outfile << endl;
3526   }
3527
3528   for (int i = 1; i <= ngMesh->GetNSeg(); i++)
3529   {
3530     const Segment & seg = ngMesh->LineSegment (i);
3531     outfile << "mesh.AddEdge([ "
3532             << seg[0] << ", "
3533             << seg[1] << " ])" << endl;
3534   }
3535   cout << "Write " << pyFile << endl;
3536 }
3537
3538 //================================================================================
3539 /*!
3540  * \brief Constructor of NETGENPlugin_ngMeshInfo
3541  */
3542 //================================================================================
3543
3544 NETGENPlugin_ngMeshInfo::NETGENPlugin_ngMeshInfo( netgen::Mesh* ngMesh):
3545   _copyOfLocalH(0)
3546 {
3547   if ( ngMesh )
3548   {
3549     _nbNodes    = ngMesh->GetNP();
3550     _nbSegments = ngMesh->GetNSeg();
3551     _nbFaces    = ngMesh->GetNSE();
3552     _nbVolumes  = ngMesh->GetNE();
3553   }
3554   else
3555   {
3556     _nbNodes = _nbSegments = _nbFaces = _nbVolumes = 0;
3557   }
3558 }
3559
3560 //================================================================================
3561 /*!
3562  * \brief Copy LocalH member from one netgen mesh to another
3563  */
3564 //================================================================================
3565
3566 void NETGENPlugin_ngMeshInfo::transferLocalH( netgen::Mesh* fromMesh,
3567                                               netgen::Mesh* toMesh )
3568 {
3569   if ( !fromMesh->LocalHFunctionGenerated() ) return;
3570   if ( !toMesh->LocalHFunctionGenerated() )
3571 #ifdef NETGEN_V5
3572     toMesh->CalcLocalH(netgen::mparam.grading);
3573 #else
3574     toMesh->CalcLocalH();
3575 #endif
3576
3577   const size_t size = sizeof( netgen::LocalH );
3578   _copyOfLocalH = new char[ size ];
3579   memcpy( (void*)_copyOfLocalH, (void*)&toMesh->LocalHFunction(), size );
3580   memcpy( (void*)&toMesh->LocalHFunction(), (void*)&fromMesh->LocalHFunction(), size );
3581 }
3582
3583 //================================================================================
3584 /*!
3585  * \brief Restore LocalH member of a netgen mesh
3586  */
3587 //================================================================================
3588
3589 void NETGENPlugin_ngMeshInfo::restoreLocalH( netgen::Mesh* toMesh )
3590 {
3591   if ( _copyOfLocalH )
3592   {
3593     const size_t size = sizeof( netgen::LocalH );
3594     memcpy( (void*)&toMesh->LocalHFunction(), (void*)_copyOfLocalH, size );
3595     delete [] _copyOfLocalH;
3596     _copyOfLocalH = 0;
3597   }
3598 }
3599
3600 //================================================================================
3601 /*!
3602  * \brief Find "internal" sub-shapes
3603  */
3604 //================================================================================
3605
3606 NETGENPlugin_Internals::NETGENPlugin_Internals( SMESH_Mesh&         mesh,
3607                                                 const TopoDS_Shape& shape,
3608                                                 bool                is3D )
3609   : _mesh( mesh ), _is3D( is3D )
3610 {
3611   SMESHDS_Mesh* meshDS = mesh.GetMeshDS();
3612
3613   TopExp_Explorer f,e;
3614   for ( f.Init( shape, TopAbs_FACE ); f.More(); f.Next() )
3615   {
3616     int faceID = meshDS->ShapeToIndex( f.Current() );
3617
3618     // find not computed internal edges
3619
3620     for ( e.Init( f.Current().Oriented(TopAbs_FORWARD), TopAbs_EDGE ); e.More(); e.Next() )
3621       if ( e.Current().Orientation() == TopAbs_INTERNAL )
3622       {
3623         SMESH_subMesh* eSM = mesh.GetSubMesh( e.Current() );
3624         if ( eSM->IsEmpty() )
3625         {
3626           _e2face.insert( make_pair( eSM->GetId(), faceID ));
3627           for ( TopoDS_Iterator v(e.Current()); v.More(); v.Next() )
3628             _e2face.insert( make_pair( meshDS->ShapeToIndex( v.Value() ), faceID ));
3629         }
3630       }
3631
3632     // find internal vertices in a face
3633     set<int> intVV; // issue 0020850 where same vertex is twice in a face
3634     for ( TopoDS_Iterator fSub( f.Current() ); fSub.More(); fSub.Next())
3635       if ( fSub.Value().ShapeType() == TopAbs_VERTEX )
3636       {
3637         int vID = meshDS->ShapeToIndex( fSub.Value() );
3638         if ( intVV.insert( vID ).second )
3639           _f2v[ faceID ].push_back( vID );
3640       }
3641
3642     if ( is3D )
3643     {
3644       // find internal faces and their subshapes where nodes are to be doubled
3645       //  to make a crack with non-sewed borders
3646
3647       if ( f.Current().Orientation() == TopAbs_INTERNAL )
3648       {
3649         _intShapes.insert( meshDS->ShapeToIndex( f.Current() ));
3650
3651         // egdes
3652         list< TopoDS_Shape > edges;
3653         for ( e.Init( f.Current(), TopAbs_EDGE ); e.More(); e.Next())
3654           if ( SMESH_MesherHelper::NbAncestors( e.Current(), mesh, TopAbs_FACE ) > 1 )
3655           {
3656             _intShapes.insert( meshDS->ShapeToIndex( e.Current() ));
3657             edges.push_back( e.Current() );
3658             // find border faces
3659             PShapeIteratorPtr fIt =
3660               SMESH_MesherHelper::GetAncestors( edges.back(),mesh,TopAbs_FACE );
3661             while ( const TopoDS_Shape* pFace = fIt->next() )
3662               if ( !pFace->IsSame( f.Current() ))
3663                 _borderFaces.insert( meshDS->ShapeToIndex( *pFace ));
3664           }
3665         // vertices
3666         // we consider vertex internal if it is shared by more than one internal edge
3667         list< TopoDS_Shape >::iterator edge = edges.begin();
3668         for ( ; edge != edges.end(); ++edge )
3669           for ( TopoDS_Iterator v( *edge ); v.More(); v.Next() )
3670           {
3671             set<int> internalEdges;
3672             PShapeIteratorPtr eIt =
3673               SMESH_MesherHelper::GetAncestors( v.Value(),mesh,TopAbs_EDGE );
3674             while ( const TopoDS_Shape* pEdge = eIt->next() )
3675             {
3676               int edgeID = meshDS->ShapeToIndex( *pEdge );
3677               if ( isInternalShape( edgeID ))
3678                 internalEdges.insert( edgeID );
3679             }
3680             if ( internalEdges.size() > 1 )
3681               _intShapes.insert( meshDS->ShapeToIndex( v.Value() ));
3682           }
3683       }
3684     }
3685   } // loop on geom faces
3686
3687   // find vertices internal in solids
3688   if ( is3D )
3689   {
3690     for ( TopExp_Explorer so(shape, TopAbs_SOLID); so.More(); so.Next())
3691     {
3692       int soID = meshDS->ShapeToIndex( so.Current() );
3693       for ( TopoDS_Iterator soSub( so.Current() ); soSub.More(); soSub.Next())
3694         if ( soSub.Value().ShapeType() == TopAbs_VERTEX )
3695           _s2v[ soID ].push_back( meshDS->ShapeToIndex( soSub.Value() ));
3696     }
3697   }
3698 }
3699
3700 //================================================================================
3701 /*!
3702  * \brief Find mesh faces on non-internal geom faces sharing internal edge
3703  * some nodes of which are to be doubled to make the second border of the "crack"
3704  */
3705 //================================================================================
3706
3707 void NETGENPlugin_Internals::findBorderElements( TIDSortedElemSet & borderElems )
3708 {
3709   if ( _intShapes.empty() ) return;
3710
3711   SMESH_Mesh& mesh = const_cast<SMESH_Mesh&>(_mesh);
3712   SMESHDS_Mesh* meshDS = mesh.GetMeshDS();
3713
3714   // loop on internal geom edges
3715   set<int>::const_iterator intShapeId = _intShapes.begin();
3716   for ( ; intShapeId != _intShapes.end(); ++intShapeId )
3717   {
3718     const TopoDS_Shape& s = meshDS->IndexToShape( *intShapeId );
3719     if ( s.ShapeType() != TopAbs_EDGE ) continue;
3720
3721     // get internal and non-internal geom faces sharing the internal edge <s>
3722     int intFace = 0;
3723     set<int>::iterator bordFace = _borderFaces.end();
3724     PShapeIteratorPtr faces = SMESH_MesherHelper::GetAncestors( s, _mesh, TopAbs_FACE );
3725     while ( const TopoDS_Shape* pFace = faces->next() )
3726     {
3727       int faceID = meshDS->ShapeToIndex( *pFace );
3728       if ( isInternalShape( faceID ))
3729         intFace = faceID;
3730       else
3731         bordFace = _borderFaces.insert( faceID ).first;
3732     }
3733     if ( bordFace == _borderFaces.end() || !intFace ) continue;
3734
3735     // get all links of mesh faces on internal geom face sharing nodes on edge <s>
3736     set< SMESH_OrientedLink > links; //!< links of faces on internal geom face
3737     list<const SMDS_MeshElement*> suspectFaces[2]; //!< mesh faces on border geom faces
3738     int nbSuspectFaces = 0;
3739     SMESHDS_SubMesh* intFaceSM = meshDS->MeshElements( intFace );
3740     if ( !intFaceSM || intFaceSM->NbElements() == 0 ) continue;
3741     SMESH_subMeshIteratorPtr smIt = mesh.GetSubMesh( s )->getDependsOnIterator(true,true);
3742     while ( smIt->more() )
3743     {
3744       SMESHDS_SubMesh* sm = smIt->next()->GetSubMeshDS();
3745       if ( !sm ) continue;
3746       SMDS_NodeIteratorPtr nIt = sm->GetNodes();
3747       while ( nIt->more() )
3748       {
3749         const SMDS_MeshNode* nOnEdge = nIt->next();
3750         SMDS_ElemIteratorPtr fIt = nOnEdge->GetInverseElementIterator(SMDSAbs_Face);
3751         while ( fIt->more() )
3752         {
3753           const SMDS_MeshElement* f = fIt->next();
3754           const int nbNodes = f->NbCornerNodes();
3755           if ( intFaceSM->Contains( f ))
3756           {
3757             for ( int i = 0; i < nbNodes; ++i )
3758               links.insert( SMESH_OrientedLink( f->GetNode(i), f->GetNode((i+1)%nbNodes)));
3759           }
3760           else
3761           {
3762             int nbDblNodes = 0;
3763             for ( int i = 0; i < nbNodes; ++i )
3764               nbDblNodes += isInternalShape( f->GetNode(i)->getshapeId() );
3765             if ( nbDblNodes )
3766               suspectFaces[ nbDblNodes < 2 ].push_back( f );
3767             nbSuspectFaces++;
3768           }
3769         }
3770       }
3771     }
3772     // suspectFaces[0] having link with same orientation as mesh faces on
3773     // the internal geom face are <borderElems>. suspectFaces[1] have
3774     // only one node on edge <s>, we decide on them later (at the 2nd loop)
3775     // by links of <borderElems> found at the 1st and 2nd loops
3776     set< SMESH_OrientedLink > borderLinks;
3777     for ( int isPostponed = 0; isPostponed < 2; ++isPostponed )
3778     {
3779       list<const SMDS_MeshElement*>::iterator fIt = suspectFaces[isPostponed].begin();
3780       for ( int nbF = 0; fIt != suspectFaces[isPostponed].end(); ++fIt, ++nbF )
3781       {
3782         const SMDS_MeshElement* f = *fIt;
3783         bool isBorder = false, linkFound = false, borderLinkFound = false;
3784         list< SMESH_OrientedLink > faceLinks;
3785         int nbNodes = f->NbCornerNodes();
3786         for ( int i = 0; i < nbNodes; ++i )
3787         {
3788           SMESH_OrientedLink link( f->GetNode(i), f->GetNode((i+1)%nbNodes));
3789           faceLinks.push_back( link );
3790           if ( !linkFound )
3791           {
3792             set< SMESH_OrientedLink >::iterator foundLink = links.find( link );
3793             if ( foundLink != links.end() )
3794             {
3795               linkFound= true;
3796               isBorder = ( foundLink->_reversed == link._reversed );
3797               if ( !isBorder && !isPostponed ) break;
3798               faceLinks.pop_back();
3799             }
3800             else if ( isPostponed && !borderLinkFound )
3801             {
3802               foundLink = borderLinks.find( link );
3803               if ( foundLink != borderLinks.end() )
3804               {
3805                 borderLinkFound = true;
3806                 isBorder = ( foundLink->_reversed != link._reversed );
3807               }
3808             }
3809           }
3810         }
3811         if ( isBorder )
3812         {
3813           borderElems.insert( f );
3814           borderLinks.insert( faceLinks.begin(), faceLinks.end() );
3815         }
3816         else if ( !linkFound && !borderLinkFound )
3817         {
3818           suspectFaces[1].push_back( f );
3819           if ( nbF > 2 * nbSuspectFaces )
3820             break; // dead loop protection
3821         }
3822       }
3823     }
3824   }
3825 }
3826
3827 //================================================================================
3828 /*!
3829  * \brief put internal shapes in maps and fill in submeshes to precompute
3830  */
3831 //================================================================================
3832
3833 void NETGENPlugin_Internals::getInternalEdges( TopTools_IndexedMapOfShape& fmap,
3834                                                TopTools_IndexedMapOfShape& emap,
3835                                                TopTools_IndexedMapOfShape& vmap,
3836                                                list< SMESH_subMesh* > smToPrecompute[])
3837 {
3838   if ( !hasInternalEdges() ) return;
3839   map<int,int>::const_iterator ev_face = _e2face.begin();
3840   for ( ; ev_face != _e2face.end(); ++ev_face )
3841   {
3842     const TopoDS_Shape& ev   = _mesh.GetMeshDS()->IndexToShape( ev_face->first );
3843     const TopoDS_Shape& face = _mesh.GetMeshDS()->IndexToShape( ev_face->second );
3844
3845     ( ev.ShapeType() == TopAbs_EDGE ? emap : vmap ).Add( ev );
3846     fmap.Add( face );
3847     //cout<<"INTERNAL EDGE or VERTEX "<<ev_face->first<<" on face "<<ev_face->second<<endl;
3848
3849     smToPrecompute[ MeshDim_1D ].push_back( _mesh.GetSubMeshContaining( ev_face->first ));
3850   }
3851 }
3852
3853 //================================================================================
3854 /*!
3855  * \brief return shapes and submeshes to be meshed and already meshed boundary submeshes
3856  */
3857 //================================================================================
3858
3859 void NETGENPlugin_Internals::getInternalFaces( TopTools_IndexedMapOfShape& fmap,
3860                                                TopTools_IndexedMapOfShape& emap,
3861                                                list< SMESH_subMesh* >&     intFaceSM,
3862                                                list< SMESH_subMesh* >&     boundarySM)
3863 {
3864   if ( !hasInternalFaces() ) return;
3865
3866   // <fmap> and <emap> are for not yet meshed shapes
3867   // <intFaceSM> is for submeshes of faces
3868   // <boundarySM> is for meshed edges and vertices
3869
3870   intFaceSM.clear();
3871   boundarySM.clear();
3872
3873   set<int> shapeIDs ( _intShapes );
3874   if ( !_borderFaces.empty() )
3875     shapeIDs.insert( _borderFaces.begin(), _borderFaces.end() );
3876
3877   set<int>::const_iterator intS = shapeIDs.begin();
3878   for ( ; intS != shapeIDs.end(); ++intS )
3879   {
3880     SMESH_subMesh* sm = _mesh.GetSubMeshContaining( *intS );
3881
3882     if ( sm->GetSubShape().ShapeType() != TopAbs_FACE ) continue;
3883
3884     intFaceSM.push_back( sm );
3885
3886     // add submeshes of not computed internal faces
3887     if ( !sm->IsEmpty() ) continue;
3888
3889     SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(true,true);
3890     while ( smIt->more() )
3891     {
3892       sm = smIt->next();
3893       const TopoDS_Shape& s = sm->GetSubShape();
3894
3895       if ( sm->IsEmpty() )
3896       {
3897         // not yet meshed
3898         switch ( s.ShapeType() ) {
3899         case TopAbs_FACE: fmap.Add ( s ); break;
3900         case TopAbs_EDGE: emap.Add ( s ); break;
3901         default:;
3902         }
3903       }
3904       else
3905       {
3906         if ( s.ShapeType() != TopAbs_FACE )
3907           boundarySM.push_back( sm );
3908       }
3909     }
3910   }
3911 }
3912
3913 //================================================================================
3914 /*!
3915  * \brief Return true if given shape is to be precomputed in order to be correctly
3916  * added to netgen mesh
3917  */
3918 //================================================================================
3919
3920 bool NETGENPlugin_Internals::isShapeToPrecompute(const TopoDS_Shape& s)
3921 {
3922   int shapeID = _mesh.GetMeshDS()->ShapeToIndex( s );
3923   switch ( s.ShapeType() ) {
3924   case TopAbs_FACE  : break; //return isInternalShape( shapeID ) || isBorderFace( shapeID );
3925   case TopAbs_EDGE  : return isInternalEdge( shapeID );
3926   case TopAbs_VERTEX: break;
3927   default:;
3928   }
3929   return false;
3930 }
3931
3932 //================================================================================
3933 /*!
3934  * \brief Return SMESH
3935  */
3936 //================================================================================
3937
3938 SMESH_Mesh& NETGENPlugin_Internals::getMesh() const
3939 {
3940   return const_cast<SMESH_Mesh&>( _mesh );
3941 }
3942
3943 //================================================================================
3944 /*!
3945  * \brief Access to a counter of NETGENPlugin_NetgenLibWrapper instances
3946  */
3947 //================================================================================
3948
3949 int& NETGENPlugin_NetgenLibWrapper::instanceCounter()
3950 {
3951   static int theCouner = 0;
3952   return theCouner;
3953 }
3954
3955 //================================================================================
3956 /*!
3957  * \brief Initialize netgen library
3958  */
3959 //================================================================================
3960
3961 NETGENPlugin_NetgenLibWrapper::NETGENPlugin_NetgenLibWrapper()
3962 {
3963   if ( instanceCounter() == 0 )
3964     Ng_Init();
3965
3966   ++instanceCounter();
3967
3968   _isComputeOk      = false;
3969   _coutBuffer       = NULL;
3970   _ngcout           = NULL;
3971   _ngcerr           = NULL;
3972   if ( !getenv( "KEEP_NETGEN_OUTPUT" ))
3973   {
3974     // redirect all netgen output (mycout,myerr,cout) to _outputFileName
3975     _outputFileName = getOutputFileName();
3976     _ngcout         = netgen::mycout;
3977     _ngcerr         = netgen::myerr;
3978     netgen::mycout  = new ofstream ( _outputFileName.c_str() );
3979     netgen::myerr   = netgen::mycout;
3980     _coutBuffer     = std::cout.rdbuf();
3981 #ifdef _DEBUG_
3982     cout << "NOTE: netgen output is redirected to file " << _outputFileName << endl;
3983 #else
3984     std::cout.rdbuf( netgen::mycout->rdbuf() );
3985 #endif
3986   }
3987
3988   _ngMesh = Ng_NewMesh();
3989 }
3990
3991 //================================================================================
3992 /*!
3993  * \brief Finish using netgen library
3994  */
3995 //================================================================================
3996
3997 NETGENPlugin_NetgenLibWrapper::~NETGENPlugin_NetgenLibWrapper()
3998 {
3999   --instanceCounter();
4000
4001   Ng_DeleteMesh( _ngMesh );
4002   Ng_Exit();
4003   RemoveTmpFiles();
4004   if ( _coutBuffer )
4005     std::cout.rdbuf( _coutBuffer );
4006 #ifdef _DEBUG_
4007   if( _isComputeOk )
4008 #endif
4009     removeOutputFile();
4010 }
4011
4012 //================================================================================
4013 /*!
4014  * \brief Set netgen mesh to delete at destruction
4015  */
4016 //================================================================================
4017
4018 void NETGENPlugin_NetgenLibWrapper::setMesh( Ng_Mesh* mesh )
4019 {
4020   if ( _ngMesh )
4021     Ng_DeleteMesh( _ngMesh );
4022   _ngMesh = mesh;
4023 }
4024
4025 //================================================================================
4026 /*!
4027  * \brief Return a unique file name
4028  */
4029 //================================================================================
4030
4031 std::string NETGENPlugin_NetgenLibWrapper::getOutputFileName()
4032 {
4033   std::string aTmpDir = SALOMEDS_Tool::GetTmpDir();
4034
4035   TCollection_AsciiString aGenericName = (char*)aTmpDir.c_str();
4036   aGenericName += "NETGEN_";
4037 #ifndef WIN32
4038   aGenericName += getpid();
4039 #else
4040   aGenericName += _getpid();
4041 #endif
4042   aGenericName += "_";
4043   aGenericName += Abs((Standard_Integer)(long) aGenericName.ToCString());
4044   aGenericName += ".out";
4045
4046   return aGenericName.ToCString();
4047 }
4048
4049 //================================================================================
4050 /*!
4051  * \brief Remove "test.out" and "problemfaces" files in current directory
4052  */
4053 //================================================================================
4054
4055 void NETGENPlugin_NetgenLibWrapper::RemoveTmpFiles()
4056 {
4057   bool rm =  SMESH_File("test.out").remove() ;
4058 #ifndef WIN32
4059   if ( rm && netgen::testout && instanceCounter() == 0 )
4060   {
4061     delete netgen::testout;
4062     netgen::testout = 0;
4063   }
4064 #endif
4065   SMESH_File("problemfaces").remove();
4066   SMESH_File("occmesh.rep").remove();
4067 }
4068
4069 //================================================================================
4070 /*!
4071  * \brief Remove file with netgen output
4072  */
4073 //================================================================================
4074
4075 void NETGENPlugin_NetgenLibWrapper::removeOutputFile()
4076 {
4077   if ( !_outputFileName.empty() )
4078   {
4079     if ( _ngcout )
4080     {
4081       delete netgen::mycout;
4082       netgen::mycout = _ngcout;
4083       netgen::myerr  = _ngcerr;
4084       _ngcout        = 0;
4085     }
4086     string    tmpDir = SALOMEDS_Tool::GetDirFromPath ( _outputFileName );
4087     string aFileName = SALOMEDS_Tool::GetNameFromPath( _outputFileName ) + ".out";
4088     SALOMEDS_Tool::ListOfFiles aFiles;
4089     aFiles.reserve(1);
4090     aFiles.push_back(aFileName.c_str());
4091
4092     SALOMEDS_Tool::RemoveTemporaryFiles( tmpDir.c_str(), aFiles, true );
4093   }
4094 }