mirror of https://github.com/axmolengine/axmol.git
Clipper2 1.1.1 (#1039)
This is a minor update since ver Clipper2 1.1.0 Fixes a clipping bug introduced in the previous release (Issue #381).
This commit is contained in:
parent
c87d5ccbb2
commit
09b32e26d5
|
@ -37,7 +37,7 @@
|
|||
|
||||
## Clipper2
|
||||
- [![Upstream](https://img.shields.io/github/v/tag/AngusJohnson/Clipper2?label=Upstream)](https://github.com/AngusJohnson/Clipper2)
|
||||
- Version: 1.1.0
|
||||
- Version: 1.1.1
|
||||
- License: BSL-1.0
|
||||
|
||||
## ConvertUTF
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
* Author : Angus Johnson *
|
||||
* Date : 14 January 2023 *
|
||||
* Date : 26 January 2023 *
|
||||
* Website : http://www.angusj.com *
|
||||
* Copyright : Angus Johnson 2010-2023 *
|
||||
* Purpose : Core Clipper Library structures and functions *
|
||||
|
@ -20,9 +20,23 @@
|
|||
|
||||
namespace Clipper2Lib
|
||||
{
|
||||
|
||||
#ifdef __cpp_exceptions
|
||||
|
||||
class Clipper2Exception : public std::exception {
|
||||
public:
|
||||
explicit Clipper2Exception(const char* description) :
|
||||
m_descr(description) {}
|
||||
virtual const char* what() const throw() { return m_descr.c_str(); }
|
||||
private:
|
||||
std::string m_descr;
|
||||
};
|
||||
|
||||
static const char* precision_error =
|
||||
"Precision exceeds the permitted range";
|
||||
static const char* range_error =
|
||||
"Values exceed permitted range";
|
||||
|
||||
#endif
|
||||
|
||||
static const double PI = 3.141592653589793238;
|
||||
|
@ -30,6 +44,9 @@ namespace Clipper2Lib
|
|||
static const int64_t MIN_COORD = -MAX_COORD;
|
||||
static const int64_t INVALID = INT64_MAX;
|
||||
|
||||
static const double MAX_DBL = (std::numeric_limits<double>::max)();
|
||||
static const double MIN_DBL = (std::numeric_limits<double>::min)();
|
||||
|
||||
//By far the most widely used filling rules for polygons are EvenOdd
|
||||
//and NonZero, sometimes called Alternate and Winding respectively.
|
||||
//https://en.wikipedia.org/wiki/Nonzero-rule
|
||||
|
@ -170,6 +187,177 @@ namespace Clipper2Lib
|
|||
using Paths64 = std::vector< Path64>;
|
||||
using PathsD = std::vector< PathD>;
|
||||
|
||||
// Rect ------------------------------------------------------------------------
|
||||
|
||||
template <typename T>
|
||||
struct Rect;
|
||||
|
||||
using Rect64 = Rect<int64_t>;
|
||||
using RectD = Rect<double>;
|
||||
|
||||
template <typename T>
|
||||
struct Rect {
|
||||
T left;
|
||||
T top;
|
||||
T right;
|
||||
T bottom;
|
||||
|
||||
Rect() :
|
||||
left(0),
|
||||
top(0),
|
||||
right(0),
|
||||
bottom(0) {}
|
||||
|
||||
Rect(T l, T t, T r, T b) :
|
||||
left(l),
|
||||
top(t),
|
||||
right(r),
|
||||
bottom(b) {}
|
||||
|
||||
|
||||
T Width() const { return right - left; }
|
||||
T Height() const { return bottom - top; }
|
||||
void Width(T width) { right = left + width; }
|
||||
void Height(T height) { bottom = top + height; }
|
||||
|
||||
Point<T> MidPoint() const
|
||||
{
|
||||
return Point<T>((left + right) / 2, (top + bottom) / 2);
|
||||
}
|
||||
|
||||
Path<T> AsPath() const
|
||||
{
|
||||
Path<T> result;
|
||||
result.reserve(4);
|
||||
result.push_back(Point<T>(left, top));
|
||||
result.push_back(Point<T>(right, top));
|
||||
result.push_back(Point<T>(right, bottom));
|
||||
result.push_back(Point<T>(left, bottom));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Contains(const Point<T>& pt) const
|
||||
{
|
||||
return pt.x > left && pt.x < right&& pt.y > top && pt.y < bottom;
|
||||
}
|
||||
|
||||
bool Contains(const Rect<T>& rec) const
|
||||
{
|
||||
return rec.left >= left && rec.right <= right &&
|
||||
rec.top >= top && rec.bottom <= bottom;
|
||||
}
|
||||
|
||||
void Scale(double scale) {
|
||||
left *= scale;
|
||||
top *= scale;
|
||||
right *= scale;
|
||||
bottom *= scale;
|
||||
}
|
||||
|
||||
bool IsEmpty() const { return bottom <= top || right <= left; };
|
||||
|
||||
bool Intersects(const Rect<T>& rec) const
|
||||
{
|
||||
return ((std::max)(left, rec.left) < (std::min)(right, rec.right)) &&
|
||||
((std::max)(top, rec.top) < (std::min)(bottom, rec.bottom));
|
||||
};
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const Rect<T>& rect) {
|
||||
os << "("
|
||||
<< rect.left << "," << rect.top << "," << rect.right << "," << rect.bottom
|
||||
<< ")";
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T1, typename T2>
|
||||
inline Rect<T1> ScaleRect(const Rect<T2>& rect, double scale)
|
||||
{
|
||||
Rect<T1> result;
|
||||
|
||||
if constexpr (std::numeric_limits<T1>::is_integer &&
|
||||
!std::numeric_limits<T2>::is_integer)
|
||||
{
|
||||
result.left = static_cast<T1>(std::round(rect.left * scale));
|
||||
result.top = static_cast<T1>(std::round(rect.top * scale));
|
||||
result.right = static_cast<T1>(std::round(rect.right * scale));
|
||||
result.bottom = static_cast<T1>(std::round(rect.bottom * scale));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.left = rect.left * scale;
|
||||
result.top = rect.top * scale;
|
||||
result.right = rect.right * scale;
|
||||
result.bottom = rect.bottom * scale;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static const Rect64 MaxInvalidRect64 = Rect64(
|
||||
INT64_MAX, INT64_MAX, INT64_MIN, INT64_MIN);
|
||||
|
||||
static const RectD MaxInvalidRectD = RectD(
|
||||
MAX_DBL, MAX_DBL, MIN_DBL, MIN_DBL);
|
||||
|
||||
inline Rect64 Bounds(const Path64& path)
|
||||
{
|
||||
Rect64 rec = MaxInvalidRect64;
|
||||
for (const Point64& pt : path)
|
||||
{
|
||||
if (pt.x < rec.left) rec.left = pt.x;
|
||||
if (pt.x > rec.right) rec.right = pt.x;
|
||||
if (pt.y < rec.top) rec.top = pt.y;
|
||||
if (pt.y > rec.bottom) rec.bottom = pt.y;
|
||||
}
|
||||
if (rec.IsEmpty()) return Rect64();
|
||||
return rec;
|
||||
}
|
||||
|
||||
inline Rect64 Bounds(const Paths64& paths)
|
||||
{
|
||||
Rect64 rec = MaxInvalidRect64;
|
||||
for (const Path64& path : paths)
|
||||
for (const Point64& pt : path)
|
||||
{
|
||||
if (pt.x < rec.left) rec.left = pt.x;
|
||||
if (pt.x > rec.right) rec.right = pt.x;
|
||||
if (pt.y < rec.top) rec.top = pt.y;
|
||||
if (pt.y > rec.bottom) rec.bottom = pt.y;
|
||||
}
|
||||
if (rec.IsEmpty()) return Rect64();
|
||||
return rec;
|
||||
}
|
||||
|
||||
inline RectD Bounds(const PathD& path)
|
||||
{
|
||||
RectD rec = MaxInvalidRectD;
|
||||
for (const PointD& pt : path)
|
||||
{
|
||||
if (pt.x < rec.left) rec.left = pt.x;
|
||||
if (pt.x > rec.right) rec.right = pt.x;
|
||||
if (pt.y < rec.top) rec.top = pt.y;
|
||||
if (pt.y > rec.bottom) rec.bottom = pt.y;
|
||||
}
|
||||
if (rec.IsEmpty()) return RectD();
|
||||
return rec;
|
||||
}
|
||||
|
||||
inline RectD Bounds(const PathsD& paths)
|
||||
{
|
||||
RectD rec = MaxInvalidRectD;
|
||||
for (const PathD& path : paths)
|
||||
for (const PointD& pt : path)
|
||||
{
|
||||
if (pt.x < rec.left) rec.left = pt.x;
|
||||
if (pt.x > rec.right) rec.right = pt.x;
|
||||
if (pt.y < rec.top) rec.top = pt.y;
|
||||
if (pt.y > rec.bottom) rec.bottom = pt.y;
|
||||
}
|
||||
if (rec.IsEmpty()) return RectD();
|
||||
return rec;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
std::ostream& operator << (std::ostream& outstream, const Path<T>& path)
|
||||
{
|
||||
|
@ -191,6 +379,7 @@ namespace Clipper2Lib
|
|||
return outstream;
|
||||
}
|
||||
|
||||
|
||||
template <typename T1, typename T2>
|
||||
inline Path<T1> ScalePath(const Path<T2>& path, double scale_x, double scale_y)
|
||||
{
|
||||
|
@ -218,6 +407,24 @@ namespace Clipper2Lib
|
|||
inline Paths<T1> ScalePaths(const Paths<T2>& paths, double scale_x, double scale_y)
|
||||
{
|
||||
Paths<T1> result;
|
||||
|
||||
if constexpr (std::numeric_limits<T1>::is_integer &&
|
||||
!std::numeric_limits<T2>::is_integer)
|
||||
{
|
||||
RectD r = Bounds(paths);
|
||||
const double max_coord_d = static_cast<double>(MAX_COORD);
|
||||
const double min_coord_d = static_cast<double>(MIN_COORD);
|
||||
if ((r.left * scale_x) < min_coord_d ||
|
||||
(r.right * scale_x) > max_coord_d ||
|
||||
(r.top * scale_y) < min_coord_d ||
|
||||
(r.bottom * scale_y) > max_coord_d)
|
||||
#ifdef __cpp_exceptions
|
||||
throw Clipper2Exception(range_error);
|
||||
#else
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
result.reserve(paths.size());
|
||||
std::transform(paths.begin(), paths.end(), back_inserter(result),
|
||||
[scale_x, scale_y](const auto& path)
|
||||
|
@ -356,129 +563,6 @@ namespace Clipper2Lib
|
|||
return result;
|
||||
}
|
||||
|
||||
// Rect ------------------------------------------------------------------------
|
||||
|
||||
template <typename T>
|
||||
struct Rect;
|
||||
|
||||
using Rect64 = Rect<int64_t>;
|
||||
using RectD = Rect<double>;
|
||||
|
||||
template <typename T>
|
||||
struct Rect {
|
||||
T left;
|
||||
T top;
|
||||
T right;
|
||||
T bottom;
|
||||
|
||||
Rect() :
|
||||
left(0),
|
||||
top(0),
|
||||
right(0),
|
||||
bottom(0) {}
|
||||
|
||||
Rect(T l, T t, T r, T b) :
|
||||
left(l),
|
||||
top(t),
|
||||
right(r),
|
||||
bottom(b) {}
|
||||
|
||||
|
||||
T Width() const { return right - left; }
|
||||
T Height() const { return bottom - top; }
|
||||
void Width(T width) { right = left + width; }
|
||||
void Height(T height) { bottom = top + height; }
|
||||
|
||||
Point<T> MidPoint() const
|
||||
{
|
||||
return Point<T>((left + right) / 2, (top + bottom) / 2);
|
||||
}
|
||||
|
||||
Path<T> AsPath() const
|
||||
{
|
||||
Path<T> result;
|
||||
result.reserve(4);
|
||||
result.push_back(Point<T>(left, top));
|
||||
result.push_back(Point<T>(right, top));
|
||||
result.push_back(Point<T>(right, bottom));
|
||||
result.push_back(Point<T>(left, bottom));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Contains(const Point<T>& pt) const
|
||||
{
|
||||
return pt.x > left && pt.x < right&& pt.y > top && pt.y < bottom;
|
||||
}
|
||||
|
||||
bool Contains(const Rect<T>& rec) const
|
||||
{
|
||||
return rec.left >= left && rec.right <= right &&
|
||||
rec.top >= top && rec.bottom <= bottom;
|
||||
}
|
||||
|
||||
void Scale(double scale) {
|
||||
left *= scale;
|
||||
top *= scale;
|
||||
right *= scale;
|
||||
bottom *= scale;
|
||||
}
|
||||
|
||||
bool IsEmpty() const { return bottom <= top || right <= left; };
|
||||
|
||||
bool Intersects(const Rect<T>& rec) const
|
||||
{
|
||||
// nb: if you get compile errors here, then it's almost certainly
|
||||
// due to including windows.h before including this header.
|
||||
// To fix this, add #define NOMINMAX just above where you
|
||||
// #include <windows.h> in you own code.
|
||||
return (std::max(left, rec.left) < std::min(right, rec.right)) &&
|
||||
(std::max(top, rec.top) < std::min(bottom, rec.bottom));
|
||||
};
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const Rect<T>& rect) {
|
||||
os << "("
|
||||
<< rect.left << "," << rect.top << "," << rect.right << "," << rect.bottom
|
||||
<< ")";
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T1, typename T2>
|
||||
inline Rect<T1> ScaleRect(const Rect<T2>& rect, double scale)
|
||||
{
|
||||
Rect<T1> result;
|
||||
|
||||
if constexpr (std::numeric_limits<T1>::is_integer &&
|
||||
!std::numeric_limits<T2>::is_integer)
|
||||
{
|
||||
result.left = static_cast<T1>(std::round(rect.left * scale));
|
||||
result.top = static_cast<T1>(std::round(rect.top * scale));
|
||||
result.right = static_cast<T1>(std::round(rect.right * scale));
|
||||
result.bottom = static_cast<T1>(std::round(rect.bottom * scale));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.left = rect.left * scale;
|
||||
result.top = rect.top * scale;
|
||||
result.right = rect.right * scale;
|
||||
result.bottom = rect.bottom * scale;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// clipper2Exception ---------------------------------------------------------
|
||||
|
||||
#ifdef __cpp_exceptions
|
||||
class Clipper2Exception : public std::exception {
|
||||
public:
|
||||
explicit Clipper2Exception(const char* description) :
|
||||
m_descr(description) {}
|
||||
virtual const char* what() const throw() { return m_descr.c_str(); }
|
||||
private:
|
||||
std::string m_descr;
|
||||
};
|
||||
#endif
|
||||
|
||||
// Miscellaneous ------------------------------------------------------------
|
||||
|
||||
inline void CheckPrecision(int& precision)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
* Author : Angus Johnson *
|
||||
* Date : 23 January 2023 *
|
||||
* Date : 27 January 2023 *
|
||||
* Website : http://www.angusj.com *
|
||||
* Copyright : Angus Johnson 2010-2023 *
|
||||
* Purpose : This is the main polygon clipping module *
|
||||
|
@ -2288,7 +2288,7 @@ namespace Clipper2Lib {
|
|||
node.edge1->curr_x = node.pt.x;
|
||||
node.edge2->curr_x = node.pt.x;
|
||||
CheckJoinLeft(*node.edge2, node.pt);
|
||||
CheckJoinRight(*node.edge1, node.pt);
|
||||
CheckJoinRight(*node.edge1, node.pt, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2648,42 +2648,45 @@ namespace Clipper2Lib {
|
|||
|
||||
void ClipperBase::CheckJoinLeft(Active& e, const Point64& pt)
|
||||
{
|
||||
if (IsOpen(e) || !IsHotEdge(e) ||
|
||||
!e.prev_in_ael || IsOpen(*e.prev_in_ael) ||
|
||||
!IsHotEdge(*e.prev_in_ael) || e.curr_x != e.prev_in_ael->curr_x ||
|
||||
pt.y <= e.top.y || pt.y <= e.prev_in_ael->top.y ||
|
||||
Active* prev = e.prev_in_ael;
|
||||
if (IsOpen(e) || !IsHotEdge(e) || !prev || IsOpen(*prev) ||
|
||||
!IsHotEdge(*prev) || e.curr_x != prev->curr_x ||
|
||||
pt.y <= e.top.y || pt.y <= prev->top.y ||
|
||||
IsJoined(e) || IsOpen(e) ||
|
||||
CrossProduct(e.top, pt, e.prev_in_ael->top))
|
||||
CrossProduct(e.top, pt, prev->top))
|
||||
return;
|
||||
|
||||
if (e.outrec->idx == e.prev_in_ael->outrec->idx)
|
||||
AddLocalMaxPoly(*e.prev_in_ael, e, pt);
|
||||
else if (e.outrec->idx < e.prev_in_ael->outrec->idx)
|
||||
JoinOutrecPaths(e, *e.prev_in_ael);
|
||||
if (e.outrec->idx == prev->outrec->idx)
|
||||
AddLocalMaxPoly(*prev, e, pt);
|
||||
else if (e.outrec->idx < prev->outrec->idx)
|
||||
JoinOutrecPaths(e, *prev);
|
||||
else
|
||||
JoinOutrecPaths(*e.prev_in_ael, e);
|
||||
e.prev_in_ael->join_with = JoinWith::Right;
|
||||
JoinOutrecPaths(*prev, e);
|
||||
prev->join_with = JoinWith::Right;
|
||||
e.join_with = JoinWith::Left;
|
||||
}
|
||||
|
||||
void ClipperBase::CheckJoinRight(Active& e, const Point64& pt)
|
||||
void ClipperBase::CheckJoinRight(Active& e,
|
||||
const Point64& pt, bool check_curr_x)
|
||||
{
|
||||
if (IsOpen(e) || !IsHotEdge(e) ||
|
||||
!e.next_in_ael || IsOpen(*e.next_in_ael) ||
|
||||
!IsHotEdge(*e.next_in_ael) || e.curr_x != e.next_in_ael->curr_x ||
|
||||
pt.y <= e.top.y || pt.y <= e.next_in_ael->top.y ||
|
||||
IsJoined(e) || IsOpen(e) ||
|
||||
CrossProduct(e.top, pt, e.next_in_ael->top))
|
||||
return;
|
||||
Active* next = e.next_in_ael;
|
||||
if (IsOpen(e) || !IsHotEdge(e) || IsJoined(e) ||
|
||||
!next || IsOpen(*next) || !IsHotEdge(*next) ||
|
||||
pt.y < e.top.y +2 || pt.y < next->top.y +2) // avoids trivial joins
|
||||
return;
|
||||
|
||||
if (e.outrec->idx == e.next_in_ael->outrec->idx)
|
||||
AddLocalMaxPoly(e, *e.next_in_ael, pt);
|
||||
else if (e.outrec->idx < e.next_in_ael->outrec->idx)
|
||||
JoinOutrecPaths(e, *e.next_in_ael);
|
||||
if (check_curr_x) next->curr_x = TopX(*next, pt.y);
|
||||
if (e.curr_x != next->curr_x ||
|
||||
CrossProduct(e.top, pt, next->top)) return;
|
||||
|
||||
if (e.outrec->idx == next->outrec->idx)
|
||||
AddLocalMaxPoly(e, *next, pt);
|
||||
else if (e.outrec->idx < next->outrec->idx)
|
||||
JoinOutrecPaths(e, *next);
|
||||
else
|
||||
JoinOutrecPaths(*e.next_in_ael, e);
|
||||
JoinOutrecPaths(*next, e);
|
||||
e.join_with = JoinWith::Right;
|
||||
e.next_in_ael->join_with = JoinWith::Left;
|
||||
next->join_with = JoinWith::Left;
|
||||
}
|
||||
|
||||
inline bool GetHorzExtendedHorzSeg(OutPt*& op, OutPt*& op2)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
* Author : Angus Johnson *
|
||||
* Date : 23 January 2023 *
|
||||
* Date : 27 January 2023 *
|
||||
* Website : http://www.angusj.com *
|
||||
* Copyright : Angus Johnson 2010-2023 *
|
||||
* Purpose : This is the main polygon clipping module *
|
||||
|
@ -245,7 +245,8 @@ namespace Clipper2Lib {
|
|||
|
||||
void Split(Active& e, const Point64& pt);
|
||||
void CheckJoinLeft(Active& e, const Point64& pt);
|
||||
void CheckJoinRight(Active& e, const Point64& pt);
|
||||
void CheckJoinRight(Active& e,
|
||||
const Point64& pt, bool check_curr_x = false);
|
||||
protected:
|
||||
bool has_open_paths_ = false;
|
||||
bool succeeded_ = true;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
* Author : Angus Johnson *
|
||||
* Date : 23 January 2023 *
|
||||
* Date : 27 January 2023 *
|
||||
* Website : http://www.angusj.com *
|
||||
* Copyright : Angus Johnson 2010-2023 *
|
||||
* Purpose : This module provides a simple interface to the Clipper Library *
|
||||
|
@ -21,18 +21,6 @@
|
|||
|
||||
namespace Clipper2Lib {
|
||||
|
||||
static const Rect64 MaxInvalidRect64 = Rect64(
|
||||
(std::numeric_limits<int64_t>::max)(),
|
||||
(std::numeric_limits<int64_t>::max)(),
|
||||
(std::numeric_limits<int64_t>::lowest)(),
|
||||
(std::numeric_limits<int64_t>::lowest)());
|
||||
|
||||
static const RectD MaxInvalidRectD = RectD(
|
||||
(std::numeric_limits<double>::max)(),
|
||||
(std::numeric_limits<double>::max)(),
|
||||
(std::numeric_limits<double>::lowest)(),
|
||||
(std::numeric_limits<double>::lowest)());
|
||||
|
||||
inline Paths64 BooleanOp(ClipType cliptype, FillRule fillrule,
|
||||
const Paths64& subjects, const Paths64& clips)
|
||||
{
|
||||
|
@ -192,64 +180,6 @@ namespace Clipper2Lib {
|
|||
return result;
|
||||
}
|
||||
|
||||
inline Rect64 Bounds(const Path64& path)
|
||||
{
|
||||
Rect64 rec = MaxInvalidRect64;
|
||||
for (const Point64& pt : path)
|
||||
{
|
||||
if (pt.x < rec.left) rec.left = pt.x;
|
||||
if (pt.x > rec.right) rec.right = pt.x;
|
||||
if (pt.y < rec.top) rec.top = pt.y;
|
||||
if (pt.y > rec.bottom) rec.bottom = pt.y;
|
||||
}
|
||||
if (rec.IsEmpty()) return Rect64();
|
||||
return rec;
|
||||
}
|
||||
|
||||
inline Rect64 Bounds(const Paths64& paths)
|
||||
{
|
||||
Rect64 rec = MaxInvalidRect64;
|
||||
for (const Path64& path : paths)
|
||||
for (const Point64& pt : path)
|
||||
{
|
||||
if (pt.x < rec.left) rec.left = pt.x;
|
||||
if (pt.x > rec.right) rec.right = pt.x;
|
||||
if (pt.y < rec.top) rec.top = pt.y;
|
||||
if (pt.y > rec.bottom) rec.bottom = pt.y;
|
||||
}
|
||||
if (rec.IsEmpty()) return Rect64();
|
||||
return rec;
|
||||
}
|
||||
|
||||
inline RectD Bounds(const PathD& path)
|
||||
{
|
||||
RectD rec = MaxInvalidRectD;
|
||||
for (const PointD& pt : path)
|
||||
{
|
||||
if (pt.x < rec.left) rec.left = pt.x;
|
||||
if (pt.x > rec.right) rec.right = pt.x;
|
||||
if (pt.y < rec.top) rec.top = pt.y;
|
||||
if (pt.y > rec.bottom) rec.bottom = pt.y;
|
||||
}
|
||||
if (rec.IsEmpty()) return RectD();
|
||||
return rec;
|
||||
}
|
||||
|
||||
inline RectD Bounds(const PathsD& paths)
|
||||
{
|
||||
RectD rec = MaxInvalidRectD;
|
||||
for (const PathD& path : paths)
|
||||
for (const PointD& pt : path)
|
||||
{
|
||||
if (pt.x < rec.left) rec.left = pt.x;
|
||||
if (pt.x > rec.right) rec.right = pt.x;
|
||||
if (pt.y < rec.top) rec.top = pt.y;
|
||||
if (pt.y > rec.bottom) rec.bottom = pt.y;
|
||||
}
|
||||
if (rec.IsEmpty()) return RectD();
|
||||
return rec;
|
||||
}
|
||||
|
||||
inline Path64 RectClip(const Rect64& rect, const Path64& path)
|
||||
{
|
||||
if (rect.IsEmpty() || path.empty()) return Path64();
|
||||
|
@ -552,7 +482,7 @@ namespace Clipper2Lib {
|
|||
{
|
||||
if (count)
|
||||
os << preamble << "+- Polygon with " << count <<
|
||||
" nested hole" << plural << std::endl;
|
||||
" hole" << plural << std::endl;
|
||||
else
|
||||
os << preamble << "+- Polygon" << std::endl;
|
||||
}
|
||||
|
@ -799,6 +729,108 @@ namespace Clipper2Lib {
|
|||
return Sqr(a * d - c * b) / (c * c + d * d);
|
||||
}
|
||||
|
||||
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 isOpenPath = false)
|
||||
{
|
||||
const size_t len = path.size(), high = len -1;
|
||||
const double epsSqr = Sqr(epsilon);
|
||||
if (len < 4) return Path<T>(path);
|
||||
|
||||
std::vector<bool> flags(len);
|
||||
std::vector<double> distSqr(len);
|
||||
size_t prior = high, curr = 0, start, next, prior2, next2;
|
||||
if (isOpenPath)
|
||||
{
|
||||
distSqr[0] = MAX_DBL;
|
||||
distSqr[high] = MAX_DBL;
|
||||
}
|
||||
else
|
||||
{
|
||||
distSqr[0] = PerpendicDistFromLineSqrd(path[0], path[high], path[1]);
|
||||
distSqr[high] = PerpendicDistFromLineSqrd(path[high], path[0], path[high - 1]);
|
||||
}
|
||||
for (size_t i = 1; i < high; ++i)
|
||||
distSqr[i] = PerpendicDistFromLineSqrd(path[i], path[i - 1], path[i + 1]);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (distSqr[curr] > epsSqr)
|
||||
{
|
||||
start = curr;
|
||||
do
|
||||
{
|
||||
curr = GetNext(curr, high, flags);
|
||||
} while (curr != start && distSqr[curr] > epsSqr);
|
||||
if (curr == start) break;
|
||||
}
|
||||
|
||||
prior = GetPrior(curr, high, flags);
|
||||
next = GetNext(curr, high, flags);
|
||||
if (next == prior) break;
|
||||
|
||||
if (distSqr[next] < distSqr[curr])
|
||||
{
|
||||
flags[next] = true;
|
||||
next = GetNext(next, high, flags);
|
||||
next2 = GetNext(next, high, flags);
|
||||
distSqr[curr] = PerpendicDistFromLineSqrd(path[curr], path[prior], path[next]);
|
||||
if (next != high || !isOpenPath)
|
||||
distSqr[next] = PerpendicDistFromLineSqrd(path[next], path[curr], path[next2]);
|
||||
curr = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
flags[curr] = true;
|
||||
curr = next;
|
||||
next = GetNext(next, high, flags);
|
||||
prior2 = GetPrior(prior, high, flags);
|
||||
distSqr[curr] = PerpendicDistFromLineSqrd(path[curr], path[prior], path[next]);
|
||||
if (prior != 0 || !isOpenPath)
|
||||
distSqr[prior] = PerpendicDistFromLineSqrd(path[prior], path[prior2], path[curr]);
|
||||
}
|
||||
}
|
||||
Path<T> result;
|
||||
result.reserve(len);
|
||||
for (typename Path<T>::size_type i = 0; i < len; ++i)
|
||||
if (!flags[i]) result.push_back(path[i]);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline Paths<T> SimplifyPaths(const Paths<T> paths,
|
||||
double epsilon, bool isOpenPath = false)
|
||||
{
|
||||
Paths<T> result;
|
||||
result.reserve(paths.size());
|
||||
for (const auto& path : paths)
|
||||
result.push_back(SimplifyPath(path, epsilon, isOpenPath));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void RDP(const Path<T> path, std::size_t begin,
|
||||
std::size_t end, double epsSqrd, std::vector<bool>& flags)
|
||||
|
@ -843,7 +875,7 @@ namespace Clipper2Lib {
|
|||
Paths<T> result;
|
||||
result.reserve(paths.size());
|
||||
std::transform(paths.begin(), paths.end(), back_inserter(result),
|
||||
[epsilon](const auto& path)
|
||||
[epsilon](const auto& path)
|
||||
{ return RamerDouglasPeucker<T>(path, epsilon); });
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
* Author : Angus Johnson *
|
||||
* Date : 21 January 2023 *
|
||||
* Date : 25 January 2023 *
|
||||
* Website : http://www.angusj.com *
|
||||
* Copyright : Angus Johnson 2010-2023 *
|
||||
* Purpose : Path Offset (Inflate/Shrink) *
|
||||
|
@ -20,20 +20,39 @@ const double floating_point_tolerance = 1e-12;
|
|||
// Miscellaneous methods
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Paths64::size_type GetLowestPolygonIdx(const Paths64& paths)
|
||||
void GetBoundsAndLowestPolyIdx(const Paths64& paths, Rect64& r, int & idx)
|
||||
{
|
||||
Paths64::size_type result = 0;
|
||||
Point64 lp = Point64(static_cast<int64_t>(0),
|
||||
std::numeric_limits<int64_t>::min());
|
||||
|
||||
for (Paths64::size_type i = 0 ; i < paths.size(); ++i)
|
||||
idx = -1;
|
||||
r = MaxInvalidRect64;
|
||||
int64_t lpx = 0;
|
||||
for (int i = 0; i < static_cast<int>(paths.size()); ++i)
|
||||
for (const Point64& p : paths[i])
|
||||
{
|
||||
if (p.y < lp.y || (p.y == lp.y && p.x >= lp.x)) continue;
|
||||
result = i;
|
||||
lp = p;
|
||||
}
|
||||
return result;
|
||||
{
|
||||
if (p.y >= r.bottom)
|
||||
{
|
||||
if (p.y > r.bottom || p.x < lpx)
|
||||
{
|
||||
idx = i;
|
||||
lpx = p.x;
|
||||
r.bottom = p.y;
|
||||
}
|
||||
}
|
||||
else if (p.y < r.top) r.top = p.y;
|
||||
if (p.x > r.right) r.right = p.x;
|
||||
else if (p.x < r.left) r.left = p.x;
|
||||
}
|
||||
if (idx < 0) r = Rect64(0, 0, 0, 0);
|
||||
if (r.top == INT64_MIN) r.bottom = r.top;
|
||||
if (r.left == INT64_MIN) r.left = r.right;
|
||||
}
|
||||
|
||||
bool IsSafeOffset(const Rect64& r, int64_t delta)
|
||||
{
|
||||
if (delta < 0) return true;
|
||||
return r.left > INT64_MIN + delta &&
|
||||
r.right < INT64_MAX - delta &&
|
||||
r.top > INT64_MIN + delta &&
|
||||
r.bottom < INT64_MAX - delta;
|
||||
}
|
||||
|
||||
PointD GetUnitNormal(const Point64& pt1, const Point64& pt2)
|
||||
|
@ -356,13 +375,17 @@ void ClipperOffset::DoGroupOffset(Group& group, double delta)
|
|||
if (group.end_type_ != EndType::Polygon) delta = std::abs(delta) * 0.5;
|
||||
bool isClosedPaths = IsClosedPath(group.end_type_);
|
||||
|
||||
//the lowermost polygon must be an outer polygon. So we can use that as the
|
||||
//designated orientation for outer polygons (needed for tidy-up clipping)
|
||||
Rect64 r;
|
||||
int idx = 0;
|
||||
GetBoundsAndLowestPolyIdx(group.paths_in_, r, idx);
|
||||
if (!IsSafeOffset(r, static_cast<int64_t>(std::ceil(delta))))
|
||||
throw "Range error - the offset delta is too large";
|
||||
|
||||
if (isClosedPaths)
|
||||
{
|
||||
//the lowermost polygon must be an outer polygon. So we can use that as the
|
||||
//designated orientation for outer polygons (needed for tidy-up clipping)
|
||||
Paths64::size_type lowestIdx = GetLowestPolygonIdx(group.paths_in_);
|
||||
// nb: don't use the default orientation here ...
|
||||
double area = Area(group.paths_in_[lowestIdx]);
|
||||
double area = Area(group.paths_in_[idx]);
|
||||
if (area == 0) return;
|
||||
group.is_reversed_ = (area < 0);
|
||||
if (group.is_reversed_) delta = -delta;
|
||||
|
@ -403,7 +426,7 @@ void ClipperOffset::DoGroupOffset(Group& group, double delta)
|
|||
else
|
||||
{
|
||||
int d = (int)std::ceil(abs_group_delta_);
|
||||
Rect64 r = Rect64(path[0].x - d, path[0].y - d, path[0].x + d, path[0].y + d);
|
||||
r = Rect64(path[0].x - d, path[0].y - d, path[0].x + d, path[0].y + d);
|
||||
group.path_ = r.AsPath();
|
||||
}
|
||||
group.paths_out_.push_back(group.path_);
|
||||
|
@ -416,21 +439,6 @@ void ClipperOffset::DoGroupOffset(Group& group, double delta)
|
|||
else OffsetOpenPath(group, path, group.end_type_);
|
||||
}
|
||||
}
|
||||
|
||||
if (!merge_groups_)
|
||||
{
|
||||
//clean up self-intersections ...
|
||||
Clipper64 c;
|
||||
c.PreserveCollinear = false;
|
||||
//the solution should retain the orientation of the input
|
||||
c.ReverseSolution = reverse_solution_ != group.is_reversed_;
|
||||
c.AddSubject(group.paths_out_);
|
||||
if (group.is_reversed_)
|
||||
c.Execute(ClipType::Union, FillRule::Negative, group.paths_out_);
|
||||
else
|
||||
c.Execute(ClipType::Union, FillRule::Positive, group.paths_out_);
|
||||
}
|
||||
|
||||
solution.reserve(solution.size() + group.paths_out_.size());
|
||||
copy(group.paths_out_.begin(), group.paths_out_.end(), back_inserter(solution));
|
||||
group.paths_out_.clear();
|
||||
|
@ -439,6 +447,8 @@ void ClipperOffset::DoGroupOffset(Group& group, double delta)
|
|||
Paths64 ClipperOffset::Execute(double delta)
|
||||
{
|
||||
solution.clear();
|
||||
if (groups_.size() == 0) return solution;
|
||||
|
||||
if (std::abs(delta) < default_arc_tolerance)
|
||||
{
|
||||
for (const Group& group : groups_)
|
||||
|
@ -453,26 +463,21 @@ Paths64 ClipperOffset::Execute(double delta)
|
|||
2.0 :
|
||||
2.0 / (miter_limit_ * miter_limit_);
|
||||
|
||||
std::vector<Group>::iterator groups_iter;
|
||||
for (groups_iter = groups_.begin();
|
||||
groups_iter != groups_.end(); ++groups_iter)
|
||||
{
|
||||
DoGroupOffset(*groups_iter, delta);
|
||||
}
|
||||
std::vector<Group>::iterator git;
|
||||
for (git = groups_.begin(); git != groups_.end(); ++git)
|
||||
DoGroupOffset(*git, delta);
|
||||
|
||||
//clean up self-intersections ...
|
||||
Clipper64 c;
|
||||
c.PreserveCollinear = false;
|
||||
//the solution should retain the orientation of the input
|
||||
c.ReverseSolution = reverse_solution_ != groups_[0].is_reversed_;
|
||||
c.AddSubject(solution);
|
||||
if (groups_[0].is_reversed_)
|
||||
c.Execute(ClipType::Union, FillRule::Negative, solution);
|
||||
else
|
||||
c.Execute(ClipType::Union, FillRule::Positive, solution);
|
||||
|
||||
if (merge_groups_ && groups_.size() > 0)
|
||||
{
|
||||
//clean up self-intersections ...
|
||||
Clipper64 c;
|
||||
c.PreserveCollinear = false;
|
||||
//the solution should retain the orientation of the input
|
||||
c.ReverseSolution = reverse_solution_ != groups_[0].is_reversed_;
|
||||
c.AddSubject(solution);
|
||||
if (groups_[0].is_reversed_)
|
||||
c.Execute(ClipType::Union, FillRule::Negative, solution);
|
||||
else
|
||||
c.Execute(ClipType::Union, FillRule::Positive, solution);
|
||||
}
|
||||
return solution;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
* Author : Angus Johnson *
|
||||
* Date : 21 January 2023 *
|
||||
* Date : 25 January 2023 *
|
||||
* Website : http://www.angusj.com *
|
||||
* Copyright : Angus Johnson 2010-2023 *
|
||||
* Purpose : Path Offset (Inflate/Shrink) *
|
||||
|
@ -49,7 +49,6 @@ private:
|
|||
|
||||
double miter_limit_ = 0.0;
|
||||
double arc_tolerance_ = 0.0;
|
||||
bool merge_groups_ = true;
|
||||
bool preserve_collinear_ = false;
|
||||
bool reverse_solution_ = false;
|
||||
|
||||
|
@ -89,14 +88,6 @@ public:
|
|||
double ArcTolerance() const { return arc_tolerance_; }
|
||||
void ArcTolerance(double arc_tolerance) { arc_tolerance_ = arc_tolerance; }
|
||||
|
||||
//MergeGroups: A path group is one or more paths added via the AddPath or
|
||||
//AddPaths methods. By default these path groups will be offset
|
||||
//independently of other groups and this may cause overlaps (intersections).
|
||||
//However, when MergeGroups is enabled, any overlapping offsets will be
|
||||
//merged (via a clipping union operation) to remove overlaps.
|
||||
bool MergeGroups() const { return merge_groups_; }
|
||||
void MergeGroups(bool merge_groups) { merge_groups_ = merge_groups; }
|
||||
|
||||
bool PreserveCollinear() const { return preserve_collinear_; }
|
||||
void PreserveCollinear(bool preserve_collinear){preserve_collinear_ = preserve_collinear;}
|
||||
|
||||
|
|
Loading…
Reference in New Issue