Skip to content

Commit

Permalink
Add an API to retrieve the polygons of selected faces, filtered by pr…
Browse files Browse the repository at this point in the history
…operty (#367| GRIDEDIT-1410)
  • Loading branch information
lucacarniato authored Sep 2, 2024
1 parent fc0601d commit e2c3ff7
Show file tree
Hide file tree
Showing 6 changed files with 297 additions and 27 deletions.
19 changes: 17 additions & 2 deletions libs/MeshKernel/include/MeshKernel/Mesh2D.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ namespace meshkernel
other
};

/// Enumerator for different filtering properties on a 2D mesh
enum class Property
{
Orthogonality = 0
};

/// @brief Default destructor
~Mesh2D() override = default;

Expand Down Expand Up @@ -224,11 +230,11 @@ namespace meshkernel

/// @brief Get the orthogonality values, the inner product of edges and segments connecting the face circumcenters
/// @return The edge orthogonality
[[nodiscard]] std::vector<double> GetOrthogonality();
[[nodiscard]] std::vector<double> GetOrthogonality() const;

/// @brief Gets the smoothness values, ratios of the face areas
/// @return The smoothness at the edges
[[nodiscard]] std::vector<double> GetSmoothness();
[[nodiscard]] std::vector<double> GetSmoothness() const;

/// @brief Gets the aspect ratios (the ratios edges lengths to flow edges lengths)
/// @param[in,out] aspectRatios The aspect ratios (passed as reference to avoid re-allocation)
Expand Down Expand Up @@ -288,6 +294,15 @@ namespace meshkernel
/// @param[in] invertDeletion Inverts the selected node to delete (instead of outside the polygon, inside the polygon)
[[nodiscard]] std::unique_ptr<UndoAction> DeleteMesh(const Polygons& polygon, DeleteMeshOptions deletionOption, bool invertDeletion);

/// @brief This method generates a mask indicating which locations are within the specified range of the given metric.
///
/// @param[in] location The location representing the location where to filter the object.
/// @param[in] property The property by which to filter locations.
/// @param[in] minValue The minimum value of the metric for filtering.
/// @param[in] maxValue The maximum value of the metric for filtering.
/// @ return A vector of boolean values. Each element corresponds to a location and is `true` if the location's metric is within the specified range, and `false` otherwise.
[[nodiscard]] std::vector<bool> FilterBasedOnMetric(Location location, Property property, double minValue, double maxValue) const;

/// @brief Inquire if a segment is crossing a face
/// @param[in] firstPoint The first point of the segment
/// @param[in] secondPoint The second point of the segment
Expand Down
52 changes: 50 additions & 2 deletions libs/MeshKernel/src/Mesh2D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1158,7 +1158,7 @@ void Mesh2D::ComputeNodeNeighbours()
}
}

std::vector<double> Mesh2D::GetOrthogonality()
std::vector<double> Mesh2D::GetOrthogonality() const
{
std::vector<double> result(GetNumEdges());
const auto numEdges = GetNumEdges();
Expand Down Expand Up @@ -1191,7 +1191,7 @@ std::vector<double> Mesh2D::GetOrthogonality()
return result;
}

std::vector<double> Mesh2D::GetSmoothness()
std::vector<double> Mesh2D::GetSmoothness() const
{
std::vector<double> result(GetNumEdges());
const auto numEdges = GetNumEdges();
Expand Down Expand Up @@ -1634,6 +1634,54 @@ std::vector<meshkernel::UInt> Mesh2D::GetHangingEdges() const
return result;
}

std::vector<bool> Mesh2D::FilterBasedOnMetric(Location location,
Property property,
double minValue,
double maxValue) const
{
if (location != Location::Faces)
{
throw ConstraintError("Unsupported location. Only location faces is supported");
}
if (property != Property::Orthogonality)
{
throw ConstraintError("Unsupported metric. Only orthogonality metric is supported");
}

const auto numFaces = GetNumFaces();

// Pre-allocate memory for result vector and set all elements to false
std::vector<bool> result(numFaces, false);

// Retrieve orthogonality values
const std::vector<double> metricValues = GetOrthogonality();

// Loop through faces and compute how many edges have the metric within the range
for (UInt f = 0; f < numFaces; ++f)
{
const UInt numFaceEdges = GetNumFaceEdges(f);
UInt numEdgesFiltered = 0;
for (UInt e = 0; e < numFaceEdges; ++e)
{
const auto edge = m_facesEdges[f][e];
const double metricValue = metricValues[edge];
if (metricValue < minValue || metricValue > maxValue)
{
break;
}
numEdgesFiltered = numEdgesFiltered + 1;
}

// If all edges have the metric within the range, the face is masked
if (numEdgesFiltered == numFaceEdges)
{
result[f] = true;
}
}

return result;
}

