// Copyright (C) Stichting Deltares 2024. All rights reserved.
//
// This file is part of the Dam Engine.
//
// The Dam Engine is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
//
// All names, logos, and references to "Deltares" are registered trademarks of
// Stichting Deltares and remain full property of Stichting Deltares at all times.
// All rights reserved.
using System.Collections.Generic;
using System.Linq;
using Deltares.DamEngine.Data.Geometry;
using NUnit.Framework;
namespace Deltares.DamEngine.Data.Tests.Geometry;
[TestFixture]
public class GeometryDataTests
{
[Test]
public void TestRemoveDoublesFromNewlyEffectedPointsAndCurves()
{
var point1 = new Point2D(1, 0); // to be kept
var point2 = new Point2D(1, 0); // to be removed as == p1
var point3 = new Point2D(5, 0); // to be kept
var point4 = new Point2D(5, 0); // to be removed as == p3
var point5 = new Point2D(1, 0); // to be removed as == p1
var point6 = new Point2D(5, 0); // to be removed as == p3
var curve1 = new GeometryCurve(point1, point2); // to be removed as no lenght
var curve2 = new GeometryCurve(point2, point3); // to be kept
var curve3 = new GeometryCurve(point1, point2); // to be removed as no lenght
var curve4 = new GeometryCurve(point4, point1); // to be removed as == c2 (in reverse order)
var curve5 = new GeometryCurve(point3, point5); // to be removed as == c2 (in reverse order)
var curve6 = new GeometryCurve(point1, point1); // to be removed as no lenght
var curve7 = new GeometryCurve(point1, point1); // to be removed as no lenght
var curve8 = new GeometryCurve(null, null); // to be removed as no points
var curve9 = new GeometryCurve(null, point2); // to be removed as no headpoint
var curve10 = new GeometryCurve(point3, null); // to be removed as no endpoint
var geometrymodel = new GeometryData();
geometrymodel.NewlyEffectedPoints.Add(point1);
geometrymodel.NewlyEffectedPoints.Add(point2);
geometrymodel.NewlyEffectedPoints.Add(point3);
geometrymodel.NewlyEffectedPoints.Add(point4);
geometrymodel.NewlyEffectedPoints.Add(point5);
geometrymodel.NewlyEffectedPoints.Add(point6);
geometrymodel.NewlyEffectedCurves.Add(curve1);
geometrymodel.NewlyEffectedCurves.Add(curve2);
geometrymodel.NewlyEffectedCurves.Add(curve3);
geometrymodel.NewlyEffectedCurves.Add(curve4);
geometrymodel.NewlyEffectedCurves.Add(curve5);
geometrymodel.NewlyEffectedCurves.Add(curve6);
geometrymodel.NewlyEffectedCurves.Add(curve7);
geometrymodel.NewlyEffectedCurves.Add(curve8);
geometrymodel.NewlyEffectedCurves.Add(curve9);
geometrymodel.NewlyEffectedCurves.Add(curve10);
geometrymodel.RemoveDoublesFromNewlyEffectedPointsAndCurves();
// only two points are unique by location
Assert.That(geometrymodel.NewlyEffectedPoints.Count, Is.EqualTo(2));
// only two curves hold both head and endpoint with unique locations
Assert.That(geometrymodel.NewlyEffectedCurves.Count, Is.EqualTo(1));
}
[Test]
public void GeometryHelperTestSurfaceTwoVertLayers()
{
GeometryData gData = CreateGeometrySurface2();
GeometryPointString line = gData.SurfaceLine;
Assert.That(line.Points.Count, Is.EqualTo(3));
Assert.That(line.Points.OrderBy(p => p.X).First().X, Is.EqualTo(1.0));
Assert.That(line.Points.OrderByDescending(p => p.X).First().X, Is.EqualTo(10.0));
}
[Test]
public void GeometryDataTestSurfaceTwoHorLayers()
{
GeometryData gData = CreateGeometrySurface();
GeometryPointString line = gData.SurfaceLine;
Assert.That(line.Points.Count, Is.EqualTo(2));
Assert.That(line.Points.OrderBy(p => p.X).First().X, Is.EqualTo(1.0));
Assert.That(line.Points.OrderByDescending(p => p.X).First().X, Is.EqualTo(10.0));
}
[Test]
public void GeometryDataTestClear()
{
GeometryData gData = CreateGeometrySurface();
Assert.Multiple(() =>
{
Assert.That(gData.Points, Has.Count.EqualTo(6));
Assert.That(gData.Curves, Has.Count.EqualTo(7));
Assert.That(gData.Loops, Has.Count.EqualTo(2));
Assert.That(gData.Surfaces, Has.Count.EqualTo(2));
});
gData.Clear();
Assert.Multiple(() =>
{
Assert.That(gData.Points, Has.Count.EqualTo(0));
Assert.That(gData.Curves, Has.Count.EqualTo(0));
Assert.That(gData.Loops, Has.Count.EqualTo(0));
Assert.That(gData.Surfaces, Has.Count.EqualTo(0));
Assert.That(gData.NewlyEffectedPoints, Has.Count.EqualTo(0));
Assert.That(gData.NewlyEffectedCurves, Has.Count.EqualTo(0));
});
}
[Test]
public void GeometryDataTestGetRightCurves()
{
GeometryData gData = CreateGeometrySurface();
var rightCurves = gData.GetRightCurves();
Assert.Multiple(() =>
{
Assert.That(rightCurves, Has.Count.EqualTo(5));
Assert.That(rightCurves[0].HeadPoint.X, Is.EqualTo(1.0));
Assert.That(rightCurves[0].EndPoint.X, Is.EqualTo(10.0));
Assert.That(rightCurves[1].HeadPoint.X, Is.EqualTo(10.0));
Assert.That(rightCurves[1].EndPoint.X, Is.EqualTo(10.0));
Assert.That(rightCurves[0].HeadPoint.Z, Is.EqualTo(0.0));
Assert.That(rightCurves[0].EndPoint.Z, Is.EqualTo(0.0));
Assert.That(rightCurves[1].HeadPoint.Z, Is.EqualTo(0.0));
Assert.That(rightCurves[1].EndPoint.Z, Is.EqualTo(5.0));
});
}
[Test]
public void GeometryDataTestGetLeftCurves()
{
GeometryData gData = CreateGeometrySurface();
var leftCurves = gData.GetLeftCurves();
Assert.Multiple(() =>
{
Assert.That(leftCurves, Has.Count.EqualTo(5));
Assert.That(leftCurves[0].HeadPoint.X, Is.EqualTo(1.0));
Assert.That(leftCurves[0].EndPoint.X, Is.EqualTo(10.0));
Assert.That(leftCurves[1].HeadPoint.X, Is.EqualTo(10.0));
Assert.That(leftCurves[1].EndPoint.X, Is.EqualTo(1.0));
Assert.That(leftCurves[0].HeadPoint.Z, Is.EqualTo(0.0));
Assert.That(leftCurves[0].EndPoint.Z, Is.EqualTo(0.0));
Assert.That(leftCurves[1].HeadPoint.Z, Is.EqualTo(10.0));
Assert.That(leftCurves[1].EndPoint.Z, Is.EqualTo(10.0));
});
}
[Test]
public void TestSimpleGeneration()
{
var geom = new GeometryData
{
Left = 0,
Bottom = 0,
Right = 100
};
var p1 = new Point2D(0, 5);
geom.NewlyEffectedPoints.Add(p1);
var p2 = new Point2D(100, 5);
geom.NewlyEffectedPoints.Add(p2);
var p3 = new Point2D(0, 0);
geom.NewlyEffectedPoints.Add(p3);
var p4 = new Point2D(100, 0);
geom.NewlyEffectedPoints.Add(p4);
geom.NewlyEffectedCurves.Add(new GeometryCurve(p1, p2));
geom.NewlyEffectedCurves.Add(new GeometryCurve(p3, p4));
geom.NewlyEffectedCurves.Add(new GeometryCurve(p1, p3));
geom.NewlyEffectedCurves.Add(new GeometryCurve(p2, p4));
geom.RegenerateGeometry();
Assert.That(geom.Points.Count, Is.EqualTo(4));
Assert.That(geom.Curves.Count, Is.EqualTo(4));
Assert.That(geom.Surfaces.Count, Is.EqualTo(1));
Assert.That(geom.Surfaces[0].InnerLoops.Count, Is.EqualTo(0));
Assert.That(geom.Surfaces[0].OuterLoop.Count, Is.EqualTo(4));
Assert.That(geom.Surfaces[0].OuterLoop.CurveList.Count, Is.EqualTo(4));
Assert.That(geom.Surfaces[0].OuterLoop.Points.Count, Is.EqualTo(4));
Assert.That(geom.Surfaces[0].OuterLoop.HasArea(), Is.True);
Assert.That(geom.Surfaces[0].OuterLoop.IsLoop(), Is.True);
Assert.That(geom.Surfaces[0].OuterLoop.IsPointInLoopArea(new Point2D(25, 3)), Is.True);
Assert.That(geom.Surfaces[0].OuterLoop.IsPointInLoopArea(new Point2D(25, 5.1)), Is.False);
}
[Test]
[TestCase(1, 0, true)]
[TestCase(10, 0, true)]
[TestCase(10, 10, true)]
[TestCase(1, 10, true)]
[TestCase(1, 1, false)]
[TestCase(0, 1, false)]
public void GivenPointsWhenGetPointAtLocationCalledThenCorrectPointIsReturned(double x, double z, bool isPresent)
{
const double tolerance = 1e-6;
var geometryModel = new GeometryData();
var point1 = new Point2D(1, 0);
var point2 = new Point2D(10, 0);
var point3 = new Point2D(10, 10);
var point4 = new Point2D(1, 10);
geometryModel.Points.AddRange(new[]
{
point1,
point2,
point3,
point4
});
Point2D point = geometryModel.GetPointAtLocation(new Point2D(x, z));
if (isPresent)
{
Assert.Multiple(() =>
{
Assert.That(point, Is.Not.Null);
Assert.That(point.X, Is.EqualTo(x).Within(tolerance));
Assert.That(point.Z, Is.EqualTo(z).Within(tolerance));
});
}
else
{
Assert.That(point, Is.Null);
}
}
private GeometryData CreateGeometrySurface()
{
var geometryModel = new GeometryData();
/* The following model looks as follows
*
* |----------|
* | |
* |----------|
* | |
* |----------|
*
*/
var point1 = new Point2D(1, 0);
var point2 = new Point2D(10, 0);
var point3 = new Point2D(10, 10);
var point4 = new Point2D(1, 10);
var point5 = new Point2D(1, 5);
var point6 = new Point2D(10, 5);
var curve1 = new GeometryCurve(point1, point2);
var curve2 = new GeometryCurve(point2, point3);
var curve3 = new GeometryCurve(point3, point4);
var curve4 = new GeometryCurve(point4, point1);
var curve5 = new GeometryCurve(point5, point6);
geometryModel.Points.AddRange(new[]
{
point1,
point2,
point3,
point4,
point5,
point6
});
geometryModel.Curves.AddRange(new[]
{
curve1,
curve2,
curve3,
curve4,
curve5
});
geometryModel.NewlyEffectedPoints.AddRange(geometryModel.Points);
geometryModel.NewlyEffectedCurves.AddRange(geometryModel.Curves);
geometryModel.RegenerateGeometry();
geometryModel.Rebox();
return geometryModel;
}
private GeometryData CreateGeometrySurface2()
{
var geometryModel = new GeometryData();
/* The following model looks as follows
*
* |-----|----|
* | | |
* | | |
* | | |
* |-----|----|
*
*/
var point1 = new Point2D(1, 0);
var point2 = new Point2D(1, 10);
var point3 = new Point2D(5, 10);
var point4 = new Point2D(5, 0);
var point5 = new Point2D(10, 10);
var point6 = new Point2D(10, 0);
var curve1 = new GeometryCurve(point1, point2);
var curve2 = new GeometryCurve(point2, point3);
var curve3 = new GeometryCurve(point3, point4);
var curve4 = new GeometryCurve(point4, point1);
var curve5 = new GeometryCurve(point3, point5);
var curve6 = new GeometryCurve(point5, point6);
var curve7 = new GeometryCurve(point6, point4);
geometryModel.Points.AddRange(new[]
{
point1,
point2,
point3,
point4,
point5,
point6
});
geometryModel.Curves.AddRange(new[]
{
curve1,
curve2,
curve3,
curve4,
curve5,
curve6,
curve7
});
geometryModel.NewlyEffectedPoints.AddRange(geometryModel.Points);
geometryModel.NewlyEffectedCurves.AddRange(geometryModel.Curves);
geometryModel.RegenerateGeometry();
return geometryModel;
}
private GeometryData CreateDonutGeometry()
{
var geometryModel = new GeometryData
{
Left = -10,
Bottom = -10,
Right = 20
};
/* The following model looks as follows
*
* |----------|
* | ------ |
* | | | |
* | | | |
* | ------ |
* |----------|
*
*/
var point1 = new Point2D(0, 0);
var point2 = new Point2D(0, 10);
var point3 = new Point2D(10, 10);
var point4 = new Point2D(10, 0);
var point5 = new Point2D(3, 3);
var point6 = new Point2D(3, 7);
var point7 = new Point2D(7, 7);
var point8 = new Point2D(7, 3);
var curve1 = new GeometryCurve(point1, point2);
var curve2 = new GeometryCurve(point2, point3);
var curve3 = new GeometryCurve(point3, point4);
var curve4 = new GeometryCurve(point4, point1);
var curve5 = new GeometryCurve(point5, point6);
var curve6 = new GeometryCurve(point6, point7);
var curve7 = new GeometryCurve(point7, point8);
var curve8 = new GeometryCurve(point8, point5);
geometryModel.Points.AddRange(new[]
{
point1,
point2,
point3,
point4,
point5,
point6,
point7,
point8
});
geometryModel.Curves.AddRange(new[]
{
curve1,
curve2,
curve3,
curve4,
curve5,
curve5,
curve6,
curve7,
curve8
});
geometryModel.NewlyEffectedPoints.AddRange(geometryModel.Points);
geometryModel.NewlyEffectedCurves.AddRange(geometryModel.Curves);
geometryModel.RegenerateGeometry();
return geometryModel;
}
public class GivenGeometryData
{
private static readonly GeometryData originalGeometryData = CreateGeometryData();
[TestFixture]
public class WhenCloned : GivenGeometryData
{
private readonly GeometryData clonedGeometryData = originalGeometryData.Clone();
[Test]
public void ThenBaseDataIsCopied()
{
Assert.That(clonedGeometryData, Is.Not.EqualTo(originalGeometryData));
Assert.Multiple(() =>
{
Assert.That(clonedGeometryData.Left, Is.EqualTo(originalGeometryData.Left));
Assert.That(clonedGeometryData.Right, Is.EqualTo(originalGeometryData.Right));
Assert.That(clonedGeometryData.Bottom, Is.EqualTo(originalGeometryData.Bottom));
});
}
[Test]
public void ThenPointsAreCopied()
{
Assert.That(clonedGeometryData.Points, Has.Count.EqualTo(originalGeometryData.Points.Count));
Assert.That(clonedGeometryData.Points, Has.Count.GreaterThan(0));
Assert.That(clonedGeometryData.Points[0], Is.Not.EqualTo(originalGeometryData.Points[0]));
Assert.That(clonedGeometryData.Points[0].X, Is.EqualTo(originalGeometryData.Points[0].X));
}
[Test]
public void ThenNewlyEffectedPointsAreCopied()
{
Assert.That(clonedGeometryData.NewlyEffectedPoints, Has.Count.EqualTo(originalGeometryData.NewlyEffectedPoints.Count));
Assert.That(clonedGeometryData.NewlyEffectedPoints, Has.Count.GreaterThan(0));
Assert.That(clonedGeometryData.NewlyEffectedPoints[0], Is.Not.EqualTo(originalGeometryData.NewlyEffectedPoints[0]));
Assert.That(clonedGeometryData.NewlyEffectedPoints[0].X, Is.EqualTo(originalGeometryData.NewlyEffectedPoints[0].X));
}
[Test]
public void ThenCurvesAreCopied()
{
Assert.That(clonedGeometryData.Curves, Has.Count.EqualTo(originalGeometryData.Curves.Count));
Assert.That(clonedGeometryData.Curves, Has.Count.GreaterThan(0));
Assert.That(clonedGeometryData.Curves[0], Is.Not.EqualTo(originalGeometryData.Curves[0]));
Assert.That(clonedGeometryData.Curves[0].HeadPoint.X, Is.EqualTo(originalGeometryData.Curves[0].HeadPoint.X));
}
[Test]
public void ThenNewlyEffectedCurvesAreCopied()
{
Assert.That(clonedGeometryData.NewlyEffectedCurves, Has.Count.EqualTo(originalGeometryData.NewlyEffectedCurves.Count));
Assert.That(clonedGeometryData.NewlyEffectedCurves, Has.Count.GreaterThan(0));
Assert.That(clonedGeometryData.NewlyEffectedCurves[0], Is.Not.EqualTo(originalGeometryData.NewlyEffectedCurves[0]));
Assert.That(clonedGeometryData.NewlyEffectedCurves[0].HeadPoint.X, Is.EqualTo(originalGeometryData.NewlyEffectedCurves[0].HeadPoint.X));
}
[Test]
public void ThenLoopsAreCopied()
{
Assert.That(clonedGeometryData.Loops, Has.Count.EqualTo(originalGeometryData.Loops.Count));
Assert.That(clonedGeometryData.Loops, Has.Count.GreaterThan(0));
Assert.That(clonedGeometryData.Loops[0], Is.Not.EqualTo(originalGeometryData.Loops[0]));
Assert.That(clonedGeometryData.Loops[0].CurveList, Has.Count.GreaterThan(0));
Assert.That(clonedGeometryData.Loops[0].CurveList, Has.Count.EqualTo(originalGeometryData.Loops[0].CurveList.Count));
}
[Test]
public void ThenSurfacesAreCopied()
{
Assert.That(clonedGeometryData.Surfaces, Has.Count.EqualTo(originalGeometryData.Surfaces.Count));
Assert.That(clonedGeometryData.Surfaces, Has.Count.GreaterThan(0));
Assert.That(clonedGeometryData.Surfaces[0], Is.Not.EqualTo(originalGeometryData.Surfaces[0]));
Assert.That(clonedGeometryData.Surfaces[0].OuterLoop.CurveList, Has.Count.GreaterThan(0));
Assert.That(clonedGeometryData.Surfaces[0].OuterLoop.CurveList, Has.Count.EqualTo(originalGeometryData.Surfaces[0].OuterLoop.CurveList.Count));
}
[Test]
public void WhenModified_ThenGeometryDataAreNotEqual()
{
ModifyGeometryData(clonedGeometryData);
Assert.Multiple(() =>
{
Assert.That(clonedGeometryData.Left, Is.Not.EqualTo(originalGeometryData.Left));
Assert.That(clonedGeometryData.Right, Is.Not.EqualTo(originalGeometryData.Right));
Assert.That(clonedGeometryData.Bottom, Is.Not.EqualTo(originalGeometryData.Bottom));
Assert.That(clonedGeometryData.Points, Has.Count.Not.EqualTo(originalGeometryData.Points.Count));
Assert.That(clonedGeometryData.NewlyEffectedPoints, Has.Count.Not.EqualTo(originalGeometryData.NewlyEffectedPoints.Count));
Assert.That(clonedGeometryData.Curves, Has.Count.Not.EqualTo(originalGeometryData.Curves.Count));
Assert.That(clonedGeometryData.NewlyEffectedCurves, Has.Count.Not.EqualTo(originalGeometryData.NewlyEffectedCurves.Count));
Assert.That(clonedGeometryData.Loops, Has.Count.Not.EqualTo(originalGeometryData.Loops.Count));
Assert.That(clonedGeometryData.Surfaces, Has.Count.Not.EqualTo(originalGeometryData.Surfaces.Count));
});
}
}
private static GeometryData CreateGeometryData()
{
var geometryData = new GeometryData
{
Left = -10.0,
Right = 10.0,
Bottom = -10.0
};
geometryData.Points.Add(new Point2D(0.1, 1.1));
geometryData.Points.Add(new Point2D(0.2, 1.2));
geometryData.NewlyEffectedPoints.Add(new Point2D(0.3, 1.3));
geometryData.NewlyEffectedPoints.Add(new Point2D(0.4, 1.4));
geometryData.Curves.Add(new GeometryCurve(geometryData.Points[0], geometryData.Points[1]));
geometryData.NewlyEffectedCurves.Add(new GeometryCurve(geometryData.NewlyEffectedPoints[0], geometryData.NewlyEffectedPoints[1]));
var loop = new GeometryLoop();
loop.CurveList.Add(new GeometryCurve(new Point2D(1.0, 2.0), new Point2D(3.0, 4.0)));
geometryData.Loops.Add(loop);
geometryData.Surfaces.Add(new GeometrySurface(loop));
return geometryData;
}
private static void ModifyGeometryData(GeometryData geometryData)
{
geometryData.Left = geometryData.Left + 1;
geometryData.Right = geometryData.Right + 1;
geometryData.Bottom = geometryData.Bottom + 1;
geometryData.Points.Add(new Point2D(10.0, 20.0));
geometryData.NewlyEffectedPoints.Add(new Point2D(30.0, 40.0));
geometryData.Curves.Add(new GeometryCurve(geometryData.Points[1], geometryData.Points[2]));
geometryData.NewlyEffectedCurves.Add(new GeometryCurve(geometryData.NewlyEffectedPoints[1], geometryData.NewlyEffectedPoints[2]));
var loop = new GeometryLoop();
loop.CurveList.Add(new GeometryCurve(new Point2D(5.0, 6.0), new Point2D(7.0, 8.0)));
geometryData.Loops.Add(loop);
geometryData.Surfaces.Add(new GeometrySurface());
}
}
[Test]
public void GivenGeometryData_WhenGetNextTopCurve_ThenReturnsExpectedCurve()
{
var geometryData = new GeometryData
{
Left = 0.0,
Right = 100.0
};
var leftCurve = new GeometryCurve(new Point2D(0.0, 10.0), new Point2D( 5.0, 200.0));
var curve = new GeometryCurve(new Point2D(5.0, 10.0), new Point2D( 6.0, 10.0));
var connectedCurveLow = new GeometryCurve(new Point2D(6.0, 10.0), new Point2D( 8.0, 11.0));
var connectedCurveHigh = new GeometryCurve(new Point2D(6.0, 10.0), new Point2D( 8.0, 12.0));
var connectedCurveExcluded = new GeometryCurve(new Point2D(6.0, 10.0), new Point2D( 8.0, 13.0));
var boundaryCurves = new List
{
leftCurve, // it is connected, but only curves on the right side are considered
curve, // the curve for which the next top curve is to be found
connectedCurveLow, // even though this is connected on the right side, it is not the highest
connectedCurveHigh, // this is the curve we are looking for
connectedCurveExcluded // even though this is the highest connected curve, it is excluded
};
var excludedCurves = new List
{
connectedCurveExcluded
};
GeometryCurve topCurve = geometryData.GetNextTopCurve(curve, boundaryCurves, excludedCurves);
Assert.That(topCurve, Is.EqualTo(connectedCurveHigh));
}
[Test]
public void GivenRightCurve_WhenGetNextTopCurve_ThenReturnsNull()
{
var geometryData = new GeometryData
{
Left = 0.0,
Right = 100.0
};
var curve = new GeometryCurve(new Point2D(5.0, 10.0), new Point2D( 100.0, 10.0));
var connectedCurve = new GeometryCurve(new Point2D(100.0, 10.0), new Point2D( 50.0, 20.0));
var boundaryCurves = new List
{
curve,
connectedCurve
};
var excludedCurves = new List();
GeometryCurve topCurve = geometryData.GetNextTopCurve(curve, boundaryCurves, excludedCurves);
Assert.That(topCurve, Is.Null);
}
}