diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 469e4b0..eee0103 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,21 +4,25 @@ on: push: pull_request: workflow_dispatch: - inputs: - forceMacBuild: - description: 'If set true override the ENABLE_MACOS_BUILD to force a build' - type: boolean - default: false env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE: Release - MACENABLED: ${{ inputs.forceMacBuild }} # for matrix check https://docs.github.com/en/actions/reference/specifications-for-github-hosted-runners jobs: + prepare_matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.matrix_setup.outputs.matrix }} + steps: + - name: Get matrix from file + id: matrix_setup + uses: ManiVaultStudio/github-actions/matrix_setup@main + cross-platform-build: name: Cross platform build + needs: prepare_matrix # The CMake configure and build commands are platform agnostic and should work equally # well on Windows or Mac. You can convert this to a matrix build if you need # cross-platform coverage. @@ -26,44 +30,19 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - include: - - name: Windows-msvc2019 - os: windows-2019 - compiler: msvc-2019 - build-cversion: 16 - build-runtime: MD - build-config: Release - - - name: Linux_gcc11 - os: ubuntu-22.04 - build-cc: gcc - build-cxx: g++ - build-compiler: gcc - build-cversion: 11 - build-config: Release - build-os: Linux - build-libcxx: libstdc++ - - - name: Macos_xcode13.4 - os: macos-12 - build-compiler: apple-clang - build-cversion: 13 - build-config: Release - build-os: Macos - build-xcode-version: 13.4 - build-libcxx: libc++ + include: ${{ fromJson(needs.prepare_matrix.outputs.matrix) }} steps: - name: Checkout the source if: github.event_name != 'pull_request' - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - name: Checkout the source - pull request if: github.event_name == 'pull_request' - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 @@ -75,12 +54,12 @@ jobs: sudo xcode-select -switch /Applications/Xcode_${{matrix.build-xcode-version}}.app - name: Setup python version - uses: actions/setup-python@v1 + uses: actions/setup-python@v5 with: python-version: "3.11" - name: Start ssh key agent - uses: webfactory/ssh-agent@v0.7.0 + uses: webfactory/ssh-agent@v0.9.0 with: ssh-private-key: ${{ secrets.RULESSUPPORT_DEPLOY_KEY }} @@ -111,6 +90,7 @@ jobs: conan-libcxx-version: ${{matrix.build-libcxx}} conan-build-type: ${{matrix.build-config}} conan-build-os: ${{matrix.build-os}} + build-arch: ${{matrix.build-arch}} conan-user: ${{secrets.LKEB_UPLOAD_USER}} conan-password: ${{secrets.LKEB_UPLOAD_USER_PASSWORD}} conan-pem: ${{secrets.LKEB_UPLOAD_CERT_CHAIN}} @@ -118,7 +98,7 @@ jobs: conan-cxx: g++-${{matrix.build-cversion}} - name: Mac build - if: startsWith(matrix.os, 'macos') && (env.MACENABLED == 'true' || vars.ENABLE_MACOS_BUILD == 'True') + if: startsWith(matrix.os, 'macos') uses: ManiVaultStudio/github-actions/conan_linuxmac_build@main with: conan-compiler: ${{matrix.build-compiler}} @@ -126,6 +106,7 @@ jobs: conan-libcxx-version: ${{matrix.build-libcxx}} conan-build-type: ${{matrix.build-config}} conan-build-os: ${{matrix.build-os}} + build-arch: ${{matrix.build-arch}} conan-user: ${{secrets.LKEB_UPLOAD_USER}} conan-password: ${{secrets.LKEB_UPLOAD_USER_PASSWORD}} conan-pem: ${{secrets.LKEB_UPLOAD_CERT_CHAIN}} diff --git a/.gitignore b/.gitignore index 6de35bc..f572591 100644 --- a/.gitignore +++ b/.gitignore @@ -534,3 +534,4 @@ FodyWeavers.xsd Build/ .DS_Store +*.bak diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f62b58..e24a34a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,30 +1,40 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.22) -set(SVPLUGIN "SpectralViewPlugin") +option(MV_UNITY_BUILD "Combine target source files into batches for faster compilation" OFF) -PROJECT(${SVPLUGIN}) +# ----------------------------------------------------------------------------- +# SpectralView Plugin +# ----------------------------------------------------------------------------- +set(SVPLUGIN "SpectralViewPlugin") +PROJECT(${SVPLUGIN} + DESCRIPTION "View plugin for ManiVault for spectral data" + LANGUAGES CXX +) +# ----------------------------------------------------------------------------- +# CMake Options +# ----------------------------------------------------------------------------- set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) -set(CMAKE_AUTOUIC ON) - -if(MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /DWIN32 /EHsc /MP /permissive- /Zc:__cplusplus /W3") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LTCG /NODEFAULTLIB:LIBCMT") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD") -endif(MSVC) - -# Check if the directory to the ManiVault installation has been provided -if(NOT DEFINED MV_INSTALL_DIR) - set(MV_INSTALL_DIR "" CACHE PATH "Directory where ManiVault is installed") - message(FATAL_ERROR "Please set MV_INSTALL_DIR to the directory where ManiVault is installed") + +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /DWIN32 /EHsc /MP /permissive- /Zc:__cplusplus /W3") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /MD") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD") endif() -file(TO_CMAKE_PATH ${MV_INSTALL_DIR} MV_INSTALL_DIR) -find_package(Qt6 6.3.1 COMPONENTS Widgets WebEngineWidgets Concurrent REQUIRED) +# ----------------------------------------------------------------------------- +# Dependencies +# ----------------------------------------------------------------------------- +find_package(Qt6 COMPONENTS Widgets WebEngineWidgets Concurrent REQUIRED) + +find_package(ManiVault COMPONENTS Core PointData ClusterData ImageData CONFIG QUIET) +# ----------------------------------------------------------------------------- +# Source files +# ----------------------------------------------------------------------------- set(PLUGIN src/SpectralViewPlugin.h src/SpectralViewPlugin.cpp @@ -34,10 +44,6 @@ set(PLUGIN src/Endmember.h ) -set(PLUGIN_MOC_HEADERS - src/SpectralViewPlugin.h -) - set(ACTIONS src/SettingsAction.h src/SettingsAction.cpp @@ -82,7 +88,7 @@ set(WEB set(AUX res/lineplot_resources.qrc - src/SpectralViewPlugin.json + PluginInfo.json ) qt6_add_resources(RESOURCE_FILES res/lineplot_resources.qrc) @@ -95,34 +101,40 @@ source_group(Widget FILES ${WIDGETS}) source_group(Web FILES ${WEB}) source_group(Aux FILES ${AUX}) +# ----------------------------------------------------------------------------- +# CMake Target +# ----------------------------------------------------------------------------- add_library(${SVPLUGIN} SHARED ${SOURCES} ${WEB} ${AUX} ${RESOURCE_FILES}) -qt_wrap_cpp(SV_PARAMETERS_MOC ${PLUGIN_MOC_HEADERS} TARGET ${SVPLUGIN}) -target_sources(${SVPLUGIN} PRIVATE ${SV_PARAMETERS_MOC}) +# ----------------------------------------------------------------------------- +# Target include directories +# ----------------------------------------------------------------------------- +target_include_directories(${SVPLUGIN} PRIVATE "${ManiVault_INCLUDE_DIR}") -target_include_directories(${SVPLUGIN} PRIVATE "${MV_INSTALL_DIR}/$/include/") +# ----------------------------------------------------------------------------- +# Target properties +# ----------------------------------------------------------------------------- +target_compile_features(${SVPLUGIN} PRIVATE cxx_std_20) -# Request C++17 -target_compile_features(${SVPLUGIN} PRIVATE cxx_std_17) +if(MV_UNITY_BUILD) + set_target_properties(${SVPLUGIN} PROPERTIES UNITY_BUILD ON) +endif() +# ----------------------------------------------------------------------------- +# Target library linking +# ----------------------------------------------------------------------------- target_link_libraries(${SVPLUGIN} PRIVATE Qt6::Widgets) target_link_libraries(${SVPLUGIN} PRIVATE Qt6::WebEngineWidgets) target_link_libraries(${SVPLUGIN} PRIVATE Qt6::Concurrent) -set(MV_LINK_PATH "${MV_INSTALL_DIR}/$/lib") -set(PLUGIN_LINK_PATH "${MV_INSTALL_DIR}/$/$,lib,Plugins>") -set(MV_LINK_SUFFIX $,${CMAKE_LINK_LIBRARY_SUFFIX},${CMAKE_SHARED_LIBRARY_SUFFIX}>) - -set(MV_LINK_LIBRARY "${MV_LINK_PATH}/${CMAKE_SHARED_LIBRARY_PREFIX}MV_Public${MV_LINK_SUFFIX}") -set(POINTDATA_LINK_LIBRARY "${PLUGIN_LINK_PATH}/${CMAKE_SHARED_LIBRARY_PREFIX}PointData${MV_LINK_SUFFIX}") -set(IMAGEDATA_LINK_LIBRARY "${PLUGIN_LINK_PATH}/${CMAKE_SHARED_LIBRARY_PREFIX}ImageData${MV_LINK_SUFFIX}") -set(CLUSTERDATA_LINK_LIBRARY "${PLUGIN_LINK_PATH}/${CMAKE_SHARED_LIBRARY_PREFIX}ClusterData${MV_LINK_SUFFIX}") - -target_link_libraries(${SVPLUGIN} PRIVATE "${MV_LINK_LIBRARY}") -target_link_libraries(${SVPLUGIN} PRIVATE "${POINTDATA_LINK_LIBRARY}") -target_link_libraries(${SVPLUGIN} PRIVATE "${IMAGEDATA_LINK_LIBRARY}") -target_link_libraries(${SVPLUGIN} PRIVATE "${CLUSTERDATA_LINK_LIBRARY}") +target_link_libraries(${SVPLUGIN} PRIVATE ManiVault::Core) +target_link_libraries(${SVPLUGIN} PRIVATE ManiVault::PointData) +target_link_libraries(${SVPLUGIN} PRIVATE ManiVault::ClusterData) +target_link_libraries(${SVPLUGIN} PRIVATE ManiVault::ImageData) +# ----------------------------------------------------------------------------- +# Target installation +# ----------------------------------------------------------------------------- install(TARGETS ${SVPLUGIN} RUNTIME DESTINATION Plugins COMPONENT PLUGINS # Windows .dll LIBRARY DESTINATION Plugins COMPONENT PLUGINS # Linux/Mac .so @@ -132,11 +144,16 @@ add_custom_command(TARGET ${SVPLUGIN} POST_BUILD COMMAND "${CMAKE_COMMAND}" --install ${CMAKE_CURRENT_BINARY_DIR} --config $ - --prefix ${MV_INSTALL_DIR}/$ + --prefix ${ManiVault_INSTALL_DIR}/$ ) +mv_handle_plugin_config(${SVPLUGIN}) + +# ----------------------------------------------------------------------------- +# Miscellaneous +# ----------------------------------------------------------------------------- # Automatically set the debug environment (command + working directory) for MSVC in debug mode -if(MSVC) - set_property(TARGET ${SVPLUGIN} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY $,${MV_INSTALL_DIR}/debug,${MV_INSTALL_DIR}/release>) - set_property(TARGET ${SVPLUGIN} PROPERTY VS_DEBUGGER_COMMAND $,"${MV_INSTALL_DIR}/debug/ManiVault Studio.exe","${MV_INSTALL_DIR}/release/ManiVault Studio.exe">) +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set_property(TARGET ${SVPLUGIN} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY $,${ManiVault_INSTALL_DIR}/Debug,$,${ManiVault_INSTALL_DIR}/RelWithDebInfo,${ManiVault_INSTALL_DIR}/Release>>) + set_property(TARGET ${SVPLUGIN} PROPERTY VS_DEBUGGER_COMMAND $,"${ManiVault_INSTALL_DIR}/Debug/ManiVault Studio.exe",$,"${ManiVault_INSTALL_DIR}/RelWithDebInfo/ManiVault Studio.exe","${ManiVault_INSTALL_DIR}/Release/ManiVault Studio.exe">>) endif() diff --git a/PluginInfo.json b/PluginInfo.json new file mode 100644 index 0000000..e7f7e64 --- /dev/null +++ b/PluginInfo.json @@ -0,0 +1,9 @@ +{ + "name" : "Spectral View", + "version" : { + "plugin" : "1.0.0", + "core" : ["1.3"] + }, + "type" : "View", + "dependencies" : ["Points", "Cluster", "Images"] +} \ No newline at end of file diff --git a/Readme.md b/Readme.md index 81c48b3..70cc73d 100644 --- a/Readme.md +++ b/Readme.md @@ -13,10 +13,14 @@ git clone git@github.com:ManiVaultStudio/SpectralViewPlugin.git ## Usage How to use: -0. Load a data set, e.g. with the [ENVI](https://github.com/ManiVaultStudio/ENVILoader) or [general image](https://github.com/ManiVaultStudio/ImageLoaderPlugin) loader plugins. **Caveat**: It's encouraged to define dimension names that encode the wavelength of the respective image channel, e.g. "304.7", "1020" or "304.7 nm". This wavelength information will be displayed in the viewer. If no dimension names are given when loading the data, ManiVault automatically numbers the dimensions, i.e. "Dim 0", etc.; in this case, the dimension numbers are displayed on the x-Axis instead of wavelengths. +0. Load a data set, e.g. with the [ENVI](https://github.com/ManiVaultStudio/ENVILoader) or [general image](https://github.com/ManiVaultStudio/ImageLoaderPlugin) loader plugins. 1. Open a spectral viewer and drag & drop the point data into the viewer. At this point you will not see anything yet. Open data in another viewer, e.g. an image viewer, and make a selection. Now you'll see the average spectral values for the selected points. 2. Create cluster for your data set, e.g. using an analysis like [mean shift clustering](https://github.com/ManiVaultStudio/MeanShiftClustering) or through manual annotation in the [scatterplot](https://github.com/ManiVaultStudio/Scatterplot). Drag & drop the cluster data into the main view of the spectral viewer. 3. Perform a mapping like the "Spectral Angle Mapping" algorithm for a selected cluster. This will cerate a new image data set. +> **Caveats**: +> - This viewer only handles point data sets that have a image data set as a child. +> - It's encouraged to define dimension names that encode the wavelength of the respective image channel, e.g. "304.7", "1020" or "304.7 nm". This wavelength information will be displayed in the viewer. If no dimension names are given when loading the data, ManiVault automatically numbers the dimensions, i.e. "Dim 0", etc.; in this case, the dimension numbers are displayed on the x-Axis instead of wavelengths. +> - This viewer expects positive data values (as is common in spectral intensities); negative values might lead to unwanted behaviour. ## References Based on Popa et al. "Visual Analysis of RIS Data for Endmember Selection" (2022): diff --git a/conanfile.py b/conanfile.py index 048f98f..f3b019e 100644 --- a/conanfile.py +++ b/conanfile.py @@ -10,16 +10,16 @@ class SpectralViewPluginConan(ConanFile): """Class to package SpectralViewPlugin using conan - Packages both RELEASE and DEBUG. - Uses rules_support (github.com/hdps/rulessupport) to derive + Packages both RELEASE and RELWITHDEBINFO. + Uses rules_support (github.com/ManiVaultStudio/rulessupport) to derive versioninfo based on the branch naming convention - as described in https://github.com/hdps/core/wiki/Branch-naming-rules + as described in https://github.com/ManiVaultStudio/core/wiki/Branch-naming-rules """ name = "SpectralViewPlugin" description = "Viewer for viewing spectral data" topics = ("hdps", "plugin", "image data", "loading") - url = "https://github.com/hdps/SpectralViewPlugin" + url = "https://github.com/ManiVaultStudio/SpectralViewPlugin" author = "B. van Lew b.van_lew@lumc.nl" # conan recipe author license = "MIT" @@ -83,25 +83,26 @@ def generate(self): generator = "Xcode" if self.settings.os == "Linux": generator = "Ninja Multi-Config" - # Use the Qt provided .cmake files - qtpath = pathlib.Path(self.deps_cpp_info["qt"].rootpath) - qt_root = str(list(qtpath.glob("**/Qt6Config.cmake"))[0].parents[3].as_posix()) tc = CMakeToolchain(self, generator=generator) - if self.settings.os == "Windows" and self.options.shared: - tc.variables["CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS"] = True - if self.settings.os == "Linux" or self.settings.os == "Macos": - tc.variables["CMAKE_CXX_STANDARD_REQUIRED"] = "ON" - tc.variables["CMAKE_PREFIX_PATH"] = qt_root - - # Set the installation directory for ManiVault based on the MV_INSTALL_DIR environment variable - # or if none is specified, set it to the build/install dir. - if not os.environ.get("MV_INSTALL_DIR", None): - os.environ["MV_INSTALL_DIR"] = os.path.join(self.build_folder, "install") - print("MV_INSTALL_DIR: ", os.environ["MV_INSTALL_DIR"]) - self.install_dir = pathlib.Path(os.environ["MV_INSTALL_DIR"]).as_posix() - # Give the installation directory to CMake - tc.variables["MV_INSTALL_DIR"] = self.install_dir + + tc.variables["CMAKE_CXX_STANDARD_REQUIRED"] = "ON" + + # Use the Qt provided .cmake files + qt_path = pathlib.Path(self.deps_cpp_info["qt"].rootpath) + qt_cfg = list(qt_path.glob("**/Qt6Config.cmake"))[0] + qt_dir = qt_cfg.parents[0].as_posix() + + tc.variables["Qt6_DIR"] = qt_dir + + # Use the ManiVault .cmake file to find ManiVault with find_package + mv_core_root = self.deps_cpp_info["hdps-core"].rootpath + manivault_dir = pathlib.Path(mv_core_root, "cmake", "mv").as_posix() + print("ManiVault_DIR: ", manivault_dir) + tc.variables["ManiVault_DIR"] = manivault_dir + + # Set some build options + tc.variables["MV_UNITY_BUILD"] = "ON" tc.generate() @@ -112,23 +113,16 @@ def _configure_cmake(self): return cmake def build(self): - print("Build OS is : ", self.settings.os) - - # The SpectralViewPlugin plugins expect the HDPS package to be in this install dir - hdps_pkg_root = self.deps_cpp_info["hdps-core"].rootpath - print("Install dir type: ", self.install_dir) - shutil.copytree(hdps_pkg_root, self.install_dir) + print("Build OS is: ", self.settings.os) cmake = self._configure_cmake() - cmake.build(build_type="Debug") - cmake.install(build_type="Debug") - - # cmake_release = self._configure_cmake() + cmake.build(build_type="RelWithDebInfo") cmake.build(build_type="Release") - cmake.install(build_type="Release") def package(self): - package_dir = os.path.join(self.build_folder, "package") + package_dir = pathlib.Path(self.build_folder, "package") + relWithDebInfo_dir = package_dir / "RelWithDebInfo" + release_dir = package_dir / "Release" print("Packaging install dir: ", package_dir) subprocess.run( [ @@ -136,9 +130,9 @@ def package(self): "--install", self.build_folder, "--config", - "Debug", + "RelWithDebInfo", "--prefix", - os.path.join(package_dir, "Debug"), + relWithDebInfo_dir, ] ) subprocess.run( @@ -149,19 +143,15 @@ def package(self): "--config", "Release", "--prefix", - os.path.join(package_dir, "Release"), + release_dir, ] ) self.copy(pattern="*", src=package_dir) - # Add the debug support files to the package - # (*.pdb) if building the Visual Studio version - if self.settings.compiler == "Visual Studio": - self.copy("*.pdb", dst="Debug/Plugins", keep_path=False) def package_info(self): - self.cpp_info.debug.libdirs = ["Debug/lib"] - self.cpp_info.debug.bindirs = ["Debug/Plugins", "Debug"] - self.cpp_info.debug.includedirs = ["Debug/include", "Debug"] + self.cpp_info.relwithdebinfo.libdirs = ["RelWithDebInfo/lib"] + self.cpp_info.relwithdebinfo.bindirs = ["RelWithDebInfo/Plugins", "RelWithDebInfo"] + self.cpp_info.relwithdebinfo.includedirs = ["RelWithDebInfo/include", "RelWithDebInfo"] self.cpp_info.release.libdirs = ["Release/lib"] self.cpp_info.release.bindirs = ["Release/Plugins", "Release"] - self.cpp_info.release.includedirs = ["Release/include", "Release"] + self.cpp_info.release.includedirs = ["Release/include", "Release"] \ No newline at end of file diff --git a/src/Endmember.cpp b/src/Endmember.cpp index 744da30..b936eab 100644 --- a/src/Endmember.cpp +++ b/src/Endmember.cpp @@ -9,7 +9,7 @@ #include #include -Endmember::Endmember(SpectralViewPlugin& spectralViewPlugin, const Dataset& dataset, int index) : +Endmember::Endmember(SpectralViewPlugin& spectralViewPlugin, const mv::Dataset& dataset, int index) : WidgetAction(&spectralViewPlugin, "Endmember"), _spectralViewPlugin(spectralViewPlugin), _active(false), @@ -30,7 +30,7 @@ Endmember::Endmember(SpectralViewPlugin& spectralViewPlugin, const Dataset::guiNameChanged, this, [this]() { + connect(&_dataset, &mv::Dataset::guiNameChanged, this, [this]() { _generalAction.getDatasetNameAction().setString(_dataset->getGuiName()); //_generalAction.getNameAction().setDefaultString(newGuiName); }); diff --git a/src/EndmembersAction.cpp b/src/EndmembersAction.cpp index 8b48e13..a04b83d 100644 --- a/src/EndmembersAction.cpp +++ b/src/EndmembersAction.cpp @@ -13,6 +13,7 @@ using namespace mv; using namespace mv::gui; +using namespace mv::util; EndmembersAction::EndmembersAction(SettingsAction& settingsAction) : WidgetAction(reinterpret_cast(&settingsAction), "EndmembersAction"), @@ -44,10 +45,8 @@ EndmembersAction::Widget::Widget(QWidget* parent, EndmembersAction* endmembersAc _removeEndmemberAction.setToolTip("Remove the selected endmember"); _saveEndmembersAction.setToolTip("Save the checked endmembers in the list"); - auto& fontAwesome = Application::getIconFont("FontAwesome"); - - _removeEndmemberAction.setIcon(fontAwesome.getIcon("trash-alt")); - _saveEndmembersAction.setIcon(fontAwesome.getIcon("save")); + _removeEndmemberAction.setIcon(StyledIcon("trash-alt")); + _saveEndmembersAction.setIcon(StyledIcon("save")); auto layout = new QVBoxLayout(); auto treeView = new QTreeView(); diff --git a/src/LineplotWidget.cpp b/src/LineplotWidget.cpp index f715522..0f17909 100644 --- a/src/LineplotWidget.cpp +++ b/src/LineplotWidget.cpp @@ -11,6 +11,8 @@ #include #include +using namespace mv::util; + LinePlotCommunicationObject::LinePlotCommunicationObject(LineplotWidget* parent) : mv::gui::WebCommunicationObject(), _parent(parent) @@ -24,9 +26,7 @@ void LinePlotCommunicationObject::js_setRGBWavelength(float wavelength, int inde } LineplotWidget::LineplotWidget() : - mv::gui::WebWidget(), _communicationObject(new LinePlotCommunicationObject(this)), - dataOptionBuffer(), loaded(false) { Q_INIT_RESOURCE(lineplot_resources); @@ -36,7 +36,7 @@ LineplotWidget::LineplotWidget() : setAcceptDrops(true); setMouseTracking(true); - setWindowIcon(mv::Application::getIconFont("FontAwesome").getIcon("chart-line")); + setWindowIcon(StyledIcon("chart-line")); } LineplotWidget::~LineplotWidget() { @@ -60,7 +60,7 @@ void LineplotWidget::setData(const std::vector& yVals, const std::vector< // check if dimension name contains a) only numbers or "." b) numbers and trailing units c) text // if a) use the number b) remove the unit c) replace names with numeric dimension count - // https://godbolt.org/z/E4b1GbM19 + // https://godbolt.org/z/8Yeej4cj6 auto determineNumberAndExtract = [](const std::string & input) -> std::pair { std::regex pattern(R"(^\D*?\s*?(\d+(\.\d+)?)\D*?$)"); std::smatch match; @@ -71,6 +71,21 @@ void LineplotWidget::setData(const std::vector& yVals, const std::vector< return std::make_pair(false, ""); }; + // add a decimal point if there is none + auto unsureFloatString = [](std::string& input) -> void { + // Regular expressions to match float numbers + std::regex decimalRegex("([0-9]+)\\.([0-9]+)"); + std::smatch match; + + // append missing decimal point + if (!std::regex_match(input, match, decimalRegex)) + input.append(".0"); + + // remove leading 0s + if (!(input.size() > 2 && input[0] == '0' && input[1] == '.' && isdigit(input[2]))) + input.erase(0, input.find_first_not_of('0')); + }; + bool replaceAllNames = false; for (const auto& dimName : dimNames) { @@ -82,14 +97,17 @@ void LineplotWidget::setData(const std::vector& yVals, const std::vector< break; } - numericDimNames.push_back(QString::fromStdString(res.second)); + std::string extractedDimName = res.second; + unsureFloatString(extractedDimName); + + numericDimNames.push_back(QString::fromStdString(extractedDimName)); } if (replaceAllNames) { numericDimNames.resize(numDimensions); for (size_t i = 0; i < numericDimNames.size(); i++) - numericDimNames[i] = QString::number(i); + numericDimNames[i] = QString::number(i) + ".0"; } // create json string that will be passed to js diff --git a/src/MapAction.cpp b/src/MapAction.cpp index 0e98581..c80b04e 100644 --- a/src/MapAction.cpp +++ b/src/MapAction.cpp @@ -34,9 +34,8 @@ MapAction::MapAction(Endmember& endmember) : _thresholdAction.setToolTip("Map pixels with value greater than threshold"); _updateAutoAction.setToolTip("Perform selected algorithm automatically"); _computeAction.setToolTip("Update map for the selected endmember"); - - auto& fontAwesome = Application::getIconFont("FontAwesome"); - _computeAction.setIcon(fontAwesome.getIcon("play")); + + _computeAction.setIconByName("play"); //_thresholdAction.setDefaultValue(0.15f); //_thresholdAction.defaultValueChanged(0.15f); diff --git a/src/SpectralViewPlugin.cpp b/src/SpectralViewPlugin.cpp index 2b48c72..be8670c 100644 --- a/src/SpectralViewPlugin.cpp +++ b/src/SpectralViewPlugin.cpp @@ -123,133 +123,140 @@ void SpectralViewPlugin::init() if (!dataTypes.contains(dataType)) { dropRegions << new DropWidget::DropRegion(this, "Incompatible data", "This type of data is not supported", "exclamation-circle", false); } + else + { + // Get points dataset from the core + auto candidateDataset = mv::data().getDataset(datasetId); - // Get points dataset from the core - auto candidateDataset = mv::data().getDataset(datasetId); + // Points dataset is about to be dropped + if (dataType == PointType) { - // Points dataset is about to be dropped - if (dataType == PointType) { + if (dataset->getChildren({ ImageType }).size() < 1) { + dropRegions << new DropWidget::DropRegion(this, "Incompatible data", "Data must have an image set as it's first child", "exclamation-circle", false); + return dropRegions; + } - // Establish drop region description - const auto description = QString("Visualize %1 as line plot").arg(datasetGuiName); + // Establish drop region description + const auto description = QString("Visualize %1 as line plot").arg(datasetGuiName); - try { + try { - if (!_points.isValid()) { - - // Load as point positions when no dataset is currently loaded - dropRegions << new DropWidget::DropRegion(this, "Point position", description, "map-marker-alt", true, [this, candidateDataset]() { - loadData({ candidateDataset }); - }); - } - else { - if (_points == candidateDataset) { - // already loaded - dropRegions << new DropWidget::DropRegion(this, "Warning", "Data already loaded", "exclamation-circle", false); + if (!_points.isValid()) { + + // Load as point positions when no dataset is currently loaded + dropRegions << new DropWidget::DropRegion(this, "Point position", description, "map-marker-alt", true, [this, candidateDataset]() { + loadData({ candidateDataset }); + }); } else { - // Establish drop region description - const auto description1 = QString("Visualize every point in %1 as one line").arg(datasetGuiName); - const auto description2 = QString("Visaualize the points in %1 as an average line").arg(datasetGuiName); + if (_points == candidateDataset) { + // already loaded + dropRegions << new DropWidget::DropRegion(this, "Warning", "Data already loaded", "exclamation-circle", false); + } + else { + // Establish drop region description + const auto description1 = QString("Visualize every point in %1 as one line").arg(datasetGuiName); + const auto description2 = QString("Visaualize the points in %1 as an average line").arg(datasetGuiName); - if (!candidateDataset->isFull() || candidateDataset->isDerivedData()) { + if (!candidateDataset->isFull() || candidateDataset->isDerivedData()) { - auto parent = candidateDataset->getDataHierarchyItem().getParent(); - auto points = parent->getDataset().get(); + auto parent = candidateDataset->getDataHierarchyItem().getParent(); + auto points = parent->getDataset().get(); - QString pointName = _points->getGuiName(); + QString pointName = _points->getGuiName(); - if (!_points->isFull() || _points->isDerivedData()) { - DataHierarchyItem* pointParents = _points->getDataHierarchyItem().getParent();; - pointName = pointParents->getDatasetReference()->getGuiName(); - } + if (!_points->isFull() || _points->isDerivedData()) { + DataHierarchyItem* pointParents = _points->getDataHierarchyItem().getParent();; + pointName = pointParents->getDatasetReference()->getGuiName(); + } - // Load as point positions when no dataset is currently loaded - dropRegions << new DropWidget::DropRegion(this, "Point position", description, "map-marker-alt", true, [this, candidateDataset]() { - _points = candidateDataset.get(); + // Load as point positions when no dataset is currently loaded + dropRegions << new DropWidget::DropRegion(this, "Point position", description, "map-marker-alt", true, [this, candidateDataset]() { + _points = candidateDataset.get(); - initializeImageRGB(); - _mainToolbarAction.setEnabled(true); - _model.removeAllEndmembers(); - }); + initializeImageRGB(); + _mainToolbarAction.setEnabled(true); + _model.removeAllEndmembers(); + }); - if (points->getNumPoints() == _points->getNumPoints() && candidateDataset->getParent()->getGuiName() == pointName) { + if (points->getNumPoints() == _points->getNumPoints() && candidateDataset->getParent()->getGuiName() == pointName) { - dropRegions << new DropWidget::DropRegion(this, "Endmembers", description1, "map-marker-alt", true, [this, candidateDataset]() { + dropRegions << new DropWidget::DropRegion(this, "Endmembers", description1, "map-marker-alt", true, [this, candidateDataset]() { - auto noEndmembers = candidateDataset.get()->getNumPoints(); + auto noEndmembers = candidateDataset.get()->getNumPoints(); - if (noEndmembers > 15) { - EndmembersCheckDialog endmembersCheckDialog(nullptr, noEndmembers); + if (noEndmembers > 15) { + EndmembersCheckDialog endmembersCheckDialog(nullptr, noEndmembers); - connect(&endmembersCheckDialog, &EndmembersCheckDialog::closeDialog, this, [this, candidateDataset]() { - addDataset(candidateDataset); - }); + connect(&endmembersCheckDialog, &EndmembersCheckDialog::closeDialog, this, [this, candidateDataset]() { + addDataset(candidateDataset); + }); - endmembersCheckDialog.exec(); + endmembersCheckDialog.exec(); - } - else - addDataset(candidateDataset); - }); + } + else + addDataset(candidateDataset); + }); - dropRegions << new DropWidget::DropRegion(this, "Average endmember", description2, "map-marker-alt", true, [this, candidateDataset]() { - addAverageDataset(candidateDataset); - }); + dropRegions << new DropWidget::DropRegion(this, "Average endmember", description2, "map-marker-alt", true, [this, candidateDataset]() { + addAverageDataset(candidateDataset); + }); + } } - } - else { - dropRegions << new DropWidget::DropRegion(this, "Points", description, "map-marker-alt", true, [this, candidateDataset]() { - _points = candidateDataset; + else { + dropRegions << new DropWidget::DropRegion(this, "Points", description, "map-marker-alt", true, [this, candidateDataset]() { + _points = candidateDataset; - initializeImageRGB(); - _mainToolbarAction.setEnabled(true); - _model.removeAllEndmembers(); - }); + initializeImageRGB(); + _mainToolbarAction.setEnabled(true); + _model.removeAllEndmembers(); + }); + } } } } + catch (std::exception& e) + { + exceptionMessageBox(QString("Unable to load '%1'").arg(datasetGuiName), e); + } + catch (...) { + exceptionMessageBox(QString("Unable to load '%1'").arg(datasetGuiName)); + } } - catch (std::exception& e) - { - exceptionMessageBox(QString("Unable to load '%1'").arg(datasetGuiName), e); - } - catch (...) { - exceptionMessageBox(QString("Unable to load '%1'").arg(datasetGuiName)); - } - } - if (dataType == ClusterType) { - const auto description = QString("Visualize every cluster in %1 as one line").arg(candidateDataset->getGuiName()); + if (dataType == ClusterType) { + const auto description = QString("Visualize every cluster in %1 as one line").arg(candidateDataset->getGuiName()); - if (_points.isValid()) { + if (_points.isValid()) { - QString pointsGuid = _points->getId(); + QString pointsGuid = _points->getId(); - if (!_points->isFull() || _points->isDerivedData()) { - DataHierarchyItem* pointParent = _points->getDataHierarchyItem().getParent(); - pointsGuid = pointParent->getDataset()->getId(); - } + if (!_points->isFull() || _points->isDerivedData()) { + DataHierarchyItem* pointParent = _points->getDataHierarchyItem().getParent(); + pointsGuid = pointParent->getDataset()->getId(); + } - DataHierarchyItem* parent = candidateDataset->getDataHierarchyItem().getParent(); + DataHierarchyItem* parent = candidateDataset->getDataHierarchyItem().getParent(); - if (parent && parent->getDataset()->getId() == pointsGuid) { + if (parent && parent->getDataset()->getId() == pointsGuid) { - dropRegions << new DropWidget::DropRegion(this, "Endmembers", description, "map-marker-alt", true, [this, candidateDataset]() { - _clusterNames.push_back(candidateDataset->getGuiName()); - _noLoadedClusters.push_back(candidateDataset.get()->getClusters().size()); - addDataset(candidateDataset); + dropRegions << new DropWidget::DropRegion(this, "Endmembers", description, "map-marker-alt", true, [this, candidateDataset]() { + _clusterNames.push_back(candidateDataset->getGuiName()); + _noLoadedClusters.push_back(candidateDataset.get()->getClusters().size()); + addDataset(candidateDataset); - }); + }); + } + else { + dropRegions << new DropWidget::DropRegion(this, "Warning", "Clusters have to be a child of and come from a set of same size as the loaded points", "exclamation-circle", false); + } } else { - dropRegions << new DropWidget::DropRegion(this, "Warning", "Clusters have to be a child of and come from a set of same size as the loaded points", "exclamation-circle", false); - } - } - else { - // Only allow user to visualize clusters as lines when there is a points dataset loaded - dropRegions << new DropWidget::DropRegion(this, "No points data loaded", "Clusters can only be visualized in concert with points data", "exclamation-circle", false); + // Only allow user to visualize clusters as lines when there is a points dataset loaded + dropRegions << new DropWidget::DropRegion(this, "No points data loaded", "Clusters can only be visualized in concert with points data", "exclamation-circle", false); + } } } @@ -1261,14 +1268,9 @@ void SpectralViewPlugin::computeAverageDataset(int width, int height, int numDim } } - -// ============================================================================= -// Factory -// ============================================================================= - -QIcon SpectralViewPluginFactory::getIcon(const QColor& color) const +SpectralViewPluginFactory::SpectralViewPluginFactory() { - return Application::getIconFont("FontAwesome").getIcon("chart-line"); + setIconByName("chart-line"); } ViewPlugin* SpectralViewPluginFactory::produce() @@ -1297,7 +1299,7 @@ mv::gui::PluginTriggerActions SpectralViewPluginFactory::getPluginTriggerActions if (PluginFactory::areAllDatasetsOfTheSameType(datasets, PointType)) { if (numberOfDatasets >= 1) { if (datasets.first()->getDataType() == PointType) { - auto pluginTriggerAction = new PluginTriggerAction(const_cast(this), this, "Spectral Viewer", "Load dataset in spectral Viewer", getIcon(), [this, getInstance, datasets](PluginTriggerAction& pluginTriggerAction) -> void { + auto pluginTriggerAction = new PluginTriggerAction(const_cast(this), this, "Spectral Viewer", "Load dataset in spectral Viewer", icon(), [this, getInstance, datasets](PluginTriggerAction& pluginTriggerAction) -> void { for (const auto& dataset : datasets) getInstance()->loadData(Datasets({ dataset })); }); diff --git a/src/SpectralViewPlugin.h b/src/SpectralViewPlugin.h index de8b112..bb7ddef 100644 --- a/src/SpectralViewPlugin.h +++ b/src/SpectralViewPlugin.h @@ -130,14 +130,12 @@ class SpectralViewPluginFactory : public ViewPluginFactory Q_INTERFACES(mv::plugin::ViewPluginFactory mv::plugin::PluginFactory) Q_OBJECT Q_PLUGIN_METADATA(IID "nl.tudelft.SpectralViewPlugin" - FILE "SpectralViewPlugin.json") + FILE "PluginInfo.json") public: - SpectralViewPluginFactory() {} - ~SpectralViewPluginFactory() override {} + SpectralViewPluginFactory(); - /** Returns the plugin icon */ - QIcon getIcon(const QColor& color = Qt::black) const override; + ~SpectralViewPluginFactory() override {} ViewPlugin* produce() override; diff --git a/src/SpectralViewPlugin.json b/src/SpectralViewPlugin.json deleted file mode 100644 index da46003..0000000 --- a/src/SpectralViewPlugin.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name" : "Spectral View", - "version" : "1", - "dependencies" : ["Points", "Cluster", "Images"] -} \ No newline at end of file diff --git a/src/ViewSettingsAction.cpp b/src/ViewSettingsAction.cpp index 834dcb0..33013b5 100644 --- a/src/ViewSettingsAction.cpp +++ b/src/ViewSettingsAction.cpp @@ -14,7 +14,7 @@ ViewSettingsAction::ViewSettingsAction(SpectralViewPlugin& spectralViewPlugin) : _showSelectionAction(this, "Show selection", true), _stdAreaEnabledAction(this, "Show std. dev.", true) { - setIcon(Application::getIconFont("FontAwesome").getIcon("cog")); + setIconByName("gear"); setText("Global settings"); _showSelectionAction.setToolTip("Show a line for the current selection"); diff --git a/src/WavelengthsRGBAction.cpp b/src/WavelengthsRGBAction.cpp index 1f8f9a6..cb12c1e 100644 --- a/src/WavelengthsRGBAction.cpp +++ b/src/WavelengthsRGBAction.cpp @@ -15,7 +15,7 @@ WavelengthsRGBAction::WavelengthsRGBAction(SpectralViewPlugin& spectralViewPlugi _greenWavelengthAction(this, "Green", { "532" }, "532"), _blueWavelengthAction(this, "Blue", { "464" }, "464") { - setIcon(Application::getIconFont("FontAwesome").getIcon("grip-lines-vertical")); + setIconByName("grip-lines-vertical"); setText("RGB Wavelengths Settings"); _wavelengthsRGBEnabledAction.setToolTip("Show 3 RGB lines that can be moved");