diff --git a/src/Plugins/SimplnxCore/CMakeLists.txt b/src/Plugins/SimplnxCore/CMakeLists.txt index 24b4cbf3f4..2ceb5bdfc7 100644 --- a/src/Plugins/SimplnxCore/CMakeLists.txt +++ b/src/Plugins/SimplnxCore/CMakeLists.txt @@ -75,6 +75,7 @@ set(FilterList InitializeData InitializeImageGeomCellData InterpolatePointCloudToRegularGridFilter + InterpolateValuesToUnstructuredGridFilter IterativeClosestPointFilter KMeansFilter KMedoidsFilter @@ -171,6 +172,7 @@ set(AlgorithmList GenerateVectorColors GeneratePythonSkeleton ImageContouring + InterpolateValuesToUnstructuredGrid KMeans KMedoids LaplacianSmoothing diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/InterpolateValuesToUnstructuredGrid.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/InterpolateValuesToUnstructuredGrid.cpp new file mode 100644 index 0000000000..73836c7090 --- /dev/null +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/InterpolateValuesToUnstructuredGrid.cpp @@ -0,0 +1,192 @@ +#include "InterpolateValuesToUnstructuredGrid.hpp" + +#include "complex/DataStructure/DataArray.hpp" +#include "complex/DataStructure/Geometry/INodeGeometry0D.hpp" +#include "complex/Utilities/DataGroupUtilities.hpp" +#include "complex/Utilities/FilterUtilities.hpp" +#include "complex/Utilities/ParallelAlgorithmUtilities.hpp" +#include "complex/Utilities/ParallelDataAlgorithm.hpp" +#include "complex/Utilities/ParallelTaskAlgorithm.hpp" + +using namespace complex; + +namespace +{ +float32 distBetweenPoints(const Vec3& srcPoint, const Vec3& destPoint) +{ + return powf(destPoint[0] - srcPoint[0], 2.0f) + pow(destPoint[1] - srcPoint[1], 2.0f) + pow(destPoint[2] - srcPoint[2], 2.0f); +} + +/** + * @brief The CalculateClosestVerticesImpl class implements a threaded algorithm that computes the normal of each + * triangle for a set of triangles + */ +class CalculateClosestVerticesImpl +{ +public: + CalculateClosestVerticesImpl(InterpolateValuesToUnstructuredGrid* filter, const INodeGeometry0D& srcGeom, const INodeGeometry0D& destGeom, std::vector& closestSrcIds, + const IFilter::MessageHandler& messageHandler, const std::atomic_bool& shouldCancel) + : m_Filter(filter) + , m_SrcGeometry(srcGeom) + , m_DestGeometry(destGeom) + , m_ClosestSrcIds(closestSrcIds) + , m_MessageHandler(messageHandler) + , m_ShouldCancel(shouldCancel) + { + } + virtual ~CalculateClosestVerticesImpl() = default; + + void generate(size_t start, size_t end) const + { + auto startTime = std::chrono::steady_clock::now(); + usize counter = 0; + usize increment = (end - start) / 100; + for(usize destVertexId = start; destVertexId < end; destVertexId++) + { + if(m_ShouldCancel) + { + return; + } + + if(counter > increment) + { + auto now = std::chrono::steady_clock::now(); + if(std::chrono::duration_cast(now - startTime).count() < 1000) + { + m_Filter->sendThreadSafeProgressMessage(counter); + counter = 0; + startTime = std::chrono::steady_clock::now(); + } + } + + Vec3 destVertexCoord = m_DestGeometry.getVertexCoordinate(destVertexId); + usize closestVertexId; + float32 closestDist = std::numeric_limits::max(); + for(usize srcVertexId = 0; srcVertexId < m_SrcGeometry.getNumberOfVertices(); srcVertexId++) + { + Vec3 srcVertexCoord = m_SrcGeometry.getVertexCoordinate(srcVertexId); + float32 dist = distBetweenPoints(srcVertexCoord, destVertexCoord); + closestVertexId = (dist < closestDist) ? srcVertexId : closestVertexId; + closestDist = (dist < closestDist) ? dist : closestDist; + } + m_ClosestSrcIds[destVertexId] = closestVertexId; + counter++; + } + + m_Filter->sendThreadSafeProgressMessage(counter); + } + + void operator()(const Range& range) const + { + generate(range.min(), range.max()); + } + +private: + InterpolateValuesToUnstructuredGrid* m_Filter = nullptr; + const INodeGeometry0D& m_SrcGeometry; + const INodeGeometry0D& m_DestGeometry; + std::vector& m_ClosestSrcIds; + const IFilter::MessageHandler& m_MessageHandler; + const std::atomic_bool& m_ShouldCancel; +}; + +struct ExecuteInterpolation +{ + template + void operator()(const IDataArray& iSrcDataArray, IDataArray& iDestDataArray, const std::vector& closestSrcIds) + { + const auto& srcDataArray = dynamic_cast&>(iSrcDataArray); + auto& destDataArray = dynamic_cast&>(iDestDataArray); + + for(usize i = 0; i < closestSrcIds.size(); i++) + { + destDataArray[i] = srcDataArray[closestSrcIds[i]]; + } + } +}; +} // namespace + +// ----------------------------------------------------------------------------- +InterpolateValuesToUnstructuredGrid::InterpolateValuesToUnstructuredGrid(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, + InterpolateValuesToUnstructuredGridInputValues* inputValues) +: m_DataStructure(dataStructure) +, m_InputValues(inputValues) +, m_ShouldCancel(shouldCancel) +, m_MessageHandler(mesgHandler) +{ +} + +// ----------------------------------------------------------------------------- +InterpolateValuesToUnstructuredGrid::~InterpolateValuesToUnstructuredGrid() noexcept = default; + +// ----------------------------------------------------------------------------- +const std::atomic_bool& InterpolateValuesToUnstructuredGrid::getCancel() +{ + return m_ShouldCancel; +} + +// ----------------------------------------------------------------------------- +Result<> InterpolateValuesToUnstructuredGrid::operator()() +{ + auto& srcGeometry = m_DataStructure.getDataRefAs(m_InputValues->SourceGeomPath); + auto& destGeometry = m_DataStructure.getDataRefAs(m_InputValues->DestinationGeomPath); + + std::vector closestSrcIds(destGeometry.getNumberOfVertices()); + + // set up thread-safe messenger + m_TotalElements = destGeometry.getNumberOfVertices(); + + // Parallel algorithm to calculate closest vertices + ParallelDataAlgorithm dataAlg; + dataAlg.setRange(0ULL, static_cast(destGeometry.getNumberOfVertices())); + dataAlg.execute(CalculateClosestVerticesImpl(this, srcGeometry, destGeometry, closestSrcIds, m_MessageHandler, m_ShouldCancel)); + m_MessageHandler(IFilter::Message::Type::Info, "Calculating Closest Vertices || 100%"); + + DataPath interpolatedAttrMatrixPath; + if(m_InputValues->UseExistingAttrMatrix) + { + interpolatedAttrMatrixPath = m_InputValues->ExistingAttrMatrixPath; + } + else + { + interpolatedAttrMatrixPath = m_InputValues->DestinationGeomPath.createChildPath(m_InputValues->CreatedAttrMatrixName); + } + + for(usize i = 0; i < m_InputValues->InputDataPaths.size(); i++) + { + const auto& dataPath = m_InputValues->InputDataPaths[i]; + m_MessageHandler(IFilter::Message::Type::Info, fmt::format("Interpolating \"{}\" Array Values || {}/{}", dataPath.getTargetName(), i + 1, m_InputValues->InputDataPaths.size())); + + if(m_ShouldCancel) + { + return {}; + } + + const auto& srcDataArray = m_DataStructure.getDataRefAs(dataPath); + auto& destDataArray = m_DataStructure.getDataRefAs(interpolatedAttrMatrixPath.createChildPath(dataPath.getTargetName())); + + ExecuteDataFunction(ExecuteInterpolation{}, srcDataArray.getDataType(), srcDataArray, destDataArray, closestSrcIds); + } + + return {}; +} + +// ----------------------------------------------------------------------------- +void InterpolateValuesToUnstructuredGrid::sendThreadSafeProgressMessage(usize counter) +{ + std::lock_guard guard(m_ProgressMessage_Mutex); + + m_ProgressCounter += counter; + auto now = std::chrono::steady_clock::now(); + if(std::chrono::duration_cast(now - m_InitialPoint).count() < 1000) + { + return; + } + + auto progressInt = static_cast((static_cast(m_ProgressCounter) / static_cast(m_TotalElements)) * 100.0f); + std::string ss = fmt::format("Calculating Closest Vertices || {}%", progressInt); + m_MessageHandler(IFilter::Message::Type::Info, ss); + + m_LastProgressInt = progressInt; + m_InitialPoint = std::chrono::steady_clock::now(); +} diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/InterpolateValuesToUnstructuredGrid.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/InterpolateValuesToUnstructuredGrid.hpp new file mode 100644 index 0000000000..704006d955 --- /dev/null +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/InterpolateValuesToUnstructuredGrid.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include "ComplexCore/ComplexCore_export.hpp" + +#include "complex/DataStructure/DataPath.hpp" +#include "complex/DataStructure/DataStructure.hpp" +#include "complex/Filter/IFilter.hpp" +#include "complex/Parameters/ArraySelectionParameter.hpp" +#include "complex/Parameters/ChoicesParameter.hpp" +#include "complex/Parameters/DataGroupSelectionParameter.hpp" +#include "complex/Parameters/DynamicTableParameter.hpp" +#include "complex/Parameters/MultiArraySelectionParameter.hpp" +#include "complex/Parameters/NumberParameter.hpp" +#include "complex/Parameters/VectorParameter.hpp" +#include "complex/Utilities/ImageRotationUtilities.hpp" + +#include + +namespace complex +{ + +struct COMPLEXCORE_EXPORT InterpolateValuesToUnstructuredGridInputValues +{ + DataPath SourceGeomPath; + DataPath DestinationGeomPath; + std::vector InputDataPaths; + bool UseExistingAttrMatrix; + DataPath ExistingAttrMatrixPath; + std::string CreatedAttrMatrixName; +}; + +/** + * @class ConditionalSetValue + * @brief This filter replaces values in the target array with a user specified value + * where a bool mask array specifies. + */ + +class COMPLEXCORE_EXPORT InterpolateValuesToUnstructuredGrid +{ +public: + InterpolateValuesToUnstructuredGrid(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, + InterpolateValuesToUnstructuredGridInputValues* inputValues); + ~InterpolateValuesToUnstructuredGrid() noexcept; + + InterpolateValuesToUnstructuredGrid(const InterpolateValuesToUnstructuredGrid&) = delete; + InterpolateValuesToUnstructuredGrid(InterpolateValuesToUnstructuredGrid&&) noexcept = delete; + InterpolateValuesToUnstructuredGrid& operator=(const InterpolateValuesToUnstructuredGrid&) = delete; + InterpolateValuesToUnstructuredGrid& operator=(InterpolateValuesToUnstructuredGrid&&) noexcept = delete; + + Result<> operator()(); + + const std::atomic_bool& getCancel(); + + void sendThreadSafeProgressMessage(usize counter); + +private: + DataStructure& m_DataStructure; + const InterpolateValuesToUnstructuredGridInputValues* m_InputValues = nullptr; + const std::atomic_bool& m_ShouldCancel; + const IFilter::MessageHandler& m_MessageHandler; + + // Thread safe Progress Message + std::chrono::steady_clock::time_point m_InitialPoint = std::chrono::steady_clock::now(); + mutable std::mutex m_ProgressMessage_Mutex; + size_t m_TotalElements = 0; + size_t m_ProgressCounter = 0; + size_t m_LastProgressInt = 0; +}; + +} // namespace complex diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/InterpolateValuesToUnstructuredGridFilter.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/InterpolateValuesToUnstructuredGridFilter.cpp new file mode 100644 index 0000000000..6a7f689314 --- /dev/null +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/InterpolateValuesToUnstructuredGridFilter.cpp @@ -0,0 +1,150 @@ +#include "InterpolateValuesToUnstructuredGridFilter.hpp" + +#include "complex/DataStructure/Geometry/INodeGeometry0D.hpp" +#include "complex/Filter/Actions/CreateArrayAction.hpp" +#include "complex/Filter/Actions/CreateAttributeMatrixAction.hpp" +#include "complex/Parameters/AttributeMatrixSelectionParameter.hpp" +#include "complex/Parameters/BoolParameter.hpp" +#include "complex/Parameters/DataObjectNameParameter.hpp" +#include "complex/Parameters/GeometrySelectionParameter.hpp" +#include "complex/Parameters/MultiArraySelectionParameter.hpp" + +#include "ComplexCore/Filters/Algorithms/InterpolateValuesToUnstructuredGrid.hpp" + +#include + +using namespace complex; + +namespace complex +{ + +//------------------------------------------------------------------------------ +std::string InterpolateValuesToUnstructuredGridFilter::name() const +{ + return FilterTraits::name; +} + +//------------------------------------------------------------------------------ +std::string InterpolateValuesToUnstructuredGridFilter::className() const +{ + return FilterTraits::className; +} + +//------------------------------------------------------------------------------ +Uuid InterpolateValuesToUnstructuredGridFilter::uuid() const +{ + return FilterTraits::uuid; +} + +//------------------------------------------------------------------------------ +std::string InterpolateValuesToUnstructuredGridFilter::humanName() const +{ + return "Interpolate Values To Unstructured Grid"; +} + +//------------------------------------------------------------------------------ +std::vector InterpolateValuesToUnstructuredGridFilter::defaultTags() const +{ + return {className(), "Vertex", "Edge", "Triangle", "Quad", "Tetrahedral", "Hexahedral"}; +} + +//------------------------------------------------------------------------------ +Parameters InterpolateValuesToUnstructuredGridFilter::parameters() const +{ + GeometrySelectionParameter::AllowedTypes geomTypes = {IGeometry::Type::Vertex, IGeometry::Type::Edge, IGeometry::Type::Triangle, + IGeometry::Type::Quad, IGeometry::Type::Tetrahedral, IGeometry::Type::Hexahedral}; + + Parameters params; + + params.insertSeparator(Parameters::Separator{"Required Input Data Objects"}); + params.insert(std::make_unique(k_SourceGeometry_Key, "Node-Based Geometry To Interpolate", "DataPath to node-based geometry to interpolate", DataPath(), geomTypes)); + params.insert(std::make_unique(k_DestinationGeometry_Key, "Interpolated Node-Based Geometry", "DataPath to node-based interpolated geometry", DataPath(), geomTypes)); + + params.insert(std::make_unique(k_InterpolatedArrayPaths_Key, "Attribute Arrays to Interpolate", "DataPaths to interpolate", std::vector(), + MultiArraySelectionParameter::AllowedTypes{IArray::ArrayType::DataArray}, GetAllNumericTypes())); + + params.insertSeparator(Parameters::Separator{"Created Data Objects"}); + params.insertLinkableParameter( + std::make_unique(k_UseExistingAttrMatrix_Key, "Use Existing Attribute Matrix", "Use an existing attribute matrix to store the interpolated arrays.", false)); + params.insert( + std::make_unique(k_ExistingAttrMatrix_Key, "Vertex Attribute Matrix", "Vertex attribute matrix to store the interpolated data", DataPath({"VertexData"}))); + params.insert(std::make_unique(k_CreatedAttrMatrix_Key, "Created Vertex Attribute Matrix", "DataPath to created AttributeMatrix for interpolated data", "VertexData")); + + params.linkParameters(k_UseExistingAttrMatrix_Key, k_CreatedAttrMatrix_Key, false); + params.linkParameters(k_UseExistingAttrMatrix_Key, k_ExistingAttrMatrix_Key, true); + + return params; +} + +//------------------------------------------------------------------------------ +IFilter::UniquePointer InterpolateValuesToUnstructuredGridFilter::clone() const +{ + return std::make_unique(); +} + +//------------------------------------------------------------------------------ +IFilter::PreflightResult InterpolateValuesToUnstructuredGridFilter::preflightImpl(const DataStructure& dataStructure, const Arguments& filterArgs, const MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel) const +{ + auto srcGeomPath = filterArgs.value(k_SourceGeometry_Key); + auto destGeomPath = filterArgs.value(k_DestinationGeometry_Key); + auto inputDataPaths = filterArgs.value>(k_InterpolatedArrayPaths_Key); + auto useExistingAttrMatrix = filterArgs.value(k_UseExistingAttrMatrix_Key); + auto existingAttrMatrixPath = filterArgs.value(k_ExistingAttrMatrix_Key); + auto createdAttrMatrixName = filterArgs.value(k_CreatedAttrMatrix_Key); + + OutputActions actions; + + if(inputDataPaths.empty()) + { + return {MakeErrorResult(-3000, "There are no attribute arrays to interpolate. Please select at least one attribute array to interpolate.")}; + } + + auto tupleValidityCheck = dataStructure.validateNumberOfTuples(inputDataPaths); + if(!tupleValidityCheck) + { + return {MakeErrorResult(-3002, fmt::format("The following DataArrays all must have equal numbers of tuples but this was not satisfied.\n{}", tupleValidityCheck.error()))}; + } + + auto* destGeometry = dataStructure.getDataAs(destGeomPath); + + DataPath interpolatedAttrMatrixPath; + if(useExistingAttrMatrix) + { + interpolatedAttrMatrixPath = existingAttrMatrixPath; + } + else + { + interpolatedAttrMatrixPath = destGeomPath.createChildPath(createdAttrMatrixName); + auto createAttrMatrixAction = std::make_unique(interpolatedAttrMatrixPath, std::vector{destGeometry->getNumberOfVertices()}); + actions.appendAction(std::move(createAttrMatrixAction)); + } + + for(const auto& inputDataPath : inputDataPaths) + { + auto* inputArray = dataStructure.getDataAs(inputDataPath); + DataPath outputDataPath = interpolatedAttrMatrixPath.createChildPath(inputDataPath.getTargetName()); + + auto createArrayAction = std::make_unique(inputArray->getDataType(), std::vector{destGeometry->getNumberOfVertices()}, std::vector{1}, outputDataPath); + actions.appendAction(std::move(createArrayAction)); + } + + return {std::move(actions)}; +} + +//------------------------------------------------------------------------------ +Result<> InterpolateValuesToUnstructuredGridFilter::executeImpl(DataStructure& data, const Arguments& args, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel) const +{ + InterpolateValuesToUnstructuredGridInputValues inputValues; + + inputValues.SourceGeomPath = args.value(k_SourceGeometry_Key); + inputValues.DestinationGeomPath = args.value(k_DestinationGeometry_Key); + inputValues.InputDataPaths = args.value>(k_InterpolatedArrayPaths_Key); + inputValues.UseExistingAttrMatrix = args.value(k_UseExistingAttrMatrix_Key); + inputValues.ExistingAttrMatrixPath = args.value(k_ExistingAttrMatrix_Key); + inputValues.CreatedAttrMatrixName = args.value(k_CreatedAttrMatrix_Key); + + return InterpolateValuesToUnstructuredGrid(data, messageHandler, shouldCancel, &inputValues)(); +} +} // namespace complex diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/InterpolateValuesToUnstructuredGridFilter.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/InterpolateValuesToUnstructuredGridFilter.hpp new file mode 100644 index 0000000000..dd41bd6b27 --- /dev/null +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/InterpolateValuesToUnstructuredGridFilter.hpp @@ -0,0 +1,95 @@ +#pragma once + +#include "ComplexCore/ComplexCore_export.hpp" + +#include "complex/Common/StringLiteral.hpp" +#include "complex/Filter/FilterTraits.hpp" +#include "complex/Filter/IFilter.hpp" + +namespace complex +{ +class COMPLEXCORE_EXPORT InterpolateValuesToUnstructuredGridFilter : public IFilter +{ +public: + InterpolateValuesToUnstructuredGridFilter() = default; + ~InterpolateValuesToUnstructuredGridFilter() noexcept override = default; + + InterpolateValuesToUnstructuredGridFilter(const InterpolateValuesToUnstructuredGridFilter&) = delete; + InterpolateValuesToUnstructuredGridFilter(InterpolateValuesToUnstructuredGridFilter&&) noexcept = delete; + + InterpolateValuesToUnstructuredGridFilter& operator=(const InterpolateValuesToUnstructuredGridFilter&) = delete; + InterpolateValuesToUnstructuredGridFilter& operator=(InterpolateValuesToUnstructuredGridFilter&&) noexcept = delete; + + // Parameter Keys + static inline constexpr StringLiteral k_SourceGeometry_Key = "source_geometry"; + static inline constexpr StringLiteral k_InterpolatedArrayPaths_Key = "interpolated_array_paths"; + static inline constexpr StringLiteral k_UseExistingAttrMatrix_Key = "use_existing_attr_matrix"; + static inline constexpr StringLiteral k_ExistingAttrMatrix_Key = "existing_attr_matrix"; + static inline constexpr StringLiteral k_CreatedAttrMatrix_Key = "created_attr_matrix"; + static inline constexpr StringLiteral k_DestinationGeometry_Key = "destination_geometry"; + + /** + * @brief + * @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 + * @return Uuid + */ + Uuid uuid() const override; + + /** + * @brief + * @return std::string + */ + std::string humanName() const override; + + /** + * @brief Returns the default tags for this filter. + * @return + */ + std::vector defaultTags() const override; + + /** + * @brief + * @return Parameters + */ + Parameters parameters() const override; + + /** + * @brief + * @return UniquePointer + */ + UniquePointer clone() const override; + +protected: + /** + * @brief + * @param data + * @param filterArgs + * @param messageHandler + * @return Result + */ + PreflightResult preflightImpl(const DataStructure& data, const Arguments& args, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel) const override; + + /** + * @brief + * @param dataStructure + * @param args + * @param pipelineNode + * @param messageHandler + * @return Result<> + */ + Result<> executeImpl(DataStructure& data, const Arguments& args, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel) const override; +}; +} // namespace complex + +COMPLEX_DEF_FILTER_TRAITS(complex, InterpolateValuesToUnstructuredGridFilter, "d8477024-7f4a-44eb-ad3f-aed6d07e972b");