From 93ca6a33c3e349b00a1c39d2e5bb0a0d19b52184 Mon Sep 17 00:00:00 2001 From: Michael Jackson Date: Wed, 28 Feb 2024 09:48:39 -0500 Subject: [PATCH] ENH: CombineSTLFiles-Add option to label the faces and vertices based on a file index (#873) Signed-off-by: Michael Jackson --- .../SimplnxCore/docs/CombineStlFilesFilter.md | 4 +- .../Filters/Algorithms/CombineStlFiles.cpp | 64 +++++++++++++++---- .../Filters/Algorithms/CombineStlFiles.hpp | 4 ++ .../Filters/CombineStlFilesFilter.cpp | 61 +++++++++++++++--- .../Filters/CombineStlFilesFilter.hpp | 4 ++ .../SimplnxCore/test/CombineStlFilesTest.cpp | 4 ++ 6 files changed, 118 insertions(+), 23 deletions(-) diff --git a/src/Plugins/SimplnxCore/docs/CombineStlFilesFilter.md b/src/Plugins/SimplnxCore/docs/CombineStlFilesFilter.md index e71d9a7aee..07b42224fb 100644 --- a/src/Plugins/SimplnxCore/docs/CombineStlFilesFilter.md +++ b/src/Plugins/SimplnxCore/docs/CombineStlFilesFilter.md @@ -2,12 +2,14 @@ ## Group (Subgroup) -AMProcessMonitoring (AMProcessMonitoring) +Reader/Input (AMProcessMonitoring) ## Description This **Filter** combines all of the STL files from a given directory into a single triangle geometry. This filter will make use of the **Import STL File Filter** to read in each stl file in the given directory and then will proceed to combine each of the imported files into a single triangle geometry. +There is an option to label the triangles and vertices with the "index" of the file that was read. This would be based on the lexographical index and starts from 1. This allows for the immediate "segmentation" of the resulting triangle geometry or just as a convenience to "color by" in the visualization widget. + % Auto generated parameter table will be inserted here ## Example Pipelines diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/CombineStlFiles.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/CombineStlFiles.cpp index d64d358391..51b504295b 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/CombineStlFiles.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/CombineStlFiles.cpp @@ -8,6 +8,7 @@ #include "simplnx/Utilities/ParallelTaskAlgorithm.hpp" #include "simplnx/Utilities/StringUtilities.hpp" +#include #include namespace fs = std::filesystem; @@ -116,17 +117,21 @@ Result<> CombineStlFiles::operator()() } } - // Sort the Goemetries in lexigraphical order based on the name of the geometry which should + // Sort the Geometries in lexicographical order based on the name of the geometry which should // match the file name. We do this because some file systems do not iterate through the directory - // contents in lexigraphical order (GitHub CI) + // contents in lexicographical order (GitHub CI) std::sort(stlGeometries.begin(), stlGeometries.end(), [](const auto& lhs, const auto& rhs) { return lhs->getName() < rhs->getName(); }); auto& combinedGeom = m_DataStructure.getDataRefAs(m_InputValues->TriangleDataContainerName); auto* combinedFaceAM = combinedGeom.getFaceAttributeMatrix(); auto& combinedFaceNormals = m_DataStructure.getDataRefAs(m_InputValues->FaceNormalsArrayName); + auto* combinedVertexAM = combinedGeom.getVertexAttributeMatrix(); + + // Make sure all arrays and Attribute Matrix are sized correctly. combinedGeom.resizeFaceList(totalTriangles); combinedGeom.resizeVertexList(totalVertices); combinedFaceAM->resizeTuples(std::vector{totalTriangles}); + combinedVertexAM->resizeTuples(std::vector{totalVertices}); usize triOffset = 0; usize vertexOffset = 0; @@ -135,29 +140,60 @@ Result<> CombineStlFiles::operator()() INodeGeometry2D::SharedFaceList& triangles = combinedGeom.getFacesRef(); INodeGeometry0D::SharedVertexList& vertices = combinedGeom.getVerticesRef(); ParallelTaskAlgorithm taskRunner; - for(auto* geom : stlGeometries) + int32 fileIndex = 1; + usize faceLabelOffset = 0; + usize vertexLabelOffset = 0; + + // Loop over each temp geometry and copy the data into the destination geometry + for(auto* currentGeometry : stlGeometries) { if(getCancel()) { return {}; } - INodeGeometry2D::SharedFaceList& curTriangles = geom->getFacesRef(); - for(usize t = 0; t < geom->getNumberOfFaces(); t++) + INodeGeometry2D::SharedFaceList& currentSharedFaceList = currentGeometry->getFacesRef(); + usize currentGeomNumTriangles = currentGeometry->getNumberOfFaces(); + usize currentGeomNumVertices = currentGeometry->getNumberOfVertices(); + for(usize triIndex = 0; triIndex < currentGeomNumTriangles; triIndex++) + { + currentSharedFaceList[3 * triIndex + 0] += triCounter; + currentSharedFaceList[3 * triIndex + 1] += triCounter; + currentSharedFaceList[3 * triIndex + 2] += triCounter; + } + triCounter += currentGeomNumVertices; + INodeGeometry0D::SharedVertexList& curVertices = currentGeometry->getVerticesRef(); + auto& curFaceNormals = tempDataStructure.getDataRefAs(currentGeometry->getFaceAttributeMatrixDataPath().createChildPath("Face Normals")); + + if(m_InputValues->LabelFaces) + { + auto& faceLabels = m_DataStructure.getDataRefAs(m_InputValues->FaceFileIndexArrayPath); + // for(usize tuple = faceLabelOffset; tuple < faceLabelOffset + currentGeomNumTriangles; tuple++) + // { + // faceLabels[tuple] = fileIndex; + // } + std::fill(faceLabels.begin() + faceLabelOffset, faceLabels.begin() + faceLabelOffset + currentGeomNumTriangles, fileIndex); + } + + faceLabelOffset += currentGeomNumTriangles; + + if(m_InputValues->LabelVertices) { - curTriangles[3 * t + 0] += triCounter; - curTriangles[3 * t + 1] += triCounter; - curTriangles[3 * t + 2] += triCounter; + auto& vertexLabels = m_DataStructure.getDataRefAs(m_InputValues->VertexFileIndexArrayPath); + // for(usize tuple = vertexLabelOffset; tuple < vertexLabelOffset + currentGeomNumVertices; tuple++) + // { + // vertexLabels[tuple] = fileIndex; + // } + std::fill(vertexLabels.begin() + vertexLabelOffset, vertexLabels.begin() + vertexLabelOffset + currentGeomNumVertices, fileIndex); } - triCounter += geom->getNumberOfVertices(); - INodeGeometry0D::SharedVertexList& curVertices = geom->getVerticesRef(); - auto& curFaceNormals = tempDataStructure.getDataRefAs(geom->getFaceAttributeMatrixDataPath().createChildPath("Face Normals")); + vertexLabelOffset += currentGeomNumVertices; - taskRunner.execute(CombineStlImpl{triangles, vertices, combinedFaceNormals, curTriangles, curVertices, curFaceNormals, triOffset, vertexOffset, faceNormalsOffset}); + taskRunner.execute(CombineStlImpl{triangles, vertices, combinedFaceNormals, currentSharedFaceList, curVertices, curFaceNormals, triOffset, vertexOffset, faceNormalsOffset}); - triOffset += geom->getNumberOfFaces() * 3; - vertexOffset += geom->getNumberOfVertices() * 3; + triOffset += currentGeomNumTriangles * 3; + vertexOffset += currentGeomNumVertices * 3; faceNormalsOffset += curFaceNormals.getSize(); + fileIndex++; } taskRunner.wait(); // This will spill over if the number of geometries to processes does not divide evenly by the number of threads. diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/CombineStlFiles.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/CombineStlFiles.hpp index 7891c572f9..769e00c586 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/CombineStlFiles.hpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/CombineStlFiles.hpp @@ -17,6 +17,10 @@ struct SIMPLNXCORE_EXPORT CombineStlFilesInputValues DataPath TriangleDataContainerName; DataPath FaceAttributeMatrixName; DataPath FaceNormalsArrayName; + DataPath FaceFileIndexArrayPath; + bool LabelFaces; + DataPath VertexFileIndexArrayPath; + bool LabelVertices; }; /** diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CombineStlFilesFilter.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CombineStlFilesFilter.cpp index 99dc52fe85..54eadb2881 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CombineStlFilesFilter.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CombineStlFilesFilter.cpp @@ -6,12 +6,13 @@ #include "simplnx/DataStructure/Geometry/TriangleGeom.hpp" #include "simplnx/Filter/Actions/CreateArrayAction.hpp" #include "simplnx/Filter/Actions/CreateGeometry2DAction.hpp" +#include "simplnx/Parameters/ArrayCreationParameter.hpp" +#include "simplnx/Parameters/BoolParameter.hpp" #include "simplnx/Parameters/DataGroupCreationParameter.hpp" #include "simplnx/Parameters/DataObjectNameParameter.hpp" #include "simplnx/Parameters/FileSystemPathParameter.hpp" -#include "simplnx/Utilities/StringUtilities.hpp" - #include "simplnx/Utilities/SIMPLConversion.hpp" +#include "simplnx/Utilities/StringUtilities.hpp" #include namespace fs = std::filesystem; @@ -59,6 +60,15 @@ Parameters CombineStlFilesFilter::parameters() const params.insertSeparator(Parameters::Separator{"Input Parameters"}); params.insert(std::make_unique(k_StlFilesPath_Key, "Path to STL Files", "The path to the folder containing all the STL files to be combined", fs::path(""), FileSystemPathParameter::ExtensionsType{}, FileSystemPathParameter::PathType::InputDir)); + + params.insertLinkableParameter(std::make_unique(k_LabelFaces_Key, "Label Triangles", "When true, each triangle will get an index associated with the index of the STL file", true)); + params.insert(std::make_unique(k_FaceLabelName_Key, "Created Face Labels", "The name of the face labels data array", "FileIndex")); + params.linkParameters(k_LabelFaces_Key, k_FaceLabelName_Key, true); + + params.insertLinkableParameter(std::make_unique(k_LabelVertices_Key, "Label Vertices", "When true, each vertex will get an index associated with the index of the STL file", true)); + params.insert(std::make_unique(k_VertexLabelName_Key, "Created Vertex Labels", "The name of the vertex labels data array", "FileIndex")); + params.linkParameters(k_LabelVertices_Key, k_VertexLabelName_Key, true); + params.insertSeparator(Parameters::Separator{"Created Data"}); params.insert(std::make_unique(k_TriangleDataContainerName_Key, "Triangle Geometry", "The path to the triangle geometry to be created from the combined STL files", DataPath({"TriangleGeometry"}))); @@ -90,6 +100,12 @@ IFilter::PreflightResult CombineStlFilesFilter::preflightImpl(const DataStructur auto pFaceNormalsArrayNameValue = filterArgs.value(k_FaceNormalsArrayName_Key); auto pVertexAttributeMatrixNameValue = filterArgs.value(k_VertexAttributeMatrixName_Key); + auto createFaceLabels = filterArgs.value(k_LabelFaces_Key); + auto faceLabelsName = filterArgs.value(k_FaceLabelName_Key); + + auto createVertexLabels = filterArgs.value(k_LabelVertices_Key); + auto vertexLabelsName = filterArgs.value(k_VertexLabelName_Key); + PreflightResult preflightResult; nx::core::Result resultOutputActions; std::vector preflightUpdatedValues; @@ -107,13 +123,34 @@ IFilter::PreflightResult CombineStlFilesFilter::preflightImpl(const DataStructur return MakePreflightErrorResult(-9370, fmt::format("No STL files were found in the selected directory '{}'", pStlFilesPathValue.string())); } - auto createTriangleGeometryAction = std::make_unique(pTriangleDataContainerNameValue, 1, 1, pVertexAttributeMatrixNameValue, pFaceAttributeMatrixNameValue, - CreateTriangleGeometryAction::k_DefaultVerticesName, CreateTriangleGeometryAction::k_DefaultFacesName); - auto faceNormalsPath = createTriangleGeometryAction->getFaceDataPath().createChildPath(pFaceNormalsArrayNameValue); - resultOutputActions.value().appendAction(std::move(createTriangleGeometryAction)); + { + auto createTriangleGeometryAction = std::make_unique(pTriangleDataContainerNameValue, 1, 1, pVertexAttributeMatrixNameValue, pFaceAttributeMatrixNameValue, + CreateTriangleGeometryAction::k_DefaultVerticesName, CreateTriangleGeometryAction::k_DefaultFacesName); + resultOutputActions.value().appendAction(std::move(createTriangleGeometryAction)); + } + DataPath faceAttributeMatrixDataPath = pTriangleDataContainerNameValue.createChildPath(pFaceAttributeMatrixNameValue); + // Create the Triangle Normals path + { + auto facePath = faceAttributeMatrixDataPath.createChildPath(pFaceNormalsArrayNameValue); + auto createArrayAction = std::make_unique(nx::core::DataType::float64, std::vector{1}, std::vector{3}, facePath); + resultOutputActions.value().appendAction(std::move(createArrayAction)); + } - auto createArrayAction = std::make_unique(nx::core::DataType::float64, std::vector{1}, std::vector{3}, faceNormalsPath); - resultOutputActions.value().appendAction(std::move(createArrayAction)); + // If the user wants to label the faces + if(createFaceLabels) + { + auto facePath = faceAttributeMatrixDataPath.createChildPath(faceLabelsName); + auto createArrayAction = std::make_unique(nx::core::DataType::uint32, std::vector{1}, std::vector{1}, facePath); + resultOutputActions.value().appendAction(std::move(createArrayAction)); + } + + // If the user wants to label the vertices + if(createVertexLabels) + { + auto vertexPath = pTriangleDataContainerNameValue.createChildPath(pVertexAttributeMatrixNameValue).createChildPath(vertexLabelsName); + auto createArrayAction = std::make_unique(nx::core::DataType::uint32, std::vector{1}, std::vector{1}, vertexPath); + resultOutputActions.value().appendAction(std::move(createArrayAction)); + } return {std::move(resultOutputActions), std::move(preflightUpdatedValues)}; } @@ -122,6 +159,8 @@ IFilter::PreflightResult CombineStlFilesFilter::preflightImpl(const DataStructur Result<> CombineStlFilesFilter::executeImpl(DataStructure& dataStructure, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel) const { + auto pVertexAttributeMatrixNameValue = filterArgs.value(k_VertexAttributeMatrixName_Key); + CombineStlFilesInputValues inputValues; inputValues.StlFilesPath = filterArgs.value(k_StlFilesPath_Key); @@ -129,6 +168,12 @@ Result<> CombineStlFilesFilter::executeImpl(DataStructure& dataStructure, const inputValues.FaceAttributeMatrixName = inputValues.TriangleDataContainerName.createChildPath(filterArgs.value(k_FaceAttributeMatrixName_Key)); inputValues.FaceNormalsArrayName = inputValues.FaceAttributeMatrixName.createChildPath(filterArgs.value(k_FaceNormalsArrayName_Key)); + inputValues.LabelFaces = filterArgs.value(k_LabelFaces_Key); + inputValues.FaceFileIndexArrayPath = inputValues.FaceAttributeMatrixName.createChildPath(filterArgs.value(k_FaceLabelName_Key)); + + inputValues.LabelVertices = filterArgs.value(k_LabelVertices_Key); + inputValues.VertexFileIndexArrayPath = DataPath({inputValues.TriangleDataContainerName.getTargetName(), pVertexAttributeMatrixNameValue, filterArgs.value(k_VertexLabelName_Key)}); + return CombineStlFiles(dataStructure, messageHandler, shouldCancel, &inputValues)(); } diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CombineStlFilesFilter.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CombineStlFilesFilter.hpp index 331e6e6168..8f38951a3c 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CombineStlFilesFilter.hpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CombineStlFilesFilter.hpp @@ -29,6 +29,10 @@ class SIMPLNXCORE_EXPORT CombineStlFilesFilter : public IFilter static inline constexpr StringLiteral k_FaceAttributeMatrixName_Key = "face_attribute_matrix_name"; static inline constexpr StringLiteral k_FaceNormalsArrayName_Key = "face_normals_array_name"; static inline constexpr StringLiteral k_VertexAttributeMatrixName_Key = "vertex_attribute_matrix_name"; + static inline constexpr StringLiteral k_LabelFaces_Key = "label_faces"; + static inline constexpr StringLiteral k_FaceLabelName_Key = "face_label_name"; + static inline constexpr StringLiteral k_LabelVertices_Key = "label_vertices"; + static inline constexpr StringLiteral k_VertexLabelName_Key = "vertex_label_name"; /** * @brief Reads SIMPL json and converts it simplnx Arguments. diff --git a/src/Plugins/SimplnxCore/test/CombineStlFilesTest.cpp b/src/Plugins/SimplnxCore/test/CombineStlFilesTest.cpp index 5f51b1efb2..d0bffcc9fe 100644 --- a/src/Plugins/SimplnxCore/test/CombineStlFilesTest.cpp +++ b/src/Plugins/SimplnxCore/test/CombineStlFilesTest.cpp @@ -40,6 +40,10 @@ TEST_CASE("SimplnxCore::CombineStlFilesFilter: Valid Filter Execution", "[Simpln args.insertOrAssign(CombineStlFilesFilter::k_FaceAttributeMatrixName_Key, std::make_any(k_FaceData)); args.insertOrAssign(CombineStlFilesFilter::k_FaceNormalsArrayName_Key, std::make_any("Face Normals")); args.insertOrAssign(CombineStlFilesFilter::k_VertexAttributeMatrixName_Key, std::make_any(k_VertexData)); + args.insertOrAssign(CombineStlFilesFilter::k_LabelFaces_Key, std::make_any(true)); + args.insertOrAssign(CombineStlFilesFilter::k_FaceLabelName_Key, std::make_any("File Index")); + args.insertOrAssign(CombineStlFilesFilter::k_LabelVertices_Key, std::make_any(true)); + args.insertOrAssign(CombineStlFilesFilter::k_VertexLabelName_Key, std::make_any("File Index")); // Preflight the filter and check result auto preflightResult = filter.preflight(dataStructure, args);