Salome HOME
[EDF27988] : Implementation of MEDCouplingUMesh.explodeMeshTo for MEDFileUMesh.reduce...
[tools/medcoupling.git] / src / INTERP_KERNEL / PointLocatorAlgos.txx
1 // Copyright (C) 2007-2023  CEA, EDF
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 // Author : Anthony Geay (CEA/DEN)
20 #ifndef __POINTLOCATORALGOS_TXX__
21 #define __POINTLOCATORALGOS_TXX__
22
23 #include "InterpolationUtils.hxx"
24 #include "CellModel.hxx"
25 #include "BBTree.txx"
26
27 #include "InterpKernelGeo2DNode.hxx"
28 #include "InterpKernelGeo2DQuadraticPolygon.hxx"
29
30 #include <list>
31 #include <vector>
32 #include <set>
33 #include <limits>
34
35 namespace INTERP_KERNEL
36 {
37   class GenericPointLocatorAlgos
38   {
39   public:
40     virtual ~GenericPointLocatorAlgos() { }
41     virtual std::list<mcIdType> locates(const double* x, double eps) = 0;
42   };
43         
44   template<class MyMeshType>
45   class PointLocatorAlgos: public GenericPointLocatorAlgos
46   {
47   private : 
48     double* _bb;
49     BBTree<MyMeshType::MY_SPACEDIM,typename MyMeshType::MyConnType>* _tree;
50     const MyMeshType& _mesh;
51   public:
52     PointLocatorAlgos(const MyMeshType& mesh):_mesh(mesh)
53     {
54       typedef typename MyMeshType::MyConnType ConnType;
55       const int SPACEDIM=MyMeshType::MY_SPACEDIM;
56       const NumberingPolicy numPol=MyMeshType::My_numPol;
57       ConnType nelem = _mesh.getNumberOfElements();
58       _bb = new double[SPACEDIM*2*nelem];
59       const ConnType* conn = _mesh.getConnectivityPtr();
60       const ConnType* conn_index = _mesh.getConnectivityIndexPtr();
61       const double* coords=_mesh.getCoordinatesPtr();
62       for (ConnType i=0; i<nelem; i++)
63         {
64           for (int idim=0; idim<SPACEDIM; idim++)
65             {
66               _bb[2*(i*SPACEDIM+idim)]=std::numeric_limits<double>::max();
67               _bb[2*(i*SPACEDIM+idim)+1]=-std::numeric_limits<double>::max();
68             }
69           for (ConnType index= conn_index[i]; index < conn_index[i+1];index++)
70             {
71               //coordelem points to the coordinates of the current node of the i-th element
72               const double* coordelem = coords+OTT<ConnType,numPol>::ind2C(conn[OTT<ConnType,numPol>::ind2C(index)])*SPACEDIM;
73
74               //the bounding box is updated by checking wheher the node is at the min/max in exach dimension
75               for (int idim=0; idim<SPACEDIM;idim++)
76                 {
77                   _bb[2*(i*SPACEDIM+idim)]=(coordelem[idim]<_bb[2*(i*SPACEDIM+idim)])?coordelem[idim]:_bb[2*(i*SPACEDIM+idim)];
78                   _bb[2*(i*SPACEDIM+idim)+1]=(coordelem[idim]>_bb[2*(i*SPACEDIM+idim)+1])?coordelem[idim]:_bb[2*(i*SPACEDIM+idim)+1];
79                 }
80             }
81         }
82       _tree=new BBTree<SPACEDIM,typename MyMeshType::MyConnType>(_bb,0,0,nelem);
83     }
84
85     ~PointLocatorAlgos()
86     {
87       delete[] _bb;
88       delete _tree;
89     }
90         
91     //returns the list of elements that contains 
92     //the point pointed to by x
93     std::list<typename MyMeshType::MyConnType> locates(const double* x, double eps)
94     {
95       typedef typename MyMeshType::MyConnType ConnType;
96       const NumberingPolicy numPol=MyMeshType::My_numPol;
97       std::vector<ConnType> candidates;
98       _tree->getElementsAroundPoint(x,candidates);
99       std::list<ConnType> retlist;
100       for(unsigned int i=0; i< candidates.size(); i++)
101         {
102           ConnType ielem=candidates[i];
103           if (elementContainsPoint(ielem,x,eps))
104             retlist.push_back(OTT<ConnType,numPol>::indFC(ielem));
105         }
106       return retlist;
107     }
108
109     static bool isElementContainsPointAlg2DSimple(const double *ptToTest, const double *cellPts, mcIdType nbEdges, double eps)
110     {
111       /* with dimension 2, it suffices to check all the edges
112          and see if the sign of double products from the point
113          is always the same.
114                          C
115                         / \
116                        /   \
117              Xo       /     \ 
118                      A-------B
119        
120          here XA^XC and XC^XB have different signs*/
121       const int SPACEDIM=MyMeshType::MY_SPACEDIM;
122       int* sign = new int[nbEdges];
123       for (mcIdType iedge=0; iedge<nbEdges; iedge++)
124         {
125           const double* A=cellPts+SPACEDIM*iedge;
126           const double* B=cellPts+SPACEDIM*((iedge+1)%nbEdges);
127           double a=mon_determinant(ptToTest, A, B);
128           if(a<-eps)
129             sign[iedge]=-1;
130           else if(a>eps)
131             sign[iedge]=1;
132           else
133             sign[iedge]=0;
134         }
135       bool ret=decideFromSign(sign, nbEdges);
136       delete [] sign;
137       return ret;
138     }
139
140     // Same as isElementContainsPointAlg2DSimple() with a different input format ...
141     static bool isElementContainsPointAlgo2DSimple2(const double *ptToTest, NormalizedCellType type,
142                                                     const double *coords, const typename MyMeshType::MyConnType *conn_elem,
143                                                     typename MyMeshType::MyConnType conn_elem_sz, double eps)
144     {
145       const int SPACEDIM=MyMeshType::MY_SPACEDIM;
146       typedef typename MyMeshType::MyConnType ConnType;
147       const NumberingPolicy numPol=MyMeshType::My_numPol;
148
149       const CellModel& cmType=CellModel::GetCellModel(type);
150       bool ret=false;
151
152       int nbEdges=cmType.getNumberOfSons();
153       double *pts = new double[nbEdges*SPACEDIM];
154       for (int iedge=0; iedge<nbEdges; iedge++)
155         {
156           const double* a=coords+SPACEDIM*(OTT<ConnType,numPol>::ind2C(conn_elem[iedge]));
157           std::copy(a,a+SPACEDIM,pts+iedge*SPACEDIM);
158         }
159       ret=isElementContainsPointAlg2DSimple(ptToTest,pts,nbEdges,eps);
160       delete [] pts;
161       return ret;
162     }
163
164     static bool isElementContainsPointAlgo2DPolygon(const double *ptToTest, NormalizedCellType type,
165                                                     const double *coords, const typename MyMeshType::MyConnType *conn_elem,
166                                                     typename MyMeshType::MyConnType conn_elem_sz, double eps)
167     {
168       // Override precision for this method only:
169       INTERP_KERNEL::QuadraticPlanarPrecision prec(eps);
170
171       const int SPACEDIM=MyMeshType::MY_SPACEDIM;
172       typedef typename MyMeshType::MyConnType ConnType;
173       const NumberingPolicy numPol=MyMeshType::My_numPol;
174
175       std::vector<INTERP_KERNEL::Node *> nodes(conn_elem_sz);
176       INTERP_KERNEL::QuadraticPolygon *pol(0);
177       for(mcIdType j=0;j<conn_elem_sz;j++)
178         {
179           mcIdType nodeId(OTT<ConnType,numPol>::ind2C(conn_elem[j]));
180           nodes[j]=new INTERP_KERNEL::Node(coords[nodeId*SPACEDIM],coords[nodeId*SPACEDIM+1]);
181         }
182       if(!INTERP_KERNEL::CellModel::GetCellModel(type).isQuadratic())
183         pol=INTERP_KERNEL::QuadraticPolygon::BuildLinearPolygon(nodes);
184       else
185         pol=INTERP_KERNEL::QuadraticPolygon::BuildArcCirclePolygon(nodes);
186       INTERP_KERNEL::Node *n(new INTERP_KERNEL::Node(ptToTest[0],ptToTest[1]));
187       double a(0.),b(0.),c(0.);
188       a=pol->normalizeMe(b,c); n->applySimilarity(b,c,a);
189       bool ret=pol->isInOrOut2(n);
190       delete pol; n->decrRef();
191       return ret;
192     }
193
194     static bool isElementContainsPointAlg3D(const double *ptToTest, const typename MyMeshType::MyConnType *conn_elem, typename MyMeshType::MyConnType conn_elem_sz, const double *coords, const CellModel& cmType, double eps)
195     {
196       const int SPACEDIM=MyMeshType::MY_SPACEDIM;
197       typedef typename MyMeshType::MyConnType ConnType;
198       const NumberingPolicy numPol=MyMeshType::My_numPol;
199       
200       int nbfaces = cmType.getNumberOfSons2(conn_elem,conn_elem_sz);
201       int *sign = new int[nbfaces];
202       ConnType *connOfSon = new ConnType[conn_elem_sz];
203       for (int iface=0; iface<nbfaces; iface++)
204         {
205           NormalizedCellType typeOfSon;
206           cmType.fillSonCellNodalConnectivity2(iface,conn_elem,conn_elem_sz,connOfSon,typeOfSon);
207           const double* AA=coords+SPACEDIM*(OTT<ConnType,numPol>::coo2C(connOfSon[0]));
208           const double* BB=coords+SPACEDIM*(OTT<ConnType,numPol>::coo2C(connOfSon[1]));
209           const double* CC=coords+SPACEDIM*(OTT<ConnType,numPol>::coo2C(connOfSon[2]));                                                       
210           double Vol=triple_product(AA,BB,CC,ptToTest);
211           if (Vol<-eps)
212             sign[iface]=-1;
213           else if (Vol>eps)
214             sign[iface]=1;
215               else
216                 sign[iface]=0;
217         }
218       bool ret=decideFromSign(sign, nbfaces);
219       delete [] sign;
220       delete [] connOfSon;
221       return ret;
222     }
223
224     /*!
225      * Precondition : spacedim==meshdim. To be checked upstream to this call.
226      */
227     static bool isElementContainsPoint(const double *ptToTest, NormalizedCellType type, const double *coords,
228                                        const typename MyMeshType::MyConnType *conn_elem,
229                                        typename MyMeshType::MyConnType conn_elem_sz, double eps)
230     {
231       const int SPACEDIM=MyMeshType::MY_SPACEDIM;
232       typedef typename MyMeshType::MyConnType ConnType;
233       const NumberingPolicy numPol=MyMeshType::My_numPol;
234
235       const CellModel& cmType=CellModel::GetCellModel(type);
236       //
237       if (SPACEDIM==2)
238         {
239           if(type != INTERP_KERNEL::NORM_POLYGON && !cmType.isQuadratic())
240             return isElementContainsPointAlgo2DSimple2(ptToTest, type, coords, conn_elem, conn_elem_sz, eps);
241           else
242             return isElementContainsPointAlgo2DPolygon(ptToTest, type, coords, conn_elem, conn_elem_sz, eps);
243         }
244       if (SPACEDIM==3)
245         {
246           return isElementContainsPointAlg3D(ptToTest,conn_elem,conn_elem_sz,coords,cmType,eps);
247         }
248
249       if(SPACEDIM==1)
250         {
251           double p1=coords[(OTT<ConnType,numPol>::ind2C(conn_elem[0]))];
252           double p2=coords[(OTT<ConnType,numPol>::ind2C(conn_elem[1]))];
253           double delta=fabs(p1-p2)+eps;
254           double val=*ptToTest-std::min(p1,p2);
255           return val>-eps && val<delta;
256         }
257       throw INTERP_KERNEL::Exception("Invalid spacedim detected ! Managed spaceDim are 2 and 3 !");
258     }
259         
260     bool elementContainsPoint(typename MyMeshType::MyConnType i, const double* x, double eps)
261     {
262       //as i is extracted from the BBTRee, it is already in C numbering
263       //it is not necessary to convert it from F to C
264       typedef typename MyMeshType::MyConnType ConnType;
265       const NumberingPolicy numPol=MyMeshType::My_numPol;
266       
267       const double* coords= _mesh.getCoordinatesPtr();
268       const ConnType* conn=_mesh.getConnectivityPtr();
269       const ConnType* conn_index= _mesh.getConnectivityIndexPtr();
270       const ConnType* conn_elem=conn+OTT<ConnType,numPol>::ind2C(conn_index[i]);
271       int conn_elem_sz=conn_index[i+1]-conn_index[i];
272       NormalizedCellType type=_mesh.getTypeOfElement(OTT<ConnType,numPol>::indFC(i));
273       return isElementContainsPoint(x,type,coords,conn_elem,conn_elem_sz,eps);
274     }
275                 
276     static bool decideFromSign(const int* sign, mcIdType nbelem)
277     {
278       int min_sign = 1;
279       int max_sign = -1;
280       for (int i=0; i<nbelem;i++)
281         {
282           min_sign=(sign[i]<min_sign)?sign[i]:min_sign;
283           max_sign=(sign[i]>max_sign)?sign[i]:max_sign;
284         }
285       return (min_sign!=-1 || max_sign!=1);     
286     }
287   };
288
289   template<class MyMeshType>
290   class PointLocatorInSimplex : public PointLocatorAlgos<MyMeshType>
291   {
292     const MyMeshType& _mesh;
293   public:
294     PointLocatorInSimplex(const MyMeshType& mesh)
295       :PointLocatorAlgos<MyMeshType>(mesh),_mesh(mesh)
296     {
297     }
298
299     //================================================================================
300     /*!
301      * \brief Returns nodes composing the simplex the point x is in
302      */
303     //================================================================================
304
305     virtual std::list<typename MyMeshType::MyConnType> locates(const double* x, double eps)
306     {
307       typedef typename MyMeshType::MyConnType ConnType;
308       const NumberingPolicy numPol=MyMeshType::My_numPol;
309
310       std::list<ConnType> simplexNodes;
311       std::list<ConnType> candidates = PointLocatorAlgos<MyMeshType>::locates(x,eps);
312       typename std::list<ConnType>::iterator eIt = candidates.begin();
313       for ( ; eIt != candidates.end(); ++eIt )
314         {
315           const ConnType i = OTT<ConnType,numPol>::ind2C( *eIt );
316           const double* coords= _mesh.getCoordinatesPtr();
317           const ConnType* conn=_mesh.getConnectivityPtr();
318           const ConnType* conn_index= _mesh.getConnectivityIndexPtr();
319           const ConnType* conn_elem=conn+OTT<ConnType,numPol>::ind2C(conn_index[i]);
320           int conn_elem_sz=conn_index[i+1]-conn_index[i];
321           NormalizedCellType type=_mesh.getTypeOfElement(OTT<ConnType,numPol>::indFC(i));
322           CellModel cell = CellModel::GetCellModel(type);
323
324           if ( cell.isQuadratic() )
325             throw Exception("P2 not implemented yet");
326
327           if ( cell.isSimplex())
328             {
329               for ( int n = 0; n < conn_elem_sz; ++n )
330                 simplexNodes.push_back( conn_elem[ n ]);
331             }
332           else
333             {
334               NormalizedCellType simlexType = cell.getDimension()==3 ? NORM_TETRA4 : NORM_TRI3;
335               std::vector<mcIdType> sonNodes;
336               NormalizedCellType sonType;
337               const unsigned nbSons = cell.getNumberOfSons2( conn_elem, conn_elem_sz );
338               for ( unsigned s = 0; s < nbSons; ++s )
339                 {
340                   sonNodes.resize( cell.getNumberOfNodesConstituentTheSon2( s, conn_elem, conn_elem_sz ));
341                   cell.fillSonCellNodalConnectivity2( s, conn_elem, conn_elem_sz, &sonNodes[0], sonType );
342                   std::set<mcIdType> sonNodesSet( sonNodes.begin(), sonNodes.end() );
343
344                   std::set< std::set< ConnType > > checkedSonSimplex;
345                   for ( unsigned sn = 0; sn < sonNodes.size(); ++sn )
346                     {
347                       std::vector< ConnType > simplexConn( cell.getDimension() + 1 );
348                       unsigned n;
349                       for ( n = 0; n < cell.getDimension()-1; ++n )
350                         simplexConn[n] = sonNodes[ (sn+n) % sonNodes.size() ];
351
352                       for ( unsigned n2 = 0; n2 < sonNodes.size()-cell.getDimension()+1; ++n2 )
353                         {
354                           simplexConn[n] = sonNodes[ (sn+n+n2) % sonNodes.size() ];
355                           std::set< ConnType > sonSimplex( simplexConn.begin(), --simplexConn.end());
356                           if ( checkedSonSimplex.insert( sonSimplex ).second )
357                             {
358                               for ( unsigned cn = 0; cn < conn_elem_sz; ++cn )
359                                 if ( !sonNodesSet.count( conn_elem[cn] ))
360                                   {
361                                     simplexConn.back() = conn_elem[cn];
362                                     if ( this->isElementContainsPoint( x, simlexType, coords,
363                                                                        &simplexConn[0], simplexConn.size(), eps ))
364                                       {
365                                         simplexNodes.insert( simplexNodes.end(),
366                                                              simplexConn.begin(), simplexConn.end());
367                                         return simplexNodes;
368                                       }
369                                   }
370                             }
371                         }
372                     }
373                 }
374             }
375         }
376       return simplexNodes;
377     }
378
379   };
380 }
381
382 #endif