diff --git a/libs/MeshKernelApi/CMakeLists.txt b/libs/MeshKernelApi/CMakeLists.txt index 1be63d013..554d97868 100644 --- a/libs/MeshKernelApi/CMakeLists.txt +++ b/libs/MeshKernelApi/CMakeLists.txt @@ -21,22 +21,40 @@ set(VERSION_INC_DIR ${CMAKE_SOURCE_DIR}/tools) # list of target sources set(SRC_LIST + ${SRC_DIR}/BoundariesAsPolygonCache.cpp + ${SRC_DIR}/CachedIntegerValues.cpp + ${SRC_DIR}/CachedPointValues.cpp + ${SRC_DIR}/FacePolygonPropertyCache.cpp + ${SRC_DIR}/HangingEdgeCache.cpp ${SRC_DIR}/MKStateUndoAction.cpp ${SRC_DIR}/MeshKernel.cpp + ${SRC_DIR}/NodeInPolygonCache.cpp + ${SRC_DIR}/ObtuseTriangleCentreCache.cpp + ${SRC_DIR}/PolygonRefinementCache.cpp + ${SRC_DIR}/SmallFlowEdgeCentreCache.cpp ) # list of target headers set( INC_LIST + ${DOMAIN_INC_DIR}/BoundariesAsPolygonCache.hpp ${DOMAIN_INC_DIR}/BoundingBox.hpp + ${DOMAIN_INC_DIR}/CachedIntegerValues.hpp + ${DOMAIN_INC_DIR}/CachedPointValues.hpp ${DOMAIN_INC_DIR}/Contacts.hpp ${DOMAIN_INC_DIR}/CurvilinearGrid.hpp + ${DOMAIN_INC_DIR}/FacePolygonPropertyCache.hpp ${DOMAIN_INC_DIR}/GeometryList.hpp ${DOMAIN_INC_DIR}/GriddedSamples.hpp + ${DOMAIN_INC_DIR}/HangingEdgeCache.hpp ${DOMAIN_INC_DIR}/MKStateUndoAction.hpp ${DOMAIN_INC_DIR}/Mesh1D.hpp ${DOMAIN_INC_DIR}/Mesh2D.hpp ${DOMAIN_INC_DIR}/MeshKernel.hpp + ${DOMAIN_INC_DIR}/NodeInPolygonCache.hpp + ${DOMAIN_INC_DIR}/ObtuseTriangleCentreCache.hpp + ${DOMAIN_INC_DIR}/PolygonRefinementCache.hpp + ${DOMAIN_INC_DIR}/SmallFlowEdgeCentreCache.hpp ${DOMAIN_INC_DIR}/State.hpp ${DOMAIN_INC_DIR}/Utils.hpp ${VERSION_INC_DIR}/Version/Version.hpp diff --git a/libs/MeshKernelApi/include/MeshKernelApi/BoundariesAsPolygonCache.hpp b/libs/MeshKernelApi/include/MeshKernelApi/BoundariesAsPolygonCache.hpp new file mode 100644 index 000000000..8020babd0 --- /dev/null +++ b/libs/MeshKernelApi/include/MeshKernelApi/BoundariesAsPolygonCache.hpp @@ -0,0 +1,66 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2024. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// contact: delft3d.support@deltares.nl +// Stichting Deltares +// P.O. Box 177 +// 2600 MH Delft, The Netherlands +// +// All indications and logos of, and references to, "Delft3D" and "Deltares" +// are registered trademarks of Stichting Deltares, and remain the property of +// Stichting Deltares. All rights reserved. +// +//------------------------------------------------------------------------------ + +#pragma once + +#include +#include +#include +#include + +#include "MeshKernelApi/CachedPointValues.hpp" +#include "MeshKernelApi/GeometryList.hpp" +#include "MeshKernelApi/Utils.hpp" + +namespace meshkernelapi +{ + + /// @brief Cache boundary polygon points + class BoundariesAsPolygonCache : public CachedPointValues + { + public: + /// @brief Constructor + BoundariesAsPolygonCache(const int lowerLeftN, + const int lowerLeftM, + const int upperRightN, + const int upperRightM, + const std::vector& boundaryPoints); + + /// @brief Determine if current options match those used to construct the object + bool ValidOptions(const int lowerLeftN, + const int lowerLeftM, + const int upperRightN, + const int upperRightM) const; + + private: + int m_lowerLeftNValue = -1; ///< Initial lower left N value + int m_lowerLeftMValue = -1; ///< Initial lower left M value + int m_upperRightNValue = -1; ///< Initial upper right N value + int m_upperRightMValue = -1; ///< Initial upper right M value + }; + +} // namespace meshkernelapi diff --git a/libs/MeshKernelApi/include/MeshKernelApi/CachedIntegerValues.hpp b/libs/MeshKernelApi/include/MeshKernelApi/CachedIntegerValues.hpp new file mode 100644 index 000000000..daa887f05 --- /dev/null +++ b/libs/MeshKernelApi/include/MeshKernelApi/CachedIntegerValues.hpp @@ -0,0 +1,64 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2024. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// contact: delft3d.support@deltares.nl +// Stichting Deltares +// P.O. Box 177 +// 2600 MH Delft, The Netherlands +// +// All indications and logos of, and references to, "Delft3D" and "Deltares" +// are registered trademarks of Stichting Deltares, and remain the property of +// Stichting Deltares. All rights reserved. +// +//------------------------------------------------------------------------------ + +#pragma once + +#include +#include + +namespace meshkernelapi +{ + /// @brief Caches x- and y-coordinate values for various algorithms + class CachedIntegerValues + { + public: + /// @brief Default constructor + CachedIntegerValues() {} + + /// @brief Construct with point values + CachedIntegerValues(const std::vector& values); + + /// @brief Destructor + virtual ~CachedIntegerValues() = default; + + /// @brief Number of points saved + int Size() const + { + return static_cast(m_values.size()); + } + + /// @brief Copy cached points to geometry + void Copy(int* buffer) const; + + protected: + /// @brief Reset the saved integer values. + void Reset(std::vector&& values); + + private: + std::vector m_values; ///< integer values + }; +} // namespace meshkernelapi diff --git a/libs/MeshKernelApi/include/MeshKernelApi/CachedPointValues.hpp b/libs/MeshKernelApi/include/MeshKernelApi/CachedPointValues.hpp new file mode 100644 index 000000000..098245439 --- /dev/null +++ b/libs/MeshKernelApi/include/MeshKernelApi/CachedPointValues.hpp @@ -0,0 +1,70 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2024. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// contact: delft3d.support@deltares.nl +// Stichting Deltares +// P.O. Box 177 +// 2600 MH Delft, The Netherlands +// +// All indications and logos of, and references to, "Delft3D" and "Deltares" +// are registered trademarks of Stichting Deltares, and remain the property of +// Stichting Deltares. All rights reserved. +// +//------------------------------------------------------------------------------ + +#pragma once + +#include +#include + +#include "MeshKernel/Point.hpp" + +#include "MeshKernelApi/GeometryList.hpp" + +namespace meshkernelapi +{ + /// @brief Caches x- and y-coordinate values for various algorithms + class CachedPointValues + { + public: + /// @brief Default constructor + CachedPointValues() {} + + /// @brief Construct with point values + CachedPointValues(const std::vector& coordinates); + + /// @brief Destructor + virtual ~CachedPointValues() = default; + + /// @brief Number of points saved + int Size() const + { + return static_cast(m_coordsX.size()); + } + + /// @brief Copy cached points to geometry + void Copy(const GeometryList& geometry) const; + + protected: + /// @brief Reset the saved coordinate values. + void Reset(std::vector&& xValues, + std::vector&& yValues); + + private: + std::vector m_coordsX; ///< x-coordinate values + std::vector m_coordsY; ///< y-coordinate values + }; +} // namespace meshkernelapi diff --git a/libs/MeshKernelApi/include/MeshKernelApi/FacePolygonPropertyCache.hpp b/libs/MeshKernelApi/include/MeshKernelApi/FacePolygonPropertyCache.hpp new file mode 100644 index 000000000..699126482 --- /dev/null +++ b/libs/MeshKernelApi/include/MeshKernelApi/FacePolygonPropertyCache.hpp @@ -0,0 +1,62 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2024. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// contact: delft3d.support@deltares.nl +// Stichting Deltares +// P.O. Box 177 +// 2600 MH Delft, The Netherlands +// +// All indications and logos of, and references to, "Delft3D" and "Deltares" +// are registered trademarks of Stichting Deltares, and remain the property of +// Stichting Deltares. All rights reserved. +// +//------------------------------------------------------------------------------ + +#pragma once + +#include + +#include "MeshKernel/Mesh2D.hpp" + +#include "MeshKernelApi/CachedPointValues.hpp" + +namespace meshkernelapi +{ + /// @brief Cache node values of faces + class FacePolygonPropertyCache : public CachedPointValues + { + + public: + /// @brief Constructor + FacePolygonPropertyCache(const int propertyValue, + const double minValue, + const double maxValue, + const meshkernel::Mesh2D& mesh, + const int validSize, + const std::vector& filterMask); + + /// @brief Determine if current options match those used to construct the object + bool ValidOptions(const int propertyValue, + const double minValue, + const double maxValue) const; + + private: + int m_propertyValue = 0; ///< Initial property value + double m_minimumValue = meshkernel::constants::missing::doubleValue; ///< Initial minimum value + double m_maximumValue = meshkernel::constants::missing::doubleValue; ///< Initial maximum value + }; + +} // namespace meshkernelapi diff --git a/libs/MeshKernelApi/include/MeshKernelApi/HangingEdgeCache.hpp b/libs/MeshKernelApi/include/MeshKernelApi/HangingEdgeCache.hpp new file mode 100644 index 000000000..b8090f802 --- /dev/null +++ b/libs/MeshKernelApi/include/MeshKernelApi/HangingEdgeCache.hpp @@ -0,0 +1,48 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2024. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// contact: delft3d.support@deltares.nl +// Stichting Deltares +// P.O. Box 177 +// 2600 MH Delft, The Netherlands +// +// All indications and logos of, and references to, "Delft3D" and "Deltares" +// are registered trademarks of Stichting Deltares, and remain the property of +// Stichting Deltares. All rights reserved. +// +//------------------------------------------------------------------------------ + +#pragma once + +#include +#include + +#include "MeshKernel/Point.hpp" + +#include "MeshKernelApi/CachedIntegerValues.hpp" + +namespace meshkernelapi +{ + + /// @brief Cache edge indices for hanging nodes/edges + class HangingEdgeCache : public CachedIntegerValues + { + public: + /// @brief Constructor + HangingEdgeCache(const std::vector& edgeIds); + }; + +} // namespace meshkernelapi diff --git a/libs/MeshKernelApi/include/MeshKernelApi/MeshKernel.hpp b/libs/MeshKernelApi/include/MeshKernelApi/MeshKernel.hpp index 514638850..e8e429f04 100644 --- a/libs/MeshKernelApi/include/MeshKernelApi/MeshKernel.hpp +++ b/libs/MeshKernelApi/include/MeshKernelApi/MeshKernel.hpp @@ -351,6 +351,7 @@ namespace meshkernelapi /// @param[in] upperRightM The m index of the upper right corner /// @param[out] boundaryPolygons The geometry list containing the boundary polygons /// @returns Error code + /// @note Values are retrieved from the cache, cached values are deleted after copying MKERNEL_API int mkernel_curvilinear_get_boundaries_as_polygons(int meshKernelId, int lowerLeftN, int lowerLeftM, int upperRightN, int upperRightM, GeometryList& boundaryPolygons); /// @brief Count the number of nodes in curvilinear grid boundary polygons. @@ -362,6 +363,7 @@ namespace meshkernelapi /// @param[in] upperRightM The m index of the upper right corner /// @param[out] numberOfPolygonNodes The number of polygon nodes /// @returns Error code + /// @note Refined boundary polygon values are cached, so that they can be copied MKERNEL_API int mkernel_curvilinear_count_boundaries_as_polygons(int meshKernelId, int lowerLeftN, int lowerLeftM, int upperRightN, int upperRightM, int& numberOfPolygonNodes); /// @brief Gets the curvilinear grid dimensions as a CurvilinearGrid struct (converted as set of edges and nodes). @@ -1261,6 +1263,7 @@ namespace meshkernelapi /// @param[out] geometryListDimension The output parameter that will store the dimension (size) of the geometry list /// containing the polygons that match the filtering criteria. /// @returns An error code indicating the success or failure of the operation. + /// @note property values are cached, so that they can be copied MKERNEL_API int mkernel_mesh2d_get_filtered_face_polygons_dimension(int meshKernelId, int propertyValue, double minValue, @@ -1275,6 +1278,7 @@ namespace meshkernelapi /// @param[in] maxValue The maximum value of the property. /// @param[out] facePolygons The geometry list containing the filtered locations. /// @returns Error code + /// @note Values are retrieved from the cache, cached values are deleted after copying MKERNEL_API int mkernel_mesh2d_get_filtered_face_polygons(int meshKernelId, int propertyValue, double minValue, @@ -1697,6 +1701,7 @@ namespace meshkernelapi /// @param[in] distance The target interval edge length /// @param[out] numberOfPolygonNodes The number of nodes after refinement /// @returns Error code + /// @note Refined polygon values are cached, so that they can be copied in the mkernel_polygon_refine MKERNEL_API int mkernel_polygon_count_refine(int meshKernelId, const GeometryList& polygonToRefine, int firstIndex, @@ -1713,6 +1718,7 @@ namespace meshkernelapi /// @param[in] secondIndex The second index of the refinement interval /// @param[out] numberOfPolygonNodes The number of nodes after refinement /// @returns Error code + /// @note Refined polygon values are cached, so that they can be copied in the mkernel_polygon_linear_refine MKERNEL_API int mkernel_polygon_count_linear_refine(int meshKernelId, const GeometryList& polygonToRefine, int firstIndex, @@ -1756,6 +1762,7 @@ namespace meshkernelapi /// @param[in] targetEdgeLength The target interval edge length /// @param[out] refinedPolygon The refined polygon /// @returns Error code + /// @note Values are retrieved from the cache, cached values are cleared after copying MKERNEL_API int mkernel_polygon_refine(int meshKernelId, const GeometryList& polygonToRefine, int firstNodeIndex, @@ -1771,6 +1778,7 @@ namespace meshkernelapi /// @param[in] secondNodeIndex The second index of the refinement interval /// @param[out] refinedPolygon The refined polygon /// @returns Error code + /// @note Values are retrieved from the cache, cached values are cleared after copying MKERNEL_API int mkernel_polygon_linear_refine(int meshKernelId, const GeometryList& polygonToRefine, int firstNodeIndex, diff --git a/libs/MeshKernelApi/include/MeshKernelApi/NodeInPolygonCache.hpp b/libs/MeshKernelApi/include/MeshKernelApi/NodeInPolygonCache.hpp new file mode 100644 index 000000000..c0c1fbfa5 --- /dev/null +++ b/libs/MeshKernelApi/include/MeshKernelApi/NodeInPolygonCache.hpp @@ -0,0 +1,61 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2024. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// contact: delft3d.support@deltares.nl +// Stichting Deltares +// P.O. Box 177 +// 2600 MH Delft, The Netherlands +// +// All indications and logos of, and references to, "Delft3D" and "Deltares" +// are registered trademarks of Stichting Deltares, and remain the property of +// Stichting Deltares. All rights reserved. +// +//------------------------------------------------------------------------------ + +#pragma once + +#include +#include + +#include "MeshKernel/Point.hpp" + +#include "MeshKernelApi/CachedIntegerValues.hpp" +#include "MeshKernelApi/GeometryList.hpp" + +namespace meshkernelapi +{ + + /// @brief Cache node indices contained in a polygon + class NodeInPolygonCache : public CachedIntegerValues + { + public: + /// @brief Constructor + NodeInPolygonCache(const std::vector& nodeMask, + const std::vector& polygonPoints, + const int inside); + + /// @brief Determine if current options match those used to construct the object + bool ValidOptions(const std::vector& polygonPoints, const int inside) const; + + private: + /// @brief Points making up the polygon + std::vector m_polygonPoints; + + /// @brief Indicates if the points are inside or outside of the polygon + int m_inside = -1; + }; + +} // namespace meshkernelapi diff --git a/libs/MeshKernelApi/include/MeshKernelApi/ObtuseTriangleCentreCache.hpp b/libs/MeshKernelApi/include/MeshKernelApi/ObtuseTriangleCentreCache.hpp new file mode 100644 index 000000000..6b6c174c8 --- /dev/null +++ b/libs/MeshKernelApi/include/MeshKernelApi/ObtuseTriangleCentreCache.hpp @@ -0,0 +1,48 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2024. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// contact: delft3d.support@deltares.nl +// Stichting Deltares +// P.O. Box 177 +// 2600 MH Delft, The Netherlands +// +// All indications and logos of, and references to, "Delft3D" and "Deltares" +// are registered trademarks of Stichting Deltares, and remain the property of +// Stichting Deltares. All rights reserved. +// +//------------------------------------------------------------------------------ + +#pragma once + +#include +#include + +#include "MeshKernel/Point.hpp" + +#include "MeshKernelApi/CachedPointValues.hpp" + +namespace meshkernelapi +{ + + /// @brief Cache centre of edges + class ObtuseTriangleCentreCache : public CachedPointValues + { + public: + /// @brief Constructor + ObtuseTriangleCentreCache(const std::vector& triangleCentres); + }; + +} // namespace meshkernelapi diff --git a/libs/MeshKernelApi/include/MeshKernelApi/PolygonRefinementCache.hpp b/libs/MeshKernelApi/include/MeshKernelApi/PolygonRefinementCache.hpp new file mode 100644 index 000000000..4d24ec463 --- /dev/null +++ b/libs/MeshKernelApi/include/MeshKernelApi/PolygonRefinementCache.hpp @@ -0,0 +1,61 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2024. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// contact: delft3d.support@deltares.nl +// Stichting Deltares +// P.O. Box 177 +// 2600 MH Delft, The Netherlands +// +// All indications and logos of, and references to, "Delft3D" and "Deltares" +// are registered trademarks of Stichting Deltares, and remain the property of +// Stichting Deltares. All rights reserved. +// +//------------------------------------------------------------------------------ + +#pragma once + +#include + +#include "MeshKernelApi/CachedPointValues.hpp" + +namespace meshkernelapi +{ + /// @brief Refined polygon point values + class PolygonRefinementCache : public CachedPointValues + { + + public: + /// @brief Constructor + PolygonRefinementCache(const std::vector& polyPoints, + const int firstIndex, + const int secondIndex, + const double edgeLength, + const std::vector& refinedPoints); + + /// @brief Determine if current options match those used to construct the object + bool ValidOptions(const std::vector& polyPoints, + const int firstIndex, + const int secondIndex, + const double edgeLength) const; + + private: + std::vector m_polygonPoints; ///< Initial polygon points + int m_firstNodeIndex = meshkernel::constants::missing::intValue; ///< Initial first node index value + int m_secondNodeIndex = meshkernel::constants::missing::intValue; ///< Initial second node index value + double m_targetEdgeLength = meshkernel::constants::missing::doubleValue; ///< Initial edge length, may be missing value. + }; + +} // namespace meshkernelapi diff --git a/libs/MeshKernelApi/include/MeshKernelApi/SmallFlowEdgeCentreCache.hpp b/libs/MeshKernelApi/include/MeshKernelApi/SmallFlowEdgeCentreCache.hpp new file mode 100644 index 000000000..f5e9b9fcf --- /dev/null +++ b/libs/MeshKernelApi/include/MeshKernelApi/SmallFlowEdgeCentreCache.hpp @@ -0,0 +1,57 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2024. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// contact: delft3d.support@deltares.nl +// Stichting Deltares +// P.O. Box 177 +// 2600 MH Delft, The Netherlands +// +// All indications and logos of, and references to, "Delft3D" and "Deltares" +// are registered trademarks of Stichting Deltares, and remain the property of +// Stichting Deltares. All rights reserved. +// +//------------------------------------------------------------------------------ + +#pragma once + +#include +#include + +#include "MeshKernel/Point.hpp" + +#include "MeshKernelApi/CachedPointValues.hpp" +#include "MeshKernelApi/GeometryList.hpp" + +namespace meshkernelapi +{ + + /// @brief Cache centre of edges + class SmallFlowEdgeCentreCache : public CachedPointValues + { + public: + /// @brief Constructor + SmallFlowEdgeCentreCache(const double lengthThreshold, + const std::vector& edgeCentres); + + /// @brief Determine if current options match those used to construct the object + bool ValidOptions(const double lengthThreshold) const; + + private: + /// @brief Threshold for small edge length + double m_lengthThreshold; + }; + +} // namespace meshkernelapi diff --git a/libs/MeshKernelApi/include/MeshKernelApi/State.hpp b/libs/MeshKernelApi/include/MeshKernelApi/State.hpp index 74024f930..b10f67aa3 100644 --- a/libs/MeshKernelApi/include/MeshKernelApi/State.hpp +++ b/libs/MeshKernelApi/include/MeshKernelApi/State.hpp @@ -38,6 +38,15 @@ #include #include +#include "MeshKernelApi/BoundariesAsPolygonCache.hpp" +#include "MeshKernelApi/CachedPointValues.hpp" +#include "MeshKernelApi/FacePolygonPropertyCache.hpp" +#include "MeshKernelApi/HangingEdgeCache.hpp" +#include "MeshKernelApi/NodeInPolygonCache.hpp" +#include "MeshKernelApi/ObtuseTriangleCentreCache.hpp" +#include "MeshKernelApi/PolygonRefinementCache.hpp" +#include "MeshKernelApi/SmallFlowEdgeCentreCache.hpp" + namespace meshkernelapi { @@ -73,6 +82,15 @@ namespace meshkernelapi // Exclusively owned state meshkernel::Projection m_projection{meshkernel::Projection::cartesian}; ///< Projection used by the meshes + + // Cached values, used when dimensions are computed first, followed by values being retrieved in a separate call + std::shared_ptr m_facePropertyCache; ///< face property cache + std::shared_ptr m_boundariesAsPolygonCache; ///< boundaries as polygon cache + std::shared_ptr m_polygonRefinementCache; ///< polygon refinement cache + std::shared_ptr m_nodeInPolygonCache; ///< node in polygon cache + std::shared_ptr m_smallFlowEdgeCentreCache; ///< small flow edge centres cache + std::shared_ptr m_hangingEdgeCache; ///< hanging edge id cache + std::shared_ptr m_obtuseTriangleCentreCache; ///< centre of obtuse triangles cache }; } // namespace meshkernelapi diff --git a/libs/MeshKernelApi/include/MeshKernelApi/Utils.hpp b/libs/MeshKernelApi/include/MeshKernelApi/Utils.hpp index e42acc13c..89c165fac 100644 --- a/libs/MeshKernelApi/include/MeshKernelApi/Utils.hpp +++ b/libs/MeshKernelApi/include/MeshKernelApi/Utils.hpp @@ -44,6 +44,8 @@ #include "MeshKernel/BilinearInterpolationOnGriddedSamples.hpp" #include +#include "MeshKernelApi/CurvilinearGrid.hpp" + #include #include #include @@ -486,7 +488,6 @@ namespace meshkernelapi const meshkernel::Projection& projection) { meshkernel::CurvilinearGridRectangular grid(projection); - return grid.Compute(makeGridParameters.num_columns, makeGridParameters.num_rows, makeGridParameters.origin_x, @@ -583,34 +584,34 @@ namespace meshkernelapi } } - static void FillFacePolygons(std::shared_ptr mesh2d, + static void FillFacePolygons(const meshkernel::Mesh2D& mesh2d, const std::vector& facesInPolygon, const GeometryList& facePolygons) { meshkernel::UInt count = 0; - for (meshkernel::UInt f = 0; f < mesh2d->GetNumFaces(); ++f) + for (meshkernel::UInt f = 0; f < mesh2d.GetNumFaces(); ++f) { if (!facesInPolygon[f]) { continue; } - const auto& faceNodes = mesh2d->m_facesNodes[f]; + const auto& faceNodes = mesh2d.m_facesNodes[f]; if (count != 0) { - facePolygons.coordinates_x[count] = missing::doubleValue; - facePolygons.coordinates_y[count] = missing::doubleValue; + facePolygons.coordinates_x[count] = meshkernel::constants::missing::doubleValue; + facePolygons.coordinates_y[count] = meshkernel::constants::missing::doubleValue; count++; } for (meshkernel::UInt n = 0u; n < faceNodes.size(); ++n) { - const auto& currentNode = mesh2d->Node(faceNodes[n]); + const auto& currentNode = mesh2d.Node(faceNodes[n]); facePolygons.coordinates_x[count] = currentNode.x; facePolygons.coordinates_y[count] = currentNode.y; count++; } - const auto& currentNode = mesh2d->Node(faceNodes[0]); + const auto& currentNode = mesh2d.Node(faceNodes[0]); facePolygons.coordinates_x[count] = currentNode.x; facePolygons.coordinates_y[count] = currentNode.y; count++; diff --git a/libs/MeshKernelApi/src/BoundariesAsPolygonCache.cpp b/libs/MeshKernelApi/src/BoundariesAsPolygonCache.cpp new file mode 100644 index 000000000..900a25c64 --- /dev/null +++ b/libs/MeshKernelApi/src/BoundariesAsPolygonCache.cpp @@ -0,0 +1,52 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2024. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// contact: delft3d.support@deltares.nl +// Stichting Deltares +// P.O. Box 177 +// 2600 MH Delft, The Netherlands +// +// All indications and logos of, and references to, "Delft3D" and "Deltares" +// are registered trademarks of Stichting Deltares, and remain the property of +// Stichting Deltares. All rights reserved. +// +//------------------------------------------------------------------------------ + +#include "MeshKernelApi/BoundariesAsPolygonCache.hpp" + +meshkernelapi::BoundariesAsPolygonCache::BoundariesAsPolygonCache(const int lowerLeftN, + const int lowerLeftM, + const int upperRightN, + const int upperRightM, + const std::vector& boundaryPoints) + : CachedPointValues(boundaryPoints), + m_lowerLeftNValue(lowerLeftN), + m_lowerLeftMValue(lowerLeftM), + m_upperRightNValue(upperRightN), + m_upperRightMValue(upperRightM) +{ +} + +bool meshkernelapi::BoundariesAsPolygonCache::ValidOptions(const int lowerLeftN, + const int lowerLeftM, + const int upperRightN, + const int upperRightM) const +{ + return lowerLeftN == m_lowerLeftNValue && + lowerLeftM == m_lowerLeftMValue && + upperRightN == m_upperRightNValue && + upperRightM == m_upperRightMValue; +} diff --git a/libs/MeshKernelApi/src/CachedIntegerValues.cpp b/libs/MeshKernelApi/src/CachedIntegerValues.cpp new file mode 100644 index 000000000..2a61c9b9a --- /dev/null +++ b/libs/MeshKernelApi/src/CachedIntegerValues.cpp @@ -0,0 +1,46 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2024. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// contact: delft3d.support@deltares.nl +// Stichting Deltares +// P.O. Box 177 +// 2600 MH Delft, The Netherlands +// +// All indications and logos of, and references to, "Delft3D" and "Deltares" +// are registered trademarks of Stichting Deltares, and remain the property of +// Stichting Deltares. All rights reserved. +// +//------------------------------------------------------------------------------ + +#include +#include + +#include "MeshKernelApi/CachedIntegerValues.hpp" + +meshkernelapi::CachedIntegerValues::CachedIntegerValues(const std::vector& values) + : m_values(values) {} + +void meshkernelapi::CachedIntegerValues::Copy(int* values) const +{ + size_t valueCount = sizeof(int) * m_values.size(); + + std::memcpy(values, m_values.data(), valueCount); +} + +void meshkernelapi::CachedIntegerValues::Reset(std::vector&& values) +{ + m_values = std::move(values); +} diff --git a/libs/MeshKernelApi/src/CachedPointValues.cpp b/libs/MeshKernelApi/src/CachedPointValues.cpp new file mode 100644 index 000000000..186b71d74 --- /dev/null +++ b/libs/MeshKernelApi/src/CachedPointValues.cpp @@ -0,0 +1,57 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2024. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// contact: delft3d.support@deltares.nl +// Stichting Deltares +// P.O. Box 177 +// 2600 MH Delft, The Netherlands +// +// All indications and logos of, and references to, "Delft3D" and "Deltares" +// are registered trademarks of Stichting Deltares, and remain the property of +// Stichting Deltares. All rights reserved. +// +//------------------------------------------------------------------------------ + +#include +#include + +#include "MeshKernelApi/CachedPointValues.hpp" + +meshkernelapi::CachedPointValues::CachedPointValues(const std::vector& coordinates) + : m_coordsX(coordinates.size()), + m_coordsY(coordinates.size()) +{ + for (size_t i = 0; i < coordinates.size(); ++i) + { + m_coordsX[i] = coordinates[i].x; + m_coordsY[i] = coordinates[i].y; + } +} + +void meshkernelapi::CachedPointValues::Copy(const GeometryList& geometry) const +{ + size_t valueCount = sizeof(double) * m_coordsX.size(); + + std::memcpy(geometry.coordinates_x, m_coordsX.data(), valueCount); + std::memcpy(geometry.coordinates_y, m_coordsY.data(), valueCount); +} + +void meshkernelapi::CachedPointValues::Reset(std::vector&& xValues, + std::vector&& yValues) +{ + m_coordsX = std::move(xValues); + m_coordsY = std::move(yValues); +} diff --git a/libs/MeshKernelApi/src/FacePolygonPropertyCache.cpp b/libs/MeshKernelApi/src/FacePolygonPropertyCache.cpp new file mode 100644 index 000000000..dda3ec840 --- /dev/null +++ b/libs/MeshKernelApi/src/FacePolygonPropertyCache.cpp @@ -0,0 +1,63 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2024. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// contact: delft3d.support@deltares.nl +// Stichting Deltares +// P.O. Box 177 +// 2600 MH Delft, The Netherlands +// +// All indications and logos of, and references to, "Delft3D" and "Deltares" +// are registered trademarks of Stichting Deltares, and remain the property of +// Stichting Deltares. All rights reserved. +// +//------------------------------------------------------------------------------ + +#include + +#include "MeshKernelApi/FacePolygonPropertyCache.hpp" +#include "MeshKernelApi/Utils.hpp" + +meshkernelapi::FacePolygonPropertyCache::FacePolygonPropertyCache(const int propertyValue, + const double minValue, + const double maxValue, + const meshkernel::Mesh2D& mesh, + const int validSize, + const std::vector& filterMask) + : m_propertyValue(propertyValue), + m_minimumValue(minValue), + m_maximumValue(maxValue) +{ + std::vector xCoordinates(validSize); + std::vector yCoordinates(validSize); + + GeometryList facePolygons; + + facePolygons.coordinates_x = xCoordinates.data(); + facePolygons.coordinates_y = yCoordinates.data(); + + FillFacePolygons(mesh, filterMask, facePolygons); + + CachedPointValues::Reset(std::move(xCoordinates), std::move(yCoordinates)); +} + +bool meshkernelapi::FacePolygonPropertyCache::ValidOptions(const int propertyValue, + const double minValue, + const double maxValue) const +{ + return propertyValue == m_propertyValue && + minValue == m_minimumValue && + maxValue == m_maximumValue; +} diff --git a/libs/MeshKernelApi/src/HangingEdgeCache.cpp b/libs/MeshKernelApi/src/HangingEdgeCache.cpp new file mode 100644 index 000000000..23b88f11c --- /dev/null +++ b/libs/MeshKernelApi/src/HangingEdgeCache.cpp @@ -0,0 +1,46 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2024. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// contact: delft3d.support@deltares.nl +// Stichting Deltares +// P.O. Box 177 +// 2600 MH Delft, The Netherlands +// +// All indications and logos of, and references to, "Delft3D" and "Deltares" +// are registered trademarks of Stichting Deltares, and remain the property of +// Stichting Deltares. All rights reserved. +// +//------------------------------------------------------------------------------ + +#include +#include +#include + +#include "MeshKernel/Exceptions.hpp" + +#include "MeshKernelApi/HangingEdgeCache.hpp" + +meshkernelapi::HangingEdgeCache::HangingEdgeCache(const std::vector& edgeId) +{ + std::vector edgeIdInt(edgeId.size()); + + for (size_t i = 0; i < edgeId.size(); ++i) + { + edgeIdInt[i] = edgeId[i]; + } + + Reset(std::move(edgeIdInt)); +} diff --git a/libs/MeshKernelApi/src/MeshKernel.cpp b/libs/MeshKernelApi/src/MeshKernel.cpp index 643584742..28a398501 100644 --- a/libs/MeshKernelApi/src/MeshKernel.cpp +++ b/libs/MeshKernelApi/src/MeshKernel.cpp @@ -89,10 +89,15 @@ #include #include -#include -#include -#include -#include +#include "MeshKernelApi/BoundariesAsPolygonCache.hpp" +#include "MeshKernelApi/CachedPointValues.hpp" +#include "MeshKernelApi/FacePolygonPropertyCache.hpp" +#include "MeshKernelApi/MKStateUndoAction.hpp" +#include "MeshKernelApi/MeshKernel.hpp" +#include "MeshKernelApi/NodeInPolygonCache.hpp" +#include "MeshKernelApi/PolygonRefinementCache.hpp" +#include "MeshKernelApi/State.hpp" +#include "MeshKernelApi/Utils.hpp" #include @@ -935,30 +940,41 @@ namespace meshkernelapi throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } - if (!meshKernelState.contains(meshKernelId)) + if (!meshKernelState[meshKernelId].m_curvilinearGrid->IsValid()) { - throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); + throw meshkernel::MeshKernelError("Invalid curvilinear grid"); } - if (!meshKernelState[meshKernelId].m_curvilinearGrid->IsValid()) + if (boundaryPolygons.coordinates_x == nullptr || boundaryPolygons.coordinates_y == nullptr) { - throw meshkernel::MeshKernelError("Invalid curvilinear grid"); + throw meshkernel::MeshKernelError("Boundary polygon array are null"); } - auto lowerLeftNUnsigned = static_cast(lowerLeftN); - auto lowerLeftMUnsigned = static_cast(lowerLeftM); - auto upperRightNUnsigned = static_cast(upperRightN); - auto upperRightMUnsigned = static_cast(upperRightM); + if (meshKernelState[meshKernelId].m_boundariesAsPolygonCache == nullptr) + { + throw meshkernel::MeshKernelError("Polygon data has not been cached"); + } - const auto minN = std::min(lowerLeftNUnsigned, upperRightNUnsigned); - const auto maxN = std::max(lowerLeftNUnsigned, upperRightNUnsigned); - const auto minM = std::min(lowerLeftMUnsigned, upperRightMUnsigned); - const auto maxM = std::max(lowerLeftMUnsigned, upperRightMUnsigned); + if (!meshKernelState[meshKernelId].m_boundariesAsPolygonCache->ValidOptions(lowerLeftN, lowerLeftM, upperRightN, upperRightM)) + { + meshKernelState[meshKernelId].m_boundariesAsPolygonCache.reset(); + throw meshkernel::ConstraintError("Given polygon ranges are incompatible with the cached values. Cached values will be deleted."); + } - const auto boundaryPolygon = meshKernelState[meshKernelId].m_curvilinearGrid->ComputeBoundaryPolygons({minN, minM}, - {maxN, maxM}); - ConvertPointVectorToGeometryList(boundaryPolygon, boundaryPolygons); + if (boundaryPolygons.num_coordinates != meshKernelState[meshKernelId].m_boundariesAsPolygonCache->Size()) + { + meshKernelState[meshKernelId].m_boundariesAsPolygonCache.reset(); + throw meshkernel::ConstraintError("Incompatible boundary polygon size (user-size /= cached-size): {} /= {}. Cached values will be deleted.", + boundaryPolygons.num_coordinates, + meshKernelState[meshKernelId].m_boundariesAsPolygonCache->Size()); + } + + // Retrieve cached values + meshKernelState[meshKernelId].m_boundariesAsPolygonCache->Copy(boundaryPolygons); + // Clear the cache now that the values have been retrieved + meshKernelState[meshKernelId].m_boundariesAsPolygonCache.reset(); } + catch (...) { lastExitCode = HandleException(); @@ -981,6 +997,12 @@ namespace meshkernelapi throw meshkernel::MeshKernelError("Invalid curvilinear grid"); } + if (meshKernelState[meshKernelId].m_boundariesAsPolygonCache != nullptr) + { + meshKernelState[meshKernelId].m_boundariesAsPolygonCache.reset(); + throw meshkernel::MeshKernelError("Polygon data has already been cached, deleting cached data"); + } + const auto lowerLeftNUnsigned = static_cast(lowerLeftN); const auto lowerLeftMUnsigned = static_cast(lowerLeftM); const auto upperRightNUnsigned = static_cast(upperRightN); @@ -994,6 +1016,7 @@ namespace meshkernelapi const auto boundaryPolygon = meshKernelState[meshKernelId].m_curvilinearGrid->ComputeBoundaryPolygons({minN, minM}, {maxN, maxM}); numberOfPolygonNodes = static_cast(boundaryPolygon.size()); + meshKernelState[meshKernelId].m_boundariesAsPolygonCache = std::make_shared(lowerLeftN, lowerLeftM, upperRightN, upperRightM, boundaryPolygon); } catch (...) { @@ -1150,9 +1173,17 @@ namespace meshkernelapi { throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } + + if (meshKernelState[meshKernelId].m_hangingEdgeCache != nullptr) + { + meshKernelState[meshKernelId].m_hangingEdgeCache.reset(); + throw meshkernel::MeshKernelError("Polygon Hanging edge has already been cached. Cached values will be delelted."); + } + meshKernelState[meshKernelId].m_mesh2d->Administrate(); const auto hangingEdges = meshKernelState[meshKernelId].m_mesh2d->GetHangingEdges(); - numHangingEdges = static_cast(hangingEdges.size()); + meshKernelState[meshKernelId].m_hangingEdgeCache = std::make_shared(hangingEdges); + numHangingEdges = meshKernelState[meshKernelId].m_hangingEdgeCache->Size(); } catch (...) { @@ -1170,11 +1201,14 @@ namespace meshkernelapi { throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } - const auto hangingEdges = meshKernelState[meshKernelId].m_mesh2d->GetHangingEdges(); - for (size_t i = 0; i < hangingEdges.size(); ++i) + + if (meshKernelState[meshKernelId].m_hangingEdgeCache == nullptr) { - edges[i] = static_cast(hangingEdges[i]); + throw meshkernel::MeshKernelError("Hanging edge data has not been cached"); } + + meshKernelState[meshKernelId].m_hangingEdgeCache->Copy(edges); + meshKernelState[meshKernelId].m_hangingEdgeCache.reset(); } catch (...) { @@ -1936,12 +1970,27 @@ namespace meshkernelapi { throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } + + if (meshKernelState[meshKernelId].m_polygonRefinementCache == nullptr) + { + throw meshkernel::MeshKernelError("Polygon data has not been cached"); + } + auto const polygonVector = ConvertGeometryListToPointVector(polygonToRefine); + if (!meshKernelState[meshKernelId].m_polygonRefinementCache->ValidOptions(polygonVector, firstNodeIndex, secondNodeIndex, targetEdgeLength)) + { + meshKernelState[meshKernelId].m_polygonRefinementCache.reset(); + throw meshkernel::ConstraintError("Given refinement properties are incompatible with the cached values. Cached values will be deleted."); + } + const meshkernel::Polygons polygon(polygonVector, meshKernelState[meshKernelId].m_projection); auto const refinementResult = polygon.RefineFirstPolygon(firstNodeIndex, secondNodeIndex, targetEdgeLength); - ConvertPointVectorToGeometryList(refinementResult, refinedPolygon); + // Retrieve cached values + meshKernelState[meshKernelId].m_polygonRefinementCache->Copy(refinedPolygon); + // Clear the cache now that the values have been retrieved + meshKernelState[meshKernelId].m_polygonRefinementCache.reset(); } catch (...) { @@ -1960,14 +2009,23 @@ namespace meshkernelapi throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } + if (meshKernelState[meshKernelId].m_polygonRefinementCache == nullptr) + { + throw meshkernel::MeshKernelError("Polygon data has not been cached"); + } + auto const polygonVector = ConvertGeometryListToPointVector(polygonToRefine); - const meshkernel::Polygons polygon(polygonVector, meshKernelState[meshKernelId].m_projection); - const auto firstNodeIndexUnsigned = static_cast(firstNodeIndex); - const auto secondNodeUnsigned = static_cast(secondNodeIndex); - const auto refinementResult = polygon.LinearRefinePolygon(0, firstNodeIndexUnsigned, secondNodeUnsigned); + if (!meshKernelState[meshKernelId].m_polygonRefinementCache->ValidOptions(polygonVector, firstNodeIndex, secondNodeIndex, meshkernel::constants::missing::doubleValue)) + { + meshKernelState[meshKernelId].m_polygonRefinementCache.reset(); + throw meshkernel::ConstraintError("Given refinement properties are incompatible with the cached values. Cached values will be deleted."); + } - ConvertPointVectorToGeometryList(refinementResult, refinedPolygon); + // Retrieve cached values + meshKernelState[meshKernelId].m_polygonRefinementCache->Copy(refinedPolygon); + // Clear the cache now that the values have been retrieved + meshKernelState[meshKernelId].m_polygonRefinementCache.reset(); } catch (...) { @@ -1991,12 +2049,21 @@ namespace meshkernelapi throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } + if (meshKernelState[meshKernelId].m_polygonRefinementCache != nullptr) + { + meshKernelState[meshKernelId].m_polygonRefinementCache.reset(); + throw meshkernel::MeshKernelError("Polygon data has already been cached. Cached values will be delelted."); + } + auto const polygonVector = ConvertGeometryListToPointVector(polygonToRefine); const meshkernel::Polygons polygon(polygonVector, meshKernelState[meshKernelId].m_projection); const auto refinedPolygon = polygon.RefineFirstPolygon(firstIndex, secondIndex, distance); + // Cache refinedPolygon + meshKernelState[meshKernelId].m_polygonRefinementCache = std::make_shared(polygonVector, firstIndex, secondIndex, distance, refinedPolygon); + numberOfPolygonNodes = static_cast(refinedPolygon.size()); } catch (...) @@ -2016,13 +2083,23 @@ namespace meshkernelapi throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } - auto const polygonVector = ConvertGeometryListToPointVector(polygonToRefine); + if (meshKernelState[meshKernelId].m_polygonRefinementCache != nullptr) + { + meshKernelState[meshKernelId].m_polygonRefinementCache.reset(); + throw meshkernel::MeshKernelError("Polygon data has already been cached. Cached values will be delelted."); + } + auto const polygonVector = ConvertGeometryListToPointVector(polygonToRefine); const meshkernel::Polygons polygon(polygonVector, meshKernelState[meshKernelId].m_projection); const auto firstNodeIndexUnsigned = static_cast(firstNodeIndex); const auto secondNodeUnsigned = static_cast(secondNodeIndex); const auto refinementResult = polygon.LinearRefinePolygon(0, firstNodeIndexUnsigned, secondNodeUnsigned); + // Cache refinedPolygon + meshKernelState[meshKernelId].m_polygonRefinementCache = std::make_shared(polygonVector, firstNodeIndex, secondNodeIndex, + meshkernel::constants::missing::doubleValue, + refinementResult); + numberOfPolygonNodes = static_cast(refinementResult.size()); } catch (...) @@ -2113,22 +2190,27 @@ namespace meshkernelapi throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } - auto const polygonVector = ConvertGeometryListToPointVector(geometryListIn); + if (meshKernelState[meshKernelId].m_nodeInPolygonCache == nullptr) + { + throw meshkernel::MeshKernelError("Node in polygon data has not been cached"); + } - const meshkernel::Polygons polygon(polygonVector, meshKernelState[meshKernelId].m_mesh2d->m_projection); + auto const polygonVector = ConvertGeometryListToPointVector(geometryListIn); - const bool selectInside = inside == 1 ? true : false; - const auto nodeMask = meshKernelState[meshKernelId].m_mesh2d->NodeMaskFromPolygon(polygon, selectInside); + if (!meshKernelState[meshKernelId].m_nodeInPolygonCache->ValidOptions(polygonVector, inside)) + { + meshKernelState[meshKernelId].m_nodeInPolygonCache.reset(); + throw meshkernel::ConstraintError("Given polygon data and inside flag are incompatible with the cached values. Cached values will be deleted."); + } - int index = 0; - for (size_t i = 0; i < meshKernelState[meshKernelId].m_mesh2d->GetNumNodes(); ++i) + if (selectedNodes == nullptr) { - if (nodeMask[i] > 0) - { - selectedNodes[index] = static_cast(i); - index++; - } + meshKernelState[meshKernelId].m_nodeInPolygonCache.reset(); + throw meshkernel::MeshKernelError("Selected node array is null"); } + + meshKernelState[meshKernelId].m_nodeInPolygonCache->Copy(selectedNodes); + meshKernelState[meshKernelId].m_nodeInPolygonCache.reset(); } catch (...) { @@ -2149,21 +2231,22 @@ namespace meshkernelapi { throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } + + if (meshKernelState[meshKernelId].m_nodeInPolygonCache != nullptr) + { + meshKernelState[meshKernelId].m_nodeInPolygonCache.reset(); + throw meshkernel::MeshKernelError("Node in polygon data has already been cached, deleting cached data"); + } + auto const polygonVector = ConvertGeometryListToPointVector(geometryListIn); const meshkernel::Polygons polygon(polygonVector, meshKernelState[meshKernelId].m_mesh2d->m_projection); - const bool selectInside = inside == 1 ? true : false; + const bool selectInside = inside == 1; const auto nodeMask = meshKernelState[meshKernelId].m_mesh2d->NodeMaskFromPolygon(polygon, selectInside); - numberOfMeshNodes = 0; - for (size_t i = 0; i < meshKernelState[meshKernelId].m_mesh2d->GetNumNodes(); ++i) - { - if (nodeMask[i] > 0) - { - numberOfMeshNodes++; - } - } + meshKernelState[meshKernelId].m_nodeInPolygonCache = std::make_shared(nodeMask, polygonVector, inside); + numberOfMeshNodes = meshKernelState[meshKernelId].m_nodeInPolygonCache->Size(); } catch (...) { @@ -2429,7 +2512,7 @@ namespace meshkernelapi validFace[f] = true; } } - FillFacePolygons(meshKernelState[meshKernelId].m_mesh2d, validFace, facePolygons); + FillFacePolygons(*meshKernelState[meshKernelId].m_mesh2d, validFace, facePolygons); } catch (...) { @@ -2484,28 +2567,45 @@ namespace meshkernelapi { throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } + if (meshKernelState[meshKernelId].m_mesh2d->GetNumNodes() <= 0) { throw meshkernel::ConstraintError("The 2d mesh contains no nodes."); } + + if (meshKernelState[meshKernelId].m_facePropertyCache != nullptr) + { + meshKernelState[meshKernelId].m_facePropertyCache.reset(); + throw meshkernel::ConstraintError("Filtered data has already been cached. Cached values will be deleted."); + } + + geometryListDimension = 0; + const auto filterEnum = static_cast(propertyValue); const auto filterMask = meshKernelState[meshKernelId].m_mesh2d->FilterBasedOnMetric(meshkernel::Location::Faces, filterEnum, minValue, maxValue); - geometryListDimension = 0; + + // Now compute the size of the arrays required for (meshkernel::UInt f = 0; f < filterMask.size(); ++f) { if (!filterMask[f]) { continue; } + const auto faceNumEdges = static_cast(meshKernelState[meshKernelId].m_mesh2d->m_facesNodes[f].size()); geometryListDimension += faceNumEdges + 2; } + if (geometryListDimension > 0) { geometryListDimension -= 1; + meshKernelState[meshKernelId].m_facePropertyCache = std::make_shared(propertyValue, minValue, maxValue, + *meshKernelState[meshKernelId].m_mesh2d, + geometryListDimension, + filterMask); } } catch (...) @@ -2533,12 +2633,21 @@ namespace meshkernelapi throw meshkernel::ConstraintError("The 2d mesh contains no nodes."); } - const auto filterEnum = static_cast(propertyValue); - const auto filterMask = meshKernelState[meshKernelId].m_mesh2d->FilterBasedOnMetric(meshkernel::Location::Faces, - filterEnum, - minValue, - maxValue); - FillFacePolygons(meshKernelState[meshKernelId].m_mesh2d, filterMask, facePolygons); + if (meshKernelState[meshKernelId].m_facePropertyCache == nullptr) + { + throw meshkernel::ConstraintError("Filtered data has not been cached"); + } + + if (!meshKernelState[meshKernelId].m_facePropertyCache->ValidOptions(propertyValue, minValue, maxValue)) + { + meshKernelState[meshKernelId].m_facePropertyCache.reset(); + throw meshkernel::ConstraintError("Given filter properties are incompatible with the cached values. Cached values will be deleted."); + } + + // Retrieve cached values + meshKernelState[meshKernelId].m_facePropertyCache->Copy(facePolygons); + // Clear the cache now that the values have been retrieved + meshKernelState[meshKernelId].m_facePropertyCache.reset(); } catch (...) { @@ -3328,10 +3437,19 @@ namespace meshkernelapi { throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } + + if (meshKernelState[meshKernelId].m_smallFlowEdgeCentreCache != nullptr) + { + meshKernelState[meshKernelId].m_smallFlowEdgeCentreCache.reset(); + throw meshkernel::ConstraintError("Small flow edge data has already been cached. Cached values will be deleted."); + } + const auto edgesCrossingSmallFlowEdges = meshKernelState[meshKernelId].m_mesh2d->GetEdgesCrossingSmallFlowEdges(smallFlowEdgesLengthThreshold); const auto smallFlowEdgeCenters = meshKernelState[meshKernelId].m_mesh2d->GetFlowEdgesCenters(edgesCrossingSmallFlowEdges); - numSmallFlowEdges = static_cast(smallFlowEdgeCenters.size()); + meshKernelState[meshKernelId].m_smallFlowEdgeCentreCache = std::make_shared(smallFlowEdgesLengthThreshold, smallFlowEdgeCenters); + + numSmallFlowEdges = meshKernelState[meshKernelId].m_smallFlowEdgeCentreCache->Size(); } catch (...) { @@ -3350,10 +3468,19 @@ namespace meshkernelapi throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } - const auto edgesCrossingSmallFlowEdges = meshKernelState[meshKernelId].m_mesh2d->GetEdgesCrossingSmallFlowEdges(smallFlowEdgesThreshold); - const auto smallFlowEdgeCenters = meshKernelState[meshKernelId].m_mesh2d->GetFlowEdgesCenters(edgesCrossingSmallFlowEdges); + if (meshKernelState[meshKernelId].m_smallFlowEdgeCentreCache == nullptr) + { + throw meshkernel::ConstraintError("Small flow edge data has not been cached"); + } + + if (!meshKernelState[meshKernelId].m_smallFlowEdgeCentreCache->ValidOptions(smallFlowEdgesThreshold)) + { + meshKernelState[meshKernelId].m_smallFlowEdgeCentreCache.reset(); + throw meshkernel::ConstraintError("Given small flow edge options are incompatible with the cached values. Cached values will be deleted."); + } - ConvertPointVectorToGeometryList(smallFlowEdgeCenters, result); + meshKernelState[meshKernelId].m_smallFlowEdgeCentreCache->Copy(result); + meshKernelState[meshKernelId].m_smallFlowEdgeCentreCache.reset(); } catch (...) { @@ -3464,9 +3591,16 @@ namespace meshkernelapi throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } + if (meshKernelState[meshKernelId].m_obtuseTriangleCentreCache != nullptr) + { + meshKernelState[meshKernelId].m_obtuseTriangleCentreCache.reset(); + throw meshkernel::MeshKernelError("Obtuse triangle centre data has already been cached, deleting cached data"); + } + const auto obtuseTriangles = meshKernelState[meshKernelId].m_mesh2d->GetObtuseTrianglesCenters(); + meshKernelState[meshKernelId].m_obtuseTriangleCentreCache = std::make_shared(obtuseTriangles); - numObtuseTriangles = static_cast(obtuseTriangles.size()); + numObtuseTriangles = meshKernelState[meshKernelId].m_obtuseTriangleCentreCache->Size(); } catch (...) { @@ -3485,9 +3619,13 @@ namespace meshkernelapi throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } - const auto obtuseTriangles = meshKernelState[meshKernelId].m_mesh2d->GetObtuseTrianglesCenters(); + if (meshKernelState[meshKernelId].m_obtuseTriangleCentreCache == nullptr) + { + throw meshkernel::MeshKernelError("Obtuse triangle centre data has not been cached"); + } - ConvertPointVectorToGeometryList(obtuseTriangles, result); + meshKernelState[meshKernelId].m_obtuseTriangleCentreCache->Copy(result); + meshKernelState[meshKernelId].m_obtuseTriangleCentreCache.reset(); } catch (...) { diff --git a/libs/MeshKernelApi/src/NodeInPolygonCache.cpp b/libs/MeshKernelApi/src/NodeInPolygonCache.cpp new file mode 100644 index 000000000..df5564af4 --- /dev/null +++ b/libs/MeshKernelApi/src/NodeInPolygonCache.cpp @@ -0,0 +1,61 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2024. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// contact: delft3d.support@deltares.nl +// Stichting Deltares +// P.O. Box 177 +// 2600 MH Delft, The Netherlands +// +// All indications and logos of, and references to, "Delft3D" and "Deltares" +// are registered trademarks of Stichting Deltares, and remain the property of +// Stichting Deltares. All rights reserved. +// +//------------------------------------------------------------------------------ + +#include +#include +#include + +#include "MeshKernel/Exceptions.hpp" + +#include "MeshKernelApi/NodeInPolygonCache.hpp" + +meshkernelapi::NodeInPolygonCache::NodeInPolygonCache(const std::vector& nodeMask, + const std::vector& polygonPoints, + const int inside) + : m_polygonPoints(polygonPoints), m_inside(inside) +{ + std::vector nodeIndices; + + nodeIndices.reserve(m_polygonPoints.size()); + + for (size_t i = 0; i < nodeMask.size(); ++i) + { + if (nodeMask[i] > 0) + { + nodeIndices.push_back(static_cast(i)); + } + } + + Reset(std::move(nodeIndices)); +} + +bool meshkernelapi::NodeInPolygonCache::ValidOptions(const std::vector& polygonPoints, const int inside) const +{ + return inside == m_inside && + polygonPoints.size() == m_polygonPoints.size() && + std::equal(polygonPoints.begin(), polygonPoints.end(), m_polygonPoints.begin()); +} diff --git a/libs/MeshKernelApi/src/ObtuseTriangleCentreCache.cpp b/libs/MeshKernelApi/src/ObtuseTriangleCentreCache.cpp new file mode 100644 index 000000000..ec065d682 --- /dev/null +++ b/libs/MeshKernelApi/src/ObtuseTriangleCentreCache.cpp @@ -0,0 +1,31 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2024. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// contact: delft3d.support@deltares.nl +// Stichting Deltares +// P.O. Box 177 +// 2600 MH Delft, The Netherlands +// +// All indications and logos of, and references to, "Delft3D" and "Deltares" +// are registered trademarks of Stichting Deltares, and remain the property of +// Stichting Deltares. All rights reserved. +// +//------------------------------------------------------------------------------ + +#include "MeshKernelApi/ObtuseTriangleCentreCache.hpp" + +meshkernelapi::ObtuseTriangleCentreCache::ObtuseTriangleCentreCache(const std::vector& triangleCentres) + : CachedPointValues(triangleCentres) {} diff --git a/libs/MeshKernelApi/src/PolygonRefinementCache.cpp b/libs/MeshKernelApi/src/PolygonRefinementCache.cpp new file mode 100644 index 000000000..e61015a6e --- /dev/null +++ b/libs/MeshKernelApi/src/PolygonRefinementCache.cpp @@ -0,0 +1,24 @@ +#include "MeshKernelApi/PolygonRefinementCache.hpp" + +meshkernelapi::PolygonRefinementCache::PolygonRefinementCache(const std::vector& polyPoints, + const int firstIndex, + const int secondIndex, + const double edgeLength, + const std::vector& refinedPoints) + : CachedPointValues(refinedPoints), + m_polygonPoints(polyPoints), + m_firstNodeIndex(firstIndex), + m_secondNodeIndex(secondIndex), + m_targetEdgeLength(edgeLength) {} + +bool meshkernelapi::PolygonRefinementCache::ValidOptions(const std::vector& polyPoints, + const int firstIndex, + const int secondIndex, + const double edgeLength) const +{ + return firstIndex == m_firstNodeIndex && + secondIndex == m_secondNodeIndex && + edgeLength == m_targetEdgeLength && + polyPoints.size() == m_polygonPoints.size() && + std::equal(polyPoints.begin(), polyPoints.end(), m_polygonPoints.begin()); +} diff --git a/libs/MeshKernelApi/src/SmallFlowEdgeCentreCache.cpp b/libs/MeshKernelApi/src/SmallFlowEdgeCentreCache.cpp new file mode 100644 index 000000000..4ee8fc9fa --- /dev/null +++ b/libs/MeshKernelApi/src/SmallFlowEdgeCentreCache.cpp @@ -0,0 +1,37 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2024. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// contact: delft3d.support@deltares.nl +// Stichting Deltares +// P.O. Box 177 +// 2600 MH Delft, The Netherlands +// +// All indications and logos of, and references to, "Delft3D" and "Deltares" +// are registered trademarks of Stichting Deltares, and remain the property of +// Stichting Deltares. All rights reserved. +// +//------------------------------------------------------------------------------ + +#include "MeshKernelApi/SmallFlowEdgeCentreCache.hpp" + +meshkernelapi::SmallFlowEdgeCentreCache::SmallFlowEdgeCentreCache(const double lengthThreshold, + const std::vector& edgeCentres) + : CachedPointValues(edgeCentres), m_lengthThreshold(lengthThreshold) {} + +bool meshkernelapi::SmallFlowEdgeCentreCache::ValidOptions(const double lengthThreshold) const +{ + return lengthThreshold == m_lengthThreshold; +} diff --git a/libs/MeshKernelApi/tests/src/ApiTest.cpp b/libs/MeshKernelApi/tests/src/ApiTest.cpp index 6166d10ca..acf8fbeee 100644 --- a/libs/MeshKernelApi/tests/src/ApiTest.cpp +++ b/libs/MeshKernelApi/tests/src/ApiTest.cpp @@ -1358,6 +1358,36 @@ TEST_F(CartesianApiTestFixture, GetHangingEdgesMesh2D_WithOneHangingEdges_Should ASSERT_EQ(hangingEdges[0], 9); } +TEST_F(CartesianApiTestFixture, GetHangingEdgesMesh2D_WithOneHangingEdges_GetOneHangingEdgesFailures) +{ + // Prepare + MakeMesh(); + auto const meshKernelId = GetMeshKernelId(); + + // delete an edge at the lower left corner to create a new hanging edge + auto errorCode = meshkernelapi::mkernel_mesh2d_delete_edge(meshKernelId, 0.5, 0.0, 0.0, 0.0, 1.0, 1.0); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + int numHangingEdges; + errorCode = meshkernelapi::mkernel_mesh2d_count_hanging_edges(meshKernelId, numHangingEdges); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Already cached + errorCode = meshkernelapi::mkernel_mesh2d_count_hanging_edges(meshKernelId, numHangingEdges); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); + + // Re-cache + errorCode = meshkernelapi::mkernel_mesh2d_count_hanging_edges(meshKernelId, numHangingEdges); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + std::vector hangingEdges(numHangingEdges); + errorCode = meshkernelapi::mkernel_mesh2d_get_hanging_edges(meshKernelId, hangingEdges.data()); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + errorCode = meshkernelapi::mkernel_mesh2d_get_hanging_edges(meshKernelId, hangingEdges.data()); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); +} + TEST_F(CartesianApiTestFixture, DeleteHangingEdgesMesh2D_WithOneHangingEdges_ShouldDeleteOneHangingEdges) { // Prepare @@ -1485,8 +1515,18 @@ TEST_F(CartesianApiTestFixture, GetNodesInPolygonMesh2D_OnMesh2D_ShouldGetAllNod auto errorCode = mkernel_mesh2d_get_dimensions(meshKernelId, mesh2d); ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + int numberOfNodes = -1; + + errorCode = mkernel_mesh2d_count_nodes_in_polygons(meshKernelId, + geometryListIn, + 1, + numberOfNodes); + + ASSERT_EQ(numberOfNodes, mesh2d.num_nodes); + // Execute std::vector selectedNodes(mesh2d.num_nodes); + errorCode = mkernel_mesh2d_get_nodes_in_polygons(meshKernelId, geometryListIn, 1, @@ -1522,6 +1562,53 @@ TEST_F(CartesianApiTestFixture, CountNodesInPolygonMesh2D_OnMesh2D_ShouldCountAl ASSERT_EQ(12, numNodes); } +TEST_F(CartesianApiTestFixture, GetNodesInPolygonMesh2D_OnMesh2D_NodeInPolygonFailures) +{ + // Prepare + MakeMesh(); + auto const meshKernelId = GetMeshKernelId(); + + // By using an empty list, all nodes will be selected + const meshkernelapi::GeometryList geometryListIn{}; + + meshkernelapi::Mesh2D mesh2d{}; + auto errorCode = mkernel_mesh2d_get_dimensions(meshKernelId, mesh2d); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + int numberOfNodes = -1; + // Execute + std::vector selectedNodes(mesh2d.num_nodes); + + // No daata has been cached. + errorCode = mkernel_mesh2d_get_nodes_in_polygons(meshKernelId, geometryListIn, 0, selectedNodes.data()); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); + + errorCode = mkernel_mesh2d_count_nodes_in_polygons(meshKernelId, geometryListIn, 1, numberOfNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + ASSERT_EQ(numberOfNodes, mesh2d.num_nodes); + + // Values have been cached already + errorCode = mkernel_mesh2d_count_nodes_in_polygons(meshKernelId, geometryListIn, 1, numberOfNodes); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); + + // Re-cache data + errorCode = mkernel_mesh2d_count_nodes_in_polygons(meshKernelId, geometryListIn, 1, numberOfNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Incorrect parameters, should be the same as the call to count (which does the cahcing) + errorCode = mkernel_mesh2d_get_nodes_in_polygons(meshKernelId, geometryListIn, 0, selectedNodes.data()); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // Re-cache data + errorCode = mkernel_mesh2d_count_nodes_in_polygons(meshKernelId, geometryListIn, 1, numberOfNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Incorrect parameters, should be the same as the call to count (which does the cahcing) + int* nullArray = nullptr; + errorCode = mkernel_mesh2d_get_nodes_in_polygons(meshKernelId, geometryListIn, 1, nullArray); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); +} + TEST_F(CartesianApiTestFixture, InsertNodeAndEdge_OnMesh2D_ShouldInsertNodeAndEdge) { // Prepare @@ -1704,6 +1791,65 @@ TEST_F(CartesianApiTestFixture, GetSmallFlowEdges_OnMesh2D_ShouldGetSmallFlowEdg ASSERT_NEAR(result.coordinates_y[0], 0.0, tolerance); } +TEST_F(CartesianApiTestFixture, GetSmallFlowEdges_OnMesh2D_GetSmallFlowEdgesFailures) +{ + // Prepare a mesh with two triangles + meshkernelapi::Mesh2D mesh2d; + + std::vector node_x{0.0, 1.0, 1.0, 1.0}; + std::vector node_y{0.0, 0.0, 0.3, -0.3}; + std::vector edge_nodes{0, 3, 3, 1, 1, 0, 1, 2, 2, 0}; + + mesh2d.node_x = node_x.data(); + mesh2d.node_y = node_y.data(); + mesh2d.edge_nodes = edge_nodes.data(); + mesh2d.num_edges = static_cast(edge_nodes.size() * 0.5); + mesh2d.num_nodes = static_cast(node_x.size()); + + // Get the meshkernel id + auto const meshKernelId = GetMeshKernelId(); + double const smallFlowEdgesThreshold = 100.0; + meshkernelapi::GeometryList result{}; + + auto errorCode = mkernel_mesh2d_get_small_flow_edge_centers(meshKernelId, smallFlowEdgesThreshold, result); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // Execute + errorCode = mkernel_mesh2d_set(meshKernelId, mesh2d); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + int numSmallFlowEdges; + errorCode = meshkernelapi::mkernel_mesh2d_count_small_flow_edge_centers(meshKernelId, smallFlowEdgesThreshold, numSmallFlowEdges); + + // Assert + std::vector coordinates_x(numSmallFlowEdges); + std::vector coordinates_y(numSmallFlowEdges); + result.coordinates_x = coordinates_x.data(); + result.coordinates_y = coordinates_y.data(); + result.num_coordinates = numSmallFlowEdges; + + errorCode = mkernel_mesh2d_get_small_flow_edge_centers(meshKernelId, smallFlowEdgesThreshold, result); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Cache has been deleted + errorCode = mkernel_mesh2d_get_small_flow_edge_centers(meshKernelId, smallFlowEdgesThreshold, result); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // Re-cache values + errorCode = meshkernelapi::mkernel_mesh2d_count_small_flow_edge_centers(meshKernelId, smallFlowEdgesThreshold, numSmallFlowEdges); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Get values with different set of options + errorCode = mkernel_mesh2d_get_small_flow_edge_centers(meshKernelId, 2.0 * smallFlowEdgesThreshold, result); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // Re-cache values + errorCode = meshkernelapi::mkernel_mesh2d_count_small_flow_edge_centers(meshKernelId, smallFlowEdgesThreshold, numSmallFlowEdges); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + // Attempt at getting the size again will cause an error + errorCode = meshkernelapi::mkernel_mesh2d_count_small_flow_edge_centers(meshKernelId, smallFlowEdgesThreshold, numSmallFlowEdges); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); +} + TEST_F(CartesianApiTestFixture, CountObtuseTriangles_OnMesh2DWithOneObtuseTriangle_ShouldCountObtuseTriangles) { // Prepare a mesh with one obtuse triangle @@ -1727,6 +1873,67 @@ TEST_F(CartesianApiTestFixture, CountObtuseTriangles_OnMesh2DWithOneObtuseTriang // Assert ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); ASSERT_EQ(1, numObtuseTriangles); + + // Need to clear the obtuse triangle cache for the next tests + meshkernelapi::GeometryList geometryList{}; + + std::vector coordinatesObtuseTrianglesX(numObtuseTriangles); + std::vector coordinatesObtuseTrianglesY(numObtuseTriangles); + geometryList.coordinates_x = coordinatesObtuseTrianglesX.data(); + geometryList.coordinates_y = coordinatesObtuseTrianglesY.data(); + geometryList.num_coordinates = numObtuseTriangles; + errorCode = mkernel_mesh2d_get_obtuse_triangles_mass_centers(meshKernelId, geometryList); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); +} + +TEST_F(CartesianApiTestFixture, CountObtuseTriangles_OnMesh2DWithOneObtuseTriangle_ObtuseTrianglesFailures) +{ + // Prepare a mesh with one obtuse triangle + meshkernelapi::Mesh2D mesh2d; + std::vector coordinatesX{0.0, 3.0, -1.0, 1.5}; + std::vector coordinatesY{0.0, 0.0, 2.0, -2.0}; + std::vector edge_nodes{0, 1, 1, 2, 2, 0, 0, 3, 3, 1}; + mesh2d.node_x = coordinatesX.data(); + mesh2d.node_y = coordinatesY.data(); + mesh2d.edge_nodes = edge_nodes.data(); + mesh2d.num_edges = static_cast(edge_nodes.size() * 0.5); + mesh2d.num_nodes = static_cast(coordinatesX.size()); + auto const meshKernelId = GetMeshKernelId(); + + // Execute + auto errorCode = mkernel_mesh2d_set(meshKernelId, mesh2d); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Need to clear the obtuse triangle cache for the next tests + meshkernelapi::GeometryList geometryList{}; + + // Data has not yet been cached + errorCode = mkernel_mesh2d_get_obtuse_triangles_mass_centers(meshKernelId, geometryList); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); + + int numObtuseTriangles; + errorCode = meshkernelapi::mkernel_mesh2d_count_obtuse_triangles(meshKernelId, numObtuseTriangles); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Already cached, cached data will be deleted + errorCode = meshkernelapi::mkernel_mesh2d_count_obtuse_triangles(meshKernelId, numObtuseTriangles); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); + + // Re-cache data. + errorCode = meshkernelapi::mkernel_mesh2d_count_obtuse_triangles(meshKernelId, numObtuseTriangles); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + std::vector coordinatesObtuseTrianglesX(numObtuseTriangles); + std::vector coordinatesObtuseTrianglesY(numObtuseTriangles); + geometryList.coordinates_x = coordinatesObtuseTrianglesX.data(); + geometryList.coordinates_y = coordinatesObtuseTrianglesY.data(); + geometryList.num_coordinates = numObtuseTriangles; + errorCode = mkernel_mesh2d_get_obtuse_triangles_mass_centers(meshKernelId, geometryList); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Cache has been deleted in the last call + errorCode = mkernel_mesh2d_get_obtuse_triangles_mass_centers(meshKernelId, geometryList); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); } TEST_F(CartesianApiTestFixture, Mesh2DCountObtuseTriangles_OnMesh2DWithOneObtuseTriangle_ShouldGetObtuseTriangle) diff --git a/libs/MeshKernelApi/tests/src/CurvilinearGridTests.cpp b/libs/MeshKernelApi/tests/src/CurvilinearGridTests.cpp index 3d8f3bbae..349a89b83 100644 --- a/libs/MeshKernelApi/tests/src/CurvilinearGridTests.cpp +++ b/libs/MeshKernelApi/tests/src/CurvilinearGridTests.cpp @@ -1490,6 +1490,92 @@ TEST_P(CurvilineartBoundariesAsPolygonsTests, GetLocationIndex_OnACurvilinearGri ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); } +TEST(CurvilinearGrid, GetLocationIndex_OnACurvilinearGrid_GetLocationIndexFailures) +{ + const int lowerLeftN = 1; + const int lowerLeftM = 1; + const int upperRightN = 8; + const int upperRightM = 8; + + // Prepare + int meshKernelId; + auto errorCode = meshkernelapi::mkernel_allocate_state(0, meshKernelId); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + MakeGridParameters makeGridParameters; + + makeGridParameters.num_columns = 10; + makeGridParameters.num_rows = 10; + makeGridParameters.angle = 0.0; + makeGridParameters.origin_x = 0.0; + makeGridParameters.origin_y = 0.0; + makeGridParameters.block_size_x = 10.0; + makeGridParameters.block_size_y = 10.0; + + errorCode = meshkernelapi::mkernel_curvilinear_compute_rectangular_grid(meshKernelId, makeGridParameters); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Initialise with a temporary size value + int numberOfPolygonNodes = 1000; + + meshkernelapi::GeometryList boundaryPolygon; + // Set coordinate arrays with temporary size for first test + std::vector coordinates_x(numberOfPolygonNodes); + std::vector coordinates_y(numberOfPolygonNodes); + + boundaryPolygon.coordinates_x = coordinates_x.data(); + boundaryPolygon.coordinates_y = coordinates_y.data(); + boundaryPolygon.num_coordinates = numberOfPolygonNodes; + boundaryPolygon.geometry_separator = constants::missing::doubleValue; + + // Try to get before being cached + errorCode = mkernel_curvilinear_get_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM, boundaryPolygon); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); + + errorCode = meshkernelapi::mkernel_curvilinear_count_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM, numberOfPolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Resize coordinate array with correct size + coordinates_x.resize(numberOfPolygonNodes); + coordinates_y.resize(numberOfPolygonNodes); + + boundaryPolygon.coordinates_x = coordinates_x.data(); + boundaryPolygon.coordinates_y = coordinates_y.data(); + boundaryPolygon.num_coordinates = numberOfPolygonNodes; + + // Check working version + boundaryPolygon.num_coordinates = numberOfPolygonNodes; + errorCode = mkernel_curvilinear_get_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM, boundaryPolygon); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Different lowerLeftN + errorCode = meshkernelapi::mkernel_curvilinear_count_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM, numberOfPolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + errorCode = mkernel_curvilinear_get_boundaries_as_polygons(meshKernelId, lowerLeftN + 1, lowerLeftM, upperRightN, upperRightM, boundaryPolygon); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // Different lowerLeftM + errorCode = meshkernelapi::mkernel_curvilinear_count_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM, numberOfPolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + errorCode = mkernel_curvilinear_get_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM + 1, upperRightN, upperRightM, boundaryPolygon); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // Different upperRightN + errorCode = meshkernelapi::mkernel_curvilinear_count_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM, numberOfPolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + errorCode = mkernel_curvilinear_get_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN + 1, upperRightM, boundaryPolygon); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // Different upperRightM + errorCode = meshkernelapi::mkernel_curvilinear_count_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM, numberOfPolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + errorCode = mkernel_curvilinear_get_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM + 1, boundaryPolygon); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + errorCode = meshkernelapi::mkernel_expunge_state(meshKernelId); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); +} + INSTANTIATE_TEST_SUITE_P(CurvilineartBoundariesAsPolygonsTests, CurvilineartBoundariesAsPolygonsTests, ::testing::ValuesIn(CurvilineartBoundariesAsPolygonsTests::GetData())); TEST(CurvilinearGrid, MakeCircularGrid_CartesianCoordinate_ShouldMakeCurvilinearGrid) diff --git a/libs/MeshKernelApi/tests/src/Mesh2DRefinmentTests.cpp b/libs/MeshKernelApi/tests/src/Mesh2DRefinmentTests.cpp index c8c59fec1..8cb748984 100644 --- a/libs/MeshKernelApi/tests/src/Mesh2DRefinmentTests.cpp +++ b/libs/MeshKernelApi/tests/src/Mesh2DRefinmentTests.cpp @@ -53,7 +53,7 @@ TEST_F(CartesianApiTestFixture, RefineAPolygonThroughApi) geometryListOut.coordinates_x = xCoordinatesOut.data(); geometryListOut.coordinates_y = yCoordinatesOut.data(); geometryListOut.values = valuesOut.data(); - errorCode = mkernel_polygon_refine(meshKernelId, geometryListIn, false, 0, 2, geometryListOut); + errorCode = mkernel_polygon_refine(meshKernelId, geometryListIn, 0, 2, 40, geometryListOut); ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); // Assert @@ -62,6 +62,73 @@ TEST_F(CartesianApiTestFixture, RefineAPolygonThroughApi) ASSERT_NEAR(92.626556, geometryListOut.coordinates_y[0], tolerance); } +TEST_F(CartesianApiTestFixture, RefineAPolygonThroughApiFailures) +{ + // Prepare + MakeMesh(); + auto const meshKernelId = GetMeshKernelId(); + + meshkernelapi::GeometryList geometryListIn; + geometryListIn.geometry_separator = meshkernel::constants::missing::doubleValue; + + std::vector xCoordinatesIn{76.251099, 498.503723, 505.253784, 76.251099}; + std::vector yCoordinatesIn{92.626556, 91.126541, 490.130554, 92.626556}; + std::vector valuesIn{0.0, 0.0, 0.0, 0.0}; + + geometryListIn.coordinates_x = xCoordinatesIn.data(); + geometryListIn.coordinates_y = yCoordinatesIn.data(); + geometryListIn.values = valuesIn.data(); + geometryListIn.num_coordinates = static_cast(xCoordinatesIn.size()); + + // Execute + meshkernelapi::GeometryList geometryListOut; + // Should fail due to the values not yet being cached. + auto errorCode = mkernel_polygon_refine(meshKernelId, geometryListIn, 0, 2, 40, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); + + int numberOfpolygonNodes; + // cache the values + errorCode = mkernel_polygon_count_refine(meshKernelId, geometryListIn, 0, 2, 40, numberOfpolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + geometryListOut.num_coordinates = numberOfpolygonNodes; + geometryListOut.geometry_separator = meshkernel::constants::missing::doubleValue; + std::vector xCoordinatesOut(numberOfpolygonNodes); + std::vector yCoordinatesOut(numberOfpolygonNodes); + std::vector valuesOut(numberOfpolygonNodes); + geometryListOut.coordinates_x = xCoordinatesOut.data(); + geometryListOut.coordinates_y = yCoordinatesOut.data(); + geometryListOut.values = valuesOut.data(); + errorCode = mkernel_polygon_refine(meshKernelId, geometryListIn, 0, 2, 40, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Should fail due to the values have been deleted in the last call + errorCode = mkernel_polygon_refine(meshKernelId, geometryListIn, 0, 2, 40, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); + + // re-cache the values + errorCode = mkernel_polygon_count_refine(meshKernelId, geometryListIn, 0, 2, 40, numberOfpolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Should fail due to different parameters + errorCode = mkernel_polygon_refine(meshKernelId, geometryListIn, 1, 2, 40, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // re-cache the values + errorCode = mkernel_polygon_count_refine(meshKernelId, geometryListIn, 0, 2, 40, numberOfpolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + // Should fail due to different parameters + errorCode = mkernel_polygon_refine(meshKernelId, geometryListIn, 0, 3, 40, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // re-cache the values + errorCode = mkernel_polygon_count_refine(meshKernelId, geometryListIn, 0, 2, 40, numberOfpolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + // Should fail due to different parameters + errorCode = mkernel_polygon_refine(meshKernelId, geometryListIn, 0, 2, 41, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); +} + TEST_F(CartesianApiTestFixture, LinearRefineAPolygonThroughApi) { // Prepare @@ -106,6 +173,61 @@ TEST_F(CartesianApiTestFixture, LinearRefineAPolygonThroughApi) ASSERT_NEAR(8.281492376, geometryListOut.coordinates_y[4], tolerance); } +TEST_F(CartesianApiTestFixture, LinearRefineAPolygonThroughApiFailures) +{ + // Prepare + MakeMesh(); + auto const meshKernelId = GetMeshKernelId(); + + meshkernelapi::GeometryList geometryListIn; + geometryListIn.geometry_separator = meshkernel::constants::missing::doubleValue; + + std::vector xCoordinatesIn{0.0, 1.0, 3.0, 11.0, 11.0, 0.0, 0.0}; + std::vector yCoordinatesIn{10.0, 10.0, 10.0, 10.0, 0.0, 0.0, 10.0}; + + geometryListIn.coordinates_x = xCoordinatesIn.data(); + geometryListIn.coordinates_y = yCoordinatesIn.data(); + geometryListIn.num_coordinates = static_cast(xCoordinatesIn.size()); + + // Execute + meshkernelapi::GeometryList geometryListOut; + auto errorCode = mkernel_polygon_linear_refine(meshKernelId, geometryListIn, 1, 4, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); + + int expectedNumberOfpolygonNodes; + // cache the values + errorCode = mkernel_polygon_count_linear_refine(meshKernelId, geometryListIn, 1, 4, expectedNumberOfpolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + geometryListOut.num_coordinates = expectedNumberOfpolygonNodes; + geometryListOut.geometry_separator = meshkernel::constants::missing::doubleValue; + std::vector xCoordinatesOut(expectedNumberOfpolygonNodes); + std::vector yCoordinatesOut(expectedNumberOfpolygonNodes); + geometryListOut.coordinates_x = xCoordinatesOut.data(); + geometryListOut.coordinates_y = yCoordinatesOut.data(); + + errorCode = mkernel_polygon_linear_refine(meshKernelId, geometryListIn, 1, 4, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Should fail due to the values have been deleted in the last call + errorCode = mkernel_polygon_linear_refine(meshKernelId, geometryListIn, 1, 4, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); + + // re-cache the values + errorCode = mkernel_polygon_count_linear_refine(meshKernelId, geometryListIn, 1, 4, expectedNumberOfpolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + // Should fail due to different parameters + errorCode = mkernel_polygon_linear_refine(meshKernelId, geometryListIn, 2, 4, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // re-cache the values + errorCode = mkernel_polygon_count_linear_refine(meshKernelId, geometryListIn, 1, 4, expectedNumberOfpolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + // Should fail due to different parameters + errorCode = mkernel_polygon_linear_refine(meshKernelId, geometryListIn, 1, 5, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); +} + TEST_F(CartesianApiTestFixture, RefineBasedOnSamplesWaveCourant_OnAUniformMesh_shouldRefineMesh) { // Prepare diff --git a/libs/MeshKernelApi/tests/src/Mesh2DTests.cpp b/libs/MeshKernelApi/tests/src/Mesh2DTests.cpp index b65690cc1..5cf9abc79 100644 --- a/libs/MeshKernelApi/tests/src/Mesh2DTests.cpp +++ b/libs/MeshKernelApi/tests/src/Mesh2DTests.cpp @@ -259,3 +259,91 @@ TEST(Mesh2DTests, GetPolygonsOfDeletedFaces_WithPolygon_ShouldGetPolygonOfDelete ASSERT_NEAR(expectedFacePolygonsY[i], yfacePolygons[i], 1e-6); } } + +TEST(Mesh2DTests, GetPolygonsOfDeletedFaces_WithPolygon_FailureTests) +{ + meshkernel::MakeGridParameters makeGridParameters; + + makeGridParameters.origin_x = 0.0; + makeGridParameters.origin_y = 0.0; + makeGridParameters.block_size_x = 1.0; + makeGridParameters.block_size_y = 1.0; + makeGridParameters.num_columns = 20; + makeGridParameters.num_rows = 20; + + int meshKernelId = 0; + const int projectionType = 0; + auto errorCode = meshkernelapi::mkernel_allocate_state(projectionType, meshKernelId); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + errorCode = meshkernelapi::mkernel_curvilinear_compute_rectangular_grid(meshKernelId, makeGridParameters); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + errorCode = mkapi::mkernel_curvilinear_convert_to_mesh2d(meshKernelId); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + // + + int propertyType = -1; + errorCode = meshkernelapi::mkernel_mesh2d_get_orthogonality_property_type(propertyType); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + double minValue = -1.0; + double maxValue = 1.0; + + meshkernelapi::GeometryList facePolygons{}; + + // face data not yet cached + errorCode = mkernel_mesh2d_get_filtered_face_polygons(meshKernelId, + propertyType + 1, + minValue, + maxValue, + facePolygons); + + // Assert + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // Execute + int geometryListDimension = -1; + errorCode = meshkernelapi::mkernel_mesh2d_get_filtered_face_polygons_dimension(meshKernelId, + propertyType, + minValue, + maxValue, + geometryListDimension); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + facePolygons.num_coordinates = geometryListDimension; + facePolygons.geometry_separator = meshkernel::constants::missing::doubleValue; + std::vector xfacePolygons(geometryListDimension); + std::vector yfacePolygons(geometryListDimension); + facePolygons.coordinates_x = xfacePolygons.data(); + facePolygons.coordinates_y = yfacePolygons.data(); + + // Check different property type + errorCode = mkernel_mesh2d_get_filtered_face_polygons(meshKernelId, + propertyType + 1, + minValue, + maxValue, + facePolygons); + + // Assert + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // Check different minimum value + errorCode = mkernel_mesh2d_get_filtered_face_polygons(meshKernelId, + propertyType, + minValue + 0.5, + maxValue, + facePolygons); + + // Assert + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + // Check different minimum value + errorCode = mkernel_mesh2d_get_filtered_face_polygons(meshKernelId, + propertyType, + minValue, + maxValue + 0.5, + facePolygons); + + // Assert + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); +}