diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.engine.h b/CPP/Clipper2Lib/include/clipper2/clipper.engine.h index a4c8fd82..f6108832 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.engine.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.engine.h @@ -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 * @@ -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); @@ -363,10 +364,7 @@ namespace Clipper2Lib { PolyPath64* AddChild(const Path64& path) override { - auto p = std::make_unique(this); - auto* result = childs_.emplace_back(std::move(p)).get(); - result->polygon_ = path; - return result; + return childs_.emplace_back(std::make_unique(this, path)).get(); } void Clear() override @@ -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(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); } @@ -423,19 +434,12 @@ namespace Clipper2Lib { PolyPathD* AddChild(const Path64& path) override { - int error_code = 0; - auto p = std::make_unique(this); - PolyPathD* result = childs_.emplace_back(std::move(p)).get(); - result->polygon_ = ScalePath(path, scale_, error_code); - return result; + return childs_.emplace_back(std::make_unique(this, path)).get(); } PolyPathD* AddChild(const PathD& path) { - auto p = std::make_unique(this); - PolyPathD* result = childs_.emplace_back(std::move(p)).get(); - result->polygon_ = path; - return result; + return childs_.emplace_back(std::make_unique(this, path)).get(); } void Clear() override diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h b/CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h index a5ea0497..bfcfacf2 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h @@ -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 * *******************************************************************************/ @@ -18,6 +18,7 @@ namespace Clipper2Lib { + // Location: the order is important here, see StartLocsIsClockwise() enum class Location { Left, Top, Right, Bottom, Inside }; class OutPt2; @@ -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; }; //------------------------------------------------------------------------------ diff --git a/CPP/Clipper2Lib/src/clipper.rectclip.cpp b/CPP/Clipper2Lib/src/clipper.rectclip.cpp index a34971ce..23809b5e 100644 --- a/CPP/Clipper2Lib/src/clipper.rectclip.cpp +++ b/CPP/Clipper2Lib/src/clipper.rectclip.cpp @@ -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 * @@ -424,6 +424,23 @@ namespace Clipper2Lib { } //switch } + bool StartLocsAreClockwise(const std::vector& startlocs) + { + int result = 0; + for (size_t i = 1; i < startlocs.size(); ++i) + { + int d = static_cast(startlocs[i]) - static_cast(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) @@ -446,7 +463,7 @@ namespace Clipper2Lib { } if (prev == Location::Inside) loc = Location::Inside; } - Location startingLoc = loc; + Location starting_loc = loc; /////////////////////////////////////////////////// size_t i = 0; @@ -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 @@ -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]); } } } diff --git a/CPP/Tests/TestRectClip.cpp b/CPP/Tests/TestRectClip.cpp index c320a9bc..4b8972a0 100644 --- a/CPP/Tests/TestRectClip.cpp +++ b/CPP/Tests/TestRectClip.cpp @@ -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())); } \ No newline at end of file diff --git a/CSharp/Clipper2Lib/Clipper.RectClip.cs b/CSharp/Clipper2Lib/Clipper.RectClip.cs index 43fcf90e..face68f9 100644 --- a/CSharp/Clipper2Lib/Clipper.RectClip.cs +++ b/CSharp/Clipper2Lib/Clipper.RectClip.cs @@ -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 * @@ -498,6 +498,23 @@ protected void GetNextLocation(Path64 path, } // switch } + private bool StartLocsAreClockwise(List 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; @@ -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]!); } } } diff --git a/Delphi/Clipper2Lib/Clipper.RectClip.pas b/Delphi/Clipper2Lib/Clipper.RectClip.pas index 8a0cf05c..91fbba66 100644 --- a/Delphi/Clipper2Lib/Clipper.RectClip.pas +++ b/Delphi/Clipper2Lib/Clipper.RectClip.pas @@ -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 * @@ -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; @@ -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; @@ -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;