199 lines
6.0 KiB
C++
199 lines
6.0 KiB
C++
#pragma once
|
|
|
|
#include <vector>
|
|
#include <stdexcept>
|
|
#include <functional>
|
|
#include <glm/glm.hpp>
|
|
|
|
namespace util {
|
|
|
|
template<class T, typename TCoord=int>
|
|
class AreaMap2D {
|
|
public:
|
|
using OutCallback = std::function<void(TCoord, TCoord, const T&)>;
|
|
private:
|
|
TCoord offsetX = 0, offsetY = 0;
|
|
TCoord sizeX, sizeY;
|
|
std::vector<T> firstBuffer;
|
|
std::vector<T> 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<T> newFirstBuffer(newVolume);
|
|
std::vector<T> 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<T>& getBuffer() const {
|
|
return firstBuffer;
|
|
}
|
|
|
|
size_t count() const {
|
|
return valuesCount;
|
|
}
|
|
|
|
TCoord area() const {
|
|
return sizeX * sizeY;
|
|
}
|
|
};
|
|
}
|