Index: Core/Common/src/Core.Common.Base/Core.Common.Base.csproj =================================================================== diff -u -r07f3d67fe9512b3c8303ff09398b0a234900d546 -r2fd427d95bfd8c000da089fedcf7b2fe2ca592e2 --- Core/Common/src/Core.Common.Base/Core.Common.Base.csproj (.../Core.Common.Base.csproj) (revision 07f3d67fe9512b3c8303ff09398b0a234900d546) +++ Core/Common/src/Core.Common.Base/Core.Common.Base.csproj (.../Core.Common.Base.csproj) (revision 2fd427d95bfd8c000da089fedcf7b2fe2ca592e2) @@ -102,6 +102,7 @@ + Index: Core/Common/src/Core.Common.Base/Geometry/Segment2DCollectionExtensions.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Base/Geometry/Segment2DCollectionExtensions.cs (revision 0) +++ Core/Common/src/Core.Common.Base/Geometry/Segment2DCollectionExtensions.cs (revision 2fd427d95bfd8c000da089fedcf7b2fe2ca592e2) @@ -0,0 +1,75 @@ +// Copyright (C) Stichting Deltares 2017. All rights reserved. +// +// This file is part of Ringtoets. +// +// Ringtoets 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; + +namespace Core.Common.Base.Geometry +{ + /// + /// Extension methods for collections of . + /// + public static class Segment2DCollectionExtensions + { + /// + /// Interpolates the segments on . + /// + /// The segments to interpolate on. + /// The x value to use for interpolation. + /// The interpolated y value of the segment that is closest to + /// or when no interpolation was found. + /// Thrown when is null. + public static double Interpolate(this IEnumerable segments, double x) + { + if (segments == null) + { + throw new ArgumentNullException(nameof(segments)); + } + + Segment2D segment = segments.LastOrDefault(s => s.FirstPoint.X <= x) ?? segments.First(); + + return Interpolate(segment, x); + } + + /// + /// Interpolates the segment on . + /// + /// The segment to interpolate on. + /// The x value to use for interpolation. + /// The interpolated y value of the or + /// when no interpolation was found. + private static double Interpolate(Segment2D segment, double x) + { + double differenceInX = segment.SecondPoint.X - segment.FirstPoint.X; + if (Math.Abs(differenceInX) < 1e-6) + { + return double.NaN; + } + + double m = (segment.SecondPoint.Y - segment.FirstPoint.Y) / differenceInX; + + double b = segment.FirstPoint.Y - (m * segment.FirstPoint.X); + + return m * x + b; + } + } +} \ No newline at end of file Index: Core/Common/test/Core.Common.Base.Test/Core.Common.Base.Test.csproj =================================================================== diff -u -r354316fca628a0f9dcdee5ff156ae4d87e662677 -r2fd427d95bfd8c000da089fedcf7b2fe2ca592e2 --- Core/Common/test/Core.Common.Base.Test/Core.Common.Base.Test.csproj (.../Core.Common.Base.Test.csproj) (revision 354316fca628a0f9dcdee5ff156ae4d87e662677) +++ Core/Common/test/Core.Common.Base.Test/Core.Common.Base.Test.csproj (.../Core.Common.Base.Test.csproj) (revision 2fd427d95bfd8c000da089fedcf7b2fe2ca592e2) @@ -93,6 +93,7 @@ + Index: Core/Common/test/Core.Common.Base.Test/Geometry/Segment2DCollectionExtensionsTest.cs =================================================================== diff -u --- Core/Common/test/Core.Common.Base.Test/Geometry/Segment2DCollectionExtensionsTest.cs (revision 0) +++ Core/Common/test/Core.Common.Base.Test/Geometry/Segment2DCollectionExtensionsTest.cs (revision 2fd427d95bfd8c000da089fedcf7b2fe2ca592e2) @@ -0,0 +1,147 @@ +// Copyright (C) Stichting Deltares 2017. All rights reserved. +// +// This file is part of Ringtoets. +// +// Ringtoets 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 Core.Common.Base.Geometry; +using NUnit.Framework; + +namespace Core.Common.Base.Test.Geometry +{ + [TestFixture] + public class Segment2DCollectionExtensionsTest + { + [Test] + public void Interpolate_SegmentsNull_ThrowsArgumentNullException() + { + // Call + TestDelegate test = () => ((IEnumerable) null).Interpolate(0); + + // Assert + var exception = Assert.Throws(test); + Assert.AreEqual("segments", exception.ParamName); + } + + [Test] + [TestCaseSource(nameof(ValidInterpolation))] + public void Interpolate_ValidArguments_ReturnsExpectedResult(IEnumerable segments, + double interpolateOnX, + double expectedY) + { + // Call + double actualY = segments.Interpolate(interpolateOnX); + + // Assert + Assert.AreEqual(expectedY, actualY); + } + + [Test] + [TestCaseSource(nameof(InvalidInterpolation))] + public void Interpolate_InvalidArguments_ReturnsNaN(IEnumerable segments, + double interpolateOnX) + { + // Call + double actualY = segments.Interpolate(interpolateOnX); + + // Assert + Assert.IsNaN(actualY); + } + + private static IEnumerable ValidInterpolation() + { + yield return new TestCaseData(new[] + { + new Segment2D(new Point2D(-1000, 0), new Point2D(1, 1)), + new Segment2D(new Point2D(1, 1), new Point2D(10, 10)), + new Segment2D(new Point2D(100, 100), new Point2D(1000, 10)) + }, 20, 20).SetName("Slope = 1, b = 0 with other segments"); + + yield return new TestCaseData(new[] + { + new Segment2D(new Point2D(0, 10), new Point2D(10, 100)) + }, 0, 10).SetName("Same as first point that passed through origin"); + + yield return new TestCaseData(new[] + { + new Segment2D(new Point2D(0, 10), new Point2D(10, 100)) + }, -10, -80).SetName("Left of first point"); + + yield return new TestCaseData(new[] + { + new Segment2D(new Point2D(1, 10), new Point2D(10, 100)) + }, 1, 10).SetName("Same as first point"); + + yield return new TestCaseData(new[] + { + new Segment2D(new Point2D(1, 10), new Point2D(10, 100)) + }, 10, 100).SetName("Same as second point"); + + yield return new TestCaseData(new[] + { + new Segment2D(new Point2D(0, 10), new Point2D(10, 10)), + new Segment2D(new Point2D(10, 10), new Point2D(20, 100)) + }, 30, 190).SetName("Right of last point"); + + yield return new TestCaseData(new[] + { + new Segment2D(new Point2D(1, 1), new Point2D(10, 10)) + }, 5, 5).SetName("Slope = 1, b = 0"); + + yield return new TestCaseData(new[] + { + new Segment2D(new Point2D(0, 0), new Point2D(10, 1000)) + }, 5, 500).SetName("Slope = 100, b = 0"); + + yield return new TestCaseData(new[] + { + new Segment2D(new Point2D(0, 0), new Point2D(-5, -9)) + }, 5, (9.0 / 5.0) * 5).SetName("Slope = 9/5, b = 0"); + + yield return new TestCaseData(new[] + { + new Segment2D(new Point2D(5, 2), new Point2D(90, 3)) + }, 50, ((1 / 85.0) * 50) + (33 / 17.0)).SetName("Slope = 1/85, b = 33/17"); + } + + private static IEnumerable InvalidInterpolation() + { + yield return new TestCaseData(new[] + { + new Segment2D(new Point2D(0, 10), new Point2D(0, 10)) + }, 0).SetName("Vertical line"); + + yield return new TestCaseData(new[] + { + new Segment2D(new Point2D(0, 0), new Point2D(double.PositiveInfinity, double.PositiveInfinity)) + }, 20).SetName("PositiveInfinity"); + + yield return new TestCaseData(new[] + { + new Segment2D(new Point2D(0, 0), new Point2D(double.NegativeInfinity, double.NegativeInfinity)) + }, 20).SetName("NegativeInfinity"); + + yield return new TestCaseData(new[] + { + new Segment2D(new Point2D(0, 0), new Point2D(double.NaN, double.NaN)) + }, 20).SetName("NaN"); + } + } +} \ No newline at end of file