@@ -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