Files
OpenNest/OpenNest.Tests/GravographIS/PolylinePrePassTests.cs
T
2026-05-23 12:40:53 -04:00

165 lines
5.1 KiB
C#

using System.Collections.Generic;
using OpenNest.Geometry;
using OpenNest.Posts.GravographIS;
namespace OpenNest.Tests.GravographIS;
public class PolylinePrePassTests
{
[Fact]
public void Stitch_TwoConnectedSegments_BecomeOnePolyline()
{
var inputs = new List<IReadOnlyList<Vector>>
{
new[] { new Vector(0, 0), new Vector(1, 0) },
new[] { new Vector(1, 0), new Vector(1, 1) },
};
var stitched = PolylinePrePass.Stitch(inputs);
Assert.Single(stitched);
Assert.Equal(3, stitched[0].Count);
Assert.Equal(new Vector(0, 0), stitched[0][0]);
Assert.Equal(new Vector(1, 0), stitched[0][1]);
Assert.Equal(new Vector(1, 1), stitched[0][2]);
}
[Fact]
public void Stitch_FourSegmentsFormingClosedSquare_BecomeOnePolyline()
{
var inputs = new List<IReadOnlyList<Vector>>
{
new[] { new Vector(0, 0), new Vector(1, 0) },
new[] { new Vector(1, 0), new Vector(1, 1) },
new[] { new Vector(1, 1), new Vector(0, 1) },
new[] { new Vector(0, 1), new Vector(0, 0) },
};
var stitched = PolylinePrePass.Stitch(inputs);
Assert.Single(stitched);
// Four edges + closing return-to-start = five vertices.
Assert.Equal(5, stitched[0].Count);
}
[Fact]
public void Stitch_ReversesOneSegmentToMakeAJoin()
{
// Second segment is given backward; stitcher should reverse it.
var inputs = new List<IReadOnlyList<Vector>>
{
new[] { new Vector(0, 0), new Vector(1, 0) },
new[] { new Vector(2, 0), new Vector(1, 0) },
};
var stitched = PolylinePrePass.Stitch(inputs);
Assert.Single(stitched);
Assert.Equal(3, stitched[0].Count);
Assert.Equal(new Vector(0, 0), stitched[0][0]);
Assert.Equal(new Vector(2, 0), stitched[0][stitched[0].Count - 1]);
}
[Fact]
public void Stitch_DisjointSegments_StayDistinct()
{
var inputs = new List<IReadOnlyList<Vector>>
{
new[] { new Vector(0, 0), new Vector(1, 0) },
new[] { new Vector(5, 5), new Vector(6, 5) },
};
var stitched = PolylinePrePass.Stitch(inputs);
Assert.Equal(2, stitched.Count);
}
[Fact]
public void Stitch_DropsZeroAndSinglePointPolylines()
{
var inputs = new List<IReadOnlyList<Vector>>
{
new Vector[] { },
new[] { new Vector(0, 0) },
new[] { new Vector(0, 0), new Vector(1, 0) },
};
var stitched = PolylinePrePass.Stitch(inputs);
Assert.Single(stitched);
Assert.Equal(2, stitched[0].Count);
}
[Fact]
public void Reorder_ReducesTotalPenUpTravelVsWorstCase()
{
// Three short polylines at (0,0), (10,0), (5,0). The greedy NN starting
// from origin should pick (0,0)→(5,0)→(10,0) (travels of 4 + 4 ≈ 8) over
// the worst-case input order (0,0)→(10,0)→(5,0) (travels 9 + 4 ≈ 13).
var inputs = new List<IReadOnlyList<Vector>>
{
new[] { new Vector(0, 0), new Vector(1, 0) },
new[] { new Vector(10, 0), new Vector(11, 0) },
new[] { new Vector(5, 0), new Vector(6, 0) },
};
var reordered = PolylinePrePass.Reorder(inputs);
Assert.Equal(3, reordered.Count);
var travelBefore = TotalPenUpTravel(inputs);
var travelAfter = TotalPenUpTravel(reordered);
Assert.True(travelAfter < travelBefore,
$"Expected reorder to reduce pen-up travel; before={travelBefore}, after={travelAfter}");
}
[Fact]
public void Reorder_ReversesPolylineIfTailIsCloser()
{
// Origin (0,0); a single polyline whose tail is much closer to origin
// than its head. Reorder should flip it.
var inputs = new List<IReadOnlyList<Vector>>
{
new[] { new Vector(10, 0), new Vector(0.5, 0) },
};
var reordered = PolylinePrePass.Reorder(inputs, allowReverse: true);
Assert.Single(reordered);
Assert.Equal(new Vector(0.5, 0), reordered[0][0]);
Assert.Equal(new Vector(10, 0), reordered[0][1]);
}
[Fact]
public void Reorder_ReverseDisabled_KeepsDirection()
{
var inputs = new List<IReadOnlyList<Vector>>
{
new[] { new Vector(10, 0), new Vector(0.5, 0) },
};
var reordered = PolylinePrePass.Reorder(inputs, allowReverse: false);
Assert.Single(reordered);
Assert.Equal(new Vector(10, 0), reordered[0][0]);
Assert.Equal(new Vector(0.5, 0), reordered[0][1]);
}
private static double TotalPenUpTravel(IEnumerable<IReadOnlyList<Vector>> polylines)
{
var total = 0.0;
Vector? last = null;
foreach (var p in polylines)
{
if (p == null || p.Count < 2) continue;
if (last.HasValue)
{
var dx = p[0].X - last.Value.X;
var dy = p[0].Y - last.Value.Y;
total += System.Math.Sqrt(dx * dx + dy * dy);
}
last = p[p.Count - 1];
}
return total;
}
}