Pangolin
Header-only C++20 plane computational geometry library
Loading...
Searching...
No Matches
closest.hpp
Go to the documentation of this file.
1#pragma once
2
4
23
24#include <array>
25#include <cstddef>
26#include <optional>
27#include <type_traits>
28#include <variant>
29#include <utility>
30
31namespace pgl {
32
33namespace detail {
34
42template <class Self, class Other>
43using closestCompare_t =
45
53template <class ShapeType>
54constexpr auto coveringSegments(const ShapeType& shape) {
55 if constexpr (PointConcept<ShapeType>) {
56 return std::array<Segment<ShapeType>, 1>{Segment<ShapeType>(shape, shape)};
57 } else {
58 auto result = shape.edges();
59 if constexpr (requires(decltype(result)& growable) { growable.emplace_back(); }) {
60 if (result.empty()) {
61 for (const auto& vertex : shape.vertices()) {
62 result.emplace_back(vertex, vertex);
63 }
64 }
65 }
66 return result;
67 }
68}
69
77template <class Self, class Other>
78constexpr auto closestNativeSegments(const Self& self, const Other& other) {
79 using Compare = closestCompare_t<Self, Other>;
80 const auto selfSegments = coveringSegments(self);
81 const auto otherSegments = coveringSegments(other);
82 using SelfSegment = typename std::decay_t<decltype(selfSegments)>::value_type;
83 using OtherSegment = typename std::decay_t<decltype(otherSegments)>::value_type;
84 using Result = std::optional<std::pair<SelfSegment, OtherSegment>>;
85
86 if (selfSegments.empty() || otherSegments.empty()) {
87 return Result{};
88 }
89 if (self.template squaredDistance<Compare>(other) == Compare{}) {
90 return Result{};
91 }
92
93 std::size_t bestSelf = 0;
94 std::size_t bestOther = 0;
95 Compare best = selfSegments[0].template squaredDistance<Compare>(otherSegments[0]);
96 for (std::size_t i = 0; i < selfSegments.size(); ++i) {
97 for (std::size_t j = 0; j < otherSegments.size(); ++j) {
98 const Compare candidate =
99 selfSegments[i].template squaredDistance<Compare>(otherSegments[j]);
100 if (candidate < best) {
101 best = candidate;
102 bestSelf = i;
103 bestOther = j;
104 }
105 }
106 }
107 return Result{std::pair<SelfSegment, OtherSegment>{selfSegments[bestSelf], otherSegments[bestOther]}};
108}
109
111template <class ResultNumber, class LabelType, class Self, class Other>
112constexpr auto closestSegmentsOf(const Self& self, const Other& other) {
113 using ResultSegment = Segment<Point<ResultNumber, LabelType>>;
114 using Result = std::optional<std::array<ResultSegment, 2>>;
115
116 const auto native = closestNativeSegments(self, other);
117 if (!native) {
118 return Result{};
119 }
120 return Result{std::array<ResultSegment, 2>{ResultSegment(native->first), ResultSegment(native->second)}};
121}
122
130template <class Piece>
131constexpr auto pieceEnds(const Piece& piece) {
132 using PiecePoint = typename Piece::PointType;
133 if constexpr (LineConcept<Piece>) {
134 return std::array<PiecePoint, 0>{};
135 } else if constexpr (RayConcept<Piece>) {
136 return std::array<PiecePoint, 1>{piece.source()};
137 } else {
138 return std::array<PiecePoint, 2>{piece.min(), piece.max()};
139 }
140}
141
148template <class ResultNumber, class LabelType, class Piece, class QueryPoint>
149constexpr Point<ResultNumber, LabelType> closestPointOn(const Piece& piece, const QueryPoint& query) {
150 using ResultPoint = Point<ResultNumber, LabelType>;
151 constexpr bool stopsAtOrigin = !LineConcept<Piece>;
152 constexpr bool stopsAtHeading = !LineConcept<Piece> && !RayConcept<Piece>;
153
154 // A ray runs from its source through its target; a segment and a line are
155 // both read from their lexicographic ends.
156 const auto& origin = [&piece]() -> const auto& {
157 if constexpr (RayConcept<Piece>) {
158 return piece.source();
159 } else {
160 return piece.min();
161 }
162 }();
163 const auto& heading = [&piece]() -> const auto& {
164 if constexpr (RayConcept<Piece>) {
165 return piece.target();
166 } else {
167 return piece.max();
168 }
169 }();
170
171 const Point<ResultNumber> first(origin);
172 const Point<ResultNumber> second(heading);
173 const Point<ResultNumber> target(query);
174
175 const auto along = second - first;
176 const auto toTarget = target - first;
177 const auto squaredLength = along * along;
178
179 if (squaredLength == ResultNumber{}) {
180 return ResultPoint(origin);
181 }
182 if constexpr (stopsAtOrigin) {
183 if (dotSign(toTarget, along) <= 0) {
184 return ResultPoint(origin);
185 }
186 }
187 if constexpr (stopsAtHeading) {
188 if (dotSign(target - second, along) >= 0) {
189 return ResultPoint(heading);
190 }
191 }
192 const ResultNumber ratio = (toTarget * along) / squaredLength;
193 return ResultPoint(first.x() + ratio * along.x(), first.y() + ratio * along.y());
194}
195
197template <class ResultNumber, class LabelType>
198struct ClosestCandidate {
199 std::optional<std::array<Point<ResultNumber, LabelType>, 2>> pair{};
200 ResultNumber distance{};
201};
202
208template <class ResultNumber, class LabelType, class FirstPiece, class SecondPiece>
209constexpr void offerPiecePair(const FirstPiece& first, const SecondPiece& second,
210 ClosestCandidate<ResultNumber, LabelType>& best) {
211 using ResultPoint = Point<ResultNumber, LabelType>;
212 const auto offer = [&best](ResultPoint here, ResultPoint there) {
213 const ResultNumber distance = here.template squaredDistance<ResultNumber>(there);
214 if (!best.pair || distance < best.distance) {
215 best.distance = distance;
216 best.pair = std::array<ResultPoint, 2>{std::move(here), std::move(there)};
217 }
218 };
219 for (const auto& end : pieceEnds(first)) {
220 offer(ResultPoint(end), closestPointOn<ResultNumber, LabelType>(second, end));
221 }
222 for (const auto& end : pieceEnds(second)) {
223 offer(closestPointOn<ResultNumber, LabelType>(first, end), ResultPoint(end));
224 }
225}
226
234template <class ResultNumber, class Unbounded, class Visitor>
235constexpr void forEachBoundaryPiece(const Unbounded& shape, Visitor&& visitor) {
236 if constexpr (HalfplaneIntersectionConcept<Unbounded>) {
237 if (shape.empty()) {
238 return;
239 }
240 for (std::size_t i = 0; i < shape.size(); ++i) {
241 std::visit([&visitor](const auto& piece) { visitor(piece); },
242 shape.template edge<ResultNumber>(i));
243 }
244 } else if constexpr (LineConcept<Unbounded>) {
245 visitor(shape);
246 } else if constexpr (RayConcept<Unbounded>) {
247 visitor(shape);
248 } else {
249 // An oriented line drops its direction; a half-plane at positive
250 // distance is only ever reached at its boundary line.
251 visitor(shape.asLine());
252 }
253}
254
256template <class ResultNumber, class LabelType, class Self, class Other>
257constexpr auto closestPointsOf(const Self& self, const Other& other) {
258 using Compare = closestCompare_t<Self, Other>;
259 using ResultPoint = Point<ResultNumber, LabelType>;
260 using Result = std::optional<std::array<ResultPoint, 2>>;
261
262 ClosestCandidate<Compare, LabelType> best;
263 if constexpr (BoundedPolygonalConcept<Self> && BoundedPolygonalConcept<Other>) {
264 // Refine the elements `closestSegments` names, so the two methods always
265 // answer about the same pair even where several realize the distance.
266 const auto native = closestNativeSegments(self, other);
267 if (!native) {
268 return Result{};
269 }
270 offerPiecePair<Compare, LabelType>(native->first, native->second, best);
271 } else if (self.template squaredDistance<Compare>(other) == Compare{}) {
272 return Result{};
273 } else if constexpr (BoundedPolygonalConcept<Self>) {
274 const auto mine = coveringSegments(self);
275 forEachBoundaryPiece<Compare>(other, [&mine, &best](const auto& piece) {
276 for (const auto& element : mine) {
277 offerPiecePair<Compare, LabelType>(element, piece, best);
278 }
279 });
280 } else {
281 const auto theirs = coveringSegments(other);
282 forEachBoundaryPiece<Compare>(self, [&theirs, &best](const auto& piece) {
283 for (const auto& element : theirs) {
284 offerPiecePair<Compare, LabelType>(piece, element, best);
285 }
286 });
287 }
288
289 if (!best.pair) {
290 return Result{};
291 }
292 return Result{std::array<ResultPoint, 2>{ResultPoint((*best.pair)[0]), ResultPoint((*best.pair)[1])}};
293}
294
295} // namespace detail
296
297template <class TNumber, class TLabel>
298template <class ResultNumber, BoundedPolygonalConcept OtherShape>
299 requires detail::ClosestPairConcept<Point<TNumber, TLabel>, OtherShape>
300constexpr auto Point<TNumber, TLabel>::closestSegments(const OtherShape& other) const {
301 return detail::closestSegmentsOf<ResultNumber, TLabel>(*this, other);
302}
303
304template <class TNumber, class TLabel>
305template <class ResultNumber, class OtherShape>
306 requires detail::ClosestPointsPairConcept<Point<TNumber, TLabel>, OtherShape>
307constexpr auto Point<TNumber, TLabel>::closestPoints(const OtherShape& other) const {
308 return detail::closestPointsOf<ResultNumber, TLabel>(*this, other);
309}
310
311template <class TPoint, class TLabel>
312template <class ResultNumber, BoundedPolygonalConcept OtherShape>
313 requires detail::ClosestPairConcept<Segment<TPoint, TLabel>, OtherShape>
314constexpr auto Segment<TPoint, TLabel>::closestSegments(const OtherShape& other) const {
315 return detail::closestSegmentsOf<ResultNumber, typename TPoint::LabelType>(*this, other);
316}
317
318template <class TPoint, class TLabel>
319template <class ResultNumber, class OtherShape>
320 requires detail::ClosestPointsPairConcept<Segment<TPoint, TLabel>, OtherShape>
321constexpr auto Segment<TPoint, TLabel>::closestPoints(const OtherShape& other) const {
322 return detail::closestPointsOf<ResultNumber, typename TPoint::LabelType>(*this, other);
323}
324
325template <class PointType_, class TLabel>
326template <class ResultNumber, BoundedPolygonalConcept OtherShape>
327 requires detail::ClosestPairConcept<OrientedSegment<PointType_, TLabel>, OtherShape>
328constexpr auto OrientedSegment<PointType_, TLabel>::closestSegments(const OtherShape& other) const {
329 return detail::closestSegmentsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
330}
331
332template <class PointType_, class TLabel>
333template <class ResultNumber, class OtherShape>
334 requires detail::ClosestPointsPairConcept<OrientedSegment<PointType_, TLabel>, OtherShape>
335constexpr auto OrientedSegment<PointType_, TLabel>::closestPoints(const OtherShape& other) const {
336 return detail::closestPointsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
337}
338
339template <class PointType_, class TLabel>
340template <class ResultNumber, BoundedPolygonalConcept OtherShape>
341 requires detail::ClosestPairConcept<Rectangle<PointType_, TLabel>, OtherShape>
342constexpr auto Rectangle<PointType_, TLabel>::closestSegments(const OtherShape& other) const {
343 return detail::closestSegmentsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
344}
345
346template <class PointType_, class TLabel>
347template <class ResultNumber, class OtherShape>
348 requires detail::ClosestPointsPairConcept<Rectangle<PointType_, TLabel>, OtherShape>
349constexpr auto Rectangle<PointType_, TLabel>::closestPoints(const OtherShape& other) const {
350 return detail::closestPointsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
351}
352
353template <class PointType_, class TLabel>
354template <class ResultNumber, BoundedPolygonalConcept OtherShape>
355 requires detail::ClosestPairConcept<Triangle<PointType_, TLabel>, OtherShape>
356constexpr auto Triangle<PointType_, TLabel>::closestSegments(const OtherShape& other) const {
357 return detail::closestSegmentsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
358}
359
360template <class PointType_, class TLabel>
361template <class ResultNumber, class OtherShape>
362 requires detail::ClosestPointsPairConcept<Triangle<PointType_, TLabel>, OtherShape>
363constexpr auto Triangle<PointType_, TLabel>::closestPoints(const OtherShape& other) const {
364 return detail::closestPointsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
365}
366
367template <class PointType_, class TLabel>
368template <class ResultNumber, BoundedPolygonalConcept OtherShape>
369 requires detail::ClosestPairConcept<Convex<PointType_, TLabel>, OtherShape>
370constexpr auto Convex<PointType_, TLabel>::closestSegments(const OtherShape& other) const {
371 return detail::closestSegmentsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
372}
373
374template <class PointType_, class TLabel>
375template <class ResultNumber, class OtherShape>
376 requires detail::ClosestPointsPairConcept<Convex<PointType_, TLabel>, OtherShape>
377constexpr auto Convex<PointType_, TLabel>::closestPoints(const OtherShape& other) const {
378 return detail::closestPointsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
379}
380
381template <class PointType_, class TLabel, class Storage>
382template <class ResultNumber, BoundedPolygonalConcept OtherShape>
383 requires detail::ClosestPairConcept<MonotoneChain<PointType_, TLabel, Storage>, OtherShape>
384constexpr auto MonotoneChain<PointType_, TLabel, Storage>::closestSegments(const OtherShape& other) const {
385 return detail::closestSegmentsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
386}
387
388template <class PointType_, class TLabel, class Storage>
389template <class ResultNumber, class OtherShape>
390 requires detail::ClosestPointsPairConcept<MonotoneChain<PointType_, TLabel, Storage>, OtherShape>
391constexpr auto MonotoneChain<PointType_, TLabel, Storage>::closestPoints(const OtherShape& other) const {
392 return detail::closestPointsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
393}
394
395template <class PointType_, class TLabel>
396template <class ResultNumber, BoundedPolygonalConcept OtherShape>
397 requires detail::ClosestPairConcept<Polyline<PointType_, TLabel>, OtherShape>
398constexpr auto Polyline<PointType_, TLabel>::closestSegments(const OtherShape& other) const {
399 return detail::closestSegmentsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
400}
401
402template <class PointType_, class TLabel>
403template <class ResultNumber, class OtherShape>
404 requires detail::ClosestPointsPairConcept<Polyline<PointType_, TLabel>, OtherShape>
405constexpr auto Polyline<PointType_, TLabel>::closestPoints(const OtherShape& other) const {
406 return detail::closestPointsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
407}
408
409template <class PointType_, class TLabel>
410template <class ResultNumber, BoundedPolygonalConcept OtherShape>
411 requires detail::ClosestPairConcept<Polygon<PointType_, TLabel>, OtherShape>
412constexpr auto Polygon<PointType_, TLabel>::closestSegments(const OtherShape& other) const {
413 return detail::closestSegmentsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
414}
415
416template <class PointType_, class TLabel>
417template <class ResultNumber, class OtherShape>
418 requires detail::ClosestPointsPairConcept<Polygon<PointType_, TLabel>, OtherShape>
419constexpr auto Polygon<PointType_, TLabel>::closestPoints(const OtherShape& other) const {
420 return detail::closestPointsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
421}
422
423template <class PointType_, class TLabel>
424template <class ResultNumber, BoundedPolygonalConcept OtherShape>
425 requires detail::ClosestPairConcept<PolygonWithHoles<PointType_, TLabel>, OtherShape>
426constexpr auto PolygonWithHoles<PointType_, TLabel>::closestSegments(const OtherShape& other) const {
427 return detail::closestSegmentsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
428}
429
430template <class PointType_, class TLabel>
431template <class ResultNumber, class OtherShape>
432 requires detail::ClosestPointsPairConcept<PolygonWithHoles<PointType_, TLabel>, OtherShape>
433constexpr auto PolygonWithHoles<PointType_, TLabel>::closestPoints(const OtherShape& other) const {
434 return detail::closestPointsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
435}
436
437template <class PointType_, class TLabel>
438template <class ResultNumber, BoundedPolygonalConcept OtherShape>
439 requires detail::ClosestPairConcept<PolygonSet<PointType_, TLabel>, OtherShape>
440auto PolygonSet<PointType_, TLabel>::closestSegments(const OtherShape& other) const {
441 return detail::closestSegmentsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
442}
443
444template <class PointType_, class TLabel>
445template <class ResultNumber, class OtherShape>
446 requires detail::ClosestPointsPairConcept<PolygonSet<PointType_, TLabel>, OtherShape>
447auto PolygonSet<PointType_, TLabel>::closestPoints(const OtherShape& other) const {
448 return detail::closestPointsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
449}
450
451template <class PointType_, class TLabel>
452template <class ResultNumber, class OtherShape>
453 requires detail::ClosestPointsPairConcept<Line<PointType_, TLabel>, OtherShape>
454constexpr auto Line<PointType_, TLabel>::closestPoints(const OtherShape& other) const {
455 return detail::closestPointsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
456}
457
458template <class PointType_, class TLabel>
459template <class ResultNumber, class OtherShape>
460 requires detail::ClosestPointsPairConcept<OrientedLine<PointType_, TLabel>, OtherShape>
461constexpr auto OrientedLine<PointType_, TLabel>::closestPoints(const OtherShape& other) const {
462 return detail::closestPointsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
463}
464
465template <class PointType_, class TLabel>
466template <class ResultNumber, class OtherShape>
467 requires detail::ClosestPointsPairConcept<Ray<PointType_, TLabel>, OtherShape>
468constexpr auto Ray<PointType_, TLabel>::closestPoints(const OtherShape& other) const {
469 return detail::closestPointsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
470}
471
472template <class PointType_, class TLabel>
473template <class ResultNumber, class OtherShape>
474 requires detail::ClosestPointsPairConcept<Halfplane<PointType_, TLabel>, OtherShape>
475constexpr auto Halfplane<PointType_, TLabel>::closestPoints(const OtherShape& other) const {
476 return detail::closestPointsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
477}
478
479template <class PointType_, class TLabel>
480template <class ResultNumber, class OtherShape>
481 requires detail::ClosestPointsPairConcept<HalfplaneIntersection<PointType_, TLabel>, OtherShape>
482constexpr auto HalfplaneIntersection<PointType_, TLabel>::closestPoints(const OtherShape& other) const {
483 return detail::closestPointsOf<ResultNumber, typename PointType_::LabelType>(*this, other);
484}
485
486} // namespace pgl
Distance and Hausdorff-style measurements between shapes.
Definition arrangement.hpp:67
@ edge
Definition bitmatrix.hpp:37
@ vertex
Definition bitmatrix.hpp:37
typename DivisionResult< Number >::type division_result_t
Convenience alias for DivisionResult.
Definition rational.hpp:1175
Point() -> Point< int >
constexpr std::partial_ordering dotSign(const Point< ANumber, ALabel > &a, const Point< BNumber, BLabel > &b)
Tells if the angle between two vectors is acute, right, or obtuse.
Definition orientation.hpp:688
Segment() -> Segment< Point<>, NoLabel >
constexpr auto closestPoints(const OtherShape &other) const
Returns the pair of points realizing the distance, nothing when the shapes meet.
Definition closest.hpp:377
constexpr auto closestSegments(const OtherShape &other) const
Returns the pair of elements realizing the distance, nothing when the shapes meet.
Definition closest.hpp:370
constexpr auto closestPoints(const OtherShape &other) const
Returns the pair of points realizing the distance, nothing when the shapes meet.
Definition closest.hpp:482
constexpr auto closestPoints(const OtherShape &other) const
Returns the pair of points realizing the distance, nothing when the shapes meet.
Definition closest.hpp:475
constexpr auto closestPoints(const OtherShape &other) const
Returns the pair of points realizing the distance, nothing when the shapes meet.
Definition closest.hpp:454
constexpr auto closestSegments(const OtherShape &other) const
Returns the pair of elements realizing the distance, nothing when the shapes meet.
Definition closest.hpp:384
constexpr auto closestPoints(const OtherShape &other) const
Returns the pair of points realizing the distance, nothing when the shapes meet.
Definition closest.hpp:391
constexpr auto closestPoints(const OtherShape &other) const
Returns the pair of points realizing the distance, nothing when the shapes meet.
Definition closest.hpp:461
constexpr auto closestSegments(const OtherShape &other) const
Returns the pair of elements realizing the distance, nothing when the shapes meet.
Definition closest.hpp:328
constexpr auto closestPoints(const OtherShape &other) const
Returns the pair of points realizing the distance, nothing when the shapes meet.
Definition closest.hpp:335
constexpr auto closestPoints(const OtherShape &other) const
Returns the pair of points realizing the distance, nothing when the shapes meet.
Definition closest.hpp:307
constexpr auto closestSegments(const OtherShape &other) const
Returns the pair of elements realizing the distance, nothing when the shapes meet.
Definition closest.hpp:300
auto closestSegments(const OtherShape &other) const
Returns the pair of elements realizing the distance, nothing when the shapes meet.
Definition closest.hpp:440
auto closestPoints(const OtherShape &other) const
Returns the pair of points realizing the distance, nothing when the shapes meet.
Definition closest.hpp:447
constexpr auto closestPoints(const OtherShape &other) const
Returns the pair of points realizing the distance, nothing when the shapes meet.
Definition closest.hpp:433
constexpr auto closestSegments(const OtherShape &other) const
Returns the pair of elements realizing the distance, nothing when the shapes meet.
Definition closest.hpp:426
constexpr auto closestPoints(const OtherShape &other) const
Returns the pair of points realizing the distance, nothing when the shapes meet.
Definition closest.hpp:419
constexpr auto closestSegments(const OtherShape &other) const
Returns the pair of elements realizing the distance, nothing when the shapes meet.
Definition closest.hpp:412
constexpr auto closestSegments(const OtherShape &other) const
Returns the pair of elements realizing the distance, nothing when the shapes meet.
Definition closest.hpp:398
constexpr auto closestPoints(const OtherShape &other) const
Returns the pair of points realizing the distance, nothing when the shapes meet.
Definition closest.hpp:405
constexpr auto closestPoints(const OtherShape &other) const
Returns the pair of points realizing the distance, nothing when the shapes meet.
Definition closest.hpp:468
constexpr auto closestSegments(const OtherShape &other) const
Returns the pair of elements realizing the distance, nothing when the shapes meet.
Definition closest.hpp:342
constexpr auto closestPoints(const OtherShape &other) const
Returns the pair of points realizing the distance, nothing when the shapes meet.
Definition closest.hpp:349
constexpr auto closestSegments(const OtherShape &other) const
Returns the pair of elements realizing the distance, nothing when the shapes meet.
Definition closest.hpp:314
constexpr auto closestPoints(const OtherShape &other) const
Returns the pair of points realizing the distance, nothing when the shapes meet.
Definition closest.hpp:321
constexpr auto closestPoints(const OtherShape &other) const
Returns the pair of points realizing the distance, nothing when the shapes meet.
Definition closest.hpp:363
constexpr auto closestSegments(const OtherShape &other) const
Returns the pair of elements realizing the distance, nothing when the shapes meet.
Definition closest.hpp:356