Skip to content

Commit

Permalink
ClipperOffset
Browse files Browse the repository at this point in the history
  added support for variable offsets (#511)
  further reduced extraneous vertices in solutions (#499)
ClipperEngine
  Fixed a minor bug with polytrees (#520)
  • Loading branch information
AngusJohnson committed May 16, 2023
1 parent 95edbf6 commit 6e15ba0
Show file tree
Hide file tree
Showing 13 changed files with 557 additions and 224 deletions.
2 changes: 2 additions & 0 deletions CPP/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ if(CLIPPER2_EXAMPLES)
UnionClipping
RectClipping
SimpleClipping
VariableOffset
)

foreach(ex ${EXAMPLES})
Expand Down Expand Up @@ -190,6 +191,7 @@ endif()
Tests/TestPolytreeHoles1.cpp
Tests/TestPolytreeHoles2.cpp
Tests/TestPolytreeHoles3.cpp
Tests/TestPolytreeHoles4.cpp
Tests/TestPolytreeIntersection.cpp
Tests/TestPolytreeUnion.cpp
Tests/TestRandomPaths.cpp
Expand Down
2 changes: 1 addition & 1 deletion CPP/Clipper2Lib/include/clipper2/clipper.core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
18 changes: 9 additions & 9 deletions CPP/Clipper2Lib/include/clipper2/clipper.engine.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 1 May 2023 *
* Date : 15 May 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : This is the main polygon clipping module *
Expand Down Expand Up @@ -270,7 +270,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;
Expand Down Expand Up @@ -347,7 +347,7 @@ 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
Expand Down Expand Up @@ -390,12 +390,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() {
Expand All @@ -415,14 +415,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;
}

Expand Down Expand Up @@ -610,7 +610,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);
}
Expand Down
47 changes: 23 additions & 24 deletions CPP/Clipper2Lib/include/clipper2/clipper.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 21 April 2023 *
* Date : 15 May 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : This module provides a simple interface to the Clipper Library *
Expand Down Expand Up @@ -161,40 +161,44 @@ 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 result;
return TranslatePath<double>(path, dx, dy);
}

inline Paths64 TranslatePaths(const Paths64& paths, int64_t dx, int64_t dy)
template <typename T>
inline Paths<T> TranslatePaths(const Paths<T>& paths, T dx, T dy)
{
Paths64 result;
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)
{
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,
Expand Down Expand Up @@ -290,14 +294,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())
Expand Down
10 changes: 7 additions & 3 deletions CPP/Clipper2Lib/include/clipper2/clipper.offset.h
Original file line number Diff line number Diff line change
@@ -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) *
Expand All @@ -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:
Expand All @@ -43,7 +44,6 @@ class ClipperOffset {
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;
Expand All @@ -62,6 +62,7 @@ class ClipperOffset {
#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);
Expand All @@ -70,7 +71,7 @@ class ClipperOffset {
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:
Expand All @@ -91,6 +92,7 @@ class ClipperOffset {

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; }
Expand All @@ -108,6 +110,8 @@ class ClipperOffset {
#ifdef USINGZ
void SetZCallback(ZCallback64 cb) { zCallback64_ = cb; }
#endif
void SetDeltaCallback(DeltaCallback64 cb) { deltaCallback64_ = cb; }

};

}
Expand Down
23 changes: 13 additions & 10 deletions CPP/Clipper2Lib/src/clipper.engine.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 1 May 2023 *
* Date : 16 May 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : This is the main polygon clipping module *
Expand Down Expand Up @@ -1378,6 +1378,7 @@ namespace Clipper2Lib {
result->owner = nullptr;
result->polypath = nullptr;
result->is_open = false;
result->splits = nullptr;
return result;
}

Expand Down Expand Up @@ -2174,7 +2175,7 @@ namespace Clipper2Lib {

if (or1 == or2)
{
or2 = new OutRec();
or2 = NewOutRec();
or2->pts = op1b;
FixOutRecPts(or2);
if (or1->pts->outrec == or2)
Expand All @@ -2186,7 +2187,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
Expand All @@ -2198,8 +2203,6 @@ namespace Clipper2Lib {
}
else
or2->owner = or1;

outrec_list_.push_back(or2);
}
else
{
Expand Down Expand Up @@ -2826,14 +2829,14 @@ namespace Clipper2Lib {
return true;
}

bool ClipperBase::CheckSplitOwner(OutRec* outrec)
bool ClipperBase::CheckSplitOwner(OutRec* outrec, OutRecList* splits)
{
for (auto s : *outrec->owner->splits)
for (auto s : *splits)
{
OutRec* split = GetRealOutRec(s);
if (split && split != outrec &&
split != outrec->owner && CheckBounds(split) &&
split->bounds.Contains(outrec->bounds) &&
if (!split || 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
Expand All @@ -2852,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;
Expand Down
Loading

0 comments on commit 6e15ba0

Please sign in to comment.