Skip to content
This repository was archived by the owner on Oct 28, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CPP/Clipper2Lib/include/clipper2/clipper.engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ namespace Clipper2Lib {
FillRule fillrule_ = FillRule::EvenOdd;
FillRule fillpos = FillRule::Positive;
int64_t bot_y_ = 0;
bool curr_x_at_top_ = false; // every active edge's curr_x is already TopX(top_y)
bool minima_list_sorted_ = false;
bool using_polytree_ = false;
Active* actives_ = nullptr;
Expand Down Expand Up @@ -226,7 +227,7 @@ namespace Clipper2Lib {
inline void UpdateEdgeIntoAEL(Active *e);
void IntersectEdges(Active &e1, Active &e2, const Point64& pt);
inline void DeleteFromAEL(Active &e);
inline void AdjustCurrXAndCopyToSEL(const int64_t top_y);
inline bool AdjustCurrXAndCopyToSEL(const int64_t top_y);
void DoIntersections(const int64_t top_y);
void AddNewIntersectNode(Active &e1, Active &e2, const int64_t top_y);
bool BuildIntersectList(const int64_t top_y);
Expand Down
22 changes: 18 additions & 4 deletions CPP/Clipper2Lib/src/clipper.engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2099,20 +2099,27 @@ namespace Clipper2Lib {
}


inline void ClipperBase::AdjustCurrXAndCopyToSEL(const int64_t top_y)
// returns true when the edges are still sorted by curr_x (ie no intersections)
inline bool ClipperBase::AdjustCurrXAndCopyToSEL(const int64_t top_y)
{
Active* e = actives_;
sel_ = e;
bool sorted = true;
int64_t prev_x = std::numeric_limits<int64_t>::min();
while (e)
{
e->prev_in_sel = e->prev_in_ael;
e->next_in_sel = e->next_in_ael;
e->jump = e->next_in_sel;
// it is safe to ignore 'joined' edges here because
// if necessary they will be split in IntersectEdges()
e->curr_x = TopX(*e, top_y);
int64_t x = TopX(*e, top_y);
e->curr_x = x;
if (x < prev_x) sorted = false;
prev_x = x;
e = e->next_in_ael;
}
return sorted;
}

bool ClipperBase::ExecuteInternal(ClipType ct, FillRule fillrule, bool use_polytrees)
Expand Down Expand Up @@ -2335,6 +2342,7 @@ namespace Clipper2Lib {

void ClipperBase::DoIntersections(const int64_t top_y)
{
curr_x_at_top_ = false;
if (BuildIntersectList(top_y))
{
ProcessIntersectList();
Expand Down Expand Up @@ -2382,7 +2390,13 @@ namespace Clipper2Lib {

//Calculate edge positions at the top of the current scanbeam, and from this
//we will determine the intersections required to reach these new positions.
AdjustCurrXAndCopyToSEL(top_y);
//If the edges are still in order there are no intersections, so the merge
//sort (which always does log2(n) passes over the edges) can be skipped.
if (AdjustCurrXAndCopyToSEL(top_y))
{
curr_x_at_top_ = true;
return false;
}
//Find all edge intersections in the current scanbeam using a stable merge
//sort that ensures only adjacent edges are intersecting. Intersect info is
//stored in FIntersectList ready to be processed in ProcessIntersectList.
Expand Down Expand Up @@ -2718,7 +2732,7 @@ namespace Clipper2Lib {
PushHorz(*e); // horizontals are processed later
}
}
else // i.e. not the top of the edge
else if (!curr_x_at_top_) // i.e. not the top of the edge
e->curr_x = TopX(*e, y);

e = e->next_in_ael;
Expand Down
23 changes: 19 additions & 4 deletions CSharp/Clipper2Lib/Clipper.Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ public class ClipperBase
private readonly OutPtPoolList _outPtPool;
private int _currentLocMin;
private long _currentBotY;
private bool _currXAtTop; // every active edge's curX is already TopX(topY)
private bool _isSortedMinimaList;
private bool _hasOpenPaths;
internal bool _using_polytree;
Expand Down Expand Up @@ -1855,22 +1856,29 @@ private Active NewActive()
return ae;
}

// returns true when the edges are still sorted by curX (ie no intersections)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AdjustCurrXAndCopyToSEL(long topY)
private bool AdjustCurrXAndCopyToSEL(long topY)
{
Active? ae = _actives;
_sel = ae;
bool sorted = true;
long prevX = long.MinValue;
while (ae != null)
{
ae.prevInSEL = ae.prevInAEL;
ae.nextInSEL = ae.nextInAEL;
ae.jump = ae.nextInSEL;
// it is safe to ignore 'joined' edges here because
// if necessary they will be split in IntersectEdges()
ae.curX = TopX(ae, topY);
long x = TopX(ae, topY);
ae.curX = x;
if (x < prevX) sorted = false;
prevX = x;
// NB don't update ae.curr.Y yet (see AddNewIntersectNode)
ae = ae.nextInAEL;
}
return sorted;
}

protected void ExecuteInternal(ClipType ct, FillRule fillRule)
Expand Down Expand Up @@ -1903,6 +1911,7 @@ protected void ExecuteInternal(ClipType ct, FillRule fillRule)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DoIntersections(long topY)
{
_currXAtTop = false;
if (!BuildIntersectList(topY)) return;
ProcessIntersectList();
DisposeIntersectNodes();
Expand Down Expand Up @@ -1984,7 +1993,13 @@ private bool BuildIntersectList(long topY)

// Calculate edge positions at the top of the current scanbeam, and from this
// we will determine the intersections required to reach these new positions.
AdjustCurrXAndCopyToSEL(topY);
// If the edges are still in order there are no intersections, so the merge
// sort (which always does log2(n) passes over the edges) can be skipped.
if (AdjustCurrXAndCopyToSEL(topY))
{
_currXAtTop = true;
return false;
}

// Find all edge intersections in the current scanbeam using a stable merge
// sort that ensures only adjacent edges are intersecting. Intersect info is
Expand Down Expand Up @@ -2331,7 +2346,7 @@ private void DoTopOfScanbeam(long y)
if (IsHorizontal(ae))
PushHorz(ae); // horizontals are processed later
}
else // i.e. not the top of the edge
else if (!_currXAtTop) // i.e. not the top of the edge
ae.curX = TopX(ae, y);

ae = ae.nextInAEL;
Expand Down
26 changes: 21 additions & 5 deletions Delphi/Clipper2Lib/Clipper.Engine.pas
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ TScanLine = record
TClipperBase = class
{$IFDEF STRICT}strict{$ENDIF} private
FBotY : Int64;
FCurrXAtTop : Boolean; // every active edge's currX is already TopX(topY)
FScanLine : PScanLine;
FCurrentLocMinIdx : Integer;
FClipType : TClipType;
Expand Down Expand Up @@ -242,7 +243,7 @@ TClipperBase = class
procedure IntersectEdges(e1, e2: PActive; pt: TPoint64);
procedure DeleteEdges(var e: PActive);
procedure DeleteFromAEL(e: PActive);
procedure AdjustCurrXAndCopyToSEL(topY: Int64);
function AdjustCurrXAndCopyToSEL(topY: Int64): Boolean;
procedure ConvertHorzSegsToJoins;
procedure ProcessHorzJoins;
procedure DoIntersections(const topY: Int64);
Expand Down Expand Up @@ -2823,10 +2824,14 @@ procedure TClipperBase.DeleteFromAEL(e: PActive);
end;
//------------------------------------------------------------------------------

procedure TClipperBase.AdjustCurrXAndCopyToSEL(topY: Int64);
function TClipperBase.AdjustCurrXAndCopyToSEL(topY: Int64): Boolean;
var
e: PActive;
x, prevX: Int64;
begin
// returns true when the edges are still sorted by currX (ie no intersections)
Result := true;
prevX := Low(Int64);
FSel := FActives;
e := FActives;
while Assigned(e) do
Expand All @@ -2836,7 +2841,10 @@ procedure TClipperBase.AdjustCurrXAndCopyToSEL(topY: Int64);
e.jump := e.nextInSEL;
// it is safe to ignore 'joined' edges here because
// if necessary they will be split in IntersectEdges()
e.currX := TopX(e, topY);
x := TopX(e, topY);
e.currX := x;
if x < prevX then Result := false;
prevX := x;
e := e.nextInAEL;
end;
end;
Expand Down Expand Up @@ -3133,6 +3141,7 @@ procedure TClipperBase.ProcessHorzJoins;

procedure TClipperBase.DoIntersections(const topY: Int64);
begin
FCurrXAtTop := false;
if BuildIntersectList(topY) then
try
ProcessIntersectList;
Expand Down Expand Up @@ -3221,7 +3230,13 @@ function TClipperBase.BuildIntersectList(const topY: Int64): Boolean;

// Calculate edge positions at the top of the current scanbeam, and from this
// we will determine the intersections required to reach these new positions.
AdjustCurrXAndCopyToSEL(topY);
// If the edges are still in order there are no intersections, so the merge
// sort (which always does log2(n) passes over the edges) can be skipped.
if AdjustCurrXAndCopyToSEL(topY) then
begin
FCurrXAtTop := true;
Exit;
end;

// Find all edge intersections in the current scanbeam using a stable merge
// sort that ensures only adjacent edges are intersecting. Intersect info is
Expand Down Expand Up @@ -3604,7 +3619,8 @@ procedure TClipperBase.DoTopOfScanbeam(Y: Int64);
if IsHorizontal(e) then
PushHorz(e);
end;
end else
end
else if not FCurrXAtTop then // i.e. not the top of the edge
e.currX := TopX(e, Y);
e := e.nextInAEL;
end;
Expand Down