Skip to content

Commit

Permalink
Fixed significant bug in non-horizontal polygon merging (AngusJohnson…
Browse files Browse the repository at this point in the history
…#381)

Fixed wrong version no. in CMake (AngusJohnson#380)
  • Loading branch information
AngusJohnson committed Jan 27, 2023
1 parent 09c531c commit 3742404
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 103 deletions.
2 changes: 1 addition & 1 deletion CPP/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.10)
project(Clipper2 VERSION 1.0.6 LANGUAGES C CXX)
project(Clipper2 VERSION 1.1.0 LANGUAGES C CXX)

set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_CXX_STANDARD 17)
Expand Down
5 changes: 3 additions & 2 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 : 23 January 2023 *
* Date : 27 January 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : This is the main polygon clipping module *
Expand Down Expand Up @@ -245,7 +245,8 @@ namespace Clipper2Lib {

void Split(Active& e, const Point64& pt);
void CheckJoinLeft(Active& e, const Point64& pt);
void CheckJoinRight(Active& e, const Point64& pt);
void CheckJoinRight(Active& e,
const Point64& pt, bool check_curr_x = false);
protected:
bool has_open_paths_ = false;
bool succeeded_ = true;
Expand Down
2 changes: 1 addition & 1 deletion CPP/Clipper2Lib/include/clipper2/clipper.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 23 January 2023 *
* Date : 27 January 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : This module provides a simple interface to the Clipper Library *
Expand Down
59 changes: 31 additions & 28 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 : 23 January 2023 *
* Date : 27 January 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : This is the main polygon clipping module *
Expand Down Expand Up @@ -2288,7 +2288,7 @@ namespace Clipper2Lib {
node.edge1->curr_x = node.pt.x;
node.edge2->curr_x = node.pt.x;
CheckJoinLeft(*node.edge2, node.pt);
CheckJoinRight(*node.edge1, node.pt);
CheckJoinRight(*node.edge1, node.pt, true);
}
}

