1 // Copyright (C) 2007-2014 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
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.
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
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // NETGENPlugin : C++ implementation
24 // File : NETGENPlugin_Mesher.cxx
25 // Author : Michael Sazonov (OCN)
28 //=============================================================================
30 #include "NETGENPlugin_Mesher.hxx"
31 #include "NETGENPlugin_Hypothesis_2D.hxx"
32 #include "NETGENPlugin_SimpleHypothesis_3D.hxx"
34 #include <SMDS_FaceOfNodes.hxx>
35 #include <SMDS_MeshElement.hxx>
36 #include <SMDS_MeshNode.hxx>
37 #include <SMESHDS_Mesh.hxx>
38 #include <SMESH_Block.hxx>
39 #include <SMESH_Comment.hxx>
40 #include <SMESH_ComputeError.hxx>
41 #include <SMESH_File.hxx>
42 #include <SMESH_Gen_i.hxx>
43 #include <SMESH_Mesh.hxx>
44 #include <SMESH_MesherHelper.hxx>
45 #include <SMESH_subMesh.hxx>
46 #include <StdMeshers_QuadToTriaAdaptor.hxx>
47 #include <StdMeshers_ViscousLayers2D.hxx>
49 #include <SALOMEDS_Tool.hxx>
51 #include <utilities.h>
53 #include <BRepBuilderAPI_Copy.hxx>
54 #include <BRep_Tool.hxx>
55 #include <Bnd_B3d.hxx>
56 #include <NCollection_Map.hxx>
57 #include <Standard_ErrorHandler.hxx>
58 #include <Standard_ProgramError.hxx>
60 #include <TopExp_Explorer.hxx>
61 #include <TopTools_DataMapIteratorOfDataMapOfShapeInteger.hxx>
62 #include <TopTools_DataMapIteratorOfDataMapOfShapeShape.hxx>
63 #include <TopTools_DataMapOfShapeInteger.hxx>
64 #include <TopTools_DataMapOfShapeShape.hxx>
65 #include <TopTools_MapOfShape.hxx>
67 #include <OSD_File.hxx>
68 #include <OSD_Path.hxx>
70 // Netgen include files
74 #include <occgeom.hpp>
75 #include <meshing.hpp>
76 //#include <ngexception.hpp>
79 extern int OCCGenerateMesh (OCCGeometry&, Mesh*&, MeshingParameters&, int, int);
81 extern int OCCGenerateMesh (OCCGeometry&, Mesh*&, int, int, char*);
83 //extern void OCCSetLocalMeshSize(OCCGeometry & geom, Mesh & mesh);
84 extern MeshingParameters mparam;
85 extern volatile multithreadt multithread;
86 extern bool merge_solids;
95 using namespace nglib;
99 #define nodeVec_ACCESS(index) ((SMDS_MeshNode*) nodeVec.at((index)))
101 #define nodeVec_ACCESS(index) ((SMDS_MeshNode*) nodeVec[index])
104 #define NGPOINT_COORDS(p) p(0),p(1),p(2)
106 // dump elements added to ng mesh
107 //#define DUMP_SEGMENTS
108 //#define DUMP_TRIANGLES
109 //#define DUMP_TRIANGLES_SCRIPT "/tmp/trias.py" //!< debug AddIntVerticesInSolids()
111 TopTools_IndexedMapOfShape ShapesWithLocalSize;
112 std::map<int,double> VertexId2LocalSize;
113 std::map<int,double> EdgeId2LocalSize;
114 std::map<int,double> FaceId2LocalSize;
116 //=============================================================================
120 //=============================================================================
122 NETGENPlugin_Mesher::NETGENPlugin_Mesher (SMESH_Mesh* mesh,
123 const TopoDS_Shape& aShape,
129 _fineness(NETGENPlugin_Hypothesis::GetDefaultFineness()),
130 _isViscousLayers2D(false),
139 SetDefaultParameters();
140 ShapesWithLocalSize.Clear();
141 VertexId2LocalSize.clear();
142 EdgeId2LocalSize.clear();
143 FaceId2LocalSize.clear();
146 //================================================================================
150 //================================================================================
152 NETGENPlugin_Mesher::~NETGENPlugin_Mesher()
160 //================================================================================
162 * Set pointer to NETGENPlugin_Mesher* field of the holder, that will be
163 * nullified at destruction of this
165 //================================================================================
167 void NETGENPlugin_Mesher::SetSelfPointer( NETGENPlugin_Mesher ** ptr )
178 //================================================================================
180 * \brief Initialize global NETGEN parameters with default values
182 //================================================================================
184 void NETGENPlugin_Mesher::SetDefaultParameters()
186 netgen::MeshingParameters& mparams = netgen::mparam;
187 // maximal mesh edge size
188 mparams.maxh = 0;//NETGENPlugin_Hypothesis::GetDefaultMaxSize();
190 // minimal number of segments per edge
191 mparams.segmentsperedge = NETGENPlugin_Hypothesis::GetDefaultNbSegPerEdge();
192 // rate of growth of size between elements
193 mparams.grading = NETGENPlugin_Hypothesis::GetDefaultGrowthRate();
194 // safety factor for curvatures (elements per radius)
195 mparams.curvaturesafety = NETGENPlugin_Hypothesis::GetDefaultNbSegPerRadius();
196 // create elements of second order
197 mparams.secondorder = NETGENPlugin_Hypothesis::GetDefaultSecondOrder();
198 // quad-dominated surface meshing
202 mparams.quad = NETGENPlugin_Hypothesis_2D::GetDefaultQuadAllowed();
203 _fineness = NETGENPlugin_Hypothesis::GetDefaultFineness();
204 mparams.uselocalh = NETGENPlugin_Hypothesis::GetDefaultSurfaceCurvature();
205 netgen::merge_solids = NETGENPlugin_Hypothesis::GetDefaultFuseEdges();
208 //=============================================================================
212 //=============================================================================
213 void SetLocalSize(TopoDS_Shape GeomShape, double LocalSize)
215 TopAbs_ShapeEnum GeomType = GeomShape.ShapeType();
216 if (GeomType == TopAbs_COMPOUND) {
217 for (TopoDS_Iterator it (GeomShape); it.More(); it.Next()) {
218 SetLocalSize(it.Value(), LocalSize);
223 if (! ShapesWithLocalSize.Contains(GeomShape))
224 key = ShapesWithLocalSize.Add(GeomShape);
226 key = ShapesWithLocalSize.FindIndex(GeomShape);
227 if (GeomType == TopAbs_VERTEX) {
228 VertexId2LocalSize[key] = LocalSize;
229 } else if (GeomType == TopAbs_EDGE) {
230 EdgeId2LocalSize[key] = LocalSize;
231 } else if (GeomType == TopAbs_FACE) {
232 FaceId2LocalSize[key] = LocalSize;
236 //=============================================================================
238 * Pass parameters to NETGEN
240 //=============================================================================
241 void NETGENPlugin_Mesher::SetParameters(const NETGENPlugin_Hypothesis* hyp)
245 netgen::MeshingParameters& mparams = netgen::mparam;
246 // Initialize global NETGEN parameters:
247 // maximal mesh segment size
248 mparams.maxh = hyp->GetMaxSize();
249 // maximal mesh element linear size
250 mparams.minh = hyp->GetMinSize();
251 // minimal number of segments per edge
252 mparams.segmentsperedge = hyp->GetNbSegPerEdge();
253 // rate of growth of size between elements
254 mparams.grading = hyp->GetGrowthRate();
255 // safety factor for curvatures (elements per radius)
256 mparams.curvaturesafety = hyp->GetNbSegPerRadius();
257 // create elements of second order
258 mparams.secondorder = hyp->GetSecondOrder() ? 1 : 0;
259 // quad-dominated surface meshing
260 // only triangles are allowed for volumic mesh (before realizing IMP 0021676)
262 mparams.quad = hyp->GetQuadAllowed() ? 1 : 0;
263 _optimize = hyp->GetOptimize();
264 _fineness = hyp->GetFineness();
265 mparams.uselocalh = hyp->GetSurfaceCurvature();
266 netgen::merge_solids = hyp->GetFuseEdges();
269 SMESH_Gen_i* smeshGen_i = SMESH_Gen_i::GetSMESHGen();
270 CORBA::Object_var anObject = smeshGen_i->GetNS()->Resolve("/myStudyManager");
271 SALOMEDS::StudyManager_var aStudyMgr = SALOMEDS::StudyManager::_narrow(anObject);
272 SALOMEDS::Study_var myStudy = aStudyMgr->GetStudyByID(hyp->GetStudyId());
274 const NETGENPlugin_Hypothesis::TLocalSize localSizes = hyp->GetLocalSizesAndEntries();
275 NETGENPlugin_Hypothesis::TLocalSize::const_iterator it = localSizes.begin();
276 for (it ; it != localSizes.end() ; it++)
278 std::string entry = (*it).first;
279 double val = (*it).second;
281 GEOM::GEOM_Object_var aGeomObj;
282 TopoDS_Shape S = TopoDS_Shape();
283 SALOMEDS::SObject_var aSObj = myStudy->FindObjectID( entry.c_str() );
284 if (!aSObj->_is_nil()) {
285 CORBA::Object_var obj = aSObj->GetObject();
286 aGeomObj = GEOM::GEOM_Object::_narrow(obj);
289 if ( !aGeomObj->_is_nil() )
290 S = smeshGen_i->GeomObjectToShape( aGeomObj.in() );
292 SetLocalSize(S, val);
297 //=============================================================================
299 * Pass simple parameters to NETGEN
301 //=============================================================================
303 void NETGENPlugin_Mesher::SetParameters(const NETGENPlugin_SimpleHypothesis_2D* hyp)
307 SetDefaultParameters();
310 //=============================================================================
312 * Link - a pair of integer numbers
314 //=============================================================================
318 Link(int _n1, int _n2) : n1(_n1), n2(_n2) {}
319 Link() : n1(0), n2(0) {}
322 int HashCode(const Link& aLink, int aLimit)
324 return HashCode(aLink.n1 + aLink.n2, aLimit);
327 Standard_Boolean IsEqual(const Link& aLink1, const Link& aLink2)
329 return (aLink1.n1 == aLink2.n1 && aLink1.n2 == aLink2.n2 ||
330 aLink1.n1 == aLink2.n2 && aLink1.n2 == aLink2.n1);
335 //================================================================================
337 * \brief return id of netgen point corresponding to SMDS node
339 //================================================================================
340 typedef map< const SMDS_MeshNode*, int > TNode2IdMap;
342 int ngNodeId( const SMDS_MeshNode* node,
343 netgen::Mesh& ngMesh,
344 TNode2IdMap& nodeNgIdMap)
346 int newNgId = ngMesh.GetNP() + 1;
348 TNode2IdMap::iterator node_id = nodeNgIdMap.insert( make_pair( node, newNgId )).first;
350 if ( node_id->second == newNgId)
352 #if defined(DUMP_SEGMENTS) || defined(DUMP_TRIANGLES)
353 cout << "Ng " << newNgId << " - " << node;
355 netgen::MeshPoint p( netgen::Point<3> (node->X(), node->Y(), node->Z()) );
356 ngMesh.AddPoint( p );
358 return node_id->second;
361 //================================================================================
363 * \brief Return computed EDGEs connected to the given one
365 //================================================================================
367 list< TopoDS_Edge > getConnectedEdges( const TopoDS_Edge& edge,
368 const TopoDS_Face& face,
369 const set< SMESH_subMesh* > & computedSM,
370 const SMESH_MesherHelper& helper,
371 map< SMESH_subMesh*, set< int > >& addedEdgeSM2Faces)
374 list< TopoDS_Edge > edges;
375 list< int > nbEdgesInWire;
376 int nbWires = SMESH_Block::GetOrderedEdges( face, edges, nbEdgesInWire);
378 // find <edge> within <edges>
379 list< TopoDS_Edge >::iterator eItFwd = edges.begin();
380 for ( ; eItFwd != edges.end(); ++eItFwd )
381 if ( edge.IsSame( *eItFwd ))
383 if ( eItFwd == edges.end()) return list< TopoDS_Edge>();
385 if ( eItFwd->Orientation() >= TopAbs_INTERNAL )
387 // connected INTERNAL edges returned from GetOrderedEdges() are wrongly oriented
388 // so treat each INTERNAL edge separately
389 TopoDS_Edge e = *eItFwd;
391 edges.push_back( e );
395 // get all computed EDGEs connected to <edge>
397 list< TopoDS_Edge >::iterator eItBack = eItFwd, ePrev;
398 TopoDS_Vertex vCommon;
399 TopTools_MapOfShape eAdded; // map used not to add a seam edge twice to <edges>
402 // put edges before <edge> to <edges> back
403 while ( edges.begin() != eItFwd )
404 edges.splice( edges.end(), edges, edges.begin() );
408 while ( ++eItFwd != edges.end() )
410 SMESH_subMesh* sm = helper.GetMesh()->GetSubMesh( *eItFwd );
412 bool connected = TopExp::CommonVertex( *ePrev, *eItFwd, vCommon );
413 bool computed = sm->IsMeshComputed();
414 bool added = addedEdgeSM2Faces[ sm ].count( helper.GetSubShapeID() );
415 bool doubled = !eAdded.Add( *eItFwd );
416 bool orientOK = (( ePrev ->Orientation() < TopAbs_INTERNAL ) ==
417 ( eItFwd->Orientation() < TopAbs_INTERNAL ) );
418 if ( !connected || !computed || !orientOK || added || doubled )
420 // stop advancement; move edges from tail to head
421 while ( edges.back() != *ePrev )
422 edges.splice( edges.begin(), edges, --edges.end() );
428 while ( eItBack != edges.begin() )
432 SMESH_subMesh* sm = helper.GetMesh()->GetSubMesh( *eItBack );
434 bool connected = TopExp::CommonVertex( *ePrev, *eItBack, vCommon );
435 bool computed = sm->IsMeshComputed();
436 bool added = addedEdgeSM2Faces[ sm ].count( helper.GetSubShapeID() );
437 bool doubled = !eAdded.Add( *eItBack );
438 bool orientOK = (( ePrev ->Orientation() < TopAbs_INTERNAL ) ==
439 ( eItBack->Orientation() < TopAbs_INTERNAL ) );
440 if ( !connected || !computed || !orientOK || added || doubled)
443 edges.erase( edges.begin(), ePrev );
447 if ( edges.front() != edges.back() )
449 // assure that the 1st vertex is meshed
450 TopoDS_Edge eLast = edges.back();
451 while ( !SMESH_Algo::VertexNode( SMESH_MesherHelper::IthVertex( 0, edges.front()), helper.GetMeshDS())
453 edges.front() != eLast )
454 edges.splice( edges.end(), edges, edges.begin() );
459 //================================================================================
461 * \brief Make triangulation of a shape precise enough
463 //================================================================================
465 void updateTriangulation( const TopoDS_Shape& shape )
467 // static set< Poly_Triangulation* > updated;
469 // TopLoc_Location loc;
470 // TopExp_Explorer fExp( shape, TopAbs_FACE );
471 // for ( ; fExp.More(); fExp.Next() )
473 // Handle(Poly_Triangulation) triangulation =
474 // BRep_Tool::Triangulation ( TopoDS::Face( fExp.Current() ), loc);
475 // if ( triangulation.IsNull() ||
476 // updated.insert( triangulation.operator->() ).second )
478 // BRepTools::Clean (shape);
481 BRepMesh_IncrementalMesh e(shape, 0.01, true);
483 catch (Standard_Failure)
486 // updated.erase( triangulation.operator->() );
487 // triangulation = BRep_Tool::Triangulation ( TopoDS::Face( fExp.Current() ), loc);
488 // updated.insert( triangulation.operator->() );
492 //================================================================================
494 * \brief Returns a medium node either existing in SMESH of created by NETGEN
495 * \param [in] corner1 - corner node 1
496 * \param [in] corner2 - corner node 2
497 * \param [in] defaultMedium - the node created by NETGEN
498 * \param [in] helper - holder of medium nodes existing in SMESH
499 * \return const SMDS_MeshNode* - the result node
501 //================================================================================
503 const SMDS_MeshNode* mediumNode( const SMDS_MeshNode* corner1,
504 const SMDS_MeshNode* corner2,
505 const SMDS_MeshNode* defaultMedium,
506 const SMESH_MesherHelper* helper)
510 TLinkNodeMap::const_iterator l2n =
511 helper->GetTLinkNodeMap().find( SMESH_TLink( corner1, corner2 ));
512 if ( l2n != helper->GetTLinkNodeMap().end() )
513 defaultMedium = l2n->second;
515 return defaultMedium;
518 //================================================================================
520 * \brief Assure that mesh on given shapes is quadratic
522 //================================================================================
524 void makeQuadratic( const TopTools_IndexedMapOfShape& shapes,
527 for ( int i = 1; i <= shapes.Extent(); ++i )
529 SMESHDS_SubMesh* smDS = mesh->GetMeshDS()->MeshElements( shapes(i) );
530 if ( !smDS ) continue;
531 SMDS_ElemIteratorPtr elemIt = smDS->GetElements();
532 if ( !elemIt->more() ) continue;
533 const SMDS_MeshElement* e = elemIt->next();
534 if ( !e || e->IsQuadratic() )
537 TIDSortedElemSet elems;
539 while ( elemIt->more() )
540 elems.insert( elems.end(), elemIt->next() );
542 SMESH_MeshEditor( mesh ).ConvertToQuadratic( /*3d=*/false, elems, /*biQuad=*/false );
548 //================================================================================
550 * \brief Initialize netgen::OCCGeometry with OCCT shape
552 //================================================================================
554 void NETGENPlugin_Mesher::PrepareOCCgeometry(netgen::OCCGeometry& occgeo,
555 const TopoDS_Shape& shape,
557 list< SMESH_subMesh* > * meshedSM,
558 NETGENPlugin_Internals* intern)
560 updateTriangulation( shape );
563 BRepBndLib::Add (shape, bb);
564 double x1,y1,z1,x2,y2,z2;
565 bb.Get (x1,y1,z1,x2,y2,z2);
566 MESSAGE("shape bounding box:\n" <<
567 "(" << x1 << " " << y1 << " " << z1 << ") " <<
568 "(" << x2 << " " << y2 << " " << z2 << ")");
569 netgen::Point<3> p1 = netgen::Point<3> (x1,y1,z1);
570 netgen::Point<3> p2 = netgen::Point<3> (x2,y2,z2);
571 occgeo.boundingbox = netgen::Box<3> (p1,p2);
573 occgeo.shape = shape;
576 // fill maps of shapes of occgeo with not yet meshed subshapes
578 // get root submeshes
579 list< SMESH_subMesh* > rootSM;
580 const int shapeID = mesh.GetMeshDS()->ShapeToIndex( shape );
581 if ( shapeID > 0 ) { // SMESH_subMesh with ID 0 may exist, don't use it!
582 rootSM.push_back( mesh.GetSubMesh( shape ));
585 for ( TopoDS_Iterator it( shape ); it.More(); it.Next() )
586 rootSM.push_back( mesh.GetSubMesh( it.Value() ));
589 // add subshapes of empty submeshes
590 list< SMESH_subMesh* >::iterator rootIt = rootSM.begin(), rootEnd = rootSM.end();
591 for ( ; rootIt != rootEnd; ++rootIt ) {
592 SMESH_subMesh * root = *rootIt;
593 SMESH_subMeshIteratorPtr smIt = root->getDependsOnIterator(/*includeSelf=*/true,
594 /*complexShapeFirst=*/true);
595 // to find a right orientation of subshapes (PAL20462)
596 TopTools_IndexedMapOfShape subShapes;
597 TopExp::MapShapes(root->GetSubShape(), subShapes);
598 while ( smIt->more() )
600 SMESH_subMesh* sm = smIt->next();
601 TopoDS_Shape shape = sm->GetSubShape();
602 if ( intern && intern->isShapeToPrecompute( shape ))
604 if ( !meshedSM || sm->IsEmpty() )
606 if ( shape.ShapeType() != TopAbs_VERTEX )
607 shape = subShapes( subShapes.FindIndex( shape ));// shape -> index -> oriented shape
608 if ( shape.Orientation() >= TopAbs_INTERNAL )
609 shape.Orientation( TopAbs_FORWARD ); // isuue 0020676
610 switch ( shape.ShapeType() ) {
611 case TopAbs_FACE : occgeo.fmap.Add( shape ); break;
612 case TopAbs_EDGE : occgeo.emap.Add( shape ); break;
613 case TopAbs_VERTEX: occgeo.vmap.Add( shape ); break;
614 case TopAbs_SOLID :occgeo.somap.Add( shape ); break;
618 // collect submeshes of meshed shapes
621 const int dim = SMESH_Gen::GetShapeDim( shape );
622 meshedSM[ dim ].push_back( sm );
626 occgeo.facemeshstatus.SetSize (occgeo.fmap.Extent());
627 occgeo.facemeshstatus = 0;
628 occgeo.face_maxh_modified.SetSize(occgeo.fmap.Extent());
629 occgeo.face_maxh_modified = 0;
630 occgeo.face_maxh.SetSize(occgeo.fmap.Extent());
631 occgeo.face_maxh = netgen::mparam.maxh;
634 //================================================================================
636 * \brief Return a default min size value suitable for the given geometry.
638 //================================================================================
640 double NETGENPlugin_Mesher::GetDefaultMinSize(const TopoDS_Shape& geom,
641 const double maxSize)
643 updateTriangulation( geom );
647 const int* pi[4] = { &i1, &i2, &i3, &i1 };
650 TopExp_Explorer fExp( geom, TopAbs_FACE );
651 for ( ; fExp.More(); fExp.Next() )
653 Handle(Poly_Triangulation) triangulation =
654 BRep_Tool::Triangulation ( TopoDS::Face( fExp.Current() ), loc);
655 if ( triangulation.IsNull() ) continue;
656 const double fTol = BRep_Tool::Tolerance( TopoDS::Face( fExp.Current() ));
657 const TColgp_Array1OfPnt& points = triangulation->Nodes();
658 const Poly_Array1OfTriangle& trias = triangulation->Triangles();
659 for ( int iT = trias.Lower(); iT <= trias.Upper(); ++iT )
661 trias(iT).Get( i1, i2, i3 );
662 for ( int j = 0; j < 3; ++j )
664 double dist2 = points(*pi[j]).SquareDistance( points( *pi[j+1] ));
665 if ( dist2 < minh && fTol*fTol < dist2 )
667 bb.Add( points(*pi[j]));
671 if ( minh > 0.25 * bb.SquareExtent() ) // simple geometry, rough triangulation
673 minh = 1e-3 * sqrt( bb.SquareExtent());
674 //cout << "BND BOX minh = " <<minh << endl;
678 minh = 3 * sqrt( minh ); // triangulation for visualization is rather fine
679 //cout << "TRIANGULATION minh = " <<minh << endl;
681 if ( minh > 0.5 * maxSize )
687 //================================================================================
689 * \brief Restrict size of elements at a given point
691 //================================================================================
693 void NETGENPlugin_Mesher::RestrictLocalSize(netgen::Mesh& ngMesh, const gp_XYZ& p, const double size)
695 if ( netgen::mparam.minh > size )
697 ngMesh.SetMinimalH( size );
698 netgen::mparam.minh = size;
700 netgen::Point3d pi(p.X(), p.Y(), p.Z());
701 ngMesh.RestrictLocalH( pi, size );
704 //================================================================================
706 * \brief fill ngMesh with nodes and elements of computed submeshes
708 //================================================================================
710 bool NETGENPlugin_Mesher::FillNgMesh(netgen::OCCGeometry& occgeom,
711 netgen::Mesh& ngMesh,
712 vector<const SMDS_MeshNode*>& nodeVec,
713 const list< SMESH_subMesh* > & meshedSM,
714 SMESH_MesherHelper* quadHelper,
715 SMESH_ProxyMesh::Ptr proxyMesh)
717 TNode2IdMap nodeNgIdMap;
718 for ( int i = 1; i < nodeVec.size(); ++i )
719 nodeNgIdMap.insert( make_pair( nodeVec[i], i ));
721 TopTools_MapOfShape visitedShapes;
722 map< SMESH_subMesh*, set< int > > visitedEdgeSM2Faces;
723 set< SMESH_subMesh* > computedSM( meshedSM.begin(), meshedSM.end() );
725 SMESH_MesherHelper helper (*_mesh);
727 int faceNgID = ngMesh.GetNFD();
729 list< SMESH_subMesh* >::const_iterator smIt, smEnd = meshedSM.end();
730 for ( smIt = meshedSM.begin(); smIt != smEnd; ++smIt )
732 SMESH_subMesh* sm = *smIt;
733 if ( !visitedShapes.Add( sm->GetSubShape() ))
736 const SMESHDS_SubMesh * smDS = sm->GetSubMeshDS();
737 if ( !smDS ) continue;
739 switch ( sm->GetSubShape().ShapeType() )
741 case TopAbs_EDGE: { // EDGE
742 // ----------------------
743 TopoDS_Edge geomEdge = TopoDS::Edge( sm->GetSubShape() );
744 if ( geomEdge.Orientation() >= TopAbs_INTERNAL )
745 geomEdge.Orientation( TopAbs_FORWARD ); // issue 0020676
747 // Add ng segments for each not meshed FACE the EDGE bounds
748 PShapeIteratorPtr fIt = helper.GetAncestors( geomEdge, *sm->GetFather(), TopAbs_FACE );
749 while ( const TopoDS_Shape * anc = fIt->next() )
751 faceNgID = occgeom.fmap.FindIndex( *anc );
753 continue; // meshed face
755 int faceSMDSId = helper.GetMeshDS()->ShapeToIndex( *anc );
756 if ( visitedEdgeSM2Faces[ sm ].count( faceSMDSId ))
757 continue; // already treated EDGE
759 TopoDS_Face face = TopoDS::Face( occgeom.fmap( faceNgID ));
760 if ( face.Orientation() >= TopAbs_INTERNAL )
761 face.Orientation( TopAbs_FORWARD ); // issue 0020676
763 // get all meshed EDGEs of the FACE connected to geomEdge (issue 0021140)
764 helper.SetSubShape( face );
765 list< TopoDS_Edge > edges = getConnectedEdges( geomEdge, face, computedSM, helper,
766 visitedEdgeSM2Faces );
768 continue; // wrong ancestor?
770 // find out orientation of <edges> within <face>
771 TopoDS_Edge eNotSeam = edges.front();
772 if ( helper.HasSeam() )
774 list< TopoDS_Edge >::iterator eIt = edges.begin();
775 while ( helper.IsRealSeam( *eIt )) ++eIt;
776 if ( eIt != edges.end() )
779 TopAbs_Orientation fOri = helper.GetSubShapeOri( face, eNotSeam );
780 bool isForwad = ( fOri == eNotSeam.Orientation() || fOri >= TopAbs_INTERNAL );
782 // get all nodes from connected <edges>
783 bool isQuad = smDS->NbElements() ? smDS->GetElements()->next()->IsQuadratic() : false;
784 StdMeshers_FaceSide fSide( face, edges, _mesh, isForwad, isQuad );
785 const vector<UVPtStruct>& points = fSide.GetUVPtStruct();
786 int i, nbSeg = fSide.NbSegments();
788 // remember EDGEs of fSide to treat only once
789 for ( int iE = 0; iE < fSide.NbEdges(); ++iE )
790 visitedEdgeSM2Faces[ helper.GetMesh()->GetSubMesh( fSide.Edge(iE )) ].insert(faceSMDSId);
792 double otherSeamParam = 0;
797 int prevNgId = ngNodeId( points[0].node, ngMesh, nodeNgIdMap );
799 for ( i = 0; i < nbSeg; ++i )
801 const UVPtStruct& p1 = points[ i ];
802 const UVPtStruct& p2 = points[ i+1 ];
804 if ( p1.node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX ) //an EDGE begins
807 if ( helper.IsRealSeam( p1.node->getshapeId() ))
809 TopoDS_Edge e = fSide.Edge( fSide.EdgeIndex( 0.5 * ( p1.normParam + p2.normParam )));
810 isSeam = helper.IsRealSeam( e );
813 otherSeamParam = helper.GetOtherParam( helper.GetPeriodicIndex() & 1 ? p2.u : p2.v );
820 seg[1] = prevNgId = ngNodeId( p2.node, ngMesh, nodeNgIdMap );
821 // node param on curve
822 seg.epgeominfo[ 0 ].dist = p1.param;
823 seg.epgeominfo[ 1 ].dist = p2.param;
825 seg.epgeominfo[ 0 ].u = p1.u;
826 seg.epgeominfo[ 0 ].v = p1.v;
827 seg.epgeominfo[ 1 ].u = p2.u;
828 seg.epgeominfo[ 1 ].v = p2.v;
830 //geomEdge = fSide.Edge( fSide.EdgeIndex( 0.5 * ( p1.normParam + p2.normParam )));
831 //seg.epgeominfo[ 0 ].edgenr = seg.epgeominfo[ 1 ].edgenr = occgeom.emap.FindIndex( geomEdge );
833 //seg.epgeominfo[ iEnd ].edgenr = edgeID; // = geom.emap.FindIndex(edge);
834 seg.si = faceNgID; // = geom.fmap.FindIndex (face);
835 seg.edgenr = ngMesh.GetNSeg() + 1; // segment id
836 ngMesh.AddSegment (seg);
838 SMESH_TNodeXYZ np1( p1.node ), np2( p2.node );
839 RestrictLocalSize( ngMesh, 0.5*(np1+np2), (np1-np2).Modulus() );
842 cout << "Segment: " << seg.edgenr << " on SMESH face " << helper.GetMeshDS()->ShapeToIndex( face ) << endl
843 << "\tface index: " << seg.si << endl
844 << "\tp1: " << seg[0] << endl
845 << "\tp2: " << seg[1] << endl
846 << "\tp0 param: " << seg.epgeominfo[ 0 ].dist << endl
847 << "\tp0 uv: " << seg.epgeominfo[ 0 ].u <<", "<< seg.epgeominfo[ 0 ].v << endl
848 //<< "\tp0 edge: " << seg.epgeominfo[ 0 ].edgenr << endl
849 << "\tp1 param: " << seg.epgeominfo[ 1 ].dist << endl
850 << "\tp1 uv: " << seg.epgeominfo[ 1 ].u <<", "<< seg.epgeominfo[ 1 ].v << endl;
851 //<< "\tp1 edge: " << seg.epgeominfo[ 1 ].edgenr << endl;
855 if ( helper.GetPeriodicIndex() && 1 ) {
856 seg.epgeominfo[ 0 ].u = otherSeamParam;
857 seg.epgeominfo[ 1 ].u = otherSeamParam;
858 swap (seg.epgeominfo[0].v, seg.epgeominfo[1].v);
860 seg.epgeominfo[ 0 ].v = otherSeamParam;
861 seg.epgeominfo[ 1 ].v = otherSeamParam;
862 swap (seg.epgeominfo[0].u, seg.epgeominfo[1].u);
864 swap (seg[0], seg[1]);
865 swap (seg.epgeominfo[0].dist, seg.epgeominfo[1].dist);
866 seg.edgenr = ngMesh.GetNSeg() + 1; // segment id
867 ngMesh.AddSegment (seg);
869 cout << "Segment: " << seg.edgenr << endl
870 << "\t is SEAM (reverse) of the previous. "
871 << " Other " << (helper.GetPeriodicIndex() && 1 ? "U" : "V")
872 << " = " << otherSeamParam << endl;
875 else if ( fOri == TopAbs_INTERNAL )
877 swap (seg[0], seg[1]);
878 swap( seg.epgeominfo[0], seg.epgeominfo[1] );
879 seg.edgenr = ngMesh.GetNSeg() + 1; // segment id
880 ngMesh.AddSegment (seg);
882 cout << "Segment: " << seg.edgenr << endl << "\t is REVERSE of the previous" << endl;
886 } // loop on geomEdge ancestors
888 if ( quadHelper ) // remember medium nodes of sub-meshes
890 SMDS_ElemIteratorPtr edges = smDS->GetElements();
891 while ( edges->more() )
893 const SMDS_MeshElement* e = edges->next();
894 if ( !quadHelper->AddTLinks( static_cast< const SMDS_MeshEdge*>( e )))
900 } // case TopAbs_EDGE
902 case TopAbs_FACE: { // FACE
903 // ----------------------
904 const TopoDS_Face& geomFace = TopoDS::Face( sm->GetSubShape() );
905 helper.SetSubShape( geomFace );
906 bool isInternalFace = ( geomFace.Orientation() == TopAbs_INTERNAL );
908 // Find solids the geomFace bounds
909 int solidID1 = 0, solidID2 = 0;
910 StdMeshers_QuadToTriaAdaptor* quadAdaptor =
911 dynamic_cast<StdMeshers_QuadToTriaAdaptor*>( proxyMesh.get() );
914 solidID1 = occgeom.somap.FindIndex( quadAdaptor->GetShape() );
918 PShapeIteratorPtr solidIt = helper.GetAncestors( geomFace, *sm->GetFather(), TopAbs_SOLID);
919 while ( const TopoDS_Shape * solid = solidIt->next() )
921 int id = occgeom.somap.FindIndex ( *solid );
922 if ( solidID1 && id != solidID1 ) solidID2 = id;
926 // Add ng face descriptors of meshed faces
928 ngMesh.AddFaceDescriptor (netgen::FaceDescriptor(faceNgID, solidID1, solidID2, 0));
930 // if second oreder is required, even already meshed faces must be passed to NETGEN
931 int fID = occgeom.fmap.Add( geomFace );
932 while ( fID < faceNgID ) // geomFace is already in occgeom.fmap, add a copy
933 fID = occgeom.fmap.Add( BRepBuilderAPI_Copy( geomFace, /*copyGeom=*/false ));
934 // Problem with the second order in a quadrangular mesh remains.
935 // 1) All quadrangles generated by NETGEN are moved to an inexistent face
936 // by FillSMesh() (find "AddFaceDescriptor")
937 // 2) Temporary triangles generated by StdMeshers_QuadToTriaAdaptor
938 // are on faces where quadrangles were.
939 // Due to these 2 points, wrong geom faces are used while conversion to qudratic
940 // of the mentioned above quadrangles and triangles
942 // Orient the face correctly in solidID1 (issue 0020206)
943 bool reverse = false;
945 TopoDS_Shape solid = occgeom.somap( solidID1 );
946 TopAbs_Orientation faceOriInSolid = helper.GetSubShapeOri( solid, geomFace );
947 if ( faceOriInSolid >= 0 )
949 helper.IsReversedSubMesh( TopoDS::Face( geomFace.Oriented( faceOriInSolid )));
952 // Add surface elements
954 netgen::Element2d tri(3);
955 tri.SetIndex ( faceNgID );
958 #ifdef DUMP_TRIANGLES
959 cout << "SMESH face " << helper.GetMeshDS()->ShapeToIndex( geomFace )
960 << " internal="<<isInternalFace << endl;
963 smDS = proxyMesh->GetSubMesh( geomFace );
965 SMDS_ElemIteratorPtr faces = smDS->GetElements();
966 while ( faces->more() )
968 const SMDS_MeshElement* f = faces->next();
969 if ( f->NbNodes() % 3 != 0 ) // not triangle
971 PShapeIteratorPtr solidIt=helper.GetAncestors(geomFace,*sm->GetFather(),TopAbs_SOLID);
972 if ( const TopoDS_Shape * solid = solidIt->next() )
973 sm = _mesh->GetSubMesh( *solid );
974 SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
975 smError.reset( new SMESH_ComputeError(COMPERR_BAD_INPUT_MESH,"Not triangle submesh"));
976 smError->myBadElements.push_back( f );
980 for ( int i = 0; i < 3; ++i )
982 const SMDS_MeshNode* node = f->GetNode( i ), * inFaceNode=0;
984 // get node UV on face
985 int shapeID = node->getshapeId();
986 if ( helper.IsSeamShape( shapeID ))
987 if ( helper.IsSeamShape( f->GetNodeWrap( i+1 )->getshapeId() ))
988 inFaceNode = f->GetNodeWrap( i-1 );
990 inFaceNode = f->GetNodeWrap( i+1 );
991 gp_XY uv = helper.GetNodeUV( geomFace, node, inFaceNode );
993 int ind = reverse ? 3-i : i+1;
994 tri.GeomInfoPi(ind).u = uv.X();
995 tri.GeomInfoPi(ind).v = uv.Y();
996 tri.PNum (ind) = ngNodeId( node, ngMesh, nodeNgIdMap );
999 ngMesh.AddSurfaceElement (tri);
1000 #ifdef DUMP_TRIANGLES
1001 cout << tri << endl;
1004 if ( isInternalFace )
1006 swap( tri[1], tri[2] );
1007 ngMesh.AddSurfaceElement (tri);
1008 #ifdef DUMP_TRIANGLES
1009 cout << tri << endl;
1014 if ( quadHelper ) // remember medium nodes of sub-meshes
1016 SMDS_ElemIteratorPtr faces = smDS->GetElements();
1017 while ( faces->more() )
1019 const SMDS_MeshElement* f = faces->next();
1020 if ( !quadHelper->AddTLinks( static_cast< const SMDS_MeshFace*>( f )))
1026 } // case TopAbs_FACE
1028 case TopAbs_VERTEX: { // VERTEX
1029 // --------------------------
1030 // issue 0021405. Add node only if a VERTEX is shared by a not meshed EDGE,
1031 // else netgen removes a free node and nodeVector becomes invalid
1032 PShapeIteratorPtr ansIt = helper.GetAncestors( sm->GetSubShape(),
1036 while ( const TopoDS_Shape* e = ansIt->next() )
1038 SMESH_subMesh* eSub = helper.GetMesh()->GetSubMesh( *e );
1039 if (( toAdd = eSub->IsEmpty() )) break;
1043 SMDS_NodeIteratorPtr nodeIt = smDS->GetNodes();
1044 if ( nodeIt->more() )
1045 ngNodeId( nodeIt->next(), ngMesh, nodeNgIdMap );
1051 } // loop on submeshes
1054 nodeVec.resize( ngMesh.GetNP() + 1 );
1055 TNode2IdMap::iterator node_NgId, nodeNgIdEnd = nodeNgIdMap.end();
1056 for ( node_NgId = nodeNgIdMap.begin(); node_NgId != nodeNgIdEnd; ++node_NgId)
1057 nodeVec[ node_NgId->second ] = node_NgId->first;
1062 //================================================================================
1064 * \brief Duplicate mesh faces on internal geom faces
1066 //================================================================================
1068 void NETGENPlugin_Mesher::FixIntFaces(const netgen::OCCGeometry& occgeom,
1069 netgen::Mesh& ngMesh,
1070 NETGENPlugin_Internals& internalShapes)
1072 SMESHDS_Mesh* meshDS = internalShapes.getMesh().GetMeshDS();
1074 // find ng indices of internal faces
1076 for ( int ngFaceID = 1; ngFaceID <= occgeom.fmap.Extent(); ++ngFaceID )
1078 int smeshID = meshDS->ShapeToIndex( occgeom.fmap( ngFaceID ));
1079 if ( internalShapes.isInternalShape( smeshID ))
1080 ngFaceIds.insert( ngFaceID );
1082 if ( !ngFaceIds.empty() )
1085 int i, nbFaces = ngMesh.GetNSE();
1086 for (int i = 1; i <= nbFaces; ++i)
1088 netgen::Element2d elem = ngMesh.SurfaceElement(i);
1089 if ( ngFaceIds.count( elem.GetIndex() ))
1091 swap( elem[1], elem[2] );
1092 ngMesh.AddSurfaceElement (elem);
1100 //================================================================================
1101 // define gp_XY_Subtracted pointer to function calling gp_XY::Subtracted(gp_XY)
1102 gp_XY_FunPtr(Subtracted);
1103 //gp_XY_FunPtr(Added);
1105 //================================================================================
1107 * \brief Evaluate distance between two 2d points along the surface
1109 //================================================================================
1111 double evalDist( const gp_XY& uv1,
1113 const Handle(Geom_Surface)& surf,
1114 const int stopHandler=-1)
1116 if ( stopHandler > 0 ) // continue recursion
1118 gp_XY mid = SMESH_MesherHelper::GetMiddleUV( surf, uv1, uv2 );
1119 return evalDist( uv1,mid, surf, stopHandler-1 ) + evalDist( mid,uv2, surf, stopHandler-1 );
1121 double dist3D = surf->Value( uv1.X(), uv1.Y() ).Distance( surf->Value( uv2.X(), uv2.Y() ));
1122 if ( stopHandler == 0 ) // stop recursion
1125 // start recursion if necessary
1126 double dist2D = SMESH_MesherHelper::applyIn2D(surf, uv1, uv2, gp_XY_Subtracted, 0).Modulus();
1127 if ( fabs( dist3D - dist2D ) < dist2D * 1e-10 )
1128 return dist3D; // equal parametrization of a planar surface
1130 return evalDist( uv1, uv2, surf, 3 ); // start recursion
1133 //================================================================================
1135 * \brief Data of vertex internal in geom face
1137 //================================================================================
1141 gp_XY uv; //!< UV in face parametric space
1142 int ngId; //!< ng id of corrsponding node
1143 gp_XY uvClose; //!< UV of closest boundary node
1144 int ngIdClose; //!< ng id of closest boundary node
1147 //================================================================================
1149 * \brief Data of vertex internal in solid
1151 //================================================================================
1155 int ngId; //!< ng id of corresponding node
1156 int ngIdClose; //!< ng id of closest 2d mesh element
1157 int ngIdCloseN; //!< ng id of closest node of the closest 2d mesh element
1160 inline double dist2(const netgen::MeshPoint& p1, const netgen::MeshPoint& p2)
1162 return gp_Pnt( NGPOINT_COORDS(p1)).SquareDistance( gp_Pnt( NGPOINT_COORDS(p2)));
1166 //================================================================================
1168 * \brief Make netgen take internal vertices in faces into account by adding
1169 * segments including internal vertices
1171 * This function works in supposition that 1D mesh is already computed in ngMesh
1173 //================================================================================
1175 void NETGENPlugin_Mesher::AddIntVerticesInFaces(const netgen::OCCGeometry& occgeom,
1176 netgen::Mesh& ngMesh,
1177 vector<const SMDS_MeshNode*>& nodeVec,
1178 NETGENPlugin_Internals& internalShapes)
1180 if ( nodeVec.size() < ngMesh.GetNP() )
1181 nodeVec.resize( ngMesh.GetNP(), 0 );
1183 SMESHDS_Mesh* meshDS = internalShapes.getMesh().GetMeshDS();
1184 SMESH_MesherHelper helper( internalShapes.getMesh() );
1186 const map<int,list<int> >& face2Vert = internalShapes.getFacesWithVertices();
1187 map<int,list<int> >::const_iterator f2v = face2Vert.begin();
1188 for ( ; f2v != face2Vert.end(); ++f2v )
1190 const TopoDS_Face& face = TopoDS::Face( meshDS->IndexToShape( f2v->first ));
1191 if ( face.IsNull() ) continue;
1192 int faceNgID = occgeom.fmap.FindIndex (face);
1193 if ( faceNgID < 0 ) continue;
1195 TopLoc_Location loc;
1196 Handle(Geom_Surface) surf = BRep_Tool::Surface(face,loc);
1198 helper.SetSubShape( face );
1199 helper.SetElementsOnShape( true );
1201 // Get data of internal vertices and add them to ngMesh
1203 multimap< double, TIntVData > dist2VData; // sort vertices by distance from boundary nodes
1205 int i, nbSegInit = ngMesh.GetNSeg();
1207 // boundary characteristics
1208 double totSegLen2D = 0;
1211 const list<int>& iVertices = f2v->second;
1212 list<int>::const_iterator iv = iVertices.begin();
1213 for ( int nbV = 0; iv != iVertices.end(); ++iv, nbV++ )
1216 // get node on vertex
1217 const TopoDS_Vertex V = TopoDS::Vertex( meshDS->IndexToShape( *iv ));
1218 const SMDS_MeshNode * nV = SMESH_Algo::VertexNode( V, meshDS );
1221 SMESH_subMesh* sm = helper.GetMesh()->GetSubMesh( V );
1222 sm->ComputeStateEngine( SMESH_subMesh::COMPUTE );
1223 nV = SMESH_Algo::VertexNode( V, meshDS );
1224 if ( !nV ) continue;
1227 netgen::MeshPoint mp( netgen::Point<3> (nV->X(), nV->Y(), nV->Z()) );
1228 ngMesh.AddPoint ( mp, 1, netgen::EDGEPOINT );
1229 vData.ngId = ngMesh.GetNP();
1230 nodeVec.push_back( nV );
1234 vData.uv = helper.GetNodeUV( face, nV, 0, &uvOK );
1235 if ( !uvOK ) helper.CheckNodeUV( face, nV, vData.uv, BRep_Tool::Tolerance(V),/*force=*/1);
1237 // loop on all segments of the face to find the node closest to vertex and to count
1238 // average segment 2d length
1239 double closeDist2 = numeric_limits<double>::max(), dist2;
1241 for (i = 1; i <= ngMesh.GetNSeg(); ++i)
1243 netgen::Segment & seg = ngMesh.LineSegment(i);
1244 if ( seg.si != faceNgID ) continue;
1246 for ( int iEnd = 0; iEnd < 2; ++iEnd)
1248 uv[iEnd].SetCoord( seg.epgeominfo[iEnd].u, seg.epgeominfo[iEnd].v );
1249 if ( ngIdLast == seg[ iEnd ] ) continue;
1250 dist2 = helper.applyIn2D(surf, uv[iEnd], vData.uv, gp_XY_Subtracted,0).SquareModulus();
1251 if ( dist2 < closeDist2 )
1252 vData.ngIdClose = seg[ iEnd ], vData.uvClose = uv[iEnd], closeDist2 = dist2;
1253 ngIdLast = seg[ iEnd ];
1257 totSegLen2D += helper.applyIn2D(surf, uv[0], uv[1], gp_XY_Subtracted, false).Modulus();
1261 dist2VData.insert( make_pair( closeDist2, vData ));
1264 if ( totNbSeg == 0 ) break;
1265 double avgSegLen2d = totSegLen2D / totNbSeg;
1267 // Loop on vertices to add segments
1269 multimap< double, TIntVData >::iterator dist_vData = dist2VData.begin();
1270 for ( ; dist_vData != dist2VData.end(); ++dist_vData )
1272 double closeDist2 = dist_vData->first, dist2;
1273 TIntVData & vData = dist_vData->second;
1275 // try to find more close node among segments added for internal vertices
1276 for (i = nbSegInit+1; i <= ngMesh.GetNSeg(); ++i)
1278 netgen::Segment & seg = ngMesh.LineSegment(i);
1279 if ( seg.si != faceNgID ) continue;
1281 for ( int iEnd = 0; iEnd < 2; ++iEnd)
1283 uv[iEnd].SetCoord( seg.epgeominfo[iEnd].u, seg.epgeominfo[iEnd].v );
1284 dist2 = helper.applyIn2D(surf, uv[iEnd], vData.uv, gp_XY_Subtracted,0).SquareModulus();
1285 if ( dist2 < closeDist2 )
1286 vData.ngIdClose = seg[ iEnd ], vData.uvClose = uv[iEnd], closeDist2 = dist2;
1289 // decide whether to use the closest node as the second end of segment or to
1290 // create a new point
1291 int segEnd1 = vData.ngId;
1292 int segEnd2 = vData.ngIdClose; // to use closest node
1293 gp_XY uvV = vData.uv, uvP = vData.uvClose;
1294 double segLenHint = ngMesh.GetH( ngMesh.Point( vData.ngId ));
1295 double nodeDist2D = sqrt( closeDist2 );
1296 double nodeDist3D = evalDist( vData.uv, vData.uvClose, surf );
1297 bool avgLenOK = ( avgSegLen2d < 0.75 * nodeDist2D );
1298 bool hintLenOK = ( segLenHint < 0.75 * nodeDist3D );
1299 //cout << "uvV " << uvV.X() <<","<<uvV.Y() << " ";
1300 if ( hintLenOK || avgLenOK )
1302 // create a point between the closest node and V
1305 double r = min( 0.5, ( hintLenOK ? segLenHint/nodeDist3D : avgSegLen2d/nodeDist2D ));
1306 // direction from V to closet node in 2D
1307 gp_Dir2d v2n( helper.applyIn2D(surf, uvP, uvV, gp_XY_Subtracted, false ));
1309 uvP = vData.uv + r * nodeDist2D * v2n.XY();
1310 gp_Pnt P = surf->Value( uvP.X(), uvP.Y() ).Transformed( loc );
1312 netgen::MeshPoint mp( netgen::Point<3> (P.X(), P.Y(), P.Z()));
1313 ngMesh.AddPoint ( mp, 1, netgen::EDGEPOINT );
1314 segEnd2 = ngMesh.GetNP();
1315 //cout << "Middle " << r << " uv " << uvP.X() << "," << uvP.Y() << "( " << ngMesh.Point(segEnd2).X()<<","<<ngMesh.Point(segEnd2).Y()<<","<<ngMesh.Point(segEnd2).Z()<<" )"<< endl;
1316 SMDS_MeshNode * nP = helper.AddNode(P.X(), P.Y(), P.Z());
1317 nodeVec.push_back( nP );
1319 //else cout << "at Node " << " uv " << uvP.X() << "," << uvP.Y() << endl;
1322 netgen::Segment seg;
1324 if ( segEnd1 > segEnd2 ) swap( segEnd1, segEnd2 ), swap( uvV, uvP );
1325 seg[0] = segEnd1; // ng node id
1326 seg[1] = segEnd2; // ng node id
1327 seg.edgenr = ngMesh.GetNSeg() + 1;// segment id
1330 seg.epgeominfo[ 0 ].dist = 0; // param on curve
1331 seg.epgeominfo[ 0 ].u = uvV.X();
1332 seg.epgeominfo[ 0 ].v = uvV.Y();
1333 seg.epgeominfo[ 1 ].dist = 1; // param on curve
1334 seg.epgeominfo[ 1 ].u = uvP.X();
1335 seg.epgeominfo[ 1 ].v = uvP.Y();
1337 // seg.epgeominfo[ 0 ].edgenr = 10; // = geom.emap.FindIndex(edge);
1338 // seg.epgeominfo[ 1 ].edgenr = 10; // = geom.emap.FindIndex(edge);
1340 ngMesh.AddSegment (seg);
1342 // add reverse segment
1343 swap (seg[0], seg[1]);
1344 swap( seg.epgeominfo[0], seg.epgeominfo[1] );
1345 seg.edgenr = ngMesh.GetNSeg() + 1; // segment id
1346 ngMesh.AddSegment (seg);
1352 //================================================================================
1354 * \brief Make netgen take internal vertices in solids into account by adding
1355 * faces including internal vertices
1357 * This function works in supposition that 2D mesh is already computed in ngMesh
1359 //================================================================================
1361 void NETGENPlugin_Mesher::AddIntVerticesInSolids(const netgen::OCCGeometry& occgeom,
1362 netgen::Mesh& ngMesh,
1363 vector<const SMDS_MeshNode*>& nodeVec,
1364 NETGENPlugin_Internals& internalShapes)
1366 #ifdef DUMP_TRIANGLES_SCRIPT
1367 // create a python script making a mesh containing triangles added for internal vertices
1368 ofstream py(DUMP_TRIANGLES_SCRIPT);
1369 py << "import SMESH"<< endl
1370 << "from salome.smesh import smeshBuilder"<<endl
1371 << "smesh = smeshBuilder.New(salome.myStudy)"
1372 << "m = smesh.Mesh(name='triangles')" << endl;
1374 if ( nodeVec.size() < ngMesh.GetNP() )
1375 nodeVec.resize( ngMesh.GetNP(), 0 );
1377 SMESHDS_Mesh* meshDS = internalShapes.getMesh().GetMeshDS();
1378 SMESH_MesherHelper helper( internalShapes.getMesh() );
1380 const map<int,list<int> >& so2Vert = internalShapes.getSolidsWithVertices();
1381 map<int,list<int> >::const_iterator s2v = so2Vert.begin();
1382 for ( ; s2v != so2Vert.end(); ++s2v )
1384 const TopoDS_Shape& solid = meshDS->IndexToShape( s2v->first );
1385 if ( solid.IsNull() ) continue;
1386 int solidNgID = occgeom.somap.FindIndex (solid);
1387 if ( solidNgID < 0 && !occgeom.somap.IsEmpty() ) continue;
1389 helper.SetSubShape( solid );
1390 helper.SetElementsOnShape( true );
1392 // find ng indices of faces within the solid
1394 for (TopExp_Explorer fExp(solid, TopAbs_FACE); fExp.More(); fExp.Next() )
1395 ngFaceIds.insert( occgeom.fmap.FindIndex( fExp.Current() ));
1396 if ( ngFaceIds.size() == 1 && *ngFaceIds.begin() == 0 )
1397 ngFaceIds.insert( 1 );
1399 // Get data of internal vertices and add them to ngMesh
1401 multimap< double, TIntVSoData > dist2VData; // sort vertices by distance from ng faces
1403 int i, nbFaceInit = ngMesh.GetNSE();
1405 // boundary characteristics
1406 double totSegLen = 0;
1409 const list<int>& iVertices = s2v->second;
1410 list<int>::const_iterator iv = iVertices.begin();
1411 for ( int nbV = 0; iv != iVertices.end(); ++iv, nbV++ )
1414 const TopoDS_Vertex V = TopoDS::Vertex( meshDS->IndexToShape( *iv ));
1416 // get node on vertex
1417 const SMDS_MeshNode * nV = SMESH_Algo::VertexNode( V, meshDS );
1420 SMESH_subMesh* sm = helper.GetMesh()->GetSubMesh( V );
1421 sm->ComputeStateEngine( SMESH_subMesh::COMPUTE );
1422 nV = SMESH_Algo::VertexNode( V, meshDS );
1423 if ( !nV ) continue;
1426 netgen::MeshPoint mpV( netgen::Point<3> (nV->X(), nV->Y(), nV->Z()) );
1427 ngMesh.AddPoint ( mpV, 1, netgen::FIXEDPOINT );
1428 vData.ngId = ngMesh.GetNP();
1429 nodeVec.push_back( nV );
1431 // loop on all 2d elements to find the one closest to vertex and to count
1432 // average segment length
1433 double closeDist2 = numeric_limits<double>::max(), avgDist2;
1434 for (i = 1; i <= ngMesh.GetNSE(); ++i)
1436 const netgen::Element2d& elem = ngMesh.SurfaceElement(i);
1437 if ( !ngFaceIds.count( elem.GetIndex() )) continue;
1439 multimap< double, int> dist2nID; // sort nodes of element by distance from V
1440 for ( int j = 0; j < elem.GetNP(); ++j)
1442 netgen::MeshPoint mp = ngMesh.Point( elem[j] );
1443 double d2 = dist2( mpV, mp );
1444 dist2nID.insert( make_pair( d2, elem[j] ));
1445 avgDist2 += d2 / elem.GetNP();
1447 totNbSeg++, totSegLen+= sqrt( dist2( mp, ngMesh.Point( elem[(j+1)%elem.GetNP()])));
1449 double dist = dist2nID.begin()->first; //avgDist2;
1450 if ( dist < closeDist2 )
1451 vData.ngIdClose= i, vData.ngIdCloseN= dist2nID.begin()->second, closeDist2= dist;
1453 dist2VData.insert( make_pair( closeDist2, vData ));
1456 if ( totNbSeg == 0 ) break;
1457 double avgSegLen = totSegLen / totNbSeg;
1459 // Loop on vertices to add triangles
1461 multimap< double, TIntVSoData >::iterator dist_vData = dist2VData.begin();
1462 for ( ; dist_vData != dist2VData.end(); ++dist_vData )
1464 double closeDist2 = dist_vData->first;
1465 TIntVSoData & vData = dist_vData->second;
1467 const netgen::MeshPoint& mpV = ngMesh.Point( vData.ngId );
1469 // try to find more close face among ones added for internal vertices
1470 for (i = nbFaceInit+1; i <= ngMesh.GetNSE(); ++i)
1472 double avgDist2 = 0;
1473 multimap< double, int> dist2nID;
1474 const netgen::Element2d& elem = ngMesh.SurfaceElement(i);
1475 for ( int j = 0; j < elem.GetNP(); ++j)
1477 double d = dist2( mpV, ngMesh.Point( elem[j] ));
1478 dist2nID.insert( make_pair( d, elem[j] ));
1479 avgDist2 += d / elem.GetNP();
1480 if ( avgDist2 < closeDist2 )
1481 vData.ngIdClose= i, vData.ngIdCloseN= dist2nID.begin()->second, closeDist2= avgDist2;
1484 // sort nodes of the closest face by angle with vector from V to the closest node
1485 const double tol = numeric_limits<double>::min();
1486 map< double, int > angle2ID;
1487 const netgen::Element2d& closeFace = ngMesh.SurfaceElement( vData.ngIdClose );
1488 netgen::MeshPoint mp[2];
1489 mp[0] = ngMesh.Point( vData.ngIdCloseN );
1490 gp_XYZ p1( NGPOINT_COORDS( mp[0] ));
1491 gp_XYZ pV( NGPOINT_COORDS( mpV ));
1492 gp_Vec v2p1( pV, p1 );
1493 double distN1 = v2p1.Magnitude();
1494 if ( distN1 <= tol ) continue;
1496 for ( int j = 0; j < closeFace.GetNP(); ++j)
1498 mp[1] = ngMesh.Point( closeFace[j] );
1499 gp_Vec v2p( pV, gp_Pnt( NGPOINT_COORDS( mp[1] )) );
1500 angle2ID.insert( make_pair( v2p1.Angle( v2p ), closeFace[j]));
1502 // get node with angle of 60 degrees or greater
1503 map< double, int >::iterator angle_id = angle2ID.lower_bound( 60. * M_PI / 180. );
1504 if ( angle_id == angle2ID.end() ) angle_id = --angle2ID.end();
1505 const double minAngle = 30. * M_PI / 180.;
1506 const double angle = angle_id->first;
1507 bool angleOK = ( angle > minAngle );
1509 // find points to create a triangle
1510 netgen::Element2d tri(3);
1512 tri[0] = vData.ngId;
1513 tri[1] = vData.ngIdCloseN; // to use the closest nodes
1514 tri[2] = angle_id->second; // to use the node with best angle
1516 // decide whether to use the closest node and the node with best angle or to create new ones
1517 for ( int isBestAngleN = 0; isBestAngleN < 2; ++isBestAngleN )
1519 bool createNew = !angleOK, distOK = true;
1521 int triInd = isBestAngleN ? 2 : 1;
1522 mp[isBestAngleN] = ngMesh.Point( tri[triInd] );
1527 double distN2 = sqrt( dist2( mpV, mp[isBestAngleN]));
1528 createNew = ( fabs( distN2 - distN1 ) > 0.25 * distN1 );
1530 else if ( angle < tol )
1532 v2p1.SetX( v2p1.X() + 1e-3 );
1538 double segLenHint = ngMesh.GetH( ngMesh.Point( vData.ngId ));
1539 bool avgLenOK = ( avgSegLen < 0.75 * distN1 );
1540 bool hintLenOK = ( segLenHint < 0.75 * distN1 );
1541 createNew = (createNew || avgLenOK || hintLenOK );
1542 // we create a new node not closer than 0.5 to the closest face
1543 // in order not to clash with other close face
1544 double r = min( 0.5, ( hintLenOK ? segLenHint : avgSegLen ) / distN1 );
1545 distFromV = r * distN1;
1549 // create a new point, between the node and the vertex if angleOK
1550 gp_XYZ p( NGPOINT_COORDS( mp[isBestAngleN] ));
1551 gp_Vec v2p( pV, p ); v2p.Normalize();
1552 if ( isBestAngleN && !angleOK )
1553 p = p1 + gp_Dir( v2p.XYZ() - v2p1.XYZ()).XYZ() * distN1 * 0.95;
1555 p = pV + v2p.XYZ() * distFromV;
1557 if ( !isBestAngleN ) p1 = p, distN1 = distFromV;
1559 mp[isBestAngleN].SetPoint( netgen::Point<3> (p.X(), p.Y(), p.Z()));
1560 ngMesh.AddPoint ( mp[isBestAngleN], 1, netgen::SURFACEPOINT );
1561 tri[triInd] = ngMesh.GetNP();
1562 nodeVec.push_back( helper.AddNode( p.X(), p.Y(), p.Z()) );
1565 ngMesh.AddSurfaceElement (tri);
1566 swap( tri[1], tri[2] );
1567 ngMesh.AddSurfaceElement (tri);
1569 #ifdef DUMP_TRIANGLES_SCRIPT
1570 py << "n1 = m.AddNode( "<< mpV.X()<<", "<< mpV.Y()<<", "<< mpV.Z()<<") "<< endl
1571 << "n2 = m.AddNode( "<< mp[0].X()<<", "<< mp[0].Y()<<", "<< mp[0].Z()<<") "<< endl
1572 << "n3 = m.AddNode( "<< mp[1].X()<<", "<< mp[1].Y()<<", "<< mp[1].Z()<<" )" << endl
1573 << "m.AddFace([n1,n2,n3])" << endl;
1575 } // loop on internal vertices of a solid
1577 } // loop on solids with internal vertices
1580 //================================================================================
1582 * \brief Fill netgen mesh with segments of a FACE
1583 * \param ngMesh - netgen mesh
1584 * \param geom - container of OCCT geometry to mesh
1585 * \param wires - data of nodes on FACE boundary
1586 * \param helper - mesher helper holding the FACE
1587 * \param nodeVec - vector of nodes in which node index == netgen ID
1588 * \retval SMESH_ComputeErrorPtr - error description
1590 //================================================================================
1592 SMESH_ComputeErrorPtr
1593 NETGENPlugin_Mesher::AddSegmentsToMesh(netgen::Mesh& ngMesh,
1594 netgen::OCCGeometry& geom,
1595 const TSideVector& wires,
1596 SMESH_MesherHelper& helper,
1597 vector< const SMDS_MeshNode* > & nodeVec)
1599 // ----------------------------
1600 // Check wires and count nodes
1601 // ----------------------------
1603 for ( int iW = 0; iW < wires.size(); ++iW )
1605 StdMeshers_FaceSidePtr wire = wires[ iW ];
1606 if ( wire->MissVertexNode() )
1608 // Commented for issue 0020960. It worked for the case, let's wait for case where it doesn't.
1609 // It seems that there is no reason for this limitation
1611 // (new SMESH_ComputeError(COMPERR_BAD_INPUT_MESH, "Missing nodes on vertices"));
1613 const vector<UVPtStruct>& uvPtVec = wire->GetUVPtStruct();
1614 if ( uvPtVec.size() != wire->NbPoints() )
1615 return SMESH_ComputeError::New(COMPERR_BAD_INPUT_MESH,
1616 SMESH_Comment("Unexpected nb of points on wire ") << iW
1617 << ": " << uvPtVec.size()<<" != "<<wire->NbPoints());
1618 nbNodes += wire->NbPoints();
1620 nodeVec.reserve( nodeVec.size() + nbNodes + 1 );
1621 if ( nodeVec.empty() )
1622 nodeVec.push_back( 0 );
1624 // -----------------
1626 // -----------------
1628 const bool wasNgMeshEmpty = ( ngMesh.GetNP() < 1 ); /* true => this method is called by
1629 NETGENPlugin_NETGEN_2D_ONLY */
1631 // map for nodes on vertices since they can be shared between wires
1632 // ( issue 0020676, face_int_box.brep) and nodes built by NETGEN
1633 map<const SMDS_MeshNode*, int > node2ngID;
1634 if ( !wasNgMeshEmpty ) // fill node2ngID with nodes built by NETGEN
1636 set< int > subIDs; // ids of sub-shapes of the FACE
1637 for ( int iW = 0; iW < wires.size(); ++iW )
1639 StdMeshers_FaceSidePtr wire = wires[ iW ];
1640 for ( int iE = 0, nbE = wire->NbEdges(); iE < nbE; ++iE )
1642 subIDs.insert( wire->EdgeID( iE ));
1643 subIDs.insert( helper.GetMeshDS()->ShapeToIndex( wire->FirstVertex( iE )));
1646 for ( size_t ngID = 1; ngID < nodeVec.size(); ++ngID )
1647 if ( subIDs.count( nodeVec[ngID]->getshapeId() ))
1648 node2ngID.insert( make_pair( nodeVec[ngID], ngID ));
1651 const int solidID = 0, faceID = geom.fmap.FindIndex( helper.GetSubShape() );
1652 if ( ngMesh.GetNFD() < 1 )
1653 ngMesh.AddFaceDescriptor (netgen::FaceDescriptor(faceID, solidID, solidID, 0));
1655 for ( int iW = 0; iW < wires.size(); ++iW )
1657 StdMeshers_FaceSidePtr wire = wires[ iW ];
1658 const vector<UVPtStruct>& uvPtVec = wire->GetUVPtStruct();
1659 const int nbSegments = wire->NbPoints() - 1;
1661 // assure the 1st node to be in node2ngID, which is needed to correctly
1662 // "close chain of segments" (see below) in case if the 1st node is not
1663 // onVertex because it is on a Viscous layer
1664 node2ngID.insert( make_pair( uvPtVec[ 0 ].node, ngMesh.GetNP() + 1 ));
1666 // compute length of every segment
1667 vector<double> segLen( nbSegments );
1668 for ( int i = 0; i < nbSegments; ++i )
1669 segLen[i] = SMESH_TNodeXYZ( uvPtVec[ i ].node ).Distance( uvPtVec[ i+1 ].node );
1671 int edgeID = 1, posID = -2;
1672 bool isInternalWire = false;
1673 double vertexNormPar = 0;
1674 const int prevNbNGSeg = ngMesh.GetNSeg();
1675 for ( int i = 0; i < nbSegments; ++i ) // loop on segments
1677 // Add the first point of a segment
1679 const SMDS_MeshNode * n = uvPtVec[ i ].node;
1680 const int posShapeID = n->getshapeId();
1681 bool onVertex = ( n->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX );
1682 bool onEdge = ( n->GetPosition()->GetTypeOfPosition() == SMDS_TOP_EDGE );
1684 // skip nodes on degenerated edges
1685 if ( helper.IsDegenShape( posShapeID ) &&
1686 helper.IsDegenShape( uvPtVec[ i+1 ].node->getshapeId() ))
1689 int ngID1 = ngMesh.GetNP() + 1, ngID2 = ngID1+1;
1690 if ( onVertex || ( !wasNgMeshEmpty && onEdge ))
1691 ngID1 = node2ngID.insert( make_pair( n, ngID1 )).first->second;
1692 if ( ngID1 > ngMesh.GetNP() )
1694 netgen::MeshPoint mp( netgen::Point<3> (n->X(), n->Y(), n->Z()) );
1695 ngMesh.AddPoint ( mp, 1, netgen::EDGEPOINT );
1696 nodeVec.push_back( n );
1698 else // n is in ngMesh already, and ngID2 in prev segment is wrong
1700 ngID2 = ngMesh.GetNP() + 1;
1701 if ( i > 0 ) // prev segment belongs to same wire
1703 netgen::Segment& prevSeg = ngMesh.LineSegment( ngMesh.GetNSeg() );
1710 netgen::Segment seg;
1712 seg[0] = ngID1; // ng node id
1713 seg[1] = ngID2; // ng node id
1714 seg.edgenr = ngMesh.GetNSeg() + 1; // ng segment id
1715 seg.si = faceID; // = geom.fmap.FindIndex (face);
1717 for ( int iEnd = 0; iEnd < 2; ++iEnd)
1719 const UVPtStruct& pnt = uvPtVec[ i + iEnd ];
1721 seg.epgeominfo[ iEnd ].dist = pnt.param; // param on curve
1722 seg.epgeominfo[ iEnd ].u = pnt.u;
1723 seg.epgeominfo[ iEnd ].v = pnt.v;
1725 // find out edge id and node parameter on edge
1726 onVertex = ( pnt.normParam + 1e-10 > vertexNormPar );
1727 if ( onVertex || posShapeID != posID )
1730 double normParam = pnt.normParam;
1732 normParam = 0.5 * ( uvPtVec[ i ].normParam + uvPtVec[ i+1 ].normParam );
1733 int edgeIndexInWire = wire->EdgeIndex( normParam );
1734 vertexNormPar = wire->LastParameter( edgeIndexInWire );
1735 const TopoDS_Edge& edge = wire->Edge( edgeIndexInWire );
1736 edgeID = geom.emap.FindIndex( edge );
1738 isInternalWire = ( edge.Orientation() == TopAbs_INTERNAL );
1739 // if ( onVertex ) // param on curve is different on each of two edges
1740 // seg.epgeominfo[ iEnd ].dist = helper.GetNodeU( edge, pnt.node );
1742 seg.epgeominfo[ iEnd ].edgenr = edgeID; // = geom.emap.FindIndex(edge);
1745 ngMesh.AddSegment (seg);
1747 // restrict size of elements near the segment
1748 SMESH_TNodeXYZ np1( n ), np2( uvPtVec[ i+1 ].node );
1749 // get an average size of adjacent segments to avoid sharp change of
1750 // element size (regression on issue 0020452, note 0010898)
1751 int iPrev = SMESH_MesherHelper::WrapIndex( i-1, nbSegments );
1752 int iNext = SMESH_MesherHelper::WrapIndex( i+1, nbSegments );
1753 double sumH = segLen[ iPrev ] + segLen[ i ] + segLen[ iNext ];
1754 int nbSeg = ( int( segLen[ iPrev ] > sumH / 100.) +
1755 int( segLen[ i ] > sumH / 100.) +
1756 int( segLen[ iNext ] > sumH / 100.));
1758 RestrictLocalSize( ngMesh, 0.5*(np1+np2), sumH / nbSeg );
1760 if ( isInternalWire )
1762 swap (seg[0], seg[1]);
1763 swap( seg.epgeominfo[0], seg.epgeominfo[1] );
1764 seg.edgenr = ngMesh.GetNSeg() + 1; // segment id
1765 ngMesh.AddSegment (seg);
1767 } // loop on segments on a wire
1769 // close chain of segments
1770 if ( nbSegments > 0 )
1772 netgen::Segment& lastSeg = ngMesh.LineSegment( ngMesh.GetNSeg() - int( isInternalWire));
1773 const SMDS_MeshNode * lastNode = uvPtVec.back().node;
1774 lastSeg[1] = node2ngID.insert( make_pair( lastNode, lastSeg[1] )).first->second;
1775 if ( lastSeg[1] > ngMesh.GetNP() )
1777 netgen::MeshPoint mp( netgen::Point<3> (lastNode->X(), lastNode->Y(), lastNode->Z()) );
1778 ngMesh.AddPoint ( mp, 1, netgen::EDGEPOINT );
1779 nodeVec.push_back( lastNode );
1781 if ( isInternalWire )
1783 netgen::Segment& realLastSeg = ngMesh.LineSegment( ngMesh.GetNSeg() );
1784 realLastSeg[0] = lastSeg[1];
1788 #ifdef DUMP_SEGMENTS
1789 cout << "BEGIN WIRE " << iW << endl;
1790 for ( int i = prevNbNGSeg+1; i <= ngMesh.GetNSeg(); ++i )
1792 netgen::Segment& seg = ngMesh.LineSegment( i );
1794 netgen::Segment& prevSeg = ngMesh.LineSegment( i-1 );
1795 if ( seg[0] == prevSeg[1] && seg[1] == prevSeg[0] )
1797 cout << "Segment: " << seg.edgenr << endl << "\tis REVRESE of the previous one" << endl;
1801 cout << "Segment: " << seg.edgenr << endl
1802 << "\tp1: " << seg[0] << endl
1803 << "\tp2: " << seg[1] << endl
1804 << "\tp0 param: " << seg.epgeominfo[ 0 ].dist << endl
1805 << "\tp0 uv: " << seg.epgeominfo[ 0 ].u <<", "<< seg.epgeominfo[ 0 ].v << endl
1806 << "\tp0 edge: " << seg.epgeominfo[ 0 ].edgenr << endl
1807 << "\tp1 param: " << seg.epgeominfo[ 1 ].dist << endl
1808 << "\tp1 uv: " << seg.epgeominfo[ 1 ].u <<", "<< seg.epgeominfo[ 1 ].v << endl
1809 << "\tp1 edge: " << seg.epgeominfo[ 1 ].edgenr << endl;
1811 cout << "--END WIRE " << iW << endl;
1814 } // loop on WIREs of a FACE
1816 // add a segment instead of an internal vertex
1817 if ( wasNgMeshEmpty )
1819 NETGENPlugin_Internals intShapes( *helper.GetMesh(), helper.GetSubShape(), /*is3D=*/false );
1820 AddIntVerticesInFaces( geom, ngMesh, nodeVec, intShapes );
1822 ngMesh.CalcSurfacesOfNode();
1827 //================================================================================
1829 * \brief Fill SMESH mesh according to contents of netgen mesh
1830 * \param occgeo - container of OCCT geometry to mesh
1831 * \param ngMesh - netgen mesh
1832 * \param initState - bn of entities in netgen mesh before computing
1833 * \param sMesh - SMESH mesh to fill in
1834 * \param nodeVec - vector of nodes in which node index == netgen ID
1835 * \param comment - returns problem description
1836 * \param quadHelper - holder of medium nodes of sub-meshes
1837 * \retval int - error
1839 //================================================================================
1841 int NETGENPlugin_Mesher::FillSMesh(const netgen::OCCGeometry& occgeo,
1842 netgen::Mesh& ngMesh,
1843 const NETGENPlugin_ngMeshInfo& initState,
1845 std::vector<const SMDS_MeshNode*>& nodeVec,
1846 SMESH_Comment& comment,
1847 SMESH_MesherHelper* quadHelper)
1849 int nbNod = ngMesh.GetNP();
1850 int nbSeg = ngMesh.GetNSeg();
1851 int nbFac = ngMesh.GetNSE();
1852 int nbVol = ngMesh.GetNE();
1854 SMESHDS_Mesh* meshDS = sMesh.GetMeshDS();
1856 // quadHelper is used for either
1857 // 1) making quadratic elements when a lower dimention mesh is loaded
1858 // to SMESH before convertion to quadratic by NETGEN
1859 // 2) sewing of quadratic elements with quadratic elements of sub-meshes
1860 if ( quadHelper && !quadHelper->GetIsQuadratic() && quadHelper->GetTLinkNodeMap().empty() )
1863 // -------------------------------------
1864 // Create and insert nodes into nodeVec
1865 // -------------------------------------
1867 nodeVec.resize( nbNod + 1 );
1868 int i, nbInitNod = initState._nbNodes;
1869 for (i = nbInitNod+1; i <= nbNod; ++i )
1871 const netgen::MeshPoint& ngPoint = ngMesh.Point(i);
1872 SMDS_MeshNode* node = NULL;
1873 TopoDS_Vertex aVert;
1874 // First, netgen creates nodes on vertices in occgeo.vmap,
1875 // so node index corresponds to vertex index
1876 // but (issue 0020776) netgen does not create nodes with equal coordinates
1877 if ( i-nbInitNod <= occgeo.vmap.Extent() )
1879 gp_Pnt p ( NGPOINT_COORDS(ngPoint) );
1880 for (int iV = i-nbInitNod; aVert.IsNull() && iV <= occgeo.vmap.Extent(); ++iV)
1882 aVert = TopoDS::Vertex( occgeo.vmap( iV ) );
1883 gp_Pnt pV = BRep_Tool::Pnt( aVert );
1884 if ( p.SquareDistance( pV ) > 1e-20 )
1887 node = const_cast<SMDS_MeshNode*>( SMESH_Algo::VertexNode( aVert, meshDS ));
1890 if (!node) // node not found on vertex
1892 node = meshDS->AddNode( NGPOINT_COORDS( ngPoint ));
1893 if (!aVert.IsNull())
1894 meshDS->SetNodeOnVertex(node, aVert);
1899 // -------------------------------------------
1900 // Create mesh segments along geometric edges
1901 // -------------------------------------------
1903 int nbInitSeg = initState._nbSegments;
1904 for (i = nbInitSeg+1; i <= nbSeg; ++i )
1906 const netgen::Segment& seg = ngMesh.LineSegment(i);
1908 int pinds[3] = { seg.pnums[0], seg.pnums[1], seg.pnums[2] };
1911 for (int j=0; j < 3; ++j)
1913 int pind = pinds[j];
1914 if (pind <= 0 || !nodeVec_ACCESS(pind))
1922 int aGeomEdgeInd = seg.epgeominfo[j].edgenr;
1923 if (aGeomEdgeInd > 0 && aGeomEdgeInd <= occgeo.emap.Extent())
1924 aEdge = TopoDS::Edge(occgeo.emap(aGeomEdgeInd));
1926 param = seg.epgeominfo[j].dist;
1929 else // middle point
1931 param = param2 * 0.5;
1933 if (!aEdge.IsNull() && nodeVec_ACCESS(pind)->getshapeId() < 1)
1935 meshDS->SetNodeOnEdge(nodeVec_ACCESS(pind), aEdge, param);
1940 SMDS_MeshEdge* edge = 0;
1941 if (nbp == 2) // second order ?
1943 if ( meshDS->FindEdge( nodeVec_ACCESS(pinds[0]), nodeVec_ACCESS(pinds[1])))
1945 if ( quadHelper ) // final mesh must be quadratic
1946 edge = quadHelper->AddEdge(nodeVec_ACCESS(pinds[0]), nodeVec_ACCESS(pinds[1]));
1948 edge = meshDS->AddEdge(nodeVec_ACCESS(pinds[0]), nodeVec_ACCESS(pinds[1]));
1952 if ( meshDS->FindEdge( nodeVec_ACCESS(pinds[0]), nodeVec_ACCESS(pinds[1]),
1953 nodeVec_ACCESS(pinds[2])))
1955 edge = meshDS->AddEdge(nodeVec_ACCESS(pinds[0]), nodeVec_ACCESS(pinds[1]),
1956 nodeVec_ACCESS(pinds[2]));
1960 if ( comment.empty() ) comment << "Cannot create a mesh edge";
1961 MESSAGE("Cannot create a mesh edge");
1962 nbSeg = nbFac = nbVol = 0;
1965 if ( !aEdge.IsNull() && edge->getshapeId() < 1 )
1966 meshDS->SetMeshElementOnShape(edge, aEdge);
1968 else if ( comment.empty() )
1970 comment << "Invalid netgen segment #" << i;
1974 // ----------------------------------------
1975 // Create mesh faces along geometric faces
1976 // ----------------------------------------
1978 int nbInitFac = initState._nbFaces;
1979 int quadFaceID = ngMesh.GetNFD() + 1;
1980 if ( nbInitFac < nbFac )
1981 // add a faces descriptor to exclude qudrangle elements generated by NETGEN
1982 // from computation of 3D mesh
1983 ngMesh.AddFaceDescriptor (netgen::FaceDescriptor(quadFaceID, /*solid1=*/0, /*solid2=*/0, 0));
1985 vector<const SMDS_MeshNode*> nodes;
1986 for (i = nbInitFac+1; i <= nbFac; ++i )
1988 const netgen::Element2d& elem = ngMesh.SurfaceElement(i);
1989 int aGeomFaceInd = elem.GetIndex();
1991 if (aGeomFaceInd > 0 && aGeomFaceInd <= occgeo.fmap.Extent())
1992 aFace = TopoDS::Face(occgeo.fmap(aGeomFaceInd));
1994 for (int j=1; j <= elem.GetNP(); ++j)
1996 int pind = elem.PNum(j);
1997 if ( pind < 1 || pind >= nodeVec.size() )
1999 if ( SMDS_MeshNode* node = nodeVec_ACCESS(pind))
2001 nodes.push_back( node );
2002 if (!aFace.IsNull() && node->getshapeId() < 1)
2004 const netgen::PointGeomInfo& pgi = elem.GeomInfoPi(j);
2005 meshDS->SetNodeOnFace(node, aFace, pgi.u, pgi.v);
2009 if ( nodes.size() != elem.GetNP() )
2011 if ( comment.empty() )
2012 comment << "Invalid netgen 2d element #" << i;
2013 continue; // bad node ids
2015 SMDS_MeshFace* face = NULL;
2016 switch (elem.GetType())
2019 if ( quadHelper ) // final mesh must be quadratic
2020 face = quadHelper->AddFace(nodes[0],nodes[1],nodes[2]);
2022 face = meshDS->AddFace(nodes[0],nodes[1],nodes[2]);
2025 if ( quadHelper ) // final mesh must be quadratic
2026 face = quadHelper->AddFace(nodes[0],nodes[1],nodes[2],nodes[3]);
2028 face = meshDS->AddFace(nodes[0],nodes[1],nodes[2],nodes[3]);
2029 // exclude qudrangle elements from computation of 3D mesh
2030 const_cast< netgen::Element2d& >( elem ).SetIndex( quadFaceID );
2033 nodes[5] = mediumNode( nodes[0],nodes[1],nodes[5], quadHelper );
2034 nodes[3] = mediumNode( nodes[1],nodes[2],nodes[3], quadHelper );
2035 nodes[4] = mediumNode( nodes[2],nodes[0],nodes[4], quadHelper );
2036 face = meshDS->AddFace(nodes[0],nodes[1],nodes[2],nodes[5],nodes[3],nodes[4]);
2039 nodes[4] = mediumNode( nodes[0],nodes[1],nodes[4], quadHelper );
2040 nodes[7] = mediumNode( nodes[1],nodes[2],nodes[7], quadHelper );
2041 nodes[5] = mediumNode( nodes[2],nodes[3],nodes[5], quadHelper );
2042 nodes[6] = mediumNode( nodes[3],nodes[0],nodes[6], quadHelper );
2043 face = meshDS->AddFace(nodes[0],nodes[1],nodes[2],nodes[3],
2044 nodes[4],nodes[7],nodes[5],nodes[6]);
2045 // exclude qudrangle elements from computation of 3D mesh
2046 const_cast< netgen::Element2d& >( elem ).SetIndex( quadFaceID );
2049 MESSAGE("NETGEN created a face of unexpected type, ignoring");
2054 if ( comment.empty() ) comment << "Cannot create a mesh face";
2055 MESSAGE("Cannot create a mesh face");
2056 nbSeg = nbFac = nbVol = 0;
2059 if (!aFace.IsNull())
2060 meshDS->SetMeshElementOnShape(face, aFace);
2063 // ------------------
2064 // Create tetrahedra
2065 // ------------------
2067 for (i = 1; i <= nbVol; ++i)
2069 const netgen::Element& elem = ngMesh.VolumeElement(i);
2070 int aSolidInd = elem.GetIndex();
2071 TopoDS_Solid aSolid;
2072 if (aSolidInd > 0 && aSolidInd <= occgeo.somap.Extent())
2073 aSolid = TopoDS::Solid(occgeo.somap(aSolidInd));
2075 for (int j=1; j <= elem.GetNP(); ++j)
2077 int pind = elem.PNum(j);
2078 if ( pind < 1 || pind >= nodeVec.size() )
2080 if ( SMDS_MeshNode* node = nodeVec_ACCESS(pind) )
2082 nodes.push_back(node);
2083 if ( !aSolid.IsNull() && node->getshapeId() < 1 )
2084 meshDS->SetNodeInVolume(node, aSolid);
2087 if ( nodes.size() != elem.GetNP() )
2089 if ( comment.empty() )
2090 comment << "Invalid netgen 3d element #" << i;
2093 SMDS_MeshVolume* vol = NULL;
2094 switch (elem.GetType())
2097 vol = meshDS->AddVolume(nodes[0],nodes[1],nodes[2],nodes[3]);
2100 nodes[4] = mediumNode( nodes[0],nodes[1],nodes[4], quadHelper );
2101 nodes[7] = mediumNode( nodes[1],nodes[2],nodes[7], quadHelper );
2102 nodes[5] = mediumNode( nodes[2],nodes[0],nodes[5], quadHelper );
2103 nodes[6] = mediumNode( nodes[0],nodes[3],nodes[6], quadHelper );
2104 nodes[8] = mediumNode( nodes[1],nodes[3],nodes[8], quadHelper );
2105 nodes[9] = mediumNode( nodes[2],nodes[3],nodes[9], quadHelper );
2106 vol = meshDS->AddVolume(nodes[0],nodes[1],nodes[2],nodes[3],
2107 nodes[4],nodes[7],nodes[5],nodes[6],nodes[8],nodes[9]);
2110 MESSAGE("NETGEN created a volume of unexpected type, ignoring");
2115 if ( comment.empty() ) comment << "Cannot create a mesh volume";
2116 MESSAGE("Cannot create a mesh volume");
2117 nbSeg = nbFac = nbVol = 0;
2120 if (!aSolid.IsNull())
2121 meshDS->SetMeshElementOnShape(vol, aSolid);
2123 return comment.empty() ? 0 : 1;
2128 //================================================================================
2130 * \brief Restrict size of elements on the given edge
2132 //================================================================================
2134 void setLocalSize(const TopoDS_Edge& edge,
2138 const int nb = 1000;
2139 Standard_Real u1, u2;
2140 Handle(Geom_Curve) curve = BRep_Tool::Curve(edge, u1, u2);
2141 if ( curve.IsNull() )
2143 TopoDS_Iterator vIt( edge );
2144 if ( !vIt.More() ) return;
2145 gp_Pnt p = BRep_Tool::Pnt( TopoDS::Vertex( vIt.Value() ));
2146 NETGENPlugin_Mesher::RestrictLocalSize( mesh, p.XYZ(), size );
2150 Standard_Real delta = (u2-u1)/nb;
2151 for(int i=0; i<nb; i++)
2153 Standard_Real u = u1 + delta*i;
2154 gp_Pnt p = curve->Value(u);
2155 NETGENPlugin_Mesher::RestrictLocalSize( mesh, p.XYZ(), size );
2156 netgen::Point3d pi(p.X(), p.Y(), p.Z());
2157 double resultSize = mesh.GetH(pi);
2158 if ( resultSize - size > 0.1*size )
2159 // netgen does restriction iff oldH/newH > 1.2 (localh.cpp:136)
2160 NETGENPlugin_Mesher::RestrictLocalSize( mesh, p.XYZ(), resultSize/1.201 );
2165 //================================================================================
2167 * \brief Convert error into text
2169 //================================================================================
2171 std::string text(int err)
2176 SMESH_Comment("Error in netgen::OCCGenerateMesh() at ") << netgen::multithread.task;
2179 //================================================================================
2181 * \brief Convert exception into text
2183 //================================================================================
2185 std::string text(Standard_Failure& ex)
2187 SMESH_Comment str("Exception in netgen::OCCGenerateMesh()");
2188 str << " at " << netgen::multithread.task
2189 << ": " << ex.DynamicType()->Name();
2190 if ( ex.GetMessageString() && strlen( ex.GetMessageString() ))
2191 str << ": " << ex.GetMessageString();
2194 //================================================================================
2196 * \brief Convert exception into text
2198 //================================================================================
2200 std::string text(netgen::NgException& ex)
2202 SMESH_Comment str("NgException");
2203 if ( strlen( netgen::multithread.task ) > 0 )
2204 str << " at " << netgen::multithread.task;
2205 str << ": " << ex.What();
2209 //================================================================================
2211 * \brief Looks for triangles lying on a SOLID
2213 //================================================================================
2215 bool hasBadElemOnSolid( const list<const SMDS_MeshElement*>& elems,
2216 SMESH_subMesh* solidSM )
2218 TopTools_IndexedMapOfShape solidSubs;
2219 TopExp::MapShapes( solidSM->GetSubShape(), solidSubs );
2220 SMESHDS_Mesh* mesh = solidSM->GetFather()->GetMeshDS();
2222 list<const SMDS_MeshElement*>::const_iterator e = elems.begin();
2223 for ( ; e != elems.end(); ++e )
2225 const SMDS_MeshElement* elem = *e;
2226 if ( elem->GetType() != SMDSAbs_Face )
2228 int nbNodesOnSolid = 0;
2229 SMDS_NodeIteratorPtr nIt = elem->nodeIterator();
2230 while ( nIt->more() )
2232 const SMDS_MeshNode* n = nIt->next();
2233 const TopoDS_Shape& s = mesh->IndexToShape( n->getshapeId() );
2234 nbNodesOnSolid += ( !s.IsNull() && solidSubs.Contains( s ));
2235 if ( nbNodesOnSolid > 2 )
2242 const double edgeMeshingTime = 0.001;
2243 const double faceMeshingTime = 0.019;
2244 const double edgeFaceMeshingTime = edgeMeshingTime + faceMeshingTime;
2245 const double faceOptimizTime = 0.06;
2246 const double voluMeshingTime = 0.15;
2247 const double volOptimizeTime = 0.77;
2250 //=============================================================================
2252 * Here we are going to use the NETGEN mesher
2254 //=============================================================================
2256 bool NETGENPlugin_Mesher::Compute()
2258 NETGENPlugin_NetgenLibWrapper ngLib;
2260 netgen::MeshingParameters& mparams = netgen::mparam;
2261 MESSAGE("Compute with:\n"
2262 " max size = " << mparams.maxh << "\n"
2263 " segments per edge = " << mparams.segmentsperedge);
2265 " growth rate = " << mparams.grading << "\n"
2266 " elements per radius = " << mparams.curvaturesafety << "\n"
2267 " second order = " << mparams.secondorder << "\n"
2268 " quad allowed = " << mparams.quad << "\n"
2269 " surface curvature = " << mparams.uselocalh << "\n"
2270 " fuse edges = " << netgen::merge_solids);
2272 SMESH_ComputeErrorPtr error = SMESH_ComputeError::New();
2273 SMESH_MesherHelper quadHelper( *_mesh );
2274 quadHelper.SetIsQuadratic( mparams.secondorder );
2276 static string debugFile = "/tmp/ngMesh.py"; /* to call toPython( ngMesh, debugFile )
2277 while debugging netgen */
2278 // -------------------------
2279 // Prepare OCC geometry
2280 // -------------------------
2282 netgen::OCCGeometry occgeo;
2283 list< SMESH_subMesh* > meshedSM[3]; // for 0-2 dimensions
2284 NETGENPlugin_Internals internals( *_mesh, _shape, _isVolume );
2285 PrepareOCCgeometry( occgeo, _shape, *_mesh, meshedSM, &internals );
2288 _totalTime = edgeFaceMeshingTime;
2290 _totalTime += faceOptimizTime;
2292 _totalTime += voluMeshingTime + ( _optimize ? volOptimizeTime : 0 );
2293 double doneTime = 0;
2296 _curShapeIndex = -1;
2298 // -------------------------
2299 // Generate the mesh
2300 // -------------------------
2303 NETGENPlugin_ngMeshInfo initState; // it remembers size of ng mesh equal to size of Smesh
2305 SMESH_Comment comment;
2308 // vector of nodes in which node index == netgen ID
2309 vector< const SMDS_MeshNode* > nodeVec;
2317 // not to RestrictLocalH() according to curvature during MESHCONST_ANALYSE
2318 mparams.uselocalh = false;
2319 mparams.grading = 0.8; // not limitited size growth
2321 if ( _simpleHyp->GetNumberOfSegments() )
2323 mparams.maxh = occgeo.boundingbox.Diam();
2326 mparams.maxh = _simpleHyp->GetLocalLength();
2329 if ( mparams.maxh == 0.0 )
2330 mparams.maxh = occgeo.boundingbox.Diam();
2331 if ( _simpleHyp || ( mparams.minh == 0.0 && _fineness != NETGENPlugin_Hypothesis::UserDefined))
2332 mparams.minh = GetDefaultMinSize( _shape, mparams.maxh );
2334 // Local size on faces
2335 occgeo.face_maxh = mparams.maxh;
2337 // Let netgen create _ngMesh and calculate element size on not meshed shapes
2341 int startWith = netgen::MESHCONST_ANALYSE;
2342 int endWith = netgen::MESHCONST_ANALYSE;
2347 err = netgen::OCCGenerateMesh(occgeo, _ngMesh, mparams, startWith, endWith);
2349 err = netgen::OCCGenerateMesh(occgeo, _ngMesh, startWith, endWith, optstr);
2351 if(netgen::multithread.terminate)
2354 comment << text(err);
2356 catch (Standard_Failure& ex)
2358 comment << text(ex);
2360 err = 0; //- MESHCONST_ANALYSE isn't so important step
2363 ngLib.setMesh(( Ng_Mesh*) _ngMesh );
2365 _ngMesh->ClearFaceDescriptors(); // we make descriptors our-self
2369 // Pass 1D simple parameters to NETGEN
2370 // --------------------------------
2371 int nbSeg = _simpleHyp->GetNumberOfSegments();
2372 double segSize = _simpleHyp->GetLocalLength();
2373 for ( int iE = 1; iE <= occgeo.emap.Extent(); ++iE )
2375 const TopoDS_Edge& e = TopoDS::Edge( occgeo.emap(iE));
2377 segSize = SMESH_Algo::EdgeLength( e ) / ( nbSeg - 0.4 );
2378 setLocalSize( e, segSize, *_ngMesh );
2381 else // if ( ! _simpleHyp )
2383 // Local size on vertices and edges
2384 // --------------------------------
2385 for(std::map<int,double>::const_iterator it=EdgeId2LocalSize.begin(); it!=EdgeId2LocalSize.end(); it++)
2387 int key = (*it).first;
2388 double hi = (*it).second;
2389 const TopoDS_Shape& shape = ShapesWithLocalSize.FindKey(key);
2390 const TopoDS_Edge& e = TopoDS::Edge(shape);
2391 setLocalSize( e, hi, *_ngMesh );
2393 for(std::map<int,double>::const_iterator it=VertexId2LocalSize.begin(); it!=VertexId2LocalSize.end(); it++)
2395 int key = (*it).first;
2396 double hi = (*it).second;
2397 const TopoDS_Shape& shape = ShapesWithLocalSize.FindKey(key);
2398 const TopoDS_Vertex& v = TopoDS::Vertex(shape);
2399 gp_Pnt p = BRep_Tool::Pnt(v);
2400 NETGENPlugin_Mesher::RestrictLocalSize( *_ngMesh, p.XYZ(), hi );
2402 for(map<int,double>::const_iterator it=FaceId2LocalSize.begin();
2403 it!=FaceId2LocalSize.end(); it++)
2405 int key = (*it).first;
2406 double val = (*it).second;
2407 const TopoDS_Shape& shape = ShapesWithLocalSize.FindKey(key);
2408 int faceNgID = occgeo.fmap.FindIndex(shape);
2409 occgeo.SetFaceMaxH(faceNgID, val);
2410 for ( TopExp_Explorer edgeExp( shape, TopAbs_EDGE ); edgeExp.More(); edgeExp.Next() )
2411 setLocalSize( TopoDS::Edge( edgeExp.Current() ), val, *_ngMesh );
2415 // Precompute internal edges (issue 0020676) in order to
2416 // add mesh on them correctly (twice) to netgen mesh
2417 if ( !err && internals.hasInternalEdges() )
2419 // load internal shapes into OCCGeometry
2420 netgen::OCCGeometry intOccgeo;
2421 internals.getInternalEdges( intOccgeo.fmap, intOccgeo.emap, intOccgeo.vmap, meshedSM );
2422 intOccgeo.boundingbox = occgeo.boundingbox;
2423 intOccgeo.shape = occgeo.shape;
2424 intOccgeo.face_maxh.SetSize(intOccgeo.fmap.Extent());
2425 intOccgeo.face_maxh = netgen::mparam.maxh;
2426 netgen::Mesh *tmpNgMesh = NULL;
2430 // compute local H on internal shapes in the main mesh
2431 //OCCSetLocalMeshSize(intOccgeo, *_ngMesh); it deletes _ngMesh->localH
2433 // let netgen create a temporary mesh
2435 netgen::OCCGenerateMesh(intOccgeo, tmpNgMesh, mparams, startWith, endWith);
2437 netgen::OCCGenerateMesh(intOccgeo, tmpNgMesh, startWith, endWith, optstr);
2439 if(netgen::multithread.terminate)
2442 // copy LocalH from the main to temporary mesh
2443 initState.transferLocalH( _ngMesh, tmpNgMesh );
2445 // compute mesh on internal edges
2446 startWith = endWith = netgen::MESHCONST_MESHEDGES;
2448 err = netgen::OCCGenerateMesh(intOccgeo, tmpNgMesh, mparams, startWith, endWith);
2450 err = netgen::OCCGenerateMesh(intOccgeo, tmpNgMesh, startWith, endWith, optstr);
2452 comment << text(err);
2454 catch (Standard_Failure& ex)
2456 comment << text(ex);
2459 initState.restoreLocalH( tmpNgMesh );
2461 // fill SMESH by netgen mesh
2462 vector< const SMDS_MeshNode* > tmpNodeVec;
2463 FillSMesh( intOccgeo, *tmpNgMesh, initState, *_mesh, tmpNodeVec, comment );
2464 err = ( err || !comment.empty() );
2466 nglib::Ng_DeleteMesh((nglib::Ng_Mesh*)tmpNgMesh);
2469 // Fill _ngMesh with nodes and segments of computed submeshes
2472 err = ! ( FillNgMesh(occgeo, *_ngMesh, nodeVec, meshedSM[ MeshDim_0D ]) &&
2473 FillNgMesh(occgeo, *_ngMesh, nodeVec, meshedSM[ MeshDim_1D ], &quadHelper));
2475 initState = NETGENPlugin_ngMeshInfo(_ngMesh);
2480 startWith = endWith = netgen::MESHCONST_MESHEDGES;
2485 err = netgen::OCCGenerateMesh(occgeo, _ngMesh, mparams, startWith, endWith);
2487 err = netgen::OCCGenerateMesh(occgeo, _ngMesh, startWith, endWith, optstr);
2489 if(netgen::multithread.terminate)
2492 comment << text(err);
2494 catch (Standard_Failure& ex)
2496 comment << text(ex);
2501 _ticTime = ( doneTime += edgeMeshingTime ) / _totalTime / _progressTic;
2503 mparams.uselocalh = true; // restore as it is used at surface optimization
2505 // ---------------------
2506 // compute surface mesh
2507 // ---------------------
2510 // Pass 2D simple parameters to NETGEN
2512 if ( double area = _simpleHyp->GetMaxElementArea() ) {
2514 mparams.maxh = sqrt(2. * area/sqrt(3.0));
2515 mparams.grading = 0.4; // moderate size growth
2518 // length from edges
2519 if ( _ngMesh->GetNSeg() ) {
2520 double edgeLength = 0;
2521 TopTools_MapOfShape visitedEdges;
2522 for ( TopExp_Explorer exp( _shape, TopAbs_EDGE ); exp.More(); exp.Next() )
2523 if( visitedEdges.Add(exp.Current()) )
2524 edgeLength += SMESH_Algo::EdgeLength( TopoDS::Edge( exp.Current() ));
2525 // we have to multiply length by 2 since for each TopoDS_Edge there
2526 // are double set of NETGEN edges, in other words, we have to
2527 // divide _ngMesh->GetNSeg() by 2.
2528 mparams.maxh = 2*edgeLength / _ngMesh->GetNSeg();
2531 mparams.maxh = 1000;
2533 mparams.grading = 0.2; // slow size growth
2535 mparams.quad = _simpleHyp->GetAllowQuadrangles();
2536 mparams.maxh = min( mparams.maxh, occgeo.boundingbox.Diam()/2 );
2537 _ngMesh->SetGlobalH (mparams.maxh);
2538 netgen::Box<3> bb = occgeo.GetBoundingBox();
2539 bb.Increase (bb.Diam()/20);
2540 _ngMesh->SetLocalH (bb.PMin(), bb.PMax(), mparams.grading);
2543 // Care of vertices internal in faces (issue 0020676)
2544 if ( internals.hasInternalVertexInFace() )
2546 // store computed segments in SMESH in order not to create SMESH
2547 // edges for ng segments added by AddIntVerticesInFaces()
2548 FillSMesh( occgeo, *_ngMesh, initState, *_mesh, nodeVec, comment );
2549 // add segments to faces with internal vertices
2550 AddIntVerticesInFaces( occgeo, *_ngMesh, nodeVec, internals );
2551 initState = NETGENPlugin_ngMeshInfo(_ngMesh);
2554 // Build viscous layers
2555 if ( _isViscousLayers2D )
2557 if ( !internals.hasInternalVertexInFace() ) {
2558 FillSMesh( occgeo, *_ngMesh, initState, *_mesh, nodeVec, comment );
2559 initState = NETGENPlugin_ngMeshInfo(_ngMesh);
2561 SMESH_ProxyMesh::Ptr viscousMesh;
2562 SMESH_MesherHelper helper( *_mesh );
2563 for ( int faceID = 1; faceID <= occgeo.fmap.Extent(); ++faceID )
2565 const TopoDS_Face& F = TopoDS::Face( occgeo.fmap( faceID ));
2566 viscousMesh = StdMeshers_ViscousLayers2D::Compute( *_mesh, F );
2569 // exclude from computation ng segments built on EDGEs of F
2570 for (int i = 1; i <= _ngMesh->GetNSeg(); i++)
2572 netgen::Segment & seg = _ngMesh->LineSegment(i);
2573 if (seg.si == faceID)
2576 // add new segments to _ngMesh instead of excluded ones
2577 helper.SetSubShape( F );
2579 StdMeshers_FaceSide::GetFaceWires( F, *_mesh, /*skipMediumNodes=*/true,
2580 error, viscousMesh );
2581 error = AddSegmentsToMesh( *_ngMesh, occgeo, wires, helper, nodeVec );
2583 if ( !error ) error = SMESH_ComputeError::New();
2585 initState = NETGENPlugin_ngMeshInfo(_ngMesh);
2588 // Let netgen compute 2D mesh
2589 startWith = netgen::MESHCONST_MESHSURFACE;
2590 endWith = _optimize ? netgen::MESHCONST_OPTSURFACE : netgen::MESHCONST_MESHSURFACE;
2595 err = netgen::OCCGenerateMesh(occgeo, _ngMesh, mparams, startWith, endWith);
2597 err = netgen::OCCGenerateMesh(occgeo, _ngMesh, startWith, endWith, optstr);
2599 if(netgen::multithread.terminate)
2602 comment << text (err);
2604 catch (Standard_Failure& ex)
2606 comment << text(ex);
2607 //err = 1; -- try to make volumes anyway
2609 catch (netgen::NgException exc)
2611 comment << text(exc);
2612 //err = 1; -- try to make volumes anyway
2617 doneTime += faceMeshingTime + ( _optimize ? faceOptimizTime : 0 );
2618 _ticTime = doneTime / _totalTime / _progressTic;
2620 // ---------------------
2621 // generate volume mesh
2622 // ---------------------
2623 // Fill _ngMesh with nodes and faces of computed 2D submeshes
2624 if ( !err && _isVolume && ( !meshedSM[ MeshDim_2D ].empty() || mparams.quad ))
2626 // load SMESH with computed segments and faces
2627 FillSMesh( occgeo, *_ngMesh, initState, *_mesh, nodeVec, comment, &quadHelper );
2629 // compute pyramids on quadrangles
2630 SMESH_ProxyMesh::Ptr proxyMesh;
2631 if ( _mesh->NbQuadrangles() > 0 )
2632 for ( int iS = 1; iS <= occgeo.somap.Extent(); ++iS )
2634 StdMeshers_QuadToTriaAdaptor* Adaptor = new StdMeshers_QuadToTriaAdaptor;
2635 proxyMesh.reset( Adaptor );
2637 int nbPyrams = _mesh->NbPyramids();
2638 Adaptor->Compute( *_mesh, occgeo.somap(iS) );
2639 if ( nbPyrams != _mesh->NbPyramids() )
2641 list< SMESH_subMesh* > quadFaceSM;
2642 for (TopExp_Explorer face(occgeo.somap(iS), TopAbs_FACE); face.More(); face.Next())
2643 if ( Adaptor->GetProxySubMesh( face.Current() ))
2645 quadFaceSM.push_back( _mesh->GetSubMesh( face.Current() ));
2646 meshedSM[ MeshDim_2D ].remove( quadFaceSM.back() );
2648 FillNgMesh(occgeo, *_ngMesh, nodeVec, quadFaceSM, &quadHelper, proxyMesh);
2651 // fill _ngMesh with faces of sub-meshes
2652 err = ! ( FillNgMesh(occgeo, *_ngMesh, nodeVec, meshedSM[ MeshDim_2D ], &quadHelper));
2653 initState = NETGENPlugin_ngMeshInfo(_ngMesh);
2654 //toPython( _ngMesh, "/tmp/ngPython.py");
2656 if (!err && _isVolume)
2658 // Pass 3D simple parameters to NETGEN
2659 const NETGENPlugin_SimpleHypothesis_3D* simple3d =
2660 dynamic_cast< const NETGENPlugin_SimpleHypothesis_3D* > ( _simpleHyp );
2662 if ( double vol = simple3d->GetMaxElementVolume() ) {
2664 mparams.maxh = pow( 72, 1/6. ) * pow( vol, 1/3. );
2665 mparams.maxh = min( mparams.maxh, occgeo.boundingbox.Diam()/2 );
2668 // length from faces
2669 mparams.maxh = _ngMesh->AverageH();
2671 _ngMesh->SetGlobalH (mparams.maxh);
2672 mparams.grading = 0.4;
2674 _ngMesh->CalcLocalH(mparams.grading);
2676 _ngMesh->CalcLocalH();
2679 // Care of vertices internal in solids and internal faces (issue 0020676)
2680 if ( internals.hasInternalVertexInSolid() || internals.hasInternalFaces() )
2682 // store computed faces in SMESH in order not to create SMESH
2683 // faces for ng faces added here
2684 FillSMesh( occgeo, *_ngMesh, initState, *_mesh, nodeVec, comment, &quadHelper );
2685 // add ng faces to solids with internal vertices
2686 AddIntVerticesInSolids( occgeo, *_ngMesh, nodeVec, internals );
2687 // duplicate mesh faces on internal faces
2688 FixIntFaces( occgeo, *_ngMesh, internals );
2689 initState = NETGENPlugin_ngMeshInfo(_ngMesh);
2691 // Let netgen compute 3D mesh
2692 startWith = endWith = netgen::MESHCONST_MESHVOLUME;
2697 err = netgen::OCCGenerateMesh(occgeo, _ngMesh, mparams, startWith, endWith);
2699 err = netgen::OCCGenerateMesh(occgeo, _ngMesh, startWith, endWith, optstr);
2701 if(netgen::multithread.terminate)
2704 if ( comment.empty() ) // do not overwrite a previos error
2705 comment << text(err);
2707 catch (Standard_Failure& ex)
2709 if ( comment.empty() ) // do not overwrite a previos error
2710 comment << text(ex);
2713 catch (netgen::NgException exc)
2715 if ( comment.empty() ) // do not overwrite a previos error
2716 comment << text(exc);
2719 _ticTime = ( doneTime += voluMeshingTime ) / _totalTime / _progressTic;
2721 // Let netgen optimize 3D mesh
2722 if ( !err && _optimize )
2724 startWith = endWith = netgen::MESHCONST_OPTVOLUME;
2729 err = netgen::OCCGenerateMesh(occgeo, _ngMesh, mparams, startWith, endWith);
2731 err = netgen::OCCGenerateMesh(occgeo, _ngMesh, startWith, endWith, optstr);
2733 if(netgen::multithread.terminate)
2736 if ( comment.empty() ) // do not overwrite a previos error
2737 comment << text(err);
2739 catch (Standard_Failure& ex)
2741 if ( comment.empty() ) // do not overwrite a previos error
2742 comment << text(ex);
2744 catch (netgen::NgException exc)
2746 if ( comment.empty() ) // do not overwrite a previos error
2747 comment << text(exc);
2751 if (!err && mparams.secondorder > 0)
2756 if ( !meshedSM[ MeshDim_1D ].empty() )
2758 // remove segments not attached to geometry (IPAL0052479)
2759 for (int i = 1; i <= _ngMesh->GetNSeg(); ++i)
2761 const netgen::Segment & seg = _ngMesh->LineSegment (i);
2762 if ( seg.epgeominfo[ 0 ].edgenr == 0 )
2763 _ngMesh->DeleteSegment( i );
2765 _ngMesh->Compress();
2767 // convert to quadratic
2768 netgen::OCCRefinementSurfaces ref (occgeo);
2769 ref.MakeSecondOrder (*_ngMesh);
2771 // care of elements already loaded to SMESH
2772 // if ( initState._nbSegments > 0 )
2773 // makeQuadratic( occgeo.emap, _mesh );
2774 // if ( initState._nbFaces > 0 )
2775 // makeQuadratic( occgeo.fmap, _mesh );
2777 catch (Standard_Failure& ex)
2779 if ( comment.empty() ) // do not overwrite a previos error
2780 comment << "Exception in netgen at passing to 2nd order ";
2782 catch (netgen::NgException exc)
2784 if ( comment.empty() ) // do not overwrite a previos error
2785 comment << exc.What();
2790 _ticTime = 0.98 / _progressTic;
2792 int nbNod = _ngMesh->GetNP();
2793 int nbSeg = _ngMesh->GetNSeg();
2794 int nbFac = _ngMesh->GetNSE();
2795 int nbVol = _ngMesh->GetNE();
2796 bool isOK = ( !err && (_isVolume ? (nbVol > 0) : (nbFac > 0)) );
2798 MESSAGE((err ? "Mesh Generation failure" : "End of Mesh Generation") <<
2799 ", nb nodes: " << nbNod <<
2800 ", nb segments: " << nbSeg <<
2801 ", nb faces: " << nbFac <<
2802 ", nb volumes: " << nbVol);
2804 // Feed back the SMESHDS with the generated Nodes and Elements
2805 if ( true /*isOK*/ ) // get whatever built
2807 FillSMesh( occgeo, *_ngMesh, initState, *_mesh, nodeVec, comment, &quadHelper );
2809 if ( quadHelper.GetIsQuadratic() ) // remove free nodes
2810 for ( size_t i = 0; i < nodeVec.size(); ++i )
2811 if ( nodeVec[i] && nodeVec[i]->NbInverseElements() == 0 )
2812 _mesh->GetMeshDS()->RemoveFreeNode( nodeVec[i], 0, /*fromGroups=*/false );
2814 SMESH_ComputeErrorPtr readErr = ReadErrors(nodeVec);
2815 if ( readErr && !readErr->myBadElements.empty() )
2818 if ( error->IsOK() && ( !isOK || comment.size() > 0 ))
2819 error->myName = COMPERR_ALGO_FAILED;
2820 if ( !comment.empty() )
2821 error->myComment = comment;
2823 // SetIsAlwaysComputed( true ) to empty sub-meshes, which
2824 // appear if the geometry contains coincident sub-shape due
2825 // to bool merge_solids = 1; in netgen/libsrc/occ/occgenmesh.cpp
2826 const int nbMaps = 2;
2827 const TopTools_IndexedMapOfShape* geoMaps[nbMaps] =
2828 { & occgeo.vmap, & occgeo.emap/*, & occgeo.fmap*/ };
2829 for ( int iMap = 0; iMap < nbMaps; ++iMap )
2830 for (int i = 1; i <= geoMaps[iMap]->Extent(); i++)
2831 if ( SMESH_subMesh* sm = _mesh->GetSubMeshContaining( geoMaps[iMap]->FindKey(i)))
2832 if ( !sm->IsMeshComputed() )
2833 sm->SetIsAlwaysComputed( true );
2835 // set bad compute error to subshapes of all failed sub-shapes
2836 if ( !error->IsOK() )
2838 bool pb2D = false, pb3D = false;
2839 for (int i = 1; i <= occgeo.fmap.Extent(); i++) {
2840 int status = occgeo.facemeshstatus[i-1];
2841 if (status == 1 ) continue;
2842 if ( SMESH_subMesh* sm = _mesh->GetSubMeshContaining( occgeo.fmap( i ))) {
2843 SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
2844 if ( !smError || smError->IsOK() ) {
2846 smError.reset( new SMESH_ComputeError( *error ));
2848 smError.reset( new SMESH_ComputeError( COMPERR_ALGO_FAILED, "Ignored" ));
2849 if ( SMESH_Algo::GetMeshError( sm ) == SMESH_Algo::MEr_OK )
2850 smError->myName = COMPERR_WARNING;
2852 pb2D = pb2D || smError->IsKO();
2855 if ( !pb2D ) // all faces are OK
2856 for (int i = 1; i <= occgeo.somap.Extent(); i++)
2857 if ( SMESH_subMesh* sm = _mesh->GetSubMeshContaining( occgeo.somap( i )))
2859 bool smComputed = nbVol && !sm->IsEmpty();
2860 if ( smComputed && internals.hasInternalVertexInSolid( sm->GetId() ))
2862 int nbIntV = internals.getSolidsWithVertices().find( sm->GetId() )->second.size();
2863 SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
2864 smComputed = ( smDS->NbElements() > 0 || smDS->NbNodes() > nbIntV );
2866 SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
2867 if ( !smComputed && ( !smError || smError->IsOK() ))
2869 smError.reset( new SMESH_ComputeError( *error ));
2870 if ( nbVol && SMESH_Algo::GetMeshError( sm ) == SMESH_Algo::MEr_OK )
2872 smError->myName = COMPERR_WARNING;
2874 else if ( !smError->myBadElements.empty() ) // bad surface mesh
2876 if ( !hasBadElemOnSolid( smError->myBadElements, sm ))
2880 pb3D = pb3D || ( smError && smError->IsKO() );
2882 if ( !pb2D && !pb3D )
2883 err = 0; // no fatal errors, only warnings
2886 ngLib._isComputeOk = !err;
2891 //=============================================================================
2895 //=============================================================================
2896 bool NETGENPlugin_Mesher::Evaluate(MapShapeNbElems& aResMap)
2898 netgen::MeshingParameters& mparams = netgen::mparam;
2901 // -------------------------
2902 // Prepare OCC geometry
2903 // -------------------------
2904 netgen::OCCGeometry occgeo;
2905 list< SMESH_subMesh* > meshedSM[4]; // for 0-3 dimensions
2906 NETGENPlugin_Internals internals( *_mesh, _shape, _isVolume );
2907 PrepareOCCgeometry( occgeo, _shape, *_mesh, meshedSM, &internals );
2909 bool tooManyElems = false;
2910 const int hugeNb = std::numeric_limits<int>::max() / 100;
2915 // pass 1D simple parameters to NETGEN
2918 // not to RestrictLocalH() according to curvature during MESHCONST_ANALYSE
2919 mparams.uselocalh = false;
2920 mparams.grading = 0.8; // not limitited size growth
2922 if ( _simpleHyp->GetNumberOfSegments() )
2924 mparams.maxh = occgeo.boundingbox.Diam();
2927 mparams.maxh = _simpleHyp->GetLocalLength();
2930 if ( mparams.maxh == 0.0 )
2931 mparams.maxh = occgeo.boundingbox.Diam();
2932 if ( _simpleHyp || ( mparams.minh == 0.0 && _fineness != NETGENPlugin_Hypothesis::UserDefined))
2933 mparams.minh = GetDefaultMinSize( _shape, mparams.maxh );
2935 // let netgen create _ngMesh and calculate element size on not meshed shapes
2936 NETGENPlugin_NetgenLibWrapper ngLib;
2937 netgen::Mesh *ngMesh = NULL;
2941 int startWith = netgen::MESHCONST_ANALYSE;
2942 int endWith = netgen::MESHCONST_MESHEDGES;
2944 int err = netgen::OCCGenerateMesh(occgeo, ngMesh, mparams, startWith, endWith);
2946 int err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
2949 if(netgen::multithread.terminate)
2952 ngLib.setMesh(( Ng_Mesh*) ngMesh );
2954 if ( SMESH_subMesh* sm = _mesh->GetSubMeshContaining( _shape ))
2955 sm->GetComputeError().reset( new SMESH_ComputeError( COMPERR_ALGO_FAILED ));
2960 // Pass 1D simple parameters to NETGEN
2961 // --------------------------------
2962 int nbSeg = _simpleHyp->GetNumberOfSegments();
2963 double segSize = _simpleHyp->GetLocalLength();
2964 for ( int iE = 1; iE <= occgeo.emap.Extent(); ++iE )
2966 const TopoDS_Edge& e = TopoDS::Edge( occgeo.emap(iE));
2968 segSize = SMESH_Algo::EdgeLength( e ) / ( nbSeg - 0.4 );
2969 setLocalSize( e, segSize, *ngMesh );
2972 else // if ( ! _simpleHyp )
2974 // Local size on vertices and edges
2975 // --------------------------------
2976 for(std::map<int,double>::const_iterator it=EdgeId2LocalSize.begin(); it!=EdgeId2LocalSize.end(); it++)
2978 int key = (*it).first;
2979 double hi = (*it).second;
2980 const TopoDS_Shape& shape = ShapesWithLocalSize.FindKey(key);
2981 const TopoDS_Edge& e = TopoDS::Edge(shape);
2982 setLocalSize( e, hi, *ngMesh );
2984 for(std::map<int,double>::const_iterator it=VertexId2LocalSize.begin(); it!=VertexId2LocalSize.end(); it++)
2986 int key = (*it).first;
2987 double hi = (*it).second;
2988 const TopoDS_Shape& shape = ShapesWithLocalSize.FindKey(key);
2989 const TopoDS_Vertex& v = TopoDS::Vertex(shape);
2990 gp_Pnt p = BRep_Tool::Pnt(v);
2991 NETGENPlugin_Mesher::RestrictLocalSize( *ngMesh, p.XYZ(), hi );
2993 for(map<int,double>::const_iterator it=FaceId2LocalSize.begin();
2994 it!=FaceId2LocalSize.end(); it++)
2996 int key = (*it).first;
2997 double val = (*it).second;
2998 const TopoDS_Shape& shape = ShapesWithLocalSize.FindKey(key);
2999 int faceNgID = occgeo.fmap.FindIndex(shape);
3000 occgeo.SetFaceMaxH(faceNgID, val);
3001 for ( TopExp_Explorer edgeExp( shape, TopAbs_EDGE ); edgeExp.More(); edgeExp.Next() )
3002 setLocalSize( TopoDS::Edge( edgeExp.Current() ), val, *ngMesh );
3005 // calculate total nb of segments and length of edges
3006 double fullLen = 0.0;
3008 int entity = mparams.secondorder > 0 ? SMDSEntity_Quad_Edge : SMDSEntity_Edge;
3009 TopTools_DataMapOfShapeInteger Edge2NbSeg;
3010 for (TopExp_Explorer exp(_shape, TopAbs_EDGE); exp.More(); exp.Next())
3012 TopoDS_Edge E = TopoDS::Edge( exp.Current() );
3013 if( !Edge2NbSeg.Bind(E,0) )
3016 double aLen = SMESH_Algo::EdgeLength(E);
3019 vector<int>& aVec = aResMap[_mesh->GetSubMesh(E)];
3021 aVec.resize( SMDSEntity_Last, 0);
3023 fullNbSeg += aVec[ entity ];
3026 // store nb of segments computed by Netgen
3027 NCollection_Map<Link> linkMap;
3028 for (int i = 1; i <= ngMesh->GetNSeg(); ++i )
3030 const netgen::Segment& seg = ngMesh->LineSegment(i);
3031 Link link(seg[0], seg[1]);
3032 if ( !linkMap.Add( link )) continue;
3033 int aGeomEdgeInd = seg.epgeominfo[0].edgenr;
3034 if (aGeomEdgeInd > 0 && aGeomEdgeInd <= occgeo.emap.Extent())
3036 vector<int>& aVec = aResMap[_mesh->GetSubMesh(occgeo.emap(aGeomEdgeInd))];
3040 // store nb of nodes on edges computed by Netgen
3041 TopTools_DataMapIteratorOfDataMapOfShapeInteger Edge2NbSegIt(Edge2NbSeg);
3042 for (; Edge2NbSegIt.More(); Edge2NbSegIt.Next())
3044 vector<int>& aVec = aResMap[_mesh->GetSubMesh(Edge2NbSegIt.Key())];
3045 if ( aVec[ entity ] > 1 && aVec[ SMDSEntity_Node ] == 0 )
3046 aVec[SMDSEntity_Node] = mparams.secondorder > 0 ? 2*aVec[ entity ]-1 : aVec[ entity ]-1;
3048 fullNbSeg += aVec[ entity ];
3049 Edge2NbSeg( Edge2NbSegIt.Key() ) = aVec[ entity ];
3051 if ( fullNbSeg == 0 )
3058 if ( double area = _simpleHyp->GetMaxElementArea() ) {
3060 mparams.maxh = sqrt(2. * area/sqrt(3.0));
3061 mparams.grading = 0.4; // moderate size growth
3064 // length from edges
3065 mparams.maxh = fullLen/fullNbSeg;
3066 mparams.grading = 0.2; // slow size growth
3069 mparams.maxh = min( mparams.maxh, occgeo.boundingbox.Diam()/2 );
3070 mparams.maxh = min( mparams.maxh, fullLen/fullNbSeg * (1. + mparams.grading));
3072 for (TopExp_Explorer exp(_shape, TopAbs_FACE); exp.More(); exp.Next())
3074 TopoDS_Face F = TopoDS::Face( exp.Current() );
3075 SMESH_subMesh *sm = _mesh->GetSubMesh(F);
3077 BRepGProp::SurfaceProperties(F,G);
3078 double anArea = G.Mass();
3079 tooManyElems = tooManyElems || ( anArea/hugeNb > mparams.maxh*mparams.maxh );
3081 if ( !tooManyElems )
3083 TopTools_MapOfShape egdes;
3084 for (TopExp_Explorer exp1(F,TopAbs_EDGE); exp1.More(); exp1.Next())
3085 if ( egdes.Add( exp1.Current() ))
3086 nb1d += Edge2NbSeg.Find(exp1.Current());
3088 int nbFaces = tooManyElems ? hugeNb : int( 4*anArea / (mparams.maxh*mparams.maxh*sqrt(3.)));
3089 int nbNodes = tooManyElems ? hugeNb : (( nbFaces*3 - (nb1d-1)*2 ) / 6 + 1 );
3091 vector<int> aVec(SMDSEntity_Last, 0);
3092 if( mparams.secondorder > 0 ) {
3093 int nb1d_in = (nbFaces*3 - nb1d) / 2;
3094 aVec[SMDSEntity_Node] = nbNodes + nb1d_in;
3095 aVec[SMDSEntity_Quad_Triangle] = nbFaces;
3098 aVec[SMDSEntity_Node] = Max ( nbNodes, 0 );
3099 aVec[SMDSEntity_Triangle] = nbFaces;
3101 aResMap[sm].swap(aVec);
3108 // pass 3D simple parameters to NETGEN
3109 const NETGENPlugin_SimpleHypothesis_3D* simple3d =
3110 dynamic_cast< const NETGENPlugin_SimpleHypothesis_3D* > ( _simpleHyp );
3112 if ( double vol = simple3d->GetMaxElementVolume() ) {
3114 mparams.maxh = pow( 72, 1/6. ) * pow( vol, 1/3. );
3115 mparams.maxh = min( mparams.maxh, occgeo.boundingbox.Diam()/2 );
3118 // using previous length from faces
3120 mparams.grading = 0.4;
3121 mparams.maxh = min( mparams.maxh, fullLen/fullNbSeg * (1. + mparams.grading));
3124 BRepGProp::VolumeProperties(_shape,G);
3125 double aVolume = G.Mass();
3126 double tetrVol = 0.1179*mparams.maxh*mparams.maxh*mparams.maxh;
3127 tooManyElems = tooManyElems || ( aVolume/hugeNb > tetrVol );
3128 int nbVols = tooManyElems ? hugeNb : int(aVolume/tetrVol);
3129 int nb1d_in = int(( nbVols*6 - fullNbSeg ) / 6 );
3130 vector<int> aVec(SMDSEntity_Last, 0 );
3131 if ( tooManyElems ) // avoid FPE
3133 aVec[SMDSEntity_Node] = hugeNb;
3134 aVec[ mparams.secondorder > 0 ? SMDSEntity_Quad_Tetra : SMDSEntity_Tetra] = hugeNb;
3138 if( mparams.secondorder > 0 ) {
3139 aVec[SMDSEntity_Node] = nb1d_in/3 + 1 + nb1d_in;
3140 aVec[SMDSEntity_Quad_Tetra] = nbVols;
3143 aVec[SMDSEntity_Node] = nb1d_in/3 + 1;
3144 aVec[SMDSEntity_Tetra] = nbVols;
3147 SMESH_subMesh *sm = _mesh->GetSubMesh(_shape);
3148 aResMap[sm].swap(aVec);
3154 double NETGENPlugin_Mesher::GetProgress(const SMESH_Algo* holder,
3155 const int * algoProgressTic,
3156 const double * algoProgress) const
3158 ((int&) _progressTic ) = *algoProgressTic + 1;
3160 if ( !_occgeom ) return 0;
3162 double progress = -1;
3165 if ( _ticTime < 0 && netgen::multithread.task[0] == 'O'/*Optimizing surface*/ )
3167 ((double&) _ticTime ) = edgeFaceMeshingTime / _totalTime / _progressTic;
3169 else if ( !_optimize /*&& _occgeom->fmap.Extent() > 1*/ )
3171 int doneShapeIndex = -1;
3172 while ( doneShapeIndex+1 < _occgeom->facemeshstatus.Size() &&
3173 _occgeom->facemeshstatus[ doneShapeIndex+1 ])
3175 if ( doneShapeIndex+1 != _curShapeIndex )
3177 ((int&) _curShapeIndex) = doneShapeIndex+1;
3178 double doneShapeRate = _curShapeIndex / double( _occgeom->fmap.Extent() );
3179 double doneTime = edgeMeshingTime + doneShapeRate * faceMeshingTime;
3180 ((double&) _ticTime) = doneTime / _totalTime / _progressTic;
3181 // cout << "shape " << _curShapeIndex << " _ticTime " << _ticTime
3182 // << " " << doneTime / _totalTime / _progressTic << endl;
3186 else if ( !_optimize && _occgeom->somap.Extent() > 1 )
3188 int curShapeIndex = _curShapeIndex;
3189 if ( _ngMesh->GetNE() > 0 )
3191 netgen::Element el = (*_ngMesh)[netgen::ElementIndex( _ngMesh->GetNE()-1 )];
3192 curShapeIndex = el.GetIndex();
3194 if ( curShapeIndex != _curShapeIndex )
3196 ((int&) _curShapeIndex) = curShapeIndex;
3197 double doneShapeRate = _curShapeIndex / double( _occgeom->somap.Extent() );
3198 double doneTime = edgeFaceMeshingTime + doneShapeRate * voluMeshingTime;
3199 ((double&) _ticTime) = doneTime / _totalTime / _progressTic;
3200 // cout << "shape " << _curShapeIndex << " _ticTime " << _ticTime
3201 // << " " << doneTime / _totalTime / _progressTic << endl;
3205 progress = Max( *algoProgressTic * _ticTime, *algoProgress );
3208 ((int&) *algoProgressTic )++;
3209 ((double&) *algoProgress) = progress;
3211 //cout << progress << " " << *algoProgressTic << " " << netgen::multithread.task << " "<< _ticTime << endl;
3213 return Min( progress, 0.99 );
3216 //================================================================================
3218 * \brief Remove "test.out" and "problemfaces" files in current directory
3220 //================================================================================
3222 void NETGENPlugin_Mesher::RemoveTmpFiles()
3224 bool rm = SMESH_File("test.out").remove() ;
3226 if (rm && netgen::testout)
3228 delete netgen::testout;
3229 netgen::testout = 0;
3232 SMESH_File("problemfaces").remove();
3233 SMESH_File("occmesh.rep").remove();
3236 //================================================================================
3238 * \brief Read mesh entities preventing successful computation from "test.out" file
3240 //================================================================================
3242 SMESH_ComputeErrorPtr
3243 NETGENPlugin_Mesher::ReadErrors(const vector<const SMDS_MeshNode* >& nodeVec)
3245 SMESH_ComputeErrorPtr err = SMESH_ComputeError::New
3246 (COMPERR_BAD_INPUT_MESH, "Some edges multiple times in surface mesh");
3247 SMESH_File file("test.out");
3249 const char* badEdgeStr = " multiple times in surface mesh";
3250 const int badEdgeStrLen = strlen( badEdgeStr );
3251 while( !file.eof() )
3253 if ( strncmp( file, "Edge ", 5 ) == 0 &&
3254 file.getInts( two ) &&
3255 strncmp( file, badEdgeStr, badEdgeStrLen ) == 0 &&
3256 two[0] < nodeVec.size() && two[1] < nodeVec.size())
3258 err->myBadElements.push_back( new SMDS_LinearEdge( nodeVec[ two[0]], nodeVec[ two[1]] ));
3259 file += badEdgeStrLen;
3261 else if ( strncmp( file, "Intersecting: ", 14 ) == 0 )
3264 // openelement 18 with open element 126
3267 vector<int> three1(3), three2(3);
3269 const char* pos = file;
3270 bool ok = ( strncmp( file, "openelement ", 12 ) == 0 );
3271 ok = ok && file.getInts( two );
3272 ok = ok && file.getInts( three1 );
3273 ok = ok && file.getInts( three2 );
3274 for ( int i = 0; ok && i < 3; ++i )
3275 ok = ( three1[i] < nodeVec.size() && nodeVec[ three1[i]]);
3276 for ( int i = 0; ok && i < 3; ++i )
3277 ok = ( three2[i] < nodeVec.size() && nodeVec[ three2[i]]);
3280 err->myBadElements.push_back( new SMDS_FaceOfNodes( nodeVec[ three1[0]],
3281 nodeVec[ three1[1]],
3282 nodeVec[ three1[2]]));
3283 err->myBadElements.push_back( new SMDS_FaceOfNodes( nodeVec[ three2[0]],
3284 nodeVec[ three2[1]],
3285 nodeVec[ three2[2]]));
3286 err->myComment = "Intersecting triangles";
3301 //================================================================================
3303 * \brief Write a python script creating an equivalent SALOME mesh.
3304 * This is useful to see what mesh is passed as input for the next step of mesh
3305 * generation (of mesh of higher dimension)
3307 //================================================================================
3309 void NETGENPlugin_Mesher::toPython( const netgen::Mesh* ngMesh,
3310 const std::string& pyFile)
3312 ofstream outfile(pyFile.c_str(), ios::out);
3313 if ( !outfile ) return;
3315 outfile << "import SMESH" << endl
3316 << "from salome.smesh import smeshBuilder" << endl
3317 << "smesh = smeshBuilder.New(salome.myStudy)" << endl
3318 << "mesh = smesh.Mesh()" << endl << endl;
3320 using namespace netgen;
3322 for (pi = PointIndex::BASE;
3323 pi < ngMesh->GetNP()+PointIndex::BASE; pi++)
3325 outfile << "mesh.AddNode( ";
3326 outfile << (*ngMesh)[pi](0) << ", ";
3327 outfile << (*ngMesh)[pi](1) << ", ";
3328 outfile << (*ngMesh)[pi](2) << ") ## "<< pi << endl;
3331 int nbDom = ngMesh->GetNDomains();
3332 for ( int i = 0; i < nbDom; ++i )
3333 outfile<< "grp" << i+1 << " = mesh.CreateEmptyGroup( SMESH.FACE, 'domain"<< i+1 << "')"<< endl;
3335 SurfaceElementIndex sei;
3336 for (sei = 0; sei < ngMesh->GetNSE(); sei++)
3338 outfile << "mesh.AddFace([ ";
3339 Element2d sel = (*ngMesh)[sei];
3340 for (int j = 0; j < sel.GetNP(); j++)
3341 outfile << sel[j] << ( j+1 < sel.GetNP() ? ", " : " ])");
3342 if ( sel.IsDeleted() ) outfile << " ## IsDeleted ";
3345 if ((*ngMesh)[sei].GetIndex())
3347 if ( int dom1 = ngMesh->GetFaceDescriptor((*ngMesh)[sei].GetIndex ()).DomainIn())
3348 outfile << "grp"<< dom1 <<".Add([ " << (int)sei+1 << " ])" << endl;
3349 if ( int dom2 = ngMesh->GetFaceDescriptor((*ngMesh)[sei].GetIndex ()).DomainOut())
3350 outfile << "grp"<< dom2 <<".Add([ " << (int)sei+1 << " ])" << endl;
3354 for (ElementIndex ei = 0; ei < ngMesh->GetNE(); ei++)
3356 Element el = (*ngMesh)[ei];
3357 outfile << "mesh.AddVolume([ ";
3358 for (int j = 0; j < el.GetNP(); j++)
3359 outfile << el[j] << ( j+1 < el.GetNP() ? ", " : " ])");
3363 for (int i = 1; i <= ngMesh->GetNSeg(); i++)
3365 const Segment & seg = ngMesh->LineSegment (i);
3366 outfile << "mesh.AddEdge([ "
3368 << seg[1] << " ])" << endl;
3370 cout << "Write " << pyFile << endl;
3373 //================================================================================
3375 * \brief Constructor of NETGENPlugin_ngMeshInfo
3377 //================================================================================
3379 NETGENPlugin_ngMeshInfo::NETGENPlugin_ngMeshInfo( netgen::Mesh* ngMesh):
3384 _nbNodes = ngMesh->GetNP();
3385 _nbSegments = ngMesh->GetNSeg();
3386 _nbFaces = ngMesh->GetNSE();
3387 _nbVolumes = ngMesh->GetNE();
3391 _nbNodes = _nbSegments = _nbFaces = _nbVolumes = 0;
3395 //================================================================================
3397 * \brief Copy LocalH member from one netgen mesh to another
3399 //================================================================================
3401 void NETGENPlugin_ngMeshInfo::transferLocalH( netgen::Mesh* fromMesh,
3402 netgen::Mesh* toMesh )
3404 if ( !fromMesh->LocalHFunctionGenerated() ) return;
3405 if ( !toMesh->LocalHFunctionGenerated() )
3407 toMesh->CalcLocalH(netgen::mparam.grading);
3409 toMesh->CalcLocalH();
3412 const size_t size = sizeof( netgen::LocalH );
3413 _copyOfLocalH = new char[ size ];
3414 memcpy( (void*)_copyOfLocalH, (void*)&toMesh->LocalHFunction(), size );
3415 memcpy( (void*)&toMesh->LocalHFunction(), (void*)&fromMesh->LocalHFunction(), size );
3418 //================================================================================
3420 * \brief Restore LocalH member of a netgen mesh
3422 //================================================================================
3424 void NETGENPlugin_ngMeshInfo::restoreLocalH( netgen::Mesh* toMesh )
3426 if ( _copyOfLocalH )
3428 const size_t size = sizeof( netgen::LocalH );
3429 memcpy( (void*)&toMesh->LocalHFunction(), (void*)_copyOfLocalH, size );
3430 delete [] _copyOfLocalH;
3435 //================================================================================
3437 * \brief Find "internal" sub-shapes
3439 //================================================================================
3441 NETGENPlugin_Internals::NETGENPlugin_Internals( SMESH_Mesh& mesh,
3442 const TopoDS_Shape& shape,
3444 : _mesh( mesh ), _is3D( is3D )
3446 SMESHDS_Mesh* meshDS = mesh.GetMeshDS();
3448 TopExp_Explorer f,e;
3449 for ( f.Init( shape, TopAbs_FACE ); f.More(); f.Next() )
3451 int faceID = meshDS->ShapeToIndex( f.Current() );
3453 // find not computed internal edges
3455 for ( e.Init( f.Current().Oriented(TopAbs_FORWARD), TopAbs_EDGE ); e.More(); e.Next() )
3456 if ( e.Current().Orientation() == TopAbs_INTERNAL )
3458 SMESH_subMesh* eSM = mesh.GetSubMesh( e.Current() );
3459 if ( eSM->IsEmpty() )
3461 _e2face.insert( make_pair( eSM->GetId(), faceID ));
3462 for ( TopoDS_Iterator v(e.Current()); v.More(); v.Next() )
3463 _e2face.insert( make_pair( meshDS->ShapeToIndex( v.Value() ), faceID ));
3467 // find internal vertices in a face
3468 set<int> intVV; // issue 0020850 where same vertex is twice in a face
3469 for ( TopoDS_Iterator fSub( f.Current() ); fSub.More(); fSub.Next())
3470 if ( fSub.Value().ShapeType() == TopAbs_VERTEX )
3472 int vID = meshDS->ShapeToIndex( fSub.Value() );
3473 if ( intVV.insert( vID ).second )
3474 _f2v[ faceID ].push_back( vID );
3479 // find internal faces and their subshapes where nodes are to be doubled
3480 // to make a crack with non-sewed borders
3482 if ( f.Current().Orientation() == TopAbs_INTERNAL )
3484 _intShapes.insert( meshDS->ShapeToIndex( f.Current() ));
3487 list< TopoDS_Shape > edges;
3488 for ( e.Init( f.Current(), TopAbs_EDGE ); e.More(); e.Next())
3489 if ( SMESH_MesherHelper::NbAncestors( e.Current(), mesh, TopAbs_FACE ) > 1 )
3491 _intShapes.insert( meshDS->ShapeToIndex( e.Current() ));
3492 edges.push_back( e.Current() );
3493 // find border faces
3494 PShapeIteratorPtr fIt =
3495 SMESH_MesherHelper::GetAncestors( edges.back(),mesh,TopAbs_FACE );
3496 while ( const TopoDS_Shape* pFace = fIt->next() )
3497 if ( !pFace->IsSame( f.Current() ))
3498 _borderFaces.insert( meshDS->ShapeToIndex( *pFace ));
3501 // we consider vertex internal if it is shared by more than one internal edge
3502 list< TopoDS_Shape >::iterator edge = edges.begin();
3503 for ( ; edge != edges.end(); ++edge )
3504 for ( TopoDS_Iterator v( *edge ); v.More(); v.Next() )
3506 set<int> internalEdges;
3507 PShapeIteratorPtr eIt =
3508 SMESH_MesherHelper::GetAncestors( v.Value(),mesh,TopAbs_EDGE );
3509 while ( const TopoDS_Shape* pEdge = eIt->next() )
3511 int edgeID = meshDS->ShapeToIndex( *pEdge );
3512 if ( isInternalShape( edgeID ))
3513 internalEdges.insert( edgeID );
3515 if ( internalEdges.size() > 1 )
3516 _intShapes.insert( meshDS->ShapeToIndex( v.Value() ));
3520 } // loop on geom faces
3522 // find vertices internal in solids
3525 for ( TopExp_Explorer so(shape, TopAbs_SOLID); so.More(); so.Next())
3527 int soID = meshDS->ShapeToIndex( so.Current() );
3528 for ( TopoDS_Iterator soSub( so.Current() ); soSub.More(); soSub.Next())
3529 if ( soSub.Value().ShapeType() == TopAbs_VERTEX )
3530 _s2v[ soID ].push_back( meshDS->ShapeToIndex( soSub.Value() ));
3535 //================================================================================
3537 * \brief Find mesh faces on non-internal geom faces sharing internal edge
3538 * some nodes of which are to be doubled to make the second border of the "crack"
3540 //================================================================================
3542 void NETGENPlugin_Internals::findBorderElements( TIDSortedElemSet & borderElems )
3544 if ( _intShapes.empty() ) return;
3546 SMESH_Mesh& mesh = const_cast<SMESH_Mesh&>(_mesh);
3547 SMESHDS_Mesh* meshDS = mesh.GetMeshDS();
3549 // loop on internal geom edges
3550 set<int>::const_iterator intShapeId = _intShapes.begin();
3551 for ( ; intShapeId != _intShapes.end(); ++intShapeId )
3553 const TopoDS_Shape& s = meshDS->IndexToShape( *intShapeId );
3554 if ( s.ShapeType() != TopAbs_EDGE ) continue;
3556 // get internal and non-internal geom faces sharing the internal edge <s>
3558 set<int>::iterator bordFace = _borderFaces.end();
3559 PShapeIteratorPtr faces = SMESH_MesherHelper::GetAncestors( s, _mesh, TopAbs_FACE );
3560 while ( const TopoDS_Shape* pFace = faces->next() )
3562 int faceID = meshDS->ShapeToIndex( *pFace );
3563 if ( isInternalShape( faceID ))
3566 bordFace = _borderFaces.insert( faceID ).first;
3568 if ( bordFace == _borderFaces.end() || !intFace ) continue;
3570 // get all links of mesh faces on internal geom face sharing nodes on edge <s>
3571 set< SMESH_OrientedLink > links; //!< links of faces on internal geom face
3572 list<const SMDS_MeshElement*> suspectFaces[2]; //!< mesh faces on border geom faces
3573 int nbSuspectFaces = 0;
3574 SMESHDS_SubMesh* intFaceSM = meshDS->MeshElements( intFace );
3575 if ( !intFaceSM || intFaceSM->NbElements() == 0 ) continue;
3576 SMESH_subMeshIteratorPtr smIt = mesh.GetSubMesh( s )->getDependsOnIterator(true,true);
3577 while ( smIt->more() )
3579 SMESHDS_SubMesh* sm = smIt->next()->GetSubMeshDS();
3580 if ( !sm ) continue;
3581 SMDS_NodeIteratorPtr nIt = sm->GetNodes();
3582 while ( nIt->more() )
3584 const SMDS_MeshNode* nOnEdge = nIt->next();
3585 SMDS_ElemIteratorPtr fIt = nOnEdge->GetInverseElementIterator(SMDSAbs_Face);
3586 while ( fIt->more() )
3588 const SMDS_MeshElement* f = fIt->next();
3589 int nbNodes = f->NbNodes() / ( f->IsQuadratic() ? 2 : 1 );
3590 if ( intFaceSM->Contains( f ))
3592 for ( int i = 0; i < nbNodes; ++i )
3593 links.insert( SMESH_OrientedLink( f->GetNode(i), f->GetNode((i+1)%nbNodes)));
3598 for ( int i = 0; i < nbNodes; ++i )
3599 nbDblNodes += isInternalShape( f->GetNode(i)->getshapeId() );
3601 suspectFaces[ nbDblNodes < 2 ].push_back( f );
3607 // suspectFaces[0] having link with same orientation as mesh faces on
3608 // the internal geom face are <borderElems>. suspectFaces[1] have
3609 // only one node on edge <s>, we decide on them later (at the 2nd loop)
3610 // by links of <borderElems> found at the 1st and 2nd loops
3611 set< SMESH_OrientedLink > borderLinks;
3612 for ( int isPostponed = 0; isPostponed < 2; ++isPostponed )
3614 list<const SMDS_MeshElement*>::iterator fIt = suspectFaces[isPostponed].begin();
3615 for ( int nbF = 0; fIt != suspectFaces[isPostponed].end(); ++fIt, ++nbF )
3617 const SMDS_MeshElement* f = *fIt;
3618 bool isBorder = false, linkFound = false, borderLinkFound = false;
3619 list< SMESH_OrientedLink > faceLinks;
3620 int nbNodes = f->NbNodes() / ( f->IsQuadratic() ? 2 : 1 );
3621 for ( int i = 0; i < nbNodes; ++i )
3623 SMESH_OrientedLink link( f->GetNode(i), f->GetNode((i+1)%nbNodes));
3624 faceLinks.push_back( link );
3627 set< SMESH_OrientedLink >::iterator foundLink = links.find( link );
3628 if ( foundLink != links.end() )
3631 isBorder = ( foundLink->_reversed == link._reversed );
3632 if ( !isBorder && !isPostponed ) break;
3633 faceLinks.pop_back();
3635 else if ( isPostponed && !borderLinkFound )
3637 foundLink = borderLinks.find( link );
3638 if ( foundLink != borderLinks.end() )
3640 borderLinkFound = true;
3641 isBorder = ( foundLink->_reversed != link._reversed );
3648 borderElems.insert( f );
3649 borderLinks.insert( faceLinks.begin(), faceLinks.end() );
3651 else if ( !linkFound && !borderLinkFound )
3653 suspectFaces[1].push_back( f );
3654 if ( nbF > 2 * nbSuspectFaces )
3655 break; // dead loop protection
3662 //================================================================================
3664 * \brief put internal shapes in maps and fill in submeshes to precompute
3666 //================================================================================
3668 void NETGENPlugin_Internals::getInternalEdges( TopTools_IndexedMapOfShape& fmap,
3669 TopTools_IndexedMapOfShape& emap,
3670 TopTools_IndexedMapOfShape& vmap,
3671 list< SMESH_subMesh* > smToPrecompute[])
3673 if ( !hasInternalEdges() ) return;
3674 map<int,int>::const_iterator ev_face = _e2face.begin();
3675 for ( ; ev_face != _e2face.end(); ++ev_face )
3677 const TopoDS_Shape& ev = _mesh.GetMeshDS()->IndexToShape( ev_face->first );
3678 const TopoDS_Shape& face = _mesh.GetMeshDS()->IndexToShape( ev_face->second );
3680 ( ev.ShapeType() == TopAbs_EDGE ? emap : vmap ).Add( ev );
3682 //cout<<"INTERNAL EDGE or VERTEX "<<ev_face->first<<" on face "<<ev_face->second<<endl;
3684 smToPrecompute[ MeshDim_1D ].push_back( _mesh.GetSubMeshContaining( ev_face->first ));
3688 //================================================================================
3690 * \brief return shapes and submeshes to be meshed and already meshed boundary submeshes
3692 //================================================================================
3694 void NETGENPlugin_Internals::getInternalFaces( TopTools_IndexedMapOfShape& fmap,
3695 TopTools_IndexedMapOfShape& emap,
3696 list< SMESH_subMesh* >& intFaceSM,
3697 list< SMESH_subMesh* >& boundarySM)
3699 if ( !hasInternalFaces() ) return;
3701 // <fmap> and <emap> are for not yet meshed shapes
3702 // <intFaceSM> is for submeshes of faces
3703 // <boundarySM> is for meshed edges and vertices
3708 set<int> shapeIDs ( _intShapes );
3709 if ( !_borderFaces.empty() )
3710 shapeIDs.insert( _borderFaces.begin(), _borderFaces.end() );
3712 set<int>::const_iterator intS = shapeIDs.begin();
3713 for ( ; intS != shapeIDs.end(); ++intS )
3715 SMESH_subMesh* sm = _mesh.GetSubMeshContaining( *intS );
3717 if ( sm->GetSubShape().ShapeType() != TopAbs_FACE ) continue;
3719 intFaceSM.push_back( sm );
3721 // add submeshes of not computed internal faces
3722 if ( !sm->IsEmpty() ) continue;
3724 SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(true,true);
3725 while ( smIt->more() )
3728 const TopoDS_Shape& s = sm->GetSubShape();
3730 if ( sm->IsEmpty() )
3733 switch ( s.ShapeType() ) {
3734 case TopAbs_FACE: fmap.Add ( s ); break;
3735 case TopAbs_EDGE: emap.Add ( s ); break;
3741 if ( s.ShapeType() != TopAbs_FACE )
3742 boundarySM.push_back( sm );
3748 //================================================================================
3750 * \brief Return true if given shape is to be precomputed in order to be correctly
3751 * added to netgen mesh
3753 //================================================================================
3755 bool NETGENPlugin_Internals::isShapeToPrecompute(const TopoDS_Shape& s)
3757 int shapeID = _mesh.GetMeshDS()->ShapeToIndex( s );
3758 switch ( s.ShapeType() ) {
3759 case TopAbs_FACE : break; //return isInternalShape( shapeID ) || isBorderFace( shapeID );
3760 case TopAbs_EDGE : return isInternalEdge( shapeID );
3761 case TopAbs_VERTEX: break;
3767 //================================================================================
3769 * \brief Return SMESH
3771 //================================================================================
3773 SMESH_Mesh& NETGENPlugin_Internals::getMesh() const
3775 return const_cast<SMESH_Mesh&>( _mesh );
3778 //================================================================================
3780 * \brief Initialize netgen library
3782 //================================================================================
3784 NETGENPlugin_NetgenLibWrapper::NETGENPlugin_NetgenLibWrapper()
3788 _isComputeOk = false;
3790 if ( !getenv( "KEEP_NETGEN_OUTPUT" ))
3792 // redirect all netgen output (mycout,myerr,cout) to _outputFileName
3793 _outputFileName = getOutputFileName();
3794 netgen::mycout = new ofstream ( _outputFileName.c_str() );
3795 netgen::myerr = netgen::mycout;
3796 _coutBuffer = std::cout.rdbuf();
3798 cout << "NOTE: netgen output is redirected to file " << _outputFileName << endl;
3800 std::cout.rdbuf( netgen::mycout->rdbuf() );
3804 _ngMesh = Ng_NewMesh();
3807 //================================================================================
3809 * \brief Finish using netgen library
3811 //================================================================================
3813 NETGENPlugin_NetgenLibWrapper::~NETGENPlugin_NetgenLibWrapper()
3815 Ng_DeleteMesh( _ngMesh );
3817 NETGENPlugin_Mesher::RemoveTmpFiles();
3819 std::cout.rdbuf( _coutBuffer );
3826 //================================================================================
3828 * \brief Set netgen mesh to delete at destruction
3830 //================================================================================
3832 void NETGENPlugin_NetgenLibWrapper::setMesh( Ng_Mesh* mesh )
3835 Ng_DeleteMesh( _ngMesh );
3839 //================================================================================
3841 * \brief Return a unique file name
3843 //================================================================================
3845 std::string NETGENPlugin_NetgenLibWrapper::getOutputFileName()
3847 std::string aTmpDir = SALOMEDS_Tool::GetTmpDir();
3849 TCollection_AsciiString aGenericName = (char*)aTmpDir.c_str();
3850 aGenericName += "NETGEN_";
3852 aGenericName += getpid();
3854 aGenericName += _getpid();
3856 aGenericName += "_";
3857 aGenericName += Abs((Standard_Integer)(long) aGenericName.ToCString());
3858 aGenericName += ".out";
3860 return aGenericName.ToCString();
3863 //================================================================================
3865 * \brief Remove file with netgen output
3867 //================================================================================
3869 void NETGENPlugin_NetgenLibWrapper::removeOutputFile()
3871 if ( !_outputFileName.empty() )
3873 if ( netgen::mycout )
3875 delete netgen::mycout;
3879 string tmpDir = SALOMEDS_Tool::GetDirFromPath ( _outputFileName );
3880 string aFileName = SALOMEDS_Tool::GetNameFromPath( _outputFileName ) + ".out";
3881 SALOMEDS::ListOfFileNames_var aFiles = new SALOMEDS::ListOfFileNames;
3883 aFiles[0] = aFileName.c_str();
3885 SALOMEDS_Tool::RemoveTemporaryFiles( tmpDir.c_str(), aFiles.in(), true );