LCOV - code coverage report
Current view: top level - cell - CollisionHandler.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 64 74 86.5 %
Date: 2025-12-06 00:15:40 Functions: 4 4 100.0 %

          Line data    Source code
       1             : #include "CollisionHandler.hpp"
       2             : #include "Disc.hpp"
       3             : #include "MathUtils.hpp"
       4             : 
       5             : #include <numbers>
       6             : 
       7             : namespace cell
       8             : {
       9             : 
      10          10 : CollisionHandler::CollisionHandler(const DiscTypeRegistry& discTypeRegistry,
      11          10 :                                    const MembraneTypeRegistry& membraneTypeRegistry)
      12          10 :     : discTypeRegistry_(discTypeRegistry)
      13          10 :     , membraneTypeRegistry_(membraneTypeRegistry)
      14             : {
      15          10 : }
      16             : 
      17           9 : void CollisionHandler::resolveCollisions(const CollisionDetector::DetectedCollisions& detectedCollisions) const
      18             : {
      19           9 :     const auto& collisions = detectedCollisions.collisions;
      20           9 :     if (collisions.empty())
      21           4 :         return;
      22             : 
      23             :     using CollisionType = CollisionDetector::CollisionType;
      24             : 
      25          16 :     for (const auto& collision : collisions)
      26             :     {
      27          11 :         const auto context = calculateCollisionContext(collision);
      28          11 :         if (context.skipCollision)
      29           6 :             continue;
      30             : 
      31           5 :         if (context.impulseChange <= 0)
      32             :         {
      33             :             // Separation will always happen within 2 time steps
      34           0 :             double beta = context.penetration / 2;
      35             : 
      36           0 :             switch (collision.type)
      37             :             {
      38           0 :             case CollisionType::DiscContainingMembrane:
      39           0 :             case CollisionType::DiscChildMembrane: context.disc->move(beta * context.normal); break;
      40           0 :             default:
      41           0 :                 context.disc->move(-beta * context.invMass1 * context.effMass * context.normal);
      42           0 :                 context.obj2->move(beta * context.invMass2 * context.effMass * context.normal);
      43             :             }
      44             :         }
      45             :         else
      46             :         {
      47           5 :             switch (collision.type)
      48             :             {
      49           3 :             case CollisionType::DiscContainingMembrane:
      50             :             case CollisionType::DiscChildMembrane:
      51           3 :                 context.disc->accelerate(context.impulseChange * context.normal * context.invMass1);
      52           3 :                 break;
      53           2 :             default:
      54           2 :                 context.disc->accelerate(-context.impulseChange * context.normal * context.invMass1);
      55           2 :                 context.obj2->accelerate(context.impulseChange * context.normal * context.invMass2);
      56             :             }
      57             :         }
      58             :     }
      59             : }
      60             : 
      61             : CollisionHandler::CollisionContext
      62          11 : CollisionHandler::calculateCollisionContext(const CollisionDetector::Collision& collision) const
      63             : {
      64          11 :     CollisionContext context;
      65             :     using CollisionType = CollisionDetector::CollisionType;
      66             : 
      67          11 :     const bool isMembraneCollision =
      68          11 :         collision.type == CollisionType::DiscContainingMembrane || collision.type == CollisionType::DiscChildMembrane;
      69             : 
      70          16 :     if (collision.isInvalidatedByDestroyedDisc() ||
      71           5 :         (isMembraneCollision && canGoThrough(collision.disc, collision.membrane, collision.type)))
      72             :     {
      73           6 :         context.skipCollision = true;
      74           6 :         return context;
      75             :     }
      76             : 
      77           5 :     context.disc = collision.disc;
      78           5 :     context.invMass1 = 1.0 / discTypeRegistry_.getByID(context.disc->getTypeID()).getMass();
      79           5 :     const double R1 = discTypeRegistry_.getByID(context.disc->getTypeID()).getRadius();
      80             : 
      81           5 :     double R2 = NAN;
      82           5 :     if (isMembraneCollision)
      83             :     {
      84           3 :         context.obj2 = collision.membrane;
      85           3 :         R2 = membraneTypeRegistry_.getByID(collision.membrane->getTypeID()).getRadius();
      86           3 :         context.invMass2 = 0;
      87             :     }
      88             :     else
      89             :     {
      90           2 :         context.obj2 = collision.otherDisc;
      91           2 :         context.invMass2 = 1.0 / discTypeRegistry_.getByID(collision.otherDisc->getTypeID()).getMass();
      92           2 :         R2 = discTypeRegistry_.getByID(collision.otherDisc->getTypeID()).getRadius();
      93             :     }
      94             : 
      95           5 :     context.effMass = 1.0 / (context.invMass1 + context.invMass2);
      96             : 
      97           5 :     const sf::Vector2d diff = context.obj2->getPosition() - context.disc->getPosition();
      98           5 :     const double distance = mathutils::abs(diff);
      99             : 
     100           5 :     if (collision.type == CollisionType::DiscContainingMembrane)
     101           0 :         context.penetration = std::abs(R2 - R1 - distance);
     102             :     else
     103           5 :         context.penetration = std::abs(R1 + R2 - distance);
     104             : 
     105             :     // The normal points in the direction of the impulse change
     106           5 :     if (std::abs(distance) < 1e-3)
     107           0 :         context.normal = sf::Vector2d{1, 0};
     108           5 :     else if (collision.type == CollisionType::DiscChildMembrane)
     109           3 :         context.normal = -diff / distance;
     110             :     else
     111           2 :         context.normal = diff / distance;
     112             : 
     113           5 :     double relativeNormalSpeed = NAN;
     114           5 :     const double e = 1.0;
     115             : 
     116           5 :     if (isMembraneCollision)
     117           3 :         relativeNormalSpeed = context.normal * collision.disc->getVelocity();
     118             :     else
     119           2 :         relativeNormalSpeed = context.normal * (context.obj2->getVelocity() - collision.disc->getVelocity());
     120             : 
     121           5 :     context.impulseChange = -context.effMass * (1 + e) * relativeNormalSpeed;
     122             : 
     123           5 :     return context;
     124             : }
     125             : 
     126           5 : bool CollisionHandler::canGoThrough(Disc* disc, Membrane* membrane,
     127             :                                     CollisionDetector::CollisionType collisionType) const
     128             : {
     129             :     using CollisionType = CollisionDetector::CollisionType;
     130             : 
     131             :     const auto permeability =
     132           5 :         membraneTypeRegistry_.getByID(membrane->getTypeID()).getPermeabilityFor(disc->getTypeID());
     133             : 
     134           5 :     if (permeability == MembraneType::Permeability::Bidirectional ||
     135           4 :         (collisionType == CollisionType::DiscChildMembrane && permeability == MembraneType::Permeability::Inward) ||
     136           0 :         (collisionType == CollisionType::DiscContainingMembrane && permeability == MembraneType::Permeability::Outward))
     137             :     {
     138           2 :         double angle = mathutils::getAngleBetween(disc->getVelocity(), membrane->getPosition() - disc->getPosition());
     139           2 :         if (std::abs(90.0 - angle) > 30)
     140           2 :             return true;
     141             :     }
     142             : 
     143           3 :     return false;
     144             : }
     145             : 
     146             : } // namespace cell

Generated by: LCOV version 1.14