Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
geom_algorithms_test_offset.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#include <cmath>
33
34using namespace Aleph;
35
36// ---- helpers ----
37
39{
40 Polygon p;
41 p.add_vertex(Point(cx - half, cy - half));
42 p.add_vertex(Point(cx + half, cy - half));
43 p.add_vertex(Point(cx + half, cy + half));
44 p.add_vertex(Point(cx - half, cy + half));
45 p.close();
46 return p;
47}
48
50{
51 Polygon p;
52 p.add_vertex(Point(0, 0));
53 p.add_vertex(Point(6, 0));
54 p.add_vertex(Point(3, 6));
55 p.close();
56 return p;
57}
58
66{
67 Polygon p;
68 p.add_vertex(Point(0, 0));
69 p.add_vertex(Point(4, 0));
70 p.add_vertex(Point(4, 2));
71 p.add_vertex(Point(2, 2));
72 p.add_vertex(Point(2, 4));
73 p.add_vertex(Point(0, 4));
74 p.close();
75 return p;
76}
77
80{
81 Polygon p;
82 // Outer tip → inner notch, alternating at 5 points.
83 const double R = 5.0, r = 2.0;
84 for (int i = 0; i < 5; ++i)
85 {
86 double angle_out = M_PI / 2.0 + i * 2.0 * M_PI / 5.0;
87 double angle_in = angle_out + M_PI / 5.0;
88 p.add_vertex(Point(Geom_Number(R * std::cos(angle_out)),
89 Geom_Number(R * std::sin(angle_out))));
90 p.add_vertex(Point(Geom_Number(r * std::cos(angle_in)),
91 Geom_Number(r * std::sin(angle_in))));
92 }
93 p.close();
94 return p;
95}
96
99{
100 Polygon p;
101 const double R = 4.0;
102 for (int i = 0; i < 5; ++i)
103 {
104 double a = M_PI / 2.0 + i * 2.0 * M_PI / 5.0;
105 p.add_vertex(Point(Geom_Number(R * std::cos(a)),
106 Geom_Number(R * std::sin(a))));
107 }
108 p.close();
109 return p;
110}
111
114{
115 Polygon p;
116 p.add_vertex(Point(0, 0));
117 p.add_vertex(Point(10, 0));
118 p.add_vertex(Point(10, 1));
119 p.add_vertex(Point(0, 1));
120 p.close();
121 return p;
122}
123
124// ---- Tests ----
125
127{
128 Polygon sq = make_square(0, 0, 2); // side=4, area=16
130 auto result = off(sq, Geom_Number(1));
131 ASSERT_FALSE(result.is_empty());
132
133 // Outward offset by 1 on a 4×4 square → 6×6 → area = 36.
134 Geom_Number area = polygon_area(result.polygons(0));
135 EXPECT_EQ(area, 36);
136 EXPECT_EQ(result.polygons(0).size(), 4);
137}
138
140{
141 Polygon sq = make_square(0, 0, 2); // side=4, area=16
143 auto result = off(sq, Geom_Number(-1));
144 ASSERT_FALSE(result.is_empty());
145
146 // Inward offset by 1 on a 4×4 square → 2×2 → area = 4.
147 Geom_Number area = polygon_area(result.polygons(0));
148 EXPECT_EQ(area, 4);
149}
150
152{
153 Polygon tri = make_triangle();
155 auto result = off(tri, Geom_Number(1));
156 ASSERT_FALSE(result.is_empty());
157
159 Geom_Number new_area = polygon_area(result.polygons(0));
161}
162
164{
165 Polygon tri = make_triangle();
167 auto result = off(tri, Geom_Number(-1));
168 ASSERT_FALSE(result.is_empty());
169
171 Geom_Number new_area = polygon_area(result.polygons(0));
173}
174
176{
177 Polygon sq = make_square(0, 0, 2);
179 auto result = off(sq, Geom_Number(0));
180 ASSERT_EQ(result.size(), size_t(1));
181
182 Geom_Number area = polygon_area(result.polygons(0));
183 EXPECT_EQ(area, polygon_area(sq));
184}
185
187{
188 Polygon sq = make_square(0, 0, 2); // side=4
190 // Inward offset of 3 on a 4×4 square collapses it.
191 auto result = off(sq, Geom_Number(-3));
192 EXPECT_TRUE(result.is_empty());
193}
194
196{
199 auto result = off(L, Geom_Number(Geom_Number(1) / 2));
200 ASSERT_FALSE(result.is_empty());
201
203 Geom_Number new_area = polygon_area(result.polygons(0));
205}
206
208{
211 auto result = off(L, Geom_Number(Geom_Number(-1) / 4));
212 ASSERT_FALSE(result.is_empty());
213
215 Geom_Number new_area = polygon_area(result.polygons(0));
217}
218
220{
223 auto result = off(star, Geom_Number(Geom_Number(1) / 2));
224 ASSERT_FALSE(result.is_empty());
225
227 // Total offset area should be larger.
228 Geom_Number total = 0;
229 for (size_t i = 0; i < result.size(); ++i)
230 total += polygon_area(result.polygons(i));
232}
233
235{
237 shapes.append(make_square(0, 0, 2));
238 shapes.append(make_triangle());
239 shapes.append(make_pentagon());
240
242 for (size_t i = 0; i < shapes.size(); ++i)
243 {
244 auto result = off(shapes(i), Geom_Number(1));
245 ASSERT_FALSE(result.is_empty()) << "shape " << i;
246 EXPECT_GT(polygon_area(result.polygons(0)), polygon_area(shapes(i)))
247 << "shape " << i;
248 }
249}
250
252{
254 shapes.append(make_square(0, 0, 3));
255 shapes.append(make_triangle());
256 shapes.append(make_pentagon());
257
259 for (size_t i = 0; i < shapes.size(); ++i)
260 {
261 auto result = off(shapes(i), Geom_Number(-1));
262 ASSERT_FALSE(result.is_empty()) << "shape " << i;
263 EXPECT_LT(polygon_area(result.polygons(0)), polygon_area(shapes(i)))
264 << "shape " << i;
265 }
266}
267
269{
270 Polygon sq = make_square(0, 0, 3);
272
273 auto out = off(sq, Geom_Number(1));
274 ASSERT_FALSE(out.is_empty());
275 EXPECT_TRUE(is_ccw(out.polygons(0)));
276
277 auto in = off(sq, Geom_Number(-1));
278 ASSERT_FALSE(in.is_empty());
279 EXPECT_TRUE(is_ccw(in.polygons(0)));
280}
281
283{
284 Polygon sq = make_square(0, 0, 2);
286
287 auto miter = off(sq, Geom_Number(1), PolygonOffset::JoinType::Miter);
288 auto bevel = off(sq, Geom_Number(1), PolygonOffset::JoinType::Bevel);
289 ASSERT_FALSE(miter.is_empty());
290 ASSERT_FALSE(bevel.is_empty());
291
292 // Bevel produces more vertices (2 per corner vs 1).
293 EXPECT_GT(bevel.polygons(0).size(), miter.polygons(0).size());
294}
295
297{
298 Polygon sq = make_square(0, 0, 2);
300 auto result = off(sq, Geom_Number(1), PolygonOffset::JoinType::Bevel);
301 ASSERT_FALSE(result.is_empty());
302
303 // 4 corners × 2 vertices = 8 vertices.
304 EXPECT_EQ(result.polygons(0).size(), size_t(8));
305
306 // Area should be less than miter (corners are cut).
307 auto miter = off(sq, Geom_Number(1), PolygonOffset::JoinType::Miter);
308 EXPECT_LT(polygon_area(result.polygons(0)),
309 polygon_area(miter.polygons(0)));
310}
311
313{
314 // A very acute triangle where miter joins would be very long.
315 Polygon p;
316 p.add_vertex(Point(0, 0));
317 p.add_vertex(Point(10, 0));
318 p.add_vertex(Point(5, 1)); // very flat triangle
319 p.close();
320
322 // With miter_limit = 1, almost all corners get beveled.
323 auto result = off(p, Geom_Number(Geom_Number(1) / 4),
324 PolygonOffset::JoinType::Miter, Geom_Number(1));
325 ASSERT_FALSE(result.is_empty());
326
327 // With very high miter limit, pure miter.
328 auto result_high = off(p, Geom_Number(Geom_Number(1) / 4),
329 PolygonOffset::JoinType::Miter, Geom_Number(100));
330 ASSERT_FALSE(result_high.is_empty());
331
332 // Low miter limit should produce >= the vertices of high miter limit.
333 EXPECT_GE(result.polygons(0).size(), result_high.polygons(0).size());
334}
335
337{
338 Polygon sq = make_square(0, 0, 3);
340
342 for (int d = 1; d <= 3; ++d)
343 {
344 auto result = off(sq, Geom_Number(d));
345 ASSERT_FALSE(result.is_empty()) << "d=" << d;
346 Geom_Number a = polygon_area(result.polygons(0));
347 EXPECT_GT(a, prev_area) << "d=" << d;
348 prev_area = a;
349 }
350}
351
353{
354 Polygon sq = make_square(0, 0, 2);
356 auto gen_result = gen_off(sq, Geom_Number(1));
357 ASSERT_FALSE(gen_result.is_empty());
358
360
361 // Both should have the same area.
363}
364
366{
367 // Small triangle: side length ~1.4. Inward by 1 should collapse.
368 Polygon p;
369 p.add_vertex(Point(0, 0));
370 p.add_vertex(Point(1, 0));
371 p.add_vertex(Point(Geom_Number(1) / 2, 1));
372 p.close();
373
375 auto result = off(p, Geom_Number(-1));
376 EXPECT_TRUE(result.is_empty());
377}
378
380{
383
384 auto out = off(pent, Geom_Number(1));
385 ASSERT_FALSE(out.is_empty());
387
388 auto in = off(pent, Geom_Number(-1));
389 ASSERT_FALSE(in.is_empty());
390 EXPECT_LT(polygon_area(in.polygons(0)), polygon_area(pent));
391}
392
394{
397
398 // Inward by 0.25 should still leave a valid corridor.
399 auto result = off(corridor, Geom_Number(Geom_Number(-1) / 4));
400 ASSERT_FALSE(result.is_empty());
401 EXPECT_LT(polygon_area(result.polygons(0)), polygon_area(corridor));
402
403 // Inward by 1 should collapse (width=1, offset=1 → zero width).
404 auto collapsed = off(corridor, Geom_Number(-1));
405 EXPECT_TRUE(collapsed.is_empty());
406}
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 Polygon outward(const Polygon &convex_poly, const Geom_Number &distance)
Outward offset (dilation) of a convex polygon.
Represents a point with rectangular coordinates in a 2D plane.
Definition point.H:229
Offset (inflate/deflate) an arbitrary simple polygon.
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
static Polygon make_L_shape()
L-shaped polygon (non-convex, one reflex vertex).
static Polygon make_square(Geom_Number cx, Geom_Number cy, Geom_Number half)
static Polygon make_triangle()
TEST_F(GeomAlgorithmsTest, Offset_SquareOutward)
static Polygon make_narrow_corridor()
Narrow horizontal corridor: width 1, length 10.
static Polygon make_pentagon()
Regular pentagon (convex).
static Polygon make_star()
Star-shaped polygon with 5 reflex vertices.
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.
mpq_class Geom_Number
Numeric type used by the geometry module.
Definition point.H:115
static Geom_Number polygon_area(const Polygon &poly)
gsl_rng * r