std::unique_ptr<meshkernel::UndoAction> Mesh2D::DeleteMesh(const Polygons& polygon, DeleteMeshOptions deletionOption, bool invertDeletion)
{
if (deletionOption == FacesWithIncludedCircumcenters)
Expand Down
38 changes: 38 additions & 0 deletions libs/MeshKernelApi/include/MeshKernelApi/MeshKernel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1182,6 +1182,11 @@ namespace meshkernelapi
/// @returns Error code
MKERNEL_API int mkernel_mesh2d_get_data(int meshKernelId, Mesh2D& mesh2d);

/// @brief Gets an int indicating the orthogonality property type for mesh2d
/// @param[out] type The int indicating the orthogonality property type
/// @returns Error code
MKERNEL_API int mkernel_mesh2d_get_orthogonality_property_type(int& type);

/// @brief Gets only the node and edge Mesh2D data
///
/// This function ought to be called after `mkernel_mesh2d_get_dimensions` has been called
Expand Down Expand Up @@ -1243,6 +1248,39 @@ namespace meshkernelapi
/// @returns Error code
MKERNEL_API int mkernel_mesh2d_get_face_polygons_dimension(int meshKernelId, int numEdges, int& geometryListDimension);

/// @brief Retrieves the dimension of the geometry list containing the face polygons within the filtering range.
///
/// This function filters face polygons within a mesh based on a specific property and location,
/// applying a range filter (minimum and maximum values). It then returns the dimension of the
/// filtered geometry list.
///
/// @param[in] meshKernelId The identifier of the mesh kernel or mesh state.
/// @param[in] propertyValue The property used to filter the locations.
/// @param[in] minValue The minimum value of the property.
/// @param[in] maxValue The maximum value of the property.
/// @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.
MKERNEL_API int mkernel_mesh2d_get_filtered_face_polygons_dimension(int meshKernelId,
int propertyValue,
double minValue,
double maxValue,
int& geometryListDimension);

/// @brief Gets the geometry list containing the face polygons within the filtering range
///
/// @param[in] meshKernelId The identifier of the mesh kernel or mesh state.
/// @param[in] propertyValue The property used to filter the locations.
/// @param[in] minValue The minimum value of the property.
/// @param[in] maxValue The maximum value of the property.
/// @param[out] facePolygons The geometry list containing the filtered locations.
/// @returns Error code
MKERNEL_API int mkernel_mesh2d_get_filtered_face_polygons(int meshKernelId,
int propertyValue,
double minValue,
double maxValue,
const GeometryList& facePolygons);

/// @brief Gets the mesh location closet to a specific coordinate.
/// @param[in] meshKernelId The id of the mesh state
/// @param[in] xCoordinate The input xCoordinate
Expand Down
34 changes: 34 additions & 0 deletions libs/MeshKernelApi/include/MeshKernelApi/Utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -583,4 +583,38 @@ namespace meshkernelapi
}
}

static void FillFacePolygons(std::shared_ptr<meshkernel::Mesh2D> mesh2d,
const std::vector<bool>& facesInPolygon,
const GeometryList& facePolygons)
{
meshkernel::UInt count = 0;
for (meshkernel::UInt f = 0; f < mesh2d->GetNumFaces(); ++f)
{
if (!facesInPolygon[f])
{
continue;
}

const auto& faceNodes = mesh2d->m_facesNodes[f];
if (count != 0)
{
facePolygons.coordinates_x[count] = missing::doubleValue;
facePolygons.coordinates_y[count] = missing::doubleValue;
count++;
}

for (meshkernel::UInt n = 0u; n < faceNodes.size(); ++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]);
facePolygons.coordinates_x[count] = currentNode.x;
facePolygons.coordinates_y[count] = currentNode.y;
count++;
}
}

} // namespace meshkernelapi
108 changes: 87 additions & 21 deletions libs/MeshKernelApi/src/MeshKernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,13 @@ namespace meshkernelapi
return lastExitCode;
}

