194 for (
int i = 0; i < 50; ++i)
196 seed = (
seed * 1103515245 + 12345) & 0x7fffffff;
198 seed = (
seed * 1103515245 + 12345) & 0x7fffffff;
223 const Point p = it.get_current_vertex();
262 EXPECT_EQ(result.kind, SegmentSegmentIntersection::Kind::NONE);
273 EXPECT_EQ(result.kind, SegmentSegmentIntersection::Kind::POINT);
285 EXPECT_EQ(result.kind, SegmentSegmentIntersection::Kind::POINT);
295 const auto result =
pair_ix(a, b);
299 scene.segments.append(b);
303 "case_segment_segment_overlap_o1",
scene,
304 "Dedicated O(1) segment-segment overlap");
306 EXPECT_EQ(result.kind, SegmentSegmentIntersection::Kind::OVERLAP);
318 EXPECT_EQ(result.kind, SegmentSegmentIntersection::Kind::OVERLAP);
330 EXPECT_EQ(result.kind, SegmentSegmentIntersection::Kind::POINT);
341 EXPECT_EQ(result.kind, SegmentSegmentIntersection::Kind::POINT);
352 EXPECT_EQ(result.kind, SegmentSegmentIntersection::Kind::NONE);
363 EXPECT_EQ(result.kind, SegmentSegmentIntersection::Kind::POINT);
373 EXPECT_EQ(result.kind, SegmentSegmentIntersection::Kind::POINT);
436 for (
size_t i = 0; i <
segs.size(); ++i)
438 for (
size_t i = 0; i < result.size(); ++i)
439 scene.highlighted_points.append(result(i).point);
441 "case_sweepline_multiple_intersections",
scene,
442 "Sweep-line / multi-intersection degeneracy");
448 for (
size_t i = 0; i < result.size(); ++i)
500 for (
size_t i = 0; i < result.size(); ++i)
810 for (
size_t i = 0; i <
rv.size(); ++i)
814 rv((i + 2) %
rv.size()));
815 if (turn == 0)
continue;
816 int s = turn > 0 ? 1 : -1;
825 return it.get_current_vertex();
844 if (
sp.intersects_with(
itq.get_current_segment()))
858 for (
size_t i = 0; i <
pv.size(); ++i)
859 for (
size_t j = 0; j <
qv.size(); ++j)
873 for (
size_t i = 0; i <
qv.size(); ++i)
874 for (
size_t j = 0; j <
pv.size(); ++j)
910 const auto r =
gjk(a, b);
914 EXPECT_EQ(
r.closest_on_first.distance_squared_to(
r.closest_on_second),
936 const auto r =
gjk(a, b);
960 const auto r =
gjk(a, b);
982 const auto ab =
gjk(a, b);
983 const auto ba =
gjk(b, a);
985 EXPECT_EQ(ab.distance_squared,
ba.distance_squared);
1025 for (
int tc = 0;
tc < 12; ++
tc)
1028 while (a.
size() < 3)
1031 for (
int i = 0; i < 16; ++i)
1033 seed = (
seed * 1103515245 + 12345) & 0x7fffffff;
1034 const int x = (
seed % 51) - 25;
1035 seed = (
seed * 1103515245 + 12345) & 0x7fffffff;
1036 const int y = (
seed % 51) - 25;
1042 while (b.
size() < 3)
1045 for (
int i = 0; i < 16; ++i)
1047 seed = (
seed * 1103515245 + 12345) & 0x7fffffff;
1048 const int x = (
seed % 51) - 25;
1049 seed = (
seed * 1103515245 + 12345) & 0x7fffffff;
1050 const int y = (
seed % 51) - 25;
1056 const auto r =
gjk(a, b);
1091 auto n =
kd.nearest(
Point(12, 12));
1095 auto n2 =
kd.nearest(
Point(48, 52));
1104 auto n =
kd.nearest(
Point(50, 50));
1112 for (
int x = 0; x < 10; ++x)
1113 for (
int y = 0;
y < 10; ++
y)
1120 for (
int x = 0; x < 10; ++x)
1121 for (
int y = 0;
y < 10; ++
y)
1124 auto n =
kd.nearest(
Point(5, 5));
1139 kd.range(5, 5, 25, 25, &
out);
1156 kd.for_each([&visited](
const Point &) { ++visited; });
1168 const auto snap =
kd.debug_snapshot();
1177 for (
size_t i = 0; i <
snap.partitions.size(); ++i)
1178 if (
snap.partitions(i).is_leaf)
1209 v.
append(it.get_current_vertex());
1238 for (
size_t t = 0; t <
r.triangles.size(); ++t)
1240 const auto & tri =
r.triangles(t);
1241 const Point & a =
r.sites(tri.i);
1242 const Point & b =
r.sites(tri.j);
1243 const Point & c =
r.sites(tri.k);
1249 for (
size_t s = 0; s <
r.sites.size(); ++s)
1251 if (s == tri.i
or s == tri.j
or s == tri.k)
1257 <<
"Site " << s <<
" violates empty-circumcircle for triangle "
1268 for (
int x = 0; x < 5; ++x)
1269 for (
int y = 0;
y < 5; ++
y)
1273 auto r = delaunay(
pts);
1277 for (
size_t t = 0; t <
r.triangles.size(); ++t)
1279 const auto & tri =
r.triangles(t);
1280 const Point & a =
r.sites(tri.i);
1281 const Point & b =
r.sites(tri.j);
1282 const Point & c =
r.sites(tri.k);
1286 for (
size_t s = 0; s <
r.sites.size(); ++s)
1288 if (s == tri.i
or s == tri.j
or s == tri.k)
1308 for (
size_t t = 0; t <
dt.triangles.size(); ++t)
1310 const auto & tri =
dt.triangles(t);
1311 const Point & a =
dt.sites(tri.i);
1312 const Point & b =
dt.sites(tri.j);
1313 const Point & c =
dt.sites(tri.k);
1320 EXPECT_EQ(
da,
db) <<
"Triangle " << t <<
": circumcenter not equidistant";
1321 EXPECT_EQ(
db, dc) <<
"Triangle " << t <<
": circumcenter not equidistant";
1337 for (
size_t e = 0; e <
r.edges.size(); ++e)
1339 const auto & edge =
r.edges(e);
1347 <<
"Edge " << e <<
" src not equidistant to sites";
1352 <<
"Edge " << e <<
" tgt not equidistant to sites";
1375 auto r = delaunay(
pts);
1380 for (
size_t i = 0; i <
r.triangles.size(); ++i)
1382 const auto & t =
r.triangles(i);
1388 "case_robust_near_collinear_delaunay",
scene,
1389 "Delaunay robustness / near-collinear");
1395 for (
size_t t = 0; t <
r.triangles.size(); ++t)
1397 const auto & tri =
r.triangles(t);
1401 for (
size_t s = 0; s <
r.sites.size(); ++s)
1403 if (s == tri.i
or s == tri.j
or s == tri.k)
1433 "case_robust_near_collinear_hull",
scene,
1434 "Convex hull robustness / near-collinear");
1478 for (
size_t i = 0; i <
segs.size(); ++i)
1480 for (
size_t i = 0; i < result.size(); ++i)
1481 scene.highlighted_points.append(result(i).point);
1483 "case_robust_near_parallel_converging",
scene,
1484 "Near-parallel segments / converging intersection");
1507 auto r = delaunay(
pts);
1511 for (
size_t t = 0; t <
r.triangles.size(); ++t)
1513 const auto & tri =
r.triangles(t);
1517 for (
size_t s = 0; s <
r.sites.size(); ++s)
1519 if (s == tri.i
or s == tri.j
or s == tri.k)
1539 auto r = delaunay(
pts);
1569 auto r = delaunay(
pts);
1574 for (
size_t i = 0; i <
r.triangles.size(); ++i)
1576 const auto & t =
r.triangles(i);
1582 "case_robust_cocircular_delaunay",
scene,
1583 "Delaunay robustness / cocircular points");
1620 auto r1 = delaunay(
pts1);
1621 auto r2 = delaunay(
pts2);
1622 auto r3 = delaunay(
pts3);
1627 EXPECT_EQ(
r1.triangles.size(), r2.triangles.size());
1638 for (
size_t i = 0; i <
ct1.size(); ++i)
1674 for (
size_t i = 0; i < v1.size(); ++i)
1697 EXPECT_EQ(
r1.distance_squared, r2.distance_squared);
1709 for (
int x = 0; x < 100; ++x)
1710 for (
int y = 0;
y < 100; ++
y)
1730 for (
int x = 0; x < 50; ++x)
1731 for (
int y = 0;
y < 100; ++
y)
1745 for (
int x = 0; x < 25; ++x)
1746 for (
int y = 0;
y < 20; ++
y)
1750 auto r = delaunay(
pts);
1755 const size_t check_limit =
r.triangles.size() < 50 ?
r.triangles.size() : 50;
1758 const auto & tri =
r.triangles(t);
1762 for (
size_t s = 0; s <
r.sites.size(); ++s)
1764 if (s == tri.i
or s == tri.j
or s == tri.k)
1779 for (
int x = 0; x <= 50; ++x)
1783 for (
int x = 50; x >= 0; --x)
1788 const size_t nv = p.
size();
1835 <<
"Andrew vs Graham vertex count mismatch";
1837 <<
"Andrew vs BruteForce vertex count mismatch";
1839 <<
"Andrew vs GiftWrapping vertex count mismatch";
1841 <<
"Andrew vs QuickHull vertex count mismatch";
1844 for (
size_t i = 0; i <
v_andrew.size(); ++i)
1847 <<
"Andrew vs Graham mismatch at index " << i;
1849 <<
"Andrew vs BruteForce mismatch at index " << i;
1851 <<
"Andrew vs GiftWrapping mismatch at index " << i;
1853 <<
"Andrew vs QuickHull mismatch at index " << i;
1862 for (
int x = 0; x <= 10; ++x)
1863 for (
int y = 0;
y <= 10; ++
y)
1894 for (
size_t i = 0; i <
v_andrew.size(); ++i)
1908 for (
int x = 0; x <= 20; ++x)
1973 for (
size_t i = 0; i < 3; ++i)
1998 auto r = delaunay(
pts);
2016 auto r = delaunay(
pts);
2020 for (
size_t t = 0; t <
r.triangles.size(); ++t)
2022 const auto & tri =
r.triangles(t);
2026 for (
size_t s = 0; s <
r.sites.size(); ++s)
2028 if (s == tri.i || s == tri.j || s == tri.k)
2031 <<
"Delaunay incremental: site " << s
2032 <<
" violates circumcircle of triangle " << t;
2079 auto r = delaunay(
pts);
2095 auto r = delaunay(
pts);
2104 for (
int x = 0; x <= 4; ++x)
2105 for (
int y = 0;
y <= 4; ++
y)
2109 auto r = delaunay(
pts);
2114 for (
size_t t = 0; t <
r.triangles.size(); ++t)
2116 const auto & tri =
r.triangles(t);
2120 for (
size_t s = 0; s <
r.sites.size(); ++s)
2122 if (s == tri.i || s == tri.j || s == tri.k)
2155 for (
size_t e = 0; e <
r.edges.size(); ++e)
2157 const auto & edge =
r.edges(e);
2158 if (edge.unbounded)
continue;
2162 EXPECT_EQ(
d_u,
d_v) <<
"Voronoi edge src not equidistant for edge " << e;
2185 for (
size_t i = 0; i < cells.size(); ++i)
2245 <<
"Collinear overlapping segments were not reported";
2254 const size_t N = 10;
2255 for (
size_t i = 0; i <
N; ++i)
2268 for (
size_t i = 0; i < result.size(); ++i)
2288 for (
size_t i = 0; i < result.size(); ++i)
2299 std::mt19937
rng(999);
2300 std::uniform_real_distribution<double>
coord(-100.0, 100.0);
2301 std::uniform_real_distribution<double> delta(-5.0, 5.0);
2303 const size_t N = 10000;
2304 for (
size_t i = 0; i <
N; ++i)
2307 double dx = delta(
rng), dy = delta(
rng);
2308 if (dx == 0
and dy == 0) dx = 1;
2317 const size_t check = std::min(result.size(), (
size_t) 100);
2318 for (
size_t i = 0; i <
check; ++i)
2320 const auto &
ix = result(i);
2326 <<
"seg " <<
ix.seg_i <<
" and seg " <<
ix.seg_j
2327 <<
" reported as intersecting but don't";
Andrew's monotonic chain convex hull algorithm.
Simple dynamic array with automatic resizing and functional operations.
T & append(const T &data)
Append a copy of data
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.
bool has_curr() const noexcept
Return true if the iterator has current item.
Iterator on the items of list.
Dynamic singly linked list with functional programming support.
T & append(const T &item)
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
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.
const Geom_Number & get_x() const noexcept
Gets the x-coordinate value.
const Geom_Number & get_y() const noexcept
Gets the y-coordinate value.
Geom_Number distance_squared_to(const Point &that) const
Calculates the squared Euclidean distance to another point.
Iterator over the edges (segments) of a polygon.
bool has_curr() const
Check if there is a current segment.
A general (irregular) 2D polygon defined by a sequence of vertices.
void add_vertex(const Point &point)
Add a vertex to the polygon.
void close()
Close the polygon.
const bool & is_closed() const
Check if the polygon is closed.
const size_t & size() const
Get the number of vertices.
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.
Point project(const Point &p) const
Orthogonal projection of a point onto this segment's infinite line, clamped to the segment's endpoint...
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 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)
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_sin_function > > sin(const __gmp_expr< T, U > &expr)
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 ¬e="")
and
Check uniqueness with explicit hash + equality functors.
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.
size_t size(Node *root) noexcept
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
mpq_class Geom_Number
Numeric type used by the geometry module.
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.
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.
::Array<::Segment > segments