GridFormat 0.2.1
I/O-Library for grid-like data structures
Loading...
Searching...
No Matches
image_grid.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2022-2023 Dennis Gläser <dennis.glaeser@iws.uni-stuttgart.de>
2// SPDX-License-Identifier: MIT
8#ifndef GRIDFORMAT_GRID_IMAGE_GRID_HPP_
9#define GRIDFORMAT_GRID_IMAGE_GRID_HPP_
10
11#include <cmath>
12#include <array>
13#include <ranges>
14#include <cassert>
15#include <utility>
16#include <numeric>
17#include <algorithm>
18#include <functional>
19#include <type_traits>
20
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>
28
29#include <gridformat/grid/cell_type.hpp>
30#include <gridformat/grid/traits.hpp>
31
32namespace GridFormat {
33
40template<std::size_t dim, Concepts::Scalar CoordinateType>
41class ImageGrid {
42 static_assert(dim > 0 && dim <= 3);
43
44 template<int codim>
45 struct Entity { std::array<std::size_t, dim> location; };
46
47 public:
48 using Cell = Entity<0>;
49 using Point = Entity<dim>;
50
52 template<Concepts::StaticallySizedMDRange<1> Size,
53 Concepts::StaticallySizedMDRange<1> Cells>
54 ImageGrid(Size&& size, Cells&& cells)
55 : ImageGrid(
56 Ranges::filled_array<dim>(CoordinateType{0}),
57 std::forward<Size>(size),
58 std::forward<Cells>(cells)
59 )
60 {}
61
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>>)
70 ImageGrid(Origin&& origin, Size&& size, Cells&& cells)
71 : ImageGrid(
72 Ranges::to_array<dim, CoordinateType>(origin),
73 Ranges::to_array<dim, CoordinateType>(size),
74 Ranges::to_array<dim, std::size_t>(cells)
75 )
76 {}
77
83 ImageGrid(std::array<CoordinateType, dim> size,
84 std::array<std::size_t, dim> cells)
85 : ImageGrid(Ranges::filled_array<dim>(CoordinateType{0}), std::move(size), std::move(cells))
86 {}
87
94 ImageGrid(std::array<CoordinateType, dim> origin,
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");
106 }
107
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; }
114
116 auto extents() const {
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);
120 })
121 );
122 }
123
125 auto ordinates(int direction) const {
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);
129 return result;
130 }
131
133 auto position(const Point& p) const {
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);
138 })
139 );
140 }
141
143 auto center(const Cell& c) const {
144 unsigned corner_count = 0;
145 auto result = Ranges::filled_array<dim>(CoordinateType{0});
146 for (const auto& p : points(c)) {
147 corner_count++;
148 const auto ppos = position(p);
149 for (unsigned i = 0; i < dim; ++i)
150 result[i] += ppos[i];
151 }
152 std::ranges::for_each(result, [&] (auto& v) { v /= static_cast<CoordinateType>(corner_count); });
153 return result;
154 }
155
157 std::size_t id(const Point& p) const {
158 return _point_mapper.map(p.location);
159 }
160
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)};
165 });
166 }
167
169 std::ranges::range auto points() const {
170 return _point_index_tuples | std::views::transform([] (const auto& indices) {
171 return Point{Ranges::to_array<dim>(indices)};
172 });
173 }
174
176 std::ranges::range auto points(const Cell& cell) const {
177 return _cell_point_offsets
178 | std::views::transform([loc = cell.location] (const auto& offset) {
179 auto point_loc = loc;
180 std::transform(
181 point_loc.begin(), point_loc.end(), offset.begin(), point_loc.begin(),
182 [] (std::size_t p, std::size_t o) { return p + o; }
183 );
184 return Point{std::move(point_loc)};
185 });
186 }
187
188 private:
189 CoordinateType _ordinate_at(std::size_t i, int direction) const {
190 return _lower_right[direction] + _spacing[direction]*static_cast<CoordinateType>(i);
191 }
192
193 template<Concepts::StaticallySizedMDRange<1> Cells>
194 auto _compute_spacing(Cells&& cells) const {
195 return Ranges::apply_pairwise(
196 std::divides{},
197 Ranges::apply_pairwise(std::minus{}, _upper_right, _lower_right),
198 cells
199 );
200 }
201
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)
205 result[i] = i;
206 std::ranges::sort(result, [&] (unsigned int dir1, unsigned int dir2) {
207 return _cell_index_tuples.size(dir1) < _cell_index_tuples.size(dir2);
208 });
209 return result;
210 }
211
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;
219};
220
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>>;
224
225
226#ifndef DOXYGEN
227namespace Traits {
228
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();
233 }
234};
235
236template<std::size_t dim, typename CT>
237struct Cells<ImageGrid<dim, CT>> {
238 static std::ranges::range auto get(const ImageGrid<dim, CT>& grid) {
239 return grid.cells();
240 }
241};
242
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();
247 }
248};
249
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();
254 }
255};
256
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();
261 }
262};
263
264template<std::size_t dim, typename CT, typename Entity>
265struct Location<ImageGrid<dim, CT>, Entity> {
266 static_assert(
267 std::is_same_v<Entity, typename ImageGrid<dim, CT>::Cell> ||
268 std::is_same_v<Entity, typename ImageGrid<dim, CT>::Point>
269 );
270 static auto get(const ImageGrid<dim, CT>&, const Entity& e) {
271 return e.location;
272 }
273};
274
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);
279 }
280};
281
282// required for structured grid concept
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);
287 }
288};
289
290// register as unstructured grid as well
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);
295 }
296};
297
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) {
301 return grid.id(p);
302 }
303};
304
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;
314 }
315};
316
317} // namespace Traits
318#endif // DOXYGEN
319
320} // namespace GridFormat
321
322#endif // GRIDFORMAT_GRID_IMAGE_GRID_HPP_
Predefined grid implementation that represents a structured, equispaced grid.
Definition: image_grid.hpp:41
ImageGrid(Size &&size, Cells &&cells)
Constructor overload for general range types.
Definition: image_grid.hpp:54
std::size_t id(const Point &p) const
Return a unique id for the given point.
Definition: image_grid.hpp:157
std::ranges::range auto cells() const
Return a range over all cells of the grid.
Definition: image_grid.hpp:162
auto extents() const
Return an array containing the number of cells in all directions.
Definition: image_grid.hpp:116
auto center(const Cell &c) const
Return the physical center position of the given grid cell.
Definition: image_grid.hpp:143
ImageGrid(std::array< CoordinateType, dim > origin, std::array< CoordinateType, dim > size, std::array< std::size_t, dim > cells)
Construct a grid with the given size and discretization.
Definition: image_grid.hpp:94
Entity< dim > Point
The type used for grid points.
Definition: image_grid.hpp:49
ImageGrid(Origin &&origin, Size &&size, Cells &&cells)
Constructor overload for general range types.
Definition: image_grid.hpp:70
auto position(const Point &p) const
Return the physical position of the given grid point.
Definition: image_grid.hpp:133
ImageGrid(std::array< CoordinateType, dim > size, std::array< std::size_t, dim > cells)
Construct a grid with the given size and discretization.
Definition: image_grid.hpp:83
std::ranges::range auto points(const Cell &cell) const
Return a range over all points in the given grid cell.
Definition: image_grid.hpp:176
std::ranges::range auto points() const
Return a range over all points of the grid.
Definition: image_grid.hpp:169
auto ordinates(int direction) const
Return the ordinates of the grid along the given direction.
Definition: image_grid.hpp:125
Entity< 0 > Cell
The type used for grid cells.
Definition: image_grid.hpp:48