8#ifndef GRIDFORMAT_GRID_IMAGE_GRID_HPP_
9#define GRIDFORMAT_GRID_IMAGE_GRID_HPP_
21#include <gridformat/common/ranges.hpp>
22#include <gridformat/common/concepts.hpp>
23#include <gridformat/common/exceptions.hpp>
24#include <gridformat/common/type_traits.hpp>
25#include <gridformat/common/iterator_facades.hpp>
26#include <gridformat/common/flat_index_mapper.hpp>
27#include <gridformat/common/md_index.hpp>
29#include <gridformat/grid/cell_type.hpp>
30#include <gridformat/grid/traits.hpp>
40template<std::
size_t dim, Concepts::Scalar CoordinateType>
42 static_assert(dim > 0 && dim <= 3);
45 struct Entity { std::array<std::size_t, dim> location; };
52 template<Concepts::StaticallySizedMDRange<1> Size,
53 Concepts::StaticallySizedMDRange<1> Cells>
56 Ranges::filled_array<dim>(CoordinateType{0}),
57 std::forward<Size>(size),
58 std::forward<Cells>(
cells)
63 template<Concepts::StaticallySizedMDRange<1> Origin,
64 Concepts::StaticallySizedMDRange<1> Size,
65 Concepts::StaticallySizedMDRange<1> Cells>
66 requires(static_size<Origin> == dim and
67 static_size<Size> == dim and
68 static_size<Cells> == dim and
69 std::integral<std::ranges::range_value_t<Cells>>)
72 Ranges::to_array<dim, CoordinateType>(origin),
73 Ranges::to_array<dim, CoordinateType>(size),
74 Ranges::to_array<dim, std::size_t>(
cells)
84 std::array<std::size_t, dim>
cells)
85 :
ImageGrid(Ranges::filled_array<dim>(CoordinateType{0}), std::move(size), std::move(
cells))
95 std::array<CoordinateType, dim> size,
96 std::array<std::size_t, dim>
cells)
97 : _lower_right{std::move(origin)}
98 , _upper_right{Ranges::apply_pairwise<CoordinateType>(std::plus{}, _lower_right, size)}
99 , _spacing{_compute_spacing(
cells)}
100 , _cell_index_tuples{MDLayout{
cells}}
101 , _point_index_tuples{MDLayout{Ranges::incremented(
cells, 1)}}
102 , _cell_point_offsets{MDLayout{Ranges::filled_array<dim, std::size_t>(2)}}
103 , _point_mapper{Ranges::incremented(
cells, 1)} {
104 if (std::ranges::any_of(
cells, [] (
const std::integral
auto i) {
return i == 0; }))
105 throw ValueError(
"Number of cells in each direction must be > 0");
108 std::size_t number_of_cells()
const {
return _cell_index_tuples.size(); }
109 std::size_t number_of_points()
const {
return _point_index_tuples.size(); }
110 std::size_t number_of_cells(
unsigned int direction)
const {
return _cell_index_tuples.size(direction); }
111 std::size_t number_of_points(
unsigned int direction)
const {
return _point_index_tuples.size(direction); }
112 const auto& origin()
const {
return _lower_right; }
113 const auto& spacing()
const {
return _spacing; }
117 return Ranges::to_array<dim, std::size_t>(
118 std::views::iota(std::size_t{0}, dim) | std::views::transform([&] (std::size_t i) {
119 return _cell_index_tuples.size(i);
126 std::vector<CoordinateType> result(number_of_points(direction));
127 for (
unsigned int i = 0; i < result.size(); ++i)
128 result[i] = _ordinate_at(i, direction);
134 return Ranges::to_array<dim, CoordinateType>(
135 std::views::iota(std::size_t{0}, dim)
136 | std::views::transform([&] (std::size_t direction) {
137 return _ordinate_at(p.location[direction], direction);
144 unsigned corner_count = 0;
145 auto result = Ranges::filled_array<dim>(CoordinateType{0});
146 for (
const auto& p :
points(c)) {
149 for (
unsigned i = 0; i < dim; ++i)
150 result[i] += ppos[i];
152 std::ranges::for_each(result, [&] (
auto& v) { v /=
static_cast<CoordinateType
>(corner_count); });
158 return _point_mapper.map(p.location);
162 std::ranges::range
auto cells()
const {
163 return _cell_index_tuples | std::views::transform([] (
const auto& indices) {
164 return Cell{Ranges::to_array<dim>(indices)};
170 return _point_index_tuples | std::views::transform([] (
const auto& indices) {
171 return Point{Ranges::to_array<dim>(indices)};
177 return _cell_point_offsets
178 | std::views::transform([loc = cell.location] (
const auto& offset) {
179 auto point_loc = loc;
181 point_loc.begin(), point_loc.end(), offset.begin(), point_loc.begin(),
182 [] (std::size_t p, std::size_t o) { return p + o; }
184 return Point{std::move(point_loc)};
189 CoordinateType _ordinate_at(std::size_t i,
int direction)
const {
190 return _lower_right[direction] + _spacing[direction]*
static_cast<CoordinateType
>(i);
193 template<Concepts::StaticallySizedMDRange<1> Cells>
194 auto _compute_spacing(Cells&& cells)
const {
195 return Ranges::apply_pairwise(
197 Ranges::apply_pairwise(std::minus{}, _upper_right, _lower_right),
202 std::array<unsigned int, dim> _directions_in_descending_size()
const {
203 std::array<unsigned int, dim> result;
204 for (
int i = 0; i < dim; ++i)
206 std::ranges::sort(result, [&] (
unsigned int dir1,
unsigned int dir2) {
207 return _cell_index_tuples.size(dir1) < _cell_index_tuples.size(dir2);
212 std::array<CoordinateType, dim> _lower_right;
213 std::array<CoordinateType, dim> _upper_right;
214 std::array<CoordinateType, dim> _spacing;
215 MDIndexRange _cell_index_tuples;
216 MDIndexRange _point_index_tuples;
217 MDIndexRange _cell_point_offsets;
218 FlatIndexMapper<dim> _point_mapper;
221template<Concepts::StaticallySizedMDRange<1> S, Concepts::StaticallySizedMDRange<1> C>
222 requires(static_size<S> == static_size<C>)
223ImageGrid(S&&, C&&) -> ImageGrid<static_size<S>, std::ranges::range_value_t<S>>;
229template<std::
size_t dim,
typename CT>
230struct Points<ImageGrid<dim, CT>> {
231 static std::ranges::range
auto get(
const ImageGrid<dim, CT>& grid) {
232 return grid.points();
236template<std::
size_t dim,
typename CT>
237struct Cells<ImageGrid<dim, CT>> {
238 static std::ranges::range
auto get(
const ImageGrid<dim, CT>& grid) {
243template<std::
size_t dim,
typename CT>
244struct Origin<ImageGrid<dim, CT>> {
245 static const auto& get(
const ImageGrid<dim, CT>& grid) {
246 return grid.origin();
250template<std::
size_t dim,
typename CT>
251struct Spacing<ImageGrid<dim, CT>> {
252 static const auto& get(
const ImageGrid<dim, CT>& grid) {
253 return grid.spacing();
257template<std::
size_t dim,
typename CT>
258struct Extents<ImageGrid<dim, CT>> {
259 static auto get(
const ImageGrid<dim, CT>& grid) {
260 return grid.extents();
264template<std::
size_t dim,
typename CT,
typename Entity>
265struct Location<ImageGrid<dim, CT>, Entity> {
267 std::is_same_v<Entity, typename ImageGrid<dim, CT>::Cell> ||
268 std::is_same_v<Entity, typename ImageGrid<dim, CT>::Point>
270 static auto get(
const ImageGrid<dim, CT>&,
const Entity& e) {
275template<std::
size_t dim,
typename CT>
276struct Ordinates<ImageGrid<dim, CT>> {
277 static auto get(
const ImageGrid<dim, CT>& grid,
unsigned int dir) {
278 return grid.ordinates(dir);
283template<std::
size_t dim,
typename CT>
284struct PointCoordinates<ImageGrid<dim, CT>, typename ImageGrid<dim, CT>::Point> {
285 static decltype(
auto) get(
const ImageGrid<dim, CT>& grid,
const typename ImageGrid<dim, CT>::Point& p) {
286 return grid.position(p);
291template<std::
size_t dim,
typename CT>
292struct CellPoints<ImageGrid<dim, CT>, typename ImageGrid<dim, CT>::Cell> {
293 static auto get(
const ImageGrid<dim, CT>& grid,
const typename ImageGrid<dim, CT>::Cell& c) {
294 return grid.points(c);
298template<std::
size_t dim,
typename CT>
299struct PointId<ImageGrid<dim, CT>, typename ImageGrid<dim, CT>::Point> {
300 static auto get(
const ImageGrid<dim, CT>& grid,
const typename ImageGrid<dim, CT>::Point& p) {
305template<std::
size_t dim,
typename CT>
306struct CellType<ImageGrid<dim, CT>, typename ImageGrid<dim, CT>::Cell> {
307 static auto get(
const ImageGrid<dim, CT>&,
const typename ImageGrid<dim, CT>::Cell&) {
308 if constexpr (dim == 1)
309 return GridFormat::CellType::segment;
310 if constexpr (dim == 2)
311 return GridFormat::CellType::pixel;
312 if constexpr (dim == 3)
313 return GridFormat::CellType::voxel;