header_utils
Loading...
Searching...
No Matches
rec2.h
1
4
5#pragma once
6
7#include "min-cpp-version/cpp17.h"
8#include <glm/common.hpp>
9#include <glm/vec2.hpp>
10#include "span.h"
11#include <functional>
12#include <optional>
13
14#define GHPL_HAS_REC2
15
17
18namespace ghassanpl
19{
20 template <template<typename> typename HASHER, typename FIRST, typename... T>
21 [[nodiscard]] constexpr size_t hash(FIRST&& first, T&&... values);
22
23 namespace geometry
24 {
25 static inline constexpr struct bounding_box_for_t {} bounding_box_for;
26
29 template <typename T>
30 struct trec2
31 {
32 glm::tvec2<T> p1 = { 0,0 };
33 glm::tvec2<T> p2 = { 0,0 };
34
35 using tvec = glm::tvec2<T>;
36 using value_type = T;
37
38 constexpr trec2() noexcept = default;
39 constexpr trec2(tvec a, tvec b) noexcept : p1(a), p2(b) {}
40 explicit constexpr trec2(span<tvec const> points) noexcept
41 {
42 *this = trec2::exclusive();
43 for (auto& p : points)
44 include(p);
45 }
46
47 template <GHPL_TYPENAME(std::same_as<tvec>)... ARGS>
48 explicit constexpr trec2(bounding_box_for_t, ARGS&&... args) noexcept
49 {
50 *this = trec2::exclusive();
51 (this->include(std::forward<ARGS>(args)), ...);
52 }
53
54 constexpr explicit trec2(tvec a) noexcept : p1(), p2(a) {}
55 constexpr trec2(T x1, T y1, T x2, T y2) noexcept : p1(x1, y1), p2(x2, y2) {}
56 constexpr trec2(const trec2&) noexcept = default;
57 constexpr trec2(trec2&&) noexcept = default;
58 template <typename U>
59 constexpr explicit trec2(const trec2<U>& other) noexcept : p1(glm::tvec2<U>(other.p1)), p2(glm::tvec2<U>(other.p2)) {}
60 template <typename U>
61 constexpr explicit trec2(trec2<U>&& other) noexcept : p1(glm::tvec2<U>(other.p1)), p2(glm::tvec2<U>(other.p2)) {}
62
63 constexpr trec2& operator=(const trec2&) noexcept = default;
64 constexpr trec2& operator=(trec2&&) noexcept = default;
65
66 static constexpr trec2 from_points(span<tvec const> points) noexcept { return trec2{ points }; }
67 template <GHPL_TYPENAME(std::same_as<tvec>)... ARGS>
68 static constexpr trec2 from_points(ARGS&&... args) noexcept { return trec2(bounding_box_for, std::forward<ARGS>(args)...); }
69 static constexpr trec2 from_size(tvec s) noexcept { return { tvec{}, s }; };
70 static constexpr trec2 from_size(tvec p, tvec s) noexcept { return { p, p + s }; };
71 static constexpr trec2 from_size(T x, T y, T w, T h) noexcept { return { x, y, x + w, y + h }; };
72 static constexpr trec2 from_center_and_size(tvec p, tvec s) noexcept { return { p - s / T(2), p + s / T(2) }; };
73 static constexpr trec2 from_center_and_size(T x, T y, T w, T h) noexcept { return { x - w / T(2), y - h / T(2), x + w / T(2), y + h / T(2) }; };
74 static constexpr trec2 from_center_and_half_size(tvec p, tvec s) noexcept { return { p - s, p + s }; };
75 static constexpr trec2 from_center_and_half_size(T x, T y, T w, T h) noexcept { return { x - w, y - h, x + w, y + h }; };
76
77 static constexpr trec2 invalid() noexcept { return { T{1}, T{1}, T{-1}, T{-1} }; };
78 static constexpr trec2 exclusive() noexcept
79 {
80 if constexpr (std::numeric_limits<T>::has_infinity)
81 return { std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity(), -std::numeric_limits<T>::infinity(), -std::numeric_limits<T>::infinity() };
82 else
83 return { std::numeric_limits<T>::max(), std::numeric_limits<T>::max(), std::numeric_limits<T>::lowest(), std::numeric_limits<T>::lowest() };
84 }
85
86 static constexpr trec2 inclusive() noexcept
87 {
88 if constexpr (std::numeric_limits<T>::has_infinity)
89 return { -std::numeric_limits<T>::infinity(), -std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity() };
90 else
91 return { std::numeric_limits<T>::lowest(), std::numeric_limits<T>::lowest(), std::numeric_limits<T>::max(), std::numeric_limits<T>::max() };
92 }
93
94 constexpr trec2 operator+(tvec op) const noexcept { return { p1 + op, p2 + op }; }
95 constexpr trec2& operator+=(tvec op) noexcept { p1 += op; p2 += op; return *this; };
96 constexpr trec2 operator-(tvec op) const noexcept { return { p1 - op, p2 - op }; }
97 constexpr trec2& operator-=(tvec op) noexcept { p1 -= op; p2 -= op; return *this; };
98 constexpr trec2 operator*(T op) const noexcept { return { p1 * op, p2 * op }; }
99 constexpr trec2 operator/(T op) const noexcept { return { p1 / op, p2 / op }; }
100 constexpr trec2 operator*(tvec op) const noexcept { return { p1 * op, p2 * op }; }
101 constexpr trec2 operator/(tvec op) const noexcept { return { p1 / op, p2 / op }; }
102
103 constexpr bool operator==(trec2 const& other) const noexcept { return p1 == other.p1 && p2 == other.p2; }
105 //constexpr auto operator<=>(trec2 const& other) const noexcept = default;
106
107 constexpr tvec operator[](size_t i) const noexcept
108 {
109 switch (i % 4)
110 {
111 case 0: return p1;
112 case 1: return right_top();
113 case 2: return p2;
114 case 3: return left_bottom();
115 }
116 return {};
117 }
118
119#ifdef __cpp_explicit_this_parameter
120 template <typename U> struct values_t { U left; U top; U right; U bottom; };
121 template <typename U> values_t(U&& left, U&& top, U&& right, U&& bottom) -> values_t<U>;
122 template <typename SELF>
123 constexpr auto values(this SELF&& self) noexcept
124 {
125 return values_t{
126 std::forward_like<SELF>(self.p1.x),
127 std::forward_like<SELF>(self.p1.y),
128 std::forward_like<SELF>(self.p2.x),
129 std::forward_like<SELF>(self.p2.y)
130 };
131 }
132#endif
133
134 constexpr tvec size() const noexcept { return p2 - p1; }
135 constexpr tvec position() const noexcept { return p1; }
136 constexpr trec2& set_position(tvec pos) noexcept { p2 += pos - p1; p1 = pos; return *this; }
137 constexpr trec2& set_position(T x, T y) noexcept { p2.x += x - p1.x; p2.y += y - p1.y; p1 = { x, y }; return *this; }
138 constexpr trec2& set_x(T x) noexcept { p2.x += x - p1.x; p1.x = x; return *this; }
139 constexpr trec2& set_y(T y) noexcept { p2.y += y - p1.y; p1.y = y; return *this; }
140 constexpr trec2& set_size(tvec size) noexcept { p2 = p1 + size; return *this; }
141 constexpr trec2& set_size(T w, T h) noexcept { p2.x = p1.x + w; p2.y = p1.y + h; return *this; }
142
143 constexpr trec2& grow(T by) noexcept { p1.x -= by; p1.y -= by; p2.x += by; p2.y += by; return *this; }
144 constexpr trec2& shrink(T by) noexcept { return grow(-by); }
145 constexpr trec2& grow(tvec by) noexcept { p1 -= by; p2 += by; return *this; }
146 constexpr trec2& shrink(tvec by) noexcept { return grow(-by); }
147 constexpr trec2 grown(T by) const noexcept { auto copy = *this; copy.p1.x -= by; copy.p1.y -= by; copy.p2.x += by; copy.p2.y += by; return copy; }
148 constexpr trec2 shrunk(T by) const noexcept { return grown(-by); }
149 constexpr trec2 grown(tvec by) const noexcept { auto copy = *this; copy.p1 -= by; copy.p2 += by; return copy; }
150 constexpr trec2 shrunk(tvec by) const noexcept { return grown(-by); }
151 constexpr trec2& grow(T left, T top, T right, T bottom) noexcept { p1.x -= left; p1.y -= top; p2.x += right; p2.y += bottom; return *this; }
152 constexpr trec2& shrink(T left, T top, T right, T bottom) noexcept { return grow(-left, -top, -right, -bottom); }
153 constexpr trec2 grown(T left, T top, T right, T bottom) const noexcept { auto copy = *this; copy.p1.x -= left; copy.p1.y -= top; copy.p2.x += right; copy.p2.y += bottom; return copy; }
154 constexpr trec2 shrunk(T left, T top, T right, T bottom) const noexcept { return grown(-left, -top, -right, -bottom); }
155
156 constexpr trec2 at_position(tvec pos) const noexcept { auto copy = *this; copy.set_position(pos); return copy; }
157 constexpr trec2 at_position(T x, T y) const noexcept { auto copy = *this; copy.set_position(x, y); return copy; }
158 constexpr trec2 sized(tvec size) const noexcept { auto copy = *this; copy.set_size(size); return copy; }
159 constexpr trec2 sized(T w, T h) const noexcept { auto copy = *this; copy.set_size(w, h); return copy; }
160 constexpr trec2 translated(tvec op) const noexcept { return *this + op; }
161 constexpr trec2 translated(T x, T y) const noexcept { return *this + tvec{ x, y }; }
162 constexpr trec2 scaled(tvec op) const noexcept { return *this * op; }
163 constexpr trec2 scaled(T x, T y) const noexcept { return *this * tvec{ x, y }; }
164 constexpr trec2 scaled(T s) const noexcept { return *this * s; }
165 constexpr T width() const noexcept { return p2.x - p1.x; };
166 constexpr T height() const noexcept { return p2.y - p1.y; };
167 constexpr trec2& set_width(T w) noexcept { p2.x = p1.x + w; return *this; };
168 constexpr trec2& set_height(T h) noexcept { p2.y = p1.y + h; return *this; };
169 constexpr T x() const noexcept { return p1.x; }
170 constexpr T y() const noexcept { return p1.y; }
171 constexpr T left() const noexcept { return p1.x; }
172 constexpr T top() const noexcept { return p1.y; }
173 constexpr T right() const noexcept { return p2.x; }
174 constexpr T bottom() const noexcept { return p2.y; }
175 constexpr tvec left_top() const noexcept { return p1; }
176 constexpr tvec left_bottom() const noexcept { return { p1.x, p2.y }; }
177 constexpr tvec right_top() const noexcept { return { p2.x, p1.y }; }
178 constexpr tvec right_bottom() const noexcept { return p2; }
179 constexpr tvec half_size() const noexcept { return (p2 - p1) / T{ 2 }; }
180 constexpr tvec center() const noexcept { return p1 + half_size(); }
181 constexpr tvec centroid() const noexcept { return center(); }
182 constexpr trec2& set_center(tvec pos) noexcept { const auto hw = half_size(); p1 = pos - hw; p2 = pos + hw; return *this; }
183 constexpr trec2 at_center(tvec pos) const noexcept { auto copy = *this; copy.set_center(pos); return copy; }
184
185 constexpr trec2 local() const noexcept { return { tvec{}, size() }; }
186 constexpr trec2 relative_to(trec2 const& other) const noexcept { return { p1 - other.position(), p2 - other.position() }; }
187
188 constexpr trec2 to_global(trec2 const& parent_rect) const noexcept { return { p1 + parent_rect.position(), p2 + parent_rect.position() }; }
189
190 constexpr glm::vec2 to_rect_space(tvec world_space) const noexcept { return glm::vec2{ world_space - p1 } / glm::vec2{ size() }; }
191 constexpr tvec to_world_space(glm::vec2 rect_space) const noexcept { return tvec{ rect_space * glm::vec2{ size() } } + p1; }
192
193 constexpr trec2& include(tvec pt) noexcept { this->p1 = glm::min(this->p1, pt); this->p2 = glm::max(this->p2, pt); return *this; };
194 constexpr trec2& include(trec2 const& rec) noexcept { return this->include(rec.p1).include(rec.p2); };
195
196 constexpr trec2 including(tvec pt) const noexcept { return { glm::min(this->p1, pt), glm::max(this->p2, pt) }; };
197 constexpr trec2 including(trec2 const& rec) const noexcept { return this->include(rec.p1).include(rec.p2); };
198
199 constexpr bool intersects(trec2 const& other) const noexcept
200 {
201 return (left() <= other.right() && other.left() <= right() && top() <= other.bottom() && other.top() <= bottom());
202 }
203
204 constexpr trec2 intersection(trec2 const& other) const noexcept
205 {
206 auto x1 = std::max(std::min(p1.x, p2.x), std::min(other.p1.x, other.p2.x));
207 auto y1 = std::max(std::min(p1.y, p2.y), std::min(other.p1.y, other.p2.y));
208 auto x2 = std::min(std::max(p1.x, p2.x), std::max(other.p1.x, other.p2.x));
209 auto y2 = std::min(std::max(p1.y, p2.y), std::max(other.p1.y, other.p2.y));
210 return { x1,y1,x2,y2 };
211 }
212
213 constexpr trec2 clipped_to(trec2 const& other) const noexcept
214 {
215 return intersection(other);
216 }
217
218 constexpr trec2 constrained_to(trec2 const& other) const noexcept;
219
221
222 constexpr bool contains(glm::vec<2, T> const& other) const noexcept
223 {
224 return other.x >= p1.x && other.y >= p1.y && other.x < p2.x && other.y < p2.y;
225 }
226
228 constexpr bool contains(trec2 const& other) const noexcept
229 {
230 return other.p1.x >= p1.x && other.p1.y >= p1.y && other.p2.x <= p2.x && other.p2.y <= p2.y;
231 }
232
233 constexpr bool is_valid() const noexcept
234 {
235 return p1.x <= p2.x && p1.y <= p2.y;
236 }
237
238 constexpr trec2 valid() const noexcept
239 {
240 auto copy = *this;
241 if (copy.p1.x > copy.p2.x) std::swap(copy.p1.x, copy.p2.x);
242 if (copy.p1.y > copy.p2.y) std::swap(copy.p1.y, copy.p2.y);
243 return copy;
244 }
245
246 constexpr trec2& make_valid() noexcept
247 {
248 if (p1.x > p2.x) std::swap(p1.x, p2.x);
249 if (p1.y > p2.y) std::swap(p1.y, p2.y);
250 return *this;
251 }
252
253 constexpr std::pair<trec2, trec2> split_vertical(T top_height) const noexcept
254 {
255 if (top_height < 0)
256 top_height = this->height() + top_height;
257 return { trec2::from_size(this->p1, {this->width(), top_height}), trec2::from_size(this->p1 + tvec{0, top_height}, {this->width(), this->height() - top_height}) };
258 }
259
260 constexpr std::pair<trec2, trec2> split_horizontal(T left_width) const noexcept
261 {
262 if (left_width < 0)
263 left_width = this->width() + left_width;
264 return { trec2::from_size(this->p1, {left_width, this->height()}), trec2::from_size(this->p1 + tvec{left_width, 0}, {this->width() - left_width, this->height()}) };
265 }
266
267 constexpr std::array<tvec, 4> split_on(tvec split_center) const noexcept;
268 constexpr std::array<tvec, 4> split_on(trec2 const& removed_portion) const noexcept;
269
270 constexpr trec2 lerp(trec2 const& other, float factor) const noexcept;
271
272 constexpr T calculate_area() const noexcept { return width() * height(); }
273
274 constexpr T edge_length() const noexcept { return (width() + height()) * 2; }
275
276 constexpr glm::vec2 edge_point_alpha(double edge_progress) const
277 {
278 edge_progress = glm::fract(edge_progress);
279 const auto w = width();
280 const auto h = height();
281 const auto el = (w + h) * 2;
282 const auto d = static_cast<T>(edge_progress * el);
283 if (d < w)
284 return glm::mix(this->left_top(), this->right_top(), d / w);
285 else if (d < w + h)
286 return glm::mix(this->right_top(), this->right_bottom(), (d - w) / h);
287 else if (d < w + h + w)
288 return glm::mix(this->right_bottom(), this->left_bottom(), (d - (w + h)) / w);
289 else
290 return glm::mix(this->left_bottom(), this->left_top(), (d - (w + h + w)) / h);
291 }
292
293 constexpr glm::vec2 point_alpha(glm::vec2 alpha) const
294 {
295 return glm::vec2{ this->p1 } + alpha * glm::vec2{ this->size() };
296 }
297
298 constexpr glm::vec2 edge_point(double edge_pos) const
299 {
300 const auto w = width();
301 const auto h = height();
302 edge_pos = fmod(edge_pos, (w + h) * 2);
303 if (edge_pos < w)
304 return glm::mix(this->left_top(), this->right_top(), edge_pos / w);
305 else if (edge_pos < w + h)
306 return glm::mix(this->right_top(), this->right_bottom(), (edge_pos - w) / h);
307 else if (edge_pos < w + h + w)
308 return glm::mix(this->right_bottom(), this->left_bottom(), (edge_pos - (w + h)) / w);
309 else
310 return glm::mix(this->left_bottom(), this->left_top(), (edge_pos - (w + h + w)) / h);
311 }
312
313 constexpr trec2 bounding_box() const noexcept
314 {
315 return *this;
316 }
317
318 constexpr glm::vec2 closest_point_to(glm::vec2 pt) const
319 {
320 const auto d = (pt - p1) / size();
321 const auto c = glm::clamp(d, glm::vec2{ 0 }, glm::vec2{ 1 });
322 return p1 + c * size();
323 }
324
325 template <typename FUNC>
326 GHPL_REQUIRES(std::is_invocable_v<FUNC, tvec>&& std::is_integral_v<T>)
327 constexpr void for_each_pixel(FUNC&& func) const
328 {
329 for (T y = p1.y; y < p2.y; ++y)
330 for (T x = p1.x; x < p2.x; ++x)
331 func(tvec{ x, y });
332 }
333
334 template <typename FUNC>
335 constexpr void for_each_edge(FUNC&& func) const
336 {
337 func(left_top(), right_top());
338 func(right_top(), right_bottom());
339 func(right_bottom(), left_bottom());
340 func(left_bottom(), left_top());
341 }
342
343 template <typename FUNC>
344 constexpr void for_each_vertex(FUNC&& func) const
345 {
346 func(left_top());
347 func(right_top());
348 func(right_bottom());
349 func(left_bottom());
350 }
351
352 constexpr size_t vertex_count() const noexcept { return 4; }
353 constexpr size_t edge_count() const noexcept { return 4; }
354
355 auto edge(size_t index) const -> std::optional<std::pair<glm::tvec2<T>, glm::tvec2<T>>>
356 {
357 switch (index)
358 {
359 case 0: return std::make_pair(left_top(), right_top());
360 case 1: return std::make_pair(right_top(), right_bottom());
361 case 2: return std::make_pair(right_bottom(), left_bottom());
362 case 3: return std::make_pair(left_bottom(), left_top());
363 }
364 return std::nullopt;
365 }
366
367 auto vertex(size_t index) const -> std::optional<tvec>
368 {
369 switch (index)
370 {
371 case 0: return left_top();
372 case 1: return right_top();
373 case 2: return right_bottom();
374 case 3: return left_bottom();
375 }
376 return std::nullopt;
377 }
378 };
379
380
381 template <typename T>
382 trec2<T> operator+(glm::tvec2<T> op, trec2<T> rec) noexcept { return { rec.p1 + op, rec.p2 + op }; }
383 }
384
385 using geometry::trec2;
386
387 template <typename T, typename STRINGIFIER>
388 auto stringify(STRINGIFIER& str, trec2<T>& b) { return str('[', b.p1.x, ',', b.p1.y, ',', b.p2.x, ',', b.p2.y, ']'); }
389 template <typename T, typename STRINGIFIER>
390 auto stringify(STRINGIFIER& str, trec2<T> const& b) { return str('[', b.p1.x, ',', b.p1.y, ',', b.p2.x, ',', b.p2.y, ']'); }
391}
392
393
394template <typename T>
395struct std::hash<ghassanpl::trec2<T>>
396{
397 std::size_t operator()(ghassanpl::trec2<T> const& s) const noexcept
398 {
399 return ghassanpl::hash(s.p1, s.p2);
400 }
401};
402
403#ifdef GHPL_HAS_ALIGN
404#include "align+rec2.h"
405#endif
Contains functions that return functors that perform an operation without output; appropriate for fun...
Definition functional.h:149
Primary namespace for everything in this library.
Definition align+rec2.h:10
Represents a 2D rectangle polygon_shape.
Definition rec2.h:31
constexpr tvec operator[](size_t i) const noexcept
TODO: Write this manually (what does it even mean to compare two rectangles?).
Definition rec2.h:107
constexpr bool contains(trec2 const &other) const noexcept
Definition rec2.h:228
constexpr bool contains(glm::vec< 2, T > const &other) const noexcept
translated by the minimum offset so that it fits inside other
Definition rec2.h:222
Represents a 2D rectangle polygon_shape.
Definition rec2.h:31