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