Update Clipper2 to 9d946d7

Fixed a number of smaller issues as well as overlapping segment
cleaning.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/14682
This commit is contained in:
Seth Hillbrand 2023-06-01 11:37:20 -07:00
parent d854e2bfe8
commit fe62a3f985
9 changed files with 467 additions and 317 deletions

View File

@ -428,7 +428,7 @@ namespace Clipper2Lib
}
template <typename T1, typename T2>
inline Path<T1> ScalePath(const Path<T2>& path,
inline Path<T1> ScalePath(const Path<T2>& path,
double scale, int& error_code)
{
return ScalePath<T1, T2>(path, scale, scale, error_code);

View File

@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 22 April 2023 *
* Date : 15 May 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : This is the main polygon clipping module *
@ -13,6 +13,7 @@
constexpr auto CLIPPER2_VERSION = "1.2.2";
#include <cstdlib>
#include <stdint.h>
#include <iostream>
#include <queue>
#include <vector>
@ -25,7 +26,6 @@ constexpr auto CLIPPER2_VERSION = "1.2.2";
#ifdef None
#undef None
#endif
namespace Clipper2Lib {
struct Scanline;
@ -38,7 +38,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 +46,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));
}
@ -99,7 +99,7 @@ namespace Clipper2Lib {
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 +110,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;
@ -183,6 +183,20 @@ namespace Clipper2Lib {
typedef std::vector<LocalMinima_ptr> LocalMinimaList;
typedef std::vector<IntersectNode> IntersectNodeList;
// ReuseableDataContainer64 ------------------------------------------------
class ReuseableDataContainer64 {
private:
friend class ClipperBase;
LocalMinimaList minima_list_;
std::vector<Vertex*> vertex_lists_;
void AddLocMin(Vertex& vert, PathType polytype, bool is_open);
public:
virtual ~ReuseableDataContainer64();
void Clear();
void AddPaths(const Paths64& paths, PathType polytype, bool is_open);
};
// ClipperBase -------------------------------------------------------------
class ClipperBase {
@ -230,7 +244,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);
@ -242,13 +256,13 @@ namespace Clipper2Lib {
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);
@ -260,7 +274,7 @@ namespace Clipper2Lib {
bool ExecuteInternal(ClipType ct, FillRule ft, bool use_polytrees);
void CleanCollinear(OutRec* outrec);
bool CheckBounds(OutRec* outrec);
bool CheckSplitOwner(OutRec* outrec);
bool CheckSplitOwner(OutRec* outrec, OutRecList* splits);
void RecursiveCheckOwners(OutRec* outrec, PolyPath* polypath);
#ifdef USINGZ
ZCallback64 zCallback_ = nullptr;
@ -275,6 +289,7 @@ namespace Clipper2Lib {
bool PreserveCollinear = true;
bool ReverseSolution = false;
void Clear();
void AddReuseableData(const ReuseableDataContainer64& reuseable_data);
#ifdef USINGZ
int64_t DefaultZ = 0;
#endif
@ -312,12 +327,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;
@ -335,9 +350,9 @@ namespace Clipper2Lib {
}
const PolyPath64* operator [] (size_t index) const
{
return childs_[index].get();
}
{
return childs_[index].get(); //std::unique_ptr
}
const PolyPath64* Child(size_t index) const
{
@ -379,12 +394,12 @@ namespace Clipper2Lib {
class PolyPathD : public PolyPath {
private:
PolyPathDList childs_;
double inv_scale_;
double scale_;
PathD polygon_;
public:
explicit PolyPathD(PolyPathD* parent = nullptr) : PolyPath(parent)
{
inv_scale_ = parent ? parent->inv_scale_ : 1.0;
scale_ = parent ? parent->scale_ : 1.0;
}
~PolyPathD() {
@ -392,7 +407,7 @@ namespace Clipper2Lib {
}
const PolyPathD* operator [] (size_t index) const
{
{
return childs_[index].get();
}
@ -404,14 +419,14 @@ namespace Clipper2Lib {
PolyPathDList::const_iterator begin() const { return childs_.cbegin(); }
PolyPathDList::const_iterator end() const { return childs_.cend(); }
void SetInvScale(double value) { inv_scale_ = value; }
double InvScale() { return inv_scale_; }
void SetScale(double value) { scale_ = value; }
double Scale() { return scale_; }
PolyPathD* AddChild(const Path64& path) override
{
int error_code = 0;
auto p = std::make_unique<PolyPathD>(this);
PolyPathD* result = childs_.emplace_back(std::move(p)).get();
result->polygon_ = ScalePath<double, int64_t>(path, inv_scale_, error_code);
result->polygon_ = ScalePath<double, int64_t>(path, scale_, error_code);
return result;
}
@ -465,7 +480,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();
@ -537,12 +552,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;
}
@ -599,7 +614,7 @@ namespace Clipper2Lib {
if (ExecuteInternal(clip_type, fill_rule, true))
{
polytree.Clear();
polytree.SetInvScale(invScale_);
polytree.SetScale(invScale_);
open_paths.clear();
BuildTreeD(polytree, open_paths);
}
@ -609,6 +624,6 @@ namespace Clipper2Lib {
};
} // namespace
} // namespace
#endif // CLIPPER_ENGINE_H

View File

@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 23 March 2023 *
* Date : 30 May 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : This module exports the Clipper2 Library (ie DLL/so) *
@ -157,14 +157,14 @@ EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths,
int precision = 2, double miter_limit = 2.0,
double arc_tolerance = 0.0, bool reverse_solution = false);
// ExecuteRectClip & ExecuteRectClipLines:
EXTERN_DLL_EXPORT CPaths64 ExecuteRectClip64(const CRect64& rect,
// RectClip & RectClipLines:
EXTERN_DLL_EXPORT CPaths64 RectClip64(const CRect64& rect,
const CPaths64 paths, bool convex_only = false);
EXTERN_DLL_EXPORT CPathsD ExecuteRectClipD(const CRectD& rect,
EXTERN_DLL_EXPORT CPathsD RectClipD(const CRectD& rect,
const CPathsD paths, int precision = 2, bool convex_only = false);
EXTERN_DLL_EXPORT CPaths64 ExecuteRectClipLines64(const CRect64& rect,
EXTERN_DLL_EXPORT CPaths64 RectClipLines64(const CRect64& rect,
const CPaths64 paths);
EXTERN_DLL_EXPORT CPathsD ExecuteRectClipLinesD(const CRectD& rect,
EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect,
const CPathsD paths, int precision = 2);
//////////////////////////////////////////////////////
@ -381,19 +381,17 @@ EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths,
return CreateCPathsD(result, 1/scale);
}
EXTERN_DLL_EXPORT CPaths64 ExecuteRectClip64(const CRect64& rect,
const CPaths64 paths, bool convex_only)
EXTERN_DLL_EXPORT CPaths64 RectClip64(const CRect64& rect, const CPaths64 paths)
{
if (CRectIsEmpty(rect) || !paths) return nullptr;
Rect64 r64 = CRectToRect(rect);
class RectClip rc(r64);
class RectClip64 rc(r64);
Paths64 pp = ConvertCPaths64(paths);
Paths64 result = rc.Execute(pp, convex_only);
Paths64 result = rc.Execute(pp);
return CreateCPaths64(result);
}
EXTERN_DLL_EXPORT CPathsD ExecuteRectClipD(const CRectD& rect,
const CPathsD paths, int precision, bool convex_only)
EXTERN_DLL_EXPORT CPathsD RectClipD(const CRectD& rect, const CPathsD paths, int precision)
{
if (CRectIsEmpty(rect) || !paths) return nullptr;
if (precision < -8 || precision > 8) return nullptr;
@ -402,30 +400,30 @@ EXTERN_DLL_EXPORT CPathsD ExecuteRectClipD(const CRectD& rect,
RectD r = CRectToRect(rect);
Rect64 rec = ScaleRect<int64_t, double>(r, scale);
Paths64 pp = ConvertCPathsD(paths, scale);
class RectClip rc(rec);
Paths64 result = rc.Execute(pp, convex_only);
class RectClip64 rc(rec);
Paths64 result = rc.Execute(pp);
return CreateCPathsD(result, 1/scale);
}
EXTERN_DLL_EXPORT CPaths64 ExecuteRectClipLines64(const CRect64& rect,
EXTERN_DLL_EXPORT CPaths64 RectClipLines64(const CRect64& rect,
const CPaths64 paths)
{
if (CRectIsEmpty(rect) || !paths) return nullptr;
Rect64 r = CRectToRect(rect);
class RectClipLines rcl (r);
class RectClipLines64 rcl (r);
Paths64 pp = ConvertCPaths64(paths);
Paths64 result = rcl.Execute(pp);
return CreateCPaths64(result);
}
EXTERN_DLL_EXPORT CPathsD ExecuteRectClipLinesD(const CRectD& rect,
EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect,
const CPathsD paths, int precision)
{
if (CRectIsEmpty(rect) || !paths) return nullptr;
if (precision < -8 || precision > 8) return nullptr;
const double scale = std::pow(10, precision);
Rect64 r = ScaleRect<int64_t, double>(CRectToRect(rect), scale);
class RectClipLines rcl(r);
class RectClipLines64 rcl(r);
Paths64 pp = ConvertCPathsD(paths, scale);
Paths64 result = rcl.Execute(pp);
return CreateCPathsD(result, 1/scale);

View File

@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 21 April 2023 *
* Date : 26 May 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : This module provides a simple interface to the Clipper Library *
@ -161,60 +161,61 @@ namespace Clipper2Lib {
return ScalePaths<double, int64_t>(solution, 1 / scale, error_code);
}
inline Path64 TranslatePath(const Path64& path, int64_t dx, int64_t dy)
template <typename T>
inline Path<T> TranslatePath(const Path<T>& path, T dx, T dy)
{
Path64 result;
Path<T> result;
result.reserve(path.size());
std::transform(path.begin(), path.end(), back_inserter(result),
[dx, dy](const auto& pt) { return Point64(pt.x + dx, pt.y +dy); });
[dx, dy](const auto& pt) { return Point<T>(pt.x + dx, pt.y +dy); });
return result;
}
inline Path64 TranslatePath(const Path64& path, int64_t dx, int64_t dy)
{
return TranslatePath<int64_t>(path, dx, dy);
}
inline PathD TranslatePath(const PathD& path, double dx, double dy)
{
PathD result;
result.reserve(path.size());
std::transform(path.begin(), path.end(), back_inserter(result),
[dx, dy](const auto& pt) { return PointD(pt.x + dx, pt.y + dy); });
return TranslatePath<double>(path, dx, dy);
}
template <typename T>
inline Paths<T> TranslatePaths(const Paths<T>& paths, T dx, T dy)
{
Paths<T> result;
result.reserve(paths.size());
std::transform(paths.begin(), paths.end(), back_inserter(result),
[dx, dy](const auto& path) { return TranslatePath(path, dx, dy); });
return result;
}
inline Paths64 TranslatePaths(const Paths64& paths, int64_t dx, int64_t dy)
{
Paths64 result;
result.reserve(paths.size());
std::transform(paths.begin(), paths.end(), back_inserter(result),
[dx, dy](const auto& path) { return TranslatePath(path, dx, dy); });
return result;
return TranslatePaths<int64_t>(paths, dx, dy);
}
inline PathsD TranslatePaths(const PathsD& paths, double dx, double dy)
{
PathsD result;
result.reserve(paths.size());
std::transform(paths.begin(), paths.end(), back_inserter(result),
[dx, dy](const auto& path) { return TranslatePath(path, dx, dy); });
return result;
return TranslatePaths<double>(paths, dx, dy);
}
inline Paths64 ExecuteRectClip(const Rect64& rect,
const Paths64& paths, bool convex_only)
inline Paths64 RectClip(const Rect64& rect, const Paths64& paths)
{
if (rect.IsEmpty() || paths.empty()) return Paths64();
RectClip rc(rect);
return rc.Execute(paths, convex_only);
RectClip64 rc(rect);
return rc.Execute(paths);
}
inline Paths64 ExecuteRectClip(const Rect64& rect,
const Path64& path, bool convex_only)
inline Paths64 RectClip(const Rect64& rect, const Path64& path)
{
if (rect.IsEmpty() || path.empty()) return Paths64();
RectClip rc(rect);
return rc.Execute(Paths64{ path }, convex_only);
RectClip64 rc(rect);
return rc.Execute(Paths64{ path });
}
inline PathsD ExecuteRectClip(const RectD& rect,
const PathsD& paths, bool convex_only, int precision = 2)
inline PathsD RectClip(const RectD& rect, const PathsD& paths, int precision = 2)
{
if (rect.IsEmpty() || paths.empty()) return PathsD();
int error_code = 0;
@ -222,32 +223,31 @@ namespace Clipper2Lib {
if (error_code) return PathsD();
const double scale = std::pow(10, precision);
Rect64 r = ScaleRect<int64_t, double>(rect, scale);
RectClip rc(r);
RectClip64 rc(r);
Paths64 pp = ScalePaths<int64_t, double>(paths, scale, error_code);
if (error_code) return PathsD(); // ie: error_code result is lost
return ScalePaths<double, int64_t>(
rc.Execute(pp, convex_only), 1 / scale, error_code);
rc.Execute(pp), 1 / scale, error_code);
}
inline PathsD ExecuteRectClip(const RectD& rect,
const PathD& path, bool convex_only, int precision = 2)
inline PathsD RectClip(const RectD& rect, const PathD& path, int precision = 2)
{
return ExecuteRectClip(rect, PathsD{ path }, convex_only, precision);
return RectClip(rect, PathsD{ path }, precision);
}
inline Paths64 ExecuteRectClipLines(const Rect64& rect, const Paths64& lines)
inline Paths64 RectClipLines(const Rect64& rect, const Paths64& lines)
{
if (rect.IsEmpty() || lines.empty()) return Paths64();
RectClipLines rcl(rect);
RectClipLines64 rcl(rect);
return rcl.Execute(lines);
}
inline Paths64 ExecuteRectClipLines(const Rect64& rect, const Path64& line)
inline Paths64 RectClipLines(const Rect64& rect, const Path64& line)
{
return ExecuteRectClipLines(rect, Paths64{ line });
return RectClipLines(rect, Paths64{ line });
}
inline PathsD ExecuteRectClipLines(const RectD& rect, const PathsD& lines, int precision = 2)
inline PathsD RectClipLines(const RectD& rect, const PathsD& lines, int precision = 2)
{
if (rect.IsEmpty() || lines.empty()) return PathsD();
int error_code = 0;
@ -255,16 +255,16 @@ namespace Clipper2Lib {
if (error_code) return PathsD();
const double scale = std::pow(10, precision);
Rect64 r = ScaleRect<int64_t, double>(rect, scale);
RectClipLines rcl(r);
RectClipLines64 rcl(r);
Paths64 p = ScalePaths<int64_t, double>(lines, scale, error_code);
if (error_code) return PathsD();
p = rcl.Execute(p);
return ScalePaths<double, int64_t>(p, 1 / scale, error_code);
}
inline PathsD ExecuteRectClipLines(const RectD& rect, const PathD& line, int precision = 2)
inline PathsD RectClipLines(const RectD& rect, const PathD& line, int precision = 2)
{
return ExecuteRectClipLines(rect, PathsD{ line }, precision);
return RectClipLines(rect, PathsD{ line }, precision);
}
namespace details
@ -290,14 +290,9 @@ namespace Clipper2Lib {
{
// return false if this child isn't fully contained by its parent
// the following algorithm is a bit too crude, and doesn't account
// for rounding errors. A better algorithm is to return false when
// consecutive vertices are found outside the parent's polygon.
//const Path64& path = pp.Polygon();
//if (std::any_of(child->Polygon().cbegin(), child->Polygon().cend(),
// [path](const auto& pt) {return (PointInPolygon(pt, path) ==
// PointInPolygonResult::IsOutside); })) return false;
// checking for a single vertex outside is a bit too crude since
// it doesn't account for rounding errors. It's better to check
// for consecutive vertices found outside the parent's polygon.
int outsideCnt = 0;
for (const Point64& pt : child->Polygon())
@ -490,6 +485,39 @@ namespace Clipper2Lib {
return result;
}
#ifdef USINGZ
template<typename T2, std::size_t N>
inline Path64 MakePathZ(const T2(&list)[N])
{
static_assert(N % 3 == 0 && std::numeric_limits<T2>::is_integer,
"MakePathZ requires integer values in multiples of 3");
std::size_t size = N / 3;
Path64 result(size);
for (size_t i = 0; i < size; ++i)
result[i] = Point64(list[i * 3],
list[i * 3 + 1], list[i * 3 + 2]);
return result;
}
template<typename T2, std::size_t N>
inline PathD MakePathZD(const T2(&list)[N])
{
static_assert(N % 3 == 0,
"MakePathZD requires values in multiples of 3");
std::size_t size = N / 3;
PathD result(size);
if constexpr (std::numeric_limits<T2>::is_integer)
for (size_t i = 0; i < size; ++i)
result[i] = PointD(list[i * 3],
list[i * 3 + 1], list[i * 3 + 2]);
else
for (size_t i = 0; i < size; ++i)
result[i] = PointD(list[i * 3], list[i * 3 + 1],
static_cast<int64_t>(list[i * 3 + 2]));
return result;
}
#endif
inline Path64 TrimCollinear(const Path64& p, bool is_open_path = false)
{
size_t len = p.size();

View File

@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 22 March 2023 *
* Date : 15 May 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : Path Offset (Inflate/Shrink) *
@ -24,6 +24,7 @@ enum class EndType {Polygon, Joined, Butt, Square, Round};
//Joined : offsets both sides of a path, with joined ends
//Polygon: offsets only one side of a closed path
typedef std::function<double(const Path64& path, const PathD& path_normals, size_t curr_idx, size_t prev_idx)> DeltaCallback64;
class ClipperOffset {
private:
@ -43,7 +44,6 @@ private:
int error_code_ = 0;
double delta_ = 0.0;
double group_delta_ = 0.0;
double abs_group_delta_ = 0.0;
double temp_lim_ = 0.0;
double steps_per_rad_ = 0.0;
double step_sin_ = 0.0;
@ -62,6 +62,7 @@ private:
#ifdef USINGZ
ZCallback64 zCallback64_ = nullptr;
#endif
DeltaCallback64 deltaCallback64_ = nullptr;
void DoSquare(Group& group, const Path64& path, size_t j, size_t k);
void DoMiter(Group& group, const Path64& path, size_t j, size_t k, double cos_a);
@ -70,7 +71,7 @@ private:
void OffsetPolygon(Group& group, Path64& path);
void OffsetOpenJoined(Group& group, Path64& path);
void OffsetOpenPath(Group& group, Path64& path);
void OffsetPoint(Group& group, Path64& path, size_t j, size_t& k);
void OffsetPoint(Group& group, Path64& path, size_t j, size_t k);
void DoGroupOffset(Group &group);
void ExecuteInternal(double delta);
public:
@ -91,6 +92,7 @@ public:
void Execute(double delta, Paths64& paths);
void Execute(double delta, PolyTree64& polytree);
void Execute(DeltaCallback64 delta_cb, Paths64& paths);
double MiterLimit() const { return miter_limit_; }
void MiterLimit(double miter_limit) { miter_limit_ = miter_limit; }
@ -108,6 +110,8 @@ public:
#ifdef USINGZ
void SetZCallback(ZCallback64 cb) { zCallback64_ = cb; }
#endif
void SetDeltaCallback(DeltaCallback64 cb) { deltaCallback64_ = cb; }
};
}

View File

@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 9 February 2023 *
* Date : 30 May 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : FAST rectangular clipping *
@ -34,10 +34,10 @@ namespace Clipper2Lib
};
//------------------------------------------------------------------------------
// RectClip
// RectClip64
//------------------------------------------------------------------------------
class RectClip {
class RectClip64 {
private:
void ExecuteInternal(const Path64& path);
Path64 GetPath(OutPt2*& op);
@ -58,23 +58,23 @@ namespace Clipper2Lib
void AddCorner(Location prev, Location curr);
void AddCorner(Location& loc, bool isClockwise);
public:
explicit RectClip(const Rect64& rect) :
explicit RectClip64(const Rect64& rect) :
rect_(rect),
rect_as_path_(rect.AsPath()),
rect_mp_(rect.MidPoint()) {}
Paths64 Execute(const Paths64& paths, bool convex_only = false);
Paths64 Execute(const Paths64& paths);
};
//------------------------------------------------------------------------------
// RectClipLines
// RectClipLines64
//------------------------------------------------------------------------------
class RectClipLines : public RectClip {
class RectClipLines64 : public RectClip64 {
private:
void ExecuteInternal(const Path64& path);
Path64 GetPath(OutPt2*& op);
public:
explicit RectClipLines(const Rect64& rect) : RectClip(rect) {};
explicit RectClipLines64(const Rect64& rect) : RectClip64(rect) {};
Paths64 Execute(const Paths64& paths);
};

View File

@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 23 April 2023 *
* Date : 20 May 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : This is the main polygon clipping module *
@ -419,7 +419,6 @@ namespace Clipper2Lib {
return outrec;
}
inline void UncoupleOutRec(Active ae)
{
OutRec* outrec = ae.outrec;
@ -484,6 +483,156 @@ namespace Clipper2Lib {
outrec->owner = new_owner;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void AddLocMin(LocalMinimaList& list,
Vertex& vert, PathType polytype, bool is_open)
{
//make sure the vertex is added only once ...
if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::None) return;
vert.flags = (vert.flags | VertexFlags::LocalMin);
list.push_back(std::make_unique <LocalMinima>(&vert, polytype, is_open));
}
void AddPaths_(const Paths64& paths, PathType polytype, bool is_open,
std::vector<Vertex*>& vertexLists, LocalMinimaList& locMinList)
{
const auto total_vertex_count =
std::accumulate(paths.begin(), paths.end(), 0,
[](const auto& a, const Path64& path)
{return a + static_cast<unsigned>(path.size()); });
if (total_vertex_count == 0) return;
Vertex* vertices = new Vertex[total_vertex_count], * v = vertices;
for (const Path64& path : paths)
{
//for each path create a circular double linked list of vertices
Vertex* v0 = v, * curr_v = v, * prev_v = nullptr;
if (path.empty())
continue;
v->prev = nullptr;
int cnt = 0;
for (const Point64& pt : path)
{
if (prev_v)
{
if (prev_v->pt == pt) continue; // ie skips duplicates
prev_v->next = curr_v;
}
curr_v->prev = prev_v;
curr_v->pt = pt;
curr_v->flags = VertexFlags::None;
prev_v = curr_v++;
cnt++;
}
if (!prev_v || !prev_v->prev) continue;
if (!is_open && prev_v->pt == v0->pt)
prev_v = prev_v->prev;
prev_v->next = v0;
v0->prev = prev_v;
v = curr_v; // ie get ready for next path
if (cnt < 2 || (cnt == 2 && !is_open)) continue;
//now find and assign local minima
bool going_up, going_up0;
if (is_open)
{
curr_v = v0->next;
while (curr_v != v0 && curr_v->pt.y == v0->pt.y)
curr_v = curr_v->next;
going_up = curr_v->pt.y <= v0->pt.y;
if (going_up)
{
v0->flags = VertexFlags::OpenStart;
AddLocMin(locMinList , *v0, polytype, true);
}
else
v0->flags = VertexFlags::OpenStart | VertexFlags::LocalMax;
}
else // closed path
{
prev_v = v0->prev;
while (prev_v != v0 && prev_v->pt.y == v0->pt.y)
prev_v = prev_v->prev;
if (prev_v == v0)
continue; // only open paths can be completely flat
going_up = prev_v->pt.y > v0->pt.y;
}
going_up0 = going_up;
prev_v = v0;
curr_v = v0->next;
while (curr_v != v0)
{
if (curr_v->pt.y > prev_v->pt.y && going_up)
{
prev_v->flags = (prev_v->flags | VertexFlags::LocalMax);
going_up = false;
}
else if (curr_v->pt.y < prev_v->pt.y && !going_up)
{
going_up = true;
AddLocMin(locMinList, *prev_v, polytype, is_open);
}
prev_v = curr_v;
curr_v = curr_v->next;
}
if (is_open)
{
prev_v->flags = prev_v->flags | VertexFlags::OpenEnd;
if (going_up)
prev_v->flags = prev_v->flags | VertexFlags::LocalMax;
else
AddLocMin(locMinList, *prev_v, polytype, is_open);
}
else if (going_up != going_up0)
{
if (going_up0) AddLocMin(locMinList, *prev_v, polytype, false);
else prev_v->flags = prev_v->flags | VertexFlags::LocalMax;
}
} // end processing current path
vertexLists.emplace_back(vertices);
}
//------------------------------------------------------------------------------
// ReuseableDataContainer64 methods ...
//------------------------------------------------------------------------------
void ReuseableDataContainer64::AddLocMin(Vertex& vert, PathType polytype, bool is_open)
{
//make sure the vertex is added only once ...
if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::None) return;
vert.flags = (vert.flags | VertexFlags::LocalMin);
minima_list_.push_back(std::make_unique <LocalMinima>(&vert, polytype, is_open));
}
void ReuseableDataContainer64::AddPaths(const Paths64& paths,
PathType polytype, bool is_open)
{
AddPaths_(paths, polytype, is_open, vertex_lists_, minima_list_);
}
ReuseableDataContainer64::~ReuseableDataContainer64()
{
Clear();
}
void ReuseableDataContainer64::Clear()
{
minima_list_.clear();
for (auto v : vertex_lists_) delete[] v;
vertex_lists_.clear();
}
//------------------------------------------------------------------------------
// ClipperBase methods ...
//------------------------------------------------------------------------------
@ -576,113 +725,26 @@ namespace Clipper2Lib {
AddPaths(tmp, polytype, is_open);
}
void ClipperBase::AddPaths(const Paths64& paths, PathType polytype, bool is_open)
{
if (is_open) has_open_paths_ = true;
minima_list_sorted_ = false;
AddPaths_(paths, polytype, is_open, vertex_lists_, minima_list_);
}
const auto total_vertex_count =
std::accumulate(paths.begin(), paths.end(), 0,
[](const auto& a, const Path64& path)
{return a + static_cast<unsigned>(path.size());});
if (total_vertex_count == 0) return;
Vertex* vertices = new Vertex[total_vertex_count], * v = vertices;
for (const Path64& path : paths)
void ClipperBase::AddReuseableData(const ReuseableDataContainer64& reuseable_data)
{
// nb: reuseable_data will continue to own the vertices
// and remains responsible for their clean up.
succeeded_ = false;
minima_list_sorted_ = false;
LocalMinimaList::const_iterator i;
for (i = reuseable_data.minima_list_.cbegin(); i != reuseable_data.minima_list_.cend(); ++i)
{
//for each path create a circular double linked list of vertices
Vertex* v0 = v, * curr_v = v, * prev_v = nullptr;
if (path.empty())
continue;
v->prev = nullptr;
int cnt = 0;
for (const Point64& pt : path)
{
if (prev_v)
{
if (prev_v->pt == pt) continue; // ie skips duplicates
prev_v->next = curr_v;
}
curr_v->prev = prev_v;
curr_v->pt = pt;
curr_v->flags = VertexFlags::None;
prev_v = curr_v++;
cnt++;
}
if (!prev_v || !prev_v->prev) continue;
if (!is_open && prev_v->pt == v0->pt)
prev_v = prev_v->prev;
prev_v->next = v0;
v0->prev = prev_v;
v = curr_v; // ie get ready for next path
if (cnt < 2 || (cnt == 2 && !is_open)) continue;
//now find and assign local minima
bool going_up, going_up0;
if (is_open)
{
curr_v = v0->next;
while (curr_v != v0 && curr_v->pt.y == v0->pt.y)
curr_v = curr_v->next;
going_up = curr_v->pt.y <= v0->pt.y;
if (going_up)
{
v0->flags = VertexFlags::OpenStart;
AddLocMin(*v0, polytype, true);
}
else
v0->flags = VertexFlags::OpenStart | VertexFlags::LocalMax;
}
else // closed path
{
prev_v = v0->prev;
while (prev_v != v0 && prev_v->pt.y == v0->pt.y)
prev_v = prev_v->prev;
if (prev_v == v0)
continue; // only open paths can be completely flat
going_up = prev_v->pt.y > v0->pt.y;
}
going_up0 = going_up;
prev_v = v0;
curr_v = v0->next;
while (curr_v != v0)
{
if (curr_v->pt.y > prev_v->pt.y && going_up)
{
prev_v->flags = (prev_v->flags | VertexFlags::LocalMax);
going_up = false;
}
else if (curr_v->pt.y < prev_v->pt.y && !going_up)
{
going_up = true;
AddLocMin(*prev_v, polytype, is_open);
}
prev_v = curr_v;
curr_v = curr_v->next;
}
if (is_open)
{
prev_v->flags = prev_v->flags | VertexFlags::OpenEnd;
if (going_up)
prev_v->flags = prev_v->flags | VertexFlags::LocalMax;
else
AddLocMin(*prev_v, polytype, is_open);
}
else if (going_up != going_up0)
{
if (going_up0) AddLocMin(*prev_v, polytype, false);
else prev_v->flags = prev_v->flags | VertexFlags::LocalMax;
}
} // end processing current path
vertex_lists_.emplace_back(vertices);
} // end AddPaths
minima_list_.push_back(std::make_unique <LocalMinima>((*i)->vertex, (*i)->polytype, (*i)->is_open));
if ((*i)->is_open) has_open_paths_ = true;
}
}
void ClipperBase::InsertScanline(int64_t y)
{
@ -1315,6 +1377,7 @@ namespace Clipper2Lib {
result->owner = nullptr;
result->polypath = nullptr;
result->is_open = false;
result->splits = nullptr;
return result;
}
@ -1596,17 +1659,14 @@ namespace Clipper2Lib {
default: if (std::abs(edge_c->wind_cnt) != 1) return nullptr; break;
}
OutPt* resultOp;
//toggle contribution ...
if (IsHotEdge(*edge_o))
{
OutPt* resultOp = AddOutPt(*edge_o, pt);
#ifdef USINGZ
if (zCallback_) SetZ(e1, e2, resultOp->pt);
#endif
resultOp = AddOutPt(*edge_o, pt);
if (IsFront(*edge_o)) edge_o->outrec->front_edge = nullptr;
else edge_o->outrec->back_edge = nullptr;
edge_o->outrec = nullptr;
return resultOp;
}
//horizontal edges can pass under open paths at a LocMins
@ -1626,11 +1686,16 @@ namespace Clipper2Lib {
return e3->outrec->pts;
}
else
return StartOpenPath(*edge_o, pt);
resultOp = StartOpenPath(*edge_o, pt);
}
else
return StartOpenPath(*edge_o, pt);
}
resultOp = StartOpenPath(*edge_o, pt);
#ifdef USINGZ
if (zCallback_) SetZ(*edge_o, *edge_c, resultOp->pt);
#endif
return resultOp;
} // end of an open path intersection
//MANAGING CLOSED PATHS FROM HERE ON
@ -2111,7 +2176,7 @@ namespace Clipper2Lib {
if (or1 == or2)
{
or2 = new OutRec();
or2 = NewOutRec();
or2->pts = op1b;
FixOutRecPts(or2);
if (or1->pts->outrec == or2)
@ -2123,7 +2188,11 @@ namespace Clipper2Lib {
if (using_polytree_)
{
if (Path1InsidePath2(or2->pts, or1->pts))
{
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
@ -2135,8 +2204,6 @@ namespace Clipper2Lib {
}
else
or2->owner = or1;
outrec_list_.push_back(or2);
}
else
{
@ -2763,14 +2830,13 @@ namespace Clipper2Lib {
return true;
}
bool ClipperBase::CheckSplitOwner(OutRec* outrec)
bool ClipperBase::CheckSplitOwner(OutRec* outrec, OutRecList* splits)
{
for (auto s : *outrec->owner->splits)
for (auto split : *splits)
{
OutRec* split = GetRealOutRec(s);
if (split && split != outrec &&
split != outrec->owner && CheckBounds(split) &&
split->bounds.Contains(outrec->bounds) &&
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) &&
Path1InsidePath2(outrec->pts, split->pts))
{
outrec->owner = split; //found in split
@ -2789,7 +2855,7 @@ namespace Clipper2Lib {
while (outrec->owner)
{
if (outrec->owner->splits && CheckSplitOwner(outrec)) break;
if (outrec->owner->splits && CheckSplitOwner(outrec, outrec->owner->splits)) break;
if (outrec->owner->pts && CheckBounds(outrec->owner) &&
outrec->owner->bounds.Contains(outrec->bounds) &&
Path1InsidePath2(outrec->pts, outrec->owner->pts)) break;

View File

@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 8 April 2023 *
* Date : 26 May 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : Path Offset (Inflate/Shrink) *
@ -205,15 +205,17 @@ void ClipperOffset::DoSquare(Group& group, const Path64& path, size_t j, size_t
{
PointD vec;
if (j == k)
vec = PointD(norms[0].y, -norms[0].x);
vec = PointD(norms[j].y, -norms[j].x);
else
vec = GetAvgUnitVector(
PointD(-norms[k].y, norms[k].x),
PointD(norms[j].y, -norms[j].x));
double abs_delta = std::abs(group_delta_);
// now offset the original vertex delta units along unit vector
PointD ptQ = PointD(path[j]);
ptQ = TranslatePoint(ptQ, abs_group_delta_ * vec.x, abs_group_delta_ * vec.y);
ptQ = TranslatePoint(ptQ, abs_delta * vec.x, abs_delta * vec.y);
// get perpendicular vertices
PointD pt1 = TranslatePoint(ptQ, group_delta_ * vec.y, group_delta_ * -vec.x);
PointD pt2 = TranslatePoint(ptQ, group_delta_ * -vec.y, group_delta_ * vec.x);
@ -260,6 +262,20 @@ void ClipperOffset::DoMiter(Group& group, const Path64& path, size_t j, size_t k
void ClipperOffset::DoRound(Group& group, const Path64& path, size_t j, size_t k, double angle)
{
if (deltaCallback64_) {
// when deltaCallback64_ is assigned, group_delta_ won't be constant,
// so we'll need to do the following calculations for *every* vertex.
double abs_delta = std::fabs(group_delta_);
double arcTol = (arc_tolerance_ > floating_point_tolerance ?
std::min(abs_delta, arc_tolerance_) :
std::log10(2 + abs_delta) * default_arc_tolerance);
double steps_per_360 = std::min(PI / std::acos(1 - arcTol / abs_delta), abs_delta * PI);
step_sin_ = std::sin(2 * PI / steps_per_360);
step_cos_ = std::cos(2 * PI / steps_per_360);
if (group_delta_ < 0.0) step_sin_ = -step_sin_;
steps_per_rad_ = steps_per_360 / (2 * PI);
}
Point64 pt = path[j];
PointD offsetVec = PointD(norms[k].x * group_delta_, norms[k].y * group_delta_);
@ -287,7 +303,7 @@ void ClipperOffset::DoRound(Group& group, const Path64& path, size_t j, size_t k
group.path.push_back(GetPerpendic(path[j], norms[j], group_delta_));
}
void ClipperOffset::OffsetPoint(Group& group, Path64& path, size_t j, size_t& k)
void ClipperOffset::OffsetPoint(Group& group, Path64& path, size_t j, size_t k)
{
// Let A = change in angle where edges join
// A == 0: ie no change in angle (flat join)
@ -302,12 +318,23 @@ 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 (cos_a > -0.99 && (sin_a * group_delta_ < 0))
if (deltaCallback64_) group_delta_ = deltaCallback64_(path, norms, j, k);
if (std::fabs(group_delta_) <= floating_point_tolerance)
{
group.path.push_back(path[j]);
return;
}
if (cos_a > 0.999) // almost straight - less than 2.5 degree (#424, #526)
{
DoMiter(group, path, j, k, cos_a);
}
else if (cos_a > -0.99 && (sin_a * group_delta_ < 0))
{
// is concave
group.path.push_back(GetPerpendic(path[j], norms[k], group_delta_));
// this extra point is the only (simple) way to ensure that
// path reversals are fully cleaned with the trailing clipper
// path reversals are fully cleaned with the trailing clipper
group.path.push_back(path[j]); // (#405)
group.path.push_back(GetPerpendic(path[j], norms[j], group_delta_));
}
@ -317,22 +344,16 @@ void ClipperOffset::OffsetPoint(Group& group, Path64& path, size_t j, size_t& k)
if (cos_a > temp_lim_ - 1) DoMiter(group, path, j, k, cos_a);
else DoSquare(group, path, j, k);
}
else if (cos_a > 0.9998)
// almost straight - less than 1 degree (#424)
DoMiter(group, path, j, k, cos_a);
else if (cos_a > 0.99 || join_type_ == JoinType::Square)
//angle less than 8 degrees or squared joins
else if (cos_a > 0.99 || join_type_ == JoinType::Square) // 0.99 ~= 8.1 deg.
DoSquare(group, path, j, k);
else
DoRound(group, path, j, k, std::atan2(sin_a, cos_a));
k = j;
}
void ClipperOffset::OffsetPolygon(Group& group, Path64& path)
{
for (Path64::size_type i = 0, j = path.size() -1; i < path.size(); j = i, ++i)
OffsetPoint(group, path, i, j);
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);
}
@ -354,34 +375,40 @@ void ClipperOffset::OffsetOpenJoined(Group& group, Path64& path)
void ClipperOffset::OffsetOpenPath(Group& group, Path64& path)
{
// do the line start cap
switch (end_type_)
if (deltaCallback64_) group_delta_ = deltaCallback64_(path, norms, 0, 0);
if (std::fabs(group_delta_) <= floating_point_tolerance)
group.path.push_back(path[0]);
else
{
case EndType::Butt:
switch (end_type_)
{
case EndType::Butt:
#ifdef USINGZ
group.path.push_back(Point64(
path[0].x - norms[0].x * group_delta_,
path[0].y - norms[0].y * group_delta_,
path[0].z));
group.path.push_back(Point64(
path[0].x - norms[0].x * group_delta_,
path[0].y - norms[0].y * group_delta_,
path[0].z));
#else
group.path.push_back(Point64(
path[0].x - norms[0].x * group_delta_,
path[0].y - norms[0].y * group_delta_));
group.path.push_back(Point64(
path[0].x - norms[0].x * group_delta_,
path[0].y - norms[0].y * group_delta_));
#endif
group.path.push_back(GetPerpendic(path[0], norms[0], group_delta_));
break;
case EndType::Round:
DoRound(group, path, 0,0, PI);
break;
default:
DoSquare(group, path, 0, 0);
break;
group.path.push_back(GetPerpendic(path[0], norms[0], group_delta_));
break;
case EndType::Round:
DoRound(group, path, 0, 0, PI);
break;
default:
DoSquare(group, path, 0, 0);
break;
}
}
size_t highI = path.size() - 1;
// offset the left side going forward
for (Path64::size_type i = 1, k = 0; i < highI; ++i)
OffsetPoint(group, path, i, k);
for (Path64::size_type j = 1, k = 0; j < highI; k = j, ++j)
OffsetPoint(group, path, j, k);
// reverse normals
for (size_t i = highI; i > 0; --i)
@ -389,31 +416,39 @@ void ClipperOffset::OffsetOpenPath(Group& group, Path64& path)
norms[0] = norms[highI];
// do the line end cap
switch (end_type_)
if (deltaCallback64_)
group_delta_ = deltaCallback64_(path, norms, highI, highI);
if (std::fabs(group_delta_) <= floating_point_tolerance)
group.path.push_back(path[highI]);
else
{
case EndType::Butt:
switch (end_type_)
{
case EndType::Butt:
#ifdef USINGZ
group.path.push_back(Point64(
path[highI].x - norms[highI].x * group_delta_,
path[highI].y - norms[highI].y * group_delta_,
path[highI].z));
group.path.push_back(Point64(
path[highI].x - norms[highI].x * group_delta_,
path[highI].y - norms[highI].y * group_delta_,
path[highI].z));
#else
group.path.push_back(Point64(
path[highI].x - norms[highI].x * group_delta_,
path[highI].y - norms[highI].y * group_delta_));
group.path.push_back(Point64(
path[highI].x - norms[highI].x * group_delta_,
path[highI].y - norms[highI].y * group_delta_));
#endif
group.path.push_back(GetPerpendic(path[highI], norms[highI], group_delta_));
break;
case EndType::Round:
DoRound(group, path, highI, highI, PI);
break;
default:
DoSquare(group, path, highI, highI);
break;
group.path.push_back(GetPerpendic(path[highI], norms[highI], group_delta_));
break;
case EndType::Round:
DoRound(group, path, highI, highI, PI);
break;
default:
DoSquare(group, path, highI, highI);
break;
}
}
for (size_t i = highI, k = 0; i > 0; --i)
OffsetPoint(group, path, i, k);
for (size_t j = highI, k = 0; j > 0; k = j, --j)
OffsetPoint(group, path, j, k);
group.paths_out.push_back(group.path);
}
@ -439,10 +474,10 @@ void ClipperOffset::DoGroupOffset(Group& group)
group.is_reversed = false;
group_delta_ = std::abs(delta_) * 0.5;
}
abs_group_delta_ = std::fabs(group_delta_);
double abs_delta = std::fabs(group_delta_);
// do range checking
if (!IsSafeOffset(r, abs_group_delta_))
if (!IsSafeOffset(r, abs_delta))
{
DoError(range_error_i);
error_code_ |= range_error_i;
@ -452,24 +487,23 @@ void ClipperOffset::DoGroupOffset(Group& group)
join_type_ = group.join_type;
end_type_ = group.end_type;
//calculate a sensible number of steps (for 360 deg for the given offset
if (group.join_type == JoinType::Round || group.end_type == EndType::Round)
if (!deltaCallback64_ &&
(group.join_type == JoinType::Round || group.end_type == EndType::Round))
{
//calculate a sensible number of steps (for 360 deg for the given offset)
// arcTol - when arc_tolerance_ is undefined (0), the amount of
// curve imprecision that's allowed is based on the size of the
// offset (delta). Obviously very large offsets will almost always
// require much less precision. See also offset_triginometry2.svg
double arcTol = (arc_tolerance_ > floating_point_tolerance ?
std::min(abs_group_delta_, arc_tolerance_) :
std::log10(2 + abs_group_delta_) * default_arc_tolerance);
double steps_per_360 = PI / std::acos(1 - arcTol / abs_group_delta_);
if (steps_per_360 > abs_group_delta_ * PI)
steps_per_360 = abs_group_delta_ * PI; //ie avoids excessive precision
std::min(abs_delta, arc_tolerance_) :
std::log10(2 + abs_delta) * default_arc_tolerance);
double steps_per_360 = std::min(PI / std::acos(1 - arcTol / abs_delta), abs_delta * PI);
step_sin_ = std::sin(2 * PI / steps_per_360);
step_cos_ = std::cos(2 * PI / steps_per_360);
if (group_delta_ < 0.0) step_sin_ = -step_sin_;
steps_per_rad_ = steps_per_360 / (2 *PI);
if (group_delta_ < 0.0) step_sin_ = -step_sin_;
steps_per_rad_ = steps_per_360 / (2 * PI);
}
bool is_joined =
@ -478,7 +512,7 @@ void ClipperOffset::DoGroupOffset(Group& group)
Paths64::iterator path_iter;
for(path_iter = group.paths_in.begin(); path_iter != group.paths_in.end(); ++path_iter)
{
auto path = *path_iter;
Path64 &path = *path_iter;
StripDuplicates(path, is_joined);
Path64::size_type cnt = path.size();
if (cnt == 0 || ((cnt < 3) && group.end_type == EndType::Polygon))
@ -491,7 +525,7 @@ void ClipperOffset::DoGroupOffset(Group& group)
//single vertex so build a circle or square ...
if (group.join_type == JoinType::Round)
{
double radius = abs_group_delta_;
double radius = abs_delta;
group.path = Ellipse(path[0], radius, radius);
#ifdef USINGZ
for (auto& p : group.path) p.z = path[0].z;
@ -499,7 +533,7 @@ void ClipperOffset::DoGroupOffset(Group& group)
}
else
{
int d = (int)std::ceil(abs_group_delta_);
int d = (int)std::ceil(abs_delta);
r = Rect64(path[0].x - d, path[0].y - d, path[0].x + d, path[0].y + d);
group.path = r.AsPath();
#ifdef USINGZ
@ -568,6 +602,7 @@ void ClipperOffset::Execute(double delta, Paths64& paths)
if (!solution.size()) return;
paths = solution;
/**/
//clean up self-intersections ...
Clipper64 c;
c.PreserveCollinear = false;
@ -583,6 +618,7 @@ void ClipperOffset::Execute(double delta, Paths64& paths)
c.Execute(ClipType::Union, FillRule::Negative, paths);
else
c.Execute(ClipType::Union, FillRule::Positive, paths);
/**/
}
@ -610,4 +646,10 @@ void ClipperOffset::Execute(double delta, PolyTree64& polytree)
c.Execute(ClipType::Union, FillRule::Positive, polytree);
}
void ClipperOffset::Execute(DeltaCallback64 delta_cb, Paths64& paths)
{
deltaCallback64_ = delta_cb;
Execute(1.0, paths);
}
} // namespace

View File

@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 22 April 2023 *
* Date : 30 May 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : FAST rectangular clipping *
@ -281,7 +281,7 @@ namespace Clipper2Lib {
// RectClip64
//----------------------------------------------------------------------------
OutPt2* RectClip::Add(Point64 pt, bool start_new)
OutPt2* RectClip64::Add(Point64 pt, bool start_new)
{
// this method is only called by InternalExecute.
// Later splitting & rejoining won't create additional op's,
@ -312,7 +312,7 @@ namespace Clipper2Lib {
return result;
}
void RectClip::AddCorner(Location prev, Location curr)
void RectClip64::AddCorner(Location prev, Location curr)
{
if (HeadingClockwise(prev, curr))
Add(rect_as_path_[static_cast<int>(prev)]);
@ -320,7 +320,7 @@ namespace Clipper2Lib {
Add(rect_as_path_[static_cast<int>(curr)]);
}
void RectClip::AddCorner(Location& loc, bool isClockwise)
void RectClip64::AddCorner(Location& loc, bool isClockwise)
{
if (isClockwise)
{
@ -334,7 +334,7 @@ namespace Clipper2Lib {
}
}
void RectClip::GetNextLocation(const Path64& path,
void RectClip64::GetNextLocation(const Path64& path,
Location& loc, int& i, int highI)
{
switch (loc)
@ -389,7 +389,7 @@ namespace Clipper2Lib {
} //switch
}
void RectClip::ExecuteInternal(const Path64& path)
void RectClip64::ExecuteInternal(const Path64& path)
{
int i = 0, highI = static_cast<int>(path.size()) - 1;
Location prev = Location::Inside, loc;
@ -546,7 +546,7 @@ namespace Clipper2Lib {
}
}
void RectClip::CheckEdges()
void RectClip64::CheckEdges()
{
for (size_t i = 0; i < results_.size(); ++i)
{
@ -606,7 +606,7 @@ namespace Clipper2Lib {
}
}
void RectClip::TidyEdges(int idx, OutPt2List& cw, OutPt2List& ccw)
void RectClip64::TidyEdges(int idx, OutPt2List& cw, OutPt2List& ccw)
{
if (ccw.empty()) return;
bool isHorz = ((idx == 1) || (idx == 3));
@ -784,7 +784,7 @@ namespace Clipper2Lib {
}
}
Path64 RectClip::GetPath(OutPt2*& op)
Path64 RectClip64::GetPath(OutPt2*& op)
{
if (!op || op->next == op->prev) return Path64();
@ -814,7 +814,7 @@ namespace Clipper2Lib {
return result;
}
Paths64 RectClip::Execute(const Paths64& paths, bool convex_only)
Paths64 RectClip64::Execute(const Paths64& paths)
{
Paths64 result;
if (rect_.IsEmpty()) return result;
@ -833,13 +833,10 @@ namespace Clipper2Lib {
}
ExecuteInternal(path);
if (!convex_only)
{
CheckEdges();
for (int i = 0; i < 4; ++i)
TidyEdges(i, edges_[i * 2], edges_[i * 2 + 1]);
}
CheckEdges();
for (int i = 0; i < 4; ++i)
TidyEdges(i, edges_[i * 2], edges_[i * 2 + 1]);
for (OutPt2*& op : results_)
{
Path64 tmp = GetPath(op);
@ -857,10 +854,10 @@ namespace Clipper2Lib {
}
//------------------------------------------------------------------------------
// RectClipLines
// RectClipLines64
//------------------------------------------------------------------------------
Paths64 RectClipLines::Execute(const Paths64& paths)
Paths64 RectClipLines64::Execute(const Paths64& paths)
{
Paths64 result;
if (rect_.IsEmpty()) return result;
@ -886,7 +883,7 @@ namespace Clipper2Lib {
return result;
}
void RectClipLines::ExecuteInternal(const Path64& path)
void RectClipLines64::ExecuteInternal(const Path64& path)
{
if (rect_.IsEmpty() || path.size() < 2) return;
@ -956,7 +953,7 @@ namespace Clipper2Lib {
///////////////////////////////////////////////////
}
Path64 RectClipLines::GetPath(OutPt2*& op)
Path64 RectClipLines64::GetPath(OutPt2*& op)
{
Path64 result;
if (!op || op == op->next) return result;