diff --git a/Directory.Packages.props b/Directory.Packages.props index 94b4042..ef9d810 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,7 +3,7 @@ true - + diff --git a/src/MeshKernelNET/Api/DisposableMesh1D.cs b/src/MeshKernelNET/Api/DisposableMesh1D.cs index d22dfee..83e0cc5 100644 --- a/src/MeshKernelNET/Api/DisposableMesh1D.cs +++ b/src/MeshKernelNET/Api/DisposableMesh1D.cs @@ -19,13 +19,13 @@ public sealed class DisposableMesh1D : DisposableNativeObject private int numNodes; [ProtoMember(5)] - private readonly int numValidNodes; + private int numValidNodes; [ProtoMember(6)] private int numEdges; [ProtoMember(7)] - private readonly int numValidEdges; + private int numValidEdges; public DisposableMesh1D() { @@ -70,7 +70,11 @@ public int NumNodes set { numNodes = value; } } - public int NumValidNodes => numValidNodes; + public int NumValidNodes + { + get { return numValidNodes; } + set { numValidNodes = value; } + } public int NumEdges { @@ -78,8 +82,12 @@ public int NumEdges set { numEdges = value; } } - public int NumValidEdges => numValidEdges; - + public int NumValidEdges + { + get { return numValidEdges; } + set { numValidEdges = value; } + } + protected override void SetNativeObject(ref Mesh1DNative nativeObject) { nativeObject.edge_nodes = GetPinnedObjectPointer(EdgeNodes); diff --git a/src/MeshKernelNET/Api/DisposableMesh2D.cs b/src/MeshKernelNET/Api/DisposableMesh2D.cs index 1ca1060..8cb1d0a 100644 --- a/src/MeshKernelNET/Api/DisposableMesh2D.cs +++ b/src/MeshKernelNET/Api/DisposableMesh2D.cs @@ -43,29 +43,30 @@ public sealed class DisposableMesh2D : DisposableNativeObject, IRe private int numNodes; [ProtoMember(13)] - private readonly int numValidNodes; + private int numValidNodes; [ProtoMember(14)] private int numEdges; [ProtoMember(15)] - private readonly int numValidEdges; + private int numValidEdges; [ProtoMember(16)] private int numFaces; [ProtoMember(17)] - private readonly int numFaceNodes; + private int numFaceNodes; public DisposableMesh2D() { } - public DisposableMesh2D(int nNodes, int nEdges, int nFaces) + public DisposableMesh2D(int nNodes, int nEdges, int nFaces, int nFaceNodes) { NumNodes = nNodes; NumEdges = nEdges; NumFaces = nFaces; + NumFaceNodes = nFaceNodes; EdgeFaces = new int[NumEdges * 2]; EdgeNodes = new int[NumEdges * 2]; @@ -157,7 +158,11 @@ public int NumNodes set { numNodes = value; } } - public int NumValidNodes => numValidNodes; + public int NumValidNodes + { + get { return numValidNodes; } + set { numValidNodes = value; } + } public int NumEdges { @@ -165,7 +170,11 @@ public int NumEdges set { numEdges = value; } } - public int NumValidEdges => numValidEdges; + public int NumValidEdges + { + get { return numValidEdges; } + set { numValidEdges = value; } + } public int NumFaces { @@ -173,7 +182,11 @@ public int NumFaces set { numFaces = value; } } - public int NumFaceNodes => numFaceNodes; + public int NumFaceNodes + { + get { return numFaceNodes; } + set { numFaceNodes = value; } + } #region IReadOnly2DMesh /// diff --git a/src/MeshKernelNET/Api/IMeshKernelApi.cs b/src/MeshKernelNET/Api/IMeshKernelApi.cs index 5086fb6..d90a414 100644 --- a/src/MeshKernelNET/Api/IMeshKernelApi.cs +++ b/src/MeshKernelNET/Api/IMeshKernelApi.cs @@ -14,6 +14,13 @@ public interface IMeshKernelApi : IDisposable /// Generated meshKernelId int AllocateState(int projectionType); + /// + /// Clear the undo state + /// + /// Id of the grid state + /// Error code + int ClearUndoState(int meshKernelId); + /// /// Computes 1d-2d contacts, where 1d nodes are connected to the closest 2d faces at the boundary /// (ggeo_make1D2DRiverLinks_dll) @@ -1376,5 +1383,21 @@ int PolygonGetOffset(int meshKernelId, /// Error code int PolygonRefine(int meshKernelId, in DisposableGeometryList disposableGeometryListIn, int firstIndex, int secondIndex, double distance, ref DisposableGeometryList disposableGeometryListOut); + + /// + /// Redo editing action + /// + /// Id of the grid state + /// If the editing action has been re-done + /// Error code + int RedoState(int meshKernelId, ref bool redone); + + /// + /// Undo editing action + /// + /// Id of the grid state + /// If the editing action has been un-done + /// Error code + int UndoState(int meshKernelId, ref bool undone); } } \ No newline at end of file diff --git a/src/MeshKernelNET/Api/MeshKernelApi.cs b/src/MeshKernelNET/Api/MeshKernelApi.cs index d730e46..2d4a26b 100644 --- a/src/MeshKernelNET/Api/MeshKernelApi.cs +++ b/src/MeshKernelNET/Api/MeshKernelApi.cs @@ -20,6 +20,12 @@ public int AllocateState(int projectionType) return meshKernelId; } + /// + public int ClearUndoState(int meshKernelId) + { + return MeshKernelDll.ClearUndoState(meshKernelId); + } + public int ContactsComputeBoundary(int meshKernelId, in IntPtr oneDNodeMask, in DisposableGeometryList polygons, double searchRadius) { GeometryListNative polygonsNative = polygons.CreateNativeObject(); @@ -797,7 +803,8 @@ public int Mesh2dGetData(int meshKernelId, out DisposableMesh2D disposableMesh2D disposableMesh2D = new DisposableMesh2D(newMesh2D.num_nodes, newMesh2D.num_edges, - newMesh2D.num_faces); + newMesh2D.num_faces, + newMesh2D.num_face_nodes); newMesh2D = disposableMesh2D.CreateNativeObject(); @@ -1245,6 +1252,18 @@ public int PolygonRefine(int meshKernelId, in DisposableGeometryList disposableG return MeshKernelDll.PolygonRefine(meshKernelId, ref geometryListNativeIn, firstIndex, secondIndex, distance, ref geometryListNativeOut); } + /// + public int RedoState(int meshKernelId, ref bool redone) + { + return MeshKernelDll.RedoState(meshKernelId, ref redone); + } + + /// + public int UndoState(int meshKernelId, ref bool undone) + { + return MeshKernelDll.UndoState(meshKernelId, ref undone); + } + private DisposableMesh2D CreateDisposableMesh2D(Mesh2DNative newMesh2DNative, bool addCellInformation = false) { var disposableMesh2D = new DisposableMesh2D @@ -1253,7 +1272,9 @@ private DisposableMesh2D CreateDisposableMesh2D(Mesh2DNative newMesh2DNative, bo NodeY = newMesh2DNative.node_y.CreateValueArray(newMesh2DNative.num_nodes), EdgeNodes = newMesh2DNative.edge_nodes.CreateValueArray(newMesh2DNative.num_edges * 2).ToArray(), NumEdges = newMesh2DNative.num_edges, - NumNodes = newMesh2DNative.num_nodes + NumNodes = newMesh2DNative.num_nodes, + NumValidNodes = newMesh2DNative.num_valid_nodes, + NumValidEdges = newMesh2DNative.num_valid_edges }; if (addCellInformation && newMesh2DNative.num_faces > 0) @@ -1277,7 +1298,9 @@ private DisposableMesh1D CreateDisposableMesh1d(Mesh1DNative newMesh1DNative) NodeX = newMesh1DNative.node_x.CreateValueArray(newMesh1DNative.num_nodes), NodeY = newMesh1DNative.node_y.CreateValueArray(newMesh1DNative.num_nodes), NumNodes = newMesh1DNative.num_nodes, - NumEdges = newMesh1DNative.num_edges + NumEdges = newMesh1DNative.num_edges, + NumValidNodes = newMesh1DNative.num_valid_nodes, + NumValidEdges = newMesh1DNative.num_valid_edges, }; return disposableMesh1D; } diff --git a/src/MeshKernelNET/Native/MeshKernelDll.cs b/src/MeshKernelNET/Native/MeshKernelDll.cs index 5f94cce..0c7784d 100644 --- a/src/MeshKernelNET/Native/MeshKernelDll.cs +++ b/src/MeshKernelNET/Native/MeshKernelDll.cs @@ -29,6 +29,14 @@ static MeshKernelDll() [DllImport(MeshKernelDllName, EntryPoint = "mkernel_allocate_state", CallingConvention = CallingConvention.Cdecl)] internal static extern int AllocateState([In] int projectionType, [In][Out] ref int meshKernelId); + /// + /// Clear the undo state + /// + /// The id of the mesh state + /// Error code + [DllImport(MeshKernelDllName, EntryPoint = "mkernel_clear_undo_state", CallingConvention = CallingConvention.Cdecl)] + internal static extern int ClearUndoState([In] int meshKernelId); + /// /// Computes 1d-2d contacts, where 1d nodes are connected to the closest 2d faces at the boundary /// (ggeo_make1D2DRiverLinks_dll) @@ -1652,5 +1660,21 @@ internal static extern int PolygonCountOffset([In] int meshKernelId, /// Error code [DllImport(MeshKernelDllName, EntryPoint = "mkernel_polygon_refine", CallingConvention = CallingConvention.Cdecl)] internal static extern int PolygonRefine([In] int meshKernelId, [In] ref GeometryListNative geometryListIn, [In] int firstIndex, [In] int secondIndex, [In] double distance, [In][Out] ref GeometryListNative geometryListOut); + + /// + /// Redo editing action + /// + /// Id of the mesh state + /// Error code + [DllImport(MeshKernelDllName, EntryPoint = "mkernel_redo_state", CallingConvention = CallingConvention.Cdecl)] + internal static extern int RedoState(int meshKernelId, [In][Out] ref bool redone); + + /// + /// Redo editing action + /// + /// Id of the mesh state + /// Error code + [DllImport(MeshKernelDllName, EntryPoint = "mkernel_undo_state", CallingConvention = CallingConvention.Cdecl)] + internal static extern int UndoState(int meshKernelId, [In][Out] ref bool undone); } } \ No newline at end of file diff --git a/test/MeshKernelNETTest/Api/MeshKernelCurvilinearTest.cs b/test/MeshKernelNETTest/Api/MeshKernelCurvilinearTest.cs index 285e902..3856052 100644 --- a/test/MeshKernelNETTest/Api/MeshKernelCurvilinearTest.cs +++ b/test/MeshKernelNETTest/Api/MeshKernelCurvilinearTest.cs @@ -969,5 +969,39 @@ public void CurvilinearGrid_EdgeNodesSerialization(int numM, int numN, IList<(in new object[] { 2, 3, new[] { (0, 2), (1, 3), (2, 4), (3, 5), (0, 1), (2, 3), (4, 5) } }, new object[] { 3, 2, new[] { (0, 3), (1, 4), (2, 5), (0, 1), (1, 2), (3, 4), (4, 5) } } }; + + [Test] + public void CurvilinearSetAndCovertThroughApi() + { + + // Setup + using (var api = new MeshKernelApi()) + using (DisposableCurvilinearGrid grid = CreateCurvilinearGrid(5, 5, 10, 10)) + { + var id = 0; + DisposableCurvilinearGrid curvilinearGrid = null; + var mesh2d = new DisposableMesh2D(); + { + try + { + // Prepare + id = api.AllocateState(0); + Assert.AreEqual(0, api.CurvilinearSet(id, grid)); + Assert.AreEqual(0, api.CurvilinearConvertToMesh2D(id)); + Assert.AreEqual(0, api.Mesh2dGetData(id, out mesh2d)); + + // Assert + Assert.AreEqual(25, mesh2d.NumNodes); + } + finally + { + api.DeallocateState(id); + curvilinearGrid?.Dispose(); + mesh2d.Dispose(); + } + } + } + } + } } diff --git a/test/MeshKernelNETTest/Api/MeshKernelTest.cs b/test/MeshKernelNETTest/Api/MeshKernelTest.cs index 8e8e0be..41f59ce 100644 --- a/test/MeshKernelNETTest/Api/MeshKernelTest.cs +++ b/test/MeshKernelNETTest/Api/MeshKernelTest.cs @@ -2467,7 +2467,7 @@ public void Mesh2dDeleteInsidePolygon(DeleteMeshInsidePolygonOptions deleteMeshI } [Test] - public void Mesh2dMakeGlobaThroughApi() + public void Mesh2dMakeGlobalThroughApi() { // Generates a mesh in spherical coordinates around the globe @@ -2493,5 +2493,56 @@ public void Mesh2dMakeGlobaThroughApi() } } } + + + [Test] + public void Mesh2dUndoTwoDeleteNodesThroughApi() + { + // Setup + using (DisposableMesh2D mesh = CreateMesh2D(4, 4, 100, 200)) + using (var api = new MeshKernelApi()) + { + var id = 0; + var mesh2d = new DisposableMesh2D(); + try + { + int numberOfVerticesBefore = mesh.NumNodes; + id = api.AllocateState(0); + + Assert.AreEqual(0, api.Mesh2dSet(id, mesh)); + + // Do + Assert.AreEqual(0, api.Mesh2dDeleteNode(id, 0)); + Assert.AreEqual(0, api.Mesh2dGetData(id, out mesh2d)); + Assert.AreEqual(-999.0, mesh2d.NodeX[0]); + Assert.AreEqual(0, api.Mesh2dDeleteNode(id, 6)); + Assert.AreEqual(0, api.Mesh2dGetData(id, out mesh2d)); + Assert.AreEqual(-999.0, mesh2d.NodeX[6]); + + // Un-do + bool undone = false; + Assert.AreEqual(0, api.UndoState(id, ref undone)); + Assert.AreEqual(true, undone); + Assert.AreEqual(0, api.Mesh2dGetData(id, out mesh2d)); + Assert.AreEqual(100.0, mesh2d.NodeX[6]); + Assert.AreEqual(numberOfVerticesBefore - 1, mesh2d.NumValidNodes); + + undone = false; + Assert.AreEqual(0, api.UndoState(id, ref undone)); + Assert.AreEqual(true, undone); + + Assert.AreEqual(0, api.Mesh2dGetData(id, out mesh2d)); + Assert.AreEqual(0.0, mesh2d.NodeX[0]); + Assert.AreEqual(numberOfVerticesBefore, mesh2d.NumValidNodes); + } + finally + { + api.DeallocateState(id); + mesh2d.Dispose(); + } + } + } + + } } \ No newline at end of file