Skip to content

Commit

Permalink
Fixed a minor bug in Clipper.RectClip (#864)
Browse files Browse the repository at this point in the history
  • Loading branch information
AngusJohnson committed Jul 5, 2024
1 parent 623f0a2 commit 4240912
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 32 deletions.
32 changes: 18 additions & 14 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 : 17 April 2024 *
* Date : 5 July 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : This is the main polygon clipping module *
Expand Down Expand Up @@ -343,6 +343,7 @@ namespace Clipper2Lib {
Path64 polygon_;
public:
explicit PolyPath64(PolyPath64* parent = nullptr) : PolyPath(parent) {}
explicit PolyPath64(PolyPath64* parent, const Path64& path) : PolyPath(parent) { polygon_ = path; }

~PolyPath64() {
childs_.resize(0);
Expand All @@ -363,10 +364,7 @@ namespace Clipper2Lib {

PolyPath64* AddChild(const Path64& path) override
{
auto p = std::make_unique<PolyPath64>(this);
auto* result = childs_.emplace_back(std::move(p)).get();
result->polygon_ = path;
return result;
return childs_.emplace_back(std::make_unique<PolyPath64>(this, path)).get();
}

void Clear() override
Expand Down Expand Up @@ -401,6 +399,19 @@ namespace Clipper2Lib {
scale_ = parent ? parent->scale_ : 1.0;
}

explicit PolyPathD(PolyPathD* parent, const Path64& path) : PolyPath(parent)
{
scale_ = parent ? parent->scale_ : 1.0;
int error_code = 0;
polygon_ = ScalePath<double, int64_t>(path, scale_, error_code);
}

explicit PolyPathD(PolyPathD* parent, const PathD& path) : PolyPath(parent)
{
scale_ = parent ? parent->scale_ : 1.0;
polygon_ = path;
}

~PolyPathD() {
childs_.resize(0);
}
Expand All @@ -423,19 +434,12 @@ namespace Clipper2Lib {

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, scale_, error_code);
return result;
return childs_.emplace_back(std::make_unique<PolyPathD>(this, path)).get();
}

PolyPathD* AddChild(const PathD& path)
{
auto p = std::make_unique<PolyPathD>(this);
PolyPathD* result = childs_.emplace_back(std::move(p)).get();
result->polygon_ = path;
return result;
return childs_.emplace_back(std::make_unique<PolyPathD>(this, path)).get();
}

void Clear() override
Expand Down
13 changes: 7 additions & 6 deletions CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 1 November 2023 *
* Date : 5 July 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : FAST rectangular clipping *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
Expand All @@ -18,6 +18,7 @@
namespace Clipper2Lib
{

// Location: the order is important here, see StartLocsIsClockwise()
enum class Location { Left, Top, Right, Bottom, Inside };

class OutPt2;
Expand All @@ -26,10 +27,10 @@ namespace Clipper2Lib
class OutPt2 {
public:
Point64 pt;
size_t owner_idx;
OutPt2List* edge;
OutPt2* next;
OutPt2* prev;
size_t owner_idx = 0;
OutPt2List* edge = nullptr;
OutPt2* next = nullptr;
OutPt2* prev = nullptr;
};

//------------------------------------------------------------------------------
Expand Down
29 changes: 24 additions & 5 deletions CPP/Clipper2Lib/src/clipper.rectclip.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 27 April 2024 *
* Date : 5 July 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : FAST rectangular clipping *
Expand Down Expand Up @@ -424,6 +424,23 @@ namespace Clipper2Lib {
} //switch
}

bool StartLocsAreClockwise(const std::vector<Location>& startlocs)
{
int result = 0;
for (size_t i = 1; i < startlocs.size(); ++i)
{
int d = static_cast<int>(startlocs[i]) - static_cast<int>(startlocs[i - 1]);
switch (d)
{
case -1: result -= 1; break;
case 1: result += 1; break;
case -3: result += 1; break;
case 3: result -= 1; break;
}
}
return result > 0;
}

void RectClip64::ExecuteInternal(const Path64& path)
{
if (path.size() < 1)
Expand All @@ -446,7 +463,7 @@ namespace Clipper2Lib {
}
if (prev == Location::Inside) loc = Location::Inside;
}
Location startingLoc = loc;
Location starting_loc = loc;

///////////////////////////////////////////////////
size_t i = 0;
Expand Down Expand Up @@ -548,7 +565,7 @@ namespace Clipper2Lib {
if (first_cross_ == Location::Inside)
{
// path never intersects
if (startingLoc != Location::Inside)
if (starting_loc != Location::Inside)
{
// path is outside rect
// but being outside, it still may not contain rect
Expand All @@ -557,11 +574,13 @@ namespace Clipper2Lib {
{
// yep, the path does fully contain rect
// so add rect to the solution
bool is_clockwise_path = StartLocsAreClockwise(start_locs_);
for (size_t j = 0; j < 4; ++j)
{
Add(rect_as_path_[j]);
size_t k = is_clockwise_path ? j : 3 - j; // reverses result path
Add(rect_as_path_[k]);
// we may well need to do some splitting later, so
AddToEdge(edges_[j * 2], results_[0]);
AddToEdge(edges_[k * 2], results_[0]);
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions CPP/Tests/TestRectClip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,14 @@ TEST(Clipper2Tests, TestRectClip3) //#637
solution = RectClip(r, subject);
//std::cout << solution << std::endl;
EXPECT_TRUE(solution.size() == 1);
}

TEST(Clipper2Tests, TestRectClipOrientation) //#864
{
const Rect64 rect(1222, 1323, 3247, 3348);
const Path64 subject = MakePath({ 375,1680, 1915,4716, 5943,586, 3987,152 });
RectClip64 clip(rect);
const auto solution = clip.Execute({ subject });
ASSERT_EQ(solution.size(), 1);
EXPECT_EQ(IsPositive(subject), IsPositive(solution.front()));
}
25 changes: 22 additions & 3 deletions CSharp/Clipper2Lib/Clipper.RectClip.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 7 May 2024 *
* Date : 5 July 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : FAST rectangular clipping *
Expand Down Expand Up @@ -498,6 +498,23 @@ protected void GetNextLocation(Path64 path,
} // switch
}

private bool StartLocsAreClockwise(List<Location> startLocs)
{
int result = 0;
for (int i = 1; i < startLocs.Count; i++)
{
int d = (int)startLocs[i] - (int)startLocs[i - 1];
switch (d)
{
case -1: result -= 1; break;
case 1: result += 1; break;
case -3: result += 1; break;
case 3: result -= 1; break;
}
}
return result > 0;
}

private void ExecuteInternal(Path64 path)
{
if (path.Count < 3 || rect_.IsEmpty()) return;
Expand Down Expand Up @@ -624,10 +641,12 @@ private void ExecuteInternal(Path64 path)
if (pathBounds_.Contains(rect_) &&
Path1ContainsPath2(path, rectPath_))
{
bool startLocsClockwise = StartLocsAreClockwise(startLocs);
for (int j = 0; j < 4; j++)
{
Add(rectPath_[j]);
AddToEdge(edges_[j * 2], results_[0]!);
int k = startLocsClockwise ? j : 3 - j; // ie reverse result path
Add(rectPath_[k]);
AddToEdge(edges_[k * 2], results_[0]!);
}
}
}
Expand Down
30 changes: 26 additions & 4 deletions Delphi/Clipper2Lib/Clipper.RectClip.pas
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

(*******************************************************************************
* Author : Angus Johnson *
* Date : 27 April 2024 *
* Date : 5 July 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : FAST rectangular clipping *
Expand Down Expand Up @@ -650,9 +650,28 @@ function TRectClip64.Execute(const paths: TPaths64): TPaths64;
end;
//------------------------------------------------------------------------------

function StartLocsAreClockwise(const startLocs: TList): Boolean;
var
i,j, res: integer;
begin
res := 0;
for i := 1 to startLocs.Count -1 do
begin
j := Ord(TLocation(startLocs[i])) - Ord(TLocation(startLocs[i - 1]));
case j of
-1: dec(res);
1: inc(res);
-3: inc(res);
3: dec(res);
end;
end;
result := res > 0;
end;
//------------------------------------------------------------------------------

procedure TRectClip64.ExecuteInternal(const path: TPath64);
var
i,highI : integer;
i,j, highI : integer;
prevPt,ip,ip2 : TPoint64;
loc, prevLoc : TLocation;
loc2 : TLocation;
Expand All @@ -661,6 +680,7 @@ procedure TRectClip64.ExecuteInternal(const path: TPath64);
crossingLoc : TLocation;
prevCrossLoc : TLocation;
isCw : Boolean;
startLocsCW : Boolean;
begin
if (Length(path) < 3) then Exit;
fStartLocs.Clear;
Expand Down Expand Up @@ -797,10 +817,12 @@ procedure TRectClip64.ExecuteInternal(const path: TPath64);
begin
// yep, the path does fully contain rect
// so add rect to the solution
startLocsCW := StartLocsAreClockwise(fStartLocs);
for i := 0 to 3 do
begin
Add(fRectPath[i]);
AddToEdge(fEdges[i*2], fResults[0]);
if startLocsCW then j := i else j := 3 - i;
Add(fRectPath[j]);
AddToEdge(fEdges[j*2], fResults[0]);
end;
end;
end;
Expand Down

0 comments on commit 4240912

Please sign in to comment.