VIPRA Documentation
Loading...
Searching...
No Matches
polygon.hpp
1#pragma once
2
3#include <array>
4#include <cassert>
5#include <cstddef>
6#include <limits>
7#include <random>
8#include <vector>
9
10#include "vipra/geometry/f3d.hpp"
11#include "vipra/geometry/line.hpp"
12
13#include "vipra/geometry/rectangle.hpp"
14#include "vipra/geometry/triangle.hpp"
15#include "vipra/random/random.hpp"
16namespace VIPRA::Geometry {
17
18class Polygon {
19 public:
20 auto random_point(VIPRA::Random::Engine& engine) const noexcept -> f3d;
21
22 [[nodiscard]] auto is_point_inside(f3d point) const noexcept -> bool;
23 [[nodiscard]] auto bounding_box() const noexcept -> Rectangle;
24 [[nodiscard]] auto center() const noexcept -> f3d;
25 [[nodiscard]] auto points() const noexcept -> std::vector<f3d> const&
26 {
27 return _points;
28 }
29 [[nodiscard]] auto sides() const noexcept -> std::vector<Line>;
30
31 private:
32 std::vector<f3d> _points;
33
34 public:
35 explicit Polygon(std::vector<f3d> const& points) : _points(points) {}
36
37 explicit Polygon(std::vector<f3d>&& points) : _points(std::move(points)) {}
38
39 template <size_t point_s>
40 explicit Polygon(std::array<f3d, point_s> const& points)
41 {
42 _points.resize(point_s);
43 std::copy(points.begin(), points.end(), _points.begin());
44 }
45
46 template <size_t point_s>
47 explicit Polygon(std::array<f3d, point_s>&& points)
48 {
49 _points.resize(point_s);
50 std::copy(points.begin(), points.end(), _points.begin());
51 }
52
53 explicit Polygon(std::vector<Line> const& lines)
54 {
55 _points.resize(lines.size());
56 for ( size_t i = 0; i < lines.size(); ++i ) {
57 _points[i] = lines[i].start;
58 }
59 }
60
61 explicit Polygon(std::vector<Line>&& lines)
62 {
63 _points.resize(lines.size());
64 for ( size_t i = 0; i < lines.size(); ++i ) {
65 _points[i] = lines[i].start;
66 }
67 }
68
69 template <size_t point_s>
70 explicit Polygon(std::array<Line, point_s> const& lines)
71 {
72 _points.resize(point_s);
73 for ( size_t i = 0; i < point_s; ++i ) {
74 _points[i] = lines[i].start;
75 }
76 }
77
78 template <size_t point_s>
79 explicit Polygon(std::array<Line, point_s>&& lines)
80 {
81 _points.resize(point_s);
82 for ( size_t i = 0; i < point_s; ++i ) {
83 _points[i] = lines[i].start;
84 }
85 }
86
87 ~Polygon() = default;
88 Polygon() = default;
89 Polygon(Polygon const&) = default;
90 auto operator=(Polygon const&) -> Polygon& = default;
91 Polygon(Polygon&&) noexcept = default;
92 auto operator=(Polygon&&) noexcept -> Polygon& = default;
93};
94
95// ---------------------------------- IMPLEMENTATION ------------------------------------------------
96
97inline auto Polygon::is_point_inside(f3d point) const noexcept -> bool
98{
99 if ( _points.size() < 3 ) return false; // A polygon must have at least 3 vertices
100
101 bool inside = false;
102 for ( size_t i = 0, j = _points.size() - 1; i < _points.size(); j = i++ ) {
103 f_pnt xi = _points[i].x;
104 f_pnt yi = _points[i].y;
105 f_pnt xj = _points[j].x;
106 f_pnt yj = _points[j].y;
107
108 // Check if the point is on an edge
109 if ( (yi > point.y) != (yj > point.y) ) {
110 f_pnt intersectX = (xj - xi) * (point.y - yi) / (yj - yi) + xi;
111 if ( point.x < intersectX ) inside = ! inside;
112 }
113 }
114 return inside;
115}
116
117inline auto Polygon::center() const noexcept -> f3d
118{
119 const auto sideList = sides();
120 f3d center;
121
122 std::for_each(sideList.begin(), sideList.end(),
123 [&](Line const& edge) { center += edge.start; });
124
125 return center /= sideList.size();
126}
127
128inline auto Polygon::random_point(VIPRA::Random::Engine& engine) const noexcept -> f3d
129{
130 // TODO(rolland): this is disgusting
131
132 auto box = bounding_box();
133
134 std::uniform_real_distribution<VIPRA::f_pnt> xDist{box.sides()[3].end.x,
135 box.sides()[3].start.x};
136 std::uniform_real_distribution<VIPRA::f_pnt> yDist{box.sides()[2].end.y,
137 box.sides()[2].start.y};
138
139 f3d point;
140
141 do {
142 point.x = xDist(engine);
143 point.y = yDist(engine);
144 } while ( ! box.is_point_inside(point) );
145 return point;
146}
147
148inline auto Polygon::bounding_box() const noexcept -> Rectangle
149{
150 f3d botLeft{std::numeric_limits<VIPRA::f_pnt>::max(),
151 std::numeric_limits<VIPRA::f_pnt>::max()};
152 f3d topRight{std::numeric_limits<VIPRA::f_pnt>::min(),
153 std::numeric_limits<VIPRA::f_pnt>::min()};
154 for ( auto const& point : _points ) {
155 topRight.x = std::max(topRight.x, point.x);
156 topRight.y = std::max(topRight.y, point.y);
157 botLeft.x = std::min(botLeft.x, point.x);
158 botLeft.y = std::min(botLeft.y, point.y);
159 }
160
161 const f3d topLeft = f3d{botLeft.x, topRight.y};
162 const f3d botRight = f3d{topRight.x, botLeft.y};
163
164 return Rectangle{botLeft, topLeft, topRight, botRight};
165}
166
167inline auto Polygon::sides() const noexcept -> std::vector<Line>
168{
169 std::vector<Line> lines;
170 lines.resize(_points.size());
171
172 for ( size_t i = 0; i < _points.size() - 1; ++i ) {
173 lines[i] = Line{_points[i], _points[i + 1]};
174 }
175 lines.back() = Line{_points.back(), _points.front()};
176
177 return lines;
178}
179
180} // namespace VIPRA::Geometry
Definition rectangle.hpp:20
Psuedo Random number engine.
Definition random.hpp:22
Definition line.hpp:11
Definition f3d.hpp:27