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