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
|
## clipper2
|
||||||
|
|
||||||
- Upstream: https://github.com/AngusJohnson/Clipper2
|
- Upstream: https://github.com/AngusJohnson/Clipper2
|
||||||
- Version: 1.5.2 (6901921c4be75126d1de60bfd24bd86a61319fd0, 2025)
|
- Version: 1.5.3 (fa165fe8364b7d0e5d5db2182369b8c82348f4ea, 2025)
|
||||||
- License: BSL 1.0
|
- License: BSL 1.0
|
||||||
|
|
||||||
Files extracted from upstream source:
|
Files extracted from upstream source:
|
||||||
@@ -131,7 +131,6 @@ Files extracted from upstream source:
|
|||||||
Patches:
|
Patches:
|
||||||
|
|
||||||
- `0001-disable-exceptions.patch` (GH-80796)
|
- `0001-disable-exceptions.patch` (GH-80796)
|
||||||
- `0002-llvm-disable-int128-math.patch` (GH-95964)
|
|
||||||
|
|
||||||
|
|
||||||
## cvtt
|
## cvtt
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Author : Angus Johnson *
|
* Author : Angus Johnson *
|
||||||
* Date : 12 May 2024 *
|
* Date : 24 March 2025 *
|
||||||
* Website : https://www.angusj.com *
|
* Website : https://www.angusj.com *
|
||||||
* Copyright : Angus Johnson 2010-2024 *
|
* Copyright : Angus Johnson 2010-2025 *
|
||||||
* Purpose : Core Clipper Library structures and functions *
|
* Purpose : Core Clipper Library structures and functions *
|
||||||
* License : https://www.boost.org/LICENSE_1_0.txt *
|
* License : https://www.boost.org/LICENSE_1_0.txt *
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@@ -250,6 +250,20 @@ namespace Clipper2Lib
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
using Paths = std::vector<Path<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 Path64 = Path<int64_t>;
|
||||||
using PathD = Path<double>;
|
using PathD = Path<double>;
|
||||||
using Paths64 = std::vector< Path64>;
|
using Paths64 = std::vector< Path64>;
|
||||||
@@ -687,56 +701,97 @@ namespace Clipper2Lib
|
|||||||
return (x > 0) - (x < 0);
|
return (x > 0) - (x < 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MultiplyUInt64Result
|
struct UInt128Struct
|
||||||
{
|
{
|
||||||
const uint64_t result = 0;
|
const uint64_t lo = 0;
|
||||||
const uint64_t carry = 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 lo = [](uint64_t x) { return x & 0xFFFFFFFF; };
|
||||||
const auto hi = [](uint64_t x) { return x >> 32; };
|
const auto hi = [](uint64_t x) { return x >> 32; };
|
||||||
|
|
||||||
const uint64_t x1 = lo(a) * lo(b);
|
const uint64_t x1 = lo(a) * lo(b);
|
||||||
const uint64_t x2 = hi(a) * lo(b) + hi(x1);
|
const uint64_t x2 = hi(a) * lo(b) + hi(x1);
|
||||||
const uint64_t x3 = lo(a) * hi(b) + lo(x2);
|
const uint64_t x3 = lo(a) * hi(b) + lo(x2);
|
||||||
const uint64_t result = lo(x3) << 32 | lo(x1);
|
const uint64_t lobits = lo(x3) << 32 | lo(x1);
|
||||||
const uint64_t carry = hi(a) * hi(b) + hi(x2) + hi(x3);
|
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
|
// 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)
|
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
|
#if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX
|
||||||
// Details: https://github.com/godotengine/godot/pull/95964#issuecomment-2306581804
|
const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b);
|
||||||
// #if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX
|
const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d);
|
||||||
// const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b);
|
return ab == cd;
|
||||||
// const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d);
|
#else
|
||||||
// return ab == cd;
|
|
||||||
// #else
|
|
||||||
// nb: unsigned values needed for calculating overflow carry
|
// nb: unsigned values needed for calculating overflow carry
|
||||||
const auto abs_a = static_cast<uint64_t>(std::abs(a));
|
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_b = static_cast<uint64_t>(std::abs(b));
|
||||||
const auto abs_c = static_cast<uint64_t>(std::abs(c));
|
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_d = static_cast<uint64_t>(std::abs(d));
|
||||||
|
|
||||||
const auto abs_ab = Multiply(abs_a, abs_b);
|
const auto ab = Multiply(abs_a, abs_b);
|
||||||
const auto abs_cd = Multiply(abs_c, abs_d);
|
const auto cd = Multiply(abs_c, abs_d);
|
||||||
|
|
||||||
// nb: it's important to differentiate 0 values here from other values
|
// nb: it's important to differentiate 0 values here from other values
|
||||||
const auto sign_ab = TriSign(a) * TriSign(b);
|
const auto sign_ab = TriSign(a) * TriSign(b);
|
||||||
const auto sign_cd = TriSign(c) * TriSign(d);
|
const auto sign_cd = TriSign(c) * TriSign(d);
|
||||||
|
|
||||||
return abs_ab == abs_cd && sign_ab == sign_cd;
|
return ab == cd && sign_ab == sign_cd;
|
||||||
// #endif
|
#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>
|
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 *
|
* Author : Angus Johnson *
|
||||||
* Date : 27 April 2024 *
|
* Date : 5 March 2025 *
|
||||||
* Website : https://www.angusj.com *
|
* 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 *
|
* Purpose : This module provides a simple interface to the Clipper Library *
|
||||||
* License : https://www.boost.org/LICENSE_1_0.txt *
|
* License : https://www.boost.org/LICENSE_1_0.txt *
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@@ -150,7 +150,7 @@ namespace Clipper2Lib {
|
|||||||
if (!delta) return paths;
|
if (!delta) return paths;
|
||||||
if (error_code) return PathsD();
|
if (error_code) return PathsD();
|
||||||
const double scale = std::pow(10, precision);
|
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);
|
clip_offset.AddPaths(ScalePaths<int64_t,double>(paths, scale, error_code), jt, et);
|
||||||
if (error_code) return PathsD();
|
if (error_code) return PathsD();
|
||||||
Paths64 solution;
|
Paths64 solution;
|
||||||
@@ -351,6 +351,29 @@ namespace Clipper2Lib {
|
|||||||
#endif
|
#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
|
} // end details namespace
|
||||||
|
|
||||||
inline std::ostream& operator<< (std::ostream& os, const PolyTree64& pp)
|
inline std::ostream& operator<< (std::ostream& os, const PolyTree64& pp)
|
||||||
@@ -611,29 +634,6 @@ namespace Clipper2Lib {
|
|||||||
return result;
|
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>
|
template <typename T>
|
||||||
inline Path<T> SimplifyPath(const Path<T> &path,
|
inline Path<T> SimplifyPath(const Path<T> &path,
|
||||||
double epsilon, bool isClosedPath = true)
|
double epsilon, bool isClosedPath = true)
|
||||||
@@ -665,13 +665,13 @@ namespace Clipper2Lib {
|
|||||||
start = curr;
|
start = curr;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
curr = GetNext(curr, high, flags);
|
curr = details::GetNext(curr, high, flags);
|
||||||
} while (curr != start && distSqr[curr] > epsSqr);
|
} while (curr != start && distSqr[curr] > epsSqr);
|
||||||
if (curr == start) break;
|
if (curr == start) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
prior = GetPrior(curr, high, flags);
|
prior = details::GetPrior(curr, high, flags);
|
||||||
next = GetNext(curr, high, flags);
|
next = details::GetNext(curr, high, flags);
|
||||||
if (next == prior) break;
|
if (next == prior) break;
|
||||||
|
|
||||||
// flag for removal the smaller of adjacent 'distances'
|
// flag for removal the smaller of adjacent 'distances'
|
||||||
@@ -680,14 +680,14 @@ namespace Clipper2Lib {
|
|||||||
prior2 = prior;
|
prior2 = prior;
|
||||||
prior = curr;
|
prior = curr;
|
||||||
curr = next;
|
curr = next;
|
||||||
next = GetNext(next, high, flags);
|
next = details::GetNext(next, high, flags);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
prior2 = GetPrior(prior, high, flags);
|
prior2 = details::GetPrior(prior, high, flags);
|
||||||
|
|
||||||
flags[curr] = true;
|
flags[curr] = true;
|
||||||
curr = next;
|
curr = next;
|
||||||
next = GetNext(next, high, flags);
|
next = details::GetNext(next, high, flags);
|
||||||
|
|
||||||
if (isClosedPath || ((curr != high) && (curr != 0)))
|
if (isClosedPath || ((curr != high) && (curr != 0)))
|
||||||
distSqr[curr] = PerpendicDistFromLineSqrd(path[curr], path[prior], path[next]);
|
distSqr[curr] = PerpendicDistFromLineSqrd(path[curr], path[prior], path[next]);
|
||||||
@@ -712,6 +712,35 @@ namespace Clipper2Lib {
|
|||||||
return result;
|
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>
|
template <typename T>
|
||||||
inline void RDP(const Path<T> path, std::size_t begin,
|
inline void RDP(const Path<T> path, std::size_t begin,
|
||||||
std::size_t end, double epsSqrd, std::vector<bool>& flags)
|
std::size_t end, double epsSqrd, std::vector<bool>& flags)
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ private:
|
|||||||
class Group {
|
class Group {
|
||||||
public:
|
public:
|
||||||
Paths64 paths_in;
|
Paths64 paths_in;
|
||||||
std::optional<size_t> lowest_path_idx{};
|
std::optional<size_t> lowest_path_idx{};
|
||||||
bool is_reversed = false;
|
bool is_reversed = false;
|
||||||
JoinType join_type;
|
JoinType join_type;
|
||||||
EndType end_type;
|
EndType end_type;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#ifndef CLIPPER_VERSION_H
|
#ifndef CLIPPER_VERSION_H
|
||||||
#define 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
|
#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 *
|
* Author : Angus Johnson *
|
||||||
* Date : 17 September 2024 *
|
* Date : 4 May 2025 *
|
||||||
* Website : https://www.angusj.com *
|
* Website : https://www.angusj.com *
|
||||||
* Copyright : Angus Johnson 2010-2024 *
|
* Copyright : Angus Johnson 2010-2025 *
|
||||||
* Purpose : This is the main polygon clipping module *
|
* Purpose : This is the main polygon clipping module *
|
||||||
* License : https://www.boost.org/LICENSE_1_0.txt *
|
* License : https://www.boost.org/LICENSE_1_0.txt *
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@@ -478,8 +478,7 @@ namespace Clipper2Lib {
|
|||||||
inline void SetOwner(OutRec* outrec, OutRec* new_owner)
|
inline void SetOwner(OutRec* outrec, OutRec* new_owner)
|
||||||
{
|
{
|
||||||
//precondition1: new_owner is never null
|
//precondition1: new_owner is never null
|
||||||
while (new_owner->owner && !new_owner->owner->pts)
|
new_owner->owner = GetRealOutRec(new_owner->owner);
|
||||||
new_owner->owner = new_owner->owner->owner;
|
|
||||||
OutRec* tmp = new_owner;
|
OutRec* tmp = new_owner;
|
||||||
while (tmp && tmp != outrec) tmp = tmp->owner;
|
while (tmp && tmp != outrec) tmp = tmp->owner;
|
||||||
if (tmp) new_owner->owner = outrec->owner;
|
if (tmp) new_owner->owner = outrec->owner;
|
||||||
@@ -532,9 +531,9 @@ namespace Clipper2Lib {
|
|||||||
val = 1 - val; // toggle val
|
val = 1 - val; // toggle val
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
double d = CrossProduct(op2->prev->pt, op2->pt, pt);
|
int i = CrossProductSign(op2->prev->pt, op2->pt, pt);
|
||||||
if (d == 0) return PointInPolygonResult::IsOn;
|
if (i == 0) return PointInPolygonResult::IsOn;
|
||||||
if ((d < 0) == is_above) val = 1 - val;
|
if ((i < 0) == is_above) val = 1 - val;
|
||||||
}
|
}
|
||||||
is_above = !is_above;
|
is_above = !is_above;
|
||||||
op2 = op2->next;
|
op2 = op2->next;
|
||||||
@@ -542,9 +541,9 @@ namespace Clipper2Lib {
|
|||||||
|
|
||||||
if (is_above != starting_above)
|
if (is_above != starting_above)
|
||||||
{
|
{
|
||||||
double d = CrossProduct(op2->prev->pt, op2->pt, pt);
|
int i = CrossProductSign(op2->prev->pt, op2->pt, pt);
|
||||||
if (d == 0) return PointInPolygonResult::IsOn;
|
if (i == 0) return PointInPolygonResult::IsOn;
|
||||||
if ((d < 0) == is_above) val = 1 - val;
|
if ((i < 0) == is_above) val = 1 - val;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (val == 0) return PointInPolygonResult::IsOutside;
|
if (val == 0) return PointInPolygonResult::IsOutside;
|
||||||
@@ -574,30 +573,31 @@ namespace Clipper2Lib {
|
|||||||
return result;
|
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
|
// this function accommodates rounding errors that
|
||||||
// so we won't jump if the first vertex is found outside
|
// can cause path micro intersections
|
||||||
PointInPolygonResult result;
|
PointInPolygonResult pip = PointInPolygonResult::IsOn;
|
||||||
int outside_cnt = 0;
|
|
||||||
OutPt* op = op1;
|
OutPt* op = op1;
|
||||||
do
|
do {
|
||||||
{
|
switch (PointInOpPolygon(op->pt, op2))
|
||||||
result = PointInOpPolygon(op->pt, op2);
|
{
|
||||||
if (result == PointInPolygonResult::IsOutside) ++outside_cnt;
|
case PointInPolygonResult::IsOutside:
|
||||||
else if (result == PointInPolygonResult::IsInside) --outside_cnt;
|
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;
|
op = op->next;
|
||||||
} while (op != op1 && std::abs(outside_cnt) < 2);
|
} while (op != op1);
|
||||||
if (std::abs(outside_cnt) > 1) return (outside_cnt < 0);
|
// result unclear, so try again using cleaned paths
|
||||||
// since path1's location is still equivocal, check its midpoint
|
return Path2ContainsPath1(GetCleanPath(op1), GetCleanPath(op2)); // (#973)
|
||||||
Point64 mp = GetBounds(GetCleanPath(op1)).MidPoint();
|
|
||||||
Path64 path2 = GetCleanPath(op2);
|
|
||||||
return PointInPolygon(mp, path2) != PointInPolygonResult::IsOutside;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
void AddLocMin(LocalMinimaList& list,
|
void AddLocMin(LocalMinimaList& list,
|
||||||
Vertex& vert, PathType polytype, bool is_open)
|
Vertex& vert, PathType polytype, bool is_open)
|
||||||
{
|
{
|
||||||
@@ -1122,21 +1122,19 @@ namespace Clipper2Lib {
|
|||||||
return newcomer.curr_x > resident.curr_x;
|
return newcomer.curr_x > resident.curr_x;
|
||||||
|
|
||||||
//get the turning direction a1.top, a2.bot, a2.top
|
//get the turning direction a1.top, a2.bot, a2.top
|
||||||
double d = CrossProduct(resident.top, newcomer.bot, newcomer.top);
|
int i = CrossProductSign(resident.top, newcomer.bot, newcomer.top);
|
||||||
if (d != 0) return d < 0;
|
if (i != 0) return i < 0;
|
||||||
|
|
||||||
//edges must be collinear to get here
|
//edges must be collinear to get here
|
||||||
//for starting open paths, place them according to
|
//for starting open paths, place them according to
|
||||||
//the direction they're about to turn
|
//the direction they're about to turn
|
||||||
if (!IsMaxima(resident) && (resident.top.y > newcomer.top.y))
|
if (!IsMaxima(resident) && (resident.top.y > newcomer.top.y))
|
||||||
{
|
{
|
||||||
return CrossProduct(newcomer.bot,
|
return (CrossProductSign(newcomer.bot, resident.top, NextVertex(resident)->pt) <= 0);
|
||||||
resident.top, NextVertex(resident)->pt) <= 0;
|
|
||||||
}
|
}
|
||||||
else if (!IsMaxima(newcomer) && (newcomer.top.y > resident.top.y))
|
else if (!IsMaxima(newcomer) && (newcomer.top.y > resident.top.y))
|
||||||
{
|
{
|
||||||
return CrossProduct(newcomer.bot,
|
return (CrossProductSign(newcomer.bot, newcomer.top, NextVertex(newcomer)->pt) >= 0);
|
||||||
newcomer.top, NextVertex(newcomer)->pt) >= 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t y = newcomer.bot.y;
|
int64_t y = newcomer.bot.y;
|
||||||
@@ -1151,7 +1149,7 @@ namespace Clipper2Lib {
|
|||||||
resident.bot, resident.top)) return true;
|
resident.bot, resident.top)) return true;
|
||||||
else
|
else
|
||||||
//compare turning direction of the alternate bound
|
//compare turning direction of the alternate bound
|
||||||
return (CrossProduct(PrevPrevVertex(resident)->pt,
|
return (CrossProductSign(PrevPrevVertex(resident)->pt,
|
||||||
newcomer.bot, PrevPrevVertex(newcomer)->pt) > 0) == newcomerIsLeft;
|
newcomer.bot, PrevPrevVertex(newcomer)->pt) > 0) == newcomerIsLeft;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1561,7 +1559,7 @@ namespace Clipper2Lib {
|
|||||||
FixSelfIntersects(outrec);
|
FixSelfIntersects(outrec);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClipperBase::DoSplitOp(OutRec* outrec, OutPt* splitOp)
|
void ClipperBase::DoSplitOp (OutRec* outrec, OutPt* splitOp)
|
||||||
{
|
{
|
||||||
// splitOp.prev -> splitOp &&
|
// splitOp.prev -> splitOp &&
|
||||||
// splitOp.next -> splitOp.next.next are intersecting
|
// splitOp.next -> splitOp.next.next are intersecting
|
||||||
@@ -1626,7 +1624,7 @@ namespace Clipper2Lib {
|
|||||||
|
|
||||||
if (using_polytree_)
|
if (using_polytree_)
|
||||||
{
|
{
|
||||||
if (Path1InsidePath2(prevOp, newOp))
|
if (Path2ContainsPath1(prevOp, newOp))
|
||||||
{
|
{
|
||||||
newOr->splits = new OutRecList();
|
newOr->splits = new OutRecList();
|
||||||
newOr->splits->emplace_back(outrec);
|
newOr->splits->emplace_back(outrec);
|
||||||
@@ -1648,19 +1646,32 @@ namespace Clipper2Lib {
|
|||||||
void ClipperBase::FixSelfIntersects(OutRec* outrec)
|
void ClipperBase::FixSelfIntersects(OutRec* outrec)
|
||||||
{
|
{
|
||||||
OutPt* op2 = outrec->pts;
|
OutPt* op2 = outrec->pts;
|
||||||
|
if (op2->prev == op2->next->next)
|
||||||
|
return; // because triangles can't self-intersect
|
||||||
for (; ; )
|
for (; ; )
|
||||||
{
|
{
|
||||||
// triangles can't self-intersect
|
|
||||||
if (op2->prev == op2->next->next) break;
|
|
||||||
if (SegmentsIntersect(op2->prev->pt,
|
if (SegmentsIntersect(op2->prev->pt,
|
||||||
op2->pt, op2->next->pt, op2->next->next->pt))
|
op2->pt, op2->next->pt, op2->next->next->pt))
|
||||||
{
|
{
|
||||||
if (op2 == outrec->pts || op2->next == outrec->pts)
|
if (SegmentsIntersect(op2->prev->pt,
|
||||||
outrec->pts = outrec->pts->prev;
|
op2->pt, op2->next->next->pt, op2->next->next->next->pt))
|
||||||
DoSplitOp(outrec, op2);
|
{
|
||||||
if (!outrec->pts) break;
|
// adjacent intersections (ie a micro self-intersections)
|
||||||
op2 = outrec->pts;
|
op2 = DuplicateOp(op2, false);
|
||||||
continue;
|
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
|
else
|
||||||
op2 = op2->next;
|
op2 = op2->next;
|
||||||
@@ -2258,7 +2269,6 @@ namespace Clipper2Lib {
|
|||||||
|
|
||||||
void MoveSplits(OutRec* fromOr, OutRec* toOr)
|
void MoveSplits(OutRec* fromOr, OutRec* toOr)
|
||||||
{
|
{
|
||||||
if (!fromOr->splits) return;
|
|
||||||
if (!toOr->splits) toOr->splits = new OutRecList();
|
if (!toOr->splits) toOr->splits = new OutRecList();
|
||||||
OutRecList::iterator orIter = fromOr->splits->begin();
|
OutRecList::iterator orIter = fromOr->splits->begin();
|
||||||
for (; orIter != fromOr->splits->end(); ++orIter)
|
for (; orIter != fromOr->splits->end(); ++orIter)
|
||||||
@@ -2266,7 +2276,6 @@ namespace Clipper2Lib {
|
|||||||
fromOr->splits->clear();
|
fromOr->splits->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ClipperBase::ProcessHorzJoins()
|
void ClipperBase::ProcessHorzJoins()
|
||||||
{
|
{
|
||||||
for (const HorzJoin& j : horz_join_list_)
|
for (const HorzJoin& j : horz_join_list_)
|
||||||
@@ -2295,8 +2304,8 @@ namespace Clipper2Lib {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (using_polytree_) //#498, #520, #584, D#576, #618
|
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
|
//swap or1's & or2's pts
|
||||||
OutPt* tmp = or1->pts;
|
OutPt* tmp = or1->pts;
|
||||||
@@ -2307,7 +2316,7 @@ namespace Clipper2Lib {
|
|||||||
//or2 is now inside or1
|
//or2 is now inside or1
|
||||||
or2->owner = or1;
|
or2->owner = or1;
|
||||||
}
|
}
|
||||||
else if (Path1InsidePath2(or2->pts, or1->pts))
|
else if (Path2ContainsPath1(or2->pts, or1->pts))
|
||||||
{
|
{
|
||||||
or2->owner = or1;
|
or2->owner = or1;
|
||||||
}
|
}
|
||||||
@@ -2320,13 +2329,14 @@ namespace Clipper2Lib {
|
|||||||
else
|
else
|
||||||
or2->owner = or1;
|
or2->owner = or1;
|
||||||
}
|
}
|
||||||
else
|
else // joining, not splitting
|
||||||
{
|
{
|
||||||
or2->pts = nullptr;
|
or2->pts = nullptr;
|
||||||
if (using_polytree_)
|
if (using_polytree_)
|
||||||
{
|
{
|
||||||
SetOwner(or2, or1);
|
SetOwner(or2, or1);
|
||||||
MoveSplits(or2, or1); //#618
|
if (or2->splits)
|
||||||
|
MoveSplits(or2, or1); //#618
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
or2->owner = or1;
|
or2->owner = or1;
|
||||||
@@ -2932,20 +2942,24 @@ namespace Clipper2Lib {
|
|||||||
{
|
{
|
||||||
for (auto split : *splits)
|
for (auto split : *splits)
|
||||||
{
|
{
|
||||||
|
if (!split->pts && split->splits &&
|
||||||
|
CheckSplitOwner(outrec, split->splits)) return true; //#942
|
||||||
split = GetRealOutRec(split);
|
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
|
split->recursive_split = outrec; // prevent infinite loops
|
||||||
|
|
||||||
if (split->splits && CheckSplitOwner(outrec, split->splits))
|
if (split->splits && CheckSplitOwner(outrec, split->splits))
|
||||||
return true;
|
return true;
|
||||||
else if (CheckBounds(split) &&
|
|
||||||
IsValidOwner(outrec, split) &&
|
if (!CheckBounds(split) || !split->bounds.Contains(outrec->bounds) ||
|
||||||
split->bounds.Contains(outrec->bounds) &&
|
!Path2ContainsPath1(outrec->pts, split->pts)) continue;
|
||||||
Path1InsidePath2(outrec->pts, split->pts))
|
|
||||||
{
|
if (!IsValidOwner(outrec, split)) // split is owned by outrec! (#957)
|
||||||
outrec->owner = split; //found in split
|
split->owner = outrec->owner;
|
||||||
return true;
|
|
||||||
}
|
outrec->owner = split;
|
||||||
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2962,7 +2976,7 @@ namespace Clipper2Lib {
|
|||||||
if (outrec->owner->splits && CheckSplitOwner(outrec, outrec->owner->splits)) break;
|
if (outrec->owner->splits && CheckSplitOwner(outrec, outrec->owner->splits)) break;
|
||||||
if (outrec->owner->pts && CheckBounds(outrec->owner) &&
|
if (outrec->owner->pts && CheckBounds(outrec->owner) &&
|
||||||
outrec->owner->bounds.Contains(outrec->bounds) &&
|
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;
|
outrec->owner = outrec->owner->owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3025,6 +3039,7 @@ namespace Clipper2Lib {
|
|||||||
{
|
{
|
||||||
OutRec* outrec = outrec_list_[i];
|
OutRec* outrec = outrec_list_[i];
|
||||||
if (!outrec || !outrec->pts) continue;
|
if (!outrec || !outrec->pts) continue;
|
||||||
|
|
||||||
if (outrec->is_open)
|
if (outrec->is_open)
|
||||||
{
|
{
|
||||||
Path64 path;
|
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 *
|
* Author : Angus Johnson *
|
||||||
* Date : 22 January 2025 *
|
* Date : 4 May 2025 *
|
||||||
* Website : https://www.angusj.com *
|
* Website : https://www.angusj.com *
|
||||||
* Copyright : Angus Johnson 2010-2025 *
|
* Copyright : Angus Johnson 2010-2025 *
|
||||||
* Purpose : Path Offset (Inflate/Shrink) *
|
* Purpose : Path Offset (Inflate/Shrink) *
|
||||||
@@ -33,22 +33,28 @@ const double arc_const = 0.002; // <-- 1/500
|
|||||||
// Miscellaneous methods
|
// 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);
|
Point64 botPt = Point64(INT64_MAX, INT64_MIN);
|
||||||
for (size_t i = 0; i < paths.size(); ++i)
|
for (size_t i = 0; i < paths.size(); ++i)
|
||||||
{
|
{
|
||||||
|
double a = MAX_DBL;
|
||||||
for (const Point64& pt : paths[i])
|
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;
|
((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.x = pt.x;
|
||||||
botPt.y = pt.y;
|
botPt.y = pt.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline double Hypot(double x, double y)
|
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)
|
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,
|
// 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.)
|
// then flag the whole group is 'reversed' (will negate delta etc.)
|
||||||
// as this is much more efficient than reversing every path.
|
// 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
|
else
|
||||||
{
|
{
|
||||||
lowest_path_idx = std::nullopt;
|
lowest_path_idx.reset();
|
||||||
is_reversed = false;
|
is_reversed = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -597,10 +604,10 @@ void ClipperOffset::ExecuteInternal(double delta)
|
|||||||
|
|
||||||
if (!solution->size()) return;
|
if (!solution->size()) return;
|
||||||
|
|
||||||
bool paths_reversed = CheckReverseOrientation();
|
bool paths_reversed = CheckReverseOrientation();
|
||||||
//clean up self-intersections ...
|
//clean up self-intersections ...
|
||||||
Clipper64 c;
|
Clipper64 c;
|
||||||
c.PreserveCollinear(false);
|
c.PreserveCollinear(preserve_collinear_);
|
||||||
//the solution should retain the orientation of the input
|
//the solution should retain the orientation of the input
|
||||||
c.ReverseSolution(reverse_solution_ != paths_reversed);
|
c.ReverseSolution(reverse_solution_ != paths_reversed);
|
||||||
#ifdef USINGZ
|
#ifdef USINGZ
|
||||||
|
|||||||
Reference in New Issue
Block a user