Expand Down Expand Up @@ -2648,42 +2648,45 @@ namespace Clipper2Lib {

void ClipperBase::CheckJoinLeft(Active& e, const Point64& pt)
{
if (IsOpen(e) || !IsHotEdge(e) ||
!e.prev_in_ael || IsOpen(*e.prev_in_ael) ||
!IsHotEdge(*e.prev_in_ael) || e.curr_x != e.prev_in_ael->curr_x ||
pt.y <= e.top.y || pt.y <= e.prev_in_ael->top.y ||
Active* prev = e.prev_in_ael;
if (IsOpen(e) || !IsHotEdge(e) || !prev || IsOpen(*prev) ||
!IsHotEdge(*prev) || e.curr_x != prev->curr_x ||
pt.y <= e.top.y || pt.y <= prev->top.y ||
IsJoined(e) || IsOpen(e) ||
CrossProduct(e.top, pt, e.prev_in_ael->top))
CrossProduct(e.top, pt, prev->top))
return;

if (e.outrec->idx == e.prev_in_ael->outrec->idx)
AddLocalMaxPoly(*e.prev_in_ael, e, pt);
else if (e.outrec->idx < e.prev_in_ael->outrec->idx)
JoinOutrecPaths(e, *e.prev_in_ael);
if (e.outrec->idx == prev->outrec->idx)
AddLocalMaxPoly(*prev, e, pt);
else if (e.outrec->idx < prev->outrec->idx)
JoinOutrecPaths(e, *prev);
else
JoinOutrecPaths(*e.prev_in_ael, e);
e.prev_in_ael->join_with = JoinWith::Right;
JoinOutrecPaths(*prev, e);
prev->join_with = JoinWith::Right;
e.join_with = JoinWith::Left;
}

void ClipperBase::CheckJoinRight(Active& e, const Point64& pt)
void ClipperBase::CheckJoinRight(Active& e,
const Point64& pt, bool check_curr_x)
{
if (IsOpen(e) || !IsHotEdge(e) ||
!e.next_in_ael || IsOpen(*e.next_in_ael) ||
!IsHotEdge(*e.next_in_ael) || e.curr_x != e.next_in_ael->curr_x ||
pt.y <= e.top.y || pt.y <= e.next_in_ael->top.y ||
IsJoined(e) || IsOpen(e) ||
CrossProduct(e.top, pt, e.next_in_ael->top))
return;

if (e.outrec->idx == e.next_in_ael->outrec->idx)
AddLocalMaxPoly(e, *e.next_in_ael, pt);
else if (e.outrec->idx < e.next_in_ael->outrec->idx)
JoinOutrecPaths(e, *e.next_in_ael);
Active* next = e.next_in_ael;
if (IsOpen(e) || !IsHotEdge(e) || IsJoined(e) ||
!next || IsOpen(*next) || !IsHotEdge(*next) ||
pt.y < e.top.y +2 || pt.y < next->top.y +2) // avoids trivial joins
return;

if (check_curr_x) next->curr_x = TopX(*next, pt.y);
if (e.curr_x != next->curr_x ||
CrossProduct(e.top, pt, next->top)) return;

if (e.outrec->idx == next->outrec->idx)
AddLocalMaxPoly(e, *next, pt);
else if (e.outrec->idx < next->outrec->idx)
JoinOutrecPaths(e, *next);
else
JoinOutrecPaths(*e.next_in_ael, e);
JoinOutrecPaths(*next, e);
e.join_with = JoinWith::Right;
e.next_in_ael->join_with = JoinWith::Left;
next->join_with = JoinWith::Left;
}

inline bool GetHorzExtendedHorzSeg(OutPt*& op, OutPt*& op2)
Expand Down
17 changes: 11 additions & 6 deletions CPP/Tests/TestPolygons.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ TEST(Clipper2Tests, TestMultiplePolygons)
ASSERT_TRUE(ifs);
ASSERT_TRUE(ifs.good());

int test_number = 1;
while (true)
const int start_num = 1;
const int end_num = 1000;

int test_number = start_num;
while (test_number <= end_num)
{
Clipper2Lib::Paths64 subject, subject_open, clip;
Clipper2Lib::Paths64 solution, solution_open;
Expand Down Expand Up @@ -71,7 +74,7 @@ TEST(Clipper2Tests, TestMultiplePolygons)
// check polygon counts
if (stored_count <= 0)
; // skip count
else if (IsInList(test_number, { 120, 138, 140, 165, 166, 167, 168, 172, 175, 178, 180 }))
else if (IsInList(test_number, { 120, 121, 130, 138, 140, 165, 166, 167, 168, 172, 175, 178, 180 }))
EXPECT_LE(count_diff, 5) << " in test " << test_number;
else if (IsInList(test_number, { 27, 126, 145, 163, 173, 176, 177, 179, 181 }))
EXPECT_LE(count_diff, 2) << " in test " << test_number;
Expand Down Expand Up @@ -100,12 +103,14 @@ TEST(Clipper2Tests, TestMultiplePolygons)
else
EXPECT_LE((double)area_diff_ratio, 0.01) << " in test " << test_number;

EXPECT_EQ(measured_count, measured_count_polytree);
EXPECT_EQ(measured_area, measured_area_polytree);
EXPECT_EQ(measured_count, measured_count_polytree)
<< " in test " << test_number;
EXPECT_EQ(measured_area, measured_area_polytree)
<< " in test " << test_number;

++test_number;
}
EXPECT_GE(test_number, 188);
//EXPECT_GE(test_number, 188);

Clipper2Lib::PathsD subjd, clipd, solutiond;
Clipper2Lib::FillRule frd = Clipper2Lib::FillRule::NonZero;
Expand Down
57 changes: 31 additions & 26 deletions CSharp/Clipper2Lib/Clipper.Engine.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 26 January 2023 *
* Date : 27 January 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : This is the main polygon clipping module *
Expand Down Expand Up @@ -1985,7 +1985,7 @@ private void ProcessIntersectList()
node.edge1.curX = node.pt.X;
node.edge2.curX = node.pt.X;
CheckJoinLeft(node.edge2, node.pt);
CheckJoinRight(node.edge1, node.pt);
CheckJoinRight(node.edge1, node.pt, true);
}
}

Expand Down Expand Up @@ -2351,43 +2351,48 @@ private void Split(Active e, Point64 currPt)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void CheckJoinLeft(Active e, Point64 currPt)
{
Active? prev = e.prevInAEL;
if (IsOpen(e) || !IsHotEdge(e) ||
e.prevInAEL == null || IsOpen(e.prevInAEL) ||
!IsHotEdge(e.prevInAEL) || e.curX != e.prevInAEL.curX ||
currPt.Y <= e.top.Y || currPt.Y <= e.prevInAEL.top.Y ||
prev == null || IsOpen(prev) ||
!IsHotEdge(prev) || e.curX != prev.curX ||
currPt.Y <= e.top.Y || currPt.Y <= prev.top.Y ||
IsJoined(e) || IsOpen(e) ||
InternalClipper.CrossProduct(e.top, currPt, e.prevInAEL.top) != 0)
InternalClipper.CrossProduct(e.top, currPt, prev.top) != 0)
return;

