Index: DamEngine/trunk/src/Deltares.DamEngine.Data/Geometry/GeometryLoop.cs =================================================================== diff -u -r3079 -r3091 --- DamEngine/trunk/src/Deltares.DamEngine.Data/Geometry/GeometryLoop.cs (.../GeometryLoop.cs) (revision 3079) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/Geometry/GeometryLoop.cs (.../GeometryLoop.cs) (revision 3091) @@ -99,6 +99,11 @@ { return false; } + // Make sure points from curves are also exist as points + if (CalcPoints.Count >= 0) + { + SyncPoints(); + } var points = new List(); points.AddRange(Points); @@ -151,7 +156,7 @@ /// private List GetLocalPoint2DList() { - return CalcPoints;//Points.Select(loopPoint => new Point2D(loopPoint.X, loopPoint.Z)).ToList(); + return CalcPoints; } /// Index: DamEngine/trunk/src/Deltares.DamEngine.Data.Tests/Deltares.DamEngine.Data.Tests.csproj =================================================================== diff -u -r3079 -r3091 --- DamEngine/trunk/src/Deltares.DamEngine.Data.Tests/Deltares.DamEngine.Data.Tests.csproj (.../Deltares.DamEngine.Data.Tests.csproj) (revision 3079) +++ DamEngine/trunk/src/Deltares.DamEngine.Data.Tests/Deltares.DamEngine.Data.Tests.csproj (.../Deltares.DamEngine.Data.Tests.csproj) (revision 3091) @@ -52,6 +52,7 @@ + Index: DamEngine/trunk/src/Deltares.DamEngine.Data.Tests/General/GeometryLoopTests.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data.Tests/General/GeometryLoopTests.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data.Tests/General/GeometryLoopTests.cs (revision 3091) @@ -0,0 +1,628 @@ +// Copyright (C) Stichting Deltares 2019. 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; +using Deltares.DamEngine.Data.Geometry; +using NUnit.Framework; + +namespace Deltares.DamEngine.Data.Tests.General +{ + [TestFixture] + public class GeometryLoopTests + { + [Test] + public void DefaultConstructor_ExpectedValues() + { + // setup + + // call + var loop = new GeometryLoop(); + + // assert + Assert.AreEqual(loop.Points.Count, loop.Count); + Assert.AreEqual(String.Empty, loop.Name); + CollectionAssert.IsEmpty(loop.Points); + //Assert.AreEqual(String.Empty, loop.PointsDescription); + + Assert.IsEmpty(loop.CurveList); + } + + [Test] + public void HasArea_LoopCurvesDescribeLine_ReturnFalse() + { + // setup + var loop = new GeometryLoop(); + loop.CurveList.Add(new GeometryCurve(new Point2D(0.0, 0.0), new Point2D(1.0, 0.0))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[0].EndPoint, new Point2D(2.0, 0.0))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[1].EndPoint, loop.CurveList[0].HeadPoint)); + + // call + var hasArea = loop.HasArea(); + + // assert + Assert.IsFalse(hasArea); + } + + [Test] + public void HasArea_LoopDoesNotHaveAtLeast3DifferentPoints_ReturnFalse() + { + // setup + var loop = new GeometryLoop(); + loop.CurveList.Add(new GeometryCurve(new Point2D(1.1,3.3), new Point2D(4.4, 6.6))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[0].HeadPoint, loop.CurveList[0].EndPoint)); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[0].HeadPoint, loop.CurveList[0].EndPoint)); + loop.CurveList.Add(new GeometryCurve(new Point2D(1.1, 3.3), new Point2D(4.4, 6.6))); + + // call + var hasArea = loop.HasArea(); + + // assert + Assert.IsFalse(hasArea); + } + + [Test] + public void HasArea_LoopHasAtLeast3DifferentPoints_ReturnTrue() + { + // setup + var loop = new GeometryLoop(); + loop.CurveList.Add(new GeometryCurve(new Point2D(1.1, 3.3), new Point2D(4.4, 6.6))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[0].EndPoint, new Point2D(1.1, 9.9))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[1].EndPoint, loop.CurveList[0].HeadPoint)); + + // call + var hasArea = loop.HasArea(); + + // assert + Assert.IsTrue(hasArea); + } + + [Test] + public void HasArea_LoopOrderCurveHasNoInfluenseOnBeingTrue() + { + // setup H-E H-E H-E + var loop = new GeometryLoop(); + var c1 = new GeometryCurve(new Point2D(10, 10), new Point2D(20, 10)); + loop.CurveList.Add(c1); + var c2 = new GeometryCurve(loop.CurveList[0].EndPoint, new Point2D(15, 20)); + loop.CurveList.Add(c2); + var c3 = new GeometryCurve(loop.CurveList[1].EndPoint, loop.CurveList[0].HeadPoint); + loop.CurveList.Add(c3); + // assert + Assert.IsTrue(loop.HasArea()); + + // reverse curve 1: E-H H-E H-E + c1.Reverse(); + var loop1 = new GeometryLoop(); + loop1.CurveList.Add(c1); + loop1.CurveList.Add(c2); + loop1.CurveList.Add(c3); + // assert + Assert.IsTrue(loop1.HasArea()); + + // rereverse c1, reverse c2: H-E E-H H-E + c1.Reverse(); + c2.Reverse(); + var loop2 = new GeometryLoop(); + loop2.CurveList.Add(c1); + loop2.CurveList.Add(c2); + loop2.CurveList.Add(c3); + // assert + Assert.IsTrue(loop2.HasArea()); + + // rereverse c2, reverse c3: H-E H-E E-H + c2.Reverse(); + c3.Reverse(); + var loop3 = new GeometryLoop(); + loop3.CurveList.Add(c1); + loop3.CurveList.Add(c2); + loop3.CurveList.Add(c3); + // assert + Assert.IsTrue(loop3.HasArea()); + + // rereverse c3, reverse c1 and c2 : E-H E-H H-E + c3.Reverse(); + c2.Reverse(); + c1.Reverse(); + var loop4 = new GeometryLoop(); + loop4.CurveList.Add(c1); + loop4.CurveList.Add(c2); + loop4.CurveList.Add(c3); + // assert + Assert.IsTrue(loop4.HasArea()); + + // reverse c3 : E-H E-H E-H + c3.Reverse(); + var loop5 = new GeometryLoop(); + loop5.CurveList.Add(c1); + loop5.CurveList.Add(c2); + loop5.CurveList.Add(c3); + // assert + Assert.IsTrue(loop5.HasArea()); + + // reverse c2 : E-H H-E E-H + c2.Reverse(); + var loop6 = new GeometryLoop(); + loop6.CurveList.Add(c1); + loop6.CurveList.Add(c2); + loop6.CurveList.Add(c3); + // assert + Assert.IsTrue(loop6.HasArea()); + + // reverse 1, rereverse c2 : H-E E-H E-H + c1.Reverse(); + c2.Reverse(); + var loop7 = new GeometryLoop(); + loop7.CurveList.Add(c1); + loop7.CurveList.Add(c2); + loop7.CurveList.Add(c3); + // assert + Assert.IsTrue(loop7.HasArea()); + } + + [Test] + [TestCase(0)] + [TestCase(1)] + public void HasArea_LoopTooFewPoints_ReturnFalse(int count) + { + // setup + var loop = new GeometryLoop(); + for (int i = 0; i < count; i++) + { + loop.CurveList.Add(new GeometryCurve(new Point2D(1.1, 3.3), new Point2D(4.4, 6.6))); + } + + // call + var hasArea = loop.HasArea(); + + // assert + Assert.IsFalse(hasArea); + } + + [Test] + public void IsClockWise_CurvesFormClockwiseConcavePolygon_ReturnTrue() + { + // setup + var loop = new GeometryLoop(); + loop.CurveList.Add(new GeometryCurve(new Point2D(-1.0, 1.0), new Point2D(0.0, 0.0))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[0].EndPoint, new Point2D(1.0, 1.0))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[1].EndPoint, new Point2D(0.0, -1.0))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[2].EndPoint, loop.CurveList[0].HeadPoint)); + + // call + var isClockWise = loop.IsClockWise(); + + // assert + Assert.IsTrue(isClockWise); + } + + [Test] + [TestCase(3)] + [TestCase(50)] + public void IsClockWise_CurvesFormClockwiseConvexPolygon_ReturnTrue(int pointCount) + { + // setup + var loop = new GeometryLoop(); + var angleSegment = 2.0 * Math.PI / pointCount; + Point2D first = null, last = null; + for (int i = 0; i < pointCount - 1; i++) + { + var angleP0 = angleSegment * i; + var angleP1 = angleP0 + angleSegment; + + var p1 = new Point2D(Math.Sin(angleP1),Math.Cos(angleP1)); + if (i == 0) + { + first = new Point2D(Math.Sin(angleP0), Math.Cos(angleP0)); + + loop.CurveList.Add(new GeometryCurve(first, p1)); + } + else + { + loop.CurveList.Add(new GeometryCurve(last, p1)); + } + last = p1; + } + // TODO: Make GeometryLoop.Points less smart, such that one could simply do loop.Points.First()/.Last() :( + loop.CurveList.Add(new GeometryCurve(last, first)); + + // call + var isClockWise = loop.IsClockWise(); + + // assert + Assert.IsTrue(isClockWise); + } + + [Test] + public void IsClockWise_CurvesFormCounterClockwiseConcavePolygon_ReturnFalse() + { + // setup + var loop = new GeometryLoop(); + loop.CurveList.Add(new GeometryCurve(new Point2D(1.0, 1.0), new Point2D(0.0, 0.0))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[0].EndPoint, new Point2D(-1.0, 1.0))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[1].EndPoint, new Point2D(0.0, -1.0))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[2].EndPoint, loop.CurveList[0].HeadPoint)); + + // call + var isClockWise = loop.IsClockWise(); + + // assert + Assert.IsFalse(isClockWise); + } + + [Test] + [TestCase(3)] + [TestCase(50)] + public void IsClockWise_CurvesFormCounterClockwiseConvexPolygon_ReturnFalse(int pointCount) + { + // setup + var loop = new GeometryLoop(); + var angleSegment = -2.0 * Math.PI / pointCount; + Point2D first = null, last = null; + for (int i = 0; i < pointCount - 1; i++) + { + var angleP0 = angleSegment * i; + var angleP1 = angleP0 + angleSegment; + + var p1 = new Point2D(Math.Sin(angleP1), Math.Cos(angleP1)); + if (i == 0) + { + first = new Point2D(Math.Sin(angleP0), Math.Cos(angleP0)); + loop.CurveList.Add(new GeometryCurve(first, p1)); + } + else + { + loop.CurveList.Add(new GeometryCurve(last, p1)); + } + last = p1; + } + // TODO: Make GeometryLoop.Points less smart, such that one could simply do loop.Points.First()/.Last() :( + loop.CurveList.Add(new GeometryCurve(last, first)); + + // call + var isClockWise = loop.IsClockWise(); + + // assert + Assert.IsFalse(isClockWise); + } + + [Test] + public void IsClockWise_CurvesFormStraightLine_ThrowInvalidOperationException() + { + // setup + var loop = new GeometryLoop(); + loop.CurveList.Add(new GeometryCurve(new Point2D(0.0, 0.0), new Point2D(1.0, 0.0))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[0].EndPoint, new Point2D(2.0, 0.0))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[1].EndPoint, loop.CurveList[0].HeadPoint)); + + // call + TestDelegate call = () => loop.IsClockWise(); + + // assert + Assert.Throws(call); + } + + [Test] + [TestCase(0)] + [TestCase(1)] + [TestCase(5)] + public void IsClockWise_TooFewUniquePoints_ThrowNotEnoughUniquePointsException(int curveCount) + { + // setup + var loop = new GeometryLoop(); + Point2D lastPoint = null; + for (int i = 0; i < curveCount; i++) + { + var newPoint = new Point2D(); + if (i == 0) + { + loop.CurveList.Add(new GeometryCurve(new Point2D(), newPoint)); + } + else + { + loop.CurveList.Add(new GeometryCurve(lastPoint, newPoint)); + } + lastPoint = newPoint; + } + + var pointCount = loop.CalcPoints.Count; + Assert.AreEqual(curveCount == 0 ? 0 : curveCount + 1, pointCount); + + // call + TestDelegate call = () => loop.IsClockWise(); + + // assert + Assert.Throws(call); + } + + [Test] + public void IsPointInLoop_PointIsInsideLoop_ReturnTrue() + { + // setup + var loop = new GeometryLoop(); + loop.CurveList.Add(new GeometryCurve(new Point2D(0.0, 0.0), new Point2D(10.0, 0.0))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[0].EndPoint, new Point2D(10.0, 10.0))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[1].EndPoint, new Point2D(0.0, 10.0))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[2].EndPoint, loop.CurveList[0].HeadPoint)); + + var point = new Point2D(1.2, 3.4); + + // call + var isContained = loop.IsPointInLoopArea(point); + + // assert + Assert.IsTrue(isContained); + } + + [Test] + [TestCase(5.0, 10.0)] + [TestCase(5.0, 0.0)] + [TestCase(0.0, 5.0)] + [TestCase(10.0, 5.0)] + public void IsPointInLoop_PointIsOnLoopEdge_ReturnTrue(double x, double z) + { + // setup + var loop = new GeometryLoop(); + loop.CurveList.Add(new GeometryCurve(new Point2D(0.0, 0.0), new Point2D(10.0, 0.0))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[0].EndPoint, new Point2D(10.0, 10.0))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[1].EndPoint, new Point2D(0.0, 10.0))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[2].EndPoint, loop.CurveList[0].HeadPoint)); + + var point = new Point2D(x, z); + + // call + var isContained = loop.IsPointInLoopArea(point); + + // assert + Assert.IsTrue(isContained); + } + + [Test] + [TestCase(0.0 - 1e-6, 5.0)] + [TestCase(10.0 + 1e-6, 5.0)] + [TestCase(5.0, 0.0 - 1e-6)] + [TestCase(5.0, 10.0 + 1e-6)] + public void IsPointInLoop_PointIsOutsideLoop_ReturnFalse(double x, double z) + { + // setup + var loop = new GeometryLoop(); + loop.CurveList.Add(new GeometryCurve(new Point2D(0.0, 0.0), new Point2D(10.0, 0.0))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[0].EndPoint, new Point2D(10.0, 10.0))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[1].EndPoint, new Point2D(0.0, 10.0))); + loop.CurveList.Add(new GeometryCurve(loop.CurveList[2].EndPoint, loop.CurveList[0].HeadPoint)); + + var point = new Point2D(x, z); + + // call + var isContained = loop.IsPointInLoopArea(point); + + // assert + Assert.IsFalse(isContained); + } + + [Test] + public void Points_OnGetWithMultipleCurves_NotSharingAnyInstances_ReturnOnlyFirstCurveHeadAndEndPoints() + { + // setup + var curve1 = new GeometryCurve(new Point2D(1.1, 3.3), new Point2D(4.4, 6.6)); + var curve2 = new GeometryCurve(new Point2D(7.7, 9.9), new Point2D(4.4, 6.6)); + var curve3 = new GeometryCurve(new Point2D(10.10, 12.12), new Point2D(7.7, 9.9)); + var curve4 = new GeometryCurve(new Point2D(13.13, 15.15), new Point2D(10.10, 12.12)); + var curve5 = new GeometryCurve(new Point2D(16.16, 18.18), new Point2D(16.16, 18.18)); + var curve6 = new GeometryCurve(new Point2D(19.19, 21.21), new Point2D(19.19, 21.21)); + var loop = new GeometryLoop(); + loop.CurveList.AddRange(new[] + { + curve1, curve2, curve3, curve4, curve5, curve6 + }); + + // call + var points = loop.CalcPoints; + + // assert + Assert.AreEqual(2, points.Count); + Assert.AreSame(curve1.EndPoint, points[0]); + Assert.AreSame(curve1.HeadPoint, points[1]); + } + + [Test] + public void Points_OnGetWithMultipleCurves_SharingInstancesBetweenCurvePointsFormingLoop_ReturnOnlyConnectedCurveHeadAndEndPoints() + { + // setup + var curve1 = new GeometryCurve(new Point2D(1.1, 3.3), new Point2D(4.4, 6.6)); + var curve2 = new GeometryCurve(new Point2D(7.7, 9.9), curve1.EndPoint); + var curve3 = new GeometryCurve(curve2.HeadPoint, new Point2D(10.10, 12.12)); + var curve4 = new GeometryCurve(new Point2D(13.13, 15.15), curve3.EndPoint); + var curve5 = new GeometryCurve(curve4.HeadPoint, new Point2D(16.16, 18.18)); + var curve6 = new GeometryCurve(curve5.EndPoint, new Point2D(19.19, 21.21)); + var curve7 = new GeometryCurve(curve1.HeadPoint, curve6.EndPoint); + var loop = new GeometryLoop(); + loop.CurveList.AddRange(new[] + { + curve1, curve2, curve3, curve4, curve5, curve6, curve7 + }); + + // call + var points = loop.CalcPoints; + + // assert + Assert.AreEqual(7, points.Count); + Assert.AreSame(curve1.HeadPoint, points[0]); + Assert.AreSame(curve1.EndPoint, points[1]); + Assert.AreSame(curve2.HeadPoint, points[2]); + Assert.AreSame(curve3.EndPoint, points[3]); + Assert.AreSame(curve4.HeadPoint, points[4]); + Assert.AreSame(curve5.EndPoint, points[5]); + Assert.AreSame(curve6.EndPoint, points[6]); + } + + [Test] + public void Points_OnGetWithMultipleCurves_SharingInstancesBetweenCurvePoints_ReturnOnlyConnectedCurveHeadAndEndPoints() + { + // setup + var curve1 = new GeometryCurve(new Point2D(1.1, 3.3), new Point2D(4.4, 6.6)); + var curve2 = new GeometryCurve(new Point2D(7.7, 9.9), curve1.EndPoint); + var curve3 = new GeometryCurve(curve2.HeadPoint, new Point2D(10.10, 12.12)); + var curve4 = new GeometryCurve(new Point2D(13.13, 15.15), curve3.EndPoint); + var curve5 = new GeometryCurve(curve4.HeadPoint, new Point2D(16.16, 18.18)); + var curve6 = new GeometryCurve(curve5.EndPoint, new Point2D(19.19, 21.21)); + var curve7 = new GeometryCurve(new Point2D(22.22, 24.24), new Point2D(25.25, 27.27)); + var curve8 = new GeometryCurve(new Point2D(28.28, 30.30), new Point2D(31.31, 33.33)); + var curve9 = new GeometryCurve(new Point2D(34.34, 36.36), curve6.EndPoint); + var loop = new GeometryLoop(); + loop.CurveList.AddRange(new[] + { + curve1, curve2, curve3, curve4, curve5, curve6, curve7, curve8, curve9 + }); + + // call + var points = loop.CalcPoints; + + // assert + Assert.AreEqual(8, points.Count); + Assert.AreSame(curve1.HeadPoint, points[0]); + Assert.AreSame(curve1.EndPoint, points[1]); + Assert.AreSame(curve2.HeadPoint, points[2]); + Assert.AreSame(curve3.EndPoint, points[3]); + Assert.AreSame(curve4.HeadPoint, points[4]); + Assert.AreSame(curve5.EndPoint, points[5]); + Assert.AreSame(curve6.EndPoint, points[6]); + Assert.AreSame(curve9.HeadPoint, points[7]); + } + + [Test] + public void Points_OnGetWithMultipleCurves_SharingInstancesEndPointAndEndPoint_ReturnUniqueHeadAndEndPoints() + { + // setup + var curve1 = new GeometryCurve(new Point2D(1.1, 3.3), new Point2D(4.4, 6.6)); + var curve2 = new GeometryCurve(new Point2D(7.7, 9.9), curve1.EndPoint); + var loop = new GeometryLoop(); + loop.CurveList.AddRange(new[] + { + curve1, curve2 + }); + + // call + var points = loop.CalcPoints; + + // assert + Assert.AreEqual(3, points.Count); + Assert.AreSame(curve1.HeadPoint, points[0]); + Assert.AreSame(curve1.EndPoint, points[1]); + Assert.AreSame(curve2.HeadPoint, points[2]); + } + + [Test] + public void Points_OnGetWithMultipleCurves_SharingInstancesEndPointAndHeadPoint_ReturnUniqueHeadAndEndPoints() + { + // setup + var curve1 = new GeometryCurve(new Point2D(1.1, 3.3), new Point2D(4.4, 6.6)); + var curve2 = new GeometryCurve(curve1.EndPoint, new Point2D(7.7, 9.9)); + var loop = new GeometryLoop(); + loop.CurveList.AddRange(new[] + { + curve1, curve2 + }); + + // call + var points = loop.CalcPoints; + + // assert + Assert.AreEqual(3, points.Count); + Assert.AreSame(curve1.HeadPoint, points[0]); + Assert.AreSame(curve1.EndPoint, points[1]); + Assert.AreSame(curve2.EndPoint, points[2]); + } + + [Test] + public void Points_OnGetWithMultipleCurves_SharingInstancesHeadPointAndEndPoint_ReturnUniqueHeadAndEndPoints() + { + // setup + var curve1 = new GeometryCurve(new Point2D(1.1, 3.3), new Point2D(4.4, 6.6)); + var curve2 = new GeometryCurve(new Point2D(7.7, 9.9), curve1.HeadPoint); + var loop = new GeometryLoop(); + loop.CurveList.AddRange(new[] + { + curve1, curve2 + }); + + // call + var points = loop.CalcPoints; + + // assert + Assert.AreEqual(3, points.Count); + Assert.AreSame(curve1.EndPoint, points[0]); + Assert.AreSame(curve1.HeadPoint, points[1]); + Assert.AreSame(curve2.HeadPoint, points[2]); + } + + [Test] + public void Points_OnGetWithMultipleCurves_SharingInstancesHeadPointAndHeadPoint_ReturnUniqueHeadAndEndPoints() + { + // setup + var curve1 = new GeometryCurve(new Point2D(1.1, 3.3), new Point2D(4.4, 6.6)); + var curve2 = new GeometryCurve(curve1.HeadPoint, new Point2D(7.7, 9.9)); + var loop = new GeometryLoop(); + loop.CurveList.AddRange(new[] + { + curve1, curve2 + }); + + // call + var points = loop.CalcPoints; + + // assert + Assert.AreEqual(3, points.Count); + Assert.AreSame(curve1.EndPoint, points[0]); + Assert.AreSame(curve1.HeadPoint, points[1]); + Assert.AreSame(curve2.EndPoint, points[2]); + } + + [Test] + public void Points_OnGetWithOneCurve_ReturnThatCurveHeadAndEndPoints() + { + // setup + var curve = new GeometryCurve(new Point2D(1.1, 3.3), new Point2D(4.4, 6.6)); + var loop = new GeometryLoop(); + loop.CurveList.Add(curve); + + // call + var points = loop.CalcPoints; + + // assert + Assert.AreEqual(2, points.Count); + Assert.AreSame(curve.HeadPoint, points[0]); + Assert.AreSame(curve.EndPoint, points[1]); + } + + [Test] + public void Points_OnGetWithoutCurves_ReturnEmptyCollection() + { + // setup + var loop = new GeometryLoop(); + + // call + var points = loop.Points; + + // assert + Assert.IsEmpty(points); + } + } +} Index: DamEngine/trunk/src/Deltares.DamEngine.Data/Geometry/Routines2D.cs =================================================================== diff -u -r3079 -r3091 --- DamEngine/trunk/src/Deltares.DamEngine.Data/Geometry/Routines2D.cs (.../Routines2D.cs) (revision 3079) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/Geometry/Routines2D.cs (.../Routines2D.cs) (revision 3091) @@ -227,6 +227,25 @@ { return Clockwise.NotEnoughUniquePoints; } + var p0 = distinctPoints.First(); + Point2D[] realDistinctPoint = new Point2D[distinctPoints.Length]; + realDistinctPoint[0] = p0; + var index = 1; + foreach (var distinctPoint in distinctPoints) + { + if (distinctPoint != p0) + { + if (!distinctPoint.LocationEquals(p0)) + { + realDistinctPoint[index] = distinctPoint; + index++; + } + } + } + if (index < 3) + { + return Clockwise.NotEnoughUniquePoints; + } double sumClockwise = 0; double clockwise;