From 1988b9e93f48188a11455852f93eef865d69dd60 Mon Sep 17 00:00:00 2001 From: louis-langholtz Date: Wed, 14 Jun 2017 13:56:43 -0600 Subject: [PATCH] Adds copy construction and copy assignment for World instances. --- Box2D/Dynamics/Body.cpp | 2 +- Box2D/Dynamics/BodyAtty.hpp | 11 +- Box2D/Dynamics/BodyDef.cpp | 48 +++ Box2D/Dynamics/BodyDef.hpp | 14 +- Box2D/Dynamics/ContactAtty.hpp | 12 +- Box2D/Dynamics/Contacts/Contact.hpp | 6 +- Box2D/Dynamics/Fixture.hpp | 6 +- Box2D/Dynamics/FixtureDef.cpp | 30 ++ Box2D/Dynamics/FixtureDef.hpp | 16 +- Box2D/Dynamics/World.cpp | 373 +++++++++++++++++-- Box2D/Dynamics/World.hpp | 39 +- Box2D/Dynamics/WorldDef.hpp | 13 +- Build/xcode5/Box2D.xcodeproj/project.pbxproj | 12 + Testbed/Framework/Main.cpp | 2 + Testbed/Framework/Test.cpp | 42 +++ Testbed/Framework/Test.hpp | 8 +- Testbed/Tests/DistanceTest.hpp | 8 +- Testbed/Tests/Pulleys.hpp | 4 +- Testbed/Tests/SensorTest.hpp | 2 +- Testbed/Tests/Tiles.hpp | 33 +- Testbed/Tests/Tumbler.hpp | 2 +- UnitTests/Body.cpp | 6 +- UnitTests/Fixture.cpp | 2 +- UnitTests/World.cpp | 98 ++++- 24 files changed, 687 insertions(+), 102 deletions(-) create mode 100644 Box2D/Dynamics/BodyDef.cpp create mode 100644 Box2D/Dynamics/FixtureDef.cpp diff --git a/Box2D/Dynamics/Body.cpp b/Box2D/Dynamics/Body.cpp index 232920e28..de5151c01 100644 --- a/Box2D/Dynamics/Body.cpp +++ b/Box2D/Dynamics/Body.cpp @@ -208,9 +208,9 @@ void Body::SetMassData(const MassData& massData) Position{Transform(massData.center, GetTransformation()), GetAngle()}, massData.center }; - const auto newCenter = GetWorldCenter(); // Update center of mass velocity. + const auto newCenter = GetWorldCenter(); const auto deltaCenter = newCenter - oldCenter; m_velocity.linear += GetRevPerpendicular(deltaCenter) * m_velocity.angular / Radian; diff --git a/Box2D/Dynamics/BodyAtty.hpp b/Box2D/Dynamics/BodyAtty.hpp index c0d8227d8..208835603 100644 --- a/Box2D/Dynamics/BodyAtty.hpp +++ b/Box2D/Dynamics/BodyAtty.hpp @@ -114,7 +114,16 @@ namespace box2d { return b.Insert(value); } - + + static bool Insert(Body* b, Joint* value) + { + if (b) + { + return Insert(*b, value); + } + return false; + } + static bool Insert(Body& b, Contact* value) { return b.Insert(value); diff --git a/Box2D/Dynamics/BodyDef.cpp b/Box2D/Dynamics/BodyDef.cpp new file mode 100644 index 000000000..c513319af --- /dev/null +++ b/Box2D/Dynamics/BodyDef.cpp @@ -0,0 +1,48 @@ +/* + * Original work Copyright (c) 2006-2011 Erin Catto http://www.box2d.org + * Modified work Copyright (c) 2017 Louis Langholtz https://github.com/louis-langholtz/Box2D + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include +#include + +namespace box2d +{ + BodyDef GetBodyDef(const Body& body) noexcept + { + auto def = BodyDef{}; + def.type = body.GetType(); + def.position = body.GetLocation(); + def.angle = body.GetAngle(); + def.linearVelocity = GetLinearVelocity(body); + def.angularVelocity = GetAngularVelocity(body); + def.linearAcceleration = body.GetLinearAcceleration(); + def.angularAcceleration = body.GetAngularAcceleration(); + def.linearDamping = body.GetLinearDamping(); + def.angularDamping = body.GetAngularDamping(); + def.underActiveTime = body.GetUnderActiveTime(); + def.allowSleep = body.IsSleepingAllowed(); + def.awake = body.IsAwake(); + def.fixedRotation = body.IsFixedRotation(); + def.bullet = body.IsAccelerable() && body.IsImpenetrable(); + def.enabled = body.IsEnabled(); + def.userData = body.GetUserData(); + return def; + } +} diff --git a/Box2D/Dynamics/BodyDef.hpp b/Box2D/Dynamics/BodyDef.hpp index 974fffd51..2d2fed6dd 100644 --- a/Box2D/Dynamics/BodyDef.hpp +++ b/Box2D/Dynamics/BodyDef.hpp @@ -3,17 +3,19 @@ * Modified work Copyright (c) 2017 Louis Langholtz https://github.com/louis-langholtz/Box2D * * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages + * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. + * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: + * * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. + * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ @@ -225,6 +227,8 @@ namespace box2d return BodyDef{}; } + BodyDef GetBodyDef(const Body& body) noexcept; + } // namespace box2d #endif /* BodyDef_hpp */ diff --git a/Box2D/Dynamics/ContactAtty.hpp b/Box2D/Dynamics/ContactAtty.hpp index 9ce5648c3..83784f3c2 100644 --- a/Box2D/Dynamics/ContactAtty.hpp +++ b/Box2D/Dynamics/ContactAtty.hpp @@ -45,6 +45,11 @@ namespace box2d return c.GetMutableManifold(); } + static void CopyFlags(Contact& to, const Contact& from) noexcept + { + to.m_flags = from.m_flags; + } + static void SetToi(Contact& c, RealNum value) noexcept { c.SetToi(value); @@ -60,9 +65,14 @@ namespace box2d ++c.m_toiCount; } + static void SetToiCount(Contact& c, Contact::substep_type value) noexcept + { + c.SetToiCount(value); + } + static void ResetToiCount(Contact& c) noexcept { - c.ResetToiCount(); + c.SetToiCount(0); } static void UnflagForFiltering(Contact& c) noexcept diff --git a/Box2D/Dynamics/Contacts/Contact.hpp b/Box2D/Dynamics/Contacts/Contact.hpp index 00ccd15d3..e36d571a9 100644 --- a/Box2D/Dynamics/Contacts/Contact.hpp +++ b/Box2D/Dynamics/Contacts/Contact.hpp @@ -237,7 +237,7 @@ class Contact void UnsetToi() noexcept; - void ResetToiCount() noexcept; + void SetToiCount(substep_type value) noexcept; /// Sets the touching flag state. /// @note This should only be called if either: @@ -445,9 +445,9 @@ inline void Contact::UnsetToi() noexcept m_flags &= ~Contact::e_toiFlag; } -inline void Contact::ResetToiCount() noexcept +inline void Contact::SetToiCount(substep_type value) noexcept { - m_toiCount = 0; + m_toiCount = value; } inline Contact::substep_type Contact::GetToiCount() const noexcept diff --git a/Box2D/Dynamics/Fixture.hpp b/Box2D/Dynamics/Fixture.hpp index 4a420ba78..481833732 100644 --- a/Box2D/Dynamics/Fixture.hpp +++ b/Box2D/Dynamics/Fixture.hpp @@ -104,7 +104,7 @@ class Fixture /// Gets the child shape. /// @details The shape is not modifiable. Use a new fixture instead. - const Shape* GetShape() const noexcept; + std::shared_ptr GetShape() const noexcept; /// Set if this fixture is a sensor. void SetSensor(bool sensor) noexcept; @@ -185,9 +185,9 @@ class Fixture bool m_isSensor = false; ///< Is/is-not sensor. 1-bytes. }; -inline const Shape* Fixture::GetShape() const noexcept +inline std::shared_ptr Fixture::GetShape() const noexcept { - return m_shape.get(); + return m_shape; } inline bool Fixture::IsSensor() const noexcept diff --git a/Box2D/Dynamics/FixtureDef.cpp b/Box2D/Dynamics/FixtureDef.cpp new file mode 100644 index 000000000..30659250a --- /dev/null +++ b/Box2D/Dynamics/FixtureDef.cpp @@ -0,0 +1,30 @@ +/* + * Original work Copyright (c) 2006-2009 Erin Catto http://www.box2d.org + * Modified work Copyright (c) 2017 Louis Langholtz https://github.com/louis-langholtz/Box2D + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include +#include + +using namespace box2d; + +FixtureDef box2d::GetFixtureDef(const Fixture& fixture) noexcept +{ + return FixtureDef{fixture.GetUserData(), fixture.IsSensor(), fixture.GetFilterData()}; +} diff --git a/Box2D/Dynamics/FixtureDef.hpp b/Box2D/Dynamics/FixtureDef.hpp index abd190815..114cf32ac 100644 --- a/Box2D/Dynamics/FixtureDef.hpp +++ b/Box2D/Dynamics/FixtureDef.hpp @@ -3,17 +3,19 @@ * Modified work Copyright (c) 2017 Louis Langholtz https://github.com/louis-langholtz/Box2D * * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages + * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. + * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: + * * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. + * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ @@ -27,6 +29,8 @@ namespace box2d { + class Fixture; + /// @brief Fixture definition. /// /// @details A fixture definition is used to create a fixture. @@ -72,6 +76,8 @@ namespace box2d { return FixtureDef{}; } + FixtureDef GetFixtureDef(const Fixture& fixture) noexcept; + } // namespace box2d #endif /* FixtureDef_hpp */ diff --git a/Box2D/Dynamics/World.cpp b/Box2D/Dynamics/World.cpp index 2db8f270d..31f8f047e 100644 --- a/Box2D/Dynamics/World.cpp +++ b/Box2D/Dynamics/World.cpp @@ -3,17 +3,19 @@ * Modified work Copyright (c) 2017 Louis Langholtz https://github.com/louis-langholtz/Box2D * * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages + * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. + * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: + * * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. + * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ @@ -26,20 +28,33 @@ #include #include #include -#include #include -#include #include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + #include #include #include #include #include -#include -#include -#include - #include #include #include @@ -452,12 +467,59 @@ World::World(const WorldDef& def): m_minVertexRadius{def.minVertexRadius}, m_maxVertexRadius{def.maxVertexRadius} { - assert(::box2d::IsValid(def.gravity.x) && ::box2d::IsValid(def.gravity.y)); - assert(def.minVertexRadius > Length{0}); - assert(def.minVertexRadius < def.maxVertexRadius); + if (def.minVertexRadius > def.maxVertexRadius) + { + throw InvalidArgument("max vertex radius must be >= min vertex radius"); + } +} + +World::World(const World& other): + m_gravity{other.m_gravity}, + m_destructionListener{other.m_destructionListener}, + m_contactListener{other.m_contactListener}, + m_contactFilter{other.m_contactFilter}, + m_flags{other.m_flags}, + m_inv_dt0{other.m_inv_dt0}, + m_minVertexRadius{other.m_minVertexRadius}, + m_maxVertexRadius{other.m_maxVertexRadius}, + m_broadPhase{other.m_broadPhase} +{ + auto bodyMap = std::map(); + auto fixtureMap = std::map(); + CopyBodies(bodyMap, fixtureMap, other.GetBodies()); + CopyJoints(bodyMap, other.GetJoints()); + CopyContacts(bodyMap, fixtureMap, other.GetContacts()); } +World& World::operator= (const World& other) +{ + Clear(); + + m_gravity = other.m_gravity; + m_destructionListener = other.m_destructionListener; + m_contactListener = other.m_contactListener; + m_contactFilter = other.m_contactFilter; + m_flags = other.m_flags; + m_inv_dt0 = other.m_inv_dt0; + m_minVertexRadius = other.m_minVertexRadius; + m_maxVertexRadius = other.m_maxVertexRadius; + m_broadPhase = other.m_broadPhase; + + auto bodyMap = std::map(); + auto fixtureMap = std::map(); + CopyBodies(bodyMap, fixtureMap, other.GetBodies()); + CopyJoints(bodyMap, other.GetJoints()); + CopyContacts(bodyMap, fixtureMap, other.GetContacts()); + + return *this; +} + World::~World() +{ + Clear(); +} + +void World::Clear() noexcept { // Gets rid of the associated contacts. while (!m_contacts.empty()) @@ -480,7 +542,7 @@ World::~World() } m_contacts.pop_front(); } - + // Gets rid of the created bodies and any associated fixtures. // Do this before getting rid of joints so that joints can be removed from // bodies in their listed order. @@ -512,6 +574,245 @@ World::~World() } } +void World::CopyBodies(std::map& bodyMap, + std::map& fixtureMap, + SizedRange range) +{ + for (const auto& otherBody: range) + { + const auto newBody = CreateBody(GetBodyDef(otherBody)); + for (auto&& otherFixture: otherBody.GetFixtures()) + { + const auto shape = otherFixture.GetShape(); + const auto fixtureDef = GetFixtureDef(otherFixture); + const auto newFixture = BodyAtty::CreateFixture(*newBody, shape, fixtureDef); + fixtureMap[&otherFixture] = newFixture; + const auto childCount = otherFixture.GetProxyCount(); + const auto proxies = static_cast(alloc(sizeof(FixtureProxy) * childCount)); + for (auto childIndex = decltype(childCount){0}; childIndex < childCount; ++childIndex) + { + const auto proxyPtr = proxies + childIndex; + const auto fp = otherFixture.GetProxy(childIndex); + new (proxyPtr) FixtureProxy{fp->aabb, fp->proxyId, newFixture, childIndex}; + m_broadPhase.SetUserData(fp->proxyId, proxyPtr); + } + FixtureAtty::SetProxies(*newFixture, Span(proxies, childCount)); + } + newBody->SetMassData(GetMassData(otherBody)); + bodyMap[&otherBody] = newBody; + } +} + +void World::CopyContacts(const std::map& bodyMap, + const std::map& fixtureMap, + SizedRange range) +{ + for (const auto& otherContact: range) + { + const auto otherFixtureA = otherContact.GetFixtureA(); + const auto otherFixtureB = otherContact.GetFixtureB(); + const auto childIndexA = otherContact.GetChildIndexA(); + const auto childIndexB = otherContact.GetChildIndexB(); + const auto newFixtureA = fixtureMap.at(otherFixtureA); + const auto newFixtureB = fixtureMap.at(otherFixtureB); + const auto newBodyA = bodyMap.at(otherFixtureA->GetBody()); + const auto newBodyB = bodyMap.at(otherFixtureB->GetBody()); + + m_contacts.emplace_back(newFixtureA, childIndexA, newFixtureB, childIndexB); + const auto newContact = GetContactPtr(m_contacts.back()); + assert(newContact); + if (newContact) + { + BodyAtty::Insert(*newBodyA, newContact); + BodyAtty::Insert(*newBodyB, newContact); + // No need to wake up the bodies - this should already be done due to above copy + + newContact->SetFriction(otherContact.GetFriction()); + newContact->SetRestitution(otherContact.GetRestitution()); + newContact->SetTangentSpeed(otherContact.GetTangentSpeed()); + auto& manifold = ContactAtty::GetMutableManifold(*newContact); + manifold = otherContact.GetManifold(); + ContactAtty::CopyFlags(*newContact, otherContact); + ContactAtty::SetToi(*newContact, otherContact.GetToi()); + ContactAtty::SetToiCount(*newContact, otherContact.GetToiCount()); + } + } +} + +void World::CopyJoints(const std::map& bodyMap, + SizedRange range) +{ + auto jointMap = std::map(); + + for (const auto otherJoint: range) + { + const auto type = otherJoint->GetType(); + switch (type) + { + case JointType::Unknown: + { + break; + } + case JointType::Revolute: + { + const auto oJoint = static_cast(otherJoint); + auto def = GetRevoluteJointDef(*oJoint); + def.bodyA = bodyMap.at(def.bodyA); + def.bodyB = bodyMap.at(def.bodyB); + const auto j = JointAtty::Create(def); + if (j) + { + Add(j, def.bodyA, def.bodyB); + jointMap[oJoint] = j; + } + break; + } + case JointType::Prismatic: + { + const auto oJoint = static_cast(otherJoint); + auto def = GetPrismaticJointDef(*oJoint); + def.bodyA = bodyMap.at(def.bodyA); + def.bodyB = bodyMap.at(def.bodyB); + const auto j = JointAtty::Create(def); + if (j) + { + Add(j, def.bodyA, def.bodyB); + jointMap[oJoint] = j; + } + break; + } + case JointType::Distance: + { + const auto oJoint = static_cast(otherJoint); + auto def = GetDistanceJointDef(*oJoint); + def.bodyA = bodyMap.at(def.bodyA); + def.bodyB = bodyMap.at(def.bodyB); + const auto j = JointAtty::Create(def); + if (j) + { + Add(j, def.bodyA, def.bodyB); + jointMap[oJoint] = j; + } + break; + } + case JointType::Pulley: + { + const auto oJoint = static_cast(otherJoint); + auto def = GetPulleyJointDef(*oJoint); + def.bodyA = bodyMap.at(def.bodyA); + def.bodyB = bodyMap.at(def.bodyB); + const auto j = JointAtty::Create(def); + if (j) + { + Add(j, def.bodyA, def.bodyB); + jointMap[oJoint] = j; + } + break; + } + case JointType::Mouse: + { + const auto oJoint = static_cast(otherJoint); + auto def = GetMouseJointDef(*oJoint); + def.bodyA = bodyMap.at(def.bodyA); + def.bodyB = bodyMap.at(def.bodyB); + const auto j = JointAtty::Create(def); + if (j) + { + Add(j, def.bodyA, def.bodyB); + jointMap[oJoint] = j; + } + break; + } + case JointType::Gear: + { + const auto oJoint = static_cast(otherJoint); + auto def = GetGearJointDef(*oJoint); + def.bodyA = bodyMap.at(def.bodyA); + def.bodyB = bodyMap.at(def.bodyB); + def.joint1 = jointMap.at(def.joint1); + def.joint2 = jointMap.at(def.joint2); + const auto j = JointAtty::Create(def); + if (j) + { + Add(j, def.bodyA, def.bodyB); + jointMap[oJoint] = j; + } + break; + } + case JointType::Wheel: + { + const auto oJoint = static_cast(otherJoint); + auto def = GetWheelJointDef(*oJoint); + def.bodyA = bodyMap.at(def.bodyA); + def.bodyB = bodyMap.at(def.bodyB); + const auto j = JointAtty::Create(def); + if (j) + { + Add(j, def.bodyA, def.bodyB); + jointMap[oJoint] = j; + } + break; + } + case JointType::Weld: + { + const auto oJoint = static_cast(otherJoint); + auto def = GetWeldJointDef(*oJoint); + def.bodyA = bodyMap.at(def.bodyA); + def.bodyB = bodyMap.at(def.bodyB); + const auto j = JointAtty::Create(def); + if (j) + { + Add(j, def.bodyA, def.bodyB); + jointMap[oJoint] = j; + } + break; + } + case JointType::Friction: + { + const auto oJoint = static_cast(otherJoint); + auto def = GetFrictionJointDef(*oJoint); + def.bodyA = bodyMap.at(def.bodyA); + def.bodyB = bodyMap.at(def.bodyB); + const auto j = JointAtty::Create(def); + if (j) + { + Add(j, def.bodyA, def.bodyB); + jointMap[oJoint] = j; + } + break; + } + case JointType::Rope: + { + const auto oJoint = static_cast(otherJoint); + auto def = GetRopeJointDef(*oJoint); + def.bodyA = bodyMap.at(def.bodyA); + def.bodyB = bodyMap.at(def.bodyB); + const auto j = JointAtty::Create(def); + if (j) + { + Add(j, def.bodyA, def.bodyB); + jointMap[oJoint] = j; + } + break; + } + case JointType::Motor: + { + const auto oJoint = static_cast(otherJoint); + auto def = GetMotorJointDef(*oJoint); + def.bodyA = bodyMap.at(def.bodyA); + def.bodyB = bodyMap.at(def.bodyB); + const auto j = JointAtty::Create(def); + if (j) + { + Add(j, def.bodyA, def.bodyB); + jointMap[oJoint] = j; + } + break; + } + } + } +} + void World::SetGravity(const LinearAcceleration2D gravity) noexcept { if (m_gravity != gravity) @@ -608,38 +909,28 @@ Joint* World::CreateJoint(const JointDef& def) } // Note: creating a joint doesn't wake the bodies. - auto j = JointAtty::Create(def); - if (!j) - { - return nullptr; - } - - // Connect to the bodies' doubly linked lists. - const auto bodyA = j->GetBodyA(); - const auto bodyB = j->GetBodyB(); - if (bodyA) - { - BodyAtty::Insert(*bodyA, j); - } - if (bodyB) + const auto j = JointAtty::Create(def); + if (j) { - BodyAtty::Insert(*bodyB, j); - } + const auto bodyA = j->GetBodyA(); + const auto bodyB = j->GetBodyB(); - // If the joint prevents collisions, then flag any contacts for filtering. - if (!def.collideConnected) - { - FlagContactsForFiltering(bodyA, bodyB); + Add(j, bodyA, bodyB); + + // If the joint prevents collisions, then flag any contacts for filtering. + if (!def.collideConnected) + { + FlagContactsForFiltering(bodyA, bodyB); + } } - - Add(*j); - return j; } -bool World::Add(Joint& j) +bool World::Add(Joint* j, Body* bodyA, Body* bodyB) { - m_joints.push_front(&j); + m_joints.push_back(j); + BodyAtty::Insert(bodyA, j); + BodyAtty::Insert(bodyB, j); return true; } @@ -2513,7 +2804,7 @@ size_t GetShapeCount(const World& world) noexcept const auto body = GetBodyPtr(b); for (auto&& fixture: body->GetFixtures()) { - shapes.insert(fixture.GetShape()); + shapes.insert(fixture.GetShape().get()); } } return shapes.size(); diff --git a/Box2D/Dynamics/World.hpp b/Box2D/Dynamics/World.hpp index ffecf2b57..51b0318ea 100644 --- a/Box2D/Dynamics/World.hpp +++ b/Box2D/Dynamics/World.hpp @@ -3,17 +3,19 @@ * Modified work Copyright (c) 2017 Louis Langholtz https://github.com/louis-langholtz/Box2D * * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages + * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. + * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: + * * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. + * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ @@ -33,6 +35,7 @@ #include #include +#include #include #include #include @@ -86,13 +89,20 @@ class World class LockedError; /// @brief Constructs a world object. + /// @throws InvalidArgument if the given max vertex radius is less than the min. World(const WorldDef& def = GetDefaultWorldDef()); + World(const World& copy); + + World& operator= (const World& other); + /// @brief Destructor. /// @details /// All physics entities are destroyed and all dynamically allocated memory is released. ~World(); + void Clear() noexcept; + /// Register a destruction listener. The listener is owned by you and must /// remain in scope. void SetDestructionListener(DestructionListener* listener) noexcept; @@ -350,6 +360,15 @@ class World ts_iters_t velocityIterations = 0; ///< Velocity iterations actually performed. }; + void CopyBodies(std::map& bodyMap, + std::map& fixtureMap, + SizedRange range); + void CopyJoints(const std::map& bodyMap, + SizedRange range); + void CopyContacts(const std::map& bodyMap, + const std::map& fixtureMap, + SizedRange range); + void InternalDestroy(Joint* joint); /// @brief Solves the step. @@ -445,7 +464,7 @@ class World ProcessContactsOutput ProcessContactsForTOI(Island& island, Body& body, RealNum toi, const StepConf& conf); - bool Add(Joint& j); + bool Add(Joint* j, Body* bodyA, Body* bodyB); bool Remove(Body& b); bool Remove(Joint& j); @@ -630,10 +649,10 @@ class World /// @sa Step. Frequency m_inv_dt0 = 0; - /// Minimum vertex radius. - const Length m_minVertexRadius; + /// @brief Minimum vertex radius. + Positive m_minVertexRadius; - /// Maximum vertex radius. + /// @brief Maximum vertex radius. /// @details /// This is the maximum shape vertex radius that any bodies' of this world should create /// fixtures for. Requests to create fixtures for shapes with vertex radiuses bigger than @@ -641,7 +660,7 @@ class World /// associated with this world that would otherwise not be able to be simulated due to /// numerical issues. It can also be set below this upper bound to constrain the differences /// between shape vertex radiuses to possibly more limited visual ranges. - const Length m_maxVertexRadius; + Positive m_maxVertexRadius; }; class World::LockedError: public std::logic_error diff --git a/Box2D/Dynamics/WorldDef.hpp b/Box2D/Dynamics/WorldDef.hpp index 200d0adab..38a41dbe4 100644 --- a/Box2D/Dynamics/WorldDef.hpp +++ b/Box2D/Dynamics/WorldDef.hpp @@ -24,6 +24,7 @@ /// Declarations of the WorldDef class. #include +#include namespace box2d { @@ -31,8 +32,8 @@ namespace box2d { struct WorldDef { constexpr WorldDef& UseGravity(LinearAcceleration2D value) noexcept; - constexpr WorldDef& UseMinVertexRadius(Length value) noexcept; - constexpr WorldDef& UseMaxVertexRadius(Length value) noexcept; + constexpr WorldDef& UseMinVertexRadius(Positive value) noexcept; + constexpr WorldDef& UseMaxVertexRadius(Positive value) noexcept; /// @brief Gravity. /// @details The acceleration all dynamic bodies are subject to. @@ -47,14 +48,14 @@ namespace box2d { /// @note This value probably should not be changed except to experiment with what can happen. /// @note Making it smaller means some shapes could have insufficient buffer for continuous collision. /// @note Making it larger may create artifacts for vertex collision. - Length minVertexRadius = DefaultLinearSlop * RealNum{2}; + Positive minVertexRadius = DefaultLinearSlop * RealNum{2}; /// @brief Maximum vertex radius. /// @details This is the maximum vertex radius that this world establishes which bodies /// shall allow fixtures to be created with. Trying to create a fixture with a shape /// having a larger vertex radius shall be rejected with a nullptr /// returned value. - Length maxVertexRadius = RealNum{255} * Meter; // linearSlop * 2550000 + Positive maxVertexRadius = RealNum{255} * Meter; // linearSlop * 2550000 }; constexpr inline WorldDef& WorldDef::UseGravity(LinearAcceleration2D value) noexcept @@ -63,13 +64,13 @@ namespace box2d { return *this; } - constexpr inline WorldDef& WorldDef::UseMinVertexRadius(Length value) noexcept + constexpr inline WorldDef& WorldDef::UseMinVertexRadius(Positive value) noexcept { minVertexRadius = value; return *this; } - constexpr inline WorldDef& WorldDef::UseMaxVertexRadius(Length value) noexcept + constexpr inline WorldDef& WorldDef::UseMaxVertexRadius(Positive value) noexcept { maxVertexRadius = value; return *this; diff --git a/Build/xcode5/Box2D.xcodeproj/project.pbxproj b/Build/xcode5/Box2D.xcodeproj/project.pbxproj index dde542d11..02a528d95 100644 --- a/Build/xcode5/Box2D.xcodeproj/project.pbxproj +++ b/Build/xcode5/Box2D.xcodeproj/project.pbxproj @@ -52,6 +52,10 @@ 470F9B371EDF6C82007EF7B6 /* RealNum.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 470F9B331EDF6C82007EF7B6 /* RealNum.hpp */; }; 470F9B3D1EE63515007EF7B6 /* InvalidArgument.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 470F9B3A1EE63515007EF7B6 /* InvalidArgument.hpp */; }; 470F9B3E1EE63515007EF7B6 /* InvalidArgument.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 470F9B3A1EE63515007EF7B6 /* InvalidArgument.hpp */; }; + 470F9B4C1EEF20B6007EF7B6 /* BodyDef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 470F9B4B1EEF20B6007EF7B6 /* BodyDef.cpp */; }; + 470F9B4D1EEF20B6007EF7B6 /* BodyDef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 470F9B4B1EEF20B6007EF7B6 /* BodyDef.cpp */; }; + 470F9B4F1EF0C8B8007EF7B6 /* FixtureDef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 470F9B4E1EF0C8B8007EF7B6 /* FixtureDef.cpp */; }; + 470F9B501EF0C8B8007EF7B6 /* FixtureDef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 470F9B4E1EF0C8B8007EF7B6 /* FixtureDef.cpp */; }; 4726DD0F1D3014990012A882 /* IndexPair.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4726DD0C1D3014990012A882 /* IndexPair.hpp */; }; 4726DD101D3014990012A882 /* IndexPair.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4726DD0C1D3014990012A882 /* IndexPair.hpp */; }; 4726DD1F1D305E5D0012A882 /* ContactFeature.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4726DD1C1D305E5D0012A882 /* ContactFeature.hpp */; }; @@ -453,6 +457,8 @@ 470F9B381EDF6F0D007EF7B6 /* RealNum.hpp.in */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = RealNum.hpp.in; sourceTree = ""; }; 470F9B3A1EE63515007EF7B6 /* InvalidArgument.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = InvalidArgument.hpp; sourceTree = ""; }; 470F9B491EEE40AA007EF7B6 /* BagOfDisks.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = BagOfDisks.hpp; sourceTree = ""; }; + 470F9B4B1EEF20B6007EF7B6 /* BodyDef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BodyDef.cpp; sourceTree = ""; }; + 470F9B4E1EF0C8B8007EF7B6 /* FixtureDef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FixtureDef.cpp; sourceTree = ""; }; 4726DD0C1D3014990012A882 /* IndexPair.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = IndexPair.hpp; sourceTree = ""; }; 4726DD1C1D305E5D0012A882 /* ContactFeature.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ContactFeature.hpp; sourceTree = ""; }; 4726DD261D3180980012A882 /* NewtonsCradle.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = NewtonsCradle.hpp; sourceTree = ""; }; @@ -960,6 +966,7 @@ 80BB894D141C3E5900F1753A /* Body.cpp */, 80BB894E141C3E5900F1753A /* Body.hpp */, 470F94C91EC4D95D00AA3C82 /* BodyAtty.hpp */, + 470F9B4B1EEF20B6007EF7B6 /* BodyDef.cpp */, 470F94A51EC4C79B00AA3C82 /* BodyDef.hpp */, 478E67C41E760AB7009B9AD5 /* BodyType.hpp */, 80BB895A141C3E5900F1753A /* Contacts */, @@ -968,6 +975,7 @@ 80BB8951141C3E5900F1753A /* Fixture.cpp */, 80BB8952141C3E5900F1753A /* Fixture.hpp */, 470F94B71EC4D4F800AA3C82 /* FixtureAtty.hpp */, + 470F9B4E1EF0C8B8007EF7B6 /* FixtureDef.cpp */, 470F94AB1EC4CAE600AA3C82 /* FixtureDef.hpp */, 4731DE181DDCD0E100E7F931 /* FixtureProxy.hpp */, 80BB8953141C3E5900F1753A /* Island.cpp */, @@ -1588,6 +1596,7 @@ 47AA5D9B1C4C5FBE008AF2BA /* BroadPhase.cpp in Sources */, 4726DD2B1D31B4090012A882 /* RayCastOutput.cpp in Sources */, 47AA5D9F1C4C5FBE008AF2BA /* Collision.cpp in Sources */, + 470F9B501EF0C8B8007EF7B6 /* FixtureDef.cpp in Sources */, 47AA5DA01C4C5FBE008AF2BA /* Distance.cpp in Sources */, 470F94D61EC649B300AA3C82 /* ContactKey.cpp in Sources */, 47AA5DA11C4C5FBE008AF2BA /* DynamicTree.cpp in Sources */, @@ -1601,6 +1610,7 @@ 47AA5DA91C4C5FBE008AF2BA /* Math.cpp in Sources */, 47AA5DAA1C4C5FBE008AF2BA /* Settings.cpp in Sources */, 47CF984C1DF9C4A30077F5F2 /* ShapeSeparation.cpp in Sources */, + 470F9B4D1EEF20B6007EF7B6 /* BodyDef.cpp in Sources */, 47AA5DAB1C4C5FBE008AF2BA /* StackAllocator.cpp in Sources */, 474AFBF91EB1B2CB002AA6C8 /* MultiShape.cpp in Sources */, 47AA5DAD1C4C5FBE008AF2BA /* Body.cpp in Sources */, @@ -1671,6 +1681,7 @@ 80BB8991141C3E5900F1753A /* DynamicTree.cpp in Sources */, 80BB8993141C3E5900F1753A /* TimeOfImpact.cpp in Sources */, 47928F3B1E6A099900EE6E9E /* BodyConstraint.cpp in Sources */, + 470F9B4C1EEF20B6007EF7B6 /* BodyDef.cpp in Sources */, 80BB8995141C3E5900F1753A /* ChainShape.cpp in Sources */, 4726DD2A1D31B4090012A882 /* RayCastOutput.cpp in Sources */, 80BB8997141C3E5900F1753A /* DiskShape.cpp in Sources */, @@ -1693,6 +1704,7 @@ 80BB89B1141C3E5900F1753A /* Island.cpp in Sources */, 475039911D2180D600D32047 /* DistanceProxy.cpp in Sources */, 80BB89B4141C3E5900F1753A /* World.cpp in Sources */, + 470F9B4F1EF0C8B8007EF7B6 /* FixtureDef.cpp in Sources */, 4751C34D1EC25951006D9E69 /* README.md in Sources */, 80BB89B6141C3E5900F1753A /* WorldCallbacks.cpp in Sources */, 80BB89BE141C3E5900F1753A /* Contact.cpp in Sources */, diff --git a/Testbed/Framework/Main.cpp b/Testbed/Framework/Main.cpp index b6b3b1826..3819dac49 100644 --- a/Testbed/Framework/Main.cpp +++ b/Testbed/Framework/Main.cpp @@ -269,6 +269,7 @@ static Test::Key GlfwKeyToTestKey(int key) { switch (key) { + case GLFW_KEY_SPACE: return Test::Key_Space; case GLFW_KEY_COMMA: return Test::Key_Comma; case GLFW_KEY_MINUS: return Test::Key_Minus; case GLFW_KEY_PERIOD: return Test::Key_Period; @@ -309,6 +310,7 @@ static Test::Key GlfwKeyToTestKey(int key) case GLFW_KEY_X: return Test::Key_X; case GLFW_KEY_Y: return Test::Key_Y; case GLFW_KEY_Z: return Test::Key_Z; + case GLFW_KEY_BACKSPACE: return Test::Key_Backspace; case GLFW_KEY_KP_SUBTRACT: return Test::Key_Subtract; case GLFW_KEY_KP_ADD: return Test::Key_Add; } diff --git a/Testbed/Framework/Test.cpp b/Testbed/Framework/Test.cpp index 9998b82db..536d5b4d6 100644 --- a/Testbed/Framework/Test.cpp +++ b/Testbed/Framework/Test.cpp @@ -387,6 +387,48 @@ Test::~Test() delete m_world; } +void Test::ResetWorld(const box2d::World &saved) +{ + SetSelectedFixture(nullptr); + + auto bombIndex = static_castGetBodies().size())>(-1); + auto groundIndex = static_castGetBodies().size())>(-1); + + { + auto i = decltype(m_world->GetBodies().size()){0}; + for (auto&& body: m_world->GetBodies()) + { + if (&body == m_bomb) + { + bombIndex = i; + } + if (&body == m_groundBody) + { + groundIndex = i; + } + ++i; + } + } + + *m_world = saved; + + { + auto i = decltype(m_world->GetBodies().size()){0}; + for (auto&& body: m_world->GetBodies()) + { + if (i == bombIndex) + { + m_bomb = &body; + } + if (i == groundIndex) + { + m_groundBody = &body; + } + ++i; + } + } +} + void Test::PreSolve(Contact& contact, const Manifold& oldManifold) { const auto& manifold = contact.GetManifold(); diff --git a/Testbed/Framework/Test.hpp b/Testbed/Framework/Test.hpp index d0974d0f9..3ebf24c4e 100644 --- a/Testbed/Framework/Test.hpp +++ b/Testbed/Framework/Test.hpp @@ -71,11 +71,11 @@ class Test : public ContactListener { public: enum Key { - Key_Comma, Key_Minus, Key_Period, Key_Equal, + Key_Space, Key_Comma, Key_Minus, Key_Period, Key_Equal, Key_0, Key_1, Key_2, Key_3, Key_4, Key_5, Key_6, Key_7, Key_8, Key_9, Key_A, Key_B, Key_C, Key_D, Key_E, Key_F, Key_G, Key_H, Key_I, Key_J, Key_K, Key_L, Key_M, Key_N, Key_O, Key_P, Key_Q, Key_R, Key_S, Key_T, Key_U, Key_V, Key_W, Key_X, Key_Y, Key_Z, - Key_Subtract, Key_Add, + Key_Backspace, Key_Subtract, Key_Add, Key_Unknown }; @@ -164,10 +164,14 @@ class Test : public ContactListener NOT_USED(drawer); } + void ResetWorld(const World& saved); + int GetStepCount() const noexcept { return m_stepCount; } PointCount GetPointCount() const noexcept { return m_pointCount; } const ContactPoint* GetPoints() const noexcept { return m_points; } const Body* GetBomb() const noexcept { return m_bomb; } + void SetBomb(Body* body) noexcept { m_bomb = body; } + void SetGroundBody(Body* body) noexcept { m_groundBody = body; } World* const m_world; TextLinePos m_textLine = TextLinePos{30}; diff --git a/Testbed/Tests/DistanceTest.hpp b/Testbed/Tests/DistanceTest.hpp index 7479af772..15f04b9f2 100644 --- a/Testbed/Tests/DistanceTest.hpp +++ b/Testbed/Tests/DistanceTest.hpp @@ -104,8 +104,8 @@ class DistanceTest : public Test void PostStep(const Settings&, Drawer& drawer) override { - const auto shapeA = static_cast(GetFixture(m_bodyA)->GetShape()); - const auto shapeB = static_cast(GetFixture(m_bodyB)->GetShape()); + const auto shapeA = static_cast(GetFixture(m_bodyA)->GetShape().get()); + const auto shapeB = static_cast(GetFixture(m_bodyB)->GetShape().get()); const auto proxyA = shapeA->GetChild(0); const auto proxyB = shapeB->GetChild(0); @@ -400,7 +400,7 @@ class DistanceTest : public Test if (body && fixture) { const auto shape = fixture->GetShape(); - auto polygon = *static_cast(shape); + auto polygon = *static_cast(shape.get()); polygon.SetVertexRadius(shape->GetVertexRadius() + RadiusIncrement); SetSelectedFixture(body->CreateFixture(std::make_shared(polygon))); body->DestroyFixture(fixture); @@ -415,7 +415,7 @@ class DistanceTest : public Test const auto newVertexRadius = lastLegitVertexRadius - RadiusIncrement; if (newVertexRadius >= Length{0}) { - PolygonShape polygon{*static_cast(shape)}; + PolygonShape polygon{*static_cast(shape.get())}; polygon.SetVertexRadius(newVertexRadius); auto newFixture = body->CreateFixture(std::make_shared(polygon)); if (newFixture) diff --git a/Testbed/Tests/Pulleys.hpp b/Testbed/Tests/Pulleys.hpp index 5615285ea..55f83e10b 100644 --- a/Testbed/Tests/Pulleys.hpp +++ b/Testbed/Tests/Pulleys.hpp @@ -62,12 +62,12 @@ class Pulleys : public Test const auto body2 = m_world->CreateBody(bd); body2->CreateFixture(shape); - PulleyJointDef pulleyDef; const auto anchor1 = Vec2(-10.0f, y + b) * Meter; const auto anchor2 = Vec2(10.0f, y + b) * Meter; const auto groundAnchor1 = Vec2(-10.0f, y + b + L) * Meter; const auto groundAnchor2 = Vec2(10.0f, y + b + L) * Meter; - pulleyDef.Initialize(body1, body2, groundAnchor1, groundAnchor2, anchor1, anchor2, 1.5f); + const auto pulleyDef = PulleyJointDef{body1, body2, + groundAnchor1, groundAnchor2, anchor1, anchor2, 1.5f}; m_joint1 = (PulleyJoint*)m_world->CreateJoint(pulleyDef); } diff --git a/Testbed/Tests/SensorTest.hpp b/Testbed/Tests/SensorTest.hpp index 7476ad862..ad1fb29f5 100644 --- a/Testbed/Tests/SensorTest.hpp +++ b/Testbed/Tests/SensorTest.hpp @@ -143,7 +143,7 @@ class SensorTest : public Test const auto body = m_bodies[i]; const auto ground = m_sensor->GetBody(); - const auto circle = static_cast(m_sensor->GetShape()); + const auto circle = static_cast(m_sensor->GetShape().get()); const auto center = GetWorldPoint(*ground, circle->GetLocation()); const auto position = body->GetLocation(); diff --git a/Testbed/Tests/Tiles.hpp b/Testbed/Tests/Tiles.hpp index 1904262fd..75d2c8da6 100644 --- a/Testbed/Tests/Tiles.hpp +++ b/Testbed/Tests/Tiles.hpp @@ -103,12 +103,14 @@ class Tiles : public Test { const auto height = m_world->GetTreeHeight(); const auto leafCount = m_world->GetProxyCount(); - assert(leafCount > 0); - const auto minimumNodeCount = 2 * leafCount - 1; - const auto minimumHeight = ceilf(logf(float(minimumNodeCount)) / logf(2.0f)); - drawer.DrawString(5, m_textLine, "dynamic tree height = %d, min = %d", - height, int(minimumHeight)); - m_textLine += DRAW_STRING_NEW_LINE; + if (leafCount > 0) + { + const auto minimumNodeCount = 2 * leafCount - 1; + const auto minimumHeight = ceilf(logf(float(minimumNodeCount)) / logf(2.0f)); + drawer.DrawString(5, m_textLine, "dynamic tree height = %d, min = %d", + height, int(minimumHeight)); + m_textLine += DRAW_STRING_NEW_LINE; + } drawer.DrawString(5, m_textLine, "create time = %6.2f ms, fixture count = %d", static_cast(m_createTime * RealNum(1000)), m_fixtureCount); @@ -122,8 +124,27 @@ class Tiles : public Test //} } + void KeyboardDown(Key key) override + { + switch (key) + { + case Key_C: + m_snapshot = *m_world; + break; + case Key_Backspace: + if (m_snapshot.GetBodies().size() > 0) + { + ResetWorld(m_snapshot); + } + break; + default: + break; + } + } + int m_fixtureCount; RealNum m_createTime; + World m_snapshot; }; } // namespace box2d diff --git a/Testbed/Tests/Tumbler.hpp b/Testbed/Tests/Tumbler.hpp index 95ef57a2d..8b3dd605b 100644 --- a/Testbed/Tests/Tumbler.hpp +++ b/Testbed/Tests/Tumbler.hpp @@ -101,7 +101,7 @@ class Tumbler : public Test { const auto selectedFixture = GetSelectedFixture(); const auto selectedShape = selectedFixture? - selectedFixture->GetShape(): static_cast(nullptr); + selectedFixture->GetShape().get(): static_cast(nullptr); switch (key) { diff --git a/UnitTests/Body.cpp b/UnitTests/Body.cpp index ad74de1fa..3bf4ab094 100644 --- a/UnitTests/Body.cpp +++ b/UnitTests/Body.cpp @@ -149,10 +149,10 @@ TEST(Body, CreateAndDestroyFixture) auto fixture = body->CreateFixture(shape, FixtureDef{}, false); const auto fshape = fixture->GetShape(); ASSERT_NE(fshape, nullptr); - EXPECT_EQ(typeid(*fshape), typeid(DiskShape)); + EXPECT_EQ(typeid(fshape.get()), typeid(const Shape*)); EXPECT_EQ(GetVertexRadius(*fshape), GetVertexRadius(*shape)); - EXPECT_EQ(static_cast(fshape)->GetLocation().x, shape->GetLocation().x); - EXPECT_EQ(static_cast(fshape)->GetLocation().y, shape->GetLocation().y); + EXPECT_EQ(static_cast(fshape.get())->GetLocation().x, shape->GetLocation().x); + EXPECT_EQ(static_cast(fshape.get())->GetLocation().y, shape->GetLocation().y); EXPECT_FALSE(body->GetFixtures().empty()); { int i = 0; diff --git a/UnitTests/Fixture.cpp b/UnitTests/Fixture.cpp index 1ad52212d..e2c1f4a95 100644 --- a/UnitTests/Fixture.cpp +++ b/UnitTests/Fixture.cpp @@ -57,7 +57,7 @@ TEST(Fixture, CreateMatchesDef) const auto fixture = body->CreateFixture(shapeA, def); EXPECT_EQ(fixture->GetBody(), body); - EXPECT_EQ(fixture->GetShape(), &(*shapeA)); + EXPECT_EQ(fixture->GetShape(), shapeA); EXPECT_EQ(fixture->GetDensity(), density); EXPECT_EQ(fixture->GetFriction(), friction); diff --git a/UnitTests/World.cpp b/UnitTests/World.cpp index de0a1728e..005d7d432 100644 --- a/UnitTests/World.cpp +++ b/UnitTests/World.cpp @@ -2,17 +2,19 @@ * Copyright (c) 2017 Louis Langholtz https://github.com/louis-langholtz/Box2D * * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages + * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. + * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: + * * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. + * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ @@ -23,13 +25,17 @@ #include #include #include -#include #include #include #include #include #include +#include +#include +#include +#include #include +#include using namespace box2d; @@ -83,6 +89,29 @@ TEST(World, Def) EXPECT_GT(max_inc, RealNum(0) * Meter * Second); } +TEST(World, Traits) +{ + EXPECT_TRUE(std::is_default_constructible::value); + EXPECT_FALSE(std::is_nothrow_default_constructible::value); + EXPECT_FALSE(std::is_trivially_default_constructible::value); + + EXPECT_TRUE(std::is_constructible::value); + EXPECT_FALSE(std::is_nothrow_constructible::value); + EXPECT_FALSE(std::is_trivially_constructible::value); + + EXPECT_TRUE(std::is_copy_constructible::value); + EXPECT_FALSE(std::is_nothrow_copy_constructible::value); + EXPECT_FALSE(std::is_trivially_copy_constructible::value); + + EXPECT_TRUE(std::is_copy_assignable::value); + EXPECT_FALSE(std::is_nothrow_copy_assignable::value); + EXPECT_FALSE(std::is_trivially_copy_assignable::value); + + EXPECT_TRUE(std::is_destructible::value); + EXPECT_TRUE(std::is_nothrow_destructible::value); + EXPECT_FALSE(std::is_trivially_destructible::value); +} + TEST(World, DefaultInit) { World world; @@ -132,6 +161,63 @@ TEST(World, Init) EXPECT_FALSE(world.IsLocked()); } +TEST(World, CopyConstruction) +{ + auto world = World{}; + + { + const auto copy = World{world}; + EXPECT_EQ(world.GetGravity(), copy.GetGravity()); + EXPECT_EQ(world.GetMinVertexRadius(), copy.GetMinVertexRadius()); + EXPECT_EQ(world.GetMaxVertexRadius(), copy.GetMaxVertexRadius()); + EXPECT_EQ(world.GetJoints().size(), copy.GetJoints().size()); + EXPECT_EQ(world.GetBodies().size(), copy.GetBodies().size()); + EXPECT_EQ(world.GetContacts().size(), copy.GetContacts().size()); + EXPECT_EQ(world.GetTreeHeight(), copy.GetTreeHeight()); + EXPECT_EQ(world.GetProxyCount(), copy.GetProxyCount()); + EXPECT_EQ(world.GetTreeBalance(), copy.GetTreeBalance()); + } + + const auto shape = std::make_shared(DiskShape::Conf{} + .UseDensity(RealNum(1) * KilogramPerSquareMeter) + .UseVertexRadius(RealNum(1) * Meter)); + const auto b1 = world.CreateBody(BodyDef{}.UseType(BodyType::Dynamic)); + b1->CreateFixture(shape); + const auto b2 = world.CreateBody(BodyDef{}.UseType(BodyType::Dynamic)); + b2->CreateFixture(shape); + + world.CreateJoint(RevoluteJointDef{b1, b2, Vec2_zero * Meter}); + world.CreateJoint(PrismaticJointDef{b1, b2, Vec2_zero * Meter, UnitVec2::GetRight()}); + world.CreateJoint(PulleyJointDef{b1, b2, Vec2_zero * Meter, Vec2_zero * Meter, + Vec2_zero * Meter, Vec2_zero * Meter, RealNum(1)}); + + auto stepConf = StepConf{}; + world.Step(stepConf); + + { + const auto copy = World{world}; + EXPECT_EQ(world.GetGravity(), copy.GetGravity()); + EXPECT_EQ(world.GetMinVertexRadius(), copy.GetMinVertexRadius()); + EXPECT_EQ(world.GetMaxVertexRadius(), copy.GetMaxVertexRadius()); + EXPECT_EQ(world.GetJoints().size(), copy.GetJoints().size()); + const auto minJoints = std::min(world.GetJoints().size(), copy.GetJoints().size()); + + auto worldJointIter = world.GetJoints().begin(); + auto copyJointIter = copy.GetJoints().begin(); + for (auto i = decltype(minJoints){0}; i < minJoints; ++i) + { + EXPECT_EQ((*worldJointIter)->GetType(), (*copyJointIter)->GetType()); + ++worldJointIter; + ++copyJointIter; + } + EXPECT_EQ(world.GetBodies().size(), copy.GetBodies().size()); + EXPECT_EQ(world.GetContacts().size(), copy.GetContacts().size()); + EXPECT_EQ(world.GetTreeHeight(), copy.GetTreeHeight()); + EXPECT_EQ(world.GetProxyCount(), copy.GetProxyCount()); + EXPECT_EQ(world.GetTreeBalance(), copy.GetTreeBalance()); + } +} + TEST(World, SetGravity) { const auto gravity = Vec2{RealNum(-4.2), RealNum(3.4)} * MeterPerSquareSecond;