Skip to content

Commit

Permalink
ENH: CombineSTLFiles-Add option to label the faces and vertices based…
Browse files Browse the repository at this point in the history
… on a file index (#873)

Signed-off-by: Michael Jackson <[email protected]>
  • Loading branch information
imikejackson authored Feb 28, 2024
1 parent 5340383 commit 93ca6a3
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 23 deletions.
4 changes: 3 additions & 1 deletion src/Plugins/SimplnxCore/docs/CombineStlFilesFilter.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "simplnx/Utilities/ParallelTaskAlgorithm.hpp"
#include "simplnx/Utilities/StringUtilities.hpp"

#include <algorithm>
#include <filesystem>
namespace fs = std::filesystem;

Expand Down Expand Up @@ -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<TriangleGeom>(m_InputValues->TriangleDataContainerName);
auto* combinedFaceAM = combinedGeom.getFaceAttributeMatrix();
auto& combinedFaceNormals = m_DataStructure.getDataRefAs<Float64Array>(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<usize>{totalTriangles});
combinedVertexAM->resizeTuples(std::vector<usize>{totalVertices});

usize triOffset = 0;
usize vertexOffset = 0;
Expand All @@ -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<Float64Array>(currentGeometry->getFaceAttributeMatrixDataPath().createChildPath("Face Normals"));

if(m_InputValues->LabelFaces)
{
auto& faceLabels = m_DataStructure.getDataRefAs<UInt32Array>(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<UInt32Array>(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<Float64Array>(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.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ struct SIMPLNXCORE_EXPORT CombineStlFilesInputValues
DataPath TriangleDataContainerName;
DataPath FaceAttributeMatrixName;
DataPath FaceNormalsArrayName;
DataPath FaceFileIndexArrayPath;
bool LabelFaces;
DataPath VertexFileIndexArrayPath;
bool LabelVertices;
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <filesystem>
namespace fs = std::filesystem;
Expand Down Expand Up @@ -59,6 +60,15 @@ Parameters CombineStlFilesFilter::parameters() const
params.insertSeparator(Parameters::Separator{"Input Parameters"});
params.insert(std::make_unique<FileSystemPathParameter>(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<BoolParameter>(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<DataObjectNameParameter>(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<BoolParameter>(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<DataObjectNameParameter>(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<DataGroupCreationParameter>(k_TriangleDataContainerName_Key, "Triangle Geometry", "The path to the triangle geometry to be created from the combined STL files",
DataPath({"TriangleGeometry"})));
Expand Down Expand Up @@ -90,6 +100,12 @@ IFilter::PreflightResult CombineStlFilesFilter::preflightImpl(const DataStructur
auto pFaceNormalsArrayNameValue = filterArgs.value<std::string>(k_FaceNormalsArrayName_Key);
auto pVertexAttributeMatrixNameValue = filterArgs.value<std::string>(k_VertexAttributeMatrixName_Key);

auto createFaceLabels = filterArgs.value<BoolParameter::ValueType>(k_LabelFaces_Key);
auto faceLabelsName = filterArgs.value<std::string>(k_FaceLabelName_Key);

auto createVertexLabels = filterArgs.value<BoolParameter::ValueType>(k_LabelVertices_Key);
auto vertexLabelsName = filterArgs.value<std::string>(k_VertexLabelName_Key);

PreflightResult preflightResult;
nx::core::Result<OutputActions> resultOutputActions;
std::vector<PreflightValue> preflightUpdatedValues;
Expand All @@ -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<CreateTriangleGeometryAction>(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<CreateTriangleGeometryAction>(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<CreateArrayAction>(nx::core::DataType::float64, std::vector<usize>{1}, std::vector<usize>{3}, facePath);
resultOutputActions.value().appendAction(std::move(createArrayAction));
}

auto createArrayAction = std::make_unique<CreateArrayAction>(nx::core::DataType::float64, std::vector<usize>{1}, std::vector<usize>{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<CreateArrayAction>(nx::core::DataType::uint32, std::vector<usize>{1}, std::vector<usize>{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<CreateArrayAction>(nx::core::DataType::uint32, std::vector<usize>{1}, std::vector<usize>{1}, vertexPath);
resultOutputActions.value().appendAction(std::move(createArrayAction));
}

return {std::move(resultOutputActions), std::move(preflightUpdatedValues)};
}
Expand All @@ -122,13 +159,21 @@ 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<std::string>(k_VertexAttributeMatrixName_Key);

CombineStlFilesInputValues inputValues;

inputValues.StlFilesPath = filterArgs.value<FileSystemPathParameter::ValueType>(k_StlFilesPath_Key);
inputValues.TriangleDataContainerName = filterArgs.value<DataPath>(k_TriangleDataContainerName_Key);
inputValues.FaceAttributeMatrixName = inputValues.TriangleDataContainerName.createChildPath(filterArgs.value<std::string>(k_FaceAttributeMatrixName_Key));
inputValues.FaceNormalsArrayName = inputValues.FaceAttributeMatrixName.createChildPath(filterArgs.value<std::string>(k_FaceNormalsArrayName_Key));

inputValues.LabelFaces = filterArgs.value<BoolParameter::ValueType>(k_LabelFaces_Key);
inputValues.FaceFileIndexArrayPath = inputValues.FaceAttributeMatrixName.createChildPath(filterArgs.value<std::string>(k_FaceLabelName_Key));

inputValues.LabelVertices = filterArgs.value<BoolParameter::ValueType>(k_LabelVertices_Key);
inputValues.VertexFileIndexArrayPath = DataPath({inputValues.TriangleDataContainerName.getTargetName(), pVertexAttributeMatrixNameValue, filterArgs.value<std::string>(k_VertexLabelName_Key)});

return CombineStlFiles(dataStructure, messageHandler, shouldCancel, &inputValues)();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions src/Plugins/SimplnxCore/test/CombineStlFilesTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ TEST_CASE("SimplnxCore::CombineStlFilesFilter: Valid Filter Execution", "[Simpln
args.insertOrAssign(CombineStlFilesFilter::k_FaceAttributeMatrixName_Key, std::make_any<std::string>(k_FaceData));
args.insertOrAssign(CombineStlFilesFilter::k_FaceNormalsArrayName_Key, std::make_any<std::string>("Face Normals"));
args.insertOrAssign(CombineStlFilesFilter::k_VertexAttributeMatrixName_Key, std::make_any<std::string>(k_VertexData));
args.insertOrAssign(CombineStlFilesFilter::k_LabelFaces_Key, std::make_any<bool>(true));
args.insertOrAssign(CombineStlFilesFilter::k_FaceLabelName_Key, std::make_any<std::string>("File Index"));
args.insertOrAssign(CombineStlFilesFilter::k_LabelVertices_Key, std::make_any<bool>(true));
args.insertOrAssign(CombineStlFilesFilter::k_VertexLabelName_Key, std::make_any<std::string>("File Index"));

// Preflight the filter and check result
auto preflightResult = filter.preflight(dataStructure, args);
Expand Down

0 comments on commit 93ca6a3

Please sign in to comment.