LCOV - code coverage report
Current view: top level - core/math - Calc.cpp (source / functions) Coverage Total Hit
Test: coverage-src.info Lines: 39.4 % 127 50
Test Date: 2025-05-27 23:26:07 Functions: 64.7 % 17 11

            Line data    Source code
       1              : #include "Calc.hpp"
       2              : 
       3              : #include <math.h>
       4              : 
       5              : #include <cmath>
       6              : 
       7              : namespace Soldank::Calc
       8              : {
       9            3 : float Distance(const glm::vec2& p1, const glm::vec2& p2)
      10              : {
      11            3 :     return glm::length(p2 - p1);
      12              : }
      13              : 
      14            9 : float SquareDistance(const glm::vec2& p1, const glm::vec2& p2)
      15              : {
      16            9 :     glm::vec2 diff = p1 - p2;
      17           18 :     return glm::dot(diff, diff);
      18              : }
      19              : 
      20            2 : float Vec2Length(const glm::vec2& v)
      21              : {
      22            2 :     return glm::length(v);
      23              : }
      24              : 
      25           20 : glm::vec2 Vec2Normalize(const glm::vec2& v)
      26              : {
      27           20 :     auto magnitude = glm::length(v);
      28           20 :     if (magnitude < 0.001) {
      29            9 :         return { 0.0F, 0.0F };
      30              :     }
      31              : 
      32           11 :     return v / magnitude;
      33              : }
      34              : 
      35            4 : float Vec2Angle(const glm::vec2& v)
      36              : {
      37            4 :     return (float)(-atan2(0.0, 1.0) - atan2(v.y, v.x));
      38              : }
      39            2 : glm::vec2 Vec2Scale(const glm::vec2& v, float scale)
      40              : {
      41            2 :     return v * scale;
      42              : }
      43              : 
      44            3 : float PointLineDistance(const glm::vec2& p1, const glm::vec2& p2, const glm::vec2& p3)
      45              : {
      46            3 :     auto u = ((p3.x - p1.x) * (p2.x - p1.x) + (p3.y - p1.y) * (p2.y - p1.y)) /
      47            3 :              (std::pow(p2.x - p1.x, 2) + std::pow(p2.y - p1.y, 2));
      48              : 
      49            3 :     auto x = p1.x + u * (p2.x - p1.x);
      50            3 :     auto y = p1.y + u * (p2.y - p1.y);
      51              : 
      52            3 :     return (float)std::sqrt(std::pow(x - p3.x, 2) + std::pow(y - p3.y, 2));
      53              : }
      54              : 
      55            5 : glm::vec2 Lerp(glm::vec2 a, glm::vec2 b, float t)
      56              : {
      57            5 :     return glm::mix(a, b, t);
      58              : }
      59              : 
      60            3 : float Lerp(float a, float b, float t)
      61              : {
      62            3 :     return std::lerp(a, b, t);
      63              : }
      64              : 
      65            1 : std::vector<glm::vec2> LineCircleCollisionPoints(glm::vec2 line1,
      66              :                                                  glm::vec2 line2,
      67              :                                                  glm::vec2 circle_center,
      68              :                                                  float radius)
      69              : {
      70            1 :     float diff_x = line2.x - line1.x;
      71            1 :     float diff_y = line2.y - line1.y;
      72              : 
      73            1 :     if (std::abs(diff_x) < 0.00001F && std::abs(diff_y) < 0.00001F) {
      74              :         // Floating point inaccuracy
      75            0 :         return {};
      76              :     }
      77              : 
      78              :     // If the angle of the bullet is bigger than 45 degrees,
      79              :     // flip the coordinate system.
      80              :     // This algorithm deals with lines being nearly horizontal just fine,
      81              :     // but nearly vertical would cause a havoc, as vertical line is not a function.
      82            1 :     bool flipped = false;
      83            1 :     if (std::abs(diff_y) > std::abs(diff_x)) {
      84            0 :         flipped = true;
      85            0 :         std::swap(line1.x, line1.y);
      86            0 :         std::swap(line2.x, line2.y);
      87            0 :         std::swap(circle_center.x, circle_center.y);
      88            0 :         std::swap(diff_x, diff_x);
      89              :     }
      90              : 
      91              :     // Line equation: ax + b - y = 0. given x1, y1, x2, y2, let's calculate a and b
      92              :     // a = (y1 - y2)/(x1 - x2)
      93            1 :     float a = diff_y / diff_x;
      94              :     // b := y - ax
      95            1 :     float b = line1.y - a * line1.x;
      96              :     // Circle equation: (x - x1)^2 + (y - y1)^2 - r^2 = 0
      97              :     // Now we need to solve: (x - x1)^2 + (y - y1)^2 - r^2 = ax + b - y
      98              :     // Simplyfing above: (a^2 + 1)x^2 + 2(ab − ay1 − x1)x + (y1^2 − r^2 + x1^2 − 2by1b^2)=0
      99              :     // now, since this is a standard Ax^2 + Bx + C equation, we find x and y using
     100              :     // x = (-B +/- sqrt(B^2 - 4ac))/(2A)
     101              :     // A = (a^2 + 1)
     102            1 :     float a1 = (a * a) + 1;
     103              :     // B = 2(ab - ay1 - x1)
     104            1 :     float b1 = 2 * (a * b - a * circle_center.y - circle_center.x);
     105              :     // C = y1^2 − r^2 + x1^2 − 2by1 + b^2
     106            1 :     float c1 = (circle_center.y * circle_center.y) - (radius * radius) +
     107            1 :                (circle_center.x * circle_center.x) - 2 * b * circle_center.y + (b * b);
     108              :     // delta = B^2 - 4AC;
     109            1 :     float delta = (b1 * b1) - 4 * a1 * c1;
     110              :     // having x1 and x2 result, we can calculate y1 and y2 from y = a * x + b
     111              : 
     112              :     // if delta < 0, no intersection
     113            1 :     if (delta < 0) {
     114            1 :         return {};
     115              :     }
     116              : 
     117            0 :     float minx = NAN;
     118            0 :     float miny = NAN;
     119            0 :     float maxx = NAN;
     120            0 :     float maxy = NAN;
     121              : 
     122            0 :     if (line1.x < line2.x) {
     123            0 :         minx = line1.x;
     124            0 :         maxx = line2.x;
     125              :     } else {
     126            0 :         minx = line2.x;
     127            0 :         maxx = line1.x;
     128              :     }
     129              : 
     130            0 :     if (line1.y < line2.y) {
     131            0 :         miny = line1.y;
     132            0 :         maxy = line2.y;
     133              :     } else {
     134            0 :         miny = line2.y;
     135            0 :         maxy = line1.y;
     136              :     }
     137              : 
     138            0 :     std::vector<glm::vec2> result;
     139              :     glm::vec2 intersect;
     140              :     // we don't care about a case of delta = 0 as it's extremaly rare,
     141              :     // also this will handle it fine, just less effecient
     142            0 :     float sqrtdelta = std::sqrt(delta);
     143            0 :     float a2 = 2 * a1;
     144            0 :     intersect.x = (-b1 - sqrtdelta) / a2;
     145            0 :     intersect.y = a * intersect.x + b;
     146              :     // we know that infinite line does intersect the circle, now let's see if our part does
     147            0 :     auto in_range = [](float a, float b, float c) { return a >= b && a <= c; };
     148            0 :     if (in_range(intersect.x, minx, maxx) && in_range(intersect.y, miny, maxy)) {
     149            0 :         if (flipped) {
     150            0 :             std::swap(intersect.x, intersect.y);
     151              :         }
     152              : 
     153            0 :         result.push_back(intersect);
     154              :     }
     155              : 
     156            0 :     intersect.x = (-b1 + sqrtdelta) / a2;
     157            0 :     intersect.y = a * intersect.x + b;
     158            0 :     if (in_range(intersect.x, minx, maxx) && in_range(intersect.y, miny, maxy)) {
     159            0 :         if (flipped) {
     160            0 :             std::swap(intersect.x, intersect.y);
     161              :         }
     162              : 
     163            0 :         result.push_back(intersect);
     164              :     }
     165              : 
     166            0 :     return result;
     167            0 : }
     168              : 
     169            3 : std::optional<glm::vec2> LineCircleCollision(const glm::vec2& start_point,
     170              :                                              const glm::vec2& end_point,
     171              :                                              const glm::vec2& circle_center,
     172              :                                              float radius)
     173              : {
     174            3 :     float r2 = radius * radius;
     175              : 
     176            3 :     if (SquareDistance(start_point, circle_center) <= r2) {
     177            0 :         return start_point;
     178              :     }
     179              : 
     180            3 :     if (SquareDistance(end_point, circle_center) <= r2) {
     181            2 :         return end_point;
     182              :     }
     183              : 
     184              :     auto intersection_result =
     185            1 :       LineCircleCollisionPoints(start_point, end_point, circle_center, radius);
     186            1 :     if (!intersection_result.empty()) {
     187            0 :         if (intersection_result.size() == 2 &&
     188            0 :             SquareDistance(intersection_result[0], start_point) >
     189            0 :               SquareDistance(intersection_result[1], start_point)) {
     190              : 
     191            0 :             return intersection_result[1];
     192              :         }
     193              : 
     194            0 :         return intersection_result[0];
     195              :     }
     196            1 :     return std::nullopt;
     197            1 : }
     198              : 
     199            0 : bool SegmentsIntersect(glm::vec2 a, glm::vec2 b, glm::vec2 c, glm::vec2 d)
     200              : {
     201            0 :     float segment_ab_center_x = b.x - a.x;
     202            0 :     float segment_ab_center_y = b.y - a.y;
     203            0 :     float segment_cd_center_x = d.x - c.x;
     204            0 :     float segment_cd_center_y = d.y - c.y;
     205              : 
     206            0 :     float s = NAN;
     207            0 :     float t = NAN;
     208            0 :     s = (-segment_ab_center_y * (a.x - c.x) + segment_ab_center_x * (a.y - c.y)) /
     209            0 :         (-segment_cd_center_x * segment_ab_center_y + segment_ab_center_x * segment_cd_center_y);
     210            0 :     t = (segment_cd_center_x * (a.y - c.y) - segment_cd_center_y * (a.x - c.x)) /
     211            0 :         (-segment_cd_center_x * segment_ab_center_y + segment_ab_center_x * segment_cd_center_y);
     212              : 
     213            0 :     return s >= 0 && s <= 1 && t >= 0 && t <= 1;
     214              : }
     215              : 
     216            0 : float Det(glm::vec2 a, glm::vec2 b, glm::vec2 c)
     217              : {
     218            0 :     return a.x * b.y + a.y * c.x + b.x * c.y - c.x * b.y - c.y * a.x - b.x * a.y;
     219              : }
     220              : 
     221            0 : float GetAngle(const glm::vec2& p1, const glm::vec2& p2)
     222              : {
     223            0 :     return std::atan2(p1.y - p2.y, p1.x - p2.x);
     224              : }
     225              : 
     226            0 : glm::vec2 RotatePoint(const glm::vec2& point, const glm::vec2& origin, float rotation)
     227              : {
     228            0 :     glm::vec4 origin4 = { origin.x, origin.y, 1.0F, 1.0F };
     229            0 :     glm::vec4 point4 = { point.x, point.y, 1.0F, 1.0F };
     230            0 :     glm::vec4 new_position = point4 - origin4;
     231            0 :     glm::mat4 transform(1);
     232            0 :     transform = glm::rotate(transform, rotation, glm::vec3(0.0, 0.0, 1.0));
     233            0 :     new_position = transform * new_position;
     234            0 :     new_position += origin4;
     235              :     glm::vec2 result;
     236            0 :     result.x = new_position.x;
     237            0 :     result.y = new_position.y;
     238            0 :     return result;
     239              : }
     240              : 
     241            0 : glm::vec2 ScalePoint(const glm::vec2& point, const glm::vec2& origin, const glm::vec2& scale_factor)
     242              : {
     243            0 :     glm::vec2 result = point - origin;
     244            0 :     result.x *= scale_factor.x;
     245            0 :     result.y *= scale_factor.y;
     246            0 :     result += origin;
     247            0 :     return result;
     248              : }
     249              : } // namespace Soldank::Calc
        

Generated by: LCOV version 2.0-1