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