mmcfilters
Public API documentation
Loading...
Searching...
No Matches
ContourComputation.hpp
1#pragma once
2
3#include "../trees/ValuedMorphologicalTreeView.hpp"
4#include "detail/ContourTraversal.hpp"
5
6#include <iterator>
7#include <memory>
8#include <span>
9#include <utility>
10#include <vector>
11
12namespace mmcfilters {
13
30 private:
32 struct SharedIndexes {
34 const MorphologicalTree& tree;
36 std::size_t mutationVersion;
38 contours::detail::NodeSupportIndex supportIndex;
40 contours::detail::ContourLifetimeIndex contourLifetimes;
41
46 explicit SharedIndexes(const MorphologicalTree& source)
47 : tree(source), mutationVersion(source.getMutationVersion()), supportIndex(source), contourLifetimes(source) {}
48
50 void requireStableTree() const { tree.requireMutationVersion(mutationVersion, "ContourComputation"); }
51 };
52
54 struct TraversalState {
56 std::shared_ptr<const SharedIndexes> indexes;
58 contours::detail::ContourTraversal traversal;
60 bool hasCurrentContour;
61
66 explicit TraversalState(std::shared_ptr<const SharedIndexes> source)
67 : indexes(std::move(source)), traversal(indexes->tree, indexes->supportIndex, indexes->contourLifetimes),
68 hasCurrentContour(traversal.advance()) {}
69 };
70
71 public:
79 class iterator {
80 public:
82 using iterator_concept = std::input_iterator_tag;
84 using iterator_category = std::input_iterator_tag;
86 using value_type = std::pair<NodeId, std::span<const PixelId>>;
88 using difference_type = std::ptrdiff_t;
89
90 iterator() = default;
91
97 if (!state_ || !state_->hasCurrentContour) {
98 throw std::out_of_range("Contour iterator is exhausted.");
99 }
100 return state_->traversal.current();
101 }
102
108 if (!state_ || !state_->hasCurrentContour) {
109 throw std::out_of_range("Contour iterator is exhausted.");
110 }
111 state_->hasCurrentContour = state_->traversal.advance();
112 return *this;
113 }
114
116 void operator++(int) { ++*this; }
117
119 friend bool operator==(const iterator& it, std::default_sentinel_t) noexcept {
120 return !it.state_ || !it.state_->hasCurrentContour;
121 }
122
123 private:
124 friend class ContourComputation;
125
130 explicit iterator(std::shared_ptr<const SharedIndexes> indexes) : state_(std::make_shared<TraversalState>(std::move(indexes))) {}
132 std::shared_ptr<TraversalState> state_;
133 };
134
139 explicit ContourComputation(const MorphologicalTree& tree) : indexes_(std::make_shared<SharedIndexes>(tree)) {}
140
145 template <AltitudeValue T>
146 explicit ContourComputation(const ValuedMorphologicalTreeView<T>& view) : ContourComputation(currentTopology(view)) {}
147
153 [[nodiscard]] std::vector<PixelId> contour(NodeId node) const {
154 const auto support = indexes_->supportIndex.support(node);
155 std::vector<PixelId> contourPixels;
156 for (PixelId pixel : support) {
157 if (indexes_->contourLifetimes.establishedIsContourPixel(pixel, node)) {
158 contourPixels.push_back(pixel);
159 }
160 }
161 return contourPixels;
162 }
163
169 indexes_->requireStableTree();
170 return iterator(indexes_);
171 }
172
177 [[nodiscard]] std::default_sentinel_t end() const noexcept { return {}; }
178
186 template <typename Consumer> void forEachContour(Consumer&& consumer) const {
187 for (auto [node, pixels] : *this) {
188 consumer(node, pixels);
189 indexes_->requireStableTree();
190 }
191 }
192
193 private:
199 template <AltitudeValue T> static const MorphologicalTree& currentTopology(const ValuedMorphologicalTreeView<T>& view) {
200 MMCFILTERS_CONTRACT_CHECKED_ONLY(view.requireTopologyUnchanged("ContourComputation"));
201 return view.topology();
202 }
203
205 std::shared_ptr<const SharedIndexes> indexes_;
206};
207
208} // namespace mmcfilters
#define MMCFILTERS_CONTRACT_CHECKED_ONLY(...)
Executes validation statements only when defensive checks are enabled.
Definition Contract.hpp:67
Single-pass iterator yielding (node, borrowed pixel span).
std::pair< NodeId, std::span< const PixelId > > value_type
Node identifier and borrowed contour span yielded by dereference.
std::input_iterator_tag iterator_concept
C++20 iterator concept for this single-pass traversal.
void operator++(int)
Advances without retaining the previous borrowed contour.
std::input_iterator_tag iterator_category
Iterator category used by standard algorithms.
friend bool operator==(const iterator &it, std::default_sentinel_t) noexcept
Tests exhaustion against the range's sentinel.
iterator & operator++()
Advances the shared single-pass position.
value_type operator*() const
Borrows the current node contour.
std::ptrdiff_t difference_type
Signed type used to represent iterator distances.
Incremental foreground A4 contours on the image domain.
std::default_sentinel_t end() const noexcept
Returns the exhaustion sentinel.
void forEachContour(Consumer &&consumer) const
Calls consumer(node, pixels) once for every live node.
ContourComputation(const ValuedMorphologicalTreeView< T > &view)
Builds contours from the topology of a current valued view.
ContourComputation(const MorphologicalTree &tree)
Builds the compact indexes for a stable tree with a 2D domain.
iterator begin() const
Starts an independent incremental post-order traversal.
std::vector< PixelId > contour(NodeId node) const
Returns an owned contour of one live node, without caching it.
Mutable connected-subset tree on a finite pixel domain.
void requireMutationVersion(std::size_t expectedVersion, const char *context) const
Rejects stale read-only views that captured an older mutation version.
std::size_t getMutationVersion() const noexcept
Returns the monotonic mutation counter used by read-only views.
Owning result for one computed scalar attribute layout and buffer.