LCOV - code coverage report
Current view: top level - cell - CollisionDetector.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 61 72 84.7 %
Date: 2025-12-06 00:15:40 Functions: 7 7 100.0 %

          Line data    Source code
       1             : #include "CollisionDetector.hpp"
       2             : #include "Disc.hpp"
       3             : #include "MathUtils.hpp"
       4             : 
       5             : #include <algorithm>
       6             : 
       7             : namespace cell
       8             : {
       9          10 : CollisionDetector::CollisionDetector(const DiscTypeRegistry& discTypeRegistry,
      10          10 :                                      const MembraneTypeRegistry& membraneTypeRegistry)
      11          10 :     : discTypeRegistry_(discTypeRegistry)
      12          10 :     , membraneTypeRegistry_(membraneTypeRegistry)
      13             : {
      14          10 : }
      15             : 
      16           9 : void CollisionDetector::buildEntries(const std::vector<Disc>& discs, const std::vector<Membrane>& membranes,
      17             :                                      const std::vector<Disc*>& intrudingDiscs)
      18             : {
      19           9 :     entries_.clear();
      20           9 :     entries_.reserve(discs.size() + membranes.size() + intrudingDiscs.size());
      21             : 
      22          27 :     for (std::size_t i = 0; i < discs.size(); ++i)
      23          18 :         entries_.push_back(createEntry(discs[i], discTypeRegistry_, i, EntryType::Disc));
      24             : 
      25          11 :     for (std::size_t i = 0; i < membranes.size(); ++i)
      26           2 :         entries_.push_back(createEntry(membranes[i], membraneTypeRegistry_, i, EntryType::Membrane));
      27             : 
      28          11 :     for (std::size_t i = 0; i < intrudingDiscs.size(); ++i)
      29           2 :         entries_.push_back(createEntry(*intrudingDiscs[i], discTypeRegistry_, i, EntryType::IntrudingDisc));
      30             : 
      31          37 :     std::sort(entries_.begin(), entries_.end(), [&](const Entry& e1, const Entry& e2) { return e1.minX < e2.minX; });
      32           9 : }
      33             : 
      34           9 : CollisionDetector::DetectedCollisions CollisionDetector::detectCollisions(const Params& params)
      35             : {
      36           9 :     params_ = params;
      37           9 :     DetectedCollisions result;
      38           9 :     auto& collisions = result.collisions;
      39           9 :     collisions.reserve(50);
      40             : 
      41          10 :     const auto getDiscPointer = [&](const Entry& entry)
      42             :     {
      43          10 :         if (entry.type == EntryType::IntrudingDisc)
      44           0 :             return (*params_.intrudingDiscs)[entry.index];
      45             : 
      46          10 :         return &(*params_.discs)[entry.index];
      47           9 :     };
      48             : 
      49          31 :     for (std::size_t i = 0; i < entries_.size(); ++i)
      50             :     {
      51          22 :         const auto& entry1 = entries_[i];
      52          22 :         if (entry1.type == EntryType::Disc && !discIsContainedByMembrane(entry1))
      53             :         {
      54           0 :             result.indexes[CollisionType::DiscContainingMembrane].push_back(collisions.size());
      55           0 :             collisions.push_back(Collision{.disc = &(*params_.discs)[entry1.index],
      56           0 :                                            .membrane = params_.containingMembrane,
      57             :                                            .type = CollisionType::DiscContainingMembrane});
      58             :         }
      59             : 
      60          36 :         for (std::size_t j = i + 1; j < entries_.size(); ++j)
      61             :         {
      62          21 :             const auto& entry2 = entries_[j];
      63             : 
      64          21 :             if (entry2.minX > entry1.maxX)
      65           7 :                 break;
      66             : 
      67             :             // Intruding disc-intruding disc: No overlap should occur here because intruders come from the parent
      68             :             // compartment and collisions are handled from parent to child
      69             :             // Membrane-intruding disc, intruding disc-membrane: Again, handled by parent
      70             :             // Membrane-membrane: Overlapping membranes aren't allowed to occur
      71             : 
      72          28 :             if ((entry1.type != EntryType::Disc && entry2.type != EntryType::Disc) ||
      73          14 :                 !mathutils::circlesOverlap(entry1.position, entry1.radius, entry2.position, entry2.radius,
      74             :                                            MinOverlap{0.1}))
      75           3 :                 continue;
      76             : 
      77          11 :             if (entry1.type == EntryType::Membrane || entry2.type == EntryType::Membrane)
      78             :             {
      79           6 :                 const bool firstIsMembrane = entry1.type == EntryType::Membrane;
      80             : 
      81             :                 Membrane* membrane =
      82           6 :                     firstIsMembrane ? &(*params_.membranes)[entry1.index] : &(*params_.membranes)[entry2.index];
      83             : 
      84           6 :                 result.indexes[CollisionType::DiscChildMembrane].push_back(collisions.size());
      85           6 :                 Disc* disc = firstIsMembrane ? &(*params_.discs)[entry2.index] : &(*params_.discs)[entry1.index];
      86             : 
      87           6 :                 collisions.push_back(
      88           6 :                     Collision{.disc = disc, .membrane = membrane, .type = CollisionType::DiscChildMembrane});
      89           6 :             }
      90             :             else
      91             :             {
      92           5 :                 if (entry1.type == EntryType::IntrudingDisc)
      93             :                 {
      94           0 :                     result.indexes[CollisionType::DiscIntrudingDisc].push_back(collisions.size());
      95           0 :                     collisions.push_back(Collision{.disc = &(*params_.discs)[entry2.index],
      96           0 :                                                    .otherDisc = (*params_.intrudingDiscs)[entry1.index],
      97             :                                                    .type = CollisionType::DiscIntrudingDisc});
      98             :                 }
      99           5 :                 else if (entry2.type == EntryType::IntrudingDisc)
     100             :                 {
     101           0 :                     result.indexes[CollisionType::DiscIntrudingDisc].push_back(collisions.size());
     102           0 :                     collisions.push_back(Collision{.disc = &(*params_.discs)[entry1.index],
     103           0 :                                                    .otherDisc = (*params_.intrudingDiscs)[entry2.index],
     104             :                                                    .type = CollisionType::DiscIntrudingDisc});
     105             :                 }
     106             :                 else
     107             :                 {
     108           5 :                     result.indexes[CollisionType::DiscDisc].push_back(collisions.size());
     109          10 :                     collisions.push_back(Collision{.disc = &(*params_.discs)[entry1.index],
     110           5 :                                                    .otherDisc = &(*params_.discs)[entry2.index],
     111             :                                                    .type = CollisionType::DiscDisc});
     112             :                 }
     113             : 
     114           5 :                 ++collisionCounts_[getDiscPointer(entry1)->getTypeID()];
     115           5 :                 ++collisionCounts_[getDiscPointer(entry2)->getTypeID()];
     116             :             }
     117             :         }
     118             :     }
     119             : 
     120          18 :     return result;
     121           0 : }
     122             : 
     123           6 : DiscTypeMap<int> CollisionDetector::getAndResetCollisionCounts()
     124             : {
     125           6 :     auto tmp = std::move(collisionCounts_);
     126           6 :     collisionCounts_.clear();
     127             : 
     128           6 :     return tmp;
     129             : }
     130             : 
     131          18 : bool CollisionDetector::discIsContainedByMembrane(const Entry& entry)
     132             : {
     133          18 :     auto disc = (*params_.discs)[entry.index];
     134             : 
     135          18 :     return mathutils::circleIsFullyContainedByCircle(
     136          18 :         disc.getPosition(), discTypeRegistry_.getByID(disc.getTypeID()).getRadius(),
     137          18 :         params_.containingMembrane->getPosition(),
     138          54 :         membraneTypeRegistry_.getByID(params_.containingMembrane->getTypeID()).getRadius());
     139             : }
     140             : 
     141             : } // namespace cell

Generated by: LCOV version 1.14