Update Clipper2 to commit 4ef91e99756f97cab23e7fa2cd78861deeb61338.
It fixes some of our issues. However, Clipper issue 618 can create problems in zone filler, Clipper2 will be updated as soom as issue 618 is fixed.
This commit is contained in:
parent
76cd637895
commit
235006a1b7
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
* Author : Angus Johnson *
|
||||
* Date : 8 April 2023 *
|
||||
* Date : 26 July 2023 *
|
||||
* Website : http://www.angusj.com *
|
||||
* Copyright : Angus Johnson 2010-2023 *
|
||||
* Purpose : Core Clipper Library structures and functions *
|
||||
|
@ -53,6 +53,7 @@ namespace Clipper2Lib
|
|||
#ifndef PI
|
||||
static const double PI = 3.141592653589793238;
|
||||
#endif
|
||||
static const int MAX_DECIMAL_PRECISION = 8; // see https://github.com/AngusJohnson/Clipper2/discussions/564
|
||||
static const int64_t MAX_COORD = INT64_MAX >> 2;
|
||||
static const int64_t MIN_COORD = -MAX_COORD;
|
||||
static const int64_t INVALID = INT64_MAX;
|
||||
|
@ -132,6 +133,7 @@ namespace Clipper2Lib
|
|||
return Point(x * scale, y * scale, z);
|
||||
}
|
||||
|
||||
void SetZ(const int64_t z_value) { z = z_value; }
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const Point& point)
|
||||
{
|
||||
|
@ -255,8 +257,8 @@ namespace Clipper2Lib
|
|||
}
|
||||
else
|
||||
{
|
||||
left = top = std::numeric_limits<T>::max();
|
||||
right = bottom = -std::numeric_limits<int64_t>::max();
|
||||
left = top = (std::numeric_limits<T>::max)();
|
||||
right = bottom = -(std::numeric_limits<int64_t>::max)();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -346,8 +348,8 @@ namespace Clipper2Lib
|
|||
template <typename T>
|
||||
Rect<T> GetBounds(const Path<T>& path)
|
||||
{
|
||||
auto xmin = std::numeric_limits<T>::max();
|
||||
auto ymin = std::numeric_limits<T>::max();
|
||||
auto xmin = (std::numeric_limits<T>::max)();
|
||||
auto ymin = (std::numeric_limits<T>::max)();
|
||||
auto xmax = std::numeric_limits<T>::lowest();
|
||||
auto ymax = std::numeric_limits<T>::lowest();
|
||||
for (const auto& p : path)
|
||||
|
@ -363,8 +365,8 @@ namespace Clipper2Lib
|
|||
template <typename T>
|
||||
Rect<T> GetBounds(const Paths<T>& paths)
|
||||
{
|
||||
auto xmin = std::numeric_limits<T>::max();
|
||||
auto ymin = std::numeric_limits<T>::max();
|
||||
auto xmin = (std::numeric_limits<T>::max)();
|
||||
auto ymin = (std::numeric_limits<T>::max)();
|
||||
auto xmax = std::numeric_limits<T>::lowest();
|
||||
auto ymax = std::numeric_limits<T>::lowest();
|
||||
for (const Path<T>& path : paths)
|
||||
|
@ -582,10 +584,10 @@ namespace Clipper2Lib
|
|||
|
||||
inline void CheckPrecision(int& precision, int& error_code)
|
||||
{
|
||||
if (precision >= -8 && precision <= 8) return;
|
||||
if (precision >= -MAX_DECIMAL_PRECISION && precision <= MAX_DECIMAL_PRECISION) return;
|
||||
error_code |= precision_error_i; // non-fatal error
|
||||
DoError(precision_error_i); // unless exceptions enabled
|
||||
precision = precision > 8 ? 8 : -8;
|
||||
DoError(precision_error_i); // does nothing unless exceptions enabled
|
||||
precision = precision > 0 ? MAX_DECIMAL_PRECISION : -MAX_DECIMAL_PRECISION;
|
||||
}
|
||||
|
||||
inline void CheckPrecision(int& precision)
|
||||
|
@ -677,29 +679,27 @@ namespace Clipper2Lib
|
|||
//nb: This statement is premised on using Cartesian coordinates
|
||||
return Area<T>(poly) >= 0;
|
||||
}
|
||||
|
||||
inline int64_t CheckCastInt64(double val)
|
||||
{
|
||||
if ((val >= max_coord) || (val <= min_coord)) return INVALID;
|
||||
else return static_cast<int64_t>(val);
|
||||
}
|
||||
|
||||
|
||||
inline bool GetIntersectPoint(const Point64& ln1a, const Point64& ln1b,
|
||||
const Point64& ln2a, const Point64& ln2b, Point64& ip)
|
||||
{
|
||||
// https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
|
||||
|
||||
double dx1 = static_cast<double>(ln1b.x - ln1a.x);
|
||||
double dy1 = static_cast<double>(ln1b.y - ln1a.y);
|
||||
double dx2 = static_cast<double>(ln2b.x - ln2a.x);
|
||||
double dy2 = static_cast<double>(ln2b.y - ln2a.y);
|
||||
|
||||
double det = dy1 * dx2 - dy2 * dx1;
|
||||
if (det == 0.0) return 0;
|
||||
double qx = dx1 * ln1a.y - dy1 * ln1a.x;
|
||||
double qy = dx2 * ln2a.y - dy2 * ln2a.x;
|
||||
ip.x = CheckCastInt64((dx1 * qy - dx2 * qx) / det);
|
||||
ip.y = CheckCastInt64((dy1 * qy - dy2 * qx) / det);
|
||||
return (ip.x != INVALID && ip.y != INVALID);
|
||||
if (det == 0.0) return false;
|
||||
double t = ((ln1a.x - ln2a.x) * dy2 - (ln1a.y - ln2a.y) * dx2) / det;
|
||||
if (t <= 0.0) ip = ln1a; // ?? check further (see also #568)
|
||||
else if (t >= 1.0) ip = ln1b; // ?? check further
|
||||
else
|
||||
{
|
||||
ip.x = static_cast<int64_t>(ln1a.x + t * dx1);
|
||||
ip.y = static_cast<int64_t>(ln1a.y + t * dy1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool SegmentsIntersect(const Point64& seg1a, const Point64& seg1b,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
* Author : Angus Johnson *
|
||||
* Date : 15 May 2023 *
|
||||
* Date : 26 July 2023 *
|
||||
* Website : http://www.angusj.com *
|
||||
* Copyright : Angus Johnson 2010-2023 *
|
||||
* Purpose : This is the main polygon clipping module *
|
||||
|
@ -13,7 +13,7 @@
|
|||
constexpr auto CLIPPER2_VERSION = "1.2.2";
|
||||
|
||||
#include <cstdlib>
|
||||
#include <stdint.h>
|
||||
#include <stdint.h> //#541
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
|
@ -23,9 +23,6 @@ constexpr auto CLIPPER2_VERSION = "1.2.2";
|
|||
|
||||
#include "clipper.core.h"
|
||||
|
||||
#ifdef None
|
||||
#undef None
|
||||
#endif
|
||||
namespace Clipper2Lib {
|
||||
|
||||
struct Scanline;
|
||||
|
@ -38,7 +35,7 @@ namespace Clipper2Lib {
|
|||
|
||||
//Note: all clipping operations except for Difference are commutative.
|
||||
enum class ClipType { None, Intersection, Union, Difference, Xor };
|
||||
|
||||
|
||||
enum class PathType { Subject, Clip };
|
||||
enum class JoinWith { None, Left, Right };
|
||||
|
||||
|
@ -46,7 +43,7 @@ namespace Clipper2Lib {
|
|||
None = 0, OpenStart = 1, OpenEnd = 2, LocalMax = 4, LocalMin = 8
|
||||
};
|
||||
|
||||
constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b)
|
||||
constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b)
|
||||
{
|
||||
return (enum VertexFlags)(uint32_t(a) & uint32_t(b));
|
||||
}
|
||||
|
@ -95,11 +92,12 @@ namespace Clipper2Lib {
|
|||
OutPt* pts = nullptr;
|
||||
PolyPath* polypath = nullptr;
|
||||
OutRecList* splits = nullptr;
|
||||
OutRec* recursive_split = nullptr;
|
||||
Rect64 bounds = {};
|
||||
Path64 path;
|
||||
bool is_open = false;
|
||||
bool horz_done = false;
|
||||
~OutRec() {
|
||||
|
||||
~OutRec() {
|
||||
if (splits) delete splits;
|
||||
// nb: don't delete the split pointers
|
||||
// as these are owned by ClipperBase's outrec_list_
|
||||
|
@ -110,7 +108,7 @@ namespace Clipper2Lib {
|
|||
//Important: UP and DOWN here are premised on Y-axis positive down
|
||||
//displays, which is the orientation used in Clipper's development.
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
struct Active {
|
||||
Point64 bot;
|
||||
Point64 top;
|
||||
|
@ -244,7 +242,7 @@ namespace Clipper2Lib {
|
|||
void SwapPositionsInAEL(Active& edge1, Active& edge2);
|
||||
OutRec* NewOutRec();
|
||||
OutPt* AddOutPt(const Active &e, const Point64& pt);
|
||||
OutPt* AddLocalMinPoly(Active &e1, Active &e2,
|
||||
OutPt* AddLocalMinPoly(Active &e1, Active &e2,
|
||||
const Point64& pt, bool is_new = false);
|
||||
OutPt* AddLocalMaxPoly(Active &e1, Active &e2, const Point64& pt);
|
||||
void DoHorizontal(Active &horz);
|
||||
|
@ -253,16 +251,15 @@ namespace Clipper2Lib {
|
|||
void DoTopOfScanbeam(const int64_t top_y);
|
||||
Active *DoMaxima(Active &e);
|
||||
void JoinOutrecPaths(Active &e1, Active &e2);
|
||||
void CompleteSplit(OutPt* op1, OutPt* op2, OutRec& outrec);
|
||||
void FixSelfIntersects(OutRec* outrec);
|
||||
void DoSplitOp(OutRec* outRec, OutPt* splitOp);
|
||||
|
||||
|
||||
inline void AddTrialHorzJoin(OutPt* op);
|
||||
void ConvertHorzSegsToJoins();
|
||||
void ProcessHorzJoins();
|
||||
|
||||
void Split(Active& e, const Point64& pt);
|
||||
inline void CheckJoinLeft(Active& e,
|
||||
inline void CheckJoinLeft(Active& e,
|
||||
const Point64& pt, bool check_curr_x = false);
|
||||
inline void CheckJoinRight(Active& e,
|
||||
const Point64& pt, bool check_curr_x = false);
|
||||
|
@ -327,12 +324,12 @@ namespace Clipper2Lib {
|
|||
|
||||
const PolyPath* Parent() const { return parent_; }
|
||||
|
||||
bool IsHole() const
|
||||
bool IsHole() const
|
||||
{
|
||||
unsigned lvl = Level();
|
||||
//Even levels except level 0
|
||||
return lvl && !(lvl & 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef typename std::vector<std::unique_ptr<PolyPath64>> PolyPath64List;
|
||||
|
@ -350,9 +347,9 @@ namespace Clipper2Lib {
|
|||
}
|
||||
|
||||
const PolyPath64* operator [] (size_t index) const
|
||||
{
|
||||
{
|
||||
return childs_[index].get(); //std::unique_ptr
|
||||
}
|
||||
}
|
||||
|
||||
const PolyPath64* Child(size_t index) const
|
||||
{
|
||||
|
@ -407,7 +404,7 @@ namespace Clipper2Lib {
|
|||
}
|
||||
|
||||
const PolyPathD* operator [] (size_t index) const
|
||||
{
|
||||
{
|
||||
return childs_[index].get();
|
||||
}
|
||||
|
||||
|
@ -480,7 +477,7 @@ namespace Clipper2Lib {
|
|||
return Execute(clip_type, fill_rule, closed_paths, dummy);
|
||||
}
|
||||
|
||||
bool Execute(ClipType clip_type, FillRule fill_rule,
|
||||
bool Execute(ClipType clip_type, FillRule fill_rule,
|
||||
Paths64& closed_paths, Paths64& open_paths)
|
||||
{
|
||||
closed_paths.clear();
|
||||
|
@ -552,12 +549,12 @@ namespace Clipper2Lib {
|
|||
void CheckCallback()
|
||||
{
|
||||
if(zCallbackD_)
|
||||
// if the user defined float point callback has been assigned
|
||||
// if the user defined float point callback has been assigned
|
||||
// then assign the proxy callback function
|
||||
ClipperBase::zCallback_ =
|
||||
ClipperBase::zCallback_ =
|
||||
std::bind(&ClipperD::ZCB, this, std::placeholders::_1,
|
||||
std::placeholders::_2, std::placeholders::_3,
|
||||
std::placeholders::_4, std::placeholders::_5);
|
||||
std::placeholders::_4, std::placeholders::_5);
|
||||
else
|
||||
ClipperBase::zCallback_ = nullptr;
|
||||
}
|
||||
|
@ -624,6 +621,6 @@ namespace Clipper2Lib {
|
|||
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
#endif // CLIPPER_ENGINE_H
|
||||
|
|
|
@ -159,9 +159,9 @@ EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths,
|
|||
|
||||
// RectClip & RectClipLines:
|
||||
EXTERN_DLL_EXPORT CPaths64 RectClip64(const CRect64& rect,
|
||||
const CPaths64 paths, bool convex_only = false);
|
||||
const CPaths64 paths);
|
||||
EXTERN_DLL_EXPORT CPathsD RectClipD(const CRectD& rect,
|
||||
const CPathsD paths, int precision = 2, bool convex_only = false);
|
||||
const CPathsD paths, int precision = 2);
|
||||
EXTERN_DLL_EXPORT CPaths64 RectClipLines64(const CRect64& rect,
|
||||
const CPaths64 paths);
|
||||
EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
* Author : Angus Johnson *
|
||||
* Date : 26 May 2023 *
|
||||
* Date : 16 July 2023 *
|
||||
* Website : http://www.angusj.com *
|
||||
* Copyright : Angus Johnson 2010-2023 *
|
||||
* Purpose : This module provides a simple interface to the Clipper Library *
|
||||
|
@ -312,75 +312,55 @@ namespace Clipper2Lib {
|
|||
}
|
||||
|
||||
static void OutlinePolyPath(std::ostream& os,
|
||||
bool isHole, size_t count, const std::string& preamble)
|
||||
size_t idx, bool isHole, size_t count, const std::string& preamble)
|
||||
{
|
||||
std::string plural = (count == 1) ? "." : "s.";
|
||||
if (isHole)
|
||||
{
|
||||
if (count)
|
||||
os << preamble << "+- Hole with " << count <<
|
||||
" nested polygon" << plural << std::endl;
|
||||
else
|
||||
os << preamble << "+- Hole" << std::endl;
|
||||
}
|
||||
os << preamble << "+- Hole (" << idx << ") contains " << count <<
|
||||
" nested polygon" << plural << std::endl;
|
||||
else
|
||||
{
|
||||
if (count)
|
||||
os << preamble << "+- Polygon with " << count <<
|
||||
os << preamble << "+- Polygon (" << idx << ") contains " << count <<
|
||||
" hole" << plural << std::endl;
|
||||
else
|
||||
os << preamble << "+- Polygon" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
static void OutlinePolyPath64(std::ostream& os, const PolyPath64& pp,
|
||||
std::string preamble, bool last_child)
|
||||
size_t idx, std::string preamble)
|
||||
{
|
||||
OutlinePolyPath(os, pp.IsHole(), pp.Count(), preamble);
|
||||
preamble += (!last_child) ? "| " : " ";
|
||||
if (pp.Count())
|
||||
{
|
||||
PolyPath64List::const_iterator it = pp.begin();
|
||||
for (; it < pp.end() - 1; ++it)
|
||||
OutlinePolyPath64(os, **it, preamble, false);
|
||||
OutlinePolyPath64(os, **it, preamble, true);
|
||||
}
|
||||
OutlinePolyPath(os, idx, pp.IsHole(), pp.Count(), preamble);
|
||||
for (size_t i = 0; i < pp.Count(); ++i)
|
||||
if (pp.Child(i)->Count())
|
||||
details::OutlinePolyPath64(os, *pp.Child(i), i, preamble + " ");
|
||||
}
|
||||
|
||||
static void OutlinePolyPathD(std::ostream& os, const PolyPathD& pp,
|
||||
std::string preamble, bool last_child)
|
||||
size_t idx, std::string preamble)
|
||||
{
|
||||
OutlinePolyPath(os, pp.IsHole(), pp.Count(), preamble);
|
||||
preamble += (!last_child) ? "| " : " ";
|
||||
if (pp.Count())
|
||||
{
|
||||
PolyPathDList::const_iterator it = pp.begin();
|
||||
for (; it < pp.end() - 1; ++it)
|
||||
OutlinePolyPathD(os, **it, preamble, false);
|
||||
OutlinePolyPathD(os, **it, preamble, true);
|
||||
}
|
||||
OutlinePolyPath(os, idx, pp.IsHole(), pp.Count(), preamble);
|
||||
for (size_t i = 0; i < pp.Count(); ++i)
|
||||
if (pp.Child(i)->Count())
|
||||
details::OutlinePolyPathD(os, *pp.Child(i), i, preamble + " ");
|
||||
}
|
||||
|
||||
} // end details namespace
|
||||
|
||||
inline std::ostream& operator<< (std::ostream& os, const PolyTree64& pp)
|
||||
{
|
||||
os << std::endl << "Polytree root" << std::endl;
|
||||
PolyPath64List::const_iterator it = pp.begin();
|
||||
for (; it < pp.end() - 1; ++it)
|
||||
details::OutlinePolyPath64(os, **it, " ", false);
|
||||
details::OutlinePolyPath64(os, **it, " ", true);
|
||||
std::string plural = (pp.Count() == 1) ? " polygon." : " polygons.";
|
||||
os << std::endl << "Polytree with " << pp.Count() << plural << std::endl;
|
||||
for (size_t i = 0; i < pp.Count(); ++i)
|
||||
if (pp.Child(i)->Count())
|
||||
details::OutlinePolyPath64(os, *pp.Child(i), i, " ");
|
||||
os << std::endl << std::endl;
|
||||
if (!pp.Level()) os << std::endl;
|
||||
return os;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<< (std::ostream& os, const PolyTreeD& pp)
|
||||
{
|
||||
PolyPathDList::const_iterator it = pp.begin();
|
||||
for (; it < pp.end() - 1; ++it)
|
||||
details::OutlinePolyPathD(os, **it, " ", false);
|
||||
details::OutlinePolyPathD(os, **it, " ", true);
|
||||
std::string plural = (pp.Count() == 1) ? " polygon." : " polygons.";
|
||||
os << std::endl << "Polytree with " << pp.Count() << plural << std::endl;
|
||||
for (size_t i = 0; i < pp.Count(); ++i)
|
||||
if (pp.Child(i)->Count())
|
||||
details::OutlinePolyPathD(os, *pp.Child(i), i, " ");
|
||||
os << std::endl << std::endl;
|
||||
if (!pp.Level()) os << std::endl;
|
||||
return os;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
* Author : Angus Johnson *
|
||||
* Date : 20 May 2023 *
|
||||
* Date : 5 August 2023 *
|
||||
* Website : http://www.angusj.com *
|
||||
* Copyright : Angus Johnson 2010-2023 *
|
||||
* Purpose : This is the main polygon clipping module *
|
||||
|
@ -419,6 +419,13 @@ namespace Clipper2Lib {
|
|||
return outrec;
|
||||
}
|
||||
|
||||
inline bool IsValidOwner(OutRec* outrec, OutRec* testOwner)
|
||||
{
|
||||
// prevent outrec owning itself either directly or indirectly
|
||||
while (testOwner && testOwner != outrec) testOwner = testOwner->owner;
|
||||
return !testOwner;
|
||||
}
|
||||
|
||||
inline void UncoupleOutRec(Active ae)
|
||||
{
|
||||
OutRec* outrec = ae.outrec;
|
||||
|
@ -483,6 +490,115 @@ namespace Clipper2Lib {
|
|||
outrec->owner = new_owner;
|
||||
}
|
||||
|
||||
static PointInPolygonResult PointInOpPolygon(const Point64& pt, OutPt* op)
|
||||
{
|
||||
if (op == op->next || op->prev == op->next)
|
||||
return PointInPolygonResult::IsOutside;
|
||||
|
||||
OutPt* op2 = op;
|
||||
do
|
||||
{
|
||||
if (op->pt.y != pt.y) break;
|
||||
op = op->next;
|
||||
} while (op != op2);
|
||||
if (op->pt.y == pt.y) // not a proper polygon
|
||||
return PointInPolygonResult::IsOutside;
|
||||
|
||||
bool is_above = op->pt.y < pt.y, starting_above = is_above;
|
||||
int val = 0;
|
||||
op2 = op->next;
|
||||
while (op2 != op)
|
||||
{
|
||||
if (is_above)
|
||||
while (op2 != op && op2->pt.y < pt.y) op2 = op2->next;
|
||||
else
|
||||
while (op2 != op && op2->pt.y > pt.y) op2 = op2->next;
|
||||
if (op2 == op) break;
|
||||
|
||||
// must have touched or crossed the pt.Y horizonal
|
||||
// and this must happen an even number of times
|
||||
|
||||
if (op2->pt.y == pt.y) // touching the horizontal
|
||||
{
|
||||
if (op2->pt.x == pt.x || (op2->pt.y == op2->prev->pt.y &&
|
||||
(pt.x < op2->prev->pt.x) != (pt.x < op2->pt.x)))
|
||||
return PointInPolygonResult::IsOn;
|
||||
|
||||
op2 = op2->next;
|
||||
if (op2 == op) break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pt.x < op2->pt.x && pt.x < op2->prev->pt.x);
|
||||
// do nothing because
|
||||
// we're only interested in edges crossing on the left
|
||||
else if ((pt.x > op2->prev->pt.x && pt.x > op2->pt.x))
|
||||
val = 1 - val; // toggle val
|
||||
else
|
||||
{
|
||||
double d = CrossProduct(op2->prev->pt, op2->pt, pt);
|
||||
if (d == 0) return PointInPolygonResult::IsOn;
|
||||
if ((d < 0) == is_above) val = 1 - val;
|
||||
}
|
||||
is_above = !is_above;
|
||||
op2 = op2->next;
|
||||
}
|
||||
|
||||
if (is_above != starting_above)
|
||||
{
|
||||
double d = CrossProduct(op2->prev->pt, op2->pt, pt);
|
||||
if (d == 0) return PointInPolygonResult::IsOn;
|
||||
if ((d < 0) == is_above) val = 1 - val;
|
||||
}
|
||||
|
||||
if (val == 0) return PointInPolygonResult::IsOutside;
|
||||
else return PointInPolygonResult::IsInside;
|
||||
}
|
||||
|
||||
inline Path64 GetCleanPath(OutPt* op)
|
||||
{
|
||||
Path64 result;
|
||||
OutPt* op2 = op;
|
||||
while (op2->next != op &&
|
||||
((op2->pt.x == op2->next->pt.x && op2->pt.x == op2->prev->pt.x) ||
|
||||
(op2->pt.y == op2->next->pt.y && op2->pt.y == op2->prev->pt.y))) op2 = op2->next;
|
||||
result.push_back(op2->pt);
|
||||
OutPt* prevOp = op2;
|
||||
op2 = op2->next;
|
||||
while (op2 != op)
|
||||
{
|
||||
if ((op2->pt.x != op2->next->pt.x || op2->pt.x != prevOp->pt.x) &&
|
||||
(op2->pt.y != op2->next->pt.y || op2->pt.y != prevOp->pt.y))
|
||||
{
|
||||
result.push_back(op2->pt);
|
||||
prevOp = op2;
|
||||
}
|
||||
op2 = op2->next;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline bool Path1InsidePath2(OutPt* op1, OutPt* op2)
|
||||
{
|
||||
// we need to make some accommodation for rounding errors
|
||||
// so we won't jump if the first vertex is found outside
|
||||
PointInPolygonResult result;
|
||||
int outside_cnt = 0;
|
||||
OutPt* op = op1;
|
||||
do
|
||||
{
|
||||
result = PointInOpPolygon(op->pt, op2);
|
||||
if (result == PointInPolygonResult::IsOutside) ++outside_cnt;
|
||||
else if (result == PointInPolygonResult::IsInside) --outside_cnt;
|
||||
op = op->next;
|
||||
} while (op != op1 && std::abs(outside_cnt) < 2);
|
||||
if (std::abs(outside_cnt) > 1) return (outside_cnt < 0);
|
||||
// since path1's location is still equivocal, check its midpoint
|
||||
Point64 mp = GetBounds(GetCleanPath(op1)).MidPoint();
|
||||
Path64 path2 = GetCleanPath(op2);
|
||||
return PointInPolygon(mp, path2) != PointInPolygonResult::IsOutside;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
@ -600,8 +716,6 @@ namespace Clipper2Lib {
|
|||
vertexLists.emplace_back(vertices);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// ReuseableDataContainer64 methods ...
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -677,7 +791,7 @@ namespace Clipper2Lib {
|
|||
{
|
||||
if (!minima_list_sorted_)
|
||||
{
|
||||
std::sort(minima_list_.begin(), minima_list_.end(), LocMinSorter());
|
||||
std::stable_sort(minima_list_.begin(), minima_list_.end(), LocMinSorter()); //#594
|
||||
minima_list_sorted_ = true;
|
||||
}
|
||||
LocalMinimaList::const_reverse_iterator i;
|
||||
|
@ -1298,7 +1412,7 @@ namespace Clipper2Lib {
|
|||
else
|
||||
SetOwner(&outrec, e->outrec);
|
||||
// nb: outRec.owner here is likely NOT the real
|
||||
// owner but this will be checked in DeepCheckOwner()
|
||||
// owner but this will be checked in RecursiveCheckOwners()
|
||||
}
|
||||
|
||||
UncoupleOutRec(e1);
|
||||
|
@ -1472,11 +1586,6 @@ namespace Clipper2Lib {
|
|||
return;
|
||||
}
|
||||
|
||||
// nb: area1 is the path's area *before* splitting, whereas area2 is
|
||||
// the area of the triangle containing splitOp & splitOp.next.
|
||||
// So the only way for these areas to have the same sign is if
|
||||
// the split triangle is larger than the path containing prevOp or
|
||||
// if there's more than one self=intersection.
|
||||
double area2 = AreaTriangle(ip, splitOp->pt, splitOp->next->pt);
|
||||
double absArea2 = std::fabs(area2);
|
||||
|
||||
|
@ -1496,18 +1605,17 @@ namespace Clipper2Lib {
|
|||
prevOp->next = newOp2;
|
||||
}
|
||||
|
||||
// area1 is the path's area *before* splitting, whereas area2 is
|
||||
// the area of the triangle containing splitOp & splitOp.next.
|
||||
// So the only way for these areas to have the same sign is if
|
||||
// the split triangle is larger than the path containing prevOp or
|
||||
// if there's more than one self-intersection.
|
||||
if (absArea2 >= 1 &&
|
||||
(absArea2 > absArea1 || (area2 > 0) == (area1 > 0)))
|
||||
{
|
||||
OutRec* newOr = NewOutRec();
|
||||
newOr->owner = outrec->owner;
|
||||
|
||||
if (using_polytree_)
|
||||
{
|
||||
if (!outrec->splits) outrec->splits = new OutRecList();
|
||||
outrec->splits->push_back(newOr);
|
||||
}
|
||||
|
||||
splitOp->outrec = newOr;
|
||||
splitOp->next->outrec = newOr;
|
||||
OutPt* newOp = new OutPt(ip, newOr);
|
||||
|
@ -1516,6 +1624,20 @@ namespace Clipper2Lib {
|
|||
newOr->pts = newOp;
|
||||
splitOp->prev = newOp;
|
||||
splitOp->next->next = newOp;
|
||||
|
||||
if (using_polytree_)
|
||||
{
|
||||
if (Path1InsidePath2(prevOp, newOp))
|
||||
{
|
||||
newOr->splits = new OutRecList();
|
||||
newOr->splits->push_back(outrec);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!outrec->splits) outrec->splits = new OutRecList();
|
||||
outrec->splits->push_back(newOr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1960,105 +2082,6 @@ namespace Clipper2Lib {
|
|||
} while (op != outrec->pts);
|
||||
}
|
||||
|
||||
inline Rect64 GetBounds(OutPt* op)
|
||||
{
|
||||
Rect64 result(op->pt.x, op->pt.y, op->pt.x, op->pt.y);
|
||||
OutPt* op2 = op->next;
|
||||
while (op2 != op)
|
||||
{
|
||||
if (op2->pt.x < result.left) result.left = op2->pt.x;
|
||||
else if (op2->pt.x > result.right) result.right = op2->pt.x;
|
||||
if (op2->pt.y < result.top) result.top = op2->pt.y;
|
||||
else if (op2->pt.y > result.bottom) result.bottom = op2->pt.y;
|
||||
op2 = op2->next;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static PointInPolygonResult PointInOpPolygon(const Point64& pt, OutPt* op)
|
||||
{
|
||||
if (op == op->next || op->prev == op->next)
|
||||
return PointInPolygonResult::IsOutside;
|
||||
|
||||
OutPt* op2 = op;
|
||||
do
|
||||
{
|
||||
if (op->pt.y != pt.y) break;
|
||||
op = op->next;
|
||||
} while (op != op2);
|
||||
if (op->pt.y == pt.y) // not a proper polygon
|
||||
return PointInPolygonResult::IsOutside;
|
||||
|
||||
bool is_above = op->pt.y < pt.y, starting_above = is_above;
|
||||
int val = 0;
|
||||
op2 = op->next;
|
||||
while (op2 != op)
|
||||
{
|
||||
if (is_above)
|
||||
while (op2 != op && op2->pt.y < pt.y) op2 = op2->next;
|
||||
else
|
||||
while (op2 != op && op2->pt.y > pt.y) op2 = op2->next;
|
||||
if (op2 == op) break;
|
||||
|
||||
// must have touched or crossed the pt.Y horizonal
|
||||
// and this must happen an even number of times
|
||||
|
||||
if (op2->pt.y == pt.y) // touching the horizontal
|
||||
{
|
||||
if (op2->pt.x == pt.x || (op2->pt.y == op2->prev->pt.y &&
|
||||
(pt.x < op2->prev->pt.x) != (pt.x < op2->pt.x)))
|
||||
return PointInPolygonResult::IsOn;
|
||||
|
||||
op2 = op2->next;
|
||||
if (op2 == op) break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pt.x < op2->pt.x && pt.x < op2->prev->pt.x);
|
||||
// do nothing because
|
||||
// we're only interested in edges crossing on the left
|
||||
else if ((pt.x > op2->prev->pt.x && pt.x > op2->pt.x))
|
||||
val = 1 - val; // toggle val
|
||||
else
|
||||
{
|
||||
double d = CrossProduct(op2->prev->pt, op2->pt, pt);
|
||||
if (d == 0) return PointInPolygonResult::IsOn;
|
||||
if ((d < 0) == is_above) val = 1 - val;
|
||||
}
|
||||
is_above = !is_above;
|
||||
op2 = op2->next;
|
||||
}
|
||||
|
||||
if (is_above != starting_above)
|
||||
{
|
||||
double d = CrossProduct(op2->prev->pt, op2->pt, pt);
|
||||
if (d == 0) return PointInPolygonResult::IsOn;
|
||||
if ((d < 0) == is_above) val = 1 - val;
|
||||
}
|
||||
|
||||
if (val == 0) return PointInPolygonResult::IsOutside;
|
||||
else return PointInPolygonResult::IsInside;
|
||||
}
|
||||
|
||||
inline bool Path1InsidePath2(OutPt* op1, OutPt* op2)
|
||||
{
|
||||
// we need to make some accommodation for rounding errors
|
||||
// so we won't jump if the first vertex is found outside
|
||||
int outside_cnt = 0;
|
||||
OutPt* op = op1;
|
||||
do
|
||||
{
|
||||
PointInPolygonResult result = PointInOpPolygon(op->pt, op2);
|
||||
if (result == PointInPolygonResult::IsOutside) ++outside_cnt;
|
||||
else if (result == PointInPolygonResult::IsInside) --outside_cnt;
|
||||
op = op->next;
|
||||
} while (op != op1 && std::abs(outside_cnt) < 2);
|
||||
if (std::abs(outside_cnt) > 1) return (outside_cnt < 0);
|
||||
// since path1's location is still equivocal, check its midpoint
|
||||
Point64 mp = GetBounds(op).MidPoint();
|
||||
return PointInOpPolygon(mp, op2) == PointInPolygonResult::IsInside;
|
||||
}
|
||||
|
||||
inline bool SetHorzSegHeadingForward(HorzSegment& hs, OutPt* opP, OutPt* opN)
|
||||
{
|
||||
if (opP->pt.x == opN->pt.x) return false;
|
||||
|
@ -2126,8 +2149,8 @@ namespace Clipper2Lib {
|
|||
{
|
||||
for (hs2 = hs1 + 1; hs2 != hs_end; ++hs2)
|
||||
{
|
||||
if (hs2->left_op->pt.x >= hs1->right_op->pt.x) break;
|
||||
if (hs2->left_to_right == hs1->left_to_right ||
|
||||
if ((hs2->left_op->pt.x >= hs1->right_op->pt.x) ||
|
||||
(hs2->left_to_right == hs1->left_to_right) ||
|
||||
(hs2->right_op->pt.x <= hs1->left_op->pt.x)) continue;
|
||||
int64_t curr_y = hs1->left_op->pt.y;
|
||||
if (hs1->left_to_right)
|
||||
|
@ -2160,6 +2183,7 @@ namespace Clipper2Lib {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void ClipperBase::ProcessHorzJoins()
|
||||
{
|
||||
for (const HorzJoin& j : horz_join_list_)
|
||||
|
@ -2174,7 +2198,7 @@ namespace Clipper2Lib {
|
|||
op1b->prev = op2b;
|
||||
op2b->next = op1b;
|
||||
|
||||
if (or1 == or2)
|
||||
if (or1 == or2) // 'join' is really a split
|
||||
{
|
||||
or2 = NewOutRec();
|
||||
or2->pts = op1b;
|
||||
|
@ -2185,21 +2209,18 @@ namespace Clipper2Lib {
|
|||
or1->pts->outrec = or1;
|
||||
}
|
||||
|
||||
if (using_polytree_)
|
||||
if (using_polytree_) //#498, #520, #584, D#576
|
||||
{
|
||||
if (Path1InsidePath2(or2->pts, or1->pts))
|
||||
if (Path1InsidePath2(or1->pts, or2->pts))
|
||||
{
|
||||
or2->owner = or1->owner;
|
||||
SetOwner(or1, or2);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetOwner(or2, or1);
|
||||
if (!or1->splits) or1->splits = new OutRecList();
|
||||
or1->splits->push_back(or2); //(#520)
|
||||
}
|
||||
else if (Path1InsidePath2(or1->pts, or2->pts))
|
||||
SetOwner(or1, or2);
|
||||
else
|
||||
{
|
||||
if (!or1->splits) or1->splits = new OutRecList();
|
||||
or1->splits->push_back(or2); //(#498)
|
||||
or2->owner = or1;
|
||||
or1->splits->push_back(or2);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -2478,7 +2499,6 @@ namespace Clipper2Lib {
|
|||
#endif
|
||||
AddTrialHorzJoin(op);
|
||||
}
|
||||
OutRec* currHorzOutrec = horz.outrec;
|
||||
|
||||
while (true) // loop through consec. horizontal edges
|
||||
{
|
||||
|
@ -2558,9 +2578,8 @@ namespace Clipper2Lib {
|
|||
e = horz.prev_in_ael;
|
||||
}
|
||||
|
||||
if (horz.outrec && horz.outrec != currHorzOutrec)
|
||||
if (horz.outrec)
|
||||
{
|
||||
currHorzOutrec = horz.outrec;
|
||||
//nb: The outrec containining the op returned by IntersectEdges
|
||||
//above may no longer be associated with horzEdge.
|
||||
AddTrialHorzJoin(GetLastOp(horz));
|
||||
|
@ -2597,7 +2616,12 @@ namespace Clipper2Lib {
|
|||
ResetHorzDirection(horz, vertex_max, horz_left, horz_right);
|
||||
}
|
||||
|
||||
if (IsHotEdge(horz)) AddOutPt(horz, horz.top);
|
||||
if (IsHotEdge(horz))
|
||||
{
|
||||
OutPt* op = AddOutPt(horz, horz.top);
|
||||
AddTrialHorzJoin(op);
|
||||
}
|
||||
|
||||
UpdateEdgeIntoAEL(&horz); // end of an intermediate horiz.
|
||||
}
|
||||
|
||||
|
@ -2824,8 +2848,8 @@ namespace Clipper2Lib {
|
|||
if (!outrec->bounds.IsEmpty()) return true;
|
||||
CleanCollinear(outrec);
|
||||
if (!outrec->pts ||
|
||||
!BuildPath64(outrec->pts, ReverseSolution, false, outrec->path))
|
||||
return false;
|
||||
!BuildPath64(outrec->pts, ReverseSolution, false, outrec->path)){
|
||||
return false;}
|
||||
outrec->bounds = GetBounds(outrec->path);
|
||||
return true;
|
||||
}
|
||||
|
@ -2834,9 +2858,15 @@ namespace Clipper2Lib {
|
|||
{
|
||||
for (auto split : *splits)
|
||||
{
|
||||
if(split == outrec || split == outrec->owner) continue;
|
||||
else if (split->splits && CheckSplitOwner(outrec, split->splits)) return true;
|
||||
else if (CheckBounds(split) && split->bounds.Contains(outrec->bounds) &&
|
||||
split = GetRealOutRec(split);
|
||||
if(!split || split->recursive_split == outrec) continue;
|
||||
split->recursive_split = outrec; // prevent infinite loops
|
||||
|
||||
if (split->splits && CheckSplitOwner(outrec, split->splits))
|
||||
return true;
|
||||
else if (CheckBounds(split) &&
|
||||
IsValidOwner(outrec, split) &&
|
||||
split->bounds.Contains(outrec->bounds) &&
|
||||
Path1InsidePath2(outrec->pts, split->pts))
|
||||
{
|
||||
outrec->owner = split; //found in split
|
||||
|
@ -3021,8 +3051,11 @@ namespace Clipper2Lib {
|
|||
if (has_open_paths_)
|
||||
open_paths.reserve(outrec_list_.size());
|
||||
|
||||
for (OutRec* outrec : outrec_list_)
|
||||
// outrec_list_.size() is not static here because
|
||||
// BuildPathD below can indirectly add additional OutRec //#607
|
||||
for (size_t i = 0; i < outrec_list_.size(); ++i)
|
||||
{
|
||||
OutRec* outrec = outrec_list_[i];
|
||||
if (!outrec || !outrec->pts) continue;
|
||||
if (outrec->is_open)
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
* Author : Angus Johnson *
|
||||
* Date : 26 May 2023 *
|
||||
* Date : 7 August 2023 *
|
||||
* Website : http://www.angusj.com *
|
||||
* Copyright : Angus Johnson 2010-2023 *
|
||||
* Purpose : Path Offset (Inflate/Shrink) *
|
||||
|
@ -78,8 +78,7 @@ inline double Hypot(double x, double y)
|
|||
}
|
||||
|
||||
inline PointD NormalizeVector(const PointD& vec)
|
||||
{
|
||||
|
||||
{
|
||||
double h = Hypot(vec.x, vec.y);
|
||||
if (AlmostZero(h)) return PointD(0,0);
|
||||
double inverseHypot = 1 / h;
|
||||
|
@ -318,7 +317,10 @@ void ClipperOffset::OffsetPoint(Group& group, Path64& path, size_t j, size_t k)
|
|||
if (sin_a > 1.0) sin_a = 1.0;
|
||||
else if (sin_a < -1.0) sin_a = -1.0;
|
||||
|
||||
if (deltaCallback64_) group_delta_ = deltaCallback64_(path, norms, j, k);
|
||||
if (deltaCallback64_) {
|
||||
group_delta_ = deltaCallback64_(path, norms, j, k);
|
||||
if (group.is_reversed) group_delta_ = -group_delta_;
|
||||
}
|
||||
if (std::fabs(group_delta_) <= floating_point_tolerance)
|
||||
{
|
||||
group.path.push_back(path[j]);
|
||||
|
@ -352,6 +354,17 @@ void ClipperOffset::OffsetPoint(Group& group, Path64& path, size_t j, size_t k)
|
|||
|
||||
void ClipperOffset::OffsetPolygon(Group& group, Path64& path)
|
||||
{
|
||||
// when the path is contracting, make sure
|
||||
// there is sufficient space to do so. //#593
|
||||
// nb: this will have a small impact on performance
|
||||
double a = Area(path);
|
||||
// contracting when orientation is opposite offset direction
|
||||
if ((a < 0) != (group_delta_ < 0))
|
||||
{
|
||||
Rect64 rec = GetBounds(path);
|
||||
if (std::fabs(group_delta_) * 2 > rec.Width()) return;
|
||||
}
|
||||
|
||||
for (Path64::size_type j = 0, k = path.size() -1; j < path.size(); k = j, ++j)
|
||||
OffsetPoint(group, path, j, k);
|
||||
group.paths_out.push_back(group.path);
|
||||
|
@ -602,7 +615,6 @@ void ClipperOffset::Execute(double delta, Paths64& paths)
|
|||
if (!solution.size()) return;
|
||||
|
||||
paths = solution;
|
||||
/**/
|
||||
//clean up self-intersections ...
|
||||
Clipper64 c;
|
||||
c.PreserveCollinear = false;
|
||||
|
@ -618,7 +630,6 @@ void ClipperOffset::Execute(double delta, Paths64& paths)
|
|||
c.Execute(ClipType::Union, FillRule::Negative, paths);
|
||||
else
|
||||
c.Execute(ClipType::Union, FillRule::Positive, paths);
|
||||
/**/
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
* Author : Angus Johnson *
|
||||
* Date : 30 May 2023 *
|
||||
* Date : 6 August 2023 *
|
||||
* Website : http://www.angusj.com *
|
||||
* Copyright : Angus Johnson 2010-2023 *
|
||||
* Purpose : FAST rectangular clipping *
|
||||
|
@ -474,7 +474,7 @@ namespace Clipper2Lib {
|
|||
// intersect pt but we'll also need the first intersect pt (ip2)
|
||||
loc = prev;
|
||||
GetIntersection(rect_as_path_, prev_pt, path[i], loc, ip2);
|
||||
if (crossing_prev != Location::Inside)
|
||||
if (crossing_prev != Location::Inside && crossing_prev != loc) //579
|
||||
AddCorner(crossing_prev, loc);
|
||||
|
||||
if (first_cross_ == Location::Inside)
|
||||
|
@ -847,7 +847,7 @@ namespace Clipper2Lib {
|
|||
//clean up after every loop
|
||||
op_container_ = std::deque<OutPt2>();
|
||||
results_.clear();
|
||||
for (OutPt2List edge : edges_) edge.clear();
|
||||
for (OutPt2List &edge : edges_) edge.clear();
|
||||
start_locs_.clear();
|
||||
}
|
||||
return result;
|
||||
|
|
Loading…
Reference in New Issue