├── src ├── MeshKernelNET │ ├── Api │ │ ├── CurvilinearDirectionOptions.cs │ │ ├── StringBufferSizeAttribute.cs │ │ ├── ProjectionOptions.cs │ │ ├── InterpolationType.cs │ │ ├── IntArrayWrapper.cs │ │ ├── DeleteMeshInsidePolygonOptions.cs │ │ ├── GridTypeOptions.cs │ │ ├── ProjectToLandBoundaryOptions.cs │ │ ├── IRemoteApiProxyProvider.cs │ │ ├── MeshRefinementTypes.cs │ │ ├── RemoteMeshKernelApi.cs │ │ ├── BoundingBox.cs │ │ ├── CurvilinearParameters.cs │ │ ├── OrthogonalizationParameters.cs │ │ ├── DisposableContacts.cs │ │ ├── IReadOnly2DMesh.cs │ │ ├── MeshRefinementParameters.cs │ │ ├── SplinesToCurvilinearParameters.cs │ │ ├── DisposableMesh1D.cs │ │ ├── DisposableGeometryList.cs │ │ ├── DisposableCurvilinearGrid.cs │ │ ├── DisposableNativeObject.cs │ │ ├── DisposableGriddedSamples.cs │ │ ├── MakeGridParameters.cs │ │ └── DisposableMesh2D.cs │ ├── Native │ │ ├── ContactsNative.cs │ │ ├── BoundingBoxNative.cs │ │ ├── CurvilinearGridNative.cs │ │ ├── CurvilinearParametersNative.cs │ │ ├── GeometryListNative.cs │ │ ├── Mesh1DNative.cs │ │ ├── OrthogonalizationParametersNative.cs │ │ ├── GriddedSamplesNative.cs │ │ ├── MakeGridParametersNative.cs │ │ ├── SplinesToCurvilinearParametersNative.cs │ │ ├── MeshRefinementParametersNative.cs │ │ └── Mesh2DNative.cs │ ├── MeshKernelNET.csproj │ └── Helpers │ │ ├── IntPtrExtensions.cs │ │ ├── StringUtilityExtensions.cs │ │ ├── NativeLibrary.cs │ │ └── NativeStructConversionExtensions.cs └── MeshKernelNET.Generators │ ├── MeshKernelNET.Generators.csproj │ └── RemoteApiGenerator.cs ├── README.md ├── Directory.Build.props ├── test └── MeshKernelNETTest │ ├── Api │ ├── CurvilinearParametersTest.cs │ ├── DisposableContactsTest.cs │ ├── DisposableCurvilinearGridTest.cs │ ├── DisposableMesh1DTest.cs │ ├── MeshKernelMesh2DTest.cs │ ├── MeshKernelSerializationTest.cs │ ├── DisposableGeometryListTest.cs │ ├── MeshKernelNetwork1dTest.cs │ ├── DisposableMesh2DTest.cs │ ├── MeshKernelTestUtils.cs │ ├── MeshKernelTestUtilsTest.cs │ └── DisposableGriddedSamplesTest.cs │ ├── MeshKernelNETTest.csproj │ └── Native │ └── NativeStructConversionExtensionsTest.cs ├── Directory.Packages.props ├── MeshKernelNET.targets ├── nuget └── MeshKernelNET.nuspec ├── MeshKernelNET.sln.DotSettings ├── MeshKernelNET.sln ├── tools └── gather_to_sign_dll.py └── .gitignore /src/MeshKernelNET/Api/CurvilinearDirectionOptions.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace MeshKernelNET.Api 4 | { 5 | public enum CurvilinearDirectionOptions 6 | { 7 | [Description("M-direction")] 8 | M = 0, 9 | 10 | [Description("N-direction")] 11 | N = 1 12 | } 13 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/StringBufferSizeAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | 4 | [assembly: InternalsVisibleTo("MeshKernelNETTest")] 5 | 6 | namespace MeshKernelNET.Api 7 | { 8 | internal class StringBufferSizeAttribute : Attribute 9 | { 10 | public int BufferSize { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/ProjectionOptions.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace MeshKernelNET.Api 4 | { 5 | public enum ProjectionOptions 6 | { 7 | [Description("Cartesian")] 8 | Cartesian = 0, 9 | 10 | [Description("Spherical")] 11 | Spherical = 1, 12 | 13 | [Description("Spherical Accurate")] 14 | SphericalAccurate = 2 15 | } 16 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/InterpolationType.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace MeshKernelNET.Api 4 | { 5 | public enum InterpolationType 6 | { 7 | [Description("Short")] 8 | Short = 0, 9 | 10 | [Description("Float")] 11 | Float = 1, 12 | 13 | [Description("Int")] 14 | Int = 2, 15 | 16 | [Description("Double")] 17 | Double = 3 18 | } 19 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/IntArrayWrapper.cs: -------------------------------------------------------------------------------- 1 | using ProtoBuf; 2 | 3 | namespace MeshKernelNET.Api 4 | { 5 | [ProtoContract] 6 | public sealed class IntArrayWrapper 7 | { 8 | public IntArrayWrapper() 9 | { 10 | } 11 | 12 | public IntArrayWrapper(int size) 13 | { 14 | Values = new int[size]; 15 | } 16 | 17 | [ProtoMember(1)] 18 | public int[] Values { get; set; } 19 | } 20 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MeshKernelNET 2 | .NET wrapper for the MeshKernel library 3 | `MeshKernelNET` is a small C# wrapper around the `MeshKernel`. `MeskKernel` is a library for creating and editing meshes. It supports 1D and 2D unstructured meshes. 4 | The underlying C++ library `MeshKernel` can be found [here](https://github.com/Deltares/MeshKernel). 5 | 6 | # Installation 7 | `MeshKernelNET` provides a nuspec file to generate a nuget package. This nuget package can be used in your solution. -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/DeleteMeshInsidePolygonOptions.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace MeshKernelNET.Api 4 | { 5 | public enum DeleteMeshInsidePolygonOptions 6 | { 7 | [Description("Not intersecting")] 8 | NotIntersecting = 0, 9 | 10 | [Description("Intersecting")] 11 | Intersecting = 1, 12 | 13 | [Description("Faces with included circumcenters")] 14 | FacesWithIncludedCircumcenters = 2 15 | } 16 | } -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | Deltares 4 | Copyright © Deltares $([System.DateTime]::Now.Year) 5 | $(MSBuildProjectName) 6 | $(MSBuildProjectName) 7 | 8 | 0 9 | 8.2.2 10 | $(MeshKernelNETVersion).$(BuildNumber) 11 | $(Version) 12 | 13 | -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/GridTypeOptions.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace MeshKernelNET.Api 4 | { 5 | public enum GridTypeOptions 6 | { 7 | [Description("Square grid")] 8 | Square = 0, 9 | 10 | [Description("Wiber grid")] 11 | Wieber = 1, 12 | 13 | [Description("Hexagonal grid type one")] 14 | HexagonalTypeOne = 2, 15 | 16 | [Description("Hexagonal grid type two")] 17 | HexagonalTypeTwo = 3, 18 | 19 | [Description("Triangular grid")] 20 | Triangular = 4 21 | } 22 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Native/ContactsNative.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace MeshKernelNET.Native 5 | { 6 | /// @brief A struct used to describe contacts between a 1d and a 2d mesh 7 | [StructLayout(LayoutKind.Sequential)] 8 | public struct ContactsNative 9 | { 10 | /// 11 | /// The indices of the 1d mesh 12 | /// 13 | public IntPtr mesh1d_indices; 14 | 15 | /// 16 | /// The indices of the 2d face 17 | /// 18 | public IntPtr mesh2d_indices; 19 | 20 | /// 21 | /// The number of contacts 22 | /// 23 | public int num_contacts; 24 | } 25 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/ProjectToLandBoundaryOptions.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace MeshKernelNET.Api 4 | { 5 | public enum ProjectToLandBoundaryOptions 6 | { 7 | [Description("Do not project to land boundary")] 8 | No = 0, 9 | 10 | [Description("To original net-boundary")] 11 | ToOriginalNetBoundary = 1, 12 | 13 | [Description("Net-boundary to landBoundary")] 14 | NetBoundaryToLandBoundary = 2, 15 | 16 | [Description("Net-boundary to land boundary and inner to land")] 17 | NetBoundaryToLandBoundaryAndInnerToLand = 3, 18 | 19 | [Description("Whole net")] 20 | WholeNet = 4, 21 | 22 | [Description("Ok")] 23 | Ok = 5 24 | } 25 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/IRemoteApiProxyProvider.cs: -------------------------------------------------------------------------------- 1 | namespace MeshKernelNET.Api 2 | { 3 | /// 4 | /// Provides a way to create and release remote API proxy instances. 5 | /// 6 | public interface IRemoteApiProxyProvider 7 | { 8 | /// 9 | /// Creates a new API proxy instance. 10 | /// 11 | IMeshKernelApi CreateProxy(); 12 | 13 | /// 14 | /// Releases or disposes of the API proxy instance. 15 | /// 16 | void ReleaseProxy(IMeshKernelApi proxy); 17 | 18 | /// 19 | /// Checks whether the API proxy instance is still alive. 20 | /// 21 | bool IsProxyAlive(IMeshKernelApi proxy); 22 | } 23 | } -------------------------------------------------------------------------------- /src/MeshKernelNET.Generators/MeshKernelNET.Generators.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | latest 6 | 7 | false 8 | Analyzer 9 | CS 10 | true 11 | 12 | true 13 | true 14 | 15 | MeshKernelNET.Generators 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/MeshKernelNET/Native/BoundingBoxNative.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace MeshKernelNET.Native 4 | { 5 | [StructLayout(LayoutKind.Sequential)] 6 | public struct BoundingBoxNative 7 | { 8 | /// 9 | /// The bounding box lower left x coordinate 10 | /// 11 | public double xLowerLeft; 12 | 13 | /// 14 | /// The bounding box lower left y coordinate 15 | /// 16 | public double yLowerLeft; 17 | 18 | /// 19 | /// The bounding box upper right x coordinate 20 | /// 21 | public double xUpperRight; 22 | 23 | /// 24 | /// The bounding box upper right y coordinate 25 | /// 26 | public double yUpperRight; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/MeshKernelNETTest/Api/CurvilinearParametersTest.cs: -------------------------------------------------------------------------------- 1 | using MeshKernelNET.Api; 2 | using NUnit.Framework; 3 | 4 | namespace MeshKernelNETTest.Api 5 | { 6 | [TestFixture] 7 | public class CurvilinearParametersTest 8 | { 9 | [Test] 10 | public void CreateDefault_ReturnsCorrectCurvilinearParameters() 11 | { 12 | // Call 13 | var parameters = CurvilinearParameters.CreateDefault(); 14 | 15 | // Assert 16 | Assert.That(parameters.MRefinement, Is.EqualTo(2000)); 17 | Assert.That(parameters.NRefinement, Is.EqualTo(40)); 18 | Assert.That(parameters.SmoothingIterations, Is.EqualTo(10)); 19 | Assert.That(parameters.SmoothingParameter, Is.EqualTo(0.5)); 20 | Assert.That(parameters.AttractionParameter, Is.EqualTo(0.0)); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Native/CurvilinearGridNative.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace MeshKernelNET.Native 5 | { 6 | /// @brief A struct used to describe the values of a curvilinear grid in a C-compatible manner 7 | [StructLayout(LayoutKind.Sequential)] 8 | public struct CurvilinearGridNative 9 | { 10 | /// 11 | /// The x-coordinates of network1d nodes 12 | /// 13 | public IntPtr node_x; 14 | 15 | /// 16 | /// The y-coordinates of network1d nodes 17 | /// 18 | public IntPtr node_y; 19 | 20 | /// 21 | /// The number of curvilinear grid nodes along m 22 | /// 23 | public int num_m; 24 | 25 | /// 26 | /// The number of curvilinear grid nodes along n 27 | /// 28 | public int num_n; 29 | } 30 | } -------------------------------------------------------------------------------- /Directory.Packages.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /MeshKernelNET.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <_FilesToCopyMeshKernel Include="$(MSBuildThisFileDirectory)..\..\content\native\**\*"/> 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | <_OriginalFilesMeshKernel Include="$(MSBuildThisFileDirectory)..\..\content\native\**\*"/> 14 | <_FilesToCleanIONetCDF Include="$(OutDir)\win-x64\native\%(_OriginalFilesMeshKernel.RecursiveDir)%(_OriginalFilesMeshKernel.FileName)%(_OriginalFilesMeshKernel.Extension)"/> 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/MeshKernelNET/MeshKernelNET.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | $(SolutionDir)\bin 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | <_Parameter1>MeshKernelNETTest 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/MeshKernelNET/Native/CurvilinearParametersNative.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace MeshKernelNET.Native 4 | { 5 | [StructLayout(LayoutKind.Sequential)] 6 | public struct CurvilinearParametersNative 7 | { 8 | /// 9 | /// *M-refinement factor for regular grid generation (2000) 10 | /// 11 | public int MRefinement; 12 | 13 | /// 14 | /// *N-refinement factor for regular grid generation (40) 15 | /// 16 | public int NRefinement; 17 | 18 | /// 19 | /// Nr. of inner iterations in regular grid smoothing (10). 20 | /// 21 | public int SmoothingIterations; 22 | 23 | /// 24 | /// * Smoothing parameter (0.5). 25 | /// 26 | public double SmoothingParameter; 27 | 28 | /// 29 | /// *Attraction/repulsion parameter (0). 30 | /// 31 | public double AttractionParameter; 32 | } 33 | } -------------------------------------------------------------------------------- /test/MeshKernelNETTest/MeshKernelNETTest.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net48 5 | $(SolutionDir)\bin 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | all 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/MeshRefinementTypes.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace MeshKernelNET.Api 4 | { 5 | /// 6 | /// Specifies the different types of mesh refinement techniques 7 | /// available in the mesh kernel. 8 | /// 9 | public enum MeshRefinementTypes 10 | { 11 | /// 12 | /// Mesh refinement based on wave Courant numbers, 13 | /// typically used in coastal and wave modeling applications. 14 | /// 15 | [Description("Wave courant")] 16 | WaveCourant = 1, 17 | 18 | /// 19 | /// Mesh refinement using specified levels to control 20 | /// resolution across different areas of the mesh. 21 | /// 22 | [Description("Refinement levels")] 23 | RefinementLevels = 2, 24 | 25 | /// 26 | /// Mesh refinement based on ridge detection algorithms, 27 | /// often used to better capture bathymetric features. 28 | /// 29 | [Description("Ridge detection")] 30 | RidgeDetection = 3 31 | } 32 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Native/GeometryListNative.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace MeshKernelNET.Native 5 | { 6 | [StructLayout(LayoutKind.Sequential)] 7 | public struct GeometryListNative 8 | { 9 | /// 10 | /// The value used as separator in coordinates_x, coordinates_y and values 11 | /// 12 | public double geometrySeperator; 13 | 14 | /// 15 | /// The value used to separate the inner part of a polygon from its outer part 16 | /// 17 | public double innerOuterSeperator; 18 | 19 | /// 20 | /// The number of coordinate values present 21 | /// 22 | public int numberOfCoordinates; 23 | 24 | /// 25 | /// The x coordinate values 26 | /// 27 | public IntPtr xCoordinates; 28 | 29 | /// 30 | /// The y coordinate values 31 | /// 32 | public IntPtr yCoordinates; 33 | 34 | /// 35 | /// The values at the point 36 | /// 37 | public IntPtr zCoordinates; 38 | } 39 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/RemoteMeshKernelApi.cs: -------------------------------------------------------------------------------- 1 | namespace MeshKernelNET.Api 2 | { 3 | /// 4 | /// Remote wrapper for that executes operations in a separate process. 5 | /// 6 | public sealed partial class RemoteMeshKernelApi : IMeshKernelApi 7 | { 8 | private readonly IRemoteApiProxyProvider provider; 9 | private IMeshKernelApi api; 10 | 11 | /// 12 | /// Initializes a new instance of the class. 13 | /// 14 | /// Provides remote API proxy instances. 15 | public RemoteMeshKernelApi(IRemoteApiProxyProvider provider) 16 | { 17 | this.provider = provider; 18 | api = provider.CreateProxy(); 19 | } 20 | 21 | /// 22 | public void Dispose() 23 | { 24 | if (api == null) 25 | { 26 | return; 27 | } 28 | 29 | if (provider.IsProxyAlive(api)) 30 | { 31 | api.Dispose(); 32 | } 33 | 34 | provider.ReleaseProxy(api); 35 | api = null; 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Helpers/IntPtrExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace MeshKernelNET.Helpers 5 | { 6 | internal static class IntPtrExtensions 7 | { 8 | public static T[] CreateValueArray(this IntPtr pointer, int size, int stringSize = 0) 9 | { 10 | var array = new T[size]; 11 | 12 | if (typeof(T) == typeof(double)) 13 | { 14 | Marshal.Copy(pointer, array as double[], 0, size); 15 | } 16 | else if (typeof(T) == typeof(int)) 17 | { 18 | Marshal.Copy(pointer, array as int[], 0, size); 19 | } 20 | else if (typeof(T) == typeof(string)) 21 | { 22 | int totalByteSize = size * stringSize; 23 | var byteArray = new byte[totalByteSize]; 24 | 25 | Marshal.Copy(pointer, byteArray, 0, totalByteSize); 26 | 27 | byteArray.GetStringArrayFromFlattenedAsciiCodedStringArray(size).CopyTo(array, 0); 28 | } 29 | else 30 | { 31 | throw new NotSupportedException("Currently only double, int and string are supported"); 32 | } 33 | 34 | return array; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /test/MeshKernelNETTest/Api/DisposableContactsTest.cs: -------------------------------------------------------------------------------- 1 | using MeshKernelNET.Api; 2 | using MeshKernelNET.Native; 3 | using NUnit.Framework; 4 | 5 | namespace MeshKernelNETTest.Api 6 | { 7 | [TestFixture] 8 | public class DisposableContactsTest 9 | { 10 | [Test] 11 | public void UpdateFromNativeObjectShouldPopulateContacts() 12 | { 13 | // Arrange 14 | var managedContacts = new DisposableContacts(); 15 | var nativeContacts = new DisposableContacts 16 | { 17 | Mesh1dIndices = new[] { 1, 2, 3 }, 18 | Mesh2dIndices = new[] { 4, 5, 6 }, 19 | NumContacts = 3, 20 | }; 21 | 22 | using (nativeContacts) 23 | { 24 | ContactsNative nativeObject = nativeContacts.CreateNativeObject(); 25 | 26 | // Act 27 | managedContacts.UpdateFromNativeObject(ref nativeObject); 28 | 29 | // Assert 30 | Assert.That(managedContacts.Mesh1dIndices, Is.EqualTo(new[] { 1, 2, 3 })); 31 | Assert.That(managedContacts.Mesh2dIndices, Is.EqualTo(new[] { 4, 5, 6 })); 32 | Assert.That(managedContacts.NumContacts, Is.EqualTo(3)); 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Native/Mesh1DNative.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace MeshKernelNET.Native 5 | { 6 | /// @brief A struct used to describe the values of a mesh 1d in a C-compatible manner 7 | [StructLayout(LayoutKind.Sequential)] 8 | public struct Mesh1DNative 9 | { 10 | /// 11 | /// The nodes composing each mesh 1d edge 12 | /// 13 | public IntPtr edge_nodes; 14 | 15 | /// 16 | /// The x-coordinates of the mesh nodes 17 | /// 18 | public IntPtr node_x; 19 | 20 | /// 21 | /// The y-coordinates of the mesh nodes 22 | /// 23 | public IntPtr node_y; 24 | 25 | /// 26 | /// The number of 1d nodes 27 | /// 28 | public int num_nodes; 29 | 30 | /// 31 | /// The number of 1d valid nodes 32 | /// 33 | public int num_valid_nodes; 34 | 35 | /// 36 | /// The number of 1d edges 37 | /// 38 | public int num_edges; 39 | 40 | /// 41 | /// The number of 1d valid edges 42 | /// 43 | public int num_valid_edges; 44 | } 45 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/BoundingBox.cs: -------------------------------------------------------------------------------- 1 | using ProtoBuf; 2 | 3 | namespace MeshKernelNET.Api 4 | { 5 | [ProtoContract(AsReferenceDefault = true)] 6 | public class BoundingBox 7 | { 8 | public static BoundingBox CreateDefault() 9 | { 10 | return new BoundingBox 11 | { 12 | xLowerLeft = double.MinValue, 13 | yLowerLeft = double.MinValue, 14 | xUpperRight = double.MaxValue, 15 | yUpperRight = double.MaxValue 16 | }; 17 | } 18 | 19 | /// 20 | /// The bounding box lower left x coordinate 21 | /// 22 | [ProtoMember(1)] 23 | public double xLowerLeft { get; set; } 24 | 25 | /// 26 | /// The bounding box lower left y coordinate 27 | /// 28 | [ProtoMember(2)] 29 | public double yLowerLeft { get; set; } 30 | 31 | /// 32 | /// The bounding box upper right x coordinate 33 | /// 34 | [ProtoMember(3)] 35 | public double xUpperRight { get; set; } 36 | 37 | /// 38 | /// The bounding box upper right y coordinate 39 | /// 40 | [ProtoMember(4)] 41 | public double yUpperRight { get; set; } 42 | } 43 | } -------------------------------------------------------------------------------- /nuget/MeshKernelNET.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MeshKernelNET 5 | 8.2.2 6 | Deltares meshing library 7 | Deltares 8 | Deltares 9 | false 10 | This is a package containing a managed dll wrapping around the native dll of MeshKernel. This native dll is a library containing functionality to generate and edit 1D, 2D meshes. 11 | Copyright © Deltares $copyrightYear$ 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/MeshKernelNETTest/Api/DisposableCurvilinearGridTest.cs: -------------------------------------------------------------------------------- 1 | using MeshKernelNET.Api; 2 | using MeshKernelNET.Native; 3 | using NUnit.Framework; 4 | 5 | namespace MeshKernelNETTest.Api 6 | { 7 | [TestFixture] 8 | public class DisposableCurvilinearGridTest 9 | { 10 | [Test] 11 | public void UpdateFromNativeObjectShouldPopulateCurvilinearGrid() 12 | { 13 | // Arrange 14 | var managedGrid = new DisposableCurvilinearGrid(); 15 | var nativeGrid = new DisposableCurvilinearGrid(3, 3) 16 | { 17 | NodeX = new[] { 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0 }, 18 | NodeY = new[] { 4.0, 5.0, 6.0, 4.0, 5.0, 6.0, 4.0, 5.0, 6.0 } 19 | }; 20 | 21 | using (nativeGrid) 22 | { 23 | CurvilinearGridNative nativeObject = nativeGrid.CreateNativeObject(); 24 | 25 | // Act 26 | managedGrid.UpdateFromNativeObject(ref nativeObject); 27 | 28 | // Assert 29 | Assert.That(managedGrid.NodeX, Is.EqualTo(new[] { 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0 })); 30 | Assert.That(managedGrid.NodeY, Is.EqualTo(new[] { 4.0, 5.0, 6.0, 4.0, 5.0, 6.0, 4.0, 5.0, 6.0 })); 31 | Assert.That(managedGrid.NumM, Is.EqualTo(3)); 32 | Assert.That(managedGrid.NumN, Is.EqualTo(3)); 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Native/OrthogonalizationParametersNative.cs: -------------------------------------------------------------------------------- 1 | namespace MeshKernelNET.Native 2 | { 3 | /// 4 | /// All orthogonalization options 5 | /// 6 | public struct OrthogonalizationParametersNative 7 | { 8 | /// 9 | /// *Number of outer iterations in orthogonalization. Increase this parameter for complex grids (2) 10 | /// 11 | public int OuterIterations { get; set; } 12 | 13 | /// 14 | /// *Number of boundary iterations in grid/net orthogonalization within itatp (25) 15 | /// 16 | public int BoundaryIterations { get; set; } 17 | 18 | /// 19 | /// *Number of inner iterations in grid/net orthogonalization within itbnd (25) 20 | /// 21 | public int InnerIterations { get; set; } 22 | 23 | /// 24 | /// *Factor from 0 to 1. between grid smoothing and grid orthogonality (0.975) 25 | /// 26 | public double OrthogonalizationToSmoothingFactor { get; set; } 27 | 28 | /// 29 | /// Minimum ATPF on the boundary (1.0) 30 | /// 31 | public double OrthogonalizationToSmoothingFactorAtBoundary { get; set; } 32 | 33 | /// 34 | /// Factor between smoother 1d0 and area-homogenizer 0d0 (1.0) 35 | /// 36 | public double ArealToAngleSmoothingFactor { get; set; } 37 | } 38 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/CurvilinearParameters.cs: -------------------------------------------------------------------------------- 1 | using ProtoBuf; 2 | 3 | namespace MeshKernelNET.Api 4 | { 5 | [ProtoContract(AsReferenceDefault = true)] 6 | public class CurvilinearParameters 7 | { 8 | public static CurvilinearParameters CreateDefault() 9 | { 10 | return new CurvilinearParameters 11 | { 12 | MRefinement = 2000, 13 | NRefinement = 40, 14 | SmoothingIterations = 10, 15 | SmoothingParameter = 0.5, 16 | AttractionParameter = 0.0 17 | }; 18 | } 19 | 20 | /// 21 | /// *M-refinement factor for regular grid generation (2000) 22 | /// 23 | [ProtoMember(1)] 24 | public int MRefinement { get; set; } 25 | 26 | /// 27 | /// *N-refinement factor for regular grid generation (40) 28 | /// 29 | [ProtoMember(2)] 30 | public int NRefinement { get; set; } 31 | 32 | /// 33 | /// *Nr. of inner iterations in regular grid smoothing (10). 34 | /// 35 | [ProtoMember(3)] 36 | public int SmoothingIterations { get; set; } 37 | 38 | /// 39 | /// * Smoothing parameter (0.5). 40 | /// 41 | [ProtoMember(4)] 42 | public double SmoothingParameter { get; set; } 43 | 44 | /// 45 | /// *Attraction/repulsion parameter (0.0). 46 | /// 47 | [ProtoMember(5)] 48 | public double AttractionParameter { get; set; } 49 | } 50 | } -------------------------------------------------------------------------------- /MeshKernelNET.sln.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | ..\D-HYDRO.DotSettings 3 | True 4 | True 5 | 1 6 | True 7 | True 8 | True 9 | True 10 | True 11 | True 12 | True -------------------------------------------------------------------------------- /test/MeshKernelNETTest/Api/DisposableMesh1DTest.cs: -------------------------------------------------------------------------------- 1 | using MeshKernelNET.Api; 2 | using MeshKernelNET.Native; 3 | using NUnit.Framework; 4 | 5 | namespace MeshKernelNETTest.Api 6 | { 7 | [TestFixture] 8 | public class DisposableMesh1DTest 9 | { 10 | [Test] 11 | public void UpdateFromNativeObjectShouldPopulateMesh1D() 12 | { 13 | // Arrange 14 | var managedMesh = new DisposableMesh1D(); 15 | var nativeMesh = new DisposableMesh1D(4, 3) 16 | { 17 | NodeX = new[] { 0.0, 1.0, 2.0, 3.0 }, 18 | NodeY = new[] { 0.0, 0.0, 0.0, 0.0 }, 19 | EdgeNodes = new[] { 0, 1, 1, 2, 2, 3 }, 20 | NumNodes = 4, 21 | NumEdges = 3, 22 | NumValidNodes = 4, 23 | NumValidEdges = 3 24 | }; 25 | 26 | using (nativeMesh) 27 | { 28 | Mesh1DNative nativeObject = nativeMesh.CreateNativeObject(); 29 | 30 | // Act 31 | managedMesh.UpdateFromNativeObject(ref nativeObject); 32 | 33 | // Assert 34 | Assert.That(managedMesh.NodeX, Is.EqualTo(new[] { 0.0, 1.0, 2.0, 3.0 })); 35 | Assert.That(managedMesh.NodeY, Is.EqualTo(new[] { 0.0, 0.0, 0.0, 0.0 })); 36 | Assert.That(managedMesh.EdgeNodes, Is.EqualTo(new[] { 0, 1, 1, 2, 2, 3 })); 37 | Assert.That(managedMesh.NumNodes, Is.EqualTo(4)); 38 | Assert.That(managedMesh.NumEdges, Is.EqualTo(3)); 39 | Assert.That(managedMesh.NumValidNodes, Is.EqualTo(4)); 40 | Assert.That(managedMesh.NumValidEdges, Is.EqualTo(3)); 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Native/GriddedSamplesNative.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace MeshKernelNET.Native 5 | { 6 | /// @brief A struct describing gridded samples 7 | [StructLayout(LayoutKind.Sequential)] 8 | public struct GriddedSamplesNative 9 | { 10 | /// 11 | /// Number of x gridded samples coordinates 12 | /// 13 | public int num_x; 14 | 15 | /// 16 | /// Number of y gridded samples coordinates 17 | /// 18 | public int num_y; 19 | 20 | /// 21 | /// X coordinate of the grid origin (lower left corner) 22 | /// 23 | public double origin_x; 24 | 25 | /// 26 | /// Y coordinate of the grid origin (lower left corner) 27 | /// 28 | public double origin_y; 29 | 30 | /// 31 | /// Constant grid cell size 32 | /// 33 | public double cell_size; 34 | 35 | /// 36 | /// If not nullptr, coordinates for non-uniform grid spacing in x direction 37 | /// 38 | public IntPtr coordinates_x; 39 | 40 | /// 41 | /// If not nullptr, coordinates for non-uniform grid spacing in y direction 42 | /// 43 | public IntPtr coordinates_y; 44 | 45 | /// 46 | /// Sample values 47 | /// 48 | public IntPtr values; 49 | 50 | /// 51 | /// The numeric representation of the values (0 = short, 1 = float, 2 = int and 3 = double) 52 | /// 53 | public int value_type; 54 | } 55 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Native/MakeGridParametersNative.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace MeshKernelNET.Native 4 | { 5 | [StructLayout(LayoutKind.Sequential)] 6 | public struct MakeGridParametersNative 7 | { 8 | /// 9 | /// * The number of columns in x direction (3) 10 | /// 11 | public int NumberOfColumns; 12 | 13 | /// 14 | /// * The number of columns in y direction (3) 15 | /// 16 | public int NumberOfRows; 17 | 18 | /// 19 | /// * The grid angle (0.0) 20 | /// 21 | public double GridAngle; 22 | 23 | /// 24 | /// * The x coordinate of the origin, located at the bottom left corner (0.0) 25 | /// 26 | public double OriginXCoordinate; 27 | 28 | /// 29 | /// * The y coordinate of the origin, located at the bottom left corner (0.0) 30 | /// 31 | public double OriginYCoordinate; 32 | 33 | /// 34 | /// * The grid block size in x dimension, used only for squared grids (10.0) 35 | /// 36 | public double XGridBlockSize; 37 | 38 | /// 39 | /// * The grid block size in y dimension, used only for squared grids (10.0) 40 | /// 41 | public double YGridBlockSize; 42 | 43 | /// 44 | /// * The x coordinate of the upper right corner (0.0) 45 | /// 46 | public double UpperRightCornerXCoordinate; 47 | 48 | /// 49 | /// * The y coordinate of the upper right corner (0.0) 50 | /// 51 | public double UpperRightCornerYCoordinate; 52 | } 53 | } -------------------------------------------------------------------------------- /test/MeshKernelNETTest/Api/MeshKernelMesh2DTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace MeshKernelNETTest.Api 4 | { 5 | [TestFixture] 6 | public class MeshKernelMesh2DTest 7 | { 8 | [Test] 9 | public void Mesh2D_GetNodeXY_AccessesNodeXY() 10 | { 11 | int numRows = 3; 12 | int numColumns = 4; 13 | using (var mesh = TestUtilityFunctions.CreateMesh2D(numRows,numColumns, 15.0, 4.0, 2.0, 3.0)) 14 | { 15 | for (int row = 0; row < numRows; ++row) 16 | { 17 | for (int column = 0; column < numColumns; ++column) 18 | { 19 | int index = row * numColumns + column; 20 | Assert.That(mesh.NodeX[index], Is.EqualTo(mesh.GetNodeX(index))); 21 | Assert.That(mesh.NodeY[index], Is.EqualTo(mesh.GetNodeY(index))); 22 | } 23 | } 24 | } 25 | } 26 | 27 | [TestCase(5,3,5*3)] 28 | [TestCase(15,42,15*42)] 29 | public void Mesh2D_GetNodeCount_CountsAllNodes(int nodesInRow, int nodesInColumn, int count) 30 | { 31 | using (var grid = TestUtilityFunctions.CreateMesh2D(nodesInRow,nodesInColumn, 15.0, 4.0, 2.0, 3.0)) 32 | { 33 | Assert.That(grid.NodeCount(), Is.EqualTo(count)); 34 | } 35 | } 36 | 37 | [TestCase(5,3,4*3+5*2)] 38 | [TestCase(15,42,15*41+14*42)] 39 | public void Mesh2D_GetEdgeCount_CountsRowOrientedAndColumnOrientedEdges(int nodesInRow, int nodesInColumn, int count) 40 | { 41 | using (var grid = TestUtilityFunctions.CreateMesh2D(nodesInRow,nodesInColumn, 15.0, 4.0, 2.0, 3.0)) 42 | { 43 | Assert.That(grid.EdgeCount(), Is.EqualTo(count)); 44 | } 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Native/SplinesToCurvilinearParametersNative.cs: -------------------------------------------------------------------------------- 1 | namespace MeshKernelNET.Native 2 | { 3 | public struct SplinesToCurvilinearParametersNative 4 | { 5 | /// 6 | /// * Aspect ratio (0.1) 7 | /// 8 | public double AspectRatio { get; set; } 9 | 10 | /// 11 | /// * Grow factor of aspect ratio (1.1) 12 | /// 13 | public double AspectRatioGrowFactor { get; set; } 14 | 15 | /// 16 | /// * Average mesh width on center spline (500.0) 17 | /// 18 | public double AverageWidth { get; set; } 19 | 20 | /// 21 | /// * Curvature adapted grid spacing, 1 or not 0 (1) 22 | /// 23 | public int CurvatureAdaptedGridSpacing { get; set; } 24 | 25 | /// 26 | /// * Grow the grid outside the prescribed grid height (1) 27 | /// 28 | public int GrowGridOutside { get; set; } 29 | 30 | /// 31 | /// * Maximum number of layers in the uniform part (5) 32 | /// 33 | public int MaximumNumberOfGridCellsInTheUniformPart { get; set; } 34 | 35 | /// 36 | /// * On-top-of-each-other tolerance (0.0001) 37 | /// 38 | public double GridsOnTopOfEachOtherTolerance { get; set; } 39 | 40 | /// 41 | /// * Minimum allowed absolute value of crossing-angle cosine (0.95) 42 | /// 43 | public double MinimumCosineOfCrossingAngles { get; set; } 44 | 45 | /// 46 | /// * Check for collisions with other parts of the front, 1 or not 0 (0) 47 | /// 48 | public int CheckFrontCollisions { get; set; } 49 | 50 | /// 51 | /// * Remove skinny triangles (1) 52 | /// 53 | public int RemoveSkinnyTriangles { get; set; } 54 | } 55 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/OrthogonalizationParameters.cs: -------------------------------------------------------------------------------- 1 | using ProtoBuf; 2 | 3 | namespace MeshKernelNET.Api 4 | { 5 | [ProtoContract(AsReferenceDefault = true)] 6 | public class OrthogonalizationParameters 7 | { 8 | public static OrthogonalizationParameters CreateDefault() 9 | { 10 | return new OrthogonalizationParameters 11 | { 12 | OuterIterations = 2, 13 | BoundaryIterations = 25, 14 | InnerIterations = 25, 15 | OrthogonalizationToSmoothingFactor = 0.975, 16 | OrthogonalizationToSmoothingFactorAtBoundary = 1.0, 17 | ArealToAngleSmoothingFactor = 1.0, 18 | }; 19 | } 20 | 21 | /// 22 | /// *Number of outer iterations in orthogonalization. Increase this parameter for complex grids (2) 23 | /// 24 | [ProtoMember(1)] 25 | public int OuterIterations { get; set; } 26 | 27 | /// 28 | /// *Number of boundary iterations in grid/net orthogonalization within itatp (25) 29 | /// 30 | [ProtoMember(2)] 31 | public int BoundaryIterations { get; set; } 32 | 33 | /// 34 | /// Number of inner iterations in grid/net orthogonalization within itbnd (25) 35 | /// 36 | [ProtoMember(3)] 37 | public int InnerIterations { get; set; } 38 | 39 | /// 40 | /// Factor from 0 to 1. between grid smoothing and grid orthogonality (0.975) 41 | /// 42 | [ProtoMember(4)] 43 | public double OrthogonalizationToSmoothingFactor { get; set; } 44 | 45 | /// 46 | /// Minimum ATPF on the boundary (1.0) 47 | /// 48 | [ProtoMember(5)] 49 | public double OrthogonalizationToSmoothingFactorAtBoundary { get; set; } 50 | 51 | /// 52 | /// Factor between smoother 1d0 and area-homogenizer 0d0 (1.0) 53 | /// 54 | [ProtoMember(6)] 55 | public double ArealToAngleSmoothingFactor { get; set; } 56 | } 57 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/DisposableContacts.cs: -------------------------------------------------------------------------------- 1 | using MeshKernelNET.Helpers; 2 | using MeshKernelNET.Native; 3 | using ProtoBuf; 4 | 5 | namespace MeshKernelNET.Api 6 | { 7 | [ProtoContract(AsReferenceDefault = true)] 8 | public sealed class DisposableContacts : DisposableNativeObject 9 | { 10 | [ProtoMember(1)] 11 | private int[] mesh1dIndices; 12 | 13 | [ProtoMember(2)] 14 | private int[] mesh2dIndices; 15 | 16 | [ProtoMember(3)] 17 | private int numContacts; 18 | 19 | public DisposableContacts() 20 | { 21 | } 22 | 23 | public DisposableContacts(int nContacts) 24 | { 25 | NumContacts = nContacts; 26 | Mesh1dIndices = new int[NumContacts]; 27 | Mesh2dIndices = new int[NumContacts]; 28 | } 29 | 30 | ~DisposableContacts() 31 | { 32 | Dispose(false); 33 | } 34 | 35 | public int[] Mesh1dIndices 36 | { 37 | get { return mesh1dIndices; } 38 | set { mesh1dIndices = value; } 39 | } 40 | 41 | public int[] Mesh2dIndices 42 | { 43 | get { return mesh2dIndices; } 44 | set { mesh2dIndices = value; } 45 | } 46 | 47 | public int NumContacts 48 | { 49 | get { return numContacts; } 50 | set { numContacts = value; } 51 | } 52 | 53 | protected override void SetNativeObject(ref ContactsNative nativeObject) 54 | { 55 | nativeObject.mesh1d_indices = GetPinnedObjectPointer(Mesh1dIndices); 56 | nativeObject.mesh2d_indices = GetPinnedObjectPointer(Mesh2dIndices); 57 | nativeObject.num_contacts = NumContacts; 58 | } 59 | 60 | public void UpdateFromNativeObject(ref ContactsNative nativeObject) 61 | { 62 | Mesh1dIndices = nativeObject.mesh1d_indices.CreateValueArray(nativeObject.num_contacts); 63 | Mesh2dIndices = nativeObject.mesh2d_indices.CreateValueArray(nativeObject.num_contacts); 64 | NumContacts = nativeObject.num_contacts; 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Native/MeshRefinementParametersNative.cs: -------------------------------------------------------------------------------- 1 | namespace MeshKernelNET.Native 2 | { 3 | /// 4 | /// A struct used to describe the mesh refinement parameters in a C-compatible manner 5 | /// 6 | public struct MeshRefinementParametersNative 7 | { 8 | /// 9 | /// Maximum number of refinement iterations, set to 1 if only one refinement is wanted (10) 10 | /// 11 | public int MaxNumRefinementIterations { get; set; } 12 | 13 | /// 14 | /// Whether to compute faces intersected by polygon (yes=1/no=0) 15 | /// 16 | public int RefineIntersected { get; set; } 17 | 18 | /// 19 | /// Whether to use the mass center when splitting a face in the refinement process (yes=1/no=0) 20 | /// 21 | public int UseMassCenterWhenRefining { get; set; } 22 | 23 | /// 24 | /// Minimum edge size 25 | /// 26 | public double MinEdgeSize { get; set; } 27 | 28 | /// 29 | /// Refinement criterion type 30 | /// 31 | public int RefinementType { get; set; } 32 | 33 | /// 34 | /// Connect hanging nodes at the end of the iteration, 1 yes or 0 no 35 | /// 36 | public int ConnectHangingNodes { get; set; } 37 | 38 | /// 39 | /// Take samples outside face into account , 1 yes 0 no 40 | /// 41 | public int AccountForSamplesOutside { get; set; } 42 | 43 | /// 44 | /// The number of smoothing iterations 45 | /// 46 | public int SmoothingIterations { get; set; } 47 | 48 | /// 49 | /// Maximum courant time in seconds 50 | /// 51 | public double MaxCourantTime { get; set; } 52 | 53 | /// 54 | /// Directional refinement, cannot be used when the number of smoothing iterations is larger than 0 55 | /// 56 | public int DirectionalRefinement { get; set; } 57 | } 58 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/IReadOnly2DMesh.cs: -------------------------------------------------------------------------------- 1 | namespace MeshKernelNET.Api 2 | { 3 | /// 4 | /// Topology and node locations of a general 2D mesh 5 | /// 6 | public interface IReadOnly2DMesh 7 | { 8 | /// 9 | /// The number of cells 10 | /// 11 | /// a nonnegative number representing the number of cells 12 | int CellCount(); 13 | 14 | /// 15 | /// The number of edges of a cell 16 | /// 17 | /// the index of the cell 18 | /// a nonnegative number representing the number of edges of the cell 19 | int GetCellEdgeCount(int cellIndex); 20 | 21 | /// 22 | /// The total number of edges in the mesh 23 | /// 24 | /// a nonnegative number representing the number of edges 25 | int EdgeCount(); 26 | 27 | /// 28 | /// The index of the first node of an edge 29 | /// 30 | /// a nonnegative number representing the node index 31 | int GetFirstNode(int edgeIndex); 32 | 33 | /// 34 | /// The index of the second node of an edge 35 | /// 36 | /// the index of the cell 37 | /// a nonnegative number representing the node index 38 | int GetLastNode(int edgeIndex); 39 | 40 | 41 | /// 42 | /// The total number of nodes in the mesh 43 | /// 44 | /// a nonnegative number representing the number of nodes 45 | int NodeCount(); 46 | 47 | /// 48 | /// The x coordinate of a node 49 | /// 50 | /// the index of the cell 51 | /// the x-coordinate value of the node 52 | double GetNodeX(int nodeIndex); 53 | 54 | /// 55 | /// The y coordinate of a node 56 | /// 57 | /// the index of the cell 58 | /// the y-coordinate value of the node 59 | double GetNodeY(int nodeIndex); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/MeshKernelNET/Helpers/StringUtilityExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using System.Text; 5 | using MeshKernelNET.Api; 6 | 7 | namespace MeshKernelNET.Helpers 8 | { 9 | internal static class StringUtilityExtensions 10 | { 11 | private const char FillChar = ' '; 12 | 13 | public static byte[] GetFlattenedAsciiCodedStringArray(this string[] strings, int bufferSize) 14 | { 15 | string flattenedAsciiCodedStringArray = string.Join("", strings.Select(s => s.ToFixedLengthString(bufferSize))); 16 | 17 | return Encoding.Convert(Encoding.Unicode, Encoding.ASCII, 18 | Encoding.Unicode.GetBytes(flattenedAsciiCodedStringArray)); 19 | } 20 | 21 | public static string[] GetStringArrayFromFlattenedAsciiCodedStringArray(this byte[] byteArray, int bufferSize) 22 | { 23 | string asciiString = Encoding.ASCII.GetString(byteArray); 24 | int blokSize = byteArray.Length / bufferSize; 25 | string[] strings = Enumerable.Range(0, bufferSize) 26 | .Select(i => asciiString.Substring(i * blokSize, blokSize)).ToArray(); 27 | 28 | return strings.ConvertFixedLengthArray(); 29 | } 30 | 31 | public static string[] GetFixedLengthStringArray(this string[] strings, int bufferSize) 32 | { 33 | for (var index = 0; index < strings.Length; index++) 34 | { 35 | strings[index] = strings[index].ToFixedLengthString(bufferSize); 36 | } 37 | 38 | return strings; 39 | } 40 | 41 | internal static string ToFixedLengthString(this string s, int bufferSize) 42 | { 43 | if (s == null) 44 | { 45 | return new string(FillChar, bufferSize); 46 | } 47 | 48 | return s.Length > bufferSize 49 | ? s.Substring(0, bufferSize) // truncate 50 | : s.PadRight(bufferSize, FillChar); // pad 51 | } 52 | 53 | public static string[] ConvertFixedLengthArray(this string[] strings) 54 | { 55 | return strings.Select(s => s.TrimEnd(FillChar)).ToArray(); 56 | } 57 | 58 | internal static int GetBufferSize(this Type type, string fieldName) 59 | { 60 | return type.GetField(fieldName)?.GetCustomAttribute()?.BufferSize ?? 0; 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /test/MeshKernelNETTest/Api/MeshKernelSerializationTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using MeshKernelNET.Api; 7 | using NUnit.Framework; 8 | using ProtoBuf; 9 | 10 | namespace MeshKernelNETTest.Api 11 | { 12 | [TestFixture] 13 | public class MeshKernelSerializationTest 14 | { 15 | [TestCaseSource(nameof(GetProtoContractTypes))] 16 | public void TypeWithProtoContract_HasDefaultConstructor(Type type) 17 | { 18 | ConstructorInfo defaultConstructor = type.GetConstructor(Type.EmptyTypes); 19 | 20 | Assert.That(defaultConstructor, Is.Not.Null, 21 | $"Type '{type.Name}' with proto contract must have a public parameterless constructor for protobuf serialization."); 22 | } 23 | 24 | [TestCaseSource(nameof(GetProtoContractTypes))] 25 | public void TypeWithProtoContract_CanBeInstantiated(Type type) 26 | { 27 | object instance = Activator.CreateInstance(type); 28 | 29 | Assert.That(instance, Is.Not.Null, 30 | $"Should be able to create instance of '{type.Name}' for protobuf serialization."); 31 | } 32 | 33 | [TestCaseSource(nameof(GetProtoContractTypes))] 34 | public void TypeWithProtoContract_CanBeSerializedAndDeserialized(Type type) 35 | { 36 | using (var stream = new MemoryStream()) 37 | { 38 | object instance = Activator.CreateInstance(type); 39 | 40 | Serializer.Serialize(stream, instance); 41 | stream.Position = 0; 42 | object deserialized = Serializer.Deserialize(type, stream); 43 | 44 | Assert.That(deserialized, Is.Not.Null, $"Should be able to deserialize '{type.Name}'."); 45 | Assert.That(deserialized, Is.TypeOf(type), $"Deserialized object should be of type '{type.Name}'."); 46 | } 47 | } 48 | 49 | private static IEnumerable GetProtoContractTypes() 50 | { 51 | Assembly assembly = typeof(MeshKernelApi).Assembly; 52 | 53 | List protoContractTypes = assembly.GetTypes() 54 | .Where(t => t.GetCustomAttribute() != null) 55 | .OrderBy(t => t.Name) 56 | .ToList(); 57 | 58 | foreach (Type type in protoContractTypes) 59 | { 60 | yield return new TestCaseData(type).SetName($"{type.Name}"); 61 | } 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Native/Mesh2DNative.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace MeshKernelNET.Native 5 | { 6 | /// 7 | /// Data object for defining a 2D grid. This object will be 8 | /// used for communicating with the MeshKernel. 9 | /// 10 | [StructLayout(LayoutKind.Sequential)] 11 | public struct Mesh2DNative 12 | { 13 | /// 14 | /// The left and right face indices of each face 15 | /// 16 | public IntPtr edge_faces; 17 | 18 | /// 19 | /// The nodes composing each mesh 2d edge 20 | /// 21 | public IntPtr edge_nodes; 22 | 23 | /// 24 | /// The edges composing each face 25 | /// 26 | public IntPtr face_edges; 27 | 28 | /// 29 | /// The nodes composing each mesh 2d face 30 | /// 31 | public IntPtr face_nodes; 32 | 33 | /// 34 | /// The number of nodes for each mesh 2d face 35 | /// 36 | public IntPtr nodes_per_face; 37 | 38 | /// 39 | /// The x-coordinates of network1d nodes 40 | /// 41 | public IntPtr node_x; 42 | 43 | /// 44 | /// The y-coordinates of network1d nodes 45 | /// 46 | public IntPtr node_y; 47 | 48 | /// 49 | /// The x-coordinates of the mesh edges middle points 50 | /// 51 | public IntPtr edge_x; 52 | 53 | /// 54 | /// The y-coordinates of the mesh edges middle points 55 | /// 56 | public IntPtr edge_y; 57 | 58 | /// 59 | /// The x-coordinates of the mesh faces mass centers 60 | /// 61 | public IntPtr face_x; 62 | 63 | /// 64 | /// The y-coordinates of the mesh faces mass centers 65 | /// 66 | public IntPtr face_y; 67 | 68 | /// 69 | /// The number of mesh nodes 70 | /// 71 | public int num_nodes; 72 | 73 | /// 74 | /// The number of valid mesh nodes 75 | /// 76 | public int num_valid_nodes; 77 | 78 | /// 79 | /// The number of edges 80 | /// 81 | public int num_edges; 82 | 83 | /// 84 | /// The number of valid edges 85 | /// 86 | public int num_valid_edges; 87 | 88 | /// 89 | /// The number of faces 90 | /// 91 | public int num_faces; 92 | 93 | /// 94 | /// The total number of nodes composing the mesh 2d faces 95 | /// 96 | public int num_face_nodes; 97 | } 98 | } -------------------------------------------------------------------------------- /test/MeshKernelNETTest/Api/DisposableGeometryListTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MeshKernelNET.Api; 3 | using MeshKernelNET.Native; 4 | using NUnit.Framework; 5 | 6 | namespace MeshKernelNETTest.Api 7 | { 8 | [TestFixture] 9 | public class DisposableGeometryListTest 10 | { 11 | [Test] 12 | public void WhenCallingCreateGeometryListNative_AGeometryListNativeShouldBeCreated() 13 | { 14 | //Arrange 15 | var list = new DisposableGeometryList 16 | { 17 | XCoordinates = new[] { 0.0, 1.0, 2.0, -999.0, 0.0, 1.0, 2.0 }, 18 | YCoordinates = new[] { 0.0, 1.0, 2.0, -999.0, 0.0, 1.0, 2.0 }, 19 | Values = new[] { 0.0, 1.0, 2.0, -999.0, 0.0, 1.0, 2.0 }, 20 | NumberOfCoordinates = 7, 21 | GeometrySeparator = -999.0 22 | }; 23 | 24 | using (list) 25 | { 26 | // Act 27 | GeometryListNative nativeGeometryList = list.CreateNativeObject(); 28 | 29 | // Assert 30 | 31 | Assert.That(nativeGeometryList.numberOfCoordinates, Is.EqualTo(list.NumberOfCoordinates)); 32 | Assert.That(nativeGeometryList.xCoordinates, Is.Not.EqualTo(IntPtr.Zero)); 33 | Assert.That(nativeGeometryList.yCoordinates, Is.Not.EqualTo(IntPtr.Zero)); 34 | Assert.That(nativeGeometryList.zCoordinates, Is.Not.EqualTo(IntPtr.Zero)); 35 | } 36 | } 37 | 38 | [Test] 39 | public void UpdateFromNativeObjectShouldPopulateGeometryList() 40 | { 41 | // Arrange 42 | var managedList = new DisposableGeometryList(); 43 | var nativeList = new DisposableGeometryList 44 | { 45 | XCoordinates = new[] { 1.0, 2.0, 3.0 }, 46 | YCoordinates = new[] { 4.0, 5.0, 6.0 }, 47 | Values = new[] { 7.0, 8.0, 9.0 }, 48 | NumberOfCoordinates = 3, 49 | GeometrySeparator = -999.0, 50 | InnerOuterSeparator = -998.0 51 | }; 52 | 53 | using (nativeList) 54 | { 55 | GeometryListNative nativeObject = nativeList.CreateNativeObject(); 56 | 57 | // Act 58 | managedList.UpdateFromNativeObject(ref nativeObject); 59 | 60 | // Assert 61 | Assert.That(managedList.XCoordinates, Is.EqualTo(new[] { 1.0, 2.0, 3.0 })); 62 | Assert.That(managedList.YCoordinates, Is.EqualTo(new[] { 4.0, 5.0, 6.0 })); 63 | Assert.That(managedList.Values, Is.EqualTo(new[] { 7.0, 8.0, 9.0 })); 64 | Assert.That(managedList.NumberOfCoordinates, Is.EqualTo(3)); 65 | Assert.That(managedList.GeometrySeparator, Is.EqualTo(-999.0)); 66 | Assert.That(managedList.InnerOuterSeparator, Is.EqualTo(-998.0)); 67 | } 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/MeshRefinementParameters.cs: -------------------------------------------------------------------------------- 1 | using ProtoBuf; 2 | 3 | namespace MeshKernelNET.Api 4 | { 5 | [ProtoContract(AsReferenceDefault = true)] 6 | public class MeshRefinementParameters 7 | { 8 | public static MeshRefinementParameters CreateDefault() 9 | { 10 | return new MeshRefinementParameters 11 | { 12 | MaxNumRefinementIterations = 10, 13 | RefineIntersected = false, 14 | UseMassCenterWhenRefining = true, 15 | MinEdgeSize = 0.5, 16 | RefinementType = MeshRefinementTypes.RefinementLevels, 17 | ConnectHangingNodes = true, 18 | AccountForSamplesOutside = false, 19 | SmoothingIterations = 5, 20 | MaxCourantTime = 120.0, 21 | DirectionalRefinement = false 22 | }; 23 | } 24 | 25 | /// 26 | /// Maximum number of refinement iterations, set to 1 if only one refinement is wanted (10) 27 | /// 28 | [ProtoMember(1)] 29 | public int MaxNumRefinementIterations { get; set; } 30 | 31 | /// 32 | /// Whether to compute faces intersected by polygon (yes=1/no=0) 33 | /// 34 | [ProtoMember(2)] 35 | public bool RefineIntersected { get; set; } 36 | 37 | /// 38 | /// Whether to use the mass center when splitting a face in the refinement process (yes=1/no=0) 39 | /// 40 | [ProtoMember(3)] 41 | public bool UseMassCenterWhenRefining { get; set; } 42 | 43 | /// 44 | /// Minimum edge size 45 | /// 46 | [ProtoMember(4)] 47 | public double MinEdgeSize { get; set; } 48 | 49 | /// 50 | /// Refinement criterion type 51 | /// 52 | [ProtoMember(5)] 53 | public MeshRefinementTypes RefinementType { get; set; } 54 | 55 | /// 56 | /// Connect hanging nodes at the end of the iteration, 1 yes or 0 no 57 | /// 58 | [ProtoMember(6)] 59 | public bool ConnectHangingNodes { get; set; } 60 | 61 | /// 62 | /// Take samples outside face into account , 1 yes 0 no 63 | /// 64 | [ProtoMember(7)] 65 | public bool AccountForSamplesOutside { get; set; } 66 | 67 | /// 68 | /// The number of smoothing iterations 69 | /// 70 | [ProtoMember(8)] 71 | public int SmoothingIterations { get; set; } 72 | 73 | /// 74 | /// Maximum courant time in seconds 75 | /// 76 | [ProtoMember(9)] 77 | public double MaxCourantTime { get; set; } 78 | 79 | /// 80 | /// Directional refinement, cannot be used when the number of smoothing iterations is larger than 0 81 | /// 82 | [ProtoMember(10)] 83 | public bool DirectionalRefinement { get; set; } 84 | } 85 | } -------------------------------------------------------------------------------- /MeshKernelNET.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31402.337 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MeshKernelNET", "src\MeshKernelNET\MeshKernelNET.csproj", "{BC6B4877-9099-4959-AB56-BE28538FB539}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D13A0EA4-B770-4003-A8E5-6ECEF1591740}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{6E2D9D61-87C3-43CB-954F-D4D6D02BC6D2}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MeshKernelNETTest", "test\MeshKernelNETTest\MeshKernelNETTest.csproj", "{CCABD237-C42C-4A32-8C52-E6B4B0BE6EAA}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution items", "Solution items", "{FB4DB790-09C4-4844-ABE9-4FB1C34119B9}" 15 | ProjectSection(SolutionItems) = preProject 16 | Directory.Build.props = Directory.Build.props 17 | Directory.Packages.props = Directory.Packages.props 18 | EndProjectSection 19 | EndProject 20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MeshKernelNET.Generators", "src\MeshKernelNET.Generators\MeshKernelNET.Generators.csproj", "{C9A9F408-CC49-4E5D-9F53-E92E1B44BC45}" 21 | EndProject 22 | Global 23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 24 | Debug|Any CPU = Debug|Any CPU 25 | Release|Any CPU = Release|Any CPU 26 | EndGlobalSection 27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 28 | {BC6B4877-9099-4959-AB56-BE28538FB539}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {BC6B4877-9099-4959-AB56-BE28538FB539}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {BC6B4877-9099-4959-AB56-BE28538FB539}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {BC6B4877-9099-4959-AB56-BE28538FB539}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {CCABD237-C42C-4A32-8C52-E6B4B0BE6EAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {CCABD237-C42C-4A32-8C52-E6B4B0BE6EAA}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {CCABD237-C42C-4A32-8C52-E6B4B0BE6EAA}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {CCABD237-C42C-4A32-8C52-E6B4B0BE6EAA}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {C9A9F408-CC49-4E5D-9F53-E92E1B44BC45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {C9A9F408-CC49-4E5D-9F53-E92E1B44BC45}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {C9A9F408-CC49-4E5D-9F53-E92E1B44BC45}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {C9A9F408-CC49-4E5D-9F53-E92E1B44BC45}.Release|Any CPU.Build.0 = Release|Any CPU 40 | EndGlobalSection 41 | GlobalSection(SolutionProperties) = preSolution 42 | HideSolutionNode = FALSE 43 | EndGlobalSection 44 | GlobalSection(NestedProjects) = preSolution 45 | {BC6B4877-9099-4959-AB56-BE28538FB539} = {D13A0EA4-B770-4003-A8E5-6ECEF1591740} 46 | {CCABD237-C42C-4A32-8C52-E6B4B0BE6EAA} = {6E2D9D61-87C3-43CB-954F-D4D6D02BC6D2} 47 | {C9A9F408-CC49-4E5D-9F53-E92E1B44BC45} = {D13A0EA4-B770-4003-A8E5-6ECEF1591740} 48 | EndGlobalSection 49 | GlobalSection(ExtensibilityGlobals) = postSolution 50 | SolutionGuid = {95422719-9EC8-468E-A6B3-42F5DDA562E8} 51 | EndGlobalSection 52 | EndGlobal 53 | -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/SplinesToCurvilinearParameters.cs: -------------------------------------------------------------------------------- 1 | using ProtoBuf; 2 | 3 | namespace MeshKernelNET.Api 4 | { 5 | [ProtoContract(AsReferenceDefault = true)] 6 | public class SplinesToCurvilinearParameters 7 | { 8 | public static SplinesToCurvilinearParameters CreateDefault() 9 | { 10 | return new SplinesToCurvilinearParameters 11 | { 12 | AspectRatio = 0.1, 13 | AspectRatioGrowFactor = 1.1, 14 | AverageWidth = 500.0, 15 | CurvatureAdaptedGridSpacing = true, 16 | GrowGridOutside = true, 17 | MaximumNumberOfGridCellsInTheUniformPart = 5, 18 | GridsOnTopOfEachOtherTolerance = 0.0001, 19 | MinimumCosineOfCrossingAngles = 0.95, 20 | CheckFrontCollisions = false, 21 | UniformGridSize = 0.0, 22 | RemoveSkinnyTriangles = true, 23 | }; 24 | } 25 | 26 | /// 27 | /// * Aspect ratio (0.1) 28 | /// 29 | [ProtoMember(1)] 30 | public double AspectRatio { get; set; } 31 | 32 | /// 33 | /// * Grow factor of aspect ratio (1.1) 34 | /// 35 | [ProtoMember(2)] 36 | public double AspectRatioGrowFactor { get; set; } 37 | 38 | /// 39 | /// * Average mesh width on center spline (0.005) 40 | /// 41 | [ProtoMember(3)] 42 | public double AverageWidth { get; set; } 43 | 44 | /// 45 | /// * Curvature adapted grid spacing, 1 or not 0 (1) 46 | /// 47 | [ProtoMember(4)] 48 | public bool CurvatureAdaptedGridSpacing { get; set; } 49 | 50 | /// 51 | /// * Grow the grid outside the prescribed grid height (1) 52 | /// 53 | [ProtoMember(5)] 54 | public bool GrowGridOutside { get; set; } 55 | 56 | /// 57 | /// * Maximum number of layers in the uniform part (5) 58 | /// 59 | [ProtoMember(6)] 60 | public int MaximumNumberOfGridCellsInTheUniformPart { get; set; } 61 | 62 | /// 63 | /// * On-top-of-each-other tolerance (0.0001) 64 | /// 65 | [ProtoMember(7)] 66 | public double GridsOnTopOfEachOtherTolerance { get; set; } 67 | 68 | /// 69 | /// * Minimum allowed absolute value of crossing-angle cosine (0.95) 70 | /// 71 | [ProtoMember(8)] 72 | public double MinimumCosineOfCrossingAngles { get; set; } 73 | 74 | /// 75 | /// * Check for collisions with other parts of the front, 1 or not 0 (0) 76 | /// 77 | [ProtoMember(9)] 78 | public bool CheckFrontCollisions { get; set; } 79 | 80 | /// 81 | /// * Uniform grid size, netboundary to grid only (0.0) 82 | /// 83 | [ProtoMember(10)] 84 | public double UniformGridSize { get; set; } 85 | 86 | /// 87 | /// * Remove skinny triangles (1) 88 | /// 89 | [ProtoMember(11)] 90 | public bool RemoveSkinnyTriangles { get; set; } 91 | } 92 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Helpers/NativeLibrary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.IO; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | 7 | namespace MeshKernelNET.Helpers 8 | { 9 | /// 10 | /// Helper functions for loading native libraries 11 | /// 12 | internal static class NativeLibrary 13 | { 14 | [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)] 15 | private static extern IntPtr LoadLibrary(string lpFileName); 16 | 17 | /// 18 | /// Loads the provided native dll ( and ) 19 | /// for the current process. 20 | /// 21 | /// 22 | /// Call this from a static constructor in a class that has DllImport external methods. This 23 | /// method uses LoadLibrary to load the correct dll for the current process (32bit or 64bit) 24 | /// before DllImport has the chance to resolve the external calls. As long as the dll name is 25 | /// the same this works. 26 | /// 27 | /// The dll file to load. 28 | /// The directory to load the dll from. 29 | public static void LoadNativeDll(string dllFileName, string directory) 30 | { 31 | using (new SwitchDllSearchDirectoryHelper(directory)) 32 | { 33 | // attempt to load the library 34 | IntPtr ptr = LoadLibrary(dllFileName); 35 | if (ptr != IntPtr.Zero) 36 | { 37 | return; 38 | } 39 | 40 | int error = Marshal.GetLastWin32Error(); 41 | var exception = new Win32Exception(error); 42 | 43 | var messageCouldNotFind = $"Could not find / load {dllFileName}"; 44 | var messageError = $"Error: {error} - {exception.Message}"; 45 | var messageFile = $"{"File"}: {directory}\\{dllFileName}"; 46 | 47 | throw new FileNotFoundException(string.Join(Environment.NewLine, messageCouldNotFind, messageError, messageFile)); 48 | } 49 | } 50 | 51 | private sealed class SwitchDllSearchDirectoryHelper : IDisposable 52 | { 53 | [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] 54 | private static extern int GetDllDirectory(int nBufferLength, StringBuilder lpPathName); 55 | 56 | [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] 57 | private static extern void SetDllDirectory(string lpPathName); 58 | 59 | private readonly string oldDirectory; 60 | 61 | public SwitchDllSearchDirectoryHelper(string dllDirectory) 62 | { 63 | oldDirectory = GetDllDirectory(); 64 | SetDllDirectory(dllDirectory); 65 | } 66 | 67 | public void Dispose() 68 | { 69 | SetDllDirectory(oldDirectory); 70 | } 71 | 72 | private static string GetDllDirectory() 73 | { 74 | var tmp = new StringBuilder(4096); 75 | GetDllDirectory(4096, tmp); 76 | return tmp.ToString(); 77 | } 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /src/MeshKernelNET.Generators/RemoteApiGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using System.Linq; 3 | using System.Text; 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.CSharp.Syntax; 6 | 7 | namespace MeshKernelNET.Generators; 8 | 9 | [Generator] 10 | [ExcludeFromCodeCoverage] 11 | public class RemoteApiGenerator : IIncrementalGenerator 12 | { 13 | private const string interfaceName = "IMeshKernelApi"; 14 | private const string implementationName = "RemoteMeshKernelApi"; 15 | 16 | private static readonly SymbolDisplayFormat typeNameOnlyFormat = new( 17 | typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameOnly, 18 | genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, 19 | miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes 20 | ); 21 | 22 | public void Initialize(IncrementalGeneratorInitializationContext context) 23 | { 24 | var interfaceDeclaration = context.SyntaxProvider.CreateSyntaxProvider( 25 | predicate: (node, _) => node is InterfaceDeclarationSyntax { Identifier.Text: interfaceName }, 26 | transform: (ctx, _) => (InterfaceDeclaration: (InterfaceDeclarationSyntax)ctx.Node, ctx.SemanticModel) 27 | ); 28 | 29 | context.RegisterSourceOutput(interfaceDeclaration, (spc, data) => 30 | { 31 | var (interfaceSyntax, semanticModel) = data; 32 | 33 | if (semanticModel.GetDeclaredSymbol(interfaceSyntax) is not INamedTypeSymbol typeSymbol) 34 | return; 35 | 36 | var sb = new StringBuilder(); 37 | 38 | sb.AppendLine("using System;"); 39 | sb.AppendLine("using System.Diagnostics.CodeAnalysis;"); 40 | sb.AppendLine(); 41 | sb.AppendLine($"namespace {typeSymbol.ContainingNamespace}"); 42 | sb.AppendLine("{"); 43 | sb.AppendLine(" /// "); 44 | sb.AppendLine($" /// Remote wrapper for that executes operations in a separate process."); 45 | sb.AppendLine(" /// "); 46 | sb.AppendLine(" [ExcludeFromCodeCoverage]"); 47 | sb.AppendLine($" public sealed partial class {implementationName}"); 48 | sb.AppendLine(" {"); 49 | 50 | foreach (IMethodSymbol method in typeSymbol.GetMembers().OfType()) 51 | { 52 | if (method.MethodKind != MethodKind.Ordinary) 53 | continue; 54 | 55 | string methodName = method.Name; 56 | string returnType = method.ReturnType.ToDisplayString(typeNameOnlyFormat); 57 | 58 | string parameters = string.Join(", ", method.Parameters.Select(p => 59 | $"{GetRefModifier(p.RefKind)}{p.Type.ToDisplayString(typeNameOnlyFormat)} {p.Name}")); 60 | 61 | string arguments = string.Join(", ", method.Parameters.Select(p => 62 | $"{GetRefModifier(p.RefKind)}{p.Name}")); 63 | 64 | sb.AppendLine(" /// "); 65 | sb.AppendLine($" public {returnType} {methodName}({parameters}) => api.{methodName}({arguments});"); 66 | } 67 | 68 | sb.AppendLine(" }"); 69 | sb.AppendLine("}"); 70 | 71 | spc.AddSource($"{implementationName}.g.cs", sb.ToString()); 72 | }); 73 | } 74 | 75 | private static string GetRefModifier(RefKind refKind) => 76 | refKind == RefKind.None ? "" : refKind.ToString().ToLower() + " "; 77 | } 78 | -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/DisposableMesh1D.cs: -------------------------------------------------------------------------------- 1 | using MeshKernelNET.Helpers; 2 | using MeshKernelNET.Native; 3 | using ProtoBuf; 4 | 5 | namespace MeshKernelNET.Api 6 | { 7 | [ProtoContract(AsReferenceDefault = true)] 8 | public sealed class DisposableMesh1D : DisposableNativeObject 9 | { 10 | [ProtoMember(1)] 11 | private int[] edgeNodes; 12 | 13 | [ProtoMember(2)] 14 | private double[] nodeX; 15 | 16 | [ProtoMember(3)] 17 | private double[] nodeY; 18 | 19 | [ProtoMember(4)] 20 | private int numNodes; 21 | 22 | [ProtoMember(5)] 23 | private int numValidNodes; 24 | 25 | [ProtoMember(6)] 26 | private int numEdges; 27 | 28 | [ProtoMember(7)] 29 | private int numValidEdges; 30 | 31 | public DisposableMesh1D() 32 | { 33 | } 34 | 35 | public DisposableMesh1D(int nNodes, int nEdges) 36 | { 37 | NumNodes = nNodes; 38 | NumEdges = nEdges; 39 | 40 | EdgeNodes = new int[NumEdges * 2]; 41 | NodeX = new double[NumNodes]; 42 | NodeY = new double[NumNodes]; 43 | } 44 | 45 | ~DisposableMesh1D() 46 | { 47 | Dispose(false); 48 | } 49 | 50 | public int[] EdgeNodes 51 | { 52 | get { return edgeNodes; } 53 | set { edgeNodes = value; } 54 | } 55 | 56 | public double[] NodeX 57 | { 58 | get { return nodeX; } 59 | set { nodeX = value; } 60 | } 61 | 62 | public double[] NodeY 63 | { 64 | get { return nodeY; } 65 | set { nodeY = value; } 66 | } 67 | 68 | public int NumNodes 69 | { 70 | get { return numNodes; } 71 | set { numNodes = value; } 72 | } 73 | 74 | public int NumValidNodes 75 | { 76 | get { return numValidNodes; } 77 | set { numValidNodes = value; } 78 | } 79 | 80 | public int NumEdges 81 | { 82 | get { return numEdges; } 83 | set { numEdges = value; } 84 | } 85 | 86 | public int NumValidEdges 87 | { 88 | get { return numValidEdges; } 89 | set { numValidEdges = value; } 90 | } 91 | 92 | protected override void SetNativeObject(ref Mesh1DNative nativeObject) 93 | { 94 | nativeObject.edge_nodes = GetPinnedObjectPointer(EdgeNodes); 95 | nativeObject.node_x = GetPinnedObjectPointer(NodeX); 96 | nativeObject.node_y = GetPinnedObjectPointer(NodeY); 97 | nativeObject.num_valid_nodes = numValidNodes; 98 | nativeObject.num_nodes = NumNodes; 99 | nativeObject.num_edges = NumEdges; 100 | nativeObject.num_valid_edges = numValidEdges; 101 | } 102 | 103 | public void UpdateFromNativeObject(ref Mesh1DNative nativeObject) 104 | { 105 | EdgeNodes = nativeObject.edge_nodes.CreateValueArray(nativeObject.num_edges * 2); 106 | NodeX = nativeObject.node_x.CreateValueArray(nativeObject.num_nodes); 107 | NodeY = nativeObject.node_y.CreateValueArray(nativeObject.num_nodes); 108 | NumValidNodes = nativeObject.num_valid_nodes; 109 | NumNodes = nativeObject.num_nodes; 110 | NumEdges = nativeObject.num_edges; 111 | NumValidEdges = nativeObject.num_valid_edges; 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /test/MeshKernelNETTest/Api/MeshKernelNetwork1dTest.cs: -------------------------------------------------------------------------------- 1 | using MeshKernelNET.Api; 2 | using NUnit.Framework; 3 | 4 | namespace MeshKernelNETTest.Api 5 | { 6 | [TestFixture] 7 | public class MeshKernelNETNetwork1DTests 8 | { 9 | [Test] 10 | public void Network1dComputeFixedChainagesThroughAPI() 11 | { 12 | // Setup 13 | using (var api = new MeshKernelApi()) 14 | using (var network1d = new DisposableGeometryList()) 15 | { 16 | var id = 0; 17 | var mesh1D = new DisposableMesh1D(); 18 | try 19 | { 20 | id = api.AllocateState(0); 21 | 22 | double separator = api.GetSeparator(); 23 | network1d.XCoordinates = new[] { 0.0, 10.0, 20.0, separator, 10.0, 10.0, 10.0 }; 24 | network1d.YCoordinates = new[] { 0.0, 0.0, 0.0, separator, -10.0, 0.0, 10.0 }; 25 | network1d.GeometrySeparator = separator; 26 | network1d.NumberOfCoordinates = 7; 27 | 28 | Assert.That(api.Network1dSet(id, network1d), Is.EqualTo(0)); 29 | 30 | double[] fixedChainages = { 5.0, separator, 5.0 }; 31 | var minFaceSize = 0.01; 32 | var fixedChainagesOffset = 10.0; 33 | 34 | Assert.That(api.Network1dComputeFixedChainages(id, fixedChainages, minFaceSize, fixedChainagesOffset), Is.EqualTo(0)); 35 | Assert.That(api.Network1dToMesh1d(id, minFaceSize), Is.EqualTo(0)); 36 | 37 | Assert.That(api.Mesh1dGetData(id, out mesh1D), Is.EqualTo(0)); 38 | 39 | Assert.That(mesh1D.NumNodes, Is.EqualTo(6)); 40 | Assert.That(mesh1D.NumEdges, Is.EqualTo(4)); 41 | } 42 | finally 43 | { 44 | api.ClearState(); 45 | mesh1D.Dispose(); 46 | } 47 | } 48 | } 49 | 50 | [Test] 51 | public void Network1dComputeOffsettedChainagesThroughAPI() 52 | { 53 | // Setup 54 | using (var api = new MeshKernelApi()) 55 | using (var network1d = new DisposableGeometryList()) 56 | { 57 | var id = 0; 58 | var mesh1D = new DisposableMesh1D(); 59 | try 60 | { 61 | id = api.AllocateState(0); 62 | 63 | double separator = api.GetSeparator(); 64 | network1d.XCoordinates = new[] { 0.0, 10.0, 20.0, separator, 10.0, 10.0, 10.0 }; 65 | network1d.YCoordinates = new[] { 0.0, 0.0, 0.0, separator, -10.0, 0.0, 10.0 }; 66 | network1d.GeometrySeparator = separator; 67 | network1d.NumberOfCoordinates = 7; 68 | 69 | Assert.That(api.Network1dSet(id, network1d), Is.EqualTo(0)); 70 | 71 | var minFaceSize = 0.01; 72 | var fixedChainagesOffset = 1.0; 73 | 74 | Assert.That(api.Network1dComputeOffsettedChainages(id, fixedChainagesOffset), Is.EqualTo(0)); 75 | Assert.That(api.Network1dToMesh1d(id, minFaceSize), Is.EqualTo(0)); 76 | Assert.That(api.Mesh1dGetData(id, out mesh1D), Is.EqualTo(0)); 77 | 78 | Assert.That(mesh1D.NumEdges, Is.EqualTo(40)); 79 | Assert.That(mesh1D.NumValidNodes, Is.EqualTo(41)); 80 | } 81 | finally 82 | { 83 | api.ClearState(); 84 | mesh1D.Dispose(); 85 | } 86 | } 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /test/MeshKernelNETTest/Api/DisposableMesh2DTest.cs: -------------------------------------------------------------------------------- 1 | using MeshKernelNET.Api; 2 | using MeshKernelNET.Native; 3 | using NUnit.Framework; 4 | 5 | namespace MeshKernelNETTest.Api 6 | { 7 | [TestFixture] 8 | public class DisposableMesh2DTest 9 | { 10 | [Test] 11 | public void UpdateFromNativeObjectShouldPopulateMesh2D() 12 | { 13 | // Arrange 14 | var managedMesh = new DisposableMesh2D(); 15 | var nativeMesh = new DisposableMesh2D(4, 4, 1, 4, 4, 4) 16 | { 17 | NodeX = new[] { 0.0, 1.0, 1.0, 0.0 }, 18 | NodeY = new[] { 0.0, 0.0, 1.0, 1.0 }, 19 | EdgeNodes = new[] { 0, 1, 1, 2, 2, 3, 3, 0 }, 20 | EdgeX = new[] { 0.5, 1.0, 0.5, 0.0 }, 21 | EdgeY = new[] { 0.0, 0.5, 1.0, 0.5 }, 22 | FaceX = new[] { 0.5 }, 23 | FaceY = new[] { 0.5 }, 24 | FaceNodes = new[] { 0, 1, 2, 3 }, 25 | NodesPerFace = new[] { 4 }, 26 | NumValidNodes = 4, 27 | NumValidEdges = 4 28 | }; 29 | 30 | using (nativeMesh) 31 | { 32 | Mesh2DNative nativeObject = nativeMesh.CreateNativeObject(); 33 | 34 | // Act 35 | managedMesh.UpdateFromNativeObject(ref nativeObject, true); 36 | 37 | // Assert 38 | Assert.That(managedMesh.NodeX, Is.EqualTo(new[] { 0.0, 1.0, 1.0, 0.0 })); 39 | Assert.That(managedMesh.NodeY, Is.EqualTo(new[] { 0.0, 0.0, 1.0, 1.0 })); 40 | Assert.That(managedMesh.EdgeNodes, Is.EqualTo(new[] { 0, 1, 1, 2, 2, 3, 3, 0 })); 41 | Assert.That(managedMesh.FaceX, Is.EqualTo(new[] { 0.5 })); 42 | Assert.That(managedMesh.FaceY, Is.EqualTo(new[] { 0.5 })); 43 | Assert.That(managedMesh.FaceNodes, Is.EqualTo(new[] { 0, 1, 2, 3 })); 44 | Assert.That(managedMesh.NodesPerFace, Is.EqualTo(new[] { 4 })); 45 | Assert.That(managedMesh.NumNodes, Is.EqualTo(4)); 46 | Assert.That(managedMesh.NumEdges, Is.EqualTo(4)); 47 | Assert.That(managedMesh.NumFaces, Is.EqualTo(1)); 48 | Assert.That(managedMesh.NumValidNodes, Is.EqualTo(4)); 49 | Assert.That(managedMesh.NumValidEdges, Is.EqualTo(4)); 50 | } 51 | } 52 | 53 | [Test] 54 | public void UpdateFromNativeObjectWithoutCellInformationShouldNotPopulateFaceData() 55 | { 56 | // Arrange 57 | var managedMesh = new DisposableMesh2D(); 58 | var nativeMesh = new DisposableMesh2D(4, 4, 1, 4, 4, 4) 59 | { 60 | NodeX = new[] { 0.0, 1.0, 1.0, 0.0 }, 61 | NodeY = new[] { 0.0, 0.0, 1.0, 1.0 }, 62 | EdgeNodes = new[] { 0, 1, 1, 2, 2, 3, 3, 0 }, 63 | EdgeX = new[] { 0.5, 1.0, 0.5, 0.0 }, 64 | EdgeY = new[] { 0.0, 0.5, 1.0, 0.5 }, 65 | FaceX = new[] { 0.5 }, 66 | FaceY = new[] { 0.5 }, 67 | FaceNodes = new[] { 0, 1, 2, 3 }, 68 | NodesPerFace = new[] { 4 }, 69 | NumValidNodes = 4, 70 | NumValidEdges = 4 71 | }; 72 | 73 | using (nativeMesh) 74 | { 75 | Mesh2DNative nativeObject = nativeMesh.CreateNativeObject(); 76 | 77 | // Act 78 | managedMesh.UpdateFromNativeObject(ref nativeObject, false); 79 | 80 | // Assert 81 | Assert.That(managedMesh.NodeX, Is.EqualTo(new[] { 0.0, 1.0, 1.0, 0.0 })); 82 | Assert.That(managedMesh.NodeY, Is.EqualTo(new[] { 0.0, 0.0, 1.0, 1.0 })); 83 | Assert.That(managedMesh.EdgeNodes, Is.EqualTo(new[] { 0, 1, 1, 2, 2, 3, 3, 0 })); 84 | Assert.That(managedMesh.NumNodes, Is.EqualTo(4)); 85 | Assert.That(managedMesh.NumEdges, Is.EqualTo(4)); 86 | Assert.That(managedMesh.FaceNodes, Is.Null); 87 | Assert.That(managedMesh.NodesPerFace, Is.Null); 88 | } 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/DisposableGeometryList.cs: -------------------------------------------------------------------------------- 1 | using MeshKernelNET.Helpers; 2 | using MeshKernelNET.Native; 3 | using ProtoBuf; 4 | 5 | namespace MeshKernelNET.Api 6 | { 7 | [ProtoContract(AsReferenceDefault = true)] 8 | public sealed class DisposableGeometryList : DisposableNativeObject 9 | { 10 | [ProtoMember(1)] 11 | private double geometrySeparator; 12 | 13 | [ProtoMember(2)] 14 | private double innerOuterSeparator; 15 | 16 | [ProtoMember(3)] 17 | private int numberOfCoordinates; 18 | 19 | [ProtoMember(4)] 20 | private double[] xCoordinates; 21 | 22 | [ProtoMember(5)] 23 | private double[] yCoordinates; 24 | 25 | [ProtoMember(6)] 26 | private double[] values; 27 | 28 | public DisposableGeometryList() 29 | { 30 | } 31 | 32 | public DisposableGeometryList(int numberOfCoordinates) 33 | { 34 | NumberOfCoordinates = numberOfCoordinates; 35 | XCoordinates = new double[numberOfCoordinates]; 36 | YCoordinates = new double[numberOfCoordinates]; 37 | Values = new double[numberOfCoordinates]; 38 | } 39 | 40 | ~DisposableGeometryList() 41 | { 42 | Dispose(false); 43 | } 44 | 45 | /// 46 | /// Separator used for separating multiple geometries 47 | /// 48 | public double GeometrySeparator 49 | { 50 | get { return geometrySeparator; } 51 | set { geometrySeparator = value; } 52 | } 53 | 54 | /// 55 | /// Separator used for separating the outer and inner rings of a polygon geometry 56 | /// 57 | public double InnerOuterSeparator 58 | { 59 | get { return innerOuterSeparator; } 60 | set { innerOuterSeparator = value; } 61 | } 62 | 63 | /// 64 | /// Total number of coordinates (including separators) 65 | /// 66 | public int NumberOfCoordinates 67 | { 68 | get { return numberOfCoordinates; } 69 | set { numberOfCoordinates = value; } 70 | } 71 | 72 | /// 73 | /// The x coordinates of the geometries 74 | /// 75 | public double[] XCoordinates 76 | { 77 | get { return xCoordinates; } 78 | set { xCoordinates = value; } 79 | } 80 | 81 | /// 82 | /// The y coordinates of the geometries 83 | /// 84 | public double[] YCoordinates 85 | { 86 | get { return yCoordinates; } 87 | set { yCoordinates = value; } 88 | } 89 | 90 | /// 91 | /// The values for the coordinates of the geometries 92 | /// 93 | public double[] Values 94 | { 95 | get { return values; } 96 | set { values = value; } 97 | } 98 | 99 | protected override void SetNativeObject(ref GeometryListNative nativeObject) 100 | { 101 | nativeObject.xCoordinates = GetPinnedObjectPointer(XCoordinates); 102 | nativeObject.yCoordinates = GetPinnedObjectPointer(YCoordinates); 103 | nativeObject.zCoordinates = GetPinnedObjectPointer(Values); 104 | nativeObject.numberOfCoordinates = NumberOfCoordinates; 105 | nativeObject.geometrySeperator = GeometrySeparator; 106 | nativeObject.innerOuterSeperator = InnerOuterSeparator; 107 | } 108 | 109 | public void UpdateFromNativeObject(ref GeometryListNative nativeObject) 110 | { 111 | XCoordinates = nativeObject.xCoordinates.CreateValueArray(nativeObject.numberOfCoordinates); 112 | YCoordinates = nativeObject.yCoordinates.CreateValueArray(nativeObject.numberOfCoordinates); 113 | Values = nativeObject.zCoordinates.CreateValueArray(nativeObject.numberOfCoordinates); 114 | NumberOfCoordinates = nativeObject.numberOfCoordinates; 115 | GeometrySeparator = nativeObject.geometrySeperator; 116 | InnerOuterSeparator = nativeObject.innerOuterSeperator; 117 | } 118 | } 119 | } -------------------------------------------------------------------------------- /test/MeshKernelNETTest/Api/MeshKernelTestUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Globalization; 5 | using System.Linq; 6 | using MeshKernelNET.Api; 7 | 8 | namespace MeshKernelNETTest.Api 9 | { 10 | public static class TestUtilityFunctions 11 | { 12 | public static DisposableMesh2D CreateMesh2D( 13 | int numbOfNodesHorizontal, 14 | int numbOfNodesVertical, 15 | double cellWidth, 16 | double cellHeight, 17 | double xOffset = 0.0, 18 | double yOffset = 0.0) 19 | { 20 | var result = new DisposableMesh2D(); 21 | 22 | var indicesValues = new int[numbOfNodesVertical, numbOfNodesHorizontal]; 23 | result.NodeX = new double[numbOfNodesHorizontal * numbOfNodesVertical]; 24 | result.NodeY = new double[numbOfNodesHorizontal * numbOfNodesVertical]; 25 | result.NumNodes = numbOfNodesHorizontal * numbOfNodesVertical; 26 | 27 | var nodeIndex = 0; 28 | for (var v = 0; v < numbOfNodesVertical; ++v) 29 | { 30 | for (var h = 0; h < numbOfNodesHorizontal; ++h) 31 | { 32 | indicesValues[v, h] = (v * numbOfNodesHorizontal) + h; 33 | result.NodeX[nodeIndex] = xOffset + (h * cellWidth); 34 | result.NodeY[nodeIndex] = yOffset + (v * cellHeight); 35 | nodeIndex++; 36 | } 37 | } 38 | 39 | result.NumEdges = ((numbOfNodesHorizontal - 1) * numbOfNodesVertical) + 40 | (numbOfNodesHorizontal * (numbOfNodesVertical - 1)); 41 | result.EdgeNodes = new int[result.NumEdges * 2]; 42 | var edgeIndex = 0; 43 | for (var v = 0; v < numbOfNodesVertical - 1; ++v) 44 | { 45 | for (var h = 0; h < numbOfNodesHorizontal; ++h) 46 | { 47 | result.EdgeNodes[edgeIndex] = indicesValues[v,h]; 48 | edgeIndex++; 49 | result.EdgeNodes[edgeIndex] = indicesValues[v + 1, h]; 50 | edgeIndex++; 51 | } 52 | } 53 | 54 | for (var v = 0; v < numbOfNodesVertical; ++v) 55 | { 56 | for (var h = 0; h < numbOfNodesHorizontal - 1; ++h) 57 | { 58 | result.EdgeNodes[edgeIndex] = indicesValues[v, h]; 59 | edgeIndex++; 60 | result.EdgeNodes[edgeIndex] = indicesValues[v, h + 1]; 61 | edgeIndex++; 62 | } 63 | } 64 | 65 | return result; 66 | } 67 | 68 | public static DisposableCurvilinearGrid CreateCurvilinearGrid( 69 | int numbOfRows = 3, 70 | int numbOfColumns = 3, 71 | double cellWidth = 1.0, 72 | double cellHeight = 1.0) 73 | { 74 | var result = new DisposableCurvilinearGrid(numbOfRows, numbOfColumns); 75 | 76 | var nodeIndex = 0; 77 | for (var v = 0; v < result.NumN; ++v) 78 | { 79 | for (var h = 0; h < result.NumM; ++h) 80 | { 81 | result.NodeX[nodeIndex] = h * cellWidth; 82 | result.NodeY[nodeIndex] = v * cellHeight; 83 | nodeIndex++; 84 | } 85 | } 86 | 87 | return result; 88 | } 89 | 90 | public static void GetTiming(Stopwatch stopwatch, string actionName, Action action) 91 | { 92 | stopwatch.Restart(); 93 | 94 | action(); 95 | 96 | stopwatch.Stop(); 97 | Console.WriteLine($"{stopwatch.Elapsed} -- {actionName}"); 98 | } 99 | 100 | public static string AsString(IEnumerable sequence) 101 | { 102 | return "[" + string.Join(",", sequence.Select(x => 103 | { 104 | if (x is IFormattable formattable) 105 | { 106 | return formattable.ToString(null, CultureInfo.InvariantCulture); 107 | } 108 | return x?.ToString(); 109 | })) + "]"; 110 | } 111 | 112 | } 113 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/DisposableCurvilinearGrid.cs: -------------------------------------------------------------------------------- 1 | using MeshKernelNET.Helpers; 2 | using MeshKernelNET.Native; 3 | using ProtoBuf; 4 | 5 | namespace MeshKernelNET.Api 6 | { 7 | [ProtoContract(AsReferenceDefault = true)] 8 | public sealed class DisposableCurvilinearGrid : DisposableNativeObject, IReadOnly2DMesh 9 | { 10 | [ProtoMember(1)] 11 | private double[] nodeX; 12 | 13 | [ProtoMember(2)] 14 | private double[] nodeY; 15 | 16 | [ProtoMember(3)] 17 | private int numM; 18 | 19 | [ProtoMember(4)] 20 | private int numN; 21 | 22 | public DisposableCurvilinearGrid() 23 | { 24 | } 25 | 26 | public DisposableCurvilinearGrid(int nN, int nM) 27 | { 28 | numM = nM; 29 | numN = nN; 30 | int NumNodes = numM * numN; 31 | 32 | NodeX = new double[NumNodes]; 33 | NodeY = new double[NumNodes]; 34 | } 35 | 36 | ~DisposableCurvilinearGrid() 37 | { 38 | Dispose(false); 39 | } 40 | 41 | public double[] NodeX 42 | { 43 | get { return nodeX; } 44 | set { nodeX = value; } 45 | } 46 | 47 | public double[] NodeY 48 | { 49 | get { return nodeY; } 50 | set { nodeY = value; } 51 | } 52 | 53 | public int NumM 54 | { 55 | get { return numM; } 56 | set { numM = value; } 57 | } 58 | 59 | public int NumN 60 | { 61 | get { return numN; } 62 | set { numN = value; } 63 | } 64 | 65 | #region IReadOnly2DMesh 66 | /// 67 | public int CellCount() 68 | { 69 | return (NumM - 1) * (NumN-1); 70 | } 71 | 72 | /// 73 | /// Note that there is no trivial relationship between cell index and edge indexes 74 | public int GetCellEdgeCount(int cellIndex) 75 | { 76 | return 4; 77 | } 78 | 79 | /// 80 | public int EdgeCount() 81 | { 82 | return (NumM-1)*NumN + // row-oriented edges 83 | NumM*(NumN - 1); // column-oriented edges 84 | } 85 | 86 | /// The edge numbering is equal to CurvilinearGrid::ConvertCurvilinearToNodesAndEdges. 87 | /// First column-oriented edges, then row-oriented edges, in row major order 88 | private (int first, int last) GetEdgeNodes(int edgeIndex) 89 | { 90 | int numNodesInRow = NumM; 91 | int numNodesInColumn = NumN; 92 | int numColumnOrientedEdges = numNodesInRow * (numNodesInColumn - 1); 93 | if (edgeIndex < numColumnOrientedEdges) 94 | { 95 | return (edgeIndex, edgeIndex + numNodesInRow); 96 | } 97 | 98 | edgeIndex -= numColumnOrientedEdges; 99 | 100 | int numRowOrientedEdgesInRow = (numNodesInRow - 1); 101 | int row = edgeIndex / numRowOrientedEdgesInRow; 102 | int first = row*numNodesInRow + edgeIndex % numRowOrientedEdgesInRow; 103 | return (first, first + 1); 104 | } 105 | 106 | /// 107 | public int GetFirstNode(int edgeIndex) 108 | { 109 | return GetEdgeNodes(edgeIndex).first; 110 | } 111 | 112 | /// 113 | public int GetLastNode(int edgeIndex) 114 | { 115 | return GetEdgeNodes(edgeIndex).last; 116 | } 117 | 118 | /// 119 | public int NodeCount() 120 | { 121 | return NumM*NumN; 122 | } 123 | 124 | /// 125 | public double GetNodeX(int nodeIndex) 126 | { 127 | return NodeX[nodeIndex]; 128 | } 129 | 130 | /// 131 | public double GetNodeY(int nodeIndex) 132 | { 133 | return NodeY[nodeIndex]; 134 | } 135 | #endregion 136 | 137 | protected override void SetNativeObject(ref CurvilinearGridNative nativeObject) 138 | { 139 | nativeObject.node_x = GetPinnedObjectPointer(NodeX); 140 | nativeObject.node_y = GetPinnedObjectPointer(NodeY); 141 | nativeObject.num_m = numM; 142 | nativeObject.num_n = numN; 143 | } 144 | 145 | public void UpdateFromNativeObject(ref CurvilinearGridNative nativeObject) 146 | { 147 | NumM = nativeObject.num_m; 148 | NumN = nativeObject.num_n; 149 | NodeX = nativeObject.node_x.CreateValueArray(nativeObject.num_m * nativeObject.num_n); 150 | NodeY = nativeObject.node_y.CreateValueArray(nativeObject.num_m * nativeObject.num_n); 151 | } 152 | } 153 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/DisposableNativeObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Runtime.InteropServices; 6 | using MeshKernelNET.Helpers; 7 | 8 | namespace MeshKernelNET.Api 9 | { 10 | /// 11 | /// Base class for disposable mesh objects. Provides pinning of arrays in memory for exchange with native calls. 12 | /// 13 | public abstract class DisposableNativeObject : IDisposable where TNative : new() 14 | { 15 | private readonly Dictionary objectGarbageCollectHandles = new Dictionary(); 16 | private bool disposed; 17 | 18 | /// 19 | /// Disposes the unmanaged resources 20 | /// 21 | ~DisposableNativeObject() 22 | { 23 | Dispose(false); 24 | } 25 | 26 | /// 27 | /// Indicates if arrays are pinned in memory 28 | /// 29 | private bool IsMemoryPinned 30 | { 31 | get { return objectGarbageCollectHandles.Count > 0; } 32 | } 33 | 34 | /// 35 | public void Dispose() 36 | { 37 | Dispose(true); 38 | GC.SuppressFinalize(this); 39 | } 40 | 41 | /// 42 | /// Creates a native structure for this object 43 | /// 44 | /// 45 | public TNative CreateNativeObject() 46 | { 47 | if (!IsMemoryPinned) 48 | { 49 | PinMemory(); 50 | } 51 | 52 | var nativeObject = new TNative(); 53 | SetNativeObject(ref nativeObject); 54 | return nativeObject; 55 | } 56 | 57 | /// 58 | /// Maps the object with current state (used in ) 59 | /// 60 | /// Newly created native object 61 | protected abstract void SetNativeObject(ref TNative nativeObject); 62 | 63 | /// 64 | /// Get the pointer to the pinned object 65 | /// 66 | /// Object to get 67 | /// 68 | protected IntPtr GetPinnedObjectPointer(object objectToLookUp) 69 | { 70 | return objectGarbageCollectHandles[objectToLookUp].AddrOfPinnedObject(); 71 | } 72 | 73 | /// 74 | /// Disposes resources 75 | /// 76 | /// 77 | /// Boolean indicating that the call is called from the dispose method 78 | /// not the destructor 79 | /// 80 | protected virtual void Dispose(bool disposing) 81 | { 82 | if (disposed) 83 | { 84 | return; 85 | } 86 | 87 | ReleaseUnmanagedResources(); 88 | 89 | disposed = true; 90 | } 91 | 92 | /// 93 | /// Pins the arrays in memory (no garbage collect until unpinned (done in dispose)) 94 | /// 95 | private void PinMemory() 96 | { 97 | IEnumerable arrayProperties = GetType().GetProperties().Where(f => f.PropertyType.IsArray); 98 | 99 | // force initialization 100 | foreach (PropertyInfo arrayProperty in arrayProperties) 101 | { 102 | Type elementType = arrayProperty.PropertyType.GetElementType(); 103 | object objectToPin = arrayProperty.GetValue(this); 104 | 105 | if (objectToPin == null) 106 | { 107 | objectToPin = Array.CreateInstance(elementType, 0); 108 | arrayProperty.SetValue(this, objectToPin); 109 | } 110 | 111 | if (elementType == typeof(string)) 112 | { 113 | int bufferSize = GetType().GetBufferSize(arrayProperty.Name); 114 | if (bufferSize == 0) 115 | { 116 | continue; 117 | } 118 | 119 | byte[] bytes = ((string[])objectToPin).GetFlattenedAsciiCodedStringArray(bufferSize); 120 | AddObjectToPin(bytes, objectToPin); 121 | } 122 | else 123 | { 124 | AddObjectToPin(objectToPin); 125 | } 126 | } 127 | } 128 | 129 | private void UnPinMemory() 130 | { 131 | foreach (KeyValuePair valuePair in objectGarbageCollectHandles) 132 | { 133 | valuePair.Value.Free(); 134 | } 135 | 136 | objectGarbageCollectHandles.Clear(); 137 | } 138 | 139 | private void AddObjectToPin(object objectToPin, object lookupObject = null) 140 | { 141 | object key = lookupObject ?? objectToPin; 142 | objectGarbageCollectHandles.Add(key, GCHandle.Alloc(objectToPin, GCHandleType.Pinned)); 143 | } 144 | 145 | private void ReleaseUnmanagedResources() 146 | { 147 | UnPinMemory(); 148 | } 149 | } 150 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/DisposableGriddedSamples.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MeshKernelNET.Native; 3 | using ProtoBuf; 4 | 5 | namespace MeshKernelNET.Api 6 | { 7 | [ProtoContract(AsReferenceDefault = true)] 8 | public sealed class DisposableGriddedSamples : DisposableNativeObject 9 | { 10 | [ProtoMember(1)] 11 | private int numX; 12 | 13 | [ProtoMember(2)] 14 | private int numY; 15 | 16 | [ProtoMember(3)] 17 | private double originX; 18 | 19 | [ProtoMember(4)] 20 | private double originY; 21 | 22 | [ProtoMember(5)] 23 | private double cellSize; 24 | 25 | [ProtoMember(6)] 26 | private double[] coordinatesX; 27 | 28 | [ProtoMember(7)] 29 | private double[] coordinatesY; 30 | 31 | [ProtoMember(8)] 32 | private short[] shortValues; 33 | 34 | [ProtoMember(9)] 35 | private float[] floatValues; 36 | 37 | [ProtoMember(10)] 38 | private int[] intValues; 39 | 40 | [ProtoMember(11)] 41 | private double[] doubleValues; 42 | 43 | [ProtoMember(12)] 44 | private InterpolationType valueType; 45 | 46 | public DisposableGriddedSamples() 47 | { 48 | } 49 | 50 | public DisposableGriddedSamples(int nX, int nY, double orgX, double orgY, double cSize, InterpolationType type) 51 | { 52 | numX = nX; 53 | numY = nY; 54 | originX = orgX; 55 | originY = orgY; 56 | cellSize = cSize; 57 | coordinatesX = new double[numX]; 58 | coordinatesY = new double[numY]; 59 | valueType = type; 60 | 61 | int length = numX * numY; 62 | 63 | switch (type) 64 | { 65 | case InterpolationType.Short: 66 | shortValues = new short[length]; 67 | break; 68 | case InterpolationType.Float: 69 | floatValues = new float[length]; 70 | break; 71 | case InterpolationType.Int: 72 | intValues = new int[length]; 73 | break; 74 | case InterpolationType.Double: 75 | doubleValues = new double[length]; 76 | break; 77 | default: 78 | throw new ArgumentException($"Unsupported interpolation type: {type}", nameof(type)); 79 | } 80 | } 81 | 82 | ~DisposableGriddedSamples() 83 | { 84 | Dispose(false); 85 | } 86 | 87 | public int NumX 88 | { 89 | get => numX; 90 | set => numX = value; 91 | } 92 | 93 | public int NumY 94 | { 95 | get => numY; 96 | set => numY = value; 97 | } 98 | 99 | public double OriginX 100 | { 101 | get => originX; 102 | set => originX = value; 103 | } 104 | 105 | public double OriginY 106 | { 107 | get => originY; 108 | set => originY = value; 109 | } 110 | 111 | public double CellSize 112 | { 113 | get => cellSize; 114 | set => cellSize = value; 115 | } 116 | 117 | public double[] CoordinatesX 118 | { 119 | get => coordinatesX; 120 | set => coordinatesX = value; 121 | } 122 | 123 | public double[] CoordinatesY 124 | { 125 | get => coordinatesY; 126 | set => coordinatesY = value; 127 | } 128 | 129 | public InterpolationType ValueType 130 | { 131 | get => valueType; 132 | set => valueType = value; 133 | } 134 | 135 | public short[] ShortValues 136 | { 137 | get => shortValues; 138 | set => shortValues = value; 139 | } 140 | 141 | public float[] FloatValues 142 | { 143 | get => floatValues; 144 | set => floatValues = value; 145 | } 146 | 147 | public int[] IntValues 148 | { 149 | get => intValues; 150 | set => intValues = value; 151 | } 152 | 153 | public double[] DoubleValues 154 | { 155 | get => doubleValues; 156 | set => doubleValues = value; 157 | } 158 | 159 | protected override void SetNativeObject(ref GriddedSamplesNative nativeObject) 160 | { 161 | nativeObject.num_x = numX; 162 | nativeObject.num_y = numY; 163 | nativeObject.origin_x = originX; 164 | nativeObject.origin_y = originY; 165 | nativeObject.cell_size = cellSize; 166 | nativeObject.coordinates_x = GetPinnedObjectPointer(coordinatesX); 167 | nativeObject.coordinates_y = GetPinnedObjectPointer(coordinatesY); 168 | nativeObject.value_type = (int)valueType; 169 | 170 | switch (valueType) 171 | { 172 | case InterpolationType.Short: 173 | nativeObject.values = GetPinnedObjectPointer(shortValues); 174 | break; 175 | case InterpolationType.Float: 176 | nativeObject.values = GetPinnedObjectPointer(floatValues); 177 | break; 178 | case InterpolationType.Int: 179 | nativeObject.values = GetPinnedObjectPointer(intValues); 180 | break; 181 | case InterpolationType.Double: 182 | nativeObject.values = GetPinnedObjectPointer(doubleValues); 183 | break; 184 | default: 185 | throw new InvalidOperationException($"Unsupported value type: {valueType}"); 186 | } 187 | } 188 | } 189 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/MakeGridParameters.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Runtime.CompilerServices; 3 | using ProtoBuf; 4 | 5 | namespace MeshKernelNET.Api 6 | { 7 | [ProtoContract(AsReferenceDefault = true)] 8 | public class MakeGridParameters : INotifyPropertyChanged 9 | { 10 | private int numberOfRows; 11 | private int numberOfColumns; 12 | private double gridAngle; 13 | private double originXCoordinate; 14 | private double originYCoordinate; 15 | private double xGridBlockSize; 16 | private double yGridBlockSize; 17 | private double upperRightCornerXCoordinate; 18 | private double upperRightCornerYCoordinate; 19 | 20 | private GridTypeOptions gridType; 21 | 22 | public static MakeGridParameters CreateDefault() 23 | { 24 | return new MakeGridParameters 25 | { 26 | GridType = GridTypeOptions.Square, 27 | NumberOfColumns = 3, 28 | NumberOfRows = 3, 29 | GridAngle = 0.0, 30 | OriginXCoordinate = 0.0, 31 | OriginYCoordinate = 0.0, 32 | XGridBlockSize = 10.0, 33 | YGridBlockSize = 10.0, 34 | UpperRightCornerXCoordinate = 0.0, 35 | UpperRightCornerYCoordinate = 0.0 36 | }; 37 | } 38 | 39 | /// 40 | /// * The type of grid to create : square = 0, wieber = 1, hexagonal type 1 = 2, hexagonal type 2 = 3, triangular = 4 (0) 41 | /// 42 | [ProtoMember(1)] 43 | public GridTypeOptions GridType 44 | { 45 | get { return gridType; } 46 | set 47 | { 48 | gridType = value; 49 | OnPropertyChanged(); 50 | } 51 | } 52 | 53 | /// 54 | /// * The number of columns in x direction (0) 55 | /// 56 | [ProtoMember(2)] 57 | public int NumberOfColumns 58 | { 59 | get { return numberOfColumns; } 60 | set 61 | { 62 | numberOfColumns = value; 63 | OnPropertyChanged(); 64 | } 65 | } 66 | 67 | /// 68 | /// * The number of columns in y direction (0) 69 | /// 70 | [ProtoMember(3)] 71 | public int NumberOfRows 72 | { 73 | get { return numberOfRows; } 74 | set 75 | { 76 | numberOfRows = value; 77 | OnPropertyChanged(); 78 | } 79 | } 80 | 81 | /// 82 | /// * The grid angle (0.0) 83 | /// 84 | [ProtoMember(4)] 85 | public double GridAngle 86 | { 87 | get { return gridAngle; } 88 | set 89 | { 90 | gridAngle = value; 91 | OnPropertyChanged(); 92 | } 93 | } 94 | 95 | /// 96 | /// * The x coordinate of the origin, located at the bottom left corner (0.0) 97 | /// 98 | [ProtoMember(5)] 99 | public double OriginXCoordinate 100 | { 101 | get { return originXCoordinate; } 102 | set 103 | { 104 | originXCoordinate = value; 105 | OnPropertyChanged(); 106 | } 107 | } 108 | 109 | /// 110 | /// * The y coordinate of the origin, located at the bottom left corner (0.0) 111 | /// 112 | [ProtoMember(6)] 113 | public double OriginYCoordinate 114 | { 115 | get { return originYCoordinate; } 116 | set 117 | { 118 | originYCoordinate = value; 119 | OnPropertyChanged(); 120 | } 121 | } 122 | 123 | /// 124 | /// * The grid block size in x dimension, used only for squared grids (0.0) 125 | /// 126 | [ProtoMember(7)] 127 | public double XGridBlockSize 128 | { 129 | get { return xGridBlockSize; } 130 | set 131 | { 132 | xGridBlockSize = value; 133 | OnPropertyChanged(); 134 | } 135 | } 136 | 137 | /// 138 | /// * The grid block size in y dimension, used only for squared grids (0.0) 139 | /// 140 | [ProtoMember(8)] 141 | public double YGridBlockSize 142 | { 143 | get { return yGridBlockSize; } 144 | set 145 | { 146 | yGridBlockSize = value; 147 | OnPropertyChanged(); 148 | } 149 | } 150 | 151 | /// 152 | /// * The x coordinate of the upper right corner (0.0) 153 | /// 154 | [ProtoMember(9)] 155 | public double UpperRightCornerXCoordinate 156 | { 157 | get { return upperRightCornerXCoordinate; } 158 | set 159 | { 160 | upperRightCornerXCoordinate = value; 161 | OnPropertyChanged(); 162 | } 163 | } 164 | 165 | /// 166 | /// * The x coordinate of the upper right corner (0.0) 167 | /// 168 | [ProtoMember(10)] 169 | public double UpperRightCornerYCoordinate 170 | { 171 | get { return upperRightCornerYCoordinate; } 172 | set 173 | { 174 | upperRightCornerYCoordinate = value; 175 | OnPropertyChanged(); 176 | } 177 | } 178 | 179 | /// 180 | public event PropertyChangedEventHandler PropertyChanged; 181 | 182 | private void OnPropertyChanged([CallerMemberName] string propertyName = null) 183 | { 184 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 185 | } 186 | } 187 | } -------------------------------------------------------------------------------- /test/MeshKernelNETTest/Api/MeshKernelTestUtilsTest.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using MeshKernelNET.Api; 3 | using NUnit.Framework; 4 | 5 | namespace MeshKernelNETTest.Api 6 | { 7 | /// 8 | /// This test fixture tests whether the utility functions for creating grids produce the same geometry and 9 | /// connectivity arrays as corresponding grids produced by MeshKernel. 10 | /// 11 | /// This test assumes the following conventional convention: 12 | /// horizontal == x-direction 13 | /// vertical == y-direction 14 | /// a row is oriented horizontally, a column oriented vertically. 15 | /// width is a length in horizontal direction, height is a length in vertical direction 16 | /// 17 | /// Note that and 18 | /// take the dimensions in a different order. 19 | /// 20 | [TestFixture] 21 | public class MeshKernelTestUtilsTest 22 | { 23 | MeshKernelApi api; 24 | 25 | [SetUp] 26 | public void Setup() 27 | { 28 | api = new MeshKernelApi(); 29 | } 30 | 31 | [TearDown] 32 | public void TearDown() 33 | { 34 | api.Dispose(); 35 | } 36 | 37 | [TestCase(3,4)] 38 | [TestCase(4,3)] 39 | public void CreateMesh2D_CreatesExpectedNumberOfNodesAndEdges(int nx, int ny) 40 | { 41 | var meshFromKernel = RectangleMeshFromMeshKernel(MakeGridParameters(nx, ny)); 42 | 43 | var mesh = TestUtilityFunctions.CreateMesh2D(nx, ny, 1, 1); 44 | 45 | Assert.That(mesh.NodeCount(), Is.EqualTo(meshFromKernel.NodeCount())); 46 | Assert.That(mesh.EdgeCount(), Is.EqualTo(meshFromKernel.EdgeCount())); 47 | } 48 | 49 | [TestCase(3,4)] 50 | [TestCase(4,3)] 51 | public void CreateMesh2D_CreatesExpectedNodeX(int nx, int ny) 52 | { 53 | var meshFromKernel = RectangleMeshFromMeshKernel(MakeGridParameters(nx, ny)); 54 | 55 | var mesh = TestUtilityFunctions.CreateMesh2D(nx, ny, 1, 1); 56 | 57 | Assert.That(mesh.NodeX.SequenceEqual(meshFromKernel.NodeX), Is.True, 58 | $"{TestUtilityFunctions.AsString(mesh.NodeX)} != {TestUtilityFunctions.AsString(meshFromKernel.NodeX)}"); 59 | } 60 | 61 | [TestCase(3,4)] 62 | [TestCase(4,3)] 63 | public void CreateMesh2D_CreatesExpectedNodeY(int nx, int ny) 64 | { 65 | var meshFromKernel = RectangleMeshFromMeshKernel(MakeGridParameters(nx, ny)); 66 | 67 | var mesh = TestUtilityFunctions.CreateMesh2D(nx, ny, 1, 1); 68 | 69 | Assert.That(mesh.NodeY.SequenceEqual(meshFromKernel.NodeY), Is.True, 70 | $"{TestUtilityFunctions.AsString(mesh.NodeY)} != {TestUtilityFunctions.AsString(meshFromKernel.NodeY)}"); 71 | } 72 | 73 | [TestCase(3,4)] 74 | [TestCase(4,3)] 75 | public void CreateMesh2D_CreatesExpectedEdgeNodes(int nx, int ny) 76 | { 77 | var meshFromKernel = RectangleMeshFromMeshKernel(MakeGridParameters(nx, ny)); 78 | 79 | var mesh = TestUtilityFunctions.CreateMesh2D(nx, ny, 1, 1); 80 | 81 | Assert.That(mesh.EdgeNodes.SequenceEqual(meshFromKernel.EdgeNodes), Is.True, 82 | $"{TestUtilityFunctions.AsString(mesh.EdgeNodes)} != {TestUtilityFunctions.AsString(meshFromKernel.EdgeNodes)}"); 83 | } 84 | 85 | [TestCase(3,4)] 86 | [TestCase(4,3)] 87 | public void CreateCurvilinearGrid_CreatesExpectedNumberOfNodesAndEdges(int nx, int ny) 88 | { 89 | var gridFromKernel = RectangleCurvilinearGridFromMeshKernel(MakeGridParameters(nx, ny)); 90 | 91 | var grid = TestUtilityFunctions.CreateCurvilinearGrid(ny, nx, 1, 1); 92 | 93 | Assert.That(grid.NodeCount(), Is.EqualTo(gridFromKernel.NodeCount())); 94 | Assert.That(grid.EdgeCount(), Is.EqualTo(gridFromKernel.EdgeCount())); 95 | } 96 | 97 | [TestCase(3,4)] 98 | [TestCase(4,3)] 99 | public void CreateCurvilinearGrid_CreatesExpectedNodeX(int nx, int ny) 100 | { 101 | var gridFromKernel = RectangleCurvilinearGridFromMeshKernel(MakeGridParameters(nx, ny)); 102 | 103 | var grid = TestUtilityFunctions.CreateCurvilinearGrid(ny, nx, 1, 1); 104 | 105 | Assert.That(grid.NodeX.SequenceEqual(gridFromKernel.NodeX), Is.True, 106 | $"{TestUtilityFunctions.AsString(grid.NodeX)} != {TestUtilityFunctions.AsString(gridFromKernel.NodeX)}"); 107 | } 108 | 109 | [TestCase(3,4)] 110 | [TestCase(4,3)] 111 | public void CreateCurvilinearGrid_CreatesExpectedNodeY(int nx, int ny) 112 | { 113 | var gridFromKernel = RectangleCurvilinearGridFromMeshKernel(MakeGridParameters(nx, ny)); 114 | 115 | var grid = TestUtilityFunctions.CreateCurvilinearGrid(ny, nx, 1, 1); 116 | 117 | Assert.That(grid.NodeY.SequenceEqual(gridFromKernel.NodeY), Is.True, 118 | $"{TestUtilityFunctions.AsString(grid.NodeY)} != {TestUtilityFunctions.AsString(gridFromKernel.NodeY)}"); 119 | } 120 | 121 | /// 122 | /// Create a data structure used to create a rectangle grid. 123 | /// 124 | /// The number of nodes in x direction 125 | /// The number of nodes in y direction 126 | /// 127 | private static MakeGridParameters MakeGridParameters(int numNodesX, int numNodesY) 128 | { 129 | return new MakeGridParameters 130 | { 131 | NumberOfColumns = numNodesX - 1, NumberOfRows = numNodesY - 1, XGridBlockSize = 1, YGridBlockSize = 1 132 | }; 133 | } 134 | 135 | private DisposableMesh2D RectangleMeshFromMeshKernel(MakeGridParameters rectangle) 136 | { 137 | int id = api.AllocateState(0); 138 | api.Mesh2dMakeRectangularMesh(id, rectangle); 139 | api.Mesh2dGetData(id, out DisposableMesh2D mesh); 140 | api.DeallocateState(id); 141 | return mesh; 142 | } 143 | 144 | private DisposableCurvilinearGrid RectangleCurvilinearGridFromMeshKernel(MakeGridParameters rectangle) 145 | { 146 | int id = api.AllocateState(0); 147 | api.CurvilinearComputeRectangularGrid(id, rectangle); 148 | api.CurvilinearGridGetData(id, out DisposableCurvilinearGrid grid); 149 | api.DeallocateState(id); 150 | return grid; 151 | } 152 | } 153 | } -------------------------------------------------------------------------------- /tools/gather_to_sign_dll.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ 3 | Script to harvest and restore dlls for signing purposes. 4 | """ 5 | 6 | __author__ = "Maarten Tegelaers" 7 | __copyright__ = "Copyright (C) Stichting Deltares, 2020" 8 | __version__ = "0.1.1" 9 | __maintainer__ = "Maarten Tegelaers" 10 | __email__ = "Maarten.Tegelaers@deltares.nl" 11 | __status__ = "Development" 12 | 13 | 14 | from pathlib import Path 15 | from typing import Generator 16 | import shutil 17 | import argparse 18 | import json 19 | 20 | 21 | def determine_to_sign_dll_names(repo_root: Path) -> Generator[str, None, None]: 22 | """ 23 | Gather all the dll names which need to be signed. 24 | 25 | We assume that the only dll's produced by the D-HYDRO solution are the 26 | ones created by their corresponding .csproj. Any other dll is the result of 27 | a NuGet package, which should already be signed to begin with. 28 | 29 | Args: 30 | repo_root (Path): Path to the repo root 31 | 32 | Returns: 33 | Generator[str, None, None]: Generator containing the dll names to sign. 34 | """ 35 | src_csprojs = (repo_root / Path("src")).glob("**/*.csproj") 36 | return (x.with_suffix(".dll").name for x in src_csprojs) 37 | 38 | 39 | def get_corresponding_path(repo_root: Path, dll_name: str) -> Path: 40 | """ 41 | Get the dll path corresponding with the dll_name from the provided repo_root. 42 | 43 | Args: 44 | repo_root (Path): Path to the repo root 45 | dll_name (str): Name of the dll 46 | """ 47 | # this hack is required to keep the capitalisation 48 | return next( 49 | (repo_root / Path("bin/netstandard2.0")).glob("**/{}".format(dll_name)) 50 | ).parent / Path(dll_name) 51 | 52 | 53 | def has_corresponding_path(repo_root: Path, dll_name: str) -> bool: 54 | """ 55 | Verify whether the provided dll has a corresponding path. 56 | Args: 57 | repo_root (Path): Path to the repo root 58 | dll_names (Generator[str, None, None]): Generator containing the dll names to sign. 59 | 60 | Returns: 61 | bool: True if a corresponding path can be found; False otherwise. 62 | """ 63 | return any((repo_root / "bin" / "netstandard2.0").glob(f"**/{dll_name}")) 64 | 65 | 66 | def find_all_dll_paths( 67 | repo_root: Path, dll_names: Generator[str, None, None] 68 | ) -> Generator[Path, None, None]: 69 | """ 70 | Find the dll paths of the dll names provided in dll_names. 71 | 72 | Args: 73 | repo_root (Path): Path to the repo root 74 | dll_names (Generator[str, None, None]): Generator containing the dll names to sign. 75 | 76 | Returns: 77 | Generator[Path, None, None]: Generator containing the dll paths to sign. 78 | """ 79 | return ( 80 | get_corresponding_path(repo_root, x) 81 | for x in dll_names 82 | if has_corresponding_path(repo_root, x) 83 | ) 84 | 85 | 86 | def generate_json_mapping_content( 87 | repo_root: Path, dll_paths: Generator[Path, None, None] 88 | ) -> dict: 89 | """ 90 | Generate the json mapping file, with which the signed dlls can be restored to their original paths. 91 | 92 | Args: 93 | repo_root (Path): Path to the repo root 94 | dll_paths (Generator[Path, None, None]): Generator containing the dll names to sign. 95 | 96 | Returns: 97 | dict: Dict describing the mapping 98 | """ 99 | return { 100 | "mapping": list( 101 | ({"dll": p.name, "path": str(p.relative_to(repo_root))} for p in dll_paths) 102 | ) 103 | } 104 | 105 | 106 | DLL_MAPPING_FILE_NAME = "dll_mapping.json" 107 | 108 | 109 | def write_json_mapping_file(sign_dir: Path, content: dict) -> None: 110 | """ 111 | Write the specified content to the mapping file within sign_dir. 112 | 113 | Args: 114 | sign_dir (Path): Path to the directory containing the dlls to sign. 115 | content (dict): dictionary describing the dll mapping. 116 | """ 117 | json_mapping_file = sign_dir / Path(DLL_MAPPING_FILE_NAME) 118 | with json_mapping_file.open("w") as f: 119 | json.dump(content, f, indent=4) 120 | 121 | 122 | def read_json_mapping_file(sign_dir: Path) -> dict: 123 | """ 124 | Load the dll mapping file from the sign_dir. 125 | 126 | Args: 127 | sign_dir (Path): Path to the directory containing the signed dlls. 128 | 129 | Returns: 130 | dict: A dictionary containing the mapping of the dlls. 131 | """ 132 | json_mapping_file = sign_dir / Path(DLL_MAPPING_FILE_NAME) 133 | with json_mapping_file.open("r") as f: 134 | return json.load(f) 135 | 136 | 137 | def harvest_to_sign_dlls( 138 | dll_paths: Generator[Path, None, None], sign_dir: Path 139 | ) -> None: 140 | """ 141 | Move the dlls specified by dll_paths to the sign_dir. 142 | 143 | Args: 144 | dll_paths: The dlls to copy 145 | sign_dir (Path): Path to the directory containing the dlls to sign. 146 | """ 147 | for p in dll_paths: 148 | shutil.move(str(p), str(sign_dir)) 149 | 150 | 151 | def restore_signed_dlls(repo_root: Path, dll_mapping: dict, sign_dir) -> None: 152 | """ 153 | Restore the signed dlls to their original locations. 154 | 155 | Args: 156 | repo_root (Path): The path to the repo root. 157 | dll_mapping (dict): Dictionary describing the mapping of dlls 158 | sign_dir (Path): Path to the directory containing the signed dlls. 159 | """ 160 | for dll_info in dll_mapping["mapping"]: 161 | shutil.move( 162 | str(sign_dir / dll_info["dll"]), str(repo_root / Path(dll_info["path"])) 163 | ) 164 | 165 | 166 | TO_SIGN_DIR_NAME = "to_sign" 167 | 168 | 169 | def harvest(repo_root: Path) -> None: 170 | """ 171 | Create a to_sign folder containing all the dlls to sign, and their corresponding mapping 172 | 173 | Args: 174 | repo_root: Path to the repo root. 175 | """ 176 | dll_names = determine_to_sign_dll_names(repo_root) 177 | dll_paths = list(find_all_dll_paths(repo_root, dll_names)) 178 | 179 | sign_dir = repo_root / Path(TO_SIGN_DIR_NAME) 180 | if not (sign_dir.exists() and sign_dir.is_dir()): 181 | sign_dir.mkdir(parents=True) 182 | 183 | dll_mapping = generate_json_mapping_content(repo_root, dll_paths) 184 | write_json_mapping_file(sign_dir, dll_mapping) 185 | 186 | harvest_to_sign_dlls(dll_paths, sign_dir) 187 | 188 | 189 | def restore(repo_root: Path) -> None: 190 | """ 191 | Restore the signed dlls to their original locations. 192 | 193 | Args: 194 | repo_root: Path to the repo root. 195 | """ 196 | sign_dir = repo_root / Path(TO_SIGN_DIR_NAME) 197 | dll_mapping = read_json_mapping_file(sign_dir) 198 | 199 | restore_signed_dlls(repo_root, dll_mapping, sign_dir) 200 | 201 | 202 | def parse_arguments(): 203 | """ 204 | Parse the arguments with which this script was called through 205 | argparse.ArgumentParser 206 | """ 207 | parser = argparse.ArgumentParser() 208 | 209 | parser.add_argument("repo_root_path", help="Path to the root of the repository.") 210 | action_group = parser.add_mutually_exclusive_group(required=True) 211 | action_group.add_argument("--harvest", action="store_true") 212 | action_group.add_argument("--restore", action="store_false") 213 | 214 | return parser.parse_args() 215 | 216 | 217 | if __name__ == "__main__": 218 | args = parse_arguments() 219 | repo_root = Path(args.repo_root_path) 220 | 221 | if args.harvest: 222 | harvest(repo_root) 223 | else: 224 | restore(repo_root) 225 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd 364 | 365 | # Jetbrains Rider IDE 366 | .idea/ 367 | D-HYDRO.DotSettings 368 | .vscode -------------------------------------------------------------------------------- /src/MeshKernelNET/Api/DisposableMesh2D.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using MeshKernelNET.Helpers; 3 | using MeshKernelNET.Native; 4 | using ProtoBuf; 5 | 6 | namespace MeshKernelNET.Api 7 | { 8 | [ProtoContract(AsReferenceDefault = true)] 9 | public sealed class DisposableMesh2D : DisposableNativeObject, IReadOnly2DMesh 10 | { 11 | [ProtoMember(1)] 12 | private int[] edgeFaces; 13 | 14 | [ProtoMember(2)] 15 | private int[] edgeNodes; 16 | 17 | [ProtoMember(3)] 18 | private int[] faceEdges; 19 | 20 | [ProtoMember(4)] 21 | private int[] faceNodes; 22 | 23 | [ProtoMember(5)] 24 | private int[] nodesPerFace; 25 | 26 | [ProtoMember(6)] 27 | private double[] nodeX; 28 | 29 | [ProtoMember(7)] 30 | private double[] nodeY; 31 | 32 | [ProtoMember(8)] 33 | private double[] edgeX; 34 | 35 | [ProtoMember(9)] 36 | private double[] edgeY; 37 | 38 | [ProtoMember(10)] 39 | private double[] faceX; 40 | 41 | [ProtoMember(11)] 42 | private double[] faceY; 43 | 44 | [ProtoMember(12)] 45 | private int numNodes; 46 | 47 | [ProtoMember(13)] 48 | private int numValidNodes; 49 | 50 | [ProtoMember(14)] 51 | private int numEdges; 52 | 53 | [ProtoMember(15)] 54 | private int numValidEdges; 55 | 56 | [ProtoMember(16)] 57 | private int numFaces; 58 | 59 | [ProtoMember(17)] 60 | private int numFaceNodes; 61 | 62 | public DisposableMesh2D() 63 | { 64 | } 65 | 66 | public DisposableMesh2D(int nNodes, int nEdges, int nFaces, int nFaceNodes, int numValidNodes, int numValidEdges) 67 | { 68 | NumNodes = nNodes; 69 | NumEdges = nEdges; 70 | NumFaces = nFaces; 71 | NumFaceNodes = nFaceNodes; 72 | NumValidNodes = numValidNodes; 73 | NumValidEdges = numValidEdges; 74 | 75 | NodeX = new double[NumNodes]; 76 | NodeY = new double[NumNodes]; 77 | EdgeNodes = new int[NumEdges * 2]; 78 | EdgeFaces = new int[NumEdges * 2]; 79 | FaceEdges = new int[NumFaceNodes]; 80 | EdgeX = new double[NumEdges]; 81 | EdgeY = new double[NumEdges]; 82 | FaceNodes = new int[NumFaceNodes]; 83 | NodesPerFace = new int[NumFaces]; 84 | FaceX = new double[NumFaces]; 85 | FaceY = new double[NumFaces]; 86 | } 87 | 88 | public DisposableMesh2D(int nNodes, int nEdges, int numValidNodes, int numValidEdges) 89 | { 90 | NumNodes = nNodes; 91 | NumEdges = nEdges; 92 | NumValidNodes = numValidNodes; 93 | NumValidEdges = numValidEdges; 94 | NodeX = new double[NumNodes]; 95 | NodeY = new double[NumNodes]; 96 | EdgeNodes = new int[NumEdges * 2]; 97 | } 98 | 99 | ~DisposableMesh2D() 100 | { 101 | Dispose(false); 102 | } 103 | 104 | public int[] EdgeFaces 105 | { 106 | get { return edgeFaces; } 107 | set { edgeFaces = value; } 108 | } 109 | 110 | public int[] EdgeNodes 111 | { 112 | get { return edgeNodes; } 113 | set { edgeNodes = value; } 114 | } 115 | 116 | public int[] FaceEdges 117 | { 118 | get { return faceEdges; } 119 | set { faceEdges = value; } 120 | } 121 | 122 | public int[] FaceNodes 123 | { 124 | get { return faceNodes; } 125 | set { faceNodes = value; } 126 | } 127 | 128 | public int[] NodesPerFace 129 | { 130 | get { return nodesPerFace; } 131 | set { nodesPerFace = value; } 132 | } 133 | 134 | public double[] NodeX 135 | { 136 | get { return nodeX; } 137 | set { nodeX = value; } 138 | } 139 | 140 | public double[] NodeY 141 | { 142 | get { return nodeY; } 143 | set { nodeY = value; } 144 | } 145 | 146 | public double[] EdgeX 147 | { 148 | get { return edgeX; } 149 | set { edgeX = value; } 150 | } 151 | 152 | public double[] EdgeY 153 | { 154 | get { return edgeY; } 155 | set { edgeY = value; } 156 | } 157 | 158 | public double[] FaceX 159 | { 160 | get { return faceX; } 161 | set { faceX = value; } 162 | } 163 | 164 | public double[] FaceY 165 | { 166 | get { return faceY; } 167 | set { faceY = value; } 168 | } 169 | 170 | public int NumNodes 171 | { 172 | get { return numNodes; } 173 | set { numNodes = value; } 174 | } 175 | 176 | public int NumValidNodes 177 | { 178 | get { return numValidNodes; } 179 | set { numValidNodes = value; } 180 | } 181 | 182 | public int NumEdges 183 | { 184 | get { return numEdges; } 185 | set { numEdges = value; } 186 | } 187 | 188 | public int NumValidEdges 189 | { 190 | get { return numValidEdges; } 191 | set { numValidEdges = value; } 192 | } 193 | 194 | public int NumFaces 195 | { 196 | get { return numFaces; } 197 | set { numFaces = value; } 198 | } 199 | 200 | public int NumFaceNodes 201 | { 202 | get { return numFaceNodes; } 203 | set { numFaceNodes = value; } 204 | } 205 | 206 | #region IReadOnly2DMesh 207 | /// 208 | public int CellCount() 209 | { 210 | return NumFaces; 211 | } 212 | 213 | /// 214 | public int GetCellEdgeCount(int cellIndex) 215 | { 216 | return NodesPerFace[cellIndex]; 217 | } 218 | 219 | /// 220 | public int EdgeCount() 221 | { 222 | return NumEdges; 223 | } 224 | 225 | /// 226 | public int GetFirstNode(int edgeIndex) 227 | { 228 | return EdgeNodes[2 * edgeIndex]; 229 | } 230 | 231 | /// 232 | public int GetLastNode(int edgeIndex) 233 | { 234 | return EdgeNodes[2 * edgeIndex + 1]; 235 | } 236 | 237 | /// 238 | public int NodeCount() 239 | { 240 | return NumNodes; 241 | } 242 | 243 | /// 244 | public double GetNodeX(int nodeIndex) 245 | { 246 | return NodeX[nodeIndex]; 247 | } 248 | 249 | /// 250 | public double GetNodeY(int nodeIndex) 251 | { 252 | return NodeY[nodeIndex]; 253 | } 254 | #endregion 255 | 256 | protected override void SetNativeObject(ref Mesh2DNative nativeObject) 257 | { 258 | nativeObject.edge_faces = GetPinnedObjectPointer(EdgeFaces); 259 | nativeObject.edge_nodes = GetPinnedObjectPointer(EdgeNodes); 260 | nativeObject.face_edges = GetPinnedObjectPointer(FaceEdges); 261 | nativeObject.face_nodes = GetPinnedObjectPointer(FaceNodes); 262 | nativeObject.nodes_per_face = GetPinnedObjectPointer(NodesPerFace); 263 | nativeObject.node_x = GetPinnedObjectPointer(NodeX); 264 | nativeObject.node_y = GetPinnedObjectPointer(NodeY); 265 | nativeObject.edge_x = GetPinnedObjectPointer(EdgeX); 266 | nativeObject.edge_y = GetPinnedObjectPointer(EdgeY); 267 | nativeObject.face_x = GetPinnedObjectPointer(FaceX); 268 | nativeObject.face_y = GetPinnedObjectPointer(FaceY); 269 | nativeObject.num_nodes = NumNodes; 270 | nativeObject.num_valid_nodes = NumValidNodes; 271 | nativeObject.num_edges = NumEdges; 272 | nativeObject.num_valid_edges = NumValidEdges; 273 | nativeObject.num_faces = NumFaces; 274 | nativeObject.num_face_nodes = NumFaceNodes; 275 | } 276 | 277 | public void UpdateFromNativeObject(ref Mesh2DNative nativeObject, bool addCellInformation = false) 278 | { 279 | EdgeNodes = nativeObject.edge_nodes.CreateValueArray(nativeObject.num_edges * 2).ToArray(); 280 | NodeX = nativeObject.node_x.CreateValueArray(nativeObject.num_nodes); 281 | NodeY = nativeObject.node_y.CreateValueArray(nativeObject.num_nodes); 282 | NumEdges = nativeObject.num_edges; 283 | NumNodes = nativeObject.num_nodes; 284 | NumValidNodes = nativeObject.num_valid_nodes; 285 | NumValidEdges = nativeObject.num_valid_edges; 286 | 287 | if (addCellInformation && nativeObject.num_faces > 0) 288 | { 289 | NumFaces = nativeObject.num_faces; 290 | NodesPerFace = nativeObject.nodes_per_face.CreateValueArray(nativeObject.num_faces); 291 | FaceNodes = nativeObject.face_nodes.CreateValueArray(NodesPerFace.Sum()); 292 | FaceX = nativeObject.face_x.CreateValueArray(nativeObject.num_faces); 293 | FaceY = nativeObject.face_y.CreateValueArray(nativeObject.num_faces); 294 | } 295 | } 296 | } 297 | } -------------------------------------------------------------------------------- /test/MeshKernelNETTest/Api/DisposableGriddedSamplesTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MeshKernelNET.Api; 3 | using NUnit.Framework; 4 | 5 | namespace MeshKernelNETTest.Api 6 | { 7 | [TestFixture] 8 | public class DisposableGriddedSamplesTests 9 | { 10 | [Test] 11 | public void Constructor_WithValidParameters_CreatesInstance() 12 | { 13 | var samples = new DisposableGriddedSamples(10, 20, 1.0, 2.0, 0.5, InterpolationType.Float); 14 | 15 | using (Assert.EnterMultipleScope()) 16 | { 17 | Assert.That(samples.NumX, Is.EqualTo(10)); 18 | Assert.That(samples.NumY, Is.EqualTo(20)); 19 | Assert.That(samples.OriginX, Is.EqualTo(1.0)); 20 | Assert.That(samples.OriginY, Is.EqualTo(2.0)); 21 | Assert.That(samples.CellSize, Is.EqualTo(0.5)); 22 | Assert.That(samples.ValueType, Is.EqualTo(InterpolationType.Float)); 23 | } 24 | } 25 | 26 | [Test] 27 | public void Constructor_WithShortType_InitializesShortArray() 28 | { 29 | var samples = new DisposableGriddedSamples(5, 4, 0.0, 0.0, 1.0, InterpolationType.Short); 30 | 31 | Assert.That(samples.ShortValues, Is.Not.Null); 32 | 33 | using (Assert.EnterMultipleScope()) 34 | { 35 | Assert.That(samples.ShortValues, Has.Length.EqualTo(20)); 36 | Assert.That(samples.FloatValues, Is.Null); 37 | Assert.That(samples.IntValues, Is.Null); 38 | Assert.That(samples.DoubleValues, Is.Null); 39 | } 40 | } 41 | 42 | [Test] 43 | public void Constructor_WithFloatType_InitializesFloatArray() 44 | { 45 | var samples = new DisposableGriddedSamples(5, 4, 0.0, 0.0, 1.0, InterpolationType.Float); 46 | 47 | Assert.That(samples.FloatValues, Is.Not.Null); 48 | 49 | using (Assert.EnterMultipleScope()) 50 | { 51 | Assert.That(samples.FloatValues, Has.Length.EqualTo(20)); 52 | Assert.That(samples.ShortValues, Is.Null); 53 | Assert.That(samples.IntValues, Is.Null); 54 | Assert.That(samples.DoubleValues, Is.Null); 55 | } 56 | } 57 | 58 | [Test] 59 | public void Constructor_WithIntType_InitializesIntArray() 60 | { 61 | var samples = new DisposableGriddedSamples(5, 4, 0.0, 0.0, 1.0, InterpolationType.Int); 62 | 63 | Assert.That(samples.IntValues, Is.Not.Null); 64 | 65 | using (Assert.EnterMultipleScope()) 66 | { 67 | Assert.That(samples.IntValues, Has.Length.EqualTo(20)); 68 | Assert.That(samples.ShortValues, Is.Null); 69 | Assert.That(samples.FloatValues, Is.Null); 70 | Assert.That(samples.DoubleValues, Is.Null); 71 | } 72 | } 73 | 74 | [Test] 75 | public void Constructor_WithDoubleType_InitializesDoubleArray() 76 | { 77 | var samples = new DisposableGriddedSamples(5, 4, 0.0, 0.0, 1.0, InterpolationType.Double); 78 | 79 | Assert.That(samples.DoubleValues, Is.Not.Null); 80 | 81 | using (Assert.EnterMultipleScope()) 82 | { 83 | Assert.That(samples.DoubleValues, Has.Length.EqualTo(20)); 84 | Assert.That(samples.ShortValues, Is.Null); 85 | Assert.That(samples.FloatValues, Is.Null); 86 | Assert.That(samples.IntValues, Is.Null); 87 | } 88 | } 89 | 90 | [Test] 91 | public void Constructor_WithInvalidType_ThrowsArgumentException() 92 | { 93 | Assert.That(() => new DisposableGriddedSamples(5, 4, 0.0, 0.0, 1.0, (InterpolationType)999), 94 | Throws.TypeOf() 95 | .With.Message.Contains("Unsupported interpolation type")); 96 | } 97 | 98 | [Test] 99 | public void Constructor_InitializesCoordinateArrays() 100 | { 101 | var samples = new DisposableGriddedSamples(3, 5, 0.0, 0.0, 1.0, InterpolationType.Float); 102 | 103 | using (Assert.EnterMultipleScope()) 104 | { 105 | Assert.That(samples.CoordinatesX, Is.Not.Null); 106 | Assert.That(samples.CoordinatesY, Is.Not.Null); 107 | } 108 | 109 | using (Assert.EnterMultipleScope()) 110 | { 111 | Assert.That(samples.CoordinatesX, Has.Length.EqualTo(3)); 112 | Assert.That(samples.CoordinatesY, Has.Length.EqualTo(5)); 113 | } 114 | } 115 | 116 | [Test] 117 | public void NumX_SetAndGet_WorksCorrectly() 118 | { 119 | var samples = new DisposableGriddedSamples(10, 10, 0.0, 0.0, 1.0, InterpolationType.Float); 120 | 121 | samples.NumX = 15; 122 | 123 | Assert.That(samples.NumX, Is.EqualTo(15)); 124 | } 125 | 126 | [Test] 127 | public void NumY_SetAndGet_WorksCorrectly() 128 | { 129 | var samples = new DisposableGriddedSamples(10, 10, 0.0, 0.0, 1.0, InterpolationType.Float); 130 | 131 | samples.NumY = 25; 132 | 133 | Assert.That(samples.NumY, Is.EqualTo(25)); 134 | } 135 | 136 | [Test] 137 | public void OriginX_SetAndGet_WorksCorrectly() 138 | { 139 | var samples = new DisposableGriddedSamples(10, 10, 0.0, 0.0, 1.0, InterpolationType.Float); 140 | 141 | samples.OriginX = 5.5; 142 | 143 | Assert.That(samples.OriginX, Is.EqualTo(5.5)); 144 | } 145 | 146 | [Test] 147 | public void OriginY_SetAndGet_WorksCorrectly() 148 | { 149 | var samples = new DisposableGriddedSamples(10, 10, 0.0, 0.0, 1.0, InterpolationType.Float); 150 | 151 | samples.OriginY = 7.3; 152 | 153 | Assert.That(samples.OriginY, Is.EqualTo(7.3)); 154 | } 155 | 156 | [Test] 157 | public void CellSize_SetAndGet_WorksCorrectly() 158 | { 159 | var samples = new DisposableGriddedSamples(10, 10, 0.0, 0.0, 1.0, InterpolationType.Float); 160 | 161 | samples.CellSize = 2.5; 162 | 163 | Assert.That(samples.CellSize, Is.EqualTo(2.5)); 164 | } 165 | 166 | [Test] 167 | public void ValueType_SetAndGet_WorksCorrectly() 168 | { 169 | var samples = new DisposableGriddedSamples(10, 10, 0.0, 0.0, 1.0, InterpolationType.Float); 170 | 171 | samples.ValueType = InterpolationType.Double; 172 | 173 | Assert.That(samples.ValueType, Is.EqualTo(InterpolationType.Double)); 174 | } 175 | 176 | [Test] 177 | public void CoordinatesX_SetAndGet_WorksCorrectly() 178 | { 179 | var samples = new DisposableGriddedSamples(10, 10, 0.0, 0.0, 1.0, InterpolationType.Float); 180 | var newCoords = new[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 }; 181 | 182 | samples.CoordinatesX = newCoords; 183 | 184 | Assert.That(samples.CoordinatesX, Is.EqualTo(newCoords)); 185 | } 186 | 187 | [Test] 188 | public void CoordinatesY_SetAndGet_WorksCorrectly() 189 | { 190 | var samples = new DisposableGriddedSamples(10, 10, 0.0, 0.0, 1.0, InterpolationType.Float); 191 | var newCoords = new[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 }; 192 | 193 | samples.CoordinatesY = newCoords; 194 | 195 | Assert.That(samples.CoordinatesY, Is.EqualTo(newCoords)); 196 | } 197 | 198 | [Test] 199 | public void ShortValues_SetAndGet_WorksCorrectly() 200 | { 201 | var samples = new DisposableGriddedSamples(2, 3, 0.0, 0.0, 1.0, InterpolationType.Short); 202 | var values = new short[] { 1, 2, 3, 4, 5, 6 }; 203 | 204 | samples.ShortValues = values; 205 | 206 | Assert.That(samples.ShortValues, Is.EqualTo(values)); 207 | } 208 | 209 | [Test] 210 | public void FloatValues_SetAndGet_WorksCorrectly() 211 | { 212 | var samples = new DisposableGriddedSamples(2, 3, 0.0, 0.0, 1.0, InterpolationType.Float); 213 | var values = new[] { 1.1f, 2.2f, 3.3f, 4.4f, 5.5f, 6.6f }; 214 | 215 | samples.FloatValues = values; 216 | 217 | Assert.That(samples.FloatValues, Is.EqualTo(values)); 218 | } 219 | 220 | [Test] 221 | public void IntValues_SetAndGet_WorksCorrectly() 222 | { 223 | var samples = new DisposableGriddedSamples(2, 3, 0.0, 0.0, 1.0, InterpolationType.Int); 224 | var values = new[] { 10, 20, 30, 40, 50, 60 }; 225 | 226 | samples.IntValues = values; 227 | 228 | Assert.That(samples.IntValues, Is.EqualTo(values)); 229 | } 230 | 231 | [Test] 232 | public void DoubleValues_SetAndGet_WorksCorrectly() 233 | { 234 | var samples = new DisposableGriddedSamples(2, 3, 0.0, 0.0, 1.0, InterpolationType.Double); 235 | var values = new[] { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6 }; 236 | 237 | samples.DoubleValues = values; 238 | 239 | Assert.That(samples.DoubleValues, Is.EqualTo(values)); 240 | } 241 | 242 | [Test] 243 | public void Constructor_WithZeroNumX_CreatesZeroLengthArrays() 244 | { 245 | var samples = new DisposableGriddedSamples(0, 10, 0.0, 0.0, 1.0, InterpolationType.Float); 246 | 247 | using (Assert.EnterMultipleScope()) 248 | { 249 | Assert.That(samples.FloatValues, Is.Empty); 250 | Assert.That(samples.CoordinatesX, Is.Empty); 251 | } 252 | } 253 | 254 | [Test] 255 | public void Constructor_WithZeroNumY_CreatesZeroLengthArrays() 256 | { 257 | var samples = new DisposableGriddedSamples(10, 0, 0.0, 0.0, 1.0, InterpolationType.Float); 258 | 259 | using (Assert.EnterMultipleScope()) 260 | { 261 | Assert.That(samples.FloatValues, Is.Empty); 262 | Assert.That(samples.CoordinatesY, Is.Empty); 263 | } 264 | } 265 | 266 | [Test] 267 | public void Constructor_WithNegativeOrigins_WorksCorrectly() 268 | { 269 | var samples = new DisposableGriddedSamples(5, 5, -10.5, -20.3, 1.0, InterpolationType.Float); 270 | 271 | using (Assert.EnterMultipleScope()) 272 | { 273 | Assert.That(samples.OriginX, Is.EqualTo(-10.5)); 274 | Assert.That(samples.OriginY, Is.EqualTo(-20.3)); 275 | } 276 | } 277 | 278 | [Test] 279 | public void Constructor_WithNegativeCellSize_WorksCorrectly() 280 | { 281 | var samples = new DisposableGriddedSamples(5, 5, 0.0, 0.0, -1.5, InterpolationType.Float); 282 | 283 | Assert.That(samples.CellSize, Is.EqualTo(-1.5)); 284 | } 285 | } 286 | } -------------------------------------------------------------------------------- /test/MeshKernelNETTest/Native/NativeStructConversionExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using GeoAPI.Geometries; 5 | using MeshKernelNET.Api; 6 | using MeshKernelNET.Helpers; 7 | using NetTopologySuite.Geometries; 8 | using NUnit.Framework; 9 | 10 | namespace MeshKernelNETTest.Native 11 | { 12 | [TestFixture] 13 | public class NativeStructConversionExtensionsTest 14 | { 15 | private const double geometrySeparator = -999.0; 16 | private const double innerOuterSeparator = -998.0; 17 | 18 | [Test] 19 | public void GivenOrthogonalizationParameters_ToOrthogonalizationParametersNative_ShouldCreateOrthogonalizationParametersNativeWithValidDefaults() 20 | { 21 | //Arrange 22 | var parameters = OrthogonalizationParameters.CreateDefault(); 23 | 24 | // Act 25 | var nativeParameters = parameters.ToOrthogonalizationParametersNative(); 26 | 27 | // Assert 28 | Assert.That(nativeParameters.OuterIterations, Is.EqualTo(parameters.OuterIterations)); 29 | Assert.That(nativeParameters.BoundaryIterations, Is.EqualTo(parameters.BoundaryIterations)); 30 | Assert.That(nativeParameters.InnerIterations, Is.EqualTo(parameters.InnerIterations)); 31 | Assert.That(nativeParameters.OrthogonalizationToSmoothingFactor, Is.EqualTo(0.975)); 32 | } 33 | 34 | [Test] 35 | public void GivenMakeGridParameters_ToMakeGridParametersNative_ShouldCreateValidMakeGridParametersNative() 36 | { 37 | //Arrange 38 | var makeGridParameters = MakeGridParameters.CreateDefault(); 39 | 40 | // Act 41 | var native = makeGridParameters.ToMakeGridParametersNative(); 42 | 43 | // Assert 44 | Assert.That(makeGridParameters.NumberOfColumns, Is.EqualTo(native.NumberOfColumns)); 45 | Assert.That(makeGridParameters.NumberOfRows, Is.EqualTo(native.NumberOfRows)); 46 | Assert.That(makeGridParameters.GridAngle, Is.EqualTo(native.GridAngle)); 47 | Assert.That(makeGridParameters.OriginXCoordinate, Is.EqualTo(native.OriginXCoordinate)); 48 | Assert.That(makeGridParameters.OriginYCoordinate, Is.EqualTo(native.OriginYCoordinate)); 49 | Assert.That(makeGridParameters.XGridBlockSize, Is.EqualTo(native.XGridBlockSize)); 50 | Assert.That(makeGridParameters.YGridBlockSize, Is.EqualTo(native.YGridBlockSize)); 51 | Assert.That(makeGridParameters.UpperRightCornerXCoordinate, Is.EqualTo(native.UpperRightCornerXCoordinate)); 52 | Assert.That(makeGridParameters.UpperRightCornerYCoordinate, Is.EqualTo(native.UpperRightCornerYCoordinate)); 53 | } 54 | 55 | [Test] 56 | public void GivenCurvilinearParameters_ToCurvilinearParametersNative_ShouldCreateValidCurvilinearParametersNative() 57 | { 58 | //Arrange 59 | var parameters = CurvilinearParameters.CreateDefault(); 60 | 61 | // Act 62 | var curvilinearParameters = parameters.ToCurvilinearParametersNative(); 63 | 64 | // Assert 65 | Assert.That(curvilinearParameters.MRefinement, Is.EqualTo(parameters.MRefinement)); 66 | Assert.That(curvilinearParameters.NRefinement, Is.EqualTo(parameters.NRefinement)); 67 | Assert.That(curvilinearParameters.SmoothingIterations, Is.EqualTo(parameters.SmoothingIterations)); 68 | Assert.That(curvilinearParameters.SmoothingParameter, Is.EqualTo(parameters.SmoothingParameter)); 69 | Assert.That(curvilinearParameters.AttractionParameter, Is.EqualTo(parameters.AttractionParameter)); 70 | } 71 | 72 | [Test] 73 | public void GivenSplinesToCurvilinearParameters_ToSplinesToCurvilinearParametersNative_ShouldCreateValidSplinesToCurvilinearParametersNative() 74 | { 75 | //Arrange 76 | var parameters = SplinesToCurvilinearParameters.CreateDefault(); 77 | 78 | // Act 79 | var splinesToCurvilinearParameters = parameters.ToSplinesToCurvilinearParametersNative(); 80 | 81 | // Assert 82 | Assert.That(splinesToCurvilinearParameters.AspectRatio, Is.EqualTo(parameters.AspectRatio)); 83 | Assert.That(splinesToCurvilinearParameters.AspectRatioGrowFactor, Is.EqualTo(parameters.AspectRatioGrowFactor)); 84 | Assert.That(splinesToCurvilinearParameters.AverageWidth, Is.EqualTo(parameters.AverageWidth)); 85 | Assert.That(splinesToCurvilinearParameters.CurvatureAdaptedGridSpacing, Is.EqualTo(Convert.ToInt32(parameters.CurvatureAdaptedGridSpacing))); 86 | Assert.That(splinesToCurvilinearParameters.GrowGridOutside, Is.EqualTo(Convert.ToInt32(parameters.GrowGridOutside))); 87 | Assert.That(splinesToCurvilinearParameters.MaximumNumberOfGridCellsInTheUniformPart, Is.EqualTo(parameters.MaximumNumberOfGridCellsInTheUniformPart)); 88 | Assert.That(splinesToCurvilinearParameters.GridsOnTopOfEachOtherTolerance, Is.EqualTo(parameters.GridsOnTopOfEachOtherTolerance)); 89 | Assert.That(splinesToCurvilinearParameters.MinimumCosineOfCrossingAngles, Is.EqualTo(parameters.MinimumCosineOfCrossingAngles)); 90 | Assert.That(splinesToCurvilinearParameters.CheckFrontCollisions, Is.EqualTo(Convert.ToInt32(parameters.CheckFrontCollisions))); 91 | Assert.That(splinesToCurvilinearParameters.RemoveSkinnyTriangles, Is.EqualTo(Convert.ToInt32(parameters.RemoveSkinnyTriangles))); 92 | } 93 | 94 | [Test] 95 | public void GivenBoundingBox_ToBoundingBoxNative_ShouldCreateValidBoundingBoxNative() 96 | { 97 | //Arrange 98 | var boundingBox = BoundingBox.CreateDefault(); 99 | 100 | // Act 101 | var boundingBoxNative = boundingBox.ToBoundingBoxNative(); 102 | 103 | // Assert 104 | Assert.That(boundingBox.xLowerLeft, Is.EqualTo(double.MinValue)); 105 | Assert.That(boundingBox.yLowerLeft, Is.EqualTo(double.MinValue)); 106 | Assert.That(boundingBox.xUpperRight, Is.EqualTo(double.MaxValue)); 107 | Assert.That(boundingBox.yUpperRight, Is.EqualTo(double.MaxValue)); 108 | 109 | Assert.That(boundingBoxNative.xLowerLeft, Is.EqualTo(double.MinValue)); 110 | Assert.That(boundingBoxNative.yLowerLeft, Is.EqualTo(double.MinValue)); 111 | Assert.That(boundingBoxNative.xUpperRight, Is.EqualTo(double.MaxValue)); 112 | Assert.That(boundingBoxNative.yUpperRight, Is.EqualTo(double.MaxValue)); 113 | } 114 | 115 | [Test] 116 | public void GivenAMultiPolygon_ToDisposableGeometryList_ShouldConvertToValidDisposableGeometryList() 117 | { 118 | //Arrange 119 | var polygonWithHole = new Polygon(new LinearRing(new[] { new Coordinate(0, 0), new Coordinate(10, 0), new Coordinate(10, 10), new Coordinate(0, 10), new Coordinate(0, 0), }), new ILinearRing[] 120 | { 121 | // add interior ring (hole) 122 | new LinearRing(new[] { new Coordinate(3, 3), new Coordinate(7, 3), new Coordinate(7, 7), new Coordinate(3, 7), new Coordinate(3, 3), }), 123 | }); 124 | var polygon2 = new Polygon(new LinearRing(new[] { new Coordinate(5, 5), new Coordinate(15, 5), new Coordinate(15, 15), new Coordinate(5, 15), new Coordinate(5, 5), })); 125 | 126 | var multiPolygon = new List(new[] { polygonWithHole, polygon2 }); 127 | 128 | // Act 129 | var disposableGeometryList = multiPolygon.ToDisposableGeometryList(geometrySeparator, -998.0); 130 | 131 | // Assert 132 | int expected = polygonWithHole.ExteriorRing.Coordinates.Length + 133 | polygonWithHole.InteriorRings[0].Coordinates.Length + 134 | polygon2.Coordinates.Length; 135 | 136 | Assert.That(disposableGeometryList.NumberOfCoordinates, Is.EqualTo(expected + 2)); 137 | 138 | double[] expectedXCoordinates = new[] { 0, 10, 10, 0, 0, innerOuterSeparator, 3, 7, 7, 3, 3, geometrySeparator, 5, 15, 15, 5, 5 }; 139 | double[] expectedYCoordinates = new[] { 0, 0, 10, 10, 0, innerOuterSeparator, 3, 3, 7, 7, 3, geometrySeparator, 5, 5, 15, 15, 5 }; 140 | double[] expectedZCoordinates = new[] { 0, 0, 0, 0, 0, innerOuterSeparator, 0, 0, 0, 0, 0, geometrySeparator, 0, 0, 0, 0, 0 }; 141 | 142 | Assert.That(disposableGeometryList.XCoordinates, Is.EqualTo(expectedXCoordinates)); 143 | Assert.That(disposableGeometryList.YCoordinates, Is.EqualTo(expectedYCoordinates)); 144 | Assert.That(disposableGeometryList.Values, Is.EqualTo(expectedZCoordinates)); 145 | } 146 | 147 | [Test] 148 | public void GivenACoordinateArray_ToDisposableGeometryList_ShouldConvertToValidDisposableGeometryList() 149 | { 150 | //Arrange 151 | var coordinates = new[] { new Coordinate(0, 0), new Coordinate(1, 1), new Coordinate(2, 2), }; 152 | 153 | // Act 154 | var disposableGeometryList = coordinates.ToDisposableGeometryList(geometrySeparator, innerOuterSeparator); 155 | 156 | // Assert 157 | var expectedXCoordinates = new[] { 0, 1, 2 }; 158 | var expectedYCoordinates = new[] { 0, 1, 2 }; 159 | var expectedZCoordinates = new[] { 0, 0, 0 }; 160 | 161 | Assert.Multiple(() => 162 | { 163 | Assert.That(disposableGeometryList.XCoordinates, Is.EqualTo(expectedXCoordinates)); 164 | Assert.That(disposableGeometryList.YCoordinates, Is.EqualTo(expectedYCoordinates)); 165 | Assert.That(disposableGeometryList.Values, Is.EqualTo(expectedZCoordinates)); 166 | Assert.That(disposableGeometryList.NumberOfCoordinates, Is.EqualTo(3)); 167 | Assert.That(disposableGeometryList.GeometrySeparator, Is.EqualTo(geometrySeparator)); 168 | Assert.That(disposableGeometryList.InnerOuterSeparator, Is.EqualTo(innerOuterSeparator)); 169 | }); 170 | } 171 | 172 | [Test] 173 | public void GivenACoordinateArray_WithSingleCoordinate_ToDisposableGeometryList_ShouldConvertToValidDisposableGeometryList() 174 | { 175 | //Arrange 176 | var coordinates = new[] { new Coordinate(1.23, 4.56, 7.89) }; 177 | 178 | // Act 179 | var disposableGeometryList = coordinates.ToDisposableGeometryList(geometrySeparator, innerOuterSeparator); 180 | 181 | // Assert 182 | Assert.Multiple(() => 183 | { 184 | Assert.That(disposableGeometryList.XCoordinates, Is.EqualTo(new[] { 1.23 })); 185 | Assert.That(disposableGeometryList.YCoordinates, Is.EqualTo(new[] { 4.56 })); 186 | Assert.That(disposableGeometryList.Values, Is.EqualTo(new[] { 7.89 })); 187 | Assert.That(disposableGeometryList.NumberOfCoordinates, Is.EqualTo(1)); 188 | Assert.That(disposableGeometryList.GeometrySeparator, Is.EqualTo(geometrySeparator)); 189 | Assert.That(disposableGeometryList.InnerOuterSeparator, Is.EqualTo(innerOuterSeparator)); 190 | }); 191 | } 192 | 193 | [Test] 194 | public void GivenACoordinateCollection_ToDisposableGeometryList_ShouldConvertToValidDisposableGeometryList() 195 | { 196 | //Arrange 197 | IEnumerable coordinates = new[] { new Coordinate(0, 0), new Coordinate(1, 1), new Coordinate(2, 2), }.AsEnumerable(); 198 | 199 | // Act 200 | var disposableGeometryList = coordinates.ToDisposableGeometryList(geometrySeparator, innerOuterSeparator); 201 | 202 | // Assert 203 | var expectedXCoordinates = new[] { 0, 1, 2 }; 204 | var expectedYCoordinates = new[] { 0, 1, 2 }; 205 | var expectedZCoordinates = new[] { 0, 0, 0 }; 206 | 207 | Assert.Multiple(() => 208 | { 209 | Assert.That(disposableGeometryList.XCoordinates, Is.EqualTo(expectedXCoordinates)); 210 | Assert.That(disposableGeometryList.YCoordinates, Is.EqualTo(expectedYCoordinates)); 211 | Assert.That(disposableGeometryList.Values, Is.EqualTo(expectedZCoordinates)); 212 | Assert.That(disposableGeometryList.NumberOfCoordinates, Is.EqualTo(3)); 213 | Assert.That(disposableGeometryList.GeometrySeparator, Is.EqualTo(geometrySeparator)); 214 | Assert.That(disposableGeometryList.InnerOuterSeparator, Is.EqualTo(innerOuterSeparator)); 215 | }); 216 | } 217 | 218 | [Test] 219 | public void GivenACoordinateCollection_WithSingleCoordinate_ToDisposableGeometryList_ShouldConvertToValidDisposableGeometryList() 220 | { 221 | //Arrange 222 | IEnumerable coordinates = new[] { new Coordinate(1.23, 4.56, 7.89) }.AsEnumerable(); 223 | 224 | // Act 225 | var disposableGeometryList = coordinates.ToDisposableGeometryList(geometrySeparator, innerOuterSeparator); 226 | 227 | // Assert 228 | Assert.Multiple(() => 229 | { 230 | Assert.That(disposableGeometryList.XCoordinates, Is.EqualTo(new[] { 1.23 })); 231 | Assert.That(disposableGeometryList.YCoordinates, Is.EqualTo(new[] { 4.56 })); 232 | Assert.That(disposableGeometryList.Values, Is.EqualTo(new[] { 7.89 })); 233 | Assert.That(disposableGeometryList.NumberOfCoordinates, Is.EqualTo(1)); 234 | Assert.That(disposableGeometryList.GeometrySeparator, Is.EqualTo(geometrySeparator)); 235 | Assert.That(disposableGeometryList.InnerOuterSeparator, Is.EqualTo(innerOuterSeparator)); 236 | }); 237 | } 238 | 239 | [Test] 240 | public void GivenACollectionOfFeatures_ToFeatureList_ShouldConvertToValidFeatureList() 241 | { 242 | //Arrange 243 | var coordinates = new[] { new Coordinate(0.0, 0.0), new Coordinate(1.0, 1.0), new Coordinate(2.0, 2.0), new Coordinate(0.0, 0.0), new Coordinate(-999.0, -999.0), new Coordinate(-998.0, -998.0) }; 244 | var disposableGeometryList = coordinates.ToDisposableGeometryList(geometrySeparator, innerOuterSeparator); 245 | 246 | // Act 247 | ICollection featureList = disposableGeometryList.ToPolygonList(); 248 | 249 | // Assert 250 | var expectedXCoordinates = new[] { 0, 1, 2, 0 }; 251 | var expectedYCoordinates = new[] { 0, 1, 2, 0 }; 252 | 253 | Coordinate[] coordinateArray = featureList.ToArray()[0].Coordinates; 254 | 255 | for (var i = 0; i < coordinateArray.Length - 1; i++) 256 | { 257 | Assert.That(coordinateArray[i].X, Is.EqualTo(expectedXCoordinates[i])); 258 | Assert.That(coordinateArray[i].Y, Is.EqualTo(expectedYCoordinates[i])); 259 | } 260 | } 261 | } 262 | } -------------------------------------------------------------------------------- /src/MeshKernelNET/Helpers/NativeStructConversionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using GeoAPI.Geometries; 5 | using MeshKernelNET.Api; 6 | using MeshKernelNET.Native; 7 | using NetTopologySuite.Geometries; 8 | 9 | namespace MeshKernelNET.Helpers 10 | { 11 | public static class NativeStructConversionExtensions 12 | { 13 | internal static OrthogonalizationParametersNative ToOrthogonalizationParametersNative(this OrthogonalizationParameters orthogonalizationParameters) 14 | { 15 | return new OrthogonalizationParametersNative 16 | { 17 | OuterIterations = orthogonalizationParameters.OuterIterations, 18 | BoundaryIterations = orthogonalizationParameters.BoundaryIterations, 19 | InnerIterations = orthogonalizationParameters.InnerIterations, 20 | OrthogonalizationToSmoothingFactor = orthogonalizationParameters.OrthogonalizationToSmoothingFactor, 21 | OrthogonalizationToSmoothingFactorAtBoundary = orthogonalizationParameters.OrthogonalizationToSmoothingFactorAtBoundary, 22 | ArealToAngleSmoothingFactor = orthogonalizationParameters.ArealToAngleSmoothingFactor 23 | }; 24 | } 25 | 26 | internal static MakeGridParametersNative ToMakeGridParametersNative(this MakeGridParameters makeGridParameters) 27 | { 28 | return new MakeGridParametersNative 29 | { 30 | NumberOfColumns = makeGridParameters.NumberOfColumns, 31 | NumberOfRows = makeGridParameters.NumberOfRows, 32 | GridAngle = makeGridParameters.GridAngle, 33 | OriginXCoordinate = makeGridParameters.OriginXCoordinate, 34 | OriginYCoordinate = makeGridParameters.OriginYCoordinate, 35 | XGridBlockSize = makeGridParameters.XGridBlockSize, 36 | YGridBlockSize = makeGridParameters.YGridBlockSize, 37 | UpperRightCornerXCoordinate = makeGridParameters.UpperRightCornerXCoordinate, 38 | UpperRightCornerYCoordinate = makeGridParameters.UpperRightCornerYCoordinate, 39 | }; 40 | } 41 | 42 | internal static CurvilinearParametersNative ToCurvilinearParametersNative(this CurvilinearParameters curvilinearParameters) 43 | { 44 | return new CurvilinearParametersNative 45 | { 46 | MRefinement = curvilinearParameters.MRefinement, 47 | NRefinement = curvilinearParameters.NRefinement, 48 | SmoothingIterations = curvilinearParameters.SmoothingIterations, 49 | SmoothingParameter = curvilinearParameters.SmoothingParameter, 50 | AttractionParameter = curvilinearParameters.AttractionParameter 51 | }; 52 | } 53 | 54 | internal static SplinesToCurvilinearParametersNative ToSplinesToCurvilinearParametersNative(this SplinesToCurvilinearParameters splinesToCurvilinearParameters) 55 | { 56 | return new SplinesToCurvilinearParametersNative 57 | { 58 | AspectRatio = splinesToCurvilinearParameters.AspectRatio, 59 | AspectRatioGrowFactor = splinesToCurvilinearParameters.AspectRatioGrowFactor, 60 | AverageWidth = splinesToCurvilinearParameters.AverageWidth, 61 | CurvatureAdaptedGridSpacing = Convert.ToInt32(splinesToCurvilinearParameters.CurvatureAdaptedGridSpacing), 62 | GrowGridOutside = Convert.ToInt32(splinesToCurvilinearParameters.GrowGridOutside), 63 | MaximumNumberOfGridCellsInTheUniformPart = splinesToCurvilinearParameters.MaximumNumberOfGridCellsInTheUniformPart, 64 | GridsOnTopOfEachOtherTolerance = splinesToCurvilinearParameters.GridsOnTopOfEachOtherTolerance, 65 | MinimumCosineOfCrossingAngles = splinesToCurvilinearParameters.MinimumCosineOfCrossingAngles, 66 | CheckFrontCollisions = Convert.ToInt32(splinesToCurvilinearParameters.CheckFrontCollisions), 67 | RemoveSkinnyTriangles = Convert.ToInt32(splinesToCurvilinearParameters.RemoveSkinnyTriangles) 68 | }; 69 | } 70 | 71 | internal static MeshRefinementParametersNative ToMeshRefinementParametersNative(this MeshRefinementParameters meshRefinementParameters) 72 | { 73 | return new MeshRefinementParametersNative 74 | { 75 | MaxNumRefinementIterations = meshRefinementParameters.MaxNumRefinementIterations, 76 | RefineIntersected = Convert.ToInt32(meshRefinementParameters.RefineIntersected), 77 | UseMassCenterWhenRefining = Convert.ToInt32(meshRefinementParameters.UseMassCenterWhenRefining), 78 | MinEdgeSize = meshRefinementParameters.MinEdgeSize, 79 | RefinementType = (int)meshRefinementParameters.RefinementType, 80 | ConnectHangingNodes = Convert.ToInt32(meshRefinementParameters.ConnectHangingNodes), 81 | AccountForSamplesOutside = Convert.ToInt32(meshRefinementParameters.AccountForSamplesOutside), 82 | SmoothingIterations = meshRefinementParameters.SmoothingIterations, 83 | MaxCourantTime = meshRefinementParameters.MaxCourantTime, 84 | DirectionalRefinement = Convert.ToInt32(meshRefinementParameters.DirectionalRefinement) 85 | }; 86 | } 87 | 88 | internal static BoundingBoxNative ToBoundingBoxNative(this BoundingBox boundingBox) 89 | { 90 | return new BoundingBoxNative 91 | { 92 | xLowerLeft = boundingBox.xLowerLeft, 93 | yLowerLeft = boundingBox.yLowerLeft, 94 | xUpperRight = boundingBox.xUpperRight, 95 | yUpperRight = boundingBox.yUpperRight 96 | }; 97 | } 98 | 99 | /// 100 | /// Converts a multi-polygon into a 101 | /// 102 | /// Multi-polygon to convert 103 | /// with the multi-polygon information 104 | public static DisposableGeometryList ToDisposableGeometryList(this IList polygons, double geometrySeparator, double innerOuterSeparator) 105 | { 106 | return DisposableGeometryListFromGeometries(polygons.OfType().ToList(), geometrySeparator, innerOuterSeparator); 107 | } 108 | 109 | /// 110 | /// Converts a collection of coordinates into a . 111 | /// 112 | /// Coordinates to convert. 113 | /// The geometry separator. 114 | /// The separator for inner and outer polygons. 115 | /// A new with the coordinates. 116 | public static DisposableGeometryList ToDisposableGeometryList(this IEnumerable coordinates, double geometrySeparator, double innerOuterSeparator) 117 | { 118 | return coordinates.ToArray().ToDisposableGeometryList(geometrySeparator, innerOuterSeparator); 119 | } 120 | 121 | /// 122 | /// Converts an array of coordinates into a . 123 | /// 124 | /// Coordinates to convert. 125 | /// The geometry separator. 126 | /// The separator for inner and outer polygons. 127 | /// A new with the coordinates. 128 | public static DisposableGeometryList ToDisposableGeometryList(this Coordinate[] coordinates, double geometrySeparator, double innerOuterSeparator) 129 | { 130 | IGeometry geometry = coordinates.Length == 1 131 | ? (IGeometry)new Point(coordinates[0]) 132 | : new LineString(coordinates); 133 | 134 | return DisposableGeometryListFromGeometries(new[] { geometry }, 135 | geometrySeparator, 136 | innerOuterSeparator); 137 | } 138 | 139 | /// 140 | /// Converts a list of geometries into a 141 | /// 142 | /// 143 | /// with the coordinate array information 144 | public static DisposableGeometryList DisposableGeometryListFromGeometries(this IList geometries, double geometrySeparator, double innerOuterSeparator) 145 | { 146 | var xCoordinates = new List(); 147 | var yCoordinates = new List(); 148 | var zCoordinates = new List(); 149 | 150 | if (geometries == null) 151 | { 152 | return new DisposableGeometryList 153 | { 154 | GeometrySeparator = geometrySeparator, 155 | InnerOuterSeparator = innerOuterSeparator, 156 | NumberOfCoordinates = xCoordinates.Count, 157 | XCoordinates = xCoordinates.ToArray(), 158 | YCoordinates = yCoordinates.ToArray(), 159 | Values = zCoordinates.ToArray() 160 | }; 161 | } 162 | 163 | List[] coordinateArrays = new[] { xCoordinates, yCoordinates, zCoordinates }; 164 | 165 | for (var i = 0; i < geometries.Count; i++) 166 | { 167 | IGeometry geometry = geometries[i]; 168 | if (geometry == null) 169 | { 170 | continue; 171 | } 172 | 173 | if (i != 0) 174 | { 175 | foreach (List coordinateArray in coordinateArrays) 176 | { 177 | coordinateArray.Add(geometrySeparator); 178 | } 179 | } 180 | 181 | if (geometry is IPolygon polygon) 182 | { 183 | AddCoordinatesToArrays(polygon.ExteriorRing.Coordinates, xCoordinates, yCoordinates, zCoordinates); 184 | 185 | for (var j = 0; j < polygon.InteriorRings.Length; j++) 186 | { 187 | foreach (List coordinateArray in coordinateArrays) 188 | { 189 | coordinateArray.Add(innerOuterSeparator); 190 | } 191 | 192 | ILineString interiorRing = polygon.InteriorRings[j]; 193 | AddCoordinatesToArrays(interiorRing.Coordinates, xCoordinates, yCoordinates, zCoordinates); 194 | } 195 | } 196 | else 197 | { 198 | AddCoordinatesToArrays(geometry.Coordinates, xCoordinates, yCoordinates, zCoordinates); 199 | } 200 | } 201 | 202 | return new DisposableGeometryList 203 | { 204 | GeometrySeparator = geometrySeparator, 205 | InnerOuterSeparator = innerOuterSeparator, 206 | NumberOfCoordinates = xCoordinates.Count, 207 | XCoordinates = xCoordinates.ToArray(), 208 | YCoordinates = yCoordinates.ToArray(), 209 | Values = zCoordinates.ToArray() 210 | }; 211 | } 212 | 213 | public static DisposableGeometryList CreateEmptyDisposableGeometryList(int length, double geometrySeparator, double innerOuterSeparator) 214 | { 215 | return new DisposableGeometryList 216 | { 217 | XCoordinates = new double[length], 218 | YCoordinates = new double[length], 219 | Values = new double[length], 220 | GeometrySeparator = geometrySeparator, 221 | InnerOuterSeparator = innerOuterSeparator, 222 | NumberOfCoordinates = length 223 | }; 224 | } 225 | 226 | public static ICollection ToPolygonList(this DisposableGeometryList disposableGeometryList) 227 | { 228 | var features = new List(); 229 | 230 | if (disposableGeometryList == null) 231 | { 232 | return features; 233 | } 234 | 235 | double innerOuterSeparator = -998.0; 236 | double geometrySeparator = disposableGeometryList.GeometrySeparator; 237 | 238 | var coordinatesOuter = new List(); 239 | var coordinatesInner = new List>(); 240 | List currentCoordinateList = coordinatesOuter; 241 | 242 | for (var i = 0; i < disposableGeometryList.NumberOfCoordinates; i++) 243 | { 244 | double xCoordinate = disposableGeometryList.XCoordinates[i]; 245 | double yCoordinate = disposableGeometryList.YCoordinates[i]; 246 | 247 | if (Math.Abs(xCoordinate - innerOuterSeparator) < 1e-10) 248 | { 249 | // add coordinate list for inner rings 250 | currentCoordinateList = new List(); 251 | coordinatesInner.Add(currentCoordinateList); 252 | continue; 253 | } 254 | 255 | if (Math.Abs(xCoordinate - geometrySeparator) < 1e-10 || 256 | i == disposableGeometryList.NumberOfCoordinates - 1) 257 | { 258 | // add first coordinate to close the linear 259 | coordinatesOuter.Add(new Coordinate(coordinatesOuter[0])); 260 | coordinatesInner.ForEach(c => c.Add(c[0])); 261 | 262 | // create polygon 263 | var shell = new LinearRing(coordinatesOuter.ToArray()); 264 | ILinearRing[] linearRings = coordinatesInner 265 | .Select(c => (ILinearRing)new LinearRing(c.ToArray())) 266 | .ToArray(); 267 | 268 | var polygon = new Polygon(shell, linearRings); 269 | features.Add(polygon); 270 | coordinatesOuter.Clear(); 271 | coordinatesInner.Clear(); 272 | currentCoordinateList = coordinatesOuter; 273 | continue; 274 | } 275 | 276 | currentCoordinateList.Add(new Coordinate(xCoordinate, yCoordinate)); 277 | } 278 | 279 | return features; 280 | } 281 | 282 | private static void AddCoordinatesToArrays(Coordinate[] interiorRingCoordinates, 283 | ICollection xCoordinates, 284 | ICollection yCoordinates, 285 | ICollection zCoordinates) 286 | { 287 | for (var index = 0; index < interiorRingCoordinates.Length; index++) 288 | { 289 | Coordinate coordinate = interiorRingCoordinates[index]; 290 | xCoordinates.Add(coordinate.X); 291 | yCoordinates.Add(coordinate.Y); 292 | zCoordinates.Add(double.IsNaN(coordinate.Z) ? 0.0 : coordinate.Z); 293 | } 294 | } 295 | } 296 | } --------------------------------------------------------------------------------