h5geo 0.4.0
C++17 and python API to work with geo-data (seismic, wells, maps, other in process) based on HDF5. Aimed at geoscientists and developers.
Loading...
Searching...
No Matches
h5easyhull.h
1#ifndef H5EASYHULL_H
2#define H5EASYHULL_H
3
4// Implementations of various convex hull algorithms
5// using the C++ Standard Library.
6// For clarity, the implementations do not check for
7// duplicate or collinear points.
8
9#include <algorithm>
10#include <iostream>
11#include <vector>
12
13#include <Eigen/Dense>
14
15
16namespace h5geo{
17
25template<typename T1, typename T2>
26T1 _ccw(const Eigen::Vector2<T1>& a,
27 const Eigen::Vector2<T1>& b,
28 const Eigen::MatrixBase<T2>& c) {
29 return (b.x() - a.x()) * (c.y() - a.y()) - (b.y() - a.y()) * (c.x() - a.x());
30}
31
36template<typename T>
37bool _isLeftOf(const Eigen::Vector2<T>& a,
38 const Eigen::Vector2<T>& b) {
39 return (a.x() < b.x() || (a.x() == b.x() && a.y() < b.y()));
40}
41
43template<typename T>
44struct _ccwSorter {
45 const Eigen::Vector2<T>& pivot;
46
47 _ccwSorter(const Eigen::Vector2<T>& inPivot) : pivot(inPivot) { }
48
49 bool operator()(const Eigen::Vector2<T>& a, const Eigen::Vector2<T>& b) {
50 return _ccw(pivot, a, b) < 0;
51 }
52};
53
58template<typename T>
59T _segmentLen(const Eigen::Vector2<T>& a,
60 const Eigen::Vector2<T>& b) {
61 return sqrt((b.x() - a.x()) * (b.x() - a.x()) + (b.y() - a.y()) * (b.y() - a.y()));
62}
63
69template<typename T1, typename T2>
70T1 _segmentDist(const Eigen::Vector2<T1>& a,
71 const Eigen::Vector2<T1>& b,
72 const Eigen::MatrixBase<T2>& p) {
73 return fabs((b.x() - a.x()) * (a.y() - p.y()) - (b.y() - a.y()) * (a.x() - p.x())) / _segmentLen(a, b);
74}
75
81template<typename T>
82size_t _getFarthestInd(const Eigen::Vector2<T>& a,
83 const Eigen::Vector2<T>& b,
84 const std::vector<Eigen::Vector2<T> >& v) {
85 size_t idxMax = 0;
86 T distMax = _segmentDist(a, b, v[idxMax]);
87
88 for (size_t i = 1; i < v.size(); ++i) {
89 T distCurr = _segmentDist(a, b, v[i]);
90 if (distCurr > distMax) {
91 idxMax = i;
92 distMax = distCurr;
93 }
94 }
95
96 return idxMax;
97}
98
99template<typename T1, typename T2>
100size_t _getFarthestInd(const Eigen::Vector2<T1>& a,
101 const Eigen::Vector2<T1>& b,
102 const Eigen::MatrixX2<T2>& v) {
103 size_t idxMax = 0;
104 T1 distMax = _segmentDist(a, b, v.row(idxMax));
105
106 for (size_t i = 1; i < v.rows(); ++i) {
107 T1 distCurr = _segmentDist(a, b, v.row(i));
108 if (distCurr > distMax) {
109 idxMax = i;
110 distMax = distCurr;
111 }
112 }
113
114 return idxMax;
115}
116
121template<typename T>
122[[deprecated("Use 'quickHull2D' instead")]]
123std::vector<Eigen::Vector2<T> > giftWrapping(std::vector<Eigen::Vector2<T> > v) {
124 // Move the leftmost point to the beginning of our vector.
125 // It will be the first point in our convext hull.
126 swap(v[0], *min_element(v.begin(), v.end(), _isLeftOf<T>));
127
128 std::vector<Eigen::Vector2<T> > hull;
129 // Repeatedly find the first _ccw point from our last hull point
130 // and put it at the front of our array.
131 // Stop when we see our first point again.
132 do {
133 hull.push_back(v[0]);
134 swap(v[0], *min_element(v.begin() + 1, v.end(), _ccwSorter(v[0])));
135 } while (v[0] != hull[0]);
136
137 return hull;
138}
139
144template<typename T>
145[[deprecated("Use 'quickHull2D' instead")]]
146std::vector<Eigen::Vector2<T> > GrahamScan(std::vector<Eigen::Vector2<T> > v) {
147 // Put our leftmost point at index 0
148 swap(v[0], *min_element(v.begin(), v.end(), _isLeftOf<T>));
149
150 // Sort the rest of the points in counter-clockwise order
151 // from our leftmost point.
152 sort(v.begin() + 1, v.end(), _ccwSorter(v[0]));
153
154 // Add our first three points to the hull.
155 std::vector<Eigen::Vector2<T> > hull;
156 auto it = v.begin();
157 hull.push_back(*it++);
158 hull.push_back(*it++);
159 hull.push_back(*it++);
160
161 while (it != v.end()) {
162 // Pop off any points that make a convex angle with *it
163 while (_ccw(*(hull.rbegin() + 1), *(hull.rbegin()), *it) >= 0) {
164 hull.pop_back();
165 }
166 hull.push_back(*it++);
167 }
168
169 return hull;
170}
171
172
176template<typename T>
177[[deprecated("Use 'quickHull2D' instead")]]
178std::vector<Eigen::Vector2<T> > monotoneChain(std::vector<Eigen::Vector2<T> > v) {
179 // Sort our points in lexicographic order.
180 sort(v.begin(), v.end(), _isLeftOf<T>);
181
182 // Find the lower half of the convex hull.
183 std::vector<Eigen::Vector2<T> > lower;
184 for (auto it = v.begin(); it != v.end(); ++it) {
185 // Pop off any points that make a convex angle with *it
186 while (lower.size() >= 2 && _ccw(*(lower.rbegin() + 1), *(lower.rbegin()), *it) >= 0) {
187 lower.pop_back();
188 }
189 lower.push_back(*it);
190 }
191
192 // Find the upper half of the convex hull.
193 std::vector<Eigen::Vector2<T> > upper;
194 for (auto it = v.rbegin(); it != v.rend(); ++it) {
195 // Pop off any points that make a convex angle with *it
196 while (upper.size() >= 2 && _ccw(*(upper.rbegin() + 1), *(upper.rbegin()), *it) >= 0) {
197 upper.pop_back();
198 }
199 upper.push_back(*it);
200 }
201
202 std::vector<Eigen::Vector2<T> > hull;
203 hull.insert(hull.end(), lower.begin(), lower.end());
204 // Both hulls include both endpoints, so leave them out when we
205 // append the upper hull.
206 hull.insert(hull.end(), upper.begin() + 1, upper.end() - 1);
207 return hull;
208}
209
210
216template<typename T>
218 const std::vector<Eigen::Vector2<T> >& v,
219 const Eigen::Vector2<T>& a,
220 const Eigen::Vector2<T>& b,
221 std::vector<Eigen::Vector2<T> >& hull)
222{
223 if (v.empty()) {
224 return;
225 }
226
227 Eigen::Vector2<T> f = v[_getFarthestInd(a, b, v)];
228
229 // Collect points to the left of segment (a, f)
230 std::vector<Eigen::Vector2<T> > left;
231 for (auto p : v) {
232 if (_ccw(a, f, p) > 0) {
233 left.push_back(p);
234 }
235 }
236 quickHull2D(left, a, f, hull);
237
238 // Add f to the hull
239 hull.push_back(f);
240
241 // Collect points to the left of segment (f, b)
242 std::vector<Eigen::Vector2<T> > right;
243 for (auto p : v) {
244 if (_ccw(f, b, p) > 0) {
245 right.push_back(p);
246 }
247 }
248 quickHull2D(right, f, b, hull);
249}
250
254template<typename T>
255std::vector<Eigen::Vector2<T> > quickHull2D(
256 const std::vector<Eigen::Vector2<T> >& v)
257{
258 // Start with the leftmost and rightmost points.
259 Eigen::Vector2<T> a = *min_element(v.begin(), v.end(), _isLeftOf<T>);
260 Eigen::Vector2<T> b = *max_element(v.begin(), v.end(), _isLeftOf<T>);
261
262 // Split the points on either side of segment (a, b)
263 std::vector<Eigen::Vector2<T> > left, right;
264 for (auto p : v) {
265 _ccw(a, b, p) > 0 ? left.push_back(p) : right.push_back(p);
266 }
267
268 std::vector<Eigen::Vector2<T> > hull;
269 // Be careful to add points to the hull
270 // in the correct order. Add our leftmost point.
271 hull.push_back(a);
272
273 // Add hull points from the left (top)
274 quickHull2D(left, a, b, hull);
275
276 // Add our rightmost point
277 hull.push_back(b);
278
279 // Add hull points from the right (bottom)
280 quickHull2D(right, b, a, hull);
281
282 hull.push_back(hull[0]);
283
284 return hull;
285}
286
293template<typename T>
295 const Eigen::MatrixX2<T>& v,
296 const Eigen::Vector2<T>& a,
297 const Eigen::Vector2<T>& b,
298 Eigen::MatrixX2<T>& hull,
299 ptrdiff_t& ih)
300{
301 if (v.size() == 0) {
302 return;
303 }
304
305 Eigen::Vector2<T> f = v.row(_getFarthestInd(a, b, v));
306
307 // Collect points to the left of segment (a, f)
308 Eigen::MatrixX2<T> left(v.rows(), 2);
309 ptrdiff_t il = 0;
310 for (ptrdiff_t i = 0; i < v.rows(); i++){
311 if (_ccw(a, f, v.row(i)) > 0) {
312 left.row(il) = v.row(i);
313 il++;
314 }
315 }
316 left.conservativeResize(il, 2);
317 quickHull2D(left, a, f, hull, ih);
318
319 // Add f to the hull
320 hull.row(ih) = f;
321 ih++;
322
323 // Collect points to the left of segment (f, b)
324 Eigen::MatrixX2<T> right(v.rows(), 2);
325 ptrdiff_t ir = 0;
326 for (ptrdiff_t i = 0; i < v.rows(); i++){
327 if (_ccw(f, b, v.row(i)) > 0) {
328 right.row(ir) = v.row(i);
329 ir++;
330 }
331 }
332 right.conservativeResize(ir, 2);
333 quickHull2D(right, f, b, hull, ih);
334};
335
339template<typename T>
340Eigen::MatrixX2<T> quickHull2D(
341 const Eigen::MatrixX2<T>& v)
342{
343 // initialize original index locations
344 Eigen::VectorX<ptrdiff_t> idx =
345 Eigen::ArrayX<ptrdiff_t>::LinSpaced(
346 v.rows(), 0, v.rows()-1);
347
348 // Start with the leftmost and rightmost points.
349// std::function<bool(const ptrdiff_t &, const ptrdiff_t &)> _isLeftOf_fun =
350// [&v](
351// const ptrdiff_t& row1,
352// const ptrdiff_t& row2)->bool
353// {
354// return (v(row1,0) < v(row2,0) || (v(row1,0) == v(row2,0) && v(row1,1) < v(row2,1)));
355// };
356
357// ptrdiff_t ia = *min_element(idx.begin(), idx.end(), _isLeftOf_fun);
358// ptrdiff_t ib = *max_element(idx.begin(), idx.end(), _isLeftOf_fun);
359
360// Eigen::Vector2<T> a = v.row(ia);
361// Eigen::Vector2<T> b = v.row(ib);
362
363 // Start with the leftmost and rightmost points.
364 // Using Iterators
365 // https://stackoverflow.com/questions/65573094/stl-iterators-with-eigen-matrices/65582213#65582213
366 Eigen::Vector2<T> a = *std::min_element(
367 v.rowwise().begin(), v.rowwise().end(),
369
370 Eigen::Vector2<T> b = *std::max_element(
371 v.rowwise().begin(), v.rowwise().end(),
373
374 // Split the points on either side of segment (a, b)
375 Eigen::MatrixX2<T> left(v.rows(), 2);
376 Eigen::MatrixX2<T> right(v.rows(), 2);
377 ptrdiff_t il = 0;
378 ptrdiff_t ir = 0;
379 for (ptrdiff_t i = 0; i < v.rows(); i++){
380 if (_ccw(a, b, v.row(i)) > 0){
381 left.row(il) = v.row(i);
382 il++;
383 } else {
384 right.row(ir) = v.row(i);
385 ir++;
386 }
387 }
388 left.conservativeResize(il, 2);
389 right.conservativeResize(ir, 2);
390
391
392 Eigen::MatrixX2<T> hull(v.rows(), 2);
393 ptrdiff_t ih = 0;
394
395 // Be careful to add points to the hull
396 // in the correct order. Add our leftmost point.
397 hull.row(ih) = a;
398 ih++;
399
400 // Add hull points from the left (top)
401 quickHull2D(left, a, b, hull, ih);
402
403 // Add our rightmost point
404 hull.row(ih) = b;
405 ih++;
406
407 // Add hull points from the right (bottom)
408 quickHull2D(right, b, a, hull, ih);
409
410 // close hull
411 hull.row(ih) = hull.row(0);
412 ih++;
413
414 hull.conservativeResize(ih, 2);
415
416 return hull;
417}
418
419}
420
421
422#endif // H5EASYHULL_H
Basic namespace.
Definition h5base.h:29
T1 _ccw(const Eigen::Vector2< T1 > &a, const Eigen::Vector2< T1 > &b, const Eigen::MatrixBase< T2 > &c)
_ccw The z-value of the cross product of segments (a, b) and (a, c). Positive means c is _ccw from (a...
Definition h5easyhull.h:26
bool _isLeftOf(const Eigen::Vector2< T > &a, const Eigen::Vector2< T > &b)
_isLeftOf Returns true if a is lexicographically before b.
Definition h5easyhull.h:37
T1 _segmentDist(const Eigen::Vector2< T1 > &a, const Eigen::Vector2< T1 > &b, const Eigen::MatrixBase< T2 > &p)
_segmentDist The unsigned distance of p from segment (a, b).
Definition h5easyhull.h:70
size_t _getFarthestInd(const Eigen::Vector2< T > &a, const Eigen::Vector2< T > &b, const std::vector< Eigen::Vector2< T > > &v)
_getFarthestInd Returns the index of the farthest point from segment (a, b).
Definition h5easyhull.h:82
T _segmentLen(const Eigen::Vector2< T > &a, const Eigen::Vector2< T > &b)
_segmentLen The length of segment (a, b).
Definition h5easyhull.h:59
std::vector< Eigen::Vector2< T > > giftWrapping(std::vector< Eigen::Vector2< T > > v)
giftWrapping The gift-wrapping algorithm for convex hull. https://en.wikipedia.org/wiki/Gift_wrapping...
Definition h5easyhull.h:123
std::vector< Eigen::Vector2< T > > monotoneChain(std::vector< Eigen::Vector2< T > > v)
monotoneChain The monotone chain algorithm for convex hull.
Definition h5easyhull.h:178
Eigen::VectorX< ptrdiff_t > sort(const Eigen::DenseBase< D > &v)
sort return indexes such that v_sorted = v(ind).
Definition h5sort.h:32
std::vector< Eigen::Vector2< T > > GrahamScan(std::vector< Eigen::Vector2< T > > v)
GrahamScan The Graham scan algorithm for convex hull. https://en.wikipedia.org/wiki/Graham_scan.
Definition h5easyhull.h:146
void quickHull2D(const std::vector< Eigen::Vector2< T > > &v, const Eigen::Vector2< T > &a, const Eigen::Vector2< T > &b, std::vector< Eigen::Vector2< T > > &hull)
quickHull2D Recursive call of the quickHull2D algorithm.
Definition h5easyhull.h:217
The _ccwSorter struct Used to sort points in _ccw order about a pivot.
Definition h5easyhull.h:44