X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FSMESH%2FSMESH_MeshEditor.cxx;h=af6147a88fe712c795d98eaddb150ce1608facca;hb=78364113e1c11a150f6273071a3f30a159ab3073;hp=9822c70eaf74f8d582d64161593867b492c958f9;hpb=2536cb0c1b75b8dccd92daf4d98323761c4d917a;p=modules%2Fsmesh.git diff --git a/src/SMESH/SMESH_MeshEditor.cxx b/src/SMESH/SMESH_MeshEditor.cxx index 9822c70ea..af6147a88 100644 --- a/src/SMESH/SMESH_MeshEditor.cxx +++ b/src/SMESH/SMESH_MeshEditor.cxx @@ -38,11 +38,12 @@ #include "SMESHDS_Group.hxx" #include "SMESHDS_Mesh.hxx" -#include "SMESH_subMesh.hxx" +#include "SMESH_Algo.hxx" #include "SMESH_ControlsDef.hxx" +#include "SMESH_Group.hxx" #include "SMESH_MesherHelper.hxx" #include "SMESH_OctreeNode.hxx" -#include "SMESH_Group.hxx" +#include "SMESH_subMesh.hxx" #include "utilities.h" @@ -51,11 +52,17 @@ #include #include #include +#include #include +#include #include +#include #include #include +#include #include +#include +#include #include #include #include @@ -75,6 +82,7 @@ #include #include #include + #include #include @@ -1102,6 +1110,7 @@ bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems, //function : BestSplit //purpose : Find better diagonal for cutting. //======================================================================= + int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad, SMESH::Controls::NumericalFunctorPtr theCrit) { @@ -1143,6 +1152,164 @@ int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad, return -1; } +namespace +{ + // Methods of splitting volumes into tetra + + const int theHexTo5[5*4] = + { + 0, 1, 5, 2, + 0, 4, 5, 7, + 0, 3, 7, 2, + 5, 6, 7, 2, + 0, 2, 5, 7 + }; + const int theHexTo6[6*4] = + { + 0, 1, 5, 2, + 0, 4, 5, 7, + 0, 3, 7, 2, + 5, 6, 7, 2, + 0, 2, 5, 7 + }; + const int thePyraTo2[2*4] = + { + 0, 1, 2, 4, + 0, 2, 3, 4 + }; + + const int thePentaTo8[8*4] = + { + 0, 1, 2, 6, + 3, 5, 4, 6, + 0, 3, 4, 6, + 0, 4, 1, 6, + 1, 4, 5, 6, + 1, 5, 2, 6, + 2, 5, 3, 6, + 2, 3, 0, 6 + }; + + struct TSplitMethod + { + int _nbTetra; + const int* _connectivity; + bool _addNode; // additional node is to be created + TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false) + : _nbTetra(nbTet), _connectivity(conn), _addNode(addNode) {} + }; + + /*! + * \brief return TSplitMethod for the given element + */ + TSplitMethod getSplitMethod( const SMDS_MeshElement* vol, const int theMethodFlags) + { + TSplitMethod method; + if ( vol->GetType() == SMDSAbs_Volume && !vol->IsPoly()) + switch ( vol->NbNodes() ) + { + case 8: + case 20: + if ( theMethodFlags & SMESH_MeshEditor::HEXA_TO_5 ) + method = TSplitMethod( 5, theHexTo5 ); + else + method = TSplitMethod( 6, theHexTo6 ); + break; + case 5: + case 13: + method = TSplitMethod( 2, thePyraTo2 ); + break; + case 6: + case 15: + method = TSplitMethod( 8, thePentaTo8, /*addNode=*/true ); + break; + default:; + } + return method; + } +} + +//======================================================================= +//function : SplitVolumesIntoTetra +//purpose : Split volumic elements into tetrahedra. +//======================================================================= + +// void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems, +// const int theMethodFlags) +// { +// // sdt-like iterator on coordinates of nodes of mesh element +// typedef SMDS_StdIterator< TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator; +// NXyzIterator xyzEnd; + +// SMESH_MesherHelper helper( *GetMesh()); + +// TIDSortedElemSet::const_iterator elem = theElems.begin(); +// for ( ; elem != theElems.end(); ++elem ) +// { +// SMDSAbs_EntityType geomType = (*elem)->GetEntityType(); +// if ( geomType <= SMDSEntity_Quad_Tetra ) +// continue; // tetra or face or edge + +// if ( (*elem)->IsQuadratic() ) +// { +// // add quadratic links to the helper +// SMDS_VolumeTool vol( *elem ); +// for ( int iF = 0; iF < vol.NbFaces(); ++iF ) +// { +// const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF ); +// for ( int iN = 0; iN < vol.NbFaceNodes( iF ); iN += 2) +// helper.AddTLinkNode( fNodes[iF], fNodes[iF+2], fNodes[iF+1] ); +// } +// helper.SetIsQuadratic( true ); +// } +// else +// { +// helper.SetIsQuadratic( false ); +// } + +// vector tetras; // splits of a volume + +// if ( geomType == SMDSEntity_Polyhedra ) +// { +// // Each face of a polyhedron is split into triangles and +// // each of triangles and a cell barycenter form a tetrahedron. + +// SMDS_VolumeTool vol( *elem ); + +// // make a node at barycenter +// gp_XYZ gc = std::accumulate( NXyzIterator((*elem)->nodesIterator()), xyzEnd,gp_XYZ(0,0,0)); +// gc /= vol.NbNodes(); +// SMDS_MeshNode* gcNode = GetMeshDS()->AddNode( gc.X(), gc.Y(), gc.Z() ); + +// for ( int iF = 0; iF < vol.NbFaces(); ++iF ) +// { +// const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF ); +// int nbFNodes = vol.NbFaceNodes( iF ); +// int nbTria = nbFNodes - 2; +// bool extFace = vol.IsFaceExternal( iF ); +// SMDS_MeshElement* tet; +// for ( int i = 0; i < nbTria; ++i ) +// { +// if ( extFace ) +// tet = helper.AddVolume( fNodes[0], fNodes[i+1], fNodes[i+2], gcNode ); +// else +// tet = helper.AddVolume( fNodes[0], fNodes[i+2], fNodes[i+1], gcNode ); +// tetras.push_back( tet ); +// } +// } + +// } +// else +// { + +// TSplitMethod splitMethod = getSplitMethod( *elem, theMethodFlags ); +// if ( splitMethod._nbTetra < 1 ) continue; + +// vector volNodes( (*elem)->begin_nodes(), (*elem)->end_nodes()); +// } +// } +// } + //======================================================================= //function : AddToSameGroups //purpose : add elemToAdd to the groups the elemInGroups belongs to @@ -4936,6 +5103,331 @@ SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems, return newGroupIDs; } + +//======================================================================= +//function : Scale +//purpose : +//======================================================================= + +SMESH_MeshEditor::PGroupIDs +SMESH_MeshEditor::Scale (TIDSortedElemSet & theElems, + const gp_Pnt& thePoint, + const std::list& theScaleFact, + const bool theCopy, + const bool theMakeGroups, + SMESH_Mesh* theTargetMesh) +{ + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + + SMESH_MeshEditor targetMeshEditor( theTargetMesh ); + SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0; + SMESHDS_Mesh* aMesh = GetMeshDS(); + + double scaleX=1.0, scaleY=1.0, scaleZ=1.0; + std::list::const_iterator itS = theScaleFact.begin(); + scaleX = (*itS); + if(theScaleFact.size()==1) { + scaleY = (*itS); + scaleZ= (*itS); + } + if(theScaleFact.size()==2) { + itS++; + scaleY = (*itS); + scaleZ= (*itS); + } + if(theScaleFact.size()>2) { + itS++; + scaleY = (*itS); + itS++; + scaleZ= (*itS); + } + + // map old node to new one + TNodeNodeMap nodeMap; + + // elements sharing moved nodes; those of them which have all + // nodes mirrored but are not in theElems are to be reversed + TIDSortedElemSet inverseElemSet; + + // source elements for each generated one + SMESH_SequenceOfElemPtr srcElems, srcNodes; + + // loop on theElems + TIDSortedElemSet::iterator itElem; + for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) { + const SMDS_MeshElement* elem = *itElem; + if ( !elem ) + continue; + + // loop on elem nodes + SMDS_ElemIteratorPtr itN = elem->nodesIterator(); + while ( itN->more() ) { + + // check if a node has been already transformed + const SMDS_MeshNode* node = cast2Node( itN->next() ); + pair n2n_isnew = + nodeMap.insert( make_pair ( node, node )); + if ( !n2n_isnew.second ) + continue; + + //double coord[3]; + //coord[0] = node->X(); + //coord[1] = node->Y(); + //coord[2] = node->Z(); + //theTrsf.Transforms( coord[0], coord[1], coord[2] ); + double dx = (node->X() - thePoint.X()) * scaleX; + double dy = (node->Y() - thePoint.Y()) * scaleY; + double dz = (node->Z() - thePoint.Z()) * scaleZ; + if ( theTargetMesh ) { + //const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] ); + const SMDS_MeshNode * newNode = + aTgtMesh->AddNode( thePoint.X()+dx, thePoint.Y()+dy, thePoint.Z()+dz ); + n2n_isnew.first->second = newNode; + myLastCreatedNodes.Append(newNode); + srcNodes.Append( node ); + } + else if ( theCopy ) { + //const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] ); + const SMDS_MeshNode * newNode = + aMesh->AddNode( thePoint.X()+dx, thePoint.Y()+dy, thePoint.Z()+dz ); + n2n_isnew.first->second = newNode; + myLastCreatedNodes.Append(newNode); + srcNodes.Append( node ); + } + else { + //aMesh->MoveNode( node, coord[0], coord[1], coord[2] ); + aMesh->MoveNode( node, thePoint.X()+dx, thePoint.Y()+dy, thePoint.Z()+dz ); + // node position on shape becomes invalid + const_cast< SMDS_MeshNode* > ( node )->SetPosition + ( SMDS_SpacePosition::originSpacePosition() ); + } + + // keep inverse elements + //if ( !theCopy && !theTargetMesh && needReverse ) { + // SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator(); + // while ( invElemIt->more() ) { + // const SMDS_MeshElement* iel = invElemIt->next(); + // inverseElemSet.insert( iel ); + // } + //} + } + } + + // either create new elements or reverse mirrored ones + //if ( !theCopy && !needReverse && !theTargetMesh ) + if ( !theCopy && !theTargetMesh ) + return PGroupIDs(); + + TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin(); + for ( ; invElemIt != inverseElemSet.end(); invElemIt++ ) + theElems.insert( *invElemIt ); + + // replicate or reverse elements + + enum { + REV_TETRA = 0, // = nbNodes - 4 + REV_PYRAMID = 1, // = nbNodes - 4 + REV_PENTA = 2, // = nbNodes - 4 + REV_FACE = 3, + REV_HEXA = 4, // = nbNodes - 4 + FORWARD = 5 + }; + int index[][8] = { + { 2, 1, 0, 3, 4, 0, 0, 0 }, // REV_TETRA + { 2, 1, 0, 3, 4, 0, 0, 0 }, // REV_PYRAMID + { 2, 1, 0, 5, 4, 3, 0, 0 }, // REV_PENTA + { 2, 1, 0, 3, 0, 0, 0, 0 }, // REV_FACE + { 2, 1, 0, 3, 6, 5, 4, 7 }, // REV_HEXA + { 0, 1, 2, 3, 4, 5, 6, 7 } // FORWARD + }; + + for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) + { + const SMDS_MeshElement* elem = *itElem; + if ( !elem || elem->GetType() == SMDSAbs_Node ) + continue; + + int nbNodes = elem->NbNodes(); + int elemType = elem->GetType(); + + if (elem->IsPoly()) { + // Polygon or Polyhedral Volume + switch ( elemType ) { + case SMDSAbs_Face: + { + vector poly_nodes (nbNodes); + int iNode = 0; + SMDS_ElemIteratorPtr itN = elem->nodesIterator(); + while (itN->more()) { + const SMDS_MeshNode* node = + static_cast(itN->next()); + TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node); + if (nodeMapIt == nodeMap.end()) + break; // not all nodes transformed + //if (needReverse) { + // // reverse mirrored faces and volumes + // poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second; + //} else { + poly_nodes[iNode] = (*nodeMapIt).second; + //} + iNode++; + } + if ( iNode != nbNodes ) + continue; // not all nodes transformed + + if ( theTargetMesh ) { + myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes)); + srcElems.Append( elem ); + } + else if ( theCopy ) { + myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes)); + srcElems.Append( elem ); + } + else { + aMesh->ChangePolygonNodes(elem, poly_nodes); + } + } + break; + case SMDSAbs_Volume: + { + // ATTENTION: Reversing is not yet done!!! + const SMDS_PolyhedralVolumeOfNodes* aPolyedre = + dynamic_cast( elem ); + if (!aPolyedre) { + MESSAGE("Warning: bad volumic element"); + continue; + } + + vector poly_nodes; + vector quantities; + + bool allTransformed = true; + int nbFaces = aPolyedre->NbFaces(); + for (int iface = 1; iface <= nbFaces && allTransformed; iface++) { + int nbFaceNodes = aPolyedre->NbFaceNodes(iface); + for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) { + const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode); + TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node); + if (nodeMapIt == nodeMap.end()) { + allTransformed = false; // not all nodes transformed + } else { + poly_nodes.push_back((*nodeMapIt).second); + } + } + quantities.push_back(nbFaceNodes); + } + if ( !allTransformed ) + continue; // not all nodes transformed + + if ( theTargetMesh ) { + myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities)); + srcElems.Append( elem ); + } + else if ( theCopy ) { + myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities)); + srcElems.Append( elem ); + } + else { + aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities); + } + } + break; + default:; + } + continue; + } + + // Regular elements + int* i = index[ FORWARD ]; + //if ( needReverse && nbNodes > 2) // reverse mirrored faces and volumes + // if ( elemType == SMDSAbs_Face ) + // i = index[ REV_FACE ]; + // else + // i = index[ nbNodes - 4 ]; + + if(elem->IsQuadratic()) { + static int anIds[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}; + i = anIds; + //if(needReverse) { + // if(nbNodes==3) { // quadratic edge + // static int anIds[] = {1,0,2}; + // i = anIds; + // } + // else if(nbNodes==6) { // quadratic triangle + // static int anIds[] = {0,2,1,5,4,3}; + // i = anIds; + // } + // else if(nbNodes==8) { // quadratic quadrangle + // static int anIds[] = {0,3,2,1,7,6,5,4}; + // i = anIds; + // } + // else if(nbNodes==10) { // quadratic tetrahedron of 10 nodes + // static int anIds[] = {0,2,1,3,6,5,4,7,9,8}; + // i = anIds; + // } + // else if(nbNodes==13) { // quadratic pyramid of 13 nodes + // static int anIds[] = {0,3,2,1,4,8,7,6,5,9,12,11,10}; + // i = anIds; + // } + // else if(nbNodes==15) { // quadratic pentahedron with 15 nodes + // static int anIds[] = {0,2,1,3,5,4,8,7,6,11,10,9,12,14,13}; + // i = anIds; + // } + // else { // nbNodes==20 - quadratic hexahedron with 20 nodes + // static int anIds[] = {0,3,2,1,4,7,6,5,11,10,9,8,15,14,13,12,16,19,18,17}; + // i = anIds; + // } + //} + } + + // find transformed nodes + vector nodes(nbNodes); + int iNode = 0; + SMDS_ElemIteratorPtr itN = elem->nodesIterator(); + while ( itN->more() ) { + const SMDS_MeshNode* node = + static_cast( itN->next() ); + TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node ); + if ( nodeMapIt == nodeMap.end() ) + break; // not all nodes transformed + nodes[ i [ iNode++ ]] = (*nodeMapIt).second; + } + if ( iNode != nbNodes ) + continue; // not all nodes transformed + + if ( theTargetMesh ) { + if ( SMDS_MeshElement* copy = + targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) { + myLastCreatedElems.Append( copy ); + srcElems.Append( elem ); + } + } + else if ( theCopy ) { + if ( SMDS_MeshElement* copy = AddElement( nodes, elem->GetType(), elem->IsPoly() )) { + myLastCreatedElems.Append( copy ); + srcElems.Append( elem ); + } + } + else { + // reverse element as it was reversed by transformation + if ( nbNodes > 2 ) + aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes ); + } + } + + PGroupIDs newGroupIDs; + + if ( theMakeGroups && theCopy || + theMakeGroups && theTargetMesh ) { + string groupPostfix = "scaled"; + newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh ); + } + + return newGroupIDs; +} + + //======================================================================= /*! * \brief Create groups of elements made during transformation @@ -5259,6 +5751,7 @@ namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint() ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType); void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems); + void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems); ~ElementBndBoxTree(); protected: @@ -5384,6 +5877,31 @@ namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint() } } + //================================================================================ + /*! + * \brief Return elements which can be intersected by the line + */ + //================================================================================ + + void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line, + TIDSortedElemSet& foundElems) + { + if ( level() && getBox().IsOut( line )) + return; + + if ( isLeaf() ) + { + for ( int i = 0; i < _elements.size(); ++i ) + if ( !_elements[i]->IsOut( line )) + foundElems.insert( _elements[i]->_element ); + } + else + { + for (int i = 0; i < 8; i++) + ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems ); + } + } + //================================================================================ /*! * \brief Construct the element box @@ -5404,60 +5922,95 @@ namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint() //======================================================================= /*! - * \brief Implementation of search for the elements by point + * \brief Implementation of search for the elements by point and + * of classification of point in 2D mesh */ //======================================================================= struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher { - SMESHDS_Mesh* _mesh; - ElementBndBoxTree* _ebbTree; - SMESH_NodeSearcherImpl* _nodeSearcher; - SMDSAbs_ElementType _elementType; - - SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh ): _mesh(&mesh),_ebbTree(0),_nodeSearcher(0) {} + SMESHDS_Mesh* _mesh; + ElementBndBoxTree* _ebbTree; + SMESH_NodeSearcherImpl* _nodeSearcher; + SMDSAbs_ElementType _elementType; + double _tolerance; + bool _outerFacesFound; + set _outerFaces; // empty means "no internal faces at all" + + SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh ) + : _mesh(&mesh),_ebbTree(0),_nodeSearcher(0), _tolerance(-1), _outerFacesFound(false) {} ~SMESH_ElementSearcherImpl() { if ( _ebbTree ) delete _ebbTree; _ebbTree = 0; if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0; } + virtual int FindElementsByPoint(const gp_Pnt& point, + SMDSAbs_ElementType type, + vector< const SMDS_MeshElement* >& foundElements); + virtual TopAbs_State GetPointState(const gp_Pnt& point); - /*! - * \brief Find elements of given type where the given point is IN or ON. - * Returns nb of found elements and elements them-selves. - * - * 'ALL' type means elements of any type excluding nodes and 0D elements - */ - int FindElementsByPoint(const gp_Pnt& point, - SMDSAbs_ElementType type, - vector< const SMDS_MeshElement* >& foundElements) + double getTolerance(); + bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face, + const double tolerance, double & param); + void findOuterBoundary(const SMDS_MeshElement* anyOuterFace); + bool isOuterBoundary(const SMDS_MeshElement* face) const + { + return _outerFaces.empty() || _outerFaces.count(face); + } + struct TInters //!< data of intersection of the line and the mesh face used in GetPointState() { - foundElements.clear(); + const SMDS_MeshElement* _face; + gp_Vec _faceNorm; + bool _coincides; //!< the line lays in face plane + TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false) + : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {} + }; + struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary()) + { + SMESH_TLink _link; + TIDSortedElemSet _faces; + TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face) + : _link( n1, n2 ), _faces( &face, &face + 1) {} + }; +}; + +ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i) +{ + return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0) + << ", _coincides="<GetMeshInfo(); - // ----------------- - // define tolerance - // ----------------- - double tolerance = 0; + _tolerance = 0; if ( _nodeSearcher && meshInfo.NbNodes() > 1 ) { double boxSize = _nodeSearcher->getTree()->maxSize(); - tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/; + _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/; } else if ( _ebbTree && meshInfo.NbElements() > 0 ) { double boxSize = _ebbTree->maxSize(); - tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/; + _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/; } - if ( tolerance == 0 ) + if ( _tolerance == 0 ) { // define tolerance by size of a most complex element int complexType = SMDSAbs_Volume; while ( complexType > SMDSAbs_All && meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 ) --complexType; - if ( complexType == SMDSAbs_All ) return foundElements.size(); // empty mesh + if ( complexType == SMDSAbs_All ) return 0; // empty mesh double elemSize; if ( complexType == int( SMDSAbs_Node )) @@ -5479,50 +6032,431 @@ struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher elemSize = max( dist, elemSize ); } } - tolerance = 1e-6 * elemSize; + _tolerance = 1e-6 * elemSize; + } + } + return _tolerance; +} + +//================================================================================ +/*! + * \brief Find intersection of the line and an edge of face and return parameter on line + */ +//================================================================================ + +bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line, + const SMDS_MeshElement* face, + const double tol, + double & param) +{ + int nbInts = 0; + param = 0; + + GeomAPI_ExtremaCurveCurve anExtCC; + Handle(Geom_Curve) lineCurve = new Geom_Line( line ); + + int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes(); + for ( int i = 0; i < nbNodes && nbInts < 2; ++i ) + { + GC_MakeSegment edge( SMESH_MeshEditor::TNodeXYZ( face->GetNode( i )), + SMESH_MeshEditor::TNodeXYZ( face->GetNode( (i+1)%nbNodes) )); + anExtCC.Init( lineCurve, edge); + if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol) + { + Quantity_Parameter pl, pe; + anExtCC.LowerDistanceParameters( pl, pe ); + param += pl; + if ( ++nbInts == 2 ) + break; + } + } + if ( nbInts > 0 ) param /= nbInts; + return nbInts > 0; +} +//================================================================================ +/*! + * \brief Find all faces belonging to the outer boundary of mesh + */ +//================================================================================ + +void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace) +{ + if ( _outerFacesFound ) return; + + // Collect all outer faces by passing from one outer face to another via their links + // and BTW find out if there are internal faces at all. + + // checked links and links where outer boundary meets internal one + set< SMESH_TLink > visitedLinks, seamLinks; + + // links to treat with already visited faces sharing them + list < TFaceLink > startLinks; + + // load startLinks with the first outerFace + startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace)); + _outerFaces.insert( outerFace ); + + TIDSortedElemSet emptySet; + while ( !startLinks.empty() ) + { + const SMESH_TLink& link = startLinks.front()._link; + TIDSortedElemSet& faces = startLinks.front()._faces; + + outerFace = *faces.begin(); + // find other faces sharing the link + const SMDS_MeshElement* f; + while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces ))) + faces.insert( f ); + + // select another outer face among the found + const SMDS_MeshElement* outerFace2 = 0; + if ( faces.size() == 2 ) + { + outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin()); + } + else if ( faces.size() > 2 ) + { + seamLinks.insert( link ); + + // link direction within the outerFace + gp_Vec n1n2( SMESH_MeshEditor::TNodeXYZ( link.node1()), + SMESH_MeshEditor::TNodeXYZ( link.node2())); + int i1 = outerFace->GetNodeIndex( link.node1() ); + int i2 = outerFace->GetNodeIndex( link.node2() ); + bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 ); + if ( rev ) n1n2.Reverse(); + // outerFace normal + gp_XYZ ofNorm, fNorm; + if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false )) + { + // direction from the link inside outerFace + gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2; + // sort all other faces by angle with the dirInOF + map< double, const SMDS_MeshElement* > angle2Face; + set< const SMDS_MeshElement* >::const_iterator face = faces.begin(); + for ( ; face != faces.end(); ++face ) + { + if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false )) + continue; + gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2; + double angle = dirInOF.AngleWithRef( dirInF, n1n2 ); + if ( angle < 0 ) angle += 2*PI; + angle2Face.insert( make_pair( angle, *face )); + } + if ( !angle2Face.empty() ) + outerFace2 = angle2Face.begin()->second; + } + } + // store the found outer face and add its links to continue seaching from + if ( outerFace2 ) + { + _outerFaces.insert( outerFace ); + int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 ); + for ( int i = 0; i < nbNodes; ++i ) + { + SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes)); + if ( visitedLinks.insert( link2 ).second ) + startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 )); + } } + startLinks.pop_front(); + } + _outerFacesFound = true; + + if ( !seamLinks.empty() ) + { + // There are internal boundaries touching the outher one, + // find all faces of internal boundaries in order to find + // faces of boundaries of holes, if any. + + } + else + { + _outerFaces.clear(); + } +} - // ================================================================================= - if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement ) +//======================================================================= +/*! + * \brief Find elements of given type where the given point is IN or ON. + * Returns nb of found elements and elements them-selves. + * + * 'ALL' type means elements of any type excluding nodes and 0D elements + */ +//======================================================================= + +int SMESH_ElementSearcherImpl:: +FindElementsByPoint(const gp_Pnt& point, + SMDSAbs_ElementType type, + vector< const SMDS_MeshElement* >& foundElements) +{ + foundElements.clear(); + + double tolerance = getTolerance(); + + // ================================================================================= + if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement ) + { + if ( !_nodeSearcher ) + _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh ); + + const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point ); + if ( !closeNode ) return foundElements.size(); + + if ( point.Distance( SMESH_MeshEditor::TNodeXYZ( closeNode )) > tolerance ) + return foundElements.size(); // to far from any node + + if ( type == SMDSAbs_Node ) { - if ( !_nodeSearcher ) - _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh ); + foundElements.push_back( closeNode ); + } + else + { + SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( SMDSAbs_0DElement ); + while ( elemIt->more() ) + foundElements.push_back( elemIt->next() ); + } + } + // ================================================================================= + else // elements more complex than 0D + { + if ( !_ebbTree || _elementType != type ) + { + if ( _ebbTree ) delete _ebbTree; + _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type ); + } + TIDSortedElemSet suspectElems; + _ebbTree->getElementsNearPoint( point, suspectElems ); + TIDSortedElemSet::iterator elem = suspectElems.begin(); + for ( ; elem != suspectElems.end(); ++elem ) + if ( !SMESH_MeshEditor::isOut( *elem, point, tolerance )) + foundElements.push_back( *elem ); + } + return foundElements.size(); +} + +//================================================================================ +/*! + * \brief Classify the given point in the closed 2D mesh + */ +//================================================================================ + +TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point) +{ + double tolerance = getTolerance(); + if ( !_ebbTree || _elementType != SMDSAbs_Face ) + { + if ( _ebbTree ) delete _ebbTree; + _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face ); + } + // Algo: analyse transition of a line starting at the point through mesh boundary; + // try three lines parallel to axis of the coordinate system and perform rough + // analysis. If solution is not clear perform thorough analysis. + + const int nbAxes = 3; + gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() }; + map< double, TInters > paramOnLine2TInters[ nbAxes ]; + list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line + multimap< int, int > nbInt2Axis; // to find the simplest case + for ( int axis = 0; axis < nbAxes; ++axis ) + { + gp_Ax1 lineAxis( point, axisDir[axis]); + gp_Lin line ( lineAxis ); - const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point ); - if ( !closeNode ) return foundElements.size(); + TIDSortedElemSet suspectFaces; // faces possibly intersecting the line + _ebbTree->getElementsNearLine( lineAxis, suspectFaces ); - if ( point.Distance( SMESH_MeshEditor::TNodeXYZ( closeNode )) > tolerance ) - return foundElements.size(); // to far from any node + // Intersect faces with the line - if ( type == SMDSAbs_Node ) + map< double, TInters > & u2inters = paramOnLine2TInters[ axis ]; + TIDSortedElemSet::iterator face = suspectFaces.begin(); + for ( ; face != suspectFaces.end(); ++face ) + { + // get face plane + gp_XYZ fNorm; + if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue; + gp_Pln facePlane( SMESH_MeshEditor::TNodeXYZ( (*face)->GetNode(0)), fNorm ); + + // perform intersection + IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane )); + if ( !intersection.IsDone() ) + continue; + if ( intersection.IsInQuadric() ) { - foundElements.push_back( closeNode ); + tangentInters[ axis ].push_back( TInters( *face, fNorm, true )); } - else + else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 ) { - SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( SMDSAbs_0DElement ); - while ( elemIt->more() ) - foundElements.push_back( elemIt->next() ); + gp_Pnt intersectionPoint = intersection.Point(1); + if ( !SMESH_MeshEditor::isOut( *face, intersectionPoint, tolerance )) + u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm ))); } } - // ================================================================================= - else // elements more complex than 0D + // Analyse intersections roughly + + int nbInter = u2inters.size(); + if ( nbInter == 0 ) + return TopAbs_OUT; + + double f = u2inters.begin()->first, l = u2inters.rbegin()->first; + if ( nbInter == 1 ) // not closed mesh + return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN; + + if ( fabs( f ) < tolerance || fabs( l ) < tolerance ) + return TopAbs_ON; + + if ( (f<0) == (l<0) ) + return TopAbs_OUT; + + int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0)); + int nbIntAfterPoint = nbInter - nbIntBeforePoint; + if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 ) + return TopAbs_IN; + + nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis )); + + if ( _outerFacesFound ) break; // pass to thorough analysis + + } // three attempts - loop on CS axes + + // Analyse intersections thoroughly. + // We make two loops maximum, on the first one we only exclude touching intersections, + // on the second, if situation is still unclear, we gather and use information on + // position of faces (internal or outer). If faces position is already gathered, + // we make the second loop right away. + + for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo ) + { + multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin(); + for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis ) { - if ( !_ebbTree || _elementType != type ) + int axis = nb_axis->second; + map< double, TInters > & u2inters = paramOnLine2TInters[ axis ]; + + gp_Ax1 lineAxis( point, axisDir[axis]); + gp_Lin line ( lineAxis ); + + // add tangent intersections to u2inters + double param; + list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin(); + for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt ) + if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param )) + u2inters.insert(make_pair( param, *tgtInt )); + tangentInters[ axis ].clear(); + + // Count intersections before and after the point excluding touching ones. + // If hasPositionInfo we count intersections of outer boundary only + + int nbIntBeforePoint = 0, nbIntAfterPoint = 0; + double f = numeric_limits::max(), l = -numeric_limits::max(); + map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1; + bool ok = ! u_int1->second._coincides; + while ( ok && u_int1 != u2inters.end() ) { - if ( _ebbTree ) delete _ebbTree; - _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type ); + double u = u_int1->first; + bool touchingInt = false; + if ( ++u_int2 != u2inters.end() ) + { + // skip intersections at the same point (if the line passes through edge or node) + int nbSamePnt = 0; + while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance ) + { + ++nbSamePnt; + ++u_int2; + } + + // skip tangent intersections + int nbTgt = 0; + const SMDS_MeshElement* prevFace = u_int1->second._face; + while ( ok && u_int2->second._coincides ) + { + if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() ) + ok = false; + else + { + nbTgt++; + u_int2++; + ok = ( u_int2 != u2inters.end() ); + } + } + if ( !ok ) break; + + // skip intersections at the same point after tangent intersections + if ( nbTgt > 0 ) + { + double u2 = u_int2->first; + ++u_int2; + while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance ) + { + ++nbSamePnt; + ++u_int2; + } + } + // decide if we skipped a touching intersection + if ( nbSamePnt + nbTgt > 0 ) + { + double minDot = numeric_limits::max(), maxDot = -numeric_limits::max(); + map< double, TInters >::iterator u_int = u_int1; + for ( ; u_int != u_int2; ++u_int ) + { + if ( u_int->second._coincides ) continue; + double dot = u_int->second._faceNorm * line.Direction(); + if ( dot > maxDot ) maxDot = dot; + if ( dot < minDot ) minDot = dot; + } + touchingInt = ( minDot*maxDot < 0 ); + } + } + if ( !touchingInt ) + { + if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face )) + { + if ( u < 0 ) + ++nbIntBeforePoint; + else + ++nbIntAfterPoint; + } + if ( u < f ) f = u; + if ( u > l ) l = u; + } + + u_int1 = u_int2; // to next intersection + + } // loop on intersections with one line + + if ( ok ) + { + if ( fabs( f ) < tolerance || fabs( l ) < tolerance ) + return TopAbs_ON; + + if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0) + return TopAbs_OUT; + + if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh + return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN; + + if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 ) + return TopAbs_IN; + + if ( (f<0) == (l<0) ) + return TopAbs_OUT; + + if ( hasPositionInfo ) + return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT; } - TIDSortedElemSet suspectElems; - _ebbTree->getElementsNearPoint( point, suspectElems ); - TIDSortedElemSet::iterator elem = suspectElems.begin(); - for ( ; elem != suspectElems.end(); ++elem ) - if ( !SMESH_MeshEditor::isOut( *elem, point, tolerance )) - foundElements.push_back( *elem ); + } // loop on intersections of the tree lines - thorough analysis + + if ( !hasPositionInfo ) + { + // gather info on faces position - is face in the outer boundary or not + map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ]; + findOuterBoundary( u2inters.begin()->second._face ); } - return foundElements.size(); - } -}; // struct SMESH_ElementSearcherImpl + + } // two attempts - with and w/o faces position info in the mesh + + return TopAbs_UNKNOWN; +} //======================================================================= /*!