5
5
using System . Collections . Generic ;
6
6
using System . Diagnostics ;
7
7
using System . Diagnostics . CodeAnalysis ;
8
+ using System . Runtime . CompilerServices ;
9
+ using System . Runtime . InteropServices ;
8
10
9
11
namespace PolygonClipper ;
10
12
@@ -162,6 +164,7 @@ public Polygon Run()
162
164
163
165
SweepEvent ? prevEvent ;
164
166
SweepEvent ? nextEvent ;
167
+ Span < SweepEvent > workspace = new SweepEvent [ 4 ] ;
165
168
while ( eventQueue . Count > 0 )
166
169
{
167
170
SweepEvent sweepEvent = eventQueue . Dequeue ( ) ;
@@ -188,7 +191,7 @@ public Polygon Run()
188
191
if ( nextEvent != null )
189
192
{
190
193
// Check intersection with the next neighbor
191
- if ( PossibleIntersection ( sweepEvent , nextEvent , eventQueue ) == 2 )
194
+ if ( PossibleIntersection ( sweepEvent , nextEvent , eventQueue , workspace ) == 2 )
192
195
{
193
196
ComputeFields ( sweepEvent , prevEvent , operation ) ;
194
197
ComputeFields ( nextEvent , sweepEvent , operation ) ;
@@ -199,7 +202,7 @@ public Polygon Run()
199
202
if ( prevEvent != null )
200
203
{
201
204
// Check intersection with the previous neighbor
202
- if ( PossibleIntersection ( prevEvent , sweepEvent , eventQueue ) == 2 )
205
+ if ( PossibleIntersection ( prevEvent , sweepEvent , eventQueue , workspace ) == 2 )
203
206
{
204
207
SweepEvent ? prevPrevEvent = statusLine . Prev ( prevEvent . PosSL ) ;
205
208
ComputeFields ( prevEvent , prevPrevEvent , operation ) ;
@@ -218,7 +221,7 @@ public Polygon Run()
218
221
// Check intersection between neighbors
219
222
if ( prevEvent != null && nextEvent != null )
220
223
{
221
- _ = PossibleIntersection ( prevEvent , nextEvent , eventQueue ) ;
224
+ _ = PossibleIntersection ( prevEvent , nextEvent , eventQueue , workspace ) ;
222
225
}
223
226
224
227
statusLine . RemoveAt ( it ) ;
@@ -499,6 +502,10 @@ private static bool InResult(SweepEvent sweepEvent, BooleanOperation operation)
499
502
/// <param name="le1">The first sweep event representing a line segment.</param>
500
503
/// <param name="le2">The second sweep event representing a line segment.</param>
501
504
/// <param name="eventQueue">The event queue to add new events to.</param>
505
+ /// <param name="workspace">
506
+ /// A scratch space for temporary storage of sweep events.
507
+ /// Must be at least 4 elements long to hold the events for the two segments and their associated other events.
508
+ /// </param>
502
509
/// <returns>
503
510
/// An integer indicating the result of the intersection:
504
511
/// <list type="bullet">
@@ -514,7 +521,8 @@ private static bool InResult(SweepEvent sweepEvent, BooleanOperation operation)
514
521
private static int PossibleIntersection (
515
522
SweepEvent le1 ,
516
523
SweepEvent le2 ,
517
- StablePriorityQueue < SweepEvent , SweepEventComparer > eventQueue )
524
+ StablePriorityQueue < SweepEvent , SweepEventComparer > eventQueue ,
525
+ Span < SweepEvent > workspace )
518
526
{
519
527
if ( le1 . OtherEvent == null || le2 . OtherEvent == null )
520
528
{
@@ -569,41 +577,55 @@ private static int PossibleIntersection(
569
577
}
570
578
571
579
// The line segments associated with le1 and le2 overlap.
572
- // TODO: Rewrite this to avoid allocation.
573
- List < SweepEvent > events = new ( 4 ) ;
574
580
bool leftCoincide = le1 . Point == le2 . Point ;
575
581
bool rightCoincide = le1 . OtherEvent . Point == le2 . OtherEvent . Point ;
576
582
577
- // Populate the events
583
+ // Populate the events.
584
+ // The working buffer has a length of 4, which is sufficient to hold the events
585
+ // for the two segments and their associated other events.
586
+ // Events are assigned in a specific order to avoid overwriting shared references.
587
+ ref SweepEvent wRef = ref MemoryMarshal . GetReference ( workspace ) ;
578
588
if ( ! leftCoincide )
579
589
{
580
590
if ( comparer . Compare ( le1 , le2 ) > 0 )
581
591
{
582
- events . Add ( le2 ) ;
583
- events . Add ( le1 ) ;
592
+ Unsafe . Add ( ref wRef , 0u ) = le2 ;
593
+ Unsafe . Add ( ref wRef , 1u ) = le1 ;
584
594
}
585
595
else
586
596
{
587
- events . Add ( le1 ) ;
588
- events . Add ( le2 ) ;
597
+ Unsafe . Add ( ref wRef , 0u ) = le1 ;
598
+ Unsafe . Add ( ref wRef , 1u ) = le2 ;
589
599
}
590
- }
591
600
592
- if ( ! rightCoincide )
601
+ // Positions 0 and 1 contain the left events of the segments.
602
+ // Positions 2 and 3 will contain the right events of the segments.
603
+ if ( ! rightCoincide )
604
+ {
605
+ Unsafe . Add ( ref wRef , 2u ) = le1. OtherEvent ;
606
+ Unsafe . Add ( ref wRef , 3u ) = le2. OtherEvent ;
607
+ }
608
+ else
609
+ {
610
+ Unsafe . Add ( ref wRef , 2u ) = le2. OtherEvent ;
611
+ Unsafe . Add ( ref wRef , 3u ) = le1. OtherEvent ;
612
+ }
613
+ }
614
+ else if ( leftCoincide && ! rightCoincide )
593
615
{
616
+ // Only the right endpoints differ, so we use positions 0 and 1 for their sorted order.
594
617
if ( comparer . Compare ( le1 . OtherEvent , le2 . OtherEvent ) > 0 )
595
618
{
596
- events . Add ( le2 . OtherEvent ) ;
597
- events . Add ( le1 . OtherEvent ) ;
619
+ Unsafe . Add ( ref wRef , 0u ) = le2. OtherEvent ;
620
+ Unsafe . Add ( ref wRef , 1u ) = le1. OtherEvent ;
598
621
}
599
622
else
600
623
{
601
- events . Add ( le1 . OtherEvent ) ;
602
- events . Add ( le2 . OtherEvent ) ;
624
+ Unsafe . Add ( ref wRef , 0u ) = le1. OtherEvent ;
625
+ Unsafe . Add ( ref wRef , 1u ) = le2. OtherEvent ;
603
626
}
604
627
}
605
628
606
- // Handle leftCoincide case
607
629
if ( leftCoincide )
608
630
{
609
631
le2 . EdgeType = EdgeType . NonContributing ;
@@ -613,30 +635,31 @@ private static int PossibleIntersection(
613
635
614
636
if ( leftCoincide && ! rightCoincide )
615
637
{
616
- DivideSegment ( events [ 1 ] . OtherEvent , events [ 0 ] . Point , eventQueue , comparer ) ;
638
+ DivideSegment ( Unsafe . Add ( ref wRef , 1u ) . OtherEvent , Unsafe . Add ( ref wRef , 0u ) . Point , eventQueue , comparer ) ;
617
639
}
618
640
619
641
return 2 ;
620
642
}
621
643
622
- // Handle the rightCoincide case
623
644
if ( rightCoincide )
624
645
{
625
- DivideSegment ( events [ 0 ] , events [ 1 ] . Point , eventQueue , comparer ) ;
646
+ // Since leftCoincide is false, the first two workspace slots contain distinct left events.
647
+ DivideSegment ( Unsafe . Add ( ref wRef , 0u ) , Unsafe . Add ( ref wRef , 1u ) . Point , eventQueue , comparer ) ;
626
648
return 3 ;
627
649
}
628
650
629
651
// Handle general overlapping case
630
- if ( events [ 0 ] != events [ 3 ] . OtherEvent )
652
+ // At this point: workspace[0,1] = sorted left events, workspace[2,3] = sorted right events.
653
+ if ( Unsafe . Add ( ref wRef , 0u ) != Unsafe . Add ( ref wRef , 3u ) . OtherEvent )
631
654
{
632
- DivideSegment ( events [ 0 ] , events [ 1 ] . Point , eventQueue , comparer ) ;
633
- DivideSegment ( events [ 1 ] , events [ 2 ] . Point , eventQueue , comparer ) ;
655
+ DivideSegment ( Unsafe . Add ( ref wRef , 0u ) , Unsafe . Add ( ref wRef , 1u ) . Point , eventQueue , comparer ) ;
656
+ DivideSegment ( Unsafe . Add ( ref wRef , 1u ) , Unsafe . Add ( ref wRef , 2u ) . Point , eventQueue , comparer ) ;
634
657
return 3 ;
635
658
}
636
659
637
660
// One segment fully contains the other
638
- DivideSegment ( events [ 0 ] , events [ 1 ] . Point , eventQueue , comparer ) ;
639
- DivideSegment ( events [ 3 ] . OtherEvent , events [ 2 ] . Point , eventQueue , comparer ) ;
661
+ DivideSegment ( Unsafe . Add ( ref wRef , 0u ) , Unsafe . Add ( ref wRef , 1u ) . Point , eventQueue , comparer ) ;
662
+ DivideSegment ( Unsafe . Add ( ref wRef , 3u ) . OtherEvent , Unsafe . Add ( ref wRef , 2u ) . Point , eventQueue , comparer ) ;
640
663
return 3 ;
641
664
}
642
665
@@ -924,6 +947,7 @@ private static ReadOnlySpan<int> PrecomputeIterationOrder(List<SweepEvent> data)
924
947
return map ;
925
948
}
926
949
950
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
927
951
private static void MarkProcessed ( SweepEvent sweepEvent , Span < bool > processed , int pos , int contourId )
928
952
{
929
953
processed [ pos ] = true ;
@@ -994,11 +1018,9 @@ private static Contour InitializeContourFromContext(SweepEvent sweepEvent, Polyg
994
1018
/// starting from the given position.
995
1019
/// </summary>
996
1020
/// <param name="pos">The current position in the result events.</param>
997
- /// <param name="resultEvents">The list of sweep events representing result segments.</param>
998
1021
/// <param name="processed">A list indicating whether each event at the corresponding index has been processed.</param>
999
- /// <param name="originalPos">The original position to return if no unprocessed event is found.</param>
1000
- /// <param name="iterationMap"></param>
1001
- /// <param name="found"></param>
1022
+ /// <param name="iterationMap">A precomputed map that indicates the next position to check for unprocessed events.</param>
1023
+ /// <param name="found">A boolean indicating whether an unprocessed event was found.</param>
1002
1024
/// <returns>The index of the next unprocessed position.</returns>
1003
1025
/// <remarks>
1004
1026
/// This method searches forward from the current position until it finds an unprocessed event with
0 commit comments