/src/gdal/alg/marching_squares/segment_merger.h
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: Marching square algorithm |
4 | | * Purpose: Core algorithm implementation for contour line generation. |
5 | | * Author: Oslandia <infos at oslandia dot com> |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2018, Oslandia <infos at oslandia dot com> |
9 | | * |
10 | | * SPDX-License-Identifier: MIT |
11 | | ****************************************************************************/ |
12 | | #ifndef MARCHING_SQUARES_SEGMENT_MERGER_H |
13 | | #define MARCHING_SQUARES_SEGMENT_MERGER_H |
14 | | |
15 | | #include "cpl_error.h" |
16 | | #include "point.h" |
17 | | |
18 | | #include <algorithm> |
19 | | #include <cassert> |
20 | | #include <list> |
21 | | #include <map> |
22 | | #include <vector> |
23 | | |
24 | | #include <iostream> |
25 | | |
26 | | namespace marching_squares |
27 | | { |
28 | | |
29 | | // SegmentMerger: join segments into linestrings and possibly into rings of |
30 | | // polygons |
31 | | template <typename LineWriter, typename LevelGenerator> struct SegmentMerger |
32 | | { |
33 | | struct LineStringEx |
34 | | { |
35 | | LineString ls = LineString(); |
36 | | bool isMerged = false; |
37 | | }; |
38 | | |
39 | | // a collection of unmerged linestrings |
40 | | typedef std::list<LineStringEx> Lines; |
41 | | |
42 | | SegmentMerger(LineWriter &lineWriter, const LevelGenerator &levelGenerator, |
43 | | bool polygonize_) |
44 | 0 | : polygonize(polygonize_), lineWriter_(lineWriter), lines_(), |
45 | 0 | endpointIndex_(), levelGenerator_(levelGenerator), m_anSkipLevels() |
46 | 0 | { |
47 | 0 | } Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::SegmentMerger(GDALRingAppender&, marching_squares::IntervalLevelRangeIterator const&, bool) Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::SegmentMerger(marching_squares::PolygonRingAppender<PolygonContourWriter>&, marching_squares::FixedLevelRangeIterator const&, bool) Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::SegmentMerger(GDALRingAppender&, marching_squares::FixedLevelRangeIterator const&, bool) |
48 | | |
49 | | ~SegmentMerger() |
50 | 0 | { |
51 | 0 | if (polygonize) |
52 | 0 | { |
53 | 0 | for (auto it = lines_.begin(); it != lines_.end(); ++it) |
54 | 0 | { |
55 | 0 | if (!it->second.empty()) |
56 | 0 | debug("remaining unclosed contour"); |
57 | 0 | } |
58 | 0 | } |
59 | | // write all remaining (non-closed) lines |
60 | 0 | for (auto it = lines_.begin(); it != lines_.end(); ++it) |
61 | 0 | { |
62 | 0 | const int levelIdx = it->first; |
63 | | |
64 | | // Skip levels that should be skipped |
65 | 0 | if (std::find(m_anSkipLevels.begin(), m_anSkipLevels.end(), |
66 | 0 | levelIdx) != m_anSkipLevels.end()) |
67 | 0 | { |
68 | 0 | continue; |
69 | 0 | } |
70 | 0 | while (it->second.begin() != it->second.end()) |
71 | 0 | { |
72 | 0 | lineWriter_.addLine(levelGenerator_.level(levelIdx), |
73 | 0 | it->second.begin()->ls, /* closed */ false); |
74 | 0 | it->second.pop_front(); |
75 | 0 | } |
76 | 0 | } |
77 | 0 | } Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::~SegmentMerger() Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::~SegmentMerger() Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::~SegmentMerger() |
78 | | |
79 | | void addSegment(int levelIdx, const Point &start, const Point &end) |
80 | 0 | { |
81 | 0 | addSegment_(levelIdx, start, end); |
82 | 0 | } Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::addSegment(int, marching_squares::Point const&, marching_squares::Point const&) Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::addSegment(int, marching_squares::Point const&, marching_squares::Point const&) Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::addSegment(int, marching_squares::Point const&, marching_squares::Point const&) |
83 | | |
84 | | void addBorderSegment(int levelIdx, const Point &start, const Point &end) |
85 | 0 | { |
86 | 0 | addSegment_(levelIdx, start, end); |
87 | 0 | } Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::addBorderSegment(int, marching_squares::Point const&, marching_squares::Point const&) Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::addBorderSegment(int, marching_squares::Point const&, marching_squares::Point const&) Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::addBorderSegment(int, marching_squares::Point const&, marching_squares::Point const&) |
88 | | |
89 | | void beginningOfLine() |
90 | 0 | { |
91 | 0 | if (polygonize) |
92 | 0 | return; |
93 | | |
94 | | // mark lines as non merged |
95 | 0 | for (auto &l : lines_) |
96 | 0 | { |
97 | 0 | for (auto &ls : l.second) |
98 | 0 | { |
99 | 0 | ls.isMerged = false; |
100 | 0 | } |
101 | 0 | } |
102 | 0 | } Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::beginningOfLine() Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::beginningOfLine() Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::beginningOfLine() |
103 | | |
104 | | void endOfLine() |
105 | 0 | { |
106 | 0 | if (polygonize) |
107 | 0 | return; |
108 | | |
109 | | // At the end of the line, we know that if no segment has been merged to |
110 | | // an existing line, it means there won't be anything more in the |
111 | | // future, we can then emit the line (this both speeds up and saves |
112 | | // memory) |
113 | | |
114 | 0 | for (auto &l : lines_) |
115 | 0 | { |
116 | 0 | const int levelIdx = l.first; |
117 | 0 | auto it = l.second.begin(); |
118 | 0 | while (it != l.second.end()) |
119 | 0 | { |
120 | 0 | if (!it->isMerged) |
121 | 0 | { |
122 | | // Note that emitLine_ erases `it` and returns an iterator |
123 | | // advanced to the next element. |
124 | 0 | it = emitLine_(levelIdx, it, /* closed */ false); |
125 | 0 | } |
126 | 0 | else |
127 | 0 | { |
128 | 0 | ++it; |
129 | 0 | } |
130 | 0 | } |
131 | 0 | } |
132 | 0 | } Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::endOfLine() Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::endOfLine() Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::endOfLine() |
133 | | |
134 | | // non copyable |
135 | | SegmentMerger(const SegmentMerger<LineWriter, LevelGenerator> &) = delete; |
136 | | SegmentMerger<LineWriter, LevelGenerator> & |
137 | | operator=(const SegmentMerger<LineWriter, LevelGenerator> &) = delete; |
138 | | |
139 | | /** |
140 | | * @brief setSkipLevels sets the levels that should be skipped |
141 | | * when polygonize option is set. |
142 | | * @param anSkipLevels integer 0-based levels to skip. |
143 | | */ |
144 | | void setSkipLevels(const std::vector<int> &anSkipLevels) |
145 | 0 | { |
146 | | // Warn if polygonize is not set |
147 | 0 | if (!polygonize) |
148 | 0 | { |
149 | 0 | CPLError( |
150 | 0 | CE_Warning, CPLE_NotSupported, |
151 | 0 | "setSkipLevels is ignored when polygonize option is not set"); |
152 | 0 | } |
153 | 0 | m_anSkipLevels = anSkipLevels; |
154 | 0 | } |
155 | | |
156 | | const bool polygonize; |
157 | | |
158 | | private: |
159 | | LineWriter &lineWriter_; |
160 | | // lines of each level |
161 | | std::map<int, Lines> lines_; |
162 | | |
163 | | // Index of the open lines of each level, keyed by their two endpoints. |
164 | | // Marching squares emits segments whose endpoints have degree two, so a |
165 | | // point can only ever be shared by two segment ends; a lookup here |
166 | | // replaces a linear scan over all open lines, which is quadratic on |
167 | | // rasters that keep many lines open at once (e.g. many long parallel |
168 | | // contours crossing each scanline). |
169 | | struct PointCompare |
170 | | { |
171 | | bool operator()(const Point &a, const Point &b) const |
172 | 0 | { |
173 | 0 | return a.x < b.x || (a.x == b.x && a.y < b.y); |
174 | 0 | } Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::PointCompare::operator()(marching_squares::Point const&, marching_squares::Point const&) const Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::PointCompare::operator()(marching_squares::Point const&, marching_squares::Point const&) const Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::PointCompare::operator()(marching_squares::Point const&, marching_squares::Point const&) const |
175 | | }; |
176 | | |
177 | | // A marching squares point has degree two, so at most two line ends can |
178 | | // sit on it (the two ends of the same line, when it has closed): a fixed |
179 | | // two-slot entry avoids a heap allocation per endpoint. |
180 | | struct EndpointEntry |
181 | | { |
182 | | typename Lines::iterator its[2]; |
183 | | int n = 0; |
184 | | }; |
185 | | |
186 | | typedef std::map<Point, EndpointEntry, PointCompare> EndpointIndex; |
187 | | std::map<int, EndpointIndex> endpointIndex_; |
188 | | |
189 | | const LevelGenerator &levelGenerator_; |
190 | | |
191 | | // Store 0-indexed levels to skip when polygonize option is set |
192 | | std::vector<int> m_anSkipLevels; |
193 | | |
194 | | void registerEndpoint_(EndpointIndex &idx, typename Lines::iterator it, |
195 | | const Point &p) |
196 | 0 | { |
197 | 0 | EndpointEntry &e = idx[p]; |
198 | 0 | assert(e.n < 2); |
199 | 0 | e.its[e.n++] = it; |
200 | 0 | } Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::registerEndpoint_(std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::EndpointEntry> > >&, std::__1::__list_iterator<marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::LineStringEx, void*>, marching_squares::Point const&) Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::registerEndpoint_(std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::EndpointEntry> > >&, std::__1::__list_iterator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::LineStringEx, void*>, marching_squares::Point const&) Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::registerEndpoint_(std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::EndpointEntry> > >&, std::__1::__list_iterator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::LineStringEx, void*>, marching_squares::Point const&) |
201 | | |
202 | | void unregisterEndpoint_(EndpointIndex &idx, typename Lines::iterator it, |
203 | | const Point &p) |
204 | 0 | { |
205 | 0 | auto f = idx.find(p); |
206 | 0 | assert(f != idx.end()); |
207 | 0 | EndpointEntry &e = f->second; |
208 | 0 | if (e.its[0] == it) |
209 | 0 | e.its[0] = e.its[1]; |
210 | 0 | else |
211 | 0 | assert(e.n == 2 && e.its[1] == it); |
212 | 0 | e.n--; |
213 | 0 | if (e.n == 0) |
214 | 0 | idx.erase(f); |
215 | 0 | } Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::unregisterEndpoint_(std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::EndpointEntry> > >&, std::__1::__list_iterator<marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::LineStringEx, void*>, marching_squares::Point const&) Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::unregisterEndpoint_(std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::EndpointEntry> > >&, std::__1::__list_iterator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::LineStringEx, void*>, marching_squares::Point const&) Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::unregisterEndpoint_(std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::EndpointEntry> > >&, std::__1::__list_iterator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::LineStringEx, void*>, marching_squares::Point const&) |
216 | | |
217 | | void addSegment_(int levelIdx, const Point &start, const Point &end) |
218 | 0 | { |
219 | |
|
220 | 0 | Lines &lines = lines_[levelIdx]; |
221 | |
|
222 | 0 | if (start == end) |
223 | 0 | { |
224 | 0 | debug("degenerate segment (%f %f)", start.x, start.y); |
225 | 0 | return; |
226 | 0 | } |
227 | | |
228 | 0 | auto idxIt = endpointIndex_.find(levelIdx); |
229 | 0 | if (idxIt == endpointIndex_.end()) |
230 | 0 | { |
231 | 0 | addSegmentLinear_(levelIdx, lines, start, end); |
232 | | // The linear scans above are quadratic when many lines stay open |
233 | | // at once (e.g. long parallel contours crossing each scanline): |
234 | | // past this size, switch the level to the endpoint index. Below |
235 | | // it, the scans are cheaper than maintaining the index. |
236 | 0 | constexpr std::size_t INDEX_THRESHOLD = 100; |
237 | 0 | if (lines.size() > INDEX_THRESHOLD) |
238 | 0 | { |
239 | 0 | EndpointIndex &idx = endpointIndex_[levelIdx]; |
240 | 0 | for (auto it = lines.begin(); it != lines.end(); ++it) |
241 | 0 | { |
242 | 0 | registerEndpoint_(idx, it, it->ls.front()); |
243 | 0 | registerEndpoint_(idx, it, it->ls.back()); |
244 | 0 | } |
245 | 0 | } |
246 | 0 | } |
247 | 0 | else |
248 | 0 | { |
249 | 0 | addSegmentIndexed_(levelIdx, lines, idxIt->second, start, end); |
250 | 0 | } |
251 | 0 | } Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::addSegment_(int, marching_squares::Point const&, marching_squares::Point const&) Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::addSegment_(int, marching_squares::Point const&, marching_squares::Point const&) Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::addSegment_(int, marching_squares::Point const&, marching_squares::Point const&) |
252 | | |
253 | | // Merge the segment by scanning the open lines: cheap while there are |
254 | | // few of them, quadratic when there are many. |
255 | | void addSegmentLinear_(int levelIdx, Lines &lines, const Point &start, |
256 | | const Point &end) |
257 | 0 | { |
258 | | // attempt to merge segment with existing line |
259 | 0 | auto it = lines.begin(); |
260 | 0 | for (; it != lines.end(); ++it) |
261 | 0 | { |
262 | 0 | if (it->ls.back() == end) |
263 | 0 | { |
264 | 0 | it->ls.push_back(start); |
265 | 0 | it->isMerged = true; |
266 | 0 | break; |
267 | 0 | } |
268 | 0 | if (it->ls.front() == end) |
269 | 0 | { |
270 | 0 | it->ls.push_front(start); |
271 | 0 | it->isMerged = true; |
272 | 0 | break; |
273 | 0 | } |
274 | 0 | if (it->ls.back() == start) |
275 | 0 | { |
276 | 0 | it->ls.push_back(end); |
277 | 0 | it->isMerged = true; |
278 | 0 | break; |
279 | 0 | } |
280 | 0 | if (it->ls.front() == start) |
281 | 0 | { |
282 | 0 | it->ls.push_front(end); |
283 | 0 | it->isMerged = true; |
284 | 0 | break; |
285 | 0 | } |
286 | 0 | } |
287 | |
|
288 | 0 | if (it == lines.end()) |
289 | 0 | { |
290 | | // new line |
291 | 0 | lines.push_back(LineStringEx()); |
292 | 0 | lines.back().ls.push_back(start); |
293 | 0 | lines.back().ls.push_back(end); |
294 | 0 | lines.back().isMerged = true; |
295 | 0 | } |
296 | 0 | else if (polygonize && (it->ls.front() == it->ls.back())) |
297 | 0 | { |
298 | | // ring closed |
299 | 0 | emitLine_(levelIdx, it, /* closed */ true); |
300 | 0 | return; |
301 | 0 | } |
302 | 0 | else |
303 | 0 | { |
304 | | // try to perform linemerge with another line |
305 | | // since we got out of the previous loop on the first match |
306 | | // there is no need to test previous elements |
307 | | // also: a segment merges at most two lines, no need to stall here |
308 | | // ;) |
309 | 0 | auto other = it; |
310 | 0 | ++other; |
311 | 0 | for (; other != lines.end(); ++other) |
312 | 0 | { |
313 | 0 | if (it->ls.back() == other->ls.front()) |
314 | 0 | { |
315 | 0 | it->ls.pop_back(); |
316 | 0 | it->ls.splice(it->ls.end(), other->ls); |
317 | 0 | it->isMerged = true; |
318 | 0 | lines.erase(other); |
319 | | // if that makes a closed ring, returns it |
320 | 0 | if (it->ls.front() == it->ls.back()) |
321 | 0 | emitLine_(levelIdx, it, /* closed */ true); |
322 | 0 | break; |
323 | 0 | } |
324 | 0 | else if (other->ls.back() == it->ls.front()) |
325 | 0 | { |
326 | 0 | it->ls.pop_front(); |
327 | 0 | other->ls.splice(other->ls.end(), it->ls); |
328 | 0 | other->isMerged = true; |
329 | 0 | lines.erase(it); |
330 | | // if that makes a closed ring, returns it |
331 | 0 | if (other->ls.front() == other->ls.back()) |
332 | 0 | emitLine_(levelIdx, other, /* closed */ true); |
333 | 0 | break; |
334 | 0 | } |
335 | | // two lists must be merged but one is in the opposite direction |
336 | 0 | else if (it->ls.back() == other->ls.back()) |
337 | 0 | { |
338 | 0 | it->ls.pop_back(); |
339 | 0 | for (auto rit = other->ls.rbegin(); rit != other->ls.rend(); |
340 | 0 | ++rit) |
341 | 0 | { |
342 | 0 | it->ls.push_back(*rit); |
343 | 0 | } |
344 | 0 | it->isMerged = true; |
345 | 0 | lines.erase(other); |
346 | | // if that makes a closed ring, returns it |
347 | 0 | if (it->ls.front() == it->ls.back()) |
348 | 0 | emitLine_(levelIdx, it, /* closed */ true); |
349 | 0 | break; |
350 | 0 | } |
351 | 0 | else if (it->ls.front() == other->ls.front()) |
352 | 0 | { |
353 | 0 | it->ls.pop_front(); |
354 | 0 | for (auto rit = other->ls.begin(); rit != other->ls.end(); |
355 | 0 | ++rit) |
356 | 0 | { |
357 | 0 | it->ls.push_front(*rit); |
358 | 0 | } |
359 | 0 | it->isMerged = true; |
360 | 0 | lines.erase(other); |
361 | | // if that makes a closed ring, returns it |
362 | 0 | if (it->ls.front() == it->ls.back()) |
363 | 0 | emitLine_(levelIdx, it, /* closed */ true); |
364 | 0 | break; |
365 | 0 | } |
366 | 0 | } |
367 | 0 | } |
368 | 0 | } Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::addSegmentLinear_(int, std::__1::list<marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::LineStringEx, std::__1::allocator<marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::LineStringEx> >&, marching_squares::Point const&, marching_squares::Point const&) Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::addSegmentLinear_(int, std::__1::list<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::LineStringEx, std::__1::allocator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::LineStringEx> >&, marching_squares::Point const&, marching_squares::Point const&) Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::addSegmentLinear_(int, std::__1::list<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::LineStringEx, std::__1::allocator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::LineStringEx> >&, marching_squares::Point const&, marching_squares::Point const&) |
369 | | |
370 | | // Merge the segment through the endpoint index. Same merging rules as |
371 | | // the linear scan (a marching squares point has degree two, so at most |
372 | | // one line can match each of the segment's endpoints), but O(log n) |
373 | | // lookups instead of O(n) scans. |
374 | | void addSegmentIndexed_(int levelIdx, Lines &lines, EndpointIndex &idx, |
375 | | const Point &start, const Point &end) |
376 | 0 | { |
377 | | // attempt to merge the segment with an existing line whose endpoint |
378 | | // matches one of the segment's endpoints |
379 | 0 | auto findLine = [&](const Point &p) |
380 | 0 | { |
381 | 0 | auto f = idx.find(p); |
382 | 0 | return f == idx.end() ? lines.end() : f->second.its[0]; |
383 | 0 | }; Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::addSegmentIndexed_(int, std::__1::list<marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::LineStringEx, std::__1::allocator<marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::LineStringEx> >&, std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::EndpointEntry> > >&, marching_squares::Point const&, marching_squares::Point const&)::{lambda(marching_squares::Point const&)#1}::operator()(marching_squares::Point const&) constUnexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::addSegmentIndexed_(int, std::__1::list<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::LineStringEx, std::__1::allocator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::LineStringEx> >&, std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::EndpointEntry> > >&, marching_squares::Point const&, marching_squares::Point const&)::{lambda(marching_squares::Point const&)#1}::operator()(marching_squares::Point const&) constUnexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::addSegmentIndexed_(int, std::__1::list<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::LineStringEx, std::__1::allocator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::LineStringEx> >&, std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::EndpointEntry> > >&, marching_squares::Point const&, marching_squares::Point const&)::{lambda(marching_squares::Point const&)#1}::operator()(marching_squares::Point const&) const |
384 | |
|
385 | 0 | Point matched = end; |
386 | 0 | Point added = start; |
387 | 0 | typename Lines::iterator it = findLine(end); |
388 | 0 | if (it == lines.end()) |
389 | 0 | { |
390 | 0 | matched = start; |
391 | 0 | added = end; |
392 | 0 | it = findLine(start); |
393 | 0 | } |
394 | |
|
395 | 0 | if (it == lines.end()) |
396 | 0 | { |
397 | | // new line |
398 | 0 | lines.push_back(LineStringEx()); |
399 | 0 | it = std::prev(lines.end()); |
400 | 0 | it->ls.push_back(start); |
401 | 0 | it->ls.push_back(end); |
402 | 0 | it->isMerged = true; |
403 | 0 | registerEndpoint_(idx, it, start); |
404 | 0 | registerEndpoint_(idx, it, end); |
405 | 0 | return; |
406 | 0 | } |
407 | | |
408 | | // extend the matched line with the segment's other endpoint |
409 | 0 | unregisterEndpoint_(idx, it, matched); |
410 | 0 | if (it->ls.back() == matched) |
411 | 0 | it->ls.push_back(added); |
412 | 0 | else |
413 | 0 | it->ls.push_front(added); |
414 | 0 | it->isMerged = true; |
415 | 0 | registerEndpoint_(idx, it, added); |
416 | | |
417 | | // The extension may close a ring, or bring the line's new endpoint |
418 | | // onto another line's endpoint; merging two lines moves the free |
419 | | // endpoint again, so iterate until neither applies. |
420 | 0 | for (;;) |
421 | 0 | { |
422 | 0 | if (polygonize && it->ls.front() == it->ls.back()) |
423 | 0 | { |
424 | | // ring closed |
425 | 0 | emitLine_(levelIdx, it, /* closed */ true); |
426 | 0 | return; |
427 | 0 | } |
428 | | // is there another line ending at `added`? |
429 | 0 | auto f = idx.find(added); |
430 | 0 | assert(f != idx.end()); |
431 | 0 | typename Lines::iterator other = lines.end(); |
432 | 0 | for (int i = 0; i < f->second.n; i++) |
433 | 0 | { |
434 | 0 | if (f->second.its[i] != it) |
435 | 0 | { |
436 | 0 | other = f->second.its[i]; |
437 | 0 | break; |
438 | 0 | } |
439 | 0 | } |
440 | 0 | if (other == lines.end()) |
441 | 0 | return; |
442 | | |
443 | | // merge `other` into `it` at the shared point `added` |
444 | 0 | unregisterEndpoint_(idx, it, added); |
445 | 0 | unregisterEndpoint_(idx, other, added); |
446 | 0 | const Point otherEnd = other->ls.front() == added |
447 | 0 | ? other->ls.back() |
448 | 0 | : other->ls.front(); |
449 | 0 | unregisterEndpoint_(idx, other, otherEnd); |
450 | 0 | if (it->ls.back() == added) |
451 | 0 | { |
452 | 0 | it->ls.pop_back(); |
453 | 0 | if (other->ls.front() == added) |
454 | 0 | { |
455 | 0 | it->ls.splice(it->ls.end(), other->ls); |
456 | 0 | } |
457 | 0 | else |
458 | 0 | { |
459 | | // opposite direction: append reversed |
460 | 0 | for (auto rit = other->ls.rbegin(); rit != other->ls.rend(); |
461 | 0 | ++rit) |
462 | 0 | { |
463 | 0 | it->ls.push_back(*rit); |
464 | 0 | } |
465 | 0 | } |
466 | 0 | } |
467 | 0 | else |
468 | 0 | { |
469 | 0 | it->ls.pop_front(); |
470 | 0 | if (other->ls.back() == added) |
471 | 0 | { |
472 | 0 | it->ls.splice(it->ls.begin(), other->ls); |
473 | 0 | } |
474 | 0 | else |
475 | 0 | { |
476 | | // opposite direction: prepend reversed |
477 | 0 | for (auto rit = other->ls.begin(); rit != other->ls.end(); |
478 | 0 | ++rit) |
479 | 0 | { |
480 | 0 | it->ls.push_front(*rit); |
481 | 0 | } |
482 | 0 | } |
483 | 0 | } |
484 | 0 | lines.erase(other); |
485 | 0 | registerEndpoint_(idx, it, otherEnd); |
486 | 0 | added = otherEnd; |
487 | 0 | } |
488 | 0 | } Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::addSegmentIndexed_(int, std::__1::list<marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::LineStringEx, std::__1::allocator<marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::LineStringEx> >&, std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::EndpointEntry> > >&, marching_squares::Point const&, marching_squares::Point const&) Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::addSegmentIndexed_(int, std::__1::list<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::LineStringEx, std::__1::allocator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::LineStringEx> >&, std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::EndpointEntry> > >&, marching_squares::Point const&, marching_squares::Point const&) Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::addSegmentIndexed_(int, std::__1::list<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::LineStringEx, std::__1::allocator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::LineStringEx> >&, std::__1::map<marching_squares::Point, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::EndpointEntry, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::PointCompare, std::__1::allocator<std::__1::pair<marching_squares::Point const, marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::EndpointEntry> > >&, marching_squares::Point const&, marching_squares::Point const&) |
489 | | |
490 | | typename Lines::iterator emitLine_(int levelIdx, |
491 | | typename Lines::iterator it, bool closed) |
492 | 0 | { |
493 | |
|
494 | 0 | Lines &lines = lines_[levelIdx]; |
495 | 0 | if (lines.empty()) |
496 | 0 | lines_.erase(levelIdx); |
497 | |
|
498 | 0 | auto idxIt = endpointIndex_.find(levelIdx); |
499 | 0 | if (idxIt != endpointIndex_.end()) |
500 | 0 | { |
501 | 0 | unregisterEndpoint_(idxIt->second, it, it->ls.front()); |
502 | 0 | unregisterEndpoint_(idxIt->second, it, it->ls.back()); |
503 | | // An emptied index means no line is open at this level anymore: |
504 | | // drop it so the level returns to the linear path until it grows |
505 | | // past the threshold again. |
506 | 0 | if (idxIt->second.empty()) |
507 | 0 | endpointIndex_.erase(idxIt); |
508 | 0 | } |
509 | | |
510 | | // consume "it" and remove it from the list |
511 | | // but clear the line if the level should be skipped |
512 | 0 | if (std::find(m_anSkipLevels.begin(), m_anSkipLevels.end(), levelIdx) != |
513 | 0 | m_anSkipLevels.end()) |
514 | 0 | { |
515 | 0 | it->ls.clear(); |
516 | 0 | } |
517 | 0 | lineWriter_.addLine(levelGenerator_.level(levelIdx), it->ls, closed); |
518 | 0 | return lines.erase(it); |
519 | 0 | } Unexecuted instantiation: marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::emitLine_(int, std::__1::__list_iterator<marching_squares::SegmentMerger<marching_squares::PolygonRingAppender<PolygonContourWriter>, marching_squares::FixedLevelRangeIterator>::LineStringEx, void*>, bool) Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::emitLine_(int, std::__1::__list_iterator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::FixedLevelRangeIterator>::LineStringEx, void*>, bool) Unexecuted instantiation: marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::emitLine_(int, std::__1::__list_iterator<marching_squares::SegmentMerger<GDALRingAppender, marching_squares::IntervalLevelRangeIterator>::LineStringEx, void*>, bool) |
520 | | }; |
521 | | |
522 | | } // namespace marching_squares |
523 | | #endif |