tile2d
Loading...
Searching...
No Matches
math.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "base.hpp"
4
11template<>
12struct std::less<glm::i32vec2> {
13 bool operator()(const glm::i32vec2& lhs, const glm::i32vec2& rhs) const {
14 return (lhs.x < rhs.x) || ((lhs.x == rhs.x) && (lhs.y < rhs.y));
15 }
16};
17
18template<>
19struct std::less<glm::vec<2, int32_t, glm::packed_lowp>> {
20 bool operator()(const glm::vec<2, int32_t, glm::packed_lowp>& lhs, const glm::vec<2, int32_t, glm::packed_lowp>& rhs) const {
21 return (lhs.x < rhs.x) || ((lhs.x == rhs.x) && (lhs.y < rhs.y));
22 }
23};
24
25template<>
26struct std::hash<glm::i32vec2> {
27 int32_t operator()(const glm::i32vec2& vec) const {
28 const int32_t p1 = 73856093;
29 const int32_t p2 = 19349663;
30
31 return vec.x * p1 ^ vec.y * p2;
32 }
33};
34
35template<>
36struct boost::hash<glm::vec<2, int32_t, glm::packed_lowp>> {
37 int32_t operator()(const glm::vec<2, int32_t, glm::packed_lowp>& vec) const {
38 const int32_t p1 = 73856093;
39 const int32_t p2 = 19349663;
40
41 return vec.x * p1 ^ vec.y * p2;
42 }
43};
44
45template<>
46struct boost::hash<glm::i32vec2> {
47 int32_t operator()(const glm::i32vec2& vec) const {
48 const int32_t p1 = 73856093;
49 const int32_t p2 = 19349663;
50
51 return vec.x * p1 ^ vec.y * p2;
52 }
53};
54
55template<>
56struct std::equal_to<glm::i32vec2> {
57 inline bool operator()(const glm::i32vec2& _1, const glm::i32vec2& _2) const {
58 return _1 == _2;
59 }
60};
61
62
63T2D_NAMESPACE_BEGIN
64
70template<typename T>
71T constexpr square(T x) {
72 return x * x;
73}
74
80struct SinCos {
81 SinCos()
82 : sin(0.0f), cos(1.0f) {}
83
84 SinCos(Float rot) {
85 setRot(rot);
86 }
87
88 SinCos(Float psin, Float pcos)
89 : sin(psin), cos(pcos) {}
90
91 void setRot(Float rot) {
92 sin = glm::sin(rot);
93 cos = glm::cos(rot);
94 }
95
96 SinCos getNegate() const {
97 return SinCos(-sin, cos);
98 }
99
100 SinCos operator-(const SinCos& other) const {
101 //sin(a−c)=sin(a)cos(c)−cos(a)sin(c)
102 //cos(a−c)=cos(a)cos(c)+sin(a)sin(c)
103
104 SinCos dif;
105 dif.sin = sin * other.cos - cos * other.sin;
106 dif.cos = cos * other.cos + sin * other.sin;
107
108 return dif;
109 }
110
111 SinCos operator+(const SinCos& other) const {
112 //sin(a+c)=sin(a)cos(c)+cos(a)sin(c)
113 //cos(a+c)=cos(a)cos(c)−sin(a)sin(c)
114
115 SinCos dif;
116 dif.sin = sin * other.cos + cos * other.sin;
117 dif.cos = cos * other.cos - sin * other.sin;
118
119 return dif;
120 }
121
122 SinCos abs() const {
123 return { glm::abs(sin), glm::abs(cos) };
124 }
125
126 Float sin;
127 Float cos;
128};
129
130inline vec2 rotate(const vec2& v, Float sin, Float cos) {
131 vec2 result;
132
133 result.x = v.x * cos - v.y * sin;
134 result.y = v.x * sin + v.y * cos;
135 return result;
136}
137
138inline vec2 rotate(const vec2& v, const SinCos& sincos) {
139 return rotate(v, sincos.sin, sincos.cos);
140}
141
142struct Transform {
143 Transform()
144 : pos(0.0f, 0.0f), rot(0.0f) {}
145
146 Transform(const vec2& pos, Float rot)
147 : pos(pos), rot(rot) {}
148
149 Transform getInverse() const {
150 return Transform(-pos, -rot);
151 }
152
153 vec2 getWorldPoint(const vec2& localPoint) const {
154 return rotate(localPoint, sincos) + pos;
155 }
156
157 vec2 getWorldPoint(const vec2& localPoint, const vec2& localOffset) const {
158 return rotate(localPoint - localOffset, sincos) + localOffset + pos;
159 }
160
161 vec2 getLocalPoint(const vec2& worldPoint, const vec2& localOffset = { 0.0f, 0.0f }) const {
162 vec2 localPoint = worldPoint - pos - localOffset;
163 localPoint = rotate(localPoint, sincos.getNegate()) + localOffset;
164
165 return localPoint;
166 }
167
168 // updates cache of sincos
169 void update() {
170 sincos.setRot(rot);
171 }
172
173 vec2 pos; // In Meters
174 Float rot; // In radians
175 SinCos sincos;
176};
177
178Float cross(const vec2& a, const vec2& b) {
179 // a.x * b.y - b.x * a.y
180 return (a.x * b.y) - (a.y * b.x);
181}
182
183inline bool overlap(Float min1, Float max1, Float min2, Float max2) {
184 return (min1 <= max2 && max1 >= min2);
185}
186
187inline Float absdot(const vec2& vec1, const vec2& vec2) {
188 return glm::abs(glm::dot(vec1, vec2));
189}
190
191inline vec2 reverse(const vec2& vec) {
192 return { vec.y, vec.x };
193}
194
195// 0 on the line
196// >0 on one side
197// <0 on the other side
198Float sideOf(const vec2& p, const vec2& a, const vec2& b) {
199 return cross(a - b, p) + cross(b, a);
200}
201
202template<typename T = Float, glm::qualifier Prec = glm::packed_lowp>
203struct AABB {
204 using TAABB = AABB<T, Prec>;
205public:
206 using vec2 = glm::vec<2, T, Prec>;
207
208 AABB()
209 : m_min(0, 0), m_max(0, 0) {}
210
211 AABB(vec2 min, vec2 max)
212 : m_min(min), m_max(max) {}
213
214 AABB(vec2 pos, T hW, T hH)
215 : AABB({ hW, hH }) {
216 *this += pos;
217 }
218
219 AABB(vec2 halfDim)
220 : m_min(-halfDim.x, -halfDim.y), m_max(halfDim.x, halfDim.y) {}
221
222 TAABB& operator+=(const vec2& off) {
223 m_min += off;
224 m_max += off;
225
226 return *this;
227 }
228
229 TAABB operator+(const vec2& off) const {
230 TAABB newAABB = *this;
231 newAABB.m_min += off;
232 newAABB.m_max += off;
233
234 return newAABB;
235 }
236
237 TAABB operator-(const vec2& off) const {
238 TAABB newAABB = *this;
239 newAABB.m_min -= off;
240 newAABB.m_max -= off;
241
242 return newAABB;
243 }
244
245 TAABB operator*(T off) const {
246 TAABB newAABB = *this;
247 newAABB.m_min *= off;
248 newAABB.m_max *= off;
249
250 return newAABB;
251 }
252
253 vec2& min() { return m_min; }
254 vec2& max() { return m_max; }
255 const vec2& min() const { return m_min; }
256 const vec2& max() const { return m_max; }
257
258 void setMin(vec2 newMin) {
259 m_min = newMin;
260 }
261
262 void setMax(vec2 newMax) {
263 m_max = newMax;
264 }
265
266 vec2 bl() const {
267 return m_min;
268 }
269
270 vec2 br() const {
271 return { m_max.x, m_min.y };
272 }
273
274 vec2 tr() const {
275 return m_max;
276 }
277
278 vec2 tl() const {
279 return { m_min.x, m_max.y };
280 }
281
282 void forEach(std::function<void(vec2)> callback) const {
283 callback(bl());
284 callback(br());
285 callback(tr());
286 callback(tl());
287 }
288
289 vec2 midpoint() const {
290 return (m_max + m_min) / (T)2;
291 }
292
293 T width() const {
294 return glm::abs(m_max.x - m_min.x);
295 }
296
297 T height() const {
298 return glm::abs(m_max.y - m_min.y);
299 }
300
301 bool intersects(const AABB& other) const {
302 vec2 d1, d2;
303 d1 = vec2(other.m_min.x, other.m_min.y) - vec2(m_max.x, m_max.y);
304 d2 = vec2(m_min.x, m_min.y) - vec2(other.m_max.x, other.m_max.y);
305
306 if (d1.x > 0.0f || d1.y > 0.0f)
307 return false;
308
309 if (d2.x > 0.0f || d2.y > 0.0f)
310 return false;
311
312 return true;
313 }
314
315 AABB<T> intersectingArea(const AABB& other) const {
316 AABB<T> area;
317
318 area.setMin({
319 std::max(m_min.x, other.m_min.x),
320 std::max(m_min.y, other.m_min.y)
321 });
322 area.setMax({
323 std::min(m_max.x, other.m_max.x),
324 std::min(m_max.y, other.m_max.y)
325 });
326
327 return area;
328 }
329
330 // When T is an integer type, rotation will be unstable.
331 AABB<T> rotate(T sin, T cos) const {
332 sin = glm::abs(sin);
333 cos = glm::abs(cos);
334
335 T hWidth = width() / (T)2;
336 T hHeight = height() / (T)2;
337
338 return {
339 midpoint(),
340 hHeight * sin + hWidth * cos,
341 hWidth * sin + hHeight * cos
342 };
343 }
344
345 AABB<T> rotate(const SinCos& sincos) const {
346 return rotate(sincos.sin, sincos.cos);
347 }
348
349 bool valid() {
350 return m_min.x < m_max.x && m_min.y < m_max.y;
351 }
352
353private:
354 vec2 m_min;
355 vec2 m_max;
356};
357
358// describes a linear line (straight line) in general form i.e. Ax + By + C = 0
359// can handle vertical lines, horizontal lines, etc. for intersection calculations
361 LinearGeneralForm(vec2 p1, vec2 p2) {
362 a = p2.y - p1.y;
363 b = -(p2.x - p1.x);
364 c = p1.y * p2.x - p1.x * p2.y;
365 }
366
367 bool intersects(const LinearGeneralForm& other, vec2& intersectPoint) {
368 float denom = a * other.b - other.a * b;
369 if (denom == 0.0f) { // will only happen if the lines are parallel
370 intersectPoint = { 0.0f, 0.0f };
371 return false;
372 }
373
374 intersectPoint.x = (b * other.c - other.b * c) / denom;
375 intersectPoint.y = (c * other.a - other.c * a) / denom;
376
377 return true;
378 }
379
380 float a;
381 float b;
382 float c;
383};
384
385template<typename T>
386T parallelAxisTheorem(T Icm, T mass, T sqDistance) {
387 return Icm + mass * sqDistance;
388}
389
390struct TOBB {
391 Transform transform;
392 vec2 extent;
393
394 inline std::array<vec2, 4> getVertices() const {
395 const vec2 wExt = rotate({ extent.x, extent.y }, transform.sincos);
396 const vec2 blExt = rotate({ extent.x, -extent.y }, transform.sincos);
397 const vec2 trExt = -blExt;
398
399 return {
400 transform.pos - wExt,
401 {transform.pos.x + blExt.x, transform.pos.y + blExt.y},
402 transform.pos + wExt,
403 {transform.pos.x + trExt.x, transform.pos.y + trExt.y}
404 };
405 }
406};
407
408
409template<typename T>
410AABB<T> computeAABBCollisionArea(const AABB<T>& _1, const AABB<T>& _2) {
411 AABB<T> area = _1.intersectingArea(_2);
412 if (!area.valid())
413 return AABB<T>(glm::vec<2, T>(NAN), glm::vec<2, T>(NAN));
414
415 return area;
416}
417
418inline bool nearlyEqual(Float a, Float b, Float max = 0.0001f) {
419 return glm::abs(a - b) < max;
420}
421
422inline bool nearlyEqual(vec2 a, vec2 b, Float max = 0.0001f) {
423 return glm::abs(a.x - b.x) < max && glm::abs(a.y - b.y) < max;
424}
425
426inline Float sqaure(Float value) {
427 return value * value;
428}
429
430inline Float pointSegmentDistance(vec2 p, vec2 v1, vec2 v2, vec2& cp) {
431 // credit goes to https://www.youtube.com/watch?v=egmZJU-1zPU&ab_channel=Two-BitCoding
432 // for this function, incredible channel and resource
433
434 vec2 p_to_v1 = p - v1;
435 vec2 v1_to_v2 = v2 - v1;
436 Float proj = glm::dot(p_to_v1, v1_to_v2);
437 Float length = glm::length2(v1_to_v2);
438
439 Float d = proj / length;
440
441 if (d <= 0.0f) {
442 cp = v1;
443 }
444 else if (d >= 1.0f) {
445 cp = v2;
446 }
447 else {
448 cp = v1 + v1_to_v2 * d;
449 }
450
451 return glm::distance(p, cp);
452}
453
454using MinMax = std::pair<Float, Float>;
455
456template<class Container>
457MinMax projectBoxOnNormal(const Container& vertices, const vec2& normal) {
458 Float firstDot = glm::dot(vertices[0], normal);
459 MinMax minMax = { firstDot, firstDot };
460
461 for (size_t i = 1; i < vertices.size(); i++) {
462 Float dot = glm::dot(vertices[i], normal);
463
464 if (dot < minMax.first) {
465 minMax.first = dot;
466 }
467 else if (dot > minMax.second) {
468 minMax.second = dot;
469 }
470 }
471
472 return minMax;
473}
474
475// Vertices are expected to be in CCW order
476template<class Container, class OutputContainer = std::vector<vec2>>
477OutputContainer sutherlandHodgmanClip(const Container& subjectVertices, const Container& clipVertices) {
478 OutputContainer outputVertices(subjectVertices.begin(), subjectVertices.end());
479 OutputContainer inputVertices;
480
481 vec2 prevClipVertex = clipVertices.back();
482 for (vec2 curClipVertex : clipVertices) {
483 LinearGeneralForm clipLine(prevClipVertex, curClipVertex);
484
485 inputVertices = outputVertices;
486 outputVertices.clear();
487
488 if (inputVertices.empty())
489 break;
490
491 vec2 prevVertex = inputVertices.back();
492 for (vec2 curVertex : inputVertices) {
493 LinearGeneralForm subLine(prevVertex, curVertex);
494
495 bool curVertexInside = sideOf(curVertex, prevClipVertex, curClipVertex) <= 0.0f;
496 bool prevVertexInside = sideOf(prevVertex, prevClipVertex, curClipVertex) <= 0.0f;
497
498 if (curVertexInside) {
499 if (!prevVertexInside) {
500 clipLine.intersects(subLine, outputVertices.emplace_back());
501 }
502
503 outputVertices.push_back(curVertex);
504 }
505 else if (prevVertexInside) {
506 clipLine.intersects(subLine, outputVertices.emplace_back());
507 }
508
509
510 prevVertex = curVertex;
511 }
512
513 prevClipVertex = curClipVertex;
514 }
515
516 return outputVertices;
517}
518
519inline bool circleCollide(Float radi1, const vec2& pos1, Float radi2, const vec2& pos2) {
520 Float difOfX = (pos2.x - pos1.x);
521 Float difOfY = (pos2.y - pos1.y);
522 Float sumOfRadi = radi1 + radi2;
523
524 return square(difOfX) + square(difOfY) <= square(sumOfRadi);
525}
526
527// puts num in the range of [0.0, 1.0]
528inline Float convertToFloat(uint8_t num) {
529 constexpr Float inv255 = 1.0f / 255.0f;
530 return (Float)num * inv255;
531}
532
533template<typename Tint>
534Tint constexpr getSqrtMagicNumber() {
535 static_assert(!(std::is_same_v<Tint, std::int64_t> || std::is_same_v<Tint, std::int32_t>));
536 return Tint(0);
537}
538
539template<>
540constexpr std::int32_t getSqrtMagicNumber() {
541 return 0x5f3759df;
542}
543
544template<>
545constexpr std::int64_t getSqrtMagicNumber() {
546 return 0x5fe6eb50c7b537a9;
547}
548
549// Q_rsqrt
550template<typename T>
551inline T fastInverseSqrt(T x) {
552 static_assert(std::is_floating_point<T>::value, "T must be floating point");
553 using Tint = typename std::conditional<sizeof(T) == 8, std::int64_t, std::int32_t>::type;
554 constexpr Tint magicNumber = getSqrtMagicNumber<Tint>();
555
556 T y = x;
557 T x2 = y * 0.5;
558 Tint i = *(Tint*)&y; // *(Tint *)&y has strict-aliasing UB. Use memcpy, or C++20 std::bit_cast ???
559 i = magicNumber - (i >> 1);
560 y = *(T*)&i;
561 y = y * (1.5 - (x2 * y * y));
562 y = y * (1.5 - (x2 * y * y)); // 2nd iteration, not *needed* but helps with precision
563
564 return y;
565}
566
567//inline vec2 normalize(const vec2& vec) {
568// vec2 normal = vec;
569// Float invSqrtMag = fastInverseSqrt<Float>(glm::dot(vec, vec));
570// normal.x *= invSqrtMag;
571// normal.y *= invSqrtMag;
572// return normal;
573//}
574
575template<typename Int, typename Float>
576inline Int floor(Float x) {
577 Int i = (Int)x;
578 return i - (i > x);
579}
580
581T2D_NAMESPACE_END
A primary include file for all other files. Its purpose is to contain "#include"s that are not within...
T2D_NAMESPACE_BEGIN T constexpr square(T x)
Returns the sqaure of x.
Definition math.hpp:71
Definition math.hpp:203
Definition math.hpp:360
Used to simplify the use of precalculated values for sine and cosine.
Definition math.hpp:80
Definition math.hpp:390
Definition math.hpp:142