if (e.outrec!.idx == e.prevInAEL.outrec!.idx)
AddLocalMaxPoly(e.prevInAEL, e, currPt);
else if (e.outrec.idx < e.prevInAEL.outrec.idx)
JoinOutrecPaths(e, e.prevInAEL);
if (e.outrec!.idx == prev.outrec!.idx)
AddLocalMaxPoly(prev, e, currPt);
else if (e.outrec.idx < prev.outrec.idx)
JoinOutrecPaths(e, prev);
else
JoinOutrecPaths(e.prevInAEL, e);
e.prevInAEL.joinWith = JoinWith.Right;
JoinOutrecPaths(prev, e);
prev.joinWith = JoinWith.Right;
e.joinWith = JoinWith.Left;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void CheckJoinRight(Active e, Point64 currPt)
private void CheckJoinRight(Active e,
Point64 currPt, bool checkNextCurrX = false)
{
if (IsOpen(e) || !IsHotEdge(e) ||
e.nextInAEL == null || IsOpen(e.nextInAEL) ||
!IsHotEdge(e.nextInAEL) || e.curX != e.nextInAEL.curX ||
currPt.Y <= e.top.Y || currPt.Y <= e.nextInAEL.top.Y ||
IsJoined(e) || IsOpen(e) ||
InternalClipper.CrossProduct(e.top, currPt, e.nextInAEL.top) != 0)
return;
Active? next = e.nextInAEL;
if (IsOpen(e) || !IsHotEdge(e) || IsJoined(e) ||
next == null || IsOpen(next) || !IsHotEdge(next) ||
currPt.Y < e.top.Y + 2 || currPt.Y < next.top.Y + 2) // avoids trivial joins
return;

if (checkNextCurrX) next.curX = TopX(next, currPt.Y);
if (e.curX != next.curX ||
(InternalClipper.CrossProduct(e.top, currPt, next.top) != 0))
return;

if (e.outrec!.idx == e.nextInAEL.outrec!.idx)
AddLocalMaxPoly(e, e.nextInAEL, currPt);
else if (e.outrec.idx < e.nextInAEL.outrec.idx)
JoinOutrecPaths(e, e.nextInAEL);
if (e.outrec!.idx == next.outrec!.idx)
AddLocalMaxPoly(e, next, currPt);
else if (e.outrec.idx < next.outrec.idx)
JoinOutrecPaths(e, next);
else
JoinOutrecPaths(e.nextInAEL, e);
JoinOutrecPaths(next, e);
e.joinWith = JoinWith.Right;
e.nextInAEL.joinWith = JoinWith.Left;
next.joinWith = JoinWith.Left;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down
66 changes: 37 additions & 29 deletions Delphi/Clipper2Lib/Clipper.Engine.pas
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

(*******************************************************************************
* Author : Angus Johnson *
* Date : 25 January 2023 *
* Date : 27 January 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : This is the main polygon clipping module *
Expand Down Expand Up @@ -242,7 +242,8 @@ TClipperBase = class
procedure Split(e: PActive; const currPt: TPoint64);
procedure CheckJoinLeft(e: PActive; const currPt: TPoint64);
{$IFDEF INLINING} inline; {$ENDIF}
procedure CheckJoinRight(e: PActive; const currPt: TPoint64);
procedure CheckJoinRight(e: PActive;
const currPt: TPoint64; checkNextCurrX: Boolean = false);
{$IFDEF INLINING} inline; {$ENDIF}
function AddLocalMinPoly(e1, e2: PActive;
const pt: TPoint64; IsNew: Boolean = false): POutPt;
Expand Down Expand Up @@ -2146,45 +2147,52 @@ procedure TClipperBase.Split(e: PActive; const currPt: TPoint64);
//------------------------------------------------------------------------------

procedure TClipperBase.CheckJoinLeft(e: PActive; const currPt: TPoint64);
var
prev: PActive;
begin
prev := e.prevInAEL;
if IsOpen(e) or not IsHotEdge(e) or
not Assigned(e.prevInAEL) or IsOpen(e.prevInAEL) or
not IsHotEdge(e.prevInAEL) or (e.currX <> e.prevInAEL.currX) or
(currPt.Y <= e.top.Y) or (currPt.Y <= e.prevInAEL.top.Y) or
not Assigned(prev) or IsOpen(prev) or
not IsHotEdge(prev) or (e.currX <> prev.currX) or
(currPt.Y <= e.top.Y) or (currPt.Y <= prev.top.Y) or
(e.joinedWith <> jwNone) or
(currPt.Y < e.top.Y +2) or (currPt.Y < e.prevInAEL.top.Y +2) or
(CrossProduct(e.top, currPt, e.prevInAEL.top) <> 0) then Exit;
(currPt.Y < e.top.Y +2) or (currPt.Y < prev.top.Y +2) or
(CrossProduct(e.top, currPt, prev.top) <> 0) then Exit;

if (e.outrec.idx = e.prevInAEL.outrec.idx) then
AddLocalMaxPoly(e.prevInAEL, e, currPt)
else if e.outrec.idx < e.prevInAEL.outrec.idx then
JoinOutrecPaths(e, e.prevInAEL)
if (e.outrec.idx = prev.outrec.idx) then
AddLocalMaxPoly(prev, e, currPt)
else if e.outrec.idx < prev.outrec.idx then
JoinOutrecPaths(e, prev)
else
JoinOutrecPaths(e.prevInAEL, e);
e.prevInAEL.joinedWith := jwRight;
JoinOutrecPaths(prev, e);
prev.joinedWith := jwRight;
e.joinedWith := jwLeft;
end;
//------------------------------------------------------------------------------

procedure TClipperBase.CheckJoinRight(e: PActive; const currPt: TPoint64);
procedure TClipperBase.CheckJoinRight(e: PActive;
const currPt: TPoint64; checkNextCurrX: Boolean);
var
next: PActive;
begin
if IsOpen(e) or not IsHotEdge(e) or
not Assigned(e.nextInAEL) or IsOpen(e.nextInAEL) or
not IsHotEdge(e.nextInAEL) or (e.currX <> e.nextInAEL.currX) or
(currPt.Y <= e.top.Y) or (currPt.Y <= e.nextInAEL.top.Y) or
(e.joinedWith <> jwNone) or
(currPt.Y < e.top.Y +2) or (currPt.Y < e.nextInAEL.top.Y +2) or
(CrossProduct(e.top, currPt, e.nextInAEL.top) <> 0) then Exit;
next := e.nextInAEL;
if IsOpen(e) or not IsHotEdge(e) or IsJoined(e) or
not Assigned(next) or IsOpen(next) or not IsHotEdge(next) or
(currPt.Y < e.top.Y +2) or (currPt.Y < next.top.Y +2) then Exit;

if (checkNextCurrX) then next.currX := TopX(next, currPt.Y);
if (e.currX <> next.currX) or
(CrossProduct(e.top, currPt, next.top) <> 0) then Exit;

if e.outrec.idx = e.nextInAEL.outrec.idx then
AddLocalMaxPoly(e, e.nextInAEL, currPt)
else if e.outrec.idx < e.nextInAEL.outrec.idx then
JoinOutrecPaths(e, e.nextInAEL)
if e.outrec.idx = next.outrec.idx then
AddLocalMaxPoly(e, next, currPt)
else if e.outrec.idx < next.outrec.idx then
JoinOutrecPaths(e, next)
else
JoinOutrecPaths(e.nextInAEL, e);
JoinOutrecPaths(next, e);

e.joinedWith := jwRight;
e.nextInAEL.joinedWith := jwLeft;
next.joinedWith := jwLeft;
end;
//------------------------------------------------------------------------------

Expand Down Expand Up @@ -2597,7 +2605,7 @@ procedure TClipperBase.ExecuteInternal(clipType: TClipType;
DoTopOfScanbeam(Y);
while PopHorz(e) do DoHorizontal(e);
end;
if Succeeded then ProcessHorzJoins;
if Succeeded then ProcessHorzJoins;
end;
//------------------------------------------------------------------------------

Expand Down Expand Up @@ -3166,7 +3174,7 @@ procedure TClipperBase.ProcessIntersectList;
active1.currX := pt.X;
active2.currX := pt.X;
CheckJoinLeft(active2, pt);
CheckJoinRight(active1, pt);
CheckJoinRight(active1, pt, true);
end;
inc(nodeI);
end;
Expand Down
5 changes: 2 additions & 3 deletions Delphi/Clipper2Lib/Clipper.pas
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

(*******************************************************************************
* Author : Angus Johnson *
* Date : 9 November 2022 *
* Date : 27 January 2023 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2022 *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : This module provides a simple interface to the Clipper Library *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************)
Expand Down Expand Up @@ -763,7 +763,6 @@ function SimplifyPath(const path: TPath64;
SetLength(flags, len);
SetLength(dsq, len);

prev := high;
curr := 0;
if (isOpenPath) then
begin
Expand Down
Loading

0 comments on commit 3742404

Please sign in to comment.