Skip to content

Commit

Permalink
Refactors Contact & adds IsFor to facilitate other code & increase te…
Browse files Browse the repository at this point in the history
…st coverage
  • Loading branch information
louis-langholtz committed Oct 7, 2023
1 parent 67a3b1e commit cecbabe
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 114 deletions.
75 changes: 23 additions & 52 deletions Library/include/playrho/Contact.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,23 +113,11 @@ class Contact
/// @see IsEnabled.
constexpr void UnsetEnabled() noexcept;

/// @brief Gets the body-A identifier.
constexpr BodyID GetBodyA() const noexcept;
/// @brief Gets contactable A.
constexpr const Contactable& GetContactableA() const noexcept;

/// @brief Gets shape A in this contact.
constexpr ShapeID GetShapeA() const noexcept;

/// @brief Get the child primitive index for shape A.
constexpr ChildCounter GetChildIndexA() const noexcept;

/// @brief Gets the body-B identifier.
constexpr BodyID GetBodyB() const noexcept;

/// @brief Gets shape B in this contact.
constexpr ShapeID GetShapeB() const noexcept;

/// @brief Get the child primitive index for shape B.
constexpr ChildCounter GetChildIndexB() const noexcept;
/// @brief Gets contactable B.
constexpr const Contactable& GetContactableB() const noexcept;

/// @brief Sets the friction value for this contact.
/// @details Override the default friction mixture.
Expand Down Expand Up @@ -368,24 +356,14 @@ constexpr void Contact::UnsetTouching() noexcept
m_flags &= ~e_touchingFlag;
}

constexpr BodyID Contact::GetBodyA() const noexcept
{
return m_contactableA.bodyId;
}

constexpr BodyID Contact::GetBodyB() const noexcept
{
return m_contactableB.bodyId;
}

constexpr ShapeID Contact::GetShapeA() const noexcept
constexpr const Contactable& Contact::GetContactableA() const noexcept
{
return m_contactableA.shapeId;
return m_contactableA;
}

constexpr ShapeID Contact::GetShapeB() const noexcept
constexpr const Contactable& Contact::GetContactableB() const noexcept
{
return m_contactableB.shapeId;
return m_contactableB;
}

constexpr void Contact::FlagForFiltering() noexcept
Expand Down Expand Up @@ -482,16 +460,6 @@ constexpr Contact::substep_type Contact::GetToiCount() const noexcept
return m_toiCount;
}

constexpr ChildCounter Contact::GetChildIndexA() const noexcept
{
return m_contactableA.childId;
}

constexpr ChildCounter Contact::GetChildIndexB() const noexcept
{
return m_contactableB.childId;
}

constexpr bool Contact::IsSensor() const noexcept
{
return (m_flags & e_sensorFlag) != 0u;
Expand Down Expand Up @@ -549,12 +517,7 @@ constexpr void Contact::IncrementToiCount() noexcept
/// @relatedalso Contact
constexpr bool operator==(const Contact& lhs, const Contact& rhs) noexcept
{
return lhs.GetBodyA() == rhs.GetBodyA() && //
lhs.GetBodyB() == rhs.GetBodyB() && //
lhs.GetShapeA() == rhs.GetShapeA() && //
lhs.GetShapeB() == rhs.GetShapeB() && //
lhs.GetChildIndexA() == rhs.GetChildIndexA() && //
lhs.GetChildIndexB() == rhs.GetChildIndexB() && //
return lhs.GetContactableA() == rhs.GetContactableB() && //
lhs.GetFriction() == rhs.GetFriction() && //
lhs.GetRestitution() == rhs.GetRestitution() && //
lhs.GetTangentSpeed() == rhs.GetTangentSpeed() && //
Expand All @@ -581,42 +544,42 @@ constexpr bool operator!=(const Contact& lhs, const Contact& rhs) noexcept
/// @relatedalso Contact
constexpr BodyID GetBodyA(const Contact& contact) noexcept
{
return contact.GetBodyA();
return contact.GetContactableA().bodyId;
}

/// @brief Gets the body B ID of the given contact.
/// @relatedalso Contact
constexpr BodyID GetBodyB(const Contact& contact) noexcept
{
return contact.GetBodyB();
return contact.GetContactableB().bodyId;
}

/// @brief Gets the shape A associated with the given contact.
/// @relatedalso Contact
constexpr ShapeID GetShapeA(const Contact& contact) noexcept
{
return contact.GetShapeA();
return contact.GetContactableA().shapeId;
}

/// @brief Gets the shape B associated with the given contact.
/// @relatedalso Contact
constexpr ShapeID GetShapeB(const Contact& contact) noexcept
{
return contact.GetShapeB();
return contact.GetContactableB().shapeId;
}

/// @brief Gets the child index A of the given contact.
/// @relatedalso Contact
constexpr ChildCounter GetChildIndexA(const Contact& contact) noexcept
{
return contact.GetChildIndexA();
return contact.GetContactableA().childId;
}

/// @brief Gets the child index B of the given contact.
/// @relatedalso Contact
constexpr ChildCounter GetChildIndexB(const Contact& contact) noexcept
{
return contact.GetChildIndexB();
return contact.GetContactableB().childId;
}

/// @brief Whether the given contact is "impenetrable".
Expand Down Expand Up @@ -888,6 +851,14 @@ constexpr void SetTangentSpeed(Contact& contact, LinearVelocity value) noexcept
contact.SetTangentSpeed(value);
}

/// @brief Is-for convenience function.
/// @return true if contact is for the identified body and shape, else false.
constexpr bool IsFor(const Contact& c, BodyID bodyID, ShapeID shapeID) noexcept
{
return IsFor(c.GetContactableA(), bodyID, shapeID) // force newline
|| IsFor(c.GetContactableB(), bodyID, shapeID);
}

} // namespace playrho

