osb/source/core/StarSectorArray2D.hpp
2024-02-25 15:46:47 +01:00

376 lines
13 KiB
C++

#pragma once
#include "StarMultiArray.hpp"
#include "StarSet.hpp"
#include "StarVector.hpp"
namespace Star {
// Holds a sparse 2d array of data based on sector size. Meant to be used as a
// fast-as-possible sparse array. Memory requiremenets are equal to the size
// of all loaded sectors PLUS pointer size * sectors wide * sectors high
template <typename ElementT, size_t SectorSize>
class SectorArray2D {
public:
typedef ElementT Element;
typedef Vec2S Sector;
struct SectorRange {
// Lower left sector
Vec2S min;
// Upper right sector *non-inclusive*
Vec2S max;
};
struct Array {
Array();
Array(Element const& def);
Element const& operator()(size_t x, size_t y) const;
Element& operator()(size_t x, size_t y);
Element elements[SectorSize * SectorSize];
};
typedef unique_ptr<Array> ArrayPtr;
typedef MultiArray<Element, 2> DynamicArray;
SectorArray2D();
SectorArray2D(size_t numSectorsWide, size_t numSectorsHigh);
void init(size_t numSectorsWide, size_t numSectorsHigh);
// Total size of array elements
size_t width() const;
size_t height() const;
// Is sector within width() and heigh()
bool sectorValid(Sector const& sector) const;
// Returns the sector that contains the given point
Sector sectorFor(size_t x, size_t y) const;
// Returns the sector range that contains the given rectangle
SectorRange sectorRange(size_t minX, size_t minY, size_t width, size_t height) const;
Vec2S sectorCorner(Sector const& id) const;
bool hasSector(Sector const& id) const;
List<Sector> loadedSectors() const;
size_t loadedSectorCount() const;
bool sectorLoaded(Sector const& id) const;
// Will return nullptr if sector is not loaded.
Array* sector(Sector const& id);
Array const* sector(Sector const& id) const;
void loadSector(Sector const& id, ArrayPtr array);
ArrayPtr copySector(Sector const& id);
ArrayPtr takeSector(Sector const& id);
void discardSector(Sector const& id);
// Will return nullptr if sector is not loaded.
Element const* get(size_t x, size_t y) const;
Element* get(size_t x, size_t y);
// Fast evaluate of elements in the given range. If evalEmpty is true, then
// function will be called even for unloaded sectors (with null pointer).
// Function is called as function(size_t x, size_t y, Element* element).
// Given function should return true to continue, false to stop. Returns
// false if any evaled functions return false.
template <typename Function>
bool eval(size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty = false) const;
template <typename Function>
bool eval(size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty = false);
// Individual sectors are stored column-major, so for speed, use this method
// to get whole columns at a time. If eval empty is true, function will be
// called with for each empty column with the correct size information, but
// the pointer will be null. Function will be called as
// function(size_t x, size_t y, Element* columnElements, size_t columnSize)
// columnSize is guaranteed never to be greater than SectorSize. Given
// function should return true to continue, false to stop. Returns false if
// any evaled columns return false.
template <typename Function>
bool evalColumns(
size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty = false) const;
template <typename Function>
bool evalColumns(size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty = false);
private:
typedef MultiArray<ArrayPtr, 2> SectorArray;
template <typename Function>
bool evalPriv(size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty);
template <typename Function>
bool evalColumnsPriv(size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty);
SectorArray m_sectors;
HashSet<Sector> m_loadedSectors;
};
template <typename ElementT, size_t SectorSize>
SectorArray2D<ElementT, SectorSize>::Array::Array()
: elements() {}
template <typename ElementT, size_t SectorSize>
SectorArray2D<ElementT, SectorSize>::Array::Array(Element const& def) {
for (size_t i = 0; i < SectorSize * SectorSize; ++i)
elements[i] = def;
}
template <typename ElementT, size_t SectorSize>
ElementT const& SectorArray2D<ElementT, SectorSize>::Array::operator()(size_t x, size_t y) const {
starAssert(x < SectorSize && y < SectorSize);
return elements[x * SectorSize + y];
}
template <typename ElementT, size_t SectorSize>
ElementT& SectorArray2D<ElementT, SectorSize>::Array::operator()(size_t x, size_t y) {
starAssert(x < SectorSize && y < SectorSize);
return elements[x * SectorSize + y];
}
template <typename ElementT, size_t SectorSize>
SectorArray2D<ElementT, SectorSize>::SectorArray2D() {}
template <typename ElementT, size_t SectorSize>
SectorArray2D<ElementT, SectorSize>::SectorArray2D(size_t numSectorsWide, size_t numSectorsHigh) {
init(numSectorsWide, numSectorsHigh);
}
template <typename ElementT, size_t SectorSize>
void SectorArray2D<ElementT, SectorSize>::init(size_t numSectorsWide, size_t numSectorsHigh) {
m_sectors.clear();
m_sectors.setSize(numSectorsWide, numSectorsHigh);
m_loadedSectors.clear();
}
template <typename ElementT, size_t SectorSize>
size_t SectorArray2D<ElementT, SectorSize>::width() const {
return m_sectors.size(0) * SectorSize;
}
template <typename ElementT, size_t SectorSize>
size_t SectorArray2D<ElementT, SectorSize>::height() const {
return m_sectors.size(1) * SectorSize;
}
template <typename ElementT, size_t SectorSize>
bool SectorArray2D<ElementT, SectorSize>::sectorValid(Sector const& sector) const {
return sector[0] < m_sectors.size(0) && sector[1] < m_sectors.size(1);
}
template <typename ElementT, size_t SectorSize>
auto SectorArray2D<ElementT, SectorSize>::sectorFor(size_t x, size_t y) const -> Sector {
return {x / SectorSize, y / SectorSize};
}
template <typename ElementT, size_t SectorSize>
auto SectorArray2D<ElementT, SectorSize>::sectorRange(size_t minX, size_t minY, size_t width, size_t height) const -> SectorRange {
return {
{minX / SectorSize, minY / SectorSize},
{(minX + width + SectorSize - 1) / SectorSize, (minY + height + SectorSize - 1) / SectorSize}
};
}
template <typename ElementT, size_t SectorSize>
Vec2S SectorArray2D<ElementT, SectorSize>::sectorCorner(Sector const& id) const {
return Vec2S(id[0] * SectorSize, id[1] * SectorSize);
}
template <typename ElementT, size_t SectorSize>
bool SectorArray2D<ElementT, SectorSize>::hasSector(Sector const& id) const {
starAssert(id[0] < m_sectors.size(0) && id[1] < m_sectors.size(1));
return (bool)m_sectors(id[0], id[1]);
}
template <typename ElementT, size_t SectorSize>
auto SectorArray2D<ElementT, SectorSize>::loadedSectors() const -> List<Sector> {
return m_loadedSectors.values();
}
template <typename ElementT, size_t SectorSize>
size_t SectorArray2D<ElementT, SectorSize>::loadedSectorCount() const {
return m_loadedSectors.size();
}
template <typename ElementT, size_t SectorSize>
bool SectorArray2D<ElementT, SectorSize>::sectorLoaded(Sector const& id) const {
return m_loadedSectors.contains(id);
}
template <typename ElementT, size_t SectorSize>
auto SectorArray2D<ElementT, SectorSize>::sector(Sector const& id) -> Array * {
return m_sectors(id[0], id[1]).get();
}
template <typename ElementT, size_t SectorSize>
auto SectorArray2D<ElementT, SectorSize>::sector(Sector const& id) const -> Array const * {
return m_sectors(id[0], id[1]).get();
}
template <typename ElementT, size_t SectorSize>
void SectorArray2D<ElementT, SectorSize>::loadSector(Sector const& id, ArrayPtr array) {
auto& data = m_sectors(id[0], id[1]);
data = std::move(array);
if (data)
m_loadedSectors.add(id);
else
m_loadedSectors.remove(id);
}
template <typename ElementT, size_t SectorSize>
typename SectorArray2D<ElementT, SectorSize>::ArrayPtr SectorArray2D<ElementT, SectorSize>::copySector(
Sector const& id) {
if (auto const& array = m_sectors(id))
return std::make_unique<Array>(*array);
else
return {};
}
template <typename ElementT, size_t SectorSize>
typename SectorArray2D<ElementT, SectorSize>::ArrayPtr SectorArray2D<ElementT, SectorSize>::takeSector(
Sector const& id) {
ArrayPtr ret;
m_loadedSectors.remove(id);
std::swap(m_sectors(id[0], id[1]), ret);
return ret;
}
template <typename ElementT, size_t SectorSize>
void SectorArray2D<ElementT, SectorSize>::discardSector(Sector const& id) {
m_loadedSectors.remove(id);
m_sectors(id[0], id[1]).reset();
}
template <typename ElementT, size_t SectorSize>
typename SectorArray2D<ElementT, SectorSize>::Element const* SectorArray2D<ElementT, SectorSize>::get(
size_t x, size_t y) const {
Array* array = m_sectors(x / SectorSize, y / SectorSize).get();
if (array) {
return &(*array)(x % SectorSize, y % SectorSize);
} else {
return nullptr;
}
}
template <typename ElementT, size_t SectorSize>
typename SectorArray2D<ElementT, SectorSize>::Element* SectorArray2D<ElementT, SectorSize>::get(size_t x, size_t y) {
Array* array = m_sectors(x / SectorSize, y / SectorSize).get();
if (array)
return &(*array)(x % SectorSize, y % SectorSize);
else
return nullptr;
}
template <typename ElementT, size_t SectorSize>
template <typename Function>
bool SectorArray2D<ElementT, SectorSize>::eval(
size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty) const {
return const_cast<SectorArray2D*>(this)->evalPriv(minX, minY, width, height, std::forward<Function>(function), evalEmpty);
}
template <typename ElementT, size_t SectorSize>
template <typename Function>
bool SectorArray2D<ElementT, SectorSize>::eval(
size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty) {
return evalPriv(minX, minY, width, height, std::forward<Function>(function), evalEmpty);
}
template <typename ElementT, size_t SectorSize>
template <typename Function>
bool SectorArray2D<ElementT, SectorSize>::evalColumns(
size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty) const {
return const_cast<SectorArray2D*>(this)->evalColumnsPriv(
minX, minY, width, height, std::forward<Function>(function), evalEmpty);
}
template <typename ElementT, size_t SectorSize>
template <typename Function>
bool SectorArray2D<ElementT, SectorSize>::evalColumns(
size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty) {
return evalColumnsPriv(minX, minY, width, height, std::forward<Function>(function), evalEmpty);
}
template <typename ElementT, size_t SectorSize>
template <typename Function>
bool SectorArray2D<ElementT, SectorSize>::evalPriv(
size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty) {
return evalColumnsPriv(minX,
minY,
width,
height,
[&function](size_t x, size_t y, Element* column, size_t columnSize) {
for (size_t i = 0; i < columnSize; ++i) {
if (column) {
if (!function(x, y + i, column + i))
return false;
} else {
if (!function(x, y + i, nullptr))
return false;
}
}
return true;
},
evalEmpty);
}
template <typename ElementT, size_t SectorSize>
template <typename Function>
bool SectorArray2D<ElementT, SectorSize>::evalColumnsPriv(
size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty) {
if (width == 0 || height == 0)
return true;
size_t maxX = minX + width;
size_t maxY = minY + height;
size_t minXSector = minX / SectorSize;
size_t maxXSector = (maxX - 1) / SectorSize;
size_t minYSector = minY / SectorSize;
size_t maxYSector = (maxY - 1) / SectorSize;
for (size_t xSector = minXSector; xSector <= maxXSector; ++xSector) {
size_t minXi = 0;
if (xSector == minXSector)
minXi = minX % SectorSize;
size_t maxXi = SectorSize - 1;
if (xSector == maxXSector)
maxXi = (maxX - 1) % SectorSize;
for (size_t ySector = minYSector; ySector <= maxYSector; ++ySector) {
Array* array = m_sectors(xSector, ySector).get();
if (!array && !evalEmpty)
continue;
size_t minYi = 0;
if (ySector == minYSector)
minYi = minY % SectorSize;
size_t maxYi = SectorSize - 1;
if (ySector == maxYSector)
maxYi = (maxY - 1) % SectorSize;
size_t y_ = ySector * SectorSize;
size_t x_ = xSector * SectorSize;
if (!array) {
for (size_t xi = minXi; xi <= maxXi; ++xi) {
if (!function(xi + x_, minYi + y_, nullptr, maxYi - minYi + 1))
return false;
}
} else {
for (size_t xi = minXi; xi <= maxXi; ++xi) {
if (!function(xi + x_, minYi + y_, &array->elements[xi * SectorSize + minYi], maxYi - minYi + 1))
return false;
}
}
}
}
return true;
}
}