| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-FileCopyrightText: 2022-2023 Dennis Gläser <dennis.glaeser@iws.uni-stuttgart.de> | ||
| 2 | // SPDX-License-Identifier: MIT | ||
| 3 | /*! | ||
| 4 | * \file | ||
| 5 | * \ingroup VTK | ||
| 6 | * \copydoc GridFormat::VTIReader | ||
| 7 | */ | ||
| 8 | #ifndef GRIDFORMAT_VTK_VTI_READER_HPP_ | ||
| 9 | #define GRIDFORMAT_VTK_VTI_READER_HPP_ | ||
| 10 | |||
| 11 | #include <string> | ||
| 12 | #include <optional> | ||
| 13 | #include <algorithm> | ||
| 14 | #include <utility> | ||
| 15 | #include <ranges> | ||
| 16 | #include <array> | ||
| 17 | #include <cmath> | ||
| 18 | |||
| 19 | #include <gridformat/common/exceptions.hpp> | ||
| 20 | #include <gridformat/common/precision.hpp> | ||
| 21 | #include <gridformat/common/ranges.hpp> | ||
| 22 | #include <gridformat/common/field.hpp> | ||
| 23 | #include <gridformat/common/lazy_field.hpp> | ||
| 24 | |||
| 25 | #include <gridformat/grid/reader.hpp> | ||
| 26 | #include <gridformat/vtk/common.hpp> | ||
| 27 | #include <gridformat/vtk/xml.hpp> | ||
| 28 | |||
| 29 | namespace GridFormat { | ||
| 30 | |||
| 31 | /*! | ||
| 32 | * \ingroup VTK | ||
| 33 | * \brief Reader for .vti file format | ||
| 34 | */ | ||
| 35 | class VTIReader : public GridReader { | ||
| 36 | private: | ||
| 37 | struct ImageSpecs { | ||
| 38 | std::array<std::size_t, 6> extents; | ||
| 39 | std::array<double, 3> spacing; | ||
| 40 | std::array<double, 3> origin; | ||
| 41 | std::array<double, 9> direction; | ||
| 42 | }; | ||
| 43 | |||
| 44 | 300 | void _open(const std::string& filename, typename GridReader::FieldNames& fields) override { | |
| 45 |
1/2✓ Branch 2 taken 300 times.
✗ Branch 3 not taken.
|
300 | auto helper = VTK::XMLReaderHelper::make_from(filename, "ImageData"); |
| 46 | 300 | auto specs = ImageSpecs{}; | |
| 47 |
3/6✓ Branch 2 taken 300 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 300 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 300 times.
✗ Branch 10 not taken.
|
300 | specs.extents = Ranges::array_from_string<std::size_t, 6>(helper.get("ImageData/Piece").get_attribute("Extent")); |
| 48 |
3/6✓ Branch 2 taken 300 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 300 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 300 times.
✗ Branch 10 not taken.
|
300 | specs.spacing = Ranges::array_from_string<double, 3>(helper.get("ImageData").get_attribute("Spacing")); |
| 49 |
3/6✓ Branch 2 taken 300 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 300 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 300 times.
✗ Branch 10 not taken.
|
300 | specs.origin = Ranges::array_from_string<double, 3>(helper.get("ImageData").get_attribute("Origin")); |
| 50 |
3/6✓ Branch 2 taken 300 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 300 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 300 times.
✗ Branch 10 not taken.
|
600 | specs.direction = Ranges::array_from_string<double, 9>(helper.get("ImageData").get_attribute_or( |
| 51 |
1/2✓ Branch 1 taken 300 times.
✗ Branch 2 not taken.
|
600 | std::string{"1 0 0 0 1 0 0 0 1"}, "Direction" |
| 52 | )); | ||
| 53 | |||
| 54 |
2/4✓ Branch 2 taken 300 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 300 times.
✗ Branch 6 not taken.
|
300 | VTK::XMLDetail::copy_field_names_from(helper.get("ImageData"), fields); |
| 55 | 300 | _helper.emplace(std::move(helper)); | |
| 56 | 300 | _image_specs.emplace(std::move(specs)); | |
| 57 | 300 | } | |
| 58 | |||
| 59 | 44 | void _close() override { | |
| 60 | 44 | _helper.reset(); | |
| 61 | 44 | _image_specs.reset(); | |
| 62 | 44 | } | |
| 63 | |||
| 64 | 1 | std::string _name() const override { | |
| 65 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
2 | return "VTIReader"; |
| 66 | } | ||
| 67 | |||
| 68 | 1659 | std::size_t _number_of_cells() const override { | |
| 69 | 1659 | return VTK::CommonDetail::number_of_entities(_specs().extents); | |
| 70 | } | ||
| 71 | |||
| 72 | 1544 | std::size_t _number_of_points() const override { | |
| 73 |
2/4✓ Branch 1 taken 1544 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1404 times.
✗ Branch 5 not taken.
|
1544 | return VTK::CommonDetail::number_of_entities(_point_extents()); |
| 74 | } | ||
| 75 | |||
| 76 | 7 | std::size_t _number_of_pieces() const override { | |
| 77 | 7 | return 1; | |
| 78 | } | ||
| 79 | |||
| 80 | 13 | typename GridReader::Vector _origin() const override { | |
| 81 | 13 | return _specs().origin; | |
| 82 | } | ||
| 83 | |||
| 84 | 13 | typename GridReader::Vector _spacing() const override { | |
| 85 | 13 | return _specs().spacing; | |
| 86 | } | ||
| 87 | |||
| 88 | ✗ | typename GridReader::Vector _basis_vector(unsigned int i) const override { | |
| 89 | ✗ | const auto specs = _specs(); | |
| 90 | return { | ||
| 91 | ✗ | specs.direction.at(i), | |
| 92 | ✗ | specs.direction.at(i+3), | |
| 93 | ✗ | specs.direction.at(i+6) | |
| 94 | ✗ | }; | |
| 95 | } | ||
| 96 | |||
| 97 | 314 | typename GridReader::PieceLocation _location() const override { | |
| 98 | 314 | const auto& specs = _specs(); | |
| 99 | typename GridReader::PieceLocation result; | ||
| 100 | 314 | result.lower_left = {specs.extents[0], specs.extents[2], specs.extents[4]}; | |
| 101 | 314 | result.upper_right = {specs.extents[1], specs.extents[3], specs.extents[5]}; | |
| 102 | 314 | return result; | |
| 103 | } | ||
| 104 | |||
| 105 | 34 | std::vector<double> _ordinates(unsigned int direction) const override { | |
| 106 |
1/2✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
|
34 | const auto& specs = _specs(); |
| 107 |
1/2✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
|
34 | const auto extents = _point_extents(); |
| 108 | 34 | const auto extent_begin = extents[direction*2]; | |
| 109 | 34 | const std::size_t num_ordinates = extents[2*direction + 1] - extent_begin; | |
| 110 |
1/2✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
|
34 | std::vector<double> ordinates(num_ordinates); |
| 111 |
1/2✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
|
68 | std::ranges::copy( |
| 112 |
2/4✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 34 times.
✗ Branch 5 not taken.
|
34 | std::views::iota(std::size_t{0}, num_ordinates) | std::views::transform([&] (std::size_t i) { |
| 113 | 122 | return specs.origin[direction] + (extent_begin + i)*specs.spacing[direction]; | |
| 114 |
1/2✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
|
34 | }), |
| 115 | ordinates.begin() | ||
| 116 | ); | ||
| 117 | 34 | return ordinates; | |
| 118 | ✗ | } | |
| 119 | |||
| 120 | 85 | bool _is_sequence() const override { | |
| 121 | 85 | return false; | |
| 122 | } | ||
| 123 | |||
| 124 | 217 | FieldPtr _points() const override { | |
| 125 |
1/2✓ Branch 1 taken 217 times.
✗ Branch 2 not taken.
|
217 | const auto pextents = _point_extents(); |
| 126 |
1/2✓ Branch 1 taken 207 times.
✗ Branch 2 not taken.
|
217 | const auto num_points = VTK::CommonDetail::number_of_entities(pextents); |
| 127 | 217 | return make_field_ptr(LazyField{ | |
| 128 | int{}, // dummy "source" | ||
| 129 | ✗ | MDLayout{{num_points, std::size_t{3}}}, | |
| 130 | Precision<double>{}, | ||
| 131 |
1/2✓ Branch 1 taken 217 times.
✗ Branch 2 not taken.
|
217 | [specs=_specs(), pextents=pextents] (const int&) { |
| 132 | return VTK::CommonDetail::serialize_structured_points( | ||
| 133 | 217 | pextents, | |
| 134 | 217 | specs.origin, | |
| 135 | 217 | specs.spacing, | |
| 136 | 217 | specs.direction | |
| 137 | 217 | ); | |
| 138 | } | ||
| 139 |
3/6✓ Branch 1 taken 217 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 217 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 217 times.
✗ Branch 9 not taken.
|
651 | }); |
| 140 | } | ||
| 141 | |||
| 142 | 321 | void _visit_cells(const typename GridReader::CellVisitor& visitor) const override { | |
| 143 | 321 | VTK::CommonDetail::visit_structured_cells(visitor, _specs().extents); | |
| 144 | 321 | } | |
| 145 | |||
| 146 | 737 | FieldPtr _cell_field(std::string_view name) const override { | |
| 147 |
2/4✓ Branch 2 taken 737 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 737 times.
✗ Branch 8 not taken.
|
737 | return _helper.value().make_data_array_field(name, "ImageData/Piece/CellData", _number_of_cells()); |
| 148 | } | ||
| 149 | |||
| 150 | 740 | FieldPtr _point_field(std::string_view name) const override { | |
| 151 |
2/4✓ Branch 2 taken 740 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 740 times.
✗ Branch 8 not taken.
|
740 | return _helper.value().make_data_array_field(name, "ImageData/Piece/PointData", _number_of_points()); |
| 152 | } | ||
| 153 | |||
| 154 | 54 | FieldPtr _meta_data_field(std::string_view name) const override { | |
| 155 |
1/2✓ Branch 4 taken 54 times.
✗ Branch 5 not taken.
|
54 | return _helper.value().make_data_array_field(name, "ImageData/FieldData"); |
| 156 | } | ||
| 157 | |||
| 158 | 1795 | std::array<std::size_t, 6> _point_extents() const { | |
| 159 | 1795 | auto result = _specs().extents; | |
| 160 | 1795 | result[1] += 1; | |
| 161 | 1795 | result[3] += 1; | |
| 162 | 1795 | result[5] += 1; | |
| 163 | 1795 | return result; | |
| 164 | } | ||
| 165 | |||
| 166 | 4366 | const ImageSpecs& _specs() const { | |
| 167 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 4366 times.
|
4366 | if (!_image_specs.has_value()) |
| 168 | ✗ | throw ValueError("No data has been read"); | |
| 169 | 4366 | return _image_specs.value(); | |
| 170 | } | ||
| 171 | |||
| 172 | std::optional<VTK::XMLReaderHelper> _helper; | ||
| 173 | std::optional<ImageSpecs> _image_specs; | ||
| 174 | }; | ||
| 175 | |||
| 176 | } // namespace GridFormat | ||
| 177 | |||
| 178 | #endif // GRIDFORMAT_VTK_VTI_READER_HPP_ | ||
| 179 |