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:
jean-pierre charras 2023-08-22 17:17:15 +02:00
parent 76cd637895
commit 235006a1b7
7 changed files with 264 additions and 243 deletions

View File

@ -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,

View File

@ -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

View File

@ -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,

View File

@ -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;

View File

@ -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)
{

View File

@ -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);
/**/
}

View File

@ -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;