Pangolin
Header-only C++20 plane computational geometry library
Loading...
Searching...
No Matches
mindisk.hpp
Go to the documentation of this file.
1#pragma once
2
4
9
10#include <algorithm>
11#include <cassert>
12#include <cstddef>
13#include <iterator>
14#include <random>
15#include <type_traits>
16#include <utility>
17#include <vector>
18
19
20namespace pgl {
21
22namespace detail {
23
24template <class Container>
25using min_disk_input_point_t =
26 std::remove_cvref_t<decltype(*std::begin(std::declval<const Container&>()))>;
27
28template <class Container>
29using min_disk_result_t =
30 Disk<Point<typename min_disk_input_point_t<Container>::NumberType>>;
31
32template <PointConcept PointType>
33[[nodiscard]] constexpr Disk<PointType> pointDisk(const PointType& point) {
34 return Disk<PointType>(point, point, point);
35}
36
43template <PointConcept PointType>
44[[nodiscard]] constexpr Disk<PointType> diameterDisk(const PointType& first,
45 const PointType& second) {
46 using Number = typename PointType::NumberType;
47 const Number two = static_cast<Number>(2);
48 const PointType midpoint((first.x() + second.x()) / two,
49 (first.y() + second.y()) / two);
50 const PointType radius((second.x() - first.x()) / two,
51 (second.y() - first.y()) / two);
52 const PointType third(midpoint.x() - radius.y(), midpoint.y() + radius.x());
53 return Disk<PointType>(first, second, third);
54}
55
65template <PointConcept PointType>
66[[nodiscard]] constexpr Disk<PointType> threePointDisk(const PointType& first,
67 const PointType& second,
68 const PointType& third) {
69 if (orientationSign(first, second, third) != 0) {
70 return Disk<PointType>(first, second, third);
71 }
72
73 const auto firstSecond = first.template squaredDistance<typename PointType::NumberType>(second);
74 const auto secondThird = second.template squaredDistance<typename PointType::NumberType>(third);
75 const auto thirdFirst = third.template squaredDistance<typename PointType::NumberType>(first);
76 if (firstSecond >= secondThird && firstSecond >= thirdFirst) {
77 return diameterDisk(first, second);
78 }
79 if (secondThird >= thirdFirst) {
80 return diameterDisk(second, third);
81 }
82 return diameterDisk(third, first);
83}
84
85} // namespace detail
86
113template <class Container, class UniformRandomBitGenerator>
114[[nodiscard]] detail::min_disk_result_t<Container>
115smallestEnclosingDisk(const Container& input, UniformRandomBitGenerator&& generator) {
116 using InputPoint = detail::min_disk_input_point_t<Container>;
117 static_assert(PointConcept<InputPoint>,
118 "smallestEnclosingDisk requires a container of pgl::Point values");
119 using ResultNumber = typename InputPoint::NumberType;
120 using ResultPoint = Point<ResultNumber>;
121 using ResultDisk = Disk<ResultPoint>;
122
123 std::vector<ResultPoint> points;
124 for (const auto& point : input) {
125 points.emplace_back(point);
126 }
127 assert(!points.empty());
128 std::shuffle(points.begin(), points.end(),
129 std::forward<UniformRandomBitGenerator>(generator));
130
131 ResultDisk disk = detail::pointDisk(points.front());
132 for (std::size_t i = 1; i < points.size(); ++i) {
133 if (disk.contains(points[i])) {
134 continue;
135 }
136
137 disk = detail::pointDisk(points[i]);
138 for (std::size_t j = 0; j < i; ++j) {
139 if (disk.contains(points[j])) {
140 continue;
141 }
142
143 disk = detail::diameterDisk(points[i], points[j]);
144 for (std::size_t k = 0; k < j; ++k) {
145 if (!disk.contains(points[k])) {
146 disk = detail::threePointDisk(points[i], points[j], points[k]);
147 }
148 }
149 }
150 }
151 return disk;
152}
153
165template <class Container>
166[[nodiscard]] detail::min_disk_result_t<Container>
167smallestEnclosingDisk(const Container& input) {
168 // Stable across runs: callers wanting another insertion order can use the
169 // generator-taking overload. The shuffle still randomizes the order for any
170 // given input, which is what the expected linear time needs; only an input
171 // built against this seed can force the worst case.
172 std::mt19937 generator(0x50474cU); // "PGL"
173 return smallestEnclosingDisk(input, generator);
174}
175
176// -----------------------------------------------------------------------------
177// Shape methods that need the algorithm above.
178
179template <class PointType, class LabelType>
180template <class UniformRandomBitGenerator>
181Disk<Point<typename Convex<PointType, LabelType>::NumberType>>
183 UniformRandomBitGenerator&& generator) const {
184 assert(!empty());
185 // Qualified: the member name would otherwise hide the free function.
187 *this, std::forward<UniformRandomBitGenerator>(generator));
188}
189
190template <class PointType, class LabelType>
196
197} // namespace pgl
Definition forward.hpp:306
Convex hull algorithms built from Pangolin point predicates.
Definition arrangement.hpp:67
constexpr std::partial_ordering orientationSign(const Point< ANumber, ALabel > &a, const Point< BNumber, BLabel > &b, const Point< CNumber, CLabel > &c)
Classifies the orientation of three points.
Definition orientation.hpp:544
detail::min_disk_result_t< Container > smallestEnclosingDisk(const Container &input, UniformRandomBitGenerator &&generator)
Computes the smallest closed disk containing a set of points.
Definition mindisk.hpp:115
Disk() -> Disk< Point<>, NoLabel >
Deduces a default disk with Point<> boundary points and no label.
Definition disk.hpp:1691
constexpr bool empty() const
Definition convex.hpp:390
Disk< Point< NumberType > > smallestEnclosingDisk() const
Returns the smallest closed disk containing the convex polygon.
Definition mindisk.hpp:192
Closed Euclidean disk stored by boundary points plus optional disk label.
Definition disk.hpp:66
Two-dimensional point with optional label payload.
Definition point.hpp:129