version 3.10-dev
grahamconvexhull.hh
Go to the documentation of this file.
1// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set et ts=4 sw=4 sts=4:
3//
4// SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
5// SPDX-License-Identifier: GPL-3.0-or-later
6//
12#ifndef DUMUX_GEOMETRY_GRAHAM_CONVEX_HULL_HH
13#define DUMUX_GEOMETRY_GRAHAM_CONVEX_HULL_HH
14
15#include <vector>
16#include <array>
17#include <algorithm>
18#include <iterator>
19
20#include <dune/common/exceptions.hh>
21#include <dune/common/fvector.hh>
22
23#include <dumux/common/math.hh>
24
25namespace Dumux {
26
34template<class ctype>
35int getOrientation(const Dune::FieldVector<ctype, 3>& a,
36 const Dune::FieldVector<ctype, 3>& b,
37 const Dune::FieldVector<ctype, 3>& c,
38 const Dune::FieldVector<ctype, 3>& normal)
39{
40 const auto d = b-a;
41 const auto e = c-b;
42 const auto f = Dumux::crossProduct(d, e);
43 const auto area = f*normal;
44 return Dumux::sign(-area);
45}
46
54template<int dim, class ctype,
55 std::enable_if_t<(dim==2), int> = 0>
56std::vector<Dune::FieldVector<ctype, 3>>
57grahamConvexHullImpl(std::vector<Dune::FieldVector<ctype, 3>>& points)
58{
59 using Point = Dune::FieldVector<ctype, 3>;
60 std::vector<Point> convexHull;
61
62 // return empty convex hull
63 if (points.size() < 3)
64 return convexHull;
65
66 // return the points (already just one triangle)
67 if (points.size() == 3)
68 return points;
69
70 // try to compute the normal vector of the plane
71 const auto a = points[1] - points[0];
72 auto b = points[2] - points[0];
73 auto normal = Dumux::crossProduct(a, b);
74
75 // make sure the normal vector has non-zero length
76 std::size_t k = 2;
77 auto norm = normal.two_norm();
78 while (norm == 0.0 && k < points.size()-1)
79 {
80 b = points[++k];
82 norm = normal.two_norm();
83 }
84
85 // if all given points are colinear -> return empty convex hull
86 if (norm == 0.0)
87 return convexHull;
88
89 using std::sqrt;
90 const auto eps = 1e-7*sqrt(norm);
91 normal /= norm;
92
93 // find the element with the smallest x coordinate (if x is the same, smallest y coordinate, and so on...)
94 auto minIt = std::min_element(points.begin(), points.end(), [&eps](const auto& a, const auto& b)
95 {
96 using std::abs;
97 return (abs(a[0]-b[0]) > eps ? a[0] < b[0] : (abs(a[1]-b[1]) > eps ? a[1] < b[1] : (a[2] < b[2])));
98 });
99
100 // swap the smallest element to the front
101 std::iter_swap(minIt, points.begin());
102
103 // choose the first (min element) as the pivot point
104 // sort in counter-clockwise order around pivot point
105 const auto pivot = points[0];
106 std::sort(points.begin()+1, points.end(), [&](const auto& a, const auto& b)
107 {
108 const int order = getOrientation(pivot, a, b, normal);
109 if (order == 0)
110 return (a-pivot).two_norm() < (b-pivot).two_norm();
111 else
112 return (order == -1);
113 });
114
115 // push the first three points
116 convexHull.reserve(50);
117 convexHull.push_back(points[0]);
118 convexHull.push_back(points[1]);
119 convexHull.push_back(points[2]);
120
121 // This is the heart of the algorithm
122 // pop_back until the last point in the queue forms a counter-clockwise oriented line
123 // with the next two vertices. Then add these points to the queue.
124 for (std::size_t i = 3; i < points.size(); ++i)
125 {
126 Point p = convexHull.back();
127 convexHull.pop_back();
128 // keep popping until the orientation a->b->currentp is counter-clockwise
129 while (getOrientation(convexHull.back(), p, points[i], normal) != -1)
130 {
131 // make sure the queue doesn't get empty
132 if (convexHull.size() == 1)
133 {
134 // before we reach i=size-1 there has to be a good candidate
135 // as not all points are colinear (a non-zero plane normal exists)
136 assert(i < points.size()-1);
137 p = points[i++];
138 }
139 else
140 {
141 p = convexHull.back();
142 convexHull.pop_back();
143 }
144 }
145
146 // add back the last popped point and this point
147 convexHull.emplace_back(std::move(p));
148 convexHull.push_back(points[i]);
149 }
150
151 return convexHull;
152}
153
160template<int dim, class ctype,
161 std::enable_if_t<(dim==2), int> = 0>
162std::vector<Dune::FieldVector<ctype, 2>>
163grahamConvexHullImpl(const std::vector<Dune::FieldVector<ctype, 2>>& points)
164{
165 std::vector<Dune::FieldVector<ctype, 3>> points3D;
166 points3D.reserve(points.size());
167 std::transform(points.begin(), points.end(), std::back_inserter(points3D),
168 [](const auto& p) { return Dune::FieldVector<ctype, 3>({p[0], p[1], 0.0}); });
169
170 const auto result3D = grahamConvexHullImpl<2>(points3D);
171
172 std::vector<Dune::FieldVector<ctype, 2>> result2D;
173 result2D.reserve(result3D.size());
174 std::transform(result3D.begin(), result3D.end(), std::back_inserter(result2D),
175 [](const auto& p) { return Dune::FieldVector<ctype, 2>({p[0], p[1]}); });
176
177 return result2D;
178}
179
185template<int dim, class ctype, int dimWorld>
186std::vector<Dune::FieldVector<ctype, dimWorld>> grahamConvexHull(std::vector<Dune::FieldVector<ctype, dimWorld>>& points)
187{
188 return grahamConvexHullImpl<dim>(points);
189}
190
197template<int dim, class ctype, int dimWorld>
198std::vector<Dune::FieldVector<ctype, dimWorld>> grahamConvexHull(const std::vector<Dune::FieldVector<ctype, dimWorld>>& points)
199{
200 auto copyPoints = points;
201 return grahamConvexHullImpl<dim>(copyPoints);
202}
203
204} // end namespace Dumux
205
206#endif
Dune::FieldVector< Scalar, 3 > crossProduct(const Dune::FieldVector< Scalar, 3 > &vec1, const Dune::FieldVector< Scalar, 3 > &vec2)
Cross product of two vectors in three-dimensional Euclidean space.
Definition: math.hh:671
constexpr int sign(const ValueType &value) noexcept
Sign or signum function.
Definition: math.hh:658
std::vector< Dune::FieldVector< ctype, 3 > > grahamConvexHullImpl(std::vector< Dune::FieldVector< ctype, 3 > > &points)
Compute the points making up the convex hull around the given set of unordered points.
Definition: grahamconvexhull.hh:57
int getOrientation(const Dune::FieldVector< ctype, 3 > &a, const Dune::FieldVector< ctype, 3 > &b, const Dune::FieldVector< ctype, 3 > &c, const Dune::FieldVector< ctype, 3 > &normal)
Returns the orientation of a sequence a-->b-->c in one plane (defined by normal vector)
Definition: grahamconvexhull.hh:35
Vector normal(const Vector &v)
Create a vector normal to the given one (v is expected to be non-zero)
Definition: normal.hh:26
std::vector< Dune::FieldVector< ctype, dimWorld > > grahamConvexHull(const std::vector< Dune::FieldVector< ctype, dimWorld > > &points)
Compute the points making up the convex hull around the given set of unordered points.
Definition: grahamconvexhull.hh:198
Define some often used mathematical functions.
Definition: adapt.hh:17