diff --git a/src/Plugins/SimplnxCore/CMakeLists.txt b/src/Plugins/SimplnxCore/CMakeLists.txt index cbfdada1db..0002e48c1a 100644 --- a/src/Plugins/SimplnxCore/CMakeLists.txt +++ b/src/Plugins/SimplnxCore/CMakeLists.txt @@ -19,8 +19,9 @@ set(FilterList CombineAttributeArraysFilter CombineNodeBasedGeometriesFilter CombineStlFilesFilter - ComputeArrayHistogramFilter + CombineTransformationMatricesFilter ComputeArrayHistogramByFeatureFilter + ComputeArrayHistogramFilter ComputeArrayStatisticsFilter ComputeBiasedFeaturesFilter ComputeBoundaryCellsFilter @@ -171,6 +172,7 @@ set(AlgorithmList CombineAttributeArrays CombineNodeBasedGeometries CombineStlFiles + CombineTransformationMatrices ComputeArrayHistogram ComputeArrayHistogramByFeature ComputeArrayStatistics diff --git a/src/Plugins/SimplnxCore/docs/CombineTransformationMatricesFilter.md b/src/Plugins/SimplnxCore/docs/CombineTransformationMatricesFilter.md new file mode 100644 index 0000000000..9598bc2631 --- /dev/null +++ b/src/Plugins/SimplnxCore/docs/CombineTransformationMatricesFilter.md @@ -0,0 +1,17 @@ +# Combine Transformation Matrices Filter + +## Group (Subgroup) + + +## Description + + +% Auto generated parameter table will be inserted here + +## 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/CombineTransformationMatrices.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/CombineTransformationMatrices.cpp new file mode 100644 index 0000000000..9eac81702e --- /dev/null +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/CombineTransformationMatrices.cpp @@ -0,0 +1,106 @@ +#include "CombineTransformationMatrices.hpp" + +#include "simplnx/DataStructure/DataArray.hpp" +#include "simplnx/DataStructure/Geometry/ImageGeom.hpp" +#include "simplnx/Utilities/FilterUtilities.hpp" +#include "simplnx/Utilities/ImageRotationUtilities.hpp" + +#include + +using namespace nx::core; + +namespace +{ +template +Eigen::Matrix CreateEigenMatrix(const AbstractDataStore& dataStore) +{ + Eigen::Matrix matrix; + matrix.fill(0); + matrix << dataStore[0], dataStore[1], dataStore[2], dataStore[3], dataStore[4], dataStore[5], dataStore[6], dataStore[7], dataStore[8], dataStore[9], dataStore[10], dataStore[11], dataStore[12], + dataStore[13], dataStore[14], dataStore[15]; + + return matrix; +} + +struct MatrixOperationFunctor +{ + template + Result<> operator()(const IDataArray& array1, const IDataArray& array2, IDataArray& outputArray) + { + using MatrixType = Eigen::Matrix; + using StoreType = AbstractDataStore; + const auto& array1StoreRef = array1.getIDataStoreRefAs(); + const auto& array2StoreRef = array2.getIDataStoreRefAs(); + + auto eigenMatrix1 = CreateEigenMatrix(array1StoreRef); + auto eigenMatrix2 = CreateEigenMatrix(array2StoreRef); + + MatrixType output = eigenMatrix1 * eigenMatrix2; + + auto& dataStore = outputArray.getIDataStoreRefAs(); + + dataStore[0] = output(0, 0); + dataStore[1] = output(0, 1); + dataStore[2] = output(0, 2); + dataStore[3] = output(0, 3); + dataStore[4] = output(1, 0); + dataStore[5] = output(1, 1); + dataStore[6] = output(1, 2); + dataStore[7] = output(1, 3); + dataStore[8] = output(2, 0); + dataStore[9] = output(2, 1); + dataStore[10] = output(2, 2); + dataStore[11] = output(2, 3); + dataStore[12] = output(3, 0); + dataStore[13] = output(3, 1); + dataStore[14] = output(3, 2); + dataStore[15] = output(3, 3); + + return {}; + } +}; + +} // namespace + +// ----------------------------------------------------------------------------- +CombineTransformationMatrices::CombineTransformationMatrices(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, + CombineTransformationMatricesInputValues* inputValues) +: m_DataStructure(dataStructure) +, m_InputValues(inputValues) +, m_ShouldCancel(shouldCancel) +, m_MessageHandler(mesgHandler) +{ +} + +// ----------------------------------------------------------------------------- +CombineTransformationMatrices::~CombineTransformationMatrices() noexcept = default; + +// ----------------------------------------------------------------------------- +const std::atomic_bool& CombineTransformationMatrices::getCancel() +{ + return m_ShouldCancel; +} + +// ----------------------------------------------------------------------------- +Result<> CombineTransformationMatrices::operator()() +{ + auto& outputArray = m_DataStructure.getDataRefAs(m_InputValues->OutputPath); + auto pathsIter = m_InputValues->SelectedPaths.begin(); + + const auto& array1 = m_DataStructure.getDataRefAs(*pathsIter++); + const auto& array2 = m_DataStructure.getDataRefAs(*pathsIter++); + if(array1.getDataType() != array2.getDataType()) + { + return MakeErrorResult(-89750, "DataType mismatch"); + } + // Combine first two matrices: second * first + ExecuteDataFunction(MatrixOperationFunctor{}, array1.getDataType(), array2, array1, outputArray); + + for(; pathsIter != m_InputValues->SelectedPaths.end(); ++pathsIter) + { + const auto& arrayRef = m_DataStructure.getDataRefAs(*pathsIter); + ExecuteDataFunction(MatrixOperationFunctor{}, outputArray.getDataType(), arrayRef, outputArray, outputArray); + } + + return {}; +} diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/CombineTransformationMatrices.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/CombineTransformationMatrices.hpp new file mode 100644 index 0000000000..bef672393c --- /dev/null +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/CombineTransformationMatrices.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include "SimplnxCore/SimplnxCore_export.hpp" + +#include "simplnx/DataStructure/DataPath.hpp" +#include "simplnx/DataStructure/DataStructure.hpp" +#include "simplnx/Filter/IFilter.hpp" +#include "simplnx/Parameters/ArrayCreationParameter.hpp" +#include "simplnx/Parameters/ArraySelectionParameter.hpp" +#include "simplnx/Parameters/ChoicesParameter.hpp" +#include "simplnx/Parameters/MultiArraySelectionParameter.hpp" + +namespace nx::core +{ + +struct SIMPLNXCORE_EXPORT CombineTransformationMatricesInputValues +{ + MultiArraySelectionParameter::ValueType SelectedPaths; + ArrayCreationParameter::ValueType OutputPath; +}; + +/** + * @class + */ +class SIMPLNXCORE_EXPORT CombineTransformationMatrices +{ +public: + CombineTransformationMatrices(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, CombineTransformationMatricesInputValues* inputValues); + ~CombineTransformationMatrices() noexcept; + + CombineTransformationMatrices(const CombineTransformationMatrices&) = delete; + CombineTransformationMatrices(CombineTransformationMatrices&&) noexcept = delete; + CombineTransformationMatrices& operator=(const CombineTransformationMatrices&) = delete; + CombineTransformationMatrices& operator=(CombineTransformationMatrices&&) noexcept = delete; + + // Error Codes + enum class ErrorCodes : int32 + { + EmptyInputArrays = -2350, + OneInputArray = -2351, + NonPositiveTupleDimValue = -2352, + TypeNameMismatch = -2353, + ComponentShapeMismatch = -2354, + InputArraysEqualAny = -2355, + InputArraysUnsupported = -2356 + }; + + Result<> operator()(); + + const std::atomic_bool& getCancel(); + +private: + DataStructure& m_DataStructure; + const CombineTransformationMatricesInputValues* 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/CombineTransformationMatricesFilter.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CombineTransformationMatricesFilter.cpp new file mode 100644 index 0000000000..ea9f735e81 --- /dev/null +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CombineTransformationMatricesFilter.cpp @@ -0,0 +1,158 @@ +#include "CombineTransformationMatricesFilter.hpp" + +#include "SimplnxCore/Filters/Algorithms/CombineTransformationMatrices.hpp" + +#include "simplnx/DataStructure/DataPath.hpp" +#include "simplnx/DataStructure/Geometry/ImageGeom.hpp" +#include "simplnx/Filter/Actions/CreateArrayAction.hpp" +#include "simplnx/Parameters/ArrayCreationParameter.hpp" +#include "simplnx/Parameters/DataObjectNameParameter.hpp" +#include "simplnx/Parameters/MultiArraySelectionParameter.hpp" +#include "simplnx/Utilities/SIMPLConversion.hpp" + +#include + +using namespace nx::core; + +namespace nx::core +{ +//------------------------------------------------------------------------------ +std::string CombineTransformationMatricesFilter::name() const +{ + return FilterTraits::name.str(); +} + +//------------------------------------------------------------------------------ +std::string CombineTransformationMatricesFilter::className() const +{ + return FilterTraits::className; +} + +//------------------------------------------------------------------------------ +Uuid CombineTransformationMatricesFilter::uuid() const +{ + return FilterTraits::uuid; +} + +//------------------------------------------------------------------------------ +std::string CombineTransformationMatricesFilter::humanName() const +{ + return "Combine Transformation Matrices"; +} + +//------------------------------------------------------------------------------ +std::vector CombineTransformationMatricesFilter::defaultTags() const +{ + return {className(), "Matrix", "Calculate", "Multiplication"}; +} + +//------------------------------------------------------------------------------ +Parameters CombineTransformationMatricesFilter::parameters() const +{ + Parameters params; + + // Create the parameter descriptors that are needed for this filter + params.insertSeparator(Parameters::Separator{"Input Parameter(s)"}); + params.insert(std::make_unique(k_InputArrays_Key, "Input Matrices", "The list of Attribute Arrays that represent Square Matrices of all the same dimensions", + MultiArraySelectionParameter::ValueType{}, MultiArraySelectionParameter::AllowedTypes{IArray::ArrayType::DataArray}, + MultiArraySelectionParameter::AllowedDataTypes{}, MultiArraySelectionParameter::AllowedComponentShapes{{1}})); + + params.insertSeparator(Parameters::Separator{"Output Parameters"}); + params.insert(std::make_unique(k_OutputArray_Key, "Output Array", "The output array that contains the output from the operations.", DataPath({""}))); + + return params; +} + +//------------------------------------------------------------------------------ +IFilter::VersionType CombineTransformationMatricesFilter::parametersVersion() const +{ + return 1; +} + +//------------------------------------------------------------------------------ +IFilter::UniquePointer CombineTransformationMatricesFilter::clone() const +{ + return std::make_unique(); +} + +//------------------------------------------------------------------------------ +IFilter::PreflightResult CombineTransformationMatricesFilter::preflightImpl(const DataStructure& dataStructure, const Arguments& filterArgs, const MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel, const ExecutionContext& executionContext) const +{ + auto inputArrayPaths = filterArgs.value(k_InputArrays_Key); + auto outputArrayPath = filterArgs.value(k_OutputArray_Key); + + PreflightResult preflightResult; + nx::core::Result resultOutputActions; + std::vector preflightUpdatedValues; + + if(inputArrayPaths.empty()) + { + return MakePreflightErrorResult(to_underlying(CombineTransformationMatrices::ErrorCodes::EmptyInputArrays), "No input arrays have been selected. Please select at least 2 input arrays."); + } + + if(inputArrayPaths.size() == 1) + { + return MakePreflightErrorResult(to_underlying(CombineTransformationMatrices::ErrorCodes::OneInputArray), "Only one input array has been selected. Please select at least 2 input arrays."); + } + + // Check for unequal array types, data types, and component dimensions + std::vector cDims; + IArray::ArrayType arrayType; + std::string arrayTypeName; + usize numTuples = 0; + nx::core::DataType arrayDataType; + for(usize i = 0; i < inputArrayPaths.size(); ++i) + { + const auto& inputDataArray = dataStructure.getDataRefAs(inputArrayPaths[i]); + + for(usize j = i + 1; j < inputArrayPaths.size(); ++j) + { + const auto& inputDataArray2 = dataStructure.getDataRefAs(inputArrayPaths[j]); + + if(inputDataArray.getDataType() != inputDataArray2.getDataType()) + { + return MakePreflightErrorResult(to_underlying(CombineTransformationMatrices::ErrorCodes::TypeNameMismatch), + fmt::format("Input array '{}' has array type '{}', but input array '{}' has array type '{}'. The array types must match.", inputArrayPaths[i].toString(), + inputDataArray.getTypeName(), inputArrayPaths[j].toString(), inputDataArray2.getTypeName())); + } + + if(inputDataArray.getNumberOfComponents() != 1) + + { + return MakePreflightErrorResult(to_underlying(CombineTransformationMatrices::ErrorCodes::ComponentShapeMismatch), + fmt::format("Input array '{}' has component shape '{}'. Input arrays must only have a single component.", inputArrayPaths[i].toString(), + fmt::join(inputDataArray.getComponentShape(), ","), inputArrayPaths[j].toString())); + } + + cDims = inputDataArray.getComponentShape(); + arrayType = inputDataArray.getArrayType(); + arrayTypeName = inputDataArray.getTypeName(); + arrayDataType = inputDataArray.getDataType(); + } + + auto tupleShape = inputDataArray.getTupleShape(); + numTuples = std::accumulate(tupleShape.begin(), tupleShape.end(), static_cast(1), std::multiplies<>()); + } + + // create the destination array for the calculated results + { + auto createArrayAction = std::make_unique(DataType::float32, IArray::ShapeType{numTuples}, IArray::ShapeType{1}, outputArrayPath); + resultOutputActions.value().appendAction(std::move(createArrayAction)); + } + + // Return both the resultOutputActions and the preflightUpdatedValues via std::move() + return {std::move(resultOutputActions), std::move(preflightUpdatedValues)}; +} + +//------------------------------------------------------------------------------ +Result<> CombineTransformationMatricesFilter::executeImpl(DataStructure& dataStructure, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel, const ExecutionContext& executionContext) const +{ + CombineTransformationMatricesInputValues inputValues; + inputValues.SelectedPaths = filterArgs.value(k_InputArrays_Key); + inputValues.OutputPath = filterArgs.value(k_OutputArray_Key); + + return CombineTransformationMatrices(dataStructure, messageHandler, shouldCancel, &inputValues)(); +} +} // namespace nx::core diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CombineTransformationMatricesFilter.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CombineTransformationMatricesFilter.hpp new file mode 100644 index 0000000000..82dc88ce90 --- /dev/null +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/CombineTransformationMatricesFilter.hpp @@ -0,0 +1,113 @@ +#pragma once + +#include "SimplnxCore/SimplnxCore_export.hpp" + +#include "simplnx/Filter/FilterTraits.hpp" +#include "simplnx/Filter/IFilter.hpp" + +namespace nx::core +{ +/** + * @class CombineTransformationMatricesFilter + * @brief This filter will .... + */ +class SIMPLNXCORE_EXPORT CombineTransformationMatricesFilter : public IFilter +{ +public: + CombineTransformationMatricesFilter() = default; + ~CombineTransformationMatricesFilter() noexcept override = default; + + CombineTransformationMatricesFilter(const CombineTransformationMatricesFilter&) = delete; + CombineTransformationMatricesFilter(CombineTransformationMatricesFilter&&) noexcept = delete; + + CombineTransformationMatricesFilter& operator=(const CombineTransformationMatricesFilter&) = delete; + CombineTransformationMatricesFilter& operator=(CombineTransformationMatricesFilter&&) noexcept = delete; + + // Parameter Keys + static inline constexpr StringLiteral k_InputArrays_Key = "input_arrays"; + static inline constexpr StringLiteral k_OutputArray_Key = "output_array_path"; + /** + * @brief Reads SIMPL json and converts it simplnx Arguments. + * @param json + * @return Result + */ + static Result FromSIMPLJson(const nlohmann::json& json); + + /** + * @brief Returns the name of the filter. + * @return + */ + std::string name() const override; + + /** + * @brief Returns the C++ classname of this filter. + * @return + */ + std::string className() const override; + + /** + * @brief Returns the uuid of the filter. + * @return + */ + Uuid uuid() const override; + + /** + * @brief Returns the human readable name of the filter. + * @return + */ + std::string humanName() const override; + + /** + * @brief Returns the default tags for this filter. + * @return + */ + std::vector defaultTags() const override; + + /** + * @brief Returns the parameters of the filter (i.e. its inputs) + * @return + */ + Parameters parameters() const override; + + /** + * @brief Returns parameters version integer. + * Initial version should always be 1. + * Should be incremented everytime the parameters change. + * @return VersionType + */ + VersionType parametersVersion() const override; + + /** + * @brief Returns a copy of the filter. + * @return + */ + UniquePointer clone() const override; + +protected: + /** + * @brief Takes in a DataStructure and checks that the filter can be run on it with the given arguments. + * Returns any warnings/errors. Also returns the changes that would be applied to the DataStructure. + * Some parts of the actions may not be completely filled out if all the required information is not available at preflight time. + * @param ds The input DataStructure instance + * @param filterArgs These are the input values for each parameter that is required for the filter + * @param messageHandler The MessageHandler object + * @return Returns a Result object with error or warning values if any of those occurred during execution of this function + */ + PreflightResult preflightImpl(const DataStructure& dataStructure, const Arguments& filterArgs, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel, + const ExecutionContext& executionContext) const override; + + /** + * @brief Applies the filter's algorithm to the DataStructure with the given arguments. Returns any warnings/errors. + * On failure, there is no guarantee that the DataStructure is in a correct state. + * @param ds The input DataStructure instance + * @param filterArgs These are the input values for each parameter that is required for the filter + * @param messageHandler The MessageHandler object + * @return Returns a Result object with error or warning values if any of those occurred during execution of this function + */ + Result<> executeImpl(DataStructure& dataStructure, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel, + const ExecutionContext& executionContext) const override; +}; +} // namespace nx::core + +SIMPLNX_DEF_FILTER_TRAITS(nx::core, CombineTransformationMatricesFilter, "d279a254-ced7-4541-95c1-bd3e59c5b697"); +/* LEGACY UUID FOR THIS FILTER ac99b706-d1e0-5f78-9246-fbbe1efd93d2 */ diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/WriteDREAM3DFilter.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/WriteDREAM3DFilter.cpp index 29886dde8c..c4e6b1292e 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/WriteDREAM3DFilter.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/WriteDREAM3DFilter.cpp @@ -42,7 +42,7 @@ Uuid WriteDREAM3DFilter::uuid() const //------------------------------------------------------------------------------ std::string WriteDREAM3DFilter::humanName() const { - return "Write DREAM3D NX File"; + return "Write DREAM3D-NX File"; } //------------------------------------------------------------------------------ diff --git a/src/Plugins/SimplnxCore/test/CMakeLists.txt b/src/Plugins/SimplnxCore/test/CMakeLists.txt index 6c5163e768..8ba2a03db5 100644 --- a/src/Plugins/SimplnxCore/test/CMakeLists.txt +++ b/src/Plugins/SimplnxCore/test/CMakeLists.txt @@ -19,8 +19,9 @@ set(${PLUGIN_NAME}UnitTest_SRCS CombineAttributeArraysTest.cpp CombineNodeBasedGeometriesTest.cpp CombineStlFilesTest.cpp - ComputeArrayHistogramTest.cpp + CombineTransformationMatricesTest.cpp ComputeArrayHistogramByFeatureTest.cpp + ComputeArrayHistogramTest.cpp ComputeArrayStatisticsTest.cpp ComputeBiasedFeaturesTest.cpp ComputeBoundaryCellsTest.cpp @@ -151,6 +152,7 @@ set(${PLUGIN_NAME}UnitTest_SRCS WriteLosAlamosFFTTest.cpp WriteNodesAndElementsFilesTest.cpp WriteSPParksSitesTest.cpp + WriteSPParksSitesTest.cpp WriteStlFileTest.cpp WriteVtkRectilinearGridTest.cpp ) diff --git a/src/Plugins/SimplnxCore/test/CombineTransformationMatricesTest.cpp b/src/Plugins/SimplnxCore/test/CombineTransformationMatricesTest.cpp new file mode 100644 index 0000000000..999b99f603 --- /dev/null +++ b/src/Plugins/SimplnxCore/test/CombineTransformationMatricesTest.cpp @@ -0,0 +1,36 @@ +#include + +#include "SimplnxCore/Filters/CombineTransformationMatricesFilter.hpp" +#include "SimplnxCore/SimplnxCore_test_dirs.hpp" + +#include "simplnx/UnitTest/UnitTestCommon.hpp" + +namespace fs = std::filesystem; +using namespace nx::core; + +namespace +{ + +} // namespace + +TEST_CASE("SimplnxCore::CombineTransformationMatricesFilter: Valid Filter Execution", "[SimplnxCore][CombineTransformationMatricesFilter]") +{ + const nx::core::UnitTest::TestFileSentinel testDataSentinel(nx::core::unit_test::k_CMakeExecutable, nx::core::unit_test::k_TestFilesDir, "add_bad_data_test.tar.gz", "add_bad_data_test"); + + DataStructure exemplarDataStructure = UnitTest::LoadDataStructure(fs::path(fmt::format("{}/add_bad_data_test/6_6_add_bad_data_test.dream3d", unit_test::k_TestFilesDir))); + + // Instantiate the filter, a DataStructure object and an Arguments Object + CombineTransformationMatricesFilter filter; + DataStructure dataStructure = UnitTest::LoadDataStructure(fs::path(fmt::format("{}/add_bad_data_test/6_6_add_bad_data_baseline.dream3d", unit_test::k_TestFilesDir))); + Arguments args; + + // Create default Parameters for the filter. + + // Preflight the filter and check result + auto preflightResult = filter.preflight(dataStructure, args); + REQUIRE(preflightResult.outputActions.valid()); + + // Execute the filter and check the result + auto executeResult = filter.execute(dataStructure, args); + REQUIRE(executeResult.result.valid()); +}