26template <
class Container>
27using closest_pair_input_point_t =
28 std::remove_cvref_t<decltype(*std::begin(std::declval<const Container&>()))>;
37template <
class Po
intType>
38using closest_pair_coordinate_t = promoted_number_t<typename PointType::NumberType>;
40template <
class Po
intType>
41using closest_pair_distance_t =
42 decltype(std::declval<const PointType&>()
43 .template squaredDistance<closest_pair_coordinate_t<PointType>>(
44 std::declval<const PointType&>()));
47template <
class Po
intType>
48struct ClosestPairCandidate {
51 closest_pair_distance_t<PointType> squaredDistance;
92template <
class Coordinate>
93inline constexpr std::size_t closestPairBruteForceThreshold =
97template <
class Po
intType>
98constexpr bool closestPairLessY(
const PointType& a,
const PointType& b) {
103 return a.x() < b.x();
112template <
class Po
intType>
113void closestPairBaseCase(
const PointType* points, std::size_t count,
114 ClosestPairCandidate<PointType>& best) {
115 using Coordinate = closest_pair_coordinate_t<PointType>;
116 for (std::size_t i = 0; i < count; ++i) {
117 for (std::size_t j = i + 1; j < count; ++j) {
118 const auto squared = points[i].template squaredDistance<Coordinate>(points[j]);
119 if (squared < best.squaredDistance) {
120 best = {points[i], points[j], squared};
148template <
class Po
intType>
149std::size_t closestPairGatherStrip(
const PointType* points, std::size_t count, std::size_t half,
150 closest_pair_coordinate_t<PointType> splitX,
151 const ClosestPairCandidate<PointType>& best, PointType* strip) {
152 using Coordinate = closest_pair_coordinate_t<PointType>;
157 const auto inside = [&](std::size_t index) {
158 const Coordinate dx = detail::asNumber<Coordinate>(points[index].
x()) - splitX;
159 return dx * dx < best.squaredDistance;
162 std::size_t low = half;
163 while (low > 0 && inside(low - 1)) {
166 std::size_t high = half;
167 while (high < count && inside(high)) {
171 std::size_t stripCount = 0;
172 for (std::size_t i = low; i < high; ++i) {
173 strip[stripCount++] = points[i];
185template <
class Po
intType>
186void closestPairScanStrip(
const PointType* strip, std::size_t stripCount,
187 ClosestPairCandidate<PointType>& best) {
188 using Coordinate = closest_pair_coordinate_t<PointType>;
189 for (std::size_t i = 0; i < stripCount; ++i) {
190 for (std::size_t j = i + 1; j < stripCount; ++j) {
191 const Coordinate dy =
192 detail::asNumber<Coordinate>(strip[j].
y()) - detail::asNumber<Coordinate>(strip[i].
y());
193 if (dy * dy >= best.squaredDistance) {
196 const auto squared = strip[i].template squaredDistance<Coordinate>(strip[j]);
197 if (squared < best.squaredDistance) {
198 best = {strip[i], strip[j], squared};
244template <std::
size_t Threshold,
class Po
intType>
245void closestPairRecursive(
const PointType* points, std::size_t count, PointType* scratch,
246 ClosestPairCandidate<PointType>& best) {
247 using Coordinate = closest_pair_coordinate_t<PointType>;
248 static_assert(Threshold >= 3,
249 "a range above the threshold must split into halves of at least two points");
253 if (count <= Threshold) {
254 closestPairBaseCase(points, count, best);
261 const std::size_t half = count / 2;
262 const Coordinate splitX =
static_cast<Coordinate
>(points[half].x());
264 closestPairRecursive<Threshold>(points, half, scratch, best);
265 closestPairRecursive<Threshold>(points + half, count - half, scratch + half, best);
269 const std::size_t stripCount =
270 closestPairGatherStrip(points, count, half, splitX, best, scratch);
271 std::sort(scratch, scratch + stripCount, closestPairLessY<PointType>);
272 closestPairScanStrip(scratch, stripCount, best);
281template <std::
size_t Threshold,
class Container>
283closestPairDriver(
const Container& input) {
284 using InputPoint = closest_pair_input_point_t<Container>;
285 using Coordinate = closest_pair_coordinate_t<InputPoint>;
286 static_assert(PointConcept<InputPoint>,
287 "closestPair requires a container of pgl::Point values");
289 std::vector<InputPoint> points(std::begin(input), std::end(input));
290 assert(points.size() >= 2);
292 std::sort(points.begin(), points.end());
293 std::vector<InputPoint> scratch(points.size());
295 ClosestPairCandidate<InputPoint> best{
296 points[0], points[1],
297 points[0].template squaredDistance<Coordinate>(points[1])};
298 closestPairRecursive<Threshold>(points.data(), points.size(), scratch.data(), best);
332template <
class Container>
336 detail::closest_pair_coordinate_t<detail::closest_pair_input_point_t<Container>>;
337 return detail::closestPairDriver<detail::closestPairBruteForceThreshold<Coordinate>>(input);
Smallest enclosing disk algorithms.
Definition arrangement.hpp:67
@ y
Definition intervaltree.hpp:24
@ x
Definition intervaltree.hpp:24
constexpr bool is_Rational_v
Definition rational.hpp:37
Segment< detail::closest_pair_input_point_t< Container > > closestPair(const Container &input)
Computes a closest pair of points by divide and conquer.
Definition closestpair.hpp:334
Segment() -> Segment< Point<>, NoLabel >
Unoriented closed segment between two endpoints plus optional segment label.
Definition segment.hpp:58