header_utils
Loading...
Searching...
No Matches
geometry_common.h
1
4
5#pragma once
6
7#include "../align.h"
8#include "../constexpr_math.h"
9#include "../enum_flags.h"
10#include "../named.h"
11#include "../rec2.h"
12#include "../functional.h"
13
14#include <glm/geometric.hpp>
15#include <glm/trigonometric.hpp>
16#include <glm/fwd.hpp>
17//#include <glm/gtx/norm.hpp>
18
21
22namespace glm
23{
24 template <typename STRINGIFIER>
25 auto stringify(STRINGIFIER& str, glm::vec4& b) { return str('[', b.x, ',', b.y, ',', b.z, ',', b.w, ']'); }
26 template <typename STRINGIFIER>
27 auto stringify(STRINGIFIER& str, glm::vec4 const& b) { return str('[', b.x, ',', b.y, ',', b.z, ',', b.w, ']'); }
28
29 template <typename STRINGIFIER>
30 auto stringify(STRINGIFIER& str, glm::vec2& b) { return str('[', b.x, ',', b.y, ']'); }
31 template <typename STRINGIFIER>
32 auto stringify(STRINGIFIER& str, glm::vec2 const& b) { return str('[', b.x, ',', b.y, ']'); }
33
34 template <std::signed_integral T>
35 constexpr auto to_signed(glm::tvec2<T> v) noexcept { return v; }
36 template <std::unsigned_integral T>
37 constexpr auto to_signed(glm::tvec2<T> v) noexcept { return glm::tvec2<std::make_signed_t<T>>(v); }
38
39 template <std::unsigned_integral T>
40 constexpr auto to_unsigned(glm::tvec2<T> v) noexcept { return v; }
41 template <std::signed_integral T>
42 constexpr auto to_unsigned(glm::tvec2<T> v) noexcept { return glm::tvec2<std::make_unsigned_t<T>>(v); }
43
45 template <std::integral T, std::integral U>
46 constexpr auto abs_distance(glm::tvec2<T> a, glm::tvec2<U> b) noexcept { return glm::abs(to_signed(a) - to_signed(b)); }
47}
48
49namespace ghassanpl
50{
51 template <typename T, template <typename U, glm::qualifier Q = glm::defaultp> typename GLM_TYPE>
52 concept is_specialization_of = std::invocable<decltype([]<typename V>(GLM_TYPE<V> const&) {}), T>;
53}
54
55namespace ghassanpl::geometry
56{
57 template <typename T>
58 struct precision_limits;
59
60 template <>
61 struct precision_limits<double>
62 {
63 static constexpr double equivalent_point_max_distance = 0.00002;
64 static constexpr double equivalent_texel_max_distance = 1.0 / 1024.0;
65 static constexpr double near_point_distance = 0.015;
66 static constexpr double point_on_plane_max_distance = 0.1;
67 static constexpr double point_on_line_max_distance = 0.1;
68
69 static constexpr double foldable_vertex_max_distance = 0.0004;
70
71 static constexpr double cos_1_deg = 0.99984769515;
72 static constexpr double cos_89_deg = 0.01745240643;
73
74 static constexpr double min_dot_product_of_parallel_normals = cos_1_deg;
75 static constexpr double max_dot_product_of_perpendicular_normals = cos_1_deg;
76 };
77
78 using rec2 = trec2<float>;
79 using irec2 = trec2<int>;
80
81 enum class winding_order
82 {
83 clockwise,
84 counter_clockwise
85 };
86
87 template <typename T>
88 constexpr T dot(glm::tvec2<T> const& a, glm::tvec2<T> const& b) noexcept
89 {
90 if (std::is_constant_evaluated())
91 return a.x * b.x + a.y * b.y;
92 else
93 return glm::dot(a, b);
94 }
95
96 template <typename T>
97 constexpr glm::tvec2<T> length(glm::tvec2<T> const& a) noexcept
98 {
99 return cem::sqrt(geometry::dot(a, a));
100 }
101
102 template <typename T>
103 constexpr std::pair<glm::tvec2<T>, T> dir_and_length(glm::tvec2<T> const& a) noexcept
104 {
105 const auto len = geometry::length(a);
106 if (len >= std::numeric_limits<T>::epsilon())
107 return { a / len, len };
108 return { {}, len };
109 }
110
111 template <typename T>
112 constexpr glm::tvec2<T> with_length(glm::tvec2<T> const& a, T length) noexcept
113 {
114 const auto dl = geometry::dir_and_length(a);
115 return dl.first * length;
116 }
117
118 template <typename T>
119 constexpr glm::tvec2<T> clamp_length(glm::tvec2<T> const& a, T min, T max) noexcept
120 {
121 const auto lsq = geometry::dot(a, a);
122 const auto sqmin = min * min;
123 const auto sqmax = max * max;
124 if (lsq < sqmin || sqmax < lsq)
125 {
126 const auto clamped = glm::clamp(lsq, min * min, max * max);
127 return with_length(a, cem::sqrt(lsq));
128 }
129 return a;
130 }
131
132 template <typename T>
133 constexpr glm::tvec2<T> max_length(glm::tvec2<T> const& a, T max) noexcept
134 {
135 const auto lsq = geometry::dot(a, a);
136 const auto sqmax = max * max;
137 if (sqmax < lsq)
138 {
139 const auto clamped = glm::min(lsq, max * max);
140 return with_length(a, cem::sqrt(lsq));
141 }
142 return a;
143 }
144}
145
146namespace ghassanpl::geometry::normals
147{
152
154 template <typename T, size_t N>
155 static constexpr bool are_similar(glm::vec<N, T> const& a, glm::vec<N, T> const& b) {
156 constexpr auto min = precision_limits<T>::min_dot_product_of_parallel_normals;
157 return geometry::dot(a, b) >= min;
158 }
159
161 template <typename T, size_t N>
162 static constexpr bool are_parallel(glm::vec<N, T> const& a, glm::vec<N, T> const& b) {
163 constexpr auto min = precision_limits<T>::min_dot_product_of_parallel_normals;
164 return glm::abs(geometry::dot(a, b)) >= min;
165 }
166
168 template <typename T, size_t N>
169 static constexpr bool are_perpendicular(glm::vec<N, T> const& a, glm::vec<N, T> const& b) {
170 constexpr auto max = precision_limits<T>::max_dot_product_of_perpendicular_normals;
171 return glm::abs(geometry::dot(a, b)) <= max;
172 }
173
175}
176
177namespace ghassanpl::geometry
178{
182
183 template <std::floating_point T> using basic_radians_t = named<T, "radians", traits::displacement>;
184 template <std::floating_point T> using basic_degrees_t = named<T, "degrees", traits::displacement>;
185
186 template <std::floating_point T> using basic_heading_t = named<T, "heading", traits::location, traits::is_location_of<basic_degrees_t<T>>>;
187
188 using degrees = basic_degrees_t<float>; ghassanpl_named_float_literal(degrees, _deg);
189 using radians = basic_radians_t<float>; ghassanpl_named_float_literal(degrees, _rad);
190 using heading = basic_heading_t<float>; ghassanpl_named_float_literal(degrees, _head);
191
192 template <std::floating_point T>
193 constexpr basic_degrees_t<T> to_degrees(basic_radians_t<T> r) { return degrees{ glm::degrees(*r) }; }
194 template <std::floating_point T>
195 constexpr basic_radians_t<T> to_radians(basic_degrees_t<T> r) { return degrees{ glm::radians(*r) }; }
196
197 template <typename TARGET, std::floating_point T>
198 requires std::same_as<TARGET, basic_degrees_t<T>>
199 constexpr TARGET named_cast(basic_radians_t<T> const& radians)
200 {
201 return basic_degrees_t<T>{ glm::degrees(radians.value) };
202 }
203
204 template <typename TARGET, std::floating_point T>
205 requires std::same_as<TARGET, basic_radians_t<T>>
206 constexpr TARGET named_cast(basic_degrees_t<T> const& degrees)
207 {
208 return basic_radians_t<T>{ glm::radians(degrees.value) };
209 }
210
211 namespace angles
212 {
215
216 template <std::floating_point T>
217 constexpr basic_degrees_t<T> ensure_positive(basic_degrees_t<T> degrees) noexcept {
218 const auto deg = cem::fmod(degrees.get(), T(360));
219 return basic_degrees_t<T>{ (deg < T(0)) ? deg + T(360) : deg };
220 }
221
222 template <std::floating_point T>
223 constexpr basic_radians_t<T> ensure_positive(basic_radians_t<T> radians) noexcept {
224 const auto rad = cem::fmod(radians.get(), glm::radians(T(360)));
225 return basic_radians_t<T>{ (rad < T(0)) ? rad + glm::radians(T(360)) : rad };
226 }
227
228 inline constexpr auto full_circle = degrees{ 360.0f };
229 inline constexpr auto half_circle = degrees{ 360.0f / 2.0f };
230 inline constexpr auto quarter_circle = degrees{ 360.0f / 4.0f };
231
232 template <size_t NTH_SLICE, size_t SLICE_COUNT, degrees starting_at = degrees{ 0.0f } >
233 inline constexpr std::pair<degrees, degrees> circle_slice = {
234 ensure_positive(degrees{NTH_SLICE * (360.0f / SLICE_COUNT)} + starting_at),
235 ensure_positive(degrees{(NTH_SLICE + 1) * (360.0f / SLICE_COUNT)} + starting_at)
236 };
237
238 constexpr std::pair<degrees, degrees> get_circle_slice(size_t nth_slice, size_t slice_count, degrees starting_at = degrees{ 0.0f }) noexcept
239 {
240 return {
241 ensure_positive(degrees{nth_slice * (360.0f / slice_count)} + starting_at),
242 ensure_positive(degrees{(nth_slice + 1) * (360.0f / slice_count)} + starting_at)
243 };
244 }
245
247 }
248
250}
251
252#ifndef GLM_ENABLE_EXPERIMENTAL
253#define GLM_ENABLE_EXPERIMENTAL
254#endif
255#include <glm/gtx/polar_coordinates.hpp>
256#undef GLM_ENABLE_EXPERIMENTAL
257
258namespace ghassanpl::geometry /* ::polar */
259{
260 template <typename T>
261 using basic_polar2d_t = named<glm::tvec2<T>, "polar", traits::location>;
262
263 using polar2d = basic_polar2d_t<float>;
264
265 template <typename T> inline basic_radians_t<T> rho(basic_polar2d_t<T> const& polar) { return basic_radians_t<T>{ polar.value.x }; }
266 template <typename T> inline basic_radians_t<T> phi(basic_polar2d_t<T> const& polar) { return basic_radians_t<T>{ polar.value.y }; }
267 template <typename T> inline basic_radians_t<T> theta(basic_polar2d_t<T> const& polar) { return basic_radians_t<T>{ polar.value.y }; }
268
269 template <typename T>
270 basic_polar2d_t<T> polar(glm::tvec2<T> const& euclidean)
271 {
272 const auto r = std::hypot(euclidean.x, euclidean.y);
273 const auto t = glm::atan(euclidean.y, euclidean.x);
274 return basic_polar2d_t<T>{ r, t };
275 }
276
277 template <typename T>
278 basic_polar2d_t<T> polar_rho_phi(basic_radians_t<T> rho, basic_radians_t<T> phi)
279 {
280 return basic_polar2d_t<T>{ *rho, *phi };
281 }
282
283 template <typename T>
284 glm::tvec2<T> euclidean(basic_polar2d_t<T> const& polar)
285 {
286 const auto r = *rho(polar);
287 const auto t = *theta(polar);
288 return glm::tvec2<T>{ r * glm::cos(t), r * glm::sin(t) };
289 }
290}
291
292namespace ghassanpl::geometry
293{
297
299
300 template <typename T>
302 {
303 T a{};
304 T b{};
305 T c{};
306
307 using value_type = T;
308 using vec2 = glm::tvec2<T>;
309
310 template <typename U>
311 U distance(glm::tvec2<U> const& point)
312 {
313 return (a * point.x + b * point.y + c) / std::hypot(a, b);
314 }
315
316 template <typename U>
317 glm::tvec2<U> closest_point_to(glm::tvec2<U> const& point)
318 {
319 const auto d = glm::distance(point);
320 return point - glm::tvec2<T>{ a, b } * d;
321 }
322
324 int side(vec2 const& point)
325 {
326 const auto d = a * point.x + b * point.y + c;
327 return d > 0 ? 1 : d < 0 ? -1 : 0;
328 }
329 };
330
331
333 template <typename T>
334 basic_line_t<T> line_crossing_points(glm::tvec2<T> const& p1, glm::tvec2<T> const& p2)
335 {
336 return basic_line_t<T>{ p1.y - p2.y, p2.x - p1.x, p1.x * p2.y - p2.x * p1.y };
337 }
338
339 template <typename T>
340 basic_line_t<T> line_from_dir(glm::tvec2<T> const& dir)
341 {
342 return basic_line_t<T>{ dir.y, -dir.x, 0 };
343 }
344
346
347}
348
349namespace ghassanpl::geometry
350{
354
356 template <typename T>
357 struct interval
358 {
359 T min{};
360 T max{};
361
362 bool overlaps(interval const& other) const { return min <= other.max && other.min <= max; }
363 T overlap(interval const& other) const { return (std::abs(other.min - max) < std::abs(other.max - min)) ? other.min - max : other.max - min; }
364 };
365
366 template <typename T>
367 interval<T> projected_on_axis(glm::tvec2<T> axis, glm::tvec2<T> const& point)
368 {
369 const auto len = glm::dot(axis, point);
370 return { len, len };
371 }
372
374}
375/*
376template <typename T>
377struct std::formatter<glm::tvec2<T>> : std::formatter<std::string>
378{
379 template<class FormatContext>
380 auto format(glm::tvec2<T> const& p, FormatContext& ctx) const { return std::formatter<string>::format(std::format("[{}, {}]", p.x, p.y), ctx); }
381};
382
383template <typename T>
384struct std::formatter<glm::tvec3<T>> : std::formatter<std::string>
385{
386 template<class FormatContext>
387 auto format(glm::tvec3<T> const& p, FormatContext& ctx) const { return std::formatter<string>::format(std::format("[{}, {}, {}]", p.x, p.y, p.z), ctx); }
388};
389
390template <typename T>
391struct std::formatter<glm::tvec4<T>> : std::formatter<std::string>
392{
393 template<class FormatContext>
394 auto format(glm::tvec4<T> const& p, FormatContext& ctx) const { return std::formatter<string>::format(std::format("[{}, {}, {}, {}]", p.x, p.y, p.z, p.w), ctx); }
395};
396
397*/
basic_line_t< T > line_crossing_points(glm::tvec2< T > const &p1, glm::tvec2< T > const &p2)
TODO: Move these to the body as static methods?
#define ghassanpl_named_float_literal(named_name, suffix)
Creates a user-defined floating-point literal which will result in a given named type.
Definition named.h:408
static constexpr bool are_perpendicular(glm::vec< N, T > const &a, glm::vec< N, T > const &b)
Whether these vectors are (mostly) perpendicular.
static constexpr bool are_similar(glm::vec< N, T > const &a, glm::vec< N, T > const &b)
Whether these vectors are (mostly) parallel and point in the same direction.
static constexpr bool are_parallel(glm::vec< N, T > const &a, glm::vec< N, T > const &b)
Whether these vectors are (mostly) parallel.
Primary namespace for everything in this library.
Definition align+rec2.h:10
TODO: This all needs to be tested.
int side(vec2 const &point)
TODO: should we create enum { positive/above, negative/below, zero/on_line/colinear/coplanar }...
For interval<T> projected_on_axis(glm::tvec2<T> axis) const;.
Represents a 2D rectangle polygon_shape.
Definition rec2.h:31
Used to create a strong typedef of T.
Definition named.h:133
This trait implies the named type is an linear type, that is, a type that has the addition operator d...
Definition named.h:62
Used in conjuction with traits::location, specifies which displacement type this is a location of.
Definition named.h:84
This trait implies the named type is an affine type, that is, a type that does not have the addition ...
Definition named.h:57