Skip to content

Commit e41d49a

Browse files
committed
Incorporated the rust port
* Did not add the new SignedArea method. More tests are failing with it * Did not touch the SegmentComparer. Adding it makes more tests fail * We now only have 9 Tests failing
1 parent 141e358 commit e41d49a

14 files changed

+129
-59
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using System;
5+
using System.Runtime.CompilerServices;
6+
7+
namespace PolygonClipper;
8+
9+
/// <summary>
10+
/// Provides extension methods for floating-point numbers.
11+
/// </summary>
12+
internal static class FloatExtensions
13+
{
14+
/// <summary>
15+
/// Returns the next representable double value in the direction of y.
16+
/// </summary>
17+
/// <remarks><see href="https://docs.rs/float_next_after/latest/src/float_next_after/lib.rs.html"/></remarks>
18+
/// <param name="x">The starting floating-point number.</param>
19+
/// <param name="y">The target floating-point number.</param>
20+
/// <returns>The next representable value of x towards y.</returns>
21+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
22+
public static double NextAfter(this double x, double y)
23+
{
24+
// Special cases
25+
if (double.IsNaN(x) || double.IsNaN(y))
26+
{
27+
return double.NaN;
28+
}
29+
30+
if (x == y)
31+
{
32+
return y;
33+
}
34+
35+
if (double.IsPositiveInfinity(x))
36+
{
37+
return double.PositiveInfinity;
38+
}
39+
40+
if (double.IsNegativeInfinity(x))
41+
{
42+
return double.NegativeInfinity;
43+
}
44+
45+
// Handle stepping from zero
46+
if (x == 0D)
47+
{
48+
return Math.CopySign(double.Epsilon, y); // Smallest positive subnormal double
49+
}
50+
51+
// Convert double to raw bits
52+
long bits = BitConverter.DoubleToInt64Bits(x);
53+
54+
// Adjust bits to get the next representable value
55+
if ((y > x) == (x > 0D)) // Moving in the same sign direction
56+
{
57+
bits++;
58+
}
59+
else
60+
{
61+
bits--;
62+
}
63+
64+
// Convert bits back to double
65+
double next = BitConverter.Int64BitsToDouble(bits);
66+
67+
// Ensure correct handling of signed zeros
68+
if (next == 0D)
69+
{
70+
return Math.CopySign(next, x);
71+
}
72+
73+
return next;
74+
}
75+
}

src/PolygonClipper/PolygonClipper.cs

Lines changed: 19 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,8 @@ public Polygon Run()
153153
}
154154

155155
Box2 clippingBB = new(min, max);
156-
if (TryTrivialOperationForNonOverlappingBoundingBoxes(subject, clipping, subjectBB, clippingBB, operation, out result))
156+
if (TryTrivialOperationForNonOverlappingBoundingBoxes(subject, clipping, subjectBB, clippingBB, operation,
157+
out result))
157158
{
158159
return result;
159160
}
@@ -221,17 +222,15 @@ public Polygon Run()
221222
sweepEvent = sweepEvent.OtherEvent;
222223
int it = sweepEvent.PosSL;
223224
prevEvent = statusLine.Prev(it);
224-
225-
statusLine.RemoveAt(it);
226-
227-
// Shift `next` to account for the removal
228-
nextEvent = statusLine.Next(it - 1);
225+
nextEvent = statusLine.Next(it);
229226

230227
// Check intersection between neighbors
231228
if (prevEvent != null && nextEvent != null)
232229
{
233230
_ = PossibleIntersection(prevEvent, nextEvent, eventQueue);
234231
}
232+
233+
statusLine.RemoveAt(it);
235234
}
236235
}
237236

