From 9e756ec10597a66d2d2b47e741450bea63e70677 Mon Sep 17 00:00:00 2001 From: Michael Jackson Date: Tue, 24 Sep 2024 16:18:40 -0400 Subject: [PATCH] FILT: CreateDataArray Advanced (Create Data Array + Initialize Data Array) (#1066) - Added new filter that allows more options and to initialize the data array with some set patterns of data (Random, fixed value, indexing) - Rearrange the parameters a bit to make the flow more logical. The user should start from the "top down" when filling out the parameters. 1: Name 2: Type 3: Number of Tuples 4: Number of Components 5: Initialize values. - Added Python script showing how to use advanced Create Data Array filter. --------- Signed-off-by: Michael Jackson Signed-off-by: Joey Kleingers Co-authored-by: Joey Kleingers --- .../Common/ITKArrayHelper.hpp | 2 + src/Plugins/SimplnxCore/CMakeLists.txt | 2 + .../docs/CreateDataArrayAdvancedFilter.md | 67 +++ .../Filters/Algorithms/InitializeData.cpp | 336 ++++++++++++ .../Filters/Algorithms/InitializeData.hpp | 115 ++++ .../Filters/CreateDataArrayAdvancedFilter.cpp | 501 ++++++++++++++++++ .../Filters/CreateDataArrayAdvancedFilter.hpp | 114 ++++ .../Filters/CreateDataArrayFilter.cpp | 16 +- .../Filters/InitializeDataFilter.cpp | 441 ++------------- src/Plugins/SimplnxCore/test/CMakeLists.txt | 1 + .../test/CreateDataArrayAdvancedTest.cpp | 138 +++++ .../scripts/create_data_array_advanced.py | 168 ++++++ 12 files changed, 1488 insertions(+), 413 deletions(-) create mode 100644 src/Plugins/SimplnxCore/docs/CreateDataArrayAdvancedFilter.md create mode 100644 src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/InitializeData.cpp create mode 100644 src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/InitializeData.hpp create mode 100644 src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CreateDataArrayAdvancedFilter.cpp create mode 100644 src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CreateDataArrayAdvancedFilter.hpp create mode 100644 src/Plugins/SimplnxCore/test/CreateDataArrayAdvancedTest.cpp create mode 100644 wrapping/python/examples/scripts/create_data_array_advanced.py diff --git a/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ITKArrayHelper.hpp b/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ITKArrayHelper.hpp index 6d6d655f83..ff19849845 100644 --- a/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ITKArrayHelper.hpp +++ b/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ITKArrayHelper.hpp @@ -383,8 +383,10 @@ DataStore> ConvertImageToDataStore(itk::Image concept NotBoolT = !std::is_same_v; +// clang-format on template Result<> ConvertImageToDataStore(DataStore& dataStore, itk::Image& image) diff --git a/src/Plugins/SimplnxCore/CMakeLists.txt b/src/Plugins/SimplnxCore/CMakeLists.txt index 9f644cc27d..0b9c01bf98 100644 --- a/src/Plugins/SimplnxCore/CMakeLists.txt +++ b/src/Plugins/SimplnxCore/CMakeLists.txt @@ -51,6 +51,7 @@ set(FilterList CopyFeatureArrayToElementArrayFilter CreateAttributeMatrixFilter CreateDataArrayFilter + CreateDataArrayAdvancedFilter CreateDataGroupFilter CreateFeatureArrayFromElementArrayFilter CreateGeometryFilter @@ -186,6 +187,7 @@ set(AlgorithmList ComputeTriangleGeomSizes FlyingEdges3D CreateColorMap + InitializeData LabelTriangleGeometry LaplacianSmoothing NearestPointFuseRegularGrids diff --git a/src/Plugins/SimplnxCore/docs/CreateDataArrayAdvancedFilter.md b/src/Plugins/SimplnxCore/docs/CreateDataArrayAdvancedFilter.md new file mode 100644 index 0000000000..8decabea5e --- /dev/null +++ b/src/Plugins/SimplnxCore/docs/CreateDataArrayAdvancedFilter.md @@ -0,0 +1,67 @@ +# Create Data Array (Advanced) + +## Group (Subgroup) + +Core (Generation) + +## Description + +This **Filter** creates a **Data Array** of any primitive type with any set of component dimensions. The array is initialized to a user defined value or with random values within a specified range. + +When initializing a multicomponent array square bracket notation can be used to specify different initialization values for each component. For example say that I want to intialize a 2 component array where the first component is 0 and the second component is 1 we would use the following input string for the *Initialization Value* + + 0;1 + +We are using semicolons instead of commas or decimal points due to different international standards (European versus United States?). + +Another example is if you want to create a floating point array where each tuple has 10 components but you just want the value of 2.5 to be used for each, then simply use: + + 2.5 + +When creating a Data Array within an Attribute matrix, the tuple dimensions will **always** be taken direct from the Attribute Matrix. This means that the *Set Tuple Dimensions* parameter can be unchecked to hide the tuple dimensions entry table. + +If the parent is **NOT an Attribute Matrix**, then the user ***MUST*** set the tuple dimensions themselves. + +### Scalar Type Values + + static const int Int8 = 0; + static const int UInt8 = 1; + static const int Int16 = 2; + static const int UInt16 = 3; + static const int Int32 = 4; + static const int UInt32 = 5; + static const int Int64 = 6; + static const int UInt64 = 7; + static const int Float = 8; + static const int Double = 9; + static const int Bool = 10; + +### Primitive Data Type Valid Ranges + +| Type | Size | Range | +|------------------|------|--------------------| +| Signed Integer | 8 bit |0 to 255| +| Unsigned Integer | 8 bit |-128 to 127| +| Signed Integer | 16 bit |-32,768 to 32,767| +| Unsigned Integer | 16 bit |0 to 65,535| +| Signed Integer | 32 bit |-2,147,483,648 to 2,147,483,647| +| Unsigned Integer | 32 bit |0 to 4,294,967,295| +| Signed Integer | 64 bit | 9,223,372,036,854,775,808 to 9,223,372,036,854,775,807| +| Unsigned Integer | 64 bit |0 to 18,446,744,073,709,551,615| +| Float | 32 bit | -3.4e+38 to -1.1e-38, 0.0, 1.1e-38 to 3.4e+38 (7 digits)| +| Double | 64 bit | -1.7e+308 to -2.2e-308, 0.0, 2.2e-308 to 1.7e+308 (15 digits)| +| Boolean | 8 bit |0 = false and any other value will be forced to 1 = true| + +The component dimensions should multiply together into a total number of components equal to at least 1. Examples of *Component Dimensions* would be [3] for an RGB Image, [1] for a gray scale image, [1] for a scalar array, [4] for a quaternions array, [10x5] for an array with 10x5 grids at each tuple, etc. All values of the array will be initialized using the chosen initialization option. + +% 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/discussions) GitHub site where the community of DREAM3D-NX users can help answer your questions. diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/InitializeData.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/InitializeData.cpp new file mode 100644 index 0000000000..5545a09a9f --- /dev/null +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/InitializeData.cpp @@ -0,0 +1,336 @@ + + +#include "InitializeData.hpp" + +#include "simplnx/Common/TypeTraits.hpp" +#include "simplnx/DataStructure/AbstractDataStore.hpp" +#include "simplnx/DataStructure/IDataArray.hpp" +#include "simplnx/Utilities/DataArrayUtilities.hpp" +#include "simplnx/Utilities/FilterUtilities.hpp" + +#include +#include + +using namespace nx::core; + +namespace +{ + +// At the current time this code could be simplified with a bool in the incremental template, HOWEVER, +// it was done this way to allow for expansion of operations down the line multiplication, division, etc. +template +struct IncrementalOptions +{ + static constexpr bool UsingAddition = UseAddition; + static constexpr bool UsingSubtraction = UseSubtraction; +}; + +using AdditionT = IncrementalOptions; +using SubtractionT = IncrementalOptions; + +template +void ValueFill(AbstractDataStore& dataStore, const std::vector& stringValues) +{ + usize numComp = dataStore.getNumberOfComponents(); // We checked that the values string is greater than max comps size so proceed check free + + if(numComp > 1) + { + std::vector values; + + for(const auto& str : stringValues) + { + values.emplace_back(ConvertTo::convert(str).value()); + } + + usize numTup = dataStore.getNumberOfTuples(); + + for(usize tup = 0; tup < numTup; tup++) + { + for(usize comp = 0; comp < numComp; comp++) + { + dataStore[tup * numComp + comp] = values[comp]; + } + } + } + else + { + Result result = ConvertTo::convert(stringValues[0]); + T value = result.value(); + dataStore.fill(value); + } +} + +template +void IncrementalFill(AbstractDataStore& dataStore, const std::vector& startValues, const std::vector& stepValues) +{ + usize numComp = dataStore.getNumberOfComponents(); // We checked that the values string is greater than max comps size so proceed check free + + std::vector values(numComp); + std::vector steps(numComp); + + for(usize comp = 0; comp < numComp; comp++) + { + Result result = ConvertTo::convert(startValues[comp]); + values[comp] = result.value(); + if constexpr(!std::is_same_v) + { + result = ConvertTo::convert(stepValues[comp]); + steps[comp] = result.value(); + } + } + + usize numTup = dataStore.getNumberOfTuples(); + + if constexpr(std::is_same_v) + { + for(usize comp = 0; comp < numComp; comp++) + { + dataStore[comp] = values[comp]; + + if constexpr(IncrementalOptions::UsingAddition) + { + values[comp] = ConvertTo::convert(stepValues[comp]).value() != 0 ? true : values[comp]; + } + if constexpr(IncrementalOptions::UsingSubtraction) + { + values[comp] = ConvertTo::convert(stepValues[comp]).value() != 0 ? false : values[comp]; + } + } + + for(usize tup = 1; tup < numTup; tup++) + { + for(usize comp = 0; comp < numComp; comp++) + { + dataStore[tup * numComp + comp] = values[comp]; + } + } + } + + if constexpr(!std::is_same_v) + { + for(usize tup = 0; tup < numTup; tup++) + { + for(usize comp = 0; comp < numComp; comp++) + { + dataStore[tup * numComp + comp] = values[comp]; + + if constexpr(IncrementalOptions::UsingAddition) + { + values[comp] += steps[comp]; + } + if constexpr(IncrementalOptions::UsingSubtraction) + { + values[comp] -= steps[comp]; + } + } + } + } +} + +template +void RandomFill(std::vector& dist, AbstractDataStore& dataStore, const uint64 seed, const bool standardizeSeed) +{ + usize numComp = dataStore.getNumberOfComponents(); // We checked that the values string is greater than max comps size so proceed check free + + std::vector generators(numComp, std::mt19937_64{}); + + for(usize comp = 0; comp < numComp; comp++) + { + generators[comp].seed((standardizeSeed ? seed : seed + comp)); // If standardizing seed all generators use the same else, use modified seeds + } + + usize numTup = dataStore.getNumberOfTuples(); + + for(usize tup = 0; tup < numTup; tup++) + { + for(usize comp = 0; comp < numComp; comp++) + { + if constexpr(std::is_floating_point_v) + { + if constexpr(Ranged) + { + dataStore[tup * numComp + comp] = static_cast(dist[comp](generators[comp])); + } + if constexpr(!Ranged) + { + if constexpr(std::is_signed_v) + { + dataStore[tup * numComp + comp] = static_cast(dist[comp](generators[comp]) * (std::numeric_limits::max() - 1) * (((rand() & 1) == 0) ? 1 : -1)); + } + if constexpr(!std::is_signed_v) + { + dataStore[tup * numComp + comp] = static_cast(dist[comp](generators[comp]) * std::numeric_limits::max()); + } + } + } + if constexpr(!std::is_floating_point_v) + { + dataStore[tup * numComp + comp] = static_cast(dist[comp](generators[comp])); + } + } + } +} + +template +void FillIncForwarder(const StepType& stepType, ArgsT&&... args) +{ + switch(stepType) + { + case StepType::Addition: { + ::IncrementalFill(std::forward(args)...); + return; + } + case StepType::Subtraction: { + ::IncrementalFill(std::forward(args)...); + return; + } + } +} + +template +void FillRandomForwarder(const std::vector& range, usize numComp, ArgsT&&... args) +{ + if constexpr(std::is_same_v) + { + std::vector> dists; + for(usize comp = 0; comp < numComp * 2; comp += 2) + { + dists.emplace_back((range.at(comp) ? 1 : 0), (range.at(comp + 1) ? 1 : 0)); + } + ::RandomFill>(dists, std::forward(args)...); + return; + } + if constexpr(!std::is_floating_point_v) + { + std::vector> dists; + for(usize comp = 0; comp < numComp * 2; comp += 2) + { + dists.emplace_back(range.at(comp), range.at(comp + 1)); + } + ::RandomFill>(dists, std::forward(args)...); + } + if constexpr(std::is_floating_point_v) + { + if constexpr(Ranged) + { + + std::vector> dists; + for(usize comp = 0; comp < numComp * 2; comp += 2) + { + dists.emplace_back(range.at(comp), range.at(comp + 1)); + } + ::RandomFill>(dists, std::forward(args)...); + } + if constexpr(!Ranged) + { + std::vector> dists; + for(usize comp = 0; comp < numComp * 2; comp += 2) + { + dists.emplace_back(0, 1); + } + ::RandomFill>(dists, std::forward(args)...); + } + } +} + +std::vector standardizeMultiComponent(const usize numComps, const std::vector& componentValues) +{ + if(componentValues.size() == numComps) + { + return {componentValues}; + } + else + { + std::vector standardized(numComps); + for(usize comp = 0; comp < numComps; comp++) + { + standardized[comp] = componentValues[0]; + } + return standardized; + } +} + +struct FillArrayFunctor +{ + template + void operator()(IDataArray& iDataArray, const InitializeDataInputValues& inputValues) + { + auto& dataStore = iDataArray.template getIDataStoreRefAs>(); + usize numComp = dataStore.getNumberOfComponents(); // We checked that the values string is greater than max comps size so proceed check free + + switch(inputValues.initType) + { + case InitializeType::FillValue: { + return ::ValueFill(dataStore, standardizeMultiComponent(numComp, inputValues.stringValues)); + } + case InitializeType::Incremental: { + return ::FillIncForwarder(inputValues.stepType, dataStore, standardizeMultiComponent(numComp, inputValues.startValues), standardizeMultiComponent(numComp, inputValues.stepValues)); + } + case InitializeType::Random: { + std::vector range; + if constexpr(!std::is_same_v) + { + for(usize comp = 0; comp < numComp; comp++) + { + range.push_back(std::numeric_limits::min()); + range.push_back(std::numeric_limits::max()); + } + } + if constexpr(std::is_same_v) + { + for(usize comp = 0; comp < numComp; comp++) + { + range.push_back(false); + range.push_back(true); + } + } + return ::FillRandomForwarder(range, numComp, dataStore, inputValues.seed, inputValues.standardizeSeed); + } + case InitializeType::RangedRandom: { + auto randBegin = standardizeMultiComponent(numComp, inputValues.randBegin); + auto randEnd = standardizeMultiComponent(numComp, inputValues.randEnd); + + std::vector range; + for(usize comp = 0; comp < numComp; comp++) + { + Result result = ConvertTo::convert(randBegin[comp]); + range.push_back(result.value()); + result = ConvertTo::convert(randEnd[comp]); + range.push_back(result.value()); + } + return ::FillRandomForwarder(range, numComp, dataStore, inputValues.seed, inputValues.standardizeSeed); + } + } + } +}; + +} // namespace + +// ----------------------------------------------------------------------------- +InitializeData::InitializeData(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, InitializeDataInputValues* inputValues) +: m_DataStructure(dataStructure) +, m_InputValues(inputValues) +, m_ShouldCancel(shouldCancel) +, m_MessageHandler(mesgHandler) +{ +} + +// ----------------------------------------------------------------------------- +InitializeData::~InitializeData() noexcept = default; + +// ----------------------------------------------------------------------------- +const std::atomic_bool& InitializeData::getCancel() +{ + return m_ShouldCancel; +} + +// ----------------------------------------------------------------------------- +Result<> InitializeData::operator()() +{ + + auto& iDataArray = m_DataStructure.getDataRefAs(m_InputValues->InputArrayPath); + + ExecuteDataFunction(::FillArrayFunctor{}, iDataArray.getDataType(), iDataArray, *m_InputValues); + + return {}; +} diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/InitializeData.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/InitializeData.hpp new file mode 100644 index 0000000000..7403460ab6 --- /dev/null +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/InitializeData.hpp @@ -0,0 +1,115 @@ +#pragma once + +#include "SimplnxCore/SimplnxCore_export.hpp" + +#include "simplnx/Filter/IFilter.hpp" +#include "simplnx/Utilities/DataArrayUtilities.hpp" +#include "simplnx/Utilities/StringUtilities.hpp" + +namespace nx::core +{ + +constexpr char k_DelimiterChar = ';'; + +enum InitializeType : uint64 +{ + FillValue, + Incremental, + Random, + RangedRandom +}; + +enum StepType : uint64 +{ + Addition, + Subtraction +}; + +struct SIMPLNXCORE_EXPORT InitializeDataInputValues +{ + DataPath InputArrayPath; + InitializeType initType; + StepType stepType; + std::vector stringValues; + std::vector startValues; + std::vector stepValues; + uint64 seed; + std::vector randBegin; + std::vector randEnd; + bool standardizeSeed; +}; + +struct SIMPLNXCORE_EXPORT ValidateMultiInputFunctor +{ + // The single comp size validation defaults to off as size 0 is checked earlier in the function + template + IFilter::PreflightResult operator()(const usize expectedComp, const std::string& unfilteredStr, const usize singleCompSize = 0) + { + std::vector splitVals = StringUtilities::split(StringUtilities::trimmed(unfilteredStr), k_DelimiterChar); + + if(splitVals.empty()) + { + return IFilter::MakePreflightErrorResult(-11610, fmt::format("A required parameter is unable to be processed with '{}' delimiter. Input: {}", k_DelimiterChar, unfilteredStr)); + } + + for(usize comp = 0; comp < splitVals.size(); comp++) + { + if(splitVals[comp].empty()) + { + return IFilter::MakePreflightErrorResult(-11611, fmt::format("Empty value found after '{}' components were converted. Check for duplicate '{}' next to one another.", comp, k_DelimiterChar)); + } + + Result result = ConvertTo::convert(splitVals[comp]); + + if(result.invalid()) + { + return IFilter::MakePreflightErrorResult(-11612, fmt::format("Unable to process '{}' into a {} value.", splitVals[comp], DataTypeToString(GetDataType()))); + } + } + + if(splitVals.size() == expectedComp) + { + return {}; // Valid + } + + if(splitVals.size() == singleCompSize) + { + return {}; // Valid + } + + if(splitVals.size() == expectedComp + 1) + { + if(unfilteredStr.back() == k_DelimiterChar) + { + return IFilter::MakePreflightErrorResult(-11613, fmt::format("Remove the extra delimiter '{}' at the end of your value sequence: {}.", k_DelimiterChar, unfilteredStr)); + } + } + + return IFilter::MakePreflightErrorResult(-11614, + fmt::format("Using '{}' as a delimiter we are unable to break '{}' into the required {} components.", k_DelimiterChar, unfilteredStr, expectedComp)); + } +}; + +class InitializeData +{ +public: + InitializeData(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, InitializeDataInputValues* inputValues); + ~InitializeData() noexcept; + + InitializeData(const InitializeData&) = delete; + InitializeData(InitializeData&&) noexcept = delete; + InitializeData& operator=(const InitializeData&) = delete; + InitializeData& operator=(InitializeData&&) noexcept = delete; + + Result<> operator()(); + + const std::atomic_bool& getCancel(); + +private: + DataStructure& m_DataStructure; + const InitializeDataInputValues* m_InputValues = nullptr; + const std::atomic_bool& m_ShouldCancel; + const IFilter::MessageHandler& m_MessageHandler; +}; + +} // namespace nx::core diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CreateDataArrayAdvancedFilter.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CreateDataArrayAdvancedFilter.cpp new file mode 100644 index 0000000000..83aadcbe52 --- /dev/null +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CreateDataArrayAdvancedFilter.cpp @@ -0,0 +1,501 @@ +#include "CreateDataArrayAdvancedFilter.hpp" + +#include "SimplnxCore/Filters/Algorithms/InitializeData.hpp" + +#include "simplnx/Common/TypesUtility.hpp" +#include "simplnx/Filter/Actions/CreateArrayAction.hpp" +#include "simplnx/Parameters/ArrayCreationParameter.hpp" +#include "simplnx/Parameters/BoolParameter.hpp" +#include "simplnx/Parameters/DataStoreFormatParameter.hpp" +#include "simplnx/Parameters/DynamicTableParameter.hpp" +#include "simplnx/Parameters/NumberParameter.hpp" +#include "simplnx/Parameters/NumericTypeParameter.hpp" +#include "simplnx/Parameters/StringParameter.hpp" +#include "simplnx/Utilities/DataArrayUtilities.hpp" +#include "simplnx/Utilities/FilterUtilities.hpp" +#include "simplnx/Utilities/SIMPLConversion.hpp" + +#include + +using namespace nx::core; + +namespace +{ +struct CreateAndInitArrayFunctor +{ + template + void operator()(IDataArray* iDataArray, const std::string& initValue) + { + Result result = ConvertTo::convert(initValue); + + auto* dataStore = iDataArray->template getIDataStoreAs>(); + dataStore->fill(result.value()); + } +}; +} // namespace + +namespace nx::core +{ +//------------------------------------------------------------------------------ +std::string CreateDataArrayAdvancedFilter::name() const +{ + return FilterTraits::name; +} + +//------------------------------------------------------------------------------ +std::string CreateDataArrayAdvancedFilter::className() const +{ + return FilterTraits::className; +} + +//------------------------------------------------------------------------------ +Uuid CreateDataArrayAdvancedFilter::uuid() const +{ + return FilterTraits::uuid; +} + +//------------------------------------------------------------------------------ +std::string CreateDataArrayAdvancedFilter::humanName() const +{ + return "Create Data Array (Advanced)"; +} + +//------------------------------------------------------------------------------ +std::vector CreateDataArrayAdvancedFilter::defaultTags() const +{ + return {className(), "Create", "Data Structure", "Data Array", "Initialize", "Make"}; +} + +//------------------------------------------------------------------------------ +Parameters CreateDataArrayAdvancedFilter::parameters() const +{ + Parameters params; + + params.insertSeparator(Parameters::Separator{"Output Data Array"}); + params.insert(std::make_unique(k_DataPath_Key, "Created Array", "Array storing the data", DataPath({"Data"}))); + params.insert(std::make_unique(k_NumericType_Key, "Output Numeric Type", "Numeric Type of data to create", NumericType::int32)); + params.insert(std::make_unique(k_DataFormat_Key, "Data Format", + "This value will specify which data format is used by the array's data store. An empty string results in in-memory data store.", "")); + + params.insertSeparator(Parameters::Separator{"Tuple Dimensions"}); + params.insertLinkableParameter(std::make_unique( + k_AdvancedOptions_Key, "Set Tuple Dimensions [not required if creating inside an existing Attribute Matrix]", + "This allows the user to set the tuple dimensions directly rather than just inheriting them. This option is NOT required if you are creating the Data Array in an Attribute Matrix", true)); + + { + DynamicTableInfo tableInfo; + tableInfo.setRowsInfo(DynamicTableInfo::StaticVectorInfo(1)); + tableInfo.setColsInfo(DynamicTableInfo::DynamicVectorInfo(1, "TUPLE DIM {}")); + const DynamicTableInfo::TableDataType defaultTable{{1.0F}}; + params.insert(std::make_unique(k_TupleDims_Key, "Data Array Tuple Dimensions (Slowest to Fastest Dimensions)", + "Slowest to Fastest Dimensions. Note this might be opposite displayed by an image geometry.", defaultTable, tableInfo)); + } + + params.insertSeparator(Parameters::Separator{"Component Dimensions"}); + { + DynamicTableInfo tableInfo; + tableInfo.setRowsInfo(DynamicTableInfo::StaticVectorInfo(1)); + tableInfo.setColsInfo(DynamicTableInfo::DynamicVectorInfo(1, "COMP DIM {}")); + const DynamicTableInfo::TableDataType defaultTable{{1.0F}}; + params.insert(std::make_unique(k_CompDims_Key, "Data Array Component Dimensions (Slowest to Fastest Dimensions)", "Slowest to Fastest Component Dimensions.", defaultTable, + tableInfo)); + } + + params.insertSeparator(Parameters::Separator{"Initialization Options"}); + params.insertLinkableParameter(std::make_unique( + k_InitType_Key, "Initialization Type", "Method for determining the what values of the data in the array should be initialized to", static_cast(0), + ChoicesParameter::Choices{"Fill Value", "Incremental/Decremental", "Random", "Random With Range"})); // sequence dependent DO NOT REORDER + + params.insert(std::make_unique(k_InitValue_Key, "Fill Values [Seperated with ;]", + "Specify values for each component. Ex: A 3-component array would be 6;8;12 and every tuple would have these same component values", "1;1;1")); + + params.insert(std::make_unique( + k_StartingFillValue_Key, "Starting Value [Seperated with ;]", + "The value to start incrementing from. Ex: 6;8;12 would increment a 3-component array starting at 6 for the first component, 8 for the 2nd, and 12 for the 3rd.", "0;1;2")); + params.insert(std::make_unique(k_StepOperation_Key, "Step Operation", "The type of step operation to perform", static_cast(0), + ChoicesParameter::Choices{"Incrementing", "Decrementing"})); + params.insert(std::make_unique(k_StepValue_Key, "Step Value [Seperated with ;]", "The number to increment/decrement the fill value by", "1;1;1")); + + params.insert(std::make_unique(k_UseSeed_Key, "Use Seed for Random Generation", "When true, the Seed Value will be used to seed the generator", false)); + params.insert(std::make_unique>(k_SeedValue_Key, "Seed Value", "The seed fed into the random generator", std::mt19937::default_seed)); + params.insert(std::make_unique(k_SeedArrayName_Key, "Stored Seed Value Array Name", "Name of the array holding the seed value", "InitializeDataFilter SeedValue")); + params.insert(std::make_unique(k_StandardizeSeed_Key, "Use the Same Seed for Each Component", + "When true the same seed will be used for each component's generator in a multi-component array", false)); + + params.insert( + std::make_unique(k_InitStartRange_Key, "Initialization Start Range [Seperated with ;]", "[Inclusive] The lower bound initialization range for random values", "0;0;0")); + params.insert(std::make_unique(k_InitEndRange_Key, "Initialization End Range [Seperated with ;]", "[Inclusive] The upper bound initialization range for random values", "1;1;1")); + + // Associate the Linkable Parameter(s) to the children parameters that they control + params.linkParameters(k_AdvancedOptions_Key, k_TupleDims_Key, true); + + // Associate the Linkable Parameter(s) to the children parameters that they control + /* Using Fill Value */ + params.linkParameters(k_InitType_Key, k_InitValue_Key, static_cast(0)); + + /* Using Incremental */ + params.linkParameters(k_InitType_Key, k_StartingFillValue_Key, static_cast(1)); + params.linkParameters(k_InitType_Key, k_StepOperation_Key, static_cast(1)); + params.linkParameters(k_InitType_Key, k_StepValue_Key, static_cast(1)); + + /* Random - Using Random */ + params.linkParameters(k_InitType_Key, k_UseSeed_Key, static_cast(2)); + params.linkParameters(k_InitType_Key, k_SeedValue_Key, static_cast(2)); + params.linkParameters(k_InitType_Key, k_SeedArrayName_Key, static_cast(2)); + params.linkParameters(k_InitType_Key, k_StandardizeSeed_Key, static_cast(2)); + + /* Random - Using Random With Range */ + params.linkParameters(k_InitType_Key, k_UseSeed_Key, static_cast(3)); + params.linkParameters(k_InitType_Key, k_SeedValue_Key, static_cast(3)); + params.linkParameters(k_InitType_Key, k_SeedArrayName_Key, static_cast(3)); + params.linkParameters(k_InitType_Key, k_StandardizeSeed_Key, static_cast(3)); + params.linkParameters(k_InitType_Key, k_InitStartRange_Key, static_cast(3)); + params.linkParameters(k_InitType_Key, k_InitEndRange_Key, static_cast(3)); + return params; +} + +//------------------------------------------------------------------------------ +IFilter::UniquePointer CreateDataArrayAdvancedFilter::clone() const +{ + return std::make_unique(); +} + +//------------------------------------------------------------------------------ +IFilter::PreflightResult CreateDataArrayAdvancedFilter::preflightImpl(const DataStructure& dataStructure, const Arguments& filterArgs, const MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel) const +{ + auto useDims = filterArgs.value(k_AdvancedOptions_Key); + auto numericType = filterArgs.value(k_NumericType_Key); + auto compDimsData = filterArgs.value(k_CompDims_Key); + auto dataArrayPath = filterArgs.value(k_DataPath_Key); + auto tableData = filterArgs.value(k_TupleDims_Key); + auto dataFormat = filterArgs.value(k_DataFormat_Key); + + nx::core::Result resultOutputActions; + + std::vector compDims(compDimsData[0].size()); + std::transform(compDimsData[0].begin(), compDimsData[0].end(), compDims.begin(), [](double val) { return static_cast(val); }); + usize numComponents = std::accumulate(compDims.begin(), compDims.end(), static_cast(1), std::multiplies<>()); + if(numComponents <= 0) + { + std::string compDimsStr = std::accumulate(compDims.begin() + 1, compDims.end(), std::to_string(compDims[0]), [](const std::string& a, int b) { return a + " x " + std::to_string(b); }); + return MakePreflightErrorResult( + -78601, + fmt::format("The chosen component dimensions ({}) results in 0 total components. Please choose component dimensions that result in a positive number of total components.", compDimsStr)); + } + + std::vector tupleDims = {}; + + auto* parentAM = dataStructure.getDataAs(dataArrayPath.getParent()); + if(parentAM == nullptr) + { + if(!useDims) + { + return MakePreflightErrorResult( + -78602, fmt::format("The DataArray to be created '{}'is not within an AttributeMatrix, so the dimensions cannot be determined implicitly. Check Set Tuple Dimensions to set the dimensions", + dataArrayPath.toString())); + } + else + { + const auto& rowData = tableData.at(0); + tupleDims.reserve(rowData.size()); + for(auto floatValue : rowData) + { + if(floatValue == 0) + { + return MakePreflightErrorResult(-78603, "Tuple dimension cannot be zero"); + } + + tupleDims.push_back(static_cast(floatValue)); + } + } + } + else + { + tupleDims = parentAM->getShape(); + if(useDims) + { + resultOutputActions.warnings().push_back( + Warning{-78604, "You checked Set Tuple Dimensions, but selected a DataPath that has an Attribute Matrix as the parent. The Attribute Matrix tuples will override your " + "custom dimensions. It is recommended to uncheck Set Tuple Dimensions for the sake of clarity."}); + } + } + + auto arrayDataType = ConvertNumericTypeToDataType(numericType); + auto action = std::make_unique(ConvertNumericTypeToDataType(numericType), tupleDims, compDims, dataArrayPath, dataFormat); + + resultOutputActions.value().appendAction(std::move(action)); + + auto seedArrayNameValue = filterArgs.value(k_SeedArrayName_Key); + auto initializeTypeValue = static_cast(filterArgs.value(k_InitType_Key)); + + // nx::core::Result resultOutputActions; + std::vector preflightUpdatedValues; + + if(arrayDataType == DataType::boolean) + { + std::stringstream updatedValStrm; + + updatedValStrm << "We detected that you are doing an operation on a boolean array.\n"; + updatedValStrm << "The ONLY two ways to specify a 'false' boolean value are as follows:\n"; + updatedValStrm << "- boolean value string types as follows ignoring apostrophe marks: 'False', 'FALSE', 'false'\n"; + updatedValStrm << "- all well formed integers and well formed floating point definitions of 0\n\n"; + + updatedValStrm << "ANY OTHER string or number WILL BE 'true', although it is good practice to define true values as follows:\n"; + updatedValStrm << "- boolean value string types as follows ignoring apostrophe marks: 'True', 'TRUE', 'true'\n"; + updatedValStrm << "- all well formed integers and well formed floating point definitions of 1"; + + preflightUpdatedValues.push_back({"Boolean Note", updatedValStrm.str()}); + } + + if(numComponents > 1) + { + std::stringstream updatedValStrm; + + updatedValStrm << "We detected that you are doing an operation on a multi-component array.\n"; + updatedValStrm + << "If you do NOT want to use unique values for each component, you can just supply one value to the input box and we will apply that value to every component for the tuple.\nExample: 1\n\n"; + + updatedValStrm << fmt::format("If you DO want to use unique values for each component, you need to supply {} values of type {} separated by '{}'.\n", numComponents, + DataTypeToString(arrayDataType), k_DelimiterChar); + + // Define a threshold for displaying all components + const usize threshold = 10; + + updatedValStrm << "Example: "; + + if(numComponents <= threshold) + { + // Show all component values + for(usize comp = 0; comp < numComponents; comp++) + { + updatedValStrm << "1"; + + if(comp != numComponents - 1) + { + updatedValStrm << k_DelimiterChar; + } + } + } + else + { + // Show a limited number of component values followed by a summary + const usize displayCount = 10; + for(usize comp = 0; comp < displayCount; comp++) + { + updatedValStrm << "1"; + + if(comp != displayCount - 1) + { + updatedValStrm << k_DelimiterChar; + } + } + updatedValStrm << fmt::format(" ... {} more '1's", numComponents - displayCount); + } + + preflightUpdatedValues.push_back({"Multi-Component Note", updatedValStrm.str()}); + } + + std::stringstream operationNuancesStrm; + + switch(initializeTypeValue) + { + case InitializeType::FillValue: { + auto result = ExecuteDataFunction(::ValidateMultiInputFunctor{}, arrayDataType, numComponents, filterArgs.value(k_InitValue_Key), 1); + if(result.outputActions.invalid()) + { + return {MergeResults(result.outputActions, std::move(resultOutputActions)), std::move(preflightUpdatedValues)}; + } + + operationNuancesStrm << "None to note"; + + break; + } + case InitializeType::Incremental: { + auto result = ExecuteDataFunction(::ValidateMultiInputFunctor{}, arrayDataType, numComponents, filterArgs.value(k_StartingFillValue_Key), 1); + if(result.outputActions.invalid()) + { + return {MergeResults(result.outputActions, std::move(resultOutputActions)), std::move(preflightUpdatedValues)}; + } + + if(arrayDataType == DataType::boolean) + { + // custom bool checks here + std::stringstream updatedValStrm; + + updatedValStrm << "We detected that you are doing an incremental operation on a boolean array.\n"; + updatedValStrm << "For the step values please enter uint8 values, preferably a 0 or 1 only.\n"; + + switch(static_cast(filterArgs.value(k_StepOperation_Key))) + { + case Addition: { + updatedValStrm << "You have currently selected the addition operation.\nAny step value that is greater than 0 will cause all values to be 'true' after the first tuple, 'true' " + "values will remain unchanged.\n"; + updatedValStrm << "The two possibilities:\n"; + updatedValStrm << "- If your start value is 'false' and step value > 0, the array will initialize to | false | true | true | ... |\n"; + updatedValStrm << "- If your start value is 'true' and step value > 0, the array will initialize to | true | true | true | ... |"; + break; + } + case Subtraction: { + updatedValStrm << "You have currently selected the addition operation.\nAny step value that is greater than 0 will cause all values to be 'false' after the first tuple, 'false' " + "values will remain unchanged.\n"; + updatedValStrm << "The two possibilities:\n"; + updatedValStrm << "- If your start value is 'true' and step value > 0, the array will initialize to | true | false | false | ... |\n"; + updatedValStrm << "- If your start value is 'false' and step value > 0, the array will initialize to | false | false | false | ... |"; + break; + } + } + + preflightUpdatedValues.push_back({"Boolean Incremental Nuances", updatedValStrm.str()}); + + result = ExecuteDataFunction(::ValidateMultiInputFunctor{}, DataType::uint8, numComponents, filterArgs.value(k_StepValue_Key), 1); + if(result.outputActions.invalid()) + { + return {MergeResults(result.outputActions, std::move(resultOutputActions)), std::move(preflightUpdatedValues)}; + } + } + else + { + result = ExecuteDataFunction(::ValidateMultiInputFunctor{}, arrayDataType, numComponents, filterArgs.value(k_StepValue_Key), 1); + if(result.outputActions.invalid()) + { + return {MergeResults(result.outputActions, std::move(resultOutputActions)), std::move(preflightUpdatedValues)}; + } + } + auto values = StringUtilities::split(filterArgs.value(k_StepValue_Key), ";", false); + std::vector zeroIdx; + for(size_t i = 0; i < values.size(); i++) + { + if(values[i] == "0") + { + zeroIdx.push_back(i); + } + } + if(!zeroIdx.empty()) + { + + operationNuancesStrm << "Warning: Zero Step Value found. Component(s) " << fmt::format("[{}]", fmt::join(zeroIdx, ",")) + << " have a ZERO value for the step/increment.\n The values for those components will be unchanged from the starting value.\n"; + operationNuancesStrm << fmt::format(" Example: Suppose we have a two component array with a Step Values of '2{}0', Starting Values of '0', and an addition Step Operation\n", k_DelimiterChar); + operationNuancesStrm << " The output array would look like 0,0 | 2,0 | 4,0 | 6,0 | ..."; + } + break; + } + case InitializeType::RangedRandom: { + auto result = ExecuteDataFunction(::ValidateMultiInputFunctor{}, arrayDataType, numComponents, filterArgs.value(k_InitStartRange_Key), 1); + if(result.outputActions.invalid()) + { + return {MergeResults(result.outputActions, std::move(resultOutputActions)), std::move(preflightUpdatedValues)}; + } + + result = ExecuteDataFunction(::ValidateMultiInputFunctor{}, arrayDataType, numComponents, filterArgs.value(k_InitEndRange_Key), 1); + if(result.outputActions.invalid()) + { + return {MergeResults(result.outputActions, std::move(resultOutputActions)), std::move(preflightUpdatedValues)}; + } + + [[fallthrough]]; + } + case InitializeType::Random: { + auto createAction = std::make_unique(DataType::uint64, std::vector{1}, std::vector{1}, DataPath({seedArrayNameValue})); + resultOutputActions.value().appendAction(std::move(createAction)); + + if(numComponents == 1) + { + if(filterArgs.value(k_StandardizeSeed_Key)) + { + operationNuancesStrm << fmt::format("You chose to standardize the seed for each component, but the array {} is a single component so it will not alter the randomization scheme.", + dataArrayPath.getTargetName()); + } + } + else + { + if(filterArgs.value(k_StandardizeSeed_Key)) + { + operationNuancesStrm << "This generates THE SAME sequences of random numbers for each component in the array based on one seed.\n"; + operationNuancesStrm << "The resulting array will look like | 1,1,1 | 9,9,9 | ...\n"; + } + else + { + operationNuancesStrm << "This generates DIFFERENT sequences of random numbers for each component in the array based on x seeds all modified versions of an original seed.\n"; + operationNuancesStrm << "The resulting array will look like | 1,9,5 | 7,1,6 | ...\n"; + } + } + + break; + } + } + + preflightUpdatedValues.push_back({"Operation Nuances", operationNuancesStrm.str()}); + + return {std::move(resultOutputActions), std::move(preflightUpdatedValues)}; +} + +//------------------------------------------------------------------------------ +Result<> CreateDataArrayAdvancedFilter::executeImpl(DataStructure& dataStructure, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel) const +{ + auto path = filterArgs.value(k_DataPath_Key); + + ExecuteNeighborFunction(CreateAndInitArrayFunctor{}, ConvertNumericTypeToDataType(filterArgs.value(k_NumericType_Key)), dataStructure.getDataAs(path), "0"); + + auto initType = static_cast(filterArgs.value(k_InitType_Key)); + + auto seed = filterArgs.value(k_SeedValue_Key); + if(!filterArgs.value(k_UseSeed_Key)) + { + seed = static_cast(std::chrono::steady_clock::now().time_since_epoch().count()); + } + + if(initType == InitializeType::Random || initType == InitializeType::RangedRandom) + { + // Store Seed Value in Top Level Array + dataStructure.getDataRefAs(DataPath({filterArgs.value(k_SeedArrayName_Key)}))[0] = seed; + } + + InitializeDataInputValues inputValues; + inputValues.InputArrayPath = filterArgs.value(k_DataPath_Key); + inputValues.initType = initType; + inputValues.stepType = static_cast(filterArgs.value(k_StepOperation_Key)); + inputValues.stringValues = StringUtilities::split(StringUtilities::trimmed(filterArgs.value(k_InitValue_Key)), k_DelimiterChar); + inputValues.startValues = StringUtilities::split(StringUtilities::trimmed(filterArgs.value(k_StartingFillValue_Key)), k_DelimiterChar); + inputValues.stepValues = StringUtilities::split(StringUtilities::trimmed(filterArgs.value(k_StepValue_Key)), k_DelimiterChar); + inputValues.seed = seed; + inputValues.randBegin = StringUtilities::split(StringUtilities::trimmed(filterArgs.value(k_InitStartRange_Key)), k_DelimiterChar); + inputValues.randEnd = StringUtilities::split(StringUtilities::trimmed(filterArgs.value(k_InitEndRange_Key)), k_DelimiterChar); + inputValues.standardizeSeed = filterArgs.value(k_StandardizeSeed_Key); + + return InitializeData(dataStructure, messageHandler, shouldCancel, &inputValues)(); +} + +namespace +{ +namespace SIMPL +{ +constexpr StringLiteral k_ScalarTypeKey = "ScalarType"; +constexpr StringLiteral k_NumberOfComponentsKey = "NumberOfComponents"; +constexpr StringLiteral k_InitializationValueKey = "InitializationValue"; +constexpr StringLiteral k_NewArrayKey = "NewArray"; +} // namespace SIMPL +} // namespace + +Result CreateDataArrayAdvancedFilter::FromSIMPLJson(const nlohmann::json& json) +{ + Arguments args = CreateDataArrayAdvancedFilter().getDefaultArguments(); + + args.insertOrAssign(k_AdvancedOptions_Key, false); + + std::vector> results; + + results.push_back(SIMPLConversion::ConvertParameter(args, json, SIMPL::k_ScalarTypeKey, k_NumericType_Key)); + // Initialize Type parameter is not applicable in NX + results.push_back(SIMPLConversion::ConvertParameter(args, json, SIMPL::k_InitializationValueKey, k_InitValue_Key)); + // Initialization Range parameter is not applicable in NX + // Starting Index value parameter is not applicable in NX + results.push_back(SIMPLConversion::ConvertParameter(args, json, SIMPL::k_NewArrayKey, k_DataPath_Key)); + + Result<> conversionResult = MergeResults(std::move(results)); + + return ConvertResultTo(std::move(conversionResult), std::move(args)); +} +} // namespace nx::core diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CreateDataArrayAdvancedFilter.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CreateDataArrayAdvancedFilter.hpp new file mode 100644 index 0000000000..87e1728f0a --- /dev/null +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CreateDataArrayAdvancedFilter.hpp @@ -0,0 +1,114 @@ +#pragma once + +#include "SimplnxCore/SimplnxCore_export.hpp" + +#include "simplnx/Common/StringLiteral.hpp" +#include "simplnx/Filter/FilterTraits.hpp" +#include "simplnx/Filter/IFilter.hpp" + +namespace nx::core +{ +class SIMPLNXCORE_EXPORT CreateDataArrayAdvancedFilter : public IFilter +{ +public: + CreateDataArrayAdvancedFilter() = default; + ~CreateDataArrayAdvancedFilter() noexcept override = default; + + CreateDataArrayAdvancedFilter(const CreateDataArrayAdvancedFilter&) = delete; + CreateDataArrayAdvancedFilter(CreateDataArrayAdvancedFilter&&) noexcept = delete; + + CreateDataArrayAdvancedFilter& operator=(const CreateDataArrayAdvancedFilter&) = delete; + CreateDataArrayAdvancedFilter& operator=(CreateDataArrayAdvancedFilter&&) noexcept = delete; + + // Parameter Keys + static inline constexpr StringLiteral k_NumericType_Key = "numeric_type_index"; + static inline constexpr StringLiteral k_AdvancedOptions_Key = "set_tuple_dimensions"; + static inline constexpr StringLiteral k_CompDims_Key = "component_dimensions"; + static inline constexpr StringLiteral k_TupleDims_Key = "tuple_dimensions"; + static inline constexpr StringLiteral k_DataPath_Key = "output_array_path"; + static inline constexpr StringLiteral k_DataFormat_Key = "data_format"; + static inline constexpr StringLiteral k_InitType_Key = "init_type_index"; + static inline constexpr StringLiteral k_InitValue_Key = "init_value"; + static inline constexpr StringLiteral k_StartingFillValue_Key = "starting_fill_value"; + static inline constexpr StringLiteral k_StepOperation_Key = "step_operation_index"; + static inline constexpr StringLiteral k_StepValue_Key = "step_value"; + static inline constexpr StringLiteral k_UseSeed_Key = "use_seed"; + static inline constexpr StringLiteral k_SeedValue_Key = "seed_value"; + static inline constexpr StringLiteral k_SeedArrayName_Key = "seed_array_name"; + static inline constexpr StringLiteral k_InitStartRange_Key = "init_start_range"; + static inline constexpr StringLiteral k_InitEndRange_Key = "init_end_range"; + static inline constexpr StringLiteral k_StandardizeSeed_Key = "standardize_seed"; + + /** + * @brief Reads SIMPL json and converts it simplnx Arguments. + * @param json + * @return Result + */ + static Result FromSIMPLJson(const nlohmann::json& json); + + /** + * @brief Returns the filter's name. + * @return std::string + */ + std::string name() const override; + + /** + * @brief Returns the C++ classname of this filter. + * @return std::string + */ + std::string className() const override; + + /** + * @brief Returns the filter's UUID. + * @return Uuid + */ + Uuid uuid() const override; + + /** + * @brief Returns the filter name as a human-readable string. + * @return std::string + */ + std::string humanName() const override; + + /** + * @brief Returns the default tags for this filter. + * @return + */ + std::vector defaultTags() const override; + + /** + * @brief Returns a collection of parameters required to execute the filter. + * @return Parameters + */ + Parameters parameters() const override; + + /** + * @brief Creates and returns a copy of the filter. + * @return UniquePointer + */ + UniquePointer clone() const override; + +protected: + /** + * @brief + * @param data + * @param filterArgs + * @param messageHandler + * @return Result + */ + PreflightResult preflightImpl(const DataStructure& dataStructure, const Arguments& filterArgs, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel) const override; + + /** + * @brief + * @param dataStructure + * @param args + * @param pipelineNode + * @param messageHandler + * @return Result<> + */ + Result<> executeImpl(DataStructure& dataStructure, const Arguments& args, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel) const override; +}; +} // namespace nx::core + +SIMPLNX_DEF_FILTER_TRAITS(nx::core, CreateDataArrayAdvancedFilter, "f23464df-abcc-40cf-bf99-f71463df633c"); diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CreateDataArrayFilter.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CreateDataArrayFilter.cpp index b93e305e5c..834668b774 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CreateDataArrayFilter.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CreateDataArrayFilter.cpp @@ -71,17 +71,13 @@ Parameters CreateDataArrayFilter::parameters() const { Parameters params; - params.insertSeparator(Parameters::Separator{"Input Parameter(s)"}); - params.insert(std::make_unique(k_NumericType_Key, "Output Numeric Type", "Numeric Type of data to create", NumericType::int32)); - params.insert(std::make_unique(k_InitializationValue_Key, "Initialization Value", "This value will be used to fill the new array", "0")); - params.insert(std::make_unique(k_NumComps_Key, "Number of Components", "Number of components", 1)); - params.insertSeparator(Parameters::Separator{"Output Data Array"}); - params.insert(std::make_unique(k_DataPath_Key, "Created Array", "Array storing the data", DataPath{})); + params.insert(std::make_unique(k_DataPath_Key, "Created Array", "Array storing the data", DataPath({"Data"}))); + params.insert(std::make_unique(k_NumericType_Key, "Output Numeric Type", "Numeric Type of data to create", NumericType::int32)); params.insert(std::make_unique(k_DataFormat_Key, "Data Format", "This value will specify which data format is used by the array's data store. An empty string results in in-memory data store.", "")); - params.insertSeparator(Parameters::Separator{"Tuple Handling"}); + params.insertSeparator(Parameters::Separator{"Tuple Dimensions"}); params.insertLinkableParameter(std::make_unique( k_AdvancedOptions_Key, "Set Tuple Dimensions [not required if creating inside an Attribute Matrix]", "This allows the user to set the tuple dimensions directly rather than just inheriting them. This option is NOT required if you are creating the Data Array in an Attribute Matrix", true)); @@ -92,6 +88,12 @@ Parameters CreateDataArrayFilter::parameters() const params.insert(std::make_unique(k_TupleDims_Key, "Data Array Dimensions (Slowest to Fastest Dimensions)", "Slowest to Fastest Dimensions. Note this might be opposite displayed by an image geometry.", tableInfo)); + params.insertSeparator(Parameters::Separator{"Component Dimensions"}); + params.insert(std::make_unique(k_NumComps_Key, "Total Number of Components", "Total number of components. Do not set the component dimensions.", 1)); + + params.insertSeparator(Parameters::Separator{"Initialization Options"}); + params.insert(std::make_unique(k_InitializationValue_Key, "Initialization Value", "This value will be used to fill the new array", "0")); + // Associate the Linkable Parameter(s) to the children parameters that they control params.linkParameters(k_AdvancedOptions_Key, k_TupleDims_Key, true); diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/InitializeDataFilter.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/InitializeDataFilter.cpp index c7867427a1..f69b25e01b 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/InitializeDataFilter.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/InitializeDataFilter.cpp @@ -1,6 +1,7 @@ #include "InitializeDataFilter.hpp" -#include "simplnx/Common/TypeTraits.hpp" +#include "SimplnxCore/Filters/Algorithms/InitializeData.hpp" + #include "simplnx/Filter/Actions/CreateArrayAction.hpp" #include "simplnx/Parameters/ArraySelectionParameter.hpp" #include "simplnx/Parameters/BoolParameter.hpp" @@ -14,8 +15,6 @@ #include -#include -#include #include #include #include @@ -24,375 +23,9 @@ using namespace nx::core; namespace { -constexpr char k_DelimiterChar = ';'; - -enum InitializeType : uint64 -{ - FillValue, - Incremental, - Random, - RangedRandom -}; - -enum StepType : uint64 -{ - Addition, - Subtraction -}; - -struct InitializeDataInputValues -{ - InitializeType initType; - StepType stepType; - std::vector stringValues; - std::vector startValues; - std::vector stepValues; - uint64 seed; - std::vector randBegin; - std::vector randEnd; - bool standardizeSeed; -}; - -// At the current time this code could be simplified with a bool in the incremental template, HOWEVER, -// it was done this way to allow for expansion of operations down the line multiplication, division, etc. -template -struct IncrementalOptions -{ - static constexpr bool UsingAddition = UseAddition; - static constexpr bool UsingSubtraction = UseSubtraction; -}; - -using AdditionT = IncrementalOptions; -using SubtractionT = IncrementalOptions; - -template -void ValueFill(AbstractDataStore& dataStore, const std::vector& stringValues) -{ - usize numComp = dataStore.getNumberOfComponents(); // We checked that the values string is greater than max comps size so proceed check free - - if(numComp > 1) - { - std::vector values; - - for(const auto& str : stringValues) - { - values.emplace_back(ConvertTo::convert(str).value()); - } - - usize numTup = dataStore.getNumberOfTuples(); - - for(usize tup = 0; tup < numTup; tup++) - { - for(usize comp = 0; comp < numComp; comp++) - { - dataStore[tup * numComp + comp] = values[comp]; - } - } - } - else - { - Result result = ConvertTo::convert(stringValues[0]); - T value = result.value(); - dataStore.fill(value); - } -} - -template -void IncrementalFill(AbstractDataStore& dataStore, const std::vector& startValues, const std::vector& stepValues) -{ - usize numComp = dataStore.getNumberOfComponents(); // We checked that the values string is greater than max comps size so proceed check free - - std::vector values(numComp); - std::vector steps(numComp); - - for(usize comp = 0; comp < numComp; comp++) - { - Result result = ConvertTo::convert(startValues[comp]); - values[comp] = result.value(); - if constexpr(!std::is_same_v) - { - result = ConvertTo::convert(stepValues[comp]); - steps[comp] = result.value(); - } - } - - usize numTup = dataStore.getNumberOfTuples(); - - if constexpr(std::is_same_v) - { - for(usize comp = 0; comp < numComp; comp++) - { - dataStore[comp] = values[comp]; - - if constexpr(IncrementalOptions::UsingAddition) - { - values[comp] = ConvertTo::convert(stepValues[comp]).value() != 0 ? true : values[comp]; - } - if constexpr(IncrementalOptions::UsingSubtraction) - { - values[comp] = ConvertTo::convert(stepValues[comp]).value() != 0 ? false : values[comp]; - } - } - - for(usize tup = 1; tup < numTup; tup++) - { - for(usize comp = 0; comp < numComp; comp++) - { - dataStore[tup * numComp + comp] = values[comp]; - } - } - } - - if constexpr(!std::is_same_v) - { - for(usize tup = 0; tup < numTup; tup++) - { - for(usize comp = 0; comp < numComp; comp++) - { - dataStore[tup * numComp + comp] = values[comp]; - - if constexpr(IncrementalOptions::UsingAddition) - { - values[comp] += steps[comp]; - } - if constexpr(IncrementalOptions::UsingSubtraction) - { - values[comp] -= steps[comp]; - } - } - } - } -} - -template -void RandomFill(std::vector& dist, AbstractDataStore& dataStore, const uint64 seed, const bool standardizeSeed) -{ - usize numComp = dataStore.getNumberOfComponents(); // We checked that the values string is greater than max comps size so proceed check free - std::vector generators(numComp, std::mt19937_64{}); - - for(usize comp = 0; comp < numComp; comp++) - { - generators[comp].seed((standardizeSeed ? seed : seed + comp)); // If standardizing seed all generators use the same else, use modified seeds - } - - usize numTup = dataStore.getNumberOfTuples(); - - for(usize tup = 0; tup < numTup; tup++) - { - for(usize comp = 0; comp < numComp; comp++) - { - if constexpr(std::is_floating_point_v) - { - if constexpr(Ranged) - { - dataStore[tup * numComp + comp] = static_cast(dist[comp](generators[comp])); - } - if constexpr(!Ranged) - { - if constexpr(std::is_signed_v) - { - dataStore[tup * numComp + comp] = static_cast(dist[comp](generators[comp]) * (std::numeric_limits::max() - 1) * (((rand() & 1) == 0) ? 1 : -1)); - } - if constexpr(!std::is_signed_v) - { - dataStore[tup * numComp + comp] = static_cast(dist[comp](generators[comp]) * std::numeric_limits::max()); - } - } - } - if constexpr(!std::is_floating_point_v) - { - dataStore[tup * numComp + comp] = static_cast(dist[comp](generators[comp])); - } - } - } } -template -void FillIncForwarder(const StepType& stepType, ArgsT&&... args) -{ - switch(stepType) - { - case StepType::Addition: { - ::IncrementalFill(std::forward(args)...); - return; - } - case StepType::Subtraction: { - ::IncrementalFill(std::forward(args)...); - return; - } - } -} - -template -void FillRandomForwarder(const std::vector& range, usize numComp, ArgsT&&... args) -{ - if constexpr(std::is_same_v) - { - std::vector> dists; - for(usize comp = 0; comp < numComp * 2; comp += 2) - { - dists.emplace_back((range.at(comp) ? 1 : 0), (range.at(comp + 1) ? 1 : 0)); - } - ::RandomFill>(dists, std::forward(args)...); - return; - } - if constexpr(!std::is_floating_point_v) - { - std::vector> dists; - for(usize comp = 0; comp < numComp * 2; comp += 2) - { - dists.emplace_back(range.at(comp), range.at(comp + 1)); - } - ::RandomFill>(dists, std::forward(args)...); - } - if constexpr(std::is_floating_point_v) - { - if constexpr(Ranged) - { - - std::vector> dists; - for(usize comp = 0; comp < numComp * 2; comp += 2) - { - dists.emplace_back(range.at(comp), range.at(comp + 1)); - } - ::RandomFill>(dists, std::forward(args)...); - } - if constexpr(!Ranged) - { - std::vector> dists; - for(usize comp = 0; comp < numComp * 2; comp += 2) - { - dists.emplace_back(0, 1); - } - ::RandomFill>(dists, std::forward(args)...); - } - } -} - -std::vector standardizeMultiComponent(const usize numComps, const std::vector& componentValues) -{ - if(componentValues.size() == numComps) - { - return {componentValues}; - } - else - { - std::vector standardized(numComps); - for(usize comp = 0; comp < numComps; comp++) - { - standardized[comp] = componentValues[0]; - } - return standardized; - } -} - -struct FillArrayFunctor -{ - template - void operator()(IDataArray& iDataArray, const InitializeDataInputValues& inputValues) - { - auto& dataStore = iDataArray.template getIDataStoreRefAs>(); - usize numComp = dataStore.getNumberOfComponents(); // We checked that the values string is greater than max comps size so proceed check free - - switch(inputValues.initType) - { - case InitializeType::FillValue: { - return ::ValueFill(dataStore, standardizeMultiComponent(numComp, inputValues.stringValues)); - } - case InitializeType::Incremental: { - return ::FillIncForwarder(inputValues.stepType, dataStore, standardizeMultiComponent(numComp, inputValues.startValues), standardizeMultiComponent(numComp, inputValues.stepValues)); - } - case InitializeType::Random: { - std::vector range; - if constexpr(!std::is_same_v) - { - for(usize comp = 0; comp < numComp; comp++) - { - range.push_back(std::numeric_limits::min()); - range.push_back(std::numeric_limits::max()); - } - } - if constexpr(std::is_same_v) - { - for(usize comp = 0; comp < numComp; comp++) - { - range.push_back(false); - range.push_back(true); - } - } - return ::FillRandomForwarder(range, numComp, dataStore, inputValues.seed, inputValues.standardizeSeed); - } - case InitializeType::RangedRandom: { - auto randBegin = standardizeMultiComponent(numComp, inputValues.randBegin); - auto randEnd = standardizeMultiComponent(numComp, inputValues.randEnd); - - std::vector range; - for(usize comp = 0; comp < numComp; comp++) - { - Result result = ConvertTo::convert(randBegin[comp]); - range.push_back(result.value()); - result = ConvertTo::convert(randEnd[comp]); - range.push_back(result.value()); - } - return ::FillRandomForwarder(range, numComp, dataStore, inputValues.seed, inputValues.standardizeSeed); - } - } - } -}; - -struct ValidateMultiInputFunctor -{ - // The single comp size validation defaults to off as size 0 is checked earlier in the function - template - IFilter::PreflightResult operator()(const usize expectedComp, const std::string& unfilteredStr, const usize singleCompSize = 0) - { - std::vector splitVals = StringUtilities::split(StringUtilities::trimmed(unfilteredStr), k_DelimiterChar); - - if(splitVals.empty()) - { - return IFilter::MakePreflightErrorResult(-11610, fmt::format("A required parameter is unable to be processed with '{}' delimiter. Input: {}", k_DelimiterChar, unfilteredStr)); - } - - for(usize comp = 0; comp < splitVals.size(); comp++) - { - if(splitVals[comp].empty()) - { - return IFilter::MakePreflightErrorResult(-11611, fmt::format("Empty value found after '{}' components were converted. Check for duplicate '{}' next to one another.", comp, k_DelimiterChar)); - } - - Result result = ConvertTo::convert(splitVals[comp]); - - if(result.invalid()) - { - return IFilter::MakePreflightErrorResult(-11612, fmt::format("Unable to process '{}' into a {} value.", splitVals[comp], DataTypeToString(GetDataType()))); - } - } - - if(splitVals.size() == expectedComp) - { - return {}; // Valid - } - - if(splitVals.size() == singleCompSize) - { - return {}; // Valid - } - - if(splitVals.size() == expectedComp + 1) - { - if(unfilteredStr.back() == k_DelimiterChar) - { - return IFilter::MakePreflightErrorResult(-11613, fmt::format("Remove the extra delimiter '{}' at the end of your value sequence: {}.", k_DelimiterChar, unfilteredStr)); - } - } - - return IFilter::MakePreflightErrorResult(-11614, - fmt::format("Using '{}' as a delimiter we are unable to break '{}' into the required {} components.", k_DelimiterChar, unfilteredStr, expectedComp)); - } -}; -} // namespace - namespace nx::core { //------------------------------------------------------------------------------ @@ -432,17 +65,17 @@ Parameters InitializeDataFilter::parameters() const params.insertSeparator(Parameters::Separator{"Input Parameter(s)"}); - params.insertLinkableParameter(std::make_unique(k_InitType_Key, "Initialization Type", "Method for determining the what values of the data in the array should be initialized to", - static_cast(0), - ChoicesParameter::Choices{"Fill Value", "Incremental", "Random", "Random With Range"})); // sequence dependent DO NOT REORDER + params.insertLinkableParameter(std::make_unique( + k_InitType_Key, "Initialization Type", "Method for determining the what values of the data in the array should be initialized to", static_cast(0), + ChoicesParameter::Choices{"Fill Value", "Incremental/Decremental", "Random", "Random With Range"})); // sequence dependent DO NOT REORDER params.insert(std::make_unique(k_InitValue_Key, "Fill Values [Seperated with ;]", "Specify values for each component. Ex: A 3-component array would be 6;8;12 and every tuple would have these same component values", "1;1;1")); params.insert(std::make_unique(k_StartingFillValue_Key, "Starting Value [Seperated with ;]", "The value to start incrementing from", "0;1;2")); params.insert(std::make_unique(k_StepOperation_Key, "Step Operation", "The type of step operation to preform", static_cast(0), - ChoicesParameter::Choices{"Addition", "Subtraction"})); - params.insert(std::make_unique(k_StepValue_Key, "Increment/Step Value [Seperated with ;]", "The number to increment/decrement the fill value by", "1;1;1")); + ChoicesParameter::Choices{"Incrementing", "Decrementing"})); + params.insert(std::make_unique(k_StepValue_Key, "Step Value [Seperated with ;]", "The number to increment/decrement the fill value by", "1;1;1")); params.insert(std::make_unique(k_UseSeed_Key, "Use Seed for Random Generation", "When true the Seed Value will be used to seed the generator", false)); params.insert(std::make_unique>(k_SeedValue_Key, "Seed Value", "The seed fed into the random generator", std::mt19937::default_seed)); @@ -490,16 +123,16 @@ IFilter::UniquePointer InitializeDataFilter::clone() const } //------------------------------------------------------------------------------ -IFilter::PreflightResult InitializeDataFilter::preflightImpl(const DataStructure& dataStructure, const Arguments& args, const MessageHandler& messageHandler, +IFilter::PreflightResult InitializeDataFilter::preflightImpl(const DataStructure& dataStructure, const Arguments& filterArgs, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel) const { - auto seedArrayNameValue = args.value(k_SeedArrayName_Key); - auto initializeTypeValue = static_cast(args.value(k_InitType_Key)); + auto seedArrayNameValue = filterArgs.value(k_SeedArrayName_Key); + auto initializeTypeValue = static_cast(filterArgs.value(k_InitType_Key)); nx::core::Result resultOutputActions; std::vector preflightUpdatedValues; - auto& iDataArray = dataStructure.getDataRefAs(args.value(k_ArrayPath_Key)); + auto& iDataArray = dataStructure.getDataRefAs(filterArgs.value(k_ArrayPath_Key)); usize numComp = iDataArray.getNumberOfComponents(); // check that the values string is greater than max comps if(iDataArray.getDataType() == DataType::boolean) @@ -548,7 +181,7 @@ IFilter::PreflightResult InitializeDataFilter::preflightImpl(const DataStructure switch(initializeTypeValue) { case InitializeType::FillValue: { - auto result = ExecuteDataFunction(::ValidateMultiInputFunctor{}, iDataArray.getDataType(), numComp, args.value(k_InitValue_Key), 1); + auto result = ExecuteDataFunction(::ValidateMultiInputFunctor{}, iDataArray.getDataType(), numComp, filterArgs.value(k_InitValue_Key), 1); if(result.outputActions.invalid()) { return {MergeResults(result.outputActions, std::move(resultOutputActions)), std::move(preflightUpdatedValues)}; @@ -559,7 +192,7 @@ IFilter::PreflightResult InitializeDataFilter::preflightImpl(const DataStructure break; } case InitializeType::Incremental: { - auto result = ExecuteDataFunction(::ValidateMultiInputFunctor{}, iDataArray.getDataType(), numComp, args.value(k_StartingFillValue_Key), 1); + auto result = ExecuteDataFunction(::ValidateMultiInputFunctor{}, iDataArray.getDataType(), numComp, filterArgs.value(k_StartingFillValue_Key), 1); if(result.outputActions.invalid()) { return {MergeResults(result.outputActions, std::move(resultOutputActions)), std::move(preflightUpdatedValues)}; @@ -573,7 +206,7 @@ IFilter::PreflightResult InitializeDataFilter::preflightImpl(const DataStructure updatedValStrm << "We detected that you are doing an incremental operation on a boolean array.\n"; updatedValStrm << "For the step values please enter uint8 values, preferably a 0 or 1 only.\n"; - switch(static_cast(args.value(k_StepOperation_Key))) + switch(static_cast(filterArgs.value(k_StepOperation_Key))) { case Addition: { updatedValStrm << "You have currently selected the addition operation.\nAny step value that is greater than 0 will cause all values to be 'true' after the first tuple, 'true' " @@ -595,7 +228,7 @@ IFilter::PreflightResult InitializeDataFilter::preflightImpl(const DataStructure preflightUpdatedValues.push_back({"Boolean Incremental Nuances", updatedValStrm.str()}); - result = ExecuteDataFunction(::ValidateMultiInputFunctor{}, DataType::uint8, numComp, args.value(k_StepValue_Key), 1); + result = ExecuteDataFunction(::ValidateMultiInputFunctor{}, DataType::uint8, numComp, filterArgs.value(k_StepValue_Key), 1); if(result.outputActions.invalid()) { return {MergeResults(result.outputActions, std::move(resultOutputActions)), std::move(preflightUpdatedValues)}; @@ -603,13 +236,13 @@ IFilter::PreflightResult InitializeDataFilter::preflightImpl(const DataStructure } else { - result = ExecuteDataFunction(::ValidateMultiInputFunctor{}, iDataArray.getDataType(), numComp, args.value(k_StepValue_Key), 1); + result = ExecuteDataFunction(::ValidateMultiInputFunctor{}, iDataArray.getDataType(), numComp, filterArgs.value(k_StepValue_Key), 1); if(result.outputActions.invalid()) { return {MergeResults(result.outputActions, std::move(resultOutputActions)), std::move(preflightUpdatedValues)}; } } - auto values = StringUtilities::split(args.value(k_StepValue_Key), ";", false); + auto values = StringUtilities::split(filterArgs.value(k_StepValue_Key), ";", false); std::vector zeroIdx; for(size_t i = 0; i < values.size(); i++) { @@ -629,13 +262,13 @@ IFilter::PreflightResult InitializeDataFilter::preflightImpl(const DataStructure break; } case InitializeType::RangedRandom: { - auto result = ExecuteDataFunction(::ValidateMultiInputFunctor{}, iDataArray.getDataType(), numComp, args.value(k_InitStartRange_Key), 1); + auto result = ExecuteDataFunction(::ValidateMultiInputFunctor{}, iDataArray.getDataType(), numComp, filterArgs.value(k_InitStartRange_Key), 1); if(result.outputActions.invalid()) { return {MergeResults(result.outputActions, std::move(resultOutputActions)), std::move(preflightUpdatedValues)}; } - result = ExecuteDataFunction(::ValidateMultiInputFunctor{}, iDataArray.getDataType(), numComp, args.value(k_InitEndRange_Key), 1); + result = ExecuteDataFunction(::ValidateMultiInputFunctor{}, iDataArray.getDataType(), numComp, filterArgs.value(k_InitEndRange_Key), 1); if(result.outputActions.invalid()) { return {MergeResults(result.outputActions, std::move(resultOutputActions)), std::move(preflightUpdatedValues)}; @@ -649,7 +282,7 @@ IFilter::PreflightResult InitializeDataFilter::preflightImpl(const DataStructure if(numComp == 1) { - if(args.value(k_StandardizeSeed_Key)) + if(filterArgs.value(k_StandardizeSeed_Key)) { operationNuancesStrm << fmt::format("You chose to standardize the seed for each component, but the array {} is a single component so it will not alter the randomization scheme.", iDataArray.getName()); @@ -657,7 +290,7 @@ IFilter::PreflightResult InitializeDataFilter::preflightImpl(const DataStructure } else { - if(args.value(k_StandardizeSeed_Key)) + if(filterArgs.value(k_StandardizeSeed_Key)) { operationNuancesStrm << "This generates THE SAME sequences of random numbers for each component in the array based on one seed.\n"; operationNuancesStrm << "The resulting array will look like | 1,1,1 | 9,9,9 | ...\n"; @@ -679,13 +312,13 @@ IFilter::PreflightResult InitializeDataFilter::preflightImpl(const DataStructure } //------------------------------------------------------------------------------ -Result<> InitializeDataFilter::executeImpl(DataStructure& dataStructure, const Arguments& args, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, +Result<> InitializeDataFilter::executeImpl(DataStructure& dataStructure, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel) const { - auto initType = static_cast(args.value(k_InitType_Key)); + auto initType = static_cast(filterArgs.value(k_InitType_Key)); - auto seed = args.value(k_SeedValue_Key); - if(!args.value(k_UseSeed_Key)) + auto seed = filterArgs.value(k_SeedValue_Key); + if(!filterArgs.value(k_UseSeed_Key)) { seed = static_cast(std::chrono::steady_clock::now().time_since_epoch().count()); } @@ -693,25 +326,21 @@ Result<> InitializeDataFilter::executeImpl(DataStructure& dataStructure, const A if(initType == InitializeType::Random || initType == InitializeType::RangedRandom) { // Store Seed Value in Top Level Array - dataStructure.getDataRefAs(DataPath({args.value(k_SeedArrayName_Key)}))[0] = seed; + dataStructure.getDataRefAs(DataPath({filterArgs.value(k_SeedArrayName_Key)}))[0] = seed; } InitializeDataInputValues inputValues; - + inputValues.InputArrayPath = filterArgs.value(k_ArrayPath_Key); inputValues.initType = initType; - inputValues.stepType = static_cast(args.value(k_StepOperation_Key)); - inputValues.stringValues = StringUtilities::split(StringUtilities::trimmed(args.value(k_InitValue_Key)), k_DelimiterChar); - inputValues.startValues = StringUtilities::split(StringUtilities::trimmed(args.value(k_StartingFillValue_Key)), k_DelimiterChar); - inputValues.stepValues = StringUtilities::split(StringUtilities::trimmed(args.value(k_StepValue_Key)), k_DelimiterChar); + inputValues.stepType = static_cast(filterArgs.value(k_StepOperation_Key)); + inputValues.stringValues = StringUtilities::split(StringUtilities::trimmed(filterArgs.value(k_InitValue_Key)), k_DelimiterChar); + inputValues.startValues = StringUtilities::split(StringUtilities::trimmed(filterArgs.value(k_StartingFillValue_Key)), k_DelimiterChar); + inputValues.stepValues = StringUtilities::split(StringUtilities::trimmed(filterArgs.value(k_StepValue_Key)), k_DelimiterChar); inputValues.seed = seed; - inputValues.randBegin = StringUtilities::split(StringUtilities::trimmed(args.value(k_InitStartRange_Key)), k_DelimiterChar); - inputValues.randEnd = StringUtilities::split(StringUtilities::trimmed(args.value(k_InitEndRange_Key)), k_DelimiterChar); - inputValues.standardizeSeed = args.value(k_StandardizeSeed_Key); - - auto& iDataArray = dataStructure.getDataRefAs(args.value(k_ArrayPath_Key)); - - ExecuteDataFunction(::FillArrayFunctor{}, iDataArray.getDataType(), iDataArray, inputValues); + inputValues.randBegin = StringUtilities::split(StringUtilities::trimmed(filterArgs.value(k_InitStartRange_Key)), k_DelimiterChar); + inputValues.randEnd = StringUtilities::split(StringUtilities::trimmed(filterArgs.value(k_InitEndRange_Key)), k_DelimiterChar); + inputValues.standardizeSeed = filterArgs.value(k_StandardizeSeed_Key); - return {}; + return InitializeData(dataStructure, messageHandler, shouldCancel, &inputValues)(); } } // namespace nx::core diff --git a/src/Plugins/SimplnxCore/test/CMakeLists.txt b/src/Plugins/SimplnxCore/test/CMakeLists.txt index 93e0bab441..691dc537d1 100644 --- a/src/Plugins/SimplnxCore/test/CMakeLists.txt +++ b/src/Plugins/SimplnxCore/test/CMakeLists.txt @@ -52,6 +52,7 @@ set(${PLUGIN_NAME}UnitTest_SRCS CoreFilterTest.cpp CreateAttributeMatrixTest.cpp CreateDataArrayTest.cpp + CreateDataArrayAdvancedTest.cpp CreateFeatureArrayFromElementArrayTest.cpp CreateGeometryTest.cpp CreateImageGeometryTest.cpp diff --git a/src/Plugins/SimplnxCore/test/CreateDataArrayAdvancedTest.cpp b/src/Plugins/SimplnxCore/test/CreateDataArrayAdvancedTest.cpp new file mode 100644 index 0000000000..670acac366 --- /dev/null +++ b/src/Plugins/SimplnxCore/test/CreateDataArrayAdvancedTest.cpp @@ -0,0 +1,138 @@ +#include "SimplnxCore/Filters/CreateDataArrayAdvancedFilter.hpp" +#include "SimplnxCore/SimplnxCore_test_dirs.hpp" + +#include "simplnx/Parameters/DynamicTableParameter.hpp" +#include "simplnx/UnitTest/UnitTestCommon.hpp" + +#include + +using namespace nx::core; + +TEST_CASE("SimplnxCore::CreateDataArrayAdvancedFilter(Instantiate)", "[SimplnxCore][CreateDataArrayAdvancedFilter]") +{ + static constexpr uint64 k_NComp = 3; + static constexpr uint64 k_NumTuples = 25; + const static DynamicTableInfo::TableDataType k_TupleDims = {{static_cast(k_NumTuples)}}; + + static const DataPath k_DataPath({"foo"}); + + CreateDataArrayAdvancedFilter filter; + DataStructure dataStructure; + Arguments args; + + args.insert(CreateDataArrayAdvancedFilter::k_NumericType_Key, std::make_any(NumericType::int32)); + args.insert(CreateDataArrayAdvancedFilter::k_CompDims_Key, std::make_any(DynamicTableInfo::TableDataType{{static_cast(k_NComp)}})); + args.insert(CreateDataArrayAdvancedFilter::k_TupleDims_Key, std::make_any(k_TupleDims)); + args.insert(CreateDataArrayAdvancedFilter::k_DataPath_Key, std::make_any(k_DataPath)); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_VALID(result.result); +} + +TEST_CASE("SimplnxCore::CreateDataArrayAdvancedFilter(Invalid Parameters)", "[SimplnxCore][CreateDataArrayAdvancedFilter]") +{ + static constexpr uint64 k_NComp = 3; + static constexpr uint64 k_NumTuples = 25; + const static DynamicTableInfo::TableDataType k_TupleDims = {{static_cast(k_NumTuples)}}; + static const DataPath k_DataPath({"foo"}); + + CreateDataArrayAdvancedFilter filter; + DataStructure dataStructure; + Arguments args; + + SECTION("Section1") + { + args.insert(CreateDataArrayAdvancedFilter::k_NumericType_Key, std::make_any(NumericType::uint16)); + args.insert(CreateDataArrayAdvancedFilter::k_CompDims_Key, std::make_any(DynamicTableInfo::TableDataType{{static_cast(k_NComp)}})); + args.insert(CreateDataArrayAdvancedFilter::k_TupleDims_Key, std::make_any(k_TupleDims)); + args.insert(CreateDataArrayAdvancedFilter::k_DataPath_Key, std::make_any(k_DataPath)); + args.insert(CreateDataArrayAdvancedFilter::k_InitValue_Key, std::make_any("-1")); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_INVALID(result.result); + } + SECTION("Section2") + { + args.insert(CreateDataArrayAdvancedFilter::k_NumericType_Key, std::make_any(NumericType::int8)); + args.insert(CreateDataArrayAdvancedFilter::k_CompDims_Key, std::make_any(DynamicTableInfo::TableDataType{{static_cast(k_NComp)}})); + args.insert(CreateDataArrayAdvancedFilter::k_TupleDims_Key, std::make_any(k_TupleDims)); + args.insert(CreateDataArrayAdvancedFilter::k_DataPath_Key, std::make_any(k_DataPath)); + args.insert(CreateDataArrayAdvancedFilter::k_InitValue_Key, std::make_any("1024")); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_INVALID(result.result); + } + SECTION("Section3") + { + args.insert(CreateDataArrayAdvancedFilter::k_NumericType_Key, std::make_any(NumericType::float32)); + args.insert(CreateDataArrayAdvancedFilter::k_CompDims_Key, std::make_any(DynamicTableInfo::TableDataType{{0.0}})); + args.insert(CreateDataArrayAdvancedFilter::k_TupleDims_Key, std::make_any(k_TupleDims)); + args.insert(CreateDataArrayAdvancedFilter::k_DataPath_Key, std::make_any(k_DataPath)); + args.insert(CreateDataArrayAdvancedFilter::k_InitValue_Key, std::make_any("1")); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_INVALID(result.result); + } + SECTION("Section4") + { + args.insert(CreateDataArrayAdvancedFilter::k_NumericType_Key, std::make_any(NumericType::float32)); + args.insert(CreateDataArrayAdvancedFilter::k_CompDims_Key, std::make_any(DynamicTableInfo::TableDataType{{1.0}})); + + DynamicTableInfo::TableDataType tupleDims = {{static_cast(0.0)}}; + args.insert(CreateDataArrayAdvancedFilter::k_TupleDims_Key, std::make_any(tupleDims)); + args.insert(CreateDataArrayAdvancedFilter::k_DataPath_Key, std::make_any(k_DataPath)); + args.insert(CreateDataArrayAdvancedFilter::k_InitValue_Key, std::make_any("1")); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_INVALID(result.result); + } + SECTION("Section5") + { + args.insert(CreateDataArrayAdvancedFilter::k_NumericType_Key, std::make_any(NumericType::int8)); + args.insert(CreateDataArrayAdvancedFilter::k_CompDims_Key, std::make_any(DynamicTableInfo::TableDataType{{1.0}})); + DynamicTableInfo::TableDataType tupleDims = {{static_cast(1.0)}}; + args.insert(CreateDataArrayAdvancedFilter::k_TupleDims_Key, std::make_any(tupleDims)); + args.insert(CreateDataArrayAdvancedFilter::k_DataPath_Key, std::make_any(k_DataPath)); + args.insert(CreateDataArrayAdvancedFilter::k_InitValue_Key, std::make_any("")); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_INVALID(result.result); + } + SECTION("Section6") + { + args.insert(CreateDataArrayAdvancedFilter::k_NumericType_Key, std::make_any(NumericType::int8)); + args.insert(CreateDataArrayAdvancedFilter::k_CompDims_Key, std::make_any(DynamicTableInfo::TableDataType{{1.0}})); + DynamicTableInfo::TableDataType tupleDims = {{static_cast(1.0)}}; + args.insert(CreateDataArrayAdvancedFilter::k_TupleDims_Key, std::make_any(tupleDims)); + args.insert(CreateDataArrayAdvancedFilter::k_DataPath_Key, std::make_any(k_DataPath)); + args.insert(CreateDataArrayAdvancedFilter::k_InitValue_Key, std::make_any("1000")); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_INVALID(result.result); + + args.insert(CreateDataArrayAdvancedFilter::k_NumericType_Key, std::make_any(NumericType::uint8)); + args.insert(CreateDataArrayAdvancedFilter::k_InitValue_Key, std::make_any("-1")); + result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_INVALID(result.result); + + args.insert(CreateDataArrayAdvancedFilter::k_NumericType_Key, std::make_any(NumericType::int16)); + args.insert(CreateDataArrayAdvancedFilter::k_InitValue_Key, std::make_any("70000")); + result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_INVALID(result.result); + + args.insert(CreateDataArrayAdvancedFilter::k_NumericType_Key, std::make_any(NumericType::uint16)); + args.insert(CreateDataArrayAdvancedFilter::k_InitValue_Key, std::make_any("-1")); + result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_INVALID(result.result); + + args.insert(CreateDataArrayAdvancedFilter::k_NumericType_Key, std::make_any(NumericType::int32)); + args.insert(CreateDataArrayAdvancedFilter::k_InitValue_Key, std::make_any("4294967297")); + result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_INVALID(result.result); + + args.insert(CreateDataArrayAdvancedFilter::k_NumericType_Key, std::make_any(NumericType::int32)); + args.insert(CreateDataArrayAdvancedFilter::k_InitValue_Key, std::make_any("-4294967297")); + result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_INVALID(result.result); + } +} diff --git a/wrapping/python/examples/scripts/create_data_array_advanced.py b/wrapping/python/examples/scripts/create_data_array_advanced.py new file mode 100644 index 0000000000..066de1dae3 --- /dev/null +++ b/wrapping/python/examples/scripts/create_data_array_advanced.py @@ -0,0 +1,168 @@ +""" +Important Note +============== + +This python file can be used as an example of how to execute the Create Data Array +(Advanced) filter using all the various initialization options. + +If you plan to use the codes below (and you are welcome to), there are a few things +that you, the developer, should take note of: + +Import Statements +----------------- + +You will most likely *NOT* need to include the following code: + + .. code:: python + + import simplnx_test_dirs as nxtest + +Filter Error Detection +---------------------- + +In each section of code a filter is created and executed immediately. This may or +may *not* be what you want to do. You can also preflight the filter to verify the +correctness of the filters before executing the filter **although** this is done +for you when the filter is executed. As such, you will want to check the 'result' +variable to see if there are any errors or warnings. If there **are** any then +you, as the developer, should act appropriately on the errors or warnings. +More specifically, this bit of code: + + .. code:: python + + nxtest.check_filter_result(nxor.ReadAngDataFilter, result) + +is used by the simplnx unit testing framework and should be replaced by your own +error checking code. You are welcome to look up the function definition and use +that. + +""" +import simplnx as nx + +import simplnx_test_dirs as nxtest + +import numpy as np + +# Create a Data Structure +data_structure = nx.DataStructure() + +#------------------------------------------------------------------------------ +# Create Data Array With Fill Value +#------------------------------------------------------------------------------ +output_array_path = nx.DataPath(["Fill Value Array"]) +array_type = nx.NumericType.float32 +tuple_dims = [[10]] +component_dims = [[3, 2]] +create_array_nx_filter = nx.CreateDataArrayAdvancedFilter() +result = create_array_nx_filter.execute(data_structure=data_structure, + component_dimensions=component_dims, + data_format="", + init_type_index=0, # Fill value initialization type + init_value="1;4;7;2;1;5", + numeric_type_index=array_type, + output_array_path=output_array_path, + tuple_dimensions=tuple_dims) +nxtest.check_filter_result(nx.CreateDataArrayFilter, result) + + +# We can check the output of the filter by simply printing the array +npdata = data_structure[output_array_path].npview() +print(npdata) + +#------------------------------------------------------------------------------ +# Create Data Array With Incremental Values +#------------------------------------------------------------------------------ +output_array_path = nx.DataPath(["Incremental Value Array"]) +array_type = nx.NumericType.int16 +tuple_dims = [[3, 3]] +component_dims = [[3]] +create_array_nx_filter = nx.CreateDataArrayAdvancedFilter() +result = create_array_nx_filter.execute(data_structure=data_structure, + component_dimensions=component_dims, + data_format="", + init_type_index=1, # Initialization type = Incremental/Decremental + starting_fill_value="1;4;7", + step_operation_index=0, # Step type = Incrementing + step_value="1;2;3", + numeric_type_index=array_type, + output_array_path=output_array_path, + tuple_dimensions=tuple_dims) +nxtest.check_filter_result(nx.CreateDataArrayFilter, result) + + +# We can check the output of the filter by simply printing the array +npdata = data_structure[output_array_path].npview() +print(npdata) + +#------------------------------------------------------------------------------ +# Create Data Array With Decremental Values +#------------------------------------------------------------------------------ +output_array_path = nx.DataPath(["Decremental Value Array"]) +array_type = nx.NumericType.int32 +tuple_dims = [[3, 3]] +component_dims = [[3]] +create_array_nx_filter = nx.CreateDataArrayAdvancedFilter() +result = create_array_nx_filter.execute(data_structure=data_structure, + component_dimensions=component_dims, + data_format="", + init_type_index=1, # Initialization type = Incremental/Decremental + starting_fill_value="1;4;7", + step_operation_index=1, # Step type = Decrementing + step_value="1;2;3", + numeric_type_index=array_type, + output_array_path=output_array_path, + tuple_dimensions=tuple_dims) +nxtest.check_filter_result(nx.CreateDataArrayFilter, result) + + +# We can check the output of the filter by simply printing the array +npdata = data_structure[output_array_path].npview() +print(npdata) + +#------------------------------------------------------------------------------ +# Create Data Array With Random Values +#------------------------------------------------------------------------------ +output_array_path = nx.DataPath(["Random Value Array"]) +array_type = nx.NumericType.int8 +tuple_dims = [[4, 4]] +component_dims = [[2]] +create_array_nx_filter = nx.CreateDataArrayAdvancedFilter() +result = create_array_nx_filter.execute(data_structure=data_structure, + component_dimensions=component_dims, + data_format="", + init_type_index=2, # Initialization type = Random + numeric_type_index=array_type, + output_array_path=output_array_path, + tuple_dimensions=tuple_dims, + seed_array_name="Seed Value Array") +nxtest.check_filter_result(nx.CreateDataArrayFilter, result) + + +# We can check the output of the filter by simply printing the array +npdata = data_structure[output_array_path].npview() +print(npdata) + +#------------------------------------------------------------------------------ +# Create Data Array With Random Values In A Range +#------------------------------------------------------------------------------ +output_array_path = nx.DataPath(["Random Values In A Range Array"]) +array_type = nx.NumericType.int8 +tuple_dims = [[4, 4]] +component_dims = [[2]] +create_array_nx_filter = nx.CreateDataArrayAdvancedFilter() +result = create_array_nx_filter.execute(data_structure=data_structure, + component_dimensions=component_dims, + data_format="", + init_type_index=3, # Initialization type = Random With Range + init_start_range="-2;4", + init_end_range="4;7", + numeric_type_index=array_type, + output_array_path=output_array_path, + tuple_dimensions=tuple_dims, + seed_array_name="Seed Value Array 2") +nxtest.check_filter_result(nx.CreateDataArrayFilter, result) + + +# We can check the output of the filter by simply printing the array +npdata = data_structure[output_array_path].npview() +print(npdata) \ No newline at end of file