#pragma once #include #include #include #include namespace util { template class AreaMap2D { public: using OutCallback = std::function; private: TCoord offsetX = 0, offsetY = 0; TCoord sizeX, sizeY; std::vector firstBuffer; std::vector secondBuffer; OutCallback outCallback; size_t valuesCount = 0; void translate(TCoord dx, TCoord dy) { if (dx == 0 && dy == 0) { return; } std::fill(secondBuffer.begin(), secondBuffer.end(), T{}); for (TCoord y = 0; y < sizeY; y++) { for (TCoord x = 0; x < sizeX; x++) { auto& value = firstBuffer[y * sizeX + x]; auto nx = x - dx; auto ny = y - dy; if (value == T{}) { continue; } if (nx < 0 || ny < 0 || nx >= sizeX || ny >= sizeY) { if (outCallback) { outCallback(x + offsetX, y + offsetY, value); } valuesCount--; continue; } secondBuffer[ny * sizeX + nx] = value; } } std::swap(firstBuffer, secondBuffer); offsetX += dx; offsetY += dy; } public: AreaMap2D(TCoord width, TCoord height) : sizeX(width), sizeY(height), firstBuffer(width * height), secondBuffer(width * height) { } const T* getIf(TCoord x, TCoord y) const { auto lx = x - offsetX; auto ly = y - offsetY; if (lx < 0 || ly < 0 || lx >= sizeX || ly >= sizeY) { return nullptr; } return &firstBuffer[ly * sizeX + lx]; } T get(TCoord x, TCoord y) const { auto lx = x - offsetX; auto ly = y - offsetY; if (lx < 0 || ly < 0 || lx >= sizeX || ly >= sizeY) { return T{}; } return firstBuffer[ly * sizeX + lx]; } T get(TCoord x, TCoord y, const T& def) const { if (auto ptr = getIf(x, y)) { const auto& value = *ptr; if (value == T{}) { return def; } return value; } return def; } bool isInside(TCoord x, TCoord y) const { auto lx = x - offsetX; auto ly = y - offsetY; return !(lx < 0 || ly < 0 || lx >= sizeX || ly >= sizeY); } const T& require(TCoord x, TCoord y) const { auto lx = x - offsetX; auto ly = y - offsetY; if (lx < 0 || ly < 0 || lx >= sizeX || ly >= sizeY) { throw std::invalid_argument("position is out of window"); } return firstBuffer[ly * sizeX + lx]; } bool set(TCoord x, TCoord y, T value) { auto lx = x - offsetX; auto ly = y - offsetY; if (lx < 0 || ly < 0 || lx >= sizeX || ly >= sizeY) { return false; } auto& element = firstBuffer[ly * sizeX + lx]; if (value && !element) { valuesCount++; } if (element && !value) { valuesCount--; } element = std::move(value); return true; } void setOutCallback(const OutCallback& callback) { outCallback = callback; } void resize(TCoord newSizeX, TCoord newSizeY) { if (newSizeX < sizeX) { TCoord delta = sizeX - newSizeX; translate(delta / 2, 0); translate(-delta, 0); translate(delta, 0); } if (newSizeY < sizeY) { TCoord delta = sizeY - newSizeY; translate(0, delta / 2); translate(0, -delta); translate(0, delta); } const TCoord newVolume = newSizeX * newSizeY; std::vector newFirstBuffer(newVolume); std::vector newSecondBuffer(newVolume); for (TCoord y = 0; y < sizeY && y < newSizeY; y++) { for (TCoord x = 0; x < sizeX && x < newSizeX; x++) { newFirstBuffer[y * newSizeX + x] = firstBuffer[y * sizeX + x]; } } sizeX = newSizeX; sizeY = newSizeY; firstBuffer = std::move(newFirstBuffer); secondBuffer = std::move(newSecondBuffer); } void setCenter(TCoord centerX, TCoord centerY) { auto deltaX = centerX - (offsetX + sizeX / 2); auto deltaY = centerY - (offsetY + sizeY / 2); if (deltaX | deltaY) { translate(deltaX, deltaY); } } void clear() { for (TCoord y = 0; y < sizeY; y++) { for (TCoord x = 0; x < sizeX; x++) { auto i = y * sizeX + x; auto value = firstBuffer[i]; firstBuffer[i] = {}; if (outCallback) { outCallback(x + offsetX, y + offsetY, value); } } } valuesCount = 0; } TCoord getOffsetX() const { return offsetX; } TCoord getOffsetY() const { return offsetY; } TCoord getWidth() const { return sizeX; } TCoord getHeight() const { return sizeX; } const std::vector& getBuffer() const { return firstBuffer; } size_t count() const { return valuesCount; } TCoord area() const { return sizeX * sizeY; } }; }