diff --git a/src/Plugins/SimplnxCore/CMakeLists.txt b/src/Plugins/SimplnxCore/CMakeLists.txt index 832ab84b75..0e7e4ea6f3 100644 --- a/src/Plugins/SimplnxCore/CMakeLists.txt +++ b/src/Plugins/SimplnxCore/CMakeLists.txt @@ -318,6 +318,7 @@ set(PLUGIN_EXTRA_SOURCES "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/CeilOperator.cpp" "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/AvizoWriter.hpp" "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/AvizoWriter.cpp" + "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/PythonPluginTemplateFile.hpp" "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/SurfaceNets/MMCellFlag.cpp" "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/SurfaceNets/MMCellFlag.h" @@ -348,11 +349,24 @@ source_group(TREE "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}" PREFIX ${PLU # the template file to use the contents of those files. This should allow easier # updating of those template files. file(READ "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/PythonFilterTemplate.py" PYTHON_FILTER_TEMPLATE) +string(HEX ${PYTHON_FILTER_TEMPLATE} PYTHON_FILTER_TEMPLATE) +string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\0," PYTHON_FILTER_TEMPLATE ${PYTHON_FILTER_TEMPLATE}) +string(APPEND PYTHON_FILTER_TEMPLATE "0x00") + + file(READ "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/PythonPluginInitTemplate.py" PYTHON_PLUGIN_INIT_TEMPLATE) +string(HEX ${PYTHON_PLUGIN_INIT_TEMPLATE} PYTHON_PLUGIN_INIT_TEMPLATE) +string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\0," PYTHON_PLUGIN_INIT_TEMPLATE ${PYTHON_PLUGIN_INIT_TEMPLATE}) +string(APPEND PYTHON_PLUGIN_INIT_TEMPLATE "0x00") + file(READ "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/PythonPluginTemplate.py" PYTHON_PLUGIN_TEMPLATE) +string(HEX ${PYTHON_PLUGIN_TEMPLATE} PYTHON_PLUGIN_TEMPLATE) +string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\0," PYTHON_PLUGIN_TEMPLATE ${PYTHON_PLUGIN_TEMPLATE}) +string(APPEND PYTHON_PLUGIN_TEMPLATE "0x00") + -configure_file("${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/PythonPluginTemplateFile.in.hpp" - "${${PLUGIN_NAME}_BINARY_DIR}/utils/PythonPluginTemplateFile.hpp") +cmpConfigureFileWithMD5Check(CONFIGURED_TEMPLATE_PATH "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/PythonPluginSourceTemplate.in.hpp" + GENERATED_FILE_PATH "${${PLUGIN_NAME}_BINARY_DIR}/generated/${PLUGIN_NAME}/utils/PythonPluginSourceTemplate.hpp") #------------------------------------------------------------------------------ # If there are additional include directories that are needed for this plugin diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginSourceTemplate.in.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginSourceTemplate.in.hpp new file mode 100644 index 0000000000..727c4f5ac6 --- /dev/null +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginSourceTemplate.in.hpp @@ -0,0 +1,40 @@ +#pragma once + +/** + * This file is auto-generated during CMake time. Any changes made to the generated file + * will be over written the next time CMake runs a configure. + */ + +#include + +namespace nx::core +{ + +// clang-format off +static const char k_PythonFilterTemplateCharArray[] = {@PYTHON_FILTER_TEMPLATE@}; +// clang-format on + +inline const std::string PythonFilterTemplate() +{ + return {k_PythonFilterTemplateCharArray}; +} + +// clang-format off +static const char k_PluginPythonFileCharArray[] = {@PYTHON_PLUGIN_TEMPLATE@}; +// clang-format on + +inline const std::string PluginPythonFile() +{ + return {k_PluginPythonFileCharArray}; +} + +// clang-format off +static const char k_PluginInitPythonFileCharArray[] = {@PYTHON_PLUGIN_INIT_TEMPLATE@}; +// clang-format on + +inline const std::string PluginInitPythonFile() +{ + return {k_PluginInitPythonFileCharArray}; +} + +}; // namespace nx::core \ No newline at end of file diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginTemplateFile.in.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginTemplateFile.hpp similarity index 88% rename from src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginTemplateFile.in.hpp rename to src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginTemplateFile.hpp index 1c91717eda..271a26ae38 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginTemplateFile.in.hpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginTemplateFile.hpp @@ -5,6 +5,8 @@ #include "simplnx/Utilities/FilterUtilities.hpp" #include "simplnx/Utilities/StringUtilities.hpp" +#include "SimplnxCore/utils/PythonPluginSourceTemplate.hpp" + #include #include @@ -16,9 +18,6 @@ namespace nx::core { inline const std::string k_FilterIncludeInsertToken = "# FILTER_INCLUDE_INSERT"; inline const std::string k_FilterNameInsertToken = "# FILTER_NAME_INSERT"; -inline const std::string k_PythonFilterTemplate = R"( -@PYTHON_FILTER_TEMPLATE@ -)"; /** * @@ -29,7 +28,7 @@ inline const std::string k_PythonFilterTemplate = R"( */ inline std::string GeneratePythonFilter(const std::string& filterName, const std::string& humanName, const std::string& uuidString) { - std::string content = k_PythonFilterTemplate; + std::string content = PythonFilterTemplate(); content = StringUtilities::replace(content, "#PYTHON_FILTER_NAME#", filterName); content = StringUtilities::replace(content, "#PYTHON_FILTER_HUMAN_NAME#", humanName); @@ -47,7 +46,13 @@ inline std::string GeneratePythonFilter(const std::string& filterName, const std inline Result<> InsertFilterNameInPluginFiles(const std::filesystem::path& pluginPath, const std::string& filterName) { Result<> result; - std::string pluginName = pluginPath.stem().string(); + std::string pluginName = pluginPath.string(); + if(StringUtilities::ends_with(pluginName, "/")) + { + pluginName.pop_back(); + std::filesystem::path plugPath(pluginName); + pluginName = plugPath.stem().string(); + } fs::path pluginPyPath = pluginPath / "Plugin.py"; if(!fs::exists(pluginPyPath)) @@ -80,10 +85,13 @@ inline Result<> InsertFilterNameInPluginFiles(const std::filesystem::path& plugi "file. This plugin file must be manually updated to include the filter import statement.", pluginFilePath.string(), k_FilterNameInsertToken)}); } - filterInsertPos--; // Go back one character to insert before the newline + else + { + filterInsertPos--; // Go back one character to insert before the newline - std::string filterImportToken = fmt::format("from {0}.{1} import {1}\n", pluginName, filterName); - content.insert(filterInsertPos, filterImportToken); + std::string filterImportToken = fmt::format("from {0}.{1} import {1}\n", pluginName, filterName); + content.insert(filterInsertPos, filterImportToken); + } } // Insert filter name @@ -96,14 +104,17 @@ inline Result<> InsertFilterNameInPluginFiles(const std::filesystem::path& plugi "file. This plugin file must be manually updated to include the filter name ('{2}').", pluginFilePath.string(), k_FilterNameInsertToken, filterName)}); } - filterInsertPos -= 2; // Go back two characters to insert before the closing brace - - std::string filterNameToken = filterName; - if(pluginFilePath.filename() == "__init__.py") + else { - filterNameToken = fmt::format("'{}'", filterNameToken); + filterInsertPos -= 2; // Go back two characters to insert before the closing brace + + std::string filterNameToken = filterName; + if(pluginFilePath.filename() == "__init__.py") + { + filterNameToken = fmt::format("'{}'", filterNameToken); + } + content.insert(filterInsertPos, "," + filterNameToken); } - content.insert(filterInsertPos, "," + filterNameToken); } std::ofstream out_file(pluginFilePath.string()); @@ -151,17 +162,19 @@ inline Result<> WritePythonFilterToPlugin(const std::filesystem::path& pluginPat */ inline Result<> WritePythonFiltersToPlugin(const std::filesystem::path& pluginPath, const std::string& filterNames) { + Result<> result; auto filterNamesSplit = StringUtilities::split(filterNames, ','); for(const auto& filterName : filterNamesSplit) { - auto result = nx::core::WritePythonFilterToPlugin(pluginPath, filterName); - if(result.invalid()) + auto filterWriteResult = nx::core::WritePythonFilterToPlugin(pluginPath, filterName); + if(filterWriteResult.invalid()) { - return result; + return filterWriteResult; } + result = MergeResults(result, filterWriteResult); } - return {}; + return result; } /** @@ -192,14 +205,6 @@ inline Result<> WritePythonFilterToFile(const std::filesystem::path& outputPath, return {}; } -inline const std::string k_PluginPythonFile = R"( -@PYTHON_PLUGIN_TEMPLATE@ -)"; - -inline const std::string k_PluginInitPythonFile = R"( -@PYTHON_PLUGIN_INIT_TEMPLATE@ -)"; - /** * * @param pluginName @@ -211,7 +216,7 @@ inline const std::string k_PluginInitPythonFile = R"( */ inline std::string GeneratePythonPlugin(const std::string& pluginName, const std::string& pluginShortName, const std::string& pluginDescription, const std::string& pluginFilterList) { - std::string content = k_PluginPythonFile; + std::string content = PluginPythonFile(); content = StringUtilities::replace(content, "#PLUGIN_NAME#", pluginName); content = StringUtilities::replace(content, "#PLUGIN_UUID#", Uuid::GenerateV4().str()); @@ -280,7 +285,7 @@ inline Result<> WritePythonPluginFiles(const std::filesystem::path& outputDirect { return MakeErrorResult(-74100, fmt::format("Error creating and opening output file at path: {}", initTempFile.tempFilePath().string())); } - std::string content = k_PluginInitPythonFile; + std::string content = PluginInitPythonFile(); content = StringUtilities::replace(content, "#PLUGIN_NAME#", pluginName); content = StringUtilities::replace(content, "#PLUGIN_UUID#", Uuid::GenerateV4().str()); diff --git a/wrapping/python/plugins/DataAnalysisToolkit/NPSortArray.py b/wrapping/python/plugins/DataAnalysisToolkit/NPSortArray.py new file mode 100644 index 0000000000..b182a7238f --- /dev/null +++ b/wrapping/python/plugins/DataAnalysisToolkit/NPSortArray.py @@ -0,0 +1,97 @@ + +from typing import List + +import simplnx as nx + +import numpy as np + +class NPSortArray: + """ + This section should contain the 'keys' that store each parameter. The value of the key should be snake_case. The name + of the value should be ALL_CAPITOL_KEY + """ + INPUT_ARRAY_KEY = 'input_array_path' + + def uuid(self) -> nx.Uuid: + """This returns the UUID of the filter. Each filter has a unique UUID value + :return: The Filter's Uuid value + :rtype: string + """ + return nx.Uuid('ae37de34-4ead-4202-9a49-6c222cbd88a8') + + def human_name(self) -> str: + """This returns the name of the filter as a user of DREAM3DNX would see it + :return: The filter's human name + :rtype: string + """ + return 'Sort Data Array (Numpy)' + + def class_name(self) -> str: + """The returns the name of the class that implements the filter + :return: The name of the implementation class + :rtype: string + """ + return 'NPSortArray' + + def name(self) -> str: + """The returns the name of filter + :return: The name of the filter + :rtype: string + """ + return 'NPSortArray' + + def default_tags(self) -> List[str]: + """This returns the default tags for this filter + :return: The default tags for the filter + :rtype: list + """ + return ['python'] + + def clone(self): + """Clones the filter + :return: A new instance of the filter + :rtype: NPSortArray + """ + return NPSortArray() + + def parameters(self) -> nx.Parameters: + """This function defines the parameters that are needed by the filter. Parameters collect the values from the user + or through a pipeline file. + """ + params = nx.Parameters() + + params.insert(nx.ArraySelectionParameter(NPSortArray.INPUT_ARRAY_KEY, 'Input Data Array', 'The input array to sort.', nx.DataPath(), nx.get_all_data_types())) + + return params + + def preflight_impl(self, data_structure: nx.DataStructure, args: dict, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy) -> nx.IFilter.PreflightResult: + """This method preflights the filter and should ensure that all inputs are sanity checked as best as possible. Array + sizes can be checked if the arrays are actually know at preflight time. Some filters will not be able to report output + array sizes during preflight (segmentation filters for example). + :returns: + :rtype: nx.IFilter.PreflightResult + """ + +# message_handler(nx.IFilter.Message(nx.IFilter.Message.Type.Info, f'Preflight: {value}')) + return nx.IFilter.PreflightResult() + + def execute_impl(self, data_structure: nx.DataStructure, args: dict, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy) -> nx.IFilter.ExecuteResult: + """ This method actually executes the filter algorithm and reports results. + :returns: + :rtype: nx.IFilter.ExecuteResult + """ + # Extract the DataPath from the arguments + input_data_arr_path: nx.DataPath = args[NPSortArray.INPUT_ARRAY_KEY] + # Use the DataPath to get a numpy view of the DataArray at that DataPath + input_array_view = data_structure[input_data_arr_path].npview() + + # Treat the view like a single dimensional array + # The entire array is sorted from first element to last element. + input_array_view = input_array_view.reshape(-1) + + # Sort the actual Data Array in place + input_array_view.sort() + + return nx.Result() + + diff --git a/wrapping/python/plugins/DataAnalysisToolkit/Plugin.py b/wrapping/python/plugins/DataAnalysisToolkit/Plugin.py index 97d6d154fd..39884ddae5 100644 --- a/wrapping/python/plugins/DataAnalysisToolkit/Plugin.py +++ b/wrapping/python/plugins/DataAnalysisToolkit/Plugin.py @@ -1,7 +1,16 @@ + +""" +Insert documentation here. +""" + from DataAnalysisToolkit.CalculateHistogramFilter import CalculateHistogramFilter from DataAnalysisToolkit.InterpolateGridDataFilter import InterpolateGridDataFilter from DataAnalysisToolkit.CliReaderFilter import CliReaderFilter from DataAnalysisToolkit.ContourDetectionFilter import ContourDetectionFilter +from DataAnalysisToolkit.NPSortArray import NPSortArray + +# FILTER_INCLUDE_INSERT + import simplnx as nx class DataAnalysisToolkit: @@ -21,4 +30,6 @@ def vendor(self) -> str: return 'BlueQuartz Software' def get_filters(self): - return [CalculateHistogramFilter, InterpolateGridDataFilter, CliReaderFilter, ContourDetectionFilter] + return [CalculateHistogramFilter,InterpolateGridDataFilter,CliReaderFilter,NPSortArray,ContourDetectionFilter] # FILTER_NAME_INSERT + + diff --git a/wrapping/python/plugins/DataAnalysisToolkit/__init__.py b/wrapping/python/plugins/DataAnalysisToolkit/__init__.py index b9221de562..596c9a2693 100644 --- a/wrapping/python/plugins/DataAnalysisToolkit/__init__.py +++ b/wrapping/python/plugins/DataAnalysisToolkit/__init__.py @@ -1,10 +1,17 @@ + from DataAnalysisToolkit.Plugin import DataAnalysisToolkit + from DataAnalysisToolkit.CalculateHistogramFilter import CalculateHistogramFilter from DataAnalysisToolkit.InterpolateGridDataFilter import InterpolateGridDataFilter from DataAnalysisToolkit.CliReaderFilter import CliReaderFilter from DataAnalysisToolkit.ContourDetectionFilter import ContourDetectionFilter +from DataAnalysisToolkit.NPSortArray import NPSortArray + +# FILTER_INCLUDE_INSERT def get_plugin(): return DataAnalysisToolkit() -__all__ = ['DataAnalysisToolkit', 'CalculateHistogramFilter', 'InterpolateGridDataFilter', 'CliReaderFilter', 'ContourDetectionFilter', 'get_plugin'] +__all__ = ['DataAnalysisToolkit','CalculateHistogramFilter', 'InterpolateGridDataFilter', 'CliReaderFilter', 'get_plugin', 'ContourDetectionFilter', 'NPSortArray'] # FILTER_NAME_INSERT + +