153 lines
4.4 KiB
C#
153 lines
4.4 KiB
C#
using System.Numerics;
|
|
using TheLivingWorld.Core.Geo;
|
|
using TheLivingWorld.Osm.Import;
|
|
|
|
namespace TheLivingWorld.Tests;
|
|
|
|
public class GeometryClipperTests
|
|
{
|
|
private static readonly RectBounds Unit = new(0, 0, 100, 100);
|
|
|
|
[Fact]
|
|
public void Leaves_a_polygon_that_is_already_inside_alone()
|
|
{
|
|
Vector2[] square = [new(10, 10), new(90, 10), new(90, 90), new(10, 90)];
|
|
|
|
var clipped = GeometryClipper.ClipPolygon(square, Unit);
|
|
|
|
Assert.NotNull(clipped);
|
|
Assert.Equal(4, clipped.Length);
|
|
Assert.Equal(Polygons.Area(square), Polygons.Area(clipped), 1);
|
|
}
|
|
|
|
[Fact]
|
|
public void Trims_a_polygon_that_hangs_over_the_edge()
|
|
{
|
|
Vector2[] overhanging = [new(50, 50), new(200, 50), new(200, 90), new(50, 90)];
|
|
|
|
var clipped = GeometryClipper.ClipPolygon(overhanging, Unit);
|
|
|
|
Assert.NotNull(clipped);
|
|
Assert.All(clipped, point => Assert.InRange(point.X, 0, 100));
|
|
Assert.Equal(50f * 40f, Polygons.Area(clipped), 1);
|
|
}
|
|
|
|
[Fact]
|
|
public void Drops_a_polygon_that_misses_the_box_entirely()
|
|
{
|
|
Vector2[] elsewhere = [new(500, 500), new(600, 500), new(600, 600)];
|
|
|
|
Assert.Null(GeometryClipper.ClipPolygon(elsewhere, Unit));
|
|
}
|
|
|
|
[Fact]
|
|
public void Keeps_the_part_of_a_line_that_crosses_the_box()
|
|
{
|
|
Vector2[] line = [new(-50, 50), new(150, 50)];
|
|
|
|
var runs = GeometryClipper.ClipPolyline(line, Unit);
|
|
|
|
var run = Assert.Single(runs);
|
|
Assert.Equal(new Vector2(0, 50), run[0]);
|
|
Assert.Equal(new Vector2(100, 50), run[^1]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Splits_a_line_that_leaves_and_comes_back()
|
|
{
|
|
Vector2[] line = [new(10, 50), new(150, 50), new(150, 20), new(10, 20)];
|
|
|
|
var runs = GeometryClipper.ClipPolyline(line, Unit);
|
|
|
|
Assert.Equal(2, runs.Count);
|
|
Assert.All(runs, run => Assert.All(run, point => Assert.InRange(point.X, 0, 100)));
|
|
}
|
|
|
|
[Fact]
|
|
public void Drops_a_line_that_never_touches_the_box()
|
|
{
|
|
Vector2[] line = [new(-50, -50), new(-10, -20)];
|
|
|
|
Assert.Empty(GeometryClipper.ClipPolyline(line, Unit));
|
|
}
|
|
}
|
|
|
|
public class PolygonsTests
|
|
{
|
|
[Fact]
|
|
public void Computes_the_area_of_a_square()
|
|
{
|
|
Vector2[] square = [new(0, 0), new(10, 0), new(10, 10), new(0, 10)];
|
|
|
|
Assert.Equal(100f, Polygons.Area(square));
|
|
}
|
|
|
|
[Fact]
|
|
public void Detects_containment()
|
|
{
|
|
Vector2[] square = [new(0, 0), new(10, 0), new(10, 10), new(0, 10)];
|
|
|
|
Assert.True(Polygons.Contains(square, new Vector2(5, 5)));
|
|
Assert.False(Polygons.Contains(square, new Vector2(15, 5)));
|
|
}
|
|
}
|
|
|
|
public class RingAssemblerTests
|
|
{
|
|
[Fact]
|
|
public void Joins_fragments_into_one_ring_whatever_their_order_or_direction()
|
|
{
|
|
// A 10x10 square split into three fragments; the middle one runs backwards.
|
|
List<IReadOnlyList<Vector2>> fragments =
|
|
[
|
|
[new Vector2(10, 10), new Vector2(0, 10), new Vector2(0, 0)],
|
|
[new Vector2(0, 0), new Vector2(10, 0)],
|
|
[new Vector2(10, 10), new Vector2(10, 0)],
|
|
];
|
|
|
|
var rings = RingAssembler.Assemble(fragments);
|
|
|
|
var ring = Assert.Single(rings);
|
|
Assert.Equal(4, ring.Length);
|
|
Assert.Equal(100f, Polygons.Area(ring));
|
|
}
|
|
|
|
[Fact]
|
|
public void Accepts_a_way_that_is_already_closed()
|
|
{
|
|
List<IReadOnlyList<Vector2>> fragments =
|
|
[
|
|
[new Vector2(0, 0), new Vector2(10, 0), new Vector2(10, 10), new Vector2(0, 10), new Vector2(0, 0)],
|
|
];
|
|
|
|
var ring = Assert.Single(RingAssembler.Assemble(fragments));
|
|
|
|
// The repeated closing vertex is dropped: a ring is stored open.
|
|
Assert.Equal(4, ring.Length);
|
|
}
|
|
|
|
[Fact]
|
|
public void Drops_fragments_that_never_close()
|
|
{
|
|
List<IReadOnlyList<Vector2>> fragments =
|
|
[
|
|
[new Vector2(0, 0), new Vector2(10, 0)],
|
|
[new Vector2(10, 0), new Vector2(10, 10)],
|
|
];
|
|
|
|
Assert.Empty(RingAssembler.Assemble(fragments));
|
|
}
|
|
|
|
[Fact]
|
|
public void Separates_two_independent_rings()
|
|
{
|
|
List<IReadOnlyList<Vector2>> fragments =
|
|
[
|
|
[new Vector2(0, 0), new Vector2(10, 0), new Vector2(10, 10), new Vector2(0, 10), new Vector2(0, 0)],
|
|
[new Vector2(50, 50), new Vector2(60, 50), new Vector2(60, 60), new Vector2(50, 50)],
|
|
];
|
|
|
|
Assert.Equal(2, RingAssembler.Assemble(fragments).Count);
|
|
}
|
|
}
|