1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-17 14:11:06 +00:00

clipper2: Update to 1.5.3

This commit is contained in:
Jakub Marcowski
2025-06-01 20:37:06 +02:00
parent 64b09905c7
commit 256fa39e51
8 changed files with 238 additions and 165 deletions

View File

@@ -1,8 +1,8 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 17 September 2024 *
* Date : 4 May 2025 *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2024 *
* Copyright : Angus Johnson 2010-2025 *
* Purpose : This is the main polygon clipping module *
* License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
@@ -478,8 +478,7 @@ namespace Clipper2Lib {
inline void SetOwner(OutRec* outrec, OutRec* new_owner)
{
//precondition1: new_owner is never null
while (new_owner->owner && !new_owner->owner->pts)
new_owner->owner = new_owner->owner->owner;
new_owner->owner = GetRealOutRec(new_owner->owner);
OutRec* tmp = new_owner;
while (tmp && tmp != outrec) tmp = tmp->owner;
if (tmp) new_owner->owner = outrec->owner;
@@ -532,9 +531,9 @@ namespace Clipper2Lib {
val = 1 - val; // toggle val
else
{
double d = CrossProduct(op2->prev->pt, op2->pt, pt);
if (d == 0) return PointInPolygonResult::IsOn;
if ((d < 0) == is_above) val = 1 - val;
int i = CrossProductSign(op2->prev->pt, op2->pt, pt);
if (i == 0) return PointInPolygonResult::IsOn;
if ((i < 0) == is_above) val = 1 - val;
}
is_above = !is_above;
op2 = op2->next;
@@ -542,9 +541,9 @@ namespace Clipper2Lib {
if (is_above != starting_above)
{
double d = CrossProduct(op2->prev->pt, op2->pt, pt);
if (d == 0) return PointInPolygonResult::IsOn;
if ((d < 0) == is_above) val = 1 - val;
int i = CrossProductSign(op2->prev->pt, op2->pt, pt);
if (i == 0) return PointInPolygonResult::IsOn;
if ((i < 0) == is_above) val = 1 - val;
}
if (val == 0) return PointInPolygonResult::IsOutside;
@@ -574,30 +573,31 @@ namespace Clipper2Lib {
return result;
}
inline bool Path1InsidePath2(OutPt* op1, OutPt* op2)
inline bool Path2ContainsPath1(OutPt* op1, OutPt* op2)
{
// we need to make some accommodation for rounding errors
// so we won't jump if the first vertex is found outside
PointInPolygonResult result;
int outside_cnt = 0;
// this function accommodates rounding errors that
// can cause path micro intersections
PointInPolygonResult pip = PointInPolygonResult::IsOn;
OutPt* op = op1;
do
{
result = PointInOpPolygon(op->pt, op2);
if (result == PointInPolygonResult::IsOutside) ++outside_cnt;
else if (result == PointInPolygonResult::IsInside) --outside_cnt;
do {
switch (PointInOpPolygon(op->pt, op2))
{
case PointInPolygonResult::IsOutside:
if (pip == PointInPolygonResult::IsOutside) return false;
pip = PointInPolygonResult::IsOutside;
break;
case PointInPolygonResult::IsInside:
if (pip == PointInPolygonResult::IsInside) return true;
pip = PointInPolygonResult::IsInside;
break;
default: break;
}
op = op->next;
} while (op != op1 && std::abs(outside_cnt) < 2);
if (std::abs(outside_cnt) > 1) return (outside_cnt < 0);
// since path1's location is still equivocal, check its midpoint
Point64 mp = GetBounds(GetCleanPath(op1)).MidPoint();
Path64 path2 = GetCleanPath(op2);
return PointInPolygon(mp, path2) != PointInPolygonResult::IsOutside;
} while (op != op1);
// result unclear, so try again using cleaned paths
return Path2ContainsPath1(GetCleanPath(op1), GetCleanPath(op2)); // (#973)
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void AddLocMin(LocalMinimaList& list,
Vertex& vert, PathType polytype, bool is_open)
{
@@ -1122,21 +1122,19 @@ namespace Clipper2Lib {
return newcomer.curr_x > resident.curr_x;
//get the turning direction a1.top, a2.bot, a2.top
double d = CrossProduct(resident.top, newcomer.bot, newcomer.top);
if (d != 0) return d < 0;
int i = CrossProductSign(resident.top, newcomer.bot, newcomer.top);
if (i != 0) return i < 0;
//edges must be collinear to get here
//for starting open paths, place them according to
//the direction they're about to turn
if (!IsMaxima(resident) && (resident.top.y > newcomer.top.y))
{
return CrossProduct(newcomer.bot,
resident.top, NextVertex(resident)->pt) <= 0;
return (CrossProductSign(newcomer.bot, resident.top, NextVertex(resident)->pt) <= 0);
}
else if (!IsMaxima(newcomer) && (newcomer.top.y > resident.top.y))
{
return CrossProduct(newcomer.bot,
newcomer.top, NextVertex(newcomer)->pt) >= 0;
return (CrossProductSign(newcomer.bot, newcomer.top, NextVertex(newcomer)->pt) >= 0);
}
int64_t y = newcomer.bot.y;
@@ -1151,7 +1149,7 @@ namespace Clipper2Lib {
resident.bot, resident.top)) return true;
else
//compare turning direction of the alternate bound
return (CrossProduct(PrevPrevVertex(resident)->pt,
return (CrossProductSign(PrevPrevVertex(resident)->pt,
newcomer.bot, PrevPrevVertex(newcomer)->pt) > 0) == newcomerIsLeft;
}
@@ -1561,7 +1559,7 @@ namespace Clipper2Lib {
FixSelfIntersects(outrec);
}
void ClipperBase::DoSplitOp(OutRec* outrec, OutPt* splitOp)
void ClipperBase::DoSplitOp (OutRec* outrec, OutPt* splitOp)
{
// splitOp.prev -> splitOp &&
// splitOp.next -> splitOp.next.next are intersecting
@@ -1626,7 +1624,7 @@ namespace Clipper2Lib {
if (using_polytree_)
{
if (Path1InsidePath2(prevOp, newOp))
if (Path2ContainsPath1(prevOp, newOp))
{
newOr->splits = new OutRecList();
newOr->splits->emplace_back(outrec);
@@ -1648,19 +1646,32 @@ namespace Clipper2Lib {
void ClipperBase::FixSelfIntersects(OutRec* outrec)
{
OutPt* op2 = outrec->pts;
if (op2->prev == op2->next->next)
return; // because triangles can't self-intersect
for (; ; )
{
// triangles can't self-intersect
if (op2->prev == op2->next->next) break;
if (SegmentsIntersect(op2->prev->pt,
op2->pt, op2->next->pt, op2->next->next->pt))
{
if (op2 == outrec->pts || op2->next == outrec->pts)
outrec->pts = outrec->pts->prev;
DoSplitOp(outrec, op2);
if (!outrec->pts) break;
op2 = outrec->pts;
continue;
if (SegmentsIntersect(op2->prev->pt,
op2->pt, op2->next->next->pt, op2->next->next->next->pt))
{
// adjacent intersections (ie a micro self-intersections)
op2 = DuplicateOp(op2, false);
op2->pt = op2->next->next->next->pt;
op2 = op2->next;
}
else
{
if (op2 == outrec->pts || op2->next == outrec->pts)
outrec->pts = outrec->pts->prev;
DoSplitOp(outrec, op2);
if (!outrec->pts) break;
op2 = outrec->pts;
if (op2->prev == op2->next->next)
break; // again, because triangles can't self-intersect
continue;
}
}
else
op2 = op2->next;
@@ -2258,7 +2269,6 @@ namespace Clipper2Lib {
void MoveSplits(OutRec* fromOr, OutRec* toOr)
{
if (!fromOr->splits) return;
if (!toOr->splits) toOr->splits = new OutRecList();
OutRecList::iterator orIter = fromOr->splits->begin();
for (; orIter != fromOr->splits->end(); ++orIter)
@@ -2266,7 +2276,6 @@ namespace Clipper2Lib {
fromOr->splits->clear();
}
void ClipperBase::ProcessHorzJoins()
{
for (const HorzJoin& j : horz_join_list_)
@@ -2295,8 +2304,8 @@ namespace Clipper2Lib {
}
if (using_polytree_) //#498, #520, #584, D#576, #618
{
if (Path1InsidePath2(or1->pts, or2->pts))
{
if (Path2ContainsPath1(or1->pts, or2->pts))
{
//swap or1's & or2's pts
OutPt* tmp = or1->pts;
@@ -2307,7 +2316,7 @@ namespace Clipper2Lib {
//or2 is now inside or1
or2->owner = or1;
}
else if (Path1InsidePath2(or2->pts, or1->pts))
else if (Path2ContainsPath1(or2->pts, or1->pts))
{
or2->owner = or1;
}
@@ -2320,13 +2329,14 @@ namespace Clipper2Lib {
else
or2->owner = or1;
}
else
else // joining, not splitting
{
or2->pts = nullptr;
if (using_polytree_)
{
SetOwner(or2, or1);
MoveSplits(or2, or1); //#618
if (or2->splits)
MoveSplits(or2, or1); //#618
}
else
or2->owner = or1;
@@ -2932,20 +2942,24 @@ namespace Clipper2Lib {
{
for (auto split : *splits)
{
if (!split->pts && split->splits &&
CheckSplitOwner(outrec, split->splits)) return true; //#942
split = GetRealOutRec(split);
if(!split || split == outrec || split->recursive_split == outrec) continue;
if (!split || split == outrec || split->recursive_split == outrec) continue;
split->recursive_split = outrec; // prevent infinite loops
if (split->splits && CheckSplitOwner(outrec, split->splits))
return true;
else if (CheckBounds(split) &&
IsValidOwner(outrec, split) &&
split->bounds.Contains(outrec->bounds) &&
Path1InsidePath2(outrec->pts, split->pts))
{
outrec->owner = split; //found in split
return true;
}
return true;
if (!CheckBounds(split) || !split->bounds.Contains(outrec->bounds) ||
!Path2ContainsPath1(outrec->pts, split->pts)) continue;
if (!IsValidOwner(outrec, split)) // split is owned by outrec! (#957)
split->owner = outrec->owner;
outrec->owner = split;
return true;
}
return false;
}
@@ -2962,7 +2976,7 @@ namespace Clipper2Lib {
if (outrec->owner->splits && CheckSplitOwner(outrec, outrec->owner->splits)) break;
if (outrec->owner->pts && CheckBounds(outrec->owner) &&
outrec->owner->bounds.Contains(outrec->bounds) &&
Path1InsidePath2(outrec->pts, outrec->owner->pts)) break;
Path2ContainsPath1(outrec->pts, outrec->owner->pts)) break;
outrec->owner = outrec->owner->owner;
}
@@ -3025,6 +3039,7 @@ namespace Clipper2Lib {
{
OutRec* outrec = outrec_list_[i];
if (!outrec || !outrec->pts) continue;
if (outrec->is_open)
{
Path64 path;