From b3e771838da1ba7c42d6208540c3dfe3c9844c91 Mon Sep 17 00:00:00 2001 From: Daniel Chappuis Date: Sat, 3 Feb 2018 20:48:08 +0100 Subject: [PATCH] Replace std::set by Set and fix issues with List and Map --- CMakeLists.txt | 1 + src/collision/CollisionDetection.cpp | 32 +- src/collision/CollisionDetection.h | 26 +- src/collision/shapes/ConvexMeshShape.h | 1 - src/containers/List.h | 37 +- src/containers/Map.h | 37 +- src/containers/Pair.h | 264 +++++++++ src/containers/Set.h | 712 +++++++++++++++++++++++++ src/engine/CollisionWorld.cpp | 19 +- src/engine/CollisionWorld.h | 26 +- src/engine/DynamicsWorld.cpp | 41 +- src/engine/DynamicsWorld.h | 28 +- src/engine/OverlappingPair.cpp | 2 +- test/main.cpp | 2 + test/tests/containers/TestList.h | 48 +- test/tests/containers/TestMap.h | 16 +- test/tests/containers/TestSet.h | 417 +++++++++++++++ 17 files changed, 1581 insertions(+), 128 deletions(-) create mode 100644 src/containers/Pair.h create mode 100644 src/containers/Set.h create mode 100644 test/tests/containers/TestSet.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 169476c9..636c6295 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -199,6 +199,7 @@ SET (REACTPHYSICS3D_SOURCES "src/containers/LinkedList.h" "src/containers/List.h" "src/containers/Map.h" + "src/containers/Set.h" ) # Create the library diff --git a/src/collision/CollisionDetection.cpp b/src/collision/CollisionDetection.cpp index 340bd823..c337e10a 100644 --- a/src/collision/CollisionDetection.cpp +++ b/src/collision/CollisionDetection.cpp @@ -37,20 +37,19 @@ #include "collision/OverlapCallback.h" #include #include -#include #include #include -#include // We want to use the ReactPhysics3D namespace using namespace reactphysics3d; using namespace std; + // Constructor CollisionDetection::CollisionDetection(CollisionWorld* world, MemoryManager& memoryManager) : mMemoryManager(memoryManager), mWorld(world), mNarrowPhaseInfoList(nullptr), mOverlappingPairs(mMemoryManager.getPoolAllocator()), mBroadPhaseAlgorithm(*this), - mIsCollisionShapesAdded(false) { + mNoCollisionPairs(mMemoryManager.getPoolAllocator()), mIsCollisionShapesAdded(false) { // Set the default collision dispatch configuration setCollisionDispatch(&mDefaultCollisionDispatch); @@ -127,14 +126,11 @@ void CollisionDetection::computeMiddlePhase() { // overlapping pair if (!mBroadPhaseAlgorithm.testOverlappingShapes(shape1, shape2)) { - Map::Iterator itToRemove = it; - ++it; - // Destroy the overlapping pair - itToRemove->second->~OverlappingPair(); + it->second->~OverlappingPair(); - mWorld->mMemoryManager.release(MemoryManager::AllocationType::Pool, itToRemove->second, sizeof(OverlappingPair)); - mOverlappingPairs.remove(itToRemove); + mWorld->mMemoryManager.release(MemoryManager::AllocationType::Pool, it->second, sizeof(OverlappingPair)); + it = mOverlappingPairs.remove(it); continue; } else { @@ -155,7 +151,7 @@ void CollisionDetection::computeMiddlePhase() { // Check if the bodies are in the set of bodies that cannot collide between each other bodyindexpair bodiesIndex = OverlappingPair::computeBodiesIndexPair(body1, body2); - if (mNoCollisionPairs.count(bodiesIndex) > 0) continue; + if (mNoCollisionPairs.contains(bodiesIndex) > 0) continue; bool isShape1Convex = shape1->getCollisionShape()->isConvex(); bool isShape2Convex = shape2->getCollisionShape()->isConvex(); @@ -349,15 +345,13 @@ void CollisionDetection::removeProxyCollisionShape(ProxyShape* proxyShape) { for (it = mOverlappingPairs.begin(); it != mOverlappingPairs.end(); ) { if (it->second->getShape1()->mBroadPhaseID == proxyShape->mBroadPhaseID|| it->second->getShape2()->mBroadPhaseID == proxyShape->mBroadPhaseID) { - Map::Iterator itToRemove = it; - ++it; // TODO : Remove all the contact manifold of the overlapping pair from the contact manifolds list of the two bodies involved // Destroy the overlapping pair - itToRemove->second->~OverlappingPair(); - mWorld->mMemoryManager.release(MemoryManager::AllocationType::Pool, itToRemove->second, sizeof(OverlappingPair)); - mOverlappingPairs.remove(itToRemove); + it->second->~OverlappingPair(); + mWorld->mMemoryManager.release(MemoryManager::AllocationType::Pool, it->second, sizeof(OverlappingPair)); + it = mOverlappingPairs.remove(it); } else { ++it; @@ -521,7 +515,7 @@ void CollisionDetection::testAABBOverlap(const AABB& aabb, OverlapCallback* over unsigned short categoryMaskBits) { assert(overlapCallback != nullptr); - std::unordered_set reportedBodies; + Set reportedBodies(mMemoryManager.getPoolAllocator()); // Ask the broad-phase to get all the overlapping shapes LinkedList overlappingNodes(mMemoryManager.getPoolAllocator()); @@ -544,7 +538,7 @@ void CollisionDetection::testAABBOverlap(const AABB& aabb, OverlapCallback* over if ((proxyShape->getCollisionCategoryBits() & categoryMaskBits) != 0) { // Add the body into the set of reported bodies - reportedBodies.insert(overlapBody->getID()); + reportedBodies.add(overlapBody->getID()); // Notify the overlap to the user overlapCallback->notifyOverlap(overlapBody); @@ -637,7 +631,7 @@ void CollisionDetection::testOverlap(CollisionBody* body, OverlapCallback* overl assert(overlapCallback != nullptr); - std::unordered_set reportedBodies; + Set reportedBodies(mMemoryManager.getPoolAllocator()); // For each proxy shape proxy shape of the body ProxyShape* bodyProxyShape = body->getProxyShapesList(); @@ -717,7 +711,7 @@ void CollisionDetection::testOverlap(CollisionBody* body, OverlapCallback* overl CollisionBody* overlapBody = proxyShape->getBody(); // Add the body into the set of reported bodies - reportedBodies.insert(overlapBody->getID()); + reportedBodies.add(overlapBody->getID()); // Notify the overlap to the user overlapCallback->notifyOverlap(overlapBody); diff --git a/src/collision/CollisionDetection.h b/src/collision/CollisionDetection.h index fa3e05c6..36bede7e 100644 --- a/src/collision/CollisionDetection.h +++ b/src/collision/CollisionDetection.h @@ -35,9 +35,26 @@ #include "memory/MemoryManager.h" #include "constraint/ContactPoint.h" #include "containers/Map.h" -#include +#include "containers/Set.h" #include +// Hash function for struct VerticesPair +// TOOD : REMOVE THIS +namespace std { + + template <> struct hash { + + size_t operator()(const reactphysics3d::bodyindexpair& pair) const { + + std::size_t seed = 0; + reactphysics3d::hash_combine(seed, pair.first); + reactphysics3d::hash_combine(seed, pair.second); + + return seed; + } + }; +} + /// ReactPhysics3D namespace namespace reactphysics3d { @@ -88,9 +105,8 @@ class CollisionDetection { // TODO : Delete this GJKAlgorithm mNarrowPhaseGJKAlgorithm; - // TODO : Maybe delete this set (what is the purpose ?) /// Set of pair of bodies that cannot collide between each other - std::set mNoCollisionPairs; + Set mNoCollisionPairs; /// True if some collision shapes have been added previously bool mIsCollisionShapesAdded; @@ -263,13 +279,13 @@ inline void CollisionDetection::addProxyCollisionShape(ProxyShape* proxyShape, // Add a pair of bodies that cannot collide with each other inline void CollisionDetection::addNoCollisionPair(CollisionBody* body1, CollisionBody* body2) { - mNoCollisionPairs.insert(OverlappingPair::computeBodiesIndexPair(body1, body2)); + mNoCollisionPairs.add(OverlappingPair::computeBodiesIndexPair(body1, body2)); } // Remove a pair of bodies that cannot collide with each other inline void CollisionDetection::removeNoCollisionPair(CollisionBody* body1, CollisionBody* body2) { - mNoCollisionPairs.erase(OverlappingPair::computeBodiesIndexPair(body1, body2)); + mNoCollisionPairs.remove(OverlappingPair::computeBodiesIndexPair(body1, body2)); } // Ask for a collision shape to be tested again during broad-phase. diff --git a/src/collision/shapes/ConvexMeshShape.h b/src/collision/shapes/ConvexMeshShape.h index b84c16dd..a24aa85b 100644 --- a/src/collision/shapes/ConvexMeshShape.h +++ b/src/collision/shapes/ConvexMeshShape.h @@ -33,7 +33,6 @@ #include "collision/TriangleMesh.h" #include "collision/PolyhedronMesh.h" #include "collision/narrowphase/GJK/GJKAlgorithm.h" -#include /// ReactPhysics3D namespace namespace reactphysics3d { diff --git a/src/containers/List.h b/src/containers/List.h index 93ec0830..49bd2d21 100644 --- a/src/containers/List.h +++ b/src/containers/List.h @@ -154,6 +154,9 @@ class List { return !(*this == iterator); } + /// Frienship + friend class List; + }; // -------------------- Methods -------------------- // @@ -227,8 +230,35 @@ class List { mSize++; } - /// Remove an element from the list at a given index - void remove(uint index) { + /// Try to find a given item of the list and return an iterator + /// pointing to that element if it exists in the list. Otherwise, + /// this method returns the end() iterator + Iterator find(const T& element) { + + for (uint i=0; i(mBuffer)[i]) { + return Iterator(mBuffer, i, mSize); + } + } + + return end(); + } + + /// Look for an element in the list and remove it + Iterator remove(const T& element) { + return remove(find(element)); + } + + /// Remove an element from the list and return a iterator + /// pointing to the element after the removed one (or end() if none) + Iterator remove(const Iterator& it) { + assert(it.mBuffer == mBuffer); + return removeAt(it.mCurrentIndex); + } + + /// Remove an element from the list at a given index and return an + /// iterator pointing to the element after the removed one (or end() if none) + Iterator removeAt(uint index) { assert(index >= 0 && index < mSize); @@ -244,6 +274,9 @@ class List { char* src = dest + sizeof(T); std::memcpy(static_cast(dest), static_cast(src), (mSize - index) * sizeof(T)); } + + // Return an iterator pointing to the element after the removed one + return Iterator(mBuffer, index, mSize); } /// Append another list at the end of the current one diff --git a/src/containers/Map.h b/src/containers/Map.h index da33448a..89594370 100644 --- a/src/containers/Map.h +++ b/src/containers/Map.h @@ -39,7 +39,8 @@ namespace reactphysics3d { // Class Map /** - * This class represents a simple generic associative map + * This class represents a simple generic associative map. This map is + * implemented with a hash table. */ template class Map { @@ -60,9 +61,9 @@ class Map { } /// Constructor - Entry(size_t hashcode, int nextValue) { + Entry(size_t hashcode, int nextEntry) { hashCode = hashcode; - next = nextValue; + next = nextEntry; keyValue = nullptr; } @@ -230,7 +231,7 @@ class Map { // If elements have been allocated if (mCapacity > 0) { - // Clear the list + // Clear the map clear(); // Destroy the entries @@ -320,7 +321,7 @@ class Map { reference operator*() const { assert(mCurrentEntry >= 0 && mCurrentEntry < mNbUsedEntries); assert(mEntries[mCurrentEntry].keyValue != nullptr); - return mEntries[mCurrentEntry].keyValue; + return *(mEntries[mCurrentEntry].keyValue); } /// Deferencable @@ -497,14 +498,18 @@ class Map { } /// Remove the element pointed by some iterator - bool remove(const Iterator& it) { + /// This method returns an iterator pointing to the element after + /// the one that has been removed + Iterator remove(const Iterator& it) { const K& key = it->first; return remove(key); } /// Remove the element from the map with a given key - bool remove(const K& key) { + /// This method returns an iterator pointing to the element after + /// the one that has been removed + Iterator remove(const K& key) { if (mCapacity > 0) { @@ -533,15 +538,27 @@ class Map { mFreeIndex = i; mNbFreeEntries++; - return true; + // Find the next entry to return the iterator + for (i += 1; i < mNbUsedEntries; i++) { + + // If the entry is not empty + if (mEntries[i].keyValue != nullptr) { + + // We have found the next non empty entry + return Iterator(mEntries, mCapacity, mNbUsedEntries, i); + } + } + + return end(); } } } - return false; + // Return the end iterator + return end(); } - /// Clear the list + /// Clear the map void clear() { if (mNbUsedEntries > 0) { diff --git a/src/containers/Pair.h b/src/containers/Pair.h new file mode 100644 index 00000000..e6a9ff8f --- /dev/null +++ b/src/containers/Pair.h @@ -0,0 +1,264 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2016 Daniel Chappuis * +********************************************************************************* +* * +* 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. * +* * +********************************************************************************/ + +#ifndef REACTPHYSICS3D_PAIR_H +#define REACTPHYSICS3D_PAIR_H + +// Libraries +#include "configuration.h" +#include "memory/MemoryAllocator.h" +#include +#include + +namespace reactphysics3d { + +// Class Pair +/** + * This class represents a simple generic pair + */ +template +class Pair { + + public: + + // -------------------- Attributes -------------------- // + + /// First element of the pair + T1 first; + + /// Second element of the pair + T2 second; + + // -------------------- Methods -------------------- // + + /// Constructor + List(MemoryAllocator& allocator, size_t capacity = 0) + : mBuffer(nullptr), mSize(0), mCapacity(0), mAllocator(allocator) { + + if (capacity > 0) { + + // Allocate memory + reserve(capacity); + } + } + + /// Copy constructor + List(const List& list) : mBuffer(nullptr), mSize(0), mCapacity(0), mAllocator(list.mAllocator) { + + // All all the elements of the list to the current one + addRange(list); + } + + /// Destructor + ~List() { + + // If elements have been allocated + if (mCapacity > 0) { + + // Clear the list + clear(); + + // Release the memory allocated on the heap + mAllocator.release(mBuffer, mCapacity * sizeof(T)); + } + } + + /// Allocate memory for a given number of elements + void reserve(size_t capacity) { + + if (capacity <= mCapacity) return; + + // Allocate memory for the new array + void* newMemory = mAllocator.allocate(capacity * sizeof(T)); + + if (mBuffer != nullptr) { + + // Copy the elements to the new allocated memory location + std::memcpy(newMemory, mBuffer, mSize * sizeof(T)); + + // Release the previously allocated memory + mAllocator.release(mBuffer, mCapacity * sizeof(T)); + } + + mBuffer = newMemory; + assert(mBuffer != nullptr); + + mCapacity = capacity; + } + + /// Add an element into the list + void add(const T& element) { + + // If we need to allocate more memory + if (mSize == mCapacity) { + reserve(mCapacity == 0 ? 1 : mCapacity * 2); + } + + // Use the copy-constructor to construct the element + new (static_cast(mBuffer) + mSize * sizeof(T)) T(element); + + mSize++; + } + + /// Try to find a given item of the list and return an iterator + /// pointing to that element if it exists in the list. Otherwise, + /// this method returns the end() iterator + Iterator find(const T& element) { + + for (uint i=0; i(mBuffer)[i])) { + return Iterator(mBuffer, i, mSize); + } + } + + return end(); + } + + /// Remove an element from the list + void remove(const Iterator& it) { + assert(it.mBuffer == mBuffer); + remove(it.mCurrentIndex); + } + + /// Remove an element from the list at a given index + void remove(uint index) { + + assert(index >= 0 && index < mSize); + + // Call the destructor + (static_cast(mBuffer)[index]).~T(); + + mSize--; + + if (index != mSize) { + + // Move the elements to fill in the empty slot + char* dest = static_cast(mBuffer) + index * sizeof(T); + char* src = dest + sizeof(T); + std::memcpy(static_cast(dest), static_cast(src), (mSize - index) * sizeof(T)); + } + } + + /// Append another list at the end of the current one + void addRange(const List& list) { + + // If we need to allocate more memory + if (mSize + list.size() > mCapacity) { + + // Allocate memory + reserve(mSize + list.size()); + } + + // Add the elements of the list to the current one + for(uint i=0; i(mBuffer) + mSize * sizeof(T)) T(list[i]); + mSize++; + } + } + + /// Clear the list + void clear() { + + // Call the destructor of each element + for (uint i=0; i < mSize; i++) { + (static_cast(mBuffer)[i]).~T(); + } + + mSize = 0; + } + + /// Return the number of elements in the list + size_t size() const { + return mSize; + } + + /// Return the capacity of the list + size_t capacity() const { + return mCapacity; + } + + /// Overloaded index operator + T& operator[](const uint index) { + assert(index >= 0 && index < mSize); + return (static_cast(mBuffer)[index]); + } + + /// Overloaded const index operator + const T& operator[](const uint index) const { + assert(index >= 0 && index < mSize); + return (static_cast(mBuffer)[index]); + } + + /// Overloaded equality operator + bool operator==(const List& list) const { + + if (mSize != list.mSize) return false; + + T* items = static_cast(mBuffer); + for (int i=0; i < mSize; i++) { + if (items[i] != list[i]) { + return false; + } + } + + return true; + } + + /// Overloaded not equal operator + bool operator!=(const List& list) const { + + return !((*this) == list); + } + + /// Overloaded assignment operator + List& operator=(const List& list) { + + if (this != &list) { + + // Clear all the elements + clear(); + + // Add all the elements of the list to the current one + addRange(list); + } + + return *this; + } + + /// Return a begin iterator + Iterator begin() const { + return Iterator(mBuffer, 0, mSize); + } + + /// Return a end iterator + Iterator end() const { + return Iterator(mBuffer, mSize, mSize); + } +}; + +} + +#endif diff --git a/src/containers/Set.h b/src/containers/Set.h new file mode 100644 index 00000000..646507a1 --- /dev/null +++ b/src/containers/Set.h @@ -0,0 +1,712 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2016 Daniel Chappuis * +********************************************************************************* +* * +* 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. * +* * +********************************************************************************/ + +#ifndef REACTPHYSICS3D_SET_H +#define REACTPHYSICS3D_SET_H + +// Libraries +#include "memory/MemoryAllocator.h" +#include "mathematics/mathematics_functions.h" +#include +#include +#include +#include + + +namespace reactphysics3d { + +// Class Set +/** + * This class represents a simple generic set. This set is implemented + * with a hash table. + */ +template +class Set { + + private: + + /// An entry of the set + struct Entry { + + size_t hashCode; // Hash code of the entry + int next; // Index of the next entry + V* value; // Pointer to the value stored in the entry + + /// Constructor + Entry() { + next = -1; + value = nullptr; + } + + /// Constructor + Entry(size_t hashcode, int nextEntry) { + hashCode = hashcode; + next = nextEntry; + value = nullptr; + } + + /// Destructor + ~Entry() { + + assert(value == nullptr); + } + + }; + + // -------------------- Constants -------------------- // + + /// Number of prime numbers in array + static constexpr int NB_PRIMES = 70; + + /// Array of prime numbers for the size of the set + static const int PRIMES[NB_PRIMES]; + + /// Largest prime number + static int LARGEST_PRIME; + + // -------------------- Attributes -------------------- // + + /// Current number of used entries in the set + int mNbUsedEntries; + + /// Number of free entries among the used ones + int mNbFreeEntries; + + /// Current capacity of the set + int mCapacity; + + /// Array with all the buckets + int* mBuckets; + + /// Array with all the entries + Entry* mEntries; + + /// Memory allocator + MemoryAllocator& mAllocator; + + /// Index to the fist free entry + int mFreeIndex; + + // -------------------- Methods -------------------- // + + /// Initialize the set + void initialize(int capacity) { + + // Compute the next larger prime size + mCapacity = getPrimeSize(capacity); + + // Allocate memory for the buckets + mBuckets = static_cast(mAllocator.allocate(mCapacity * sizeof(int))); + + // Allocate memory for the entries + mEntries = static_cast(mAllocator.allocate(mCapacity * sizeof(Entry))); + + // Initialize the buckets and entries + for (int i=0; i mCapacity); + assert(isPrimeNumber(newCapacity)); + + // Allocate memory for the buckets + int* newBuckets = static_cast(mAllocator.allocate(newCapacity * sizeof(int))); + + // Allocate memory for the entries + Entry* newEntries = static_cast(mAllocator.allocate(newCapacity * sizeof(Entry))); + + // Initialize the new buckets + for (int i=0; i(&newEntries[i])) Entry(); + } + + // For each used entry + for (int i=0; i 0) { + + size_t hashCode = std::hash()(value); + int bucket = hashCode % mCapacity; + + for (int i = mBuckets[bucket]; i >= 0; i = mEntries[i].next) { + if (mEntries[i].hashCode == hashCode && (*mEntries[i].value) == value) { + return i; + } + } + } + + return -1; + } + + /// Return the prime number that is larger or equal to the number in parameter + /// for the size of the set + static int getPrimeSize(int number) { + + // Check if the next larger prime number is in the precomputed array of primes + for (int i = 0; i < NB_PRIMES; i++) { + if (PRIMES[i] >= number) return PRIMES[i]; + } + + // Manually compute the next larger prime number + for (int i = (number | 1); i < std::numeric_limits::max(); i+=2) { + + if (isPrimeNumber(i)) { + return i; + } + } + + return number; + } + + /// Clear and reset the set + void reset() { + + // If elements have been allocated + if (mCapacity > 0) { + + // Clear the list + clear(); + + // Destroy the entries + for (int i=0; i < mCapacity; i++) { + mEntries[i].~Entry(); + } + + mAllocator.release(mBuckets, mCapacity * sizeof(int)); + mAllocator.release(mEntries, mCapacity * sizeof(Entry)); + + mNbUsedEntries = 0; + mNbFreeEntries = 0; + mCapacity = 0; + mBuckets = nullptr; + mEntries = nullptr; + mFreeIndex = -1; + } + } + + public: + + /// Class Iterator + /** + * This class represents an iterator for the Set + */ + class Iterator { + + private: + + /// Array of entries + const Entry* mEntries; + + /// Capacity of the map + int mCapacity; + + /// Number of used entries in the map + int mNbUsedEntries; + + /// Index of the current entry + int mCurrentEntry; + + /// Advance the iterator + void advance() { + + // If we are trying to move past the end + assert(mCurrentEntry < mNbUsedEntries); + + for (mCurrentEntry += 1; mCurrentEntry < mNbUsedEntries; mCurrentEntry++) { + + // If the entry is not empty + if (mEntries[mCurrentEntry].value != nullptr) { + + // We have found the next non empty entry + return; + } + } + + // We have not find a non empty entry, we return an iterator to the end + mCurrentEntry = mCapacity; + } + + public: + + // Iterator traits + using value_type = V; + using difference_type = std::ptrdiff_t; + using pointer = V*; + using reference = V&; + using iterator_category = std::forward_iterator_tag; + + /// Constructor + Iterator() = default; + + /// Constructor + Iterator(const Entry* entries, int capacity, int nbUsedEntries, int currentEntry) + :mEntries(entries), mCapacity(capacity), mNbUsedEntries(nbUsedEntries), mCurrentEntry(currentEntry) { + + } + + /// Copy constructor + Iterator(const Iterator& it) + :mEntries(it.mEntries), mCapacity(it.mCapacity), mNbUsedEntries(it.mNbUsedEntries), mCurrentEntry(it.mCurrentEntry) { + + } + + /// Deferencable + reference operator*() const { + assert(mCurrentEntry >= 0 && mCurrentEntry < mNbUsedEntries); + assert(mEntries[mCurrentEntry].value != nullptr); + return *(mEntries[mCurrentEntry].value); + } + + /// Deferencable + pointer operator->() const { + assert(mCurrentEntry >= 0 && mCurrentEntry < mNbUsedEntries); + assert(mEntries[mCurrentEntry].value != nullptr); + return mEntries[mCurrentEntry].value; + } + + /// Post increment (it++) + Iterator& operator++() { + advance(); + return *this; + } + + /// Pre increment (++it) + Iterator operator++(int number) { + Iterator tmp = *this; + advance(); + return tmp; + } + + /// Equality operator (it == end()) + bool operator==(const Iterator& iterator) const { + return mCurrentEntry == iterator.mCurrentEntry && mEntries == iterator.mEntries; + } + + /// Inequality operator (it != end()) + bool operator!=(const Iterator& iterator) const { + return !(*this == iterator); + } + }; + + + // -------------------- Methods -------------------- // + + /// Constructor + Set(MemoryAllocator& allocator, size_t capacity = 0) + : mNbUsedEntries(0), mNbFreeEntries(0), mCapacity(0), mBuckets(nullptr), + mEntries(nullptr), mAllocator(allocator), mFreeIndex(-1) { + + // If the largest prime has not been computed yet + if (LARGEST_PRIME == -1) { + + // Compute the largest prime number (largest map capacity) + LARGEST_PRIME = getPrimeSize(PRIMES[NB_PRIMES - 1] + 2); + } + + if (capacity > 0) { + + initialize(capacity); + } + } + + /// Copy constructor + Set(const Set& set) + :mNbUsedEntries(set.mNbUsedEntries), mNbFreeEntries(set.mNbFreeEntries), mCapacity(set.mCapacity), + mAllocator(set.mAllocator), mFreeIndex(set.mFreeIndex) { + + // Allocate memory for the buckets + mBuckets = static_cast(mAllocator.allocate(mCapacity * sizeof(int))); + + // Allocate memory for the entries + mEntries = static_cast(mAllocator.allocate(mCapacity * sizeof(Entry))); + + // Copy the buckets + std::memcpy(mBuckets, set.mBuckets, mCapacity * sizeof(int)); + + // Copy the entries + for (int i=0; i < mCapacity; i++) { + + new (&mEntries[i]) Entry(set.mEntries[i].hashCode, set.mEntries[i].next); + + if (set.mEntries[i].value != nullptr) { + mEntries[i].value = static_cast(mAllocator.allocate(sizeof(V))); + new (mEntries[i].value) V(*(set.mEntries[i].value)); + } + } + } + + /// Destructor + ~Set() { + + reset(); + } + + /// Allocate memory for a given number of elements + void reserve(size_t capacity) { + + if (capacity <= mCapacity) return; + + if (capacity > LARGEST_PRIME && LARGEST_PRIME > mCapacity) { + capacity = LARGEST_PRIME; + } + else { + capacity = getPrimeSize(capacity); + } + + expand(capacity); + } + + /// Return true if the set contains a given value + bool contains(const V& value) const { + return findEntry(value) != -1; + } + + /// Add a value into the set + void add(const V& value) { + + if (mCapacity == 0) { + initialize(0); + } + + // Compute the hash code of the value + size_t hashCode = std::hash()(value); + + // Compute the corresponding bucket index + int bucket = hashCode % mCapacity; + + // Check if the item is already in the set + for (int i = mBuckets[bucket]; i >= 0; i = mEntries[i].next) { + + // If there is already an item with the same value in the set + if (mEntries[i].hashCode == hashCode && (*mEntries[i].value) == value) { + + return; + } + } + + size_t entryIndex; + + // If there are free entries to use + if (mNbFreeEntries > 0) { + assert(mFreeIndex >= 0); + entryIndex = mFreeIndex; + mFreeIndex = mEntries[entryIndex].next; + mNbFreeEntries--; + } + else { + + // If we need to allocator more entries + if (mNbUsedEntries == mCapacity) { + + // Allocate more memory + reserve(mCapacity * 2); + + // Recompute the bucket index + bucket = hashCode % mCapacity; + } + + entryIndex = mNbUsedEntries; + mNbUsedEntries++; + } + + assert(mEntries[entryIndex].value == nullptr); + mEntries[entryIndex].hashCode = hashCode; + mEntries[entryIndex].next = mBuckets[bucket]; + mEntries[entryIndex].value = static_cast(mAllocator.allocate(sizeof(V))); + assert(mEntries[entryIndex].value != nullptr); + new (mEntries[entryIndex].value) V(value); + mBuckets[bucket] = entryIndex; + } + + /// Remove the element pointed by some iterator + /// This method returns an iterator pointing to the + /// element after the one that has been removed + Iterator remove(const Iterator& it) { + + return remove(*it); + } + + /// Remove the element from the set with a given value + /// This method returns an iterator pointing to the + /// element after the one that has been removed + Iterator remove(const V& value) { + + if (mCapacity > 0) { + + size_t hashcode = std::hash()(value); + int bucket = hashcode % mCapacity; + int last = -1; + for (int i = mBuckets[bucket]; i >= 0; last = i, i = mEntries[i].next) { + + if (mEntries[i].hashCode == hashcode && (*mEntries[i].value) == value) { + + if (last < 0 ) { + mBuckets[bucket] = mEntries[i].next; + } + else { + mEntries[last].next = mEntries[i].next; + } + + // Release memory for the value if any + if (mEntries[i].value != nullptr) { + mEntries[i].value->~V(); + mAllocator.release(mEntries[i].value, sizeof(V)); + mEntries[i].value = nullptr; + } + mEntries[i].hashCode = -1; + mEntries[i].next = mFreeIndex; + mFreeIndex = i; + mNbFreeEntries++; + + // Find the next valid entry to return an iterator + for (i += 1; i < mNbUsedEntries; i++) { + + // If the entry is not empty + if (mEntries[i].value != nullptr) { + + // We have found the next non empty entry + return Iterator(mEntries, mCapacity, mNbUsedEntries, i); + } + } + + return end(); + } + } + } + + return end(); + } + + /// Clear the set + void clear() { + + if (mNbUsedEntries > 0) { + + for (int i=0; i < mCapacity; i++) { + mBuckets[i] = -1; + mEntries[i].next = -1; + if (mEntries[i].value != nullptr) { + mEntries[i].value->~V(); + mAllocator.release(mEntries[i].value, sizeof(V)); + mEntries[i].value = nullptr; + } + } + + mFreeIndex = -1; + mNbUsedEntries = 0; + mNbFreeEntries = 0; + } + + assert(size() == 0); + } + + /// Return the number of elements in the set + int size() const { + return mNbUsedEntries - mNbFreeEntries; + } + + /// Return the capacity of the set + int capacity() const { + return mCapacity; + } + + /// Try to find an item of the set given a key. + /// The method returns an iterator to the found item or + /// an iterator pointing to the end if not found + Iterator find(const V& value) const { + + int bucket; + int entry = -1; + + if (mCapacity > 0) { + + size_t hashCode = std::hash()(value); + bucket = hashCode % mCapacity; + + for (int i = mBuckets[bucket]; i >= 0; i = mEntries[i].next) { + if (mEntries[i].hashCode == hashCode && *(mEntries[i].value) == value) { + entry = i; + break; + } + } + } + + if (entry == -1) { + return end(); + } + + assert(mEntries[entry].value != nullptr); + + return Iterator(mEntries, mCapacity, mNbUsedEntries, entry); + } + + /// Overloaded equality operator + bool operator==(const Set& set) const { + + if (size() != set.size()) return false; + + for (auto it = begin(); it != end(); ++it) { + if(!set.contains(*it)) { + return false; + } + } + + return true; + } + + /// Overloaded not equal operator + bool operator!=(const Set& set) const { + + return !((*this) == set); + } + + /// Overloaded assignment operator + Set& operator=(const Set& set) { + + // Check for self assignment + if (this != &set) { + + // Reset the set + reset(); + + if (set.mCapacity > 0) { + + // Compute the next larger prime size + mCapacity = getPrimeSize(set.mCapacity); + + // Allocate memory for the buckets + mBuckets = static_cast(mAllocator.allocate(mCapacity * sizeof(int))); + + // Allocate memory for the entries + mEntries = static_cast(mAllocator.allocate(mCapacity * sizeof(Entry))); + + // Copy the buckets + std::memcpy(mBuckets, set.mBuckets, mCapacity * sizeof(int)); + + // Copy the entries + for (int i=0; i < mCapacity; i++) { + + new (&mEntries[i]) Entry(set.mEntries[i].hashCode, set.mEntries[i].next); + + if (set.mEntries[i].value != nullptr) { + mEntries[i].value = static_cast(mAllocator.allocate(sizeof(V))); + new (mEntries[i].value) V(*(set.mEntries[i].value)); + } + } + + mNbUsedEntries = set.mNbUsedEntries; + mNbFreeEntries = set.mNbFreeEntries; + mFreeIndex = set.mFreeIndex; + } + } + + return *this; + } + + /// Return a begin iterator + Iterator begin() const { + + // If the map is empty + if (size() == 0) { + + // Return an iterator to the end + return end(); + } + + // Find the first used entry + int entry; + for (entry=0; entry < mNbUsedEntries; entry++) { + if (mEntries[entry].value != nullptr) { + return Iterator(mEntries, mCapacity, mNbUsedEntries, entry); + } + } + + assert(false); + } + + /// Return a end iterator + Iterator end() const { + return Iterator(mEntries, mCapacity, mNbUsedEntries, mCapacity); + } +}; + +template +const int Set::PRIMES[NB_PRIMES] = {3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919, + 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, + 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437, + 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, + 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559}; + +template +int Set::LARGEST_PRIME = -1; + +} + +#endif diff --git a/src/engine/CollisionWorld.cpp b/src/engine/CollisionWorld.cpp index 1a462c60..9388acda 100644 --- a/src/engine/CollisionWorld.cpp +++ b/src/engine/CollisionWorld.cpp @@ -33,7 +33,7 @@ using namespace std; // Constructor CollisionWorld::CollisionWorld() - : mCollisionDetection(this, mMemoryManager), mCurrentBodyID(0), + : mCollisionDetection(this, mMemoryManager), mBodies(mMemoryManager.getPoolAllocator()), mCurrentBodyID(0), mFreeBodiesIDs(mMemoryManager.getPoolAllocator()), mEventListener(nullptr) { #ifdef IS_PROFILING_ACTIVE @@ -49,14 +49,11 @@ CollisionWorld::CollisionWorld() CollisionWorld::~CollisionWorld() { // Destroy all the collision bodies that have not been removed - std::set::iterator itBodies; - for (itBodies = mBodies.begin(); itBodies != mBodies.end(); ) { - std::set::iterator itToRemove = itBodies; - ++itBodies; - destroyCollisionBody(*itToRemove); + for (int i=mBodies.size() - 1 ; i >= 0; i--) { + destroyCollisionBody(mBodies[i]); } - assert(mBodies.empty()); + assert(mBodies.size() == 0); } // Create a collision body and add it to the world @@ -80,7 +77,7 @@ CollisionBody* CollisionWorld::createCollisionBody(const Transform& transform) { assert(collisionBody != nullptr); // Add the collision body to the world - mBodies.insert(collisionBody); + mBodies.add(collisionBody); #ifdef IS_PROFILING_ACTIVE @@ -108,7 +105,7 @@ void CollisionWorld::destroyCollisionBody(CollisionBody* collisionBody) { collisionBody->~CollisionBody(); // Remove the collision body from the list of bodies - mBodies.erase(collisionBody); + mBodies.remove(collisionBody); // Free the object from the memory allocator mMemoryManager.release(MemoryManager::AllocationType::Pool, collisionBody, sizeof(CollisionBody)); @@ -121,7 +118,7 @@ bodyindex CollisionWorld::computeNextAvailableBodyID() { bodyindex bodyID; if (mFreeBodiesIDs.size() != 0) { bodyID = mFreeBodiesIDs[mFreeBodiesIDs.size() - 1]; - mFreeBodiesIDs.remove(mFreeBodiesIDs.size() - 1); + mFreeBodiesIDs.removeAt(mFreeBodiesIDs.size() - 1); } else { bodyID = mCurrentBodyID; @@ -135,7 +132,7 @@ bodyindex CollisionWorld::computeNextAvailableBodyID() { void CollisionWorld::resetContactManifoldListsOfBodies() { // For each rigid body of the world - for (std::set::iterator it = mBodies.begin(); it != mBodies.end(); ++it) { + for (List::Iterator it = mBodies.begin(); it != mBodies.end(); ++it) { // Reset the contact manifold list of the body (*it)->resetContactManifoldsList(); diff --git a/src/engine/CollisionWorld.h b/src/engine/CollisionWorld.h index bdffe4d1..1cc501e2 100644 --- a/src/engine/CollisionWorld.h +++ b/src/engine/CollisionWorld.h @@ -27,8 +27,6 @@ #define REACTPHYSICS3D_COLLISION_WORLD_H // Libraries -#include -#include #include #include "mathematics/mathematics.h" #include "containers/List.h" @@ -68,7 +66,7 @@ class CollisionWorld { CollisionDetection mCollisionDetection; /// All the bodies (rigid and soft) of the world - std::set mBodies; + List mBodies; /// Current body ID bodyindex mCurrentBodyID; @@ -109,12 +107,6 @@ class CollisionWorld { /// Deleted assignment operator CollisionWorld& operator=(const CollisionWorld& world) = delete; - /// Return an iterator to the beginning of the bodies of the physics world - std::set::iterator getBodiesBeginIterator(); - - /// Return an iterator to the end of the bodies of the physics world - std::set::iterator getBodiesEndIterator(); - /// Create a collision body CollisionBody* createCollisionBody(const Transform& transform); @@ -167,22 +159,6 @@ class CollisionWorld { friend class ConvexMeshShape; }; -// Return an iterator to the beginning of the bodies of the physics world -/** - * @return An starting iterator to the set of bodies of the world - */ -inline std::set::iterator CollisionWorld::getBodiesBeginIterator() { - return mBodies.begin(); -} - -// Return an iterator to the end of the bodies of the physics world -/** - * @return An ending iterator to the set of bodies of the world - */ -inline std::set::iterator CollisionWorld::getBodiesEndIterator() { - return mBodies.end(); -} - // Set the collision dispatch configuration /// This can be used to replace default collision detection algorithms by your /// custom algorithm for instance. diff --git a/src/engine/DynamicsWorld.cpp b/src/engine/DynamicsWorld.cpp index 3ef9cd30..77938f9e 100644 --- a/src/engine/DynamicsWorld.cpp +++ b/src/engine/DynamicsWorld.cpp @@ -44,7 +44,8 @@ DynamicsWorld::DynamicsWorld(const Vector3 &gravity) mContactSolver(mMemoryManager), mNbVelocitySolverIterations(DEFAULT_VELOCITY_SOLVER_NB_ITERATIONS), mNbPositionSolverIterations(DEFAULT_POSITION_SOLVER_NB_ITERATIONS), - mIsSleepingEnabled(SLEEPING_ENABLED), mGravity(gravity), mTimeStep(decimal(1.0f / 60.0f)), + mIsSleepingEnabled(SLEEPING_ENABLED), mRigidBodies(mMemoryManager.getPoolAllocator()), + mJoints(mMemoryManager.getPoolAllocator()), mGravity(gravity), mTimeStep(decimal(1.0f / 60.0f)), mIsGravityEnabled(true), mConstrainedLinearVelocities(nullptr), mConstrainedAngularVelocities(nullptr), mSplitLinearVelocities(nullptr), mSplitAngularVelocities(nullptr), mConstrainedPositions(nullptr), @@ -67,19 +68,13 @@ DynamicsWorld::DynamicsWorld(const Vector3 &gravity) DynamicsWorld::~DynamicsWorld() { // Destroy all the joints that have not been removed - std::set::iterator itJoints; - for (itJoints = mJoints.begin(); itJoints != mJoints.end();) { - std::set::iterator itToRemove = itJoints; - ++itJoints; - destroyJoint(*itToRemove); + for (int i=mJoints.size() - 1; i >= 0; i--) { + destroyJoint(mJoints[i]); } // Destroy all the rigid bodies that have not been removed - std::set::iterator itRigidBodies; - for (itRigidBodies = mRigidBodies.begin(); itRigidBodies != mRigidBodies.end();) { - std::set::iterator itToRemove = itRigidBodies; - ++itRigidBodies; - destroyRigidBody(*itToRemove); + for (int i=mRigidBodies.size() - 1; i >= 0; i--) { + destroyRigidBody(mRigidBodies[i]); } assert(mJoints.size() == 0); @@ -257,7 +252,7 @@ void DynamicsWorld::initVelocityArrays() { // Initialize the map of body indexes in the velocity arrays uint i = 0; - for (std::set::iterator it = mRigidBodies.begin(); it != mRigidBodies.end(); ++it) { + for (List::Iterator it = mRigidBodies.begin(); it != mRigidBodies.end(); ++it) { mSplitLinearVelocities[i].setToZero(); mSplitAngularVelocities[i].setToZero(); @@ -388,7 +383,7 @@ void DynamicsWorld::solvePositionCorrection() { PROFILE("DynamicsWorld::solvePositionCorrection()", &mProfiler); // Do not continue if there is no constraints - if (mJoints.empty()) return; + if (mJoints.size() == 0) return; // For each island of the world for (uint islandIndex = 0; islandIndex < mNbIslands; islandIndex++) { @@ -423,8 +418,8 @@ RigidBody* DynamicsWorld::createRigidBody(const Transform& transform) { assert(rigidBody != nullptr); // Add the rigid body to the physics world - mBodies.insert(rigidBody); - mRigidBodies.insert(rigidBody); + mBodies.add(rigidBody); + mRigidBodies.add(rigidBody); #ifdef IS_PROFILING_ACTIVE @@ -461,8 +456,8 @@ void DynamicsWorld::destroyRigidBody(RigidBody* rigidBody) { rigidBody->~RigidBody(); // Remove the rigid body from the list of rigid bodies - mBodies.erase(rigidBody); - mRigidBodies.erase(rigidBody); + mBodies.remove(rigidBody); + mRigidBodies.remove(rigidBody); // Free the object from the memory allocator mMemoryManager.release(MemoryManager::AllocationType::Pool, rigidBody, sizeof(RigidBody)); @@ -536,7 +531,7 @@ Joint* DynamicsWorld::createJoint(const JointInfo& jointInfo) { } // Add the joint into the world - mJoints.insert(newJoint); + mJoints.add(newJoint); // Add the joint into the joint list of the bodies involved in the joint addJointToBody(newJoint); @@ -565,7 +560,7 @@ void DynamicsWorld::destroyJoint(Joint* joint) { joint->getBody2()->setIsSleeping(false); // Remove the joint from the world - mJoints.erase(joint); + mJoints.remove(joint); // Remove the joint from the joint list of the bodies involved in the joint joint->mBody1->removeJointFromJointsList(mMemoryManager, joint); @@ -622,11 +617,11 @@ void DynamicsWorld::computeIslands() { int nbContactManifolds = 0; // Reset all the isAlreadyInIsland variables of bodies, joints and contact manifolds - for (std::set::iterator it = mRigidBodies.begin(); it != mRigidBodies.end(); ++it) { + for (List::Iterator it = mRigidBodies.begin(); it != mRigidBodies.end(); ++it) { int nbBodyManifolds = (*it)->resetIsAlreadyInIslandAndCountManifolds(); nbContactManifolds += nbBodyManifolds; } - for (std::set::iterator it = mJoints.begin(); it != mJoints.end(); ++it) { + for (List::Iterator it = mJoints.begin(); it != mJoints.end(); ++it) { (*it)->mIsAlreadyInIsland = false; } @@ -636,7 +631,7 @@ void DynamicsWorld::computeIslands() { nbBytesStack)); // For each rigid body of the world - for (std::set::iterator it = mRigidBodies.begin(); it != mRigidBodies.end(); ++it) { + for (List::Iterator it = mRigidBodies.begin(); it != mRigidBodies.end(); ++it) { RigidBody* body = *it; @@ -818,7 +813,7 @@ void DynamicsWorld::enableSleeping(bool isSleepingEnabled) { if (!mIsSleepingEnabled) { // For each body of the world - std::set::iterator it; + List::Iterator it; for (it = mRigidBodies.begin(); it != mRigidBodies.end(); ++it) { // Wake up the rigid body diff --git a/src/engine/DynamicsWorld.h b/src/engine/DynamicsWorld.h index 705f63d5..af2fb659 100644 --- a/src/engine/DynamicsWorld.h +++ b/src/engine/DynamicsWorld.h @@ -66,10 +66,10 @@ class DynamicsWorld : public CollisionWorld { bool mIsSleepingEnabled; /// All the rigid bodies of the physics world - std::set mRigidBodies; + List mRigidBodies; /// All the joints of the world - std::set mJoints; + List mJoints; /// Gravity vector of the world Vector3 mGravity; @@ -215,12 +215,6 @@ class DynamicsWorld : public CollisionWorld { /// Return the number of joints in the world uint getNbJoints() const; - /// Return an iterator to the beginning of the rigid bodies of the physics world - std::set::iterator getRigidBodiesBeginIterator(); - - /// Return an iterator to the end of the rigid bodies of the physics world - std::set::iterator getRigidBodiesEndIterator(); - /// Return true if the sleeping technique is enabled bool isSleepingEnabled() const; @@ -260,7 +254,7 @@ class DynamicsWorld : public CollisionWorld { inline void DynamicsWorld::resetBodiesForceAndTorque() { // For each body of the world - std::set::iterator it; + List::Iterator it; for (it = mRigidBodies.begin(); it != mRigidBodies.end(); ++it) { (*it)->mExternalForce.setToZero(); (*it)->mExternalTorque.setToZero(); @@ -370,22 +364,6 @@ inline uint DynamicsWorld::getNbJoints() const { return mJoints.size(); } -// Return an iterator to the beginning of the bodies of the physics world -/** - * @return Starting iterator of the set of rigid bodies - */ -inline std::set::iterator DynamicsWorld::getRigidBodiesBeginIterator() { - return mRigidBodies.begin(); -} - -// Return an iterator to the end of the bodies of the physics world -/** - * @return Ending iterator of the set of rigid bodies - */ -inline std::set::iterator DynamicsWorld::getRigidBodiesEndIterator() { - return mRigidBodies.end(); -} - // Return true if the sleeping technique is enabled /** * @return True if the sleeping technique is enabled and false otherwise diff --git a/src/engine/OverlappingPair.cpp b/src/engine/OverlappingPair.cpp index a5ca870a..3dbdc2f4 100644 --- a/src/engine/OverlappingPair.cpp +++ b/src/engine/OverlappingPair.cpp @@ -187,7 +187,7 @@ void OverlappingPair::clearObsoleteLastFrameCollisionInfos() { it->second->~LastFrameCollisionInfo(); mPersistentAllocator.release(it->second, sizeof(LastFrameCollisionInfo)); - mLastFrameCollisionInfos.remove(it++); + it = mLastFrameCollisionInfos.remove(it); } else { ++it; diff --git a/test/main.cpp b/test/main.cpp index 8bb62ea4..24b9f003 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -41,6 +41,7 @@ #include "tests/collision/TestTriangleVertexArray.h" #include "tests/containers/TestList.h" #include "tests/containers/TestMap.h" +#include "tests/containers/TestSet.h" using namespace reactphysics3d; @@ -52,6 +53,7 @@ int main() { testSuite.addTest(new TestList("List")); testSuite.addTest(new TestMap("Map")); + testSuite.addTest(new TestSet("Set")); // ---------- Mathematics tests ---------- // diff --git a/test/tests/containers/TestList.h b/test/tests/containers/TestList.h index 669b0bef..750d0788 100644 --- a/test/tests/containers/TestList.h +++ b/test/tests/containers/TestList.h @@ -62,6 +62,7 @@ class TestList : public Test { testAddRemoveClear(); testAssignment(); testIndexing(); + testFind(); testEquality(); testReserve(); testIterators(); @@ -142,27 +143,52 @@ class TestList : public Test { list3.add(3); list3.add(4); - list3.remove(3); + auto it = list3.removeAt(3); test(list3.size() == 3); test(list3.capacity() == 4); + test(it == list3.end()); test(list3[0] = 1); test(list3[1] = 2); test(list3[2] = 3); - list3.remove(1); + it = list3.removeAt(1); test(list3.size() == 2); test(list3.capacity() == 4); test(list3[0] = 1); test(list3[1] = 3); + test(*it = 3); - list3.remove(0); + list3.removeAt(0); test(list3.size() == 1); test(list3.capacity() == 4); test(list3[0] = 3); - list3.remove(0); + it = list3.removeAt(0); test(list3.size() == 0); test(list3.capacity() == 4); + test(it == list3.end()); + + list3.add(1); + list3.add(2); + list3.add(3); + it = list3.begin(); + list3.remove(it); + test(list3.size() == 2); + test(list3[0] == 2); + test(list3[1] == 3); + it = list3.find(3); + list3.remove(it); + test(list3.size() == 1); + test(list3[0] == 2); + + list3.add(5); + list3.add(6); + list3.add(7); + it = list3.remove(7); + test(it == list3.end()); + test(list3.size() == 3); + it = list3.remove(5); + test((*it) == 6); // ----- Test addRange() ----- // @@ -270,6 +296,20 @@ class TestList : public Test { test(list1[1] == 8); } + void testFind() { + + List list1(mAllocator); + list1.add(1); + list1.add(2); + list1.add(3); + list1.add(4); + list1.add(5); + + test(list1.find(1) == list1.begin()); + test(*(list1.find(2)) == 2); + test(*(list1.find(5)) == 5); + } + void testEquality() { List list1(mAllocator); diff --git a/test/tests/containers/TestMap.h b/test/tests/containers/TestMap.h index 503b0c24..2e9b3133 100644 --- a/test/tests/containers/TestMap.h +++ b/test/tests/containers/TestMap.h @@ -195,9 +195,10 @@ class TestMap : public Test { test(!map1.containsKey(8)); test(map1.size() == 1); - map1.remove(56); + auto it = map1.remove(56); test(!map1.containsKey(56)); test(map1.size() == 0); + test(it == map1.end()); isValid = true; for (int i = 0; i < 1000000; i++) { @@ -219,12 +220,19 @@ class TestMap : public Test { map3.add(std::make_pair(2, 20)); map3.add(std::make_pair(3, 30)); test(map3.size() == 3); - auto it = map3.begin(); + it = map3.begin(); map3.remove(it++); test(!map3.containsKey(1)); test(map3.size() == 2); test(it->second == 20); + map3.add(std::make_pair(56, 32)); + map3.add(std::make_pair(23, 89)); + for (it = map3.begin(); it != map3.end();) { + it = map3.remove(it); + } + test(map3.size() == 0); + // ----- Test clear() ----- // Map map4(mAllocator); @@ -367,6 +375,7 @@ class TestMap : public Test { Map map2(mAllocator); map2 = map1; test(map2.size() == map1.size()); + test(map1 == map2); test(map2[1] == 3); test(map2[2] == 6); test(map2[10] == 30); @@ -374,6 +383,7 @@ class TestMap : public Test { Map map3(mAllocator, 100); map3 = map1; test(map3.size() == map1.size()); + test(map3 == map1); test(map3[1] == 3); test(map3[2] == 6); test(map3[10] == 30); @@ -381,12 +391,14 @@ class TestMap : public Test { Map map4(mAllocator); map3 = map4; test(map3.size() == 0); + test(map3 == map4); Map map5(mAllocator); map5.add(std::make_pair(7, 8)); map5.add(std::make_pair(19, 70)); map1 = map5; test(map5.size() == map1.size()); + test(map5 == map1); test(map1[7] == 8); test(map1[19] == 70); } diff --git a/test/tests/containers/TestSet.h b/test/tests/containers/TestSet.h new file mode 100644 index 00000000..ddf2c245 --- /dev/null +++ b/test/tests/containers/TestSet.h @@ -0,0 +1,417 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2016 Daniel Chappuis * +********************************************************************************* +* * +* 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. * +* * +********************************************************************************/ + +#ifndef TEST_SET_H +#define TEST_SET_H + +// Libraries +#include "Test.h" +#include "containers/Set.h" +#include "memory/DefaultAllocator.h" + +// Key to test map with always same hash values +namespace reactphysics3d { + struct TestValueSet { + int key; + + TestValueSet(int k) :key(k) {} + + bool operator==(const TestValueSet& testValue) const { + return key == testValue.key; + } + }; +} + +// Hash function for struct VerticesPair +namespace std { + + template <> struct hash { + + size_t operator()(const reactphysics3d::TestValueSet& value) const { + return 1; + } + }; +} + +/// Reactphysics3D namespace +namespace reactphysics3d { + +// Class TestSet +/** + * Unit test for the Set class + */ +class TestSet : public Test { + + private : + + // ---------- Atributes ---------- // + + DefaultAllocator mAllocator; + + public : + + // ---------- Methods ---------- // + + /// Constructor + TestSet(const std::string& name) : Test(name) { + + } + + /// Run the tests + void run() { + + testConstructors(); + testReserve(); + testAddRemoveClear(); + testContains(); + testFind(); + testEquality(); + testAssignment(); + testIterators(); + } + + void testConstructors() { + + // ----- Constructors ----- // + + Set set1(mAllocator); + test(set1.capacity() == 0); + test(set1.size() == 0); + + Set set2(mAllocator, 100); + test(set2.capacity() >= 100); + test(set2.size() == 0); + + // ----- Copy Constructors ----- // + Set set3(set1); + test(set3.capacity() == set1.capacity()); + test(set3.size() == set1.size()); + + Set set4(mAllocator); + set4.add(10); + set4.add(20); + set4.add(30); + test(set4.capacity() >= 3); + test(set4.size() == 3); + set4.add(30); + test(set4.size() == 3); + + Set set5(set4); + test(set5.capacity() == set4.capacity()); + test(set5.size() == set4.size()); + test(set5.contains(10)); + test(set5.contains(20)); + test(set5.contains(30)); + } + + void testReserve() { + + Set set1(mAllocator); + set1.reserve(15); + test(set1.capacity() >= 15); + set1.add("test1"); + set1.add("test2"); + test(set1.capacity() >= 15); + + set1.reserve(10); + test(set1.capacity() >= 15); + + set1.reserve(100); + test(set1.capacity() >= 100); + test(set1.contains("test1")); + test(set1.contains("test2")); + } + + void testAddRemoveClear() { + + // ----- Test add() ----- // + + Set set1(mAllocator); + set1.add(10); + set1.add(80); + set1.add(130); + test(set1.contains(10)); + test(set1.contains(80)); + test(set1.contains(130)); + test(set1.size() == 3); + + Set set2(mAllocator, 15); + for (int i = 0; i < 1000000; i++) { + set2.add(i); + } + bool isValid = true; + for (int i = 0; i < 1000000; i++) { + if (!set2.contains(i)) isValid = false; + } + test(isValid); + + set1.remove(10); + set1.add(10); + test(set1.size() == 3); + test(set1.contains(10)); + + set1.add(34); + test(set1.contains(34)); + test(set1.size() == 4); + + // ----- Test remove() ----- // + + set1.remove(10); + test(!set1.contains(10)); + test(set1.contains(80)); + test(set1.contains(130)); + test(set1.contains(34)); + test(set1.size() == 3); + + set1.remove(80); + test(!set1.contains(80)); + test(set1.contains(130)); + test(set1.contains(34)); + test(set1.size() == 2); + + set1.remove(130); + test(!set1.contains(130)); + test(set1.contains(34)); + test(set1.size() == 1); + + set1.remove(34); + test(!set1.contains(34)); + test(set1.size() == 0); + + isValid = true; + for (int i = 0; i < 1000000; i++) { + set2.remove(i); + } + for (int i = 0; i < 1000000; i++) { + if (set2.contains(i)) isValid = false; + } + test(isValid); + test(set2.size() == 0); + + Set set3(mAllocator); + for (int i=0; i < 1000000; i++) { + set3.add(i); + set3.remove(i); + } + + set3.add(1); + set3.add(2); + set3.add(3); + test(set3.size() == 3); + auto it = set3.begin(); + set3.remove(it++); + test(!set3.contains(1)); + test(set3.size() == 2); + test(*it == 2); + + set3.add(6); + set3.add(7); + set3.add(8); + for (it = set3.begin(); it != set3.end();) { + it = set3.remove(it); + } + test(set3.size() == 0); + + // ----- Test clear() ----- // + + Set set4(mAllocator); + set4.add(2); + set4.add(4); + set4.add(6); + set4.clear(); + test(set4.size() == 0); + set4.add(2); + test(set4.size() == 1); + test(set4.contains(2)); + set4.clear(); + test(set4.size() == 0); + + Set set5(mAllocator); + set5.clear(); + test(set5.size() == 0); + + // ----- Test map with always same hash value for keys ----- // + + Set set6(mAllocator); + for (int i=0; i < 1000; i++) { + set6.add(TestValueSet(i)); + } + bool isTestValid = true; + for (int i=0; i < 1000; i++) { + if (!set6.contains(TestValueSet(i))) { + isTestValid = false; + } + } + test(isTestValid); + for (int i=0; i < 1000; i++) { + set6.remove(TestValueSet(i)); + } + test(set6.size() == 0); + } + + void testContains() { + + Set set1(mAllocator); + + test(!set1.contains(2)); + test(!set1.contains(4)); + test(!set1.contains(6)); + + set1.add(2); + set1.add(4); + set1.add(6); + + test(set1.contains(2)); + test(set1.contains(4)); + test(set1.contains(6)); + + set1.remove(4); + test(!set1.contains(4)); + test(set1.contains(2)); + test(set1.contains(6)); + + set1.clear(); + test(!set1.contains(2)); + test(!set1.contains(6)); + } + + void testFind() { + + Set set1(mAllocator); + set1.add(2); + set1.add(4); + set1.add(6); + test(set1.find(2) != set1.end()); + test(set1.find(4) != set1.end()); + test(set1.find(6) != set1.end()); + test(set1.find(45) == set1.end()); + + set1.remove(2); + + test(set1.find(2) == set1.end()); + } + + void testEquality() { + + Set set1(mAllocator, 10); + Set set2(mAllocator, 2); + + test(set1 == set2); + + set1.add("a"); + set1.add("b"); + set1.add("c"); + + set2.add("a"); + set2.add("b"); + set2.add("h"); + + test(set1 == set1); + test(set2 == set2); + test(set1 != set2); + test(set2 != set1); + + set1.add("a"); + set2.remove("h"); + set2.add("c"); + + test(set1 == set2); + test(set2 == set1); + + Set set3(mAllocator); + set3.add("a"); + + test(set1 != set3); + test(set2 != set3); + test(set3 != set1); + test(set3 != set2); + } + + void testAssignment() { + + Set set1(mAllocator); + set1.add(1); + set1.add(2); + set1.add(10); + + Set set2(mAllocator); + set2 = set1; + test(set2.size() == set1.size()); + test(set2.contains(1)); + test(set2.contains(2)); + test(set2.contains(10)); + test(set1 == set2); + + Set set3(mAllocator, 100); + set3 = set1; + test(set3.size() == set1.size()); + test(set3 == set1); + test(set3.contains(1)); + test(set3.contains(2)); + test(set3.contains(10)); + + Set set4(mAllocator); + set3 = set4; + test(set3.size() == 0); + test(set3 == set4); + + Set set5(mAllocator); + set5.add(7); + set5.add(19); + set1 = set5; + test(set5.size() == set1.size()); + test(set1 == set5); + test(set1.contains(7)); + test(set1.contains(19)); + } + + void testIterators() { + + Set set1(mAllocator); + + test(set1.begin() == set1.end()); + + set1.add(1); + set1.add(2); + set1.add(3); + set1.add(4); + + Set::Iterator itBegin = set1.begin(); + Set::Iterator it = set1.begin(); + + test(itBegin == it); + + int size = 0; + for (auto it = set1.begin(); it != set1.end(); ++it) { + test(set1.contains(*it)); + size++; + } + test(set1.size() == size); + } + }; + +} + +#endif