#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 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 ArrayPtr; typedef MultiArray 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 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 bool eval(size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty = false) const; template 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 bool evalColumns( size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty = false) const; template bool evalColumns(size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty = false); private: typedef MultiArray SectorArray; template bool evalPriv(size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty); template bool evalColumnsPriv(size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty); SectorArray m_sectors; HashSet m_loadedSectors; }; template SectorArray2D::Array::Array() : elements() {} template SectorArray2D::Array::Array(Element const& def) { for (size_t i = 0; i < SectorSize * SectorSize; ++i) elements[i] = def; } template ElementT const& SectorArray2D::Array::operator()(size_t x, size_t y) const { starAssert(x < SectorSize && y < SectorSize); return elements[x * SectorSize + y]; } template ElementT& SectorArray2D::Array::operator()(size_t x, size_t y) { starAssert(x < SectorSize && y < SectorSize); return elements[x * SectorSize + y]; } template SectorArray2D::SectorArray2D() {} template SectorArray2D::SectorArray2D(size_t numSectorsWide, size_t numSectorsHigh) { init(numSectorsWide, numSectorsHigh); } template void SectorArray2D::init(size_t numSectorsWide, size_t numSectorsHigh) { m_sectors.clear(); m_sectors.setSize(numSectorsWide, numSectorsHigh); m_loadedSectors.clear(); } template size_t SectorArray2D::width() const { return m_sectors.size(0) * SectorSize; } template size_t SectorArray2D::height() const { return m_sectors.size(1) * SectorSize; } template bool SectorArray2D::sectorValid(Sector const& sector) const { return sector[0] < m_sectors.size(0) && sector[1] < m_sectors.size(1); } template auto SectorArray2D::sectorFor(size_t x, size_t y) const -> Sector { return {x / SectorSize, y / SectorSize}; } template auto SectorArray2D::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 Vec2S SectorArray2D::sectorCorner(Sector const& id) const { return Vec2S(id[0] * SectorSize, id[1] * SectorSize); } template bool SectorArray2D::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 auto SectorArray2D::loadedSectors() const -> List { return m_loadedSectors.values(); } template size_t SectorArray2D::loadedSectorCount() const { return m_loadedSectors.size(); } template bool SectorArray2D::sectorLoaded(Sector const& id) const { return m_loadedSectors.contains(id); } template auto SectorArray2D::sector(Sector const& id) -> Array * { return m_sectors(id[0], id[1]).get(); } template auto SectorArray2D::sector(Sector const& id) const -> Array const * { return m_sectors(id[0], id[1]).get(); } template void SectorArray2D::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 SectorArray2D::ArrayPtr SectorArray2D::copySector( Sector const& id) { if (auto const& array = m_sectors(id)) return std::make_unique(*array); else return {}; } template typename SectorArray2D::ArrayPtr SectorArray2D::takeSector( Sector const& id) { ArrayPtr ret; m_loadedSectors.remove(id); std::swap(m_sectors(id[0], id[1]), ret); return ret; } template void SectorArray2D::discardSector(Sector const& id) { m_loadedSectors.remove(id); m_sectors(id[0], id[1]).reset(); } template typename SectorArray2D::Element const* SectorArray2D::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 SectorArray2D::Element* SectorArray2D::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 template bool SectorArray2D::eval( size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty) const { return const_cast(this)->evalPriv(minX, minY, width, height, std::forward(function), evalEmpty); } template template bool SectorArray2D::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), evalEmpty); } template template bool SectorArray2D::evalColumns( size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty) const { return const_cast(this)->evalColumnsPriv( minX, minY, width, height, std::forward(function), evalEmpty); } template template bool SectorArray2D::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), evalEmpty); } template template bool SectorArray2D::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 template bool SectorArray2D::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; } }