Yasiu Math 1
Collection of mathematic functions that help create game mechanics and procedural tools
Loading...
Searching...
No Matches
YasiuMathLib.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2025 Grzegorz Krug.
3 * All Rights Reserved.
4 */
6
7#pragma once
8
9#include <vector>
10#include <cassert>
11#include <cmath>
12// #include <numbers>
13// #include <iostream>
14// #include <math.h>
15// #include <ostream>
16#include <algorithm>
17#include <unordered_set>
18
19#include "YasiuConstants.h"
20
21#if __cplusplus >= 202002L
22#define CPP20_OR_LATER 1
23#else
24#define CPP20_OR_LATER 0
25#endif
26
27
28namespace YasiuMath {
32 template<typename T>
33 struct Point {
34 T x = 0;
35 T y = 0;
36
37 Point( T x, T y )
38 : x(x), y(y) {}
39
40 Point( std::pair<T, T> point )
41 : x(point.first), y(point.second) {}
42
43 T fastAngle()
44 {
45 if ( x == 0.f ) {
46 return y;
47 }
48 else {
49 return static_cast<T>(atan2(y, x));
50 }
51 }
52
53 T fastAngleModded()
54 {
55 // return fastAngle();
56 return static_cast<T>(fmod(fastAngle(), static_cast<T>(YasiuNums::Y_PI)));
57 }
58 };
59
60
64 template<typename U, typename T>
65 struct IndexedPair {
66 U index = 0;
67 T first;
68 T second;
69
70 IndexedPair() = default;
71
72 IndexedPair( U index, T first, T second )
73 : index(index), first(first), second(second) {};
74
75 Point<T> operator-( const IndexedPair<U, T>& other ) const
76 {
77 return Point<T>{first - other.first, second - other.second};
78 }
79
80 IndexedPair<U, T> subtract( const IndexedPair<U, T>& other ) const
81 {
82 return IndexedPair<U, T>{index, first - other.first, second - other.second};
83 }
84
85 bool operator==( const IndexedPair<U, T>& other ) const
86 {
87 return index == other.index;
88 }
89 };
90
91
96 template<typename T>
97 struct PointAngle {
98 int index = 0;
99 T angle = 0;
100
101 PointAngle( const int& index, T angle )
102 : index(index), angle(angle) {}
103
104 PointAngle( const IndexedPair<int, T>& point )
105 {
106 index = point.index;
107 angle = atan2(point.second, point.first);
108 }
109
110 PointAngle( const int& ind, const T& x, const T& y )
111 : index(ind)
112 {
113 angle = atan2(y, x);
114 }
115
116 bool operator<( const PointAngle<T>& other ) const
117 {
118 return angle < other.angle;
119 }
120
121 bool operator>( const PointAngle<T>& other ) const
122 {
123 return angle > other.angle;
124 }
125 };
126
127
129 namespace AngleUtils {
130 template<typename T>
131 static inline double Degrees2Radians( T degree )
132 {
133 return static_cast<T>(YasiuNums::Y_PI * degree / 180.f);
134 }
135
136 template<typename T>
137 static inline T Radians2Degrees( T radian )
138 {
139 return static_cast<T>(radian * 180.f / YasiuNums::Y_PI);
140 }
141
148 template<typename T>
149 static T NormalizeAngleToPeriod( T angle, T period = 360.f )
150 {
151 if ( period < 0 ) { return angle; }
152 T temp = static_cast<T>(fmod(angle, period));
153 if ( temp < 0 ) {
154 temp += period;
155 }
156 return temp;
157 }
158 }
159
160
161// public:
162 // template<typename T>
163 // static std::vector<std::pair<T, T>> SpreadPointsOnArcByXY( const T X, const T Y, const T spreadDistance );
164
165 // template<typename T>
166 // static std::vector<std::pair<T, T>> SpreadPointsOnArcByAngleRadius( const T angle, const T radius,
167 // const T spreadDistance );
168
172 namespace Trigonometry {
182 template<typename T>
183 std::vector<std::pair<T, T>> SpreadPointsOnTangentByAngleRadius( const T angle, const T radius, const T spreadDistance )
184 {
185 assert(radius > 0);
186 assert(spreadDistance > 0);
187
188 std::vector<std::pair<T, T>> result;
189
190 /* Beta = 90 - alfa */
191 T beta = static_cast<T>(YasiuNums::Y_PI / 2. - angle);
192
193 /*
194 * sin(B) = opposite / hypotenuse -> dy = sin * hypotenuse
195 * cos(B) = adjacent / hypotenuse -> dx = cos * hypotenuse
196 */
197
198 // T halfSpread = spreadDistance / 2;
199 T dy = sin(beta) * spreadDistance;
200 T dx = cos(beta) * spreadDistance;
201
202 T X = cos(angle) * radius;
203 T Y = sin(angle) * radius;
204
205
206 // Projected triangle aligns with axis different than +X +Y
207 // Axis for X is positive, for Y is Negative ( angle goes anti-clockwise )
208 // Starting from 0 = +X
209 result.push_back({X + dx, Y - dy});
210 // This is just negated vector
211 result.push_back({X - dx, Y + dy});
212
213 // std::tuple<float> test;
214 // test
215 // test.g
216
217 return result;
218 }
219
231 template<typename T>
232 std::vector<std::pair<T, T>> SpreadPointsOnTangentByXY( const T X, const T Y, const T spreadDistance )
233 {
234 T angle = atan2f(Y, X);
235 T radius = sqrtf((X * X) + (Y * Y));
236 return SpreadPointsOnTangentByAngleRadius(angle, radius, spreadDistance);
237 }
238
259 template<typename T>
260 T FindMinimalRadiusForIntersectingTangentsOnArc( const T alfa, const T beta, const T symmetricWidth )
261 {
262 /*
263 * sin (alfa) * A - sin(beta) * b
264 * R = ----------------------------------
265 * cos(beta) - cos(alfa)
266 */
267 T angleDiff = fmod(abs(alfa - beta), 360);
268 assert(symmetricWidth > 0);
269 assert(angleDiff > 0);
270 assert(angleDiff != 180); // Only place when they don't meet in infinite space
271
272 T nominator = symmetricWidth * (sin(alfa) + sin(beta));
273 T denominator = cos(beta) - cos(alfa);
274 T result = abs(nominator / denominator);
275
276 return result;
277 }
278
298 template<typename T>
300 const T alfa,
301 const T beta,
302 const T widthA,
303 const T widthB
304 )
305 {
306 std::pair<T, T> result;
307
308 T angleDiff = fmod(abs(alfa - beta), 360); /* Keep angle in 0-360 range */
309 assert(widthA > 0);
310 assert(widthB > 0);
311 assert(angleDiff > 0);
312 assert(angleDiff != 180);
313
314 /* X - formula case */
315 T nominator1 = sin(alfa) * widthA + sin(beta) * widthB;
316 T denominator1 = cos(alfa) - cos(beta);
317 T radius1 = abs(nominator1 / denominator1);
318
319 /* Y - formula case */
320 T nominator2 = -(cos(alfa) * widthA + cos(beta) * widthB);
321 T denominator2 = sin(alfa) - sin(beta);
322 T radius2 = abs(nominator2 / denominator2);
323
324 if ( widthA < widthB ) {
325 /* Pack longer radius to first */
326 if ( radius1 > radius2 ) {
327 result.first = radius1;
328 result.second = radius2;
329 }
330 else {
331 result.first = radius2;
332 result.second = radius1;
333 }
334 }
335 else {
336 /* Pack shorter radius to first */
337 if ( radius1 < radius2 ) {
338 result.first = radius1;
339 result.second = radius2;
340 }
341 else {
342 result.first = radius2;
343 result.second = radius1;
344 }
345 }
346 return result;
347 }
348 }
349
350
354 namespace ConvexHull {
355 /* @cond INTERNAL */
356 /* Helper function for convex hull */
357 template<typename T>
358 inline std::pair<T, T> CalculateVector(
359 const std::vector<std::pair<T, T>>& polygonPoints,
360 const int& ind1,
361 const int& ind2
362 )
363 {
364 // if ( convexStack.size() < 2 ) {
365 // return {static_cast<T>(0), static_cast<T>(0)};
366 // }
367
368 // const int ind1 = convexStack.at(convexStack.size() - 2);
369 // const int ind2 = convexStack.at(convexStack.size() - 1);
370 // std::cout << "Polygon array size: " << polygonPoints.size() << ", ind1: " << ind1 << ", ind2: " << ind2
371 // << std::endl;
372 T dX = polygonPoints.at(ind1).first - polygonPoints.at(ind2).first;
373 T dY = polygonPoints.at(ind1).second - polygonPoints.at(ind2).second;
374
375 // std::cout << "Last vector: " << dX << " " << dY << " ( " << ind1 << ", " << ind2 << " )" << std::endl;
376 return {dX, dY};
377 }
378
379 template<typename T>
380 static T Cross( const std::pair<T, T>& A, const std::pair<T, T>& B )
381 {
382 auto temp1 = A.first * B.second; //- B.second * A.first;
383 auto temp2 = A.second * B.first;
384 return temp1 - temp2;
385 }
386
387 /* Function to check backwards if any previous points need to be removed */
388 template<typename T>
389 static void CheckHullBackwards(
390 std::vector<int>& currentConvex,
391 const int& checkIndex,
392 const std::vector<std::pair<T, T>>& allPoints,
393 const bool clockWise = false
394 )
395 {
396 if ( currentConvex.size() < 2 ) { return; }
397
398 T cross;
399 if ( clockWise ) {
400 std::pair<T, T> vec1 = CalculateVector<T>(allPoints, currentConvex.at(currentConvex.size() - 1), checkIndex);
401 std::pair<T, T> vec2 = CalculateVector<T>(
402 allPoints,
403 currentConvex.at(currentConvex.size() - 2),
404 currentConvex.at(currentConvex.size() - 1)
405 );
406 cross = Cross<T>(vec1, vec2);
407 }
408 else {
409 std::pair<T, T> vec1 = CalculateVector<T>(
410 allPoints,
411 currentConvex.at(currentConvex.size() - 1),
412 currentConvex.at(currentConvex.size() - 2)
413 );
414 std::pair<T, T> vec2 = CalculateVector<T>(allPoints, checkIndex, currentConvex.at(currentConvex.size() - 1));
415 cross = Cross<T>(vec1, vec2);
416 }
417 // std::cout << "Checking point: " << checkIndex << ", cross: " << cross << std::endl;
418
419 if ( cross <= 0 ) {
420 /* Pop invalid, reassign new */
421 // std::cout << " - Removing previous point: " << currentConvex.at(currentConvex.size() - 1) << ", for: " << checkIndex << "\n";
422 currentConvex.pop_back();
423 // std::cout << " Last element now: " << currentConvex.at(currentConvex.size() - 1) << "\n";
424 CheckHullBackwards(currentConvex, checkIndex, allPoints, clockWise);
425 }
426 else {
427 // std::cout << " Checked pt: " << checkIndex << "\n";
428 }
429 return;
430 }
431
432
436 template<typename T>
437 static void AddPointToConvex(
438 std::vector<int>& currentConvex,
439 const int& index,
440 const std::vector<std::pair<T, T>>& allPoints,
441 const bool clockWise = false
442 )
443 {
444 if ( currentConvex.size() <= 1 ) {
445 currentConvex.push_back(index);
446 return;
447 }
448
449 CheckHullBackwards(currentConvex, index, allPoints, clockWise);
450 currentConvex.push_back(index);
451 // std::cout << " +Hull point added: " << index << "\n";
452 };
453
454 /* @endcond */
455
462 template<typename T>
463 static std::vector<int> ConvexHull2D( const std::vector<std::pair<T, T>>& polygonPoints )
464 {
465 if ( polygonPoints.size() == 0 ) {
466 return {};
467 }
468 else if ( polygonPoints.size() == 1 ) {
469 return {0};
470 }
471 else if ( polygonPoints.size() == 2 ) {
472 return {0, 1};
473 }
474
475 std::vector<IndexedPair<int, T>> sortedPoints; /* Points sorted in Y Axis */
476 sortedPoints.reserve(polygonPoints.size() + 1);
477 for ( int i = 0; i < static_cast<int>(polygonPoints.size()); i++ ) {
478 sortedPoints.emplace_back(IndexedPair<int, T>(i, polygonPoints.at(i).first, polygonPoints.at(i).second));
479 }
480
481 std::sort(
482 sortedPoints.begin(),
483 sortedPoints.end(),
484 [] ( const auto& a, const auto& b ) {
485 /* Ascending Y [y -> Y] */
486 return a.second < b.second;
487 }
488 );
489
490 IndexedPair<int, T> bottom = sortedPoints.at(0);
491 IndexedPair<int, T> top = sortedPoints.at(sortedPoints.size() - 1);
492 Point<T> startEndDiff = top - bottom;
493 T angleThreshold = startEndDiff.fastAngleModded();
494
495 std::vector<PointAngle<T>> pointsOnLeft;
496 std::vector<PointAngle<T>> pointsOnRight;
497
498 /* == Splitting points to left and right == */
499 for ( IndexedPair<int, T> point : sortedPoints ) {
500 // std::cout << "\t" << point.index << " : " << point.first << ", " << point.second << std::endl;
501 if ( point == bottom || point == top ) {
502 continue;
503 }
504
505 Point<T> temp = (point - bottom);
506 T angle = temp.fastAngleModded();
507 // std::cout << "Comparing angle(" << point.index << "): " << angle << " < " << angleThreshold << "\n";
508 if ( point.first == top.first && point.first == bottom.second ) {
509 /* Ignore */
510 }
511 else if ( angle > angleThreshold ) {
512 pointsOnLeft.emplace_back(point.index, angle);
513 }
514 else {
515 pointsOnRight.emplace_back(point.index, angle);
516 }
517 }
518 // std::cout << "\n";
519
520 std::sort(pointsOnLeft.begin(), pointsOnLeft.end(), std::greater<PointAngle<T>>());
521 std::sort(pointsOnRight.begin(), pointsOnRight.end());
522 // return {};
523
524 std::unordered_set<int> visitedPoints;
525 visitedPoints.reserve(polygonPoints.size());
526
527 /* GOING COUNTERCLOCKWISE FROM TOP */
528 std::vector<int> convexLeft;
529 std::vector<int> convexRight;
530 convexLeft.reserve(polygonPoints.size());
531 convexRight.reserve(polygonPoints.size());
532
533 /* Initial 2 points */
534 convexLeft.push_back(bottom.index);
535 visitedPoints.insert(bottom.index);
536 if ( pointsOnLeft.size() > 0 ) {
537 convexLeft.push_back(pointsOnLeft.at(0).index);
538 visitedPoints.insert(pointsOnLeft.at(0).index);
539 Point<T> pt = CalculateVector<T>(polygonPoints, pointsOnLeft.at(0).index, bottom.index);
540
541 // std::cout << "\nLeft Hull\n";
542 for ( const PointAngle<T>& nextPoint : pointsOnLeft ) {
543 if ( nextPoint.index == top.index ) {
544 // std::cout << "This is top index, stop loop: " << nextPoint.index << "\n";
545 break;
546 }
547#if CPP20_OR_LATER
548 if ( visitedPoints.contains(nextPoint.index) ) {
549 // std::cout << "Skipping visited point: " << nextPoint.index << "\n";
550 continue;
551 }
552#else
553 if ( visitedPoints.find(nextPoint.index) != visitedPoints.end() ) {
554 // std::cout << "Skipping visited point: " << nextPoint.index << "\n";
555 continue;
556 }
557#endif
558
559 // std::cout << nextPoint.index << "\n";
560
561 visitedPoints.insert(nextPoint.index);
562 AddPointToConvex<T>(convexLeft, nextPoint.index, polygonPoints, true);
563 }
564 }
565 AddPointToConvex<T>(convexLeft, top.index, polygonPoints, true);
566 // std::cout << "Convex L:\n";
567 // for ( auto pt : convexLeft ) {
568 // // std::cout << ", " << pt;
569 // }
570 // std::cout << std::endl;
571
572 visitedPoints.clear();
573 convexRight.push_back(bottom.index);
574 visitedPoints.insert(bottom.index);
575 if ( pointsOnRight.size() > 0 ) {
576 convexRight.push_back(pointsOnRight.at(0).index);
577 visitedPoints.insert(pointsOnRight.at(0).index);
578
579 /* GOING COUNTERCLOCKWISE FROM BOTTOM */
580 // std::cout << "\n\nPoints with angles right:\n";
581 // std::cout << "\nRight Hull\n";
582 for ( const PointAngle<T>& nextPoint : pointsOnRight ) {
583 if ( nextPoint.index == top.index ) {
584 // std::cout << "This is top index, stop loop: " << nextPoint.index << "\n";
585 break;
586 }
587 if ( visitedPoints.find(nextPoint.index) != visitedPoints.end() ) {
588 // std::cout << "Skipping visited point: " << nextPoint.index << "\n";
589 continue;
590 }
591
592 // std::cout << "= Next PT:" << nextPoint.index << "\n";
593 visitedPoints.insert(nextPoint.index);
594 AddPointToConvex<T>(convexRight, nextPoint.index, polygonPoints);
595 }
596 }
597 AddPointToConvex<T>(convexRight, top.index, polygonPoints);
598 // std::cout << "Convex R:\n";
599 // for ( auto pt : convexRight ) {
600 // std::cout << ", " << pt;
601 // }
602 // std::cout << std::endl;
603
604
605 /* RESULTS */
606 // std::cout << "\nPrinting points:\n";
607 // std::cout << "\n";
608
609 // std::cout << "Convex hull:\n";
610 // for ( int ind : convexRight ) {
611 // std::pair<T, T> point = polygonPoints.at(ind);
612 // std::cout << " == " << ind << "\t" << point.first << " _ " << point.second << "\n";
613 // }
614 // std::cout << "===\n";
615
616 // for ( int ind : convexLeft ) {
617 // auto point = polygonPoints.at(ind);
618 // std::cout << " == " << ind << "\t" << point.first << " _ " << point.second << "\n";
619 // }
620
621 // convexLeft.pop_back();
622 // convexRight.pop_back();
623
624 // for ( auto it = convexLeft.end() - 1; it != convexRight.begin(); --it ) {}
625 // for ( int point : convexLeft ) {
626 for ( auto it = convexLeft.end(); it != convexLeft.begin(); ) {
627 --it;
628 int point = *(it);
629 if ( point == top.index || point == bottom.index ) { continue; }
630 convexRight.push_back(point);
631 }
632
633 return convexRight;
634 };
635
636 // template<typename T>
637 // static std::vector<std::pair<T, T>> MinBoundingBoxFromHull( const std::vector<std::pair<T, T>>& convexPoints )
638 // {
639 // auto temp = std::vector<std::pair<T, T>>{};
640 // return temp;
641 // };
642 //
643 // template<typename T>
644 // static std::vector<std::pair<T, T>> MinBoundingBox( const std::vector<std::pair<T, T>>& polygonPoints )
645 // {
646 // const std::vector<int> indexes = ConvexHull<T>(polygonPoints);
647 // std::vector<std::pair<T, T>> hullPoints;
648 // hullPoints.reserve(indexes.size());
649 // for ( auto ind : indexes ) {
650 // hullPoints.push_back(polygonPoints.at(ind));
651 // }
652 // return MinBoundingBoxFromHull(hullPoints);
653 // };
654 }
655};
Collection of functions working with angles and rotation.
Definition YasiuMathLib.h:129
static T NormalizeAngleToPeriod(T angle, T period=360.f)
Normalize angle by removing full periods from value, result is in range <0, period>.
Definition YasiuMathLib.h:149
Collection of convex hull functions and helper functions.
Definition YasiuMathLib.h:354
static std::vector< int > ConvexHull2D(const std::vector< std::pair< T, T > > &polygonPoints)
Find Convex Hull in 2D space from given points.
Definition YasiuMathLib.h:463
Functions that calculate trigonometry problems in 2D / 3D space.
Definition YasiuMathLib.h:172
std::vector< std::pair< T, T > > SpreadPointsOnTangentByAngleRadius(const T angle, const T radius, const T spreadDistance)
Definition YasiuMathLib.h:183
T FindMinimalRadiusForIntersectingTangentsOnArc(const T alfa, const T beta, const T symmetricWidth)
Finds radius of circle for both tangent line that intersect.
Definition YasiuMathLib.h:260
std::vector< std::pair< T, T > > SpreadPointsOnTangentByXY(const T X, const T Y, const T spreadDistance)
Spread points on tangent line to arc.
Definition YasiuMathLib.h:232
std::pair< T, T > FindMinimalRadiusForIntersectingTangentsOnArcAsymmetric(const T alfa, const T beta, const T widthA, const T widthB)
Finds radius of 2 circles for both tangent line that intersect. Tangents on circle are defined by ang...
Definition YasiuMathLib.h:299
Helper object, for point description in algorithm.
Definition YasiuMathLib.h:65
Object 'point' that has its angle related to center. Helper object, for point description in algorith...
Definition YasiuMathLib.h:97
Helper object, for point description in algorithm.
Definition YasiuMathLib.h:33