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
|
## Clipper2
|
||||||
- [![Upstream](https://img.shields.io/github/v/tag/AngusJohnson/Clipper2?label=Upstream)](https://github.com/AngusJohnson/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
|
- License: BSL-1.0
|
||||||
|
|
||||||
## ConvertUTF
|
## ConvertUTF
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Author : Angus Johnson *
|
* Author : Angus Johnson *
|
||||||
* Date : 14 January 2023 *
|
* Date : 26 January 2023 *
|
||||||
* Website : http://www.angusj.com *
|
* Website : http://www.angusj.com *
|
||||||
* Copyright : Angus Johnson 2010-2023 *
|
* Copyright : Angus Johnson 2010-2023 *
|
||||||
* Purpose : Core Clipper Library structures and functions *
|
* Purpose : Core Clipper Library structures and functions *
|
||||||
|
@ -20,9 +20,23 @@
|
||||||
|
|
||||||
namespace Clipper2Lib
|
namespace Clipper2Lib
|
||||||
{
|
{
|
||||||
|
|
||||||
#ifdef __cpp_exceptions
|
#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 =
|
static const char* precision_error =
|
||||||
"Precision exceeds the permitted range";
|
"Precision exceeds the permitted range";
|
||||||
|
static const char* range_error =
|
||||||
|
"Values exceed permitted range";
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const double PI = 3.141592653589793238;
|
static const double PI = 3.141592653589793238;
|
||||||
|
@ -30,6 +44,9 @@ namespace Clipper2Lib
|
||||||
static const int64_t MIN_COORD = -MAX_COORD;
|
static const int64_t MIN_COORD = -MAX_COORD;
|
||||||
static const int64_t INVALID = INT64_MAX;
|
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
|
//By far the most widely used filling rules for polygons are EvenOdd
|
||||||
//and NonZero, sometimes called Alternate and Winding respectively.
|
//and NonZero, sometimes called Alternate and Winding respectively.
|
||||||
//https://en.wikipedia.org/wiki/Nonzero-rule
|
//https://en.wikipedia.org/wiki/Nonzero-rule
|
||||||
|
@ -170,6 +187,177 @@ namespace Clipper2Lib
|
||||||
using Paths64 = std::vector< Path64>;
|
using Paths64 = std::vector< Path64>;
|
||||||
using PathsD = std::vector< PathD>;
|
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>
|
template <typename T>
|
||||||
std::ostream& operator << (std::ostream& outstream, const Path<T>& path)
|
std::ostream& operator << (std::ostream& outstream, const Path<T>& path)
|
||||||
{
|
{
|
||||||
|
@ -191,6 +379,7 @@ namespace Clipper2Lib
|
||||||
return outstream;
|
return outstream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <typename T1, typename T2>
|
template <typename T1, typename T2>
|
||||||
inline Path<T1> ScalePath(const Path<T2>& path, double scale_x, double scale_y)
|
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)
|
inline Paths<T1> ScalePaths(const Paths<T2>& paths, double scale_x, double scale_y)
|
||||||
{
|
{
|
||||||
Paths<T1> result;
|
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());
|
result.reserve(paths.size());
|
||||||
std::transform(paths.begin(), paths.end(), back_inserter(result),
|
std::transform(paths.begin(), paths.end(), back_inserter(result),
|
||||||
[scale_x, scale_y](const auto& path)
|
[scale_x, scale_y](const auto& path)
|
||||||
|
@ -356,129 +563,6 @@ namespace Clipper2Lib
|
||||||
return result;
|
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 ------------------------------------------------------------
|
// Miscellaneous ------------------------------------------------------------
|
||||||
|
|
||||||
inline void CheckPrecision(int& precision)
|
inline void CheckPrecision(int& precision)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Author : Angus Johnson *
|
* Author : Angus Johnson *
|
||||||
* Date : 23 January 2023 *
|
* Date : 27 January 2023 *
|
||||||
* Website : http://www.angusj.com *
|
* Website : http://www.angusj.com *
|
||||||
* Copyright : Angus Johnson 2010-2023 *
|
* Copyright : Angus Johnson 2010-2023 *
|
||||||
* Purpose : This is the main polygon clipping module *
|
* Purpose : This is the main polygon clipping module *
|
||||||
|
@ -2288,7 +2288,7 @@ namespace Clipper2Lib {
|
||||||
node.edge1->curr_x = node.pt.x;
|
node.edge1->curr_x = node.pt.x;
|
||||||
node.edge2->curr_x = node.pt.x;
|
node.edge2->curr_x = node.pt.x;
|
||||||
CheckJoinLeft(*node.edge2, node.pt);
|
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)
|
void ClipperBase::CheckJoinLeft(Active& e, const Point64& pt)
|
||||||
{
|
{
|
||||||
if (IsOpen(e) || !IsHotEdge(e) ||
|
Active* prev = e.prev_in_ael;
|
||||||
!e.prev_in_ael || IsOpen(*e.prev_in_ael) ||
|
if (IsOpen(e) || !IsHotEdge(e) || !prev || IsOpen(*prev) ||
|
||||||
!IsHotEdge(*e.prev_in_ael) || e.curr_x != e.prev_in_ael->curr_x ||
|
!IsHotEdge(*prev) || e.curr_x != prev->curr_x ||
|
||||||
pt.y <= e.top.y || pt.y <= e.prev_in_ael->top.y ||
|
pt.y <= e.top.y || pt.y <= prev->top.y ||
|
||||||
IsJoined(e) || IsOpen(e) ||
|
IsJoined(e) || IsOpen(e) ||
|
||||||
CrossProduct(e.top, pt, e.prev_in_ael->top))
|
CrossProduct(e.top, pt, prev->top))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (e.outrec->idx == e.prev_in_ael->outrec->idx)
|
if (e.outrec->idx == prev->outrec->idx)
|
||||||
AddLocalMaxPoly(*e.prev_in_ael, e, pt);
|
AddLocalMaxPoly(*prev, e, pt);
|
||||||
else if (e.outrec->idx < e.prev_in_ael->outrec->idx)
|
else if (e.outrec->idx < prev->outrec->idx)
|
||||||
JoinOutrecPaths(e, *e.prev_in_ael);
|
JoinOutrecPaths(e, *prev);
|
||||||
else
|
else
|
||||||
JoinOutrecPaths(*e.prev_in_ael, e);
|
JoinOutrecPaths(*prev, e);
|
||||||
e.prev_in_ael->join_with = JoinWith::Right;
|
prev->join_with = JoinWith::Right;
|
||||||
e.join_with = JoinWith::Left;
|
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) ||
|
Active* next = e.next_in_ael;
|
||||||
!e.next_in_ael || IsOpen(*e.next_in_ael) ||
|
if (IsOpen(e) || !IsHotEdge(e) || IsJoined(e) ||
|
||||||
!IsHotEdge(*e.next_in_ael) || e.curr_x != e.next_in_ael->curr_x ||
|
!next || IsOpen(*next) || !IsHotEdge(*next) ||
|
||||||
pt.y <= e.top.y || pt.y <= e.next_in_ael->top.y ||
|
pt.y < e.top.y +2 || pt.y < next->top.y +2) // avoids trivial joins
|
||||||
IsJoined(e) || IsOpen(e) ||
|
|
||||||
CrossProduct(e.top, pt, e.next_in_ael->top))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (e.outrec->idx == e.next_in_ael->outrec->idx)
|
if (check_curr_x) next->curr_x = TopX(*next, pt.y);
|
||||||
AddLocalMaxPoly(e, *e.next_in_ael, pt);
|
if (e.curr_x != next->curr_x ||
|
||||||
else if (e.outrec->idx < e.next_in_ael->outrec->idx)
|
CrossProduct(e.top, pt, next->top)) return;
|
||||||
JoinOutrecPaths(e, *e.next_in_ael);
|
|
||||||
|
if (e.outrec->idx == next->outrec->idx)
|
||||||
|
AddLocalMaxPoly(e, *next, pt);
|
||||||
|
else if (e.outrec->idx < next->outrec->idx)
|
||||||
|
JoinOutrecPaths(e, *next);
|
||||||
else
|
else
|
||||||
JoinOutrecPaths(*e.next_in_ael, e);
|
JoinOutrecPaths(*next, e);
|
||||||
e.join_with = JoinWith::Right;
|
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)
|
inline bool GetHorzExtendedHorzSeg(OutPt*& op, OutPt*& op2)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Author : Angus Johnson *
|
* Author : Angus Johnson *
|
||||||
* Date : 23 January 2023 *
|
* Date : 27 January 2023 *
|
||||||
* Website : http://www.angusj.com *
|
* Website : http://www.angusj.com *
|
||||||
* Copyright : Angus Johnson 2010-2023 *
|
* Copyright : Angus Johnson 2010-2023 *
|
||||||
* Purpose : This is the main polygon clipping module *
|
* Purpose : This is the main polygon clipping module *
|
||||||
|
@ -245,7 +245,8 @@ namespace Clipper2Lib {
|
||||||
|
|
||||||
void Split(Active& e, const Point64& pt);
|
void Split(Active& e, const Point64& pt);
|
||||||
void CheckJoinLeft(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:
|
protected:
|
||||||
bool has_open_paths_ = false;
|
bool has_open_paths_ = false;
|
||||||
bool succeeded_ = true;
|
bool succeeded_ = true;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Author : Angus Johnson *
|
* Author : Angus Johnson *
|
||||||
* Date : 23 January 2023 *
|
* Date : 27 January 2023 *
|
||||||
* Website : http://www.angusj.com *
|
* Website : http://www.angusj.com *
|
||||||
* Copyright : Angus Johnson 2010-2023 *
|
* Copyright : Angus Johnson 2010-2023 *
|
||||||
* Purpose : This module provides a simple interface to the Clipper Library *
|
* Purpose : This module provides a simple interface to the Clipper Library *
|
||||||
|
@ -21,18 +21,6 @@
|
||||||
|
|
||||||
namespace Clipper2Lib {
|
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,
|
inline Paths64 BooleanOp(ClipType cliptype, FillRule fillrule,
|
||||||
const Paths64& subjects, const Paths64& clips)
|
const Paths64& subjects, const Paths64& clips)
|
||||||
{
|
{
|
||||||
|
@ -192,64 +180,6 @@ namespace Clipper2Lib {
|
||||||
return result;
|
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)
|
inline Path64 RectClip(const Rect64& rect, const Path64& path)
|
||||||
{
|
{
|
||||||
if (rect.IsEmpty() || path.empty()) return Path64();
|
if (rect.IsEmpty() || path.empty()) return Path64();
|
||||||
|
@ -552,7 +482,7 @@ namespace Clipper2Lib {
|
||||||
{
|
{
|
||||||
if (count)
|
if (count)
|
||||||
os << preamble << "+- Polygon with " << count <<
|
os << preamble << "+- Polygon with " << count <<
|
||||||
" nested hole" << plural << std::endl;
|
" hole" << plural << std::endl;
|
||||||
else
|
else
|
||||||
os << preamble << "+- Polygon" << std::endl;
|
os << preamble << "+- Polygon" << std::endl;
|
||||||
}
|
}
|
||||||
|
@ -799,6 +729,108 @@ namespace Clipper2Lib {
|
||||||
return Sqr(a * d - c * b) / (c * c + d * d);
|
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>
|
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)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Author : Angus Johnson *
|
* Author : Angus Johnson *
|
||||||
* Date : 21 January 2023 *
|
* Date : 25 January 2023 *
|
||||||
* Website : http://www.angusj.com *
|
* Website : http://www.angusj.com *
|
||||||
* Copyright : Angus Johnson 2010-2023 *
|
* Copyright : Angus Johnson 2010-2023 *
|
||||||
* Purpose : Path Offset (Inflate/Shrink) *
|
* Purpose : Path Offset (Inflate/Shrink) *
|
||||||
|
@ -20,20 +20,39 @@ const double floating_point_tolerance = 1e-12;
|
||||||
// Miscellaneous methods
|
// Miscellaneous methods
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
Paths64::size_type GetLowestPolygonIdx(const Paths64& paths)
|
void GetBoundsAndLowestPolyIdx(const Paths64& paths, Rect64& r, int & idx)
|
||||||
{
|
{
|
||||||
Paths64::size_type result = 0;
|
idx = -1;
|
||||||
Point64 lp = Point64(static_cast<int64_t>(0),
|
r = MaxInvalidRect64;
|
||||||
std::numeric_limits<int64_t>::min());
|
int64_t lpx = 0;
|
||||||
|
for (int i = 0; i < static_cast<int>(paths.size()); ++i)
|
||||||
for (Paths64::size_type i = 0 ; i < paths.size(); ++i)
|
|
||||||
for (const Point64& p : paths[i])
|
for (const Point64& p : paths[i])
|
||||||
{
|
{
|
||||||
if (p.y < lp.y || (p.y == lp.y && p.x >= lp.x)) continue;
|
if (p.y >= r.bottom)
|
||||||
result = i;
|
{
|
||||||
lp = p;
|
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;
|
||||||
}
|
}
|
||||||
return result;
|
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)
|
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;
|
if (group.end_type_ != EndType::Polygon) delta = std::abs(delta) * 0.5;
|
||||||
bool isClosedPaths = IsClosedPath(group.end_type_);
|
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)
|
if (isClosedPaths)
|
||||||
{
|
{
|
||||||
//the lowermost polygon must be an outer polygon. So we can use that as the
|
double area = Area(group.paths_in_[idx]);
|
||||||
//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]);
|
|
||||||
if (area == 0) return;
|
if (area == 0) return;
|
||||||
group.is_reversed_ = (area < 0);
|
group.is_reversed_ = (area < 0);
|
||||||
if (group.is_reversed_) delta = -delta;
|
if (group.is_reversed_) delta = -delta;
|
||||||
|
@ -403,7 +426,7 @@ void ClipperOffset::DoGroupOffset(Group& group, double delta)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int d = (int)std::ceil(abs_group_delta_);
|
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.path_ = r.AsPath();
|
||||||
}
|
}
|
||||||
group.paths_out_.push_back(group.path_);
|
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_);
|
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());
|
solution.reserve(solution.size() + group.paths_out_.size());
|
||||||
copy(group.paths_out_.begin(), group.paths_out_.end(), back_inserter(solution));
|
copy(group.paths_out_.begin(), group.paths_out_.end(), back_inserter(solution));
|
||||||
group.paths_out_.clear();
|
group.paths_out_.clear();
|
||||||
|
@ -439,6 +447,8 @@ void ClipperOffset::DoGroupOffset(Group& group, double delta)
|
||||||
Paths64 ClipperOffset::Execute(double delta)
|
Paths64 ClipperOffset::Execute(double delta)
|
||||||
{
|
{
|
||||||
solution.clear();
|
solution.clear();
|
||||||
|
if (groups_.size() == 0) return solution;
|
||||||
|
|
||||||
if (std::abs(delta) < default_arc_tolerance)
|
if (std::abs(delta) < default_arc_tolerance)
|
||||||
{
|
{
|
||||||
for (const Group& group : groups_)
|
for (const Group& group : groups_)
|
||||||
|
@ -453,26 +463,21 @@ Paths64 ClipperOffset::Execute(double delta)
|
||||||
2.0 :
|
2.0 :
|
||||||
2.0 / (miter_limit_ * miter_limit_);
|
2.0 / (miter_limit_ * miter_limit_);
|
||||||
|
|
||||||
std::vector<Group>::iterator groups_iter;
|
std::vector<Group>::iterator git;
|
||||||
for (groups_iter = groups_.begin();
|
for (git = groups_.begin(); git != groups_.end(); ++git)
|
||||||
groups_iter != groups_.end(); ++groups_iter)
|
DoGroupOffset(*git, delta);
|
||||||
{
|
|
||||||
DoGroupOffset(*groups_iter, 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;
|
return solution;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Author : Angus Johnson *
|
* Author : Angus Johnson *
|
||||||
* Date : 21 January 2023 *
|
* Date : 25 January 2023 *
|
||||||
* Website : http://www.angusj.com *
|
* Website : http://www.angusj.com *
|
||||||
* Copyright : Angus Johnson 2010-2023 *
|
* Copyright : Angus Johnson 2010-2023 *
|
||||||
* Purpose : Path Offset (Inflate/Shrink) *
|
* Purpose : Path Offset (Inflate/Shrink) *
|
||||||
|
@ -49,7 +49,6 @@ private:
|
||||||
|
|
||||||
double miter_limit_ = 0.0;
|
double miter_limit_ = 0.0;
|
||||||
double arc_tolerance_ = 0.0;
|
double arc_tolerance_ = 0.0;
|
||||||
bool merge_groups_ = true;
|
|
||||||
bool preserve_collinear_ = false;
|
bool preserve_collinear_ = false;
|
||||||
bool reverse_solution_ = false;
|
bool reverse_solution_ = false;
|
||||||
|
|
||||||
|
@ -89,14 +88,6 @@ public:
|
||||||
double ArcTolerance() const { return arc_tolerance_; }
|
double ArcTolerance() const { return arc_tolerance_; }
|
||||||
void ArcTolerance(double arc_tolerance) { arc_tolerance_ = 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_; }
|
bool PreserveCollinear() const { return preserve_collinear_; }
|
||||||
void PreserveCollinear(bool preserve_collinear){preserve_collinear_ = preserve_collinear;}
|
void PreserveCollinear(bool preserve_collinear){preserve_collinear_ = preserve_collinear;}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue