From e46064098de61a2b34a13b7f97bff34de2fdc954 Mon Sep 17 00:00:00 2001 From: RStolkerDeltares Date: Thu, 12 Feb 2026 17:45:17 +0100 Subject: [PATCH] GRIDEDIT-2158: Added API for calculating spline intersections --- .../Api/DisposableSplineIntersections.cs | 91 +++++++++ src/MeshKernelNET/Api/IMeshKernelApi.cs | 25 +++ src/MeshKernelNET/Api/MeshKernelApi.cs | 40 ++++ src/MeshKernelNET/Native/MeshKernelDll.cs | 36 ++++ .../Native/SplineIntersectionsNative.cs | 37 ++++ .../Api/DisposableGriddedSamplesTest.cs | 28 +++ .../Api/DisposableSplineIntersectionsTest.cs | 184 ++++++++++++++++++ .../Api/MeshKernelApiTest.cs | 175 ++++++++++++++++- 8 files changed, 611 insertions(+), 5 deletions(-) create mode 100644 src/MeshKernelNET/Api/DisposableSplineIntersections.cs create mode 100644 src/MeshKernelNET/Native/SplineIntersectionsNative.cs create mode 100644 test/MeshKernelNETTest/Api/DisposableSplineIntersectionsTest.cs diff --git a/src/MeshKernelNET/Api/DisposableSplineIntersections.cs b/src/MeshKernelNET/Api/DisposableSplineIntersections.cs new file mode 100644 index 0000000..883a4e5 --- /dev/null +++ b/src/MeshKernelNET/Api/DisposableSplineIntersections.cs @@ -0,0 +1,91 @@ +using MeshKernelNET.Helpers; +using MeshKernelNET.Native; +using ProtoBuf; + +namespace MeshKernelNET.Api +{ + [ProtoContract(AsReferenceDefault = true)] + public sealed class DisposableSplineIntersections : DisposableNativeObject + { + [ProtoMember(1)] + private int numIntersections; + + [ProtoMember(2)] + private int[] splineIndex; + + [ProtoMember(3)] + private double[] intersectionAngle; + + [ProtoMember(4)] + private double[] intersectionX; + + [ProtoMember(5)] + private double[] intersectionY; + + public DisposableSplineIntersections() + { + } + + public DisposableSplineIntersections(int numIntersections) + { + this.numIntersections = numIntersections; + splineIndex = new int[numIntersections]; + intersectionAngle = new double[numIntersections]; + intersectionX = new double[numIntersections]; + intersectionY = new double[numIntersections]; + } + + ~DisposableSplineIntersections() + { + Dispose(false); + } + + public int NumIntersections + { + get => numIntersections; + set => numIntersections = value; + } + + public int[] SplineIndex + { + get => splineIndex; + set => splineIndex = value; + } + + public double[] IntersectionAngle + { + get => intersectionAngle; + set => intersectionAngle = value; + } + + public double[] IntersectionX + { + get => intersectionX; + set => intersectionX = value; + } + + public double[] IntersectionY + { + get => intersectionY; + set => intersectionY = value; + } + + protected override void SetNativeObject(ref SplineIntersectionsNative nativeObject) + { + nativeObject.NumIntersections = numIntersections; + nativeObject.SplineIndex = GetPinnedObjectPointer(splineIndex); + nativeObject.IntersectionAngle = GetPinnedObjectPointer(intersectionAngle); + nativeObject.IntersectionX = GetPinnedObjectPointer(intersectionX); + nativeObject.IntersectionY = GetPinnedObjectPointer(intersectionY); + } + + public void UpdateFromNativeObject(ref SplineIntersectionsNative nativeObject) + { + numIntersections = nativeObject.NumIntersections; + splineIndex = nativeObject.SplineIndex.CreateValueArray(nativeObject.NumIntersections); + intersectionAngle = nativeObject.IntersectionAngle.CreateValueArray(nativeObject.NumIntersections); + intersectionX = nativeObject.IntersectionX.CreateValueArray(nativeObject.NumIntersections); + intersectionY = nativeObject.IntersectionY.CreateValueArray(nativeObject.NumIntersections); + } + } +} \ No newline at end of file diff --git a/src/MeshKernelNET/Api/IMeshKernelApi.cs b/src/MeshKernelNET/Api/IMeshKernelApi.cs index 8ecf5cb..bb47065 100644 --- a/src/MeshKernelNET/Api/IMeshKernelApi.cs +++ b/src/MeshKernelNET/Api/IMeshKernelApi.cs @@ -1823,6 +1823,31 @@ int SplinesToLandBoundary(int meshKernelId, ref DisposableGeometryList splines, int firstIndex, int secondIndex); + + /// + /// Initializes spline intersection checking with a set of cached splines for subsequent intersection checks. + /// Must be called before . + /// + /// The id of the mesh state + /// The set of splines to cache for intersection checking + /// Error code + int SplineIntersectionsInitialize(int meshKernelId, DisposableGeometryList crossSplines); + + /// + /// Checks the given spline for intersections with the previously cached splines. + /// + /// The id of the mesh state + /// The spline to check for intersections + /// The intersection data containing all found intersections + /// Error code + int GetSplineIntersections(int meshKernelId, DisposableGeometryList spline, out DisposableSplineIntersections intersections); + + /// + /// Finalizes and cleans up the spline intersection checking. + /// + /// The id of the mesh state + /// Error code + int SplineIntersectionsFinalize(int meshKernelId); /// /// Redo editing action diff --git a/src/MeshKernelNET/Api/MeshKernelApi.cs b/src/MeshKernelNET/Api/MeshKernelApi.cs index d531f1e..22e1e8f 100644 --- a/src/MeshKernelNET/Api/MeshKernelApi.cs +++ b/src/MeshKernelNET/Api/MeshKernelApi.cs @@ -1718,6 +1718,46 @@ public int SplinesToLandBoundary(int meshKernelId, GeometryListNative polygonNative = splines.CreateNativeObject(); return MeshKernelDll.SplinesToLandBoundary(meshKernelId, ref landboundariesNative, ref polygonNative, firstIndex, secondIndex); } + + /// + public int SplineIntersectionsInitialize(int meshKernelId, DisposableGeometryList crossSplines) + { + GeometryListNative crossSplinesNative = crossSplines.CreateNativeObject(); + return MeshKernelDll.InitializeSplineIntersection(meshKernelId, ref crossSplinesNative); + } + + /// + public int GetSplineIntersections(int meshKernelId, DisposableGeometryList spline, out DisposableSplineIntersections intersections) + { + intersections = new DisposableSplineIntersections(); + GeometryListNative splineNative = spline.CreateNativeObject(); + + var numberOfIntersections = 0; + int exitCode = MeshKernelDll.CheckSplineIntersection(meshKernelId, ref splineNative, ref numberOfIntersections); + if (exitCode != 0) + { + return exitCode; + } + + using (var exchangeIntersections = new DisposableSplineIntersections(numberOfIntersections)) + { + SplineIntersectionsNative intersectionsNative = exchangeIntersections.CreateNativeObject(); + exitCode = MeshKernelDll.GetSplineIntersectionData(meshKernelId, ref intersectionsNative); + if (exitCode != 0) + { + return exitCode; + } + + intersections.UpdateFromNativeObject(ref intersectionsNative); + return exitCode; + } + } + + /// + public int SplineIntersectionsFinalize(int meshKernelId) + { + return MeshKernelDll.FinalizeSplineIntersection(meshKernelId); + } /// public int RedoState(ref bool redone, ref int meshKernelId) diff --git a/src/MeshKernelNET/Native/MeshKernelDll.cs b/src/MeshKernelNET/Native/MeshKernelDll.cs index cc27f80..3e7783d 100644 --- a/src/MeshKernelNET/Native/MeshKernelDll.cs +++ b/src/MeshKernelNET/Native/MeshKernelDll.cs @@ -2094,6 +2094,42 @@ internal static extern int SplinesToLandBoundary([In] int meshKernelId, [In] int startSplineIndex, [In] int endSplineIndex); + /// + /// Initialize the spline intersection calculation + /// + /// The id of the mesh state + /// A set of splines to check against + /// Error code + [DllImport(MeshKernelDllName, EntryPoint = "mkernel_initialise_spline_intersection", CallingConvention = CallingConvention.Cdecl)] + internal static extern int InitializeSplineIntersection([In] int meshKernelId, [In] ref GeometryListNative crossSplines); + + /// + /// Compute the intersections of a spline with the cached splines and return the number of intersections found + /// + /// The id of the mesh state + /// A single spline to check for intersections + /// The number of spline intersections computed + /// Error code + [DllImport(MeshKernelDllName, EntryPoint = "mkernel_check_spline_intersection", CallingConvention = CallingConvention.Cdecl)] + internal static extern int CheckSplineIntersection([In] int meshKernelId, [In] ref GeometryListNative spline, [In][Out] ref int numberOfIntersections); + + /// + /// Get the spline intersection data + /// + /// The id of the mesh state + /// The spline intersection data + /// Error code + [DllImport(MeshKernelDllName, EntryPoint = "mkernel_get_spline_intersection_data", CallingConvention = CallingConvention.Cdecl)] + internal static extern int GetSplineIntersectionData([In] int meshKernelId, [In][Out] ref SplineIntersectionsNative intersectionData); + + /// + /// Finalize the spline intersection calculation + /// + /// The id of the mesh state + /// Error code + [DllImport(MeshKernelDllName, EntryPoint = "mkernel_finalise_spline_intersection", CallingConvention = CallingConvention.Cdecl)] + internal static extern int FinalizeSplineIntersection([In] int meshKernelId); + /// /// Redo editing action /// diff --git a/src/MeshKernelNET/Native/SplineIntersectionsNative.cs b/src/MeshKernelNET/Native/SplineIntersectionsNative.cs new file mode 100644 index 0000000..5625266 --- /dev/null +++ b/src/MeshKernelNET/Native/SplineIntersectionsNative.cs @@ -0,0 +1,37 @@ +using System; +using System.Runtime.InteropServices; + +namespace MeshKernelNET.Native +{ + /// + /// A struct used to describe the intersection points of a spline with a number of other splines. + /// + [StructLayout(LayoutKind.Sequential)] + public struct SplineIntersectionsNative + { + /// + /// The number of spline intersections + /// + public int NumIntersections { get; set; } + + /// + /// The index of the intersected spline + /// + public IntPtr SplineIndex { get; set; } + + /// + /// The angle of the intersection + /// + public IntPtr IntersectionAngle { get; set; } + + /// + /// The x coordinate of the intersection point + /// + public IntPtr IntersectionX { get; set; } + + /// + /// The y coordinate of the intersection point + /// + public IntPtr IntersectionY { get; set; } + } +} \ No newline at end of file diff --git a/test/MeshKernelNETTest/Api/DisposableGriddedSamplesTest.cs b/test/MeshKernelNETTest/Api/DisposableGriddedSamplesTest.cs index 10e7c98..eea182d 100644 --- a/test/MeshKernelNETTest/Api/DisposableGriddedSamplesTest.cs +++ b/test/MeshKernelNETTest/Api/DisposableGriddedSamplesTest.cs @@ -1,5 +1,6 @@ using System; using MeshKernelNET.Api; +using MeshKernelNET.Native; using NUnit.Framework; namespace MeshKernelNETTest.Api @@ -282,5 +283,32 @@ public void Constructor_WithNegativeCellSize_WorksCorrectly() Assert.That(samples.CellSize, Is.EqualTo(-1.5)); } + + [Test] + public void CreateNativeObject_CreatesValidNativeStructure() + { + var samples = new DisposableGriddedSamples(3, 4, 1.0, 2.0, 0.5, InterpolationType.Float) + { + FloatValues = new[] { 1.1f, 2.2f, 3.3f, 4.4f, 5.5f, 6.6f, 7.7f, 8.8f, 9.9f, 10.1f, 11.1f, 12.2f } + }; + + using (samples) + { + GriddedSamplesNative nativeSamples = samples.CreateNativeObject(); + + using (Assert.EnterMultipleScope()) + { + Assert.That(nativeSamples.num_x, Is.EqualTo(3)); + Assert.That(nativeSamples.num_y, Is.EqualTo(4)); + Assert.That(nativeSamples.origin_x, Is.EqualTo(1.0)); + Assert.That(nativeSamples.origin_y, Is.EqualTo(2.0)); + Assert.That(nativeSamples.cell_size, Is.EqualTo(0.5)); + Assert.That(nativeSamples.coordinates_x, Is.Not.EqualTo(IntPtr.Zero)); + Assert.That(nativeSamples.coordinates_y, Is.Not.EqualTo(IntPtr.Zero)); + Assert.That(nativeSamples.values, Is.Not.EqualTo(IntPtr.Zero)); + Assert.That(nativeSamples.value_type, Is.EqualTo((int)InterpolationType.Float)); + } + } + } } } \ No newline at end of file diff --git a/test/MeshKernelNETTest/Api/DisposableSplineIntersectionsTest.cs b/test/MeshKernelNETTest/Api/DisposableSplineIntersectionsTest.cs new file mode 100644 index 0000000..57b3b23 --- /dev/null +++ b/test/MeshKernelNETTest/Api/DisposableSplineIntersectionsTest.cs @@ -0,0 +1,184 @@ +using System; +using MeshKernelNET.Api; +using MeshKernelNET.Native; +using NUnit.Framework; + +namespace MeshKernelNETTest.Api +{ + [TestFixture] + public class DisposableSplineIntersectionsTests + { + [Test] + public void Constructor_WithValidParameters_CreatesInstance() + { + var intersections = new DisposableSplineIntersections(5); + + using (Assert.EnterMultipleScope()) + { + Assert.That(intersections.NumIntersections, Is.EqualTo(5)); + Assert.That(intersections.SplineIndex, Is.Not.Null); + Assert.That(intersections.IntersectionAngle, Is.Not.Null); + Assert.That(intersections.IntersectionX, Is.Not.Null); + Assert.That(intersections.IntersectionY, Is.Not.Null); + } + } + + [Test] + public void Constructor_InitializesArraysWithCorrectLength() + { + var intersections = new DisposableSplineIntersections(10); + + using (Assert.EnterMultipleScope()) + { + Assert.That(intersections.SplineIndex, Has.Length.EqualTo(10)); + Assert.That(intersections.IntersectionAngle, Has.Length.EqualTo(10)); + Assert.That(intersections.IntersectionX, Has.Length.EqualTo(10)); + Assert.That(intersections.IntersectionY, Has.Length.EqualTo(10)); + } + } + + [Test] + public void NumIntersections_SetAndGet_WorksCorrectly() + { + var intersections = new DisposableSplineIntersections(5); + + intersections.NumIntersections = 10; + + Assert.That(intersections.NumIntersections, Is.EqualTo(10)); + } + + [Test] + public void SplineIndex_SetAndGet_WorksCorrectly() + { + var intersections = new DisposableSplineIntersections(3); + var indices = new[] { 0, 2, 5 }; + + intersections.SplineIndex = indices; + + Assert.That(intersections.SplineIndex, Is.EqualTo(indices)); + } + + [Test] + public void IntersectionAngle_SetAndGet_WorksCorrectly() + { + var intersections = new DisposableSplineIntersections(3); + var angles = new[] { 0.5, 1.2, 2.8 }; + + intersections.IntersectionAngle = angles; + + Assert.That(intersections.IntersectionAngle, Is.EqualTo(angles)); + } + + [Test] + public void IntersectionX_SetAndGet_WorksCorrectly() + { + var intersections = new DisposableSplineIntersections(3); + var xCoords = new[] { 10.5, 20.3, 30.7 }; + + intersections.IntersectionX = xCoords; + + Assert.That(intersections.IntersectionX, Is.EqualTo(xCoords)); + } + + [Test] + public void IntersectionY_SetAndGet_WorksCorrectly() + { + var intersections = new DisposableSplineIntersections(3); + var yCoords = new[] { 15.2, 25.6, 35.9 }; + + intersections.IntersectionY = yCoords; + + Assert.That(intersections.IntersectionY, Is.EqualTo(yCoords)); + } + + [Test] + public void Constructor_WithZeroIntersections_CreatesEmptyArrays() + { + var intersections = new DisposableSplineIntersections(0); + + using (Assert.EnterMultipleScope()) + { + Assert.That(intersections.SplineIndex, Is.Empty); + Assert.That(intersections.IntersectionAngle, Is.Empty); + Assert.That(intersections.IntersectionX, Is.Empty); + Assert.That(intersections.IntersectionY, Is.Empty); + } + } + + [Test] + public void Constructor_WithNegativeAngles_WorksCorrectly() + { + var intersections = new DisposableSplineIntersections(2) + { + IntersectionAngle = new[] { -1.5, -2.3 } + }; + + Assert.That(intersections.IntersectionAngle, Is.EqualTo(new[] { -1.5, -2.3 })); + } + + [Test] + public void Constructor_WithNegativeCoordinates_WorksCorrectly() + { + var intersections = new DisposableSplineIntersections(2) + { + IntersectionX = new[] { -10.5, -20.3 }, + IntersectionY = new[] { -15.2, -25.6 } + }; + + using (Assert.EnterMultipleScope()) + { + Assert.That(intersections.IntersectionX, Is.EqualTo(new[] { -10.5, -20.3 })); + Assert.That(intersections.IntersectionY, Is.EqualTo(new[] { -15.2, -25.6 })); + } + } + + [Test] + public void AllProperties_CanBeSetAndRetrieved() + { + var intersections = new DisposableSplineIntersections(2) + { + NumIntersections = 2, + SplineIndex = new[] { 1, 3 }, + IntersectionAngle = new[] { 1.57, 3.14 }, + IntersectionX = new[] { 100.0, 200.0 }, + IntersectionY = new[] { 150.0, 250.0 } + }; + + using (Assert.EnterMultipleScope()) + { + Assert.That(intersections.NumIntersections, Is.EqualTo(2)); + Assert.That(intersections.SplineIndex, Is.EqualTo(new[] { 1, 3 })); + Assert.That(intersections.IntersectionAngle, Is.EqualTo(new[] { 1.57, 3.14 })); + Assert.That(intersections.IntersectionX, Is.EqualTo(new[] { 100.0, 200.0 })); + Assert.That(intersections.IntersectionY, Is.EqualTo(new[] { 150.0, 250.0 })); + } + } + + [Test] + public void CreateNativeObject_CreatesValidNativeStructure() + { + var intersections = new DisposableSplineIntersections(3) + { + NumIntersections = 3, + SplineIndex = new[] { 0, 1, 2 }, + IntersectionAngle = new[] { 0.5, 1.0, 1.5 }, + IntersectionX = new[] { 10.0, 20.0, 30.0 }, + IntersectionY = new[] { 15.0, 25.0, 35.0 } + }; + + using (intersections) + { + SplineIntersectionsNative nativeIntersections = intersections.CreateNativeObject(); + + using (Assert.EnterMultipleScope()) + { + Assert.That(nativeIntersections.NumIntersections, Is.EqualTo(3)); + Assert.That(nativeIntersections.SplineIndex, Is.Not.EqualTo(IntPtr.Zero)); + Assert.That(nativeIntersections.IntersectionAngle, Is.Not.EqualTo(IntPtr.Zero)); + Assert.That(nativeIntersections.IntersectionX, Is.Not.EqualTo(IntPtr.Zero)); + Assert.That(nativeIntersections.IntersectionY, Is.Not.EqualTo(IntPtr.Zero)); + } + } + } + } +} \ No newline at end of file diff --git a/test/MeshKernelNETTest/Api/MeshKernelApiTest.cs b/test/MeshKernelNETTest/Api/MeshKernelApiTest.cs index 61c9f8c..38e8a7b 100644 --- a/test/MeshKernelNETTest/Api/MeshKernelApiTest.cs +++ b/test/MeshKernelNETTest/Api/MeshKernelApiTest.cs @@ -268,13 +268,11 @@ public void Mesh2dMergeTwoNodesThroughApi() Assert.That(mesh2D.NumValidEdges, Is.EqualTo(numberOfEdgesBefore-1)); var mesh2DEdgeNodes = mesh2D.EdgeNodes; - - const int invalidatedEdge = 21; + int[] invalidatedEdgeNodes = { 42, 43 }; Assert.That(mesh2DEdgeNodes.Select((n,i) => (n,i)) .Where(t => t.n < 0) .Select(t => t.i),Is.EquivalentTo(invalidatedEdgeNodes), AsString(mesh2DEdgeNodes)); - const int reconnectedEdge = 8; int[] reconnectedEdgeNodes = { 8, 13 }; Assert.That(mesh2DEdgeNodes.Skip(2 * 8).Take(2),Is.EquivalentTo(reconnectedEdgeNodes), AsString(mesh2DEdgeNodes)); } @@ -3695,7 +3693,6 @@ public void Mesh2dExpungeStateThroughApi() [Test] public void Mesh2dGetNodeEdgeDataThroughApi() { - // Setup using (DisposableMesh2D mesh = CreateMesh2D(7, 10, 10, 10)) using (var api = new MeshKernelApi()) @@ -3738,6 +3735,174 @@ public void Mesh2dGetNodeEdgeDataThroughApi() } } } - } + + [Test] + public void SplinesComputeIntersections_WithIntersectingSplines_ShouldReturnIntersections() + { + // Setup + using (var api = new MeshKernelApi()) + using (var crossSplines = new DisposableGeometryList()) + using (var testSpline = new DisposableGeometryList()) + { + var intersections = new DisposableSplineIntersections(); + + try + { + // prepare + var id = api.AllocateState(0); + + crossSplines.XCoordinates = new[] { 0.0, 10.0 }; + crossSplines.YCoordinates = new[] { 5.0, 5.0 }; + crossSplines.Values = new[] { 0.0, 0.0 }; + crossSplines.NumberOfCoordinates = 2; + + testSpline.XCoordinates = new[] { 5.0, 5.0 }; + testSpline.YCoordinates = new[] { 0.0, 10.0 }; + testSpline.Values = new[] { 0.0, 0.0 }; + testSpline.NumberOfCoordinates = 2; + + // execute & assert + Assert.That(api.SplineIntersectionsInitialize(id, crossSplines), Is.EqualTo(0)); + + Assert.That(api.GetSplineIntersections(id, testSpline, out intersections), Is.EqualTo(0)); + Assert.That(intersections.NumIntersections, Is.EqualTo(1)); + Assert.That(intersections.IntersectionX[0], Is.EqualTo(5.0)); + Assert.That(intersections.IntersectionY[0], Is.EqualTo(5.0)); + + Assert.That(api.SplineIntersectionsFinalize(id), Is.EqualTo(0)); + } + finally + { + api.ClearState(); + intersections.Dispose(); + } + } + } + + [Test] + public void SplinesComputeIntersections_WithNonIntersectingSplines_ShouldReturnZeroIntersections() + { + // Setup + using (var api = new MeshKernelApi()) + using (var crossSplines = new DisposableGeometryList()) + using (var testSpline = new DisposableGeometryList()) + { + var intersections = new DisposableSplineIntersections(); + try + { + // prepare + var id = api.AllocateState(0); + + crossSplines.XCoordinates = new[] { 0.0, 10.0 }; + crossSplines.YCoordinates = new[] { 0.0, 0.0 }; + crossSplines.Values = new[] { 0.0, 0.0 }; + crossSplines.NumberOfCoordinates = 2; + + testSpline.XCoordinates = new[] { 0.0, 10.0 }; + testSpline.YCoordinates = new[] { 5.0, 5.0 }; + testSpline.Values = new[] { 0.0, 0.0 }; + testSpline.NumberOfCoordinates = 2; + + // execute & assert + Assert.That(api.SplineIntersectionsInitialize(id, crossSplines), Is.EqualTo(0)); + + Assert.That(api.GetSplineIntersections(id, testSpline, out intersections), Is.EqualTo(0)); + Assert.That(intersections.NumIntersections, Is.EqualTo(0)); + + Assert.That(api.SplineIntersectionsFinalize(id), Is.EqualTo(0)); + } + finally + { + api.ClearState(); + intersections.Dispose(); + } + } + } + [Test] + public void SplinesComputeIntersections_CalledMultipleTimes_ShouldWorkCorrectly() + { + // Setup + using (var api = new MeshKernelApi()) + using (var crossSplines = new DisposableGeometryList()) + using (var testSpline1 = new DisposableGeometryList()) + using (var testSpline2 = new DisposableGeometryList()) + { + var intersections1 = new DisposableSplineIntersections(); + var intersections2 = new DisposableSplineIntersections(); + + try + { + // prepare + var id = api.AllocateState(0); + + // Create horizontal cross spline + crossSplines.XCoordinates = new[] { 0.0, 10.0 }; + crossSplines.YCoordinates = new[] { 5.0, 5.0 }; + crossSplines.Values = new[] { 0.0, 0.0 }; + crossSplines.NumberOfCoordinates = 2; + + testSpline1.XCoordinates = new[] { 3.0, 3.0 }; + testSpline1.YCoordinates = new[] { 0.0, 10.0 }; + testSpline1.Values = new[] { 0.0, 0.0 }; + testSpline1.NumberOfCoordinates = 2; + + testSpline2.XCoordinates = new[] { 7.0, 7.0 }; + testSpline2.YCoordinates = new[] { 0.0, 10.0 }; + testSpline2.Values = new[] { 0.0, 0.0 }; + testSpline2.NumberOfCoordinates = 2; + + // execute & assert + Assert.That(api.SplineIntersectionsInitialize(id, crossSplines), Is.EqualTo(0)); + + Assert.That(api.GetSplineIntersections(id, testSpline1, out intersections1), Is.EqualTo(0)); + Assert.That(intersections1.NumIntersections, Is.EqualTo(1)); + Assert.That(intersections1.IntersectionX[0], Is.EqualTo(3.0)); + Assert.That(intersections1.IntersectionY[0], Is.EqualTo(5.0)); + + Assert.That(api.GetSplineIntersections(id, testSpline2, out intersections2), Is.EqualTo(0)); + Assert.That(intersections2.NumIntersections, Is.EqualTo(1)); + Assert.That(intersections2.IntersectionX[0], Is.EqualTo(7.0)); + Assert.That(intersections2.IntersectionY[0], Is.EqualTo(5.0)); + + Assert.That(api.SplineIntersectionsFinalize(id), Is.EqualTo(0)); + } + finally + { + api.ClearState(); + intersections1.Dispose(); + intersections2.Dispose(); + } + } + } + + [Test] + public void SplinesComputeIntersections_WithoutInitialization_ShouldFail() + { + // Setup + using (var api = new MeshKernelApi()) + using (var testSpline = new DisposableGeometryList()) + { + var intersections = new DisposableSplineIntersections(); + try + { + // prepare + var id = api.AllocateState(0); + + testSpline.XCoordinates = new[] { 5.0, 5.0 }; + testSpline.YCoordinates = new[] { 0.0, 10.0 }; + testSpline.Values = new[] { 0.0, 0.0 }; + testSpline.NumberOfCoordinates = 2; + + // execute & assert + Assert.That(api.GetSplineIntersections(id, testSpline, out intersections), Is.Not.EqualTo(0)); + } + finally + { + api.ClearState(); + intersections.Dispose(); + } + } + } + } }