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