Skip to content

Commit

Permalink
Add RemoveFlaggedEdges filter
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Jackson <[email protected]>
  • Loading branch information
imikejackson committed Jun 3, 2024
1 parent b33bc87 commit 1d38947
Show file tree
Hide file tree
Showing 8 changed files with 738 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/Plugins/SimplnxCore/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ set(FilterList
RegularGridSampleSurfaceMeshFilter
RemoveFlaggedFeaturesFilter
RemoveFlaggedTrianglesFilter
RemoveFlaggedEdgesFilter
RemoveFlaggedVerticesFilter
RequireMinimumSizeFeaturesFilter
RenameDataObjectFilter
Expand Down Expand Up @@ -194,6 +195,7 @@ set(AlgorithmList
RegularGridSampleSurfaceMesh
RemoveFlaggedFeatures
RemoveFlaggedTriangles
RemoveFlaggedEdges
ReplaceElementAttributesWithNeighborValues
ResampleImageGeom
ResampleRectGridToImageGeom
Expand Down
38 changes: 38 additions & 0 deletions src/Plugins/SimplnxCore/docs/RemoveFlaggedEdgesFilter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Remove Flagged Triangles

## Group (Subgroup)

Surface Meshing (Misc)

## Description

This **Filter** removes **Triangles** from the supplied **Triangle Geometry** that are flagged by a boolean mask array as **true**. A new reduced **Geometry** is created that contains all the remaining **Triangles**. It is unknown until run time how many **Triangles** will be removed from the **Geometry**. Therefore, this **Filter** requires that a new **TriangleGeom** be created to contain the reduced **Triangle Geometry**. This new **Geometry** will *NOT* contain copies of any **Feature Attribute Matrix** or **Ensemble Attribute Matrix** from the original **Geometry**.

- Additionally, all **Vertex** data will be copied, with tuples *removed* for any **Vertices** removed by the **Filter**. The user must supply a name for the reduced **Geometry**.

The mask is expected to be over the triangles themselves so it should be based on something from the ***Face Data*** **Attribute Matrix**, generally we suggest basing the mask on the created **Region Ids** array from the *Label Triangle Geometry Filter*.

*Note:* Since it cannot be known before run time how many **Vertices** will be removed, the new **Vertex Geometry** and
all associated **Vertex** data to be copied will be initialized to have size 0.

## Example Output

- The next figure shows a triangle geometry that has had a mask generated. Yellow parts are flagged as true.

![Masked triangle geometries for removal.](Images/RemoveFlaggedTriangles_1.png)

- The next figure shows the result of running the filter.

![Resulting triangle geometry](Images/RemoveFlaggedTriangles_2.png)

% Auto generated parameter table will be inserted here

## Example Pipelines

## License & Copyright

Please see the description file distributed with this plugin.

## DREAM3D-NX Help

If you need help, need to file a bug report or want to request a new feature, please head over to the [DREAM3DNX-Issues](https://github.com/BlueQuartzSoftware/DREAM3DNX-Issues) GitHub site where the community of DREAM3D-NX users can help answer your questions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
#include "RemoveFlaggedEdges.hpp"

#include "simplnx/DataStructure/DataArray.hpp"
#include "simplnx/DataStructure/DataGroup.hpp"
#include "simplnx/DataStructure/Geometry/EdgeGeom.hpp"
#include "simplnx/Utilities/DataArrayUtilities.hpp"
#include "simplnx/Utilities/ParallelAlgorithmUtilities.hpp"
#include "simplnx/Utilities/ParallelDataAlgorithm.hpp"
#include "simplnx/Utilities/ParallelTaskAlgorithm.hpp"

using namespace nx::core;

namespace
{
constexpr usize k_NumVerts = 2;

/**
* @brief
* @tparam T
*/
template <typename T>
class CopyCellDataArray
{
public:
CopyCellDataArray(const IDataArray& oldCellArray, IDataArray& newCellArray, const std::vector<usize>& newEdgesIndex, const std::atomic_bool& shouldCancel)
: m_OldCellArray(dynamic_cast<const DataArray<T>&>(oldCellArray))
, m_NewCellArray(dynamic_cast<DataArray<T>&>(newCellArray))
, m_NewEdgesIndex(newEdgesIndex)
, m_ShouldCancel(shouldCancel)
{
}

~CopyCellDataArray() = default;

CopyCellDataArray(const CopyCellDataArray&) = default;
CopyCellDataArray(CopyCellDataArray&&) noexcept = default;
CopyCellDataArray& operator=(const CopyCellDataArray&) = delete;
CopyCellDataArray& operator=(CopyCellDataArray&&) noexcept = delete;

void operator()() const
{
convert();
}

protected:
void convert() const
{
size_t numComps = m_OldCellArray.getNumberOfComponents();
const auto& oldCellData = m_OldCellArray.getDataStoreRef();

auto& dataStore = m_NewCellArray.getDataStoreRef();
std::fill(dataStore.begin(), dataStore.end(), static_cast<T>(-1));

uint64 destTupleIndex = 0;
for(const auto& srcIndex : m_NewEdgesIndex)
{
for(size_t compIndex = 0; compIndex < numComps; compIndex++)
{
dataStore.setValue(destTupleIndex * numComps + compIndex, oldCellData.getValue(srcIndex * numComps + compIndex));
}
destTupleIndex++;
}
}

private:
const DataArray<T>& m_OldCellArray;
DataArray<T>& m_NewCellArray;
const std::vector<usize>& m_NewEdgesIndex;
const std::atomic_bool& m_ShouldCancel;
};

/**
* @brief The PopulateReducedGeometryEdgesImpl pulls the vertices associated with a triangle then locates the indices in
* the new VertexList then assigns that "new" triangle to Reduced Geometry
*/
class PopulateReducedGeometryEdgesImpl
{
public:
PopulateReducedGeometryEdgesImpl(const EdgeGeom& originalTriangle, EdgeGeom& reducedTriangle, const std::vector<usize>& newEdgesIndex, const std::vector<usize>& newVerticesIndex)
: m_OriginalTriangle(originalTriangle)
, m_ReducedEgeGeom(reducedTriangle)
, m_NewEdgesIndex(newEdgesIndex)
, m_NewVerticesIndex(newVerticesIndex)
{
}
~PopulateReducedGeometryEdgesImpl() = default;

PopulateReducedGeometryEdgesImpl(const PopulateReducedGeometryEdgesImpl&) = default; // Copy Constructor Not Implemented
PopulateReducedGeometryEdgesImpl(PopulateReducedGeometryEdgesImpl&&) = delete; // Move Constructor Not Implemented
PopulateReducedGeometryEdgesImpl& operator=(const PopulateReducedGeometryEdgesImpl&) = delete; // Copy Assignment Not Implemented
PopulateReducedGeometryEdgesImpl& operator=(PopulateReducedGeometryEdgesImpl&&) = delete; // Move Assignment Not Implemented

void generate(usize start, usize end) const
{
for(usize index = start; index < end; index++)
{
// pull old vertices
usize oldVertexIndices[k_NumVerts] = {0, 0};
m_OriginalTriangle.getEdgePointIds(m_NewEdgesIndex[index], oldVertexIndices);

// locate new vertex index for each vertex index
usize newVertexIndices[k_NumVerts] = {0, 0};
for(usize vertIndex = 0; vertIndex < k_NumVerts; vertIndex++)
{
const auto& itr = lower_bound(m_NewVerticesIndex.cbegin(), m_NewVerticesIndex.cend(), oldVertexIndices[vertIndex]); // find first instance of value as iterator
usize indexOfTarget = std::distance(m_NewVerticesIndex.cbegin(), itr);
newVertexIndices[vertIndex] = indexOfTarget;
}

// set the triangle in reduced
m_ReducedEgeGeom.setEdgePointIds(index, newVertexIndices);
}
}

void operator()(const Range& range) const
{
generate(range.min(), range.max());
}

private:
const EdgeGeom& m_OriginalTriangle;
EdgeGeom& m_ReducedEgeGeom;
const std::vector<usize>& m_NewEdgesIndex;
const std::vector<usize>& m_NewVerticesIndex;
};
} // namespace

// -----------------------------------------------------------------------------
RemoveFlaggedEdges::RemoveFlaggedEdges(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, RemoveFlaggedEdgesInputValues* inputValues)
: m_DataStructure(dataStructure)
, m_InputValues(inputValues)
, m_ShouldCancel(shouldCancel)
, m_MessageHandler(mesgHandler)
{
}

// -----------------------------------------------------------------------------
const std::atomic_bool& RemoveFlaggedEdges::getCancel()
{
return m_ShouldCancel;
}

// -----------------------------------------------------------------------------
Result<> RemoveFlaggedEdges::operator()()
{
// Remove Edges from reduced according to removeEdgesIndex
const auto& originalEdgeGeom = m_DataStructure.getDataRefAs<EdgeGeom>(m_InputValues->EdgeGeometry);
std::unique_ptr<MaskCompare> maskCompare;
try
{
maskCompare = InstantiateMaskCompare(m_DataStructure, m_InputValues->MaskArrayPath);
} catch(const std::out_of_range& exception)
{
// This really should NOT be happening as the path was verified during preflight BUT we may be calling this from
// somewhere else that is NOT going through the normal nx::core::IFilter API of Preflight and Execute
std::string message = fmt::format("Mask Array DataPath does not exist or is not of the correct type (Bool | UInt8) {}", m_InputValues->MaskArrayPath.toString());
return MakeErrorResult(-54070, message);
}
auto& reducedEdgeGeom = m_DataStructure.getDataRefAs<EdgeGeom>(m_InputValues->ReducedEdgeGeometry);

// Set up allocated masks
usize size = originalEdgeGeom.getNumberOfEdges();
std::vector<usize> newEdgesIndexList;
newEdgesIndexList.reserve(size);

// parse mask Edges list and load a list of indices for Edges to keep
for(usize index = 0; index < size; index++)
{
if(!maskCompare->isTrue(index))
{
newEdgesIndexList.push_back(index);
}
}
newEdgesIndexList.shrink_to_fit();

if(getCancel())
{
return {};
}
if(newEdgesIndexList.empty())
{
return MakeErrorResult(-67880, "Re-evaluate mask conditions - with current configuration all Edges will be stripped!");
}

// flatten a list of the indices of vertices used by the Edges
std::vector<usize> vertexListIndices; // also used as a pseudo look up table in PopulateReducedGeometryEdgesImpl
usize vertIDs[k_NumVerts] = {0, 0};
for(usize& index : newEdgesIndexList)
{

originalEdgeGeom.getEdgePointIds(index, vertIDs);
vertexListIndices.push_back(vertIDs[0]);
vertexListIndices.push_back(vertIDs[1]);
}
if(getCancel())
{
return {};
}

if(vertexListIndices.empty())
{
return MakeErrorResult(-67881, "Re-evaluate mask conditions - with current configuration all vertices will be dumped!");
}

// clear duplicate values out of vector
std::sort(vertexListIndices.begin(), vertexListIndices.end()); // orders ascending !!!!! Basis for later search !!!!!
auto dupes = std::unique(vertexListIndices.begin(), vertexListIndices.end());
vertexListIndices.erase(dupes, vertexListIndices.end());

// define new sizing
size = vertexListIndices.size();
reducedEdgeGeom.resizeVertexList(size); // resize accordingly
reducedEdgeGeom.getVertexAttributeMatrix()->resizeTuples({size});

// load reduced Geometry Vertex list according to used vertices
Point3Df coords = {0.0f, 0.0f, 0.0f};
for(usize i = 0; i < size; i++)
{
coords = originalEdgeGeom.getVertexCoordinate(vertexListIndices[i]);
reducedEdgeGeom.setVertexCoordinate(i, coords);
}

if(getCancel())
{
return {};
}

// Set up preprocessing conditions (allocation for parallelization)
size = newEdgesIndexList.size();
reducedEdgeGeom.resizeEdgeList(size); // resize accordingly
reducedEdgeGeom.getEdgeAttributeMatrix()->resizeTuples({size});

// parse Edges and reassign indexes to match new vertex list
ParallelDataAlgorithm dataAlg;
dataAlg.setRange(0, size);
dataAlg.execute(PopulateReducedGeometryEdgesImpl(originalEdgeGeom, reducedEdgeGeom, newEdgesIndexList, vertexListIndices));

// The actual cropping of the dataStructure arrays is done in parallel where parallel here
// refers to the cropping of each DataArray being done on a separate thread.
ParallelTaskAlgorithm taskRunner;
const auto& srcCellDataAM = originalEdgeGeom.getEdgeAttributeMatrixRef();
auto& destCellDataAM = reducedEdgeGeom.getEdgeAttributeMatrixRef();
for(const auto& [dataId, oldDataObject] : srcCellDataAM)
{
if(m_ShouldCancel)
{
return {};
}

const auto& oldDataArray = dynamic_cast<const IDataArray&>(*oldDataObject);
const std::string srcName = oldDataArray.getName();

auto& newDataArray = dynamic_cast<IDataArray&>(destCellDataAM.at(srcName));

m_MessageHandler(fmt::format("Reducing Edge Geometry || Copying Data Array {}", srcName));
ExecuteParallelFunction<CopyCellDataArray>(oldDataArray.getDataType(), taskRunner, oldDataArray, newDataArray, newEdgesIndexList, m_ShouldCancel);
}
taskRunner.wait(); // This will spill over if the number of DataArrays to process does not divide evenly by the number of threads.

return {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#pragma once

#include "SimplnxCore/SimplnxCore_export.hpp"

#include "simplnx/DataStructure/DataPath.hpp"
#include "simplnx/DataStructure/DataStructure.hpp"
#include "simplnx/Filter/IFilter.hpp"
#include "simplnx/Parameters/ArraySelectionParameter.hpp"
#include "simplnx/Parameters/ChoicesParameter.hpp"
#include "simplnx/Parameters/DataGroupSelectionParameter.hpp"
#include "simplnx/Parameters/StringParameter.hpp"

namespace
{

const std::string k_Ignore("Ignore Edge Data");
const std::string k_CopyAll("Copy All Edge Data");
const std::string k_CopySelected("Copy Selected Edge Data");

const nx::core::ChoicesParameter::Choices k_ArrayHandlingChoices = {k_Ignore, k_CopySelected, k_CopyAll};

const nx::core::ChoicesParameter::ValueType k_IgnoreArraysIdx = 0ULL;
const nx::core::ChoicesParameter::ValueType k_CopySelectedArraysIdx = 1ULL;
const nx::core::ChoicesParameter::ValueType k_CopyAllArraysIdx = 2ULL;

} // namespace

namespace nx::core
{
struct SIMPLNXCORE_EXPORT RemoveFlaggedEdgesInputValues
{
DataPath EdgeGeometry;
DataPath MaskArrayPath;
DataPath ReducedEdgeGeometry;
};

/**
* @class ConditionalSetValueFilter
*/
class SIMPLNXCORE_EXPORT RemoveFlaggedEdges
{
public:
RemoveFlaggedEdges(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, RemoveFlaggedEdgesInputValues* inputValues);
~RemoveFlaggedEdges() noexcept = default;

RemoveFlaggedEdges(const RemoveFlaggedEdges&) = delete;
RemoveFlaggedEdges(RemoveFlaggedEdges&&) noexcept = delete;
RemoveFlaggedEdges& operator=(const RemoveFlaggedEdges&) = delete;
RemoveFlaggedEdges& operator=(RemoveFlaggedEdges&&) noexcept = delete;

Result<> operator()();

const std::atomic_bool& getCancel();

private:
DataStructure& m_DataStructure;
const RemoveFlaggedEdgesInputValues* m_InputValues = nullptr;
const std::atomic_bool& m_ShouldCancel;
const IFilter::MessageHandler& m_MessageHandler;
};
} // namespace nx::core
Loading

0 comments on commit 1d38947

Please sign in to comment.