Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
geom_algorithms_test_trapezoidal_map.cc
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
32
33using namespace Aleph;
34
37
38// ---- helpers ----
39
41{
42 size_t count = 0;
43 for (size_t i = 0; i < res.trapezoids.size(); ++i)
44 if (res.trapezoids(i).active) ++count;
45 return count;
46}
47
50{
51 Polygon p;
52 p.add_vertex(Point(cx - half, cy - half));
53 p.add_vertex(Point(cx + half, cy - half));
54 p.add_vertex(Point(cx + half, cy + half));
55 p.add_vertex(Point(cx - half, cy + half));
56 p.close();
57 return p;
58}
59
61{
62 Polygon p;
63 p.add_vertex(Point(0, 0));
64 p.add_vertex(Point(6, 0));
65 p.add_vertex(Point(3, 6));
66 p.close();
67 return p;
68}
69
71{
72 // Regular-ish hexagon centered at (5,5) with radius ~4.
73 Polygon p;
74 p.add_vertex(Point(9, 5));
75 p.add_vertex(Point(7, 9));
76 p.add_vertex(Point(3, 9));
77 p.add_vertex(Point(1, 5));
78 p.add_vertex(Point(3, 1));
79 p.add_vertex(Point(7, 1));
80 p.close();
81 return p;
82}
83
84// ============================================================================
85// Basic tests
86// ============================================================================
87
89{
91 segs.append(Segment(Point(0, 0), Point(4, 2)));
92
93 TM tm;
94 auto res = tm(segs);
95
96 // Should have at least 4 active trapezoids:
97 // left of src, above, below, right of tgt.
99
100 // Query above the segment.
101 auto loc = res.locate(Point(2, 4));
102 EXPECT_EQ(loc.type, LT::TRAPEZOID);
103
104 // Query below the segment.
105 loc = res.locate(Point(2, -2));
106 EXPECT_EQ(loc.type, LT::TRAPEZOID);
107
108 // Query left of the segment.
109 loc = res.locate(Point(-2, 0));
110 EXPECT_EQ(loc.type, LT::TRAPEZOID);
111
112 // Query right of the segment.
113 loc = res.locate(Point(6, 1));
114 EXPECT_EQ(loc.type, LT::TRAPEZOID);
115}
116
118{
120 segs.append(Segment(Point(0, 0), Point(4, 0)));
121 segs.append(Segment(Point(0, 2), Point(4, 2)));
122
123 TM tm;
124 auto res = tm(segs);
125
126 // Query between the two segments.
127 auto loc = res.locate(Point(2, 1));
128 EXPECT_EQ(loc.type, LT::TRAPEZOID);
129
130 // Query above both.
131 loc = res.locate(Point(2, 4));
132 EXPECT_EQ(loc.type, LT::TRAPEZOID);
133
134 // Query below both.
135 loc = res.locate(Point(2, -2));
136 EXPECT_EQ(loc.type, LT::TRAPEZOID);
137}
138
140{
141 // V-shape: two segments sharing a common left endpoint.
143 segs.append(Segment(Point(0, 0), Point(4, 2)));
144 segs.append(Segment(Point(0, 0), Point(4, -2)));
145
146 TM tm;
147 auto res = tm(segs);
148
149 // Query inside the V (between the two segments, right of origin).
150 auto loc = res.locate(Point(2, 0));
151 EXPECT_EQ(loc.type, LT::TRAPEZOID);
152
153 // Query at the shared endpoint.
154 loc = res.locate(Point(0, 0));
155 EXPECT_EQ(loc.type, LT::ON_POINT);
156}
157
159{
160 Polygon sq = make_square_poly(2, 2, 2);
161
162 TM tm;
163 auto res = tm(sq);
164
165 // Inside the square.
166 EXPECT_TRUE(res.contains(Point(2, 2)));
167 EXPECT_TRUE(res.contains(Point(1, 1)));
168 EXPECT_TRUE(res.contains(Point(3, 3)));
169
170 // Outside the square.
171 EXPECT_FALSE(res.contains(Point(5, 5)));
172 EXPECT_FALSE(res.contains(Point(-1, -1)));
173 EXPECT_FALSE(res.contains(Point(2, 5)));
174
175 // On boundary — contains() detects boundary correctly.
176 EXPECT_TRUE(res.contains(Point(0, 2)));
177}
178
180{
182
183 TM tm;
184 auto res = tm(tri);
185
186 // Centroid (2,2) is inside.
187 EXPECT_TRUE(res.contains(Point(2, 2)));
188 EXPECT_TRUE(res.contains(Point(3, 1)));
189
190 // Outside.
191 EXPECT_FALSE(res.contains(Point(0, 6)));
192 EXPECT_FALSE(res.contains(Point(-1, -1)));
193
194 // Vertex.
195 auto loc = res.locate(Point(0, 0));
196 EXPECT_EQ(loc.type, LT::ON_POINT);
197
198 // Edge midpoint — contains() always detects boundary correctly.
199 // Note: locate() may return TRAPEZOID for boundary points depending
200 // on the random DAG structure, so we test contains() instead.
201 EXPECT_TRUE(res.contains(Point(3, 0)));
202}
203
204// ============================================================================
205// Degeneracy tests
206// ============================================================================
207
209{
211 segs.append(Segment(Point(2, 0), Point(2, 4)));
212
213 TM tm;
214 auto res = tm(segs);
215
216 // Left of the vertical segment.
217 auto loc = res.locate(Point(0, 2));
218 EXPECT_EQ(loc.type, LT::TRAPEZOID);
219
220 // Right of the vertical segment.
221 loc = res.locate(Point(4, 2));
222 EXPECT_EQ(loc.type, LT::TRAPEZOID);
223}
224
226{
227 // Star pattern: 4 segments from common origin.
228 const Point center(0, 0);
230 segs.append(Segment(center, Point(4, 0)));
231 segs.append(Segment(center, Point(0, 4)));
232 segs.append(Segment(center, Point(-4, 0)));
233 segs.append(Segment(center, Point(0, -4)));
234
235 TM tm;
236 auto res = tm(segs);
237
238 // Origin is a shared endpoint.
239 auto loc = res.locate(center);
240 EXPECT_EQ(loc.type, LT::ON_POINT);
241
242 // Each quadrant should be reachable.
243 loc = res.locate(Point(2, 2));
244 EXPECT_EQ(loc.type, LT::TRAPEZOID);
245 loc = res.locate(Point(-2, 2));
246 EXPECT_EQ(loc.type, LT::TRAPEZOID);
247 loc = res.locate(Point(-2, -2));
248 EXPECT_EQ(loc.type, LT::TRAPEZOID);
249 loc = res.locate(Point(2, -2));
250 EXPECT_EQ(loc.type, LT::TRAPEZOID);
251}
252
254{
256 segs.append(Segment(Point(0, 0), Point(4, 0)));
257
258 TM tm;
259 auto res = tm(segs);
260
261 // Point on the segment interior.
262 auto loc = res.locate(Point(2, 0));
263 EXPECT_EQ(loc.type, LT::ON_SEGMENT);
264}
265
267{
269 segs.append(Segment(Point(0, 0), Point(4, 2)));
270
271 TM tm;
272 auto res = tm(segs);
273
274 auto loc = res.locate(Point(0, 0));
275 EXPECT_EQ(loc.type, LT::ON_POINT);
276
277 loc = res.locate(Point(4, 2));
278 EXPECT_EQ(loc.type, LT::ON_POINT);
279}
280
282{
284 segs.append(Segment(Point(0, 3), Point(6, 3)));
285
286 TM tm;
287 auto res = tm(segs);
288
289 // Above.
290 auto loc = res.locate(Point(3, 5));
291 EXPECT_EQ(loc.type, LT::TRAPEZOID);
292
293 // Below.
294 loc = res.locate(Point(3, 1));
295 EXPECT_EQ(loc.type, LT::TRAPEZOID);
296
297 // On.
298 loc = res.locate(Point(3, 3));
299 EXPECT_EQ(loc.type, LT::ON_SEGMENT);
300}
301
302// ============================================================================
303// Correctness / stress tests
304// ============================================================================
305
307{
308 Polygon hex = make_hexagon();
309
310 TM tm;
311 auto res = tm(hex);
312
313 // Cross-validate with PointInPolygonWinding for a grid of points.
314 for (int x = 0; x <= 10; ++x)
315 for (int y = 0; y <= 10; ++y)
316 {
317 Point p(x, y);
318 const bool tm_inside = res.contains(p);
319 const auto winding_loc = PointInPolygonWinding::locate(hex, p);
320 const bool winding_inside =
321 winding_loc != PointInPolygonWinding::Location::Outside;
322
324 << "Mismatch at (" << x << ", " << y << "): "
325 << "tm=" << tm_inside << " winding=" << winding_inside;
326 }
327}
328
330{
331 // Two non-overlapping triangles.
332 Polygon t1;
333 t1.add_vertex(Point(0, 0));
334 t1.add_vertex(Point(4, 0));
335 t1.add_vertex(Point(2, 3));
336 t1.close();
337
338 Polygon t2;
339 t2.add_vertex(Point(6, 0));
340 t2.add_vertex(Point(10, 0));
341 t2.add_vertex(Point(8, 3));
342 t2.close();
343
345 polys.append(t1);
346 polys.append(t2);
347
348 TM tm;
349 auto res = tm(polys);
350
351 // Inside first triangle.
352 EXPECT_TRUE(res.contains(Point(2, 1)));
353 // Inside second triangle.
354 EXPECT_TRUE(res.contains(Point(8, 1)));
355 // Between the two triangles.
356 EXPECT_FALSE(res.contains(Point(5, 1)));
357 // Outside both.
358 EXPECT_FALSE(res.contains(Point(5, 5)));
359}
360
362{
363 // Build segments from a square and a triangle.
365
366 // Square (0,0)-(4,0)-(4,4)-(0,4).
367 segs.append(Segment(Point(0, 0), Point(4, 0)));
368 segs.append(Segment(Point(4, 0), Point(4, 4)));
369 segs.append(Segment(Point(4, 4), Point(0, 4)));
370 segs.append(Segment(Point(0, 4), Point(0, 0)));
371
372 // Triangle (6,0)-(10,0)-(8,4).
373 segs.append(Segment(Point(6, 0), Point(10, 0)));
374 segs.append(Segment(Point(10, 0), Point(8, 4)));
375 segs.append(Segment(Point(8, 4), Point(6, 0)));
376
377 // Diamond (3,6)-(5,8)-(3,10)-(1,8).
378 segs.append(Segment(Point(3, 6), Point(5, 8)));
379 segs.append(Segment(Point(5, 8), Point(3, 10)));
380 segs.append(Segment(Point(3, 10), Point(1, 8)));
381 segs.append(Segment(Point(1, 8), Point(3, 6)));
382
383 TM tm;
384 auto res = tm(segs);
385
386 // The map should have more than the initial bounding-box trapezoid.
388
389 // All 10+ unique points should be locatable.
390 for (size_t i = 0; i < res.num_input_points; ++i)
391 {
392 auto loc = res.locate(res.points(i));
393 EXPECT_TRUE(loc.type == LT::ON_POINT or loc.type == LT::ON_SEGMENT)
394 << "Point " << i << " not located as ON_POINT or ON_SEGMENT";
395 }
396}
397
399{
400 // Build the same map twice and ensure query results match.
401 Polygon sq = make_square_poly(2, 2, 2);
402
403 TM tm;
404 auto res1 = tm(sq);
405 auto res2 = tm(sq);
406
407 // Test a grid of points — both maps should agree on contains().
408 for (int x = -1; x <= 5; ++x)
409 for (int y = -1; y <= 5; ++y)
410 {
411 Point p(x, y);
412 EXPECT_EQ(res1.contains(p), res2.contains(p))
413 << "Non-deterministic result at (" << x << ", " << y << ")";
414 }
415}
416
417// ============================================================================
418// Edge cases
419// ============================================================================
420
422{
424
425 TM tm;
426 auto res = tm(segs);
427
428 // Should have exactly 1 active trapezoid (the bounding box).
430
431 // Any point query should return TRAPEZOID.
432 auto loc = res.locate(Point(0, 0));
433 EXPECT_EQ(loc.type, LT::TRAPEZOID);
434}
435
437{
439 segs.append(Segment(Point(1, 1), Point(1, 1)));
440
441 TM tm;
442 EXPECT_THROW(tm(segs), std::domain_error);
443}
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:139
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:245
static Location locate(const Polygon &poly, const Point &p)
Classify point location with respect to a polygon.
Represents a point with rectangular coordinates in a 2D plane.
Definition point.H:229
A general (irregular) 2D polygon defined by a sequence of vertices.
Definition polygon.H:246
void add_vertex(const Point &point)
Add a vertex to the polygon.
Definition polygon.H:677
void close()
Close the polygon.
Definition polygon.H:840
Represents a line segment between two points.
Definition point.H:827
O(log n) point location via trapezoidal map with DAG search.
LocationType
Classification of a point location query result.
#define TEST(name)
static Polygon make_square_poly(Geom_Number cx, Geom_Number cy, Geom_Number half)
static size_t count_active_trapezoids(const TM::Result &res)
static Polygon make_hexagon()
static Polygon make_triangle_poly()
static mpfr_t y
Definition mpfr_mul_d.c:3
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
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.
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
The trapezoidal map and DAG search structure.