Salome HOME
fix regression with seam edges made by the previous revision
[plugins/netgenplugin.git] / src / NETGENPlugin / NETGENPlugin_Mesher.cxx
1 //  Copyright (C) 2007-2010  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 <utilities.h>
47
48 #include <vector>
49 #include <limits>
50
51 #include <BRep_Tool.hxx>
52 #include <NCollection_Map.hxx>
53 #include <OSD_File.hxx>
54 #include <OSD_Path.hxx>
55 #include <Standard_ErrorHandler.hxx>
56 #include <Standard_ProgramError.hxx>
57 #include <TCollection_AsciiString.hxx>
58 #include <TopExp.hxx>
59 #include <TopExp_Explorer.hxx>
60 #include <TopTools_DataMapIteratorOfDataMapOfShapeInteger.hxx>
61 #include <TopTools_DataMapIteratorOfDataMapOfShapeShape.hxx>
62 #include <TopTools_DataMapOfShapeInteger.hxx>
63 #include <TopTools_DataMapOfShapeShape.hxx>
64 #include <TopTools_ListIteratorOfListOfShape.hxx>
65 #include <TopTools_MapOfShape.hxx>
66 #include <TopoDS.hxx>
67 #include <GeomAdaptor_Curve.hxx>
68 #include <GCPnts_AbscissaPoint.hxx>
69
70 // Netgen include files
71 #ifndef OCCGEOMETRY
72 #define OCCGEOMETRY
73 #endif
74 #include <occgeom.hpp>
75 #include <meshing.hpp>
76 //#include <ngexception.hpp>
77 namespace netgen {
78   extern int OCCGenerateMesh (OCCGeometry&, Mesh*&, int, int, char*);
79   extern MeshingParameters mparam;
80   extern volatile multithreadt multithread;
81 }
82
83 using namespace nglib;
84 using namespace std;
85
86 #ifdef _DEBUG_
87 #define nodeVec_ACCESS(index) ((SMDS_MeshNode*) nodeVec.at((index)))
88 #else
89 #define nodeVec_ACCESS(index) ((SMDS_MeshNode*) nodeVec[index])
90 #endif
91
92 #ifdef NETGEN_NEW
93 #define NGPOINT_COORDS(p) p(0),p(1),p(2)
94 #else
95 #define NGPOINT_COORDS(p) p.X(),p.Y(),p.Z()
96 #endif
97
98 // dump elements added to ng mesh
99 //#define DUMP_SEGMENTS
100 //#define DUMP_TRIANGLES
101 //#define DUMP_TRIANGLES_SCRIPT "/tmp/trias.py" //!< debug addIntVerticesInSolids()
102
103 TopTools_IndexedMapOfShape ShapesWithLocalSize;
104 std::map<int,double> VertexId2LocalSize;
105 std::map<int,double> EdgeId2LocalSize;
106 std::map<int,double> FaceId2LocalSize;
107
108 //=============================================================================
109 /*!
110  *
111  */
112 //=============================================================================
113
114 NETGENPlugin_Mesher::NETGENPlugin_Mesher (SMESH_Mesh* mesh,
115                                           const TopoDS_Shape& aShape,
116                                           const bool isVolume)
117   : _mesh    (mesh),
118     _shape   (aShape),
119     _isVolume(isVolume),
120     _optimize(true),
121     _simpleHyp(NULL)
122 {
123   defaultParameters();
124   ShapesWithLocalSize.Clear();
125   VertexId2LocalSize.clear();
126   EdgeId2LocalSize.clear();
127   FaceId2LocalSize.clear();
128 }
129
130 //================================================================================
131 /*!
132  * \brief Initialize global NETGEN parameters with default values
133  */
134 //================================================================================
135
136 void NETGENPlugin_Mesher::defaultParameters()
137 {
138   netgen::MeshingParameters& mparams = netgen::mparam;
139   // maximal mesh edge size
140   mparams.maxh = NETGENPlugin_Hypothesis::GetDefaultMaxSize();
141   // minimal number of segments per edge
142   mparams.segmentsperedge = NETGENPlugin_Hypothesis::GetDefaultNbSegPerEdge();
143   // rate of growth of size between elements
144   mparams.grading = NETGENPlugin_Hypothesis::GetDefaultGrowthRate();
145   // safety factor for curvatures (elements per radius)
146   mparams.curvaturesafety = NETGENPlugin_Hypothesis::GetDefaultNbSegPerRadius();
147   // create elements of second order
148   mparams.secondorder = NETGENPlugin_Hypothesis::GetDefaultSecondOrder() ? 1 : 0;
149   // quad-dominated surface meshing
150   if (_isVolume)
151     mparams.quad = 0;
152   else
153     mparams.quad = NETGENPlugin_Hypothesis_2D::GetDefaultQuadAllowed() ? 1 : 0;
154 }
155
156 //=============================================================================
157 /*!
158  *
159  */
160 //=============================================================================
161 void SetLocalSize(TopoDS_Shape GeomShape, double LocalSize)
162 {
163   TopAbs_ShapeEnum GeomType = GeomShape.ShapeType();
164   if (GeomType == TopAbs_COMPOUND) {
165     for (TopoDS_Iterator it (GeomShape); it.More(); it.Next()) {
166       SetLocalSize(it.Value(), LocalSize);
167     }
168     return;
169   }
170   int key;
171   if (! ShapesWithLocalSize.Contains(GeomShape))
172     key = ShapesWithLocalSize.Add(GeomShape);
173   else
174     key = ShapesWithLocalSize.FindIndex(GeomShape);
175   if (GeomType == TopAbs_VERTEX) {
176     VertexId2LocalSize[key] = LocalSize;
177   } else if (GeomType == TopAbs_EDGE) {
178     EdgeId2LocalSize[key] = LocalSize;
179   } else if (GeomType == TopAbs_FACE) {
180     FaceId2LocalSize[key] = LocalSize;
181   }
182 }
183
184 //=============================================================================
185 /*!
186  * Pass parameters to NETGEN
187  */
188 //=============================================================================
189 void NETGENPlugin_Mesher::SetParameters(const NETGENPlugin_Hypothesis* hyp)
190 {
191   if (hyp)
192   {
193     netgen::MeshingParameters& mparams = netgen::mparam;
194     // Initialize global NETGEN parameters:
195     // maximal mesh segment size
196     mparams.maxh = hyp->GetMaxSize();
197     // minimal number of segments per edge
198     mparams.segmentsperedge = hyp->GetNbSegPerEdge();
199     // rate of growth of size between elements
200     mparams.grading = hyp->GetGrowthRate();
201     // safety factor for curvatures (elements per radius)
202     mparams.curvaturesafety = hyp->GetNbSegPerRadius();
203     // create elements of second order
204     mparams.secondorder = hyp->GetSecondOrder() ? 1 : 0;
205     // quad-dominated surface meshing
206     // only triangles are allowed for volumic mesh
207     if (!_isVolume)
208       mparams.quad = static_cast<const NETGENPlugin_Hypothesis_2D*>
209         (hyp)->GetQuadAllowed() ? 1 : 0;
210     _optimize = hyp->GetOptimize();
211     _simpleHyp = NULL;
212
213     SMESH_Gen_i* smeshGen_i = SMESH_Gen_i::GetSMESHGen();
214     CORBA::Object_var anObject = smeshGen_i->GetNS()->Resolve("/myStudyManager");
215     SALOMEDS::StudyManager_var aStudyMgr = SALOMEDS::StudyManager::_narrow(anObject);
216     SALOMEDS::Study_var myStudy = aStudyMgr->GetStudyByID(hyp->GetStudyId());
217     
218     const NETGENPlugin_Hypothesis::TLocalSize localSizes = hyp->GetLocalSizesAndEntries();
219     NETGENPlugin_Hypothesis::TLocalSize::const_iterator it = localSizes.begin();
220     for (it ; it != localSizes.end() ; it++)
221       {
222         std::string entry = (*it).first;
223         double val = (*it).second;
224         // --
225         GEOM::GEOM_Object_var aGeomObj;
226         TopoDS_Shape S = TopoDS_Shape();
227         SALOMEDS::SObject_var aSObj = myStudy->FindObjectID( entry.c_str() );
228         SALOMEDS::GenericAttribute_var anAttr;
229         if (!aSObj->_is_nil() && aSObj->FindAttribute(anAttr, "AttributeIOR")) {
230           SALOMEDS::AttributeIOR_var anIOR = SALOMEDS::AttributeIOR::_narrow(anAttr);
231           CORBA::String_var aVal = anIOR->Value();
232           CORBA::Object_var obj = myStudy->ConvertIORToObject(aVal);
233           aGeomObj = GEOM::GEOM_Object::_narrow(obj);
234         }
235         if ( !aGeomObj->_is_nil() )
236           S = smeshGen_i->GeomObjectToShape( aGeomObj.in() );
237         // --
238         SetLocalSize(S, val);
239       }
240   }
241 }
242
243 //=============================================================================
244 /*!
245  * Pass simple parameters to NETGEN
246  */
247 //=============================================================================
248
249 void NETGENPlugin_Mesher::SetParameters(const NETGENPlugin_SimpleHypothesis_2D* hyp)
250 {
251   _simpleHyp = hyp;
252   if ( _simpleHyp )
253     defaultParameters();
254 }
255
256 //=============================================================================
257 /*!
258  *  Link - a pair of integer numbers
259  */
260 //=============================================================================
261 struct Link
262 {
263   int n1, n2;
264   Link(int _n1, int _n2) : n1(_n1), n2(_n2) {}
265   Link() : n1(0), n2(0) {}
266 };
267
268 int HashCode(const Link& aLink, int aLimit)
269 {
270   return HashCode(aLink.n1 + aLink.n2, aLimit);
271 }
272
273 Standard_Boolean IsEqual(const Link& aLink1, const Link& aLink2)
274 {
275   return (aLink1.n1 == aLink2.n1 && aLink1.n2 == aLink2.n2 ||
276           aLink1.n1 == aLink2.n2 && aLink1.n2 == aLink2.n1);
277 }
278
279 //================================================================================
280 /*!
281  * \brief Initialize netgen::OCCGeometry with OCCT shape
282  */
283 //================================================================================
284
285 void NETGENPlugin_Mesher::PrepareOCCgeometry(netgen::OCCGeometry&     occgeo,
286                                              const TopoDS_Shape&      shape,
287                                              SMESH_Mesh&              mesh,
288                                              list< SMESH_subMesh* > * meshedSM,
289                                              NETGENPlugin_Internals*  intern)
290 {
291   BRepTools::Clean (shape);
292   try {
293 #if (OCC_VERSION_MAJOR << 16 | OCC_VERSION_MINOR << 8 | OCC_VERSION_MAINTENANCE) > 0x060100
294     OCC_CATCH_SIGNALS;
295 #endif
296     BRepMesh_IncrementalMesh e(shape, 0.01, true);
297   } catch (Standard_Failure) {
298   }
299   Bnd_Box bb;
300   BRepBndLib::Add (shape, bb);
301   double x1,y1,z1,x2,y2,z2;
302   bb.Get (x1,y1,z1,x2,y2,z2);
303   MESSAGE("shape bounding box:\n" <<
304           "(" << x1 << " " << y1 << " " << z1 << ") " <<
305           "(" << x2 << " " << y2 << " " << z2 << ")");
306   netgen::Point<3> p1 = netgen::Point<3> (x1,y1,z1);
307   netgen::Point<3> p2 = netgen::Point<3> (x2,y2,z2);
308   occgeo.boundingbox = netgen::Box<3> (p1,p2);
309
310   occgeo.shape = shape;
311   occgeo.changed = 1;
312
313   // fill maps of shapes of occgeo with not yet meshed subshapes
314
315   // get root submeshes
316   list< SMESH_subMesh* > rootSM;
317   if ( SMESH_subMesh* sm = mesh.GetSubMeshContaining( shape )) {
318     rootSM.push_back( sm );
319   }
320   else {
321     for ( TopoDS_Iterator it( shape ); it.More(); it.Next() )
322       rootSM.push_back( mesh.GetSubMesh( it.Value() ));
323   }
324
325   // add subshapes of empty submeshes
326   list< SMESH_subMesh* >::iterator rootIt = rootSM.begin(), rootEnd = rootSM.end();
327   for ( ; rootIt != rootEnd; ++rootIt ) {
328     SMESH_subMesh * root = *rootIt;
329     SMESH_subMeshIteratorPtr smIt = root->getDependsOnIterator(/*includeSelf=*/true,
330                                                                /*complexShapeFirst=*/true);
331     // to find a right orientation of subshapes (PAL20462)
332     TopTools_IndexedMapOfShape subShapes;
333     TopExp::MapShapes(root->GetSubShape(), subShapes);
334     while ( smIt->more() )
335     {
336       SMESH_subMesh* sm = smIt->next();
337       TopoDS_Shape shape = sm->GetSubShape();
338       if ( intern && intern->isShapeToPrecompute( shape ))
339         continue;
340       if ( !meshedSM || sm->IsEmpty() )
341       {
342         if ( shape.ShapeType() != TopAbs_VERTEX )
343           shape = subShapes( subShapes.FindIndex( shape ));// shape -> index -> oriented shape
344         if ( shape.Orientation() >= TopAbs_INTERNAL )
345           shape.Orientation( TopAbs_FORWARD ); // isuue 0020676
346         switch ( shape.ShapeType() ) {
347         case TopAbs_FACE  : occgeo.fmap.Add( shape ); break;
348         case TopAbs_EDGE  : occgeo.emap.Add( shape ); break;
349         case TopAbs_VERTEX: occgeo.vmap.Add( shape ); break;
350         case TopAbs_SOLID :occgeo.somap.Add( shape ); break;
351         default:;
352         }
353       }
354       // collect submeshes of meshed shapes
355       else if (meshedSM)
356       {
357         const int dim = SMESH_Gen::GetShapeDim( shape );
358         meshedSM[ dim ].push_back( sm );
359       }
360     }
361   }
362   occgeo.facemeshstatus.SetSize (occgeo.fmap.Extent());
363   occgeo.facemeshstatus = 0;
364 #ifdef NETGEN_NEW
365   occgeo.face_maxh.SetSize(occgeo.fmap.Extent());
366   occgeo.face_maxh = netgen::mparam.maxh;
367   occgeo.face_maxh_modified.SetSize(occgeo.fmap.Extent());
368   occgeo.face_maxh_modified = 0;
369 #endif
370
371 }
372
373 namespace
374 {
375   //================================================================================
376   /*!
377    * \brief return id of netgen point corresponding to SMDS node
378    */
379   //================================================================================
380   typedef map< const SMDS_MeshNode*, int > TNode2IdMap;
381
382   int ngNodeId( const SMDS_MeshNode* node,
383                 netgen::Mesh&        ngMesh,
384                 TNode2IdMap&         nodeNgIdMap)
385   {
386     int newNgId = ngMesh.GetNP() + 1;
387
388     TNode2IdMap::iterator node_id = nodeNgIdMap.insert( make_pair( node, newNgId )).first;
389
390     if ( node_id->second == newNgId)
391     {
392 #if defined(DUMP_SEGMENTS) || defined(DUMP_TRIANGLES)
393       cout << "Ng " << newNgId << " - " << node;
394 #endif
395       netgen::MeshPoint p( netgen::Point<3> (node->X(), node->Y(), node->Z()) );
396       ngMesh.AddPoint( p );
397     }
398     return node_id->second;
399   }
400
401   //================================================================================
402   /*!
403    * \brief Return computed EDGEs connected to the given one
404    */
405   //================================================================================
406
407   list< TopoDS_Edge > getConnectedEdges( const TopoDS_Edge&            edge,
408                                          const TopoDS_Face&            face,
409                                          const set< SMESH_subMesh* > & computedSM,
410                                          const SMESH_MesherHelper&     helper )
411   {
412     // get ordered EDGEs
413     TopoDS_Vertex v1;
414     list< TopoDS_Edge > edges;
415     list< int > nbEdgesInWire;
416     int nbWires = SMESH_Block::GetOrderedEdges( face, v1, edges, nbEdgesInWire);
417
418     // find <edge> within <edges>
419     list< TopoDS_Edge >::iterator eItFwd = edges.begin();
420     for ( ; eItFwd != edges.end(); ++eItFwd )
421       if ( edge.IsSame( *eItFwd ))
422         break;
423     if ( eItFwd == edges.end()) return list< TopoDS_Edge>();
424
425     if ( eItFwd->Orientation() >= TopAbs_INTERNAL )
426     {
427       // connected INTERNAL edges returned from GetOrderedEdges() are wrongly oriented
428       // so treat each INTERNAL edge separately
429       TopoDS_Edge e = *eItFwd;
430       edges.clear();
431       edges.push_back( e );
432       return edges;
433     }
434     // find not computed or not connected EDGEs around <edge>
435
436     list< TopoDS_Edge >::iterator eItBack = eItFwd, ePrev;
437     TopoDS_Vertex vCommon;
438
439     // put edges after <edge> at <edges> head
440     while ( edges.back() != *eItFwd )
441       edges.splice( edges.begin(), edges, --edges.end() );
442
443     // search backward
444     while ( eItBack != edges.begin() )
445     {
446       ePrev = eItBack;
447       --eItBack;
448       bool connected = TopExp::CommonVertex( *ePrev, *eItBack, vCommon );
449       bool computed  = helper.GetMesh()->GetSubMesh( *eItBack )->IsMeshComputed();
450       bool orientOK  = (( ePrev  ->Orientation() < TopAbs_INTERNAL ) ==
451                         ( eItBack->Orientation() < TopAbs_INTERNAL )    );
452       if ( !connected || !computed || !orientOK)
453       {
454         // move edges from head to tail
455         while ( edges.begin() != eItBack )
456           edges.splice( edges.end(), edges, edges.begin() );
457         edges.erase( eItBack );
458         break;
459       }
460     }
461     // search forward
462     ePrev = eItFwd;
463     while ( ++eItFwd != edges.end() )
464     {
465       bool connected = TopExp::CommonVertex( *ePrev, *eItFwd, vCommon );
466       bool computed  = helper.GetMesh()->GetSubMesh( *eItFwd )->IsMeshComputed();
467       bool orientOK  = (( ePrev ->Orientation() < TopAbs_INTERNAL ) ==
468                         ( eItFwd->Orientation() < TopAbs_INTERNAL )    );
469       if ( !connected || !computed || !orientOK )
470       {
471         edges.erase( eItFwd, edges.end() );
472         break;
473       }
474       ePrev = eItFwd;
475     }
476     if ( edges.front() != edges.back() )
477     {
478       // assure that the 1st vertex is meshed
479       TopoDS_Edge eLast = edges.back();
480       while ( !SMESH_Algo::VertexNode( SMESH_MesherHelper::IthVertex( 0, edges.front()), helper.GetMeshDS())
481               &&
482               edges.front() != eLast )
483         edges.splice( edges.end(), edges, edges.begin() );
484     }
485     return edges;
486   }
487 }
488
489 //================================================================================
490 /*!
491  * \brief fill ngMesh with nodes and elements of computed submeshes
492  */
493 //================================================================================
494
495 bool NETGENPlugin_Mesher::fillNgMesh(const netgen::OCCGeometry&     occgeom,
496                                      netgen::Mesh&                  ngMesh,
497                                      vector<const SMDS_MeshNode*>&  nodeVec,
498                                      const list< SMESH_subMesh* > & meshedSM)
499 {
500   TNode2IdMap nodeNgIdMap;
501   if ( !nodeVec.empty() )
502     for ( int i = 1; i < nodeVec.size(); ++i )
503       nodeNgIdMap.insert( make_pair( nodeVec[i], i ));
504
505   TopTools_MapOfShape visitedShapes;
506   map< SMESH_subMesh*, set< int > > visitedEdgeSM2Faces;
507   set< SMESH_subMesh* > computedSM( meshedSM.begin(), meshedSM.end() );
508
509   SMESH_MesherHelper helper (*_mesh);
510
511   int faceID = occgeom.fmap.Extent();
512
513   list< SMESH_subMesh* >::const_iterator smIt, smEnd = meshedSM.end();
514   for ( smIt = meshedSM.begin(); smIt != smEnd; ++smIt )
515   {
516     SMESH_subMesh* sm = *smIt;
517     if ( !visitedShapes.Add( sm->GetSubShape() ))
518       continue;
519
520     SMESHDS_SubMesh * smDS = sm->GetSubMeshDS();
521     if ( !smDS ) continue;
522
523     switch ( sm->GetSubShape().ShapeType() )
524     {
525     case TopAbs_EDGE: { // EDGE
526       // ----------------------
527       TopoDS_Edge geomEdge  = TopoDS::Edge( sm->GetSubShape() );
528       if ( geomEdge.Orientation() >= TopAbs_INTERNAL )
529         geomEdge.Orientation( TopAbs_FORWARD ); // issue 0020676
530
531       // Add ng segments for each not meshed FACE the EDGE bounds
532       PShapeIteratorPtr fIt = helper.GetAncestors( geomEdge, *sm->GetFather(), TopAbs_FACE );
533       while ( const TopoDS_Shape * anc = fIt->next() )
534       {
535         int faceID = occgeom.fmap.FindIndex( *anc );
536         if ( faceID < 1 )
537           continue; // meshed face
538         if ( visitedEdgeSM2Faces[ sm ].count( faceID ))
539           continue; // already treated EDGE
540
541         TopoDS_Face face = TopoDS::Face( occgeom.fmap( faceID ));
542         if ( face.Orientation() >= TopAbs_INTERNAL )
543           face.Orientation( TopAbs_FORWARD ); // issue 0020676
544
545         // get all meshed EDGEs of the FACE connected to geomEdge (issue 0021140)
546         helper.SetSubShape( face );
547         list< TopoDS_Edge > edges = getConnectedEdges( geomEdge, face, computedSM, helper );
548
549         // find out orientation of <edges> within <face>
550         TopoDS_Edge eNotSeam = edges.front();
551         if ( helper.HasSeam() )
552         {
553           list< TopoDS_Edge >::iterator eIt = edges.begin();
554           while ( helper.IsRealSeam( *eIt )) ++eIt;
555           if ( eIt != edges.end() )
556             eNotSeam = *eIt;
557         }
558         TopAbs_Orientation fOri = helper.GetSubShapeOri( face, eNotSeam );
559         bool isForwad = ( fOri == eNotSeam.Orientation() || fOri >= TopAbs_INTERNAL );
560
561         // get all nodes from connected <edges>
562         bool isQuad   = smDS->NbElements() ? smDS->GetElements()->next()->IsQuadratic() : false;
563         StdMeshers_FaceSide fSide( face, edges, _mesh, isForwad, isQuad );
564         const vector<UVPtStruct>& points = fSide.GetUVPtStruct();
565         int i, nbSeg = fSide.NbSegments();
566
567         // remembre EDGEs of fSide to treat only once
568         for ( int iE = 0; iE < fSide.NbEdges(); ++iE )
569           visitedEdgeSM2Faces[ helper.GetMesh()->GetSubMesh( fSide.Edge(iE )) ].insert( faceID );
570
571         double otherSeamParam = 0;
572         bool isSeam = false;
573
574         // add segments
575
576         int prevNgId = ngNodeId( points[0].node, ngMesh, nodeNgIdMap );
577
578         for ( i = 0; i < nbSeg; ++i )
579         {
580           const UVPtStruct& p1 = points[ i ];
581           const UVPtStruct& p2 = points[ i+1 ];
582
583           if ( p1.node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX ) //an EDGE begins
584           {
585             isSeam = false;
586             if ( helper.IsRealSeam( p1.node->getshapeId() ))
587             {
588               geomEdge = fSide.Edge( fSide.EdgeIndex( 0.5 * ( p1.normParam + p2.normParam )));
589               isSeam = helper.IsRealSeam( geomEdge );
590               if ( isSeam )
591                 otherSeamParam = helper.GetOtherParam( helper.GetPeriodicIndex() & 1 ? p2.u : p2.v );
592             }
593           }
594           netgen::Segment seg;
595           // ng node ids
596           seg[0] = prevNgId;
597           seg[1] = prevNgId = ngNodeId( p2.node, ngMesh, nodeNgIdMap );
598           // node param on curve
599           seg.epgeominfo[ 0 ].dist = p1.param;
600           seg.epgeominfo[ 1 ].dist = p2.param;
601           // uv on face
602           seg.epgeominfo[ 0 ].u = p1.u;
603           seg.epgeominfo[ 0 ].v = p1.v;
604           seg.epgeominfo[ 1 ].u = p2.u;
605           seg.epgeominfo[ 1 ].v = p2.v;
606
607           //geomEdge = fSide.Edge( fSide.EdgeIndex( 0.5 * ( p1.normParam + p2.normParam )));
608           //seg.epgeominfo[ 0 ].edgenr = seg.epgeominfo[ 1 ].edgenr = occgeom.emap.FindIndex( geomEdge );
609
610           //seg.epgeominfo[ iEnd ].edgenr = edgeID; //  = geom.emap.FindIndex(edge);
611           seg.si = faceID;                   // = geom.fmap.FindIndex (face);
612           seg.edgenr = ngMesh.GetNSeg() + 1; // segment id
613           ngMesh.AddSegment (seg);
614
615           netgen::Point3d ngP1(p1.node->X(), p1.node->Y(), p1.node->Z());
616           netgen::Point3d ngP2(p2.node->X(), p2.node->Y(), p2.node->Z());
617           ngMesh.RestrictLocalH( netgen::Center( ngP1,ngP2), Dist(ngP1,ngP2));
618
619 #ifdef DUMP_SEGMENTS
620           cout << "Segment: " << seg.edgenr << " on SMESH face " << helper.GetMeshDS()->ShapeToIndex( face ) << endl
621                << "\tface index: " << seg.si << endl
622                << "\tp1: " << seg[0] << endl
623                << "\tp2: " << seg[1] << endl
624                << "\tp0 param: " << seg.epgeominfo[ 0 ].dist << endl
625                << "\tp0 uv: " << seg.epgeominfo[ 0 ].u <<", "<< seg.epgeominfo[ 0 ].v << endl
626             //<< "\tp0 edge: " << seg.epgeominfo[ 0 ].edgenr << endl
627                << "\tp1 param: " << seg.epgeominfo[ 1 ].dist << endl
628                << "\tp1 uv: " << seg.epgeominfo[ 1 ].u <<", "<< seg.epgeominfo[ 1 ].v << endl;
629             //<< "\tp1 edge: " << seg.epgeominfo[ 1 ].edgenr << endl;
630 #endif
631           if ( isSeam )
632           {
633             if ( helper.GetPeriodicIndex() && 1 ) {
634               seg.epgeominfo[ 0 ].u = otherSeamParam;
635               seg.epgeominfo[ 1 ].u = otherSeamParam;
636               swap (seg.epgeominfo[0].v, seg.epgeominfo[1].v);
637             } else {
638               seg.epgeominfo[ 0 ].v = otherSeamParam;
639               seg.epgeominfo[ 1 ].v = otherSeamParam;
640               swap (seg.epgeominfo[0].u, seg.epgeominfo[1].u);
641             }
642             swap (seg[0], seg[1]);
643             swap (seg.epgeominfo[0].dist, seg.epgeominfo[1].dist);
644             seg.edgenr = ngMesh.GetNSeg() + 1; // segment id
645             ngMesh.AddSegment (seg);
646 #ifdef DUMP_SEGMENTS
647             cout << "Segment: " << seg.edgenr << endl
648                  << "\t is SEAM (reverse) of the previous. "
649                  << " Other " << (helper.GetPeriodicIndex() && 1 ? "U" : "V")
650                  << " = " << otherSeamParam << endl;
651 #endif
652           }
653           else if ( fOri == TopAbs_INTERNAL )
654           {
655             swap (seg[0], seg[1]);
656             swap( seg.epgeominfo[0], seg.epgeominfo[1] );
657             seg.edgenr = ngMesh.GetNSeg() + 1; // segment id
658             ngMesh.AddSegment (seg);
659 #ifdef DUMP_SEGMENTS
660             cout << "Segment: " << seg.edgenr << endl << "\t is REVERSE of the previous" << endl;
661 #endif
662           }
663         }
664       } // loop on geomEdge ancestors
665
666       break;
667     } // case TopAbs_EDGE
668
669     case TopAbs_FACE: { // FACE
670       // ----------------------
671       const TopoDS_Face& geomFace  = TopoDS::Face( sm->GetSubShape() );
672       helper.SetSubShape( geomFace );
673       bool isInternalFace = ( geomFace.Orientation() == TopAbs_INTERNAL );
674
675       // Find solids the geomFace bounds
676       int solidID1 = 0, solidID2 = 0;
677       PShapeIteratorPtr solidIt = helper.GetAncestors( geomFace, *sm->GetFather(), TopAbs_SOLID);
678       while ( const TopoDS_Shape * solid = solidIt->next() )
679       {
680         int id = occgeom.somap.FindIndex ( *solid );
681         if ( solidID1 && id != solidID1 ) solidID2 = id;
682         else                              solidID1 = id;
683       }
684       faceID++;
685       //_faceDescriptors[ faceID ].first  = solidID1;
686       //_faceDescriptors[ faceID ].second = solidID2;
687       // Add ng face descriptors of meshed faces
688       ngMesh.AddFaceDescriptor (netgen::FaceDescriptor(faceID, solidID1, solidID2, 0));
689
690       // Orient the face correctly in solidID1 (issue 0020206)
691       bool reverse = false;
692       if ( solidID1 ) {
693         TopoDS_Shape solid = occgeom.somap( solidID1 );
694         TopAbs_Orientation faceOriInSolid = helper.GetSubShapeOri( solid, geomFace );
695         if ( faceOriInSolid >= 0 )
696           reverse = SMESH_Algo::IsReversedSubMesh
697             ( TopoDS::Face( geomFace.Oriented( faceOriInSolid )), helper.GetMeshDS() );
698       }
699
700       // Add surface elements
701
702       netgen::Element2d tri(3);
703       tri.SetIndex ( faceID );
704
705
706 #ifdef DUMP_TRIANGLES
707       cout << "SMESH face " << helper.GetMeshDS()->ShapeToIndex( geomFace )
708            << " internal="<<isInternalFace<< " border="<<isBorderFace << endl;
709 #endif
710       SMDS_ElemIteratorPtr faces = smDS->GetElements();
711       while ( faces->more() )
712       {
713         const SMDS_MeshElement* f = faces->next();
714         if ( f->NbNodes() % 3 != 0 ) // not triangle
715         {
716           PShapeIteratorPtr solidIt=helper.GetAncestors(geomFace,*sm->GetFather(),TopAbs_SOLID);
717           if ( const TopoDS_Shape * solid = solidIt->next() )
718             sm = _mesh->GetSubMesh( *solid );
719           SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
720           smError.reset( new SMESH_ComputeError(COMPERR_BAD_INPUT_MESH,"Not triangle submesh"));
721           smError->myBadElements.push_back( f );
722           return false;
723         }
724
725         for ( int i = 0; i < 3; ++i )
726         {
727           const SMDS_MeshNode* node = f->GetNode( i ), * inFaceNode=0;
728
729           // get node UV on face
730           int shapeID = node->getshapeId();
731           if ( helper.IsSeamShape( shapeID ))
732             if ( helper.IsSeamShape( f->GetNodeWrap( i+1 )->getshapeId() ))
733               inFaceNode = f->GetNodeWrap( i-1 );
734             else 
735               inFaceNode = f->GetNodeWrap( i+1 );
736           gp_XY uv = helper.GetNodeUV( geomFace, node, inFaceNode );
737
738           int ind = reverse ? 3-i : i+1;
739           tri.GeomInfoPi(ind).u = uv.X();
740           tri.GeomInfoPi(ind).v = uv.Y();
741           tri.PNum      (ind) = ngNodeId( node, ngMesh, nodeNgIdMap );
742         }
743
744         ngMesh.AddSurfaceElement (tri);
745 #ifdef DUMP_TRIANGLES
746         cout << tri << endl;
747 #endif
748
749         if ( isInternalFace )
750         {
751           swap( tri[1], tri[2] );
752           ngMesh.AddSurfaceElement (tri);
753 #ifdef DUMP_TRIANGLES
754         cout << tri << endl;
755 #endif
756         }
757       }
758       break;
759     } // case TopAbs_FACE
760
761     case TopAbs_VERTEX: { // VERTEX
762       // --------------------------
763       SMDS_NodeIteratorPtr nodeIt = smDS->GetNodes();
764       if ( nodeIt->more() )
765         ngNodeId( nodeIt->next(), ngMesh, nodeNgIdMap );
766       break;
767     }
768     default:;
769     } // switch
770   } // loop on submeshes
771
772   // fill nodeVec
773   nodeVec.resize( ngMesh.GetNP() + 1 );
774   TNode2IdMap::iterator node_NgId, nodeNgIdEnd = nodeNgIdMap.end();
775   for ( node_NgId = nodeNgIdMap.begin(); node_NgId != nodeNgIdEnd; ++node_NgId)
776     nodeVec[ node_NgId->second ] = node_NgId->first;
777
778   return true;
779 }
780
781 //================================================================================
782 /*!
783  * \brief Duplicate mesh faces on internal geom faces
784  */
785 //================================================================================
786
787 void NETGENPlugin_Mesher::fixIntFaces(const netgen::OCCGeometry& occgeom,
788                                       netgen::Mesh&              ngMesh,
789                                       NETGENPlugin_Internals&    internalShapes)
790 {
791   SMESHDS_Mesh* meshDS = internalShapes.getMesh().GetMeshDS();
792   
793   // find ng indices of internal faces
794   set<int> ngFaceIds;
795   for ( int ngFaceID = 1; ngFaceID <= occgeom.fmap.Extent(); ++ngFaceID )
796   {
797     int smeshID = meshDS->ShapeToIndex( occgeom.fmap( ngFaceID ));
798     if ( internalShapes.isInternalShape( smeshID ))
799       ngFaceIds.insert( ngFaceID );
800   }
801   if ( !ngFaceIds.empty() )
802   {
803     // duplicate faces
804     int i, nbFaces = ngMesh.GetNSE();
805     for (int i = 1; i <= nbFaces; ++i)
806     {
807       netgen::Element2d elem = ngMesh.SurfaceElement(i);
808       if ( ngFaceIds.count( elem.GetIndex() ))
809       {
810         swap( elem[1], elem[2] );
811         ngMesh.AddSurfaceElement (elem);
812       }
813     }
814   }
815 }
816
817 namespace
818 {
819   //================================================================================
820   // define gp_XY_Subtracted pointer to function calling gp_XY::Subtracted(gp_XY)
821   gp_XY_FunPtr(Subtracted);
822   //gp_XY_FunPtr(Added);
823
824   //================================================================================
825   /*!
826    * \brief Evaluate distance between two 2d points along the surface
827    */
828   //================================================================================
829
830   double evalDist( const gp_XY&                uv1,
831                    const gp_XY&                uv2,
832                    const Handle(Geom_Surface)& surf,
833                    const int                   stopHandler=-1)
834   {
835     if ( stopHandler > 0 ) // continue recursion
836     {
837       gp_XY mid = SMESH_MesherHelper::GetMiddleUV( surf, uv1, uv2 );
838       return evalDist( uv1,mid, surf, stopHandler-1 ) + evalDist( mid,uv2, surf, stopHandler-1 );
839     }
840     double dist3D = surf->Value( uv1.X(), uv1.Y() ).Distance( surf->Value( uv2.X(), uv2.Y() ));
841     if ( stopHandler == 0 ) // stop recursion
842       return dist3D;
843     
844     // start recursion if necessary
845     double dist2D = SMESH_MesherHelper::applyIn2D(surf, uv1, uv2, gp_XY_Subtracted, 0).Modulus();
846     if ( fabs( dist3D - dist2D ) < dist2D * 1e-10 )
847       return dist3D; // equal parametrization of a planar surface
848
849     return evalDist( uv1, uv2, surf, 3 ); // start recursion
850   }
851
852   //================================================================================
853   /*!
854    * \brief Data of vertex internal in geom face
855    */
856   //================================================================================
857
858   struct TIntVData
859   {
860     gp_XY uv;        //!< UV in face parametric space
861     int   ngId;      //!< ng id of corrsponding node
862     gp_XY uvClose;   //!< UV of closest boundary node
863     int   ngIdClose; //!< ng id of closest boundary node
864   };
865
866   //================================================================================
867   /*!
868    * \brief Data of vertex internal in solid
869    */
870   //================================================================================
871
872   struct TIntVSoData
873   {
874     int   ngId;      //!< ng id of corresponding node
875     int   ngIdClose; //!< ng id of closest 2d mesh element
876     int   ngIdCloseN; //!< ng id of closest node of the closest 2d mesh element
877   };
878
879   inline double dist2(const netgen::MeshPoint& p1, const netgen::MeshPoint& p2)
880   {
881     return gp_Pnt( NGPOINT_COORDS(p1)).SquareDistance( gp_Pnt( NGPOINT_COORDS(p2)));
882   }
883 }
884
885 //================================================================================
886 /*!
887  * \brief Make netgen take internal vertices in faces into account by adding
888  *        segments including internal vertices
889  *
890  * This function works in supposition that 1D mesh is already computed in ngMesh
891  */
892 //================================================================================
893
894 void NETGENPlugin_Mesher::addIntVerticesInFaces(const netgen::OCCGeometry&     occgeom,
895                                                 netgen::Mesh&                  ngMesh,
896                                                 vector<const SMDS_MeshNode*>&  nodeVec,
897                                                 NETGENPlugin_Internals&        internalShapes)
898 {
899   if ( nodeVec.size() < ngMesh.GetNP() )
900     nodeVec.resize( ngMesh.GetNP(), 0 );
901
902   SMESHDS_Mesh* meshDS = internalShapes.getMesh().GetMeshDS();
903   SMESH_MesherHelper helper( internalShapes.getMesh() );
904
905   const map<int,list<int> >& face2Vert = internalShapes.getFacesWithVertices();
906   map<int,list<int> >::const_iterator f2v = face2Vert.begin();
907   for ( ; f2v != face2Vert.end(); ++f2v )
908   {
909     const TopoDS_Face& face = TopoDS::Face( meshDS->IndexToShape( f2v->first ));
910     if ( face.IsNull() ) continue;
911     int faceNgID = occgeom.fmap.FindIndex (face);
912     if ( faceNgID < 0 ) continue;
913
914     TopLoc_Location loc;
915     Handle(Geom_Surface) surf = BRep_Tool::Surface(face,loc);
916
917     helper.SetSubShape( face );
918     helper.SetElementsOnShape( true );
919
920     // Get data of internal vertices and add them to ngMesh
921
922     multimap< double, TIntVData > dist2VData; // sort vertices by distance from boundary nodes
923
924     int i, nbSegInit = ngMesh.GetNSeg();
925
926     // boundary characteristics
927     double totSegLen2D = 0;
928     int totNbSeg = 0;
929
930     const list<int>& iVertices = f2v->second;
931     list<int>::const_iterator iv = iVertices.begin();
932     for ( int nbV = 0; iv != iVertices.end(); ++iv, nbV++ )
933     {
934       TIntVData vData;
935       // get node on vertex
936       const TopoDS_Vertex V = TopoDS::Vertex( meshDS->IndexToShape( *iv ));
937       const SMDS_MeshNode * nV = SMESH_Algo::VertexNode( V, meshDS );
938       if ( !nV )
939       {
940         SMESH_subMesh* sm = helper.GetMesh()->GetSubMesh( V );
941         sm->ComputeStateEngine( SMESH_subMesh::COMPUTE );
942         nV = SMESH_Algo::VertexNode( V, meshDS );
943         if ( !nV ) continue;
944       }
945       // add ng node
946       netgen::MeshPoint mp( netgen::Point<3> (nV->X(), nV->Y(), nV->Z()) );
947       ngMesh.AddPoint ( mp, 1, netgen::EDGEPOINT );
948       vData.ngId = ngMesh.GetNP();
949       nodeVec.push_back( nV );
950
951       // get node UV
952       bool uvOK = false;
953       vData.uv = helper.GetNodeUV( face, nV, 0, &uvOK );
954       if ( !uvOK ) helper.CheckNodeUV( face, nV, vData.uv, BRep_Tool::Tolerance(V),/*force=*/1);
955
956       // loop on all segments of the face to find the node closest to vertex and to count
957       // average segment 2d length
958       double closeDist2 = numeric_limits<double>::max(), dist2;
959       int ngIdLast = 0;
960       for (i = 1; i <= ngMesh.GetNSeg(); ++i)
961       {
962         netgen::Segment & seg = ngMesh.LineSegment(i);
963         if ( seg.si != faceNgID ) continue;
964         gp_XY uv[2];
965         for ( int iEnd = 0; iEnd < 2; ++iEnd)
966         {
967           uv[iEnd].SetCoord( seg.epgeominfo[iEnd].u, seg.epgeominfo[iEnd].v );
968           if ( ngIdLast == seg[ iEnd ] ) continue;
969           dist2 = helper.applyIn2D(surf, uv[iEnd], vData.uv, gp_XY_Subtracted,0).SquareModulus();
970           if ( dist2 < closeDist2 )
971             vData.ngIdClose = seg[ iEnd ], vData.uvClose = uv[iEnd], closeDist2 = dist2;
972           ngIdLast = seg[ iEnd ];
973         }
974         if ( !nbV )
975         {
976           totSegLen2D += helper.applyIn2D(surf, uv[0], uv[1], gp_XY_Subtracted, false).Modulus();
977           totNbSeg++;
978         }
979       }
980       dist2VData.insert( make_pair( closeDist2, vData ));
981     }
982
983     if ( totNbSeg == 0 ) break;
984     double avgSegLen2d = totSegLen2D / totNbSeg;
985
986     // Loop on vertices to add segments
987
988     multimap< double, TIntVData >::iterator dist_vData = dist2VData.begin();
989     for ( ; dist_vData != dist2VData.end(); ++dist_vData )
990     {
991       double closeDist2 = dist_vData->first, dist2;
992       TIntVData & vData = dist_vData->second;
993
994       // try to find more close node among segments added for internal vertices
995       for (i = nbSegInit+1; i <= ngMesh.GetNSeg(); ++i)
996       {
997         netgen::Segment & seg = ngMesh.LineSegment(i);
998         if ( seg.si != faceNgID ) continue;
999         gp_XY uv[2];
1000         for ( int iEnd = 0; iEnd < 2; ++iEnd)
1001         {
1002           uv[iEnd].SetCoord( seg.epgeominfo[iEnd].u, seg.epgeominfo[iEnd].v );
1003           dist2 = helper.applyIn2D(surf, uv[iEnd], vData.uv, gp_XY_Subtracted,0).SquareModulus();
1004           if ( dist2 < closeDist2 )
1005             vData.ngIdClose = seg[ iEnd ], vData.uvClose = uv[iEnd], closeDist2 = dist2;
1006         }
1007       }
1008       // decide whether to use the closest node as the second end of segment or to
1009       // create a new point
1010       int segEnd1 = vData.ngId;
1011       int segEnd2 = vData.ngIdClose; // to use closest node
1012       gp_XY uvV = vData.uv, uvP = vData.uvClose;
1013       double segLenHint  = ngMesh.GetH( ngMesh.Point( vData.ngId ));
1014       double nodeDist2D  = sqrt( closeDist2 );
1015       double nodeDist3D  = evalDist( vData.uv, vData.uvClose, surf );
1016       bool avgLenOK  = ( avgSegLen2d < 0.75 * nodeDist2D );
1017       bool hintLenOK = ( segLenHint  < 0.75 * nodeDist3D );
1018       //cout << "uvV " << uvV.X() <<","<<uvV.Y() << " ";
1019       if ( hintLenOK || avgLenOK )
1020       {
1021         // create a point between the closest node and V
1022
1023         // how far from V
1024         double r = min( 0.5, ( hintLenOK ? segLenHint/nodeDist3D : avgSegLen2d/nodeDist2D ));
1025         // direction from V to closet node in 2D
1026         gp_Dir2d v2n( helper.applyIn2D(surf, uvP, uvV, gp_XY_Subtracted, false ));
1027         // new point
1028         uvP = vData.uv + r * nodeDist2D * v2n.XY();
1029         gp_Pnt P = surf->Value( uvP.X(), uvP.Y() ).Transformed( loc );
1030
1031         netgen::MeshPoint mp( netgen::Point<3> (P.X(), P.Y(), P.Z()));
1032         ngMesh.AddPoint ( mp, 1, netgen::EDGEPOINT );
1033         segEnd2 = ngMesh.GetNP();
1034         //cout << "Middle " << r << " uv " << uvP.X() << "," << uvP.Y() << "( " << ngMesh.Point(segEnd2).X()<<","<<ngMesh.Point(segEnd2).Y()<<","<<ngMesh.Point(segEnd2).Z()<<" )"<< endl;
1035         SMDS_MeshNode * nP = helper.AddNode(P.X(), P.Y(), P.Z());
1036         nodeVec.push_back( nP );
1037       }
1038       //else cout << "at Node " << " uv " << uvP.X() << "," << uvP.Y() << endl;
1039
1040       // Add the segment
1041       netgen::Segment seg;
1042
1043       if ( segEnd1 > segEnd2 ) swap( segEnd1, segEnd2 ), swap( uvV, uvP );
1044       seg[0] = segEnd1;  // ng node id
1045       seg[1] = segEnd2;  // ng node id
1046       seg.edgenr = ngMesh.GetNSeg() + 1;// segment id
1047       seg.si = faceNgID;
1048
1049       seg.epgeominfo[ 0 ].dist = 0; // param on curve
1050       seg.epgeominfo[ 0 ].u    = uvV.X();
1051       seg.epgeominfo[ 0 ].v    = uvV.Y();
1052       seg.epgeominfo[ 1 ].dist = 1; // param on curve
1053       seg.epgeominfo[ 1 ].u    = uvP.X();
1054       seg.epgeominfo[ 1 ].v    = uvP.Y();
1055
1056 //       seg.epgeominfo[ 0 ].edgenr = 10; //  = geom.emap.FindIndex(edge);
1057 //       seg.epgeominfo[ 1 ].edgenr = 10; //  = geom.emap.FindIndex(edge);
1058
1059       ngMesh.AddSegment (seg);
1060
1061       // add reverse segment
1062       swap (seg[0], seg[1]);
1063       swap( seg.epgeominfo[0], seg.epgeominfo[1] );
1064       seg.edgenr = ngMesh.GetNSeg() + 1; // segment id
1065       ngMesh.AddSegment (seg);
1066     }
1067
1068   }
1069 }
1070
1071 //================================================================================
1072 /*!
1073  * \brief Make netgen take internal vertices in solids into account by adding
1074  *        faces including internal vertices
1075  *
1076  * This function works in supposition that 2D mesh is already computed in ngMesh
1077  */
1078 //================================================================================
1079
1080 void NETGENPlugin_Mesher::addIntVerticesInSolids(const netgen::OCCGeometry&     occgeom,
1081                                                  netgen::Mesh&                  ngMesh,
1082                                                  vector<const SMDS_MeshNode*>&  nodeVec,
1083                                                  NETGENPlugin_Internals&        internalShapes)
1084 {
1085 #ifdef DUMP_TRIANGLES_SCRIPT
1086   // create a python script making a mesh containing triangles added for internal vertices
1087   ofstream py(DUMP_TRIANGLES_SCRIPT);
1088   py << "from smesh import * "<< endl
1089      << "m = Mesh(name='triangles')" << endl;
1090 #endif
1091   if ( nodeVec.size() < ngMesh.GetNP() )
1092     nodeVec.resize( ngMesh.GetNP(), 0 );
1093
1094   SMESHDS_Mesh* meshDS = internalShapes.getMesh().GetMeshDS();
1095   SMESH_MesherHelper helper( internalShapes.getMesh() );
1096
1097   const map<int,list<int> >& so2Vert = internalShapes.getSolidsWithVertices();
1098   map<int,list<int> >::const_iterator s2v = so2Vert.begin();
1099   for ( ; s2v != so2Vert.end(); ++s2v )
1100   {
1101     const TopoDS_Shape& solid = meshDS->IndexToShape( s2v->first );
1102     if ( solid.IsNull() ) continue;
1103     int solidNgID = occgeom.somap.FindIndex (solid);
1104     if ( solidNgID < 0 && !occgeom.somap.IsEmpty() ) continue;
1105
1106     helper.SetSubShape( solid );
1107     helper.SetElementsOnShape( true );
1108
1109     // find ng indices of faces within the solid
1110     set<int> ngFaceIds;
1111     for (TopExp_Explorer fExp(solid, TopAbs_FACE); fExp.More(); fExp.Next() )
1112       ngFaceIds.insert( occgeom.fmap.FindIndex( fExp.Current() ));
1113     if ( ngFaceIds.size() == 1 && *ngFaceIds.begin() == 0 )
1114       ngFaceIds.insert( 1 );
1115
1116     // Get data of internal vertices and add them to ngMesh
1117
1118     multimap< double, TIntVSoData > dist2VData; // sort vertices by distance from ng faces
1119
1120     int i, nbFaceInit = ngMesh.GetNSE();
1121
1122     // boundary characteristics
1123     double totSegLen = 0;
1124     int totNbSeg = 0;
1125
1126     const list<int>& iVertices = s2v->second;
1127     list<int>::const_iterator iv = iVertices.begin();
1128     for ( int nbV = 0; iv != iVertices.end(); ++iv, nbV++ )
1129     {
1130       TIntVSoData vData;
1131       const TopoDS_Vertex V = TopoDS::Vertex( meshDS->IndexToShape( *iv ));
1132
1133       // get node on vertex
1134       const SMDS_MeshNode * nV = SMESH_Algo::VertexNode( V, meshDS );
1135       if ( !nV )
1136       {
1137         SMESH_subMesh* sm = helper.GetMesh()->GetSubMesh( V );
1138         sm->ComputeStateEngine( SMESH_subMesh::COMPUTE );
1139         nV = SMESH_Algo::VertexNode( V, meshDS );
1140         if ( !nV ) continue;
1141       }
1142       // add ng node
1143       netgen::MeshPoint mpV( netgen::Point<3> (nV->X(), nV->Y(), nV->Z()) );
1144       ngMesh.AddPoint ( mpV, 1, netgen::FIXEDPOINT );
1145       vData.ngId = ngMesh.GetNP();
1146       nodeVec.push_back( nV );
1147
1148       // loop on all 2d elements to find the one closest to vertex and to count
1149       // average segment length
1150       double closeDist2 = numeric_limits<double>::max(), avgDist2;
1151       for (i = 1; i <= ngMesh.GetNSE(); ++i)
1152       {
1153         const netgen::Element2d& elem = ngMesh.SurfaceElement(i);
1154         if ( !ngFaceIds.count( elem.GetIndex() )) continue;
1155         avgDist2 = 0;
1156         multimap< double, int> dist2nID; // sort nodes of element by distance from V
1157         for ( int j = 0; j < elem.GetNP(); ++j)
1158         {
1159           netgen::MeshPoint mp = ngMesh.Point( elem[j] );
1160           double d2 = dist2( mpV, mp );
1161           dist2nID.insert( make_pair( d2, elem[j] ));
1162           avgDist2 += d2 / elem.GetNP();
1163           if ( !nbV )
1164             totNbSeg++, totSegLen+= sqrt( dist2( mp, ngMesh.Point( elem[(j+1)%elem.GetNP()])));
1165         }
1166         double dist = dist2nID.begin()->first; //avgDist2;
1167         if ( dist < closeDist2 )
1168           vData.ngIdClose= i, vData.ngIdCloseN= dist2nID.begin()->second, closeDist2= dist;
1169       }
1170       dist2VData.insert( make_pair( closeDist2, vData ));
1171     }
1172
1173     if ( totNbSeg == 0 ) break;
1174     double avgSegLen = totSegLen / totNbSeg;
1175
1176     // Loop on vertices to add triangles
1177
1178     multimap< double, TIntVSoData >::iterator dist_vData = dist2VData.begin();
1179     for ( ; dist_vData != dist2VData.end(); ++dist_vData )
1180     {
1181       double closeDist2   = dist_vData->first;
1182       TIntVSoData & vData = dist_vData->second;
1183
1184       const netgen::MeshPoint& mpV = ngMesh.Point( vData.ngId );
1185
1186       // try to find more close face among ones added for internal vertices
1187       for (i = nbFaceInit+1; i <= ngMesh.GetNSE(); ++i)
1188       {
1189         double avgDist2 = 0;
1190         multimap< double, int> dist2nID;
1191         const netgen::Element2d& elem = ngMesh.SurfaceElement(i);
1192         for ( int j = 0; j < elem.GetNP(); ++j)
1193         {
1194           double d = dist2( mpV, ngMesh.Point( elem[j] ));
1195           dist2nID.insert( make_pair( d, elem[j] ));
1196           avgDist2 += d / elem.GetNP();
1197           if ( avgDist2 < closeDist2 )
1198             vData.ngIdClose= i, vData.ngIdCloseN= dist2nID.begin()->second, closeDist2= avgDist2;
1199         }
1200       }
1201       // sort nodes of the closest face by angle with vector from V to the closest node
1202       const double tol = numeric_limits<double>::min();
1203       map< double, int > angle2ID;
1204       const netgen::Element2d& closeFace = ngMesh.SurfaceElement( vData.ngIdClose );
1205       netgen::MeshPoint mp[2];
1206       mp[0] = ngMesh.Point( vData.ngIdCloseN );
1207       gp_XYZ p1( NGPOINT_COORDS( mp[0] ));
1208       gp_XYZ pV( NGPOINT_COORDS( mpV ));
1209       gp_Vec v2p1( pV, p1 );
1210       double distN1 = v2p1.Magnitude();
1211       if ( distN1 <= tol ) continue;
1212       v2p1 /= distN1;
1213       for ( int j = 0; j < closeFace.GetNP(); ++j)
1214       {
1215         mp[1] = ngMesh.Point( closeFace[j] );
1216         gp_Vec v2p( pV, gp_Pnt( NGPOINT_COORDS( mp[1] )) );
1217         angle2ID.insert( make_pair( v2p1.Angle( v2p ), closeFace[j]));
1218       }
1219       // get node with angle of 60 degrees or greater
1220       map< double, int >::iterator angle_id = angle2ID.lower_bound( 60*PI180 );
1221       if ( angle_id == angle2ID.end() ) angle_id = --angle2ID.end();
1222       const double minAngle = 30 * PI180;
1223       const double angle = angle_id->first;
1224       bool angleOK = ( angle > minAngle );
1225
1226       // find points to create a triangle
1227       netgen::Element2d tri(3);
1228       tri.SetIndex ( 1 );
1229       tri[0] = vData.ngId;
1230       tri[1] = vData.ngIdCloseN; // to use the closest nodes
1231       tri[2] = angle_id->second; // to use the node with best angle
1232
1233       // decide whether to use the closest node and the node with best angle or to create new ones
1234       for ( int isBestAngleN = 0; isBestAngleN < 2; ++isBestAngleN )
1235       {
1236         bool createNew = !angleOK, distOK = true;
1237         double distFromV;
1238         int triInd = isBestAngleN ? 2 : 1;
1239         mp[isBestAngleN] = ngMesh.Point( tri[triInd] );
1240         if ( isBestAngleN )
1241         {
1242           if ( angleOK )
1243           {
1244             double distN2 = sqrt( dist2( mpV, mp[isBestAngleN]));
1245             createNew = ( fabs( distN2 - distN1 ) > 0.25 * distN1 );
1246           }
1247           else if ( angle < tol )
1248           {
1249             v2p1.SetX( v2p1.X() + 1e-3 );
1250           }
1251           distFromV = distN1;
1252         }
1253         else
1254         {
1255           double segLenHint = ngMesh.GetH( ngMesh.Point( vData.ngId ));
1256           bool avgLenOK  = ( avgSegLen < 0.75 * distN1 );
1257           bool hintLenOK = ( segLenHint  < 0.75 * distN1 );
1258           createNew = (createNew || avgLenOK || hintLenOK );
1259           // we create a new node not closer than 0.5 to the closest face
1260           // in order not to clash with other close face
1261           double r = min( 0.5, ( hintLenOK ? segLenHint : avgSegLen ) / distN1 );
1262           distFromV = r * distN1;
1263         }
1264         if ( createNew )
1265         {
1266           // create a new point, between the node and the vertex if angleOK
1267           gp_XYZ p( NGPOINT_COORDS( mp[isBestAngleN] ));
1268           gp_Vec v2p( pV, p ); v2p.Normalize();
1269           if ( isBestAngleN && !angleOK )
1270             p = p1 + gp_Dir( v2p.XYZ() - v2p1.XYZ()).XYZ() * distN1 * 0.95;
1271           else
1272             p = pV + v2p.XYZ() * distFromV;
1273
1274           if ( !isBestAngleN ) p1 = p, distN1 = distFromV;
1275
1276           mp[isBestAngleN].SetPoint( netgen::Point<3> (p.X(), p.Y(), p.Z()));
1277           ngMesh.AddPoint ( mp[isBestAngleN], 1, netgen::SURFACEPOINT );
1278           tri[triInd] = ngMesh.GetNP();
1279           nodeVec.push_back( helper.AddNode( p.X(), p.Y(), p.Z()) );
1280         }
1281       }
1282       ngMesh.AddSurfaceElement (tri);
1283       swap( tri[1], tri[2] );
1284       ngMesh.AddSurfaceElement (tri);
1285
1286 #ifdef DUMP_TRIANGLES_SCRIPT
1287       py << "n1 = m.AddNode( "<< mpV.X()<<", "<< mpV.Y()<<", "<< mpV.Z()<<") "<< endl
1288          << "n2 = m.AddNode( "<< mp[0].X()<<", "<< mp[0].Y()<<", "<< mp[0].Z()<<") "<< endl
1289          << "n3 = m.AddNode( "<< mp[1].X()<<", "<< mp[1].Y()<<", "<< mp[1].Z()<<" )" << endl
1290          << "m.AddFace([n1,n2,n3])" << endl;
1291 #endif
1292     } // loop on internal vertices of a solid
1293
1294   } // loop on solids with internal vertices
1295 }
1296
1297 //================================================================================
1298 /*!
1299  * \brief Fill SMESH mesh according to contents of netgen mesh
1300  *  \param occgeo - container of OCCT geometry to mesh
1301  *  \param ngMesh - netgen mesh
1302  *  \param initState - bn of entities in netgen mesh before computing
1303  *  \param sMesh - SMESH mesh to fill in
1304  *  \param nodeVec - vector of nodes in which node index == netgen ID
1305  *  \retval int - error
1306  */
1307 //================================================================================
1308
1309 int NETGENPlugin_Mesher::FillSMesh(const netgen::OCCGeometry&          occgeo,
1310                                    const netgen::Mesh&                 ngMesh,
1311                                    const NETGENPlugin_ngMeshInfo&      initState,
1312                                    SMESH_Mesh&                         sMesh,
1313                                    std::vector<const SMDS_MeshNode*>&  nodeVec,
1314                                    SMESH_Comment&                      comment)
1315 {
1316   int nbNod = ngMesh.GetNP();
1317   int nbSeg = ngMesh.GetNSeg();
1318   int nbFac = ngMesh.GetNSE();
1319   int nbVol = ngMesh.GetNE();
1320
1321   SMESHDS_Mesh* meshDS = sMesh.GetMeshDS();
1322
1323   // create and insert nodes into nodeVec
1324   nodeVec.resize( nbNod + 1 );
1325   int i, nbInitNod = initState._nbNodes;
1326   for (i = nbInitNod+1; i <= nbNod; ++i )
1327   {
1328     const netgen::MeshPoint& ngPoint = ngMesh.Point(i);
1329     SMDS_MeshNode* node = NULL;
1330     TopoDS_Vertex aVert;
1331     // First, netgen creates nodes on vertices in occgeo.vmap,
1332     // so node index corresponds to vertex index
1333     // but (issue 0020776) netgen does not create nodes with equal coordinates
1334     if ( i-nbInitNod <= occgeo.vmap.Extent() )
1335     {
1336       gp_Pnt p ( NGPOINT_COORDS(ngPoint) );
1337       for (int iV = i-nbInitNod; aVert.IsNull() && iV <= occgeo.vmap.Extent(); ++iV)
1338       {
1339         aVert = TopoDS::Vertex( occgeo.vmap( iV ) );
1340         gp_Pnt pV = BRep_Tool::Pnt( aVert );
1341         if ( p.SquareDistance( pV ) > 1e-20 )
1342           aVert.Nullify();
1343         else
1344           node = const_cast<SMDS_MeshNode*>( SMESH_Algo::VertexNode( aVert, meshDS ));
1345       }
1346     }
1347     if (!node) // node not found on vertex
1348     {
1349       node = meshDS->AddNode( NGPOINT_COORDS( ngPoint ));
1350       if (!aVert.IsNull())
1351         meshDS->SetNodeOnVertex(node, aVert);
1352     }
1353     nodeVec[i] = node;
1354   }
1355
1356   // create mesh segments along geometric edges
1357   int nbInitSeg = initState._nbSegments;
1358   for (i = nbInitSeg+1; i <= nbSeg; ++i )
1359   {
1360     const netgen::Segment& seg = ngMesh.LineSegment(i);
1361     TopoDS_Edge aEdge;
1362 #ifdef NETGEN_NEW
1363     int pinds[3] = { seg.pnums[0], seg.pnums[1], seg.pnums[2] };
1364 #else
1365     int pinds[3] = { seg.p1, seg.p2, seg.pmid };
1366 #endif
1367     int nbp = 0;
1368     double param2 = 0;
1369     for (int j=0; j < 3; ++j)
1370     {
1371       int pind = pinds[j];
1372       if (pind <= 0 || !nodeVec_ACCESS(pind))
1373         break;
1374       ++nbp;
1375       double param;
1376       if (j < 2)
1377       {
1378         if (aEdge.IsNull())
1379         {
1380           int aGeomEdgeInd = seg.epgeominfo[j].edgenr;
1381           if (aGeomEdgeInd > 0 && aGeomEdgeInd <= occgeo.emap.Extent())
1382             aEdge = TopoDS::Edge(occgeo.emap(aGeomEdgeInd));
1383         }
1384         param = seg.epgeominfo[j].dist;
1385         param2 += param;
1386       }
1387       else // middle point
1388       {
1389         param = param2 * 0.5;
1390       }
1391       if (!aEdge.IsNull() && nodeVec_ACCESS(pind)->getshapeId() < 1)
1392       {
1393         meshDS->SetNodeOnEdge(nodeVec_ACCESS(pind), aEdge, param);
1394       }
1395     }
1396     if ( nbp > 1 )
1397     {
1398       SMDS_MeshEdge* edge = 0;
1399       if (nbp == 2) // second order ?
1400       {
1401         if ( meshDS->FindEdge( nodeVec_ACCESS(pinds[0]), nodeVec_ACCESS(pinds[1])))
1402           continue;
1403         edge = meshDS->AddEdge(nodeVec_ACCESS(pinds[0]), nodeVec_ACCESS(pinds[1]));
1404       }
1405       else
1406       {
1407         if ( meshDS->FindEdge( nodeVec_ACCESS(pinds[0]), nodeVec_ACCESS(pinds[1]),
1408                                nodeVec_ACCESS(pinds[2])))
1409           continue;
1410         edge = meshDS->AddEdge(nodeVec_ACCESS(pinds[0]), nodeVec_ACCESS(pinds[1]),
1411                                nodeVec_ACCESS(pinds[2]));
1412       }
1413       if (!edge)
1414       {
1415         if ( comment.empty() ) comment << "Cannot create a mesh edge";
1416         MESSAGE("Cannot create a mesh edge");
1417         nbSeg = nbFac = nbVol = 0;
1418         break;
1419       }
1420       if ( !aEdge.IsNull() && edge->getshapeId() < 1 )
1421         meshDS->SetMeshElementOnShape(edge, aEdge);
1422     }
1423     else if ( comment.empty() )
1424     {
1425       comment << "Invalid netgen segment #" << i;
1426     }
1427   }
1428
1429   // create mesh faces along geometric faces
1430   int nbInitFac = initState._nbFaces;
1431   for (i = nbInitFac+1; i <= nbFac; ++i )
1432   {
1433     const netgen::Element2d& elem = ngMesh.SurfaceElement(i);
1434     int aGeomFaceInd = elem.GetIndex();
1435     TopoDS_Face aFace;
1436     if (aGeomFaceInd > 0 && aGeomFaceInd <= occgeo.fmap.Extent())
1437       aFace = TopoDS::Face(occgeo.fmap(aGeomFaceInd));
1438     vector<SMDS_MeshNode*> nodes;
1439     for (int j=1; j <= elem.GetNP(); ++j)
1440     {
1441       int pind = elem.PNum(j);
1442       if ( pind < 1 || pind >= nodeVec.size() )
1443         break;
1444       if ( SMDS_MeshNode* node = nodeVec_ACCESS(pind))
1445       {
1446         nodes.push_back(node);
1447         if (!aFace.IsNull() && node->getshapeId() < 1)
1448         {
1449           const netgen::PointGeomInfo& pgi = elem.GeomInfoPi(j);
1450           meshDS->SetNodeOnFace(node, aFace, pgi.u, pgi.v);
1451         }
1452       }
1453     }
1454     if ( nodes.size() != elem.GetNP() )
1455     {
1456       if ( comment.empty() )
1457         comment << "Invalid netgen 2d element #" << i;
1458       continue; // bad node ids
1459     }
1460     SMDS_MeshFace* face = NULL;
1461     switch (elem.GetType())
1462     {
1463     case netgen::TRIG:
1464       face = meshDS->AddFace(nodes[0],nodes[1],nodes[2]);
1465       break;
1466     case netgen::QUAD:
1467       face = meshDS->AddFace(nodes[0],nodes[1],nodes[2],nodes[3]);
1468       break;
1469     case netgen::TRIG6:
1470       face = meshDS->AddFace(nodes[0],nodes[1],nodes[2],nodes[5],nodes[3],nodes[4]);
1471       break;
1472     case netgen::QUAD8:
1473       face = meshDS->AddFace(nodes[0],nodes[1],nodes[2],nodes[3],
1474                              nodes[4],nodes[7],nodes[5],nodes[6]);
1475       break;
1476     default:
1477       MESSAGE("NETGEN created a face of unexpected type, ignoring");
1478       continue;
1479     }
1480     if (!face)
1481     {
1482       if ( comment.empty() ) comment << "Cannot create a mesh face";
1483       MESSAGE("Cannot create a mesh face");
1484       nbSeg = nbFac = nbVol = 0;
1485       break;
1486     }
1487     if (!aFace.IsNull())
1488       meshDS->SetMeshElementOnShape(face, aFace);
1489   }
1490
1491   // create tetrahedra
1492   for (i = 1; i <= nbVol; ++i)
1493   {
1494     const netgen::Element& elem = ngMesh.VolumeElement(i);      
1495     int aSolidInd = elem.GetIndex();
1496     TopoDS_Solid aSolid;
1497     if (aSolidInd > 0 && aSolidInd <= occgeo.somap.Extent())
1498       aSolid = TopoDS::Solid(occgeo.somap(aSolidInd));
1499     vector<SMDS_MeshNode*> nodes;
1500     for (int j=1; j <= elem.GetNP(); ++j)
1501     {
1502       int pind = elem.PNum(j);
1503       if ( pind < 1 || pind >= nodeVec.size() )
1504         break;
1505       if ( SMDS_MeshNode* node = nodeVec_ACCESS(pind) )
1506       {
1507         nodes.push_back(node);
1508         if ( !aSolid.IsNull() && node->getshapeId() < 1 )
1509           meshDS->SetNodeInVolume(node, aSolid);
1510       }
1511     }
1512     if ( nodes.size() != elem.GetNP() )
1513     {
1514       if ( comment.empty() )
1515         comment << "Invalid netgen 3d element #" << i;
1516       continue;
1517     }
1518     SMDS_MeshVolume* vol = NULL;
1519     switch (elem.GetType())
1520     {
1521     case netgen::TET:
1522       vol = meshDS->AddVolume(nodes[0],nodes[1],nodes[2],nodes[3]);
1523       break;
1524     case netgen::TET10:
1525       vol = meshDS->AddVolume(nodes[0],nodes[1],nodes[2],nodes[3],
1526                               nodes[4],nodes[7],nodes[5],nodes[6],nodes[8],nodes[9]);
1527       break;
1528     default:
1529       MESSAGE("NETGEN created a volume of unexpected type, ignoring");
1530       continue;
1531     }
1532     if (!vol)
1533     {
1534       if ( comment.empty() ) comment << "Cannot create a mesh volume";
1535       MESSAGE("Cannot create a mesh volume");
1536       nbSeg = nbFac = nbVol = 0;
1537       break;
1538     }
1539     if (!aSolid.IsNull())
1540       meshDS->SetMeshElementOnShape(vol, aSolid);
1541   }
1542   return comment.empty() ? 0 : 1;
1543 }
1544
1545 namespace
1546 {
1547   //================================================================================
1548   /*!
1549    * \brief Restrict size of elements on the given edge 
1550    */
1551   //================================================================================
1552
1553   void setLocalSize(const TopoDS_Edge& edge,
1554                     double             size,
1555                     netgen::Mesh&      mesh)
1556   {
1557     const int nb = 1000;
1558     Standard_Real u1, u2;
1559     Handle(Geom_Curve) curve = BRep_Tool::Curve(edge, u1, u2);
1560     if ( curve.IsNull() )
1561     {
1562       TopoDS_Iterator vIt( edge );
1563       if ( !vIt.More() ) return;
1564       gp_Pnt p = BRep_Tool::Pnt( TopoDS::Vertex( vIt.Value() ));
1565       netgen::Point3d pi(p.X(), p.Y(), p.Z());
1566       mesh.RestrictLocalH(pi, size);
1567     }
1568     else
1569     {
1570       Standard_Real delta = (u2-u1)/nb;
1571       for(int i=0; i<nb; i++)
1572       {
1573         Standard_Real u = u1 + delta*i;
1574         gp_Pnt p = curve->Value(u);
1575         netgen::Point3d pi(p.X(), p.Y(), p.Z());
1576         mesh.RestrictLocalH(pi, size);
1577         double resultSize = mesh.GetH(pi);
1578         if ( resultSize - size > 0.1*size )
1579           // netgen does restriction iff oldH/newH > 1.2 (localh.cpp:136)
1580           mesh.RestrictLocalH(pi, resultSize/1.201);
1581       }
1582     }
1583   }
1584
1585   //================================================================================
1586   /*!
1587    * \brief Convert error into text
1588    */
1589   //================================================================================
1590
1591   std::string text(int err)
1592   {
1593     if ( !err )
1594       return string("");
1595     return
1596       SMESH_Comment("Error in netgen::OCCGenerateMesh() at ") << netgen::multithread.task;
1597   }
1598
1599   //================================================================================
1600   /*!
1601    * \brief Convert exception into text
1602    */
1603   //================================================================================
1604
1605   std::string text(Standard_Failure& ex)
1606   {
1607     SMESH_Comment str("Exception in  netgen::OCCGenerateMesh()");
1608     str << " at " << netgen::multithread.task
1609         << ": " << ex.DynamicType()->Name();
1610     if ( ex.GetMessageString() && strlen( ex.GetMessageString() ))
1611       str << ": " << ex.GetMessageString();
1612     return str;
1613   }
1614 }
1615
1616 //=============================================================================
1617 /*!
1618  * Here we are going to use the NETGEN mesher
1619  */
1620 //=============================================================================
1621
1622 bool NETGENPlugin_Mesher::Compute()
1623 {
1624   NETGENPlugin_NetgenLibWrapper ngLib;
1625
1626   netgen::MeshingParameters& mparams = netgen::mparam;
1627   MESSAGE("Compute with:\n"
1628           " max size = " << mparams.maxh << "\n"
1629           " segments per edge = " << mparams.segmentsperedge);
1630   MESSAGE("\n"
1631           " growth rate = " << mparams.grading << "\n"
1632           " elements per radius = " << mparams.curvaturesafety << "\n"
1633           " second order = " << mparams.secondorder << "\n"
1634           " quad allowed = " << mparams.quad);
1635
1636   SMESH_ComputeErrorPtr error = SMESH_ComputeError::New();
1637   
1638
1639   // -------------------------
1640   // Prepare OCC geometry
1641   // -------------------------
1642
1643   netgen::OCCGeometry occgeo;
1644   list< SMESH_subMesh* > meshedSM[3]; // for 0-2 dimensions
1645   NETGENPlugin_Internals internals( *_mesh, _shape, _isVolume );
1646   PrepareOCCgeometry( occgeo, _shape, *_mesh, meshedSM, &internals );
1647
1648   // -------------------------
1649   // Local size on faces
1650   // -------------------------
1651   
1652 #ifdef NETGEN_NEW
1653   if ( ! _simpleHyp )
1654     {
1655       for(std::map<int,double>::const_iterator it=FaceId2LocalSize.begin(); it!=FaceId2LocalSize.end(); it++)
1656         {
1657           int key = (*it).first;
1658           double val = (*it).second;
1659           const TopoDS_Shape& shape = ShapesWithLocalSize.FindKey(key);
1660           int faceID = occgeo.fmap.FindIndex(shape);
1661           occgeo.SetFaceMaxH(faceID, val);
1662         }
1663     }
1664 #endif
1665   
1666   // -------------------------
1667   // Generate the mesh
1668   // -------------------------
1669
1670   netgen::Mesh *ngMesh = NULL;
1671   NETGENPlugin_ngMeshInfo initState; // it remembers size of ng mesh equal to size of Smesh
1672
1673   SMESH_Comment comment;
1674   int err = 0;
1675
1676   // vector of nodes in which node index == netgen ID
1677   vector< const SMDS_MeshNode* > nodeVec;
1678   
1679   {
1680     // ----------------
1681     // compute 1D mesh
1682     // ----------------
1683     if ( _simpleHyp )
1684     {
1685       // not to RestrictLocalH() according to curvature during MESHCONST_ANALYSE
1686       mparams.uselocalh = false;
1687       mparams.grading = 0.8; // not limitited size growth
1688
1689       if ( _simpleHyp->GetNumberOfSegments() )
1690         // nb of segments
1691         mparams.maxh = occgeo.boundingbox.Diam();
1692       else
1693         // segment length
1694         mparams.maxh = _simpleHyp->GetLocalLength();
1695     }
1696
1697     // Let netgen create ngMesh and calculate element size on not meshed shapes
1698     char *optstr = 0;
1699     int startWith = netgen::MESHCONST_ANALYSE;
1700     int endWith   = netgen::MESHCONST_ANALYSE;
1701     try
1702     {
1703       OCC_CATCH_SIGNALS;
1704       err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
1705 #ifdef WITH_SMESH_CANCEL_COMPUTE
1706       if(netgen::multithread.terminate)
1707         return false;
1708 #endif
1709       comment << text(err);
1710     }
1711     catch (Standard_Failure& ex)
1712     {
1713       comment << text(ex);
1714       if ( !ngMesh )
1715         return false;
1716       err = 1;
1717     }
1718     ngLib.setMesh(( Ng_Mesh*) ngMesh );
1719
1720     if ( _simpleHyp )
1721     {
1722       // Pass 1D simple parameters to NETGEN
1723       // --------------------------------
1724       int      nbSeg = _simpleHyp->GetNumberOfSegments();
1725       double segSize = _simpleHyp->GetLocalLength();
1726       for ( int iE = 1; iE <= occgeo.emap.Extent(); ++iE )
1727       {
1728         const TopoDS_Edge& e = TopoDS::Edge( occgeo.emap(iE));
1729         if ( nbSeg )
1730           segSize = SMESH_Algo::EdgeLength( e ) / ( nbSeg - 0.4 );
1731         setLocalSize( e, segSize, *ngMesh );
1732       }
1733     }
1734     else // if ( ! _simpleHyp )
1735     {
1736       // Local size on vertices and edges
1737       // --------------------------------
1738       for(std::map<int,double>::const_iterator it=EdgeId2LocalSize.begin(); it!=EdgeId2LocalSize.end(); it++)
1739       {
1740         int key = (*it).first;
1741         double hi = (*it).second;
1742         const TopoDS_Shape& shape = ShapesWithLocalSize.FindKey(key);
1743         const TopoDS_Edge& e = TopoDS::Edge(shape);
1744         setLocalSize( e, hi, *ngMesh );
1745       }
1746       for(std::map<int,double>::const_iterator it=VertexId2LocalSize.begin(); it!=VertexId2LocalSize.end(); it++)
1747       {
1748         int key = (*it).first;
1749         double hi = (*it).second;
1750         const TopoDS_Shape& shape = ShapesWithLocalSize.FindKey(key);
1751         const TopoDS_Vertex& v = TopoDS::Vertex(shape);
1752         gp_Pnt p = BRep_Tool::Pnt(v);
1753         netgen::Point3d pi(p.X(), p.Y(), p.Z());
1754         ngMesh->RestrictLocalH(pi, hi);
1755       }
1756     }
1757
1758     // Precompute internal edges (issue 0020676) in order to
1759     // add mesh on them correctly (twice) to netgen mesh
1760     if ( !err && internals.hasInternalEdges() )
1761     {
1762       // load internal shapes into OCCGeometry
1763       netgen::OCCGeometry intOccgeo;
1764       internals.getInternalEdges( intOccgeo.fmap, intOccgeo.emap, intOccgeo.vmap, meshedSM );
1765       intOccgeo.boundingbox = occgeo.boundingbox;
1766       intOccgeo.shape = occgeo.shape;
1767 #ifdef NETGEN_NEW
1768       intOccgeo.face_maxh.SetSize(intOccgeo.fmap.Extent());
1769       intOccgeo.face_maxh = netgen::mparam.maxh;
1770 #endif
1771
1772       // let netgen compute element size by the main geometry in temporary mesh
1773       netgen::Mesh *tmpNgMesh = NULL;
1774       try
1775       {
1776         OCC_CATCH_SIGNALS;
1777         netgen::OCCGenerateMesh(occgeo, tmpNgMesh, startWith, endWith, optstr);
1778 #ifdef WITH_SMESH_CANCEL_COMPUTE
1779         if(netgen::multithread.terminate)
1780           return false;
1781 #endif
1782         // compute mesh on internal edges
1783         endWith = netgen::MESHCONST_MESHEDGES;
1784         err = netgen::OCCGenerateMesh(intOccgeo, tmpNgMesh, startWith, endWith, optstr);
1785         comment << text(err);
1786       }
1787       catch (Standard_Failure& ex)
1788       {
1789         comment << text(ex);
1790         err = 1;
1791       }
1792       // fill SMESH by netgen mesh
1793       vector< const SMDS_MeshNode* > tmpNodeVec;
1794       FillSMesh( intOccgeo, *tmpNgMesh, initState, *_mesh, tmpNodeVec, comment );
1795       err = ( err || !comment.empty() );
1796
1797       nglib::Ng_DeleteMesh((nglib::Ng_Mesh*)tmpNgMesh);
1798     }
1799
1800     // Fill ngMesh with nodes and segments of computed submeshes
1801     if ( !err )
1802     {
1803       _faceDescriptors.clear();
1804       err = ! ( fillNgMesh(occgeo, *ngMesh, nodeVec, meshedSM[ MeshDim_0D ]) &&
1805                 fillNgMesh(occgeo, *ngMesh, nodeVec, meshedSM[ MeshDim_1D ]));
1806     }
1807     initState = NETGENPlugin_ngMeshInfo(ngMesh);
1808
1809     // Compute 1d mesh
1810     if (!err)
1811     {
1812       startWith = endWith = netgen::MESHCONST_MESHEDGES;
1813       try
1814       {
1815         OCC_CATCH_SIGNALS;
1816         err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
1817 #ifdef WITH_SMESH_CANCEL_COMPUTE
1818         if(netgen::multithread.terminate)
1819           return false;
1820 #endif
1821         comment << text(err);
1822       }
1823       catch (Standard_Failure& ex)
1824       {
1825         comment << text(ex);
1826         err = 1;
1827       }
1828     }
1829     mparams.uselocalh = true; // restore as it is used at surface optimization
1830
1831     // ---------------------
1832     // compute surface mesh
1833     // ---------------------
1834     if (!err)
1835     {
1836       // Pass 2D simple parameters to NETGEN
1837       if ( _simpleHyp ) {
1838         if ( double area = _simpleHyp->GetMaxElementArea() ) {
1839           // face area
1840           mparams.maxh = sqrt(2. * area/sqrt(3.0));
1841           mparams.grading = 0.4; // moderate size growth
1842         }
1843         else {
1844           // length from edges
1845           if ( ngMesh->GetNSeg() ) {
1846             double edgeLength = 0;
1847             TopTools_MapOfShape visitedEdges;
1848             for ( TopExp_Explorer exp( _shape, TopAbs_EDGE ); exp.More(); exp.Next() )
1849               if( visitedEdges.Add(exp.Current()) )
1850                 edgeLength += SMESH_Algo::EdgeLength( TopoDS::Edge( exp.Current() ));
1851             // we have to multiply length by 2 since for each TopoDS_Edge there
1852             // are double set of NETGEN edges or, in other words, we have to
1853             // divide ngMesh->GetNSeg() by 2.
1854             mparams.maxh = 2*edgeLength / ngMesh->GetNSeg();
1855           }
1856           else {
1857             mparams.maxh = 1000;
1858           }
1859           mparams.grading = 0.2; // slow size growth
1860         }
1861         mparams.maxh = min( mparams.maxh, occgeo.boundingbox.Diam()/2 );
1862         ngMesh->SetGlobalH (mparams.maxh);
1863         netgen::Box<3> bb = occgeo.GetBoundingBox();
1864         bb.Increase (bb.Diam()/20);
1865         ngMesh->SetLocalH (bb.PMin(), bb.PMax(), mparams.grading);
1866       }
1867
1868       // Care of vertices internal in faces (issue 0020676)
1869       if ( internals.hasInternalVertexInFace() )
1870       {
1871         // store computed segments in SMESH in order not to create SMESH
1872         // edges for ng segments added by addIntVerticesInFaces()
1873         FillSMesh( occgeo, *ngMesh, initState, *_mesh, nodeVec, comment );
1874         // add segments to faces with internal vertices
1875         addIntVerticesInFaces( occgeo, *ngMesh, nodeVec, internals );
1876         initState = NETGENPlugin_ngMeshInfo(ngMesh);
1877       }
1878
1879       // Let netgen compute 2D mesh
1880       startWith = netgen::MESHCONST_MESHSURFACE;
1881       endWith = _optimize ? netgen::MESHCONST_OPTSURFACE : netgen::MESHCONST_MESHSURFACE;
1882       try
1883       {
1884         OCC_CATCH_SIGNALS;
1885         err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
1886 #ifdef WITH_SMESH_CANCEL_COMPUTE
1887         if(netgen::multithread.terminate)
1888           return false;
1889 #endif
1890         comment << text (err);
1891       }
1892       catch (Standard_Failure& ex)
1893       {
1894         comment << text(ex);
1895         err = 1;
1896       }
1897       catch (netgen::NgException exc)
1898       {
1899         error->myName = err = COMPERR_ALGO_FAILED;
1900         comment << exc.What();
1901       }
1902     }
1903     // ---------------------
1904     // generate volume mesh
1905     // ---------------------
1906       // Fill ngMesh with nodes and faces of computed 2D submeshes
1907     if ( !err && _isVolume && !meshedSM[ MeshDim_2D ].empty() )
1908     {
1909       // load SMESH with computed segments and faces
1910       FillSMesh( occgeo, *ngMesh, initState, *_mesh, nodeVec, comment );
1911       // fill ng mesh
1912       err = ! ( fillNgMesh(occgeo, *ngMesh, nodeVec, meshedSM[ MeshDim_2D ]));
1913       initState = NETGENPlugin_ngMeshInfo(ngMesh);
1914     }
1915     if (!err && _isVolume)
1916     {
1917       // Pass 3D simple parameters to NETGEN
1918       const NETGENPlugin_SimpleHypothesis_3D* simple3d =
1919         dynamic_cast< const NETGENPlugin_SimpleHypothesis_3D* > ( _simpleHyp );
1920       if ( simple3d ) {
1921         if ( double vol = simple3d->GetMaxElementVolume() ) {
1922           // max volume
1923           mparams.maxh = pow( 72, 1/6. ) * pow( vol, 1/3. );
1924           mparams.maxh = min( mparams.maxh, occgeo.boundingbox.Diam()/2 );
1925         }
1926         else {
1927           // length from faces
1928           mparams.maxh = ngMesh->AverageH();
1929         }
1930         ngMesh->SetGlobalH (mparams.maxh);
1931         mparams.grading = 0.4;
1932         ngMesh->CalcLocalH();
1933       }
1934       // Care of vertices internal in solids and internal faces (issue 0020676)
1935       if ( internals.hasInternalVertexInSolid() || internals.hasInternalFaces() )
1936       {
1937         // store computed faces in SMESH in order not to create SMESH
1938         // faces for ng faces added here
1939         FillSMesh( occgeo, *ngMesh, initState, *_mesh, nodeVec, comment );
1940         // add ng faces to solids with internal vertices
1941         addIntVerticesInSolids( occgeo, *ngMesh, nodeVec, internals );
1942         // duplicate mesh faces on internal faces
1943         fixIntFaces( occgeo, *ngMesh, internals );
1944         initState = NETGENPlugin_ngMeshInfo(ngMesh);
1945       }
1946       // Let netgen compute 3D mesh
1947       startWith = endWith = netgen::MESHCONST_MESHVOLUME;
1948       try
1949       {
1950         OCC_CATCH_SIGNALS;
1951         err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
1952 #ifdef WITH_SMESH_CANCEL_COMPUTE
1953         if(netgen::multithread.terminate)
1954           return false;
1955 #endif
1956         comment << text(err);
1957       }
1958       catch (Standard_Failure& ex)
1959       {
1960         comment << text(ex);
1961         err = 1;
1962       }
1963       catch (netgen::NgException exc)
1964       {
1965         error->myName = err = COMPERR_ALGO_FAILED;
1966         comment << exc.What();
1967       }
1968       // Let netgen optimize 3D mesh
1969       if ( !err && _optimize )
1970       {
1971         startWith = endWith = netgen::MESHCONST_OPTVOLUME;
1972         try
1973         {
1974           OCC_CATCH_SIGNALS;
1975           err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
1976 #ifdef WITH_SMESH_CANCEL_COMPUTE
1977           if(netgen::multithread.terminate)
1978             return false;
1979 #endif
1980           comment << text(err);
1981         }
1982         catch (Standard_Failure& ex)
1983         {
1984           comment << text(ex);
1985           err = 1;
1986         }
1987         catch (netgen::NgException exc)
1988         {
1989           error->myName = err = COMPERR_ALGO_FAILED;
1990           comment << exc.What();
1991         }
1992       }
1993     }
1994     if (!err && mparams.secondorder > 0)
1995     {
1996       try
1997       {
1998         OCC_CATCH_SIGNALS;
1999         netgen::OCCRefinementSurfaces ref (occgeo);
2000         ref.MakeSecondOrder (*ngMesh);
2001       }
2002       catch (Standard_Failure& ex)
2003       {
2004         comment << "Exception in netgen at passing to 2nd order ";
2005         err = 1;
2006       }
2007       catch (netgen::NgException exc)
2008       {
2009         error->myName = err = COMPERR_ALGO_FAILED;
2010         comment << exc.What();
2011       }
2012     }
2013   }
2014   int nbNod = ngMesh->GetNP();
2015   int nbSeg = ngMesh->GetNSeg();
2016   int nbFac = ngMesh->GetNSE();
2017   int nbVol = ngMesh->GetNE();
2018   bool isOK = ( !err && (_isVolume ? (nbVol > 0) : (nbFac > 0)) );
2019
2020   MESSAGE((err ? "Mesh Generation failure" : "End of Mesh Generation") <<
2021           ", nb nodes: "    << nbNod <<
2022           ", nb segments: " << nbSeg <<
2023           ", nb faces: "    << nbFac <<
2024           ", nb volumes: "  << nbVol);
2025
2026   // ------------------------------------------------------------
2027   // Feed back the SMESHDS with the generated Nodes and Elements
2028   // ------------------------------------------------------------
2029
2030   if ( true /*isOK*/ ) // get whatever built
2031     FillSMesh( occgeo, *ngMesh, initState, *_mesh, nodeVec, comment ); //!< 
2032
2033   SMESH_ComputeErrorPtr readErr = readErrors(nodeVec);
2034   if ( readErr && !readErr->myBadElements.empty() )
2035     error = readErr;
2036
2037   if ( error->IsOK() && ( !isOK || comment.size() > 0 ))
2038     error->myName = COMPERR_ALGO_FAILED;
2039   if ( !comment.empty() )
2040     error->myComment = comment;
2041
2042   // SetIsAlwaysComputed( true ) to empty sub-meshes, which
2043   // appear if the geometry contains coincident sub-shape due
2044   // to bool merge_solids = 1; in netgen/libsrc/occ/occgenmesh.cpp
2045   const int nbMaps = 2;
2046   const TopTools_IndexedMapOfShape* geoMaps[nbMaps] =
2047     { & occgeo.vmap, & occgeo.emap/*, & occgeo.fmap*/ };
2048   for ( int iMap = 0; iMap < nbMaps; ++iMap )
2049     for (int i = 1; i <= geoMaps[iMap]->Extent(); i++)
2050       if ( SMESH_subMesh* sm = _mesh->GetSubMeshContaining( geoMaps[iMap]->FindKey(i)))
2051         if ( !sm->IsMeshComputed() )
2052           sm->SetIsAlwaysComputed( true );
2053
2054   // set bad compute error to subshapes of all failed subshapes shapes
2055   if ( !error->IsOK() && err )
2056   {
2057     bool pb2D = false;
2058     for (int i = 1; i <= occgeo.fmap.Extent(); i++) {
2059       int status = occgeo.facemeshstatus[i-1];
2060       if (status == 1 ) continue;
2061       pb2D = true;
2062       if ( SMESH_subMesh* sm = _mesh->GetSubMeshContaining( occgeo.fmap( i ))) {
2063         SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
2064         if ( !smError || smError->IsOK() ) {
2065           if ( status == -1 )
2066             smError.reset( new SMESH_ComputeError( *error ));
2067           else
2068             smError.reset( new SMESH_ComputeError( COMPERR_ALGO_FAILED, "Ignored" ));
2069         }
2070       }
2071     }
2072     if ( !pb2D ) // all faces are OK
2073       for (int i = 1; i <= occgeo.somap.Extent(); i++)
2074         if ( SMESH_subMesh* sm = _mesh->GetSubMeshContaining( occgeo.somap( i )))
2075         {
2076           bool smComputed = !sm->IsEmpty();
2077           if ( smComputed && internals.hasInternalVertexInSolid( sm->GetId() ))
2078           {
2079             int nbIntV = internals.getSolidsWithVertices().find( sm->GetId() )->second.size();
2080             SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
2081             smComputed = ( smDS->NbElements() > 0 || smDS->NbNodes() > nbIntV );
2082           }
2083           SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
2084           if ( !smComputed && ( !smError || smError->IsOK() ))
2085             smError.reset( new SMESH_ComputeError( *error ));
2086         }
2087   }
2088
2089   return error->IsOK();
2090 }
2091
2092 //=============================================================================
2093 /*!
2094  * Evaluate
2095  */
2096 //=============================================================================
2097 bool NETGENPlugin_Mesher::Evaluate(MapShapeNbElems& aResMap)
2098 {
2099   netgen::MeshingParameters& mparams = netgen::mparam;
2100
2101
2102   // -------------------------
2103   // Prepare OCC geometry
2104   // -------------------------
2105   netgen::OCCGeometry occgeo;
2106   PrepareOCCgeometry( occgeo, _shape, *_mesh );
2107
2108   bool tooManyElems = false;
2109   const int hugeNb = std::numeric_limits<int>::max() / 100;
2110
2111   // ----------------
2112   // evaluate 1D 
2113   // ----------------
2114   // pass 1D simple parameters to NETGEN
2115   if ( _simpleHyp ) {
2116     if ( int nbSeg = _simpleHyp->GetNumberOfSegments() ) {
2117       // nb of segments
2118       mparams.segmentsperedge = nbSeg + 0.1;
2119       mparams.maxh = occgeo.boundingbox.Diam();
2120       mparams.grading = 0.01;
2121     }
2122     else {
2123       // segment length
2124       mparams.segmentsperedge = 1;
2125       mparams.maxh = _simpleHyp->GetLocalLength();
2126     }
2127   }
2128   // let netgen create ngMesh and calculate element size on not meshed shapes
2129   NETGENPlugin_NetgenLibWrapper ngLib;
2130   netgen::Mesh *ngMesh = NULL;
2131   char *optstr = 0;
2132   int startWith = netgen::MESHCONST_ANALYSE;
2133   int endWith   = netgen::MESHCONST_MESHEDGES;
2134   int err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
2135 #ifdef WITH_SMESH_CANCEL_COMPUTE
2136   if(netgen::multithread.terminate)
2137     return false;
2138 #endif
2139   ngLib.setMesh(( Ng_Mesh*) ngMesh );
2140   if (err) {
2141     if ( SMESH_subMesh* sm = _mesh->GetSubMeshContaining( _shape ))
2142       sm->GetComputeError().reset( new SMESH_ComputeError( COMPERR_ALGO_FAILED ));
2143     return false;
2144   }
2145
2146   // calculate total nb of segments and length of edges
2147   double fullLen = 0.0;
2148   int fullNbSeg = 0;
2149   int entity = mparams.secondorder > 0 ? SMDSEntity_Quad_Edge : SMDSEntity_Edge;
2150   TopTools_DataMapOfShapeInteger Edge2NbSeg;
2151   for (TopExp_Explorer exp(_shape, TopAbs_EDGE); exp.More(); exp.Next())
2152   {
2153     TopoDS_Edge E = TopoDS::Edge( exp.Current() );
2154     if( !Edge2NbSeg.Bind(E,0) )
2155       continue;
2156
2157     double aLen = SMESH_Algo::EdgeLength(E);
2158     fullLen += aLen;
2159
2160     vector<int>& aVec = aResMap[_mesh->GetSubMesh(E)];
2161     if ( aVec.empty() )
2162       aVec.resize( SMDSEntity_Last, 0);
2163     else
2164       fullNbSeg += aVec[ entity ];
2165   }
2166
2167   // store nb of segments computed by Netgen
2168   NCollection_Map<Link> linkMap;
2169   for (int i = 1; i <= ngMesh->GetNSeg(); ++i )
2170   {
2171     const netgen::Segment& seg = ngMesh->LineSegment(i);
2172     Link link(seg[0], seg[1]);
2173     if ( !linkMap.Add( link )) continue;
2174     int aGeomEdgeInd = seg.epgeominfo[0].edgenr;
2175     if (aGeomEdgeInd > 0 && aGeomEdgeInd <= occgeo.emap.Extent())
2176     {
2177       vector<int>& aVec = aResMap[_mesh->GetSubMesh(occgeo.emap(aGeomEdgeInd))];
2178       aVec[ entity ]++;
2179     }
2180   }
2181   // store nb of nodes on edges computed by Netgen
2182   TopTools_DataMapIteratorOfDataMapOfShapeInteger Edge2NbSegIt(Edge2NbSeg);
2183   for (; Edge2NbSegIt.More(); Edge2NbSegIt.Next())
2184   {
2185     vector<int>& aVec = aResMap[_mesh->GetSubMesh(Edge2NbSegIt.Key())];
2186     if ( aVec[ entity ] > 1 && aVec[ SMDSEntity_Node ] == 0 )
2187       aVec[SMDSEntity_Node] = mparams.secondorder > 0  ? 2*aVec[ entity ]-1 : aVec[ entity ]-1;
2188
2189     fullNbSeg += aVec[ entity ];
2190     Edge2NbSeg( Edge2NbSegIt.Key() ) = aVec[ entity ];
2191   }
2192
2193   // ----------------
2194   // evaluate 2D 
2195   // ----------------
2196   if ( _simpleHyp ) {
2197     if ( double area = _simpleHyp->GetMaxElementArea() ) {
2198       // face area
2199       mparams.maxh = sqrt(2. * area/sqrt(3.0));
2200       mparams.grading = 0.4; // moderate size growth
2201     }
2202     else {
2203       // length from edges
2204       mparams.maxh = fullLen/fullNbSeg;
2205       mparams.grading = 0.2; // slow size growth
2206     }
2207   }
2208   mparams.maxh = min( mparams.maxh, occgeo.boundingbox.Diam()/2 );
2209   mparams.maxh = min( mparams.maxh, fullLen/fullNbSeg * (1. + mparams.grading));
2210
2211   for (TopExp_Explorer exp(_shape, TopAbs_FACE); exp.More(); exp.Next())
2212   {
2213     TopoDS_Face F = TopoDS::Face( exp.Current() );
2214     SMESH_subMesh *sm = _mesh->GetSubMesh(F);
2215     GProp_GProps G;
2216     BRepGProp::SurfaceProperties(F,G);
2217     double anArea = G.Mass();
2218     tooManyElems = tooManyElems || ( anArea/hugeNb > mparams.maxh*mparams.maxh );
2219     int nb1d = 0;
2220     if ( !tooManyElems )
2221     {
2222       TopTools_MapOfShape egdes;
2223       for (TopExp_Explorer exp1(F,TopAbs_EDGE); exp1.More(); exp1.Next())
2224         if ( egdes.Add( exp1.Current() ))
2225           nb1d += Edge2NbSeg.Find(exp1.Current());
2226     }
2227     int nbFaces = tooManyElems ? hugeNb : int( 4*anArea / (mparams.maxh*mparams.maxh*sqrt(3.)));
2228     int nbNodes = tooManyElems ? hugeNb : (( nbFaces*3 - (nb1d-1)*2 ) / 6 + 1 );
2229
2230     vector<int> aVec(SMDSEntity_Last, 0);
2231     if( mparams.secondorder > 0 ) {
2232       int nb1d_in = (nbFaces*3 - nb1d) / 2;
2233       aVec[SMDSEntity_Node] = nbNodes + nb1d_in;
2234       aVec[SMDSEntity_Quad_Triangle] = nbFaces;
2235     }
2236     else {
2237       aVec[SMDSEntity_Node] = Max ( nbNodes, 0  );
2238       aVec[SMDSEntity_Triangle] = nbFaces;
2239     }
2240     aResMap[sm].swap(aVec);
2241   }
2242
2243   // ----------------
2244   // evaluate 3D
2245   // ----------------
2246   if(_isVolume) {
2247     // pass 3D simple parameters to NETGEN
2248     const NETGENPlugin_SimpleHypothesis_3D* simple3d =
2249       dynamic_cast< const NETGENPlugin_SimpleHypothesis_3D* > ( _simpleHyp );
2250     if ( simple3d ) {
2251       if ( double vol = simple3d->GetMaxElementVolume() ) {
2252         // max volume
2253         mparams.maxh = pow( 72, 1/6. ) * pow( vol, 1/3. );
2254         mparams.maxh = min( mparams.maxh, occgeo.boundingbox.Diam()/2 );
2255       }
2256       else {
2257         // using previous length from faces
2258       }
2259       mparams.grading = 0.4;
2260       mparams.maxh = min( mparams.maxh, fullLen/fullNbSeg * (1. + mparams.grading));
2261     }
2262     GProp_GProps G;
2263     BRepGProp::VolumeProperties(_shape,G);
2264     double aVolume = G.Mass();
2265     double tetrVol = 0.1179*mparams.maxh*mparams.maxh*mparams.maxh;
2266     tooManyElems = tooManyElems || ( aVolume/hugeNb > tetrVol );
2267     int nbVols = tooManyElems ? hugeNb : int(aVolume/tetrVol);
2268     int nb1d_in = int(( nbVols*6 - fullNbSeg ) / 6 );
2269     vector<int> aVec(SMDSEntity_Last, 0 );
2270     if ( tooManyElems ) // avoid FPE
2271     {
2272       aVec[SMDSEntity_Node] = hugeNb;
2273       aVec[ mparams.secondorder > 0 ? SMDSEntity_Quad_Tetra : SMDSEntity_Tetra] = hugeNb;
2274     }
2275     else
2276     {
2277       if( mparams.secondorder > 0 ) {
2278         aVec[SMDSEntity_Node] = nb1d_in/3 + 1 + nb1d_in;
2279         aVec[SMDSEntity_Quad_Tetra] = nbVols;
2280       }
2281       else {
2282         aVec[SMDSEntity_Node] = nb1d_in/3 + 1;
2283         aVec[SMDSEntity_Tetra] = nbVols;
2284       }
2285     }
2286     SMESH_subMesh *sm = _mesh->GetSubMesh(_shape);
2287     aResMap[sm].swap(aVec);
2288   }
2289
2290   return true;
2291 }
2292
2293 //================================================================================
2294 /*!
2295  * \brief Remove "test.out" and "problemfaces" files in current directory
2296  */
2297 //================================================================================
2298
2299 void NETGENPlugin_Mesher::RemoveTmpFiles()
2300 {
2301   SMESH_File("test.out").remove();
2302   SMESH_File("problemfaces").remove();
2303   SMESH_File("occmesh.rep").remove();
2304 }
2305
2306 //================================================================================
2307 /*!
2308  * \brief Read mesh entities preventing successful computation from "test.out" file
2309  */
2310 //================================================================================
2311
2312 SMESH_ComputeErrorPtr
2313 NETGENPlugin_Mesher::readErrors(const vector<const SMDS_MeshNode* >& nodeVec)
2314 {
2315   SMESH_ComputeErrorPtr err = SMESH_ComputeError::New
2316     (COMPERR_BAD_INPUT_MESH, "Some edges multiple times in surface mesh");
2317   SMESH_File file("test.out");
2318   vector<int> two(2);
2319   const char* badEdgeStr = " multiple times in surface mesh";
2320   const int   badEdgeStrLen = strlen( badEdgeStr );
2321   while( !file.eof() )
2322   {
2323     if ( strncmp( file, "Edge ", 5 ) == 0 &&
2324          file.getInts( two ) &&
2325          strncmp( file, badEdgeStr, badEdgeStrLen ) == 0 &&
2326          two[0] < nodeVec.size() && two[1] < nodeVec.size())
2327     {
2328       err->myBadElements.push_back( new SMDS_LinearEdge( nodeVec[ two[0]], nodeVec[ two[1]] ));
2329       file += badEdgeStrLen;
2330     }
2331     else if ( strncmp( file, "Intersecting: ", 14 ) == 0 )
2332     {
2333 // Intersecting: 
2334 // openelement 18 with open element 126
2335 // 41  36  38  
2336 // 69  70  72
2337       vector<int> three1(3), three2(3);
2338       file.getLine();
2339       const char* pos = file;
2340       bool ok = ( strncmp( file, "openelement ", 12 ) == 0 );
2341       ok = ok && file.getInts( two );
2342       ok = ok && file.getInts( three1 );
2343       ok = ok && file.getInts( three2 );
2344       for ( int i = 0; ok && i < 3; ++i )
2345         ok = ( three1[i] < nodeVec.size() && nodeVec[ three1[i]]);
2346       for ( int i = 0; ok && i < 3; ++i ) 
2347         ok = ( three2[i] < nodeVec.size() && nodeVec[ three2[i]]);
2348       if ( ok )
2349       {
2350         err->myBadElements.push_back( new SMDS_FaceOfNodes( nodeVec[ three1[0]],
2351                                                             nodeVec[ three1[1]],
2352                                                             nodeVec[ three1[2]]));
2353         err->myBadElements.push_back( new SMDS_FaceOfNodes( nodeVec[ three2[0]],
2354                                                             nodeVec[ three2[1]],
2355                                                             nodeVec[ three2[2]]));
2356         err->myComment = "Intersecting triangles";
2357       }
2358       else
2359       {
2360         file.setPos( pos );
2361       }
2362     }
2363     else
2364     {
2365       ++file;
2366     }
2367   }
2368   return err;
2369 }
2370
2371 //================================================================================
2372 /*!
2373  * \brief Constructor of NETGENPlugin_ngMeshInfo
2374  */
2375 //================================================================================
2376
2377 NETGENPlugin_ngMeshInfo::NETGENPlugin_ngMeshInfo( netgen::Mesh* ngMesh)
2378 {
2379   if ( ngMesh )
2380   {
2381     _nbNodes    = ngMesh->GetNP();
2382     _nbSegments = ngMesh->GetNSeg();
2383     _nbFaces    = ngMesh->GetNSE();
2384     _nbVolumes  = ngMesh->GetNE();
2385   }
2386   else
2387   {
2388     _nbNodes = _nbSegments = _nbFaces = _nbVolumes = 0;
2389   }
2390 }
2391
2392 //================================================================================
2393 /*!
2394  * \brief Find "internal" sub-shapes
2395  */
2396 //================================================================================
2397
2398 NETGENPlugin_Internals::NETGENPlugin_Internals( SMESH_Mesh&         mesh,
2399                                                 const TopoDS_Shape& shape,
2400                                                 bool                is3D )
2401   : _mesh( mesh ), _is3D( is3D )
2402 {
2403   SMESHDS_Mesh* meshDS = mesh.GetMeshDS();
2404
2405   TopExp_Explorer f,e;
2406   for ( f.Init( shape, TopAbs_FACE ); f.More(); f.Next() )
2407   {
2408     int faceID = meshDS->ShapeToIndex( f.Current() );
2409
2410     // find not computed internal edges
2411
2412     for ( e.Init( f.Current().Oriented(TopAbs_FORWARD), TopAbs_EDGE ); e.More(); e.Next() )
2413       if ( e.Current().Orientation() == TopAbs_INTERNAL )
2414       {
2415         SMESH_subMesh* eSM = mesh.GetSubMesh( e.Current() );
2416         if ( eSM->IsEmpty() )
2417         {
2418           _e2face.insert( make_pair( eSM->GetId(), faceID ));
2419           for ( TopoDS_Iterator v(e.Current()); v.More(); v.Next() )
2420             _e2face.insert( make_pair( meshDS->ShapeToIndex( v.Value() ), faceID ));
2421         }
2422       }
2423
2424     // find internal vertices in a face
2425     set<int> intVV; // issue 0020850 where same vertex is twice in a face
2426     for ( TopoDS_Iterator fSub( f.Current() ); fSub.More(); fSub.Next())
2427       if ( fSub.Value().ShapeType() == TopAbs_VERTEX )
2428       {
2429         int vID = meshDS->ShapeToIndex( fSub.Value() );
2430         if ( intVV.insert( vID ).second )
2431           _f2v[ faceID ].push_back( vID );
2432       }
2433
2434     if ( is3D )
2435     {
2436       // find internal faces and their subshapes where nodes are to be doubled
2437       //  to make a crack with non-sewed borders
2438
2439       if ( f.Current().Orientation() == TopAbs_INTERNAL )
2440       {
2441         _intShapes.insert( meshDS->ShapeToIndex( f.Current() ));
2442
2443         // egdes
2444         list< TopoDS_Shape > edges;
2445         for ( e.Init( f.Current(), TopAbs_EDGE ); e.More(); e.Next())
2446           if ( SMESH_MesherHelper::NbAncestors( e.Current(), mesh, TopAbs_FACE ) > 1 )
2447           {
2448             _intShapes.insert( meshDS->ShapeToIndex( e.Current() ));
2449             edges.push_back( e.Current() );
2450             // find border faces
2451             PShapeIteratorPtr fIt =
2452               SMESH_MesherHelper::GetAncestors( edges.back(),mesh,TopAbs_FACE );
2453             while ( const TopoDS_Shape* pFace = fIt->next() )
2454               if ( !pFace->IsSame( f.Current() ))
2455                 _borderFaces.insert( meshDS->ShapeToIndex( *pFace ));
2456           }
2457         // vertices
2458         // we consider vertex internal if it is shared by more than one internal edge
2459         list< TopoDS_Shape >::iterator edge = edges.begin();
2460         for ( ; edge != edges.end(); ++edge )
2461           for ( TopoDS_Iterator v( *edge ); v.More(); v.Next() )
2462           {
2463             set<int> internalEdges;
2464             PShapeIteratorPtr eIt =
2465               SMESH_MesherHelper::GetAncestors( v.Value(),mesh,TopAbs_EDGE );
2466             while ( const TopoDS_Shape* pEdge = eIt->next() )
2467             {
2468               int edgeID = meshDS->ShapeToIndex( *pEdge );
2469               if ( isInternalShape( edgeID ))
2470                 internalEdges.insert( edgeID );
2471             }
2472             if ( internalEdges.size() > 1 )
2473               _intShapes.insert( meshDS->ShapeToIndex( v.Value() ));
2474           }
2475       }
2476     }
2477   } // loop on geom faces
2478
2479   // find vertices internal in solids
2480   if ( is3D )
2481   {
2482     for ( TopExp_Explorer so(shape, TopAbs_SOLID); so.More(); so.Next())
2483     {
2484       int soID = meshDS->ShapeToIndex( so.Current() );
2485       for ( TopoDS_Iterator soSub( so.Current() ); soSub.More(); soSub.Next())
2486         if ( soSub.Value().ShapeType() == TopAbs_VERTEX )
2487           _s2v[ soID ].push_back( meshDS->ShapeToIndex( soSub.Value() ));
2488     }
2489   }
2490 }
2491
2492 //================================================================================
2493 /*!
2494  * \brief Find mesh faces on non-internal geom faces sharing internal edge
2495  * some nodes of which are to be doubled to make the second border of the "crack"
2496  */
2497 //================================================================================
2498
2499 void NETGENPlugin_Internals::findBorderElements( TIDSortedElemSet & borderElems )
2500 {
2501   if ( _intShapes.empty() ) return;
2502
2503   SMESH_Mesh& mesh = const_cast<SMESH_Mesh&>(_mesh);
2504   SMESHDS_Mesh* meshDS = mesh.GetMeshDS();
2505
2506   // loop on internal geom edges
2507   set<int>::const_iterator intShapeId = _intShapes.begin();
2508   for ( ; intShapeId != _intShapes.end(); ++intShapeId )
2509   {
2510     const TopoDS_Shape& s = meshDS->IndexToShape( *intShapeId );
2511     if ( s.ShapeType() != TopAbs_EDGE ) continue;
2512
2513     // get internal and non-internal geom faces sharing the internal edge <s>
2514     int intFace = 0;
2515     set<int>::iterator bordFace = _borderFaces.end();
2516     PShapeIteratorPtr faces = SMESH_MesherHelper::GetAncestors( s, _mesh, TopAbs_FACE );
2517     while ( const TopoDS_Shape* pFace = faces->next() )
2518     {
2519       int faceID = meshDS->ShapeToIndex( *pFace );
2520       if ( isInternalShape( faceID ))
2521         intFace = faceID;
2522       else
2523         bordFace = _borderFaces.insert( faceID ).first;
2524     }
2525     if ( bordFace == _borderFaces.end() || !intFace ) continue;
2526
2527     // get all links of mesh faces on internal geom face sharing nodes on edge <s>
2528     set< SMESH_OrientedLink > links; //!< links of faces on internal geom face
2529     list<const SMDS_MeshElement*> suspectFaces[2]; //!< mesh faces on border geom faces
2530     int nbSuspectFaces = 0;
2531     SMESHDS_SubMesh* intFaceSM = meshDS->MeshElements( intFace );
2532     if ( !intFaceSM || intFaceSM->NbElements() == 0 ) continue;
2533     SMESH_subMeshIteratorPtr smIt = mesh.GetSubMesh( s )->getDependsOnIterator(true,true);
2534     while ( smIt->more() )
2535     {
2536       SMESHDS_SubMesh* sm = smIt->next()->GetSubMeshDS();
2537       if ( !sm ) continue;
2538       SMDS_NodeIteratorPtr nIt = sm->GetNodes();
2539       while ( nIt->more() )
2540       {
2541         const SMDS_MeshNode* nOnEdge = nIt->next();
2542         SMDS_ElemIteratorPtr fIt = nOnEdge->GetInverseElementIterator(SMDSAbs_Face);
2543         while ( fIt->more() )
2544         {
2545           const SMDS_MeshElement* f = fIt->next();
2546           int nbNodes = f->NbNodes() / ( f->IsQuadratic() ? 2 : 1 );
2547           if ( intFaceSM->Contains( f ))
2548           {
2549             for ( int i = 0; i < nbNodes; ++i )
2550               links.insert( SMESH_OrientedLink( f->GetNode(i), f->GetNode((i+1)%nbNodes)));
2551           }
2552           else
2553           {
2554             int nbDblNodes = 0;
2555             for ( int i = 0; i < nbNodes; ++i )
2556               nbDblNodes += isInternalShape( f->GetNode(i)->getshapeId() );
2557             if ( nbDblNodes )
2558               suspectFaces[ nbDblNodes < 2 ].push_back( f );
2559             nbSuspectFaces++;
2560           }
2561         }
2562       }
2563     }
2564     // suspectFaces[0] having link with same orientation as mesh faces on
2565     // the internal geom face are <borderElems>. suspectFaces[1] have
2566     // only one node on edge <s>, we decide on them later (at the 2nd loop)
2567     // by links of <borderElems> found at the 1st and 2nd loops
2568     set< SMESH_OrientedLink > borderLinks;
2569     for ( int isPostponed = 0; isPostponed < 2; ++isPostponed )
2570     {
2571       list<const SMDS_MeshElement*>::iterator fIt = suspectFaces[isPostponed].begin();
2572       for ( int nbF = 0; fIt != suspectFaces[isPostponed].end(); ++fIt, ++nbF )
2573       {
2574         const SMDS_MeshElement* f = *fIt;
2575         bool isBorder = false, linkFound = false, borderLinkFound = false;
2576         list< SMESH_OrientedLink > faceLinks;
2577         int nbNodes = f->NbNodes() / ( f->IsQuadratic() ? 2 : 1 );
2578         for ( int i = 0; i < nbNodes; ++i )
2579         {
2580           SMESH_OrientedLink link( f->GetNode(i), f->GetNode((i+1)%nbNodes));
2581           faceLinks.push_back( link );
2582           if ( !linkFound )
2583           {
2584             set< SMESH_OrientedLink >::iterator foundLink = links.find( link );
2585             if ( foundLink != links.end() )
2586             {
2587               linkFound= true;
2588               isBorder = ( foundLink->_reversed == link._reversed );
2589               if ( !isBorder && !isPostponed ) break;
2590               faceLinks.pop_back();
2591             }
2592             else if ( isPostponed && !borderLinkFound )
2593             {
2594               foundLink = borderLinks.find( link );
2595               if ( foundLink != borderLinks.end() )
2596               {
2597                 borderLinkFound = true;
2598                 isBorder = ( foundLink->_reversed != link._reversed );
2599               }
2600             }
2601           }
2602         }
2603         if ( isBorder )
2604         {
2605           borderElems.insert( f );
2606           borderLinks.insert( faceLinks.begin(), faceLinks.end() );
2607         }
2608         else if ( !linkFound && !borderLinkFound )
2609         {
2610           suspectFaces[1].push_back( f );
2611           if ( nbF > 2 * nbSuspectFaces )
2612             break; // dead loop protection
2613         }
2614       }
2615     }
2616   }
2617 }
2618
2619 //================================================================================
2620 /*!
2621  * \brief put internal shapes in maps and fill in submeshes to precompute
2622  */
2623 //================================================================================
2624
2625 void NETGENPlugin_Internals::getInternalEdges( TopTools_IndexedMapOfShape& fmap,
2626                                                TopTools_IndexedMapOfShape& emap,
2627                                                TopTools_IndexedMapOfShape& vmap,
2628                                                list< SMESH_subMesh* > smToPrecompute[])
2629 {
2630   if ( !hasInternalEdges() ) return;
2631   map<int,int>::const_iterator ev_face = _e2face.begin();
2632   for ( ; ev_face != _e2face.end(); ++ev_face )
2633   {
2634     const TopoDS_Shape& ev   = _mesh.GetMeshDS()->IndexToShape( ev_face->first );
2635     const TopoDS_Shape& face = _mesh.GetMeshDS()->IndexToShape( ev_face->second );
2636
2637     ( ev.ShapeType() == TopAbs_EDGE ? emap : vmap ).Add( ev );
2638     fmap.Add( face );
2639     //cout<<"INTERNAL EDGE or VERTEX "<<ev_face->first<<" on face "<<ev_face->second<<endl;
2640
2641     smToPrecompute[ MeshDim_1D ].push_back( _mesh.GetSubMeshContaining( ev_face->first ));
2642   }
2643 }
2644
2645 //================================================================================
2646 /*!
2647  * \brief return shapes and submeshes to be meshed and already meshed boundary submeshes
2648  */
2649 //================================================================================
2650
2651 void NETGENPlugin_Internals::getInternalFaces( TopTools_IndexedMapOfShape& fmap,
2652                                                TopTools_IndexedMapOfShape& emap,
2653                                                list< SMESH_subMesh* >&     intFaceSM,
2654                                                list< SMESH_subMesh* >&     boundarySM)
2655 {
2656   if ( !hasInternalFaces() ) return;
2657
2658   // <fmap> and <emap> are for not yet meshed shapes
2659   // <intFaceSM> is for submeshes of faces
2660   // <boundarySM> is for meshed edges and vertices
2661
2662   intFaceSM.clear();
2663   boundarySM.clear();
2664
2665   set<int> shapeIDs ( _intShapes );
2666   if ( !_borderFaces.empty() )
2667     shapeIDs.insert( _borderFaces.begin(), _borderFaces.end() );
2668
2669   set<int>::const_iterator intS = shapeIDs.begin();
2670   for ( ; intS != shapeIDs.end(); ++intS )
2671   {
2672     SMESH_subMesh* sm = _mesh.GetSubMeshContaining( *intS );
2673
2674     if ( sm->GetSubShape().ShapeType() != TopAbs_FACE ) continue;
2675
2676     intFaceSM.push_back( sm );
2677
2678     // add submeshes of not computed internal faces
2679     if ( !sm->IsEmpty() ) continue;
2680
2681     SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(true,true);
2682     while ( smIt->more() )
2683     {
2684       sm = smIt->next();
2685       const TopoDS_Shape& s = sm->GetSubShape();
2686
2687       if ( sm->IsEmpty() )
2688       {
2689         // not yet meshed
2690         switch ( s.ShapeType() ) {
2691         case TopAbs_FACE: fmap.Add ( s ); break;
2692         case TopAbs_EDGE: emap.Add ( s ); break;
2693         default:;
2694         }
2695       }
2696       else
2697       {
2698         if ( s.ShapeType() != TopAbs_FACE )
2699           boundarySM.push_back( sm );
2700       }
2701     }
2702   }
2703 }
2704
2705 //================================================================================
2706 /*!
2707  * \brief Return true if given shape is to be precomputed in order to be correctly
2708  * added to netgen mesh
2709  */
2710 //================================================================================
2711
2712 bool NETGENPlugin_Internals::isShapeToPrecompute(const TopoDS_Shape& s)
2713 {
2714   int shapeID = _mesh.GetMeshDS()->ShapeToIndex( s );
2715   switch ( s.ShapeType() ) {
2716   case TopAbs_FACE  : break; //return isInternalShape( shapeID ) || isBorderFace( shapeID );
2717   case TopAbs_EDGE  : return isInternalEdge( shapeID );
2718   case TopAbs_VERTEX: break;
2719   default:;
2720   }
2721   return false;
2722 }
2723
2724 //================================================================================
2725 /*!
2726  * \brief Return SMESH
2727  */
2728 //================================================================================
2729
2730 SMESH_Mesh& NETGENPlugin_Internals::getMesh() const
2731 {
2732   return const_cast<SMESH_Mesh&>( _mesh );
2733 }
2734
2735 //================================================================================
2736 /*!
2737  * \brief Initialize netgen library
2738  */
2739 //================================================================================
2740
2741 NETGENPlugin_NetgenLibWrapper::NETGENPlugin_NetgenLibWrapper()
2742 {
2743   Ng_Init();
2744   _ngMesh = Ng_NewMesh();
2745 }
2746
2747 //================================================================================
2748 /*!
2749  * \brief Finish using netgen library
2750  */
2751 //================================================================================
2752
2753 NETGENPlugin_NetgenLibWrapper::~NETGENPlugin_NetgenLibWrapper()
2754 {
2755   Ng_DeleteMesh( _ngMesh );
2756   Ng_Exit();
2757   NETGENPlugin_Mesher::RemoveTmpFiles();
2758 }
2759
2760 //================================================================================
2761 /*!
2762  * \brief Set netgen mesh to delete at destruction
2763  */
2764 //================================================================================
2765
2766 void NETGENPlugin_NetgenLibWrapper::setMesh( Ng_Mesh* mesh )
2767 {
2768   if ( _ngMesh )
2769     Ng_DeleteMesh( _ngMesh );
2770   _ngMesh = mesh;
2771 }