+// ========================================================================
+namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
+{
+ const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
+ const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
+ const double NodeRadius = 1e-9; // to enlarge bnd box of element
+
+ //=======================================================================
+ /*!
+ * \brief Octal tree of bounding boxes of elements
+ */
+ //=======================================================================
+
+ class ElementBndBoxTree : public SMESH_Octree
+ {
+ public:
+
+ ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType);
+ void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems);
+ ~ElementBndBoxTree();
+
+ protected:
+ ElementBndBoxTree() {}
+ SMESH_Octree* allocateOctreeChild() const { return new ElementBndBoxTree; }
+ void buildChildrenData();
+ Bnd_B3d* buildRootBox();
+ private:
+ //!< Bounding box of element
+ struct ElementBox : public Bnd_B3d
+ {
+ const SMDS_MeshElement* _element;
+ int _refCount; // an ElementBox can be included in several tree branches
+ ElementBox(const SMDS_MeshElement* elem);
+ };
+ vector< ElementBox* > _elements;
+ };
+
+ //================================================================================
+ /*!
+ * \brief ElementBndBoxTree creation
+ */
+ //================================================================================
+
+ ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType)
+ :SMESH_Octree( new SMESH_Octree::Limit( MaxLevel, /*minSize=*/0. ))
+ {
+ int nbElems = mesh.GetMeshInfo().NbElements( elemType );
+ _elements.reserve( nbElems );
+
+ SMDS_ElemIteratorPtr elemIt = mesh.elementsIterator( elemType );
+ while ( elemIt->more() )
+ _elements.push_back( new ElementBox( elemIt->next() ));
+
+ if ( _elements.size() > MaxNbElemsInLeaf )
+ compute();
+ else
+ myIsLeaf = true;
+ }
+
+ //================================================================================
+ /*!
+ * \brief Destructor
+ */
+ //================================================================================
+
+ ElementBndBoxTree::~ElementBndBoxTree()
+ {
+ for ( int i = 0; i < _elements.size(); ++i )
+ if ( --_elements[i]->_refCount <= 0 )
+ delete _elements[i];
+ }
+
+ //================================================================================
+ /*!
+ * \brief Return the maximal box
+ */
+ //================================================================================
+
+ Bnd_B3d* ElementBndBoxTree::buildRootBox()
+ {
+ Bnd_B3d* box = new Bnd_B3d;
+ for ( int i = 0; i < _elements.size(); ++i )
+ box->Add( *_elements[i] );
+ return box;
+ }
+
+ //================================================================================
+ /*!
+ * \brief Redistrubute element boxes among children
+ */
+ //================================================================================
+
+ void ElementBndBoxTree::buildChildrenData()
+ {
+ for ( int i = 0; i < _elements.size(); ++i )
+ {
+ for (int j = 0; j < 8; j++)
+ {
+ if ( !_elements[i]->IsOut( myChildren[j]->getBox() ))
+ {
+ _elements[i]->_refCount++;
+ ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
+ }
+ }
+ _elements[i]->_refCount--;
+ }
+ _elements.clear();
+
+ for (int j = 0; j < 8; j++)
+ {
+ ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
+ if ( child->_elements.size() <= MaxNbElemsInLeaf )
+ child->myIsLeaf = true;
+
+ if ( child->_elements.capacity() - child->_elements.size() > 1000 )
+ child->_elements.resize( child->_elements.size() ); // compact
+ }
+ }
+
+ //================================================================================
+ /*!
+ * \brief Return elements which can include the point
+ */
+ //================================================================================
+
+ void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
+ TIDSortedElemSet& foundElems)
+ {
+ if ( level() && getBox().IsOut( point.XYZ() ))
+ return;
+
+ if ( isLeaf() )
+ {
+ for ( int i = 0; i < _elements.size(); ++i )
+ if ( !_elements[i]->IsOut( point.XYZ() ))
+ foundElems.insert( _elements[i]->_element );
+ }
+ else
+ {
+ for (int i = 0; i < 8; i++)
+ ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
+ }
+ }
+
+ //================================================================================
+ /*!
+ * \brief Construct the element box
+ */
+ //================================================================================
+
+ ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem)
+ {
+ _element = elem;
+ _refCount = 1;
+ SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
+ while ( nIt->more() )
+ Add( TNodeXYZ( cast2Node( nIt->next() )));
+ Enlarge( NodeRadius );
+ }
+
+} // namespace
+
+//=======================================================================
+/*!
+ * \brief Implementation of search for the elements by point
+ */
+//=======================================================================
+
+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) {}
+ ~SMESH_ElementSearcherImpl()
+ {
+ if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
+ if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
+ }
+
+ /*!
+ * \brief Return elements of given type where the given point is IN or ON.
+ *
+ * 'ALL' type means elements of any type excluding nodes and 0D elements
+ */
+ void FindElementsByPoint(const gp_Pnt& point,
+ SMDSAbs_ElementType type,
+ vector< const SMDS_MeshElement* >& foundElements)
+ {
+ foundElements.clear();
+
+ const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
+
+ // -----------------
+ // define tolerance
+ // -----------------
+ double tolerance = 0;
+ if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
+ {
+ double boxSize = _nodeSearcher->getTree()->maxSize();
+ tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
+ }
+ else if ( _ebbTree && meshInfo.NbElements() > 0 )
+ {
+ double boxSize = _ebbTree->maxSize();
+ tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
+ }
+ 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; // empty mesh
+
+ double elemSize;
+ if ( complexType == int( SMDSAbs_Node ))
+ {
+ SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
+ elemSize = 1;
+ if ( meshInfo.NbNodes() > 2 )
+ elemSize = TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
+ }
+ else
+ {
+ const SMDS_MeshElement* elem =
+ _mesh->elementsIterator( SMDSAbs_ElementType( complexType ))->next();
+ SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
+ TNodeXYZ n1( cast2Node( nodeIt->next() ));
+ while ( nodeIt->more() )
+ {
+ double dist = n1.Distance( cast2Node( nodeIt->next() ));
+ elemSize = max( dist, elemSize );
+ }
+ }
+ tolerance = 1e-6 * elemSize;
+ }
+
+ // =================================================================================
+ if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement )
+ {
+ if ( !_nodeSearcher )
+ _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
+
+ const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
+ if ( !closeNode ) return;
+
+ if ( point.Distance( TNodeXYZ( closeNode )) > tolerance )
+ return; // to far from any node
+
+ if ( type == SMDSAbs_Node )
+ {
+ 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 );
+ }
+ }
+}; // struct SMESH_ElementSearcherImpl
+
+//=======================================================================
+/*!
+ * \brief Return SMESH_ElementSearcher
+ */
+//=======================================================================
+
+SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
+{
+ return new SMESH_ElementSearcherImpl( *GetMeshDS() );
+}
+
+//=======================================================================
+/*!
+ * \brief Return true if the point is IN or ON of the element
+ */
+//=======================================================================
+
+bool SMESH_MeshEditor::isOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
+{
+ if ( element->GetType() == SMDSAbs_Volume)
+ {
+ return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
+ }
+
+ // get ordered nodes
+
+ vector< gp_XYZ > xyz;
+
+ SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
+ if ( element->IsQuadratic() )
+ if (const SMDS_QuadraticFaceOfNodes* f=dynamic_cast<const SMDS_QuadraticFaceOfNodes*>(element))
+ nodeIt = f->interlacedNodesElemIterator();
+ else if (const SMDS_QuadraticEdge* e =dynamic_cast<const SMDS_QuadraticEdge*>(element))
+ nodeIt = e->interlacedNodesElemIterator();
+
+ while ( nodeIt->more() )
+ xyz.push_back( TNodeXYZ( cast2Node( nodeIt->next() )));
+
+ int i, nbNodes = element->NbNodes();
+
+ if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
+ {
+ // compute face normal
+ gp_Vec faceNorm(0,0,0);
+ xyz.push_back( xyz.front() );
+ for ( i = 0; i < nbNodes; ++i )
+ {
+ gp_Vec edge1( xyz[i+1], xyz[i]);
+ gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
+ faceNorm += edge1 ^ edge2;
+ }
+ double normSize = faceNorm.Magnitude();
+ if ( normSize <= tol )
+ {
+ // degenerated face: point is out if it is out of all face edges
+ for ( i = 0; i < nbNodes; ++i )
+ {
+ SMDS_MeshNode n1( xyz[i].X(), xyz[i].Y(), xyz[i].Z() );
+ SMDS_MeshNode n2( xyz[i+1].X(), xyz[i+1].Y(), xyz[i+1].Z() );
+ SMDS_MeshEdge edge( &n1, &n2 );
+ if ( !isOut( &edge, point, tol ))
+ return false;
+ }
+ return true;
+ }
+ faceNorm /= normSize;
+
+ // check if the point lays on face plane
+ gp_Vec n2p( xyz[0], point );
+ if ( fabs( n2p * faceNorm ) > tol )
+ return true; // not on face plane
+
+ // check if point is out of face boundary:
+ // define it by closest transition of a ray point->infinity through face boundary
+ // on the face plane.
+ // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
+ // to find intersections of the ray with the boundary.
+ gp_Vec ray = n2p;
+ gp_Vec plnNorm = ray ^ faceNorm;
+ normSize = plnNorm.Magnitude();
+ if ( normSize <= tol ) return false; // point coincides with the first node
+ plnNorm /= normSize;
+ // for each node of the face, compute its signed distance to the plane
+ vector<double> dist( nbNodes + 1);
+ for ( i = 0; i < nbNodes; ++i )
+ {
+ gp_Vec n2p( xyz[i], point );
+ dist[i] = n2p * plnNorm;
+ }
+ dist.back() = dist.front();
+ // find the closest intersection
+ int iClosest = -1;
+ double rClosest, distClosest = 1e100;;
+ gp_Pnt pClosest;
+ for ( i = 0; i < nbNodes; ++i )
+ {
+ double r;
+ if ( dist[i] < tol )
+ r = 0.;
+ else if ( dist[i+1] < tol )
+ r = 1.;
+ else if ( dist[i] * dist[i+1] < 0 )
+ r = dist[i] / ( dist[i] - dist[i+1] );
+ else
+ continue; // no intersection
+ gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
+ gp_Vec p2int ( point, pInt);
+ if ( p2int * ray > -tol ) // right half-space
+ {
+ double intDist = p2int.SquareMagnitude();
+ if ( intDist < distClosest )
+ {
+ iClosest = i;
+ rClosest = r;
+ pClosest = pInt;
+ distClosest = intDist;
+ }
+ }
+ }
+ if ( iClosest < 0 )
+ return true; // no intesections - out
+
+ // analyse transition
+ gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
+ gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
+ gp_Vec p2int ( point, pClosest );
+ bool out = (edgeNorm * p2int) < -tol;
+ if ( rClosest > 0. && rClosest < 1. ) // not node intersection
+ return out;
+
+ // ray pass through a face node; analyze transition through an adjacent edge
+ gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
+ gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
+ gp_Vec edgeAdjacent( p1, p2 );
+ gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
+ bool out2 = (edgeNorm2 * p2int) < -tol;
+
+ bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
+ return covexCorner ? (out || out2) : (out && out2);
+ }
+ if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
+ {
+ // point is out of edge if it is NOT ON any straight part of edge
+ // (we consider quadratic edge as being composed of two straight parts)
+ for ( i = 1; i < nbNodes; ++i )
+ {
+ gp_Vec edge( xyz[i-1], xyz[i]);
+ gp_Vec n1p ( xyz[i-1], point);
+ double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
+ if ( dist > tol )
+ continue;
+ gp_Vec n2p( xyz[i], point );
+ if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
+ continue;
+ return false; // point is ON this part
+ }
+ return true;
+ }
+ // Node or 0D element -------------------------------------------------------------------------
+ {
+ gp_Vec n2p ( xyz[0], point );
+ return n2p.Magnitude() <= tol;
+ }
+ return true;
+}
+