diff options
author | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
---|---|---|
committer | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
commit | 6352e8e3196f78388b6c771073f9e03eaa612673 (patch) | |
tree | e23772f79a7fbc41bc9108951e9e136857484bf4 /source/core/StarSectorArray2D.hpp | |
parent | 6741a057e5639280d85d0f88ba26f000baa58f61 (diff) |
everything everywhere
all at once
Diffstat (limited to 'source/core/StarSectorArray2D.hpp')
-rw-r--r-- | source/core/StarSectorArray2D.hpp | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/source/core/StarSectorArray2D.hpp b/source/core/StarSectorArray2D.hpp new file mode 100644 index 0000000..4645804 --- /dev/null +++ b/source/core/StarSectorArray2D.hpp @@ -0,0 +1,378 @@ +#ifndef STAR_SECTOR_SET_HPP +#define STAR_SECTOR_SET_HPP + +#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 = 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 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 move(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, 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, 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, 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, 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; +} + +} + +#endif |