Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
geom_algorithms_test_edgecases_sweepline_minkowski_kdtree.cc
Go to the documentation of this file.
2#include <random>
3#include <cmath>
4
5
6// ---------- Edge cases: ClosestPair ----------
7
14
15
17{
19 one.append(Point(1, 1));
21 EXPECT_THROW((void) cp(one), std::domain_error);
22}
23
24
26{
28 dups.append(Point(7, 7));
29 dups.append(Point(7, 7));
30 dups.append(Point(7, 7));
31 dups.append(Point(7, 7));
32
34 auto res = cp(dups);
35
36 EXPECT_EQ(res.distance_squared, 0);
37 EXPECT_EQ(res.first, Point(7, 7));
38 EXPECT_EQ(res.second, Point(7, 7));
39}
40
41
42// ---------- Edge cases: CuttingEarsTriangulation ----------
43
45{
46 // A polygon with only 2 vertices cannot be closed (requires >= 3)
47 // so the exception is thrown at close() time, not at triangulation time
49 {
50 Polygon p;
51 p.add_vertex(Point(0, 0));
52 p.add_vertex(Point(1, 0));
53 p.close();
54 },
55 std::domain_error);
56}
57
58
59// ---------- Edge cases: RotatingCalipers ----------
60
62{
63 Polygon p;
64 p.add_vertex(Point(1, 1));
65 // Not closed — should throw.
66
68 EXPECT_THROW((void) calipers.diameter(p), std::domain_error);
69 EXPECT_THROW((void) calipers.minimum_width(p), std::domain_error);
70}
71
72
73// ---------- Edge cases: PointInPolygon ----------
74
76{
77 // A polygon with only 2 vertices cannot be closed (requires >= 3)
78 // so the exception is thrown at close() time
80 {
81 Polygon p;
82 p.add_vertex(Point(0, 0));
83 p.add_vertex(Point(5, 5));
84 p.close();
85 },
86 std::domain_error);
87}
88
89
90// ---------- Edge cases: Convex hull algorithms with 2 collinear points ----------
91
93{
94 DynList<Point> points;
95 points.append(Point(0, 0));
96 points.append(Point(5, 5));
97
99 Polygon hull = andrew(points);
100
101 // Degenerate 2-point hull cannot be closed (requires >= 3 vertices)
102 EXPECT_EQ(hull.size(), 2u);
103 EXPECT_FALSE(hull.is_closed());
106}
107
108
115
116
126
127
139
140
147
148
157
158
160{
161 DynList<Point> points;
162 points.append(Point(0, 0));
163 points.append(Point(5, 5));
164
166 Polygon hull = graham(points);
167 // Degenerate 2-point hull cannot be closed (requires >= 3 vertices)
168 EXPECT_EQ(hull.size(), 2u);
169 EXPECT_FALSE(hull.is_closed());
170}
171
172
174{
176 dups.append(Point(7, 7));
177 dups.append(Point(7, 7));
178 dups.append(Point(7, 7));
179
182 EXPECT_EQ(hull.size(), 1u);
183}
184
185
186// ---------- Cross-algorithm consistency ----------
187
189{
190 // All five hull algorithms should produce the same vertex set.
191 DynList<Point> points;
192 // Deterministic "random" set avoiding cocircular degeneracies.
193 // The constants below are the canonical glibc `rand()` LCG parameters
194 // (a, c, m) = (1103515245, 12345, 2^31). We use a hand-rolled LCG
195 // instead of <random> to keep the generated sequence portable across
196 // standard libraries — the test asserts that the five hull
197 // implementations agree on the same input set, so any well-defined
198 // deterministic generator works.
199 constexpr unsigned int LCG_MULT = 1103515245u;
200 constexpr unsigned int LCG_INCR = 12345u;
201 constexpr unsigned int LCG_MASK = 0x7fffffffu;
202 unsigned int seed = 12345;
203 for (int i = 0; i < 50; ++i)
204 {
206 int x = static_cast<int>(seed % 1000);
208 int y = static_cast<int>(seed % 1000);
209 points.append(Point(x, y));
210 }
211
217
218 Polygon h_andrew = andrew(points);
219 Polygon h_graham = graham(points);
220 Polygon h_qh = qh(points);
221 Polygon h_gw = gw(points);
222 Polygon h_bf = bf(points);
223
224 EXPECT_EQ(h_andrew.size(), h_graham.size());
225 EXPECT_EQ(h_andrew.size(), h_qh.size());
226 EXPECT_EQ(h_andrew.size(), h_gw.size());
227 EXPECT_EQ(h_andrew.size(), h_bf.size());
228
229 // Every vertex of Andrew's hull should appear in every other hull.
230 for (Polygon::Vertex_Iterator it(h_andrew); it.has_curr(); it.next_ne())
231 {
232 const Point p = it.get_current_vertex();
237 }
238}
239
240
241// ---------- Delaunay: as_triangles helper ----------
242
244{
246 auto r = delaunay({Point(0, 0), Point(6, 0), Point(3, 5), Point(6, 5),
247 Point(0, 5)});
248
250
251 size_t count = 0;
252 for (DynList<Triangle>::Iterator it(tris); it.has_curr(); it.next_ne())
253 ++count;
254
255 EXPECT_EQ(count, r.triangles.size());
256}
257
258
259// ============================================================================
260// Phase 4 — New Algorithms Tests
261// ============================================================================
262
263// ---------- SegmentSegmentIntersection (Dedicated O(1)) ----------
264
266{
268 const auto result = pair_ix(Segment(Point(0, 0), Point(2, 0)),
269 Segment(Point(0, 2), Point(2, 2)));
270
271 EXPECT_EQ(result.kind, SegmentSegmentIntersection::Kind::NONE);
272 EXPECT_FALSE(result.intersects());
273}
274
275
277{
279 const auto result = pair_ix(Segment(Point(0, 0), Point(4, 4)),
280 Segment(Point(0, 4), Point(4, 0)));
281
282 EXPECT_EQ(result.kind, SegmentSegmentIntersection::Kind::POINT);
283 EXPECT_TRUE(result.intersects());
284 EXPECT_EQ(result.point, Point(2, 2));
285}
286
287
289{
291 const auto result = pair_ix(Segment(Point(0, 0), Point(4, 0)),
292 Segment(Point(4, 0), Point(4, 3)));
293
294 EXPECT_EQ(result.kind, SegmentSegmentIntersection::Kind::POINT);
295 EXPECT_EQ(result.point, Point(4, 0));
296}
297
298
300{
302 const Segment a(Point(0, 0), Point(6, 0));
303 const Segment b(Point(2, 0), Point(4, 0));
304 const auto result = pair_ix(a, b);
305
308 scene.segments.append(b);
309 scene.highlighted_points.append(Point(2, 0));
310 scene.highlighted_points.append(Point(4, 0));
312 "case_segment_segment_overlap_o1", scene,
313 "Dedicated O(1) segment-segment overlap");
314
315 EXPECT_EQ(result.kind, SegmentSegmentIntersection::Kind::OVERLAP);
316 EXPECT_EQ(result.overlap.get_src_point(), Point(2, 0));
317 EXPECT_EQ(result.overlap.get_tgt_point(), Point(4, 0));
318}
319
320
322{
324 const auto result = pair_ix(Segment(Point(2, 5), Point(2, 1)),
325 Segment(Point(2, 3), Point(2, 7)));
326
327 EXPECT_EQ(result.kind, SegmentSegmentIntersection::Kind::OVERLAP);
328 EXPECT_EQ(result.overlap.get_src_point(), Point(2, 3));
329 EXPECT_EQ(result.overlap.get_tgt_point(), Point(2, 5));
330}
331
332
334{
336 const auto result = pair_ix(Segment(Point(3, 3), Point(3, 3)),
337 Segment(Point(0, 0), Point(6, 6)));
338
339 EXPECT_EQ(result.kind, SegmentSegmentIntersection::Kind::POINT);
340 EXPECT_EQ(result.point, Point(3, 3));
341}
342
343
345{
347 const auto result = pair_ix(Segment(Point(7, -2), Point(7, -2)),
348 Segment(Point(7, -2), Point(7, -2)));
349
350 EXPECT_EQ(result.kind, SegmentSegmentIntersection::Kind::POINT);
351 EXPECT_EQ(result.point, Point(7, -2));
352}
353
354
356{
358 const auto result = pair_ix(Segment(Point(1, 1), Point(1, 1)),
359 Segment(Point(2, 2), Point(2, 2)));
360
361 EXPECT_EQ(result.kind, SegmentSegmentIntersection::Kind::NONE);
362 EXPECT_FALSE(result.intersects());
363}
364
365
367{
369 const auto result = pair_ix(Segment(Point(0, 0), Point(2, 0)),
370 Segment(Point(2, 0), Point(5, 0)));
371
372 EXPECT_EQ(result.kind, SegmentSegmentIntersection::Kind::POINT);
373 EXPECT_EQ(result.point, Point(2, 0));
374}
375
376
378{
379 const auto result = segment_segment_intersection(Segment(Point(0, 0), Point(3, 3)),
380 Segment(Point(0, 3), Point(3, 0)));
381
382 EXPECT_EQ(result.kind, SegmentSegmentIntersection::Kind::POINT);
383 EXPECT_EQ(result.point, Point(Geom_Number(3, 2), Geom_Number(3, 2)));
384}
385
386
387// ---------- SweepLineSegmentIntersection ----------
388
396
397
399{
402 segs.append(Segment(Point(0, 0), Point(5, 5)));
403 auto result = sweep(segs);
404 EXPECT_EQ(result.size(), 0u);
405}
406
407
409{
412 segs.append(Segment(Point(0, 0), Point(5, 0)));
413 segs.append(Segment(Point(0, 1), Point(5, 1)));
414 auto result = sweep(segs);
415 EXPECT_EQ(result.size(), 0u);
416}
417
418
420{
423 segs.append(Segment(Point(0, 0), Point(4, 4)));
424 segs.append(Segment(Point(0, 4), Point(4, 0)));
425 auto result = sweep(segs);
426 ASSERT_EQ(result.size(), 1u);
427 EXPECT_EQ(result(0).point, Point(2, 2));
428 EXPECT_EQ(result(0).seg_i, 0u);
429 EXPECT_EQ(result(0).seg_j, 1u);
430}
431
432
434{
435 // Three segments forming a triangle of intersections.
438 segs.append(Segment(Point(0, 0), Point(6, 6))); // s0: diagonal up
439 segs.append(Segment(Point(0, 6), Point(6, 0))); // s1: diagonal down
440 segs.append(Segment(Point(0, 3), Point(6, 3))); // s2: horizontal
441
442 auto result = sweep(segs);
443
445 for (size_t i = 0; i < segs.size(); ++i)
447 for (size_t i = 0; i < result.size(); ++i)
448 scene.highlighted_points.append(result(i).point);
450 "case_sweepline_multiple_intersections", scene,
451 "Sweep-line / multi-intersection degeneracy");
452
453 // s0 x s1 at (3,3), s0 x s2 at (3,3), s1 x s2 at (3,3)
454 // Actually: s0 x s2 at (3,3), s1 x s2 at (3,3), s0 x s1 at (3,3)
455 // All three intersect at (3,3).
456 EXPECT_EQ(result.size(), 3u);
457 for (size_t i = 0; i < result.size(); ++i)
458 EXPECT_EQ(result(i).point, Point(3, 3));
459}
460
461
463{
466 segs.append(Segment(Point(0, 0), Point(1, 0)));
467 segs.append(Segment(Point(3, 3), Point(4, 3)));
468 auto result = sweep(segs);
469 EXPECT_EQ(result.size(), 0u);
470}
471
472
474{
477 segs.append(Segment(Point(0, 2), Point(4, 2))); // horizontal
478 segs.append(Segment(Point(2, 0), Point(2, 2))); // vertical, touching
479 auto result = sweep(segs);
480 ASSERT_EQ(result.size(), 1u);
481 EXPECT_EQ(result(0).point, Point(2, 2));
482}
483
484
486{
489 segs.append(Segment(Point(1, 1), Point(1, 1))); // zero length
490 segs.append(Segment(Point(0, 0), Point(2, 2)));
491 EXPECT_THROW((void) sweep(segs), std::domain_error);
492}
493
494
496{
497 // Four segments through center (2,2).
500 segs.append(Segment(Point(0, 2), Point(4, 2))); // horizontal
501 segs.append(Segment(Point(2, 0), Point(2, 4))); // vertical
502 segs.append(Segment(Point(0, 0), Point(4, 4))); // diagonal up
503 segs.append(Segment(Point(0, 4), Point(4, 0))); // diagonal down
504
505 auto result = sweep(segs);
506
507 // C(4,2) = 6 pairs, all intersecting at (2,2).
508 EXPECT_EQ(result.size(), 6u);
509 for (size_t i = 0; i < result.size(); ++i)
510 EXPECT_EQ(result(i).point, Point(2, 2));
511}
512
513
514// ---------- MonotonePolygonTriangulation ----------
515
517{
518 Polygon p;
519 p.add_vertex(Point(0, 0));
520 p.add_vertex(Point(4, 0));
521 p.add_vertex(Point(2, 3));
522 p.close();
523
526
527 size_t count = 0;
528 for (DynList<Triangle>::Iterator it(tris); it.has_curr(); it.next_ne())
529 ++count;
530 EXPECT_EQ(count, 1u);
531}
532
533
535{
536 Polygon p;
537 p.add_vertex(Point(0, 0));
538 p.add_vertex(Point(4, 0));
539 p.add_vertex(Point(4, 4));
540 p.add_vertex(Point(0, 4));
541 p.close();
542
545
546 size_t count = 0;
547 for (DynList<Triangle>::Iterator it(tris); it.has_curr(); it.next_ne())
548 ++count;
549 EXPECT_EQ(count, 2u);
550}
551
552
554{
555 Polygon p;
556 p.add_vertex(Point(0, 0));
557 p.add_vertex(Point(0, 4));
558 p.add_vertex(Point(4, 4));
559 p.add_vertex(Point(4, 0));
560 p.close();
561
564
565 size_t count = 0;
566 for (DynList<Triangle>::Iterator it(tris); it.has_curr(); it.next_ne())
567 ++count;
568 EXPECT_EQ(count, 2u);
569}
570
571
573{
574 Polygon p;
575 p.add_vertex(Point(2, 0));
576 p.add_vertex(Point(4, 1.5));
577 p.add_vertex(Point(3, 4));
578 p.add_vertex(Point(1, 4));
579 p.add_vertex(Point(0, 1.5));
580 p.close();
581
584
585 size_t count = 0;
586 for (DynList<Triangle>::Iterator it(tris); it.has_curr(); it.next_ne())
587 ++count;
588 EXPECT_EQ(count, 3u);
589}
590
591
593{
594 Polygon p;
595 p.add_vertex(Point(1, 0));
596 p.add_vertex(Point(2, 0));
597 p.add_vertex(Point(3, 1));
598 p.add_vertex(Point(2, 2));
599 p.add_vertex(Point(1, 2));
600 p.add_vertex(Point(0, 1));
601 p.close();
602
605
606 size_t count = 0;
607 for (DynList<Triangle>::Iterator it(tris); it.has_curr(); it.next_ne())
608 ++count;
609 EXPECT_EQ(count, 4u);
610}
611
612
614{
615 Polygon p;
616 p.add_vertex(Point(0, 0));
617 p.add_vertex(Point(4, 0));
618 p.add_vertex(Point(2, 3));
619
621 EXPECT_THROW((void) mt(p), std::domain_error);
622}
623
624
626{
627 // Degenerate (collinear) polygon should throw - either at close() time
628 // if vertices are reduced, or at triangulation time for zero area
630 {
631 Polygon p;
632 p.add_vertex(Point(0, 0));
633 p.add_vertex(Point(1, 0));
634 p.add_vertex(Point(2, 0));
635 p.close();
637 (void) mt(p);
638 },
639 std::domain_error);
640}
641
642
644{
645 // L-shaped polygon (non-monotone): both methods should produce n-2 triangles.
646 Polygon p;
647 p.add_vertex(Point(0, 0));
648 p.add_vertex(Point(4, 0));
649 p.add_vertex(Point(4, 2));
650 p.add_vertex(Point(2, 2));
651 p.add_vertex(Point(2, 4));
652 p.add_vertex(Point(0, 4));
653 p.close();
654
657
660
661 size_t mt_count = 0;
662 for (DynList<Triangle>::Iterator it(mt_tris); it.has_curr(); it.next_ne())
663 ++mt_count;
664
665 size_t ear_count = 0;
666 for (DynList<Triangle>::Iterator it(ear_tris); it.has_curr(); it.next_ne())
667 ++ear_count;
668
669 EXPECT_EQ(mt_count, 4u);
671}
672
673
674// ---------- MinkowskiSumConvex ----------
675
677{
678 // Square [0,1]^2 ⊕ Square [0,1]^2 = Square [0,2]^2.
679 Polygon sq;
680 sq.add_vertex(Point(0, 0));
681 sq.add_vertex(Point(1, 0));
682 sq.add_vertex(Point(1, 1));
683 sq.add_vertex(Point(0, 1));
684 sq.close();
685
687 Polygon result = mink(sq, sq);
688
689 EXPECT_EQ(result.size(), 4u);
690 EXPECT_TRUE(result.is_closed());
695}
696
697
699{
700 Polygon sq;
701 sq.add_vertex(Point(0, 0));
702 sq.add_vertex(Point(2, 0));
703 sq.add_vertex(Point(2, 2));
704 sq.add_vertex(Point(0, 2));
705 sq.close();
706
707 Polygon tri;
708 tri.add_vertex(Point(0, 0));
709 tri.add_vertex(Point(1, 0));
710 tri.add_vertex(Point(0, 1));
711 tri.close();
712
714 Polygon result = mink(sq, tri);
715
716 // Square (4 edges) + Triangle (3 edges) = up to 7 vertices.
717 EXPECT_TRUE(result.is_closed());
718 EXPECT_GE(result.size(), 3u);
719 EXPECT_LE(result.size(), 7u);
720
721 // The sum must contain the extreme vertices.
722 EXPECT_TRUE(polygon_contains_vertex(result, Point(0, 0))); // (0,0)+(0,0)
723 EXPECT_TRUE(polygon_contains_vertex(result, Point(3, 0))); // (2,0)+(1,0)
724 EXPECT_TRUE(polygon_contains_vertex(result, Point(0, 3))); // (0,2)+(0,1)
725}
726
727
729{
730 // CW square ⊕ CW square should still work.
732 sq_cw.add_vertex(Point(0, 0));
733 sq_cw.add_vertex(Point(0, 1));
734 sq_cw.add_vertex(Point(1, 1));
735 sq_cw.add_vertex(Point(1, 0));
736 sq_cw.close();
737
739 Polygon result = mink(sq_cw, sq_cw);
740
741 EXPECT_EQ(result.size(), 4u);
742 EXPECT_TRUE(result.is_closed());
747}
748
749
751{
753 convex.add_vertex(Point(0, 0));
754 convex.add_vertex(Point(2, 0));
755 convex.add_vertex(Point(2, 2));
756 convex.add_vertex(Point(0, 2));
757 convex.close();
758
760 concave.add_vertex(Point(0, 0));
761 concave.add_vertex(Point(4, 0));
762 concave.add_vertex(Point(2, 1));
763 concave.add_vertex(Point(4, 4));
764 concave.add_vertex(Point(0, 4));
765 concave.close();
766
768 EXPECT_THROW((void) mink(convex, concave), std::domain_error);
769 EXPECT_THROW((void) mink(concave, convex), std::domain_error);
770}
771
772
774{
775 Polygon open;
776 open.add_vertex(Point(0, 0));
777 open.add_vertex(Point(1, 0));
778 open.add_vertex(Point(1, 1));
779
780 Polygon closed;
781 closed.add_vertex(Point(0, 0));
782 closed.add_vertex(Point(1, 0));
783 closed.add_vertex(Point(0, 1));
784 closed.close();
785
787 EXPECT_THROW((void) mink(open, closed), std::domain_error);
788}
789
790
792{
793 // Pentagon ⊕ Triangle — result must be convex.
795 pent.add_vertex(Point(2, 0));
796 pent.add_vertex(Point(4, 1));
797 pent.add_vertex(Point(3, 3));
798 pent.add_vertex(Point(1, 3));
799 pent.add_vertex(Point(0, 1));
800 pent.close();
801
802 Polygon tri;
803 tri.add_vertex(Point(0, 0));
804 tri.add_vertex(Point(1, 0));
805 tri.add_vertex(Point(0, 1));
806 tri.close();
807
809 Polygon result = mink(pent, tri);
810 EXPECT_TRUE(result.is_closed());
811 EXPECT_GE(result.size(), 3u);
812
813 // Verify convexity: all turns should be consistent.
815 for (Polygon::Vertex_Iterator it(result); it.has_curr(); it.next_ne())
816 rv.append(it.get_current_vertex());
817
818 int sign = 0;
819 for (size_t i = 0; i < rv.size(); ++i)
820 {
821 Geom_Number turn =
822 area_of_parallelogram(rv(i), rv((i + 1) % rv.size()),
823 rv((i + 2) % rv.size()));
824 if (turn == 0) continue;
825 int s = turn > 0 ? 1 : -1;
826 if (sign == 0) sign = s;
827 else EXPECT_EQ(sign, s);
828 }
829}
830
831static Point first_vertex_of(const Polygon & poly)
832{
833 for (Polygon::Vertex_Iterator it(poly); it.has_curr(); it.next_ne())
834 return it.get_current_vertex();
835 return Point(0, 0);
836}
837
839 Point & out_p, Point & out_q)
840{
843 {
845 out_q = out_p;
846 return 0;
847 }
848
849 for (Polygon::Segment_Iterator itp(p); itp.has_curr(); itp.next_ne())
850 {
851 const Segment sp = itp.get_current_segment();
852 for (Polygon::Segment_Iterator itq(q); itq.has_curr(); itq.next_ne())
853 if (sp.intersects_with(itq.get_current_segment()))
854 {
856 out_q = out_p;
857 return 0;
858 }
859 }
860
863
864 bool has_best = false;
866
867 for (size_t i = 0; i < pv.size(); ++i)
868 for (size_t j = 0; j < qv.size(); ++j)
869 {
870 const Segment e(qv(j), qv((j + 1) % qv.size()));
871 const Point proj = e.project(pv(i));
872 const Geom_Number d2 = pv(i).distance_squared_to(proj);
873 if (not has_best or d2 < best_d2)
874 {
875 has_best = true;
876 best_d2 = d2;
877 out_p = pv(i);
878 out_q = proj;
879 }
880 }
881
882 for (size_t i = 0; i < qv.size(); ++i)
883 for (size_t j = 0; j < pv.size(); ++j)
884 {
885 const Segment e(pv(j), pv((j + 1) % pv.size()));
886 const Point proj = e.project(qv(i));
887 const Geom_Number d2 = qv(i).distance_squared_to(proj);
888 if (not has_best or d2 < best_d2)
889 {
890 has_best = true;
891 best_d2 = d2;
892 out_p = proj;
893 out_q = qv(i);
894 }
895 }
896
897 return best_d2;
898}
899
900// ---------- ConvexPolygonDistanceGJK ----------
901
903{
904 Polygon a;
905 a.add_vertex(Point(0, 0));
906 a.add_vertex(Point(1, 0));
907 a.add_vertex(Point(1, 1));
908 a.add_vertex(Point(0, 1));
909 a.close();
910
911 Polygon b;
912 b.add_vertex(Point(2, 0));
913 b.add_vertex(Point(3, 0));
914 b.add_vertex(Point(3, 1));
915 b.add_vertex(Point(2, 1));
916 b.close();
917
919 const auto r = gjk(a, b);
920
921 EXPECT_FALSE(r.intersects);
922 EXPECT_EQ(r.distance_squared, Geom_Number(1));
923 EXPECT_EQ(r.closest_on_first.distance_squared_to(r.closest_on_second),
924 r.distance_squared);
925 EXPECT_LE(r.gjk_iterations, 64u);
926}
927
929{
930 Polygon a;
931 a.add_vertex(Point(0, 0));
932 a.add_vertex(Point(3, 0));
933 a.add_vertex(Point(3, 3));
934 a.add_vertex(Point(0, 3));
935 a.close();
936
937 Polygon b;
938 b.add_vertex(Point(2, 2));
939 b.add_vertex(Point(4, 2));
940 b.add_vertex(Point(4, 4));
941 b.add_vertex(Point(2, 4));
942 b.close();
943
945 const auto r = gjk(a, b);
946
947 EXPECT_TRUE(r.intersects);
948 EXPECT_EQ(r.distance_squared, Geom_Number(0));
949 EXPECT_EQ(r.distance, Geom_Number(0));
950}
951
953{
954 Polygon a;
955 a.add_vertex(Point(0, 0));
956 a.add_vertex(Point(1, 0));
957 a.add_vertex(Point(1, 1));
958 a.add_vertex(Point(0, 1));
959 a.close();
960
961 Polygon b;
962 b.add_vertex(Point(1, 0));
963 b.add_vertex(Point(2, 0));
964 b.add_vertex(Point(2, 1));
965 b.add_vertex(Point(1, 1));
966 b.close();
967
969 const auto r = gjk(a, b);
970
971 EXPECT_TRUE(r.intersects);
972 EXPECT_EQ(r.distance_squared, Geom_Number(0));
973}
974
976{
977 Polygon a;
978 a.add_vertex(Point(0, 0));
979 a.add_vertex(Point(4, 0));
980 a.add_vertex(Point(2, 2));
981 a.close();
982
983 Polygon b;
984 b.add_vertex(Point(6, 1));
985 b.add_vertex(Point(9, 1));
986 b.add_vertex(Point(9, 4));
987 b.add_vertex(Point(6, 4));
988 b.close();
989
991 const auto ab = gjk(a, b);
992 const auto ba = gjk(b, a);
993
994 EXPECT_EQ(ab.distance_squared, ba.distance_squared);
995 EXPECT_EQ(ab.distance, ba.distance);
996 EXPECT_EQ(ab.intersects, ba.intersects);
997}
998
1000{
1002 convex.add_vertex(Point(0, 0));
1003 convex.add_vertex(Point(2, 0));
1004 convex.add_vertex(Point(2, 2));
1005 convex.add_vertex(Point(0, 2));
1006 convex.close();
1007
1009 concave.add_vertex(Point(0, 0));
1010 concave.add_vertex(Point(4, 0));
1011 concave.add_vertex(Point(2, 1));
1012 concave.add_vertex(Point(4, 4));
1013 concave.add_vertex(Point(0, 4));
1014 concave.close();
1015
1016 Polygon open;
1017 open.add_vertex(Point(0, 0));
1018 open.add_vertex(Point(1, 0));
1019 open.add_vertex(Point(1, 1));
1020
1022 EXPECT_THROW((void) gjk(open, convex), std::domain_error);
1023 EXPECT_THROW((void) gjk(convex, open), std::domain_error);
1024 EXPECT_THROW((void) gjk(concave, convex), std::domain_error);
1025 EXPECT_THROW((void) gjk(convex, concave), std::domain_error);
1026}
1027
1029{
1032
1033 unsigned int seed = 424242;
1034 for (int tc = 0; tc < 12; ++tc)
1035 {
1036 Polygon a, b;
1037 while (a.size() < 3)
1038 {
1040 for (int i = 0; i < 16; ++i)
1041 {
1042 seed = (seed * 1103515245 + 12345) & 0x7fffffff;
1043 const int x = static_cast<int>(seed % 51) - 25;
1044 seed = (seed * 1103515245 + 12345) & 0x7fffffff;
1045 const int y = static_cast<int>(seed % 51) - 25;
1046 pts.append(Point(x, y));
1047 }
1048 a = hull(pts);
1049 }
1050
1051 while (b.size() < 3)
1052 {
1054 for (int i = 0; i < 16; ++i)
1055 {
1056 seed = (seed * 1103515245 + 12345) & 0x7fffffff;
1057 const int x = static_cast<int>(seed % 51) - 25;
1058 seed = (seed * 1103515245 + 12345) & 0x7fffffff;
1059 const int y = static_cast<int>(seed % 51) - 25;
1060 pts.append(Point(x + 10, y + 7));
1061 }
1062 b = hull(pts);
1063 }
1064
1065 const auto r = gjk(a, b);
1066 Point bp, bq;
1068
1069 EXPECT_EQ(r.distance_squared, brute_d2);
1070 EXPECT_EQ(r.intersects, brute_d2 == 0);
1071 }
1072}
1073
1074
1075// ---------- KDTreePointSearch ----------
1076
1078{
1079 KDTreePointSearch kd(0, 0, 100, 100);
1080 EXPECT_TRUE(kd.is_empty());
1081
1082 EXPECT_TRUE(kd.insert(Point(10, 20)));
1083 EXPECT_TRUE(kd.insert(Point(50, 50)));
1084 EXPECT_FALSE(kd.insert(Point(10, 20))); // duplicate
1085
1086 EXPECT_EQ(kd.size(), 2u);
1087 EXPECT_TRUE(kd.contains(Point(10, 20)));
1088 EXPECT_TRUE(kd.contains(Point(50, 50)));
1089 EXPECT_FALSE(kd.contains(Point(30, 30)));
1090}
1091
1092
1094{
1095 KDTreePointSearch kd(0, 0, 100, 100);
1096 kd.insert(Point(10, 10));
1097 kd.insert(Point(50, 50));
1098 kd.insert(Point(90, 90));
1099
1100 auto n = kd.nearest(Point(12, 12));
1101 ASSERT_TRUE(n.has_value());
1102 EXPECT_EQ(*n, Point(10, 10));
1103
1104 auto n2 = kd.nearest(Point(48, 52));
1105 ASSERT_TRUE(n2.has_value());
1106 EXPECT_EQ(*n2, Point(50, 50));
1107}
1108
1109
1111{
1112 KDTreePointSearch kd(0, 0, 100, 100);
1113 auto n = kd.nearest(Point(50, 50));
1114 EXPECT_FALSE(n.has_value());
1115}
1116
1117
1119{
1120 Array<Point> points;
1121 for (int x = 0; x < 10; ++x)
1122 for (int y = 0; y < 10; ++y)
1123 points.append(Point(x, y));
1124
1125 auto kd = KDTreePointSearch::build(points, 0, 0, 10, 10);
1126
1127 EXPECT_EQ(kd.size(), 100u);
1128
1129 for (int x = 0; x < 10; ++x)
1130 for (int y = 0; y < 10; ++y)
1131 EXPECT_TRUE(kd.contains(Point(x, y)));
1132
1133 auto n = kd.nearest(Point(5, 5));
1134 ASSERT_TRUE(n.has_value());
1135 EXPECT_EQ(*n, Point(5, 5));
1136}
1137
1138
1140{
1141 KDTreePointSearch kd(0, 0, 100, 100);
1142 kd.insert(Point(10, 10));
1143 kd.insert(Point(20, 20));
1144 kd.insert(Point(50, 50));
1145 kd.insert(Point(80, 80));
1146
1148 kd.range(5, 5, 25, 25, &out);
1149
1150 size_t count = 0;
1151 for (DynList<Point>::Iterator it(out); it.has_curr(); it.next_ne())
1152 ++count;
1153 EXPECT_EQ(count, 2u); // (10,10) and (20,20)
1154}
1155
1156
1158{
1159 KDTreePointSearch kd(0, 0, 100, 100);
1160 kd.insert(Point(1, 1));
1161 kd.insert(Point(2, 2));
1162 kd.insert(Point(3, 3));
1163
1164 size_t visited = 0;
1165 kd.for_each([&visited](const Point &) { ++visited; });
1166 EXPECT_EQ(visited, 3u);
1167}
1168
1170{
1171 KDTreePointSearch kd(0, 0, 100, 100);
1172 kd.insert(Point(10, 10));
1173 kd.insert(Point(25, 40));
1174 kd.insert(Point(70, 20));
1175 kd.insert(Point(80, 90));
1176
1177 const auto snap = kd.debug_snapshot();
1178 EXPECT_EQ(snap.points.size(), kd.size());
1179 EXPECT_GT(snap.partitions.size(), 0u);
1180 EXPECT_EQ(snap.bounds.get_xmin(), Geom_Number(0));
1181 EXPECT_EQ(snap.bounds.get_ymin(), Geom_Number(0));
1182 EXPECT_EQ(snap.bounds.get_xmax(), Geom_Number(100));
1183 EXPECT_EQ(snap.bounds.get_ymax(), Geom_Number(100));
1184
1185 size_t leaves = 0;
1186 for (size_t i = 0; i < snap.partitions.size(); ++i)
1187 if (snap.partitions(i).is_leaf)
1188 ++leaves;
1189 EXPECT_EQ(leaves, kd.size());
1190}
1191
1192
1193// ============================================================================
1194// Phase 5 — Rigorous Tests
1195// ============================================================================
1196
1197// ---------- 5.1 Property tests: Delaunay empty-circumcircle ----------
1198
1199// Helper: squared distance between two points (exact).
1200static Geom_Number dist2(const Point & a, const Point & b)
1201
1202{
1203
1204 return a.distance_squared_to(b);
1205
1206}
1207
1208
1209// Helper: extract sorted vertex set from polygon for comparison.
1211
1212{
1213
1214 Array<Point> v;
1215
1216 for (Polygon::Vertex_Iterator it(p); it.has_curr(); it.next_ne())
1217
1218 v.append(it.get_current_vertex());
1219
1220 quicksort_op(v, [](const Point & a, const Point & b)
1221
1222 {
1223
1224 if (a.get_x() != b.get_x())
1225
1226 return a.get_x() < b.get_x();
1227
1228 return a.get_y() < b.get_y();
1229
1230 });
1231
1232 return v;
1233
1234}
1235
1236
1238{
1239 // The Delaunay property: for every triangle, no other site is strictly
1240 // inside its circumcircle.
1242 auto r = delaunay({Point(0, 0), Point(6, 0), Point(3, 5), Point(6, 5),
1243 Point(0, 5), Point(3, 2), Point(1, 3), Point(5, 1)});
1244
1245 ASSERT_GE(r.triangles.size(), 1u);
1246
1247 for (size_t t = 0; t < r.triangles.size(); ++t)
1248 {
1249 const auto & tri = r.triangles(t);
1250 const Point & a = r.sites(tri.i);
1251 const Point & b = r.sites(tri.j);
1252 const Point & c = r.sites(tri.k);
1253
1254 // Compute circumcenter and squared circumradius.
1255 const Point cc = circumcenter_of(a, b, c);
1256 const Geom_Number r2 = dist2(cc, a);
1257
1258 for (size_t s = 0; s < r.sites.size(); ++s)
1259 {
1260 if (s == tri.i or s == tri.j or s == tri.k)
1261 continue;
1262
1263 const Geom_Number d2 = dist2(cc, r.sites(s));
1264 // d2 must be >= r2 (no site strictly inside the circumcircle).
1265 EXPECT_GE(d2, r2)
1266 << "Site " << s << " violates empty-circumcircle for triangle "
1267 << t;
1268 }
1269 }
1270}
1271
1272
1274{
1275 // Grid of 5x5 points — a stress test of the circumcircle property.
1277 for (int x = 0; x < 5; ++x)
1278 for (int y = 0; y < 5; ++y)
1279 pts.append(Point(x, y));
1280
1282 auto r = delaunay(pts);
1283
1284 ASSERT_GE(r.triangles.size(), 1u);
1285
1286 for (size_t t = 0; t < r.triangles.size(); ++t)
1287 {
1288 const auto & tri = r.triangles(t);
1289 const Point & a = r.sites(tri.i);
1290 const Point & b = r.sites(tri.j);
1291 const Point & c = r.sites(tri.k);
1292 const Point cc = circumcenter_of(a, b, c);
1293 const Geom_Number r2 = dist2(cc, a);
1294
1295 for (size_t s = 0; s < r.sites.size(); ++s)
1296 {
1297 if (s == tri.i or s == tri.j or s == tri.k)
1298 continue;
1299 EXPECT_GE(dist2(cc, r.sites(s)), r2);
1300 }
1301 }
1302}
1303
1304
1305// ---------- 5.1 Property tests: Voronoi equidistance ----------
1306
1308{
1309 // Each bounded Voronoi edge connects two circumcenters.
1310 // Each circumcenter (Voronoi vertex) is equidistant to the 3 sites
1311 // of its Delaunay triangle.
1313 auto dt = delaunay({Point(0, 0), Point(5, 0), Point(6, 3), Point(0, 4),
1314 Point(2, 2), Point(4, 4)});
1315 ASSERT_GE(dt.triangles.size(), 1u);
1316
1317 for (size_t t = 0; t < dt.triangles.size(); ++t)
1318 {
1319 const auto & tri = dt.triangles(t);
1320 const Point & a = dt.sites(tri.i);
1321 const Point & b = dt.sites(tri.j);
1322 const Point & c = dt.sites(tri.k);
1323 const Point cc = circumcenter_of(a, b, c);
1324
1325 const Geom_Number da = dist2(cc, a);
1326 const Geom_Number db = dist2(cc, b);
1327 const Geom_Number dc = dist2(cc, c);
1328
1329 EXPECT_EQ(da, db) << "Triangle " << t << ": circumcenter not equidistant";
1330 EXPECT_EQ(db, dc) << "Triangle " << t << ": circumcenter not equidistant";
1331 }
1332}
1333
1334
1336{
1337 // For each bounded Voronoi edge (connecting two circumcenters c0 and c1),
1338 // the two adjacent sites u,v should be equidistant from the edge midpoint.
1340 auto dt = delaunay({Point(0, 0), Point(5, 0), Point(6, 3), Point(0, 4),
1341 Point(2, 2), Point(4, 4)});
1342
1344 auto r = voronoi(dt);
1345
1346 for (size_t e = 0; e < r.edges.size(); ++e)
1347 {
1348 const auto & edge = r.edges(e);
1349 if (edge.unbounded)
1350 continue;
1351
1352 // Both endpoints are circumcenters equidistant to sites u and v.
1353 const Geom_Number d_src_u = dist2(edge.src, r.sites(edge.site_u));
1354 const Geom_Number d_src_v = dist2(edge.src, r.sites(edge.site_v));
1356 << "Edge " << e << " src not equidistant to sites";
1357
1358 const Geom_Number d_tgt_u = dist2(edge.tgt, r.sites(edge.site_u));
1359 const Geom_Number d_tgt_v = dist2(edge.tgt, r.sites(edge.site_v));
1361 << "Edge " << e << " tgt not equidistant to sites";
1362 }
1363}
1364
1365
1366// ---------- 5.2 Numerical robustness: near-collinear ----------
1367
1369{
1370 // Points almost collinear but with tiny deviation — exact arithmetic
1371 // should handle this correctly.
1372 // Using rational offsets like 1/1000000 instead of floating-point.
1373 Geom_Number tiny(1, 1000000); // 10^-6 as exact rational
1374
1376 pts.append(Point(0, 0));
1377 pts.append(Point(1, tiny));
1378 pts.append(Point(2, -tiny));
1379 pts.append(Point(3, tiny));
1380 pts.append(Point(4, 0));
1381 pts.append(Point(2, 1)); // clearly off-axis to guarantee non-collinear set
1382
1384 auto r = delaunay(pts);
1385
1387 for (DynList<Point>::Iterator it(pts); it.has_curr(); it.next_ne())
1388 scene.points.append(it.get_curr());
1389 for (size_t i = 0; i < r.triangles.size(); ++i)
1390 {
1391 const auto & t = r.triangles(i);
1392 scene.segments.append(Segment(r.sites(t.i), r.sites(t.j)));
1393 scene.segments.append(Segment(r.sites(t.j), r.sites(t.k)));
1394 scene.segments.append(Segment(r.sites(t.k), r.sites(t.i)));
1395 }
1397 "case_robust_near_collinear_delaunay", scene,
1398 "Delaunay robustness / near-collinear");
1399
1400 // Should produce a valid triangulation.
1401 EXPECT_GE(r.triangles.size(), 1u);
1402
1403 // Verify circumcircle property.
1404 for (size_t t = 0; t < r.triangles.size(); ++t)
1405 {
1406 const auto & tri = r.triangles(t);
1407 const Point cc = circumcenter_of(r.sites(tri.i), r.sites(tri.j),
1408 r.sites(tri.k));
1409 const Geom_Number r2 = dist2(cc, r.sites(tri.i));
1410 for (size_t s = 0; s < r.sites.size(); ++s)
1411 {
1412 if (s == tri.i or s == tri.j or s == tri.k)
1413 continue;
1414 EXPECT_GE(dist2(cc, r.sites(s)), r2);
1415 }
1416 }
1417}
1418
1419
1421{
1422 // Near-collinear points should still produce a valid hull.
1423 Geom_Number tiny(1, 10000000); // 10^-7
1424
1426 pts.append(Point(0, 0));
1427 pts.append(Point(1, tiny));
1428 pts.append(Point(2, 0));
1429 pts.append(Point(3, -tiny));
1430 pts.append(Point(4, 0));
1431 pts.append(Point(2, 1)); // off-line to make non-degenerate
1432
1435
1437 for (DynList<Point>::Iterator it(pts); it.has_curr(); it.next_ne())
1438 scene.points.append(it.get_curr());
1439 scene.polygons.append(hull);
1442 "case_robust_near_collinear_hull", scene,
1443 "Convex hull robustness / near-collinear");
1444
1445 EXPECT_TRUE(hull.is_closed());
1446 EXPECT_GE(hull.size(), 3u);
1447
1448 // Hull must contain the extremes.
1452}
1453
1454
1455// ---------- 5.2 Numerical robustness: near-parallel segments ----------
1456
1458{
1459 // Two segments that are nearly parallel — they intersect at a very
1460 // distant point. The sweep line should either find 0 or 1 intersection
1461 // depending on whether the segments actually overlap.
1462 Geom_Number tiny(1, 100000000); // 10^-8
1463
1466 segs.append(Segment(Point(0, 0), Point(10, 0)));
1467 segs.append(Segment(Point(0, tiny), Point(10, tiny))); // almost parallel
1468
1469 auto result = sweep(segs);
1470 EXPECT_EQ(result.size(), 0u); // truly parallel, no intersection
1471}
1472
1473
1475{
1476 // Two segments that converge at a nearly-parallel angle.
1477 Geom_Number tiny(1, 1000000);
1478
1481 segs.append(Segment(Point(0, 0), Point(10, 0)));
1482 segs.append(Segment(Point(0, tiny), Point(10, -tiny))); // slight converge
1483
1484 auto result = sweep(segs);
1485
1487 for (size_t i = 0; i < segs.size(); ++i)
1489 for (size_t i = 0; i < result.size(); ++i)
1490 scene.highlighted_points.append(result(i).point);
1492 "case_robust_near_parallel_converging", scene,
1493 "Near-parallel segments / converging intersection");
1494
1495 ASSERT_EQ(result.size(), 1u);
1496 // Intersection must be exact.
1497 EXPECT_EQ(result(0).point.get_y(), Geom_Number(0)); // should be on y=0 plane
1498}
1499
1500
1501// ---------- 5.2 Numerical robustness: extreme coordinates ----------
1502
1504{
1505 // Points with very large coordinates — exact arithmetic handles this.
1506 Geom_Number big(1000000000); // 10^9
1507
1509 pts.append(Point(big, big));
1510 pts.append(Point(-big, big));
1511 pts.append(Point(-big, -big));
1512 pts.append(Point(big, -big));
1513 pts.append(Point(0, 0));
1514
1516 auto r = delaunay(pts);
1517 EXPECT_GE(r.triangles.size(), 1u);
1518
1519 // Verify circumcircle property with big coords.
1520 for (size_t t = 0; t < r.triangles.size(); ++t)
1521 {
1522 const auto & tri = r.triangles(t);
1523 const Point cc = circumcenter_of(r.sites(tri.i), r.sites(tri.j),
1524 r.sites(tri.k));
1525 const Geom_Number r2 = dist2(cc, r.sites(tri.i));
1526 for (size_t s = 0; s < r.sites.size(); ++s)
1527 {
1528 if (s == tri.i or s == tri.j or s == tri.k)
1529 continue;
1530 EXPECT_GE(dist2(cc, r.sites(s)), r2);
1531 }
1532 }
1533}
1534
1535
1537{
1538 // Points with very small coordinates.
1539 Geom_Number eps(1, 1000000000); // 10^-9
1540
1542 pts.append(Point(0, 0));
1543 pts.append(Point(eps, 0));
1544 pts.append(Point(0, eps));
1545 pts.append(Point(eps, eps));
1546
1548 auto r = delaunay(pts);
1549 EXPECT_GE(r.triangles.size(), 2u);
1550}
1551
1552
1553// ---------- 5.2 Numerical robustness: cocircular points ----------
1554
1556{
1557 // 8 points on a circle — a degenerate case for Delaunay.
1558 // The triangulation should still be valid and complete.
1559 // Use rational approximations of circle points:
1560 // (2,0), (0,2), (-2,0), (0,-2), and 4 diagonal points.
1562 pts.append(Point(2, 0));
1563 pts.append(Point(0, 2));
1564 pts.append(Point(-2, 0));
1565 pts.append(Point(0, -2));
1566
1567 // Points at 45-degree offsets (rational approximation on circle r=2).
1568 // Use exact: sqrt(2) ≈ 1414/1000 (close enough for testing cocircularity).
1569 // Actually, for true cocircularity use x^2+y^2 = 4.
1570 // (1, sqrt(3)) is on circle r=2: 1+3=4. Use Geom_Number for sqrt(3).
1571 // Instead, let's use: (8/5, 6/5) since (8/5)^2+(6/5)^2 = 64/25+36/25 = 100/25 = 4.
1572 pts.append(Point(Geom_Number(8, 5), Geom_Number(6, 5)));
1573 pts.append(Point(Geom_Number(-8, 5), Geom_Number(6, 5)));
1574 pts.append(Point(Geom_Number(-8, 5), Geom_Number(-6, 5)));
1575 pts.append(Point(Geom_Number(8, 5), Geom_Number(-6, 5)));
1576
1578 auto r = delaunay(pts);
1579
1581 for (DynList<Point>::Iterator it(pts); it.has_curr(); it.next_ne())
1582 scene.points.append(it.get_curr());
1583 for (size_t i = 0; i < r.triangles.size(); ++i)
1584 {
1585 const auto & t = r.triangles(i);
1586 scene.segments.append(Segment(r.sites(t.i), r.sites(t.j)));
1587 scene.segments.append(Segment(r.sites(t.j), r.sites(t.k)));
1588 scene.segments.append(Segment(r.sites(t.k), r.sites(t.i)));
1589 }
1591 "case_robust_cocircular_delaunay", scene,
1592 "Delaunay robustness / cocircular points");
1593
1594 // Must produce a triangulation.
1595 EXPECT_GE(r.triangles.size(), 6u); // at least 6 triangles for 8 cocircular pts
1596
1597 // All sites should participate.
1598 EXPECT_EQ(r.sites.size(), 8u);
1599}
1600
1601
1602// ---------- 5.3 Determinism: permuted inputs produce same results ----------
1603
1605{
1606 // The Delaunay output should be the same regardless of input order.
1608 pts1.append(Point(0, 0));
1609 pts1.append(Point(5, 0));
1610 pts1.append(Point(6, 3));
1611 pts1.append(Point(0, 4));
1612 pts1.append(Point(3, 2));
1613
1614 DynList<Point> pts2; // reverse order
1615 pts2.append(Point(3, 2));
1616 pts2.append(Point(0, 4));
1617 pts2.append(Point(6, 3));
1618 pts2.append(Point(5, 0));
1619 pts2.append(Point(0, 0));
1620
1621 DynList<Point> pts3; // shuffled
1622 pts3.append(Point(6, 3));
1623 pts3.append(Point(0, 0));
1624 pts3.append(Point(3, 2));
1625 pts3.append(Point(5, 0));
1626 pts3.append(Point(0, 4));
1627
1629 auto r1 = delaunay(pts1);
1630 auto r2 = delaunay(pts2);
1631 auto r3 = delaunay(pts3);
1632
1633 // Same number of sites and triangles.
1634 EXPECT_EQ(r1.sites.size(), r2.sites.size());
1635 EXPECT_EQ(r1.sites.size(), r3.sites.size());
1636 EXPECT_EQ(r1.triangles.size(), r2.triangles.size());
1637 EXPECT_EQ(r1.triangles.size(), r3.triangles.size());
1638
1639 // Canonical triangle sets should match.
1640 auto ct1 = canonical_triangles(r1);
1641 auto ct2 = canonical_triangles(r2);
1642 auto ct3 = canonical_triangles(r3);
1643
1644 ASSERT_EQ(ct1.size(), ct2.size());
1645 ASSERT_EQ(ct1.size(), ct3.size());
1646
1647 for (size_t i = 0; i < ct1.size(); ++i)
1648 {
1649 EXPECT_EQ(ct1(i).a, ct2(i).a);
1650 EXPECT_EQ(ct1(i).b, ct2(i).b);
1651 EXPECT_EQ(ct1(i).c, ct2(i).c);
1652 EXPECT_EQ(ct1(i).a, ct3(i).a);
1653 EXPECT_EQ(ct1(i).b, ct3(i).b);
1654 EXPECT_EQ(ct1(i).c, ct3(i).c);
1655 }
1656}
1657
1658
1660{
1662 pts1.append(Point(0, 0));
1663 pts1.append(Point(5, 0));
1664 pts1.append(Point(6, 3));
1665 pts1.append(Point(0, 4));
1666 pts1.append(Point(3, 1)); // interior point
1667
1669 pts2.append(Point(3, 1));
1670 pts2.append(Point(0, 4));
1671 pts2.append(Point(6, 3));
1672 pts2.append(Point(5, 0));
1673 pts2.append(Point(0, 0));
1674
1676 Polygon h1 = andrew(pts1);
1677 Polygon h2 = andrew(pts2);
1678
1679 auto v1 = sorted_hull_vertices(h1);
1680 auto v2 = sorted_hull_vertices(h2);
1681
1682 ASSERT_EQ(v1.size(), v2.size());
1683 for (size_t i = 0; i < v1.size(); ++i)
1684 EXPECT_EQ(v1(i), v2(i));
1685}
1686
1687
1689{
1691 pts1.append(Point(0, 0));
1692 pts1.append(Point(10, 10));
1693 pts1.append(Point(1, 0)); // closest pair: (0,0)-(1,0)
1694 pts1.append(Point(5, 5));
1695
1697 pts2.append(Point(5, 5));
1698 pts2.append(Point(1, 0));
1699 pts2.append(Point(0, 0));
1700 pts2.append(Point(10, 10));
1701
1703 auto r1 = cp(pts1);
1704 auto r2 = cp(pts2);
1705
1706 EXPECT_EQ(r1.distance_squared, r2.distance_squared);
1707 // Same pair (possibly swapped).
1708 EXPECT_TRUE(matches_unordered_pair(r1.first, r1.second, r2.first, r2.second));
1709}
1710
1711
1712// ---------- 5.4 Performance: large datasets ----------
1713
1715{
1716 // 10000 points on a grid — convex hull should return the boundary.
1718 for (int x = 0; x < 100; ++x)
1719 for (int y = 0; y < 100; ++y)
1720 pts.append(Point(x, y));
1721
1724
1725 EXPECT_TRUE(hull.is_closed());
1726 // The hull of a grid is the bounding rectangle.
1727 EXPECT_EQ(hull.size(), 4u);
1732}
1733
1734
1736{
1737 // 5000 points on a grid; minimum distance = 1.
1739 for (int x = 0; x < 50; ++x)
1740 for (int y = 0; y < 100; ++y)
1741 pts.append(Point(x, y));
1742
1744 auto r = cp(pts);
1745
1746 EXPECT_EQ(r.distance_squared, Geom_Number(1));
1747}
1748
1749
1751{
1752 // 500 points on a grid — verify valid Delaunay.
1754 for (int x = 0; x < 25; ++x)
1755 for (int y = 0; y < 20; ++y)
1756 pts.append(Point(x, y));
1757
1759 auto r = delaunay(pts);
1760
1761 EXPECT_GE(r.triangles.size(), 1u);
1762
1763 // Spot-check a few triangles for circumcircle property.
1764 const size_t check_limit = r.triangles.size() < 50 ? r.triangles.size() : 50;
1765 for (size_t t = 0; t < check_limit; ++t)
1766 {
1767 const auto & tri = r.triangles(t);
1768 const Point cc = circumcenter_of(r.sites(tri.i), r.sites(tri.j),
1769 r.sites(tri.k));
1770 const Geom_Number r2 = dist2(cc, r.sites(tri.i));
1771 for (size_t s = 0; s < r.sites.size(); ++s)
1772 {
1773 if (s == tri.i or s == tri.j or s == tri.k)
1774 continue;
1775 EXPECT_GE(dist2(cc, r.sites(s)), r2);
1776 }
1777 }
1778}
1779
1780
1782{
1783 // Build a simple polygon with ~100 vertices (zigzag) — no collinear edges.
1784 // Triangulation should produce n-2 triangles.
1785 Polygon p;
1786
1787 // Bottom zigzag: (0,0), (1,1), (2,0), (3,1), ..., (48,0), (49,1), (50,0)
1788 for (int x = 0; x <= 50; ++x)
1789 p.add_vertex(Point(x, (x % 2 == 0) ? 0 : 1));
1790
1791 // Top zigzag going back: (50,10), (49,9), (48,10), ..., (1,9), (0,10)
1792 for (int x = 50; x >= 0; --x)
1793 p.add_vertex(Point(x, (x % 2 == 0) ? 10 : 9));
1794
1795 p.close();
1796
1797 const size_t nv = p.size();
1798 ASSERT_GE(nv, 50u);
1799
1802
1803 size_t count = 0;
1804 for (DynList<Triangle>::Iterator it(tris); it.has_curr(); it.next_ne())
1805 ++count;
1806
1807 EXPECT_EQ(count, nv - 2);
1808}
1809
1810
1811// ---------- 5.5 Cross-algorithm comparison: 5 convex hulls ----------
1812
1814{
1816 pts.append(Point(0, 0));
1817 pts.append(Point(5, 0));
1818 pts.append(Point(6, 3));
1819 pts.append(Point(3, 6));
1820 pts.append(Point(0, 4));
1821 pts.append(Point(2, 1)); // interior
1822 pts.append(Point(3, 2)); // interior
1823
1829
1835
1841
1842 // All must have the same vertex count.
1843 ASSERT_EQ(v_andrew.size(), v_graham.size())
1844 << "Andrew vs Graham vertex count mismatch";
1845 ASSERT_EQ(v_andrew.size(), v_brute.size())
1846 << "Andrew vs BruteForce vertex count mismatch";
1847 ASSERT_EQ(v_andrew.size(), v_gift.size())
1848 << "Andrew vs GiftWrapping vertex count mismatch";
1849 ASSERT_EQ(v_andrew.size(), v_quick.size())
1850 << "Andrew vs QuickHull vertex count mismatch";
1851
1852 // All must have the same vertices.
1853 for (size_t i = 0; i < v_andrew.size(); ++i)
1854 {
1856 << "Andrew vs Graham mismatch at index " << i;
1857 EXPECT_EQ(v_andrew(i), v_brute(i))
1858 << "Andrew vs BruteForce mismatch at index " << i;
1859 EXPECT_EQ(v_andrew(i), v_gift(i))
1860 << "Andrew vs GiftWrapping mismatch at index " << i;
1861 EXPECT_EQ(v_andrew(i), v_quick(i))
1862 << "Andrew vs QuickHull mismatch at index " << i;
1863 }
1864}
1865
1866
1868{
1869 // 100 points, mix of grid + interior + boundary.
1871 for (int x = 0; x <= 10; ++x)
1872 for (int y = 0; y <= 10; ++y)
1873 pts.append(Point(x, y));
1874
1875 // Add some extra interior points.
1876 pts.append(Point(5, 5));
1877 pts.append(Point(3, 7));
1878 pts.append(Point(8, 2));
1879
1885
1891
1897
1898 ASSERT_EQ(v_andrew.size(), v_graham.size());
1899 ASSERT_EQ(v_andrew.size(), v_brute.size());
1900 ASSERT_EQ(v_andrew.size(), v_gift.size());
1901 ASSERT_EQ(v_andrew.size(), v_quick.size());
1902
1903 for (size_t i = 0; i < v_andrew.size(); ++i)
1904 {
1905 EXPECT_EQ(v_andrew(i), v_graham(i));
1906 EXPECT_EQ(v_andrew(i), v_brute(i));
1907 EXPECT_EQ(v_andrew(i), v_gift(i));
1908 EXPECT_EQ(v_andrew(i), v_quick(i));
1909 }
1910}
1911
1912
1914{
1915 // Many collinear points on the hull boundary.
1917 for (int x = 0; x <= 20; ++x)
1918 {
1919 pts.append(Point(x, 0)); // bottom
1920 pts.append(Point(x, 10)); // top
1921 }
1922 pts.append(Point(0, 5)); // left
1923 pts.append(Point(20, 5)); // right
1924
1928
1932
1936
1937 // For collinear points, algorithms may differ on whether they include
1938 // intermediate points. Compare just the extreme corners.
1943
1948
1953}
1954
1955
1957{
1958 // All points on hull (triangle) — all algorithms must agree.
1960 pts.append(Point(0, 0));
1961 pts.append(Point(10, 0));
1962 pts.append(Point(5, 8));
1963
1969
1975
1976 ASSERT_EQ(v_andrew.size(), 3u);
1977 ASSERT_EQ(v_graham.size(), 3u);
1978 ASSERT_EQ(v_brute.size(), 3u);
1979 ASSERT_EQ(v_gift.size(), 3u);
1980 ASSERT_EQ(v_quick.size(), 3u);
1981
1982 for (size_t i = 0; i < 3; ++i)
1983 {
1984 EXPECT_EQ(v_andrew(i), v_graham(i));
1985 EXPECT_EQ(v_andrew(i), v_brute(i));
1986 EXPECT_EQ(v_andrew(i), v_gift(i));
1987 EXPECT_EQ(v_andrew(i), v_quick(i));
1988 }
1989}
1990
1991
1992// ============================================================================
1993// Section 5.1 — Tests for new algorithms
1994// ============================================================================
1995
1996// ---------- Delaunay O(n log n) — randomized incremental ----------
1997
1999{
2001 pts.append(Point(0, 0));
2002 pts.append(Point(4, 0));
2003 pts.append(Point(4, 4));
2004 pts.append(Point(0, 4));
2005
2007 auto r = delaunay(pts);
2008
2009 EXPECT_EQ(r.sites.size(), 4u);
2010 EXPECT_EQ(r.triangles.size(), 2u);
2011}
2012
2013
2015{
2017 pts.append(Point(0, 0));
2018 pts.append(Point(5, 0));
2019 pts.append(Point(5, 5));
2020 pts.append(Point(0, 5));
2021 pts.append(Point(2, 3));
2022 pts.append(Point(3, 1));
2023
2025 auto r = delaunay(pts);
2026
2027 EXPECT_GE(r.triangles.size(), 4u);
2028
2029 for (size_t t = 0; t < r.triangles.size(); ++t)
2030 {
2031 const auto & tri = r.triangles(t);
2032 const Point cc = circumcenter_of(r.sites(tri.i), r.sites(tri.j),
2033 r.sites(tri.k));
2034 const Geom_Number r2 = dist2(cc, r.sites(tri.i));
2035 for (size_t s = 0; s < r.sites.size(); ++s)
2036 {
2037 if (s == tri.i || s == tri.j || s == tri.k)
2038 continue;
2039 EXPECT_GE(dist2(cc, r.sites(s)), r2)
2040 << "Delaunay incremental: site " << s
2041 << " violates circumcircle of triangle " << t;
2042 }
2043 }
2044}
2045
2046
2048{
2050 pts.append(Point(0, 0));
2051 pts.append(Point(10, 0));
2052 pts.append(Point(10, 10));
2053 pts.append(Point(0, 10));
2054 pts.append(Point(5, 5));
2055 pts.append(Point(3, 7));
2056 pts.append(Point(7, 2));
2057 pts.append(Point(1, 3));
2058
2060 auto rbw = bw(pts);
2061
2063 auto rinc = inc(pts);
2064
2065 EXPECT_EQ(rbw.sites.size(), rinc.sites.size());
2066 EXPECT_EQ(rbw.triangles.size(), rinc.triangles.size());
2067}
2068
2069
2071{
2073 auto r = delaunay({Point(0, 0), Point(1, 0), Point(0, 1)});
2074 EXPECT_EQ(r.sites.size(), 3u);
2075 EXPECT_EQ(r.triangles.size(), 1u);
2076}
2077
2078
2080{
2082 pts.append(Point(0, 0));
2083 pts.append(Point(1, 0));
2084 pts.append(Point(2, 0));
2085 pts.append(Point(3, 0));
2086
2088 auto r = delaunay(pts);
2089
2090 EXPECT_EQ(r.triangles.size(), 0u);
2091}
2092
2093
2095{
2097 pts.append(Point(0, 0));
2098 pts.append(Point(1, 0));
2099 pts.append(Point(0, 1));
2100 pts.append(Point(0, 0));
2101 pts.append(Point(1, 0));
2102
2104 auto r = delaunay(pts);
2105 EXPECT_EQ(r.sites.size(), 3u);
2106 EXPECT_EQ(r.triangles.size(), 1u);
2107}
2108
2109
2111{
2113 for (int x = 0; x <= 4; ++x)
2114 for (int y = 0; y <= 4; ++y)
2115 pts.append(Point(x, y));
2116
2118 auto r = delaunay(pts);
2119
2120 EXPECT_EQ(r.sites.size(), 25u);
2121 EXPECT_GE(r.triangles.size(), 32u);
2122
2123 for (size_t t = 0; t < r.triangles.size(); ++t)
2124 {
2125 const auto & tri = r.triangles(t);
2126 const Point cc = circumcenter_of(r.sites(tri.i), r.sites(tri.j),
2127 r.sites(tri.k));
2128 const Geom_Number cr2 = dist2(cc, r.sites(tri.i));
2129 for (size_t s = 0; s < r.sites.size(); ++s)
2130 {
2131 if (s == tri.i || s == tri.j || s == tri.k)
2132 continue;
2133 EXPECT_GE(dist2(cc, r.sites(s)), cr2);
2134 }
2135 }
2136}
2137
2138
2139// ---------- VoronoiDiagramFortune ----------
2140
2142{
2144 auto r = voronoi({Point(0, 0), Point(4, 0), Point(4, 4), Point(0, 4)});
2145
2146 EXPECT_EQ(r.sites.size(), 4u);
2147 EXPECT_GE(r.vertices.size(), 1u);
2148 EXPECT_GE(r.edges.size(), 1u);
2149}
2150
2151
2153{
2155 pts.append(Point(0, 0));
2156 pts.append(Point(6, 0));
2157 pts.append(Point(3, 5));
2158 pts.append(Point(6, 5));
2159 pts.append(Point(0, 5));
2160
2162 auto r = voronoi(pts);
2163
2164 for (size_t e = 0; e < r.edges.size(); ++e)
2165 {
2166 const auto & edge = r.edges(e);
2167 if (edge.unbounded) continue;
2168
2169 const Geom_Number d_u = dist2(edge.src, r.sites(edge.site_u));
2170 const Geom_Number d_v = dist2(edge.src, r.sites(edge.site_v));
2171 EXPECT_EQ(d_u, d_v) << "Voronoi edge src not equidistant for edge " << e;
2172 }
2173}
2174
2175
2177{
2179 pts.append(Point(1, 1));
2180 pts.append(Point(3, 1));
2181 pts.append(Point(2, 3));
2182
2183 Polygon clip;
2184 clip.add_vertex(Point(0, 0));
2185 clip.add_vertex(Point(4, 0));
2186 clip.add_vertex(Point(4, 4));
2187 clip.add_vertex(Point(0, 4));
2188 clip.close();
2189
2191 auto cells = voronoi.clipped_cells(pts, clip);
2192
2193 EXPECT_EQ(cells.size(), 3u);
2194 for (size_t i = 0; i < cells.size(); ++i)
2195 EXPECT_TRUE(cells(i).polygon.is_closed());
2196}
2197
2198
2200{
2202 pts.append(Point(0, 0));
2203 pts.append(Point(6, 0));
2204 pts.append(Point(7, 3));
2205 pts.append(Point(2, 7));
2206 pts.append(Point(-1, 4));
2207
2210
2211 const auto dual = dual_voronoi(pts);
2212 const auto fortune = fortune_voronoi(pts);
2213
2214 EXPECT_EQ(fortune.sites.size(), dual.sites.size());
2215 EXPECT_EQ(fortune.vertices.size(), dual.vertices.size());
2216 EXPECT_EQ(fortune.edges.size(), dual.edges.size());
2217 EXPECT_EQ(fortune.cells.size(), dual.cells.size());
2218}
2219
2220
2221// ---------- ConvexPolygonDecomposition ----------
2222
2224{
2225 Polygon p;
2226 p.add_vertex(Point(0, 0));
2227 p.add_vertex(Point(4, 0));
2228 p.add_vertex(Point(2, 3));
2229 p.close();
2230
2232 auto parts = decomp(p);
2233
2234 EXPECT_EQ(parts.size(), 1u);
2235 EXPECT_TRUE(parts(0).is_closed());
2236}
2237
2238
2239// ---------- SweepLineSegmentIntersection: new critical tests ----------
2240
2242{
2243 // Two collinear overlapping segments on the x-axis.
2244 // Segments [0,4] and [2,6] overlap on [2,4].
2247 segs.append(Segment(Point(0, 0), Point(4, 0)));
2248 segs.append(Segment(Point(2, 0), Point(6, 0)));
2249
2250 auto result = sweep(segs);
2251
2252 // Overlap must be reported at least at one overlap boundary point.
2253 EXPECT_GE(result.size(), 1u)
2254 << "Collinear overlapping segments were not reported";
2255}
2256
2257
2259{
2260 // 10 segments all sharing endpoint (5,5), fanning out.
2263 const size_t N = 10;
2264 for (size_t i = 0; i < N; ++i)
2265 {
2266 Geom_Number angle = Geom_Number(2) * Geom_Number(M_PI) * Geom_Number(static_cast<long>(i))
2267 / Geom_Number(static_cast<long>(N));
2268 Point far(Geom_Number(5) + Geom_Number(10) * Geom_Number(cos(angle.get_d())),
2269 Geom_Number(5) + Geom_Number(10) * Geom_Number(sin(angle.get_d())));
2270 segs.append(Segment(Point(5, 5), far));
2271 }
2272
2273 auto result = sweep(segs);
2274
2275 // C(10,2) = 45 pairwise intersections, all at (5,5).
2276 EXPECT_EQ(result.size(), N * (N - 1) / 2);
2277 for (size_t i = 0; i < result.size(); ++i)
2278 EXPECT_EQ(result(i).point, Point(5, 5));
2279}
2280
2281
2283{
2284 // Mix of vertical, horizontal, and diagonal segments.
2287
2288 segs.append(Segment(Point(3, 0), Point(3, 6))); // vertical
2289 segs.append(Segment(Point(0, 3), Point(6, 3))); // horizontal
2290 segs.append(Segment(Point(0, 0), Point(6, 6))); // diagonal
2291
2292 auto result = sweep(segs);
2293
2294 // vertical x horizontal at (3,3), vertical x diagonal at (3,3),
2295 // horizontal x diagonal at (3,3).
2296 EXPECT_EQ(result.size(), 3u);
2297 for (size_t i = 0; i < result.size(); ++i)
2298 EXPECT_EQ(result(i).point, Point(3, 3));
2299}
2300
2301
2303{
2304 // 10K random short segments. Verify each reported intersection is real.
2307
2308 std::mt19937 rng(999);
2309 std::uniform_real_distribution<double> coord(-100.0, 100.0);
2310 std::uniform_real_distribution<double> delta(-5.0, 5.0);
2311
2312 const size_t N = 10000;
2313 for (size_t i = 0; i < N; ++i)
2314 {
2315 double x = coord(rng), y = coord(rng);
2316 double dx = delta(rng), dy = delta(rng);
2317 if (dx == 0 and dy == 0) dx = 1;
2319 Point(Geom_Number(x + dx), Geom_Number(y + dy))));
2320 }
2321
2322 auto result = sweep(segs);
2323
2324 // Spot-check first 100 intersections: the reported point must lie on
2325 // both participating segments (within tolerance).
2326 const size_t check = std::min(result.size(), (size_t) 100);
2327 for (size_t i = 0; i < check; ++i)
2328 {
2329 const auto & ix = result(i);
2330 const Segment & s1 = segs(ix.seg_i);
2331 const Segment & s2 = segs(ix.seg_j);
2332
2333 // Verify the two segments actually intersect.
2334 EXPECT_TRUE(s1.intersects_with(s2))
2335 << "seg " << ix.seg_i << " and seg " << ix.seg_j
2336 << " reported as intersecting but don't";
2337 }
2338}
size_t size_t int32_t * out
Definition ca-c-api.h:120
Andrew's monotonic chain convex hull algorithm.
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:250
Brute force convex hull algorithm.
Closest pair of points via divide and conquer.
Decompose a simple polygon into convex parts using Hertel-Mehlhorn.
Distance between two closed convex polygons using GJK.
Polygon triangulation using the ear-cutting algorithm.
Exact Delaunay triangulation using the Bowyer-Watson incremental algorithm.
static DynList< Triangle > as_triangles(const Result &result)
Convert indexed triangulation to geometric triangles.
O(n log n) expected-time Delaunay's triangulation.
Iterator on the items of list.
Definition htlist.H:1420
Doubly-linked list (defined in tpl_dynList.H).
Definition htlist.H:1155
T & append(const T &item)
Definition htlist.H:1271
static Array< Point > extract_vertices(const Polygon &poly)
Extract vertices from a polygon into an array for indexed access.
Gift wrapping (Jarvis march) convex hull algorithm.
Graham scan convex hull algorithm.
bool has_curr() const noexcept
Definition htlist.H:930
Spatial point index for O(log n) nearest-neighbor queries.
static KDTreePointSearch build(const Array< Point > &points, const Geom_Number &xmin, const Geom_Number &ymin, const Geom_Number &xmax, const Geom_Number &ymax)
Build a balanced KD-tree from a point array.
Exact Minkowski sum of two closed convex polygons.
O(n log n) triangulation of simple polygons via y-monotone partition + linear-time monotone triangula...
static bool contains(const Polygon &poly, const Point &p)
Return true if the point is inside or on the boundary.
Represents a point with rectangular coordinates in a 2D plane.
Definition point.H:221
const Geom_Number & get_x() const noexcept
Gets the x-coordinate value.
Definition point.H:448
const Geom_Number & get_y() const noexcept
Gets the y-coordinate value.
Definition point.H:457
Geom_Number distance_squared_to(const Point &that) const
Calculates the squared Euclidean distance to another point.
Definition point.H:1490
Iterator over the edges (segments) of a polygon.
Definition polygon.H:521
bool has_curr() const
Check if there is a current segment.
Definition polygon.H:538
A general (irregular) 2D polygon defined by a sequence of vertices.
Definition polygon.H:247
void add_vertex(const Point &point)
Add a vertex to the polygon.
Definition polygon.H:678
void close()
Close the polygon.
Definition polygon.H:843
const bool & is_closed() const
Check if the polygon is closed.
Definition polygon.H:474
const size_t & size() const
Get the number of vertices.
Definition polygon.H:478
QuickHull convex hull algorithm.
Rotating calipers metrics for convex polygons.
Dedicated exact intersection for a single pair of segments.
Represents a line segment between two points.
Definition point.H:837
Point project(const Point &p) const
Orthogonal projection of a point onto this segment's infinite line, clamped to the segment's endpoint...
Definition point.H:1161
Report all pairwise intersection points among a set of segments.
Fortune sweep-line Voronoi construction.
Array< ClippedCell > clipped_cells(const DynList< Point > &pts, const Polygon &clip) const
Compute Voronoi cells clipped to a bounding polygon.
Voronoi diagram derived as the dual of a Delaunay triangulation.
O(n log n) Voronoi diagram construction.
static mt19937 rng
#define N
Definition fib.C:294
static Array< Point > sorted_hull_vertices(const Polygon &p)
static Point first_vertex_of(const Polygon &poly)
static Geom_Number brute_convex_distance_squared(const Polygon &p, const Polygon &q, Point &out_p, Point &out_q)
static Geom_Number dist2(const Point &a, const Point &b)
TEST_F(GeomAlgorithmsTest, ClosestPairEmptyInputThrows)
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_cos_function > > cos(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4080
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_sin_function > > sin(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4081
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
static mpfr_t y
Definition mpfr_mul_d.c:3
void add_polygon_vertices(SvgScene &scene, const ::Polygon &poly, const bool as_highlight=false)
std::filesystem::path emit_case_svg(const std::string &case_id, const SvgScene &scene, const std::string &note="")
Geom_Number area_of_parallelogram(const Point &a, const Point &b, const Point &c)
Compute the signed area of the parallelogram defined by vectors a->b and a->c.
Definition point.H:2886
size_t size(Node *root) noexcept
and
Check uniqueness with explicit hash + equality functors.
mpq_class Geom_Number
Numeric type used by the geometry module.
Definition point.H:113
SegmentSegmentIntersection::Result segment_segment_intersection(const Segment &s1, const Segment &s2)
Convenience free-function wrapper for SegmentSegmentIntersection.
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
void quicksort_op(C< T > &a, const Compare &cmp=Compare(), const size_t threshold=Quicksort_Threshold)
Optimized quicksort for containers using operator().
Iterator over the vertices of a polygon.
Definition polygon.H:490
bool check(GT &g)
ValueArg< size_t > seed
Definition testHash.C:53
gsl_rng * r