You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-04 12:00:25 +00:00
clipper2: Update to 1.5.3
This commit is contained in:
3
thirdparty/README.md
vendored
3
thirdparty/README.md
vendored
@@ -120,7 +120,7 @@ Files extracted from upstream source:
|
||||
## clipper2
|
||||
|
||||
- Upstream: https://github.com/AngusJohnson/Clipper2
|
||||
- Version: 1.5.2 (6901921c4be75126d1de60bfd24bd86a61319fd0, 2025)
|
||||
- Version: 1.5.3 (fa165fe8364b7d0e5d5db2182369b8c82348f4ea, 2025)
|
||||
- License: BSL 1.0
|
||||
|
||||
Files extracted from upstream source:
|
||||
@@ -131,7 +131,6 @@ Files extracted from upstream source:
|
||||
Patches:
|
||||
|
||||
- `0001-disable-exceptions.patch` (GH-80796)
|
||||
- `0002-llvm-disable-int128-math.patch` (GH-95964)
|
||||
|
||||
|
||||
## cvtt
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*******************************************************************************
|
||||
* Author : Angus Johnson *
|
||||
* Date : 12 May 2024 *
|
||||
* Date : 24 March 2025 *
|
||||
* Website : https://www.angusj.com *
|
||||
* Copyright : Angus Johnson 2010-2024 *
|
||||
* Copyright : Angus Johnson 2010-2025 *
|
||||
* Purpose : Core Clipper Library structures and functions *
|
||||
* License : https://www.boost.org/LICENSE_1_0.txt *
|
||||
*******************************************************************************/
|
||||
@@ -250,6 +250,20 @@ namespace Clipper2Lib
|
||||
template <typename T>
|
||||
using Paths = std::vector<Path<T>>;
|
||||
|
||||
template <typename T, typename T2=T>
|
||||
Path<T>& operator<<(Path<T>& poly, const Point<T2>& p)
|
||||
{
|
||||
poly.emplace_back(p);
|
||||
return poly;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Paths<T>& operator<<(Paths<T>& polys, const Path<T>& p)
|
||||
{
|
||||
polys.emplace_back(p);
|
||||
return polys;
|
||||
}
|
||||
|
||||
using Path64 = Path<int64_t>;
|
||||
using PathD = Path<double>;
|
||||
using Paths64 = std::vector< Path64>;
|
||||
@@ -687,56 +701,97 @@ namespace Clipper2Lib
|
||||
return (x > 0) - (x < 0);
|
||||
}
|
||||
|
||||
struct MultiplyUInt64Result
|
||||
struct UInt128Struct
|
||||
{
|
||||
const uint64_t result = 0;
|
||||
const uint64_t carry = 0;
|
||||
const uint64_t lo = 0;
|
||||
const uint64_t hi = 0;
|
||||
|
||||
bool operator==(const MultiplyUInt64Result& other) const
|
||||
bool operator==(const UInt128Struct& other) const
|
||||
{
|
||||
return result == other.result && carry == other.carry;
|
||||
return lo == other.lo && hi == other.hi;
|
||||
};
|
||||
};
|
||||
|
||||
inline MultiplyUInt64Result Multiply(uint64_t a, uint64_t b) // #834, #835
|
||||
inline UInt128Struct Multiply(uint64_t a, uint64_t b) // #834, #835
|
||||
{
|
||||
// note to self - lamba expressions follow
|
||||
const auto lo = [](uint64_t x) { return x & 0xFFFFFFFF; };
|
||||
const auto hi = [](uint64_t x) { return x >> 32; };
|
||||
|
||||
const uint64_t x1 = lo(a) * lo(b);
|
||||
const uint64_t x2 = hi(a) * lo(b) + hi(x1);
|
||||
const uint64_t x3 = lo(a) * hi(b) + lo(x2);
|
||||
const uint64_t result = lo(x3) << 32 | lo(x1);
|
||||
const uint64_t carry = hi(a) * hi(b) + hi(x2) + hi(x3);
|
||||
const uint64_t lobits = lo(x3) << 32 | lo(x1);
|
||||
const uint64_t hibits = hi(a) * hi(b) + hi(x2) + hi(x3);
|
||||
|
||||
return { result, carry };
|
||||
return { lobits, hibits };
|
||||
}
|
||||
|
||||
// returns true if (and only if) a * b == c * d
|
||||
inline bool ProductsAreEqual(int64_t a, int64_t b, int64_t c, int64_t d)
|
||||
{
|
||||
// Work around LLVM issue: https://github.com/llvm/llvm-project/issues/16778
|
||||
// Details: https://github.com/godotengine/godot/pull/95964#issuecomment-2306581804
|
||||
// #if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX
|
||||
// const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b);
|
||||
// const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d);
|
||||
// return ab == cd;
|
||||
// #else
|
||||
#if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX
|
||||
const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b);
|
||||
const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d);
|
||||
return ab == cd;
|
||||
#else
|
||||
// nb: unsigned values needed for calculating overflow carry
|
||||
const auto abs_a = static_cast<uint64_t>(std::abs(a));
|
||||
const auto abs_b = static_cast<uint64_t>(std::abs(b));
|
||||
const auto abs_c = static_cast<uint64_t>(std::abs(c));
|
||||
const auto abs_d = static_cast<uint64_t>(std::abs(d));
|
||||
|
||||
const auto abs_ab = Multiply(abs_a, abs_b);
|
||||
const auto abs_cd = Multiply(abs_c, abs_d);
|
||||
const auto ab = Multiply(abs_a, abs_b);
|
||||
const auto cd = Multiply(abs_c, abs_d);
|
||||
|
||||
// nb: it's important to differentiate 0 values here from other values
|
||||
const auto sign_ab = TriSign(a) * TriSign(b);
|
||||
const auto sign_cd = TriSign(c) * TriSign(d);
|
||||
|
||||
return abs_ab == abs_cd && sign_ab == sign_cd;
|
||||
// #endif
|
||||
return ab == cd && sign_ab == sign_cd;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline int CrossProductSign(const Point<T>& pt1, const Point<T>& pt2, const Point<T>& pt3)
|
||||
{
|
||||
const auto a = pt2.x - pt1.x;
|
||||
const auto b = pt3.y - pt2.y;
|
||||
const auto c = pt2.y - pt1.y;
|
||||
const auto d = pt3.x - pt2.x;
|
||||
|
||||
#if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX
|
||||
const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b);
|
||||
const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d);
|
||||
if (ab > cd) return 1;
|
||||
else if (ab < cd) return -1;
|
||||
else return 0;
|
||||
#else
|
||||
// nb: unsigned values needed for calculating carry into 'hi'
|
||||
const auto abs_a = static_cast<uint64_t>(std::abs(a));
|
||||
const auto abs_b = static_cast<uint64_t>(std::abs(b));
|
||||
const auto abs_c = static_cast<uint64_t>(std::abs(c));
|
||||
const auto abs_d = static_cast<uint64_t>(std::abs(d));
|
||||
|
||||
const auto ab = Multiply(abs_a, abs_b);
|
||||
const auto cd = Multiply(abs_c, abs_d);
|
||||
|
||||
const auto sign_ab = TriSign(a) * TriSign(b);
|
||||
const auto sign_cd = TriSign(c) * TriSign(d);
|
||||
|
||||
if (sign_ab == sign_cd)
|
||||
{
|
||||
int result;
|
||||
if (ab.hi == cd.hi)
|
||||
{
|
||||
if (ab.lo == cd.lo) return 0;
|
||||
result = (ab.lo > cd.lo) ? 1 : -1;
|
||||
}
|
||||
else result = (ab.hi > cd.hi) ? 1 : -1;
|
||||
return (sign_ab > 0) ? result : -result;
|
||||
}
|
||||
return (sign_ab > sign_cd) ? 1 : -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
||||
93
thirdparty/clipper2/include/clipper2/clipper.h
vendored
93
thirdparty/clipper2/include/clipper2/clipper.h
vendored
@@ -1,8 +1,8 @@
|
||||
/*******************************************************************************
|
||||
* Author : Angus Johnson *
|
||||
* Date : 27 April 2024 *
|
||||
* Date : 5 March 2025 *
|
||||
* Website : https://www.angusj.com *
|
||||
* Copyright : Angus Johnson 2010-2024 *
|
||||
* Copyright : Angus Johnson 2010-2025 *
|
||||
* Purpose : This module provides a simple interface to the Clipper Library *
|
||||
* License : https://www.boost.org/LICENSE_1_0.txt *
|
||||
*******************************************************************************/
|
||||
@@ -150,7 +150,7 @@ namespace Clipper2Lib {
|
||||
if (!delta) return paths;
|
||||
if (error_code) return PathsD();
|
||||
const double scale = std::pow(10, precision);
|
||||
ClipperOffset clip_offset(miter_limit, arc_tolerance);
|
||||
ClipperOffset clip_offset(miter_limit, arc_tolerance * scale);
|
||||
clip_offset.AddPaths(ScalePaths<int64_t,double>(paths, scale, error_code), jt, et);
|
||||
if (error_code) return PathsD();
|
||||
Paths64 solution;
|
||||
@@ -351,6 +351,29 @@ namespace Clipper2Lib {
|
||||
#endif
|
||||
}
|
||||
|
||||
inline size_t GetNext(size_t current, size_t high,
|
||||
const std::vector<bool>& flags)
|
||||
{
|
||||
++current;
|
||||
while (current <= high && flags[current]) ++current;
|
||||
if (current <= high) return current;
|
||||
current = 0;
|
||||
while (flags[current]) ++current;
|
||||
return current;
|
||||
}
|
||||
|
||||
inline size_t GetPrior(size_t current, size_t high,
|
||||
const std::vector<bool>& flags)
|
||||
{
|
||||
if (current == 0) current = high;
|
||||
else --current;
|
||||
while (current > 0 && flags[current]) --current;
|
||||
if (!flags[current]) return current;
|
||||
current = high;
|
||||
while (flags[current]) --current;
|
||||
return current;
|
||||
}
|
||||
|
||||
} // end details namespace
|
||||
|
||||
inline std::ostream& operator<< (std::ostream& os, const PolyTree64& pp)
|
||||
@@ -611,29 +634,6 @@ namespace Clipper2Lib {
|
||||
return result;
|
||||
}
|
||||
|
||||
inline size_t GetNext(size_t current, size_t high,
|
||||
const std::vector<bool>& flags)
|
||||
{
|
||||
++current;
|
||||
while (current <= high && flags[current]) ++current;
|
||||
if (current <= high) return current;
|
||||
current = 0;
|
||||
while (flags[current]) ++current;
|
||||
return current;
|
||||
}
|
||||
|
||||
inline size_t GetPrior(size_t current, size_t high,
|
||||
const std::vector<bool>& flags)
|
||||
{
|
||||
if (current == 0) current = high;
|
||||
else --current;
|
||||
while (current > 0 && flags[current]) --current;
|
||||
if (!flags[current]) return current;
|
||||
current = high;
|
||||
while (flags[current]) --current;
|
||||
return current;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline Path<T> SimplifyPath(const Path<T> &path,
|
||||
double epsilon, bool isClosedPath = true)
|
||||
@@ -665,13 +665,13 @@ namespace Clipper2Lib {
|
||||
start = curr;
|
||||
do
|
||||
{
|
||||
curr = GetNext(curr, high, flags);
|
||||
curr = details::GetNext(curr, high, flags);
|
||||
} while (curr != start && distSqr[curr] > epsSqr);
|
||||
if (curr == start) break;
|
||||
}
|
||||
|
||||
prior = GetPrior(curr, high, flags);
|
||||
next = GetNext(curr, high, flags);
|
||||
prior = details::GetPrior(curr, high, flags);
|
||||
next = details::GetNext(curr, high, flags);
|
||||
if (next == prior) break;
|
||||
|
||||
// flag for removal the smaller of adjacent 'distances'
|
||||
@@ -680,14 +680,14 @@ namespace Clipper2Lib {
|
||||
prior2 = prior;
|
||||
prior = curr;
|
||||
curr = next;
|
||||
next = GetNext(next, high, flags);
|
||||
next = details::GetNext(next, high, flags);
|
||||
}
|
||||
else
|
||||
prior2 = GetPrior(prior, high, flags);
|
||||
prior2 = details::GetPrior(prior, high, flags);
|
||||
|
||||
flags[curr] = true;
|
||||
curr = next;
|
||||
next = GetNext(next, high, flags);
|
||||
next = details::GetNext(next, high, flags);
|
||||
|
||||
if (isClosedPath || ((curr != high) && (curr != 0)))
|
||||
distSqr[curr] = PerpendicDistFromLineSqrd(path[curr], path[prior], path[next]);
|
||||
@@ -712,6 +712,35 @@ namespace Clipper2Lib {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline bool Path2ContainsPath1(const Path<T>& path1, const Path<T>& path2)
|
||||
{
|
||||
// precondition: paths must not intersect, except for
|
||||
// transient (and presumed 'micro') path intersections
|
||||
PointInPolygonResult pip = PointInPolygonResult::IsOn;
|
||||
for (const Point<T>& pt : path1)
|
||||
{
|
||||
switch (PointInPolygon(pt, path2))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (pip != PointInPolygonResult::IsInside) return false;
|
||||
// result is likely true but check midpoint
|
||||
Point<T> mp1 = GetBounds(path1).MidPoint();
|
||||
return PointInPolygon(mp1, path2) == PointInPolygonResult::IsInside;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void RDP(const Path<T> path, std::size_t begin,
|
||||
std::size_t end, double epsSqrd, std::vector<bool>& flags)
|
||||
|
||||
@@ -35,7 +35,7 @@ private:
|
||||
class Group {
|
||||
public:
|
||||
Paths64 paths_in;
|
||||
std::optional<size_t> lowest_path_idx{};
|
||||
std::optional<size_t> lowest_path_idx{};
|
||||
bool is_reversed = false;
|
||||
JoinType join_type;
|
||||
EndType end_type;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#ifndef CLIPPER_VERSION_H
|
||||
#define CLIPPER_VERSION_H
|
||||
|
||||
constexpr auto CLIPPER2_VERSION = "1.5.2";
|
||||
constexpr auto CLIPPER2_VERSION = "1.5.3";
|
||||
|
||||
#endif // CLIPPER_VERSION_H
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
diff --git a/thirdparty/clipper2/include/clipper2/clipper.core.h b/thirdparty/clipper2/include/clipper2/clipper.core.h
|
||||
index 110bee4c10..aa003bf032 100644
|
||||
--- a/thirdparty/clipper2/include/clipper2/clipper.core.h
|
||||
+++ b/thirdparty/clipper2/include/clipper2/clipper.core.h
|
||||
@@ -715,11 +715,13 @@ namespace Clipper2Lib
|
||||
// returns true if (and only if) a * b == c * d
|
||||
inline bool ProductsAreEqual(int64_t a, int64_t b, int64_t c, int64_t d)
|
||||
{
|
||||
-#if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX
|
||||
- const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b);
|
||||
- const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d);
|
||||
- return ab == cd;
|
||||
-#else
|
||||
+// Work around LLVM issue: https://github.com/llvm/llvm-project/issues/16778
|
||||
+// Details: https://github.com/godotengine/godot/pull/95964#issuecomment-2306581804
|
||||
+// #if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX
|
||||
+// const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b);
|
||||
+// const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d);
|
||||
+// return ab == cd;
|
||||
+// #else
|
||||
// nb: unsigned values needed for calculating overflow carry
|
||||
const auto abs_a = static_cast<uint64_t>(std::abs(a));
|
||||
const auto abs_b = static_cast<uint64_t>(std::abs(b));
|
||||
@@ -734,7 +736,7 @@ namespace Clipper2Lib
|
||||
const auto sign_cd = TriSign(c) * TriSign(d);
|
||||
|
||||
return abs_ab == abs_cd && sign_ab == sign_cd;
|
||||
-#endif
|
||||
+// #endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
143
thirdparty/clipper2/src/clipper.engine.cpp
vendored
143
thirdparty/clipper2/src/clipper.engine.cpp
vendored
@@ -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;
|
||||
|
||||
29
thirdparty/clipper2/src/clipper.offset.cpp
vendored
29
thirdparty/clipper2/src/clipper.offset.cpp
vendored
@@ -1,6 +1,6 @@
|
||||
/*******************************************************************************
|
||||
* Author : Angus Johnson *
|
||||
* Date : 22 January 2025 *
|
||||
* Date : 4 May 2025 *
|
||||
* Website : https://www.angusj.com *
|
||||
* Copyright : Angus Johnson 2010-2025 *
|
||||
* Purpose : Path Offset (Inflate/Shrink) *
|
||||
@@ -33,22 +33,28 @@ const double arc_const = 0.002; // <-- 1/500
|
||||
// Miscellaneous methods
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
std::optional<size_t> GetLowestClosedPathIdx(const Paths64& paths)
|
||||
void GetLowestClosedPathInfo(const Paths64& paths, std::optional<size_t>& idx, bool& is_neg_area)
|
||||
{
|
||||
std::optional<size_t> result;
|
||||
idx.reset();
|
||||
Point64 botPt = Point64(INT64_MAX, INT64_MIN);
|
||||
for (size_t i = 0; i < paths.size(); ++i)
|
||||
{
|
||||
double a = MAX_DBL;
|
||||
for (const Point64& pt : paths[i])
|
||||
{
|
||||
if ((pt.y < botPt.y) ||
|
||||
if ((pt.y < botPt.y) ||
|
||||
((pt.y == botPt.y) && (pt.x >= botPt.x))) continue;
|
||||
result = i;
|
||||
if (a == MAX_DBL)
|
||||
{
|
||||
a = Area(paths[i]);
|
||||
if (a == 0) break; // invalid closed path, so break from inner loop
|
||||
is_neg_area = a < 0;
|
||||
}
|
||||
idx = i;
|
||||
botPt.x = pt.x;
|
||||
botPt.y = pt.y;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline double Hypot(double x, double y)
|
||||
@@ -141,15 +147,16 @@ ClipperOffset::Group::Group(const Paths64& _paths, JoinType _join_type, EndType
|
||||
|
||||
if (end_type == EndType::Polygon)
|
||||
{
|
||||
lowest_path_idx = GetLowestClosedPathIdx(paths_in);
|
||||
bool is_neg_area;
|
||||
GetLowestClosedPathInfo(paths_in, lowest_path_idx, is_neg_area);
|
||||
// the lowermost path must be an outer path, so if its orientation is negative,
|
||||
// then flag the whole group is 'reversed' (will negate delta etc.)
|
||||
// as this is much more efficient than reversing every path.
|
||||
is_reversed = (lowest_path_idx.has_value()) && Area(paths_in[lowest_path_idx.value()]) < 0;
|
||||
is_reversed = lowest_path_idx.has_value() && is_neg_area;
|
||||
}
|
||||
else
|
||||
{
|
||||
lowest_path_idx = std::nullopt;
|
||||
lowest_path_idx.reset();
|
||||
is_reversed = false;
|
||||
}
|
||||
}
|
||||
@@ -597,10 +604,10 @@ void ClipperOffset::ExecuteInternal(double delta)
|
||||
|
||||
if (!solution->size()) return;
|
||||
|
||||
bool paths_reversed = CheckReverseOrientation();
|
||||
bool paths_reversed = CheckReverseOrientation();
|
||||
//clean up self-intersections ...
|
||||
Clipper64 c;
|
||||
c.PreserveCollinear(false);
|
||||
c.PreserveCollinear(preserve_collinear_);
|
||||
//the solution should retain the orientation of the input
|
||||
c.ReverseSolution(reverse_solution_ != paths_reversed);
|
||||
#ifdef USINGZ
|
||||
|
||||
Reference in New Issue
Block a user