74template <
class ExactPo
int>
75void dropCollinearRingVertices(std::vector<ExactPoint>& ring) {
77 while (changed && ring.size() > 3) {
79 for (std::size_t i = 0; i < ring.size() && ring.size() > 3;) {
80 const ExactPoint& previous = ring[(i + ring.size() - 1) % ring.size()];
81 const ExactPoint& next = ring[(i + 1) % ring.size()];
83 ring.erase(ring.begin() +
static_cast<std::ptrdiff_t
>(i));
117template <
class ResultPo
int,
class ExactPo
int>
119 const Arrangement<ExactPoint>& arrangement,
const std::vector<char>& keep) {
121 using ExactNumber =
typename ExactPoint::NumberType;
124 constexpr std::size_t none = std::numeric_limits<std::size_t>::max();
126 std::vector<PolygonWithHoles<ResultPoint>> result;
127 const auto isKept = [&](HalfedgeId h) {
return keep[arrangement.face(h).index()] != 0; };
133 std::vector<std::size_t> parent(arrangement.faceCount());
134 for (std::size_t i = 0; i < parent.size(); ++i) {
137 const auto findRoot = [&parent](std::size_t
x) {
138 while (parent[
x] !=
x) {
139 parent[
x] = parent[parent[
x]];
144 std::vector<HalfedgeId> boundary;
145 for (std::uint32_t i = 0; i < arrangement.halfedgeCount(); ++i) {
146 const HalfedgeId h(i);
150 if (isKept(arrangement.twin(h))) {
151 parent[findRoot(arrangement.face(h).index())] =
152 findRoot(arrangement.face(arrangement.twin(h)).index());
154 boundary.push_back(h);
164 const auto nextBoundary = [&](HalfedgeId h) {
165 HalfedgeId ahead = arrangement.next(h);
166 while (isKept(arrangement.twin(ahead))) {
167 ahead = arrangement.next(arrangement.twin(ahead));
172 std::map<std::size_t, std::vector<std::vector<ExactPoint>>> ringsOfPiece;
173 std::vector<char> walked(arrangement.halfedgeCount(), 0);
174 for (
const HalfedgeId start : boundary) {
175 if (walked[start.index()] != 0) {
178 std::vector<ExactPoint> walk;
179 HalfedgeId h = start;
181 walked[h.index()] = 1;
182 walk.push_back(arrangement[arrangement.source(h)]);
184 }
while (h != start);
185 splitWalkIntoRings(walk, ringsOfPiece[findRoot(arrangement.face(start).index())]);
192 const auto convert = [](
const std::vector<ExactPoint>& ring) {
193 std::vector<ResultPoint> converted;
194 converted.reserve(ring.size());
195 for (
const ExactPoint&
vertex : ring) {
196 converted.emplace_back(
vertex);
198 constexpr bool exact = std::is_same_v<ResultPoint, ExactPoint>;
199 return ResultPolygon(std::move(converted), exact);
202 for (
auto& entry : ringsOfPiece) {
203 std::vector<ExactPolygon> outers;
204 std::vector<ExactPolygon> holes;
205 for (std::vector<ExactPoint>& ring : entry.second) {
206 dropCollinearRingVertices(ring);
207 const int orientation = ringOrientation(ring);
208 if (orientation == 0) {
216 if (orientation < 0) {
217 std::reverse(ring.begin(), ring.end());
219 std::rotate(ring.begin(), std::min_element(ring.begin(), ring.end()), ring.end());
220 (orientation > 0 ? outers : holes).emplace_back(std::move(ring),
true);
222 if (outers.empty()) {
231 std::vector<std::vector<ResultPolygon>> holesOfOuter(outers.size());
232 for (
const ExactPolygon& hole : holes) {
233 std::size_t owner = 0;
234 if (outers.size() > 1) {
235 const ExactPoint witness = hole.template pointInside<ExactNumber>();
236 std::size_t best = none;
237 for (std::size_t i = 0; i < outers.size(); ++i) {
238 if (outers[i].contains(witness) &&
239 (best == none || outers[i].twiceArea() < outers[best].twiceArea())) {
243 owner = best == none ? 0 : best;
245 holesOfOuter[owner].push_back(convert(hole.vertices()));
255 constexpr bool exact = std::is_same_v<ResultPoint, ExactPoint>;
256 for (std::size_t i = 0; i < outers.size(); ++i) {
257 if constexpr (exact) {
258 std::sort(holesOfOuter[i].begin(), holesOfOuter[i].end());
260 result.emplace_back(convert(outers[i].vertices()), std::move(holesOfOuter[i]),
268 std::sort(result.begin(), result.end());
284template <
class ExactPo
int>
286 using ExactNumber =
typename ExactPoint::NumberType;
289 assert(!cuts.empty());
290 ExactNumber loX = cuts.front().min().x();
291 ExactNumber loY = cuts.front().min().y();
292 ExactNumber hiX = loX;
293 ExactNumber hiY = loY;
294 for (
const ExactSegment& cut : cuts) {
295 for (
const ExactPoint& point : {cut.min(), cut.max()}) {
296 loX = std::min(loX, point.x());
297 loY = std::min(loY, point.y());
298 hiX = std::max(hiX, point.x());
299 hiY = std::max(hiY, point.y());
302 const ExactNumber margin(1);
303 const std::array<ExactPoint, 4> corners{
304 ExactPoint(loX - margin, loY - margin), ExactPoint(hiX + margin, loY - margin),
305 ExactPoint(hiX + margin, hiY + margin), ExactPoint(loX - margin, hiY + margin)};
306 std::vector<ExactSegment> segments = cuts;
307 for (std::size_t i = 0; i < corners.size(); ++i) {
308 segments.emplace_back(corners[i], corners[(i + 1) % corners.size()]);
310 return Arrangement<ExactPoint>(segments);
317template <
class ResultPo
int,
class ExactPo
int,
class KeepWitness>
320 using ExactNumber =
typename ExactPoint::NumberType;
326 const Arrangement<ExactPoint> arrangement = framedArrangement(cuts);
328 std::vector<char> keep(arrangement.faceCount(), 0);
329 for (std::uint32_t i = 0; i < arrangement.faceCount(); ++i) {
331 if (!arrangement.isUnbounded(f)) {
332 keep[f.index()] =
static_cast<char>(
333 keepWitness(arrangement.template witness<ExactNumber>(f)));
336 return regularizedCellsFromKeep<ResultPoint>(arrangement, keep);
364template <
class ResultPo
int,
class ShapeA,
class ShapeB,
class KeepCell>
366 using ExactNumber = Exact1DNumber<typename ShapeA::NumberType, typename ShapeB::NumberType>;
369 const bool aHasArea = !a.isDegenerate();
370 const bool bHasArea = !b.isDegenerate();
372 std::vector<Segment<ExactPoint>> cuts;
373 appendCutSegments<ExactPoint>(a, cuts);
374 appendCutSegments<ExactPoint>(b, cuts);
375 return regularizedCells<ResultPoint>(
376 cuts, [&a, &b, aHasArea, bHasArea, &keepCell](
const ExactPoint& witness) {
377 return keepCell(aHasArea && a.contains(witness), bHasArea && b.contains(witness));
399template <
class ResultPo
int,
class ShapeType>
401 const std::vector<ShapeType>& distinct) {
402 using ShapeNumber =
typename ShapeType::NumberType;
403 using ExactNumber = Exact1DNumber<ShapeNumber, ShapeNumber>;
408 const Arrangement<ExactPoint> arrangement(distinct, detail::simpleBoundaries);
411 const std::size_t faceCount = arrangement.faceCount();
412 const std::size_t pieceCount = distinct.size();
413 constexpr std::size_t wordBits = 64;
414 const std::size_t words = (pieceCount + wordBits - 1) / wordBits;
419 std::vector<std::uint32_t> faceEdgeBegin(faceCount + 1, 0);
420 for (std::uint32_t i = 0; i < arrangement.halfedgeCount(); ++i) {
421 const HalfedgeId h(i);
422 ++faceEdgeBegin[arrangement.face(h).index() + 1];
424 for (std::size_t i = 0; i < faceCount; ++i) {
425 faceEdgeBegin[i + 1] += faceEdgeBegin[i];
427 std::vector<std::uint32_t> faceEdge(arrangement.halfedgeCount(), 0);
429 std::vector<std::uint32_t> cursor(faceEdgeBegin.begin(), faceEdgeBegin.end() - 1);
430 for (std::uint32_t i = 0; i < arrangement.halfedgeCount(); ++i) {
431 const HalfedgeId h(i);
432 faceEdge[cursor[arrangement.face(h).index()]++] = h.index();
444 std::vector<std::uint64_t> membership(words, 0);
445 std::size_t covered = 0;
446 const auto crossEdge = [&](std::uint32_t halfedge) {
447 for (
const std::uint32_t origin : arrangement.originsOf(HalfedgeId(halfedge))) {
448 const std::size_t word = origin / wordBits;
449 const std::uint64_t mask = std::uint64_t{1} << (origin % wordBits);
450 if ((membership[word] & mask) == 0) {
455 membership[word] ^= mask;
461 std::uint32_t cursor;
462 std::uint32_t entered;
464 constexpr std::uint32_t noEdge = ~std::uint32_t{0};
466 std::vector<std::size_t> coverage(faceCount, 0);
467 std::vector<char> seen(faceCount, 0);
468 std::vector<Frame> stack;
470 stack.push_back(Frame{0, faceEdgeBegin[0], noEdge});
473 while (!stack.empty()) {
474 Frame& top = stack.back();
475 if (top.cursor == faceEdgeBegin[top.face + 1]) {
476 if (top.entered != noEdge) {
477 crossEdge(top.entered);
482 const std::uint32_t h = faceEdge[top.cursor++];
483 const std::uint32_t next = arrangement.face(arrangement.twin(HalfedgeId(h))).index();
484 if (seen[next] != 0) {
489 coverage[next] = covered;
491 stack.push_back(Frame{next, faceEdgeBegin[next], h});
494 assert(covered == 0);
495 assert(std::ranges::all_of(seen, [](
char value) {
return value != 0; }));
496 std::vector<char> keep(faceCount, 0);
497 for (std::uint32_t i = 0; i < arrangement.faceCount(); ++i) {
499 keep[f.index()] =
static_cast<char>(coverage[f.index()] != 0);
501 return regularizedCellsFromKeep<ResultPoint>(arrangement, keep);
523template <
class ResultPo
int,
class TriangleType>
524std::optional<PolygonSet<ResultPoint>> regularizedUnionOfIntegralTriangles(
525 const std::vector<TriangleType>& triangles) {
526 using ShapeNumber =
typename TriangleType::NumberType;
527 using ExactNumber = Exact1DNumber<ShapeNumber, ShapeNumber>;
537 std::array<IPoint, 3>
vertex;
538 std::int64_t minX, minY, maxX, maxY;
541 constexpr std::int64_t safeCoordinate = 1000000000;
542 const auto narrow = [](
const ShapeNumber& value) -> std::optional<std::int64_t> {
544 if (!value.isInteger()) {
548 const Integer integer =
static_cast<Integer
>(value);
549 if (!detail::representableAs<std::int64_t>(integer)) {
552 return detail::narrowTo<std::int64_t>(integer);
553 }
else if constexpr (detail::extended_integral<ShapeNumber> ||
554 std::same_as<ShapeNumber, BigInt>) {
555 if (!detail::representableAs<std::int64_t>(value)) {
558 return detail::narrowTo<std::int64_t>(value);
564 const auto cross = [](Wide ax, Wide ay, Wide bx, Wide by) {
565 return ax * by - ay * bx;
568 std::vector<ITriangle> integral;
569 integral.reserve(triangles.size());
570 for (
const TriangleType& triangle : triangles) {
571 ITriangle converted{};
572 const auto vertices = triangle.vertices();
573 for (std::size_t i = 0; i < 3; ++i) {
574 const auto x = narrow(vertices[i].
x());
575 const auto y = narrow(vertices[i].
y());
579 if (*
x < -safeCoordinate || safeCoordinate < *
x ||
580 *
y < -safeCoordinate || safeCoordinate < *
y) {
583 converted.vertex[i] = {*
x, *
y};
585 converted.minX = converted.maxX = converted.vertex[0].x;
586 converted.minY = converted.maxY = converted.vertex[0].y;
587 for (
const IPoint& point : converted.vertex) {
588 converted.minX = std::min(converted.minX, point.x);
589 converted.minY = std::min(converted.minY, point.y);
590 converted.maxX = std::max(converted.maxX, point.x);
591 converted.maxY = std::max(converted.maxY, point.y);
593 const IPoint& a = converted.vertex[0];
594 const IPoint& b = converted.vertex[1];
595 const IPoint& c = converted.vertex[2];
596 converted.twiceArea =
597 cross(Wide(b.x) - a.x, Wide(b.y) - a.y, Wide(c.x) - a.x, Wide(c.y) - a.y);
598 if (converted.twiceArea < 0) {
599 converted.twiceArea = -converted.twiceArea;
601 integral.push_back(converted);
605 std::vector<std::uint32_t> order(integral.size());
606 std::iota(order.begin(), order.end(), std::uint32_t{0});
607 std::sort(order.begin(), order.end(), [&integral](std::uint32_t left, std::uint32_t right) {
608 return integral[left].twiceArea > integral[right].twiceArea;
615 const auto fraction = [](Wide numerator, Wide denominator) {
616 if (denominator < 0) {
617 numerator = -numerator;
618 denominator = -denominator;
620 return Fraction{numerator, denominator};
622 const auto less = [](
const Fraction& left,
const Fraction& right) {
623 return left.numerator * right.denominator <
624 right.numerator * left.denominator;
626 const auto maximum = [&less](
const Fraction& left,
const Fraction& right) {
627 return less(left, right) ? right : left;
629 const auto minimum = [&less](
const Fraction& left,
const Fraction& right) {
630 return less(left, right) ? left : right;
632 const auto exact = [](
const Fraction& value) {
633 return ExactNumber(BigInt(value.numerator), BigInt(value.denominator));
635 constexpr Fraction zero{0, 1};
636 constexpr Fraction one{1, 1};
637 std::vector<ExactSegment> exposed;
640 std::vector<std::pair<Fraction, Fraction>> uncovered;
642 for (std::size_t owner = 0; owner < integral.size(); ++owner) {
643 const ITriangle& mine = integral[owner];
645 const IPoint from = mine.vertex[
edge];
646 const IPoint to = mine.vertex[(
edge + 1) % 3];
647 const Wide dx = Wide(to.x) - from.x;
648 const Wide dy = Wide(to.y) - from.y;
649 const std::int64_t edgeMinX = std::min(from.x, to.x);
650 const std::int64_t edgeMinY = std::min(from.y, to.y);
651 const std::int64_t edgeMaxX = std::max(from.x, to.x);
652 const std::int64_t edgeMaxY = std::max(from.y, to.y);
653 uncovered.assign(1, {zero, one});
655 for (
const std::uint32_t other : order) {
656 const ITriangle& theirs = integral[other];
657 if (other == owner || edgeMaxX < theirs.minX || theirs.maxX < edgeMinX ||
658 edgeMaxY < theirs.minY || theirs.maxY < edgeMinY) {
663 bool feasible =
true;
664 bool coincident =
false;
665 bool interiorOnRight =
false;
666 for (std::size_t wall = 0; wall < 3; ++wall) {
667 const IPoint a = theirs.vertex[wall];
668 const IPoint b = theirs.vertex[(wall + 1) % 3];
669 const Wide wx = Wide(b.x) - a.x;
670 const Wide wy = Wide(b.y) - a.y;
671 const Wide c = cross(wx, wy, Wide(from.x) - a.x,
673 const Wide slope = cross(wx, wy, dx, dy);
681 interiorOnRight = wx * dx + wy * dy < 0;
685 const Fraction at = fraction(-c, slope);
687 low = maximum(low, at);
689 high = minimum(high, at);
691 if (!less(low, high)) {
696 if (!feasible || !less(low, high) || (coincident && !interiorOnRight)) {
705 std::size_t first = 0;
706 while (first < uncovered.size() && !less(low, uncovered[first].second)) {
709 std::size_t last = first;
710 while (last < uncovered.size() && less(uncovered[last].first, high)) {
716 const Fraction head = uncovered[first].first;
717 const Fraction tail = uncovered[last - 1].second;
718 const bool keepHead = less(head, low);
719 const bool keepTail = less(high, tail);
720 if (keepHead && keepTail && last - first == 1) {
721 uncovered[first] = {head, low};
722 uncovered.insert(uncovered.begin() +
static_cast<std::ptrdiff_t
>(first) + 1,
725 std::size_t write = first;
727 uncovered[write++] = {head, low};
730 uncovered[write++] = {high, tail};
732 uncovered.erase(uncovered.begin() +
static_cast<std::ptrdiff_t
>(write),
733 uncovered.begin() +
static_cast<std::ptrdiff_t
>(last));
735 if (uncovered.empty()) {
740 const auto pointAt = [&](
const Fraction& parameter) {
741 const ExactNumber t = exact(parameter);
742 return ExactPoint(ExactNumber(from.x) + ExactNumber(dx) * t,
743 ExactNumber(from.y) + ExactNumber(dy) * t);
745 for (
const auto& piece : uncovered) {
746 exposed.emplace_back(pointAt(piece.first), pointAt(piece.second));
751 return regularizedCells<ResultPoint>(exposed, [&triangles](
const ExactPoint& witness) {
752 return std::ranges::any_of(
753 triangles, [&witness](
const TriangleType& triangle) {
return triangle.contains(witness); });
779template <
class ResultPo
int,
class ShapeRange>
781 bool simpleBoundaries =
false) {
782 using ShapeType = std::ranges::range_value_t<ShapeRange>;
793 std::vector<ShapeType> distinct(std::ranges::begin(shapes), std::ranges::end(shapes));
794 std::erase_if(distinct, [](
const ShapeType& shape) {
return shape.isDegenerate(); });
795 std::sort(distinct.begin(), distinct.end());
796 distinct.erase(std::unique(distinct.begin(), distinct.end()), distinct.end());
798 if (distinct.empty()) {
802 if constexpr (detail::is_polygon_set_v<ShapeType>) {
809 std::vector<typename ShapeType::ComponentType> components;
810 for (
const ShapeType& set : distinct) {
811 components.insert(components.end(), set.components().begin(), set.components().end());
814 }
else if constexpr (detail::is_convex_v<ShapeType> || detail::is_triangle_v<ShapeType> ||
815 detail::is_rectangle_v<ShapeType>) {
816 if constexpr (detail::is_triangle_v<ShapeType> &&
817 !std::floating_point<typename ShapeType::NumberType>) {
818 if (distinct.size() >= 16) {
820 detail::regularizedUnionOfIntegralTriangles<ResultPoint>(distinct)) {
821 return std::move(*result);
825 return detail::regularizedUnionByCoverage<ResultPoint>(distinct);
827 using ShapeNumber =
typename ShapeType::NumberType;
830 if (simpleBoundaries) {
831 return detail::regularizedUnionByCoverage<ResultPoint>(distinct);
833 std::vector<Segment<ExactPoint>> cuts;
834 for (
const ShapeType& shape : distinct) {
835 detail::appendCutSegments<ExactPoint>(shape, cuts);
837 return detail::regularizedCells<ResultPoint>(cuts, [&distinct](
const ExactPoint& witness) {
838 return std::ranges::any_of(
839 distinct, [&witness](
const ShapeType& shape) {
return shape.contains(witness); });
855template <
class OtherShape>
856constexpr decltype(
auto) booleanOperand(
const OtherShape& other) {
857 if constexpr (is_rectangle_v<OtherShape> || is_triangle_v<OtherShape> ||
858 is_convex_v<OtherShape>) {
859 return other.asPolygon();
866template <
class ResultPo
int,
class ShapeA,
class ShapeB>
868 return regularizedBoolean<ResultPoint>(a, b, [](
bool inA,
bool inB) {
return inA && !inB; });
872template <
class ResultPo
int,
class ShapeA,
class ShapeB>
874 return regularizedBoolean<ResultPoint>(a, b, [](
bool inA,
bool inB) {
return inA || inB; });
894template <
class ResultPo
int,
class ShapeA,
class ShapeB>
896 if (!a.bbox().interiorsIntersect(b.bbox())) {
899 return regularizedBoolean<ResultPoint>(a, b, [](
bool inA,
bool inB) {
return inA && inB; });
936template <
class ResultPo
int,
class ShapeA,
class ShapeB>
938literalIntersection(
const ShapeA& a,
const ShapeB& b) {
939 using ExactNumber = Exact1DNumber<typename ShapeA::NumberType, typename ShapeB::NumberType>;
943 using Piece = std::variant<ResultPoint, ResultPolyline, ResultRegion>;
945 std::vector<Piece> pieces;
949 if (a.empty() || b.empty() || !a.bbox().intersects(b.bbox())) {
953 std::vector<Segment<ExactPoint>> cuts;
954 appendCutSegments<ExactPoint>(a, cuts);
955 const std::size_t cutsOfA = cuts.size();
956 appendCutSegments<ExactPoint>(b, cuts);
961 const ExactPoint
vertex(a.bbox().min());
963 pieces.emplace_back(ResultPoint(
vertex));
967 if (cuts.size() == cutsOfA) {
968 const ExactPoint
vertex(b.bbox().min());
970 pieces.emplace_back(ResultPoint(
vertex));
975 const Arrangement<ExactPoint> arrangement = framedArrangement(cuts);
980 std::vector<char> keep(arrangement.faceCount(), 0);
981 for (std::uint32_t i = 0; i < arrangement.faceCount(); ++i) {
983 if (!arrangement.isUnbounded(f)) {
984 const ExactPoint witness = arrangement.template witness<ExactNumber>(f);
985 keep[f.index()] =
static_cast<char>(a.contains(witness) && b.contains(witness));
990 std::vector<std::uint32_t> strandDegree(arrangement.vertexCount(), 0);
991 std::vector<std::vector<std::uint32_t>> adjacency(arrangement.vertexCount());
992 for (std::uint32_t i = 0; i < arrangement.edgeCount(); ++i) {
993 const HalfedgeId h(2 * i);
994 if (keep[arrangement.face(h).index()] != 0 ||
995 keep[arrangement.face(arrangement.twin(h)).index()] != 0) {
998 const ExactPoint witness = arrangement.template witness<ExactNumber>(h);
999 if (!a.contains(witness) || !b.contains(witness)) {
1002 const std::uint32_t source = arrangement.source(h).index();
1003 const std::uint32_t target = arrangement.target(h).index();
1004 adjacency[source].push_back(target);
1005 adjacency[target].push_back(source);
1006 ++strandDegree[source];
1007 ++strandDegree[target];
1013 for (std::uint32_t i = 0; i < arrangement.vertexCount(); ++i) {
1014 if (strandDegree[i] != 0) {
1017 const VertexId v(i);
1018 const HalfedgeId start = arrangement.outgoing(v);
1019 assert(start.valid());
1020 bool besideKept =
false;
1021 HalfedgeId h = start;
1023 besideKept = keep[arrangement.face(h).index()] != 0;
1024 h = arrangement.next(arrangement.twin(h));
1025 }
while (h != start && !besideKept);
1029 const ExactPoint& position = arrangement[v];
1030 if (a.contains(position) && b.contains(position)) {
1031 pieces.emplace_back(ResultPoint(position));
1039 const auto takeEdge = [&adjacency](std::uint32_t from, std::uint32_t to) {
1040 auto& out = adjacency[from];
1041 out.erase(std::find(out.begin(), out.end(), to));
1042 auto& back = adjacency[to];
1043 back.erase(std::find(back.begin(), back.end(), from));
1045 const auto walkFrom = [&](std::uint32_t start,
bool stopAtJunctions) {
1046 std::vector<std::uint32_t> walk{start};
1047 std::uint32_t current = start;
1048 while (!adjacency[current].empty() &&
1049 (!stopAtJunctions || adjacency[current].size() == 1)) {
1050 const std::uint32_t next = adjacency[current].front();
1051 takeEdge(current, next);
1052 walk.push_back(next);
1057 std::vector<std::vector<std::uint32_t>> strands;
1058 for (std::uint32_t i = 0; i < adjacency.size(); ++i) {
1059 if (adjacency[i].size() == 1) {
1060 strands.push_back(walkFrom(i,
true));
1063 for (std::uint32_t i = 0; i < adjacency.size(); ++i) {
1064 while (!adjacency[i].empty()) {
1065 strands.push_back(walkFrom(i,
false));
1072 const auto removable = [&](std::uint32_t before, std::uint32_t at, std::uint32_t after) {
1073 return strandDegree[at] == 2 &&
1074 collinear(arrangement[VertexId(before)], arrangement[VertexId(at)],
1075 arrangement[VertexId(after)]);
1077 for (std::vector<std::uint32_t>& walk : strands) {
1078 if (walk.size() > 2 && walk.front() == walk.back()) {
1082 std::size_t corner = 0;
1083 while (corner < walk.size() &&
1084 removable(walk[(corner + walk.size() - 1) % walk.size()], walk[corner],
1085 walk[(corner + 1) % walk.size()])) {
1088 std::rotate(walk.begin(),
1089 walk.begin() +
static_cast<std::ptrdiff_t
>(corner % walk.size()),
1091 walk.push_back(walk.front());
1093 std::vector<ResultPoint> vertices;
1094 vertices.reserve(walk.size());
1095 vertices.emplace_back(arrangement[VertexId(walk.front())]);
1096 for (std::size_t i = 1; i + 1 < walk.size(); ++i) {
1097 if (!removable(walk[i - 1], walk[i], walk[i + 1])) {
1098 vertices.emplace_back(arrangement[VertexId(walk[i])]);
1101 vertices.emplace_back(arrangement[VertexId(walk.back())]);
1102 pieces.emplace_back(ResultPolyline(std::move(vertices)));
1105 for (
const ResultRegion& region : regularizedCellsFromKeep<ResultPoint>(arrangement, keep)) {
1106 pieces.emplace_back(region);
1115template <
class ResultPo
int,
class ShapeA,
class ShapeB>
1117 return regularizedBoolean<ResultPoint>(a, b, [](
bool inA,
bool inB) {
return inA != inB; });
1126template <
class Po
intType_,
class TLabel>
1127template <
class ResultNumber>
1143 const std::array<PolygonWithHoles, 1> self{*
this};
1147template <
class Po
intType_,
class TLabel>
1148template <
class ResultNumber>
1155 std::vector<PolygonWithHoles<ResultPoint>> pieces;
1156 pieces.reserve(components_.size());
1157 for (
const auto&
component : components_) {
1159 pieces.push_back(piece);
1165template <
class Po
intType_,
class TLabel>
1166template <
class ResultNumber, PolygonConcept OtherPolygon>
1169 return detail::regularizedDifference<Point<ResultNumber, typename PointType_::LabelType>>(*
this,
1173template <
class Po
intType_,
class TLabel>
1174template <
class ResultNumber, ConvexConcept OtherConvex>
1180template <
class Po
intType_,
class TLabel>
1181template <
class ResultNumber, TriangleConcept OtherTriangle>
1187template <
class Po
intType_,
class TLabel>
1188template <
class ResultNumber, RectangleConcept OtherRectangle>
1194template <
class Po
intType_,
class TLabel>
1195template <
class ResultNumber, PolygonWithHolesConcept OtherRegion>
1198 return detail::regularizedDifference<Point<ResultNumber, typename PointType_::LabelType>>(*
this,
1202template <
class Po
intType_,
class TLabel>
1203template <
class ResultNumber, PolygonSetConcept OtherSet>
1206 return detail::regularizedDifference<Point<ResultNumber, typename PointType_::LabelType>>(*
this,
1210template <
class Po
intType_,
class TLabel>
1211template <
class ResultNumber, PolygonConcept OtherPolygon>
1214 return detail::regularizedDifference<Point<ResultNumber, typename PointType_::LabelType>>(*
this,
1218template <
class Po
intType_,
class TLabel>
1219template <
class ResultNumber, ConvexConcept OtherConvex>
1225template <
class Po
intType_,
class TLabel>
1226template <
class ResultNumber, TriangleConcept OtherTriangle>
1232template <
class Po
intType_,
class TLabel>
1233template <
class ResultNumber, RectangleConcept OtherRectangle>
1239template <
class Po
intType_,
class TLabel>
1240template <
class ResultNumber, PolygonWithHolesConcept OtherRegion>
1243 return detail::regularizedDifference<Point<ResultNumber, typename PointType_::LabelType>>(*
this,
1247template <
class Po
intType_,
class TLabel>
1248template <
class ResultNumber, PolygonSetConcept OtherSet>
1251 return detail::regularizedDifference<Point<ResultNumber, typename PointType_::LabelType>>(*
this,
1263template <
class Po
intType_,
class TLabel>
1264template <
class ResultNumber, PolygonalRegionConcept OtherRegion>
1270template <
class Po
intType_,
class TLabel>
1271template <
class ResultNumber, PolygonalRegionConcept OtherRegion>
1277template <
class Po
intType_,
class TLabel>
1278template <
class ResultNumber, PolygonalRegionConcept OtherRegion>
1306template <
class Po
intType_,
class TLabel>
1307template <
class ResultNumber, HalfplaneIntersectionConcept OtherIntersection>
1310 using ExactNumber = detail::region_exact_number_t<typename OtherIntersection::NumberType>;
1315 if (other.isDegenerate()) {
1318 const auto clipped = detail::regionClippedToBox(other,
bbox());
1319 if (clipped.isDegenerate()) {
1325template <
class Po
intType_,
class TLabel>
1326template <
class ResultNumber, HalfplaneConcept OtherHalfplane>
1332template <
class Po
intType_,
class TLabel>
1333template <
class ResultNumber, HalfplaneIntersectionConcept OtherIntersection>
1336 using ExactNumber = detail::region_exact_number_t<typename OtherIntersection::NumberType>;
1340 if (other.isDegenerate()) {
1343 const auto clipped = detail::regionClippedToBox(other,
bbox());
1344 if (clipped.isDegenerate()) {
1350template <
class Po
intType_,
class TLabel>
1351template <
class ResultNumber, HalfplaneConcept OtherHalfplane>
1357template <
class Po
intType_,
class TLabel>
1358template <
class ResultNumber, HalfplaneIntersectionConcept OtherIntersection>
1364template <
class Po
intType_,
class TLabel>
1365template <
class ResultNumber, HalfplaneConcept OtherHalfplane>
1371template <
class Po
intType_,
class TLabel>
1372template <
class ResultNumber, HalfplaneIntersectionConcept OtherIntersection>
1378template <
class Po
intType_,
class TLabel>
1379template <
class ResultNumber, HalfplaneConcept OtherHalfplane>
1385template <
class Po
intType_,
class TLabel>
1386template <
class ResultNumber, HalfplaneIntersectionConcept OtherIntersection>
1392template <
class Po
intType_,
class TLabel>
1393template <
class ResultNumber, HalfplaneConcept OtherHalfplane>
1399template <
class Po
intType_,
class TLabel>
1400template <
class ResultNumber, HalfplaneIntersectionConcept OtherIntersection>
1406template <
class Po
intType_,
class TLabel>
1407template <
class ResultNumber, HalfplaneConcept OtherHalfplane>
1420template <
class Po
intType_,
class TLabel>
1421template <
class ResultNumber, RectangleConcept OtherRectangle>
1427template <
class Po
intType_,
class TLabel>
1428template <
class ResultNumber, TriangleConcept OtherTriangle>
1434template <
class Po
intType_,
class TLabel>
1435template <
class ResultNumber, RectangleConcept OtherRectangle>
1441template <
class Po
intType_,
class TLabel>
1442template <
class ResultNumber, ConvexConcept OtherConvex>
1448template <
class Po
intType_,
class TLabel>
1449template <
class ResultNumber, TriangleConcept OtherTriangle>
1455template <
class Po
intType_,
class TLabel>
1456template <
class ResultNumber, RectangleConcept OtherRectangle>
1462template <
class Po
intType_,
class TLabel>
1463template <
class ResultNumber, PolygonConcept OtherPolygon>
1466 using ExactNumber = detail::Exact1DNumber<NumberType, typename OtherPolygon::NumberType>;
1474template <
class Po
intType_,
class TLabel>
1475template <
class ResultNumber, ConvexConcept OtherConvex>
1481template <
class Po
intType_,
class TLabel>
1482template <
class ResultNumber, TriangleConcept OtherTriangle>
1488template <
class Po
intType_,
class TLabel>
1489template <
class ResultNumber, RectangleConcept OtherRectangle>
1495template <
class Po
intType_,
class TLabel>
1496template <
class ResultNumber, PolygonWithHolesConcept OtherRegion>
1499 return detail::regularizedUnion<Point<ResultNumber, typename PointType_::LabelType>>(*
this, other);
1502template <
class Po
intType_,
class TLabel>
1503template <
class ResultNumber, PolygonConcept OtherPolygon>
1506 return detail::regularizedUnion<Point<ResultNumber, typename PointType_::LabelType>>(*
this, other);
1509template <
class Po
intType_,
class TLabel>
1510template <
class ResultNumber, ConvexConcept OtherConvex>
1516template <
class Po
intType_,
class TLabel>
1517template <
class ResultNumber, TriangleConcept OtherTriangle>
1523template <
class Po
intType_,
class TLabel>
1524template <
class ResultNumber, RectangleConcept OtherRectangle>
1530template <
class Po
intType_,
class TLabel>
1531template <
class ResultNumber, PolygonWithHolesConcept OtherRegion>
1534 return detail::regularizedUnion<Point<ResultNumber, typename PointType_::LabelType>>(*
this, other);
1542template <
class Po
intType_,
class TLabel>
1543template <
class ResultNumber, RectangleConcept OtherRectangle>
1549template <
class Po
intType_,
class TLabel>
1550template <
class ResultNumber, TriangleConcept OtherTriangle>
1556template <
class Po
intType_,
class TLabel>
1557template <
class ResultNumber, RectangleConcept OtherRectangle>
1563template <
class Po
intType_,
class TLabel>
1564template <
class ResultNumber, ConvexConcept OtherConvex>
1570template <
class Po
intType_,
class TLabel>
1571template <
class ResultNumber, TriangleConcept OtherTriangle>
1577template <
class Po
intType_,
class TLabel>
1578template <
class ResultNumber, RectangleConcept OtherRectangle>
1584template <
class Po
intType_,
class TLabel>
1585template <
class ResultNumber, PolygonConcept OtherPolygon>
1588 return detail::regularizedSymmetricDifference<Point<ResultNumber, typename PointType_::LabelType>>(*
this, other);
1591template <
class Po
intType_,
class TLabel>
1592template <
class ResultNumber, ConvexConcept OtherConvex>
1598template <
class Po
intType_,
class TLabel>
1599template <
class ResultNumber, TriangleConcept OtherTriangle>
1605template <
class Po
intType_,
class TLabel>
1606template <
class ResultNumber, RectangleConcept OtherRectangle>
1612template <
class Po
intType_,
class TLabel>
1613template <
class ResultNumber, PolygonWithHolesConcept OtherRegion>
1616 return detail::regularizedSymmetricDifference<Point<ResultNumber, typename PointType_::LabelType>>(*
this, other);
1619template <
class Po
intType_,
class TLabel>
1620template <
class ResultNumber, PolygonConcept OtherPolygon>
1623 return detail::regularizedSymmetricDifference<Point<ResultNumber, typename PointType_::LabelType>>(*
this, other);
1626template <
class Po
intType_,
class TLabel>
1627template <
class ResultNumber, ConvexConcept OtherConvex>
1633template <
class Po
intType_,
class TLabel>
1634template <
class ResultNumber, TriangleConcept OtherTriangle>
1640template <
class Po
intType_,
class TLabel>
1641template <
class ResultNumber, RectangleConcept OtherRectangle>
1647template <
class Po
intType_,
class TLabel>
1648template <
class ResultNumber, PolygonWithHolesConcept OtherRegion>
1651 return detail::regularizedSymmetricDifference<Point<ResultNumber, typename PointType_::LabelType>>(*
this, other);
1654template <
class Po
intType_,
class TLabel>
1655template <
class ResultNumber, PolygonConcept OtherPolygon>
1658 return detail::regularizedIntersection<Point<ResultNumber, typename PointType_::LabelType>>(*
this, other);
1661template <
class Po
intType_,
class TLabel>
1662template <
class ResultNumber, ConvexConcept OtherConvex>
1668template <
class Po
intType_,
class TLabel>
1669template <
class ResultNumber, TriangleConcept OtherTriangle>
1675template <
class Po
intType_,
class TLabel>
1676template <
class ResultNumber, RectangleConcept OtherRectangle>
1682template <
class Po
intType_,
class TLabel>
1683template <
class ResultNumber, PolygonWithHolesConcept OtherRegion>
1686 return detail::regularizedIntersection<Point<ResultNumber, typename PointType_::LabelType>>(*
this, other);
1689template <
class Po
intType_,
class TLabel>
1690template <
class ResultNumber, HalfplaneIntersectionConcept OtherIntersection>
1693 using ExactNumber = detail::region_exact_number_t<typename OtherIntersection::NumberType>;
1700 const auto clipped = detail::regionClippedToBox(other,
bbox());
1701 if (clipped.isDegenerate()) {
1707template <
class Po
intType_,
class TLabel>
1708template <
class ResultNumber, HalfplaneConcept OtherHalfplane>
1721template <
class Po
intType_,
class TLabel>
1722template <
class ResultNumber, PolygonConcept OtherPolygon>
1723std::vector<std::variant<Point<ResultNumber, typename PointType_::LabelType>,
1727 return detail::literalIntersection<Point<ResultNumber, typename PointType_::LabelType>>(
1731template <
class Po
intType_,
class TLabel>
1732template <
class ResultNumber, ConvexConcept OtherConvex>
1733std::vector<std::variant<Point<ResultNumber, typename PointType_::LabelType>,
1740template <
class Po
intType_,
class TLabel>
1741template <
class ResultNumber, TriangleConcept OtherTriangle>
1742std::vector<std::variant<Point<ResultNumber, typename PointType_::LabelType>,
1749template <
class Po
intType_,
class TLabel>
1750template <
class ResultNumber, RectangleConcept OtherRectangle>
1751std::vector<std::variant<Point<ResultNumber, typename PointType_::LabelType>,
1758template <
class Po
intType_,
class TLabel>
1759template <
class ResultNumber, PolygonWithHolesConcept OtherRegion>
1760std::vector<std::variant<Point<ResultNumber, typename PointType_::LabelType>,
1764 return detail::literalIntersection<Point<ResultNumber, typename PointType_::LabelType>>(
1768template <
class Po
intType_,
class TLabel>
1769template <
class ResultNumber, HalfplaneIntersectionConcept OtherIntersection>
1770std::vector<std::variant<Point<ResultNumber, typename PointType_::LabelType>,
1774 using ExactNumber = detail::region_exact_number_t<typename OtherIntersection::NumberType>;
1775 if (
empty() || other.empty()) {
1782 const auto clipped = detail::regionClippedToBox(other,
bbox());
1783 return detail::literalIntersection<Point<ResultNumber, typename PointType_::LabelType>>(
1784 *
this, clipped.template asConvex<ExactNumber>());
1787template <
class Po
intType_,
class TLabel>
1788template <
class ResultNumber, HalfplaneConcept OtherHalfplane>
1789std::vector<std::variant<Point<ResultNumber, typename PointType_::LabelType>,
1806template <
class Po
intType_,
class TLabel>
1807template <
class ResultNumber, detail::SetBooleanOperandConcept OtherShape>
1810 return detail::regularizedDifference<Point<ResultNumber, typename PointType_::LabelType>>(
1811 *
this, detail::booleanOperand(other));
1814template <
class Po
intType_,
class TLabel>
1815template <
class ResultNumber, detail::SetBooleanOperandConcept OtherShape>
1818 return detail::regularizedUnion<Point<ResultNumber, typename PointType_::LabelType>>(
1819 *
this, detail::booleanOperand(other));
1822template <
class Po
intType_,
class TLabel>
1823template <
class ResultNumber, detail::SetBooleanOperandConcept OtherShape>
1826 return detail::regularizedIntersection<Point<ResultNumber, typename PointType_::LabelType>>(
1827 *
this, detail::booleanOperand(other));
1830template <
class Po
intType_,
class TLabel>
1831template <
class ResultNumber, detail::SetBooleanOperandConcept OtherShape>
1834 return detail::regularizedSymmetricDifference<
1838template <
class Po
intType_,
class TLabel>
1839template <
class ResultNumber, HalfplaneIntersectionConcept OtherIntersection>
1842 using ExactNumber = detail::region_exact_number_t<typename OtherIntersection::NumberType>;
1849 const auto clipped = detail::regionClippedToBox(other,
bbox());
1850 if (clipped.isDegenerate()) {
1856template <
class Po
intType_,
class TLabel>
1857template <
class ResultNumber, HalfplaneConcept OtherHalfplane>
1863template <
class Po
intType_,
class TLabel>
1864template <
class ResultNumber, detail::SetBooleanOperandConcept OtherShape>
1865std::vector<std::variant<Point<ResultNumber, typename PointType_::LabelType>,
1869 return detail::literalIntersection<Point<ResultNumber, typename PointType_::LabelType>>(
1870 *
this, detail::booleanOperand(other));
1873template <
class Po
intType_,
class TLabel>
1874template <
class ResultNumber, HalfplaneIntersectionConcept OtherIntersection>
1875std::vector<std::variant<Point<ResultNumber, typename PointType_::LabelType>,
1879 using ExactNumber = detail::region_exact_number_t<typename OtherIntersection::NumberType>;
1880 if (
empty() || other.empty()) {
1886 const auto clipped = detail::regionClippedToBox(other,
bbox());
1887 return detail::literalIntersection<Point<ResultNumber, typename PointType_::LabelType>>(
1888 *
this, clipped.template asConvex<ExactNumber>());
1891template <
class Po
intType_,
class TLabel>
1892template <
class ResultNumber, HalfplaneConcept OtherHalfplane>
1893std::vector<std::variant<Point<ResultNumber, typename PointType_::LabelType>,
Planar subdivision induced by a set of one-dimensional shapes.
detail::Handle< FaceTag > FaceId
Handle of a face of this arrangement specialization.
Definition arrangement.hpp:185
detail::Handle< VertexTag > VertexId
Handle of a vertex of this arrangement specialization.
Definition arrangement.hpp:181
detail::Handle< HalfedgeTag > HalfedgeId
Handle of a halfedge of this arrangement specialization.
Definition arrangement.hpp:183
Definition arrangement.hpp:67
@ y
Definition intervaltree.hpp:24
@ x
Definition intervaltree.hpp:24
constexpr bool is_Rational_v
Definition rational.hpp:37
@ edge
Definition bitmatrix.hpp:37
@ vertex
Definition bitmatrix.hpp:37
boost::multiprecision::number< boost::multiprecision::cpp_int_backend< 127, 127, boost::multiprecision::signed_magnitude, boost::multiprecision::unchecked, void > > int128
Signed 128-bit integer.
Definition numeric.hpp:64
PolygonSet() -> PolygonSet< Point<>, NoLabel >
Definition polygonset.hpp:1699
typename rational_int< T >::type rational_int_t
Definition rational.hpp:61
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
PolygonWithHoles() -> PolygonWithHoles< Point<>, NoLabel >
Definition polygonwithholes.hpp:3093
Segment() -> Segment< Point<>, NoLabel >
Polyline() -> Polyline< Point<>, NoLabel >
Definition polyline.hpp:2369
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
auto difference(const Shape< OtherPoint > &other) const
Returns the regularized set difference of the two shapes (A ∖ B), re-dispatching through the wrapper'...
Definition convex.hpp:2538
constexpr Polygon< PointType > asPolygon() const
Definition convex.hpp:636
auto symmetricDifference(const Shape< OtherPoint > &other) const
Returns the regularized symmetric difference of the two shapes (A △ B), re-dispatching through the wr...
Definition convex.hpp:2555
auto regularizedUnion(const Shape< OtherPoint > &other) const
Returns the regularized union of the two shapes (A ∪ B), re-dispatching through the wrapper's own reg...
Definition convex.hpp:2519
Two-dimensional point with optional label payload.
Definition point.hpp:129
Set of closed regions with pairwise disjoint interiors.
Definition polygonset.hpp:165
PolygonSet< Point< ResultNumber, typename PointType::LabelType > > symmetricDifference(const OtherShape &other) const
Returns the regularized symmetric difference of the two shapes (A △ B).
PolygonSet< Point< ResultNumber, typename PointType::LabelType > > difference(const OtherShape &other) const
Returns the regularized set difference of the two shapes (A ∖ B).
std::vector< std::variant< Point< ResultNumber, typename PointType::LabelType >, Polyline< Point< ResultNumber, typename PointType::LabelType > >, PolygonWithHoles< Point< ResultNumber, typename PointType::LabelType > > > > intersection(const OtherShape &other) const
Returns the intersection of the two shapes (A ∩ B), empty when they are disjoint.
PolygonSet< Point< ResultNumber, typename PointType::LabelType > > regularizedUnion(const OtherShape &other) const
Returns the regularized union of the two shapes (A ∪ B).
PolygonSet< Point< ResultNumber, typename PointType::LabelType > > regularizedIntersection(const OtherShape &other) const
Returns the regularized intersection of the two shapes (A ∩ B).
constexpr bool empty() const
Definition polygonset.hpp:485
constexpr const ComponentType & component(std::size_t index) const
Definition polygonset.hpp:271
constexpr bool isDegenerate() const
Definition polygonset.hpp:499
PolygonSet< Point< ResultNumber, typename PointType::LabelType > > regularized() const
Returns the set without its slits (closure(A°)).
constexpr const Rectangle< PointType > & bbox() const
Closed region bounded by one outer simple polygon minus disjoint polygonal holes.
Definition polygonwithholes.hpp:89
constexpr const Rectangle< PointType > & bbox() const
Definition polygonwithholes.hpp:1571
PolygonSet< Point< ResultNumber, typename PointType::LabelType > > regularizedUnion(const OtherPolygon &other) const
Returns the regularized union of the two shapes (A ∪ B).
constexpr bool isDegenerate() const
Definition polygonwithholes.hpp:441
PolygonSet< Point< ResultNumber, typename PointType::LabelType > > regularizedIntersection(const OtherPolygon &other) const
Returns the regularized intersection of the two shapes (A ∩ B).
PolygonSet< Point< ResultNumber, typename PointType::LabelType > > symmetricDifference(const OtherPolygon &other) const
Returns the regularized symmetric difference of the two shapes (A △ B).
friend struct PolygonWithHoles
Definition polygonwithholes.hpp:3314
constexpr std::optional< Point< ResultNumber, typename PointType::LabelType > > intersection(const OtherPoint &other) const
Returns the intersection of the two shapes (A ∩ B), empty when they are disjoint.
PolygonSet< Point< ResultNumber, typename PointType::LabelType > > regularized() const
Returns the region without its slits (closure(A°)), as a set of regions.
PolygonSet< Point< ResultNumber, typename PointType::LabelType > > difference(const OtherPolygon &other) const
Returns the regularized set difference of the two shapes (A ∖ B).
constexpr bool empty() const
Definition polygonwithholes.hpp:430
PolygonSet< Point< ResultNumber, typename PointType::LabelType > > difference(const OtherPolygon &other) const
Returns the regularized set difference of the two shapes (A ∖ B).
PolygonSet< Point< ResultNumber, typename PointType::LabelType > > regularizedUnion(const OtherPolygon &other) const
Returns the regularized union of the two shapes (A ∪ B).
PolygonSet< Point< ResultNumber, typename PointType::LabelType > > symmetricDifference(const OtherPolygon &other) const
Returns the regularized symmetric difference of the two shapes (A △ B).
constexpr PolygonWithHoles< PointType > asPolygonWithHoles() const
Definition polygon.hpp:835
Open polygonal chain stored in traversal order; may self-intersect.
Definition polyline.hpp:69
auto symmetricDifference(const Shape< OtherPoint > &other) const
Returns the regularized symmetric difference of the two shapes (A △ B), re-dispatching through the wr...
Definition rectangle.hpp:1673
auto regularizedUnion(const Shape< OtherPoint > &other) const
Returns the regularized union of the two shapes (A ∪ B), re-dispatching through the wrapper's own reg...
Definition rectangle.hpp:1637
auto difference(const Shape< OtherPoint > &other) const
Returns the regularized set difference of the two shapes (A ∖ B), re-dispatching through the wrapper'...
Definition rectangle.hpp:1656
constexpr Polygon< PointType > asPolygon() const
Definition rectangle.hpp:737
auto regularizedUnion(const Shape< OtherPoint > &other) const
Returns the regularized union of the two shapes (A ∪ B), re-dispatching through the wrapper's own reg...
Definition triangle.hpp:1515
auto symmetricDifference(const Shape< OtherPoint > &other) const
Returns the regularized symmetric difference of the two shapes (A △ B), re-dispatching through the wr...
Definition triangle.hpp:1551
constexpr Polygon< PointType > asPolygon() const
Definition triangle.hpp:533
auto difference(const Shape< OtherPoint > &other) const
Returns the regularized set difference of the two shapes (A ∖ B), re-dispatching through the wrapper'...
Definition triangle.hpp:1534