68template <
class T>
struct minkowskiPointOf {
using type =
typename T::PointType; };
69template <
class Number,
class Label>
struct minkowskiPointOf<
Point<Number, Label>> {
using type = Point<Number, Label>; };
70template <
class Po
intType>
struct minkowskiPointOf<
Shape<PointType>> {
using type = PointType; };
71template <
class T>
using minkowskiPointOf_t =
typename minkowskiPointOf<std::remove_cvref_t<T>>::type;
80template <
class A,
class B>
81using minkowskiPoint_t =
Point<
82 std::common_type_t<typename minkowskiPointOf_t<A>::NumberType,
83 typename minkowskiPointOf_t<B>::NumberType>,
84 typename minkowskiPointOf_t<A>::LabelType>;
97template <
class A,
class B>
98using minkowskiRegionPoint_t =
Point<
99 std::conditional_t<is_halfplane_intersection_v<A> || is_halfplane_intersection_v<B>,
100 division_result_t<typename minkowskiPoint_t<A, B>::NumberType>,
104template <
class A,
class B>
105 requires MinkowskiSummableConcept<A, B>
106constexpr auto minkowskiSumOf(
const A& a,
const B& b);
115template <
class ShapeT,
class TranslationNumber,
class TranslationLabel>
116constexpr auto minkowskiTranslated(
const ShapeT& shape,
119 using ResultPoint = minkowskiPoint_t<ShapeT, Translation>;
120 using ResultNumber =
typename ResultPoint::NumberType;
123 const auto moved = [&translation](
const auto&
vertex) {
125 detail::asNumber<ResultNumber>(
vertex.x()) + detail::asNumber<ResultNumber>(translation.x()),
126 detail::asNumber<ResultNumber>(
vertex.y()) + detail::asNumber<ResultNumber>(translation.y()));
129 if constexpr (is_point_v<ShapeT>) {
131 }
else if constexpr (is_empty_shape_v<ShapeT>) {
133 return EmptyShape<ResultPoint>{};
134 }
else if constexpr (is_shape_v<ShapeT>) {
136 [&translation](
const auto& alternative) {
141 using Label =
typename ShapeT::LabelType;
143 if constexpr (is_segment_v<ShapeT>) {
145 }
else if constexpr (is_oriented_segment_v<ShapeT>) {
147 }
else if constexpr (is_line_v<ShapeT>) {
149 }
else if constexpr (is_oriented_line_v<ShapeT>) {
151 }
else if constexpr (is_ray_v<ShapeT>) {
153 }
else if constexpr (is_halfplane_v<ShapeT>) {
155 }
else if constexpr (is_rectangle_v<ShapeT>) {
159 }
else if constexpr (is_triangle_v<ShapeT>) {
161 }
else if constexpr (is_disk_v<ShapeT>) {
167 const auto translate = [&translation](
auto result) {
168 result += translation;
169 if constexpr (has_label_v<Label>) {
170 result.label() = Label{};
175 if constexpr (is_convex_v<ShapeT>) {
177 }
else if constexpr (is_polygon_v<ShapeT>) {
179 }
else if constexpr (is_monotone_chain_v<ShapeT>) {
181 }
else if constexpr (is_polyline_v<ShapeT>) {
183 }
else if constexpr (is_polygon_with_holes_v<ShapeT>) {
185 }
else if constexpr (is_polygon_set_v<ShapeT>) {
188 static_assert(is_halfplane_intersection_v<ShapeT>,
189 "minkowskiTranslated has no branch for this shape kind: every "
190 "shape is closed under translation, so a new one needs one here");
204template <
class ResultPo
int,
class ShapeT>
205constexpr std::vector<ResultPoint> minkowskiVertices(
const ShapeT& shape) {
206 using ResultNumber =
typename ResultPoint::NumberType;
208 std::vector<ResultPoint> vertices;
209 const auto append = [&vertices](
const auto&
vertex) {
210 vertices.emplace_back(detail::asNumber<ResultNumber>(
vertex.x()),
211 detail::asNumber<ResultNumber>(
vertex.y()));
214 if constexpr (is_convex_v<ShapeT>) {
217 vertices.reserve(shape.size());
218 for (
const auto&
vertex : shape) {
227 if (coversNoPoint(shape)) {
230 for (
const auto&
vertex : shape.vertices()) {
262template <
class HalfplaneT,
class ShapeT>
263constexpr auto minkowskiHalfplaneSum(
const HalfplaneT& halfplane,
const ShapeT& shape) {
264 using ResultPoint = minkowskiPoint_t<HalfplaneT, ShapeT>;
265 using ResultNumber =
typename ResultPoint::NumberType;
268 const auto& source = halfplane.source();
269 const auto& target = halfplane.target();
270 const ResultNumber dx = detail::asNumber<ResultNumber>(target.x()) - detail::asNumber<ResultNumber>(source.x());
271 const ResultNumber dy = detail::asNumber<ResultNumber>(target.y()) - detail::asNumber<ResultNumber>(source.y());
274 ResultPoint support(ResultNumber{}, ResultNumber{});
276 for (
const auto&
vertex : shape.vertices()) {
277 const ResultNumber
x =
static_cast<ResultNumber
>(
vertex.x());
278 const ResultNumber
y =
static_cast<ResultNumber
>(
vertex.y());
279 const ResultNumber side = dx *
y - dy *
x;
280 if (!found || side < best) {
283 support = ResultPoint(
x,
y);
287 return ResultHalfplane(ResultPoint(detail::asNumber<ResultNumber>(source.x()),
288 detail::asNumber<ResultNumber>(source.y())),
289 ResultPoint(detail::asNumber<ResultNumber>(target.x()),
290 detail::asNumber<ResultNumber>(target.y())));
292 const auto moved = [&support](
const auto& point) {
293 return ResultPoint(detail::asNumber<ResultNumber>(point.x()) + support.x(),
294 detail::asNumber<ResultNumber>(point.y()) + support.y());
296 return ResultHalfplane(moved(source), moved(target));
323template <
class ResultPo
int>
324struct MinkowskiPolyhedron {
326 std::vector<ResultPoint> directions;
328 std::vector<ResultPoint> anchors;
330 std::vector<ResultPoint> recessions;
336template <
class ResultPo
int>
337constexpr typename ResultPoint::NumberType minkowskiCross(
const ResultPoint& u,
338 const ResultPoint& v) {
339 return u.x() * v.y() - u.y() * v.x();
355template <
class ResultPo
int,
class ShapeT>
356constexpr MinkowskiPolyhedron<ResultPoint> minkowskiPolyhedronOf(
const ShapeT& shape) {
357 using ResultNumber =
typename ResultPoint::NumberType;
359 MinkowskiPolyhedron<ResultPoint> polyhedron;
360 const ResultNumber zero{};
361 const auto cast = [](
const auto& point) {
362 return ResultPoint(detail::asNumber<ResultNumber>(point.x()),
363 detail::asNumber<ResultNumber>(point.y()));
365 const auto reversed = [](
const ResultPoint& vector) {
366 return ResultPoint(-vector.x(), -vector.y());
369 if constexpr (is_point_v<ShapeT>) {
373 polyhedron.anchors.push_back(cast(shape));
374 }
else if constexpr (is_line_v<ShapeT> || is_oriented_line_v<ShapeT> || is_ray_v<ShapeT> ||
375 is_halfplane_v<ShapeT>) {
376 const ResultPoint source = cast(shape[0]);
377 const ResultPoint target = cast(shape[1]);
378 const ResultPoint forward(target.x() - source.x(), target.y() - source.y());
379 polyhedron.anchors.push_back(source);
380 if constexpr (is_halfplane_v<ShapeT>) {
385 polyhedron.directions.push_back(forward);
386 polyhedron.recessions.push_back(forward);
387 polyhedron.recessions.push_back(reversed(forward));
388 polyhedron.recessions.emplace_back(-forward.y(), forward.x());
389 }
else if constexpr (is_ray_v<ShapeT>) {
393 polyhedron.directions.push_back(forward);
394 polyhedron.directions.push_back(reversed(forward));
395 polyhedron.directions.emplace_back(forward.y(), -forward.x());
396 polyhedron.recessions.push_back(forward);
400 polyhedron.directions.push_back(forward);
401 polyhedron.directions.push_back(reversed(forward));
402 polyhedron.recessions.push_back(forward);
403 polyhedron.recessions.push_back(reversed(forward));
405 }
else if constexpr (is_halfplane_intersection_v<ShapeT>) {
407 polyhedron.empty =
true;
410 const std::size_t n = shape.size();
413 const ResultNumber one =
static_cast<ResultNumber
>(1);
414 polyhedron.anchors.emplace_back(zero, zero);
415 polyhedron.recessions.emplace_back(one, zero);
416 polyhedron.recessions.emplace_back(-one, zero);
417 polyhedron.recessions.emplace_back(zero, one);
418 polyhedron.recessions.emplace_back(zero, -one);
422 std::vector<ResultPoint> sources;
423 std::vector<ResultNumber> offsets;
426 polyhedron.directions.reserve(n);
427 for (
const auto& halfplane : shape) {
428 const ResultPoint source = cast(halfplane.source());
429 const ResultPoint target = cast(halfplane.target());
430 const ResultPoint direction(target.x() - source.x(), target.y() - source.y());
431 sources.push_back(source);
432 polyhedron.directions.push_back(direction);
433 offsets.push_back(minkowskiCross(direction, source));
438 const ResultPoint& forward = polyhedron.directions.front();
439 polyhedron.anchors.push_back(sources.front());
440 polyhedron.recessions.push_back(forward);
441 polyhedron.recessions.push_back(reversed(forward));
442 polyhedron.recessions.emplace_back(-forward.y(), forward.x());
450 for (std::size_t i = 0; i < n; ++i) {
451 const std::size_t next = (i + 1) % n;
452 const ResultPoint& here = polyhedron.directions[i];
453 const ResultPoint& there = polyhedron.directions[next];
454 const ResultNumber turn = minkowskiCross(here, there);
457 polyhedron.anchors.emplace_back(
458 (offsets[i] * there.x() - offsets[next] * here.x()) / turn,
459 (offsets[i] * there.y() - offsets[next] * here.y()) / turn);
461 polyhedron.recessions.push_back(here);
462 polyhedron.recessions.push_back(reversed(there));
465 if (polyhedron.anchors.empty()) {
468 polyhedron.anchors = std::move(sources);
473 std::vector<ResultPoint> vertices = minkowskiVertices<ResultPoint>(shape);
474 if (vertices.empty()) {
475 polyhedron.empty =
true;
478 if (vertices.size() >= 2) {
481 polyhedron.directions.reserve(vertices.size());
482 for (std::size_t i = 0; i < vertices.size(); ++i) {
483 const ResultPoint& from = vertices[i];
484 const ResultPoint& to = vertices[(i + 1) % vertices.size()];
485 polyhedron.directions.emplace_back(to.x() - from.x(), to.y() - from.y());
488 polyhedron.anchors = std::move(vertices);
501template <
class ResultPo
int>
502constexpr std::optional<ResultPoint> minkowskiInfimumPoint(
503 const MinkowskiPolyhedron<ResultPoint>& polyhedron,
const ResultPoint& direction) {
504 using ResultNumber =
typename ResultPoint::NumberType;
506 for (
const auto& recession : polyhedron.recessions) {
507 if (
crossSign(direction, recession) < 0) {
511 const ResultPoint* best =
nullptr;
512 ResultNumber least{};
513 for (
const auto& anchor : polyhedron.anchors) {
514 const ResultNumber value = minkowskiCross(direction, anchor);
515 if (best ==
nullptr || value < least) {
520 if (best ==
nullptr) {
547template <
class A,
class B>
548constexpr auto minkowskiPolyhedralSum(
const A& a,
const B& b) {
549 using ResultPoint = minkowskiRegionPoint_t<A, B>;
552 const MinkowskiPolyhedron<ResultPoint> left = minkowskiPolyhedronOf<ResultPoint>(a);
553 const MinkowskiPolyhedron<ResultPoint> right = minkowskiPolyhedronOf<ResultPoint>(b);
554 if (left.empty || right.empty) {
561 const auto clamp = [®ion, &left, &right](
const ResultPoint& direction) {
562 const std::optional<ResultPoint> here = minkowskiInfimumPoint(left, direction);
566 const std::optional<ResultPoint> there = minkowskiInfimumPoint(right, direction);
570 const ResultPoint base(here->x() + there->x(), here->y() + there->y());
572 base, ResultPoint(base.x() + direction.x(), base.y() + direction.y())));
574 for (
const auto& direction : left.directions) {
577 for (
const auto& direction : right.directions) {
594template <
class Po
intType>
595constexpr int minkowskiDirectionOrder(
const PointType& u,
const PointType& v) {
596 using Number =
typename PointType::NumberType;
598 const auto half = [](
const PointType& direction) {
599 const Number origin{};
600 return (direction.y() < origin || (direction.y() == origin && direction.x() < origin)) ? 1
604 const int halfU = half(u);
605 const int halfV = half(v);
606 if (halfU != halfV) {
607 return halfU < halfV ? -1 : 1;
629template <
class A,
class B>
630constexpr auto minkowskiConvexSum(
const A& a,
const B& b) {
631 using ResultPoint = minkowskiPoint_t<A, B>;
632 using ResultNumber =
typename ResultPoint::NumberType;
634 std::vector<ResultPoint> left = minkowskiVertices<ResultPoint>(a);
635 std::vector<ResultPoint> right = minkowskiVertices<ResultPoint>(b);
636 if (left.empty() || right.empty()) {
643 const auto rotateToLowest = [](std::vector<ResultPoint>& vertices) {
644 auto lowest = vertices.begin();
645 for (
auto it = vertices.begin() + 1; it != vertices.end(); ++it) {
646 if (it->y() < lowest->y() || (it->y() == lowest->y() && it->x() < lowest->x())) {
650 std::rotate(vertices.begin(), lowest, vertices.end());
652 rotateToLowest(left);
653 rotateToLowest(right);
657 const auto edgesOf = [](
const std::vector<ResultPoint>& vertices) {
658 std::vector<ResultPoint> edges;
659 if (vertices.size() >= 2) {
660 edges.reserve(vertices.size());
661 for (std::size_t i = 0; i < vertices.size(); ++i) {
662 const ResultPoint& from = vertices[i];
663 const ResultPoint& to = vertices[(i + 1) % vertices.size()];
664 edges.emplace_back(to.x() - from.x(), to.y() - from.y());
669 const std::vector<ResultPoint> leftEdges = edgesOf(left);
670 const std::vector<ResultPoint> rightEdges = edgesOf(right);
672 std::vector<ResultPoint> boundary;
673 boundary.reserve(leftEdges.size() + rightEdges.size() + 1);
674 ResultPoint current(left.front().x() + right.front().x(),
675 left.front().y() + right.front().y());
676 boundary.push_back(current);
680 while (i < leftEdges.size() || j < rightEdges.size()) {
681 ResultPoint step(ResultNumber{}, ResultNumber{});
682 if (j == rightEdges.size()) {
683 step = leftEdges[i++];
684 }
else if (i == leftEdges.size()) {
685 step = rightEdges[j++];
687 const int order = minkowskiDirectionOrder(leftEdges[i], rightEdges[j]);
689 step = leftEdges[i++];
690 }
else if (order > 0) {
691 step = rightEdges[j++];
695 step = ResultPoint(leftEdges[i].
x() + rightEdges[j].
x(),
696 leftEdges[i].
y() + rightEdges[j].
y());
701 current = ResultPoint(current.x() + step.x(), current.y() + step.y());
702 boundary.push_back(current);
704 if (boundary.size() > 1) {
710 std::rotate(boundary.begin(), std::min_element(boundary.begin(), boundary.end()),
721template <
class A,
class B>
722 requires MinkowskiSummableConcept<A, B>
723constexpr auto minkowskiSumOf(
const A& a,
const B& b) {
724 using ResultPoint = minkowskiPoint_t<A, B>;
726 if constexpr (is_shape_v<A> || is_shape_v<B>) {
733 const auto sum = [](
const auto& left,
const auto& right) -> ResultShape {
734 if constexpr (
requires { ResultShape(minkowskiSumOf(left, right)); }) {
735 return ResultShape(minkowskiSumOf(left, right));
737 throw std::logic_error(
738 "Shape::minkowskiSum is not defined for this pair of alternatives, or its "
739 "result does not fit the wrapper's point type");
742 if constexpr (is_shape_v<A> && is_shape_v<B>) {
743 return std::visit(sum, a.variant(), b.variant());
744 }
else if constexpr (is_shape_v<A>) {
745 return std::visit([&b, &sum](
const auto& left) {
return sum(left, b); }, a.variant());
747 return std::visit([&a, &sum](
const auto& right) {
return sum(a, right); }, b.variant());
749 }
else if constexpr (is_empty_shape_v<A> || is_empty_shape_v<B>) {
750 return EmptyShape<ResultPoint>{};
751 }
else if constexpr (is_point_v<B>) {
752 return minkowskiTranslated(a, b);
753 }
else if constexpr (is_point_v<A>) {
754 return minkowskiTranslated(b, a);
755 }
else if constexpr (is_rectangle_v<A> && is_rectangle_v<B>) {
759 if (a.empty() || b.empty()) {
763 minkowskiSumOf(a.max(), b.max()),
true);
764 }
else if constexpr (is_halfplane_v<A> && !UnboundedConvexConcept<B>) {
766 return minkowskiHalfplaneSum(a, b);
767 }
else if constexpr (is_halfplane_v<B> && !UnboundedConvexConcept<A>) {
768 return minkowskiHalfplaneSum(b, a);
769 }
else if constexpr (UnboundedConvexConcept<A> || UnboundedConvexConcept<B>) {
772 return minkowskiPolyhedralSum(a, b);
774 return minkowskiConvexSum(a, b);
786#define PGL_DEFINE_MINKOWSKI_SUM(SHAPE) \
787 template <class PointType, class LabelType> \
788 template <class OtherShape> \
789 requires MinkowskiSummableConcept<SHAPE<PointType, LabelType>, OtherShape> \
790 constexpr auto SHAPE<PointType, LabelType>::minkowskiSum(const OtherShape& other) const { \
791 return detail::minkowskiSumOf(*this, other); \
810#undef PGL_DEFINE_MINKOWSKI_SUM
816template <
class Po
intType_,
class TLabel>
817template <
class ResultNumber, DiskConcept OtherDisk>
824 const ResultNumber radii =
827 leftCenter.y() + rightCenter.y()),
834template <
class Po
intType_,
class TLabel>
835template <
class ResultNumber, DiskConcept OtherDisk>
840 const ResultNumber dx = detail::asNumber<ResultNumber>(
target().
x()) - detail::asNumber<ResultNumber>(
source().
x());
841 const ResultNumber dy = detail::asNumber<ResultNumber>(
target().
y()) - detail::asNumber<ResultNumber>(
source().
y());
844 if constexpr (!
requires(ResultNumber v) { std::sqrt(v); }) {
845 throw std::runtime_error(
"std::sqrt is not available for the requested ResultNumber type");
847 const ResultNumber length = std::sqrt(dx * dx + dy * dy);
851 const auto center = other.template center<ResultNumber>();
852 const ResultNumber radius = other.template radius<ResultNumber>();
853 const ResultNumber offsetX = center.x() + radius * dy / length;
854 const ResultNumber offsetY = center.y() - radius * dx / length;
856 const auto moved = [&offsetX, &offsetY](
const auto& point) {
857 return ResultPoint(detail::asNumber<ResultNumber>(point.x()) + offsetX,
858 detail::asNumber<ResultNumber>(point.y()) + offsetY);
864template <
class Po
intType_,
class TLabel>
865template <
class ResultNumber, HalfplaneConcept OtherHalfplane>
871template <
class Number,
class Label>
872template <
class OtherShape>
875 return detail::minkowskiSumOf(*
this, other);
878template <
class Po
intType>
879template <
class OtherShape>
882 return detail::minkowskiSumOf(*
this, other);
885template <
class Po
intType,
class LabelType,
class Storage>
886template <
class OtherShape>
889 return detail::minkowskiSumOf(*
this, other);
892template <
class Po
intType>
893template <
class OtherShape>
896 return detail::minkowskiSumOf(*
this, other);
907template <
class A,
class B>
909[[nodiscard]]
constexpr auto operator+(
const A& a,
const B& b) {
910 return a.minkowskiSum(b);
Shape pairs whose Minkowski sum Pangolin can represent.
Definition forward.hpp:476
#define PGL_DEFINE_MINKOWSKI_SUM(SHAPE)
Definition minkowski.hpp:786
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
Line() -> Line< Point<>, NoLabel >
auto grahamScan(const Container &points_)
Computes the convex hull of a point container using Graham's scan.
Definition convexhull.hpp:177
PolygonSet() -> PolygonSet< Point<>, NoLabel >
Definition polygonset.hpp:1699
OrientedSegment() -> OrientedSegment< Point<>, NoLabel >
constexpr std::partial_ordering crossSign(const Point< UNumber, ULabel > &u, const Point< VNumber, VLabel > &v)
Classifies the turn from one vector to another.
Definition orientation.hpp:583
MonotoneChain() -> MonotoneChain< Point<>, NoLabel >
Definition monotonechain.hpp:2439
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 >
BitMatrix< PointType > operator+(const PointType &vector, const BitMatrix< PointType > &matrix)
Returns the same cells translated by a vector.
Definition bitmatrix.hpp:2660
Polyline() -> Polyline< Point<>, NoLabel >
Definition polyline.hpp:2369
Ray() -> Ray< 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
OrientedLine() -> OrientedLine< Point<>, NoLabel >
Triangle() -> Triangle< Point<>, NoLabel >
Definition triangle.hpp:2029
constexpr Convex()=default
Creates a convex with no vertex.
Closed Euclidean disk stored by boundary points plus optional disk label.
Definition disk.hpp:66
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 minkowskiSum(const OtherShape &other) const
Returns the Minkowski sum of this shape and another (A ⊕ B).
Definition minkowski.hpp:802
constexpr auto minkowskiSum(const OtherShape &other) const
Returns the Minkowski sum of this shape and another (A ⊕ B).
Definition minkowski.hpp:881
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 auto minkowskiSum(const OtherShape &other) const
Returns the Minkowski sum of this shape and another (A ⊕ B).
Definition minkowski.hpp:799
constexpr const PointType & source() const
Returns the source boundary point.
Definition halfplane.hpp:181
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 minkowskiSum(const OtherShape &other) const
Returns the Minkowski sum of this shape and another (A ⊕ B).
Definition minkowski.hpp:888
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 minkowskiSum(const OtherShape &other) const
Returns the Minkowski sum of this shape and another (A ⊕ B).
Definition minkowski.hpp:874
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 minkowskiSum(const OtherShape &other) const
Returns the Minkowski sum of this shape and another (A ⊕ B).
Definition minkowski.hpp:895
constexpr Triangle()=default
Creates the degenerate triangle (0,0),(0,0),(0,0).