Add auto-expanding in all directions util::spatial_array
This commit is contained in:
parent
dc1b86fa75
commit
649c051b61
1 changed files with 174 additions and 0 deletions
174
libs/util/include/psemek/util/spatial_array.hpp
Normal file
174
libs/util/include/psemek/util/spatial_array.hpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue