Add auto-expanding in all directions util::spatial_array

This commit is contained in:
Nikita Lisitsa 2022-08-05 15:54:58 +03:00
parent dc1b86fa75
commit 649c051b61

View file

@ -0,0 +1,174 @@
#pragma once
#include <psemek/util/array.hpp>
#include <stdexcept>
namespace psemek::util
{
template <typename T, std::size_t N, typename Index = std::int32_t>
struct spatial_array
{
spatial_array();
using index_type = Index;
template <typename ... Ix>
T & get(Ix ... index);
template <typename ... Ix>
T * get_if(Ix ... index);
template <typename ... Ix>
T const * get_if(Ix ... index) const;
template <typename ... Ix>
T const & at(Ix ... index) const;
template <typename ... Ix>
bool contains(Ix ... index) const;
template <typename ... Ix>
void expand(Ix ... index);
void clear();
private:
Index origin_[N];
array<T, N> array_;
};
template <typename T, std::size_t N, typename Index>
spatial_array<T, N, Index>::spatial_array()
{
for (std::size_t i = 0; i < N; ++i)
origin_[i] = 0;
}
template <typename T, std::size_t N, typename Index>
template <typename ... Ix>
T & spatial_array<T, N, Index>::get(Ix ... index)
{
if (!contains(index...))
expand(index...);
Index ix[N] {index...};
for (std::size_t i = 0; i < N; ++i)
ix[i] -= origin_[i];
return array_(ix);
}
template <typename T, std::size_t N, typename Index>
template <typename ... Ix>
T * spatial_array<T, N, Index>::get_if(Ix ... index)
{
if (!contains(index...))
return nullptr;
Index ix[N] {index...};
for (std::size_t i = 0; i < N; ++i)
ix[i] -= origin_[i];
return &array_(ix);
}
template <typename T, std::size_t N, typename Index>
template <typename ... Ix>
T const * spatial_array<T, N, Index>::get_if(Ix ... index) const
{
if (!contains(index...))
return nullptr;
Index ix[N] {index...};
for (std::size_t i = 0; i < N; ++i)
ix[i] -= origin_[i];
return &array_(ix);
}
template <typename T, std::size_t N, typename Index>
template <typename ... Ix>
T const & spatial_array<T, N, Index>::at(Ix ... index) const
{
auto const size = array_.dims();
Index ix[N] {index...};
for (std::size_t i = 0; i < N; ++i)
{
if (ix[i] < origin_[i] || ix[i] >= origin_[i] + static_cast<Index>(size[i]))
throw std::runtime_error("element out of range");
ix[i] -= origin_[i];
}
return array_(ix);
}
template <typename T, std::size_t N, typename Index>
template <typename ... Ix>
bool spatial_array<T, N, Index>::contains(Ix ... index) const
{
auto const size = array_.dims();
Index ix[N] {index...};
for (std::size_t i = 0; i < N; ++i)
if (ix[i] < origin_[i] || ix[i] >= origin_[i] + static_cast<Index>(size[i]))
return false;
return true;
}
template <typename T, std::size_t N, typename Index>
template <typename ... Ix>
void spatial_array<T, N, Index>::expand(Ix ... index)
{
Index ix[N] {index...};
if (array_.empty())
{
std::array<std::size_t, N> size;
for (std::size_t i = 0; i < N; ++i)
{
origin_[i] = ix[i];
size[i] = 1;
}
array_.resize(size);
return;
}
Index new_origin[N];
std::array<std::size_t, N> new_size = array_.dims();
for (std::size_t i = 0; i < N; ++i)
new_origin[i] = origin_[i];
for (std::size_t i = 0; i < N; ++i)
{
while (ix[i] >= new_origin[i] + static_cast<Index>(new_size[i]))
new_size[i] += new_size[i];
while (ix[i] < new_origin[i])
{
new_origin[i] -= static_cast<Index>(new_size[i]);
new_size[i] += new_size[i];
}
}
array<T, N> new_array(new_size);
for (auto idx : array_.indices())
{
std::array<std::size_t, N> new_idx;
for (std::size_t i = 0; i < N; ++i)
new_idx[i] = static_cast<Index>(idx[i]) + origin_[i] - new_origin[i];
new_array(new_idx) = std::move(array_(idx));
}
array_ = std::move(new_array);
for (std::size_t i = 0; i < N; ++i)
origin_[i] = new_origin[i];
}
template <typename T, std::size_t N, typename Index>
void spatial_array<T, N, Index>::clear()
{
array_.clear();
}
}