+std::map<int,double> SolidId2LocalSize;
+
+std::vector<SMESHUtils::ControlPnt> ControlPoints;
+std::set<int> ShapesWithControlPoints; // <-- allows calling SetLocalSize() several times w/o recomputing ControlPoints
+
+namespace
+{
+ inline void NOOP_Deleter(void *) { ; }
+
+ //=============================================================================
+ /*!
+ * Link - a pair of integer numbers
+ */
+ //=============================================================================
+ struct Link
+ {
+ int n1, n2;
+ Link(int _n1, int _n2) : n1(_n1), n2(_n2) {}
+ Link() : n1(0), n2(0) {}
+ bool Contains( int n ) const { return n == n1 || n == n2; }
+ bool IsConnected( const Link& other ) const
+ {
+ return (( Contains( other.n1 ) || Contains( other.n2 )) && ( this != &other ));
+ }
+ static int HashCode(const Link& aLink, int aLimit)
+ {
+ return ::HashCode(aLink.n1 + aLink.n2, aLimit);
+ }
+
+ static Standard_Boolean IsEqual(const Link& aLink1, const Link& aLink2)
+ {
+ return (( aLink1.n1 == aLink2.n1 && aLink1.n2 == aLink2.n2 ) ||
+ ( aLink1.n1 == aLink2.n2 && aLink1.n2 == aLink2.n1 ));
+ }
+ };
+
+ typedef NCollection_Map<Link,Link> TLinkMap;
+
+ //================================================================================
+ /*!
+ * \brief return id of netgen point corresponding to SMDS node
+ */
+ //================================================================================
+ typedef map< const SMDS_MeshNode*, int > TNode2IdMap;
+
+ int ngNodeId( const SMDS_MeshNode* node,
+ netgen::Mesh& ngMesh,
+ TNode2IdMap& nodeNgIdMap)
+ {
+ int newNgId = ngMesh.GetNP() + 1;
+
+ TNode2IdMap::iterator node_id = nodeNgIdMap.insert( make_pair( node, newNgId )).first;
+
+ if ( node_id->second == newNgId)
+ {
+#if defined(DUMP_SEGMENTS) || defined(DUMP_TRIANGLES)
+ cout << "Ng " << newNgId << " - " << node;
+#endif
+ netgen::MeshPoint p( netgen::Point<3> (node->X(), node->Y(), node->Z()) );
+ ngMesh.AddPoint( p );
+ }
+ return node_id->second;
+ }
+
+ //================================================================================
+ /*!
+ * \brief Return computed EDGEs connected to the given one
+ */
+ //================================================================================
+
+ list< TopoDS_Edge > getConnectedEdges( const TopoDS_Edge& edge,
+ const TopoDS_Face& face,
+ const set< SMESH_subMesh* > & /*computedSM*/,
+ const SMESH_MesherHelper& helper,
+ map< SMESH_subMesh*, set< int > >& addedEdgeSM2Faces)
+ {
+ // get ordered EDGEs
+ list< TopoDS_Edge > edges;
+ list< int > nbEdgesInWire;
+ /*int nbWires =*/ SMESH_Block::GetOrderedEdges( face, edges, nbEdgesInWire);
+
+ // find <edge> within <edges>
+ list< TopoDS_Edge >::iterator eItFwd = edges.begin();
+ for ( ; eItFwd != edges.end(); ++eItFwd )
+ if ( edge.IsSame( *eItFwd ))
+ break;
+ if ( eItFwd == edges.end()) return list< TopoDS_Edge>();
+
+ if ( eItFwd->Orientation() >= TopAbs_INTERNAL )
+ {
+ // connected INTERNAL edges returned from GetOrderedEdges() are wrongly oriented
+ // so treat each INTERNAL edge separately
+ TopoDS_Edge e = *eItFwd;
+ edges.clear();
+ edges.push_back( e );
+ return edges;
+ }
+
+ // get all computed EDGEs connected to <edge>
+
+ list< TopoDS_Edge >::iterator eItBack = eItFwd, ePrev;
+ TopoDS_Vertex vCommon;
+ TopTools_MapOfShape eAdded; // map used not to add a seam edge twice to <edges>
+ eAdded.Add( edge );
+
+ // put edges before <edge> to <edges> back
+ while ( edges.begin() != eItFwd )
+ edges.splice( edges.end(), edges, edges.begin() );
+
+ // search forward
+ ePrev = eItFwd;
+ while ( ++eItFwd != edges.end() )
+ {
+ SMESH_subMesh* sm = helper.GetMesh()->GetSubMesh( *eItFwd );
+
+ bool connected = TopExp::CommonVertex( *ePrev, *eItFwd, vCommon );
+ bool computed = !sm->IsEmpty();
+ bool added = addedEdgeSM2Faces[ sm ].count( helper.GetSubShapeID() );
+ bool doubled = !eAdded.Add( *eItFwd );
+ bool orientOK = (( ePrev ->Orientation() < TopAbs_INTERNAL ) ==
+ ( eItFwd->Orientation() < TopAbs_INTERNAL ) );
+ if ( !connected || !computed || !orientOK || added || doubled )
+ {
+ // stop advancement; move edges from tail to head
+ while ( edges.back() != *ePrev )
+ edges.splice( edges.begin(), edges, --edges.end() );
+ break;
+ }
+ ePrev = eItFwd;
+ }
+ // search backward
+ while ( eItBack != edges.begin() )
+ {
+ ePrev = eItBack;
+ --eItBack;
+ SMESH_subMesh* sm = helper.GetMesh()->GetSubMesh( *eItBack );
+
+ bool connected = TopExp::CommonVertex( *ePrev, *eItBack, vCommon );
+ bool computed = !sm->IsEmpty();
+ bool added = addedEdgeSM2Faces[ sm ].count( helper.GetSubShapeID() );
+ bool doubled = !eAdded.Add( *eItBack );
+ bool orientOK = (( ePrev ->Orientation() < TopAbs_INTERNAL ) ==
+ ( eItBack->Orientation() < TopAbs_INTERNAL ) );
+ if ( !connected || !computed || !orientOK || added || doubled)
+ {
+ // stop advancement
+ edges.erase( edges.begin(), ePrev );
+ break;
+ }
+ }
+ if ( edges.front() != edges.back() )
+ {
+ // assure that the 1st vertex is meshed
+ TopoDS_Edge eLast = edges.back();
+ while ( !SMESH_Algo::VertexNode( SMESH_MesherHelper::IthVertex( 0, edges.front()), helper.GetMeshDS())
+ &&
+ edges.front() != eLast )
+ edges.splice( edges.end(), edges, edges.begin() );
+ }
+ return edges;
+ }
+
+ //================================================================================
+ /*!
+ * \brief Make triangulation of a shape precise enough
+ */
+ //================================================================================
+
+ void updateTriangulation( const TopoDS_Shape& shape )
+ {
+ // static set< Poly_Triangulation* > updated;
+
+ // TopLoc_Location loc;
+ // TopExp_Explorer fExp( shape, TopAbs_FACE );
+ // for ( ; fExp.More(); fExp.Next() )
+ // {
+ // Handle(Poly_Triangulation) triangulation =
+ // BRep_Tool::Triangulation ( TopoDS::Face( fExp.Current() ), loc);
+ // if ( triangulation.IsNull() ||
+ // updated.insert( triangulation.operator->() ).second )
+ // {
+ // BRepTools::Clean (shape);
+ try {
+ OCC_CATCH_SIGNALS;
+ BRepMesh_IncrementalMesh e(shape, 0.01, true);
+ }
+ catch (Standard_Failure&)
+ {
+ }
+ // updated.erase( triangulation.operator->() );
+ // triangulation = BRep_Tool::Triangulation ( TopoDS::Face( fExp.Current() ), loc);
+ // updated.insert( triangulation.operator->() );
+ // }
+ // }
+ }
+ //================================================================================
+ /*!
+ * \brief Returns a medium node either existing in SMESH of created by NETGEN
+ * \param [in] corner1 - corner node 1
+ * \param [in] corner2 - corner node 2
+ * \param [in] defaultMedium - the node created by NETGEN
+ * \param [in] helper - holder of medium nodes existing in SMESH
+ * \return const SMDS_MeshNode* - the result node
+ */
+ //================================================================================
+
+ const SMDS_MeshNode* mediumNode( const SMDS_MeshNode* corner1,
+ const SMDS_MeshNode* corner2,
+ const SMDS_MeshNode* defaultMedium,
+ const SMESH_MesherHelper* helper)
+ {
+ if ( helper )
+ {
+ TLinkNodeMap::const_iterator l2n =
+ helper->GetTLinkNodeMap().find( SMESH_TLink( corner1, corner2 ));
+ if ( l2n != helper->GetTLinkNodeMap().end() )
+ defaultMedium = l2n->second;
+ }
+ return defaultMedium;
+ }
+
+ //================================================================================
+ /*!
+ * \brief Assure that mesh on given shapes is quadratic
+ */
+ //================================================================================
+
+ // void makeQuadratic( const TopTools_IndexedMapOfShape& shapes,
+ // SMESH_Mesh* mesh )
+ // {
+ // for ( int i = 1; i <= shapes.Extent(); ++i )
+ // {
+ // SMESHDS_SubMesh* smDS = mesh->GetMeshDS()->MeshElements( shapes(i) );
+ // if ( !smDS ) continue;
+ // SMDS_ElemIteratorPtr elemIt = smDS->GetElements();
+ // if ( !elemIt->more() ) continue;
+ // const SMDS_MeshElement* e = elemIt->next();
+ // if ( !e || e->IsQuadratic() )
+ // continue;
+
+ // TIDSortedElemSet elems;
+ // elems.insert( e );
+ // while ( elemIt->more() )
+ // elems.insert( elems.end(), elemIt->next() );
+
+ // SMESH_MeshEditor( mesh ).ConvertToQuadratic( /*3d=*/false, elems, /*biQuad=*/false );
+ // }
+ // }
+
+ //================================================================================
+ /*!
+ * \brief Restrict size of elements on the given edge
+ */
+ //================================================================================
+
+ void setLocalSize(const TopoDS_Edge& edge,
+ double size,
+ netgen::Mesh& mesh,
+ const bool overrideMinH = true)
+ {
+ if ( size <= std::numeric_limits<double>::min() )
+ return;
+ Standard_Real u1, u2;
+ Handle(Geom_Curve) curve = BRep_Tool::Curve(edge, u1, u2);
+ if ( curve.IsNull() )
+ {
+ TopoDS_Iterator vIt( edge );
+ if ( !vIt.More() ) return;
+ gp_Pnt p = BRep_Tool::Pnt( TopoDS::Vertex( vIt.Value() ));
+ NETGENPlugin_Mesher::RestrictLocalSize( mesh, p.XYZ(), size, overrideMinH );
+ }
+ else
+ {
+ const int nb = (int)( 1.5 * SMESH_Algo::EdgeLength( edge ) / size );
+ Standard_Real delta = (u2-u1)/nb;
+ for(int i=0; i<nb; i++)
+ {
+ Standard_Real u = u1 + delta*i;
+ gp_Pnt p = curve->Value(u);
+ NETGENPlugin_Mesher::RestrictLocalSize( mesh, p.XYZ(), size, overrideMinH );
+ netgen::Point3d pi(p.X(), p.Y(), p.Z());
+ double resultSize = mesh.GetH(pi);
+ if ( resultSize - size > 0.1*size )
+ // netgen does restriction iff oldH/newH > 1.2 (localh.cpp:136)
+ NETGENPlugin_Mesher::RestrictLocalSize( mesh, p.XYZ(), resultSize/1.201, overrideMinH );
+ }
+ }
+ }
+
+ //================================================================================
+ /*!
+ * \brief Return triangle size for a given chordalError and radius of curvature
+ */
+ //================================================================================
+
+ double elemSizeForChordalError( double chordalError, double radius )
+ {
+ if ( 2 * radius < chordalError )
+ return 1.5 * radius;
+ return Sqrt( 3 ) * Sqrt( chordalError * ( 2 * radius - chordalError ));
+ }
+
+ //=============================================================================
+ /*!
+ *
+ */
+ //=============================================================================
+
+ void setLocalSize(const TopoDS_Shape& GeomShape, double LocalSize)
+ {
+ if ( GeomShape.IsNull() ) return;
+ TopAbs_ShapeEnum GeomType = GeomShape.ShapeType();
+ if (GeomType == TopAbs_COMPOUND) {
+ for (TopoDS_Iterator it (GeomShape); it.More(); it.Next()) {
+ setLocalSize(it.Value(), LocalSize);
+ }
+ return;
+ }
+ int key;
+ if (! ShapesWithLocalSize.Contains(GeomShape))
+ key = ShapesWithLocalSize.Add(GeomShape);
+ else
+ key = ShapesWithLocalSize.FindIndex(GeomShape);
+ if (GeomType == TopAbs_VERTEX) {
+ VertexId2LocalSize[key] = LocalSize;
+ } else if (GeomType == TopAbs_EDGE) {
+ EdgeId2LocalSize[key] = LocalSize;
+ } else if (GeomType == TopAbs_FACE) {
+ FaceId2LocalSize[key] = LocalSize;
+ } else if (GeomType == TopAbs_SOLID) {
+ SolidId2LocalSize[key] = LocalSize;
+ }
+ return;
+ }
+
+ //================================================================================
+ /*!
+ * \brief Return faceNgID or faceNgID-1 depending on side the given proxy face lies
+ * \param [in] f - proxy face
+ * \param [in] solidSMDSIDs - IDs of SOLIDs sharing the FACE on which face lies
+ * \param [in] faceNgID - NETGEN ID of the FACE
+ * \return int - NETGEN ID of the FACE
+ */
+ //================================================================================
+
+ int getFaceNgID( const SMDS_MeshElement* face,
+ const int * solidSMDSIDs,
+ const int faceNgID )
+ {
+ for ( int i = 0; i < 3; ++i )
+ {
+ const SMDS_MeshNode* n = face->GetNode( i );
+ const int shapeID = n->GetShapeID();
+ if ( shapeID == solidSMDSIDs[0] )
+ return faceNgID - 1;
+ if ( shapeID == solidSMDSIDs[1] )
+ return faceNgID;
+ }
+ std::vector<const SMDS_MeshNode*> fNodes( face->begin_nodes(), face->end_nodes() );
+ std::vector<const SMDS_MeshElement*> vols;
+ if ( SMDS_Mesh::GetElementsByNodes( fNodes, vols, SMDSAbs_Volume ))
+ for ( size_t i = 0; i < vols.size(); ++i )
+ {
+ const int shapeID = vols[i]->GetShapeID();
+ if ( shapeID == solidSMDSIDs[0] )
+ return faceNgID - 1;
+ if ( shapeID == solidSMDSIDs[1] )
+ return faceNgID;
+ }
+ return faceNgID;
+ }
+
+} // namespace