1 /**
2  *   SFCGAL
3  *
4  *   Copyright (C) 2012-2013 Oslandia <infos@oslandia.com>
5  *   Copyright (C) 2012-2013 IGN (http://www.ign.fr)
6  *
7  *   This library is free software; you can redistribute it and/or
8  *   modify it under the terms of the GNU Library General Public
9  *   License as published by the Free Software Foundation; either
10  *   version 2 of the License, or (at your option) any later version.
11  *
12  *   This library is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *   Library General Public License for more details.
16 
17  *   You should have received a copy of the GNU Library General Public
18  *   License along with this library; if not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <map>
22 #include <sstream>
23 
24 #include <SFCGAL/Kernel.h>
25 #include <SFCGAL/algorithm/intersects.h>
26 #include <SFCGAL/algorithm/intersection.h>
27 #include <SFCGAL/algorithm/connection.h>
28 #include <SFCGAL/algorithm/covers.h>
29 #include <SFCGAL/algorithm/isValid.h>
30 #include <SFCGAL/detail/triangulate/triangulateInGeometrySet.h>
31 #include <SFCGAL/detail/GeometrySet.h>
32 #include <SFCGAL/Envelope.h>
33 #include <SFCGAL/Exception.h>
34 #include <SFCGAL/LineString.h>
35 #include <SFCGAL/TriangulatedSurface.h>
36 #include <SFCGAL/PolyhedralSurface.h>
37 
38 #include <CGAL/box_intersection_d.h>
39 
40 #include <CGAL/Side_of_triangle_mesh.h>
41 
42 using namespace SFCGAL::detail;
43 
44 namespace SFCGAL {
45 namespace algorithm {
46 //
47 // Type of pa must be of larger dimension than type of pb
_intersects(const PrimitiveHandle<2> & pa,const PrimitiveHandle<2> & pb)48 bool _intersects( const PrimitiveHandle<2>& pa, const PrimitiveHandle<2>& pb )
49 {
50     //
51     // Point vs. Point
52     //
53 
54     if ( pa.handle.which() == PrimitivePoint && pb.handle.which() == PrimitivePoint ) {
55         return *boost::get<const CGAL::Point_2<Kernel>* >( pa.handle )
56                == *boost::get<const CGAL::Point_2<Kernel>* >( pb.handle );
57     }
58 
59     //
60     // Segment vs. Point
61     //
62 
63     else if ( pa.handle.which() == PrimitiveSegment && pb.handle.which() == PrimitivePoint ) {
64         const CGAL::Segment_2<Kernel>* seg = pa.as<CGAL::Segment_2<Kernel> >();
65         const CGAL::Point_2<Kernel>* pt = pb.as<CGAL::Point_2<Kernel> >();
66         return seg->has_on( *pt );
67     }
68 
69     //
70     // Segment vs. Segment
71     //
72 
73     else if ( pa.handle.which() == PrimitiveSegment && pb.handle.which() == PrimitiveSegment ) {
74         const CGAL::Segment_2<Kernel>* seg1 = pa.as<CGAL::Segment_2<Kernel> >();
75         const CGAL::Segment_2<Kernel>* seg2 = pb.as<CGAL::Segment_2<Kernel> >();
76         return CGAL::do_intersect( *seg1, *seg2 );
77     }
78 
79     //
80     // Polygon vs. Point
81     //
82 
83     else if ( pa.handle.which() == PrimitiveSurface && pb.handle.which() == PrimitivePoint ) {
84         // Polygon versus Point
85         const CGAL::Polygon_with_holes_2<Kernel>* poly = pa.as<CGAL::Polygon_with_holes_2<Kernel> >();
86         const CGAL::Point_2<Kernel>* pt = pb.as<CGAL::Point_2<Kernel> >();
87 
88         int b1 = poly->outer_boundary().bounded_side( *pt );
89 
90         if ( b1 == CGAL::ON_BOUNDARY ) {
91             return true;
92         }
93 
94         if ( b1 == CGAL::ON_BOUNDED_SIDE ) {
95             CGAL::Polygon_with_holes_2<Kernel>::Hole_const_iterator it;
96 
97             for ( it = poly->holes_begin(); it != poly->holes_end(); ++it ) {
98                 int b = it->bounded_side( *pt );
99 
100                 if ( b == CGAL::ON_BOUNDED_SIDE ) {
101                     return false;
102                 }
103             }
104         }
105         else {
106             return false;
107         }
108 
109         return true;
110     }
111 
112     //
113     // Polygon vs. Segment
114     //
115 
116     else if ( pa.handle.which() == PrimitiveSurface && pb.handle.which() == PrimitiveSegment ) {
117         const CGAL::Polygon_with_holes_2<Kernel>* poly = pa.as<CGAL::Polygon_with_holes_2<Kernel> >();
118         const CGAL::Segment_2<Kernel>* seg = pb.as<CGAL::Segment_2<Kernel> >();
119 
120         // 1. if the segment intersects a boundary of the polygon, returns true
121         // 2. else, if one of the point of the segment intersects the polygon, returns true
122 
123         GeometrySet<2> segment;
124         segment.addSegments( seg, seg+1 );
125 
126         // 1.
127         GeometrySet<2> boundary;
128         boundary.addSegments( poly->outer_boundary().edges_begin(),
129                               poly->outer_boundary().edges_end() );
130 
131         // recurse call
132         if ( intersects( boundary, segment ) ) {
133             return true;
134         }
135 
136         for ( CGAL::Polygon_with_holes_2<Kernel>::Hole_const_iterator it = poly->holes_begin();
137                 it != poly->holes_end();
138                 ++it ) {
139             GeometrySet<2> hole;
140             hole.addSegments( it->edges_begin(), it->edges_end() );
141 
142             // recurse call
143             if ( intersects( hole, segment ) ) {
144                 return true;
145             }
146         }
147 
148         // 2. call the polygon, point version
149         CGAL::Point_2<Kernel> pt = seg->source();
150         PrimitiveHandle<2> ppoly( poly );
151         PrimitiveHandle<2> ppt( &pt );
152         return intersects( ppoly, ppt );
153     }
154 
155     //
156     // Polygon vs. Polygon
157     //
158 
159     else if ( pa.handle.which() == PrimitiveSurface && pb.handle.which() == PrimitiveSurface ) {
160         const CGAL::Polygon_with_holes_2<Kernel>* poly1 = pa.as<CGAL::Polygon_with_holes_2<Kernel> >();
161         const CGAL::Polygon_with_holes_2<Kernel>* poly2 = pb.as<CGAL::Polygon_with_holes_2<Kernel> >();
162 
163         // 1. if rings intersects, returns true
164         // 2. else, if poly1 is inside poly2 or poly1 inside poly2 (but not in holes), returns true
165 
166         GeometrySet<2> rings1, rings2;
167         rings1.addSegments( poly1->outer_boundary().edges_begin(),
168                             poly1->outer_boundary().edges_end() );
169 
170         for ( CGAL::Polygon_with_holes_2<Kernel>::Hole_const_iterator it = poly1->holes_begin();
171                 it != poly1->holes_end();
172                 ++it ) {
173             rings1.addSegments( it->edges_begin(), it->edges_end() );
174         }
175 
176         rings2.addSegments( poly2->outer_boundary().edges_begin(),
177                             poly2->outer_boundary().edges_end() );
178 
179         for ( CGAL::Polygon_with_holes_2<Kernel>::Hole_const_iterator it = poly2->holes_begin();
180                 it != poly2->holes_end();
181                 ++it ) {
182             rings2.addSegments( it->edges_begin(), it->edges_end() );
183         }
184 
185         // 1.
186         if ( intersects( rings1, rings2 ) ) {
187             return true;
188         }
189 
190         // 2.
191         CGAL::Bbox_2 box1, box2;
192         box1 = poly1->bbox();
193         box2 = poly2->bbox();
194         Envelope e1( box1.xmin(), box1.xmax(), box1.ymin(), box1.ymax() );
195         Envelope e2( box2.xmin(), box2.xmax(), box2.ymin(), box2.ymax() );
196 
197         // if pa is inside pb
198         if ( Envelope::contains( e2, e1 ) ) {
199             // is pa inside one of pb's holes ?
200             CGAL::Point_2<Kernel> pt = *poly1->outer_boundary().vertices_begin();
201 
202             for ( CGAL::Polygon_with_holes_2<Kernel>::Hole_const_iterator it = poly2->holes_begin();
203                     it != poly2->holes_end();
204                     ++it ) {
205                 CGAL::Bounded_side b2 = it->bounded_side( pt );
206 
207                 if ( b2 == CGAL::ON_BOUNDED_SIDE ) {
208                     return false;
209                 }
210             }
211 
212             return true;
213         }
214 
215         // if pb is inside pa
216         if ( Envelope::contains( e1, e2 ) ) {
217             // is pa inside one of pb's holes ?
218             CGAL::Point_2<Kernel> pt = *poly2->outer_boundary().vertices_begin();
219 
220             for ( CGAL::Polygon_with_holes_2<Kernel>::Hole_const_iterator it = poly1->holes_begin();
221                     it != poly1->holes_end();
222                     ++it ) {
223                 CGAL::Bounded_side b2 = it->bounded_side( pt );
224 
225                 if ( b2 == CGAL::ON_BOUNDED_SIDE ) {
226                     return false;
227                 }
228             }
229 
230             return true;
231         }
232 
233         return false;
234     }
235 
236     return false;
237 }
238 
239 
240 //
241 // intersects of a volume with any other type
242 struct intersects_volume_x : public boost::static_visitor<bool> {
243     const MarkedPolyhedron* polyhedron;
244 
intersects_volume_xSFCGAL::algorithm::intersects_volume_x245     intersects_volume_x( const MarkedPolyhedron* vol ) : polyhedron( vol ) {}
246 
247     template <class T>
operator ()SFCGAL::algorithm::intersects_volume_x248     bool operator() ( const T* geometry ) const {
249         // intersection between a solid and a geometry
250         // 1. either one of the geometry' point lies inside the solid
251         // 2. or the geometry intersects one of the surfaces
252 
253         // 1.
254 
255         if ( polyhedron->is_closed() ) {
256             // this test is needed only if its a volume
257             // if the polyhedron is not closed, this is not a volume, actually
258 
259             CGAL::Side_of_triangle_mesh<MarkedPolyhedron, Kernel> is_in_poly( *polyhedron );
260 
261             GeometrySet<3> points;
262             points.collectPoints( geometry );
263 
264             for ( GeometrySet<3>::PointCollection::const_iterator pit = points.points().begin();
265                     pit != points.points().end(); ++pit ) {
266                 if ( is_in_poly( pit->primitive() ) != CGAL::ON_UNBOUNDED_SIDE ) {
267                     return true;
268                 }
269             }
270         }
271 
272         // 2.
273 
274         // triangulate the polyhedron_3
275         GeometrySet<3> g;
276         g.addPrimitive( *geometry );
277 
278         GeometrySet<3> triangles;
279         triangulate::triangulate( *polyhedron, triangles );
280 
281         return intersects( g, triangles );
282     }
283 };
284 
285 //
286 // Type of pa must be of larger dimension than type of pb
_intersects(const PrimitiveHandle<3> & pa,const PrimitiveHandle<3> & pb)287 bool _intersects( const PrimitiveHandle<3>& pa, const PrimitiveHandle<3>& pb )
288 {
289     if ( pa.handle.which() == PrimitivePoint && pb.handle.which() == PrimitivePoint ) {
290         return *boost::get<const CGAL::Point_3<Kernel>* >( pa.handle )
291                == *boost::get<const CGAL::Point_3<Kernel>* >( pb.handle );
292     }
293     else if ( pa.handle.which() == PrimitiveSegment && pb.handle.which() == PrimitivePoint ) {
294         const CGAL::Segment_3<Kernel>* seg = pa.as<CGAL::Segment_3<Kernel> >();
295         const CGAL::Point_3<Kernel>* pt = pb.as<CGAL::Point_3<Kernel> >();
296         return seg->has_on( *pt );
297     }
298     else if ( pa.handle.which() == PrimitiveSegment && pb.handle.which() == PrimitiveSegment ) {
299         const CGAL::Segment_3<Kernel>* sega = pa.as<CGAL::Segment_3<Kernel> >();
300         const CGAL::Segment_3<Kernel>* segb = pb.as<CGAL::Segment_3<Kernel> >();
301         return CGAL::do_intersect( *sega, *segb );
302     }
303 
304     if ( pa.handle.which() == PrimitiveVolume ) {
305         intersects_volume_x visitor( pa.as<MarkedPolyhedron >() );
306         return boost::apply_visitor( visitor, pb.handle );
307     }
308 
309     if ( pa.handle.which() == PrimitiveSurface && pb.handle.which() == PrimitivePoint ) {
310         const CGAL::Triangle_3<Kernel>* tri = pa.as<CGAL::Triangle_3<Kernel> >();
311         const CGAL::Point_3<Kernel>* pt = pb.as<CGAL::Point_3<Kernel> >();
312         return tri->has_on( *pt );
313     }
314 
315     if ( pa.handle.which() == PrimitiveSurface && pb.handle.which() == PrimitiveSegment ) {
316         const CGAL::Triangle_3<Kernel>* tri = pa.as<CGAL::Triangle_3<Kernel> >();
317         const CGAL::Segment_3<Kernel>* seg = pb.as<CGAL::Segment_3<Kernel> >();
318         return CGAL::do_intersect( *tri, *seg );
319     }
320 
321     if ( pa.handle.which() == PrimitiveSurface && pb.handle.which() == PrimitiveSurface ) {
322         const CGAL::Triangle_3<Kernel>* tri1 = pa.as<CGAL::Triangle_3<Kernel> >();
323         const CGAL::Triangle_3<Kernel>* tri2 = pb.as<CGAL::Triangle_3<Kernel> >();
324         return CGAL::do_intersect( *tri1, *tri2 );
325     }
326 
327     return false;
328 }
329 
330 //
331 // We deal here with symmetric call
332 template <int Dim>
dispatch_intersects_sym(const PrimitiveHandle<Dim> & pa,const PrimitiveHandle<Dim> & pb)333 bool dispatch_intersects_sym( const PrimitiveHandle<Dim>& pa, const PrimitiveHandle<Dim>& pb )
334 {
335     // assume types are ordered by dimension within the boost::variant
336     if ( pa.handle.which() >= pb.handle.which() ) {
337         return _intersects( pa, pb );
338     }
339     else {
340         return _intersects( pb, pa );
341     }
342 }
343 
344 template <int Dim>
intersects(const PrimitiveHandle<Dim> & pa,const PrimitiveHandle<Dim> & pb)345 bool intersects( const PrimitiveHandle<Dim>& pa, const PrimitiveHandle<Dim>& pb )
346 {
347     return dispatch_intersects_sym( pa, pb );
348 }
349 
350 struct found_an_intersection {};
351 
352 template <int Dim>
353 struct intersects_cb {
operator ()SFCGAL::algorithm::intersects_cb354     void operator()( const typename PrimitiveBox<Dim>::Type& a,
355                      const typename PrimitiveBox<Dim>::Type& b ) {
356         if ( dispatch_intersects_sym( *a.handle(), *b.handle() ) ) {
357             throw found_an_intersection();
358         }
359     }
360 };
361 
362 template <int Dim>
intersects(const GeometrySet<Dim> & a,const GeometrySet<Dim> & b)363 bool intersects( const GeometrySet<Dim>& a, const GeometrySet<Dim>& b )
364 {
365     typename SFCGAL::detail::HandleCollection<Dim>::Type ahandles, bhandles;
366     typename SFCGAL::detail::BoxCollection<Dim>::Type aboxes, bboxes;
367     a.computeBoundingBoxes( ahandles, aboxes );
368     b.computeBoundingBoxes( bhandles, bboxes );
369 
370     try {
371         intersects_cb<Dim> cb;
372         CGAL::box_intersection_d( aboxes.begin(), aboxes.end(),
373                                   bboxes.begin(), bboxes.end(),
374                                   cb );
375     }
376     catch ( found_an_intersection& e ) {
377         return true;
378     }
379 
380     return false;
381 }
382 
383 template bool intersects<2>( const GeometrySet<2>& a, const GeometrySet<2>& b );
384 template bool intersects<3>( const GeometrySet<3>& a, const GeometrySet<3>& b );
385 
386 template bool intersects<2>( const PrimitiveHandle<2>& a, const PrimitiveHandle<2>& b );
387 template bool intersects<3>( const PrimitiveHandle<3>& a, const PrimitiveHandle<3>& b );
388 
intersects(const Geometry & ga,const Geometry & gb)389 bool intersects( const Geometry& ga, const Geometry& gb )
390 {
391     SFCGAL_ASSERT_GEOMETRY_VALIDITY_2D( ga );
392     SFCGAL_ASSERT_GEOMETRY_VALIDITY_2D( gb );
393 
394     GeometrySet<2> gsa( ga );
395     GeometrySet<2> gsb( gb );
396 
397     return intersects( gsa, gsb );
398 }
399 
intersects3D(const Geometry & ga,const Geometry & gb)400 bool intersects3D( const Geometry& ga, const Geometry& gb )
401 {
402     SFCGAL_ASSERT_GEOMETRY_VALIDITY_3D( ga );
403     SFCGAL_ASSERT_GEOMETRY_VALIDITY_3D( gb );
404 
405     GeometrySet<3> gsa( ga );
406     GeometrySet<3> gsb( gb );
407 
408     return intersects( gsa, gsb );
409 }
410 
intersects(const Geometry & ga,const Geometry & gb,NoValidityCheck)411 bool intersects( const Geometry& ga, const Geometry& gb, NoValidityCheck )
412 {
413     GeometrySet<2> gsa( ga );
414     GeometrySet<2> gsb( gb );
415 
416     return intersects( gsa, gsb );
417 }
418 
intersects3D(const Geometry & ga,const Geometry & gb,NoValidityCheck)419 bool intersects3D( const Geometry& ga, const Geometry& gb, NoValidityCheck )
420 {
421     GeometrySet<3> gsa( ga );
422     GeometrySet<3> gsb( gb );
423 
424     return intersects( gsa, gsb );
425 }
426 
427 template< int Dim >
selfIntersectsImpl(const LineString & line)428 bool selfIntersectsImpl( const LineString& line )
429 {
430 
431     if ( line.numSegments() < 2 ) {
432         return false;    // one segment cannot intersect
433     }
434 
435     // note: zero length segments are a pain, to avoid algorithm complexity
436     // we start by filtering them out
437     const size_t numPoints = line.numPoints();
438     LineString l;
439 
440     for ( size_t i = 0; i != numPoints; ++i ) {
441         if ( i==0 || l.endPoint() != line.pointN( i ) ) {
442             l.addPoint( line.pointN( i ) );
443         }
444     }
445 
446     const size_t numSegments = l.numSegments();
447 
448     // test any two pairs of segments
449     for ( size_t i = 0; i != numSegments; ++i ) {
450         // first line segment is point i and i+1
451         for ( size_t j = i + 1; j < numSegments; ++j ) {
452             /** @todo find a way to avoid ugly copy/paste here, toPoint_d< Dim > can be used,
453              * but I dont know what to do with Kernel::Segment_Dim and Kernel::Point_Dim
454              */
455             std::unique_ptr< Geometry > inter; // null if no intersection
456 
457             if ( Dim == 2 ) {
458                 const CGAL::Segment_2< Kernel > s1( l.pointN( i ).toPoint_2(), l.pointN( i + 1 ).toPoint_2() ) ;
459                 const CGAL::Segment_2< Kernel > s2( l.pointN( j ).toPoint_2(), l.pointN( j + 1 ).toPoint_2() ) ;
460                 const CGAL::Object out = CGAL::intersection( s1, s2 );
461 
462                 if ( out.is< Kernel::Point_2 >() ) {
463                     inter.reset( new Point( CGAL::object_cast< Kernel::Point_2 >( out ) ) );
464                 }
465                 else if ( out.is< Kernel::Segment_2 >() ) {
466                     const Kernel::Segment_2& s = CGAL::object_cast< Kernel::Segment_2 >( out );
467                     inter.reset( new LineString( s.point( 0 ), s.point( 1 ) ) ) ;
468                 }
469             }
470             else {
471                 const CGAL::Segment_3< Kernel > s1( l.pointN( i ).toPoint_3(), l.pointN( i + 1 ).toPoint_3() ) ;
472                 const CGAL::Segment_3< Kernel > s2( l.pointN( j ).toPoint_3(), l.pointN( j + 1 ).toPoint_3() ) ;
473                 const CGAL::Object out = CGAL::intersection( s1, s2 );
474 
475                 if ( out.is< Kernel::Point_3 >() ) {
476                     inter.reset( new Point( CGAL::object_cast< Kernel::Point_3 >( out ) ) );
477                 }
478                 else if ( out.is< Kernel::Segment_3 >() ) {
479                     const Kernel::Segment_3& s = CGAL::object_cast< Kernel::Segment_3 >( out );
480                     inter.reset( new LineString( s.point( 0 ), s.point( 1 ) ) ) ;
481                 }
482             }
483 
484             if ( inter.get() && inter->is< LineString >() ) {
485                 return true;    // segments overlap
486             }
487             else if ( inter.get() && inter->is< Point >()
488                       && !( i + 1 == j ) // one contact point between consecutive segments is ok
489                       && !( ( i == 0 )
490                             && ( j + 1 == numSegments )
491                             && inter->as< Point >() == l.startPoint()
492                             && inter->as< Point >() == l.endPoint() ) ) {
493                 return true;    // contact point that is not a contact between startPoint and endPoint
494             }
495         }
496     }
497 
498     return false;
499 }
500 
selfIntersects(const LineString & l)501 bool selfIntersects( const LineString& l )
502 {
503     return selfIntersectsImpl<2>( l );
504 }
selfIntersects3D(const LineString & l)505 bool selfIntersects3D( const LineString& l )
506 {
507     return selfIntersectsImpl<3>( l );
508 }
509 
510 
511 template< int Dim >
selfIntersectsImpl(const PolyhedralSurface & s,const SurfaceGraph & graph)512 bool selfIntersectsImpl( const PolyhedralSurface& s, const SurfaceGraph& graph )
513 {
514     size_t numPolygons = s.numPolygons();
515 
516     for ( size_t pi=0; pi != numPolygons; ++pi ) {
517         for ( size_t pj=pi+1; pj < numPolygons; ++pj ) {
518             std::unique_ptr< Geometry > inter = Dim == 3
519                                               ? intersection3D( s.polygonN( pi ), s.polygonN( pj ) )
520                                               : intersection( s.polygonN( pi ), s.polygonN( pj ) ) ;
521 
522             if ( !inter->isEmpty() ) {
523                 // two cases:
524                 // - neighbors can have a line as intersection
525                 // - non neighbors can only have a point or a set of points
526                 typedef SurfaceGraph::FaceGraph::adjacency_iterator Iterator;
527                 std::pair< Iterator, Iterator > neighbors = boost::adjacent_vertices( pi, graph.faceGraph() );
528 
529                 if ( neighbors.second != std::find( neighbors.first, neighbors.second, pj ) ) {
530                     // neighbor
531                     //std::cerr << pi << " " << pj << " neighbor\n";
532                     if ( !inter->is< LineString >() ) {
533                         return true;
534                     }
535                 }
536                 else {
537                     // not a neighbor
538                     //std::cerr << pi << " " << pj << " not neighbor\n";
539                     if ( inter->dimension() != 0 ) {
540                         return true;
541                     }
542                 }
543             }
544         }
545     }
546 
547     return false;
548 }
549 
selfIntersects(const PolyhedralSurface & s,const SurfaceGraph & g)550 bool selfIntersects( const PolyhedralSurface& s, const SurfaceGraph& g )
551 {
552     return selfIntersectsImpl<2>( s, g );
553 }
554 
selfIntersects3D(const PolyhedralSurface & s,const SurfaceGraph & g)555 bool selfIntersects3D( const PolyhedralSurface& s, const SurfaceGraph& g )
556 {
557     return selfIntersectsImpl<3>( s, g );
558 }
559 
560 template< int Dim >
selfIntersectsImpl(const TriangulatedSurface & tin,const SurfaceGraph & graph)561 bool selfIntersectsImpl( const TriangulatedSurface& tin, const SurfaceGraph& graph )
562 {
563     size_t numTriangles = tin.numTriangles();
564 
565     for ( size_t ti=0; ti != numTriangles; ++ti ) {
566         for ( size_t tj=ti+1; tj < numTriangles; ++tj ) {
567             std::unique_ptr< Geometry > inter = Dim == 3
568                                               ? intersection3D( tin.triangleN( ti ), tin.triangleN( tj ) )
569                                               : intersection( tin.triangleN( ti ), tin.triangleN( tj ) ) ;
570 
571             if ( !inter->isEmpty() ) {
572                 // two cases:
573                 // - neighbors can have a line as intersection
574                 // - non neighbors can only have a point or a set of points
575                 typedef SurfaceGraph::FaceGraph::adjacency_iterator Iterator;
576                 std::pair< Iterator, Iterator > neighbors = boost::adjacent_vertices( ti, graph.faceGraph() );
577 
578                 if ( neighbors.second != std::find( neighbors.first, neighbors.second, tj ) ) {
579                     // neighbor
580                     //std::cerr << ti << " " << tj << " neighbor\n";
581                     if ( !inter->is< LineString >() ) {
582                         return true;
583                     }
584                 }
585                 else {
586                     // not a neighbor
587                     //std::cerr << ti << " " << tj << " not neighbor\n";
588                     if ( inter->dimension() != 0 ) {
589                         return true;
590                     }
591                 }
592             }
593         }
594     }
595 
596     return false;
597 }
598 
selfIntersects(const TriangulatedSurface & tin,const SurfaceGraph & g)599 bool selfIntersects( const TriangulatedSurface& tin, const SurfaceGraph& g )
600 {
601     return selfIntersectsImpl<2>( tin, g );
602 }
603 
selfIntersects3D(const TriangulatedSurface & tin,const SurfaceGraph & g)604 bool selfIntersects3D( const TriangulatedSurface& tin, const SurfaceGraph& g )
605 {
606     return selfIntersectsImpl<3>( tin, g );
607 }
608 
609 }
610 }
611