MKERNEL_API int mkernel_mesh2d_get_orthogonality_property_type(int& type)
{
lastExitCode = meshkernel::ExitCode::Success;
type = static_cast<int>(meshkernel::Mesh2D::Property::Orthogonality);
return lastExitCode;
}

MKERNEL_API int mkernel_mesh1d_get_dimensions(int meshKernelId, Mesh1D& mesh1d)
{
lastExitCode = meshkernel::ExitCode::Success;
Expand Down Expand Up @@ -2320,34 +2327,17 @@ namespace meshkernelapi
}
meshKernelState[meshKernelId].m_mesh2d->Administrate();
const auto numFaces = meshKernelState[meshKernelId].m_mesh2d->GetNumFaces();
meshkernel::UInt count = 0;
std::vector<bool> validFace(numFaces, false);
for (meshkernel::UInt f = 0; f < numFaces; ++f)
{
const auto& faceNodes = meshKernelState[meshKernelId].m_mesh2d->m_facesNodes[f];
const auto faceNumEdges = static_cast<int>(faceNodes.size());
if (faceNumEdges != numEdges)
{
continue;
}
if (count != 0)
{
facePolygons.coordinates_x[count] = missing::doubleValue;
facePolygons.coordinates_y[count] = missing::doubleValue;
count++;
}

for (meshkernel::UInt n = 0u; n < faceNodes.size(); ++n)
if (faceNumEdges == numEdges)
{
const auto& currentNode = meshKernelState[meshKernelId].m_mesh2d->Node(faceNodes[n]);
facePolygons.coordinates_x[count] = currentNode.x;
facePolygons.coordinates_y[count] = currentNode.y;
count++;
validFace[f] = true;
}
const auto& currentNode = meshKernelState[meshKernelId].m_mesh2d->Node(faceNodes[0]);
facePolygons.coordinates_x[count] = currentNode.x;
facePolygons.coordinates_y[count] = currentNode.y;
count++;
}
FillFacePolygons(meshKernelState[meshKernelId].m_mesh2d, validFace, facePolygons);
}
catch (...)
{
Expand Down Expand Up @@ -2389,6 +2379,82 @@ namespace meshkernelapi
return lastExitCode;
}

MKERNEL_API int mkernel_mesh2d_get_filtered_face_polygons_dimension(int meshKernelId,
int propertyValue,
double minValue,
double maxValue,
int& geometryListDimension)
{
lastExitCode = meshkernel::ExitCode::Success;
try
{
if (!meshKernelState.contains(meshKernelId))
{
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.");
}
const auto filterEnum = static_cast<meshkernel::Mesh2D::Property>(propertyValue);
const auto filterMask = meshKernelState[meshKernelId].m_mesh2d->FilterBasedOnMetric(meshkernel::Location::Faces,
filterEnum,
minValue,
maxValue);
geometryListDimension = 0;
for (meshkernel::UInt f = 0; f < filterMask.size(); ++f)
{
if (!filterMask[f])
{
continue;
}
const auto faceNumEdges = static_cast<int>(meshKernelState[meshKernelId].m_mesh2d->m_facesNodes[f].size());
geometryListDimension += faceNumEdges + 2;
}
if (geometryListDimension > 0)
{
geometryListDimension -= 1;
}
}
catch (...)
{
lastExitCode = HandleException();
}
return lastExitCode;
}

MKERNEL_API int mkernel_mesh2d_get_filtered_face_polygons(int meshKernelId,
int propertyValue,
double minValue,
double maxValue,
const GeometryList& facePolygons)
{
lastExitCode = meshkernel::ExitCode::Success;
try
{
if (!meshKernelState.contains(meshKernelId))
{
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.");
}

const auto filterEnum = static_cast<meshkernel::Mesh2D::Property>(propertyValue);
const auto filterMask = meshKernelState[meshKernelId].m_mesh2d->FilterBasedOnMetric(meshkernel::Location::Faces,
filterEnum,
minValue,
maxValue);
FillFacePolygons(meshKernelState[meshKernelId].m_mesh2d, filterMask, facePolygons);
}
catch (...)
{
lastExitCode = HandleException();
}
return lastExitCode;
}

MKERNEL_API int mkernel_polygon_get_offset(int meshKernelId, const GeometryList& geometryListIn, int inWard, double distance, GeometryList& geometryListOut)
{
lastExitCode = meshkernel::ExitCode::Success;
Expand Down
Loading

0 comments on commit e2c3ff7

Please sign in to comment.