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