Pangolin
Header-only C++20 plane computational geometry library
Loading...
Searching...
No Matches
minkowskierosion.hpp
Go to the documentation of this file.
1#pragma once
2
4
116
117#include <algorithm>
118#include <cstddef>
119#include <optional>
120#include <stdexcept>
121#include <type_traits>
122#include <utility>
123#include <vector>
124
125
126namespace pgl {
127
128namespace detail {
129
144template <class A, class B>
145using minkowskiErosionPoint_t = Point<
146 std::conditional_t<is_halfplane_intersection_v<std::remove_cvref_t<B>>,
147 division_result_t<typename minkowskiPoint_t<A, B>::NumberType>,
150
152template <class A, class B>
153using minkowskiErosionRegion_t = HalfplaneIntersection<minkowskiErosionPoint_t<A, B>>;
154
163template <class ResultPoint>
164struct MinkowskiErosionConstraint {
166 ResultPoint direction;
168 ResultPoint anchor;
169};
170
192template <class ResultPoint, class ShapeT>
193constexpr std::optional<std::vector<MinkowskiErosionConstraint<ResultPoint>>>
194minkowskiErosionConstraints(const ShapeT& shape) {
195 using ResultNumber = typename ResultPoint::NumberType;
196
197 std::vector<MinkowskiErosionConstraint<ResultPoint>> constraints;
198 const auto cast = [](const auto& point) {
199 return ResultPoint(detail::asNumber<ResultNumber>(point.x()),
200 detail::asNumber<ResultNumber>(point.y()));
201 };
202 const auto add = [&constraints](const ResultPoint& direction, const ResultPoint& anchor) {
203 constraints.push_back(MinkowskiErosionConstraint<ResultPoint>{direction, anchor});
204 };
205 const auto reversed = [](const ResultPoint& vector) {
206 return ResultPoint(-vector.x(), -vector.y());
207 };
208 // The four constraints whose intersection is a single point: `cross(d, ·)`
209 // reads a coordinate for each axis direction, and the two signs of it pin
210 // that coordinate from both sides.
211 const auto pin = [&add](const ResultPoint& point) {
212 const ResultNumber zero{};
213 const ResultNumber one = static_cast<ResultNumber>(1);
214 add(ResultPoint(one, zero), point);
215 add(ResultPoint(-one, zero), point);
216 add(ResultPoint(zero, one), point);
217 add(ResultPoint(zero, -one), point);
218 };
219
220 if constexpr (is_point_v<ShapeT>) {
221 pin(cast(shape));
222 } else if constexpr (is_line_v<ShapeT> || is_oriented_line_v<ShapeT> || is_ray_v<ShapeT> ||
223 is_halfplane_v<ShapeT>) {
224 const ResultPoint source = cast(shape[0]);
225 const ResultPoint target = cast(shape[1]);
226 const ResultPoint forward(target.x() - source.x(), target.y() - source.y());
227 add(forward, source);
228 if constexpr (!is_halfplane_v<ShapeT>) {
229 // A line and a ray are both pinned to their supporting line; only a
230 // half-plane keeps a side.
231 add(reversed(forward), source);
232 if constexpr (is_ray_v<ShapeT>) {
233 // `cross((dy, -dx), p - s)` is `dot(d, p - s)`, which is the cap
234 // at the source: the ray runs forward from it and no further
235 // back.
236 add(ResultPoint(forward.y(), -forward.x()), source);
237 }
238 }
239 } else if constexpr (is_halfplane_intersection_v<ShapeT>) {
240 if (shape.empty()) {
241 return std::nullopt;
242 }
243 // Stored as constraints already, and this is the receiver whose vertices
244 // the construction never has to look at.
245 for (const auto& halfplane : shape) {
246 const ResultPoint source = cast(halfplane.source());
247 const ResultPoint target = cast(halfplane.target());
248 add(ResultPoint(target.x() - source.x(), target.y() - source.y()), source);
249 }
250 } else {
251 // Bounded: the hull vertices counterclockwise, with the collinear ones
252 // dropped, so that consecutive ones span an edge of the boundary.
253 const std::vector<ResultPoint> vertices = minkowskiVertices<ResultPoint>(shape);
254 if (vertices.empty()) {
255 return std::nullopt;
256 }
257 if (vertices.size() == 1) {
258 pin(vertices.front());
259 } else if (vertices.size() == 2) {
260 const ResultPoint& from = vertices.front();
261 const ResultPoint& to = vertices.back();
262 const ResultPoint along(to.x() - from.x(), to.y() - from.y());
263 add(along, from);
264 add(reversed(along), from);
265 add(ResultPoint(along.y(), -along.x()), from);
266 add(ResultPoint(-along.y(), along.x()), to);
267 } else {
268 for (std::size_t i = 0; i < vertices.size(); ++i) {
269 const ResultPoint& from = vertices[i];
270 const ResultPoint& to = vertices[(i + 1) % vertices.size()];
271 add(ResultPoint(to.x() - from.x(), to.y() - from.y()), from);
272 }
273 }
274 }
275 return constraints;
276}
277
304template <class A, class B>
305constexpr auto minkowskiConvexErosion(const A& a, const B& b) {
306 using ResultPoint = minkowskiErosionPoint_t<A, B>;
308
309 const MinkowskiPolyhedron<ResultPoint> eroder = minkowskiPolyhedronOf<ResultPoint>(b);
310 if (eroder.empty) {
311 return Region(); // the whole plane, which is what no constraint means
312 }
313 const auto constraints = minkowskiErosionConstraints<ResultPoint>(a);
314 if (!constraints) {
315 return Region(Convex<ResultPoint>()); // nothing fits in the empty set
316 }
317
318 Region region;
319 for (const auto& constraint : *constraints) {
320 const std::optional<ResultPoint> support =
321 minkowskiInfimumPoint(eroder, constraint.direction);
322 if (!support) {
323 return Region(Convex<ResultPoint>());
324 }
325 const ResultPoint base(constraint.anchor.x() - support->x(),
326 constraint.anchor.y() - support->y());
327 region.insert(Halfplane<ResultPoint>(
328 base, ResultPoint(base.x() + constraint.direction.x(),
329 base.y() + constraint.direction.y())));
330 }
331 return region;
332}
333
348template <class HalfplaneT, class ShapeT>
349constexpr auto minkowskiHalfplaneErosion(const HalfplaneT& halfplane, const ShapeT& shape) {
350 using ResultPoint = minkowskiErosionPoint_t<HalfplaneT, ShapeT>;
351 using ResultNumber = typename ResultPoint::NumberType;
353
354 const auto& source = halfplane.source();
355 const auto& target = halfplane.target();
356 const ResultNumber dx =
357 detail::asNumber<ResultNumber>(target.x()) - detail::asNumber<ResultNumber>(source.x());
358 const ResultNumber dy =
359 detail::asNumber<ResultNumber>(target.y()) - detail::asNumber<ResultNumber>(source.y());
360
361 bool found = false;
362 ResultPoint support(ResultNumber{}, ResultNumber{});
363 ResultNumber best{};
364 for (const auto& vertex : shape.vertices()) {
365 const ResultNumber x = detail::asNumber<ResultNumber>(vertex.x());
366 const ResultNumber y = detail::asNumber<ResultNumber>(vertex.y());
367 const ResultNumber side = dx * y - dy * x; // cross(d, vertex)
368 if (!found || side < best) {
369 found = true;
370 best = side;
371 support = ResultPoint(x, y);
372 }
373 }
374 if (!found) {
375 throw std::logic_error(
376 "Halfplane::minkowskiErosion by a shape that covers no point is the whole plane, "
377 "which no half-plane represents");
378 }
379 const auto moved = [&support](const auto& point) {
380 return ResultPoint(detail::asNumber<ResultNumber>(point.x()) - support.x(),
381 detail::asNumber<ResultNumber>(point.y()) - support.y());
382 };
383 return ResultHalfplane(moved(source), moved(target));
384}
385
397template <class A, class B>
398 requires MinkowskiSummableConcept<A, B>
399constexpr auto minkowskiErosionOf(const A& a, const B& b) {
400 using ResultPoint = minkowskiPoint_t<A, B>;
401
402 if constexpr (is_shape_v<A> || is_shape_v<B>) {
403 using ResultShape = Shape<ResultPoint>;
404 // As for the sum: only the pair of stored alternatives decides whether
405 // the erosion exists and fits the wrapper, and neither is known until
406 // run time.
407 const auto erode = [](const auto& left, const auto& right) -> ResultShape {
408 if constexpr (requires { ResultShape(minkowskiErosionOf(left, right)); }) {
409 return ResultShape(minkowskiErosionOf(left, right));
410 } else {
411 throw std::logic_error(
412 "Shape::minkowskiErosion is not defined for this pair of alternatives, or "
413 "its result does not fit the wrapper's point type");
414 }
415 };
416 if constexpr (is_shape_v<A> && is_shape_v<B>) {
417 return std::visit(erode, a.variant(), b.variant());
418 } else if constexpr (is_shape_v<A>) {
419 return std::visit([&b, &erode](const auto& left) { return erode(left, b); },
420 a.variant());
421 } else {
422 return std::visit([&a, &erode](const auto& right) { return erode(a, right); },
423 b.variant());
424 }
425 } else if constexpr (is_empty_shape_v<B>) {
426 // Every translate of the empty set fits in every shape, so this is the
427 // whole plane whatever the receiver -- the one answer of a shrinking
428 // operation that is larger than what it shrinks.
429 return minkowskiErosionRegion_t<A, B>();
430 } else if constexpr (is_point_v<B>) {
431 // Eroding by one point is the translation by its negation, and every
432 // shape kind is closed under it, so this is the one erosion that keeps
433 // the receiver's own type -- the empty shape included, which is why this
434 // comes before the empty receiver below.
435 return minkowskiTranslated(a, -b);
436 } else if constexpr (is_empty_shape_v<A>) {
437 using Region = minkowskiErosionRegion_t<A, B>;
438 // Nothing fits in the empty set except the empty set.
439 return coversNoPoint(b) ? Region() : Region(Convex<minkowskiErosionPoint_t<A, B>>());
440 } else if constexpr (is_disk_v<B>) {
441 // A disk operand reaches here only against a `Point` receiver -- an
442 // `EmptyShape` one is answered above, and @ref Disk and @ref Halfplane
443 // carry their own overloads -- so the only disk that fits is one
444 // covering a single point, and its centre is then its own defining
445 // point, exactly.
446 using Region = minkowskiErosionRegion_t<A, B>;
447 if (!b.isPoint()) {
448 return Region(Convex<minkowskiErosionPoint_t<A, B>>());
449 }
450 return Region(minkowskiConvexErosion(a, b.a()));
451 } else if constexpr (is_rectangle_v<A> && is_rectangle_v<B>) {
452 // Two axis-aligned rectangles are the one non-trivial pair closed under
453 // the erosion, as they are under the sum: an interval fits in an
454 // interval by both ends at once, so the minima and the maxima subtract
455 // and the result is empty exactly when a side of the operand is longer.
456 using ResultRectangle = Rectangle<ResultPoint>;
457 if (b.empty()) {
458 throw std::logic_error(
459 "Rectangle::minkowskiErosion by an empty rectangle is the whole plane, which no "
460 "rectangle represents");
461 }
462 if (a.empty()) {
463 return ResultRectangle();
464 }
465 const auto corner = [](const auto& left, const auto& right) {
466 using ResultNumber = typename ResultPoint::NumberType;
467 return ResultPoint(
468 detail::asNumber<ResultNumber>(left.x()) - detail::asNumber<ResultNumber>(right.x()),
469 detail::asNumber<ResultNumber>(left.y()) -
470 detail::asNumber<ResultNumber>(right.y()));
471 };
472 const ResultPoint low = corner(a.min(), b.min());
473 const ResultPoint high = corner(a.max(), b.max());
474 if (high.x() < low.x() || high.y() < low.y()) {
475 return ResultRectangle();
476 }
477 return ResultRectangle(low, high, true);
478 } else if constexpr (is_halfplane_v<A> && !UnboundedConvexConcept<B>) {
479 // A half-plane absorbs anything bounded and stays one.
480 return minkowskiHalfplaneErosion(a, b);
481 } else {
482 // Everything left is a convex receiver clamped constraint by
483 // constraint -- and the pairs whose receiver is *not* convex, which the
484 // concept admits only against a half-plane operand: an unbounded
485 // operand fits in no bounded receiver, so the answer is the empty
486 // region, and the hull the clamp reads instead of the receiver cannot
487 // change it. See the file comment.
488 return minkowskiConvexErosion(a, b);
489 }
490}
491
544template <class ResultPoint, class ShapeA, class ShapeB>
545PolygonSet<ResultPoint> regularizedMinkowskiErosion(const ShapeA& a, const ShapeB& b) {
546 using ResultNumber = typename ResultPoint::NumberType;
547 using SumPoint = minkowskiPoint_t<ShapeA, ShapeB>;
548 using SumNumber = typename SumPoint::NumberType;
550 using ExactNumber = typename ExactPoint::NumberType;
551
552 if (coversNoPoint(b)) {
553 throw std::logic_error(
554 "minkowskiErosion by a shape that covers no point is the whole plane, which no "
555 "PolygonSet represents");
556 }
557 if (!minkowskiHasArea(a)) {
558 // Regularization keeps only what has area, and an erosion only shrinks:
559 // a chain, a polyline and a collapsed region all erode to nothing.
560 return {};
561 }
562
563 if constexpr (is_polygon_v<ShapeA> || is_polygon_with_holes_v<ShapeA>) {
564 if (minkowskiIsConvex(a)) {
565 const auto region = minkowskiConvexErosion(minkowskiAsConvex(a), b);
566 if (region.isDegenerate()) {
567 return {}; // nothing with area survives the regularization
568 }
570 Polygon<ResultPoint>(region.template asConvex<ResultNumber>().asPolygon())));
571 }
572 }
573
574 // `A ⊖ B ⊆ A` holds only when the operand covers the origin, and the last
575 // step below is a difference from the receiver, which would truncate the
576 // answer for an operand that does not: the erosion by an operand placed
577 // elsewhere is the same set translated. So the operand is moved onto one of
578 // its own vertices first — a vertex is a point of it, where a corner of its
579 // bounding box need not be — and the answer is moved back at the end.
580 const auto operandVertices = b.vertices();
581 const auto anchor = *operandVertices.begin();
582 const auto centered = minkowskiTranslated(b, -anchor);
583
584 // The window. It has to hold the receiver, so that its complement inside it
585 // is the receiver's own boundary, and every translate `a + b`, so that a
586 // point escaping the receiver escapes into that complement rather than out
587 // of the window. One unit of slack keeps the two boundaries apart.
588 const auto exact = [](const auto& value) { return detail::asNumber<ExactNumber>(value); };
589 const auto boxA = a.bbox();
590 const auto boxB = centered.bbox();
591 const ExactNumber one = static_cast<ExactNumber>(1);
592 const ExactPoint low(
593 std::min(exact(boxA.min().x()), exact(boxA.min().x()) + exact(boxB.min().x())) - one,
594 std::min(exact(boxA.min().y()), exact(boxA.min().y()) + exact(boxB.min().y())) - one);
595 const ExactPoint high(
596 std::max(exact(boxA.max().x()), exact(boxA.max().x()) + exact(boxB.max().x())) + one,
597 std::max(exact(boxA.max().y()), exact(boxA.max().y()) + exact(boxB.max().y())) + one);
598 const Rectangle<ExactPoint> window(low, high, true);
599
600 const PolygonSet<ExactPoint> outside =
601 regularizedDifference<ExactPoint>(window.asPolygon(), booleanOperand(a));
602 const PolygonSet<ExactPoint> forbidden =
603 setMinkowskiSum<ExactPoint>(outside, centered.rotated90(2));
605 regularizedDifference<ResultPoint>(booleanOperand(a), forbidden);
606 erosion -= ResultPoint(asNumber<ResultNumber>(anchor.x()), asNumber<ResultNumber>(anchor.y()));
607 return erosion;
608}
609
610} // namespace detail
611
612// -----------------------------------------------------------------------------
613// Member entry points: the pairs whose sum is one shape
614//
615// Each shape forwards to the same dispatcher, over the same pairs its
616// minkowskiSum accepts, since MinkowskiSummableConcept gates both.
617
618#define PGL_DEFINE_MINKOWSKI_EROSION(SHAPE) \
619 template <class PointType, class LabelType> \
620 template <class OtherShape> \
621 requires MinkowskiSummableConcept<SHAPE<PointType, LabelType>, OtherShape> \
622 constexpr auto SHAPE<PointType, LabelType>::minkowskiErosion(const OtherShape& other) \
623 const { \
624 return detail::minkowskiErosionOf(*this, other); \
625 }
626
642
643#undef PGL_DEFINE_MINKOWSKI_EROSION
644
645template <class Number, class Label>
646template <class OtherShape>
648constexpr auto Point<Number, Label>::minkowskiErosion(const OtherShape& other) const {
649 return detail::minkowskiErosionOf(*this, other);
650}
651
652template <class PointType>
653template <class OtherShape>
655constexpr auto EmptyShape<PointType>::minkowskiErosion(const OtherShape& other) const {
656 return detail::minkowskiErosionOf(*this, other);
657}
658
659template <class PointType, class LabelType, class Storage>
660template <class OtherShape>
663 const OtherShape& other) const {
664 return detail::minkowskiErosionOf(*this, other);
665}
666
667template <class PointType>
668template <class OtherShape>
670constexpr auto Shape<PointType>::minkowskiErosion(const OtherShape& other) const {
671 return detail::minkowskiErosionOf(*this, other);
672}
673
674// -----------------------------------------------------------------------------
675// A convex receiver eroded by a bounded operand the sum answers elsewhere
676//
677// These are the pairs minkowskiSum hands to the higher-ranked operand, because
678// the sum is commutative and the operand's concavity decides the result type.
679// The erosion is not commutative, and the *receiver's* convexity decides it
680// here: only the operand's support function is read, so a non-convex one costs
681// nothing and the answer is the same convex region every other convex receiver
682// gets.
683
684#define PGL_DEFINE_CONVEX_MINKOWSKI_EROSION(SHAPE) \
685 template <class PointType_, class TLabel> \
686 template <class OtherShape> \
687 requires (!MinkowskiSummableConcept<SHAPE<PointType_, TLabel>, OtherShape> && \
688 BoundedPolygonalConcept<OtherShape>) \
689 constexpr auto SHAPE<PointType_, TLabel>::minkowskiErosion(const OtherShape& other) \
690 const { \
691 return detail::minkowskiConvexErosion(*this, other); \
692 }
693
699
700#undef PGL_DEFINE_CONVEX_MINKOWSKI_EROSION
701
702// -----------------------------------------------------------------------------
703// The region-valued receivers
704//
705// One definition each, over the whole operand family: unlike the sum, whose
706// result type turns on whether an operand is a body, every one of these erodes
707// to a set of regions, and the construction reads the pair the same way whatever
708// the operand is.
709
710#define PGL_DEFINE_REGION_MINKOWSKI_EROSION(RECEIVER) \
711 template <class PointType_, class TLabel> \
712 template <class ResultNumber, class OtherShape> \
713 requires (!MinkowskiSummableConcept<RECEIVER<PointType_, TLabel>, OtherShape> && \
714 BoundedPolygonalConcept<OtherShape>) \
715 PolygonSet<Point<ResultNumber, typename PointType_::LabelType>> \
716 RECEIVER<PointType_, TLabel>::minkowskiErosion(const OtherShape& other) const { \
717 return detail::regularizedMinkowskiErosion< \
718 Point<ResultNumber, typename PointType_::LabelType>>(*this, other); \
719 }
720
725
726#undef PGL_DEFINE_REGION_MINKOWSKI_EROSION
727
728// The chain carries the same definition, with its third template parameter. Its
729// sum keeps a polygon-valued overload set of its own for a convex operand,
730// which the erosion has no use for: a chain has no area, so it erodes to the
731// empty set whatever it is eroded by, and the regularized engine says so
732// without building anything.
733
734template <class PointType_, class TLabel, class Storage>
735template <class ResultNumber, class OtherShape>
740 return detail::regularizedMinkowskiErosion<Point<ResultNumber, typename PointType_::LabelType>>(
741 *this, other);
742}
743
744// -----------------------------------------------------------------------------
745// The curved pairs, mirroring the two the sum can answer
746//
747// Both carry a ResultNumber of their own, for the reason
748// @ref Disk::minkowskiSum(const OtherDisk&) const does: a disk's radius is a
749// square root of what it stores, so these leave the lattice where every other
750// erosion stays on it.
751
752template <class PointType_, class TLabel>
753template <class ResultNumber, DiskConcept OtherDisk>
754std::optional<Disk<Point<ResultNumber, typename Disk<PointType_, TLabel>::PointLabelType>>>
755Disk<PointType_, TLabel>::minkowskiErosion(const OtherDisk& other) const {
756 using ResultPoint = Point<ResultNumber, PointLabelType>;
757
758 const ResultNumber radii = radius<ResultNumber>() - other.template radius<ResultNumber>();
759 if (radii < ResultNumber{}) {
760 return std::nullopt; // the operand is wider than the receiver
761 }
762 const auto leftCenter = center<ResultNumber>();
763 const auto rightCenter = other.template center<ResultNumber>();
764 return Disk<ResultPoint>(ResultPoint(leftCenter.x() - rightCenter.x(),
765 leftCenter.y() - rightCenter.y()),
766 radii);
767}
768
769template <class PointType_, class TLabel>
770template <class ResultNumber, DiskConcept OtherDisk>
774
775 const ResultNumber dx = detail::asNumber<ResultNumber>(target().x()) -
776 detail::asNumber<ResultNumber>(source().x());
777 const ResultNumber dy = detail::asNumber<ResultNumber>(target().y()) -
778 detail::asNumber<ResultNumber>(source().y());
779 // Reported the way Disk::radius reports it: an exact result type has no
780 // square root to offer, and says so rather than rounding silently.
781 if constexpr (!requires(ResultNumber v) { std::sqrt(v); }) {
782 throw std::runtime_error("std::sqrt is not available for the requested ResultNumber type");
783 } else {
784 const ResultNumber length = std::sqrt(dx * dx + dy * dy);
785
786 // The sum slides the boundary out by the disk's support point in the
787 // outward normal `(dy, -dx)/|d|`; the erosion slides it in by the same
788 // point, which is the whole difference between the two.
789 const auto center = other.template center<ResultNumber>();
790 const ResultNumber radius = other.template radius<ResultNumber>();
791 const ResultNumber offsetX = center.x() + radius * dy / length;
792 const ResultNumber offsetY = center.y() - radius * dx / length;
793
794 const auto moved = [&offsetX, &offsetY](const auto& point) {
795 return ResultPoint(detail::asNumber<ResultNumber>(point.x()) - offsetX,
796 detail::asNumber<ResultNumber>(point.y()) - offsetY);
797 };
798 return Halfplane<ResultPoint>(moved(source()), moved(target()));
799 }
800}
801
802template <class PointType_, class TLabel>
803template <class ResultNumber, HalfplaneConcept OtherHalfplane>
805Disk<PointType_, TLabel>::minkowskiErosion(const OtherHalfplane& other) const {
806 // A half-plane is unbounded and a disk is not, so no translate of one fits:
807 // the one pair whose erosion is the empty set by its types alone.
808 (void)other;
810}
811
812} // namespace pgl
Bounded polygonal primitives, convex or not.
Definition forward.hpp:373
Shape pairs whose Minkowski sum Pangolin can represent.
Definition forward.hpp:476
#define PGL_DEFINE_REGION_MINKOWSKI_EROSION(RECEIVER)
Definition minkowskierosion.hpp:710
#define PGL_DEFINE_MINKOWSKI_EROSION(SHAPE)
Definition minkowskierosion.hpp:618
#define PGL_DEFINE_CONVEX_MINKOWSKI_EROSION(SHAPE)
Definition minkowskierosion.hpp:684
Minkowski sums whose result is not a single convex shape: one region when the substantive case is con...
Definition arrangement.hpp:67
HalfplaneIntersection() -> HalfplaneIntersection< Point<>, NoLabel >
Definition halfplaneintersection.hpp:2308
@ y
Definition intervaltree.hpp:24
@ x
Definition intervaltree.hpp:24
Rectangle() -> Rectangle< Point<>, NoLabel >
Definition rectangle.hpp:2384
@ vertex
Definition bitmatrix.hpp:37
Point() -> Point< int >
PolygonSet() -> PolygonSet< Point<>, NoLabel >
Definition polygonset.hpp:1699
OrientedSegment() -> OrientedSegment< Point<>, NoLabel >
Shape(const std::variant< T, Ts... > &) -> Shape< detail::shape_point_type_t< T > >
PolygonWithHoles() -> PolygonWithHoles< Point<>, NoLabel >
Definition polygonwithholes.hpp:3093
Convex() -> Convex< Point<>, NoLabel >
Definition convex.hpp:3311
Segment() -> Segment< Point<>, NoLabel >
Halfplane() -> Halfplane< Point<>, NoLabel >
Polygon() -> Polygon< Point<>, NoLabel >
Definition polygon.hpp:3200
Disk() -> Disk< Point<>, NoLabel >
Deduces a default disk with Point<> boundary points and no label.
Definition disk.hpp:1691
Triangle() -> Triangle< Point<>, NoLabel >
Definition triangle.hpp:2029
constexpr Convex()=default
Creates a convex with no vertex.
constexpr Point< ResultNumber, PointLabelType > center() const
Definition disk.hpp:284
constexpr ResultNumber radius() const
Definition disk.hpp:333
constexpr Disk()=default
Creates a disk with all three boundary points at the origin.
constexpr auto minkowskiErosion(const OtherShape &other) const
Returns the Minkowski erosion of this shape by another (A ⊖ B).
Definition minkowskierosion.hpp:635
The empty set of points in the plane.
Definition emptyshape.hpp:33
constexpr auto minkowskiErosion(const OtherShape &other) const
Returns the Minkowski erosion of this shape by another (A ⊖ B).
Definition minkowskierosion.hpp:655
friend struct HalfplaneIntersection
Definition halfplaneintersection.hpp:2308
Closed half-plane defined by an oriented boundary line.
Definition halfplane.hpp:51
constexpr const PointType & target() const
Returns the target boundary point.
Definition halfplane.hpp:193
constexpr const PointType & source() const
Returns the source boundary point.
Definition halfplane.hpp:181
constexpr auto minkowskiErosion(const OtherShape &other) const
Returns the Minkowski erosion of this shape by another (A ⊖ B).
Definition minkowskierosion.hpp:632
constexpr Halfplane()=default
Creates the degenerate half-plane (0,0)->(0,0).
constexpr Line()=default
Creates the degenerate line (0,0)--(0,0).
constexpr auto minkowskiErosion(const OtherShape &other) const
Returns the Minkowski erosion of this shape by another (A ⊖ B).
Definition minkowskierosion.hpp:662
constexpr OrientedLine()=default
Creates the degenerate oriented line (0,0)--(0,0).
constexpr OrientedSegment()=default
Creates the degenerate oriented segment (0,0)->(0,0).
Two-dimensional point with optional label payload.
Definition point.hpp:129
constexpr auto minkowskiErosion(const OtherShape &other) const
Returns the Minkowski erosion of this shape by another (A ⊖ B).
Definition minkowskierosion.hpp:648
TLabel LabelType
Definition point.hpp:133
TNumber NumberType
Definition point.hpp:131
friend struct PolygonSet
Definition polygonset.hpp:1873
friend struct PolygonWithHoles
Definition polygonwithholes.hpp:3314
constexpr Polygon()=default
Creates a polygon with no vertex.
constexpr Polyline()=default
Creates a polyline with no vertex.
constexpr Ray()=default
Creates the degenerate ray (0,0)--(0,0)->.
constexpr Rectangle()
Creates the empty rectangle [(0,0),(-1,-1)].
Definition rectangle.hpp:120
constexpr Segment()=default
Creates the degenerate segment (0,0)--(0,0).
constexpr auto minkowskiErosion(const OtherShape &other) const
Returns the Minkowski erosion of this shape by another (A ⊖ B).
Definition minkowskierosion.hpp:670
constexpr Triangle()=default
Creates the degenerate triangle (0,0),(0,0),(0,0).