X-Git-Url: http://git.salome-platform.org/gitweb/?p=modules%2Fsmesh.git;a=blobdiff_plain;f=src%2FSMESHUtils%2FSMESH_MeshAlgos.cxx;h=a2c9c10368fe357f5ee0c6011b6ac255774f8ff5;hp=91f6984a343f8054b23bce2d3587a8fdf90d475d;hb=98ec6be586ce93a33c242c6ce1f90b609ce31493;hpb=8d297d6698f361d4f2dde723050bcfbaea050920 diff --git a/src/SMESHUtils/SMESH_MeshAlgos.cxx b/src/SMESHUtils/SMESH_MeshAlgos.cxx index 91f6984a3..a2c9c1036 100644 --- a/src/SMESHUtils/SMESH_MeshAlgos.cxx +++ b/src/SMESHUtils/SMESH_MeshAlgos.cxx @@ -1,4 +1,4 @@ -// Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2019 CEA/DEN, EDF R&D, OPEN CASCADE // // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS @@ -23,7 +23,7 @@ // Created : Tue Apr 30 18:00:36 2013 // Author : Edward AGAPOV (eap) -// This file holds some low level algorithms extracted from SMESH_MeshEditor +// Initially this file held some low level algorithms extracted from SMESH_MeshEditor // to make them accessible from Controls package #include "SMESH_MeshAlgos.hxx" @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -237,6 +238,7 @@ namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint() void getElementsInBox ( const Bnd_B3d& box, TElemSeq& foundElems ); void getElementsInSphere ( const gp_XYZ& center, const double radius, TElemSeq& foundElems ); ElementBndBoxTree* getLeafAtPoint( const gp_XYZ& point ); + int getNbElements(); protected: ElementBndBoxTree() {} @@ -284,6 +286,11 @@ namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint() TElementBoxPool& elBoPool = getLimitAndPool()->_elBoPool; +#ifdef _DEBUG_ + if ( theElemIt && !theElemIt->more() ) + std::cout << "WARNING: ElementBndBoxTree constructed on empty iterator!" << std::endl; +#endif + SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType ); while ( elemIt->more() ) { @@ -460,6 +467,27 @@ namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint() return 0; } + //================================================================================ + /*! + * \brief Return number of elements + */ + //================================================================================ + + int ElementBndBoxTree::getNbElements() + { + int nb = 0; + if ( isLeaf() ) + { + nb = _elements.size(); + } + else + { + for (int i = 0; i < 8; i++) + nb += ((ElementBndBoxTree*) myChildren[i])->getNbElements(); + } + return nb; + } + //================================================================================ /*! * \brief Construct the element box @@ -873,7 +901,7 @@ SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt& point, radius = point.Distance( boxCenter ) - 0.5 * ebbTree->maxSize(); if ( radius < 0 ) radius = ebbTree->maxSize() / pow( 2., getTreeHeight()) / 2; - while ( suspectElems.empty() ) + while ( suspectElems.empty() && radius < 1e100 ) { ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems ); radius *= 1.1; @@ -1243,27 +1271,52 @@ gp_XYZ SMESH_ElementSearcherImpl::Project(const gp_Pnt& point, ElementBndBoxTree*& ebbTree = _ebbTree[ _elementType ]; if ( !ebbTree ) - ebbTree = new ElementBndBoxTree( *_mesh, _elementType ); + ebbTree = new ElementBndBoxTree( *_mesh, _elementType, _meshPartIt ); gp_XYZ p = point.XYZ(); ElementBndBoxTree* ebbLeaf = ebbTree->getLeafAtPoint( p ); - const Bnd_B3d* box = ebbLeaf->getBox(); - double radius = ( box->CornerMax() - box->CornerMin() ).Modulus(); + const Bnd_B3d* box = ebbLeaf ? ebbLeaf->getBox() : ebbTree->getBox(); + gp_XYZ pMin = box->CornerMin(), pMax = box->CornerMax(); + double radius = Precision::Infinite(); + if ( ebbLeaf || !box->IsOut( p )) + { + for ( int i = 1; i <= 3; ++i ) + { + double d = 0.5 * ( pMax.Coord(i) - pMin.Coord(i) ); + if ( d > Precision::Confusion() ) + radius = Min( d, radius ); + } + if ( !ebbLeaf ) + radius /= ebbTree->getHeight( /*full=*/true ); + } + else // p outside of box + { + for ( int i = 1; i <= 3; ++i ) + { + double d = 0; + if ( point.Coord(i) < pMin.Coord(i) ) + d = pMin.Coord(i) - point.Coord(i); + else if ( point.Coord(i) > pMax.Coord(i) ) + d = point.Coord(i) - pMax.Coord(i); + if ( d > Precision::Confusion() ) + radius = Min( d, radius ); + } + } ElementBndBoxTree::TElemSeq elems; ebbTree->getElementsInSphere( p, radius, elems ); - while ( elems.empty() ) + while ( elems.empty() && radius < 1e100 ) { - radius *= 1.5; + radius *= 1.1; ebbTree->getElementsInSphere( p, radius, elems ); } gp_XYZ proj, bestProj; const SMDS_MeshElement* elem = 0; - double minDist = 2 * radius; + double minDist = Precision::Infinite(); ElementBndBoxTree::TElemSeq::iterator e = elems.begin(); for ( ; e != elems.end(); ++e ) { - double d = SMESH_MeshAlgos::GetDistance( *e, p, &proj ); + double d = SMESH_MeshAlgos::GetDistance( *e, point, &proj ); if ( d < minDist ) { bestProj = proj; @@ -1271,6 +1324,23 @@ gp_XYZ SMESH_ElementSearcherImpl::Project(const gp_Pnt& point, minDist = d; } } + if ( minDist > radius ) + { + ElementBndBoxTree::TElemSeq elems2; + ebbTree->getElementsInSphere( p, minDist, elems2 ); + for ( e = elems2.begin(); e != elems2.end(); ++e ) + { + if ( elems.count( *e )) + continue; + double d = SMESH_MeshAlgos::GetDistance( *e, point, &proj ); + if ( d < minDist ) + { + bestProj = proj; + elem = *e; + minDist = d; + } + } + } if ( closestElem ) *closestElem = elem; return bestProj; @@ -1459,7 +1529,8 @@ namespace // . RIGHT . // . . enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8, - POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX }; + POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX, + POS_MAX = POS_RIGHT }; struct PointPos { PositionName _name; @@ -1476,9 +1547,9 @@ namespace //================================================================================ /*! - * \brief Return of a point relative to a segment + * \brief Return position of a point relative to a segment * \param point2D - the point to analyze position of - * \param xyVec - end points of segments + * \param segEnds - end points of segments * \param index0 - 0-based index of the first point of segment * \param posToFindOut - flags of positions to detect * \retval PointPos - point position @@ -1557,10 +1628,35 @@ double SMESH_MeshAlgos::GetDistance( const SMDS_MeshFace* face, const double badDistance = -1; if ( !face ) return badDistance; + int nbCorners = face->NbCornerNodes(); + if ( nbCorners > 3 ) + { + std::vector< const SMDS_MeshNode* > nodes; + int nbTria = SMESH_MeshAlgos::Triangulate().GetTriangles( face, nodes ); + + double minDist = Precision::Infinite(); + gp_XYZ cp; + for ( int i = 0; i < 3 * nbTria; i += 3 ) + { + SMDS_FaceOfNodes triangle( nodes[i], nodes[i+1], nodes[i+2] ); + double dist = GetDistance( &triangle, point, closestPnt ); + if ( dist < minDist ) + { + minDist = dist; + if ( closestPnt ) + cp = *closestPnt; + } + } + + if ( closestPnt ) + *closestPnt = cp; + return minDist; + } + // coordinates of nodes (medium nodes, if any, ignored) typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator; std::vector xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() ); - xyz.resize( face->NbCornerNodes()+1 ); + xyz.resize( 4 ); // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis, // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane. @@ -1584,7 +1680,7 @@ double SMESH_MeshAlgos::GetDistance( const SMDS_MeshFace* face, // move all the nodes to 2D std::vector xy( xyz.size() ); - for ( size_t i = 0;i < xyz.size()-1; ++i ) + for ( size_t i = 0; i < 3; ++i ) { gp_XYZ p3d = xyz[i]; trsf.Transforms( p3d ); @@ -1599,35 +1695,35 @@ double SMESH_MeshAlgos::GetDistance( const SMDS_MeshFace* face, gp_XY point2D( tmpPnt.X(), tmpPnt.Z() ); // loop on edges of the face to analyze point position ralative to the face - std::set< PointPos > pntPosSet; + std::vector< PointPos > pntPosByType[ POS_MAX + 1 ]; for ( size_t i = 1; i < xy.size(); ++i ) { PointPos pos = getPointPosition( point2D, &xy[0], i-1 ); - pntPosSet.insert( pos ); + pntPosByType[ pos._name ].push_back( pos ); } // compute distance - PointPos pos = *pntPosSet.begin(); - switch ( pos._name ) - { - case POS_LEFT: + + double dist = badDistance; + + if ( pntPosByType[ POS_LEFT ].size() > 0 ) // point is most close to an edge { - // point is most close to an edge + PointPos& pos = pntPosByType[ POS_LEFT ][0]; + gp_Vec edge( xyz[ pos._index ], xyz[ pos._index+1 ]); gp_Vec n1p ( xyz[ pos._index ], point ); double u = ( edge * n1p ) / edge.SquareMagnitude(); // param [0,1] on the edge - // projection of the point on the edge - gp_XYZ proj = xyz[ pos._index ] + u * edge.XYZ(); + gp_XYZ proj = xyz[ pos._index ] + u * edge.XYZ(); // projection on the edge + dist = point.Distance( proj ); if ( closestPnt ) *closestPnt = proj; - return point.Distance( proj ); } - case POS_RIGHT: + + else if ( pntPosByType[ POS_RIGHT ].size() >= 2 ) // point is inside the face { - // point is inside the face - double distToFacePlane = Abs( tmpPnt.Y() ); + dist = Abs( tmpPnt.Y() ); if ( closestPnt ) { - if ( distToFacePlane < std::numeric_limits::min() ) { + if ( dist < std::numeric_limits::min() ) { *closestPnt = point.XYZ(); } else { @@ -1636,17 +1732,26 @@ double SMESH_MeshAlgos::GetDistance( const SMDS_MeshFace* face, *closestPnt = tmpPnt; } } - return distToFacePlane; } - case POS_VERTEX: + + else if ( pntPosByType[ POS_VERTEX ].size() > 0 ) // point is most close to a node { - // point is most close to a node - gp_Vec distVec( point, xyz[ pos._index ]); - return distVec.Magnitude(); - } - default:; + double minDist2 = Precision::Infinite(); + for ( size_t i = 0; i < pntPosByType[ POS_VERTEX ].size(); ++i ) + { + PointPos& pos = pntPosByType[ POS_VERTEX ][i]; + + double d2 = point.SquareDistance( xyz[ pos._index ]); + if ( minDist2 > d2 ) + { + if ( closestPnt ) *closestPnt = xyz[ pos._index ]; + minDist2 = d2; + } + } + dist = Sqrt( minDist2 ); } - return badDistance; + + return dist; } //======================================================================= @@ -1723,7 +1828,7 @@ double SMESH_MeshAlgos::GetDistance( const SMDS_MeshVolume* volume, !vTool.GetFaceBaryCenter( iF, bc[0], bc[1], bc[2] )) continue; gp_XYZ bcp = point.XYZ() - gp_XYZ( bc[0], bc[1], bc[2] ); - if ( gp_XYZ( n[0], n[1], n[2] ) * bcp < 1e-6 ) + if ( gp_XYZ( n[0], n[1], n[2] ) * bcp < -1e-12 ) continue; // find distance to a facet @@ -1858,6 +1963,222 @@ SMESH_MeshAlgos::FindFaceInSet(const SMDS_MeshNode* n1, return face; } +//================================================================================ +/*! + * Return sharp edges of faces and non-manifold ones. Optionally adds existing edges. + */ +//================================================================================ + +std::vector< SMESH_MeshAlgos::Edge > +SMESH_MeshAlgos::FindSharpEdges( SMDS_Mesh* theMesh, + double theAngle, + bool theAddExisting ) +{ + std::vector< Edge > resultEdges; + if ( !theMesh ) return resultEdges; + + typedef std::pair< bool, const SMDS_MeshNode* > TIsSharpAndMedium; + typedef NCollection_DataMap< SMESH_TLink, TIsSharpAndMedium, SMESH_TLink > TLinkSharpMap; + + TLinkSharpMap linkIsSharp( theMesh->NbFaces() ); + TIsSharpAndMedium sharpMedium( true, 0 ); + bool & isSharp = sharpMedium.first; + const SMDS_MeshNode* & nMedium = sharpMedium.second; + + if ( theAddExisting ) + { + for ( SMDS_EdgeIteratorPtr edgeIt = theMesh->edgesIterator(); edgeIt->more(); ) + { + const SMDS_MeshElement* edge = edgeIt->next(); + nMedium = ( edge->IsQuadratic() ) ? edge->GetNode(2) : 0; + linkIsSharp.Bind( SMESH_TLink( edge->GetNode(0), edge->GetNode(1)), sharpMedium ); + } + } + + // check angles between face normals + + const double angleCos = Cos( theAngle * M_PI / 180. ), angleCos2 = angleCos * angleCos; + gp_XYZ norm1, norm2; + std::vector< const SMDS_MeshNode* > faceNodes, linkNodes(2); + std::vector linkFaces; + + int nbSharp = linkIsSharp.Extent(); + for ( SMDS_FaceIteratorPtr faceIt = theMesh->facesIterator(); faceIt->more(); ) + { + const SMDS_MeshElement* face = faceIt->next(); + size_t nbCorners = face->NbCornerNodes(); + + faceNodes.assign( face->begin_nodes(), face->end_nodes() ); + if ( faceNodes.size() == nbCorners ) + faceNodes.resize( nbCorners * 2, 0 ); + + const SMDS_MeshNode* nPrev = faceNodes[ nbCorners-1 ]; + for ( size_t i = 0; i < nbCorners; ++i ) + { + SMESH_TLink link( nPrev, faceNodes[i] ); + if ( !linkIsSharp.IsBound( link )) + { + linkNodes[0] = link.node1(); + linkNodes[1] = link.node2(); + linkFaces.clear(); + theMesh->GetElementsByNodes( linkNodes, linkFaces, SMDSAbs_Face ); + + isSharp = false; + if ( linkFaces.size() > 2 ) + { + isSharp = true; + } + else if ( linkFaces.size() == 2 && + FaceNormal( linkFaces[0], norm1, /*normalize=*/false ) && + FaceNormal( linkFaces[1], norm2, /*normalize=*/false )) + { + double dot = norm1 * norm2; // == cos * |norm1| * |norm2| + if (( dot < 0 ) == ( angleCos < 0 )) + { + double cos2 = dot * dot / norm1.SquareModulus() / norm2.SquareModulus(); + isSharp = ( angleCos < 0 ) ? ( cos2 > angleCos2 ) : ( cos2 < angleCos2 ); + } + else + { + isSharp = ( angleCos > 0 ); + } + } + nMedium = faceNodes[( i-1+nbCorners ) % nbCorners + nbCorners ]; + + linkIsSharp.Bind( link, sharpMedium ); + nbSharp += isSharp; + } + + nPrev = faceNodes[i]; + } + } + + resultEdges.resize( nbSharp ); + TLinkSharpMap::Iterator linkIsSharpIter( linkIsSharp ); + for ( int i = 0; linkIsSharpIter.More() && i < nbSharp; linkIsSharpIter.Next() ) + { + const SMESH_TLink& link = linkIsSharpIter.Key(); + const TIsSharpAndMedium& isSharpMedium = linkIsSharpIter.Value(); + if ( isSharpMedium.first ) + { + Edge & edge = resultEdges[ i++ ]; + edge._node1 = link.node1(); + edge._node2 = link.node2(); + edge._medium = isSharpMedium.second; + } + } + + return resultEdges; +} + +//================================================================================ +/*! + * Distribute all faces of the mesh between groups using given edges as group boundaries + */ +//================================================================================ + +std::vector< std::vector< const SMDS_MeshElement* > > +SMESH_MeshAlgos::SeparateFacesByEdges( SMDS_Mesh* theMesh, const std::vector< Edge >& theEdges ) +{ + std::vector< std::vector< const SMDS_MeshElement* > > groups; + if ( !theMesh ) return groups; + + // build map of face edges (SMESH_TLink) and their faces + + typedef std::vector< const SMDS_MeshElement* > TFaceVec; + typedef NCollection_DataMap< SMESH_TLink, TFaceVec, SMESH_TLink > TFacesByLinks; + TFacesByLinks facesByLink( theMesh->NbFaces() ); + + std::vector< const SMDS_MeshNode* > faceNodes; + for ( SMDS_FaceIteratorPtr faceIt = theMesh->facesIterator(); faceIt->more(); ) + { + const SMDS_MeshElement* face = faceIt->next(); + size_t nbCorners = face->NbCornerNodes(); + + faceNodes.assign( face->begin_nodes(), face->end_nodes() ); + faceNodes.resize( nbCorners + 1 ); + faceNodes[ nbCorners ] = faceNodes[0]; + + face->setIsMarked( false ); + + for ( size_t i = 0; i < nbCorners; ++i ) + { + SMESH_TLink link( faceNodes[i], faceNodes[i+1] ); + TFaceVec* linkFaces = facesByLink.ChangeSeek( link ); + if ( !linkFaces ) + { + linkFaces = facesByLink.Bound( link, TFaceVec() ); + linkFaces->reserve(2); + } + linkFaces->push_back( face ); + } + } + + // remove the given edges from facesByLink map + + for ( size_t i = 0; i < theEdges.size(); ++i ) + { + SMESH_TLink link( theEdges[i]._node1, theEdges[i]._node2 ); + facesByLink.UnBind( link ); + } + + // faces connected via links of facesByLink map form a group + + while ( !facesByLink.IsEmpty() ) + { + groups.push_back( TFaceVec() ); + TFaceVec & group = groups.back(); + + group.push_back( TFacesByLinks::Iterator( facesByLink ).Value()[0] ); + group.back()->setIsMarked( true ); + + for ( size_t iF = 0; iF < group.size(); ++iF ) + { + const SMDS_MeshElement* face = group[iF]; + size_t nbCorners = face->NbCornerNodes(); + faceNodes.assign( face->begin_nodes(), face->end_nodes() ); + faceNodes.resize( nbCorners + 1 ); + faceNodes[ nbCorners ] = faceNodes[0]; + + for ( size_t iN = 0; iN < nbCorners; ++iN ) + { + SMESH_TLink link( faceNodes[iN], faceNodes[iN+1] ); + if ( const TFaceVec* faces = facesByLink.Seek( link )) + { + const TFaceVec& faceNeighbors = *faces; + for ( size_t i = 0; i < faceNeighbors.size(); ++i ) + if ( !faceNeighbors[i]->isMarked() ) + { + group.push_back( faceNeighbors[i] ); + faceNeighbors[i]->setIsMarked( true ); + } + facesByLink.UnBind( link ); + } + } + } + } + + // find faces that are alone in its group; they were not in facesByLink + + int nbInGroups = 0; + for ( size_t i = 0; i < groups.size(); ++i ) + nbInGroups += groups[i].size(); + if ( nbInGroups < theMesh->NbFaces() ) + { + for ( SMDS_FaceIteratorPtr faceIt = theMesh->facesIterator(); faceIt->more(); ) + { + const SMDS_MeshElement* face = faceIt->next(); + if ( !face->isMarked() ) + { + groups.push_back( TFaceVec() ); + groups.back().push_back( face ); + } + } + } + + return groups; +} + //================================================================================ /*! * \brief Calculate normal of a mesh face @@ -1932,6 +2253,195 @@ bool SMESH_MeshAlgos::IsRightOrder( const SMDS_MeshElement* face, return ( diff == 1 ) || ( diff == -face->NbNodes()+1 ); } +//======================================================================= +/*! + * \brief Partition given 1D elements into groups of contiguous edges. + * A node where number of meeting edges != 2 is a group end. + * An optional startNode is used to orient groups it belongs to. + * \return a list of edge groups and a list of corresponding node groups. + * If a group is closed, the first and last nodes of the group are same. + */ +//======================================================================= + +void SMESH_MeshAlgos::Get1DBranches( SMDS_ElemIteratorPtr theEdgeIt, + TElemGroupVector& theEdgeGroups, + TNodeGroupVector& theNodeGroups, + const SMDS_MeshNode* theStartNode ) +{ + if ( !theEdgeIt ) + return; + + // build map of nodes and their adjacent edges + + typedef std::vector< const SMDS_MeshNode* > TNodeVec; + typedef std::vector< const SMDS_MeshElement* > TEdgeVec; + typedef NCollection_DataMap< const SMDS_MeshNode*, TEdgeVec, SMESH_Hasher > TEdgesByNodeMap; + TEdgesByNodeMap edgesByNode; + + while ( theEdgeIt->more() ) + { + const SMDS_MeshElement* edge = theEdgeIt->next(); + if ( edge->GetType() != SMDSAbs_Edge ) + continue; + + const SMDS_MeshNode* nodes[2] = { edge->GetNode(0), edge->GetNode(1) }; + for ( int i = 0; i < 2; ++i ) + { + TEdgeVec* nodeEdges = edgesByNode.ChangeSeek( nodes[i] ); + if ( !nodeEdges ) + { + nodeEdges = edgesByNode.Bound( nodes[i], TEdgeVec() ); + nodeEdges->reserve(2); + } + nodeEdges->push_back( edge ); + } + } + + if ( edgesByNode.IsEmpty() ) + return; + + + // build edge branches + + TElemGroupVector branches(2); + TNodeGroupVector nodeBranches(2); + + while ( !edgesByNode.IsEmpty() ) + { + if ( !theStartNode || !edgesByNode.IsBound( theStartNode )) + { + theStartNode = TEdgesByNodeMap::Iterator( edgesByNode ).Key(); + } + + size_t nbBranches = 0; + bool startIsBranchEnd = false; + + while ( edgesByNode.IsBound( theStartNode )) + { + // initialize a new branch + + ++nbBranches; + if ( branches.size() < nbBranches ) + { + branches.push_back ( TEdgeVec() ); + nodeBranches.push_back( TNodeVec() ); + } + TEdgeVec & branch = branches [ nbBranches - 1 ]; + TNodeVec & nodeBranch = nodeBranches[ nbBranches - 1 ]; + branch.clear(); + nodeBranch.clear(); + { + TEdgeVec& edges = edgesByNode( theStartNode ); + startIsBranchEnd = ( edges.size() != 2 ); + + int nbEdges = 0; + const SMDS_MeshElement* startEdge = 0; + for ( size_t i = 0; i < edges.size(); ++i ) + { + if ( !startEdge && edges[i] ) + { + startEdge = edges[i]; + edges[i] = 0; + } + nbEdges += bool( edges[i] ); + } + if ( nbEdges == 0 ) + edgesByNode.UnBind( theStartNode ); + if ( !startEdge ) + continue; + + branch.push_back( startEdge ); + + nodeBranch.push_back( theStartNode ); + nodeBranch.push_back( branch.back()->GetNode(0) ); + if ( nodeBranch.back() == theStartNode ) + nodeBranch.back() = branch.back()->GetNode(1); + } + + // fill the branch + + bool isBranchEnd = false; + TEdgeVec* edgesPtr; + + while (( !isBranchEnd ) && ( edgesPtr = edgesByNode.ChangeSeek( nodeBranch.back() ))) + { + TEdgeVec& edges = *edgesPtr; + + isBranchEnd = ( edges.size() != 2 ); + + const SMDS_MeshNode* lastNode = nodeBranch.back(); + + switch ( edges.size() ) + { + case 1: + edgesByNode.UnBind( lastNode ); + break; + + case 2: + { + if ( const SMDS_MeshElement* nextEdge = edges[ edges[0] == branch.back() ]) + { + branch.push_back( nextEdge ); + + const SMDS_MeshNode* nextNode = nextEdge->GetNode(0); + if ( nodeBranch.back() == nextNode ) + nextNode = nextEdge->GetNode(1); + nodeBranch.push_back( nextNode ); + } + edgesByNode.UnBind( lastNode ); + break; + } + + default: + int nbEdges = 0; + for ( size_t i = 0; i < edges.size(); ++i ) + { + if ( edges[i] == branch.back() ) + edges[i] = 0; + nbEdges += bool( edges[i] ); + } + if ( nbEdges == 0 ) + edgesByNode.UnBind( lastNode ); + } + } + } // while ( edgesByNode.IsBound( theStartNode )) + + + // put the found branches to the result + + if ( nbBranches == 2 && !startIsBranchEnd ) // join two branches starting at the same node + { + std::reverse( nodeBranches[0].begin(), nodeBranches[0].end() ); + nodeBranches[0].pop_back(); + nodeBranches[0].reserve( nodeBranches[0].size() + nodeBranches[1].size() ); + nodeBranches[0].insert( nodeBranches[0].end(), + nodeBranches[1].begin(), nodeBranches[1].end() ); + + std::reverse( branches[0].begin(), branches[0].end() ); + branches[0].reserve( branches[0].size() + branches[1].size() ); + branches[0].insert( branches[0].end(), branches[1].begin(), branches[1].end() ); + + nodeBranches[1].clear(); + branches[1].clear(); + } + + for ( size_t i = 0; i < nbBranches; ++i ) + { + if ( branches[i].empty() ) + continue; + + theEdgeGroups.push_back( TEdgeVec() ); + theEdgeGroups.back().swap( branches[i] ); + + theNodeGroups.push_back( TNodeVec() ); + theNodeGroups.back().swap( nodeBranches[i] ); + } + + } // while ( !edgesByNode.IsEmpty() ) + + return; +} + //======================================================================= /*! * \brief Return SMESH_NodeSearcher