121#include <type_traits>
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>,
152template <
class A,
class B>
163template <
class ResultPo
int>
164struct MinkowskiErosionConstraint {
166 ResultPoint direction;
192template <
class ResultPo
int,
class ShapeT>
193constexpr std::optional<std::vector<MinkowskiErosionConstraint<ResultPoint>>>
194minkowskiErosionConstraints(
const ShapeT& shape) {
195 using ResultNumber =
typename ResultPoint::NumberType;
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()));
202 const auto add = [&constraints](
const ResultPoint& direction,
const ResultPoint& anchor) {
203 constraints.push_back(MinkowskiErosionConstraint<ResultPoint>{direction, anchor});
205 const auto reversed = [](
const ResultPoint& vector) {
206 return ResultPoint(-vector.x(), -vector.y());
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);
220 if constexpr (is_point_v<ShapeT>) {
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>) {
231 add(reversed(forward), source);
232 if constexpr (is_ray_v<ShapeT>) {
236 add(ResultPoint(forward.y(), -forward.x()), source);
239 }
else if constexpr (is_halfplane_intersection_v<ShapeT>) {
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);
253 const std::vector<ResultPoint> vertices = minkowskiVertices<ResultPoint>(shape);
254 if (vertices.empty()) {
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());
264 add(reversed(along), from);
265 add(ResultPoint(along.y(), -along.x()), from);
266 add(ResultPoint(-along.y(), along.x()), to);
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);
304template <
class A,
class B>
305constexpr auto minkowskiConvexErosion(
const A& a,
const B& b) {
306 using ResultPoint = minkowskiErosionPoint_t<A, B>;
309 const MinkowskiPolyhedron<ResultPoint> eroder = minkowskiPolyhedronOf<ResultPoint>(b);
313 const auto constraints = minkowskiErosionConstraints<ResultPoint>(a);
319 for (
const auto& constraint : *constraints) {
320 const std::optional<ResultPoint> support =
321 minkowskiInfimumPoint(eroder, constraint.direction);
325 const ResultPoint base(constraint.anchor.x() - support->x(),
326 constraint.anchor.y() - support->y());
328 base, ResultPoint(base.x() + constraint.direction.x(),
329 base.y() + constraint.direction.y())));
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;
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());
362 ResultPoint support(ResultNumber{}, ResultNumber{});
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;
368 if (!found || side < best) {
371 support = ResultPoint(
x,
y);
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");
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());
383 return ResultHalfplane(moved(source), moved(target));
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>;
402 if constexpr (is_shape_v<A> || is_shape_v<B>) {
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));
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");
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); },
422 return std::visit([&a, &erode](
const auto& right) {
return erode(a, right); },
425 }
else if constexpr (is_empty_shape_v<B>) {
429 return minkowskiErosionRegion_t<A, B>();
430 }
else if constexpr (is_point_v<B>) {
435 return minkowskiTranslated(a, -b);
436 }
else if constexpr (is_empty_shape_v<A>) {
437 using Region = minkowskiErosionRegion_t<A, B>;
439 return coversNoPoint(b) ? Region() : Region(
Convex<minkowskiErosionPoint_t<A, B>>());
440 }
else if constexpr (is_disk_v<B>) {
446 using Region = minkowskiErosionRegion_t<A, B>;
448 return Region(
Convex<minkowskiErosionPoint_t<A, B>>());
450 return Region(minkowskiConvexErosion(a, b.a()));
451 }
else if constexpr (is_rectangle_v<A> && is_rectangle_v<B>) {
458 throw std::logic_error(
459 "Rectangle::minkowskiErosion by an empty rectangle is the whole plane, which no "
460 "rectangle represents");
463 return ResultRectangle();
465 const auto corner = [](
const auto& left,
const auto& right) {
466 using ResultNumber =
typename ResultPoint::NumberType;
468 detail::asNumber<ResultNumber>(left.x()) - detail::asNumber<ResultNumber>(right.x()),
469 detail::asNumber<ResultNumber>(left.y()) -
470 detail::asNumber<ResultNumber>(right.y()));
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();
477 return ResultRectangle(low, high,
true);
478 }
else if constexpr (is_halfplane_v<A> && !UnboundedConvexConcept<B>) {
480 return minkowskiHalfplaneErosion(a, b);
488 return minkowskiConvexErosion(a, b);
544template <
class ResultPo
int,
class ShapeA,
class ShapeB>
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;
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");
557 if (!minkowskiHasArea(a)) {
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()) {
580 const auto operandVertices = b.vertices();
581 const auto anchor = *operandVertices.begin();
582 const auto centered = minkowskiTranslated(b, -anchor);
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);
601 regularizedDifference<ExactPoint>(window.asPolygon(), booleanOperand(a));
603 setMinkowskiSum<ExactPoint>(outside, centered.rotated90(2));
605 regularizedDifference<ResultPoint>(booleanOperand(a), forbidden);
606 erosion -= ResultPoint(asNumber<ResultNumber>(anchor.x()), asNumber<ResultNumber>(anchor.y()));
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) \
624 return detail::minkowskiErosionOf(*this, other); \
643#undef PGL_DEFINE_MINKOWSKI_EROSION
645template <
class Number,
class Label>
646template <
class OtherShape>
649 return detail::minkowskiErosionOf(*
this, other);
652template <
class Po
intType>
653template <
class OtherShape>
656 return detail::minkowskiErosionOf(*
this, other);
659template <
class Po
intType,
class LabelType,
class Storage>
660template <
class OtherShape>
663 const OtherShape& other)
const {
664 return detail::minkowskiErosionOf(*
this, other);
667template <
class Po
intType>
668template <
class OtherShape>
671 return detail::minkowskiErosionOf(*
this, other);
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) \
691 return detail::minkowskiConvexErosion(*this, other); \
700#undef PGL_DEFINE_CONVEX_MINKOWSKI_EROSION
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); \
726#undef PGL_DEFINE_REGION_MINKOWSKI_EROSION
734template <
class Po
intType_,
class TLabel,
class Storage>
735template <
class ResultNumber,
class OtherShape>
740 return detail::regularizedMinkowskiErosion<Point<ResultNumber, typename PointType_::LabelType>>(
752template <
class Po
intType_,
class TLabel>
753template <
class ResultNumber, DiskConcept OtherDisk>
754std::optional<Disk<Point<ResultNumber, typename Disk<PointType_, TLabel>::PointLabelType>>>
759 if (radii < ResultNumber{}) {
765 leftCenter.y() - rightCenter.y()),
769template <
class Po
intType_,
class TLabel>
770template <
class ResultNumber, DiskConcept OtherDisk>
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());
781 if constexpr (!
requires(ResultNumber v) { std::sqrt(v); }) {
782 throw std::runtime_error(
"std::sqrt is not available for the requested ResultNumber type");
784 const ResultNumber length = std::sqrt(dx * dx + dy * dy);
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;
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);
802template <
class Po
intType_,
class TLabel>
803template <
class ResultNumber, HalfplaneConcept OtherHalfplane>
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
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).