Skip to content

Commit 3a7433f

Browse files
committed
Port PrecomputeIterationOrder
1 parent e41d49a commit 3a7433f

File tree

1 file changed

+124
-43
lines changed

1 file changed

+124
-43
lines changed

src/PolygonClipper/PolygonClipper.cs

Lines changed: 124 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,11 @@ private static Polygon ConnectEdges(List<SweepEvent> sortedEvents, SweepEventCom
742742
for (int i = 0; i < sortedEvents.Count; i++)
743743
{
744744
SweepEvent se = sortedEvents[i];
745-
if ((se.Left && se.InResult) || (!se.Left && se.OtherEvent.InResult))
745+
if ((se.Left && se.InResult))
746+
{
747+
resultEvents.Add(se);
748+
}
749+
else if (!se.Left && se.OtherEvent.InResult)
746750
{
747751
resultEvents.Add(se);
748752
}
@@ -777,12 +781,14 @@ private static Polygon ConnectEdges(List<SweepEvent> sortedEvents, SweepEventCom
777781
for (int i = 0; i < resultEvents.Count; i++)
778782
{
779783
SweepEvent sweepEvent = resultEvents[i];
780-
if (!sweepEvent.Left)
784+
if (sweepEvent.Left)
781785
{
782786
(sweepEvent.OtherEvent.Pos, sweepEvent.Pos) = (sweepEvent.Pos, sweepEvent.OtherEvent.Pos);
783787
}
784788
}
785789

790+
ReadOnlySpan<int> iterationMap = PrecomputeIterationOrder(resultEvents);
791+
786792
Polygon result = new();
787793
Span<bool> processed = new bool[resultEvents.Count];
788794
for (int i = 0; i < resultEvents.Count; i++)
@@ -796,31 +802,29 @@ private static Polygon ConnectEdges(List<SweepEvent> sortedEvents, SweepEventCom
796802
Contour contour = InitializeContourFromContext(resultEvents[i], result, contourId);
797803

798804
int pos = i;
799-
int originalPos = i;
800805
Vertex initial = resultEvents[i].Point;
801806
contour.AddVertex(initial);
802807

803808
// Main loop to process the contour
804-
do
809+
while (true)
805810
{
806-
processed[pos] = true;
807-
resultEvents[pos].OutputContourId = contourId;
811+
MarkProcessed(resultEvents[pos], processed, pos, contourId);
812+
pos = resultEvents[pos].Pos;
813+
814+
MarkProcessed(resultEvents[pos], processed, pos, contourId);
808815

809-
if (resultEvents[pos].Left)
816+
contour.AddVertex(resultEvents[pos].Point);
817+
pos = NextPos(pos, processed, iterationMap, out bool found);
818+
if (!found)
810819
{
811-
resultEvents[pos].ResultInOut = false;
820+
break;
812821
}
813-
else
822+
823+
if (resultEvents[pos].Point == initial)
814824
{
815-
resultEvents[pos].OtherEvent.ResultInOut = true;
825+
break;
816826
}
817-
818-
pos = resultEvents[pos].Pos;
819-
processed[pos] = true;
820-
resultEvents[pos].OutputContourId = contourId;
821-
contour.AddVertex(resultEvents[pos].Point);
822-
pos = NextPos(pos, resultEvents, processed, originalPos);
823-
} while (pos != originalPos && pos < resultEvents.Count);
827+
}
824828

825829
result.Push(contour);
826830
}
@@ -848,6 +852,95 @@ private static Polygon ConnectEdges(List<SweepEvent> sortedEvents, SweepEventCom
848852
return polygon;
849853
}
850854

855+
private static ReadOnlySpan<int> PrecomputeIterationOrder(List<SweepEvent> data)
856+
{
857+
Span<int> map = new Span<int>(new int[data.Count]);
858+
859+
int i = 0;
860+
while (i < data.Count)
861+
{
862+
SweepEvent xRef = data[i];
863+
864+
// Find index range of R events
865+
int rFrom = i;
866+
while (i < data.Count && xRef.Point == data[i].Point && !data[i].Left)
867+
{
868+
i++;
869+
}
870+
871+
int rUptoExclusive = i;
872+
873+
// Find index range of L events
874+
int lFrom = i;
875+
while (i < data.Count && xRef.Point == data[i].Point)
876+
{
877+
if (!data[i].Left)
878+
{
879+
throw new InvalidOperationException("Expected left event");
880+
}
881+
882+
i++;
883+
}
884+
885+
int lUptoExclusive = i;
886+
887+
bool hasREvents = rUptoExclusive > rFrom;
888+
bool hasLEvents = lUptoExclusive > lFrom;
889+
890+
if (hasREvents)
891+
{
892+
int rUpto = rUptoExclusive - 1;
893+
894+
// Connect elements in [rFrom, rUpto) to larger index
895+
for (int j = rFrom; j < rUpto; j++)
896+
{
897+
map[j] = j + 1;
898+
}
899+
900+
// Special handling of *last* element: Connect either the last L event
901+
// or loop back to start of R events (if no L events).
902+
if (hasLEvents)
903+
{
904+
map[rUpto] = lUptoExclusive - 1;
905+
}
906+
else
907+
{
908+
map[rUpto] = rFrom;
909+
}
910+
}
911+
912+
if (hasLEvents)
913+
{
914+
int lUpto = lUptoExclusive - 1;
915+
916+
// Connect elements in (lFrom, lUpto] to lower index
917+
for (int j = lFrom + 1; j <= lUpto; j++)
918+
{
919+
map[j] = j - 1;
920+
}
921+
922+
// Special handling of *first* element: Connect either to the first R event
923+
// or loop back to end of L events (if no R events).
924+
if (hasREvents)
925+
{
926+
map[lFrom] = rFrom;
927+
}
928+
else
929+
{
930+
map[lFrom] = lUpto;
931+
}
932+
}
933+
}
934+
935+
return map;
936+
}
937+
938+
private static void MarkProcessed(SweepEvent sweepEvent, Span<bool> processed, int pos, int contourId)
939+
{
940+
processed[pos] = true;
941+
sweepEvent.OutputContourId = contourId;
942+
}
943+
851944
/// <summary>
852945
/// Initializes a contour based on its context in relation to previous events and contours.
853946
/// Implements the 4 cases of parent contours from the Martinez paper (Fig. 4).
@@ -915,6 +1008,8 @@ private static Contour InitializeContourFromContext(SweepEvent sweepEvent, Polyg
9151008
/// <param name="resultEvents">The list of sweep events representing result segments.</param>
9161009
/// <param name="processed">A list indicating whether each event at the corresponding index has been processed.</param>
9171010
/// <param name="originalPos">The original position to return if no unprocessed event is found.</param>
1011+
/// <param name="iterationMap"></param>
1012+
/// <param name="found"></param>
9181013
/// <returns>The index of the next unprocessed position.</returns>
9191014
/// <remarks>
9201015
/// This method searches forward from the current position until it finds an unprocessed event with
@@ -923,41 +1018,27 @@ private static Contour InitializeContourFromContext(SweepEvent sweepEvent, Polyg
9231018
/// </remarks>
9241019
private static int NextPos(
9251020
int pos,
926-
List<SweepEvent> resultEvents,
9271021
ReadOnlySpan<bool> processed,
928-
int originalPos)
1022+
ReadOnlySpan<int> iterationMap,
1023+
out bool found)
9291024
{
930-
int newPos = pos + 1;
931-
Vertex initial = resultEvents[pos].Point;
932-
Vertex next = default;
933-
934-
if (newPos < resultEvents.Count)
935-
{
936-
next = resultEvents[newPos].Point;
937-
}
1025+
int startPos = pos;
9381026

939-
// Search forward for the next unprocessed event with a different point
940-
while (newPos < resultEvents.Count && initial == next)
1027+
while (true)
9411028
{
942-
if (!processed[newPos])
1029+
pos = iterationMap[pos];
1030+
if (pos == startPos)
9431031
{
944-
return newPos;
1032+
// Entire group is already processed?
1033+
found = false;
1034+
return Int32.MinValue;
9451035
}
9461036

947-
newPos++;
948-
if (newPos < resultEvents.Count)
1037+
if (!processed[pos])
9491038
{
950-
next = resultEvents[newPos].Point;
1039+
found = true;
1040+
return pos;
9511041
}
9521042
}
953-
954-
// If not found, search backward for an unprocessed event
955-
newPos = pos - 1;
956-
while (newPos > originalPos && processed[newPos])
957-
{
958-
newPos--;
959-
}
960-
961-
return newPos;
9621043
}
9631044
}

0 commit comments

Comments
 (0)