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:
parent
d854e2bfe8
commit
fe62a3f985
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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; }
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue