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