1 // Copyright (C) 2007-2020 CEA/DEN, EDF R&D
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.
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.
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
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 // Author : Anthony Geay (CEA/DEN)
23 #include "CurveIntersector.hxx"
24 #include "InterpolationUtils.hxx"
25 #include "PointLocatorAlgos.txx"
29 namespace INTERP_KERNEL
31 template<class MyMeshType, class MyMatrix>
32 CurveIntersector<MyMeshType,MyMatrix>
33 ::CurveIntersector(const MyMeshType& meshT, const MyMeshType& meshS,
34 double precision, double tolerance, double medianLine, int printLevel):
37 _tolerance(tolerance),
38 _precision(precision),
39 _median_line(medianLine),
40 _print_level(printLevel)
42 if ( SPACEDIM != 1 && SPACEDIM != 2 )
43 throw Exception("CurveIntersector(): space dimension of mesh must be 1 or 2");
45 throw Exception("CurveIntersector(): mesh dimension must be 1");
47 _connectT = meshT.getConnectivityPtr();
48 _connectS = meshS.getConnectivityPtr();
49 _connIndexT = meshT.getConnectivityIndexPtr();
50 _connIndexS = meshS.getConnectivityIndexPtr();
51 _coordsT = meshT.getCoordinatesPtr();
52 _coordsS = meshS.getCoordinatesPtr();
55 template<class MyMeshType, class MyMatrix>
56 CurveIntersector<MyMeshType,MyMatrix>::~CurveIntersector()
60 //================================================================================
62 \brief creates the bounding boxes for all the cells of mesh \a mesh
64 \param mesh structure pointing to the mesh
65 \param bbox vector containing the bounding boxes
67 //================================================================================
69 template<class MyMeshType, class MyMatrix>
70 void CurveIntersector<MyMeshType,MyMatrix>::createBoundingBoxes (const MyMeshType& mesh,
71 std::vector<double>& bbox)
73 long nbelems = mesh.getNumberOfElements();
74 bbox.resize(2*SPACEDIM* nbelems);
75 const double* coords = mesh.getCoordinatesPtr();
76 const ConnType* conn = mesh.getConnectivityPtr();
77 const ConnType* conn_index = mesh.getConnectivityIndexPtr();
79 for(long icell=0; icell<nbelems; icell++)
81 ConnType nb_nodes_per_elem = conn_index[icell+1]-conn_index[icell];
82 //initializing bounding box limits
83 for(int idim=0; idim<SPACEDIM; idim++)
85 bbox[2*SPACEDIM*ibox+2*idim] = std::numeric_limits<double>::max();
86 bbox[2*SPACEDIM*ibox+2*idim+1] = -std::numeric_limits<double>::max();
88 //updating the bounding box with each node of the element
89 for (ConnType j=0; j<nb_nodes_per_elem; j++)
91 const double* coord_node = coords +
92 SPACEDIM*OTT<ConnType,numPol>
93 ::coo2C(conn[OTT<ConnType,numPol>::conn2C(conn_index[icell]+j)]);
94 for(int idim=0; idim<SPACEDIM; idim++)
96 double x = *(coord_node+idim);
97 bbox[ibox*2*SPACEDIM + 2*idim] =
98 ( bbox[ibox*2*SPACEDIM + 2*idim] < x ) ? bbox[ibox*2*SPACEDIM + 2*idim] : x;
99 bbox[ibox*2*SPACEDIM + 2*idim+1] =
100 ( bbox[ibox*2*SPACEDIM + 2*idim+1] > x ) ? bbox[ibox*2*SPACEDIM + 2*idim+1] : x;
108 Computes the bounding box of a given element. iP in numPol mode.
110 template<class MyMeshType, class MyMatrix>
111 void CurveIntersector<MyMeshType,MyMatrix>::getElemBB (double* bb,
112 const MyMeshType& mesh,
116 const double* coords = mesh.getCoordinatesPtr();
117 const ConnType* conn_index = mesh.getConnectivityIndexPtr();
118 const ConnType* conn = mesh.getConnectivityPtr();
119 //initializing bounding box limits
120 for(int idim=0; idim<SPACEDIM; idim++)
122 bb[2*idim ] = std::numeric_limits<double>::max();
123 bb[2*idim+1] = -std::numeric_limits<double>::max();
126 for (ConnType i=0; i<nb_nodes; i++)
128 //MN: iP= cell index, not node index, use of connectivity array ?
129 const double* coord_node = coords +
130 SPACEDIM*(OTT<ConnType,numPol>::coo2C(conn[OTT<ConnType,numPol>::conn2C(conn_index[OTT<ConnType,numPol>::ind2C(iP)]+i)]));
131 for(int idim=0; idim<SPACEDIM; idim++)
133 double x = *(coord_node+idim);
134 bb[2*idim ] = (x<bb[2*idim ]) ? x : bb[2*idim ];
135 bb[2*idim+1] = (x>bb[2*idim+1]) ? x : bb[2*idim+1];
141 * \param [in] startOfSeg - input coming from intersectSegments or intersectSegmentsInternal
142 * \param [in] endOfSeg - input coming from intersectSegments or intersectSegmentsInternal. NO Assume about sort
143 * \param [in] pt - position of point that the method computes the bary coords for.
145 template<class MyMeshType, class MyMatrix>
146 void CurveIntersector<MyMeshType,MyMatrix>::ComputeBaryCoordsOf(double startOfSeg, double endOfSeg, double pt, double& startPos, double& endPos)
148 double deno(endOfSeg-startOfSeg);
149 startPos = (endOfSeg-pt)/deno;
150 startPos = std::max(startPos,0.); startPos = std::min(startPos,1.);
154 /*! Readjusts a set of bounding boxes so that they are extended
155 in all dimensions for avoiding missing interesting intersections
157 \param bbox vector containing the bounding boxes
159 template<class MyMeshType, class MyMatrix>
160 void CurveIntersector<MyMeshType,MyMatrix>::adjustBoundingBoxes (std::vector<double>& bbox,
161 double adjustmentEpsAbs)
163 std::size_t size = bbox.size()/(2*SPACEDIM);
164 for (std::size_t i=0; i<size; i++)
166 for(int idim=0; idim<SPACEDIM; idim++)
168 bbox[i*2*SPACEDIM+2*idim ] -= adjustmentEpsAbs;
169 bbox[i*2*SPACEDIM+2*idim+1] += adjustmentEpsAbs;
175 * @param icellT id in target mesh in format of MyMeshType.
176 * @param coordsT output val that stores coordinates of the target cell
177 * automatically resized to the right length.
178 * @return true if segment is quadratic and in this case coordinates of medium node
179 * are placed in the middle of coordsT
181 template<class MyMeshType, class MyMatrix>
182 bool CurveIntersector<MyMeshType,MyMatrix>::getRealTargetCoordinates(ConnType icellT, std::vector<double>& coordsT) const
184 ConnType nbNodesT(_connIndexT[OTT<ConnType,numPol>::ind2C(icellT)+1] - _connIndexT[OTT<ConnType,numPol>::ind2C(icellT)]);
185 coordsT.resize(SPACEDIM*nbNodesT);
186 for (ConnType iT=0; iT<nbNodesT; iT++)
188 for(int idim=0; idim<SPACEDIM; idim++)
190 coordsT[SPACEDIM*iT+idim] =
191 _coordsT[SPACEDIM*OTT<ConnType,numPol>::coo2C(_connectT[OTT<ConnType,numPol>::conn2C(_connIndexT[OTT<ConnType,numPol>::ind2C(icellT)]+iT)])+idim];
196 for(int idim=0; idim<SPACEDIM; idim++)
197 std::swap( coordsT[SPACEDIM*1+idim], coordsT[SPACEDIM*2+idim]);
203 template<class MyMeshType, class MyMatrix>
204 typename MyMeshType::MyConnType CurveIntersector<MyMeshType,MyMatrix>::getNodeIdOfTargetCellAt(ConnType icellT, ConnType nodeIdInCellT) const
206 ConnType nbNodesT(_connIndexT[OTT<ConnType,numPol>::ind2C(icellT)+1] - _connIndexT[OTT<ConnType,numPol>::ind2C(icellT)]);
207 if(nodeIdInCellT>=0 && nodeIdInCellT<nbNodesT)
208 return OTT<ConnType,numPol>::coo2C(_connectT[OTT<ConnType,numPol>::conn2C(_connIndexT[OTT<ConnType,numPol>::ind2C(icellT)]+nodeIdInCellT)]);
210 throw Exception("getNodeIdOfTargetCellAt : error in nodeId in cell");
214 * @param icellS id in source mesh in format of MyMeshType.
215 * @param coordsS output val that stores coordinates of the source cell automatically resized to the right length.
216 * @return true if segment is quadratic and in this case coordinates of medium node
217 * are placed in the middle of coordsS
219 template<class MyMeshType, class MyMatrix>
220 bool CurveIntersector<MyMeshType,MyMatrix>::getRealSourceCoordinates(ConnType icellS, std::vector<double>& coordsS) const
222 ConnType nbNodesS = _connIndexS[OTT<ConnType,numPol>::ind2C(icellS)+1] - _connIndexS[OTT<ConnType,numPol>::ind2C(icellS)];
223 coordsS.resize(SPACEDIM*nbNodesS);
224 for(ConnType iS=0; iS<nbNodesS; iS++)
226 for(int idim=0; idim<SPACEDIM; idim++)
228 coordsS[SPACEDIM*iS+idim] =
229 _coordsS[SPACEDIM*OTT<ConnType,numPol>::coo2C(_connectS[OTT<ConnType,numPol>::conn2C(_connIndexS[OTT<ConnType,numPol>::ind2C(icellS)]+iS)])+idim];
234 for(int idim=0; idim<SPACEDIM; idim++)
235 std::swap( coordsS[SPACEDIM*1+idim], coordsS[SPACEDIM*2+idim]);
241 template<class MyMeshType, class MyMatrix>
242 typename MyMeshType::MyConnType CurveIntersector<MyMeshType,MyMatrix>::getNodeIdOfSourceCellAt(ConnType icellS, ConnType nodeIdInCellS) const
244 ConnType nbNodesS(_connIndexS[OTT<ConnType,numPol>::ind2C(icellS)+1] - _connIndexS[OTT<ConnType,numPol>::ind2C(icellS)]);
245 if(nodeIdInCellS>=0 && nodeIdInCellS<nbNodesS)
246 return OTT<ConnType,numPol>::coo2C(_connectS[OTT<ConnType,numPol>::conn2C(_connIndexS[OTT<ConnType,numPol>::ind2C(icellS)]+nodeIdInCellS)]);
248 throw Exception("getNodeIdOfSourceCellAt : error in nodeId in cell");
252 * \brief Return dual segments of given segment
253 * \param icell - given segment in C mode
255 * \param segments - dual segments
257 template<class MyMeshType, class MyMatrix>
258 void CurveIntersector<MyMeshType,MyMatrix>::getDualSegments(ConnType icell,
259 const MyMeshType& mesh,
260 std::vector<TDualSegment>& segments)
262 // get coordinates of cell nodes
264 std::vector<double> ncoords;
265 std::vector<ConnType> nodeIds;
267 const ConnType *connect = mesh.getConnectivityPtr();
268 const ConnType *connIndex = mesh.getConnectivityIndexPtr();
269 const double *coords = mesh.getCoordinatesPtr();
271 nbNodes = connIndex[icell+1] - connIndex[icell];
273 ncoords.resize(SPACEDIM*nbNodes);
274 nodeIds.resize(nbNodes);
276 for(ConnType i=0; i<nbNodes; i++)
277 for(int idim=0; idim<SPACEDIM; idim++)
279 nodeIds[i] = connect[OTT<ConnType,numPol>::conn2C(connIndex[OTT<ConnType,numPol>::ind2C(icell)]+i)];
280 ncoords[SPACEDIM*i+idim] = coords[SPACEDIM*OTT<ConnType,numPol>::coo2C(nodeIds[i])+idim];
282 if ( nbNodes > 2 ) // quadratic segment, put medium node in the middle
284 for(int idim=0; idim<SPACEDIM; idim++)
285 std::swap( ncoords[SPACEDIM*1+idim], ncoords[SPACEDIM*2+idim]);
286 std::swap( nodeIds[1], nodeIds[2] );
292 segments.reserve( 2*nbNodes );
293 for(ConnType i=0; i<nbNodes-1; i++)
295 segments.push_back(TDualSegment());
296 TDualSegment& seg1 = segments.back();
297 segments.push_back(TDualSegment());
298 TDualSegment& seg2 = segments.back();
300 seg1._nodeId = nodeIds[i];
301 seg2._nodeId = nodeIds[i+1];
303 seg1._coords.resize( SPACEDIM * 2 );
304 seg2._coords.resize( SPACEDIM * 2 );
306 for(int idim=0; idim<SPACEDIM; idim++)
308 double c1 = ncoords[SPACEDIM*i+idim];
309 double c2 = ncoords[SPACEDIM*(i+1)+idim];
310 double m = 0.5 * ( c1 + c2 );
311 seg1._coords[ idim ] = c1;
312 seg1._coords[ SPACEDIM + idim ] = m;
313 seg2._coords[ idim ] = m;
314 seg2._coords[ SPACEDIM + idim ] = c2;
319 template<class MyMeshType, class MyMatrix>
320 bool CurveIntersector<MyMeshType,MyMatrix>::projectionThis(const double *coordsT, const double *coordsS, double& xs0, double& xs1, double& xt) const
328 xs0 = coordsS[0]; xs1 = coordsS[1];
333 const double *s0(coordsS),*s1(coordsS + 2);
334 double s01[2] = { s1[X]-s0[X], s1[Y]-s0[Y] }; // src segment direction
335 double sSize = sqrt( s01[X]*s01[X] + s01[Y]*s01[Y] ); // src segment size
336 if( sSize < this->_precision )
338 s01[X] /= sSize; s01[Y] /= sSize; // normalize s01
339 double t[2] = { coordsT[X]-s0[X], coordsT[Y]-s0[Y] };
340 xs0 = 0. ; xs1 = sSize; xt = s01[X]*t[X] + s01[Y]*t[Y];
341 double proj_t_on_s[2] = { s0[X]+xt*s01[X], s0[Y]+xt*s01[Y] };
342 double dist_t_s_vect[2] = { coordsT[X]-proj_t_on_s[X], coordsT[Y]-proj_t_on_s[Y] };
343 double dist_t_s = sqrt( dist_t_s_vect[X]*dist_t_s_vect[X]+dist_t_s_vect[Y]*dist_t_s_vect[Y] );
344 return dist_t_s < this->_precision;
347 throw Exception("CurveIntersector::projectionThis : space dimension of mesh must be 1 or 2");
351 template<class MyMeshType, class MyMatrix>
352 bool CurveIntersector<MyMeshType,MyMatrix>::projectionThis(const double *coordsT, const double *coordsS,
353 double& xs0, double& xs1, double& xt0, double& xt1) const
355 xt0 = coordsT[0]; xt1 = coordsT[1];
356 xs0 = coordsS[0]; xs1 = coordsS[1];
363 // check if two segments overlap in 2D within tolerance
365 const double* t0 = coordsT;
366 const double* t1 = coordsT + 2;
367 double t01[2] = { t1[X]-t0[X], t1[Y]-t0[Y] }; // tgt segment direction
368 double tSize = sqrt( t01[X]*t01[X] + t01[Y]*t01[Y] ); // tgt segment size
369 if ( tSize < _precision )
370 return false; // degenerated segment
371 t01[X] /= tSize, t01[Y] /= tSize; // normalize t01
373 const double* s0 = coordsS;
374 const double* s1 = coordsS + 2;
375 double t0s0[2] = { s0[X]-t0[X], s0[Y]-t0[Y] };
376 double t0s1[2] = { s1[X]-t0[X], s1[Y]-t0[Y] };
377 double nt01_x_t0s0 = t0s0[X] * t01[Y] - t0s0[Y] * t01[X]; // t0s0 dot norm of t01
378 double nt01_x_t0s1 = t0s1[X] * t01[Y] - t0s1[Y] * t01[X]; // t0s1 dot norm of t01
379 double dist_ts0 = fabs( nt01_x_t0s0 ); // dist from tgt seg to s0
380 double dist_ts1 = fabs( nt01_x_t0s1 ); // dist from tgt seg to s1
381 bool s0_out_of_tol = ( dist_ts0 > _tolerance );
382 bool s1_out_of_tol = ( dist_ts1 > _tolerance );
383 if ( nt01_x_t0s0 * nt01_x_t0s1 > 0 && ( s0_out_of_tol || s1_out_of_tol ))
384 return false; // tgt segment is to far from src segment
386 double S0[2] = { s0[X], s0[Y] };
387 double S1[2] = { s1[X], s1[Y] };
388 if ( s0_out_of_tol ) // put s0 within tolerance
390 double t = _tolerance * nt01_x_t0s0 / dist_ts0; // signed tolerance
391 double r = ( nt01_x_t0s0 - t ) / ( nt01_x_t0s0 - nt01_x_t0s1 );
392 S0[X] = s0[X] * ( 1.-r ) + s1[X] * r;
393 S0[Y] = s0[Y] * ( 1.-r ) + s1[Y] * r;
395 if ( s1_out_of_tol ) // put s1 within tolerance
397 double t = _tolerance * nt01_x_t0s1 / dist_ts1; // signed tolerance
398 double r = ( nt01_x_t0s1 - t ) / ( nt01_x_t0s1 - nt01_x_t0s0 );
399 S1[X] = s1[X] * ( 1.-r ) + s0[X] * r;
400 S1[Y] = s1[Y] * ( 1.-r ) + s0[Y] * r;
403 // project tgt and src segments to median line
405 double s01[2] = { S1[X]-S0[X], S1[Y]-S0[Y] }; // src segment direction
406 double sSize = sqrt( s01[X]*s01[X] + s01[Y]*s01[Y] ); // src segment size
407 if ( sSize < _precision )
408 return false; // degenerated segment
409 s01[X] /= sSize, s01[Y] /= sSize; // normalize s01
411 // make t01 and s01 codirected
412 double t01_x_s01 = t01[X] * s01[X] + t01[Y] * s01[Y]; // t01 dot s01
414 s01[X] = -s01[X], s01[Y] = -s01[Y];
416 double medianDir[2] = {
417 t01[X] * ( 1.-_median_line) + s01[X] * _median_line,
418 t01[Y] * ( 1.-_median_line) + s01[Y] * _median_line
420 double medianSize = sqrt( medianDir[X]*medianDir[X] + medianDir[Y]*medianDir[Y] );
421 if ( medianSize < std::numeric_limits<double>::min() )
422 return false; // strange...
423 medianDir[X] /= medianSize, medianDir[Y] /= medianSize;
425 xt0 = t0[X] * medianDir[X] + t0[Y] * medianDir[Y];
426 xt1 = t1[X] * medianDir[X] + t1[Y] * medianDir[Y];
427 xs0 = S0[X] * medianDir[X] + S0[Y] * medianDir[Y];
428 xs1 = S1[X] * medianDir[X] + S1[Y] * medianDir[Y];
430 } // if ( SPACEDIM == 2 )
435 * \brief Return length of intersection of two segments
437 template<class MyMeshType, class MyMatrix>
438 double CurveIntersector<MyMeshType,MyMatrix>::intersectSegmentsInternal(const double *coordsT, const double *coordsS, double& xs0, double& xs1, double& xt0, double& xt1) const
440 if(!projectionThis(coordsT,coordsS,xs0,xs1,xt0,xt1))
443 if ( xt0 > xt1 ) std::swap( xt0, xt1 );
444 if ( xs0 > xs1 ) std::swap( xs0, xs1 );
446 double x0 = std::max( xt0, xs0 );
447 double x1 = std::min( xt1, xs1 );
448 return ( x0 < x1 ) ? ( x1 - x0 ) : 0.;
451 template<class MyMeshType>
452 class DummyMyMeshType1D
455 static const int MY_SPACEDIM=1;
456 static const int MY_MESHDIM=8;
457 typedef mcIdType MyConnType;
458 static const INTERP_KERNEL::NumberingPolicy My_numPol=MyMeshType::My_numPol;
460 // useless, but for windows compilation ...
461 const double *getCoordinatesPtr() const { return nullptr; }
462 const MyConnType *getConnectivityPtr() const { return nullptr; }
463 const MyConnType *getConnectivityIndexPtr() const { return nullptr; }
464 INTERP_KERNEL::NormalizedCellType getTypeOfElement(MyConnType) const { return (INTERP_KERNEL::NormalizedCellType)0; }
469 * This method determines if a target point ( \a coordsT ) is in source seg2 contained in \a coordsS. To do so _precision attribute is used.
470 * If target point is in, \a xs0, \a xs1 and \a xt are set to 1D referential for a further barycentric computation.
471 * This method deals with SPACEDIM == 2 (see projectionThis).
473 template<class MyMeshType, class MyMatrix>
474 bool CurveIntersector<MyMeshType,MyMatrix>::isPtIncludedInSeg(const double *coordsT, const double *coordsS, double& xs0, double& xs1, double& xt) const
476 if(!projectionThis(coordsT,coordsS,xs0,xs1,xt))
478 constexpr ConnType TAB[2]={0,1};
479 const double coordsS_1D[2]={xs0,xs1};
480 const double *coordsT_1D(&xt);
481 return PointLocatorAlgos<DummyMyMeshType1D<MyMeshType>>::isElementContainsPoint(coordsT_1D,NORM_SEG2,coordsS_1D,TAB,2,this->_precision);
485 * \brief Return length of intersection of two segments
487 template<class MyMeshType, class MyMatrix>
488 double CurveIntersector<MyMeshType,MyMatrix>::intersectSegments(const double *coordsT, const double *coordsS) const
490 double xs0,xs1,xt0,xt1;
491 return intersectSegmentsInternal(coordsT,coordsS,xs0,xs1,xt0,xt1);