Initial commit: Final state of the master project
This commit is contained in:
490
Research/core/Util/BinaryTree.h
Normal file
490
Research/core/Util/BinaryTree.h
Normal file
@@ -0,0 +1,490 @@
|
||||
#pragma once
|
||||
#include "../Defines.h"
|
||||
#include "../BitHelper.h"
|
||||
#include "../CollectionHelper.h"
|
||||
#include "../Serializer.h"
|
||||
#include "../../inc/tbb/parallel_for_each.h"
|
||||
#include "../../inc/tbb/parallel_for.h"
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
template<typename T>
|
||||
class BinaryTree
|
||||
{
|
||||
private:
|
||||
template<typename U>
|
||||
struct BinaryTreeNode
|
||||
{
|
||||
private:
|
||||
BinaryTreeNode<U>* mChildren[2];
|
||||
BinaryTree<U>* mTree;
|
||||
T mMaterial;
|
||||
|
||||
inline unsigned8 GetChildIndex(size_t coordinate, unsigned8 levelsLeft) const {
|
||||
return BitHelper::GetLS(coordinate, levelsLeft) ? 1 : 0;
|
||||
}
|
||||
|
||||
inline BinaryTreeNode<U>* GetNodeAt(size_t coordinate, unsigned8 levelsLeft) const
|
||||
{
|
||||
return mChildren[GetChildIndex(coordinate, levelsLeft)];
|
||||
}
|
||||
public:
|
||||
BinaryTreeNode(BinaryTree<U>* tree) :
|
||||
mTree(tree)
|
||||
{
|
||||
mChildren[0] = mChildren[1] = NULL;
|
||||
}
|
||||
|
||||
~BinaryTreeNode() {}
|
||||
|
||||
BinaryTreeNode* GetChild(unsigned8 index) const
|
||||
{
|
||||
assert(index < 2);
|
||||
return mChildren[index];
|
||||
}
|
||||
|
||||
void SetChild(BinaryTreeNode<U>* child, unsigned8 index) { mChildren[index] = child; }
|
||||
|
||||
size_t GetChildCount() const
|
||||
{
|
||||
return
|
||||
(mChildren[0] == NULL ? 0 : 1) +
|
||||
(mChildren[1] == NULL ? 0 : 1);
|
||||
}
|
||||
bool IsLeaf() const { return mChildren[0] == NULL && mChildren[1] == NULL; }
|
||||
|
||||
BinaryTreeNode<U>* AddNode(size_t coordinate, unsigned8 levelsLeft)
|
||||
{
|
||||
auto node = GetNodeAt(coordinate, levelsLeft);
|
||||
if (node == NULL)
|
||||
{
|
||||
node = mTree->Create();
|
||||
SetChild(node, GetChildIndex(coordinate, levelsLeft));
|
||||
}
|
||||
if (levelsLeft == 0)
|
||||
return node;
|
||||
else
|
||||
return node->AddNode(coordinate, levelsLeft - 1);
|
||||
}
|
||||
|
||||
U GetValueAt(size_t coordinate, unsigned8 levelsLeft) const
|
||||
{
|
||||
auto node = GetNodeAt(coordinate, levelsLeft);
|
||||
if (node == NULL) return GetValue();
|
||||
if (levelsLeft == 0) return node->GetValue();
|
||||
return node->GetValueAt(coordinate, levelsLeft - 1);
|
||||
}
|
||||
|
||||
U GetValue() const { return mMaterial; }
|
||||
|
||||
void SetValue(const T& material) { mMaterial = material; }
|
||||
|
||||
void Traverse(const std::function<void(const BinaryTreeNode<U>*)>& f) const
|
||||
{
|
||||
f.operator()(this);
|
||||
if (mChildren[0] != NULL) mChildren[0]->Traverse(f);
|
||||
if (mChildren[1] != NULL) mChildren[1]->Traverse(f);
|
||||
}
|
||||
|
||||
static bool Compare(BinaryTreeNode<U>* a, BinaryTreeNode<U>* b)
|
||||
{
|
||||
if (a->GetChild(0) != b->GetChild(0)) return (size_t)a->GetChild(0) < (size_t)b->GetChild(0);
|
||||
if (a->GetChild(1) != b->GetChild(1)) return (size_t)a->GetChild(1) < (size_t)b->GetChild(1);
|
||||
return a->GetValue() < b->GetValue();
|
||||
}
|
||||
|
||||
static bool Equals(BinaryTreeNode<U>* a, BinaryTreeNode<U>* b)
|
||||
{
|
||||
if (a == b) return true;
|
||||
if (a == NULL || b == NULL) return false;
|
||||
return a->GetChild(0) == b->GetChild(0) && a->GetChild(1) == b->GetChild(1) && a->GetValue() == b->GetValue();
|
||||
}
|
||||
};
|
||||
|
||||
unsigned8 mDepth;
|
||||
BinaryTreeNode<T>* mRoot;
|
||||
std::vector<BinaryTreeNode<T>*> mNodePool;
|
||||
|
||||
std::vector<BinaryTreeNode<T>*> GetNodes()
|
||||
{
|
||||
std::vector<BinaryTreeNode<T>*> res;
|
||||
std::function<void(const BinaryTreeNode<T>*)> nodeFinder = [&](const BinaryTreeNode<T>*) { res.push_back(this); };
|
||||
mRoot->Traverse(nodeFinder);
|
||||
CollectionHelper::Unique(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
void ShiftDown(unsigned8 levels)
|
||||
{
|
||||
// Shifts the root down the given number of levels
|
||||
if (levels == 0) return;
|
||||
|
||||
for (unsigned8 i = 0; i < levels; i++)
|
||||
{
|
||||
BinaryTreeNode<T>* newRoot = Create();
|
||||
newRoot->SetChild(mRoot, 0);
|
||||
BinaryTreeNode<T>* oldRoot = mRoot;
|
||||
mRoot = newRoot;
|
||||
mNodePool[0] = newRoot;
|
||||
mNodePool[mNodePool.size() - 1] = oldRoot;
|
||||
}
|
||||
}
|
||||
|
||||
static void CalculateNodeLevelsRecursive(BinaryTreeNode<T>* node, unsigned8 level, std::vector<unsigned8>& nodeLevels, const std::unordered_map<BinaryTreeNode<T>*, size_t>& nodeIndices)
|
||||
{
|
||||
assert(nodeIndices.find(node) != nodeIndices.end());
|
||||
auto nodeIndex = nodeIndices.find(node);
|
||||
nodeLevels[nodeIndex->second] = level;
|
||||
for (unsigned8 child = 0; child < 2; child++)
|
||||
{
|
||||
auto childNode = node->GetChild(child);
|
||||
if (childNode != NULL)
|
||||
CalculateNodeLevelsRecursive(childNode, level + 1, nodeLevels, nodeIndices);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculates the levels of all nodes. Levels are stored in the same order as the node pool.
|
||||
std::vector<unsigned8> CalculateNodeLevels() const
|
||||
{
|
||||
auto nodeIndices = CollectionHelper::GetIndexMap(mNodePool);
|
||||
std::vector<unsigned8> nodeLevels(mNodePool.size());
|
||||
CalculateNodeLevelsRecursive(mRoot, 0, nodeLevels, nodeIndices);
|
||||
return nodeLevels;
|
||||
}
|
||||
|
||||
inline static size_t GetNodePointer(const size_t& index, const size_t& pointerSize, const size_t& valueSize, const size_t& firstLeafIndex, const bool& onlyLeafsContainValues)
|
||||
{
|
||||
if (!onlyLeafsContainValues)
|
||||
return 2 + index * (pointerSize * 2 + valueSize);
|
||||
else
|
||||
{
|
||||
size_t pointer = 2 + std::min(index, firstLeafIndex) * (pointerSize * 2);
|
||||
if (index > firstLeafIndex)
|
||||
pointer += (index - firstLeafIndex - 1) * (pointerSize * 2 + valueSize);
|
||||
return pointer;
|
||||
}
|
||||
}
|
||||
public:
|
||||
BinaryTree()
|
||||
{
|
||||
mDepth = 0;
|
||||
mRoot = Create();
|
||||
}
|
||||
~BinaryTree()
|
||||
{
|
||||
tbb::parallel_for_each(mNodePool.begin(), mNodePool.end(), [](BinaryTreeNode<T>* node) { delete node; });
|
||||
mNodePool.clear();
|
||||
}
|
||||
|
||||
void AddLeafNode(size_t coordinate) { AddNode(coordinate, mDepth); }
|
||||
void AddNode(size_t coordinate, unsigned8 level)
|
||||
{
|
||||
assert(level <= mDepth);
|
||||
mRoot->AddNode(coordinate, level);
|
||||
}
|
||||
|
||||
T GetValueAtLeaf(size_t coordinate) const { return GetValueAtNode(coordinate, mDepth); }
|
||||
T GetValueAtNode(size_t coordinate, unsigned8 level) const
|
||||
{
|
||||
assert(level <= mDepth);
|
||||
return mRoot->GetValueAt(coordinate, level);
|
||||
}
|
||||
void SetValueAtLeaf(size_t coordinate, T value) { SetValueAtNode(coordinate, mDepth, value); }
|
||||
void SetValueAtNode(size_t coordinate, unsigned8 level, T value)
|
||||
{
|
||||
assert(level <= mDepth);
|
||||
auto node = mRoot->AddNode(coordinate, level);
|
||||
node->SetValue(value);
|
||||
}
|
||||
|
||||
BinaryTreeNode<T>* Create()
|
||||
{
|
||||
BinaryTreeNode<T>* newNode = new BinaryTreeNode<T>(this);
|
||||
mNodePool.push_back(newNode);
|
||||
return newNode;
|
||||
}
|
||||
|
||||
void SetDepth(unsigned8 wantedDepth, bool shiftExisting)
|
||||
{
|
||||
if (mDepth != wantedDepth)
|
||||
{
|
||||
if (wantedDepth < mDepth)
|
||||
ShaveUntil(wantedDepth);
|
||||
else
|
||||
ShiftDown(wantedDepth - mDepth);
|
||||
}
|
||||
mDepth = wantedDepth;
|
||||
}
|
||||
|
||||
// Deletes all nodes of which the level is greater than the given level
|
||||
void ShaveUntil(unsigned8 level)
|
||||
{
|
||||
// Calculate the level for all nodes
|
||||
std::vector<unsigned8> nodeLevels = CalculateNodeLevels();
|
||||
for (size_t i = 0; i < mNodePool.size(); i++)
|
||||
if (nodeLevels[i] > level)
|
||||
{
|
||||
delete mNodePool[i];
|
||||
mNodePool[i] = NULL;
|
||||
}
|
||||
else if (nodeLevels[i] == level)
|
||||
{
|
||||
// Set all pointers to NULL:
|
||||
for (unsigned8 child = 0; child < 2; child++) mNodePool[i]->SetChild(NULL, child);
|
||||
}
|
||||
mNodePool.erase(std::remove_if(mNodePool.begin(), mNodePool.end(), [](const BinaryTreeNode<T>* node) { return node == NULL; }));
|
||||
}
|
||||
|
||||
void ReplaceValues(const std::unordered_map<T, T>& replacers)
|
||||
{
|
||||
tbb::parallel_for(size_t(0), mNodePool.size(), [&](const size_t& i)
|
||||
{
|
||||
T curValue = mNodePool[i]->GetValue();
|
||||
auto replacer = replacers.find(curValue);
|
||||
if (replacer != replacers.end())
|
||||
mNodePool[i]->SetValue(replacer->second);
|
||||
});
|
||||
}
|
||||
|
||||
void Serialize(std::ostream& file) const
|
||||
{
|
||||
Serializer<unsigned8>::Serialize(mDepth, file);
|
||||
|
||||
// Write the number of nodes in the binary tree:
|
||||
Serializer<unsigned64>::Serialize((unsigned64)mNodePool.size(), file);
|
||||
|
||||
// Write the node materials
|
||||
for (size_t i = 0; i < mNodePool.size(); i++)
|
||||
Serializer<T>::Serialize(mNodePool[i]->GetValue(), file);
|
||||
|
||||
// Write (consequtively) the child pointers
|
||||
std::unordered_map<BinaryTreeNode<T>*, size_t> nodeIndices = CollectionHelper::GetIndexMap(mNodePool);
|
||||
for (size_t i = 0; i < mNodePool.size(); i++)
|
||||
for (unsigned8 child = 0; child < 2; child++)
|
||||
{
|
||||
// Use 0 as NULL pointer, add 1 to all actual pointers
|
||||
BinaryTreeNode<T>* childNode = mNodePool[i]->GetChild(child);
|
||||
unsigned64 pointer = 0;
|
||||
if (childNode != NULL)
|
||||
{
|
||||
assert(nodeIndices.find(childNode) != nodeIndices.end());
|
||||
pointer = nodeIndices[childNode] + 1;
|
||||
}
|
||||
Serializer<unsigned64>::Serialize(pointer, file);
|
||||
}
|
||||
}
|
||||
|
||||
void Deserialize(std::istream& file)
|
||||
{
|
||||
if (mNodePool.size() > 1)
|
||||
{
|
||||
for (auto node = mNodePool.begin(); node != mNodePool.end(); node++)
|
||||
delete *node;
|
||||
mNodePool.resize(1);
|
||||
}
|
||||
|
||||
Serializer<unsigned8>::Deserialize(mDepth, file);
|
||||
|
||||
unsigned64 nodeCount;
|
||||
Serializer<unsigned64>::Deserialize(nodeCount, file);
|
||||
// The root is always already created, so create nodeCount - 1 nodes:
|
||||
for (size_t i = 0; i < nodeCount - 1; i++) Create();
|
||||
|
||||
// Deserialize the materials for each node
|
||||
T dummy;
|
||||
for (size_t i = 0; i < nodeCount; i++)
|
||||
{
|
||||
Serializer<T>::Deserialize(dummy, file);
|
||||
mNodePool[i]->SetValue(dummy);
|
||||
}
|
||||
|
||||
// Create all pointers
|
||||
for (size_t i = 0; i < mNodePool.size(); i++)
|
||||
for (unsigned8 child = 0; child < 2; child++)
|
||||
{
|
||||
unsigned64 pointer;
|
||||
Serializer<unsigned64>::Deserialize(pointer, file);
|
||||
if (pointer != 0)
|
||||
mNodePool[i]->SetChild(mNodePool[pointer - 1], child);
|
||||
}
|
||||
}
|
||||
|
||||
// Converts the current binary tree to a DAG, meaning that all duplicate nodes are removed.
|
||||
void ToDAG()
|
||||
{
|
||||
// Fill the current layer with all leaf nodes
|
||||
std::vector<BinaryTreeNode<T>*> dagNodePool(1, mRoot);
|
||||
std::vector<BinaryTreeNode<T>*> currentLayer;
|
||||
std::unordered_set<BinaryTreeNode<T>*> nodesLeft;
|
||||
std::unordered_map<BinaryTreeNode<T>*, std::vector<BinaryTreeNode<T>*>> parentsMap;
|
||||
for (auto node : mNodePool)
|
||||
{
|
||||
if (node->IsLeaf()) currentLayer.push_back(node);
|
||||
else
|
||||
{
|
||||
nodesLeft.insert(node);
|
||||
for (unsigned8 child = 0; child < 2; child++)
|
||||
{
|
||||
auto childNode = node->GetChild(child);
|
||||
if (childNode != NULL)
|
||||
{
|
||||
auto parent = parentsMap.find(childNode);
|
||||
if (parent == parentsMap.end())
|
||||
parentsMap.insert(std::make_pair(childNode, std::vector<BinaryTreeNode<T>*>(1, node)));
|
||||
else
|
||||
parent->second.push_back(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (!currentLayer.empty() && currentLayer[0] != mRoot)
|
||||
{
|
||||
// Find unique nodes and replace them
|
||||
tbb::parallel_sort(currentLayer.begin(), currentLayer.end(), [](BinaryTreeNode<T>* a, BinaryTreeNode<T>* b) { return BinaryTreeNode<T>::Compare(a, b); });
|
||||
BinaryTreeNode<T>* cur = NULL;
|
||||
std::vector<std::pair<BinaryTreeNode<T>*, BinaryTreeNode<T>*>> replacements;
|
||||
std::vector<BinaryTreeNode<T>*> nextLayer;
|
||||
size_t uniqueNodes = 0;
|
||||
for (auto node : currentLayer)
|
||||
{
|
||||
if (BinaryTreeNode<T>::Equals(node, cur))
|
||||
// Make sure that all nodes are replaced by their equals
|
||||
replacements.push_back(std::make_pair(node, cur));
|
||||
else
|
||||
{
|
||||
uniqueNodes++;
|
||||
cur = node;
|
||||
dagNodePool.push_back(cur);
|
||||
}
|
||||
auto parents = parentsMap.find(node);
|
||||
if (parents != parentsMap.end())
|
||||
nextLayer.insert(nextLayer.end(), parents->second.begin(), parents->second.end());
|
||||
}
|
||||
CollectionHelper::Unique(nextLayer, [](BinaryTreeNode<T>* a, BinaryTreeNode<T>* b) { return BinaryTreeNode<T>::Compare(a, b); });
|
||||
if (uniqueNodes != currentLayer.size())
|
||||
{
|
||||
for (auto replacement : replacements)
|
||||
{
|
||||
auto toReplace = replacement.first;
|
||||
auto replacer = replacement.second;
|
||||
auto parentsIt = parentsMap.find(toReplace);
|
||||
if (parentsIt == parentsMap.end())
|
||||
continue;
|
||||
std::vector<BinaryTreeNode<T>*> parents = parentsIt->second;
|
||||
for (auto parent : parents)
|
||||
{
|
||||
for (unsigned8 child = 0; child < 2; child++)
|
||||
{
|
||||
if (parent->GetChild(child) == toReplace)
|
||||
parent->SetChild(replacer, child);
|
||||
}
|
||||
}
|
||||
delete toReplace;
|
||||
}
|
||||
}
|
||||
currentLayer = nextLayer;
|
||||
}
|
||||
mNodePool = dagNodePool;
|
||||
}
|
||||
|
||||
std::vector<unsigned8> Serialize(bool onlyLeafsContainValues) const
|
||||
{
|
||||
// The first byte contains the number of bytes per pointer
|
||||
unsigned8 pointerSize = GetSerializedPointerByteSize(onlyLeafsContainValues);
|
||||
unsigned8 valueSize = GetMaterialByteSize();
|
||||
|
||||
std::vector<unsigned8> res(GetSerializedByteCount(onlyLeafsContainValues), 0);
|
||||
res[0] = mDepth;
|
||||
res[1] = pointerSize;
|
||||
|
||||
std::vector<BinaryTreeNode<T>*> nodePoolCopy(mNodePool.size());
|
||||
std::copy(mNodePool.begin(), mNodePool.end(), nodePoolCopy.begin());
|
||||
size_t firstLeafIndex = ~size_t(0);
|
||||
if (onlyLeafsContainValues)
|
||||
{
|
||||
tbb::parallel_sort(nodePoolCopy.begin() + 1, nodePoolCopy.end(), [](BinaryTreeNode<T>* a, BinaryTreeNode<T>* b)
|
||||
{
|
||||
return !(a->IsLeaf()) && (b->IsLeaf());
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < nodePoolCopy.size(); i++)
|
||||
{
|
||||
BinaryTreeNode<T>* node = nodePoolCopy[i];
|
||||
if (node->IsLeaf() && firstLeafIndex > i)
|
||||
firstLeafIndex = i;
|
||||
}
|
||||
}
|
||||
std::unordered_map<BinaryTreeNode<T>*, size_t> nodeIndices = CollectionHelper::GetIndexMap(nodePoolCopy);
|
||||
|
||||
for (size_t i = 0; i < nodePoolCopy.size(); i++)
|
||||
{
|
||||
BinaryTreeNode<T>* node = nodePoolCopy[i];
|
||||
size_t nodePointer = GetNodePointer(i, pointerSize, valueSize, firstLeafIndex, onlyLeafsContainValues);
|
||||
// Write the node pointers
|
||||
for (unsigned8 child = 0; child < 2; child++)
|
||||
{
|
||||
BinaryTreeNode<T>* childNode = node->GetChild(child);
|
||||
if (childNode != NULL)
|
||||
{
|
||||
assert(nodeIndices.find(childNode) != nodeIndices.end());
|
||||
size_t childIndex = nodeIndices[childNode];
|
||||
size_t pointer = GetNodePointer(childIndex, pointerSize, valueSize, firstLeafIndex, onlyLeafsContainValues);
|
||||
BitHelper::SplitInBytesAndMove(pointer, res, nodePointer + pointerSize * child, pointerSize);
|
||||
}
|
||||
}
|
||||
// Then write the node content/value
|
||||
if (!onlyLeafsContainValues || node->IsLeaf())
|
||||
{
|
||||
std::vector<unsigned8> serializedValue = node->GetValue().Serialize();
|
||||
std::move(serializedValue.begin(), serializedValue.end(), res.begin() + nodePointer + pointerSize * 2);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
size_t GetNodeCount() const { return mNodePool.size(); }
|
||||
size_t GetLeafNodeCount() const {
|
||||
size_t leafNodeCount = 0;
|
||||
for (auto node : mNodePool) if (node->IsLeaf()) leafNodeCount++;
|
||||
return leafNodeCount;
|
||||
}
|
||||
|
||||
static size_t GetSerializedByteCount(const size_t& nodeCount, const size_t& leafCount, const size_t& pointerSize, const size_t& valueSize, const bool& onlyLeafsContainValues)
|
||||
{
|
||||
return (onlyLeafsContainValues ? leafCount : nodeCount) * valueSize + (2 * pointerSize) * nodeCount;
|
||||
}
|
||||
|
||||
size_t GetSerializedByteCount(bool onlyLeafsContainValues) const
|
||||
{
|
||||
return GetSerializedByteCount(GetNodeCount(), GetLeafNodeCount(), GetSerializedPointerByteSize(onlyLeafsContainValues), GetMaterialByteSize(), onlyLeafsContainValues);
|
||||
}
|
||||
|
||||
unsigned8 GetSerializedNodeByteSize(bool onlyLeafsContainValues) const
|
||||
{
|
||||
return 2 * GetSerializedPointerByteSize(onlyLeafsContainValues) + GetMaterialByteSize();
|
||||
}
|
||||
|
||||
unsigned8 GetMaterialByteSize() const
|
||||
{
|
||||
return sizeof(T);
|
||||
}
|
||||
|
||||
unsigned8 GetSerializedPointerByteSize(bool onlyLeafsContainValues) const
|
||||
{
|
||||
// Count the number of leaf nodes:
|
||||
size_t leafNodeCount = 0;
|
||||
if (onlyLeafsContainValues)
|
||||
leafNodeCount = GetLeafNodeCount();
|
||||
bool fits = false;
|
||||
unsigned8 pointerSize = 0;
|
||||
while (!fits)
|
||||
{
|
||||
++pointerSize;
|
||||
size_t requiredBytes = GetSerializedByteCount(GetNodeCount(), leafNodeCount, pointerSize, GetMaterialByteSize(), onlyLeafsContainValues);
|
||||
fits = BitHelper::Exp2(8 * pointerSize) > requiredBytes;
|
||||
}
|
||||
return pointerSize;
|
||||
}
|
||||
};
|
||||
223
Research/core/Util/BlockVector.h
Normal file
223
Research/core/Util/BlockVector.h
Normal file
@@ -0,0 +1,223 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include "IndexIterator.h"
|
||||
//#include "../BitHelper.h"
|
||||
|
||||
template <typename T, unsigned8 BLOCK_POINTER_BITS = 14>
|
||||
class BlockVector
|
||||
{
|
||||
public:
|
||||
const static unsigned64 BLOCK_SIZE = 1 << BLOCK_POINTER_BITS;
|
||||
const static size_t BLOCK_SIZE_MASK = BLOCK_SIZE - 1;
|
||||
protected:
|
||||
typedef std::vector<T> block;
|
||||
std::vector<block*> mData;
|
||||
size_t mSize;
|
||||
|
||||
inline size_t block_count(size_t n) const { return (n >> BLOCK_POINTER_BITS) + ((n % BLOCK_SIZE == 0) ? 0 : 1); }
|
||||
inline size_t block_index(size_t i) const { return i >> BLOCK_POINTER_BITS; }
|
||||
inline size_t item_index(size_t i) const { return i & BLOCK_SIZE_MASK; }
|
||||
|
||||
void shrink_blocks(size_t newBlockCount)
|
||||
{
|
||||
// If the size is smaller, delete blocks that are no longer needed.
|
||||
for (size_t i = newBlockCount; i < mData.size(); i++)
|
||||
delete mData[i];
|
||||
}
|
||||
void init_blocks(size_t oldSize = 0)
|
||||
{
|
||||
for (size_t i = oldSize; i < mData.size(); i++)
|
||||
mData[i] = new block(BLOCK_SIZE);
|
||||
}
|
||||
void init_blocks(const T& value, size_t oldSize)
|
||||
{
|
||||
for (size_t i = oldSize; i < mData.size(); i++)
|
||||
mData[i] = new block(BLOCK_SIZE, value);
|
||||
}
|
||||
public:
|
||||
typedef IndexIterator<const BlockVector<T>, const T> const_iterator;
|
||||
typedef IndexIterator<BlockVector<T>, T> iterator;
|
||||
|
||||
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
|
||||
typedef std::reverse_iterator<iterator> reverse_iterator;
|
||||
|
||||
BlockVector() : mData(std::vector<std::array<T, BLOCK_SIZE>*>()), mSize(0) {}
|
||||
BlockVector(size_t capacity) :
|
||||
mData(std::vector<block*>()),
|
||||
mSize(0)
|
||||
{
|
||||
reserve(capacity);
|
||||
}
|
||||
BlockVector(const BlockVector& r)
|
||||
{
|
||||
mData = std::vector<block*>(r.mData.size());
|
||||
init(0);
|
||||
for (size_t block = 0; block < r.mData.size(); block++)
|
||||
std::copy(r.mData[block]->begin(), r.mData[block]->end(), mData[block]->begin());
|
||||
mSize = r.mSize;
|
||||
}
|
||||
BlockVector(BlockVector&& r)
|
||||
{
|
||||
mData = std::move(r.mData); r.mData.clear();
|
||||
mSize = std::move(r.mSize); r.mSize = 0;
|
||||
}
|
||||
|
||||
virtual ~BlockVector()
|
||||
{
|
||||
for (block* arr : mData)
|
||||
{
|
||||
arr->clear();
|
||||
delete arr;
|
||||
}
|
||||
mData.clear();
|
||||
}
|
||||
|
||||
// Capacity
|
||||
size_t size() const { return mSize; }
|
||||
size_t max_size() const { return mData.max_size() * mSize; }
|
||||
|
||||
void reserve(size_t n)
|
||||
{
|
||||
size_t newBlockCount = block_count(n);
|
||||
if (mData.size() >= newBlockCount) return; // big enough
|
||||
size_t oldSize = mData.size();
|
||||
mData.resize(newBlockCount);
|
||||
for (size_t i = oldSize; i < mData.size(); i++)
|
||||
{
|
||||
mData[i] = new block();
|
||||
mData[i]->reserve(BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
// Shrinks the vector so it contains n entries.
|
||||
void shrink(size_t n)
|
||||
{
|
||||
size_t newBlockCount = block_count(n);
|
||||
shrink_blocks(newBlockCount);
|
||||
mData.resize(newBlockCount);
|
||||
mSize = n;
|
||||
}
|
||||
|
||||
void resize(size_t n)
|
||||
{
|
||||
size_t oldBlockCount = mData.size();
|
||||
size_t newBlockCount = block_count(n);
|
||||
shrink_blocks(newBlockCount);
|
||||
mData.resize(newBlockCount, NULL);
|
||||
init_blocks(oldBlockCount);
|
||||
mSize = n;
|
||||
}
|
||||
void resize(size_t n, const T& val)
|
||||
{
|
||||
size_t oldBlockCount = mData.size();
|
||||
size_t newBlockCount = block_count(n);
|
||||
shrink_blocks(newBlockCount);
|
||||
mData.resize(newBlockCount, NULL);
|
||||
init_blocks(val, oldBlockCount);
|
||||
mSize = n;
|
||||
}
|
||||
|
||||
inline size_t capacity() const { return mData.size() * BLOCK_SIZE; }
|
||||
inline bool empty() const { return mData.empty(); }
|
||||
void shrink_to_fit() { resize(mSize); }
|
||||
|
||||
// Element access
|
||||
const T& at(size_t i) const { assert(i < mSize); return mData[block_index(i)]->at(item_index(i)); }
|
||||
T& at(size_t i) { assert(i < mSize); return mData[block_index(i)]->at(item_index(i)); }
|
||||
const T& operator[](size_t i) const { return at(i); }
|
||||
T& operator[](size_t i) { return at(i); }
|
||||
const T& front() const { return at(0); }
|
||||
T& front() { return at(0); }
|
||||
const T& back() const { return at(mSize - 1); }
|
||||
T& back() { return at(mSize - 1); }
|
||||
|
||||
// Modifiers
|
||||
void push_back(const T& val)
|
||||
{
|
||||
// Increase the size if it doesn't fit
|
||||
if (block_index(mSize) >= mData.size()) reserve(mSize + 1);
|
||||
// Put the item in the last position
|
||||
block* b = mData[block_index(mSize)];
|
||||
size_t itemIndex = item_index(mSize);
|
||||
if (b->size() == itemIndex) b->push_back(val);
|
||||
else mData->at(itemIndex) = val;
|
||||
mSize++;
|
||||
}
|
||||
void push_back(T&& val)
|
||||
{
|
||||
// Increase the size if it doesn't fit
|
||||
if (block_index(mSize) >= mData.size()) reserve(mSize + 1);
|
||||
// Put the item in the last position
|
||||
block* b = mData[block_index(mSize)];
|
||||
size_t itemIndex = item_index(mSize);
|
||||
if (b->size() == itemIndex) b->push_back(val);
|
||||
else b->at(itemIndex) = val;
|
||||
mSize++;
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
void emplace_back(Args&&... args)
|
||||
{
|
||||
size_t blockIndex = block_index(mSize);
|
||||
if (blockIndex >= mData.size()) reserve(mSize + 1);
|
||||
block* b = mData[blockIndex];
|
||||
size_t itemIndex = item_index(mSize);
|
||||
if (b->size() == itemIndex) b->emplace_back(std::forward<Args>(args)...);
|
||||
else b->at(itemIndex) = T(std::forward<Args>(args)...);
|
||||
mSize++;
|
||||
}
|
||||
|
||||
void pop_back() {
|
||||
mSize--;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
for (block* block : mData)
|
||||
delete block;
|
||||
mData.clear();
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
// Iterators
|
||||
const_iterator begin() const
|
||||
{
|
||||
return const_iterator(this, 0);
|
||||
}
|
||||
|
||||
const_iterator end() const
|
||||
{
|
||||
return const_iterator(this, mSize);
|
||||
}
|
||||
|
||||
const_reverse_iterator rbegin() const
|
||||
{
|
||||
return const_reverse_iterator(end());
|
||||
}
|
||||
|
||||
const_reverse_iterator rend() const
|
||||
{
|
||||
return const_reverse_iterator(begin());
|
||||
}
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
return iterator(this, 0);
|
||||
}
|
||||
|
||||
iterator end()
|
||||
{
|
||||
return iterator(this, mSize);
|
||||
}
|
||||
|
||||
reverse_iterator rbegin()
|
||||
{
|
||||
return reverse_iterator(end());
|
||||
}
|
||||
|
||||
reverse_iterator rend()
|
||||
{
|
||||
return reverse_iterator(begin());
|
||||
}
|
||||
|
||||
};
|
||||
101
Research/core/Util/BoolArray.cpp
Normal file
101
Research/core/Util/BoolArray.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#include "BoolArray.h"
|
||||
#include "../BitHelper.h"
|
||||
#include <assert.h>
|
||||
|
||||
BoolArray::BoolArray(size_t size, bool value) :
|
||||
mData(std::vector<unsigned8>(BitHelper::RoundToBytes(size) / 8, value ? 255 : 0)),
|
||||
mSize(size)
|
||||
{}
|
||||
|
||||
BoolArray::~BoolArray() {}
|
||||
|
||||
void BoolArray::Set(const size_t& i, bool value)
|
||||
{
|
||||
if (i > mSize) Resize(i + 1);
|
||||
size_t byteIndex = GetByteIndex(i);
|
||||
unsigned8 bitIndex = GetBitIndex(i);
|
||||
unsigned8 bitMask = BitHelper::GetHSSingleBitMask<unsigned8>(bitIndex);
|
||||
mData[byteIndex] &= ~bitMask;
|
||||
if (value) mData[byteIndex] |= bitMask;
|
||||
}
|
||||
|
||||
void BoolArray::SetRange(const size_t& start, const size_t& end, bool value)
|
||||
{
|
||||
if (end < start) return;
|
||||
if (end > mSize) Resize(end);
|
||||
|
||||
size_t startByteIndex = GetByteIndex(start);
|
||||
size_t endByteIndex = GetByteIndex(end);
|
||||
|
||||
unsigned8 bitMask = BitHelper::GetHSMask<unsigned8>(GetBitIndex(start), 8);
|
||||
if (startByteIndex == endByteIndex)
|
||||
bitMask = BitHelper::GetHSMask<unsigned8>(GetBitIndex(start), GetBitIndex(end));
|
||||
// Set the bits in the first block:
|
||||
mData[startByteIndex] &= ~bitMask;
|
||||
if (value) mData[startByteIndex] |= bitMask;
|
||||
if (startByteIndex == endByteIndex) return; // We're done if only one byte is affected
|
||||
|
||||
// Set the bits in all bytes between the start and the end bytes
|
||||
unsigned8 allSetValue = value ? 0xFF : 0x00;
|
||||
for (size_t byte = startByteIndex + 1; byte < endByteIndex; byte++)
|
||||
mData[byte] = allSetValue;
|
||||
|
||||
// Set the required bits in the end byte
|
||||
if (end % 8 != 0)
|
||||
{
|
||||
bitMask = BitHelper::GetHSMask<unsigned8>(0, GetBitIndex(end));
|
||||
mData[endByteIndex] &= ~bitMask;
|
||||
if (value) mData[endByteIndex] |= bitMask;
|
||||
}
|
||||
}
|
||||
|
||||
bool BoolArray::Any(const size_t& start, size_t end) const
|
||||
{
|
||||
if (end >= mSize) end = mSize - 1;
|
||||
if (end <= start) return false;
|
||||
|
||||
size_t startByteIndex = GetByteIndex(start);
|
||||
size_t endByteIndex = GetByteIndex(end);
|
||||
|
||||
if (startByteIndex == endByteIndex)
|
||||
return (BitHelper::GetHSMask<unsigned8>(GetBitIndex(start), GetBitIndex(end)) & mData[startByteIndex]) != 0;
|
||||
|
||||
// Check the first block
|
||||
unsigned8 bitMask = BitHelper::GetHSMask<unsigned8>(GetBitIndex(start), 8);
|
||||
if ((mData[startByteIndex] & bitMask) != 0) return true;
|
||||
|
||||
// Check the bytes inbetween
|
||||
for (size_t byte = startByteIndex + 1; byte < endByteIndex; byte++)
|
||||
if ((mData[byte] & 0xFF) != 0) return true;
|
||||
|
||||
// Check the last bits
|
||||
return (mData[endByteIndex] & BitHelper::GetHSMask<unsigned8>(0, GetBitIndex(end))) != 0;
|
||||
}
|
||||
|
||||
bool BoolArray::Get(const size_t& i) const
|
||||
{
|
||||
assert(i < mSize);
|
||||
return BitHelper::GetHS(mData[GetByteIndex(i)], GetBitIndex(i));
|
||||
}
|
||||
void BoolArray::Resize(const size_t& size)
|
||||
{
|
||||
mSize = size;
|
||||
mData.resize(BitHelper::RoundToBytes(size) / 8);
|
||||
}
|
||||
|
||||
void BoolArray::Clear()
|
||||
{
|
||||
mSize = 0;
|
||||
mData.clear();
|
||||
mData.shrink_to_fit();
|
||||
}
|
||||
|
||||
void BoolArray::ShrinkToFit()
|
||||
{
|
||||
mData.shrink_to_fit();
|
||||
}
|
||||
|
||||
bool BoolArray::operator[](const size_t& i) const
|
||||
{
|
||||
return Get(i);
|
||||
}
|
||||
31
Research/core/Util/BoolArray.h
Normal file
31
Research/core/Util/BoolArray.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include "../Defines.h"
|
||||
#include <vector>
|
||||
#include <stddef.h>
|
||||
|
||||
class BoolArray
|
||||
{
|
||||
public:
|
||||
BoolArray(size_t size, bool value = false);
|
||||
BoolArray() : BoolArray(0) {}
|
||||
~BoolArray();
|
||||
|
||||
void Set(const size_t& i, bool value);
|
||||
bool Get(const size_t& i) const;
|
||||
void SetRange(const size_t& start, const size_t& end, bool value);
|
||||
bool Any(const size_t& start = 0, size_t end = ~size_t(0)) const;
|
||||
size_t Size() const { return mSize; }
|
||||
bool Empty() const { return mSize == 0; }
|
||||
void Resize(const size_t& size);
|
||||
void Clear();
|
||||
void ShrinkToFit();
|
||||
|
||||
bool operator[](const size_t& i) const;
|
||||
private:
|
||||
static inline size_t GetByteIndex(const size_t& i) { return i >> 3; }
|
||||
static inline unsigned8 GetBitIndex(const size_t& i) { return i & 0x7; }
|
||||
|
||||
size_t mSize;
|
||||
std::vector<unsigned8> mData;
|
||||
};
|
||||
|
||||
187
Research/core/Util/IndexIterator.h
Normal file
187
Research/core/Util/IndexIterator.h
Normal file
@@ -0,0 +1,187 @@
|
||||
#pragma once
|
||||
#include <iterator>
|
||||
|
||||
template<typename ContainerT, typename T>
|
||||
class IndexIterator : public std::iterator<std::random_access_iterator_tag, T>
|
||||
{
|
||||
protected:
|
||||
size_t mIndex;
|
||||
ContainerT* mContainer;
|
||||
public:
|
||||
typedef std::random_access_iterator_tag iterator_category;
|
||||
typedef
|
||||
typename iterator<std::random_access_iterator_tag, T>::value_type
|
||||
value_type;
|
||||
typedef
|
||||
typename iterator<std::random_access_iterator_tag, T>::difference_type
|
||||
difference_type;
|
||||
typedef
|
||||
typename iterator<std::random_access_iterator_tag, T>::reference
|
||||
reference;
|
||||
typedef
|
||||
typename iterator<std::random_access_iterator_tag, T>::pointer
|
||||
pointer;
|
||||
|
||||
|
||||
IndexIterator() : mContainer(NULL), mIndex(0) {}
|
||||
IndexIterator(ContainerT* container) : mContainer(container), mIndex(0) {}
|
||||
IndexIterator(ContainerT* container, size_t index) : mContainer(container), mIndex(index) {}
|
||||
IndexIterator(const IndexIterator<ContainerT, T>& r) : mContainer(r.mContainer), mIndex(r.mIndex) {}
|
||||
|
||||
IndexIterator& operator=(const IndexIterator& r)
|
||||
{
|
||||
mIndex = r.mIndex; mContainer = r.mContainer; return *this;
|
||||
}
|
||||
|
||||
IndexIterator& operator++() // PREFIX
|
||||
{
|
||||
++mIndex; return *this;
|
||||
}
|
||||
|
||||
IndexIterator& operator--() // PREFIX
|
||||
{
|
||||
--mIndex; return *this;
|
||||
}
|
||||
|
||||
IndexIterator operator++(int) // POSTFIX
|
||||
{
|
||||
return IndexIterator(mContainer, mIndex++);
|
||||
}
|
||||
|
||||
IndexIterator operator--(int) // POSTFIX
|
||||
{
|
||||
return IndexIterator(mContainer, mIndex--);
|
||||
}
|
||||
|
||||
IndexIterator operator+(const difference_type& n) const
|
||||
{
|
||||
return IndexIterator(mContainer, mIndex + n);
|
||||
}
|
||||
|
||||
IndexIterator& operator+=(const difference_type& n)
|
||||
{
|
||||
mIndex += n; return *this;
|
||||
}
|
||||
|
||||
IndexIterator operator-(const difference_type& n) const
|
||||
{
|
||||
return IndexIterator(mContainer, mIndex - n);
|
||||
}
|
||||
|
||||
IndexIterator& operator-=(const difference_type& n)
|
||||
{
|
||||
mIndex -= n; return *this;
|
||||
}
|
||||
|
||||
reference operator*() const
|
||||
{
|
||||
return mContainer->operator[](mIndex);
|
||||
}
|
||||
|
||||
pointer operator->() const
|
||||
{
|
||||
return &mContainer->operator[](mIndex);
|
||||
}
|
||||
|
||||
reference operator[](const difference_type& n) const
|
||||
{
|
||||
return mContainer->operator[](mIndex + n);
|
||||
}
|
||||
|
||||
template<typename ContainerTypeT, typename TypeT>
|
||||
friend bool operator==(
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r1,
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r2);
|
||||
|
||||
template<typename ContainerTypeT, typename TypeT>
|
||||
friend bool operator!=(
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r1,
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r2);
|
||||
|
||||
template<typename ContainerTypeT, typename TypeT>
|
||||
friend bool operator<(
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r1,
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r2);
|
||||
|
||||
template<typename ContainerTypeT, typename TypeT>
|
||||
friend bool operator>(
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r1,
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r2);
|
||||
|
||||
template<typename ContainerTypeT, typename TypeT>
|
||||
friend bool operator<=(
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r1,
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r2);
|
||||
|
||||
template<typename ContainerTypeT, typename TypeT>
|
||||
friend bool operator>=(
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r1,
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r2);
|
||||
|
||||
template<typename ContainerTypeT, typename TypeT>
|
||||
friend typename IndexIterator<ContainerTypeT, TypeT>::difference_type operator+(
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r1,
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r2);
|
||||
|
||||
template<typename ContainerTypeT, typename TypeT>
|
||||
friend typename IndexIterator<ContainerTypeT, TypeT>::difference_type operator-(
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r1,
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r2);
|
||||
};
|
||||
|
||||
template<typename ContainerT, typename T>
|
||||
bool operator==(const IndexIterator<ContainerT, T>& r1, const IndexIterator<ContainerT, T>& r2)
|
||||
{
|
||||
return (r1.mIndex == r2.mIndex && r1.mContainer == r2.mContainer);
|
||||
}
|
||||
|
||||
template<typename ContainerT, typename T>
|
||||
bool operator!=(const IndexIterator<ContainerT, T>& r1, const IndexIterator<ContainerT, T>& r2)
|
||||
{
|
||||
return ((r1.mIndex != r2.mIndex) || (r1.mContainer != r2.mContainer));
|
||||
}
|
||||
|
||||
template<typename ContainerT, typename T>
|
||||
bool operator<(const IndexIterator<ContainerT, T>& r1, const IndexIterator<ContainerT, T>& r2)
|
||||
{
|
||||
if (r1.mContainer != r2.mContainer) return r1.mContainer < r2.mContainer;
|
||||
return (r1.mIndex < r2.mIndex);
|
||||
}
|
||||
|
||||
template<typename ContainerT, typename T>
|
||||
bool operator>(const IndexIterator<ContainerT, T>& r1, const IndexIterator<ContainerT, T>& r2)
|
||||
{
|
||||
if (r1.mContainer != r2.mContainer) return r1.mContainer > r2.mContainer;
|
||||
return (r1.mIndex > r2.mIndex);
|
||||
}
|
||||
|
||||
template<typename ContainerT, typename T>
|
||||
bool operator<=(const IndexIterator<ContainerT, T>& r1, const IndexIterator<ContainerT, T>& r2)
|
||||
{
|
||||
if (r1.mContainer != r2.mContainer) return r1.mContainer < r2.mContainer;
|
||||
return (r1.mIndex <= r2.mIndex);
|
||||
}
|
||||
|
||||
template<typename ContainerT, typename T>
|
||||
bool operator>=(const IndexIterator<ContainerT, T>& r1, const IndexIterator<ContainerT, T>& r2)
|
||||
{
|
||||
if (r1.mContainer != r2.mContainer) return r1.mContainer > r2.mContainer;
|
||||
return (r1.mIndex >= r2.mIndex);
|
||||
}
|
||||
|
||||
template<typename ContainerT, typename T>
|
||||
typename IndexIterator<ContainerT, T>::difference_type operator+(
|
||||
const IndexIterator<ContainerT, T>& r1,
|
||||
const IndexIterator<ContainerT, T>& r2)
|
||||
{
|
||||
assert(r1.mContainer == r2.mContainer);
|
||||
return r1.mIndex + r2.mIndex;
|
||||
}
|
||||
|
||||
template<typename ContainerT, typename T>
|
||||
typename IndexIterator<ContainerT, T>::difference_type operator-(
|
||||
const IndexIterator<ContainerT, T>& r1, const IndexIterator<ContainerT, T>& r2)
|
||||
{
|
||||
assert(r1.mContainer == r2.mContainer);
|
||||
return r1.mIndex - r2.mIndex;
|
||||
}
|
||||
131
Research/core/Util/ObjectPool.h
Normal file
131
Research/core/Util/ObjectPool.h
Normal file
@@ -0,0 +1,131 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <algorithm>
|
||||
|
||||
#include "../../inc/tbb/parallel_sort.h"
|
||||
#include "BoolArray.h"
|
||||
#include "BlockVector.h"
|
||||
#include "../CollectionHelper.h"
|
||||
|
||||
#define USE_BLOCK_VECTOR
|
||||
|
||||
// Class for memory efficiency: stores all objects of some type in a vector. Pointers to the object can then be requested.
|
||||
template<typename T>
|
||||
class ObjectPool
|
||||
{
|
||||
private:
|
||||
#ifdef USE_BLOCK_VECTOR
|
||||
BlockVector<T> mData;
|
||||
#else
|
||||
std::vector<T> mData;
|
||||
#endif
|
||||
BoolArray mMarkedForDeletion;
|
||||
const size_t mBaseCapacity = 0;
|
||||
|
||||
bool AnyMarkedForDeletion() const { return !mMarkedForDeletion.Empty(); }
|
||||
bool MarkedForDeletion(size_t index) const { return index < mMarkedForDeletion.Size() && mMarkedForDeletion[index]; }
|
||||
void MarkForDeletion(size_t index)
|
||||
{
|
||||
if (index >= mMarkedForDeletion.Size())
|
||||
mMarkedForDeletion.Resize(index + 1);
|
||||
mMarkedForDeletion.Set(index, true);
|
||||
}
|
||||
public:
|
||||
ObjectPool(size_t initialCapacity = 1) :
|
||||
#ifdef USE_BLOCK_VECTOR
|
||||
mData(BlockVector<T>(initialCapacity)),
|
||||
#else
|
||||
mData(std::vector<T>()),
|
||||
#endif
|
||||
mMarkedForDeletion(BoolArray()),
|
||||
mBaseCapacity(initialCapacity)
|
||||
{
|
||||
#ifndef USE_BLOCK_VECTOR
|
||||
mData.reserve(initialCapacity);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Adds a new item to the pool.
|
||||
template <class... Args>
|
||||
T* Create(Args&&... args)
|
||||
{
|
||||
//// If any nodes are marked for deletion, reuse their position
|
||||
//if (!mMarkedForDeletion.empty())
|
||||
//{
|
||||
// T* res = &mData[mMarkedForDeletion.top()];
|
||||
// mMarkedForDeletion.pop();
|
||||
// return res;
|
||||
//}
|
||||
//else // Else, resize the data array to create some space for this new node
|
||||
//{
|
||||
mData.emplace_back(std::forward<Args>(args)...);
|
||||
return &mData[mData.size() - 1];
|
||||
//}
|
||||
}
|
||||
|
||||
// Moves the node from the given memory position into this pool. Returns the new pointer to this node
|
||||
T* Add(T* value)
|
||||
{
|
||||
mData.emplace_back(std::move(*value));
|
||||
return &mData[mData.size() - 1];
|
||||
}
|
||||
|
||||
void Delete(size_t i) { MarkForDeletion(i); }
|
||||
|
||||
// Removes all nodes that are marked for deletion.
|
||||
void Clean()
|
||||
{
|
||||
// There is only stuff to clean if there are nodes marked for deletion
|
||||
if (!AnyMarkedForDeletion()) return;
|
||||
|
||||
size_t newIdx = 0;
|
||||
for (size_t oldIdx = 0; oldIdx < mData.size(); oldIdx++)
|
||||
// If this node should not be deleted, move it to the new position.
|
||||
if (!MarkedForDeletion(oldIdx))
|
||||
{
|
||||
if (oldIdx != newIdx)
|
||||
mData[newIdx] = std::move(mData[oldIdx]);
|
||||
//std::swap(mData[newIdx], mData[oldIdx]);
|
||||
newIdx++;
|
||||
}
|
||||
#ifdef USE_BLOCK_VECTOR
|
||||
mData.shrink(newIdx);
|
||||
mData.reserve(mBaseCapacity);
|
||||
#else
|
||||
mData.erase(mData.begin() + newIdx, mData.end());
|
||||
mData.shrink_to_fit();
|
||||
mData.reserve(mBaseCapacity);
|
||||
#endif
|
||||
mMarkedForDeletion = BoolArray();
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
mData.clear();
|
||||
mMarkedForDeletion.Clear();
|
||||
}
|
||||
|
||||
template<typename Comparer = std::less<T>>
|
||||
void Sort(size_t startIdx, size_t endIdx, const Comparer& comparer = Comparer())
|
||||
{
|
||||
// Check if any node are deleted in the range that needs to be sorted:
|
||||
if (AnyMarkedForDeletion() && mMarkedForDeletion.Any(startIdx, endIdx))
|
||||
Clean();
|
||||
tbb::parallel_sort(mData.begin() + startIdx, mData.begin() + endIdx, comparer);
|
||||
}
|
||||
|
||||
inline size_t Size() const { return mData.size(); }
|
||||
|
||||
inline const T* operator[](size_t i) const
|
||||
{
|
||||
if (MarkedForDeletion(i)) return NULL;
|
||||
return &mData.at(i);
|
||||
}
|
||||
|
||||
inline T* operator[](size_t i)
|
||||
{
|
||||
if (MarkedForDeletion(i)) return NULL;
|
||||
return &mData.at(i);
|
||||
}
|
||||
};
|
||||
119
Research/core/Util/Path.h
Normal file
119
Research/core/Util/Path.h
Normal file
@@ -0,0 +1,119 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include "../Defines.h"
|
||||
#include "../Serializer.h"
|
||||
|
||||
// Path stores a recording detailing any type.
|
||||
// Interpolator should be a functor with a method specification:
|
||||
// T operator()(const T& a, const T& b, double time)
|
||||
// Where a and b should be interpolated between, and time is a value between 0 and 1, indicating the interpolation value
|
||||
template<typename T, typename Interpolator>
|
||||
class Path
|
||||
{
|
||||
private:
|
||||
std::vector<T> mFrames;
|
||||
std::vector<double> mFrameTimes;
|
||||
|
||||
public:
|
||||
Path() : mFrames(std::vector<T>()), mFrameTimes(std::vector<double>()) {}
|
||||
~Path() {}
|
||||
|
||||
void SetStateAtTime(const double& time, const T& state)
|
||||
{
|
||||
// Find the position of this new state:
|
||||
auto pos = std::lower_bound(mFrameTimes.begin(), mFrameTimes.end(), time);
|
||||
size_t idx = pos - mFrameTimes.begin();
|
||||
mFrames.insert(mFrames.begin() + idx, state);
|
||||
mFrameTimes.insert(pos, time);
|
||||
}
|
||||
|
||||
T GetStateAtTime(const double& time) const
|
||||
{
|
||||
// Find the frames between which the requested time takes place
|
||||
size_t afterIdx = std::upper_bound(mFrameTimes.begin(), mFrameTimes.end(), time) - mFrameTimes.begin();
|
||||
size_t beforeIdx = afterIdx - 1;
|
||||
if (afterIdx == mFrameTimes.size()) // End of the recording, return the last camera state
|
||||
return mFrames.back();
|
||||
if (afterIdx == 0)
|
||||
return mFrames.front();
|
||||
|
||||
// Now linearly interpolate the frames:
|
||||
double t = ((time - mFrameTimes[beforeIdx]) / (mFrameTimes[afterIdx] - mFrameTimes[beforeIdx]));
|
||||
|
||||
const T& before = mFrames[beforeIdx];
|
||||
const T& after = mFrames[afterIdx];
|
||||
|
||||
return Interpolator()(before, after, t);
|
||||
}
|
||||
|
||||
double GetLength() const {
|
||||
if (Empty()) return 0;
|
||||
return mFrameTimes.back() - mFrameTimes.front();
|
||||
}
|
||||
|
||||
size_t Size() const { return mFrames.size(); }
|
||||
|
||||
/// Removes the camera path node at index i
|
||||
void RemoveAt(size_t i)
|
||||
{
|
||||
mFrames.erase(mFrames.begin() + i);
|
||||
mFrameTimes.erase(mFrames.begin() + i);
|
||||
}
|
||||
|
||||
/// Removes the last stored camera path node
|
||||
void RemoveLast()
|
||||
{
|
||||
mFrames.pop_back();
|
||||
mFrameTimes.pop_back();
|
||||
}
|
||||
|
||||
// Makes sure the frametimes go from 0 to the length of the recording
|
||||
void Normalize()
|
||||
{
|
||||
if (Empty()) return;
|
||||
double offset = mFrameTimes[0];
|
||||
for (size_t i = 0; i < mFrameTimes.size(); i++)
|
||||
mFrameTimes[i] = mFrameTimes[i] - offset;
|
||||
}
|
||||
bool Empty() const { return mFrames.empty(); }
|
||||
void Clear()
|
||||
{
|
||||
mFrames.clear();
|
||||
mFrameTimes.clear();
|
||||
}
|
||||
|
||||
void WriteToFile(const std::string& filename)
|
||||
{
|
||||
std::ofstream file(filename, std::ios::binary);
|
||||
unsigned32 recordingLength = (unsigned32)mFrames.size();
|
||||
Serializer<unsigned32>::Serialize(recordingLength, file);
|
||||
if (recordingLength > 0)
|
||||
{
|
||||
Serializer<T*>::Serialize(&mFrames[0], mFrames.size(), file);
|
||||
Serializer<double*>::Serialize(&mFrameTimes[0], mFrames.size(), file);
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
bool ReadFromFile(const std::string& filename)
|
||||
{
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
if (file.good()) {
|
||||
unsigned32 recordingLength = 0;
|
||||
Serializer<unsigned32>::Deserialize(recordingLength, file);
|
||||
mFrames.resize(recordingLength);
|
||||
mFrameTimes.resize(recordingLength);
|
||||
if (recordingLength > 0)
|
||||
{
|
||||
Serializer<T*>::Deserialize(&mFrames[0], mFrames.size(), file);
|
||||
Serializer<double*>::Deserialize(&mFrameTimes[0], mFrames.size(), file);
|
||||
}
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
64
Research/core/Util/SmallDynamicArray.h
Normal file
64
Research/core/Util/SmallDynamicArray.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#pragma warning(disable:4996)
|
||||
|
||||
#include "../Defines.h"
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
// Wrapper for standard C array that attempts to minimize memory usage. The user of this class should keep track of the size of the array to correctly be able to insert etc.
|
||||
template<typename T>
|
||||
class SmallDynamicArray
|
||||
{
|
||||
public:
|
||||
SmallDynamicArray() : mData(NULL) {}
|
||||
SmallDynamicArray(size_t size) : mData(new T[size]) {}
|
||||
SmallDynamicArray(SmallDynamicArray&& source) // Move ctor
|
||||
{
|
||||
mData = source.mData;
|
||||
source.mData = NULL;
|
||||
}
|
||||
~SmallDynamicArray() { if (mData != NULL) delete[] mData; }
|
||||
|
||||
SmallDynamicArray& operator=(SmallDynamicArray&& source) // Move assignment operator
|
||||
{
|
||||
mData = source.mData;
|
||||
source.mData = NULL;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void Set(const size_t& i, T value) { mData[i] = value; }
|
||||
inline T& Get(const size_t& i) const { return mData[i]; }
|
||||
inline T& operator[](const size_t& i) const { return mData[i]; }
|
||||
inline void SetRange(const T* source, size_t begin, size_t end) { std::copy(source + begin, source + end, mData); }
|
||||
void Clear() { delete[] mData; mData = NULL; }
|
||||
inline bool IsEmpty() const { return mData == NULL; }
|
||||
|
||||
void Resize(size_t oldSize, const size_t& newSize)
|
||||
{
|
||||
T* newData = new T[newSize];
|
||||
if (!IsEmpty())
|
||||
{
|
||||
std::copy(mData, mData + std::min(oldSize, newSize), newData);
|
||||
delete[] mData;
|
||||
}
|
||||
mData = newData;
|
||||
}
|
||||
|
||||
void Insert(size_t index, const T& value, size_t curSize)
|
||||
{
|
||||
unsigned* newData = new unsigned[curSize + 1];
|
||||
// Move the data over to a new array with the correct size
|
||||
if (mData != NULL)
|
||||
{
|
||||
std::copy(mData, mData + index, newData);
|
||||
std::copy(mData + index, mData + curSize, newData + index + 1);
|
||||
delete[] mData;
|
||||
}
|
||||
newData[index] = value;
|
||||
mData = newData;
|
||||
}
|
||||
|
||||
private:
|
||||
T* mData;
|
||||
};
|
||||
|
||||
20
Research/core/Util/Stopwatch.cpp
Normal file
20
Research/core/Util/Stopwatch.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "Stopwatch.h"
|
||||
|
||||
Stopwatch::Stopwatch()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
Stopwatch::~Stopwatch()
|
||||
{
|
||||
}
|
||||
|
||||
void Stopwatch::Reset()
|
||||
{
|
||||
begin = clock();
|
||||
}
|
||||
|
||||
double Stopwatch::GetTime()
|
||||
{
|
||||
return double(clock() - begin) / CLOCKS_PER_SEC;
|
||||
}
|
||||
17
Research/core/Util/Stopwatch.h
Normal file
17
Research/core/Util/Stopwatch.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <ctime>
|
||||
|
||||
class Stopwatch
|
||||
{
|
||||
public:
|
||||
Stopwatch();
|
||||
~Stopwatch();
|
||||
|
||||
void Reset();
|
||||
// Returns the time in seconds
|
||||
double GetTime();
|
||||
|
||||
private:
|
||||
clock_t begin;
|
||||
};
|
||||
|
||||
71
Research/core/Util/TransferFunction.h
Normal file
71
Research/core/Util/TransferFunction.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
#include "../Defines.h"
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include "../MathHelper.h"
|
||||
|
||||
template<typename T>
|
||||
class TransferFunction
|
||||
{
|
||||
public:
|
||||
struct Node
|
||||
{
|
||||
unsigned32 value;
|
||||
float opacity;
|
||||
T material;
|
||||
|
||||
Node(unsigned32 value, float opacity, T mat) : value(value), opacity(opacity), material(mat) {}
|
||||
Node(unsigned32 value) : value(value), opacity(0), material(T()) {}
|
||||
};
|
||||
|
||||
struct NodeCompare { bool operator()(const Node& a, const Node& b) { return a.value < b.value; } };
|
||||
|
||||
TransferFunction()
|
||||
{
|
||||
}
|
||||
|
||||
~TransferFunction() { mFunction.clear(); }
|
||||
|
||||
void AddNode(unsigned32 value, float opacity, T mat)
|
||||
{
|
||||
Node node(value, opacity, mat);
|
||||
auto pos = LowerBound(node);
|
||||
if (pos != mFunction.end() && pos->value == value)
|
||||
mFunction[pos - mFunction.begin()] = node;
|
||||
else
|
||||
mFunction.insert(pos, node);
|
||||
}
|
||||
|
||||
template<typename Interpolator>
|
||||
void Evaluate(unsigned32 value, float& outOpacity, T& outMat, Interpolator interpolator) const
|
||||
{
|
||||
Node node(value);
|
||||
// Set default values
|
||||
outOpacity = 1.f;
|
||||
outMat = T();
|
||||
// Find the current node
|
||||
auto pos = LowerBound(node);
|
||||
if (pos == mFunction.end()) return;
|
||||
|
||||
Node after = *pos;
|
||||
if (pos == mFunction.begin())
|
||||
{
|
||||
outOpacity = after.opacity;
|
||||
outMat = after.material;
|
||||
return;
|
||||
}
|
||||
pos--;
|
||||
Node before = *pos;
|
||||
float t = float(value - before.value) / float(after.value - before.value);
|
||||
outMat = interpolator(before.material, after.material, t);
|
||||
outOpacity = MathHelper::lerp(t, before.opacity, after.opacity);
|
||||
}
|
||||
private:
|
||||
typename std::vector<Node>::const_iterator LowerBound(const Node& node) const
|
||||
{
|
||||
return std::lower_bound(mFunction.begin(), mFunction.end(), node, NodeCompare());
|
||||
}
|
||||
|
||||
std::vector<Node> mFunction;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user