#endif // PLAYRHO_CONTACT_HPP
5 changes: 3 additions & 2 deletions Library/source/playrho/d2/AABB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,9 @@ AABB ComputeIntersectingAABB(const World& world, // force newline

AABB ComputeIntersectingAABB(const World& world, const Contact& c)
{
return ComputeIntersectingAABB(world, c.GetBodyA(), c.GetShapeA(), c.GetChildIndexA(),
c.GetBodyB(), c.GetShapeB(), c.GetChildIndexB());
return ComputeIntersectingAABB(world, // force newline
GetBodyA(c), GetShapeA(c), GetChildIndexA(c), // force newline
GetBodyB(c), GetShapeB(c), GetChildIndexB(c));
}

AABB GetAABB(const RayCastInput& input) noexcept
Expand Down
79 changes: 35 additions & 44 deletions Library/source/playrho/d2/AabbTreeWorld.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1159,17 +1159,12 @@ void SetShape(AabbTreeWorld& world, ShapeID id, Shape def) // NOLINT(readability
}), lastProxy);
// Destroy any contacts associated with the fixture.
Erase(world.m_bodyContacts[to_underlying(bodyId)], [&world,bodyId,shapeId,&b](ContactID contactID) {
auto& contact = world.m_contactBuffer[to_underlying(contactID)];
const auto bodyIdA = GetBodyA(contact);
const auto shapeIdA = GetShapeA(contact);
const auto bodyIdB = GetBodyB(contact);
const auto shapeIdB = GetShapeB(contact);
if ((bodyIdA == bodyId && shapeIdA == shapeId) ||
(bodyIdB == bodyId && shapeIdB == shapeId)) {
world.Destroy(contactID, &b);
return true;
const auto& contact = world.m_contactBuffer[to_underlying(contactID)];
if (!IsFor(contact, bodyId, shapeId)) {
return false;
}
return false;
world.Destroy(contactID, &b);
return true;
});
const auto fixture = std::make_pair(bodyId, shapeId);
EraseAll(world.m_fixturesForProxies, fixture);
Expand All @@ -1186,8 +1181,8 @@ void SetShape(AabbTreeWorld& world, ShapeID id, Shape def) // NOLINT(readability
const auto shapeIdB = GetShapeB(c);
if (shapeIdA == id || shapeIdB == id) {
c.FlagForFiltering();
world.m_bodyBuffer[to_underlying(c.GetBodyA())].SetAwake();
world.m_bodyBuffer[to_underlying(c.GetBodyB())].SetAwake();
world.m_bodyBuffer[to_underlying(GetBodyA(c))].SetAwake();
world.m_bodyBuffer[to_underlying(GetBodyB(c))].SetAwake();
anyNeedFiltering = true;
}
}
Expand All @@ -1201,10 +1196,10 @@ void SetShape(AabbTreeWorld& world, ShapeID id, Shape def) // NOLINT(readability
if ((IsSensor(shape) != IsSensor(def)) || (GetFriction(shape) != GetFriction(def)) ||
(GetRestitution(shape) != GetRestitution(def)) || geometryChanged) {
for (auto&& c: world.m_contactBuffer) {
if (c.GetShapeA() == id || c.GetShapeB() == id) {
if (GetShapeA(c) == id || GetShapeB(c) == id) {
c.FlagForUpdating();
world.m_bodyBuffer[to_underlying(c.GetBodyA())].SetAwake();
world.m_bodyBuffer[to_underlying(c.GetBodyB())].SetAwake();
world.m_bodyBuffer[to_underlying(GetBodyA(c))].SetAwake();
world.m_bodyBuffer[to_underlying(GetBodyB(c))].SetAwake();
}
}
}
Expand Down Expand Up @@ -2086,8 +2081,8 @@ void AabbTreeWorld::InternalDestroy(ContactID contactID, const Body* from)
// so call it now
m_listeners.endContact(contactID);
}
const auto bodyIdA = contact.GetBodyA();
const auto bodyIdB = contact.GetBodyB();
const auto bodyIdA = GetBodyA(contact);
const auto bodyIdB = GetBodyB(contact);
const auto bodyA = &m_bodyBuffer[to_underlying(bodyIdA)];
const auto bodyB = &m_bodyBuffer[to_underlying(bodyIdB)];
if (bodyA != from) {
Expand Down Expand Up @@ -2147,12 +2142,12 @@ AabbTreeWorld::DestroyContactsStats AabbTreeWorld::DestroyContacts(KeyedContactI
const auto contactID = std::get<ContactID>(c);
auto& contact = m_contactBuffer[to_underlying(contactID)];
if (contact.NeedsFiltering()) {
const auto bodyIdA = contact.GetBodyA();
const auto bodyIdB = contact.GetBodyB();
const auto bodyIdA = GetBodyA(contact);
const auto bodyIdB = GetBodyB(contact);
const auto& bodyA = m_bodyBuffer[to_underlying(bodyIdA)];
const auto& bodyB = m_bodyBuffer[to_underlying(bodyIdB)];
const auto& shapeA = m_shapeBuffer[to_underlying(contact.GetShapeA())];
const auto& shapeB = m_shapeBuffer[to_underlying(contact.GetShapeB())];
const auto& shapeA = m_shapeBuffer[to_underlying(GetShapeA(contact))];
const auto& shapeB = m_shapeBuffer[to_underlying(GetShapeB(contact))];
if (!EitherIsAccelerable(bodyA, bodyB) ||
!ShouldCollide(m_jointBuffer, m_bodyJoints, bodyIdA, bodyIdB) ||
!ShouldCollide(shapeA, shapeB)) {
Expand Down Expand Up @@ -2195,8 +2190,8 @@ AabbTreeWorld::UpdateContactsStats AabbTreeWorld::UpdateContacts(const StepConf&
for_each(/*execution::par_unseq,*/ begin(m_contacts), end(m_contacts), [&](const auto& c) {
const auto contactID = std::get<ContactID>(c);
auto& contact = m_contactBuffer[to_underlying(contactID)];
const auto& bodyA = m_bodyBuffer[to_underlying(contact.GetBodyA())];
const auto& bodyB = m_bodyBuffer[to_underlying(contact.GetBodyB())];
const auto& bodyA = m_bodyBuffer[to_underlying(GetBodyA(contact))];
const auto& bodyB = m_bodyBuffer[to_underlying(GetBodyB(contact))];

// Awake && speedable (dynamic or kinematic) means collidable.
// At least one body must be collidable
Expand Down Expand Up @@ -2442,12 +2437,12 @@ void AabbTreeWorld::Update( // NOLINT(readability-function-cognitive-complexity)
const auto oldTouching = c.IsTouching();
auto newTouching = false;

const auto bodyIdA = c.GetBodyA();
const auto shapeIdA = c.GetShapeA();
const auto indexA = c.GetChildIndexA();
const auto bodyIdB = c.GetBodyB();
const auto shapeIdB = c.GetShapeB();
const auto indexB = c.GetChildIndexB();
const auto bodyIdA = GetBodyA(c);
const auto shapeIdA = GetShapeA(c);
const auto indexA = GetChildIndexA(c);
const auto bodyIdB = GetBodyB(c);
const auto shapeIdB = GetShapeB(c);
const auto indexB = GetChildIndexB(c);
const auto& shapeA = m_shapeBuffer[to_underlying(shapeIdA)];
const auto& shapeB = m_shapeBuffer[to_underlying(shapeIdB)];
const auto& bodyA = m_bodyBuffer[to_underlying(bodyIdA)];
Expand Down Expand Up @@ -2633,16 +2628,12 @@ void SetBody(AabbTreeWorld& world, BodyID id, Body value) // NOLINT(readability-
for (auto&& shapeId: oldShapeIds) {
// Destroy any contacts associated with the fixture.
Erase(world.m_bodyContacts[to_underlying(id)], [&world,id,shapeId,&body](ContactID contactID) {
auto& contact = world.m_contactBuffer[to_underlying(contactID)];
const auto bodyIdA = GetBodyA(contact);
const auto shapeIdA = GetShapeA(contact);
const auto bodyIdB = GetBodyB(contact);
const auto shapeIdB = GetShapeB(contact);
if ((bodyIdA == id && shapeIdA == shapeId) || (bodyIdB == id && shapeIdB == shapeId)) {
world.Destroy(contactID, &body);
return true;
const auto& contact = world.m_contactBuffer[to_underlying(contactID)];
if (!IsFor(contact, id, shapeId)) {
return false;
}
return false;
world.Destroy(contactID, &body);
return true;
});
EraseAll(world.m_fixturesForProxies, std::make_pair(id, shapeId));
DestroyProxies(world.m_tree, FindProxies(world.m_tree, id, shapeId), world.m_proxiesForContacts);
Expand All @@ -2664,8 +2655,8 @@ void SetBody(AabbTreeWorld& world, BodyID id, Body value) // NOLINT(readability-
else { // sleep associated contacts whose other body is also asleep
for (const auto& elem: world.m_bodyContacts[to_underlying(id)]) {
auto& contact = world.m_contactBuffer[to_underlying(std::get<ContactID>(elem))];
const auto otherID = (contact.GetBodyA() != id)
? contact.GetBodyA(): contact.GetBodyB();
const auto otherID = (GetBodyA(contact) != id)
? GetBodyA(contact): GetBodyB(contact);
if (!world.m_bodyBuffer[to_underlying(otherID)].IsAwake()) {
contact.UnsetIsActive();
}
Expand All @@ -2683,10 +2674,10 @@ void SetContact(AabbTreeWorld& world, ContactID id, Contact value)
const auto& contact = world.m_contactBuffer.at(to_underlying(id));

// Make sure body identifiers and shape identifiers are valid...
[[maybe_unused]] const auto& bodyA = world.m_bodyBuffer.at(to_underlying(value.GetBodyA()));
[[maybe_unused]] const auto& bodyB = world.m_bodyBuffer.at(to_underlying(value.GetBodyB()));
[[maybe_unused]] const auto& shapeA = world.m_shapeBuffer.at(to_underlying(value.GetShapeA()));
[[maybe_unused]] const auto& shapeB = world.m_shapeBuffer.at(to_underlying(value.GetShapeB()));
[[maybe_unused]] const auto& bodyA = world.m_bodyBuffer.at(to_underlying(GetBodyA(value)));
[[maybe_unused]] const auto& bodyB = world.m_bodyBuffer.at(to_underlying(GetBodyB(value)));
[[maybe_unused]] const auto& shapeA = world.m_shapeBuffer.at(to_underlying(GetShapeA(value)));
[[maybe_unused]] const auto& shapeB = world.m_shapeBuffer.at(to_underlying(GetShapeB(value)));

assert(IsActive(contact) == (IsAwake(bodyA) || IsAwake(bodyB)));
assert(IsImpenetrable(contact) == (IsImpenetrable(bodyA) || IsImpenetrable(bodyB)));
Expand Down
8 changes: 4 additions & 4 deletions Library/source/playrho/d2/WorldManifold.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,10 @@ WorldManifold GetWorldManifold(const Manifold& manifold,
WorldManifold GetWorldManifold(const World& world, const Contact& contact, const Manifold& manifold)
{
return GetWorldManifold(manifold,
GetTransformation(world, contact.GetBodyA()),
GetVertexRadius(GetShape(world, contact.GetShapeA()), contact.GetChildIndexA()),
GetTransformation(world, contact.GetBodyB()),
GetVertexRadius(GetShape(world, contact.GetShapeB()), contact.GetChildIndexB()));
GetTransformation(world, GetBodyA(contact)),
GetVertexRadius(GetShape(world, GetShapeA(contact)), GetChildIndexA(contact)),
GetTransformation(world, GetBodyB(contact)),
GetVertexRadius(GetShape(world, GetShapeB(contact)), GetChildIndexB(contact)));
}

} /* namespace d2 */
Expand Down
1 change: 1 addition & 0 deletions UnitTests/AabbTreeWorld.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ TEST(AabbTreeWorld, CreateDestroyContactingBodies)
EXPECT_TRUE(contacts.empty());
EXPECT_EQ(contacts.size(), ContactCounter(0));
EXPECT_TRUE(IsDestroyed(world, ContactID{0u}));
EXPECT_THROW(SetContact(world, ContactID{0u}, Contact{}), std::out_of_range);

Destroy(world, body2);
EXPECT_EQ(GetBodies(world).size(), BodyCounter(0));
Expand Down
Loading

0 comments on commit cecbabe

Please sign in to comment.