Pangolin
Header-only C++20 plane computational geometry library
Loading...
Searching...
No Matches
distance.hpp
Go to the documentation of this file.
1#pragma once
2
4#include "shape/convex.hpp"
5
17
18#include <algorithm>
19#include <cassert>
20#include <cmath>
21#include <cstddef>
22#include <ranges>
23
24
25namespace pgl {
26
27namespace detail {
28
41template <class ResultNumber, class Self, class OtherShape>
42constexpr ResultNumber maxVertexSquaredDistance(const Self& self, const OtherShape& other) {
43 const auto self_vertices = self.vertices();
44 ResultNumber worst = other.template squaredDistance<ResultNumber>(self_vertices[0]);
45 for (std::size_t index = 1; index < self_vertices.size(); ++index) {
46 const ResultNumber current = other.template squaredDistance<ResultNumber>(self_vertices[index]);
47 if (worst < current) {
48 worst = current;
49 }
50 }
51 return worst;
52}
53
54} // namespace detail
55
56// -----------------------------------------------------------------------------
57// Point
58
59template <class Number, class Label>
60template <class ResultNumber, PointConcept OtherPoint>
61constexpr auto Point<Number, Label>::squaredDistance(const OtherPoint& other) const {
62 const ResultNumber dx = detail::asNumber<ResultNumber>(x()) - detail::asNumber<ResultNumber>(other.x());
63 const ResultNumber dy = detail::asNumber<ResultNumber>(y()) - detail::asNumber<ResultNumber>(other.y());
64 return dx * dx + dy * dy;
65}
66
67template <class Number, class Label>
68template <class ApproximateNumber, PointConcept OtherPoint>
69ApproximateNumber Point<Number, Label>::distance(const OtherPoint& other) const {
70 return std::sqrt(this->template squaredDistance<ApproximateNumber>(other));
71}
72
73template <class Number, class Label>
74template <class ResultNumber, PointConcept OtherPoint>
75constexpr auto Point<Number, Label>::distanceL1(const OtherPoint& other) const {
76 const ResultNumber dx = detail::asNumber<ResultNumber>(x()) - detail::asNumber<ResultNumber>(other.x());
77 const ResultNumber dy = detail::asNumber<ResultNumber>(y()) - detail::asNumber<ResultNumber>(other.y());
78 return pgl::detail::abs(dx) + pgl::detail::abs(dy);
79}
80
81template <class Number, class Label>
82template <class ResultNumber, PointConcept OtherPoint>
83constexpr auto Point<Number, Label>::distanceLInf(const OtherPoint& other) const {
84 const ResultNumber dx = detail::asNumber<ResultNumber>(x()) - detail::asNumber<ResultNumber>(other.x());
85 const ResultNumber dy = detail::asNumber<ResultNumber>(y()) - detail::asNumber<ResultNumber>(other.y());
86 return std::max(pgl::detail::abs(dx), pgl::detail::abs(dy));
87}
88
89template <class Number, class Label>
90template <class ResultNumber, PointConcept OtherPoint>
91constexpr auto Point<Number, Label>::squaredHausdorffDistance(const OtherPoint& other) const {
93}
94
95// -----------------------------------------------------------------------------
96// Segment
97
98template <class PointType, class LabelType>
99template <class ResultNumber, PointConcept OtherPoint>
100constexpr auto Segment<PointType, LabelType>::squaredDistance(const OtherPoint& point) const {
101 const Point<ResultNumber> a(min());
102 const Point<ResultNumber> b(max());
103 const Point<ResultNumber> p(point);
104
105 const auto ab = b - a;
106 const auto ap = p - a;
107 const auto bp = p - b;
108 const auto squared_length = ab * ab;
109
110 if (squared_length == 0) {
111 return a.template squaredDistance<ResultNumber>(p);
112 }
113
114 // Which of the three pieces of the segment's Voronoi diagram p falls in.
115 // Both clamps are sign tests on a projection, so dotSign settles them in
116 // the promoted coordinate type rather than in the caller's ResultNumber —
117 // the same reason Triangle::isRectangle prefers it to the plain dot
118 // product operator. `ap . ab >= ab . ab` is `bp . ab >= 0`, which saves
119 // comparing against the squared length as well.
120 if (dotSign(ap, ab) <= 0) {
121 return static_cast<ResultNumber>(ap * ap);
122 }
123
124 if (dotSign(bp, ab) >= 0) {
125 return static_cast<ResultNumber>(bp * bp);
126 }
127
128 const ResultNumber twice_triangle_area = ab.x() * ap.y() - ab.y() * ap.x();
129 return static_cast<ResultNumber>((twice_triangle_area * twice_triangle_area) / squared_length);
130}
131
132template <class PointType, class LabelType>
133template <class ResultNumber, SegmentConcept OtherSegment>
134constexpr auto Segment<PointType, LabelType>::squaredDistance(const OtherSegment& other) const {
135 if (intersects(other)) {
136 return ResultNumber{};
137 }
138
139 const auto this_min_to_other = other.template squaredDistance<ResultNumber>(min());
140 const auto this_max_to_other = other.template squaredDistance<ResultNumber>(max());
141 const auto other_min_to_this = this->template squaredDistance<ResultNumber>(other.min());
142 const auto other_max_to_this = this->template squaredDistance<ResultNumber>(other.max());
143
144 const auto best_from_this = this_min_to_other < this_max_to_other ? this_min_to_other : this_max_to_other;
145 const auto best_from_other = other_min_to_this < other_max_to_this ? other_min_to_this : other_max_to_this;
146
147 return best_from_this < best_from_other ? best_from_this : best_from_other;
148}
149
150template <class PointType, class LabelType>
151template <class ResultNumber, SegmentConcept OtherSegment>
152constexpr auto Segment<PointType, LabelType>::squaredHausdorffDistance(const OtherSegment& other) const {
153 const auto this_min_to_other = other.template squaredDistance<ResultNumber>(min());
154 const auto this_max_to_other = other.template squaredDistance<ResultNumber>(max());
155 const auto other_min_to_this = this->template squaredDistance<ResultNumber>(other.min());
156 const auto other_max_to_this = this->template squaredDistance<ResultNumber>(other.max());
157
158 const auto worst_from_this = this_min_to_other > this_max_to_other ? this_min_to_other : this_max_to_other;
159 const auto worst_from_other = other_min_to_this > other_max_to_this ? other_min_to_this : other_max_to_this;
160
161 return worst_from_this > worst_from_other ? worst_from_this : worst_from_other;
162}
163
164template <class PointType, class LabelType>
165template <class ResultNumber, PointConcept OtherPoint>
166constexpr auto Segment<PointType, LabelType>::squaredHausdorffDistance(const OtherPoint& point) const {
167 // The point-side supremum (point-to-segment distance) is never larger than
168 // the segment-side supremum (a min is never above a max), so the segment's
169 // farthest endpoint from the point is the whole answer.
170 return detail::maxVertexSquaredDistance<ResultNumber>(*this, point);
171}
172
173// -----------------------------------------------------------------------------
174// OrientedSegment
175
176template <class PointType, class LabelType>
177template <class ResultNumber, PointConcept OtherPoint>
178constexpr auto OrientedSegment<PointType, LabelType>::squaredDistance(const OtherPoint& point) const {
179 return static_cast<Segment<PointType>>(*this).template squaredDistance<ResultNumber>(point);
180}
181
182template <class PointType, class LabelType>
183template <class ResultNumber, SegmentConcept OtherSegment>
184constexpr auto OrientedSegment<PointType, LabelType>::squaredDistance(const OtherSegment& other) const {
185 return static_cast<Segment<PointType>>(*this).template squaredDistance<ResultNumber>(other);
186}
187
188template <class PointType, class LabelType>
189template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
190constexpr auto OrientedSegment<PointType, LabelType>::squaredDistance(const OtherOrientedSegment& other) const {
191 return static_cast<Segment<PointType>>(*this).template squaredDistance<ResultNumber>(
193}
194
195template <class PointType, class LabelType>
196template <class ResultNumber, SegmentConcept OtherSegment>
197constexpr auto OrientedSegment<PointType, LabelType>::squaredHausdorffDistance(const OtherSegment& other) const {
198 return static_cast<Segment<PointType>>(*this).template squaredHausdorffDistance<ResultNumber>(other);
199}
200
201template <class PointType, class LabelType>
202template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
203constexpr auto OrientedSegment<PointType, LabelType>::squaredHausdorffDistance(const OtherOrientedSegment& other) const {
204 return static_cast<Segment<PointType>>(*this).template squaredHausdorffDistance<ResultNumber>(
206}
207
208template <class PointType, class LabelType>
209template <class ResultNumber, PointConcept OtherPoint>
210constexpr auto OrientedSegment<PointType, LabelType>::squaredHausdorffDistance(const OtherPoint& point) const {
211 return static_cast<Segment<PointType>>(*this).template squaredHausdorffDistance<ResultNumber>(point);
212}
213
214// -----------------------------------------------------------------------------
215// Line
216
217template <class PointType, class LabelType>
218template <class ResultNumber, PointConcept OtherPoint>
219constexpr auto Line<PointType, LabelType>::squaredDistance(const OtherPoint& point) const {
220 if (isDegenerate()) {
221 return min().template squaredDistance<ResultNumber>(point);
222 }
223
224 const Point<ResultNumber> a(min());
225 const Point<ResultNumber> b(max());
226 const Point<ResultNumber> p(point);
227 const auto ab = b - a;
228 const auto ap = p - a;
229 const ResultNumber twice_triangle_area = ab.x() * ap.y() - ab.y() * ap.x();
230 const ResultNumber squared_length = ab * ab;
231 return static_cast<ResultNumber>((twice_triangle_area * twice_triangle_area) / squared_length);
232}
233
234template <class PointType, class LabelType>
235template <class ResultNumber, LineConcept OtherLine>
236constexpr auto Line<PointType, LabelType>::squaredDistance(const OtherLine& other) const {
237 if (intersects(other)) {
238 return ResultNumber{};
239 }
240
241 return other.template squaredDistance<ResultNumber>(min());
242}
243
244template <class PointType, class LabelType>
245template <class ResultNumber, SegmentConcept OtherSegment>
246constexpr auto Line<PointType, LabelType>::squaredDistance(const OtherSegment& other) const {
247 if (intersects(other)) {
248 return ResultNumber{};
249 }
250
251 const auto source_distance = this->template squaredDistance<ResultNumber>(other.min());
252 const auto target_distance = this->template squaredDistance<ResultNumber>(other.max());
253 return source_distance < target_distance ? source_distance : target_distance;
254}
255
256template <class PointType, class LabelType>
257template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
258constexpr auto Line<PointType, LabelType>::squaredDistance(const OtherOrientedSegment& other) const {
260}
261
262// -----------------------------------------------------------------------------
263// OrientedLine
264
265template <class PointType, class LabelType>
266template <class ResultNumber, PointConcept OtherPoint>
267constexpr auto OrientedLine<PointType, LabelType>::squaredDistance(const OtherPoint& point) const {
268 return this->asLine().template squaredDistance<ResultNumber>(point);
269}
270
271template <class PointType, class LabelType>
272template <class ResultNumber, LineConcept OtherLine>
273constexpr auto OrientedLine<PointType, LabelType>::squaredDistance(const OtherLine& other) const {
274 return this->asLine().template squaredDistance<ResultNumber>(other);
275}
276
277template <class PointType, class LabelType>
278template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
279constexpr auto OrientedLine<PointType, LabelType>::squaredDistance(const OtherOrientedLine& other) const {
280 return this->asLine().template squaredDistance<ResultNumber>(other.asLine());
281}
282
283template <class PointType, class LabelType>
284template <class ResultNumber, SegmentConcept OtherSegment>
285constexpr auto OrientedLine<PointType, LabelType>::squaredDistance(const OtherSegment& other) const {
286 return this->asLine().template squaredDistance<ResultNumber>(other);
287}
288
289template <class PointType, class LabelType>
290template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
291constexpr auto OrientedLine<PointType, LabelType>::squaredDistance(const OtherOrientedSegment& other) const {
292 return this->asLine().template squaredDistance<ResultNumber>(other);
293}
294
295// -----------------------------------------------------------------------------
296// Ray
297
298template <class PointType, class LabelType>
299template <class ResultNumber, PointConcept OtherPoint>
300constexpr auto Ray<PointType, LabelType>::squaredDistance(const OtherPoint& point) const {
301 if (isDegenerate()) {
302 return source().template squaredDistance<ResultNumber>(point);
303 }
304
305 const Point<ResultNumber> a(source());
306 const Point<ResultNumber> b(target());
307 const Point<ResultNumber> p(point);
308 const auto ab = b - a;
309 const auto ap = p - a;
310 const auto squared_length = ab * ab;
311
312 if (squared_length == 0) {
313 return static_cast<ResultNumber>(ap * ap);
314 }
315
316 // Behind the source, so the source is the nearest point; the projection's
317 // sign is promoted for the same reason as the segment's clamps above.
318 if (dotSign(ap, ab) <= 0) {
319 return static_cast<ResultNumber>(ap * ap);
320 }
321
322 const ResultNumber twice_triangle_area = ab.x() * ap.y() - ab.y() * ap.x();
323 return static_cast<ResultNumber>((twice_triangle_area * twice_triangle_area) / squared_length);
324}
325
326template <class PointType, class LabelType>
327template <class ResultNumber, LineConcept OtherLine>
328constexpr auto Ray<PointType, LabelType>::squaredDistance(const OtherLine& other) const {
329 if (intersects(other)) {
330 return ResultNumber{};
331 }
332
333 return other.template squaredDistance<ResultNumber>(source());
334}
335
336template <class PointType, class LabelType>
337template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
338constexpr auto Ray<PointType, LabelType>::squaredDistance(const OtherOrientedLine& other) const {
339 if (intersects(other)) {
340 return ResultNumber{};
341 }
342
343 return other.template squaredDistance<ResultNumber>(source());
344}
345
346template <class PointType, class LabelType>
347template <class ResultNumber, SegmentConcept OtherSegment>
348constexpr auto Ray<PointType, LabelType>::squaredDistance(const OtherSegment& other) const {
349 if (intersects(other)) {
350 return ResultNumber{};
351 }
352
353 const auto source_to_other = other.template squaredDistance<ResultNumber>(source());
354 const auto other_min_to_this = this->template squaredDistance<ResultNumber>(other.min());
355 const auto other_max_to_this = this->template squaredDistance<ResultNumber>(other.max());
356 const auto best_from_segment = other_min_to_this < other_max_to_this ? other_min_to_this : other_max_to_this;
357 return source_to_other < best_from_segment ? source_to_other : best_from_segment;
358}
359
360template <class PointType, class LabelType>
361template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
362constexpr auto Ray<PointType, LabelType>::squaredDistance(const OtherOrientedSegment& other) const {
363 if (intersects(other)) {
364 return ResultNumber{};
365 }
366
367 const auto source_to_other = other.template squaredDistance<ResultNumber>(source());
368 const auto other_source_to_this = this->template squaredDistance<ResultNumber>(other.source());
369 const auto other_target_to_this = this->template squaredDistance<ResultNumber>(other.target());
370 const auto best_from_segment = other_source_to_this < other_target_to_this ? other_source_to_this : other_target_to_this;
371 return source_to_other < best_from_segment ? source_to_other : best_from_segment;
372}
373
374template <class PointType, class LabelType>
375template <class ResultNumber, RayConcept OtherRay>
376constexpr auto Ray<PointType, LabelType>::squaredDistance(const OtherRay& other) const {
377 if (intersects(other)) {
378 return ResultNumber{};
379 }
380
381 const auto this_source_to_other = other.template squaredDistance<ResultNumber>(source());
382 const auto other_source_to_this = this->template squaredDistance<ResultNumber>(other.source());
383 return this_source_to_other < other_source_to_this ? this_source_to_other : other_source_to_this;
384}
385
386// -----------------------------------------------------------------------------
387// Halfplane
388
389template <class PointType, class LabelType>
390template <class ResultNumber, PointConcept OtherPoint>
391constexpr auto Halfplane<PointType, LabelType>::squaredDistance(const OtherPoint& point) const {
392 if (intersects(point)) {
393 return ResultNumber{};
394 }
395 return asLine().template squaredDistance<ResultNumber>(point);
396}
397
398template <class PointType, class LabelType>
399template <class ResultNumber, SegmentConcept OtherSegment>
400constexpr auto Halfplane<PointType, LabelType>::squaredDistance(const OtherSegment& other) const {
401 if (intersects(other)) {
402 return ResultNumber{};
403 }
404 return asLine().template squaredDistance<ResultNumber>(other);
405}
406
407template <class PointType, class LabelType>
408template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
409constexpr auto Halfplane<PointType, LabelType>::squaredDistance(const OtherOrientedSegment& other) const {
410 if (intersects(other)) {
411 return ResultNumber{};
412 }
413 return asLine().template squaredDistance<ResultNumber>(other);
414}
415
416template <class PointType, class LabelType>
417template <class ResultNumber, LineConcept OtherLine>
418constexpr auto Halfplane<PointType, LabelType>::squaredDistance(const OtherLine& other) const {
419 if (intersects(other)) {
420 return ResultNumber{};
421 }
422 return asLine().template squaredDistance<ResultNumber>(other);
423}
424
425template <class PointType, class LabelType>
426template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
427constexpr auto Halfplane<PointType, LabelType>::squaredDistance(const OtherOrientedLine& other) const {
428 if (intersects(other)) {
429 return ResultNumber{};
430 }
431 return asLine().template squaredDistance<ResultNumber>(other);
432}
433
434template <class PointType, class LabelType>
435template <class ResultNumber, RayConcept OtherRay>
436constexpr auto Halfplane<PointType, LabelType>::squaredDistance(const OtherRay& other) const {
437 if (intersects(other)) {
438 return ResultNumber{};
439 }
440 return asLine().template squaredDistance<ResultNumber>(other);
441}
442
443template <class PointType, class LabelType>
444template <class ResultNumber, HalfplaneConcept OtherHalfplane>
445constexpr auto Halfplane<PointType, LabelType>::squaredDistance(const OtherHalfplane& other) const {
446 if (intersects(other)) {
447 return ResultNumber{};
448 }
449 return asLine().template squaredDistance<ResultNumber>(other.asLine());
450}
451
452// -----------------------------------------------------------------------------
453// Rectangle
454
455template <class PointType, class LabelType>
456template <class Left, class Right>
457constexpr auto Rectangle<PointType, LabelType>::axisDistance(const Left& first_min, const Left& first_max, const Right& second_min, const Right& second_max)
458 -> std::common_type_t<Left, Right> {
459 using Distance = std::common_type_t<Left, Right>;
460
461 if (first_max < second_min) {
462 return static_cast<Distance>(second_min) - static_cast<Distance>(first_max);
463 }
464
465 if (second_max < first_min) {
466 return static_cast<Distance>(first_min) - static_cast<Distance>(second_max);
467 }
468
469 return Distance{};
470}
471
472template <class PointType, class LabelType>
473template <class ResultNumber, PointConcept OtherPoint>
474constexpr auto Rectangle<PointType, LabelType>::squaredDistance(const OtherPoint& point) const {
475 // There is no nearest point of the empty set, so it has no distance to
476 // anything; asking for one is a precondition violation.
477 assert(!empty());
478 const ResultNumber dx = static_cast<ResultNumber>(axisDistance(min().x(), max().x(), point.x(), point.x()));
479 const ResultNumber dy = static_cast<ResultNumber>(axisDistance(min().y(), max().y(), point.y(), point.y()));
480 return dx * dx + dy * dy;
481}
482
483template <class PointType, class LabelType>
484template <class ResultNumber, LineConcept OtherLine>
485constexpr auto Rectangle<PointType, LabelType>::squaredDistance(const OtherLine& other) const {
486 // There is no nearest point of the empty set, so it has no distance to
487 // anything; asking for one is a precondition violation.
488 assert(!empty());
489 if (intersects(other)) {
490 return ResultNumber{};
491 }
492
493 const auto rectangle_vertices = vertices();
494 auto best_distance = other.template squaredDistance<ResultNumber>(rectangle_vertices[0]);
495 for (std::size_t index = 1; index < rectangle_vertices.size(); ++index) {
496 const auto current_distance = other.template squaredDistance<ResultNumber>(rectangle_vertices[index]);
497 if (current_distance < best_distance) {
498 best_distance = current_distance;
499 }
500 }
501
502 return best_distance;
503}
504
505template <class PointType, class LabelType>
506template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
507constexpr auto Rectangle<PointType, LabelType>::squaredDistance(const OtherOrientedLine& other) const {
508 // There is no nearest point of the empty set, so it has no distance to
509 // anything; asking for one is a precondition violation.
510 assert(!empty());
511 return this->template squaredDistance<ResultNumber>(other.asLine());
512}
513
514template <class PointType, class LabelType>
515template <class ResultNumber, SegmentConcept OtherSegment>
516constexpr auto Rectangle<PointType, LabelType>::squaredDistance(const OtherSegment& other) const {
517 // There is no nearest point of the empty set, so it has no distance to
518 // anything; asking for one is a precondition violation.
519 assert(!empty());
520 if (intersects(other)) {
521 return ResultNumber{};
522 }
523
524 auto best_distance = this->template squaredDistance<ResultNumber>(other.min());
525 const auto other_max_distance = this->template squaredDistance<ResultNumber>(other.max());
526 if (other_max_distance < best_distance) {
527 best_distance = other_max_distance;
528 }
529
530 const auto rectangle_edges = edges();
531 for (const auto& edge : rectangle_edges) {
532 const auto current_distance = edge.template squaredDistance<ResultNumber>(other);
533 if (current_distance < best_distance) {
534 best_distance = current_distance;
535 }
536 }
537
538 return best_distance;
539}
540
541template <class PointType, class LabelType>
542template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
543constexpr auto Rectangle<PointType, LabelType>::squaredDistance(const OtherOrientedSegment& other) const {
544 // There is no nearest point of the empty set, so it has no distance to
545 // anything; asking for one is a precondition violation.
546 assert(!empty());
548}
549
550template <class PointType, class LabelType>
551template <class ResultNumber, RayConcept OtherRay>
552constexpr auto Rectangle<PointType, LabelType>::squaredDistance(const OtherRay& other) const {
553 // There is no nearest point of the empty set, so it has no distance to
554 // anything; asking for one is a precondition violation.
555 assert(!empty());
556 if (intersects(other)) {
557 return ResultNumber{};
558 }
559
560 auto best_distance = this->template squaredDistance<ResultNumber>(other.source());
561 const auto rectangle_edges = edges();
562 for (const auto& edge : rectangle_edges) {
563 const auto current_distance = other.template squaredDistance<ResultNumber>(edge);
564 if (current_distance < best_distance) {
565 best_distance = current_distance;
566 }
567 }
568
569 return best_distance;
570}
571
572template <class PointType, class LabelType>
573template <class ResultNumber, HalfplaneConcept OtherHalfplane>
574constexpr auto Rectangle<PointType, LabelType>::squaredDistance(const OtherHalfplane& other) const {
575 // There is no nearest point of the empty set, so it has no distance to
576 // anything; asking for one is a precondition violation.
577 assert(!empty());
578 if (intersects(other)) {
579 return ResultNumber{};
580 }
581 return this->template squaredDistance<ResultNumber>(other.asLine());
582}
583
584template <class PointType, class LabelType>
585template <class ResultNumber, RectangleConcept OtherRectangle>
586constexpr auto Rectangle<PointType, LabelType>::squaredDistance(const OtherRectangle& other) const {
587 // There is no nearest point of the empty set, so it has no distance to
588 // anything; asking for one is a precondition violation.
589 assert(!empty());
590 const ResultNumber dx = static_cast<ResultNumber>(axisDistance(min().x(), max().x(), other.min().x(), other.max().x()));
591 const ResultNumber dy = static_cast<ResultNumber>(axisDistance(min().y(), max().y(), other.min().y(), other.max().y()));
592 return dx * dx + dy * dy;
593}
594
595template <class PointType, class LabelType>
596template <class ResultNumber, RectangleConcept OtherRectangle>
597constexpr auto Rectangle<PointType, LabelType>::squaredHausdorffDistance(const OtherRectangle& other) const {
598 // There is no nearest point of the empty set, so it has no distance to
599 // anything; asking for one is a precondition violation.
600 assert(!empty());
601 const auto worst_from_this = detail::maxVertexSquaredDistance<ResultNumber>(*this, other);
602 const auto worst_from_other = detail::maxVertexSquaredDistance<ResultNumber>(other, *this);
603 return worst_from_this > worst_from_other ? worst_from_this : worst_from_other;
604}
605
606template <class PointType, class LabelType>
607template <class ResultNumber, PointConcept OtherPoint>
608constexpr auto Rectangle<PointType, LabelType>::squaredHausdorffDistance(const OtherPoint& point) const {
609 // There is no nearest point of the empty set, so it has no distance to
610 // anything; asking for one is a precondition violation.
611 assert(!empty());
612 return detail::maxVertexSquaredDistance<ResultNumber>(*this, point);
613}
614
615template <class PointType, class LabelType>
616template <class ResultNumber, SegmentConcept OtherSegment>
617constexpr auto Rectangle<PointType, LabelType>::squaredHausdorffDistance(const OtherSegment& other) const {
618 // There is no nearest point of the empty set, so it has no distance to
619 // anything; asking for one is a precondition violation.
620 assert(!empty());
621 const auto worst_from_this = detail::maxVertexSquaredDistance<ResultNumber>(*this, other);
622 const auto worst_from_other = detail::maxVertexSquaredDistance<ResultNumber>(other, *this);
623 return worst_from_this > worst_from_other ? worst_from_this : worst_from_other;
624}
625
626template <class PointType, class LabelType>
627template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
628constexpr auto Rectangle<PointType, LabelType>::squaredHausdorffDistance(const OtherOrientedSegment& other) const {
629 // There is no nearest point of the empty set, so it has no distance to
630 // anything; asking for one is a precondition violation.
631 assert(!empty());
632 const auto worst_from_this = detail::maxVertexSquaredDistance<ResultNumber>(*this, other);
633 const auto worst_from_other = detail::maxVertexSquaredDistance<ResultNumber>(other, *this);
634 return worst_from_this > worst_from_other ? worst_from_this : worst_from_other;
635}
636
637// -----------------------------------------------------------------------------
638// Triangle
639
640template <class PointType, class LabelType>
641template <class ResultNumber, class OtherShape>
642constexpr ResultNumber Triangle<PointType, LabelType>::edgeMinSquaredDistance(const OtherShape& other) const {
643 const auto triangle_edges = edges();
644 auto best = triangle_edges[0].template squaredDistance<ResultNumber>(other);
645 for (std::size_t index = 1; index < triangle_edges.size(); ++index) {
646 const auto current = triangle_edges[index].template squaredDistance<ResultNumber>(other);
647 if (current < best) {
648 best = current;
649 }
650 }
651 return best;
652}
653
654template <class PointType, class LabelType>
655template <class ResultNumber, class OtherShape>
656constexpr ResultNumber Triangle<PointType, LabelType>::vertexMinSquaredDistance(const OtherShape& other) const {
657 const auto triangle_vertices = vertices();
658 auto best = other.template squaredDistance<ResultNumber>(triangle_vertices[0]);
659 for (std::size_t index = 1; index < triangle_vertices.size(); ++index) {
660 const auto current = other.template squaredDistance<ResultNumber>(triangle_vertices[index]);
661 if (current < best) {
662 best = current;
663 }
664 }
665 return best;
666}
667
668template <class PointType, class LabelType>
669template <class ResultNumber, PointConcept OtherPoint>
670constexpr auto Triangle<PointType, LabelType>::squaredDistance(const OtherPoint& point) const {
671 if (intersects(point)) {
672 return ResultNumber{};
673 }
674 return this->template edgeMinSquaredDistance<ResultNumber>(point);
675}
676
677template <class PointType, class LabelType>
678template <class ResultNumber, SegmentConcept OtherSegment>
679constexpr auto Triangle<PointType, LabelType>::squaredDistance(const OtherSegment& other) const {
680 if (intersects(other)) {
681 return ResultNumber{};
682 }
683 return this->template edgeMinSquaredDistance<ResultNumber>(other);
684}
685
686template <class PointType, class LabelType>
687template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
688constexpr auto Triangle<PointType, LabelType>::squaredDistance(const OtherOrientedSegment& other) const {
689 if (intersects(other)) {
690 return ResultNumber{};
691 }
692 return this->template edgeMinSquaredDistance<ResultNumber>(other);
693}
694
695template <class PointType, class LabelType>
696template <class ResultNumber, LineConcept OtherLine>
697constexpr auto Triangle<PointType, LabelType>::squaredDistance(const OtherLine& other) const {
698 if (intersects(other)) {
699 return ResultNumber{};
700 }
701 return this->template vertexMinSquaredDistance<ResultNumber>(other);
702}
703
704template <class PointType, class LabelType>
705template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
706constexpr auto Triangle<PointType, LabelType>::squaredDistance(const OtherOrientedLine& other) const {
707 if (intersects(other)) {
708 return ResultNumber{};
709 }
710 return this->template vertexMinSquaredDistance<ResultNumber>(other);
711}
712
713template <class PointType, class LabelType>
714template <class ResultNumber, RayConcept OtherRay>
715constexpr auto Triangle<PointType, LabelType>::squaredDistance(const OtherRay& other) const {
716 if (intersects(other)) {
717 return ResultNumber{};
718 }
719 return this->template edgeMinSquaredDistance<ResultNumber>(other);
720}
721
722template <class PointType, class LabelType>
723template <class ResultNumber, HalfplaneConcept OtherHalfplane>
724constexpr auto Triangle<PointType, LabelType>::squaredDistance(const OtherHalfplane& other) const {
725 if (intersects(other)) {
726 return ResultNumber{};
727 }
728 return this->template squaredDistance<ResultNumber>(other.asLine());
729}
730
731template <class PointType, class LabelType>
732template <class ResultNumber, RectangleConcept OtherRectangle>
733constexpr auto Triangle<PointType, LabelType>::squaredDistance(const OtherRectangle& other) const {
734 if (intersects(other)) {
735 return ResultNumber{};
736 }
737 return this->template edgeMinSquaredDistance<ResultNumber>(other);
738}
739
740template <class PointType, class LabelType>
741template <class ResultNumber, TriangleConcept OtherTriangle>
742constexpr auto Triangle<PointType, LabelType>::squaredDistance(const OtherTriangle& other) const {
743 if (intersects(other)) {
744 return ResultNumber{};
745 }
746 return this->template edgeMinSquaredDistance<ResultNumber>(other);
747}
748
749template <class PointType, class LabelType>
750template <class ResultNumber, PointConcept OtherPoint>
751constexpr auto Triangle<PointType, LabelType>::squaredHausdorffDistance(const OtherPoint& point) const {
752 return detail::maxVertexSquaredDistance<ResultNumber>(*this, point);
753}
754
755template <class PointType, class LabelType>
756template <class ResultNumber, SegmentConcept OtherSegment>
757constexpr auto Triangle<PointType, LabelType>::squaredHausdorffDistance(const OtherSegment& other) const {
758 const auto worst_from_this = detail::maxVertexSquaredDistance<ResultNumber>(*this, other);
759 const auto worst_from_other = detail::maxVertexSquaredDistance<ResultNumber>(other, *this);
760 return worst_from_this > worst_from_other ? worst_from_this : worst_from_other;
761}
762
763template <class PointType, class LabelType>
764template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
765constexpr auto Triangle<PointType, LabelType>::squaredHausdorffDistance(const OtherOrientedSegment& other) const {
766 const auto worst_from_this = detail::maxVertexSquaredDistance<ResultNumber>(*this, other);
767 const auto worst_from_other = detail::maxVertexSquaredDistance<ResultNumber>(other, *this);
768 return worst_from_this > worst_from_other ? worst_from_this : worst_from_other;
769}
770
771template <class PointType, class LabelType>
772template <class ResultNumber, RectangleConcept OtherRectangle>
773constexpr auto Triangle<PointType, LabelType>::squaredHausdorffDistance(const OtherRectangle& other) const {
774 const auto worst_from_this = detail::maxVertexSquaredDistance<ResultNumber>(*this, other);
775 const auto worst_from_other = detail::maxVertexSquaredDistance<ResultNumber>(other, *this);
776 return worst_from_this > worst_from_other ? worst_from_this : worst_from_other;
777}
778
779template <class PointType, class LabelType>
780template <class ResultNumber, TriangleConcept OtherTriangle>
781constexpr auto Triangle<PointType, LabelType>::squaredHausdorffDistance(const OtherTriangle& other) const {
782 const auto worst_from_this = detail::maxVertexSquaredDistance<ResultNumber>(*this, other);
783 const auto worst_from_other = detail::maxVertexSquaredDistance<ResultNumber>(other, *this);
784 return worst_from_this > worst_from_other ? worst_from_this : worst_from_other;
785}
786
787// Convex
788
789template <class PointType_, class LabelType>
790template <class ResultNumber, PointConcept OtherPoint>
791constexpr auto Convex<PointType_, LabelType>::squaredDistance(const OtherPoint& point) const {
792 // Promoted so a query point of a different coordinate type (e.g. a Disk's
793 // inherently-double center) combines safely with this polygon's own vertex
794 // type, matching the promotion orientationDeterminant already applies below.
795 using Num = detail::dot_coordinate_t<NumberType, typename OtherPoint::NumberType>;
796 if (contains(point)) {
797 return ResultNumber{};
798 }
799 if (size() <= 32) {
800 auto edgeVector = edges();
801 ResultNumber best = edgeVector[0].template squaredDistance<ResultNumber>(point);
802 for (auto & e : edgeVector) {
803 const ResultNumber current = e.template squaredDistance<ResultNumber>(point);
804 if (current < best) {
805 best = current;
806 }
807 }
808 return best;
809 }
810
811 const std::ptrdiff_t n = static_cast<std::ptrdiff_t>(size());
812 const auto V = [this](std::ptrdiff_t i) { return get(i); };
813 const auto facing = [&](std::ptrdiff_t i) {
814 return orientationDeterminant(V(i), V(i + 1), point) < 0;
815 };
816
817 // Reference point c = (v0 + v1 + v2) / 3, strictly interior. To stay exact we
818 // work with vectors scaled by 3 (a positive factor, so signs are preserved).
819 const Num sx = detail::asNumber<Num>(V(0).x()) + detail::asNumber<Num>(V(1).x()) + detail::asNumber<Num>(V(2).x());
820 const Num sy = detail::asNumber<Num>(V(0).y()) + detail::asNumber<Num>(V(1).y()) + detail::asNumber<Num>(V(2).y());
821 const Num bx = Num{3} * detail::asNumber<Num>(point.x()) - sx; // 3 * (q - c)
822 const Num by = Num{3} * detail::asNumber<Num>(point.y()) - sy;
823
824 // Sign of (V(k) - c) x (q - c). Around the boundary this changes sign exactly
825 // twice: at the edge the ray c->q exits through (which faces q) and at the
826 // opposite edge (which does not).
827 const auto dSign = [&](std::ptrdiff_t k) -> int {
828 const Num ax = Num{3} * detail::asNumber<Num>(V(k).x()) - sx; // 3 * (V(k) - c)
829 const Num ay = Num{3} * detail::asNumber<Num>(V(k).y()) - sy;
830 const Num cross = ax * by - ay * bx;
831 return (cross > Num{0}) - (cross < Num{0});
832 };
833 // D(k) above is linear in V(k) with gradient (by, -bx); its argmax/argmin are
834 // the genuinely unimodal support function, so they bracket the two sign
835 // changes and are found in O(log n) with the existing cyclic search.
836 const auto gDot = [&](std::ptrdiff_t k) {
837 return detail::asNumber<Num>(V(k).x()) * by - detail::asNumber<Num>(V(k).y()) * bx;
838 };
839 const auto indices = std::views::iota(std::ptrdiff_t{0}, n);
840 const std::ptrdiff_t mPos = *detail::cyclicMax(
841 indices.begin(), indices.end(), [&](std::ptrdiff_t k) { return gDot(k); });
842 const std::ptrdiff_t mNeg = *detail::cyclicMax(
843 indices.begin(), indices.end(), [&](std::ptrdiff_t k) { return -gDot(k); });
844
845 const auto wrap = [n](std::ptrdiff_t a) { return ((a % n) + n) % n; };
846 // Largest offset in [0, len] for which pred(from + step * offset) holds; the
847 // predicate is true...false over the bracket, so a binary search locates the
848 // transition.
849 const auto lastWhile =
850 [&](std::ptrdiff_t from, std::ptrdiff_t step, std::ptrdiff_t len, auto pred) {
851 std::ptrdiff_t lo = 0, hi = len;
852 while (lo < hi) {
853 const std::ptrdiff_t mid = (lo + hi + 1) / 2;
854 if (pred(from + step * mid)) {
855 lo = mid;
856 } else {
857 hi = mid - 1;
858 }
859 }
860 return lo;
861 };
862
863 // Exit edge (faces q): the + -> - sign change between the two D extrema.
864 const std::ptrdiff_t anchor = mPos + lastWhile(mPos, 1, wrap(mNeg - mPos),
865 [&](std::ptrdiff_t k) { return dSign(k) > 0; });
866 // Back edge (does not face q): the - -> + sign change on the other side.
867 const std::ptrdiff_t back = mNeg + lastWhile(mNeg, 1, wrap(mPos - mNeg),
868 [&](std::ptrdiff_t k) { return dSign(k) < 0; });
870 // Grow the contiguous chain of q-facing edges around the anchor.
871 const std::ptrdiff_t hiEdge = anchor + lastWhile(anchor, 1, wrap(back - anchor), facing);
872 const std::ptrdiff_t loEdge = anchor - lastWhile(anchor, -1, wrap(anchor - back), facing);
873
874 // Over the facing chain the per-edge distance is unimodal: pick the closest.
875 const auto edgeDist = [&](std::ptrdiff_t i) {
876 return Segment<PointType>(V(i), V(i + 1)).template squaredDistance<ResultNumber>(point);
877 };
878 std::ptrdiff_t lo = 0, hi = hiEdge - loEdge;
879 while (lo < hi) {
880 const std::ptrdiff_t mid = (lo + hi) / 2;
881 if (edgeDist(loEdge + mid) < edgeDist(loEdge + mid + 1)) {
882 hi = mid;
883 } else {
884 lo = mid + 1;
885 }
886 }
887 return edgeDist(loEdge + lo);
888}
889
890template <class PointType_, class LabelType>
891template <class ResultNumber, SegmentConcept OtherSegment>
892constexpr auto Convex<PointType_, LabelType>::squaredDistance(const OtherSegment& other) const {
893 if (intersects(other)) {
894 return ResultNumber{};
895 }
896
897 if (size() <= 32) {
898 auto edgeVector = edges();
899 ResultNumber best = edgeVector[0].template squaredDistance<ResultNumber>(other);
900 for (auto& e : edgeVector) {
901 const ResultNumber current = e.template squaredDistance<ResultNumber>(other);
902 if (current < best) {
903 best = current;
904 }
905 }
906 return best;
907 }
908
909 // Disjoint, large polygon. Two witnesses come from the segment endpoints; the
910 // third (polygon vertex closest to the segment interior) lies on a vertex
911 // extremal along the segment normal, found in O(log n).
912 const std::ptrdiff_t n = static_cast<std::ptrdiff_t>(size());
913
914 ResultNumber best = this->template squaredDistance<ResultNumber>(other.min());
915 const ResultNumber from_max = this->template squaredDistance<ResultNumber>(other.max());
916 if (from_max < best) {
917 best = from_max;
918 }
919
920 // Support functional along the segment normal. The orientation determinant of
921 // (segment.min, segment.max, vertex) is the signed-distance functional — it is
922 // linear, so it is cyclic-unimodal over the CCW vertices and cyclicMax locates
923 // its extrema; those two vertices are the polygon's nearest to the segment's
924 // supporting line from either side, the only interior-witness candidates. The
925 // orientation primitive keeps the coordinate promotion overflow-safe.
926 const PointType a(other.min());
927 const PointType b(other.max());
928 const auto support = [&](std::ptrdiff_t i) {
929 return orientationDeterminant(a, b, get(i));
930 };
931 const auto indices = std::views::iota(std::ptrdiff_t{0}, n);
932 const std::ptrdiff_t hi = *detail::cyclicMax(
933 indices.begin(), indices.end(), [&](std::ptrdiff_t k) { return support(k); });
934 const std::ptrdiff_t lo = *detail::cyclicMax(
935 indices.begin(), indices.end(), [&](std::ptrdiff_t k) { return -support(k); });
936
937 const ResultNumber from_hi = other.template squaredDistance<ResultNumber>(get(hi));
938 if (from_hi < best) {
939 best = from_hi;
940 }
941 const ResultNumber from_lo = other.template squaredDistance<ResultNumber>(get(lo));
942 if (from_lo < best) {
943 best = from_lo;
944 }
945
946 return best;
947}
948
949template <class PointType_, class LabelType>
950template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
951constexpr auto Convex<PointType_, LabelType>::squaredDistance(const OtherOrientedSegment& other) const {
952 return this->template squaredDistance<ResultNumber>(
954}
955
956template <class PointType_, class LabelType>
957template <class ResultNumber, ConvexConcept OtherConvex>
958constexpr auto Convex<PointType_, LabelType>::squaredDistance(const OtherConvex& other) const {
959 if (intersects(other)) {
960 return ResultNumber{};
961 }
962
963 // Disjoint convex polygons: the closest point of one lies on its boundary, so
964 // the minimum over the edges of either polygon of the edge-to-polygon distance
965 // is the polygon-to-polygon distance. Query the edges of the polygon with
966 // fewer vertices to minimize the number of O(log n) segment searches.
967 const auto minOverEdges = [](const auto& source, const auto& target) {
968 const auto edgeVector = source.edges();
969 ResultNumber best = target.template squaredDistance<ResultNumber>(edgeVector[0]);
970 for (const auto& edge : edgeVector) {
971 const ResultNumber current = target.template squaredDistance<ResultNumber>(edge);
972 if (current < best) {
973 best = current;
974 }
975 }
976 return best;
977 };
978
979 if (size() <= other.size()) {
980 return minOverEdges(*this, other);
981 }
982 return minOverEdges(other, *this);
983}
984
985template <class PointType_, class LabelType>
986template <class ResultNumber, TriangleConcept OtherTriangle>
987constexpr auto Convex<PointType_, LabelType>::squaredDistance(const OtherTriangle& other) const {
988 return this->template squaredDistance<ResultNumber>(other.asConvex());
989}
990
991template <class PointType_, class LabelType>
992template <class ResultNumber, RectangleConcept OtherRectangle>
993constexpr auto Convex<PointType_, LabelType>::squaredDistance(const OtherRectangle& other) const {
994 return this->template squaredDistance<ResultNumber>(other.asConvex());
995}
996
997template <class PointType_, class LabelType>
998template <class ResultNumber, LineConcept OtherLine>
999constexpr auto Convex<PointType_, LabelType>::squaredDistance(const OtherLine& other) const {
1000 if (intersects(other)) {
1001 return ResultNumber{};
1002 }
1003
1004 if (size() <= 8) {
1005 ResultNumber best = other.template squaredDistance<ResultNumber>(get(0));
1006 for (std::ptrdiff_t i = 1; i < static_cast<std::ptrdiff_t>(size()); ++i) {
1007 const ResultNumber current = other.template squaredDistance<ResultNumber>(get(i));
1008 if (current < best) {
1009 best = current;
1010 }
1011 }
1012 return best;
1013 }
1014
1015 // Disjoint line: the whole polygon lies on one side, so its closest point to
1016 // the line is the vertex extremal along the line normal toward the line. The
1017 // orientation determinant of (line.min, line.max, vertex) is the signed
1018 // distance functional — linear, hence cyclic-unimodal over the CCW vertices.
1019 // cyclicMax locates its two extrema (the polygon's nearest and farthest
1020 // vertices from the line), the only closest-vertex candidates. Using the
1021 // orientation primitive keeps the coordinate promotion overflow-safe.
1022 const std::ptrdiff_t n = static_cast<std::ptrdiff_t>(size());
1023 const PointType a(other.min());
1024 const PointType b(other.max());
1025 const auto support = [&](std::ptrdiff_t i) {
1026 return orientationDeterminant(a, b, get(i));
1027 };
1028 const auto indices = std::views::iota(std::ptrdiff_t{0}, n);
1029 const std::ptrdiff_t hi = *detail::cyclicMax(
1030 indices.begin(), indices.end(), [&](std::ptrdiff_t k) { return support(k); });
1031 const std::ptrdiff_t lo = *detail::cyclicMax(
1032 indices.begin(), indices.end(), [&](std::ptrdiff_t k) { return -support(k); });
1033
1034 const ResultNumber from_hi = other.template squaredDistance<ResultNumber>(get(hi));
1035 const ResultNumber from_lo = other.template squaredDistance<ResultNumber>(get(lo));
1036 return from_hi < from_lo ? from_hi : from_lo;
1037}
1038
1039template <class PointType_, class LabelType>
1040template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
1041constexpr auto Convex<PointType_, LabelType>::squaredDistance(const OtherOrientedLine& other) const {
1042 return this->template squaredDistance<ResultNumber>(other.asLine());
1043}
1044
1045template <class PointType_, class LabelType>
1046template <class ResultNumber, RayConcept OtherRay>
1047constexpr auto Convex<PointType_, LabelType>::squaredDistance(const OtherRay& other) const {
1048 if (intersects(other)) {
1049 return ResultNumber{};
1050 }
1051
1052 if (size() <= 16) {
1053 auto edgeVector = edges();
1054 ResultNumber best = other.template squaredDistance<ResultNumber>(edgeVector[0]);
1055 for (const auto& e : edgeVector) {
1056 const ResultNumber current = other.template squaredDistance<ResultNumber>(e);
1057 if (current < best) {
1058 best = current;
1059 }
1060 }
1061 return best;
1062 }
1063
1064 // Disjoint, large polygon. One witness comes from the ray's source; the other
1065 // (polygon vertex closest to the ray interior) lies on a vertex extremal along
1066 // the ray normal, found in O(log n). The orientation determinant of
1067 // (ray.source, ray.target, vertex) is the signed-distance functional — linear,
1068 // hence cyclic-unimodal over the CCW vertices — and stays overflow-safe.
1069 const std::ptrdiff_t n = static_cast<std::ptrdiff_t>(size());
1070
1071 ResultNumber best = this->template squaredDistance<ResultNumber>(other.source());
1072
1073 const PointType a(other.source());
1074 const PointType b(other.target());
1075 const auto support = [&](std::ptrdiff_t i) {
1076 return orientationDeterminant(a, b, get(i));
1077 };
1078 const auto indices = std::views::iota(std::ptrdiff_t{0}, n);
1079 const std::ptrdiff_t hi = *detail::cyclicMax(
1080 indices.begin(), indices.end(), [&](std::ptrdiff_t k) { return support(k); });
1081 const std::ptrdiff_t lo = *detail::cyclicMax(
1082 indices.begin(), indices.end(), [&](std::ptrdiff_t k) { return -support(k); });
1083
1084 const ResultNumber from_hi = other.template squaredDistance<ResultNumber>(get(hi));
1085 if (from_hi < best) {
1086 best = from_hi;
1087 }
1088 const ResultNumber from_lo = other.template squaredDistance<ResultNumber>(get(lo));
1089 if (from_lo < best) {
1090 best = from_lo;
1091 }
1092
1093 return best;
1094}
1095
1096template <class PointType_, class LabelType>
1097template <class ResultNumber, HalfplaneConcept OtherHalfplane>
1098constexpr auto Convex<PointType_, LabelType>::squaredDistance(const OtherHalfplane& other) const {
1099 if (intersects(other)) {
1100 return ResultNumber{};
1101 }
1102
1103 // Disjoint: the polygon lies entirely on the far side of the boundary line,
1104 // so the closest point of the half-plane is on its boundary.
1105 return this->template squaredDistance<ResultNumber>(other.asLine());
1106}
1107
1108template <class PointType_, class LabelType>
1109template <class ResultNumber, PointConcept OtherPoint>
1110constexpr auto Convex<PointType_, LabelType>::squaredHausdorffDistance(const OtherPoint& point) const {
1111 return detail::maxVertexSquaredDistance<ResultNumber>(*this, point);
1112}
1113
1114template <class PointType_, class LabelType>
1115template <class ResultNumber, SegmentConcept OtherSegment>
1116constexpr auto Convex<PointType_, LabelType>::squaredHausdorffDistance(const OtherSegment& other) const {
1117 const auto worst_from_this = detail::maxVertexSquaredDistance<ResultNumber>(*this, other);
1118 const auto worst_from_other = detail::maxVertexSquaredDistance<ResultNumber>(other, *this);
1119 return worst_from_this > worst_from_other ? worst_from_this : worst_from_other;
1120}
1121
1122template <class PointType_, class LabelType>
1123template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
1124constexpr auto Convex<PointType_, LabelType>::squaredHausdorffDistance(const OtherOrientedSegment& other) const {
1125 const auto worst_from_this = detail::maxVertexSquaredDistance<ResultNumber>(*this, other);
1126 const auto worst_from_other = detail::maxVertexSquaredDistance<ResultNumber>(other, *this);
1127 return worst_from_this > worst_from_other ? worst_from_this : worst_from_other;
1128}
1129
1130template <class PointType_, class LabelType>
1131template <class ResultNumber, RectangleConcept OtherRectangle>
1132constexpr auto Convex<PointType_, LabelType>::squaredHausdorffDistance(const OtherRectangle& other) const {
1133 const auto worst_from_this = detail::maxVertexSquaredDistance<ResultNumber>(*this, other);
1134 const auto worst_from_other = detail::maxVertexSquaredDistance<ResultNumber>(other, *this);
1135 return worst_from_this > worst_from_other ? worst_from_this : worst_from_other;
1136}
1137
1138template <class PointType_, class LabelType>
1139template <class ResultNumber, TriangleConcept OtherTriangle>
1140constexpr auto Convex<PointType_, LabelType>::squaredHausdorffDistance(const OtherTriangle& other) const {
1141 const auto worst_from_this = detail::maxVertexSquaredDistance<ResultNumber>(*this, other);
1142 const auto worst_from_other = detail::maxVertexSquaredDistance<ResultNumber>(other, *this);
1143 return worst_from_this > worst_from_other ? worst_from_this : worst_from_other;
1144}
1145
1146template <class PointType_, class LabelType>
1147template <class ResultNumber, ConvexConcept OtherConvex>
1148constexpr auto Convex<PointType_, LabelType>::squaredHausdorffDistance(const OtherConvex& other) const {
1149 const auto worst_from_this = detail::maxVertexSquaredDistance<ResultNumber>(*this, other);
1150 const auto worst_from_other = detail::maxVertexSquaredDistance<ResultNumber>(other, *this);
1151 return worst_from_this > worst_from_other ? worst_from_this : worst_from_other;
1152}
1153
1154// -----------------------------------------------------------------------------
1155// Disk
1156
1157namespace detail {
1158
1171template <class Float = double, class DiskType, class OtherShape>
1172Float diskExteriorSquaredDistance(const DiskType& disk, const OtherShape& other) {
1173 const Float gap =
1174 std::sqrt(other.template squaredDistance<Float>(disk.template center<Float>()))
1175 - disk.template radius<Float>();
1176 return gap * gap;
1177}
1178
1179} // namespace detail
1180
1181template <class PointType_, class LabelType>
1182template <class ResultNumber, DiskConcept OtherDisk>
1183detail::floating_result_t<ResultNumber> Convex<PointType_, LabelType>::squaredDistance(
1184 const OtherDisk& other) const {
1185 using Float = detail::floating_result_t<ResultNumber>;
1186 if (intersects(other)) {
1187 return Float{0};
1188 }
1189 return detail::diskExteriorSquaredDistance<Float>(other, *this);
1190}
1191
1192template <class PointType_, class TLabel>
1193template <class ResultNumber, PointConcept OtherPoint>
1194detail::floating_result_t<ResultNumber> Disk<PointType_, TLabel>::squaredDistance(
1195 const OtherPoint& point) const {
1196 using Float = detail::floating_result_t<ResultNumber>;
1197 if (contains(point)) {
1198 return Float{0};
1199 }
1200
1201 // Exterior point: the nearest point of the disk is on the circle, so the
1202 // distance is |point - center| - radius. The squared distance is generally
1203 // irrational, hence the floating result. Evaluate the gap directly as
1204 // sqrt(dc2) - sqrt(r2); this well-conditioned form avoids the catastrophic
1205 // cancellation of the algebraically equal dc2 + r2 - 2*sqrt(dc2 * r2).
1206 const Float gap = center<Float>().template distance<Float>(point) - radius<Float>();
1207 return gap * gap;
1208}
1209
1210template <class PointType_, class TLabel>
1211template <class ResultNumber, SegmentConcept OtherSegment>
1212detail::floating_result_t<ResultNumber> Disk<PointType_, TLabel>::squaredDistance(
1213 const OtherSegment& other) const {
1214 using Float = detail::floating_result_t<ResultNumber>;
1215 if (intersects(other)) {
1216 return Float{0};
1217 }
1218 return detail::diskExteriorSquaredDistance<Float>(*this, other);
1219}
1220
1221template <class PointType_, class TLabel>
1222template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
1223detail::floating_result_t<ResultNumber> Disk<PointType_, TLabel>::squaredDistance(
1224 const OtherOrientedSegment& other) const {
1225 using Float = detail::floating_result_t<ResultNumber>;
1226 if (intersects(other)) {
1227 return Float{0};
1228 }
1229 return detail::diskExteriorSquaredDistance<Float>(*this, other);
1230}
1231
1232template <class PointType_, class TLabel>
1233template <class ResultNumber, LineConcept OtherLine>
1234detail::floating_result_t<ResultNumber> Disk<PointType_, TLabel>::squaredDistance(
1235 const OtherLine& other) const {
1236 using Float = detail::floating_result_t<ResultNumber>;
1237 if (intersects(other)) {
1238 return Float{0};
1239 }
1240 return detail::diskExteriorSquaredDistance<Float>(*this, other);
1241}
1242
1243template <class PointType_, class TLabel>
1244template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
1245detail::floating_result_t<ResultNumber> Disk<PointType_, TLabel>::squaredDistance(
1246 const OtherOrientedLine& other) const {
1247 using Float = detail::floating_result_t<ResultNumber>;
1248 if (intersects(other)) {
1249 return Float{0};
1250 }
1251 return detail::diskExteriorSquaredDistance<Float>(*this, other);
1252}
1253
1254template <class PointType_, class TLabel>
1255template <class ResultNumber, RayConcept OtherRay>
1256detail::floating_result_t<ResultNumber> Disk<PointType_, TLabel>::squaredDistance(
1257 const OtherRay& other) const {
1258 using Float = detail::floating_result_t<ResultNumber>;
1259 if (intersects(other)) {
1260 return Float{0};
1261 }
1262 return detail::diskExteriorSquaredDistance<Float>(*this, other);
1263}
1264
1265template <class PointType_, class TLabel>
1266template <class ResultNumber, HalfplaneConcept OtherHalfplane>
1267detail::floating_result_t<ResultNumber> Disk<PointType_, TLabel>::squaredDistance(
1268 const OtherHalfplane& other) const {
1269 using Float = detail::floating_result_t<ResultNumber>;
1270 if (intersects(other)) {
1271 return Float{0};
1272 }
1273 return detail::diskExteriorSquaredDistance<Float>(*this, other);
1274}
1275
1276template <class PointType_, class TLabel>
1277template <class ResultNumber, RectangleConcept OtherRectangle>
1278detail::floating_result_t<ResultNumber> Disk<PointType_, TLabel>::squaredDistance(
1279 const OtherRectangle& other) const {
1280 using Float = detail::floating_result_t<ResultNumber>;
1281 if (intersects(other)) {
1282 return Float{0};
1283 }
1284 return detail::diskExteriorSquaredDistance<Float>(*this, other);
1285}
1286
1287template <class PointType_, class TLabel>
1288template <class ResultNumber, TriangleConcept OtherTriangle>
1289detail::floating_result_t<ResultNumber> Disk<PointType_, TLabel>::squaredDistance(
1290 const OtherTriangle& other) const {
1291 using Float = detail::floating_result_t<ResultNumber>;
1292 if (intersects(other)) {
1293 return Float{0};
1294 }
1295 return detail::diskExteriorSquaredDistance<Float>(*this, other);
1296}
1297
1298template <class PointType_, class TLabel>
1299template <class ResultNumber, DiskConcept OtherDisk>
1300detail::floating_result_t<ResultNumber> Disk<PointType_, TLabel>::squaredDistance(
1301 const OtherDisk& other) const {
1302 using Float = detail::floating_result_t<ResultNumber>;
1303 if (intersects(other)) {
1304 return Float{0};
1305 }
1306
1307 // Disjoint disks: the nearest points lie on the two circles along the line
1308 // through the centers, so the distance is the center separation minus both
1309 // radii. Subtracting the radii from the directly computed center distance is
1310 // well-conditioned (no catastrophic cancellation in the squared form).
1311 const Float gap = center<Float>().template distance<Float>(other.template center<Float>())
1312 - radius<Float>() - other.template radius<Float>();
1313 return gap * gap;
1314}
1315
1316// -----------------------------------------------------------------------------
1317// Polygon
1318
1319template <class PointType_, class TLabel>
1320template <class ResultNumber, class OtherShape>
1321constexpr ResultNumber Polygon<PointType_, TLabel>::edgeMinSquaredDistance(const OtherShape& other) const {
1322 const auto boundaryEdges = edges();
1323 ResultNumber best = boundaryEdges[0].template squaredDistance<ResultNumber>(other);
1324 for (std::size_t index = 1; index < boundaryEdges.size(); ++index) {
1325 const ResultNumber current = boundaryEdges[index].template squaredDistance<ResultNumber>(other);
1326 if (current < best) {
1327 best = current;
1328 }
1329 }
1330 return best;
1331}
1332
1333template <class PointType_, class TLabel>
1334template <class ResultNumber, PointConcept OtherPoint>
1335constexpr auto Polygon<PointType_, TLabel>::squaredDistance(const OtherPoint& point) const {
1336 if (intersects(point)) {
1337 return ResultNumber{};
1338 }
1339 return this->template edgeMinSquaredDistance<ResultNumber>(point);
1340}
1341
1342template <class PointType_, class TLabel>
1343template <class ResultNumber, SegmentConcept OtherSegment>
1344constexpr auto Polygon<PointType_, TLabel>::squaredDistance(const OtherSegment& other) const {
1345 if (intersects(other)) {
1346 return ResultNumber{};
1347 }
1348 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1349}
1350
1351template <class PointType_, class TLabel>
1352template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
1353constexpr auto Polygon<PointType_, TLabel>::squaredDistance(const OtherOrientedSegment& other) const {
1354 if (intersects(other)) {
1355 return ResultNumber{};
1356 }
1357 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1358}
1359
1360template <class PointType_, class TLabel>
1361template <class ResultNumber, LineConcept OtherLine>
1362constexpr auto Polygon<PointType_, TLabel>::squaredDistance(const OtherLine& other) const {
1363 if (intersects(other)) {
1364 return ResultNumber{};
1365 }
1366 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1367}
1368
1369template <class PointType_, class TLabel>
1370template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
1371constexpr auto Polygon<PointType_, TLabel>::squaredDistance(const OtherOrientedLine& other) const {
1372 if (intersects(other)) {
1373 return ResultNumber{};
1374 }
1375 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1376}
1377
1378template <class PointType_, class TLabel>
1379template <class ResultNumber, RayConcept OtherRay>
1380constexpr auto Polygon<PointType_, TLabel>::squaredDistance(const OtherRay& other) const {
1381 if (intersects(other)) {
1382 return ResultNumber{};
1383 }
1384 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1385}
1386
1387template <class PointType_, class TLabel>
1388template <class ResultNumber, HalfplaneConcept OtherHalfplane>
1389constexpr auto Polygon<PointType_, TLabel>::squaredDistance(const OtherHalfplane& other) const {
1390 if (intersects(other)) {
1391 return ResultNumber{};
1392 }
1393 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1394}
1395
1396template <class PointType_, class TLabel>
1397template <class ResultNumber, RectangleConcept OtherRectangle>
1398constexpr auto Polygon<PointType_, TLabel>::squaredDistance(const OtherRectangle& other) const {
1399 if (intersects(other)) {
1400 return ResultNumber{};
1401 }
1402 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1403}
1404
1405template <class PointType_, class TLabel>
1406template <class ResultNumber, TriangleConcept OtherTriangle>
1407constexpr auto Polygon<PointType_, TLabel>::squaredDistance(const OtherTriangle& other) const {
1408 if (intersects(other)) {
1409 return ResultNumber{};
1410 }
1411 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1412}
1413
1414template <class PointType_, class TLabel>
1415template <class ResultNumber, ConvexConcept OtherConvex>
1416constexpr auto Polygon<PointType_, TLabel>::squaredDistance(const OtherConvex& other) const {
1417 if (intersects(other)) {
1418 return ResultNumber{};
1419 }
1420 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1421}
1422
1423template <class PointType_, class TLabel>
1424template <class ResultNumber, PolygonConcept OtherPolygon>
1425constexpr auto Polygon<PointType_, TLabel>::squaredDistance(const OtherPolygon& other) const {
1426 if (intersects(other)) {
1427 return ResultNumber{};
1428 }
1429 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1430}
1431
1432template <class PointType_, class TLabel>
1433template <class ResultNumber, class DiskPointType, class DiskLabel>
1434detail::floating_result_t<ResultNumber> Polygon<PointType_, TLabel>::squaredDistance(
1435 const Disk<DiskPointType, DiskLabel>& disk) const {
1436 using Float = detail::floating_result_t<ResultNumber>;
1437 if (intersects(disk)) {
1438 return Float{0};
1439 }
1440 return detail::diskExteriorSquaredDistance<Float>(disk, *this);
1441}
1442
1443template <class PointType_, class TLabel>
1444template <class ResultNumber, MonotoneChainConcept OtherChain>
1445constexpr auto Polygon<PointType_, TLabel>::squaredDistance(const OtherChain& other) const {
1446 if (intersects(other)) {
1447 return ResultNumber{};
1448 }
1449 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1450}
1451
1452// -----------------------------------------------------------------------------
1453// MonotoneChain
1454
1455template <class PointType, class LabelType, class Storage>
1456template <class ResultNumber, class OtherShape>
1457constexpr ResultNumber MonotoneChain<PointType, LabelType, Storage>::edgeMinSquaredDistance(const OtherShape& other) const {
1458 assert(size() >= 1);
1459 if (size() == 1) {
1460 // A chain collapsed to a single vertex has no edge to measure from;
1461 // its distance is the distance from that vertex.
1462 return (*this)[0].template squaredDistance<ResultNumber>(other);
1463 }
1464 ResultNumber best = this->template boundaryAt<false>(0).template squaredDistance<ResultNumber>(other);
1465 for (std::size_t index = 1; index + 1 < size(); ++index) {
1466 const ResultNumber current =
1467 this->template boundaryAt<false>(index).template squaredDistance<ResultNumber>(other);
1468 if (current < best) {
1469 best = current;
1470 }
1471 }
1472 return best;
1473}
1474
1475template <class PointType, class LabelType, class Storage>
1476template <class ResultNumber, PointConcept OtherPoint>
1477constexpr auto MonotoneChain<PointType, LabelType, Storage>::squaredDistance(const OtherPoint& point) const {
1478 if (intersects(point)) {
1479 return ResultNumber{};
1480 }
1481 return this->template edgeMinSquaredDistance<ResultNumber>(point);
1482}
1483
1484template <class PointType, class LabelType, class Storage>
1485template <class ResultNumber, SegmentConcept OtherSegment>
1486constexpr auto MonotoneChain<PointType, LabelType, Storage>::squaredDistance(const OtherSegment& other) const {
1487 if (intersects(other)) {
1488 return ResultNumber{};
1489 }
1490 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1491}
1492
1493template <class PointType, class LabelType, class Storage>
1494template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
1495constexpr auto MonotoneChain<PointType, LabelType, Storage>::squaredDistance(const OtherOrientedSegment& other) const {
1496 if (intersects(other)) {
1497 return ResultNumber{};
1498 }
1499 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1500}
1501
1502template <class PointType, class LabelType, class Storage>
1503template <class ResultNumber, LineConcept OtherLine>
1504constexpr auto MonotoneChain<PointType, LabelType, Storage>::squaredDistance(const OtherLine& other) const {
1505 if (intersects(other)) {
1506 return ResultNumber{};
1507 }
1508 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1509}
1510
1511template <class PointType, class LabelType, class Storage>
1512template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
1513constexpr auto MonotoneChain<PointType, LabelType, Storage>::squaredDistance(const OtherOrientedLine& other) const {
1514 if (intersects(other)) {
1515 return ResultNumber{};
1516 }
1517 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1518}
1519
1520template <class PointType, class LabelType, class Storage>
1521template <class ResultNumber, RayConcept OtherRay>
1522constexpr auto MonotoneChain<PointType, LabelType, Storage>::squaredDistance(const OtherRay& other) const {
1523 if (intersects(other)) {
1524 return ResultNumber{};
1525 }
1526 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1527}
1528
1529template <class PointType, class LabelType, class Storage>
1530template <class ResultNumber, HalfplaneConcept OtherHalfplane>
1531constexpr auto MonotoneChain<PointType, LabelType, Storage>::squaredDistance(const OtherHalfplane& other) const {
1532 if (intersects(other)) {
1533 return ResultNumber{};
1534 }
1535 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1536}
1537
1538template <class PointType, class LabelType, class Storage>
1539template <class ResultNumber, RectangleConcept OtherRectangle>
1540constexpr auto MonotoneChain<PointType, LabelType, Storage>::squaredDistance(const OtherRectangle& other) const {
1541 if (intersects(other)) {
1542 return ResultNumber{};
1543 }
1544 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1545}
1546
1547template <class PointType, class LabelType, class Storage>
1548template <class ResultNumber, TriangleConcept OtherTriangle>
1549constexpr auto MonotoneChain<PointType, LabelType, Storage>::squaredDistance(const OtherTriangle& other) const {
1550 if (intersects(other)) {
1551 return ResultNumber{};
1552 }
1553 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1554}
1555
1556template <class PointType, class LabelType, class Storage>
1557template <class ResultNumber, ConvexConcept OtherConvex>
1558constexpr auto MonotoneChain<PointType, LabelType, Storage>::squaredDistance(const OtherConvex& other) const {
1559 if (intersects(other)) {
1560 return ResultNumber{};
1561 }
1562 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1563}
1564
1565template <class PointType, class LabelType, class Storage>
1566template <class ResultNumber, MonotoneChainConcept OtherChain>
1567constexpr auto MonotoneChain<PointType, LabelType, Storage>::squaredDistance(const OtherChain& other) const {
1568 if (intersects(other)) {
1569 return ResultNumber{};
1570 }
1571 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1572}
1573
1574template <class PointType, class LabelType, class Storage>
1575template <class ResultNumber, class DiskPointType, class DiskLabel>
1577 const Disk<DiskPointType, DiskLabel>& disk) const {
1578 using Float = detail::floating_result_t<ResultNumber>;
1579 if (intersects(disk)) {
1580 return Float{0};
1581 }
1582 return detail::diskExteriorSquaredDistance<Float>(disk, *this);
1583}
1584
1585// -----------------------------------------------------------------------------
1586// Polyline
1587
1588template <class PointType, class LabelType>
1589template <class ResultNumber, class OtherShape>
1590constexpr ResultNumber Polyline<PointType, LabelType>::edgeMinSquaredDistance(const OtherShape& other) const {
1591 assert(size() >= 1);
1592 if (size() == 1) {
1593 // A polyline collapsed to a single vertex has no edge to measure from;
1594 // its distance is the distance from that vertex.
1595 return (*this)[0].template squaredDistance<ResultNumber>(other);
1596 }
1597 ResultNumber best = this->template boundaryAt<false>(0).template squaredDistance<ResultNumber>(other);
1598 for (std::size_t index = 1; index + 1 < size(); ++index) {
1599 const ResultNumber current =
1600 this->template boundaryAt<false>(index).template squaredDistance<ResultNumber>(other);
1601 if (current < best) {
1602 best = current;
1603 }
1604 }
1605 return best;
1606}
1607
1608template <class PointType, class LabelType>
1609template <class ResultNumber, PointConcept OtherPoint>
1610constexpr auto Polyline<PointType, LabelType>::squaredDistance(const OtherPoint& point) const {
1611 if (intersects(point)) {
1612 return ResultNumber{};
1613 }
1614 return this->template edgeMinSquaredDistance<ResultNumber>(point);
1615}
1616
1617template <class PointType, class LabelType>
1618template <class ResultNumber, SegmentConcept OtherSegment>
1619constexpr auto Polyline<PointType, LabelType>::squaredDistance(const OtherSegment& other) const {
1620 if (intersects(other)) {
1621 return ResultNumber{};
1622 }
1623 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1624}
1625
1626template <class PointType, class LabelType>
1627template <class ResultNumber, PolylineConcept OtherPolyline>
1628constexpr auto Polyline<PointType, LabelType>::squaredDistance(const OtherPolyline& other) const {
1629 if (intersects(other)) {
1630 return ResultNumber{};
1631 }
1632 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1633}
1634
1635template <class PointType, class LabelType>
1636template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
1637constexpr auto Polyline<PointType, LabelType>::squaredDistance(const OtherOrientedSegment& other) const {
1638 if (intersects(other)) {
1639 return ResultNumber{};
1640 }
1641 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1642}
1643
1644template <class PointType, class LabelType>
1645template <class ResultNumber, LineConcept OtherLine>
1646constexpr auto Polyline<PointType, LabelType>::squaredDistance(const OtherLine& other) const {
1647 if (intersects(other)) {
1648 return ResultNumber{};
1649 }
1650 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1651}
1652
1653template <class PointType, class LabelType>
1654template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
1655constexpr auto Polyline<PointType, LabelType>::squaredDistance(const OtherOrientedLine& other) const {
1656 if (intersects(other)) {
1657 return ResultNumber{};
1658 }
1659 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1660}
1661
1662template <class PointType, class LabelType>
1663template <class ResultNumber, RayConcept OtherRay>
1664constexpr auto Polyline<PointType, LabelType>::squaredDistance(const OtherRay& other) const {
1665 if (intersects(other)) {
1666 return ResultNumber{};
1667 }
1668 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1669}
1670
1671template <class PointType, class LabelType>
1672template <class ResultNumber, HalfplaneConcept OtherHalfplane>
1673constexpr auto Polyline<PointType, LabelType>::squaredDistance(const OtherHalfplane& other) const {
1674 if (intersects(other)) {
1675 return ResultNumber{};
1676 }
1677 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1678}
1679
1680template <class PointType, class LabelType>
1681template <class ResultNumber, RectangleConcept OtherRectangle>
1682constexpr auto Polyline<PointType, LabelType>::squaredDistance(const OtherRectangle& other) const {
1683 if (intersects(other)) {
1684 return ResultNumber{};
1685 }
1686 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1687}
1688
1689template <class PointType, class LabelType>
1690template <class ResultNumber, TriangleConcept OtherTriangle>
1691constexpr auto Polyline<PointType, LabelType>::squaredDistance(const OtherTriangle& other) const {
1692 if (intersects(other)) {
1693 return ResultNumber{};
1694 }
1695 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1696}
1697
1698template <class PointType, class LabelType>
1699template <class ResultNumber, ConvexConcept OtherConvex>
1700constexpr auto Polyline<PointType, LabelType>::squaredDistance(const OtherConvex& other) const {
1701 if (intersects(other)) {
1702 return ResultNumber{};
1703 }
1704 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1705}
1706
1707template <class PointType, class LabelType>
1708template <class ResultNumber, MonotoneChainConcept OtherChain>
1709constexpr auto Polyline<PointType, LabelType>::squaredDistance(const OtherChain& other) const {
1710 if (intersects(other)) {
1711 return ResultNumber{};
1712 }
1713 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1714}
1715
1716template <class PointType, class LabelType>
1717template <class ResultNumber, class DiskPointType, class DiskLabel>
1718detail::floating_result_t<ResultNumber> Polyline<PointType, LabelType>::squaredDistance(
1719 const Disk<DiskPointType, DiskLabel>& disk) const {
1720 using Float = detail::floating_result_t<ResultNumber>;
1721 if (intersects(disk)) {
1722 return Float{0};
1723 }
1724 return detail::diskExteriorSquaredDistance<Float>(disk, *this);
1725}
1726
1727template <class PointType_, class TLabel>
1728template <class ResultNumber, PolylineConcept OtherPolyline>
1729constexpr auto Polygon<PointType_, TLabel>::squaredDistance(const OtherPolyline& other) const {
1730 if (intersects(other)) {
1731 return ResultNumber{};
1732 }
1733 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1734}
1735
1736
1737// -----------------------------------------------------------------------------
1738// HalfplaneIntersection
1739//
1740// When the region and the other shape are disjoint, the closest point of the
1741// convex region lies on its boundary, so the distance is the minimum over the
1742// region's boundary edges (segments, rays, or lines) of the edge-to-shape
1743// distance. Edges are built with an exact coordinate type; the caller's
1744// ResultNumber governs only the final cast. Querying the empty region is
1745// undefined behavior.
1746
1747namespace detail {
1748
1749template <class ResultNumber, class Region, class Other>
1750constexpr ResultNumber regionEdgesSquaredDistance(const Region& region, const Other& other) {
1751 ResultNumber best{};
1752 bool has = false;
1753 for (std::size_t i = 0; i < region.size(); ++i) {
1754 const ResultNumber current = std::visit(
1755 [&other](const auto& piece) {
1756 return static_cast<ResultNumber>(piece.template squaredDistance<ResultNumber>(other));
1757 },
1758 region.template edge<ResultNumber>(i));
1759 if (!has || current < best) {
1760 best = current;
1761 has = true;
1762 }
1763 }
1764 return best;
1765}
1766
1767} // namespace detail
1768
1769#define PGL_HPI_SQUARED_DISTANCE(ConceptName, ArgType) \
1770 template <class PointType, class LabelType> \
1771 template <class ResultNumber, ConceptName ArgType> \
1772 constexpr auto HalfplaneIntersection<PointType, LabelType>::squaredDistance( \
1773 const ArgType& other) const { \
1774 if (intersects(other)) { \
1775 return ResultNumber{}; \
1776 } \
1777 return detail::regionEdgesSquaredDistance<ResultNumber>(*this, other); \
1778 }
1779
1793
1794#undef PGL_HPI_SQUARED_DISTANCE
1795
1796template <class PointType, class LabelType>
1797template <class ResultNumber, DiskConcept OtherDisk>
1798detail::floating_result_t<ResultNumber>
1800 using Float = detail::floating_result_t<ResultNumber>;
1801 if (intersects(other)) {
1802 return Float{0};
1803 }
1804 // A distance to a disk is irrational, so the whole scan runs in Float; the
1805 // edges themselves stay exact, hence the separate E.
1806 using E = detail::region_exact_number_t<NumberType>;
1807 Float best{0};
1808 bool has = false;
1809 for (std::size_t i = 0; i < size(); ++i) {
1810 const Float current = std::visit(
1811 [&other](const auto& piece) { return other.template squaredDistance<Float>(piece); },
1812 this->template edge<E>(i));
1813 if (!has || current < best) {
1814 best = current;
1815 has = true;
1816 }
1817 }
1818 return best;
1819}
1820
1821template <class PointType, class LabelType>
1822template <class ResultNumber, HalfplaneIntersectionConcept OtherRegion>
1823constexpr auto HalfplaneIntersection<PointType, LabelType>::squaredDistance(const OtherRegion& other) const {
1824 // Disjoint convex regions realize their distance on this region's
1825 // boundary; each edge re-dispatches into the other region's own edge scan.
1826 if (intersects(other)) {
1827 return ResultNumber{};
1828 }
1829 return detail::regionEdgesSquaredDistance<ResultNumber>(*this, other);
1830}
1831
1832
1833// -----------------------------------------------------------------------------
1834// PolygonWithHoles
1835//
1836// The region is closed, so a shape it misses is nearest to a point of ∂A — the
1837// edges of the outer ring and of every hole. One scan serves every operand.
1838
1839template <class PointType, class LabelType>
1840template <class ResultNumber, class OtherShape>
1841constexpr ResultNumber PolygonWithHoles<PointType, LabelType>::edgeMinSquaredDistance(const OtherShape& other) const {
1842 ResultNumber best{};
1843 bool seeded = false;
1844 anyBoundaryEdge([&](const auto& edge) {
1845 const ResultNumber current = edge.template squaredDistance<ResultNumber>(other);
1846 if (!seeded || current < best) {
1847 best = current;
1848 seeded = true;
1849 }
1850 return false;
1851 });
1852 return best;
1853}
1854
1855template <class PointType, class LabelType>
1856template <class ResultNumber, PointConcept OtherPoint>
1857constexpr auto PolygonWithHoles<PointType, LabelType>::squaredDistance(const OtherPoint& point) const {
1858 if (intersects(point)) {
1859 return ResultNumber{};
1860 }
1861 return this->template edgeMinSquaredDistance<ResultNumber>(point);
1862}
1863
1864template <class PointType, class LabelType>
1865template <class ResultNumber, SegmentConcept OtherSegment>
1866constexpr auto PolygonWithHoles<PointType, LabelType>::squaredDistance(const OtherSegment& other) const {
1867 if (intersects(other)) {
1868 return ResultNumber{};
1869 }
1870 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1871}
1872
1873template <class PointType, class LabelType>
1874template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
1875constexpr auto PolygonWithHoles<PointType, LabelType>::squaredDistance(const OtherOrientedSegment& other) const {
1876 if (intersects(other)) {
1877 return ResultNumber{};
1878 }
1879 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1880}
1881
1882template <class PointType, class LabelType>
1883template <class ResultNumber, LineConcept OtherLine>
1884constexpr auto PolygonWithHoles<PointType, LabelType>::squaredDistance(const OtherLine& other) const {
1885 if (intersects(other)) {
1886 return ResultNumber{};
1887 }
1888 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1889}
1890
1891template <class PointType, class LabelType>
1892template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
1893constexpr auto PolygonWithHoles<PointType, LabelType>::squaredDistance(const OtherOrientedLine& other) const {
1894 if (intersects(other)) {
1895 return ResultNumber{};
1896 }
1897 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1898}
1899
1900template <class PointType, class LabelType>
1901template <class ResultNumber, RayConcept OtherRay>
1902constexpr auto PolygonWithHoles<PointType, LabelType>::squaredDistance(const OtherRay& other) const {
1903 if (intersects(other)) {
1904 return ResultNumber{};
1905 }
1906 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1907}
1908
1909template <class PointType, class LabelType>
1910template <class ResultNumber, HalfplaneConcept OtherHalfplane>
1911constexpr auto PolygonWithHoles<PointType, LabelType>::squaredDistance(const OtherHalfplane& other) const {
1912 if (intersects(other)) {
1913 return ResultNumber{};
1914 }
1915 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1916}
1917
1918template <class PointType, class LabelType>
1919template <class ResultNumber, RectangleConcept OtherRectangle>
1920constexpr auto PolygonWithHoles<PointType, LabelType>::squaredDistance(const OtherRectangle& other) const {
1921 if (intersects(other)) {
1922 return ResultNumber{};
1923 }
1924 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1925}
1926
1927template <class PointType, class LabelType>
1928template <class ResultNumber, TriangleConcept OtherTriangle>
1929constexpr auto PolygonWithHoles<PointType, LabelType>::squaredDistance(const OtherTriangle& other) const {
1930 if (intersects(other)) {
1931 return ResultNumber{};
1932 }
1933 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1934}
1935
1936template <class PointType, class LabelType>
1937template <class ResultNumber, ConvexConcept OtherConvex>
1938constexpr auto PolygonWithHoles<PointType, LabelType>::squaredDistance(const OtherConvex& other) const {
1939 if (intersects(other)) {
1940 return ResultNumber{};
1941 }
1942 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1943}
1944
1945template <class PointType, class LabelType>
1946template <class ResultNumber, PolygonConcept OtherPolygon>
1947constexpr auto PolygonWithHoles<PointType, LabelType>::squaredDistance(const OtherPolygon& other) const {
1948 if (intersects(other)) {
1949 return ResultNumber{};
1950 }
1951 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1952}
1953
1954// Against another region the scan over this region's ring edges is still
1955// enough: the other region's own ring edges are what its edge-to-shape distance
1956// minimizes over, so the pair is measured boundary against boundary either way.
1957template <class PointType, class LabelType>
1958template <class ResultNumber, PolygonWithHolesConcept OtherRegion>
1959constexpr auto PolygonWithHoles<PointType, LabelType>::squaredDistance(const OtherRegion& other) const {
1960 if (intersects(other)) {
1961 return ResultNumber{};
1962 }
1963 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1964}
1965
1966template <class PointType, class LabelType>
1967template <class ResultNumber, MonotoneChainConcept OtherChain>
1968constexpr auto PolygonWithHoles<PointType, LabelType>::squaredDistance(const OtherChain& other) const {
1969 if (intersects(other)) {
1970 return ResultNumber{};
1971 }
1972 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1973}
1974
1975template <class PointType, class LabelType>
1976template <class ResultNumber, PolylineConcept OtherPolyline>
1977constexpr auto PolygonWithHoles<PointType, LabelType>::squaredDistance(const OtherPolyline& other) const {
1978 if (intersects(other)) {
1979 return ResultNumber{};
1980 }
1981 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1982}
1983
1984// The operand may be unbounded, which changes nothing: the region is bounded and
1985// closed, so a disjoint pair still realizes its distance on ∂A, and each ring
1986// edge hands the query on to the other region's own edge scan.
1987template <class PointType, class LabelType>
1988template <class ResultNumber, HalfplaneIntersectionConcept OtherIntersection>
1989constexpr auto PolygonWithHoles<PointType, LabelType>::squaredDistance(const OtherIntersection& other) const {
1990 if (intersects(other)) {
1991 return ResultNumber{};
1992 }
1993 return this->template edgeMinSquaredDistance<ResultNumber>(other);
1994}
1995
1996// The nearest point of a disjoint disk lies on its circle, so the gap is the
1997// distance to its center less its radius — generally irrational, hence the
1998// floating result every other distance to a Disk returns.
1999template <class PointType, class LabelType>
2000template <class ResultNumber, DiskConcept OtherDisk>
2001detail::floating_result_t<ResultNumber>
2003 using Float = detail::floating_result_t<ResultNumber>;
2004 if (other.isDegenerate()) {
2005 // A degenerate disk has no centre or radius to measure from: it is the
2006 // point a() when its radius is zero, and undefined otherwise.
2007 return this->template squaredDistance<Float>(other.a());
2008 }
2009 if (intersects(other)) {
2010 return Float{0};
2011 }
2012 return detail::diskExteriorSquaredDistance<Float>(other, *this);
2013}
2014
2015
2016// ---------------------------------------------------------------------------
2017// PolygonSet
2018//
2019// The distance to a union is the minimum of the distances, with no caveat and
2020// no operand it fails for. An empty set has no component to measure from and
2021// reports zero.
2022
2023template <class PointType, class LabelType>
2024template <class ResultNumber, detail::SetOperandConcept OtherShape>
2025auto PolygonSet<PointType, LabelType>::squaredDistance(const OtherShape& other) const {
2026 return minOverComponents([&other](const ComponentType& component) {
2027 return component.template squaredDistance<ResultNumber>(other);
2028 });
2029}
2030
2031template <class PointType, class LabelType>
2032template <class ResultNumber, PolygonSetConcept OtherSet>
2033auto PolygonSet<PointType, LabelType>::squaredDistance(const OtherSet& other) const {
2034 ResultNumber best{};
2035 bool seeded = false;
2036 for (const auto& component : other) {
2037 const ResultNumber current = this->template squaredDistance<ResultNumber>(component);
2038 if (!seeded || current < best) {
2039 best = current;
2040 seeded = true;
2041 }
2042 }
2043 return best;
2044}
2045
2046} // namespace pgl
Definition forward.hpp:315
Definition forward.hpp:312
Definition forward.hpp:309
Definition forward.hpp:320
Definition forward.hpp:310
Definition forward.hpp:308
Definition forward.hpp:306
Definition forward.hpp:316
Definition forward.hpp:321
Definition forward.hpp:311
Definition forward.hpp:313
Definition forward.hpp:307
Definition forward.hpp:314
#define PGL_HPI_SQUARED_DISTANCE(ConceptName, ArgType)
Definition distance.hpp:1769
Implementations of the 'intersection' predicate.
Definition arrangement.hpp:67
@ y
Definition intervaltree.hpp:24
@ x
Definition intervaltree.hpp:24
@ edge
Definition bitmatrix.hpp:37
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
constexpr auto orientationDeterminant(const Point< ANumber, ALabel > &a, const Point< BNumber, BLabel > &b, const Point< CNumber, CLabel > &c)
Returns the signed orientation determinant of three points.
Definition orientation.hpp:518
constexpr bool intersects(const OtherSegment &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:716
constexpr PointType get(std::ptrdiff_t index) const
Cyclic access: same as operator[] but index is taken modulo size(); negative indices wrap from the en...
Definition convex.hpp:289
constexpr auto squaredHausdorffDistance(const OtherPoint &point) const
Returns the squared Hausdorff distance to the given shape.
Definition distance.hpp:1110
constexpr auto squaredDistance(const OtherPoint &point) const
Returns the squared Euclidean distance to the given shape.
Definition distance.hpp:791
constexpr std::vector< Segment< PointType > > edges() const
Returns the edges of the convex polygon.
Definition convex.hpp:529
constexpr bool contains(const OtherPoint &point) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:1135
size_t size() const
Returns the number of vertices in the convex polygon.
Definition convex.hpp:840
PointType_ PointType
Definition convex.hpp:171
Closed Euclidean disk stored by boundary points plus optional disk label.
Definition disk.hpp:66
constexpr Point< ResultNumber, PointLabelType > center() const
Returns the center (circumcenter of the three boundary points) in an explicitly chosen coordinate typ...
Definition disk.hpp:284
constexpr bool intersects(const OtherSegment &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:904
constexpr bool contains(const OtherPoint &other) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:1015
detail::floating_result_t< ResultNumber > squaredDistance(const OtherPoint &point) const
Returns the squared Euclidean distance from this disk to a point.
Definition distance.hpp:1194
constexpr ResultNumber radius() const
Returns the radius.
Definition disk.hpp:333
constexpr auto squaredDistance(const OtherPoint &other) const
Returns the squared Euclidean distance to the given shape.
Definition distance.hpp:1780
constexpr std::size_t size() const
Returns the number of stored (non-redundant) half-planes.
Definition halfplaneintersection.hpp:596
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:1808
constexpr auto squaredDistance(const OtherPoint &point) const
Returns the squared Euclidean distance to the given shape.
Definition distance.hpp:391
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:622
constexpr Line< PointType > asLine() const
Returns the boundary line without orientation.
Definition halfplane.hpp:318
constexpr const PointType & max() const
Returns the largest stored defining point.
Definition line.hpp:189
constexpr const PointType & min() const
Returns the smallest stored defining point.
Definition line.hpp:180
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:312
constexpr auto squaredDistance(const OtherPoint &point) const
Returns the squared Euclidean distance to the given shape.
Definition distance.hpp:219
constexpr bool isDegenerate() const
Returns whether the defining points coincide.
Definition predicates.hpp:451
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:1353
constexpr auto squaredDistance(const OtherPoint &point) const
Returns the squared Euclidean distance to the given shape.
Definition distance.hpp:1477
constexpr auto squaredDistance(const OtherPoint &point) const
Returns the squared Euclidean distance to the given shape.
Definition distance.hpp:267
constexpr Line< PointType > asLine() const
Returns the line without orientation.
Definition orientedline.hpp:321
constexpr auto squaredHausdorffDistance(const OtherSegment &other) const
Returns the squared Hausdorff distance to another unordered segment.
Definition distance.hpp:197
constexpr auto squaredDistance(const OtherPoint &point) const
Returns the squared Euclidean distance to the given shape.
Definition distance.hpp:178
Two-dimensional point with optional label payload.
Definition point.hpp:129
ApproximateNumber distance(const OtherPoint &other) const
Returns the Euclidean distance to another point.
Definition distance.hpp:69
constexpr auto distanceLInf(const OtherPoint &other) const
Returns the Chebyshev distance to another point.
Definition distance.hpp:83
constexpr auto squaredHausdorffDistance(const OtherPoint &other) const
Returns the squared Hausdorff distance to another point.
Definition distance.hpp:91
constexpr auto squaredDistance(const OtherPoint &other) const
Returns the squared Euclidean distance to the given shape.
Definition distance.hpp:61
static constexpr std::size_t size()
Definition point.hpp:246
constexpr const NumberType & x() const
Returns the x coordinate.
Definition point.hpp:193
constexpr auto distanceL1(const OtherPoint &other) const
Returns the Manhattan distance to another point.
Definition distance.hpp:75
auto squaredDistance(const OtherShape &other) const
Computes the squared Euclidean distance to the other shape.
Definition distance.hpp:2025
constexpr const ComponentType & component(std::size_t index) const
Accesses a component by index.
Definition polygonset.hpp:271
PolygonWithHoles< PointType > ComponentType
Definition polygonset.hpp:169
constexpr bool intersects(const OtherPoint &point) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition polygonwithholes.hpp:1649
constexpr auto squaredDistance(const OtherPoint &point) const
Computes the squared Euclidean distance to the other shape.
Definition distance.hpp:1857
constexpr auto squaredDistance(const OtherChain &other) const
Returns the squared Euclidean distance to the given shape.
Definition distance.hpp:1445
constexpr bool intersects(const OtherChain &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:1596
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:1620
constexpr auto squaredDistance(const OtherPoint &point) const
Returns the squared Euclidean distance to the given shape.
Definition distance.hpp:1610
constexpr bool isDegenerate() const
Returns whether the defining points coincide.
Definition predicates.hpp:727
constexpr const PointType & target() const
Returns the second stored point defining the direction.
Definition ray.hpp:193
constexpr auto squaredDistance(const OtherPoint &point) const
Returns the squared Euclidean distance to the given shape.
Definition distance.hpp:300
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:409
constexpr const PointType & source() const
Returns the source point of the ray.
Definition ray.hpp:181
constexpr auto squaredHausdorffDistance(const OtherRectangle &other) const
Returns the squared Hausdorff distance to another rectangle.
Definition distance.hpp:597
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:502
constexpr const PointType & min() const
Returns the minimum corner (min x, min y).
Definition rectangle.hpp:347
constexpr bool empty() const
Returns whether the rectangle is the empty set of points.
Definition rectangle.hpp:290
constexpr std::array< Segment< PointType >, 4 > edges() const
Returns the four edges as unordered segments.
Definition bounding.hpp:199
constexpr auto squaredDistance(const OtherPoint &point) const
Returns the squared Euclidean distance to the given shape.
Definition distance.hpp:474
constexpr std::array< PointType, 4 > vertices() const
Returns the four vertices in counterclockwise order.
Definition bounding.hpp:188
constexpr std::ptrdiff_t index(const PointType &point) const
Returns the smallest index i with (*this)[i] == point, or -1 if no corner equals point.
Definition rectangle.hpp:330
constexpr const PointType & max() const
Returns the maximum corner (max x, max y).
Definition rectangle.hpp:359
Unoriented closed segment between two endpoints plus optional segment label.
Definition segment.hpp:58
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:48
constexpr const PointType & max() const
Returns the largest stored endpoint.
Definition segment.hpp:199
constexpr const PointType & min() const
Returns the smallest stored endpoint.
Definition segment.hpp:190
constexpr auto squaredDistance(const OtherPoint &point) const
Returns the squared Euclidean distance to the given shape.
Definition distance.hpp:100
constexpr auto squaredHausdorffDistance(const OtherSegment &other) const
Returns the squared Hausdorff distance to another segment.
Definition distance.hpp:152
constexpr auto squaredHausdorffDistance(const OtherPoint &point) const
Returns the squared Hausdorff distance to the given shape.
Definition distance.hpp:751
constexpr auto squaredDistance(const OtherPoint &point) const
Returns the squared Euclidean distance to the given shape.
Definition distance.hpp:670
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:134