// Copyright (C) Stichting Deltares 2019. All rights reserved. // // This file is part of Riskeer. // // Riskeer is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser 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 Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser 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; using System.Collections.Generic; using System.Linq; using Core.Common.Base.Geometry; using Core.Common.TestUtil; using NUnit.Framework; namespace Core.Common.Geometry.Test { [TestFixture] public class AdvancedMath2DTest { [Test] public void PolygonIntersectionWithPolygon_NoIntersection_ReturnsEmptyCollection() { // Setup Point2D[] polyA = CreateBasePolygon(); var polyB = new[] { new Point2D(5, 0), new Point2D(5, 4), new Point2D(9, 4), new Point2D(9, 0) }; // Call IEnumerable> intersections = AdvancedMath2D.PolygonIntersectionWithPolygon(polyA, polyB); // Assert CollectionAssert.IsEmpty(intersections); } [Test] public void PolygonIntersectionWithPolygon_WithSelfIntersectingPolygon_ThrowsInvalidPolygonException() { // Setup Point2D[] polyA = CreateBasePolygon(); var polyB = new[] { new Point2D(4, 0), new Point2D(4, 4), new Point2D(6, 0), new Point2D(8, 4), new Point2D(8, 0) }; // Call TestDelegate test = () => AdvancedMath2D.PolygonIntersectionWithPolygon(polyB, polyA); // Assert Assert.Throws(test); } [Test] public void PolygonIntersectionWithPolygon_IntersectsComplete_ReturnsIntersectionEqualToPolygon() { // Setup Point2D[] polyA = CreateBasePolygon(); var polyB = new[] { new Point2D(0, 0), new Point2D(0, 4), new Point2D(4, 4), new Point2D(4, 0) }; // Call IEnumerable> intersections = AdvancedMath2D.PolygonIntersectionWithPolygon(polyA, polyB); // Assert Assert.AreEqual(1, intersections.Count()); Assert.AreEqual(polyA, intersections.ElementAt(0)); } [Test] public void PolygonIntersectionWithPolygon_PartlyIntersects_ReturnsPartialIntersection() { // Setup Point2D[] polyA = CreateBasePolygon(); var polyB = new[] { new Point2D(0, 0), new Point2D(0, 4), new Point2D(2, 4), new Point2D(2, 0) }; // Call IEnumerable> intersections = AdvancedMath2D.PolygonIntersectionWithPolygon(polyA, polyB); // Assert Assert.AreEqual(1, intersections.Count()); CollectionAssert.AreEqual(polyB, intersections.ElementAt(0)); } [Test] public void PolygonIntersectionWithPolygon_TouchesOnSide_ReturnsEmptyCollection() { // Setup Point2D[] polyA = CreateBasePolygon(); var polyB = new[] { new Point2D(4, 0), new Point2D(4, 4), new Point2D(6, 4), new Point2D(6, 0) }; // Call IEnumerable> intersections = AdvancedMath2D.PolygonIntersectionWithPolygon(polyA, polyB); // Assert Assert.AreEqual(new[] { new[] { new Point2D(4, 4), new Point2D(4, 0) } }, intersections); } [Test] public void PolygonIntersectionWithPolygon_TouchesWithPointOnSide_ReturnsEmptyCollection() { // Setup Point2D[] polyA = CreateBasePolygon(); var polyB = new[] { new Point2D(5, 0), new Point2D(4, 2), new Point2D(5, 4), new Point2D(6, 4), new Point2D(6, 0) }; // Call IEnumerable> intersections = AdvancedMath2D.PolygonIntersectionWithPolygon(polyA, polyB); // Assert Assert.AreEqual(new[] { new[] { new Point2D(4, 2) } }, intersections); } [Test] public void PolygonIntersectionWithPolygon_PartiallyIntersectsTwice_ReturnsTwoIntersections() { // Setup Point2D[] polyA = CreateBasePolygon(); var polyB = new[] { new Point2D(2, 0), new Point2D(2, 1), new Point2D(5, 1), new Point2D(5, 3), new Point2D(2, 3), new Point2D(2, 4), new Point2D(6, 4), new Point2D(6, 0) }; // Call IEnumerable> intersections = AdvancedMath2D.PolygonIntersectionWithPolygon(polyA, polyB); // Assert Assert.AreEqual(2, intersections.Count()); CollectionAssert.AreEqual(new[] { new Point2D(2, 4), new Point2D(4, 4), new Point2D(4, 3), new Point2D(2, 3) }, intersections.ElementAt(0)); CollectionAssert.AreEqual(new[] { new Point2D(4, 1), new Point2D(4, 0), new Point2D(2, 0), new Point2D(2, 1) }, intersections.ElementAt(1)); } [Test] public void PolygonIntersectionWithPolygon_IntersectsPolygonLineAndPoint_ReturnsTwoIntersections() { // Setup Point2D[] polyA = CreateBasePolygon(); var polyB = new[] { new Point2D(0, -2), new Point2D(0, 5), new Point2D(0.5, 5), new Point2D(0.5, -1), new Point2D(1.0, 0), new Point2D(1.5, 0), new Point2D(2.0, -1), new Point2D(3.0, 0), new Point2D(4.0, -2) }; // Call IEnumerable> intersections = AdvancedMath2D.PolygonIntersectionWithPolygon(polyA, polyB); // Assert Assert.AreEqual(3, intersections.Count()); CollectionAssert.AreEqual(new[] { new Point2D(3.0, 0.0) }, intersections.ElementAt(0)); CollectionAssert.AreEqual(new[] { new Point2D(1.5, 0.0), new Point2D(1.0, 0.0) }, intersections.ElementAt(1)); CollectionAssert.AreEqual(new[] { new Point2D(0.0, 0.0), new Point2D(0.0, 4.0), new Point2D(0.5, 4.0), new Point2D(0.5, 0.0) }, intersections.ElementAt(2)); } [Test] public void FromXToXY_WithoutPoints_ThrowsArgumentNullException() { // Call TestDelegate test = () => AdvancedMath2D.FromXToXY(null, new Point2D(0, 0), 3, 2); // Assert string paramName = TestHelper.AssertThrowsArgumentExceptionAndTestMessage( test, "Cannot transform to coordinates without a source.").ParamName; Assert.AreEqual("xCoordinates", paramName); } [Test] public void FromXToXY_WithoutReferencePoint_ThrowsArgumentNullException() { // Call TestDelegate test = () => AdvancedMath2D.FromXToXY(new double[0], null, 3, 2); // Assert string paramName = TestHelper.AssertThrowsArgumentExceptionAndTestMessage( test, "Cannot transform to coordinates without a reference point.").ParamName; Assert.AreEqual("referencePoint", paramName); } [Test] public void FromXToXY_NoPoints_ReturnsEmptyCollection() { // Call IEnumerable points = AdvancedMath2D.FromXToXY(new double[0], new Point2D(0, 0), 3, 2); // Assert CollectionAssert.IsEmpty(points); } [Test] public void FromXToXY_WithoutTransformations_ReturnsCoordinatesOnYAxis() { // Setup double[] xCoordinates = ThreeRandomXCoordinates(); var referencePoint = new Point2D(0, 0); // Call IEnumerable points = AdvancedMath2D.FromXToXY(xCoordinates, referencePoint, 0, 0); // Assert CollectionElementsAlmostEquals(xCoordinates.Select(x => new Point2D(0, x)), points); } [Test] public void FromXToXY_WithOffset_ReturnsCoordinatesNearerToOrigin() { // Setup double[] xCoordinates = ThreeRandomXCoordinates(); var referencePoint = new Point2D(0, 0); int offset = new Random(21).Next(); // Call IEnumerable points = AdvancedMath2D.FromXToXY(xCoordinates, referencePoint, offset, 0); // Assert CollectionElementsAlmostEquals(xCoordinates.Select(x => new Point2D(0, x - offset)), points); } [Test] public void FromXToXY_WithRotation180_ReturnsCoordinatesOnNegativeYAxis() { // Setup double[] xCoordinates = ThreeRandomXCoordinates(); var referencePoint = new Point2D(0, 0); // Call IEnumerable points = AdvancedMath2D.FromXToXY(xCoordinates, referencePoint, 0, 180); // Assert CollectionElementsAlmostEquals(xCoordinates.Select(x => new Point2D(0, -x)), points); } [Test] public void FromXToXY_WithReferencePoint_ReturnsCoordinatesFromReferencePoint() { // Setup var random = new Random(21); double[] xCoordinates = ThreeRandomXCoordinates(); var referencePoint = new Point2D(random.NextDouble(), random.NextDouble()); // Call IEnumerable points = AdvancedMath2D.FromXToXY(xCoordinates, referencePoint, 0, 0); // Assert CollectionElementsAlmostEquals(xCoordinates.Select(x => new Point2D(referencePoint.X, referencePoint.Y + x)), points); } [Test] public void FromXToXY_WithReferencePointOffsetAndRotation_ReturnsCoordinatesFromReferencePoint() { // Setup const double center = 5.0; double[] xCoordinates = { center - Math.Sqrt(8), center, center + Math.Sqrt(2) }; var referencePoint = new Point2D(3, 4); const double offset = 5; const double rotation = 45; // Call IEnumerable points = AdvancedMath2D.FromXToXY(xCoordinates, referencePoint, offset, rotation); // Assert CollectionElementsAlmostEquals(new[] { new Point2D(1, 2), new Point2D(3, 4), new Point2D(4, 5) }, points); } [Test] public void CompleteLineToPolygon_WithoutLine_ThrowsArgumentNullException() { // Call TestDelegate test = () => AdvancedMath2D.CompleteLineToPolygon(null, double.NaN).ToArray(); // Assert var exception = Assert.Throws(test); Assert.AreEqual("line", exception.ParamName); } [Test] public void CompleteLineToPolygon_LineWithLessThanTwoPoints_ThrowsArgumentNullException([Range(0, 1)] int pointCount) { // Setup IEnumerable points = Enumerable.Repeat(new Point2D(3, 2), pointCount); // Call TestDelegate test = () => AdvancedMath2D.CompleteLineToPolygon(points, double.NaN).ToArray(); // Assert const string message = "The line needs to have at least two points to be able to create a complete polygon."; var exception = TestHelper.AssertThrowsArgumentExceptionAndTestMessage(test, message); Assert.AreEqual("line", exception.ParamName); } [Test] [TestCase(3)] [TestCase(0)] [TestCase(-9)] [TestCase(double.NaN)] public void CompleteLineToPolygon_LineWithTwoPoints_TwoPointsAtBottomLevelAdded(double completingPointsLevel) { // Setup var random = new Random(21); int firstPointX = random.Next(); int lastPointX = random.Next(); var points = new[] { new Point2D(firstPointX, random.Next()), new Point2D(random.Next(), random.Next()), new Point2D(lastPointX, random.Next()) }; // Call IEnumerable pointsOfPolygon = AdvancedMath2D.CompleteLineToPolygon(points, completingPointsLevel); // Assert Assert.AreEqual(points, pointsOfPolygon.Take(3)); Assert.AreEqual(new Point2D(lastPointX, completingPointsLevel), pointsOfPolygon.ElementAt(3)); Assert.AreEqual(new Point2D(firstPointX, completingPointsLevel), pointsOfPolygon.ElementAt(4)); } private static double[] ThreeRandomXCoordinates() { var random = new Random(21); return new[] { random.NextDouble(), random.NextDouble(), random.NextDouble() }; } private static void CollectionElementsAlmostEquals(IEnumerable expected, IEnumerable actual) { Assert.AreEqual(expected.Count(), actual.Count()); for (var index = 0; index < expected.Count(); index++) { Point2D actualPoint = actual.ElementAt(index); Point2D expectedPoint = expected.ElementAt(index); const double delta = 1e-8; Assert.AreEqual(expectedPoint.X, actualPoint.X, delta); Assert.AreEqual(expectedPoint.Y, actualPoint.Y, delta); } } private static Point2D[] CreateBasePolygon() { return new[] { new Point2D(0, 0), new Point2D(0, 4), new Point2D(4, 4), new Point2D(4, 0) }; } } }