diff --git a/src/Plugins/ComplexCore/docs/ExtractVertexGeometryFilter.md b/src/Plugins/ComplexCore/docs/ExtractVertexGeometryFilter.md index 8976d3062a..a598c538a1 100644 --- a/src/Plugins/ComplexCore/docs/ExtractVertexGeometryFilter.md +++ b/src/Plugins/ComplexCore/docs/ExtractVertexGeometryFilter.md @@ -2,13 +2,16 @@ ## Group (Subgroup) -Core Filters (Conversion) +Core Filters (Geometry) ## Description This filter will extract all the voxel centers of an Image Geometry or a RectilinearGrid geometry -into a new VertexGeometry. The user is given the option to copy or move cell arrays over to the -newly created VertexGeometry. +into a new Vertex Geometry. The user is given the option to copy or move cell arrays over to the +newly created VertexGeometry. The user can also supply a mask array which has the effect of only +creating a vertex if the mask value = TRUE. + +![Example showing the use of a Mask array to only generate specific points.](Images/ExtractVertexGeometry_1.png) % Auto generated parameter table will be inserted here diff --git a/src/Plugins/ComplexCore/docs/Images/ExtractVertexGeometry_1.png b/src/Plugins/ComplexCore/docs/Images/ExtractVertexGeometry_1.png new file mode 100644 index 0000000000..d545a03dbb Binary files /dev/null and b/src/Plugins/ComplexCore/docs/Images/ExtractVertexGeometry_1.png differ diff --git a/src/Plugins/ComplexCore/pipelines/ExtractVertexGeometry.d3dpipeline b/src/Plugins/ComplexCore/pipelines/ExtractVertexGeometry.d3dpipeline new file mode 100644 index 0000000000..1fc7d0fa9b --- /dev/null +++ b/src/Plugins/ComplexCore/pipelines/ExtractVertexGeometry.d3dpipeline @@ -0,0 +1,188 @@ +{ + "isDisabled": false, + "name": "Untitled Pipeline", + "pipeline": [ + { + "args": { + "cell_attribute_matrix_name": "Cell Data", + "cell_ensemble_attribute_matrix_name": "CellEnsembleData", + "data_container_name": "DataContainer", + "input_file": "Data/Small_IN100/Slice_1.ang" + }, + "comments": "", + "filter": { + "name": "complex::ReadAngDataFilter", + "uuid": "5b062816-79ac-47ce-93cb-e7966896bcbd" + }, + "isDisabled": false + }, + { + "args": { + "euler_angles_array_path": "DataContainer/Cell Data/EulerAngles", + "rotation_axis": [ + 0.0, + 0.0, + 1.0, + 90.0 + ] + }, + "comments": "", + "filter": { + "name": "complex::RotateEulerRefFrameFilter", + "uuid": "0458edcd-3655-4465-adc8-b036d76138b5" + }, + "isDisabled": false + }, + { + "args": { + "created_image_geometry": "DataContainer", + "remove_original_geometry": true, + "rotate_slice_by_slice": false, + "rotation_axis": [ + 0.0, + 1.0, + 0.0, + 180.0 + ], + "rotation_matrix": [ + [ + 0.0, + 0.0, + 0.0 + ], + [ + 0.0, + 0.0, + 0.0 + ], + [ + 0.0, + 0.0, + 0.0 + ] + ], + "rotation_representation": 0, + "selected_image_geometry": "DataContainer" + }, + "comments": "", + "filter": { + "name": "complex::RotateSampleRefFrameFilter", + "uuid": "d2451dc1-a5a1-4ac2-a64d-7991669dcffc" + }, + "isDisabled": false + }, + { + "args": { + "array_thresholds": { + "inverted": false, + "thresholds": [ + { + "array_path": "DataContainer/Cell Data/Confidence Index", + "comparison": 0, + "inverted": false, + "type": "array", + "union": 0, + "value": 0.9 + } + ], + "type": "collection", + "union": 0 + }, + "created_data_path": "Mask", + "created_mask_type": 10, + "custom_false_value": 0.0, + "custom_true_value": 1.0, + "use_custom_false_value": false, + "use_custom_true_value": false + }, + "comments": "", + "filter": { + "name": "complex::MultiThresholdObjects", + "uuid": "4246245e-1011-4add-8436-0af6bed19228" + }, + "isDisabled": false + }, + { + "args": { + "cell_euler_angles_array_path": "DataContainer/Cell Data/EulerAngles", + "cell_ipf_colors_array_name": "IPFColors", + "cell_phases_array_path": "DataContainer/Cell Data/Phases", + "crystal_structures_array_path": "DataContainer/CellEnsembleData/CrystalStructures", + "mask_array_path": "", + "reference_dir": [ + 0.0, + 0.0, + 1.0 + ], + "use_mask": false + }, + "comments": "", + "filter": { + "name": "complex::GenerateIPFColorsFilter", + "uuid": "64cb4f27-6e5e-4dd2-8a03-0c448cb8f5e6" + }, + "isDisabled": false + }, + { + "args": { + "array_thresholds": { + "inverted": false, + "thresholds": [ + { + "array_path": "DataContainer/Cell Data/Y Position", + "comparison": 1, + "inverted": false, + "type": "array", + "union": 0, + "value": 25.0 + } + ], + "type": "collection", + "union": 0 + }, + "created_data_path": "Mask Y Pos", + "created_mask_type": 10, + "custom_false_value": 0.0, + "custom_true_value": 1.0, + "use_custom_false_value": false, + "use_custom_true_value": false + }, + "comments": "", + "filter": { + "name": "complex::MultiThresholdObjects", + "uuid": "4246245e-1011-4add-8436-0af6bed19228" + }, + "isDisabled": false + }, + { + "args": { + "array_handling": 0, + "included_data_array_paths": [ + "DataContainer/Cell Data/Confidence Index", + "DataContainer/Cell Data/EulerAngles", + "DataContainer/Cell Data/Fit", + "DataContainer/Cell Data/IPFColors", + "DataContainer/Cell Data/Image Quality", + "DataContainer/Cell Data/Phases", + "DataContainer/Cell Data/SEM Signal", + "DataContainer/Cell Data/X Position", + "DataContainer/Cell Data/Y Position", + "DataContainer/Cell Data/Mask" + ], + "input_geometry_path": "DataContainer", + "mask_array_path": "DataContainer/Cell Data/Mask Y Pos", + "output_shared_vertex_list_name": "SharedVertexList", + "output_vertex_attr_matrix_name": "VertexData", + "output_vertex_geometry_path": "Vertex Geometry", + "use_mask": true + }, + "comments": "This filter will only create vertices where the Y Position was below a threshold set in the previous filter. The filter will move the arrays to the newly created vertext geometry", + "filter": { + "name": "complex::ExtractVertexGeometryFilter", + "uuid": "621a71ca-124b-4471-ad1a-02f05ffba099" + }, + "isDisabled": false + } + ], + "version": 1 +} \ No newline at end of file diff --git a/src/Plugins/ComplexCore/src/ComplexCore/Filters/Algorithms/ExtractVertexGeometry.cpp b/src/Plugins/ComplexCore/src/ComplexCore/Filters/Algorithms/ExtractVertexGeometry.cpp index d6bd57ec0e..b7bd537366 100644 --- a/src/Plugins/ComplexCore/src/ComplexCore/Filters/Algorithms/ExtractVertexGeometry.cpp +++ b/src/Plugins/ComplexCore/src/ComplexCore/Filters/Algorithms/ExtractVertexGeometry.cpp @@ -3,6 +3,7 @@ #include "complex/DataStructure/DataArray.hpp" #include "complex/DataStructure/Geometry/IGridGeometry.hpp" #include "complex/DataStructure/Geometry/VertexGeom.hpp" +#include "complex/Utilities/DataArrayUtilities.hpp" #include "complex/Utilities/FilterUtilities.hpp" using namespace complex; @@ -12,28 +13,36 @@ namespace struct CopyDataFunctor { template - void operator()(const IDataArray& srcIArray, IDataArray& destIArray, std::optional maskArray) + inline void copyTuple(const DataArray& srcArray, DataArray& destArray, usize srcTupleIdx, usize destTupleIndex) + { + usize numComps = srcArray.getNumberOfComponents(); + for(usize cIdx = 0; cIdx < numComps; cIdx++) + { + destArray[destTupleIndex * numComps + cIdx] = srcArray[srcTupleIdx * numComps + cIdx]; + } + } + + template + void operator()(const IDataArray& srcIArray, IDataArray& destIArray, const std::vector& maskArray) { using DataArrayType = DataArray; const DataArrayType& srcArray = dynamic_cast(srcIArray); DataArrayType& destArray = dynamic_cast(destIArray); - usize destIdx = 0; - usize srcSize = srcArray.getSize(); - for(size_t idx = 0; idx < srcSize; idx++) + bool useMask = !maskArray.empty(); + usize destTupleIdx = 0; + usize srcSize = srcArray.getNumberOfTuples(); + for(size_t tupleIdx = 0; tupleIdx < srcSize; tupleIdx++) { - if(maskArray.has_value()) + if(useMask && maskArray[tupleIdx]) { - if((*maskArray)->at(idx)) - { - destArray[destIdx] = srcArray[idx]; - destIdx++; - } + copyTuple(srcArray, destArray, tupleIdx, destTupleIdx); + destTupleIdx++; } - else + else if(!useMask) { - destArray[idx] = srcArray[idx]; + copyTuple(srcArray, destArray, tupleIdx, tupleIdx); } } } @@ -66,76 +75,113 @@ Result<> ExtractVertexGeometry::operator()() VertexGeom& vertexGeometry = m_DataStructure.getDataRefAs(m_InputValues->VertexGeometryPath); SizeVec3 dims = inputGeometry.getDimensions(); - usize cellCount = std::accumulate(dims.begin(), dims.end(), static_cast(1), std::multiplies()); + const usize cellCount = std::accumulate(dims.begin(), dims.end(), static_cast(1ULL), std::multiplies<>()); usize totalCells = cellCount; // We save this here because it may change based on the use_mask flag. + usize vertexCount = cellCount; + DataPath vertexAttributeMatrixDataPath = vertexGeometry.getVertexAttributeMatrixDataPath(); + + DataPath maskArrayPath = m_InputValues->MaskArrayPath; + // The mask array may have gotten moved to the vertex array, if so, we need to find that out. + if(m_InputValues->UseMask && !m_DataStructure.containsData(m_InputValues->MaskArrayPath)) + { + if(!m_InputValues->IncludedDataArrayPaths.empty() && m_InputValues->UseMask) + { + for(const auto& dataPath : m_InputValues->IncludedDataArrayPaths) + { + if(dataPath == m_InputValues->MaskArrayPath) + { + maskArrayPath = vertexAttributeMatrixDataPath.createChildPath(m_InputValues->MaskArrayPath.getTargetName()); + } + } + } + } + // Copy the mask array into a std::vector. This is just easier in + // case the mask array is indeed one of the copied or moved arrays. + std::vector maskedPoints; if(m_InputValues->UseMask) { - const BoolArray& maskArray = m_DataStructure.getDataRefAs(m_InputValues->MaskArrayPath); - cellCount = std::count(maskArray.begin(), maskArray.end(), true); + std::unique_ptr maskArrayPtr = nullptr; + try + { + maskArrayPtr = std::move(InstantiateMaskCompare(m_DataStructure, 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 + // some other context that is NOT going through the normal complex::IFilter API of Preflight and Execute + return MakeErrorResult(-53900, fmt::format("Mask Array DataPath does not exist or is not of the correct type (Bool | UInt8) {}", maskArrayPath.toString())); + } - vertexGeometry.resizeVertexList(cellCount); + vertexCount = 0; + maskedPoints.resize(totalCells, false); + for(usize i = 0; i < totalCells; i++) + { + if(maskArrayPtr->isTrue(i)) + { + maskedPoints[i] = true; + vertexCount++; + } + } + vertexGeometry.resizeVertexList(vertexCount); } + // Use the APIs from the IGeometryGrid to get the XYZ coord for the center + // of each cell and then set that into the new VertexGeometry IGeometry::SharedVertexList& vertices = vertexGeometry.getVerticesRef(); auto& verticesDataStore = vertices.getDataStoreRef(); - - // Use the APIs from the IGeometryGrid to get the XYZ coord for the center of each cell and then set that into - // the new VertexGeometry usize vertIdx = 0; for(usize idx = 0; idx < totalCells; idx++) { if(m_InputValues->UseMask) { - const BoolArray& maskArray = m_DataStructure.getDataRefAs(m_InputValues->MaskArrayPath); - if(maskArray[idx]) + if(maskedPoints[idx]) { - Point3D coords = inputGeometry.getCoordsf(idx); + const Point3D coords = inputGeometry.getCoordsf(idx); verticesDataStore.setTuple(vertIdx, coords.toArray()); vertIdx++; } } else { - Point3D coords = inputGeometry.getCoordsf(idx); + const Point3D coords = inputGeometry.getCoordsf(idx); verticesDataStore.setTuple(idx, coords.toArray()); } } - // Copy the data from the cell data arrays to the vertex data arrays - if(!m_InputValues->IncludedDataArrayPaths.empty()) + // If we are copying arrays, either with or without a mask, this code is applicable. + if(m_InputValues->ArrayHandling == static_cast(ArrayHandlingType::CopyArrays)) { - DataPath vertexGeomPath = m_InputValues->VertexGeometryPath; - DataPath vertexAttrMatrixPath = vertexGeomPath.createChildPath(m_InputValues->IncludedDataArrayPaths[0].getParent().getTargetName()); - AttributeMatrix& vertexAttrMatrix = m_DataStructure.getDataRefAs(vertexAttrMatrixPath); - - std::optional maskArray; - if(m_InputValues->UseMask) + // Since we made copies of the DataArrays, we can safely resize the entire Attribute Matrix, + // which will resize all the contained DataArrays + AttributeMatrix& vertexAttrMatrix = vertexGeometry.getVertexAttributeMatrixRef(); + vertexAttrMatrix.resizeTuples({vertexCount}); + for(const auto& dataArrayPath : m_InputValues->IncludedDataArrayPaths) { - const BoolArray* maskArrayPtr = m_DataStructure.getDataAs(m_InputValues->MaskArrayPath); - cellCount = std::count(maskArrayPtr->begin(), maskArrayPtr->end(), true); - vertexAttrMatrix.resizeTuples({cellCount}); - - for(const auto& srcDataArrayPath : m_InputValues->IncludedDataArrayPaths) - { - DataPath destDataArrayPath = vertexAttrMatrixPath.createChildPath(srcDataArrayPath.getTargetName()); - IDataArray& destDataArray = m_DataStructure.getDataRefAs(destDataArrayPath); - auto& destDataArrayStore = destDataArray.getIDataStoreRef(); - destDataArrayStore.resizeTuples({cellCount}); - } - - maskArray = maskArrayPtr; + const IDataArray& srcIDataArray = m_DataStructure.getDataRefAs(dataArrayPath); + DataPath destDataArrayPath = vertexAttributeMatrixDataPath.createChildPath(srcIDataArray.getName()); + IDataArray& destDataArray = m_DataStructure.getDataRefAs(destDataArrayPath); + ExecuteDataFunction(CopyDataFunctor{}, srcIDataArray.getDataType(), srcIDataArray, destDataArray, maskedPoints); } + } + // If we are MOVING DataArrays, and we are NOT using a Mask then the MoveDataAction + // took care of renaming/moving the arrays for us and we are done. + + // If we are MOVING arrays AND we are using a mask then we need this code block to execute + if(m_InputValues->ArrayHandling == static_cast(ArrayHandlingType::MoveArrays) && m_InputValues->UseMask && vertexCount != cellCount) + { + // The arrays have already been moved at this point, so the source and + // destinations are the same. This should work. for(const auto& dataArrayPath : m_InputValues->IncludedDataArrayPaths) { - const IDataArray& srcIDataArray = m_DataStructure.getDataRefAs(dataArrayPath); - - DataPath destDataArrayPath = vertexAttrMatrixPath.createChildPath(srcIDataArray.getName()); + DataPath srcDataArrayPath = vertexAttributeMatrixDataPath.createChildPath(dataArrayPath.getTargetName()); + DataPath destDataArrayPath = vertexAttributeMatrixDataPath.createChildPath(dataArrayPath.getTargetName()); + const IDataArray& srcIDataArray = m_DataStructure.getDataRefAs(srcDataArrayPath); IDataArray& destDataArray = m_DataStructure.getDataRefAs(destDataArrayPath); - - ExecuteDataFunction(CopyDataFunctor{}, srcIDataArray.getDataType(), srcIDataArray, destDataArray, maskArray); + ExecuteDataFunction(CopyDataFunctor{}, srcIDataArray.getDataType(), srcIDataArray, destDataArray, maskedPoints); } + AttributeMatrix& vertexAttrMatrix = vertexGeometry.getVertexAttributeMatrixRef(); + vertexAttrMatrix.resizeTuples({vertexCount}); } return {}; diff --git a/src/Plugins/ComplexCore/src/ComplexCore/Filters/Algorithms/ExtractVertexGeometry.hpp b/src/Plugins/ComplexCore/src/ComplexCore/Filters/Algorithms/ExtractVertexGeometry.hpp index f352bde211..4fc0c2614a 100644 --- a/src/Plugins/ComplexCore/src/ComplexCore/Filters/Algorithms/ExtractVertexGeometry.hpp +++ b/src/Plugins/ComplexCore/src/ComplexCore/Filters/Algorithms/ExtractVertexGeometry.hpp @@ -33,6 +33,12 @@ class COMPLEXCORE_EXPORT ExtractVertexGeometry ExtractVertexGeometry& operator=(const ExtractVertexGeometry&) = delete; ExtractVertexGeometry& operator=(ExtractVertexGeometry&&) noexcept = delete; + enum class ArrayHandlingType : ChoicesParameter::ValueType + { + MoveArrays, + CopyArrays + }; + Result<> operator()(); const std::atomic_bool& getCancel(); diff --git a/src/Plugins/ComplexCore/src/ComplexCore/Filters/ExtractVertexGeometryFilter.cpp b/src/Plugins/ComplexCore/src/ComplexCore/Filters/ExtractVertexGeometryFilter.cpp index b61efd30f0..07da8cdf49 100644 --- a/src/Plugins/ComplexCore/src/ComplexCore/Filters/ExtractVertexGeometryFilter.cpp +++ b/src/Plugins/ComplexCore/src/ComplexCore/Filters/ExtractVertexGeometryFilter.cpp @@ -6,15 +6,28 @@ #include "complex/Filter/Actions/CreateArrayAction.hpp" #include "complex/Filter/Actions/CreateVertexGeometryAction.hpp" #include "complex/Filter/Actions/DeleteDataAction.hpp" +#include "complex/Filter/Actions/MoveDataAction.hpp" #include "complex/Parameters/ArraySelectionParameter.hpp" #include "complex/Parameters/BoolParameter.hpp" #include "complex/Parameters/ChoicesParameter.hpp" #include "complex/Parameters/DataGroupCreationParameter.hpp" #include "complex/Parameters/DataGroupSelectionParameter.hpp" #include "complex/Parameters/DataObjectNameParameter.hpp" +#include "complex/Parameters/GeometrySelectionParameter.hpp" #include "complex/Parameters/MultiArraySelectionParameter.hpp" using namespace complex; +namespace +{ + +using FeatureIdsArrayType = Int32Array; +using GoodVoxelsArrayType = BoolArray; + +inline constexpr int32 k_MissingGeomError = -72440; +inline constexpr int32 k_IncorrectInputArray = -72441; +inline constexpr int32 k_MissingInputArray = -72442; +inline constexpr int32 k_MissingOrIncorrectGoodVoxelsArray = -72443; +} // namespace namespace complex { @@ -45,7 +58,7 @@ std::string ExtractVertexGeometryFilter::humanName() const //------------------------------------------------------------------------------ std::vector ExtractVertexGeometryFilter::defaultTags() const { - return {className(), "Core", "Conversion"}; + return {className(), "Core", "Geometry", "Conversion", "Image", "Generate", "Create", "Vertex"}; } //------------------------------------------------------------------------------ @@ -53,18 +66,26 @@ Parameters ExtractVertexGeometryFilter::parameters() const { Parameters params; // Create the parameter descriptors that are needed for this filter - params.insert( - std::make_unique(k_ArrayHandling_Key, "Array Handling", "Move or Copy input data arrays", 0, ChoicesParameter::Choices{"Move Attribute Arrays", "Copy Attribute Arrays"})); + params.insertSeparator(Parameters::Separator{"Input Parameters"}); + params.insert(std::make_unique(k_ArrayHandling_Key, "Array Handling", "[0] Move or [1] Copy input data arrays", 0, + ChoicesParameter::Choices{"Move Attribute Arrays", "Copy Attribute Arrays"})); + + params.insertSeparator(Parameters::Separator{"Optional Data Mask"}); params.insertLinkableParameter(std::make_unique(k_UseMask_Key, "Use Mask", "Specifies whether or not to use a mask array", false)); params.insert(std::make_unique(k_MaskArrayPath_Key, "Mask Array", "DataPath to the boolean mask array. Values that are true will mark that cell/point as usable.", - DataPath{}, ArraySelectionParameter::AllowedTypes{DataType::boolean}, ArraySelectionParameter::AllowedComponentShapes{{1}})); - params.insert(std::make_unique(k_InputGeometryPath_Key, "Input Geometry", "The input Image/RectilinearGrid Geometry to convert", DataPath{}, - DataGroupSelectionParameter::AllowedTypes{BaseGroup::GroupType::ImageGeom, BaseGroup::GroupType::RectGridGeom})); + DataPath{}, ArraySelectionParameter::AllowedTypes{DataType::boolean, DataType::uint8}, ArraySelectionParameter::AllowedComponentShapes{{1}})); + + params.insertSeparator(Parameters::Separator{"Input Image or RectilinearGrid Geometry"}); + params.insert(std::make_unique(k_InputGeometryPath_Key, "Input Geometry", "The input Image/RectilinearGrid Geometry to convert", DataPath{}, + GeometrySelectionParameter::AllowedTypes{IGeometry::Type::Image, IGeometry::Type::RectGrid})); + params.insert(std::make_unique(k_IncludedDataArrayPaths_Key, "Included Attribute Arrays", "The arrays to copy/move to the vertex array", MultiArraySelectionParameter::ValueType{}, MultiArraySelectionParameter::AllowedTypes{IArray::ArrayType::DataArray}, complex::GetAllDataTypes())); - params.insert(std::make_unique(k_VertexGeometryPath_Key, "Output Vertex Geometry", "The complete path to the vertex geometry that will be created", - DataPath({"[Vertex Geometry]"}))); + + params.insertSeparator(Parameters::Separator{"Output Vertex Geometry"}); + params.insert( + std::make_unique(k_VertexGeometryPath_Key, "Output Vertex Geometry", "The complete path to the vertex geometry that will be created", DataPath({"Vertex Geometry"}))); params.insert(std::make_unique(k_VertexAttrMatrixName_Key, "Output Vertex Attribute Matrix Name", "The name of the vertex attribute matrix that will be created", std::string{"VertexData"})); params.insert(std::make_unique(k_SharedVertexListName_Key, "Output Shared Vertex List Name", "The name of the shared vertex list that will be created", @@ -87,7 +108,7 @@ IFilter::PreflightResult ExtractVertexGeometryFilter::preflightImpl(const DataSt const std::atomic_bool& shouldCancel) const { auto pArrayHandlingValue = filterArgs.value(k_ArrayHandling_Key); - auto pUseMaskValue = filterArgs.value(k_UseMask_Key); + auto pUseGoodVoxelsValue = filterArgs.value(k_UseMask_Key); auto pMaskArrayPathValue = filterArgs.value(k_MaskArrayPath_Key); auto pInputGeometryPathValue = filterArgs.value(k_InputGeometryPath_Key); auto pIncludedDataArrayPathsValue = filterArgs.value(k_IncludedDataArrayPaths_Key); @@ -97,77 +118,76 @@ IFilter::PreflightResult ExtractVertexGeometryFilter::preflightImpl(const DataSt complex::Result resultOutputActions; - ArrayHandlingType arrayHandlingType = static_cast(pArrayHandlingValue); + const ExtractVertexGeometry::ArrayHandlingType arrayHandlingType = static_cast(pArrayHandlingValue); const IGridGeometry& geometry = dataStructure.getDataRefAs({pInputGeometryPathValue}); SizeVec3 dims = geometry.getDimensions(); usize geomElementCount = dims[0] * dims[1] * dims[2]; - if(pUseMaskValue) - { - const BoolArray* maskArray = dataStructure.getDataAs(pMaskArrayPathValue); - if(maskArray->getNumberOfTuples() != geomElementCount) - { - return {MakeErrorResult(-2003, fmt::format("{0}: The data array with path '{1}' has a tuple count of {2}, but this does not match the " - "number of tuples required by geometry '{3}' ({4})", - humanName(), pMaskArrayPathValue.toString(), maskArray->getNumberOfTuples(), geometry.getName(), geomElementCount))}; - } - } - std::optional cellAttrMatrixPathR; - for(const DataPath& dataPath : pIncludedDataArrayPathsValue) + // Create the Vertex Geometry + auto createVertexGeometryAction = std::make_unique(pVertexGeometryPathValue, geomElementCount, pVertexAttrMatrixNameValue, pSharedVertexListNameValue); + resultOutputActions.value().appendAction(std::move(createVertexGeometryAction)); + + std::vector dataPaths = pIncludedDataArrayPathsValue; + + // Validate the GoodVoxels/Mask Array combination + if(pUseGoodVoxelsValue) { - DataPath parentPath = dataPath.getParent(); - if(parentPath.empty()) + const complex::IDataArray* goodVoxelsArray = dataStructure.getDataAs(pMaskArrayPathValue); + if(nullptr == goodVoxelsArray) { - return {MakeErrorResult(-2004, fmt::format("{}: The data array with path '{}' has no parent. It must have an AttributeMatrix as a parent.", humanName(), dataPath.toString()))}; + return {nonstd::make_unexpected(std::vector{Error{k_MissingOrIncorrectGoodVoxelsArray, fmt::format("Mask array is not located at path: '{}'", pMaskArrayPathValue.toString())}})}; } - if(dataStructure.getDataAs(parentPath) == nullptr) + + if(goodVoxelsArray->getDataType() != DataType::boolean && goodVoxelsArray->getDataType() != DataType::uint8) { - return {MakeErrorResult(-2005, fmt::format("{}: The data array with path '{}' does not have an AttributeMatrix as a parent.", humanName(), dataPath.toString()))}; + return {nonstd::make_unexpected( + std::vector{Error{k_MissingOrIncorrectGoodVoxelsArray, fmt::format("Mask array at path '{}' is not of the correct type. It must be Bool or UInt8", pMaskArrayPathValue.toString())}})}; } + dataPaths.push_back(pMaskArrayPathValue); + } - const IDataArray& dataArray = dataStructure.getDataRefAs(dataPath); + // Validate the number of tuples for each of the DataArrays that are to be moved/copied + auto tupleValidityCheck = dataStructure.validateNumberOfTuples(dataPaths); + if(!tupleValidityCheck) + { + return {MakeErrorResult(-651, fmt::format("The following DataArrays all must have equal number of tuples but this was not satisfied.\n{}", tupleValidityCheck.error()))}; + } + + // Validate the number of tuples in the arrays (if any) and the number of tuples in the input grid geometry (which is also the same + // as the output Vertex Geometry + if(!dataPaths.empty()) + { + const IDataArray& dataArray = dataStructure.getDataRefAs(dataPaths.front()); if(dataArray.getNumberOfTuples() != geomElementCount) { - return {MakeErrorResult(-2006, fmt::format("{}: The data array with path '{}' has a tuple count of {}, but this does not match the " - "number of vertices required by geometry with path '{}' ({})", - humanName(), dataPath.toString(), dataArray.getNumberOfTuples(), pVertexGeometryPathValue.toString(), geomElementCount))}; + return {MakeErrorResult(-2006, fmt::format("The selected DataArrays do not have the correct number of tuples. The Input Geometry ({}) has {} tuples but the " + " selected DataArrays have {} tuples.)", + pInputGeometryPathValue.toString(), geomElementCount, dataArray.getNumberOfTuples()))}; } + } - if(cellAttrMatrixPathR.has_value()) - { - DataPath cellAttrMatrixPath = *cellAttrMatrixPathR; - if(parentPath != *cellAttrMatrixPathR) - { - return {MakeErrorResult( - -2007, fmt::format("{}: The data array with path '{}' does not have the AttributeMatrix at path '{}' as a parent. All data arrays must have the same AttributeMatrix parent.", humanName(), - dataPath.toString(), cellAttrMatrixPath.toString()))}; - } - } - else + // Create appropriate actions to either copy or move the data + const DataPath vertexAttrMatrixPath = pVertexGeometryPathValue.createChildPath(pVertexAttrMatrixNameValue); + for(const DataPath& dataPath : pIncludedDataArrayPathsValue) + { + const IDataArray& dataArray = dataStructure.getDataRefAs(dataPath); + + if(arrayHandlingType == ExtractVertexGeometry::ArrayHandlingType::CopyArrays) { - cellAttrMatrixPathR = parentPath; - { - auto createVertexGeometryAction = std::make_unique(pVertexGeometryPathValue, geomElementCount, parentPath.getTargetName(), pSharedVertexListNameValue); - resultOutputActions.value().appendAction(std::move(createVertexGeometryAction)); - } + DataPath newDataPath = vertexAttrMatrixPath.createChildPath(dataPath.getTargetName()); + auto createArrayAction = std::make_unique(dataArray.getDataType(), dataArray.getTupleShape(), dataArray.getComponentShape(), newDataPath); + resultOutputActions.value().appendAction(std::move(createArrayAction)); } - - DataPath newDataPath = pVertexGeometryPathValue.createChildPath(parentPath.getTargetName()).createChildPath(dataArray.getName()); - - auto createArrayAction = std::make_unique(dataArray.getDataType(), dataArray.getTupleShape(), dataArray.getComponentShape(), newDataPath); - resultOutputActions.value().appendAction(std::move(createArrayAction)); - - if(arrayHandlingType == ArrayHandlingType::MoveArrays) + else if(arrayHandlingType == ExtractVertexGeometry::ArrayHandlingType::MoveArrays) { - if(!pUseMaskValue) - { - auto deleteDataAction = std::make_unique(dataPath, DeleteDataAction::DeleteType::JustObject); - resultOutputActions.value().appendDeferredAction(std::move(deleteDataAction)); - } + auto moveDataAction = std::make_unique(dataPath, vertexAttrMatrixPath); + resultOutputActions.value().appendAction(std::move(moveDataAction)); } } + // PreflightResult preflightResult; + // std::vector preflightUpdatedValues; // Return both the resultOutputActions and the preflightUpdatedValues via std::move() diff --git a/src/Plugins/ComplexCore/src/ComplexCore/Filters/ExtractVertexGeometryFilter.hpp b/src/Plugins/ComplexCore/src/ComplexCore/Filters/ExtractVertexGeometryFilter.hpp index a466936c65..3db49d6dc0 100644 --- a/src/Plugins/ComplexCore/src/ComplexCore/Filters/ExtractVertexGeometryFilter.hpp +++ b/src/Plugins/ComplexCore/src/ComplexCore/Filters/ExtractVertexGeometryFilter.hpp @@ -31,15 +31,9 @@ class COMPLEXCORE_EXPORT ExtractVertexGeometryFilter : public IFilter static inline constexpr StringLiteral k_MaskArrayPath_Key = "mask_array_path"; static inline constexpr StringLiteral k_InputGeometryPath_Key = "input_geometry_path"; static inline constexpr StringLiteral k_IncludedDataArrayPaths_Key = "included_data_array_paths"; - static inline constexpr StringLiteral k_VertexGeometryPath_Key = "vertex_geometry_path"; - static inline constexpr StringLiteral k_VertexAttrMatrixName_Key = "vertex_attr_matrix_name"; - static inline constexpr StringLiteral k_SharedVertexListName_Key = "shared_vertex_list_name"; - - enum class ArrayHandlingType : usize - { - MoveArrays, - CopyArrays - }; + static inline constexpr StringLiteral k_VertexGeometryPath_Key = "output_vertex_geometry_path"; + static inline constexpr StringLiteral k_VertexAttrMatrixName_Key = "output_vertex_attr_matrix_name"; + static inline constexpr StringLiteral k_SharedVertexListName_Key = "output_shared_vertex_list_name"; /** * @brief Returns the name of the filter. diff --git a/src/complex/Filter/Actions/MoveDataAction.cpp b/src/complex/Filter/Actions/MoveDataAction.cpp index 6b0af3ac87..3adf8df866 100644 --- a/src/complex/Filter/Actions/MoveDataAction.cpp +++ b/src/complex/Filter/Actions/MoveDataAction.cpp @@ -46,7 +46,7 @@ Result<> MoveDataAction::apply(DataStructure& dataStructure, Mode mode) const for(const auto parentId : parentIds) { auto parentPaths = dataStructure.getDataPathsForId(parentId); - for(const auto parentPath : parentPaths) + for(const auto& parentPath : parentPaths) { if(m_NewParentPath == parentPath) { diff --git a/src/complex/Utilities/DataArrayUtilities.hpp b/src/complex/Utilities/DataArrayUtilities.hpp index 402d9f0ea4..234b0110bd 100644 --- a/src/complex/Utilities/DataArrayUtilities.hpp +++ b/src/complex/Utilities/DataArrayUtilities.hpp @@ -789,9 +789,11 @@ struct MaskCompare virtual void setValue(usize index, bool val) = 0; - virtual usize getNumberOfTuples() = 0; + virtual usize getNumberOfTuples() const = 0; - virtual usize getNumberOfComponents() = 0; + virtual usize getNumberOfComponents() const = 0; + + virtual usize countTrueValues() const = 0; }; struct BoolMaskCompare : public MaskCompare @@ -819,14 +821,19 @@ struct BoolMaskCompare : public MaskCompare { m_Array[index] = val; } - usize getNumberOfTuples() override + usize getNumberOfTuples() const override { return m_Array.getNumberOfTuples(); } - usize getNumberOfComponents() override + usize getNumberOfComponents() const override { return m_Array.getNumberOfComponents(); } + + usize countTrueValues() const override + { + return std::count(m_Array.begin(), m_Array.end(), true); + } }; struct UInt8MaskCompare : public MaskCompare @@ -853,14 +860,20 @@ struct UInt8MaskCompare : public MaskCompare { m_Array[index] = static_cast(val); } - usize getNumberOfTuples() override + usize getNumberOfTuples() const override { return m_Array.getNumberOfTuples(); } - usize getNumberOfComponents() override + usize getNumberOfComponents() const override { return m_Array.getNumberOfComponents(); } + + usize countTrueValues() const override + { + const usize falseCount = std::count(m_Array.begin(), m_Array.end(), 0); + return getNumberOfTuples() - falseCount; + } }; /**