@@ -364,25 +363,13 @@ private static void ProcessSegment(
364363
e1.ContourId = e2.ContourId = contourId;
365364

366365
// Determine which endpoint is the left endpoint
367-
//if (s.Min == s.Source)
368-
//{
369-
// e2.Left = false;
370-
//}
371-
//else if (s.Min == s.Target)
372-
//{
373-
// e1.Left = false;
374-
//}
375-
//else
376-
{
377-
// As a fallback, use the comparator for floating-point precision issues
378-
if (eventQueue.Comparer.Compare(e1, e2) < 0)
379-
{
380-
e2.Left = false;
381-
}
382-
else
383-
{
384-
e1.Left = false;
385-
}
366+
if (eventQueue.Comparer.Compare(e1, e2) < 0)
367+
{
368+
e2.Left = false;
369+
}
370+
else
371+
{
372+
e1.Left = false;
386373
}
387374

388375
min = Vertex.Min(min, s.Min);
@@ -503,8 +490,9 @@ private static bool InResult(SweepEvent sweepEvent, BooleanOperation operation)
503490
{
504491
BooleanOperation.Intersection => !sweepEvent.OtherInOut,
505492
BooleanOperation.Union => sweepEvent.OtherInOut,
506-
BooleanOperation.Difference => (sweepEvent.OtherInOut && sweepEvent.PolygonType == PolygonType.Subject) ||
507-
(!sweepEvent.OtherInOut && sweepEvent.PolygonType == PolygonType.Clipping),
493+
BooleanOperation.Difference =>
494+
(sweepEvent.OtherInOut && sweepEvent.PolygonType == PolygonType.Subject) ||
495+
(!sweepEvent.OtherInOut && sweepEvent.PolygonType == PolygonType.Clipping),
508496
BooleanOperation.Xor => true,
509497
_ => false,
510498
},
@@ -711,7 +699,7 @@ private static void DivideSegment(
711699
{
712700
// TODO: enabling this line makes a single test issue76.geojson fail.
713701
// The files are different in the two reference repositories but both fail.
714-
// p = new Vertex(NextAfter(p.X, true), p.Y);
702+
p = new Vertex(p.X.NextAfter(double.PositiveInfinity), p.Y);
715703
}
716704

717705
// Create the right event for the left segment (new right endpoint)
@@ -740,34 +728,6 @@ private static void DivideSegment(
740728
eventQueue.Enqueue(r);
741729
}
742730

743-
/// <summary>
744-
/// Returns the next representable double-precision floating-point value in the given direction.
745-
/// <see href="https://docs.rs/float_next_after/latest/float_next_after/trait.NextAfter.html"/>
746-
/// </summary>
747-
/// <param name="x">The starting double value.</param>
748-
/// <param name="up">If true, moves towards positive infinity; otherwise, towards negative infinity.</param>
749-
/// <returns>The next representable double in the given direction.</returns>
750-
private static double NextAfter(double x, bool up)
751-
{
752-
if (double.IsNaN(x) || x == double.PositiveInfinity || x == double.NegativeInfinity)
753-
{
754-
return x; // NaN and infinity stay the same
755-
}
756-
757-
// Convert double to its IEEE 754 bit representation
758-
long bits = BitConverter.DoubleToInt64Bits(x);
759-
if (up)
760-
{
761-
bits += (bits >= 0) ? 1 : -1; // Increase magnitude
762-
}
763-
else
764-
{
765-
bits += (bits > 0) ? -1 : 1; // Decrease magnitude
766-
}
767-
768-
return BitConverter.Int64BitsToDouble(bits);
769-
}
770-
771731
/// <summary>
772732
/// Connects edges in the result polygon by processing the sweep events
773733
/// and constructing contours for the final result.
@@ -860,8 +820,7 @@ private static Polygon ConnectEdges(List<SweepEvent> sortedEvents, SweepEventCom
860820
resultEvents[pos].OutputContourId = contourId;
861821
contour.AddVertex(resultEvents[pos].Point);
862822
pos = NextPos(pos, resultEvents, processed, originalPos);
863-
}
864-
while (pos != originalPos && pos < resultEvents.Count);
823+
} while (pos != originalPos && pos < resultEvents.Count);
865824

866825
result.Push(contour);
867826
}
@@ -878,7 +837,8 @@ private static Polygon ConnectEdges(List<SweepEvent> sortedEvents, SweepEventCom
878837
polygon.Push(contour);
879838

880839
// Followed by holes if any
881-
for (int j = 0; j < contour.HoleCount; j++) {
840+
for (int j = 0; j < contour.HoleCount; j++)
841+
{
882842
int holeId = contour.GetHoleIndex(j);
883843
polygon.Push(result[holeId]);
884844
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using Xunit;
5+
6+
namespace PolygonClipper.Tests;
7+
8+
public class FloatExtensionsTests
9+
{
10+
public static TheoryData<double, double, double> NextAfterTestData => new()
11+
{
12+
{ 0.0, double.PositiveInfinity, double.Epsilon },
13+
{ -0.0, double.PositiveInfinity, double.Epsilon },
14+
{ 0.0, double.NegativeInfinity, -double.Epsilon },
15+
{ -0.0, double.NegativeInfinity, -double.Epsilon },
16+
{ 1.0, double.PositiveInfinity, 1.0000000000000002 },
17+
{ 1.0, double.NegativeInfinity, 0.9999999999999999 },
18+
{ -1.0, double.PositiveInfinity, -0.9999999999999999 },
19+
{ -1.0, double.NegativeInfinity, -1.0000000000000002 },
20+
{ double.MaxValue, double.PositiveInfinity, double.PositiveInfinity },
21+
{ double.MinValue, double.NegativeInfinity, double.NegativeInfinity },
22+
{ double.PositiveInfinity, double.PositiveInfinity, double.PositiveInfinity },
23+
{ double.NegativeInfinity, double.NegativeInfinity, double.NegativeInfinity },
24+
{ double.NaN, double.PositiveInfinity, double.NaN },
25+
{ double.NaN, double.NegativeInfinity, double.NaN },
26+
};
27+
28+
[Theory]
29+
[MemberData(nameof(NextAfterTestData))]
30+
public void NextAfter_ShouldReturnCorrectResult(double input, double target, double expected)
31+
{
32+
double result = input.NextAfter(target);
33+
Assert.Equal(expected, result);
34+
}
35+
}

tests/TestData/Generic/failing/basic2_poly_with_hole.geojson renamed to tests/TestData/Generic/basic2_poly_with_hole.geojson

File renamed without changes.

tests/TestData/Generic/failing/basic4_multi_poly_with_hole.geojson renamed to tests/TestData/Generic/basic4_multi_poly_with_hole.geojson

File renamed without changes.

tests/TestData/Generic/failing/daef_cross_selfintersecting.geojson renamed to tests/TestData/Generic/daef_cross_selfintersecting.geojson

File renamed without changes.

tests/TestData/Generic/failing/daef_holed_rectangle2.geojson renamed to tests/TestData/Generic/daef_holed_rectangle2.geojson

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)