140#include <type_traits>
180template <
class Shape>
181std::vector<Convex<typename Shape::PointType>> minkowskiConvexPieces(
const Shape& shape) {
182 using ShapePoint =
typename Shape::PointType;
185 std::vector<PieceConvex> pieces;
189 const auto add = [&pieces](std::vector<ShapePoint> vertices) {
190 pieces.push_back(PieceConvex(std::move(vertices)));
193 if constexpr (is_polygon_v<Shape> || is_polygon_with_holes_v<Shape>) {
194 const auto addEdge = [&add](
const auto&
edge) { add({
edge.min(),
edge.max()}); };
195 if (shape.isDegenerate()) {
196 if constexpr (is_polygon_with_holes_v<Shape>) {
197 for (
const auto&
edge : shape.edges()) {
201 for (
const auto&
edge : shape.edgesView()) {
208 pieces = shape.convexPartition();
209 if constexpr (is_polygon_with_holes_v<Shape>) {
210 for (
const auto& slit : regionSlits(shape)) {
214 }
else if constexpr (is_polyline_v<Shape> || is_monotone_chain_v<Shape>) {
220 if (shape.size() == 1) {
223 for (
const auto&
edge : shape.edgesView()) {
227 std::vector<ShapePoint> vertices;
228 for (
const auto&
vertex : shape.vertices()) {
229 vertices.emplace_back(
vertex);
231 add(std::move(vertices));
259template <
class Shape,
class OtherShape>
260decltype(
auto) holeFilteredFor(
const Shape& shape,
const OtherShape& other) {
261 if constexpr (is_polygon_with_holes_v<Shape>) {
262 if (shape.holes().empty()) {
266 std::common_type_t<typename Shape::NumberType, typename OtherShape::NumberType>;
267 const auto box = other.bbox();
268 const Extent width = Extent(box.max().x()) - Extent(box.min().x());
269 const Extent height = Extent(box.max().y()) - Extent(box.min().y());
271 std::vector<typename Shape::PolygonType> kept;
272 for (
const auto& hole : shape.holes()) {
273 const auto holeBox = hole.bbox();
274 if (Extent(holeBox.max().x()) - Extent(holeBox.min().x()) >= width &&
275 Extent(holeBox.max().y()) - Extent(holeBox.min().y()) >= height) {
276 kept.push_back(hole);
279 return Shape(shape.outer(), std::move(kept),
true);
301template <
class ResultPo
int,
class ShapeA,
class ShapeB>
304 const auto left = minkowskiConvexPieces(a);
305 const auto right = minkowskiConvexPieces(b);
306 using SumConvex =
decltype(minkowskiConvexSum(left.front(), right.front()));
308 std::vector<SumConvex> sums;
309 sums.reserve(left.size() * right.size());
310 for (
const auto& piece : left) {
311 for (
const auto& other : right) {
312 SumConvex sum = minkowskiConvexSum(piece, other);
313 if (!sum.isDegenerate()) {
314 sums.push_back(std::move(sum));
322 std::sort(sums.begin(), sums.end());
323 sums.erase(std::unique(sums.begin(), sums.end()), sums.end());
344template <
class Shape>
345bool minkowskiIsConvex(
const Shape& shape) {
346 if constexpr (is_polygon_v<Shape>) {
347 return shape.isConvex();
348 }
else if constexpr (is_polygon_with_holes_v<Shape>) {
349 return shape.holes().empty() && shape.outer().isConvex();
350 }
else if constexpr (is_polyline_v<Shape> || is_monotone_chain_v<Shape>) {
364template <
class Shape>
365bool minkowskiHasArea(
const Shape& shape) {
366 if constexpr (is_segment_v<Shape> || is_oriented_segment_v<Shape> ||
367 is_polyline_v<Shape> || is_monotone_chain_v<Shape>) {
370 return !shape.isDegenerate();
383template <
class Shape>
384auto minkowskiAsConvex(
const Shape& shape) {
385 using ShapePoint =
typename Shape::PointType;
386 if constexpr (is_convex_v<Shape>) {
388 }
else if constexpr (is_polygon_v<Shape>) {
390 }
else if constexpr (is_polygon_with_holes_v<Shape>) {
393 std::vector<ShapePoint> vertices;
394 for (
const auto&
vertex : shape.vertices()) {
395 vertices.emplace_back(
vertex);
415template <
class Number>
416using chainWide_t = promoted_number_t<promoted_number_t<Number>>;
419template <
class Number>
420constexpr int chainSignOf(
const Number& value) {
422 return value > zero ? 1 : (value < zero ? -1 : 0);
431template <
class P,
class Q>
432constexpr int chainSideSign(
const P& a,
const P& b,
const Q& p) {
452struct ChainEnvelopeVertex {
456 bool isCrossing =
false;
461constexpr ChainEnvelopeVertex<P> chainVertexAt(
const P& point) {
462 return ChainEnvelopeVertex<P>{point, P(), P(), P(), P(),
false};
467constexpr ChainEnvelopeVertex<P> chainVertexCrossing(
const P& a,
const P& b,
const P& c,
469 return ChainEnvelopeVertex<P>{P(), a, b, c, d,
true};
473template <
class P,
class Number>
474constexpr ChainEnvelopeVertex<P> chainVertexOnVertical(
const P& a,
const P& b,
const Number&
x) {
475 using Coordinate =
typename P::NumberType;
476 const Coordinate cut =
static_cast<Coordinate
>(
x);
477 return chainVertexCrossing(a, b, P(cut, Coordinate{}), P(cut, Coordinate(1)));
494constexpr int chainVertexSide(
const ChainEnvelopeVertex<P>&
vertex,
const P& u1,
const P& u2) {
496 return chainSideSign(u1, u2,
vertex.point);
498 using Wide = chainWide_t<typename P::NumberType>;
499 const auto wide = [](
const auto& value) ->
decltype(
auto) {
500 return detail::asNumber<Wide>(value);
503 const Wide rx = wide(
vertex.b.x()) - wide(
vertex.a.x());
504 const Wide ry = wide(
vertex.b.y()) - wide(
vertex.a.y());
505 const Wide sx = wide(
vertex.d.x()) - wide(
vertex.c.x());
506 const Wide sy = wide(
vertex.d.y()) - wide(
vertex.c.y());
507 const Wide den = rx * sy - ry * sx;
508 assert(den != Wide{} &&
"an envelope only ever names a crossing of two crossing lines");
512 const Wide ux = wide(u2.x()) - wide(u1.x());
513 const Wide uy = wide(u2.y()) - wide(u1.y());
515 ux * (wide(
vertex.a.y()) - wide(u1.y())) - uy * (wide(
vertex.a.x()) - wide(u1.x()));
516 const Wide slope = ux * ry - uy * rx;
518 return chainSignOf(base * den + slope * num) * chainSignOf(den);
528template <
class P,
class Number>
529constexpr int chainVertexXSign(
const ChainEnvelopeVertex<P>&
vertex,
const Number&
x) {
531 return chainSignOf(
vertex.point.x() -
x);
533 using Wide = chainWide_t<typename P::NumberType>;
534 const auto wide = [](
const auto& value) ->
decltype(
auto) {
535 return detail::asNumber<Wide>(value);
538 const Wide rx = wide(
vertex.b.x()) - wide(
vertex.a.x());
539 const Wide ry = wide(
vertex.b.y()) - wide(
vertex.a.y());
540 const Wide sx = wide(
vertex.d.x()) - wide(
vertex.c.x());
541 const Wide sy = wide(
vertex.d.y()) - wide(
vertex.c.y());
542 const Wide den = rx * sy - ry * sx;
543 assert(den != Wide{} &&
"an envelope only ever names a crossing of two crossing lines");
547 return chainSignOf((wide(
vertex.a.x()) - wide(
x)) * den + rx * num) * chainSignOf(den);
559template <
class ResultPo
int,
class P>
560ResultPoint chainVertexPoint(
const ChainEnvelopeVertex<P>&
vertex) {
561 using ResultNumber =
typename ResultPoint::NumberType;
563 return ResultPoint(detail::asNumber<ResultNumber>(
vertex.point.x()),
564 detail::asNumber<ResultNumber>(
vertex.point.y()));
566 using Wide = chainWide_t<typename P::NumberType>;
567 const auto wide = [](
const auto& value) ->
decltype(
auto) {
568 return detail::asNumber<Wide>(value);
571 const Wide rx = wide(
vertex.b.x()) - wide(
vertex.a.x());
572 const Wide ry = wide(
vertex.b.y()) - wide(
vertex.a.y());
573 const Wide sx = wide(
vertex.d.x()) - wide(
vertex.c.x());
574 const Wide sy = wide(
vertex.d.y()) - wide(
vertex.c.y());
575 const Wide den = rx * sy - ry * sx;
576 assert(den != Wide{} &&
"an envelope only ever names a crossing of two crossing lines");
580 const Wide xn = wide(
vertex.a.x()) * den + rx * num;
581 const Wide yn = wide(
vertex.a.y()) * den + ry * num;
583 if constexpr (extended_integral<Wide>) {
586 return ResultPoint(
static_cast<ResultNumber
>(
Rational<Wide>(xn, den)),
591 return ResultPoint(
static_cast<ResultNumber
>(xn / den),
592 static_cast<ResultNumber
>(yn / den));
610struct ChainEnvelopeArc {
613 ChainEnvelopeVertex<P> start;
614 ChainEnvelopeVertex<P> step;
615 typename P::NumberType stepAt{};
616 bool hasStep =
false;
621ChainEnvelopeArc<P> chainArcEnteredAt(
const P& a,
const P& b,
622 const ChainEnvelopeVertex<P>& start) {
623 ChainEnvelopeArc<P> arc;
632ChainEnvelopeArc<P> chainArcSteppedInto(
const P& a,
const P& b,
633 const ChainEnvelopeVertex<P>& start,
634 const ChainEnvelopeVertex<P>& step,
635 const typename P::NumberType&
x) {
636 ChainEnvelopeArc<P> arc = chainArcEnteredAt(a, b, start);
668void chainMergeArc(std::vector<ChainEnvelopeArc<P>>& envelope,
const std::vector<P>& arc,
670 assert(arc.size() >= 2 &&
"a piece with no span cannot bound an envelope");
671 const int keep = keepHigher ? 1 : -1;
673 const auto plain = [&arc](std::size_t index) {
674 return chainArcEnteredAt(arc[index], arc[index + 1], chainVertexAt(arc[index]));
677 if (envelope.empty()) {
678 for (std::size_t index = 0; index + 1 < arc.size(); ++index) {
679 envelope.push_back(plain(index));
686 const auto entry = [&envelope](std::size_t index) ->
const ChainEnvelopeVertex<P>& {
687 return envelope[index].hasStep ? envelope[index].step : envelope[index].start;
690 const auto& x0 = arc.front().x();
691 std::size_t i = envelope.size() - 1;
692 while (i > 0 && chainVertexXSign(entry(i), x0) > 0) {
698 std::size_t kept = i + 1;
699 std::vector<ChainEnvelopeArc<P>> merged;
704 const int startSide = -chainSideSign(envelope[i].a, envelope[i].b, arc.front());
705 int winner = keep * startSide >= 0 ? 1 : -1;
712 ChainEnvelopeVertex<P> from;
713 if (chainVertexXSign(entry(i), x0) == 0) {
717 from = chainVertexOnVertical(envelope[i].a, envelope[i].b, x0);
719 merged.push_back(chainArcSteppedInto(arc[0], arc[1], chainVertexAt(arc[0]), from, x0));
723 const bool envelopeEndsLast = i + 1 == envelope.size();
726 const int order = envelopeEndsLast
727 ? chainSignOf(envelope[i].b.x() - arc[j + 1].x())
728 : chainVertexXSign(entry(i + 1), arc[j + 1].
x());
734 side = envelopeEndsLast ? chainSideSign(arc[j], arc[j + 1], envelope[i].b)
735 : chainVertexSide(entry(i + 1), arc[j], arc[j + 1]);
737 side = -chainSideSign(envelope[i].a, envelope[i].b, arc[j + 1]);
739 const int ahead = keep * side;
740 if (ahead != 0 && ahead != winner) {
743 const ChainEnvelopeVertex<P> crossing =
744 chainVertexCrossing(envelope[i].a, envelope[i].b, arc[j], arc[j + 1]);
745 merged.push_back(ahead > 0
746 ? chainArcEnteredAt(envelope[i].a, envelope[i].b, crossing)
747 : chainArcEnteredAt(arc[j], arc[j + 1], crossing));
755 if (j + 1 >= arc.size()) {
759 merged.push_back(plain(j));
763 if (envelopeEndsLast) {
767 const auto& stepAt = envelope[i].b.x();
770 ? chainArcEnteredAt(arc[j], arc[j + 1], chainVertexAt(envelope[i].b))
771 : chainArcSteppedInto(
773 chainVertexOnVertical(arc[j], arc[j + 1], stepAt),
774 chainVertexAt(envelope[i].b), stepAt));
776 for (std::size_t rest = j + 1; rest + 1 < arc.size(); ++rest) {
777 merged.push_back(plain(rest));
782 if (!envelope[i].hasStep) {
784 merged.push_back(envelope[i]);
792 const int afterSide = chainVertexSide(envelope[i].start, arc[j], arc[j + 1]);
793 const int after = afterSide == 0 ? winner : keep * afterSide;
794 const auto& stepAt = envelope[i].stepAt;
797 winner > 0 ? envelope[i]
798 : chainArcSteppedInto(
799 envelope[i].a, envelope[i].b, envelope[i].start,
800 chainVertexOnVertical(arc[j], arc[j + 1], stepAt),
802 }
else if (winner > 0) {
803 merged.push_back(chainArcSteppedInto(
804 arc[j], arc[j + 1], chainVertexOnVertical(arc[j], arc[j + 1], stepAt),
805 envelope[i].step, stepAt));
812 envelope.resize(kept);
813 envelope.insert(envelope.end(), std::make_move_iterator(merged.begin()),
814 std::make_move_iterator(merged.end()));
829template <
class P,
class L>
830std::vector<P> chainPieceArc(
const Convex<P, L>& piece,
bool upper) {
831 const std::size_t n = piece.size();
832 const std::size_t top = piece.maxIndex();
833 assert(n >= 2 &&
"a piece with no span has no arc; an operand of no width is handled apart");
839 arc.reserve(n - top + 1);
840 arc.push_back(piece[0]);
841 for (std::size_t k = n; k > top; --k) {
842 arc.push_back(piece[k - 1]);
844 std::size_t drop = 0;
845 while (drop + 1 < arc.size() && arc[drop].x() == arc[drop + 1].x()) {
848 arc.erase(arc.begin(), arc.begin() +
static_cast<std::ptrdiff_t
>(drop));
850 arc.reserve(top + 1);
851 for (std::size_t k = 0; k <= top; ++k) {
852 arc.push_back(piece[k]);
854 while (arc.size() >= 2 && arc[arc.size() - 2].x() == arc.back().x()) {
862template <
class ResultPo
int,
class P>
863void chainAppendEnvelope(std::vector<ResultPoint>& walk,
864 const std::vector<ChainEnvelopeArc<P>>& envelope) {
865 using ResultNumber =
typename ResultPoint::NumberType;
866 for (
const ChainEnvelopeArc<P>& arc : envelope) {
868 walk.push_back(chainVertexPoint<ResultPoint>(arc.step));
870 walk.push_back(chainVertexPoint<ResultPoint>(arc.start));
872 walk.emplace_back(detail::asNumber<ResultNumber>(envelope.back().b.x()),
873 detail::asNumber<ResultNumber>(envelope.back().b.y()));
877template <
class ResultPo
int>
878void chainDropRepeated(std::vector<ResultPoint>& walk) {
879 walk.erase(std::unique(walk.begin(), walk.end()), walk.end());
880 while (walk.size() >= 2 && walk.front() == walk.back()) {
893template <
class ResultPo
int>
894void chainDropCollinear(std::vector<ResultPoint>& walk) {
895 if (walk.size() < 3) {
898 std::vector<ResultPoint> kept;
899 kept.reserve(walk.size());
900 for (std::size_t index = 0; index < walk.size(); ++index) {
901 const ResultPoint& previous = walk[(index + walk.size() - 1) % walk.size()];
902 const ResultPoint&
vertex = walk[index];
903 const ResultPoint& next = walk[(index + 1) % walk.size()];
934template <
class ResultPo
int,
class ChainType,
class ConvexOperand>
935std::vector<ResultPoint> chainSumWalk(
const ChainType& chain,
const ConvexOperand& other) {
936 using SumPoint = minkowskiPoint_t<ChainType, ConvexOperand>;
937 using SumNumber =
typename SumPoint::NumberType;
938 using ResultNumber =
typename ResultPoint::NumberType;
940 const auto convert = [](
const auto&
vertex) {
941 return ResultPoint(detail::asNumber<ResultNumber>(
vertex.x()),
942 detail::asNumber<ResultNumber>(
vertex.y()));
944 const auto shifted = [](
const auto& p,
const auto& q) {
945 return SumPoint(detail::asNumber<SumNumber>(p.x()) + detail::asNumber<SumNumber>(q.x()),
946 detail::asNumber<SumNumber>(p.y()) + detail::asNumber<SumNumber>(q.y()));
949 const std::vector<SumPoint> operandVertices = minkowskiVertices<SumPoint>(other);
950 if (chain.empty() || operandVertices.empty()) {
954 const auto byX = [](
const SumPoint& p,
const SumPoint& q) {
return p.x() < q.x(); };
955 const auto [leftmost, rightmost] =
956 std::minmax_element(operandVertices.begin(), operandVertices.end(), byX);
957 if (leftmost->x() == rightmost->x()) {
962 const auto byY = [](
const SumPoint& p,
const SumPoint& q) {
return p.y() < q.y(); };
963 const auto [lowest, highest] =
964 std::minmax_element(operandVertices.begin(), operandVertices.end(), byY);
965 std::vector<ResultPoint> swept;
966 swept.reserve(2 * chain.size());
967 for (std::size_t index = 0; index < chain.size(); ++index) {
968 swept.push_back(convert(shifted(chain[index], *lowest)));
970 for (std::size_t index = chain.size(); index > 0; --index) {
971 swept.push_back(convert(shifted(chain[index - 1], *highest)));
973 chainDropRepeated(swept);
974 chainDropCollinear(swept);
978 std::vector<ChainEnvelopeArc<SumPoint>> lower;
979 std::vector<ChainEnvelopeArc<SumPoint>> upper;
980 const auto mergePiece = [&lower, &upper](
const auto& piece) {
981 chainMergeArc(lower, chainPieceArc(piece,
false),
false);
982 chainMergeArc(upper, chainPieceArc(piece,
true),
true);
985 if (chain.size() == 1) {
987 std::vector<SumPoint> translated;
988 translated.reserve(operandVertices.size());
989 for (
const SumPoint&
vertex : operandVertices) {
990 translated.push_back(shifted(
vertex, chain[0]));
994 for (
const auto&
edge : chain.edgesView()) {
995 mergePiece(minkowskiConvexSum(
edge, other));
999 std::vector<ResultPoint> walk;
1000 chainAppendEnvelope(walk, lower);
1001 const std::size_t fromRight = walk.size();
1002 chainAppendEnvelope(walk, upper);
1004 std::reverse(walk.begin() +
static_cast<std::ptrdiff_t
>(fromRight), walk.end());
1005 chainDropRepeated(walk);
1006 chainDropCollinear(walk);
1023template <
class ResultPo
int,
class ChainType,
class ConvexOperand>
1025 std::vector<ResultPoint> walk = chainSumWalk<ResultPoint>(chain, other);
1033 std::rotate(walk.begin(), std::min_element(walk.begin(), walk.end()), walk.end());
1055std::vector<std::vector<P>> minkowskiMonotoneRuns(
const std::vector<P>& walk) {
1056 std::vector<std::vector<P>> runs;
1057 const auto direction = [](
const P& from,
const P& to) {
1058 return from == to ? 0 : (from < to ? 1 : -1);
1061 std::size_t start = 0;
1062 while (start + 1 < walk.size()) {
1063 const int forward = direction(walk[start], walk[start + 1]);
1068 std::size_t end = start + 1;
1069 while (end + 1 < walk.size() && direction(walk[end], walk[end + 1]) == forward) {
1072 std::vector<P> run(walk.begin() +
static_cast<std::ptrdiff_t
>(start),
1073 walk.begin() +
static_cast<std::ptrdiff_t
>(end) + 1);
1075 std::reverse(run.begin(), run.end());
1077 runs.push_back(std::move(run));
1095template <
class Shape>
1096std::vector<std::vector<typename Shape::PointType>> minkowskiBoundaryRuns(
const Shape& shape) {
1097 using ShapePoint =
typename Shape::PointType;
1099 std::vector<std::vector<ShapePoint>> walks;
1100 const auto addRing = [&walks](
const auto& ring) {
1101 std::vector<ShapePoint> walk = ring.vertices();
1102 if (!walk.empty()) {
1103 walk.push_back(walk.front());
1105 walks.push_back(std::move(walk));
1107 if constexpr (is_polygon_with_holes_v<Shape>) {
1108 addRing(shape.outer());
1109 for (
const auto& hole : shape.holes()) {
1112 }
else if constexpr (is_polygon_v<Shape>) {
1116 std::vector<ShapePoint> walk;
1117 walk.reserve(shape.size());
1118 for (std::size_t index = 0; index < shape.size(); ++index) {
1119 walk.push_back(shape[index]);
1121 walks.push_back(std::move(walk));
1124 std::vector<std::vector<ShapePoint>> runs;
1125 for (
const std::vector<ShapePoint>& walk : walks) {
1126 std::vector<std::vector<ShapePoint>> walkRuns = minkowskiMonotoneRuns(walk);
1127 if (walkRuns.empty() && !walk.empty()) {
1131 walkRuns.push_back({walk.front()});
1133 runs.insert(runs.end(), std::make_move_iterator(walkRuns.begin()),
1134 std::make_move_iterator(walkRuns.end()));
1170template <
class ExactPo
int,
class Shape,
class ConvexOperand>
1171std::vector<PolygonWithHoles<ExactPoint>> minkowskiBoundaryPieces(
1172 const Shape& shape,
const ConvexOperand& other,
1173 std::vector<std::vector<typename Shape::PointType>> runs) {
1174 using ShapePoint =
typename Shape::PointType;
1175 using SumPoint = minkowskiPoint_t<Shape, ConvexOperand>;
1176 using SumNumber =
typename SumPoint::NumberType;
1177 using ExactNumber =
typename ExactPoint::NumberType;
1180 std::vector<PolygonWithHoles<ExactPoint>> pieces;
1181 pieces.reserve(runs.size() + 1);
1183 for (std::vector<ShapePoint>& run : runs) {
1185 ExactPolygon sum = chainMinkowskiSum<ExactPoint>(chain, other);
1186 if (sum.size() >= 3) {
1187 pieces.emplace_back(std::move(sum), std::vector<ExactPolygon>{},
true);
1191 if constexpr (is_polygon_v<Shape> || is_polygon_with_holes_v<Shape>) {
1194 if (!shape.isDegenerate()) {
1195 const std::vector<SumPoint> operandVertices = minkowskiVertices<SumPoint>(other);
1196 if (operandVertices.empty()) {
1199 const SumPoint& q0 = operandVertices.front();
1203 const auto translated = [&q0](
const auto& ring) {
1204 std::vector<ExactPoint> moved;
1205 moved.reserve(ring.size());
1206 for (
const auto&
vertex : ring.vertices()) {
1207 moved.emplace_back(
static_cast<ExactNumber
>(detail::asNumber<SumNumber>(
vertex.x()) +
1209 static_cast<ExactNumber
>(detail::asNumber<SumNumber>(
vertex.y()) +
1212 return ExactPolygon(std::move(moved),
true);
1214 if constexpr (is_polygon_with_holes_v<Shape>) {
1215 std::vector<ExactPolygon> holes;
1216 holes.reserve(shape.holes().size());
1217 for (
const auto& hole : shape.holes()) {
1218 holes.push_back(translated(hole));
1220 pieces.emplace_back(translated(shape.outer()), std::move(holes),
true);
1222 pieces.emplace_back(translated(shape), std::vector<ExactPolygon>{},
true);
1237template <
class Shape>
1238inline constexpr bool minkowskiHasWalkableBoundary =
1239 is_polygon_v<Shape> || is_polygon_with_holes_v<Shape> || is_polyline_v<Shape> ||
1240 is_monotone_chain_v<Shape>;
1251template <
class Shape>
1252bool minkowskiHasSimpleBoundary(
const Shape& shape) {
1253 if constexpr (is_polygon_with_holes_v<Shape>) {
1254 return shape.holes().empty() || regionSlits(shape).empty();
1256 return is_polygon_v<Shape> || is_polyline_v<Shape> || is_monotone_chain_v<Shape>;
1278template <
class Ring>
1279std::size_t minkowskiReflexCount(
const Ring& ring,
bool isHole) {
1280 const std::size_t n = ring.size();
1284 std::size_t reflex = 0;
1285 for (std::size_t i = 0; i < n; ++i) {
1286 const auto turn =
orientationSign(ring[(i + n - 1) % n], ring[i], ring[(i + 1) % n]);
1287 if (isHole ? turn > 0 : turn < 0) {
1307template <
class Shape>
1308std::pair<std::size_t, std::size_t> minkowskiPieceEstimate(
const Shape& shape) {
1309 if constexpr (is_polygon_v<Shape> || is_polygon_with_holes_v<Shape>) {
1310 std::size_t edges = 0;
1311 std::size_t reflex = 0;
1312 std::size_t holes = 0;
1313 if constexpr (is_polygon_with_holes_v<Shape>) {
1314 edges = shape.outer().size();
1315 reflex = minkowskiReflexCount(shape.outer(),
false);
1316 holes = shape.holes().size();
1317 for (
const auto& hole : shape.holes()) {
1318 edges += hole.size();
1319 reflex += minkowskiReflexCount(hole,
true);
1322 edges = shape.size();
1323 reflex = minkowskiReflexCount(shape,
false);
1325 const std::size_t pieces = reflex + 1 + holes;
1326 return {pieces, edges + 2 * (pieces - 1 + holes)};
1328 const std::size_t edges = shape.size() > 1 ? shape.size() - 1 : 0;
1329 const std::size_t pieces = edges > 0 ? edges : 1;
1330 return {pieces, 2 * pieces};
1389template <
class Shape,
class ConvexOperand,
class Runs>
1390bool minkowskiBoundaryPays(
const Shape& shape,
const ConvexOperand& other,
const Runs& runs) {
1391 const std::size_t operandEdges = other.size();
1392 std::size_t edges = 0;
1393 if constexpr (is_polygon_with_holes_v<Shape>) {
1394 edges = shape.outer().size();
1395 for (
const auto& hole : shape.holes()) {
1396 edges += hole.size();
1398 }
else if constexpr (is_polygon_v<Shape>) {
1399 edges = shape.size();
1401 edges = shape.size() > 1 ? shape.size() - 1 : 0;
1403 const auto [pieces, pieceVertices] = minkowskiPieceEstimate(shape);
1404 const std::size_t convexEdges = pieceVertices + pieces * operandEdges;
1405 const std::size_t boundaryEdges = 2 * edges + runs.size() * operandEdges;
1406 return 6 * boundaryEdges < 5 * convexEdges;
1413template <
class ResultPo
int,
class ShapeA,
class ShapeB>
1453template <
class ExactPo
int,
class ShapeA,
class ShapeB>
1454std::vector<PolygonWithHoles<ExactPoint>> minkowskiOneSidedPieces(
const ShapeA& a,
1456 std::vector<PolygonWithHoles<ExactPoint>> pieces;
1457 for (
const auto& piece : minkowskiConvexPieces(a)) {
1459 pieces.insert(pieces.end(), sum.begin(), sum.end());
1477template <
class Shape>
1478std::size_t minkowskiPieceCount(
const Shape& shape) {
1479 if constexpr (is_polygon_with_holes_v<Shape>) {
1480 return shape.vertexCount();
1482 return shape.size();
1517template <
class ShapeA,
class ShapeB>
1518bool minkowskiOneSidedDecomposesLeft(
const ShapeA& a,
const ShapeB& b) {
1519 return static_cast<long double>(minkowskiPieceCount(a)) * b.bbox().template area<long double>() <=
1520 static_cast<long double>(minkowskiPieceCount(b)) * a.bbox().template area<long double>();
1570template <
class ResultPo
int,
class ShapeA,
class ShapeB>
1573 using SumPoint = minkowskiPoint_t<ShapeA, ShapeB>;
1574 using SumNumber =
typename SumPoint::NumberType;
1579 const auto& left = holeFilteredFor(a, b);
1580 const auto& right = holeFilteredFor(b, a);
1582 const bool leftConvex = minkowskiIsConvex(left);
1583 const bool rightConvex = minkowskiIsConvex(right);
1585 if (leftConvex && rightConvex) {
1586 const auto sum = minkowskiConvexSum(minkowskiAsConvex(left), minkowskiAsConvex(right));
1587 if (sum.isDegenerate()) {
1610 constexpr bool exactPieces = !std::is_floating_point_v<SumNumber>;
1611 if constexpr (exactPieces && minkowskiHasWalkableBoundary<std::remove_cvref_t<
decltype(left)>>) {
1612 if (rightConvex && minkowskiHasArea(right) && minkowskiHasSimpleBoundary(left)) {
1613 const auto convexRight = minkowskiAsConvex(right);
1614 auto runs = minkowskiBoundaryRuns(left);
1615 if (minkowskiBoundaryPays(left, convexRight, runs)) {
1617 minkowskiBoundaryPieces<ExactPoint>(left, convexRight, std::move(runs)),
true);
1621 if constexpr (exactPieces && minkowskiHasWalkableBoundary<std::remove_cvref_t<
decltype(right)>>) {
1622 if (leftConvex && minkowskiHasArea(left) && minkowskiHasSimpleBoundary(right)) {
1623 const auto convexLeft = minkowskiAsConvex(left);
1624 auto runs = minkowskiBoundaryRuns(right);
1625 if (minkowskiBoundaryPays(right, convexLeft, runs)) {
1627 minkowskiBoundaryPieces<ExactPoint>(right, convexLeft, std::move(runs)),
true);
1649 if constexpr (exactPieces) {
1650 if (!leftConvex && !rightConvex && minkowskiHasArea(left) &&
1651 minkowskiHasArea(right)) {
1652 auto pieces = minkowskiOneSidedDecomposesLeft(left, right)
1653 ? minkowskiOneSidedPieces<ExactPoint>(left, right)
1654 : minkowskiOneSidedPieces<ExactPoint>(right, left);
1659 return decomposedMinkowskiSum<ResultPoint>(left, right);
1687template <
class ResultPo
int,
class ShapeA,
class ShapeB>
1690 if (sum.componentCount() == 0) {
1693 return sum.component(0);
1708template <
class ResultPo
int,
class SetT,
class ShapeB>
1710 using SumPoint = minkowskiPoint_t<SetT, ShapeB>;
1711 using SumNumber =
typename SumPoint::NumberType;
1714 std::vector<PolygonWithHoles<ExactPoint>> regions;
1715 const auto addSum = [®ions](
const auto& left,
const auto& right) {
1716 for (
const auto& region : regularizedMinkowskiSum<ExactPoint>(left, right)) {
1717 regions.push_back(region);
1720 for (
const auto& component : set) {
1721 if constexpr (is_polygon_set_v<ShapeB>) {
1722 for (
const auto& piece : other) {
1723 addSum(component, piece);
1726 addSum(component, other);
1746#define PGL_DEFINE_REGION_MINKOWSKI_SUM(RECEIVER, CONCEPT, OPERAND) \
1747 template <class PointType_, class TLabel> \
1748 template <class ResultNumber, CONCEPT OPERAND> \
1749 PolygonWithHoles<Point<ResultNumber, typename PointType_::LabelType>> \
1750 RECEIVER<PointType_, TLabel>::minkowskiSum(const OPERAND& other) const { \
1751 return detail::singleRegionMinkowskiSum< \
1752 Point<ResultNumber, typename PointType_::LabelType>>(*this, other); \
1758#define PGL_DEFINE_REGION_SET_MINKOWSKI_SUM(RECEIVER, CONCEPT, OPERAND) \
1759 template <class PointType_, class TLabel> \
1760 template <class ResultNumber, CONCEPT OPERAND> \
1761 PolygonSet<Point<ResultNumber, typename PointType_::LabelType>> \
1762 RECEIVER<PointType_, TLabel>::minkowskiSum(const OPERAND& other) const { \
1763 return detail::regularizedMinkowskiSum< \
1764 Point<ResultNumber, typename PointType_::LabelType>>(*this, other); \
1821#undef PGL_DEFINE_REGION_MINKOWSKI_SUM
1828#define PGL_DEFINE_CHAIN_MINKOWSKI_SUM(CONCEPT, OPERAND) \
1829 template <class PointType_, class TLabel, class Storage> \
1830 template <class ResultNumber, CONCEPT OPERAND> \
1831 Polygon<Point<ResultNumber, typename PointType_::LabelType>> \
1832 MonotoneChain<PointType_, TLabel, Storage>::minkowskiSum(const OPERAND& other) const { \
1833 return detail::chainMinkowskiSum<Point<ResultNumber, typename PointType_::LabelType>>( \
1841#undef PGL_DEFINE_CHAIN_MINKOWSKI_SUM
1848#define PGL_DEFINE_CHAIN_REGULARIZED_SUM(CONCEPT, OPERAND) \
1849 template <class PointType_, class TLabel, class Storage> \
1850 template <class ResultNumber, CONCEPT OPERAND> \
1851 PolygonSet<Point<ResultNumber, typename PointType_::LabelType>> \
1852 MonotoneChain<PointType_, TLabel, Storage>::minkowskiSum(const OPERAND& other) const { \
1853 return detail::regularizedMinkowskiSum< \
1854 Point<ResultNumber, typename PointType_::LabelType>>(*this, other); \
1863#undef PGL_DEFINE_CHAIN_REGULARIZED_SUM
1870template <
class Po
intType_,
class TLabel>
1871template <
class ResultNumber, detail::SetMinkowskiOperandConcept OtherShape>
1874 return detail::setMinkowskiSum<Point<ResultNumber, typename PointType_::LabelType>>(*
this,
1882#define PGL_DEFINE_SET_MIRROR_MINKOWSKI_SUM(RECEIVER) \
1883 template <class PointType_, class TLabel> \
1884 template <class ResultNumber, PolygonSetConcept OtherSet> \
1885 PolygonSet<Point<ResultNumber, typename PointType_::LabelType>> \
1886 RECEIVER<PointType_, TLabel>::minkowskiSum(const OtherSet& other) const { \
1887 return other.template minkowskiSum<ResultNumber>(*this); \
1894#undef PGL_DEFINE_SET_MIRROR_MINKOWSKI_SUM
Regularized boolean operations on closed polygonal regions.
Definition forward.hpp:315
Definition forward.hpp:320
Definition forward.hpp:308
Definition forward.hpp:316
Definition forward.hpp:317
Definition forward.hpp:321
Definition forward.hpp:313
Definition forward.hpp:307
Definition forward.hpp:314
#define PGL_DEFINE_REGION_MINKOWSKI_SUM(RECEIVER, CONCEPT, OPERAND)
Definition minkowskisum.hpp:1746
#define PGL_DEFINE_SET_MIRROR_MINKOWSKI_SUM(RECEIVER)
Definition minkowskisum.hpp:1882
#define PGL_DEFINE_REGION_SET_MINKOWSKI_SUM(RECEIVER, CONCEPT, OPERAND)
Definition minkowskisum.hpp:1758
#define PGL_DEFINE_CHAIN_MINKOWSKI_SUM(CONCEPT, OPERAND)
Definition minkowskisum.hpp:1828
#define PGL_DEFINE_CHAIN_REGULARIZED_SUM(CONCEPT, OPERAND)
Definition minkowskisum.hpp:1848
Definition arrangement.hpp:67
@ x
Definition intervaltree.hpp:24
@ edge
Definition bitmatrix.hpp:37
@ vertex
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
PolygonSet() -> PolygonSet< Point<>, NoLabel >
Definition polygonset.hpp:1699
constexpr std::partial_ordering orientationSign(const Point< ANumber, ALabel > &a, const Point< BNumber, BLabel > &b, const Point< CNumber, CLabel > &c)
Classifies the orientation of three points.
Definition orientation.hpp:544
Rational(T) -> Rational< T >
MonotoneChain() -> MonotoneChain< Point<>, NoLabel >
Definition monotonechain.hpp:2439
constexpr bool collinear(const Point< ANumber, ALabel > &a, const Point< BNumber, BLabel > &b, const Point< CNumber, CLabel > &c)
Tests whether three points are collinear.
Definition orientation.hpp:651
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
Polygon() -> Polygon< Point<>, NoLabel >
Definition polygon.hpp:3200
PolygonSet< ResultPoint > regularizedUnionOf(const ShapeRange &shapes, bool simpleBoundaries=false)
The regularized union of arbitrarily many shapes, as a set of regions.
Definition booleans.hpp:780
Set of closed regions with pairwise disjoint interiors.
Definition polygonset.hpp:165
constexpr auto minkowskiSum(const OtherShape &other) const
Returns the Minkowski sum of this shape and another (A ⊕ B).
Definition minkowski.hpp:807
friend struct PolygonWithHoles
Definition polygonwithholes.hpp:3314
constexpr Polygon()=default
constexpr Polyline()=default