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